From b2580406fc5cfd81df66b061121203b52988f368 Mon Sep 17 00:00:00 2001 From: Bastian Germann Date: Tue, 15 Jan 2019 21:40:44 +0000 Subject: [PATCH] Import gambas3_3.12.2.orig.tar.gz [dgit import orig gambas3_3.12.2.orig.tar.gz] --- AUTHORS | 274 + CONTRIBUTING.md | 149 + COPYING | 339 + ChangeLog | 0 INSTALL | 1 + INSTALL.html | 1216 + Makefile.am | 59 + NEWS | 0 README | 38 + README.commit | 60 + README.md | 43 + TEMPLATE/README | 34 + TEMPLATE/TEMPLATE.c | 28 + TEMPLATE/TEMPLATE.conf | 79 + TEMPLATE/TEMPLATE.cpp | 28 + TEMPLATE/TEMPLATE.h | 35 + TEMPLATE/conf/gb.cairo.conf | 76 + TEMPLATE/conf/gb.dbus.conf | 76 + TEMPLATE/conf/gb.desktop.conf | 76 + TEMPLATE/conf/gb.gmp.conf | 76 + TEMPLATE/conf/gb.gsl.conf | 76 + TEMPLATE/conf/gb.image.io.conf | 76 + TEMPLATE/conf/gb.media.conf | 76 + TEMPLATE/conf/gb.mime.conf | 76 + TEMPLATE/conf/gb.net.pop3.conf | 76 + TEMPLATE/conf/gb.net.smtp.conf | 76 + TEMPLATE/conf/gb.openal.conf | 76 + TEMPLATE/make-component | 64 + TEMPLATE/template/AUTHORS | 0 TEMPLATE/template/ChangeLog | 0 TEMPLATE/template/Makefile.am | 3 + TEMPLATE/template/NEWS | 0 TEMPLATE/template/README | 0 TEMPLATE/template/SOURCES | 2 + TEMPLATE/template/configure.ac | 72 + TEMPLATE/template/make-component | 3 + TEMPLATE/template/src/.component | 3 + TEMPLATE/template/src/Makefile.am | 12 + TODO | 79 + VERSION | 1 + acinclude.m4 | 1591 + app/AUTHORS | 0 app/COPYING | 1 + app/ChangeLog | 0 app/INSTALL | 231 + app/Makefile.am | 65 + app/NEWS | 0 app/README | 0 app/TODO | 0 app/acinclude.m4 | 1 + app/configure.ac | 19 + app/desktop/gambas3.appdata.xml | 31 + app/desktop/gambas3.desktop | 10 + app/desktop/gambas3.png | Bin 0 -> 3144 bytes app/desktop/gambas3.svg | 540 + app/examples/Basic/Blights/.directory | 2 + app/examples/Basic/Blights/.icon.png | Bin 0 -> 4209 bytes app/examples/Basic/Blights/.lang/ca.mo | Bin 0 -> 489 bytes app/examples/Basic/Blights/.lang/ca.po | 28 + app/examples/Basic/Blights/.lang/cs.mo | Bin 0 -> 443 bytes app/examples/Basic/Blights/.lang/cs.po | 20 + app/examples/Basic/Blights/.lang/de.mo | Bin 0 -> 442 bytes app/examples/Basic/Blights/.lang/de.po | 21 + app/examples/Basic/Blights/.lang/es.mo | Bin 0 -> 441 bytes app/examples/Basic/Blights/.lang/es.po | 20 + app/examples/Basic/Blights/.lang/fr.po | 3 + app/examples/Basic/Blights/.lang/nl.mo | Bin 0 -> 443 bytes app/examples/Basic/Blights/.lang/nl.po | 20 + app/examples/Basic/Blights/.lang/sv.mo | Bin 0 -> 358 bytes app/examples/Basic/Blights/.lang/sv.po | 15 + app/examples/Basic/Blights/.project | 16 + app/examples/Basic/Blights/.src/win1.class | 36 + app/examples/Basic/Blights/.src/win1.form | 53 + app/examples/Basic/Blights/ampoule.png | Bin 0 -> 1543 bytes app/examples/Basic/Blights/bloff.xpm | 1234 + app/examples/Basic/Blights/blon.xpm | 1503 + app/examples/Basic/Collection/.directory | 2 + app/examples/Basic/Collection/.icon.png | Bin 0 -> 4310 bytes app/examples/Basic/Collection/.lang/ca.mo | Bin 0 -> 708 bytes app/examples/Basic/Collection/.lang/ca.po | 48 + app/examples/Basic/Collection/.lang/cs.mo | Bin 0 -> 622 bytes app/examples/Basic/Collection/.lang/cs.po | 40 + app/examples/Basic/Collection/.lang/de.mo | Bin 0 -> 619 bytes app/examples/Basic/Collection/.lang/de.po | 41 + app/examples/Basic/Collection/.lang/es.mo | Bin 0 -> 615 bytes app/examples/Basic/Collection/.lang/es.po | 41 + app/examples/Basic/Collection/.lang/nl.mo | Bin 0 -> 666 bytes app/examples/Basic/Collection/.lang/nl.po | 41 + app/examples/Basic/Collection/.project | 17 + .../Basic/Collection/.src/CThing.class | 6 + .../Basic/Collection/.src/FStart.class | 40 + .../Basic/Collection/.src/FStart.form | 20 + app/examples/Basic/Collection/collection.png | Bin 0 -> 2062 bytes app/examples/Basic/DragNDrop/.directory | 2 + app/examples/Basic/DragNDrop/.icon.png | Bin 0 -> 4159 bytes app/examples/Basic/DragNDrop/.project | 18 + .../Basic/DragNDrop/.src/FDragNDrop.class | 138 + .../Basic/DragNDrop/.src/FDragNDrop.form | 107 + app/examples/Basic/DragNDrop/drop.png | Bin 0 -> 1503 bytes app/examples/Basic/Object/.directory | 2 + app/examples/Basic/Object/.icon.png | Bin 0 -> 4189 bytes app/examples/Basic/Object/.lang/ca.mo | Bin 0 -> 724 bytes app/examples/Basic/Object/.lang/ca.po | 43 + app/examples/Basic/Object/.lang/cs.mo | Bin 0 -> 735 bytes app/examples/Basic/Object/.lang/cs.po | 40 + app/examples/Basic/Object/.lang/de.mo | Bin 0 -> 734 bytes app/examples/Basic/Object/.lang/de.po | 41 + app/examples/Basic/Object/.lang/es.mo | Bin 0 -> 686 bytes app/examples/Basic/Object/.lang/es.po | 37 + app/examples/Basic/Object/.lang/nl.mo | Bin 0 -> 789 bytes app/examples/Basic/Object/.lang/nl.po | 41 + app/examples/Basic/Object/.project | 17 + app/examples/Basic/Object/.src/CThing.class | 6 + app/examples/Basic/Object/.src/FStart.class | 44 + app/examples/Basic/Object/.src/FStart.form | 28 + app/examples/Basic/Object/object.png | Bin 0 -> 1499 bytes app/examples/Basic/Timer/.directory | 2 + app/examples/Basic/Timer/.icon.png | Bin 0 -> 4418 bytes app/examples/Basic/Timer/.lang/ca.mo | Bin 0 -> 1508 bytes app/examples/Basic/Timer/.lang/ca.po | 113 + app/examples/Basic/Timer/.lang/cs.mo | Bin 0 -> 1411 bytes app/examples/Basic/Timer/.lang/cs.po | 92 + app/examples/Basic/Timer/.lang/de.mo | Bin 0 -> 1420 bytes app/examples/Basic/Timer/.lang/de.po | 93 + app/examples/Basic/Timer/.lang/es.mo | Bin 0 -> 1469 bytes app/examples/Basic/Timer/.lang/es.po | 96 + app/examples/Basic/Timer/.lang/nl.mo | Bin 0 -> 1428 bytes app/examples/Basic/Timer/.lang/nl.po | 92 + app/examples/Basic/Timer/.project | 19 + .../Basic/Timer/.src/FOtherTimer.class | 85 + .../Basic/Timer/.src/FOtherTimer.form | 40 + app/examples/Basic/Timer/.src/FTimer.class | 56 + app/examples/Basic/Timer/.src/FTimer.form | 97 + app/examples/Basic/Timer/timer.png | Bin 0 -> 2778 bytes .../Control/ArrayOfControls/.directory | 2 + .../Control/ArrayOfControls/.icon.png | Bin 0 -> 4209 bytes .../Control/ArrayOfControls/.lang/ca.mo | Bin 0 -> 832 bytes .../Control/ArrayOfControls/.lang/ca.po | 45 + .../Control/ArrayOfControls/.lang/cs.mo | Bin 0 -> 782 bytes .../Control/ArrayOfControls/.lang/cs.po | 44 + .../Control/ArrayOfControls/.lang/de.mo | Bin 0 -> 806 bytes .../Control/ArrayOfControls/.lang/de.po | 44 + .../Control/ArrayOfControls/.lang/nl.mo | Bin 0 -> 806 bytes .../Control/ArrayOfControls/.lang/nl.po | 43 + app/examples/Control/ArrayOfControls/.project | 18 + .../Control/ArrayOfControls/.src/FMain.class | 83 + .../Control/ArrayOfControls/.src/FMain.form | 25 + .../Control/ArrayOfControls/green.png | Bin 0 -> 873 bytes .../Control/ArrayOfControls/green1.png | Bin 0 -> 1548 bytes .../Control/ArrayOfControls/phone.png | Bin 0 -> 18663 bytes app/examples/Control/ArrayOfControls/red.png | Bin 0 -> 1452 bytes app/examples/Control/ArrayOfControls/red1.png | Bin 0 -> 1571 bytes app/examples/Control/Embedder/.directory | 2 + app/examples/Control/Embedder/.icon.png | Bin 0 -> 4242 bytes app/examples/Control/Embedder/.lang/ca.mo | Bin 0 -> 1518 bytes app/examples/Control/Embedder/.lang/ca.po | 70 + app/examples/Control/Embedder/.lang/cs.mo | Bin 0 -> 1373 bytes app/examples/Control/Embedder/.lang/cs.po | 62 + app/examples/Control/Embedder/.lang/de.mo | Bin 0 -> 1463 bytes app/examples/Control/Embedder/.lang/de.po | 59 + app/examples/Control/Embedder/.lang/es.mo | Bin 0 -> 1492 bytes app/examples/Control/Embedder/.lang/es.po | 57 + app/examples/Control/Embedder/.lang/nl.mo | Bin 0 -> 1416 bytes app/examples/Control/Embedder/.lang/nl.po | 55 + app/examples/Control/Embedder/.project | 20 + .../Control/Embedder/.src/FMain.class | 89 + app/examples/Control/Embedder/.src/FMain.form | 54 + app/examples/Control/Embedder/embedder.png | Bin 0 -> 2623 bytes .../Control/HighlightEditor/.directory | 2 + .../.hidden/screenshots/2014-12-17.png | Bin 0 -> 58870 bytes .../Control/HighlightEditor/.icon.png | Bin 0 -> 3541 bytes .../Control/HighlightEditor/.lang/ca.mo | Bin 0 -> 724 bytes .../Control/HighlightEditor/.lang/ca.po | 43 + .../Control/HighlightEditor/.lang/cs.mo | Bin 0 -> 842 bytes .../Control/HighlightEditor/.lang/cs.po | 40 + .../Control/HighlightEditor/.lang/de.mo | Bin 0 -> 873 bytes .../Control/HighlightEditor/.lang/de.po | 41 + .../Control/HighlightEditor/.lang/es.mo | Bin 0 -> 569 bytes .../Control/HighlightEditor/.lang/es.po | 34 + .../Control/HighlightEditor/.lang/nl.mo | Bin 0 -> 833 bytes .../Control/HighlightEditor/.lang/nl.po | 39 + app/examples/Control/HighlightEditor/.project | 19 + .../HighlightEditor/.src/FEditor.class | 184 + .../Control/HighlightEditor/.src/FEditor.form | 36 + .../Control/HighlightEditor/download.html | 155 + .../Control/HighlightEditor/editor.png | Bin 0 -> 1657 bytes app/examples/Control/LCDLabel/.directory | 2 + app/examples/Control/LCDLabel/.icon.png | Bin 0 -> 3429 bytes app/examples/Control/LCDLabel/.project | 16 + .../Control/LCDLabel/.src/FMain.class | 54 + app/examples/Control/LCDLabel/.src/FMain.form | 33 + .../Control/LCDLabel/.src/TimeBox.class | 152 + app/examples/Control/LCDLabel/alarm.ogg | Bin 0 -> 61621 bytes app/examples/Control/LCDLabel/lcdlabel.png | Bin 0 -> 492 bytes app/examples/Control/MapView/.directory | 2 + .../Control/MapView/.hidden/mapview.png | Bin 0 -> 1749 bytes app/examples/Control/MapView/.icon.png | Bin 0 -> 3486 bytes app/examples/Control/MapView/.project | 18 + app/examples/Control/MapView/.src/FMain.class | 141 + app/examples/Control/MapView/.src/FMain.form | 80 + app/examples/Control/TextEdit/.directory | 2 + app/examples/Control/TextEdit/.icon.png | Bin 0 -> 4151 bytes app/examples/Control/TextEdit/.lang/ca.mo | Bin 0 -> 1741 bytes app/examples/Control/TextEdit/.lang/ca.po | 118 + app/examples/Control/TextEdit/.lang/cs.mo | Bin 0 -> 1640 bytes app/examples/Control/TextEdit/.lang/cs.po | 109 + app/examples/Control/TextEdit/.lang/de.mo | Bin 0 -> 1600 bytes app/examples/Control/TextEdit/.lang/de.po | 116 + app/examples/Control/TextEdit/.lang/es.mo | Bin 0 -> 1553 bytes app/examples/Control/TextEdit/.lang/es.po | 101 + app/examples/Control/TextEdit/.lang/fr.mo | Bin 0 -> 1037 bytes app/examples/Control/TextEdit/.lang/fr.po | 83 + app/examples/Control/TextEdit/.lang/nl.mo | Bin 0 -> 1549 bytes app/examples/Control/TextEdit/.lang/nl.po | 99 + app/examples/Control/TextEdit/.lang/sv.mo | Bin 0 -> 1185 bytes app/examples/Control/TextEdit/.lang/sv.po | 84 + app/examples/Control/TextEdit/.project | 23 + .../Control/TextEdit/.src/FMain.class | 171 + app/examples/Control/TextEdit/.src/FMain.form | 136 + .../Control/TextEdit/.src/frmShowHtml.class | 29 + .../Control/TextEdit/.src/frmShowHtml.form | 29 + app/examples/Control/TextEdit/edit.png | Bin 0 -> 1233 bytes app/examples/Control/TextEdit/text.html | 17 + app/examples/Control/TreeView/.directory | 2 + app/examples/Control/TreeView/.icon.png | Bin 0 -> 4093 bytes app/examples/Control/TreeView/.lang/ca.mo | Bin 0 -> 1286 bytes app/examples/Control/TreeView/.lang/ca.po | 88 + app/examples/Control/TreeView/.lang/cs.mo | Bin 0 -> 1225 bytes app/examples/Control/TreeView/.lang/cs.po | 76 + app/examples/Control/TreeView/.lang/de.mo | Bin 0 -> 1210 bytes app/examples/Control/TreeView/.lang/de.po | 77 + app/examples/Control/TreeView/.lang/es.mo | Bin 0 -> 1068 bytes app/examples/Control/TreeView/.lang/es.po | 73 + app/examples/Control/TreeView/.lang/nl.mo | Bin 0 -> 1184 bytes app/examples/Control/TreeView/.lang/nl.po | 75 + app/examples/Control/TreeView/.project | 18 + .../TreeView/.src/TreeViewExample.class | 184 + .../TreeView/.src/TreeViewExample.form | 59 + app/examples/Control/TreeView/Female.png | Bin 0 -> 293 bytes app/examples/Control/TreeView/Male.png | Bin 0 -> 323 bytes app/examples/Control/TreeView/treeview.png | Bin 0 -> 471 bytes app/examples/Control/Wizard/.directory | 2 + app/examples/Control/Wizard/.icon.png | Bin 0 -> 4168 bytes app/examples/Control/Wizard/.lang/ca.mo | Bin 0 -> 4121 bytes app/examples/Control/Wizard/.lang/ca.po | 137 + app/examples/Control/Wizard/.lang/cs.mo | Bin 0 -> 4008 bytes app/examples/Control/Wizard/.lang/cs.po | 136 + app/examples/Control/Wizard/.lang/de.mo | Bin 0 -> 4124 bytes app/examples/Control/Wizard/.lang/de.po | 136 + app/examples/Control/Wizard/.lang/nl.mo | Bin 0 -> 2333 bytes app/examples/Control/Wizard/.lang/nl.po | 107 + app/examples/Control/Wizard/.project | 17 + app/examples/Control/Wizard/.src/FMain.class | 65 + app/examples/Control/Wizard/.src/FMain.form | 118 + app/examples/Control/Wizard/wizard.png | Bin 0 -> 1510 bytes app/examples/Database/Database/.component | 4 + app/examples/Database/Database/.directory | 2 + app/examples/Database/Database/.icon.png | Bin 0 -> 4336 bytes app/examples/Database/Database/.lang/ca.mo | Bin 0 -> 1832 bytes app/examples/Database/Database/.lang/ca.po | 160 + app/examples/Database/Database/.lang/cs.mo | Bin 0 -> 1795 bytes app/examples/Database/Database/.lang/cs.po | 157 + app/examples/Database/Database/.lang/de.mo | Bin 0 -> 1817 bytes app/examples/Database/Database/.lang/de.po | 153 + app/examples/Database/Database/.lang/es.mo | Bin 0 -> 1784 bytes app/examples/Database/Database/.lang/es.po | 151 + app/examples/Database/Database/.lang/nl.mo | Bin 0 -> 1718 bytes app/examples/Database/Database/.lang/nl.po | 147 + app/examples/Database/Database/.project | 20 + .../Database/Database/.src/FMain.class | 178 + .../Database/Database/.src/FMain.form | 117 + .../Database/Database/.src/FRequest.class | 131 + .../Database/Database/.src/FRequest.form | 12 + .../Database/Database/.src/FTest.class | 25 + .../Database/Database/.src/FTest.form | 121 + .../Database/Database/.src/Form1.class | 83 + .../Database/Database/.src/Form1.form | 8 + app/examples/Database/Database/database.png | Bin 0 -> 1142 bytes .../MySQLExample/.action/FBrowser.action | 70 + .../MySQLExample/.action/FNewIndex.action | 10 + .../MySQLExample/.action/FNewRoutine.action | 9 + .../MySQLExample/.action/FQuery.action | 14 + .../MySQLExample/.action/FResult.action | 29 + .../MySQLExample/.action/FScript.action | 15 + .../MySQLExample/.action/FTables.action | 37 + app/examples/Database/MySQLExample/.directory | 2 + app/examples/Database/MySQLExample/.icon.png | Bin 0 -> 4409 bytes .../Database/MySQLExample/.lang/ca.mo | Bin 0 -> 6760 bytes .../Database/MySQLExample/.lang/ca.po | 539 + .../Database/MySQLExample/.lang/cs.mo | Bin 0 -> 6581 bytes .../Database/MySQLExample/.lang/cs.po | 536 + .../Database/MySQLExample/.lang/de.mo | Bin 0 -> 6656 bytes .../Database/MySQLExample/.lang/de.po | 536 + .../Database/MySQLExample/.lang/es.mo | Bin 0 -> 6546 bytes .../Database/MySQLExample/.lang/es.po | 533 + .../Database/MySQLExample/.lang/fr.mo | Bin 0 -> 6143 bytes .../Database/MySQLExample/.lang/fr.po | 1546 + .../Database/MySQLExample/.lang/nl.mo | Bin 0 -> 6578 bytes .../Database/MySQLExample/.lang/nl.po | 531 + app/examples/Database/MySQLExample/.project | 26 + .../.src/CreateObjects/FNewDatabase.class | 33 + .../.src/CreateObjects/FNewDatabase.form | 54 + .../.src/CreateObjects/FNewEvent.class | 51 + .../.src/CreateObjects/FNewEvent.form | 53 + .../.src/CreateObjects/FNewField.class | 53 + .../.src/CreateObjects/FNewField.form | 77 + .../.src/CreateObjects/FNewIndex.class | 79 + .../.src/CreateObjects/FNewIndex.form | 70 + .../.src/CreateObjects/FNewRoutine.class | 65 + .../.src/CreateObjects/FNewRoutine.form | 71 + .../.src/CreateObjects/FNewTable.class | 55 + .../.src/CreateObjects/FNewTable.form | 54 + .../.src/CreateObjects/FNewTrigger.class | 62 + .../.src/CreateObjects/FNewTrigger.form | 64 + .../.src/CreateObjects/FNewView.class | 45 + .../.src/CreateObjects/FNewView.form | 44 + .../Database/MySQLExample/.src/FConnect.class | 46 + .../Database/MySQLExample/.src/FConnect.form | 73 + .../Database/MySQLExample/.src/FMessage.class | 21 + .../Database/MySQLExample/.src/FMessage.form | 12 + .../Database/MySQLExample/.src/FTables.class | 1186 + .../Database/MySQLExample/.src/FTables.form | 206 + .../Database/MySQLExample/.src/modMain.module | 69 + .../Database/MySQLExample/icons/16/Admin.png | Bin 0 -> 1099 bytes .../Database/MySQLExample/icons/16/Blob.png | Bin 0 -> 646 bytes .../MySQLExample/icons/16/Column_FK.png | Bin 0 -> 529 bytes .../MySQLExample/icons/16/Database.png | Bin 0 -> 805 bytes .../MySQLExample/icons/16/Datetime.png | Bin 0 -> 1140 bytes .../Database/MySQLExample/icons/16/Field.png | Bin 0 -> 653 bytes .../MySQLExample/icons/16/Function.png | Bin 0 -> 245 bytes .../Database/MySQLExample/icons/16/Index.png | Bin 0 -> 1122 bytes .../Database/MySQLExample/icons/16/Lock.png | Bin 0 -> 1195 bytes .../Database/MySQLExample/icons/16/New.png | Bin 0 -> 817 bytes .../MySQLExample/icons/16/Numeric.png | Bin 0 -> 1021 bytes .../MySQLExample/icons/16/Primarykey.png | Bin 0 -> 585 bytes .../MySQLExample/icons/16/Refresh.png | Bin 0 -> 349 bytes .../MySQLExample/icons/16/Routine.png | Bin 0 -> 827 bytes .../Database/MySQLExample/icons/16/String.png | Bin 0 -> 1024 bytes .../Database/MySQLExample/icons/16/Table.png | Bin 0 -> 823 bytes .../MySQLExample/icons/16/Trigger.png | Bin 0 -> 930 bytes .../Database/MySQLExample/icons/16/View.png | Bin 0 -> 697 bytes .../MySQLExample/icons/16/Warning.png | Bin 0 -> 543 bytes .../Database/MySQLExample/icons/24/Null.png | Bin 0 -> 121 bytes .../Database/PictureDatabase/.directory | 2 + .../Database/PictureDatabase/.icon.png | Bin 0 -> 4228 bytes .../Database/PictureDatabase/.lang/ca.mo | Bin 0 -> 1339 bytes .../Database/PictureDatabase/.lang/ca.po | 84 + .../Database/PictureDatabase/.lang/cs.mo | Bin 0 -> 1213 bytes .../Database/PictureDatabase/.lang/cs.po | 75 + .../Database/PictureDatabase/.lang/de.mo | Bin 0 -> 1244 bytes .../Database/PictureDatabase/.lang/de.po | 76 + .../Database/PictureDatabase/.lang/es.mo | Bin 0 -> 784 bytes .../Database/PictureDatabase/.lang/es.po | 36 + .../Database/PictureDatabase/.lang/nl.mo | Bin 0 -> 1259 bytes .../Database/PictureDatabase/.lang/nl.po | 67 + .../Database/PictureDatabase/.project | 25 + .../.src/FormPictureDatabase.class | 227 + .../.src/FormPictureDatabase.form | 67 + .../.src/ModuleDatabase.module | 215 + .../Images/document-save-as.png | Bin 0 -> 845 bytes .../PictureDatabase/Images/document-save.png | Bin 0 -> 903 bytes .../Images/image-x-generic.png | Bin 0 -> 1150 bytes .../PictureDatabase/Images/list-add.png | Bin 0 -> 366 bytes .../PictureDatabase/Images/list-remove.png | Bin 0 -> 282 bytes app/examples/Drawing/AnalogWatch/.directory | 2 + .../.hidden/screenshots/2014-12-14.png | Bin 0 -> 24631 bytes app/examples/Drawing/AnalogWatch/.icon.png | Bin 0 -> 3642 bytes app/examples/Drawing/AnalogWatch/.project | 17 + .../Drawing/AnalogWatch/.src/FrmClock.class | 152 + .../Drawing/AnalogWatch/.src/FrmClock.form | 30 + app/examples/Drawing/AnalogWatch/timer.png | Bin 0 -> 2778 bytes app/examples/Drawing/Barcode/.directory | 2 + app/examples/Drawing/Barcode/.icon.png | Bin 0 -> 3990 bytes app/examples/Drawing/Barcode/.lang/ca.mo | Bin 0 -> 1641 bytes app/examples/Drawing/Barcode/.lang/ca.po | 77 + app/examples/Drawing/Barcode/.lang/cs.mo | Bin 0 -> 1609 bytes app/examples/Drawing/Barcode/.lang/cs.po | 76 + app/examples/Drawing/Barcode/.lang/de.mo | Bin 0 -> 1567 bytes app/examples/Drawing/Barcode/.lang/de.po | 76 + app/examples/Drawing/Barcode/.project | 21 + app/examples/Drawing/Barcode/.src/FMain.class | 161 + app/examples/Drawing/Barcode/.src/FMain.form | 88 + .../Drawing/Barcode/.src/modCrBcode.module | 98 + app/examples/Drawing/Barcode/barcode.png | Bin 0 -> 765 bytes app/examples/Drawing/Chart/.directory | 2 + app/examples/Drawing/Chart/.icon.png | Bin 0 -> 4083 bytes app/examples/Drawing/Chart/.lang/ca.mo | Bin 0 -> 977 bytes app/examples/Drawing/Chart/.lang/ca.po | 85 + app/examples/Drawing/Chart/.lang/cs.mo | Bin 0 -> 1002 bytes app/examples/Drawing/Chart/.lang/cs.po | 84 + app/examples/Drawing/Chart/.lang/de.mo | Bin 0 -> 1008 bytes app/examples/Drawing/Chart/.lang/de.po | 81 + app/examples/Drawing/Chart/.lang/es.mo | Bin 0 -> 946 bytes app/examples/Drawing/Chart/.lang/es.po | 78 + app/examples/Drawing/Chart/.project | 16 + .../Drawing/Chart/.src/FormChart.class | 112 + .../Drawing/Chart/.src/FormChart.form | 32 + .../Drawing/Chart/.src/FormData.class | 43 + app/examples/Drawing/Chart/.src/FormData.form | 89 + app/examples/Drawing/Chart/graph.png | Bin 0 -> 225 bytes app/examples/Drawing/Clock/.directory | 2 + app/examples/Drawing/Clock/.icon.png | Bin 0 -> 4367 bytes app/examples/Drawing/Clock/.icon/16.png | Bin 0 -> 949 bytes app/examples/Drawing/Clock/.icon/32.png | Bin 0 -> 2599 bytes app/examples/Drawing/Clock/.icon/48.png | Bin 0 -> 4946 bytes app/examples/Drawing/Clock/.lang/ca.mo | Bin 0 -> 860 bytes app/examples/Drawing/Clock/.lang/ca.po | 63 + app/examples/Drawing/Clock/.lang/cs.mo | Bin 0 -> 803 bytes app/examples/Drawing/Clock/.lang/cs.po | 56 + app/examples/Drawing/Clock/.lang/de.mo | Bin 0 -> 800 bytes app/examples/Drawing/Clock/.lang/de.po | 57 + app/examples/Drawing/Clock/.lang/es.mo | Bin 0 -> 654 bytes app/examples/Drawing/Clock/.lang/es.po | 53 + app/examples/Drawing/Clock/.project | 21 + app/examples/Drawing/Clock/.src/FClock.class | 170 + app/examples/Drawing/Clock/.src/FClock.form | 55 + app/examples/Drawing/Clock/img/arrow_hour.png | Bin 0 -> 396 bytes app/examples/Drawing/Clock/img/arrow_min.png | Bin 0 -> 420 bytes app/examples/Drawing/Clock/img/arrow_sec.png | Bin 0 -> 1253 bytes .../Drawing/Clock/img/clock_bg_big1.png | Bin 0 -> 19642 bytes .../Drawing/Clock/img/clock_bg_big2.png | Bin 0 -> 24928 bytes .../Drawing/Clock/img/clock_bg_big3.png | Bin 0 -> 50841 bytes .../Drawing/Clock/img/clock_bg_big4.png | Bin 0 -> 21425 bytes app/examples/Drawing/Fractal/.directory | 2 + .../.hidden/screenshots/2014-12-14.png | Bin 0 -> 152730 bytes app/examples/Drawing/Fractal/.icon.png | Bin 0 -> 3483 bytes app/examples/Drawing/Fractal/.lang/cs.mo | Bin 0 -> 1127 bytes app/examples/Drawing/Fractal/.lang/cs.po | 60 + app/examples/Drawing/Fractal/.lang/fr.mo | Bin 0 -> 1155 bytes app/examples/Drawing/Fractal/.lang/fr.po | 61 + app/examples/Drawing/Fractal/.project | 18 + .../Drawing/Fractal/.src/FFractal.class | 245 + .../Drawing/Fractal/.src/FFractal.form | 18 + .../Drawing/Fractal/.src/FractalTask.class | 324 + app/examples/Drawing/Fractal/icon.png | Bin 0 -> 4737 bytes app/examples/Drawing/Fractal/rose.jpg | Bin 0 -> 26938 bytes app/examples/Drawing/GSLSpline/.directory | 2 + app/examples/Drawing/GSLSpline/.icon.png | Bin 0 -> 4045 bytes app/examples/Drawing/GSLSpline/.project | 14 + .../Drawing/GSLSpline/.src/FMain.class | 130 + .../Drawing/GSLSpline/.src/FMain.form | 45 + app/examples/Drawing/GSLSpline/spline.png | Bin 0 -> 1632 bytes app/examples/Drawing/Gravity/.directory | 2 + app/examples/Drawing/Gravity/.icon.png | Bin 0 -> 4437 bytes app/examples/Drawing/Gravity/.lang/ca.mo | Bin 0 -> 1961 bytes app/examples/Drawing/Gravity/.lang/ca.po | 135 + app/examples/Drawing/Gravity/.lang/cs.mo | Bin 0 -> 1885 bytes app/examples/Drawing/Gravity/.lang/cs.po | 128 + app/examples/Drawing/Gravity/.lang/de.mo | Bin 0 -> 1895 bytes app/examples/Drawing/Gravity/.lang/de.po | 129 + app/examples/Drawing/Gravity/.lang/es.mo | Bin 0 -> 1872 bytes app/examples/Drawing/Gravity/.lang/es.po | 126 + app/examples/Drawing/Gravity/.project | 18 + .../Drawing/Gravity/.src/FAbout.class | 8 + app/examples/Drawing/Gravity/.src/FAbout.form | 39 + app/examples/Drawing/Gravity/.src/FMain.class | 232 + app/examples/Drawing/Gravity/.src/FMain.form | 144 + app/examples/Drawing/Gravity/.src/cBall.class | 9 + app/examples/Drawing/Gravity/gravity.png | Bin 0 -> 7084 bytes .../Drawing/OnScreenDisplay/.directory | 2 + .../Drawing/OnScreenDisplay/.icon.png | Bin 0 -> 4186 bytes .../Drawing/OnScreenDisplay/.lang/ca.mo | Bin 0 -> 495 bytes .../Drawing/OnScreenDisplay/.lang/ca.po | 27 + .../Drawing/OnScreenDisplay/.lang/cs.mo | Bin 0 -> 439 bytes .../Drawing/OnScreenDisplay/.lang/cs.po | 20 + .../Drawing/OnScreenDisplay/.lang/de.mo | Bin 0 -> 454 bytes .../Drawing/OnScreenDisplay/.lang/de.po | 21 + .../Drawing/OnScreenDisplay/.lang/es.mo | Bin 0 -> 361 bytes .../Drawing/OnScreenDisplay/.lang/es.po | 15 + app/examples/Drawing/OnScreenDisplay/.project | 18 + .../.src/FOnScreenDisplay.class | 67 + .../.src/FOnScreenDisplay.form | 12 + app/examples/Drawing/OnScreenDisplay/icon.png | Bin 0 -> 1300 bytes app/examples/Drawing/Painting/.directory | 2 + .../.hidden/screenshots/2014-12-14.png | Bin 0 -> 90069 bytes app/examples/Drawing/Painting/.icon.png | Bin 0 -> 3331 bytes app/examples/Drawing/Painting/.lang/ca.mo | Bin 0 -> 1687 bytes app/examples/Drawing/Painting/.lang/ca.po | 126 + app/examples/Drawing/Painting/.lang/cs.mo | Bin 0 -> 1695 bytes app/examples/Drawing/Painting/.lang/cs.po | 128 + app/examples/Drawing/Painting/.lang/de.mo | Bin 0 -> 1695 bytes app/examples/Drawing/Painting/.lang/de.po | 129 + app/examples/Drawing/Painting/.project | 17 + .../Drawing/Painting/.src/FMain.class | 548 + app/examples/Drawing/Painting/.src/FMain.form | 195 + .../Painting/.src/MMakeSourceFile.module | 32 + app/examples/Drawing/Painting/Example1 | 29 + app/examples/Drawing/Painting/Example10 | 11 + app/examples/Drawing/Painting/Example11 | 26 + app/examples/Drawing/Painting/Example12 | 18 + app/examples/Drawing/Painting/Example13 | 16 + app/examples/Drawing/Painting/Example14 | 28 + app/examples/Drawing/Painting/Example15 | 22 + app/examples/Drawing/Painting/Example16 | 25 + app/examples/Drawing/Painting/Example17 | 29 + app/examples/Drawing/Painting/Example18 | 16 + app/examples/Drawing/Painting/Example2 | 29 + app/examples/Drawing/Painting/Example20 | 17 + app/examples/Drawing/Painting/Example21 | 14 + app/examples/Drawing/Painting/Example22 | 10 + app/examples/Drawing/Painting/Example3 | 15 + app/examples/Drawing/Painting/Example4 | 13 + app/examples/Drawing/Painting/Example5 | 61 + app/examples/Drawing/Painting/Example6 | 27 + app/examples/Drawing/Painting/Example7 | 17 + app/examples/Drawing/Painting/Example8 | 22 + app/examples/Drawing/Painting/Example9 | 23 + app/examples/Drawing/Painting/clovis.jpg | Bin 0 -> 15838 bytes app/examples/Drawing/Painting/gambas.svg | 540 + app/examples/Drawing/Painting/icon.png | Bin 0 -> 2078 bytes app/examples/Drawing/Painting/image.jpg | Bin 0 -> 19496 bytes app/examples/Drawing/QuasiRegular/.directory | 2 + .../.hidden/screenshots/2014-12-14.png | Bin 0 -> 44667 bytes app/examples/Drawing/QuasiRegular/.icon.png | Bin 0 -> 3730 bytes app/examples/Drawing/QuasiRegular/.project | 14 + .../Drawing/QuasiRegular/.src/FMain.class | 87 + .../Drawing/QuasiRegular/.src/FMain.form | 10 + app/examples/Drawing/QuasiRegular/icon.png | Bin 0 -> 705 bytes .../Drawing/RandomColorSort/.directory | 2 + .../.hidden/screenshots/2014-12-14.png | Bin 0 -> 247987 bytes .../Drawing/RandomColorSort/.icon.png | Bin 0 -> 3663 bytes app/examples/Drawing/RandomColorSort/.project | 15 + .../Drawing/RandomColorSort/.src/FMain.class | 265 + .../Drawing/RandomColorSort/.src/FMain.form | 59 + .../RandomColorSort/RandomColorSort.png | Bin 0 -> 10533 bytes app/examples/Drawing/Tablet/.directory | 2 + .../Tablet/.hidden/screenshots/2014-12-14.png | Bin 0 -> 59466 bytes app/examples/Drawing/Tablet/.icon.png | Bin 0 -> 3536 bytes app/examples/Drawing/Tablet/.project | 15 + app/examples/Drawing/Tablet/.src/FMain.class | 178 + app/examples/Drawing/Tablet/.src/FMain.form | 162 + app/examples/Drawing/Tablet/Icon.png | Bin 0 -> 3753 bytes app/examples/Games/BeastScroll/.dir_icon.png | Bin 0 -> 1571 bytes app/examples/Games/BeastScroll/.directory | 2 + app/examples/Games/BeastScroll/.icon.png | Bin 0 -> 3371 bytes app/examples/Games/BeastScroll/.project | 18 + .../Games/BeastScroll/.src/MMain.module | 215 + app/examples/Games/BeastScroll/b-title.mod | Bin 0 -> 56838 bytes app/examples/Games/BeastScroll/bgd1_ciel.png | Bin 0 -> 6797 bytes .../Games/BeastScroll/bgd2_montagnes.png | Bin 0 -> 177395 bytes app/examples/Games/BeastScroll/bgd3_sol1.png | Bin 0 -> 9994 bytes app/examples/Games/BeastScroll/bgd4_sol2.png | Bin 0 -> 9668 bytes app/examples/Games/BeastScroll/bgd5_sol3.png | Bin 0 -> 4772 bytes app/examples/Games/BeastScroll/fireworks.png | Bin 0 -> 1560 bytes app/examples/Games/BeastScroll/logo.png | Bin 0 -> 4768 bytes app/examples/Games/BeastScroll/scrolltext.png | Bin 0 -> 18100 bytes .../Games/BeastScroll/sprite_arbre.png | Bin 0 -> 73213 bytes .../Games/BeastScroll/sprite_barriere.png | Bin 0 -> 6455 bytes .../Games/BeastScroll/sprite_nuages1.png | Bin 0 -> 14285 bytes .../Games/BeastScroll/sprite_nuages2.png | Bin 0 -> 5079 bytes .../Games/BeastScroll/sprite_nuages3.png | Bin 0 -> 2389 bytes .../Games/BeastScroll/sprite_nuages4.png | Bin 0 -> 1036 bytes app/examples/Games/Concent/.directory | 2 + app/examples/Games/Concent/.icon.png | Bin 0 -> 4417 bytes app/examples/Games/Concent/.icon/16.png | Bin 0 -> 949 bytes app/examples/Games/Concent/.icon/32.png | Bin 0 -> 2599 bytes app/examples/Games/Concent/.icon/48.png | Bin 0 -> 4946 bytes app/examples/Games/Concent/.lang/ca.mo | Bin 0 -> 6958 bytes app/examples/Games/Concent/.lang/ca.po | 217 + app/examples/Games/Concent/.lang/cs.mo | Bin 0 -> 5602 bytes app/examples/Games/Concent/.lang/cs.po | 224 + app/examples/Games/Concent/.lang/de.mo | Bin 0 -> 2582 bytes app/examples/Games/Concent/.lang/de.po | 168 + app/examples/Games/Concent/.lang/en.mo | Bin 0 -> 2997 bytes app/examples/Games/Concent/.lang/en.po | 226 + app/examples/Games/Concent/.lang/es.mo | Bin 0 -> 6698 bytes app/examples/Games/Concent/.lang/es.po | 120 + app/examples/Games/Concent/.lang/fr.mo | Bin 0 -> 1873 bytes app/examples/Games/Concent/.lang/fr.po | 164 + app/examples/Games/Concent/.project | 22 + app/examples/Games/Concent/.src/fotos.class | 7 + app/examples/Games/Concent/.src/fotos.form | 210 + .../Games/Concent/.src/frmAcerca.class | 12 + .../Games/Concent/.src/frmAcerca.form | 20 + .../Games/Concent/.src/frmInstrucciones.class | 11 + .../Games/Concent/.src/frmInstrucciones.form | 22 + .../Games/Concent/.src/funciones.module | 162 + .../Games/Concent/.src/principal.class | 390 + .../Games/Concent/.src/principal.form | 220 + app/examples/Games/Concent/Blockhit.wav | Bin 0 -> 1148 bytes app/examples/Games/Concent/CHANGELOG | 6 + app/examples/Games/Concent/Missed.wav | Bin 0 -> 15612 bytes app/examples/Games/Concent/Newlevel.wav | Bin 0 -> 19842 bytes app/examples/Games/Concent/Paddle.wav | Bin 0 -> 1754 bytes app/examples/Games/Concent/Setup.wav | Bin 0 -> 768 bytes app/examples/Games/Concent/Wallhit.wav | Bin 0 -> 768 bytes app/examples/Games/Concent/applause.wav | Bin 0 -> 33654 bytes app/examples/Games/Concent/imagenes/an1.gif | Bin 0 -> 1601 bytes app/examples/Games/Concent/imagenes/an10.gif | Bin 0 -> 2209 bytes app/examples/Games/Concent/imagenes/an11.gif | Bin 0 -> 1365 bytes app/examples/Games/Concent/imagenes/an12.gif | Bin 0 -> 1332 bytes app/examples/Games/Concent/imagenes/an13.gif | Bin 0 -> 2223 bytes app/examples/Games/Concent/imagenes/an14.gif | Bin 0 -> 1924 bytes app/examples/Games/Concent/imagenes/an15.gif | Bin 0 -> 1132 bytes app/examples/Games/Concent/imagenes/an16.gif | Bin 0 -> 1318 bytes app/examples/Games/Concent/imagenes/an17.gif | Bin 0 -> 1845 bytes app/examples/Games/Concent/imagenes/an18.gif | Bin 0 -> 1571 bytes app/examples/Games/Concent/imagenes/an19.gif | Bin 0 -> 1849 bytes app/examples/Games/Concent/imagenes/an2.gif | Bin 0 -> 1373 bytes app/examples/Games/Concent/imagenes/an20.gif | Bin 0 -> 1540 bytes app/examples/Games/Concent/imagenes/an21.gif | Bin 0 -> 1720 bytes app/examples/Games/Concent/imagenes/an22.gif | Bin 0 -> 1332 bytes app/examples/Games/Concent/imagenes/an23.gif | Bin 0 -> 961 bytes app/examples/Games/Concent/imagenes/an24.gif | Bin 0 -> 1627 bytes app/examples/Games/Concent/imagenes/an25.gif | Bin 0 -> 1694 bytes app/examples/Games/Concent/imagenes/an26.gif | Bin 0 -> 1931 bytes app/examples/Games/Concent/imagenes/an27.gif | Bin 0 -> 2124 bytes app/examples/Games/Concent/imagenes/an28.gif | Bin 0 -> 1944 bytes app/examples/Games/Concent/imagenes/an29.gif | Bin 0 -> 1414 bytes app/examples/Games/Concent/imagenes/an3.gif | Bin 0 -> 1279 bytes app/examples/Games/Concent/imagenes/an30.gif | Bin 0 -> 1891 bytes app/examples/Games/Concent/imagenes/an31.gif | Bin 0 -> 1910 bytes app/examples/Games/Concent/imagenes/an32.gif | Bin 0 -> 1455 bytes app/examples/Games/Concent/imagenes/an33.gif | Bin 0 -> 1444 bytes app/examples/Games/Concent/imagenes/an34.gif | Bin 0 -> 1772 bytes app/examples/Games/Concent/imagenes/an35.gif | Bin 0 -> 1737 bytes app/examples/Games/Concent/imagenes/an36.gif | Bin 0 -> 1686 bytes app/examples/Games/Concent/imagenes/an37.gif | Bin 0 -> 2003 bytes app/examples/Games/Concent/imagenes/an38.gif | Bin 0 -> 1782 bytes app/examples/Games/Concent/imagenes/an39.gif | Bin 0 -> 2443 bytes app/examples/Games/Concent/imagenes/an4.gif | Bin 0 -> 1227 bytes app/examples/Games/Concent/imagenes/an40.gif | Bin 0 -> 2582 bytes app/examples/Games/Concent/imagenes/an5.gif | Bin 0 -> 1628 bytes app/examples/Games/Concent/imagenes/an6.gif | Bin 0 -> 2511 bytes app/examples/Games/Concent/imagenes/an7.gif | Bin 0 -> 1811 bytes app/examples/Games/Concent/imagenes/an8.gif | Bin 0 -> 1756 bytes app/examples/Games/Concent/imagenes/an9.gif | Bin 0 -> 2486 bytes .../Games/Concent/imagenes/colombia.gif | Bin 0 -> 145 bytes app/examples/Games/Concent/imagenes/inter.gif | Bin 0 -> 131 bytes app/examples/Games/Concent/imagenes/inter.jpg | Bin 0 -> 1148 bytes app/examples/Games/Concent/imagenes/logo.gif | Bin 0 -> 6138 bytes app/examples/Games/Concent/imagenes/logo.png | Bin 0 -> 1591 bytes app/examples/Games/Concent/imagenes/ok.gif | Bin 0 -> 1355 bytes .../Games/Concent/imagenes/tierra.gif | Bin 0 -> 32485 bytes .../Games/Concent/imagenes/tierra3.jpg | Bin 0 -> 23070 bytes app/examples/Games/Concent/move.wav | Bin 0 -> 13318 bytes app/examples/Games/Concent/shuffle.wav | Bin 0 -> 6999 bytes app/examples/Games/DeepSpace/.directory | 2 + app/examples/Games/DeepSpace/.icon.png | Bin 0 -> 4334 bytes app/examples/Games/DeepSpace/.lang/ca.mo | Bin 0 -> 758 bytes app/examples/Games/DeepSpace/.lang/ca.po | 51 + app/examples/Games/DeepSpace/.lang/cs.mo | Bin 0 -> 691 bytes app/examples/Games/DeepSpace/.lang/cs.po | 44 + app/examples/Games/DeepSpace/.lang/de.mo | Bin 0 -> 676 bytes app/examples/Games/DeepSpace/.lang/de.po | 44 + app/examples/Games/DeepSpace/.lang/es.mo | Bin 0 -> 722 bytes app/examples/Games/DeepSpace/.lang/es.po | 44 + app/examples/Games/DeepSpace/.project | 16 + .../Games/DeepSpace/.src/CBullet.class | 11 + .../Games/DeepSpace/.src/CObject.class | 87 + .../Games/DeepSpace/.src/FAbout.class | 19 + app/examples/Games/DeepSpace/.src/FAbout.form | 19 + app/examples/Games/DeepSpace/.src/FMain.class | 81 + app/examples/Games/DeepSpace/.src/FMain.form | 35 + .../Games/DeepSpace/.src/MMain.module | 348 + .../Games/DeepSpace/.src/MMath.module | 64 + .../Games/DeepSpace/doc/.html_files/eg3.gif | Bin 0 -> 2782 bytes .../Games/DeepSpace/doc/.html_files/eg3b.gif | Bin 0 -> 4590 bytes .../Games/DeepSpace/doc/coordinates.html | 150 + app/examples/Games/DeepSpace/doc/howto.txt | 16 + app/examples/Games/DeepSpace/doc/todo.txt | 17 + .../Games/DeepSpace/images/deepspace.png | Bin 0 -> 14195 bytes .../Games/DeepSpace/object.data/kite.2do | 8 + .../Games/DeepSpace/object.data/main.lst | 12 + .../Games/DeepSpace/object.data/ship.2do | 13 + .../Games/DeepSpace/object.data/triangle.2do | 8 + .../Games/DeepSpace/object.data/x-wing.2do | 38 + app/examples/Games/GNUBoxWorld/.directory | 2 + .../.hidden/screenshots/2014-12-14.png | Bin 0 -> 185109 bytes app/examples/Games/GNUBoxWorld/.icon.png | Bin 0 -> 3722 bytes app/examples/Games/GNUBoxWorld/.lang/ca.mo | Bin 0 -> 3974 bytes app/examples/Games/GNUBoxWorld/.lang/ca.po | 112 + app/examples/Games/GNUBoxWorld/.lang/cs.mo | Bin 0 -> 3119 bytes app/examples/Games/GNUBoxWorld/.lang/cs.po | 128 + app/examples/Games/GNUBoxWorld/.lang/de.mo | Bin 0 -> 3748 bytes app/examples/Games/GNUBoxWorld/.lang/de.po | 105 + app/examples/Games/GNUBoxWorld/.lang/es_AR.mo | Bin 0 -> 2898 bytes app/examples/Games/GNUBoxWorld/.lang/es_AR.po | 113 + app/examples/Games/GNUBoxWorld/.project | 19 + .../Games/GNUBoxWorld/.src/Cell.class | 78 + .../Games/GNUBoxWorld/.src/FMain.class | 274 + .../Games/GNUBoxWorld/.src/FMain.form | 37 + .../Games/GNUBoxWorld/.src/FrmAbout.class | 19 + .../Games/GNUBoxWorld/.src/FrmAbout.form | 45 + .../Games/GNUBoxWorld/.src/GameBoard.class | 595 + app/examples/Games/GNUBoxWorld/License | 189 + app/examples/Games/GNUBoxWorld/abajo.png | Bin 0 -> 5205 bytes app/examples/Games/GNUBoxWorld/arriba.png | Bin 0 -> 5291 bytes app/examples/Games/GNUBoxWorld/derecha.png | Bin 0 -> 4347 bytes app/examples/Games/GNUBoxWorld/destino.png | Bin 0 -> 9527 bytes app/examples/Games/GNUBoxWorld/ganador.png | Bin 0 -> 255686 bytes app/examples/Games/GNUBoxWorld/icon.png | Bin 0 -> 5889 bytes app/examples/Games/GNUBoxWorld/izquierda.png | Bin 0 -> 4350 bytes app/examples/Games/GNUBoxWorld/logo.png | Bin 0 -> 108170 bytes app/examples/Games/GNUBoxWorld/movible.png | Bin 0 -> 3831 bytes .../Games/GNUBoxWorld/movibleendestino.png | Bin 0 -> 3656 bytes .../Games/GNUBoxWorld/obstaculo-l.png | Bin 0 -> 1633 bytes .../Games/GNUBoxWorld/obstaculo-lr.png | Bin 0 -> 1643 bytes .../Games/GNUBoxWorld/obstaculo-r.png | Bin 0 -> 1627 bytes app/examples/Games/GNUBoxWorld/obstaculo.png | Bin 0 -> 1575 bytes app/examples/Games/GNUBoxWorld/piso.png | Bin 0 -> 10046 bytes app/examples/Games/GameOfLife/.debug | 9 + app/examples/Games/GameOfLife/.directory | 2 + app/examples/Games/GameOfLife/.icon.png | Bin 0 -> 3502 bytes app/examples/Games/GameOfLife/.lang/ca.mo | Bin 0 -> 2176 bytes app/examples/Games/GameOfLife/.lang/ca.po | 131 + app/examples/Games/GameOfLife/.lang/cs.mo | Bin 0 -> 2110 bytes app/examples/Games/GameOfLife/.lang/cs.po | 130 + app/examples/Games/GameOfLife/.lang/de.mo | Bin 0 -> 2092 bytes app/examples/Games/GameOfLife/.lang/de.po | 121 + app/examples/Games/GameOfLife/.project | 17 + .../Games/GameOfLife/.src/CGameField.class | 236 + .../Games/GameOfLife/.src/FMain.class | 95 + app/examples/Games/GameOfLife/.src/FMain.form | 162 + .../Games/GameOfLife/glob2-icon-48x48.png | Bin 0 -> 3390 bytes app/examples/Games/Invaders/.directory | 2 + app/examples/Games/Invaders/.icon.png | Bin 0 -> 3092 bytes app/examples/Games/Invaders/.project | 13 + .../Games/Invaders/.src/Enemies.class | 102 + app/examples/Games/Invaders/.src/Enemy.class | 161 + app/examples/Games/Invaders/.src/MMain.module | 139 + .../Games/Invaders/.src/Missile.class | 84 + .../Games/Invaders/.src/Missiles.class | 65 + app/examples/Games/Invaders/invaders.png | Bin 0 -> 125 bytes app/examples/Games/MineSweeper/.directory | 2 + .../.hidden/screenshots/2014-12-14.png | Bin 0 -> 48121 bytes app/examples/Games/MineSweeper/.icon.png | Bin 0 -> 3434 bytes app/examples/Games/MineSweeper/.lang/cs.mo | Bin 0 -> 703 bytes app/examples/Games/MineSweeper/.lang/cs.po | 52 + app/examples/Games/MineSweeper/.lang/ja.mo | Bin 0 -> 766 bytes app/examples/Games/MineSweeper/.lang/ja.po | 53 + app/examples/Games/MineSweeper/.lang/zh.mo | Bin 0 -> 714 bytes app/examples/Games/MineSweeper/.lang/zh.po | 52 + app/examples/Games/MineSweeper/.lang/zh_TW.mo | Bin 0 -> 726 bytes app/examples/Games/MineSweeper/.lang/zh_TW.po | 52 + app/examples/Games/MineSweeper/.project | 19 + .../Games/MineSweeper/.src/FMain.class | 249 + .../Games/MineSweeper/.src/FMain.form | 77 + .../Games/MineSweeper/.src/FSettings.class | 66 + .../Games/MineSweeper/.src/FSettings.form | 71 + .../MineSweeper/.src/MineSweeperGame.class | 194 + .../Games/MineSweeper/image/bigflag.png | Bin 0 -> 6066 bytes .../Games/MineSweeper/image/cover.png | Bin 0 -> 331 bytes .../Games/MineSweeper/image/coveron.png | Bin 0 -> 238 bytes .../Games/MineSweeper/image/empty.png | Bin 0 -> 97 bytes .../Games/MineSweeper/image/expr_lose.png | Bin 0 -> 1941 bytes .../Games/MineSweeper/image/expr_normal.png | Bin 0 -> 1869 bytes .../Games/MineSweeper/image/expr_o.png | Bin 0 -> 1462 bytes .../Games/MineSweeper/image/expr_win.png | Bin 0 -> 2259 bytes .../Games/MineSweeper/image/false.png | Bin 0 -> 1021 bytes app/examples/Games/MineSweeper/image/flag.png | Bin 0 -> 765 bytes app/examples/Games/MineSweeper/image/mine.png | Bin 0 -> 755 bytes .../Games/MineSweeper/image/number_1.png | Bin 0 -> 388 bytes .../Games/MineSweeper/image/number_2.png | Bin 0 -> 407 bytes .../Games/MineSweeper/image/number_3.png | Bin 0 -> 428 bytes .../Games/MineSweeper/image/number_4.png | Bin 0 -> 387 bytes .../Games/MineSweeper/image/number_5.png | Bin 0 -> 381 bytes .../Games/MineSweeper/image/number_6.png | Bin 0 -> 462 bytes .../Games/MineSweeper/image/number_7.png | Bin 0 -> 367 bytes .../Games/MineSweeper/image/number_8.png | Bin 0 -> 454 bytes app/examples/Games/Pong/.directory | 2 + app/examples/Games/Pong/.icon.png | Bin 0 -> 4849 bytes app/examples/Games/Pong/.project | 13 + app/examples/Games/Pong/.src/Ball.class | 145 + app/examples/Games/Pong/.src/MMain.module | 191 + app/examples/Games/Pong/.src/NPC.class | 116 + app/examples/Games/Pong/.src/Paddle.class | 96 + app/examples/Games/Pong/SPEED | 47 + app/examples/Games/Pong/pong.png | Bin 0 -> 89 bytes app/examples/Games/Puzzle1To8/.directory | 2 + app/examples/Games/Puzzle1To8/.icon.png | Bin 0 -> 4303 bytes app/examples/Games/Puzzle1To8/.lang/ca.mo | Bin 0 -> 3803 bytes app/examples/Games/Puzzle1To8/.lang/ca.po | 124 + app/examples/Games/Puzzle1To8/.lang/cs.mo | Bin 0 -> 3007 bytes app/examples/Games/Puzzle1To8/.lang/cs.po | 104 + app/examples/Games/Puzzle1To8/.lang/de.mo | Bin 0 -> 3599 bytes app/examples/Games/Puzzle1To8/.lang/de.po | 112 + app/examples/Games/Puzzle1To8/.lang/es_AR.mo | Bin 0 -> 3064 bytes app/examples/Games/Puzzle1To8/.lang/es_AR.po | 97 + app/examples/Games/Puzzle1To8/.project | 18 + .../Games/Puzzle1To8/.src/Casillero.class | 66 + .../Games/Puzzle1To8/.src/Esquema.class | 110 + .../Games/Puzzle1To8/.src/FMain.class | 111 + app/examples/Games/Puzzle1To8/.src/FMain.form | 103 + .../Games/Puzzle1To8/.src/FrmAbout.class | 18 + .../Games/Puzzle1To8/.src/FrmAbout.form | 39 + .../Games/Puzzle1To8/.src/FrmAyuda.class | 18 + .../Games/Puzzle1To8/.src/FrmAyuda.form | 37 + app/examples/Games/Puzzle1To8/Licence | 189 + app/examples/Games/Puzzle1To8/ejemplo1.png | Bin 0 -> 5594 bytes app/examples/Games/Puzzle1To8/ejemplo2.png | Bin 0 -> 5481 bytes app/examples/Games/Puzzle1To8/logo.png | Bin 0 -> 41279 bytes .../Games/RobotFindsKitten/.directory | 2 + app/examples/Games/RobotFindsKitten/.icon.png | Bin 0 -> 4141 bytes .../Games/RobotFindsKitten/.lang/ca.mo | Bin 0 -> 1230 bytes .../Games/RobotFindsKitten/.lang/ca.po | 40 + .../Games/RobotFindsKitten/.lang/cs.mo | Bin 0 -> 1157 bytes .../Games/RobotFindsKitten/.lang/cs.po | 32 + .../Games/RobotFindsKitten/.lang/de.mo | Bin 0 -> 1233 bytes .../Games/RobotFindsKitten/.lang/de.po | 32 + .../Games/RobotFindsKitten/.lang/es.mo | Bin 0 -> 1080 bytes .../Games/RobotFindsKitten/.lang/es.po | 28 + app/examples/Games/RobotFindsKitten/.project | 17 + .../Games/RobotFindsKitten/.src/Frfk.class | 186 + .../Games/RobotFindsKitten/.src/Frfk.form | 32 + app/examples/Games/RobotFindsKitten/COPYING | 340 + app/examples/Games/RobotFindsKitten/heart.png | Bin 0 -> 9149 bytes app/examples/Games/RobotFindsKitten/nkis.txt | 622 + .../Games/RobotFindsKitten/readme.txt | 24 + app/examples/Games/Snake/.directory | 2 + app/examples/Games/Snake/.icon.png | Bin 0 -> 3584 bytes app/examples/Games/Snake/.lang/ca.mo | Bin 0 -> 1682 bytes app/examples/Games/Snake/.lang/ca.po | 102 + app/examples/Games/Snake/.lang/cs.mo | Bin 0 -> 1581 bytes app/examples/Games/Snake/.lang/cs.po | 114 + app/examples/Games/Snake/.lang/de.mo | Bin 0 -> 1630 bytes app/examples/Games/Snake/.lang/de.po | 101 + app/examples/Games/Snake/.project | 18 + app/examples/Games/Snake/.src/FrmMain.class | 430 + app/examples/Games/Snake/.src/FrmMain.form | 89 + app/examples/Games/Snake/apple.png | Bin 0 -> 1141 bytes app/examples/Games/Snake/body.png | Bin 0 -> 1138 bytes app/examples/Games/Snake/dead.wav | Bin 0 -> 10026 bytes app/examples/Games/Snake/eat.wav | Bin 0 -> 1192 bytes app/examples/Games/Snake/head.png | Bin 0 -> 1197 bytes app/examples/Games/Snake/start.wav | Bin 0 -> 31810 bytes app/examples/Games/Solitaire/.directory | 2 + app/examples/Games/Solitaire/.icon.png | Bin 0 -> 4209 bytes app/examples/Games/Solitaire/.lang/ca.mo | Bin 0 -> 1343 bytes app/examples/Games/Solitaire/.lang/ca.po | 96 + app/examples/Games/Solitaire/.lang/cs.mo | Bin 0 -> 1258 bytes app/examples/Games/Solitaire/.lang/cs.po | 94 + app/examples/Games/Solitaire/.lang/de.mo | Bin 0 -> 1301 bytes app/examples/Games/Solitaire/.lang/de.po | 89 + app/examples/Games/Solitaire/.lang/es.mo | Bin 0 -> 1004 bytes app/examples/Games/Solitaire/.lang/es.po | 79 + app/examples/Games/Solitaire/.project | 17 + .../Games/Solitaire/.src/CBoardDesign.class | 9 + app/examples/Games/Solitaire/.src/CMove.class | 5 + .../Games/Solitaire/.src/FBoardSelect.class | 35 + .../Games/Solitaire/.src/FBoardSelect.form | 23 + .../Games/Solitaire/.src/FGameArea.class | 368 + .../Games/Solitaire/.src/FGameArea.form | 64 + .../Games/Solitaire/.src/Global.class | 11 + .../Games/Solitaire/.src/MBoards.module | 69 + app/examples/Games/Solitaire/ball.png | Bin 0 -> 3327 bytes app/examples/Games/Solitaire/new.png | Bin 0 -> 155 bytes app/examples/Games/Solitaire/quit.png | Bin 0 -> 179 bytes app/examples/Games/Solitaire/redo.png | Bin 0 -> 131 bytes app/examples/Games/Solitaire/undo.png | Bin 0 -> 131 bytes app/examples/Games/StarField/.directory | 2 + app/examples/Games/StarField/.icon.png | Bin 0 -> 3140 bytes app/examples/Games/StarField/.project | 13 + .../Games/StarField/.src/MMain.module | 82 + app/examples/Games/StarField/enterprise.png | Bin 0 -> 4623 bytes app/examples/Games/StarField/logo.png | Bin 0 -> 12795 bytes app/examples/Image/ImageViewer/.directory | 2 + app/examples/Image/ImageViewer/.icon.png | Bin 0 -> 4419 bytes app/examples/Image/ImageViewer/.lang/ca.mo | Bin 0 -> 978 bytes app/examples/Image/ImageViewer/.lang/ca.po | 64 + app/examples/Image/ImageViewer/.lang/cs.mo | Bin 0 -> 945 bytes app/examples/Image/ImageViewer/.lang/cs.po | 60 + app/examples/Image/ImageViewer/.lang/de.mo | Bin 0 -> 928 bytes app/examples/Image/ImageViewer/.lang/de.po | 57 + app/examples/Image/ImageViewer/.lang/es.mo | Bin 0 -> 624 bytes app/examples/Image/ImageViewer/.lang/es.po | 43 + app/examples/Image/ImageViewer/.lang/nl.mo | Bin 0 -> 907 bytes app/examples/Image/ImageViewer/.lang/nl.po | 55 + app/examples/Image/ImageViewer/.project | 20 + .../Image/ImageViewer/.src/FViewer.class | 106 + .../Image/ImageViewer/.src/FViewer.form | 52 + app/examples/Image/ImageViewer/image.png | Bin 0 -> 8428 bytes app/examples/Image/ImageViewer/test.png | Bin 0 -> 110 bytes .../Image/Lighttable/.action/FMain.action | 10 + app/examples/Image/Lighttable/.directory | 2 + app/examples/Image/Lighttable/.icon.png | Bin 0 -> 4434 bytes app/examples/Image/Lighttable/.lang/ca.mo | Bin 0 -> 6923 bytes app/examples/Image/Lighttable/.lang/ca.po | 337 + app/examples/Image/Lighttable/.lang/cs.mo | Bin 0 -> 6606 bytes app/examples/Image/Lighttable/.lang/cs.po | 330 + app/examples/Image/Lighttable/.lang/de.mo | Bin 0 -> 6654 bytes app/examples/Image/Lighttable/.lang/de.po | 330 + app/examples/Image/Lighttable/.lang/en.mo | Bin 0 -> 303 bytes app/examples/Image/Lighttable/.lang/en.po | 264 + app/examples/Image/Lighttable/.lang/nl.mo | Bin 0 -> 6754 bytes app/examples/Image/Lighttable/.lang/nl.po | 327 + app/examples/Image/Lighttable/.project | 27 + .../Image/Lighttable/.src/FHelp.class | 28 + app/examples/Image/Lighttable/.src/FHelp.form | 26 + .../Image/Lighttable/.src/FInfo.class | 37 + app/examples/Image/Lighttable/.src/FInfo.form | 25 + .../Image/Lighttable/.src/FMain.class | 903 + app/examples/Image/Lighttable/.src/FMain.form | 162 + .../Image/Lighttable/.src/FRename.class | 58 + .../Image/Lighttable/.src/FRename.form | 25 + .../Image/Lighttable/.src/FRenameAll.class | 101 + .../Image/Lighttable/.src/FRenameAll.form | 77 + .../Lighttable/.src/FRenameAllWarning.class | 19 + .../Lighttable/.src/FRenameAllWarning.form | 29 + .../Image/Lighttable/.src/FSlideshow.class | 56 + .../Image/Lighttable/.src/FSlideshow.form | 46 + .../Image/Lighttable/.src/FStart.class | 135 + .../Image/Lighttable/.src/FStart.form | 42 + .../Image/Lighttable/.src/FTime.class | 27 + app/examples/Image/Lighttable/.src/FTime.form | 58 + .../Image/Lighttable/.src/MMain.module | 53 + app/examples/Image/Lighttable/CHANGELOG | 3 + app/examples/Image/Lighttable/FStart.class | 92 + app/examples/Image/Lighttable/FStart.form | 35 + app/examples/Image/Lighttable/Help_ca.html | 60 + app/examples/Image/Lighttable/Help_de.html | 74 + app/examples/Image/Lighttable/Help_en.html | 71 + app/examples/Image/Lighttable/LTicon.png | Bin 0 -> 296 bytes app/examples/Image/Lighttable/Liesmich.txt | 33 + app/examples/Image/Lighttable/Readme.txt | 33 + app/examples/Image/Lighttable/close.png | Bin 0 -> 596 bytes app/examples/Image/Lighttable/hand1.png | Bin 0 -> 176 bytes .../Image/Lighttable/help-contents.png | Bin 0 -> 742 bytes app/examples/Image/Lighttable/lighttable.png | Bin 0 -> 6536 bytes app/examples/Image/Lighttable/move.png | Bin 0 -> 169 bytes app/examples/Image/Lighttable/zoom-in.png | Bin 0 -> 710 bytes app/examples/Image/PhotoTouch/.directory | 2 + .../.hidden/screenshots/phototouch.jpg | Bin 0 -> 150309 bytes app/examples/Image/PhotoTouch/.icon.png | Bin 0 -> 3488 bytes app/examples/Image/PhotoTouch/.lang/fr.mo | Bin 0 -> 2197 bytes app/examples/Image/PhotoTouch/.lang/fr.po | 184 + app/examples/Image/PhotoTouch/.lang/nl.mo | Bin 0 -> 2345 bytes app/examples/Image/PhotoTouch/.lang/nl.po | 183 + app/examples/Image/PhotoTouch/.project | 24 + .../Image/PhotoTouch/.src/CAnimation.class | 97 + .../Image/PhotoTouch/.src/CButton.class | 189 + .../Image/PhotoTouch/.src/FBrightness.class | 146 + .../Image/PhotoTouch/.src/FBrightness.form | 37 + .../Image/PhotoTouch/.src/FMain.class | 1117 + app/examples/Image/PhotoTouch/.src/FMain.form | 65 + .../Image/PhotoTouch/.src/FResize.class | 57 + .../Image/PhotoTouch/.src/FResize.form | 40 + .../Image/PhotoTouch/.src/FScissors.class | 260 + .../Image/PhotoTouch/.src/FScissors.form | 38 + app/examples/Image/PhotoTouch/balance.png | Bin 0 -> 1125 bytes app/examples/Image/PhotoTouch/blur.png | Bin 0 -> 1309 bytes app/examples/Image/PhotoTouch/brightness.png | Bin 0 -> 1534 bytes app/examples/Image/PhotoTouch/contrast.png | Bin 0 -> 1340 bytes app/examples/Image/PhotoTouch/delete.png | Bin 0 -> 853 bytes app/examples/Image/PhotoTouch/despeckle.png | Bin 0 -> 1530 bytes app/examples/Image/PhotoTouch/film.png | Bin 0 -> 756 bytes app/examples/Image/PhotoTouch/gamma.png | Bin 0 -> 693 bytes app/examples/Image/PhotoTouch/hflip.png | Bin 0 -> 953 bytes app/examples/Image/PhotoTouch/icon.png | Bin 0 -> 4904 bytes app/examples/Image/PhotoTouch/invert.png | Bin 0 -> 1942 bytes app/examples/Image/PhotoTouch/magic.png | Bin 0 -> 1093 bytes app/examples/Image/PhotoTouch/next.png | Bin 0 -> 749 bytes app/examples/Image/PhotoTouch/normalize.png | Bin 0 -> 910 bytes app/examples/Image/PhotoTouch/oil.png | Bin 0 -> 1189 bytes app/examples/Image/PhotoTouch/ok.png | Bin 0 -> 655 bytes app/examples/Image/PhotoTouch/photo.png | Bin 0 -> 1068 bytes app/examples/Image/PhotoTouch/previous.png | Bin 0 -> 724 bytes app/examples/Image/PhotoTouch/quit.png | Bin 0 -> 1034 bytes app/examples/Image/PhotoTouch/redo.png | Bin 0 -> 1042 bytes app/examples/Image/PhotoTouch/resize.png | Bin 0 -> 852 bytes app/examples/Image/PhotoTouch/rotate-left.png | Bin 0 -> 1204 bytes .../Image/PhotoTouch/rotate-right.png | Bin 0 -> 1192 bytes app/examples/Image/PhotoTouch/save-all.png | Bin 0 -> 998 bytes app/examples/Image/PhotoTouch/save.png | Bin 0 -> 674 bytes app/examples/Image/PhotoTouch/scissors.png | Bin 0 -> 1549 bytes app/examples/Image/PhotoTouch/sharpen.png | Bin 0 -> 1301 bytes app/examples/Image/PhotoTouch/undo.png | Bin 0 -> 1071 bytes app/examples/Image/PhotoTouch/usb.png | Bin 0 -> 739 bytes app/examples/Image/PhotoTouch/vflip.png | Bin 0 -> 1018 bytes app/examples/Image/PhotoTouch/zoom-fit.png | Bin 0 -> 1571 bytes app/examples/Image/PhotoTouch/zoom-in.png | Bin 0 -> 1634 bytes .../Image/PhotoTouch/zoom-original.png | Bin 0 -> 1615 bytes app/examples/Image/PhotoTouch/zoom-out.png | Bin 0 -> 1568 bytes app/examples/Misc/Console/.directory | 2 + app/examples/Misc/Console/.icon.png | Bin 0 -> 4129 bytes app/examples/Misc/Console/.lang/fr.mo | Bin 0 -> 303 bytes app/examples/Misc/Console/.lang/fr.po | 19 + app/examples/Misc/Console/.project | 16 + app/examples/Misc/Console/.src/FConsole.class | 162 + app/examples/Misc/Console/.src/FConsole.form | 37 + app/examples/Misc/Console/terminal.png | Bin 0 -> 2312 bytes app/examples/Misc/DBusExplorer/.directory | 2 + .../.hidden/screenshots/2014-12-14.png | Bin 0 -> 131380 bytes app/examples/Misc/DBusExplorer/.icon.png | Bin 0 -> 3487 bytes app/examples/Misc/DBusExplorer/.project | 18 + .../DBusExplorer/.src/FVersiongbXML.class | 272 + .../Misc/DBusExplorer/.src/FVersiongbXML.form | 127 + app/examples/Misc/DBusExplorer/dbus22.png | Bin 0 -> 894 bytes app/examples/Misc/DBusExplorer/dbus64.png | Bin 0 -> 4222 bytes app/examples/Misc/DBusExplorer/method.png | Bin 0 -> 724 bytes app/examples/Misc/DBusExplorer/property.png | Bin 0 -> 728 bytes app/examples/Misc/DBusExplorer/signal.png | Bin 0 -> 780 bytes app/examples/Misc/Evaluator/.directory | 2 + app/examples/Misc/Evaluator/.icon.png | Bin 0 -> 4208 bytes app/examples/Misc/Evaluator/.lang/ca.mo | Bin 0 -> 1092 bytes app/examples/Misc/Evaluator/.lang/ca.po | 76 + app/examples/Misc/Evaluator/.lang/cs.mo | Bin 0 -> 1007 bytes app/examples/Misc/Evaluator/.lang/cs.po | 68 + app/examples/Misc/Evaluator/.lang/de.mo | Bin 0 -> 1014 bytes app/examples/Misc/Evaluator/.lang/de.po | 69 + app/examples/Misc/Evaluator/.lang/es.mo | Bin 0 -> 974 bytes app/examples/Misc/Evaluator/.lang/es.po | 71 + app/examples/Misc/Evaluator/.project | 18 + app/examples/Misc/Evaluator/.src/FEval.class | 53 + app/examples/Misc/Evaluator/.src/FEval.form | 83 + app/examples/Misc/Evaluator/calculator.png | Bin 0 -> 3612 bytes app/examples/Misc/Explorer/.directory | 2 + app/examples/Misc/Explorer/.icon.png | Bin 0 -> 3277 bytes app/examples/Misc/Explorer/.lang/ca.mo | Bin 0 -> 881 bytes app/examples/Misc/Explorer/.lang/ca.po | 56 + app/examples/Misc/Explorer/.lang/cs.mo | Bin 0 -> 816 bytes app/examples/Misc/Explorer/.lang/cs.po | 52 + app/examples/Misc/Explorer/.lang/de.mo | Bin 0 -> 828 bytes app/examples/Misc/Explorer/.lang/de.po | 49 + app/examples/Misc/Explorer/.lang/es.mo | Bin 0 -> 564 bytes app/examples/Misc/Explorer/.lang/es.po | 35 + app/examples/Misc/Explorer/.project | 19 + .../Misc/Explorer/.src/FExplorer.class | 175 + .../Misc/Explorer/.src/FExplorer.form | 38 + app/examples/Misc/Explorer/folder.png | Bin 0 -> 2450 bytes app/examples/Misc/Notepad/.directory | 2 + app/examples/Misc/Notepad/.icon.png | Bin 0 -> 4288 bytes app/examples/Misc/Notepad/.lang/ca.mo | Bin 0 -> 1869 bytes app/examples/Misc/Notepad/.lang/ca.po | 148 + app/examples/Misc/Notepad/.lang/cs.mo | Bin 0 -> 1818 bytes app/examples/Misc/Notepad/.lang/cs.po | 150 + app/examples/Misc/Notepad/.lang/de.mo | Bin 0 -> 1833 bytes app/examples/Misc/Notepad/.lang/de.po | 141 + app/examples/Misc/Notepad/.lang/es.mo | Bin 0 -> 1210 bytes app/examples/Misc/Notepad/.lang/es.po | 95 + app/examples/Misc/Notepad/.project | 19 + app/examples/Misc/Notepad/.src/FAbout.class | 25 + app/examples/Misc/Notepad/.src/FAbout.form | 22 + app/examples/Misc/Notepad/.src/FNotepad.class | 222 + app/examples/Misc/Notepad/.src/FNotepad.form | 93 + app/examples/Misc/Notepad/notepad.png | Bin 0 -> 4015 bytes app/examples/Misc/PDFViewer/.directory | 2 + app/examples/Misc/PDFViewer/.icon.png | Bin 0 -> 4253 bytes app/examples/Misc/PDFViewer/.lang/ca.mo | Bin 0 -> 1300 bytes app/examples/Misc/PDFViewer/.lang/ca.po | 80 + app/examples/Misc/PDFViewer/.lang/cs.mo | Bin 0 -> 1407 bytes app/examples/Misc/PDFViewer/.lang/cs.po | 80 + app/examples/Misc/PDFViewer/.lang/de.mo | Bin 0 -> 1402 bytes app/examples/Misc/PDFViewer/.lang/de.po | 80 + app/examples/Misc/PDFViewer/.lang/es.mo | Bin 0 -> 1120 bytes app/examples/Misc/PDFViewer/.lang/es.po | 64 + app/examples/Misc/PDFViewer/.project | 22 + app/examples/Misc/PDFViewer/.src/FMain.class | 472 + app/examples/Misc/PDFViewer/.src/FMain.form | 126 + app/examples/Misc/PDFViewer/.src/Fabout.class | 43 + app/examples/Misc/PDFViewer/.src/Fabout.form | 32 + app/examples/Misc/PDFViewer/pdf.png | Bin 0 -> 4588 bytes app/examples/Misc/SystemTray/.directory | 2 + app/examples/Misc/SystemTray/.icon.png | Bin 0 -> 3462 bytes app/examples/Misc/SystemTray/.project | 14 + app/examples/Misc/SystemTray/.src/FMain.class | 64 + app/examples/Misc/SystemTray/.src/FMain.form | 14 + app/examples/Misc/SystemTray/bg.png | Bin 0 -> 163 bytes app/examples/Misc/SystemTray/icon.png | Bin 0 -> 4363 bytes .../Misc/WatchGambasDirectory/.directory | 2 + .../Misc/WatchGambasDirectory/.icon.png | Bin 0 -> 3409 bytes .../Misc/WatchGambasDirectory/.project | 13 + .../WatchGambasDirectory/.src/MMain.module | 54 + .../Misc/WatchGambasDirectory/watch.svg | 1257 + app/examples/Multimedia/CDPlayer/.directory | 2 + app/examples/Multimedia/CDPlayer/.icon.png | Bin 0 -> 4311 bytes app/examples/Multimedia/CDPlayer/.lang/ca.mo | Bin 0 -> 883 bytes app/examples/Multimedia/CDPlayer/.lang/ca.po | 60 + app/examples/Multimedia/CDPlayer/.lang/cs.mo | Bin 0 -> 798 bytes app/examples/Multimedia/CDPlayer/.lang/cs.po | 52 + app/examples/Multimedia/CDPlayer/.lang/es.mo | Bin 0 -> 627 bytes app/examples/Multimedia/CDPlayer/.lang/es.po | 44 + app/examples/Multimedia/CDPlayer/.project | 19 + .../Multimedia/CDPlayer/.src/Fcdplayer.class | 139 + .../Multimedia/CDPlayer/.src/Fcdplayer.form | 47 + app/examples/Multimedia/CDPlayer/cdrom.png | Bin 0 -> 3987 bytes .../Multimedia/MediaPlayer/.directory | 2 + .../.hidden/screenshots/MediaPlayer.jpg | Bin 0 -> 144138 bytes app/examples/Multimedia/MediaPlayer/.icon.png | Bin 0 -> 3571 bytes .../Multimedia/MediaPlayer/.lang/fr.mo | Bin 0 -> 1032 bytes .../Multimedia/MediaPlayer/.lang/fr.po | 64 + app/examples/Multimedia/MediaPlayer/.project | 55 + .../MediaPlayer/.src/CAnimation.class | 101 + .../Multimedia/MediaPlayer/.src/CButton.class | 215 + .../MediaPlayer/.src/FControl.class | 133 + .../Multimedia/MediaPlayer/.src/FControl.form | 37 + .../Multimedia/MediaPlayer/.src/FMain.class | 616 + .../Multimedia/MediaPlayer/.src/FMain.form | 18 + .../Multimedia/MediaPlayer/.src/FTags.class | 147 + .../Multimedia/MediaPlayer/.src/FTags.form | 91 + .../Multimedia/MediaPlayer/.src/MTest.module | 67 + .../Multimedia/MediaPlayer/brightness.png | Bin 0 -> 1534 bytes .../Multimedia/MediaPlayer/config.png | Bin 0 -> 831 bytes .../Multimedia/MediaPlayer/contrast.png | Bin 0 -> 1340 bytes app/examples/Multimedia/MediaPlayer/eject.png | Bin 0 -> 1004 bytes app/examples/Multimedia/MediaPlayer/film.png | Bin 0 -> 756 bytes .../Multimedia/MediaPlayer/fullscreen.png | Bin 0 -> 598 bytes app/examples/Multimedia/MediaPlayer/gamma.png | Bin 0 -> 693 bytes app/examples/Multimedia/MediaPlayer/icon.png | Bin 0 -> 2509 bytes app/examples/Multimedia/MediaPlayer/info.png | Bin 0 -> 2413 bytes app/examples/Multimedia/MediaPlayer/mute.png | Bin 0 -> 958 bytes app/examples/Multimedia/MediaPlayer/pause.png | Bin 0 -> 393 bytes app/examples/Multimedia/MediaPlayer/play.png | Bin 0 -> 757 bytes app/examples/Multimedia/MediaPlayer/quit.png | Bin 0 -> 1034 bytes .../Multimedia/MediaPlayer/screenshot.png | Bin 0 -> 1090 bytes .../Multimedia/MediaPlayer/seek-backward.png | Bin 0 -> 1316 bytes .../Multimedia/MediaPlayer/seek-forward.png | Bin 0 -> 1283 bytes .../Multimedia/MediaPlayer/skip-backward.png | Bin 0 -> 1093 bytes .../Multimedia/MediaPlayer/skip-forward.png | Bin 0 -> 1085 bytes app/examples/Multimedia/MediaPlayer/stop.png | Bin 0 -> 495 bytes .../Multimedia/MediaPlayer/subtitle.png | Bin 0 -> 1174 bytes app/examples/Multimedia/MediaPlayer/undo.png | Bin 0 -> 1091 bytes app/examples/Multimedia/MediaPlayer/video.png | Bin 0 -> 762 bytes .../Multimedia/MediaPlayer/visualisation.png | Bin 0 -> 1093 bytes .../Multimedia/MediaPlayer/volume-0.png | Bin 0 -> 908 bytes .../Multimedia/MediaPlayer/volume-1.png | Bin 0 -> 785 bytes .../Multimedia/MediaPlayer/volume-2.png | Bin 0 -> 1052 bytes .../Multimedia/MediaPlayer/volume-3.png | Bin 0 -> 1384 bytes .../Multimedia/MediaPlayer/volume.png | Bin 0 -> 1384 bytes .../Multimedia/MediaPlayer/zoom-in.png | Bin 0 -> 1634 bytes .../Multimedia/MoviePlayer/.directory | 2 + app/examples/Multimedia/MoviePlayer/.icon.png | Bin 0 -> 4342 bytes .../Multimedia/MoviePlayer/.lang/ca.mo | Bin 0 -> 804 bytes .../Multimedia/MoviePlayer/.lang/ca.po | 32 + .../Multimedia/MoviePlayer/.lang/cs.mo | Bin 0 -> 708 bytes .../Multimedia/MoviePlayer/.lang/cs.po | 30 + .../Multimedia/MoviePlayer/.lang/es.mo | Bin 0 -> 611 bytes .../Multimedia/MoviePlayer/.lang/es.po | 27 + app/examples/Multimedia/MoviePlayer/.project | 18 + .../MoviePlayer/.src/FMoviePlayer.class | 136 + .../MoviePlayer/.src/FMoviePlayer.form | 42 + app/examples/Multimedia/MoviePlayer/video.png | Bin 0 -> 5244 bytes .../Multimedia/MusicPlayer/.directory | 2 + app/examples/Multimedia/MusicPlayer/.icon.png | Bin 0 -> 4226 bytes .../Multimedia/MusicPlayer/.icon/16.png | Bin 0 -> 288 bytes .../Multimedia/MusicPlayer/.icon/32.png | Bin 0 -> 272 bytes .../Multimedia/MusicPlayer/.icon/48.png | Bin 0 -> 555 bytes .../Multimedia/MusicPlayer/.lang/ca.mo | Bin 0 -> 542 bytes .../Multimedia/MusicPlayer/.lang/ca.po | 32 + .../Multimedia/MusicPlayer/.lang/cs.mo | Bin 0 -> 488 bytes .../Multimedia/MusicPlayer/.lang/cs.po | 24 + .../Multimedia/MusicPlayer/.lang/es.mo | Bin 0 -> 453 bytes .../Multimedia/MusicPlayer/.lang/es.po | 24 + .../Multimedia/MusicPlayer/.lang/fr.mo | Bin 0 -> 493 bytes .../Multimedia/MusicPlayer/.lang/fr.po | 35 + app/examples/Multimedia/MusicPlayer/.project | 25 + .../MusicPlayer/.src/FSoundPlayer.class | 145 + .../MusicPlayer/.src/FSoundPlayer.form | 62 + app/examples/Multimedia/MusicPlayer/sound.png | Bin 0 -> 3551 bytes app/examples/Multimedia/MyWebCam/.directory | 2 + app/examples/Multimedia/MyWebCam/.icon.png | Bin 0 -> 4360 bytes app/examples/Multimedia/MyWebCam/.lang/ca.mo | Bin 0 -> 1259 bytes app/examples/Multimedia/MyWebCam/.lang/ca.po | 107 + app/examples/Multimedia/MyWebCam/.lang/cs.mo | Bin 0 -> 1288 bytes app/examples/Multimedia/MyWebCam/.lang/cs.po | 112 + app/examples/Multimedia/MyWebCam/.lang/es.mo | Bin 0 -> 1177 bytes app/examples/Multimedia/MyWebCam/.lang/es.po | 95 + app/examples/Multimedia/MyWebCam/.project | 19 + .../Multimedia/MyWebCam/.src/Form1.class | 203 + .../Multimedia/MyWebCam/.src/Form1.form | 122 + app/examples/Multimedia/MyWebCam/camera.png | Bin 0 -> 5363 bytes .../Multimedia/WaveGenerator/.directory | 2 + .../Multimedia/WaveGenerator/.icon.png | Bin 0 -> 4098 bytes .../Multimedia/WaveGenerator/.project | 13 + .../Multimedia/WaveGenerator/.src/FMain.class | 102 + .../Multimedia/WaveGenerator/.src/FMain.form | 61 + .../WaveGenerator/audio-headphones.png | Bin 0 -> 2578 bytes app/examples/Multimedia/WebCam/.directory | 2 + app/examples/Multimedia/WebCam/.icon.png | Bin 0 -> 4272 bytes app/examples/Multimedia/WebCam/.project | 15 + .../Multimedia/WebCam/.src/FDevice.class | 306 + .../Multimedia/WebCam/.src/FDevice.form | 151 + .../Multimedia/WebCam/.src/FMain.class | 126 + .../Multimedia/WebCam/.src/FMain.form | 68 + app/examples/Multimedia/WebCam/camera.png | Bin 0 -> 1027 bytes app/examples/Multimedia/WebCam/settings.png | Bin 0 -> 248 bytes .../Networking/ClientSocket/.directory | 2 + .../Networking/ClientSocket/.icon.png | Bin 0 -> 3551 bytes .../Networking/ClientSocket/.lang/ca.mo | Bin 0 -> 2155 bytes .../Networking/ClientSocket/.lang/ca.po | 127 + .../Networking/ClientSocket/.lang/cs.mo | Bin 0 -> 2000 bytes .../Networking/ClientSocket/.lang/cs.po | 120 + .../Networking/ClientSocket/.lang/de.mo | Bin 0 -> 1989 bytes .../Networking/ClientSocket/.lang/de.po | 120 + .../Networking/ClientSocket/.lang/es.mo | Bin 0 -> 946 bytes .../Networking/ClientSocket/.lang/es.po | 63 + .../Networking/ClientSocket/.lang/nl.mo | Bin 0 -> 2064 bytes .../Networking/ClientSocket/.lang/nl.po | 120 + app/examples/Networking/ClientSocket/.project | 20 + .../ClientSocket/.src/FrmMain.class | 231 + .../Networking/ClientSocket/.src/FrmMain.form | 75 + .../Networking/ClientSocket/socket.png | Bin 0 -> 2200 bytes app/examples/Networking/DnsClient/.directory | 2 + app/examples/Networking/DnsClient/.icon.png | Bin 0 -> 4317 bytes app/examples/Networking/DnsClient/.lang/ca.mo | Bin 0 -> 1368 bytes app/examples/Networking/DnsClient/.lang/ca.po | 100 + app/examples/Networking/DnsClient/.lang/cs.mo | Bin 0 -> 1307 bytes app/examples/Networking/DnsClient/.lang/cs.po | 92 + app/examples/Networking/DnsClient/.lang/de.mo | Bin 0 -> 1349 bytes app/examples/Networking/DnsClient/.lang/de.po | 93 + app/examples/Networking/DnsClient/.lang/es.mo | Bin 0 -> 1038 bytes app/examples/Networking/DnsClient/.lang/es.po | 67 + app/examples/Networking/DnsClient/.lang/nl.mo | Bin 0 -> 1318 bytes app/examples/Networking/DnsClient/.lang/nl.po | 92 + app/examples/Networking/DnsClient/.project | 17 + .../Networking/DnsClient/.src/FMain.class | 308 + .../Networking/DnsClient/.src/FMain.form | 122 + .../Networking/DnsClient/dnsclient.png | Bin 0 -> 1808 bytes app/examples/Networking/HTTPGet/.directory | 2 + app/examples/Networking/HTTPGet/.icon.png | Bin 0 -> 3515 bytes app/examples/Networking/HTTPGet/.lang/ca.mo | Bin 0 -> 1708 bytes app/examples/Networking/HTTPGet/.lang/ca.po | 124 + app/examples/Networking/HTTPGet/.lang/cs.mo | Bin 0 -> 1601 bytes app/examples/Networking/HTTPGet/.lang/cs.po | 121 + app/examples/Networking/HTTPGet/.lang/de.mo | Bin 0 -> 1608 bytes app/examples/Networking/HTTPGet/.lang/de.po | 117 + .../Networking/HTTPGet/.lang/en_GB.mo | Bin 0 -> 1326 bytes .../Networking/HTTPGet/.lang/en_GB.po | 120 + app/examples/Networking/HTTPGet/.lang/es.mo | Bin 0 -> 1308 bytes app/examples/Networking/HTTPGet/.lang/es.po | 91 + app/examples/Networking/HTTPGet/.lang/nl.mo | Bin 0 -> 1628 bytes app/examples/Networking/HTTPGet/.lang/nl.po | 120 + app/examples/Networking/HTTPGet/.project | 22 + .../Networking/HTTPGet/.src/ClsParams.class | 8 + .../Networking/HTTPGet/.src/FConfig.class | 75 + .../Networking/HTTPGet/.src/FConfig.form | 64 + .../Networking/HTTPGet/.src/FHttpGet.class | 285 + .../Networking/HTTPGet/.src/FHttpGet.form | 65 + .../Networking/HTTPGet/httpclient.png | Bin 0 -> 1768 bytes app/examples/Networking/HTTPPost/.directory | 2 + app/examples/Networking/HTTPPost/.icon.png | Bin 0 -> 3515 bytes app/examples/Networking/HTTPPost/.lang/ca.mo | Bin 0 -> 1304 bytes app/examples/Networking/HTTPPost/.lang/ca.po | 80 + app/examples/Networking/HTTPPost/.lang/cs.mo | Bin 0 -> 1209 bytes app/examples/Networking/HTTPPost/.lang/cs.po | 72 + app/examples/Networking/HTTPPost/.lang/de.mo | Bin 0 -> 1202 bytes app/examples/Networking/HTTPPost/.lang/de.po | 73 + app/examples/Networking/HTTPPost/.lang/es.mo | Bin 0 -> 1011 bytes app/examples/Networking/HTTPPost/.lang/es.po | 56 + app/examples/Networking/HTTPPost/.lang/nl.mo | Bin 0 -> 1255 bytes app/examples/Networking/HTTPPost/.lang/nl.po | 72 + app/examples/Networking/HTTPPost/.project | 20 + .../Networking/HTTPPost/.src/FHttpPost.class | 105 + .../Networking/HTTPPost/.src/FHttpPost.form | 85 + .../Networking/HTTPPost/httpclient.png | Bin 0 -> 1768 bytes app/examples/Networking/POPMailbox/.directory | 2 + app/examples/Networking/POPMailbox/.icon.png | Bin 0 -> 4202 bytes app/examples/Networking/POPMailbox/.project | 18 + .../Networking/POPMailbox/.src/FMain.class | 106 + .../Networking/POPMailbox/.src/FMain.form | 50 + .../POPMailbox/.src/FSettings.class | 68 + .../Networking/POPMailbox/.src/FSettings.form | 66 + .../Networking/POPMailbox/pop3client.png | Bin 0 -> 957 bytes app/examples/Networking/SerialPort/.directory | 2 + app/examples/Networking/SerialPort/.icon.png | Bin 0 -> 4320 bytes app/examples/Networking/SerialPort/.project | 15 + .../Networking/SerialPort/.src/FAbout.class | 16 + .../Networking/SerialPort/.src/FAbout.form | 32 + .../Networking/SerialPort/.src/FMain.class | 246 + .../Networking/SerialPort/.src/FMain.form | 189 + .../SerialPort/.src/Module_Config.module | 92 + .../SerialPort/.src/Module_RS232.module | 93 + .../Networking/SerialPort/serialport.png | Bin 0 -> 2498 bytes .../Networking/ServerSocket/.directory | 2 + .../Networking/ServerSocket/.icon.png | Bin 0 -> 3502 bytes .../Networking/ServerSocket/.lang/ca.mo | Bin 0 -> 1172 bytes .../Networking/ServerSocket/.lang/ca.po | 112 + .../Networking/ServerSocket/.lang/cs.mo | Bin 0 -> 1118 bytes .../Networking/ServerSocket/.lang/cs.po | 104 + .../Networking/ServerSocket/.lang/es.mo | Bin 0 -> 1055 bytes .../Networking/ServerSocket/.lang/es.po | 99 + .../Networking/ServerSocket/.lang/nl.mo | Bin 0 -> 1113 bytes .../Networking/ServerSocket/.lang/nl.po | 104 + app/examples/Networking/ServerSocket/.project | 21 + .../ServerSocket/.src/FrmMain.class | 176 + .../Networking/ServerSocket/.src/FrmMain.form | 61 + .../Networking/ServerSocket/serversocket.png | Bin 0 -> 2122 bytes .../Networking/UDPServerClient/.directory | 2 + .../Networking/UDPServerClient/.icon.png | Bin 0 -> 4294 bytes .../Networking/UDPServerClient/.lang/ca.mo | Bin 0 -> 1541 bytes .../Networking/UDPServerClient/.lang/ca.po | 104 + .../Networking/UDPServerClient/.lang/cs.mo | Bin 0 -> 1439 bytes .../Networking/UDPServerClient/.lang/cs.po | 96 + .../Networking/UDPServerClient/.lang/es.mo | Bin 0 -> 1428 bytes .../Networking/UDPServerClient/.lang/es.po | 101 + .../Networking/UDPServerClient/.lang/nl.mo | Bin 0 -> 1425 bytes .../Networking/UDPServerClient/.lang/nl.po | 96 + .../Networking/UDPServerClient/.project | 17 + .../UDPServerClient/.src/FrmClient.class | 73 + .../UDPServerClient/.src/FrmClient.form | 70 + .../UDPServerClient/.src/FrmServer.class | 86 + .../UDPServerClient/.src/FrmServer.form | 38 + .../Networking/UDPServerClient/udpsocket.png | Bin 0 -> 2161 bytes app/examples/Networking/WebBrowser/.directory | 2 + app/examples/Networking/WebBrowser/.icon.png | Bin 0 -> 3585 bytes .../Networking/WebBrowser/.lang/ca.mo | Bin 0 -> 2487 bytes .../Networking/WebBrowser/.lang/ca.po | 212 + .../Networking/WebBrowser/.lang/cs.mo | Bin 0 -> 2362 bytes .../Networking/WebBrowser/.lang/cs.po | 204 + .../Networking/WebBrowser/.lang/de.mo | Bin 0 -> 2368 bytes .../Networking/WebBrowser/.lang/de.po | 205 + .../Networking/WebBrowser/.lang/es.mo | Bin 0 -> 314 bytes .../Networking/WebBrowser/.lang/es.po | 62 + .../Networking/WebBrowser/.lang/nl.mo | Bin 0 -> 2363 bytes .../Networking/WebBrowser/.lang/nl.po | 204 + app/examples/Networking/WebBrowser/.project | 26 + .../Networking/WebBrowser/.src/FAuth.class | 38 + .../Networking/WebBrowser/.src/FAuth.form | 60 + .../Networking/WebBrowser/.src/FBrowser.class | 584 + .../Networking/WebBrowser/.src/FBrowser.form | 352 + .../WebBrowser/.src/FDownload.class | 50 + .../Networking/WebBrowser/.src/FDownload.form | 34 + .../WebBrowser/.src/FDownloadList.class | 20 + .../WebBrowser/.src/FDownloadList.form | 20 + .../WebBrowser/.src/FEditable.class | 15 + .../Networking/WebBrowser/.src/FEditable.form | 19 + .../Networking/WebBrowser/.src/FOption.class | 67 + .../Networking/WebBrowser/.src/FOption.form | 69 + app/examples/Networking/WebBrowser/icon.png | Bin 0 -> 2932 bytes .../Networking/WebBrowser/list-ordered.png | Bin 0 -> 518 bytes .../Networking/WebBrowser/list-unordered.png | Bin 0 -> 237 bytes app/examples/OpenGL/3DWebCam/.directory | 2 + app/examples/OpenGL/3DWebCam/.icon.png | Bin 0 -> 4343 bytes app/examples/OpenGL/3DWebCam/.project | 18 + .../OpenGL/3DWebCam/.src/Mmain.module | 256 + app/examples/OpenGL/3DWebCam/webcam.png | Bin 0 -> 6442 bytes app/examples/OpenGL/GambasGears/.directory | 2 + app/examples/OpenGL/GambasGears/.icon.png | Bin 0 -> 3450 bytes app/examples/OpenGL/GambasGears/.project | 18 + .../OpenGL/GambasGears/.src/Module1.module | 256 + app/examples/OpenGL/GambasGears/gears.png | Bin 0 -> 3488 bytes app/examples/OpenGL/Md2Model/.directory | 2 + app/examples/OpenGL/Md2Model/.icon.png | Bin 0 -> 3521 bytes app/examples/OpenGL/Md2Model/.project | 21 + app/examples/OpenGL/Md2Model/.src/FMain.class | 243 + app/examples/OpenGL/Md2Model/.src/FMain.form | 31 + app/examples/OpenGL/Md2Model/Weapon.md2 | Bin 0 -> 71708 bytes app/examples/OpenGL/Md2Model/Weapon.png | Bin 0 -> 17376 bytes app/examples/OpenGL/Md2Model/bauul.jpg | Bin 0 -> 25801 bytes app/examples/OpenGL/Md2Model/bauul.md2 | Bin 0 -> 261308 bytes app/examples/OpenGL/Md2Model/goblin.jpg | Bin 0 -> 23224 bytes app/examples/OpenGL/Md2Model/goblin.md2 | Bin 0 -> 259348 bytes app/examples/OpenGL/Md2Model/icon.png | Bin 0 -> 5529 bytes app/examples/OpenGL/Md2Model/igdosh.png | Bin 0 -> 22466 bytes app/examples/OpenGL/Md2Model/knight.jpg | Bin 0 -> 16927 bytes app/examples/OpenGL/Md2Model/knight.md2 | Bin 0 -> 320236 bytes app/examples/OpenGL/Md2Model/ogro.jpg | Bin 0 -> 23954 bytes app/examples/OpenGL/Md2Model/ogro.md2 | Bin 0 -> 317276 bytes app/examples/OpenGL/Md2Model/rat.jpg | Bin 0 -> 22384 bytes app/examples/OpenGL/Md2Model/rat.md2 | Bin 0 -> 303388 bytes app/examples/OpenGL/Md2Model/rhino.jpg | Bin 0 -> 18925 bytes app/examples/OpenGL/Md2Model/rhino.md2 | Bin 0 -> 418932 bytes app/examples/OpenGL/NeHeTutorial/.directory | 2 + app/examples/OpenGL/NeHeTutorial/.icon.png | Bin 0 -> 4212 bytes app/examples/OpenGL/NeHeTutorial/.project | 16 + .../OpenGL/NeHeTutorial/.src/Example1.module | 48 + .../OpenGL/NeHeTutorial/.src/Example10.module | 389 + .../OpenGL/NeHeTutorial/.src/Example11.module | 157 + .../OpenGL/NeHeTutorial/.src/Example16.module | 238 + .../OpenGL/NeHeTutorial/.src/Example19.module | 295 + .../OpenGL/NeHeTutorial/.src/Example2.module | 73 + .../OpenGL/NeHeTutorial/.src/Example25.module | 243 + .../OpenGL/NeHeTutorial/.src/Example3.module | 76 + .../OpenGL/NeHeTutorial/.src/Example4.module | 90 + .../OpenGL/NeHeTutorial/.src/Example42.module | 301 + .../OpenGL/NeHeTutorial/.src/Example5.module | 149 + .../OpenGL/NeHeTutorial/.src/Example6.module | 184 + .../OpenGL/NeHeTutorial/.src/Example7.module | 219 + .../OpenGL/NeHeTutorial/.src/Example8.module | 232 + .../OpenGL/NeHeTutorial/.src/Example9.module | 190 + .../OpenGL/NeHeTutorial/.src/MMain.module | 17 + app/examples/OpenGL/NeHeTutorial/NeHe.png | Bin 0 -> 14493 bytes app/examples/OpenGL/NeHeTutorial/Sphere.txt | 486 + app/examples/OpenGL/NeHeTutorial/Star.png | Bin 0 -> 4910 bytes app/examples/OpenGL/NeHeTutorial/Torus.txt | 486 + app/examples/OpenGL/NeHeTutorial/Tube.txt | 486 + app/examples/OpenGL/NeHeTutorial/barrel.png | Bin 0 -> 294 bytes app/examples/OpenGL/NeHeTutorial/ceiling.png | Bin 0 -> 140734 bytes app/examples/OpenGL/NeHeTutorial/crate.jpeg | Bin 0 -> 16088 bytes app/examples/OpenGL/NeHeTutorial/floor.png | Bin 0 -> 29009 bytes app/examples/OpenGL/NeHeTutorial/glass.png | Bin 0 -> 41118 bytes app/examples/OpenGL/NeHeTutorial/icon.png | Bin 0 -> 3377 bytes app/examples/OpenGL/NeHeTutorial/rainbow.txt | 12 + app/examples/OpenGL/NeHeTutorial/wall.jpeg | Bin 0 -> 121454 bytes app/examples/OpenGL/NeHeTutorial/world.txt | 16 + .../OpenGL/NeHeTutorialShell/.directory | 2 + .../OpenGL/NeHeTutorialShell/.icon.png | Bin 0 -> 4202 bytes .../OpenGL/NeHeTutorialShell/.project | 12 + .../OpenGL/NeHeTutorialShell/.src/FMain.class | 64 + .../OpenGL/NeHeTutorialShell/.src/FMain.form | 64 + .../OpenGL/NeHeTutorialShell/icon.png | Bin 0 -> 4015 bytes .../OpenGL/NeHeTutorialShell/nehe.png | Bin 0 -> 5785 bytes .../OpenGL/PDFPresentation/.directory | 2 + app/examples/OpenGL/PDFPresentation/.icon.png | Bin 0 -> 3540 bytes app/examples/OpenGL/PDFPresentation/.project | 26 + .../OpenGL/PDFPresentation/.src/CLogo.class | 224 + .../.src/CPdfPresentation.class | 464 + .../OpenGL/PDFPresentation/.src/FMain.class | 111 + .../OpenGL/PDFPresentation/.src/FMain.form | 37 + .../OpenGL/PDFPresentation/.src/MMain.module | 12 + app/examples/OpenGL/PDFPresentation/icon.png | Bin 0 -> 14695 bytes app/examples/OpenGL/PDFPresentation/logo.png | Bin 0 -> 27199 bytes app/examples/OpenGL/PDFPresentation/music.xm | Bin 0 -> 482806 bytes app/examples/OpenGL/TunnelSDL/.dir_icon.png | Bin 0 -> 1571 bytes app/examples/OpenGL/TunnelSDL/.directory | 2 + app/examples/OpenGL/TunnelSDL/.icon.png | Bin 0 -> 3650 bytes app/examples/OpenGL/TunnelSDL/.project | 22 + .../OpenGL/TunnelSDL/.src/CTunnel.class | 132 + .../OpenGL/TunnelSDL/.src/MMain.module | 67 + app/examples/OpenGL/TunnelSDL/texture.png | Bin 0 -> 118636 bytes app/examples/OpenGL/TunnelSDL/tunnelsdl.png | Bin 0 -> 7012 bytes app/examples/Printing/Printing/.directory | 2 + .../.hidden/screenshots/2014-12-14.png | Bin 0 -> 76629 bytes app/examples/Printing/Printing/.icon.png | Bin 0 -> 3334 bytes app/examples/Printing/Printing/.project | 15 + .../Printing/Printing/.src/FMain.class | 170 + .../Printing/Printing/.src/FMain.form | 89 + .../Printing/Printing/molly-malone.txt | 35 + .../Printing/Printing/printer-laser.png | Bin 0 -> 1358 bytes .../.connection/Connection1.connection | 11 + .../.connection/Connection1.template | 65 + .../Printing/ReportExample/.directory | 2 + .../Printing/ReportExample/.hidden/report.png | Bin 0 -> 3154 bytes app/examples/Printing/ReportExample/.icon.png | Bin 0 -> 3544 bytes app/examples/Printing/ReportExample/.project | 17 + .../Printing/ReportExample/.src/FMain.class | 41 + .../Printing/ReportExample/.src/FMain.form | 72 + .../Printing/ReportExample/.src/Report1.class | 32 + .../ReportExample/.src/Report1.report | 102 + .../Printing/ReportExample/.src/Report2.class | 19 + .../ReportExample/.src/Report2.report | 19 + .../Printing/ReportExample/.src/Report3.class | 45 + .../ReportExample/.src/Report3.report | 74 + .../Printing/ReportExample/gambas.svg | 540 + app/examples/Web/SmallWiki/.directory | 2 + app/examples/Web/SmallWiki/.icon.png | Bin 0 -> 15892 bytes app/examples/Web/SmallWiki/.project | 14 + .../Web/SmallWiki/.public/critical.png | Bin 0 -> 589 bytes app/examples/Web/SmallWiki/.public/edit.png | Bin 0 -> 116 bytes app/examples/Web/SmallWiki/.public/info.png | Bin 0 -> 777 bytes app/examples/Web/SmallWiki/.public/logo.png | Bin 0 -> 4125 bytes app/examples/Web/SmallWiki/.public/style.css | 233 + app/examples/Web/SmallWiki/.public/tip.png | Bin 0 -> 534 bytes app/examples/Web/SmallWiki/.public/up.png | Bin 0 -> 185 bytes .../Web/SmallWiki/.public/warning.png | Bin 0 -> 704 bytes app/examples/Web/SmallWiki/.src/Main.module | 282 + .../Web/SmallWiki/.src/Markdown.class | 40 + .../Web/SmallWiki/.src/MarkdownLink.class | 9 + app/examples/Web/SmallWiki/.src/Markup.module | 909 + app/examples/Web/SmallWiki/.src/Wiki.class | 47 + app/examples/Web/SmallWiki/.src/Wiki.webpage | 90 + .../Web/SmallWiki/.src/WikiMarkdown.class | 31 + app/examples/Web/SmallWiki/page | 8 + app/examples/Web/SmallWiki/passwd | 1 + app/mime/application-x-gambasscript.png | Bin 0 -> 7529 bytes app/mime/application-x-gambasscript.xml | 14 + app/mime/application-x-gambasserverpage.png | Bin 0 -> 8809 bytes app/mime/application-x-gambasserverpage.xml | 13 + app/reconf | 1 + app/src/INSTALL | 1 + .../.connection/Connection1.connection | 11 + .../.connection/Connection1.template | 160 + .../.connection/Connection2.connection | 10 + .../.connection/gambas3_farm.connection | 11 + .../.connection/gambas3_farm.template | 213 + app/src/gambas-farm-server/.directory | 2 + app/src/gambas-farm-server/.icon.png | Bin 0 -> 8866 bytes app/src/gambas-farm-server/.project | 16 + app/src/gambas-farm-server/.src/MMain.module | 1031 + app/src/gambas-farm-server/logo.png | Bin 0 -> 743 bytes app/src/gambas-farm-server/usage.txt | 8 + .../.connection/Connection1.connection | 9 + app/src/gambas-wiki/.directory | 2 + app/src/gambas-wiki/.hidden/CHANGELOG | 7 + .../.hidden/Uncompressed/.public/hilitor.js | 236 + .../Uncompressed/.public/style-rtl.css | 16 + .../.hidden/Uncompressed/.public/style-w.css | 20 + .../.hidden/Uncompressed/.public/style.css | 677 + app/src/gambas-wiki/.icon.png | Bin 0 -> 14647 bytes app/src/gambas-wiki/.lang/ar.mo | Bin 0 -> 992 bytes app/src/gambas-wiki/.lang/ar.po | 224 + app/src/gambas-wiki/.lang/ca.mo | Bin 0 -> 2296 bytes app/src/gambas-wiki/.lang/ca.po | 224 + app/src/gambas-wiki/.lang/cs.mo | Bin 0 -> 2257 bytes app/src/gambas-wiki/.lang/cs.po | 224 + app/src/gambas-wiki/.lang/de.mo | Bin 0 -> 2339 bytes app/src/gambas-wiki/.lang/de.po | 224 + app/src/gambas-wiki/.lang/es.mo | Bin 0 -> 5043 bytes app/src/gambas-wiki/.lang/es.po | 319 + app/src/gambas-wiki/.lang/es_ES.mo | Bin 0 -> 5046 bytes app/src/gambas-wiki/.lang/es_ES.po | 319 + app/src/gambas-wiki/.lang/fa.mo | Bin 0 -> 1792 bytes app/src/gambas-wiki/.lang/fa.po | 224 + app/src/gambas-wiki/.lang/fr.mo | Bin 0 -> 5173 bytes app/src/gambas-wiki/.lang/fr.po | 319 + app/src/gambas-wiki/.lang/it.mo | Bin 0 -> 2146 bytes app/src/gambas-wiki/.lang/it.po | 224 + app/src/gambas-wiki/.lang/nl.mo | Bin 0 -> 4977 bytes app/src/gambas-wiki/.lang/nl.po | 316 + app/src/gambas-wiki/.lang/pt_BR.mo | Bin 0 -> 5016 bytes app/src/gambas-wiki/.lang/pt_BR.po | 315 + app/src/gambas-wiki/.lang/ru.mo | Bin 0 -> 2799 bytes app/src/gambas-wiki/.lang/ru.po | 224 + app/src/gambas-wiki/.lang/sv.mo | Bin 0 -> 2265 bytes app/src/gambas-wiki/.lang/sv.po | 224 + app/src/gambas-wiki/.lang/zh.mo | Bin 0 -> 3949 bytes app/src/gambas-wiki/.lang/zh.po | 263 + app/src/gambas-wiki/.project | 29 + app/src/gambas-wiki/.public/critical.png | Bin 0 -> 495 bytes app/src/gambas-wiki/.public/edit.png | Bin 0 -> 237 bytes app/src/gambas-wiki/.public/error-bg.png | Bin 0 -> 153 bytes app/src/gambas-wiki/.public/gambas.png | Bin 0 -> 16241 bytes app/src/gambas-wiki/.public/hilitor.js | 305 + app/src/gambas-wiki/.public/home.png | Bin 0 -> 217 bytes app/src/gambas-wiki/.public/info.png | Bin 0 -> 313 bytes app/src/gambas-wiki/.public/logo.png | Bin 0 -> 4125 bytes app/src/gambas-wiki/.public/style-nh.css | 27 + app/src/gambas-wiki/.public/style-rtl.css | 4 + app/src/gambas-wiki/.public/style-w.css | 20 + app/src/gambas-wiki/.public/style.css | 757 + app/src/gambas-wiki/.public/tip.png | Bin 0 -> 241 bytes app/src/gambas-wiki/.public/up-gray.png | Bin 0 -> 197 bytes app/src/gambas-wiki/.public/up.png | Bin 0 -> 201 bytes app/src/gambas-wiki/.public/vb.png | Bin 0 -> 907 bytes app/src/gambas-wiki/.public/warning.png | Bin 0 -> 786 bytes app/src/gambas-wiki/.src/CAuthor.class | 78 + app/src/gambas-wiki/.src/CClassInfo.class | 554 + app/src/gambas-wiki/.src/CComponent.class | 1000 + app/src/gambas-wiki/.src/CPropertyInfo.class | 246 + app/src/gambas-wiki/.src/CSymbolInfo.class | 684 + app/src/gambas-wiki/.src/CUser.class | 99 + app/src/gambas-wiki/.src/Confirm.class | 24 + app/src/gambas-wiki/.src/Confirm.webpage | 43 + app/src/gambas-wiki/.src/HttpStat.module | 118 + app/src/gambas-wiki/.src/Main.module | 1513 + app/src/gambas-wiki/.src/OldWiki.module | 599 + app/src/gambas-wiki/.src/Register.class | 17 + app/src/gambas-wiki/.src/Register.webpage | 131 + app/src/gambas-wiki/.src/Wiki.class | 360 + app/src/gambas-wiki/.src/Wiki.webpage | 217 + app/src/gambas-wiki/.src/WikiMarkdown.class | 310 + app/src/gambas-wiki/authors.txt | 1 + app/src/gambas-wiki/gambas3-ide.project | 1 + app/src/gambas-wiki/gambas3-scripter.project | 1 + app/src/gambas-wiki/icon.png | Bin 0 -> 1973 bytes app/src/gambas-wiki/page | 8 + app/src/gambas-wiki/passwd | 1 + app/src/gambas3/.directory | 2 + .../.hidden/Uncompressed/help/class-help.html | 36 + .../.hidden/Uncompressed/help/wiki/style.css | 520 + .../gambas3/.hidden/font/GambasBold-12.sfd | 26779 +++++++++ .../gambas3/.hidden/font/GambasBold-13.sfd | 47625 ++++++++++++++++ .../gambas3/.hidden/font/GambasMedium-12.sfd | 24951 ++++++++ .../gambas3/.hidden/font/GambasMedium-13.sfd | 26977 +++++++++ .../gambas3/.hidden/font/GambasMedium-14.sfd | 8870 +++ app/src/gambas3/.hidden/font/LICENSE | 97 + app/src/gambas3/.hidden/make-help-archive | 4 + app/src/gambas3/.hidden/report-ng.sh | 132 + app/src/gambas3/.icon.png | Bin 0 -> 12002 bytes app/src/gambas3/.lang/ar.mo | Bin 0 -> 30163 bytes app/src/gambas3/.lang/ar.po | 7777 +++ app/src/gambas3/.lang/ca.mo | Bin 0 -> 82608 bytes app/src/gambas3/.lang/ca.po | 7684 +++ app/src/gambas3/.lang/cs.mo | Bin 0 -> 99476 bytes app/src/gambas3/.lang/cs.po | 7809 +++ app/src/gambas3/.lang/cy.mo | Bin 0 -> 13914 bytes app/src/gambas3/.lang/cy.po | 6455 +++ app/src/gambas3/.lang/de.mo | Bin 0 -> 128493 bytes app/src/gambas3/.lang/de.po | 7645 +++ app/src/gambas3/.lang/el.mo | Bin 0 -> 85898 bytes app/src/gambas3/.lang/el.po | 7612 +++ app/src/gambas3/.lang/es.mo | Bin 0 -> 119255 bytes app/src/gambas3/.lang/es.po | 7363 +++ app/src/gambas3/.lang/es_ES.mo | Bin 0 -> 123057 bytes app/src/gambas3/.lang/es_ES.po | 7188 +++ app/src/gambas3/.lang/fa.mo | Bin 0 -> 26739 bytes app/src/gambas3/.lang/fa.po | 6670 +++ app/src/gambas3/.lang/fr.mo | Bin 0 -> 134583 bytes app/src/gambas3/.lang/fr.po | 7877 +++ app/src/gambas3/.lang/gl_ES.mo | Bin 0 -> 13320 bytes app/src/gambas3/.lang/gl_ES.po | 7875 +++ app/src/gambas3/.lang/hr.mo | Bin 0 -> 20693 bytes app/src/gambas3/.lang/hr.po | 6440 +++ app/src/gambas3/.lang/hu.mo | Bin 0 -> 25792 bytes app/src/gambas3/.lang/hu.po | 6518 +++ app/src/gambas3/.lang/id.mo | Bin 0 -> 18324 bytes app/src/gambas3/.lang/id.po | 6453 +++ app/src/gambas3/.lang/it.mo | Bin 0 -> 130139 bytes app/src/gambas3/.lang/it.po | 7882 +++ app/src/gambas3/.lang/ja.mo | Bin 0 -> 48744 bytes app/src/gambas3/.lang/ja.po | 6518 +++ app/src/gambas3/.lang/ko.mo | Bin 0 -> 10986 bytes app/src/gambas3/.lang/ko.po | 6233 ++ app/src/gambas3/.lang/lt.mo | Bin 0 -> 39726 bytes app/src/gambas3/.lang/lt.po | 6233 ++ app/src/gambas3/.lang/nl.mo | Bin 0 -> 123447 bytes app/src/gambas3/.lang/nl.po | 7257 +++ app/src/gambas3/.lang/no.mo | Bin 0 -> 15216 bytes app/src/gambas3/.lang/no.po | 7514 +++ app/src/gambas3/.lang/pl.mo | Bin 0 -> 22161 bytes app/src/gambas3/.lang/pl.po | 6287 ++ app/src/gambas3/.lang/pt.mo | Bin 0 -> 22402 bytes app/src/gambas3/.lang/pt.po | 6505 +++ app/src/gambas3/.lang/pt_BR.mo | Bin 0 -> 126171 bytes app/src/gambas3/.lang/pt_BR.po | 7471 +++ app/src/gambas3/.lang/ro.mo | Bin 0 -> 42035 bytes app/src/gambas3/.lang/ro.po | 6233 ++ app/src/gambas3/.lang/ru.mo | Bin 0 -> 57070 bytes app/src/gambas3/.lang/ru.po | 6798 +++ app/src/gambas3/.lang/sl.mo | Bin 0 -> 21788 bytes app/src/gambas3/.lang/sl.po | 7854 +++ app/src/gambas3/.lang/sv.mo | Bin 0 -> 82657 bytes app/src/gambas3/.lang/sv.po | 6694 +++ app/src/gambas3/.lang/tr.mo | Bin 0 -> 53126 bytes app/src/gambas3/.lang/tr.po | 6513 +++ app/src/gambas3/.lang/zh.mo | Bin 0 -> 113094 bytes app/src/gambas3/.lang/zh.po | 7515 +++ app/src/gambas3/.lang/zh_TW.mo | Bin 0 -> 74862 bytes app/src/gambas3/.lang/zh_TW.po | 7863 +++ app/src/gambas3/.project | 57 + app/src/gambas3/.src/CRecentProject.class | 259 + app/src/gambas3/.src/CStyle.class | 76 + app/src/gambas3/.src/CWaitingAnimation.class | 113 + .../gambas3/.src/Component/CClassInfo.class | 1032 + .../gambas3/.src/Component/CComponent.class | 1733 + .../.src/Component/CDocumentation.class | 1167 + app/src/gambas3/.src/Component/CModule.class | 106 + .../.src/Component/CPropertyInfo.class | 396 + .../gambas3/.src/Component/CSymbolInfo.class | 1441 + .../gambas3/.src/Connection/FExportData.class | 146 + .../gambas3/.src/Connection/FExportData.form | 47 + .../.src/Connection/FImportTable.class | 512 + .../gambas3/.src/Connection/FImportTable.form | 155 + .../.src/Connection/FNewConnection.class | 434 + .../.src/Connection/FNewConnection.form | 220 + .../gambas3/.src/Connection/FPasteTable.class | 226 + .../gambas3/.src/Connection/FPasteTable.form | 77 + .../.src/Connection/MConnection.module | 551 + app/src/gambas3/.src/Debug/CProfile.class | 128 + app/src/gambas3/.src/Debug/Design.module | 1471 + app/src/gambas3/.src/Debug/FCrash.class | 147 + app/src/gambas3/.src/Debug/FCrash.form | 75 + app/src/gambas3/.src/Debug/FDebugButton.class | 81 + app/src/gambas3/.src/Debug/FDebugButton.form | 54 + app/src/gambas3/.src/Debug/FDebugExpr.class | 875 + app/src/gambas3/.src/Debug/FDebugExpr.form | 73 + app/src/gambas3/.src/Debug/FDebugInfo.class | 1404 + app/src/gambas3/.src/Debug/FDebugInfo.form | 244 + app/src/gambas3/.src/Debug/FOutput.class | 511 + app/src/gambas3/.src/Debug/FOutput.form | 124 + app/src/gambas3/.src/Debug/FProfile.class | 1071 + app/src/gambas3/.src/Debug/FProfile.form | 151 + .../.src/Dialog/Database/FFieldChooser.class | 159 + .../.src/Dialog/Database/FFieldChooser.form | 104 + .../.src/Dialog/Database/FTableChooser.class | 75 + .../.src/Dialog/Database/FTableChooser.form | 52 + .../gambas3/.src/Dialog/FColorChooser.class | 311 + .../gambas3/.src/Dialog/FColorChooser.form | 54 + .../gambas3/.src/Dialog/FFileProperty.class | 177 + .../gambas3/.src/Dialog/FFileProperty.form | 60 + .../gambas3/.src/Dialog/FFontChooser.class | 41 + app/src/gambas3/.src/Dialog/FFontChooser.form | 32 + app/src/gambas3/.src/Dialog/FList.class | 225 + app/src/gambas3/.src/Dialog/FList.form | 94 + app/src/gambas3/.src/Dialog/FSelectIcon.class | 198 + app/src/gambas3/.src/Dialog/FSelectIcon.form | 67 + app/src/gambas3/.src/Editor/CBookmark.class | 181 + .../gambas3/.src/Editor/CInsertColor.class | 252 + app/src/gambas3/.src/Editor/CInsertDate.class | 174 + app/src/gambas3/.src/Editor/CPosition.class | 204 + app/src/gambas3/.src/Editor/CTask.class | 89 + app/src/gambas3/.src/Editor/CUndo.class | 188 + .../.src/Editor/Code/CCompletion.class | 129 + .../gambas3/.src/Editor/Code/CDatatype.class | 98 + .../.src/Editor/Code/CSampleCode.class | 505 + .../.src/Editor/Code/FCompletion.class | 1162 + .../gambas3/.src/Editor/Code/FCompletion.form | 53 + .../.src/Editor/Code/FConflictEditor.class | 1576 + .../.src/Editor/Code/FConflictEditor.form | 637 + .../gambas3/.src/Editor/Code/FEditor.class | 3887 ++ app/src/gambas3/.src/Editor/Code/FEditor.form | 701 + .../.src/Editor/Code/FPasteSpecial.class | 206 + .../.src/Editor/Code/FPasteSpecial.form | 64 + .../.src/Editor/Code/FProcedureList.class | 216 + .../.src/Editor/Code/FProcedureList.form | 22 + .../gambas3/.src/Editor/Code/FSignature.class | 272 + .../gambas3/.src/Editor/Code/FSignature.form | 58 + .../.src/Editor/Code/FTextEditor.class | 1788 + .../gambas3/.src/Editor/Code/FTextEditor.form | 708 + .../.src/Editor/Code/MPrettyCode.module | 491 + .../.src/Editor/Connection/CField.class | 31 + .../.src/Editor/Connection/CIndexField.class | 18 + .../Editor/Connection/FConnectionEditor.class | 1992 + .../Editor/Connection/FConnectionEditor.form | 512 + .../.src/Editor/Connection/FNewTable.class | 75 + .../.src/Editor/Connection/FNewTable.form | 64 + app/src/gambas3/.src/Editor/FGotoLine.class | 36 + app/src/gambas3/.src/Editor/FGotoLine.form | 31 + app/src/gambas3/.src/Editor/FInsertChar.class | 113 + app/src/gambas3/.src/Editor/FInsertChar.form | 16 + .../gambas3/.src/Editor/Form/CControl.class | 2373 + app/src/gambas3/.src/Editor/Form/CMenu.class | 79 + app/src/gambas3/.src/Editor/Form/FForm.class | 4415 ++ app/src/gambas3/.src/Editor/Form/FForm.form | 949 + .../gambas3/.src/Editor/Form/FFormStack.class | 334 + .../gambas3/.src/Editor/Form/FFormStack.form | 43 + app/src/gambas3/.src/Editor/Form/FMenu.class | 1117 + app/src/gambas3/.src/Editor/Form/FMenu.form | 313 + .../gambas3/.src/Editor/Form/FProperty.class | 1351 + .../gambas3/.src/Editor/Form/FProperty.form | 95 + app/src/gambas3/.src/Editor/Form/FText.class | 61 + app/src/gambas3/.src/Editor/Form/FText.form | 33 + .../gambas3/.src/Editor/Form/FToolBox.class | 375 + .../gambas3/.src/Editor/Form/FToolBox.form | 17 + .../gambas3/.src/Editor/Form/FToolPanel.class | 217 + .../gambas3/.src/Editor/Form/FToolPanel.form | 14 + .../.src/Editor/Image/CImageClipboard.class | 13 + .../.src/Editor/Image/CImageSelection.class | 1107 + .../.src/Editor/Image/CImageShape.class | 331 + .../.src/Editor/Image/FImageEditor.class | 3297 ++ .../.src/Editor/Image/FImageEditor.form | 825 + .../Editor/Image/FImageOffsetSelection.class | 26 + .../Editor/Image/FImageOffsetSelection.form | 30 + .../.src/Editor/Image/FImageProperty.class | 1512 + .../.src/Editor/Image/FImageProperty.form | 624 + .../.src/Editor/Image/FImageQuality.class | 20 + .../.src/Editor/Image/FImageQuality.form | 25 + .../.src/Editor/Image/FImageResize.class | 146 + .../.src/Editor/Image/FImageResize.form | 103 + .../.src/Editor/Image/FImageRotate.class | 39 + .../.src/Editor/Image/FImageRotate.form | 25 + .../gambas3/.src/Editor/MCompressFile.module | 548 + .../ProjectChooser/FProjectChooser.class | 179 + .../ProjectChooser/FProjectChooser.form | 65 + .../ProjectChooser/ProjectChooser.class | 113 + .../gambas3/.src/Exported/TextEditor.class | 387 + app/src/gambas3/.src/FMain.class | 2952 + app/src/gambas3/.src/FMain.form | 1740 + app/src/gambas3/.src/FSave.class | 83 + app/src/gambas3/.src/FSave.form | 48 + app/src/gambas3/.src/FScreenshot.class | 46 + app/src/gambas3/.src/FScreenshot.form | 41 + app/src/gambas3/.src/Family/CFamily.class | 643 + .../.src/Family/Form/CFamilyForm.class | 153 + .../gambas3/.src/Family/Report/AngleBox.class | 90 + .../.src/Family/Report/CFamilyReport.class | 574 + .../.src/Family/Report/CReportBrush.class | 264 + .../gambas3/.src/Family/Report/CoordBox.class | 412 + .../Family/Report/FReportBorderChooser.class | 503 + .../Family/Report/FReportBorderChooser.form | 169 + .../Report/FReportBoxShadowChooser.class | 136 + .../Report/FReportBoxShadowChooser.form | 79 + .../Family/Report/FReportBrushChooser.class | 561 + .../Family/Report/FReportBrushChooser.form | 118 + .../Family/Report/FReportCoordChooser.class | 95 + .../Family/Report/FReportCoordChooser.form | 33 + .../Family/Report/FReportPaddingChooser.class | 97 + .../Family/Report/FReportPaddingChooser.form | 58 + .../Family/TermForm/CFamilyTermForm.class | 99 + .../.src/Family/WebForm/CFamilyWebForm.class | 263 + .../.src/Family/WebForm/FWebFontChooser.class | 126 + .../.src/Family/WebForm/FWebFontChooser.form | 97 + .../.src/Family/WebForm/WebformWebMenu.class | 51 + app/src/gambas3/.src/Help/FHelpBrowser.class | 320 + app/src/gambas3/.src/Help/FHelpBrowser.form | 124 + app/src/gambas3/.src/Help/FHelpShortcut.class | 44 + app/src/gambas3/.src/Help/FHelpShortcut.form | 39 + app/src/gambas3/.src/Help/FTips.class | 185 + app/src/gambas3/.src/Help/FTips.form | 66 + app/src/gambas3/.src/Help/HelpView.class | 129 + app/src/gambas3/.src/Help/MHelp.module | 998 + app/src/gambas3/.src/Help/Markup.module | 587 + app/src/gambas3/.src/Help/Wiki/URL.class | 103 + app/src/gambas3/.src/Help/Wiki/Wiki.module | 885 + .../gambas3/.src/Help/Wiki/WikiMarkdown.class | 256 + app/src/gambas3/.src/MMime.module | 123 + app/src/gambas3/.src/MTheme.module | 261 + .../gambas3/.src/Options/CBackground.class | 71 + app/src/gambas3/.src/Options/FOption.class | 1589 + app/src/gambas3/.src/Options/FOption.form | 1219 + app/src/gambas3/.src/Options/FProxy.class | 55 + app/src/gambas3/.src/Options/FProxy.form | 67 + app/src/gambas3/.src/Options/FSnippet.class | 79 + app/src/gambas3/.src/Options/FSnippet.form | 43 + .../gambas3/.src/Packager/FMakeInstall.class | 1275 + .../gambas3/.src/Packager/FMakeInstall.form | 703 + .../.src/Packager/FSelectExtraFile.class | 39 + .../.src/Packager/FSelectExtraFile.form | 35 + app/src/gambas3/.src/Packager/Package.module | 2645 + app/src/gambas3/.src/Project.module | 6537 +++ app/src/gambas3/.src/Project/ArgListBox.class | 225 + .../gambas3/.src/Project/ArgListEditor.class | 204 + .../gambas3/.src/Project/CProjectInfo.class | 260 + .../gambas3/.src/Project/CProjectList.class | 668 + .../gambas3/.src/Project/CProjectTree.class | 984 + .../Project/Component/ComponentChooser.class | 167 + .../Project/Component/FComponentChooser.class | 794 + .../Project/Component/FComponentChooser.form | 107 + .../Project/Component/FSelectComponent.class | 24 + .../Project/Component/FSelectComponent.form | 30 + .../.src/Project/Conversion/FConvert.class | 56 + .../.src/Project/Conversion/FConvert.form | 22 + .../.src/Project/Conversion/MConvert.module | 819 + .../gambas3/.src/Project/FCreateFile.class | 333 + app/src/gambas3/.src/Project/FCreateFile.form | 155 + .../gambas3/.src/Project/FCreateProject.class | 255 + .../gambas3/.src/Project/FCreateProject.form | 144 + .../gambas3/.src/Project/FImportFile.class | 199 + app/src/gambas3/.src/Project/FImportFile.form | 20 + .../.src/Project/FMakeExecutable.class | 59 + .../gambas3/.src/Project/FMakeExecutable.form | 48 + .../gambas3/.src/Project/FOpenProject.class | 197 + .../gambas3/.src/Project/FOpenProject.form | 117 + .../.src/Project/FProjectProperty.class | 1436 + .../.src/Project/FProjectProperty.form | 679 + .../gambas3/.src/Project/FSaveProjectAs.class | 103 + .../gambas3/.src/Project/FSaveProjectAs.form | 65 + .../gambas3/.src/Project/Farm/CSoftware.class | 692 + .../.src/Project/Farm/CSoftwareGroup.class | 27 + .../.src/Project/Farm/FFarmConfig.class | 32 + .../.src/Project/Farm/FFarmConfig.form | 30 + .../.src/Project/Farm/FFarmLogin.class | 113 + .../gambas3/.src/Project/Farm/FFarmLogin.form | 90 + .../.src/Project/Farm/FFarmRegister.class | 64 + .../.src/Project/Farm/FFarmRegister.form | 100 + .../.src/Project/Farm/FFarmRequest.class | 64 + .../.src/Project/Farm/FFarmRequest.form | 32 + .../gambas3/.src/Project/Farm/FPublish.class | 355 + .../gambas3/.src/Project/Farm/FPublish.form | 237 + .../.src/Project/Farm/FSoftwareFarm.class | 753 + .../.src/Project/Farm/FSoftwareFarm.form | 318 + .../.src/Project/Farm/FarmIdentity.class | 144 + .../.src/Project/Farm/FarmRequest.class | 396 + .../Project/Farm/FarmRequestManager.module | 97 + .../.src/Project/Farm/Publish/CTag.class | 78 + .../Project/Farm/Publish/CTagCompletion.class | 71 + .../Project/Farm/Publish/CTagEditor.class | 186 + .../.src/Project/Farm/SoftwareBox.class | 326 + .../.src/Project/Library/CLibraryInfo.class | 225 + .../.src/Project/Library/FSelectLibrary.class | 146 + .../.src/Project/Library/FSelectLibrary.form | 52 + .../.src/Project/Library/LibraryItem.class | 120 + .../.src/Project/Patch/FMakePatch.class | 155 + .../.src/Project/Patch/FMakePatch.form | 98 + .../gambas3/.src/Project/Patch/FPatch.class | 221 + .../gambas3/.src/Project/Patch/FPatch.form | 68 + .../gambas3/.src/Project/Patch/Patch.class | 212 + app/src/gambas3/.src/Project/ProjectBox.class | 467 + app/src/gambas3/.src/Save.module | 56 + app/src/gambas3/.src/Search/CFindResult.class | 37 + app/src/gambas3/.src/Search/CGrepResult.class | 6 + app/src/gambas3/.src/Search/FSearch.class | 1305 + app/src/gambas3/.src/Search/FSearch.form | 173 + .../.src/Translation/CTranslation.class | 193 + .../.src/Translation/FNewTranslation.class | 45 + .../.src/Translation/FNewTranslation.form | 21 + .../gambas3/.src/Translation/FTranslate.class | 1530 + .../gambas3/.src/Translation/FTranslate.form | 296 + .../gambas3/.src/Translation/Language.module | 272 + app/src/gambas3/.src/Util.module | 68 + .../gambas3/.src/Util/MErrorMessage.module | 384 + .../gambas3/.src/Util/MRemoveAccents.module | 69 + .../gambas3/.src/Util/MSdlDefaultFont.module | 77 + .../.src/VersionControl/CVersionControl.class | 105 + .../VersionControl/CVersionControlGit.class | 413 + .../CVersionControlSubversion.class | 371 + .../.src/VersionControl/FConflict.class | 288 + .../.src/VersionControl/FConflict.form | 121 + .../.src/VersionControl/FProjectVersion.class | 307 + .../.src/VersionControl/FProjectVersion.form | 231 + .../.src/VersionControl/FVersionControl.class | 105 + .../.src/VersionControl/FVersionControl.form | 61 + .../.src/VersionControl/FVersionError.class | 53 + .../.src/VersionControl/FVersionError.form | 40 + .../.src/VersionControl/VersionControl.module | 640 + .../gambas3/.src/Welcome/CCoolButton.class | 230 + app/src/gambas3/.src/Welcome/CSnowFlake.class | 8 + app/src/gambas3/.src/Welcome/CUser.class | 11 + app/src/gambas3/.src/Welcome/CWelcome.class | 235 + app/src/gambas3/.src/Welcome/FAbout.class | 390 + app/src/gambas3/.src/Welcome/FAbout.form | 36 + .../gambas3/.src/Welcome/FSystemInfo.class | 263 + app/src/gambas3/.src/Welcome/FSystemInfo.form | 58 + app/src/gambas3/.src/Welcome/FWelcome.class | 416 + app/src/gambas3/.src/Welcome/FWelcome.form | 108 + app/src/gambas3/AUTHORS | 1 + app/src/gambas3/_fake_project | 4 + app/src/gambas3/font/GambasBold-12.bdf | 36160 ++++++++++++ app/src/gambas3/font/GambasBold-13.bdf | 41652 ++++++++++++++ app/src/gambas3/font/GambasMedium-12.bdf | 36575 ++++++++++++ app/src/gambas3/font/GambasMedium-13.bdf | 41540 ++++++++++++++ app/src/gambas3/font/font.allow | 7 + app/src/gambas3/font/font.conf | 4 + app/src/gambas3/gitignore | 14 + app/src/gambas3/help/class-help.html | 36 + app/src/gambas3/help/component-help.html | 25 + app/src/gambas3/help/property-help.html | 22 + app/src/gambas3/help/symbol-help.html | 20 + app/src/gambas3/help/wiki/critical.png | Bin 0 -> 589 bytes app/src/gambas3/help/wiki/error-bg.png | Bin 0 -> 153 bytes app/src/gambas3/help/wiki/info.png | Bin 0 -> 777 bytes app/src/gambas3/help/wiki/page.html | 37 + app/src/gambas3/help/wiki/style-custom.css | 13 + app/src/gambas3/help/wiki/style-offline.css | 17 + app/src/gambas3/help/wiki/style.css | 1 + app/src/gambas3/help/wiki/tip.png | Bin 0 -> 534 bytes app/src/gambas3/help/wiki/up.png | Bin 0 -> 185 bytes app/src/gambas3/help/wiki/vb.png | Bin 0 -> 907 bytes app/src/gambas3/help/wiki/warning.png | Bin 0 -> 704 bytes app/src/gambas3/img/16/arrange-h-dark.png | Bin 0 -> 161 bytes app/src/gambas3/img/16/arrange-h.png | Bin 0 -> 161 bytes .../gambas3/img/16/arrange-hcenter-dark.png | Bin 0 -> 163 bytes app/src/gambas3/img/16/arrange-hcenter.png | Bin 0 -> 164 bytes app/src/gambas3/img/16/arrange-lr-dark.png | Bin 0 -> 163 bytes app/src/gambas3/img/16/arrange-lr.png | Bin 0 -> 164 bytes app/src/gambas3/img/16/arrange-tb-dark.png | Bin 0 -> 162 bytes app/src/gambas3/img/16/arrange-tb.png | Bin 0 -> 162 bytes app/src/gambas3/img/16/arrange-v-dark.png | Bin 0 -> 158 bytes app/src/gambas3/img/16/arrange-v.png | Bin 0 -> 158 bytes .../gambas3/img/16/arrange-vcenter-dark.png | Bin 0 -> 166 bytes app/src/gambas3/img/16/arrange-vcenter.png | Bin 0 -> 166 bytes app/src/gambas3/img/16/average-dark.png | Bin 0 -> 424 bytes app/src/gambas3/img/16/average.png | Bin 0 -> 424 bytes app/src/gambas3/img/16/checked-gray.png | Bin 0 -> 210 bytes app/src/gambas3/img/16/checked-lock.png | Bin 0 -> 161 bytes app/src/gambas3/img/16/checked.png | Bin 0 -> 210 bytes app/src/gambas3/img/16/close-window.png | Bin 0 -> 164 bytes app/src/gambas3/img/16/collapse-container.png | Bin 0 -> 165 bytes app/src/gambas3/img/16/cursive.png | Bin 0 -> 551 bytes .../gambas3/img/16/delete-container-dark.png | Bin 0 -> 171 bytes app/src/gambas3/img/16/delete-container.png | Bin 0 -> 171 bytes app/src/gambas3/img/16/deprecated.png | Bin 0 -> 228 bytes .../gambas3/img/16/embed-container-dark.png | Bin 0 -> 159 bytes app/src/gambas3/img/16/embed-container.png | Bin 0 -> 159 bytes app/src/gambas3/img/16/expand-container.png | Bin 0 -> 165 bytes app/src/gambas3/img/16/experimental.png | Bin 0 -> 208 bytes app/src/gambas3/img/16/fantasy.png | Bin 0 -> 478 bytes app/src/gambas3/img/16/finished.png | Bin 0 -> 206 bytes app/src/gambas3/img/16/key.png | Bin 0 -> 608 bytes app/src/gambas3/img/16/lcase-dark.png | Bin 0 -> 407 bytes app/src/gambas3/img/16/lcase.png | Bin 0 -> 407 bytes app/src/gambas3/img/16/max-window.png | Bin 0 -> 169 bytes app/src/gambas3/img/16/monospace.png | Bin 0 -> 372 bytes app/src/gambas3/img/16/percent-dark.png | Bin 0 -> 555 bytes app/src/gambas3/img/16/percent.png | Bin 0 -> 555 bytes app/src/gambas3/img/16/red-arrow-c.png | Bin 0 -> 155 bytes app/src/gambas3/img/16/red-arrow-h.png | Bin 0 -> 148 bytes app/src/gambas3/img/16/red-arrow-r.png | Bin 0 -> 151 bytes app/src/gambas3/img/16/red-arrow-v.png | Bin 0 -> 152 bytes .../gambas3/img/16/remove-from-container.png | Bin 0 -> 159 bytes app/src/gambas3/img/16/rename.png | Bin 0 -> 219 bytes app/src/gambas3/img/16/round-ne.png | Bin 0 -> 219 bytes app/src/gambas3/img/16/round-nw.png | Bin 0 -> 221 bytes app/src/gambas3/img/16/round-se.png | Bin 0 -> 219 bytes app/src/gambas3/img/16/round-sw.png | Bin 0 -> 216 bytes app/src/gambas3/img/16/sans-serif.png | Bin 0 -> 487 bytes app/src/gambas3/img/16/select.png | Bin 0 -> 171 bytes app/src/gambas3/img/16/separator.png | Bin 0 -> 155 bytes app/src/gambas3/img/16/serif.png | Bin 0 -> 493 bytes app/src/gambas3/img/16/stack-dark.png | Bin 0 -> 164 bytes app/src/gambas3/img/16/stack.png | Bin 0 -> 164 bytes app/src/gambas3/img/16/tabmove.png | Bin 0 -> 275 bytes app/src/gambas3/img/16/tabstrip.png | Bin 0 -> 248 bytes app/src/gambas3/img/16/tile.png | Bin 0 -> 120 bytes .../gambas3/img/16/toggle-container-dark.png | Bin 0 -> 164 bytes app/src/gambas3/img/16/toggle-container.png | Bin 0 -> 164 bytes app/src/gambas3/img/16/ucase-dark.png | Bin 0 -> 464 bytes app/src/gambas3/img/16/ucase.png | Bin 0 -> 464 bytes app/src/gambas3/img/16/unchecked.png | Bin 0 -> 156 bytes app/src/gambas3/img/16/unfinished.png | Bin 0 -> 274 bytes app/src/gambas3/img/16/white-close.png | Bin 0 -> 157 bytes app/src/gambas3/img/32/added.png | Bin 0 -> 354 bytes app/src/gambas3/img/32/comment-dark.png | Bin 0 -> 255 bytes app/src/gambas3/img/32/comment.png | Bin 0 -> 255 bytes app/src/gambas3/img/32/conflict.png | Bin 0 -> 363 bytes app/src/gambas3/img/32/cross.png | Bin 0 -> 171 bytes app/src/gambas3/img/32/do-not-translate.png | Bin 0 -> 450 bytes app/src/gambas3/img/32/eol-dark.png | Bin 0 -> 194 bytes app/src/gambas3/img/32/eol.png | Bin 0 -> 194 bytes app/src/gambas3/img/32/exported.png | Bin 0 -> 611 bytes app/src/gambas3/img/32/filter-menu.png | Bin 0 -> 530 bytes app/src/gambas3/img/32/filter.png | Bin 0 -> 521 bytes app/src/gambas3/img/32/green-arrow.png | Bin 0 -> 217 bytes app/src/gambas3/img/32/linked.png | Bin 0 -> 198 bytes app/src/gambas3/img/32/magic.png | Bin 0 -> 241 bytes app/src/gambas3/img/32/modified.png | Bin 0 -> 395 bytes app/src/gambas3/img/32/opengl.png | Bin 0 -> 1329 bytes app/src/gambas3/img/32/startup-dark.png | Bin 0 -> 199 bytes app/src/gambas3/img/32/startup.png | Bin 0 -> 199 bytes app/src/gambas3/img/32/tab-dark.png | Bin 0 -> 206 bytes app/src/gambas3/img/32/tab.png | Bin 0 -> 206 bytes app/src/gambas3/img/32/uncomment-dark.png | Bin 0 -> 325 bytes app/src/gambas3/img/32/uncomment.png | Bin 0 -> 325 bytes app/src/gambas3/img/32/wrap-dark.png | Bin 0 -> 204 bytes app/src/gambas3/img/32/wrap.png | Bin 0 -> 204 bytes app/src/gambas3/img/48/all.png | Bin 0 -> 1521 bytes app/src/gambas3/img/64/atari-dark.png | Bin 0 -> 202 bytes app/src/gambas3/img/64/atari.png | Bin 0 -> 202 bytes app/src/gambas3/img/background/baraka.png | Bin 0 -> 10448 bytes app/src/gambas3/img/background/christ.png | Bin 0 -> 209 bytes app/src/gambas3/img/background/circle.png | Bin 0 -> 193 bytes app/src/gambas3/img/background/cross.png | Bin 0 -> 146 bytes app/src/gambas3/img/background/cubism.png | Bin 0 -> 63853 bytes app/src/gambas3/img/background/dark.png | Bin 0 -> 113 bytes app/src/gambas3/img/background/gambas.png | Bin 0 -> 974 bytes app/src/gambas3/img/background/hline.png | Bin 0 -> 138 bytes app/src/gambas3/img/background/hose.png | Bin 0 -> 44520 bytes app/src/gambas3/img/background/illusion.png | Bin 0 -> 2946 bytes app/src/gambas3/img/background/labyrinth.png | Bin 0 -> 973 bytes app/src/gambas3/img/background/light.png | Bin 0 -> 113 bytes app/src/gambas3/img/background/list | 26 + app/src/gambas3/img/background/medracim.png | Bin 0 -> 172457 bytes app/src/gambas3/img/background/mosaic.png | Bin 0 -> 76015 bytes app/src/gambas3/img/background/muhammad.png | Bin 0 -> 17744 bytes app/src/gambas3/img/background/nature.png | Bin 0 -> 46970 bytes app/src/gambas3/img/background/oil.png | Bin 0 -> 74848 bytes app/src/gambas3/img/background/point.png | Bin 0 -> 135 bytes app/src/gambas3/img/background/quasi.png | Bin 0 -> 34460 bytes app/src/gambas3/img/background/smoke.png | Bin 0 -> 25578 bytes app/src/gambas3/img/background/square.png | Bin 0 -> 480 bytes app/src/gambas3/img/background/star.png | Bin 0 -> 34262 bytes app/src/gambas3/img/background/tawhid.png | Bin 0 -> 22454 bytes app/src/gambas3/img/background/vline.png | Bin 0 -> 133 bytes app/src/gambas3/img/background/warda.png | Bin 0 -> 24560 bytes app/src/gambas3/img/background/weave.png | Bin 0 -> 60153 bytes app/src/gambas3/img/broken.svg | 122 + app/src/gambas3/img/contrib/Radoslav.gif | Bin 0 -> 2405 bytes app/src/gambas3/img/control/button.png | Bin 0 -> 598 bytes app/src/gambas3/img/control/checkbox.png | Bin 0 -> 198 bytes app/src/gambas3/img/control/columnview.png | Bin 0 -> 157 bytes app/src/gambas3/img/control/combobox.png | Bin 0 -> 312 bytes app/src/gambas3/img/control/dial.png | Bin 0 -> 745 bytes app/src/gambas3/img/control/dnsclient.png | Bin 0 -> 1192 bytes app/src/gambas3/img/control/drawingarea.png | Bin 0 -> 652 bytes app/src/gambas3/img/control/editor.png | Bin 0 -> 350 bytes app/src/gambas3/img/control/embedder.png | Bin 0 -> 1510 bytes app/src/gambas3/img/control/frame.png | Bin 0 -> 554 bytes app/src/gambas3/img/control/ftpclient.png | Bin 0 -> 1128 bytes app/src/gambas3/img/control/glarea.png | Bin 0 -> 1617 bytes app/src/gambas3/img/control/gridview.png | Bin 0 -> 155 bytes app/src/gambas3/img/control/hbox.png | Bin 0 -> 189 bytes app/src/gambas3/img/control/hpanel.png | Bin 0 -> 191 bytes app/src/gambas3/img/control/hsplit.png | Bin 0 -> 207 bytes app/src/gambas3/img/control/httpclient.png | Bin 0 -> 1170 bytes app/src/gambas3/img/control/iconview.png | Bin 0 -> 145 bytes app/src/gambas3/img/control/label.png | Bin 0 -> 621 bytes app/src/gambas3/img/control/lcdnumber.png | Bin 0 -> 442 bytes app/src/gambas3/img/control/listbox.png | Bin 0 -> 135 bytes app/src/gambas3/img/control/listview.png | Bin 0 -> 141 bytes app/src/gambas3/img/control/moviebox.png | Bin 0 -> 493 bytes app/src/gambas3/img/control/panel.png | Bin 0 -> 138 bytes app/src/gambas3/img/control/picturebox.png | Bin 0 -> 237 bytes app/src/gambas3/img/control/printer.png | Bin 0 -> 171 bytes app/src/gambas3/img/control/progressbar.png | Bin 0 -> 176 bytes app/src/gambas3/img/control/radiobutton.png | Bin 0 -> 502 bytes app/src/gambas3/img/control/scrollarea.png | Bin 0 -> 642 bytes app/src/gambas3/img/control/scrollbar.png | Bin 0 -> 209 bytes app/src/gambas3/img/control/scrollview.png | Bin 0 -> 180 bytes app/src/gambas3/img/control/select.png | Bin 0 -> 984 bytes app/src/gambas3/img/control/separator.png | Bin 0 -> 133 bytes app/src/gambas3/img/control/serialport.png | Bin 0 -> 419 bytes app/src/gambas3/img/control/serversocket.png | Bin 0 -> 484 bytes app/src/gambas3/img/control/slider.png | Bin 0 -> 262 bytes app/src/gambas3/img/control/socket.png | Bin 0 -> 358 bytes app/src/gambas3/img/control/spinbox.png | Bin 0 -> 213 bytes app/src/gambas3/img/control/spring.png | Bin 0 -> 165 bytes app/src/gambas3/img/control/tabstrip.png | Bin 0 -> 236 bytes app/src/gambas3/img/control/textarea.png | Bin 0 -> 460 bytes app/src/gambas3/img/control/textbox.png | Bin 0 -> 483 bytes app/src/gambas3/img/control/textedit.png | Bin 0 -> 966 bytes app/src/gambas3/img/control/textlabel.png | Bin 0 -> 736 bytes app/src/gambas3/img/control/timer.png | Bin 0 -> 493 bytes app/src/gambas3/img/control/togglebutton.png | Bin 0 -> 252 bytes app/src/gambas3/img/control/toolbutton.png | Bin 0 -> 631 bytes app/src/gambas3/img/control/trayicon.png | Bin 0 -> 583 bytes app/src/gambas3/img/control/treeview.png | Bin 0 -> 172 bytes app/src/gambas3/img/control/udpsocket.png | Bin 0 -> 640 bytes app/src/gambas3/img/control/unknown.png | Bin 0 -> 125 bytes app/src/gambas3/img/control/vbox.png | Bin 0 -> 191 bytes app/src/gambas3/img/control/voidbutton.png | Bin 0 -> 445 bytes app/src/gambas3/img/control/vpanel.png | Bin 0 -> 200 bytes app/src/gambas3/img/control/vsplit.png | Bin 0 -> 219 bytes app/src/gambas3/img/control/webview.png | Bin 0 -> 1076 bytes app/src/gambas3/img/draw/cap-butt-dark.png | Bin 0 -> 141 bytes app/src/gambas3/img/draw/cap-butt.png | Bin 0 -> 141 bytes app/src/gambas3/img/draw/cap-round-dark.png | Bin 0 -> 288 bytes app/src/gambas3/img/draw/cap-round.png | Bin 0 -> 288 bytes app/src/gambas3/img/draw/cap-square-dark.png | Bin 0 -> 146 bytes app/src/gambas3/img/draw/cap-square.png | Bin 0 -> 146 bytes app/src/gambas3/img/draw/crop-dark.png | Bin 0 -> 160 bytes app/src/gambas3/img/draw/crop.png | Bin 0 -> 160 bytes app/src/gambas3/img/draw/desaturate-dark.png | Bin 0 -> 160 bytes app/src/gambas3/img/draw/desaturate.png | Bin 0 -> 183 bytes app/src/gambas3/img/draw/difference-dark.png | Bin 0 -> 163 bytes app/src/gambas3/img/draw/difference.png | Bin 0 -> 163 bytes app/src/gambas3/img/draw/duplicate-dark.png | Bin 0 -> 155 bytes app/src/gambas3/img/draw/duplicate.png | Bin 0 -> 155 bytes app/src/gambas3/img/draw/ellipse-dark.png | Bin 0 -> 261 bytes app/src/gambas3/img/draw/ellipse.png | Bin 0 -> 261 bytes app/src/gambas3/img/draw/exclusive-dark.png | Bin 0 -> 164 bytes app/src/gambas3/img/draw/exclusive.png | Bin 0 -> 164 bytes app/src/gambas3/img/draw/grid-dark.png | Bin 0 -> 137 bytes app/src/gambas3/img/draw/grid.png | Bin 0 -> 137 bytes app/src/gambas3/img/draw/hflip-dark.png | Bin 0 -> 160 bytes app/src/gambas3/img/draw/hflip.png | Bin 0 -> 160 bytes app/src/gambas3/img/draw/hide-dark.png | Bin 0 -> 175 bytes app/src/gambas3/img/draw/hide.png | Bin 0 -> 175 bytes .../gambas3/img/draw/intersection-dark.png | Bin 0 -> 164 bytes app/src/gambas3/img/draw/intersection.png | Bin 0 -> 164 bytes app/src/gambas3/img/draw/invert-dark.png | Bin 0 -> 182 bytes app/src/gambas3/img/draw/invert.png | Bin 0 -> 182 bytes app/src/gambas3/img/draw/join-bevel-dark.png | Bin 0 -> 202 bytes app/src/gambas3/img/draw/join-bevel.png | Bin 0 -> 202 bytes app/src/gambas3/img/draw/join-miter-dark.png | Bin 0 -> 146 bytes app/src/gambas3/img/draw/join-miter.png | Bin 0 -> 146 bytes app/src/gambas3/img/draw/join-round-dark.png | Bin 0 -> 289 bytes app/src/gambas3/img/draw/join-round.png | Bin 0 -> 289 bytes app/src/gambas3/img/draw/line-dark.png | Bin 0 -> 261 bytes app/src/gambas3/img/draw/line.png | Bin 0 -> 261 bytes app/src/gambas3/img/draw/magic-dark.png | Bin 0 -> 168 bytes app/src/gambas3/img/draw/magic.png | Bin 0 -> 168 bytes app/src/gambas3/img/draw/move.png | Bin 0 -> 163 bytes app/src/gambas3/img/draw/offset-dark.png | Bin 0 -> 157 bytes app/src/gambas3/img/draw/offset.png | Bin 0 -> 157 bytes app/src/gambas3/img/draw/rectangle-dark.png | Bin 0 -> 140 bytes app/src/gambas3/img/draw/rectangle.png | Bin 0 -> 140 bytes app/src/gambas3/img/draw/resize-dark.png | Bin 0 -> 170 bytes app/src/gambas3/img/draw/resize.png | Bin 0 -> 170 bytes app/src/gambas3/img/draw/rotate-dark.png | Bin 0 -> 210 bytes app/src/gambas3/img/draw/rotate-left-dark.png | Bin 0 -> 160 bytes app/src/gambas3/img/draw/rotate-left.png | Bin 0 -> 160 bytes .../gambas3/img/draw/rotate-right-dark.png | Bin 0 -> 161 bytes app/src/gambas3/img/draw/rotate-right.png | Bin 0 -> 161 bytes app/src/gambas3/img/draw/rotate.png | Bin 0 -> 210 bytes app/src/gambas3/img/draw/scroll-dark.png | Bin 0 -> 212 bytes app/src/gambas3/img/draw/scroll.png | Bin 0 -> 219 bytes app/src/gambas3/img/draw/shgrid.png | Bin 0 -> 133 bytes app/src/gambas3/img/draw/text-base-dark.png | Bin 0 -> 191 bytes app/src/gambas3/img/draw/text-base.png | Bin 0 -> 191 bytes app/src/gambas3/img/draw/text-bottom-dark.png | Bin 0 -> 192 bytes app/src/gambas3/img/draw/text-bottom.png | Bin 0 -> 192 bytes app/src/gambas3/img/draw/text-dark.png | Bin 0 -> 173 bytes app/src/gambas3/img/draw/text-middle-dark.png | Bin 0 -> 189 bytes app/src/gambas3/img/draw/text-middle.png | Bin 0 -> 189 bytes app/src/gambas3/img/draw/text-top-dark.png | Bin 0 -> 192 bytes app/src/gambas3/img/draw/text-top.png | Bin 0 -> 192 bytes app/src/gambas3/img/draw/text.png | Bin 0 -> 173 bytes app/src/gambas3/img/draw/transparent-dark.png | Bin 0 -> 157 bytes app/src/gambas3/img/draw/transparent.png | Bin 0 -> 157 bytes app/src/gambas3/img/draw/union-dark.png | Bin 0 -> 164 bytes app/src/gambas3/img/draw/union.png | Bin 0 -> 164 bytes app/src/gambas3/img/draw/vflip-dark.png | Bin 0 -> 161 bytes app/src/gambas3/img/draw/vflip.png | Bin 0 -> 161 bytes app/src/gambas3/img/family/column-invert.png | Bin 0 -> 153 bytes app/src/gambas3/img/family/column.png | Bin 0 -> 150 bytes app/src/gambas3/img/family/expand.png | Bin 0 -> 155 bytes app/src/gambas3/img/family/fill.png | Bin 0 -> 138 bytes .../gambas3/img/family/horizontal-invert.png | Bin 0 -> 142 bytes app/src/gambas3/img/family/horizontal.png | Bin 0 -> 140 bytes app/src/gambas3/img/family/hsplit.png | Bin 0 -> 144 bytes app/src/gambas3/img/family/row-invert.png | Bin 0 -> 152 bytes app/src/gambas3/img/family/row.png | Bin 0 -> 151 bytes .../gambas3/img/family/vertical-invert.png | Bin 0 -> 141 bytes app/src/gambas3/img/family/vertical.png | Bin 0 -> 139 bytes app/src/gambas3/img/family/vsplit.png | Bin 0 -> 150 bytes app/src/gambas3/img/fir.png | Bin 0 -> 6977 bytes app/src/gambas3/img/logo/archlinux.png | Bin 0 -> 2137 bytes app/src/gambas3/img/logo/autotools.png | Bin 0 -> 2115 bytes app/src/gambas3/img/logo/debian.png | Bin 0 -> 2277 bytes app/src/gambas3/img/logo/fedora.png | Bin 0 -> 2846 bytes app/src/gambas3/img/logo/folder.png | Bin 0 -> 2647 bytes app/src/gambas3/img/logo/gambas.svg | 540 + app/src/gambas3/img/logo/head-16.png | Bin 0 -> 514 bytes app/src/gambas3/img/logo/head-256.png | Bin 0 -> 14310 bytes app/src/gambas3/img/logo/head-32.png | Bin 0 -> 946 bytes app/src/gambas3/img/logo/logo-ide.png | Bin 0 -> 6834 bytes app/src/gambas3/img/logo/logo.png | Bin 0 -> 5164 bytes app/src/gambas3/img/logo/mageia.png | Bin 0 -> 1967 bytes app/src/gambas3/img/logo/mandriva.png | Bin 0 -> 2048 bytes app/src/gambas3/img/logo/package-gnu.png | Bin 0 -> 3166 bytes app/src/gambas3/img/logo/project.png | Bin 0 -> 1242 bytes app/src/gambas3/img/logo/redhat.png | Bin 0 -> 1373 bytes app/src/gambas3/img/logo/self-extractible.png | Bin 0 -> 2595 bytes app/src/gambas3/img/logo/slackware.png | Bin 0 -> 3840 bytes app/src/gambas3/img/logo/suse.png | Bin 0 -> 2386 bytes app/src/gambas3/img/logo/ubuntu.png | Bin 0 -> 2113 bytes app/src/gambas3/img/module/class-dark.png | Bin 0 -> 311 bytes app/src/gambas3/img/module/class.png | Bin 0 -> 311 bytes app/src/gambas3/img/module/form-dark.png | Bin 0 -> 147 bytes app/src/gambas3/img/module/form.png | Bin 0 -> 147 bytes app/src/gambas3/img/module/module-dark.png | Bin 0 -> 458 bytes app/src/gambas3/img/module/module.png | Bin 0 -> 459 bytes app/src/gambas3/img/module/report-dark.png | Bin 0 -> 175 bytes app/src/gambas3/img/module/report.png | Bin 0 -> 178 bytes app/src/gambas3/img/module/termform-dark.png | Bin 0 -> 483 bytes app/src/gambas3/img/module/termform.png | Bin 0 -> 544 bytes app/src/gambas3/img/module/webform-dark.png | Bin 0 -> 598 bytes app/src/gambas3/img/module/webform.png | Bin 0 -> 637 bytes app/src/gambas3/img/module/webpage-dark.png | Bin 0 -> 271 bytes app/src/gambas3/img/module/webpage.png | Bin 0 -> 271 bytes app/src/gambas3/img/search.png | Bin 0 -> 2518 bytes app/src/gambas3/img/snowflake.png | Bin 0 -> 5197 bytes app/src/gambas3/img/symbol/constant.png | Bin 0 -> 134 bytes app/src/gambas3/img/symbol/control.png | Bin 0 -> 134 bytes app/src/gambas3/img/symbol/event.png | Bin 0 -> 302 bytes app/src/gambas3/img/symbol/method.png | Bin 0 -> 214 bytes app/src/gambas3/img/symbol/property-ro.png | Bin 0 -> 134 bytes app/src/gambas3/img/symbol/property.png | Bin 0 -> 134 bytes app/src/gambas3/img/symbol/s-method.png | Bin 0 -> 214 bytes app/src/gambas3/img/symbol/s-property-ro.png | Bin 0 -> 134 bytes app/src/gambas3/img/symbol/s-property.png | Bin 0 -> 134 bytes app/src/gambas3/img/symbol/s-variable.png | Bin 0 -> 134 bytes app/src/gambas3/img/symbol/special.png | Bin 0 -> 234 bytes app/src/gambas3/img/symbol/symbol.png | Bin 0 -> 164 bytes app/src/gambas3/img/symbol/unknown.png | Bin 0 -> 147 bytes app/src/gambas3/img/symbol/variable.png | Bin 0 -> 134 bytes app/src/gambas3/img/waiting.gif | Bin 0 -> 4931 bytes app/src/gambas3/img/welcome/border.png | Bin 0 -> 445 bytes app/src/gambas3/img/welcome/corner.png | Bin 0 -> 2845 bytes app/src/gambas3/install/Makefile.am | 57 + app/src/gambas3/install/acinclude.m4 | 158 + app/src/gambas3/install/categories | 136 + app/src/gambas3/install/group/archlinux | 51 + app/src/gambas3/install/group/autotools | 1 + app/src/gambas3/install/group/debian | 57 + app/src/gambas3/install/group/fedora | 34 + app/src/gambas3/install/group/mageia | 99 + app/src/gambas3/install/group/mandriva | 99 + app/src/gambas3/install/group/self | 1 + app/src/gambas3/install/group/slackware | 34 + app/src/gambas3/install/group/suse | 225 + app/src/gambas3/install/group/ubuntu | 57 + app/src/gambas3/install/menu/archlinux | 64 + app/src/gambas3/install/menu/autotools | 1 + app/src/gambas3/install/menu/debian | 64 + app/src/gambas3/install/menu/fedora | 109 + app/src/gambas3/install/menu/mageia | 111 + app/src/gambas3/install/menu/mandriva | 111 + app/src/gambas3/install/menu/self | 1 + app/src/gambas3/install/menu/slackware | 109 + app/src/gambas3/install/menu/suse | 109 + app/src/gambas3/install/menu/ubuntu | 64 + app/src/gambas3/install/slack-desc-header | 7 + app/src/gambas3/license | 15 + app/src/gambas3/po-header.txt | 10 + app/src/gambas3/pot-header.txt | 10 + app/src/gambas3/shortcut.desktop | 10 + app/src/gambas3/snippets | 57 + app/src/gambas3/support.txt | 145 + app/src/gambas3/tags.txt | 19 + app/src/gambas3/template/CClass.class | 9 + app/src/gambas3/template/CContainer.class | 9 + app/src/gambas3/template/CControl.class | 29 + app/src/gambas3/template/FMain.class | 9 + app/src/gambas3/template/FMain.form | 6 + app/src/gambas3/template/FTest.class | 9 + app/src/gambas3/template/FTest.form | 5 + app/src/gambas3/template/MMain.module | 5 + app/src/gambas3/template/MTest.module | 9 + app/src/gambas3/template/_project | 108 + app/src/gambas3/template/ccontainer.png | Bin 0 -> 1606 bytes app/src/gambas3/template/ccontrol.png | Bin 0 -> 1606 bytes app/src/gambas3/template/list | 22 + app/src/gambas3/theme/amber | 23 + app/src/gambas3/theme/amethyst | 22 + app/src/gambas3/theme/blues | 23 + app/src/gambas3/theme/emerald | 22 + app/src/gambas3/theme/gambas | 22 + app/src/gambas3/theme/obsidian | 23 + app/src/gambas3/theme/pastel | 23 + app/src/gambas3/theme/quest | 22 + app/src/gambas3/theme/quick | 23 + app/src/gambas3/theme/ruby | 22 + app/src/gambas3/theme/sapphire | 23 + app/src/gambas3/theme/visual | 22 + app/src/gambas3/theme/zen | 23 + app/src/gambas3/tips/tips.ca | 463 + app/src/gambas3/tips/tips.cs | 547 + app/src/gambas3/tips/tips.de | 417 + app/src/gambas3/tips/tips.en | 592 + app/src/gambas3/tips/tips.es | 302 + app/src/gambas3/tips/tips.fr | 604 + app/src/gambas3/tips/tips.it | 256 + app/src/gambas3/tips/tips.nl | 274 + app/src/gambas3/tips/tips.pl | 280 + app/src/gambas3/tips/tips.sl | 236 + app/src/gambas3/tips/tips.sv | 401 + app/src/gambas3/tips/tips.tr | 278 + app/src/gambas3/tips/tips.zh | 341 + app/src/gambas3/tips/tips.zh_TW | 442 + app/src/gambas3/usage | 29 + app/src/gb.wiki/.directory | 2 + app/src/gb.wiki/.icon.png | Bin 0 -> 4229 bytes app/src/gb.wiki/.lang/nl.mo | Bin 0 -> 2352 bytes app/src/gb.wiki/.lang/nl.po | 188 + app/src/gb.wiki/.lang/zh.mo | Bin 0 -> 2255 bytes app/src/gb.wiki/.lang/zh.po | 188 + app/src/gb.wiki/.project | 16 + app/src/gb.wiki/.src/Actions.module | 189 + app/src/gb.wiki/.src/Controllers/Admin.module | 115 + app/src/gb.wiki/.src/Controllers/Image.module | 42 + .../.src/Controllers/UserManage.module | 109 + app/src/gb.wiki/.src/Controllers/Wiki.module | 64 + app/src/gb.wiki/.src/DBScheme.class | 21 + app/src/gb.wiki/.src/Main.module | 253 + app/src/gb.wiki/.src/Models/Comments.module | 2 + app/src/gb.wiki/.src/Models/Pages.module | 58 + app/src/gb.wiki/.src/Models/Users.module | 163 + app/src/gb.wiki/.src/Models/_Page.class | 171 + app/src/gb.wiki/.src/Models/_User.class | 162 + app/src/gb.wiki/.src/Parser.module | 1223 + app/src/gb.wiki/.src/Path.module | 120 + .../gb.wiki/.src/Views/Admin/WAdminMain.class | 2 + .../.src/Views/Admin/WAdminMain.webpage | 78 + app/src/gb.wiki/.src/Views/WAccount.class | 2 + app/src/gb.wiki/.src/Views/WAccount.webpage | 52 + app/src/gb.wiki/.src/Views/WEdit.class | 2 + app/src/gb.wiki/.src/Views/WEdit.webpage | 27 + app/src/gb.wiki/.src/Views/WHeader.class | 190 + app/src/gb.wiki/.src/Views/WHeader.webpage | 242 + app/src/gb.wiki/.src/Views/WLogin.class | 2 + app/src/gb.wiki/.src/Views/WLogin.webpage | 23 + app/src/gb.wiki/.src/Views/WWiki.class | 1 + app/src/gb.wiki/.src/Views/WWiki.webpage | 17 + app/src/gb.wiki/help.png | Bin 0 -> 1563 bytes app/src/gbs3/.directory | 2 + app/src/gbs3/.icon.png | Bin 0 -> 17137 bytes app/src/gbs3/.project | 18 + app/src/gbs3/.src/CComponent.class | 307 + app/src/gbs3/.src/MMain.module | 572 + app/src/gbs3/.src/MServerPage.module | 145 + app/src/gbs3/icon.png | Bin 0 -> 1528 bytes app/src/gbs3/license | 6 + app/src/gbs3/usage-gbs | 14 + app/src/gbs3/usage-gbw | 13 + app/template/console/.directory | 2 + app/template/console/.icon.png | Bin 0 -> 7173 bytes app/template/console/.lang/es.mo | Bin 0 -> 525 bytes app/template/console/.lang/es.po | 19 + app/template/console/.lang/es_ES.mo | Bin 0 -> 528 bytes app/template/console/.lang/es_ES.po | 19 + app/template/console/.lang/fr.mo | Bin 0 -> 577 bytes app/template/console/.lang/fr.po | 19 + app/template/console/.project | 11 + app/template/console/.src/Main.module | 7 + app/template/console/icon.png | Bin 0 -> 264 bytes app/template/database/.directory | 2 + app/template/database/.icon.png | Bin 0 -> 6607 bytes app/template/database/.project | 16 + app/template/database/.src/FMain.class | 2 + app/template/database/.src/FMain.form | 5 + app/template/database/icon.png | Bin 0 -> 147 bytes app/template/graphical/.directory | 2 + app/template/graphical/.icon.png | Bin 0 -> 9715 bytes app/template/graphical/.lang/es.mo | Bin 0 -> 424 bytes app/template/graphical/.lang/es.po | 19 + app/template/graphical/.lang/es_ES.mo | Bin 0 -> 427 bytes app/template/graphical/.lang/es_ES.po | 19 + app/template/graphical/.lang/fr.mo | Bin 0 -> 438 bytes app/template/graphical/.lang/fr.po | 19 + app/template/graphical/.project | 14 + app/template/graphical/.src/FMain.class | 2 + app/template/graphical/.src/FMain.form | 5 + app/template/graphical/icon.png | Bin 0 -> 429 bytes app/template/gtk2/.directory | 2 + app/template/gtk2/.icon.png | Bin 0 -> 13010 bytes app/template/gtk2/.lang/es.mo | Bin 0 -> 470 bytes app/template/gtk2/.lang/es.po | 19 + app/template/gtk2/.lang/es_ES.mo | Bin 0 -> 473 bytes app/template/gtk2/.lang/es_ES.po | 19 + app/template/gtk2/.lang/fr.mo | Bin 0 -> 486 bytes app/template/gtk2/.lang/fr.po | 19 + app/template/gtk2/.project | 14 + app/template/gtk2/.src/FMain.class | 2 + app/template/gtk2/.src/FMain.form | 5 + app/template/gtk2/icon.png | Bin 0 -> 644 bytes app/template/gtk3/.directory | 2 + app/template/gtk3/.icon.png | Bin 0 -> 13132 bytes app/template/gtk3/.lang/es.mo | Bin 0 -> 470 bytes app/template/gtk3/.lang/es.po | 19 + app/template/gtk3/.lang/fr.mo | Bin 0 -> 486 bytes app/template/gtk3/.lang/fr.po | 19 + app/template/gtk3/.project | 14 + app/template/gtk3/.src/FMain.class | 2 + app/template/gtk3/.src/FMain.form | 5 + app/template/gtk3/icon.png | Bin 0 -> 647 bytes app/template/qt/.directory | 2 + app/template/qt/.icon.png | Bin 0 -> 13190 bytes app/template/qt/.lang/es.mo | Bin 0 -> 554 bytes app/template/qt/.lang/es.po | 19 + app/template/qt/.lang/es_ES.mo | Bin 0 -> 557 bytes app/template/qt/.lang/es_ES.po | 19 + app/template/qt/.lang/fr.mo | Bin 0 -> 606 bytes app/template/qt/.lang/fr.po | 19 + app/template/qt/.project | 14 + app/template/qt/.src/FMain.class | 2 + app/template/qt/.src/FMain.form | 5 + app/template/qt/icon.png | Bin 0 -> 769 bytes app/template/sdl/.directory | 2 + app/template/sdl/.icon.png | Bin 0 -> 10332 bytes app/template/sdl/.lang/es.mo | Bin 0 -> 503 bytes app/template/sdl/.lang/es.po | 19 + app/template/sdl/.lang/es_ES.mo | Bin 0 -> 506 bytes app/template/sdl/.lang/es_ES.po | 19 + app/template/sdl/.lang/fr.mo | Bin 0 -> 517 bytes app/template/sdl/.lang/fr.po | 19 + app/template/sdl/.project | 14 + app/template/sdl/.src/MMain.module | 40 + app/template/sdl/icon.png | Bin 0 -> 538 bytes app/template/web/.directory | 2 + app/template/web/.icon.png | Bin 0 -> 14817 bytes app/template/web/.lang/es.mo | Bin 0 -> 564 bytes app/template/web/.lang/es.po | 19 + app/template/web/.lang/es_ES.mo | Bin 0 -> 567 bytes app/template/web/.lang/es_ES.po | 19 + app/template/web/.lang/fr.mo | Bin 0 -> 648 bytes app/template/web/.lang/fr.po | 19 + app/template/web/.project | 14 + app/template/web/.src/Main.class | 2 + app/template/web/.src/Main.webpage | 27 + app/template/web/icon.png | Bin 0 -> 775 bytes app/template/webform/.directory | 2 + app/template/webform/.icon.png | Bin 0 -> 12020 bytes app/template/webform/.lang/fr.mo | Bin 0 -> 675 bytes app/template/webform/.lang/fr.po | 27 + app/template/webform/.project | 16 + app/template/webform/.src/Webform1.class | 22 + app/template/webform/.src/Webform1.webform | 27 + app/template/webform/icon.png | Bin 0 -> 668 bytes app/template/~subversion/.directory | 2 + app/template/~subversion/.icon.png | Bin 0 -> 9478 bytes app/template/~subversion/.lang/es.mo | Bin 0 -> 479 bytes app/template/~subversion/.lang/es.po | 19 + app/template/~subversion/.lang/es_ES.mo | Bin 0 -> 482 bytes app/template/~subversion/.lang/es_ES.po | 19 + app/template/~subversion/.lang/fr.mo | Bin 0 -> 499 bytes app/template/~subversion/.lang/fr.po | 19 + app/template/~subversion/.project | 11 + app/template/~subversion/.src/Main.module | 7 + app/template/~subversion/icon.png | Bin 0 -> 465 bytes benchmark/benchmark.gbs | 136 + benchmark/mandelbrot.gbs | 80 + benchmark/mandelbrot.pl | 75 + benchmark/mandelbrot.py | 54 + benchmark/nbody.gbs | 212 + benchmark/nbody.pl | 112 + benchmark/nbody.py | 120 + benchmark/polynom.gbs | 34 + benchmark/polynom.pl | 35 + benchmark/polynom.py | 23 + benchmark/primes.gbs | 61 + benchmark/primes.pl | 44 + benchmark/primes.py | 30 + benchmark/sort.gbs | 69 + benchmark/sort.pl | 129 + benchmark/sort.py | 115 + benchmark/string.gbs | 23 + benchmark/string.pl | 18 + benchmark/string.py | 23 + build-dist | 34 + comp/AUTHORS | 0 comp/COPYING | 1 + comp/ChangeLog | 0 comp/INSTALL | 231 + comp/Makefile.am | 52 + comp/NEWS | 0 comp/README | 0 comp/TODO | 0 comp/acinclude.m4 | 1 + comp/configure.ac | 16 + comp/reconf | 1 + comp/src/gb.args/.component | 4 + comp/src/gb.args/.directory | 2 + comp/src/gb.args/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.args/.lang/cs.mo | Bin 0 -> 658 bytes comp/src/gb.args/.lang/cs.po | 36 + comp/src/gb.args/.lang/es.mo | Bin 0 -> 611 bytes comp/src/gb.args/.lang/es.po | 35 + comp/src/gb.args/.lang/es_ES.mo | Bin 0 -> 614 bytes comp/src/gb.args/.lang/es_ES.po | 35 + comp/src/gb.args/.lang/fr.mo | Bin 0 -> 642 bytes comp/src/gb.args/.lang/fr.po | 35 + comp/src/gb.args/.lang/nl.mo | Bin 0 -> 618 bytes comp/src/gb.args/.lang/nl.po | 36 + comp/src/gb.args/.lang/zh.mo | Bin 0 -> 633 bytes comp/src/gb.args/.lang/zh.po | 36 + comp/src/gb.args/.project | 13 + comp/src/gb.args/.src/Args.module | 214 + comp/src/gb.args/.src/MMain.module | 16 + comp/src/gb.chart/.component | 7 + comp/src/gb.chart/.directory | 2 + comp/src/gb.chart/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.chart/.project | 15 + comp/src/gb.chart/.src/CPoint.class | 13 + comp/src/gb.chart/.src/CRect.class | 16 + comp/src/gb.chart/.src/Chart.class | 376 + comp/src/gb.chart/.src/ChartStyle.class | 9 + comp/src/gb.chart/.src/ChartType.class | 33 + comp/src/gb.chart/.src/FTest.class | 510 + comp/src/gb.chart/.src/FTest.form | 234 + comp/src/gb.chart/.src/FTest2.class | 45 + comp/src/gb.chart/.src/FTest2.form | 19 + comp/src/gb.chart/.src/MTools.module | 376 + comp/src/gb.chart/.src/Styles/_CSerie.class | 151 + .../gb.chart/.src/Styles/_CStyleAreas.class | 135 + .../src/gb.chart/.src/Styles/_CStyleBar.class | 109 + .../gb.chart/.src/Styles/_CStyleColumns.class | 113 + .../gb.chart/.src/Styles/_CStyleLine.class | 122 + .../src/gb.chart/.src/Styles/_CStylePie.class | 106 + .../gb.chart/.src/Styles/_CStylePlot.class | 94 + comp/src/gb.chart/.src/_CAxes.class | 89 + comp/src/gb.chart/.src/_CHeaders.class | 103 + comp/src/gb.chart/.src/_CLabel.class | 41 + comp/src/gb.chart/.src/_CLabels.class | 3 + comp/src/gb.chart/.src/_CLegend.class | 206 + comp/src/gb.chart/.src/_CTitle.class | 53 + comp/src/gb.chart/.src/_CXAxe.class | 25 + comp/src/gb.chart/.src/_CYAxe.class | 22 + comp/src/gb.chart/.src/_Colors.class | 65 + comp/src/gb.chart/img/areas.png | Bin 0 -> 308 bytes comp/src/gb.chart/img/areasnormal.png | Bin 0 -> 278 bytes comp/src/gb.chart/img/areaspercent.png | Bin 0 -> 226 bytes comp/src/gb.chart/img/areasstacked.png | Bin 0 -> 308 bytes comp/src/gb.chart/img/bars.png | Bin 0 -> 235 bytes comp/src/gb.chart/img/barsnormal.png | Bin 0 -> 204 bytes comp/src/gb.chart/img/barspercent.png | Bin 0 -> 172 bytes comp/src/gb.chart/img/barsstacked.png | Bin 0 -> 177 bytes comp/src/gb.chart/img/columns.png | Bin 0 -> 201 bytes .../gb.chart/img/columnslinecombination.png | Bin 0 -> 249 bytes comp/src/gb.chart/img/columnsnormal.png | Bin 0 -> 195 bytes comp/src/gb.chart/img/columnspercent.png | Bin 0 -> 194 bytes comp/src/gb.chart/img/columnsstacked.png | Bin 0 -> 191 bytes comp/src/gb.chart/img/lines.png | Bin 0 -> 307 bytes comp/src/gb.chart/img/linesnormal.png | Bin 0 -> 307 bytes comp/src/gb.chart/img/linespercent.png | Bin 0 -> 217 bytes comp/src/gb.chart/img/linesstacked.png | Bin 0 -> 309 bytes comp/src/gb.chart/img/linessymbols.png | Bin 0 -> 322 bytes comp/src/gb.chart/img/net.png | Bin 0 -> 334 bytes comp/src/gb.chart/img/pie.png | Bin 0 -> 290 bytes comp/src/gb.chart/img/pienormal.png | Bin 0 -> 290 bytes comp/src/gb.chart/img/pieoffset1.png | Bin 0 -> 315 bytes comp/src/gb.chart/img/pieoffset2.png | Bin 0 -> 333 bytes comp/src/gb.chart/img/pierings.png | Bin 0 -> 367 bytes comp/src/gb.chart/img/plots.png | Bin 0 -> 229 bytes comp/src/gb.chart/img/plotsnormal.png | Bin 0 -> 229 bytes comp/src/gb.chart/img/stockchart.png | Bin 0 -> 246 bytes comp/src/gb.db.form/.component | 6 + .../.connection/Connection1.connection | 8 + .../.connection/Connection2.connection | 11 + .../.connection/Connection3.connection | 12 + .../.connection/Connection4.connection | 11 + comp/src/gb.db.form/.directory | 2 + .../.hidden/control/databrowser.png | Bin 0 -> 184 bytes .../.hidden/control/datacheckbox.png | Bin 0 -> 169 bytes .../gb.db.form/.hidden/control/datacombo.png | Bin 0 -> 281 bytes .../.hidden/control/datacomboview.png | Bin 0 -> 243 bytes .../.hidden/control/datacontrol.png | Bin 0 -> 439 bytes .../gb.db.form/.hidden/control/datasource.png | Bin 0 -> 132 bytes .../gb.db.form/.hidden/control/dataview.png | Bin 0 -> 151 bytes comp/src/gb.db.form/.icon.png | Bin 0 -> 10563 bytes comp/src/gb.db.form/.lang/ca.mo | Bin 0 -> 1846 bytes comp/src/gb.db.form/.lang/ca.po | 156 + comp/src/gb.db.form/.lang/cs.mo | Bin 0 -> 1770 bytes comp/src/gb.db.form/.lang/cs.po | 152 + comp/src/gb.db.form/.lang/de.mo | Bin 0 -> 1875 bytes comp/src/gb.db.form/.lang/de.po | 152 + comp/src/gb.db.form/.lang/es.mo | Bin 0 -> 2442 bytes comp/src/gb.db.form/.lang/es.po | 203 + comp/src/gb.db.form/.lang/es_ES.mo | Bin 0 -> 2465 bytes comp/src/gb.db.form/.lang/es_ES.po | 203 + comp/src/gb.db.form/.lang/fr.mo | Bin 0 -> 1855 bytes comp/src/gb.db.form/.lang/fr.po | 302 + comp/src/gb.db.form/.lang/it.mo | Bin 0 -> 1115 bytes comp/src/gb.db.form/.lang/it.po | 120 + comp/src/gb.db.form/.lang/nl.mo | Bin 0 -> 2503 bytes comp/src/gb.db.form/.lang/nl.po | 204 + comp/src/gb.db.form/.lang/pt_BR.mo | Bin 0 -> 1520 bytes comp/src/gb.db.form/.lang/pt_BR.po | 120 + comp/src/gb.db.form/.lang/sv.mo | Bin 0 -> 1142 bytes comp/src/gb.db.form/.lang/sv.po | 100 + comp/src/gb.db.form/.lang/zh.mo | Bin 0 -> 2393 bytes comp/src/gb.db.form/.lang/zh.po | 207 + comp/src/gb.db.form/.project | 22 + comp/src/gb.db.form/.src/Common.module | 110 + comp/src/gb.db.form/.src/DataBrowser.class | 210 + comp/src/gb.db.form/.src/DataCheckBox.class | 163 + comp/src/gb.db.form/.src/DataCombo.class | 344 + comp/src/gb.db.form/.src/DataComboView.class | 493 + comp/src/gb.db.form/.src/DataConnection.class | 147 + comp/src/gb.db.form/.src/DataControl.class | 385 + comp/src/gb.db.form/.src/DataField.class | 82 + comp/src/gb.db.form/.src/DataSource.class | 764 + comp/src/gb.db.form/.src/DataTable.class | 454 + comp/src/gb.db.form/.src/DataTree.class | 145 + comp/src/gb.db.form/.src/DataView.class | 1279 + comp/src/gb.db.form/.src/FBlobEditor.class | 229 + comp/src/gb.db.form/.src/FBlobEditor.form | 49 + comp/src/gb.db.form/.src/FBrowser.class | 272 + comp/src/gb.db.form/.src/FBrowser.form | 65 + comp/src/gb.db.form/.src/Test/FMain.class | 9 + comp/src/gb.db.form/.src/Test/FMain.form | 16 + comp/src/gb.db.form/.src/Test/FMain2.class | 74 + comp/src/gb.db.form/.src/Test/FMain2.form | 139 + comp/src/gb.db.form/.src/Test/FMain3.class | 8 + comp/src/gb.db.form/.src/Test/FMain3.form | 33 + comp/src/gb.db.form/.src/Test/FTest.class | 9 + comp/src/gb.db.form/.src/Test/FTest.form | 46 + comp/src/gb.db.form/.src/Test/Main.module | 24 + comp/src/gb.dbus.trayicon/.component | 7 + comp/src/gb.dbus.trayicon/.directory | 2 + .../.hidden/com.canonical.dbusmenu.xml | 437 + comp/src/gb.dbus.trayicon/.hidden/doc.txt | 167 + comp/src/gb.dbus.trayicon/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.dbus.trayicon/.project | 14 + .../.src/DBusStatusIcon.class | 348 + .../.src/DBusStatusIconMenu.class | 238 + comp/src/gb.dbus.trayicon/.src/FMain.class | 60 + comp/src/gb.dbus.trayicon/.src/FMain.form | 18 + comp/src/gb.dbus.trayicon/.src/FTest.class | 94 + comp/src/gb.dbus.trayicon/.src/FTest.form | 70 + comp/src/gb.dbus.trayicon/.src/Main.module | 56 + comp/src/gb.dbus.trayicon/.src/TrayIcon.class | 323 + .../src/gb.dbus.trayicon/.src/TrayIcons.class | 44 + .../gb.dbus.trayicon/.src/_DBusMenuItem.class | 6 + .../.src/_DBusMenuLayout.class | 6 + .../.src/_DBusMenuProperties.class | 6 + .../.src/_DBusStatusIconPixmap.class | 29 + .../.src/_DBusStatusIconTooltip.class | 16 + .../src/gb.dbus.trayicon/.src/_DBusUInt.class | 16 + comp/src/gb.dbus.trayicon/default.png | Bin 0 -> 9744 bytes comp/src/gb.desktop/.component | 6 + comp/src/gb.desktop/.directory | 2 + .../.hidden/control/desktopwatcher.png | Bin 0 -> 2758 bytes .../.hidden/xdg-utils-1.0.2/LICENSE | 18 + .../.hidden/xdg-utils-1.0.2/scripts/README | 17 + .../xdg-utils-1.0.2/scripts/xdg-desktop-icon | 549 + .../xdg-utils-1.0.2/scripts/xdg-desktop-menu | 1264 + .../.hidden/xdg-utils-1.0.2/scripts/xdg-email | 640 + .../xdg-utils-1.0.2/scripts/xdg-icon-resource | 837 + .../.hidden/xdg-utils-1.0.2/scripts/xdg-mime | 1090 + .../.hidden/xdg-utils-1.0.2/scripts/xdg-open | 436 + .../xdg-utils-1.0.2/scripts/xdg-screensaver | 786 + .../xdg-utils-1.0.3.pre.patch/xdg-email | 730 + .../.hidden/xdg-utils-1.0.3.pre/xdg-copy | 303 + .../xdg-utils-1.0.3.pre/xdg-desktop-icon | 559 + .../xdg-utils-1.0.3.pre/xdg-desktop-menu | 1224 + .../.hidden/xdg-utils-1.0.3.pre/xdg-email | 723 + .../xdg-utils-1.0.3.pre/xdg-file-dialog | 603 + .../xdg-utils-1.0.3.pre/xdg-icon-resource | 841 + .../.hidden/xdg-utils-1.0.3.pre/xdg-mime | 1212 + .../.hidden/xdg-utils-1.0.3.pre/xdg-open | 523 + .../xdg-utils-1.0.3.pre/xdg-screensaver | 888 + .../.hidden/xdg-utils-1.0.3.pre/xdg-settings | 872 + .../.hidden/xdg-utils-1.0.3.pre/xdg-su | 438 + .../.hidden/xdg-utils-1.0.3.pre/xdg-terminal | 462 + comp/src/gb.desktop/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.desktop/.project | 16 + comp/src/gb.desktop/.src/Atom.class | 19 + comp/src/gb.desktop/.src/Desktop.class | 615 + comp/src/gb.desktop/.src/DesktopFile.class | 710 + comp/src/gb.desktop/.src/DesktopMime.class | 484 + comp/src/gb.desktop/.src/DesktopWatcher.class | 97 + comp/src/gb.desktop/.src/DesktopWindow.class | 329 + comp/src/gb.desktop/.src/Main.module | 300 + comp/src/gb.desktop/.src/Tests/Form1.class | 74 + comp/src/gb.desktop/.src/Tests/Form1.form | 25 + comp/src/gb.desktop/.src/Tests/Form11.class | 2 + comp/src/gb.desktop/.src/Tests/Form11.form | 11 + comp/src/gb.desktop/.src/Tests/Form2.class | 35 + comp/src/gb.desktop/.src/Tests/Form2.form | 10 + comp/src/gb.desktop/.src/Tests/Module1.module | 7 + comp/src/gb.desktop/.src/_DesktopIcons.class | 16 + comp/src/gb.desktop/.src/_DesktopMenus.class | 15 + .../src/gb.desktop/.src/_DesktopVirtual.class | 100 + .../gb.desktop/.src/_Desktop_Passwords.class | 174 + .../.src/_Desktop_ScreenSaver.class | 45 + .../gb.desktop/.src/_Desktop_Windows.class | 83 + .../src/gb.desktop/xdg-utils/xdg-desktop-icon | 758 + .../src/gb.desktop/xdg-utils/xdg-desktop-menu | 1448 + comp/src/gb.desktop/xdg-utils/xdg-email | 1037 + .../gb.desktop/xdg-utils/xdg-icon-resource | 1054 + comp/src/gb.desktop/xdg-utils/xdg-mime | 1585 + comp/src/gb.desktop/xdg-utils/xdg-open | 1066 + comp/src/gb.desktop/xdg-utils/xdg-screensaver | 1428 + comp/src/gb.desktop/xdg-utils/xdg-settings | 1390 + comp/src/gb.eval.highlight/.component | 4 + comp/src/gb.eval.highlight/.directory | 2 + comp/src/gb.eval.highlight/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.eval.highlight/.project | 12 + comp/src/gb.eval.highlight/.src/Init.module | 167 + comp/src/gb.eval.highlight/.src/Main.module | 59 + .../.src/OldHighlighter/Helper.module | 71 + .../.src/OldHighlighter/Highlight.class | 75 + .../.src/OldHighlighter/HighlightC.module | 274 + .../OldHighlighter/HighlightCPlusPlus.module | 53 + .../.src/OldHighlighter/HighlightCSS.module | 243 + .../.src/OldHighlighter/HighlightDiff.module | 34 + .../.src/OldHighlighter/HighlightHTML.module | 538 + .../OldHighlighter/HighlightJavascript.module | 287 + .../.src/OldHighlighter/HighlightSQL.module | 159 + .../.src/TextHighlighter.class | 308 + .../.src/TextHighlighterStyle.class | 20 + .../.src/TextHighlighter_C.class | 282 + .../.src/TextHighlighter_CPlusPlus.class | 57 + .../.src/TextHighlighter_CSS.class | 274 + .../.src/TextHighlighter_Diff.class | 35 + .../.src/TextHighlighter_Gambas.class | 78 + .../.src/TextHighlighter_Html.class | 562 + .../.src/TextHighlighter_Javascript.class | 288 + .../.src/TextHighlighter_SQL.class | 171 + .../.src/TextHighlighter_WebPage.class | 11 + comp/src/gb.eval.highlight/css/properties | 232 + comp/src/gb.eval.highlight/css/values | 289 + comp/src/gb.eval.highlight/sql/datatypes | 67 + comp/src/gb.eval.highlight/sql/functions | 206 + comp/src/gb.eval.highlight/sql/keywords | 543 + comp/src/gb.eval.highlight/sql/operators | 58 + comp/src/gb.form.dialog/.component | 6 + comp/src/gb.form.dialog/.directory | 2 + comp/src/gb.form.dialog/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.form.dialog/.lang/ca.mo | Bin 0 -> 753 bytes comp/src/gb.form.dialog/.lang/ca.po | 44 + comp/src/gb.form.dialog/.lang/cs.mo | Bin 0 -> 696 bytes comp/src/gb.form.dialog/.lang/cs.po | 42 + comp/src/gb.form.dialog/.lang/de.mo | Bin 0 -> 708 bytes comp/src/gb.form.dialog/.lang/de.po | 42 + comp/src/gb.form.dialog/.lang/es.mo | Bin 0 -> 712 bytes comp/src/gb.form.dialog/.lang/es.po | 46 + comp/src/gb.form.dialog/.lang/es_ES.mo | Bin 0 -> 713 bytes comp/src/gb.form.dialog/.lang/es_ES.po | 46 + comp/src/gb.form.dialog/.lang/fr.mo | Bin 0 -> 902 bytes comp/src/gb.form.dialog/.lang/fr.po | 63 + comp/src/gb.form.dialog/.lang/it.mo | Bin 0 -> 530 bytes comp/src/gb.form.dialog/.lang/it.po | 28 + comp/src/gb.form.dialog/.lang/ja.mo | Bin 0 -> 558 bytes comp/src/gb.form.dialog/.lang/ja.po | 33 + comp/src/gb.form.dialog/.lang/nl.mo | Bin 0 -> 737 bytes comp/src/gb.form.dialog/.lang/nl.po | 40 + comp/src/gb.form.dialog/.lang/pt_BR.mo | Bin 0 -> 617 bytes comp/src/gb.form.dialog/.lang/pt_BR.po | 38 + comp/src/gb.form.dialog/.lang/sv.mo | Bin 0 -> 605 bytes comp/src/gb.form.dialog/.lang/sv.po | 32 + comp/src/gb.form.dialog/.lang/zh.mo | Bin 0 -> 724 bytes comp/src/gb.form.dialog/.lang/zh.po | 39 + comp/src/gb.form.dialog/.project | 20 + comp/src/gb.form.dialog/.src/Dialog.class | 142 + .../gb.form.dialog/.src/FAskPassword.class | 102 + .../src/gb.form.dialog/.src/FAskPassword.form | 76 + comp/src/gb.form.dialog/.src/FDirDialog.class | 61 + comp/src/gb.form.dialog/.src/FDirDialog.form | 30 + .../src/gb.form.dialog/.src/FFileDialog.class | 132 + comp/src/gb.form.dialog/.src/FFileDialog.form | 18 + .../src/gb.form.dialog/.src/FFontDialog.class | 60 + comp/src/gb.form.dialog/.src/FFontDialog.form | 33 + comp/src/gb.form.dialog/.src/FInputDate.class | 43 + comp/src/gb.form.dialog/.src/FInputDate.form | 37 + comp/src/gb.form.dialog/.src/Main.module | 15 + comp/src/gb.form.editor/.component | 5 + comp/src/gb.form.editor/.directory | 2 + .../.hidden/control/texteditor.png | Bin 0 -> 159 bytes comp/src/gb.form.editor/.icon.png | Bin 0 -> 6144 bytes comp/src/gb.form.editor/.project | 15 + comp/src/gb.form.editor/.src/CCommand.class | 251 + .../gb.form.editor/.src/CCommandBefore.class | 7 + comp/src/gb.form.editor/.src/CDocument.class | 1690 + comp/src/gb.form.editor/.src/CLineInfo.class | 106 + comp/src/gb.form.editor/.src/FTest.class | 10 + comp/src/gb.form.editor/.src/FTest.form | 13 + .../src/gb.form.editor/.src/FTestEditor.class | 202 + comp/src/gb.form.editor/.src/FTestEditor.form | 64 + comp/src/gb.form.editor/.src/Helper.module | 71 + comp/src/gb.form.editor/.src/Main.module | 16 + comp/src/gb.form.editor/.src/TextEditor.class | 5704 ++ .../gb.form.editor/.src/TextEditorMode.class | 280 + .../.src/TextEditorMode_CSS.class | 5 + .../.src/TextEditorMode_Gambas.class | 71 + .../.src/TextEditorMode_HTML.class | 82 + .../.src/TextEditorMode_Javascript.class | 7 + .../.src/TextEditorMode_SQL.class | 5 + .../.src/TextEditorMode_WebPage.class | 3 + .../gb.form.editor/.src/TextEditorStyle.class | 110 + .../.src/_TextEditor_Line.class | 185 + .../.src/_TextEditor_Rows.class | 386 + .../.src/_TextEditor_State.class | 32 + .../.src/_TextEditor_Styles.class | 62 + comp/src/gb.form.editor/Text1 | 19 + comp/src/gb.form.editor/test.html | 89 + comp/src/gb.form.mdi/.component | 6 + comp/src/gb.form.mdi/.directory | 2 + .../gb.form.mdi/.hidden/control/toolbar.png | Bin 0 -> 539 bytes .../gb.form.mdi/.hidden/control/workspace.png | Bin 0 -> 198 bytes comp/src/gb.form.mdi/.icon.png | Bin 0 -> 5376 bytes comp/src/gb.form.mdi/.lang/ca.mo | Bin 0 -> 2338 bytes comp/src/gb.form.mdi/.lang/ca.po | 160 + comp/src/gb.form.mdi/.lang/cs.mo | Bin 0 -> 2247 bytes comp/src/gb.form.mdi/.lang/cs.po | 160 + comp/src/gb.form.mdi/.lang/de.mo | Bin 0 -> 2098 bytes comp/src/gb.form.mdi/.lang/de.po | 270 + comp/src/gb.form.mdi/.lang/es.mo | Bin 0 -> 3238 bytes comp/src/gb.form.mdi/.lang/es.po | 215 + comp/src/gb.form.mdi/.lang/es_ES.mo | Bin 0 -> 3235 bytes comp/src/gb.form.mdi/.lang/es_ES.po | 215 + comp/src/gb.form.mdi/.lang/fr.mo | Bin 0 -> 3044 bytes comp/src/gb.form.mdi/.lang/fr.po | 255 + comp/src/gb.form.mdi/.lang/it.mo | Bin 0 -> 314 bytes comp/src/gb.form.mdi/.lang/it.po | 219 + comp/src/gb.form.mdi/.lang/ja.mo | Bin 0 -> 522 bytes comp/src/gb.form.mdi/.lang/ja.po | 39 + comp/src/gb.form.mdi/.lang/nl.mo | Bin 0 -> 3402 bytes comp/src/gb.form.mdi/.lang/nl.po | 244 + comp/src/gb.form.mdi/.lang/pt_BR.mo | Bin 0 -> 2193 bytes comp/src/gb.form.mdi/.lang/pt_BR.po | 156 + comp/src/gb.form.mdi/.lang/sv.mo | Bin 0 -> 2071 bytes comp/src/gb.form.mdi/.lang/sv.po | 148 + comp/src/gb.form.mdi/.lang/zh.mo | Bin 0 -> 2159 bytes comp/src/gb.form.mdi/.lang/zh.po | 161 + comp/src/gb.form.mdi/.project | 23 + comp/src/gb.form.mdi/.src/Action/Action.class | 112 + .../src/gb.form.mdi/.src/Action/CAction.class | 128 + .../gb.form.mdi/.src/Action/MAction.module | 170 + comp/src/gb.form.mdi/.src/MMain.module | 7 + .../gb.form.mdi/.src/Shortcut/FShortcut.class | 336 + .../gb.form.mdi/.src/Shortcut/FShortcut.form | 61 + .../.src/Shortcut/FShortcutEditor.class | 100 + .../.src/Shortcut/FShortcutEditor.form | 22 + comp/src/gb.form.mdi/.src/Tests/FMain.class | 66 + comp/src/gb.form.mdi/.src/Tests/FMain.form | 116 + comp/src/gb.form.mdi/.src/Tests/FMain1.class | 48 + comp/src/gb.form.mdi/.src/Tests/FMain1.form | 105 + comp/src/gb.form.mdi/.src/Tests/FMain2.class | 1 + comp/src/gb.form.mdi/.src/Tests/FMain2.form | 121 + .../.src/Tests/FTestSidePanel.class | 12 + .../.src/Tests/FTestSidePanel.form | 19 + comp/src/gb.form.mdi/.src/Tests/Form1.class | 17 + comp/src/gb.form.mdi/.src/Tests/Form1.form | 17 + comp/src/gb.form.mdi/.src/Tests/Form2.class | 17 + comp/src/gb.form.mdi/.src/Tests/Form2.form | 22 + .../gb.form.mdi/.src/ToolBar/CToolbar.class | 18 + .../gb.form.mdi/.src/ToolBar/FToolBar.class | 1734 + .../gb.form.mdi/.src/ToolBar/FToolBar.form | 27 + .../.src/ToolBar/FToolBarConfig.class | 500 + .../.src/ToolBar/FToolBarConfig.form | 98 + .../gb.form.mdi/.src/ToolBar/ToolBar.class | 153 + .../.src/ToolBar/ToolBarExpander.class | 9 + .../gb.form.mdi/.src/Workspace/CWindow.class | 9 + .../.src/Workspace/FWorkspace.class | 1044 + .../.src/Workspace/FWorkspace.form | 146 + .../.src/Workspace/Workspace.class | 262 + comp/src/gb.form.mdi/70a017.png | Bin 0 -> 1542 bytes comp/src/gb.form.mdi/control/buttonbox.png | Bin 0 -> 290 bytes comp/src/gb.form.mdi/control/combobox.png | Bin 0 -> 287 bytes comp/src/gb.form.mdi/control/datebox.png | Bin 0 -> 568 bytes comp/src/gb.form.mdi/control/valuebox.png | Bin 0 -> 374 bytes comp/src/gb.form.mdi/img/close.png | Bin 0 -> 112 bytes comp/src/gb.form.mdi/img/configure.png | Bin 0 -> 141 bytes comp/src/gb.form.mdi/img/configure_dark.png | Bin 0 -> 139 bytes comp/src/gb.form.mdi/img/expander.png | Bin 0 -> 188 bytes comp/src/gb.form.mdi/img/handle-v.png | Bin 0 -> 100 bytes comp/src/gb.form.mdi/img/handle.png | Bin 0 -> 97 bytes comp/src/gb.form.mdi/img/hash.png | Bin 0 -> 86 bytes comp/src/gb.form.mdi/img/roll.png | Bin 0 -> 90 bytes comp/src/gb.form.mdi/img/separator.png | Bin 0 -> 92 bytes comp/src/gb.form.mdi/img/space.png | Bin 0 -> 92 bytes comp/src/gb.form.mdi/img/unroll.png | Bin 0 -> 93 bytes comp/src/gb.form.stock/.component | 4 + comp/src/gb.form.stock/.directory | 2 + comp/src/gb.form.stock/.hidden/map | 163 + comp/src/gb.form.stock/.icon.png | Bin 0 -> 5522 bytes comp/src/gb.form.stock/.project | 13 + comp/src/gb.form.stock/.src/Main.module | 215 + .../gb.form.stock/.src/_DefaultStock.class | 119 + .../gb.form.stock/gambas-mono/128/error.png | Bin 0 -> 1208 bytes .../gb.form.stock/gambas-mono/128/gambas.png | Bin 0 -> 2729 bytes .../gb.form.stock/gambas-mono/128/info.png | Bin 0 -> 324 bytes .../gambas-mono/128/question.png | Bin 0 -> 2723 bytes .../gb.form.stock/gambas-mono/128/warning.png | Bin 0 -> 929 bytes .../gb.form.stock/gambas-mono/32/access.png | Bin 0 -> 194 bytes comp/src/gb.form.stock/gambas-mono/32/add.png | Bin 0 -> 143 bytes .../gambas-mono/32/align-bottom.png | Bin 0 -> 160 bytes .../gambas-mono/32/align-center.png | Bin 0 -> 156 bytes .../gambas-mono/32/align-height.png | Bin 0 -> 148 bytes .../gambas-mono/32/align-left.png | Bin 0 -> 153 bytes .../gambas-mono/32/align-middle.png | Bin 0 -> 153 bytes .../gambas-mono/32/align-right.png | Bin 0 -> 169 bytes .../gambas-mono/32/align-top.png | Bin 0 -> 168 bytes .../gambas-mono/32/align-width.png | Bin 0 -> 153 bytes .../gb.form.stock/gambas-mono/32/apply.png | Bin 0 -> 240 bytes .../gb.form.stock/gambas-mono/32/archive.png | Bin 0 -> 180 bytes .../gb.form.stock/gambas-mono/32/attach.png | Bin 0 -> 1129 bytes .../gb.form.stock/gambas-mono/32/audio.png | Bin 0 -> 475 bytes .../gb.form.stock/gambas-mono/32/battery.png | Bin 0 -> 161 bytes .../src/gb.form.stock/gambas-mono/32/book.png | Bin 0 -> 212 bytes .../gb.form.stock/gambas-mono/32/bookmark.png | Bin 0 -> 250 bytes .../gb.form.stock/gambas-mono/32/bottom.png | Bin 0 -> 189 bytes comp/src/gb.form.stock/gambas-mono/32/c.png | Bin 0 -> 424 bytes .../gambas-mono/32/calculator.png | Bin 0 -> 154 bytes .../gb.form.stock/gambas-mono/32/calendar.png | Bin 0 -> 189 bytes .../gb.form.stock/gambas-mono/32/camera.png | Bin 0 -> 234 bytes .../gb.form.stock/gambas-mono/32/cancel.png | Bin 0 -> 337 bytes .../gb.form.stock/gambas-mono/32/cdrom.png | Bin 0 -> 428 bytes .../gambas-mono/32/clear-rtl.png | Bin 0 -> 258 bytes .../gb.form.stock/gambas-mono/32/clear.png | Bin 0 -> 259 bytes .../gb.form.stock/gambas-mono/32/clock.png | Bin 0 -> 498 bytes .../gb.form.stock/gambas-mono/32/close.png | Bin 0 -> 337 bytes .../gambas-mono/32/color-picker.png | Bin 0 -> 242 bytes .../gb.form.stock/gambas-mono/32/color.png | Bin 0 -> 292 bytes .../gambas-mono/32/component.png | Bin 0 -> 144 bytes .../gb.form.stock/gambas-mono/32/computer.png | Bin 0 -> 170 bytes .../gb.form.stock/gambas-mono/32/connect.png | Bin 0 -> 250 bytes .../src/gb.form.stock/gambas-mono/32/copy.png | Bin 0 -> 196 bytes comp/src/gb.form.stock/gambas-mono/32/cpp.png | Bin 0 -> 493 bytes comp/src/gb.form.stock/gambas-mono/32/css.png | Bin 0 -> 666 bytes comp/src/gb.form.stock/gambas-mono/32/cut.png | Bin 0 -> 441 bytes .../gb.form.stock/gambas-mono/32/database.png | Bin 0 -> 362 bytes .../gb.form.stock/gambas-mono/32/delete.png | Bin 0 -> 526 bytes .../gb.form.stock/gambas-mono/32/desktop.png | Bin 0 -> 240 bytes .../gambas-mono/32/development.png | Bin 0 -> 242 bytes .../gambas-mono/32/directory.png | Bin 0 -> 151 bytes .../gambas-mono/32/disconnect.png | Bin 0 -> 253 bytes .../src/gb.form.stock/gambas-mono/32/down.png | Bin 0 -> 182 bytes .../gb.form.stock/gambas-mono/32/download.png | Bin 0 -> 200 bytes .../gb.form.stock/gambas-mono/32/earth.png | Bin 0 -> 1017 bytes .../src/gb.form.stock/gambas-mono/32/edit.png | Bin 0 -> 219 bytes .../gb.form.stock/gambas-mono/32/eject.png | Bin 0 -> 190 bytes comp/src/gb.form.stock/gambas-mono/32/end.png | Bin 0 -> 187 bytes .../gb.form.stock/gambas-mono/32/erase.png | Bin 0 -> 225 bytes .../gb.form.stock/gambas-mono/32/error.png | Bin 0 -> 357 bytes .../src/gb.form.stock/gambas-mono/32/exec.png | Bin 0 -> 278 bytes .../gambas-mono/32/file-manager.png | Bin 0 -> 140 bytes .../src/gb.form.stock/gambas-mono/32/file.png | Bin 0 -> 147 bytes .../src/gb.form.stock/gambas-mono/32/fill.png | Bin 0 -> 313 bytes .../gb.form.stock/gambas-mono/32/filter.png | Bin 0 -> 189 bytes .../src/gb.form.stock/gambas-mono/32/find.png | Bin 0 -> 402 bytes .../gb.form.stock/gambas-mono/32/first.png | Bin 0 -> 220 bytes .../src/gb.form.stock/gambas-mono/32/flag.png | Bin 0 -> 177 bytes .../gb.form.stock/gambas-mono/32/flip-h.png | Bin 0 -> 196 bytes .../gb.form.stock/gambas-mono/32/flip-v.png | Bin 0 -> 193 bytes .../gb.form.stock/gambas-mono/32/floppy.png | Bin 0 -> 184 bytes .../src/gb.form.stock/gambas-mono/32/font.png | Bin 0 -> 404 bytes .../gb.form.stock/gambas-mono/32/forward.png | Bin 0 -> 205 bytes .../gambas-mono/32/fullscreen.png | Bin 0 -> 205 bytes .../gb.form.stock/gambas-mono/32/gambas.png | Bin 0 -> 935 bytes .../src/gb.form.stock/gambas-mono/32/game.png | Bin 0 -> 199 bytes comp/src/gb.form.stock/gambas-mono/32/gnu.png | Bin 0 -> 1370 bytes .../src/gb.form.stock/gambas-mono/32/grid.png | Bin 0 -> 139 bytes .../gb.form.stock/gambas-mono/32/group.png | Bin 0 -> 830 bytes comp/src/gb.form.stock/gambas-mono/32/h.png | Bin 0 -> 237 bytes .../src/gb.form.stock/gambas-mono/32/halt.png | Bin 0 -> 397 bytes .../gb.form.stock/gambas-mono/32/harddisk.png | Bin 0 -> 151 bytes .../gb.form.stock/gambas-mono/32/hardware.png | Bin 0 -> 167 bytes .../src/gb.form.stock/gambas-mono/32/help.png | Bin 0 -> 626 bytes .../src/gb.form.stock/gambas-mono/32/home.png | Bin 0 -> 219 bytes .../src/gb.form.stock/gambas-mono/32/html.png | Bin 0 -> 467 bytes .../gb.form.stock/gambas-mono/32/identity.png | Bin 0 -> 214 bytes .../gb.form.stock/gambas-mono/32/image.png | Bin 0 -> 225 bytes .../gambas-mono/32/important.png | Bin 0 -> 518 bytes .../gb.form.stock/gambas-mono/32/indent.png | Bin 0 -> 183 bytes .../src/gb.form.stock/gambas-mono/32/info.png | Bin 0 -> 196 bytes .../gambas-mono/32/insert-image.png | Bin 0 -> 246 bytes .../gambas-mono/32/insert-link.png | Bin 0 -> 266 bytes .../gambas-mono/32/insert-text.png | Bin 0 -> 200 bytes .../gb.form.stock/gambas-mono/32/internet.png | Bin 0 -> 839 bytes comp/src/gb.form.stock/gambas-mono/32/js.png | Bin 0 -> 547 bytes .../src/gb.form.stock/gambas-mono/32/jump.png | Bin 0 -> 246 bytes .../gb.form.stock/gambas-mono/32/keyboard.png | Bin 0 -> 162 bytes .../src/gb.form.stock/gambas-mono/32/lamp.png | Bin 0 -> 372 bytes .../gb.form.stock/gambas-mono/32/language.png | Bin 0 -> 168 bytes .../src/gb.form.stock/gambas-mono/32/last.png | Bin 0 -> 228 bytes .../src/gb.form.stock/gambas-mono/32/left.png | Bin 0 -> 216 bytes .../src/gb.form.stock/gambas-mono/32/link.png | Bin 0 -> 221 bytes .../gb.form.stock/gambas-mono/32/linux.png | Bin 0 -> 1172 bytes .../src/gb.form.stock/gambas-mono/32/lock.png | Bin 0 -> 265 bytes .../gb.form.stock/gambas-mono/32/lower.png | Bin 0 -> 155 bytes .../src/gb.form.stock/gambas-mono/32/mail.png | Bin 0 -> 168 bytes .../gb.form.stock/gambas-mono/32/make-all.png | Bin 0 -> 193 bytes .../src/gb.form.stock/gambas-mono/32/make.png | Bin 0 -> 191 bytes .../gambas-mono/32/media-player.png | Bin 0 -> 176 bytes .../src/gb.form.stock/gambas-mono/32/menu.png | Bin 0 -> 139 bytes .../gambas-mono/32/microphone.png | Bin 0 -> 451 bytes .../gb.form.stock/gambas-mono/32/monitor.png | Bin 0 -> 239 bytes .../gb.form.stock/gambas-mono/32/mouse.png | Bin 0 -> 226 bytes .../gambas-mono/32/multimedia.png | Bin 0 -> 276 bytes .../gb.form.stock/gambas-mono/32/muted.png | Bin 0 -> 321 bytes .../gb.form.stock/gambas-mono/32/network.png | Bin 0 -> 167 bytes .../gb.form.stock/gambas-mono/32/new-dir.png | Bin 0 -> 177 bytes .../gambas-mono/32/new-window.png | Bin 0 -> 182 bytes comp/src/gb.form.stock/gambas-mono/32/new.png | Bin 0 -> 173 bytes .../src/gb.form.stock/gambas-mono/32/next.png | Bin 0 -> 194 bytes .../gambas-mono/32/office-calc.png | Bin 0 -> 158 bytes .../gambas-mono/32/office-draw.png | Bin 0 -> 237 bytes .../gb.form.stock/gambas-mono/32/office.png | Bin 0 -> 163 bytes comp/src/gb.form.stock/gambas-mono/32/ok.png | Bin 0 -> 240 bytes .../gambas-mono/32/open-recent.png | Bin 0 -> 230 bytes .../src/gb.form.stock/gambas-mono/32/open.png | Bin 0 -> 197 bytes .../gb.form.stock/gambas-mono/32/options.png | Bin 0 -> 272 bytes .../gb.form.stock/gambas-mono/32/package.png | Bin 0 -> 200 bytes .../gb.form.stock/gambas-mono/32/paste.png | Bin 0 -> 183 bytes .../gb.form.stock/gambas-mono/32/pause.png | Bin 0 -> 137 bytes comp/src/gb.form.stock/gambas-mono/32/pda.png | Bin 0 -> 154 bytes comp/src/gb.form.stock/gambas-mono/32/pdf.png | Bin 0 -> 493 bytes comp/src/gb.form.stock/gambas-mono/32/pen.png | Bin 0 -> 209 bytes .../gb.form.stock/gambas-mono/32/people.png | Bin 0 -> 830 bytes .../gb.form.stock/gambas-mono/32/phone.png | Bin 0 -> 154 bytes .../src/gb.form.stock/gambas-mono/32/play.png | Bin 0 -> 184 bytes .../gb.form.stock/gambas-mono/32/plugin.png | Bin 0 -> 286 bytes .../gb.form.stock/gambas-mono/32/preview.png | Bin 0 -> 392 bytes .../gb.form.stock/gambas-mono/32/previous.png | Bin 0 -> 201 bytes .../gb.form.stock/gambas-mono/32/print.png | Bin 0 -> 167 bytes .../gb.form.stock/gambas-mono/32/printer.png | Bin 0 -> 167 bytes .../gb.form.stock/gambas-mono/32/program.png | Bin 0 -> 187 bytes .../gambas-mono/32/properties.png | Bin 0 -> 157 bytes .../gb.form.stock/gambas-mono/32/question.png | Bin 0 -> 742 bytes .../src/gb.form.stock/gambas-mono/32/quit.png | Bin 0 -> 382 bytes .../gb.form.stock/gambas-mono/32/raise.png | Bin 0 -> 158 bytes .../gb.form.stock/gambas-mono/32/recent.png | Bin 0 -> 348 bytes .../gb.form.stock/gambas-mono/32/record.png | Bin 0 -> 321 bytes .../src/gb.form.stock/gambas-mono/32/redo.png | Bin 0 -> 367 bytes .../gb.form.stock/gambas-mono/32/refresh.png | Bin 0 -> 467 bytes .../gb.form.stock/gambas-mono/32/remove.png | Bin 0 -> 137 bytes .../gb.form.stock/gambas-mono/32/replace.png | Bin 0 -> 412 bytes .../gb.form.stock/gambas-mono/32/revert.png | Bin 0 -> 251 bytes .../gb.form.stock/gambas-mono/32/rewind.png | Bin 0 -> 203 bytes .../gb.form.stock/gambas-mono/32/right.png | Bin 0 -> 209 bytes .../gambas-mono/32/rotate-left.png | Bin 0 -> 187 bytes .../gambas-mono/32/rotate-right.png | Bin 0 -> 187 bytes .../gb.form.stock/gambas-mono/32/save-as.png | Bin 0 -> 206 bytes .../src/gb.form.stock/gambas-mono/32/save.png | Bin 0 -> 198 bytes .../gb.form.stock/gambas-mono/32/scanner.png | Bin 0 -> 173 bytes .../gb.form.stock/gambas-mono/32/science.png | Bin 0 -> 241 bytes .../gb.form.stock/gambas-mono/32/screen.png | Bin 0 -> 157 bytes .../gb.form.stock/gambas-mono/32/script.png | Bin 0 -> 427 bytes .../gb.form.stock/gambas-mono/32/security.png | Bin 0 -> 580 bytes .../gambas-mono/32/select-all.png | Bin 0 -> 193 bytes .../gb.form.stock/gambas-mono/32/select.png | Bin 0 -> 181 bytes .../gb.form.stock/gambas-mono/32/server.png | Bin 0 -> 153 bytes .../gb.form.stock/gambas-mono/32/shortcut.png | Bin 0 -> 482 bytes .../gambas-mono/32/sort-ascent.png | Bin 0 -> 184 bytes .../gambas-mono/32/sort-descent.png | Bin 0 -> 189 bytes .../gambas-mono/32/spell-check.png | Bin 0 -> 805 bytes .../src/gb.form.stock/gambas-mono/32/star.png | Bin 0 -> 897 bytes .../gb.form.stock/gambas-mono/32/start.png | Bin 0 -> 191 bytes .../src/gb.form.stock/gambas-mono/32/stop.png | Bin 0 -> 137 bytes comp/src/gb.form.stock/gambas-mono/32/sun.png | Bin 0 -> 420 bytes .../gb.form.stock/gambas-mono/32/system.png | Bin 0 -> 161 bytes .../gb.form.stock/gambas-mono/32/table.png | Bin 0 -> 151 bytes .../gb.form.stock/gambas-mono/32/tablet.png | Bin 0 -> 175 bytes .../gb.form.stock/gambas-mono/32/terminal.png | Bin 0 -> 451 bytes .../gambas-mono/32/text-baseline.png | Bin 0 -> 189 bytes .../gambas-mono/32/text-bold.png | Bin 0 -> 568 bytes .../gambas-mono/32/text-bottom.png | Bin 0 -> 174 bytes .../gambas-mono/32/text-center.png | Bin 0 -> 157 bytes .../gambas-mono/32/text-fill.png | Bin 0 -> 139 bytes .../gambas-mono/32/text-italic.png | Bin 0 -> 686 bytes .../gambas-mono/32/text-left.png | Bin 0 -> 153 bytes .../gambas-mono/32/text-middle.png | Bin 0 -> 191 bytes .../gambas-mono/32/text-right.png | Bin 0 -> 154 bytes .../gambas-mono/32/text-strike.png | Bin 0 -> 587 bytes .../gb.form.stock/gambas-mono/32/text-top.png | Bin 0 -> 173 bytes .../gambas-mono/32/text-underline.png | Bin 0 -> 666 bytes .../src/gb.form.stock/gambas-mono/32/text.png | Bin 0 -> 157 bytes .../gb.form.stock/gambas-mono/32/tools.png | Bin 0 -> 427 bytes comp/src/gb.form.stock/gambas-mono/32/top.png | Bin 0 -> 187 bytes .../gb.form.stock/gambas-mono/32/trash.png | Bin 0 -> 155 bytes .../src/gb.form.stock/gambas-mono/32/undo.png | Bin 0 -> 360 bytes .../gb.form.stock/gambas-mono/32/unindent.png | Bin 0 -> 185 bytes .../gb.form.stock/gambas-mono/32/unlock.png | Bin 0 -> 289 bytes comp/src/gb.form.stock/gambas-mono/32/up.png | Bin 0 -> 180 bytes .../gb.form.stock/gambas-mono/32/upload.png | Bin 0 -> 196 bytes .../src/gb.form.stock/gambas-mono/32/user.png | Bin 0 -> 414 bytes .../gb.form.stock/gambas-mono/32/vector.png | Bin 0 -> 273 bytes .../gb.form.stock/gambas-mono/32/video.png | Bin 0 -> 209 bytes .../gambas-mono/32/view-detail.png | Bin 0 -> 145 bytes .../gambas-mono/32/view-icon.png | Bin 0 -> 137 bytes .../gambas-mono/32/view-normal.png | Bin 0 -> 276 bytes .../gambas-mono/32/view-split-h.png | Bin 0 -> 151 bytes .../gambas-mono/32/view-split-v.png | Bin 0 -> 147 bytes .../gb.form.stock/gambas-mono/32/volume.png | Bin 0 -> 475 bytes comp/src/gb.form.stock/gambas-mono/32/vpn.png | Bin 0 -> 320 bytes .../gb.form.stock/gambas-mono/32/warning.png | Bin 0 -> 336 bytes .../gb.form.stock/gambas-mono/32/watch.png | Bin 0 -> 499 bytes .../gb.form.stock/gambas-mono/32/webcam.png | Bin 0 -> 440 bytes .../src/gb.form.stock/gambas-mono/32/wifi.png | Bin 0 -> 593 bytes .../gb.form.stock/gambas-mono/32/wizard.png | Bin 0 -> 275 bytes .../gb.form.stock/gambas-mono/32/zoom-fit.png | Bin 0 -> 554 bytes .../gb.form.stock/gambas-mono/32/zoom-in.png | Bin 0 -> 453 bytes .../gambas-mono/32/zoom-normal.png | Bin 0 -> 503 bytes .../gb.form.stock/gambas-mono/32/zoom-out.png | Bin 0 -> 450 bytes comp/src/gb.form.stock/gambas/128/error.png | Bin 0 -> 1212 bytes comp/src/gb.form.stock/gambas/128/gambas.png | Bin 0 -> 2901 bytes comp/src/gb.form.stock/gambas/128/info.png | Bin 0 -> 308 bytes .../src/gb.form.stock/gambas/128/question.png | Bin 0 -> 2316 bytes comp/src/gb.form.stock/gambas/128/warning.png | Bin 0 -> 919 bytes comp/src/gb.form.stock/gambas/32/access.png | Bin 0 -> 194 bytes comp/src/gb.form.stock/gambas/32/add.png | Bin 0 -> 143 bytes .../gb.form.stock/gambas/32/align-bottom.png | Bin 0 -> 168 bytes .../gb.form.stock/gambas/32/align-center.png | Bin 0 -> 166 bytes .../gb.form.stock/gambas/32/align-height.png | Bin 0 -> 155 bytes .../gb.form.stock/gambas/32/align-left.png | Bin 0 -> 163 bytes .../gb.form.stock/gambas/32/align-middle.png | Bin 0 -> 163 bytes .../gb.form.stock/gambas/32/align-right.png | Bin 0 -> 174 bytes .../src/gb.form.stock/gambas/32/align-top.png | Bin 0 -> 180 bytes .../gb.form.stock/gambas/32/align-width.png | Bin 0 -> 159 bytes comp/src/gb.form.stock/gambas/32/apply.png | Bin 0 -> 240 bytes comp/src/gb.form.stock/gambas/32/archive.png | Bin 0 -> 180 bytes comp/src/gb.form.stock/gambas/32/attach.png | Bin 0 -> 1129 bytes comp/src/gb.form.stock/gambas/32/audio.png | Bin 0 -> 475 bytes comp/src/gb.form.stock/gambas/32/battery.png | Bin 0 -> 163 bytes comp/src/gb.form.stock/gambas/32/book.png | Bin 0 -> 212 bytes comp/src/gb.form.stock/gambas/32/bookmark.png | Bin 0 -> 249 bytes comp/src/gb.form.stock/gambas/32/bottom.png | Bin 0 -> 189 bytes comp/src/gb.form.stock/gambas/32/c.png | Bin 0 -> 427 bytes .../gb.form.stock/gambas/32/calculator.png | Bin 0 -> 159 bytes comp/src/gb.form.stock/gambas/32/calendar.png | Bin 0 -> 226 bytes comp/src/gb.form.stock/gambas/32/camera.png | Bin 0 -> 231 bytes comp/src/gb.form.stock/gambas/32/cancel.png | Bin 0 -> 332 bytes comp/src/gb.form.stock/gambas/32/cdrom.png | Bin 0 -> 408 bytes .../src/gb.form.stock/gambas/32/clear-rtl.png | Bin 0 -> 258 bytes comp/src/gb.form.stock/gambas/32/clear.png | Bin 0 -> 259 bytes comp/src/gb.form.stock/gambas/32/clock.png | Bin 0 -> 493 bytes comp/src/gb.form.stock/gambas/32/close.png | Bin 0 -> 337 bytes .../gb.form.stock/gambas/32/color-picker.png | Bin 0 -> 238 bytes comp/src/gb.form.stock/gambas/32/color.png | Bin 0 -> 292 bytes .../src/gb.form.stock/gambas/32/component.png | Bin 0 -> 152 bytes comp/src/gb.form.stock/gambas/32/computer.png | Bin 0 -> 173 bytes comp/src/gb.form.stock/gambas/32/connect.png | Bin 0 -> 245 bytes comp/src/gb.form.stock/gambas/32/copy.png | Bin 0 -> 200 bytes comp/src/gb.form.stock/gambas/32/cpp.png | Bin 0 -> 490 bytes comp/src/gb.form.stock/gambas/32/css.png | Bin 0 -> 686 bytes comp/src/gb.form.stock/gambas/32/cut.png | Bin 0 -> 477 bytes comp/src/gb.form.stock/gambas/32/database.png | Bin 0 -> 361 bytes comp/src/gb.form.stock/gambas/32/delete.png | Bin 0 -> 526 bytes comp/src/gb.form.stock/gambas/32/desktop.png | Bin 0 -> 255 bytes .../gb.form.stock/gambas/32/development.png | Bin 0 -> 249 bytes .../src/gb.form.stock/gambas/32/directory.png | Bin 0 -> 151 bytes .../gb.form.stock/gambas/32/disconnect.png | Bin 0 -> 251 bytes comp/src/gb.form.stock/gambas/32/down.png | Bin 0 -> 182 bytes comp/src/gb.form.stock/gambas/32/download.png | Bin 0 -> 200 bytes comp/src/gb.form.stock/gambas/32/earth.png | Bin 0 -> 1090 bytes comp/src/gb.form.stock/gambas/32/edit.png | Bin 0 -> 222 bytes comp/src/gb.form.stock/gambas/32/eject.png | Bin 0 -> 190 bytes comp/src/gb.form.stock/gambas/32/end.png | Bin 0 -> 187 bytes comp/src/gb.form.stock/gambas/32/erase.png | Bin 0 -> 237 bytes comp/src/gb.form.stock/gambas/32/error.png | Bin 0 -> 357 bytes comp/src/gb.form.stock/gambas/32/exec.png | Bin 0 -> 282 bytes .../gb.form.stock/gambas/32/file-manager.png | Bin 0 -> 140 bytes comp/src/gb.form.stock/gambas/32/file.png | Bin 0 -> 147 bytes comp/src/gb.form.stock/gambas/32/fill.png | Bin 0 -> 298 bytes comp/src/gb.form.stock/gambas/32/filter.png | Bin 0 -> 210 bytes comp/src/gb.form.stock/gambas/32/find.png | Bin 0 -> 402 bytes comp/src/gb.form.stock/gambas/32/first.png | Bin 0 -> 220 bytes comp/src/gb.form.stock/gambas/32/flag.png | Bin 0 -> 180 bytes comp/src/gb.form.stock/gambas/32/flip-h.png | Bin 0 -> 196 bytes comp/src/gb.form.stock/gambas/32/flip-v.png | Bin 0 -> 193 bytes comp/src/gb.form.stock/gambas/32/floppy.png | Bin 0 -> 184 bytes comp/src/gb.form.stock/gambas/32/font.png | Bin 0 -> 404 bytes comp/src/gb.form.stock/gambas/32/forward.png | Bin 0 -> 205 bytes .../gb.form.stock/gambas/32/fullscreen.png | Bin 0 -> 205 bytes comp/src/gb.form.stock/gambas/32/gambas.png | Bin 0 -> 946 bytes comp/src/gb.form.stock/gambas/32/game.png | Bin 0 -> 205 bytes comp/src/gb.form.stock/gambas/32/gnu.png | Bin 0 -> 1370 bytes comp/src/gb.form.stock/gambas/32/grid.png | Bin 0 -> 139 bytes comp/src/gb.form.stock/gambas/32/group.png | Bin 0 -> 917 bytes comp/src/gb.form.stock/gambas/32/h.png | Bin 0 -> 215 bytes comp/src/gb.form.stock/gambas/32/halt.png | Bin 0 -> 383 bytes comp/src/gb.form.stock/gambas/32/harddisk.png | Bin 0 -> 164 bytes comp/src/gb.form.stock/gambas/32/hardware.png | Bin 0 -> 180 bytes comp/src/gb.form.stock/gambas/32/help.png | Bin 0 -> 661 bytes comp/src/gb.form.stock/gambas/32/home.png | Bin 0 -> 197 bytes comp/src/gb.form.stock/gambas/32/html.png | Bin 0 -> 552 bytes comp/src/gb.form.stock/gambas/32/identity.png | Bin 0 -> 221 bytes comp/src/gb.form.stock/gambas/32/image.png | Bin 0 -> 237 bytes .../src/gb.form.stock/gambas/32/important.png | Bin 0 -> 541 bytes comp/src/gb.form.stock/gambas/32/indent.png | Bin 0 -> 188 bytes comp/src/gb.form.stock/gambas/32/info.png | Bin 0 -> 189 bytes .../gb.form.stock/gambas/32/insert-image.png | Bin 0 -> 266 bytes .../gb.form.stock/gambas/32/insert-link.png | Bin 0 -> 248 bytes .../gb.form.stock/gambas/32/insert-text.png | Bin 0 -> 208 bytes comp/src/gb.form.stock/gambas/32/internet.png | Bin 0 -> 838 bytes comp/src/gb.form.stock/gambas/32/js.png | Bin 0 -> 547 bytes comp/src/gb.form.stock/gambas/32/jump.png | Bin 0 -> 246 bytes comp/src/gb.form.stock/gambas/32/keyboard.png | Bin 0 -> 171 bytes comp/src/gb.form.stock/gambas/32/lamp.png | Bin 0 -> 366 bytes comp/src/gb.form.stock/gambas/32/language.png | Bin 0 -> 168 bytes comp/src/gb.form.stock/gambas/32/last.png | Bin 0 -> 228 bytes comp/src/gb.form.stock/gambas/32/left.png | Bin 0 -> 216 bytes comp/src/gb.form.stock/gambas/32/link.png | Bin 0 -> 200 bytes comp/src/gb.form.stock/gambas/32/linux.png | Bin 0 -> 1550 bytes comp/src/gb.form.stock/gambas/32/lock.png | Bin 0 -> 262 bytes comp/src/gb.form.stock/gambas/32/lower.png | Bin 0 -> 168 bytes comp/src/gb.form.stock/gambas/32/mail.png | Bin 0 -> 171 bytes comp/src/gb.form.stock/gambas/32/make-all.png | Bin 0 -> 196 bytes comp/src/gb.form.stock/gambas/32/make.png | Bin 0 -> 194 bytes .../gb.form.stock/gambas/32/media-player.png | Bin 0 -> 179 bytes comp/src/gb.form.stock/gambas/32/menu.png | Bin 0 -> 139 bytes .../gb.form.stock/gambas/32/microphone.png | Bin 0 -> 526 bytes comp/src/gb.form.stock/gambas/32/monitor.png | Bin 0 -> 212 bytes comp/src/gb.form.stock/gambas/32/mouse.png | Bin 0 -> 226 bytes .../gb.form.stock/gambas/32/multimedia.png | Bin 0 -> 297 bytes comp/src/gb.form.stock/gambas/32/muted.png | Bin 0 -> 297 bytes comp/src/gb.form.stock/gambas/32/network.png | Bin 0 -> 173 bytes comp/src/gb.form.stock/gambas/32/new-dir.png | Bin 0 -> 177 bytes .../gb.form.stock/gambas/32/new-window.png | Bin 0 -> 189 bytes comp/src/gb.form.stock/gambas/32/new.png | Bin 0 -> 173 bytes comp/src/gb.form.stock/gambas/32/next.png | Bin 0 -> 194 bytes .../gb.form.stock/gambas/32/office-calc.png | Bin 0 -> 157 bytes .../gb.form.stock/gambas/32/office-draw.png | Bin 0 -> 226 bytes comp/src/gb.form.stock/gambas/32/office.png | Bin 0 -> 163 bytes comp/src/gb.form.stock/gambas/32/ok.png | Bin 0 -> 240 bytes .../gb.form.stock/gambas/32/open-recent.png | Bin 0 -> 246 bytes comp/src/gb.form.stock/gambas/32/open.png | Bin 0 -> 206 bytes comp/src/gb.form.stock/gambas/32/options.png | Bin 0 -> 283 bytes comp/src/gb.form.stock/gambas/32/package.png | Bin 0 -> 200 bytes comp/src/gb.form.stock/gambas/32/paste.png | Bin 0 -> 188 bytes comp/src/gb.form.stock/gambas/32/pause.png | Bin 0 -> 137 bytes comp/src/gb.form.stock/gambas/32/pda.png | Bin 0 -> 157 bytes comp/src/gb.form.stock/gambas/32/pdf.png | Bin 0 -> 505 bytes comp/src/gb.form.stock/gambas/32/pen.png | Bin 0 -> 209 bytes comp/src/gb.form.stock/gambas/32/people.png | Bin 0 -> 830 bytes comp/src/gb.form.stock/gambas/32/phone.png | Bin 0 -> 157 bytes comp/src/gb.form.stock/gambas/32/play.png | Bin 0 -> 183 bytes comp/src/gb.form.stock/gambas/32/plugin.png | Bin 0 -> 286 bytes comp/src/gb.form.stock/gambas/32/preview.png | Bin 0 -> 399 bytes comp/src/gb.form.stock/gambas/32/previous.png | Bin 0 -> 201 bytes comp/src/gb.form.stock/gambas/32/print.png | Bin 0 -> 167 bytes comp/src/gb.form.stock/gambas/32/printer.png | Bin 0 -> 171 bytes comp/src/gb.form.stock/gambas/32/program.png | Bin 0 -> 190 bytes .../gb.form.stock/gambas/32/properties.png | Bin 0 -> 160 bytes comp/src/gb.form.stock/gambas/32/question.png | Bin 0 -> 715 bytes comp/src/gb.form.stock/gambas/32/quit.png | Bin 0 -> 491 bytes comp/src/gb.form.stock/gambas/32/raise.png | Bin 0 -> 168 bytes comp/src/gb.form.stock/gambas/32/recent.png | Bin 0 -> 287 bytes comp/src/gb.form.stock/gambas/32/record.png | Bin 0 -> 321 bytes comp/src/gb.form.stock/gambas/32/redo.png | Bin 0 -> 367 bytes comp/src/gb.form.stock/gambas/32/refresh.png | Bin 0 -> 467 bytes comp/src/gb.form.stock/gambas/32/remove.png | Bin 0 -> 137 bytes comp/src/gb.form.stock/gambas/32/replace.png | Bin 0 -> 416 bytes comp/src/gb.form.stock/gambas/32/revert.png | Bin 0 -> 251 bytes comp/src/gb.form.stock/gambas/32/rewind.png | Bin 0 -> 203 bytes comp/src/gb.form.stock/gambas/32/right.png | Bin 0 -> 209 bytes .../gb.form.stock/gambas/32/rotate-left.png | Bin 0 -> 187 bytes .../gb.form.stock/gambas/32/rotate-right.png | Bin 0 -> 187 bytes comp/src/gb.form.stock/gambas/32/save-as.png | Bin 0 -> 205 bytes comp/src/gb.form.stock/gambas/32/save.png | Bin 0 -> 197 bytes comp/src/gb.form.stock/gambas/32/scanner.png | Bin 0 -> 181 bytes comp/src/gb.form.stock/gambas/32/science.png | Bin 0 -> 277 bytes comp/src/gb.form.stock/gambas/32/screen.png | Bin 0 -> 168 bytes comp/src/gb.form.stock/gambas/32/script.png | Bin 0 -> 429 bytes comp/src/gb.form.stock/gambas/32/security.png | Bin 0 -> 723 bytes .../gb.form.stock/gambas/32/select-all.png | Bin 0 -> 197 bytes comp/src/gb.form.stock/gambas/32/select.png | Bin 0 -> 181 bytes comp/src/gb.form.stock/gambas/32/server.png | Bin 0 -> 158 bytes comp/src/gb.form.stock/gambas/32/shortcut.png | Bin 0 -> 482 bytes .../gb.form.stock/gambas/32/sort-ascent.png | Bin 0 -> 187 bytes .../gb.form.stock/gambas/32/sort-descent.png | Bin 0 -> 192 bytes .../gb.form.stock/gambas/32/spell-check.png | Bin 0 -> 811 bytes comp/src/gb.form.stock/gambas/32/star.png | Bin 0 -> 600 bytes comp/src/gb.form.stock/gambas/32/start.png | Bin 0 -> 191 bytes comp/src/gb.form.stock/gambas/32/stop.png | Bin 0 -> 137 bytes comp/src/gb.form.stock/gambas/32/sun.png | Bin 0 -> 339 bytes comp/src/gb.form.stock/gambas/32/system.png | Bin 0 -> 161 bytes comp/src/gb.form.stock/gambas/32/table.png | Bin 0 -> 161 bytes comp/src/gb.form.stock/gambas/32/tablet.png | Bin 0 -> 190 bytes comp/src/gb.form.stock/gambas/32/terminal.png | Bin 0 -> 523 bytes .../gb.form.stock/gambas/32/text-baseline.png | Bin 0 -> 193 bytes .../src/gb.form.stock/gambas/32/text-bold.png | Bin 0 -> 568 bytes .../gb.form.stock/gambas/32/text-bottom.png | Bin 0 -> 178 bytes .../gb.form.stock/gambas/32/text-center.png | Bin 0 -> 157 bytes .../src/gb.form.stock/gambas/32/text-fill.png | Bin 0 -> 139 bytes .../gb.form.stock/gambas/32/text-italic.png | Bin 0 -> 686 bytes .../src/gb.form.stock/gambas/32/text-left.png | Bin 0 -> 153 bytes .../gb.form.stock/gambas/32/text-middle.png | Bin 0 -> 195 bytes .../gb.form.stock/gambas/32/text-right.png | Bin 0 -> 154 bytes .../gb.form.stock/gambas/32/text-strike.png | Bin 0 -> 590 bytes comp/src/gb.form.stock/gambas/32/text-top.png | Bin 0 -> 176 bytes .../gambas/32/text-underline.png | Bin 0 -> 672 bytes comp/src/gb.form.stock/gambas/32/text.png | Bin 0 -> 160 bytes comp/src/gb.form.stock/gambas/32/tools.png | Bin 0 -> 488 bytes comp/src/gb.form.stock/gambas/32/top.png | Bin 0 -> 187 bytes comp/src/gb.form.stock/gambas/32/trash.png | Bin 0 -> 164 bytes comp/src/gb.form.stock/gambas/32/undo.png | Bin 0 -> 361 bytes comp/src/gb.form.stock/gambas/32/unindent.png | Bin 0 -> 188 bytes comp/src/gb.form.stock/gambas/32/unlock.png | Bin 0 -> 285 bytes comp/src/gb.form.stock/gambas/32/up.png | Bin 0 -> 180 bytes comp/src/gb.form.stock/gambas/32/upload.png | Bin 0 -> 196 bytes comp/src/gb.form.stock/gambas/32/user.png | Bin 0 -> 481 bytes comp/src/gb.form.stock/gambas/32/vector.png | Bin 0 -> 249 bytes comp/src/gb.form.stock/gambas/32/video.png | Bin 0 -> 209 bytes .../gb.form.stock/gambas/32/view-detail.png | Bin 0 -> 152 bytes .../src/gb.form.stock/gambas/32/view-icon.png | Bin 0 -> 137 bytes .../gb.form.stock/gambas/32/view-normal.png | Bin 0 -> 313 bytes .../gb.form.stock/gambas/32/view-split-h.png | Bin 0 -> 151 bytes .../gb.form.stock/gambas/32/view-split-v.png | Bin 0 -> 147 bytes comp/src/gb.form.stock/gambas/32/volume.png | Bin 0 -> 475 bytes comp/src/gb.form.stock/gambas/32/vpn.png | Bin 0 -> 333 bytes comp/src/gb.form.stock/gambas/32/warning.png | Bin 0 -> 336 bytes comp/src/gb.form.stock/gambas/32/watch.png | Bin 0 -> 496 bytes comp/src/gb.form.stock/gambas/32/webcam.png | Bin 0 -> 539 bytes comp/src/gb.form.stock/gambas/32/wifi.png | Bin 0 -> 613 bytes comp/src/gb.form.stock/gambas/32/wizard.png | Bin 0 -> 286 bytes comp/src/gb.form.stock/gambas/32/zoom-fit.png | Bin 0 -> 554 bytes comp/src/gb.form.stock/gambas/32/zoom-in.png | Bin 0 -> 453 bytes .../gb.form.stock/gambas/32/zoom-normal.png | Bin 0 -> 503 bytes comp/src/gb.form.stock/gambas/32/zoom-out.png | Bin 0 -> 450 bytes comp/src/gb.form.stock/links | 44 + comp/src/gb.form.terminal/.component | 7 + comp/src/gb.form.terminal/.directory | 2 + .../.hidden/Konsole keys README.txt | 73 + .../gb.form.terminal/.hidden/Konsole keys.txt | 175 + comp/src/gb.form.terminal/.hidden/XtermVT100 | 457 + .../.hidden/control/terminalview.png | Bin 0 -> 149 bytes comp/src/gb.form.terminal/.icon.png | Bin 0 -> 5675 bytes comp/src/gb.form.terminal/.project | 17 + .../.src/TerminalView/CTerminalLine.class | 144 + .../.src/TerminalView/FOtherTest.class | 11 + .../.src/TerminalView/FOtherTest.form | 8 + .../.src/TerminalView/FTestTerminalView.class | 127 + .../.src/TerminalView/FTestTerminalView.form | 50 + .../.src/TerminalView/Form1.class | 44 + .../.src/TerminalView/Form1.form | 9 + .../.src/TerminalView/MTest.module | 8 + .../.src/TerminalView/PipeTest.class | 224 + .../.src/TerminalView/PipeTest.form | 75 + .../.src/TerminalView/PipedTask.class | 73 + .../.src/TerminalView/TelNetProtocol.class | 139 + .../.src/TerminalView/TerminalAttr.class | 57 + .../.src/TerminalView/TerminalFilter.class | 25 + .../TerminalView/TerminalFilter_VT100.class | 867 + .../.src/TerminalView/TerminalScreen.class | 2240 + .../.src/TerminalView/TerminalView.class | 1327 + .../gb.form.terminal/.src/VT100/Attr.class | 220 + .../gb.form.terminal/.src/VT100/Console.class | 63 + .../gb.form.terminal/.src/VT100/Main.module | 38 + comp/src/gb.form.terminal/Text1 | 5 + .../gb.form.terminal/XtermTests/16colors.sh | 119 + .../gb.form.terminal/XtermTests/256colors.pl | 51 + .../gb.form.terminal/XtermTests/256colors2.pl | 180 + .../gb.form.terminal/XtermTests/88colors.pl | 49 + .../gb.form.terminal/XtermTests/88colors2.pl | 182 + .../gb.form.terminal/XtermTests/8colors.sh | 107 + .../gb.form.terminal/XtermTests/acolors.sh | 93 + .../XtermTests/doublechars.sh | 116 + .../gb.form.terminal/XtermTests/dynamic.pl | 132 + .../gb.form.terminal/XtermTests/dynamic.sh | 89 + .../gb.form.terminal/XtermTests/dynamic2.sh | 114 + comp/src/gb.form.terminal/XtermTests/fonts.sh | 88 + .../gb.form.terminal/XtermTests/paste64.pl | 190 + .../XtermTests/query-color.pl | 169 + .../XtermTests/query-fonts.pl | 167 + .../src/gb.form.terminal/XtermTests/resize.pl | 141 + .../src/gb.form.terminal/XtermTests/resize.sh | 111 + .../gb.form.terminal/XtermTests/tcapquery.pl | 325 + comp/src/gb.form.terminal/XtermTests/title.sh | 84 + comp/src/gb.form.terminal/brush_dark.png | Bin 0 -> 85 bytes comp/src/gb.form.terminal/brush_light.png | Bin 0 -> 85 bytes comp/src/gb.form.terminal/brush_medium.png | Bin 0 -> 85 bytes comp/src/gb.form/.component | 5 + comp/src/gb.form/.directory | 2 + comp/src/gb.form/.hidden/CHANGELOG | 7 + .../src/gb.form/.hidden/control/buttonbox.png | Bin 0 -> 162 bytes .../gb.form/.hidden/control/colorbutton.png | Bin 0 -> 504 bytes .../gb.form/.hidden/control/colorchooser.png | Bin 0 -> 745 bytes .../gb.form/.hidden/control/colorpalette.png | Bin 0 -> 221 bytes comp/src/gb.form/.hidden/control/datebox.png | Bin 0 -> 261 bytes .../gb.form/.hidden/control/datechooser.png | Bin 0 -> 547 bytes comp/src/gb.form/.hidden/control/dirbox.png | Bin 0 -> 154 bytes .../gb.form/.hidden/control/dirchooser.png | Bin 0 -> 164 bytes comp/src/gb.form/.hidden/control/dirview.png | Bin 0 -> 154 bytes .../gb.form/.hidden/control/documentview.png | Bin 0 -> 620 bytes comp/src/gb.form/.hidden/control/expander.png | Bin 0 -> 400 bytes .../gb.form/.hidden/control/filechooser.png | Bin 0 -> 155 bytes .../.hidden/control/fileproperties.png | Bin 0 -> 216 bytes comp/src/gb.form/.hidden/control/fileview.png | Bin 0 -> 194 bytes comp/src/gb.form/.hidden/control/fontbox.png | Bin 0 -> 267 bytes .../gb.form/.hidden/control/fontchooser.png | Bin 0 -> 372 bytes .../src/gb.form/.hidden/control/iconpanel.png | Bin 0 -> 139 bytes .../src/gb.form/.hidden/control/imageview.png | Bin 0 -> 363 bytes comp/src/gb.form/.hidden/control/lcdlabel.png | Bin 0 -> 492 bytes .../gb.form/.hidden/control/listcontainer.png | Bin 0 -> 356 bytes .../gb.form/.hidden/control/listeditor.png | Bin 0 -> 329 bytes comp/src/gb.form/.hidden/control/maskbox.png | Bin 0 -> 338 bytes .../gb.form/.hidden/control/menubutton.png | Bin 0 -> 600 bytes .../gb.form/.hidden/control/messageview.png | Bin 0 -> 138 bytes .../src/gb.form/.hidden/control/sidepanel.png | Bin 0 -> 927 bytes .../src/gb.form/.hidden/control/sliderbox.png | Bin 0 -> 292 bytes comp/src/gb.form/.hidden/control/spinbar.png | Bin 0 -> 201 bytes comp/src/gb.form/.hidden/control/spinner.png | Bin 0 -> 518 bytes .../gb.form/.hidden/control/switchbutton.png | Bin 0 -> 365 bytes .../src/gb.form/.hidden/control/tableview.png | Bin 0 -> 249 bytes comp/src/gb.form/.hidden/control/tabpanel.png | Bin 0 -> 128 bytes .../src/gb.form/.hidden/control/toolpanel.png | Bin 0 -> 139 bytes comp/src/gb.form/.hidden/control/urllabel.png | Bin 0 -> 382 bytes comp/src/gb.form/.hidden/control/valuebox.png | Bin 0 -> 328 bytes comp/src/gb.form/.hidden/control/wizard.png | Bin 0 -> 198 bytes comp/src/gb.form/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.form/.lang/ar.mo | Bin 0 -> 1155 bytes comp/src/gb.form/.lang/ar.po | 733 + comp/src/gb.form/.lang/ca.mo | Bin 0 -> 3251 bytes comp/src/gb.form/.lang/ca.po | 754 + comp/src/gb.form/.lang/cs.mo | Bin 0 -> 4222 bytes comp/src/gb.form/.lang/cs.po | 728 + comp/src/gb.form/.lang/de.mo | Bin 0 -> 6059 bytes comp/src/gb.form/.lang/de.po | 728 + comp/src/gb.form/.lang/es.mo | Bin 0 -> 7146 bytes comp/src/gb.form/.lang/es.po | 727 + comp/src/gb.form/.lang/es_ES.mo | Bin 0 -> 7149 bytes comp/src/gb.form/.lang/es_ES.po | 727 + comp/src/gb.form/.lang/fa.mo | Bin 0 -> 1017 bytes comp/src/gb.form/.lang/fa.po | 725 + comp/src/gb.form/.lang/fr.mo | Bin 0 -> 6262 bytes comp/src/gb.form/.lang/fr.po | 488 + comp/src/gb.form/.lang/it.mo | Bin 0 -> 1533 bytes comp/src/gb.form/.lang/it.po | 743 + comp/src/gb.form/.lang/ja.mo | Bin 0 -> 1634 bytes comp/src/gb.form/.lang/ja.po | 743 + comp/src/gb.form/.lang/nl.mo | Bin 0 -> 7708 bytes comp/src/gb.form/.lang/nl.po | 707 + comp/src/gb.form/.lang/pt_BR.mo | Bin 0 -> 2348 bytes comp/src/gb.form/.lang/pt_BR.po | 762 + comp/src/gb.form/.lang/sv.mo | Bin 0 -> 1891 bytes comp/src/gb.form/.lang/sv.po | 772 + comp/src/gb.form/.lang/zh.mo | Bin 0 -> 6161 bytes comp/src/gb.form/.lang/zh.po | 734 + comp/src/gb.form/.lang/zh_TW.mo | Bin 0 -> 1053 bytes comp/src/gb.form/.lang/zh_TW.po | 725 + comp/src/gb.form/.project | 28 + comp/src/gb.form/.src/Balloon/Balloon.class | 89 + comp/src/gb.form/.src/Balloon/FBalloon.class | 421 + comp/src/gb.form/.src/Balloon/FBalloon.form | 26 + comp/src/gb.form/.src/Button/ButtonBox.class | 378 + comp/src/gb.form/.src/Button/MenuButton.class | 497 + .../gb.form/.src/Button/SwitchButton.class | 338 + comp/src/gb.form/.src/Color/ColorButton.class | 148 + .../src/gb.form/.src/Color/ColorChooser.class | 113 + .../src/gb.form/.src/Color/ColorPalette.class | 391 + .../gb.form/.src/Color/FColorChooser.class | 609 + .../src/gb.form/.src/Color/FColorChooser.form | 178 + comp/src/gb.form/.src/Completion.class | 342 + comp/src/gb.form/.src/Date/DateBox.class | 286 + comp/src/gb.form/.src/Date/DateChooser.class | 265 + comp/src/gb.form/.src/Date/FCalendar.class | 917 + comp/src/gb.form/.src/Date/FCalendar.form | 115 + .../.src/Date/_DateChooser_Colors.class | 26 + .../gb.form/.src/Date/_DateChooser_Data.class | 27 + .../gb.form/.src/Date/_DateChooser_Date.class | 68 + .../.src/DocumentView/DocumentView.class | 996 + .../.src/DocumentView/_DocumentItem.class | 40 + .../.src/DocumentView/_DocumentLayout.class | 8 + comp/src/gb.form/.src/Expander/Expander.class | 100 + .../src/gb.form/.src/Expander/FExpander.class | 221 + comp/src/gb.form/.src/Expander/FExpander.form | 31 + comp/src/gb.form/.src/FInputBox.class | 56 + comp/src/gb.form/.src/FInputBox.form | 39 + .../.src/File/Bookmark/CBookmark.class | 54 + .../.src/File/Bookmark/CBookmarkList.class | 181 + .../.src/File/Bookmark/FEditBookmark.class | 140 + .../.src/File/Bookmark/FEditBookmark.form | 50 + comp/src/gb.form/.src/File/CTaskPreview.class | 98 + .../gb.form/.src/File/Chooser/DirButton.class | 134 + .../.src/File/Chooser/DirChooser.class | 259 + .../.src/File/Chooser/FDirChooser.class | 1648 + .../.src/File/Chooser/FDirChooser.form | 175 + .../.src/File/Chooser/FileChooser.class | 401 + comp/src/gb.form/.src/File/DirBox.class | 65 + comp/src/gb.form/.src/File/DirCache.class | 159 + comp/src/gb.form/.src/File/DirView.class | 721 + comp/src/gb.form/.src/File/FileView.class | 1137 + .../.src/File/Properties/CTaskDirSize.class | 71 + .../File/Properties/FFileProperties.class | 448 + .../.src/File/Properties/FFileProperties.form | 198 + .../.src/File/Properties/FileProperties.class | 83 + .../File/Properties/_FilePropertiesData.class | 6 + comp/src/gb.form/.src/FileCompletion.class | 42 + comp/src/gb.form/.src/Font/FFontChooser.class | 543 + comp/src/gb.form/.src/Font/FFontChooser.form | 89 + comp/src/gb.form/.src/Font/FontBox.class | 114 + comp/src/gb.form/.src/Font/FontChooser.class | 153 + comp/src/gb.form/.src/Help.module | 74 + .../gb.form/.src/IconPanel/IconPanel.class | 347 + .../.src/IconPanel/_IconPanelContainer.class | 78 + .../gb.form/.src/ImageView/ImageView.class | 415 + comp/src/gb.form/.src/InputBox.class | 9 + comp/src/gb.form/.src/LCDLabel.class | 455 + comp/src/gb.form/.src/ListContainer.class | 413 + .../gb.form/.src/ListEditor/FListEditor.class | 234 + .../gb.form/.src/ListEditor/FListEditor.form | 48 + .../gb.form/.src/ListEditor/ListEditor.class | 93 + comp/src/gb.form/.src/Main.module | 233 + comp/src/gb.form/.src/MaskBox.class | 748 + comp/src/gb.form/.src/Message/FMessage.class | 87 + comp/src/gb.form/.src/Message/FMessage.form | 43 + comp/src/gb.form/.src/Message/Message.module | 9 + .../gb.form/.src/Message/MessageView.class | 235 + comp/src/gb.form/.src/MessageLabel.class | 151 + .../.src/MultiContainer/_MultiContainer.class | 237 + .../MultiContainer/_MultiContainerTab.class | 67 + .../gb.form/.src/SidePanel/FSidePanel.class | 1179 + .../gb.form/.src/SidePanel/FSidePanel.form | 25 + .../gb.form/.src/SidePanel/SidePanel.class | 228 + comp/src/gb.form/.src/SliderBox.class | 157 + comp/src/gb.form/.src/SpinBar/SpinBar.class | 433 + comp/src/gb.form/.src/Spinner.class | 181 + comp/src/gb.form/.src/Stock.class | 834 + comp/src/gb.form/.src/TabPanel/TabPanel.class | 975 + .../.src/TabPanel/_TabPanelButton.class | 567 + .../.src/TabPanel/_TabPanelContainer.class | 108 + comp/src/gb.form/.src/TableView.class | 558 + .../src/gb.form/.src/TagBox/TagComboBox.class | 64 + .../.src/TagBox/_TagComboBox_Item.class | 13 + comp/src/gb.form/.src/Test/FBugFileView.class | 22 + comp/src/gb.form/.src/Test/FBugFileView.form | 27 + .../src/gb.form/.src/Test/FDocumentView.class | 137 + comp/src/gb.form/.src/Test/FDocumentView.form | 84 + comp/src/gb.form/.src/Test/FFont.class | 2 + comp/src/gb.form/.src/Test/FFont.form | 10 + comp/src/gb.form/.src/Test/FIconPanel.class | 3 + comp/src/gb.form/.src/Test/FIconPanel.form | 29 + comp/src/gb.form/.src/Test/FIconView.class | 30 + comp/src/gb.form/.src/Test/FIconView.form | 20 + comp/src/gb.form/.src/Test/FLCDLabel.class | 17 + comp/src/gb.form/.src/Test/FLCDLabel.form | 19 + comp/src/gb.form/.src/Test/FMain.class | 20 + comp/src/gb.form/.src/Test/FMain.form | 67 + comp/src/gb.form/.src/Test/FSpinBar.class | 1 + comp/src/gb.form/.src/Test/FSpinBar.form | 27 + comp/src/gb.form/.src/Test/FSpinner.class | 15 + comp/src/gb.form/.src/Test/FSpinner.form | 17 + .../src/gb.form/.src/Test/FSwitchButton.class | 13 + comp/src/gb.form/.src/Test/FSwitchButton.form | 44 + comp/src/gb.form/.src/Test/FTabPanel.class | 78 + comp/src/gb.form/.src/Test/FTabPanel.form | 48 + comp/src/gb.form/.src/Test/FTableView.class | 51 + comp/src/gb.form/.src/Test/FTableView.form | 17 + .../gb.form/.src/Test/FTestArrangement.class | 14 + .../gb.form/.src/Test/FTestArrangement.form | 18 + comp/src/gb.form/.src/Test/FTestBalloon.class | 19 + comp/src/gb.form/.src/Test/FTestBalloon.form | 14 + .../gb.form/.src/Test/FTestColorChooser.class | 26 + .../gb.form/.src/Test/FTestColorChooser.form | 16 + .../gb.form/.src/Test/FTestCompletion.class | 24 + .../gb.form/.src/Test/FTestCompletion.form | 21 + .../gb.form/.src/Test/FTestDateChooser.class | 26 + .../gb.form/.src/Test/FTestDateChooser.form | 39 + .../src/gb.form/.src/Test/FTestFileView.class | 52 + comp/src/gb.form/.src/Test/FTestFileView.form | 77 + .../gb.form/.src/Test/FTestImageView.class | 52 + .../src/gb.form/.src/Test/FTestImageView.form | 5 + .../gb.form/.src/Test/FTestListEditor.class | 8 + .../gb.form/.src/Test/FTestListEditor.form | 8 + comp/src/gb.form/.src/Test/FTestMaskBox.class | 14 + comp/src/gb.form/.src/Test/FTestMaskBox.form | 10 + .../gb.form/.src/Test/FTestMenuButton.class | 2 + .../gb.form/.src/Test/FTestMenuButton.form | 78 + .../gb.form/.src/Test/FTestMessageView.class | 8 + .../gb.form/.src/Test/FTestMessageView.form | 25 + .../gb.form/.src/Test/FTestSidePanel.class | 21 + .../src/gb.form/.src/Test/FTestSidePanel.form | 40 + .../gb.form/.src/Test/FTestToolPanel.class | 2 + .../src/gb.form/.src/Test/FTestToolPanel.form | 16 + .../src/gb.form/.src/Test/FTestValueBox.class | 8 + comp/src/gb.form/.src/Test/FTestValueBox.form | 22 + comp/src/gb.form/.src/Test/FTestWizard.class | 8 + comp/src/gb.form/.src/Test/FTestWizard.form | 30 + comp/src/gb.form/.src/Test/FWiki.class | 10 + comp/src/gb.form/.src/Test/FWiki.form | 27 + comp/src/gb.form/.src/Test/Form2.class | 87 + comp/src/gb.form/.src/Test/Form2.form | 101 + comp/src/gb.form/.src/Test/Form4.class | 10 + comp/src/gb.form/.src/Test/Form4.form | 15 + comp/src/gb.form/.src/TestControl.class | 11 + .../src/gb.form/.src/ToolPanel/FToolBar.class | 73 + comp/src/gb.form/.src/ToolPanel/FToolBar.form | 15 + .../gb.form/.src/ToolPanel/ToolPanel.class | 270 + .../.src/ToolPanel/ToolPanelContainer.class | 97 + comp/src/gb.form/.src/URLLabel.class | 180 + comp/src/gb.form/.src/ValueBox.class | 392 + comp/src/gb.form/.src/Wizard/FWizard.class | 150 + comp/src/gb.form/.src/Wizard/FWizard.form | 51 + comp/src/gb.form/.src/Wizard/Wizard.class | 452 + .../.src/Wizard/_WizardContainer.class | 56 + comp/src/gb.form/img/16/cross.png | Bin 0 -> 115 bytes comp/src/gb.form/img/32/filter-menu.png | Bin 0 -> 506 bytes comp/src/gb.form/img/32/filter.png | Bin 0 -> 474 bytes comp/src/gb.form/img/32/warning.png | Bin 0 -> 1932 bytes .../gb.form/img/8/new/side-bottom-void.png | Bin 0 -> 117 bytes comp/src/gb.form/img/8/new/side-bottom.png | Bin 0 -> 111 bytes comp/src/gb.form/img/8/new/side-left.png | Bin 0 -> 112 bytes .../src/gb.form/img/8/new/side-right-void.png | Bin 0 -> 115 bytes comp/src/gb.form/img/8/new/side-right.png | Bin 0 -> 112 bytes comp/src/gb.form/img/8/new/side-top.png | Bin 0 -> 112 bytes comp/src/gb.form/img/8/side-bottom-void.png | Bin 0 -> 112 bytes comp/src/gb.form/img/8/side-bottom.png | Bin 0 -> 112 bytes comp/src/gb.form/img/8/side-left.png | Bin 0 -> 113 bytes comp/src/gb.form/img/8/side-right-void.png | Bin 0 -> 112 bytes comp/src/gb.form/img/8/side-right.png | Bin 0 -> 109 bytes comp/src/gb.form/img/8/side-top.png | Bin 0 -> 115 bytes comp/src/gb.form/img/colormap.png | Bin 0 -> 1440 bytes comp/src/gb.form/img/cross.png | Bin 0 -> 299 bytes comp/src/gb.form/img/handle-h.png | Bin 0 -> 102 bytes comp/src/gb.form/img/handle-v.png | Bin 0 -> 97 bytes comp/src/gb.form/img/round.png | Bin 0 -> 124 bytes comp/src/gb.form/img/select-dark.png | Bin 0 -> 112 bytes comp/src/gb.form/img/select.png | Bin 0 -> 114 bytes comp/src/gb.form/img/unknown.svg | 72 + comp/src/gb.form/img/valuemap.png | Bin 0 -> 897 bytes comp/src/gb.form/map/icon.map | 207 + comp/src/gb.form/stock/16/gambas.png | Bin 0 -> 489 bytes comp/src/gb.form/stock/16/gnu.png | Bin 0 -> 360 bytes comp/src/gb.form/stock/16/linux.png | Bin 0 -> 773 bytes comp/src/gb.form/stock/32/gambas.png | Bin 0 -> 925 bytes comp/src/gb.form/stock/32/gnu.png | Bin 0 -> 1022 bytes comp/src/gb.form/stock/32/linux.png | Bin 0 -> 1767 bytes comp/src/gb.form/stock/scalable/gambas.svg | 91 + comp/src/gb.form/stock/scalable/gnu.svg | 94 + comp/src/gb.form/stock/scalable/linux.svg | 165 + comp/src/gb.gui.base/.component | 5 + comp/src/gb.gui.base/.directory | 2 + comp/src/gb.gui.base/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.gui.base/.project | 15 + comp/src/gb.gui.base/.src/Action.class | 554 + comp/src/gb.gui.base/.src/Border.class | 861 + comp/src/gb.gui.base/.src/Draw.module | 674 + comp/src/gb.gui.base/.src/Fill.class | 5 + .../gb.gui.base/.src/GridView/GridView.class | 2400 + .../.src/GridView/GridViewSelection.class | 193 + .../.src/GridView/_GridView_Cell.class | 244 + .../.src/GridView/_GridView_Column.class | 149 + .../.src/GridView/_GridView_Columns.class | 260 + .../.src/GridView/_GridView_Data.class | 37 + .../.src/GridView/_GridView_Row.class | 96 + .../.src/GridView/_GridView_Rows.class | 527 + .../gb.gui.base/.src/IconView/IconView.class | 1651 + .../.src/IconView/_IconView_Item.class | 766 + comp/src/gb.gui.base/.src/Line.class | 5 + .../gb.gui.base/.src/ListBox/ListBox.class | 438 + .../.src/ListBox/_ListBox_Item.class | 47 + comp/src/gb.gui.base/.src/Main.module | 65 + comp/src/gb.gui.base/.src/Message.class | 34 + comp/src/gb.gui.base/.src/MyComboBox.class | 43 + comp/src/gb.gui.base/.src/Paint.class | 376 + comp/src/gb.gui.base/.src/Picture.class | 133 + comp/src/gb.gui.base/.src/PictureBox.class | 311 + comp/src/gb.gui.base/.src/ProgressBar.class | 188 + comp/src/gb.gui.base/.src/ScrollArea.class | 1069 + comp/src/gb.gui.base/.src/ScrollView.class | 127 + comp/src/gb.gui.base/.src/Shortcut.class | 45 + comp/src/gb.gui.base/.src/SpinBox.class | 478 + comp/src/gb.gui.base/.src/Split/HSplit.class | 15 + comp/src/gb.gui.base/.src/Split/VSplit.class | 15 + comp/src/gb.gui.base/.src/Split/_Split.class | 448 + comp/src/gb.gui.base/.src/Spring.class | 15 + comp/src/gb.gui.base/.src/Test/FBorder.class | 28 + comp/src/gb.gui.base/.src/Test/FBorder.form | 16 + .../src/gb.gui.base/.src/Test/FGridView.class | 179 + comp/src/gb.gui.base/.src/Test/FGridView.form | 53 + .../src/gb.gui.base/.src/Test/FIconView.class | 56 + comp/src/gb.gui.base/.src/Test/FIconView.form | 29 + comp/src/gb.gui.base/.src/Test/FListBox.class | 21 + comp/src/gb.gui.base/.src/Test/FListBox.form | 21 + comp/src/gb.gui.base/.src/Test/FMain.class | 35 + comp/src/gb.gui.base/.src/Test/FMain.form | 21 + comp/src/gb.gui.base/.src/Test/FPaint.class | 29 + comp/src/gb.gui.base/.src/Test/FPaint.form | 10 + .../gb.gui.base/.src/Test/FProgressBar.class | 21 + .../gb.gui.base/.src/Test/FProgressBar.form | 23 + .../gb.gui.base/.src/Test/FScrollArea.class | 45 + .../gb.gui.base/.src/Test/FScrollArea.form | 219 + .../gb.gui.base/.src/Test/FTestClipping.class | 36 + .../gb.gui.base/.src/Test/FTestClipping.form | 10 + .../gb.gui.base/.src/Test/FTestCombo.class | 36 + .../src/gb.gui.base/.src/Test/FTestCombo.form | 81 + .../.src/Test/FTestMouseWheel.class | 14 + .../.src/Test/FTestMouseWheel.form | 25 + .../src/gb.gui.base/.src/Test/FTreeView.class | 141 + comp/src/gb.gui.base/.src/Test/FTreeView.form | 66 + .../.src/Test/FileView/CTaskPreview.class | 98 + .../.src/Test/FileView/DirCache.class | 159 + .../.src/Test/FileView/FTestFileView.class | 14 + .../.src/Test/FileView/FTestFileView.form | 17 + .../.src/Test/FileView/FileView.class | 1126 + .../.src/Test/FileView/Help.module | 74 + .../.src/TreeView/ColumnView.class | 219 + .../gb.gui.base/.src/TreeView/ListView.class | 17 + .../gb.gui.base/.src/TreeView/RenameBox.class | 84 + .../gb.gui.base/.src/TreeView/TreeView.class | 32 + .../.src/TreeView/_ColumnView_Columns.class | 66 + .../gb.gui.base/.src/TreeView/_TreeView.class | 1584 + .../.src/TreeView/_TreeView_Item.class | 580 + comp/src/gb.gui.base/.src/_Draw_Clip.class | 76 + comp/src/gb.gui.base/.src/_Draw_Style.class | 55 + comp/src/gb.gui.base/.src/_Gui.class | 82 + comp/src/gb.gui.base/pattern/10.png | Bin 0 -> 69 bytes comp/src/gb.gui.base/pattern/11.png | Bin 0 -> 70 bytes comp/src/gb.gui.base/pattern/12.png | Bin 0 -> 82 bytes comp/src/gb.gui.base/pattern/13.png | Bin 0 -> 82 bytes comp/src/gb.gui.base/pattern/14.png | Bin 0 -> 78 bytes comp/src/gb.gui.base/pattern/2.png | Bin 0 -> 71 bytes comp/src/gb.gui.base/pattern/3.png | Bin 0 -> 74 bytes comp/src/gb.gui.base/pattern/4.png | Bin 0 -> 74 bytes comp/src/gb.gui.base/pattern/5.png | Bin 0 -> 71 bytes comp/src/gb.gui.base/pattern/6.png | Bin 0 -> 76 bytes comp/src/gb.gui.base/pattern/7.png | Bin 0 -> 76 bytes comp/src/gb.gui.base/pattern/8.png | Bin 0 -> 73 bytes comp/src/gb.gui.base/pattern/9.png | Bin 0 -> 70 bytes comp/src/gb.gui.base/picturebox.png | 1 + comp/src/gb.logging/.component | 4 + comp/src/gb.logging/.directory | 2 + comp/src/gb.logging/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.logging/.project | 11 + comp/src/gb.logging/.src/ComplexLogger.class | 118 + comp/src/gb.logging/.src/ConsoleHandler.class | 50 + comp/src/gb.logging/.src/FileHandler.class | 91 + comp/src/gb.logging/.src/Formatter.module | 167 + comp/src/gb.logging/.src/LogHandler.class | 64 + comp/src/gb.logging/.src/LogLevel.module | 11 + comp/src/gb.logging/.src/LogRotator.module | 65 + comp/src/gb.logging/.src/Logger.class | 108 + comp/src/gb.logging/.src/MTest.module | 36 + comp/src/gb.map/.component | 7 + comp/src/gb.map/.directory | 2 + comp/src/gb.map/.hidden/control/mapview.png | Bin 0 -> 1749 bytes comp/src/gb.map/.icon.png | Bin 0 -> 18119 bytes comp/src/gb.map/.project | 19 + comp/src/gb.map/.src/FCarto.class | 133 + comp/src/gb.map/.src/FCarto.form | 13 + comp/src/gb.map/.src/Map.class | 375 + comp/src/gb.map/.src/MapView.class | 367 + comp/src/gb.map/.src/Shapes/_ShapeItem.class | 81 + comp/src/gb.map/.src/Sprite.class | 119 + comp/src/gb.map/.src/Tests/FMain.class | 193 + comp/src/gb.map/.src/Tests/FMain.form | 35 + comp/src/gb.map/.src/Tests/FTestWmts.class | 24 + comp/src/gb.map/.src/Tests/FTestWmts.form | 8 + comp/src/gb.map/.src/Tests/Form1.class | 23 + comp/src/gb.map/.src/Tests/Form1.form | 21 + comp/src/gb.map/.src/Tests/Form2.class | 96 + comp/src/gb.map/.src/Tests/Form2.form | 17 + comp/src/gb.map/.src/Tests/Form3.class | 42 + comp/src/gb.map/.src/Tests/Form3.form | 8 + comp/src/gb.map/.src/Tests/Form4.class | 23 + comp/src/gb.map/.src/Tests/Form4.form | 10 + comp/src/gb.map/.src/Tests/Form5.class | 28 + comp/src/gb.map/.src/Tests/Form5.form | 8 + comp/src/gb.map/.src/Tests/Form6.class | 40 + comp/src/gb.map/.src/Tests/Form6.form | 10 + comp/src/gb.map/.src/Tests/MMain.module | 24 + comp/src/gb.map/.src/Tools/Geo.module | 135 + comp/src/gb.map/.src/Tools/MyPaint.class | 21 + comp/src/gb.map/.src/Tools/Proj.class | 87 + comp/src/gb.map/.src/Types/MapBounds.class | 143 + comp/src/gb.map/.src/Types/MapPoint.class | 180 + comp/src/gb.map/.src/Types/TileSource.module | 13 + comp/src/gb.map/.src/Types/_Tile.class | 18 + comp/src/gb.map/.src/_MapLayer.class | 78 + comp/src/gb.map/.src/_MapShape.class | 502 + comp/src/gb.map/.src/_MapTile.class | 770 + comp/src/gb.map/.src/_ViewLayer.class | 124 + comp/src/gb.map/Text1 | 10 + comp/src/gb.map/bar.png | Bin 0 -> 96 bytes comp/src/gb.map/cursor.png | Bin 0 -> 349 bytes comp/src/gb.map/minus.png | Bin 0 -> 283 bytes comp/src/gb.map/plus.png | Bin 0 -> 304 bytes comp/src/gb.map/point.png | Bin 0 -> 1010 bytes comp/src/gb.map/pointsparcelle | 0 comp/src/gb.markdown/.component | 4 + comp/src/gb.markdown/.directory | 2 + comp/src/gb.markdown/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.markdown/.project | 10 + comp/src/gb.markdown/.src/MTest.module | 10 + comp/src/gb.markdown/.src/Markdown.class | 66 + comp/src/gb.markdown/.src/MarkdownLink.class | 10 + comp/src/gb.markdown/.src/Markup.module | 1026 + comp/src/gb.markdown/test.txt | 43 + comp/src/gb.media.form/.component | 5 + comp/src/gb.media.form/.directory | 2 + .../.hidden/control/mediaview.png | Bin 0 -> 285 bytes comp/src/gb.media.form/.icon.png | Bin 0 -> 6958 bytes comp/src/gb.media.form/.project | 14 + .../src/gb.media.form/.src/FMediaPlayer.class | 401 + comp/src/gb.media.form/.src/FMediaPlayer.form | 76 + comp/src/gb.media.form/.src/FTest.class | 1 + comp/src/gb.media.form/.src/FTest.form | 11 + comp/src/gb.media.form/.src/MediaView.class | 141 + comp/src/gb.memcached/.component | 6 + comp/src/gb.memcached/.directory | 2 + comp/src/gb.memcached/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.memcached/.project | 14 + comp/src/gb.memcached/.src/FMain.class | 51 + comp/src/gb.memcached/.src/FMain.form | 76 + comp/src/gb.memcached/.src/Main.module | 57 + comp/src/gb.memcached/.src/Memcached.class | 414 + .../gb.memcached/.src/_Memcached_Key.class | 130 + comp/src/gb.mysql/.component | 6 + comp/src/gb.mysql/.directory | 2 + comp/src/gb.mysql/.icon.png | Bin 0 -> 10724 bytes comp/src/gb.mysql/.lang/es.mo | Bin 0 -> 438 bytes comp/src/gb.mysql/.lang/es.po | 34 + comp/src/gb.mysql/.project | 18 + comp/src/gb.mysql/.src/Connection.class | 12 + comp/src/gb.mysql/.src/DB.class | 12 + comp/src/gb.mysql/.src/_DataBase.class | 153 + comp/src/gb.mysql/.src/_DataTypes.class | 241 + comp/src/gb.mysql/.src/_Event.class | 51 + comp/src/gb.mysql/.src/_Field.class | 94 + .../gb.mysql/.src/_FieldEspecifications.class | 62 + comp/src/gb.mysql/.src/_Index.class | 50 + comp/src/gb.mysql/.src/_MySQL.class | 324 + comp/src/gb.mysql/.src/_Result.class | 124 + comp/src/gb.mysql/.src/_Routines.class | 83 + comp/src/gb.mysql/.src/_Table.class | 161 + .../src/gb.mysql/.src/_TableMaintenance.class | 71 + comp/src/gb.mysql/.src/_Trigger.class | 53 + comp/src/gb.mysql/.src/_User.class | 79 + comp/src/gb.mysql/.src/_Version.class | 34 + comp/src/gb.mysql/.src/_View.class | 43 + comp/src/gb.mysql/.src/modMain.module | 67 + comp/src/gb.mysql/logo.png | Bin 0 -> 6577 bytes comp/src/gb.net.pop3/.component | 5 + comp/src/gb.net.pop3/.directory | 2 + .../.hidden/control/pop3client.png | Bin 0 -> 490 bytes comp/src/gb.net.pop3/.icon.png | Bin 0 -> 16882 bytes comp/src/gb.net.pop3/.lang/cs.mo | Bin 0 -> 448 bytes comp/src/gb.net.pop3/.lang/cs.po | 44 + comp/src/gb.net.pop3/.lang/es.mo | Bin 0 -> 424 bytes comp/src/gb.net.pop3/.lang/es.po | 19 + comp/src/gb.net.pop3/.lang/es_ES.mo | Bin 0 -> 427 bytes comp/src/gb.net.pop3/.lang/es_ES.po | 19 + comp/src/gb.net.pop3/.lang/nl.mo | Bin 0 -> 446 bytes comp/src/gb.net.pop3/.lang/nl.po | 20 + comp/src/gb.net.pop3/.lang/zh.mo | Bin 0 -> 438 bytes comp/src/gb.net.pop3/.lang/zh.po | 20 + comp/src/gb.net.pop3/.project | 15 + comp/src/gb.net.pop3/.src/MTest.module | 54 + comp/src/gb.net.pop3/.src/Net.class | 18 + comp/src/gb.net.pop3/.src/POPClient.class | 72 + comp/src/gb.net.pop3/.src/Pop3Client.class | 530 + comp/src/gb.net.pop3/.src/SSLClient.class | 76 + comp/src/gb.net.pop3/.src/TCPClient.class | 102 + .../.src/_Pop3Client_Message.class | 144 + comp/src/gb.net.smtp/.component | 4 + comp/src/gb.net.smtp/.directory | 2 + .../.hidden/control/smtpclient.png | Bin 0 -> 469 bytes comp/src/gb.net.smtp/.icon.png | Bin 0 -> 16837 bytes comp/src/gb.net.smtp/.project | 11 + comp/src/gb.net.smtp/.src/Encode.module | 159 + comp/src/gb.net.smtp/.src/Main.module | 83 + comp/src/gb.net.smtp/.src/Net.class | 7 + comp/src/gb.net.smtp/.src/SmtpClient.class | 580 + comp/src/gb.net.smtp/.src/SmtpPart.class | 126 + comp/src/gb.net.smtp/.src/SmtpSession.class | 147 + comp/src/gb.net.smtp/.src/SslSession.class | 76 + comp/src/gb.net.smtp/.src/TcpSession.class | 89 + comp/src/gb.net.smtp/.src/TlsSession.class | 77 + comp/src/gb.report/.component | 7 + .../.connection/Connection1.connection | 10 + .../.connection/Connection2.connection | 10 + .../gb.report/.connection/MainConn.connection | 9 + comp/src/gb.report/.dir_icon.png | Bin 0 -> 1571 bytes comp/src/gb.report/.directory | 2 + .../.hidden/control/reportdrawingarea.png | Bin 0 -> 621 bytes .../.hidden/control/reportgridview.png | Bin 0 -> 139 bytes .../gb.report/.hidden/control/reporthbox.png | Bin 0 -> 153 bytes .../gb.report/.hidden/control/reportimage.png | Bin 0 -> 1531 bytes .../gb.report/.hidden/control/reportlabel.png | Bin 0 -> 449 bytes .../gb.report/.hidden/control/reportline.png | Bin 0 -> 113 bytes .../.hidden/control/reportpagebreak.png | Bin 0 -> 158 bytes .../gb.report/.hidden/control/reportpanel.png | Bin 0 -> 154 bytes .../.hidden/control/reportsvgimage.png | Bin 0 -> 2284 bytes .../.hidden/control/reporttextlabel.png | Bin 0 -> 686 bytes .../gb.report/.hidden/control/reportvbox.png | Bin 0 -> 241 bytes .../gb.report/.hidden/control/reportview.png | Bin 0 -> 732 bytes .../.hidden/control/reportvpanel.png | Bin 0 -> 200 bytes comp/src/gb.report/.icon.png | Bin 0 -> 16142 bytes comp/src/gb.report/.lang/ca.mo | Bin 0 -> 2638 bytes comp/src/gb.report/.lang/ca.po | 224 + comp/src/gb.report/.lang/cs.mo | Bin 0 -> 2646 bytes comp/src/gb.report/.lang/cs.po | 233 + comp/src/gb.report/.lang/es.mo | Bin 0 -> 2864 bytes comp/src/gb.report/.lang/es.po | 423 + comp/src/gb.report/.lang/es_ES.mo | Bin 0 -> 2867 bytes comp/src/gb.report/.lang/es_ES.po | 436 + comp/src/gb.report/.lang/fr.mo | Bin 0 -> 1530 bytes comp/src/gb.report/.lang/fr.po | 454 + comp/src/gb.report/.lang/nl.mo | Bin 0 -> 4609 bytes comp/src/gb.report/.lang/nl.po | 436 + comp/src/gb.report/.lang/zh.mo | Bin 0 -> 4196 bytes comp/src/gb.report/.lang/zh.po | 423 + comp/src/gb.report/.project | 25 + .../gb.report/.src/Borders/ReportBorder.class | 209 + .../.src/Borders/_ReportBorderSide.class | 16 + .../.src/Borders/_ReportRoundCorner.class | 136 + .../.src/BoxShadow/FReportBoxEditor.class | 26 + .../.src/BoxShadow/FReportBoxEditor.form | 11 + .../.src/BoxShadow/ReportBoxShadow.class | 222 + .../.src/BoxShadow/_ReportBoxShadow.class | 13 + .../gb.report/.src/Brush/ReportBrush.class | 222 + .../.src/Controls/ReportControl.class | 596 + .../gb.report/.src/MainTools/MReport.module | 26 + .../.src/MainTools/ReportUnits.module | 92 + .../.src/MainTools/Types/TControl.class | 76 + .../.src/MainTools/Types/TPageColumn.class | 8 + .../.src/MainTools/Types/TSizeHint.class | 5 + .../.src/MainTools/Types/TSizeParse.class | 70 + comp/src/gb.report/.src/Optional/Align.class | 10 + .../src/gb.report/.src/Optional/Arrange.class | 9 + comp/src/gb.report/.src/Optional/Line.class | 13 + .../.src/Padding/ReportPadding.class | 63 + comp/src/gb.report/.src/Preview/CPrint.class | 54 + .../src/gb.report/.src/Preview/FOptions.class | 9 + comp/src/gb.report/.src/Preview/FOptions.form | 52 + .../src/gb.report/.src/Preview/FPreview.class | 400 + comp/src/gb.report/.src/Preview/FPreview.form | 274 + comp/src/gb.report/.src/Preview/FPrint.class | 37 + comp/src/gb.report/.src/Preview/FPrint.form | 132 + comp/src/gb.report/.src/Preview/Form1.class | 30 + comp/src/gb.report/.src/Preview/Form1.form | 59 + .../gb.report/.src/Preview/ReportView.class | 709 + .../.src/Preview/ReportViewTask.class | 33 + comp/src/gb.report/.src/Report.class | 794 + comp/src/gb.report/.src/ReportContainer.class | 945 + .../gb.report/.src/ReportDrawingArea.class | 63 + comp/src/gb.report/.src/ReportFrame.class | 354 + comp/src/gb.report/.src/ReportGridView.class | 188 + .../ReportGridView/_ReportGridViewCell.class | 1 + .../_ReportGridViewColumn.class | 18 + .../_ReportGridViewColumns.class | 117 + .../ReportGridView/_ReportGridViewRow.class | 51 + .../ReportGridView/_ReportGridViewRows.class | 160 + comp/src/gb.report/.src/ReportHBox.class | 21 + comp/src/gb.report/.src/ReportImage.class | 186 + comp/src/gb.report/.src/ReportLabel.class | 226 + comp/src/gb.report/.src/ReportLine.class | 127 + comp/src/gb.report/.src/ReportPageBreak.class | 6 + comp/src/gb.report/.src/ReportPanel.class | 25 + comp/src/gb.report/.src/ReportSection.class | 23 + comp/src/gb.report/.src/ReportSvgImage.class | 185 + comp/src/gb.report/.src/ReportTextLabel.class | 97 + comp/src/gb.report/.src/ReportVBox.class | 33 + comp/src/gb.report/.src/ReportVPanel.class | 336 + .../src/gb.report/.src/Tests/Old/Paints.class | 33 + .../gb.report/.src/Tests/Old/Report1.class | 8 + .../gb.report/.src/Tests/Old/Report1.report | 55 + .../gb.report/.src/Tests/Old/Report10.class | 8 + .../gb.report/.src/Tests/Old/Report10.report | 64 + .../gb.report/.src/Tests/Old/Report12.class | 16 + .../gb.report/.src/Tests/Old/Report12.report | 17 + .../gb.report/.src/Tests/Old/Report13.class | 38 + .../gb.report/.src/Tests/Old/Report13.report | 21 + .../gb.report/.src/Tests/Old/Report14.class | 32 + .../gb.report/.src/Tests/Old/Report14.report | 90 + .../gb.report/.src/Tests/Old/Report2.class | 20 + .../gb.report/.src/Tests/Old/Report2.report | 19 + .../gb.report/.src/Tests/Old/Report3.class | 20 + .../gb.report/.src/Tests/Old/Report3.report | 42 + .../gb.report/.src/Tests/Old/Report4.class | 46 + .../gb.report/.src/Tests/Old/Report4.report | 96 + .../gb.report/.src/Tests/Old/Report5.class | 1 + .../gb.report/.src/Tests/Old/Report5.report | 85 + .../gb.report/.src/Tests/Old/Report6.class | 2 + .../gb.report/.src/Tests/Old/Report6.report | 17 + .../gb.report/.src/Tests/Old/Report7.class | 29 + .../gb.report/.src/Tests/Old/Report7.report | 59 + .../gb.report/.src/Tests/Old/Report8.class | 56 + .../gb.report/.src/Tests/Old/Report8.report | 79 + .../gb.report/.src/Tests/Old/myReport1.class | 43 + .../gb.report/.src/Tests/Old/myReport1.report | 142 + .../gb.report/.src/Tests/Old/myReport2.class | 85 + .../gb.report/.src/Tests/Old/myReport2.report | 228 + .../gb.report/.src/Tests/Old/myReport5.class | 22 + .../gb.report/.src/Tests/Old/myReport5.report | 91 + .../gb.report/.src/Tests/OutputReport.class | 146 + .../gb.report/.src/Tests/OutputReport.report | 306 + .../gb.report/.src/Tests/OutputReport2.class | 166 + .../gb.report/.src/Tests/OutputReport2.report | 308 + comp/src/gb.report/.src/Tests/Paints.class | 33 + comp/src/gb.report/.src/Tests/Report11.class | 29 + comp/src/gb.report/.src/Tests/Report11.report | 29 + comp/src/gb.report/.src/Tests/Report41.class | 36 + comp/src/gb.report/.src/Tests/Report41.report | 34 + comp/src/gb.report/.src/Tests/Report51.class | 69 + comp/src/gb.report/.src/Tests/Report51.report | 15 + comp/src/gb.report/.src/Tests/Report52.class | 69 + comp/src/gb.report/.src/Tests/Report52.report | 15 + comp/src/gb.report/.src/Tests/Report9.class | 17 + comp/src/gb.report/.src/Tests/Report9.report | 22 + comp/src/gb.report/.src/Tests/Test.module | 9 + .../.src/Tests/rpTestShadowGrid.class | 23 + .../.src/Tests/rpTestShadowGrid.report | 25 + comp/src/gb.report/ChangeLog | 6 + comp/src/gb.report/gambas.svg | 540 + comp/src/gb.report/img/16/red-arrow-h.png | Bin 0 -> 117 bytes comp/src/gb.report/img/16/red-arrow-v.png | Bin 0 -> 120 bytes comp/src/gb.report/img/22/FullWidth.png | Bin 0 -> 139 bytes comp/src/gb.report/img/22/OnePage.png | Bin 0 -> 117 bytes comp/src/gb.report/img/22/RealSize.png | Bin 0 -> 204 bytes comp/src/gb.report/img/22/TwoPage.png | Bin 0 -> 121 bytes comp/src/gb.report/img/32/Collatecopie.png | Bin 0 -> 449 bytes comp/src/gb.report/img/32/Empty.png | Bin 0 -> 719 bytes comp/src/gb.report/img/32/grayscale.png | Bin 0 -> 1564 bytes comp/src/gb.report/img/32/reverse.png | Bin 0 -> 820 bytes comp/src/gb.report/img/control/hbox.png | Bin 0 -> 158 bytes comp/src/gb.report/img/control/label.png | Bin 0 -> 466 bytes comp/src/gb.report/img/control/picturebox.png | Bin 0 -> 2381 bytes comp/src/gb.report/img/control/vbox.png | Bin 0 -> 250 bytes comp/src/gb.report/img/control/vpanel.png | Bin 0 -> 200 bytes comp/src/gb.report/img/logo.svg | 540 + comp/src/gb.report/printer1.png | Bin 0 -> 1630 bytes comp/src/gb.report2/.component | 5 + .../.connection/Connection1.connection | 11 + .../.connection/Connection2.connection | 10 + .../.connection/Connection2.template | 398 + comp/src/gb.report2/.directory | 2 + .../.hidden/control/reportdrawingarea.png | 1 + .../.hidden/control/reportgridview.png | 1 + .../gb.report2/.hidden/control/reporthbox.png | 1 + .../.hidden/control/reportimage.png | 1 + .../.hidden/control/reportlabel.png | 1 + .../gb.report2/.hidden/control/reportline.png | Bin 0 -> 101 bytes .../.hidden/control/reportpagebreak.png | Bin 0 -> 558 bytes .../.hidden/control/reportpanel.png | 1 + .../.hidden/control/reportsvgimage.png | Bin 0 -> 273 bytes .../.hidden/control/reporttextlabel.png | 1 + .../gb.report2/.hidden/control/reportvbox.png | 1 + .../gb.report2/.hidden/control/reportview.png | Bin 0 -> 233 bytes .../.hidden/control/reportvpanel.png | 1 + comp/src/gb.report2/.icon.png | Bin 0 -> 5893 bytes comp/src/gb.report2/.lang/es.mo | Bin 0 -> 1672 bytes comp/src/gb.report2/.lang/es.po | 343 + comp/src/gb.report2/.lang/es_ES.mo | Bin 0 -> 1675 bytes comp/src/gb.report2/.lang/es_ES.po | 343 + comp/src/gb.report2/.lang/fr.mo | Bin 0 -> 2132 bytes comp/src/gb.report2/.lang/fr.po | 389 + comp/src/gb.report2/.lang/nl.mo | Bin 0 -> 4159 bytes comp/src/gb.report2/.lang/nl.po | 388 + comp/src/gb.report2/.project | 19 + .../gb.report2/.src/Evaluator/CResult.class | 55 + .../gb.report2/.src/Evaluator/_RepExp.class | 50 + comp/src/gb.report2/.src/Optional/Align.class | 10 + .../gb.report2/.src/Optional/Arrange.class | 11 + comp/src/gb.report2/.src/Optional/Line.class | 13 + comp/src/gb.report2/.src/Report.class | 792 + .../src/gb.report2/.src/ReportContainer.class | 1080 + comp/src/gb.report2/.src/ReportControl.class | 648 + .../gb.report2/.src/ReportDrawingArea.class | 96 + comp/src/gb.report2/.src/ReportFrame.class | 366 + .../.src/ReportGridView/ReportGridView.class | 52 + .../_ReportGridViewColumn.class | 96 + .../_ReportGridViewColumns.class | 141 + .../ReportGridView/_ReportGridViewData.class | 2 + .../ReportGridView/_ReportGridViewRow.class | 52 + .../ReportGridView/_ReportGridViewRows.class | 120 + comp/src/gb.report2/.src/ReportHBox.class | 21 + comp/src/gb.report2/.src/ReportImage.class | 200 + comp/src/gb.report2/.src/ReportLabel.class | 239 + comp/src/gb.report2/.src/ReportLine.class | 127 + .../src/gb.report2/.src/ReportPageBreak.class | 6 + comp/src/gb.report2/.src/ReportPanel.class | 25 + comp/src/gb.report2/.src/ReportSection.class | 23 + comp/src/gb.report2/.src/ReportSvgImage.class | 200 + .../src/gb.report2/.src/ReportTextLabel.class | 143 + comp/src/gb.report2/.src/ReportVBox.class | 17 + comp/src/gb.report2/.src/ReportVPanel.class | 26 + comp/src/gb.report2/.src/Tests/Report10.class | 15 + .../src/gb.report2/.src/Tests/Report10.report | 50 + comp/src/gb.report2/.src/Tests/Report13.class | 8 + .../src/gb.report2/.src/Tests/Report13.report | 48 + comp/src/gb.report2/.src/Tests/Report14.class | 8 + .../src/gb.report2/.src/Tests/Report14.report | 66 + comp/src/gb.report2/.src/Tests/Report15.class | 2 + .../src/gb.report2/.src/Tests/Report15.report | 13 + comp/src/gb.report2/.src/Tests/Report16.class | 14 + .../src/gb.report2/.src/Tests/Report16.report | 27 + comp/src/gb.report2/.src/Tests/Report17.class | 8 + .../src/gb.report2/.src/Tests/Report17.report | 24 + .../src/gb.report2/.src/Tests/old/FMain.class | 16 + comp/src/gb.report2/.src/Tests/old/FMain.form | 10 + .../gb.report2/.src/Tests/old/Module1.module | 19 + .../.src/Tests/old/OutputReport2.class | 166 + .../.src/Tests/old/OutputReport2.report | 308 + .../gb.report2/.src/Tests/old/Report1.class | 43 + .../gb.report2/.src/Tests/old/Report1.report | 50 + .../gb.report2/.src/Tests/old/Report11.class | 32 + .../gb.report2/.src/Tests/old/Report11.report | 101 + .../gb.report2/.src/Tests/old/Report12.class | 20 + .../gb.report2/.src/Tests/old/Report12.report | 47 + .../gb.report2/.src/Tests/old/Report2.class | 22 + .../gb.report2/.src/Tests/old/Report2.report | 26 + .../gb.report2/.src/Tests/old/Report3.class | 26 + .../gb.report2/.src/Tests/old/Report3.report | 27 + .../gb.report2/.src/Tests/old/Report4.class | 18 + .../gb.report2/.src/Tests/old/Report4.report | 23 + .../gb.report2/.src/Tests/old/Report5.class | 8 + .../gb.report2/.src/Tests/old/Report5.report | 34 + .../gb.report2/.src/Tests/old/Report51.class | 207 + .../gb.report2/.src/Tests/old/Report51.report | 128 + .../gb.report2/.src/Tests/old/Report6.class | 2 + .../gb.report2/.src/Tests/old/Report6.report | 14 + .../gb.report2/.src/Tests/old/Report7.class | 30 + .../gb.report2/.src/Tests/old/Report7.report | 13 + .../gb.report2/.src/Tests/old/Report8.class | 2 + .../gb.report2/.src/Tests/old/Report8.report | 22 + .../gb.report2/.src/Tests/old/Report9.class | 9 + .../gb.report2/.src/Tests/old/Report9.report | 12 + comp/src/gb.report2/.src/Tools/CPrint.class | 54 + comp/src/gb.report2/.src/Tools/MUtil.module | 24 + .../.src/Types/Base/ReportBrush.class | 222 + .../.src/Types/Base/ReportMargin.class | 40 + .../.src/Types/Base/ReportPadding.class | 94 + .../.src/Types/Border/ReportBorder.class | 205 + .../.src/Types/Border/_ReportBorderSide.class | 16 + .../Types/Border/_ReportRoundCorner.class | 137 + .../Types/BoxShadow/ReportBoxShadow.class | 225 + .../Types/BoxShadow/_ReportBoxShadow.class | 14 + .../.src/Types/ReportSizeHints.class | 26 + .../.src/Types/ReportSizeParser.class | 83 + comp/src/gb.report2/.src/Types/TControl.class | 3 + .../gb.report2/.src/Types/TSizeParse.class | 5 + .../.src/Types/_ReportVirtualControl.class | 78 + .../src/gb.report2/.src/Viewer/FPreview.class | 535 + comp/src/gb.report2/.src/Viewer/FPreview.form | 362 + .../gb.report2/.src/Viewer/ReportView.class | 414 + comp/src/gb.report2/16/full-width.png | Bin 0 -> 134 bytes comp/src/gb.report2/16/one-page.png | Bin 0 -> 126 bytes comp/src/gb.report2/16/real-size.png | Bin 0 -> 192 bytes comp/src/gb.report2/16/red-arrow-h.png | Bin 0 -> 117 bytes comp/src/gb.report2/16/red-arrow-v.png | Bin 0 -> 120 bytes comp/src/gb.report2/16/two-pages.png | Bin 0 -> 130 bytes comp/src/gb.report2/22/FullWidth.png | Bin 0 -> 139 bytes comp/src/gb.report2/22/OnePage.png | Bin 0 -> 117 bytes comp/src/gb.report2/22/RealSize.png | Bin 0 -> 204 bytes comp/src/gb.report2/22/TwoPage.png | Bin 0 -> 121 bytes comp/src/gb.report2/32/Collatecopie.png | Bin 0 -> 449 bytes comp/src/gb.report2/32/Empty.png | Bin 0 -> 719 bytes comp/src/gb.report2/32/grayscale.png | Bin 0 -> 1564 bytes comp/src/gb.report2/32/reverse.png | Bin 0 -> 820 bytes comp/src/gb.report2/FunctionsList | 52 + comp/src/gb.report2/Structure | 9 + comp/src/gb.report2/gambas.svg | 540 + comp/src/gb.report2/icon.png | Bin 0 -> 2357 bytes comp/src/gb.report2/tmpJournal | 12 + comp/src/gb.report2/tortueface.gif | Bin 0 -> 1019 bytes comp/src/gb.scanner/.component | 4 + comp/src/gb.scanner/.directory | 2 + comp/src/gb.scanner/.icon.png | Bin 0 -> 13202 bytes comp/src/gb.scanner/.project | 18 + comp/src/gb.scanner/.src/Demo/Form1.class | 22 + comp/src/gb.scanner/.src/Demo/Form1.form | 11 + comp/src/gb.scanner/.src/MTest.module | 17 + comp/src/gb.scanner/.src/MTest2.module | 21 + comp/src/gb.scanner/.src/Module1.module | 28 + comp/src/gb.scanner/.src/Module2.module | 10 + comp/src/gb.scanner/.src/Scanner.class | 393 + comp/src/gb.scanner/.src/Scanners.class | 167 + comp/src/gb.scanner/.src/_Option.class | 124 + comp/src/gb.scanner/scanner.png | Bin 0 -> 22747 bytes comp/src/gb.settings/.component | 4 + comp/src/gb.settings/.directory | 2 + comp/src/gb.settings/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.settings/.project | 14 + comp/src/gb.settings/.src/Main.module | 23 + comp/src/gb.settings/.src/Settings.class | 834 + .../src/gb.settings/.src/_Settings_Keys.class | 55 + comp/src/gb.term.form/.component | 7 + comp/src/gb.term.form/.directory | 2 + comp/src/gb.term.form/.hidden/CHANGELOG | 4 + .../.hidden/control/termbutton.png | 1 + .../.hidden/control/termcheckbox.png | 1 + .../.hidden/control/termframe.png | 1 + .../gb.term.form/.hidden/control/termhbox.png | 1 + .../.hidden/control/termlabel.png | 1 + .../.hidden/control/termlistbox.png | 1 + .../.hidden/control/termpanel.png | 1 + .../.hidden/control/termpicturebox.png | 1 + .../.hidden/control/termradiobutton.png | 1 + .../.hidden/control/termscrollbar.png | 1 + .../.hidden/control/termtextbox.png | 1 + .../gb.term.form/.hidden/control/termvbox.png | 1 + comp/src/gb.term.form/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.term.form/.lang/nl.mo | Bin 0 -> 1086 bytes comp/src/gb.term.form/.lang/nl.po | 20 + comp/src/gb.term.form/.project | 16 + comp/src/gb.term.form/.src/Align.class | 8 + comp/src/gb.term.form/.src/Arrange.class | 4 + comp/src/gb.term.form/.src/Attr.class | 341 + comp/src/gb.term.form/.src/Border.class | 4 + comp/src/gb.term.form/.src/Char.class | 14 + comp/src/gb.term.form/.src/Desktop.class | 45 + .../gb.term.form/.src/Dialog/Message.class | 42 + comp/src/gb.term.form/.src/Key.class | 65 + comp/src/gb.term.form/.src/Mouse.class | 102 + comp/src/gb.term.form/.src/TermButton.class | 150 + comp/src/gb.term.form/.src/TermCheckBox.class | 114 + comp/src/gb.term.form/.src/TermColor.class | 21 + .../src/gb.term.form/.src/TermContainer.class | 243 + comp/src/gb.term.form/.src/TermControl.class | 783 + comp/src/gb.term.form/.src/TermForm.class | 23 + comp/src/gb.term.form/.src/TermFrame.class | 57 + comp/src/gb.term.form/.src/TermHBox.class | 9 + comp/src/gb.term.form/.src/TermLabel.class | 73 + comp/src/gb.term.form/.src/TermListBox.class | 201 + comp/src/gb.term.form/.src/TermPanel.class | 23 + .../gb.term.form/.src/TermPictureBox.class | 222 + .../gb.term.form/.src/TermRadioButton.class | 112 + .../src/gb.term.form/.src/TermScrollBar.class | 173 + comp/src/gb.term.form/.src/TermTextBox.class | 187 + comp/src/gb.term.form/.src/TermVBox.class | 11 + comp/src/gb.term.form/.src/TermWindow.class | 577 + comp/src/gb.term.form/.src/TermWindows.class | 715 + comp/src/gb.term.form/.src/Test/FTest2.class | 61 + comp/src/gb.term.form/.src/Test/Main.module | 136 + comp/src/gb.term.form/.src/Test/Main2.module | 61 + comp/src/gb.term.form/.src/Test/Main3.module | 7 + .../src/gb.term.form/.src/Test/Module1.module | 7 + .../gb.term.form/.src/Test/Termform1.class | 8 + .../gb.term.form/.src/Test/Termform1.termform | 16 + .../gb.term.form/.src/Test/Termform2.class | 9 + .../gb.term.form/.src/Test/Termform2.termform | 5 + comp/src/gb.term.form/.src/Test/trfTest.class | 85 + comp/src/gb.term.form/all.png | Bin 0 -> 1507 bytes comp/src/gb.util.web/.component | 4 + comp/src/gb.util.web/.directory | 2 + .../.hidden/control/ccontainer.png | Bin 0 -> 1606 bytes .../gb.util.web/.hidden/control/ccontrol.png | Bin 0 -> 1606 bytes comp/src/gb.util.web/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.util.web/.project | 9 + comp/src/gb.util.web/.src/JS.class | 68 + comp/src/gb.util.web/.src/JSON.module | 373 + .../src/gb.util.web/.src/JSONCollection.class | 29 + comp/src/gb.util.web/.src/MMain.module | 25 + comp/src/gb.util.web/.src/URL.class | 187 + comp/src/gb.util.web/.src/URLQuery.class | 113 + comp/src/gb.util/.component | 4 + comp/src/gb.util/.directory | 2 + comp/src/gb.util/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.util/.project | 9 + comp/src/gb.util/.src/Class.class | 112 + comp/src/gb.util/.src/ClassStat.class | 11 + comp/src/gb.util/.src/CsvFile.class | 93 + comp/src/gb.util/.src/Date.module | 154 + comp/src/gb.util/.src/File.class | 29 + comp/src/gb.util/.src/MMain.module | 58 + comp/src/gb.util/.src/MPhonetic_French.module | 278 + comp/src/gb.util/.src/Shell.module | 59 + comp/src/gb.util/.src/String.class | 291 + comp/src/gb.web.feed/.component | 7 + comp/src/gb.web.feed/.directory | 2 + comp/src/gb.web.feed/.hidden/TODO | 10 + comp/src/gb.web.feed/.icon.png | Bin 0 -> 12094 bytes comp/src/gb.web.feed/.lang/nl.mo | Bin 0 -> 3318 bytes comp/src/gb.web.feed/.lang/nl.po | 148 + comp/src/gb.web.feed/.project | 14 + comp/src/gb.web.feed/.src/Main.module | 28 + comp/src/gb.web.feed/.src/Rss.class | 427 + comp/src/gb.web.feed/.src/RssCategory.class | 24 + comp/src/gb.web.feed/.src/RssCloud.class | 64 + comp/src/gb.web.feed/.src/RssDate.class | 38 + comp/src/gb.web.feed/.src/RssEnclosure.class | 29 + comp/src/gb.web.feed/.src/RssGuid.class | 37 + comp/src/gb.web.feed/.src/RssImage.class | 79 + comp/src/gb.web.feed/.src/RssItem.class | 113 + comp/src/gb.web.feed/.src/RssSource.class | 27 + comp/src/gb.web.feed/.src/RssTextInput.class | 51 + comp/src/gb.web.feed/Feed-icon.svg | 18 + comp/src/gb.web.feed/test.xml | 41 + comp/src/gb.web.form/.component | 7 + comp/src/gb.web.form/.directory | 2 + .../.hidden/Uncompressed/gw-style.css | 504 + .../gb.web.form/.hidden/Uncompressed/lib.js | 1321 + .../.hidden/Uncompressed/style.css | 732 + comp/src/gb.web.form/.hidden/calendar.js | 1577 + .../gb.web.form/.hidden/control/webbutton.png | 1 + .../.hidden/control/webcheckbox.png | 1 + .../.hidden/control/webcombobox.png | 1 + .../.hidden/control/webcontainer.png | Bin 0 -> 109 bytes .../.hidden/control/webdatebox.png | 1 + .../.hidden/control/webdatechooser.png | 1 + .../.hidden/control/webexpander.png | 1 + .../gb.web.form/.hidden/control/webhbox.png | 1 + .../gb.web.form/.hidden/control/webhtml.png | Bin 0 -> 403 bytes .../gb.web.form/.hidden/control/webimage.png | 1 + .../gb.web.form/.hidden/control/weblabel.png | 1 + .../.hidden/control/weblistbox.png | 1 + .../gb.web.form/.hidden/control/webmenu.png | Bin 0 -> 637 bytes .../.hidden/control/webmenubar.png | Bin 0 -> 472 bytes .../.hidden/control/webmenuitem.png | Bin 0 -> 1059 bytes .../.hidden/control/webprogressbar.png | 1 + .../.hidden/control/webradiobutton.png | 1 + .../.hidden/control/webscrollview.png | 1 + .../.hidden/control/webseparator.png | 1 + .../gb.web.form/.hidden/control/webslider.png | 1 + .../.hidden/control/webspinbox.png | 1 + .../gb.web.form/.hidden/control/webtable.png | Bin 0 -> 195 bytes .../.hidden/control/webtabpanel.png | 1 + .../.hidden/control/webtextarea.png | 1 + .../.hidden/control/webtextbox.png | 1 + .../gb.web.form/.hidden/control/webtimer.png | 1 + .../.hidden/control/webuploadarea.png | Bin 0 -> 245 bytes .../.hidden/control/webuploadbutton.png | Bin 0 -> 153 bytes .../.hidden/control/webuploader.png | Bin 0 -> 387 bytes .../gb.web.form/.hidden/control/webvbox.png | 1 + comp/src/gb.web.form/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.web.form/.lang/fr.mo | Bin 0 -> 582 bytes comp/src/gb.web.form/.lang/fr.po | 274 + comp/src/gb.web.form/.lang/nl.mo | Bin 0 -> 3096 bytes comp/src/gb.web.form/.lang/nl.po | 272 + comp/src/gb.web.form/.project | 17 + comp/src/gb.web.form/.public/favicon.png | Bin 0 -> 16241 bytes .../src/gb.web.form/.public/gw-arrow-down.png | Bin 0 -> 132 bytes .../src/gb.web.form/.public/gw-arrow-left.png | Bin 0 -> 117 bytes .../gb.web.form/.public/gw-arrow-right.png | Bin 0 -> 114 bytes comp/src/gb.web.form/.public/gw-arrow-up.png | Bin 0 -> 130 bytes comp/src/gb.web.form/.public/gw-close.png | Bin 0 -> 478 bytes comp/src/gb.web.form/.public/gw-max.png | Bin 0 -> 233 bytes .../src/gb.web.form/.public/gw-table-more.gif | Bin 0 -> 723 bytes comp/src/gb.web.form/.public/gw-waiting.gif | Bin 0 -> 6968 bytes .../src/gb.web.form/.public/message/close.svg | 148 + .../src/gb.web.form/.public/message/error.png | Bin 0 -> 1173 bytes comp/src/gb.web.form/.public/message/info.png | Bin 0 -> 828 bytes .../gb.web.form/.public/message/question.png | Bin 0 -> 1189 bytes .../gb.web.form/.public/message/warning.png | Bin 0 -> 588 bytes comp/src/gb.web.form/.public/new.png | Bin 0 -> 608 bytes comp/src/gb.web.form/.public/open.png | Bin 0 -> 817 bytes comp/src/gb.web.form/.src/Align.class | 23 + comp/src/gb.web.form/.src/Arrange.class | 5 + .../gb.web.form/.src/Calendar/FCalendar.class | 25 + .../.src/Calendar/FCalendar.webform | 9 + .../.src/Calendar/WebDateBox.class | 121 + .../.src/Calendar/WebDateChooser.class | 238 + comp/src/gb.web.form/.src/Color.class | 32 + comp/src/gb.web.form/.src/Header.class | 36 + comp/src/gb.web.form/.src/Header.webpage | 12 + comp/src/gb.web.form/.src/Main.module | 30 + .../gb.web.form/.src/Message/FMessage.class | 94 + .../gb.web.form/.src/Message/FMessage.webform | 43 + .../gb.web.form/.src/Message/Message.class | 61 + comp/src/gb.web.form/.src/Scroll.class | 8 + comp/src/gb.web.form/.src/Select.class | 7 + comp/src/gb.web.form/.src/Test/FHello.class | 8 + comp/src/gb.web.form/.src/Test/FHello.webform | 31 + .../.src/Test/FTestWebUploader.class | 16 + .../.src/Test/FTestWebUploader.webform | 18 + comp/src/gb.web.form/.src/Test/Webform1.class | 83 + .../gb.web.form/.src/Test/Webform1.webform | 135 + comp/src/gb.web.form/.src/Test/Webform2.class | 146 + .../gb.web.form/.src/Test/Webform2.webform | 274 + comp/src/gb.web.form/.src/Test/Webform3.class | 75 + .../gb.web.form/.src/Test/Webform3.webform | 41 + comp/src/gb.web.form/.src/Test/Webform4.class | 41 + .../gb.web.form/.src/Test/Webform4.webform | 45 + comp/src/gb.web.form/.src/Test/Webform5.class | 16 + .../gb.web.form/.src/Test/Webform5.webform | 25 + comp/src/gb.web.form/.src/Test/Webform6.class | 14 + .../gb.web.form/.src/Test/Webform6.webform | 25 + comp/src/gb.web.form/.src/WebButton.class | 117 + comp/src/gb.web.form/.src/WebCheckBox.class | 67 + comp/src/gb.web.form/.src/WebComboBox.class | 248 + comp/src/gb.web.form/.src/WebContainer.class | 415 + comp/src/gb.web.form/.src/WebControl.class | 1061 + .../gb.web.form/.src/WebControlStyle.class | 46 + comp/src/gb.web.form/.src/WebExpander.class | 102 + comp/src/gb.web.form/.src/WebForm.class | 940 + comp/src/gb.web.form/.src/WebHBox.class | 15 + comp/src/gb.web.form/.src/WebHtml.class | 32 + comp/src/gb.web.form/.src/WebImage.class | 61 + comp/src/gb.web.form/.src/WebLabel.class | 83 + comp/src/gb.web.form/.src/WebMenu.class | 133 + comp/src/gb.web.form/.src/WebMenuBar.class | 14 + comp/src/gb.web.form/.src/WebMenuItem.class | 70 + .../src/gb.web.form/.src/WebProgressBar.class | 58 + .../src/gb.web.form/.src/WebRadioButton.class | 81 + comp/src/gb.web.form/.src/WebScrollView.class | 188 + comp/src/gb.web.form/.src/WebSeparator.class | 18 + comp/src/gb.web.form/.src/WebSlider.class | 21 + comp/src/gb.web.form/.src/WebSpinBox.class | 123 + comp/src/gb.web.form/.src/WebTabPanel.class | 193 + .../gb.web.form/.src/WebTable/WebTable.class | 644 + .../.src/WebTable/WebTableData.class | 8 + .../.src/WebTable/WebTableSelection.class | 296 + .../.src/WebTable/_WebTableColumn.class | 136 + .../.src/WebTable/_WebTableColumns.class | 115 + comp/src/gb.web.form/.src/WebTextArea.class | 147 + comp/src/gb.web.form/.src/WebTextBox.class | 187 + comp/src/gb.web.form/.src/WebTimer.class | 93 + comp/src/gb.web.form/.src/WebUploadArea.class | 198 + .../gb.web.form/.src/WebUploadButton.class | 137 + comp/src/gb.web.form/.src/WebUploader.class | 135 + comp/src/gb.web.form/.src/WebVBox.class | 15 + comp/src/gb.web.form/.src/WebWindow.class | 372 + comp/src/gb.web.form/ac.js | 225 + comp/src/gb.web.form/arrow-down.png | Bin 0 -> 91 bytes comp/src/gb.web.form/arrow-right.png | Bin 0 -> 94 bytes comp/src/gb.web.form/clear.png | Bin 0 -> 116 bytes comp/src/gb.web.form/lib.js | 1440 + comp/src/gb.web.form/shadow.png | Bin 0 -> 76 bytes comp/src/gb.web.form/style.css | 861 + comp/src/gb.web/.component | 5 + comp/src/gb.web/.directory | 2 + comp/src/gb.web/.hidden/Session_opt | 527 + comp/src/gb.web/.icon.png | Bin 0 -> 10569 bytes comp/src/gb.web/.project | 16 + comp/src/gb.web/.src/Application.module | 87 + comp/src/gb.web/.src/CGI.module | 124 + comp/src/gb.web/.src/FileSessionManager.class | 316 + comp/src/gb.web/.src/Main.module | 82 + comp/src/gb.web/.src/Request.module | 268 + comp/src/gb.web/.src/Response.module | 370 + comp/src/gb.web/.src/Session.module | 287 + comp/src/gb.web/.src/SessionManager.class | 38 + .../gb.web/.src/SqliteSessionManager.class | 372 + comp/src/gb.web/.src/URL.class | 62 + comp/src/gb.web/.src/WebPage.class | 104 + comp/src/gb.web/.src/Webpage1.class | 7 + comp/src/gb.web/.src/Webpage1.webpage | 22 + comp/src/gb.web/.src/Webpage2.class | 2 + comp/src/gb.web/.src/Webpage2.webpage | 1 + comp/src/gb.web/.src/_Request_Get.class | 48 + comp/src/gb.web/.src/_Request_Post.class | 243 + comp/src/gb.web/.src/_ResponseCache.module | 13 + comp/src/order | 1 + component.am | 65 + configure.ac | 95 + gb.cairo/AUTHORS | 0 gb.cairo/COPYING | 1 + gb.cairo/ChangeLog | 0 gb.cairo/INSTALL | 1 + gb.cairo/Makefile.am | 3 + gb.cairo/NEWS | 0 gb.cairo/README | 0 gb.cairo/acinclude.m4 | 1 + gb.cairo/component.am | 1 + gb.cairo/configure.ac | 17 + gb.cairo/gambas.h | 1 + gb.cairo/gb.image.h | 1 + gb.cairo/gb_common.h | 1 + gb.cairo/m4 | 1 + gb.cairo/reconf | 1 + gb.cairo/src/Makefile.am | 11 + gb.cairo/src/c_cairo.c | 1199 + gb.cairo/src/c_cairo.h | 85 + gb.cairo/src/c_surface.c | 210 + gb.cairo/src/c_surface.h | 50 + gb.cairo/src/gb.cairo.component | 4 + gb.cairo/src/main.c | 57 + gb.cairo/src/main.h | 37 + gb.compress.bzlib2/AUTHORS | 0 gb.compress.bzlib2/COPYING | 1 + gb.compress.bzlib2/ChangeLog | 0 gb.compress.bzlib2/INSTALL | 1 + gb.compress.bzlib2/Makefile.am | 3 + gb.compress.bzlib2/NEWS | 0 gb.compress.bzlib2/README | 0 gb.compress.bzlib2/acinclude.m4 | 1 + gb.compress.bzlib2/component.am | 1 + gb.compress.bzlib2/configure.ac | 25 + gb.compress.bzlib2/gambas.h | 1 + gb.compress.bzlib2/gb.compress.h | 1 + gb.compress.bzlib2/gb_common.h | 1 + gb.compress.bzlib2/m4 | 1 + gb.compress.bzlib2/reconf | 1 + gb.compress.bzlib2/src/Makefile.am | 10 + .../src/gb.compress.bzlib2.component | 5 + gb.compress.bzlib2/src/main.c | 579 + gb.compress.bzlib2/src/main.h | 49 + gb.compress.zlib/AUTHORS | 0 gb.compress.zlib/COPYING | 1 + gb.compress.zlib/ChangeLog | 0 gb.compress.zlib/INSTALL | 1 + gb.compress.zlib/Makefile.am | 3 + gb.compress.zlib/NEWS | 0 gb.compress.zlib/README | 0 gb.compress.zlib/acinclude.m4 | 1 + gb.compress.zlib/component.am | 1 + gb.compress.zlib/configure.ac | 23 + gb.compress.zlib/gambas.h | 1 + gb.compress.zlib/gb.compress.h | 1 + gb.compress.zlib/gb_common.h | 1 + gb.compress.zlib/m4 | 1 + gb.compress.zlib/reconf | 1 + gb.compress.zlib/src/Makefile.am | 10 + .../src/gb.compress.zlib.component | 5 + gb.compress.zlib/src/main.c | 527 + gb.compress.zlib/src/main.h | 47 + gb.crypt/AUTHORS | 0 gb.crypt/COPYING | 1 + gb.crypt/ChangeLog | 0 gb.crypt/INSTALL | 1 + gb.crypt/Makefile.am | 3 + gb.crypt/NEWS | 0 gb.crypt/README | 0 gb.crypt/acinclude.m4 | 1 + gb.crypt/component.am | 1 + gb.crypt/configure.ac | 27 + gb.crypt/gambas.h | 1 + gb.crypt/gb_common.h | 1 + gb.crypt/m4 | 1 + gb.crypt/reconf | 1 + gb.crypt/src/Makefile.am | 14 + gb.crypt/src/c_crypt.c | 203 + gb.crypt/src/c_crypt.h | 33 + gb.crypt/src/gb.crypt.component | 7 + gb.crypt/src/main.c | 47 + gb.crypt/src/main.h | 34 + gb.db.mysql/AUTHORS | 0 gb.db.mysql/COPYING | 1 + gb.db.mysql/ChangeLog | 0 gb.db.mysql/INSTALL | 1 + gb.db.mysql/Makefile.am | 3 + gb.db.mysql/NEWS | 0 gb.db.mysql/README | 0 gb.db.mysql/acinclude.m4 | 1 + gb.db.mysql/component.am | 1 + gb.db.mysql/configure.ac | 22 + gb.db.mysql/gambas.h | 1 + gb.db.mysql/gb.db.h | 1 + gb.db.mysql/gb.db.proto.h | 1 + gb.db.mysql/gb_common.h | 1 + gb.db.mysql/m4 | 1 + gb.db.mysql/reconf | 1 + gb.db.mysql/src/Makefile.am | 11 + gb.db.mysql/src/gb.db.mysql.component | 5 + gb.db.mysql/src/main.c | 2704 + gb.db.mysql/src/main.h | 38 + gb.db.odbc/AUTHORS | 0 gb.db.odbc/COPYING | 1 + gb.db.odbc/ChangeLog | 37 + gb.db.odbc/INSTALL | 1 + gb.db.odbc/Makefile.am | 3 + gb.db.odbc/NEWS | 0 gb.db.odbc/README | 0 gb.db.odbc/acinclude.m4 | 1 + gb.db.odbc/component.am | 1 + gb.db.odbc/configure.ac | 27 + gb.db.odbc/gambas.h | 1 + gb.db.odbc/gb.db.h | 1 + gb.db.odbc/gb.db.proto.h | 1 + gb.db.odbc/gb_common.h | 1 + gb.db.odbc/m4 | 1 + gb.db.odbc/reconf | 1 + gb.db.odbc/src/Makefile.am | 11 + gb.db.odbc/src/gb.db.odbc.component | 6 + gb.db.odbc/src/main.c | 3380 ++ gb.db.odbc/src/main.h | 38 + gb.db.postgresql/AUTHORS | 0 gb.db.postgresql/COPYING | 1 + gb.db.postgresql/ChangeLog | 0 gb.db.postgresql/INSTALL | 1 + gb.db.postgresql/Makefile.am | 3 + gb.db.postgresql/NEWS | 0 gb.db.postgresql/README | 0 gb.db.postgresql/acinclude.m4 | 1 + gb.db.postgresql/component.am | 1 + gb.db.postgresql/configure.ac | 27 + gb.db.postgresql/gambas.h | 1 + gb.db.postgresql/gb.db.h | 1 + gb.db.postgresql/gb.db.proto.h | 1 + gb.db.postgresql/gb_common.h | 1 + gb.db.postgresql/m4 | 1 + gb.db.postgresql/reconf | 1 + gb.db.postgresql/src/Makefile.am | 11 + .../src/gb.db.postgresql.component | 6 + gb.db.postgresql/src/main.c | 2982 + gb.db.postgresql/src/main.h | 38 + gb.db.sqlite2/AUTHORS | 0 gb.db.sqlite2/COPYING | 1 + gb.db.sqlite2/ChangeLog | 38 + gb.db.sqlite2/INSTALL | 1 + gb.db.sqlite2/Makefile.am | 3 + gb.db.sqlite2/NEWS | 0 gb.db.sqlite2/README | 56 + gb.db.sqlite2/TODO | 15 + gb.db.sqlite2/acinclude.m4 | 1 + gb.db.sqlite2/component.am | 1 + gb.db.sqlite2/configure.ac | 25 + gb.db.sqlite2/gambas.h | 1 + gb.db.sqlite2/gb.db.h | 1 + gb.db.sqlite2/gb.db.proto.h | 1 + gb.db.sqlite2/gb_common.h | 1 + gb.db.sqlite2/m4 | 1 + gb.db.sqlite2/reconf | 1 + gb.db.sqlite2/src/Makefile.am | 13 + gb.db.sqlite2/src/dataset.cpp | 585 + gb.db.sqlite2/src/dataset.h | 420 + gb.db.sqlite2/src/gb.db.sqlite2.component | 6 + gb.db.sqlite2/src/main.cpp | 2528 + gb.db.sqlite2/src/main.h | 44 + gb.db.sqlite2/src/qry_dat.cpp | 469 + gb.db.sqlite2/src/qry_dat.h | 302 + gb.db.sqlite2/src/sqlitedataset.cpp | 783 + gb.db.sqlite2/src/sqlitedataset.h | 208 + gb.db.sqlite2/src/stringhelper.cpp | 95 + gb.db.sqlite2/src/stringhelper.h | 72 + gb.db.sqlite3/AUTHORS | 0 gb.db.sqlite3/COPYING | 1 + gb.db.sqlite3/ChangeLog | 55 + gb.db.sqlite3/INSTALL | 1 + gb.db.sqlite3/Makefile.am | 3 + gb.db.sqlite3/NEWS | 0 gb.db.sqlite3/README | 60 + gb.db.sqlite3/acinclude.m4 | 1 + gb.db.sqlite3/component.am | 1 + gb.db.sqlite3/configure.ac | 19 + gb.db.sqlite3/gambas.h | 1 + gb.db.sqlite3/gb.db.h | 1 + gb.db.sqlite3/gb.db.proto.h | 1 + gb.db.sqlite3/gb_common.h | 1 + gb.db.sqlite3/m4 | 1 + gb.db.sqlite3/reconf | 1 + gb.db.sqlite3/src/Makefile.am | 14 + gb.db.sqlite3/src/README | 56 + gb.db.sqlite3/src/gb.db.sqlite3.component | 6 + gb.db.sqlite3/src/gb_buffer.c | 104 + gb.db.sqlite3/src/gb_buffer.h | 47 + gb.db.sqlite3/src/helper.c | 523 + gb.db.sqlite3/src/helper.h | 68 + gb.db.sqlite3/src/main.c | 2534 + gb.db.sqlite3/src/main.h | 39 + gb.dbus/AUTHORS | 0 gb.dbus/COPYING | 1 + gb.dbus/ChangeLog | 0 gb.dbus/INSTALL | 1 + gb.dbus/Makefile.am | 3 + gb.dbus/NEWS | 0 gb.dbus/README | 0 gb.dbus/acinclude.m4 | 1 + gb.dbus/component.am | 1 + gb.dbus/configure.ac | 15 + gb.dbus/gambas.h | 1 + gb.dbus/gb_common.h | 1 + gb.dbus/m4 | 1 + gb.dbus/reconf | 1 + gb.dbus/src/Makefile.am | 17 + gb.dbus/src/c_dbus.c | 74 + gb.dbus/src/c_dbus.h | 35 + gb.dbus/src/c_dbusconnection.c | 286 + gb.dbus/src/c_dbusconnection.h | 49 + gb.dbus/src/c_dbusobserver.c | 322 + gb.dbus/src/c_dbusobserver.h | 63 + gb.dbus/src/c_dbusvariant.c | 72 + gb.dbus/src/c_dbusvariant.h | 48 + gb.dbus/src/dbus_print_message.c | 435 + gb.dbus/src/dbus_print_message.h | 55 + gb.dbus/src/gb.dbus.component | 4 + gb.dbus/src/gb.dbus/.directory | 2 + gb.dbus/src/gb.dbus/.icon.png | Bin 0 -> 10569 bytes gb.dbus/src/gb.dbus/.project | 9 + gb.dbus/src/gb.dbus/.src/CTest.class | 65 + gb.dbus/src/gb.dbus/.src/CTest2.class | 9 + gb.dbus/src/gb.dbus/.src/CTest3.class | 15 + gb.dbus/src/gb.dbus/.src/DBus.class | 165 + .../src/gb.dbus/.src/DBusApplication.class | 135 + gb.dbus/src/gb.dbus/.src/DBusObject.class | 702 + gb.dbus/src/gb.dbus/.src/DBusProxy.class | 309 + gb.dbus/src/gb.dbus/.src/DBusSignal.class | 33 + gb.dbus/src/gb.dbus/.src/DBusValues.class | 4 + gb.dbus/src/gb.dbus/.src/MMain.module | 154 + gb.dbus/src/gb.dbus/.src/MyObject.class | 32 + gb.dbus/src/gb.dbus/.src/MyValue.class | 5 + gb.dbus/src/gb.dbus/.src/_DBusNull.class | 5 + gb.dbus/src/gb.dbus/.src/mpris.class | 205 + gb.dbus/src/helper.c | 1557 + gb.dbus/src/helper.h | 57 + gb.dbus/src/main.c | 60 + gb.dbus/src/main.h | 39 + gb.desktop.gnome.keyring/AUTHORS | 0 gb.desktop.gnome.keyring/COPYING | 1 + gb.desktop.gnome.keyring/ChangeLog | 0 gb.desktop.gnome.keyring/INSTALL | 1 + gb.desktop.gnome.keyring/Makefile.am | 3 + gb.desktop.gnome.keyring/NEWS | 0 gb.desktop.gnome.keyring/README | 0 gb.desktop.gnome.keyring/acinclude.m4 | 1 + gb.desktop.gnome.keyring/component.am | 1 + gb.desktop.gnome.keyring/configure.ac | 18 + gb.desktop.gnome.keyring/gambas.h | 1 + gb.desktop.gnome.keyring/gb_common.h | 1 + gb.desktop.gnome.keyring/m4 | 1 + gb.desktop.gnome.keyring/reconf | 1 + gb.desktop.gnome.keyring/src/Makefile.am | 11 + .../src/gb.desktop.gnome.keyring.component | 3 + gb.desktop.gnome.keyring/src/keyring.c | 161 + gb.desktop.gnome.keyring/src/keyring.h | 34 + gb.desktop.gnome.keyring/src/main.c | 49 + gb.desktop.gnome.keyring/src/main.h | 33 + gb.desktop.x11/AUTHORS | 0 gb.desktop.x11/COPYING | 1 + gb.desktop.x11/ChangeLog | 0 gb.desktop.x11/INSTALL | 1 + gb.desktop.x11/Makefile.am | 3 + gb.desktop.x11/NEWS | 0 gb.desktop.x11/README | 0 gb.desktop.x11/acinclude.m4 | 1 + gb.desktop.x11/component.am | 1 + gb.desktop.x11/configure.ac | 23 + gb.desktop.x11/gambas.h | 1 + gb.desktop.x11/gb.image.h | 1 + gb.desktop.x11/gb_common.h | 1 + gb.desktop.x11/gb_list.h | 1 + gb.desktop.x11/gb_list_temp.h | 1 + gb.desktop.x11/m4 | 1 + gb.desktop.x11/reconf | 1 + gb.desktop.x11/src/Makefile.am | 38 + gb.desktop.x11/src/c_x11.c | 759 + gb.desktop.x11/src/c_x11.h | 48 + gb.desktop.x11/src/c_x11systray.c | 187 + gb.desktop.x11/src/c_x11systray.h | 38 + gb.desktop.x11/src/gb.desktop.x11.component | 4 + gb.desktop.x11/src/gb_list.c | 24 + gb.desktop.x11/src/main.c | 53 + gb.desktop.x11/src/main.h | 35 + gb.desktop.x11/src/systray/common.h | 82 + gb.desktop.x11/src/systray/debug.c | 150 + gb.desktop.x11/src/systray/debug.h | 85 + gb.desktop.x11/src/systray/embed.c | 357 + gb.desktop.x11/src/systray/embed.h | 43 + gb.desktop.x11/src/systray/icons.c | 242 + gb.desktop.x11/src/systray/icons.h | 117 + gb.desktop.x11/src/systray/kde_tray.c | 161 + gb.desktop.x11/src/systray/kde_tray.h | 34 + gb.desktop.x11/src/systray/list.h | 76 + gb.desktop.x11/src/systray/settings.c | 949 + gb.desktop.x11/src/systray/settings.h | 94 + gb.desktop.x11/src/systray/systray.c | 943 + gb.desktop.x11/src/systray/systray.h | 45 + gb.desktop.x11/src/systray/tray.c | 732 + gb.desktop.x11/src/systray/tray.h | 141 + gb.desktop.x11/src/systray/wmh.c | 232 + gb.desktop.x11/src/systray/wmh.h | 102 + gb.desktop.x11/src/systray/xembed.c | 537 + gb.desktop.x11/src/systray/xembed.h | 49 + gb.desktop.x11/src/systray/xutils.c | 442 + gb.desktop.x11/src/systray/xutils.h | 116 + gb.desktop.x11/src/x11.c | 835 + gb.desktop.x11/src/x11.h | 116 + gb.gmp/AUTHORS | 0 gb.gmp/COPYING | 1 + gb.gmp/ChangeLog | 0 gb.gmp/INSTALL | 1 + gb.gmp/Makefile.am | 3 + gb.gmp/NEWS | 0 gb.gmp/README | 0 gb.gmp/acinclude.m4 | 1 + gb.gmp/component.am | 1 + gb.gmp/configure.ac | 20 + gb.gmp/gambas.h | 1 + gb.gmp/gb_common.h | 1 + gb.gmp/m4 | 1 + gb.gmp/reconf | 1 + gb.gmp/src/Makefile.am | 13 + gb.gmp/src/c_bigint.c | 682 + gb.gmp/src/c_bigint.h | 44 + gb.gmp/src/c_rational.c | 730 + gb.gmp/src/c_rational.h | 42 + gb.gmp/src/gb.gmp.component | 3 + gb.gmp/src/main.c | 84 + gb.gmp/src/main.h | 39 + gb.gsl/AUTHORS | 0 gb.gsl/COPYING | 1 + gb.gsl/ChangeLog | 0 gb.gsl/INSTALL | 1 + gb.gsl/Makefile.am | 3 + gb.gsl/NEWS | 0 gb.gsl/README | 0 gb.gsl/Test/test/.directory | 2 + gb.gsl/Test/test/.icon.png | Bin 0 -> 3682 bytes gb.gsl/Test/test/.project | 9 + gb.gsl/Test/test/.src/MMain.module | 247 + gb.gsl/Test/test/.src/Test.class | 199 + gb.gsl/Test/test/.src/TestComplex.class | 94 + gb.gsl/Test/test/.src/TestSuite.class | 141 + gb.gsl/acinclude.m4 | 1 + gb.gsl/component.am | 1 + gb.gsl/configure.ac | 18 + gb.gsl/gambas.h | 1 + gb.gsl/gb_common.h | 1 + gb.gsl/m4 | 1 + gb.gsl/reconf | 1 + gb.gsl/src/Makefile.am | 16 + gb.gsl/src/c_complex.c | 689 + gb.gsl/src/c_complex.h | 70 + gb.gsl/src/c_gsl.c | 315 + gb.gsl/src/c_gsl.h | 37 + gb.gsl/src/c_matrix.c | 1300 + gb.gsl/src/c_matrix.h | 44 + gb.gsl/src/c_newtonpolynomial.c | 53 + gb.gsl/src/c_newtonpolynomial.h | 48 + gb.gsl/src/c_polynomial.c | 914 + gb.gsl/src/c_polynomial.h | 48 + gb.gsl/src/c_vector.c | 817 + gb.gsl/src/c_vector.h | 52 + gb.gsl/src/gb.gsl.component | 5 + gb.gsl/src/main.c | 88 + gb.gsl/src/main.h | 50 + gb.gtk/AUTHORS | 0 gb.gtk/COPYING | 1 + gb.gtk/ChangeLog | 118 + gb.gtk/INSTALL | 1 + gb.gtk/Makefile.am | 3 + gb.gtk/NEWS | 0 gb.gtk/README | 0 gb.gtk/TODO | 16 + gb.gtk/acinclude.m4 | 1 + gb.gtk/component.am | 1 + gb.gtk/configure.ac | 28 + gb.gtk/gambas.h | 1 + gb.gtk/gb.draw.h | 1 + gb.gtk/gb.geom.h | 1 + gb.gtk/gb.gl.h | 1 + gb.gtk/gb.image.h | 1 + gb.gtk/gb.paint.h | 1 + gb.gtk/gb_common.h | 1 + gb.gtk/m4 | 1 + gb.gtk/reconf | 1 + gb.gtk/share | 1 + gb.gtk/src/CButton.cpp | 309 + gb.gtk/src/CButton.h | 53 + gb.gtk/src/CClipboard.cpp | 589 + gb.gtk/src/CClipboard.h | 36 + gb.gtk/src/CColor.cpp | 139 + gb.gtk/src/CColor.h | 33 + gb.gtk/src/CConst.cpp | 133 + gb.gtk/src/CConst.h | 42 + gb.gtk/src/CContainer.cpp | 616 + gb.gtk/src/CContainer.h | 92 + gb.gtk/src/CDialog.cpp | 294 + gb.gtk/src/CDialog.h | 34 + gb.gtk/src/CDraw.cpp | 39 + gb.gtk/src/CDraw.h | 39 + gb.gtk/src/CDrawingArea.cpp | 258 + gb.gtk/src/CDrawingArea.h | 53 + gb.gtk/src/CFont.cpp | 421 + gb.gtk/src/CFont.h | 57 + gb.gtk/src/CFrame.cpp | 211 + gb.gtk/src/CFrame.h | 55 + gb.gtk/src/CImage.cpp | 285 + gb.gtk/src/CImage.h | 55 + gb.gtk/src/CKey.cpp | 184 + gb.gtk/src/CKey.h | 34 + gb.gtk/src/CLabel.cpp | 166 + gb.gtk/src/CLabel.h | 49 + gb.gtk/src/CMenu.cpp | 600 + gb.gtk/src/CMenu.h | 56 + gb.gtk/src/CMessage.cpp | 136 + gb.gtk/src/CMessage.h | 33 + gb.gtk/src/CMouse.cpp | 432 + gb.gtk/src/CMouse.h | 54 + gb.gtk/src/CMovieBox.cpp | 135 + gb.gtk/src/CMovieBox.h | 50 + gb.gtk/src/CPicture.cpp | 276 + gb.gtk/src/CPicture.h | 57 + gb.gtk/src/CScreen.cpp | 480 + gb.gtk/src/CScreen.h | 54 + gb.gtk/src/CSeparator.cpp | 51 + gb.gtk/src/CSeparator.h | 47 + gb.gtk/src/CSlider.cpp | 161 + gb.gtk/src/CSlider.h | 59 + gb.gtk/src/CStock.cpp | 52 + gb.gtk/src/CStock.h | 35 + gb.gtk/src/CStyle.cpp | 894 + gb.gtk/src/CStyle.h | 35 + gb.gtk/src/CTabStrip.cpp | 386 + gb.gtk/src/CTabStrip.h | 53 + gb.gtk/src/CTextArea.cpp | 501 + gb.gtk/src/CTextArea.h | 52 + gb.gtk/src/CTextBox.cpp | 539 + gb.gtk/src/CTextBox.h | 62 + gb.gtk/src/CTrayIcon.cpp | 336 + gb.gtk/src/CTrayIcon.h | 52 + gb.gtk/src/CWatcher.cpp | 141 + gb.gtk/src/CWatcher.h | 44 + gb.gtk/src/CWidget.cpp | 1055 + gb.gtk/src/CWidget.h | 88 + gb.gtk/src/CWindow.cpp | 950 + gb.gtk/src/CWindow.h | 69 + gb.gtk/src/Makefile.am | 101 + gb.gtk/src/cpaint_impl.cpp | 1668 + gb.gtk/src/cpaint_impl.h | 49 + gb.gtk/src/cprinter.cpp | 378 + gb.gtk/src/cprinter.h | 52 + gb.gtk/src/csvgimage.cpp | 266 + gb.gtk/src/csvgimage.h | 62 + gb.gtk/src/desktop.c | 1 + gb.gtk/src/desktop.h | 1 + gb.gtk/src/font-parser.cpp | 175 + gb.gtk/src/font-parser.h | 32 + gb.gtk/src/gapplication.cpp | 1552 + gb.gtk/src/gapplication.h | 135 + gb.gtk/src/gb.gtk.component | 6 + gb.gtk/src/gb.gtk.h | 64 + gb.gtk/src/gbutton.cpp | 803 + gb.gtk/src/gbutton.h | 100 + gb.gtk/src/gclipboard.h | 55 + gb.gtk/src/gcolor.h | 32 + gb.gtk/src/gcombobox.cpp | 645 + gb.gtk/src/gcombobox.h | 94 + gb.gtk/src/gcontainer.cpp | 842 + gb.gtk/src/gcontainer.h | 147 + gb.gtk/src/gcontrol.cpp | 2794 + gb.gtk/src/gcontrol.h | 320 + gb.gtk/src/gcursor.cpp | 89 + gb.gtk/src/gcursor.h | 49 + gb.gtk/src/gdesktop.cpp | 402 + gb.gtk/src/gdesktop.h | 78 + gb.gtk/src/gdialog.h | 59 + gb.gtk/src/gdrag.cpp | 897 + gb.gtk/src/gdrag.h | 116 + gb.gtk/src/gdrawingarea.cpp | 468 + gb.gtk/src/gdrawingarea.h | 86 + gb.gtk/src/gfont.cpp | 724 + gb.gtk/src/gfont.h | 107 + gb.gtk/src/gframe.cpp | 255 + gb.gtk/src/gframe.h | 74 + gb.gtk/src/ggambastag.h | 38 + gb.gtk/src/gglarea.cpp | 38 + gb.gtk/src/gglarea.h | 33 + gb.gtk/src/gkey.cpp | 566 + gb.gtk/src/gkey.h | 70 + gb.gtk/src/glabel.cpp | 381 + gb.gtk/src/glabel.h | 74 + gb.gtk/src/gmainwindow.cpp | 1732 + gb.gtk/src/gmainwindow.h | 206 + gb.gtk/src/gmenu.cpp | 1169 + gb.gtk/src/gmenu.h | 150 + gb.gtk/src/gmessage.cpp | 762 + gb.gtk/src/gmessage.h | 42 + gb.gtk/src/gmouse.cpp | 338 + gb.gtk/src/gmouse.h | 92 + gb.gtk/src/gmoviebox.cpp | 163 + gb.gtk/src/gmoviebox.h | 53 + gb.gtk/src/gpicture.cpp | 1230 + gb.gtk/src/gpicture.h | 134 + gb.gtk/src/gplugin.h | 45 + gb.gtk/src/gprinter.cpp | 735 + gb.gtk/src/gprinter.h | 116 + gb.gtk/src/gscrollbar.h | 36 + gb.gtk/src/gseparator.cpp | 103 + gb.gtk/src/gseparator.h | 33 + gb.gtk/src/gshare.h | 92 + gb.gtk/src/gsignals.cpp | 355 + gb.gtk/src/gslider.cpp | 332 + gb.gtk/src/gslider.h | 75 + gb.gtk/src/gtabstrip.cpp | 856 + gb.gtk/src/gtabstrip.h | 93 + gb.gtk/src/gtag.h | 42 + gb.gtk/src/gtextarea.cpp | 1081 + gb.gtk/src/gtextarea.h | 122 + gb.gtk/src/gtextbox.cpp | 517 + gb.gtk/src/gtextbox.h | 86 + gb.gtk/src/gtools.cpp | 2331 + gb.gtk/src/gtools.h | 220 + gb.gtk/src/gtrayicon.cpp | 289 + gb.gtk/src/gtrayicon.h | 84 + gb.gtk/src/gtree.cpp | 1291 + gb.gtk/src/gtree.h | 190 + gb.gtk/src/kentities.h | 860 + gb.gtk/src/main.cpp | 595 + gb.gtk/src/main.h | 60 + gb.gtk/src/opengl/Makefile.am | 13 + gb.gtk/src/opengl/c_glarea.c | 185 + gb.gtk/src/opengl/c_glarea.h | 48 + gb.gtk/src/opengl/gb.gtk.opengl.component | 6 + gb.gtk/src/opengl/main.c | 61 + gb.gtk/src/opengl/main.h | 39 + gb.gtk/src/sm/bonobo-macros.h | 100 + gb.gtk/src/sm/gnome-client.c | 3083 + gb.gtk/src/sm/gnome-client.h | 487 + gb.gtk/src/sm/gnome-ice.c | 153 + gb.gtk/src/sm/gnome-ice.h | 44 + gb.gtk/src/sm/gnome-macros.h | 64 + gb.gtk/src/sm/gnome-marshal.c | 369 + gb.gtk/src/sm/gnome-marshal.h | 79 + gb.gtk/src/sm/gnome-uidefs.h | 122 + gb.gtk/src/sm/gnometypebuiltins.c | 459 + gb.gtk/src/sm/gnometypebuiltins.h | 76 + gb.gtk/src/sm/libgnomeui.h | 88 + gb.gtk/src/sm/libgnomeuiP.h | 56 + gb.gtk/src/sm/sm.h | 38 + gb.gtk/src/watcher.cpp | 150 + gb.gtk/src/watcher.h | 58 + gb.gtk/src/widgets.h | 164 + gb.gtk/src/x11.c | 1 + gb.gtk/src/x11.h | 1 + gb.gtk3/AUTHORS | 0 gb.gtk3/COPYING | 1 + gb.gtk3/ChangeLog | 0 gb.gtk3/INSTALL | 1 + gb.gtk3/Makefile.am | 3 + gb.gtk3/NEWS | 0 gb.gtk3/README | 0 gb.gtk3/TODO | 0 gb.gtk3/acinclude.m4 | 1 + gb.gtk3/component.am | 1 + gb.gtk3/configure.ac | 22 + gb.gtk3/gambas.h | 1 + gb.gtk3/gb.draw.h | 1 + gb.gtk3/gb.geom.h | 1 + gb.gtk3/gb.gl.h | 1 + gb.gtk3/gb.image.h | 1 + gb.gtk3/gb.paint.h | 1 + gb.gtk3/gb_common.h | 1 + gb.gtk3/m4 | 1 + gb.gtk3/reconf | 1 + gb.gtk3/share | 1 + gb.gtk3/src/CButton.cpp | 1 + gb.gtk3/src/CButton.h | 1 + gb.gtk3/src/CClipboard.cpp | 1 + gb.gtk3/src/CClipboard.h | 1 + gb.gtk3/src/CColor.cpp | 1 + gb.gtk3/src/CColor.h | 1 + gb.gtk3/src/CConst.cpp | 1 + gb.gtk3/src/CConst.h | 1 + gb.gtk3/src/CContainer.cpp | 1 + gb.gtk3/src/CContainer.h | 1 + gb.gtk3/src/CDialog.cpp | 1 + gb.gtk3/src/CDialog.h | 1 + gb.gtk3/src/CDraw.cpp | 1 + gb.gtk3/src/CDraw.h | 1 + gb.gtk3/src/CDrawingArea.cpp | 1 + gb.gtk3/src/CDrawingArea.h | 1 + gb.gtk3/src/CFont.cpp | 1 + gb.gtk3/src/CFont.h | 1 + gb.gtk3/src/CFrame.cpp | 1 + gb.gtk3/src/CFrame.h | 1 + gb.gtk3/src/CImage.cpp | 1 + gb.gtk3/src/CImage.h | 1 + gb.gtk3/src/CKey.cpp | 1 + gb.gtk3/src/CKey.h | 1 + gb.gtk3/src/CLabel.cpp | 1 + gb.gtk3/src/CLabel.h | 1 + gb.gtk3/src/CMenu.cpp | 1 + gb.gtk3/src/CMenu.h | 1 + gb.gtk3/src/CMessage.cpp | 1 + gb.gtk3/src/CMessage.h | 1 + gb.gtk3/src/CMouse.cpp | 1 + gb.gtk3/src/CMouse.h | 1 + gb.gtk3/src/CMovieBox.cpp | 1 + gb.gtk3/src/CMovieBox.h | 1 + gb.gtk3/src/CPicture.cpp | 1 + gb.gtk3/src/CPicture.h | 1 + gb.gtk3/src/CScreen.cpp | 1 + gb.gtk3/src/CScreen.h | 1 + gb.gtk3/src/CSeparator.cpp | 1 + gb.gtk3/src/CSeparator.h | 1 + gb.gtk3/src/CSlider.cpp | 1 + gb.gtk3/src/CSlider.h | 1 + gb.gtk3/src/CStock.cpp | 1 + gb.gtk3/src/CStock.h | 1 + gb.gtk3/src/CStyle.cpp | 1 + gb.gtk3/src/CStyle.h | 1 + gb.gtk3/src/CTabStrip.cpp | 1 + gb.gtk3/src/CTabStrip.h | 1 + gb.gtk3/src/CTextArea.cpp | 1 + gb.gtk3/src/CTextArea.h | 1 + gb.gtk3/src/CTextBox.cpp | 1 + gb.gtk3/src/CTextBox.h | 1 + gb.gtk3/src/CTrayIcon.cpp | 1 + gb.gtk3/src/CTrayIcon.h | 1 + gb.gtk3/src/CWatcher.cpp | 1 + gb.gtk3/src/CWatcher.h | 1 + gb.gtk3/src/CWidget.cpp | 1 + gb.gtk3/src/CWidget.h | 1 + gb.gtk3/src/CWindow.cpp | 1 + gb.gtk3/src/CWindow.h | 1 + gb.gtk3/src/Makefile.am | 101 + gb.gtk3/src/cpaint_impl.cpp | 1 + gb.gtk3/src/cpaint_impl.h | 1 + gb.gtk3/src/cprinter.cpp | 1 + gb.gtk3/src/cprinter.h | 1 + gb.gtk3/src/csvgimage.cpp | 1 + gb.gtk3/src/csvgimage.h | 1 + gb.gtk3/src/desktop.c | 1 + gb.gtk3/src/desktop.h | 1 + gb.gtk3/src/font-parser.cpp | 1 + gb.gtk3/src/font-parser.h | 1 + gb.gtk3/src/gapplication.cpp | 1 + gb.gtk3/src/gapplication.h | 1 + gb.gtk3/src/gb.gtk.h | 1 + gb.gtk3/src/gb.gtk3.component | 7 + gb.gtk3/src/gbutton.cpp | 1 + gb.gtk3/src/gbutton.h | 1 + gb.gtk3/src/gclipboard.h | 1 + gb.gtk3/src/gcolor.h | 1 + gb.gtk3/src/gcombobox.cpp | 1 + gb.gtk3/src/gcombobox.h | 1 + gb.gtk3/src/gcontainer.cpp | 1 + gb.gtk3/src/gcontainer.h | 1 + gb.gtk3/src/gcontrol.cpp | 1 + gb.gtk3/src/gcontrol.h | 1 + gb.gtk3/src/gcursor.cpp | 1 + gb.gtk3/src/gcursor.h | 1 + gb.gtk3/src/gdesktop.cpp | 1 + gb.gtk3/src/gdesktop.h | 1 + gb.gtk3/src/gdialog.h | 1 + gb.gtk3/src/gdrag.cpp | 1 + gb.gtk3/src/gdrag.h | 1 + gb.gtk3/src/gdrawingarea.cpp | 1 + gb.gtk3/src/gdrawingarea.h | 1 + gb.gtk3/src/gfont.cpp | 1 + gb.gtk3/src/gfont.h | 1 + gb.gtk3/src/gframe.cpp | 1 + gb.gtk3/src/gframe.h | 1 + gb.gtk3/src/ggambastag.h | 1 + gb.gtk3/src/gglarea.cpp | 1 + gb.gtk3/src/gglarea.h | 1 + gb.gtk3/src/gkey.cpp | 1 + gb.gtk3/src/gkey.h | 1 + gb.gtk3/src/glabel.cpp | 1 + gb.gtk3/src/glabel.h | 1 + gb.gtk3/src/gmainwindow.cpp | 1 + gb.gtk3/src/gmainwindow.h | 1 + gb.gtk3/src/gmenu.cpp | 1 + gb.gtk3/src/gmenu.h | 1 + gb.gtk3/src/gmessage.cpp | 1 + gb.gtk3/src/gmessage.h | 1 + gb.gtk3/src/gmouse.cpp | 1 + gb.gtk3/src/gmouse.h | 1 + gb.gtk3/src/gmoviebox.cpp | 1 + gb.gtk3/src/gmoviebox.h | 1 + gb.gtk3/src/gpicture.cpp | 1 + gb.gtk3/src/gpicture.h | 1 + gb.gtk3/src/gplugin.h | 1 + gb.gtk3/src/gprinter.cpp | 1 + gb.gtk3/src/gprinter.h | 1 + gb.gtk3/src/gscrollbar.h | 1 + gb.gtk3/src/gseparator.cpp | 1 + gb.gtk3/src/gseparator.h | 1 + gb.gtk3/src/gshare.h | 1 + gb.gtk3/src/gsignals.cpp | 1 + gb.gtk3/src/gslider.cpp | 1 + gb.gtk3/src/gslider.h | 1 + gb.gtk3/src/gtabstrip.cpp | 1 + gb.gtk3/src/gtabstrip.h | 1 + gb.gtk3/src/gtag.h | 1 + gb.gtk3/src/gtextarea.cpp | 1 + gb.gtk3/src/gtextarea.h | 1 + gb.gtk3/src/gtextbox.cpp | 1 + gb.gtk3/src/gtextbox.h | 1 + gb.gtk3/src/gtools.cpp | 1 + gb.gtk3/src/gtools.h | 1 + gb.gtk3/src/gtrayicon.cpp | 1 + gb.gtk3/src/gtrayicon.h | 1 + gb.gtk3/src/gtree.cpp | 1 + gb.gtk3/src/gtree.h | 1 + gb.gtk3/src/kentities.h | 1 + gb.gtk3/src/main.cpp | 1 + gb.gtk3/src/main.h | 1 + gb.gtk3/src/sm/bonobo-macros.h | 1 + gb.gtk3/src/sm/gnome-client.c | 1 + gb.gtk3/src/sm/gnome-client.h | 1 + gb.gtk3/src/sm/gnome-ice.c | 1 + gb.gtk3/src/sm/gnome-ice.h | 1 + gb.gtk3/src/sm/gnome-macros.h | 1 + gb.gtk3/src/sm/gnome-marshal.c | 1 + gb.gtk3/src/sm/gnome-marshal.h | 1 + gb.gtk3/src/sm/gnome-uidefs.h | 1 + gb.gtk3/src/sm/gnometypebuiltins.c | 1 + gb.gtk3/src/sm/gnometypebuiltins.h | 1 + gb.gtk3/src/sm/libgnomeui.h | 1 + gb.gtk3/src/sm/libgnomeuiP.h | 1 + gb.gtk3/src/sm/sm.h | 1 + gb.gtk3/src/watcher.cpp | 1 + gb.gtk3/src/watcher.h | 1 + gb.gtk3/src/widgets.h | 1 + gb.gtk3/src/x11.c | 1 + gb.gtk3/src/x11.h | 1 + gb.httpd/AUTHORS | 0 gb.httpd/COPYING | 1 + gb.httpd/ChangeLog | 0 gb.httpd/INSTALL | 1 + gb.httpd/Makefile.am | 3 + gb.httpd/NEWS | 0 gb.httpd/README | 0 gb.httpd/acinclude.m4 | 1 + gb.httpd/component.am | 1 + gb.httpd/configure.ac | 150 + gb.httpd/gambas.h | 1 + gb.httpd/gb_common.h | 1 + gb.httpd/m4 | 1 + gb.httpd/reconf | 1 + gb.httpd/src/Makefile.am | 20 + gb.httpd/src/fdwatch.c | 840 + gb.httpd/src/fdwatch.h | 85 + gb.httpd/src/gb.httpd.component | 3 + gb.httpd/src/libhttpd.c | 4376 ++ gb.httpd/src/libhttpd.h | 298 + gb.httpd/src/main.c | 90 + gb.httpd/src/main.h | 52 + gb.httpd/src/match.c | 86 + gb.httpd/src/match.h | 36 + gb.httpd/src/mime_encodings.h | 8 + gb.httpd/src/mime_types.h | 569 + gb.httpd/src/strerror.c | 37 + gb.httpd/src/tdate_parse.c | 312 + gb.httpd/src/tdate_parse.h | 33 + gb.httpd/src/thttpd.c | 2277 + gb.httpd/src/thttpd.h | 405 + gb.httpd/src/timers.c | 334 + gb.httpd/src/timers.h | 110 + gb.httpd/src/version.h | 9 + gb.image.imlib/AUTHORS | 0 gb.image.imlib/COPYING | 1 + gb.image.imlib/ChangeLog | 0 gb.image.imlib/INSTALL | 1 + gb.image.imlib/Makefile.am | 3 + gb.image.imlib/NEWS | 0 gb.image.imlib/README | 0 gb.image.imlib/acinclude.m4 | 1 + gb.image.imlib/component.am | 1 + gb.image.imlib/configure.ac | 17 + gb.image.imlib/gambas.h | 1 + gb.image.imlib/gb.draw.h | 1 + gb.image.imlib/gb.image.h | 1 + gb.image.imlib/gb_common.h | 1 + gb.image.imlib/m4 | 1 + gb.image.imlib/reconf | 1 + gb.image.imlib/src/Makefile.am | 10 + gb.image.imlib/src/c_image.c | 281 + gb.image.imlib/src/c_image.h | 47 + gb.image.imlib/src/c_imlib.c | 129 + gb.image.imlib/src/c_imlib.h | 33 + gb.image.imlib/src/gb.image.imlib.component | 5 + gb.image.imlib/src/main.c | 49 + gb.image.imlib/src/main.h | 38 + gb.image.io/AUTHORS | 0 gb.image.io/COPYING | 1 + gb.image.io/ChangeLog | 0 gb.image.io/INSTALL | 1 + gb.image.io/Makefile.am | 3 + gb.image.io/NEWS | 0 gb.image.io/README | 0 gb.image.io/acinclude.m4 | 1 + gb.image.io/component.am | 1 + gb.image.io/configure.ac | 17 + gb.image.io/gambas.h | 1 + gb.image.io/gb.image.h | 1 + gb.image.io/gb_common.h | 1 + gb.image.io/m4 | 1 + gb.image.io/reconf | 1 + gb.image.io/src/Makefile.am | 10 + gb.image.io/src/c_image.c | 311 + gb.image.io/src/c_image.h | 44 + gb.image.io/src/gb.image.io.component | 6 + gb.image.io/src/main.c | 51 + gb.image.io/src/main.h | 38 + gb.jit.llvm/AUTHORS | 1 + gb.jit.llvm/COPYING | 1 + gb.jit.llvm/ChangeLog | 0 gb.jit.llvm/INSTALL | 1 + gb.jit.llvm/Makefile.am | 3 + gb.jit.llvm/NEWS | 0 gb.jit.llvm/README | 17 + gb.jit.llvm/acinclude.m4 | 1 + gb.jit.llvm/component.am | 1 + gb.jit.llvm/configure.ac | 53 + gb.jit.llvm/gambas.h | 1 + gb.jit.llvm/gb_common.h | 1 + gb.jit.llvm/m4 | 1 + gb.jit.llvm/reconf | 1 + gb.jit.llvm/src/Makefile.am | 34 + gb.jit.llvm/src/gb.jit.h | 218 + gb.jit.llvm/src/gb.jit.llvm.component | 5 + gb.jit.llvm/src/jit.h | 1309 + gb.jit.llvm/src/jit_api.cpp | 79 + gb.jit.llvm/src/jit_codegen.cpp | 7001 +++ gb.jit.llvm/src/jit_codegen_conv.h | 786 + gb.jit.llvm/src/jit_compile.cpp | 139 + gb.jit.llvm/src/jit_conv.cpp | 280 + gb.jit.llvm/src/jit_expressions.cpp | 1872 + gb.jit.llvm/src/jit_gambas_pass.cpp | 139 + gb.jit.llvm/src/jit_gambas_pass.h | 31 + gb.jit.llvm/src/jit_read.cpp | 1100 + gb.jit.llvm/src/jit_runtime.c | 1090 + gb.jit.llvm/src/jit_runtime.h | 64 + gb.jit.llvm/src/main.cpp | 78 + gb.jit.llvm/src/main.h | 257 + gb.libxml/AUTHORS | 0 gb.libxml/COPYING | 1 + gb.libxml/ChangeLog | 0 gb.libxml/INSTALL | 1 + gb.libxml/Makefile.am | 3 + gb.libxml/NEWS | 0 gb.libxml/README | 0 gb.libxml/acinclude.m4 | 1 + gb.libxml/component.am | 1 + gb.libxml/configure.ac | 19 + gb.libxml/gambas.h | 1 + gb.libxml/m4 | 1 + gb.libxml/reconf | 1 + gb.libxml/src/CXMLDocument.c | 174 + gb.libxml/src/CXMLDocument.h | 54 + gb.libxml/src/CXMLNode.c | 300 + gb.libxml/src/CXMLNode.h | 52 + gb.libxml/src/CXMLReader.c | 517 + gb.libxml/src/CXMLReader.h | 54 + gb.libxml/src/CXMLWriter.c | 429 + gb.libxml/src/CXMLWriter.h | 52 + gb.libxml/src/Makefile.am | 15 + gb.libxml/src/gb.libxml.component | 7 + gb.libxml/src/libxml.kateproject | 7 + gb.libxml/src/main.c | 73 + gb.libxml/src/main.h | 35 + gb.media/AUTHORS | 0 gb.media/COPYING | 1 + gb.media/ChangeLog | 0 gb.media/INSTALL | 1 + gb.media/Makefile.am | 3 + gb.media/NEWS | 0 gb.media/README | 0 gb.media/acinclude.m4 | 1 + gb.media/component.am | 1 + gb.media/configure.ac | 16 + gb.media/gambas.h | 1 + gb.media/gb.image.h | 1 + gb.media/gb_common.h | 1 + gb.media/m4 | 1 + gb.media/reconf | 1 + gb.media/src/Makefile.am | 13 + gb.media/src/c_media.c | 2079 + gb.media/src/c_media.h | 136 + gb.media/src/c_mediaplayer.c | 658 + gb.media/src/c_mediaplayer.h | 62 + gb.media/src/c_mediavideo.c | 73 + gb.media/src/c_mediavideo.h | 50 + gb.media/src/gb.media.component | 4 + gb.media/src/main.c | 91 + gb.media/src/main.h | 45 + gb.mime/AUTHORS | 0 gb.mime/COPYING | 1 + gb.mime/ChangeLog | 0 gb.mime/INSTALL | 1 + gb.mime/Makefile.am | 3 + gb.mime/NEWS | 0 gb.mime/README | 0 gb.mime/acinclude.m4 | 1 + gb.mime/component.am | 1 + gb.mime/configure.ac | 57 + gb.mime/gambas.h | 1 + gb.mime/gb_common.h | 1 + gb.mime/m4 | 1 + gb.mime/reconf | 1 + gb.mime/src/Makefile.am | 13 + gb.mime/src/c_mime.c | 108 + gb.mime/src/c_mime.h | 35 + gb.mime/src/c_mimemessage.c | 304 + gb.mime/src/c_mimemessage.h | 48 + gb.mime/src/c_mimepart.c | 459 + gb.mime/src/c_mimepart.h | 45 + gb.mime/src/gb.mime.component | 3 + gb.mime/src/main.c | 61 + gb.mime/src/main.h | 46 + gb.ncurses/AUTHORS | 0 gb.ncurses/COPYING | 1 + gb.ncurses/ChangeLog | 0 gb.ncurses/INSTALL | 1 + gb.ncurses/Makefile.am | 3 + gb.ncurses/NEWS | 0 gb.ncurses/README | 0 gb.ncurses/acinclude.m4 | 1 + gb.ncurses/component.am | 1 + gb.ncurses/configure.ac | 19 + gb.ncurses/gambas.h | 1 + gb.ncurses/gb_common.h | 1 + gb.ncurses/m4 | 1 + gb.ncurses/reconf | 1 + gb.ncurses/src/Makefile.am | 17 + gb.ncurses/src/c_color.c | 279 + gb.ncurses/src/c_color.h | 13 + gb.ncurses/src/c_input.c | 632 + gb.ncurses/src/c_input.h | 63 + gb.ncurses/src/c_key.c | 76 + gb.ncurses/src/c_key.h | 29 + gb.ncurses/src/c_screen.c | 272 + gb.ncurses/src/c_screen.h | 49 + gb.ncurses/src/c_window.c | 944 + gb.ncurses/src/c_window.h | 63 + gb.ncurses/src/gb.ncurses.component | 3 + gb.ncurses/src/main.c | 105 + gb.ncurses/src/main.h | 37 + gb.net.curl/AUTHORS | 0 gb.net.curl/COPYING | 1 + gb.net.curl/ChangeLog | 0 gb.net.curl/INSTALL | 1 + gb.net.curl/Makefile.am | 3 + gb.net.curl/NEWS | 0 gb.net.curl/README | 0 gb.net.curl/acinclude.m4 | 1 + gb.net.curl/component.am | 1 + gb.net.curl/configure.ac | 20 + gb.net.curl/gambas.h | 1 + gb.net.curl/gb_common.h | 1 + gb.net.curl/m4 | 1 + gb.net.curl/reconf | 1 + gb.net.curl/src/CCurl.c | 776 + gb.net.curl/src/CCurl.h | 128 + gb.net.curl/src/CFtpClient.c | 333 + gb.net.curl/src/CFtpClient.h | 56 + gb.net.curl/src/CHttpClient.c | 701 + gb.net.curl/src/CHttpClient.h | 77 + gb.net.curl/src/CNet.c | 201 + gb.net.curl/src/CNet.h | 47 + gb.net.curl/src/CProxy.c | 135 + gb.net.curl/src/CProxy.h | 43 + gb.net.curl/src/Makefile.am | 21 + gb.net.curl/src/gb.net.curl.component | 12 + gb.net.curl/src/gb.net.curl/.directory | 2 + gb.net.curl/src/gb.net.curl/.icon.png | Bin 0 -> 10569 bytes gb.net.curl/src/gb.net.curl/.project | 10 + .../src/gb.net.curl/.src/Download.class | 96 + .../gb.net.curl/.src/DownloadManager.class | 360 + .../src/gb.net.curl/.src/HttpForm.class | 124 + gb.net.curl/src/gb.net.curl/.src/MMain.module | 42 + gb.net.curl/src/gbcurl.c | 372 + gb.net.curl/src/gbcurl.h | 71 + gb.net.curl/src/main.c | 60 + gb.net.curl/src/main.h | 34 + gb.net/AUTHORS | 0 gb.net/COPYING | 1 + gb.net/ChangeLog | 241 + gb.net/INSTALL | 1 + gb.net/Makefile.am | 3 + gb.net/NEWS | 0 gb.net/README | 0 gb.net/acinclude.m4 | 1 + gb.net/component.am | 1 + gb.net/configure.ac | 19 + gb.net/gambas.h | 1 + gb.net/gb_common.h | 1 + gb.net/m4 | 1 + gb.net/reconf | 1 + gb.net/src/CDnsClient.c | 720 + gb.net/src/CDnsClient.h | 68 + gb.net/src/CNet.c | 160 + gb.net/src/CNet.h | 85 + gb.net/src/CSerialPort.c | 752 + gb.net/src/CSerialPort.h | 82 + gb.net/src/CServerSocket.c | 629 + gb.net/src/CServerSocket.h | 69 + gb.net/src/CSocket.c | 937 + gb.net/src/CSocket.h | 112 + gb.net/src/CUdpSocket.c | 647 + gb.net/src/CUdpSocket.h | 72 + gb.net/src/Makefile.am | 16 + gb.net/src/doc/README | 146 + gb.net/src/doc/changes.txt | 87 + gb.net/src/doc/threading.sxw | Bin 0 -> 8432 bytes gb.net/src/gb.net.component | 12 + gb.net/src/gb_network.h | 37 + gb.net/src/main.c | 67 + gb.net/src/main.h | 34 + gb.net/src/tools.c | 427 + gb.net/src/tools.h | 66 + gb.openal/AUTHORS | 0 gb.openal/COPYING | 1 + gb.openal/ChangeLog | 0 gb.openal/INSTALL | 1 + gb.openal/Makefile.am | 3 + gb.openal/NEWS | 0 gb.openal/README | 0 gb.openal/acinclude.m4 | 1 + gb.openal/component.am | 1 + gb.openal/configure.ac | 17 + gb.openal/gambas.h | 1 + gb.openal/gb_common.h | 1 + gb.openal/m4 | 1 + gb.openal/reconf | 1 + gb.openal/src/Makefile.am | 10 + gb.openal/src/c_al.c | 673 + gb.openal/src/c_al.h | 33 + gb.openal/src/c_alc.c | 509 + gb.openal/src/c_alc.h | 52 + gb.openal/src/c_alure.c | 411 + gb.openal/src/c_alure.h | 45 + gb.openal/src/gb.openal.component | 3 + gb.openal/src/main.c | 55 + gb.openal/src/main.h | 45 + gb.opengl/AUTHORS | 0 gb.opengl/COPYING | 1 + gb.opengl/ChangeLog | 0 gb.opengl/INSTALL | 1 + gb.opengl/Makefile.am | 3 + gb.opengl/NEWS | 0 gb.opengl/README | 7 + gb.opengl/acinclude.m4 | 1 + gb.opengl/component.am | 1 + gb.opengl/configure.ac | 34 + gb.opengl/gambas.h | 1 + gb.opengl/gb.image.h | 1 + gb.opengl/gb_common.h | 1 + gb.opengl/m4 | 1 + gb.opengl/reconf | 1 + gb.opengl/src/GL.c | 1069 + gb.opengl/src/GL.h | 35 + gb.opengl/src/GLclipping.c | 58 + gb.opengl/src/GLclipping.h | 32 + gb.opengl/src/GLcolorLighting.c | 421 + gb.opengl/src/GLcolorLighting.h | 61 + gb.opengl/src/GLcoordTransf.c | 122 + gb.opengl/src/GLcoordTransf.h | 43 + gb.opengl/src/GLdisplayList.c | 81 + gb.opengl/src/GLdisplayList.h | 38 + gb.opengl/src/GLeval.c | 115 + gb.opengl/src/GLeval.h | 45 + gb.opengl/src/GLfog.c | 68 + gb.opengl/src/GLfog.h | 34 + gb.opengl/src/GLframeBufferOps.c | 142 + gb.opengl/src/GLframeBufferOps.h | 49 + gb.opengl/src/GLinfo.c | 501 + gb.opengl/src/GLinfo.h | 34 + gb.opengl/src/GLmodesExec.c | 64 + gb.opengl/src/GLmodesExec.h | 36 + gb.opengl/src/GLpixelOperations.c | 75 + gb.opengl/src/GLpixelOperations.h | 37 + gb.opengl/src/GLprimitives.c | 178 + gb.opengl/src/GLprimitives.h | 43 + gb.opengl/src/GLrasterization.c | 214 + gb.opengl/src/GLrasterization.h | 49 + gb.opengl/src/GLselectFeedback.c | 150 + gb.opengl/src/GLselectFeedback.h | 38 + gb.opengl/src/GLtextureMapping.c | 334 + gb.opengl/src/GLtextureMapping.h | 59 + gb.opengl/src/Makefile.am | 29 + gb.opengl/src/framebufferobject.c | 194 + gb.opengl/src/framebufferobject.h | 46 + gb.opengl/src/gb.gl.h | 42 + gb.opengl/src/gb.opengl.component | 6 + gb.opengl/src/glsl/GL.c | 114 + gb.opengl/src/glsl/GL.h | 35 + gb.opengl/src/glsl/GLattributes.c | 175 + gb.opengl/src/glsl/GLattributes.h | 38 + gb.opengl/src/glsl/GLinfo.h | 31 + gb.opengl/src/glsl/GLprogram.c | 98 + gb.opengl/src/glsl/GLprogram.h | 38 + gb.opengl/src/glsl/GLshader.c | 154 + gb.opengl/src/glsl/GLshader.h | 41 + gb.opengl/src/glsl/GLuniform.c | 519 + gb.opengl/src/glsl/GLuniform.h | 56 + gb.opengl/src/glsl/Makefile.am | 17 + gb.opengl/src/glsl/gb.opengl.glsl.component | 6 + gb.opengl/src/glsl/main.c | 48 + gb.opengl/src/glsl/main.h | 39 + gb.opengl/src/glu/GLU.c | 151 + gb.opengl/src/glu/GLU.h | 33 + gb.opengl/src/glu/GLUcoordTransf.c | 61 + gb.opengl/src/glu/GLUcoordTransf.h | 34 + gb.opengl/src/glu/GLUnurb.c | 153 + gb.opengl/src/glu/GLUnurb.h | 47 + gb.opengl/src/glu/GLUproject.c | 138 + gb.opengl/src/glu/GLUproject.h | 34 + gb.opengl/src/glu/GLUquadratic.c | 112 + gb.opengl/src/glu/GLUquadratic.h | 40 + gb.opengl/src/glu/GLUtextureImage.c | 53 + gb.opengl/src/glu/GLUtextureImage.h | 33 + gb.opengl/src/glu/Makefile.am | 20 + gb.opengl/src/glu/cglunurb.c | 74 + gb.opengl/src/glu/cglunurb.h | 50 + gb.opengl/src/glu/cgluquadric.c | 64 + gb.opengl/src/glu/cgluquadric.h | 63 + gb.opengl/src/glu/gb.opengl.glu.component | 6 + gb.opengl/src/glu/main.c | 84 + gb.opengl/src/glu/main.h | 45 + gb.opengl/src/main.c | 111 + gb.opengl/src/main.h | 47 + gb.opengl/src/sge/Makefile.am | 13 + gb.opengl/src/sge/cmd2model.c | 589 + gb.opengl/src/sge/cmd2model.h | 167 + gb.opengl/src/sge/cmd2object.c | 175 + gb.opengl/src/sge/cmd2object.h | 48 + gb.opengl/src/sge/gb.opengl.sge.component | 4 + gb.opengl/src/sge/main.c | 49 + gb.opengl/src/sge/main.h | 36 + gb.openssl/AUTHORS | 0 gb.openssl/COPYING | 1 + gb.openssl/ChangeLog | 0 gb.openssl/INSTALL | 1 + gb.openssl/Makefile.am | 3 + gb.openssl/NEWS | 0 gb.openssl/README | 0 gb.openssl/acinclude.m4 | 1 + gb.openssl/component.am | 1 + gb.openssl/configure.ac | 20 + gb.openssl/gambas.h | 1 + gb.openssl/gb_common.h | 1 + gb.openssl/m4 | 1 + gb.openssl/reconf | 1 + gb.openssl/src/Makefile.am | 16 + gb.openssl/src/c_cipher.c | 487 + gb.openssl/src/c_cipher.h | 29 + gb.openssl/src/c_digest.c | 276 + gb.openssl/src/c_digest.h | 30 + gb.openssl/src/c_hmac.c | 73 + gb.openssl/src/c_hmac.h | 30 + gb.openssl/src/gb.openssl.component | 3 + gb.openssl/src/main.c | 92 + gb.openssl/src/main.h | 34 + gb.pcre/AUTHORS | 0 gb.pcre/COPYING | 1 + gb.pcre/ChangeLog | 0 gb.pcre/INSTALL | 1 + gb.pcre/Makefile.am | 3 + gb.pcre/NEWS | 0 gb.pcre/README | 0 gb.pcre/acinclude.m4 | 1 + gb.pcre/component.am | 1 + gb.pcre/configure.ac | 19 + gb.pcre/gambas.h | 1 + gb.pcre/gb_common.h | 1 + gb.pcre/m4 | 1 + gb.pcre/reconf | 1 + gb.pcre/src/Makefile.am | 15 + gb.pcre/src/README | 62 + gb.pcre/src/gb.pcre.component | 7 + gb.pcre/src/gb.pcre.h | 40 + gb.pcre/src/main.c | 62 + gb.pcre/src/main.h | 37 + gb.pcre/src/regexp.c | 532 + gb.pcre/src/regexp.h | 63 + gb.pdf/AUTHORS | 0 gb.pdf/COPYING | 1 + gb.pdf/ChangeLog | 15 + gb.pdf/INSTALL | 1 + gb.pdf/Makefile.am | 3 + gb.pdf/NEWS | 0 gb.pdf/README | 0 gb.pdf/acinclude.m4 | 1 + gb.pdf/component.am | 1 + gb.pdf/configure.ac | 47 + gb.pdf/gambas.h | 1 + gb.pdf/gb.gtk.h | 1 + gb.pdf/gb.image.h | 1 + gb.pdf/gb_common.h | 1 + gb.pdf/m4 | 1 + gb.pdf/reconf | 1 + gb.pdf/src/CPdfDocument.cpp | 1517 + gb.pdf/src/CPdfDocument.h | 115 + gb.pdf/src/Makefile.am | 16 + gb.pdf/src/gb.pdf.component | 8 + gb.pdf/src/main.cpp | 85 + gb.pdf/src/main.h | 35 + gb.qt4/AUTHORS | 0 gb.qt4/COPYING | 1 + gb.qt4/ChangeLog | 0 gb.qt4/INSTALL | 1 + gb.qt4/Makefile.am | 3 + gb.qt4/NEWS | 0 gb.qt4/README | 0 gb.qt4/acinclude.m4 | 1 + gb.qt4/component.am | 1 + gb.qt4/configure.ac | 55 + gb.qt4/gambas.h | 1 + gb.qt4/gb.draw.h | 1 + gb.qt4/gb.eval.h | 1 + gb.qt4/gb.geom.h | 1 + gb.qt4/gb.gl.h | 1 + gb.qt4/gb.image.h | 1 + gb.qt4/gb.paint.h | 1 + gb.qt4/gb.qt.am | 6 + gb.qt4/gb_common.h | 1 + gb.qt4/gbc_read_common.h | 1 + gb.qt4/m4 | 1 + gb.qt4/reconf | 1 + gb.qt4/share/gb.form.action.h | 93 + gb.qt4/share/gb.form.arrangement.h | 692 + gb.qt4/share/gb.form.const.h | 112 + gb.qt4/share/gb.form.font.h | 37 + gb.qt4/share/gb.form.print.h | 55 + gb.qt4/share/gb.form.properties.h | 194 + gb.qt4/share/gb.form.trayicon.h | 615 + gb.qt4/share/gb.form.trayicon.large.h | 263 + gb.qt4/src/CButton.cpp | 685 + gb.qt4/src/CButton.h | 114 + gb.qt4/src/CCheckBox.cpp | 184 + gb.qt4/src/CCheckBox.h | 83 + gb.qt4/src/CClipboard.cpp | 963 + gb.qt4/src/CClipboard.h | 72 + gb.qt4/src/CColor.cpp | 181 + gb.qt4/src/CColor.h | 37 + gb.qt4/src/CConst.cpp | 251 + gb.qt4/src/CConst.h | 46 + gb.qt4/src/CContainer.cpp | 1483 + gb.qt4/src/CContainer.h | 170 + gb.qt4/src/CDialog.cpp | 403 + gb.qt4/src/CDialog.h | 34 + gb.qt4/src/CDraw.cpp | 281 + gb.qt4/src/CDraw.h | 47 + gb.qt4/src/CDrawingArea.cpp | 761 + gb.qt4/src/CDrawingArea.h | 147 + gb.qt4/src/CEmbedder.cpp | 143 + gb.qt4/src/CEmbedder.h | 67 + gb.qt4/src/CFont.cpp | 654 + gb.qt4/src/CFont.h | 85 + gb.qt4/src/CFrame.cpp | 118 + gb.qt4/src/CFrame.h | 68 + gb.qt4/src/CImage.cpp | 344 + gb.qt4/src/CImage.h | 56 + gb.qt4/src/CKey.cpp | 222 + gb.qt4/src/CKey.h | 51 + gb.qt4/src/CLabel.cpp | 445 + gb.qt4/src/CLabel.h | 106 + gb.qt4/src/CMenu.cpp | 1220 + gb.qt4/src/CMenu.h | 136 + gb.qt4/src/CMessage.cpp | 554 + gb.qt4/src/CMessage.h | 64 + gb.qt4/src/CMouse.cpp | 575 + gb.qt4/src/CMouse.h | 100 + gb.qt4/src/CMovieBox.cpp | 179 + gb.qt4/src/CMovieBox.h | 57 + gb.qt4/src/CPanel.cpp | 194 + gb.qt4/src/CPanel.h | 67 + gb.qt4/src/CPicture.cpp | 392 + gb.qt4/src/CPicture.h | 78 + gb.qt4/src/CRadioButton.cpp | 195 + gb.qt4/src/CRadioButton.h | 83 + gb.qt4/src/CScreen.cpp | 498 + gb.qt4/src/CScreen.h | 53 + gb.qt4/src/CScrollBar.cpp | 226 + gb.qt4/src/CScrollBar.h | 81 + gb.qt4/src/CSlider.cpp | 283 + gb.qt4/src/CSlider.h | 81 + gb.qt4/src/CStyle.cpp | 526 + gb.qt4/src/CStyle.h | 35 + gb.qt4/src/CTabStrip.cpp | 934 + gb.qt4/src/CTabStrip.h | 106 + gb.qt4/src/CTextArea.cpp | 696 + gb.qt4/src/CTextArea.h | 70 + gb.qt4/src/CTextBox.cpp | 961 + gb.qt4/src/CTextBox.h | 112 + gb.qt4/src/CWatch.cpp | 153 + gb.qt4/src/CWatch.h | 62 + gb.qt4/src/CWatcher.cpp | 179 + gb.qt4/src/CWatcher.h | 74 + gb.qt4/src/CWidget.cpp | 3435 ++ gb.qt4/src/CWidget.h | 306 + gb.qt4/src/CWindow.cpp | 2908 + gb.qt4/src/CWindow.h | 278 + gb.qt4/src/Makefile.am | 61 + gb.qt4/src/cpaint_impl.cpp | 1584 + gb.qt4/src/cpaint_impl.h | 92 + gb.qt4/src/cprinter.cpp | 519 + gb.qt4/src/cprinter.h | 55 + gb.qt4/src/csvgimage.cpp | 274 + gb.qt4/src/csvgimage.h | 57 + gb.qt4/src/ctrayicon.cpp | 456 + gb.qt4/src/ctrayicon.h | 70 + gb.qt4/src/desktop.c | 119 + gb.qt4/src/desktop.h | 40 + gb.qt4/src/ext/CDial.cpp | 201 + gb.qt4/src/ext/CDial.h | 60 + gb.qt4/src/ext/CEditor.cpp | 1873 + gb.qt4/src/ext/CEditor.h | 80 + gb.qt4/src/ext/CLCDNumber.cpp | 178 + gb.qt4/src/ext/CLCDNumber.h | 43 + gb.qt4/src/ext/CTextEdit.cpp | 734 + gb.qt4/src/ext/CTextEdit.h | 86 + gb.qt4/src/ext/Makefile.am | 20 + gb.qt4/src/ext/garray.cpp | 71 + gb.qt4/src/ext/garray.h | 112 + gb.qt4/src/ext/gb.qt4.ext.component | 4 + gb.qt4/src/ext/gdocument.cpp | 1786 + gb.qt4/src/ext/gdocument.h | 238 + gb.qt4/src/ext/gstring.cpp | 70 + gb.qt4/src/ext/gstring.h | 275 + gb.qt4/src/ext/gview.cpp | 3447 ++ gb.qt4/src/ext/gview.h | 317 + gb.qt4/src/ext/main.cpp | 93 + gb.qt4/src/ext/main.h | 41 + gb.qt4/src/fix_breeze.cpp | 263 + gb.qt4/src/fix_breeze.h | 55 + gb.qt4/src/gb.qt.h | 172 + gb.qt4/src/gb.qt4.component | 6 + gb.qt4/src/main.cpp | 1556 + gb.qt4/src/main.h | 166 + gb.qt4/src/opengl/CGLarea.cpp | 202 + gb.qt4/src/opengl/CGLarea.h | 60 + gb.qt4/src/opengl/Makefile.am | 14 + gb.qt4/src/opengl/gb.qt4.opengl.component | 6 + gb.qt4/src/opengl/main.cpp | 53 + gb.qt4/src/opengl/main.h | 37 + gb.qt4/src/trayicon.xpm | 163 + gb.qt4/src/webkit/Makefile.am | 19 + gb.qt4/src/webkit/ccookiejar.cpp | 188 + gb.qt4/src/webkit/ccookiejar.h | 77 + gb.qt4/src/webkit/control/webview.png | Bin 0 -> 1160 bytes gb.qt4/src/webkit/cwebdownload.cpp | 369 + gb.qt4/src/webkit/cwebdownload.h | 89 + gb.qt4/src/webkit/cwebelement.cpp | 399 + gb.qt4/src/webkit/cwebelement.h | 52 + gb.qt4/src/webkit/cwebframe.cpp | 177 + gb.qt4/src/webkit/cwebframe.h | 56 + gb.qt4/src/webkit/cwebhittest.cpp | 121 + gb.qt4/src/webkit/cwebhittest.h | 54 + gb.qt4/src/webkit/cwebsettings.cpp | 489 + gb.qt4/src/webkit/cwebsettings.h | 50 + gb.qt4/src/webkit/cwebview.cpp | 943 + gb.qt4/src/webkit/cwebview.h | 122 + gb.qt4/src/webkit/gb.qt4.webkit.component | 10 + gb.qt4/src/webkit/main.cpp | 143 + gb.qt4/src/webkit/main.h | 41 + gb.qt4/src/x11.c | 810 + gb.qt4/src/x11.h | 132 + gb.qt5/AUTHORS | 0 gb.qt5/COPYING | 1 + gb.qt5/ChangeLog | 0 gb.qt5/INSTALL | 1 + gb.qt5/Makefile.am | 3 + gb.qt5/NEWS | 0 gb.qt5/README | 0 gb.qt5/acinclude.m4 | 1 + gb.qt5/component.am | 1 + gb.qt5/configure.ac | 53 + gb.qt5/gambas.h | 1 + gb.qt5/gb.draw.h | 1 + gb.qt5/gb.eval.h | 1 + gb.qt5/gb.geom.h | 1 + gb.qt5/gb.gl.h | 1 + gb.qt5/gb.image.h | 1 + gb.qt5/gb.paint.h | 1 + gb.qt5/gb.qt.am | 6 + gb.qt5/gb_common.h | 1 + gb.qt5/m4 | 1 + gb.qt5/reconf | 1 + gb.qt5/share | 1 + gb.qt5/src/CButton.cpp | 1 + gb.qt5/src/CButton.h | 1 + gb.qt5/src/CCheckBox.cpp | 1 + gb.qt5/src/CCheckBox.h | 1 + gb.qt5/src/CClipboard.cpp | 1 + gb.qt5/src/CClipboard.h | 1 + gb.qt5/src/CColor.cpp | 1 + gb.qt5/src/CColor.h | 1 + gb.qt5/src/CConst.cpp | 1 + gb.qt5/src/CConst.h | 1 + gb.qt5/src/CContainer.cpp | 1 + gb.qt5/src/CContainer.h | 1 + gb.qt5/src/CDialog.cpp | 1 + gb.qt5/src/CDialog.h | 1 + gb.qt5/src/CDraw.cpp | 1 + gb.qt5/src/CDraw.h | 1 + gb.qt5/src/CDrawingArea.cpp | 1 + gb.qt5/src/CDrawingArea.h | 1 + gb.qt5/src/CEmbedder.cpp | 1 + gb.qt5/src/CEmbedder.h | 1 + gb.qt5/src/CFont.cpp | 1 + gb.qt5/src/CFont.h | 1 + gb.qt5/src/CFrame.cpp | 1 + gb.qt5/src/CFrame.h | 1 + gb.qt5/src/CImage.cpp | 1 + gb.qt5/src/CImage.h | 1 + gb.qt5/src/CKey.cpp | 1 + gb.qt5/src/CKey.h | 1 + gb.qt5/src/CLabel.cpp | 1 + gb.qt5/src/CLabel.h | 1 + gb.qt5/src/CMenu.cpp | 1 + gb.qt5/src/CMenu.h | 1 + gb.qt5/src/CMessage.cpp | 1 + gb.qt5/src/CMessage.h | 1 + gb.qt5/src/CMouse.cpp | 1 + gb.qt5/src/CMouse.h | 1 + gb.qt5/src/CMovieBox.cpp | 1 + gb.qt5/src/CMovieBox.h | 1 + gb.qt5/src/CPanel.cpp | 1 + gb.qt5/src/CPanel.h | 1 + gb.qt5/src/CPicture.cpp | 1 + gb.qt5/src/CPicture.h | 1 + gb.qt5/src/CRadioButton.cpp | 1 + gb.qt5/src/CRadioButton.h | 1 + gb.qt5/src/CScreen.cpp | 1 + gb.qt5/src/CScreen.h | 1 + gb.qt5/src/CScrollBar.cpp | 1 + gb.qt5/src/CScrollBar.h | 1 + gb.qt5/src/CSlider.cpp | 1 + gb.qt5/src/CSlider.h | 1 + gb.qt5/src/CStyle.cpp | 1 + gb.qt5/src/CStyle.h | 1 + gb.qt5/src/CTabStrip.cpp | 1 + gb.qt5/src/CTabStrip.h | 1 + gb.qt5/src/CTextArea.cpp | 1 + gb.qt5/src/CTextArea.h | 1 + gb.qt5/src/CTextBox.cpp | 1 + gb.qt5/src/CTextBox.h | 1 + gb.qt5/src/CWatch.cpp | 1 + gb.qt5/src/CWatch.h | 1 + gb.qt5/src/CWatcher.cpp | 1 + gb.qt5/src/CWatcher.h | 1 + gb.qt5/src/CWidget.cpp | 1 + gb.qt5/src/CWidget.h | 1 + gb.qt5/src/CWindow.cpp | 1 + gb.qt5/src/CWindow.h | 1 + gb.qt5/src/Makefile.am | 55 + gb.qt5/src/cpaint_impl.cpp | 1 + gb.qt5/src/cpaint_impl.h | 1 + gb.qt5/src/cprinter.cpp | 1 + gb.qt5/src/cprinter.h | 1 + gb.qt5/src/csvgimage.cpp | 1 + gb.qt5/src/csvgimage.h | 1 + gb.qt5/src/ctrayicon.cpp | 1 + gb.qt5/src/ctrayicon.h | 1 + gb.qt5/src/desktop.c | 1 + gb.qt5/src/desktop.h | 1 + gb.qt5/src/ext/CDial.cpp | 1 + gb.qt5/src/ext/CDial.h | 1 + gb.qt5/src/ext/CLCDNumber.cpp | 1 + gb.qt5/src/ext/CLCDNumber.h | 1 + gb.qt5/src/ext/CTextEdit.cpp | 1 + gb.qt5/src/ext/CTextEdit.h | 1 + gb.qt5/src/ext/Makefile.am | 16 + gb.qt5/src/ext/gb.qt5.ext.component | 4 + gb.qt5/src/ext/main.cpp | 67 + gb.qt5/src/ext/main.h | 36 + gb.qt5/src/fix_breeze.cpp | 1 + gb.qt5/src/fix_breeze.h | 1 + gb.qt5/src/gb.qt.h | 1 + gb.qt5/src/gb.qt5.component | 6 + gb.qt5/src/main.cpp | 1 + gb.qt5/src/main.h | 1 + gb.qt5/src/opengl/CGLarea.cpp | 158 + gb.qt5/src/opengl/CGLarea.h | 60 + gb.qt5/src/opengl/COldGLarea.cpp | 204 + gb.qt5/src/opengl/COldGLarea.h | 60 + gb.qt5/src/opengl/Makefile.am | 23 + gb.qt5/src/opengl/gb.qt5.opengl.component | 6 + gb.qt5/src/opengl/main.cpp | 58 + gb.qt5/src/opengl/main.h | 37 + gb.qt5/src/trayicon.xpm | 1 + gb.qt5/src/webkit/Makefile.am | 20 + gb.qt5/src/webkit/ccookiejar.cpp | 1 + gb.qt5/src/webkit/ccookiejar.h | 1 + gb.qt5/src/webkit/control/webview.png | Bin 0 -> 1572 bytes gb.qt5/src/webkit/cwebdownload.cpp | 1 + gb.qt5/src/webkit/cwebdownload.h | 1 + gb.qt5/src/webkit/cwebelement.cpp | 1 + gb.qt5/src/webkit/cwebelement.h | 1 + gb.qt5/src/webkit/cwebframe.cpp | 1 + gb.qt5/src/webkit/cwebframe.h | 1 + gb.qt5/src/webkit/cwebhittest.cpp | 1 + gb.qt5/src/webkit/cwebhittest.h | 1 + gb.qt5/src/webkit/cwebsettings.cpp | 1 + gb.qt5/src/webkit/cwebsettings.h | 1 + gb.qt5/src/webkit/cwebview.cpp | 1 + gb.qt5/src/webkit/cwebview.h | 1 + gb.qt5/src/webkit/gb.qt5.webkit.component | 10 + gb.qt5/src/webkit/main.cpp | 1 + gb.qt5/src/webkit/main.h | 1 + gb.qt5/src/x11.c | 1 + gb.qt5/src/x11.h | 1 + gb.sdl.sound/AUTHORS | 5 + gb.sdl.sound/COPYING | 1 + gb.sdl.sound/ChangeLog | 0 gb.sdl.sound/INSTALL | 1 + gb.sdl.sound/Makefile.am | 3 + gb.sdl.sound/NEWS | 0 gb.sdl.sound/README | 10 + gb.sdl.sound/acinclude.m4 | 1 + gb.sdl.sound/component.am | 1 + gb.sdl.sound/configure.ac | 23 + gb.sdl.sound/gambas.h | 1 + gb.sdl.sound/gb_common.h | 1 + gb.sdl.sound/m4 | 1 + gb.sdl.sound/reconf | 1 + gb.sdl.sound/src/Makefile.am | 13 + gb.sdl.sound/src/cdrom.c | 491 + gb.sdl.sound/src/cdrom.h | 52 + gb.sdl.sound/src/gb.sdl.sound.component | 9 + gb.sdl.sound/src/main.c | 97 + gb.sdl.sound/src/main.h | 33 + gb.sdl.sound/src/sound.c | 604 + gb.sdl.sound/src/sound.h | 72 + gb.sdl/AUTHORS | 6 + gb.sdl/COPYING | 1 + gb.sdl/ChangeLog | 0 gb.sdl/INSTALL | 1 + gb.sdl/Makefile.am | 3 + gb.sdl/NEWS | 0 gb.sdl/README | 12 + gb.sdl/acinclude.m4 | 1 + gb.sdl/component.am | 1 + gb.sdl/configure.ac | 48 + gb.sdl/gambas.h | 1 + gb.sdl/gb.image.h | 1 + gb.sdl/gb_common.h | 1 + gb.sdl/m4 | 1 + gb.sdl/reconf | 1 + gb.sdl/src/Cconst.cpp | 69 + gb.sdl/src/Cconst.h | 34 + gb.sdl/src/Cdesktop.cpp | 51 + gb.sdl/src/Cdesktop.h | 34 + gb.sdl/src/Cdraw.cpp | 367 + gb.sdl/src/Cdraw.h | 49 + gb.sdl/src/Cfont.cpp | 243 + gb.sdl/src/Cfont.h | 47 + gb.sdl/src/Cimage.cpp | 160 + gb.sdl/src/Cimage.h | 58 + gb.sdl/src/Cjoystick.cpp | 296 + gb.sdl/src/Cjoystick.h | 52 + gb.sdl/src/Ckey.cpp | 235 + gb.sdl/src/Ckey.h | 50 + gb.sdl/src/Cmouse.cpp | 319 + gb.sdl/src/Cmouse.h | 65 + gb.sdl/src/Cwindow.cpp | 548 + gb.sdl/src/Cwindow.h | 78 + gb.sdl/src/Makefile.am | 34 + gb.sdl/src/SDL_h.h | 84 + gb.sdl/src/SDLapp.cpp | 266 + gb.sdl/src/SDLapp.h | 61 + gb.sdl/src/SDLcore.cpp | 52 + gb.sdl/src/SDLcore.h | 50 + gb.sdl/src/SDLcursor.cpp | 126 + gb.sdl/src/SDLcursor.h | 52 + gb.sdl/src/SDLdebug.cpp | 98 + gb.sdl/src/SDLdebug.h | 34 + gb.sdl/src/SDLerror.cpp | 30 + gb.sdl/src/SDLerror.h | 41 + gb.sdl/src/SDLfont.cpp | 586 + gb.sdl/src/SDLfont.h | 95 + gb.sdl/src/SDLgfx.cpp | 533 + gb.sdl/src/SDLgfx.h | 76 + gb.sdl/src/SDLosrender.cpp | 79 + gb.sdl/src/SDLosrender.h | 45 + gb.sdl/src/SDLsurface.cpp | 284 + gb.sdl/src/SDLsurface.h | 74 + gb.sdl/src/SDLtexture.cpp | 177 + gb.sdl/src/SDLtexture.h | 73 + gb.sdl/src/SDLwindow.cpp | 276 + gb.sdl/src/SDLwindow.h | 105 + gb.sdl/src/default_font.h | 632 + gb.sdl/src/gb.sdl.component | 6 + gb.sdl/src/main.cpp | 155 + gb.sdl/src/main.h | 42 + gb.sdl2/AUTHORS | 0 gb.sdl2/COPYING | 1 + gb.sdl2/ChangeLog | 0 gb.sdl2/INSTALL | 1 + gb.sdl2/Makefile.am | 3 + gb.sdl2/NEWS | 0 gb.sdl2/README | 0 gb.sdl2/acinclude.m4 | 1 + gb.sdl2/component.am | 1 + gb.sdl2/configure.ac | 26 + gb.sdl2/gambas.h | 1 + gb.sdl2/gb.geom.h | 1 + gb.sdl2/gb.image.h | 1 + gb.sdl2/gb_common.h | 1 + gb.sdl2/gb_list.h | 1 + gb.sdl2/gb_list_temp.h | 1 + gb.sdl2/m4 | 1 + gb.sdl2/reconf | 1 + gb.sdl2/src/Makefile.am | 29 + gb.sdl2/src/audio/Makefile.am | 15 + gb.sdl2/src/audio/c_channel.c | 411 + gb.sdl2/src/audio/c_channel.h | 55 + gb.sdl2/src/audio/c_music.c | 241 + gb.sdl2/src/audio/c_music.h | 36 + gb.sdl2/src/audio/c_sound.c | 141 + gb.sdl2/src/audio/c_sound.h | 41 + gb.sdl2/src/audio/gb.sdl2.audio.component | 2 + gb.sdl2/src/audio/main.c | 159 + gb.sdl2/src/audio/main.h | 52 + gb.sdl2/src/c_draw.c | 421 + gb.sdl2/src/c_draw.h | 49 + gb.sdl2/src/c_font.c | 527 + gb.sdl2/src/c_font.h | 52 + gb.sdl2/src/c_image.c | 205 + gb.sdl2/src/c_image.h | 57 + gb.sdl2/src/c_key.c | 256 + gb.sdl2/src/c_key.h | 37 + gb.sdl2/src/c_mouse.c | 276 + gb.sdl2/src/c_mouse.h | 50 + gb.sdl2/src/c_window.c | 670 + gb.sdl2/src/c_window.h | 72 + gb.sdl2/src/default_font.c | 200 + gb.sdl2/src/default_font.h | 38 + gb.sdl2/src/default_font_data.h | 627 + gb.sdl2/src/gb.sdl2.component | 5 + gb.sdl2/src/main.c | 212 + gb.sdl2/src/main.h | 78 + gb.v4l/AUTHORS | 0 gb.v4l/COPYING | 1 + gb.v4l/ChangeLog | 241 + gb.v4l/INSTALL | 1 + gb.v4l/Makefile.am | 3 + gb.v4l/NEWS | 0 gb.v4l/README | 4 + gb.v4l/acinclude.m4 | 1 + gb.v4l/component.am | 1 + gb.v4l/configure.ac | 25 + gb.v4l/gambas.h | 1 + gb.v4l/gb.image.h | 1 + gb.v4l/gb_common.h | 1 + gb.v4l/m4 | 1 + gb.v4l/orig/video-capture-0.2.tar.gz | Bin 0 -> 10049 bytes gb.v4l/reconf | 1 + gb.v4l/src/CConverters.c | 255 + gb.v4l/src/CWebcam.c | 1906 + gb.v4l/src/CWebcam.h | 197 + gb.v4l/src/Makefile.am | 14 + gb.v4l/src/gb.v4l.component | 10 + gb.v4l/src/gv4l2.c | 803 + gb.v4l/src/main.c | 50 + gb.v4l/src/main.h | 36 + gb.v4l/src/videodev.h | 343 + gb.xml/AUTHORS | 0 gb.xml/COPYING | 1 + gb.xml/ChangeLog | 0 gb.xml/INSTALL | 1 + gb.xml/Makefile.am | 4 + gb.xml/NEWS | 0 gb.xml/README | 7 + gb.xml/TODO | 8 + gb.xml/acinclude.m4 | 1 + gb.xml/component.am | 1 + gb.xml/configure.ac | 37 + gb.xml/gambas.h | 1 + gb.xml/gb_common.h | 1 + gb.xml/m4 | 1 + gb.xml/reconf | 1 + gb.xml/src/.directory | 6 + gb.xml/src/CDocument.cpp | 238 + gb.xml/src/CDocument.h | 29 + gb.xml/src/CElement.cpp | 411 + gb.xml/src/CElement.h | 29 + gb.xml/src/CExplorer.cpp | 154 + gb.xml/src/CExplorer.h | 37 + gb.xml/src/CNode.cpp | 438 + gb.xml/src/CNode.h | 31 + gb.xml/src/CReader.cpp | 435 + gb.xml/src/CReader.h | 42 + gb.xml/src/CTextNode.cpp | 94 + gb.xml/src/CTextNode.h | 31 + gb.xml/src/Makefile.am | 18 + gb.xml/src/document.cpp | 215 + gb.xml/src/document.h | 38 + gb.xml/src/element.cpp | 335 + gb.xml/src/element.h | 63 + gb.xml/src/explorer.cpp | 138 + gb.xml/src/explorer.h | 65 + gb.xml/src/gb.xml.component | 5 + gb.xml/src/gb.xml.h | 204 + gb.xml/src/gb.xml/.component | 4 + gb.xml/src/gb.xml/.directory | 2 + gb.xml/src/gb.xml/.icon.png | Bin 0 -> 10569 bytes gb.xml/src/gb.xml/.project | 9 + gb.xml/src/gb.xml/.src/MTest.module | 34 + gb.xml/src/gb.xml/.src/MTest2.module | 32 + gb.xml/src/gb.xml/.src/XmlReader.class | 104 + gb.xml/src/gb.xml/.src/XmlWriter.class | 240 + gb.xml/src/gb.xml/.src/_XmlWriterDTD.class | 8 + gb.xml/src/gb.xml/text.xml | 2 + gb.xml/src/gbinterface.h | 27 + gb.xml/src/html/CHTMLDocument.cpp | 236 + gb.xml/src/html/CHTMLDocument.h | 34 + gb.xml/src/html/CHTMLElement.cpp | 121 + gb.xml/src/html/CHTMLElement.h | 32 + gb.xml/src/html/Makefile.am | 19 + gb.xml/src/html/cssfilter.cpp | 195 + gb.xml/src/html/cssfilter.h | 8 + gb.xml/src/html/gb.xml.html.component | 5 + gb.xml/src/html/gb.xml.html.h | 26 + gb.xml/src/html/htmldocument.cpp | 317 + gb.xml/src/html/htmldocument.h | 61 + gb.xml/src/html/htmlelement.cpp | 104 + gb.xml/src/html/htmlelement.h | 42 + gb.xml/src/html/htmlmain.cpp | 63 + gb.xml/src/html/htmlmain.h | 35 + gb.xml/src/html/htmlparser.cpp | 342 + gb.xml/src/html/htmlparser.h | 9 + gb.xml/src/html/htmlserializer.cpp | 239 + gb.xml/src/html/htmlserializer.h | 10 + gb.xml/src/main.cpp | 139 + gb.xml/src/main.h | 32 + gb.xml/src/node.cpp | 635 + gb.xml/src/node.h | 98 + gb.xml/src/parser.cpp | 341 + gb.xml/src/parser.h | 14 + gb.xml/src/reader.cpp | 499 + gb.xml/src/reader.h | 114 + gb.xml/src/rpc/Makefile.am | 2 + gb.xml/src/rpc/gb.xml.rpc.component | 1 + gb.xml/src/rpc/gb.xml.rpc/.component | 4 + gb.xml/src/rpc/gb.xml.rpc/.directory | 2 + gb.xml/src/rpc/gb.xml.rpc/.icon.png | Bin 0 -> 10569 bytes gb.xml/src/rpc/gb.xml.rpc/.project | 14 + gb.xml/src/rpc/gb.xml.rpc/.src/RpcArray.class | 250 + gb.xml/src/rpc/gb.xml.rpc/.src/RpcAtom.class | 29 + .../src/rpc/gb.xml.rpc/.src/RpcClient.class | 372 + .../src/rpc/gb.xml.rpc/.src/RpcFunction.class | 125 + .../src/rpc/gb.xml.rpc/.src/RpcServer.class | 357 + .../src/rpc/gb.xml.rpc/.src/RpcStruct.class | 151 + gb.xml/src/rpc/gb.xml.rpc/.src/RpcType.class | 101 + .../rpc/gb.xml.rpc/.src/Test/CXMLRPC.class | 94 + .../src/rpc/gb.xml.rpc/.src/Test/MMain.module | 63 + .../src/rpc/gb.xml.rpc/.src/Test/MTest.module | 61 + gb.xml/src/rpc/gb.xml.rpc/.src/Tools.module | 338 + gb.xml/src/rpc/gb.xml.rpc/.src/XmlRpc.class | 101 + gb.xml/src/rpc/gb.xml.rpc/.src/hPost.class | 94 + .../src/rpc/gb.xml.rpc/.src/miniServer.class | 465 + gb.xml/src/serializer.cpp | 351 + gb.xml/src/serializer.h | 18 + gb.xml/src/textnode.cpp | 336 + gb.xml/src/textnode.h | 51 + gb.xml/src/utils.cpp | 372 + gb.xml/src/utils.h | 62 + gb.xml/src/xslt/CXSLT.cpp | 181 + gb.xml/src/xslt/CXSLT.h | 44 + gb.xml/src/xslt/Makefile.am | 11 + gb.xml/src/xslt/gb.xml.xslt.component | 6 + gb.xml/src/xslt/main.cpp | 48 + gb.xml/src/xslt/main.h | 38 + gb.xml/src/xslt/xslt.pro | 14 + logo/gambas-ide.svg | 166 + logo/gambas.svg | 91 + m4/ax_compare_version.m4 | 177 + m4/gb_cflags_gcc_option.m4 | 226 + m4/gb_httpd.m4 | 188 + m4/gb_sdl.m4 | 185 + main/AUTHORS | 0 main/COPYING | 1 + main/ChangeLog | 1 + main/INSTALL | 231 + main/Makefile.am | 89 + main/NEWS | 0 main/README | 0 main/TODO | 0 main/acinclude.m4 | 1 + main/compile | 142 + main/component.am | 1 + main/configure.ac | 146 + main/gb.pcre.h | 1 + main/gbc/Makefile.am | 72 + main/gbc/gb_alloc.c | 24 + main/gbc/gb_array.c | 25 + main/gbc/gb_buffer.c | 25 + main/gbc/gb_common.c | 32 + main/gbc/gb_error.c | 274 + main/gbc/gb_error.h | 127 + main/gbc/gb_file.c | 29 + main/gbc/gb_file.h | 24 + main/gbc/gb_str.c | 199 + main/gbc/gb_str.h | 42 + main/gbc/gb_table.c | 26 + main/gbc/gba.c | 415 + main/gbc/gbc.c | 520 + main/gbc/gbc_arch.c | 43 + main/gbc/gbc_archive.c | 444 + main/gbc/gbc_archive.h | 49 + main/gbc/gbc_chown.c | 52 + main/gbc/gbc_chown.h | 29 + main/gbc/gbc_class.c | 1013 + main/gbc/gbc_class.h | 275 + main/gbc/gbc_code.c | 36 + main/gbc/gbc_compile.c | 697 + main/gbc/gbc_compile.h | 112 + main/gbc/gbc_dump.c | 845 + main/gbc/gbc_form.c | 531 + main/gbc/gbc_form.h | 57 + main/gbc/gbc_form_webpage.c | 526 + main/gbc/gbc_header.c | 1147 + main/gbc/gbc_header.h | 69 + main/gbc/gbc_help.c | 179 + main/gbc/gbc_help.h | 40 + main/gbc/gbc_output.c | 1383 + main/gbc/gbc_output.h | 52 + main/gbc/gbc_pcode.c | 28 + main/gbc/gbc_preprocess.c | 325 + main/gbc/gbc_preprocess.h | 37 + main/gbc/gbc_read.c | 1241 + main/gbc/gbc_read.h | 47 + main/gbc/gbc_reserved.c | 27 + main/gbc/gbc_reserved_make.c | 469 + main/gbc/gbc_trans.c | 851 + main/gbc/gbc_trans.h | 257 + main/gbc/gbc_trans_code.c | 743 + main/gbc/gbc_trans_ctrl.c | 1310 + main/gbc/gbc_trans_expr.c | 1115 + main/gbc/gbc_trans_subr.c | 1036 + main/gbc/gbc_trans_tree.c | 825 + main/gbc/gbc_type.c | 126 + main/gbc/gbc_type.h | 109 + main/gbc/gbi.c | 923 + main/gbx/Makefile.am | 98 + main/gbx/gb.jit.h | 115 + main/gbx/gb_alloc.c | 27 + main/gbx/gb_array.c | 25 + main/gbx/gb_buffer.c | 25 + main/gbx/gb_common.c | 111 + main/gbx/gb_common_check.h | 40 + main/gbx/gb_error.c | 767 + main/gbx/gb_error.h | 186 + main/gbx/gb_file.c | 38 + main/gbx/gb_file.h | 26 + main/gbx/gb_hash.c | 24 + main/gbx/gb_list.c | 24 + main/gbx/gb_table.c | 28 + main/gbx/gbx.c | 485 + main/gbx/gbx_api.c | 2461 + main/gbx/gbx_api.h | 230 + main/gbx/gbx_archive.c | 783 + main/gbx/gbx_archive.h | 103 + main/gbx/gbx_c_application.c | 347 + main/gbx/gbx_c_application.h | 35 + main/gbx/gbx_c_array.c | 2176 + main/gbx/gbx_c_array.h | 115 + main/gbx/gbx_c_class.c | 990 + main/gbx/gbx_c_class.h | 38 + main/gbx/gbx_c_collection.c | 422 + main/gbx/gbx_c_collection.h | 70 + main/gbx/gbx_c_enum.c | 207 + main/gbx/gbx_c_enum.h | 53 + main/gbx/gbx_c_error.c | 168 + main/gbx/gbx_c_error.h | 33 + main/gbx/gbx_c_file.c | 1122 + main/gbx/gbx_c_file.h | 80 + main/gbx/gbx_c_gambas.c | 270 + main/gbx/gbx_c_gambas.h | 35 + main/gbx/gbx_c_observer.c | 148 + main/gbx/gbx_c_observer.h | 57 + main/gbx/gbx_c_process.c | 1312 + main/gbx/gbx_c_process.h | 82 + main/gbx/gbx_c_string.c | 1046 + main/gbx/gbx_c_string.h | 50 + main/gbx/gbx_c_system.c | 383 + main/gbx/gbx_c_system.h | 35 + main/gbx/gbx_c_task.c | 739 + main/gbx/gbx_c_task.h | 60 + main/gbx/gbx_c_timer.c | 206 + main/gbx/gbx_c_timer.h | 45 + main/gbx/gbx_class.c | 1562 + main/gbx/gbx_class.h | 557 + main/gbx/gbx_class_desc.h | 207 + main/gbx/gbx_class_info.c | 448 + main/gbx/gbx_class_init.c | 173 + main/gbx/gbx_class_load.c | 1360 + main/gbx/gbx_class_load.h | 302 + main/gbx/gbx_class_native.c | 356 + main/gbx/gbx_compare.c | 692 + main/gbx/gbx_compare.h | 63 + main/gbx/gbx_component.c | 430 + main/gbx/gbx_component.h | 89 + main/gbx/gbx_date.c | 913 + main/gbx/gbx_date.h | 109 + main/gbx/gbx_debug.c | 623 + main/gbx/gbx_debug.h | 88 + main/gbx/gbx_eval.c | 163 + main/gbx/gbx_eval.h | 41 + main/gbx/gbx_event.c | 271 + main/gbx/gbx_event.h | 59 + main/gbx/gbx_exec.c | 2042 + main/gbx/gbx_exec.h | 353 + main/gbx/gbx_exec_enum.c | 97 + main/gbx/gbx_exec_loop.c | 4162 ++ main/gbx/gbx_exec_operator.c | 368 + main/gbx/gbx_exec_pop.c | 319 + main/gbx/gbx_exec_push.c | 743 + main/gbx/gbx_expression.h | 75 + main/gbx/gbx_extern.c | 788 + main/gbx/gbx_extern.h | 49 + main/gbx/gbx_info.h | 70 + main/gbx/gbx_jit.c | 345 + main/gbx/gbx_jit.h | 55 + main/gbx/gbx_library.c | 365 + main/gbx/gbx_library.h | 77 + main/gbx/gbx_local.c | 1766 + main/gbx/gbx_local.h | 123 + main/gbx/gbx_math.c | 242 + main/gbx/gbx_math.h | 72 + main/gbx/gbx_number.c | 543 + main/gbx/gbx_number.h | 45 + main/gbx/gbx_object.c | 517 + main/gbx/gbx_object.h | 234 + main/gbx/gbx_project.c | 406 + main/gbx/gbx_project.h | 61 + main/gbx/gbx_regexp.c | 304 + main/gbx/gbx_regexp.h | 37 + main/gbx/gbx_replace.c | 25 + main/gbx/gbx_signal.c | 403 + main/gbx/gbx_signal.h | 61 + main/gbx/gbx_split.c | 243 + main/gbx/gbx_split.h | 31 + main/gbx/gbx_stack.c | 161 + main/gbx/gbx_stack.h | 132 + main/gbx/gbx_stream.c | 1826 + main/gbx/gbx_stream.h | 253 + main/gbx/gbx_stream_arch.c | 165 + main/gbx/gbx_stream_buffer.c | 245 + main/gbx/gbx_stream_direct.c | 223 + main/gbx/gbx_stream_lock.c | 109 + main/gbx/gbx_stream_memory.c | 186 + main/gbx/gbx_stream_pipe.c | 127 + main/gbx/gbx_stream_process.c | 118 + main/gbx/gbx_stream_string.c | 140 + main/gbx/gbx_string.c | 1324 + main/gbx/gbx_string.h | 224 + main/gbx/gbx_struct.c | 102 + main/gbx/gbx_struct.h | 51 + main/gbx/gbx_subr.c | 244 + main/gbx/gbx_subr.h | 262 + main/gbx/gbx_subr_conv.c | 286 + main/gbx/gbx_subr_extern.c | 340 + main/gbx/gbx_subr_file.c | 1152 + main/gbx/gbx_subr_math.c | 942 + main/gbx/gbx_subr_math_temp.h | 463 + main/gbx/gbx_subr_misc.c | 432 + main/gbx/gbx_subr_string.c | 1359 + main/gbx/gbx_subr_test.c | 1010 + main/gbx/gbx_subr_test_temp.h | 333 + main/gbx/gbx_subr_time.c | 287 + main/gbx/gbx_type.c | 351 + main/gbx/gbx_type.h | 96 + main/gbx/gbx_value.c | 2189 + main/gbx/gbx_value.h | 656 + main/gbx/gbx_variant.h | 99 + main/gbx/gbx_watch.c | 739 + main/gbx/gbx_watch.h | 73 + main/lib/Makefile.am | 2 + main/lib/clipper/LICENSE | 26 + main/lib/clipper/Makefile.am | 19 + main/lib/clipper/c_clipper.cpp | 485 + main/lib/clipper/c_clipper.h | 48 + main/lib/clipper/clipper.cpp | 4610 ++ main/lib/clipper/clipper.hpp | 398 + main/lib/clipper/gb.clipper.component | 4 + main/lib/clipper/gb.geom.h | 1 + main/lib/clipper/main.cpp | 62 + main/lib/clipper/main.h | 36 + main/lib/complex/Makefile.am | 14 + main/lib/complex/ccomplex.c | 533 + main/lib/complex/ccomplex.h | 44 + main/lib/complex/gb.complex.component | 4 + main/lib/complex/main.c | 58 + main/lib/complex/main.h | 34 + main/lib/compress/CCompress.c | 187 + main/lib/compress/CCompress.h | 51 + main/lib/compress/CUncompress.c | 149 + main/lib/compress/CUncompress.h | 49 + main/lib/compress/Makefile.am | 12 + main/lib/compress/gb.compress.component | 3 + main/lib/compress/gb.compress.h | 68 + main/lib/compress/main.c | 111 + main/lib/compress/main.h | 39 + main/lib/data/Makefile.am | 21 + main/lib/data/TODO | 55 + main/lib/data/c_avltree.c | 777 + main/lib/data/c_avltree.h | 33 + main/lib/data/c_circular.c | 307 + main/lib/data/c_circular.h | 33 + main/lib/data/c_deque.c | 391 + main/lib/data/c_deque.h | 36 + main/lib/data/c_graph.c | 485 + main/lib/data/c_graph.h | 66 + main/lib/data/c_graphmatrix.c | 699 + main/lib/data/c_graphmatrix.h | 34 + main/lib/data/c_heap.c | 406 + main/lib/data/c_heap.h | 33 + main/lib/data/c_list.c | 1363 + main/lib/data/c_list.h | 36 + main/lib/data/c_trie.c | 590 + main/lib/data/c_trie.h | 33 + main/lib/data/gb.data.component | 3 + main/lib/data/gb.data/.component | 3 + main/lib/data/gb.data/.directory | 2 + main/lib/data/gb.data/.icon.png | Bin 0 -> 10569 bytes main/lib/data/gb.data/.project | 10 + main/lib/data/gb.data/.src/MMain.module | 4 + main/lib/data/gb.data/.src/PrioSet.class | 63 + .../data/gb.data/.src/_PrioSet_Entry.class | 17 + main/lib/data/list.h | 99 + main/lib/data/lookup3.h | 142 + main/lib/data/main.c | 81 + main/lib/data/main.h | 32 + main/lib/data/string_compare.h | 43 + main/lib/data/trie.c | 756 + main/lib/data/trie.h | 82 + main/lib/db/CConnection.c | 883 + main/lib/db/CConnection.h | 56 + main/lib/db/CDatabase.c | 206 + main/lib/db/CDatabase.h | 55 + main/lib/db/CField.c | 329 + main/lib/db/CField.h | 58 + main/lib/db/CIndex.c | 274 + main/lib/db/CIndex.h | 56 + main/lib/db/CResult.c | 998 + main/lib/db/CResult.h | 78 + main/lib/db/CResultField.c | 258 + main/lib/db/CResultField.h | 57 + main/lib/db/CTable.c | 443 + main/lib/db/CTable.h | 63 + main/lib/db/CUser.c | 229 + main/lib/db/CUser.h | 56 + main/lib/db/Makefile.am | 25 + main/lib/db/c_subcollection.c | 265 + main/lib/db/c_subcollection.h | 68 + main/lib/db/deletemap.c | 183 + main/lib/db/deletemap.h | 35 + main/lib/db/gb.db.component | 4 + main/lib/db/gb.db.h | 268 + main/lib/db/gb.db.proto.h | 148 + main/lib/db/gb.db/.component | 3 + main/lib/db/gb.db/.directory | 2 + main/lib/db/gb.db/.icon.png | Bin 0 -> 10569 bytes main/lib/db/gb.db/.project | 10 + main/lib/db/gb.db/.src/Connection.class | 256 + main/lib/db/gb.db/.src/Connections.class | 215 + main/lib/db/gb.db/.src/Main.module | 16 + main/lib/db/gb.db/.src/SQLRequest.class | 174 + main/lib/db/gb_barray.h | 57 + main/lib/db/main.c | 699 + main/lib/db/main.h | 64 + main/lib/db/sqlite.c | 216 + main/lib/db/sqlite.h | 35 + main/lib/debug/CDebug.c | 294 + main/lib/debug/CDebug.h | 39 + main/lib/debug/Makefile.am | 18 + main/lib/debug/debug.c | 1136 + main/lib/debug/debug.h | 94 + main/lib/debug/gb.debug.component | 2 + main/lib/debug/gb.debug.h | 120 + main/lib/debug/main.c | 72 + main/lib/debug/main.h | 36 + main/lib/debug/print.c | 486 + main/lib/debug/print.h | 39 + main/lib/debug/profile.c | 245 + main/lib/debug/profile.h | 34 + main/lib/draw/Makefile.am | 15 + main/lib/draw/cdraw.c | 1309 + main/lib/draw/cdraw.h | 45 + main/lib/draw/cpaint.c | 1801 + main/lib/draw/cpaint.h | 49 + main/lib/draw/gb.draw.h | 98 + main/lib/draw/gb.geom.h | 1 + main/lib/draw/gb.image.h | 1 + main/lib/draw/gb.paint.h | 243 + main/lib/draw/gb_list.c | 24 + main/lib/draw/main.c | 75 + main/lib/draw/main.h | 39 + main/lib/draw/matrix.c | 221 + main/lib/draw/matrix.h | 49 + main/lib/eval/Makefile.am | 29 + main/lib/eval/c_expression.c | 196 + main/lib/eval/c_expression.h | 49 + main/lib/eval/c_highlight.c | 316 + main/lib/eval/c_highlight.h | 36 + main/lib/eval/c_system.c | 110 + main/lib/eval/c_system.h | 35 + main/lib/eval/eval.c | 291 + main/lib/eval/eval.h | 62 + main/lib/eval/eval_analyze.c | 746 + main/lib/eval/eval_analyze.h | 32 + main/lib/eval/eval_code.c | 37 + main/lib/eval/eval_code.h | 110 + main/lib/eval/eval_read.c | 1409 + main/lib/eval/eval_read.h | 36 + main/lib/eval/eval_reserved.c | 26 + main/lib/eval/eval_trans.c | 458 + main/lib/eval/eval_trans.h | 73 + main/lib/eval/eval_trans_expr.c | 618 + main/lib/eval/eval_trans_tree.c | 713 + main/lib/eval/gb.eval.component | 20 + main/lib/eval/gb.eval.h | 118 + main/lib/eval/gb_alloc_override.h | 34 + main/lib/eval/gb_array.c | 26 + main/lib/eval/gb_error.c | 116 + main/lib/eval/gb_error.h | 59 + main/lib/eval/gb_table.c | 28 + main/lib/eval/main.c | 78 + main/lib/eval/main.h | 35 + main/lib/gb.component | 26 + main/lib/geom/Makefile.am | 10 + main/lib/geom/cpoint.c | 33 + main/lib/geom/cpoint.h | 54 + main/lib/geom/cpoint_temp.h | 258 + main/lib/geom/crect.c | 32 + main/lib/geom/crect.h | 60 + main/lib/geom/crect_temp.h | 404 + main/lib/geom/gb.geom.h | 105 + main/lib/geom/main.c | 67 + main/lib/geom/main.h | 37 + main/lib/gui.opengl/Makefile.am | 11 + main/lib/gui.opengl/gb.gui.opengl.component | 6 + main/lib/gui.opengl/main.c | 73 + main/lib/gui.opengl/main.h | 34 + main/lib/gui.qt.opengl/Makefile.am | 12 + .../gui.qt.opengl/gb.gui.qt.opengl.component | 4 + main/lib/gui.qt.opengl/main.c | 71 + main/lib/gui.qt.opengl/main.h | 34 + main/lib/gui.qt.webkit/Makefile.am | 12 + .../gui.qt.webkit/gb.gui.qt.webkit.component | 4 + main/lib/gui.qt.webkit/main.c | 71 + main/lib/gui.qt.webkit/main.h | 34 + main/lib/gui.qt/Makefile.am | 12 + main/lib/gui.qt/gb.gui.qt.component | 6 + main/lib/gui.qt/main.c | 158 + main/lib/gui.qt/main.h | 36 + main/lib/gui.trayicon/Makefile.am | 13 + main/lib/gui.trayicon/cfaketrayicon.c | 83 + main/lib/gui.trayicon/cfaketrayicon.h | 35 + .../gui.trayicon/gb.gui.trayicon.component | 5 + main/lib/gui.trayicon/main.c | 73 + main/lib/gui.trayicon/main.h | 34 + main/lib/gui/Makefile.am | 12 + main/lib/gui/gb.gui.component | 6 + main/lib/gui/main.c | 176 + main/lib/gui/main.h | 34 + main/lib/image.effect/CImage.cpp | 473 + main/lib/image.effect/CImage.h | 48 + main/lib/image.effect/Makefile.am | 29 + main/lib/image.effect/effect.cpp | 153 + main/lib/image.effect/effect.h | 44 + .../image.effect/gb.image.effect.component | 5 + main/lib/image.effect/kcpuinfo.cpp | 212 + main/lib/image.effect/kcpuinfo.h | 92 + main/lib/image.effect/kimageeffect.cpp | 5043 ++ main/lib/image.effect/kimageeffect.h | 811 + main/lib/image.effect/main.cpp | 51 + main/lib/image.effect/main.h | 36 + main/lib/image.effect/qcolor.cpp | 1021 + main/lib/image.effect/qcolor.h | 255 + main/lib/image.effect/qimage.cpp | 147 + main/lib/image.effect/qimage.h | 493 + main/lib/image.effect/qpoint.cpp | 463 + main/lib/image.effect/qpoint.h | 235 + main/lib/image.effect/qrect.cpp | 981 + main/lib/image.effect/qrect.h | 294 + main/lib/image.effect/qsize.cpp | 452 + main/lib/image.effect/qsize.h | 256 + main/lib/image.effect/qt.h | 57 + main/lib/image/CImage.c | 598 + main/lib/image/CImage.h | 48 + main/lib/image/CImageStat.c | 140 + main/lib/image/CImageStat.h | 51 + main/lib/image/Makefile.am | 24 + main/lib/image/c_color.c | 677 + main/lib/image/c_color.h | 52 + main/lib/image/gb.image.component | 5 + main/lib/image/gb.image.h | 171 + main/lib/image/image.c | 2438 + main/lib/image/image.h | 89 + main/lib/image/image_stat.c | 624 + main/lib/image/image_stat.h | 66 + main/lib/image/main.c | 82 + main/lib/image/main.h | 35 + main/lib/inotify/Makefile.am | 13 + main/lib/inotify/TODO | 5 + main/lib/inotify/c_watch.c | 697 + main/lib/inotify/c_watch.h | 46 + main/lib/inotify/gb.inotify.component | 3 + main/lib/inotify/gb_list.c | 24 + main/lib/inotify/main.c | 43 + main/lib/inotify/main.h | 32 + main/lib/jit/Makefile.am | 16 + main/lib/jit/gb.jit.component | 6 + main/lib/jit/gb.jit/.component | 4 + main/lib/jit/gb.jit/.directory | 2 + main/lib/jit/gb.jit/.icon.png | Bin 0 -> 10569 bytes main/lib/jit/gb.jit/.project | 13 + main/lib/jit/gb.jit/.src/Jit.module | 222 + main/lib/jit/gb.jit/.src/Main.module | 7 + main/lib/jit/gb.jit/.src/_ClassStat.class | 102 + main/lib/jit/gb.jit/gambas.h | 1 + main/lib/jit/gb.jit/gb.jit.h | 1 + main/lib/jit/gb.jit/gb_error_common.h | 1 + main/lib/jit/gb.jit/jit.h | 570 + main/lib/jit/gb_str.c | 227 + main/lib/jit/gb_str.h | 42 + main/lib/jit/gbc_reserved.c | 30 + main/lib/jit/jit.c | 517 + main/lib/jit/jit.h | 94 + main/lib/jit/jit_body.c | 3359 ++ main/lib/jit/main.c | 66 + main/lib/jit/main.h | 39 + main/lib/option/Makefile.am | 14 + main/lib/option/gb.option.component | 4 + main/lib/option/getoptions.c | 396 + main/lib/option/getoptions.h | 56 + main/lib/option/main.c | 90 + main/lib/option/main.h | 35 + main/lib/signal/Makefile.am | 14 + main/lib/signal/csignal.c | 261 + main/lib/signal/csignal.h | 34 + main/lib/signal/gb.signal.component | 3 + main/lib/signal/main.c | 48 + main/lib/signal/main.h | 34 + main/lib/term/Makefile.am | 14 + main/lib/term/cterm.c | 478 + main/lib/term/cterm.h | 44 + main/lib/term/gb.term.component | 3 + main/lib/term/main.c | 49 + main/lib/term/main.h | 36 + main/lib/vb/Makefile.am | 14 + main/lib/vb/gb.vb.component | 6 + main/lib/vb/main.c | 50 + main/lib/vb/main.h | 35 + main/lib/vb/vb.c | 312 + main/lib/vb/vb.h | 33 + main/lib/vb/vbdate.c | 215 + main/lib/vb/vbdate.h | 32 + main/m4 | 1 + main/mime/application-x-gambas3.png | Bin 0 -> 7788 bytes main/mime/application-x-gambas3.xml | 12 + main/reconf | 1 + main/share/Makefile.am | 1 + main/share/gambas.h | 1256 + main/share/gb_alloc.h | 117 + main/share/gb_alloc_temp.h | 493 + main/share/gb_arch.h | 90 + main/share/gb_arch_temp.h | 362 + main/share/gb_array.h | 117 + main/share/gb_array_temp.h | 223 + main/share/gb_buffer.h | 49 + main/share/gb_buffer_temp.h | 163 + main/share/gb_class_desc_common.h | 40 + main/share/gb_code.h | 156 + main/share/gb_code_temp.h | 1442 + main/share/gb_common.h | 212 + main/share/gb_common_buffer.h | 46 + main/share/gb_common_buffer_temp.h | 113 + main/share/gb_common_case.h | 60 + main/share/gb_common_case_temp.h | 104 + main/share/gb_common_string.h | 37 + main/share/gb_common_string_temp.h | 197 + main/share/gb_common_swap.h | 42 + main/share/gb_common_swap_temp.h | 71 + main/share/gb_component.h | 71 + main/share/gb_error_common.h | 213 + main/share/gb_file_share.h | 141 + main/share/gb_file_temp.h | 1287 + main/share/gb_hash.h | 106 + main/share/gb_hash_temp.h | 534 + main/share/gb_limit.h | 103 + main/share/gb_list.h | 43 + main/share/gb_list_temp.h | 90 + main/share/gb_magic.h | 35 + main/share/gb_pcode.h | 172 + main/share/gb_pcode_temp.h | 478 + main/share/gb_replace.h | 51 + main/share/gb_replace_temp.h | 133 + main/share/gb_reserved.h | 371 + main/share/gb_reserved_keyword.h | 586 + main/share/gb_reserved_temp.h | 966 + main/share/gb_table.h | 93 + main/share/gb_table_temp.h | 603 + main/share/gb_type_common.h | 92 + main/share/gbc_read_common.h | 93 + main/share/gbc_trans_common.h | 177 + main/share/gbx_subr_common.h | 680 + main/tools/gbh3/.directory | 2 + main/tools/gbh3/.icon.png | Bin 0 -> 3398 bytes main/tools/gbh3/.project | 12 + main/tools/gbh3/.src/MMain.module | 447 + main/tools/gbh3/.src/MOldMain.module | 256 + main/tools/gbh3/README | 112 + main/tools/gbh3/icon.png | Bin 0 -> 3211 bytes main/tools/gbh3/license | 6 + main/tools/gbh3/usage | 18 + reconf | 7 + reconf-all | 9 + version.m4 | 13 + 6752 files changed, 1290542 insertions(+) create mode 100644 AUTHORS create mode 100644 CONTRIBUTING.md create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 INSTALL.html create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 README.commit create mode 100644 README.md create mode 100644 TEMPLATE/README create mode 100644 TEMPLATE/TEMPLATE.c create mode 100644 TEMPLATE/TEMPLATE.conf create mode 100644 TEMPLATE/TEMPLATE.cpp create mode 100644 TEMPLATE/TEMPLATE.h create mode 100644 TEMPLATE/conf/gb.cairo.conf create mode 100644 TEMPLATE/conf/gb.dbus.conf create mode 100644 TEMPLATE/conf/gb.desktop.conf create mode 100644 TEMPLATE/conf/gb.gmp.conf create mode 100644 TEMPLATE/conf/gb.gsl.conf create mode 100644 TEMPLATE/conf/gb.image.io.conf create mode 100644 TEMPLATE/conf/gb.media.conf create mode 100644 TEMPLATE/conf/gb.mime.conf create mode 100644 TEMPLATE/conf/gb.net.pop3.conf create mode 100644 TEMPLATE/conf/gb.net.smtp.conf create mode 100644 TEMPLATE/conf/gb.openal.conf create mode 100755 TEMPLATE/make-component create mode 100644 TEMPLATE/template/AUTHORS create mode 100644 TEMPLATE/template/ChangeLog create mode 100644 TEMPLATE/template/Makefile.am create mode 100644 TEMPLATE/template/NEWS create mode 100644 TEMPLATE/template/README create mode 100644 TEMPLATE/template/SOURCES create mode 100644 TEMPLATE/template/configure.ac create mode 100755 TEMPLATE/template/make-component create mode 100644 TEMPLATE/template/src/.component create mode 100644 TEMPLATE/template/src/Makefile.am create mode 100644 TODO create mode 100644 VERSION create mode 100644 acinclude.m4 create mode 100644 app/AUTHORS create mode 120000 app/COPYING create mode 100644 app/ChangeLog create mode 100644 app/INSTALL create mode 100644 app/Makefile.am create mode 100644 app/NEWS create mode 100644 app/README create mode 100644 app/TODO create mode 120000 app/acinclude.m4 create mode 100644 app/configure.ac create mode 100644 app/desktop/gambas3.appdata.xml create mode 100644 app/desktop/gambas3.desktop create mode 100644 app/desktop/gambas3.png create mode 100644 app/desktop/gambas3.svg create mode 100644 app/examples/Basic/Blights/.directory create mode 100644 app/examples/Basic/Blights/.icon.png create mode 100644 app/examples/Basic/Blights/.lang/ca.mo create mode 100644 app/examples/Basic/Blights/.lang/ca.po create mode 100644 app/examples/Basic/Blights/.lang/cs.mo create mode 100644 app/examples/Basic/Blights/.lang/cs.po create mode 100644 app/examples/Basic/Blights/.lang/de.mo create mode 100644 app/examples/Basic/Blights/.lang/de.po create mode 100644 app/examples/Basic/Blights/.lang/es.mo create mode 100644 app/examples/Basic/Blights/.lang/es.po create mode 100644 app/examples/Basic/Blights/.lang/fr.po create mode 100644 app/examples/Basic/Blights/.lang/nl.mo create mode 100644 app/examples/Basic/Blights/.lang/nl.po create mode 100644 app/examples/Basic/Blights/.lang/sv.mo create mode 100644 app/examples/Basic/Blights/.lang/sv.po create mode 100644 app/examples/Basic/Blights/.project create mode 100644 app/examples/Basic/Blights/.src/win1.class create mode 100644 app/examples/Basic/Blights/.src/win1.form create mode 100644 app/examples/Basic/Blights/ampoule.png create mode 100644 app/examples/Basic/Blights/bloff.xpm create mode 100644 app/examples/Basic/Blights/blon.xpm create mode 100644 app/examples/Basic/Collection/.directory create mode 100644 app/examples/Basic/Collection/.icon.png create mode 100644 app/examples/Basic/Collection/.lang/ca.mo create mode 100644 app/examples/Basic/Collection/.lang/ca.po create mode 100644 app/examples/Basic/Collection/.lang/cs.mo create mode 100644 app/examples/Basic/Collection/.lang/cs.po create mode 100644 app/examples/Basic/Collection/.lang/de.mo create mode 100644 app/examples/Basic/Collection/.lang/de.po create mode 100644 app/examples/Basic/Collection/.lang/es.mo create mode 100644 app/examples/Basic/Collection/.lang/es.po create mode 100644 app/examples/Basic/Collection/.lang/nl.mo create mode 100644 app/examples/Basic/Collection/.lang/nl.po create mode 100644 app/examples/Basic/Collection/.project create mode 100644 app/examples/Basic/Collection/.src/CThing.class create mode 100644 app/examples/Basic/Collection/.src/FStart.class create mode 100644 app/examples/Basic/Collection/.src/FStart.form create mode 100644 app/examples/Basic/Collection/collection.png create mode 100644 app/examples/Basic/DragNDrop/.directory create mode 100644 app/examples/Basic/DragNDrop/.icon.png create mode 100644 app/examples/Basic/DragNDrop/.project create mode 100644 app/examples/Basic/DragNDrop/.src/FDragNDrop.class create mode 100644 app/examples/Basic/DragNDrop/.src/FDragNDrop.form create mode 100644 app/examples/Basic/DragNDrop/drop.png create mode 100644 app/examples/Basic/Object/.directory create mode 100644 app/examples/Basic/Object/.icon.png create mode 100644 app/examples/Basic/Object/.lang/ca.mo create mode 100644 app/examples/Basic/Object/.lang/ca.po create mode 100644 app/examples/Basic/Object/.lang/cs.mo create mode 100644 app/examples/Basic/Object/.lang/cs.po create mode 100644 app/examples/Basic/Object/.lang/de.mo create mode 100644 app/examples/Basic/Object/.lang/de.po create mode 100644 app/examples/Basic/Object/.lang/es.mo create mode 100644 app/examples/Basic/Object/.lang/es.po create mode 100644 app/examples/Basic/Object/.lang/nl.mo create mode 100644 app/examples/Basic/Object/.lang/nl.po create mode 100644 app/examples/Basic/Object/.project create mode 100644 app/examples/Basic/Object/.src/CThing.class create mode 100644 app/examples/Basic/Object/.src/FStart.class create mode 100644 app/examples/Basic/Object/.src/FStart.form create mode 100644 app/examples/Basic/Object/object.png create mode 100644 app/examples/Basic/Timer/.directory create mode 100644 app/examples/Basic/Timer/.icon.png create mode 100644 app/examples/Basic/Timer/.lang/ca.mo create mode 100644 app/examples/Basic/Timer/.lang/ca.po create mode 100644 app/examples/Basic/Timer/.lang/cs.mo create mode 100644 app/examples/Basic/Timer/.lang/cs.po create mode 100644 app/examples/Basic/Timer/.lang/de.mo create mode 100644 app/examples/Basic/Timer/.lang/de.po create mode 100644 app/examples/Basic/Timer/.lang/es.mo create mode 100644 app/examples/Basic/Timer/.lang/es.po create mode 100644 app/examples/Basic/Timer/.lang/nl.mo create mode 100644 app/examples/Basic/Timer/.lang/nl.po create mode 100644 app/examples/Basic/Timer/.project create mode 100644 app/examples/Basic/Timer/.src/FOtherTimer.class create mode 100644 app/examples/Basic/Timer/.src/FOtherTimer.form create mode 100644 app/examples/Basic/Timer/.src/FTimer.class create mode 100644 app/examples/Basic/Timer/.src/FTimer.form create mode 100644 app/examples/Basic/Timer/timer.png create mode 100644 app/examples/Control/ArrayOfControls/.directory create mode 100644 app/examples/Control/ArrayOfControls/.icon.png create mode 100644 app/examples/Control/ArrayOfControls/.lang/ca.mo create mode 100644 app/examples/Control/ArrayOfControls/.lang/ca.po create mode 100644 app/examples/Control/ArrayOfControls/.lang/cs.mo create mode 100644 app/examples/Control/ArrayOfControls/.lang/cs.po create mode 100644 app/examples/Control/ArrayOfControls/.lang/de.mo create mode 100644 app/examples/Control/ArrayOfControls/.lang/de.po create mode 100644 app/examples/Control/ArrayOfControls/.lang/nl.mo create mode 100644 app/examples/Control/ArrayOfControls/.lang/nl.po create mode 100644 app/examples/Control/ArrayOfControls/.project create mode 100644 app/examples/Control/ArrayOfControls/.src/FMain.class create mode 100644 app/examples/Control/ArrayOfControls/.src/FMain.form create mode 100644 app/examples/Control/ArrayOfControls/green.png create mode 100644 app/examples/Control/ArrayOfControls/green1.png create mode 100644 app/examples/Control/ArrayOfControls/phone.png create mode 100644 app/examples/Control/ArrayOfControls/red.png create mode 100644 app/examples/Control/ArrayOfControls/red1.png create mode 100644 app/examples/Control/Embedder/.directory create mode 100644 app/examples/Control/Embedder/.icon.png create mode 100644 app/examples/Control/Embedder/.lang/ca.mo create mode 100644 app/examples/Control/Embedder/.lang/ca.po create mode 100644 app/examples/Control/Embedder/.lang/cs.mo create mode 100644 app/examples/Control/Embedder/.lang/cs.po create mode 100644 app/examples/Control/Embedder/.lang/de.mo create mode 100644 app/examples/Control/Embedder/.lang/de.po create mode 100644 app/examples/Control/Embedder/.lang/es.mo create mode 100644 app/examples/Control/Embedder/.lang/es.po create mode 100644 app/examples/Control/Embedder/.lang/nl.mo create mode 100644 app/examples/Control/Embedder/.lang/nl.po create mode 100644 app/examples/Control/Embedder/.project create mode 100644 app/examples/Control/Embedder/.src/FMain.class create mode 100644 app/examples/Control/Embedder/.src/FMain.form create mode 100644 app/examples/Control/Embedder/embedder.png create mode 100644 app/examples/Control/HighlightEditor/.directory create mode 100644 app/examples/Control/HighlightEditor/.hidden/screenshots/2014-12-17.png create mode 100644 app/examples/Control/HighlightEditor/.icon.png create mode 100644 app/examples/Control/HighlightEditor/.lang/ca.mo create mode 100644 app/examples/Control/HighlightEditor/.lang/ca.po create mode 100644 app/examples/Control/HighlightEditor/.lang/cs.mo create mode 100644 app/examples/Control/HighlightEditor/.lang/cs.po create mode 100644 app/examples/Control/HighlightEditor/.lang/de.mo create mode 100644 app/examples/Control/HighlightEditor/.lang/de.po create mode 100644 app/examples/Control/HighlightEditor/.lang/es.mo create mode 100644 app/examples/Control/HighlightEditor/.lang/es.po create mode 100644 app/examples/Control/HighlightEditor/.lang/nl.mo create mode 100644 app/examples/Control/HighlightEditor/.lang/nl.po create mode 100644 app/examples/Control/HighlightEditor/.project create mode 100644 app/examples/Control/HighlightEditor/.src/FEditor.class create mode 100644 app/examples/Control/HighlightEditor/.src/FEditor.form create mode 100644 app/examples/Control/HighlightEditor/download.html create mode 100644 app/examples/Control/HighlightEditor/editor.png create mode 100644 app/examples/Control/LCDLabel/.directory create mode 100644 app/examples/Control/LCDLabel/.icon.png create mode 100644 app/examples/Control/LCDLabel/.project create mode 100644 app/examples/Control/LCDLabel/.src/FMain.class create mode 100644 app/examples/Control/LCDLabel/.src/FMain.form create mode 100644 app/examples/Control/LCDLabel/.src/TimeBox.class create mode 100644 app/examples/Control/LCDLabel/alarm.ogg create mode 100644 app/examples/Control/LCDLabel/lcdlabel.png create mode 100644 app/examples/Control/MapView/.directory create mode 100644 app/examples/Control/MapView/.hidden/mapview.png create mode 100644 app/examples/Control/MapView/.icon.png create mode 100644 app/examples/Control/MapView/.project create mode 100644 app/examples/Control/MapView/.src/FMain.class create mode 100644 app/examples/Control/MapView/.src/FMain.form create mode 100644 app/examples/Control/TextEdit/.directory create mode 100644 app/examples/Control/TextEdit/.icon.png create mode 100644 app/examples/Control/TextEdit/.lang/ca.mo create mode 100644 app/examples/Control/TextEdit/.lang/ca.po create mode 100644 app/examples/Control/TextEdit/.lang/cs.mo create mode 100644 app/examples/Control/TextEdit/.lang/cs.po create mode 100644 app/examples/Control/TextEdit/.lang/de.mo create mode 100644 app/examples/Control/TextEdit/.lang/de.po create mode 100644 app/examples/Control/TextEdit/.lang/es.mo create mode 100644 app/examples/Control/TextEdit/.lang/es.po create mode 100644 app/examples/Control/TextEdit/.lang/fr.mo create mode 100644 app/examples/Control/TextEdit/.lang/fr.po create mode 100644 app/examples/Control/TextEdit/.lang/nl.mo create mode 100644 app/examples/Control/TextEdit/.lang/nl.po create mode 100644 app/examples/Control/TextEdit/.lang/sv.mo create mode 100644 app/examples/Control/TextEdit/.lang/sv.po create mode 100644 app/examples/Control/TextEdit/.project create mode 100644 app/examples/Control/TextEdit/.src/FMain.class create mode 100644 app/examples/Control/TextEdit/.src/FMain.form create mode 100644 app/examples/Control/TextEdit/.src/frmShowHtml.class create mode 100644 app/examples/Control/TextEdit/.src/frmShowHtml.form create mode 100644 app/examples/Control/TextEdit/edit.png create mode 100644 app/examples/Control/TextEdit/text.html create mode 100644 app/examples/Control/TreeView/.directory create mode 100644 app/examples/Control/TreeView/.icon.png create mode 100644 app/examples/Control/TreeView/.lang/ca.mo create mode 100644 app/examples/Control/TreeView/.lang/ca.po create mode 100644 app/examples/Control/TreeView/.lang/cs.mo create mode 100644 app/examples/Control/TreeView/.lang/cs.po create mode 100644 app/examples/Control/TreeView/.lang/de.mo create mode 100644 app/examples/Control/TreeView/.lang/de.po create mode 100644 app/examples/Control/TreeView/.lang/es.mo create mode 100644 app/examples/Control/TreeView/.lang/es.po create mode 100644 app/examples/Control/TreeView/.lang/nl.mo create mode 100644 app/examples/Control/TreeView/.lang/nl.po create mode 100644 app/examples/Control/TreeView/.project create mode 100644 app/examples/Control/TreeView/.src/TreeViewExample.class create mode 100644 app/examples/Control/TreeView/.src/TreeViewExample.form create mode 100644 app/examples/Control/TreeView/Female.png create mode 100644 app/examples/Control/TreeView/Male.png create mode 100644 app/examples/Control/TreeView/treeview.png create mode 100644 app/examples/Control/Wizard/.directory create mode 100644 app/examples/Control/Wizard/.icon.png create mode 100644 app/examples/Control/Wizard/.lang/ca.mo create mode 100644 app/examples/Control/Wizard/.lang/ca.po create mode 100644 app/examples/Control/Wizard/.lang/cs.mo create mode 100644 app/examples/Control/Wizard/.lang/cs.po create mode 100644 app/examples/Control/Wizard/.lang/de.mo create mode 100644 app/examples/Control/Wizard/.lang/de.po create mode 100644 app/examples/Control/Wizard/.lang/nl.mo create mode 100644 app/examples/Control/Wizard/.lang/nl.po create mode 100644 app/examples/Control/Wizard/.project create mode 100644 app/examples/Control/Wizard/.src/FMain.class create mode 100644 app/examples/Control/Wizard/.src/FMain.form create mode 100644 app/examples/Control/Wizard/wizard.png create mode 100644 app/examples/Database/Database/.component create mode 100644 app/examples/Database/Database/.directory create mode 100644 app/examples/Database/Database/.icon.png create mode 100644 app/examples/Database/Database/.lang/ca.mo create mode 100644 app/examples/Database/Database/.lang/ca.po create mode 100644 app/examples/Database/Database/.lang/cs.mo create mode 100644 app/examples/Database/Database/.lang/cs.po create mode 100644 app/examples/Database/Database/.lang/de.mo create mode 100644 app/examples/Database/Database/.lang/de.po create mode 100644 app/examples/Database/Database/.lang/es.mo create mode 100644 app/examples/Database/Database/.lang/es.po create mode 100644 app/examples/Database/Database/.lang/nl.mo create mode 100644 app/examples/Database/Database/.lang/nl.po create mode 100644 app/examples/Database/Database/.project create mode 100644 app/examples/Database/Database/.src/FMain.class create mode 100644 app/examples/Database/Database/.src/FMain.form create mode 100644 app/examples/Database/Database/.src/FRequest.class create mode 100644 app/examples/Database/Database/.src/FRequest.form create mode 100644 app/examples/Database/Database/.src/FTest.class create mode 100644 app/examples/Database/Database/.src/FTest.form create mode 100644 app/examples/Database/Database/.src/Form1.class create mode 100644 app/examples/Database/Database/.src/Form1.form create mode 100644 app/examples/Database/Database/database.png create mode 100644 app/examples/Database/MySQLExample/.action/FBrowser.action create mode 100644 app/examples/Database/MySQLExample/.action/FNewIndex.action create mode 100644 app/examples/Database/MySQLExample/.action/FNewRoutine.action create mode 100644 app/examples/Database/MySQLExample/.action/FQuery.action create mode 100644 app/examples/Database/MySQLExample/.action/FResult.action create mode 100644 app/examples/Database/MySQLExample/.action/FScript.action create mode 100644 app/examples/Database/MySQLExample/.action/FTables.action create mode 100644 app/examples/Database/MySQLExample/.directory create mode 100644 app/examples/Database/MySQLExample/.icon.png create mode 100644 app/examples/Database/MySQLExample/.lang/ca.mo create mode 100644 app/examples/Database/MySQLExample/.lang/ca.po create mode 100644 app/examples/Database/MySQLExample/.lang/cs.mo create mode 100644 app/examples/Database/MySQLExample/.lang/cs.po create mode 100644 app/examples/Database/MySQLExample/.lang/de.mo create mode 100644 app/examples/Database/MySQLExample/.lang/de.po create mode 100644 app/examples/Database/MySQLExample/.lang/es.mo create mode 100644 app/examples/Database/MySQLExample/.lang/es.po create mode 100644 app/examples/Database/MySQLExample/.lang/fr.mo create mode 100644 app/examples/Database/MySQLExample/.lang/fr.po create mode 100644 app/examples/Database/MySQLExample/.lang/nl.mo create mode 100644 app/examples/Database/MySQLExample/.lang/nl.po create mode 100644 app/examples/Database/MySQLExample/.project create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.class create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.form create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.class create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.form create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.class create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.class create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.form create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.class create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.form create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.class create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.form create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.class create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.form create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.class create mode 100644 app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.form create mode 100644 app/examples/Database/MySQLExample/.src/FConnect.class create mode 100644 app/examples/Database/MySQLExample/.src/FConnect.form create mode 100644 app/examples/Database/MySQLExample/.src/FMessage.class create mode 100644 app/examples/Database/MySQLExample/.src/FMessage.form create mode 100644 app/examples/Database/MySQLExample/.src/FTables.class create mode 100644 app/examples/Database/MySQLExample/.src/FTables.form create mode 100644 app/examples/Database/MySQLExample/.src/modMain.module create mode 100644 app/examples/Database/MySQLExample/icons/16/Admin.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Blob.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Column_FK.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Database.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Datetime.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Field.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Function.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Index.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Lock.png create mode 100644 app/examples/Database/MySQLExample/icons/16/New.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Numeric.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Primarykey.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Refresh.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Routine.png create mode 100644 app/examples/Database/MySQLExample/icons/16/String.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Table.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Trigger.png create mode 100644 app/examples/Database/MySQLExample/icons/16/View.png create mode 100644 app/examples/Database/MySQLExample/icons/16/Warning.png create mode 100644 app/examples/Database/MySQLExample/icons/24/Null.png create mode 100644 app/examples/Database/PictureDatabase/.directory create mode 100644 app/examples/Database/PictureDatabase/.icon.png create mode 100644 app/examples/Database/PictureDatabase/.lang/ca.mo create mode 100644 app/examples/Database/PictureDatabase/.lang/ca.po create mode 100644 app/examples/Database/PictureDatabase/.lang/cs.mo create mode 100644 app/examples/Database/PictureDatabase/.lang/cs.po create mode 100644 app/examples/Database/PictureDatabase/.lang/de.mo create mode 100644 app/examples/Database/PictureDatabase/.lang/de.po create mode 100644 app/examples/Database/PictureDatabase/.lang/es.mo create mode 100644 app/examples/Database/PictureDatabase/.lang/es.po create mode 100644 app/examples/Database/PictureDatabase/.lang/nl.mo create mode 100644 app/examples/Database/PictureDatabase/.lang/nl.po create mode 100644 app/examples/Database/PictureDatabase/.project create mode 100644 app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class create mode 100644 app/examples/Database/PictureDatabase/.src/FormPictureDatabase.form create mode 100644 app/examples/Database/PictureDatabase/.src/ModuleDatabase.module create mode 100644 app/examples/Database/PictureDatabase/Images/document-save-as.png create mode 100644 app/examples/Database/PictureDatabase/Images/document-save.png create mode 100644 app/examples/Database/PictureDatabase/Images/image-x-generic.png create mode 100644 app/examples/Database/PictureDatabase/Images/list-add.png create mode 100644 app/examples/Database/PictureDatabase/Images/list-remove.png create mode 100644 app/examples/Drawing/AnalogWatch/.directory create mode 100644 app/examples/Drawing/AnalogWatch/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Drawing/AnalogWatch/.icon.png create mode 100644 app/examples/Drawing/AnalogWatch/.project create mode 100644 app/examples/Drawing/AnalogWatch/.src/FrmClock.class create mode 100644 app/examples/Drawing/AnalogWatch/.src/FrmClock.form create mode 100644 app/examples/Drawing/AnalogWatch/timer.png create mode 100644 app/examples/Drawing/Barcode/.directory create mode 100644 app/examples/Drawing/Barcode/.icon.png create mode 100644 app/examples/Drawing/Barcode/.lang/ca.mo create mode 100644 app/examples/Drawing/Barcode/.lang/ca.po create mode 100644 app/examples/Drawing/Barcode/.lang/cs.mo create mode 100644 app/examples/Drawing/Barcode/.lang/cs.po create mode 100644 app/examples/Drawing/Barcode/.lang/de.mo create mode 100644 app/examples/Drawing/Barcode/.lang/de.po create mode 100644 app/examples/Drawing/Barcode/.project create mode 100644 app/examples/Drawing/Barcode/.src/FMain.class create mode 100644 app/examples/Drawing/Barcode/.src/FMain.form create mode 100644 app/examples/Drawing/Barcode/.src/modCrBcode.module create mode 100644 app/examples/Drawing/Barcode/barcode.png create mode 100644 app/examples/Drawing/Chart/.directory create mode 100644 app/examples/Drawing/Chart/.icon.png create mode 100644 app/examples/Drawing/Chart/.lang/ca.mo create mode 100644 app/examples/Drawing/Chart/.lang/ca.po create mode 100644 app/examples/Drawing/Chart/.lang/cs.mo create mode 100644 app/examples/Drawing/Chart/.lang/cs.po create mode 100644 app/examples/Drawing/Chart/.lang/de.mo create mode 100644 app/examples/Drawing/Chart/.lang/de.po create mode 100644 app/examples/Drawing/Chart/.lang/es.mo create mode 100644 app/examples/Drawing/Chart/.lang/es.po create mode 100644 app/examples/Drawing/Chart/.project create mode 100644 app/examples/Drawing/Chart/.src/FormChart.class create mode 100644 app/examples/Drawing/Chart/.src/FormChart.form create mode 100644 app/examples/Drawing/Chart/.src/FormData.class create mode 100644 app/examples/Drawing/Chart/.src/FormData.form create mode 100644 app/examples/Drawing/Chart/graph.png create mode 100644 app/examples/Drawing/Clock/.directory create mode 100644 app/examples/Drawing/Clock/.icon.png create mode 100644 app/examples/Drawing/Clock/.icon/16.png create mode 100644 app/examples/Drawing/Clock/.icon/32.png create mode 100644 app/examples/Drawing/Clock/.icon/48.png create mode 100644 app/examples/Drawing/Clock/.lang/ca.mo create mode 100644 app/examples/Drawing/Clock/.lang/ca.po create mode 100644 app/examples/Drawing/Clock/.lang/cs.mo create mode 100644 app/examples/Drawing/Clock/.lang/cs.po create mode 100644 app/examples/Drawing/Clock/.lang/de.mo create mode 100644 app/examples/Drawing/Clock/.lang/de.po create mode 100644 app/examples/Drawing/Clock/.lang/es.mo create mode 100644 app/examples/Drawing/Clock/.lang/es.po create mode 100644 app/examples/Drawing/Clock/.project create mode 100644 app/examples/Drawing/Clock/.src/FClock.class create mode 100644 app/examples/Drawing/Clock/.src/FClock.form create mode 100644 app/examples/Drawing/Clock/img/arrow_hour.png create mode 100644 app/examples/Drawing/Clock/img/arrow_min.png create mode 100644 app/examples/Drawing/Clock/img/arrow_sec.png create mode 100644 app/examples/Drawing/Clock/img/clock_bg_big1.png create mode 100644 app/examples/Drawing/Clock/img/clock_bg_big2.png create mode 100644 app/examples/Drawing/Clock/img/clock_bg_big3.png create mode 100644 app/examples/Drawing/Clock/img/clock_bg_big4.png create mode 100644 app/examples/Drawing/Fractal/.directory create mode 100644 app/examples/Drawing/Fractal/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Drawing/Fractal/.icon.png create mode 100644 app/examples/Drawing/Fractal/.lang/cs.mo create mode 100644 app/examples/Drawing/Fractal/.lang/cs.po create mode 100644 app/examples/Drawing/Fractal/.lang/fr.mo create mode 100644 app/examples/Drawing/Fractal/.lang/fr.po create mode 100644 app/examples/Drawing/Fractal/.project create mode 100644 app/examples/Drawing/Fractal/.src/FFractal.class create mode 100644 app/examples/Drawing/Fractal/.src/FFractal.form create mode 100644 app/examples/Drawing/Fractal/.src/FractalTask.class create mode 100644 app/examples/Drawing/Fractal/icon.png create mode 100644 app/examples/Drawing/Fractal/rose.jpg create mode 100644 app/examples/Drawing/GSLSpline/.directory create mode 100644 app/examples/Drawing/GSLSpline/.icon.png create mode 100644 app/examples/Drawing/GSLSpline/.project create mode 100644 app/examples/Drawing/GSLSpline/.src/FMain.class create mode 100644 app/examples/Drawing/GSLSpline/.src/FMain.form create mode 100644 app/examples/Drawing/GSLSpline/spline.png create mode 100644 app/examples/Drawing/Gravity/.directory create mode 100644 app/examples/Drawing/Gravity/.icon.png create mode 100644 app/examples/Drawing/Gravity/.lang/ca.mo create mode 100644 app/examples/Drawing/Gravity/.lang/ca.po create mode 100644 app/examples/Drawing/Gravity/.lang/cs.mo create mode 100644 app/examples/Drawing/Gravity/.lang/cs.po create mode 100644 app/examples/Drawing/Gravity/.lang/de.mo create mode 100644 app/examples/Drawing/Gravity/.lang/de.po create mode 100644 app/examples/Drawing/Gravity/.lang/es.mo create mode 100644 app/examples/Drawing/Gravity/.lang/es.po create mode 100644 app/examples/Drawing/Gravity/.project create mode 100644 app/examples/Drawing/Gravity/.src/FAbout.class create mode 100644 app/examples/Drawing/Gravity/.src/FAbout.form create mode 100644 app/examples/Drawing/Gravity/.src/FMain.class create mode 100644 app/examples/Drawing/Gravity/.src/FMain.form create mode 100644 app/examples/Drawing/Gravity/.src/cBall.class create mode 100644 app/examples/Drawing/Gravity/gravity.png create mode 100644 app/examples/Drawing/OnScreenDisplay/.directory create mode 100644 app/examples/Drawing/OnScreenDisplay/.icon.png create mode 100644 app/examples/Drawing/OnScreenDisplay/.lang/ca.mo create mode 100644 app/examples/Drawing/OnScreenDisplay/.lang/ca.po create mode 100644 app/examples/Drawing/OnScreenDisplay/.lang/cs.mo create mode 100644 app/examples/Drawing/OnScreenDisplay/.lang/cs.po create mode 100644 app/examples/Drawing/OnScreenDisplay/.lang/de.mo create mode 100644 app/examples/Drawing/OnScreenDisplay/.lang/de.po create mode 100644 app/examples/Drawing/OnScreenDisplay/.lang/es.mo create mode 100644 app/examples/Drawing/OnScreenDisplay/.lang/es.po create mode 100644 app/examples/Drawing/OnScreenDisplay/.project create mode 100644 app/examples/Drawing/OnScreenDisplay/.src/FOnScreenDisplay.class create mode 100644 app/examples/Drawing/OnScreenDisplay/.src/FOnScreenDisplay.form create mode 100644 app/examples/Drawing/OnScreenDisplay/icon.png create mode 100644 app/examples/Drawing/Painting/.directory create mode 100644 app/examples/Drawing/Painting/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Drawing/Painting/.icon.png create mode 100644 app/examples/Drawing/Painting/.lang/ca.mo create mode 100644 app/examples/Drawing/Painting/.lang/ca.po create mode 100644 app/examples/Drawing/Painting/.lang/cs.mo create mode 100644 app/examples/Drawing/Painting/.lang/cs.po create mode 100644 app/examples/Drawing/Painting/.lang/de.mo create mode 100644 app/examples/Drawing/Painting/.lang/de.po create mode 100644 app/examples/Drawing/Painting/.project create mode 100644 app/examples/Drawing/Painting/.src/FMain.class create mode 100644 app/examples/Drawing/Painting/.src/FMain.form create mode 100644 app/examples/Drawing/Painting/.src/MMakeSourceFile.module create mode 100644 app/examples/Drawing/Painting/Example1 create mode 100644 app/examples/Drawing/Painting/Example10 create mode 100644 app/examples/Drawing/Painting/Example11 create mode 100644 app/examples/Drawing/Painting/Example12 create mode 100644 app/examples/Drawing/Painting/Example13 create mode 100644 app/examples/Drawing/Painting/Example14 create mode 100644 app/examples/Drawing/Painting/Example15 create mode 100644 app/examples/Drawing/Painting/Example16 create mode 100644 app/examples/Drawing/Painting/Example17 create mode 100644 app/examples/Drawing/Painting/Example18 create mode 100644 app/examples/Drawing/Painting/Example2 create mode 100644 app/examples/Drawing/Painting/Example20 create mode 100644 app/examples/Drawing/Painting/Example21 create mode 100644 app/examples/Drawing/Painting/Example22 create mode 100644 app/examples/Drawing/Painting/Example3 create mode 100644 app/examples/Drawing/Painting/Example4 create mode 100644 app/examples/Drawing/Painting/Example5 create mode 100644 app/examples/Drawing/Painting/Example6 create mode 100644 app/examples/Drawing/Painting/Example7 create mode 100644 app/examples/Drawing/Painting/Example8 create mode 100644 app/examples/Drawing/Painting/Example9 create mode 100644 app/examples/Drawing/Painting/clovis.jpg create mode 100644 app/examples/Drawing/Painting/gambas.svg create mode 100644 app/examples/Drawing/Painting/icon.png create mode 100644 app/examples/Drawing/Painting/image.jpg create mode 100644 app/examples/Drawing/QuasiRegular/.directory create mode 100644 app/examples/Drawing/QuasiRegular/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Drawing/QuasiRegular/.icon.png create mode 100644 app/examples/Drawing/QuasiRegular/.project create mode 100644 app/examples/Drawing/QuasiRegular/.src/FMain.class create mode 100644 app/examples/Drawing/QuasiRegular/.src/FMain.form create mode 100644 app/examples/Drawing/QuasiRegular/icon.png create mode 100644 app/examples/Drawing/RandomColorSort/.directory create mode 100644 app/examples/Drawing/RandomColorSort/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Drawing/RandomColorSort/.icon.png create mode 100644 app/examples/Drawing/RandomColorSort/.project create mode 100644 app/examples/Drawing/RandomColorSort/.src/FMain.class create mode 100644 app/examples/Drawing/RandomColorSort/.src/FMain.form create mode 100644 app/examples/Drawing/RandomColorSort/RandomColorSort.png create mode 100644 app/examples/Drawing/Tablet/.directory create mode 100644 app/examples/Drawing/Tablet/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Drawing/Tablet/.icon.png create mode 100644 app/examples/Drawing/Tablet/.project create mode 100644 app/examples/Drawing/Tablet/.src/FMain.class create mode 100644 app/examples/Drawing/Tablet/.src/FMain.form create mode 100644 app/examples/Drawing/Tablet/Icon.png create mode 100644 app/examples/Games/BeastScroll/.dir_icon.png create mode 100644 app/examples/Games/BeastScroll/.directory create mode 100644 app/examples/Games/BeastScroll/.icon.png create mode 100644 app/examples/Games/BeastScroll/.project create mode 100644 app/examples/Games/BeastScroll/.src/MMain.module create mode 100644 app/examples/Games/BeastScroll/b-title.mod create mode 100644 app/examples/Games/BeastScroll/bgd1_ciel.png create mode 100644 app/examples/Games/BeastScroll/bgd2_montagnes.png create mode 100644 app/examples/Games/BeastScroll/bgd3_sol1.png create mode 100644 app/examples/Games/BeastScroll/bgd4_sol2.png create mode 100644 app/examples/Games/BeastScroll/bgd5_sol3.png create mode 100644 app/examples/Games/BeastScroll/fireworks.png create mode 100644 app/examples/Games/BeastScroll/logo.png create mode 100644 app/examples/Games/BeastScroll/scrolltext.png create mode 100644 app/examples/Games/BeastScroll/sprite_arbre.png create mode 100644 app/examples/Games/BeastScroll/sprite_barriere.png create mode 100644 app/examples/Games/BeastScroll/sprite_nuages1.png create mode 100644 app/examples/Games/BeastScroll/sprite_nuages2.png create mode 100644 app/examples/Games/BeastScroll/sprite_nuages3.png create mode 100644 app/examples/Games/BeastScroll/sprite_nuages4.png create mode 100644 app/examples/Games/Concent/.directory create mode 100644 app/examples/Games/Concent/.icon.png create mode 100644 app/examples/Games/Concent/.icon/16.png create mode 100644 app/examples/Games/Concent/.icon/32.png create mode 100644 app/examples/Games/Concent/.icon/48.png create mode 100644 app/examples/Games/Concent/.lang/ca.mo create mode 100644 app/examples/Games/Concent/.lang/ca.po create mode 100644 app/examples/Games/Concent/.lang/cs.mo create mode 100644 app/examples/Games/Concent/.lang/cs.po create mode 100644 app/examples/Games/Concent/.lang/de.mo create mode 100644 app/examples/Games/Concent/.lang/de.po create mode 100644 app/examples/Games/Concent/.lang/en.mo create mode 100644 app/examples/Games/Concent/.lang/en.po create mode 100644 app/examples/Games/Concent/.lang/es.mo create mode 100644 app/examples/Games/Concent/.lang/es.po create mode 100644 app/examples/Games/Concent/.lang/fr.mo create mode 100644 app/examples/Games/Concent/.lang/fr.po create mode 100644 app/examples/Games/Concent/.project create mode 100644 app/examples/Games/Concent/.src/fotos.class create mode 100644 app/examples/Games/Concent/.src/fotos.form create mode 100644 app/examples/Games/Concent/.src/frmAcerca.class create mode 100644 app/examples/Games/Concent/.src/frmAcerca.form create mode 100644 app/examples/Games/Concent/.src/frmInstrucciones.class create mode 100644 app/examples/Games/Concent/.src/frmInstrucciones.form create mode 100644 app/examples/Games/Concent/.src/funciones.module create mode 100644 app/examples/Games/Concent/.src/principal.class create mode 100644 app/examples/Games/Concent/.src/principal.form create mode 100644 app/examples/Games/Concent/Blockhit.wav create mode 100644 app/examples/Games/Concent/CHANGELOG create mode 100644 app/examples/Games/Concent/Missed.wav create mode 100644 app/examples/Games/Concent/Newlevel.wav create mode 100644 app/examples/Games/Concent/Paddle.wav create mode 100644 app/examples/Games/Concent/Setup.wav create mode 100644 app/examples/Games/Concent/Wallhit.wav create mode 100644 app/examples/Games/Concent/applause.wav create mode 100644 app/examples/Games/Concent/imagenes/an1.gif create mode 100644 app/examples/Games/Concent/imagenes/an10.gif create mode 100644 app/examples/Games/Concent/imagenes/an11.gif create mode 100644 app/examples/Games/Concent/imagenes/an12.gif create mode 100644 app/examples/Games/Concent/imagenes/an13.gif create mode 100644 app/examples/Games/Concent/imagenes/an14.gif create mode 100644 app/examples/Games/Concent/imagenes/an15.gif create mode 100644 app/examples/Games/Concent/imagenes/an16.gif create mode 100644 app/examples/Games/Concent/imagenes/an17.gif create mode 100644 app/examples/Games/Concent/imagenes/an18.gif create mode 100644 app/examples/Games/Concent/imagenes/an19.gif create mode 100644 app/examples/Games/Concent/imagenes/an2.gif create mode 100644 app/examples/Games/Concent/imagenes/an20.gif create mode 100644 app/examples/Games/Concent/imagenes/an21.gif create mode 100644 app/examples/Games/Concent/imagenes/an22.gif create mode 100644 app/examples/Games/Concent/imagenes/an23.gif create mode 100644 app/examples/Games/Concent/imagenes/an24.gif create mode 100644 app/examples/Games/Concent/imagenes/an25.gif create mode 100644 app/examples/Games/Concent/imagenes/an26.gif create mode 100644 app/examples/Games/Concent/imagenes/an27.gif create mode 100644 app/examples/Games/Concent/imagenes/an28.gif create mode 100644 app/examples/Games/Concent/imagenes/an29.gif create mode 100644 app/examples/Games/Concent/imagenes/an3.gif create mode 100644 app/examples/Games/Concent/imagenes/an30.gif create mode 100644 app/examples/Games/Concent/imagenes/an31.gif create mode 100644 app/examples/Games/Concent/imagenes/an32.gif create mode 100644 app/examples/Games/Concent/imagenes/an33.gif create mode 100644 app/examples/Games/Concent/imagenes/an34.gif create mode 100644 app/examples/Games/Concent/imagenes/an35.gif create mode 100644 app/examples/Games/Concent/imagenes/an36.gif create mode 100644 app/examples/Games/Concent/imagenes/an37.gif create mode 100644 app/examples/Games/Concent/imagenes/an38.gif create mode 100644 app/examples/Games/Concent/imagenes/an39.gif create mode 100644 app/examples/Games/Concent/imagenes/an4.gif create mode 100644 app/examples/Games/Concent/imagenes/an40.gif create mode 100644 app/examples/Games/Concent/imagenes/an5.gif create mode 100644 app/examples/Games/Concent/imagenes/an6.gif create mode 100644 app/examples/Games/Concent/imagenes/an7.gif create mode 100644 app/examples/Games/Concent/imagenes/an8.gif create mode 100644 app/examples/Games/Concent/imagenes/an9.gif create mode 100644 app/examples/Games/Concent/imagenes/colombia.gif create mode 100644 app/examples/Games/Concent/imagenes/inter.gif create mode 100644 app/examples/Games/Concent/imagenes/inter.jpg create mode 100644 app/examples/Games/Concent/imagenes/logo.gif create mode 100644 app/examples/Games/Concent/imagenes/logo.png create mode 100644 app/examples/Games/Concent/imagenes/ok.gif create mode 100644 app/examples/Games/Concent/imagenes/tierra.gif create mode 100644 app/examples/Games/Concent/imagenes/tierra3.jpg create mode 100644 app/examples/Games/Concent/move.wav create mode 100644 app/examples/Games/Concent/shuffle.wav create mode 100644 app/examples/Games/DeepSpace/.directory create mode 100644 app/examples/Games/DeepSpace/.icon.png create mode 100644 app/examples/Games/DeepSpace/.lang/ca.mo create mode 100644 app/examples/Games/DeepSpace/.lang/ca.po create mode 100644 app/examples/Games/DeepSpace/.lang/cs.mo create mode 100644 app/examples/Games/DeepSpace/.lang/cs.po create mode 100644 app/examples/Games/DeepSpace/.lang/de.mo create mode 100644 app/examples/Games/DeepSpace/.lang/de.po create mode 100644 app/examples/Games/DeepSpace/.lang/es.mo create mode 100644 app/examples/Games/DeepSpace/.lang/es.po create mode 100644 app/examples/Games/DeepSpace/.project create mode 100644 app/examples/Games/DeepSpace/.src/CBullet.class create mode 100644 app/examples/Games/DeepSpace/.src/CObject.class create mode 100644 app/examples/Games/DeepSpace/.src/FAbout.class create mode 100644 app/examples/Games/DeepSpace/.src/FAbout.form create mode 100644 app/examples/Games/DeepSpace/.src/FMain.class create mode 100644 app/examples/Games/DeepSpace/.src/FMain.form create mode 100644 app/examples/Games/DeepSpace/.src/MMain.module create mode 100644 app/examples/Games/DeepSpace/.src/MMath.module create mode 100644 app/examples/Games/DeepSpace/doc/.html_files/eg3.gif create mode 100644 app/examples/Games/DeepSpace/doc/.html_files/eg3b.gif create mode 100644 app/examples/Games/DeepSpace/doc/coordinates.html create mode 100644 app/examples/Games/DeepSpace/doc/howto.txt create mode 100644 app/examples/Games/DeepSpace/doc/todo.txt create mode 100644 app/examples/Games/DeepSpace/images/deepspace.png create mode 100644 app/examples/Games/DeepSpace/object.data/kite.2do create mode 100644 app/examples/Games/DeepSpace/object.data/main.lst create mode 100644 app/examples/Games/DeepSpace/object.data/ship.2do create mode 100644 app/examples/Games/DeepSpace/object.data/triangle.2do create mode 100644 app/examples/Games/DeepSpace/object.data/x-wing.2do create mode 100644 app/examples/Games/GNUBoxWorld/.directory create mode 100644 app/examples/Games/GNUBoxWorld/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Games/GNUBoxWorld/.icon.png create mode 100644 app/examples/Games/GNUBoxWorld/.lang/ca.mo create mode 100644 app/examples/Games/GNUBoxWorld/.lang/ca.po create mode 100644 app/examples/Games/GNUBoxWorld/.lang/cs.mo create mode 100644 app/examples/Games/GNUBoxWorld/.lang/cs.po create mode 100644 app/examples/Games/GNUBoxWorld/.lang/de.mo create mode 100644 app/examples/Games/GNUBoxWorld/.lang/de.po create mode 100644 app/examples/Games/GNUBoxWorld/.lang/es_AR.mo create mode 100644 app/examples/Games/GNUBoxWorld/.lang/es_AR.po create mode 100644 app/examples/Games/GNUBoxWorld/.project create mode 100644 app/examples/Games/GNUBoxWorld/.src/Cell.class create mode 100644 app/examples/Games/GNUBoxWorld/.src/FMain.class create mode 100644 app/examples/Games/GNUBoxWorld/.src/FMain.form create mode 100644 app/examples/Games/GNUBoxWorld/.src/FrmAbout.class create mode 100644 app/examples/Games/GNUBoxWorld/.src/FrmAbout.form create mode 100644 app/examples/Games/GNUBoxWorld/.src/GameBoard.class create mode 100644 app/examples/Games/GNUBoxWorld/License create mode 100644 app/examples/Games/GNUBoxWorld/abajo.png create mode 100644 app/examples/Games/GNUBoxWorld/arriba.png create mode 100644 app/examples/Games/GNUBoxWorld/derecha.png create mode 100644 app/examples/Games/GNUBoxWorld/destino.png create mode 100644 app/examples/Games/GNUBoxWorld/ganador.png create mode 100644 app/examples/Games/GNUBoxWorld/icon.png create mode 100644 app/examples/Games/GNUBoxWorld/izquierda.png create mode 100644 app/examples/Games/GNUBoxWorld/logo.png create mode 100644 app/examples/Games/GNUBoxWorld/movible.png create mode 100644 app/examples/Games/GNUBoxWorld/movibleendestino.png create mode 100644 app/examples/Games/GNUBoxWorld/obstaculo-l.png create mode 100644 app/examples/Games/GNUBoxWorld/obstaculo-lr.png create mode 100644 app/examples/Games/GNUBoxWorld/obstaculo-r.png create mode 100644 app/examples/Games/GNUBoxWorld/obstaculo.png create mode 100644 app/examples/Games/GNUBoxWorld/piso.png create mode 100644 app/examples/Games/GameOfLife/.debug create mode 100644 app/examples/Games/GameOfLife/.directory create mode 100644 app/examples/Games/GameOfLife/.icon.png create mode 100644 app/examples/Games/GameOfLife/.lang/ca.mo create mode 100644 app/examples/Games/GameOfLife/.lang/ca.po create mode 100644 app/examples/Games/GameOfLife/.lang/cs.mo create mode 100644 app/examples/Games/GameOfLife/.lang/cs.po create mode 100644 app/examples/Games/GameOfLife/.lang/de.mo create mode 100644 app/examples/Games/GameOfLife/.lang/de.po create mode 100644 app/examples/Games/GameOfLife/.project create mode 100644 app/examples/Games/GameOfLife/.src/CGameField.class create mode 100644 app/examples/Games/GameOfLife/.src/FMain.class create mode 100644 app/examples/Games/GameOfLife/.src/FMain.form create mode 100644 app/examples/Games/GameOfLife/glob2-icon-48x48.png create mode 100644 app/examples/Games/Invaders/.directory create mode 100644 app/examples/Games/Invaders/.icon.png create mode 100644 app/examples/Games/Invaders/.project create mode 100644 app/examples/Games/Invaders/.src/Enemies.class create mode 100644 app/examples/Games/Invaders/.src/Enemy.class create mode 100644 app/examples/Games/Invaders/.src/MMain.module create mode 100644 app/examples/Games/Invaders/.src/Missile.class create mode 100644 app/examples/Games/Invaders/.src/Missiles.class create mode 100644 app/examples/Games/Invaders/invaders.png create mode 100644 app/examples/Games/MineSweeper/.directory create mode 100644 app/examples/Games/MineSweeper/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Games/MineSweeper/.icon.png create mode 100644 app/examples/Games/MineSweeper/.lang/cs.mo create mode 100644 app/examples/Games/MineSweeper/.lang/cs.po create mode 100644 app/examples/Games/MineSweeper/.lang/ja.mo create mode 100644 app/examples/Games/MineSweeper/.lang/ja.po create mode 100644 app/examples/Games/MineSweeper/.lang/zh.mo create mode 100644 app/examples/Games/MineSweeper/.lang/zh.po create mode 100644 app/examples/Games/MineSweeper/.lang/zh_TW.mo create mode 100644 app/examples/Games/MineSweeper/.lang/zh_TW.po create mode 100644 app/examples/Games/MineSweeper/.project create mode 100644 app/examples/Games/MineSweeper/.src/FMain.class create mode 100644 app/examples/Games/MineSweeper/.src/FMain.form create mode 100644 app/examples/Games/MineSweeper/.src/FSettings.class create mode 100644 app/examples/Games/MineSweeper/.src/FSettings.form create mode 100644 app/examples/Games/MineSweeper/.src/MineSweeperGame.class create mode 100644 app/examples/Games/MineSweeper/image/bigflag.png create mode 100644 app/examples/Games/MineSweeper/image/cover.png create mode 100644 app/examples/Games/MineSweeper/image/coveron.png create mode 100644 app/examples/Games/MineSweeper/image/empty.png create mode 100644 app/examples/Games/MineSweeper/image/expr_lose.png create mode 100644 app/examples/Games/MineSweeper/image/expr_normal.png create mode 100644 app/examples/Games/MineSweeper/image/expr_o.png create mode 100644 app/examples/Games/MineSweeper/image/expr_win.png create mode 100644 app/examples/Games/MineSweeper/image/false.png create mode 100644 app/examples/Games/MineSweeper/image/flag.png create mode 100644 app/examples/Games/MineSweeper/image/mine.png create mode 100644 app/examples/Games/MineSweeper/image/number_1.png create mode 100644 app/examples/Games/MineSweeper/image/number_2.png create mode 100644 app/examples/Games/MineSweeper/image/number_3.png create mode 100644 app/examples/Games/MineSweeper/image/number_4.png create mode 100644 app/examples/Games/MineSweeper/image/number_5.png create mode 100644 app/examples/Games/MineSweeper/image/number_6.png create mode 100644 app/examples/Games/MineSweeper/image/number_7.png create mode 100644 app/examples/Games/MineSweeper/image/number_8.png create mode 100644 app/examples/Games/Pong/.directory create mode 100644 app/examples/Games/Pong/.icon.png create mode 100644 app/examples/Games/Pong/.project create mode 100644 app/examples/Games/Pong/.src/Ball.class create mode 100644 app/examples/Games/Pong/.src/MMain.module create mode 100644 app/examples/Games/Pong/.src/NPC.class create mode 100644 app/examples/Games/Pong/.src/Paddle.class create mode 100644 app/examples/Games/Pong/SPEED create mode 100644 app/examples/Games/Pong/pong.png create mode 100644 app/examples/Games/Puzzle1To8/.directory create mode 100644 app/examples/Games/Puzzle1To8/.icon.png create mode 100644 app/examples/Games/Puzzle1To8/.lang/ca.mo create mode 100644 app/examples/Games/Puzzle1To8/.lang/ca.po create mode 100644 app/examples/Games/Puzzle1To8/.lang/cs.mo create mode 100644 app/examples/Games/Puzzle1To8/.lang/cs.po create mode 100644 app/examples/Games/Puzzle1To8/.lang/de.mo create mode 100644 app/examples/Games/Puzzle1To8/.lang/de.po create mode 100644 app/examples/Games/Puzzle1To8/.lang/es_AR.mo create mode 100644 app/examples/Games/Puzzle1To8/.lang/es_AR.po create mode 100644 app/examples/Games/Puzzle1To8/.project create mode 100644 app/examples/Games/Puzzle1To8/.src/Casillero.class create mode 100644 app/examples/Games/Puzzle1To8/.src/Esquema.class create mode 100644 app/examples/Games/Puzzle1To8/.src/FMain.class create mode 100644 app/examples/Games/Puzzle1To8/.src/FMain.form create mode 100644 app/examples/Games/Puzzle1To8/.src/FrmAbout.class create mode 100644 app/examples/Games/Puzzle1To8/.src/FrmAbout.form create mode 100644 app/examples/Games/Puzzle1To8/.src/FrmAyuda.class create mode 100644 app/examples/Games/Puzzle1To8/.src/FrmAyuda.form create mode 100644 app/examples/Games/Puzzle1To8/Licence create mode 100644 app/examples/Games/Puzzle1To8/ejemplo1.png create mode 100644 app/examples/Games/Puzzle1To8/ejemplo2.png create mode 100644 app/examples/Games/Puzzle1To8/logo.png create mode 100644 app/examples/Games/RobotFindsKitten/.directory create mode 100644 app/examples/Games/RobotFindsKitten/.icon.png create mode 100644 app/examples/Games/RobotFindsKitten/.lang/ca.mo create mode 100644 app/examples/Games/RobotFindsKitten/.lang/ca.po create mode 100644 app/examples/Games/RobotFindsKitten/.lang/cs.mo create mode 100644 app/examples/Games/RobotFindsKitten/.lang/cs.po create mode 100644 app/examples/Games/RobotFindsKitten/.lang/de.mo create mode 100644 app/examples/Games/RobotFindsKitten/.lang/de.po create mode 100644 app/examples/Games/RobotFindsKitten/.lang/es.mo create mode 100644 app/examples/Games/RobotFindsKitten/.lang/es.po create mode 100644 app/examples/Games/RobotFindsKitten/.project create mode 100644 app/examples/Games/RobotFindsKitten/.src/Frfk.class create mode 100644 app/examples/Games/RobotFindsKitten/.src/Frfk.form create mode 100644 app/examples/Games/RobotFindsKitten/COPYING create mode 100644 app/examples/Games/RobotFindsKitten/heart.png create mode 100644 app/examples/Games/RobotFindsKitten/nkis.txt create mode 100644 app/examples/Games/RobotFindsKitten/readme.txt create mode 100644 app/examples/Games/Snake/.directory create mode 100644 app/examples/Games/Snake/.icon.png create mode 100644 app/examples/Games/Snake/.lang/ca.mo create mode 100644 app/examples/Games/Snake/.lang/ca.po create mode 100644 app/examples/Games/Snake/.lang/cs.mo create mode 100644 app/examples/Games/Snake/.lang/cs.po create mode 100644 app/examples/Games/Snake/.lang/de.mo create mode 100644 app/examples/Games/Snake/.lang/de.po create mode 100644 app/examples/Games/Snake/.project create mode 100644 app/examples/Games/Snake/.src/FrmMain.class create mode 100644 app/examples/Games/Snake/.src/FrmMain.form create mode 100644 app/examples/Games/Snake/apple.png create mode 100644 app/examples/Games/Snake/body.png create mode 100644 app/examples/Games/Snake/dead.wav create mode 100644 app/examples/Games/Snake/eat.wav create mode 100644 app/examples/Games/Snake/head.png create mode 100644 app/examples/Games/Snake/start.wav create mode 100644 app/examples/Games/Solitaire/.directory create mode 100644 app/examples/Games/Solitaire/.icon.png create mode 100644 app/examples/Games/Solitaire/.lang/ca.mo create mode 100644 app/examples/Games/Solitaire/.lang/ca.po create mode 100644 app/examples/Games/Solitaire/.lang/cs.mo create mode 100644 app/examples/Games/Solitaire/.lang/cs.po create mode 100644 app/examples/Games/Solitaire/.lang/de.mo create mode 100644 app/examples/Games/Solitaire/.lang/de.po create mode 100644 app/examples/Games/Solitaire/.lang/es.mo create mode 100644 app/examples/Games/Solitaire/.lang/es.po create mode 100644 app/examples/Games/Solitaire/.project create mode 100644 app/examples/Games/Solitaire/.src/CBoardDesign.class create mode 100644 app/examples/Games/Solitaire/.src/CMove.class create mode 100644 app/examples/Games/Solitaire/.src/FBoardSelect.class create mode 100644 app/examples/Games/Solitaire/.src/FBoardSelect.form create mode 100644 app/examples/Games/Solitaire/.src/FGameArea.class create mode 100644 app/examples/Games/Solitaire/.src/FGameArea.form create mode 100644 app/examples/Games/Solitaire/.src/Global.class create mode 100644 app/examples/Games/Solitaire/.src/MBoards.module create mode 100644 app/examples/Games/Solitaire/ball.png create mode 100644 app/examples/Games/Solitaire/new.png create mode 100644 app/examples/Games/Solitaire/quit.png create mode 100644 app/examples/Games/Solitaire/redo.png create mode 100644 app/examples/Games/Solitaire/undo.png create mode 100644 app/examples/Games/StarField/.directory create mode 100644 app/examples/Games/StarField/.icon.png create mode 100644 app/examples/Games/StarField/.project create mode 100644 app/examples/Games/StarField/.src/MMain.module create mode 100644 app/examples/Games/StarField/enterprise.png create mode 100644 app/examples/Games/StarField/logo.png create mode 100644 app/examples/Image/ImageViewer/.directory create mode 100644 app/examples/Image/ImageViewer/.icon.png create mode 100644 app/examples/Image/ImageViewer/.lang/ca.mo create mode 100644 app/examples/Image/ImageViewer/.lang/ca.po create mode 100644 app/examples/Image/ImageViewer/.lang/cs.mo create mode 100644 app/examples/Image/ImageViewer/.lang/cs.po create mode 100644 app/examples/Image/ImageViewer/.lang/de.mo create mode 100644 app/examples/Image/ImageViewer/.lang/de.po create mode 100644 app/examples/Image/ImageViewer/.lang/es.mo create mode 100644 app/examples/Image/ImageViewer/.lang/es.po create mode 100644 app/examples/Image/ImageViewer/.lang/nl.mo create mode 100644 app/examples/Image/ImageViewer/.lang/nl.po create mode 100644 app/examples/Image/ImageViewer/.project create mode 100644 app/examples/Image/ImageViewer/.src/FViewer.class create mode 100644 app/examples/Image/ImageViewer/.src/FViewer.form create mode 100644 app/examples/Image/ImageViewer/image.png create mode 100644 app/examples/Image/ImageViewer/test.png create mode 100644 app/examples/Image/Lighttable/.action/FMain.action create mode 100644 app/examples/Image/Lighttable/.directory create mode 100644 app/examples/Image/Lighttable/.icon.png create mode 100644 app/examples/Image/Lighttable/.lang/ca.mo create mode 100644 app/examples/Image/Lighttable/.lang/ca.po create mode 100644 app/examples/Image/Lighttable/.lang/cs.mo create mode 100644 app/examples/Image/Lighttable/.lang/cs.po create mode 100644 app/examples/Image/Lighttable/.lang/de.mo create mode 100644 app/examples/Image/Lighttable/.lang/de.po create mode 100644 app/examples/Image/Lighttable/.lang/en.mo create mode 100644 app/examples/Image/Lighttable/.lang/en.po create mode 100644 app/examples/Image/Lighttable/.lang/nl.mo create mode 100644 app/examples/Image/Lighttable/.lang/nl.po create mode 100644 app/examples/Image/Lighttable/.project create mode 100644 app/examples/Image/Lighttable/.src/FHelp.class create mode 100644 app/examples/Image/Lighttable/.src/FHelp.form create mode 100644 app/examples/Image/Lighttable/.src/FInfo.class create mode 100644 app/examples/Image/Lighttable/.src/FInfo.form create mode 100644 app/examples/Image/Lighttable/.src/FMain.class create mode 100644 app/examples/Image/Lighttable/.src/FMain.form create mode 100644 app/examples/Image/Lighttable/.src/FRename.class create mode 100644 app/examples/Image/Lighttable/.src/FRename.form create mode 100644 app/examples/Image/Lighttable/.src/FRenameAll.class create mode 100644 app/examples/Image/Lighttable/.src/FRenameAll.form create mode 100644 app/examples/Image/Lighttable/.src/FRenameAllWarning.class create mode 100644 app/examples/Image/Lighttable/.src/FRenameAllWarning.form create mode 100644 app/examples/Image/Lighttable/.src/FSlideshow.class create mode 100644 app/examples/Image/Lighttable/.src/FSlideshow.form create mode 100644 app/examples/Image/Lighttable/.src/FStart.class create mode 100644 app/examples/Image/Lighttable/.src/FStart.form create mode 100644 app/examples/Image/Lighttable/.src/FTime.class create mode 100644 app/examples/Image/Lighttable/.src/FTime.form create mode 100644 app/examples/Image/Lighttable/.src/MMain.module create mode 100644 app/examples/Image/Lighttable/CHANGELOG create mode 100644 app/examples/Image/Lighttable/FStart.class create mode 100644 app/examples/Image/Lighttable/FStart.form create mode 100644 app/examples/Image/Lighttable/Help_ca.html create mode 100644 app/examples/Image/Lighttable/Help_de.html create mode 100644 app/examples/Image/Lighttable/Help_en.html create mode 100644 app/examples/Image/Lighttable/LTicon.png create mode 100644 app/examples/Image/Lighttable/Liesmich.txt create mode 100644 app/examples/Image/Lighttable/Readme.txt create mode 100644 app/examples/Image/Lighttable/close.png create mode 100644 app/examples/Image/Lighttable/hand1.png create mode 100644 app/examples/Image/Lighttable/help-contents.png create mode 100644 app/examples/Image/Lighttable/lighttable.png create mode 100644 app/examples/Image/Lighttable/move.png create mode 100644 app/examples/Image/Lighttable/zoom-in.png create mode 100644 app/examples/Image/PhotoTouch/.directory create mode 100644 app/examples/Image/PhotoTouch/.hidden/screenshots/phototouch.jpg create mode 100644 app/examples/Image/PhotoTouch/.icon.png create mode 100644 app/examples/Image/PhotoTouch/.lang/fr.mo create mode 100644 app/examples/Image/PhotoTouch/.lang/fr.po create mode 100644 app/examples/Image/PhotoTouch/.lang/nl.mo create mode 100644 app/examples/Image/PhotoTouch/.lang/nl.po create mode 100644 app/examples/Image/PhotoTouch/.project create mode 100644 app/examples/Image/PhotoTouch/.src/CAnimation.class create mode 100644 app/examples/Image/PhotoTouch/.src/CButton.class create mode 100644 app/examples/Image/PhotoTouch/.src/FBrightness.class create mode 100644 app/examples/Image/PhotoTouch/.src/FBrightness.form create mode 100644 app/examples/Image/PhotoTouch/.src/FMain.class create mode 100644 app/examples/Image/PhotoTouch/.src/FMain.form create mode 100644 app/examples/Image/PhotoTouch/.src/FResize.class create mode 100644 app/examples/Image/PhotoTouch/.src/FResize.form create mode 100644 app/examples/Image/PhotoTouch/.src/FScissors.class create mode 100644 app/examples/Image/PhotoTouch/.src/FScissors.form create mode 100644 app/examples/Image/PhotoTouch/balance.png create mode 100644 app/examples/Image/PhotoTouch/blur.png create mode 100644 app/examples/Image/PhotoTouch/brightness.png create mode 100644 app/examples/Image/PhotoTouch/contrast.png create mode 100644 app/examples/Image/PhotoTouch/delete.png create mode 100644 app/examples/Image/PhotoTouch/despeckle.png create mode 100644 app/examples/Image/PhotoTouch/film.png create mode 100644 app/examples/Image/PhotoTouch/gamma.png create mode 100644 app/examples/Image/PhotoTouch/hflip.png create mode 100644 app/examples/Image/PhotoTouch/icon.png create mode 100644 app/examples/Image/PhotoTouch/invert.png create mode 100644 app/examples/Image/PhotoTouch/magic.png create mode 100644 app/examples/Image/PhotoTouch/next.png create mode 100644 app/examples/Image/PhotoTouch/normalize.png create mode 100644 app/examples/Image/PhotoTouch/oil.png create mode 100644 app/examples/Image/PhotoTouch/ok.png create mode 100644 app/examples/Image/PhotoTouch/photo.png create mode 100644 app/examples/Image/PhotoTouch/previous.png create mode 100644 app/examples/Image/PhotoTouch/quit.png create mode 100644 app/examples/Image/PhotoTouch/redo.png create mode 100644 app/examples/Image/PhotoTouch/resize.png create mode 100644 app/examples/Image/PhotoTouch/rotate-left.png create mode 100644 app/examples/Image/PhotoTouch/rotate-right.png create mode 100644 app/examples/Image/PhotoTouch/save-all.png create mode 100644 app/examples/Image/PhotoTouch/save.png create mode 100644 app/examples/Image/PhotoTouch/scissors.png create mode 100644 app/examples/Image/PhotoTouch/sharpen.png create mode 100644 app/examples/Image/PhotoTouch/undo.png create mode 100644 app/examples/Image/PhotoTouch/usb.png create mode 100644 app/examples/Image/PhotoTouch/vflip.png create mode 100644 app/examples/Image/PhotoTouch/zoom-fit.png create mode 100644 app/examples/Image/PhotoTouch/zoom-in.png create mode 100644 app/examples/Image/PhotoTouch/zoom-original.png create mode 100644 app/examples/Image/PhotoTouch/zoom-out.png create mode 100644 app/examples/Misc/Console/.directory create mode 100644 app/examples/Misc/Console/.icon.png create mode 100644 app/examples/Misc/Console/.lang/fr.mo create mode 100644 app/examples/Misc/Console/.lang/fr.po create mode 100644 app/examples/Misc/Console/.project create mode 100644 app/examples/Misc/Console/.src/FConsole.class create mode 100644 app/examples/Misc/Console/.src/FConsole.form create mode 100644 app/examples/Misc/Console/terminal.png create mode 100644 app/examples/Misc/DBusExplorer/.directory create mode 100644 app/examples/Misc/DBusExplorer/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Misc/DBusExplorer/.icon.png create mode 100644 app/examples/Misc/DBusExplorer/.project create mode 100644 app/examples/Misc/DBusExplorer/.src/FVersiongbXML.class create mode 100644 app/examples/Misc/DBusExplorer/.src/FVersiongbXML.form create mode 100644 app/examples/Misc/DBusExplorer/dbus22.png create mode 100644 app/examples/Misc/DBusExplorer/dbus64.png create mode 100644 app/examples/Misc/DBusExplorer/method.png create mode 100644 app/examples/Misc/DBusExplorer/property.png create mode 100644 app/examples/Misc/DBusExplorer/signal.png create mode 100644 app/examples/Misc/Evaluator/.directory create mode 100644 app/examples/Misc/Evaluator/.icon.png create mode 100644 app/examples/Misc/Evaluator/.lang/ca.mo create mode 100644 app/examples/Misc/Evaluator/.lang/ca.po create mode 100644 app/examples/Misc/Evaluator/.lang/cs.mo create mode 100644 app/examples/Misc/Evaluator/.lang/cs.po create mode 100644 app/examples/Misc/Evaluator/.lang/de.mo create mode 100644 app/examples/Misc/Evaluator/.lang/de.po create mode 100644 app/examples/Misc/Evaluator/.lang/es.mo create mode 100644 app/examples/Misc/Evaluator/.lang/es.po create mode 100644 app/examples/Misc/Evaluator/.project create mode 100644 app/examples/Misc/Evaluator/.src/FEval.class create mode 100644 app/examples/Misc/Evaluator/.src/FEval.form create mode 100644 app/examples/Misc/Evaluator/calculator.png create mode 100644 app/examples/Misc/Explorer/.directory create mode 100644 app/examples/Misc/Explorer/.icon.png create mode 100644 app/examples/Misc/Explorer/.lang/ca.mo create mode 100644 app/examples/Misc/Explorer/.lang/ca.po create mode 100644 app/examples/Misc/Explorer/.lang/cs.mo create mode 100644 app/examples/Misc/Explorer/.lang/cs.po create mode 100644 app/examples/Misc/Explorer/.lang/de.mo create mode 100644 app/examples/Misc/Explorer/.lang/de.po create mode 100644 app/examples/Misc/Explorer/.lang/es.mo create mode 100644 app/examples/Misc/Explorer/.lang/es.po create mode 100644 app/examples/Misc/Explorer/.project create mode 100644 app/examples/Misc/Explorer/.src/FExplorer.class create mode 100644 app/examples/Misc/Explorer/.src/FExplorer.form create mode 100644 app/examples/Misc/Explorer/folder.png create mode 100644 app/examples/Misc/Notepad/.directory create mode 100644 app/examples/Misc/Notepad/.icon.png create mode 100644 app/examples/Misc/Notepad/.lang/ca.mo create mode 100644 app/examples/Misc/Notepad/.lang/ca.po create mode 100644 app/examples/Misc/Notepad/.lang/cs.mo create mode 100644 app/examples/Misc/Notepad/.lang/cs.po create mode 100644 app/examples/Misc/Notepad/.lang/de.mo create mode 100644 app/examples/Misc/Notepad/.lang/de.po create mode 100644 app/examples/Misc/Notepad/.lang/es.mo create mode 100644 app/examples/Misc/Notepad/.lang/es.po create mode 100644 app/examples/Misc/Notepad/.project create mode 100644 app/examples/Misc/Notepad/.src/FAbout.class create mode 100644 app/examples/Misc/Notepad/.src/FAbout.form create mode 100644 app/examples/Misc/Notepad/.src/FNotepad.class create mode 100644 app/examples/Misc/Notepad/.src/FNotepad.form create mode 100644 app/examples/Misc/Notepad/notepad.png create mode 100644 app/examples/Misc/PDFViewer/.directory create mode 100644 app/examples/Misc/PDFViewer/.icon.png create mode 100644 app/examples/Misc/PDFViewer/.lang/ca.mo create mode 100644 app/examples/Misc/PDFViewer/.lang/ca.po create mode 100644 app/examples/Misc/PDFViewer/.lang/cs.mo create mode 100644 app/examples/Misc/PDFViewer/.lang/cs.po create mode 100644 app/examples/Misc/PDFViewer/.lang/de.mo create mode 100644 app/examples/Misc/PDFViewer/.lang/de.po create mode 100644 app/examples/Misc/PDFViewer/.lang/es.mo create mode 100644 app/examples/Misc/PDFViewer/.lang/es.po create mode 100644 app/examples/Misc/PDFViewer/.project create mode 100644 app/examples/Misc/PDFViewer/.src/FMain.class create mode 100644 app/examples/Misc/PDFViewer/.src/FMain.form create mode 100644 app/examples/Misc/PDFViewer/.src/Fabout.class create mode 100644 app/examples/Misc/PDFViewer/.src/Fabout.form create mode 100644 app/examples/Misc/PDFViewer/pdf.png create mode 100644 app/examples/Misc/SystemTray/.directory create mode 100644 app/examples/Misc/SystemTray/.icon.png create mode 100644 app/examples/Misc/SystemTray/.project create mode 100644 app/examples/Misc/SystemTray/.src/FMain.class create mode 100644 app/examples/Misc/SystemTray/.src/FMain.form create mode 100644 app/examples/Misc/SystemTray/bg.png create mode 100644 app/examples/Misc/SystemTray/icon.png create mode 100644 app/examples/Misc/WatchGambasDirectory/.directory create mode 100644 app/examples/Misc/WatchGambasDirectory/.icon.png create mode 100644 app/examples/Misc/WatchGambasDirectory/.project create mode 100644 app/examples/Misc/WatchGambasDirectory/.src/MMain.module create mode 100644 app/examples/Misc/WatchGambasDirectory/watch.svg create mode 100644 app/examples/Multimedia/CDPlayer/.directory create mode 100644 app/examples/Multimedia/CDPlayer/.icon.png create mode 100644 app/examples/Multimedia/CDPlayer/.lang/ca.mo create mode 100644 app/examples/Multimedia/CDPlayer/.lang/ca.po create mode 100644 app/examples/Multimedia/CDPlayer/.lang/cs.mo create mode 100644 app/examples/Multimedia/CDPlayer/.lang/cs.po create mode 100644 app/examples/Multimedia/CDPlayer/.lang/es.mo create mode 100644 app/examples/Multimedia/CDPlayer/.lang/es.po create mode 100644 app/examples/Multimedia/CDPlayer/.project create mode 100644 app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class create mode 100644 app/examples/Multimedia/CDPlayer/.src/Fcdplayer.form create mode 100644 app/examples/Multimedia/CDPlayer/cdrom.png create mode 100644 app/examples/Multimedia/MediaPlayer/.directory create mode 100644 app/examples/Multimedia/MediaPlayer/.hidden/screenshots/MediaPlayer.jpg create mode 100644 app/examples/Multimedia/MediaPlayer/.icon.png create mode 100644 app/examples/Multimedia/MediaPlayer/.lang/fr.mo create mode 100644 app/examples/Multimedia/MediaPlayer/.lang/fr.po create mode 100644 app/examples/Multimedia/MediaPlayer/.project create mode 100644 app/examples/Multimedia/MediaPlayer/.src/CAnimation.class create mode 100644 app/examples/Multimedia/MediaPlayer/.src/CButton.class create mode 100644 app/examples/Multimedia/MediaPlayer/.src/FControl.class create mode 100644 app/examples/Multimedia/MediaPlayer/.src/FControl.form create mode 100644 app/examples/Multimedia/MediaPlayer/.src/FMain.class create mode 100644 app/examples/Multimedia/MediaPlayer/.src/FMain.form create mode 100644 app/examples/Multimedia/MediaPlayer/.src/FTags.class create mode 100644 app/examples/Multimedia/MediaPlayer/.src/FTags.form create mode 100644 app/examples/Multimedia/MediaPlayer/.src/MTest.module create mode 100644 app/examples/Multimedia/MediaPlayer/brightness.png create mode 100644 app/examples/Multimedia/MediaPlayer/config.png create mode 100644 app/examples/Multimedia/MediaPlayer/contrast.png create mode 100644 app/examples/Multimedia/MediaPlayer/eject.png create mode 100644 app/examples/Multimedia/MediaPlayer/film.png create mode 100644 app/examples/Multimedia/MediaPlayer/fullscreen.png create mode 100644 app/examples/Multimedia/MediaPlayer/gamma.png create mode 100644 app/examples/Multimedia/MediaPlayer/icon.png create mode 100644 app/examples/Multimedia/MediaPlayer/info.png create mode 100644 app/examples/Multimedia/MediaPlayer/mute.png create mode 100644 app/examples/Multimedia/MediaPlayer/pause.png create mode 100644 app/examples/Multimedia/MediaPlayer/play.png create mode 100644 app/examples/Multimedia/MediaPlayer/quit.png create mode 100644 app/examples/Multimedia/MediaPlayer/screenshot.png create mode 100644 app/examples/Multimedia/MediaPlayer/seek-backward.png create mode 100644 app/examples/Multimedia/MediaPlayer/seek-forward.png create mode 100644 app/examples/Multimedia/MediaPlayer/skip-backward.png create mode 100644 app/examples/Multimedia/MediaPlayer/skip-forward.png create mode 100644 app/examples/Multimedia/MediaPlayer/stop.png create mode 100644 app/examples/Multimedia/MediaPlayer/subtitle.png create mode 100644 app/examples/Multimedia/MediaPlayer/undo.png create mode 100644 app/examples/Multimedia/MediaPlayer/video.png create mode 100644 app/examples/Multimedia/MediaPlayer/visualisation.png create mode 100644 app/examples/Multimedia/MediaPlayer/volume-0.png create mode 100644 app/examples/Multimedia/MediaPlayer/volume-1.png create mode 100644 app/examples/Multimedia/MediaPlayer/volume-2.png create mode 100644 app/examples/Multimedia/MediaPlayer/volume-3.png create mode 100644 app/examples/Multimedia/MediaPlayer/volume.png create mode 100644 app/examples/Multimedia/MediaPlayer/zoom-in.png create mode 100644 app/examples/Multimedia/MoviePlayer/.directory create mode 100644 app/examples/Multimedia/MoviePlayer/.icon.png create mode 100644 app/examples/Multimedia/MoviePlayer/.lang/ca.mo create mode 100644 app/examples/Multimedia/MoviePlayer/.lang/ca.po create mode 100644 app/examples/Multimedia/MoviePlayer/.lang/cs.mo create mode 100644 app/examples/Multimedia/MoviePlayer/.lang/cs.po create mode 100644 app/examples/Multimedia/MoviePlayer/.lang/es.mo create mode 100644 app/examples/Multimedia/MoviePlayer/.lang/es.po create mode 100644 app/examples/Multimedia/MoviePlayer/.project create mode 100644 app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.class create mode 100644 app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.form create mode 100644 app/examples/Multimedia/MoviePlayer/video.png create mode 100644 app/examples/Multimedia/MusicPlayer/.directory create mode 100644 app/examples/Multimedia/MusicPlayer/.icon.png create mode 100644 app/examples/Multimedia/MusicPlayer/.icon/16.png create mode 100644 app/examples/Multimedia/MusicPlayer/.icon/32.png create mode 100644 app/examples/Multimedia/MusicPlayer/.icon/48.png create mode 100644 app/examples/Multimedia/MusicPlayer/.lang/ca.mo create mode 100644 app/examples/Multimedia/MusicPlayer/.lang/ca.po create mode 100644 app/examples/Multimedia/MusicPlayer/.lang/cs.mo create mode 100644 app/examples/Multimedia/MusicPlayer/.lang/cs.po create mode 100644 app/examples/Multimedia/MusicPlayer/.lang/es.mo create mode 100644 app/examples/Multimedia/MusicPlayer/.lang/es.po create mode 100644 app/examples/Multimedia/MusicPlayer/.lang/fr.mo create mode 100644 app/examples/Multimedia/MusicPlayer/.lang/fr.po create mode 100644 app/examples/Multimedia/MusicPlayer/.project create mode 100644 app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.class create mode 100644 app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.form create mode 100644 app/examples/Multimedia/MusicPlayer/sound.png create mode 100644 app/examples/Multimedia/MyWebCam/.directory create mode 100644 app/examples/Multimedia/MyWebCam/.icon.png create mode 100644 app/examples/Multimedia/MyWebCam/.lang/ca.mo create mode 100644 app/examples/Multimedia/MyWebCam/.lang/ca.po create mode 100644 app/examples/Multimedia/MyWebCam/.lang/cs.mo create mode 100644 app/examples/Multimedia/MyWebCam/.lang/cs.po create mode 100644 app/examples/Multimedia/MyWebCam/.lang/es.mo create mode 100644 app/examples/Multimedia/MyWebCam/.lang/es.po create mode 100644 app/examples/Multimedia/MyWebCam/.project create mode 100644 app/examples/Multimedia/MyWebCam/.src/Form1.class create mode 100644 app/examples/Multimedia/MyWebCam/.src/Form1.form create mode 100644 app/examples/Multimedia/MyWebCam/camera.png create mode 100644 app/examples/Multimedia/WaveGenerator/.directory create mode 100644 app/examples/Multimedia/WaveGenerator/.icon.png create mode 100644 app/examples/Multimedia/WaveGenerator/.project create mode 100644 app/examples/Multimedia/WaveGenerator/.src/FMain.class create mode 100644 app/examples/Multimedia/WaveGenerator/.src/FMain.form create mode 100644 app/examples/Multimedia/WaveGenerator/audio-headphones.png create mode 100644 app/examples/Multimedia/WebCam/.directory create mode 100644 app/examples/Multimedia/WebCam/.icon.png create mode 100644 app/examples/Multimedia/WebCam/.project create mode 100644 app/examples/Multimedia/WebCam/.src/FDevice.class create mode 100644 app/examples/Multimedia/WebCam/.src/FDevice.form create mode 100644 app/examples/Multimedia/WebCam/.src/FMain.class create mode 100644 app/examples/Multimedia/WebCam/.src/FMain.form create mode 100644 app/examples/Multimedia/WebCam/camera.png create mode 100644 app/examples/Multimedia/WebCam/settings.png create mode 100644 app/examples/Networking/ClientSocket/.directory create mode 100644 app/examples/Networking/ClientSocket/.icon.png create mode 100644 app/examples/Networking/ClientSocket/.lang/ca.mo create mode 100644 app/examples/Networking/ClientSocket/.lang/ca.po create mode 100644 app/examples/Networking/ClientSocket/.lang/cs.mo create mode 100644 app/examples/Networking/ClientSocket/.lang/cs.po create mode 100644 app/examples/Networking/ClientSocket/.lang/de.mo create mode 100644 app/examples/Networking/ClientSocket/.lang/de.po create mode 100644 app/examples/Networking/ClientSocket/.lang/es.mo create mode 100644 app/examples/Networking/ClientSocket/.lang/es.po create mode 100644 app/examples/Networking/ClientSocket/.lang/nl.mo create mode 100644 app/examples/Networking/ClientSocket/.lang/nl.po create mode 100644 app/examples/Networking/ClientSocket/.project create mode 100644 app/examples/Networking/ClientSocket/.src/FrmMain.class create mode 100644 app/examples/Networking/ClientSocket/.src/FrmMain.form create mode 100644 app/examples/Networking/ClientSocket/socket.png create mode 100644 app/examples/Networking/DnsClient/.directory create mode 100644 app/examples/Networking/DnsClient/.icon.png create mode 100644 app/examples/Networking/DnsClient/.lang/ca.mo create mode 100644 app/examples/Networking/DnsClient/.lang/ca.po create mode 100644 app/examples/Networking/DnsClient/.lang/cs.mo create mode 100644 app/examples/Networking/DnsClient/.lang/cs.po create mode 100644 app/examples/Networking/DnsClient/.lang/de.mo create mode 100644 app/examples/Networking/DnsClient/.lang/de.po create mode 100644 app/examples/Networking/DnsClient/.lang/es.mo create mode 100644 app/examples/Networking/DnsClient/.lang/es.po create mode 100644 app/examples/Networking/DnsClient/.lang/nl.mo create mode 100644 app/examples/Networking/DnsClient/.lang/nl.po create mode 100644 app/examples/Networking/DnsClient/.project create mode 100644 app/examples/Networking/DnsClient/.src/FMain.class create mode 100644 app/examples/Networking/DnsClient/.src/FMain.form create mode 100644 app/examples/Networking/DnsClient/dnsclient.png create mode 100644 app/examples/Networking/HTTPGet/.directory create mode 100644 app/examples/Networking/HTTPGet/.icon.png create mode 100644 app/examples/Networking/HTTPGet/.lang/ca.mo create mode 100644 app/examples/Networking/HTTPGet/.lang/ca.po create mode 100644 app/examples/Networking/HTTPGet/.lang/cs.mo create mode 100644 app/examples/Networking/HTTPGet/.lang/cs.po create mode 100644 app/examples/Networking/HTTPGet/.lang/de.mo create mode 100644 app/examples/Networking/HTTPGet/.lang/de.po create mode 100644 app/examples/Networking/HTTPGet/.lang/en_GB.mo create mode 100644 app/examples/Networking/HTTPGet/.lang/en_GB.po create mode 100644 app/examples/Networking/HTTPGet/.lang/es.mo create mode 100644 app/examples/Networking/HTTPGet/.lang/es.po create mode 100644 app/examples/Networking/HTTPGet/.lang/nl.mo create mode 100644 app/examples/Networking/HTTPGet/.lang/nl.po create mode 100644 app/examples/Networking/HTTPGet/.project create mode 100644 app/examples/Networking/HTTPGet/.src/ClsParams.class create mode 100644 app/examples/Networking/HTTPGet/.src/FConfig.class create mode 100644 app/examples/Networking/HTTPGet/.src/FConfig.form create mode 100644 app/examples/Networking/HTTPGet/.src/FHttpGet.class create mode 100644 app/examples/Networking/HTTPGet/.src/FHttpGet.form create mode 100644 app/examples/Networking/HTTPGet/httpclient.png create mode 100644 app/examples/Networking/HTTPPost/.directory create mode 100644 app/examples/Networking/HTTPPost/.icon.png create mode 100644 app/examples/Networking/HTTPPost/.lang/ca.mo create mode 100644 app/examples/Networking/HTTPPost/.lang/ca.po create mode 100644 app/examples/Networking/HTTPPost/.lang/cs.mo create mode 100644 app/examples/Networking/HTTPPost/.lang/cs.po create mode 100644 app/examples/Networking/HTTPPost/.lang/de.mo create mode 100644 app/examples/Networking/HTTPPost/.lang/de.po create mode 100644 app/examples/Networking/HTTPPost/.lang/es.mo create mode 100644 app/examples/Networking/HTTPPost/.lang/es.po create mode 100644 app/examples/Networking/HTTPPost/.lang/nl.mo create mode 100644 app/examples/Networking/HTTPPost/.lang/nl.po create mode 100644 app/examples/Networking/HTTPPost/.project create mode 100644 app/examples/Networking/HTTPPost/.src/FHttpPost.class create mode 100644 app/examples/Networking/HTTPPost/.src/FHttpPost.form create mode 100644 app/examples/Networking/HTTPPost/httpclient.png create mode 100644 app/examples/Networking/POPMailbox/.directory create mode 100644 app/examples/Networking/POPMailbox/.icon.png create mode 100644 app/examples/Networking/POPMailbox/.project create mode 100644 app/examples/Networking/POPMailbox/.src/FMain.class create mode 100644 app/examples/Networking/POPMailbox/.src/FMain.form create mode 100644 app/examples/Networking/POPMailbox/.src/FSettings.class create mode 100644 app/examples/Networking/POPMailbox/.src/FSettings.form create mode 100644 app/examples/Networking/POPMailbox/pop3client.png create mode 100644 app/examples/Networking/SerialPort/.directory create mode 100644 app/examples/Networking/SerialPort/.icon.png create mode 100644 app/examples/Networking/SerialPort/.project create mode 100644 app/examples/Networking/SerialPort/.src/FAbout.class create mode 100644 app/examples/Networking/SerialPort/.src/FAbout.form create mode 100644 app/examples/Networking/SerialPort/.src/FMain.class create mode 100644 app/examples/Networking/SerialPort/.src/FMain.form create mode 100644 app/examples/Networking/SerialPort/.src/Module_Config.module create mode 100644 app/examples/Networking/SerialPort/.src/Module_RS232.module create mode 100644 app/examples/Networking/SerialPort/serialport.png create mode 100644 app/examples/Networking/ServerSocket/.directory create mode 100644 app/examples/Networking/ServerSocket/.icon.png create mode 100644 app/examples/Networking/ServerSocket/.lang/ca.mo create mode 100644 app/examples/Networking/ServerSocket/.lang/ca.po create mode 100644 app/examples/Networking/ServerSocket/.lang/cs.mo create mode 100644 app/examples/Networking/ServerSocket/.lang/cs.po create mode 100644 app/examples/Networking/ServerSocket/.lang/es.mo create mode 100644 app/examples/Networking/ServerSocket/.lang/es.po create mode 100644 app/examples/Networking/ServerSocket/.lang/nl.mo create mode 100644 app/examples/Networking/ServerSocket/.lang/nl.po create mode 100644 app/examples/Networking/ServerSocket/.project create mode 100644 app/examples/Networking/ServerSocket/.src/FrmMain.class create mode 100644 app/examples/Networking/ServerSocket/.src/FrmMain.form create mode 100644 app/examples/Networking/ServerSocket/serversocket.png create mode 100644 app/examples/Networking/UDPServerClient/.directory create mode 100644 app/examples/Networking/UDPServerClient/.icon.png create mode 100644 app/examples/Networking/UDPServerClient/.lang/ca.mo create mode 100644 app/examples/Networking/UDPServerClient/.lang/ca.po create mode 100644 app/examples/Networking/UDPServerClient/.lang/cs.mo create mode 100644 app/examples/Networking/UDPServerClient/.lang/cs.po create mode 100644 app/examples/Networking/UDPServerClient/.lang/es.mo create mode 100644 app/examples/Networking/UDPServerClient/.lang/es.po create mode 100644 app/examples/Networking/UDPServerClient/.lang/nl.mo create mode 100644 app/examples/Networking/UDPServerClient/.lang/nl.po create mode 100644 app/examples/Networking/UDPServerClient/.project create mode 100644 app/examples/Networking/UDPServerClient/.src/FrmClient.class create mode 100644 app/examples/Networking/UDPServerClient/.src/FrmClient.form create mode 100644 app/examples/Networking/UDPServerClient/.src/FrmServer.class create mode 100644 app/examples/Networking/UDPServerClient/.src/FrmServer.form create mode 100644 app/examples/Networking/UDPServerClient/udpsocket.png create mode 100644 app/examples/Networking/WebBrowser/.directory create mode 100644 app/examples/Networking/WebBrowser/.icon.png create mode 100644 app/examples/Networking/WebBrowser/.lang/ca.mo create mode 100644 app/examples/Networking/WebBrowser/.lang/ca.po create mode 100644 app/examples/Networking/WebBrowser/.lang/cs.mo create mode 100644 app/examples/Networking/WebBrowser/.lang/cs.po create mode 100644 app/examples/Networking/WebBrowser/.lang/de.mo create mode 100644 app/examples/Networking/WebBrowser/.lang/de.po create mode 100644 app/examples/Networking/WebBrowser/.lang/es.mo create mode 100644 app/examples/Networking/WebBrowser/.lang/es.po create mode 100644 app/examples/Networking/WebBrowser/.lang/nl.mo create mode 100644 app/examples/Networking/WebBrowser/.lang/nl.po create mode 100644 app/examples/Networking/WebBrowser/.project create mode 100644 app/examples/Networking/WebBrowser/.src/FAuth.class create mode 100644 app/examples/Networking/WebBrowser/.src/FAuth.form create mode 100644 app/examples/Networking/WebBrowser/.src/FBrowser.class create mode 100644 app/examples/Networking/WebBrowser/.src/FBrowser.form create mode 100644 app/examples/Networking/WebBrowser/.src/FDownload.class create mode 100644 app/examples/Networking/WebBrowser/.src/FDownload.form create mode 100644 app/examples/Networking/WebBrowser/.src/FDownloadList.class create mode 100644 app/examples/Networking/WebBrowser/.src/FDownloadList.form create mode 100644 app/examples/Networking/WebBrowser/.src/FEditable.class create mode 100644 app/examples/Networking/WebBrowser/.src/FEditable.form create mode 100644 app/examples/Networking/WebBrowser/.src/FOption.class create mode 100644 app/examples/Networking/WebBrowser/.src/FOption.form create mode 100644 app/examples/Networking/WebBrowser/icon.png create mode 100644 app/examples/Networking/WebBrowser/list-ordered.png create mode 100644 app/examples/Networking/WebBrowser/list-unordered.png create mode 100644 app/examples/OpenGL/3DWebCam/.directory create mode 100644 app/examples/OpenGL/3DWebCam/.icon.png create mode 100644 app/examples/OpenGL/3DWebCam/.project create mode 100644 app/examples/OpenGL/3DWebCam/.src/Mmain.module create mode 100644 app/examples/OpenGL/3DWebCam/webcam.png create mode 100644 app/examples/OpenGL/GambasGears/.directory create mode 100644 app/examples/OpenGL/GambasGears/.icon.png create mode 100644 app/examples/OpenGL/GambasGears/.project create mode 100644 app/examples/OpenGL/GambasGears/.src/Module1.module create mode 100644 app/examples/OpenGL/GambasGears/gears.png create mode 100644 app/examples/OpenGL/Md2Model/.directory create mode 100644 app/examples/OpenGL/Md2Model/.icon.png create mode 100644 app/examples/OpenGL/Md2Model/.project create mode 100644 app/examples/OpenGL/Md2Model/.src/FMain.class create mode 100644 app/examples/OpenGL/Md2Model/.src/FMain.form create mode 100644 app/examples/OpenGL/Md2Model/Weapon.md2 create mode 100644 app/examples/OpenGL/Md2Model/Weapon.png create mode 100644 app/examples/OpenGL/Md2Model/bauul.jpg create mode 100644 app/examples/OpenGL/Md2Model/bauul.md2 create mode 100644 app/examples/OpenGL/Md2Model/goblin.jpg create mode 100644 app/examples/OpenGL/Md2Model/goblin.md2 create mode 100644 app/examples/OpenGL/Md2Model/icon.png create mode 100644 app/examples/OpenGL/Md2Model/igdosh.png create mode 100644 app/examples/OpenGL/Md2Model/knight.jpg create mode 100644 app/examples/OpenGL/Md2Model/knight.md2 create mode 100644 app/examples/OpenGL/Md2Model/ogro.jpg create mode 100644 app/examples/OpenGL/Md2Model/ogro.md2 create mode 100644 app/examples/OpenGL/Md2Model/rat.jpg create mode 100644 app/examples/OpenGL/Md2Model/rat.md2 create mode 100644 app/examples/OpenGL/Md2Model/rhino.jpg create mode 100644 app/examples/OpenGL/Md2Model/rhino.md2 create mode 100644 app/examples/OpenGL/NeHeTutorial/.directory create mode 100644 app/examples/OpenGL/NeHeTutorial/.icon.png create mode 100644 app/examples/OpenGL/NeHeTutorial/.project create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example1.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example10.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example11.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example16.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example19.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example2.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example25.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example3.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example4.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example42.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example5.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example6.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example7.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example8.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/Example9.module create mode 100644 app/examples/OpenGL/NeHeTutorial/.src/MMain.module create mode 100644 app/examples/OpenGL/NeHeTutorial/NeHe.png create mode 100644 app/examples/OpenGL/NeHeTutorial/Sphere.txt create mode 100644 app/examples/OpenGL/NeHeTutorial/Star.png create mode 100644 app/examples/OpenGL/NeHeTutorial/Torus.txt create mode 100644 app/examples/OpenGL/NeHeTutorial/Tube.txt create mode 100644 app/examples/OpenGL/NeHeTutorial/barrel.png create mode 100644 app/examples/OpenGL/NeHeTutorial/ceiling.png create mode 100644 app/examples/OpenGL/NeHeTutorial/crate.jpeg create mode 100644 app/examples/OpenGL/NeHeTutorial/floor.png create mode 100644 app/examples/OpenGL/NeHeTutorial/glass.png create mode 100644 app/examples/OpenGL/NeHeTutorial/icon.png create mode 100644 app/examples/OpenGL/NeHeTutorial/rainbow.txt create mode 100644 app/examples/OpenGL/NeHeTutorial/wall.jpeg create mode 100644 app/examples/OpenGL/NeHeTutorial/world.txt create mode 100644 app/examples/OpenGL/NeHeTutorialShell/.directory create mode 100644 app/examples/OpenGL/NeHeTutorialShell/.icon.png create mode 100644 app/examples/OpenGL/NeHeTutorialShell/.project create mode 100644 app/examples/OpenGL/NeHeTutorialShell/.src/FMain.class create mode 100644 app/examples/OpenGL/NeHeTutorialShell/.src/FMain.form create mode 100644 app/examples/OpenGL/NeHeTutorialShell/icon.png create mode 100644 app/examples/OpenGL/NeHeTutorialShell/nehe.png create mode 100644 app/examples/OpenGL/PDFPresentation/.directory create mode 100644 app/examples/OpenGL/PDFPresentation/.icon.png create mode 100644 app/examples/OpenGL/PDFPresentation/.project create mode 100644 app/examples/OpenGL/PDFPresentation/.src/CLogo.class create mode 100644 app/examples/OpenGL/PDFPresentation/.src/CPdfPresentation.class create mode 100644 app/examples/OpenGL/PDFPresentation/.src/FMain.class create mode 100644 app/examples/OpenGL/PDFPresentation/.src/FMain.form create mode 100644 app/examples/OpenGL/PDFPresentation/.src/MMain.module create mode 100644 app/examples/OpenGL/PDFPresentation/icon.png create mode 100644 app/examples/OpenGL/PDFPresentation/logo.png create mode 100644 app/examples/OpenGL/PDFPresentation/music.xm create mode 100644 app/examples/OpenGL/TunnelSDL/.dir_icon.png create mode 100644 app/examples/OpenGL/TunnelSDL/.directory create mode 100644 app/examples/OpenGL/TunnelSDL/.icon.png create mode 100644 app/examples/OpenGL/TunnelSDL/.project create mode 100644 app/examples/OpenGL/TunnelSDL/.src/CTunnel.class create mode 100644 app/examples/OpenGL/TunnelSDL/.src/MMain.module create mode 100644 app/examples/OpenGL/TunnelSDL/texture.png create mode 100644 app/examples/OpenGL/TunnelSDL/tunnelsdl.png create mode 100644 app/examples/Printing/Printing/.directory create mode 100644 app/examples/Printing/Printing/.hidden/screenshots/2014-12-14.png create mode 100644 app/examples/Printing/Printing/.icon.png create mode 100644 app/examples/Printing/Printing/.project create mode 100644 app/examples/Printing/Printing/.src/FMain.class create mode 100644 app/examples/Printing/Printing/.src/FMain.form create mode 100644 app/examples/Printing/Printing/molly-malone.txt create mode 100644 app/examples/Printing/Printing/printer-laser.png create mode 100644 app/examples/Printing/ReportExample/.connection/Connection1.connection create mode 100644 app/examples/Printing/ReportExample/.connection/Connection1.template create mode 100644 app/examples/Printing/ReportExample/.directory create mode 100644 app/examples/Printing/ReportExample/.hidden/report.png create mode 100644 app/examples/Printing/ReportExample/.icon.png create mode 100644 app/examples/Printing/ReportExample/.project create mode 100644 app/examples/Printing/ReportExample/.src/FMain.class create mode 100644 app/examples/Printing/ReportExample/.src/FMain.form create mode 100644 app/examples/Printing/ReportExample/.src/Report1.class create mode 100644 app/examples/Printing/ReportExample/.src/Report1.report create mode 100644 app/examples/Printing/ReportExample/.src/Report2.class create mode 100644 app/examples/Printing/ReportExample/.src/Report2.report create mode 100644 app/examples/Printing/ReportExample/.src/Report3.class create mode 100644 app/examples/Printing/ReportExample/.src/Report3.report create mode 100644 app/examples/Printing/ReportExample/gambas.svg create mode 100644 app/examples/Web/SmallWiki/.directory create mode 100644 app/examples/Web/SmallWiki/.icon.png create mode 100644 app/examples/Web/SmallWiki/.project create mode 100644 app/examples/Web/SmallWiki/.public/critical.png create mode 100644 app/examples/Web/SmallWiki/.public/edit.png create mode 100644 app/examples/Web/SmallWiki/.public/info.png create mode 100644 app/examples/Web/SmallWiki/.public/logo.png create mode 100644 app/examples/Web/SmallWiki/.public/style.css create mode 100644 app/examples/Web/SmallWiki/.public/tip.png create mode 100644 app/examples/Web/SmallWiki/.public/up.png create mode 100644 app/examples/Web/SmallWiki/.public/warning.png create mode 100644 app/examples/Web/SmallWiki/.src/Main.module create mode 100644 app/examples/Web/SmallWiki/.src/Markdown.class create mode 100644 app/examples/Web/SmallWiki/.src/MarkdownLink.class create mode 100644 app/examples/Web/SmallWiki/.src/Markup.module create mode 100644 app/examples/Web/SmallWiki/.src/Wiki.class create mode 100644 app/examples/Web/SmallWiki/.src/Wiki.webpage create mode 100644 app/examples/Web/SmallWiki/.src/WikiMarkdown.class create mode 100644 app/examples/Web/SmallWiki/page create mode 100644 app/examples/Web/SmallWiki/passwd create mode 100644 app/mime/application-x-gambasscript.png create mode 100644 app/mime/application-x-gambasscript.xml create mode 100644 app/mime/application-x-gambasserverpage.png create mode 100644 app/mime/application-x-gambasserverpage.xml create mode 120000 app/reconf create mode 100644 app/src/INSTALL create mode 100644 app/src/gambas-farm-server/.connection/Connection1.connection create mode 100644 app/src/gambas-farm-server/.connection/Connection1.template create mode 100644 app/src/gambas-farm-server/.connection/Connection2.connection create mode 100644 app/src/gambas-farm-server/.connection/gambas3_farm.connection create mode 100644 app/src/gambas-farm-server/.connection/gambas3_farm.template create mode 100644 app/src/gambas-farm-server/.directory create mode 100644 app/src/gambas-farm-server/.icon.png create mode 100644 app/src/gambas-farm-server/.project create mode 100644 app/src/gambas-farm-server/.src/MMain.module create mode 100644 app/src/gambas-farm-server/logo.png create mode 100644 app/src/gambas-farm-server/usage.txt create mode 100644 app/src/gambas-wiki/.connection/Connection1.connection create mode 100644 app/src/gambas-wiki/.directory create mode 100644 app/src/gambas-wiki/.hidden/CHANGELOG create mode 100644 app/src/gambas-wiki/.hidden/Uncompressed/.public/hilitor.js create mode 100644 app/src/gambas-wiki/.hidden/Uncompressed/.public/style-rtl.css create mode 100644 app/src/gambas-wiki/.hidden/Uncompressed/.public/style-w.css create mode 100644 app/src/gambas-wiki/.hidden/Uncompressed/.public/style.css create mode 100644 app/src/gambas-wiki/.icon.png create mode 100644 app/src/gambas-wiki/.lang/ar.mo create mode 100644 app/src/gambas-wiki/.lang/ar.po create mode 100644 app/src/gambas-wiki/.lang/ca.mo create mode 100644 app/src/gambas-wiki/.lang/ca.po create mode 100644 app/src/gambas-wiki/.lang/cs.mo create mode 100644 app/src/gambas-wiki/.lang/cs.po create mode 100644 app/src/gambas-wiki/.lang/de.mo create mode 100644 app/src/gambas-wiki/.lang/de.po create mode 100644 app/src/gambas-wiki/.lang/es.mo create mode 100644 app/src/gambas-wiki/.lang/es.po create mode 100644 app/src/gambas-wiki/.lang/es_ES.mo create mode 100644 app/src/gambas-wiki/.lang/es_ES.po create mode 100644 app/src/gambas-wiki/.lang/fa.mo create mode 100644 app/src/gambas-wiki/.lang/fa.po create mode 100644 app/src/gambas-wiki/.lang/fr.mo create mode 100644 app/src/gambas-wiki/.lang/fr.po create mode 100644 app/src/gambas-wiki/.lang/it.mo create mode 100644 app/src/gambas-wiki/.lang/it.po create mode 100644 app/src/gambas-wiki/.lang/nl.mo create mode 100644 app/src/gambas-wiki/.lang/nl.po create mode 100644 app/src/gambas-wiki/.lang/pt_BR.mo create mode 100644 app/src/gambas-wiki/.lang/pt_BR.po create mode 100644 app/src/gambas-wiki/.lang/ru.mo create mode 100644 app/src/gambas-wiki/.lang/ru.po create mode 100644 app/src/gambas-wiki/.lang/sv.mo create mode 100644 app/src/gambas-wiki/.lang/sv.po create mode 100644 app/src/gambas-wiki/.lang/zh.mo create mode 100644 app/src/gambas-wiki/.lang/zh.po create mode 100644 app/src/gambas-wiki/.project create mode 100644 app/src/gambas-wiki/.public/critical.png create mode 100644 app/src/gambas-wiki/.public/edit.png create mode 100644 app/src/gambas-wiki/.public/error-bg.png create mode 100644 app/src/gambas-wiki/.public/gambas.png create mode 100644 app/src/gambas-wiki/.public/hilitor.js create mode 100644 app/src/gambas-wiki/.public/home.png create mode 100644 app/src/gambas-wiki/.public/info.png create mode 100644 app/src/gambas-wiki/.public/logo.png create mode 100644 app/src/gambas-wiki/.public/style-nh.css create mode 100644 app/src/gambas-wiki/.public/style-rtl.css create mode 100644 app/src/gambas-wiki/.public/style-w.css create mode 100644 app/src/gambas-wiki/.public/style.css create mode 100644 app/src/gambas-wiki/.public/tip.png create mode 100644 app/src/gambas-wiki/.public/up-gray.png create mode 100644 app/src/gambas-wiki/.public/up.png create mode 100644 app/src/gambas-wiki/.public/vb.png create mode 100644 app/src/gambas-wiki/.public/warning.png create mode 100644 app/src/gambas-wiki/.src/CAuthor.class create mode 100644 app/src/gambas-wiki/.src/CClassInfo.class create mode 100644 app/src/gambas-wiki/.src/CComponent.class create mode 100644 app/src/gambas-wiki/.src/CPropertyInfo.class create mode 100644 app/src/gambas-wiki/.src/CSymbolInfo.class create mode 100644 app/src/gambas-wiki/.src/CUser.class create mode 100644 app/src/gambas-wiki/.src/Confirm.class create mode 100644 app/src/gambas-wiki/.src/Confirm.webpage create mode 100644 app/src/gambas-wiki/.src/HttpStat.module create mode 100644 app/src/gambas-wiki/.src/Main.module create mode 100644 app/src/gambas-wiki/.src/OldWiki.module create mode 100644 app/src/gambas-wiki/.src/Register.class create mode 100644 app/src/gambas-wiki/.src/Register.webpage create mode 100644 app/src/gambas-wiki/.src/Wiki.class create mode 100644 app/src/gambas-wiki/.src/Wiki.webpage create mode 100644 app/src/gambas-wiki/.src/WikiMarkdown.class create mode 120000 app/src/gambas-wiki/authors.txt create mode 120000 app/src/gambas-wiki/gambas3-ide.project create mode 120000 app/src/gambas-wiki/gambas3-scripter.project create mode 100644 app/src/gambas-wiki/icon.png create mode 100644 app/src/gambas-wiki/page create mode 100644 app/src/gambas-wiki/passwd create mode 100644 app/src/gambas3/.directory create mode 100644 app/src/gambas3/.hidden/Uncompressed/help/class-help.html create mode 100644 app/src/gambas3/.hidden/Uncompressed/help/wiki/style.css create mode 100644 app/src/gambas3/.hidden/font/GambasBold-12.sfd create mode 100644 app/src/gambas3/.hidden/font/GambasBold-13.sfd create mode 100644 app/src/gambas3/.hidden/font/GambasMedium-12.sfd create mode 100644 app/src/gambas3/.hidden/font/GambasMedium-13.sfd create mode 100644 app/src/gambas3/.hidden/font/GambasMedium-14.sfd create mode 100644 app/src/gambas3/.hidden/font/LICENSE create mode 100644 app/src/gambas3/.hidden/make-help-archive create mode 100644 app/src/gambas3/.hidden/report-ng.sh create mode 100644 app/src/gambas3/.icon.png create mode 100644 app/src/gambas3/.lang/ar.mo create mode 100644 app/src/gambas3/.lang/ar.po create mode 100644 app/src/gambas3/.lang/ca.mo create mode 100644 app/src/gambas3/.lang/ca.po create mode 100644 app/src/gambas3/.lang/cs.mo create mode 100644 app/src/gambas3/.lang/cs.po create mode 100644 app/src/gambas3/.lang/cy.mo create mode 100644 app/src/gambas3/.lang/cy.po create mode 100644 app/src/gambas3/.lang/de.mo create mode 100644 app/src/gambas3/.lang/de.po create mode 100644 app/src/gambas3/.lang/el.mo create mode 100644 app/src/gambas3/.lang/el.po create mode 100644 app/src/gambas3/.lang/es.mo create mode 100644 app/src/gambas3/.lang/es.po create mode 100644 app/src/gambas3/.lang/es_ES.mo create mode 100644 app/src/gambas3/.lang/es_ES.po create mode 100644 app/src/gambas3/.lang/fa.mo create mode 100644 app/src/gambas3/.lang/fa.po create mode 100644 app/src/gambas3/.lang/fr.mo create mode 100644 app/src/gambas3/.lang/fr.po create mode 100644 app/src/gambas3/.lang/gl_ES.mo create mode 100644 app/src/gambas3/.lang/gl_ES.po create mode 100644 app/src/gambas3/.lang/hr.mo create mode 100644 app/src/gambas3/.lang/hr.po create mode 100644 app/src/gambas3/.lang/hu.mo create mode 100644 app/src/gambas3/.lang/hu.po create mode 100644 app/src/gambas3/.lang/id.mo create mode 100644 app/src/gambas3/.lang/id.po create mode 100644 app/src/gambas3/.lang/it.mo create mode 100644 app/src/gambas3/.lang/it.po create mode 100644 app/src/gambas3/.lang/ja.mo create mode 100644 app/src/gambas3/.lang/ja.po create mode 100644 app/src/gambas3/.lang/ko.mo create mode 100644 app/src/gambas3/.lang/ko.po create mode 100644 app/src/gambas3/.lang/lt.mo create mode 100644 app/src/gambas3/.lang/lt.po create mode 100644 app/src/gambas3/.lang/nl.mo create mode 100644 app/src/gambas3/.lang/nl.po create mode 100644 app/src/gambas3/.lang/no.mo create mode 100644 app/src/gambas3/.lang/no.po create mode 100644 app/src/gambas3/.lang/pl.mo create mode 100644 app/src/gambas3/.lang/pl.po create mode 100644 app/src/gambas3/.lang/pt.mo create mode 100644 app/src/gambas3/.lang/pt.po create mode 100644 app/src/gambas3/.lang/pt_BR.mo create mode 100644 app/src/gambas3/.lang/pt_BR.po create mode 100644 app/src/gambas3/.lang/ro.mo create mode 100644 app/src/gambas3/.lang/ro.po create mode 100644 app/src/gambas3/.lang/ru.mo create mode 100644 app/src/gambas3/.lang/ru.po create mode 100644 app/src/gambas3/.lang/sl.mo create mode 100644 app/src/gambas3/.lang/sl.po create mode 100644 app/src/gambas3/.lang/sv.mo create mode 100644 app/src/gambas3/.lang/sv.po create mode 100644 app/src/gambas3/.lang/tr.mo create mode 100644 app/src/gambas3/.lang/tr.po create mode 100644 app/src/gambas3/.lang/zh.mo create mode 100644 app/src/gambas3/.lang/zh.po create mode 100644 app/src/gambas3/.lang/zh_TW.mo create mode 100644 app/src/gambas3/.lang/zh_TW.po create mode 100644 app/src/gambas3/.project create mode 100644 app/src/gambas3/.src/CRecentProject.class create mode 100644 app/src/gambas3/.src/CStyle.class create mode 100644 app/src/gambas3/.src/CWaitingAnimation.class create mode 100644 app/src/gambas3/.src/Component/CClassInfo.class create mode 100644 app/src/gambas3/.src/Component/CComponent.class create mode 100644 app/src/gambas3/.src/Component/CDocumentation.class create mode 100644 app/src/gambas3/.src/Component/CModule.class create mode 100644 app/src/gambas3/.src/Component/CPropertyInfo.class create mode 100644 app/src/gambas3/.src/Component/CSymbolInfo.class create mode 100644 app/src/gambas3/.src/Connection/FExportData.class create mode 100644 app/src/gambas3/.src/Connection/FExportData.form create mode 100644 app/src/gambas3/.src/Connection/FImportTable.class create mode 100644 app/src/gambas3/.src/Connection/FImportTable.form create mode 100644 app/src/gambas3/.src/Connection/FNewConnection.class create mode 100644 app/src/gambas3/.src/Connection/FNewConnection.form create mode 100644 app/src/gambas3/.src/Connection/FPasteTable.class create mode 100644 app/src/gambas3/.src/Connection/FPasteTable.form create mode 100644 app/src/gambas3/.src/Connection/MConnection.module create mode 100644 app/src/gambas3/.src/Debug/CProfile.class create mode 100644 app/src/gambas3/.src/Debug/Design.module create mode 100644 app/src/gambas3/.src/Debug/FCrash.class create mode 100644 app/src/gambas3/.src/Debug/FCrash.form create mode 100644 app/src/gambas3/.src/Debug/FDebugButton.class create mode 100644 app/src/gambas3/.src/Debug/FDebugButton.form create mode 100644 app/src/gambas3/.src/Debug/FDebugExpr.class create mode 100644 app/src/gambas3/.src/Debug/FDebugExpr.form create mode 100644 app/src/gambas3/.src/Debug/FDebugInfo.class create mode 100644 app/src/gambas3/.src/Debug/FDebugInfo.form create mode 100644 app/src/gambas3/.src/Debug/FOutput.class create mode 100644 app/src/gambas3/.src/Debug/FOutput.form create mode 100644 app/src/gambas3/.src/Debug/FProfile.class create mode 100644 app/src/gambas3/.src/Debug/FProfile.form create mode 100644 app/src/gambas3/.src/Dialog/Database/FFieldChooser.class create mode 100644 app/src/gambas3/.src/Dialog/Database/FFieldChooser.form create mode 100644 app/src/gambas3/.src/Dialog/Database/FTableChooser.class create mode 100644 app/src/gambas3/.src/Dialog/Database/FTableChooser.form create mode 100644 app/src/gambas3/.src/Dialog/FColorChooser.class create mode 100644 app/src/gambas3/.src/Dialog/FColorChooser.form create mode 100644 app/src/gambas3/.src/Dialog/FFileProperty.class create mode 100644 app/src/gambas3/.src/Dialog/FFileProperty.form create mode 100644 app/src/gambas3/.src/Dialog/FFontChooser.class create mode 100644 app/src/gambas3/.src/Dialog/FFontChooser.form create mode 100644 app/src/gambas3/.src/Dialog/FList.class create mode 100644 app/src/gambas3/.src/Dialog/FList.form create mode 100644 app/src/gambas3/.src/Dialog/FSelectIcon.class create mode 100644 app/src/gambas3/.src/Dialog/FSelectIcon.form create mode 100644 app/src/gambas3/.src/Editor/CBookmark.class create mode 100644 app/src/gambas3/.src/Editor/CInsertColor.class create mode 100644 app/src/gambas3/.src/Editor/CInsertDate.class create mode 100644 app/src/gambas3/.src/Editor/CPosition.class create mode 100644 app/src/gambas3/.src/Editor/CTask.class create mode 100644 app/src/gambas3/.src/Editor/CUndo.class create mode 100644 app/src/gambas3/.src/Editor/Code/CCompletion.class create mode 100644 app/src/gambas3/.src/Editor/Code/CDatatype.class create mode 100644 app/src/gambas3/.src/Editor/Code/CSampleCode.class create mode 100644 app/src/gambas3/.src/Editor/Code/FCompletion.class create mode 100644 app/src/gambas3/.src/Editor/Code/FCompletion.form create mode 100644 app/src/gambas3/.src/Editor/Code/FConflictEditor.class create mode 100644 app/src/gambas3/.src/Editor/Code/FConflictEditor.form create mode 100644 app/src/gambas3/.src/Editor/Code/FEditor.class create mode 100644 app/src/gambas3/.src/Editor/Code/FEditor.form create mode 100644 app/src/gambas3/.src/Editor/Code/FPasteSpecial.class create mode 100644 app/src/gambas3/.src/Editor/Code/FPasteSpecial.form create mode 100644 app/src/gambas3/.src/Editor/Code/FProcedureList.class create mode 100644 app/src/gambas3/.src/Editor/Code/FProcedureList.form create mode 100644 app/src/gambas3/.src/Editor/Code/FSignature.class create mode 100644 app/src/gambas3/.src/Editor/Code/FSignature.form create mode 100644 app/src/gambas3/.src/Editor/Code/FTextEditor.class create mode 100644 app/src/gambas3/.src/Editor/Code/FTextEditor.form create mode 100644 app/src/gambas3/.src/Editor/Code/MPrettyCode.module create mode 100644 app/src/gambas3/.src/Editor/Connection/CField.class create mode 100644 app/src/gambas3/.src/Editor/Connection/CIndexField.class create mode 100644 app/src/gambas3/.src/Editor/Connection/FConnectionEditor.class create mode 100644 app/src/gambas3/.src/Editor/Connection/FConnectionEditor.form create mode 100644 app/src/gambas3/.src/Editor/Connection/FNewTable.class create mode 100644 app/src/gambas3/.src/Editor/Connection/FNewTable.form create mode 100644 app/src/gambas3/.src/Editor/FGotoLine.class create mode 100644 app/src/gambas3/.src/Editor/FGotoLine.form create mode 100644 app/src/gambas3/.src/Editor/FInsertChar.class create mode 100644 app/src/gambas3/.src/Editor/FInsertChar.form create mode 100644 app/src/gambas3/.src/Editor/Form/CControl.class create mode 100644 app/src/gambas3/.src/Editor/Form/CMenu.class create mode 100644 app/src/gambas3/.src/Editor/Form/FForm.class create mode 100644 app/src/gambas3/.src/Editor/Form/FForm.form create mode 100644 app/src/gambas3/.src/Editor/Form/FFormStack.class create mode 100644 app/src/gambas3/.src/Editor/Form/FFormStack.form create mode 100644 app/src/gambas3/.src/Editor/Form/FMenu.class create mode 100644 app/src/gambas3/.src/Editor/Form/FMenu.form create mode 100644 app/src/gambas3/.src/Editor/Form/FProperty.class create mode 100644 app/src/gambas3/.src/Editor/Form/FProperty.form create mode 100644 app/src/gambas3/.src/Editor/Form/FText.class create mode 100644 app/src/gambas3/.src/Editor/Form/FText.form create mode 100644 app/src/gambas3/.src/Editor/Form/FToolBox.class create mode 100644 app/src/gambas3/.src/Editor/Form/FToolBox.form create mode 100644 app/src/gambas3/.src/Editor/Form/FToolPanel.class create mode 100644 app/src/gambas3/.src/Editor/Form/FToolPanel.form create mode 100644 app/src/gambas3/.src/Editor/Image/CImageClipboard.class create mode 100644 app/src/gambas3/.src/Editor/Image/CImageSelection.class create mode 100644 app/src/gambas3/.src/Editor/Image/CImageShape.class create mode 100644 app/src/gambas3/.src/Editor/Image/FImageEditor.class create mode 100644 app/src/gambas3/.src/Editor/Image/FImageEditor.form create mode 100644 app/src/gambas3/.src/Editor/Image/FImageOffsetSelection.class create mode 100644 app/src/gambas3/.src/Editor/Image/FImageOffsetSelection.form create mode 100644 app/src/gambas3/.src/Editor/Image/FImageProperty.class create mode 100644 app/src/gambas3/.src/Editor/Image/FImageProperty.form create mode 100644 app/src/gambas3/.src/Editor/Image/FImageQuality.class create mode 100644 app/src/gambas3/.src/Editor/Image/FImageQuality.form create mode 100644 app/src/gambas3/.src/Editor/Image/FImageResize.class create mode 100644 app/src/gambas3/.src/Editor/Image/FImageResize.form create mode 100644 app/src/gambas3/.src/Editor/Image/FImageRotate.class create mode 100644 app/src/gambas3/.src/Editor/Image/FImageRotate.form create mode 100644 app/src/gambas3/.src/Editor/MCompressFile.module create mode 100644 app/src/gambas3/.src/Exported/ProjectChooser/FProjectChooser.class create mode 100644 app/src/gambas3/.src/Exported/ProjectChooser/FProjectChooser.form create mode 100644 app/src/gambas3/.src/Exported/ProjectChooser/ProjectChooser.class create mode 100644 app/src/gambas3/.src/Exported/TextEditor.class create mode 100644 app/src/gambas3/.src/FMain.class create mode 100644 app/src/gambas3/.src/FMain.form create mode 100644 app/src/gambas3/.src/FSave.class create mode 100644 app/src/gambas3/.src/FSave.form create mode 100644 app/src/gambas3/.src/FScreenshot.class create mode 100644 app/src/gambas3/.src/FScreenshot.form create mode 100644 app/src/gambas3/.src/Family/CFamily.class create mode 100644 app/src/gambas3/.src/Family/Form/CFamilyForm.class create mode 100644 app/src/gambas3/.src/Family/Report/AngleBox.class create mode 100644 app/src/gambas3/.src/Family/Report/CFamilyReport.class create mode 100644 app/src/gambas3/.src/Family/Report/CReportBrush.class create mode 100644 app/src/gambas3/.src/Family/Report/CoordBox.class create mode 100644 app/src/gambas3/.src/Family/Report/FReportBorderChooser.class create mode 100644 app/src/gambas3/.src/Family/Report/FReportBorderChooser.form create mode 100644 app/src/gambas3/.src/Family/Report/FReportBoxShadowChooser.class create mode 100644 app/src/gambas3/.src/Family/Report/FReportBoxShadowChooser.form create mode 100644 app/src/gambas3/.src/Family/Report/FReportBrushChooser.class create mode 100644 app/src/gambas3/.src/Family/Report/FReportBrushChooser.form create mode 100644 app/src/gambas3/.src/Family/Report/FReportCoordChooser.class create mode 100644 app/src/gambas3/.src/Family/Report/FReportCoordChooser.form create mode 100644 app/src/gambas3/.src/Family/Report/FReportPaddingChooser.class create mode 100644 app/src/gambas3/.src/Family/Report/FReportPaddingChooser.form create mode 100644 app/src/gambas3/.src/Family/TermForm/CFamilyTermForm.class create mode 100644 app/src/gambas3/.src/Family/WebForm/CFamilyWebForm.class create mode 100644 app/src/gambas3/.src/Family/WebForm/FWebFontChooser.class create mode 100644 app/src/gambas3/.src/Family/WebForm/FWebFontChooser.form create mode 100644 app/src/gambas3/.src/Family/WebForm/WebformWebMenu.class create mode 100644 app/src/gambas3/.src/Help/FHelpBrowser.class create mode 100644 app/src/gambas3/.src/Help/FHelpBrowser.form create mode 100644 app/src/gambas3/.src/Help/FHelpShortcut.class create mode 100644 app/src/gambas3/.src/Help/FHelpShortcut.form create mode 100644 app/src/gambas3/.src/Help/FTips.class create mode 100644 app/src/gambas3/.src/Help/FTips.form create mode 100644 app/src/gambas3/.src/Help/HelpView.class create mode 100644 app/src/gambas3/.src/Help/MHelp.module create mode 100644 app/src/gambas3/.src/Help/Markup.module create mode 100644 app/src/gambas3/.src/Help/Wiki/URL.class create mode 100644 app/src/gambas3/.src/Help/Wiki/Wiki.module create mode 100644 app/src/gambas3/.src/Help/Wiki/WikiMarkdown.class create mode 100644 app/src/gambas3/.src/MMime.module create mode 100644 app/src/gambas3/.src/MTheme.module create mode 100644 app/src/gambas3/.src/Options/CBackground.class create mode 100644 app/src/gambas3/.src/Options/FOption.class create mode 100644 app/src/gambas3/.src/Options/FOption.form create mode 100644 app/src/gambas3/.src/Options/FProxy.class create mode 100644 app/src/gambas3/.src/Options/FProxy.form create mode 100644 app/src/gambas3/.src/Options/FSnippet.class create mode 100644 app/src/gambas3/.src/Options/FSnippet.form create mode 100644 app/src/gambas3/.src/Packager/FMakeInstall.class create mode 100644 app/src/gambas3/.src/Packager/FMakeInstall.form create mode 100644 app/src/gambas3/.src/Packager/FSelectExtraFile.class create mode 100644 app/src/gambas3/.src/Packager/FSelectExtraFile.form create mode 100644 app/src/gambas3/.src/Packager/Package.module create mode 100644 app/src/gambas3/.src/Project.module create mode 100644 app/src/gambas3/.src/Project/ArgListBox.class create mode 100644 app/src/gambas3/.src/Project/ArgListEditor.class create mode 100644 app/src/gambas3/.src/Project/CProjectInfo.class create mode 100644 app/src/gambas3/.src/Project/CProjectList.class create mode 100644 app/src/gambas3/.src/Project/CProjectTree.class create mode 100644 app/src/gambas3/.src/Project/Component/ComponentChooser.class create mode 100644 app/src/gambas3/.src/Project/Component/FComponentChooser.class create mode 100644 app/src/gambas3/.src/Project/Component/FComponentChooser.form create mode 100644 app/src/gambas3/.src/Project/Component/FSelectComponent.class create mode 100644 app/src/gambas3/.src/Project/Component/FSelectComponent.form create mode 100644 app/src/gambas3/.src/Project/Conversion/FConvert.class create mode 100644 app/src/gambas3/.src/Project/Conversion/FConvert.form create mode 100644 app/src/gambas3/.src/Project/Conversion/MConvert.module create mode 100644 app/src/gambas3/.src/Project/FCreateFile.class create mode 100644 app/src/gambas3/.src/Project/FCreateFile.form create mode 100644 app/src/gambas3/.src/Project/FCreateProject.class create mode 100644 app/src/gambas3/.src/Project/FCreateProject.form create mode 100644 app/src/gambas3/.src/Project/FImportFile.class create mode 100644 app/src/gambas3/.src/Project/FImportFile.form create mode 100644 app/src/gambas3/.src/Project/FMakeExecutable.class create mode 100644 app/src/gambas3/.src/Project/FMakeExecutable.form create mode 100644 app/src/gambas3/.src/Project/FOpenProject.class create mode 100644 app/src/gambas3/.src/Project/FOpenProject.form create mode 100644 app/src/gambas3/.src/Project/FProjectProperty.class create mode 100644 app/src/gambas3/.src/Project/FProjectProperty.form create mode 100644 app/src/gambas3/.src/Project/FSaveProjectAs.class create mode 100644 app/src/gambas3/.src/Project/FSaveProjectAs.form create mode 100644 app/src/gambas3/.src/Project/Farm/CSoftware.class create mode 100644 app/src/gambas3/.src/Project/Farm/CSoftwareGroup.class create mode 100644 app/src/gambas3/.src/Project/Farm/FFarmConfig.class create mode 100644 app/src/gambas3/.src/Project/Farm/FFarmConfig.form create mode 100644 app/src/gambas3/.src/Project/Farm/FFarmLogin.class create mode 100644 app/src/gambas3/.src/Project/Farm/FFarmLogin.form create mode 100644 app/src/gambas3/.src/Project/Farm/FFarmRegister.class create mode 100644 app/src/gambas3/.src/Project/Farm/FFarmRegister.form create mode 100644 app/src/gambas3/.src/Project/Farm/FFarmRequest.class create mode 100644 app/src/gambas3/.src/Project/Farm/FFarmRequest.form create mode 100644 app/src/gambas3/.src/Project/Farm/FPublish.class create mode 100644 app/src/gambas3/.src/Project/Farm/FPublish.form create mode 100644 app/src/gambas3/.src/Project/Farm/FSoftwareFarm.class create mode 100644 app/src/gambas3/.src/Project/Farm/FSoftwareFarm.form create mode 100644 app/src/gambas3/.src/Project/Farm/FarmIdentity.class create mode 100644 app/src/gambas3/.src/Project/Farm/FarmRequest.class create mode 100644 app/src/gambas3/.src/Project/Farm/FarmRequestManager.module create mode 100644 app/src/gambas3/.src/Project/Farm/Publish/CTag.class create mode 100644 app/src/gambas3/.src/Project/Farm/Publish/CTagCompletion.class create mode 100644 app/src/gambas3/.src/Project/Farm/Publish/CTagEditor.class create mode 100644 app/src/gambas3/.src/Project/Farm/SoftwareBox.class create mode 100644 app/src/gambas3/.src/Project/Library/CLibraryInfo.class create mode 100644 app/src/gambas3/.src/Project/Library/FSelectLibrary.class create mode 100644 app/src/gambas3/.src/Project/Library/FSelectLibrary.form create mode 100644 app/src/gambas3/.src/Project/Library/LibraryItem.class create mode 100644 app/src/gambas3/.src/Project/Patch/FMakePatch.class create mode 100644 app/src/gambas3/.src/Project/Patch/FMakePatch.form create mode 100644 app/src/gambas3/.src/Project/Patch/FPatch.class create mode 100644 app/src/gambas3/.src/Project/Patch/FPatch.form create mode 100644 app/src/gambas3/.src/Project/Patch/Patch.class create mode 100644 app/src/gambas3/.src/Project/ProjectBox.class create mode 100644 app/src/gambas3/.src/Save.module create mode 100644 app/src/gambas3/.src/Search/CFindResult.class create mode 100644 app/src/gambas3/.src/Search/CGrepResult.class create mode 100644 app/src/gambas3/.src/Search/FSearch.class create mode 100644 app/src/gambas3/.src/Search/FSearch.form create mode 100644 app/src/gambas3/.src/Translation/CTranslation.class create mode 100644 app/src/gambas3/.src/Translation/FNewTranslation.class create mode 100644 app/src/gambas3/.src/Translation/FNewTranslation.form create mode 100644 app/src/gambas3/.src/Translation/FTranslate.class create mode 100644 app/src/gambas3/.src/Translation/FTranslate.form create mode 100644 app/src/gambas3/.src/Translation/Language.module create mode 100644 app/src/gambas3/.src/Util.module create mode 100644 app/src/gambas3/.src/Util/MErrorMessage.module create mode 100644 app/src/gambas3/.src/Util/MRemoveAccents.module create mode 100644 app/src/gambas3/.src/Util/MSdlDefaultFont.module create mode 100644 app/src/gambas3/.src/VersionControl/CVersionControl.class create mode 100644 app/src/gambas3/.src/VersionControl/CVersionControlGit.class create mode 100644 app/src/gambas3/.src/VersionControl/CVersionControlSubversion.class create mode 100644 app/src/gambas3/.src/VersionControl/FConflict.class create mode 100644 app/src/gambas3/.src/VersionControl/FConflict.form create mode 100644 app/src/gambas3/.src/VersionControl/FProjectVersion.class create mode 100644 app/src/gambas3/.src/VersionControl/FProjectVersion.form create mode 100644 app/src/gambas3/.src/VersionControl/FVersionControl.class create mode 100644 app/src/gambas3/.src/VersionControl/FVersionControl.form create mode 100644 app/src/gambas3/.src/VersionControl/FVersionError.class create mode 100644 app/src/gambas3/.src/VersionControl/FVersionError.form create mode 100644 app/src/gambas3/.src/VersionControl/VersionControl.module create mode 100644 app/src/gambas3/.src/Welcome/CCoolButton.class create mode 100644 app/src/gambas3/.src/Welcome/CSnowFlake.class create mode 100644 app/src/gambas3/.src/Welcome/CUser.class create mode 100644 app/src/gambas3/.src/Welcome/CWelcome.class create mode 100644 app/src/gambas3/.src/Welcome/FAbout.class create mode 100644 app/src/gambas3/.src/Welcome/FAbout.form create mode 100644 app/src/gambas3/.src/Welcome/FSystemInfo.class create mode 100644 app/src/gambas3/.src/Welcome/FSystemInfo.form create mode 100644 app/src/gambas3/.src/Welcome/FWelcome.class create mode 100644 app/src/gambas3/.src/Welcome/FWelcome.form create mode 120000 app/src/gambas3/AUTHORS create mode 100644 app/src/gambas3/_fake_project create mode 100644 app/src/gambas3/font/GambasBold-12.bdf create mode 100644 app/src/gambas3/font/GambasBold-13.bdf create mode 100644 app/src/gambas3/font/GambasMedium-12.bdf create mode 100644 app/src/gambas3/font/GambasMedium-13.bdf create mode 100644 app/src/gambas3/font/font.allow create mode 100644 app/src/gambas3/font/font.conf create mode 100644 app/src/gambas3/gitignore create mode 100644 app/src/gambas3/help/class-help.html create mode 100644 app/src/gambas3/help/component-help.html create mode 100644 app/src/gambas3/help/property-help.html create mode 100644 app/src/gambas3/help/symbol-help.html create mode 100644 app/src/gambas3/help/wiki/critical.png create mode 100644 app/src/gambas3/help/wiki/error-bg.png create mode 100644 app/src/gambas3/help/wiki/info.png create mode 100644 app/src/gambas3/help/wiki/page.html create mode 100644 app/src/gambas3/help/wiki/style-custom.css create mode 100644 app/src/gambas3/help/wiki/style-offline.css create mode 120000 app/src/gambas3/help/wiki/style.css create mode 100644 app/src/gambas3/help/wiki/tip.png create mode 100644 app/src/gambas3/help/wiki/up.png create mode 100644 app/src/gambas3/help/wiki/vb.png create mode 100644 app/src/gambas3/help/wiki/warning.png create mode 100644 app/src/gambas3/img/16/arrange-h-dark.png create mode 100644 app/src/gambas3/img/16/arrange-h.png create mode 100644 app/src/gambas3/img/16/arrange-hcenter-dark.png create mode 100644 app/src/gambas3/img/16/arrange-hcenter.png create mode 100644 app/src/gambas3/img/16/arrange-lr-dark.png create mode 100644 app/src/gambas3/img/16/arrange-lr.png create mode 100644 app/src/gambas3/img/16/arrange-tb-dark.png create mode 100644 app/src/gambas3/img/16/arrange-tb.png create mode 100644 app/src/gambas3/img/16/arrange-v-dark.png create mode 100644 app/src/gambas3/img/16/arrange-v.png create mode 100644 app/src/gambas3/img/16/arrange-vcenter-dark.png create mode 100644 app/src/gambas3/img/16/arrange-vcenter.png create mode 100644 app/src/gambas3/img/16/average-dark.png create mode 100644 app/src/gambas3/img/16/average.png create mode 100644 app/src/gambas3/img/16/checked-gray.png create mode 100644 app/src/gambas3/img/16/checked-lock.png create mode 100644 app/src/gambas3/img/16/checked.png create mode 100644 app/src/gambas3/img/16/close-window.png create mode 100644 app/src/gambas3/img/16/collapse-container.png create mode 100644 app/src/gambas3/img/16/cursive.png create mode 100644 app/src/gambas3/img/16/delete-container-dark.png create mode 100644 app/src/gambas3/img/16/delete-container.png create mode 100644 app/src/gambas3/img/16/deprecated.png create mode 100644 app/src/gambas3/img/16/embed-container-dark.png create mode 100644 app/src/gambas3/img/16/embed-container.png create mode 100644 app/src/gambas3/img/16/expand-container.png create mode 100644 app/src/gambas3/img/16/experimental.png create mode 100644 app/src/gambas3/img/16/fantasy.png create mode 100644 app/src/gambas3/img/16/finished.png create mode 100644 app/src/gambas3/img/16/key.png create mode 100644 app/src/gambas3/img/16/lcase-dark.png create mode 100644 app/src/gambas3/img/16/lcase.png create mode 100644 app/src/gambas3/img/16/max-window.png create mode 100644 app/src/gambas3/img/16/monospace.png create mode 100644 app/src/gambas3/img/16/percent-dark.png create mode 100644 app/src/gambas3/img/16/percent.png create mode 100644 app/src/gambas3/img/16/red-arrow-c.png create mode 100644 app/src/gambas3/img/16/red-arrow-h.png create mode 100644 app/src/gambas3/img/16/red-arrow-r.png create mode 100644 app/src/gambas3/img/16/red-arrow-v.png create mode 100644 app/src/gambas3/img/16/remove-from-container.png create mode 100644 app/src/gambas3/img/16/rename.png create mode 100644 app/src/gambas3/img/16/round-ne.png create mode 100644 app/src/gambas3/img/16/round-nw.png create mode 100644 app/src/gambas3/img/16/round-se.png create mode 100644 app/src/gambas3/img/16/round-sw.png create mode 100644 app/src/gambas3/img/16/sans-serif.png create mode 100644 app/src/gambas3/img/16/select.png create mode 100644 app/src/gambas3/img/16/separator.png create mode 100644 app/src/gambas3/img/16/serif.png create mode 100644 app/src/gambas3/img/16/stack-dark.png create mode 100644 app/src/gambas3/img/16/stack.png create mode 100644 app/src/gambas3/img/16/tabmove.png create mode 100644 app/src/gambas3/img/16/tabstrip.png create mode 100644 app/src/gambas3/img/16/tile.png create mode 100644 app/src/gambas3/img/16/toggle-container-dark.png create mode 100644 app/src/gambas3/img/16/toggle-container.png create mode 100644 app/src/gambas3/img/16/ucase-dark.png create mode 100644 app/src/gambas3/img/16/ucase.png create mode 100644 app/src/gambas3/img/16/unchecked.png create mode 100644 app/src/gambas3/img/16/unfinished.png create mode 100644 app/src/gambas3/img/16/white-close.png create mode 100644 app/src/gambas3/img/32/added.png create mode 100644 app/src/gambas3/img/32/comment-dark.png create mode 100644 app/src/gambas3/img/32/comment.png create mode 100644 app/src/gambas3/img/32/conflict.png create mode 100644 app/src/gambas3/img/32/cross.png create mode 100644 app/src/gambas3/img/32/do-not-translate.png create mode 100644 app/src/gambas3/img/32/eol-dark.png create mode 100644 app/src/gambas3/img/32/eol.png create mode 100644 app/src/gambas3/img/32/exported.png create mode 100644 app/src/gambas3/img/32/filter-menu.png create mode 100644 app/src/gambas3/img/32/filter.png create mode 100644 app/src/gambas3/img/32/green-arrow.png create mode 100644 app/src/gambas3/img/32/linked.png create mode 100644 app/src/gambas3/img/32/magic.png create mode 100644 app/src/gambas3/img/32/modified.png create mode 100644 app/src/gambas3/img/32/opengl.png create mode 100644 app/src/gambas3/img/32/startup-dark.png create mode 100644 app/src/gambas3/img/32/startup.png create mode 100644 app/src/gambas3/img/32/tab-dark.png create mode 100644 app/src/gambas3/img/32/tab.png create mode 100644 app/src/gambas3/img/32/uncomment-dark.png create mode 100644 app/src/gambas3/img/32/uncomment.png create mode 100644 app/src/gambas3/img/32/wrap-dark.png create mode 100644 app/src/gambas3/img/32/wrap.png create mode 100644 app/src/gambas3/img/48/all.png create mode 100644 app/src/gambas3/img/64/atari-dark.png create mode 100644 app/src/gambas3/img/64/atari.png create mode 100644 app/src/gambas3/img/background/baraka.png create mode 100644 app/src/gambas3/img/background/christ.png create mode 100644 app/src/gambas3/img/background/circle.png create mode 100644 app/src/gambas3/img/background/cross.png create mode 100644 app/src/gambas3/img/background/cubism.png create mode 100644 app/src/gambas3/img/background/dark.png create mode 100644 app/src/gambas3/img/background/gambas.png create mode 100644 app/src/gambas3/img/background/hline.png create mode 100644 app/src/gambas3/img/background/hose.png create mode 100644 app/src/gambas3/img/background/illusion.png create mode 100644 app/src/gambas3/img/background/labyrinth.png create mode 100644 app/src/gambas3/img/background/light.png create mode 100644 app/src/gambas3/img/background/list create mode 100644 app/src/gambas3/img/background/medracim.png create mode 100644 app/src/gambas3/img/background/mosaic.png create mode 100644 app/src/gambas3/img/background/muhammad.png create mode 100644 app/src/gambas3/img/background/nature.png create mode 100644 app/src/gambas3/img/background/oil.png create mode 100644 app/src/gambas3/img/background/point.png create mode 100644 app/src/gambas3/img/background/quasi.png create mode 100644 app/src/gambas3/img/background/smoke.png create mode 100644 app/src/gambas3/img/background/square.png create mode 100644 app/src/gambas3/img/background/star.png create mode 100644 app/src/gambas3/img/background/tawhid.png create mode 100644 app/src/gambas3/img/background/vline.png create mode 100644 app/src/gambas3/img/background/warda.png create mode 100644 app/src/gambas3/img/background/weave.png create mode 100644 app/src/gambas3/img/broken.svg create mode 100644 app/src/gambas3/img/contrib/Radoslav.gif create mode 100644 app/src/gambas3/img/control/button.png create mode 100644 app/src/gambas3/img/control/checkbox.png create mode 100644 app/src/gambas3/img/control/columnview.png create mode 100644 app/src/gambas3/img/control/combobox.png create mode 100644 app/src/gambas3/img/control/dial.png create mode 100644 app/src/gambas3/img/control/dnsclient.png create mode 100644 app/src/gambas3/img/control/drawingarea.png create mode 100644 app/src/gambas3/img/control/editor.png create mode 100644 app/src/gambas3/img/control/embedder.png create mode 100644 app/src/gambas3/img/control/frame.png create mode 100644 app/src/gambas3/img/control/ftpclient.png create mode 100644 app/src/gambas3/img/control/glarea.png create mode 100644 app/src/gambas3/img/control/gridview.png create mode 100644 app/src/gambas3/img/control/hbox.png create mode 100644 app/src/gambas3/img/control/hpanel.png create mode 100644 app/src/gambas3/img/control/hsplit.png create mode 100644 app/src/gambas3/img/control/httpclient.png create mode 100644 app/src/gambas3/img/control/iconview.png create mode 100644 app/src/gambas3/img/control/label.png create mode 100644 app/src/gambas3/img/control/lcdnumber.png create mode 100644 app/src/gambas3/img/control/listbox.png create mode 100644 app/src/gambas3/img/control/listview.png create mode 100644 app/src/gambas3/img/control/moviebox.png create mode 100644 app/src/gambas3/img/control/panel.png create mode 100644 app/src/gambas3/img/control/picturebox.png create mode 100644 app/src/gambas3/img/control/printer.png create mode 100644 app/src/gambas3/img/control/progressbar.png create mode 100644 app/src/gambas3/img/control/radiobutton.png create mode 100644 app/src/gambas3/img/control/scrollarea.png create mode 100644 app/src/gambas3/img/control/scrollbar.png create mode 100644 app/src/gambas3/img/control/scrollview.png create mode 100644 app/src/gambas3/img/control/select.png create mode 100644 app/src/gambas3/img/control/separator.png create mode 100644 app/src/gambas3/img/control/serialport.png create mode 100644 app/src/gambas3/img/control/serversocket.png create mode 100644 app/src/gambas3/img/control/slider.png create mode 100644 app/src/gambas3/img/control/socket.png create mode 100644 app/src/gambas3/img/control/spinbox.png create mode 100644 app/src/gambas3/img/control/spring.png create mode 100644 app/src/gambas3/img/control/tabstrip.png create mode 100644 app/src/gambas3/img/control/textarea.png create mode 100644 app/src/gambas3/img/control/textbox.png create mode 100644 app/src/gambas3/img/control/textedit.png create mode 100644 app/src/gambas3/img/control/textlabel.png create mode 100644 app/src/gambas3/img/control/timer.png create mode 100644 app/src/gambas3/img/control/togglebutton.png create mode 100644 app/src/gambas3/img/control/toolbutton.png create mode 100644 app/src/gambas3/img/control/trayicon.png create mode 100644 app/src/gambas3/img/control/treeview.png create mode 100644 app/src/gambas3/img/control/udpsocket.png create mode 100644 app/src/gambas3/img/control/unknown.png create mode 100644 app/src/gambas3/img/control/vbox.png create mode 100644 app/src/gambas3/img/control/voidbutton.png create mode 100644 app/src/gambas3/img/control/vpanel.png create mode 100644 app/src/gambas3/img/control/vsplit.png create mode 100644 app/src/gambas3/img/control/webview.png create mode 100644 app/src/gambas3/img/draw/cap-butt-dark.png create mode 100644 app/src/gambas3/img/draw/cap-butt.png create mode 100644 app/src/gambas3/img/draw/cap-round-dark.png create mode 100644 app/src/gambas3/img/draw/cap-round.png create mode 100644 app/src/gambas3/img/draw/cap-square-dark.png create mode 100644 app/src/gambas3/img/draw/cap-square.png create mode 100644 app/src/gambas3/img/draw/crop-dark.png create mode 100644 app/src/gambas3/img/draw/crop.png create mode 100644 app/src/gambas3/img/draw/desaturate-dark.png create mode 100644 app/src/gambas3/img/draw/desaturate.png create mode 100644 app/src/gambas3/img/draw/difference-dark.png create mode 100644 app/src/gambas3/img/draw/difference.png create mode 100644 app/src/gambas3/img/draw/duplicate-dark.png create mode 100644 app/src/gambas3/img/draw/duplicate.png create mode 100644 app/src/gambas3/img/draw/ellipse-dark.png create mode 100644 app/src/gambas3/img/draw/ellipse.png create mode 100644 app/src/gambas3/img/draw/exclusive-dark.png create mode 100644 app/src/gambas3/img/draw/exclusive.png create mode 100644 app/src/gambas3/img/draw/grid-dark.png create mode 100644 app/src/gambas3/img/draw/grid.png create mode 100644 app/src/gambas3/img/draw/hflip-dark.png create mode 100644 app/src/gambas3/img/draw/hflip.png create mode 100644 app/src/gambas3/img/draw/hide-dark.png create mode 100644 app/src/gambas3/img/draw/hide.png create mode 100644 app/src/gambas3/img/draw/intersection-dark.png create mode 100644 app/src/gambas3/img/draw/intersection.png create mode 100644 app/src/gambas3/img/draw/invert-dark.png create mode 100644 app/src/gambas3/img/draw/invert.png create mode 100644 app/src/gambas3/img/draw/join-bevel-dark.png create mode 100644 app/src/gambas3/img/draw/join-bevel.png create mode 100644 app/src/gambas3/img/draw/join-miter-dark.png create mode 100644 app/src/gambas3/img/draw/join-miter.png create mode 100644 app/src/gambas3/img/draw/join-round-dark.png create mode 100644 app/src/gambas3/img/draw/join-round.png create mode 100644 app/src/gambas3/img/draw/line-dark.png create mode 100644 app/src/gambas3/img/draw/line.png create mode 100644 app/src/gambas3/img/draw/magic-dark.png create mode 100644 app/src/gambas3/img/draw/magic.png create mode 100644 app/src/gambas3/img/draw/move.png create mode 100644 app/src/gambas3/img/draw/offset-dark.png create mode 100644 app/src/gambas3/img/draw/offset.png create mode 100644 app/src/gambas3/img/draw/rectangle-dark.png create mode 100644 app/src/gambas3/img/draw/rectangle.png create mode 100644 app/src/gambas3/img/draw/resize-dark.png create mode 100644 app/src/gambas3/img/draw/resize.png create mode 100644 app/src/gambas3/img/draw/rotate-dark.png create mode 100644 app/src/gambas3/img/draw/rotate-left-dark.png create mode 100644 app/src/gambas3/img/draw/rotate-left.png create mode 100644 app/src/gambas3/img/draw/rotate-right-dark.png create mode 100644 app/src/gambas3/img/draw/rotate-right.png create mode 100644 app/src/gambas3/img/draw/rotate.png create mode 100644 app/src/gambas3/img/draw/scroll-dark.png create mode 100644 app/src/gambas3/img/draw/scroll.png create mode 100644 app/src/gambas3/img/draw/shgrid.png create mode 100644 app/src/gambas3/img/draw/text-base-dark.png create mode 100644 app/src/gambas3/img/draw/text-base.png create mode 100644 app/src/gambas3/img/draw/text-bottom-dark.png create mode 100644 app/src/gambas3/img/draw/text-bottom.png create mode 100644 app/src/gambas3/img/draw/text-dark.png create mode 100644 app/src/gambas3/img/draw/text-middle-dark.png create mode 100644 app/src/gambas3/img/draw/text-middle.png create mode 100644 app/src/gambas3/img/draw/text-top-dark.png create mode 100644 app/src/gambas3/img/draw/text-top.png create mode 100644 app/src/gambas3/img/draw/text.png create mode 100644 app/src/gambas3/img/draw/transparent-dark.png create mode 100644 app/src/gambas3/img/draw/transparent.png create mode 100644 app/src/gambas3/img/draw/union-dark.png create mode 100644 app/src/gambas3/img/draw/union.png create mode 100644 app/src/gambas3/img/draw/vflip-dark.png create mode 100644 app/src/gambas3/img/draw/vflip.png create mode 100644 app/src/gambas3/img/family/column-invert.png create mode 100644 app/src/gambas3/img/family/column.png create mode 100644 app/src/gambas3/img/family/expand.png create mode 100644 app/src/gambas3/img/family/fill.png create mode 100644 app/src/gambas3/img/family/horizontal-invert.png create mode 100644 app/src/gambas3/img/family/horizontal.png create mode 100644 app/src/gambas3/img/family/hsplit.png create mode 100644 app/src/gambas3/img/family/row-invert.png create mode 100644 app/src/gambas3/img/family/row.png create mode 100644 app/src/gambas3/img/family/vertical-invert.png create mode 100644 app/src/gambas3/img/family/vertical.png create mode 100644 app/src/gambas3/img/family/vsplit.png create mode 100644 app/src/gambas3/img/fir.png create mode 100644 app/src/gambas3/img/logo/archlinux.png create mode 100644 app/src/gambas3/img/logo/autotools.png create mode 100644 app/src/gambas3/img/logo/debian.png create mode 100644 app/src/gambas3/img/logo/fedora.png create mode 100644 app/src/gambas3/img/logo/folder.png create mode 100644 app/src/gambas3/img/logo/gambas.svg create mode 100644 app/src/gambas3/img/logo/head-16.png create mode 100644 app/src/gambas3/img/logo/head-256.png create mode 100644 app/src/gambas3/img/logo/head-32.png create mode 100644 app/src/gambas3/img/logo/logo-ide.png create mode 100644 app/src/gambas3/img/logo/logo.png create mode 100644 app/src/gambas3/img/logo/mageia.png create mode 100644 app/src/gambas3/img/logo/mandriva.png create mode 100644 app/src/gambas3/img/logo/package-gnu.png create mode 100644 app/src/gambas3/img/logo/project.png create mode 100644 app/src/gambas3/img/logo/redhat.png create mode 100644 app/src/gambas3/img/logo/self-extractible.png create mode 100644 app/src/gambas3/img/logo/slackware.png create mode 100644 app/src/gambas3/img/logo/suse.png create mode 100644 app/src/gambas3/img/logo/ubuntu.png create mode 100644 app/src/gambas3/img/module/class-dark.png create mode 100644 app/src/gambas3/img/module/class.png create mode 100644 app/src/gambas3/img/module/form-dark.png create mode 100644 app/src/gambas3/img/module/form.png create mode 100644 app/src/gambas3/img/module/module-dark.png create mode 100644 app/src/gambas3/img/module/module.png create mode 100644 app/src/gambas3/img/module/report-dark.png create mode 100644 app/src/gambas3/img/module/report.png create mode 100644 app/src/gambas3/img/module/termform-dark.png create mode 100644 app/src/gambas3/img/module/termform.png create mode 100644 app/src/gambas3/img/module/webform-dark.png create mode 100644 app/src/gambas3/img/module/webform.png create mode 100644 app/src/gambas3/img/module/webpage-dark.png create mode 100644 app/src/gambas3/img/module/webpage.png create mode 100644 app/src/gambas3/img/search.png create mode 100644 app/src/gambas3/img/snowflake.png create mode 100644 app/src/gambas3/img/symbol/constant.png create mode 100644 app/src/gambas3/img/symbol/control.png create mode 100644 app/src/gambas3/img/symbol/event.png create mode 100644 app/src/gambas3/img/symbol/method.png create mode 100644 app/src/gambas3/img/symbol/property-ro.png create mode 100644 app/src/gambas3/img/symbol/property.png create mode 100644 app/src/gambas3/img/symbol/s-method.png create mode 100644 app/src/gambas3/img/symbol/s-property-ro.png create mode 100644 app/src/gambas3/img/symbol/s-property.png create mode 100644 app/src/gambas3/img/symbol/s-variable.png create mode 100644 app/src/gambas3/img/symbol/special.png create mode 100644 app/src/gambas3/img/symbol/symbol.png create mode 100644 app/src/gambas3/img/symbol/unknown.png create mode 100644 app/src/gambas3/img/symbol/variable.png create mode 100644 app/src/gambas3/img/waiting.gif create mode 100644 app/src/gambas3/img/welcome/border.png create mode 100644 app/src/gambas3/img/welcome/corner.png create mode 100644 app/src/gambas3/install/Makefile.am create mode 100644 app/src/gambas3/install/acinclude.m4 create mode 100644 app/src/gambas3/install/categories create mode 100644 app/src/gambas3/install/group/archlinux create mode 100644 app/src/gambas3/install/group/autotools create mode 100644 app/src/gambas3/install/group/debian create mode 100644 app/src/gambas3/install/group/fedora create mode 100644 app/src/gambas3/install/group/mageia create mode 100644 app/src/gambas3/install/group/mandriva create mode 100644 app/src/gambas3/install/group/self create mode 100644 app/src/gambas3/install/group/slackware create mode 100644 app/src/gambas3/install/group/suse create mode 100644 app/src/gambas3/install/group/ubuntu create mode 100644 app/src/gambas3/install/menu/archlinux create mode 100644 app/src/gambas3/install/menu/autotools create mode 100644 app/src/gambas3/install/menu/debian create mode 100644 app/src/gambas3/install/menu/fedora create mode 100644 app/src/gambas3/install/menu/mageia create mode 100644 app/src/gambas3/install/menu/mandriva create mode 100644 app/src/gambas3/install/menu/self create mode 100644 app/src/gambas3/install/menu/slackware create mode 100644 app/src/gambas3/install/menu/suse create mode 100644 app/src/gambas3/install/menu/ubuntu create mode 100644 app/src/gambas3/install/slack-desc-header create mode 100644 app/src/gambas3/license create mode 100644 app/src/gambas3/po-header.txt create mode 100644 app/src/gambas3/pot-header.txt create mode 100644 app/src/gambas3/shortcut.desktop create mode 100644 app/src/gambas3/snippets create mode 100644 app/src/gambas3/support.txt create mode 100644 app/src/gambas3/tags.txt create mode 100644 app/src/gambas3/template/CClass.class create mode 100644 app/src/gambas3/template/CContainer.class create mode 100644 app/src/gambas3/template/CControl.class create mode 100644 app/src/gambas3/template/FMain.class create mode 100644 app/src/gambas3/template/FMain.form create mode 100644 app/src/gambas3/template/FTest.class create mode 100644 app/src/gambas3/template/FTest.form create mode 100644 app/src/gambas3/template/MMain.module create mode 100644 app/src/gambas3/template/MTest.module create mode 100644 app/src/gambas3/template/_project create mode 100644 app/src/gambas3/template/ccontainer.png create mode 100644 app/src/gambas3/template/ccontrol.png create mode 100644 app/src/gambas3/template/list create mode 100644 app/src/gambas3/theme/amber create mode 100644 app/src/gambas3/theme/amethyst create mode 100644 app/src/gambas3/theme/blues create mode 100644 app/src/gambas3/theme/emerald create mode 100644 app/src/gambas3/theme/gambas create mode 100644 app/src/gambas3/theme/obsidian create mode 100644 app/src/gambas3/theme/pastel create mode 100644 app/src/gambas3/theme/quest create mode 100644 app/src/gambas3/theme/quick create mode 100644 app/src/gambas3/theme/ruby create mode 100644 app/src/gambas3/theme/sapphire create mode 100644 app/src/gambas3/theme/visual create mode 100644 app/src/gambas3/theme/zen create mode 100644 app/src/gambas3/tips/tips.ca create mode 100644 app/src/gambas3/tips/tips.cs create mode 100644 app/src/gambas3/tips/tips.de create mode 100644 app/src/gambas3/tips/tips.en create mode 100644 app/src/gambas3/tips/tips.es create mode 100644 app/src/gambas3/tips/tips.fr create mode 100644 app/src/gambas3/tips/tips.it create mode 100644 app/src/gambas3/tips/tips.nl create mode 100644 app/src/gambas3/tips/tips.pl create mode 100644 app/src/gambas3/tips/tips.sl create mode 100644 app/src/gambas3/tips/tips.sv create mode 100644 app/src/gambas3/tips/tips.tr create mode 100644 app/src/gambas3/tips/tips.zh create mode 100644 app/src/gambas3/tips/tips.zh_TW create mode 100644 app/src/gambas3/usage create mode 100644 app/src/gb.wiki/.directory create mode 100644 app/src/gb.wiki/.icon.png create mode 100644 app/src/gb.wiki/.lang/nl.mo create mode 100644 app/src/gb.wiki/.lang/nl.po create mode 100644 app/src/gb.wiki/.lang/zh.mo create mode 100644 app/src/gb.wiki/.lang/zh.po create mode 100644 app/src/gb.wiki/.project create mode 100644 app/src/gb.wiki/.src/Actions.module create mode 100644 app/src/gb.wiki/.src/Controllers/Admin.module create mode 100644 app/src/gb.wiki/.src/Controllers/Image.module create mode 100644 app/src/gb.wiki/.src/Controllers/UserManage.module create mode 100644 app/src/gb.wiki/.src/Controllers/Wiki.module create mode 100644 app/src/gb.wiki/.src/DBScheme.class create mode 100644 app/src/gb.wiki/.src/Main.module create mode 100644 app/src/gb.wiki/.src/Models/Comments.module create mode 100644 app/src/gb.wiki/.src/Models/Pages.module create mode 100644 app/src/gb.wiki/.src/Models/Users.module create mode 100644 app/src/gb.wiki/.src/Models/_Page.class create mode 100644 app/src/gb.wiki/.src/Models/_User.class create mode 100644 app/src/gb.wiki/.src/Parser.module create mode 100644 app/src/gb.wiki/.src/Path.module create mode 100644 app/src/gb.wiki/.src/Views/Admin/WAdminMain.class create mode 100644 app/src/gb.wiki/.src/Views/Admin/WAdminMain.webpage create mode 100644 app/src/gb.wiki/.src/Views/WAccount.class create mode 100644 app/src/gb.wiki/.src/Views/WAccount.webpage create mode 100644 app/src/gb.wiki/.src/Views/WEdit.class create mode 100644 app/src/gb.wiki/.src/Views/WEdit.webpage create mode 100644 app/src/gb.wiki/.src/Views/WHeader.class create mode 100644 app/src/gb.wiki/.src/Views/WHeader.webpage create mode 100644 app/src/gb.wiki/.src/Views/WLogin.class create mode 100644 app/src/gb.wiki/.src/Views/WLogin.webpage create mode 100644 app/src/gb.wiki/.src/Views/WWiki.class create mode 100644 app/src/gb.wiki/.src/Views/WWiki.webpage create mode 100644 app/src/gb.wiki/help.png create mode 100644 app/src/gbs3/.directory create mode 100644 app/src/gbs3/.icon.png create mode 100644 app/src/gbs3/.project create mode 100644 app/src/gbs3/.src/CComponent.class create mode 100644 app/src/gbs3/.src/MMain.module create mode 100644 app/src/gbs3/.src/MServerPage.module create mode 100644 app/src/gbs3/icon.png create mode 100644 app/src/gbs3/license create mode 100644 app/src/gbs3/usage-gbs create mode 100644 app/src/gbs3/usage-gbw create mode 100644 app/template/console/.directory create mode 100644 app/template/console/.icon.png create mode 100644 app/template/console/.lang/es.mo create mode 100644 app/template/console/.lang/es.po create mode 100644 app/template/console/.lang/es_ES.mo create mode 100644 app/template/console/.lang/es_ES.po create mode 100644 app/template/console/.lang/fr.mo create mode 100644 app/template/console/.lang/fr.po create mode 100644 app/template/console/.project create mode 100644 app/template/console/.src/Main.module create mode 100644 app/template/console/icon.png create mode 100644 app/template/database/.directory create mode 100644 app/template/database/.icon.png create mode 100644 app/template/database/.project create mode 100644 app/template/database/.src/FMain.class create mode 100644 app/template/database/.src/FMain.form create mode 100644 app/template/database/icon.png create mode 100644 app/template/graphical/.directory create mode 100644 app/template/graphical/.icon.png create mode 100644 app/template/graphical/.lang/es.mo create mode 100644 app/template/graphical/.lang/es.po create mode 100644 app/template/graphical/.lang/es_ES.mo create mode 100644 app/template/graphical/.lang/es_ES.po create mode 100644 app/template/graphical/.lang/fr.mo create mode 100644 app/template/graphical/.lang/fr.po create mode 100644 app/template/graphical/.project create mode 100644 app/template/graphical/.src/FMain.class create mode 100644 app/template/graphical/.src/FMain.form create mode 100644 app/template/graphical/icon.png create mode 100644 app/template/gtk2/.directory create mode 100644 app/template/gtk2/.icon.png create mode 100644 app/template/gtk2/.lang/es.mo create mode 100644 app/template/gtk2/.lang/es.po create mode 100644 app/template/gtk2/.lang/es_ES.mo create mode 100644 app/template/gtk2/.lang/es_ES.po create mode 100644 app/template/gtk2/.lang/fr.mo create mode 100644 app/template/gtk2/.lang/fr.po create mode 100644 app/template/gtk2/.project create mode 100644 app/template/gtk2/.src/FMain.class create mode 100644 app/template/gtk2/.src/FMain.form create mode 100644 app/template/gtk2/icon.png create mode 100644 app/template/gtk3/.directory create mode 100644 app/template/gtk3/.icon.png create mode 100644 app/template/gtk3/.lang/es.mo create mode 100644 app/template/gtk3/.lang/es.po create mode 100644 app/template/gtk3/.lang/fr.mo create mode 100644 app/template/gtk3/.lang/fr.po create mode 100644 app/template/gtk3/.project create mode 100644 app/template/gtk3/.src/FMain.class create mode 100644 app/template/gtk3/.src/FMain.form create mode 100644 app/template/gtk3/icon.png create mode 100644 app/template/qt/.directory create mode 100644 app/template/qt/.icon.png create mode 100644 app/template/qt/.lang/es.mo create mode 100644 app/template/qt/.lang/es.po create mode 100644 app/template/qt/.lang/es_ES.mo create mode 100644 app/template/qt/.lang/es_ES.po create mode 100644 app/template/qt/.lang/fr.mo create mode 100644 app/template/qt/.lang/fr.po create mode 100644 app/template/qt/.project create mode 100644 app/template/qt/.src/FMain.class create mode 100644 app/template/qt/.src/FMain.form create mode 100644 app/template/qt/icon.png create mode 100644 app/template/sdl/.directory create mode 100644 app/template/sdl/.icon.png create mode 100644 app/template/sdl/.lang/es.mo create mode 100644 app/template/sdl/.lang/es.po create mode 100644 app/template/sdl/.lang/es_ES.mo create mode 100644 app/template/sdl/.lang/es_ES.po create mode 100644 app/template/sdl/.lang/fr.mo create mode 100644 app/template/sdl/.lang/fr.po create mode 100644 app/template/sdl/.project create mode 100644 app/template/sdl/.src/MMain.module create mode 100644 app/template/sdl/icon.png create mode 100644 app/template/web/.directory create mode 100644 app/template/web/.icon.png create mode 100644 app/template/web/.lang/es.mo create mode 100644 app/template/web/.lang/es.po create mode 100644 app/template/web/.lang/es_ES.mo create mode 100644 app/template/web/.lang/es_ES.po create mode 100644 app/template/web/.lang/fr.mo create mode 100644 app/template/web/.lang/fr.po create mode 100644 app/template/web/.project create mode 100644 app/template/web/.src/Main.class create mode 100644 app/template/web/.src/Main.webpage create mode 100644 app/template/web/icon.png create mode 100644 app/template/webform/.directory create mode 100644 app/template/webform/.icon.png create mode 100644 app/template/webform/.lang/fr.mo create mode 100644 app/template/webform/.lang/fr.po create mode 100644 app/template/webform/.project create mode 100644 app/template/webform/.src/Webform1.class create mode 100644 app/template/webform/.src/Webform1.webform create mode 100644 app/template/webform/icon.png create mode 100644 app/template/~subversion/.directory create mode 100644 app/template/~subversion/.icon.png create mode 100644 app/template/~subversion/.lang/es.mo create mode 100644 app/template/~subversion/.lang/es.po create mode 100644 app/template/~subversion/.lang/es_ES.mo create mode 100644 app/template/~subversion/.lang/es_ES.po create mode 100644 app/template/~subversion/.lang/fr.mo create mode 100644 app/template/~subversion/.lang/fr.po create mode 100644 app/template/~subversion/.project create mode 100644 app/template/~subversion/.src/Main.module create mode 100644 app/template/~subversion/icon.png create mode 100755 benchmark/benchmark.gbs create mode 100755 benchmark/mandelbrot.gbs create mode 100755 benchmark/mandelbrot.pl create mode 100755 benchmark/mandelbrot.py create mode 100755 benchmark/nbody.gbs create mode 100755 benchmark/nbody.pl create mode 100755 benchmark/nbody.py create mode 100755 benchmark/polynom.gbs create mode 100755 benchmark/polynom.pl create mode 100755 benchmark/polynom.py create mode 100755 benchmark/primes.gbs create mode 100755 benchmark/primes.pl create mode 100755 benchmark/primes.py create mode 100755 benchmark/sort.gbs create mode 100755 benchmark/sort.pl create mode 100755 benchmark/sort.py create mode 100755 benchmark/string.gbs create mode 100755 benchmark/string.pl create mode 100755 benchmark/string.py create mode 100755 build-dist create mode 100644 comp/AUTHORS create mode 120000 comp/COPYING create mode 100644 comp/ChangeLog create mode 100644 comp/INSTALL create mode 100644 comp/Makefile.am create mode 100644 comp/NEWS create mode 100644 comp/README create mode 100644 comp/TODO create mode 120000 comp/acinclude.m4 create mode 100644 comp/configure.ac create mode 120000 comp/reconf create mode 100644 comp/src/gb.args/.component create mode 100644 comp/src/gb.args/.directory create mode 100644 comp/src/gb.args/.icon.png create mode 100644 comp/src/gb.args/.lang/cs.mo create mode 100644 comp/src/gb.args/.lang/cs.po create mode 100644 comp/src/gb.args/.lang/es.mo create mode 100644 comp/src/gb.args/.lang/es.po create mode 100644 comp/src/gb.args/.lang/es_ES.mo create mode 100644 comp/src/gb.args/.lang/es_ES.po create mode 100644 comp/src/gb.args/.lang/fr.mo create mode 100644 comp/src/gb.args/.lang/fr.po create mode 100644 comp/src/gb.args/.lang/nl.mo create mode 100644 comp/src/gb.args/.lang/nl.po create mode 100644 comp/src/gb.args/.lang/zh.mo create mode 100644 comp/src/gb.args/.lang/zh.po create mode 100644 comp/src/gb.args/.project create mode 100644 comp/src/gb.args/.src/Args.module create mode 100644 comp/src/gb.args/.src/MMain.module create mode 100644 comp/src/gb.chart/.component create mode 100644 comp/src/gb.chart/.directory create mode 100644 comp/src/gb.chart/.icon.png create mode 100644 comp/src/gb.chart/.project create mode 100644 comp/src/gb.chart/.src/CPoint.class create mode 100644 comp/src/gb.chart/.src/CRect.class create mode 100644 comp/src/gb.chart/.src/Chart.class create mode 100644 comp/src/gb.chart/.src/ChartStyle.class create mode 100644 comp/src/gb.chart/.src/ChartType.class create mode 100644 comp/src/gb.chart/.src/FTest.class create mode 100644 comp/src/gb.chart/.src/FTest.form create mode 100644 comp/src/gb.chart/.src/FTest2.class create mode 100644 comp/src/gb.chart/.src/FTest2.form create mode 100644 comp/src/gb.chart/.src/MTools.module create mode 100644 comp/src/gb.chart/.src/Styles/_CSerie.class create mode 100644 comp/src/gb.chart/.src/Styles/_CStyleAreas.class create mode 100644 comp/src/gb.chart/.src/Styles/_CStyleBar.class create mode 100644 comp/src/gb.chart/.src/Styles/_CStyleColumns.class create mode 100644 comp/src/gb.chart/.src/Styles/_CStyleLine.class create mode 100644 comp/src/gb.chart/.src/Styles/_CStylePie.class create mode 100644 comp/src/gb.chart/.src/Styles/_CStylePlot.class create mode 100644 comp/src/gb.chart/.src/_CAxes.class create mode 100644 comp/src/gb.chart/.src/_CHeaders.class create mode 100644 comp/src/gb.chart/.src/_CLabel.class create mode 100644 comp/src/gb.chart/.src/_CLabels.class create mode 100644 comp/src/gb.chart/.src/_CLegend.class create mode 100644 comp/src/gb.chart/.src/_CTitle.class create mode 100644 comp/src/gb.chart/.src/_CXAxe.class create mode 100644 comp/src/gb.chart/.src/_CYAxe.class create mode 100644 comp/src/gb.chart/.src/_Colors.class create mode 100644 comp/src/gb.chart/img/areas.png create mode 100644 comp/src/gb.chart/img/areasnormal.png create mode 100644 comp/src/gb.chart/img/areaspercent.png create mode 100644 comp/src/gb.chart/img/areasstacked.png create mode 100644 comp/src/gb.chart/img/bars.png create mode 100644 comp/src/gb.chart/img/barsnormal.png create mode 100644 comp/src/gb.chart/img/barspercent.png create mode 100644 comp/src/gb.chart/img/barsstacked.png create mode 100644 comp/src/gb.chart/img/columns.png create mode 100644 comp/src/gb.chart/img/columnslinecombination.png create mode 100644 comp/src/gb.chart/img/columnsnormal.png create mode 100644 comp/src/gb.chart/img/columnspercent.png create mode 100644 comp/src/gb.chart/img/columnsstacked.png create mode 100644 comp/src/gb.chart/img/lines.png create mode 100644 comp/src/gb.chart/img/linesnormal.png create mode 100644 comp/src/gb.chart/img/linespercent.png create mode 100644 comp/src/gb.chart/img/linesstacked.png create mode 100644 comp/src/gb.chart/img/linessymbols.png create mode 100644 comp/src/gb.chart/img/net.png create mode 100644 comp/src/gb.chart/img/pie.png create mode 100644 comp/src/gb.chart/img/pienormal.png create mode 100644 comp/src/gb.chart/img/pieoffset1.png create mode 100644 comp/src/gb.chart/img/pieoffset2.png create mode 100644 comp/src/gb.chart/img/pierings.png create mode 100644 comp/src/gb.chart/img/plots.png create mode 100644 comp/src/gb.chart/img/plotsnormal.png create mode 100644 comp/src/gb.chart/img/stockchart.png create mode 100644 comp/src/gb.db.form/.component create mode 100644 comp/src/gb.db.form/.connection/Connection1.connection create mode 100644 comp/src/gb.db.form/.connection/Connection2.connection create mode 100644 comp/src/gb.db.form/.connection/Connection3.connection create mode 100644 comp/src/gb.db.form/.connection/Connection4.connection create mode 100644 comp/src/gb.db.form/.directory create mode 100644 comp/src/gb.db.form/.hidden/control/databrowser.png create mode 100644 comp/src/gb.db.form/.hidden/control/datacheckbox.png create mode 100644 comp/src/gb.db.form/.hidden/control/datacombo.png create mode 100644 comp/src/gb.db.form/.hidden/control/datacomboview.png create mode 100644 comp/src/gb.db.form/.hidden/control/datacontrol.png create mode 100644 comp/src/gb.db.form/.hidden/control/datasource.png create mode 100644 comp/src/gb.db.form/.hidden/control/dataview.png create mode 100644 comp/src/gb.db.form/.icon.png create mode 100644 comp/src/gb.db.form/.lang/ca.mo create mode 100644 comp/src/gb.db.form/.lang/ca.po create mode 100644 comp/src/gb.db.form/.lang/cs.mo create mode 100644 comp/src/gb.db.form/.lang/cs.po create mode 100644 comp/src/gb.db.form/.lang/de.mo create mode 100644 comp/src/gb.db.form/.lang/de.po create mode 100644 comp/src/gb.db.form/.lang/es.mo create mode 100644 comp/src/gb.db.form/.lang/es.po create mode 100644 comp/src/gb.db.form/.lang/es_ES.mo create mode 100644 comp/src/gb.db.form/.lang/es_ES.po create mode 100644 comp/src/gb.db.form/.lang/fr.mo create mode 100644 comp/src/gb.db.form/.lang/fr.po create mode 100644 comp/src/gb.db.form/.lang/it.mo create mode 100644 comp/src/gb.db.form/.lang/it.po create mode 100644 comp/src/gb.db.form/.lang/nl.mo create mode 100644 comp/src/gb.db.form/.lang/nl.po create mode 100644 comp/src/gb.db.form/.lang/pt_BR.mo create mode 100644 comp/src/gb.db.form/.lang/pt_BR.po create mode 100644 comp/src/gb.db.form/.lang/sv.mo create mode 100644 comp/src/gb.db.form/.lang/sv.po create mode 100644 comp/src/gb.db.form/.lang/zh.mo create mode 100644 comp/src/gb.db.form/.lang/zh.po create mode 100644 comp/src/gb.db.form/.project create mode 100644 comp/src/gb.db.form/.src/Common.module create mode 100644 comp/src/gb.db.form/.src/DataBrowser.class create mode 100644 comp/src/gb.db.form/.src/DataCheckBox.class create mode 100644 comp/src/gb.db.form/.src/DataCombo.class create mode 100644 comp/src/gb.db.form/.src/DataComboView.class create mode 100644 comp/src/gb.db.form/.src/DataConnection.class create mode 100644 comp/src/gb.db.form/.src/DataControl.class create mode 100644 comp/src/gb.db.form/.src/DataField.class create mode 100644 comp/src/gb.db.form/.src/DataSource.class create mode 100644 comp/src/gb.db.form/.src/DataTable.class create mode 100644 comp/src/gb.db.form/.src/DataTree.class create mode 100644 comp/src/gb.db.form/.src/DataView.class create mode 100644 comp/src/gb.db.form/.src/FBlobEditor.class create mode 100644 comp/src/gb.db.form/.src/FBlobEditor.form create mode 100644 comp/src/gb.db.form/.src/FBrowser.class create mode 100644 comp/src/gb.db.form/.src/FBrowser.form create mode 100644 comp/src/gb.db.form/.src/Test/FMain.class create mode 100644 comp/src/gb.db.form/.src/Test/FMain.form create mode 100644 comp/src/gb.db.form/.src/Test/FMain2.class create mode 100644 comp/src/gb.db.form/.src/Test/FMain2.form create mode 100644 comp/src/gb.db.form/.src/Test/FMain3.class create mode 100644 comp/src/gb.db.form/.src/Test/FMain3.form create mode 100644 comp/src/gb.db.form/.src/Test/FTest.class create mode 100644 comp/src/gb.db.form/.src/Test/FTest.form create mode 100644 comp/src/gb.db.form/.src/Test/Main.module create mode 100644 comp/src/gb.dbus.trayicon/.component create mode 100644 comp/src/gb.dbus.trayicon/.directory create mode 100644 comp/src/gb.dbus.trayicon/.hidden/com.canonical.dbusmenu.xml create mode 100644 comp/src/gb.dbus.trayicon/.hidden/doc.txt create mode 100644 comp/src/gb.dbus.trayicon/.icon.png create mode 100644 comp/src/gb.dbus.trayicon/.project create mode 100644 comp/src/gb.dbus.trayicon/.src/DBusStatusIcon.class create mode 100644 comp/src/gb.dbus.trayicon/.src/DBusStatusIconMenu.class create mode 100644 comp/src/gb.dbus.trayicon/.src/FMain.class create mode 100644 comp/src/gb.dbus.trayicon/.src/FMain.form create mode 100644 comp/src/gb.dbus.trayicon/.src/FTest.class create mode 100644 comp/src/gb.dbus.trayicon/.src/FTest.form create mode 100644 comp/src/gb.dbus.trayicon/.src/Main.module create mode 100644 comp/src/gb.dbus.trayicon/.src/TrayIcon.class create mode 100644 comp/src/gb.dbus.trayicon/.src/TrayIcons.class create mode 100644 comp/src/gb.dbus.trayicon/.src/_DBusMenuItem.class create mode 100644 comp/src/gb.dbus.trayicon/.src/_DBusMenuLayout.class create mode 100644 comp/src/gb.dbus.trayicon/.src/_DBusMenuProperties.class create mode 100644 comp/src/gb.dbus.trayicon/.src/_DBusStatusIconPixmap.class create mode 100644 comp/src/gb.dbus.trayicon/.src/_DBusStatusIconTooltip.class create mode 100644 comp/src/gb.dbus.trayicon/.src/_DBusUInt.class create mode 100644 comp/src/gb.dbus.trayicon/default.png create mode 100644 comp/src/gb.desktop/.component create mode 100644 comp/src/gb.desktop/.directory create mode 100644 comp/src/gb.desktop/.hidden/control/desktopwatcher.png create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.2/LICENSE create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.2/scripts/README create mode 100755 comp/src/gb.desktop/.hidden/xdg-utils-1.0.2/scripts/xdg-desktop-icon create mode 100755 comp/src/gb.desktop/.hidden/xdg-utils-1.0.2/scripts/xdg-desktop-menu create mode 100755 comp/src/gb.desktop/.hidden/xdg-utils-1.0.2/scripts/xdg-email create mode 100755 comp/src/gb.desktop/.hidden/xdg-utils-1.0.2/scripts/xdg-icon-resource create mode 100755 comp/src/gb.desktop/.hidden/xdg-utils-1.0.2/scripts/xdg-mime create mode 100755 comp/src/gb.desktop/.hidden/xdg-utils-1.0.2/scripts/xdg-open create mode 100755 comp/src/gb.desktop/.hidden/xdg-utils-1.0.2/scripts/xdg-screensaver create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre.patch/xdg-email create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-copy create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-desktop-icon create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-desktop-menu create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-email create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-file-dialog create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-icon-resource create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-mime create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-open create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-screensaver create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-settings create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-su create mode 100644 comp/src/gb.desktop/.hidden/xdg-utils-1.0.3.pre/xdg-terminal create mode 100644 comp/src/gb.desktop/.icon.png create mode 100644 comp/src/gb.desktop/.project create mode 100644 comp/src/gb.desktop/.src/Atom.class create mode 100644 comp/src/gb.desktop/.src/Desktop.class create mode 100644 comp/src/gb.desktop/.src/DesktopFile.class create mode 100644 comp/src/gb.desktop/.src/DesktopMime.class create mode 100644 comp/src/gb.desktop/.src/DesktopWatcher.class create mode 100644 comp/src/gb.desktop/.src/DesktopWindow.class create mode 100644 comp/src/gb.desktop/.src/Main.module create mode 100644 comp/src/gb.desktop/.src/Tests/Form1.class create mode 100644 comp/src/gb.desktop/.src/Tests/Form1.form create mode 100644 comp/src/gb.desktop/.src/Tests/Form11.class create mode 100644 comp/src/gb.desktop/.src/Tests/Form11.form create mode 100644 comp/src/gb.desktop/.src/Tests/Form2.class create mode 100644 comp/src/gb.desktop/.src/Tests/Form2.form create mode 100644 comp/src/gb.desktop/.src/Tests/Module1.module create mode 100644 comp/src/gb.desktop/.src/_DesktopIcons.class create mode 100644 comp/src/gb.desktop/.src/_DesktopMenus.class create mode 100644 comp/src/gb.desktop/.src/_DesktopVirtual.class create mode 100644 comp/src/gb.desktop/.src/_Desktop_Passwords.class create mode 100644 comp/src/gb.desktop/.src/_Desktop_ScreenSaver.class create mode 100644 comp/src/gb.desktop/.src/_Desktop_Windows.class create mode 100755 comp/src/gb.desktop/xdg-utils/xdg-desktop-icon create mode 100755 comp/src/gb.desktop/xdg-utils/xdg-desktop-menu create mode 100755 comp/src/gb.desktop/xdg-utils/xdg-email create mode 100755 comp/src/gb.desktop/xdg-utils/xdg-icon-resource create mode 100755 comp/src/gb.desktop/xdg-utils/xdg-mime create mode 100755 comp/src/gb.desktop/xdg-utils/xdg-open create mode 100755 comp/src/gb.desktop/xdg-utils/xdg-screensaver create mode 100755 comp/src/gb.desktop/xdg-utils/xdg-settings create mode 100644 comp/src/gb.eval.highlight/.component create mode 100644 comp/src/gb.eval.highlight/.directory create mode 100644 comp/src/gb.eval.highlight/.icon.png create mode 100644 comp/src/gb.eval.highlight/.project create mode 100644 comp/src/gb.eval.highlight/.src/Init.module create mode 100644 comp/src/gb.eval.highlight/.src/Main.module create mode 100644 comp/src/gb.eval.highlight/.src/OldHighlighter/Helper.module create mode 100644 comp/src/gb.eval.highlight/.src/OldHighlighter/Highlight.class create mode 100644 comp/src/gb.eval.highlight/.src/OldHighlighter/HighlightC.module create mode 100644 comp/src/gb.eval.highlight/.src/OldHighlighter/HighlightCPlusPlus.module create mode 100644 comp/src/gb.eval.highlight/.src/OldHighlighter/HighlightCSS.module create mode 100644 comp/src/gb.eval.highlight/.src/OldHighlighter/HighlightDiff.module create mode 100644 comp/src/gb.eval.highlight/.src/OldHighlighter/HighlightHTML.module create mode 100644 comp/src/gb.eval.highlight/.src/OldHighlighter/HighlightJavascript.module create mode 100644 comp/src/gb.eval.highlight/.src/OldHighlighter/HighlightSQL.module create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighterStyle.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_C.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_CPlusPlus.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_CSS.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_Diff.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_Gambas.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_Html.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_Javascript.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_SQL.class create mode 100644 comp/src/gb.eval.highlight/.src/TextHighlighter_WebPage.class create mode 100644 comp/src/gb.eval.highlight/css/properties create mode 100644 comp/src/gb.eval.highlight/css/values create mode 100644 comp/src/gb.eval.highlight/sql/datatypes create mode 100644 comp/src/gb.eval.highlight/sql/functions create mode 100644 comp/src/gb.eval.highlight/sql/keywords create mode 100644 comp/src/gb.eval.highlight/sql/operators create mode 100644 comp/src/gb.form.dialog/.component create mode 100644 comp/src/gb.form.dialog/.directory create mode 100644 comp/src/gb.form.dialog/.icon.png create mode 100644 comp/src/gb.form.dialog/.lang/ca.mo create mode 100644 comp/src/gb.form.dialog/.lang/ca.po create mode 100644 comp/src/gb.form.dialog/.lang/cs.mo create mode 100644 comp/src/gb.form.dialog/.lang/cs.po create mode 100644 comp/src/gb.form.dialog/.lang/de.mo create mode 100644 comp/src/gb.form.dialog/.lang/de.po create mode 100644 comp/src/gb.form.dialog/.lang/es.mo create mode 100644 comp/src/gb.form.dialog/.lang/es.po create mode 100644 comp/src/gb.form.dialog/.lang/es_ES.mo create mode 100644 comp/src/gb.form.dialog/.lang/es_ES.po create mode 100644 comp/src/gb.form.dialog/.lang/fr.mo create mode 100644 comp/src/gb.form.dialog/.lang/fr.po create mode 100644 comp/src/gb.form.dialog/.lang/it.mo create mode 100644 comp/src/gb.form.dialog/.lang/it.po create mode 100644 comp/src/gb.form.dialog/.lang/ja.mo create mode 100644 comp/src/gb.form.dialog/.lang/ja.po create mode 100644 comp/src/gb.form.dialog/.lang/nl.mo create mode 100644 comp/src/gb.form.dialog/.lang/nl.po create mode 100644 comp/src/gb.form.dialog/.lang/pt_BR.mo create mode 100644 comp/src/gb.form.dialog/.lang/pt_BR.po create mode 100644 comp/src/gb.form.dialog/.lang/sv.mo create mode 100644 comp/src/gb.form.dialog/.lang/sv.po create mode 100644 comp/src/gb.form.dialog/.lang/zh.mo create mode 100644 comp/src/gb.form.dialog/.lang/zh.po create mode 100644 comp/src/gb.form.dialog/.project create mode 100644 comp/src/gb.form.dialog/.src/Dialog.class create mode 100644 comp/src/gb.form.dialog/.src/FAskPassword.class create mode 100644 comp/src/gb.form.dialog/.src/FAskPassword.form create mode 100644 comp/src/gb.form.dialog/.src/FDirDialog.class create mode 100644 comp/src/gb.form.dialog/.src/FDirDialog.form create mode 100644 comp/src/gb.form.dialog/.src/FFileDialog.class create mode 100644 comp/src/gb.form.dialog/.src/FFileDialog.form create mode 100644 comp/src/gb.form.dialog/.src/FFontDialog.class create mode 100644 comp/src/gb.form.dialog/.src/FFontDialog.form create mode 100644 comp/src/gb.form.dialog/.src/FInputDate.class create mode 100644 comp/src/gb.form.dialog/.src/FInputDate.form create mode 100644 comp/src/gb.form.dialog/.src/Main.module create mode 100644 comp/src/gb.form.editor/.component create mode 100644 comp/src/gb.form.editor/.directory create mode 100644 comp/src/gb.form.editor/.hidden/control/texteditor.png create mode 100644 comp/src/gb.form.editor/.icon.png create mode 100644 comp/src/gb.form.editor/.project create mode 100644 comp/src/gb.form.editor/.src/CCommand.class create mode 100644 comp/src/gb.form.editor/.src/CCommandBefore.class create mode 100644 comp/src/gb.form.editor/.src/CDocument.class create mode 100644 comp/src/gb.form.editor/.src/CLineInfo.class create mode 100644 comp/src/gb.form.editor/.src/FTest.class create mode 100644 comp/src/gb.form.editor/.src/FTest.form create mode 100644 comp/src/gb.form.editor/.src/FTestEditor.class create mode 100644 comp/src/gb.form.editor/.src/FTestEditor.form create mode 100644 comp/src/gb.form.editor/.src/Helper.module create mode 100644 comp/src/gb.form.editor/.src/Main.module create mode 100644 comp/src/gb.form.editor/.src/TextEditor.class create mode 100644 comp/src/gb.form.editor/.src/TextEditorMode.class create mode 100644 comp/src/gb.form.editor/.src/TextEditorMode_CSS.class create mode 100644 comp/src/gb.form.editor/.src/TextEditorMode_Gambas.class create mode 100644 comp/src/gb.form.editor/.src/TextEditorMode_HTML.class create mode 100644 comp/src/gb.form.editor/.src/TextEditorMode_Javascript.class create mode 100644 comp/src/gb.form.editor/.src/TextEditorMode_SQL.class create mode 100644 comp/src/gb.form.editor/.src/TextEditorMode_WebPage.class create mode 100644 comp/src/gb.form.editor/.src/TextEditorStyle.class create mode 100644 comp/src/gb.form.editor/.src/_TextEditor_Line.class create mode 100644 comp/src/gb.form.editor/.src/_TextEditor_Rows.class create mode 100644 comp/src/gb.form.editor/.src/_TextEditor_State.class create mode 100644 comp/src/gb.form.editor/.src/_TextEditor_Styles.class create mode 100644 comp/src/gb.form.editor/Text1 create mode 100644 comp/src/gb.form.editor/test.html create mode 100644 comp/src/gb.form.mdi/.component create mode 100644 comp/src/gb.form.mdi/.directory create mode 100644 comp/src/gb.form.mdi/.hidden/control/toolbar.png create mode 100644 comp/src/gb.form.mdi/.hidden/control/workspace.png create mode 100644 comp/src/gb.form.mdi/.icon.png create mode 100644 comp/src/gb.form.mdi/.lang/ca.mo create mode 100644 comp/src/gb.form.mdi/.lang/ca.po create mode 100644 comp/src/gb.form.mdi/.lang/cs.mo create mode 100644 comp/src/gb.form.mdi/.lang/cs.po create mode 100644 comp/src/gb.form.mdi/.lang/de.mo create mode 100644 comp/src/gb.form.mdi/.lang/de.po create mode 100644 comp/src/gb.form.mdi/.lang/es.mo create mode 100644 comp/src/gb.form.mdi/.lang/es.po create mode 100644 comp/src/gb.form.mdi/.lang/es_ES.mo create mode 100644 comp/src/gb.form.mdi/.lang/es_ES.po create mode 100644 comp/src/gb.form.mdi/.lang/fr.mo create mode 100644 comp/src/gb.form.mdi/.lang/fr.po create mode 100644 comp/src/gb.form.mdi/.lang/it.mo create mode 100644 comp/src/gb.form.mdi/.lang/it.po create mode 100644 comp/src/gb.form.mdi/.lang/ja.mo create mode 100644 comp/src/gb.form.mdi/.lang/ja.po create mode 100644 comp/src/gb.form.mdi/.lang/nl.mo create mode 100644 comp/src/gb.form.mdi/.lang/nl.po create mode 100644 comp/src/gb.form.mdi/.lang/pt_BR.mo create mode 100644 comp/src/gb.form.mdi/.lang/pt_BR.po create mode 100644 comp/src/gb.form.mdi/.lang/sv.mo create mode 100644 comp/src/gb.form.mdi/.lang/sv.po create mode 100644 comp/src/gb.form.mdi/.lang/zh.mo create mode 100644 comp/src/gb.form.mdi/.lang/zh.po create mode 100644 comp/src/gb.form.mdi/.project create mode 100644 comp/src/gb.form.mdi/.src/Action/Action.class create mode 100644 comp/src/gb.form.mdi/.src/Action/CAction.class create mode 100644 comp/src/gb.form.mdi/.src/Action/MAction.module create mode 100644 comp/src/gb.form.mdi/.src/MMain.module create mode 100644 comp/src/gb.form.mdi/.src/Shortcut/FShortcut.class create mode 100644 comp/src/gb.form.mdi/.src/Shortcut/FShortcut.form create mode 100644 comp/src/gb.form.mdi/.src/Shortcut/FShortcutEditor.class create mode 100644 comp/src/gb.form.mdi/.src/Shortcut/FShortcutEditor.form create mode 100644 comp/src/gb.form.mdi/.src/Tests/FMain.class create mode 100644 comp/src/gb.form.mdi/.src/Tests/FMain.form create mode 100644 comp/src/gb.form.mdi/.src/Tests/FMain1.class create mode 100644 comp/src/gb.form.mdi/.src/Tests/FMain1.form create mode 100644 comp/src/gb.form.mdi/.src/Tests/FMain2.class create mode 100644 comp/src/gb.form.mdi/.src/Tests/FMain2.form create mode 100644 comp/src/gb.form.mdi/.src/Tests/FTestSidePanel.class create mode 100644 comp/src/gb.form.mdi/.src/Tests/FTestSidePanel.form create mode 100644 comp/src/gb.form.mdi/.src/Tests/Form1.class create mode 100644 comp/src/gb.form.mdi/.src/Tests/Form1.form create mode 100644 comp/src/gb.form.mdi/.src/Tests/Form2.class create mode 100644 comp/src/gb.form.mdi/.src/Tests/Form2.form create mode 100644 comp/src/gb.form.mdi/.src/ToolBar/CToolbar.class create mode 100644 comp/src/gb.form.mdi/.src/ToolBar/FToolBar.class create mode 100644 comp/src/gb.form.mdi/.src/ToolBar/FToolBar.form create mode 100644 comp/src/gb.form.mdi/.src/ToolBar/FToolBarConfig.class create mode 100644 comp/src/gb.form.mdi/.src/ToolBar/FToolBarConfig.form create mode 100644 comp/src/gb.form.mdi/.src/ToolBar/ToolBar.class create mode 100644 comp/src/gb.form.mdi/.src/ToolBar/ToolBarExpander.class create mode 100644 comp/src/gb.form.mdi/.src/Workspace/CWindow.class create mode 100644 comp/src/gb.form.mdi/.src/Workspace/FWorkspace.class create mode 100644 comp/src/gb.form.mdi/.src/Workspace/FWorkspace.form create mode 100644 comp/src/gb.form.mdi/.src/Workspace/Workspace.class create mode 100644 comp/src/gb.form.mdi/70a017.png create mode 100644 comp/src/gb.form.mdi/control/buttonbox.png create mode 100644 comp/src/gb.form.mdi/control/combobox.png create mode 100644 comp/src/gb.form.mdi/control/datebox.png create mode 100644 comp/src/gb.form.mdi/control/valuebox.png create mode 100644 comp/src/gb.form.mdi/img/close.png create mode 100644 comp/src/gb.form.mdi/img/configure.png create mode 100644 comp/src/gb.form.mdi/img/configure_dark.png create mode 100644 comp/src/gb.form.mdi/img/expander.png create mode 100644 comp/src/gb.form.mdi/img/handle-v.png create mode 100644 comp/src/gb.form.mdi/img/handle.png create mode 100644 comp/src/gb.form.mdi/img/hash.png create mode 100644 comp/src/gb.form.mdi/img/roll.png create mode 100644 comp/src/gb.form.mdi/img/separator.png create mode 100644 comp/src/gb.form.mdi/img/space.png create mode 100644 comp/src/gb.form.mdi/img/unroll.png create mode 100644 comp/src/gb.form.stock/.component create mode 100644 comp/src/gb.form.stock/.directory create mode 100644 comp/src/gb.form.stock/.hidden/map create mode 100644 comp/src/gb.form.stock/.icon.png create mode 100644 comp/src/gb.form.stock/.project create mode 100644 comp/src/gb.form.stock/.src/Main.module create mode 100644 comp/src/gb.form.stock/.src/_DefaultStock.class create mode 100644 comp/src/gb.form.stock/gambas-mono/128/error.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/gambas.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/info.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/question.png create mode 100644 comp/src/gb.form.stock/gambas-mono/128/warning.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/access.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/add.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/align-bottom.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/align-center.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/align-height.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/align-left.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/align-middle.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/align-right.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/align-top.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/align-width.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/apply.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/archive.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/attach.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/audio.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/battery.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/book.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/bookmark.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/bottom.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/c.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/calculator.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/calendar.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/camera.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/cancel.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/cdrom.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/clear-rtl.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/clear.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/clock.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/close.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/color-picker.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/color.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/component.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/computer.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/connect.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/copy.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/cpp.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/css.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/cut.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/database.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/delete.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/desktop.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/development.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/directory.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/disconnect.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/down.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/download.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/earth.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/edit.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/eject.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/end.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/erase.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/error.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/exec.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/file-manager.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/file.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/fill.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/filter.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/find.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/first.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/flag.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/flip-h.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/flip-v.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/floppy.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/font.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/forward.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/fullscreen.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/gambas.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/game.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/gnu.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/grid.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/group.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/h.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/halt.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/harddisk.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/hardware.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/help.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/home.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/html.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/identity.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/image.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/important.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/indent.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/info.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/insert-image.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/insert-link.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/insert-text.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/internet.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/js.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/jump.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/keyboard.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/lamp.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/language.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/last.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/left.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/link.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/linux.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/lock.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/lower.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/mail.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/make-all.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/make.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/media-player.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/menu.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/microphone.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/monitor.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/mouse.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/multimedia.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/muted.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/network.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/new-dir.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/new-window.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/new.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/next.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/office-calc.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/office-draw.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/office.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/ok.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/open-recent.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/open.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/options.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/package.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/paste.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/pause.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/pda.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/pdf.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/pen.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/people.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/phone.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/play.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/plugin.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/preview.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/previous.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/print.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/printer.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/program.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/properties.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/question.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/quit.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/raise.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/recent.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/record.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/redo.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/refresh.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/remove.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/replace.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/revert.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/rewind.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/right.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/rotate-left.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/rotate-right.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/save-as.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/save.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/scanner.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/science.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/screen.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/script.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/security.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/select-all.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/select.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/server.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/shortcut.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/sort-ascent.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/sort-descent.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/spell-check.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/star.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/start.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/stop.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/sun.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/system.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/table.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/tablet.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/terminal.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-baseline.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-bold.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-bottom.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-center.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-fill.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-italic.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-left.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-middle.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-right.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-strike.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-top.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text-underline.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/text.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/tools.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/top.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/trash.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/undo.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/unindent.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/unlock.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/up.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/upload.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/user.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/vector.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/video.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/view-detail.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/view-icon.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/view-normal.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/view-split-h.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/view-split-v.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/volume.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/vpn.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/warning.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/watch.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/webcam.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/wifi.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/wizard.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/zoom-fit.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/zoom-in.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/zoom-normal.png create mode 100644 comp/src/gb.form.stock/gambas-mono/32/zoom-out.png create mode 100644 comp/src/gb.form.stock/gambas/128/error.png create mode 100644 comp/src/gb.form.stock/gambas/128/gambas.png create mode 100644 comp/src/gb.form.stock/gambas/128/info.png create mode 100644 comp/src/gb.form.stock/gambas/128/question.png create mode 100644 comp/src/gb.form.stock/gambas/128/warning.png create mode 100644 comp/src/gb.form.stock/gambas/32/access.png create mode 100644 comp/src/gb.form.stock/gambas/32/add.png create mode 100644 comp/src/gb.form.stock/gambas/32/align-bottom.png create mode 100644 comp/src/gb.form.stock/gambas/32/align-center.png create mode 100644 comp/src/gb.form.stock/gambas/32/align-height.png create mode 100644 comp/src/gb.form.stock/gambas/32/align-left.png create mode 100644 comp/src/gb.form.stock/gambas/32/align-middle.png create mode 100644 comp/src/gb.form.stock/gambas/32/align-right.png create mode 100644 comp/src/gb.form.stock/gambas/32/align-top.png create mode 100644 comp/src/gb.form.stock/gambas/32/align-width.png create mode 100644 comp/src/gb.form.stock/gambas/32/apply.png create mode 100644 comp/src/gb.form.stock/gambas/32/archive.png create mode 100644 comp/src/gb.form.stock/gambas/32/attach.png create mode 100644 comp/src/gb.form.stock/gambas/32/audio.png create mode 100644 comp/src/gb.form.stock/gambas/32/battery.png create mode 100644 comp/src/gb.form.stock/gambas/32/book.png create mode 100644 comp/src/gb.form.stock/gambas/32/bookmark.png create mode 100644 comp/src/gb.form.stock/gambas/32/bottom.png create mode 100644 comp/src/gb.form.stock/gambas/32/c.png create mode 100644 comp/src/gb.form.stock/gambas/32/calculator.png create mode 100644 comp/src/gb.form.stock/gambas/32/calendar.png create mode 100644 comp/src/gb.form.stock/gambas/32/camera.png create mode 100644 comp/src/gb.form.stock/gambas/32/cancel.png create mode 100644 comp/src/gb.form.stock/gambas/32/cdrom.png create mode 100644 comp/src/gb.form.stock/gambas/32/clear-rtl.png create mode 100644 comp/src/gb.form.stock/gambas/32/clear.png create mode 100644 comp/src/gb.form.stock/gambas/32/clock.png create mode 100644 comp/src/gb.form.stock/gambas/32/close.png create mode 100644 comp/src/gb.form.stock/gambas/32/color-picker.png create mode 100644 comp/src/gb.form.stock/gambas/32/color.png create mode 100644 comp/src/gb.form.stock/gambas/32/component.png create mode 100644 comp/src/gb.form.stock/gambas/32/computer.png create mode 100644 comp/src/gb.form.stock/gambas/32/connect.png create mode 100644 comp/src/gb.form.stock/gambas/32/copy.png create mode 100644 comp/src/gb.form.stock/gambas/32/cpp.png create mode 100644 comp/src/gb.form.stock/gambas/32/css.png create mode 100644 comp/src/gb.form.stock/gambas/32/cut.png create mode 100644 comp/src/gb.form.stock/gambas/32/database.png create mode 100644 comp/src/gb.form.stock/gambas/32/delete.png create mode 100644 comp/src/gb.form.stock/gambas/32/desktop.png create mode 100644 comp/src/gb.form.stock/gambas/32/development.png create mode 100644 comp/src/gb.form.stock/gambas/32/directory.png create mode 100644 comp/src/gb.form.stock/gambas/32/disconnect.png create mode 100644 comp/src/gb.form.stock/gambas/32/down.png create mode 100644 comp/src/gb.form.stock/gambas/32/download.png create mode 100644 comp/src/gb.form.stock/gambas/32/earth.png create mode 100644 comp/src/gb.form.stock/gambas/32/edit.png create mode 100644 comp/src/gb.form.stock/gambas/32/eject.png create mode 100644 comp/src/gb.form.stock/gambas/32/end.png create mode 100644 comp/src/gb.form.stock/gambas/32/erase.png create mode 100644 comp/src/gb.form.stock/gambas/32/error.png create mode 100644 comp/src/gb.form.stock/gambas/32/exec.png create mode 100644 comp/src/gb.form.stock/gambas/32/file-manager.png create mode 100644 comp/src/gb.form.stock/gambas/32/file.png create mode 100644 comp/src/gb.form.stock/gambas/32/fill.png create mode 100644 comp/src/gb.form.stock/gambas/32/filter.png create mode 100644 comp/src/gb.form.stock/gambas/32/find.png create mode 100644 comp/src/gb.form.stock/gambas/32/first.png create mode 100644 comp/src/gb.form.stock/gambas/32/flag.png create mode 100644 comp/src/gb.form.stock/gambas/32/flip-h.png create mode 100644 comp/src/gb.form.stock/gambas/32/flip-v.png create mode 100644 comp/src/gb.form.stock/gambas/32/floppy.png create mode 100644 comp/src/gb.form.stock/gambas/32/font.png create mode 100644 comp/src/gb.form.stock/gambas/32/forward.png create mode 100644 comp/src/gb.form.stock/gambas/32/fullscreen.png create mode 100644 comp/src/gb.form.stock/gambas/32/gambas.png create mode 100644 comp/src/gb.form.stock/gambas/32/game.png create mode 100644 comp/src/gb.form.stock/gambas/32/gnu.png create mode 100644 comp/src/gb.form.stock/gambas/32/grid.png create mode 100644 comp/src/gb.form.stock/gambas/32/group.png create mode 100644 comp/src/gb.form.stock/gambas/32/h.png create mode 100644 comp/src/gb.form.stock/gambas/32/halt.png create mode 100644 comp/src/gb.form.stock/gambas/32/harddisk.png create mode 100644 comp/src/gb.form.stock/gambas/32/hardware.png create mode 100644 comp/src/gb.form.stock/gambas/32/help.png create mode 100644 comp/src/gb.form.stock/gambas/32/home.png create mode 100644 comp/src/gb.form.stock/gambas/32/html.png create mode 100644 comp/src/gb.form.stock/gambas/32/identity.png create mode 100644 comp/src/gb.form.stock/gambas/32/image.png create mode 100644 comp/src/gb.form.stock/gambas/32/important.png create mode 100644 comp/src/gb.form.stock/gambas/32/indent.png create mode 100644 comp/src/gb.form.stock/gambas/32/info.png create mode 100644 comp/src/gb.form.stock/gambas/32/insert-image.png create mode 100644 comp/src/gb.form.stock/gambas/32/insert-link.png create mode 100644 comp/src/gb.form.stock/gambas/32/insert-text.png create mode 100644 comp/src/gb.form.stock/gambas/32/internet.png create mode 100644 comp/src/gb.form.stock/gambas/32/js.png create mode 100644 comp/src/gb.form.stock/gambas/32/jump.png create mode 100644 comp/src/gb.form.stock/gambas/32/keyboard.png create mode 100644 comp/src/gb.form.stock/gambas/32/lamp.png create mode 100644 comp/src/gb.form.stock/gambas/32/language.png create mode 100644 comp/src/gb.form.stock/gambas/32/last.png create mode 100644 comp/src/gb.form.stock/gambas/32/left.png create mode 100644 comp/src/gb.form.stock/gambas/32/link.png create mode 100644 comp/src/gb.form.stock/gambas/32/linux.png create mode 100644 comp/src/gb.form.stock/gambas/32/lock.png create mode 100644 comp/src/gb.form.stock/gambas/32/lower.png create mode 100644 comp/src/gb.form.stock/gambas/32/mail.png create mode 100644 comp/src/gb.form.stock/gambas/32/make-all.png create mode 100644 comp/src/gb.form.stock/gambas/32/make.png create mode 100644 comp/src/gb.form.stock/gambas/32/media-player.png create mode 100644 comp/src/gb.form.stock/gambas/32/menu.png create mode 100644 comp/src/gb.form.stock/gambas/32/microphone.png create mode 100644 comp/src/gb.form.stock/gambas/32/monitor.png create mode 100644 comp/src/gb.form.stock/gambas/32/mouse.png create mode 100644 comp/src/gb.form.stock/gambas/32/multimedia.png create mode 100644 comp/src/gb.form.stock/gambas/32/muted.png create mode 100644 comp/src/gb.form.stock/gambas/32/network.png create mode 100644 comp/src/gb.form.stock/gambas/32/new-dir.png create mode 100644 comp/src/gb.form.stock/gambas/32/new-window.png create mode 100644 comp/src/gb.form.stock/gambas/32/new.png create mode 100644 comp/src/gb.form.stock/gambas/32/next.png create mode 100644 comp/src/gb.form.stock/gambas/32/office-calc.png create mode 100644 comp/src/gb.form.stock/gambas/32/office-draw.png create mode 100644 comp/src/gb.form.stock/gambas/32/office.png create mode 100644 comp/src/gb.form.stock/gambas/32/ok.png create mode 100644 comp/src/gb.form.stock/gambas/32/open-recent.png create mode 100644 comp/src/gb.form.stock/gambas/32/open.png create mode 100644 comp/src/gb.form.stock/gambas/32/options.png create mode 100644 comp/src/gb.form.stock/gambas/32/package.png create mode 100644 comp/src/gb.form.stock/gambas/32/paste.png create mode 100644 comp/src/gb.form.stock/gambas/32/pause.png create mode 100644 comp/src/gb.form.stock/gambas/32/pda.png create mode 100644 comp/src/gb.form.stock/gambas/32/pdf.png create mode 100644 comp/src/gb.form.stock/gambas/32/pen.png create mode 100644 comp/src/gb.form.stock/gambas/32/people.png create mode 100644 comp/src/gb.form.stock/gambas/32/phone.png create mode 100644 comp/src/gb.form.stock/gambas/32/play.png create mode 100644 comp/src/gb.form.stock/gambas/32/plugin.png create mode 100644 comp/src/gb.form.stock/gambas/32/preview.png create mode 100644 comp/src/gb.form.stock/gambas/32/previous.png create mode 100644 comp/src/gb.form.stock/gambas/32/print.png create mode 100644 comp/src/gb.form.stock/gambas/32/printer.png create mode 100644 comp/src/gb.form.stock/gambas/32/program.png create mode 100644 comp/src/gb.form.stock/gambas/32/properties.png create mode 100644 comp/src/gb.form.stock/gambas/32/question.png create mode 100644 comp/src/gb.form.stock/gambas/32/quit.png create mode 100644 comp/src/gb.form.stock/gambas/32/raise.png create mode 100644 comp/src/gb.form.stock/gambas/32/recent.png create mode 100644 comp/src/gb.form.stock/gambas/32/record.png create mode 100644 comp/src/gb.form.stock/gambas/32/redo.png create mode 100644 comp/src/gb.form.stock/gambas/32/refresh.png create mode 100644 comp/src/gb.form.stock/gambas/32/remove.png create mode 100644 comp/src/gb.form.stock/gambas/32/replace.png create mode 100644 comp/src/gb.form.stock/gambas/32/revert.png create mode 100644 comp/src/gb.form.stock/gambas/32/rewind.png create mode 100644 comp/src/gb.form.stock/gambas/32/right.png create mode 100644 comp/src/gb.form.stock/gambas/32/rotate-left.png create mode 100644 comp/src/gb.form.stock/gambas/32/rotate-right.png create mode 100644 comp/src/gb.form.stock/gambas/32/save-as.png create mode 100644 comp/src/gb.form.stock/gambas/32/save.png create mode 100644 comp/src/gb.form.stock/gambas/32/scanner.png create mode 100644 comp/src/gb.form.stock/gambas/32/science.png create mode 100644 comp/src/gb.form.stock/gambas/32/screen.png create mode 100644 comp/src/gb.form.stock/gambas/32/script.png create mode 100644 comp/src/gb.form.stock/gambas/32/security.png create mode 100644 comp/src/gb.form.stock/gambas/32/select-all.png create mode 100644 comp/src/gb.form.stock/gambas/32/select.png create mode 100644 comp/src/gb.form.stock/gambas/32/server.png create mode 100644 comp/src/gb.form.stock/gambas/32/shortcut.png create mode 100644 comp/src/gb.form.stock/gambas/32/sort-ascent.png create mode 100644 comp/src/gb.form.stock/gambas/32/sort-descent.png create mode 100644 comp/src/gb.form.stock/gambas/32/spell-check.png create mode 100644 comp/src/gb.form.stock/gambas/32/star.png create mode 100644 comp/src/gb.form.stock/gambas/32/start.png create mode 100644 comp/src/gb.form.stock/gambas/32/stop.png create mode 100644 comp/src/gb.form.stock/gambas/32/sun.png create mode 100644 comp/src/gb.form.stock/gambas/32/system.png create mode 100644 comp/src/gb.form.stock/gambas/32/table.png create mode 100644 comp/src/gb.form.stock/gambas/32/tablet.png create mode 100644 comp/src/gb.form.stock/gambas/32/terminal.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-baseline.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-bold.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-bottom.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-center.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-fill.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-italic.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-left.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-middle.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-right.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-strike.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-top.png create mode 100644 comp/src/gb.form.stock/gambas/32/text-underline.png create mode 100644 comp/src/gb.form.stock/gambas/32/text.png create mode 100644 comp/src/gb.form.stock/gambas/32/tools.png create mode 100644 comp/src/gb.form.stock/gambas/32/top.png create mode 100644 comp/src/gb.form.stock/gambas/32/trash.png create mode 100644 comp/src/gb.form.stock/gambas/32/undo.png create mode 100644 comp/src/gb.form.stock/gambas/32/unindent.png create mode 100644 comp/src/gb.form.stock/gambas/32/unlock.png create mode 100644 comp/src/gb.form.stock/gambas/32/up.png create mode 100644 comp/src/gb.form.stock/gambas/32/upload.png create mode 100644 comp/src/gb.form.stock/gambas/32/user.png create mode 100644 comp/src/gb.form.stock/gambas/32/vector.png create mode 100644 comp/src/gb.form.stock/gambas/32/video.png create mode 100644 comp/src/gb.form.stock/gambas/32/view-detail.png create mode 100644 comp/src/gb.form.stock/gambas/32/view-icon.png create mode 100644 comp/src/gb.form.stock/gambas/32/view-normal.png create mode 100644 comp/src/gb.form.stock/gambas/32/view-split-h.png create mode 100644 comp/src/gb.form.stock/gambas/32/view-split-v.png create mode 100644 comp/src/gb.form.stock/gambas/32/volume.png create mode 100644 comp/src/gb.form.stock/gambas/32/vpn.png create mode 100644 comp/src/gb.form.stock/gambas/32/warning.png create mode 100644 comp/src/gb.form.stock/gambas/32/watch.png create mode 100644 comp/src/gb.form.stock/gambas/32/webcam.png create mode 100644 comp/src/gb.form.stock/gambas/32/wifi.png create mode 100644 comp/src/gb.form.stock/gambas/32/wizard.png create mode 100644 comp/src/gb.form.stock/gambas/32/zoom-fit.png create mode 100644 comp/src/gb.form.stock/gambas/32/zoom-in.png create mode 100644 comp/src/gb.form.stock/gambas/32/zoom-normal.png create mode 100644 comp/src/gb.form.stock/gambas/32/zoom-out.png create mode 100644 comp/src/gb.form.stock/links create mode 100644 comp/src/gb.form.terminal/.component create mode 100644 comp/src/gb.form.terminal/.directory create mode 100644 comp/src/gb.form.terminal/.hidden/Konsole keys README.txt create mode 100644 comp/src/gb.form.terminal/.hidden/Konsole keys.txt create mode 100644 comp/src/gb.form.terminal/.hidden/XtermVT100 create mode 100644 comp/src/gb.form.terminal/.hidden/control/terminalview.png create mode 100644 comp/src/gb.form.terminal/.icon.png create mode 100644 comp/src/gb.form.terminal/.project create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/CTerminalLine.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/FOtherTest.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/FOtherTest.form create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/FTestTerminalView.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/FTestTerminalView.form create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/Form1.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/Form1.form create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/MTest.module create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/PipeTest.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/PipeTest.form create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/PipedTask.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/TelNetProtocol.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/TerminalAttr.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/TerminalFilter.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/TerminalFilter_VT100.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/TerminalScreen.class create mode 100644 comp/src/gb.form.terminal/.src/TerminalView/TerminalView.class create mode 100644 comp/src/gb.form.terminal/.src/VT100/Attr.class create mode 100644 comp/src/gb.form.terminal/.src/VT100/Console.class create mode 100644 comp/src/gb.form.terminal/.src/VT100/Main.module create mode 100644 comp/src/gb.form.terminal/Text1 create mode 100644 comp/src/gb.form.terminal/XtermTests/16colors.sh create mode 100644 comp/src/gb.form.terminal/XtermTests/256colors.pl create mode 100644 comp/src/gb.form.terminal/XtermTests/256colors2.pl create mode 100644 comp/src/gb.form.terminal/XtermTests/88colors.pl create mode 100644 comp/src/gb.form.terminal/XtermTests/88colors2.pl create mode 100644 comp/src/gb.form.terminal/XtermTests/8colors.sh create mode 100644 comp/src/gb.form.terminal/XtermTests/acolors.sh create mode 100644 comp/src/gb.form.terminal/XtermTests/doublechars.sh create mode 100644 comp/src/gb.form.terminal/XtermTests/dynamic.pl create mode 100644 comp/src/gb.form.terminal/XtermTests/dynamic.sh create mode 100644 comp/src/gb.form.terminal/XtermTests/dynamic2.sh create mode 100644 comp/src/gb.form.terminal/XtermTests/fonts.sh create mode 100644 comp/src/gb.form.terminal/XtermTests/paste64.pl create mode 100644 comp/src/gb.form.terminal/XtermTests/query-color.pl create mode 100644 comp/src/gb.form.terminal/XtermTests/query-fonts.pl create mode 100644 comp/src/gb.form.terminal/XtermTests/resize.pl create mode 100644 comp/src/gb.form.terminal/XtermTests/resize.sh create mode 100644 comp/src/gb.form.terminal/XtermTests/tcapquery.pl create mode 100644 comp/src/gb.form.terminal/XtermTests/title.sh create mode 100644 comp/src/gb.form.terminal/brush_dark.png create mode 100644 comp/src/gb.form.terminal/brush_light.png create mode 100644 comp/src/gb.form.terminal/brush_medium.png create mode 100644 comp/src/gb.form/.component create mode 100644 comp/src/gb.form/.directory create mode 100644 comp/src/gb.form/.hidden/CHANGELOG create mode 100644 comp/src/gb.form/.hidden/control/buttonbox.png create mode 100644 comp/src/gb.form/.hidden/control/colorbutton.png create mode 100644 comp/src/gb.form/.hidden/control/colorchooser.png create mode 100644 comp/src/gb.form/.hidden/control/colorpalette.png create mode 100644 comp/src/gb.form/.hidden/control/datebox.png create mode 100644 comp/src/gb.form/.hidden/control/datechooser.png create mode 100644 comp/src/gb.form/.hidden/control/dirbox.png create mode 100644 comp/src/gb.form/.hidden/control/dirchooser.png create mode 100644 comp/src/gb.form/.hidden/control/dirview.png create mode 100644 comp/src/gb.form/.hidden/control/documentview.png create mode 100644 comp/src/gb.form/.hidden/control/expander.png create mode 100644 comp/src/gb.form/.hidden/control/filechooser.png create mode 100644 comp/src/gb.form/.hidden/control/fileproperties.png create mode 100644 comp/src/gb.form/.hidden/control/fileview.png create mode 100644 comp/src/gb.form/.hidden/control/fontbox.png create mode 100644 comp/src/gb.form/.hidden/control/fontchooser.png create mode 100644 comp/src/gb.form/.hidden/control/iconpanel.png create mode 100644 comp/src/gb.form/.hidden/control/imageview.png create mode 100644 comp/src/gb.form/.hidden/control/lcdlabel.png create mode 100644 comp/src/gb.form/.hidden/control/listcontainer.png create mode 100644 comp/src/gb.form/.hidden/control/listeditor.png create mode 100644 comp/src/gb.form/.hidden/control/maskbox.png create mode 100644 comp/src/gb.form/.hidden/control/menubutton.png create mode 100644 comp/src/gb.form/.hidden/control/messageview.png create mode 100644 comp/src/gb.form/.hidden/control/sidepanel.png create mode 100644 comp/src/gb.form/.hidden/control/sliderbox.png create mode 100644 comp/src/gb.form/.hidden/control/spinbar.png create mode 100644 comp/src/gb.form/.hidden/control/spinner.png create mode 100644 comp/src/gb.form/.hidden/control/switchbutton.png create mode 100644 comp/src/gb.form/.hidden/control/tableview.png create mode 100644 comp/src/gb.form/.hidden/control/tabpanel.png create mode 100644 comp/src/gb.form/.hidden/control/toolpanel.png create mode 100644 comp/src/gb.form/.hidden/control/urllabel.png create mode 100644 comp/src/gb.form/.hidden/control/valuebox.png create mode 100644 comp/src/gb.form/.hidden/control/wizard.png create mode 100644 comp/src/gb.form/.icon.png create mode 100644 comp/src/gb.form/.lang/ar.mo create mode 100644 comp/src/gb.form/.lang/ar.po create mode 100644 comp/src/gb.form/.lang/ca.mo create mode 100644 comp/src/gb.form/.lang/ca.po create mode 100644 comp/src/gb.form/.lang/cs.mo create mode 100644 comp/src/gb.form/.lang/cs.po create mode 100644 comp/src/gb.form/.lang/de.mo create mode 100644 comp/src/gb.form/.lang/de.po create mode 100644 comp/src/gb.form/.lang/es.mo create mode 100644 comp/src/gb.form/.lang/es.po create mode 100644 comp/src/gb.form/.lang/es_ES.mo create mode 100644 comp/src/gb.form/.lang/es_ES.po create mode 100644 comp/src/gb.form/.lang/fa.mo create mode 100644 comp/src/gb.form/.lang/fa.po create mode 100644 comp/src/gb.form/.lang/fr.mo create mode 100644 comp/src/gb.form/.lang/fr.po create mode 100644 comp/src/gb.form/.lang/it.mo create mode 100644 comp/src/gb.form/.lang/it.po create mode 100644 comp/src/gb.form/.lang/ja.mo create mode 100644 comp/src/gb.form/.lang/ja.po create mode 100644 comp/src/gb.form/.lang/nl.mo create mode 100644 comp/src/gb.form/.lang/nl.po create mode 100644 comp/src/gb.form/.lang/pt_BR.mo create mode 100644 comp/src/gb.form/.lang/pt_BR.po create mode 100644 comp/src/gb.form/.lang/sv.mo create mode 100644 comp/src/gb.form/.lang/sv.po create mode 100644 comp/src/gb.form/.lang/zh.mo create mode 100644 comp/src/gb.form/.lang/zh.po create mode 100644 comp/src/gb.form/.lang/zh_TW.mo create mode 100644 comp/src/gb.form/.lang/zh_TW.po create mode 100644 comp/src/gb.form/.project create mode 100644 comp/src/gb.form/.src/Balloon/Balloon.class create mode 100644 comp/src/gb.form/.src/Balloon/FBalloon.class create mode 100644 comp/src/gb.form/.src/Balloon/FBalloon.form create mode 100644 comp/src/gb.form/.src/Button/ButtonBox.class create mode 100644 comp/src/gb.form/.src/Button/MenuButton.class create mode 100644 comp/src/gb.form/.src/Button/SwitchButton.class create mode 100644 comp/src/gb.form/.src/Color/ColorButton.class create mode 100644 comp/src/gb.form/.src/Color/ColorChooser.class create mode 100644 comp/src/gb.form/.src/Color/ColorPalette.class create mode 100644 comp/src/gb.form/.src/Color/FColorChooser.class create mode 100644 comp/src/gb.form/.src/Color/FColorChooser.form create mode 100644 comp/src/gb.form/.src/Completion.class create mode 100644 comp/src/gb.form/.src/Date/DateBox.class create mode 100644 comp/src/gb.form/.src/Date/DateChooser.class create mode 100644 comp/src/gb.form/.src/Date/FCalendar.class create mode 100644 comp/src/gb.form/.src/Date/FCalendar.form create mode 100644 comp/src/gb.form/.src/Date/_DateChooser_Colors.class create mode 100644 comp/src/gb.form/.src/Date/_DateChooser_Data.class create mode 100644 comp/src/gb.form/.src/Date/_DateChooser_Date.class create mode 100644 comp/src/gb.form/.src/DocumentView/DocumentView.class create mode 100644 comp/src/gb.form/.src/DocumentView/_DocumentItem.class create mode 100644 comp/src/gb.form/.src/DocumentView/_DocumentLayout.class create mode 100644 comp/src/gb.form/.src/Expander/Expander.class create mode 100644 comp/src/gb.form/.src/Expander/FExpander.class create mode 100644 comp/src/gb.form/.src/Expander/FExpander.form create mode 100644 comp/src/gb.form/.src/FInputBox.class create mode 100644 comp/src/gb.form/.src/FInputBox.form create mode 100644 comp/src/gb.form/.src/File/Bookmark/CBookmark.class create mode 100644 comp/src/gb.form/.src/File/Bookmark/CBookmarkList.class create mode 100644 comp/src/gb.form/.src/File/Bookmark/FEditBookmark.class create mode 100644 comp/src/gb.form/.src/File/Bookmark/FEditBookmark.form create mode 100644 comp/src/gb.form/.src/File/CTaskPreview.class create mode 100644 comp/src/gb.form/.src/File/Chooser/DirButton.class create mode 100644 comp/src/gb.form/.src/File/Chooser/DirChooser.class create mode 100644 comp/src/gb.form/.src/File/Chooser/FDirChooser.class create mode 100644 comp/src/gb.form/.src/File/Chooser/FDirChooser.form create mode 100644 comp/src/gb.form/.src/File/Chooser/FileChooser.class create mode 100644 comp/src/gb.form/.src/File/DirBox.class create mode 100644 comp/src/gb.form/.src/File/DirCache.class create mode 100644 comp/src/gb.form/.src/File/DirView.class create mode 100644 comp/src/gb.form/.src/File/FileView.class create mode 100644 comp/src/gb.form/.src/File/Properties/CTaskDirSize.class create mode 100644 comp/src/gb.form/.src/File/Properties/FFileProperties.class create mode 100644 comp/src/gb.form/.src/File/Properties/FFileProperties.form create mode 100644 comp/src/gb.form/.src/File/Properties/FileProperties.class create mode 100644 comp/src/gb.form/.src/File/Properties/_FilePropertiesData.class create mode 100644 comp/src/gb.form/.src/FileCompletion.class create mode 100644 comp/src/gb.form/.src/Font/FFontChooser.class create mode 100644 comp/src/gb.form/.src/Font/FFontChooser.form create mode 100644 comp/src/gb.form/.src/Font/FontBox.class create mode 100644 comp/src/gb.form/.src/Font/FontChooser.class create mode 100644 comp/src/gb.form/.src/Help.module create mode 100644 comp/src/gb.form/.src/IconPanel/IconPanel.class create mode 100644 comp/src/gb.form/.src/IconPanel/_IconPanelContainer.class create mode 100644 comp/src/gb.form/.src/ImageView/ImageView.class create mode 100644 comp/src/gb.form/.src/InputBox.class create mode 100644 comp/src/gb.form/.src/LCDLabel.class create mode 100644 comp/src/gb.form/.src/ListContainer.class create mode 100644 comp/src/gb.form/.src/ListEditor/FListEditor.class create mode 100644 comp/src/gb.form/.src/ListEditor/FListEditor.form create mode 100644 comp/src/gb.form/.src/ListEditor/ListEditor.class create mode 100644 comp/src/gb.form/.src/Main.module create mode 100644 comp/src/gb.form/.src/MaskBox.class create mode 100644 comp/src/gb.form/.src/Message/FMessage.class create mode 100644 comp/src/gb.form/.src/Message/FMessage.form create mode 100644 comp/src/gb.form/.src/Message/Message.module create mode 100644 comp/src/gb.form/.src/Message/MessageView.class create mode 100644 comp/src/gb.form/.src/MessageLabel.class create mode 100644 comp/src/gb.form/.src/MultiContainer/_MultiContainer.class create mode 100644 comp/src/gb.form/.src/MultiContainer/_MultiContainerTab.class create mode 100644 comp/src/gb.form/.src/SidePanel/FSidePanel.class create mode 100644 comp/src/gb.form/.src/SidePanel/FSidePanel.form create mode 100644 comp/src/gb.form/.src/SidePanel/SidePanel.class create mode 100644 comp/src/gb.form/.src/SliderBox.class create mode 100644 comp/src/gb.form/.src/SpinBar/SpinBar.class create mode 100644 comp/src/gb.form/.src/Spinner.class create mode 100644 comp/src/gb.form/.src/Stock.class create mode 100644 comp/src/gb.form/.src/TabPanel/TabPanel.class create mode 100644 comp/src/gb.form/.src/TabPanel/_TabPanelButton.class create mode 100644 comp/src/gb.form/.src/TabPanel/_TabPanelContainer.class create mode 100644 comp/src/gb.form/.src/TableView.class create mode 100644 comp/src/gb.form/.src/TagBox/TagComboBox.class create mode 100644 comp/src/gb.form/.src/TagBox/_TagComboBox_Item.class create mode 100644 comp/src/gb.form/.src/Test/FBugFileView.class create mode 100644 comp/src/gb.form/.src/Test/FBugFileView.form create mode 100644 comp/src/gb.form/.src/Test/FDocumentView.class create mode 100644 comp/src/gb.form/.src/Test/FDocumentView.form create mode 100644 comp/src/gb.form/.src/Test/FFont.class create mode 100644 comp/src/gb.form/.src/Test/FFont.form create mode 100644 comp/src/gb.form/.src/Test/FIconPanel.class create mode 100644 comp/src/gb.form/.src/Test/FIconPanel.form create mode 100644 comp/src/gb.form/.src/Test/FIconView.class create mode 100644 comp/src/gb.form/.src/Test/FIconView.form create mode 100644 comp/src/gb.form/.src/Test/FLCDLabel.class create mode 100644 comp/src/gb.form/.src/Test/FLCDLabel.form create mode 100644 comp/src/gb.form/.src/Test/FMain.class create mode 100644 comp/src/gb.form/.src/Test/FMain.form create mode 100644 comp/src/gb.form/.src/Test/FSpinBar.class create mode 100644 comp/src/gb.form/.src/Test/FSpinBar.form create mode 100644 comp/src/gb.form/.src/Test/FSpinner.class create mode 100644 comp/src/gb.form/.src/Test/FSpinner.form create mode 100644 comp/src/gb.form/.src/Test/FSwitchButton.class create mode 100644 comp/src/gb.form/.src/Test/FSwitchButton.form create mode 100644 comp/src/gb.form/.src/Test/FTabPanel.class create mode 100644 comp/src/gb.form/.src/Test/FTabPanel.form create mode 100644 comp/src/gb.form/.src/Test/FTableView.class create mode 100644 comp/src/gb.form/.src/Test/FTableView.form create mode 100644 comp/src/gb.form/.src/Test/FTestArrangement.class create mode 100644 comp/src/gb.form/.src/Test/FTestArrangement.form create mode 100644 comp/src/gb.form/.src/Test/FTestBalloon.class create mode 100644 comp/src/gb.form/.src/Test/FTestBalloon.form create mode 100644 comp/src/gb.form/.src/Test/FTestColorChooser.class create mode 100644 comp/src/gb.form/.src/Test/FTestColorChooser.form create mode 100644 comp/src/gb.form/.src/Test/FTestCompletion.class create mode 100644 comp/src/gb.form/.src/Test/FTestCompletion.form create mode 100644 comp/src/gb.form/.src/Test/FTestDateChooser.class create mode 100644 comp/src/gb.form/.src/Test/FTestDateChooser.form create mode 100644 comp/src/gb.form/.src/Test/FTestFileView.class create mode 100644 comp/src/gb.form/.src/Test/FTestFileView.form create mode 100644 comp/src/gb.form/.src/Test/FTestImageView.class create mode 100644 comp/src/gb.form/.src/Test/FTestImageView.form create mode 100644 comp/src/gb.form/.src/Test/FTestListEditor.class create mode 100644 comp/src/gb.form/.src/Test/FTestListEditor.form create mode 100644 comp/src/gb.form/.src/Test/FTestMaskBox.class create mode 100644 comp/src/gb.form/.src/Test/FTestMaskBox.form create mode 100644 comp/src/gb.form/.src/Test/FTestMenuButton.class create mode 100644 comp/src/gb.form/.src/Test/FTestMenuButton.form create mode 100644 comp/src/gb.form/.src/Test/FTestMessageView.class create mode 100644 comp/src/gb.form/.src/Test/FTestMessageView.form create mode 100644 comp/src/gb.form/.src/Test/FTestSidePanel.class create mode 100644 comp/src/gb.form/.src/Test/FTestSidePanel.form create mode 100644 comp/src/gb.form/.src/Test/FTestToolPanel.class create mode 100644 comp/src/gb.form/.src/Test/FTestToolPanel.form create mode 100644 comp/src/gb.form/.src/Test/FTestValueBox.class create mode 100644 comp/src/gb.form/.src/Test/FTestValueBox.form create mode 100644 comp/src/gb.form/.src/Test/FTestWizard.class create mode 100644 comp/src/gb.form/.src/Test/FTestWizard.form create mode 100644 comp/src/gb.form/.src/Test/FWiki.class create mode 100644 comp/src/gb.form/.src/Test/FWiki.form create mode 100644 comp/src/gb.form/.src/Test/Form2.class create mode 100644 comp/src/gb.form/.src/Test/Form2.form create mode 100644 comp/src/gb.form/.src/Test/Form4.class create mode 100644 comp/src/gb.form/.src/Test/Form4.form create mode 100644 comp/src/gb.form/.src/TestControl.class create mode 100644 comp/src/gb.form/.src/ToolPanel/FToolBar.class create mode 100644 comp/src/gb.form/.src/ToolPanel/FToolBar.form create mode 100644 comp/src/gb.form/.src/ToolPanel/ToolPanel.class create mode 100644 comp/src/gb.form/.src/ToolPanel/ToolPanelContainer.class create mode 100644 comp/src/gb.form/.src/URLLabel.class create mode 100644 comp/src/gb.form/.src/ValueBox.class create mode 100644 comp/src/gb.form/.src/Wizard/FWizard.class create mode 100644 comp/src/gb.form/.src/Wizard/FWizard.form create mode 100644 comp/src/gb.form/.src/Wizard/Wizard.class create mode 100644 comp/src/gb.form/.src/Wizard/_WizardContainer.class create mode 100644 comp/src/gb.form/img/16/cross.png create mode 100644 comp/src/gb.form/img/32/filter-menu.png create mode 100644 comp/src/gb.form/img/32/filter.png create mode 100644 comp/src/gb.form/img/32/warning.png create mode 100644 comp/src/gb.form/img/8/new/side-bottom-void.png create mode 100644 comp/src/gb.form/img/8/new/side-bottom.png create mode 100644 comp/src/gb.form/img/8/new/side-left.png create mode 100644 comp/src/gb.form/img/8/new/side-right-void.png create mode 100644 comp/src/gb.form/img/8/new/side-right.png create mode 100644 comp/src/gb.form/img/8/new/side-top.png create mode 100644 comp/src/gb.form/img/8/side-bottom-void.png create mode 100644 comp/src/gb.form/img/8/side-bottom.png create mode 100644 comp/src/gb.form/img/8/side-left.png create mode 100644 comp/src/gb.form/img/8/side-right-void.png create mode 100644 comp/src/gb.form/img/8/side-right.png create mode 100644 comp/src/gb.form/img/8/side-top.png create mode 100644 comp/src/gb.form/img/colormap.png create mode 100644 comp/src/gb.form/img/cross.png create mode 100644 comp/src/gb.form/img/handle-h.png create mode 100644 comp/src/gb.form/img/handle-v.png create mode 100644 comp/src/gb.form/img/round.png create mode 100644 comp/src/gb.form/img/select-dark.png create mode 100644 comp/src/gb.form/img/select.png create mode 100644 comp/src/gb.form/img/unknown.svg create mode 100644 comp/src/gb.form/img/valuemap.png create mode 100644 comp/src/gb.form/map/icon.map create mode 100644 comp/src/gb.form/stock/16/gambas.png create mode 100644 comp/src/gb.form/stock/16/gnu.png create mode 100644 comp/src/gb.form/stock/16/linux.png create mode 100644 comp/src/gb.form/stock/32/gambas.png create mode 100644 comp/src/gb.form/stock/32/gnu.png create mode 100644 comp/src/gb.form/stock/32/linux.png create mode 100644 comp/src/gb.form/stock/scalable/gambas.svg create mode 100644 comp/src/gb.form/stock/scalable/gnu.svg create mode 100644 comp/src/gb.form/stock/scalable/linux.svg create mode 100644 comp/src/gb.gui.base/.component create mode 100644 comp/src/gb.gui.base/.directory create mode 100644 comp/src/gb.gui.base/.icon.png create mode 100644 comp/src/gb.gui.base/.project create mode 100644 comp/src/gb.gui.base/.src/Action.class create mode 100644 comp/src/gb.gui.base/.src/Border.class create mode 100644 comp/src/gb.gui.base/.src/Draw.module create mode 100644 comp/src/gb.gui.base/.src/Fill.class create mode 100644 comp/src/gb.gui.base/.src/GridView/GridView.class create mode 100644 comp/src/gb.gui.base/.src/GridView/GridViewSelection.class create mode 100644 comp/src/gb.gui.base/.src/GridView/_GridView_Cell.class create mode 100644 comp/src/gb.gui.base/.src/GridView/_GridView_Column.class create mode 100644 comp/src/gb.gui.base/.src/GridView/_GridView_Columns.class create mode 100644 comp/src/gb.gui.base/.src/GridView/_GridView_Data.class create mode 100644 comp/src/gb.gui.base/.src/GridView/_GridView_Row.class create mode 100644 comp/src/gb.gui.base/.src/GridView/_GridView_Rows.class create mode 100644 comp/src/gb.gui.base/.src/IconView/IconView.class create mode 100644 comp/src/gb.gui.base/.src/IconView/_IconView_Item.class create mode 100644 comp/src/gb.gui.base/.src/Line.class create mode 100644 comp/src/gb.gui.base/.src/ListBox/ListBox.class create mode 100644 comp/src/gb.gui.base/.src/ListBox/_ListBox_Item.class create mode 100644 comp/src/gb.gui.base/.src/Main.module create mode 100644 comp/src/gb.gui.base/.src/Message.class create mode 100644 comp/src/gb.gui.base/.src/MyComboBox.class create mode 100644 comp/src/gb.gui.base/.src/Paint.class create mode 100644 comp/src/gb.gui.base/.src/Picture.class create mode 100644 comp/src/gb.gui.base/.src/PictureBox.class create mode 100644 comp/src/gb.gui.base/.src/ProgressBar.class create mode 100644 comp/src/gb.gui.base/.src/ScrollArea.class create mode 100644 comp/src/gb.gui.base/.src/ScrollView.class create mode 100644 comp/src/gb.gui.base/.src/Shortcut.class create mode 100644 comp/src/gb.gui.base/.src/SpinBox.class create mode 100644 comp/src/gb.gui.base/.src/Split/HSplit.class create mode 100644 comp/src/gb.gui.base/.src/Split/VSplit.class create mode 100644 comp/src/gb.gui.base/.src/Split/_Split.class create mode 100644 comp/src/gb.gui.base/.src/Spring.class create mode 100644 comp/src/gb.gui.base/.src/Test/FBorder.class create mode 100644 comp/src/gb.gui.base/.src/Test/FBorder.form create mode 100644 comp/src/gb.gui.base/.src/Test/FGridView.class create mode 100644 comp/src/gb.gui.base/.src/Test/FGridView.form create mode 100644 comp/src/gb.gui.base/.src/Test/FIconView.class create mode 100644 comp/src/gb.gui.base/.src/Test/FIconView.form create mode 100644 comp/src/gb.gui.base/.src/Test/FListBox.class create mode 100644 comp/src/gb.gui.base/.src/Test/FListBox.form create mode 100644 comp/src/gb.gui.base/.src/Test/FMain.class create mode 100644 comp/src/gb.gui.base/.src/Test/FMain.form create mode 100644 comp/src/gb.gui.base/.src/Test/FPaint.class create mode 100644 comp/src/gb.gui.base/.src/Test/FPaint.form create mode 100644 comp/src/gb.gui.base/.src/Test/FProgressBar.class create mode 100644 comp/src/gb.gui.base/.src/Test/FProgressBar.form create mode 100644 comp/src/gb.gui.base/.src/Test/FScrollArea.class create mode 100644 comp/src/gb.gui.base/.src/Test/FScrollArea.form create mode 100644 comp/src/gb.gui.base/.src/Test/FTestClipping.class create mode 100644 comp/src/gb.gui.base/.src/Test/FTestClipping.form create mode 100644 comp/src/gb.gui.base/.src/Test/FTestCombo.class create mode 100644 comp/src/gb.gui.base/.src/Test/FTestCombo.form create mode 100644 comp/src/gb.gui.base/.src/Test/FTestMouseWheel.class create mode 100644 comp/src/gb.gui.base/.src/Test/FTestMouseWheel.form create mode 100644 comp/src/gb.gui.base/.src/Test/FTreeView.class create mode 100644 comp/src/gb.gui.base/.src/Test/FTreeView.form create mode 100644 comp/src/gb.gui.base/.src/Test/FileView/CTaskPreview.class create mode 100644 comp/src/gb.gui.base/.src/Test/FileView/DirCache.class create mode 100644 comp/src/gb.gui.base/.src/Test/FileView/FTestFileView.class create mode 100644 comp/src/gb.gui.base/.src/Test/FileView/FTestFileView.form create mode 100644 comp/src/gb.gui.base/.src/Test/FileView/FileView.class create mode 100644 comp/src/gb.gui.base/.src/Test/FileView/Help.module create mode 100644 comp/src/gb.gui.base/.src/TreeView/ColumnView.class create mode 100644 comp/src/gb.gui.base/.src/TreeView/ListView.class create mode 100644 comp/src/gb.gui.base/.src/TreeView/RenameBox.class create mode 100644 comp/src/gb.gui.base/.src/TreeView/TreeView.class create mode 100644 comp/src/gb.gui.base/.src/TreeView/_ColumnView_Columns.class create mode 100644 comp/src/gb.gui.base/.src/TreeView/_TreeView.class create mode 100644 comp/src/gb.gui.base/.src/TreeView/_TreeView_Item.class create mode 100644 comp/src/gb.gui.base/.src/_Draw_Clip.class create mode 100644 comp/src/gb.gui.base/.src/_Draw_Style.class create mode 100644 comp/src/gb.gui.base/.src/_Gui.class create mode 100644 comp/src/gb.gui.base/pattern/10.png create mode 100644 comp/src/gb.gui.base/pattern/11.png create mode 100644 comp/src/gb.gui.base/pattern/12.png create mode 100644 comp/src/gb.gui.base/pattern/13.png create mode 100644 comp/src/gb.gui.base/pattern/14.png create mode 100644 comp/src/gb.gui.base/pattern/2.png create mode 100644 comp/src/gb.gui.base/pattern/3.png create mode 100644 comp/src/gb.gui.base/pattern/4.png create mode 100644 comp/src/gb.gui.base/pattern/5.png create mode 100644 comp/src/gb.gui.base/pattern/6.png create mode 100644 comp/src/gb.gui.base/pattern/7.png create mode 100644 comp/src/gb.gui.base/pattern/8.png create mode 100644 comp/src/gb.gui.base/pattern/9.png create mode 120000 comp/src/gb.gui.base/picturebox.png create mode 100644 comp/src/gb.logging/.component create mode 100644 comp/src/gb.logging/.directory create mode 100644 comp/src/gb.logging/.icon.png create mode 100644 comp/src/gb.logging/.project create mode 100644 comp/src/gb.logging/.src/ComplexLogger.class create mode 100644 comp/src/gb.logging/.src/ConsoleHandler.class create mode 100644 comp/src/gb.logging/.src/FileHandler.class create mode 100644 comp/src/gb.logging/.src/Formatter.module create mode 100644 comp/src/gb.logging/.src/LogHandler.class create mode 100644 comp/src/gb.logging/.src/LogLevel.module create mode 100644 comp/src/gb.logging/.src/LogRotator.module create mode 100644 comp/src/gb.logging/.src/Logger.class create mode 100644 comp/src/gb.logging/.src/MTest.module create mode 100644 comp/src/gb.map/.component create mode 100644 comp/src/gb.map/.directory create mode 100644 comp/src/gb.map/.hidden/control/mapview.png create mode 100644 comp/src/gb.map/.icon.png create mode 100644 comp/src/gb.map/.project create mode 100644 comp/src/gb.map/.src/FCarto.class create mode 100644 comp/src/gb.map/.src/FCarto.form create mode 100644 comp/src/gb.map/.src/Map.class create mode 100644 comp/src/gb.map/.src/MapView.class create mode 100644 comp/src/gb.map/.src/Shapes/_ShapeItem.class create mode 100644 comp/src/gb.map/.src/Sprite.class create mode 100644 comp/src/gb.map/.src/Tests/FMain.class create mode 100644 comp/src/gb.map/.src/Tests/FMain.form create mode 100644 comp/src/gb.map/.src/Tests/FTestWmts.class create mode 100644 comp/src/gb.map/.src/Tests/FTestWmts.form create mode 100644 comp/src/gb.map/.src/Tests/Form1.class create mode 100644 comp/src/gb.map/.src/Tests/Form1.form create mode 100644 comp/src/gb.map/.src/Tests/Form2.class create mode 100644 comp/src/gb.map/.src/Tests/Form2.form create mode 100644 comp/src/gb.map/.src/Tests/Form3.class create mode 100644 comp/src/gb.map/.src/Tests/Form3.form create mode 100644 comp/src/gb.map/.src/Tests/Form4.class create mode 100644 comp/src/gb.map/.src/Tests/Form4.form create mode 100644 comp/src/gb.map/.src/Tests/Form5.class create mode 100644 comp/src/gb.map/.src/Tests/Form5.form create mode 100644 comp/src/gb.map/.src/Tests/Form6.class create mode 100644 comp/src/gb.map/.src/Tests/Form6.form create mode 100644 comp/src/gb.map/.src/Tests/MMain.module create mode 100644 comp/src/gb.map/.src/Tools/Geo.module create mode 100644 comp/src/gb.map/.src/Tools/MyPaint.class create mode 100644 comp/src/gb.map/.src/Tools/Proj.class create mode 100644 comp/src/gb.map/.src/Types/MapBounds.class create mode 100644 comp/src/gb.map/.src/Types/MapPoint.class create mode 100644 comp/src/gb.map/.src/Types/TileSource.module create mode 100644 comp/src/gb.map/.src/Types/_Tile.class create mode 100644 comp/src/gb.map/.src/_MapLayer.class create mode 100644 comp/src/gb.map/.src/_MapShape.class create mode 100644 comp/src/gb.map/.src/_MapTile.class create mode 100644 comp/src/gb.map/.src/_ViewLayer.class create mode 100644 comp/src/gb.map/Text1 create mode 100644 comp/src/gb.map/bar.png create mode 100644 comp/src/gb.map/cursor.png create mode 100644 comp/src/gb.map/minus.png create mode 100644 comp/src/gb.map/plus.png create mode 100644 comp/src/gb.map/point.png create mode 100644 comp/src/gb.map/pointsparcelle create mode 100644 comp/src/gb.markdown/.component create mode 100644 comp/src/gb.markdown/.directory create mode 100644 comp/src/gb.markdown/.icon.png create mode 100644 comp/src/gb.markdown/.project create mode 100644 comp/src/gb.markdown/.src/MTest.module create mode 100644 comp/src/gb.markdown/.src/Markdown.class create mode 100644 comp/src/gb.markdown/.src/MarkdownLink.class create mode 100644 comp/src/gb.markdown/.src/Markup.module create mode 100644 comp/src/gb.markdown/test.txt create mode 100644 comp/src/gb.media.form/.component create mode 100644 comp/src/gb.media.form/.directory create mode 100644 comp/src/gb.media.form/.hidden/control/mediaview.png create mode 100644 comp/src/gb.media.form/.icon.png create mode 100644 comp/src/gb.media.form/.project create mode 100644 comp/src/gb.media.form/.src/FMediaPlayer.class create mode 100644 comp/src/gb.media.form/.src/FMediaPlayer.form create mode 100644 comp/src/gb.media.form/.src/FTest.class create mode 100644 comp/src/gb.media.form/.src/FTest.form create mode 100644 comp/src/gb.media.form/.src/MediaView.class create mode 100644 comp/src/gb.memcached/.component create mode 100644 comp/src/gb.memcached/.directory create mode 100644 comp/src/gb.memcached/.icon.png create mode 100644 comp/src/gb.memcached/.project create mode 100644 comp/src/gb.memcached/.src/FMain.class create mode 100644 comp/src/gb.memcached/.src/FMain.form create mode 100644 comp/src/gb.memcached/.src/Main.module create mode 100644 comp/src/gb.memcached/.src/Memcached.class create mode 100644 comp/src/gb.memcached/.src/_Memcached_Key.class create mode 100644 comp/src/gb.mysql/.component create mode 100644 comp/src/gb.mysql/.directory create mode 100644 comp/src/gb.mysql/.icon.png create mode 100644 comp/src/gb.mysql/.lang/es.mo create mode 100644 comp/src/gb.mysql/.lang/es.po create mode 100644 comp/src/gb.mysql/.project create mode 100644 comp/src/gb.mysql/.src/Connection.class create mode 100644 comp/src/gb.mysql/.src/DB.class create mode 100644 comp/src/gb.mysql/.src/_DataBase.class create mode 100644 comp/src/gb.mysql/.src/_DataTypes.class create mode 100644 comp/src/gb.mysql/.src/_Event.class create mode 100644 comp/src/gb.mysql/.src/_Field.class create mode 100644 comp/src/gb.mysql/.src/_FieldEspecifications.class create mode 100644 comp/src/gb.mysql/.src/_Index.class create mode 100644 comp/src/gb.mysql/.src/_MySQL.class create mode 100644 comp/src/gb.mysql/.src/_Result.class create mode 100644 comp/src/gb.mysql/.src/_Routines.class create mode 100644 comp/src/gb.mysql/.src/_Table.class create mode 100644 comp/src/gb.mysql/.src/_TableMaintenance.class create mode 100644 comp/src/gb.mysql/.src/_Trigger.class create mode 100644 comp/src/gb.mysql/.src/_User.class create mode 100644 comp/src/gb.mysql/.src/_Version.class create mode 100644 comp/src/gb.mysql/.src/_View.class create mode 100644 comp/src/gb.mysql/.src/modMain.module create mode 100644 comp/src/gb.mysql/logo.png create mode 100644 comp/src/gb.net.pop3/.component create mode 100644 comp/src/gb.net.pop3/.directory create mode 100644 comp/src/gb.net.pop3/.hidden/control/pop3client.png create mode 100644 comp/src/gb.net.pop3/.icon.png create mode 100644 comp/src/gb.net.pop3/.lang/cs.mo create mode 100644 comp/src/gb.net.pop3/.lang/cs.po create mode 100644 comp/src/gb.net.pop3/.lang/es.mo create mode 100644 comp/src/gb.net.pop3/.lang/es.po create mode 100644 comp/src/gb.net.pop3/.lang/es_ES.mo create mode 100644 comp/src/gb.net.pop3/.lang/es_ES.po create mode 100644 comp/src/gb.net.pop3/.lang/nl.mo create mode 100644 comp/src/gb.net.pop3/.lang/nl.po create mode 100644 comp/src/gb.net.pop3/.lang/zh.mo create mode 100644 comp/src/gb.net.pop3/.lang/zh.po create mode 100644 comp/src/gb.net.pop3/.project create mode 100644 comp/src/gb.net.pop3/.src/MTest.module create mode 100644 comp/src/gb.net.pop3/.src/Net.class create mode 100644 comp/src/gb.net.pop3/.src/POPClient.class create mode 100644 comp/src/gb.net.pop3/.src/Pop3Client.class create mode 100644 comp/src/gb.net.pop3/.src/SSLClient.class create mode 100644 comp/src/gb.net.pop3/.src/TCPClient.class create mode 100644 comp/src/gb.net.pop3/.src/_Pop3Client_Message.class create mode 100644 comp/src/gb.net.smtp/.component create mode 100644 comp/src/gb.net.smtp/.directory create mode 100644 comp/src/gb.net.smtp/.hidden/control/smtpclient.png create mode 100644 comp/src/gb.net.smtp/.icon.png create mode 100644 comp/src/gb.net.smtp/.project create mode 100644 comp/src/gb.net.smtp/.src/Encode.module create mode 100644 comp/src/gb.net.smtp/.src/Main.module create mode 100644 comp/src/gb.net.smtp/.src/Net.class create mode 100644 comp/src/gb.net.smtp/.src/SmtpClient.class create mode 100644 comp/src/gb.net.smtp/.src/SmtpPart.class create mode 100644 comp/src/gb.net.smtp/.src/SmtpSession.class create mode 100644 comp/src/gb.net.smtp/.src/SslSession.class create mode 100644 comp/src/gb.net.smtp/.src/TcpSession.class create mode 100644 comp/src/gb.net.smtp/.src/TlsSession.class create mode 100644 comp/src/gb.report/.component create mode 100644 comp/src/gb.report/.connection/Connection1.connection create mode 100644 comp/src/gb.report/.connection/Connection2.connection create mode 100644 comp/src/gb.report/.connection/MainConn.connection create mode 100644 comp/src/gb.report/.dir_icon.png create mode 100644 comp/src/gb.report/.directory create mode 100644 comp/src/gb.report/.hidden/control/reportdrawingarea.png create mode 100644 comp/src/gb.report/.hidden/control/reportgridview.png create mode 100644 comp/src/gb.report/.hidden/control/reporthbox.png create mode 100644 comp/src/gb.report/.hidden/control/reportimage.png create mode 100644 comp/src/gb.report/.hidden/control/reportlabel.png create mode 100644 comp/src/gb.report/.hidden/control/reportline.png create mode 100644 comp/src/gb.report/.hidden/control/reportpagebreak.png create mode 100644 comp/src/gb.report/.hidden/control/reportpanel.png create mode 100644 comp/src/gb.report/.hidden/control/reportsvgimage.png create mode 100644 comp/src/gb.report/.hidden/control/reporttextlabel.png create mode 100644 comp/src/gb.report/.hidden/control/reportvbox.png create mode 100644 comp/src/gb.report/.hidden/control/reportview.png create mode 100644 comp/src/gb.report/.hidden/control/reportvpanel.png create mode 100644 comp/src/gb.report/.icon.png create mode 100644 comp/src/gb.report/.lang/ca.mo create mode 100644 comp/src/gb.report/.lang/ca.po create mode 100644 comp/src/gb.report/.lang/cs.mo create mode 100644 comp/src/gb.report/.lang/cs.po create mode 100644 comp/src/gb.report/.lang/es.mo create mode 100644 comp/src/gb.report/.lang/es.po create mode 100644 comp/src/gb.report/.lang/es_ES.mo create mode 100644 comp/src/gb.report/.lang/es_ES.po create mode 100644 comp/src/gb.report/.lang/fr.mo create mode 100644 comp/src/gb.report/.lang/fr.po create mode 100644 comp/src/gb.report/.lang/nl.mo create mode 100644 comp/src/gb.report/.lang/nl.po create mode 100644 comp/src/gb.report/.lang/zh.mo create mode 100644 comp/src/gb.report/.lang/zh.po create mode 100644 comp/src/gb.report/.project create mode 100644 comp/src/gb.report/.src/Borders/ReportBorder.class create mode 100644 comp/src/gb.report/.src/Borders/_ReportBorderSide.class create mode 100644 comp/src/gb.report/.src/Borders/_ReportRoundCorner.class create mode 100644 comp/src/gb.report/.src/BoxShadow/FReportBoxEditor.class create mode 100644 comp/src/gb.report/.src/BoxShadow/FReportBoxEditor.form create mode 100644 comp/src/gb.report/.src/BoxShadow/ReportBoxShadow.class create mode 100644 comp/src/gb.report/.src/BoxShadow/_ReportBoxShadow.class create mode 100644 comp/src/gb.report/.src/Brush/ReportBrush.class create mode 100644 comp/src/gb.report/.src/Controls/ReportControl.class create mode 100644 comp/src/gb.report/.src/MainTools/MReport.module create mode 100644 comp/src/gb.report/.src/MainTools/ReportUnits.module create mode 100644 comp/src/gb.report/.src/MainTools/Types/TControl.class create mode 100644 comp/src/gb.report/.src/MainTools/Types/TPageColumn.class create mode 100644 comp/src/gb.report/.src/MainTools/Types/TSizeHint.class create mode 100644 comp/src/gb.report/.src/MainTools/Types/TSizeParse.class create mode 100644 comp/src/gb.report/.src/Optional/Align.class create mode 100644 comp/src/gb.report/.src/Optional/Arrange.class create mode 100644 comp/src/gb.report/.src/Optional/Line.class create mode 100644 comp/src/gb.report/.src/Padding/ReportPadding.class create mode 100644 comp/src/gb.report/.src/Preview/CPrint.class create mode 100644 comp/src/gb.report/.src/Preview/FOptions.class create mode 100644 comp/src/gb.report/.src/Preview/FOptions.form create mode 100644 comp/src/gb.report/.src/Preview/FPreview.class create mode 100644 comp/src/gb.report/.src/Preview/FPreview.form create mode 100644 comp/src/gb.report/.src/Preview/FPrint.class create mode 100644 comp/src/gb.report/.src/Preview/FPrint.form create mode 100644 comp/src/gb.report/.src/Preview/Form1.class create mode 100644 comp/src/gb.report/.src/Preview/Form1.form create mode 100644 comp/src/gb.report/.src/Preview/ReportView.class create mode 100644 comp/src/gb.report/.src/Preview/ReportViewTask.class create mode 100644 comp/src/gb.report/.src/Report.class create mode 100644 comp/src/gb.report/.src/ReportContainer.class create mode 100644 comp/src/gb.report/.src/ReportDrawingArea.class create mode 100644 comp/src/gb.report/.src/ReportFrame.class create mode 100644 comp/src/gb.report/.src/ReportGridView.class create mode 100644 comp/src/gb.report/.src/ReportGridView/_ReportGridViewCell.class create mode 100644 comp/src/gb.report/.src/ReportGridView/_ReportGridViewColumn.class create mode 100644 comp/src/gb.report/.src/ReportGridView/_ReportGridViewColumns.class create mode 100644 comp/src/gb.report/.src/ReportGridView/_ReportGridViewRow.class create mode 100644 comp/src/gb.report/.src/ReportGridView/_ReportGridViewRows.class create mode 100644 comp/src/gb.report/.src/ReportHBox.class create mode 100644 comp/src/gb.report/.src/ReportImage.class create mode 100644 comp/src/gb.report/.src/ReportLabel.class create mode 100644 comp/src/gb.report/.src/ReportLine.class create mode 100644 comp/src/gb.report/.src/ReportPageBreak.class create mode 100644 comp/src/gb.report/.src/ReportPanel.class create mode 100644 comp/src/gb.report/.src/ReportSection.class create mode 100644 comp/src/gb.report/.src/ReportSvgImage.class create mode 100644 comp/src/gb.report/.src/ReportTextLabel.class create mode 100644 comp/src/gb.report/.src/ReportVBox.class create mode 100644 comp/src/gb.report/.src/ReportVPanel.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Paints.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report1.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report1.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report10.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report10.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report12.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report12.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report13.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report13.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report14.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report14.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report2.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report2.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report3.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report3.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report4.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report4.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report5.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report5.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report6.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report6.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report7.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report7.report create mode 100644 comp/src/gb.report/.src/Tests/Old/Report8.class create mode 100644 comp/src/gb.report/.src/Tests/Old/Report8.report create mode 100644 comp/src/gb.report/.src/Tests/Old/myReport1.class create mode 100644 comp/src/gb.report/.src/Tests/Old/myReport1.report create mode 100644 comp/src/gb.report/.src/Tests/Old/myReport2.class create mode 100644 comp/src/gb.report/.src/Tests/Old/myReport2.report create mode 100644 comp/src/gb.report/.src/Tests/Old/myReport5.class create mode 100644 comp/src/gb.report/.src/Tests/Old/myReport5.report create mode 100644 comp/src/gb.report/.src/Tests/OutputReport.class create mode 100644 comp/src/gb.report/.src/Tests/OutputReport.report create mode 100644 comp/src/gb.report/.src/Tests/OutputReport2.class create mode 100644 comp/src/gb.report/.src/Tests/OutputReport2.report create mode 100644 comp/src/gb.report/.src/Tests/Paints.class create mode 100644 comp/src/gb.report/.src/Tests/Report11.class create mode 100644 comp/src/gb.report/.src/Tests/Report11.report create mode 100644 comp/src/gb.report/.src/Tests/Report41.class create mode 100644 comp/src/gb.report/.src/Tests/Report41.report create mode 100644 comp/src/gb.report/.src/Tests/Report51.class create mode 100644 comp/src/gb.report/.src/Tests/Report51.report create mode 100644 comp/src/gb.report/.src/Tests/Report52.class create mode 100644 comp/src/gb.report/.src/Tests/Report52.report create mode 100644 comp/src/gb.report/.src/Tests/Report9.class create mode 100644 comp/src/gb.report/.src/Tests/Report9.report create mode 100644 comp/src/gb.report/.src/Tests/Test.module create mode 100644 comp/src/gb.report/.src/Tests/rpTestShadowGrid.class create mode 100644 comp/src/gb.report/.src/Tests/rpTestShadowGrid.report create mode 100644 comp/src/gb.report/ChangeLog create mode 100644 comp/src/gb.report/gambas.svg create mode 100644 comp/src/gb.report/img/16/red-arrow-h.png create mode 100644 comp/src/gb.report/img/16/red-arrow-v.png create mode 100644 comp/src/gb.report/img/22/FullWidth.png create mode 100644 comp/src/gb.report/img/22/OnePage.png create mode 100644 comp/src/gb.report/img/22/RealSize.png create mode 100644 comp/src/gb.report/img/22/TwoPage.png create mode 100644 comp/src/gb.report/img/32/Collatecopie.png create mode 100644 comp/src/gb.report/img/32/Empty.png create mode 100644 comp/src/gb.report/img/32/grayscale.png create mode 100644 comp/src/gb.report/img/32/reverse.png create mode 100644 comp/src/gb.report/img/control/hbox.png create mode 100644 comp/src/gb.report/img/control/label.png create mode 100644 comp/src/gb.report/img/control/picturebox.png create mode 100644 comp/src/gb.report/img/control/vbox.png create mode 100644 comp/src/gb.report/img/control/vpanel.png create mode 100644 comp/src/gb.report/img/logo.svg create mode 100644 comp/src/gb.report/printer1.png create mode 100644 comp/src/gb.report2/.component create mode 100644 comp/src/gb.report2/.connection/Connection1.connection create mode 100644 comp/src/gb.report2/.connection/Connection2.connection create mode 100644 comp/src/gb.report2/.connection/Connection2.template create mode 100644 comp/src/gb.report2/.directory create mode 120000 comp/src/gb.report2/.hidden/control/reportdrawingarea.png create mode 120000 comp/src/gb.report2/.hidden/control/reportgridview.png create mode 120000 comp/src/gb.report2/.hidden/control/reporthbox.png create mode 120000 comp/src/gb.report2/.hidden/control/reportimage.png create mode 120000 comp/src/gb.report2/.hidden/control/reportlabel.png create mode 100644 comp/src/gb.report2/.hidden/control/reportline.png create mode 100644 comp/src/gb.report2/.hidden/control/reportpagebreak.png create mode 120000 comp/src/gb.report2/.hidden/control/reportpanel.png create mode 100644 comp/src/gb.report2/.hidden/control/reportsvgimage.png create mode 120000 comp/src/gb.report2/.hidden/control/reporttextlabel.png create mode 120000 comp/src/gb.report2/.hidden/control/reportvbox.png create mode 100644 comp/src/gb.report2/.hidden/control/reportview.png create mode 120000 comp/src/gb.report2/.hidden/control/reportvpanel.png create mode 100644 comp/src/gb.report2/.icon.png create mode 100644 comp/src/gb.report2/.lang/es.mo create mode 100644 comp/src/gb.report2/.lang/es.po create mode 100644 comp/src/gb.report2/.lang/es_ES.mo create mode 100644 comp/src/gb.report2/.lang/es_ES.po create mode 100644 comp/src/gb.report2/.lang/fr.mo create mode 100644 comp/src/gb.report2/.lang/fr.po create mode 100644 comp/src/gb.report2/.lang/nl.mo create mode 100644 comp/src/gb.report2/.lang/nl.po create mode 100644 comp/src/gb.report2/.project create mode 100644 comp/src/gb.report2/.src/Evaluator/CResult.class create mode 100644 comp/src/gb.report2/.src/Evaluator/_RepExp.class create mode 100644 comp/src/gb.report2/.src/Optional/Align.class create mode 100644 comp/src/gb.report2/.src/Optional/Arrange.class create mode 100644 comp/src/gb.report2/.src/Optional/Line.class create mode 100644 comp/src/gb.report2/.src/Report.class create mode 100644 comp/src/gb.report2/.src/ReportContainer.class create mode 100644 comp/src/gb.report2/.src/ReportControl.class create mode 100644 comp/src/gb.report2/.src/ReportDrawingArea.class create mode 100644 comp/src/gb.report2/.src/ReportFrame.class create mode 100644 comp/src/gb.report2/.src/ReportGridView/ReportGridView.class create mode 100644 comp/src/gb.report2/.src/ReportGridView/_ReportGridViewColumn.class create mode 100644 comp/src/gb.report2/.src/ReportGridView/_ReportGridViewColumns.class create mode 100644 comp/src/gb.report2/.src/ReportGridView/_ReportGridViewData.class create mode 100644 comp/src/gb.report2/.src/ReportGridView/_ReportGridViewRow.class create mode 100644 comp/src/gb.report2/.src/ReportGridView/_ReportGridViewRows.class create mode 100644 comp/src/gb.report2/.src/ReportHBox.class create mode 100644 comp/src/gb.report2/.src/ReportImage.class create mode 100644 comp/src/gb.report2/.src/ReportLabel.class create mode 100644 comp/src/gb.report2/.src/ReportLine.class create mode 100644 comp/src/gb.report2/.src/ReportPageBreak.class create mode 100644 comp/src/gb.report2/.src/ReportPanel.class create mode 100644 comp/src/gb.report2/.src/ReportSection.class create mode 100644 comp/src/gb.report2/.src/ReportSvgImage.class create mode 100644 comp/src/gb.report2/.src/ReportTextLabel.class create mode 100644 comp/src/gb.report2/.src/ReportVBox.class create mode 100644 comp/src/gb.report2/.src/ReportVPanel.class create mode 100644 comp/src/gb.report2/.src/Tests/Report10.class create mode 100644 comp/src/gb.report2/.src/Tests/Report10.report create mode 100644 comp/src/gb.report2/.src/Tests/Report13.class create mode 100644 comp/src/gb.report2/.src/Tests/Report13.report create mode 100644 comp/src/gb.report2/.src/Tests/Report14.class create mode 100644 comp/src/gb.report2/.src/Tests/Report14.report create mode 100644 comp/src/gb.report2/.src/Tests/Report15.class create mode 100644 comp/src/gb.report2/.src/Tests/Report15.report create mode 100644 comp/src/gb.report2/.src/Tests/Report16.class create mode 100644 comp/src/gb.report2/.src/Tests/Report16.report create mode 100644 comp/src/gb.report2/.src/Tests/Report17.class create mode 100644 comp/src/gb.report2/.src/Tests/Report17.report create mode 100644 comp/src/gb.report2/.src/Tests/old/FMain.class create mode 100644 comp/src/gb.report2/.src/Tests/old/FMain.form create mode 100644 comp/src/gb.report2/.src/Tests/old/Module1.module create mode 100644 comp/src/gb.report2/.src/Tests/old/OutputReport2.class create mode 100644 comp/src/gb.report2/.src/Tests/old/OutputReport2.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report1.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report1.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report11.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report11.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report12.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report12.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report2.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report2.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report3.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report3.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report4.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report4.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report5.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report5.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report51.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report51.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report6.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report6.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report7.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report7.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report8.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report8.report create mode 100644 comp/src/gb.report2/.src/Tests/old/Report9.class create mode 100644 comp/src/gb.report2/.src/Tests/old/Report9.report create mode 100644 comp/src/gb.report2/.src/Tools/CPrint.class create mode 100644 comp/src/gb.report2/.src/Tools/MUtil.module create mode 100644 comp/src/gb.report2/.src/Types/Base/ReportBrush.class create mode 100644 comp/src/gb.report2/.src/Types/Base/ReportMargin.class create mode 100644 comp/src/gb.report2/.src/Types/Base/ReportPadding.class create mode 100644 comp/src/gb.report2/.src/Types/Border/ReportBorder.class create mode 100644 comp/src/gb.report2/.src/Types/Border/_ReportBorderSide.class create mode 100644 comp/src/gb.report2/.src/Types/Border/_ReportRoundCorner.class create mode 100644 comp/src/gb.report2/.src/Types/BoxShadow/ReportBoxShadow.class create mode 100644 comp/src/gb.report2/.src/Types/BoxShadow/_ReportBoxShadow.class create mode 100644 comp/src/gb.report2/.src/Types/ReportSizeHints.class create mode 100644 comp/src/gb.report2/.src/Types/ReportSizeParser.class create mode 100644 comp/src/gb.report2/.src/Types/TControl.class create mode 100644 comp/src/gb.report2/.src/Types/TSizeParse.class create mode 100644 comp/src/gb.report2/.src/Types/_ReportVirtualControl.class create mode 100644 comp/src/gb.report2/.src/Viewer/FPreview.class create mode 100644 comp/src/gb.report2/.src/Viewer/FPreview.form create mode 100644 comp/src/gb.report2/.src/Viewer/ReportView.class create mode 100644 comp/src/gb.report2/16/full-width.png create mode 100644 comp/src/gb.report2/16/one-page.png create mode 100644 comp/src/gb.report2/16/real-size.png create mode 100644 comp/src/gb.report2/16/red-arrow-h.png create mode 100644 comp/src/gb.report2/16/red-arrow-v.png create mode 100644 comp/src/gb.report2/16/two-pages.png create mode 100644 comp/src/gb.report2/22/FullWidth.png create mode 100644 comp/src/gb.report2/22/OnePage.png create mode 100644 comp/src/gb.report2/22/RealSize.png create mode 100644 comp/src/gb.report2/22/TwoPage.png create mode 100644 comp/src/gb.report2/32/Collatecopie.png create mode 100644 comp/src/gb.report2/32/Empty.png create mode 100644 comp/src/gb.report2/32/grayscale.png create mode 100644 comp/src/gb.report2/32/reverse.png create mode 100644 comp/src/gb.report2/FunctionsList create mode 100644 comp/src/gb.report2/Structure create mode 100644 comp/src/gb.report2/gambas.svg create mode 100644 comp/src/gb.report2/icon.png create mode 100644 comp/src/gb.report2/tmpJournal create mode 100644 comp/src/gb.report2/tortueface.gif create mode 100644 comp/src/gb.scanner/.component create mode 100644 comp/src/gb.scanner/.directory create mode 100644 comp/src/gb.scanner/.icon.png create mode 100644 comp/src/gb.scanner/.project create mode 100644 comp/src/gb.scanner/.src/Demo/Form1.class create mode 100644 comp/src/gb.scanner/.src/Demo/Form1.form create mode 100644 comp/src/gb.scanner/.src/MTest.module create mode 100644 comp/src/gb.scanner/.src/MTest2.module create mode 100644 comp/src/gb.scanner/.src/Module1.module create mode 100644 comp/src/gb.scanner/.src/Module2.module create mode 100644 comp/src/gb.scanner/.src/Scanner.class create mode 100644 comp/src/gb.scanner/.src/Scanners.class create mode 100644 comp/src/gb.scanner/.src/_Option.class create mode 100644 comp/src/gb.scanner/scanner.png create mode 100644 comp/src/gb.settings/.component create mode 100644 comp/src/gb.settings/.directory create mode 100644 comp/src/gb.settings/.icon.png create mode 100644 comp/src/gb.settings/.project create mode 100644 comp/src/gb.settings/.src/Main.module create mode 100644 comp/src/gb.settings/.src/Settings.class create mode 100644 comp/src/gb.settings/.src/_Settings_Keys.class create mode 100644 comp/src/gb.term.form/.component create mode 100644 comp/src/gb.term.form/.directory create mode 100644 comp/src/gb.term.form/.hidden/CHANGELOG create mode 120000 comp/src/gb.term.form/.hidden/control/termbutton.png create mode 120000 comp/src/gb.term.form/.hidden/control/termcheckbox.png create mode 120000 comp/src/gb.term.form/.hidden/control/termframe.png create mode 120000 comp/src/gb.term.form/.hidden/control/termhbox.png create mode 120000 comp/src/gb.term.form/.hidden/control/termlabel.png create mode 120000 comp/src/gb.term.form/.hidden/control/termlistbox.png create mode 120000 comp/src/gb.term.form/.hidden/control/termpanel.png create mode 120000 comp/src/gb.term.form/.hidden/control/termpicturebox.png create mode 120000 comp/src/gb.term.form/.hidden/control/termradiobutton.png create mode 120000 comp/src/gb.term.form/.hidden/control/termscrollbar.png create mode 120000 comp/src/gb.term.form/.hidden/control/termtextbox.png create mode 120000 comp/src/gb.term.form/.hidden/control/termvbox.png create mode 100644 comp/src/gb.term.form/.icon.png create mode 100644 comp/src/gb.term.form/.lang/nl.mo create mode 100644 comp/src/gb.term.form/.lang/nl.po create mode 100644 comp/src/gb.term.form/.project create mode 100644 comp/src/gb.term.form/.src/Align.class create mode 100644 comp/src/gb.term.form/.src/Arrange.class create mode 100644 comp/src/gb.term.form/.src/Attr.class create mode 100644 comp/src/gb.term.form/.src/Border.class create mode 100644 comp/src/gb.term.form/.src/Char.class create mode 100644 comp/src/gb.term.form/.src/Desktop.class create mode 100644 comp/src/gb.term.form/.src/Dialog/Message.class create mode 100644 comp/src/gb.term.form/.src/Key.class create mode 100644 comp/src/gb.term.form/.src/Mouse.class create mode 100644 comp/src/gb.term.form/.src/TermButton.class create mode 100644 comp/src/gb.term.form/.src/TermCheckBox.class create mode 100644 comp/src/gb.term.form/.src/TermColor.class create mode 100644 comp/src/gb.term.form/.src/TermContainer.class create mode 100644 comp/src/gb.term.form/.src/TermControl.class create mode 100644 comp/src/gb.term.form/.src/TermForm.class create mode 100644 comp/src/gb.term.form/.src/TermFrame.class create mode 100644 comp/src/gb.term.form/.src/TermHBox.class create mode 100644 comp/src/gb.term.form/.src/TermLabel.class create mode 100644 comp/src/gb.term.form/.src/TermListBox.class create mode 100644 comp/src/gb.term.form/.src/TermPanel.class create mode 100644 comp/src/gb.term.form/.src/TermPictureBox.class create mode 100644 comp/src/gb.term.form/.src/TermRadioButton.class create mode 100644 comp/src/gb.term.form/.src/TermScrollBar.class create mode 100644 comp/src/gb.term.form/.src/TermTextBox.class create mode 100644 comp/src/gb.term.form/.src/TermVBox.class create mode 100644 comp/src/gb.term.form/.src/TermWindow.class create mode 100644 comp/src/gb.term.form/.src/TermWindows.class create mode 100644 comp/src/gb.term.form/.src/Test/FTest2.class create mode 100644 comp/src/gb.term.form/.src/Test/Main.module create mode 100644 comp/src/gb.term.form/.src/Test/Main2.module create mode 100644 comp/src/gb.term.form/.src/Test/Main3.module create mode 100644 comp/src/gb.term.form/.src/Test/Module1.module create mode 100644 comp/src/gb.term.form/.src/Test/Termform1.class create mode 100644 comp/src/gb.term.form/.src/Test/Termform1.termform create mode 100644 comp/src/gb.term.form/.src/Test/Termform2.class create mode 100644 comp/src/gb.term.form/.src/Test/Termform2.termform create mode 100644 comp/src/gb.term.form/.src/Test/trfTest.class create mode 100644 comp/src/gb.term.form/all.png create mode 100644 comp/src/gb.util.web/.component create mode 100644 comp/src/gb.util.web/.directory create mode 100644 comp/src/gb.util.web/.hidden/control/ccontainer.png create mode 100644 comp/src/gb.util.web/.hidden/control/ccontrol.png create mode 100644 comp/src/gb.util.web/.icon.png create mode 100644 comp/src/gb.util.web/.project create mode 100644 comp/src/gb.util.web/.src/JS.class create mode 100644 comp/src/gb.util.web/.src/JSON.module create mode 100644 comp/src/gb.util.web/.src/JSONCollection.class create mode 100644 comp/src/gb.util.web/.src/MMain.module create mode 100644 comp/src/gb.util.web/.src/URL.class create mode 100644 comp/src/gb.util.web/.src/URLQuery.class create mode 100644 comp/src/gb.util/.component create mode 100644 comp/src/gb.util/.directory create mode 100644 comp/src/gb.util/.icon.png create mode 100644 comp/src/gb.util/.project create mode 100644 comp/src/gb.util/.src/Class.class create mode 100644 comp/src/gb.util/.src/ClassStat.class create mode 100644 comp/src/gb.util/.src/CsvFile.class create mode 100644 comp/src/gb.util/.src/Date.module create mode 100644 comp/src/gb.util/.src/File.class create mode 100644 comp/src/gb.util/.src/MMain.module create mode 100644 comp/src/gb.util/.src/MPhonetic_French.module create mode 100644 comp/src/gb.util/.src/Shell.module create mode 100644 comp/src/gb.util/.src/String.class create mode 100644 comp/src/gb.web.feed/.component create mode 100644 comp/src/gb.web.feed/.directory create mode 100644 comp/src/gb.web.feed/.hidden/TODO create mode 100644 comp/src/gb.web.feed/.icon.png create mode 100644 comp/src/gb.web.feed/.lang/nl.mo create mode 100644 comp/src/gb.web.feed/.lang/nl.po create mode 100644 comp/src/gb.web.feed/.project create mode 100644 comp/src/gb.web.feed/.src/Main.module create mode 100644 comp/src/gb.web.feed/.src/Rss.class create mode 100644 comp/src/gb.web.feed/.src/RssCategory.class create mode 100644 comp/src/gb.web.feed/.src/RssCloud.class create mode 100644 comp/src/gb.web.feed/.src/RssDate.class create mode 100644 comp/src/gb.web.feed/.src/RssEnclosure.class create mode 100644 comp/src/gb.web.feed/.src/RssGuid.class create mode 100644 comp/src/gb.web.feed/.src/RssImage.class create mode 100644 comp/src/gb.web.feed/.src/RssItem.class create mode 100644 comp/src/gb.web.feed/.src/RssSource.class create mode 100644 comp/src/gb.web.feed/.src/RssTextInput.class create mode 100644 comp/src/gb.web.feed/Feed-icon.svg create mode 100644 comp/src/gb.web.feed/test.xml create mode 100644 comp/src/gb.web.form/.component create mode 100644 comp/src/gb.web.form/.directory create mode 100644 comp/src/gb.web.form/.hidden/Uncompressed/gw-style.css create mode 100644 comp/src/gb.web.form/.hidden/Uncompressed/lib.js create mode 100644 comp/src/gb.web.form/.hidden/Uncompressed/style.css create mode 100644 comp/src/gb.web.form/.hidden/calendar.js create mode 120000 comp/src/gb.web.form/.hidden/control/webbutton.png create mode 120000 comp/src/gb.web.form/.hidden/control/webcheckbox.png create mode 120000 comp/src/gb.web.form/.hidden/control/webcombobox.png create mode 100644 comp/src/gb.web.form/.hidden/control/webcontainer.png create mode 120000 comp/src/gb.web.form/.hidden/control/webdatebox.png create mode 120000 comp/src/gb.web.form/.hidden/control/webdatechooser.png create mode 120000 comp/src/gb.web.form/.hidden/control/webexpander.png create mode 120000 comp/src/gb.web.form/.hidden/control/webhbox.png create mode 100644 comp/src/gb.web.form/.hidden/control/webhtml.png create mode 120000 comp/src/gb.web.form/.hidden/control/webimage.png create mode 120000 comp/src/gb.web.form/.hidden/control/weblabel.png create mode 120000 comp/src/gb.web.form/.hidden/control/weblistbox.png create mode 100644 comp/src/gb.web.form/.hidden/control/webmenu.png create mode 100644 comp/src/gb.web.form/.hidden/control/webmenubar.png create mode 100644 comp/src/gb.web.form/.hidden/control/webmenuitem.png create mode 120000 comp/src/gb.web.form/.hidden/control/webprogressbar.png create mode 120000 comp/src/gb.web.form/.hidden/control/webradiobutton.png create mode 120000 comp/src/gb.web.form/.hidden/control/webscrollview.png create mode 120000 comp/src/gb.web.form/.hidden/control/webseparator.png create mode 120000 comp/src/gb.web.form/.hidden/control/webslider.png create mode 120000 comp/src/gb.web.form/.hidden/control/webspinbox.png create mode 100644 comp/src/gb.web.form/.hidden/control/webtable.png create mode 120000 comp/src/gb.web.form/.hidden/control/webtabpanel.png create mode 120000 comp/src/gb.web.form/.hidden/control/webtextarea.png create mode 120000 comp/src/gb.web.form/.hidden/control/webtextbox.png create mode 120000 comp/src/gb.web.form/.hidden/control/webtimer.png create mode 100644 comp/src/gb.web.form/.hidden/control/webuploadarea.png create mode 100644 comp/src/gb.web.form/.hidden/control/webuploadbutton.png create mode 100644 comp/src/gb.web.form/.hidden/control/webuploader.png create mode 120000 comp/src/gb.web.form/.hidden/control/webvbox.png create mode 100644 comp/src/gb.web.form/.icon.png create mode 100644 comp/src/gb.web.form/.lang/fr.mo create mode 100644 comp/src/gb.web.form/.lang/fr.po create mode 100644 comp/src/gb.web.form/.lang/nl.mo create mode 100644 comp/src/gb.web.form/.lang/nl.po create mode 100644 comp/src/gb.web.form/.project create mode 100644 comp/src/gb.web.form/.public/favicon.png create mode 100644 comp/src/gb.web.form/.public/gw-arrow-down.png create mode 100644 comp/src/gb.web.form/.public/gw-arrow-left.png create mode 100644 comp/src/gb.web.form/.public/gw-arrow-right.png create mode 100644 comp/src/gb.web.form/.public/gw-arrow-up.png create mode 100644 comp/src/gb.web.form/.public/gw-close.png create mode 100644 comp/src/gb.web.form/.public/gw-max.png create mode 100644 comp/src/gb.web.form/.public/gw-table-more.gif create mode 100644 comp/src/gb.web.form/.public/gw-waiting.gif create mode 100644 comp/src/gb.web.form/.public/message/close.svg create mode 100644 comp/src/gb.web.form/.public/message/error.png create mode 100644 comp/src/gb.web.form/.public/message/info.png create mode 100644 comp/src/gb.web.form/.public/message/question.png create mode 100644 comp/src/gb.web.form/.public/message/warning.png create mode 100644 comp/src/gb.web.form/.public/new.png create mode 100644 comp/src/gb.web.form/.public/open.png create mode 100644 comp/src/gb.web.form/.src/Align.class create mode 100644 comp/src/gb.web.form/.src/Arrange.class create mode 100644 comp/src/gb.web.form/.src/Calendar/FCalendar.class create mode 100644 comp/src/gb.web.form/.src/Calendar/FCalendar.webform create mode 100644 comp/src/gb.web.form/.src/Calendar/WebDateBox.class create mode 100644 comp/src/gb.web.form/.src/Calendar/WebDateChooser.class create mode 100644 comp/src/gb.web.form/.src/Color.class create mode 100644 comp/src/gb.web.form/.src/Header.class create mode 100644 comp/src/gb.web.form/.src/Header.webpage create mode 100644 comp/src/gb.web.form/.src/Main.module create mode 100644 comp/src/gb.web.form/.src/Message/FMessage.class create mode 100644 comp/src/gb.web.form/.src/Message/FMessage.webform create mode 100644 comp/src/gb.web.form/.src/Message/Message.class create mode 100644 comp/src/gb.web.form/.src/Scroll.class create mode 100644 comp/src/gb.web.form/.src/Select.class create mode 100644 comp/src/gb.web.form/.src/Test/FHello.class create mode 100644 comp/src/gb.web.form/.src/Test/FHello.webform create mode 100644 comp/src/gb.web.form/.src/Test/FTestWebUploader.class create mode 100644 comp/src/gb.web.form/.src/Test/FTestWebUploader.webform create mode 100644 comp/src/gb.web.form/.src/Test/Webform1.class create mode 100644 comp/src/gb.web.form/.src/Test/Webform1.webform create mode 100644 comp/src/gb.web.form/.src/Test/Webform2.class create mode 100644 comp/src/gb.web.form/.src/Test/Webform2.webform create mode 100644 comp/src/gb.web.form/.src/Test/Webform3.class create mode 100644 comp/src/gb.web.form/.src/Test/Webform3.webform create mode 100644 comp/src/gb.web.form/.src/Test/Webform4.class create mode 100644 comp/src/gb.web.form/.src/Test/Webform4.webform create mode 100644 comp/src/gb.web.form/.src/Test/Webform5.class create mode 100644 comp/src/gb.web.form/.src/Test/Webform5.webform create mode 100644 comp/src/gb.web.form/.src/Test/Webform6.class create mode 100644 comp/src/gb.web.form/.src/Test/Webform6.webform create mode 100644 comp/src/gb.web.form/.src/WebButton.class create mode 100644 comp/src/gb.web.form/.src/WebCheckBox.class create mode 100644 comp/src/gb.web.form/.src/WebComboBox.class create mode 100644 comp/src/gb.web.form/.src/WebContainer.class create mode 100644 comp/src/gb.web.form/.src/WebControl.class create mode 100644 comp/src/gb.web.form/.src/WebControlStyle.class create mode 100644 comp/src/gb.web.form/.src/WebExpander.class create mode 100644 comp/src/gb.web.form/.src/WebForm.class create mode 100644 comp/src/gb.web.form/.src/WebHBox.class create mode 100644 comp/src/gb.web.form/.src/WebHtml.class create mode 100644 comp/src/gb.web.form/.src/WebImage.class create mode 100644 comp/src/gb.web.form/.src/WebLabel.class create mode 100644 comp/src/gb.web.form/.src/WebMenu.class create mode 100644 comp/src/gb.web.form/.src/WebMenuBar.class create mode 100644 comp/src/gb.web.form/.src/WebMenuItem.class create mode 100644 comp/src/gb.web.form/.src/WebProgressBar.class create mode 100644 comp/src/gb.web.form/.src/WebRadioButton.class create mode 100644 comp/src/gb.web.form/.src/WebScrollView.class create mode 100644 comp/src/gb.web.form/.src/WebSeparator.class create mode 100644 comp/src/gb.web.form/.src/WebSlider.class create mode 100644 comp/src/gb.web.form/.src/WebSpinBox.class create mode 100644 comp/src/gb.web.form/.src/WebTabPanel.class create mode 100644 comp/src/gb.web.form/.src/WebTable/WebTable.class create mode 100644 comp/src/gb.web.form/.src/WebTable/WebTableData.class create mode 100644 comp/src/gb.web.form/.src/WebTable/WebTableSelection.class create mode 100644 comp/src/gb.web.form/.src/WebTable/_WebTableColumn.class create mode 100644 comp/src/gb.web.form/.src/WebTable/_WebTableColumns.class create mode 100644 comp/src/gb.web.form/.src/WebTextArea.class create mode 100644 comp/src/gb.web.form/.src/WebTextBox.class create mode 100644 comp/src/gb.web.form/.src/WebTimer.class create mode 100644 comp/src/gb.web.form/.src/WebUploadArea.class create mode 100644 comp/src/gb.web.form/.src/WebUploadButton.class create mode 100644 comp/src/gb.web.form/.src/WebUploader.class create mode 100644 comp/src/gb.web.form/.src/WebVBox.class create mode 100644 comp/src/gb.web.form/.src/WebWindow.class create mode 100644 comp/src/gb.web.form/ac.js create mode 100644 comp/src/gb.web.form/arrow-down.png create mode 100644 comp/src/gb.web.form/arrow-right.png create mode 100644 comp/src/gb.web.form/clear.png create mode 100644 comp/src/gb.web.form/lib.js create mode 100644 comp/src/gb.web.form/shadow.png create mode 100644 comp/src/gb.web.form/style.css create mode 100644 comp/src/gb.web/.component create mode 100644 comp/src/gb.web/.directory create mode 100644 comp/src/gb.web/.hidden/Session_opt create mode 100644 comp/src/gb.web/.icon.png create mode 100644 comp/src/gb.web/.project create mode 100644 comp/src/gb.web/.src/Application.module create mode 100644 comp/src/gb.web/.src/CGI.module create mode 100644 comp/src/gb.web/.src/FileSessionManager.class create mode 100644 comp/src/gb.web/.src/Main.module create mode 100644 comp/src/gb.web/.src/Request.module create mode 100644 comp/src/gb.web/.src/Response.module create mode 100644 comp/src/gb.web/.src/Session.module create mode 100644 comp/src/gb.web/.src/SessionManager.class create mode 100644 comp/src/gb.web/.src/SqliteSessionManager.class create mode 100644 comp/src/gb.web/.src/URL.class create mode 100644 comp/src/gb.web/.src/WebPage.class create mode 100644 comp/src/gb.web/.src/Webpage1.class create mode 100644 comp/src/gb.web/.src/Webpage1.webpage create mode 100644 comp/src/gb.web/.src/Webpage2.class create mode 100644 comp/src/gb.web/.src/Webpage2.webpage create mode 100644 comp/src/gb.web/.src/_Request_Get.class create mode 100644 comp/src/gb.web/.src/_Request_Post.class create mode 100644 comp/src/gb.web/.src/_ResponseCache.module create mode 100644 comp/src/order create mode 100644 component.am create mode 100644 configure.ac create mode 100644 gb.cairo/AUTHORS create mode 120000 gb.cairo/COPYING create mode 100644 gb.cairo/ChangeLog create mode 120000 gb.cairo/INSTALL create mode 100644 gb.cairo/Makefile.am create mode 100644 gb.cairo/NEWS create mode 100644 gb.cairo/README create mode 120000 gb.cairo/acinclude.m4 create mode 120000 gb.cairo/component.am create mode 100644 gb.cairo/configure.ac create mode 120000 gb.cairo/gambas.h create mode 120000 gb.cairo/gb.image.h create mode 120000 gb.cairo/gb_common.h create mode 120000 gb.cairo/m4 create mode 120000 gb.cairo/reconf create mode 100644 gb.cairo/src/Makefile.am create mode 100644 gb.cairo/src/c_cairo.c create mode 100644 gb.cairo/src/c_cairo.h create mode 100644 gb.cairo/src/c_surface.c create mode 100644 gb.cairo/src/c_surface.h create mode 100644 gb.cairo/src/gb.cairo.component create mode 100644 gb.cairo/src/main.c create mode 100644 gb.cairo/src/main.h create mode 100644 gb.compress.bzlib2/AUTHORS create mode 120000 gb.compress.bzlib2/COPYING create mode 100644 gb.compress.bzlib2/ChangeLog create mode 120000 gb.compress.bzlib2/INSTALL create mode 100644 gb.compress.bzlib2/Makefile.am create mode 100644 gb.compress.bzlib2/NEWS create mode 100644 gb.compress.bzlib2/README create mode 120000 gb.compress.bzlib2/acinclude.m4 create mode 120000 gb.compress.bzlib2/component.am create mode 100644 gb.compress.bzlib2/configure.ac create mode 120000 gb.compress.bzlib2/gambas.h create mode 120000 gb.compress.bzlib2/gb.compress.h create mode 120000 gb.compress.bzlib2/gb_common.h create mode 120000 gb.compress.bzlib2/m4 create mode 120000 gb.compress.bzlib2/reconf create mode 100644 gb.compress.bzlib2/src/Makefile.am create mode 100644 gb.compress.bzlib2/src/gb.compress.bzlib2.component create mode 100644 gb.compress.bzlib2/src/main.c create mode 100644 gb.compress.bzlib2/src/main.h create mode 100644 gb.compress.zlib/AUTHORS create mode 120000 gb.compress.zlib/COPYING create mode 100644 gb.compress.zlib/ChangeLog create mode 120000 gb.compress.zlib/INSTALL create mode 100644 gb.compress.zlib/Makefile.am create mode 100644 gb.compress.zlib/NEWS create mode 100644 gb.compress.zlib/README create mode 120000 gb.compress.zlib/acinclude.m4 create mode 120000 gb.compress.zlib/component.am create mode 100644 gb.compress.zlib/configure.ac create mode 120000 gb.compress.zlib/gambas.h create mode 120000 gb.compress.zlib/gb.compress.h create mode 120000 gb.compress.zlib/gb_common.h create mode 120000 gb.compress.zlib/m4 create mode 120000 gb.compress.zlib/reconf create mode 100644 gb.compress.zlib/src/Makefile.am create mode 100644 gb.compress.zlib/src/gb.compress.zlib.component create mode 100644 gb.compress.zlib/src/main.c create mode 100644 gb.compress.zlib/src/main.h create mode 100644 gb.crypt/AUTHORS create mode 120000 gb.crypt/COPYING create mode 100644 gb.crypt/ChangeLog create mode 120000 gb.crypt/INSTALL create mode 100644 gb.crypt/Makefile.am create mode 100644 gb.crypt/NEWS create mode 100644 gb.crypt/README create mode 120000 gb.crypt/acinclude.m4 create mode 120000 gb.crypt/component.am create mode 100644 gb.crypt/configure.ac create mode 120000 gb.crypt/gambas.h create mode 120000 gb.crypt/gb_common.h create mode 120000 gb.crypt/m4 create mode 120000 gb.crypt/reconf create mode 100644 gb.crypt/src/Makefile.am create mode 100644 gb.crypt/src/c_crypt.c create mode 100644 gb.crypt/src/c_crypt.h create mode 100644 gb.crypt/src/gb.crypt.component create mode 100644 gb.crypt/src/main.c create mode 100644 gb.crypt/src/main.h create mode 100644 gb.db.mysql/AUTHORS create mode 120000 gb.db.mysql/COPYING create mode 100644 gb.db.mysql/ChangeLog create mode 120000 gb.db.mysql/INSTALL create mode 100644 gb.db.mysql/Makefile.am create mode 100644 gb.db.mysql/NEWS create mode 100644 gb.db.mysql/README create mode 120000 gb.db.mysql/acinclude.m4 create mode 120000 gb.db.mysql/component.am create mode 100644 gb.db.mysql/configure.ac create mode 120000 gb.db.mysql/gambas.h create mode 120000 gb.db.mysql/gb.db.h create mode 120000 gb.db.mysql/gb.db.proto.h create mode 120000 gb.db.mysql/gb_common.h create mode 120000 gb.db.mysql/m4 create mode 120000 gb.db.mysql/reconf create mode 100755 gb.db.mysql/src/Makefile.am create mode 100644 gb.db.mysql/src/gb.db.mysql.component create mode 100644 gb.db.mysql/src/main.c create mode 100644 gb.db.mysql/src/main.h create mode 100644 gb.db.odbc/AUTHORS create mode 120000 gb.db.odbc/COPYING create mode 100644 gb.db.odbc/ChangeLog create mode 120000 gb.db.odbc/INSTALL create mode 100644 gb.db.odbc/Makefile.am create mode 100644 gb.db.odbc/NEWS create mode 100644 gb.db.odbc/README create mode 120000 gb.db.odbc/acinclude.m4 create mode 120000 gb.db.odbc/component.am create mode 100644 gb.db.odbc/configure.ac create mode 120000 gb.db.odbc/gambas.h create mode 120000 gb.db.odbc/gb.db.h create mode 120000 gb.db.odbc/gb.db.proto.h create mode 120000 gb.db.odbc/gb_common.h create mode 120000 gb.db.odbc/m4 create mode 120000 gb.db.odbc/reconf create mode 100644 gb.db.odbc/src/Makefile.am create mode 100644 gb.db.odbc/src/gb.db.odbc.component create mode 100644 gb.db.odbc/src/main.c create mode 100644 gb.db.odbc/src/main.h create mode 100644 gb.db.postgresql/AUTHORS create mode 120000 gb.db.postgresql/COPYING create mode 100644 gb.db.postgresql/ChangeLog create mode 120000 gb.db.postgresql/INSTALL create mode 100644 gb.db.postgresql/Makefile.am create mode 100644 gb.db.postgresql/NEWS create mode 100644 gb.db.postgresql/README create mode 120000 gb.db.postgresql/acinclude.m4 create mode 120000 gb.db.postgresql/component.am create mode 100644 gb.db.postgresql/configure.ac create mode 120000 gb.db.postgresql/gambas.h create mode 120000 gb.db.postgresql/gb.db.h create mode 120000 gb.db.postgresql/gb.db.proto.h create mode 120000 gb.db.postgresql/gb_common.h create mode 120000 gb.db.postgresql/m4 create mode 120000 gb.db.postgresql/reconf create mode 100644 gb.db.postgresql/src/Makefile.am create mode 100644 gb.db.postgresql/src/gb.db.postgresql.component create mode 100644 gb.db.postgresql/src/main.c create mode 100644 gb.db.postgresql/src/main.h create mode 100644 gb.db.sqlite2/AUTHORS create mode 120000 gb.db.sqlite2/COPYING create mode 100644 gb.db.sqlite2/ChangeLog create mode 120000 gb.db.sqlite2/INSTALL create mode 100644 gb.db.sqlite2/Makefile.am create mode 100644 gb.db.sqlite2/NEWS create mode 100644 gb.db.sqlite2/README create mode 100644 gb.db.sqlite2/TODO create mode 120000 gb.db.sqlite2/acinclude.m4 create mode 120000 gb.db.sqlite2/component.am create mode 100644 gb.db.sqlite2/configure.ac create mode 120000 gb.db.sqlite2/gambas.h create mode 120000 gb.db.sqlite2/gb.db.h create mode 120000 gb.db.sqlite2/gb.db.proto.h create mode 120000 gb.db.sqlite2/gb_common.h create mode 120000 gb.db.sqlite2/m4 create mode 120000 gb.db.sqlite2/reconf create mode 100755 gb.db.sqlite2/src/Makefile.am create mode 100644 gb.db.sqlite2/src/dataset.cpp create mode 100644 gb.db.sqlite2/src/dataset.h create mode 100644 gb.db.sqlite2/src/gb.db.sqlite2.component create mode 100644 gb.db.sqlite2/src/main.cpp create mode 100644 gb.db.sqlite2/src/main.h create mode 100644 gb.db.sqlite2/src/qry_dat.cpp create mode 100644 gb.db.sqlite2/src/qry_dat.h create mode 100644 gb.db.sqlite2/src/sqlitedataset.cpp create mode 100644 gb.db.sqlite2/src/sqlitedataset.h create mode 100644 gb.db.sqlite2/src/stringhelper.cpp create mode 100644 gb.db.sqlite2/src/stringhelper.h create mode 100644 gb.db.sqlite3/AUTHORS create mode 120000 gb.db.sqlite3/COPYING create mode 100644 gb.db.sqlite3/ChangeLog create mode 120000 gb.db.sqlite3/INSTALL create mode 100644 gb.db.sqlite3/Makefile.am create mode 100644 gb.db.sqlite3/NEWS create mode 100644 gb.db.sqlite3/README create mode 120000 gb.db.sqlite3/acinclude.m4 create mode 120000 gb.db.sqlite3/component.am create mode 100644 gb.db.sqlite3/configure.ac create mode 120000 gb.db.sqlite3/gambas.h create mode 120000 gb.db.sqlite3/gb.db.h create mode 120000 gb.db.sqlite3/gb.db.proto.h create mode 120000 gb.db.sqlite3/gb_common.h create mode 120000 gb.db.sqlite3/m4 create mode 120000 gb.db.sqlite3/reconf create mode 100755 gb.db.sqlite3/src/Makefile.am create mode 100644 gb.db.sqlite3/src/README create mode 100644 gb.db.sqlite3/src/gb.db.sqlite3.component create mode 100644 gb.db.sqlite3/src/gb_buffer.c create mode 100644 gb.db.sqlite3/src/gb_buffer.h create mode 100644 gb.db.sqlite3/src/helper.c create mode 100644 gb.db.sqlite3/src/helper.h create mode 100644 gb.db.sqlite3/src/main.c create mode 100644 gb.db.sqlite3/src/main.h create mode 100644 gb.dbus/AUTHORS create mode 120000 gb.dbus/COPYING create mode 100644 gb.dbus/ChangeLog create mode 120000 gb.dbus/INSTALL create mode 100644 gb.dbus/Makefile.am create mode 100644 gb.dbus/NEWS create mode 100644 gb.dbus/README create mode 120000 gb.dbus/acinclude.m4 create mode 120000 gb.dbus/component.am create mode 100644 gb.dbus/configure.ac create mode 120000 gb.dbus/gambas.h create mode 120000 gb.dbus/gb_common.h create mode 120000 gb.dbus/m4 create mode 120000 gb.dbus/reconf create mode 100644 gb.dbus/src/Makefile.am create mode 100644 gb.dbus/src/c_dbus.c create mode 100644 gb.dbus/src/c_dbus.h create mode 100644 gb.dbus/src/c_dbusconnection.c create mode 100644 gb.dbus/src/c_dbusconnection.h create mode 100644 gb.dbus/src/c_dbusobserver.c create mode 100644 gb.dbus/src/c_dbusobserver.h create mode 100644 gb.dbus/src/c_dbusvariant.c create mode 100644 gb.dbus/src/c_dbusvariant.h create mode 100644 gb.dbus/src/dbus_print_message.c create mode 100644 gb.dbus/src/dbus_print_message.h create mode 100644 gb.dbus/src/gb.dbus.component create mode 100644 gb.dbus/src/gb.dbus/.directory create mode 100644 gb.dbus/src/gb.dbus/.icon.png create mode 100644 gb.dbus/src/gb.dbus/.project create mode 100644 gb.dbus/src/gb.dbus/.src/CTest.class create mode 100644 gb.dbus/src/gb.dbus/.src/CTest2.class create mode 100644 gb.dbus/src/gb.dbus/.src/CTest3.class create mode 100644 gb.dbus/src/gb.dbus/.src/DBus.class create mode 100644 gb.dbus/src/gb.dbus/.src/DBusApplication.class create mode 100644 gb.dbus/src/gb.dbus/.src/DBusObject.class create mode 100644 gb.dbus/src/gb.dbus/.src/DBusProxy.class create mode 100644 gb.dbus/src/gb.dbus/.src/DBusSignal.class create mode 100644 gb.dbus/src/gb.dbus/.src/DBusValues.class create mode 100644 gb.dbus/src/gb.dbus/.src/MMain.module create mode 100644 gb.dbus/src/gb.dbus/.src/MyObject.class create mode 100644 gb.dbus/src/gb.dbus/.src/MyValue.class create mode 100644 gb.dbus/src/gb.dbus/.src/_DBusNull.class create mode 100644 gb.dbus/src/gb.dbus/.src/mpris.class create mode 100644 gb.dbus/src/helper.c create mode 100644 gb.dbus/src/helper.h create mode 100644 gb.dbus/src/main.c create mode 100644 gb.dbus/src/main.h create mode 100644 gb.desktop.gnome.keyring/AUTHORS create mode 120000 gb.desktop.gnome.keyring/COPYING create mode 100644 gb.desktop.gnome.keyring/ChangeLog create mode 120000 gb.desktop.gnome.keyring/INSTALL create mode 100644 gb.desktop.gnome.keyring/Makefile.am create mode 100644 gb.desktop.gnome.keyring/NEWS create mode 100644 gb.desktop.gnome.keyring/README create mode 120000 gb.desktop.gnome.keyring/acinclude.m4 create mode 120000 gb.desktop.gnome.keyring/component.am create mode 100644 gb.desktop.gnome.keyring/configure.ac create mode 120000 gb.desktop.gnome.keyring/gambas.h create mode 120000 gb.desktop.gnome.keyring/gb_common.h create mode 120000 gb.desktop.gnome.keyring/m4 create mode 120000 gb.desktop.gnome.keyring/reconf create mode 100644 gb.desktop.gnome.keyring/src/Makefile.am create mode 100644 gb.desktop.gnome.keyring/src/gb.desktop.gnome.keyring.component create mode 100644 gb.desktop.gnome.keyring/src/keyring.c create mode 100644 gb.desktop.gnome.keyring/src/keyring.h create mode 100644 gb.desktop.gnome.keyring/src/main.c create mode 100644 gb.desktop.gnome.keyring/src/main.h create mode 100644 gb.desktop.x11/AUTHORS create mode 120000 gb.desktop.x11/COPYING create mode 100644 gb.desktop.x11/ChangeLog create mode 120000 gb.desktop.x11/INSTALL create mode 100644 gb.desktop.x11/Makefile.am create mode 100644 gb.desktop.x11/NEWS create mode 100644 gb.desktop.x11/README create mode 120000 gb.desktop.x11/acinclude.m4 create mode 120000 gb.desktop.x11/component.am create mode 100644 gb.desktop.x11/configure.ac create mode 120000 gb.desktop.x11/gambas.h create mode 120000 gb.desktop.x11/gb.image.h create mode 120000 gb.desktop.x11/gb_common.h create mode 120000 gb.desktop.x11/gb_list.h create mode 120000 gb.desktop.x11/gb_list_temp.h create mode 120000 gb.desktop.x11/m4 create mode 120000 gb.desktop.x11/reconf create mode 100644 gb.desktop.x11/src/Makefile.am create mode 100644 gb.desktop.x11/src/c_x11.c create mode 100644 gb.desktop.x11/src/c_x11.h create mode 100644 gb.desktop.x11/src/c_x11systray.c create mode 100644 gb.desktop.x11/src/c_x11systray.h create mode 100644 gb.desktop.x11/src/gb.desktop.x11.component create mode 100644 gb.desktop.x11/src/gb_list.c create mode 100644 gb.desktop.x11/src/main.c create mode 100644 gb.desktop.x11/src/main.h create mode 100644 gb.desktop.x11/src/systray/common.h create mode 100644 gb.desktop.x11/src/systray/debug.c create mode 100644 gb.desktop.x11/src/systray/debug.h create mode 100644 gb.desktop.x11/src/systray/embed.c create mode 100644 gb.desktop.x11/src/systray/embed.h create mode 100644 gb.desktop.x11/src/systray/icons.c create mode 100644 gb.desktop.x11/src/systray/icons.h create mode 100644 gb.desktop.x11/src/systray/kde_tray.c create mode 100644 gb.desktop.x11/src/systray/kde_tray.h create mode 100644 gb.desktop.x11/src/systray/list.h create mode 100644 gb.desktop.x11/src/systray/settings.c create mode 100644 gb.desktop.x11/src/systray/settings.h create mode 100644 gb.desktop.x11/src/systray/systray.c create mode 100644 gb.desktop.x11/src/systray/systray.h create mode 100644 gb.desktop.x11/src/systray/tray.c create mode 100644 gb.desktop.x11/src/systray/tray.h create mode 100644 gb.desktop.x11/src/systray/wmh.c create mode 100644 gb.desktop.x11/src/systray/wmh.h create mode 100644 gb.desktop.x11/src/systray/xembed.c create mode 100644 gb.desktop.x11/src/systray/xembed.h create mode 100644 gb.desktop.x11/src/systray/xutils.c create mode 100644 gb.desktop.x11/src/systray/xutils.h create mode 100644 gb.desktop.x11/src/x11.c create mode 100644 gb.desktop.x11/src/x11.h create mode 100644 gb.gmp/AUTHORS create mode 120000 gb.gmp/COPYING create mode 100644 gb.gmp/ChangeLog create mode 120000 gb.gmp/INSTALL create mode 100644 gb.gmp/Makefile.am create mode 100644 gb.gmp/NEWS create mode 100644 gb.gmp/README create mode 120000 gb.gmp/acinclude.m4 create mode 120000 gb.gmp/component.am create mode 100644 gb.gmp/configure.ac create mode 120000 gb.gmp/gambas.h create mode 120000 gb.gmp/gb_common.h create mode 120000 gb.gmp/m4 create mode 120000 gb.gmp/reconf create mode 100644 gb.gmp/src/Makefile.am create mode 100644 gb.gmp/src/c_bigint.c create mode 100644 gb.gmp/src/c_bigint.h create mode 100644 gb.gmp/src/c_rational.c create mode 100644 gb.gmp/src/c_rational.h create mode 100644 gb.gmp/src/gb.gmp.component create mode 100644 gb.gmp/src/main.c create mode 100644 gb.gmp/src/main.h create mode 100644 gb.gsl/AUTHORS create mode 120000 gb.gsl/COPYING create mode 100644 gb.gsl/ChangeLog create mode 120000 gb.gsl/INSTALL create mode 100644 gb.gsl/Makefile.am create mode 100644 gb.gsl/NEWS create mode 100644 gb.gsl/README create mode 100644 gb.gsl/Test/test/.directory create mode 100644 gb.gsl/Test/test/.icon.png create mode 100644 gb.gsl/Test/test/.project create mode 100644 gb.gsl/Test/test/.src/MMain.module create mode 100644 gb.gsl/Test/test/.src/Test.class create mode 100644 gb.gsl/Test/test/.src/TestComplex.class create mode 100644 gb.gsl/Test/test/.src/TestSuite.class create mode 120000 gb.gsl/acinclude.m4 create mode 120000 gb.gsl/component.am create mode 100644 gb.gsl/configure.ac create mode 120000 gb.gsl/gambas.h create mode 120000 gb.gsl/gb_common.h create mode 120000 gb.gsl/m4 create mode 120000 gb.gsl/reconf create mode 100644 gb.gsl/src/Makefile.am create mode 100644 gb.gsl/src/c_complex.c create mode 100644 gb.gsl/src/c_complex.h create mode 100644 gb.gsl/src/c_gsl.c create mode 100644 gb.gsl/src/c_gsl.h create mode 100644 gb.gsl/src/c_matrix.c create mode 100644 gb.gsl/src/c_matrix.h create mode 100644 gb.gsl/src/c_newtonpolynomial.c create mode 100644 gb.gsl/src/c_newtonpolynomial.h create mode 100644 gb.gsl/src/c_polynomial.c create mode 100644 gb.gsl/src/c_polynomial.h create mode 100644 gb.gsl/src/c_vector.c create mode 100644 gb.gsl/src/c_vector.h create mode 100644 gb.gsl/src/gb.gsl.component create mode 100644 gb.gsl/src/main.c create mode 100644 gb.gsl/src/main.h create mode 100644 gb.gtk/AUTHORS create mode 120000 gb.gtk/COPYING create mode 100644 gb.gtk/ChangeLog create mode 120000 gb.gtk/INSTALL create mode 100644 gb.gtk/Makefile.am create mode 100644 gb.gtk/NEWS create mode 100644 gb.gtk/README create mode 100644 gb.gtk/TODO create mode 120000 gb.gtk/acinclude.m4 create mode 120000 gb.gtk/component.am create mode 100644 gb.gtk/configure.ac create mode 120000 gb.gtk/gambas.h create mode 120000 gb.gtk/gb.draw.h create mode 120000 gb.gtk/gb.geom.h create mode 120000 gb.gtk/gb.gl.h create mode 120000 gb.gtk/gb.image.h create mode 120000 gb.gtk/gb.paint.h create mode 120000 gb.gtk/gb_common.h create mode 120000 gb.gtk/m4 create mode 120000 gb.gtk/reconf create mode 120000 gb.gtk/share create mode 100644 gb.gtk/src/CButton.cpp create mode 100644 gb.gtk/src/CButton.h create mode 100644 gb.gtk/src/CClipboard.cpp create mode 100644 gb.gtk/src/CClipboard.h create mode 100644 gb.gtk/src/CColor.cpp create mode 100644 gb.gtk/src/CColor.h create mode 100644 gb.gtk/src/CConst.cpp create mode 100644 gb.gtk/src/CConst.h create mode 100644 gb.gtk/src/CContainer.cpp create mode 100644 gb.gtk/src/CContainer.h create mode 100644 gb.gtk/src/CDialog.cpp create mode 100644 gb.gtk/src/CDialog.h create mode 100644 gb.gtk/src/CDraw.cpp create mode 100644 gb.gtk/src/CDraw.h create mode 100644 gb.gtk/src/CDrawingArea.cpp create mode 100644 gb.gtk/src/CDrawingArea.h create mode 100644 gb.gtk/src/CFont.cpp create mode 100644 gb.gtk/src/CFont.h create mode 100644 gb.gtk/src/CFrame.cpp create mode 100644 gb.gtk/src/CFrame.h create mode 100644 gb.gtk/src/CImage.cpp create mode 100644 gb.gtk/src/CImage.h create mode 100644 gb.gtk/src/CKey.cpp create mode 100644 gb.gtk/src/CKey.h create mode 100644 gb.gtk/src/CLabel.cpp create mode 100644 gb.gtk/src/CLabel.h create mode 100644 gb.gtk/src/CMenu.cpp create mode 100644 gb.gtk/src/CMenu.h create mode 100644 gb.gtk/src/CMessage.cpp create mode 100644 gb.gtk/src/CMessage.h create mode 100644 gb.gtk/src/CMouse.cpp create mode 100644 gb.gtk/src/CMouse.h create mode 100644 gb.gtk/src/CMovieBox.cpp create mode 100644 gb.gtk/src/CMovieBox.h create mode 100644 gb.gtk/src/CPicture.cpp create mode 100644 gb.gtk/src/CPicture.h create mode 100644 gb.gtk/src/CScreen.cpp create mode 100644 gb.gtk/src/CScreen.h create mode 100644 gb.gtk/src/CSeparator.cpp create mode 100644 gb.gtk/src/CSeparator.h create mode 100644 gb.gtk/src/CSlider.cpp create mode 100644 gb.gtk/src/CSlider.h create mode 100644 gb.gtk/src/CStock.cpp create mode 100644 gb.gtk/src/CStock.h create mode 100644 gb.gtk/src/CStyle.cpp create mode 100644 gb.gtk/src/CStyle.h create mode 100644 gb.gtk/src/CTabStrip.cpp create mode 100644 gb.gtk/src/CTabStrip.h create mode 100644 gb.gtk/src/CTextArea.cpp create mode 100644 gb.gtk/src/CTextArea.h create mode 100644 gb.gtk/src/CTextBox.cpp create mode 100644 gb.gtk/src/CTextBox.h create mode 100644 gb.gtk/src/CTrayIcon.cpp create mode 100644 gb.gtk/src/CTrayIcon.h create mode 100644 gb.gtk/src/CWatcher.cpp create mode 100644 gb.gtk/src/CWatcher.h create mode 100644 gb.gtk/src/CWidget.cpp create mode 100644 gb.gtk/src/CWidget.h create mode 100644 gb.gtk/src/CWindow.cpp create mode 100644 gb.gtk/src/CWindow.h create mode 100644 gb.gtk/src/Makefile.am create mode 100644 gb.gtk/src/cpaint_impl.cpp create mode 100644 gb.gtk/src/cpaint_impl.h create mode 100644 gb.gtk/src/cprinter.cpp create mode 100644 gb.gtk/src/cprinter.h create mode 100644 gb.gtk/src/csvgimage.cpp create mode 100644 gb.gtk/src/csvgimage.h create mode 120000 gb.gtk/src/desktop.c create mode 120000 gb.gtk/src/desktop.h create mode 100644 gb.gtk/src/font-parser.cpp create mode 100644 gb.gtk/src/font-parser.h create mode 100644 gb.gtk/src/gapplication.cpp create mode 100644 gb.gtk/src/gapplication.h create mode 100644 gb.gtk/src/gb.gtk.component create mode 100644 gb.gtk/src/gb.gtk.h create mode 100644 gb.gtk/src/gbutton.cpp create mode 100644 gb.gtk/src/gbutton.h create mode 100644 gb.gtk/src/gclipboard.h create mode 100644 gb.gtk/src/gcolor.h create mode 100644 gb.gtk/src/gcombobox.cpp create mode 100644 gb.gtk/src/gcombobox.h create mode 100644 gb.gtk/src/gcontainer.cpp create mode 100644 gb.gtk/src/gcontainer.h create mode 100644 gb.gtk/src/gcontrol.cpp create mode 100644 gb.gtk/src/gcontrol.h create mode 100644 gb.gtk/src/gcursor.cpp create mode 100644 gb.gtk/src/gcursor.h create mode 100644 gb.gtk/src/gdesktop.cpp create mode 100644 gb.gtk/src/gdesktop.h create mode 100644 gb.gtk/src/gdialog.h create mode 100644 gb.gtk/src/gdrag.cpp create mode 100644 gb.gtk/src/gdrag.h create mode 100644 gb.gtk/src/gdrawingarea.cpp create mode 100644 gb.gtk/src/gdrawingarea.h create mode 100644 gb.gtk/src/gfont.cpp create mode 100644 gb.gtk/src/gfont.h create mode 100644 gb.gtk/src/gframe.cpp create mode 100644 gb.gtk/src/gframe.h create mode 100644 gb.gtk/src/ggambastag.h create mode 100644 gb.gtk/src/gglarea.cpp create mode 100644 gb.gtk/src/gglarea.h create mode 100644 gb.gtk/src/gkey.cpp create mode 100644 gb.gtk/src/gkey.h create mode 100644 gb.gtk/src/glabel.cpp create mode 100644 gb.gtk/src/glabel.h create mode 100644 gb.gtk/src/gmainwindow.cpp create mode 100644 gb.gtk/src/gmainwindow.h create mode 100644 gb.gtk/src/gmenu.cpp create mode 100644 gb.gtk/src/gmenu.h create mode 100644 gb.gtk/src/gmessage.cpp create mode 100644 gb.gtk/src/gmessage.h create mode 100644 gb.gtk/src/gmouse.cpp create mode 100644 gb.gtk/src/gmouse.h create mode 100644 gb.gtk/src/gmoviebox.cpp create mode 100644 gb.gtk/src/gmoviebox.h create mode 100644 gb.gtk/src/gpicture.cpp create mode 100644 gb.gtk/src/gpicture.h create mode 100644 gb.gtk/src/gplugin.h create mode 100644 gb.gtk/src/gprinter.cpp create mode 100644 gb.gtk/src/gprinter.h create mode 100644 gb.gtk/src/gscrollbar.h create mode 100644 gb.gtk/src/gseparator.cpp create mode 100644 gb.gtk/src/gseparator.h create mode 100644 gb.gtk/src/gshare.h create mode 100644 gb.gtk/src/gsignals.cpp create mode 100644 gb.gtk/src/gslider.cpp create mode 100644 gb.gtk/src/gslider.h create mode 100644 gb.gtk/src/gtabstrip.cpp create mode 100644 gb.gtk/src/gtabstrip.h create mode 100644 gb.gtk/src/gtag.h create mode 100644 gb.gtk/src/gtextarea.cpp create mode 100644 gb.gtk/src/gtextarea.h create mode 100644 gb.gtk/src/gtextbox.cpp create mode 100644 gb.gtk/src/gtextbox.h create mode 100644 gb.gtk/src/gtools.cpp create mode 100644 gb.gtk/src/gtools.h create mode 100644 gb.gtk/src/gtrayicon.cpp create mode 100644 gb.gtk/src/gtrayicon.h create mode 100644 gb.gtk/src/gtree.cpp create mode 100644 gb.gtk/src/gtree.h create mode 100644 gb.gtk/src/kentities.h create mode 100644 gb.gtk/src/main.cpp create mode 100644 gb.gtk/src/main.h create mode 100644 gb.gtk/src/opengl/Makefile.am create mode 100644 gb.gtk/src/opengl/c_glarea.c create mode 100644 gb.gtk/src/opengl/c_glarea.h create mode 100644 gb.gtk/src/opengl/gb.gtk.opengl.component create mode 100644 gb.gtk/src/opengl/main.c create mode 100644 gb.gtk/src/opengl/main.h create mode 100644 gb.gtk/src/sm/bonobo-macros.h create mode 100644 gb.gtk/src/sm/gnome-client.c create mode 100644 gb.gtk/src/sm/gnome-client.h create mode 100644 gb.gtk/src/sm/gnome-ice.c create mode 100644 gb.gtk/src/sm/gnome-ice.h create mode 100644 gb.gtk/src/sm/gnome-macros.h create mode 100644 gb.gtk/src/sm/gnome-marshal.c create mode 100644 gb.gtk/src/sm/gnome-marshal.h create mode 100644 gb.gtk/src/sm/gnome-uidefs.h create mode 100644 gb.gtk/src/sm/gnometypebuiltins.c create mode 100644 gb.gtk/src/sm/gnometypebuiltins.h create mode 100644 gb.gtk/src/sm/libgnomeui.h create mode 100644 gb.gtk/src/sm/libgnomeuiP.h create mode 100644 gb.gtk/src/sm/sm.h create mode 100644 gb.gtk/src/watcher.cpp create mode 100644 gb.gtk/src/watcher.h create mode 100644 gb.gtk/src/widgets.h create mode 120000 gb.gtk/src/x11.c create mode 120000 gb.gtk/src/x11.h create mode 100644 gb.gtk3/AUTHORS create mode 120000 gb.gtk3/COPYING create mode 100644 gb.gtk3/ChangeLog create mode 120000 gb.gtk3/INSTALL create mode 100644 gb.gtk3/Makefile.am create mode 100644 gb.gtk3/NEWS create mode 100644 gb.gtk3/README create mode 100644 gb.gtk3/TODO create mode 120000 gb.gtk3/acinclude.m4 create mode 120000 gb.gtk3/component.am create mode 100644 gb.gtk3/configure.ac create mode 120000 gb.gtk3/gambas.h create mode 120000 gb.gtk3/gb.draw.h create mode 120000 gb.gtk3/gb.geom.h create mode 120000 gb.gtk3/gb.gl.h create mode 120000 gb.gtk3/gb.image.h create mode 120000 gb.gtk3/gb.paint.h create mode 120000 gb.gtk3/gb_common.h create mode 120000 gb.gtk3/m4 create mode 120000 gb.gtk3/reconf create mode 120000 gb.gtk3/share create mode 120000 gb.gtk3/src/CButton.cpp create mode 120000 gb.gtk3/src/CButton.h create mode 120000 gb.gtk3/src/CClipboard.cpp create mode 120000 gb.gtk3/src/CClipboard.h create mode 120000 gb.gtk3/src/CColor.cpp create mode 120000 gb.gtk3/src/CColor.h create mode 120000 gb.gtk3/src/CConst.cpp create mode 120000 gb.gtk3/src/CConst.h create mode 120000 gb.gtk3/src/CContainer.cpp create mode 120000 gb.gtk3/src/CContainer.h create mode 120000 gb.gtk3/src/CDialog.cpp create mode 120000 gb.gtk3/src/CDialog.h create mode 120000 gb.gtk3/src/CDraw.cpp create mode 120000 gb.gtk3/src/CDraw.h create mode 120000 gb.gtk3/src/CDrawingArea.cpp create mode 120000 gb.gtk3/src/CDrawingArea.h create mode 120000 gb.gtk3/src/CFont.cpp create mode 120000 gb.gtk3/src/CFont.h create mode 120000 gb.gtk3/src/CFrame.cpp create mode 120000 gb.gtk3/src/CFrame.h create mode 120000 gb.gtk3/src/CImage.cpp create mode 120000 gb.gtk3/src/CImage.h create mode 120000 gb.gtk3/src/CKey.cpp create mode 120000 gb.gtk3/src/CKey.h create mode 120000 gb.gtk3/src/CLabel.cpp create mode 120000 gb.gtk3/src/CLabel.h create mode 120000 gb.gtk3/src/CMenu.cpp create mode 120000 gb.gtk3/src/CMenu.h create mode 120000 gb.gtk3/src/CMessage.cpp create mode 120000 gb.gtk3/src/CMessage.h create mode 120000 gb.gtk3/src/CMouse.cpp create mode 120000 gb.gtk3/src/CMouse.h create mode 120000 gb.gtk3/src/CMovieBox.cpp create mode 120000 gb.gtk3/src/CMovieBox.h create mode 120000 gb.gtk3/src/CPicture.cpp create mode 120000 gb.gtk3/src/CPicture.h create mode 120000 gb.gtk3/src/CScreen.cpp create mode 120000 gb.gtk3/src/CScreen.h create mode 120000 gb.gtk3/src/CSeparator.cpp create mode 120000 gb.gtk3/src/CSeparator.h create mode 120000 gb.gtk3/src/CSlider.cpp create mode 120000 gb.gtk3/src/CSlider.h create mode 120000 gb.gtk3/src/CStock.cpp create mode 120000 gb.gtk3/src/CStock.h create mode 120000 gb.gtk3/src/CStyle.cpp create mode 120000 gb.gtk3/src/CStyle.h create mode 120000 gb.gtk3/src/CTabStrip.cpp create mode 120000 gb.gtk3/src/CTabStrip.h create mode 120000 gb.gtk3/src/CTextArea.cpp create mode 120000 gb.gtk3/src/CTextArea.h create mode 120000 gb.gtk3/src/CTextBox.cpp create mode 120000 gb.gtk3/src/CTextBox.h create mode 120000 gb.gtk3/src/CTrayIcon.cpp create mode 120000 gb.gtk3/src/CTrayIcon.h create mode 120000 gb.gtk3/src/CWatcher.cpp create mode 120000 gb.gtk3/src/CWatcher.h create mode 120000 gb.gtk3/src/CWidget.cpp create mode 120000 gb.gtk3/src/CWidget.h create mode 120000 gb.gtk3/src/CWindow.cpp create mode 120000 gb.gtk3/src/CWindow.h create mode 100644 gb.gtk3/src/Makefile.am create mode 120000 gb.gtk3/src/cpaint_impl.cpp create mode 120000 gb.gtk3/src/cpaint_impl.h create mode 120000 gb.gtk3/src/cprinter.cpp create mode 120000 gb.gtk3/src/cprinter.h create mode 120000 gb.gtk3/src/csvgimage.cpp create mode 120000 gb.gtk3/src/csvgimage.h create mode 120000 gb.gtk3/src/desktop.c create mode 120000 gb.gtk3/src/desktop.h create mode 120000 gb.gtk3/src/font-parser.cpp create mode 120000 gb.gtk3/src/font-parser.h create mode 120000 gb.gtk3/src/gapplication.cpp create mode 120000 gb.gtk3/src/gapplication.h create mode 120000 gb.gtk3/src/gb.gtk.h create mode 100644 gb.gtk3/src/gb.gtk3.component create mode 120000 gb.gtk3/src/gbutton.cpp create mode 120000 gb.gtk3/src/gbutton.h create mode 120000 gb.gtk3/src/gclipboard.h create mode 120000 gb.gtk3/src/gcolor.h create mode 120000 gb.gtk3/src/gcombobox.cpp create mode 120000 gb.gtk3/src/gcombobox.h create mode 120000 gb.gtk3/src/gcontainer.cpp create mode 120000 gb.gtk3/src/gcontainer.h create mode 120000 gb.gtk3/src/gcontrol.cpp create mode 120000 gb.gtk3/src/gcontrol.h create mode 120000 gb.gtk3/src/gcursor.cpp create mode 120000 gb.gtk3/src/gcursor.h create mode 120000 gb.gtk3/src/gdesktop.cpp create mode 120000 gb.gtk3/src/gdesktop.h create mode 120000 gb.gtk3/src/gdialog.h create mode 120000 gb.gtk3/src/gdrag.cpp create mode 120000 gb.gtk3/src/gdrag.h create mode 120000 gb.gtk3/src/gdrawingarea.cpp create mode 120000 gb.gtk3/src/gdrawingarea.h create mode 120000 gb.gtk3/src/gfont.cpp create mode 120000 gb.gtk3/src/gfont.h create mode 120000 gb.gtk3/src/gframe.cpp create mode 120000 gb.gtk3/src/gframe.h create mode 120000 gb.gtk3/src/ggambastag.h create mode 120000 gb.gtk3/src/gglarea.cpp create mode 120000 gb.gtk3/src/gglarea.h create mode 120000 gb.gtk3/src/gkey.cpp create mode 120000 gb.gtk3/src/gkey.h create mode 120000 gb.gtk3/src/glabel.cpp create mode 120000 gb.gtk3/src/glabel.h create mode 120000 gb.gtk3/src/gmainwindow.cpp create mode 120000 gb.gtk3/src/gmainwindow.h create mode 120000 gb.gtk3/src/gmenu.cpp create mode 120000 gb.gtk3/src/gmenu.h create mode 120000 gb.gtk3/src/gmessage.cpp create mode 120000 gb.gtk3/src/gmessage.h create mode 120000 gb.gtk3/src/gmouse.cpp create mode 120000 gb.gtk3/src/gmouse.h create mode 120000 gb.gtk3/src/gmoviebox.cpp create mode 120000 gb.gtk3/src/gmoviebox.h create mode 120000 gb.gtk3/src/gpicture.cpp create mode 120000 gb.gtk3/src/gpicture.h create mode 120000 gb.gtk3/src/gplugin.h create mode 120000 gb.gtk3/src/gprinter.cpp create mode 120000 gb.gtk3/src/gprinter.h create mode 120000 gb.gtk3/src/gscrollbar.h create mode 120000 gb.gtk3/src/gseparator.cpp create mode 120000 gb.gtk3/src/gseparator.h create mode 120000 gb.gtk3/src/gshare.h create mode 120000 gb.gtk3/src/gsignals.cpp create mode 120000 gb.gtk3/src/gslider.cpp create mode 120000 gb.gtk3/src/gslider.h create mode 120000 gb.gtk3/src/gtabstrip.cpp create mode 120000 gb.gtk3/src/gtabstrip.h create mode 120000 gb.gtk3/src/gtag.h create mode 120000 gb.gtk3/src/gtextarea.cpp create mode 120000 gb.gtk3/src/gtextarea.h create mode 120000 gb.gtk3/src/gtextbox.cpp create mode 120000 gb.gtk3/src/gtextbox.h create mode 120000 gb.gtk3/src/gtools.cpp create mode 120000 gb.gtk3/src/gtools.h create mode 120000 gb.gtk3/src/gtrayicon.cpp create mode 120000 gb.gtk3/src/gtrayicon.h create mode 120000 gb.gtk3/src/gtree.cpp create mode 120000 gb.gtk3/src/gtree.h create mode 120000 gb.gtk3/src/kentities.h create mode 120000 gb.gtk3/src/main.cpp create mode 120000 gb.gtk3/src/main.h create mode 120000 gb.gtk3/src/sm/bonobo-macros.h create mode 120000 gb.gtk3/src/sm/gnome-client.c create mode 120000 gb.gtk3/src/sm/gnome-client.h create mode 120000 gb.gtk3/src/sm/gnome-ice.c create mode 120000 gb.gtk3/src/sm/gnome-ice.h create mode 120000 gb.gtk3/src/sm/gnome-macros.h create mode 120000 gb.gtk3/src/sm/gnome-marshal.c create mode 120000 gb.gtk3/src/sm/gnome-marshal.h create mode 120000 gb.gtk3/src/sm/gnome-uidefs.h create mode 120000 gb.gtk3/src/sm/gnometypebuiltins.c create mode 120000 gb.gtk3/src/sm/gnometypebuiltins.h create mode 120000 gb.gtk3/src/sm/libgnomeui.h create mode 120000 gb.gtk3/src/sm/libgnomeuiP.h create mode 120000 gb.gtk3/src/sm/sm.h create mode 120000 gb.gtk3/src/watcher.cpp create mode 120000 gb.gtk3/src/watcher.h create mode 120000 gb.gtk3/src/widgets.h create mode 120000 gb.gtk3/src/x11.c create mode 120000 gb.gtk3/src/x11.h create mode 100644 gb.httpd/AUTHORS create mode 120000 gb.httpd/COPYING create mode 100644 gb.httpd/ChangeLog create mode 120000 gb.httpd/INSTALL create mode 100644 gb.httpd/Makefile.am create mode 100644 gb.httpd/NEWS create mode 100644 gb.httpd/README create mode 120000 gb.httpd/acinclude.m4 create mode 120000 gb.httpd/component.am create mode 100644 gb.httpd/configure.ac create mode 120000 gb.httpd/gambas.h create mode 120000 gb.httpd/gb_common.h create mode 120000 gb.httpd/m4 create mode 120000 gb.httpd/reconf create mode 100644 gb.httpd/src/Makefile.am create mode 100644 gb.httpd/src/fdwatch.c create mode 100644 gb.httpd/src/fdwatch.h create mode 100644 gb.httpd/src/gb.httpd.component create mode 100644 gb.httpd/src/libhttpd.c create mode 100644 gb.httpd/src/libhttpd.h create mode 100644 gb.httpd/src/main.c create mode 100644 gb.httpd/src/main.h create mode 100644 gb.httpd/src/match.c create mode 100644 gb.httpd/src/match.h create mode 100644 gb.httpd/src/mime_encodings.h create mode 100644 gb.httpd/src/mime_types.h create mode 100644 gb.httpd/src/strerror.c create mode 100644 gb.httpd/src/tdate_parse.c create mode 100644 gb.httpd/src/tdate_parse.h create mode 100644 gb.httpd/src/thttpd.c create mode 100644 gb.httpd/src/thttpd.h create mode 100644 gb.httpd/src/timers.c create mode 100644 gb.httpd/src/timers.h create mode 100644 gb.httpd/src/version.h create mode 100644 gb.image.imlib/AUTHORS create mode 120000 gb.image.imlib/COPYING create mode 100644 gb.image.imlib/ChangeLog create mode 120000 gb.image.imlib/INSTALL create mode 100644 gb.image.imlib/Makefile.am create mode 100644 gb.image.imlib/NEWS create mode 100644 gb.image.imlib/README create mode 120000 gb.image.imlib/acinclude.m4 create mode 120000 gb.image.imlib/component.am create mode 100644 gb.image.imlib/configure.ac create mode 120000 gb.image.imlib/gambas.h create mode 120000 gb.image.imlib/gb.draw.h create mode 120000 gb.image.imlib/gb.image.h create mode 120000 gb.image.imlib/gb_common.h create mode 120000 gb.image.imlib/m4 create mode 120000 gb.image.imlib/reconf create mode 100644 gb.image.imlib/src/Makefile.am create mode 100644 gb.image.imlib/src/c_image.c create mode 100644 gb.image.imlib/src/c_image.h create mode 100644 gb.image.imlib/src/c_imlib.c create mode 100644 gb.image.imlib/src/c_imlib.h create mode 100644 gb.image.imlib/src/gb.image.imlib.component create mode 100644 gb.image.imlib/src/main.c create mode 100644 gb.image.imlib/src/main.h create mode 100644 gb.image.io/AUTHORS create mode 120000 gb.image.io/COPYING create mode 100644 gb.image.io/ChangeLog create mode 120000 gb.image.io/INSTALL create mode 100644 gb.image.io/Makefile.am create mode 100644 gb.image.io/NEWS create mode 100644 gb.image.io/README create mode 120000 gb.image.io/acinclude.m4 create mode 120000 gb.image.io/component.am create mode 100644 gb.image.io/configure.ac create mode 120000 gb.image.io/gambas.h create mode 120000 gb.image.io/gb.image.h create mode 120000 gb.image.io/gb_common.h create mode 120000 gb.image.io/m4 create mode 120000 gb.image.io/reconf create mode 100644 gb.image.io/src/Makefile.am create mode 100644 gb.image.io/src/c_image.c create mode 100644 gb.image.io/src/c_image.h create mode 100644 gb.image.io/src/gb.image.io.component create mode 100644 gb.image.io/src/main.c create mode 100644 gb.image.io/src/main.h create mode 100644 gb.jit.llvm/AUTHORS create mode 120000 gb.jit.llvm/COPYING create mode 100644 gb.jit.llvm/ChangeLog create mode 120000 gb.jit.llvm/INSTALL create mode 100644 gb.jit.llvm/Makefile.am create mode 100644 gb.jit.llvm/NEWS create mode 100644 gb.jit.llvm/README create mode 120000 gb.jit.llvm/acinclude.m4 create mode 120000 gb.jit.llvm/component.am create mode 100644 gb.jit.llvm/configure.ac create mode 120000 gb.jit.llvm/gambas.h create mode 120000 gb.jit.llvm/gb_common.h create mode 120000 gb.jit.llvm/m4 create mode 120000 gb.jit.llvm/reconf create mode 100644 gb.jit.llvm/src/Makefile.am create mode 100644 gb.jit.llvm/src/gb.jit.h create mode 100644 gb.jit.llvm/src/gb.jit.llvm.component create mode 100644 gb.jit.llvm/src/jit.h create mode 100644 gb.jit.llvm/src/jit_api.cpp create mode 100644 gb.jit.llvm/src/jit_codegen.cpp create mode 100644 gb.jit.llvm/src/jit_codegen_conv.h create mode 100644 gb.jit.llvm/src/jit_compile.cpp create mode 100644 gb.jit.llvm/src/jit_conv.cpp create mode 100644 gb.jit.llvm/src/jit_expressions.cpp create mode 100644 gb.jit.llvm/src/jit_gambas_pass.cpp create mode 100644 gb.jit.llvm/src/jit_gambas_pass.h create mode 100644 gb.jit.llvm/src/jit_read.cpp create mode 100644 gb.jit.llvm/src/jit_runtime.c create mode 100644 gb.jit.llvm/src/jit_runtime.h create mode 100644 gb.jit.llvm/src/main.cpp create mode 100644 gb.jit.llvm/src/main.h create mode 100644 gb.libxml/AUTHORS create mode 120000 gb.libxml/COPYING create mode 100644 gb.libxml/ChangeLog create mode 120000 gb.libxml/INSTALL create mode 100644 gb.libxml/Makefile.am create mode 100644 gb.libxml/NEWS create mode 100644 gb.libxml/README create mode 120000 gb.libxml/acinclude.m4 create mode 120000 gb.libxml/component.am create mode 100644 gb.libxml/configure.ac create mode 120000 gb.libxml/gambas.h create mode 120000 gb.libxml/m4 create mode 120000 gb.libxml/reconf create mode 100644 gb.libxml/src/CXMLDocument.c create mode 100644 gb.libxml/src/CXMLDocument.h create mode 100644 gb.libxml/src/CXMLNode.c create mode 100644 gb.libxml/src/CXMLNode.h create mode 100644 gb.libxml/src/CXMLReader.c create mode 100644 gb.libxml/src/CXMLReader.h create mode 100644 gb.libxml/src/CXMLWriter.c create mode 100644 gb.libxml/src/CXMLWriter.h create mode 100644 gb.libxml/src/Makefile.am create mode 100644 gb.libxml/src/gb.libxml.component create mode 100644 gb.libxml/src/libxml.kateproject create mode 100644 gb.libxml/src/main.c create mode 100644 gb.libxml/src/main.h create mode 100644 gb.media/AUTHORS create mode 120000 gb.media/COPYING create mode 100644 gb.media/ChangeLog create mode 120000 gb.media/INSTALL create mode 100644 gb.media/Makefile.am create mode 100644 gb.media/NEWS create mode 100644 gb.media/README create mode 120000 gb.media/acinclude.m4 create mode 120000 gb.media/component.am create mode 100644 gb.media/configure.ac create mode 120000 gb.media/gambas.h create mode 120000 gb.media/gb.image.h create mode 120000 gb.media/gb_common.h create mode 120000 gb.media/m4 create mode 120000 gb.media/reconf create mode 100644 gb.media/src/Makefile.am create mode 100644 gb.media/src/c_media.c create mode 100644 gb.media/src/c_media.h create mode 100644 gb.media/src/c_mediaplayer.c create mode 100644 gb.media/src/c_mediaplayer.h create mode 100644 gb.media/src/c_mediavideo.c create mode 100644 gb.media/src/c_mediavideo.h create mode 100644 gb.media/src/gb.media.component create mode 100644 gb.media/src/main.c create mode 100644 gb.media/src/main.h create mode 100644 gb.mime/AUTHORS create mode 120000 gb.mime/COPYING create mode 100644 gb.mime/ChangeLog create mode 120000 gb.mime/INSTALL create mode 100644 gb.mime/Makefile.am create mode 100644 gb.mime/NEWS create mode 100644 gb.mime/README create mode 120000 gb.mime/acinclude.m4 create mode 120000 gb.mime/component.am create mode 100644 gb.mime/configure.ac create mode 120000 gb.mime/gambas.h create mode 120000 gb.mime/gb_common.h create mode 120000 gb.mime/m4 create mode 120000 gb.mime/reconf create mode 100644 gb.mime/src/Makefile.am create mode 100644 gb.mime/src/c_mime.c create mode 100644 gb.mime/src/c_mime.h create mode 100644 gb.mime/src/c_mimemessage.c create mode 100644 gb.mime/src/c_mimemessage.h create mode 100644 gb.mime/src/c_mimepart.c create mode 100644 gb.mime/src/c_mimepart.h create mode 100644 gb.mime/src/gb.mime.component create mode 100644 gb.mime/src/main.c create mode 100644 gb.mime/src/main.h create mode 100644 gb.ncurses/AUTHORS create mode 120000 gb.ncurses/COPYING create mode 100644 gb.ncurses/ChangeLog create mode 120000 gb.ncurses/INSTALL create mode 100644 gb.ncurses/Makefile.am create mode 100644 gb.ncurses/NEWS create mode 100644 gb.ncurses/README create mode 120000 gb.ncurses/acinclude.m4 create mode 120000 gb.ncurses/component.am create mode 100644 gb.ncurses/configure.ac create mode 120000 gb.ncurses/gambas.h create mode 120000 gb.ncurses/gb_common.h create mode 120000 gb.ncurses/m4 create mode 120000 gb.ncurses/reconf create mode 100644 gb.ncurses/src/Makefile.am create mode 100644 gb.ncurses/src/c_color.c create mode 100644 gb.ncurses/src/c_color.h create mode 100644 gb.ncurses/src/c_input.c create mode 100644 gb.ncurses/src/c_input.h create mode 100644 gb.ncurses/src/c_key.c create mode 100644 gb.ncurses/src/c_key.h create mode 100644 gb.ncurses/src/c_screen.c create mode 100644 gb.ncurses/src/c_screen.h create mode 100644 gb.ncurses/src/c_window.c create mode 100644 gb.ncurses/src/c_window.h create mode 100644 gb.ncurses/src/gb.ncurses.component create mode 100644 gb.ncurses/src/main.c create mode 100644 gb.ncurses/src/main.h create mode 100644 gb.net.curl/AUTHORS create mode 120000 gb.net.curl/COPYING create mode 100644 gb.net.curl/ChangeLog create mode 120000 gb.net.curl/INSTALL create mode 100644 gb.net.curl/Makefile.am create mode 100644 gb.net.curl/NEWS create mode 100644 gb.net.curl/README create mode 120000 gb.net.curl/acinclude.m4 create mode 120000 gb.net.curl/component.am create mode 100644 gb.net.curl/configure.ac create mode 120000 gb.net.curl/gambas.h create mode 120000 gb.net.curl/gb_common.h create mode 120000 gb.net.curl/m4 create mode 120000 gb.net.curl/reconf create mode 100644 gb.net.curl/src/CCurl.c create mode 100644 gb.net.curl/src/CCurl.h create mode 100644 gb.net.curl/src/CFtpClient.c create mode 100644 gb.net.curl/src/CFtpClient.h create mode 100644 gb.net.curl/src/CHttpClient.c create mode 100644 gb.net.curl/src/CHttpClient.h create mode 100644 gb.net.curl/src/CNet.c create mode 100644 gb.net.curl/src/CNet.h create mode 100644 gb.net.curl/src/CProxy.c create mode 100644 gb.net.curl/src/CProxy.h create mode 100644 gb.net.curl/src/Makefile.am create mode 100644 gb.net.curl/src/gb.net.curl.component create mode 100644 gb.net.curl/src/gb.net.curl/.directory create mode 100644 gb.net.curl/src/gb.net.curl/.icon.png create mode 100644 gb.net.curl/src/gb.net.curl/.project create mode 100644 gb.net.curl/src/gb.net.curl/.src/Download.class create mode 100644 gb.net.curl/src/gb.net.curl/.src/DownloadManager.class create mode 100644 gb.net.curl/src/gb.net.curl/.src/HttpForm.class create mode 100644 gb.net.curl/src/gb.net.curl/.src/MMain.module create mode 100644 gb.net.curl/src/gbcurl.c create mode 100644 gb.net.curl/src/gbcurl.h create mode 100644 gb.net.curl/src/main.c create mode 100644 gb.net.curl/src/main.h create mode 100644 gb.net/AUTHORS create mode 120000 gb.net/COPYING create mode 100644 gb.net/ChangeLog create mode 120000 gb.net/INSTALL create mode 100644 gb.net/Makefile.am create mode 100644 gb.net/NEWS create mode 100644 gb.net/README create mode 120000 gb.net/acinclude.m4 create mode 120000 gb.net/component.am create mode 100644 gb.net/configure.ac create mode 120000 gb.net/gambas.h create mode 120000 gb.net/gb_common.h create mode 120000 gb.net/m4 create mode 120000 gb.net/reconf create mode 100644 gb.net/src/CDnsClient.c create mode 100644 gb.net/src/CDnsClient.h create mode 100644 gb.net/src/CNet.c create mode 100644 gb.net/src/CNet.h create mode 100644 gb.net/src/CSerialPort.c create mode 100644 gb.net/src/CSerialPort.h create mode 100644 gb.net/src/CServerSocket.c create mode 100644 gb.net/src/CServerSocket.h create mode 100644 gb.net/src/CSocket.c create mode 100644 gb.net/src/CSocket.h create mode 100644 gb.net/src/CUdpSocket.c create mode 100644 gb.net/src/CUdpSocket.h create mode 100644 gb.net/src/Makefile.am create mode 100644 gb.net/src/doc/README create mode 100644 gb.net/src/doc/changes.txt create mode 100644 gb.net/src/doc/threading.sxw create mode 100644 gb.net/src/gb.net.component create mode 100644 gb.net/src/gb_network.h create mode 100644 gb.net/src/main.c create mode 100644 gb.net/src/main.h create mode 100644 gb.net/src/tools.c create mode 100644 gb.net/src/tools.h create mode 100644 gb.openal/AUTHORS create mode 120000 gb.openal/COPYING create mode 100644 gb.openal/ChangeLog create mode 120000 gb.openal/INSTALL create mode 100644 gb.openal/Makefile.am create mode 100644 gb.openal/NEWS create mode 100644 gb.openal/README create mode 120000 gb.openal/acinclude.m4 create mode 120000 gb.openal/component.am create mode 100644 gb.openal/configure.ac create mode 120000 gb.openal/gambas.h create mode 120000 gb.openal/gb_common.h create mode 120000 gb.openal/m4 create mode 120000 gb.openal/reconf create mode 100644 gb.openal/src/Makefile.am create mode 100644 gb.openal/src/c_al.c create mode 100644 gb.openal/src/c_al.h create mode 100644 gb.openal/src/c_alc.c create mode 100644 gb.openal/src/c_alc.h create mode 100644 gb.openal/src/c_alure.c create mode 100644 gb.openal/src/c_alure.h create mode 100644 gb.openal/src/gb.openal.component create mode 100644 gb.openal/src/main.c create mode 100644 gb.openal/src/main.h create mode 100644 gb.opengl/AUTHORS create mode 120000 gb.opengl/COPYING create mode 100644 gb.opengl/ChangeLog create mode 120000 gb.opengl/INSTALL create mode 100644 gb.opengl/Makefile.am create mode 100644 gb.opengl/NEWS create mode 100644 gb.opengl/README create mode 120000 gb.opengl/acinclude.m4 create mode 120000 gb.opengl/component.am create mode 100644 gb.opengl/configure.ac create mode 120000 gb.opengl/gambas.h create mode 120000 gb.opengl/gb.image.h create mode 120000 gb.opengl/gb_common.h create mode 120000 gb.opengl/m4 create mode 120000 gb.opengl/reconf create mode 100644 gb.opengl/src/GL.c create mode 100644 gb.opengl/src/GL.h create mode 100644 gb.opengl/src/GLclipping.c create mode 100644 gb.opengl/src/GLclipping.h create mode 100644 gb.opengl/src/GLcolorLighting.c create mode 100644 gb.opengl/src/GLcolorLighting.h create mode 100644 gb.opengl/src/GLcoordTransf.c create mode 100644 gb.opengl/src/GLcoordTransf.h create mode 100644 gb.opengl/src/GLdisplayList.c create mode 100644 gb.opengl/src/GLdisplayList.h create mode 100644 gb.opengl/src/GLeval.c create mode 100644 gb.opengl/src/GLeval.h create mode 100644 gb.opengl/src/GLfog.c create mode 100644 gb.opengl/src/GLfog.h create mode 100644 gb.opengl/src/GLframeBufferOps.c create mode 100644 gb.opengl/src/GLframeBufferOps.h create mode 100644 gb.opengl/src/GLinfo.c create mode 100644 gb.opengl/src/GLinfo.h create mode 100644 gb.opengl/src/GLmodesExec.c create mode 100644 gb.opengl/src/GLmodesExec.h create mode 100644 gb.opengl/src/GLpixelOperations.c create mode 100644 gb.opengl/src/GLpixelOperations.h create mode 100644 gb.opengl/src/GLprimitives.c create mode 100644 gb.opengl/src/GLprimitives.h create mode 100644 gb.opengl/src/GLrasterization.c create mode 100644 gb.opengl/src/GLrasterization.h create mode 100644 gb.opengl/src/GLselectFeedback.c create mode 100644 gb.opengl/src/GLselectFeedback.h create mode 100644 gb.opengl/src/GLtextureMapping.c create mode 100644 gb.opengl/src/GLtextureMapping.h create mode 100644 gb.opengl/src/Makefile.am create mode 100644 gb.opengl/src/framebufferobject.c create mode 100644 gb.opengl/src/framebufferobject.h create mode 100644 gb.opengl/src/gb.gl.h create mode 100644 gb.opengl/src/gb.opengl.component create mode 100644 gb.opengl/src/glsl/GL.c create mode 100644 gb.opengl/src/glsl/GL.h create mode 100644 gb.opengl/src/glsl/GLattributes.c create mode 100644 gb.opengl/src/glsl/GLattributes.h create mode 100644 gb.opengl/src/glsl/GLinfo.h create mode 100644 gb.opengl/src/glsl/GLprogram.c create mode 100644 gb.opengl/src/glsl/GLprogram.h create mode 100644 gb.opengl/src/glsl/GLshader.c create mode 100644 gb.opengl/src/glsl/GLshader.h create mode 100644 gb.opengl/src/glsl/GLuniform.c create mode 100644 gb.opengl/src/glsl/GLuniform.h create mode 100644 gb.opengl/src/glsl/Makefile.am create mode 100644 gb.opengl/src/glsl/gb.opengl.glsl.component create mode 100644 gb.opengl/src/glsl/main.c create mode 100644 gb.opengl/src/glsl/main.h create mode 100644 gb.opengl/src/glu/GLU.c create mode 100644 gb.opengl/src/glu/GLU.h create mode 100644 gb.opengl/src/glu/GLUcoordTransf.c create mode 100644 gb.opengl/src/glu/GLUcoordTransf.h create mode 100644 gb.opengl/src/glu/GLUnurb.c create mode 100644 gb.opengl/src/glu/GLUnurb.h create mode 100644 gb.opengl/src/glu/GLUproject.c create mode 100644 gb.opengl/src/glu/GLUproject.h create mode 100644 gb.opengl/src/glu/GLUquadratic.c create mode 100644 gb.opengl/src/glu/GLUquadratic.h create mode 100644 gb.opengl/src/glu/GLUtextureImage.c create mode 100644 gb.opengl/src/glu/GLUtextureImage.h create mode 100644 gb.opengl/src/glu/Makefile.am create mode 100644 gb.opengl/src/glu/cglunurb.c create mode 100644 gb.opengl/src/glu/cglunurb.h create mode 100644 gb.opengl/src/glu/cgluquadric.c create mode 100644 gb.opengl/src/glu/cgluquadric.h create mode 100644 gb.opengl/src/glu/gb.opengl.glu.component create mode 100644 gb.opengl/src/glu/main.c create mode 100644 gb.opengl/src/glu/main.h create mode 100644 gb.opengl/src/main.c create mode 100644 gb.opengl/src/main.h create mode 100644 gb.opengl/src/sge/Makefile.am create mode 100644 gb.opengl/src/sge/cmd2model.c create mode 100644 gb.opengl/src/sge/cmd2model.h create mode 100644 gb.opengl/src/sge/cmd2object.c create mode 100644 gb.opengl/src/sge/cmd2object.h create mode 100644 gb.opengl/src/sge/gb.opengl.sge.component create mode 100644 gb.opengl/src/sge/main.c create mode 100644 gb.opengl/src/sge/main.h create mode 100644 gb.openssl/AUTHORS create mode 120000 gb.openssl/COPYING create mode 100644 gb.openssl/ChangeLog create mode 120000 gb.openssl/INSTALL create mode 100644 gb.openssl/Makefile.am create mode 100644 gb.openssl/NEWS create mode 100644 gb.openssl/README create mode 120000 gb.openssl/acinclude.m4 create mode 120000 gb.openssl/component.am create mode 100644 gb.openssl/configure.ac create mode 120000 gb.openssl/gambas.h create mode 120000 gb.openssl/gb_common.h create mode 120000 gb.openssl/m4 create mode 120000 gb.openssl/reconf create mode 100644 gb.openssl/src/Makefile.am create mode 100644 gb.openssl/src/c_cipher.c create mode 100644 gb.openssl/src/c_cipher.h create mode 100644 gb.openssl/src/c_digest.c create mode 100644 gb.openssl/src/c_digest.h create mode 100644 gb.openssl/src/c_hmac.c create mode 100644 gb.openssl/src/c_hmac.h create mode 100644 gb.openssl/src/gb.openssl.component create mode 100644 gb.openssl/src/main.c create mode 100644 gb.openssl/src/main.h create mode 100644 gb.pcre/AUTHORS create mode 120000 gb.pcre/COPYING create mode 100644 gb.pcre/ChangeLog create mode 120000 gb.pcre/INSTALL create mode 100644 gb.pcre/Makefile.am create mode 100644 gb.pcre/NEWS create mode 100644 gb.pcre/README create mode 120000 gb.pcre/acinclude.m4 create mode 120000 gb.pcre/component.am create mode 100644 gb.pcre/configure.ac create mode 120000 gb.pcre/gambas.h create mode 120000 gb.pcre/gb_common.h create mode 120000 gb.pcre/m4 create mode 120000 gb.pcre/reconf create mode 100644 gb.pcre/src/Makefile.am create mode 100644 gb.pcre/src/README create mode 100644 gb.pcre/src/gb.pcre.component create mode 100644 gb.pcre/src/gb.pcre.h create mode 100644 gb.pcre/src/main.c create mode 100644 gb.pcre/src/main.h create mode 100644 gb.pcre/src/regexp.c create mode 100644 gb.pcre/src/regexp.h create mode 100644 gb.pdf/AUTHORS create mode 120000 gb.pdf/COPYING create mode 100644 gb.pdf/ChangeLog create mode 120000 gb.pdf/INSTALL create mode 100644 gb.pdf/Makefile.am create mode 100644 gb.pdf/NEWS create mode 100644 gb.pdf/README create mode 120000 gb.pdf/acinclude.m4 create mode 120000 gb.pdf/component.am create mode 100644 gb.pdf/configure.ac create mode 120000 gb.pdf/gambas.h create mode 120000 gb.pdf/gb.gtk.h create mode 120000 gb.pdf/gb.image.h create mode 120000 gb.pdf/gb_common.h create mode 120000 gb.pdf/m4 create mode 120000 gb.pdf/reconf create mode 100644 gb.pdf/src/CPdfDocument.cpp create mode 100644 gb.pdf/src/CPdfDocument.h create mode 100644 gb.pdf/src/Makefile.am create mode 100644 gb.pdf/src/gb.pdf.component create mode 100644 gb.pdf/src/main.cpp create mode 100644 gb.pdf/src/main.h create mode 100644 gb.qt4/AUTHORS create mode 120000 gb.qt4/COPYING create mode 100644 gb.qt4/ChangeLog create mode 120000 gb.qt4/INSTALL create mode 100644 gb.qt4/Makefile.am create mode 100644 gb.qt4/NEWS create mode 100644 gb.qt4/README create mode 120000 gb.qt4/acinclude.m4 create mode 120000 gb.qt4/component.am create mode 100644 gb.qt4/configure.ac create mode 120000 gb.qt4/gambas.h create mode 120000 gb.qt4/gb.draw.h create mode 120000 gb.qt4/gb.eval.h create mode 120000 gb.qt4/gb.geom.h create mode 120000 gb.qt4/gb.gl.h create mode 120000 gb.qt4/gb.image.h create mode 120000 gb.qt4/gb.paint.h create mode 100644 gb.qt4/gb.qt.am create mode 120000 gb.qt4/gb_common.h create mode 120000 gb.qt4/gbc_read_common.h create mode 120000 gb.qt4/m4 create mode 120000 gb.qt4/reconf create mode 100644 gb.qt4/share/gb.form.action.h create mode 100644 gb.qt4/share/gb.form.arrangement.h create mode 100644 gb.qt4/share/gb.form.const.h create mode 100644 gb.qt4/share/gb.form.font.h create mode 100644 gb.qt4/share/gb.form.print.h create mode 100644 gb.qt4/share/gb.form.properties.h create mode 100644 gb.qt4/share/gb.form.trayicon.h create mode 100644 gb.qt4/share/gb.form.trayicon.large.h create mode 100644 gb.qt4/src/CButton.cpp create mode 100644 gb.qt4/src/CButton.h create mode 100644 gb.qt4/src/CCheckBox.cpp create mode 100644 gb.qt4/src/CCheckBox.h create mode 100644 gb.qt4/src/CClipboard.cpp create mode 100644 gb.qt4/src/CClipboard.h create mode 100644 gb.qt4/src/CColor.cpp create mode 100644 gb.qt4/src/CColor.h create mode 100644 gb.qt4/src/CConst.cpp create mode 100644 gb.qt4/src/CConst.h create mode 100755 gb.qt4/src/CContainer.cpp create mode 100644 gb.qt4/src/CContainer.h create mode 100644 gb.qt4/src/CDialog.cpp create mode 100644 gb.qt4/src/CDialog.h create mode 100644 gb.qt4/src/CDraw.cpp create mode 100644 gb.qt4/src/CDraw.h create mode 100644 gb.qt4/src/CDrawingArea.cpp create mode 100644 gb.qt4/src/CDrawingArea.h create mode 100644 gb.qt4/src/CEmbedder.cpp create mode 100644 gb.qt4/src/CEmbedder.h create mode 100644 gb.qt4/src/CFont.cpp create mode 100644 gb.qt4/src/CFont.h create mode 100644 gb.qt4/src/CFrame.cpp create mode 100644 gb.qt4/src/CFrame.h create mode 100644 gb.qt4/src/CImage.cpp create mode 100644 gb.qt4/src/CImage.h create mode 100644 gb.qt4/src/CKey.cpp create mode 100644 gb.qt4/src/CKey.h create mode 100644 gb.qt4/src/CLabel.cpp create mode 100644 gb.qt4/src/CLabel.h create mode 100644 gb.qt4/src/CMenu.cpp create mode 100644 gb.qt4/src/CMenu.h create mode 100644 gb.qt4/src/CMessage.cpp create mode 100644 gb.qt4/src/CMessage.h create mode 100644 gb.qt4/src/CMouse.cpp create mode 100644 gb.qt4/src/CMouse.h create mode 100644 gb.qt4/src/CMovieBox.cpp create mode 100644 gb.qt4/src/CMovieBox.h create mode 100644 gb.qt4/src/CPanel.cpp create mode 100644 gb.qt4/src/CPanel.h create mode 100644 gb.qt4/src/CPicture.cpp create mode 100644 gb.qt4/src/CPicture.h create mode 100644 gb.qt4/src/CRadioButton.cpp create mode 100644 gb.qt4/src/CRadioButton.h create mode 100644 gb.qt4/src/CScreen.cpp create mode 100644 gb.qt4/src/CScreen.h create mode 100644 gb.qt4/src/CScrollBar.cpp create mode 100644 gb.qt4/src/CScrollBar.h create mode 100644 gb.qt4/src/CSlider.cpp create mode 100644 gb.qt4/src/CSlider.h create mode 100644 gb.qt4/src/CStyle.cpp create mode 100644 gb.qt4/src/CStyle.h create mode 100644 gb.qt4/src/CTabStrip.cpp create mode 100644 gb.qt4/src/CTabStrip.h create mode 100644 gb.qt4/src/CTextArea.cpp create mode 100644 gb.qt4/src/CTextArea.h create mode 100644 gb.qt4/src/CTextBox.cpp create mode 100644 gb.qt4/src/CTextBox.h create mode 100644 gb.qt4/src/CWatch.cpp create mode 100644 gb.qt4/src/CWatch.h create mode 100644 gb.qt4/src/CWatcher.cpp create mode 100644 gb.qt4/src/CWatcher.h create mode 100644 gb.qt4/src/CWidget.cpp create mode 100644 gb.qt4/src/CWidget.h create mode 100644 gb.qt4/src/CWindow.cpp create mode 100644 gb.qt4/src/CWindow.h create mode 100644 gb.qt4/src/Makefile.am create mode 100644 gb.qt4/src/cpaint_impl.cpp create mode 100644 gb.qt4/src/cpaint_impl.h create mode 100644 gb.qt4/src/cprinter.cpp create mode 100644 gb.qt4/src/cprinter.h create mode 100644 gb.qt4/src/csvgimage.cpp create mode 100644 gb.qt4/src/csvgimage.h create mode 100644 gb.qt4/src/ctrayicon.cpp create mode 100644 gb.qt4/src/ctrayicon.h create mode 100644 gb.qt4/src/desktop.c create mode 100644 gb.qt4/src/desktop.h create mode 100644 gb.qt4/src/ext/CDial.cpp create mode 100644 gb.qt4/src/ext/CDial.h create mode 100644 gb.qt4/src/ext/CEditor.cpp create mode 100644 gb.qt4/src/ext/CEditor.h create mode 100644 gb.qt4/src/ext/CLCDNumber.cpp create mode 100644 gb.qt4/src/ext/CLCDNumber.h create mode 100644 gb.qt4/src/ext/CTextEdit.cpp create mode 100644 gb.qt4/src/ext/CTextEdit.h create mode 100644 gb.qt4/src/ext/Makefile.am create mode 100644 gb.qt4/src/ext/garray.cpp create mode 100644 gb.qt4/src/ext/garray.h create mode 100644 gb.qt4/src/ext/gb.qt4.ext.component create mode 100644 gb.qt4/src/ext/gdocument.cpp create mode 100644 gb.qt4/src/ext/gdocument.h create mode 100644 gb.qt4/src/ext/gstring.cpp create mode 100644 gb.qt4/src/ext/gstring.h create mode 100644 gb.qt4/src/ext/gview.cpp create mode 100644 gb.qt4/src/ext/gview.h create mode 100644 gb.qt4/src/ext/main.cpp create mode 100644 gb.qt4/src/ext/main.h create mode 100644 gb.qt4/src/fix_breeze.cpp create mode 100644 gb.qt4/src/fix_breeze.h create mode 100644 gb.qt4/src/gb.qt.h create mode 100644 gb.qt4/src/gb.qt4.component create mode 100644 gb.qt4/src/main.cpp create mode 100644 gb.qt4/src/main.h create mode 100644 gb.qt4/src/opengl/CGLarea.cpp create mode 100644 gb.qt4/src/opengl/CGLarea.h create mode 100644 gb.qt4/src/opengl/Makefile.am create mode 100644 gb.qt4/src/opengl/gb.qt4.opengl.component create mode 100644 gb.qt4/src/opengl/main.cpp create mode 100644 gb.qt4/src/opengl/main.h create mode 100644 gb.qt4/src/trayicon.xpm create mode 100644 gb.qt4/src/webkit/Makefile.am create mode 100644 gb.qt4/src/webkit/ccookiejar.cpp create mode 100644 gb.qt4/src/webkit/ccookiejar.h create mode 100644 gb.qt4/src/webkit/control/webview.png create mode 100644 gb.qt4/src/webkit/cwebdownload.cpp create mode 100644 gb.qt4/src/webkit/cwebdownload.h create mode 100644 gb.qt4/src/webkit/cwebelement.cpp create mode 100644 gb.qt4/src/webkit/cwebelement.h create mode 100644 gb.qt4/src/webkit/cwebframe.cpp create mode 100644 gb.qt4/src/webkit/cwebframe.h create mode 100644 gb.qt4/src/webkit/cwebhittest.cpp create mode 100644 gb.qt4/src/webkit/cwebhittest.h create mode 100644 gb.qt4/src/webkit/cwebsettings.cpp create mode 100644 gb.qt4/src/webkit/cwebsettings.h create mode 100644 gb.qt4/src/webkit/cwebview.cpp create mode 100644 gb.qt4/src/webkit/cwebview.h create mode 100644 gb.qt4/src/webkit/gb.qt4.webkit.component create mode 100644 gb.qt4/src/webkit/main.cpp create mode 100644 gb.qt4/src/webkit/main.h create mode 100644 gb.qt4/src/x11.c create mode 100644 gb.qt4/src/x11.h create mode 100644 gb.qt5/AUTHORS create mode 120000 gb.qt5/COPYING create mode 100644 gb.qt5/ChangeLog create mode 120000 gb.qt5/INSTALL create mode 100644 gb.qt5/Makefile.am create mode 100644 gb.qt5/NEWS create mode 100644 gb.qt5/README create mode 120000 gb.qt5/acinclude.m4 create mode 120000 gb.qt5/component.am create mode 100644 gb.qt5/configure.ac create mode 120000 gb.qt5/gambas.h create mode 120000 gb.qt5/gb.draw.h create mode 120000 gb.qt5/gb.eval.h create mode 120000 gb.qt5/gb.geom.h create mode 120000 gb.qt5/gb.gl.h create mode 120000 gb.qt5/gb.image.h create mode 120000 gb.qt5/gb.paint.h create mode 100644 gb.qt5/gb.qt.am create mode 120000 gb.qt5/gb_common.h create mode 120000 gb.qt5/m4 create mode 120000 gb.qt5/reconf create mode 120000 gb.qt5/share create mode 120000 gb.qt5/src/CButton.cpp create mode 120000 gb.qt5/src/CButton.h create mode 120000 gb.qt5/src/CCheckBox.cpp create mode 120000 gb.qt5/src/CCheckBox.h create mode 120000 gb.qt5/src/CClipboard.cpp create mode 120000 gb.qt5/src/CClipboard.h create mode 120000 gb.qt5/src/CColor.cpp create mode 120000 gb.qt5/src/CColor.h create mode 120000 gb.qt5/src/CConst.cpp create mode 120000 gb.qt5/src/CConst.h create mode 120000 gb.qt5/src/CContainer.cpp create mode 120000 gb.qt5/src/CContainer.h create mode 120000 gb.qt5/src/CDialog.cpp create mode 120000 gb.qt5/src/CDialog.h create mode 120000 gb.qt5/src/CDraw.cpp create mode 120000 gb.qt5/src/CDraw.h create mode 120000 gb.qt5/src/CDrawingArea.cpp create mode 120000 gb.qt5/src/CDrawingArea.h create mode 120000 gb.qt5/src/CEmbedder.cpp create mode 120000 gb.qt5/src/CEmbedder.h create mode 120000 gb.qt5/src/CFont.cpp create mode 120000 gb.qt5/src/CFont.h create mode 120000 gb.qt5/src/CFrame.cpp create mode 120000 gb.qt5/src/CFrame.h create mode 120000 gb.qt5/src/CImage.cpp create mode 120000 gb.qt5/src/CImage.h create mode 120000 gb.qt5/src/CKey.cpp create mode 120000 gb.qt5/src/CKey.h create mode 120000 gb.qt5/src/CLabel.cpp create mode 120000 gb.qt5/src/CLabel.h create mode 120000 gb.qt5/src/CMenu.cpp create mode 120000 gb.qt5/src/CMenu.h create mode 120000 gb.qt5/src/CMessage.cpp create mode 120000 gb.qt5/src/CMessage.h create mode 120000 gb.qt5/src/CMouse.cpp create mode 120000 gb.qt5/src/CMouse.h create mode 120000 gb.qt5/src/CMovieBox.cpp create mode 120000 gb.qt5/src/CMovieBox.h create mode 120000 gb.qt5/src/CPanel.cpp create mode 120000 gb.qt5/src/CPanel.h create mode 120000 gb.qt5/src/CPicture.cpp create mode 120000 gb.qt5/src/CPicture.h create mode 120000 gb.qt5/src/CRadioButton.cpp create mode 120000 gb.qt5/src/CRadioButton.h create mode 120000 gb.qt5/src/CScreen.cpp create mode 120000 gb.qt5/src/CScreen.h create mode 120000 gb.qt5/src/CScrollBar.cpp create mode 120000 gb.qt5/src/CScrollBar.h create mode 120000 gb.qt5/src/CSlider.cpp create mode 120000 gb.qt5/src/CSlider.h create mode 120000 gb.qt5/src/CStyle.cpp create mode 120000 gb.qt5/src/CStyle.h create mode 120000 gb.qt5/src/CTabStrip.cpp create mode 120000 gb.qt5/src/CTabStrip.h create mode 120000 gb.qt5/src/CTextArea.cpp create mode 120000 gb.qt5/src/CTextArea.h create mode 120000 gb.qt5/src/CTextBox.cpp create mode 120000 gb.qt5/src/CTextBox.h create mode 120000 gb.qt5/src/CWatch.cpp create mode 120000 gb.qt5/src/CWatch.h create mode 120000 gb.qt5/src/CWatcher.cpp create mode 120000 gb.qt5/src/CWatcher.h create mode 120000 gb.qt5/src/CWidget.cpp create mode 120000 gb.qt5/src/CWidget.h create mode 120000 gb.qt5/src/CWindow.cpp create mode 120000 gb.qt5/src/CWindow.h create mode 100644 gb.qt5/src/Makefile.am create mode 120000 gb.qt5/src/cpaint_impl.cpp create mode 120000 gb.qt5/src/cpaint_impl.h create mode 120000 gb.qt5/src/cprinter.cpp create mode 120000 gb.qt5/src/cprinter.h create mode 120000 gb.qt5/src/csvgimage.cpp create mode 120000 gb.qt5/src/csvgimage.h create mode 120000 gb.qt5/src/ctrayicon.cpp create mode 120000 gb.qt5/src/ctrayicon.h create mode 120000 gb.qt5/src/desktop.c create mode 120000 gb.qt5/src/desktop.h create mode 120000 gb.qt5/src/ext/CDial.cpp create mode 120000 gb.qt5/src/ext/CDial.h create mode 120000 gb.qt5/src/ext/CLCDNumber.cpp create mode 120000 gb.qt5/src/ext/CLCDNumber.h create mode 120000 gb.qt5/src/ext/CTextEdit.cpp create mode 120000 gb.qt5/src/ext/CTextEdit.h create mode 100644 gb.qt5/src/ext/Makefile.am create mode 100644 gb.qt5/src/ext/gb.qt5.ext.component create mode 100644 gb.qt5/src/ext/main.cpp create mode 100644 gb.qt5/src/ext/main.h create mode 120000 gb.qt5/src/fix_breeze.cpp create mode 120000 gb.qt5/src/fix_breeze.h create mode 120000 gb.qt5/src/gb.qt.h create mode 100644 gb.qt5/src/gb.qt5.component create mode 120000 gb.qt5/src/main.cpp create mode 120000 gb.qt5/src/main.h create mode 100644 gb.qt5/src/opengl/CGLarea.cpp create mode 100644 gb.qt5/src/opengl/CGLarea.h create mode 100644 gb.qt5/src/opengl/COldGLarea.cpp create mode 100644 gb.qt5/src/opengl/COldGLarea.h create mode 100644 gb.qt5/src/opengl/Makefile.am create mode 100644 gb.qt5/src/opengl/gb.qt5.opengl.component create mode 100644 gb.qt5/src/opengl/main.cpp create mode 100644 gb.qt5/src/opengl/main.h create mode 120000 gb.qt5/src/trayicon.xpm create mode 100644 gb.qt5/src/webkit/Makefile.am create mode 120000 gb.qt5/src/webkit/ccookiejar.cpp create mode 120000 gb.qt5/src/webkit/ccookiejar.h create mode 100644 gb.qt5/src/webkit/control/webview.png create mode 120000 gb.qt5/src/webkit/cwebdownload.cpp create mode 120000 gb.qt5/src/webkit/cwebdownload.h create mode 120000 gb.qt5/src/webkit/cwebelement.cpp create mode 120000 gb.qt5/src/webkit/cwebelement.h create mode 120000 gb.qt5/src/webkit/cwebframe.cpp create mode 120000 gb.qt5/src/webkit/cwebframe.h create mode 120000 gb.qt5/src/webkit/cwebhittest.cpp create mode 120000 gb.qt5/src/webkit/cwebhittest.h create mode 120000 gb.qt5/src/webkit/cwebsettings.cpp create mode 120000 gb.qt5/src/webkit/cwebsettings.h create mode 120000 gb.qt5/src/webkit/cwebview.cpp create mode 120000 gb.qt5/src/webkit/cwebview.h create mode 100644 gb.qt5/src/webkit/gb.qt5.webkit.component create mode 120000 gb.qt5/src/webkit/main.cpp create mode 120000 gb.qt5/src/webkit/main.h create mode 120000 gb.qt5/src/x11.c create mode 120000 gb.qt5/src/x11.h create mode 100644 gb.sdl.sound/AUTHORS create mode 120000 gb.sdl.sound/COPYING create mode 100644 gb.sdl.sound/ChangeLog create mode 120000 gb.sdl.sound/INSTALL create mode 100644 gb.sdl.sound/Makefile.am create mode 100644 gb.sdl.sound/NEWS create mode 100644 gb.sdl.sound/README create mode 120000 gb.sdl.sound/acinclude.m4 create mode 120000 gb.sdl.sound/component.am create mode 100644 gb.sdl.sound/configure.ac create mode 120000 gb.sdl.sound/gambas.h create mode 120000 gb.sdl.sound/gb_common.h create mode 120000 gb.sdl.sound/m4 create mode 120000 gb.sdl.sound/reconf create mode 100644 gb.sdl.sound/src/Makefile.am create mode 100644 gb.sdl.sound/src/cdrom.c create mode 100644 gb.sdl.sound/src/cdrom.h create mode 100644 gb.sdl.sound/src/gb.sdl.sound.component create mode 100644 gb.sdl.sound/src/main.c create mode 100644 gb.sdl.sound/src/main.h create mode 100644 gb.sdl.sound/src/sound.c create mode 100644 gb.sdl.sound/src/sound.h create mode 100644 gb.sdl/AUTHORS create mode 120000 gb.sdl/COPYING create mode 100644 gb.sdl/ChangeLog create mode 120000 gb.sdl/INSTALL create mode 100644 gb.sdl/Makefile.am create mode 100644 gb.sdl/NEWS create mode 100644 gb.sdl/README create mode 120000 gb.sdl/acinclude.m4 create mode 120000 gb.sdl/component.am create mode 100644 gb.sdl/configure.ac create mode 120000 gb.sdl/gambas.h create mode 120000 gb.sdl/gb.image.h create mode 120000 gb.sdl/gb_common.h create mode 120000 gb.sdl/m4 create mode 120000 gb.sdl/reconf create mode 100644 gb.sdl/src/Cconst.cpp create mode 100644 gb.sdl/src/Cconst.h create mode 100644 gb.sdl/src/Cdesktop.cpp create mode 100644 gb.sdl/src/Cdesktop.h create mode 100644 gb.sdl/src/Cdraw.cpp create mode 100644 gb.sdl/src/Cdraw.h create mode 100644 gb.sdl/src/Cfont.cpp create mode 100644 gb.sdl/src/Cfont.h create mode 100644 gb.sdl/src/Cimage.cpp create mode 100644 gb.sdl/src/Cimage.h create mode 100644 gb.sdl/src/Cjoystick.cpp create mode 100644 gb.sdl/src/Cjoystick.h create mode 100644 gb.sdl/src/Ckey.cpp create mode 100644 gb.sdl/src/Ckey.h create mode 100644 gb.sdl/src/Cmouse.cpp create mode 100644 gb.sdl/src/Cmouse.h create mode 100644 gb.sdl/src/Cwindow.cpp create mode 100644 gb.sdl/src/Cwindow.h create mode 100644 gb.sdl/src/Makefile.am create mode 100644 gb.sdl/src/SDL_h.h create mode 100644 gb.sdl/src/SDLapp.cpp create mode 100644 gb.sdl/src/SDLapp.h create mode 100644 gb.sdl/src/SDLcore.cpp create mode 100644 gb.sdl/src/SDLcore.h create mode 100644 gb.sdl/src/SDLcursor.cpp create mode 100644 gb.sdl/src/SDLcursor.h create mode 100644 gb.sdl/src/SDLdebug.cpp create mode 100644 gb.sdl/src/SDLdebug.h create mode 100644 gb.sdl/src/SDLerror.cpp create mode 100644 gb.sdl/src/SDLerror.h create mode 100644 gb.sdl/src/SDLfont.cpp create mode 100644 gb.sdl/src/SDLfont.h create mode 100644 gb.sdl/src/SDLgfx.cpp create mode 100644 gb.sdl/src/SDLgfx.h create mode 100644 gb.sdl/src/SDLosrender.cpp create mode 100644 gb.sdl/src/SDLosrender.h create mode 100644 gb.sdl/src/SDLsurface.cpp create mode 100644 gb.sdl/src/SDLsurface.h create mode 100644 gb.sdl/src/SDLtexture.cpp create mode 100644 gb.sdl/src/SDLtexture.h create mode 100644 gb.sdl/src/SDLwindow.cpp create mode 100644 gb.sdl/src/SDLwindow.h create mode 100644 gb.sdl/src/default_font.h create mode 100644 gb.sdl/src/gb.sdl.component create mode 100644 gb.sdl/src/main.cpp create mode 100644 gb.sdl/src/main.h create mode 100644 gb.sdl2/AUTHORS create mode 120000 gb.sdl2/COPYING create mode 100644 gb.sdl2/ChangeLog create mode 120000 gb.sdl2/INSTALL create mode 100644 gb.sdl2/Makefile.am create mode 100644 gb.sdl2/NEWS create mode 100644 gb.sdl2/README create mode 120000 gb.sdl2/acinclude.m4 create mode 120000 gb.sdl2/component.am create mode 100644 gb.sdl2/configure.ac create mode 120000 gb.sdl2/gambas.h create mode 120000 gb.sdl2/gb.geom.h create mode 120000 gb.sdl2/gb.image.h create mode 120000 gb.sdl2/gb_common.h create mode 120000 gb.sdl2/gb_list.h create mode 120000 gb.sdl2/gb_list_temp.h create mode 120000 gb.sdl2/m4 create mode 120000 gb.sdl2/reconf create mode 100644 gb.sdl2/src/Makefile.am create mode 100644 gb.sdl2/src/audio/Makefile.am create mode 100644 gb.sdl2/src/audio/c_channel.c create mode 100644 gb.sdl2/src/audio/c_channel.h create mode 100644 gb.sdl2/src/audio/c_music.c create mode 100644 gb.sdl2/src/audio/c_music.h create mode 100644 gb.sdl2/src/audio/c_sound.c create mode 100644 gb.sdl2/src/audio/c_sound.h create mode 100644 gb.sdl2/src/audio/gb.sdl2.audio.component create mode 100644 gb.sdl2/src/audio/main.c create mode 100644 gb.sdl2/src/audio/main.h create mode 100644 gb.sdl2/src/c_draw.c create mode 100644 gb.sdl2/src/c_draw.h create mode 100644 gb.sdl2/src/c_font.c create mode 100644 gb.sdl2/src/c_font.h create mode 100644 gb.sdl2/src/c_image.c create mode 100644 gb.sdl2/src/c_image.h create mode 100644 gb.sdl2/src/c_key.c create mode 100644 gb.sdl2/src/c_key.h create mode 100644 gb.sdl2/src/c_mouse.c create mode 100644 gb.sdl2/src/c_mouse.h create mode 100644 gb.sdl2/src/c_window.c create mode 100644 gb.sdl2/src/c_window.h create mode 100644 gb.sdl2/src/default_font.c create mode 100644 gb.sdl2/src/default_font.h create mode 100644 gb.sdl2/src/default_font_data.h create mode 100644 gb.sdl2/src/gb.sdl2.component create mode 100644 gb.sdl2/src/main.c create mode 100644 gb.sdl2/src/main.h create mode 100644 gb.v4l/AUTHORS create mode 120000 gb.v4l/COPYING create mode 100644 gb.v4l/ChangeLog create mode 120000 gb.v4l/INSTALL create mode 100644 gb.v4l/Makefile.am create mode 100644 gb.v4l/NEWS create mode 100644 gb.v4l/README create mode 120000 gb.v4l/acinclude.m4 create mode 120000 gb.v4l/component.am create mode 100644 gb.v4l/configure.ac create mode 120000 gb.v4l/gambas.h create mode 120000 gb.v4l/gb.image.h create mode 120000 gb.v4l/gb_common.h create mode 120000 gb.v4l/m4 create mode 100644 gb.v4l/orig/video-capture-0.2.tar.gz create mode 120000 gb.v4l/reconf create mode 100644 gb.v4l/src/CConverters.c create mode 100644 gb.v4l/src/CWebcam.c create mode 100644 gb.v4l/src/CWebcam.h create mode 100644 gb.v4l/src/Makefile.am create mode 100644 gb.v4l/src/gb.v4l.component create mode 100644 gb.v4l/src/gv4l2.c create mode 100644 gb.v4l/src/main.c create mode 100644 gb.v4l/src/main.h create mode 100644 gb.v4l/src/videodev.h create mode 100755 gb.xml/AUTHORS create mode 120000 gb.xml/COPYING create mode 100755 gb.xml/ChangeLog create mode 120000 gb.xml/INSTALL create mode 100755 gb.xml/Makefile.am create mode 100755 gb.xml/NEWS create mode 100755 gb.xml/README create mode 100644 gb.xml/TODO create mode 120000 gb.xml/acinclude.m4 create mode 120000 gb.xml/component.am create mode 100755 gb.xml/configure.ac create mode 120000 gb.xml/gambas.h create mode 120000 gb.xml/gb_common.h create mode 120000 gb.xml/m4 create mode 120000 gb.xml/reconf create mode 100755 gb.xml/src/.directory create mode 100644 gb.xml/src/CDocument.cpp create mode 100644 gb.xml/src/CDocument.h create mode 100644 gb.xml/src/CElement.cpp create mode 100644 gb.xml/src/CElement.h create mode 100644 gb.xml/src/CExplorer.cpp create mode 100644 gb.xml/src/CExplorer.h create mode 100644 gb.xml/src/CNode.cpp create mode 100644 gb.xml/src/CNode.h create mode 100644 gb.xml/src/CReader.cpp create mode 100644 gb.xml/src/CReader.h create mode 100644 gb.xml/src/CTextNode.cpp create mode 100644 gb.xml/src/CTextNode.h create mode 100644 gb.xml/src/Makefile.am create mode 100644 gb.xml/src/document.cpp create mode 100644 gb.xml/src/document.h create mode 100644 gb.xml/src/element.cpp create mode 100644 gb.xml/src/element.h create mode 100644 gb.xml/src/explorer.cpp create mode 100644 gb.xml/src/explorer.h create mode 100755 gb.xml/src/gb.xml.component create mode 100644 gb.xml/src/gb.xml.h create mode 100644 gb.xml/src/gb.xml/.component create mode 100644 gb.xml/src/gb.xml/.directory create mode 100644 gb.xml/src/gb.xml/.icon.png create mode 100644 gb.xml/src/gb.xml/.project create mode 100644 gb.xml/src/gb.xml/.src/MTest.module create mode 100644 gb.xml/src/gb.xml/.src/MTest2.module create mode 100644 gb.xml/src/gb.xml/.src/XmlReader.class create mode 100644 gb.xml/src/gb.xml/.src/XmlWriter.class create mode 100644 gb.xml/src/gb.xml/.src/_XmlWriterDTD.class create mode 100644 gb.xml/src/gb.xml/text.xml create mode 100644 gb.xml/src/gbinterface.h create mode 100644 gb.xml/src/html/CHTMLDocument.cpp create mode 100644 gb.xml/src/html/CHTMLDocument.h create mode 100644 gb.xml/src/html/CHTMLElement.cpp create mode 100644 gb.xml/src/html/CHTMLElement.h create mode 100644 gb.xml/src/html/Makefile.am create mode 100644 gb.xml/src/html/cssfilter.cpp create mode 100644 gb.xml/src/html/cssfilter.h create mode 100755 gb.xml/src/html/gb.xml.html.component create mode 100644 gb.xml/src/html/gb.xml.html.h create mode 100644 gb.xml/src/html/htmldocument.cpp create mode 100644 gb.xml/src/html/htmldocument.h create mode 100644 gb.xml/src/html/htmlelement.cpp create mode 100644 gb.xml/src/html/htmlelement.h create mode 100644 gb.xml/src/html/htmlmain.cpp create mode 100644 gb.xml/src/html/htmlmain.h create mode 100644 gb.xml/src/html/htmlparser.cpp create mode 100644 gb.xml/src/html/htmlparser.h create mode 100644 gb.xml/src/html/htmlserializer.cpp create mode 100644 gb.xml/src/html/htmlserializer.h create mode 100644 gb.xml/src/main.cpp create mode 100644 gb.xml/src/main.h create mode 100644 gb.xml/src/node.cpp create mode 100644 gb.xml/src/node.h create mode 100644 gb.xml/src/parser.cpp create mode 100644 gb.xml/src/parser.h create mode 100644 gb.xml/src/reader.cpp create mode 100644 gb.xml/src/reader.h create mode 100644 gb.xml/src/rpc/Makefile.am create mode 120000 gb.xml/src/rpc/gb.xml.rpc.component create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.component create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.directory create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.icon.png create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.project create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/RpcArray.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/RpcAtom.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/RpcClient.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/RpcFunction.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/RpcServer.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/RpcStruct.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/RpcType.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/Test/CXMLRPC.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/Test/MMain.module create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/Test/MTest.module create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/Tools.module create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/XmlRpc.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/hPost.class create mode 100644 gb.xml/src/rpc/gb.xml.rpc/.src/miniServer.class create mode 100644 gb.xml/src/serializer.cpp create mode 100644 gb.xml/src/serializer.h create mode 100644 gb.xml/src/textnode.cpp create mode 100644 gb.xml/src/textnode.h create mode 100644 gb.xml/src/utils.cpp create mode 100644 gb.xml/src/utils.h create mode 100755 gb.xml/src/xslt/CXSLT.cpp create mode 100755 gb.xml/src/xslt/CXSLT.h create mode 100755 gb.xml/src/xslt/Makefile.am create mode 100755 gb.xml/src/xslt/gb.xml.xslt.component create mode 100755 gb.xml/src/xslt/main.cpp create mode 100755 gb.xml/src/xslt/main.h create mode 100644 gb.xml/src/xslt/xslt.pro create mode 100644 logo/gambas-ide.svg create mode 100644 logo/gambas.svg create mode 100644 m4/ax_compare_version.m4 create mode 100644 m4/gb_cflags_gcc_option.m4 create mode 100644 m4/gb_httpd.m4 create mode 100644 m4/gb_sdl.m4 create mode 100644 main/AUTHORS create mode 120000 main/COPYING create mode 120000 main/ChangeLog create mode 100644 main/INSTALL create mode 100644 main/Makefile.am create mode 100644 main/NEWS create mode 100644 main/README create mode 100644 main/TODO create mode 120000 main/acinclude.m4 create mode 100755 main/compile create mode 120000 main/component.am create mode 100644 main/configure.ac create mode 120000 main/gb.pcre.h create mode 100644 main/gbc/Makefile.am create mode 100644 main/gbc/gb_alloc.c create mode 100644 main/gbc/gb_array.c create mode 100644 main/gbc/gb_buffer.c create mode 100644 main/gbc/gb_common.c create mode 100644 main/gbc/gb_error.c create mode 100644 main/gbc/gb_error.h create mode 100644 main/gbc/gb_file.c create mode 100644 main/gbc/gb_file.h create mode 100644 main/gbc/gb_str.c create mode 100644 main/gbc/gb_str.h create mode 100644 main/gbc/gb_table.c create mode 100644 main/gbc/gba.c create mode 100644 main/gbc/gbc.c create mode 100644 main/gbc/gbc_arch.c create mode 100644 main/gbc/gbc_archive.c create mode 100644 main/gbc/gbc_archive.h create mode 100644 main/gbc/gbc_chown.c create mode 100644 main/gbc/gbc_chown.h create mode 100644 main/gbc/gbc_class.c create mode 100644 main/gbc/gbc_class.h create mode 100644 main/gbc/gbc_code.c create mode 100644 main/gbc/gbc_compile.c create mode 100644 main/gbc/gbc_compile.h create mode 100644 main/gbc/gbc_dump.c create mode 100644 main/gbc/gbc_form.c create mode 100644 main/gbc/gbc_form.h create mode 100644 main/gbc/gbc_form_webpage.c create mode 100644 main/gbc/gbc_header.c create mode 100644 main/gbc/gbc_header.h create mode 100644 main/gbc/gbc_help.c create mode 100644 main/gbc/gbc_help.h create mode 100644 main/gbc/gbc_output.c create mode 100644 main/gbc/gbc_output.h create mode 100644 main/gbc/gbc_pcode.c create mode 100644 main/gbc/gbc_preprocess.c create mode 100644 main/gbc/gbc_preprocess.h create mode 100644 main/gbc/gbc_read.c create mode 100644 main/gbc/gbc_read.h create mode 100644 main/gbc/gbc_reserved.c create mode 100644 main/gbc/gbc_reserved_make.c create mode 100644 main/gbc/gbc_trans.c create mode 100644 main/gbc/gbc_trans.h create mode 100644 main/gbc/gbc_trans_code.c create mode 100644 main/gbc/gbc_trans_ctrl.c create mode 100644 main/gbc/gbc_trans_expr.c create mode 100644 main/gbc/gbc_trans_subr.c create mode 100644 main/gbc/gbc_trans_tree.c create mode 100644 main/gbc/gbc_type.c create mode 100644 main/gbc/gbc_type.h create mode 100644 main/gbc/gbi.c create mode 100644 main/gbx/Makefile.am create mode 100644 main/gbx/gb.jit.h create mode 100644 main/gbx/gb_alloc.c create mode 100644 main/gbx/gb_array.c create mode 100644 main/gbx/gb_buffer.c create mode 100644 main/gbx/gb_common.c create mode 100644 main/gbx/gb_common_check.h create mode 100644 main/gbx/gb_error.c create mode 100644 main/gbx/gb_error.h create mode 100644 main/gbx/gb_file.c create mode 100644 main/gbx/gb_file.h create mode 100644 main/gbx/gb_hash.c create mode 100644 main/gbx/gb_list.c create mode 100644 main/gbx/gb_table.c create mode 100644 main/gbx/gbx.c create mode 100644 main/gbx/gbx_api.c create mode 100644 main/gbx/gbx_api.h create mode 100644 main/gbx/gbx_archive.c create mode 100644 main/gbx/gbx_archive.h create mode 100644 main/gbx/gbx_c_application.c create mode 100644 main/gbx/gbx_c_application.h create mode 100644 main/gbx/gbx_c_array.c create mode 100644 main/gbx/gbx_c_array.h create mode 100644 main/gbx/gbx_c_class.c create mode 100644 main/gbx/gbx_c_class.h create mode 100644 main/gbx/gbx_c_collection.c create mode 100644 main/gbx/gbx_c_collection.h create mode 100644 main/gbx/gbx_c_enum.c create mode 100644 main/gbx/gbx_c_enum.h create mode 100644 main/gbx/gbx_c_error.c create mode 100644 main/gbx/gbx_c_error.h create mode 100644 main/gbx/gbx_c_file.c create mode 100644 main/gbx/gbx_c_file.h create mode 100644 main/gbx/gbx_c_gambas.c create mode 100644 main/gbx/gbx_c_gambas.h create mode 100644 main/gbx/gbx_c_observer.c create mode 100644 main/gbx/gbx_c_observer.h create mode 100644 main/gbx/gbx_c_process.c create mode 100644 main/gbx/gbx_c_process.h create mode 100644 main/gbx/gbx_c_string.c create mode 100644 main/gbx/gbx_c_string.h create mode 100644 main/gbx/gbx_c_system.c create mode 100644 main/gbx/gbx_c_system.h create mode 100644 main/gbx/gbx_c_task.c create mode 100644 main/gbx/gbx_c_task.h create mode 100644 main/gbx/gbx_c_timer.c create mode 100644 main/gbx/gbx_c_timer.h create mode 100644 main/gbx/gbx_class.c create mode 100644 main/gbx/gbx_class.h create mode 100644 main/gbx/gbx_class_desc.h create mode 100644 main/gbx/gbx_class_info.c create mode 100644 main/gbx/gbx_class_init.c create mode 100644 main/gbx/gbx_class_load.c create mode 100644 main/gbx/gbx_class_load.h create mode 100644 main/gbx/gbx_class_native.c create mode 100644 main/gbx/gbx_compare.c create mode 100644 main/gbx/gbx_compare.h create mode 100644 main/gbx/gbx_component.c create mode 100644 main/gbx/gbx_component.h create mode 100644 main/gbx/gbx_date.c create mode 100644 main/gbx/gbx_date.h create mode 100644 main/gbx/gbx_debug.c create mode 100644 main/gbx/gbx_debug.h create mode 100644 main/gbx/gbx_eval.c create mode 100644 main/gbx/gbx_eval.h create mode 100644 main/gbx/gbx_event.c create mode 100644 main/gbx/gbx_event.h create mode 100644 main/gbx/gbx_exec.c create mode 100644 main/gbx/gbx_exec.h create mode 100644 main/gbx/gbx_exec_enum.c create mode 100644 main/gbx/gbx_exec_loop.c create mode 100644 main/gbx/gbx_exec_operator.c create mode 100644 main/gbx/gbx_exec_pop.c create mode 100644 main/gbx/gbx_exec_push.c create mode 100644 main/gbx/gbx_expression.h create mode 100644 main/gbx/gbx_extern.c create mode 100644 main/gbx/gbx_extern.h create mode 100644 main/gbx/gbx_info.h create mode 100644 main/gbx/gbx_jit.c create mode 100644 main/gbx/gbx_jit.h create mode 100644 main/gbx/gbx_library.c create mode 100644 main/gbx/gbx_library.h create mode 100644 main/gbx/gbx_local.c create mode 100755 main/gbx/gbx_local.h create mode 100644 main/gbx/gbx_math.c create mode 100644 main/gbx/gbx_math.h create mode 100644 main/gbx/gbx_number.c create mode 100644 main/gbx/gbx_number.h create mode 100644 main/gbx/gbx_object.c create mode 100644 main/gbx/gbx_object.h create mode 100644 main/gbx/gbx_project.c create mode 100644 main/gbx/gbx_project.h create mode 100644 main/gbx/gbx_regexp.c create mode 100644 main/gbx/gbx_regexp.h create mode 100644 main/gbx/gbx_replace.c create mode 100644 main/gbx/gbx_signal.c create mode 100644 main/gbx/gbx_signal.h create mode 100644 main/gbx/gbx_split.c create mode 100644 main/gbx/gbx_split.h create mode 100644 main/gbx/gbx_stack.c create mode 100644 main/gbx/gbx_stack.h create mode 100644 main/gbx/gbx_stream.c create mode 100644 main/gbx/gbx_stream.h create mode 100644 main/gbx/gbx_stream_arch.c create mode 100644 main/gbx/gbx_stream_buffer.c create mode 100644 main/gbx/gbx_stream_direct.c create mode 100644 main/gbx/gbx_stream_lock.c create mode 100644 main/gbx/gbx_stream_memory.c create mode 100644 main/gbx/gbx_stream_pipe.c create mode 100644 main/gbx/gbx_stream_process.c create mode 100644 main/gbx/gbx_stream_string.c create mode 100644 main/gbx/gbx_string.c create mode 100644 main/gbx/gbx_string.h create mode 100644 main/gbx/gbx_struct.c create mode 100644 main/gbx/gbx_struct.h create mode 100644 main/gbx/gbx_subr.c create mode 100644 main/gbx/gbx_subr.h create mode 100644 main/gbx/gbx_subr_conv.c create mode 100644 main/gbx/gbx_subr_extern.c create mode 100755 main/gbx/gbx_subr_file.c create mode 100644 main/gbx/gbx_subr_math.c create mode 100644 main/gbx/gbx_subr_math_temp.h create mode 100644 main/gbx/gbx_subr_misc.c create mode 100644 main/gbx/gbx_subr_string.c create mode 100644 main/gbx/gbx_subr_test.c create mode 100644 main/gbx/gbx_subr_test_temp.h create mode 100644 main/gbx/gbx_subr_time.c create mode 100644 main/gbx/gbx_type.c create mode 100644 main/gbx/gbx_type.h create mode 100644 main/gbx/gbx_value.c create mode 100644 main/gbx/gbx_value.h create mode 100644 main/gbx/gbx_variant.h create mode 100644 main/gbx/gbx_watch.c create mode 100644 main/gbx/gbx_watch.h create mode 100644 main/lib/Makefile.am create mode 100644 main/lib/clipper/LICENSE create mode 100644 main/lib/clipper/Makefile.am create mode 100644 main/lib/clipper/c_clipper.cpp create mode 100644 main/lib/clipper/c_clipper.h create mode 100644 main/lib/clipper/clipper.cpp create mode 100644 main/lib/clipper/clipper.hpp create mode 100644 main/lib/clipper/gb.clipper.component create mode 120000 main/lib/clipper/gb.geom.h create mode 100644 main/lib/clipper/main.cpp create mode 100644 main/lib/clipper/main.h create mode 100644 main/lib/complex/Makefile.am create mode 100644 main/lib/complex/ccomplex.c create mode 100644 main/lib/complex/ccomplex.h create mode 100644 main/lib/complex/gb.complex.component create mode 100644 main/lib/complex/main.c create mode 100644 main/lib/complex/main.h create mode 100644 main/lib/compress/CCompress.c create mode 100644 main/lib/compress/CCompress.h create mode 100644 main/lib/compress/CUncompress.c create mode 100644 main/lib/compress/CUncompress.h create mode 100644 main/lib/compress/Makefile.am create mode 100644 main/lib/compress/gb.compress.component create mode 100644 main/lib/compress/gb.compress.h create mode 100644 main/lib/compress/main.c create mode 100644 main/lib/compress/main.h create mode 100644 main/lib/data/Makefile.am create mode 100644 main/lib/data/TODO create mode 100644 main/lib/data/c_avltree.c create mode 100644 main/lib/data/c_avltree.h create mode 100644 main/lib/data/c_circular.c create mode 100644 main/lib/data/c_circular.h create mode 100644 main/lib/data/c_deque.c create mode 100644 main/lib/data/c_deque.h create mode 100644 main/lib/data/c_graph.c create mode 100644 main/lib/data/c_graph.h create mode 100644 main/lib/data/c_graphmatrix.c create mode 100644 main/lib/data/c_graphmatrix.h create mode 100644 main/lib/data/c_heap.c create mode 100644 main/lib/data/c_heap.h create mode 100644 main/lib/data/c_list.c create mode 100644 main/lib/data/c_list.h create mode 100644 main/lib/data/c_trie.c create mode 100644 main/lib/data/c_trie.h create mode 100644 main/lib/data/gb.data.component create mode 100644 main/lib/data/gb.data/.component create mode 100644 main/lib/data/gb.data/.directory create mode 100644 main/lib/data/gb.data/.icon.png create mode 100644 main/lib/data/gb.data/.project create mode 100644 main/lib/data/gb.data/.src/MMain.module create mode 100644 main/lib/data/gb.data/.src/PrioSet.class create mode 100644 main/lib/data/gb.data/.src/_PrioSet_Entry.class create mode 100644 main/lib/data/list.h create mode 100644 main/lib/data/lookup3.h create mode 100644 main/lib/data/main.c create mode 100644 main/lib/data/main.h create mode 100644 main/lib/data/string_compare.h create mode 100644 main/lib/data/trie.c create mode 100644 main/lib/data/trie.h create mode 100644 main/lib/db/CConnection.c create mode 100644 main/lib/db/CConnection.h create mode 100644 main/lib/db/CDatabase.c create mode 100644 main/lib/db/CDatabase.h create mode 100644 main/lib/db/CField.c create mode 100644 main/lib/db/CField.h create mode 100644 main/lib/db/CIndex.c create mode 100644 main/lib/db/CIndex.h create mode 100644 main/lib/db/CResult.c create mode 100644 main/lib/db/CResult.h create mode 100644 main/lib/db/CResultField.c create mode 100644 main/lib/db/CResultField.h create mode 100644 main/lib/db/CTable.c create mode 100644 main/lib/db/CTable.h create mode 100644 main/lib/db/CUser.c create mode 100644 main/lib/db/CUser.h create mode 100644 main/lib/db/Makefile.am create mode 100644 main/lib/db/c_subcollection.c create mode 100644 main/lib/db/c_subcollection.h create mode 100644 main/lib/db/deletemap.c create mode 100644 main/lib/db/deletemap.h create mode 100644 main/lib/db/gb.db.component create mode 100644 main/lib/db/gb.db.h create mode 100644 main/lib/db/gb.db.proto.h create mode 100644 main/lib/db/gb.db/.component create mode 100644 main/lib/db/gb.db/.directory create mode 100644 main/lib/db/gb.db/.icon.png create mode 100644 main/lib/db/gb.db/.project create mode 100644 main/lib/db/gb.db/.src/Connection.class create mode 100644 main/lib/db/gb.db/.src/Connections.class create mode 100644 main/lib/db/gb.db/.src/Main.module create mode 100644 main/lib/db/gb.db/.src/SQLRequest.class create mode 100644 main/lib/db/gb_barray.h create mode 100644 main/lib/db/main.c create mode 100644 main/lib/db/main.h create mode 100644 main/lib/db/sqlite.c create mode 100644 main/lib/db/sqlite.h create mode 100644 main/lib/debug/CDebug.c create mode 100644 main/lib/debug/CDebug.h create mode 100644 main/lib/debug/Makefile.am create mode 100644 main/lib/debug/debug.c create mode 100644 main/lib/debug/debug.h create mode 100644 main/lib/debug/gb.debug.component create mode 100644 main/lib/debug/gb.debug.h create mode 100644 main/lib/debug/main.c create mode 100644 main/lib/debug/main.h create mode 100644 main/lib/debug/print.c create mode 100644 main/lib/debug/print.h create mode 100644 main/lib/debug/profile.c create mode 100644 main/lib/debug/profile.h create mode 100644 main/lib/draw/Makefile.am create mode 100644 main/lib/draw/cdraw.c create mode 100644 main/lib/draw/cdraw.h create mode 100644 main/lib/draw/cpaint.c create mode 100644 main/lib/draw/cpaint.h create mode 100644 main/lib/draw/gb.draw.h create mode 120000 main/lib/draw/gb.geom.h create mode 120000 main/lib/draw/gb.image.h create mode 100644 main/lib/draw/gb.paint.h create mode 100644 main/lib/draw/gb_list.c create mode 100644 main/lib/draw/main.c create mode 100644 main/lib/draw/main.h create mode 100644 main/lib/draw/matrix.c create mode 100644 main/lib/draw/matrix.h create mode 100644 main/lib/eval/Makefile.am create mode 100644 main/lib/eval/c_expression.c create mode 100644 main/lib/eval/c_expression.h create mode 100644 main/lib/eval/c_highlight.c create mode 100644 main/lib/eval/c_highlight.h create mode 100644 main/lib/eval/c_system.c create mode 100644 main/lib/eval/c_system.h create mode 100644 main/lib/eval/eval.c create mode 100644 main/lib/eval/eval.h create mode 100644 main/lib/eval/eval_analyze.c create mode 100644 main/lib/eval/eval_analyze.h create mode 100644 main/lib/eval/eval_code.c create mode 100644 main/lib/eval/eval_code.h create mode 100644 main/lib/eval/eval_read.c create mode 100644 main/lib/eval/eval_read.h create mode 100644 main/lib/eval/eval_reserved.c create mode 100644 main/lib/eval/eval_trans.c create mode 100644 main/lib/eval/eval_trans.h create mode 100644 main/lib/eval/eval_trans_expr.c create mode 100644 main/lib/eval/eval_trans_tree.c create mode 100644 main/lib/eval/gb.eval.component create mode 100644 main/lib/eval/gb.eval.h create mode 100644 main/lib/eval/gb_alloc_override.h create mode 100644 main/lib/eval/gb_array.c create mode 100644 main/lib/eval/gb_error.c create mode 100644 main/lib/eval/gb_error.h create mode 100644 main/lib/eval/gb_table.c create mode 100644 main/lib/eval/main.c create mode 100644 main/lib/eval/main.h create mode 100644 main/lib/gb.component create mode 100644 main/lib/geom/Makefile.am create mode 100644 main/lib/geom/cpoint.c create mode 100644 main/lib/geom/cpoint.h create mode 100644 main/lib/geom/cpoint_temp.h create mode 100644 main/lib/geom/crect.c create mode 100644 main/lib/geom/crect.h create mode 100644 main/lib/geom/crect_temp.h create mode 100644 main/lib/geom/gb.geom.h create mode 100644 main/lib/geom/main.c create mode 100644 main/lib/geom/main.h create mode 100644 main/lib/gui.opengl/Makefile.am create mode 100644 main/lib/gui.opengl/gb.gui.opengl.component create mode 100644 main/lib/gui.opengl/main.c create mode 100644 main/lib/gui.opengl/main.h create mode 100644 main/lib/gui.qt.opengl/Makefile.am create mode 100644 main/lib/gui.qt.opengl/gb.gui.qt.opengl.component create mode 100644 main/lib/gui.qt.opengl/main.c create mode 100644 main/lib/gui.qt.opengl/main.h create mode 100644 main/lib/gui.qt.webkit/Makefile.am create mode 100644 main/lib/gui.qt.webkit/gb.gui.qt.webkit.component create mode 100644 main/lib/gui.qt.webkit/main.c create mode 100644 main/lib/gui.qt.webkit/main.h create mode 100644 main/lib/gui.qt/Makefile.am create mode 100644 main/lib/gui.qt/gb.gui.qt.component create mode 100644 main/lib/gui.qt/main.c create mode 100644 main/lib/gui.qt/main.h create mode 100644 main/lib/gui.trayicon/Makefile.am create mode 100644 main/lib/gui.trayicon/cfaketrayicon.c create mode 100644 main/lib/gui.trayicon/cfaketrayicon.h create mode 100644 main/lib/gui.trayicon/gb.gui.trayicon.component create mode 100644 main/lib/gui.trayicon/main.c create mode 100644 main/lib/gui.trayicon/main.h create mode 100644 main/lib/gui/Makefile.am create mode 100644 main/lib/gui/gb.gui.component create mode 100644 main/lib/gui/main.c create mode 100644 main/lib/gui/main.h create mode 100644 main/lib/image.effect/CImage.cpp create mode 100644 main/lib/image.effect/CImage.h create mode 100644 main/lib/image.effect/Makefile.am create mode 100644 main/lib/image.effect/effect.cpp create mode 100644 main/lib/image.effect/effect.h create mode 100644 main/lib/image.effect/gb.image.effect.component create mode 100644 main/lib/image.effect/kcpuinfo.cpp create mode 100644 main/lib/image.effect/kcpuinfo.h create mode 100644 main/lib/image.effect/kimageeffect.cpp create mode 100644 main/lib/image.effect/kimageeffect.h create mode 100644 main/lib/image.effect/main.cpp create mode 100644 main/lib/image.effect/main.h create mode 100644 main/lib/image.effect/qcolor.cpp create mode 100644 main/lib/image.effect/qcolor.h create mode 100644 main/lib/image.effect/qimage.cpp create mode 100644 main/lib/image.effect/qimage.h create mode 100644 main/lib/image.effect/qpoint.cpp create mode 100644 main/lib/image.effect/qpoint.h create mode 100644 main/lib/image.effect/qrect.cpp create mode 100644 main/lib/image.effect/qrect.h create mode 100644 main/lib/image.effect/qsize.cpp create mode 100644 main/lib/image.effect/qsize.h create mode 100644 main/lib/image.effect/qt.h create mode 100644 main/lib/image/CImage.c create mode 100644 main/lib/image/CImage.h create mode 100644 main/lib/image/CImageStat.c create mode 100644 main/lib/image/CImageStat.h create mode 100644 main/lib/image/Makefile.am create mode 100644 main/lib/image/c_color.c create mode 100644 main/lib/image/c_color.h create mode 100644 main/lib/image/gb.image.component create mode 100644 main/lib/image/gb.image.h create mode 100644 main/lib/image/image.c create mode 100644 main/lib/image/image.h create mode 100644 main/lib/image/image_stat.c create mode 100644 main/lib/image/image_stat.h create mode 100644 main/lib/image/main.c create mode 100644 main/lib/image/main.h create mode 100644 main/lib/inotify/Makefile.am create mode 100644 main/lib/inotify/TODO create mode 100644 main/lib/inotify/c_watch.c create mode 100644 main/lib/inotify/c_watch.h create mode 100644 main/lib/inotify/gb.inotify.component create mode 100644 main/lib/inotify/gb_list.c create mode 100644 main/lib/inotify/main.c create mode 100644 main/lib/inotify/main.h create mode 100644 main/lib/jit/Makefile.am create mode 100644 main/lib/jit/gb.jit.component create mode 100644 main/lib/jit/gb.jit/.component create mode 100644 main/lib/jit/gb.jit/.directory create mode 100644 main/lib/jit/gb.jit/.icon.png create mode 100644 main/lib/jit/gb.jit/.project create mode 100644 main/lib/jit/gb.jit/.src/Jit.module create mode 100644 main/lib/jit/gb.jit/.src/Main.module create mode 100644 main/lib/jit/gb.jit/.src/_ClassStat.class create mode 120000 main/lib/jit/gb.jit/gambas.h create mode 120000 main/lib/jit/gb.jit/gb.jit.h create mode 120000 main/lib/jit/gb.jit/gb_error_common.h create mode 100644 main/lib/jit/gb.jit/jit.h create mode 100644 main/lib/jit/gb_str.c create mode 100644 main/lib/jit/gb_str.h create mode 100644 main/lib/jit/gbc_reserved.c create mode 100644 main/lib/jit/jit.c create mode 100644 main/lib/jit/jit.h create mode 100644 main/lib/jit/jit_body.c create mode 100644 main/lib/jit/main.c create mode 100644 main/lib/jit/main.h create mode 100755 main/lib/option/Makefile.am create mode 100755 main/lib/option/gb.option.component create mode 100755 main/lib/option/getoptions.c create mode 100755 main/lib/option/getoptions.h create mode 100755 main/lib/option/main.c create mode 100755 main/lib/option/main.h create mode 100644 main/lib/signal/Makefile.am create mode 100644 main/lib/signal/csignal.c create mode 100644 main/lib/signal/csignal.h create mode 100644 main/lib/signal/gb.signal.component create mode 100644 main/lib/signal/main.c create mode 100644 main/lib/signal/main.h create mode 100644 main/lib/term/Makefile.am create mode 100644 main/lib/term/cterm.c create mode 100644 main/lib/term/cterm.h create mode 100644 main/lib/term/gb.term.component create mode 100644 main/lib/term/main.c create mode 100644 main/lib/term/main.h create mode 100644 main/lib/vb/Makefile.am create mode 100644 main/lib/vb/gb.vb.component create mode 100644 main/lib/vb/main.c create mode 100644 main/lib/vb/main.h create mode 100644 main/lib/vb/vb.c create mode 100644 main/lib/vb/vb.h create mode 100644 main/lib/vb/vbdate.c create mode 100644 main/lib/vb/vbdate.h create mode 120000 main/m4 create mode 100644 main/mime/application-x-gambas3.png create mode 100644 main/mime/application-x-gambas3.xml create mode 120000 main/reconf create mode 100644 main/share/Makefile.am create mode 100644 main/share/gambas.h create mode 100644 main/share/gb_alloc.h create mode 100644 main/share/gb_alloc_temp.h create mode 100644 main/share/gb_arch.h create mode 100644 main/share/gb_arch_temp.h create mode 100644 main/share/gb_array.h create mode 100644 main/share/gb_array_temp.h create mode 100644 main/share/gb_buffer.h create mode 100644 main/share/gb_buffer_temp.h create mode 100644 main/share/gb_class_desc_common.h create mode 100644 main/share/gb_code.h create mode 100644 main/share/gb_code_temp.h create mode 100644 main/share/gb_common.h create mode 100644 main/share/gb_common_buffer.h create mode 100644 main/share/gb_common_buffer_temp.h create mode 100644 main/share/gb_common_case.h create mode 100644 main/share/gb_common_case_temp.h create mode 100644 main/share/gb_common_string.h create mode 100644 main/share/gb_common_string_temp.h create mode 100644 main/share/gb_common_swap.h create mode 100644 main/share/gb_common_swap_temp.h create mode 100644 main/share/gb_component.h create mode 100644 main/share/gb_error_common.h create mode 100644 main/share/gb_file_share.h create mode 100644 main/share/gb_file_temp.h create mode 100644 main/share/gb_hash.h create mode 100644 main/share/gb_hash_temp.h create mode 100644 main/share/gb_limit.h create mode 100644 main/share/gb_list.h create mode 100644 main/share/gb_list_temp.h create mode 100644 main/share/gb_magic.h create mode 100644 main/share/gb_pcode.h create mode 100644 main/share/gb_pcode_temp.h create mode 100644 main/share/gb_replace.h create mode 100644 main/share/gb_replace_temp.h create mode 100644 main/share/gb_reserved.h create mode 100644 main/share/gb_reserved_keyword.h create mode 100644 main/share/gb_reserved_temp.h create mode 100644 main/share/gb_table.h create mode 100644 main/share/gb_table_temp.h create mode 100644 main/share/gb_type_common.h create mode 100644 main/share/gbc_read_common.h create mode 100644 main/share/gbc_trans_common.h create mode 100644 main/share/gbx_subr_common.h create mode 100644 main/tools/gbh3/.directory create mode 100644 main/tools/gbh3/.icon.png create mode 100644 main/tools/gbh3/.project create mode 100644 main/tools/gbh3/.src/MMain.module create mode 100644 main/tools/gbh3/.src/MOldMain.module create mode 100644 main/tools/gbh3/README create mode 100644 main/tools/gbh3/icon.png create mode 100644 main/tools/gbh3/license create mode 100644 main/tools/gbh3/usage create mode 100755 reconf create mode 100755 reconf-all create mode 100644 version.m4 diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..3b750e21 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,274 @@ +# +# GAMBAS AUTHORS FILE +# +# This file should list all people that have written code or done translations for Gambas. +# If you want to add or fix anything, please write on the mailing-list. +# I apologize by advance for any mistake or omission. +# +# The format of that file is the following: +# +# [Name of the author] ([Country]) <[mail address]> +# [Description in Gambas markup syntax] +# ... +# +# [Name of another author] ({Country]) <[mail address]> +# [Description in Gambas markup syntax] +# ... +# +# Void lines and line beginning with '#' are ignored. +# + +Fabien Bodard (France) + IDE file/project/picture selector and mascot redesign. + The reporting components. + The 'gb.chart' component. + The 'gb.map' component. + The 'gb.scanner' component. + +Nigel Gerrard (United Kingdom) + The [MySQL database driver](http://mysql.com) + The now deprecated QT extended component. + The old SQLite database driver. + +Paul Gardner-Stephen (Australia) + Porting Gambas on Solaris. + +Daniel Campos (Spain) + The networking component + The compression component + The old XML component + The GTK+ component. + The now deprecated VideoForLinux component + The PDF component. + Spanish translation. + +Carlos F. A. Paniago (Brazil) + Porting Gambas on FreeBSD. + +Rob Kudla (USA) + The old Gambas Wiki hosting. + Mandriva RPM packages maintainer. + The 'gb.pcre' component. + +Ronald Onstenk (Netherlands) + The find list in the 1.0 Development Environnement + +Brandon Bergren (USA) + Porting Gambas on Cygwin. + +Laurent Carlier (France) + The SDL component. + The OpenGL component. + ArchLinux support. + +José L. Redrejo Rodríguez (Spain) + Let Gambas compile on all Debian architectures. + Allowed the Gambas IDE to make Debian packages. + Debian and Linex package maintainer. + Spanish translation. + +Andrea Bortolan (Italy) + The ODBC database driver. + +Ahmad Kamal + Arabic translation. + +Daif Al-Otaibi + Arabic translation. + +Dimitri Bellini (Italy) + Italian translation. + +Vincenzo Virgilio (Italy) + Italian translation. + +Maurizio Pozzobon (Italy) + Italian translation + +Jordi Sayol (Andorra) + Catalan translation + +Kazutaka Harada (Japan) + Japanese translation + +Yizhou He + Simplified Chinese translation. + +Knut Berg + Norwegian translation. + +Chunchi Lin + Traditional Chinese translation. + +Wojciech Saltarski (Poland) + Polish translation. + +Radoslav Dejanovic (Croatia) + Croatian translation. + +Luis Minero (Portugal) + Portuguese translation. + +Iuri Matias (Portugal) + Portuguese translation. + +Peter Cernoch (Czech Republic) + Czech translation. + +Nelson Ferraz + Brazilian Portuguese translation. + +Fermyno Gutierrez + Brazilian Portuguese translation. + +Ronald Onstenk (Netherlands) + Dutch translation. + +Fabrice Mous (Netherlands) + Dutch translation. + +Sergey Irupin (Russia) + Russian translation. + +Nima Mohammadi (Iran) + Farsi translation. + +Miha Ambroz + Slovenian translation. + +David Cendal Lago + Galician translation. + +Peter Landgren + Swedish translation. + +Marco Bauer (Germany) + German translation. + +Klaus-Peter Richter (Germany) + German translation. + +Fatih Asici (Turkey) + Turkish translation. + +Balázs Bárány + Hungarian translation. + +Kevin Donnelly + Welsh translation. + +Sahatma Petrus Dolok Marupa Siagian (Indonesia) + Indonesian translation. + +Rizky Tahara Shita + Indonesian translation + +Stefano Palmeri (Italy) + Italian translation. + +Alexander Kazancev (Russia) + Russian translation. + +Robert Rowe (USA) + Development environment enhancements. + +David Villalobos Cambronero (Costa Rica) + The 'gb.mysql' MySQL specific component. + Spanish translation. + +Peter Mathijssen + Dutch translation. + +Liang Wei (China) + Simplified Chinese translation. + +Philippe Séraphin (France) + IDE Packager wizard enhancements. + +Alexandros Prekates (Greece) + Greek translation. + +Pablo Mileti (Argentina) + GNUBoxWorld and Puzzle1To8 examples. + +Timothy Marshal-Nichols (United Kingdom) + PictureDatabase example. + Printing example. + +Jairo Alonso Badillo Bedoya (Columnia) + Concent game example. + +Gareth Bult (United Kingdom) + WebCam video example. + +Iman Karim (Germany) + Gravity example. + GameOfLife example. + +Stefan Lang (Germany) + German translation. + +Radek Fryšták (Czech Republic) + Czech translation. + +Mathias Ebermann (Germany) + German translation. + +Florin Iacob (Romania) + Romanian translation. + +Josef Kubíček (Czech Republic) + Czech translation. + +Regimantas Baublys (Lithuania) + Lithuanian translation. + +Edison Henrique Andreassy (Brazil) + Brazilian Portuguese translation. + +Randall Morgan + The 'gb.gsl' GNU Scientific Library component. + +Tobias Boege (Germany) + The 'gb.data' component. + The 'gb.inotify' component. + The 'gb.ncurses' component. + The 'gb.openssl' component. + +Adrien Prokopowicz (France) + The rewritten 'gb.xml' component. + The 'gb.xml.html' component. + +Emil Lenngren (Sweden) + The initial Just-In-Time compiler. + +Bruce Bruen (Australia) + IDE Packager wizard enhancements. + +Sebastian Kulesz + Spanish translation. + Gambas Debian/Ubuntu packaging. + The 'gb.logging' component. + The 'gb.memcached' component. + The 'gb.net.pop3' component. + +Paul Wheeler (USA) + English Grammar Check & Clarifications to Wiki entries. + +Willy Raets (Netherlands) + Dutch translation. + +Marcelo López (Argentina) + ODBC driver enhancements. + +Bastian Germann (Germany) + Cygwin package. + Debian package. + pkgsrc package. + Porting on NetBSD and OpenBSD. + Portability fixes for Cygwin. + +Benoît Minisini (France) + French translation. + Main developer. Did everything else... + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..74bf15c7 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,149 @@ +# How To Contribute + +This guide provides a step-by-step tutorial on how to contribute to the Gambas source code and translations. + +It will cover the sources' organization, some contributing guidelines and how to use Git and GitLab in order to submit your contribution. + +## Using Git and GitLab + +The Gambas source code is managed by a Git repository, hosted on [GitLab.com](https://gitlab.com/gambas/gambas). + +To handle new contributions, we use the [Project forking workflow](https://docs.gitlab.com/ce/workflow/forking_workflow.html) : +since contributors do not have the permission to write to the Gambas source code directly, you will have to create a separate repository containing your changes, and then create a merge request asking the Gambas developers to merge your changes into the main repository. + +While this might sound complex to new contributors, this document is made to guide through this process, step by step. + +If you are having trouble with the steps mentioned here, or if you have any question regarding a contribution, you can [ask on the mailing-list](http://gambaswiki.org/wiki/doc/forum). + +### Creating a GitLab account + +First, you will need a [GitLab account](https://gitlab.com/users/sign_in) in order to submit any changes. + +We also recommend you [use SSH to work with Git repositories](https://docs.gitlab.com/ce/ssh/README.html), instead of HTTPS. +Not only you won't have to enter your GitLab username and password every time you want to interact with the repository, but it is also more secure as your password is never sent through the network. + +You can also [use GPG to sign your commits](https://docs.gitlab.com/ee/user/project/gpg_signed_commits/index.html), although it is not required. + +### Forking the Gambas Repository + +Now that your GitLab account is set up, we can now [fork the Gambas repository](https://docs.gitlab.com/ce/gitlab-basics/fork-project.html). +This will create a copy of the Gambas repository, but it will belong to you, so you can make any change you want. + +To do this, just go over to the [Gambas project page](https://gitlab.com/gambas/gambas), and click the "Fork" button. + +You will then be asked where to put the forked repository. Once complete, the new repository will appear under your account. + +You can then clone the repository to your local machine, using the following command (replace `` with your GitLab username): + + git clone git@gitlab.com:/gambas.git + +### Making changes to your repository + +Once the cloning is complete, you can make changes to your local copy, which will then have to be commited and pushed. + +First, you can check which files you changed with the `git status`. It's always good to check before commiting ! +You can also view the full diff with the `git diff` command. + +Once everything is done, you will have to select which files you want to commit next using the [git add](https://git-scm.com/docs/git-add) command. + +You can either select specific files or directories using `git add file1.c file2.c main/gbx`, or just select everything using `git add -A`. + +You can then make your commit using the `git commit` command. +This command will open an editor to let you write your commit message. + +For some guidelines on how to write commit messages, see the Writing commit messages section. + +This command will start the default editor (usually `vi`), but you can change this by setting the `EDITOR` environment variable to the command starting your favorite editor. + +Now that the commit is done, you can push it to your GitLab repository using the `git push` command. + +### Creating the merge request + +With your changes now pushed to the GitLab repository, the final step is to create a [Merge Request](https://docs.gitlab.com/ee/user/project/merge_requests/index.html), +kindly asking the Gambas developers to merge your changes to the main Gambas repository. + +Since this process is entirerly made through GitLab, [use the following instructions to create your merge requests](https://docs.gitlab.com/ee/gitlab-basics/add-merge-request.html). + +Its is probable that your changes won't be accepted right away, and you will be asked by the Gambas maintainers to make some changes. + +In this case, you can make your changes, commit them and then push them. +The Merge request on GitLab will be automatically updated, you won't have to recreate it. + +### Keeping your repository up to date + +During the time you make changes to your version of the Gambas source code, or while your request is being reviewed, it is likely that the Gambas repository will receive some updates. + +Since a forked repository is basically a clone, the official version and your version are completely separate, it will not receive newer commits automatically. + +However, you can setup your local repository to connect to both repositories, so you can pull changes from the official Gambas repository, merge them with your changes locally, and then push them to your forked repository. + +First, we will setup your local Git repository by adding the original Gambas repository as a second remote : + + git remote add upstream https://gitlab.com/gambas/gambas.git + +We now have added a new remote named `upstream` to the local repository, pointing to the original Gambas repository. +(You can list all the remotes with the `git remote -v` command.) + +This means Git can now pull changes from the original Gambas repository using the following command : + + git pull upstream master + +This command will take the changes from the `master` branch of the `upstream` remote. + +If you made commits to your version of the repository, it will merge them with the new changes, creating a new merge commit. + +When the merge is complete, you can simply use `git push` to push all these changes to your version of the repository. If you have any Merge Request pending, they will get updated automatically. + + +## Writing commit messages + +In order to automatically generate changelogs for each release, commits in the Gambas repository have to follow a very specific format. + +Here is an example : + + This commit contains things, adds stuff and has lots of fluff. + (but this won't go in the changelog) + + [GB.QT4] + * NEW: Added things to the component. + * BUG: Fixed a bug in the Foo function. + * NEW: Added this very long modification... + ...and it takes more than one Line To Write it. + + This won't go into the changelog either. + + [COMPILER] + * BUG: What an awful bug! + * OPT: Make things go faster. + + [GB.GTK3] + * NEW: The component is now complete! + +As per the Git commit message convention, the first line of each commit is a short description of what it contains. +This line does not end up in the changelog, but it appears in git logs, as well as in the GitLab interface. + +Then, the commit message consists of the following parts : + +* A slot, between square brackets (e.g. `[GB.QT4]`) +* One or more modifications, each prefixed with a tag, which is either `* NEW: `, `* BUG: `, or `* OPT: `, with a space at the end. + +The slot's name is the one of the component modified (in uppercase), or one of these if the changes do not affect a component : +* `[INTERPRETER]` for changes in the interpreter (gbx3). +* `[COMPILER]` for changes in the compiler (gbc3). +* `[ARCHIVER]` for changes in the archiver (gba3). +* `[INFORMER]` for changes in the informer (gbi3). +* `[DEVELOPMENT ENVIRONMENT]` for changes in the IDE (gambas3). +* `[CONFIGURATION]` for changes in the automake/autoconf configuration process +* `[WIKI CGI SCRIPT]` for changes in the wiki CGI script. +* `[WEB SITE MAKER]` for changes in the Gambas web site generator. +* `[EXAMPLES]` for changes in any example. + +The tag's name is one of the following: + +* `NEW` is for new features or translations, updates or other improvements; +* `BUG` is for bug fixes and other corrections +* `OPT` is for optimizations + +Things without an impact for the user (such as refactorings or code cleanups) should not end up in the Changelog. + +All lines without a tag will not appear in the changelog, but if you want a modification to span across multiple lines, you will have to prefix it with two spaces. diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..d159169d --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..94ca1b53 --- /dev/null +++ b/INSTALL @@ -0,0 +1 @@ +Visit http://gambaswiki.org/wiki/install. diff --git a/INSTALL.html b/INSTALL.html new file mode 100644 index 00000000..41a242a7 --- /dev/null +++ b/INSTALL.html @@ -0,0 +1,1216 @@ + + + + + +Gambas Documentation - Compilation & Installation + + + + + + + +
+Compilation & Installation +
+ +

+→ See the latest version of this page. +

+ + + +

+

Requirements

+ +

+

Development Packages

+

+In order to compile Gambas, you must install the following development packages. +

+The actual name of these development packages depends on your +distribution, so please refer to the distribution specific pages above +for more details. + +

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Component + +Requirements (libraries or pkg-config module) +
+Compilation + +gcc g++ automake autoconf libtool >= 2.0 +
+Interpreter + +libffi +
+gb.compress.bzlib2 + +libbz2.so +
+gb.compress.zlib + +libz.so +
+gb.cairo + +cairo >= 1.6.0 cairo-ft >= 1.6.0 +
+gb.crypt + +libcrypt.so +
+gb.db.mysql + +libmysqlclient.so, libz.so +
+gb.db.odbc + +libodbc.so +
+gb.db.postgresql + +libpq.so +
+gb.db.sqlite2 + +libsqlite.so +
+gb.db.sqlite3 + +libsqlite3.so +
+gb.dbus + +dbus-1 +
+gb.desktop + +libXtst.so +
+gb.desktop.gnome + +gnome-keyring-1 +
+gb.gmp + +libgmp.so +
+gb.gsl + +libgsl.so libgslcblas.so +
+gb.gtk + +gtk+-2.0 >= 2.16 librsvg-2.0 >= 2.14.3 cairo >= 1.6.0 cairo-ft >= 1.6.0 gtk+-unix-print-2.0 >= 2.10 +
+gb.gtk.opengl + +gtkglext-1.0 +
+gb.image.io + +gdk-pixbuf +
+gb.image.imlib + +imlib +
+gb.jit + +LLVM >= 3.1 +
+gb.libxml + +libxml-2.0 +
+gb.media + +gstreamer-0.10 >= 0.10.31 gstreamer-interfaces-0.10 >= 0.10.31 for Gambas <= 3.4 +

+gstreamer-1.0 gstreamer-video-1.0 for Gambas >= 3.5 +

+gb.mime + +gmime-2.4 or gmime-2.6 +
+gb.ncurses + +ncurses.so panel.so +
+gb.net.curl + +libcurl >= 7.13 +
+gb.net.smtp + +glib-2.0 +
+gb.opengl gb.opengl.glsl + +libGL.so libGLEW.so +
+gb.opengl.glu + +libGLU.so +
+gb.openssl + +openssl +
+gb.pcre + +libpcre.so +
+gb.pdf + +poppler >= 0.5 +
+gb.qt4 gb.qt4.ext gb.qt4.opengl gb.qt4.webkit + +All Qt4 libraries >= Qt 4.5 +
+gb.sdl + +libSDL.so libSDL_ttf.so libGL.so libGLEW.so +
+gb.sdl.sound + +libSDL.so, libSDL_mixer.so +
+gb.v4l + +libjpeg.so libpng.so Video4Linux >= 2.0 +
+gb.xml.xslt + +libxml-2.0 libxslt +
+

+

Other requirements

+

+You must have the right to write to /tmp, otherwise Gambas will not work. +

+The following versions of GNU tools are needed: +

    +
  • automake 1.11.1
  • +
  • autoconf 2.68
  • +
  • libtool 2.4
  • +

    +

+ +Compiling with older version may or may not work! + +

+

How to compile and install Gambas 3

+

+

Source package configuration

+

+When you are sure that everything is downloaded, type the following +magic sentences in a shell. +

+

i
+'$' represents your shell prompt, and '...' are the +messages printed during the configuration and compilation. +
+

+First, enter the source top-level directory. +

+

$ cd <path/to/source/directory>
+

+Then, type that to create the configuration scripts. +

+

$ ./reconf-all
+

+Then type that to analyze the current system and configure the package: +

+

$ ./configure -C
+...
+

+If a library or a development package is missing, then you will be +warned that some components are disabled. +

+

i
+There are more 'configure' specific explanations in the INSTALL file located in the top-level source directory. +I invite you to read them. +
+

+

Compilation

+

+If everything is configured without error, then run this command to compile the program: +

+

$ make
+...
+

+

Installation

+

+If everything compiles without error, then enter this command to install everything: +

+

i
+You must be root to install Gambas on your system. +
+

+

$ su -c "make install"
+Password: 
+...
+

+or +

+

$ sudo make install
+Password: 
+...
+

+ +

Troubleshooting

+

+If you did several successive compilations, after having updated the source from the subversion +repository for example, and if something fails during +all this process, you can try to "reconfigure" the configuration scripts by typing the following command: +

+

$ ./reconf-all
+

+Then you can run ./configure -C again +

+If it does not work, I need to know what happened exactly. +To do so, type the following command : +

+

$ ( ./configure -C; make; make install ) > output.txt 2>&1
+

+And send me the file "output.txt" by mail, with every other +detail about your computer and your distribution you find +useful. +

+

Instructions for specific distributions

+

+ +

+

+ + + + + + + + + + + + + +
A  +Arch Linux +
D  +Debian +
F  +Fedora + Â· FreeBSD +
M  +Mageia + Â· Mandriva +
O  +OpenSUSE 10.2 +
P  +Pardus +
U  +Ubuntu +
+

+

The Development Environment

+

+The Gambas IDE is made with Gambas. In order to compile and use it, you need to compile the following components: + +

+

+ +

+

Compiling from Subversion

+

+To compile the latest development version of Gambas from the subversion repository, +read the How To Deal With Subversion page. + +

+ + \ No newline at end of file diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..62ab6c6b --- /dev/null +++ b/Makefile.am @@ -0,0 +1,59 @@ +SUBDIRS = \ + main \ + @bzlib2_dir@ \ + @zlib_dir@ \ + @mysql_dir@ \ + @odbc_dir@ \ + @postgresql_dir@ \ + @sqlite2_dir@ \ + @sqlite3_dir@ \ + @net_dir@ \ + @curl_dir@ \ + @mime_dir@ \ + @pcre_dir@ \ + @sdl_dir@ \ + @sdlsound_dir@ \ + @sdl2_dir@ \ + @libxml_dir@ \ + @xml_dir@ \ + @v4l_dir@ \ + @crypt_dir@ \ + @qt4_dir@ \ + @qt5_dir@ \ + @gtk_dir@ \ + @gtk3_dir@ \ + @opengl_dir@ \ + @x11_dir@ \ + @keyring_dir@ \ + @pdf_dir@ \ + @cairo_dir@ \ + @imageio_dir@ \ + @imageimlib_dir@ \ + @dbus_dir@ \ + @gsl_dir@ \ + @gmp_dir@ \ + @ncurses_dir@ \ + @media_dir@ \ + @httpd_dir@ \ + @openssl_dir@ \ + @openal_dir@ \ + comp \ + app \ + . + +EXTRA_DIST = component.am README README.*[^~] TODO TEMPLATE reconf reconf-all VERSION + +am__tar = ${AMTAR} cof - "$$tardir" + +install-exec-local: + @if test -s $(srcdir)/warnings.log; then \ + echo ; \ + echo "||" ; \ + cat $(srcdir)/warnings.log ; \ + echo "||" ; \ + echo ; \ + fi + @rm -f $(srcdir)/warnings.log + +dist-hook: + @(cd $(distdir); rm -rf `find . -name ".gambas" -o -name ".lock" -o -name ".xvpics" -o -name "*~" -o -name "*.out" -o -name "*.pot" -o -name "*.gambas" -o -name "core.*" -o -name "vgcore.*" -o -name ".kdbg*" -o -name ".svn"`;) diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/README b/README new file mode 100644 index 00000000..cfffabad --- /dev/null +++ b/README @@ -0,0 +1,38 @@ +WELCOME TO GAMBAS! + +GAMBAS is a free implementation of a graphical development environment +based on a BASIC interpreter and a full development platform. It is very +inspired by Visual Basic and Java. + +Go to http://gambas.sourceforge.net to get more information: how to compile +and install it, where to find binary packages, how to report a bug... + +Go to http://gambaswiki.org for language documentation. + +The following pieces of code were borrowed and adapted: + +- The natural string comparison algorithme was adapted from the algorithm + made by Martin Pol. See http://sourcefrog.net/projects/natsort/ for more + details. + +- The hash table implementation was adapted from the glib one. + +- The HTML entities parsing in gb.gtk comes from KHTML sources. + +- The gb.image.effect sources are adapted from KDE 3 image effect routines. + +- The blur image algorithm was adapted from the StackBlur javascript + routine from http://www.quasimondo.com/StackBlurForCanvas. + +- The gb.clipper library embeds the Clipper library. See + http://www.angusj.com/delphi/clipper.php for mode details. + +- The function that computes the easter day of a specific year uses an + algorithm made by Aloysius Lilius And Christophorus Clavius. + +If I forget some borrowed code in the list above, just tell me. + +Enjoy Gambas! + +-- +Benoît diff --git a/README.commit b/README.commit new file mode 100644 index 00000000..3e4f88fe --- /dev/null +++ b/README.commit @@ -0,0 +1,60 @@ +STANDARD FORMAT FOR COMMIT MESSAGES +----------------------------------- + +This message is for all developers that will commit something +into the git repository. + +I want to have a standard way to write commit messages, so that ChangeLog can +be almost automatically generated. + +The format is the following: + +- One line that will be a summary of the changes displayed next to the commit + in GitLab. + +- A ChangeLog slot, between '[' & ']' + + Slots are the name of the component, in uppercase if possible, or some other + slots like [INTERPRETER], [COMPILER]... + +- A ChangeLog modification: a '*', a space, the word 'BUG','NEW' or 'OPT', a + colon, a space, and the text. + + 'BUG' is for a fix, 'NEW' for a new feature, and 'OPT' for an optimization. + + If a changelog modification is more than one line, you must use a two space + indent. + +- Other ChangeLog modifications for the same slot. + +- Other slots. + +- Void lines are ignored. + +- All other lines won't go into the changelog. + +For example: + +--8<----------- +I did this thing, and this will be the summary displayed in GitLab. + +[GB.QT5] +* BUG: I fixed this bug. +* NEW: I made this very long modification.... +  and it takes more than one line to write it. + +This won't go into the changelog too. + +[GB.SDL2] +* BUG: What an awful bug! + +[GB.GTK3] +* NEW: I finally finished the component :-) + +--8<----------- + +You should really try hard to follow this scheme, otherwise generating the +release notes becomes truly a pain for me. Be nice! + +-- +Benoît. diff --git a/README.md b/README.md new file mode 100644 index 00000000..b7ed0ccd --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ + + + +# Gambas Almost Means BASIC + +GAMBAS is a free implementation of a graphical development environment +based on a BASIC interpreter and a full development platform. It is very +inspired by Visual Basic and Java. + +Go to http://gambas.sourceforge.net to get more information: how to compile +and install it, where to find binary packages, how to report a bug... + +Go to http://gambaswiki.org for the language documentation. + +The following pieces of code were borrowed and adapted: + +- The natural string comparison algorithme was adapted from the algorithm + made by Martin Pol. See http://sourcefrog.net/projects/natsort/ for more + details. + +- The hash table implementation was adapted from the glib one. + +- The HTML entities parsing in gb.gtk comes from KHTML sources. + +- The gb.image.effect sources are adapted from KDE 3 image effect routines. + +- The blur image algorithm was adapted from the StackBlur javascript + routine from http://www.quasimondo.com/StackBlurForCanvas. + +- The gb.clipper library embeds the Clipper library. See + http://www.angusj.com/delphi/clipper.php for mode details. + +- The function that computes the easter day of a specific year uses an + algorithm made by Aloysius Lilius And Christophorus Clavius. + +- The blurring algoritm is based on the 'StackBlur' algorithm made by Mario Klingemann. + +If I forget some borrowed code in the list above, just tell me. + +Enjoy Gambas! + +Benoît. diff --git a/TEMPLATE/README b/TEMPLATE/README new file mode 100644 index 00000000..f2b32731 --- /dev/null +++ b/TEMPLATE/README @@ -0,0 +1,34 @@ +COMPONENT DIRECTORY TEMPLATE + +Here you will find the 'make-component' script, that creates a initial +component source directory from a configuration file located in the +'conf' directory. + +BE CAREFUL! This template only works inside the complete gambas source +package structure. + +For example, let suppose you want to make a component named 'gb.test', +based on the well known 'test' library: + +1) Copy the file 'TEMPLATE.conf' in the 'conf' directory, and rename it + as 'gb.test.conf' + +2) Open a terminal, with the current directory being the one where + the 'make-component' script is located. This is important, because + otherwise the script won't work. + +3) Edit the file 'gb.test.conf', and replaces the value of each + '#define' directive by the appropriate ones. Each directive has + a commentary that explains its role. + +4) Once done, run './make-component gb.test'. + +5) If everything is ok, you will find a new component directory in the + parent directory named gb.test, with all needed files and symbolic + links ready to be build: configure.ac, Makefile.am, ... + +If something is weird, tell me on the gambas developer mailing-list! + +Benoît. + + diff --git a/TEMPLATE/TEMPLATE.c b/TEMPLATE/TEMPLATE.c new file mode 100644 index 00000000..99c76741 --- /dev/null +++ b/TEMPLATE/TEMPLATE.c @@ -0,0 +1,28 @@ +$/$*************************************************************************** +## + __SOURCE_NAME##.c +## + __COMPONENT component +## + __COPYRIGHT __AUTHOR __EMAIL +## + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. +## + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +## + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. +## +***************************************************************************/ +## +$#$define _##_##__SOURCE_UNAME##_C +## +$#$include $:$##__SOURCE_NAME##.h$:$ diff --git a/TEMPLATE/TEMPLATE.conf b/TEMPLATE/TEMPLATE.conf new file mode 100644 index 00000000..92d4351c --- /dev/null +++ b/TEMPLATE/TEMPLATE.conf @@ -0,0 +1,79 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2012 +#define __AUTHOR Joe Smith +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.test + +/* Name of the component with points replaced by dashes */ +#define __COMPONENT_DASH gb-test + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_test + +/* Short name of the component */ +#define __NAME test + +/* Short name of the component in uppercase */ +#define __UNAME TEST + +/* Description of the component */ +#define __DESCRIPTION Testing component + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 0 + +#if __USE_PKGCONFIG + + /* Name of the package for pkg-config */ + #define __PKGCONFIG_NAME test-1.0 + + /* Minimum version needed */ + #define __PKGCONFIG_VERSION 1.2.8 + +#else /* __USE_PKGCONFIG */ + + /* If your component uses C */ + #define __USE_C 1 + + /* If your component uses C++ */ + #define __USE_CPLUSPLUS 1 + + /* If your component uses multi-threading */ + #define __USE_THREAD 1 + + /* If your component uses X-Window */ + #define __USE_XWINDOW 1 + + /* Includes to search for */ + #define __SEARCH_INCLUDE test.h test2.h + + /* Includes directories search path */ + #define __SEARCH_INCLUDE_PATH /usr/local/lib /usr/local /usr/lib /usr + + /* Includes sub-directories search */ + #define __SEARCH_INCLUDE_DIR test/include include test*/include test/*/include + + /* Libraries to search for */ + #define __SEARCH_LIBRARY libtest.$SHLIBEXT libjpeg.$SHLIBEXT libpng.$SHLIBEXT + + /* Libraries directories search path */ + #define __SEARCH_LIBRARY_PATH /usr/local /usr + + /* Libraries sub-directories search path */ + #define __SEARCH_LIBRARY_DIR lib + + /* Libraries to link with */ + #define __LIBRARY -ljpeg -lpng -ltest + + /* Includes to link with */ + #define __INCLUDE -ljpeg -lpng -ltest + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h test.c test.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/TEMPLATE.cpp b/TEMPLATE/TEMPLATE.cpp new file mode 100644 index 00000000..828c73ad --- /dev/null +++ b/TEMPLATE/TEMPLATE.cpp @@ -0,0 +1,28 @@ +$/$*************************************************************************** +## + __SOURCE_NAME##.c +## + __COMPONENT component +## + __COPYRIGHT __AUTHOR __EMAIL +## + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. +## + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +## + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. +## +***************************************************************************/ +## +$#$define _##_##__SOURCE_UNAME##_C +## +$#$include $:$##__SOURCE_NAME##.h##$:$ diff --git a/TEMPLATE/TEMPLATE.h b/TEMPLATE/TEMPLATE.h new file mode 100644 index 00000000..894fd805 --- /dev/null +++ b/TEMPLATE/TEMPLATE.h @@ -0,0 +1,35 @@ +$/$*************************************************************************** +## + __SOURCE_NAME##.h +## + __COMPONENT component +## + __COPYRIGHT __AUTHOR __EMAIL +## + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. +## + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +## + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. +## +***************************************************************************/ +## +$#$ifndef _##_##__SOURCE_UNAME##_H +$#$define _##_##__SOURCE_UNAME##_H +## +$#$include "gambas.h" +## +$#$ifndef _##_##__MAIN_UNAME##_C +extern GB_INTERFACE GB; +$#$endif +## +$#$endif $/$* _##_##__SOURCE_UNAME##_H */ diff --git a/TEMPLATE/conf/gb.cairo.conf b/TEMPLATE/conf/gb.cairo.conf new file mode 100644 index 00000000..33c6c173 --- /dev/null +++ b/TEMPLATE/conf/gb.cairo.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2009 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.cairo + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_cairo + +/* Short name of the component */ +#define __NAME cairo + +/* Short name of the component in uppercase */ +#define __UNAME CAIRO + +/* Description of the component */ +#define __DESCRIPTION Cairo library + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 1 + +#if __USE_PKGCONFIG + + /* Name of the package for pkg-config */ + #define __PKGCONFIG_NAME cairo + + /* Minimum version needed */ + #define __PKGCONFIG_VERSION 1.8.0 + +#else /* __USE_PKGCONFIG */ + + /* If your component uses C */ + #define __USE_C 1 + + /* If your component uses C++ */ + #define __USE_CPLUSPLUS 1 + + /* If your component uses multi-threading */ + #define __USE_THREAD 1 + + /* If your component uses X-Window */ + #define __USE_XWINDOW 1 + + /* Includes to search for */ + #define __SEARCH_INCLUDE test.h test2.h + + /* Includes directories search path */ + #define __SEARCH_INCLUDE_PATH /usr/local/lib /usr/local /usr/lib /usr + + /* Includes sub-directories search */ + #define __SEARCH_INCLUDE_DIR test/include include test*/include test/*/include + + /* Libraries to search for */ + #define __SEARCH_LIBRARY libtest.$SHLIBEXT libjpeg.$SHLIBEXT libpng.$SHLIBEXT + + /* Libraries directories search path */ + #define __SEARCH_LIBRARY_PATH /usr/local /usr + + /* Libraries sub-directories search path */ + #define __SEARCH_LIBRARY_DIR lib + + /* Libraries to link with */ + #define __LIBRARY -ljpeg -lpng -ltest + + /* Includes to link with */ + #define __INCLUDE -ljpeg -lpng -ltest + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.dbus.conf b/TEMPLATE/conf/gb.dbus.conf new file mode 100644 index 00000000..828e9f1e --- /dev/null +++ b/TEMPLATE/conf/gb.dbus.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2009 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.dbus + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_dbus + +/* Short name of the component */ +#define __NAME dbus + +/* Short name of the component in uppercase */ +#define __UNAME DBUS + +/* Description of the component */ +#define __DESCRIPTION DBUS management + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 1 + +#if __USE_PKGCONFIG + +/* Name of the package for pkg-config */ +#define __PKGCONFIG_NAME dbus-1 + +/* Minimum version needed */ +//#define __PKGCONFIG_VERSION 2.14.3 + +#else /* __USE_PKGCONFIG */ + +/* If your component uses C */ +#define __USE_C 1 + +/* If your component uses C++ */ +#define __USE_CPLUSPLUS 0 + +/* If your component uses multi-threading */ +#define __USE_THREAD 0 + +/* If your component uses X-Window */ +#define __USE_XWINDOW 0 + +/* Includes to search for */ +#define __SEARCH_INCLUDE + +/* Includes directories search path */ +#define __SEARCH_INCLUDE_PATH + +/* Includes sub-directories search */ +#define __SEARCH_INCLUDE_DIR + +/* Libraries to search for */ +#define __SEARCH_LIBRARY + +/* Libraries directories search path */ +#define __SEARCH_LIBRARY_PATH + +/* Libraries sub-directories search path */ +#define __SEARCH_LIBRARY_DIR + +/* Libraries to link with */ +#define __LIBRARY + +/* Includes to look for */ +#define __INCLUDE + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h cdbus.c cdbus.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.desktop.conf b/TEMPLATE/conf/gb.desktop.conf new file mode 100644 index 00000000..d76ed3d9 --- /dev/null +++ b/TEMPLATE/conf/gb.desktop.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2007 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.desktop + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_desktop + +/* Short name of the component */ +#define __NAME desktop + +/* Short name of the component in uppercase */ +#define __UNAME DESKTOP + +/* Description of the component */ +#define __DESCRIPTION Desktop-neutral routines + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 0 + +#if __USE_PKGCONFIG + +/* Name of the package for pkg-config */ +#define __PKGCONFIG_NAME glib-2.0 + +/* Minimum version needed */ +#define __PKGCONFIG_VERSION + +#else /* __USE_PKGCONFIG */ + +/* If your component uses C */ +#define __USE_C 1 + +/* If your component uses C++ */ +#define __USE_CPLUSPLUS 0 + +/* If your component uses multi-threading */ +#define __USE_THREAD 1 + +/* If your component uses X-Window */ +#define __USE_XWINDOW 1 + +/* Includes to search for */ +#define __SEARCH_INCLUDE + +/* Includes directories search path */ +#define __SEARCH_INCLUDE_PATH + +/* Includes sub-directories search */ +#define __SEARCH_INCLUDE_DIR + +/* Libraries to search for */ +#define __SEARCH_LIBRARY + +/* Libraries directories search path */ +#define __SEARCH_LIBRARY_PATH + +/* Libraries sub-directories search path */ +#define __SEARCH_LIBRARY_DIR + +/* Libraries to link with */ +#define __LIBRARY + +/* Includes to look for */ +#define __INCLUDE + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.gmp.conf b/TEMPLATE/conf/gb.gmp.conf new file mode 100644 index 00000000..70af8182 --- /dev/null +++ b/TEMPLATE/conf/gb.gmp.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2012 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.gmp + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_gmp + +/* Short name of the component */ +#define __NAME gmp + +/* Short name of the component in uppercase */ +#define __UNAME GMP + +/* Description of the component */ +#define __DESCRIPTION GNU multi-precision arithmetic library component + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 0 + +#if __USE_PKGCONFIG + + /* Name of the package for pkg-config */ + #define __PKGCONFIG_NAME gsl + + /* Minimum version needed */ + #define __PKGCONFIG_VERSION + +#else /* __USE_PKGCONFIG */ + + /* If your component uses C */ + #define __USE_C 1 + + /* If your component uses C++ */ + #define __USE_CPLUSPLUS 0 + + /* If your component uses multi-threading */ + #define __USE_THREAD 0 + + /* If your component uses X-Window */ + #define __USE_XWINDOW 0 + + /* Includes to search for */ + #define __SEARCH_INCLUDE gmp.h + + /* Includes directories search path */ + #define __SEARCH_INCLUDE_PATH /usr/local /usr + + /* Includes sub-directories search */ + #define __SEARCH_INCLUDE_DIR include + + /* Libraries to search for */ + #define __SEARCH_LIBRARY libgmp.$SHLIBEXT + + /* Libraries directories search path */ + #define __SEARCH_LIBRARY_PATH /usr/local /usr + + /* Libraries sub-directories search path */ + #define __SEARCH_LIBRARY_DIR lib/* lib + + /* Libraries to link with */ + #define __LIBRARY -lgmp + + /* Includes to link with */ + #define __INCLUDE + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.gsl.conf b/TEMPLATE/conf/gb.gsl.conf new file mode 100644 index 00000000..9dc4a603 --- /dev/null +++ b/TEMPLATE/conf/gb.gsl.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2012 +#define __AUTHOR Randall Morgan +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.gsl + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_gsl + +/* Short name of the component */ +#define __NAME gsl + +/* Short name of the component in uppercase */ +#define __UNAME GSL + +/* Description of the component */ +#define __DESCRIPTION GNU Scientific Library component + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 1 + +#if __USE_PKGCONFIG + + /* Name of the package for pkg-config */ + #define __PKGCONFIG_NAME gsl + + /* Minimum version needed */ + #define __PKGCONFIG_VERSION + +#else /* __USE_PKGCONFIG */ + + /* If your component uses C */ + #define __USE_C 1 + + /* If your component uses C++ */ + #define __USE_CPLUSPLUS 1 + + /* If your component uses multi-threading */ + #define __USE_THREAD 0 + + /* If your component uses X-Window */ + #define __USE_XWINDOW 0 + + /* Includes to search for */ + #define __SEARCH_INCLUDE + + /* Includes directories search path */ + #define __SEARCH_INCLUDE_PATH /usr/local/lib /usr/local /usr/lib /usr + + /* Includes sub-directories search */ + #define __SEARCH_INCLUDE_DIR /usr/local/include gsl/include include gsl*/include gsl/*/include + + /* Libraries to search for */ + #define __SEARCH_LIBRARY libgsl.$SHLIBEXT + + /* Libraries directories search path */ + #define __SEARCH_LIBRARY_PATH /usr/local /usr + + /* Libraries sub-directories search path */ + #define __SEARCH_LIBRARY_DIR local/lib lib + + /* Libraries to link with */ + #define __LIBRARY libgsl + + /* Includes to link with */ + #define __INCLUDE libgsl + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h c_gsl.c c_gsl.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.image.io.conf b/TEMPLATE/conf/gb.image.io.conf new file mode 100644 index 00000000..a38c7cf8 --- /dev/null +++ b/TEMPLATE/conf/gb.image.io.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2009 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.image.io + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_image_io + +/* Short name of the component */ +#define __NAME image_io + +/* Short name of the component in uppercase */ +#define __UNAME IMAGE_IO + +/* Description of the component */ +#define __DESCRIPTION Image loading and saving + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 1 + +#if __USE_PKGCONFIG + +/* Name of the package for pkg-config */ +#define __PKGCONFIG_NAME gdk-pixbuf-2.0 + +/* Minimum version needed */ +#define __PKGCONFIG_VERSION 2.14.3 + +#else /* __USE_PKGCONFIG */ + +/* If your component uses C */ +#define __USE_C 1 + +/* If your component uses C++ */ +#define __USE_CPLUSPLUS 0 + +/* If your component uses multi-threading */ +#define __USE_THREAD 0 + +/* If your component uses X-Window */ +#define __USE_XWINDOW 0 + +/* Includes to search for */ +#define __SEARCH_INCLUDE + +/* Includes directories search path */ +#define __SEARCH_INCLUDE_PATH + +/* Includes sub-directories search */ +#define __SEARCH_INCLUDE_DIR + +/* Libraries to search for */ +#define __SEARCH_LIBRARY + +/* Libraries directories search path */ +#define __SEARCH_LIBRARY_PATH + +/* Libraries sub-directories search path */ +#define __SEARCH_LIBRARY_DIR + +/* Libraries to link with */ +#define __LIBRARY + +/* Includes to look for */ +#define __INCLUDE + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h CImage.c CImage.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.media.conf b/TEMPLATE/conf/gb.media.conf new file mode 100644 index 00000000..31b75bd0 --- /dev/null +++ b/TEMPLATE/conf/gb.media.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2012 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.media + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_media + +/* Short name of the component */ +#define __NAME media + +/* Short name of the component in uppercase */ +#define __UNAME MEDIA + +/* Description of the component */ +#define __DESCRIPTION GStreamer component + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 1 + +#if __USE_PKGCONFIG + +/* Name of the package for pkg-config */ +#define __PKGCONFIG_NAME gstreamer-0.10 + +/* Minimum version needed */ +//#define __PKGCONFIG_VERSION 2.14.3 + +#else /* __USE_PKGCONFIG */ + +/* If your component uses C */ +#define __USE_C 1 + +/* If your component uses C++ */ +#define __USE_CPLUSPLUS 0 + +/* If your component uses multi-threading */ +#define __USE_THREAD 0 + +/* If your component uses X-Window */ +#define __USE_XWINDOW 0 + +/* Includes to search for */ +#define __SEARCH_INCLUDE + +/* Includes directories search path */ +#define __SEARCH_INCLUDE_PATH + +/* Includes sub-directories search */ +#define __SEARCH_INCLUDE_DIR + +/* Libraries to search for */ +#define __SEARCH_LIBRARY + +/* Libraries directories search path */ +#define __SEARCH_LIBRARY_PATH + +/* Libraries sub-directories search path */ +#define __SEARCH_LIBRARY_DIR + +/* Libraries to link with */ +#define __LIBRARY + +/* Includes to look for */ +#define __INCLUDE + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.mime.conf b/TEMPLATE/conf/gb.mime.conf new file mode 100644 index 00000000..2866478c --- /dev/null +++ b/TEMPLATE/conf/gb.mime.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2012 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.mime + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_mime + +/* Short name of the component */ +#define __NAME mime + +/* Short name of the component in uppercase */ +#define __UNAME MIME + +/* Description of the component */ +#define __DESCRIPTION MIME message management based on gmime library + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 1 + +#if __USE_PKGCONFIG + + /* Name of the package for pkg-config */ + #define __PKGCONFIG_NAME gmime-2.6 + + /* Minimum version needed */ + #define __PKGCONFIG_VERSION + +#else /* __USE_PKGCONFIG */ + + /* If your component uses C */ + #define __USE_C 1 + + /* If your component uses C++ */ + #define __USE_CPLUSPLUS 0 + + /* If your component uses multi-threading */ + #define __USE_THREAD 0 + + /* If your component uses X-Window */ + #define __USE_XWINDOW 0 + + /* Includes to search for */ + #define __SEARCH_INCLUDE gmp.h + + /* Includes directories search path */ + #define __SEARCH_INCLUDE_PATH /usr/local /usr + + /* Includes sub-directories search */ + #define __SEARCH_INCLUDE_DIR include + + /* Libraries to search for */ + #define __SEARCH_LIBRARY libgmp.$SHLIBEXT + + /* Libraries directories search path */ + #define __SEARCH_LIBRARY_PATH /usr/local /usr + + /* Libraries sub-directories search path */ + #define __SEARCH_LIBRARY_DIR lib/* lib + + /* Libraries to link with */ + #define __LIBRARY -lgmp + + /* Includes to link with */ + #define __INCLUDE + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.net.pop3.conf b/TEMPLATE/conf/gb.net.pop3.conf new file mode 100644 index 00000000..2ecaafcd --- /dev/null +++ b/TEMPLATE/conf/gb.net.pop3.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2006 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.image + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_image + +/* Short name of the component */ +#define __NAME image + +/* Short name of the component in uppercase */ +#define __UNAME IMAGE + +/* Description of the component */ +#define __DESCRIPTION Image processing component + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 0 + +#if __USE_PKGCONFIG + +/* Name of the package for pkg-config */ +#define __PKGCONFIG_NAME test-1.0 + +/* Minimum version needed */ +#define __PKGCONFIG_VERSION 1.2.8 + +#else /* __USE_PKGCONFIG */ + +/* If your component uses C */ +#define __USE_C 0 + +/* If your component uses C++ */ +#define __USE_CPLUSPLUS 1 + +/* If your component uses multi-threading */ +#define __USE_THREAD 1 + +/* If your component uses X-Window */ +#define __USE_XWINDOW 0 + +/* Includes to search for */ +#define __SEARCH_INCLUDE + +/* Includes directories search path */ +#define __SEARCH_INCLUDE_PATH + +/* Includes sub-directories search */ +#define __SEARCH_INCLUDE_DIR + +/* Libraries to search for */ +#define __SEARCH_LIBRARY + +/* Libraries directories search path */ +#define __SEARCH_LIBRARY_PATH + +/* Libraries sub-directories search path */ +#define __SEARCH_LIBRARY_DIR + +/* Libraries to link with */ +#define __LIBRARY + +/* Includes to look for */ +#define __INCLUDE + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.cpp main.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.net.smtp.conf b/TEMPLATE/conf/gb.net.smtp.conf new file mode 100644 index 00000000..7e074c17 --- /dev/null +++ b/TEMPLATE/conf/gb.net.smtp.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2006 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.net.smtp + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_net_smtp + +/* Short name of the component */ +#define __NAME smtp + +/* Short name of the component in uppercase */ +#define __UNAME SMTP + +/* Description of the component */ +#define __DESCRIPTION SMTP client component + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 1 + +#if __USE_PKGCONFIG + +/* Name of the package for pkg-config */ +#define __PKGCONFIG_NAME glib-2.0 + +/* Minimum version needed */ +#define __PKGCONFIG_VERSION + +#else /* __USE_PKGCONFIG */ + +/* If your component uses C */ +#define __USE_C 0 + +/* If your component uses C++ */ +#define __USE_CPLUSPLUS 1 + +/* If your component uses multi-threading */ +#define __USE_THREAD 1 + +/* If your component uses X-Window */ +#define __USE_XWINDOW 0 + +/* Includes to search for */ +#define __SEARCH_INCLUDE + +/* Includes directories search path */ +#define __SEARCH_INCLUDE_PATH + +/* Includes sub-directories search */ +#define __SEARCH_INCLUDE_DIR + +/* Libraries to search for */ +#define __SEARCH_LIBRARY + +/* Libraries directories search path */ +#define __SEARCH_LIBRARY_PATH + +/* Libraries sub-directories search path */ +#define __SEARCH_LIBRARY_DIR + +/* Libraries to link with */ +#define __LIBRARY + +/* Includes to look for */ +#define __INCLUDE + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.cpp main.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/conf/gb.openal.conf b/TEMPLATE/conf/gb.openal.conf new file mode 100644 index 00000000..e5f03861 --- /dev/null +++ b/TEMPLATE/conf/gb.openal.conf @@ -0,0 +1,76 @@ +/* Copyrights */ +#define __COPYRIGHT (c) 2013 +#define __AUTHOR Benoît Minisini +#define __EMAIL + +/* Name of the component */ +#define __COMPONENT gb.openal + +/* Name of the component with points replaced by underscore */ +#define __COMPONENT_UNDERSCORE gb_openal + +/* Short name of the component */ +#define __NAME openal + +/* Short name of the component in uppercase */ +#define __UNAME OPENAL + +/* Description of the component */ +#define __DESCRIPTION Open AL library + +/* If the component detection uses pkg-config */ +#define __USE_PKGCONFIG 1 + +#if __USE_PKGCONFIG + + /* Name of the package for pkg-config */ + #define __PKGCONFIG_NAME openal + + /* Minimum version needed */ + #define __PKGCONFIG_VERSION 1.14 + +#else /* __USE_PKGCONFIG */ + + /* If your component uses C */ + #define __USE_C 1 + + /* If your component uses C++ */ + #define __USE_CPLUSPLUS 1 + + /* If your component uses multi-threading */ + #define __USE_THREAD 1 + + /* If your component uses X-Window */ + #define __USE_XWINDOW 1 + + /* Includes to search for */ + #define __SEARCH_INCLUDE test.h test2.h + + /* Includes directories search path */ + #define __SEARCH_INCLUDE_PATH /usr/local/lib /usr/local /usr/lib /usr + + /* Includes sub-directories search */ + #define __SEARCH_INCLUDE_DIR test/include include test*/include test/*/include + + /* Libraries to search for */ + #define __SEARCH_LIBRARY libtest.$SHLIBEXT libjpeg.$SHLIBEXT libpng.$SHLIBEXT + + /* Libraries directories search path */ + #define __SEARCH_LIBRARY_PATH /usr/local /usr + + /* Libraries sub-directories search path */ + #define __SEARCH_LIBRARY_DIR lib + + /* Libraries to link with */ + #define __LIBRARY -ljpeg -lpng -ltest + + /* Includes to link with */ + #define __INCLUDE -ljpeg -lpng -ltest + +#endif /* __USE_PKGCONFIG */ + +/* Source file list */ +#define __SOURCES main.c main.h + +/* Main C/C++ source basename in uppercase */ +#define __MAIN_UNAME MAIN diff --git a/TEMPLATE/make-component b/TEMPLATE/make-component new file mode 100755 index 00000000..c49eb522 --- /dev/null +++ b/TEMPLATE/make-component @@ -0,0 +1,64 @@ +#!/bin/bash + +if test -d ../$1; then + echo "$0: error: This component already exists"; + exit 1; +fi + +if test ! -e ./conf/$1.conf; then + echo "$0: error: The configuration file for this component does not exist"; + exit 1; +fi + +if test x`which cpp` == x; then + echo "$0: error: cpp was not found"; + exit 1; +fi + +echo "Creating component directory $1..." +cp -R template ../$1 +rm -rf ../$1/.svn ../$1/*/.svn + +echo "Applying template..." +for i in ../$1/* ../$1/*/*; do + if test ! -h $i && test ! -d $i; then + cpp -P -include ./conf/$1.conf -o $i.out $i; + rm -f $i; + cat $i.out | sed s/"\\$\/\\$"/"\/"/g | sed s/"\\$'\\$"/"\""/g | sed s/"\\$:\\$"/"\""/g | sed s/"\\$\#\\$"/"\#"/g | sed s/"\#@\#"/"@"/g | sed s/"\#\#"/""/g > $i; + rm -f $i.out; + fi +done + +echo "Creating source files..." +for i in `cat ../$1/SOURCES`; do + BASENAME=`basename $i`; + SOURCE=../$1/src/$i; + if test ! `basename $i .h` = $BASENAME; then + cpp -P -include ./conf/$1.conf -D__SOURCE_NAME=`basename $i .h` -D__SOURCE_UNAME=`basename $i .h | tr '[:lower:]' '[:upper:]'` -o $SOURCE TEMPLATE.h; + elif test ! `basename $i .c` = $BASENAME; then + cpp -P -include ./conf/$1.conf -D__SOURCE_NAME=`basename $i .c` -D__SOURCE_UNAME=`basename $i .c | tr '[:lower:]' '[:upper:]'` -o $SOURCE TEMPLATE.c; + elif test ! `basename $i .cpp` = $BASENAME; then + cpp -P -include ./conf/$1.conf -D__SOURCE_NAME=`basename $i .cpp` -D__SOURCE_UNAME=`basename $i .cpp | tr '[:lower:]' '[:upper:]'` -o $SOURCE TEMPLATE.cpp; + fi + cat $SOURCE | sed s/"\\$\/\\$"/"\/"/g | sed s/"\\$'\\$"/"\""/g | sed s/"\\$:\\$"/"\""/g | sed s/"\\$\#\\$"/"\#"/g | sed s/"\#@\#"/"@"/g | sed s/"\#\#"/""/g > $SOURCE.tmp; + rm -f $SOURCE; + mv $SOURCE.tmp $SOURCE; +done +rm -f ../$1/SOURCES + +echo "Creating symbolic links..." +pushd . > /dev/null +cd ../$1 +rm -f `find . -name \*~` +for i in ../acinclude.m4 ../component.am ../main/share/gambas.h ../main/share/gb_common.h ../reconf ../INSTALL ../COPYING ../missing ../m4; do + ln -s $i; +done +popd > /dev/null + +pushd . > /dev/null +cd ../$1 +(source ./make-component) +rm -f make-component +popd > /dev/null + + diff --git a/TEMPLATE/template/AUTHORS b/TEMPLATE/template/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/TEMPLATE/template/ChangeLog b/TEMPLATE/template/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/TEMPLATE/template/Makefile.am b/TEMPLATE/template/Makefile.am new file mode 100644 index 00000000..008b94a7 --- /dev/null +++ b/TEMPLATE/template/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @__UNAME##_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/TEMPLATE/template/NEWS b/TEMPLATE/template/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/TEMPLATE/template/README b/TEMPLATE/template/README new file mode 100644 index 00000000..e69de29b diff --git a/TEMPLATE/template/SOURCES b/TEMPLATE/template/SOURCES new file mode 100644 index 00000000..7e45e083 --- /dev/null +++ b/TEMPLATE/template/SOURCES @@ -0,0 +1,2 @@ +__SOURCES + diff --git a/TEMPLATE/template/configure.ac b/TEMPLATE/template/configure.ac new file mode 100644 index 00000000..b4e99781 --- /dev/null +++ b/TEMPLATE/template/configure.ac @@ -0,0 +1,72 @@ +dnl ---- configure.ac for __COMPONENT + +m4_include([../version.m4]) +AC_INIT(gambas3-__COMPONENT_DASH, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(__COMPONENT) +AC_PROG_LIBTOOL + +#if __USE_XWINDOW || __USE_QT +GB_CHECK_XWINDOW +#endif + +#if __USE_QT +GB_FIND_QT_MOC +#endif + +#if __USE_PKGCONFIG + +GB_COMPONENT_PKG_CONFIG( + __NAME, + __UNAME, + __COMPONENT, + [src], + __PKGCONFIG_NAME) + +#else + +#if __USE_C +#define __LIBRARY_C $C_LIB +#else +#define __LIBRARY_C +#endif +#if __USE_CPP +#define __LIBRARY_CPP $CXX_LIB +#else +#define __LIBRARY_CPP +#endif +#if __USE_THREAD +#define __LIBRARY_THREAD $THREAD_LIB +#define __INCLUDE_THREAD $THREAD_INC +#else +#define __LIBRARY_THREAD +#define __INCLUDE_THREAD +#endif + +GB_COMPONENT( + __NAME, + __UNAME, + __COMPONENT, + [src], +#ifdef __SEARCH_INCLUDE + [GB_FIND(__SEARCH_INCLUDE, __SEARCH_INCLUDE_PATH, __SEARCH_INCLUDE_DIR)], +#else + [], +#endif +#ifdef __SEARCH_LIBRARY + [GB_FIND(__SEARCH_LIBRARY, __SEARCH_LIBRARY_PATH, __SEARCH_LIBRARY_DIR)], +#else + [], +#endif + [__LIBRARY_C __LIBRARY_CPP __LIBRARY_THREAD __LIBRARY], + [__INCLUDE_THREAD __INCLUDE]) + +#endif + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/TEMPLATE/template/make-component b/TEMPLATE/template/make-component new file mode 100755 index 00000000..447880a0 --- /dev/null +++ b/TEMPLATE/template/make-component @@ -0,0 +1,3 @@ +$#$!/bin/sh +cd src +mv gb.xxx.component __COMPONENT.component diff --git a/TEMPLATE/template/src/.component b/TEMPLATE/template/src/.component new file mode 100644 index 00000000..10e636c3 --- /dev/null +++ b/TEMPLATE/template/src/.component @@ -0,0 +1,3 @@ +[Component] +Author= +Alpha=1 diff --git a/TEMPLATE/template/src/Makefile.am b/TEMPLATE/template/src/Makefile.am new file mode 100644 index 00000000..29c457f4 --- /dev/null +++ b/TEMPLATE/template/src/Makefile.am @@ -0,0 +1,12 @@ +COMPONENT = __COMPONENT +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = __COMPONENT.la + +__COMPONENT_UNDERSCORE##_la_LIBADD = #@#__UNAME##_LIB@ +__COMPONENT_UNDERSCORE##_la_LDFLAGS = -module @LD_FLAGS@ #@#__UNAME##_LDFLAGS@ +__COMPONENT_UNDERSCORE##_la_CPPFLAGS = #@#__UNAME##_INC@ + +__COMPONENT_UNDERSCORE##_la_SOURCES = __SOURCES + + diff --git a/TODO b/TODO new file mode 100644 index 00000000..dda0e0fd --- /dev/null +++ b/TODO @@ -0,0 +1,79 @@ +TODO list: always incomplete :-) +-------------------------------- + +COMPILER + +- Compilation error must always indicate the column, not sometimes only the line. + +INTERPRETER + +- A special syntax to make variable writable once only (for controls variables). +- Add global parameters for each component. Or no, a global configuration repository, to + tell, for example, which socket the mysql component must use... +- Make objects printable, with a "_print" hidden method or something else. +- Make objects writable and readable to streams. +- Type mismatch error: print the mismatched values. +- Special runtime debugging commands for dumping all the variables for example. +- A new debugging compilation option to only put line number information in the output files. +- If project compilation version < compiler version then => compile all +- Catch kill signal to remove temporary files. +- Sorting two or more arrays at the same time. +- MOD with floats. +- Make the error information associated with the current stack frame. +- Timer.TimeLeft. +- OPEN ... LOCK. True lock while reading/writing a file. +- select() system call can fail if a watched file descriptor is in error. But how can I know + the offending file descritor? Maybe by using poll() instead. +- Find a way to optimize the &= operator. + +DEBUGGER + +- Add/Remove a breakpoint without pausing the program! + +DEVELOPMENT ENVIRONMENT + +- Manage Object properties in IDE. +- Make a visual control clipboard. +- Generates an index control->component to suggest components for missing controls. +- Be able to open a .tar.gz project, and compress it back when the project is closed. +- Conditional breakpoints. +- Define a control order somewhere for the toolbox. +- An option to interpret "." and "," when reading float in a CSV import. +- Class template files. +- Redesign the source code navigator (F2 and SHIFT+F2). + +GUI RELATED STUFF + +- Deleting a currently expanding item in TreeView crashes. +- ValueBox.Value should be visible in the IDE. +- Moveable tabs. +- ListView and GridView selection interface should be the same. +- More clever vertical toolbar. + +DESKTOP COMPONENT + +- Detect an already running application. + +DATABASE COMPONENT + +- Add Views support in database component. +- Changeable client Charset in database driver. +- Returns the number of records affected by a query. +- A new database driver model. +- Copy a result line into another one. +- Use SAVEPOINT in postgresql to simulate nested transactions. + +DOCUMENTATION WIKI + +- Mass rename command. +- Fix last changes: only those in one language. +- A documentation page to explain the Gambas syntax. +- User comments on documentation pages. + +NETWORK COMPONENT + +- Do a big cleanup. + +COMPONENTS + +- Put version number in *.component files, and use it when making dependencies in the IDE. diff --git a/VERSION b/VERSION new file mode 100644 index 00000000..8531a3b7 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +3.12.2 diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 00000000..7be6518f --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,1591 @@ +####################################################################################### +## +## The following macros are specific to Gambas. +## Some of them are made by me (Benoît Minisini) +## Feel free to use these macros as you need ! +## +## IMPORTANT: This file is shared by all Gambas +## source packages +## +####################################################################################### + +## --------------------------------------------------------------------------- +## GB_MESSAGE +## Prints a message, and stores it in a summay file to print it later +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_MESSAGE], +[ + echo "|| $1" >> $srcdir/warnings.log +]) + +## --------------------------------------------------------------------------- +## GB_MESSAGE +## Prints a warning message, and stores it in a summay file to print it later +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_WARNING], +[ + AC_MSG_WARN($1) + GB_MESSAGE([$1]) +]) + +## --------------------------------------------------------------------------- +## GB_CLEAR_MESSAGES +## Clear summary +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_CLEAR_MESSAGES], +[ + rm -f $srcdir/warnings.log + touch $srcdir/warnings.log +]) + +## --------------------------------------------------------------------------- +## GB_PRINT_MESSAGES +## Print summary +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_PRINT_MESSAGES], +[ + if test -s $srcdir/warnings.log; then + echo + echo "||" + cat $srcdir/warnings.log + echo "||" + echo + fi + + if test -e FAILED && test "x${GAMBAS_CONFIG_FAILURE}" != "x"; then + AC_MSG_ERROR([Failed to configure $3]) + fi +]) + +## --------------------------------------------------------------------------- +## GB_INIT_AUTOMAKE +## automake initialization with common version number +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_INIT_AUTOMAKE], +[ + AM_INIT_AUTOMAKE([subdir-objects]) + m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(yes)]) + AC_CONFIG_HEADER([config.h]) + AC_PREFIX_DEFAULT(/usr) + + GAMBAS_VERSION=GB_VERSION_MAJOR + GAMBAS_MINOR_VERSION=GB_VERSION_MINOR + + AC_SUBST(GAMBAS_VERSION) + AC_SUBST(GAMBAS_MINOR_VERSION) + + AC_DEFINE(GAMBAS_VERSION, GB_VERSION_MAJOR, Gambas version) + AC_DEFINE(GAMBAS_MINOR_VERSION, GB_VERSION_MINOR, Gambas minor version) + + AC_DEFINE(GAMBAS_VERSION_STRING, "GB_VERSION_MAJOR", Gambas version string) + AC_DEFINE(GAMBAS_FULL_VERSION_STRING, "GB_VERSION_MAJOR.GB_VERSION_MINOR", Gambas full version string) + + AC_DEFINE(GAMBAS_FULL_VERSION, GB_VERSION_FULL, [Full Gambas version]) + AC_DEFINE(GAMBAS_PCODE_VERSION, GB_PCODE_VERSION, [Gambas bytecode version]) + AC_DEFINE(GAMBAS_PCODE_VERSION_MIN, GB_PCODE_VERSION_MIN, [Minimum Gambas bytecode version]) + + GB_CLEAR_MESSAGES +]) + +## --------------------------------------------------------------------------- +## GB_CONFIG_SUBDIRS +## configuration of a component sub-directory, with a flag for disabling it +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_CONFIG_SUBDIRS], +[ + AC_ARG_ENABLE( + $1, + [ --enable-$1 enable $1 component (default: yes)], + gb_enable_$1=$enableval, + gb_enable_$1=yes + ) + + if test "$gb_enable_$1" = "yes"; then + if test -d $srcdir/$2; then + AC_CONFIG_SUBDIRS($2) + $1_dir=$2 + fi + else + GB_WARNING([$1 component is disabled by configure option]) + $1_dir="" + fi + + AC_SUBST($1_dir) +]) + +## --------------------------------------------------------------------------- +## GB_INIT_SHORT GB_INIT GB_LIBTOOL +## configure.ac initialization +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_INIT_SHORT], +[ + AC_CONFIG_SRCDIR([configure.ac]) + AM_MAINTAINER_MODE + + COMPONENT=$1 + + GB_INIT_AUTOMAKE + + AC_CANONICAL_HOST + + gbbindir=$bindir/gambas$GAMBAS_VERSION + AC_SUBST(gbbindir) + gblibdir=$libdir/gambas$GAMBAS_VERSION + AC_SUBST(gblibdir) + gbdatadir=$datadir/gambas$GAMBAS_VERSION + AC_SUBST(gbdatadir) + + AC_PROG_INSTALL + AC_PROG_LN_S +]) + +AC_DEFUN([GB_LIBTOOL], +[ + AC_LIBTOOL_DLOPEN + ##AC_LIBLTDL_CONVENIENCE + AC_LIBTOOL_WIN32_DLL + AC_DISABLE_STATIC + + AC_SUBST(INCLTDL) + AC_SUBST(LIBLTDL) + + dnl LD_FLAGS="-Wl,-O1" + if test $SYSTEM == "CYGWIN"; then + LD_FLAGS="$LD_FLAGS -no-undefined" + fi + AC_SUBST(LD_FLAGS) +]) + +AC_DEFUN([GB_INIT], +[ + GB_INIT_SHORT($1) + GB_SYSTEM + GB_LIBTOOL + + dnl ---- Checks for headers needed by the following tests + + AC_CHECK_HEADERS(unistd.h) + + dnl ---- Checks for header files. + + dnl AC_HEADER_DIRENT + dnl AC_HEADER_STDC + dnl AC_HEADER_SYS_WAIT + + dnl ---- Checks for typedefs, structures, and compiler characteristics. + + dnl AC_C_CONST + dnl AC_TYPE_PID_T + dnl AC_TYPE_SIZE_T + dnl AC_HEADER_TIME + dnl AC_STRUCT_TM + dnl AC_C_LONG_DOUBLE + + dnl ---- Checks for library functions. + + dnl AC_FUNC_ALLOCA + dnl AC_PROG_GCC_TRADITIONAL + dnl AC_TYPE_SIGNAL + dnl AC_FUNC_STRCOLL + dnl AC_FUNC_STRFTIME + dnl AC_FUNC_VPRINTF + dnl AC_FUNC_WAIT3 + dnl AC_CHECK_FUNCS(getcwd gettimeofday mkdir rmdir select socket strdup strerror strtod strtol sysinfo) + + AC_CHECK_FUNCS(setenv unsetenv getdomainname getpt cfmakeraw fstatat) + + dnl ---- Checks for libraries + + dnl AC_CHECK_LIB(m, main, echo) + dnl AC_CHECK_LIB(z, main, echo) + + GB_LIBC + + dnl ---- Check for C++ libraries + + AC_CHECK_LIB(gcc_s, main, CXX_LIB="$CXX_LIB -lgcc_s") + AC_CHECK_LIB(stdc++, main, CXX_LIB="$CXX_LIB -lstdc++") + + AC_SUBST(CXX_LIB) + + dnl ---- Check for shared library extension + + GB_SHARED_LIBRARY_EXT() + + dnl ---- Check for threading + + GB_THREAD() + + dnl ---- Check for mathematic libraries + + GB_MATH() + + dnl ---- Check for gettext library + + GB_GETTEXT() + + dnl ---- Check for inotify library + + GB_INOTIFY() + + dnl ---- Check for monotonic clock + + GB_MONOTONIC() + + dnl ---- Support for colorgcc + dnl ---- WARNING: libtool does not support colorgcc! + + dnl AC_PATH_PROG(COLORGCC, colorgcc) + + if test x"$COLORGCC" != x; then + if test "$gambas_colorgcc" = "yes"; then + CC="colorgcc" + CXX="g++" + fi + fi + + dnl ---- Support for ccache + + AC_ARG_ENABLE( + ccache, + [ --enable-ccache use ccache if present (default: yes)], + gambas_ccache=$enableval, + gambas_ccache=yes + ) + + AC_PATH_PROG(CCACHE, ccache) + + if test "$gambas_colorgcc" = "yes"; then + if test x"$CCACHE" != x; then + + CC="ccache $CC" + CXX="ccache $CXX" + + if test x"$COLORGCC" != x; then + if test "$gambas_colorgcc" = "yes"; then + CC="colorgcc" + CXX="colorgcc" + fi + fi + + fi + fi + + dnl ---- debug option + + AC_ARG_ENABLE( + debug, + [ --enable-debug compile for debugging (default: yes)], + gambas_debug=$enableval, + gambas_debug=yes + ) + + AM_CONDITIONAL(DEBUG, test "$gambas_debug" = yes) + + dnl ---- optimization option + + AC_ARG_ENABLE( + optimization, + [ --enable-optimization compile with optimizations (default: yes)], + gambas_optimization=$enableval, + gambas_optimization=yes + ) + + AM_CONDITIONAL(OPTIMIZE, test "$gambas_optimization" = yes) + + AM_CFLAGS="$AM_CFLAGS -pipe -Wall -Wno-unused-value -fsigned-char" + if test $SYSTEM = "MACOSX"; then + AM_CFLAGS="$AM_CFLAGS -fnested-functions" + fi + + AM_CXXFLAGS="$AM_CXXFLAGS -pipe -Wall -fno-exceptions -Wno-unused-value -fsigned-char" + + dnl ---- Check for gcc visibility flag + + have_gcc_visibility=no + + if test $SYSTEM != "CYGWIN"; then + GB_CFLAGS_GCC_OPTION([-fvisibility=hidden],, + [ + AM_CFLAGS="$AM_CFLAGS -fvisibility=hidden" + AM_CXXFLAGS="$AM_CXXFLAGS -fvisibility=hidden" + have_gcc_visibility=yes]) + fi + + if test "$have_gcc_visibility" = "yes"; then + AC_DEFINE(HAVE_GCC_VISIBILITY, 1, [Whether gcc supports -fvisibility=hidden]) + fi + + dnl ---- check for -flto compiler flag + + GB_CFLAGS_GCC_OPTION([-flto],, + [ + GB_CFLAGS_LTO=" -flto" + have_gcc_lto=yes + ]) + + AC_ARG_ENABLE( + lto, + [ --enable-lto enable link time optimization (default: no)], + gambas_lto=$enableval, + gambas_lto=no + ) + + if test "$gambas_lto" = "no"; then + have_gcc_lto=no; + GB_CFLAGS_LTO=""; + fi + + if test "$have_gcc_lto" = "yes"; then + AC_DEFINE(HAVE_GCC_LTO, 1, [Whether gcc supports -flto]) + fi + + dnl ---- check for -std=c++11 compiler flag + + GB_CXXFLAGS_GCC_OPTION([-std=c++11],, + [ + GB_CXXFLAGS_STD_CPP11=" -std=c++11" + have_gcc_std_cpp11x=yes + ]) + + if test "$have_gcc_std_cpp11" = "yes"; then + AC_DEFINE(HAVE_GCC_STD_CPP11, 1, [Whether g++ supports -std=c++11]) + fi + + dnl ---- Debug flags + + if test "$gambas_debug" = "yes"; then + AM_CFLAGS="$AM_CFLAGS -g -ggdb" + AM_CXXFLAGS="$AM_CXXFLAGS -g -ggdb" + fi + + dnl ---- Optimization flags + + if test "x$gambas_optimization" = "xyes"; then + AM_CFLAGS_OPT="$AM_CFLAGS -O3" + AM_CFLAGS="$AM_CFLAGS -Os" + AM_CXXFLAGS_OPT="$AM_CXXFLAGS -O3 -fno-omit-frame-pointer" + AM_CXXFLAGS="$AM_CXXFLAGS -Os -fno-omit-frame-pointer" + else + AM_CFLAGS_OPT="$AM_CFLAGS -O0" + AM_CFLAGS="$AM_CFLAGS -O0" + AM_CXXFLAGS_OPT="$AM_CXXFLAGS -O0" + AM_CXXFLAGS="$AM_CXXFLAGS -O0" + fi + + CFLAGS="" + CXXFLAGS="" + + dnl ---- Checks for programs + + AC_PROG_CPP + AC_PROG_CXX + AC_PROG_CC + AC_PROG_MAKE_SET + + AC_SUBST(AM_CFLAGS) + AC_SUBST(AM_CFLAGS_OPT) + AC_SUBST(AM_CXXFLAGS) + AC_SUBST(AM_CXXFLAGS_OPT) + AC_SUBST(GB_CFLAGS_LTO) + AC_SUBST(GB_CXXFLAGS_STD_CPP11) + + rm -f DISABLED DISABLED.* FAILED +]) + + +## --------------------------------------------------------------------------- +## GB_THREAD +## Detect threading compiler options +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_THREAD], +[ + case "${host}" in + *-*-freebsd* | *-*-netbsd* | *-*-darwin* ) + THREAD_LIB="" + THREAD_INC="-pthread -D_REENTRANT" + GBX_THREAD_LIB="" + GBX_THREAD_INC="-pthread -D_REENTRANT" + GBX_THREAD_LDFLAGS="" + ;; + *-*-haiku* ) + THREAD_LIB="" + THREAD_INC="" + GBX_THREAD_LIB="" + GBX_THREAD_INC="" + GBX_THREAD_LDFLAGS="" + ;; + *) + THREAD_LIB="-lpthread" + THREAD_INC="-D_REENTRANT" + GBX_THREAD_LIB="-lpthread" + GBX_THREAD_INC="-D_REENTRANT" + GBX_THREAD_LDFLAGS="-Wl,--no-as-needed" + ;; + esac + + AC_MSG_CHECKING(for threading compiler options) + AC_MSG_RESULT($THREAD_INC) + AC_MSG_CHECKING(for threading linker options) + AC_MSG_RESULT($THREAD_LIB) + + AC_SUBST(THREAD_LIB) + AC_SUBST(THREAD_INC) + AC_SUBST(GBX_THREAD_LIB) + AC_SUBST(GBX_THREAD_INC) + AC_SUBST(GBX_THREAD_LDFLAGS) +]) + + +## --------------------------------------------------------------------------- +## GB_LIBC +## Detect C library +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_LIBC], +[ + case "${host}" in + *-*-haiku* ) + dnl Haiku has implicit C library in libroot. + C_LIB="" + ;; + *) + C_LIB="-lc" + ;; + esac + + AC_MSG_CHECKING(for C library) + AC_MSG_RESULT($C_LIB) + + AC_SUBST(C_LIB) +]) + + +## --------------------------------------------------------------------------- +## GB_MATH +## Detect mathematic libraries +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_MATH], +[ + case "${host}" in + *-*-haiku* ) + MATH_LIB="" + ;; + *) + MATH_LIB="-lm" + ;; + esac + + AC_MSG_CHECKING(for mathematic libraries) + AC_MSG_RESULT($MATH_LIB) + + AC_SUBST(MATH_LIB) +]) + + +## --------------------------------------------------------------------------- +## GB_CHECK_MATH_FUNC +## Check a specific mathematical function +## +## $1 = name of the function +## $2 = macro to define +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_CHECK_MATH_FUNC], +[AC_CACHE_CHECK(for $1, + gb_cv_math_$1, + [AC_TRY_COMPILE( + [ + #define _ISOC9X_SOURCE 1 + #define _ISOC99_SOURCE 1 + #define __USE_ISOC99 1 + #define __USE_ISOC9X 1 + #include + ], + [ + int value = $1 (1.0); + ], + gb_cv_math_$1=yes, gb_cv_math_$1=no + )]) + + if test $gb_cv_math_$1 = yes; then + AC_DEFINE(HAVE_$2, 1, [Define if you have $1 function.]) + fi +]) + +## --------------------------------------------------------------------------- +## GB_MATH_FUNC +## Detect which mathematical functions are available +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_MATH_FUNC], +[ + ac_save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -$MATH_LIB" + + GB_CHECK_MATH_FUNC(exp10, EXP10) + GB_CHECK_MATH_FUNC(exp2, EXP2) + GB_CHECK_MATH_FUNC(log2, LOG2) + + LDFLAGS=$ac_save_LDFLAGS +]) + + +## --------------------------------------------------------------------------- +## GB_SYSTEM +## Detects the target system and its architecture +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_SYSTEM], +[ + AC_MSG_CHECKING(target system) + + case "${host}" in + *-*-linux*-gnu* ) + SYSTEM=LINUX + AC_DEFINE(OS_GNU, 1, [Target system is of GNU family]) + AC_DEFINE(OS_LINUX, 1, [Target system is Linux]) + AC_DEFINE(SYSTEM, "Linux", [Operating system]) + ;; + *-*-linux* ) + SYSTEM=LINUX + AC_DEFINE(OS_LINUX, 1, [Target system is Linux]) + AC_DEFINE(SYSTEM, "Linux", [Operating system]) + ;; + *-*-freebsd* ) + SYSTEM=FREEBSD + AC_DEFINE(OS_BSD, 1, [Target system is of BSD family]) + AC_DEFINE(OS_FREEBSD, 1, [Target system is FreeBSD]) + AC_DEFINE(SYSTEM, "FreeBSD", [Operating system]) + ;; + *-*-netbsd* ) + SYSTEM=NETBSD + AC_DEFINE(OS_BSD, 1, [Target system is of BSD family]) + AC_DEFINE(OS_NETBSD, 1, [Target system is NetBSD]) + AC_DEFINE(SYSTEM, "NetBSD", [Operating system]) + ;; + *-*-openbsd* ) + SYSTEM=OPENBSD + AC_DEFINE(OS_BSD, 1, [Target system is of BSD family]) + AC_DEFINE(OS_OPENBSD, 1, [Target system is OpenBSD]) + AC_DEFINE(SYSTEM, "OpenBSD", [Operating system]) + ;; + *-*-cygwin* ) + SYSTEM=CYGWIN + AC_DEFINE(OS_CYGWIN, 1, [Target system is Cygwin/Windows]) + AC_DEFINE(SYSTEM, "Cygwin", [Operating system]) + ;; + *-*-darwin* | *-*-rhapsody* ) + SYSTEM=MACOSX + AC_DEFINE(OS_BSD, 1, [Target system is of BSD family]) + AC_DEFINE(OS_FREEBSD, 1, [Target system is FreeBSD]) + AC_DEFINE(OS_MACOSX, 1, [Target system is MacOS X]) + AC_DEFINE(SYSTEM, "MacOSX", [Operating system]) + ;; + *-*-solaris* ) + SYSTEM=SOLARIS + AC_DEFINE(OS_SOLARIS, 1, [Target system is Solaris]) + AC_DEFINE(SYSTEM, "Solaris", [Operating system]) + ;; + *-*-k*bsd*-gnu* ) + SYSTEM=KFREEBSD + AC_DEFINE(OS_BSD, 1, [Target system is of BSD family]) + AC_DEFINE(OS_GNU, 1, [Target system is of GNU family]) + AC_DEFINE(OS_KFREEBSD, 1, [Target system is kFREEBSD]) + AC_DEFINE(SYSTEM, "kFreeBSD", [Operating system]) + ;; + *-gnu* ) + SYSTEM=HURD + AC_DEFINE(OS_GNU, 1, [Target system is of GNU family]) + AC_DEFINE(OS_HURD, 1, [Target system is Hurd]) + AC_DEFINE(SYSTEM, "Hurd", [Operating system]) + ;; + *-*-haiku* ) + SYSTEM=HAIKU + dnl AC_DEFINE(OS_GNU, 1, [Target system is of GNU family]) + AC_DEFINE(OS_HAIKU, 1, [Target system is Haiku]) + AC_DEFINE(SYSTEM, "Haiku", [Operating system]) + ;; + * ) + SYSTEM=UNKNOWN + AC_DEFINE(SYSTEM, "unknown", [Operating system]) + GB_MESSAGE([System is unknown]) + ;; + esac + + AC_MSG_RESULT($SYSTEM) + + AC_MSG_CHECKING(target architecture) + + case "${host}" in + i*86-*-* ) + ARCH=X86 + AC_DEFINE(ARCH_X86, 1, [Target architecture is x86]) + AC_DEFINE(ARCHITECTURE, "x86", [Architecture]) + ;; + x86_64-*-* | amd64-* | ia64-* ) + ARCH=X86_64 + AC_DEFINE(ARCH_X86_64, 1, [Target architecture is x86_64]) + AC_DEFINE(ARCHITECTURE, "x86_64", [Architecture]) + ;; + arm*-*-* ) + ARCH=ARM + AC_DEFINE(ARCH_ARM, 1, [Target architecture is ARM]) + AC_DEFINE(ARCHITECTURE, "arm", [Architecture]) + ;; + powerpc*-*-* ) + ARCH=PPC + AC_DEFINE(ARCH_PPC, 1, [Target architecture is PowerPC]) + AC_DEFINE(ARCHITECTURE, "powerpc", [Architecture]) + ;; + *) + ARCH=UNKNOWN + AC_DEFINE(ARCHITECTURE, "unknown", [Architecture]) + GB_MESSAGE([Architecture is unknown]) + ;; + esac + + AC_MSG_RESULT($ARCH) +]) + + +## --------------------------------------------------------------------------- +## GB_SHARED_LIBRARY_EXT +## Detects shared library extension +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_SHARED_LIBRARY_EXT], +[ + AC_MSG_CHECKING(which extension is used for shared libraries) + + case "${host}" in + *-*-cygwin* ) + SHLIBEXT="dll.a" + AC_DEFINE(SHARED_LIBRARY_EXT, "dll", [Shared library extension is '.dll.a']) + ;; + *-*-darwin* ) + SHLIBEXT="dylib" + AC_DEFINE(SHARED_LIBRARY_EXT, "dylib", [Shared library extension is '.dylib']) + ;; + *) + SHLIBEXT="so" + AC_DEFINE(SHARED_LIBRARY_EXT, "so", [Shared library extension is '.so']) + ;; + esac + + AC_SUBST(SHLIBEXT) + + AC_MSG_RESULT([.$SHLIBEXT]) +]) + + +## --------------------------------------------------------------------------- +## GB_GETTEXT +## Detects if we must link to an external gettext library +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_GETTEXT], +[ + AC_MSG_CHECKING(for external gettext library) + + case "${host}" in + *-*-openbsd* ) + GETTEXT_LIB=-llibgettext + ;; + *) + GETTEXT_LIB= + ;; + esac + + AC_SUBST(GETTEXT_LIB) + AC_SUBST(GETTEXT_LDFLAGS) + + AC_MSG_RESULT($GETTEXT_LIB) +]) + + +## --------------------------------------------------------------------------- +## GB_INOTIFY +## Detects if we must link to an external inotify library +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_INOTIFY], +[ + AC_MSG_CHECKING(for external inotify library) + + case "${host}" in + *-*-linux* ) + GB_INOTIFY_LIB= + ;; + *) + GB_INOTIFY_LIB=-linotify + ;; + esac + + AC_SUBST(GB_INOTIFY_LIB) + AC_MSG_RESULT($GB_INOTIFY_LIB) +]) + + +## --------------------------------------------------------------------------- +## GB_MONOTONIC +## Detect monotonic clock +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_MONOTONIC], +[ + AC_CACHE_CHECK(for monotonic clock, gb_cv_monotonic_clock, + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + #ifdef HAVE_UNISTD_H + #include + #endif + ]], [[ + #if !defined(_POSIX_MONOTONIC_CLOCK) || _POSIX_MONOTONIC_CLOCK < 0 || !defined(CLOCK_MONOTONIC) + #error Either _POSIX_MONOTONIC_CLOCK or CLOCK_MONOTONIC not defined + #endif + ]])],[ + gb_cv_monotonic_clock=yes + ],[ + gb_cv_monotonic_clock=no + ]) + ) + + if test "$gb_cv_monotonic_clock" = "yes"; then + + AC_DEFINE(HAVE_MONOTONIC_CLOCK,1,[Have a monotonic clock]) + + ac_save_LIBS="$LIBS" + AC_SEARCH_LIBS(clock_gettime, rt) + RT_LIB=$LIBS + LIBS=$ac_save_LIBS + + fi + + AC_SUBST(RT_LIB) + AC_SUBST(RT_LDFLAGS) +]) + + + + +## --------------------------------------------------------------------------- +## GB_FIND +## Find files in directories +## +## $1 = Files to search +## $2 = Directories +## $3 = Sub-directories patterns +## +## Returns a path list in $gb_val +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_FIND], +[ +dnl echo "Searching $1, $2, $3" +gb_val="" +gb_save=`pwd` +gb_file_list="$1" + +gb_main_dir_list="$2" +gb_sub_dir_list="$3" + +gb_sub_dir_list_64=`echo "$gb_sub_dir_list" | sed s/"lib"/"lib64"/g` + +if test $SYSTEM == "HAIKU"; then + gb_arch="`getarch`" + gb_main_dir_list="$gb_main_dir_list `findpaths -c' ' -a "$gb_arch" B_FIND_PATH_DEVELOP_DIRECTORY`" + gb_arch_inc_subdir=headers + gb_arch_lib_subdir=lib + if test "$gb_arch" != "`getarch -p`"; then + gb_arch_inc_subdir="headers/$gb_arch" + gb_arch_lib_subdir="lib/$gb_arch" + fi + gb_sub_dir_list=`echo "$gb_sub_dir_list" | sed "s:include:$gb_arch_inc_subdir:g;s:lib:$gb_arch_lib_subdir:g"` +fi + +## if there is 'lib' inside sub-directories, then we decide to search "lib64" first. + +if test "$gb_sub_dir_list_64" != "$gb_sub_dir_list"; then + gb_sub_dir_list="$gb_sub_dir_list_64 $gb_sub_dir_list"; + + gb_main_dir_list_64=`echo "$gb_main_dir_list" | sed s/"lib"/"lib64"/g` + + if test "$gb_main_dir_list_64" != "$gb_main_dir_list"; then + gb_main_dir_list="$gb_main_dir_list_64 $gb_main_dir_list"; + fi + +fi + +for gb_main_dir in $gb_main_dir_list; do + dnl echo "search $gb_main_dir" + if test -d $gb_main_dir; then + cd $gb_main_dir + for gb_search_dir in $gb_sub_dir_list; do + for gb_dir in $gb_search_dir/ $gb_search_dir/*/ $gb_search_dir/*/*/ $gb_search_dir/*/*/*/; do + + dnl echo "search subdir $gb_dir" + gb_new_file_list="" + gb_find_dir="" + + for gb_file in $gb_file_list; do + + dnl echo "search file $gb_file" + gb_find=no + if test -r "$gb_main_dir/$gb_dir/$gb_file" || test -d "$gb_main_dir/$gb_dir/$gb_file"; then + + ifelse($4,[], + + gb_find=yes, + + for gb_test in $4; do + gb_output=`ls -la $gb_main_dir/$gb_dir/$gb_file | grep "$gb_test"` + if test "x$gb_output" != "x"; then + gb_find=yes + fi + done + ) + + fi + + if test "$gb_find" = "yes"; then + dnl echo "FOUND!" + if test "x$gb_find_dir" = "x"; then + if test "x$gb_val" = "x"; then + gb_val="$gb_main_dir/$gb_dir" + else + gb_val="$gb_val $gb_main_dir/$gb_dir" + fi + fi + gb_find_dir=yes + else + gb_new_file_list="$gb_new_file_list $gb_file" + fi + + done + + gb_file_list=$gb_new_file_list + + if test "x$gb_file_list" = "x " || test "x$gb_file_list" = "x"; then + break 3 + fi + + done + done + fi +done + +if test "x$gb_file_list" != "x " && test "x$gb_file_list" != "x"; then + gb_val=no +fi + +cd $gb_save +]) + + +## --------------------------------------------------------------------------- +## GB_COMPONENT_PKG_CONFIG +## Component detection macro based on pkg-config +## +## $1 = Component key in lower case (ex: pgsql) +## $2 = Component key in upper case (ex: PGSQL) +## $3 = Component name (ex: gb.db.postgresql) +## $4 = Sub-directory name +## $5 = pkg-config module(s) name(s) with optional required version(s) +## $6 = Warning message (optional) +## +## => defines HAVE_*_COMPONENT (to know if you can compile the component) +## *_INC (for the compiler) and *_LIB / *_LDFLAGS (for the linker) +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_COMPONENT_PKG_CONFIG], +[ + AC_ARG_ENABLE( + $1, + [ --enable-$1 enable $3 (default: yes)], + gb_enable_$1=$enableval, + gb_enable_$1=yes + ) + + dnl AC_ARG_WITH($1-includes, + dnl [ --with-$1-includes where the $3 headers are located. ], + dnl [ gb_inc_$1="$withval" ]) + + dnl AC_ARG_WITH($1-libraries, + dnl [ --with-$1-libraries where the $3 libraries are located. ], + dnl [ gb_lib_$1="$withval" ]) + + have_$1=no + + if test "$gb_enable_$1" = "yes" && test ! -e DISABLED && test ! -e DISABLED.$3; then + + AC_MSG_CHECKING(for $3 component with pkg-config) + + gb_inc_$1="" + gb_lib_$1="" + gb_ldflags_$1="" + have_$1=yes + gb_testval="" + + pkg-config --silence-errors --exists $5 + if test $? -eq "0"; then + + ## Checking for headers + + $2_INC="`pkg-config --cflags $5`" + + ## Checking for libraries + + $2_LIB="`pkg-config --libs-only-l $5`" + $2_LDFLAGS="`pkg-config --libs-only-L $5` `pkg-config --libs-only-other $5`" + $2_DIR=$4 + + else + + have_$1=no + + fi + + fi + + if test "$have_$1" = "no"; then + + if test "$gb_in_component_search" != "yes"; then + touch DISABLED.$3 + if test "$gb_enable_$1" = "yes"; then + touch FAILED + fi + fi + + if test "$gb_enable_$1" = "yes"; then + AC_MSG_RESULT(no) + fi + + for pkgcmp in $5 + do + + pkg-config --silence-errors --exists $pkgcmp + if test $? -eq "1"; then + GB_WARNING([Unable to met pkg-config requirement: $pkgcmp]) + fi + + done + + else + + AC_DEFINE(HAVE_$2_COMPONENT, 1, [Have $3 component]) + + AC_MSG_RESULT(OK) + + fi + + if test "$have_$1" = "no"; then + + $2_INC="" + $2_LIB="" + $2_LDFLAGS="" + $2_DIR="" + if test "$gb_in_component_search" != "yes"; then + if test x"$6" = x; then + GB_WARNING([$3 is disabled]) + else + GB_WARNING([$6]) + fi + fi + + fi + + AC_SUBST($2_INC) + AC_SUBST($2_LIB) + AC_SUBST($2_LDFLAGS) + AC_SUBST($2_DIR) +]) + + +## --------------------------------------------------------------------------- +## GB_COMPONENT +## Component detection macro that searches for files +## +## $1 = Component key in lower case (ex: postgresql) +## $2 = Component key in upper case (ex: POSTGRESQL) +## $3 = Component name (ex: gb.db.postgresql) +## $4 = Sub-directory name +## $5 = How to get include path (must return it in gb_val) +## $6 = How to get library path (must return it in gb_val) +## $7 = Libraries +## $8 = Compiler flags (optional) +## $9 = Warning message (optional) +## +## => defines HAVE_*_COMPONENT (to know if you can compile the component) +## *_INC (for the compiler) and *_LIB (for the linker) +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_COMPONENT], +[ + AC_ARG_ENABLE( + $1, + [ --enable-$1 enable $3 (default: yes)], + gb_enable_$1=$enableval, + gb_enable_$1=yes + ) + + gb_inc_$1=no + gb_lib_$1=no + + if test "$gb_enable_$1" = "yes" && test ! -e DISABLED && test ! -e DISABLED.$3; then + + ## Checking for headers + + AC_MSG_CHECKING(for $3 headers) + + AC_ARG_WITH($1-includes, + [ --with-$1-includes where the $3 headers are located. ], + [ gb_inc_$1="$withval" ]) + + AC_CACHE_VAL(gb_cv_header_$1, [ + + if test "$gb_inc_$1" = no; then + gb_val="" + $5 + gb_inc_$1=$gb_val + fi + + gb_cv_header_$1=$gb_inc_$1 + ]) + + AC_MSG_RESULT([$gb_cv_header_$1]) + + if test "$gb_cv_header_$1" = "no"; then + for gb_result in $gb_file_list; do + GB_WARNING([Unable to find file: $gb_result]) + done + fi + + $2_INC="" + + for gb_dir in $gb_cv_header_$1; do + if test "$gb_dir" != "/usr/include"; then + if test "$gb_dir" != "/usr/include/"; then + $2_INC="$$2_INC -I$gb_dir" + fi + fi + done + + if test "x$8" != "x"; then + $2_INC="$$2_INC $8" + fi + + if test "$gb_cv_header_$1" = no; then + have_inc_$1="no" + $2_INC="" + else + have_inc_$1="yes" + fi + + ## Checking for libraries + + AC_MSG_CHECKING(for $3 libraries) + + AC_ARG_WITH($1-libraries, + [ --with-$1-libraries where the $3 libraries are located. ], + [ gb_lib_$1="$withval" ]) + + AC_CACHE_VAL(gb_cv_lib_$1, [ + + if test "$gb_lib_$1" = no; then + gb_val="" + $6 + gb_lib_$1=$gb_val + fi + + gb_cv_lib_$1=$gb_lib_$1 + ]) + + if test "$gb_cv_lib_$1" = no; then + have_lib_$1="no" + else + have_lib_$1="yes" + fi + + AC_MSG_RESULT([$gb_cv_lib_$1]) + + if test "$gb_cv_lib_$1" = "no"; then + for gb_result in $gb_file_list; do + GB_WARNING([Unable to find file: $gb_result]) + done + fi + + $2_LIB="" + $2_LDFLAGS="" + $2_PATH="" + + for gb_dir in $gb_cv_lib_$1; do + if test "x$$2_PATH" = "x"; then + $2_PATH="$gb_dir/.." + fi + if test "$gb_dir" != "/lib" && test "$gb_dir" != "/lib/"&& test "$gb_dir" != "/usr/lib" && test "$gb_dir" != "/usr/lib/"; then + $2_LDFLAGS="$$2_LDFLAGS -L$gb_dir"; + fi + done + + $2_LIB="$$2_LIB $7" + + fi + + if test "$have_inc_$1" = "yes" && test "$have_lib_$1" = "yes"; then + + have_$1=yes + $2_DIR=$4 + AC_DEFINE(HAVE_$2_COMPONENT, 1, Have $3) + + else + + have_$1=no + touch DISABLED.$3 + if test "$gb_enable_$1" = "yes"; then + touch FAILED + fi + + fi + + if test "$have_$1" = "no"; then + + $2_INC="" + $2_LIB="" + $2_DIR="" + $2_LDFLAGS="" + if test x"$9" = x; then + GB_WARNING([$3 is disabled]) + else + GB_WARNING([$9]) + fi + + fi + + AC_SUBST($2_INC) + AC_SUBST($2_LIB) + AC_SUBST($2_LDFLAGS) + AC_SUBST($2_DIR) + AC_SUBST($2_PATH) + +]) + + +## --------------------------------------------------------------------------- +## GB_COMPONENT_SEARCH +## Component detection macro that uses GB_COMPONENT_PKG_CONFIG first, and +## then GB_COMPONENT. +## +## $1 = Component key in lower case (ex: postgresql) +## $2 = Component key in upper case (ex: POSTGRESQL) +## $3 = Component name (ex: PostgreSQL) +## $4 = Sub-directory name +## $5 = pkg-config module name (optional) +## $6 = How to get include path (must return it in gb_val) +## $7 = How to get library path (must return it in gb_val) +## $8 = Libraries +## $9 = Compiler flags (optional) +## $10 = Warning message (optional) +## +## => defines HAVE_*_COMPONENT (to know if you can compile the component) +## *_INC (for the compiler) and *_LIB (for the linker) +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_COMPONENT_SEARCH], +[ +gb_in_component_search=yes + GB_COMPONENT_PKG_CONFIG( + $1, + $2, + $3, + $4, + $5, + $10 + ) +gb_in_component_search=no + if test -z "${$2_LIB}"; then + GB_COMPONENT( + $1, + $2, + $3, + $4, + $6, + $7, + $8, + $9, + $10 + ) + fi +]) + + +## --------------------------------------------------------------------------- +## GB_FIND_QT_MOC +## Find QT moc compiler +## +## $1 = QT version +## $2 = components to disable +## +## Returns a path list in $gb_val +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_FIND_QT_MOC], +[ + gb_path_qt_moc=no + if test x$1 = x; then + gb_qt_version=3 + else + gb_qt_version=$1 + fi + + AC_ARG_WITH(moc, + [ --with-moc The path to the QT moc compiler. ], + [ gb_path_qt_moc="$withval" ]) + + AC_MSG_CHECKING(for QT meta-object compiler) + + AC_CACHE_VAL(gb_cv_path_qt_moc, [ + + gb_val="" + if test "$gb_path_qt_moc" = no; then + + for gb_dir in $QTDIR /usr/lib/qt$gb_qt_version /usr/lib/qt/$gb_qt_version /usr/local/lib/qt$gb_qt_version /usr/local/lib/qt/$gb_qt_version /usr/local/qt$gb_qt_version /usr/local/qt/$gb_qt_version /usr/share/qt$gb_qt_version /usr/qt/$gb_qt_version /usr/pkg/qt$gb_qt_version /usr/pkg /usr; do + + gb_dir=$gb_dir/bin + + if test -r "$gb_dir/moc"; then + if test "x`$gb_dir/moc -v 2>&1 | grep " $gb_qt_version\."`" != x; then + gb_val=$gb_dir/moc + break + fi + fi + + done + + gb_path_qt_moc=$gb_val + fi + + gb_cv_path_qt_moc=$gb_path_qt_moc + ]) + + AC_MSG_RESULT([$gb_cv_path_qt_moc]) + + if test x"$gb_cv_path_qt_moc" = x; then + GB_WARNING([QT moc compiler not found. Try --with-moc option.]) + MOC="" + touch DISABLED + else + MOC=$gb_cv_path_qt_moc + fi + + AC_SUBST(MOC) +]) + +## --------------------------------------------------------------------------- +## GB_CHECK_XWINDOW +## Check the X-Window system installation +## +## $1 = components to disable +## --------------------------------------------------------------------------- + +AC_DEFUN([GB_CHECK_XWINDOW], +[ + AC_PATH_XTRA + + if test x"$have_x" = xyes; then + if test -z `echo $X_LIBS | grep "\-lX11"`; then + X_LIBS="$X_LIBS -lX11" + fi + if test -z `echo $X_LIBS | grep "\-lXext"`; then + X_LIBS="$X_LIBS -lXext" + fi + X_LIBS="$X_PRE_LIBS $X_LIBS" + else + touch DISABLED + fi + +]) + +## --------------------------------------------------------------------------- +## Some macros +## --------------------------------------------------------------------------- + +dnl Like AC_CHECK_HEADER, but it uses the already-computed -I directories. + +AC_DEFUN([AC_CHECK_X_HEADER], [ + ac_save_CPPFLAGS="$CPPFLAGS" + if test \! -z "$includedir" ; then + CPPFLAGS="$CPPFLAGS -I$includedir" + fi + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + AC_CHECK_HEADER([$1],[$2],[$3]) + CPPFLAGS="$ac_save_CPPFLAGS" +]) + +dnl Like AC_CHECK_LIB, but it used the -L dirs set up by the X checks. + +AC_DEFUN([AC_CHECK_X_LIB], [ + ac_save_CPPFLAGS="$CPPFLAGS" + ac_save_LDFLAGS="$LDFLAGS" + + if test \! -z "$includedir" ; then + CPPFLAGS="$CPPFLAGS -I$includedir" + fi + + dnl note: $X_CFLAGS includes $x_includes + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + + if test \! -z "$libdir" ; then + LDFLAGS="$LDFLAGS -L$libdir" + fi + + dnl note: $X_LIBS includes $x_libraries + + LDFLAGS="$LDFLAGS $X_LIBS" + AC_CHECK_LIB([$1], [$2], [$3], [$4], [$5]) + CPPFLAGS="$ac_save_CPPFLAGS" + LDFLAGS="$ac_save_LDFLAGS"] +) + +dnl Check if it is possible to turn off run time type information (RTTI) +AC_DEFUN([AC_PROG_CXX_FNO_RTTI], +[AC_CACHE_CHECK(whether ${CXX-g++} accepts -fno-rtti, ac_cv_prog_cxx_fno_rtti, +[echo 'void f(){}' > conftest.cc +if test -z "`${CXX-g++} -fno-rtti -c conftest.cc 2>&1`"; then + ac_cv_prog_cxx_fno_rtti=yes + CXXFLAGS="${CXXFLAGS} -fno-rtti" +else + ac_cv_prog_cxx_fno_rtti=no +fi +rm -f conftest* +])]) + +dnl Check if the type socklen_t is defined anywhere +AC_DEFUN([AC_C_SOCKLEN_T], +[AC_CACHE_CHECK(for socklen_t, ac_cv_c_socklen_t, +[ AC_TRY_COMPILE([ +#include +#include +],[ +socklen_t foo; +],[ + ac_cv_c_socklen_t=yes +],[ + ac_cv_c_socklen_t=no + AC_DEFINE(socklen_t,int) +])])]) + +dnl Check for sys_errlist[] and sys_nerr, check for declaration +dnl Check nicked from aclocal.m4 from GNU bash 2.01 +AC_DEFUN([AC_SYS_ERRLIST], +[AC_MSG_CHECKING([for sys_errlist and sys_nerr]) +AC_CACHE_VAL(ac_cv_sys_errlist, +[AC_TRY_LINK([#include ], +[extern char *sys_errlist[]; + extern int sys_nerr; + char *msg = sys_errlist[sys_nerr - 1];], + ac_cv_sys_errlist=yes, ac_cv_sys_errlist=no)])dnl +AC_MSG_RESULT($ac_cv_sys_errlist) +if test $ac_cv_sys_errlist = yes; then +AC_DEFINE(HAVE_SYS_ERRLIST) +fi +]) + +dnl @synopsis AX_CFLAGS_GCC_OPTION (optionflag [,[shellvar][,[A][,[NA]]]) +dnl +dnl AX_CFLAGS_GCC_OPTION(-fvomit-frame) would show a message as like +dnl "checking CFLAGS for gcc -fvomit-frame ... yes" and adds the +dnl optionflag to CFLAGS if it is understood. You can override the +dnl shellvar-default of CFLAGS of course. The order of arguments stems +dnl from the explicit macros like AX_CFLAGS_WARN_ALL. +dnl +dnl The cousin AX_CXXFLAGS_GCC_OPTION would check for an option to add +dnl to CXXFLAGS - and it uses the autoconf setup for C++ instead of C +dnl (since it is possible to use different compilers for C and C++). +dnl +dnl The macro is a lot simpler than any special AX_CFLAGS_* macro (or +dnl ac_cxx_rtti.m4 macro) but allows to check for arbitrary options. +dnl However, if you use this macro in a few places, it would be great +dnl if you would make up a new function-macro and submit it to the +dnl ac-archive. +dnl +dnl - $1 option-to-check-for : required ("-option" as non-value) +dnl - $2 shell-variable-to-add-to : CFLAGS (or CXXFLAGS in the other case) +dnl - $3 action-if-found : add value to shellvariable +dnl - $4 action-if-not-found : nothing +dnl +dnl note: in earlier versions, $1-$2 were swapped. We try to detect the +dnl situation and accept a $2=~/-/ as being the old +dnl option-to-check-for. +dnl +dnl also: there are other variants that emerged from the original macro +dnl variant which did just test an option to be possibly added. +dnl However, some compilers accept an option silently, or possibly for +dnl just another option that was not intended. Therefore, we have to do +dnl a generic test for a compiler family. For gcc we check "-pedantic" +dnl being accepted which is also understood by compilers who just want +dnl to be compatible with gcc even when not being made from gcc +dnl sources. +dnl +dnl see also: +dnl +dnl AX_CFLAGS_SUN_OPTION AX_CFLAGS_HPUX_OPTION +dnl AX_CFLAGS_AIX_OPTION AX_CFLAGS_IRIX_OPTION +dnl +dnl @category C +dnl @author Guido Draheim +dnl @version 2003-11-04 +dnl @license GPLWithACException + +AC_DEFUN([AX_CFLAGS_GCC_OPTION_OLD], [dnl +AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$2])dnl +AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for gcc m4_ifval($2,$2,-option)], +VAR,[VAR="no, unknown" + AC_LANG_SAVE + AC_LANG_C + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic % m4_ifval($2,$2,-option)" dnl GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_TRY_COMPILE([],[return 0;], + [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR]) + else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + + +dnl the only difference - the LANG selection... and the default FLAGS + +AC_DEFUN([AX_CXXFLAGS_GCC_OPTION_OLD], [dnl +AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_gcc_option_$2])dnl +AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for gcc m4_ifval($2,$2,-option)], +VAR,[VAR="no, unknown" + AC_LANG_SAVE + AC_LANG_CXX + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic % m4_ifval($2,$2,-option)" dnl GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_TRY_COMPILE([],[return 0;], + [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR]) + else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + +dnl ------------------------------------------------------------------------- + +AC_DEFUN([AX_CFLAGS_GCC_OPTION_NEW], [dnl +AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$1])dnl +AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)], +VAR,[VAR="no, unknown" + AC_LANG_SAVE + AC_LANG_C + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic % m4_ifval($1,$1,-option)" dnl GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_TRY_COMPILE([],[return 0;], + [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR]) + else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"]) + m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + + +dnl the only difference - the LANG selection... and the default FLAGS + +AC_DEFUN([AX_CXXFLAGS_GCC_OPTION_NEW], [dnl +AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_gcc_option_$1])dnl +AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)], +VAR,[VAR="no, unknown" + AC_LANG_SAVE + AC_LANG_CXX + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic % m4_ifval($1,$1,-option)" dnl GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_TRY_COMPILE([],[return 0;], + [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR]) + else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"]) + m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + +AC_DEFUN([AX_CFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1, +[AX_CFLAGS_GCC_OPTION_NEW($@)],[AX_CFLAGS_GCC_OPTION_OLD($@)])]) + +AC_DEFUN([AX_CXXFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1, +[AX_CXXFLAGS_GCC_OPTION_NEW($@)],[AX_CXXFLAGS_GCC_OPTION_OLD($@)])]) + + diff --git a/app/AUTHORS b/app/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/app/COPYING b/app/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/app/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/app/ChangeLog b/app/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/app/INSTALL b/app/INSTALL new file mode 100644 index 00000000..64d33306 --- /dev/null +++ b/app/INSTALL @@ -0,0 +1,231 @@ + +REQUIREMENTS +============ + +Read the IMPORTANT NOTES in the README file. + + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + + +Gambas Options +============== + + --with-intl-includes where the internationalization headers are located. + --with-intl-libraries where the internationalization libraries are located. + --with-qt-includes where the QT component headers are located. + --with-qt-libraries where the QT component libraries are located. + --with-kde-includes where the KDE 3.x component headers are located. + --with-kde-libraries where the KDE 3.x component libraries are located. + --with-net-includes where the Networking component headers are located. + --with-net-libraries where the Networking component libraries are located. + --with-postgresql-includes where the PostgreSQL driver headers are located. + --with-postgresql-libraries where the PostgreSQL driver libraries are located. + --with-mysql-includes where the MySQL driver headers are located. + --with-mysql-libraries where the MySQL driver libraries are located. + --with-sdl-includes where the SDL component headers are located. + --with-sdl-libraries where the SDL component libraries are located. + +--disable-debug Remove debug information from binary files. + +--enable-optimization Enable optimization during compilation. + +--disable-preloading Disable the preloading of components. + +--disable-qt-component Do not compile the QT component. + + +Component options +================= + +XXX is a component or library name: + + intl internationalization library + kde KDE component + mysql MySQL driver + net Network component + postgresql PostgreSQL driver + qt QT component + sdl SDL component + +--with-XXX-libraries Where the libraries are located. + +--with-XXX-includes Where the headers are located. + +Use these options if the configure script cannot detect the +location of librairies and/or headers. + +The components or libraries that are not detected are automatically disables, +and then not compiled. + +That's all ! Good luck... diff --git a/app/Makefile.am b/app/Makefile.am new file mode 100644 index 00000000..3dbb0c4c --- /dev/null +++ b/app/Makefile.am @@ -0,0 +1,65 @@ +EXTRA_DIST = reconf src spec mime desktop template + +install-exec-local: + @if test "x$(ROOT)" != "x"; then \ + echo "[Installing with ROOT=$(ROOT)]"; \ + fi + @if test "x$(DESTDIR)" != "x"; then \ + echo "[Installing with DESTDIR=$(DESTDIR)]"; \ + ROOT=$DESTDIR; \ + fi + + @echo "Installing the development environment..." + @(cd $(srcdir)/src; d=`pwd`; \ + for p in `cat INSTALL`; do \ + echo "Compiling $$p..."; cd $$d/$$p; \ + $(DESTDIR)$(bindir)/gbc$(GAMBAS_VERSION) -ag -r $(DESTDIR)$(prefix); \ + if test $$? -eq 0; then \ + $(DESTDIR)$(bindir)/gba$(GAMBAS_VERSION); \ + rm -rf .gambas; \ + echo "Installing $$p..."; \ + $(INSTALL) $$p.gambas $(DESTDIR)$(bindir); \ + else \ + echo "|| Unable to compile $$p" >> ../../../warnings.log; \ + fi \ + done) + ##@if test "$(bindir)" != "$(ROOT)/usr/bin" && test "$(bindir)" != "$(ROOT)/usr/bin/"; then + @$(LN_S) -f gambas$(GAMBAS_VERSION).gambas $(DESTDIR)$(bindir)/gambas$(GAMBAS_VERSION) || true + + @echo "Installing the scripter..." + @$(LN_S) -f gbs$(GAMBAS_VERSION).gambas $(DESTDIR)$(bindir)/gbs$(GAMBAS_VERSION) || true + @$(LN_S) -f gbs$(GAMBAS_VERSION).gambas $(DESTDIR)$(bindir)/gbw$(GAMBAS_VERSION) || true + @if test x"$(XDG_UTILS)" != x; then \ + $(INSTALL) -d $(DESTDIR)$(gbdatadir)/icons; \ + cp -f $(srcdir)/mime/application-x-gambas*.png $(DESTDIR)$(gbdatadir)/icons; \ + echo "Registering Gambas script mimetype..."; \ + xdg-icon-resource install --context mimetypes --size 256 $(DESTDIR)$(gbdatadir)/icons/application-x-gambasscript.png application-x-gambasscript; \ + xdg-mime install $(srcdir)/mime/application-x-gambasscript.xml; \ + echo "Registering Gambas server page mimetype..."; \ + xdg-icon-resource install --context mimetypes --size 256 $(DESTDIR)$(gbdatadir)/icons/application-x-gambasserverpage.png application-x-gambasserverpage; \ + xdg-mime install $(srcdir)/mime/application-x-gambasserverpage.xml; \ + fi + + @echo "Installing the Gambas appdata file..." + @$(INSTALL) -d $(DESTDIR)$(datarootdir)/appdata; + @$(INSTALL) $(srcdir)/desktop/gambas3.appdata.xml $(DESTDIR)$(datarootdir)/appdata; + @$(INSTALL) -d $(DESTDIR)$(datarootdir)/metainfo; + @$(INSTALL) $(srcdir)/desktop/gambas3.appdata.xml $(DESTDIR)$(datarootdir)/metainfo; + + @echo "Installing the Gambas template projects..." + @$(INSTALL) -d $(DESTDIR)$(gbdatadir)/template; + @cp -R $(srcdir)/template/* $(DESTDIR)$(gbdatadir)/template; + +uninstall-local: + @rm -f $(DESTDIR)$(bindir)/gambas$(GAMBAS_VERSION) + @rm -f $(DESTDIR)$(bindir)/gbs$(GAMBAS_VERSION) + @rm -f $(DESTDIR)$(bindir)/gbw$(GAMBAS_VERSION) + @(cd $(srcdir)/src; for p in `cat INSTALL`; do rm -f $(DESTDIR)$(bindir)/$$p.gambas; done) + @rm -rf $(DESTDIR)$(datarootdir)/metainfo/gambas3.appdata.xml + @rm -rf $(DESTDIR)$(datarootdir)/appdata/gambas3.appdata.xml + @rm -rf $(DESTDIR)$(gbdatadir)/template + +dist-hook: + @(cd $(distdir)/src; \ + rm -rf `find . -name ".gambas" -o -name ".action" -o -name ".lock" -o -name ".xvpics" -o -name "*~" -o -name "*.out" -o -name "*.pot" -o -name "*.gambas" -o -name "core*" -o -name ".kdbg*" -o -name ".svn"`;) + diff --git a/app/NEWS b/app/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/app/README b/app/README new file mode 100644 index 00000000..e69de29b diff --git a/app/TODO b/app/TODO new file mode 100644 index 00000000..e69de29b diff --git a/app/acinclude.m4 b/app/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/app/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/app/configure.ac b/app/configure.ac new file mode 100644 index 00000000..d4866867 --- /dev/null +++ b/app/configure.ac @@ -0,0 +1,19 @@ +dnl ---- configure.ac for Gambas development environment + +dnl ---- Initialization + +m4_include([../version.m4]) +AC_INIT(gambas3-ide, GB_VERSION, GB_MAIL, [], GB_URL) +GB_INIT_SHORT(ide) + +dnl ---- Check for Portland scripts + +AC_CHECK_PROGS(XDG_UTILS, [xdg-mime xdg-icon-resource], []) + +dnl ---- Create makefiles + +AC_OUTPUT( \ +Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/app/desktop/gambas3.appdata.xml b/app/desktop/gambas3.appdata.xml new file mode 100644 index 00000000..b504f6fb --- /dev/null +++ b/app/desktop/gambas3.appdata.xml @@ -0,0 +1,31 @@ + + + gambas3.desktop + CC0-1.0 + GPL-2.0+ + IDE for the Gambas language + +

Gambas is a free development environment and a full powerful development platform based on a Basic interpreter with object extensions, as easy as Visual Basic. This application provides a graphical IDE, with a database manager, an image editor, and a report designer, to assist in the creation of programs with Gambas.

+
+ + + http://gambas.sourceforge.net/2014-07-26.png + The Gambas 3 development environment + + + Benoît Minisini + gambas@users.sourceforge.net + gambas3.desktop + http://gambas.sourceforge.net/ + http://gambaswiki.org/bugtracker + http://gambas.sourceforge.net/ + http://gambas.sourceforge.net/ + + basic + interpreter + visual + graphical + object language + development environment + +
diff --git a/app/desktop/gambas3.desktop b/app/desktop/gambas3.desktop new file mode 100644 index 00000000..b8613749 --- /dev/null +++ b/app/desktop/gambas3.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Name=Gambas3 +Exec=gambas3 +Comment=Gambas3 Integrated Development Environment +Icon=gambas3.png +Terminal=false +Type=Application +Categories=Application;Development; +Encoding=UTF-8 +StartupNotify=true diff --git a/app/desktop/gambas3.png b/app/desktop/gambas3.png new file mode 100644 index 0000000000000000000000000000000000000000..c988a0a166b59daca0df236f6dc2b0c23a308547 GIT binary patch literal 3144 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 i-N7B)!5#b`;(q`SGc0E->M#NT0000 + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Gambas 3 + 15/09/2008 + + + Fabien Bodard + + + + + Common Creative + + + + + Fabien Bodard + + + gbFBodard150908 + http://gambas.sf.net + + + gambas3 logo basic shrimp + + + A pretty shrimp, a malicious new gambas logo... we got the power ! + + + Fabien Bodard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/examples/Basic/Blights/.directory b/app/examples/Basic/Blights/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Basic/Blights/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Basic/Blights/.icon.png b/app/examples/Basic/Blights/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0c1982e11033bafde2fa4e869e77992384b3512e GIT binary patch literal 4209 zcmV-%5RUJOP)-|HAB_SJI5+?Sqs;lPB zd)@QCpZEK9|GHm~;3Bh}tuLY-2ONzxP)GlsyJyv&e7^;qkLBL62Y{4-n}vlzq&I8U zZ5r>lsB^J`Pwv5S1WE`#zd4_8Z7rdp!(`((!nm^65bxR#K=!SF0%73TaU#c`AX4+I ziyJ`EXa0htK|8QzZ5G=;Rf27Jc&r*q(gLLT4Pr%`hu(A`=@<8*9UHA7H`T*;Z!co=noM47fM31@X6*E}vADy4CGlnEvvEZR z_uW-SdvBZ#-#tP7sUeV_iwi*6mtIF}NDjF9@=baC{Y|-4_rMQdgT7%%*r%`UXziu` z_$l&I1&{yBQpyVgeE-oFcKmf1l!t)u5m&yM1Gr~<-`!e>dkm%X&+-1F5sNCXvcG!& z?OE05BYeT#`#?yffRC@s;u|*?FrXDbdkdOFBkn$C1N{+BywyO$9O644E#%JYa{1{W zB7E!zO~mX3C?5!cay-PPcjf@J5cjN_f6n3oypHDT{ioDR+qU=KaR1hfH{Pv6`9B^+ zYtUMA?c!9v_t64UQa$W!g5y09cP7v^6C8WHmA1wXwtgU+Z{4$kg9)zJDn}qfN z4hVtJ0_C{TTH?z6Ao2DW&L4o1PQLAM-nM!^DF+(FuCqa?_^16KU1$d?3;lfWBl#>V z4e&}Ey!kd*6P~zjL(Pe9YLBHFbqjuJ^T20{SzD3DLwn%qs>#>6eIC#Pfq~WHdtr`)W!XIa;GLNtpL*`h9^lLz06=Nx?CQ_4xleY=Tc$fP zJpg+o{w^gGnrRb*=>edzXXycdYj&nC?VSMby;sKK4k7bzB!K70A{KD3mfR~Q37Z69 z{P!?y9pDRbyxqctfXa4B&~0*Ko> zJn`6ZY;DAC5%C#nl3*duui=ZuG)3 zsmNhr`Q_+04kDGnii9z|0i?W;Fed9Xgo+qS$>>xMK+kR*OjuDm1{!e{6@k{c)^8@% zR?C&?E*gs0qJ*OM;B%BNzkxtX_Jz|gCIe8)jv7))Q%&g{^b7$U-u);YwFj{^Bv!2< zC76m6xA3QD60NJ^*nuXpiY{m0Ge6~2&k0Ia-H4PIF6j_cLO?2M8cGUtY6R#RbOBHO z_!E5Xb6fc2&mLvhZ+^uOezym!xE$pTkmZ6Qk>K@xPh+$m##NX@AkBxN+#1ZDy!fd4?jgmI6@>N8~^g_$-2@% zdM6Tf@+fY@xS&bbRBtnsGz}>*XY&;7NH?7LJ-h$zo9tS-lFLFNl7wW%;&N7O-pawg z1WMaDR&<1{5X5>~DM`-8=TEwz0H*4c0#h1FnNkWS6>;XG?F9Y3t+aKWqW;K>RHnC5 zws9lLKp;Dt*3K^O|IY`Bdy|MKY#eh4$BK@}pKV3a&GqD_7ho8!v;Nkcmnk|8rA$Lf z9ad5ZI6e5EsSjwYIY`(QC*s67-rmA3i?VRC3V6J#mAj9$^M8*#Ox3f`A&M^J7YCX+ z6^bIo>CQ)Mjn&c2;)2D@D_D5p9{uzlAe7W$Lm9G53WS)D{pe}>fY+;Eq9_<9k3H=78Z(!X6$(D1%sM6M4}D<*Tkj z&8G9$HYt%q_@a#?UL^yBl2&24&i)=?A^@!s%8*@#OX+SU<-`#}I8!={oiF{G+izOQ zA5SRyO&dp8_=HB{V8#+`%2|%QzJE(5qbxHy*-r;-Tm5uL`i8(q-{ zcSo%;uhd!#M?%7i5lqr7T6rDYckjgtbs?2O#?@QsuX>4;$~Elo)f9F#k;gfHoodntWMFfWW8Fabn8-##cG2drlC`if7W7!oSoD<<=HsGot+;NPo z)C36chy)$w^vqR{iZ`h3NkZkxcO+R-BF6f6Jqdsk(Q(&s5K0P^%S|vlA6IfRLkC_b zWFbo*GSt0JI=F0RWM)O9dm8+)||@!NubghZf)kFJw>DxtibONPBZx#@$OM{k#PWq#AE^ifr^T0Fdgi|+AR$77KdS|X76=FOY(i1{J zDiIx9GSym-Cq>JauHjV@Y+bjQXIm|54(!F{t)%I#z4Y~Wamoo{Hn*eFbC50&QwpY}vLv^J#~=GO^Rkji%Pb~59Oao8 zc93`7ZOF1UtX|Yf^{FL!!yq^KU7NTf~7Nt zproR>ppfpoH)v^T;k8|_QCeEcuzP@#%GLP&XMU+Ag%)G|f|%$5h7u8H+yjJ|IB%Z6 zu#!!NWN&LD3ycV_)|?{as!cd%(4`2<4-|;)=T81pa@Bi>Lue|aK&CSic@WKlmICKo**k`RL z#fWz+Iih*o1EiAX*ob0F#B}R)sTtW^yI~Ur$Ex^!b2IL&0>VPm)!ojG*M9^SEvEG7 zLH>H;ExME!U*SSD8c*dK2BX8INy)lJmE@INMq|y}C_~M7JuvReCfa2)G&Dp@OAF1- z%@ken5dy*Fv$i?O1B5so0xpR;wmnZesIyJhiWe;*<>0GCvkK6`bZUg;*e@RBvs><< zV##Xq3rjdy{TjPpeFlHY5go9}u)0(=52Fgz!=DJOH%-H<_JYFw7G6!!`m}M(2 zr+d&MJ+GKxTKZX6n1s~hgOS0cQGkeW#>q)>me<2>w})G=zlBGB_b9H)D@lY0$Z~s# z$1Q{qB&TMu;o2=+zH%M)^~X4P=yjU9JFy&xyo@Z0)^9@0!RzO&MQeezq^>!iT4G$A zlsW>VXC0wcQNGu_vQA=s9xfBTqA(V~w_&tUb z>KmvV<$?e*pLnJDFYcuDGFy*rmb&x+04Sm3)=*#Fr$115Ezms<0N4bS1Af5GJ2zBZ z>}CUTU>G<7{CNxjfCtD276Q3I8sOt1`&->v%wc5M>j&C_TA&4pO_&3sMn!uV@c_vg zc9UO(rbi)Iqs0UgO!FT6QW1PEK;}+x;U{eI{^)k9c7We(8iRY_&iL@M` z=P&n@818(})O`4^pK!D$jNwkgaQkulQmH6`4I5TtC_z@5k6W*qKW*JP*{x)@>_w=J z9BDH6(NnGL*xN&2cPD)ZA0~X{x9^$oF`HjdNJD2AhC3BcQZ`O}fULBUskzr}oKZZ? zq07(P{3!mMVx*xkTdU}JVH-|t@I4nlX6x?Uj-yg=yL}L?VQHzwy!`oW`-e?utv6@; z)jFU1+yxUmon>bx;W1*^c9iXpe4eep+(TyO?EhSS$=m+}{!T`;nq-uf00000NkvXX Hu0mjfPqr0Z literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Blights/.lang/ca.mo b/app/examples/Basic/Blights/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..837ffd89ed6acea96248406744e4d7fe73dfe8b0 GIT binary patch literal 489 zcmZvY%}xR_5XaZA%O+m*;Kju8Y>F%xLl)N<5fTizG+jw53# z, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Blights\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-16 23:31+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: win1.form:16 +msgid "Blinkenlights" +msgstr "Llums perpallejants" + +#: .project:1 +msgid "Blinking lights example" +msgstr "Enemple del Llums perpallejants" + diff --git a/app/examples/Basic/Blights/.lang/cs.mo b/app/examples/Basic/Blights/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..dffd4c7bc7d49e490244b4fa1895b2660784ad66 GIT binary patch literal 443 zcmYL_OHRWu5Qfcb6NwEh*dXLRasi1&6$+{$Zi&cC=|h4QZfa7uu2UtZDkp#out0Ey zo}zFK&ccK)GVWR=wrXy3VGpSR&&aqhV=}ZX}<=I#yhPXr07kMEwZR3dE+|f3} zeHM4zFtDPK#9}T>lQ`T68{ab;6F(#l#kZmDyT=bouw4#H`b~f>ND6^Hh+o;8PoWG-&tAmJQEqDp||&w5qYO<-0zs<~ysMS~r~ ai~0AbQ5;1-AFJg=@gW{m?r3cbh5i8b2z>Sc literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Blights/.lang/cs.po b/app/examples/Basic/Blights/.lang/cs.po new file mode 100644 index 00000000..dd532b12 --- /dev/null +++ b/app/examples/Basic/Blights/.lang/cs.po @@ -0,0 +1,20 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Blinking lights example" +msgstr "Příklad blikajícíh světel" + +#: win1.form:16 +msgid "Blinkenlights" +msgstr "Blikající světla" diff --git a/app/examples/Basic/Blights/.lang/de.mo b/app/examples/Basic/Blights/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..4507c36d0f2d112ac217173eb72a7d2feef894c9 GIT binary patch literal 442 zcmZXQy-ve05XZyU5{Us879_kQ7mygLP*7dsmIx=Q(hmtH-1M3nQYUf@Do+3}z{0bz z@g6)27dmv{VA##rFAOfl50og?)a)1zD0|2|zK>8p zQSto!gW_z~R~?;P=_k;+%I3COWlCpcl@nOXMVW)sH^n2QmIhNg22-g*d!)~r7T_ngS6jom((Y Q2UJxm$jR*c%aH%|UjW;94FCWD literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Blights/.lang/de.po b/app/examples/Basic/Blights/.lang/de.po new file mode 100644 index 00000000..de41ad66 --- /dev/null +++ b/app/examples/Basic/Blights/.lang/de.po @@ -0,0 +1,21 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Blinking lights example" +msgstr "Beispiel für blinkende Lichter" + +#: win1.form:16 +msgid "Blinkenlights" +msgstr "blinkende Lichter" + diff --git a/app/examples/Basic/Blights/.lang/es.mo b/app/examples/Basic/Blights/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..cccd4ebbb73eafb960c143d74e262969c7255871 GIT binary patch literal 441 zcmZXQ%}&EG49COQr4pB2xPX*9$^ePOR0djEvr&<@Q}-dkiO`y^qf4qZL*ohH1vv3; zJPR&6?0}V@|FLY@mVa9NUj%IrIYus#14JN=Jt2pPM2--RkR3ukP+G_*{!dW9QStrt zK_R=1YwWfx)GMg6$mh1+M1{)fCZ@2K%c=x2GI{~2WkJSfVCq6EmyW#ty*K1^#*=Xn zN6si_3A`0elKIktOCPwGurOx6NAF@c3{D^8h&w{omQ9RQbt$bjE*(sTpph4H+U21a zh@R*B2_KJduvFzMnFE`ET)I?v(Qw*~>1$;{;kfq- z_pz|HXH_W+bw$&;G&R`k>14n<+q!107ch*gRA+bymv){D>kzR@p{A8ImCQh5)0*%F M;B7R`fR_KWzkASkH~;_u literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Blights/.lang/es.po b/app/examples/Basic/Blights/.lang/es.po new file mode 100644 index 00000000..7445bc3c --- /dev/null +++ b/app/examples/Basic/Blights/.lang/es.po @@ -0,0 +1,20 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2014-11-11 23:48 UTC\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Blinking lights example" +msgstr "Ejemplo de luces parpadeantes" + +#: win1.form:16 +msgid "Blinkenlights" +msgstr "Luces parpadeantes" diff --git a/app/examples/Basic/Blights/.lang/fr.po b/app/examples/Basic/Blights/.lang/fr.po new file mode 100644 index 00000000..0ce4e3df --- /dev/null +++ b/app/examples/Basic/Blights/.lang/fr.po @@ -0,0 +1,3 @@ +#: win1.class:54 +msgid "Blinkenlights" +msgstr "" diff --git a/app/examples/Basic/Blights/.lang/nl.mo b/app/examples/Basic/Blights/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..31e739044a21208fad8a4fa738cb3334e87360e6 GIT binary patch literal 443 zcmaiw%}xR_5XaZA%Nj3UjERTdO|grZ;ED#sl@OPYAmG))j=R;;Zn_2G348$K1NdS- zi&Hspbkbk{opv()XMPTMzdVc`$tA?1n;IM{;J z-d$2pU;$F+CE&(pMJ95X8=XT*oAd@7sC3R#phMuZki?B{Yvhd7;sF? ziTO>fEGYbWm7_HaOM8|}F4Ps7X518DuSdgsTHVw+hc94gq^3H-nS`V|5!U}_VhgR! I1VAR\n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Blinking lights example" +msgstr "Blinking lights voorbeeld" + +#: win1.form:16 +msgid "Blinkenlights" +msgstr "Blinkenlights" + diff --git a/app/examples/Basic/Blights/.lang/sv.mo b/app/examples/Basic/Blights/.lang/sv.mo new file mode 100644 index 0000000000000000000000000000000000000000..01ad967bf0af7c52f13104a79c0b059042819ee3 GIT binary patch literal 358 zcmYMwyH3L}6b9gcV2Ol;m>Il-3v5*gRZZiz5zd8ND#3zGVp2nDM~*@8AiN%L!Olqs z`pe(sW1V0AIXU=tAp77HoP$H)gCQw60{7q-Jb`0S?l}&e{|xW9xoxF-6RMTX+V)Or z^sA^W4r{y;rk7fIC?&HyvY=>1vph^!f@5M+-l>YdC3@xNkoxPBNDPH34)B~`2toXDSUCX7qM%9{|Uf7$mnB(zoZs_tN45q5mwN#CV U#?R8a&h`(uss(EKrqjmx1MFs9{Qv*} literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Blights/.lang/sv.po b/app/examples/Basic/Blights/.lang/sv.po new file mode 100644 index 00000000..ba0558c1 --- /dev/null +++ b/app/examples/Basic/Blights/.lang/sv.po @@ -0,0 +1,15 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: win1.class:54 +msgid "Blinkenlights" +msgstr "Blinkande lampor" diff --git a/app/examples/Basic/Blights/.project b/app/examples/Basic/Blights/.project new file mode 100644 index 00000000..c9afd747 --- /dev/null +++ b/app/examples/Basic/Blights/.project @@ -0,0 +1,16 @@ +# Gambas Project File 3.0 +Title=Blinking lights example +Startup=win1 +Icon=ampoule.png +Version=3.11.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Basic/Blights/.src/win1.class b/app/examples/Basic/Blights/.src/win1.class new file mode 100644 index 00000000..910404ca --- /dev/null +++ b/app/examples/Basic/Blights/.src/win1.class @@ -0,0 +1,36 @@ +' Gambas class file + + +Public Sub Timer1_Timer() + + Dim diode As PictureBox + Dim l As Integer + + For Each diode In [l1, l2, l3, l4, l5, l6, l7, l8] + l = Rnd() * 2 + If l = 0 Then + diode.Picture = Picture["bloff.xpm"] + Else + diode.Picture = Picture["blon.xpm"] + Endif + Next + +End + + + +Public Sub Form_Open() + + Dim hPict As Picture + Dim diode As PictureBox + + hPict = Picture["blon.xpm"] + + For Each diode In [l1, l2, l3, l4, l5, l6, l7, l8] + diode.Resize(hPict.Width, hPict.Height) + diode.Background = Color.DarkGreen + Next + + Me.Resize(hPict.W * 8 + Me.W - Me.ClientW, hPict.H + Me.H - Me.ClientH) + +End diff --git a/app/examples/Basic/Blights/.src/win1.form b/app/examples/Basic/Blights/.src/win1.form new file mode 100644 index 00000000..2a3bcfa5 --- /dev/null +++ b/app/examples/Basic/Blights/.src/win1.form @@ -0,0 +1,53 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(20,29.5714,65,7) + Text = ("Blinkenlights") + Icon = Picture["blon.xpm"] + Resizable = False + Arrangement = Arrange.Horizontal + { l1 PictureBox + MoveScaled(0,0,7,7) + Picture = Picture["bloff.xpm"] + Alignment = Align.Center + } + { l2 PictureBox + MoveScaled(7,0,7,7) + Picture = Picture["bloff.xpm"] + Alignment = Align.Center + } + { l3 PictureBox + MoveScaled(14,0,7,7) + Picture = Picture["bloff.xpm"] + Alignment = Align.Center + } + { l4 PictureBox + MoveScaled(21,0,7,7) + Picture = Picture["bloff.xpm"] + Alignment = Align.Center + } + { l5 PictureBox + MoveScaled(28,0,7,7) + Picture = Picture["bloff.xpm"] + Alignment = Align.Center + } + { l6 PictureBox + MoveScaled(35,0,7,7) + Picture = Picture["bloff.xpm"] + Alignment = Align.Center + } + { l7 PictureBox + MoveScaled(42,0,7,7) + Picture = Picture["bloff.xpm"] + Alignment = Align.Center + } + { l8 PictureBox + MoveScaled(49,0,7,7) + Picture = Picture["blon.xpm"] + Alignment = Align.Center + } + { Timer1 #Timer + Enabled = True + Delay = 200 + } +} diff --git a/app/examples/Basic/Blights/ampoule.png b/app/examples/Basic/Blights/ampoule.png new file mode 100644 index 0000000000000000000000000000000000000000..169097084b60f2331cd91695f23bc6d8f92d9650 GIT binary patch literal 1543 zcmV+i2Kf1jP)2L`6zl35iHZ2t*%1s1;SI z+7c>0BDF73gqjB)nyBKTO-YIfML2Z}PD&D+&4s4___JTPekD2&o&;CgOOL(@BV6c|3hCx?$``+7l;s8I@ku#c!pih^RM%- zlbSfaRJGl2?R`G+#(zX$^2nzSh3?z@>%p%)joR~RP&b26!L@uhq4E*9<(6qKra$J! zsh_a8l>5%U7Z#2K&OH;j`tk!KN@U%+?O#8P-1!g$1^^q}YNI=OsLa-%-C(6moS8Gf zWa7r+!{0ob{)>0WG6AWbNMy-8}vn;RBVH zHQ<&SzfQ@^KvUF>wXWc}t^#xdffWhlO3wAZvH`{OU|t7x2S}l1@wvr1A(m>wZVL|J zlowgZT80lUuc(2uljeKh-tZA_WfwvzKy?A6Fh~(kw|7cVE9ENpi3s8>rFi?2Io-Uu zC4pyN%ilVhb&U zi#-zPjUwd0${_7bS%>Qeuoh-`^=$4BKncjRESR>9$UgAW?LSV>mEN%8*T8LVFvYfw z$IFy}zylz4Ve3iWJ^#=5e|5HW9Wa2U`V2_z=~H<2wc?RmbIG`yne#|Ac(Kk4fDLjd zsDlvP22uxhT4&+f`^hJdrGF1(fux^-J1Jc0GLnFv=N=2~fA+D^(csY5Fd@H*Dg7Wt zJwOovx#w;rq!T1#XOmz2LHyXP?xcWe;O{;W=U%Vl0I=Z~hqNc2KB)bifqn?7{-!X5 zc6zP4Ss#fBQrAY4!#~U%`&f4}z$`EYq-ynF%Y#G%HoyK<_ry4SWvxho64)4ox4o-Cs~|Wgzjyk>YX7Mt<~j#&@kg6-AR! zGzqzU4oT`mXrF=1MN*gEUKP9{*n4@B`FiLAwq}n-F5Q-F0-A)P36zi^pb2km!tc&m z?;QAP;my^+*YqazO+Iz%l%;7JrfE_r6v*Xr7=}SMn?+SsB9RFBe4g>~@!yP&j{X>! zZx{JmN>DSqyStl_kr6D*BAd+;kH@)kj$vVZ@65{U#CE?ltu(%GvWDy@!t zH){aLaj-0lN~LnwKFhK+pqHk{$0sIG4{qBw)oK;XvS==&ZQB8$gSuO2=_ zGiMM&kj-XEr_;>M&7tc$(P)$tCr*$^B-RwnnlccJ#STtQP0fZvA!J!b2!X1qsH#dh z9AUZ>6GCwF=1r!jr-{X4OiWC0^5jV_U%rfC81wmj z{w7f59z?jN5&$_()683T^vV84US37Vmji_cld-m-4(#Xijk1WgTG) c #5A301D", +", c #64331B", +"' c #65341B", +") c #6C341C", +"! c #6C321A", +"~ c #6B371D", +"{ c #68361F", +"] c #6D371C", +"^ c #73381D", +"/ c #79381D", +"( c #813C1A", +"_ c #753B1C", +": c #713A1D", +"< c #6E371D", +"[ c #6F391E", +"} c #7E3D19", +"| c #7B3E1C", +"1 c #6E371C", +"2 c #68351F", +"3 c #60311E", +"4 c #4E2C1C", +"5 c #4A2B1D", +"6 c #502D1C", +"7 c #582D1D", +"8 c #63301D", +"9 c #78391C", +"0 c #7A391A", +"a c #763619", +"b c #773A1B", +"c c #76391A", +"d c #76381A", +"e c #77381B", +"f c #773A1C", +"g c #733719", +"h c #743A1A", +"i c #76381B", +"j c #6F3719", +"k c #72391A", +"l c #73381A", +"m c #743619", +"n c #743518", +"o c #743517", +"p c #723719", +"q c #77391B", +"r c #6F3418", +"s c #68321D", +"t c #552D1B", +"u c #4F2C1B", +"v c #502C1B", +"w c #512D1B", +"x c #5B2F1B", +"y c #5F301B", +"z c #66361E", +"A c #6D381C", +"B c #6F391B", +"C c #6D381B", +"D c #6D351B", +"E c #743C1C", +"F c #753C1D", +"G c #823C1A", +"H c #793C1B", +"I c #793B1C", +"J c #773C1C", +"K c #783D1B", +"L c #86461C", +"M c #793E1C", +"N c #793F1C", +"O c #833D1A", +"P c #7A3C1C", +"Q c #77391C", +"R c #70381C", +"S c #66351B", +"T c #6B341B", +"U c #68351B", +"V c #67361C", +"W c #613119", +"X c #643119", +"Y c #63341B", +"Z c #60301A", +"` c #64311A", +" . c #65321A", +".. c #6A371C", +"+. c #61321A", +"@. c #5F301D", +"#. c #61331F", +"$. c #5E331D", +"%. c #5C321D", +"&. c #592F1E", +"*. c #62311B", +"=. c #693119", +"-. c #713519", +";. c #75381A", +">. c #69351E", +",. c #6D381D", +"'. c #62341D", +"). c #60331D", +"!. c #5D301C", +"~. c #5B311C", +"{. c #5C2F1B", +"]. c #62311C", +"^. c #6B361E", +"/. c #6C351A", +"(. c #6B351A", +"_. c #6E351A", +":. c #74381B", +"<. c #7B3B1B", +"[. c #7E3918", +"}. c #833D18", +"|. c #773B1B", +"1. c #713A1C", +"2. c #6C381D", +"3. c #69341B", +"4. c #6C371E", +"5. c #6C361E", +"6. c #6C351E", +"7. c #73391D", +"8. c #77381D", +"9. c #81401B", +"0. c #88451B", +"a. c #80411B", +"b. c #723A1B", +"c. c #703A1D", +"d. c #6A371E", +"e. c #68351D", +"f. c #65341E", +"g. c #6C341D", +"h. c #70351C", +"i. c #6F371F", +"j. c #6E3720", +"k. c #6F371A", +"l. c #6C341A", +"m. c #6B381C", +"n. c #6B391C", +"o. c #68341A", +"p. c #67321A", +"q. c #622D18", +"r. c #612C19", +"s. c #612D19", +"t. c #612E1A", +"u. c #64331C", +"v. c #66361F", +"w. c #6B3720", +"x. c #6F351E", +"y. c #76371A", +"z. c #6F371D", +"A. c #65351E", +"B. c #63321C", +"C. c #6A321A", +"D. c #6C351B", +"E. c #6C361C", +"F. c #6C341B", +"G. c #63341D", +"H. c #66351D", +"I. c #6E361A", +"J. c #72361A", +"K. c #773B1A", +"L. c #733A1B", +"M. c #6B351B", +"N. c #6E381C", +"O. c #743A1C", +"P. c #7A3F1C", +"Q. c #793B1A", +"R. c #72381B", +"S. c #71391B", +"T. c #6B391D", +"U. c #6D371B", +"V. c #6B351C", +"W. c #6A3A1E", +"X. c #6B381D", +"Y. c #6A381E", +"Z. c #6E391C", +"`. c #70361A", +" + c #73351A", +".+ c #6B341C", +"++ c #69341D", +"@+ c #64341D", +"#+ c #62321C", +"$+ c #65351C", +"%+ c #62341C", +"&+ c #57301D", +"*+ c #562E1C", +"=+ c #592E1E", +"-+ c #5C321E", +";+ c #582E1D", +">+ c #572E1E", +",+ c #572E1D", +"'+ c #562E1E", +")+ c #55301E", +"!+ c #5B341E", +"~+ c #5F341E", +"{+ c #61341E", +"]+ c #6A361F", +"^+ c #65331D", +"/+ c #69361E", +"(+ c #60321A", +"_+ c #60321B", +":+ c #5E341C", +"<+ c #6A361E", +"[+ c #6A331D", +"}+ c #70371C", +"|+ c #6B361C", +"1+ c #6C371C", +"2+ c #763B1A", +"3+ c #743C1D", +"4+ c #6F3B1E", +"5+ c #713B1D", +"6+ c #703C1E", +"7+ c #6F3A1D", +"8+ c #6B371B", +"9+ c #69371D", +"0+ c #71371A", +"a+ c #7D3C1B", +"b+ c #803B19", +"c+ c #853D18", +"d+ c #83411A", +"e+ c #803F1A", +"f+ c #7C3D1A", +"g+ c #773A19", +"h+ c #733B1C", +"i+ c #6C391C", +"j+ c #78391B", +"k+ c #7E3A19", +"l+ c #813A18", +"m+ c #7C3A1A", +"n+ c #7E3B1A", +"o+ c #7C3B1A", +"p+ c #71371B", +"q+ c #6D361B", +"r+ c #733B1E", +"s+ c #71381D", +"t+ c #6F351B", +"u+ c #6C351C", +"v+ c #69351B", +"w+ c #69351A", +"x+ c #6A3419", +"y+ c #6B341A", +"z+ c #6F361B", +"A+ c #6E311A", +"B+ c #6F341B", +"C+ c #6C351F", +"D+ c #69331A", +"E+ c #68321A", +"F+ c #5D301A", +"G+ c #5E301B", +"H+ c #64341B", +"I+ c #66321B", +"J+ c #5F311B", +"K+ c #5C301B", +"L+ c #67341C", +"M+ c #672F1B", +"N+ c #66341C", +"O+ c #6C361D", +"P+ c #6E381D", +"Q+ c #733C1C", +"R+ c #6B381E", +"S+ c #67351D", +"T+ c #6A361B", +"U+ c #71391C", +"V+ c #71381C", +"W+ c #763B1C", +"X+ c #61341C", +"Y+ c #58311C", +"Z+ c #532D1B", +"`+ c #533323", +" @ c #513729", +".@ c #503A2D", +"+@ c #4F392C", +"@@ c #4F3627", +"#@ c #50301F", +"$@ c #552E1B", +"%@ c #60331B", +"&@ c #6A341A", +"*@ c #68331B", +"=@ c #70361B", +"-@ c #74381A", +";@ c #723819", +">@ c #743719", +",@ c #75371A", +"'@ c #73371A", +")@ c #6E371B", +"!@ c #6F381C", +"~@ c #71371C", +"{@ c #70371A", +"]@ c #71381B", +"^@ c #6F381A", +"/@ c #69341A", +"(@ c #64321A", +"_@ c #65301A", +":@ c #6A331B", +"<@ c #5E2F1B", +"[@ c #8A3F17", +"}@ c #883E17", +"|@ c #813B18", +"1@ c #7B3818", +"2@ c #783919", +"3@ c #743A1B", +"4@ c #73391A", +"5@ c #6F371B", +"6@ c #733B1B", +"7@ c #72391B", +"8@ c #733A1C", +"9@ c #73391B", +"0@ c #75381C", +"a@ c #6D3A1C", +"b@ c #6F381B", +"c@ c #5F321C", +"d@ c #3F281D", +"e@ c #40342F", +"f@ c #4F4B49", +"g@ c #5E5E5E", +"h@ c #6B6B6B", +"i@ c #757575", +"j@ c #7B7B7B", +"k@ c #787878", +"l@ c #6F6F6F", +"m@ c #646464", +"n@ c #565554", +"o@ c #48413D", +"p@ c #3C2C25", +"q@ c #4A2B1C", +"r@ c #66321A", +"s@ c #68321B", +"t@ c #6C361B", +"u@ c #723B1B", +"v@ c #783C1A", +"w@ c #7E3C1A", +"x@ c #7F3E1A", +"y@ c #853E19", +"z@ c #854019", +"A@ c #894219", +"B@ c #843E18", +"C@ c #823D18", +"D@ c #803D18", +"E@ c #7C3818", +"F@ c #823C17", +"G@ c #823E18", +"H@ c #6A381C", +"I@ c #64341C", +"J@ c #6F3519", +"K@ c #793919", +"L@ c #853F18", +"M@ c #8C4419", +"N@ c #8C4217", +"O@ c #8A4017", +"P@ c #853C17", +"Q@ c #813A17", +"R@ c #7D3C19", +"S@ c #763718", +"T@ c #743819", +"U@ c #793D1B", +"V@ c #69341C", +"W@ c #6B371E", +"X@ c #733B1D", +"Y@ c #743B1B", +"Z@ c #68371E", +"`@ c #3A2921", +" # c #353433", +".# c #5B5A5A", +"+# c #6D6D6D", +"@# c #767676", +"## c #7F7F7F", +"$# c #858585", +"%# c #8B8B8B", +"&# c #8F8F8F", +"*# c #8D8D8D", +"=# c #878787", +"-# c #808080", +";# c #7A7A7A", +"># c #707070", +",# c #666666", +"'# c #484746", +")# c #2C2724", +"!# c #4F2E1E", +"~# c #6C3519", +"{# c #6F3619", +"]# c #763919", +"^# c #773819", +"/# c #7E3C19", +"(# c #7E3B19", +"_# c #77391A", +":# c #6F381D", +"<# c #6B361B", +"[# c #69361A", +"}# c #66341B", +"|# c #64351C", +"1# c #65331A", +"2# c #5F341C", +"3# c #58321D", +"4# c #592E1B", +"5# c #5D311B", +"6# c #703A1B", +"7# c #80421B", +"8# c #82401A", +"9# c #8B4017", +"0# c #873E17", +"a# c #833E18", +"b# c #7D3A18", +"c# c #773718", +"d# c #753719", +"e# c #502E1D", +"f# c #272727", +"g# c #444444", +"h# c #646363", +"i# c #696969", +"j# c #7C7C7C", +"k# c #818181", +"l# c #868686", +"m# c #888888", +"n# c #828282", +"o# c #7E7E7E", +"p# c #717171", +"q# c #5A5A5A", +"r# c #333333", +"s# c #372821", +"t# c #60301B", +"u# c #61321B", +"v# c #66341E", +"w# c #5F321D", +"x# c #62331D", +"y# c #65341C", +"z# c #6C3518", +"A# c #6D361C", +"B# c #6C351D", +"C# c #6D3319", +"D# c #70391B", +"E# c #83421C", +"F# c #833F1A", +"G# c #7C3B1B", +"H# c #763819", +"I# c #68361C", +"J# c #5C321C", +"K# c #592D1A", +"L# c #5D2F1A", +"M# c #63311A", +"N# c #64351B", +"O# c #6A341B", +"P# c #71361A", +"Q# c #763A1A", +"R# c #7B3A19", +"S# c #7A3919", +"T# c #7E3A17", +"U# c #482D1F", +"V# c #2A2A2A", +"W# c #484848", +"X# c #5F5F5F", +"Y# c #6C6C6C", +"Z# c #727272", +"`# c #797979", +" $ c #7D7D7D", +".$ c #747474", +"+$ c #6E6E6E", +"@$ c #686868", +"#$ c #616161", +"$$ c #535353", +"%$ c #373737", +"&$ c #312824", +"*$ c #6A351C", +"=$ c #6E361C", +"-$ c #72381A", +";$ c #70371B", +">$ c #6F361C", +",$ c #703619", +"'$ c #6E381E", +")$ c #6B391E", +"!$ c #6E361B", +"~$ c #733819", +"{$ c #743618", +"]$ c #78381A", +"^$ c #793819", +"/$ c #7F3D18", +"($ c #7F3C18", +"_$ c #864019", +":$ c #853F19", +"<$ c #803D1A", +"[$ c #7D3F1C", +"}$ c #713B1C", +"|$ c #6C381C", +"1$ c #60321C", +"2$ c #5C2E1A", +"3$ c #5C2F1A", +"4$ c #613019", +"5$ c #6E3519", +"6$ c #813E19", +"7$ c #873F18", +"8$ c #432C20", +"9$ c #282828", +"0$ c #414141", +"a$ c #504F4F", +"b$ c #555555", +"c$ c #5D5D5D", +"d$ c #6A6A6A", +"e$ c #777777", +"f$ c #585858", +"g$ c #525151", +"h$ c #4A4949", +"i$ c #2F2F2F", +"j$ c #2D2724", +"k$ c #7E3718", +"l$ c #753519", +"m$ c #7B3C1B", +"n$ c #783418", +"o$ c #773418", +"p$ c #773A1A", +"q$ c #6F3A1C", +"r$ c #6C3A1D", +"s$ c #6E381B", +"t$ c #6D3419", +"u$ c #66351C", +"v$ c #5F351E", +"w$ c #512F1E", +"x$ c #4B2A1B", +"y$ c #4F2B1C", +"z$ c #532D1C", +"A$ c #5E321D", +"B$ c #3E2D22", +"C$ c #252525", +"D$ c #323232", +"E$ c #464545", +"F$ c #4D4D4D", +"G$ c #545454", +"H$ c #5C5C5C", +"I$ c #636363", +"J$ c #737373", +"K$ c #656565", +"L$ c #565656", +"M$ c #4F4F4F", +"N$ c #494949", +"O$ c #3F3E3E", +"P$ c #2F2824", +"Q$ c #6F3B1D", +"R$ c #813F1B", +"S$ c #7F3C1A", +"T$ c #7B3B19", +"U$ c #783A19", +"V$ c #733919", +"W$ c #6D351A", +"X$ c #68371D", +"Y$ c #67361E", +"Z$ c #69361D", +"`$ c #6E391D", +" % c #81431B", +".% c #81441C", +"+% c #7B3F1B", +"@% c #703C1D", +"#% c #703C1C", +"$% c #6D361A", +"%% c #72371A", +"&% c #763B1B", +"*% c #7A3B1A", +"=% c #813D19", +"-% c #883F18", +";% c #56331F", +">% c #272726", +",% c #383838", +"'% c #4B4B4B", +")% c #5B5B5B", +"!% c #626262", +"~% c #4E4E4E", +"{% c #464646", +"]% c #3F3F3F", +"^% c #2C2C2C", +"/% c #362923", +"(% c #5D311C", +"_% c #5B321D", +":% c #582D1A", +"<% c #542B1A", +"[% c #532B1B", +"}% c #502B1C", +"|% c #552D1C", +"1% c #572E1B", +"2% c #783E1C", +"3% c #7C3E1A", +"4% c #7E3B1B", +"5% c #79381B", +"6% c #7F3C1B", +"7% c #843C18", +"8% c #833D17", +"9% c #833C17", +"0% c #823A17", +"a% c #7F3D19", +"b% c #823E1A", +"c% c #803D19", +"d% c #853F1A", +"e% c #863E17", +"f% c #863D18", +"g% c #2A2725", +"h% c #262625", +"i% c #292929", +"j% c #4A4A4A", +"k% c #676767", +"l% c #454545", +"m% c #3C3C3C", +"n% c #303030", +"o% c #4C2D1F", +"p% c #74371A", +"q% c #753A1C", +"r% c #7A3E1C", +"s% c #7E3D1B", +"t% c #894119", +"u% c #914518", +"v% c #954417", +"w% c #8C461B", +"x% c #61351E", +"y% c #63331C", +"z% c #67351B", +"A% c #763C1B", +"B% c #793E1B", +"C% c #7D421B", +"D% c #5F311C", +"E% c #542D1C", +"F% c #332722", +"G% c #262626", +"H% c #252524", +"I% c #404040", +"J% c #3B3B3B", +"K% c #272625", +"L% c #753517", +"M% c #723618", +"N% c #6B3318", +"O% c #663018", +"P% c #673018", +"Q% c #652E19", +"R% c #632F19", +"S% c #633019", +"T% c #592F1A", +"U% c #5A311B", +"V% c #5D341D", +"W% c #5E351D", +"X% c #8F3F17", +"Y% c #874018", +"Z% c #803C19", +"`% c #823C19", +" & c #833E19", +".& c #863E18", +"+& c #8C4117", +"@& c #8B3F17", +"#& c #893E17", +"$& c #61301A", +"%& c #4F2C1C", +"&& c #353535", +"*& c #606060", +"=& c #595959", +"-& c #3A3A3A", +";& c #2E2E2E", +">& c #452C20", +",& c #7D3718", +"'& c #7D3918", +")& c #853B17", +"!& c #843B17", +"~& c #843E19", +"{& c #833F19", +"]& c #873E16", +"^& c #8A3E17", +"/& c #6F3A1B", +"(& c #783A1A", +"_& c #783B1B", +":& c #753819", +"<& c #7E3B18", +"[& c #813C17", +"}& c #7E3917", +"|& c #7D3B1A", +"1& c #4B2E20", +"2& c #2B2B2B", +"3& c #343434", +"4& c #505050", +"5& c #575757", +"6& c #525252", +"7& c #4C4C4C", +"8& c #393939", +"9& c #68341B", +"0& c #65331B", +"a& c #65361C", +"b& c #67361D", +"c& c #68381D", +"d& c #693A1E", +"e& c #6A391E", +"f& c #6A3921", +"g& c #6D391D", +"h& c #68351E", +"i& c #68351C", +"j& c #69381C", +"k& c #723619", +"l& c #773818", +"m& c #793918", +"n& c #302824", +"o& c #2D2D2D", +"p& c #515151", +"q& c #424242", +"r& c #472C1F", +"s& c #562C1A", +"t& c #562E1B", +"u& c #5C311C", +"v& c #5A311C", +"w& c #592F1B", +"x& c #58301C", +"y& c #5E341D", +"z& c #65361D", +"A& c #673821", +"B& c #6E371A", +"C& c #6F361A", +"D& c #70351B", +"E& c #6E3419", +"F& c #3E3E3E", +"G& c #474747", +"H& c #382921", +"I& c #6C371A", +"J& c #6B3419", +"K& c #693419", +"L& c #6A361A", +"M& c #6A351B", +"N& c #6A361C", +"O& c #6E3A1B", +"P& c #71351A", +"Q& c #6D361D", +"R& c #70351A", +"S& c #753B1A", +"T& c #52311F", +"U& c #3D3D3D", +"V& c #65311B", +"W& c #6A361D", +"X& c #753A1B", +"Y& c #6E3A1D", +"Z& c #70391C", +"`& c #6F371C", +" * c #6C371D", +".* c #6D361E", +"+* c #783F1D", +"@* c #402C22", +"#* c #2B2624", +"$* c #67331C", +"%* c #6F391C", +"&* c #6D3A1E", +"** c #75391A", +"=* c #853D17", +"-* c #7D3D1B", +";* c #723E1D", +">* c #703B1C", +",* c #70381B", +"'* c #66361C", +")* c #382A23", +"!* c #434343", +"~* c #313131", +"{* c #2B2725", +"]* c #75391B", +"^* c #70381A", +"/* c #7C401C", +"(* c #7F421B", +"_* c #874119", +":* c #6A371B", +"<* c #6C391D", +"[* c #793D1A", +"}* c #783D1C", +"|* c #743B1D", +"1* c #6B371C", +"2* c #342822", +"3* c #292625", +"4* c #57331F", +"5* c #66361D", +"6* c #713618", +"7* c #723A1C", +"8* c #713A1B", +"9* c #6B3B1D", +"0* c #63331F", +"a* c #63341F", +"b* c #673620", +"c* c #63331B", +"d* c #803A18", +"e* c #7F3919", +"f* c #7D3A19", +"g* c #6D341A", +"h* c #372922", +"i* c #292624", +"j* c #5D3221", +"k* c #6B3521", +"l* c #62321F", +"m* c #833E1A", +"n* c #2B2625", +"o* c #462B1E", +"p* c #363636", +"q* c #372A23", +"r* c #282524", +"s* c #4D3020", +"t* c #83401A", +"u* c #7E3E1B", +"v* c #472E21", +"w* c #6A391D", +"x* c #86421B", +"y* c #763E1D", +"z* c #733D1D", +"A* c #6F3C1D", +"B* c #3C2C23", +"C* c #773B1C", +"D* c #67331B", +"E* c #3A2B23", +"F* c #67341D", +"G* c #72381C", +"H* c #68361D", +"I* c #61331B", +"J* c #62321A", +"K* c #67341B", +"L* c #61331C", +"M* c #462D20", +"N* c #68331C", +"O* c #6E391E", +"P* c #72371B", +"Q* c #6D381A", +"R* c #5D301B", +"S* c #59301C", +"T* c #59311D", +"U* c #512B1B", +"V* c #512A1B", +"W* c #5D2F1B", +"X* c #62311A", +"Y* c #3A2A22", +"Z* c #322823", +"`* c #6C3419", +" = c #69351C", +".= c #683820", +"+= c #65371E", +"@= c #69381E", +"#= c #65371D", +"$= c #62321B", +"%= c #5F341D", +"&= c #61341D", +"*= c #63311B", +"== c #66331B", +"-= c #6C3319", +";= c #352923", +">= c #5F331D", +",= c #733618", +"'= c #77381A", +")= c #76391B", +"!= c #6D371A", +"~= c #6B3A1E", +"{= c #853E18", +"]= c #823F1A", +"^= c #823F19", +"/= c #823A18", +"(= c #7D3818", +"_= c #7C3718", +":= c #78371A", +"<= c #7E3818", +"[= c #472B1E", +"}= c #8A4218", +"|= c #863C17", +"1= c #813C19", +"2= c #873E18", +"3= c #8E3E16", +"4= c #8E3E17", +"5= c #57331E", +"6= c #522E1C", +"7= c #532C1B", +"8= c #5D2D1A", +"9= c #5D2C19", +"0= c #5F2D19", +"a= c #612F19", +"b= c #642F19", +"c= c #6A3318", +"d= c #6E3318", +"e= c #6F3419", +"f= c #84411B", +"g= c #382A22", +"h= c #43291E", +"i= c #7A401B", +"j= c #743D1B", +"k= c #753D1C", +"l= c #6E381A", +"m= c #6E3A1C", +"n= c #62331C", +"o= c #5B351E", +"p= c #944316", +"q= c #884219", +"r= c #823F1B", +"s= c #7A3E1D", +"t= c #753E1D", +"u= c #723A1E", +"v= c #713C1E", +"w= c #6C391E", +"x= c #75361A", +"y= c #75381B", +"z= c #63301A", +"A= c #512A1A", +"B= c #332621", +"C= c #262524", +"D= c #2F2723", +"E= c #88421A", +"F= c #823E1B", +"G= c #7F3D1B", +"H= c #803B1A", +"I= c #844019", +"J= c #7B3C1C", +"K= c #813D1C", +"L= c #813D1B", +"M= c #8A441A", +"N= c #984717", +"O= c #63321B", +"P= c #5F2F1D", +"Q= c #592C1B", +"R= c #572C1B", +"S= c #4D2A1B", +"T= c #4D291B", +"U= c #4F291A", +"V= c #53291A", +"W= c #532A1A", +"X= c #57301C", +"Y= c #5A301B", +"Z= c #68331A", +"`= c #4B2C1C", +" - c #2E2621", +".- c #2B2523", +"+- c #422A1E", +"@- c #743818", +"#- c #853D16", +"$- c #713619", +"%- c #6C381B", +"&- c #6B3A1C", +"*- c #6E3B1D", +"=- c #763D1B", +"-- c #82441C", +";- c #7E411B", +">- c #793B19", +",- c #7E3E1A", +"'- c #67381E", +")- c #63371E", +"!- c #64381E", +"~- c #65361E", +"{- c #63351D", +"]- c #66351A", +"^- c #703719", +"/- c #7F3F1B", +"(- c #78391A", +"_- c #763618", +":- c #693217", +"<- c #602D16", +"[- c #5A2914", +"}- c #432012", +"|- c #2D1C15", +"1- c #241E1B", +"2- c #23211F", +"3- c #291C16", +"4- c #402313", +"5- c #5F3015", +"6- c #723C18", +"7- c #6A351A", +"8- c #4E2B1B", +"9- c #502A1B", +"0- c #532E1D", +"a- c #5C331D", +"b- c #6D3A1D", +"c- c #703519", +"d- c #6A3A1F", +"e- c #743519", +"f- c #783519", +"g- c #773719", +"h- c #763419", +"i- c #763519", +"j- c #7A3618", +"k- c #7F3918", +"l- c #7A3719", +"m- c #79381A", +"n- c #7A3716", +"o- c #733313", +"p- c #602B10", +"q- c #4A220E", +"r- c #351A0B", +"s- c #261309", +"t- c #21150E", +"u- c #211A17", +"v- c #221E1C", +"w- c #232120", +"x- c #242322", +"y- c #23201E", +"z- c #211C19", +"A- c #211712", +"B- c #22120A", +"C- c #2C160A", +"D- c #3F1D0B", +"E- c #59290E", +"F- c #6F3212", +"G- c #7D3815", +"H- c #7C3A18", +"I- c #6E3418", +"J- c #5B2E1A", +"K- c #5A2E1A", +"L- c #6B361D", +"M- c #813F1A", +"N- c #843F1A", +"O- c #7E3D1A", +"P- c #743B1C", +"Q- c #773B1D", +"R- c #6C371F", +"S- c #6E381F", +"T- c #6F351C", +"U- c #703419", +"V- c #713419", +"W- c #703319", +"X- c #6B351E", +"Y- c #633119", +"Z- c #653219", +"`- c #5F2F18", +" ; c #532A16", +".; c #472613", +"+; c #3A1E10", +"@; c #321A0E", +"#; c #2C170C", +"$; c #28160B", +"%; c #25140A", +"&; c #241209", +"*; c #231208", +"=; c #241308", +"-; c #271409", +";; c #2B150A", +">; c #30170A", +",; c #371B0C", +"'; c #3E1E0D", +"); c #4C2410", +"!; c #602D12", +"~; c #703414", +"{; c #773615", +"]; c #763818", +"^; c #6C361A", +"/; c #5D321D", +"(; c #65351D", +"_; c #703B1E", +":; c #7A401D", +"<; c #7F421D", +"[; c #80411C", +"}; c #773D1C", +"|; c #73371C", +"1; c #63321D", +"2; c #61321C", +"3; c #61331D", +"4; c #61321D", +"5; c #60311A", +"6; c #552D1A", +"7; c #542D19", +"8; c #4C2917", +"9; c #452415", +"0; c #3F2214", +"a; c #3C2012", +"b; c #3A2012", +"c; c #3C2011", +"d; c #3B2013", +"e; c #3E2010", +"f; c #401F0F", +"g; c #3F2012", +"h; c #432212", +"i; c #482613", +"j; c #4C2714", +"k; c #582B15", +"l; c #652F15", +"m; c #6B3317", +"n; c #743718", +"o; c #863F18", +"p; c #883F17", +"q; c #893F18", +"r; c #813C18", +"s; c #763918", +"t; c #64301A", +"u; c #5D331D", +"v; c #5C341D", +"w; c #6F3618", +"x; c #803B18", +"y; c #803C18", +"z; c #7D3C18", +"A; c #753919", +"B; c #733718", +"C; c #663218", +"D; c #623118", +"E; c #613117", +"F; c #5D2E17", +"G; c #5B2E18", +"H; c #5A2E17", +"I; c #592F16", +"J; c #542C16", +"K; c #552D16", +"L; c #572B14", +"M; c #592B15", +"N; c #5A2D15", +"O; c #5E2E15", +"P; c #623017", +"Q; c #652F18", +"R; c #693318", +"S; c #6F3819", +"T; c #773918", +"U; c #7B391A", +"V; c #8A3E16", +"W; c #8D4117", +"X; c #8D4218", +"Y; c #68371C", +"Z; c #813D18", +"`; c #803E19", +" > c #723B1C", +".> c #67321B", +"+> c #6E3619", +"@> c #673319", +"#> c #653118", +"$> c #6A3217", +"%> c #6B3218", +"&> c #6A341C", +"*> c #70391D", +"=> c #79361A", +"-> c #7A371A", +";> c #803917", +">> c #883D17", +",> c #7D3B19", +"'> c #72391C", +")> c #6F391D", +"!> c #7A381A", +"~> c #72391D", +"{> c #71371D", +"]> c #77371A", +"^> c #69371C", +"/> c #67371E", +"(> c #67331D", +"_> c #5C301C", +":> c #61301C", +"<> c #69321B", +"[> c #6D341B", +"}> c #67321E", +"|> c #60321D", +"1> c #66331A", +"2> c #64331A", +"3> c #74371B", +"4> c #7C391A", +"5> c #7B391B", +"6> c #74381C", +"7> c #7B3A1A", +"8> c #66341D", +"9> c #62361D", +"0> c #5F311A", +"a> c #60311B", +"b> c #68341D", +"c> c #60331F", +"d> c #60331E", +"e> c #5B2E1C", +"f> c #562D1C", +"g> c #582F1E", +"h> c #582F1D", +"i> c #542E1C", +"j> c #59301D", +"k> c #60311C", +"l> c #6A381D", +"m> c #65321D", +"n> c #65341D", +"o> c #6C3219", +"p> c #6B3119", +"q> c #693019", +"r> c #64321C", +"s> c #62311D", +"t> c #64351F", +"u> c #6B3721", +"v> c #693921", +"w> c #62341F", +"x> c #5A2D1B", +"y> c #592D1B", +"z> c #592F1C", +"A> c #5F2E1A", +"B> c #69391D", +"C> c #6D3621", +"D> c #70351E", +"E> c #6E3620", +"F> c #66331E", +"G> c #61321E", +"H> c #5B311D", +"I> c #5D311D", +"J> c #6A351F", +"K> c #6B3620", +"L> c #6B361F", +"M> c #6E3621", +"N> c #69371E", +". + @ # $ % % & * = - ; > = , ' ) ! ~ { ] ^ / ( _ : < < [ } | ^ / ( _ : < < [ 1 2 3 @ 4 5 6 7 8 1 9 0 a b c d e f g h i ", +"j k l m n n o p q r s t u v w x y z A B C D E F G H I J K L L M N M O P Q R S T ! U V W X Y Z ` ...+.@.#.$.%.&.*.=.-.;.", +"= >.,.'.).; !.~.{.].^.' /./.(._.:.<.[.}.|.1.2.3.4.5.6.7.8.9.0.a.K b.c.d.e.f.g.h.i.j.T _.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.", +"A.A.B.C.D.E.F.G.H.I.J.K.L.M.M.1 N.O.P.Q.R.S.T.e.1 M.U.V.A f.W.X.Y.X.Z./.`. +F..+++@+#+$+%+&+*+> =+-+;+>+,+,+'+)+!+~+{+]+", +"^+/+^.+.(+_+(+:+#+<+[+T }+Z.|+1+2+3+4+5+6+7+8+N.9+0+a+b+c+d+e+f+g+h+i+b.Q.j+k+l+m+d q n+o+p+q+r+s+t+u+v+w+x+y+z+! A+B+C+", +"+ D+E+F+G+H+I+J+K+L+M+N+e.O+P+Q+M R+^+S+T+U+V+W+V+X+Y+Z+`+ @.@+@@@#@$@%@&@*@=@-@J.;@p >@,@'@)@!@U+!@~@{@]@^@/@(@' _@:@<@", +"[@[@}@|@1@2@3@4@5@1 D 6@7@b.8@9@0@V+p+J.a@!@b@c@d@e@f@g@h@i@j@k@l@m@n@o@p@q@r@s@D.R ] *@t@u@v@w@x@y@z@A@B@C@D@E@F@G@}.k+", +"n.H@I@J@K@L@M@N@O@P@Q@R@S@T@U@l V@W@X@Y@,.Z@`@ #.#+#@###$#%#&#*#=#-#;#>#,#'#)#!#~#{#]#^#/#(#_#:#:#:#<#[#}#' ' D.3.1+}#|#", +"R.T 1#2#3#4#5#6#7#8#9#0#B@a#b#c#d#'@;@p e#f#g#h#i#l@@#j#k#l#m#l#n#o#k@p#h@m@q#r#s#{.t#u#v#N+, H+w#x#y#z#A#B#M.>@g C#3.D#", +"E#F#G#H#]@I#J#K#L#M#N#O#P#Q#R#S#o+T#R#U#V#W#.#X#,#Y#Z#`# $-#n#k#o#;#.$+$@$#$.#$$%$&${.*$=$V+-$`.;$>$D.,$'$)$R+!$~${$]$^$", +"D@/$($_$:$<$[$P.}$|$1$2$3$3$4$5$6$7$8$9$0$a$b$c$m@d$p#@#`#j# $j#;#e$Z#+#,#X#f$g$h$i$j$B.k$l$>@d#b m$S#n$o$a c#Q#p$_#c 2@", +"M.t@q$r$T.] s$t$u$v$w$x$y$z$A$<#2+B$C$D$E$F$G$H$I$i#+$J$@#k@`#`#e$.$>#h@K$g@L$M$N$O$C$P$Q$R$S$T$U$V$W$e.X$Y$* N+L+Z$* `$", +" %.%+%@%#%q$A $%-${@%%&%*%=%:$-%;%C$>%,%g#'%G$)%!%@$+#>#J$.$i@i@.$Z#+$i#m@g@L$~%{%]%^%C$/%L#(%_%:%<%[%}%y$|%1%x E+z+2%3%", +"4%5%6%7%8%9%0%a%b%c%d%L@e%f%<$T+g%h%i%,%0$j%$$)%!%k%d$+#l@>#p#p#>#+$h@@$I$c$L$~%l%m%n%C$C$o%p%y.,@N.7+U+;$q%r%s%t%u%v%w%", +"x%; G+y%z%n.a@^@A%B%C%&%<#D%E%F%G%H%i%%$I%j%$$q#X#I$k%i#d$Y#Y#Y#d$i#@$m@#$H$L$~%g#J%n%H%C$K%t$L%M%N%O%P%Q%R%S%T%U%V%W%p.", +"X%Y%P@Z%`% &b%.&+&@&#&Z%J@$&%&K%C$G%i%&&I%j%$$f$c$*&!%m@,#,#k%k%,#m@I$*&c$=&b$F$g#-&;&f#h%C$>&,&'&*%l+)&!&~&{&{&F#7$]&^&", +"s$/&Q+W+(&_&:&^#<&[&}&|&p$_#1&C$h%9$2&3&]%N$4&b$=&)%c$*&#$!%I$!%#$*&g@H$q#5&6&7&g#8&;&9$G%C$C$` + (@o.9&0&a&u.b&c&d&e&C ", +"f&g&q+h&i&j&}#/.M%p k&:&l&m&n&C$G%9$o&&&]%W#F$p&G$5&=&)%H$c$c$c$H$)%=&f$b$6&~%j%q&8&n%V#f#h%C$r&K#s&t&x u&v&w&x&v&y&z&A&", +"i+B&C&B&{@]@D&0+Y@X@O+++E&I#C$G%f#V#n%%$F&l%N$F$M$6&b$5&f$f$f$f$5&L$L$$$4&F$'%G&0$-&r#^%9$G%C$H&k.I&x+J&K&o.L&M&N&A O&j&", +"R 5@/.-.P&C&Q&7@R&=$] ]@S&T&C$h%9$2&D$8&]%g#l%W#'%~%M$p&6&$$$$$$6&p&4&~%7&N$G&g#q&U&&&;&i%f#G%j$V&V&i&1+v+W$z+..W&V+X&R ", +"L.Y&..Z&`& *|+U..*$+X.+*_ @*C$G%9$o&3&m%0$g#l%l%G&N$j%'%F$F$F$F$F$7&'%N$W#{%g#g#q&F&%$i$V#f#G%#*Y$t@J+$*..%*A#&*Y&b.c **", +"=*-*{@..q+$%Y&;*Q$>*L.,*'*)*C$G%9$;&&&U&q&l%g#l%l%{%{%G&W#W#W#W#W#G&{%{%l%{%g#l%!*]%8&~*V#f#G%{*D.K@q ]*8+* U.^*Y@/*(*_*", +":*<#N&%*' <*1.=@[*}*|* *1*2*G%f#i%;&&&U&q&g#l%l%{%l%l%l%{%{%{%{%l%l%l%l%{%{%l%l%!*I%8&~*V#f#G%3*4*5*6*k&`&7*z%g&8*;$S.9*", +"b%0*a*b*c*z&Q$X&d*e*f*p+g*h*C$G%i%;&&&m%q&l%l%l%{%{%{%{%l%l%l%l%l%{%{%{%l%{%g#g#!*I%8&~*V#f#G%i*j*k*l*0*a*b*c*z&Q$X&d*m*", +"| ^ / ( _ : < < [ 1 2 3 @ 2*C$G%9$^%3&m%q&!*l%l%{%{%{%{%{%{%{%{%{%{%{%l%{%l%l%g#!*]%,%n%V#f#h%n*~+{ ] ^ / ( _ : < < [ } ", +"L M N M O P Q R S T ! U V o*C$h%9$2&D$-&I%!*l%g#l%{%{%{%{%{%{%{%{%{%l%{%l%l%l%g#0$U&p*;&i%f#h%#*..B C D E F G H I J K L ", +"0.a.K b.c.d.e.f.g.h.i.j.T D%C$h%f#V#n%%$F&q&g#l%l%{%l%{%{%{%{%{%{%{%{%l%l%g#l%g#I%J%r#^%9$G%G%q*[.}.|.1.2.3.4.5.6.7.8.9.", +"W.X.Y.X.Z./.`. +F..+++@+#+%+r*C$G%i%;&&&m%0$!*g#l%l%{%{%l%{%{%{%{%l%{%g#g#l%g#q&F&%$~*2&f#G%C$s*P.Q.R.S.T.e.1 M.U.V.A f.", +"t*u*Q.h+i+b.Q.j+k+l+m+d q m+v*C$h%f#2&D$8&F&q&!*l%l%g#l%l%l%{%{%l%l%g#l%l%g#q&I%J%3&o&i%f#G%C$w*4+5+6+7+8+N.9+0+a+b+.&x*", +"y*z*A*Z.1+N.M.*@=@-@J.;@p >@w#h%C$f#i%;&3&-&]%0$!*l%l%g#g#l%l%l%l%g#l%g#g#q&0$m%%$n%2&f#h%C$B*R+^+S+T+U+V+W+V+9+X.C&|.J ", +"C*P+m.7*] E.N&i&D*s@D.R ] *@z%E*C$G%f#V#i$&&J%]%q&!*g#l%g#g#g#g#l%g#l%g#!*I%U&,%~*^%9$G%C$C$F*V+p+J.a@!@b@G*5@F.U.U.b@|.", +"Y&H*y#%+I*0&J*(+c*K*~#{#]#^#/#L*3*H%G%9$2&n%&&J%F&0$!*!*g#l%l%l%g#g#!*q&I%m%,%D$o&9$f#H%C$M*N*W@X@Y@,.O*R D#s$P*Q*Z&B q+", +"; R*S*x&x&T*1%U*V*<%W*X*t#u#v#N+Y*C$H%h%9$2&i$3&8&U&]%0$q&q&q&q&q&0$I%U&-&%$D$o&9$f#H%G%Z*`*d#'@;@p ~#9&l.M.D.T =.=+=Y ", +"@=#=$=%=&=|#' c*J+*===<#-=*$=$V+k.;=G%H%h%9$2&;&D$&&8&J%m%F&F&F&U&m%-&%$3&n%^%i%G%H%C$3*>=S#o+T#Z%/#g+,=S@'=)=3+%*B&!=~=", +".&{=C@]=^=0%/=(=n _=(=0 :=<=k$l$>@/.q*G%H%h%f#i%2&;&~*r#&&%$%$%$p*3&D$n%o&V#9$f#h%C$G%[=4$5$6$7$[@}=.&f%|=7%1=P@2=3=4==*", +"5=6=7=2$8=9=0=a=b=c=d=e=n c%f=R$S$T$k.g=C$C$G%f#f#i%V#o&o&;&i$i$;&^%2&V#9$f#f#H%C$G%h=z$A$<#2+i=j=k=l=m=m.'*%+V&n=}#i&o=", +"p=q=r=s=t=u=v=w=t@'@x=]$y=-$T z=(%_%:%A=B=C=C$C$h%G%f#9$9$i%i%i%9$9$f#f#h%h%C$h%D=a&*%=%:$-%E=r=} F=G=H=G I=b%J=K=L=M=N=", +"h+O=P=Q=R=S=T=U=V=W=t X=Y=*=Z=E&p%y.,@N.7+`= -h%G%G%h%G%G%G%f#G%G%G%h%C$G%G%.-+-@-L@#-f%<$/#v@-$P&g $-%-&-*-*-=---;->-,-", +"'-)-)-!-~-~-{-]-U.^-H#b (&/-|&(-_-L%M%:-<-[-}-|-1-H%f#f#G%G%G%G%G%G%f#G%2-3-4-5-6-k 7-D%*+8-9-0-a-y#y+q$S.b-<*1 c--=A d-", +")=S@S@M%$-e-_-f-(-g-h-i-j-k-<=l-m-,&'&U$n-o-p-q-r-s-t-u-v-w-x-w-y-z-A-B-C-D-E-F-G-H-I-$&J-:%K-y L-S.&%M-N-_$N-O-{&,-P-Q-", +"0+^#i-U.R-S-)@M&.*T-U-V-W-t+X-:@&@p.+ Y-Z-`- ;.;+;@;#;$;%;&;*;=;-;;;>;,;';);!;~;{;R#p$(&)=];^;I@G.W*{./;(;_;C*:;<;[;};!$", +"O#|;_#/@U N&T+= 1;2;3;L*= v#a*4;` 5;K#s&6;:%7;8;9;0;a;b;c;d;a;e;f;g;h;i;j;k;l;m;r n;l&H-a#o;p;}@q;r;s;t;K-u;v;+.O#q+1+V.", +"U 9&T K*}#K*[#w;6*-$U+;.x;y;(#z;A;B;k.I&x+c=C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;Z&V.++E&X&T@T;m$U;/=V;W;X;D@}* =L*I#Y;c*9&", +"a#C@E@Z;C@o;M@~&Z%`;*%h >9&0&] G*)@.>V&i&1+o.J&l.S 0&(.+>o.o.@>#>$>%>J&&>^*e==$] ]@S&b.b.L.3+*>5@c-p%=>->;>0%>>@&^&,>}.", +"*=|#|#K*I&5@5@R.'>Z&)>X&!>i ~>{>]>c 7.t@J+$*..%*A#&*Y&b.c **L.Y&..Z&`& *|+U..*$+X.+*_ ^>/>/+(>, '.u&_>:>z&x y <>[>}>|>0&", +"<>O#E.1>2>2>0&v+q+Z&u=_.3>4>5>i 6>7>l+K@q ]*8+* U.^*Y@/*(*_*=*-*{@..q+$%Y&;*Q$>*L.,*'*u$1 `&9&8>9>1$5#0>a>5;(+u$b>s Z$<>", +"c>d>e>,+f>f>,+,+g>h>h>i>j>2;k>c@>=(>l>5*6*k&`&7*z%g&8*;$S.9*:*<#N&%*' <*1.=@[*}*|* *1*3.t@L.L.-$F.m>n>[>o>p>q>r>r>s>@+t>", +"u>v>w>>=x>y>z>A>I#B>B>..T E.M.B#C>D>E>k*l*0*a*b*c*z&Q$X&d*m*b%0*a*b*c*z&Q$X&d*e*f*p+g*3.3.M&V@F>G>H>I>4;'.&={+J>K>L>M>N>"}; diff --git a/app/examples/Basic/Blights/blon.xpm b/app/examples/Basic/Blights/blon.xpm new file mode 100644 index 00000000..dee0b520 --- /dev/null +++ b/app/examples/Basic/Blights/blon.xpm @@ -0,0 +1,1503 @@ +/* XPM */ +static char * blon_xpm[] = { +"60 48 1452 2", +" c None", +". c #70341A", +"+ c #65321B", +"@ c #5C301D", +"# c #63311C", +"$ c #642E19", +"% c #642D19", +"& c #642E1A", +"* c #69381D", +"= c #65331C", +"- c #64311B", +"; c #5E321C", +"> c #5A301D", +", c #64331B", +"' c #65341B", +") c #6C341C", +"! c #6C321A", +"~ c #6B371D", +"{ c #68361F", +"] c #6D371C", +"^ c #73381D", +"/ c #79381D", +"( c #813C1A", +"_ c #753B1C", +": c #713A1D", +"< c #6E371D", +"[ c #6F391E", +"} c #7E3D19", +"| c #7B3E1C", +"1 c #6E371C", +"2 c #68351F", +"3 c #60311E", +"4 c #4E2C1C", +"5 c #4A2B1D", +"6 c #502D1C", +"7 c #582D1D", +"8 c #63301D", +"9 c #78391C", +"0 c #7A391A", +"a c #763619", +"b c #773A1B", +"c c #76391A", +"d c #76381A", +"e c #77381B", +"f c #773A1C", +"g c #733719", +"h c #743A1A", +"i c #76381B", +"j c #6F3719", +"k c #72391A", +"l c #73381A", +"m c #743619", +"n c #743518", +"o c #743517", +"p c #723719", +"q c #77391B", +"r c #6F3418", +"s c #68321D", +"t c #552D1B", +"u c #4F2C1B", +"v c #502C1B", +"w c #512D1B", +"x c #5B2F1B", +"y c #5F301B", +"z c #66361E", +"A c #6D381C", +"B c #6F391B", +"C c #6D381B", +"D c #6D351B", +"E c #743C1C", +"F c #753C1D", +"G c #823C1A", +"H c #793C1B", +"I c #793B1C", +"J c #773C1C", +"K c #783D1B", +"L c #86461C", +"M c #793E1C", +"N c #793F1C", +"O c #833D1A", +"P c #7A3C1C", +"Q c #77391C", +"R c #70381C", +"S c #66351B", +"T c #6B341B", +"U c #68351B", +"V c #67361C", +"W c #613119", +"X c #643119", +"Y c #63341B", +"Z c #60301A", +"` c #64311A", +" . c #65321A", +".. c #6A371C", +"+. c #61321A", +"@. c #5F301D", +"#. c #61331F", +"$. c #5E331D", +"%. c #5C321D", +"&. c #592F1E", +"*. c #62311B", +"=. c #693119", +"-. c #713519", +";. c #75381A", +">. c #69351E", +",. c #6D381D", +"'. c #62341D", +"). c #60331D", +"!. c #5D301C", +"~. c #5B311C", +"{. c #5C2F1B", +"]. c #62311C", +"^. c #6B361E", +"/. c #6C351A", +"(. c #6B351A", +"_. c #6E351A", +":. c #74381B", +"<. c #7B3B1B", +"[. c #7E3918", +"}. c #833D18", +"|. c #773B1B", +"1. c #713A1C", +"2. c #6C381D", +"3. c #69341B", +"4. c #6C371E", +"5. c #6C361E", +"6. c #6C351E", +"7. c #73391D", +"8. c #77381D", +"9. c #81401B", +"0. c #88451B", +"a. c #80411B", +"b. c #723A1B", +"c. c #703A1D", +"d. c #6A371E", +"e. c #68351D", +"f. c #65341E", +"g. c #6C341D", +"h. c #70351C", +"i. c #6F371F", +"j. c #6E3720", +"k. c #6F371A", +"l. c #6C341A", +"m. c #6B381C", +"n. c #6B391C", +"o. c #68341A", +"p. c #67321A", +"q. c #622D18", +"r. c #612C19", +"s. c #612D19", +"t. c #612E1A", +"u. c #64331C", +"v. c #66361F", +"w. c #6B3720", +"x. c #6F351E", +"y. c #76371A", +"z. c #6F371D", +"A. c #65351E", +"B. c #63321C", +"C. c #6A321A", +"D. c #6C351B", +"E. c #6C361C", +"F. c #6C341B", +"G. c #63341D", +"H. c #66351D", +"I. c #6E361A", +"J. c #72361A", +"K. c #773B1A", +"L. c #733A1B", +"M. c #6B351B", +"N. c #6E381C", +"O. c #743A1C", +"P. c #7A3F1C", +"Q. c #793B1A", +"R. c #72381B", +"S. c #71391B", +"T. c #6B391D", +"U. c #6D371B", +"V. c #6B351C", +"W. c #6A3A1E", +"X. c #6B381D", +"Y. c #6A381E", +"Z. c #6E391C", +"`. c #70361A", +" + c #73351A", +".+ c #6B341C", +"++ c #69341D", +"@+ c #64341D", +"#+ c #62321C", +"$+ c #65351C", +"%+ c #62341C", +"&+ c #57301D", +"*+ c #562E1C", +"=+ c #592E1E", +"-+ c #5C321E", +";+ c #582E1D", +">+ c #572E1E", +",+ c #572E1D", +"'+ c #562E1E", +")+ c #55301E", +"!+ c #5B341E", +"~+ c #5F341E", +"{+ c #61341E", +"]+ c #6A361F", +"^+ c #65331D", +"/+ c #69361E", +"(+ c #60321A", +"_+ c #60321B", +":+ c #5E341C", +"<+ c #6A361E", +"[+ c #6A331D", +"}+ c #70371C", +"|+ c #6B361C", +"1+ c #6C371C", +"2+ c #763B1A", +"3+ c #743C1D", +"4+ c #6F3B1E", +"5+ c #713B1D", +"6+ c #703C1E", +"7+ c #6F3A1D", +"8+ c #6B371B", +"9+ c #69371D", +"0+ c #71371A", +"a+ c #7D3C1B", +"b+ c #803B19", +"c+ c #883E18", +"d+ c #89431B", +"e+ c #87411A", +"f+ c #823F1B", +"g+ c #7C3B1A", +"h+ c #743B1C", +"i+ c #6C391C", +"j+ c #78391B", +"k+ c #7E3A19", +"l+ c #813A18", +"m+ c #7C3A1A", +"n+ c #7E3B1A", +"o+ c #71371B", +"p+ c #6D361B", +"q+ c #733B1E", +"r+ c #71381D", +"s+ c #6F351B", +"t+ c #6C351C", +"u+ c #69351B", +"v+ c #69351A", +"w+ c #6A3419", +"x+ c #6B341A", +"y+ c #6F361B", +"z+ c #6E311A", +"A+ c #6F341B", +"B+ c #6C351F", +"C+ c #69331A", +"D+ c #68321A", +"E+ c #5D301A", +"F+ c #5E301B", +"G+ c #64341B", +"H+ c #66321B", +"I+ c #5F311B", +"J+ c #5C301B", +"K+ c #67341C", +"L+ c #672F1B", +"M+ c #66341C", +"N+ c #6C361D", +"O+ c #6E381D", +"P+ c #733C1C", +"Q+ c #6B381E", +"R+ c #67351D", +"S+ c #6A361B", +"T+ c #71391C", +"U+ c #71381C", +"V+ c #763B1C", +"W+ c #7F3C1E", +"X+ c #954420", +"Y+ c #AC5328", +"Z+ c #C46938", +"`+ c #CF7543", +" @ c #D47848", +".@ c #D17746", +"+@ c #C56F3E", +"@@ c #B55F31", +"#@ c #9D4821", +"$@ c #8C3F1D", +"%@ c #73361B", +"&@ c #68331B", +"*@ c #70361B", +"=@ c #74381A", +"-@ c #723819", +";@ c #743719", +">@ c #75371A", +",@ c #73371A", +"'@ c #6E371B", +")@ c #6F381C", +"!@ c #71371C", +"~@ c #70371A", +"{@ c #71381B", +"]@ c #6F381A", +"^@ c #69341A", +"/@ c #64321A", +"(@ c #65301A", +"_@ c #6A331B", +":@ c #5E2F1B", +"<@ c #8A3F17", +"[@ c #883E17", +"}@ c #813B18", +"|@ c #7B3818", +"1@ c #783919", +"2@ c #743A1B", +"3@ c #73391A", +"4@ c #6F371B", +"5@ c #733B1B", +"6@ c #72391B", +"7@ c #733A1C", +"8@ c #73391B", +"9@ c #75381C", +"0@ c #6D3A1C", +"a@ c #6F381B", +"b@ c #A24621", +"c@ c #CF622F", +"d@ c #E89558", +"e@ c #FCC281", +"f@ c #FFD599", +"g@ c #FEE0AA", +"h@ c #FEE7B9", +"i@ c #FEE8C0", +"j@ c #FFE8BC", +"k@ c #FEE4B0", +"l@ c #FFDAA0", +"m@ c #FECE8E", +"n@ c #F1AD6C", +"o@ c #DB783F", +"p@ c #B34D25", +"q@ c #76371C", +"r@ c #68321B", +"s@ c #6C361B", +"t@ c #723B1B", +"u@ c #783C1A", +"v@ c #7E3C1A", +"w@ c #7F3E1A", +"x@ c #853E19", +"y@ c #854019", +"z@ c #894219", +"A@ c #843E18", +"B@ c #823D18", +"C@ c #803D18", +"D@ c #7C3818", +"E@ c #823C17", +"F@ c #823E18", +"G@ c #6A381C", +"H@ c #64341C", +"I@ c #6F3519", +"J@ c #793919", +"K@ c #853F18", +"L@ c #8C4419", +"M@ c #8C4217", +"N@ c #8A4017", +"O@ c #853C17", +"P@ c #813A17", +"Q@ c #7D3C19", +"R@ c #763718", +"S@ c #743819", +"T@ c #793D1B", +"U@ c #69341C", +"V@ c #6B371E", +"W@ c #733B1D", +"X@ c #743B1B", +"Y@ c #8D4523", +"Z@ c #E66B34", +"`@ c #FBAD62", +" # c #FFEE9F", +".# c #FFF9B2", +"+# c #FFFEBF", +"@# c #FEFECB", +"## c #FEFED1", +"$# c #FFFFD8", +"%# c #FFFFDB", +"&# c #FFFFD9", +"*# c #FFFFD4", +"=# c #FDFECB", +"-# c #FFFFC3", +";# c #FFFCB7", +"># c #FFF8AA", +",# c #FBD483", +"'# c #F37F40", +")# c #B44E26", +"!# c #6C3519", +"~# c #6F3619", +"{# c #763919", +"]# c #773819", +"^# c #7E3C19", +"/# c #7E3B19", +"(# c #77391A", +"_# c #6F381D", +":# c #6B361B", +"<# c #69361A", +"[# c #66341B", +"}# c #64351C", +"|# c #65331A", +"1# c #5F341C", +"2# c #58321D", +"3# c #592E1B", +"4# c #5D311B", +"5# c #703A1B", +"6# c #80421B", +"7# c #82401A", +"8# c #8B4017", +"9# c #873E17", +"0# c #833E18", +"a# c #7D3A18", +"b# c #773718", +"c# c #753719", +"d# c #753819", +"e# c #BB5729", +"f# c #FF8643", +"g# c #FFE185", +"h# c #FEFEAC", +"i# c #FFFFB3", +"j# c #FFFEBB", +"k# c #FFFFC5", +"l# c #FFFFCD", +"m# c #FFFFDA", +"n# c #FFFFD6", +"o# c #FFFFCF", +"p# c #FFFFC6", +"q# c #FFFEBE", +"r# c #FFFEB4", +"s# c #FFFFAB", +"t# c #FFFFA2", +"u# c #FFB261", +"v# c #DD6531", +"w# c #7A3A1E", +"x# c #60301B", +"y# c #61321B", +"z# c #66341E", +"A# c #5F321D", +"B# c #62331D", +"C# c #65341C", +"D# c #6C3518", +"E# c #6D361C", +"F# c #6C351D", +"G# c #6D3319", +"H# c #70391B", +"I# c #83421C", +"J# c #833F1A", +"K# c #7C3B1B", +"L# c #763819", +"M# c #68361C", +"N# c #5C321C", +"O# c #592D1A", +"P# c #5D2F1A", +"Q# c #63311A", +"R# c #64351B", +"S# c #6A341B", +"T# c #71361A", +"U# c #763A1A", +"V# c #7B3A19", +"W# c #7A3919", +"X# c #7E3A17", +"Y# c #8D421C", +"Z# c #D9622E", +"`# c #FF994D", +" $ c #FFF58F", +".$ c #FEFEA3", +"+$ c #FFFEAC", +"@$ c #FFFFB7", +"#$ c #FFFFC0", +"$$ c #FFFFCA", +"%$ c #FFFFD1", +"&$ c #FFFFDC", +"*$ c #FFFFDE", +"=$ c #FFFFDD", +"-$ c #FFFFD3", +";$ c #FFFFCB", +">$ c #FFFFC2", +",$ c #FFFFB8", +"'$ c #FFFEAE", +")$ c #FEFEA2", +"!$ c #FEFE99", +"~$ c #FFC56A", +"{$ c #F37439", +"]$ c #994622", +"^$ c #6A351C", +"/$ c #6E361C", +"($ c #72381A", +"_$ c #70371B", +":$ c #6F361C", +"<$ c #703619", +"[$ c #6E381E", +"}$ c #6B391E", +"|$ c #6E361B", +"1$ c #733819", +"2$ c #743618", +"3$ c #78381A", +"4$ c #793819", +"5$ c #7F3D18", +"6$ c #7F3C18", +"7$ c #864019", +"8$ c #853F19", +"9$ c #803D1A", +"0$ c #7D3F1C", +"a$ c #713B1C", +"b$ c #6C381C", +"c$ c #60321C", +"d$ c #5C2E1A", +"e$ c #5C2F1A", +"f$ c #613019", +"g$ c #6E3519", +"h$ c #813E19", +"i$ c #8F421A", +"j$ c #E66A32", +"k$ c #FF9148", +"l$ c #FEED83", +"m$ c #FEFE9A", +"n$ c #FFFEA5", +"o$ c #FFFFB1", +"p$ c #FFFFBC", +"q$ c #FFFFD7", +"r$ c #FFFFE1", +"s$ c #FFFFE3", +"t$ c #FFFFD0", +"u$ c #FFFFC8", +"v$ c #FFFFBD", +"w$ c #FFFFB2", +"x$ c #FFFEA6", +"y$ c #FFFF9B", +"z$ c #FEFE90", +"A$ c #FEB65D", +"B$ c #F97237", +"C$ c #B44F25", +"D$ c #7E3718", +"E$ c #753519", +"F$ c #7B3C1B", +"G$ c #783418", +"H$ c #773418", +"I$ c #773A1A", +"J$ c #6F3A1C", +"K$ c #6C3A1D", +"L$ c #6E381B", +"M$ c #6D3419", +"N$ c #66351C", +"O$ c #5F351E", +"P$ c #512F1E", +"Q$ c #4B2A1B", +"R$ c #4F2B1C", +"S$ c #532D1C", +"T$ c #5E321D", +"U$ c #E76E34", +"V$ c #FF803E", +"W$ c #FFC968", +"X$ c #FEFE91", +"Y$ c #FFFF9E", +"Z$ c #FFFFAA", +"`$ c #FFFFB6", +" % c #FFFFCE", +".% c #FFFFE4", +"+% c #FFFFE7", +"@% c #FFFFE8", +"#% c #FFFFE6", +"$% c #FFFFDF", +"%% c #FFFFC4", +"&% c #FFFE9F", +"*% c #FFFF93", +"=% c #FEFE85", +"-% c #FF8340", +";% c #F77137", +">% c #AB5024", +",% c #813F1B", +"'% c #7F3C1A", +")% c #7B3B19", +"!% c #783A19", +"~% c #733919", +"{% c #6D351A", +"]% c #68371D", +"^% c #67361E", +"/% c #69361D", +"(% c #6E391D", +"_% c #81431B", +":% c #81441C", +"<% c #7B3F1B", +"[% c #703C1D", +"}% c #703C1C", +"|% c #6D361A", +"1% c #72371A", +"2% c #763B1B", +"3% c #7A3B1A", +"4% c #813D19", +"5% c #883F18", +"6% c #D4642F", +"7% c #FF7E3D", +"8% c #FE9246", +"9% c #FFEF7E", +"0% c #FFFF94", +"a% c #FFFFBE", +"b% c #FFFFE5", +"c% c #FFFFEB", +"d% c #FFFFEE", +"e% c #FFFFEF", +"f% c #FFFFCC", +"g% c #FFFFA4", +"h% c #FFFE95", +"i% c #FFFF8B", +"j% c #FFC05F", +"k% c #FE7438", +"l% c #E66A34", +"m% c #78391E", +"n% c #5D311C", +"o% c #5B321D", +"p% c #582D1A", +"q% c #542B1A", +"r% c #532B1B", +"s% c #502B1C", +"t% c #552D1C", +"u% c #572E1B", +"v% c #783E1C", +"w% c #7C3E1A", +"x% c #7E3B1B", +"y% c #79381B", +"z% c #7F3C1B", +"A% c #843C18", +"B% c #833D17", +"C% c #833C17", +"D% c #823A17", +"E% c #7F3D19", +"F% c #823E1A", +"G% c #803D19", +"H% c #853F1A", +"I% c #863E17", +"J% c #863D18", +"K% c #A74F23", +"L% c #FA793B", +"M% c #FE823E", +"N% c #FFA651", +"O% c #FFFE88", +"P% c #FFFF98", +"Q% c #FFFFA8", +"R% c #FFFFED", +"S% c #FFFFF2", +"T% c #FFFFF4", +"U% c #FFFFF5", +"V% c #FFFFF1", +"W% c #FFFFC7", +"X% c #FFFF9A", +"Y% c #FFFF8C", +"Z% c #FFDB6C", +"`% c #FD7D3C", +" & c #FE773A", +".& c #C4592B", +"+& c #74371A", +"@& c #753A1C", +"#& c #7A3E1C", +"$& c #7E3D1B", +"%& c #894119", +"&& c #914518", +"*& c #954417", +"=& c #8C461B", +"-& c #61351E", +";& c #63331C", +">& c #67351B", +",& c #763C1B", +"'& c #793E1B", +")& c #7D421B", +"!& c #5F311C", +"~& c #61331E", +"{& c #D06633", +"]& c #FF7F3D", +"^& c #FF8F45", +"/& c #FFB156", +"(& c #FFFF89", +"_& c #FFFFAE", +":& c #FFFFF9", +"<& c #FFFFFC", +"[& c #FFFFFD", +"}& c #FFFFFB", +"|& c #FFFFF8", +"1& c #FFFFF3", +"2& c #FFFFEC", +"3& c #FFFFB0", +"4& c #FFFF9F", +"5& c #FFFF8E", +"6& c #FFDC6C", +"7& c #FE8841", +"8& c #FF7D3D", +"9& c #FF7639", +"0& c #92421E", +"a& c #753517", +"b& c #723618", +"c& c #6B3318", +"d& c #663018", +"e& c #673018", +"f& c #652E19", +"g& c #632F19", +"h& c #633019", +"i& c #592F1A", +"j& c #5A311B", +"k& c #5D341D", +"l& c #5E351D", +"m& c #8F3F17", +"n& c #874018", +"o& c #803C19", +"p& c #823C19", +"q& c #833E19", +"r& c #863E18", +"s& c #8C4117", +"t& c #8B3F17", +"u& c #893E17", +"v& c #61301A", +"w& c #894323", +"x& c #FE8842", +"y& c #FF9A4A", +"z& c #FFBF5C", +"A& c #FFFB89", +"B& c #FFFFA3", +"C& c #FFFFB5", +"D& c #FFFFE0", +"E& c #FFFFEA", +"F& c #FFFFFA", +"G& c #FFFFFE", +"H& c #FFFFFF", +"I& c #FFFFD2", +"J& c #FFFFA5", +"K& c #FFFF91", +"L& c #FFD969", +"M& c #FF9949", +"N& c #FE8440", +"O& c #FF7A3B", +"P& c #DE6431", +"Q& c #7D3718", +"R& c #7D3918", +"S& c #853B17", +"T& c #843B17", +"U& c #843E19", +"V& c #833F19", +"W& c #873F18", +"X& c #873E16", +"Y& c #8A3E17", +"Z& c #6F3A1B", +"`& c #783A1A", +" * c #783B1B", +".* c #7E3B18", +"+* c #813C17", +"@* c #7E3917", +"#* c #7D3B1A", +"$* c #7A3A1A", +"%* c #CF6430", +"&* c #FE813E", +"** c #FE9045", +"=* c #FFA750", +"-* c #FFD164", +";* c #FFFA8B", +">* c #FFFF95", +",* c #FFDD6C", +"'* c #FFAB53", +")* c #FE7C3B", +"!* c #7D3B1E", +"~* c #68341B", +"{* c #65331B", +"]* c #65361C", +"^* c #67361D", +"/* c #68381D", +"(* c #693A1E", +"_* c #6A391E", +":* c #6A3921", +"<* c #6D391D", +"[* c #68351E", +"}* c #68351C", +"|* c #69381C", +"1* c #723619", +"2* c #773818", +"3* c #833D19", +"4* c #FB7B3B", +"5* c #FF8741", +"6* c #FF994A", +"7* c #FFB858", +"8* c #FFE770", +"9* c #FFFF92", +"0* c #FFFFAD", +"a* c #FFFFE9", +"b* c #FFFFBF", +"c* c #FFF277", +"d* c #FF8440", +"e* c #FF793B", +"f* c #B7552A", +"g* c #562C1A", +"h* c #562E1B", +"i* c #5C311C", +"j* c #5A311C", +"k* c #592F1B", +"l* c #58301C", +"m* c #5E341D", +"n* c #65361D", +"o* c #673821", +"p* c #6E371A", +"q* c #6F361A", +"r* c #70351B", +"s* c #6E3419", +"t* c #9A4B23", +"u* c #FF7F3E", +"v* c #FE8B42", +"w* c #FFA24E", +"x* c #FFC960", +"y* c #FFF87D", +"z* c #FFFF9D", +"A* c #FFFEB1", +"B* c #FFFFF7", +"C* c #FFFFA0", +"D* c #FFFE84", +"E* c #FFD366", +"F* c #FFA44F", +"G* c #FF8C43", +"H* c #E76B33", +"I* c #6C371A", +"J* c #6B3419", +"K* c #693419", +"L* c #6A361A", +"M* c #6A351B", +"N* c #6A361C", +"O* c #6E3A1B", +"P* c #71351A", +"Q* c #6D361D", +"R* c #70351A", +"S* c #753B1A", +"T* c #BD5C2C", +"U* c #FF823F", +"V* c #FFAB52", +"W* c #FFD767", +"X* c #FFFF88", +"Y* c #FFFFA6", +"Z* c #FFFEA8", +"`* c #FFFF8F", +" = c #FFE56E", +".= c #FFAF54", +"+= c #FF9146", +"@= c #FE7C3C", +"#= c #F27237", +"$= c #76381D", +"%= c #65311B", +"&= c #6A361D", +"*= c #753A1B", +"== c #6E3A1D", +"-= c #70391C", +";= c #6F371C", +">= c #6C371D", +",= c #6D361E", +"'= c #783F1D", +")= c #D46833", +"!= c #FF9548", +"~= c #FFB356", +"{= c #FFE46F", +"]= c #FFFEAB", +"^= c #FFFFBB", +"/= c #FFFFD5", +"(= c #FFFFB9", +"_= c #FFFF97", +":= c #FFF075", +"<= c #FFB959", +"[= c #FF9648", +"}= c #F67438", +"|= c #934623", +"1= c #67331C", +"2= c #6F391C", +"3= c #6D3A1E", +"4= c #75391A", +"5= c #853D17", +"6= c #7D3D1B", +"7= c #723E1D", +"8= c #6F3B1D", +"9= c #703B1C", +"0= c #70381B", +"a= c #66361C", +"b= c #E57037", +"c= c #FE8640", +"d= c #FFB859", +"e= c #FFEC73", +"f= c #FFFFAF", +"g= c #FFF57A", +"h= c #FF833F", +"i= c #F77438", +"j= c #A74B21", +"k= c #75391B", +"l= c #70381A", +"m= c #7C401C", +"n= c #7F421B", +"o= c #874119", +"p= c #6A371B", +"q= c #6C391D", +"r= c #793D1A", +"s= c #783D1C", +"t= c #743B1D", +"u= c #6B371C", +"v= c #ED7439", +"w= c #FFBB5A", +"x= c #FFFF96", +"y= c #FFFFF0", +"z= c #FFF67C", +"A= c #FFC35E", +"B= c #FF9B4B", +"C= c #F87539", +"D= c #9D4C26", +"E= c #66361D", +"F= c #713618", +"G= c #723A1C", +"H= c #713A1B", +"I= c #6B3B1D", +"J= c #63331F", +"K= c #63341F", +"L= c #673620", +"M= c #63331B", +"N= c #803A18", +"O= c #7F3919", +"P= c #7D3A19", +"Q= c #6D341A", +"R= c #E57238", +"S= c #FFBA5A", +"T= c #FFEF73", +"U= c #FFFF9C", +"V= c #FFF67B", +"W= c #FFC25D", +"X= c #FF9B4A", +"Y= c #F87639", +"Z= c #9A4827", +"`= c #6B3521", +" - c #62321F", +".- c #833E1A", +"+- c #CF6834", +"@- c #FFB758", +"#- c #FFE871", +"$- c #FFFF99", +"%- c #FFF276", +"&- c #FFBC5A", +"*- c #FF9849", +"=- c #F57538", +"-- c #8E4523", +";- c #BB5D2D", +">- c #FFB154", +",- c #FFDE6B", +"'- c #FFFFA9", +")- c #FFFFF6", +"!- c #FFEB72", +"~- c #FFB557", +"{- c #FE7F3C", +"]- c #F17438", +"^- c #7F3F1F", +"/- c #9D4C25", +"(- c #FE8340", +"_- c #FFA851", +":- c #FFFD82", +"<- c #FFDD6A", +"[- c #FF9046", +"}- c #ED7036", +"|- c #6F3A1E", +"1- c #FF813E", +"2- c #FF8D44", +"3- c #FFA04D", +"4- c #FFF176", +"5- c #FFFEB0", +"6- c #FFFA7D", +"7- c #FFA24D", +"8- c #FF8A43", +"9- c #FE7C3D", +"0- c #CA5F2D", +"a- c #83401A", +"b- c #7E3E1B", +"c- c #733B1C", +"d- c #803C1A", +"e- c #DA6C33", +"f- c #FE8741", +"g- c #FE9848", +"h- c #FFB255", +"i- c #FFFE87", +"j- c #FFFEB6", +"k- c #FFFEB5", +"l- c #FFFFA7", +"m- c #FFFF8D", +"n- c #FFB657", +"o- c #FF9749", +"p- c #FF7B3B", +"q- c #954923", +"r- c #86421B", +"s- c #763E1D", +"t- c #733D1D", +"u- c #6F3C1D", +"v- c #A55026", +"w- c #FF9145", +"x- c #FFA44E", +"y- c #FFC65F", +"z- c #FFF177", +"A- c #FFFFBA", +"B- c #FFFEAD", +"C- c #FFF87C", +"D- c #FFCB62", +"E- c #FFA54F", +"F- c #FE8D44", +"G- c #E96F35", +"H- c #773B1C", +"I- c #67331B", +"J- c #7A3D1E", +"K- c #E37438", +"L- c #FFB054", +"M- c #FFD666", +"N- c #FFFA7F", +"O- c #FFFEAF", +"P- c #FFFD84", +"Q- c #FFDC6A", +"R- c #FF9748", +"S- c #FE8540", +"T- c #FE7B3C", +"U- c #9A4924", +"V- c #72381C", +"W- c #68361D", +"X- c #61331B", +"Y- c #62321A", +"Z- c #67341B", +"`- c #B45728", +" ; c #FA823F", +".; c #FE8C42", +"+; c #FFA14D", +"@; c #FFDE6A", +"#; c #FFFE81", +"$; c #FFE56F", +"%; c #FF9F4C", +"&; c #D56631", +"*; c #6E391E", +"=; c #72371B", +"-; c #6D381A", +";; c #5D301B", +">; c #59301C", +",; c #59311D", +"'; c #512B1B", +"); c #512A1B", +"!; c #5D2F1B", +"~; c #62311A", +"{; c #D86E35", +"]; c #FE9144", +"^; c #FFDF6B", +"/; c #FFFC7F", +"(; c #FFFFC1", +"_; c #FFE76F", +":; c #FFC05C", +"<; c #FFA34E", +"[; c #F57639", +"}; c #90431E", +"|; c #69351C", +"1; c #683820", +"2; c #65371E", +"3; c #69381E", +"4; c #65371D", +"5; c #62321B", +"6; c #5F341D", +"7; c #61341D", +"8; c #63311B", +"9; c #66331B", +"0; c #6C3319", +"a; c #813F1D", +"b; c #EB773A", +"c; c #FFB958", +"d; c #FFF378", +"e; c #FFFE8A", +"f; c #FFFEB3", +"g; c #FF843F", +"h; c #B15427", +"i; c #773A19", +"j; c #733618", +"k; c #77381A", +"l; c #76391B", +"m; c #6D371A", +"n; c #6B3A1E", +"o; c #853E18", +"p; c #823F1A", +"q; c #823F19", +"r; c #823A18", +"s; c #7D3818", +"t; c #7C3718", +"u; c #78371A", +"v; c #7E3818", +"w; c #8D431F", +"x; c #EF783A", +"y; c #FFC85F", +"z; c #FFE16C", +"A; c #FFF47A", +"B; c #FFFE86", +"C; c #FFF77C", +"D; c #FFCC61", +"E; c #FFB256", +"F; c #FE9044", +"G; c #FE833F", +"H; c #AC532A", +"I; c #8A4218", +"J; c #863C17", +"K; c #813C19", +"L; c #873E18", +"M; c #8E3E16", +"N; c #8E3E17", +"O; c #57331E", +"P; c #522E1C", +"Q; c #532C1B", +"R; c #5D2D1A", +"S; c #5D2C19", +"T; c #5F2D19", +"U; c #612F19", +"V; c #642F19", +"W; c #6A3318", +"X; c #6E3318", +"Y; c #6F3419", +"Z; c #84411B", +"`; c #8E451E", +" > c #EA7738", +".> c #FF8D43", +"+> c #FFB556", +"@> c #FFC75F", +"#> c #FFE66F", +"$> c #FFF679", +"%> c #FFF67A", +"&> c #FFE870", +"*> c #FFDA69", +"=> c #FFB757", +"-> c #FF9A49", +";> c #FE7D3C", +">> c #984B29", +",> c #7A401B", +"'> c #743D1B", +")> c #753D1C", +"!> c #6E381A", +"~> c #6E3A1C", +"{> c #62331C", +"]> c #5B351E", +"^> c #944316", +"/> c #884219", +"(> c #7A3E1D", +"_> c #753E1D", +":> c #723A1E", +"<> c #713C1E", +"[> c #6C391E", +"}> c #75361A", +"|> c #75381B", +"1> c #63301A", +"2> c #67331D", +"3> c #D76B36", +"4> c #FC833F", +"5> c #FF8842", +"6> c #FD9A49", +"7> c #FFAD53", +"8> c #FFBE5A", +"9> c #FFC55E", +"0> c #FFC35D", +"a> c #FFBE5B", +"b> c #FF8641", +"c> c #FF813F", +"d> c #F4773A", +"e> c #A04D24", +"f> c #88421A", +"g> c #823E1B", +"h> c #7F3D1B", +"i> c #803B1A", +"j> c #844019", +"k> c #7B3C1C", +"l> c #813D1C", +"m> c #813D1B", +"n> c #8A441A", +"o> c #984717", +"p> c #63321B", +"q> c #5F2F1D", +"r> c #592C1B", +"s> c #572C1B", +"t> c #4D2A1B", +"u> c #4D291B", +"v> c #4F291A", +"w> c #53291A", +"x> c #532A1A", +"y> c #57301C", +"z> c #5A301B", +"A> c #68331A", +"B> c #BD582A", +"C> c #ED7539", +"D> c #FF8A42", +"E> c #FF8E44", +"F> c #FE9346", +"G> c #FF9C4B", +"H> c #FF9E4B", +"I> c #FF9E4C", +"J> c #FF9C4A", +"K> c #FD9246", +"L> c #F97A3A", +"M> c #DA632F", +"N> c #9F451D", +"O> c #863D16", +"P> c #713619", +"Q> c #6C381B", +"R> c #6B3A1C", +"S> c #6E3B1D", +"T> c #763D1B", +"U> c #82441C", +"V> c #7E411B", +"W> c #793B19", +"X> c #7E3E1A", +"Y> c #67381E", +"Z> c #63371E", +"`> c #64381E", +" , c #65361E", +"., c #63351D", +"+, c #66351A", +"@, c #703719", +"#, c #7F3F1B", +"$, c #78391A", +"%, c #763618", +"&, c #6C3217", +"*, c #6E2F17", +"=, c #7A2C15", +"-, c #A3361B", +";, c #D14E26", +">, c #F26C34", +",, c #FF8943", +"', c #FF8B43", +"), c #FF8942", +"!, c #FA7539", +"~, c #E4592A", +"{, c #C1441F", +"], c #993918", +"^, c #873E19", +"/, c #4E2B1B", +"(, c #502A1B", +"_, c #532E1D", +":, c #5C331D", +"<, c #6D3A1D", +"[, c #703519", +"}, c #6A3A1F", +"|, c #743519", +"1, c #783519", +"2, c #773719", +"3, c #763419", +"4, c #763519", +"5, c #7A3618", +"6, c #7F3918", +"7, c #7A3719", +"8, c #79381A", +"9, c #863817", +"0, c #963715", +"a, c #A73414", +"b, c #BC3416", +"c, c #D03617", +"d, c #DE3B19", +"e, c #ED4F25", +"f, c #F5642F", +"g, c #FB7337", +"h, c #FE793A", +"i, c #FF7D3C", +"j, c #FC7538", +"k, c #F86932", +"l, c #F15629", +"m, c #E6421D", +"n, c #D73517", +"o, c #C73314", +"p, c #B63514", +"q, c #A33815", +"r, c #933B16", +"s, c #833B18", +"t, c #703418", +"u, c #5B2E1A", +"v, c #5A2E1A", +"w, c #6B361D", +"x, c #813F1A", +"y, c #843F1A", +"z, c #7E3D1A", +"A, c #773B1D", +"B, c #6C371F", +"C, c #6E381F", +"D, c #6F351C", +"E, c #703419", +"F, c #713419", +"G, c #703319", +"H, c #6B351E", +"I, c #6A341A", +"J, c #753119", +"K, c #812F18", +"L, c #952F17", +"M, c #A82C16", +"N, c #B72D16", +"O, c #C12D15", +"P, c #C82E15", +"Q, c #CD2D16", +"R, c #D02D14", +"S, c #D12D14", +"T, c #D12E14", +"U, c #CF2D14", +"V, c #CB2D14", +"W, c #C42E14", +"X, c #B82D14", +"Y, c #AA2E14", +"Z, c #9E3315", +"`, c #923715", +" ' c #853716", +".' c #763818", +"+' c #6C361A", +"@' c #5D321D", +"#' c #65351D", +"$' c #703B1E", +"%' c #7A401D", +"&' c #7F421D", +"*' c #80411C", +"=' c #773D1C", +"-' c #73371C", +";' c #63321D", +">' c #61321C", +",' c #61331D", +"'' c #61331C", +")' c #61321D", +"!' c #60311A", +"~' c #572D1A", +"{' c #6A2E1A", +"]' c #742D19", +"^' c #802A17", +"/' c #892A17", +"(' c #942A17", +"_' c #9D2C17", +":' c #A62D17", +"<' c #A92F19", +"[' c #AD2F19", +"}' c #AE2F16", +"|' c #AC2D15", +"1' c #A52C17", +"2' c #A02D16", +"3' c #9A2F17", +"4' c #8F2D16", +"5' c #882F17", +"6' c #833216", +"7' c #7A3417", +"8' c #753518", +"9' c #753718", +"0' c #7C3A18", +"a' c #863F18", +"b' c #883F17", +"c' c #893F18", +"d' c #813C18", +"e' c #763918", +"f' c #64301A", +"g' c #5D331D", +"h' c #5C341D", +"i' c #6F3618", +"j' c #803B18", +"k' c #803C18", +"l' c #7D3C18", +"m' c #753919", +"n' c #733718", +"o' c #6C3318", +"p' c #6D3318", +"q' c #703219", +"r' c #773318", +"s' c #7C3118", +"t' c #813119", +"u' c #873219", +"v' c #8B3317", +"w' c #893218", +"x' c #8C3218", +"y' c #8D3117", +"z' c #8C3017", +"A' c #893117", +"B' c #883117", +"C' c #843318", +"D' c #7F3119", +"E' c #7B3418", +"F' c #79391A", +"G' c #773918", +"H' c #7B391A", +"I' c #8A3E16", +"J' c #8D4117", +"K' c #8D4218", +"L' c #68371C", +"M' c #813D18", +"N' c #803E19", +"O' c #723B1C", +"P' c #67321B", +"Q' c #72351A", +"R' c #6E351B", +"S' c #70341C", +"T' c #78361A", +"U' c #7D3719", +"V' c #7A351A", +"W' c #793419", +"X' c #753218", +"Y' c #793318", +"Z' c #783319", +"`' c #743419", +" ) c #70391D", +".) c #79361A", +"+) c #7A371A", +"@) c #803917", +"#) c #883D17", +"$) c #7D3B19", +"%) c #72391C", +"&) c #6F391D", +"*) c #7A381A", +"=) c #72391D", +"-) c #71371D", +";) c #77371A", +">) c #69371C", +",) c #67371E", +"') c #5C301C", +")) c #61301C", +"!) c #69321B", +"~) c #6D341B", +"{) c #67321E", +"]) c #60321D", +"^) c #66331A", +"/) c #64331A", +"() c #74371B", +"_) c #7C391A", +":) c #7B391B", +"<) c #74381C", +"[) c #7B3A1A", +"}) c #66341D", +"|) c #62361D", +"1) c #5F311A", +"2) c #60311B", +"3) c #68341D", +"4) c #60331F", +"5) c #60331E", +"6) c #5B2E1C", +"7) c #562D1C", +"8) c #582F1E", +"9) c #582F1D", +"0) c #542E1C", +"a) c #59301D", +"b) c #60311C", +"c) c #5F321C", +"d) c #5F331D", +"e) c #6A381D", +"f) c #65321D", +"g) c #65341D", +"h) c #6C3219", +"i) c #6B3119", +"j) c #693019", +"k) c #64321C", +"l) c #62311D", +"m) c #64351F", +"n) c #6B3721", +"o) c #693921", +"p) c #62341F", +"q) c #5A2D1B", +"r) c #592D1B", +"s) c #592F1C", +"t) c #5F2E1A", +"u) c #69391D", +"v) c #6D3621", +"w) c #70351E", +"x) c #6E3620", +"y) c #66331E", +"z) c #61321E", +"A) c #5B311D", +"B) c #5D311D", +"C) c #6A351F", +"D) c #6B3620", +"E) c #6B361F", +"F) c #6E3621", +"G) c #69371E", +". + @ # $ % % & * = - ; > = , ' ) ! ~ { ] ^ / ( _ : < < [ } | ^ / ( _ : < < [ 1 2 3 @ 4 5 6 7 8 1 9 0 a b c d e f g h i ", +"j k l m n n o p q r s t u v w x y z A B C D E F G H I J K L L M N M O P Q R S T ! U V W X Y Z ` ...+.@.#.$.%.&.*.=.-.;.", +"= >.,.'.).; !.~.{.].^.' /./.(._.:.<.[.}.|.1.2.3.4.5.6.7.8.9.0.a.K b.c.d.e.f.g.h.i.j.T _.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.", +"A.A.B.C.D.E.F.G.H.I.J.K.L.M.M.1 N.O.P.Q.R.S.T.e.1 M.U.V.A f.W.X.Y.X.Z./.`. +F..+++@+#+$+%+&+*+> =+-+;+>+,+,+'+)+!+~+{+]+", +"^+/+^.+.(+_+(+:+#+<+[+T }+Z.|+1+2+3+4+5+6+7+8+N.9+0+a+b+c+d+e+f+g+h+i+b.Q.j+k+l+m+d q n+g+o+p+q+r+s+t+u+v+w+x+y+! z+A+B+", +"+ C+D+E+F+G+H+I+J+K+L+M+e.N+O+P+M Q+^+R+S+T+U+V+o+W+X+Y+Z+`+ @.@+@@@#@$@%@&@*@=@J.-@p ;@>@,@'@)@T+)@!@~@{@]@^@/@' (@_@:@", +"<@<@[@}@|@1@2@3@4@1 D 5@6@b.7@8@9@U+o+J.0@)@a@b@c@d@e@f@g@h@i@j@k@l@m@n@o@p@q@r@D.R ] &@s@t@u@v@w@x@y@z@A@B@C@D@E@F@}.k+", +"n.G@H@I@J@K@L@M@N@O@P@Q@R@S@T@l U@V@W@X@,.Y@Z@`@ #.#+#@###$#%#&#*#=#-#;#>#,#'#)#!#~#{#]#^#/#(#_#_#_#:#<#[#' ' D.3.1+[#}#", +"R.T |#1#2#3#4#5#6#7#8#9#A@0#a#b#c#,@-@d#e#f#g#h#i#j#k#l#*#&#%#m#n#o#p#q#r#s#t#u#v#w#x#y#z#M+, G+A#B#C#D#E#F#M.;@g G#3.H#", +"I#J#K#L#{@M#N#O#P#Q#R#S#T#U#V#W#g+X#Y#Z#`# $.$+$@$#$$$%$$#&$*$=$&#-$;$>$,$'$)$!$~${$]$^$/$U+($`._$:$D.<$[$}$Q+|$1$2$3$4$", +"C@5$6$7$8$9$0$P.a$b$c$d$e$e$f$g$h$i$j$k$l$m$n$o$p$p#o#q$=$r$s$r$*$$#t$u$v$w$x$y$z$A$B$C$D$E$;@c#b F$W#G$H$a b#U#I$(#c 1@", +"M.s@J$K$T.] L$M$N$O$P$Q$R$S$T$:#2+U$V$W$X$Y$Z$`$-# %q$*$.%+%@%#%.%$%n#o#%%,$s#&%*%=%-%;%>%,%'%)%!%~%{%e.]%^%* M+K+/%* (%", +"_%:%<%[%}%J$A |%($~@1%2%3%4%8$5%6%7%8%9%0%t#o$a%;$n#*$b%c%d%e%e%c%b%$%n#f%#$o$g%h%i%j%k%l%m%n%o%p%q%r%s%R$t%u%x D+y+v%w%", +"x%y%z%A%B%C%D%E%F%G%H%K@I%J%9$K%L%M%N%O%P%Q%,$p#-$*$#%R%S%T%U%T%V%R%#%=$-$W%,$Z$X%Y%Z%`% &.&+&y.>@N.7+T+_$@&#&$&%&&&*&=&", +"-&; F+;&>&n.0@]@,&'&)&2%:#!&~&{&]&^&/&(&Y$_&#$l#m#.%d%T%:&<&[&}&|&1&2&.%m# %#$3&4&5&6&7&8&9&0&a&b&c&d&e&f&g&h&i&j&k&l&p.", +"m&n&O@o&p&q&F%r&s&t&u&o&I@v&w&8&x&y&z&A&B&C&p#-$D&E&1&F&G&G&H&G&G&:&S%E&$%I&W%@$J&K&L&M&N&O&P&Q&R&3%l+S&T&U&V&V&J#W&X&Y&", +"L$Z&P+V+`& *d#]#.*+*@*#*I$$*%*&***=*-*;*Q%p$;$&#.%e%:&H&H&G&H&G&H&G&|&d%s$$#;$p$Z$>*,*'*^&)* &!*+ /@o.~*{*]*u.^*/*(*_*C ", +":*<*p+[*}*|*[#/.b&p 1*d#2*3*4*5*6*7*8*9*0*#$o#=$a*T%<&H&G&H&H&H&G&H&<&S%+%&$ %b*_&P%c*z&y&d*e*f*O#g*h*x i*j*k*l*j*m*n*o*", +"i+p*q*p*~@{@r*0+X@W@N+++s*t*u*v*w*x*y*z*A*>$I&$%2&|&H&H&H&H&H&H&H&H&G&B*E&=$t$>$3&C*D*E*F*G*O&H*k.I*w+J*K*o.L*M*N*A O*|*", +"R 4@/.-.P*q*Q*6@R*/$] {@S*T*U***V*W*X*Y*@$p#-$D&d%F&H&G&H&H&H&H&H&G&G&|&2&*$%$%%C&Z*`* =.=+=@=#=$=%=}*1+u+{%y+..&=U+*=R ", +"L.==..-=;=>=|+U.,=$+X.'=_ )=d*!=~={=`*]=^=;$$#s$e%}&H&H&H&H&H&H&H&H&G&:&R%D&/=u$(=s#_=:=<=[=V$}=|=s@I+1=..2=E#3===b.c 4=", +"5=6=~@..p+|%==7=8=9=L.0=a=b=c=6*d=e=0%f=a% %m#+%S%<&H&H&H&H&H&H&H&H&G&F&d%s$q$;$p$_&y$g=z&M&h=i=j=J@q k=8+* U.l=X@m=n=o=", +"p=:#N*2=' q=1.*@r=s=t=>=u=v=5*y&w=:=x=o$#$t$&$@%T%[&H&H&H&H&H&H&H&H&G&}&y=b%&#f%a%f=z*z=A=B=-%C=D=E=F=1*;=G=>&<*H=_$S.I=", +"F%J=K=L=M=n*8=*=N=O=P=o+Q=R=5*y&S=T=>*o$#$o#=$a*T%[&H&H&H&H&H&H&H&H&G&<&V%b%&#l#a%f=U=V=W=X=-%Y=Z=`= -J=K=L=M=n*8=*=N=.-", +"| ^ / ( _ : < < [ 1 2 3 @ +-5*M&@-#-K&'$a% %%#+%1&<&H&G&H&H&H&H&H&G&G&F&e%s$q$;$p$_&$-%-&-*-U*=---{ ] ^ / ( _ : < < [ } ", +"L M N M O P Q R S T ! U V ;-c=!=>-,-i%'-^=$$$#.%d%:&G&H&G&H&H&H&G&H&G&)-c%D&/=u$(=s#*%!-~-!={-]-^-B C D E F G H I J K L ", +"0.a.K b.c.d.e.f.g.h.i.j.T /-(-**_--*:-t#`$k#-$$%E&1&F&H&H&H&G&H&H&G&:&y=#%&$%$-#C&x$(&<-V*[-8&}-[.}.|.1.2.3.4.5.6.7.8.9.", +"W.X.Y.X.Z./.`. +F..+++@+#+|-1-2-3-A=4-_=5-a%f%&#s$2&1&F&[&H&H&H&[&|&V%a*D&n#$$v$3&U=6-x*7-8-9-0-P.Q.R.S.T.e.1 M.U.V.A f.", +"a-b-Q.c-i+b.Q.j+k+l+m+d q d-e-f-g-h-<-i-J&j--#t$m#s$c%V%)-B*:&B*T%e%a*D&$# %>$k-l-m-8*n-o-h=p-q-4+5+6+7+8+N.9+0+a+b+r&r-", +"s-t-u-Z.1+N.M.&@*@=@J.-@p ;@v-h=w-x-y-z-0%0*A-k#%$&#D&b%E&R%R%2&a*b%*$q$ %%%,$B-$-C-D-E-F-u*G-Q+^+R+S+T+U+V+U+9+X.q*|.J ", +"H-O+m.G=] E.N*}*I-r@D.R ] &@J-K-5*6*L-M-N-X%O-A-%%l#/=m#*$D&r$D&=$&#-$f%-#(='$Y$P-Q-~=R-S-T-U-U+o+J.0@)@a@V-4@F.U.U.a@|.", +"==W-C#%+X-{*Y-(+M=Z-!#~#{#]#^#`- ;.;+;7*@;#;X%B-,$b*W%l#%$*#*#-$t$l#W%b*@$0*Y$i-$;&-%;v*7%&;t+V@W@X@,.*;R H#L$=;-;-=B p+", +"; ;;>;l*l*,;u%';);q%!;~;x#y#z#M+{;c=];x-&-^;/;>*l-w$(=a%>$%%k#%%(;a%,$A*'-$-P-_;:;<;**&*[;};c#,@-@p !#~*l.M.D.T |;1;2;Y ", +"3;4;5;6;7;}#' M=I+8;9;:#0;^$/$U+a;b;f-8%<;c;W*d;e;y$Y*_&f;C&`$C&w$'$l-z*m-z=@;&-<;];g;4*h;W#g+X#o&^#i;j;R@k;l;3+2=p*m;n;", +"r&o;B@p;q;D%r;s;n t;s;0 u;v;D$E$;@w;x;5*];3-h-y;z;A;B;K&P%U=Y$z*$-9*X*C;$;D;E;3-F;G;8&H;f$g$h$W&<@I;r&J%J;A%K;O@L;M;N;5=", +"O;P;Q;d$R;S;T;U;V;W;X;Y;n G%Z;,%'%)%`; >5*.>X==*+>@>W*#>:=$>C;%>%-&>*>x*=>=*->v*h=;>>>S$T$:#2+,>'>)>!>~>m.a=%+%={>[#}*]>", +"^>/>f+(>_>:><>[>s@,@}>3$|>($T 1>n%o%p%2>3>4>5>w-6><;7>n-8>W=9>0>a>@-7><;y&F;b>c>d>e>3%4%8$5%f>f+} g>h>i>G j>F%k>l>m>n>o>", +"c-p>q>r>s>t>u>v>w>x>t y>z>8;A>s*+&y.>@N.9@B>C>c=D>E>F>*-G>H>%;I>J>*-K>E>x&d*L>M>N>K@O>J%9$^#u@($P*g P>Q>R>S>S>T>U>V>W>X>", +"Y>Z>Z>`> , ,.,+,U.@,L#b `&#,#*$,%,a&b&&,*,=,-,;,>,d*,,',',G*',',',),x&d*!,~,{,],^,$*/.!&*+/,(,_,:,C#x+J$S.<,q=1 [,0;A },", +"l;R@R@b&P>|,%,1,$,2,3,4,5,6,v;7,8,Q&R&V#9,0,a,b,c,d,e,f,g,h,V$i,j,k,l,m,n,o,p,q,r,s,t,v&u,p%v,y w,S.2%x,y,7$y,z,V&X>h+A,", +"0+]#4,U.B,C,'@M*,=D,E,F,G,s+H,_@I,p.+ X 0;J,K,L,M,N,O,P,Q,R,S,S,T,U,V,W,X,Y,Z,`, 'k+I$`&l;.'+'H@G.!;{.@'#'$'H-%'&'*'='|$", +"S#-'(#^@U N*S+= ;'>','''= z#K=)'` !'O#g*~'t.{']'^'/'('_':'<'['}'|'1'2'3'4'5'6'7'8'9'2*0'0#a'b'[@c'd'e'f'v,g'h'+.S#p+1+V.", +"U ~*T Z-[#Z-<#i'F=($T+;.j'k'/#l'm'n'k.I*w+o'p'q'r's't'u'v'w'x'y'z'A'B'C'D'E'F'@&t+++s**=S@G'F$H'r;I'J'K'C@s=|;''M#L'M=~*", +"0#B@D@M'B@a'L@U&o&N'3%h O'~*{*] V-'@P'%=}*1+^@s*Q'R'S'T'U'V'V'W'X'Y'Z'`'h.=@F,/$] {@S*b.b.L.3+ )4@[,+&.)+)@)D%#)t&Y&$)}.", +"8;}#}#Z-I*4@4@R.%)-=&)*=*)i =)-);)c 7.s@I+1=..2=E#3===b.c 4=L.==..-=;=>=|+U.,=$+X.'=_ >),)/+2>, '.i*')))n*x y !)~){)]){*", +"!)S#E.^)/)/){*u+p+-=:>_.()_):)i <)[)l+J@q k=8+* U.l=X@m=n=o=5=6=~@..p+|%==7=8=9=L.0=a=N$1 ;=~*})|)c$4#1)2)!'(+N$3)s /%!)", +"4)5)6),+7)7),+,+8)9)9)0)a)>'b)c)d)2>e)E=F=1*;=G=>&<*H=_$S.I=p=:#N*2=' q=1.*@r=s=t=>=u=3.s@L.L.($F.f)g)~)h)i)j)k)k)l)@+m)", +"n)o)p)d)q)r)s)t)M#u)u)..T E.M.F#v)w)x)`= -J=K=L=M=n*8=*=N=.-F%J=K=L=M=n*8=*=N=O=P=o+Q=3.3.M*U@y)z)A)B))''.7;{+C)D)E)F)G)"}; diff --git a/app/examples/Basic/Collection/.directory b/app/examples/Basic/Collection/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Basic/Collection/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Basic/Collection/.icon.png b/app/examples/Basic/Collection/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..52e66249c734d935df42d897a0ea6ab79841d9c4 GIT binary patch literal 4310 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 Ef@}{+iU0rr literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Collection/.lang/ca.mo b/app/examples/Basic/Collection/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..0e2a891f4626bf2fd6f00058d8cb749bec2d16f2 GIT binary patch literal 708 zcmZvZ&ubGw6vs!cRaQJ$R1iHp!JACnje=!kq!3exm>Oy-9=uF6SZekZjyf&(AEZ{EINv+w)b$~%E`9^3#g!3A&#!ubLs@D;3q zZ{QO60an0Ia1Q(em%-|(qgvOYF}MXT!oPD`h-=UcdKLN_8uH#kSD}Z{E714Q(C;G@ zsRFW?EySJyq37{A`^O0RqG!sIGhZ0JWD?I+RhKM!mX&8(3L1E>#FnaERHACe(?U<27+JHA(sGc?=WHEXq)U~e(Nnq22SxD9N9dALJ5CZA-<9o{ zlKW|L?{=KTar8tvFUMACS1NC8N{@}r3wov+Q__P;%LQYjQBz}D>>eUsPiAVua?Gkq z>F*upa4_tj%#yU?sAsfijrvWE0X)zBow`(o?oc*W*0JyIj5p=_NnDs?kFD(M%;b1x zO6$9Yk4ELps#0zmTe*~Uy|}t=yF*82SJTZdC7tMn92w3FFOSE>KT%$lN=IUwGxPZk x){zyTK8XID!wx8CvdbT6u@#zW^{-#_ORn%1v1O{ToSN-_w$n3?-H;F1iQg*SysH2J literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Collection/.lang/ca.po b/app/examples/Basic/Collection/.lang/ca.po new file mode 100644 index 00000000..721125f0 --- /dev/null +++ b/app/examples/Basic/Collection/.lang/ca.po @@ -0,0 +1,48 @@ +# Catalan translation of Collection +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the Collection package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Collection\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 17:14+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FStart.form:26 +msgid "by juergen@zdero.com" +msgstr "per juergen@zdero.com" + +#: FStart.form:10 +msgid "Collection" +msgstr "Recoŀlecció" + +#: .project:1 +msgid "Collection example" +msgstr "Exemple de recoŀlecció " + +#: FStart.form:16 +msgid "Create Collection" +msgstr "Crea una recoŀlecció" + +#: FStart.class:18 +msgid "Item" +msgstr "Element" + +#: FStart.class:27 +msgid "myThings" +msgstr "CosesMeves" + +#: FStart.class:31 +msgid "Name = " +msgstr "Nom = " + diff --git a/app/examples/Basic/Collection/.lang/cs.mo b/app/examples/Basic/Collection/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..6b77fd62225677c038a97c2948dfbea53183705c GIT binary patch literal 622 zcmYk1yKWOf6ov-~Az2DUL`RA#NQK5g7cDXJ%HD)k_9ow?Ae!;+VYBhFXVL)P!z_Ii#=kQa!(-?@MLo}oU$eGAoQrb|6SwNFNv<`vK;C8HTVIR#ySY@O$z zRU=assb>A-QWiBywK~DpEXdgqbd3=%O}+M^ci_{Z-|YoS?4$|n!nCxPtb2&>G?;ToYamou| zec+W#LoYrU*vP#wY>Bd=bg}Zd)QN(~U(NTX@!DxCX+UD!Y=ZL`I5V#%f|v3w&5pUQ z!88Z`eYU&u*|9!>X1>f+j$e1_?x-{lIaC718IW{wF~1xOp40Ko$^4QGXJ)Dv7t4c^ cq)Mp8_c15&{M$K9X_GKgsVNMvzGNl&2gE#@g#Z8m literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Collection/.lang/cs.po b/app/examples/Basic/Collection/.lang/cs.po new file mode 100644 index 00000000..fd96208e --- /dev/null +++ b/app/examples/Basic/Collection/.lang/cs.po @@ -0,0 +1,40 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Collection example" +msgstr "Příklad kolekcí" + +#: FStart.class:18 +msgid "Item" +msgstr "Položka" + +#: FStart.class:27 +msgid "myThings" +msgstr "moveVěci" + +#: FStart.class:31 +msgid "Name = " +msgstr "Název =" + +#: FStart.form:10 +msgid "Collection" +msgstr "Kolekce" + +#: FStart.form:16 +msgid "Create Collection" +msgstr "Vytvoř kolekci" + +#: FStart.form:26 +msgid "by juergen@zdero.com" +msgstr "-" diff --git a/app/examples/Basic/Collection/.lang/de.mo b/app/examples/Basic/Collection/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..2db6422cc9990c7a7149fd3f5785e88f5d33570c GIT binary patch literal 619 zcmYk3O>fjN5QYtuf&v#R4oGmA1Be6i0=Fu(Xu~Eg5&2l{MV|&Ik9{|+;7xEC(NQ!S$9d)rb2;Mol{=InO8N9Q3Pv)` zWf;)w+)v{t$9WcciR!GJ7^S;f+1B`UFijH5f;6JNC=KGIAB5p38joKhRF@a3WI18g z_>=_M;nbDfOOk$F^;%P|V|?<`IE~i*J?}m92CcTNQEs_FKb9};(?zW+{ep_QG9BCf z>EuA}uH#%=XKZAoi*|;m`?PynSx-puimPr>aZP7mKAX)kt|v+ujb(g-79nzxBt^rO KW*Vzvj@>^-u$2w~ literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Collection/.lang/de.po b/app/examples/Basic/Collection/.lang/de.po new file mode 100644 index 00000000..5cfb1a51 --- /dev/null +++ b/app/examples/Basic/Collection/.lang/de.po @@ -0,0 +1,41 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Collection example" +msgstr "Beispiel für Collection" + +#: FStart.class:18 +msgid "Item" +msgstr "Ding" + +#: FStart.class:27 +msgid "myThings" +msgstr "meine Sachen" + +#: FStart.class:31 +msgid "Name = " +msgstr "-" + +#: FStart.form:10 +msgid "Collection" +msgstr "-" + +#: FStart.form:16 +msgid "Create Collection" +msgstr "Collection erstellen" + +#: FStart.form:26 +msgid "by juergen@zdero.com" +msgstr "-" + diff --git a/app/examples/Basic/Collection/.lang/es.mo b/app/examples/Basic/Collection/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..e574f1ba41055e8a3e08beba3632f11a3d5650a9 GIT binary patch literal 615 zcmZvYyKWOf6ov-~w}k>CA{vV6(HQ8WC5b$;C&`Mv7+)emH(nn$YwgZh<8hSm475B3 z6%Ewk9q8z3D5?1e1=$5h`t%>ooSXh1+gl$Q;x>2!#^4_K0785L5%>zW!8h;_dhLN0*bwtKN)AOzsZ1tmSN8iuH5whD)Kn{7kr+DE}hD$DxvvtepZ@X_)a@pAndTy>id()r6z<*KnPx9!xyTeh(7Ki>WU)g+t$ literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Collection/.lang/es.po b/app/examples/Basic/Collection/.lang/es.po new file mode 100644 index 00000000..f6daebb5 --- /dev/null +++ b/app/examples/Basic/Collection/.lang/es.po @@ -0,0 +1,41 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FStart.form:26 +msgid "by juergen@zdero.com" +msgstr "por juergen@zdero.com" + +#: FStart.form:10 +msgid "Collection" +msgstr "Colección" + +#: .project:1 +msgid "Collection example" +msgstr "Ejemplo de colección" + +#: FStart.form:16 +msgid "Create Collection" +msgstr "Crear colección" + +#: FStart.class:18 +msgid "Item" +msgstr "Artículo" + +#: FStart.class:27 +msgid "myThings" +msgstr "" + +#: FStart.class:31 +msgid "Name = " +msgstr "Nombre =" + diff --git a/app/examples/Basic/Collection/.lang/nl.mo b/app/examples/Basic/Collection/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..202a9cae0b7ffe027806e44a264a4f35280dc64e GIT binary patch literal 666 zcmaKoyKWOf6ov-~0hWq@(m-(vIy7cCAQUz+vSpUY!f`C)1R>Fky(jApvpcKVv5E5* z6m+}+k3j=HH7(CTgZKw>qsfsz{c|}t{XbTgJ_^Kja1Xo%H^C?1<0}ZjH?RV}gInMy zSOUMmHSinU0n3;E^|}jP1^2-Xyl-3<;x;sa`uaCeU-u5W46UK7&?Bhdtjz0S2>&``0fY4g3mv{Ih4HdTX_b<=v9 zS3I+(;)b!=Gc+i(naWs>SQRn#+WoCvzsP2<*V6g_IWYy>-R!=Y19aB0kvBaXn zmW~hI!%C~X+@Q%+*_vH*cl1K8pXK>w_u0y%oR}1b#I!!nU2tkM+&%W5X>N=iGwakl a{^c`Unw\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Collection example" +msgstr "Collectie voorbeeld" + +#: FStart.class:18 +msgid "Item" +msgstr "Element" + +#: FStart.class:27 +msgid "myThings" +msgstr "myThings" + +#: FStart.class:31 +msgid "Name = " +msgstr "Naam =" + +#: FStart.form:10 +msgid "Collection" +msgstr "Collectie" + +#: FStart.form:16 +msgid "Create Collection" +msgstr "Creëer collectie" + +#: FStart.form:26 +msgid "by juergen@zdero.com" +msgstr "door juergen@zdero.com" + diff --git a/app/examples/Basic/Collection/.project b/app/examples/Basic/Collection/.project new file mode 100644 index 00000000..3f5d5a0b --- /dev/null +++ b/app/examples/Basic/Collection/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +# Compiled with Gambas +Title=Collection example +Startup=FStart +Icon=collection.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Basic/Collection/.src/CThing.class b/app/examples/Basic/Collection/.src/CThing.class new file mode 100644 index 00000000..4b526262 --- /dev/null +++ b/app/examples/Basic/Collection/.src/CThing.class @@ -0,0 +1,6 @@ +' Gambas class file + +Public Name As String +Public X As Integer +Public Y As Integer +Public ID As Integer diff --git a/app/examples/Basic/Collection/.src/FStart.class b/app/examples/Basic/Collection/.src/FStart.class new file mode 100644 index 00000000..926810ba --- /dev/null +++ b/app/examples/Basic/Collection/.src/FStart.class @@ -0,0 +1,40 @@ +' Gambas class file + +Static Public Sub Main() + Dim myForm As Form + myForm = New FStart + myForm.Show +End + +Public myThings As Collection +Public myThing As CThing + +Public Sub btnCreate_Click() +'creating objects and add to collection + Dim i As Integer + myThings = New Collection + For i = 1 To 7 + myThing = New CThing + myThing.Name = ("Item") & " " & CStr(i) + myThing.X = i + myThing.Y = i + myThing.ID = i + myThings.Add(myThing, CStr(myThing.ID)) + Next + +'show the collectionmembers in treeview + + tvThings.Add("root", ("myThings")) + + For Each myThing In myThings + tvThings.Add(myThing.ID, myThing.Name,, "root") + tvThings.Add(myThing.ID & "Name", ("Name = ") & myThing.Name,, CStr(myThing.ID)) + tvThings.Add(myThing.ID & "X", "X= " & myThing.X,, CStr(myThing.ID)) + tvThings.Add(myThing.ID & "Y", "Y= " & myThing.Y,, CStr(myThing.ID)) + tvThings.Add(myThing.ID & "ID", "ID= " & myThing.ID,, CStr(myThing.ID)) + Next + + tvThings["root"].Expanded = True + btnCreate.Enabled = False +End + diff --git a/app/examples/Basic/Collection/.src/FStart.form b/app/examples/Basic/Collection/.src/FStart.form new file mode 100644 index 00000000..715d33f5 --- /dev/null +++ b/app/examples/Basic/Collection/.src/FStart.form @@ -0,0 +1,20 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(33,14,61,40) + Text = ("Collection") + Icon = Picture["collection.png"] + Resizable = False + { btnCreate Button + MoveScaled(37,1,23,4) + Text = ("Create Collection") + } + { tvThings TreeView + MoveScaled(1,1,35,38) + Sorted = True + } + { Label1 Label + MoveScaled(37,35,24,4) + Text = ("by juergen@zdero.com") + } +} diff --git a/app/examples/Basic/Collection/collection.png b/app/examples/Basic/Collection/collection.png new file mode 100644 index 0000000000000000000000000000000000000000..ce524b157eba9740ff2e1f3fb51baedfe0b85c59 GIT binary patch literal 2062 zcmV+p2=VucP)w^CZR_|!#}AHeu`^X` zr>(Wc4^BI^?esB@_6-AC6>)&ESkV*<5~%~2z)eEl_kGX%oZS!S8Ys2~OWQSb=6tyO zto7S#{r6h?!ll@zzjY+OlKW}wj(vXwsxHabN3y$yXT|S5`RH9cvY8h%QhxW0l=08( zd>&$n|83w;|D_`Gxj)~t{#hgS)QBga_odunNx9pX@{tiK8}HnI5SrUQic4{E%N|nH zyYJYrysPCK>v}3WJK7a&9~|eLlV}u9=iu3Iz4gw}lbbfespJ10fRztimi+AZe%#aT ztXZ-!gbmmZNcq8~kqBf=c=-o?ss10{xf!$f^_kqOGcC~CuHD>q|GIlJH7bL7FfGts z&>bi_P%J+s2XZF3KuvdJZBD!S*3#SW9K-HE^`ZV}%z*Tj6Mz~5F$sqZh=-w4fsiGm7H(}zh|x!$9R1ban}C7;9KiRG9t^55CkZXXaApd8<$?lw!KiaF5U0MQT}Fu=AvSx(L+lmNg6 ztN>(OfZYh~E{vRos!EtQ8yXrw3B!;Hx&iO{pt%xv2G=tJ2p3zP2LO--L;}{-!*B?U z5S+0bA2AQ0M#FbwGs0n~CiN&le(FBD+#9EY!d`Z;=AF2f77DS&Y-gF2fKb#;W+u9LyZ$ z-)P~V`oPdy5?}9Dzm{0@@t><(wyk2tZQ7!yF#f3=e;u#Ij%$G)JP7$v4P-6A=bTu* zz^lwb&CrmR-2VEFInAo)HFIxy1dcdypj-2E|9s|Qz}jg%I!Pk)(U3+DhH zTtxTUo9FGWTfgy(;p=X#BbtCh28wAg3ZNIjEO1qFnAX8Djt;kwomGMA2J<&-GM#^M z+N0(002`9MeBsMynAcTAm6a?~=QDoo$g;7TQkTD1-vdnRLhYFo-n!=APi>*11#&si z^I+tIz(ziRDCVJ5WLbTR%f?b1O|?^OswSdB&IWZ15=Vx(K6-!+cl5G&-2}x*iAk$K z+RGqa!92B{LRWUd%idPDOgi~^W_E~Mz4px#H zNd+B72#`J~f{N-2IvQKKBGE}}MKixC{sCMR%?B_XDA~aW5J1#a!__??Lr_@*`2rLQ zL1`uipishd6@0tg&-^fF#1PGm&CInsNR6Jw*95UxC5;VnIvQGO4kxL0V;sr#F=0#s ziqxc!g6)852F@XIP4t7GXs5SdEG6ME(Ue?+#raQ5EdXaFqr|h+%TmFz8Ana65myj zHsGot-*2iaS(!%0T{KTcH!MtJ0tFRCC`>q$q-B<<6~+OyU!3uuZGUcH?Y31-p9m{n zpm-_EZPkX7ab9W4S0GJ*?qm4n-m0r0&%2#Sjf`3#ZROED6IG~zx}<~UnGjaX)73`> zN(Uy~J>yeG+p3nT%PVlfa;6=TDS#&fFy9AHk{WY({EghyQ}3mppGXQ>(-vl{k$26v))T5MoaPQnqpVgDsG?w8G>{n z2<%JJd7qygE9v(gDg0??$$PkH@PxfD`Y245tb^--*wA-CcEF;?L3Ojz6-DA z-jFT#zdERwmL_UeC!-;C&&h)P>PhFV?~gij-hzf{3!|V9|fsgq^?%X0~YL z`l@dxw=Q@f8m(nCKaQI#V859;?(EZ_CeQDH{)_Mf1lPMdF5=J21*U2%!Vh*lzQn4y swdM#39tJwg{{C+k0#*Z;&$NMm0Pgn9CQ=SiV*mgE07*qoM6N<$g298?_y7O^ literal 0 HcmV?d00001 diff --git a/app/examples/Basic/DragNDrop/.directory b/app/examples/Basic/DragNDrop/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Basic/DragNDrop/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Basic/DragNDrop/.icon.png b/app/examples/Basic/DragNDrop/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7b64ae2517d510933119ae063f03a1a0e736e9f6 GIT binary patch literal 4159 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 Drag.Image Then Stop Event + +End + +Public Sub TreeView1_DragMove() + + 'IF Drag.Type <> Drag.Image THEN STOP EVENT + + With TreeView1 + If Not .FindAt(Drag.X, Drag.Y) Then + Drag.Show(TreeView1, .Item.X, .Item.Y, .Item.W, .Item.H) + Else + Drag.Show(TreeView1) + Endif + End With + +End + +Public Sub TreeView1_Drop() + + Dim sKey As String + + With TreeView1 + + If Not .FindAt(Drag.X, Drag.Y) Then + sKey = .Item.Key + Endif + + Inc $iKey + + If Drag.Type = Drag.Image Then + .Add($iKey, "#" & $iKey, Drag.Data.Picture, sKey).EnsureVisible + ' ELSE IF Drag.Type = Drag.Text THEN + ' .Add($iKey, Drag.Data,, sKey).EnsureVisible + Endif + + End With + +End + +Public Sub TreeView1_MouseDrag() + + Dim hImage As Image + + If Not Mouse.Left Then Return + + With TreeView1 + If .FindAt(Mouse.X, Mouse.Y) Then Return + If Not .Key Then Return + + hImage = New Image(32 + 8 + .Font.TextWidth(.Current.Text), 32, Color.Transparent) + Paint.Begin(hImage) + Try Paint.DrawImage(.Current.Picture.Image, 0, 0) + 'Try Draw.Picture(.Current.Picture, 0, 0) + Paint.Font = .Font + Paint.Text(.Current.Text, 34, 0, hImage.Width, 32, Align.Left) + Paint.Fill + Paint.End + + Drag.Icon = hImage.Picture + 'hImage.Save("~/drag.png") + 'Drag.Icon = .Current.Picture + .Drag(.Key, MIME_TYPE) + End With + +End + +Public Sub imgHole_Drag() + + 'DEBUG Drag.Type;; Drag.Format + If Drag.Type = Drag.Text Then + If Drag.Format = MIME_TYPE Then + Return + Endif + Endif + + Stop Event + +End + +Public Sub imgHole_Drop() + + TreeView1.Remove(Drag.Data) + +End + +Public Sub Form_Open() + + Me.Center + TreeView1.Add("Test", "Test", Picture["drop.png"]) + +End + + +Public Sub imgHole_DragMove() + + 'DEBUG Drag.Type;; Drag.Format + If Drag.Type = Drag.Text Then + If Drag.Format = MIME_TYPE Then + Drag.Show(imgHole) + Return + Endif + Endif + + Stop Event + +End + + +Public Sub Test_DragMove() + + Drag.Show(Last) + 'PRINT LAST.ScreenX;; LAST.ScreenY;; LAST.Window.ScreenX;; LAST.Window.ScreenY + +End + +Public Sub Form_DragMove() + + Test_DragMove + +End diff --git a/app/examples/Basic/DragNDrop/.src/FDragNDrop.form b/app/examples/Basic/DragNDrop/.src/FDragNDrop.form new file mode 100644 index 00000000..caed31f0 --- /dev/null +++ b/app/examples/Basic/DragNDrop/.src/FDragNDrop.form @@ -0,0 +1,107 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(56,27,44,56) + Text = ("Drag & Drop") + Icon = Picture["drop.png"] + Arrangement = Arrange.Horizontal + Spacing = True + Margin = True + { VBox2 VBox Test + Name = "VBox2" + MoveScaled(1,3,10,42) + Visible = False + Drop = True + Spacing = True + { Button1 Button Test + Name = "Button1" + MoveScaled(0,2,10,4) + Drop = True + Text = ("Button1") + } + { Panel2 Panel Test + Name = "Panel2" + MoveScaled(2,9,6,8) + Drop = True + Border = Border.Etched + } + { ScrollView1 ScrollView Test + Name = "ScrollView1" + MoveScaled(2,20,7,19) + Drop = True + { ScrollView2 ScrollView Test + Name = "ScrollView2" + MoveScaled(1,5,4,7) + Background = &HF7FFDF& + Drop = True + } + } + } + { TreeView1 TreeView + MoveScaled(14,2,20,41) + Drop = True + Expand = True + } + { VBox1 VBox + MoveScaled(36,2,6,44) + Spacing = True + { PictureBox1 PictureBox imgIcon + Name = "PictureBox1" + MoveScaled(0,0,6,6) + Tag = "Bicycle" + Picture = Picture["icon:/32/flag"] + Alignment = Align.Center + Border = Border.Raised + } + { PictureBox2 PictureBox imgIcon + Name = "PictureBox2" + MoveScaled(0,6,6,6) + Tag = "Eye" + Picture = Picture["icon:/32/lamp"] + Alignment = Align.Center + Border = Border.Raised + } + { PictureBox3 PictureBox imgIcon + Name = "PictureBox3" + MoveScaled(0,12,6,6) + Tag = "Map" + Picture = Picture["icon:/32/internet"] + Alignment = Align.Center + Border = Border.Raised + } + { PictureBox4 PictureBox imgIcon + Name = "PictureBox4" + MoveScaled(0,18,6,6) + Tag = "Happy" + Picture = Picture["icon:/32/bookmark"] + Alignment = Align.Center + Border = Border.Raised + } + { PictureBox5 PictureBox imgIcon + Name = "PictureBox5" + MoveScaled(0,24,6,6) + Tag = "Magnify" + Picture = Picture["icon:/32/phone"] + Alignment = Align.Center + Border = Border.Raised + } + { PictureBox6 PictureBox imgIcon + Name = "PictureBox6" + MoveScaled(0,30,6,6) + Tag = "Highlight" + Picture = Picture["icon:/32/book"] + Alignment = Align.Center + Border = Border.Raised + } + { Panel1 Panel + MoveScaled(1,36,4,3) + Expand = True + } + { imgHole PictureBox + MoveScaled(0,38,6,6) + Drop = True + Picture = Picture["icon:/48/trash"] + Stretch = True + } + } +} diff --git a/app/examples/Basic/DragNDrop/drop.png b/app/examples/Basic/DragNDrop/drop.png new file mode 100644 index 0000000000000000000000000000000000000000..d286286f1313350b52ac0e13470a3c73429e6e81 GIT binary patch literal 1503 zcmah|2~1OG9B-=ytpYM-9)pD$j}Bp=qC+KG83dVttl$QCtf(l_;Uu!@B2GbM8y=hD z)B!48OL2&TAm~Jn77Dgt3*~70+P;>yuk>!;>p@{q_J~_z%#!`{`~K&b@1Oti{gjj# z>qS^bz+f<5aXX^Ps0^Grb7rADnqJ(6!Qh~z_>>sbY-?+5XlM}Le%|nB&GhtiFOM@c z-2b$>sk)XnHs&x}P5lEr#sgYq6&10=_vqyv?6&-(e0mj?+1bwQXhmOmO>8#%U-ci! zVzJ)xylEB|T|iAVBg(N@?3*D}=ka)Nd0v|t85vboRsR|KU#CPS2BacDA`(l4Vn8Sq z4*?RfbIjpz*lczLvB5B6vA|}t#b`1c3?{v6+$92r?M|CYts0pjg#v{_E=M5;K@^}& z2FgU@;lbg4p=d}Asgx?EQZ=Gfj*QFzy)RcNK)C`G17Z-A^80&-g@abaqEabA1t^gM zeSLgL18EJTgTn%g)ud1=WT3SBWmnC^2Y2t43kLf@xomJ~U|_JnPrx@>4Du1V-Z(m{ zgE&3i{63!1tXGVHMzc<580~$<FLSgb{kE43v5DcR&=;_hppts^=?iVTBONr^7r=-2ngtU*=dC>YPCv@ z5~PL^I5swx-Nk|+NTY!e1j)(CArgrlolMwf)r@LnGMU|OFD)%SeE6`Bj}NP(U8B*U zEk;L-ZU~J=^Y->`XSTwK6|o`cC@mI?+wEqu+H4NQ;k1vroUZXPv)SC**3#Pcq6I~3 zGrPOfX0y3muBoZ1$;nBh(O6MYL8VfwtE(p_CiHr})oS(f@)8IHFbqqjQWe@?r4k*F zL?QtI;MB{ei)g{Sa>iOvNPcccWsWKiT^J{DofKYnOA;zmy&qy*(wO z=OBebIYjb^pMU%Ot8IaSVPQmKL`3183nfdFw-dieJso{0B{%*n|5K4Zp^SpUPP+QW z;1m1h-Wp?h-QKP9@!wq=$6t(Lul%Dj6Y~3YayKqGi%@;+=%wQ1^N0@m#^H4(p&+W} zKB06EVb4x7dFKF^yB+|xY;G>>kJlDs^@$-N_|jj*0l>hT<;9GO+HH8^mJdQgS|Vx9 z&DlyiCyT&{Pe>>Wqvl49hgV#m)U99jDmpprSWut+iCyr-E`}i4Ne!>;X9k6tQV}fj zB_-W$Jkft39B->)(6N4WI-L=m_AFP7_^n|l`PHPiFjgHkJ^%5dLHI*<>f;0K`b<*P zUL2Kja9-csGBTNb{nE#^JN7qait8Wttf-{>5}L9@i9z=hfX63=k7Oj%nf5F;VQJc? z`vonZ#gTOTb~-nhw_#FFEV%3)Qt~Ag)2Ojt3uq*bchP?9zB&~Y@_jkpYL<>vI+I=hfP@#e6UR3v_dC_Vz!Cl}<|3_D z|DpE1CAfUwq-MB&ap=vKXLUws%`=?GhD{6SCa({qgq@o*iW@R}D03JXY%usD2FC|8 zh;@1AJm%)LhcmeoswzIGY~$HYPT27D>y1s{24xY90iOqAV^<$p9FAZ0sOY%a{60mY u)9Ik>g1Z;hd3n3@N%+}$XQ#r{9319*N2F&`%xclh;>N`!Mn8(86#NbK&oQY0 literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Object/.directory b/app/examples/Basic/Object/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Basic/Object/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Basic/Object/.icon.png b/app/examples/Basic/Object/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..842f3355e0b50d39b8df71129ce8ee4d26c480f4 GIT binary patch literal 4189 zcmV-j5TfsiP) zsS82rN4BB0KncO8Z!6)e>nb>PCeF$)^xH|c7+97!m@gp~{Pc;x=6 zG_||j{EtVeIoSs?cx4GN^|P;I3}i+E{Ozq1`RiMYscM67y#^go@cgsqo@s2S=EzAV zW(%JAr+HMCM)>;11~&gG3MxoMgm6{p(gk>EQ^$HE#6y-cB^UVoiI~IQH06K((Yp(( zE{6ED^*cdGq=0*t7x1MIl+tAsPrL>7-6^{dpsO>+(YH?F#rt^hLuK6e-eSJ{e=+{* zn{_084^#+*KR<);tD%Z9bj}0vd!s z7=h9OjFH&I@562S!^I1rq%QF_7mX#8$l85Mym&qlX8yx2kTyoc?6NRl`(O$4r$l)5 zG`x8LoFQ-A_o4b|E60yCFt04k;}6ZpSRp?DBRJRyT0?qT0E7bL0}4w^@CCwv%AR*= z0%!xCZ!TT|U}2fbdb3Wv`1MWgtG_iugxL@5$7n--y3K=k6ti|oHgC4U#zPSEQiKL@ zx}leSRSkrt&$mB5nPoF`czhc?zjyd~flv^PKwx2v!Scaq1KLIe^Z)GgiqU@j#^yFN ztLV}M7!dGytKd>x^iDu)9LMMR$L`C&XXLSSJ3wE$002|+$9DZVBXF!)-Z9#N(FO2R z@pm;r7)I?Fj4l8Me~b?JOT)QR+S>(q_+goJHQn*QkN_Sh#2nyZBY9X1BQ{Kcl~1FSuJq>L*mBj-EuYJnR0UCOwnl!_^(MjUvzAN3u= z2xlv$W0p)gVAugE#;*m?zDMne*XXW2%)-^5Mf)C7y-V{ja<~Fg%D81qm9R!QK#Y?B zTKnwU@ib=oOs37ep61#r+D;!~{tX{}clev(0@zX~EM>{Kk|P8dhdzMTKKplVLd=|l zTYZ3TnL}SeCTSss5^Bu#Ukc2K?5-?7ZdpRb)Z2o7?A_qTlY8mSO-FbRrv4Ol<(lc0 z*I?e*gH!@1)`OK6LCVW1#&DjNP%%p>nHcE+%-GsN&q;8ms}{Sw9E`!f={CAgALqJU zn^TjQp@ic2o)?(1U^S7f!pp~B3>QEtKVeBF<7z~5FqbI6{;eB1b9@iJ0dMhAvNE#K zu0uFCpTvp19Nt|=LHRZ8{N;B!*>;qQ#kU~k^pJDc#WL?kDKrQ~I8az}RXloWBx zQZla8@FV&nQ$VbzjURsh5gsa;$g}rjh;7w&%& zi8^+OfMs3QPS;4cSxUw&DTt5dE%>okIQk2=e)224xM&fxy1Pjil7(|BS$Nwz_H=kC zIUcv?!uEhSBl zl0v}Q#{XP7;B@sKdTf`NPI9EFfjedtpbJWQW^W_w4>s|?Pd&-r-@Je*pT!S$*Kx8t zffQ$xk1+=4Og(c-=P;pk`sI7|qdPz-X?iSW$rdRPVo2}DjFJOhuX>sCj2^lJc|_Cl z$rF-!Q;XQ&oZNT4#253*ejzqZPq69Dop)A>A*~+vkDTlTYLXSumTVDPNci*~*UmaC+#(lJK z2nmBk6L&qd=b@%A;d>81h+RAxF?9jK;tF&kj`R9&$(XSSfgz*$G&ihSj~qkb3*)n_ z`5P=-kV9nZT+oN;souo)ebd=j@~^2?qH_vx*6GF|Y;>z7wbd*wEu~;!5>V(N&$sKP z&D?v_RQ4r;v>Tgf{XsJ4UQe{AlRn2IH=K#3Bti%#UUw@`{p=B(z0Yw{SmXy4-@5)j z8XSkB)+Sbc;B#Yp?Z7p4|IEq{ErQ$t+WZOWZzpSB9c!|Vk$5$m<7Hnzp92O3&;}_z ztJyL^(X0ewpouUBG4O&;RaY^o%;L2ZjT|6>9m&M&X~*yD#D>D=S+ke2tKCp`rXJf(r}K@iOjvynJD0tmYqxKv+=+7($(>8rGNF9tn8^cV z^&KQ_QV@}R4n%uu3CmTG2tv{(E8QjNoWYGuI)?+yU_ut)Y*=L{EC5T2xR4^$?*M!u zNG4;1+*Y)nLVH#zqT>+B)<)dU02!4_=sj_ejQk0_bi9Sk#8Iq_9J-IZi5&`)F=;Nr zoC$np!A;n~3)`r0AxK0flW1-tV{#BkEsEk<;MkC~>Fwyl6y+e#F{h`b7_@+dIP|Xz zC`nvMLIAoy1Oy&u8f$0`q!I74$*5e6*V94Iu{SZ=qp+l$NM9$tc7Tpvhy)aqLJpRK zto(`0zxI9OMtlHnWFp58m5?>Hkw97^70tc`&%){RI2>ICX<6so`zIx0AitYo0w|Ff zw1!3~DNuHRjKUJ^%uM=rzfRJLA%q}UTtQL118G?_yBhz*KJ>IjWKFKbM1q98K8lsc zk%K#!GjI90i*O=;J=2bUgS1>jC_Oc^6z_=9b<*Wb!6#AY8CJ5IN)b~s z)lRB@l>$;>!V@T+R>_ow%gCI+oXiC`&{g#cG9#A^ZJ3l_0uw7pn@~d1acJo1Y@6n|3+mN(b>=ho`WUf=B2mt>Zv2lv!E%Qg=bP-O(hJ* z80x#?WKS%o>exO!p_n{nE>ews;$vFf6O^yI1!Fx!Aa@J7D_n3La1|mUnC&%k?7;VE zp82)WLqMej=#PQ3At04V3{06CV+Naw`SX_YM?BUopTln&9jbTlz)qV@-CH~8=xpJn z?j~N}gvu>K+BO4qC@H9FXk+2bMLhr9vs~+TP+PT|JKp~>GV_WriM%jA^8&v^DqyCeZy`;EYJM0PYd^S1k(uSjDsoi(Y)jt7U703_{{ z02*{h62Up@5NF+O$y5mR=KvWW7z_FmG1@#`c3vSXZeBy_;l2E#zMeoqDLul_(%Qr= zt3C)b<}l^Z9{zOnEm~9>p|a^148hq;=}kn*k&@*zW;3y37PZv}P?ox=`M^_Czl=N- zr82RZ^gKmc1VE?u^28HFI~-b@6o(2vL)ZK-k6QOI2M7^|{;c)u!}?!5_%uM$^)nq; zpL2ECbse63_6ZVmmV?nKA@TNY=M!u1C2v9rz1C=;#%Qm&A`u_1M1v6L<6?$3)-r;|II&j`z-lT zzXON~J*b!z=UE;O1cKbT>JFaz`9|#7*WvYaQ4k2?x(-4JGPCozdBs|;S+ty*n#1hb z_d0d0%{W>!F|UB~o7N!4vivM8l7-ihEWGCJhyMJxc&c;z=*p z^Yw*owZ6h&DVYoftqy>UfcegCGvs3yfHpcBcNNAM5li?=8_iX>Lq>oUMj0W65W+-k z9fkfR*xk{2!n3mjv7{#x2}c=YxEd7@LMR^-3e zW~r-B001S7>-2S;_}GnQD}dHP0f03?B@hMzynRE(l@1?pfhcek_}zd2070Mxm<|*J zIY5Xj>~D4FfhINn?*y8F<3IzD95Mz(^^*>ga)9JQT9zvVre6f7zr+ELQP#n)7Qz=I z$oLU1`^Xl8*|{v3md($;IF;6Jhev)|$M5&H6KmK_+bfUaMVsF-Hn-pZ9S&9ZUQi6h<5O>}%`b6ot>{g05>_DiM9IUhWx91w!yrYed)@C~PJW0>N zpS@$m2QaC$j8n}mSb=PU>4j*wi-H`35RewI&Pg7|(ee{++eo-*GSX7S8~1YN4;#?Q z-glh*0G8jk39YgS1VWIgX5JKs2_=)*@ONv@d)>J0S8IOZQlBWY~y0z4esp#00000NkvXXu0mjfBgq!J literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Object/.lang/ca.mo b/app/examples/Basic/Object/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..165b168ffff941c0209e291eba9567a4aa0db458 GIT binary patch literal 724 zcmZ{hv2N5r5QYsTAejOYKth6IA_@xF<9v}&Ra z*8Bu_z%Ote`~hoV^+FKbfZm2a{72jO?&7@9K<^>`0B!5OK(9f+LT^G(plF-D0e5Mu z@K(TOaA}!^Lz9LMYqlBdt&b^N(@9Jj>`x9k@wCuVYE{*R^vY=BsVu94gCytVJ^7qz zoGYEtdXQL_p3kLHcHWxiTyA7dJ6-Ii;tgAenK8{D3`hIoB_F9aC!S$eOucT@6;V$_ zkEyp2Z$u9ecf%Lbc`>$9J1k{vOwWx?6}^&;Ddfaj@yR25R`J|(%dj{V^Ect>oW*0p;auoXj{m=s?T z)8<6^a8%S*7GlTP(#52!S?C76KF!^Mru6|u{qU_A8BUcKi#21~mR=T8he5^mzxUx_ sIOX;hlrk-Dl&Iv#H%jlD*$LSNM{L!BN+cDF-275!, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Object\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-16 23:31+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Object manipulation example" +msgstr "Exemple de manipulacicó d'objectes" + +#: FStart.form:12 +msgid "Object " +msgstr "Objecte" + +#: FStart.form:17 +msgid "create the Thing !" +msgstr "crea l'objecte!" + +#: FStart.form:22 +msgid "check the Thing !" +msgstr "verifica l'objecte!" + +#: FStart.form:33 +msgid "destroy the Thing !" +msgstr "destrueix l'objecte!" + +#: FStart.form:38 +msgid " by juergen@zdero.com" +msgstr "per juergen@zdero.com" diff --git a/app/examples/Basic/Object/.lang/cs.mo b/app/examples/Basic/Object/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..d6d1b5191b2d67ea45a221cf2315833ad245a695 GIT binary patch literal 735 zcmZ9J%Wl&^6ov;{E(#KYB37_C5kf4G1MFH;P^`u+5q1*gQmKdq6MLFCab}|NIBN0& ztXQ&$;1wbsfkhh$c02(rc@6$4Edh@7`5Vui@j0IPzOnwEA+CbE;5E1gJ^~wGz-{mq zTme789q<#}18Wx;y9qylH{dP!7979~ya9H|FEVx+j$m770N;n-!gl`xz5#!RufyM9 zyU+H8;b~mL(|%(e*k0DapP=rP2D#8ZA%1H_llTj3D!Hpo(7u4=2vPXxB&PblhMc3ygWKJEL>R<&L$H0n-MOcJ~3R8Zcf1K(>pVcn^C^rG&D)rYU@HNOz> z%s6e$Wrjac+NIr25KzqvecJXzuNsuSN~P(yTF;RxdpYk3r!Dx%rNFE0b!^G)ASkCv zN$K7y$E8A84gFPrPfCvqJ4zZMQEoa$KSmsxN8^+y@+n1qt}|htb=tem<|@y&bttss z%SgpIIhQuOi77DW^z4U*HJ0<^#W3YDo!>$vNa22l%TKJK%yKTe`9R?G$9f`<7c`lg miCWGR^E(T-FdL#Pt\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Object manipulation example" +msgstr "Příklad manipulace objeků" + +#: FStart.class:29 +msgid "You need to create the Thing first!" +msgstr "Potřebujete první vytvořit objekt!" + +#: FStart.form:12 +msgid "Object " +msgstr "Objekt" + +#: FStart.form:18 +msgid "create the Thing !" +msgstr "vytvořit Věc !" + +#: FStart.form:23 +msgid "check the Thing !" +msgstr "zkontroluj Věc !" + +#: FStart.form:33 +msgid "destroy the Thing !" +msgstr "zničit Věc !" + +#: FStart.form:38 +msgid " by juergen@zdero.com" +msgstr "-" diff --git a/app/examples/Basic/Object/.lang/de.mo b/app/examples/Basic/Object/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..4c9a1200c3b18110dd154cd32b85d9cd9ef3b94a GIT binary patch literal 734 zcmZ9J%Wl&^6ow5g7X^t+5i3}n2niNw0_=(@D5h~sgdIn@R4QV@#6C%;b;j0sf*M|c zCt$}juw)fh*}#TJfF-ZN??6altZ%-V@v+YFobOwk?;VVb;1+lVE`blA9ACf<@D*GD zKfq1!6Wj(HXB_7md>7t=AHmn*0CvD@P%h=!-;Uub_6*;F-@%vRkMLFaGkgX92AAKJ zbz!+2=P;iKo1m;^1NC%GiU!iVbm)5>5k{in(eua+oA+KuEx!`T!it`e zy1*Y~#-+V}5KzktecJIuuNl<6Mx*0*yH63SM^ias(PKGrDezkR{W9fF5Y$su%gksU z<5DGThW@&~hqVWl-Ar57NH@z-kL444KToC7k0~BYQ?Px~@9l~0bzE82kd5$loF%w9 zm$nDWRvdxn8LJ{!oYHVHH&piI*oyE}FPyGonk?o;!I~0T{D~plm|aesrmU;WHXd8b X&0;=ez1o&h%YfBEy7*L}W7_!zy!pWG literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Object/.lang/de.po b/app/examples/Basic/Object/.lang/de.po new file mode 100644 index 00000000..337def3f --- /dev/null +++ b/app/examples/Basic/Object/.lang/de.po @@ -0,0 +1,41 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Object manipulation example" +msgstr "Beispiel für Objekt-Manipulation" + +#: FStart.class:29 +msgid "You need to create the Thing first!" +msgstr "Sie müssen das Objekt erst erstellen!" + +#: FStart.form:12 +msgid "Object " +msgstr "-" + +#: FStart.form:18 +msgid "create the Thing !" +msgstr "Objekt erstellen" + +#: FStart.form:23 +msgid "check the Thing !" +msgstr "Objekt prüfen" + +#: FStart.form:33 +msgid "destroy the Thing !" +msgstr "Objekt löschen" + +#: FStart.form:38 +msgid " by juergen@zdero.com" +msgstr "-" + diff --git a/app/examples/Basic/Object/.lang/es.mo b/app/examples/Basic/Object/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..0876e7f1a810b82979227ff93d65be3f6a4b29d5 GIT binary patch literal 686 zcmZ{hzityj5XKish;&p0fsjHmh!WO77aall0NYJg%!}*N8d)iy0n4XRn ziI!)effgQsl8%?)1^Bi|OpsXVr*HM`?96E9*Y4H_fwKc{ff2X@-UENWfa~BZ@G(EY z9{35ag5O{lY+n%K8uTVqfP29IlNW`!4DCSgK%YTxKsD6=dk6J#AEExMuYv2?*~V=P z_#BtcTwG|frum9($-4b^%GR_BvlRJc&V{2>T`FDGtA%ov(ZnZeSua==GcI0}n=y@M zl`iSFC@d?-=d39=&YJZ(uWyadF1u5C%(g+!kor;Qd32P}aWWWo^Ip);<$&K*{!zwA z8q$j-8pte{aYRq^FzeoZnfH<)RgIG)t8|0zj1B2R&aeU(L*9%qY zM^wy|ZP+~?j}GPjCeF9@imgm^VWt>SNc)q@1)?_gKQnhmBy+}i4NaL&$6i$5KWlFo lti_o|eEUQlv#hO|C`$`Pg=rM#?`^E$A1n4NSXF\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FStart.form:38 +msgid " by juergen@zdero.com" +msgstr "por juergen@zdero.com" + +#: FStart.form:22 +msgid "check the Thing !" +msgstr "¡ Comprobar la cosa !" + +#: FStart.form:17 +msgid "create the Thing !" +msgstr "¡ Crear la cosa !" + +#: FStart.form:33 +msgid "destroy the Thing !" +msgstr "¡ Destruir la cosa !" + +#: FStart.form:12 +msgid "Object " +msgstr "Objeto" + +#: .project:1 +msgid "Object manipulation example" +msgstr "Ejemplo de manipulación de objetos" + diff --git a/app/examples/Basic/Object/.lang/nl.mo b/app/examples/Basic/Object/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..54564e029b405565dac7b3740d1d68cddac5b2a4 GIT binary patch literal 789 zcmb7>&u-H|5XN0vC^9F?pB~^a5kef0gHt%9rl3S^r~+vdg|rBX0~>p4Z*g|5^(Jk2 z2~LO$Z_q1m&pFi#F%hAdfy$$^Yy$bybb?5wq zy8eYl0Cm2Q`W@%-y#SVg^MaCD&~QqJV>T(P#@mFAuEu)gb%%!>Tl%Xbp=2@6g_T+n zAB$0uv-eVuNiip6HN}R7WwII5Ad@Pk18GWI@#2i*H)qnw{99S^5-zRL)48&q(au58 zPJ(?lCGL%=*W7&8+-}i+tN*;+-SvCjpwAQOHiIo(AJJM^dk}={!P`!B z-w~w^21ck7f1pi7FJzuisV~@;v~lDXjf5wh>!QlS*sRQC!DFKfu4P4=ZW`}DUq;28C%-IsTE?pt%V^s fz|#}M3Il>yqjT)c>fF>$Pv$b1ijr()dZy|ZTENt5 literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Object/.lang/nl.po b/app/examples/Basic/Object/.lang/nl.po new file mode 100644 index 00000000..41b61e99 --- /dev/null +++ b/app/examples/Basic/Object/.lang/nl.po @@ -0,0 +1,41 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2014-09-23 01:18+0100\n" +"Last-Translator: Willy Raets \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Object manipulation example" +msgstr "Object manipulatie voorbeeld" + +#: FStart.class:29 +msgid "You need to create the Thing first!" +msgstr "Je dient het Ding! eerst te creëren!" + +#: FStart.form:12 +msgid "Object " +msgstr "Object " + +#: FStart.form:18 +msgid "create the Thing !" +msgstr "creëer het Ding!" + +#: FStart.form:23 +msgid "check the Thing !" +msgstr "controleer het Ding!" + +#: FStart.form:33 +msgid "destroy the Thing !" +msgstr "Vernietig het Ding!" + +#: FStart.form:38 +msgid " by juergen@zdero.com" +msgstr " door juergen@zdero.com" + diff --git a/app/examples/Basic/Object/.project b/app/examples/Basic/Object/.project new file mode 100644 index 00000000..9e18e993 --- /dev/null +++ b/app/examples/Basic/Object/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=Object manipulation example +Startup=FStart +Icon=object.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Basic/Object/.src/CThing.class b/app/examples/Basic/Object/.src/CThing.class new file mode 100644 index 00000000..4b526262 --- /dev/null +++ b/app/examples/Basic/Object/.src/CThing.class @@ -0,0 +1,6 @@ +' Gambas class file + +Public Name As String +Public X As Integer +Public Y As Integer +Public ID As Integer diff --git a/app/examples/Basic/Object/.src/FStart.class b/app/examples/Basic/Object/.src/FStart.class new file mode 100644 index 00000000..a12d9abc --- /dev/null +++ b/app/examples/Basic/Object/.src/FStart.class @@ -0,0 +1,44 @@ +' Gambas class file + +Public myThing As CThing + +Public Sub btnCreateThing_Click() + + myThing = New CThing + + With mything + .Name = "Dummy-Thing" + .X = 11 + .Y = 22 + .ID = 33 + End With + + txtCheckResult.Visible = True + +End + +Public Sub btnCheckThing_Click() + + If myThing Then + + With mything + txtCheckResult.Text = Subst("&1, X= &2, Y= &3, ID= &4", .Name, .X, .Y, .ID) + End With + + Else + Message.Warning(("You need to create the Thing first!")) + Endif + +End + +Public Sub btnDestroy_Click() + + If Not mything Then + Message.Warning(("You need to create the Thing first!")) + Return + Endif + + myThing = Null + txtCheckResult.Visible = False + +End diff --git a/app/examples/Basic/Object/.src/FStart.form b/app/examples/Basic/Object/.src/FStart.form new file mode 100644 index 00000000..c667a61a --- /dev/null +++ b/app/examples/Basic/Object/.src/FStart.form @@ -0,0 +1,28 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(35.7143,18.5714,67,17) + Text = ("Object ") + Icon = Picture["object.png"] + Resizable = False + { btnCreateThing Button + MoveScaled(1,1,21,4) + Text = ("create the Thing !") + } + { btnCheckThing Button + MoveScaled(23,1,21,4) + Text = ("check the Thing !") + } + { txtCheckResult TextBox + MoveScaled(1,7,65,4) + Visible = False + } + { btnDestroy Button + MoveScaled(45,1,21,4) + Text = ("destroy the Thing !") + } + { Label1 Label + MoveScaled(1,12,26,4) + Text = (" by juergen@zdero.com") + } +} diff --git a/app/examples/Basic/Object/object.png b/app/examples/Basic/Object/object.png new file mode 100644 index 0000000000000000000000000000000000000000..e2e3e67d7d8275e45eb1b5bc62151de5c2d8a16d GIT binary patch literal 1499 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, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Timer\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 23:21+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +#: FTimer.class:74 +msgid "Timer example" +msgstr "Exemple de temporitzador" + +#: FOtherTimer.class:38 +msgid "Please fill in valid values!" +msgstr "Introduïu valors vàlids!" + +#: FOtherTimer.class:63 +msgid "Time is over!" +msgstr "El temps ha acabat!" + +#: FOtherTimer.class:95 +msgid "Other Example" +msgstr "Un altre exemple" + +#: FOtherTimer.class:103 +#: FTimer.class:80 +msgid "Start" +msgstr "Inicia" + +#: FOtherTimer.class:110 +msgid "3" +msgstr "-" + +#: FOtherTimer.class:117 +msgid ":" +msgstr "-" + +#: FOtherTimer.class:125 +msgid "0" +msgstr "-" + +#: FOtherTimer.class:131 +msgid "Another timer example by Maxim Lapis <maxim_lapis@web.de>" +msgstr "Un altre exemple de temporitzador per Maxim Lapis <maxim_lapis@web.de>" + +#: FTimer.class:81 +msgid "Trigger" +msgstr "Gatell" + +#: FTimer.class:94 +msgid "Reset" +msgstr "Restableix" + +#: FTimer.class:100 +msgid "1500" +msgstr "-" + +#: FTimer.class:106 +msgid "Start Timer in..." +msgstr "Inicia temporitzador en..." + +#: FTimer.class:113 +msgid "Start delay" +msgstr "Temps encès" + +#: FTimer.class:120 +msgid "Stop delay" +msgstr "Temps apagat" + +#: FTimer.class:126 +msgid "200" +msgstr "-" + +#: FTimer.class:132 +msgid "800" +msgstr "-" + +#: FTimer.class:143 +msgid "Start/Stop" +msgstr "Inicia/Atura" + +#: FTimer.class:154 +msgid "Timer example by Juergen Zdero <juergen@zdero.com>" +msgstr "Exemple de temporitzador per Juergen Zdero <juergen@zdero.com>" + +#: FTimer.class:159 +msgid "ms" +msgstr "-" + +#: FTimer.class:172 +msgid "Or start and stop Timer" +msgstr "O temporitzador d'engega i apaga" + +#~ msgid "Delay" +#~ msgstr "Retard" +#~ msgid "High-Time" +#~ msgstr "Temps en alta" +#~ msgid "Low-Time" +#~ msgstr "Temps en baixa" + diff --git a/app/examples/Basic/Timer/.lang/cs.mo b/app/examples/Basic/Timer/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..590a59e50e51f620078d260c5794c44ccb761217 GIT binary patch literal 1411 zcmb7?&x;dD6vs=~A8Xd%Kk(wh8^j+Vof#Ez%#7nSrpJw&UnH4<6&9(a%S_L7szP^7 zoajOJvxN@i6yAyiMk`H*X$xRS*Ra;-BF6CGBKky;$j2pL(wz?^o*I{aYRg ztV56^kRIe`NVo|b)*EmO_!j&De4p?mh);aNHUoYJ4}<473$Y6;RUb& z`vl1S-39l8Pm}w<5Rpml2wZ>oCQ zSI3)nLX;wkt&A*5?c-$HRO5i6)DP|r!itKDl@(QX9P9%x3UoT1PAt9(Wp{8Mts)SP z$wq^#+PtEMS<)8u1~DNbSu@BfEsep@Fxrnt1XsWwrlX>Pw$Wh!ZbM7jy}G2LrqXoH zSCQFhcwu0jzQxvb!?gPS)}o--OInaS3H{YD%D)6 zm@1c?is}S>>13r<8T!q0D^8*0WL^5Dlquv+T`Lv6R9?o`sYO!9p|mE-(9CQ;Per%j z(WF;!bNOjEo2_`&>J(gcb4fOpQ&X~)p}bqXGRr9^^ZDs8NSmlR^vO`FkSln@`c9|E zQkM-zsFAMQMm<(7+tGF?13f{Fc^P4>7iViT&iK%e%etW=$I}ht2fCS|@p@oW!byHv z8LmHGTc!5;qqWt=Q2Nwck+JE>-ijjQf9H!TsyhFx{WnF6Mym$(?tKj>t_FJT*)UVL zUqb(3{l|+s!)L{%z-joH`i8RUt*otD>4%0E6lsiKc9b?a^\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FTimer.form:28 +msgid "Timer example" +msgstr "Příklad časovače" + +#: FOtherTimer.class:16 +msgid "The counter is set to stop at 0 seconds!" +msgstr "Počítadlo je nastaveno na 0 sekund!" + +#: FOtherTimer.class:44 +msgid "Stop" +msgstr "Stop" + +#: FOtherTimer.class:64 +msgid "Time is over!" +msgstr "Čas vypršel!" + +#: FOtherTimer.form:14 +msgid "Other Example" +msgstr "Jiné příklady" + +#: FOtherTimer.form:22 FTimer.form:34 +msgid "Start" +msgstr "-" + +#: FOtherTimer.form:35 +msgid ":" +msgstr "-" + +#: FOtherTimer.form:48 +msgid "Another timer example by Maxim Lapis <maxim_lapis@web.de>" +msgstr "Další příklad časovače od Maxim Lapis <maxim_lapis@web.de>" + +#: FTimer.form:35 +msgid "Trigger" +msgstr "-" + +#: FTimer.form:45 +msgid "Reset" +msgstr "-" + +#: FTimer.form:51 +msgid "1500" +msgstr "-" + +#: FTimer.form:57 +msgid "Start Timer in..." +msgstr "Start Časovače v..." + +#: FTimer.form:64 +msgid "Start delay" +msgstr "Start čas" + +#: FTimer.form:71 +msgid "Stop delay" +msgstr "Stop čas" + +#: FTimer.form:77 +msgid "200" +msgstr "-" + +#: FTimer.form:83 +msgid "800" +msgstr "-" + +#: FTimer.form:94 +msgid "Start/Stop" +msgstr "-" + +#: FTimer.form:102 +msgid "Timer example by Juergen Zdero <juergen@zdero.com>" +msgstr "Příklad časovače od Juergen Zdero <juergen@zdero.com>" + +#: FTimer.form:107 +msgid "ms" +msgstr "-" + +#: FTimer.form:120 +msgid "Or start and stop Timer" +msgstr "Nebo start a konec Časovače" diff --git a/app/examples/Basic/Timer/.lang/de.mo b/app/examples/Basic/Timer/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..18d6f61d0252ea8ef52dd24e7eb349677c35c0ee GIT binary patch literal 1420 zcmb7?&ubGw6vxNk8h`&n1eNjN50FjTidfTHyPB;=^P?oGU_oTF88@SwnXo<zNu{ z;876o;@{xOo8ra4Ko6n^6+MXHKj8Q6PE)~CCwcRk_h$C}n0cS}tbfKZ_CpRq-a-yT zqBWQ>K7;GQFW@@xo8?asfA$O04EP&70G?gT*mkfEZUApu2HxCuOgWUyofN(!;ZK+5MdoDt7hp0zv&mSLX&kAROr%I76W zabAJ+-CJ-k_ztA}K3e-%%kSV8*nfb>z&{|}-9Z$)11x|pI0e$VhamZVV!0D*8uZOJ z2+aQt^0Nm*@%KRxBE5Sc&2<;JkrWKN3mSB%bRSd`<-Zw1@!9B^kr6fq8HW^^tF-Bg zm>b!{EEfGRO1(IeZfh-}mML_6OR$3rv0>M>7@ z9~6&}lvB*K1y`;wQ#w#lA`+-{`o=XXYvuUURD+pY*D3xiamR zN)4~soWfBR4*XC!E#dcyyyDg_&r!(9N@Y5dc^!u?+G=+ZTOEtlRt{>xC&(F1Hobi=&l-6w#JDv)3N2;*M$J^56m}7r69dx;nDtlDt zqL4{niij_0wc2jNUE9`3y9$pouzl4RiVXuThhX!A_|5C!E|R{FeaAlU^0)uCm-2&d znlH5#PqorYFdao`Ml?}V+(kshrCvtNqikMCgKk2Hw?nk(;5IgGR}D+e;ttj2%2(4^ N?jVWYyh_q4{sO7#S498- literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Timer/.lang/de.po b/app/examples/Basic/Timer/.lang/de.po new file mode 100644 index 00000000..4486944e --- /dev/null +++ b/app/examples/Basic/Timer/.lang/de.po @@ -0,0 +1,93 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FTimer.form:28 +msgid "Timer example" +msgstr "Timer-Beispiel" + +#: FOtherTimer.class:16 +msgid "The counter is set to stop at 0 seconds!" +msgstr "Der Timer soll in 0 Sekunden stoppen!" + +#: FOtherTimer.class:44 +msgid "Stop" +msgstr "-" + +#: FOtherTimer.class:64 +msgid "Time is over!" +msgstr "Zeit ist abgelaufen !" + +#: FOtherTimer.form:14 +msgid "Other Example" +msgstr "Noch ein Beispiel" + +#: FOtherTimer.form:22 FTimer.form:34 +msgid "Start" +msgstr "-" + +#: FOtherTimer.form:35 +msgid ":" +msgstr "-" + +#: FOtherTimer.form:48 +msgid "Another timer example by Maxim Lapis <maxim_lapis@web.de>" +msgstr "Noch ein Timer-Beispiel von Maxim Lapis <maxim_lapis@web.de>" + +#: FTimer.form:35 +msgid "Trigger" +msgstr "Auslösen" + +#: FTimer.form:45 +msgid "Reset" +msgstr "Zurücksetzen" + +#: FTimer.form:51 +msgid "1500" +msgstr "-" + +#: FTimer.form:57 +msgid "Start Timer in..." +msgstr "Timer starten in..." + +#: FTimer.form:64 +msgid "Start delay" +msgstr "Start nach" + +#: FTimer.form:71 +msgid "Stop delay" +msgstr "Stop nach" + +#: FTimer.form:77 +msgid "200" +msgstr "-" + +#: FTimer.form:83 +msgid "800" +msgstr "-" + +#: FTimer.form:94 +msgid "Start/Stop" +msgstr "-" + +#: FTimer.form:102 +msgid "Timer example by Juergen Zdero <juergen@zdero.com>" +msgstr "Timer-Beispiel von Juergen Zdero <juergen@zdero.com>" + +#: FTimer.form:107 +msgid "ms" +msgstr "-" + +#: FTimer.form:120 +msgid "Or start and stop Timer" +msgstr "Oder Timer starten und stoppen" + diff --git a/app/examples/Basic/Timer/.lang/es.mo b/app/examples/Basic/Timer/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..be4ac5a62ab2619ac74de380ec51df5406adff18 GIT binary patch literal 1469 zcmb7@&ubGw6vxL}f5qRwMHEJa9+XY1qLgi{S(|N*%`cKv(2Gp6<95nsC(dqbEB+0F zpcnrJ4_*Ypn_k6#K=I~95EL&SMEt(Z#46~)32#1c-p;%qGcWJ=jXq)+hakrwuOY`F z_cma{_zaTz1=A?_72F7Zv-aMOTRsH$V*ME0 z4n6^M;B(7YAf10>`3}5<^+#|EcnZltWoIcVq zpfhxyV$eMd$5yNC2Pw`$$Yuz_XJd~g-2n~C6|UJX2wllO$N>n&rn=Y!J9B8e;&Jzf!gl@!K1(|jySr+Q?&8Ii4cM|Sdo z91GJaRyRCJg-N-H18BO>+vJedGw1%mEo($lB$D4!QN&fuS45-&nwMpAf;D9#Q`RCc zgL&Wr8}s?RwFEK}YXg0}g(y%FYG9>+TqRtuNOOW^;fMA7xw6$=U6v-4F~1Q=qkq-6 zG_V%$k~QDey{x>pQDKO3dI@V7y(GJ-Qx2SK(j-d9E^l}<^WL1#uldbZxn9dP>P}Ox zDEjD>L@Hf=-S?VKweFNWex>eK%cpPDYksaGlGJG%5hsyIwQ>3ELZ!lMUe)IXzv`7M zMXyw9`mNS9w#MPI2&L1OqUZ98SDRa)kcCR67^%EA;rbqz=c?tZzn$El1F zZmo}eQn{Lr_amX=Dc)Ta23LP^p*`zNuJ5C?Zb{?#aaRW_4qZOkQE84jzy0c|(OfP` zG>yhH@HRAU)Ljv1!=e3e7vZs0R|&({;0@_gOGbZqMMtfNWaT(CXdCJw4Q0?#I%_8D z30pFcL!$EEQj}S5H6@xZK-BDv9&R0P2qO&o(thn@83^V___L4`z9`_$^i+&;ng91& U_?LZnJO9&#C6#13XoDyC3C`7H)c^nh literal 0 HcmV?d00001 diff --git a/app/examples/Basic/Timer/.lang/es.po b/app/examples/Basic/Timer/.lang/es.po new file mode 100644 index 00000000..8d2a4f8e --- /dev/null +++ b/app/examples/Basic/Timer/.lang/es.po @@ -0,0 +1,96 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FTimer.form:28 +msgid "Timer example" +msgstr "Ejemplo de Temporizador" + +#: FOtherTimer.class:38 +msgid "Please fill in valid values!" +msgstr "Ingrese valores validos!" + +#: FOtherTimer.class:63 +msgid "Time is over!" +msgstr "El tiempo ha terminado" + +#: FOtherTimer.form:14 +msgid "Other Example" +msgstr "Otro ejemplo" + +#: FOtherTimer.form:22 FTimer.form:34 +msgid "Start" +msgstr "Iniciar" + +#: FOtherTimer.form:29 +msgid "3" +msgstr "-" + +#: FOtherTimer.form:36 +msgid ":" +msgstr "-" + +#: FOtherTimer.form:44 +msgid "0" +msgstr "-" + +#: FOtherTimer.form:50 +msgid "Another timer example by Maxim Lapis <maxim_lapis@web.de>" +msgstr "Otro ejemplo de Temporizador por Maxim Lapis <maxim_lapis@web.de>" + +#: FTimer.form:35 +msgid "Trigger" +msgstr "Disparador" + +#: FTimer.form:48 +msgid "Reset" +msgstr "Reiniciar" + +#: FTimer.form:54 +msgid "1500" +msgstr "-" + +#: FTimer.form:60 +msgid "Start Timer in..." +msgstr "Iniciar temporizador en..." + +#: FTimer.form:67 +msgid "Start delay" +msgstr "Retardar inicio" + +#: FTimer.form:74 +msgid "Stop delay" +msgstr "Retardar parada" + +#: FTimer.form:80 +msgid "200" +msgstr "-" + +#: FTimer.form:86 +msgid "800" +msgstr "-" + +#: FTimer.form:97 +msgid "Start/Stop" +msgstr "Iniciar/Parar" + +#: FTimer.form:108 +msgid "Timer example by Juergen Zdero <juergen@zdero.com>" +msgstr "Ejemplo de Temporizador por Juergen Zdero <juergen@zdero.com>" + +#: FTimer.form:113 +msgid "ms" +msgstr "-" + +#: FTimer.form:126 +msgid "Or start and stop Timer" +msgstr "O iniciar y detener el Temporizador" diff --git a/app/examples/Basic/Timer/.lang/nl.mo b/app/examples/Basic/Timer/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..4e3b7e6ccf9be5036acc0bc0229062834fb6647a GIT binary patch literal 1428 zcmb7?&rcIU6vtP^U-c(wFeaFc7c?PTwnZY83T?0uw50?JCdR~UyN`C7?asP8<%jX& zSr5jff&Ss?)2q z7RY83m32a6DNt={Xmi0a*C+_3@^WISID5A^UlMDj<&}l%Vrr>sF4KnOL-V$+DJQa4 z=7woan%Oa7Wpdf3tbL30`byk^&Kl363WlN06Q;`}Nnk}sDFuKe>(PNXUemC}CJnY5LfRcNe- zB({BIscB1(`mQa#DbZ-z0h)blwKiu??BC;L*C{YdUPC$3Yv#m6UFwu!#(yPr6mm$! zwV_l{CvqL(V8sFxiNZqsx01%Q#@qumwjOH?aetFovG>HARl-01vo zne832&~V9(bcWK1ztKW\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FTimer.form:28 +msgid "Timer example" +msgstr "Timer voorbeeld" + +#: FOtherTimer.class:16 +msgid "The counter is set to stop at 0 seconds!" +msgstr "The teller is ingestelt om bij 0 seconden te stoppen!" + +#: FOtherTimer.class:44 +msgid "Stop" +msgstr "Stop" + +#: FOtherTimer.class:64 +msgid "Time is over!" +msgstr "Tijd is voorbij!" + +#: FOtherTimer.form:14 +msgid "Other Example" +msgstr "Ander Voorbeeld" + +#: FOtherTimer.form:22 FTimer.form:34 +msgid "Start" +msgstr "Start" + +#: FOtherTimer.form:35 +msgid ":" +msgstr "-" + +#: FOtherTimer.form:48 +msgid "Another timer example by Maxim Lapis <maxim_lapis@web.de>" +msgstr "Een ander timer voorbeeld door Maxim Lapsis <maxil_lapis@web.de>" + +#: FTimer.form:35 +msgid "Trigger" +msgstr "-" + +#: FTimer.form:45 +msgid "Reset" +msgstr "" + +#: FTimer.form:51 +msgid "1500" +msgstr "-" + +#: FTimer.form:57 +msgid "Start Timer in..." +msgstr "Start Timer in..." + +#: FTimer.form:64 +msgid "Start delay" +msgstr "Start uitstel" + +#: FTimer.form:71 +msgid "Stop delay" +msgstr "Stop uitstel" + +#: FTimer.form:77 +msgid "200" +msgstr "-" + +#: FTimer.form:83 +msgid "800" +msgstr "-" + +#: FTimer.form:94 +msgid "Start/Stop" +msgstr "Start/Stop" + +#: FTimer.form:102 +msgid "Timer example by Juergen Zdero <juergen@zdero.com>" +msgstr "Timer voorbeeld door Juergen Zdero <juergen@zdero.com>" + +#: FTimer.form:107 +msgid "ms" +msgstr "-" + +#: FTimer.form:120 +msgid "Or start and stop Timer" +msgstr "Of start en stop Timer" diff --git a/app/examples/Basic/Timer/.project b/app/examples/Basic/Timer/.project new file mode 100644 index 00000000..8be0e980 --- /dev/null +++ b/app/examples/Basic/Timer/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.10.90 +Title=Timer example +Startup=FTimer +Icon=timer.png +Version=3.10.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Authors="Maxim Lapis" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Basic/Timer/.src/FOtherTimer.class b/app/examples/Basic/Timer/.src/FOtherTimer.class new file mode 100644 index 00000000..984d437b --- /dev/null +++ b/app/examples/Basic/Timer/.src/FOtherTimer.class @@ -0,0 +1,85 @@ +' Gambas class file + +'&HC00F47& = red +'&H4BC021& = green + +Private $iCounter As Integer + +Public Sub Button1_Click() + + Dim iSeconds, iMinutes As Integer + + iSeconds = vbSeconds.Value + iMinutes = vbMinutes.Value + + If iSeconds = 0 And iMinutes = 0 Then + Message.Info(("The counter is set to stop at 0 seconds!")) + Return + Endif + + If Not Timer1.enabled Then + + 'Normalize minutes and seconds + If iSeconds >= 60 Then + + While iSeconds > 59 + + Inc iMinutes + iSeconds = iSeconds - 60 + + Wend + + Endif + + 'Update normalized values + vbSeconds.Value = iSeconds + vbMinutes.Value = iMinutes + + 'Store the total count to simplify code + $iCounter = iSeconds + iMinutes * 60 + + 'Let the timer start! + Timer1.enabled = True + + Button1.Text = ("Stop") + Button1.Background = &HC00F47& + + Else + + Button1.Text = ("Start") + Button1.Background = &H4BC021& + Timer1.enabled = False + Endif + + 'thank you Benoit, that is just a great thing +End + +Public Sub Timer1_Timer() + + 'The counter has reached 0. Stop it and update the window + If $iCounter = 0 Then + Timer1.Enabled = False + Button1.Text = ("Start") + Button1.Background = &H4BC021& + Message.Info(("Time is over!")) + Else + + 'Seconds are over, decrease the minute count by 1 and reset the seconds count + If vbSeconds.Value = 0 Then + + Dec vbMinutes.Value + + vbSeconds.Value = 59 + + Else + + Dec vbSeconds.Value + + Endif + + Endif + + 'Also decrease the global counter + Dec $iCounter + +End diff --git a/app/examples/Basic/Timer/.src/FOtherTimer.form b/app/examples/Basic/Timer/.src/FOtherTimer.form new file mode 100644 index 00000000..ea90e61b --- /dev/null +++ b/app/examples/Basic/Timer/.src/FOtherTimer.form @@ -0,0 +1,40 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(52.7143,62.4286,32,18) + Background = &HB1C00E& + Text = ("Other Example") + Icon = Picture["timer.png"] + Resizable = False + { Button1 Button + MoveScaled(17,11,11,5) + Font = Font["14"] + Background = &H4BC021& + Text = ("Start") + } + { vbSeconds ValueBox + MoveScaled(10,11,6,5) + Font = Font["18"] + Background = &H93B8B0& + Value = "3" + } + { TextLabel2 TextLabel + MoveScaled(7,11,3,4) + Font = Font["Adobe Courier,18,Bold"] + Text = (":") + Alignment = Align.Center + } + { vbMinutes ValueBox + MoveScaled(1,11,6,5) + Font = Font["18"] + Background = &H93B8B0& + Value = "0" + } + { TextLabel1 TextLabel + MoveScaled(1,1,29,9) + Text = ("Another timer example by Maxim Lapis <maxim_lapis@web.de>") + } + { Timer1 #Timer + #MoveScaled(23,2) + } +} diff --git a/app/examples/Basic/Timer/.src/FTimer.class b/app/examples/Basic/Timer/.src/FTimer.class new file mode 100644 index 00000000..9400ca76 --- /dev/null +++ b/app/examples/Basic/Timer/.src/FTimer.class @@ -0,0 +1,56 @@ +' Gambas class file + +Public Sub Form_Open() + + FOtherTimer.Show + +End + +Public Sub Button1_Click() + + Timer1.Delay = CInt(TextBox1.Text) + Timer1.Enabled = True + +End + +Public Sub Timer1_Timer() + + Label1.Background = &HFF0000& + +End + +Public Sub Button2_Click() + + Timer1.Enabled = False 'try without this line + Label1.Background = &HDCDCDC& + +End + +Public Sub ToggleButton1_Click() + + If ToggleButton1.Value = True Then + Timer2.Delay = CInt(TextBox2.Text) 'low + Timer3.Delay = CInt(TextBox3.Text) 'high + Timer2.Enabled = True + Else + Timer2.Enabled = False + Timer3.Enabled = False + Endif + +End + +Public Sub Timer2_Timer() + + Label5.Background = &HFF0000& + Timer3.Enabled = True + Timer2.Enabled = False + +End + +Public Sub Timer3_Timer() + + Label5.Background = &H00FF00& + Timer2.Enabled = True + Timer3.Enabled = False + +End diff --git a/app/examples/Basic/Timer/.src/FTimer.form b/app/examples/Basic/Timer/.src/FTimer.form new file mode 100644 index 00000000..a56eb981 --- /dev/null +++ b/app/examples/Basic/Timer/.src/FTimer.form @@ -0,0 +1,97 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(33.5714,24,52,33) + Text = ("Timer example") + Icon = Picture["timer.png"] + Resizable = False + { Button1 Button + MoveScaled(35,14,15,4) + ToolTip = ("Start") + Text = ("Trigger") + } + { Label1 Label + MoveScaled(30,14,3.5714,4) + Border = Border.Etched + } + { Button2 Button + MoveScaled(14,14,14,4) + ToolTip = ("Reset") + Text = ("Reset") + } + { TextBox1 TextBox + MoveScaled(2,14,8,4) + Text = ("1500") + Alignment = Align.Right + } + { Label2 Label + MoveScaled(2,10,34,3) + Text = ("Start Timer in...") + Alignment = Align.Left + } + { Label3 Label + MoveScaled(2,24,11,3) + Foreground = &H00AA00& + Text = ("Start delay") + Alignment = Align.Left + } + { Label4 Label + MoveScaled(15,24,13,3) + Foreground = &HFF0000& + Text = ("Stop delay") + Alignment = Align.Left + } + { TextBox2 TextBox + MoveScaled(2,27,8,4) + Text = ("200") + Alignment = Align.Right + } + { TextBox3 TextBox + MoveScaled(15,27,8,4) + Text = ("800") + Alignment = Align.Right + } + { Label5 Label + MoveScaled(30,27,3.5714,4) + Border = Border.Etched + } + { ToggleButton1 ToggleButton + MoveScaled(35,27,15,4) + Text = ("Start/Stop") + } + { Timer2 #Timer + #MoveScaled(36,1) + } + { TextLabel1 TextLabel + MoveScaled(2,1,33,6) + Text = ("Timer example by Juergen Zdero <juergen@zdero.com>") + } + { Label6 Label + MoveScaled(10,14,4,4) + Text = ("ms") + } + { Separator1 Separator + MoveScaled(2,9,48,1) + } + { Separator2 Separator + MoveScaled(2,18,48,3) + } + { Label7 Label + MoveScaled(2,21,33,3) + Text = ("Or start and stop Timer") + } + { Label8 Label + MoveScaled(10,27,4,4) + Text = ("ms") + } + { Label9 Label + MoveScaled(23,27,4,4) + Text = ("ms") + } + { Timer1 #Timer + #MoveScaled(41,1) + } + { Timer3 #Timer + #MoveScaled(45,1) + } +} diff --git a/app/examples/Basic/Timer/timer.png b/app/examples/Basic/Timer/timer.png new file mode 100644 index 0000000000000000000000000000000000000000..6708fdd27788c95fe589ee232dff093831b8041a GIT binary patch literal 2778 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 literal 0 HcmV?d00001 diff --git a/app/examples/Control/ArrayOfControls/.directory b/app/examples/Control/ArrayOfControls/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Control/ArrayOfControls/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Control/ArrayOfControls/.icon.png b/app/examples/Control/ArrayOfControls/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..cc422dcf5e94a28456f006d4ef0f5368a1fc4a81 GIT binary patch literal 4209 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 literal 0 HcmV?d00001 diff --git a/app/examples/Control/ArrayOfControls/.lang/ca.po b/app/examples/Control/ArrayOfControls/.lang/ca.po new file mode 100644 index 00000000..41e8b209 --- /dev/null +++ b/app/examples/Control/ArrayOfControls/.lang/ca.po @@ -0,0 +1,45 @@ +msgid "" +msgstr "" +"Project-Id-Version: ArrayOfControls\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 16:53+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Array of Controls" +msgstr "Matriu de controls" + +#: FMain.class:70 +msgid "Clear" +msgstr "Neteja" + +#: FMain.class:112 +msgid "Dial" +msgstr "Marcador" + +#: FMain.class:80 +msgid "Dialing " +msgstr "Marcant" + +#: .project:2 +msgid "Example how to use an array of controls" +msgstr "Exemple de com fer servir una matriu de controls" + +#: FMain.class:8 +msgid "Quit" +msgstr "Surt" + +#: FMain.class:78 +msgid "There is no number I could dial." +msgstr "No hi ha cap número que pugui marcar." + +#: FMain.class:45 +msgid "You have clicked the button &1" +msgstr "Heu fet clic al botó &1" + diff --git a/app/examples/Control/ArrayOfControls/.lang/cs.mo b/app/examples/Control/ArrayOfControls/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..4202759d8736d20af7f3d13a4caf54a891e0af0b GIT binary patch literal 782 zcmYk3&2AGh6on0xzX*`%7KH8wSiq!IVo?nRb<|8t)X5KNQmO3R88>m`*pbIm+OAoW zWhGd`n!Ey=q!L?R0KqH3iVfm8P?alvb8OGK*H_nH=jVE0r1TC*oo7gV!gOW0f*9a1w|-H6s>+KOA7)keM4Yy>T_Fa6J; z!kq|dJC0gGtr1irdfo_Y)dw#d^|+Mq+y!mRb*{KGHl(#ql2ARW#k3sPqH3}dRVuA` zbMqOjx?AwB2-<=VLQ10gddFY6oFpqsmW}P6c|t1Hsew;XP z3q!@FenM%F+g!M(o%UL=bmsGI?Ft*jIyD*Q9MaN`bR~B3b}~K^dNQV=QG#t9PDib^a`{`)rV9k?vlW|}1jP`gR3B}2$$=H!U{jWx+Z}cp;ZSa12mO33x#\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Array of Controls" +msgstr "Řízení pole" + +#: .project:2 +msgid "Example how to use an array of controls" +msgstr "Příklad jak použít řízení pole" + +#: FMain.class:8 +msgid "Quit" +msgstr "Ukončit" + +#: FMain.class:45 +msgid "You have clicked the button &1" +msgstr "Kliknul si na tlačítko &1" + +#: FMain.class:70 +msgid "Clear" +msgstr "Vyčsti" + +#: FMain.class:78 +msgid "There is no number I could dial." +msgstr "Toto není číslo, nelze volat." + +#: FMain.class:80 +msgid "Dialing " +msgstr "Vytáčení " + +#: FMain.form:25 +msgid "Dial" +msgstr "Vytočit" diff --git a/app/examples/Control/ArrayOfControls/.lang/de.mo b/app/examples/Control/ArrayOfControls/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..3595d0ff66a1e667a836cf2bd9d2e273fb7aeeed GIT binary patch literal 806 zcmY+Czi!k(5XKh>37kX$vSsYWBz(Dhnp5NuK7$r@1dn<-{;$%cKg@hIE;a*;1QgQ)USzsM-|UrleHPnBq*u zyPS|qnZ~7aM$?@ov1!d4j-A&|ye+ncGTNujVC_Y)5zp2y-v?-1@ye?&RCz;M^QxGpcB$c*a_OvYS3!+!@=Mw zj_OG%C+rPb<~~J1cVkptxe`UInQ9t4IXmN1qtot$XZQ}p9_-z`ov zsq|xtQ)vr!Pe#LaZ~5$8)z%JMFVwL~FlV2Z$I3Otz;THYW}H{$YKKI0^reVX*5cLC zr!@PgB%ZM\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Array of Controls" +msgstr "Steuerelement-Array" + +#: .project:2 +msgid "Example how to use an array of controls" +msgstr "Beispiel, wie man einen Array von Steuerelementen benutzt" + +#: FMain.class:8 +msgid "Quit" +msgstr "Beenden" + +#: FMain.class:45 +msgid "You have clicked the button &1" +msgstr "Du hast die Taste &1 angeklickt" + +#: FMain.class:70 +msgid "Clear" +msgstr "Löschen" + +#: FMain.class:78 +msgid "There is no number I could dial." +msgstr "Es gibt keine Nummer zum Wählen." + +#: FMain.class:80 +msgid "Dialing " +msgstr "Wähle " + +#: FMain.form:25 +msgid "Dial" +msgstr "Wählen" diff --git a/app/examples/Control/ArrayOfControls/.lang/nl.mo b/app/examples/Control/ArrayOfControls/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..21dc7147212f2b358655f4987274b382533ab1eb GIT binary patch literal 806 zcmZvZ&2AGh6om~GS|YIkwt(&eHLFQ8{ZUK!5mghUAWBP>7O`|@ZZc*Zdt`gkmMv>m zhzDTL3$Z~U9)T4s5XWhwZm{(E*dE{OGxvVGweU$`TnBeR4sL)iAdc_gCinraf?r?( z{02+lFKB|rOK0aRP{Mv4dJlRC{R0iq+faM??5+3EnC}BLzV{Kj3H^Nj{VVi7_CKL_ zp-WeUK#ExmmNUN)u^1Iw!3<`Ek%9F8V>dBBD^(nKp&M8OMmUv@pD&&+Y(<&=D zr%*5r>kzD=^cap9Q+@aNW zx}K!%q`gY%MkigP!@*ADh4djAIBC3=!MYB;R$8CZf#l%n(Q({7&3Ve&RxSK|D14zR zuAQy8W!?A*5}0u<$H;GVW4FKCyKt7a+Kp(74W2lya3%0bSgEvB<{@Q;be_ZG!@;v; z>*8LtGGdqXOlEUbxIRwd%LM_!{@SoK`&)i}nXTQT*q45UgbVIz-E;?i^U Vo`->KMLAPxpvx(OO0CKu{sGR?)+PV| literal 0 HcmV?d00001 diff --git a/app/examples/Control/ArrayOfControls/.lang/nl.po b/app/examples/Control/ArrayOfControls/.lang/nl.po new file mode 100644 index 00000000..d2b10fc5 --- /dev/null +++ b/app/examples/Control/ArrayOfControls/.lang/nl.po @@ -0,0 +1,43 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: ArrayOfControls 3.5.90\n" +"PO-Revision-Date: 2014-10-02 15:13 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Array of Controls" +msgstr "Reeks van controles" + +#: .project:2 +msgid "Example how to use an array of controls" +msgstr "Voorbeeld in hoe een 'Reeks van controles' te gebruiken" + +#: FMain.form:25 +msgid "Dial" +msgstr "Draai" + +#: FMain.class:8 +msgid "Quit" +msgstr "Afsluiten" + +#: FMain.class:45 +msgid "You have clicked the button &1" +msgstr "Je hebt op de knop &1 geklikt" + +#: FMain.class:70 +msgid "Clear" +msgstr "Opschonen" + +#: FMain.class:78 +msgid "There is no number I could dial." +msgstr "Er is geen nummer wat ik kan draaien" + +#: FMain.class:80 +msgid "Dialing " +msgstr "Nummer kiezen" + diff --git a/app/examples/Control/ArrayOfControls/.project b/app/examples/Control/ArrayOfControls/.project new file mode 100644 index 00000000..ac6590b4 --- /dev/null +++ b/app/examples/Control/ArrayOfControls/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=Array of Controls +Startup=FMain +Icon=phone.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Description="Example how to use an array of controls" +Authors="Matti (math.eber@t-online.de)" +TabSize=2 +Translate=1 +Language=de +ControlPublic=1 +SourcePath=/home/mathias/Basic +Packager=1 diff --git a/app/examples/Control/ArrayOfControls/.src/FMain.class b/app/examples/Control/ArrayOfControls/.src/FMain.class new file mode 100644 index 00000000..3f90faa8 --- /dev/null +++ b/app/examples/Control/ArrayOfControls/.src/FMain.class @@ -0,0 +1,83 @@ +' Gambas class file + +Private aButtons[13] As Button ' Dim the array of controls + +Public Sub Form_Open() + Dim i As Integer + + btnClose.Tooltip = ("Quit") + Me.Center + For i = 1 To 12 + aButtons[i] = New Button(Me) As "Buttongroup" ' Create a new button in the array and store it in an action group + With aButtons[i] + .X = (i - Int((i - 1) / 3) * 3) * 60 - 30 + .Y = Int((i - 1) / 3) * 60 + 170 + .Width = 42 + .Height = 42 + .Font.Grade = 5 + If i = 10 Then + .Text = "*" + Else If i = 11 Then + .Text = "0" + Else If i = 12 Then + .Text = "#" + Else + .Text = i + Endif + End With + Next + +End + + +Public Sub Buttongroup_Click() ' This is an event of the action group. This event is fired if any of the buttons is clicked + ' You see a list of available events if you type "Buttongroup_" +' Dim w As Integer + + tbNumber.Text = tbNumber.Text & Last.Text ' find out with LAST which button was clicked and hand its text over to TextBox + + ' w = Last.Width ' this would return the width of the last button + ' Print w +End + +Public Sub Buttongroup_Menu() ' Another event of the action group: Right-click on any of the buttons + + Message.Info(Subst(("You have clicked the button &1"), Last.Text)) + +End + +Public Sub btnClose_Click() + + If tbNumber.Text = "" Then + Me.Close + Else + tbNumber.Text = "" + Endif + +End + +Public Sub Form_KeyPress() + + If Key.code = Key.Esc Then Me.Close + +End + +Public Sub tbNumber_Change() + + If tbNumber.Text = "" Then + btnClose.Tooltip = ("Quit") + Else + btnClose.Tooltip = ("Clear") + Endif + +End + +Public Sub btnDial_Click() + + If tbNumber.Text = "" Then + Message.Warning(("There is no number I could dial.")) + Else + Message.Info(("Dialing ") & tbNumber.Text) + Endif + +End diff --git a/app/examples/Control/ArrayOfControls/.src/FMain.form b/app/examples/Control/ArrayOfControls/.src/FMain.form new file mode 100644 index 00000000..a4d87f51 --- /dev/null +++ b/app/examples/Control/ArrayOfControls/.src/FMain.form @@ -0,0 +1,25 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,32,61) + Background = &H525252& + Icon = Picture["Phone,.png"] + Resizable = False + Utility = True + { tbNumber TextBox + MoveScaled(3,5,26,6) + Font = Font["Bold,+4"] + Background = &HBBBFB7& + Alignment = Align.Right + ReadOnly = True + } + { btnDial Button + MoveScaled(3,15,12,6) + ToolTip = ("Dial") + Picture = Picture["green.png"] + } + { btnClose Button + MoveScaled(17,15,12,6) + Picture = Picture["red.png"] + } +} diff --git a/app/examples/Control/ArrayOfControls/green.png b/app/examples/Control/ArrayOfControls/green.png new file mode 100644 index 0000000000000000000000000000000000000000..930782f8a6a9d45d5f7703e9bd31a7cb1bde4341 GIT binary patch literal 873 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabRA=0VAK!r32_B-8R{7r)`8Im28LA( z4C@&ffD9msjDR8=85q{UlmVrHT!;dQ;IAt7$OemBAX9q z17#s@fT#!BvKnR)kOAZ(%L3H^&ELtuFcGF^F_;0g4z3JhGQ@)rHqZ))OCVf`+kgtT zfyIGRKsLl=h)STZVHyo%@>qbLN+}8Q3ua(auGqxvaO}J6;eSv1os@zep9uT*GWv>) znS{rmPtA;bu6YYQ`@2JI-n-imOD?}S{YL1*7CV3QpRp#DJZB*Ed{Z8_YK>NIKf}{qOvPZ`2wEK0f((GVPi5?%+-x&PB($_13$KoY0(a ze_-yUBle8@*WZ`tVJxy(q;YY@B9}u(0$jKLZnRjSR`s7#wBvZrKb0*Z_cuFvIUA*Y zUT%DDfzeAn!5;n(#Y*QMe`sBhnvwBCf1%=$qL0j)r=GdF9iQXE_2KnHmIYiU>z>;g zzGPq#Wo&dg!Ffvj%Navc+spncDIHH7XJnM+?K{D>i|Z=q@7G7c

z=MHo6Vej zy?puSWNC@(6N}}ezHcm#m-&9RI6m%I`QLg*+raF3+@4E6fbxu|tDnm{r-UW|-zYu- literal 0 HcmV?d00001 diff --git a/app/examples/Control/ArrayOfControls/green1.png b/app/examples/Control/ArrayOfControls/green1.png new file mode 100644 index 0000000000000000000000000000000000000000..3008e150da0d1f25b311fe7740bbdd0f2f46c0be GIT binary patch literal 1548 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 KMNUMnLSTY~xpcY! literal 0 HcmV?d00001 diff --git a/app/examples/Control/ArrayOfControls/red.png b/app/examples/Control/ArrayOfControls/red.png new file mode 100644 index 0000000000000000000000000000000000000000..19a5c61a361c0c7b1c17c1f926e96600fed650da GIT binary patch literal 1452 zcmV;d1ylNoP)0~Q;-*1~xMXUgPMx>~7sPDF&B&BQe3=BkNE{!g&KP52 zbWt}Yl5G*mlr1LUOQLge%g8dfsTs@EEx>b5dHNJOUJ8tt(muBz?t^rtEv@^1JLg>Y z{lDI?>jV?PUfz`P_Kce&%LAS03=BnDO0j*Bqk*Y`?{%?lB6o-JJ~nv6IX>Ft_P~t@ zfo3E_Z(vH~AQs^aT5(x<{1Cp2pHvx|lmd~K1I%x-9ZPXJ2C+NxS3FapHpJPCwhHwD z-k&iT`7l;T-ck~rj(i6{ta5rHUQw_n=dprV&|l*(HQw7~YVQS?UxXgAIvsVcEcjf` zi#ajpRL+m~Cr^!y9@@IOHs^Sr$m=<4dbn~znh(Qa+UmTk;OjYubIugpbMiunMzwrZ z&hDI8yRd6D2DW)!?o3O$>%Y%)aG5ell^ytClO+6d)y zHszcsIGwX$G)?a=$)D?H#YBk@GQGx%8V?mbTjNYlf6lQQy9;i4k!&A}bADeWjz)vU zlvQw!mInQ}EU=-StyggJQsmuyqRAKVLChLOWGL`_Lq;NAG;8ScoShdZR? zc)G%`FGZfSyvBV^euGtGki*%s9JklFzKeZLeue?8Xk$qzFYFD>#^<|u;1c9t4y=z{ zb-szjF{9&TWNx4hf5aCDsI;;h3-Gn9;1-GeK!q2^BJSbpf^XtpT;&4!N&E*p(JO_r zJ8~FvGp2>qJ62}A%^=jwC)ssB`%_et~7-g2l%pzY9DvkDU$X zMwVbRuEl(*WsCF$c6BDpMYaUi1g@#Xlz1YQj->r-(8u!DMlUUM%E@(tX-8O(j}MzzLy@Oid2qBm z*-Ud2TN_MkQm?Wjj4}zQaz&F@0|OOFn>R-m%^4)#g$}e5Q6Qdzg zX-VfgQOP1tmPAWi$}MIAJ%L|IwSGcsoihVdOW6)AzHgl6WGwX4KFDVF57o5V@$S$;JsiR$9=1PB)2U>V4q_9jF4ZzZH|HS^7rrN`SwF|j_ z{;;Mp84RRx{FT!ByI%7#XLGrb#DX8xnO?rXK4)DoGxlC$jUNwr+BLV896e(ReLH6$ zr!QyC|4TL;kx-)}=johg&Ypa%U2{K6i?k2MoKrP69ipT0IxH{vSivK;bmyTOUmf>A zgN}l&d0HMD-P~1Y*)g)7NhorV_8PYpZdu@m8RK6=uUrBt0Er>X!?mG-tK6jx8PjzU+!;r4~K>eI8N?Z>#d~cy~`K zxeBg}d@3>{V|L^{=nl+?9L2vPkK=DGy!>O%et*oJHQ~QVLJ-N|?SZ}k0000O3 literal 0 HcmV?d00001 diff --git a/app/examples/Control/ArrayOfControls/red1.png b/app/examples/Control/ArrayOfControls/red1.png new file mode 100644 index 0000000000000000000000000000000000000000..3cbb165d0469195eb69054977ea2a7c057924b2a GIT binary patch literal 1571 zcmV+;2Hg3HP)W)p+=2A+QCKSEUqf=YkVSd7dBy2;Fd@mLSzP`rOJ`WA^fw213kPQC}14z3v7giMq~M~ zue3D4#)5|nz8JYSFo_~E8kj9r=CCZXp}A?~cwk=(Pqnf4*PMJTtz8_zq~ualYCME* zM7CiHFG=2w1omYVZ5*9s6~-br7Tks{k&lN2=p8)S!fy`n#(iZp7LCAlgnevkup2jG zdElAAAC~e82D(zX&&d#>n>b9%C>;$tB6qiNU*zscXJCKekse+R6bqd_C&&3fmEYAU zYSe4|_$2RZrsr}bXH(Aia^A{`IeT;NkMH5QCkdzNtns5NMNVJNgJ)QKnReeLCuytk znJR~?6jcTXxbuvAbv9ODhWAHq2BReb8bY<);_j+jwZ^Cib_VeY)S8+2|mY{Hs?V5jE z%4=!4=4xD964+M)FL&|e5YMzRSZ6;DN4hEvHWzFNj8%B$8YUv4#-sQe*2VJ?5z@~j zPGce@?ysOXur|`=!n(K<+ouzO?JhVInG5t*ur8#YGZ<6u98c7G$9&^WuLGy>=Zrlo zd3J)?nPhQwq`Tnr_-3R#oCgir~Z?HP>1*|~~RGLvLSU*AksnkzYupCPR^%bdOY9*gxIC3_of||@| z#dN{5oxHKwQCrVYoxET)QVB^X3+Y(=1CPVK(u=j=+kHH6ews z6&)F0YS3|!v))-G@2?H1DXlB@H=uil`^(6lAt9V8A52WJ>I{=h*c!-U%3(`0m*u*h ztC(I4Kv{jgQd*nRTJ8~InjI9$cB4|^?b%e3-B`DUV>MnV_$XEc#w4Wzvys>9sV3eN z#7qLYJF=@BUfLpUA%Q3Y`53LI`4C1cjWkB>$Bhkk;MMiKaGawVm4cNGiY(FYOklPr z>0BTYql`}TM1wsT4pf$KszLWv{HvYu6ZC{cW~VArv789Bws3Wcb~5s2aCuU$pGCF2 z=F3~nn;O?dt`8i@ddV{mMOq8)8RDjSn_V$!)9eJ8H&*a2@ zes((1^o60yc`WBx&Qu?N=}(9+dFojx18lDI^_(MBCaV18Bq0h$XD0BXJo~Geg<*7k#TG(MPkTKdvxNDBbu_>mCeFo3Q)JFRvqZxA> z7kuf|*_yGs!LEXP12@Oi|M#xrfpNyy%+p!)xYrXe+#dN{U1E-OOXRuR^eok zyqdsvsZp&7+!~n;yoMk2@~37|&M(f3^35w`*U{ZblyG}wM@*?$Id9pV)bkrL@%?CI zJWDP6k3H#h#R;kJFO*9ryg}B^aD((!vppo*UXS*W4pIZJg+#$aIMiDnvv?6h{{^I3 VY5ioD5IFz<002ovPDHLkV1nS={i^@~ literal 0 HcmV?d00001 diff --git a/app/examples/Control/Embedder/.directory b/app/examples/Control/Embedder/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Control/Embedder/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Control/Embedder/.icon.png b/app/examples/Control/Embedder/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b6e995d784509d051e5a749b038edd8d78f83985 GIT binary patch literal 4242 zcmV;D5N+>?P)H3eCIOjBVO z-3*tj0CVnt11%KVgJqQ&JaS(JRplAHbOhcw0geN^n&8@+e75#2q-fJNlu~^8?sC3* z_Z+_Xy+ds6JU*@8`m++C@^5ybJr}K^C_BKn?kVT?Rr$Qu3_pDx%+#5_DcfVvk_7Wh zSi3Bb$L^oUsXm)qzIBwQlQEEi%S(WHU)qV*kQwŸ}f>-9zK>xJ*W1^sbw+%tVo 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 literal 0 HcmV?d00001 diff --git a/app/examples/Control/Embedder/.lang/ca.po b/app/examples/Control/Embedder/.lang/ca.po new file mode 100644 index 00000000..6a79091e --- /dev/null +++ b/app/examples/Control/Embedder/.lang/ca.po @@ -0,0 +1,70 @@ +# Catalan translation of Embedder +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the Embedder package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Embedder\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 17:28+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Embedder" +msgstr "Incrustador" + +#: FMain.class:18 +msgid "Window not found!" +msgstr "Finestra no trobada." + +#: FMain.class:21 +msgid "Several windows found. I take the first one!" +msgstr "Diverses finestres trobades. Agafo la primera." + +#: FMain.class:60 +msgid "Embed error" +msgstr "Error d'Incrustat" + +#: FMain.class:106 +msgid "Desktop application embedder" +msgstr "Incrustador d'aplicació d'escriptori" + +#: FMain.class:120 +msgid "Window title" +msgstr "Títol de la finestra" + +#: FMain.class:125 +msgid "Enter there the title of the window you want to embed." +msgstr "Introduïu el títol de la finestra que voleu incrustar." + +#: FMain.class:132 +msgid "" +"Click on the Embed button to search the window whose title \n" +"is specified in the left field, and to embed it in the \n" +"blue rectangle below." +msgstr "" +"Feu clic al botó Incrusta per cercar la finestra, el títol de la qual \n" +"està especificat al camp de l'esquerra, i per incrustar a dins \n" +"del rectangle blau de sota." + +#: FMain.class:133 +msgid "&Embed" +msgstr "&Incrusta" + +#: FMain.class:140 +msgid "Click on the Discard button to free the application window from its jail." +msgstr "Feu clic al botó Descarta per alliberar la finestra de l'aplicació de la seva presó." + +#: FMain.class:141 +msgid "&Discard" +msgstr "&Descarta" + diff --git a/app/examples/Control/Embedder/.lang/cs.mo b/app/examples/Control/Embedder/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..447acf28c2da65ae8769115d2167a73d3219501a GIT binary patch literal 1373 zcmZXTzmFS56vqb$zYGNg3WO-$N$3hVZ!Qgr9CO5+H^GX3>3#%p-FWxe9`AZ)H9Ko_ zr&RQjXsDoZ>EcQ%q%^*hfRZ}U^I!0twUayGN#l<-^WOJ;=Xw0s%^N=m#M@XOVLirr z7wZ|84|++6H^43MHSiO#58ej92fqV--Fq(!@fNrX`usZh85n{);A7D1EBc&GMe0&sXo}-9M^9R z_GnNz=T8g`EpwS6vkV2&DeTXcj?ElrW=hKCG?6L|{wG+r`Hx)A(njM_a3eAwn;adK zOPMMvr#$0G4Hd_v*49!UI>JU8?~v57H^u$}==?mTGDr(1%aN0M1f2n=W*&$p=ZP~J zUAJDA#%x9Dj@VkW`Ce{n$JV>E>r1a~nc;QAr>3B})b53mKpgUntxPv_$!TZ`9S77R zCzG|%P}$sJ*t{jaUJp+j+{)*@*eRQdzBLnEq}qzBkJ#oIca8du{fCVQAw3EY4qLr$ zrQfR_@J#u`YEwF{(Klh^pxWtGn+^J^SL?JsJ?VABN?YcxdSs=}Q|XMY(f#9go4Soo zNcFJOXtnP(n$3go@Nf^QdQ`{}SC3duYt(LZ9~}FZ^>+JSssdw2S2;DRbXuM8%HQo^ zx3X_AJB@ay8T>f@*4@fdsq|fn#?t2OK0iLXU%hjc=WPwyRzn?`Sm{xX?hKTxi0u\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Embedder" +msgstr "ZapoÅ¡těč" + +#: FMain.class:20 +msgid "Window not found!" +msgstr "Okno nenalezeno!" + +#: FMain.class:23 +msgid "Several windows found. I take the first one!" +msgstr "Nalezeno několik obek. Beru první!" + +#: FMain.class:62 +msgid "Embed error" +msgstr "Chyba zapuÅ¡tění" + +#: FMain.form:15 +msgid "Desktop application embedder" +msgstr "Aplikace zapuÅ¡těna" + +#: FMain.form:29 +msgid "Window title" +msgstr "Titulek okna" + +#: FMain.form:34 +msgid "Enter there the title of the window you want to embed." +msgstr "Zde zadejte název okna, které chcete zapustit." + +#: FMain.form:40 +msgid "" +"Click on the Embed button to search the window whose title \n" +"is specified in the left field, and to embed it in the \n" +"blue rectangle below." +msgstr "" +"Klikněte na tlačítko Zapustit pro vyhledávání okna, jehož název\n" +"je uvedené v levém poli, a zapuÅ¡těno bude do\n" +"modrého obdélníku níže." + +#: FMain.form:41 +msgid "&Embed" +msgstr "Za&pustit" + +#: FMain.form:48 +msgid "Click on the Discard button to free the application window from its jail." +msgstr "Klikněte na ZruÅ¡it a okno aplikace bude uvolněno." + +#: FMain.form:49 +msgid "&Discard" +msgstr "&ZruÅ¡it" diff --git a/app/examples/Control/Embedder/.lang/de.mo b/app/examples/Control/Embedder/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..194858af78bb9502e78c20b99a5290359b8c0c24 GIT binary patch literal 1463 zcmaJ=L66%+6ds@y2wVUmE){yc>;dAmy;M~Qt6CdpiJELy$!-zc#`d#4-N`tb88@`* ziC=(__y=%+AHaoUFL2~uapzy~jh)R_AQ)-%+4J6f-}j!M|9tP-_X6u3>>Jq6u;0ag zfgKjTBE*}(o51V9kAbJaJHT&&-vGnC_g@v_ZQyNS$R7dk0ux{#_zW0$e*y-rUx5O5 ze*?aO$KPKQq6ho~SO8xDe*_*~>-hfyet`GuueaC44d58>9{~rz4EQ1NJ7D1d83_88 z8&vHI{Go;*9oAbNw7!wv`VM!)f?T%_#>!>V=Heh(r<{u;t+ETM4f%>`lpgNFqg&}A zrH%I?Qd41>+YGtc;DYoD=UZj+dW%@SCgmMHl1dN$McB&xgU+$ES=AEmNVZk&prE{F z>M2KV!SrI+f4$#UkUDZ!JnuD1g*W_RK14ZUo= zy_&NXtvh0C>)msEXgu5C&UQt2w(8<#!%ymlw$k{$k%3t8W42Q7`r@dl8quG2@e9<+})Tk=t?N3@Uacb*FC?uNZ(L=Tl?HTum1zz0*as zkLR!bt&Nt-d`ej*tz-Y$`Pn48ca;}x6>Ou#WOc4gIi!23@;z}dd;W`KgE3Vm zMX&JqAdD&-S}7*k6qExov0Y+2VJnDeU+CvQWEJ\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Embedder" +msgstr "-" + +#: FMain.class:20 +msgid "Window not found!" +msgstr "Fenster nicht gefunden!" + +#: FMain.class:23 +msgid "Several windows found. I take the first one!" +msgstr "Mehrere Fenster gefunden. Ich nehme das erste!" + +#: FMain.class:62 +msgid "Embed error" +msgstr "Fehler beim Einbinden" + +#: FMain.form:15 +msgid "Desktop application embedder" +msgstr "Desktop-Anwendungs-Einbinder" + +#: FMain.form:29 +msgid "Window title" +msgstr "Fenstertitel" + +#: FMain.form:34 +msgid "Enter there the title of the window you want to embed." +msgstr "Geben Sie hier den Titel des Fensters ein, das Sie einbinden wollen." + +#: FMain.form:40 +msgid "" +"Click on the Embed button to search the window whose title \n" +"is specified in the left field, and to embed it in the \n" +"blue rectangle below." +msgstr "Klicken Sie auf die Einbetten Schaltfläche, um das Fenster, dessen Titel im linken Feld angegeben ist, zu suchen und es im blauen Rechteck unten einzubetten." + +#: FMain.form:41 +msgid "&Embed" +msgstr "&Einbinden" + +#: FMain.form:48 +msgid "Click on the Discard button to free the application window from its jail." +msgstr "Klicken Sie auf die Lösen Schaltfläche, um das Anwendungsfenster aus der Anbindung zu lösen." + +#: FMain.form:49 +msgid "&Discard" +msgstr "&Lösen" diff --git a/app/examples/Control/Embedder/.lang/es.mo b/app/examples/Control/Embedder/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..310d0320cfb4ec22541b8dc104fe7b521b55ee5d GIT binary patch literal 1492 zcmaKr&u<(x6vquMlnz`ds)SGvFP8{GXWB{#qGcO(m7TULcC*pu2Z95VXU4nh#IdKg zCrhH9_zSr32jswk11Gp4acC}Z<_i1|{0DqJGrI{OVafW;*w62M-}CdIx2}J~Fy6qt zgZUiuP0Zgh!=RTKdktIzUj^R-cfbeW*Wg!RIQQ1ejJ*!t2ZMbV{18mRyWn#$`27wH zv3>#>&i(@4#OJTq8vX`;f&V|iAHnW*#@+@00&juuyuw%n#^6Wb2KWJ3f^UQ0g15n+ zLAYl(FyX2m;atcegbpLb4I}7=d96EW3j^VrvEG+1=eA($>2W3swxwi#L`IXJ3+iT@ z?RNKGwnY)((knrcbmU5r%c&Fv$wrzIQ%_h>#a-gM2%yzG-ln&C# z?qIwf?Tq7zIFVs9?sG3X^jX@Q#G`TC@6pq7XEb>K^YL~X4Y~92p5@vp?v3rx8sSb$=<(u9f3H+r>W?X(bL)hExWD%}-ng_2vZlhusm@Iy^{hi1ne-7` zp9pBx+=fOijkoxE%4eMNx?zPTp(r!{;s+htc4=KVTFEUZB{STHCB^9kin!)fi15ej z4$fsbi4|9&aVy1>|8u{TaOpm)!ZW(+J+Dp;`HSy;r3^)LPNHq%oEK=;T5!&hORGbk zJ*%X!1*a&-YeG6x^g0xYHeYCMPKB1fILFPvF+ZzRFya|MGzp=B>*5r~)~ju9Xfap2 za=DeLqckiHJ$}V~fu+@1HqhRhqB_eFQ?0US3briYGrX=s;O(vyCTvtGFH41Jt(Ajj sq=Wk^xJ&58`M$$*HK9\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FMain.form:50 +msgid "&Discard" +msgstr "&Retornar" + +#: FMain.form:42 +msgid "&Embed" +msgstr "&Empotrar" + +#: FMain.form:49 +msgid "Click on the Discard button to free the application window from its jail." +msgstr "Haga click en el botón Retornar para liberar la ventana de la aplicación de su jaula." + +#: FMain.form:41 +msgid "Click on the Embed button to search the window whose title \nis specified in the left field, and to embed it in the \nblue rectangle below." +msgstr "Haga click en el botón Empotrar para buscar la ventana cuyo título \nha especificado en el campo de la izquierda \ncon el fin de empotrarla en el rectángulo azul de abajo. " + +#: FMain.form:15 +msgid "Desktop application embedder" +msgstr "Empotrador de aplicaciones de escritorio" + +#: FMain.class:60 +msgid "Embed error" +msgstr "Error de empotrado" + +#: .project:1 +msgid "Embedder" +msgstr "Empotrador" + +#: FMain.form:34 +msgid "Enter there the title of the window you want to embed." +msgstr "Introduzca el título de la ventana que desea empotrar." + +#: FMain.class:21 +msgid "Several windows found. I take the first one!" +msgstr "Multiples ventanas encontradas. ¡Usando la primera!" + +#: FMain.class:18 +msgid "Window not found!" +msgstr "¡Ventana no encontrada!" + +#: FMain.form:29 +msgid "Window title" +msgstr "Título de la ventana" + diff --git a/app/examples/Control/Embedder/.lang/nl.mo b/app/examples/Control/Embedder/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..0a30de9a595af2b4a5c3db796a4f2cbfe2ba3597 GIT binary patch literal 1416 zcmZWoO>Y!O5N!ekn2uuJ${jD=(y$x09wvEh$!;hz439R$jm$9E?|A74u zc3gBqh_k>Y;J3h^fm^^n@FnmCFy>wOMu_i#Jz$Jq0p0{|0IvX_10(MnV662P_zCzP z81vqp6arM@J@8N9-@wmxkrj@se^?QAWtD_==hi{*{ol54T1RW%`NC^SqfwdFc1!OeTAf{qbQCIpz>izR_cz)aTTN)fv03`r-fK&KMA&_C~1X0`n9-G-05XokMhU7r{ZP4 zE&~tfTCaaS@AvZFHR|6O^l#G6_FDG4^daAN(s(U{bpv{;wBD!3lJRCM)A-|l4e8pp z1>PkzzELfA&bC}wH(CXO8Fg|5extL8n-4b@lKMq2TeBFKL5ckqA%UNTtF4yG+@-3K z&U3i8v;82yy@-oahV1eUQ`uUX(SUB3Dr91L2QJc`0-MOiZ(6~ZE!D}Ei>qX%iNOs% z4Tr_Mq6g#S%YlLp)Y#C7_n@5(3>Bgu_M95mY>F?)(_oMB?k3(tHe?=Ho^Uig3Ph8# z&PF`9Xr-7PZ0Ms6qaLly1R^ldnvjHlj(7@|x#\n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Embedder" +msgstr "-" + +#: FMain.form:15 +msgid "Desktop application embedder" +msgstr "Desktop applicatie embedder" + +#: FMain.form:29 +msgid "Window title" +msgstr "Venster titel" + +#: FMain.form:34 +msgid "Enter there the title of the window you want to embed." +msgstr "Voer hier de titel in van het venster dat je wilt embedden." + +#: FMain.form:40 +msgid "Click on the Embed button to search the window whose title \nis specified in the left field, and to embed it in the \nblue rectangle below." +msgstr "Klik op de Embed knop om het venster te zoeken wiens titel\ngespecificieerd is in het linker veld en om het te embedden in\n de blauwe rechthoek beneden." + +#: FMain.form:41 +msgid "&Embed" +msgstr "-" + +#: FMain.form:48 +msgid "Click on the Discard button to free the application window from its jail." +msgstr "Klik op de Uitsluiten knop op het applicatievenster uit zijn gevangenis te bevrijden." + +#: FMain.form:49 +msgid "&Discard" +msgstr "&Uitsluiten" + +#: FMain.class:20 +msgid "Window not found!" +msgstr "Venster niet gevonden!" + +#: FMain.class:23 +msgid "Several windows found. I take the first one!" +msgstr "Verschillende vensters gevonden. Ik gebruik het eerste!" + +#: FMain.class:62 +msgid "Embed error" +msgstr "Embed fout" + diff --git a/app/examples/Control/Embedder/.project b/app/examples/Control/Embedder/.project new file mode 100644 index 00000000..e08d456e --- /dev/null +++ b/app/examples/Control/Embedder/.project @@ -0,0 +1,20 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=Embedder +Startup=FMain +Icon=embedder.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.desktop +Environment="GB_GUI=gb.gtk" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Control/Embedder/.src/FMain.class b/app/examples/Control/Embedder/.src/FMain.class new file mode 100644 index 00000000..4fc5b9dd --- /dev/null +++ b/app/examples/Control/Embedder/.src/FMain.class @@ -0,0 +1,89 @@ +' Gambas class file + +Public Sub btnEmbed_Click() + + Dim sTitle As String + Dim aHandle As Integer[] + Dim iHandle As Integer + + sTitle = Trim(txtTitle.Text) + If Not sTitle Then Return + + If Left(sTitle, 2) = "0x" Then + iHandle = Val("&" & Mid$(sTitle, 3)) + Else If Left(sTitle) = "&" Then + iHandle = Val(sTitle) + Else +' aHandle = Desktop.FindWindow(Trim(txtTitle.Text)) + aHandle = Desktop.FindWindow(txtTitle.Text) + If aHandle.Count = 0 Then + Message.Warning(("Window not found!")) + Return + Else If aHandle.Count >= 2 Then + Message(("Several windows found. I take the first one!")) + Endif + iHandle = aHandle[0] + Endif + + Try embEmbedder.Embed(iHandle) + If Error Then Message.Warning(Error.Text) + +End + +Public Sub embEmbedder_Embed() + + btnEmbed.Enabled = False + btnDiscard.Enabled = True + +End + +Public Sub btnDiscard_Click() + + embEmbedder.Discard + embEmbedder_Close + +End + +Public Sub embEmbedder_Close() + + btnEmbed.Enabled = True + btnDiscard.Enabled = False + +End + +Public Sub Form_Open() + + lblID.Text = "&" & Hex$(embEmbedder.Id) + +End + +Public Sub embEmbedder_Error() + + Message.Error(("Embed error")) + +End + + +Public Sub Process_Read() + + Dim sStr As String + + 'READ #LAST, sStr, Lof(LAST) + Line Input #Last, sStr + Print "\t"; sStr + +End + +Public Sub Process_Error(sStr As String) + + Print "\t"; sStr; + If Right(sStr) <> "\n" Then Print + +End + +Public Sub Process_Kill() + + Print "Process_Kill" + Print Last.State;; Last.Value + +End diff --git a/app/examples/Control/Embedder/.src/FMain.form b/app/examples/Control/Embedder/.src/FMain.form new file mode 100644 index 00000000..40465f3d --- /dev/null +++ b/app/examples/Control/Embedder/.src/FMain.form @@ -0,0 +1,54 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(32.7143,25,84,60) + Text = ("Desktop application embedder") + Icon = Picture["embedder.png"] + Arrangement = Arrange.Vertical + { Panel1 Panel + MoveScaled(0,0,82,6) + Arrangement = Arrange.Horizontal + Spacing = True + Margin = True + { Label1 Label + MoveScaled(2,1,16,4) + Font = Font["Bold"] + AutoResize = True + Text = ("Window title") + } + { txtTitle TextBox + MoveScaled(19,1,17,4) + ToolTip = ("Enter there the title of the window you want to embed.") + Expand = True + } + { btnEmbed Button + MoveScaled(37,1,14,4) + ToolTip = ("Click on the Embed button to search the window whose title \nis specified in the left field, and to embed it in the \nblue rectangle below.") + Text = ("&Embed") + Default = True + } + { btnDiscard Button + MoveScaled(52,1,14,4) + Enabled = False + ToolTip = ("Click on the Discard button to free the application window from its jail.") + Text = ("&Discard") + } + { lblID Label + MoveScaled(69,1,11,4) + Visible = False + Alignment = Align.Center + Border = Border.Sunken + } + } + { Panel2 Panel + MoveScaled(3,8,67,43) + Background = &H9FCFFF& + Expand = True + Arrangement = Arrange.Fill + Padding = 4 + { embEmbedder Embedder + MoveScaled(8,8,35,25) + Expand = True + } + } +} diff --git a/app/examples/Control/Embedder/embedder.png b/app/examples/Control/Embedder/embedder.png new file mode 100644 index 0000000000000000000000000000000000000000..fa90352044e39b15665b1fdc476fb55faa601013 GIT binary patch literal 2623 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 literal 0 HcmV?d00001 diff --git a/app/examples/Control/HighlightEditor/.directory b/app/examples/Control/HighlightEditor/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Control/HighlightEditor/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Control/HighlightEditor/.hidden/screenshots/2014-12-17.png b/app/examples/Control/HighlightEditor/.hidden/screenshots/2014-12-17.png new file mode 100644 index 0000000000000000000000000000000000000000..9db951a333c6c49e43dce7b34f41de25afaed402 GIT binary patch literal 58870 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 @|Wx1m7{5P?P@x84BZZm;c2)?VO0dj={x zp#60v-vik8Z#oTs5cfRmAAlgv^~bL=rYR@8)qKiZdykyxPtvcGmvwFh!EGs3RL_vl zgZT_9-TI3A*F{;HuUlxA!xL|ZG(KDb;xCxEtozyTFBi|aOLU%twYw)~A4%>LE zZV_+D5#iG&<}hZ;g0{V=|j=5d#2a+N9wRo0yh-JNi~dr*4&8Ew?j7r9W^`c)1; zuJnRO*g;L@@wpLm++^12-oW?ZB%S+FmVL-Ndqw;lg)i zVJjlx+g#weF5ZU9SaU)131mlatA`eCLpNrx&uTvEpg9cl?ldK;6zd(V`26>}+7-ZE zOzT$6HZSAF#AvrBpOSwCAk4d&P(JD^O(j3DAJqu|Ca|N?>w9JZ@mZl!Uc8yqPm@iP zr9Prs{BE*+$twwr7~*iTT7bsg^lmBOO%Kp3@`0|ce&1sAZZdo5vVI4}` zehP!UZ z7~lqh+|UD7#mHScyLxwb#&bF(kW?A{5E&D_5`Wjbj0d2|`k`I4>FX-ieXD8I9Kfjc?jnr*SS_@gNG&imHV632o0ln&SGF zQLvXLiip@PXBVp$Jn}6|siW3Zm=}YZqjr4xxPY!Gb8k!%y3+0;H4-(UzQmad>Kfc!6kU##!VY ziZNTSHuur9X^Q*s*ah#5rhxag*^L~^T7?+*D#V8`AoD`+-L3tym)Kh!Y2C=>8t$x~ zL?MRe>cT}HxPmN!PM%9W{-0}|7H1mpDCo~DzV!|a7~ftVeJA3MVbpIQthUkTa@z0R zohlD@RxVT*9?6m_t*mV8>iUYS?G}D|Y7aABtS!^va%M7K@Pdzzk5;o9lY)W*oRKnG z^FEtjRf;sz)yj=bD%JJ$^a4Xe+4DV|rB|8UF0E&0XGqlKA1oit%Vv@P|IWn28MZOsCq+E z5_Ke7hW)<5eoKCP0R=&?|8n!pr9G4+G&`GuD>=kC#W`#6^7rrGhu1sBC%>^}bgZmo zz+w)yD{pr=NNYjNz-d2CK)}f*Bv@}x3{aLlV6%{#6h(~fXjmq(Q zqE$>>@gk?kqR3K(5(Vi8h7VL|Hp~ZvMO_mxUTH6L?AI1O@Q%Ef+M>lfdkf~n`XU|^ z3<&L5r8o0jo-lo$Y1Zc>mHOh|y6T3l-s71{fDCsa?7R1Z_8<{p7sOA#Hjc zg-Du6zp>{tmQC7CFl@I}8GQIx&((J2&SVd`O|Jv+^^|()q(n?i>^nZ|Hy=cdG7ud@ zNcj7!ZC1W-sT6A&F17f9r((r|Rr-B$QdK&ML(ts3fN=o2#B=0+OhP>Qfw$auJjRyY zYg}5@CaAUXdS4=^$1R5yKnPsir&i>hwnx8KS99!x9%Iu_vB=3rcWwf9O9L?NU_oVv z9fyU53EZ6TnNF6F-CP_P?@d=`ei|6PluYJ|KHHfnw_OuI-Uq!k_3Kw)r<^67Og)gAMoKQGx6tT*v7YNoE_Ms{Q#sML~P6xmTsi;JqTim#EtPh7ADHD`P6! zbSFnN7|;s>$A6KvLejS66~1%7lfG|$&ppb<1aja0^Y)z@!b?bSmaSHr$!4RkZnWM7 zRdm)pE&|O$0vT2HjCq?GU&} zkAT*WY-niccSnapmE}ioADl0S+qp%kd zK|pya6GcSZRKC1e?`L=uw~kvMRvXL zj#M~v`20thIy&53MqHB4e~MJ};u~z!n!#1BPP1UnS-g2jMd}-V9CBm9?uG%=|6%8H zUzB8NYKq)cqT32xwJr_BTPZI#08`9Da4X}a@AOLQVKg`#4 zKDqVu_@UrF2~G_S#Q-r51(*IA4Zmz2UvE4!Vx@YpdSQEWbGoFh$4MXi<@GfdHnxI^ zr)mF@%!NRY`eB?^Url<<1-|v$#$G9u$0FdMEvX9FH2J*CXfm&PuaORe5|npfohpkr zA;i|oTlL(f(6?`clm@bGNcDB7k96(h6rr={P2LV%2o4HY1ZSI>eIxS6D6{aN*tK!?CpF4-)c;*-uEGvEAt8p!RjMEFh#yVd+E zNXP0g=3T36YadxJ|Nbf;y3!R%fpVvut2mmc)U$h?oRoyeVuI;|fF_hZQmWgE0DhTb zmOcn<5G-KfP+raq)8F4u$nE$&Fc1l5d3o8|+PY~-&SQa=oZMFl==sFt!;4Y2?Q4Z( zmf>R+B<1pz>C7!Gz@w|Phjz7|?-FHY1XnseU8H7F|Or# zlH#e06+0*xMo)%>ZDW|yELgolPAf$;%p4=EcD`_M0Q-Y=)uRA+^s4SKa(vKtG z`)>FId4q?3N4}9-_QBxO=}+P$ha z1Wm4tjIxfT0*479O-%wGS8KTP3JMZ9^A6zpx0frS(0@rh&Jj;vi9pk`9}q#Q#St0) zHRwq-)RF$vj^{PvbD{{V`nHC~OPK})q{ldd(e7kNE$|#}s>)dSEIC>bp8(Tc*dEsw z+>fN&DGCw)AXAh3LCMs%>UTBAT#K$6O9J)gwThD7Ly3mQlnQGH%*D)_QeMqwX?;~C z$MbNDm&#@OL;lQ%qZnl>wvB=wwJ)aMJBr|+aD1M^nM+mCa`xl2TR&WQxR1@rq2#+- z#Ww0s5>r*h`&5|#4DT78j}6@1-Cyvzaah$KiR5AJA6 zFrLZq2^kp~NXfoFr&B)!seNqT9eAX|aHa&Vp1!_XgNsAD6`^DjM>q&6!=?HkpebfZ z$oh{^KBla?nwnC>p66hL1O^5!c2T5)SnYW>refpd)X~uq03OsbQs;Ca-^J!}``I6j zpuIEv6*4;ddNBJu>D#v@iVhqc8~|BQJp@4sTnl)}@LQZ;&KrY;aSH~*!O59Evi1E2 ztS&>S+Z*pWo=e00CP694K4G8<^Y5?N&LHX{V_;j8~Q~93ec2}%|UW=(B($eS=2jSMlh*sE}y^3#6s8yi+y#kdmy?gJ21>7D zUPTD7%qx(@r=(k>CFeXcT^PYY?wj^K12cG!;q z5n?}xe@rxnepM%|9FO@@u*p^8nqy3v&`~o9kbiad`oJLKy+DJ*@7~InZuz}ueg2D( z=k?!ANIJvEBbaoXK218xh1@^iy(V4k{x2RR!X)y4=C=C-Fm^I9FtENp-4ZI@O#~`z zkXeaJNYL0d{I|M*H_*W&RjqWeMdla&Gt~InJFH9YrsaJd3)r3hrhZpGBn7ALp#gv2 z)-2fLs);`(!RqNx$xZA>{Ad2baI4k@z?>s|3}${7b>pC^j!s9&Y^n;dG^K$%r6=Uq zQ{o%^`Dxk@hLO}a0)LEZblybYUjbeYxd1xrOuh;ODuNGVy(KWBc67`{p@~U&b%XxL zoWz#){I7J^mC|KLTV-jI&V)pEG680=lgPzS;Nsev`J?0W?=rE42+WS}9%MiBs-xH8 zFTvNETGPS~+b(7Xwm1ZPpz_Tk;O+}K^4;Pg-r=__ieRpi)4aS{S*u6WTn+s6hu5fT z4b#Z|lICr(NLRB?#qy7F=<>)B$=hX%x_H=|b8=Huy1qObkc<#J;3dN(6_n2n&k+!y z%C9E%9)4zsbD5!s+3ZbYi1YhnbJ?MSw;8Mo*Aq%%SUF5pQWGJh*A>syk;r&|_8)}8 zA-@x1@*=}tb3UnMFj}9r?QdYWOXb{{b?xppnXl&rDP2@-?82$b^Vdy3NfsMnK9_T! zT3V>SvM#KtOVdE9rbt_dpXh*{wHPyF*Eln4tVWf$8{MhC`&6qeU5bx*Z-D;W(zhXd zta=+pdR$gLz1@HzUvcNWOvQ9bE6loa{oMFemK9~>S97nnqC$WV`aYUT!H<`|m_noW zGd^$_7#J}0TJ@@9$bbn*hhU1vNKV+lo_`M`#yu>EaLb)&kc9*g8oI{o#;&h?AO9IY+)gAlX=?mTb!hQ{+V4Yg6tj?A9JDGw#r zFg2u$DqqHAQhEPS93doKqpF4huZ9L*r#S~)P(0;bn}ekOF+BXz>gwp?ne%jWFXG}Z zdECecr8WNg#~J)ePOmj76;Ir&H7XCBOc9FM+KVB6LDy#K~&kCuB3^HiE%q@My$Wy&abMF zqM`d9s5oM^u{cL$O^G7C=G~9QcOzAK!+v@$jDjZFh|70PGO6BNV%=oMW=eiUcGQ1uhF!R@yMqDLi62I;0E@r&z}QNxG9Ix&X6C@whhC0_RF3PKFN# zEs^HkeM9ngkzc<@-kY@a`Azh-=|K zjdQ=J`8ucS4m`4r=rUQ8W3IXg00#|K2?G;AzO~vO;HQ^Vg859 z-9pZ|&WUL2h0OcBEov0B+aK8ppGK-!lRh7{J0Di~kfhY_34@zXW*NjkCuL`hGU|8l z72}V=iv^u~n|u3a!7tdo^$W8#;96w&)%%@tFaG>)eJn!o2F#W(j}f8od`sHw1$_sq zs5L2Ayhw0=e_71Zu(!vjf&~Cv$F_kX2xew0BhW7xvX2AzHRI9l9-w3=O5lRez8}$z zNPtsOVO`r;(5!`iTLSu)-GBYM|8TW8b(iGN$%v&xZ|M*;&C}|!+%}qg1P@)*z3Ls=03Nz|0p(cCy7J(zM+#l-m6 zlLB83Ke)Ym&xG~Uqmr_>k|$*W^M$0ZNE$Cc-VH-~h}5g`v_m9fm&)(*&PmHL_pOli zxmanNpv%rOKm-|`nud%D+1#9z<8pj_%@mQD z_82A%VJ*yaeCtZ3JMa+ju{O?f*|E3ehIt|1=z&pN(I@0%@kMz?j?Km2ncL`JRd}Bw zTbTnmZF3pp>Aa5QY_)jTElr^B$C&pV-RWr6a7&;l>zyO|{2XjR*`i{R_`@AxfuyoK%={e=*Oee5 zNy#r?Iif`fq9V4iI=$o_GS`g|qP7ysTXIc!RPNz)g_>=JMeC2{ZLchJcb&pg^2dqx z4K*7*`QZHw-IJQ$E{lW{Vl|?9_!gxxI9``qL69v zLy?t#q^!KShX!~k7~bU2OSkKnmE!E~jYVRGk`x=hiWw}+e1b=>?XTaVsDp`TvcHT1 zV#=}$G%|l2i%QjYbrBFaGozqj)wV3E38II7#Wagcs?V}sXTP%GpV1|xh`0(Z$1{gT zWN6)7<`^MBV>ke?&biax8862emQGC%FW8-q{v2BkA}HvzbRe!uMMqmN`~LYY z?-dMUHV8M*K0(jC_apjc8ZUx^#vbQwr4>ld&8sSBrPwX*qjM!EY>ZN!X@Qx-lnNE| z2G1}N70vNTjmx<|7#8L|&B+krok^=ughxtA877khZ!^i0#3HP&7Le=>9(nh!@1rDi zY3-wu}lbng6{6UBXg!BgdA&MwqNJp=mH3C|-(2^AG{Y@VrC)KU-24s}3<<=J{Gn??Le58p>Rx_+H9@q{nXY9Kw+;(W z7cq{~O#o9Dn04?A5wWc;ECpRqaGgF6`myi-4|8uF7ggV{jnZ9;bcv{-boU^lAc`Q; z4II_bTc?OPnU+JFAv&L_HlCA9(K0(Geu1RB z$#8_g(IR1$8$jU$}1_xO>TznU3t(*w{fA6m|XMipaLHA(2&3FSn#;tcql!udrNGQ(M2OCCb(z&^TFSrxDNr?!sQ-ZX&-;PA<6~vt{hc{@8AICl zfywu3A?mz7Q_e5BveQS*@ec@$2PI>usrjOrB2kT}#ES!&tS63K4LV1rLwnENC%kyG zHg3pP(&5v(J8dL$gc>|h*1A@xtIZ4R0&ULWjRT0$%rB1{JRKanDA5L?$k?4UFA^O$ zH~o%IjP!!sP~ye*n0iYJMCa3oI3!=@Q0GuhJ$|By>hE79cpO0^q*M?RKhPdyWBF6g)=~=F>UiI;0C(_sL+4+w5f`>nfLAL@% zBh_cv*2izYHJY@>JLGX|A0O>qSD43AyT&-wRSH4^X?1;DCl|X=@G}ItN7Ri zJ3~K10cOS*@DMkntpB~*$M2ydETEc0$$ir6`Z~?i$0k7?m->*6Ns2VrttWF8Vc@gw z=MYPhqv9??U=-|}s(p<6b{Rw^a|FS^1i-<$VavagEE ziiJ^C>#J^aBj>{9xfr zv^=_2Q#HMl+Vw#{V8c>t-q>xnJbZPu7lFSn6zE=$F;PJ5Kc2GKfX2n)~S$XUY1 z@&t?3oHbFQOHDOHw34l1Aq+hXEdl#$uIjD@lC_oVr@f~hILVPVubkTlIH2C5qTfZm z5mPzV;uXYx@j?x)dz*{(qrh+bUBOsxl7!$XA)12Y1=@Agxd9_`on61i9z7)hGx@Hf z0OIs}`*#P&4fWLN^0zCbjK5~%4YI#I#OAgI1$;vrGz}-6mjfH%fX7Z>!eSRS#`buZ zci78#V)VnjGNfBVNPU02YgZ@xNrUlXXU}GFHn`U}MZW7f3uqSd;{;7s+R9JqPK2=P zur+MpC_SVorB}Fpv3=ZaP9yO3afcHKlP1m%$}9P15hfthk^RUZ&>& zEQe<^R@?SmpNdXpw8!pEoot1#rIC{`nQ%$m{NC7;xty-kJ@aCkyVdu++eem8`lR4X zi<)V&Gy)SddV=hlT3=x}?NI^hgfJMffrdUl$22L^W^nV%)j^^-Vt(6&gO}AoSvw{&q(MM9(Cf|J>g%57QQ7gYB>A$MB(+il8UIq8W!g#9kTfw4|Jj` z$2T@h$0mlFsxTMy7gS$JE6S6gE{@WONSS)W^z^^fI5~-(xip_Kn323@oDa&STHPE= zY#wZ;_1JefWbPLw^E0vyzRZ7EQ$eaTfhFKJX3jJi3@Im+RS8Gsu0NH6U<4Fss z5}Uh!}*EM@K5IHnY?lzz<)MXm2>Z0d^FVDWRQ zmMd9ZSYMabH`-!Wp3K_h++ ze(K-Pn#yB2n>@nS^rp(~3V*Y429=rFaf$pf%bsJ*_iXpq37SW2IDrjp_g+)H$&auk zKyxwb<5sS~`fieU5B=s3;)@i^A{jIMIPH?k7egas6tOQ%UvCvB7t-|#ff~GZ`g?4g z>D3A@Xip{5o@};h9;ePeGfG5!u>a=zx%~5C=dp{XB`1izv`seGuDgJzmW8OrjgORM zRZ@}FNC%v$?t8sW53QtG=M+~5#(u;EjbdzZs^5)WUQziN8|3!r(TJJ;!h&vZ$HIQr zv+?R$b)Ri23bhR}j^gkG8zUQ=-P-z8l9z3@vB-Fx4_2!7^NaHVb%kCdr7%zLgJ%aF z_dA2y%{E0)mIvSfneDIr@()RjX8fApQu%)Gm40p!gUnDKzM^p@}ljn{;m%&=Y_Kg<@?lY_6t(tYqy;;ay zIMa4pLR|rfzTGKQMCSS1F2wr58^!T)~!*trluo$`uXOdWF6NhBAgw9Zh z=^LC-7U{LOuD(^+;3a0YYFWK{w1m&`z($w6s@8iB8HZ|w=k{MDbPMa%}@X!QEi;kp?Cn9zA2S+XnDlwpU82x0lX$L@OOw z48UVN%KK9Ey%NMj<$T?A*v)$#abByYf2@D?unfN-f}J7>tvPI}yO3Pk)v9CZs;lg| z!}+cY`rvSe%`#+$ijJuDjni-d`+(u<@(lgg(Bnj47h9QZ1=5vFVO45cn^D`4-% zsp~q{18@rqadF{Xb5T*7yUf3z6J*(vH>~mUYq0#^o=;4YCh7| zr}b=rwFM0(ES^95MP6TeM9k;Nzxs172yX{3!07#G5wW(ksEj@Bxubo%M=`03-uW*4)^@y=|rc=rMw5X$s%GxnvLyh4E_ia{TcjVEM z-AM@3;POI_^H0m1t}*H`AM=a)Zd|`7aDr$jzbBEAyu(+*X#g0TySYqUxx&K>AvmyWhY(5 zgEj6BXNz=>uPBJoCj2^OKyHhFh~sEtm-ti&MTr|lNkuNjo`-FS`|c4^8ZNHCgR0($ zcXE7!T%u|YA>!49I~^>Wb8?E7TT<9uA~b;e-}X$A9E~vI_h-5&7G>O9B+7B=!%N!` zEW~J|>0>S!RqhiwTj)vW;>~1nj%N&~dj=XqQZB?ss(-MiD!n$@f65_1**ysf0}m*R zof$^DXLMyKABy1r(1&`7>y(`S8~(Q=)Wej{k1viV%$$9YE(O*$#9Y2r zp5b=RB$HZNMvLp69cjL|_TRFC9N;YoS5XuC6TLI`j-I8eK8SJa$=uxVCI5+=F4+4C z+nVB$w~tOE#m67nAtxU}>DBrv<{+r)ML+xF(b3@%U@$Qa{UO3wUtA1%xJ>0?93@8wp{qM!*@@j?l_=Y8MXf=&?i?Njjjo_D_} z1Z_7@ssA=#KY8Ng0pI#WH|0@EfOLPBf*KSeavyZD3XfcSM7NIXGnWdF_<$ zWyNcXI_JvUnbeOQOm)qM^(fgce~;k4UdKzBk`<{4OQE(OL(7aRPKjl>`Yd}`g~)$i z^H0l6YZ9uhTJ5zngBl-Yj9}Gb@nv*jU0%PSiupBn<8!K4VKky2Bo9`G_Ak$gZRHC#sQav@;=<%2ReQXST$|$uha7768eHs<2_oz4geRqq>+D3*?!2Vl`=QIGfAs#yE?Wj znx53gQ$HuPQoUDiZSO{z6h?^lHFfSgLM&&J^vlf1FL7a@gyduT*oM4=EusiMXo-RHbbm z=#){?z^xtC^R~@=exLTa*Qims-llA&BnrTk{io8?KN!Ns+ivW?mZY_lJOvy*L?6io zR8%ZTBql~D(y!tn>2B|_aKZ%XKZ@zmReCjT!of#Hpsm` z_I_%XxwL@j(DR9sMg?w0#tTh6xu~SN)vI6T_SleuAm97W;m7yIeE%E8;16dpqk=&%fVbzh=^jJ9_eDJ?NkNj7nc7#^IuE;+Kux>T^>{z(T zf4;#8d4EhQs?fJJ4z;qGwt7Sad~Oa`=R=w4>-d#6`TrJO+tL&Z6!n^LSg(spUP%FD&y_N9aPQoSP9dJ1UFNF1qHb-!CMtu*$M z&@=StSzrC3=_i@ExDd!P*SD*Gu>R`9JdkGz2-2%H8@TVk+$nC*<7LI=GEhenUtN_0 z&WXR&+y2ghV_RDfZ#btBC>lE zyU@9&zz8xZ&bN2zI~wU;gcmE#Nh~jvwIorUulu`SsN_cxB~x7Q4H(hkF(K6D(nwp1 z^*fs4ab@__E6k9b7n!BtLy8%ILj$l>J$*%^)S zFB{ny)le-tiV#j%+~~44GmC@lhe{(d)bcgmPiDFu?dSD`g-0O=L|J2>$R2BHU7Tcu zxKFhOSNEpWIR6Z6s@%|#Y6Og@Vaw^>1&_t`o!VNysfonX>T1uf3O4lMLjP)a7e;*M z)|6aj1bualv}gz&*u~EgZk)cPb>+El*nA+oFbkIV>qKp?yBf{4Nf*CShc{1CHz$7@3SuE@Wt|&Dm zLs8nl)D=GB@lH+?5G!@H$z!cD5yXs}MCB{&%#-c!Z4-%VEV8Ou zR3;|ZB#khPfqU>Pr_K)v_h?R)Y}G6~&$T-Q{QNWxW~3+2Qxz`!v2mt~?2sCFfXOe|(njv~+f4-v^yvdTuX5c!&qiWXtUyf+Ku_m!SV(^Tp20 z^st#QOEDJZb_l7fjL|)33!E+->9m{n!(R=~yFM6dVuf4dO52uf-Pp8hj9QkHG1R~T z0`B|S-7TZlRl*F$JgqMfrk!(YC2=KL*~r(rD-LLf#Oqdv$L+4PR=d7GCCL*3GF8-l z2~SsA`qbRs6!K)}$>)pA(ETXB1bES5#)8b+pJdL|(Z7;8vjtm9g19$qA4}P?5T6bp3m?&aBrVT1P@q;-J`4_&6a%^8mUwR(_XCfi+z(1ah%4d zwJz>*vOgJUIu-=2@+?WiwNPp#v$NAslZMLe?do!CtytI0{UGV^$x2?9} zCOc~lH{!mARmW7QsFgK?r6jH2+xOO&HO}}Jo*^o?yXO0w0Nff)-nQL{EGu?W<7}zm zD5o@!C{S>;8_Hgo0MU!e|q}JsT6rur)0|lu2V2HMji^A+)&9F?(wh2gtKX4EQGW=|^c)>%w zy%2|~fPi-dB2GAy75UpS{dp9(Z|8#LcC?i+<^_8pCd$?pmAPi09wP<0RYttZv5&I( zUrA^SIA|*)>X3~yB!i9%8<>VR_|irF(@U;9oLT>#BPixtRrXc>lw|+Tjs)70_e-t5 z_Ds#yWb>8f$=C7&3k@^GarxG7&S2{p%<^lJ*-mzi4M)q$Ru=B-x;;~B{xbo4?Ejn)YD4u}%`X_Kc3Mp5@jLG4F&q2esZ2Q@+L zI3XdBjc+chaiS@IjSqNN2m3R}Y5#x-ZB!xR?8c3_rtVZ8iR%~ic2h;yABNuT0-Cfh z%<^vaeO_hcTo&Q@_yf&^bu;@TCo{9^?8Q^gjVnio20ZOgyGGN1w&dpOgN)7%Lu?h&#N)q{cy)R zG{U57qj>QK*#l>54zK0DVr|&Ybd5K^<89Rbe)Cr?{^k&pB?a$#?& zeN3`SpMw)%sX$UjpBJp+S%glqw&<9onjmq$( zABd$>dz5Qo0a@f-j?ebE|FnegJhc9hHTTsy2}R8`4Me&-FrtS zm1X?Tm^@lpC%hPsnng(B$k0@E-(JoOVqbj~s3?zdpUb&d{p(Zk8 z*7EskEW)$h(LLGSV~87}XBZEcTe!c@@8I=(n$LP}FF~ijK``{{xBpO`TU_ME;m~{v zZevfIeZy9LMi4((&^*8%AnucfVOwb*&w464oL1G{tl-JH9fU*71UY49XVj4nH47au z>AK(f>d82nIt2FNh-FmeYw>McD7}uNE&5F^NMuC25MTBXwQKlfQ#d0(-U)$R6m0eo z_(#-^+^T)o%c_B^(hXyNbtR;CmXTdt{L=-}>KCu?!o%iZBBSBsE#2N8lk);CHH#^; zo}WPP-Wz{M5k6Z-MQ3KF9zm@ftN$_f@nIl-_zXNPr_!4FU|zaXA@OA_Y%MFEt-Jg6 zVXEY`um>Mt|ER6&U`GbA<<>NDqoeiio7AY2^Bs3;QEkoD`6eKGdd#P|*@`F)4rWOl z?m!PZg9tTOopF|;qQVw`jTTDOSdjOD7J~+ExPr;!pn(nH$7goV7oRUi=9&*S7E)1- zUCF>lqtQ#x+?o~yF7owPHifdL-9P;14M$iP)}ub|JC4-%C+t5pT<|?L6uuGnFy6Gp zn(PoCLoNkT0q4LEe%$!OmFySJlwuPM=fd1q0L#^+F@un7jd+eRI1)y-o)KJ7)YpCv zX&dT|dHg%_jgs+kh-hX``)AbMJeUeJK!29zV?eLq#-a*I}1=Q2YZMiVYxA zqUBmgT9Uh;Kr1=JE&=cvW{wC zvmeU9MC;|$rf$$ziABk_bGKrS5(hY8@9sg=0(MO>oNY+@9!sB48ahqE)dI zePg-FVPEVvU_>Mg3hB=b8`icfw?)pyG)DMzyB=D-8U4}8b|Pexr$1F&;{diKqB*`) zxyQ*ie_%Uf$q^Hi^e&*7dxf0*%whkv;?9gt#iyZI5)O~GHPyISH9dFq`sS8=uE>8t z*4Jn0()9XeIDR0;&3o8sWh|;Uv>>~(s;Nfex63S}S#HlvDCYXbq%U=P1TMw->Q%UQ z@x3ksDkj{70hy74=TkWR&^JFF?faV=pu4a~4?|@MNIrc^e?~QeY8<@o=s3)6&Ys`K zy&G*Wl1xhYbK$Gq0&JNlo5h(FYs%_4SY3rdKvS?p=5)q)-WJ!QX7>mkxJYkmm~Rh? zm~6toNC86%$0j`b(gsnsy(;!fxa~81u#J3)>ibvxoYUo9;ex07-}93VOf=fKvR%`j zil@1{WZNlC^#a3@)r{?qdPCx@vDgXDv<|3vjSJ419jVt7%2_YTE;$$EBXH28(CXzp_yd129!fyve{=azC@(65@OF1=#-NN2!BK)dDU`sDuk z_(xAzyH9Sx92S?z!PBQ?;*$Q}j z?PIwVej3SSeEe#Sh6)sQUw;Qa3_X=JQ?Yio{6pd&r!N6Id!eOowQ7MceoFQ0 zapqD8EisXGz9k9Y2j))FhZ0TWKZ8AjO4*FAS$J5i9wD^bmqxW#O9r)--l&2%j01*9OAufiODKqJ0_#Aad=ElzV%a)Uv)Uw%_DUl}Uubv90> zu#I~bM!8B)|6~T1vYR1e^@Zw_xSXx!{TY2IgSe6s8t|29WQk*ArB{5r1vEQ)Z*Mdr zv0o!qzQ&@6^F{Y(cM6li6a{2M`Cl$Q#G)~nDLixlJ z76viBwSf8lM%cBe;KoLi7y9@uEULRk1r90gyk=wclqeW=guV=2lu!YrJV|a6@(^4L z=>3-E3k5CKqBTJYjnop>lDj1bjU#QbLuWanu}%bqvR$m_5MI2Sl&#$JKo1r} zxVBC3O{vil}0Ab+lJi;zXVX|-bBypYk=8HP|4o$L)@p~?sKQiVzS?}FDP8#`!gD_)>K zvB1U>lSvQTJKOtq^6DTa*~4Km3{2LK&Aw*F*sA zKLR+#zP;YP-sU3}nZW(ZzSJQ@C(PDbX-2=|6A(X~+-S7iSX{%;p{?H`iC%)VTk8GICC%Vd<;?RH^-|R+%Rdx&MuPBU_Ae zjYCaD0yFYOjRouyVTJTV52i}_L+#*kT6um&o>s8kyC+>d7L$+=3W8d+5!r33bPf7S z($eTgMn<4VL%mr4xec#hc;SC5J@b#O&c86OKnVc+p4;2d(8k{0ND#-}AJwr_*3v?9 zpgKFBUZt5p-HW}8i%D}JzN#)iq~VG;P9lksfOzyI30{@WfT9m4klDH_Y>ADfC~i2W zMPu^bMNjiZ+yl|j?_iHTvrO>(%1S#(UBBAi|84&k19bOQ)5=jT2vvhdk&qqOdC`55 z(6BIL5UrOuoew~(-|u7xois0icmnuu{8~;Q5=Tczdp9@S^pcWj(4IlYz@Qh)$iUE3 zYTW*#uTR+R?6nIiVQHLV`H)XLPBt_qQ0@QvE2#0n0}ydVSbg( z71Me?nzj7G%%=GD6gogp|DCMG6VEgDi%Qk`90&ntWK-vrkTUOA+t=we3b zZ``=C+b=^3TB1m2y$^cw`XnVJ(5l_{ZaFP?qZ|GX@m0M7)kvn#uVjTwYlW9i_MjC4 zhlu(?zItJTuwANj(!bB%z5eS2eHC@zEN!p0U%&w8h6^;Opn>)eP&k234Wj5*JuP~5 zu2(0Q6msgPVc;_|ZSp%_#c3-l!p6UE+}s@Guz!mx3l}vTcqgb5){X&hDcNX zPlh-=RaNyqI3JAw|M2iSON(hx;DO;0rNRV8eAkGX#~nkYP`Dm$&Ot=iQ0kCZ=%tcC zZI!b~!(uDaV74!+9V%cs0esBY_8hQ;?4}Hbhaz)>lsTSh%UV3Xhn7W=6|OH#8$ZFN z3AcIPc#d9wHkACSN6JdI_TI-|%io+E(M%_%4m_%CZ+t4$uL6WYpA|uO4-;fJA zhhR@zFK{XCI^{~)B?cuPYEKgq(?Yy=Wz;2rVB1HQ*%b$D&VZFK@b2l_}; z8YIhMsXNCnR>PN_th>?Z5s>#lbV~>azjAZ!y8g zQCR4)H_m&xb27JQJ~g^(ygLQGl(`!3CaR*|eYidf-}N6j6E-~;o`fr3uIu{vN=_dz zxr6Nnz5cawO4lAh&wOu^GABWXr}8#MNUsadVX%D^l#p3i?;* zjkq$!--q~g;KCelv(rz8>gFIFOp=Jfhj~~=aBO&G(T8c#Vx{B9kN4pl5+r^wX`hJM z%W+^ToK>nfyQKn6>U{fbo$B!GCzVr?Q?fM%`H#>W%9qMG2V=1i0dKYxq?n4Pwx)3% zBocxphKs>ty)~KFr*pEVVmvz^!g%Y|sXdw)Yq9;Ht2+_xqXA4|@S6#dK^4i)k05rzx0sm?6evpqw;n~MJIA3;XC7$=qB5j|>Kh;L6) z{gv--s?u#&V-7E+ovacCz1Eb1l)NO6+aqNLY=MR_yfYr(V6e)?04IdxZvUNyUr`y=g4|;O*NB z5f8qclkGz7-g^(W^6L+s?{{_X5)tNW_p(dW-xuLAMQWTqe(b=~g-iT%H%Sn!d~GZ$ zLqp$SiH24iDRvvn$K(zuHFkgeXgCpNHBtfANr*ItGv+N@O$sHP5#ERo0GaiMnxUtF zTz>WKN-`#)e<3X$M!C#2y2XE$R&VilcVE}3E*1@6!<)fuI! zr__mi82EGS+5b{W*!VxEBzUP+TvQPBrmzSL#@ZIcBU5r%(SXk{e=r3DqhDL=z0>|} z5(oqhhvE8$4!o2eiF7DnK{jW|;1ml|QdXCnE8AV#sXY~{uc3g67Ffa%mB9|mz&Xwl zE{PsQ26YG6?L$mP0g-lve|}1Kg*K45i`nlFb4GsGuq^~Gf`h3ky0PHd8(J1dkG>@N zisSBVzo0rO3(9k4zDMr*MJ9*chm78tSO=BK%wtdUQKNYmkHVYU3h0g96M`Y%;oP#B zWBqNJ#QCI!%kL|r`s>u}(0~zd%V*LF80P@f#(b9thg63Y?E15>a9DD_v`}37^e}&5 zzXPU3gb@nH7hrXK8wq2$D%9WTD8#D61z&1fcC{fXlWkj{OH1ZwJ7OjT9Bu*Sz+LVU z$Z+w%eJ(|#ign+ngy0Gl6@9u`W4#5IuylX9{d4hWo+SaWbmKAc1?qvNVdfpz9Tii4 z&hIZ(>%H{^=kF`MUR7iOSm8uN*%o(C1&=|3Iwg|@m?>SBQR+x<0)j3>QzdFfeBa2b z`lo=a?&pMNt{&xK-fFzGN<}lbA=Y5JAOCxTD*AJR8WFk}__rg|Ws-Q$|9?lO5nZ1D z;mGtf#4OclY`W&tvnT0~Pqrn%B2n2DlsZVuJ_p=+@}D*^L-zsAGE-uJTlw8)fBP)L zK76+~1s-BXwgOf5O* z#8PPum}2c;o6>1xS~x=TjN%M5Q5af}wU%ce1K6za<946!^FNaQ@2`^ny!75*Va^Ut zx9XYesI}{awp|IcZ`jvd@Ky5L^fVCKvK2X*)xwTxfitf161gK0|M4(H8xG87F~G4> zz_ELTjf+cx_T@`1FL{zM=r@@Dbfqa={iO~d^)rb1!KFmR^R<7&Paz0WG5!NF>V!dI z9BJ}%~ak=n#)!vVkON?x;?Vn)nOqYei8JUkSbiq4TDGr+hK2A zgaF(S?=6BI>l8gihXtSL`mDKOG%yO$2e?6l42Ciy=)x^Y|HWOk=Cu7;D-47 zCapV_?AH$b%?iQ{u#6JAuugRs`33!YpN^@~-3H2&TiyKOq)Zpl>Rg4IUe1Mo@%NMG zRSb*#eHD^MwL(LBHq!rirn#T)u{O!tllVS+r$;%$>Hrg9`+x8+Kv9YsC zm>*-)F*CMtLU|5u7S3ps&(qthm?-=|qpcqa&sSI2>Ky?&fE_@kA6-8hIoW>XF8R9w73OTv`D3=?l2x@vj8ns0WgeZ2$3C5ij z^hAoX{!7%e6uWfABKwaKYVw)m3&jbo93_l(w~sax-vJCaFUXJ@zSR?*VeKd8`;Dh2)rQSpo(_(IDJ7=oz* zZuxc9CSdj3?oiT*i9#kT2G=1*3YRc26P1%ssn>U*ZJm zbX)JqtvljDu8^&D1>s7q46f1MUVvy-8~UCp74!C;VdCcL8$3~zwX%4tQf7KoB>tDr zLFzA`!{FKjfJBWx%-Fi8bW3NV}PCw@sQOomZW+ zNc{tfj5eSGSU6k(`%kGEGyF-7BQ_)=MeCMJPEWp@AJR^`mm2{$w8Y@?HP5PbVn6lF zERY8yF=H_8>fmg}YMVFOcmuY*6bTi5>XqsNW01L^HgJl+l}0PQ>5Cr!j(cFBie;2? z)LOD8*6_qoVoPG<`ALb&R5b;RLHtAgE9{wUg^b(WQGt)cES=k~++w#mwfr^QMRZV? zZq1BP`e##!w2j?^du$v5e^d~O&c0L+B5URVv`+tq8m^2Va!jJBZ<6+N=7thge-myT zNs+Hq6?eEMcIFren|O{7oUJp}f`XU-<4y&&VW+6)M-L$z|qF=7cS0{%|8)y#I>~H9%O67F_izE%zpqneJ(g6$O$oDuCJe zDvJJaIPc3BEWm|=_OfH}1uT$6t6~EX0X8;vU{FxTfVpOar(nf*G`=e*_utwr_C+X} ziw>$7#Jf{~Et!Y?tLW9Q|5vu(=2lP6_3w-9jAn9^TU+Dq0Dx{mEJD6Zag?N)^hrUH zIW=LnhJWP$^GExnHYSg<60kirWPY?|YLgaJ7Qvr(d8EQfu8|oZu`!)kRNN3e zI{4u#ABzox&CjP}0Lz=pg5B&zrz}T?$+M#$zq;ZV#(85If0uLYwY&Tr#MiE^ zygkiM&m@;N^^Mr)F?Jm&6V~6UADTA_I6q>kHh?G39v5=i5g`3XZrc`oO=GG002faL zIPfmYO>M3rk=BzNGNj^kb@r@*TmbbCSE(fv5Z>>tv6- ziG=KcZ8cOo7YBa7kjFRW`_lLN{Y7@*W|f)8eNzwL@Y#R1pFfzUiNqpBo-pHZG6gP* zM~-)w8_j=v)0!5M^UweoBUJ2BIpP3i;U_b-0ny~EHf-#(J}?YlrK4d_FW%KjHPF>* zxmIOqF?)>MKa7nHgd9_&IU$jvl}0iVXNt%}a@4*mrrDih-43FNU>e#8Wi^;o{RJPB zoa{^QrDvHLT?Tf_f54V#wb#>pV`-Rw^JkoHvz1?vVMt+(xh?Eb%uCSuqlnW`*|INs zxPsvqRL*wNWhWUs8f1J-kQqB&->2+PX&P(=xXScOe5aE9PB7gMa)d>bp_P`YwY|)K zk~t@2$TV|jyES{y_P6Ko`ZT5}l=}wjcb6oDsH4v%8lc*~M^2RG&$9X+Q5;NG7J8z< z4N?HT^q_R!{9#Q+1w(Whu+`c(t@^y2+rQ#gTf(^amyjn1kul_dT04w&KBe@GArV)8 z4bTmdeB~<~l4T5E{Y}_uRA5)!<5GVU5TlYAr-Fz1Cv!I(fBdJXVi?=x33Y)t{b-cm zQ_0q?5Kh(z15~BT6rMBw>w(6{<60A?$E(_vIX1!xD`STS9W|d?KFd*-Lc4uaw{|&z zJM1bmw1gYa50n_8@bL$MZdeB=y=F%XBWTu)H8e;8Nxh4@U63i;WN3*0eD86+;~abU z6hCPX(epx;wC_ISi7;}sKk%34#Kgj{9cYW=rZ38;Hk>KyxRa?I8qf7<#^8X!?Jk57 zRkwq4(|Ol0d+kWVu$8;QW+=xA1cs^47Nf>%YiJt0_0FR9L;PR5|C3+C@WJeT)0B~( zhO;AqW-BCE1ua5DRK4L*Iu&HN>#RH#eihr_-6xH9yry46I=hYz&z3g&y0@bAZ<)r+ z+LzO-^qm~tU&emwoA>W71TcO3SxOP;9*j24yYeGq->je&RIUQL;++PU?t_*2#_YG` z&uhK^qdUVoO)&rdRtU(h1Mg7kOhYo5?R#O^gtX;0Dm-W8*TdcovxQ%~7MzwQnwdoR zV1%pOdScat!oU8yW;GLJybxAU_m93TeHuzHYt|N%yR(=oP=E6H@Hs{C9ajm;V`|JP zl3yUG2$@0MGb8M!?OuGf*qk0MO+-Y4_aN7djRHY%dA1=%!wC6ARagJSX=T}YeNaUz6Bd+H^trxOMQJJJ~df%WC>C{!OAB_4ShHHxsQ^Y$Ip1x`ogkvDV zmB;UOg0VYgmX=Xm@MW>A&(Rg-dr{|X(O)_)Xhwo>;MT4`Z?JoFwkN`(HQjV6oE!W9FQ|56HRCRENF(NEq>=; zz;CYhO8yo-M#A0!5qMnF0BH}%w&?xBx!hMK= z_RN4ixT^(m?!PI~(ww*aT(h3qZ@(*EboXcs94lqo@z$QjTxzr({0sp@T&8K88Fc$Hm(28Xzn|Y;1$g*R`|rB(U9ddqk0W46TW#xtv{g`#Ahf%u)|vUTvpC_j8o-Nu*y)ti5r_;x z(xM-ept>-taE+TEvt~?f)z>8mQ!*f8z#uZ|(lu%cx-0W4k4prY6rH6rLI8kwW(U}02y5J)$8Uw1~w~Xjepsv zA4HUkuhD6PlboO=)8y%t^A6Tr$_JJi3C+*y2TftVF zR+Lrgb47V;bLTtY>IK~?`FVUo!9f**N_JCigKsb=_Ikygp$9!Uu|w4HTrFnjYFpB# z%+qt%+noIsVTQ(9C!+RcwD#|Y?{#v&Qz2K}>sheVjIDon#T*xf>nUtOVgZuQTh8C0 zuYE7RL|>zPnZ6vPY@nrOoZ7j+pHxh@d*{X6K5d~9S+?B|Dg_phn|z^#FE4LsjM*f6 zxH+;xcl?+=@T>b(;pgA{AGrpuP%F{~ZD~s`A_C=x;VqYFZ|Ng(b0SV%?fG=4c|f`i zIt~GUn_d4turUvt{WG51Iqw7rFh;Ol_;Oc9`*`zhkYan$$y|G(i)Mw4k0*JTdC>Q2Pnk`^?_P_G6pbl4__?#VVjlcgz!M-YFLU6~!<2Hx3>SB|)9Q-`;%bjPt1f*ub zR;xI^7-ei>WyRA6W%3LK*QRzFx>5IjMQKUp-B+!6z{cnw1D68w2EA4l3GkFzg1$2I zl5gMguAz-SE*>+s(SzCyaiZ?5|K?6B7*v5VM~s$(Lp7$Q)cF33{2Tb4I-*(6;S-ILKCKE{@4E7ZkpIG-=|4O44 zjvkuo)!G9KImUJkt^looHfK-Knz>N1jSiRo5JYUTGk)~h+FHe>BckhkhhK^#e5=g# z8o!HWKs(^9DYKr}q;Y^hWd_39IyIQOXKk zo6?%#bsN*owoQK5`%o^>3@dXo>L+aZgr=fCzJV$^ZMqxOC|yZMV$T>-yv2Fvi_nRu zc!m3i!Bn+Rbu{ht<#M0UDOs=uB+c=>E*waTG>v$aa(Io$Ma@XSS~0A)*8UUZ30--V z?f=!*nTJEY?{S>0#ZZ=X=#;IIn>D#OLzXg1izOmPl*4G0knCCFT8^cZWsK~dv4yc@ zX^uUbbIBMQvSkn%qd7FGq;x-{Zr$hJJO9n|{GMli&-eG6-}m?ZeBYnf%VX^@KvXKA zufM5uKcwmxKJ@9_tD56I4R#DLR2XVTgKYD64rg)K@G0*2gJM&ngx8m?9y+$Ie1&EX>tP5G|U2M#ukLojpVeNFg<9QDH=6;@5_9= zRYFE{ep=3uWl}+(VY5@?<1JcpWp!l{85up-BhnzIZquzQ&uEx1@GVhQi@9WM8k!dx zXm+vyx9G)DS@5RA2ov*+-P5Ia*7|kwL!{B~Eob({@w;A}<*w@*?tPPD2xiVE3m5;K zPks96gXU`rKEqRi6JrVGjgP{6g#c`Wj#!|r{z~zL;KYo$1|Z#au(-&S*% zSAJu8{&n>7t?3~aIx+WaZ95arkXnVM=7GSAE7keR(LsxE5NpXSK*XVIco`u@RF zI?xqQE({Y%@L}xFI>efh}TP^AtASOuk6|_CDS!p zw_S=qG0}4zTeQ`Z%gCi`J+eZZm3Ry4J7Z~$!(WnqcKZwgeUC!%oW>h#s zh>xf|8TX{h5ZUtkfs?nQ(;NhAPv}<@WRX)MXWXCtonzU~aa|5T39s5(HL`vx-2>z# zEW1Ssc!kAu9C*SuXebuu$!m&Zhyljf7p$$gXK0Zm^tf@+)*(RU{w6!I|9jTqPv6$} zK}SaB%ZE+E<~q)6Ub#6ty6oft)kf4{K|y_J_V?w+NJ{L;_vFJtQvZ8ZhNnnS2*d_) zF4XGyunj)Vt2evO$txv`D6o~0k!BZd(qw;M85>JZ7A4i%b{%>(mrbU0-uschE?2md z#f3pFYS)dxV^65RLgXENEVg%!#7$zCjz!7qJrA@*;M^blXbZ&YPo}mm8hj|s`*(TQ z6k`~kU9ctnRgoPhF2&HnPIyHHJ4lFmnJH{UL!Q3AaJ< z#gX+prpupwcS$+vXz}35ClFcTRwku--Z7@6g_T*lYO)=$>5+EGb%dP_uaH=20b>j${f1LmFI6RG ze&hP(gfE|=%16&d%=Bw3V^X}^{_Ayb{<2|qu?9tR0zuP_53;Bz^YJ9%+|JPdXOG&} z=*K?rS8}@DsTgSX9|aP3>4Vt&%A^__nNQx$yOjSWP2xw8^ky~7m5DLY{YnM?+)l$@IS+>2^r5g10()1|g;+eT_==ouWe<>vXk z_@$+#9UUDZnn!;b6O&UlLV?&(Nq$5Ti8R3ARD2dyQ&STb6U%HbNQ9Y5QC}YqVML}R zT>BJCXXh4gZkc8GbmreKQc?W>Jrs6#O-o&y|)#P^bstIHwWx$+vlE@*(YEs zDS=$$!s4!L=QQ*qMK`0DYXbcIB;RQ>eR{ps%h1Nq4t##O{Lr zc9Ss@H)i;V9#|RR=XU`lIXr5vt@l7#g;$S63kl`r&ZR-+^TE>sb%*ztbvTQGO$Cg7g-uO(b?1g>O)wS1EY{OYp|~wQufhBgoHkmA z)$tmTzlLSb`*>Uie_%rc$TEUS{e+vK;c<6&ABxgV>aoUPF0<9Rzh+iU8-P2aC-vtO zj*j@|I=+ccNr}nHN$@F#rBEL_sSghir)OsRfBN9()aawmYbI%n!SumMLKM;dmeo!m z5$HpC7mgClUt3@2ihQzB-hw{8vm!=DMh1}^0J`l~Mj{F1XU`H)DBvMm4B#FH2M04| zW-hWHJYzLH+YLAT#N;Gg(X1pZNo6av6s*)sXkHsyBM_1M+aO(px=OtbC#b+a8rrfU z&M97s>*!ddS3J$tgF!RHn^tyoMDg1OzoZEgKkRX*G;_EIB_L{FhQ-_^=gTuvhf zQVE;Ju*m%Rw#R2S-DpzN7U1u844zyy=kmyp!!xj_omkLvH$MAEC7;}AVwVefp-Lsa z3fD7SNioK7TeZng*y`=?z1DyGjV3lN0XzeaYp%F>o}s?V%O6dPxBuodBEkiSz~AmT zV-}T}t8UaYaI9b8dN-Qh7+lZT8HYEX+gkGY)O-431RU?`1pufip5FhjF?=njmaNQgI&DRzJ{Fg5PV~*`+f_o7<=L_)6Gcuj=h$a6(2JkgP!Umo(l5JuVvGWCJ zULrd4VKIl0HByrOnLGtg7qniMjrm68diCo@*^ zCN%*uSpnt9gjP}|r<%sKpaZLjem;gI!L9b-|$tyOd+PEN@^`t%94{m zNXTn51$v&#$Gcy|EU06C-CBA(n>cgo2+QyJ>J1Sf3&FTH*OOqt)6@={tTk<>y?dGP8MLI-wk=k&1#Tm#Ok zHRMNT;ALz=g~g=W8acf81SM6svFFVnas13t=B|1Gc^zjQF(m1 zs-+p9*4H)Y5>uU^r;=JqlbjLsb_wktZZ8m(;TI;jhpz z;Gm3)XQ#mPKnT)jx|tg-B^b)Pt^!Q8HYrU~T2dvY6ih1O;>EZQgJ-)r)pwlsL+`S< zu$y@sHV_5ErKNQD_VMgLK1U{yN5*mSk|TI_3Xyd{uARbkwKJ=*60NOEzU!KoDLgHy zl3EB8R{~);mjEyq>6KHh4aBVs2`|l&o|8PXumrE9l9w90dFoIPzj*%VG`{s~MAaf* z*n5KGu@q7;LEadHeY%S!l}ngex#0SB{dkY(jDWGvGI4Dw-6y0GGDm<5 z2kZIIql&?#izjS?!XWWTW*j`nK`mI#KmGk*W6i2Y%v*tf)?B<)68oe7BC@a^fg#d+ zihDLab>6b{lD#(4&6X`&F8PcxX2Qz$?c0ZxlA)m?8cu0s^c)wZgb>;8^*n@BZof7{ z^eTyza?JM$gUB5MAHTbUE$im7Kjr7Fu^8<-M0Dv|M&pBw*barEC|XH`5R~8XAkY8% zHtfdNIW9ECe#Jkm{bNqrHf8-i-2cTtyQ-c9=<4bs8jaH3-A#3MHL+NXa5&7!$Oyy3 z!-PU1Dk>^)9EX=*ewjxfeH1As_qrAcb1v5>9Z&RXCmiezWYQvMikMgnb+k4yXSU|U zwr&oR!U{)m;%9M3VmMZS-7P0rw7??hvlvZ`GH|Mk9~aE$XNPvPO2Ce2F?~iev$K_r zTb{mZ@&F|zC3JLj5C{b5>FJ@jw->+PkJg%jfdS^tn@4wdH?Hflb?a6J2L}-%Flm;N z>Rc!&F(MTX_UdixMe0ZIr0j8}JY$5C;Mi#*d4`4c>v?tOZtPeeQfZ3r+RR|%d*m-( z!^dY0vrl(nW%3yOWG6E>KF*#yAL91k?V!p|aumsz)@){GRo%460|WvADk>^yZ*M0r zFOO(6N-!A2@Ao5wARdoXU0qFoe?Lkod_EsyiUlZX5ZU#B3--44BBcQ=C6YoA1ds+i zPmoS02xj{6yc*8tS0VydG}Z!(bQ3Nq=49weOSQ|kvU88 z7tG{OR;lvg^26aULqkJotq}rbPW-RLgsVUbk%a8N zfD$H&62UPGKu8Y+4yU`@>GuUlj#xx$SK-75h_~#+cn+l%RfIJovgZSoGzld@jVUIi0H0Kt&`c`l*HW|c zPNK{2Cc5GthML|-Mhb~|hB?I*P(GKy%nH)B&B=jbj*h@V6UGVI^t3l31=A{HT?c7@ zF};1K5z$HlejiV+@R1h`01ym@XlXf2I-SCGU0m0tpr8Q3)iq0z>gp38_f@+1dTdrNTg|S9{nXBiFuQC8%VQ$|Y}oJsLqmgvLSd?_tC4bS zTHe*#MDA;)p0pmy83IbA#y-Z@#$^`2KzYf z#YlGbpbE>7mc@83Nl8J|$uq31tLKf^U*Y!50G&;HdE}w55iOc^#RAMO3$f*k>j0Qv zy?&y;vZ5gSOH)`_hSmbxJ{K4Xn3UccUjt;TQxgJcNKOPl9%6jzh~&>;>8!cD^x`j> zS&~OVaW(PL6mPz>gYxx{A?K}O^}=47j-SE=Y51ESyVz`Xmppp3RHDZDZ}KMTC{dsm@lK`&@qf)mrAy zK9^xS&k16L9v8sNu>h?_!plnKDIs#Rr*jr8W|Nle?(Ss1POz)>I7MqW;rT+ew|63f zdBjc}!#_Ggc)nzli zGP|aZO-ond%LJ*&pMeYn@iI2~eVu$RsQAL?A7fQCLi*4ijOQV;d4HiS?1Q^mKC6N? zKJcGDg~%w5H1EY8ji2|P3l)H{1>fFK$`8MFC(k}sP4nptJ!fJR$swM+tCWhOFiXnE z;tkhz*}i={LWuLYh^&K`VvO^I1xO{6Dw_rJ48pxYo^D1_DfiyDiOR!`{Hm)9Ur8l# zVd(4c;eq=fg@sF~X>Q z@e8&*ct`fCkd;dS>T{l$GwJ!QTelLA$Hzpts0HK%LRyd%*}W0hpf0sot6sR2{Dxhm zN-8mtLRy97@C(oJjm=xATe_Nx*>h=V`jDNw-Xt`4DgL4|;*kOl8$(-12emB+SikAg ztGitI3o2=7Xt?a>$Ah4Pf&xZOn&Dh7!bqLvsZ5R!kV+=Tj|3T9DK4=+?DP5g(*2L{ z{A=5>7TUJF!~Tyr(cg>hd6XBGP_=Fomui11 zMoZRkl00HpU5%Znl3{L~3(wRcXGyW@l)n@`)igdtl4FE(e zez>_#Jf{JSa7UAl!Wd5^)2{N2;ifwwGqx~No|HleVe))t6tY7gc6O-E@kM=!v@27| zbT)Rn2^9e4fIs9{UhHhVZM;?h#lLv}#QXk8;XKz!>n0WeCA>^}c%bd;>#FVr`X>Yc zHUYIj2=D+3n&1BT;w$QXAmA^i=RQD2RH_t z1kyPRPE?k(pD_!NT$`QpKLnVp2zIt40S7Qs3EnJ(FGrADxP@D|g49_BoIO@PFE2ZhfFG=YC;fFN!6l8gm$=W-Hbb9B*`{|rq%>!HTz#pvDpPz)iqK@ zkb2H0wwX2T%}fP0arUE}27XZRchY%fO}3t3v3oLk&5z_K6@&aGI%L%Er9GbZdHR(4 z+u3&i5&T|qD16|#6UJ+SsWWG;S@ZGj8SXy-(Q zWTIzIXufA%?K3h>>-u4TKnrtdXlqF6Ao;*2Hr^;-&6d%w2to^!u*GdqAvHPaz0iTE z=(N{-wD3h?-SQWS`h<|Vd>2%a?!?Zk*Ic#`fvt*Z^+ck^nc1k8u8hX<%mwxj?!Lm` literal 0 HcmV?d00001 diff --git a/app/examples/Control/HighlightEditor/.lang/ca.po b/app/examples/Control/HighlightEditor/.lang/ca.po new file mode 100644 index 00000000..2ea14157 --- /dev/null +++ b/app/examples/Control/HighlightEditor/.lang/ca.po @@ -0,0 +1,43 @@ +# Catalan translation of HighlightEditor +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the HighlightEditor package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: HighlightEditor\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-16 23:32+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FEditor.class:208 +msgid "Highlight HTML" +msgstr "HTML ressaltat" + +#: FEditor.class:213 +msgid "Highlight immediately" +msgstr "Ressalta immediatament" + +#: FEditor.class:199 +msgid "HTML Highlighting Editor" +msgstr "Editor del ressalt d'HTML" + +#: .project:2 +msgid "HTML highlighting with the editor control" +msgstr "Ressaltador d'HTML amb el control de l'editor" + +#: FEditor.class:204 +msgid "Popup" +msgstr "Emergent" + +#: FEditor.class:221 +msgid "Quit" +msgstr "Surt" + diff --git a/app/examples/Control/HighlightEditor/.lang/cs.mo b/app/examples/Control/HighlightEditor/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..a1e4d6e600ef386d5cf36333efe14c96263b218f GIT binary patch literal 842 zcmYk4%Wl&^6ov;V7ljI;N=Qf`bhZdN&=r*_s8-`P5q1*Nq!JaIAsOOHY>#D+(>4zP zY`SEDV1vXqk5D#lC3pdrEZFb@Y?1gU=_Mom{^Xq7pE>@uwD5u9T!ZdHub}JDXULrI z&~4}kbRYT+-GCM^Fm?;P16~G~!3FR!SOZr<2EBz$-**=oy9a&+m%swN3H}5Z!9Sq! z@E7#Jf1qh$&ea*F&ntMEIS5bZnwm=xtG0cQsE*=|GT} zW>8nfI#nS%t0vQI?o~z+Qn?o4K5MEZPuPoGYSs>9Mq-agNhl}_)GirmMR_Ks{bvqN z`_pu8H_x<+Xx>nnHB&VZU2WBR*0xA9sbZU&&iZp_)1_^OU;JW5+iAXRs~*c zdg(eX%Wqf}hpG+RuRYjl)ZLQDGi|j~9%muP|F>zQ<9Sqfe3w>T->G?PPNmXvx3-=k zHSXuUFRZrUkxiac-|U!?tDd(O%CbuP=Q%c&{F?8c`&%wQDy`$@M2vC!3H)fWrynLE zm+=$o20YD#e%fhoSS#mw#?~8=T5jA`JuJzlmDf_2*vU13sig<;UNOi9Y#^FmR+oiBBLd4x@QD#TSf?%IrUoK>2h4 literal 0 HcmV?d00001 diff --git a/app/examples/Control/HighlightEditor/.lang/cs.po b/app/examples/Control/HighlightEditor/.lang/cs.po new file mode 100644 index 00000000..6e3be1d3 --- /dev/null +++ b/app/examples/Control/HighlightEditor/.lang/cs.po @@ -0,0 +1,40 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTML highlighting with the editor control" +msgstr "HTML zvýrazňovač s editorem" + +#: .project:2 +msgid "This example show how to use the Editor control with custom highlighting." +msgstr "Tento příklad ukazuje jak použít Editor s volitelným zvýrazňováním." + +#: FEditor.form:15 +msgid "HTML Highlighting Editor" +msgstr "Editor zvýrazňující HTML" + +#: FEditor.form:20 +msgid "Popup" +msgstr "Vyskakovaví" + +#: FEditor.form:24 +msgid "Highlight HTML" +msgstr "Zvýraznění HTML" + +#: FEditor.form:29 +msgid "Highlight immediately" +msgstr "Zvýraznit ihned" + +#: FEditor.form:36 +msgid "Quit" +msgstr "Ukončit" diff --git a/app/examples/Control/HighlightEditor/.lang/de.mo b/app/examples/Control/HighlightEditor/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..5796eab0f64161431b23a67bb97e21e4ef687d7f GIT binary patch literal 873 zcmZvZ&2G~`6oscyeiRUkN+2Xy+${nJx}q`#)ot7+!cIbxRsykc<6C>co{470{d)j- z0(OY!fd#L?0`U%r9mfukf*9%Oo1f#kXMDe`EWZ;Nm!aFxOXv#p9;(Mj=qB_Dx&wWM zu0dCqgt!jg0xy88;4=6S?1Af`fZjkiFnxPYh&K2Etk<8x74REaTfc*sz#m`+E}ft6 zW7_Q3tj9&Dt^-?Bx6Tc#5Zy@}QCDVp0XeBM3RCH{nIGh5500eE$>mJkIB2SsGrACe zzNY%JGfP>PuqKX+6EW1YY9^jl(uqkfE%9+u&I%@*>m#b=G*y1?337#jVGr_Sr8-X0q(weo2gY&{KjLfQ*QAIkdB>mZ9y&%+q<5Mf{#o;2~YWqQJ3xAFk%AL%R$MLbdKPwWc9?>*UjAi$D zce3rREqryX12$f$raDEFd|KO=uGKWjixcx&vZ+*N=bia?n-!JPQOf17=6Ykt6&o(N zM2v;qQqJa3o1FJqiLpM=#{ISBiA`pe;;\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTML highlighting with the editor control" +msgstr "HTML-Markierungen mit dem Editor-Steuerelement" + +#: .project:2 +msgid "This example show how to use the Editor control with custom highlighting." +msgstr "Dieses Beispiel zeigt, wie man das Editor-Steuerelement mit benutzerdefinierten Markierungen verwendet." + +#: FEditor.form:15 +msgid "HTML Highlighting Editor" +msgstr "HTML-Markierungseditor" + +#: FEditor.form:20 +msgid "Popup" +msgstr "-" + +#: FEditor.form:24 +msgid "Highlight HTML" +msgstr "HTML-Marks hervorheben" + +#: FEditor.form:29 +msgid "Highlight immediately" +msgstr "Sofort hervorheben" + +#: FEditor.form:36 +msgid "Quit" +msgstr "Beenden" + diff --git a/app/examples/Control/HighlightEditor/.lang/es.mo b/app/examples/Control/HighlightEditor/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..e15d33cbd8bc8daf3b60320f11c11e84c012b017 GIT binary patch literal 569 zcmY+A!DucW_;t_ZOBzO$If;N7DXW%Dz0)By~ z;2b;zf4~~J0)N3Zcn&UC7W&l(i+UZo)$`l@1{sohw6~YhYE}Wp^S#SH>kczXT~nRb zAQ)SbC{1vl-Sw!I{8O6AM3+VdU7xYOo6e@}Xl4TId-q9?15ZnStbJpgjj5mPze^4U z9gAX+=Dnz&^Fn_%?T~k{A*S~tDR`FiPD0&0&eE4Bc~3-AHGvPkvQ4evC8mRsl+;Ty zK|3N#Qn{COI)xYvb|JOZOjVi>wVK2vliuN|J-H+0UTrp&BVQ_*BhJBzOzAF z+&G)UM9^Qtt7)x_-KO!W@{JB}N5ccYb+2o^eAJ!`J9Z_$6w}rR6C$>F7LA=HuNzed hRl51Zh!y|U%hHpvO;9%c{%+wICiLwW2dXxn-2g+ZkM#fm literal 0 HcmV?d00001 diff --git a/app/examples/Control/HighlightEditor/.lang/es.po b/app/examples/Control/HighlightEditor/.lang/es.po new file mode 100644 index 00000000..016970b6 --- /dev/null +++ b/app/examples/Control/HighlightEditor/.lang/es.po @@ -0,0 +1,34 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FEditor.class:201 +msgid "HTML Highlighting Editor" +msgstr "Editor con resaltado HTML " + +#: FEditor.class:206 +msgid "Popup" +msgstr "Popup" + +#: FEditor.class:210 +msgid "Highlight HTML" +msgstr "Resaltado HTML" + +#: FEditor.class:215 +msgid "Highlight immediately" +msgstr "Resaltar instantáneamente" + +#: FEditor.class:222 +msgid "Quit" +msgstr "Salir" + +#~ msgid "HTML highlighting with the editor control" +#~ msgstr "Resaltado HTML con el control editor" diff --git a/app/examples/Control/HighlightEditor/.lang/nl.mo b/app/examples/Control/HighlightEditor/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..f4f6adeba555f61a44ec22fca6bb1b72eeb34df5 GIT binary patch literal 833 zcmZ9J&2AGh6on0xzfiH@M?!+dWyxZOBv4U8L7+CMMU5J&6ePsP$=qg~cZrP&vLp*PyS^ zE$An75n4SV#8vP*cp9vME8sow8Mp!d2H$~~!M7)cxCwp$SHVx<74S2N5b+H>4}J$< zfxqV8u@q;^38U=s3{=*^@w6@=PLQ!d7*;nq{4QKDA>{m?&5(JWmVETKHRl zriFWj4>qIaj$+YvcEG(5?Ih6)c3xT2Sp3X9%bj>FzF)1h_o96ssWK+oLQf5(uM*AmarpqEPP3GGV`o;Jqi=5fL!);5n>x{&%*s=KpGk-GahRp6C&!*p8>14*?f3I5e3sa}|Os0~AwHgd*c8iUO z#FIm>;S<(dD$vMU*JaiTX(\n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTML highlighting with the editor control" +msgstr "HTML markering met editor control" + +#: .project:2 +msgid "This example shows how to use the Editor control with custom highlighting." +msgstr "Dit voorbeeld laat zien hoe je de Editor control kunt gebruiken met aangespaste markering." + +#: FEditor.form:15 +msgid "HTML Highlighting Editor" +msgstr "HTML markeer Editor" + +#: FEditor.form:20 +msgid "Popup" +msgstr "-" + +#: FEditor.form:24 +msgid "Highlight HTML" +msgstr "Markeer HTML" + +#: FEditor.form:29 +msgid "Highlight immediately" +msgstr "Onmiddelijk markeren" + +#: FEditor.form:36 +msgid "Quit" +msgstr "Afsluiten" + diff --git a/app/examples/Control/HighlightEditor/.project b/app/examples/Control/HighlightEditor/.project new file mode 100644 index 00000000..777be4f4 --- /dev/null +++ b/app/examples/Control/HighlightEditor/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=HTML highlighting with the editor control +Startup=FEditor +Icon=editor.png +Version=1.0.0 +Component=gb.image +Component=gb.qt4 +Component=gb.qt4.ext +Description="Syntax highlighting example\n\nThis example shows how to use the Editor control with custom highlighting." +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Example +Address=benoit@localhost +License=General Public Licence +Packager=1 +Screenshot=2014-12-17.png diff --git a/app/examples/Control/HighlightEditor/.src/FEditor.class b/app/examples/Control/HighlightEditor/.src/FEditor.class new file mode 100644 index 00000000..5c66f472 --- /dev/null +++ b/app/examples/Control/HighlightEditor/.src/FEditor.class @@ -0,0 +1,184 @@ +' Gambas class file + +Private hEditor As Editor +Private $bIgnore As Boolean +Private $bModified As Boolean + +Public Sub Form_Open() + + $bIgnore = True + Editor1.Text = File.Load("download.html") + + Editor1.Flags[Editor.HighlightBraces] = True + Editor1.Styles[Highlight.Operator].Color = Color.DarkGreen + Editor1.Styles[Highlight.Operator].Bold = True + Editor1.Styles[Highlight.String].Color = Color.DarkRed + Editor1.Flags[Editor.ShowLineNumbers] = True + Editor1.Flags[Editor.NoFolding] = True + Editor1.Flags[Editor.ShowCurrentLine] = True + + Editor2.Flags[Editor.HighlightBraces] = True + Editor2.Styles[Highlight.Operator].Color = Color.DarkGreen + Editor2.Styles[Highlight.Operator].Bold = True + Editor2.Styles[Highlight.String].Color = Color.DarkRed + + VSplit1.Layout = [1, 0] + mnuImmediately_Click + +End + +Public Sub Editor1_Highlight() + + Dim iState As Integer + Dim iNextState As Integer + Dim iInd As Integer + Dim J As Integer + Dim sText As String + Dim sCar As String + Dim iPos As Integer + Dim bMarkup As Boolean + + iState = Highlight.State + sText = Highlight.Text + + 'PRINT "Highlight:";; iState;; sText + + For iInd = 1 To String.Len(sText) + + iNextState = iState + sCar = String.Mid$(sText, iInd, 1) + + If bMarkup Then + + If sCar = ">" Then + bMarkup = False + iState = Highlight.Keyword + iNextState = Highlight.Normal + Else If sCar = " " Then + iNextState = Highlight.Operator + Else If sCar = "=" Then + iNextState = Highlight.String + Endif + + Else + + Select Case iState + Case Highlight.Normal + If sCar = "<" Then + If String.Mid$(sText, iInd, 4) = " +

+What is that new animal ? Well, Gambas +is a free development environment based on a Basic interpreter +with object extensions, like Visual Basic™ (but it is NOT +a clone !). Read the +introduction +for more information. +

+With Gambas, you can quickly design your program GUI, access +MySQL, PostgreSQL and SQLite databases, pilot KDE applications with DCOP, +translate your program into many languages, create network applications +easily, make 3D OpenGL applications, and so on... +

+http://gambas.sourceforge.net + \ No newline at end of file diff --git a/app/examples/Control/TreeView/.directory b/app/examples/Control/TreeView/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Control/TreeView/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Control/TreeView/.icon.png b/app/examples/Control/TreeView/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c56ed6572a0f49de4e39c792b26ed178176748e2 GIT binary patch literal 4093 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 literal 0 HcmV?d00001 diff --git a/app/examples/Control/TreeView/.lang/ca.mo b/app/examples/Control/TreeView/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..968c7593cdae86b908185ad460872f1c6abc5893 GIT binary patch literal 1286 zcmb7?&u$b)5XPGT0XFYvN6VR?cFK7ZF+i^?jB=v z%1h)0!U-uzAg-LS#2JoUK;n+X1LTmeb_cUT${{WF{HDIHsjlw%-W}fYhr&3Fc?|O} z%;T6d+wj163f919;7;%bxDR{@9s*xi@dr?9d;%qJ`!`DM1^0m8f=4PI2X`Po34RCu zSj88rI0X+v?^L`EUPe3s>!|%FxC`-{?c;skg3`}>a6kAFJPQ6@#YeE2{CzgK(&{)G4yh;~Nb$hqYk8B$B?%h@J}+=1MO3^^a# z8{LESCPVIca>(88nGmEG99=2u4~)$`ySk!*4m5r?$8x2)Vj}Bw%d+;wYwTiBGd+K0 zK5|Z;`;knbGit6^lu@-xkD`#UsM_P?4(Hmky2f^>=3QVP>4wf(Er`0vd9lW0VaYQu z8-76Tb)65fxFzs^2=u_47}-&8gIaa!>i#|LGtw@jj_w&OTq~nzTUT9QtfJRsK1-I_ z2UEC|K9{d`e@zy7&4`*@)sa)0YBZZk<9ssJq((EnaPe%T*=W>$(IF;FUb|p*EPP7W z3!fRfsW%EsGb{BVFF$YSK~dB>TtU8DDfJ2`ORV#hwqm?4%rCURbkeLhYOTWIB&feJ zL<5o6L#!cfdXdgJv-dgG;m%ijlA9vX&ZPza{<5p6-)HAWxwWZfK=@dQ>DY@Y& z46{?yG=80R^!+R~r)zhTZo!#}$>%mxYU!x0b~QzJjkN(rh?|dL4tbVtBtnBRM5t0u z-{8orn%D;7kcmS~zU7i`5lr7~K8HhoD>a;, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: TreeView\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-21 01:56+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: TreeViewExample.form:28 +msgid "About" +msgstr "Quant a" + +#: TreeViewExample.class:49 +msgid "
Item rect is (" +msgstr "
Posició (" + +#: TreeViewExample.class:42 +msgid " children." +msgstr " fills." + +#: TreeViewExample.form:74 +msgid "&Clear events" +msgstr "&Neteja els esdeveniments" + +#: TreeViewExample.form:54 +msgid "Current item" +msgstr "Element actual" + +#: TreeViewExample.form:64 +msgid "Events" +msgstr "Esdeveniments" + +#: TreeViewExample.form:85 +msgid "Female" +msgstr "Femení" + +#: TreeViewExample.class:42 +msgid " has " +msgstr " té " + +#: TreeViewExample.class:46 +msgid " has 1 child." +msgstr " té un fill." + +#: TreeViewExample.class:44 +msgid " has no children." +msgstr " no té fills." + +#: TreeViewExample.form:25 +msgid "Help" +msgstr "Ajuda" + +#: TreeViewExample.form:40 +msgid "Insert Name" +msgstr "Insereix un nom" + +#: TreeViewExample.form:79 +msgid "Male" +msgstr "Masculí" + +#: TreeViewExample.form:45 +msgid "Remove Name" +msgstr "Elimina el nom" + +#: .project:1 +msgid "TreeView example" +msgstr "Exemple de visualització en arbre" + +#: TreeViewExample.form:20 +msgid "TreeView Example" +msgstr "Exemple de visualització en arbre" + +#: TreeViewExample.class:139 +msgid "TreeView example written by C. Packard and Fabien Hutrel." +msgstr "Exemple de Vista d'Arbre escrit per C. Packard i Fabien Hutrel." + diff --git a/app/examples/Control/TreeView/.lang/cs.mo b/app/examples/Control/TreeView/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..d544c2cb647ba688ce5f2362ef4a08b76b1f7a42 GIT binary patch literal 1225 zcmaKq&ubGw6vszfwZ`AIApU~6v6&iaXnv8T0liJKW3$cfOqiXt%~21c z2agIO9ts}pp?DWskTmE12Oc~Lp7rFx?<8rXqCPVF$@{)H^WNLpPs4*R2<#Nr1+2GN zXRxm9!vppdJPkerPk?WF{swpq{zq^a+yM`OpTVQx7w{nXt;ZkWApC*-gd72fK;w_} z{IlRm_@h1E1SjCnfdqRWfu@hg1KnO<_V^AQf&KwJ5AJ{w@GEHYzk`Rt2#T5hMnDrk z2ObAULHx)KJWTxw@FIA($0~RS{u2;wbj+M%W;gE)a>vYGV`g@9zA-cZ5SFQ9dN5{Y zHzOH4goXAx@0E^iMfJ>OMaM0d4VSK9N1{52=xbEYU?=0b;LvaF_(ng>+z zil$yjFA=*e8=52<3P(ahkT~7sSspN-lX>pfNm7JdX_{dHCuyVRc_5d0S12mZOCDdL z&MFJ)2<{2|9|B!bp4ME@a+4;aG{-76rd-N|OJ`ZxLtwt475Aegr{ofPwUVw?;wto{ zh|`>%xM$BfwB+Oq$!sQ)%UXH9?3ti7!!(c6`;MKr(phW9rt{f&I(hj)HseH6EYwy} zF%kMqOBJWH3#k;%*lCAOI%zwZnzmJg;cTFV2j);au&pC%FfI!n39vJ)U@wK zrCRLg#Azg*OgsJlV$o}ngcO($<(hT$qxq`7TKAbJZsBg23c0?$P@J{K`+26Vhg?~X zs7Tini*Y($_H@Kta?n~c7hjiJ2DSG4&P5w7){|IA5!`6qB%_p?+t0mFdzv=f)|xNz zw{!p5vdewZ+NdGdm+h@ulP+<52wBSNzE@)vkJvTcz!oI!<}k9bcSw7a3q~@nwYps1 ze(f?+GRL6KQqWo#lB65$Es}LZjgk6luKfx%edf}FRuUgiYrQIQeAVY}gY=`=_Mhlq S_EN#>Arr05-@c?jzoVbKML!Gx literal 0 HcmV?d00001 diff --git a/app/examples/Control/TreeView/.lang/cs.po b/app/examples/Control/TreeView/.lang/cs.po new file mode 100644 index 00000000..23b02a14 --- /dev/null +++ b/app/examples/Control/TreeView/.lang/cs.po @@ -0,0 +1,76 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "TreeView example" +msgstr "Příklad stromového pohledu" + +#: TreeViewExample.class:42 +msgid "&1 has &2 children." +msgstr "&1 má &2 potomků." + +#: TreeViewExample.class:44 +msgid "&1 has no children." +msgstr "&1 nemá potomka." + +#: TreeViewExample.class:46 +msgid "&1 has 1 child." +msgstr "&1 má 1 potomka." + +#: TreeViewExample.class:49 +msgid "
Item rect is (" +msgstr "
Obdelník položky je (" + +#: TreeViewExample.class:150 +msgid "TreeView example written by C. Packard and Fabien Hutrel." +msgstr "Příklad Stromového pohledu napsaný C. Packard a Fabien Hutrel." + +#: TreeViewExample.form:20 +msgid "TreeView Example" +msgstr "Příklad Stromového pohledu" + +#: TreeViewExample.form:25 +msgid "Help" +msgstr "Nápověda" + +#: TreeViewExample.form:28 +msgid "About" +msgstr "O aplikaci" + +#: TreeViewExample.form:40 +msgid "Insert Name" +msgstr "Vložit jméno" + +#: TreeViewExample.form:45 +msgid "Remove Name" +msgstr "Odstranit jméno" + +#: TreeViewExample.form:54 +msgid "Current item" +msgstr "Aktuální položka" + +#: TreeViewExample.form:64 +msgid "Events" +msgstr "Události" + +#: TreeViewExample.form:74 +msgid "&Clear events" +msgstr "&Vyčistit události" + +#: TreeViewExample.form:79 +msgid "Male" +msgstr "Muž" + +#: TreeViewExample.form:85 +msgid "Female" +msgstr "Žena" diff --git a/app/examples/Control/TreeView/.lang/de.mo b/app/examples/Control/TreeView/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..8b73f9eaad65f1319b40fd8e47a7632996cc8aa2 GIT binary patch literal 1210 zcma))J8u&~5XTo1AeeWEKtV`sLlgU|d;ug1kod2Ci7g={M%w$`Ja%VhcfL+fzG9GbSeLQ(ur6X< zKZFnD1$Z8O37!GpMEYCs6!eeaH24`j0)7Keg5SZT;E#yEz)9#6hZ#EoPJvdRiS$|U zEcB}pZ-aNBr$C0i&%g`#eLjIb;A?Oed>84Tz)R4-fcD&dT~s9;>8{b_$5~MaPf)9qsXLpbLm8+9FS^MG zZp!1EUTGs!$j6EWr$jwzoz9vtl;Dp%x8&ps&YH{9g+wlM?QtRR#j+waPFV{T`oaV{ z!PhINUlRkJIaHjnE;P\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "TreeView example" +msgstr "TreeView-Beispiel" + +#: TreeViewExample.class:42 +msgid "&1 has &2 children." +msgstr "&1 hat &2 Unterpunkte." + +#: TreeViewExample.class:44 +msgid "&1 has no children." +msgstr "&1 hat keine Unterpunkte." + +#: TreeViewExample.class:46 +msgid "&1 has 1 child." +msgstr "&1 hat 1 Unterpunkt." + +#: TreeViewExample.class:49 +msgid "
Item rect is (" +msgstr "
Element auf Position (" + +#: TreeViewExample.class:150 +msgid "TreeView example written by C. Packard and Fabien Hutrel." +msgstr "TreeView-Beispiel geschrieben von C. Packard und Fabien Hutrel." + +#: TreeViewExample.form:20 +msgid "TreeView Example" +msgstr "TreeView-Beispiel" + +#: TreeViewExample.form:25 +msgid "Help" +msgstr "Hilfe" + +#: TreeViewExample.form:28 +msgid "About" +msgstr "Uber" + +#: TreeViewExample.form:40 +msgid "Insert Name" +msgstr "Name einfügen" + +#: TreeViewExample.form:45 +msgid "Remove Name" +msgstr "Name entfernen" + +#: TreeViewExample.form:54 +msgid "Current item" +msgstr "Gegenwärtiges Element" + +#: TreeViewExample.form:64 +msgid "Events" +msgstr "Ereignisse" + +#: TreeViewExample.form:74 +msgid "&Clear events" +msgstr "&Ereignisse löschen" + +#: TreeViewExample.form:79 +msgid "Male" +msgstr "Männlich" + +#: TreeViewExample.form:85 +msgid "Female" +msgstr "Weiblich" + diff --git a/app/examples/Control/TreeView/.lang/es.mo b/app/examples/Control/TreeView/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..8a37fa0888fec15086afb5205af892e4bc53aa35 GIT binary patch literal 1068 zcmZvZyKWOf6owZPZYJD9K|?WB8nlK`1?Qry>~&({wThjf6ULmcQ9^Y-p720c@J~$ z0zMc+@GdAoTl1=j-+(s|AA*;_BXABp1}}pj3w{P?5q|}*gWrnyXA%DbZ$tkBJ^^Pg z;)DNHa2~&FGkN|QXyZP32h6~0;A_zK^%lGW9)h-?4+W2k{4r?z`c%YUKzpz6;4=6d z#IYyCuD5rw``X^m4|@lD?sV8Q?K@m8gc4}4V23>q+d4OGu&~I6JY@@ySvVANq)C>i z+F~x9#!{WHsm8=Lqdk%)b-cdHj8n}t)*B`aC61qG>Xh)9oK1?yPD7@o4Qug)oWQDG zO4J_ZLQfOz2-;`<0Z+zAo(hx;Y+nmwNV5KfRm-fy!DIWR3VZH*T{mdEHIHq!D?#JI zi+0N|HF;{>p5`)*xly{p>f6mGYk7gsR{X$gG}paat?O@Xt)Wy7GCm-;M?9&prq|lo zwnwfso9nSCD?K=?sjyPe2>i4D7R!%HRV8sh95)%GA43P`;W*|(F5}&~PRXop_v-G_ zS)FZbm$d84P(?xxDr~7QOvxH(@E~Mu9g2}kPdv;e_zmSmfT6iIt?^@#2syP?!Pk&< z$XP^=A5((f6yu@Ec%0jdiXa=2cDzXz@xos$mn!LNn4aTw*FyG31cMcJWmAux|7eWT Q5RNN0R{Ff*lYj003nvKwMF0Q* literal 0 HcmV?d00001 diff --git a/app/examples/Control/TreeView/.lang/es.po b/app/examples/Control/TreeView/.lang/es.po new file mode 100644 index 00000000..5140a0e7 --- /dev/null +++ b/app/examples/Control/TreeView/.lang/es.po @@ -0,0 +1,73 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: TreeViewExample.class:42 +msgid "&1 has &2 children." +msgstr "&1 tiene &2 hijos." + +#: TreeViewExample.class:46 +msgid "&1 has 1 child." +msgstr "&1 tiene 1 hijo." + +#: TreeViewExample.class:44 +msgid "&1 has no children." +msgstr "&1 no tiene hijos." + +#: TreeViewExample.form:74 +msgid "&Clear events" +msgstr "&Eliminar eventos" + +#: TreeViewExample.class:49 +msgid "
Item rect is (" +msgstr "" + +#: TreeViewExample.form:28 +msgid "About" +msgstr "Acerca de" + +#: TreeViewExample.form:54 +msgid "Current item" +msgstr "Elemento actual" + +#: TreeViewExample.form:64 +msgid "Events" +msgstr "Eventos" + +#: TreeViewExample.form:85 +msgid "Female" +msgstr "Mujer" + +#: TreeViewExample.form:25 +msgid "Help" +msgstr "Ayuda" + +#: TreeViewExample.form:40 +msgid "Insert Name" +msgstr "Insertar nombre" + +#: TreeViewExample.form:79 +msgid "Male" +msgstr "Hombre" + +#: TreeViewExample.form:45 +msgid "Remove Name" +msgstr "Eliminar nombre" + +#: .project:1 TreeViewExample.form:20 +msgid "TreeView Example" +msgstr "Ejemplo de TreeView" + +#: TreeViewExample.class:150 +msgid "TreeView example written by C. Packard and Fabien Hutrel." +msgstr "Ejemplo de TreeView escrito por C. Packard y Fabien Hutrel." + diff --git a/app/examples/Control/TreeView/.lang/nl.mo b/app/examples/Control/TreeView/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..61d5ddbfb3c1b2ba65d79b71695ce5c03a24480c GIT binary patch literal 1184 zcmaKq&ubGw6vszfwWih|Rgr?=Tr3Li+O%3~tW}9=8!)w$G+v!#UpLckW|rC6q&<35 z@1De)S5N*0{sH2DAR-7}6})-yJG(KdqCR%^!}~FB-kUccCP$w$$SJJzSg)|oVO=?Z zALI#m8hi?#0AKd?*Wd*7x8NlB9y|no0*`@Tz{BA8K7W9t&_@n3HV%$~PCwJvr@)iY zFZMYP-hjRWeghwaXF)rX$2|jGoiD&C@D1qdcn7+?A3=A{XYe@q6)b?xiBi}&=z5+2 zkAfFKcm8G2 zva)++(?#AE37?+jt+tE;L(2bUE9RzwuJrzxazw&#>XNbvTWp%8s-+G$)UsSA{1Us{ z)Tw3V)F35HaI#7syi6St5nH7wW>uAtv3yN*h}E2RgF3oPxuI@|8j^avvMV|> z!@zq+T1$#I_juXo>!P(IOu&T-__An9I95|@DDv65(c7ris|H?!Od_=^@j?1If8L)d ztUvHJs4Jb%yC*D_`0Pyanpd2`$BT2N>vMdwUM}1hiS_D6s3a0rn-YI0qiBzB2(k%Z z>^bF5fP0E`?BmL8J88?9Qln$?wF#GyK!vFYkzYlHTD4XgR#Ze8Wv$Q=j@XM4Vrkc2 zjUyq|O-w_WgzT-&`m(n$jB}-Ik?|_3r30zL5?^RaTgb*rv}N6}cccnvFzn1)%#8W8 zGebzYuYG|l)I`~+MJ5S6I!;<`tw{ZC`VYEf*lH>R8RoP9eKTtqo%Se_+dIr-YeICm zO>K-~WK0n;DP<0nUO BA|e0) literal 0 HcmV?d00001 diff --git a/app/examples/Control/TreeView/.lang/nl.po b/app/examples/Control/TreeView/.lang/nl.po new file mode 100644 index 00000000..b3ad76d7 --- /dev/null +++ b/app/examples/Control/TreeView/.lang/nl.po @@ -0,0 +1,75 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: TreeView 3.6.0\n" +"PO-Revision-Date: 2014-10-10 13:53 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "TreeView example" +msgstr "TreeView voorbeeld" + +#: TreeViewExample.form:20 +msgid "TreeView Example" +msgstr "TreeView Voorbeeld" + +#: TreeViewExample.form:25 +msgid "Help" +msgstr "-" + +#: TreeViewExample.form:28 +msgid "About" +msgstr "Over" + +#: TreeViewExample.form:40 +msgid "Insert Name" +msgstr "Naam tussenvoegen" + +#: TreeViewExample.form:45 +msgid "Remove Name" +msgstr "Naam verwijderen" + +#: TreeViewExample.form:54 +msgid "Current item" +msgstr "Huidig item" + +#: TreeViewExample.form:64 +msgid "Events" +msgstr "Gebeurtenissen" + +#: TreeViewExample.form:74 +msgid "&Clear events" +msgstr "&Gebeurtenissen opschonen" + +#: TreeViewExample.form:79 +msgid "Male" +msgstr "Mannelijk" + +#: TreeViewExample.form:85 +msgid "Female" +msgstr "Vrouwelijk" + +#: TreeViewExample.class:42 +msgid "&1 has &2 children." +msgstr "&1 heeft &2 kinderen." + +#: TreeViewExample.class:44 +msgid "&1 has no children." +msgstr "&1 heeft geen kinderen." + +#: TreeViewExample.class:46 +msgid "&1 has 1 child." +msgstr "&1 heeft 1 kind." + +#: TreeViewExample.class:49 +msgid "
Item rect is (" +msgstr "
Item rect is (" + +#: TreeViewExample.class:150 +msgid "TreeView example written by C. Packard and Fabien Hutrel." +msgstr "TreeView voorbeeld geschreven door C. Packard en Fabien Hutrel." + diff --git a/app/examples/Control/TreeView/.project b/app/examples/Control/TreeView/.project new file mode 100644 index 00000000..2ccad8d9 --- /dev/null +++ b/app/examples/Control/TreeView/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.0 +Title=TreeView example +Startup=TreeViewExample +Icon=treeview.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Environment="GB_GUI=gb.qt4" +TabSize=2 +Translate=1 +Language=fr +Maintainer=fabien +Vendor=Princeton +Address=fabien@arcalis +License=General Public Licence +Packager=1 diff --git a/app/examples/Control/TreeView/.src/TreeViewExample.class b/app/examples/Control/TreeView/.src/TreeViewExample.class new file mode 100644 index 00000000..8dc275e7 --- /dev/null +++ b/app/examples/Control/TreeView/.src/TreeViewExample.class @@ -0,0 +1,184 @@ +' Gambas class file + +Public intEventNumber As Integer + +Public Sub Form_Open() + + Dim picMale As Picture + Dim picFemale As Picture + + picFemale = Picture["Female.png"] + picMale = Picture["Male.png"] + + 'This will populate our treeview with our starting entries + 'Note: I'll just keep the entries text and its key the same to keep it simple + TreeView1.Add("Bill", "Bill", picMale) + TreeView1.Add("Ted", "Ted", picMale, "Bill") + TreeView1.Add("Sally", "Sally", picFemale, "Bill") + TreeView1.Add("Frank", "Frank", picMale, "Sally") + 'TreeView1.MoveCurrent + 'TreeView1.Item.Selected = TRUE + 'TreeView1.Item.Expanded = TRUE + + TreeView1["Bill"].Expanded = True + +End + +Private Sub RefreshInfo() + + 'This little check just updates our label so that we know how many + 'children an entry has. + + Dim sText As String + + If Not TreeView1.Current Then + Textlabel1.Text = "" + Return + Endif + + With TreeView1.Current + + If .Children > 1 Then + sText = Subst(("&1 has &2 children."), .Text, .Children) + Else If .Children = 0 Then + sText = Subst(("&1 has no children."), .Text) + Else + sText = Subst(("&1 has 1 child."), .Text) + End If + + sText &= ("
Item rect is (") & .X & "," & .Y & "," & .W & "," & .H & ")" + + End With + + TextLabel1.Text = sText + +End + +Public Sub TreeView1_Click() + + 'This just updates our event stack + AddLog("Click") + +End + +Public Sub Button1_Click() + + Dim sIcon As String + Dim sParent As String + + If Textbox1.Text <> Null Then + If RadioButton1.Value Then + sIcon = "Male.png" + Else + sIcon = "Female.png" + End If + 'Gets the parent item: the current item, or nothing is the treeview is void + sParent = TreeView1.Key + 'Now we will add a new entry with a key and a name of what was in the text box + 'We will place it as a child of the currently selected entry + TreeView1.Add(Textbox1.Text, Textbox1.Text, Picture[sIcon], sParent).EnsureVisible + TreeView1.Item.EnsureVisible 'This will make sure that the item we just added to the list is in the visable area of the control. (Scrolling if necessary) + TextBox1.Text = "" 'This empties out textbox + RefreshInfo ' This will update our label and reflect the new number of kids + End If + +End + +Public Sub Button2_Click() + + If Not TreeView1.Key Then Return + 'Lets remove the current cursor item + TreeView1.Remove(TreeView1.Key) + 'Now move the cursor to the current item (since we are now pointing at a deleted item) + 'But first we check the count to make sure we didn't delete the last item in the list + 'if we did then we obviously don't run this part. + If TreeView1.Count > 0 Then + 'TreeView1.MoveCurrent + 'This selects or 'highlights' our current item + 'TreeView1.Current.Selected = TRUE + 'This will update our label and reflect the new number of kids + RefreshInfo + End If + +End + +Public Sub TreeView1_Collapse() + 'This just updates our event stack + + AddLog("Collapse") + +End + +Public Sub TreeView1_DblClick() + 'This just updates our event stack + + AddLog("DblClick") + +End + +Public Sub TreeView1_Select() + 'This just updates our event stack + + RefreshInfo + AddLog("Select") + +End + +Public Sub TreeView1_Delete() + 'This just updates our event stack + + AddLog("Delete") + +End + +Public Sub TreeView1_Expand() + 'This just updates our event stack + + AddLog("Expand") + +End + +Public Sub Button3_Click() + + TextArea1.Text = "" + 'IntEventNumber = 0 + +End + +Public Sub About_Click() + + Message.Info(("TreeView example written by C. Packard and Fabien Hutrel.")) + +End + +Public Sub TreeView1_Activate() + 'This just updates our event stack + + AddLog("Activate") + +End + +Public Sub TreeView1_Rename() + 'This just updates our event stack + + AddLog("Rename") + +End + +Private Sub AddLog(anevent As String) + 'This updates our event stack, this sub is used by all events... it display the current node key. + + Dim sKey As String + + Try sKey = TreeView1.Item.Key + TextArea1.Text = "Event(" & intEventNumber & "): " & anevent & " Item: '" & sKey & "'\n" & TextArea1.Text + TextArea1.Pos = 0 + Inc intEventNumber + +End + +Public Sub TreeView1_Cancel() + + AddLog("Cancel") + +End diff --git a/app/examples/Control/TreeView/.src/TreeViewExample.form b/app/examples/Control/TreeView/.src/TreeViewExample.form new file mode 100644 index 00000000..d1bd6c96 --- /dev/null +++ b/app/examples/Control/TreeView/.src/TreeViewExample.form @@ -0,0 +1,59 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(25,11.1429,73,54) + Text = ("TreeView Example") + Icon = Picture["treeview.png"] + Resizable = False + { Help Menu + Text = ("Help") + { About Menu + Text = ("About") + } + } + { TreeView1 TreeView + MoveScaled(1,1,24,48) + Mode = Select.Multiple + Editable = True + } + { Button1 Button + MoveScaled(54,1,17,4) + Text = ("Insert Name") + } + { Button2 Button + MoveScaled(54,6,17,4) + Text = ("Remove Name") + } + { TextBox1 TextBox + MoveScaled(27,1,25,4) + } + { Frame1 Frame + MoveScaled(27,11,44,13) + Text = ("Current item") + { TextLabel1 TextLabel + MoveScaled(1,4,38,8) + } + } + { Label2 Label + MoveScaled(27,25,39,4) + Font = Font["Bold"] + Text = ("Events") + } + { TextArea1 TextArea + MoveScaled(27,29,44,15) + ReadOnly = True + } + { Button3 Button + MoveScaled(51,45,20,4) + Text = ("&Clear events") + } + { RadioButton1 RadioButton + MoveScaled(27,6,12,4) + Text = ("Male") + Value = True + } + { RadioButton2 RadioButton + MoveScaled(40,6,13,4) + Text = ("Female") + } +} diff --git a/app/examples/Control/TreeView/Female.png b/app/examples/Control/TreeView/Female.png new file mode 100644 index 0000000000000000000000000000000000000000..3c1a3bb21aae6cf2350be5eb3ece0d190ece3d0f GIT binary patch literal 293 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaN3?zjj6;1;wtpJ}8S0Me5;Xebze;|n;UHPLY z1ys#j666;Qqh%dE5=RbAdOHBM$~39e3lbn4!2fxZ)Oo12=9s$~zz+_qK_J{QMUTqEr$)2p4} z_r^uQ{j=7L$-4!PT-?=mF0n;Jm`OXiL1F=mSwo{>%9aU2H!|4RCM@-`O7L!6@a@== zse7+4v`b>N7I|jBL;OI)cOmAc+VAgJ-^hrh^6U}dWH{`Tu{0|3H!;>XS(+ z1FGjM3GxdD()B!%TR%2;n{quciwdz?1r*ut>Eak7aXC35L4bvcjjgTxy_|u8L6Zd& zkJqs>egz>1O|_@T`40%Kt4vkoDLcmRAhhUxgTjxEMl&vdFM7DH@_o+?kp`KI=HjfZ zspnZ6EbHBp#Q*>R literal 0 HcmV?d00001 diff --git a/app/examples/Control/TreeView/treeview.png b/app/examples/Control/TreeView/treeview.png new file mode 100644 index 0000000000000000000000000000000000000000..760ff1f884602717f3388dd9e490e2797d8e48d0 GIT binary patch literal 471 zcmV;|0Vw{7P)kdg0004=NklE8X}A;&KG>-(Hxaf)rX1 zgeq7j(@x@I(#-TSNt*G%CFhdlm-{>U-g6^H`Ph2VvmHgFe5|{sz`mYjtm|Qmmqs>Z zGBXngkvwku)hoI>uGuni56Rh$7dkShetOyneP~?6XRm$qJQN_4 zIh8jG-1f>;TFlBCOr=g7n}K047IEl{v2T8uge%C!-nayIgRzJWo5f!UEPQu>zjhi7 zY`Gydl{z{v@TAfEVx@by9@YTfUPvIq-E!6%B)Db2%#+oR)M0hrLWMvbR&xtqR}0Lm zFi?k8UT0xYT&GgAC9HN03my&~nH9Tnv)aOHtl+*xBom3fuNJr{R+R$PSVhQ1MYaY9 zaVcOuff-^M#UE-7QfQ$yNTG$+AiXR5!EaYXfkBy7gB13I$|BA|`h~DBeX`@31ulSY(1~`bBD^Nmk-=OE|gN-e`f(-y|a)n z{^&3}TTV^uUVl{r6n|+i+I7$xvXgy$_wE8Vtx9Ki9X$Iwn9(zJqcNA$mc*Z)%i5)B zJiMiZ*6tYBefI=4wF4l1mzDq}Uw#{{Au-;|-)+d@Z#QI9*#$p*3wj2@cFxpoYwo7z zcr7`}f~Ws!F{OF&e1BULJN_~V%16BL6I1?~3-G}9o~>Gl2Mncir}_Mmuti0ja=!A& z-5HhVLVV8Fy&xn~z$dTC;On>Maat>WaR?gwhTMGwr+dSkI8=vi4)C43^11i=Y<}{8 zVLtJL2BMA)$`3-ITpuwRH**2B5DzRboHluYI9KzreYNWK2e)^xe`IsoJMU*u`VZx3 z4O(l~EKKHmcjb|k>|}?Fstqo^&SnI3jE`XA5uH$mfSW!sQ{yMSiY$DA6$9*6@Xcrdb2l(C{xhyG; z=T9y0?qRUTym7~YsuP`@I^M+M`~Z(Vumo-R`O33!q#0Zn65_ld6le!f7_J0IAT+4t z#WNGY)nGgN+!X+p=If+)8^o^fZ|`3J!wDiR`1V1xt4UAr@SWSTxp_r0?{>kqqY$=- z2#uhnshkIqlwu|4o&#rS$&zYnxPV4$_ea6r2nTo1yR{=UyECj0T9 zcXa71vSud0h=8X$1vBxY4+5@>WjVa~=)LKmoOo?&4{&EL06=m2`PpA!cu%&=+a|j( zxd6^k{9R5Enn?!+lM8^xIZqGxYr|As+It0f=ph-6x%8QTB>{Ya5VnAawB#W%j@UQ> z);`|J?mz4i^@)9i&C4qWx&|a1dBXs#vA>J8j~sj~F<{&c!ZWIhk|~1AD+B;yj_y5k zremS7y+i!_%gud@3*yz-kasolANIq35RihwF~=Ng7<7zxYR!T2dTZQxz@x-vDL~B8 zp|R(#>u4k9h_K&K;|L3JZVg{5kP{XFqcJyZC@D=PW&GJHUM^4*&!sdCrBqlcHQ~aC z{iq)hCU~||ZrG4R9x(2L6c;W7xQ{?)YCSJKsToF%J2^WpH<;|1_YH)1G5no3R(-~x;RT-V{? zzU_$l3yD=7rcb6Ykda87U!jCL@AhW`J)w73hGQCrP+|3+pg;d%aC2=1{iz8E+d?h3eird5eheiqYRa8B zz*AC~hM}aX)c7mzId%czP#4eq_rpApo5OQkKF_YFpQ4~-K3{+Q1=>Pk!fBZVu3SgP zRe|9rk*JeL@fyZO9du3fHbY6%kOK33-hvbEgcHy6+TVPWUCWknMPDBYLb7yWDN8qP zro6{SX$RMe3@IxF(XM8S5;O4!5-zF$6M0I3DGjAeDFx$-n7U}krnkG9mX2C#j_hVZ zYBMEk*OCatXJ*pe-oYb3eUw;S0x{dcH3x95$Y}gIRs`KxLw0H&hT%Euy)Jl}z|&C5 zG?dgKC53=9$$zRoprxvukS9jijdHxTiQDF7;AZ6UbVV~;kF@f?PdrY=?_NO^T)~t3 z8>sDzAjO%^M{AAM*2u!Vh0MyEd+}cV^SH4a`Fhrj> zjlsBd(u8DjNfrmYBe)tKdvXV#y5%O~)_j(VlZ~_-Y$WP}5lFxp>_;erXnO-W5u4KG zSEHuc`Ol>S6>gakVJDD770V|TKx>3DWQXBVx>HFxHbV$^LT9n_^*-n}Jh+{Pbd7dtxKav676FAj=K1#R-oZWBmT(~Aqg#6z zY&=5ZqN^DU^)g`Dqy`c(ltc(Y&Q%+D;x`XtRlGp0Fi7_)et7l0G+7o|ovp09_5K+V znXHr6+NHYURZ?^2P*jk~EN?x%wvW!}oGERUWK;lGBc*M$OYIZwN+OgJC?$|m2q}lw z1RAOxwlyi7yt zPza=0)g*T9qamD*&MYNKo@V8oWL|B_LCB#g_CnwPkeSxco|bR1HuV_0d#)I5%VVex zA%p|qx|(mUi_4J!h7_iff{`MHAQ}zhk9Fd@(4CZraBQNT&6rI!1WQ-YUwwq2A=rJY z19hq#BbY+p@pqx8m0;l_e5-EZi(mQ?*@ah}SLy$Jve)C50H_Xh>ng@OJ?vF@?lG92-JP5ZJUe*U;&W z!yNDsEM1Nr>LGOUU9@YHnOi{oKrj6sFFpMb?^P7~Eer)o={YR<$PF`o>k}a*ER|1D z8@v%OiM+Yu2Xyvy)7#aKOwK2z@g;6b*hXS}2YdJ)ooeN{5E#>r80p_Nj1fSI$VeIx zE<-ALyaY3I@gycPu>WnMRu~}!zU(5h%pRm+&>nMfst@4KSw_8exa|)_>Y|u&3V*1ZErnH7)*s>B(ue3tx_zkr1l!T$;lU7q z7DUDfKzO8@U`QpeROrAg^5&FM+yi})C_+lwcRokX%4-Qanm~FkHD-voS-C_li>9cB zl7jxkcrpTAwANIRTe5r_6yL`K1hlc59eOoE{|Qoxo9OeVP!~?7-CxV|KmRKCtyzxBC zGd$SJPj~8~No}P&x_y|9t*F#2q{qWZ8%hc)o4Qy!e;F^n@EjkB^-y2ApWANwEQx8^=PbZf-znk( z38ZGvrgZrl{<7oec$cJLl&l_Cf9&zd9AFp`7+xbXhG&FoC|`<^<6u%Ui?WM&`l(-G z9N0-pdOEHUy!7f0oWwNbiuJ5mw*gMKqqRV5J-VRkH53P$y6`Oc7=vBSeC^xcpt5Ek z^)-hWX@5Fp*mb8YiOH#yuDF&M20OabXwO|UrL7ctl$#-jJYYxwCBkFUpcp%2DxAB3 zjo05nBxNphyfMNpwWM9W5!V}_rly`F0|tE!rx>h0INaq92?5|y5+4qgb?yAibAO~O zn8mTqI!>HC@Sd63RAua&o2)1$$tjvo>B{RV`{S<>=qYW-r9maaX9SQ$&Zq=xLdh?l z&&EZ|@y7h*CM6@|{J1fTq>g$%?pNG$!<{To3=%!E7wx);;dOtZJXnXzSdyK~Dlhoj zS`abC@uT~(21D<;&twJOzm7*KHh$(CbhXssSm7ycr8wi=h8&Xxq>|>yT+oq-$=2zT z(=u6e-A3|`Rq%XcBi@WWLPFEg*~*RU?tpm&ebr%`$Z@!25i&l`EUqpH3TfFw?mk1Os!k3msD44=At*Nf7qx9rK zuHSgiw1@+ElXICP4o$oMSTyxSk!fenh18?T$Y5L+AS~QbrKAv3EAI9BxP9GiJn`FY zcotlR9Xd^h*GDX7A%q|?IgRVq+|0^l*HBY)jPe6-)6m(D<+|jgWl(VKM$Xp$M1(W1 z;`of{1L+e#2Z~W{QtB`aUw`GK`^|n!d>g=w+NrkVPH(IAB?d#usNZMw00h1I$D8KK zM-2d1yMty-p|uv_h@)KXa@p;W5g>(DT1X*;(D5F35Qambucx=#_9T15QCmhLmKsW9 zm!ASc2<4!C0iWUa^_;FA)&&8iKl7)?H@yj|C5|51EOq$_0HB19Spz-QpZ$3L8lZDj z0AM3f3IqTz@7+*wslx$cz#wn}_``?*06riWm~D2vfy+?Z>jheYQ$Q0C z9Ww_+4U_gUsf2L4#lO}J@4U2i^uJ=p$%vAJc-k2zWu!tf?wcmsI-$;>Z;>#kdYp#&Kzer{h~ zIH~Og*{x(Yl_Atxjx-qj>kG~7DC?r9vz?yu#|a(z%?Cz&1ciC|)U|hDc$4uZWa7q7 zlaZnk0^+>Jl;q(8y8Nt7+X!UMMj8sUxq`M=AHma-WW$%kcc9?e6j=wfV@bZG){+w0l8uaQiC8$~M0#g>cU$V2UU&CM zS|2FRoVak|;@1Fo?s;J^d_y@Sa?{&|9$dQ@S>~Che`}KRj_tpCQ z%g_H>;Q9vcZ{hwY?(gCL!zb{A>&@qc_%v`6_!;02ffaBMcop~|unm0flR|tA_8)82=PrI0lxxV1D=O1ZvkJ#d%zEy_H*F7z+V8jfd2q~7x*eRc?EbA z$oneb1K8kzJpS*`B|FbS$*<%6HQ<+kSAlH*J>Yp@)qMXI@CM$03w#;)5%3)FzrZ`d zb0B30_%84w@DY&Z{1wRl_!sbtz!&htdb3VYK|rP1#ryMxsk4jM@bxL&?Aw=ce--!h zxUqG-m>b1+nRZ&W#eAp2ARj!k?|o zm}EujM3y6?mezW1qp}#S26U{Ap`Ie^3guLB=9rg9&4{cDdU~Z-Rmj9nlCVTF&moIN z2MY}p1?5UluTpNSLI-UtzZAdstugkPC+eU|--U={Sp{C^lq?q3@~-pM3-eepD#vMS zu~P+Si#q2$#BJ*X*?yEVa(1LZivkt=1(Y1?2htTJog%M{%7ZFMS5MD|c7;7&Vw0&i zh7N_wXun~#r*c_)MSbh2P+kwJXf!NtgpX}am4aYqVL^u^6%H96%W+1nwb4dvt!He0 zc)zwmZLH~yeG9cf-B}Blhy@SXbWl}bW_(Oj4`2!#f+>N9wp3JThi}%6$+JHshVkd4 zO~($@vuYtR0F_GXRJL|WZ>(KfP$}vNrIMXVFFBa9R}r5sxX-mY;9u=b6c z3f0FbXM&=tE_;{=0Y0cv`Ud~Glmb&elZIjFY&{rew43xZ63UNYag>zQq(xsF<}d3@386qFnMM6fa-*N*$2H8Lyt%lU|Y$3(I2gAIBmS{lo&ehEYRI&eW0a zlR8pvoXRBDpsaBwHCrSNj-{V(j;A~ajr|${khPeRCQM_I&5Z@?l|9acE;*F?+RQWP zhAev!X6(RMkY{{Uyw7R^q(pkRTSzV#!{9FY&h72_<}2B93n3Fg zc4vP)!ma^ne`#cp!dD1+QH}5Og*{)zXDr}ct@9EImGqsbZbKRo3 zbg3G}SHHe`h0^1CMd#OPY2z z+#w0hBV|yBkWQGYQ;W;F6fDI7uLhI9fGY?$s^o{RxQEyxW7I&qXDv>1qB3M8mFh@B zbcuYGvlf}_$@}6K2IYZuT0-?$VYLDmFr+O(1uCBOUZZrNDYGryCs{oNrDVymvLplGSwhWbe`T~8361pB{I#z_BpMwVY0d?HEFGElsl6)2i3HC)dmq+0pmBK^fd-)b z*`0I`+Z?GHW$6ECY}!MGqJvV!E@UmIT~p(T;1u3*AK^#=IcS%hR`Um(d0^vXI*@EQ zcw>L3t~x-k6$nrAIcfCeKRml0T;!pR4xWoKM@ zp0au2)6~xSq~;5!7NUkJMq!9vhHa>W9214iD7G}{=QUS`Sj3yAtrqv3iu3JZOn|5- z=V|QkF`}~8t-0B#;xU|(V={4FhZFQuFoS>%eDvdt;m#d!T;u1|%(;&v6u3)c4l-(> gj0E4}*5q-;|BPUeYvjTbx-4K(G*Y8dAhNsSf1!sVwEzGB literal 0 HcmV?d00001 diff --git a/app/examples/Control/Wizard/.lang/ca.po b/app/examples/Control/Wizard/.lang/ca.po new file mode 100644 index 00000000..b7610477 --- /dev/null +++ b/app/examples/Control/Wizard/.lang/ca.po @@ -0,0 +1,137 @@ +msgid "" +msgstr "" +"Project-Id-Version: Wizard\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-21 00:21+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FMain.class:114 +msgid "Amount" +msgstr "Quantitat" + +#: FMain.class:124 +msgid "Article" +msgstr "-" + +#: FMain.class:169 +msgid "As this is only an example,
nothing will be ordered,
nothing will be saved,
and nothing will be sent." +msgstr "Ja que això és només un exemple,
res s'ordenarà,
res es desarà,
ni res s'enviarà." + +#: FMain.class:147 +msgid "image files of the Gambas logo" +msgstr "Fitxers d'imatges del logotip del Gambas" + +#: FMain.class:95 +msgid "Introduction" +msgstr "Introducció" + +#: FMain.class:108 +msgid "I order, according to the terms & conditions, the following items:" +msgstr "Ordeno, d'acord amb els termes i condicions, els següents elements:" + +#: FMain.class:152 +msgid "I want to read the terms and conditions" +msgstr "Vull llegir els termes i condicions" + +#: FMain.class:158 +msgid "I want to save my order" +msgstr "Vull desar la meva comanda" + +#: FMain.class:138 +msgid "kg of frozen shrimps" +msgstr "kg de gambes congelades" + +#: FMain.class:129 +msgid "lines of code from gambas 3" +msgstr "Línies de codi del Gambas 3" + +#: FMain.class:100 +msgid "" +"Most of the properties of this wizard are selected in the properties window, not by code. This is because for designing the wizard you need the properties window anyway. \n" +"

\n" +"

To see the properties window, you have to do save the project in yout home directory so that it is not write protected anymore.
\n" +"

\n" +"To design the pages of the wizard (or to see the properties), just click in the IDE on the \"Next\" button.\n" +"

\n" +"The essential property of the wizard container is Count. This defines the number of steps the user will have to click through. On the last page the button \"Next\" changes automatically to \"OK\" to finish the task.\n" +"

\n" +"In some situations you might want to skip steps of the wizard. You find this in the code of the CheckBoxes in step 2.\n" +"

\n" +msgstr "" +"La majoria de propietats d'aquest assistent es seleccionen a la finestra de propietats, i no al codi. Això es deu al fet que per dissenyar l'assistent es necessita la finestra de propietats de totes maneres. \n" +"

\n" +"

Per veure la finestra de propietats, l'heu de desar al directori arrel del vostre projecte projecte, així ja no estarà protegit contra escriptura.
\n" +"

\n" +"Per dissenyar les pàgines de l'assistent (o veure'n les propietats), a l'IDE feu clic al botó «Següent».\n" +"

\n" +"La propietat essencial del contenidor de l'assistent és Count. Aquesta defineix el nombre de passos que l'usuari haurà de fer. A l'última pàgina el botó «Següent» canvia automàticament a «D'acord» per acabar la tasca.\n" +"

\n" +"En algunes situacions podeu ometre els passos de l'assistent. Trobareu això en el codi de les caselles de verificació en el pas 2.\n" +"

\n" + +#: FMain.class:177 +msgid "Please enter your address here:" +msgstr "Introduïu la vostra adreça aquí:" + +#: FMain.class:184 +msgid "Save your order" +msgstr "Deseu la vostra comanda" + +#: FMain.class:196 +msgid "Send your order" +msgstr "Envieu la vostra comanda" + +#: FMain.class:162 +msgid "Terms and Conditions" +msgstr "Termes i condicions" + +#: FMain.class:204 +msgid "The following items:" +msgstr "Els següents articles:" + +#: FMain.class:189 +msgid "Where do you want to save your order?" +msgstr "On voleu desar la vostra comanda?" + +#: FMain.class:209 +msgid "will be delivered to:" +msgstr "serà lliurat a:" + +#: .project:1 +msgid "Wizard example" +msgstr "Exemple d'assistent" + +#: FMain.class:26 +msgid "" +"You didn't enter your address.\n" +"Your order can't be submitted." +msgstr "" +"No heu entrat la vostra adreça.\n" +"No es pot processar la vostra comanda." + +#: FMain.class:173 +msgid "Your address" +msgstr "La vostra adreça " + +#: FMain.class:103 +msgid "Your order" +msgstr "La vostra comanda" + +#: FMain.class:219 +msgid "Your order is now ready to be sent. Please check if everything is correct." +msgstr "La vostra comanda està llesta per a ser enviada. Reviseu si tot és correcte." + +#: FMain.class:39 +msgid "" +"Your order was submitted successfully.\n" +"The wizard will close now." +msgstr "" +"La vostra comanda s'ha processat correctament.\n" +"L'assistent s'acaba aquí." + diff --git a/app/examples/Control/Wizard/.lang/cs.mo b/app/examples/Control/Wizard/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..aae9201e2f0b87549d5d119d29f9353247cbdda8 GIT binary patch literal 4008 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\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Wizard example" +msgstr "Příklad průvodce" + +#: FMain.class:26 +msgid "" +"You didn't enter your address.\n" +"Your order can't be submitted." +msgstr "" +"Nezadali jste Vaši adresu.\n" +"Vaše objednávka je nedoručitelná." + +#: FMain.class:39 +msgid "" +"Your order was submitted successfully.\n" +"The wizard will close now." +msgstr "" +"Vaše objednávka byla úspěšně odeslána.\n" +"Průvodce bude ukončen." + +#: FMain.form:38 +msgid "Introduction" +msgstr "Úvod" + +#: FMain.form:43 +msgid "" +"Most of the properties of this wizard are selected in the properties window, not by code. This is because for designing the wizard you need the properties window anyway. \n" +"

\n" +"

To see the properties window, you have to do save the project in yout home directory so that it is not write protected anymore.
\n" +"

\n" +"To design the pages of the wizard (or to see the properties), just click in the IDE on the \"Next\" button.\n" +"

\n" +"The essential property of the wizard container is Count. This defines the number of steps the user will have to click through. On the last page the button \"Next\" changes automatically to \"OK\" to finish the task.\n" +"

\n" +"In some situations you might want to skip steps of the wizard. You find this in the code of the CheckBoxes in step 2.\n" +"

\n" +msgstr "" +"Většina z vlastností tohoto průvodce jsou vybrané v okně vlastností, nikoli v kódu. Je to proto, že pro navrhování průvodce budete potřebovat okno vlastností tak jako tak. \n" +"

\n" +"

Chcete-li zobrazit v okně vlastností, co musíte udělat, uložit projekt ve svém domovském adresáři, a ten není chráněn proti zápisu.
\n" +"

\n" +"Chcete-li návrh stránkek průvodce (nebo viz vlastnosti), stačí kliknout na IDE na \"Další\" tlačítko.\n" +"

\n" +"V základní vlastnost je průvodce kontejneru Count(počet). Toto určuje počet kroků, které bude muset uživatele proklikat. Na poslední stráncena tlačítko \"Další\" se automaticky změní na \"OK\"prodokončení úkolu.\n" +"

\n" +"V některých situacích byste mohli chtít přeskočit kroky průvodce. Najdete to v kódu políček v kroku 2.\n" +"

\n" + +#: FMain.form:46 +msgid "Your order" +msgstr "Váše objednávka" + +#: FMain.form:51 +msgid "I order, according to the terms & conditions, the following items:" +msgstr "Objednávka, v souladu s podmínkami, následující položky:" + +#: FMain.form:57 +msgid "Amount" +msgstr "Částka" + +#: FMain.form:67 +msgid "Article" +msgstr "Předmět" + +#: FMain.form:72 +msgid "lines of code from gambas 3" +msgstr "řádků ku z gambasu 3" + +#: FMain.form:81 +msgid "kg of frozen shrimps" +msgstr "kg mražených krevet" + +#: FMain.form:90 +msgid "image files of the Gambas logo" +msgstr "obrazové soubory s logem Gambasu" + +#: FMain.form:95 +msgid "I want to read the terms and conditions" +msgstr "Chci si přečíst podmínky" + +#: FMain.form:101 +msgid "I want to save my order" +msgstr "Chci uložit moji objednávku" + +#: FMain.form:105 +msgid "Terms and Conditions" +msgstr "Podmínky" + +#: FMain.form:112 +msgid "As this is only an example,
nothing will be ordered,
nothing will be saved,
and nothing will be sent." +msgstr "Protože se jedná pouze o příklad,
nic se neobjedná,
nic se neuloženo
a nic nebude posláno." + +#: FMain.form:116 +msgid "Your address" +msgstr "Vaše adresa" + +#: FMain.form:120 +msgid "Please enter your address here:" +msgstr "Prosím, vložte sem vaši adresu:" + +#: FMain.form:127 +msgid "Save your order" +msgstr "Uložit objednávku" + +#: FMain.form:132 +msgid "Where do you want to save your order?" +msgstr "Kam chcete uložit objednávku?" + +#: FMain.form:139 +msgid "Send your order" +msgstr "Poslat objednávku" + +#: FMain.form:147 +msgid "The following items:" +msgstr "Následující položky:" + +#: FMain.form:152 +msgid "will be delivered to:" +msgstr "bude dodáno:" + +#: FMain.form:162 +msgid "Your order is now ready to be sent. Please check if everything is correct." +msgstr "Vaše objednávka je nyní připravena k odeslání. Zkontrolujte prosím, zda je vše v pořádku." diff --git a/app/examples/Control/Wizard/.lang/de.mo b/app/examples/Control/Wizard/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..6c41b011c2d3b03baa43b49e56db168f8af3750c GIT binary patch literal 4124 zcmaKv&2J<}8OEC>kYp0R!uN-uWI+-V&n#buXmFez+mp=@d%di^SuGq;O?S<7?Q~ZU z-PJp5i@yMgBNr~Y>?z{R0j-pS4_rAQapMFxBqT0KocO)fJ)W`Gq2-=ux~uA~=Y8t^ z82|01XMP)TeU@k3Dd{R{YM@W0@j;PY%M_HKe5@EH6F_zQ3V zegJ+G{3j^(UU)8wz78tzi{KU*;maNH6MQ{-zVYKeXn5A(4e(Fk>)>;2@-p}%Q1<&d z9~K`b?fXA}s@dm1p!oYDWM!XkgI@ys;A`Md!Ow!f0`Gyp0mYAhfTF{{!5F;8!x1%$Xw+EPR635aC8m7=1Eaht z^Gdy};=(1?+rm|w;r*n@vSJ|uUAdJRzp?lVC2Sw#oF$q@YWidC%Vvs`KAj!g>>M~KfG^#S0iM>gbb(d$uE-4n9;<6f_z#%a` zb=;~tHnFZNqb5bE5>wf!3r^#4Yy70Bl`|L?71lU3RymN9g+A%2&enXpvo+3&c=pS> z@Me3C+3?cSrJm;=zls!adYbxoH`Rlh1Bo*m&z3@u z_J)LJFt%~aJn|cATzg-*Ub7nxnM#7o>TD?=N8KE0D=|^wwRNTxkI-#bQb5?Z$CQ`} zqml_mxjG*+5d)Ps^X47rS2iiI)U#pe)3T_iX-^$C{4x#sIUWZ~4NcXOj#KSK3SIjm z*Ce0LNH=8IIDB_Q5DKxWwaUL8`Wqr*evLYO7Zl|d~&x2}a=Pla{e)rUU zLIArGTX^0^uEgh3pxxBOvz_7*#26Ki)eoATquZI$#4XC=Jb_Xwog^h%tCSMZkB%gs z!uwDZq9emTYtP3ap(QDI&r>=&M&YM2`kok+5M}kssf*^{itaVJ*jA_LK64YBxL5oY z6nhX&xFU)|Q3fUxXqP&PfbN4O6W!u}G)vM45-C zJZZ`#Yba}yN$nO5gN3eEoAWJ>k#SNJ0KJYV(uQd$vT;_Rx+@mF$mSAC6Pv9v=%&uc z7|V)j5zVG@ij%T9H5^@9+I(I`SqNm_hf`)SSJP(A52IxfPfTXvEG4JtN4LvD%0YLO zbl)>&MU3^;?ZNK5gPTM3-tf-R=;7Ct%)hSLx(mL;l6rfY<(v>FeGMZh%=K#dz!F9a7Ta`a$?8WYHm$N z>F}ZLEi4_u**NtTK}E`BSu@bSBgyB6VOmL;Uy}i=A(F&MAB@xGVxy)eKYM)kK4sIX zo2H7>v&R!(jg643;pQY|0Mb_4HZ19a7|75501K(z<|Eb-ImTU>o8yzgslXfKYDv~b zL+dg_opIA>4~xuXl2g21PUcCpL7hQWg;AMMNrACB6X6JZJ>l(yuTnVJ4nZ1`8W~i` z!K|VzNT0lCY^HL4cAVC^+6xU*QS%n;Q?v^hNC^5X?IUxY&x$-JF|TTocmL1Y)0&J< zjoVatT~WCcDhm6AgqVf?n21_==#Ub8r*f`T>6K`kYEjUCmY$ikR47myJH~4c@`*o{ zHtIj3R|-3%K0+gRi0%zaW@>K?i;xoYJ1~g627szGv4-%x(W341bm?8uRZXrR9S9KYu4S5&NqNfHzn4!#|Anpf=)Y)U1?+H14I`?JS zQW0Ey4;^(=Nz|}#JyP`;t>QGZ=Injb{(u;)F$~FEMqyFp(KNG5X zDe>)ktmO<;ii4b*>?_e7JlI{fSSRFKd*@wtCx|mO76))8b`7Lt3#vW|!VMvY|eHOuGq;VnosrLeD>UL48%R15X6YRNcM zI|6fRf?vJPWt%hYT$?g$#0eln!**21HUFE36`{+|sxekOb(JaA=o^OS<=GR;l6bkP zim~Dcn1t#yI53m8P0~v-YYD9ORErv7%9NrX$xWQCZ`SMVNi;7@Vv`3BMxh4K&nd#a zXvbzrLhrOtkW6n{d3T>yUP&+KACJk@%+8*q\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Wizard example" +msgstr "Beispiel: Wizard" + +#: FMain.class:26 +msgid "" +"You didn't enter your address.\n" +"Your order can't be submitted." +msgstr "" +"Sie haben Ihre Adresse nicht angegeben.\n" +"Ihre Bestellung kann nicht übermittelt werden." + +#: FMain.class:39 +msgid "" +"Your order was submitted successfully.\n" +"The wizard will close now." +msgstr "" +"Ihre Bestellung wurde erfolgreich übermittelt.\n" +"Der Assistent wird nun geschlossen." + +#: FMain.form:38 +msgid "Introduction" +msgstr "Einleitung" + +#: FMain.form:43 +msgid "" +"Most of the properties of this wizard are selected in the properties window, not by code. This is because for designing the wizard you need the properties window anyway. \n" +"

\n" +"

To see the properties window, you have to do save the project in yout home directory so that it is not write protected anymore.
\n" +"

\n" +"To design the pages of the wizard (or to see the properties), just click in the IDE on the \"Next\" button.\n" +"

\n" +"The essential property of the wizard container is Count. This defines the number of steps the user will have to click through. On the last page the button \"Next\" changes automatically to \"OK\" to finish the task.\n" +"

\n" +"In some situations you might want to skip steps of the wizard. You find this in the code of the CheckBoxes in step 2.\n" +"

\n" +msgstr "" +"Die meisten Einstellungen dieses Assistenten sind im Eigenschaftenfenster eingestellt, nicht mittels Code. Weil man zum Design sowieso nicht um das Eigenschaftenfenster herumkommt. \n" +"

\n" +"

Um das Eigenschaftenfenster zu sehen, musst du das Projekt in deinem home-Verzeichnis speichern, wo es nicht mehr schreibgeschützt ist.
\n" +"

\n" +"Um die Seiten des Assistenten zu gestalten (oder die Eigenschaften anzusehen), klicke in der IDE einfach auf die \"Weiter\" Schaltfläche.\n" +"

\n" +"Die zentrale Eigenschaft des Assistenten ist Count. Damit wird die Anzahl der Schritte definiert, die der Anwender durchklicken muss. Auf der letzten Seite ändert sich \"Weiter\" automatisch in \"OK\", um den Assistenten abzuschließen.\n" +"

\n" +"In manchen Situationen mag man Schritte des Assistenten überspringen. Siehe hierzu den Code der CheckBoxen in Schritt 2.\n" +"

\n" + +#: FMain.form:46 +msgid "Your order" +msgstr "Ihre Bestellung" + +#: FMain.form:51 +msgid "I order, according to the terms & conditions, the following items:" +msgstr "Ich bestelle gemäß den Geschäftsbedingungen folgende Artikel:" + +#: FMain.form:57 +msgid "Amount" +msgstr "Menge" + +#: FMain.form:67 +msgid "Article" +msgstr "Artikel" + +#: FMain.form:72 +msgid "lines of code from gambas 3" +msgstr "Codezeilen aus Gambas 3" + +#: FMain.form:81 +msgid "kg of frozen shrimps" +msgstr "kg tiefgekühlte Scampi" + +#: FMain.form:90 +msgid "image files of the Gambas logo" +msgstr "Bilddateien des Gambas-Logos" + +#: FMain.form:95 +msgid "I want to read the terms and conditions" +msgstr "Ich möchte die Geschäftsbedingungen lesen" + +#: FMain.form:101 +msgid "I want to save my order" +msgstr "Ich möchte meine Bestellung speichern" + +#: FMain.form:105 +msgid "Terms and Conditions" +msgstr "Geschäftsbedingungen" + +#: FMain.form:112 +msgid "As this is only an example,
nothing will be ordered,
nothing will be saved,
and nothing will be sent." +msgstr "Da dies nur ein Beispiel ist,
wird nichts bestellt,
nichts gespeichert,
und nichts gesendet." + +#: FMain.form:116 +msgid "Your address" +msgstr "Ihre Adresse" + +#: FMain.form:120 +msgid "Please enter your address here:" +msgstr "Bitte geben Sie hier ihre Adresse ein:" + +#: FMain.form:127 +msgid "Save your order" +msgstr "Bestellung speichern" + +#: FMain.form:132 +msgid "Where do you want to save your order?" +msgstr "Wo möchten Sie Ihre Bestellung speichern?" + +#: FMain.form:139 +msgid "Send your order" +msgstr "Bestellung übermitteln" + +#: FMain.form:147 +msgid "The following items:" +msgstr "Die folgenden Artikel:" + +#: FMain.form:152 +msgid "will be delivered to:" +msgstr "werden geliefert an:" + +#: FMain.form:162 +msgid "Your order is now ready to be sent. Please check if everything is correct." +msgstr "Ihre Bestellung kann nun übermittelt werden. Bitte überprüfen Sie, ob alle Angaben korrekt sind." diff --git a/app/examples/Control/Wizard/.lang/nl.mo b/app/examples/Control/Wizard/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..1a0b06f60d7c91e7668caad9fb9666e34fdc4349 GIT binary patch literal 2333 zcmaKs&u<$=6vu~BewiPo;a4by4hKk)Vv{tbDyF~ECRC}Y8ntdBE}-%5^Lpa(j5Ra1 z>m2BnKLAc}1&I@9j$ApwKY%JEPTWu>?tI_u+A*at^6qEXGw=6%dvD?JmjY`M*Ku6G z;(80$*N@#Gl|2 z$o?#bA2VthgvB1T&P`b2WS{$vOId)!LQ@E1W3w&SiyP=I@-AA1n z66-<6t_~7EXU#+^E9s8v4-74zYx@hONtc{UvlFsMkDSzl*P@`%`*<3y-8OX+l z*eW$FOO<8N#j2x&2P7l;evog=%-LK=?QB?1|96}*Zp;UDq<&~Z#aP+MVxCk!C(Bk3 zr034DW3oRfO^YoXy~~Fg+YsA^R3Hi8$jgZvda3fCimtS2)ot z_}`9q`($P4dfgucVYDqzXD)}VcYRruGHYVmsW}-9p9vk}nqu3#Jvdz7%IiDi14`eN zRj$_>XBsQD?Hl!*G}63N-%yd7^3=-e>H6vlesXoK`Q90MyLF}ZkqWWi^2!FIqVuc- z0m_?-Vvy(9_T*BITp2fLkn*t`x_UrE?*`OxzPJDdwiqJfrnIJZee3$>fj_Ho16>r6 z|9WdOz>!FI;=2Q*wEaM4UF8GC^S4_c*3TZCV^=!l>zfw2z?3xQ*|v@~ahYV zvq=tV-^Vn}b}{z7O4%|@8ps8S!m;(SLcilAe%0&}20v}`IXq0qzQ-S-{}Ypj&?Ib& z5BBw*t!Bdw;H<*VZ1O3H9fyhEBg{L-U3N9zv~7wT{DG5G3G@n7=6tRkot^)1S!{4P z|MYklp(R%!jX08mspi?F&nh6U5xzO?c;FK!6FwC*f~$F>1{0-@9zN1(vrK+ElcT(t zM<_`F@k8nH^nojEhq8xE_>_6cWkPVU(BQ8ZzKVu8_KsABr%{*rgJ9C@2Iwes4l63B z8v)PRX8)=g=T3sHV1_83Z3xv#O$JMWdkUXRlzW6Ix`^ZfWJCFTBd8-Txiqb_W cOtWrK)k?OU_%*@CRHq=rpQvfGN(eQ@-(~}p5C8xG literal 0 HcmV?d00001 diff --git a/app/examples/Control/Wizard/.lang/nl.po b/app/examples/Control/Wizard/.lang/nl.po new file mode 100644 index 00000000..e9ba6980 --- /dev/null +++ b/app/examples/Control/Wizard/.lang/nl.po @@ -0,0 +1,107 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Wizard 3.6.0\n" +"PO-Revision-Date: 2014-10-10 13:56 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Wizard example" +msgstr "Wizard voorbeeld" + +#: FMain.form:38 +msgid "Introduction" +msgstr "Introducties" + +#: FMain.form:43 +msgid "Most of the properties of this wizard are selected in the properties window, not by code. This is because for designing the wizard you need the properties window anyway. \n

\n

To see the properties window, you have to do save the project in yout home directory so that it is not write protected anymore.
\n

\nTo design the pages of the wizard (or to see the properties), just click in the IDE on the \"Next\" button.\n

\nThe essential property of the wizard container is Count. This defines the number of steps the user will have to click through. On the last page the button \"Next\" changes automatically to \"OK\" to finish the task.\n

\nIn some situations you might want to skip steps of the wizard. You find this in the code of the CheckBoxes in step 2.\n

\n" +msgstr "" + +#: FMain.form:46 +msgid "Your order" +msgstr "Je order" + +#: FMain.form:51 +msgid "I order, according to the terms & conditions, the following items:" +msgstr "Ik bestel, volgens de voorwaarden en condities, de volgende items:" + +#: FMain.form:57 +msgid "Amount" +msgstr "Bedrag" + +#: FMain.form:67 +msgid "Article" +msgstr "Artikel" + +#: FMain.form:72 +msgid "lines of code from gambas 3" +msgstr "aantal code lijnen van gambas3" + +#: FMain.form:81 +msgid "kg of frozen shrimps" +msgstr "kg bevroren garnalen" + +#: FMain.form:90 +msgid "image files of the Gambas logo" +msgstr "afbeeldingsbestanden van het Gambas logo" + +#: FMain.form:95 +msgid "I want to read the terms and conditions" +msgstr "Ik wil de voorwaarden en condities lezen" + +#: FMain.form:101 +msgid "I want to save my order" +msgstr "Ik wil mijn order opslaan" + +#: FMain.form:105 +msgid "Terms and Conditions" +msgstr "Voorwaarden en condities" + +#: FMain.form:112 +msgid "As this is only an example,
nothing will be ordered,
nothing will be saved,
and nothing will be sent." +msgstr "Gezien dit slechts een voorbeeld is zal er,
niets besteld worden,
niets opgeslagen worden,
en niets verzonden worden." + +#: FMain.form:116 +msgid "Your address" +msgstr "Je adres" + +#: FMain.form:120 +msgid "Please enter your address here:" +msgstr "Voer je adres hier in:" + +#: FMain.form:127 +msgid "Save your order" +msgstr "Order opslaan" + +#: FMain.form:132 +msgid "Where do you want to save your order?" +msgstr "Waar wil je de order opslaan?" + +#: FMain.form:139 +msgid "Send your order" +msgstr "Verzend je order" + +#: FMain.form:147 +msgid "The following items:" +msgstr "De volgende items:" + +#: FMain.form:152 +msgid "will be delivered to:" +msgstr "zal afgeleverd worden aan:" + +#: FMain.form:162 +msgid "Your order is now ready to be sent. Please check if everything is correct." +msgstr "Je order is nu klaar voor verzending. Controleer of alles in orde is." + +#: FMain.class:34 +msgid "Your order was submitted successfully.\nThe wizard will close now." +msgstr "Je opdracht is succesvol ingedient.\nDe wizard zal nu sluiten." + +#: FMain.class:58 +msgid "You didn't enter your address.\nYour order can't be submitted." +msgstr "Je hebt je adres niet ingevoerd.\nDe order kan niet worden ingedient." + diff --git a/app/examples/Control/Wizard/.project b/app/examples/Control/Wizard/.project new file mode 100644 index 00000000..7cc164c2 --- /dev/null +++ b/app/examples/Control/Wizard/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.0 +Title=Wizard example +Startup=FMain +Icon=wizard.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.desktop +Component=gb.pcre +Authors="Matti, slightly modified by Benoît Minisini" +TabSize=2 +Translate=1 +Language=de +Packager=1 diff --git a/app/examples/Control/Wizard/.src/FMain.class b/app/examples/Control/Wizard/.src/FMain.class new file mode 100644 index 00000000..7b91b974 --- /dev/null +++ b/app/examples/Control/Wizard/.src/FMain.class @@ -0,0 +1,65 @@ +' Gambas class file + +Public Sub _new() + Me.Center +End + +Public Sub Wizard1_Cancel() ' user clicks abort + + Me.Close + +End + +Public Sub Wizard1_Change() ' user clicks next + + Select Case Wizard1.Index ' be aware that the index starts with 0! + + Case 1 + vbCode.setFocus + + Case 3 + txaAddress.SetFocus + + Case 5 + + txlOrder.Text = vbCode.Text & " " & lblCode.Text & "
" & vbFrozen.Text & " " & lblFrozen.Text & "
" & vbImg.Text & " " & lblImg.Text + txlAddress.Text = Replace(txaAddress.Text, "\n", "
") + + End Select + +End + +Public Sub Wizard1_Close() ' user clicks ok on the last page + + Message.Info(("Your order was submitted successfully.\nThe wizard will close now.")) + Me.Close + +End + +Public Sub chbTerms_Click() ' user wants (not) to read "terms & conditions" + + Wizard1[2].Enabled = chbTerms.Value ' step 3 is visible or not, according to this decision + +End + +Public Sub chbSave_Click() ' user wants (not) to save + + Wizard1[4].Enabled = chbSave.Value ' step 5 is visible or not, according to this decision + +End + +Public Sub Wizard1_BeforeChange() + + Select Case Wizard1.Index + + Case 4 + + If txaAddress.Text = "" Then + Message.Error(("You didn't enter your address.\nYour order can't be submitted.")) + Wizard1.Index = 3 ' move back to step 4 + Stop Event + Endif + + End Select + +End diff --git a/app/examples/Control/Wizard/.src/FMain.form b/app/examples/Control/Wizard/.src/FMain.form new file mode 100644 index 00000000..e629b59e --- /dev/null +++ b/app/examples/Control/Wizard/.src/FMain.form @@ -0,0 +1,118 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,83,59) + Resizable = False + { Wizard1 Wizard + MoveScaled(1,1,81,57) + Count = 6 + TextFont = Font["Bold,Italic,+3"] + ShowIndex = True + Index = 0 + Text = ("Introduction") + { TextLabel1 TextLabel + MoveScaled(2,2,76,43) + Font = Font["+1"] + Text = ("Most of the properties of this wizard are selected in the properties window, not by code. This is because for designing the wizard you need the properties window anyway. \n

\n

To see the properties window, you have to do save the project in yout home directory so that it is not write protected anymore.
\n

\nTo design the pages of the wizard (or to see the properties), just click in the IDE on the \"Next\" button.\n

\nThe essential property of the wizard container is Count. This defines the number of steps the user will have to click through. On the last page the button \"Next\" changes automatically to \"OK\" to finish the task.\n

\nIn some situations you might want to skip steps of the wizard. You find this in the code of the CheckBoxes in step 2.\n

\n") + } + Index = 1 + Text = ("Your order") + { TextLabel5 TextLabel + MoveScaled(2,2,75,4) + Font = Font["Bold,+1"] + Text = ("I order, according to the terms & conditions, the following items:") + } + { Label1 Label + MoveScaled(2,8,11,4) + Font = Font["+1"] + Text = ("Amount") + } + { vbCode ValueBox + MoveScaled(2,13,8,4) + } + { Label2 Label + MoveScaled(13,8,11,4) + Font = Font["+1"] + Text = ("Article") + } + { lblCode Label + MoveScaled(13,13,63,4) + Text = ("lines of code from gambas 3") + } + { vbFrozen ValueBox + MoveScaled(2,18,8,4) + } + { lblFrozen Label + MoveScaled(13,18,63,4) + Text = ("kg of frozen shrimps") + } + { vbImg ValueBox + MoveScaled(2,23,8,4) + } + { lblImg Label + MoveScaled(13,23,63,4) + Text = ("image files of the Gambas logo") + } + { chbTerms CheckBox + MoveScaled(2,31,75,5) + Text = ("I want to read the terms and conditions") + Value = CheckBox.True + } + { chbSave CheckBox + MoveScaled(2,37,40,4) + Text = ("I want to save my order") + Value = CheckBox.True + } + Index = 2 + Text = ("Terms and Conditions") + { TextLabel6 TextLabel + MoveScaled(2,2,76,41) + Font = Font["Serif,Bold,Italic,+1"] + Background = Color.TextBackground + Padding = 8 + Text = ("As this is only an example,
nothing will be ordered,
nothing will be saved,
and nothing will be sent.") + Border = Border.Plain + } + Index = 3 + Text = ("Your address") + { Label3 Label + MoveScaled(2,2,41,3) + Text = ("Please enter your address here:") + } + { txaAddress TextArea + MoveScaled(2,6,76,15) + } + Index = 4 + Text = ("Save your order") + { Label4 Label + MoveScaled(2,1,66,4) + Font = Font["Bold"] + Text = ("Where do you want to save your order?") + } + { DirChooser1 DirChooser + MoveScaled(2,5,76,38) + } + Index = 5 + Text = ("Send your order") + { txlOrder TextLabel + MoveScaled(4,13,70,8) + } + { Label6 Label + MoveScaled(2,9,22,3) + Text = ("The following items:") + } + { Label7 Label + MoveScaled(2,23,25,3) + Text = ("will be delivered to:") + } + { txlAddress TextLabel + MoveScaled(4,27,72,15) + } + { TextLabel2 TextLabel + MoveScaled(2,1,75,7) + Font = Font["Bold,+1"] + Text = ("Your order is now ready to be sent. Please check if everything is correct.") + } + Index = 0 + } +} diff --git a/app/examples/Control/Wizard/wizard.png b/app/examples/Control/Wizard/wizard.png new file mode 100644 index 0000000000000000000000000000000000000000..fff0e3f12ac0dfcd7a5137bca353a46c4d506c4d GIT binary patch literal 1510 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 COOzb| literal 0 HcmV?d00001 diff --git a/app/examples/Database/Database/.lang/ca.po b/app/examples/Database/Database/.lang/ca.po new file mode 100644 index 00000000..c99c5bb2 --- /dev/null +++ b/app/examples/Database/Database/.lang/ca.po @@ -0,0 +1,160 @@ +# Catalan translation of Database +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the Database package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Database\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-16 23:33+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FTest.class:136 +msgid "Active" +msgstr "Actiu" + +#: FTest.class:131 +msgid "Birth" +msgstr "Dia de naixement" + +#: FTest.class:60 +msgid "Bound controls example" +msgstr "Exemple de controls del límit" + +#: FMain.class:321 +msgid "" +"Bound controls\n" +"example..." +msgstr "" +"Controls del límit\n" +"exemple..." + +#: FTest.class:116 +msgid "Color" +msgstr "Color" + +#: FTest.class:146 +msgid "Comment" +msgstr "Comentari" + +#: FMain.class:249 +msgid "Connect" +msgstr "Connecta" + +#: FMain.class:220 +msgid "Connection" +msgstr "Connexió" + +#: FMain.class:298 +msgid "Create" +msgstr "Crea" + +#: FMain.class:282 +msgid "Create database if it does not exist" +msgstr "Crea la base de dades si no existeix" + +#: FMain.class:224 +msgid "Database" +msgstr "Base de dades" + +#: FMain.class:214 +msgid "Database example" +msgstr "Exemple de base de dades" + +#: FMain.class:287 +msgid "Debug" +msgstr "Depuració" + +#: FMain.class:303 +msgid "Delete" +msgstr "Suprimeix" + +#: FMain.class:308 +msgid "&Fill with" +msgstr "&Omple amb" + +#: FMain.class:255 +msgid "firebird" +msgstr "-" + +#: FTest.class:121 +msgid "First Name" +msgstr "Nom de pila" + +#: FMain.class:234 +msgid "Host" +msgstr "Ordinador central" + +#: FTest.class:111 +msgid "Id" +msgstr "Identificador" + +#: FMain.class:255 +msgid "mysql" +msgstr "-" + +#: FTest.class:126 +msgid "Name" +msgstr "Nom" + +#: FMain.class:255 +msgid "odbc" +msgstr "-" + +#: FMain.class:244 +msgid "Password" +msgstr "Contrasenya" + +#: FMain.class:255 +msgid "postgresql" +msgstr "-" + +#: FMain.class:326 +msgid "Records" +msgstr "Registres" + +#: FMain.class:337 +msgid "Run..." +msgstr "Executa..." + +#: FTest.class:141 +msgid "Salary" +msgstr "Salari" + +#: FMain.class:255 +msgid "sqlite3" +msgstr "-" + +#: FMain.class:333 +#: FRequest.class:27 +msgid "SQL request" +msgstr "Petició SQL" + +#: FMain.class:294 +msgid "Tables 'test' && 'color'" +msgstr "Taules «comprova» && «color»" + +#: FMain.class:265 +msgid "test" +msgstr "Comprova" + +#: FTest.class:78 +msgid "Test form" +msgstr "Formulari de prova" + +#: FMain.class:229 +msgid "Type" +msgstr "Tipus" + +#: FMain.class:239 +msgid "User" +msgstr "Usuari" + diff --git a/app/examples/Database/Database/.lang/cs.mo b/app/examples/Database/Database/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..02cfc1560b190b9d9767a9d29e390807f4df98c0 GIT binary patch literal 1795 zcmZ{iO>A355XUD_3YbEnl=4+zX{$sCUfK$QDicuM*lA4tRojWwf{@nlZQPBY-@5zO zP91STAOzw7JpfYG18T*kNa4hVOD|T$6^RQcBuy?q_L8~Qt*AA-F9 zBFOtMgB{sxGxWI+6>@A)>!=f3Cp zzPEn>vj4~4{wc`$eGYQ`FF=m}706fl2ITkr7JLN!0Ys_kSCIXF13Atgo;N)I^!&^7 zrsouj#r|CL+YPQIkEbUX=fG3Wnd9={Iy?l~1$h9%d2p}Y57`3|5WW-l4-d}!nelAW z%Ustdyv}#y;oEwm;LEk$4LJaL7V;?MNeHen9$c%ZAbTMP*)qML<5;X|caCzQ(*s2( zw1slQ^pi-0CUMrpX+~X;-CnF_XJ`MjBK!)?o7h-PRjfm_Q!f&qMKx}EwNS2?-FA?He^ z6`69eT-eU<=S zlXg}dYiXCE>E&TBPVm4fw=ECbMzO<)IrG=#I8#lV(JC zEjrO~^SVgXxDETOYSMsMQn#cq?%lDiBqJQu=*o5J==JXCT4InxKowpc(4tCXHk^f; z$nKCDy?*Mn`?n1v\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FMain.form:33 +msgid "Database example" +msgstr "Příklad databáze" + +#: FMain.form:39 +msgid "Connection" +msgstr "Připojení" + +#: FMain.form:43 +msgid "Database" +msgstr "Databáze" + +#: FMain.form:48 +msgid "Type" +msgstr "Typ" + +#: FMain.form:53 +msgid "Host" +msgstr "-" + +#: FMain.form:58 +msgid "User" +msgstr "Uživatel" + +#: FMain.form:63 +msgid "Password" +msgstr "Heslo" + +#: FMain.form:68 +msgid "Connect" +msgstr "Připojit" + +#: FMain.form:73 +msgid "firebird" +msgstr "-" + +#: FMain.form:73 +msgid "mysql" +msgstr "-" + +#: FMain.form:73 +msgid "odbc" +msgstr "-" + +#: FMain.form:73 +msgid "postgresql" +msgstr "-" + +#: FMain.form:73 +msgid "sqlite3" +msgstr "-" + +#: FMain.form:83 +msgid "test" +msgstr "-" + +#: FMain.form:98 +msgid "Create database if it does not exist" +msgstr "Vytvořit databázi když neexistuje" + +#: FMain.form:103 +msgid "Debug" +msgstr "Ladit" + +#: FMain.form:108 +msgid "Port" +msgstr "-" + +#: FMain.form:119 +msgid "Tables 'test' && 'color'" +msgstr "Takulky 'test' && 'color'" + +#: FMain.form:123 +msgid "Create" +msgstr "Vytvořit" + +#: FMain.form:128 +msgid "Delete" +msgstr "Smazat" + +#: FMain.form:133 +msgid "&Fill with" +msgstr "&Vyplnit s" + +#: FMain.form:146 +msgid "" +"Bound controls\n" +"example..." +msgstr "" +"Příklad na\n" +"vázané ovládací\n" +"prvky..." + +#: FMain.form:151 +msgid "Records" +msgstr "Záznamy" + +#: FMain.form:158 FRequest.class:26 +msgid "SQL request" +msgstr "SQL dotaz" + +#: FMain.form:162 +msgid "Run..." +msgstr "Spustit..." + +#: FTest.form:31 +msgid "Bound controls example" +msgstr "Příklad na vázané ovládací prvky" + +#: FTest.form:49 +msgid "Test form" +msgstr "Testovací formulář" + +#: FTest.form:82 +msgid "Id" +msgstr "-" + +#: FTest.form:87 +msgid "Color" +msgstr "-" + +#: FTest.form:92 +msgid "First Name" +msgstr "Jméno" + +#: FTest.form:97 +msgid "Name" +msgstr "Název" + +#: FTest.form:102 +msgid "Birth" +msgstr "Narození" + +#: FTest.form:107 +msgid "Active" +msgstr "Aktivita" + +#: FTest.form:112 +msgid "Salary" +msgstr "Plat" + +#: FTest.form:117 +msgid "Comment" +msgstr "Komentář" diff --git a/app/examples/Database/Database/.lang/de.mo b/app/examples/Database/Database/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..cc4bf9d53a0fd5509514c576f737625ccbd3137c GIT binary patch literal 1817 zcmZ{iO>A355XZMP6flI)FTzKGr7g*UKDQMDRVJXm#LuZ!I}WxJseq8y&trS(^S*R< z-6TB$C)5iS0&ze{2zo(WIKibi3vq=D;)KMB8y8L-K>WwqBm%+Ov%fd{HM6t-pN~v^ zD9}#BPQVrp2=P4l`d#SI&K(q@0G;1c*KSOp&j(=dJmya)bUf$xI6{}#ym zKL$D0XW+fyci=o8W&7;xB$c_ZavS$oc;cqSTWh9Cr%AIL>t7OyJ?bqk+c* zX9KzB|J5)(X-@`nIk|C-CUL>ju){FU=Rp|Pi?zo2b4?_Sb7TE*I}Uql^0lL7zAxX2 z8*7zYkoAs({wQn)b`th9Y!>!7jBCP;-{uL}G1w`Xn5*b4lbhNPM4Wnkoy4NHa2E5C z>B=-W-sYJr(3To*WK=8`|1~4Sj9ALE+@c>2$#~EhO82O9BbF>FPXV&4yy_@NQun0x zvYV5WCigf_J1@#%cJE^^Oqn{Pz9>^hIIE(q^KwNENnFaYt=bh6XH_}3ncJ>d&8-(r zO7ZPPb7VOG)+;q>>Dq{}p`|iqw?$iZGQ^+v7@L=Kb8q=)(RwPOC>YN=%w}wdt4Sg^u>XWdej6N?JeK`igpu&AR)^>4y}#9cAU0Y&#hr$Mxt7VD$~Kl3(E8uo}A~#xBU$g u@yOw71G{rwC^U*YJ&PAawYV6p&`Hw&qfwUV2vTO5qNZkqLoH(L|NaL5B7Eck literal 0 HcmV?d00001 diff --git a/app/examples/Database/Database/.lang/de.po b/app/examples/Database/Database/.lang/de.po new file mode 100644 index 00000000..8362dcd4 --- /dev/null +++ b/app/examples/Database/Database/.lang/de.po @@ -0,0 +1,153 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FMain.form:33 +msgid "Database example" +msgstr "Datenbank-Beispiel" + +#: FMain.form:39 +msgid "Connection" +msgstr "Verbindung" + +#: FMain.form:43 +msgid "Database" +msgstr "Datenbank" + +#: FMain.form:48 +msgid "Type" +msgstr "Typ" + +#: FMain.form:53 +msgid "Host" +msgstr "-" + +#: FMain.form:58 +msgid "User" +msgstr "Benutzer" + +#: FMain.form:63 +msgid "Password" +msgstr "Passwort" + +#: FMain.form:68 +msgid "Connect" +msgstr "Verbinden" + +#: FMain.form:73 +msgid "firebird" +msgstr "-" + +#: FMain.form:73 +msgid "mysql" +msgstr "-" + +#: FMain.form:73 +msgid "odbc" +msgstr "-" + +#: FMain.form:73 +msgid "postgresql" +msgstr "-" + +#: FMain.form:73 +msgid "sqlite3" +msgstr "-" + +#: FMain.form:83 +msgid "test" +msgstr "-" + +#: FMain.form:98 +msgid "Create database if it does not exist" +msgstr "Datenbank erstellen, falls sie nicht existiert" + +#: FMain.form:103 +msgid "Debug" +msgstr "Debuggen" + +#: FMain.form:108 +msgid "Port" +msgstr "-" + +#: FMain.form:119 +msgid "Tables 'test' && 'color'" +msgstr "Tabellen 'test' && 'color'" + +#: FMain.form:123 +msgid "Create" +msgstr "Erstellen" + +#: FMain.form:128 +msgid "Delete" +msgstr "Löschen" + +#: FMain.form:133 +msgid "&Fill with" +msgstr "&Füllen mit" + +#: FMain.form:146 +msgid "Bound controls\nexample..." +msgstr "Beispiel:\nGebundene\nSchaltflächen..." + +#: FMain.form:151 +msgid "Records" +msgstr "Datensätze" + +#: FMain.form:158 FRequest.class:26 +msgid "SQL request" +msgstr "SQL-Abfrage" + +#: FMain.form:162 +msgid "Run..." +msgstr "Ausführen..." + +#: FTest.form:31 +msgid "Bound controls example" +msgstr "Beispiel für gebundene Schaltflächen" + +#: FTest.form:49 +msgid "Test form" +msgstr "Testformular" + +#: FTest.form:82 +msgid "Id" +msgstr "ID" + +#: FTest.form:87 +msgid "Color" +msgstr "Farbe" + +#: FTest.form:92 +msgid "First Name" +msgstr "Vorname" + +#: FTest.form:97 +msgid "Name" +msgstr "Nachname" + +#: FTest.form:102 +msgid "Birth" +msgstr "Geburtsdatum" + +#: FTest.form:107 +msgid "Active" +msgstr "Aktiv" + +#: FTest.form:112 +msgid "Salary" +msgstr "Gehalt" + +#: FTest.form:117 +msgid "Comment" +msgstr "Kommentar" + diff --git a/app/examples/Database/Database/.lang/es.mo b/app/examples/Database/Database/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..8034a5d7c9ed0dbb949f530d9faca466fd87f6a9 GIT binary patch literal 1784 zcmb7?xsM!06vhi9GfT4$om31QIpfZ)eMNca5v6 z*IvXwKuCZ@MvBM~0U|&SjF1Bo2S!4cEQkmQ2oS{n-cIiUBB9i;eqGl)s=mAXc7H0+ zj=-LWoxD$o<6yK49opXeg{XjY-~-?Ra1VG8d=@+eJ_Rn8^L_9^j4zdZALR3^AfI>O zQSd`>FZen5IQRv~e!m5I-*u4B{Q&L;Z-JcBPv!V$kk8)*Io@v|pZf!R2>iPo@513{ zFrEYFz{4Q>J67@p$bMb}IsO~vdYLkm)OUhFTnJBNu%8}G7QhS*g zaxym_?zHowQLb+NY=vpixR{6rr37D1ZJn1Lm614aaIBSxjX_U2ch%TL^eG0M=odMs zKDZD`OIHfQ-hoP$T^B<&P7!*+V{Spt&&!3F3t13DOvn{uGcjDBk{CI%VntgTYxvF9 z-IY|BWE_hr0#7XQ7^XeFD%h9kS#udT5VVruB3XwUsL5V^`K|i7P+koCgI2dw>2-rX zt!Z8iP%_oz+hM&Qw7Ws0F3)#s?bgfhbUR@sQqBiMt8$krZ){CAM^Pj@^>!#vhwXYR zI$Liv`r%-323zwDIF(0LrE6c)}>B-JySGj>#E*q3s=;+S|EfTV!yjtTwDOT(V-}<&e&euV*^L z7;3XJRdPdQhylSFC!L1MW+H>qS$~^LJ465D48%4qEJ7?9%jj91k%gOVY)K5ruIU8I zXYI|Vkczi(ed8+y0ZyQ2Ya9}^mMqtZbyuWbNyu>~LlnX~RR5*JCL~xus+zA&PHjPB RC5C!xAjx9m4*riS?*TLkfnopv literal 0 HcmV?d00001 diff --git a/app/examples/Database/Database/.lang/es.po b/app/examples/Database/Database/.lang/es.po new file mode 100644 index 00000000..d41e9b09 --- /dev/null +++ b/app/examples/Database/Database/.lang/es.po @@ -0,0 +1,151 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FMain.class:211 +msgid "Database example" +msgstr "Ejemplo de base de datos" + +#: FMain.class:217 +msgid "Connection" +msgstr "Conexión" + +#: FMain.class:221 +msgid "Database" +msgstr "Base de datos" + +#: FMain.class:226 +msgid "Type" +msgstr "Tipo" + +#: FMain.class:231 +msgid "Host" +msgstr "Servidor" + +#: FMain.class:236 +msgid "User" +msgstr "Usuario" + +#: FMain.class:241 +msgid "Password" +msgstr "Contraseña" + +#: FMain.class:246 +msgid "Connect" +msgstr "Conectar" + +#: FMain.class:251 +msgid "firebird" +msgstr "firebird" + +#: FMain.class:251 +msgid "mysql" +msgstr "mysql" + +#: FMain.class:251 +msgid "odbc" +msgstr "odbc" + +#: FMain.class:251 +msgid "postgresql" +msgstr "postgresql" + +#: FMain.class:251 +msgid "sqlite3" +msgstr "sqlite3" + +#: FMain.class:261 +msgid "test" +msgstr "test" + +#: FMain.class:276 +msgid "Create database if it does not exist" +msgstr "Crear la base de datos si no existe" + +#: FMain.class:281 +msgid "Debug" +msgstr "Depurar" + +#: FMain.class:288 +msgid "Tables 'test' && 'color'" +msgstr "Tablas 'test' && 'color'" + +#: FMain.class:292 +msgid "Create" +msgstr "Crear" + +#: FMain.class:297 +msgid "Delete" +msgstr "Borrar" + +#: FMain.class:302 +msgid "&Fill with" +msgstr "&Llenar con" + +#: FMain.class:315 +msgid "" +"Bound controls\n" +"example..." +msgstr "" +"Ejemplo de\n" +"controles de acceso..." + +#: FMain.class:320 +msgid "Records" +msgstr "Registros" + +#: FMain.class:327 FRequest.class:26 +msgid "SQL request" +msgstr "Consulta SQL" + +#: FMain.class:331 +msgid "Run..." +msgstr "Correr..." + +#: FTest.class:53 +msgid "Bound controls example" +msgstr "Ejemplo de controles de acceso" + +#: FTest.class:71 +msgid "Test form" +msgstr "Formulario de prueba" + +#: FTest.class:104 +msgid "Id" +msgstr "Id" + +#: FTest.class:109 +msgid "Color" +msgstr "Color" + +#: FTest.class:114 +msgid "First Name" +msgstr "Primer nombre" + +#: FTest.class:119 +msgid "Name" +msgstr "Nombre" + +#: FTest.class:124 +msgid "Birth" +msgstr "Nacimiento" + +#: FTest.class:129 +msgid "Active" +msgstr "Activo" + +#: FTest.class:134 +msgid "Salary" +msgstr "Salario" + +#: FTest.class:139 +msgid "Comment" +msgstr "Comentario" diff --git a/app/examples/Database/Database/.lang/nl.mo b/app/examples/Database/Database/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..1dc610a1bf752784b54be2e81d08a2242cfdb6a5 GIT binary patch literal 1718 zcmZ9K&u?2r5XZN)(AGeKQp%6=V@r`O4$pPkP&E!kG)WCnlQwni6fPC}{3iA`&%5sK zKD)^ufy99`h!au~0>lLgi9;){hzp2IuUvYA8<&2^tewCn>ht2FQM|#q$rrzo6d$*?$w4INlAA^C9px(184&AA|T8 zcksjhz6IIek5RuD^;yd|v7jB41zVU*cB~1`=DtL?Rez9EwOak3 zMMjHQw7GTo?f1z9&=~4WsjU%;s{uH zw=G&K%@My6a8{A!vaEEtnu=)QKyF&s7p?69iMA&v`rH0iE^L-|!~j9Nj#!82KxYNJ z5NpofMD@vXmTZvosJ<>ICCRhZx2kis(%RLeNkh%X1U9P6Gqsu7q&AnFnUyo=>b06| zw-!q)%7>)ol<~O=*45>8^lMu-6$LNf9rYORDu1vD(-`sy;!?QnXKD{bawr>C5!aG~95Bf>I58N=qf>h!`0$2?^`W{GI)Ftp@oI%Y$1JC?To9W(8D&S?Bd bdnRp*%iA+)MXnJq;Wu%G-Wu;YDE~bKWMFAe literal 0 HcmV?d00001 diff --git a/app/examples/Database/Database/.lang/nl.po b/app/examples/Database/Database/.lang/nl.po new file mode 100644 index 00000000..73496e2f --- /dev/null +++ b/app/examples/Database/Database/.lang/nl.po @@ -0,0 +1,147 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Database 3.5.90\n" +"PO-Revision-Date: 2014-09-24 16:00 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FMain.form:33 +msgid "Database example" +msgstr "Database voorbeeld" + +#: FMain.form:39 +msgid "Connection" +msgstr "Verbinding" + +#: FMain.form:43 +msgid "Database" +msgstr "-" + +#: FMain.form:48 +msgid "Type" +msgstr "-" + +#: FMain.form:53 +msgid "Host" +msgstr "-" + +#: FMain.form:58 +msgid "User" +msgstr "Gebruiker" + +#: FMain.form:63 +msgid "Password" +msgstr "Wachtwoord" + +#: FMain.form:68 +msgid "Connect" +msgstr "Verbind" + +#: FMain.form:73 +msgid "postgresql" +msgstr "-" + +#: FMain.form:73 +msgid "mysql" +msgstr "-" + +#: FMain.form:73 +msgid "sqlite3" +msgstr "-" + +#: FMain.form:73 +msgid "odbc" +msgstr "-" + +#: FMain.form:83 +msgid "test" +msgstr "-" + +#: FMain.form:98 +msgid "Create database if it does not exist" +msgstr "Creëer database indien deze niet bestaat" + +#: FMain.form:103 +msgid "Debug" +msgstr "Foutenopsporing" + +#: FMain.form:108 +msgid "Port" +msgstr "Poort" + +#: FMain.form:119 +msgid "Tables 'test' && 'color'" +msgstr "Tabellen 'test' && 'kleur'" + +#: FMain.form:123 +msgid "Create" +msgstr "Creëer" + +#: FMain.form:128 +msgid "Delete" +msgstr "Verwijder" + +#: FMain.form:133 +msgid "&Fill with" +msgstr "&Vullen met" + +#: FMain.form:146 +msgid "Bound controls\nexample..." +msgstr "Gebonden controles\nvoorbeeld..." + +#: FMain.form:151 +msgid "Records" +msgstr "-" + +#: FMain.form:158 FRequest.class:26 +msgid "SQL request" +msgstr "SQL verzoek" + +#: FMain.form:162 +msgid "Run..." +msgstr "Uitvoeren..." + +#: FTest.form:31 +msgid "Bound controls example" +msgstr "Gebonden controles voorbeeld" + +#: FTest.form:49 +msgid "Test form" +msgstr "Test formulier" + +#: FTest.form:82 +msgid "Id" +msgstr "-" + +#: FTest.form:87 +msgid "Color" +msgstr "Kleur" + +#: FTest.form:92 +msgid "First Name" +msgstr "Voornaam" + +#: FTest.form:97 +msgid "Name" +msgstr "Naam" + +#: FTest.form:102 +msgid "Birth" +msgstr "Geboorte" + +#: FTest.form:107 +msgid "Active" +msgstr "Actief" + +#: FTest.form:112 +msgid "Salary" +msgstr "Salaris" + +#: FTest.form:117 +msgid "Comment" +msgstr "Commentaar" + diff --git a/app/examples/Database/Database/.project b/app/examples/Database/Database/.project new file mode 100644 index 00000000..9440cd6e --- /dev/null +++ b/app/examples/Database/Database/.project @@ -0,0 +1,20 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.8.90 +Title=Database example +Startup=FMain +Icon=database.png +Version=3.8.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.db +Component=gb.db.form +TabSize=2 +Translate=1 +Language=fr +Maintainer=fabien +Vendor=Princeton +Address=fabien@arcalis +License=General Public Licence +Packager=1 diff --git a/app/examples/Database/Database/.src/FMain.class b/app/examples/Database/Database/.src/FMain.class new file mode 100644 index 00000000..41dc05ab --- /dev/null +++ b/app/examples/Database/Database/.src/FMain.class @@ -0,0 +1,178 @@ +' Gambas class file + +Private $hConn As Connection + +Public Sub btnConnect_Click() + + Dim sName As String + + Try $hConn.Close + '$hConn = NEW Connection + + sName = txtName.Text + + With $hConn + .Type = cmbType.Text + .Host = txtHost.Text + .Port = txtPort.Text + .Login = txtUser.Text + .Password = txtPassword.Text + .Name = "" + End With + + If chkCreate.Value Then + + $hConn.Open + If Not $hConn.Databases.Exist(sName) Then + $hConn.Databases.Add(sName) + Endif + $hConn.Close + + Endif + + $hConn.Name = sName + $hConn.Open + + frmDatabase.Enabled = True + frmRequest.Enabled = True + +Catch + + Message.Error(DConv(Error.Text)) + +End + +Public Sub btnCreate_Click() + + Dim hTable As Table + + hTable = $hConn.Tables.Add("test") + + hTable.Fields.Add("id", db.Long) + hTable.Fields.Add("color", db.Integer,, 1) + hTable.Fields.Add("firstname", db.String, 16) + hTable.Fields.Add("name", db.String, 32) + hTable.Fields.Add("birth", db.Date) + hTable.Fields.Add("active", db.Boolean) + hTable.Fields.Add("salary", db.Float) + hTable.Fields.Add("comment", db.String) + hTable.Fields.Add("image", db.Blob) + + hTable.PrimaryKey = ["id"] + + hTable.Update + + hTable = $hConn.Tables.Add("color") + + hTable.Fields.Add("color", db.Serial) + hTable.Fields.Add("name", gb.String, 32) + hTable.Fields.Add("french", gb.String, 32) + + hTable.PrimaryKey = ["color"] + + hTable.Update + +Catch + + Message.Error(DConv(Error.Text)) + +End + +Public Sub btnDelete_Click() + + Try $hConn.Tables.Remove("test") + Try $hConn.Tables.Remove("color") + +End + +Public Sub btnFill_Click() + + Dim iInd As Integer + Dim rTest As Result + Dim rColor As Result + Dim sColor As String + Dim aName As String[] = ["Paul", "Pierre", "Jacques", "Antoine", "Mathieu", "Robert", "Stéphane", "Yannick", "Frédéric"] + Dim aFrench As String[] = ["Noir", "Blanc", "Rouge", "Vert", "Bleu", "Jaune", "Transparent"] + Inc Application.Busy + + $hConn.Begin + + rColor = $hConn.Create("color") + + For Each sColor In ["Black", "White", "Red", "Green", "Blue", "Yellow", "Transparent"] + + rColor!name = sColor + rColor!french = aFrench[iInd] + Inc iInd + rColor.Update + + Next + + rTest = $hConn.Create("test") + + For iInd = 1 To txtCount.Value + + rTest!id = iInd + rTest!color = Int(Rnd(6)) + 1 + rTest!firstname = aName[Int(Rnd(aName.Count))] + rTest!name = "Name #" & iInd + rTest!birth = CDate("01/01/1970") + Int(Rnd(10000)) + rTest!active = Int(Rnd(2)) + rTest!salary = Round(Rnd(1000, 10000), -2) + + rTest.Update + + Next + + $hConn.Commit + +Finally + + Dec Application.Busy + +Catch + + $hConn.Rollback + Message.Error(DConv(Error.Text)) + +End + +Public Sub btnRun_Click() + + Dim rData As Result + Dim hForm As FRequest + + rData = $hConn.Exec(txtRequest.Text) + hForm = New FRequest($hConn, rData) + hForm.Show + +'Catch + + 'Message.Error(DConv(Error.Text)) + +End + +Public Sub Form_Open() + + $hConn = New Connection + FRequest.Init = True + +End + +Public Sub Form_Close() + + $hConn.Close + +End + +Public Sub chkDebug_Click() + + DB.Debug = chkDebug.Value + +End + +Public Sub btnTest_Click() + + FTest.Show + +End diff --git a/app/examples/Database/Database/.src/FMain.form b/app/examples/Database/Database/.src/FMain.form new file mode 100644 index 00000000..a82964c5 --- /dev/null +++ b/app/examples/Database/Database/.src/FMain.form @@ -0,0 +1,117 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(43.5714,25,63,79) + Text = ("Database example") + Icon = Picture["database.png"] + Resizable = False + { Frame1 Frame + MoveScaled(1,1,61,40) + Text = ("Connection") + { Label5 Label + MoveScaled(2,19,14,4) + Text = ("Database") + } + { Label1 Label + MoveScaled(2,4,15,4) + Text = ("Type") + } + { Label2 Label + MoveScaled(2,9,15,4) + Text = ("Host") + } + { Label3 Label + MoveScaled(2,24,14,4) + Text = ("User") + } + { Label4 Label + MoveScaled(2,29,14,4) + Text = ("Password") + } + { btnConnect Button + MoveScaled(46,4,13,4) + Text = ("Connect") + } + { cmbType ComboBox + MoveScaled(18,4,24,4) + List = [("postgresql"), ("mysql"), ("sqlite3"), ("odbc")] + Text = (" ") + } + { txtHost TextBox + MoveScaled(18,9,24,4) + } + { txtName TextBox + MoveScaled(18,19,24,4) + Text = ("test") + } + { txtUser TextBox + MoveScaled(18,24,24,4) + MaxLength = 16 + } + { txtPassword TextBox + MoveScaled(18,29,24,4) + Password = True + } + { chkCreate CheckBox + MoveScaled(2,34,56,4) + Text = ("Create database if it does not exist") + } + { chkDebug CheckBox + MoveScaled(46,9,13,4) + Text = ("Debug") + } + { Label7 Label + MoveScaled(2,14,15,4) + Text = ("Port") + } + { txtPort TextBox + MoveScaled(18,14,24,4) + } + } + { frmDatabase Frame + MoveScaled(1,42,61,15) + Enabled = False + Text = ("Tables 'test' && 'color'") + { btnCreate Button + MoveScaled(2,4,12,4) + Text = ("Create") + } + { btnDelete Button + MoveScaled(2,9,12,4) + Text = ("Delete") + } + { btnFill Button + MoveScaled(18,4,16,4) + Text = ("&Fill with") + } + { txtCount SpinBox + MoveScaled(18,9,11,4) + MinValue = 10 + MaxValue = 100000 + Step = 1000 + Value = 10000 + } + { btnTest Button + MoveScaled(41,4,18,9) + Text = ("Bound controls\nexample...") + } + { Label6 Label + MoveScaled(30,9,10,4) + Text = ("Records") + } + } + { frmRequest Frame + MoveScaled(1,58,61,20) + Enabled = False + Text = ("SQL request") + { btnRun Button + MoveScaled(49,4,11,4) + Text = ("Run...") + Default = True + } + { txtRequest TextArea + MoveScaled(2,4,46,15) + Wrap = True + } + } +} diff --git a/app/examples/Database/Database/.src/FRequest.class b/app/examples/Database/Database/.src/FRequest.class new file mode 100644 index 00000000..6fc8b118 --- /dev/null +++ b/app/examples/Database/Database/.src/FRequest.class @@ -0,0 +1,131 @@ +' Gambas class file + +Static Public Init As Boolean + +Private $hConn As Connection +Private $rData As Result + +Public Sub _new(hConn As Connection, rData As Result) + + $hConn = hConn + $rData = rData + + RefreshTitle + + ReadData + + Me.Move(Int(Rnd(Desktop.W - Me.W)), Int(Rnd(Desktop.H - Me.H))) + +End + + +Private Sub RefreshTitle() + + Dim sTitle As String + + sTitle = ("SQL request") & " - " & $hConn.Name + + Me.Title = sTitle + +End + + +Private Sub ReadData() + + Dim hField As ResultField + Dim iInd As Integer + + Inc Application.Busy + + tbvData.Rows.Count = 0 + + tbvData.Columns.Count = $rData.Fields.Count + + For Each hField In $rData.Fields + + With hField + + 'PRINT .Name; ": "; .Type; " "; .Length + + tbvData.Columns[iInd].Text = .Name + tbvData.Columns[iInd].Width = WidthFromType(tbvData, .Type, .Length, .Name) + + End With + + Inc iInd + Next + + tbvData.Rows.Count = $rData.Count + +Finally + + Dec Application.Busy + +Catch + + Message.Error("Cannot exec request." & "\n\n" & DConv(Error.Text)) + +End + + +Public Sub tbvData_Data(Row As Integer, Column As Integer) + + $rData.MoveTo(Row) + + tbvData.Data.Text = Str($rData[tbvData.Columns[Column].Text]) + tbvData.Data.Background = Color.RGB((Row Mod 31) * 8, (Row Mod 17) * 15, (Row Mod 13) * 21) + tbvData.Data.Foreground = Color.White + +End + + +Private Function WidthFromType(hCtrl As Control, iType As Integer, iLength As Integer, sTitle As String) As Integer + + Dim iWidth As Integer + + Select Case iType + + Case gb.Boolean + iWidth = hCtrl.Font.TextWidth(Str(False)) + 32 + + Case gb.Integer + iWidth = hCtrl.Font.TextWidth("1234567890") + 16 + + Case gb.Long + iWidth = hCtrl.Font.TextWidth("12345678901234567890") + 16 + + Case gb.Float + iWidth = hCtrl.Font.TextWidth(CStr(Pi) & "E+999") + 16 + + Case gb.Date + iWidth = hCtrl.Font.TextWidth(Str(Now)) + 16 + + Case gb.String + If iLength = 0 Then iLength = 255 + iLength = Min(32, iLength) + iWidth = hCtrl.Font.TextWidth("X") * iLength + 16 + + End Select + + iWidth = Max(iWidth, hCtrl.Font.TextWidth(sTitle) + 8) + + Return iWidth + +End + +' Private Function rowcount() As Integer +' +' Dim rows As Integer +' +' rows = 0 +' Do +' $rData.MoveTo(rows) +' Inc rows +' Loop +' +' Catch +' +' Return rows +' +' End + diff --git a/app/examples/Database/Database/.src/FRequest.form b/app/examples/Database/Database/.src/FRequest.form new file mode 100644 index 00000000..ba9a0184 --- /dev/null +++ b/app/examples/Database/Database/.src/FRequest.form @@ -0,0 +1,12 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(29.1667,19.3333,77.5,54.1667) + Text = ("") + Arrangement = Arrange.Fill + { tbvData GridView + MoveScaled(1,1,36,29) + Mode = Select.Single + Header = GridView.Both + } +} diff --git a/app/examples/Database/Database/.src/FTest.class b/app/examples/Database/Database/.src/FTest.class new file mode 100644 index 00000000..91803ebd --- /dev/null +++ b/app/examples/Database/Database/.src/FTest.class @@ -0,0 +1,25 @@ +' Gambas class file + +'PRIVATE $hConn AS NEW Connection + +Public Sub Form_Open() + + 'DataSource2.Connection = DB.Current + +End + +Public Sub DataControl6_Validate(Value As Variant) + + If IsNull(Value) Then Return + If Value < 0 Or Value > 10000 Then + Stop Event + Endif + +End + + +' Public Sub DataSource1_BeforeSave(Data As Result) +' +' Data["color"] = DataSource2["color"] +' +' End diff --git a/app/examples/Database/Database/.src/FTest.form b/app/examples/Database/Database/.src/FTest.form new file mode 100644 index 00000000..fc1a02ad --- /dev/null +++ b/app/examples/Database/Database/.src/FTest.form @@ -0,0 +1,121 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(3,10,67,92) + Text = ("Bound controls example") + Arrangement = Arrange.Fill + Spacing = True + Margin = True + { DataSource2 DataSource + MoveScaled(1,1,65,90) + Arrangement = Arrange.Vertical + Spacing = True + Table = "color" + ReadOnly = True + { Label7 Label + MoveScaled(1,1,62,4) + Font = Font["Bold,+1"] + Background = Color.SelectedBackground + Foreground = Color.SelectedForeground + Padding = 4 + Text = ("Test form") + } + { DataBrowser2 DataBrowser + MoveScaled(1,6,62,10) + Control = False + Columns = ["name"] + Header = False + } + { DataSource1 DataSource + MoveScaled(1,17,63,72) + Expand = True + Arrangement = Arrange.Vertical + Spacing = True + Table = "test" + { DataBrowser1 DataBrowser + MoveScaled(7,4,52,14) + Expand = True + Columns = ["id", "active", "firstname"] + } + { HBox1 HBox + MoveScaled(2,21,59,50) + Spacing = True + { VBox2 VBox + MoveScaled(1,1,12,39) + Spacing = True + { Label1 Label + MoveScaled(1,3,9,4) + Text = ("Id") + } + { Label9 Label + MoveScaled(1,7,9,4) + Text = ("Color") + } + { Label2 Label + MoveScaled(1,11,10,4) + Text = ("First Name") + } + { Label3 Label + MoveScaled(1,15,9,4) + Text = ("Name") + } + { Label4 Label + MoveScaled(1,19,9,4) + Text = ("Birth") + } + { Label5 Label + MoveScaled(1,23,9,4) + Text = ("Active") + } + { Label6 Label + MoveScaled(1,27,9,4) + Text = ("Salary") + } + { Label8 Label + MoveScaled(1,31,9,4) + Text = ("Comment") + } + } + { VBox1 VBox + MoveScaled(14,1,44,48) + Expand = True + Spacing = True + { DataControl1 DataControl + MoveScaled(1,2,30,4) + Field = "id" + } + { DataCombo1 DataCombo + MoveScaled(1,7,30,4) + Field = "color" + Table = "color" + } + { DataControl2 DataControl + MoveScaled(1,12,30,4) + Field = "firstname" + } + { DataControl3 DataControl + MoveScaled(1,17,30,4) + Field = "name" + } + { DataControl4 DataControl + MoveScaled(1,22,30,4) + Field = "birth" + } + { DataControl5 DataControl + MoveScaled(1,27,30,4) + Field = "active" + } + { DataControl6 DataControl + MoveScaled(1,32,30,4) + Field = "salary" + } + { DataControl7 DataControl + MoveScaled(1,37,42,10) + Expand = True + Field = "comment" + } + } + } + } + } +} diff --git a/app/examples/Database/Database/.src/Form1.class b/app/examples/Database/Database/.src/Form1.class new file mode 100644 index 00000000..8239c98f --- /dev/null +++ b/app/examples/Database/Database/.src/Form1.class @@ -0,0 +1,83 @@ +' Gambas class file + +Private $hConn As New Connection +Private $res As Result +'------------------------------------------------- +Public Sub Form_Open() +Dim iCount As Integer +Dim hTable As Table +Dim rTest As Result +Dim sql As String + +'define the gridview layout +GridView1.header = GridView.Horizontal +GridView1.grid = True +GridView1.Rows.count = 0 +GridView1.Columns.count = 2 +GridView1.Columns[0].text = "ID" +GridView1.Columns[1].text = "Value" +GridView1.Columns[0].width = 55 +GridView1.Columns[1].width = 55 + + +With $hConn + .Type = "sqlite" + .host = User.home + .name = "" +End With + +' 'delete an existing test.sqlite +' If Exist(User.home & "/test.sqlite") Then +' Kill User.home & "/test.sqlite" +' Endif + +' 'create test.sqlite +' $hConn.Open +' $hConn.Databases.Add("test.sqlite") +' $hconn.Close + +'define the table sampleTable +$hconn.name = "test.sqlite" +$hConn.Open +' hTable = $hConn.Tables.Add("sampleTable") +' hTable.Fields.Add("s_seq", db.Integer) +' hTable.Fields.Add("s_rndm", db.Integer) +' hTable.PrimaryKey = ["s_seq"] +' hTable.Update +' +' 'fill the table with generated data +' $hconn.Begin +' rTest = $hConn.Create("sampleTable") +' For iCount = 1 To 1000000 +' rTest!s_seq = iCount +' rTest!s_rndm = Int(Rnd(0, 100)) +' rTest.Update +' Next +' $hConn.Commit + +'read the database +sql = "select s_seq as ID, s_rndm as Value from sampleTable" +$res = $hconn.Exec(sql) + +Catch +$hConn.Rollback +Message.Error(DConv(Error.Text)) + +End +'------------------------------------------------- +Public Sub Form_Activate() +'change the rowcount of the gridview from 0 to the number of records. +'This triggers the data handling event + +GridView1.Rows.Count = $res.Count +End +'------------------------------------------------- +Public Sub GridView1_Data(Row As Integer, Column As Integer) + $res.moveTo(row) + GridView1.Data.text = Str($res[GridView1.Columns[column].text]) +End +'------------------------------------------------- +Public Sub Form_Close() + $hconn.Close +End +'------------------------------------------------- \ No newline at end of file diff --git a/app/examples/Database/Database/.src/Form1.form b/app/examples/Database/Database/.src/Form1.form new file mode 100644 index 00000000..08c1b47e --- /dev/null +++ b/app/examples/Database/Database/.src/Form1.form @@ -0,0 +1,8 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,64,64) + { GridView1 GridView + MoveScaled(6,5,44,45) + } +} diff --git a/app/examples/Database/Database/database.png b/app/examples/Database/Database/database.png new file mode 100644 index 0000000000000000000000000000000000000000..e809be00a7a242c7d0723424ae6ac56f594d0b34 GIT binary patch literal 1142 zcmV-+1d02JP)kdg000C%Nkl0zzS9% z7Jwp^A|%8LRulnQfI?8(x^0})@z@^69(z1r_v5he%{a8B_v-3M=braD@B5tRyibV! z{}aOT7(ZrFh#w02S?$#?rOzEbJHC=!Y^*Taj1TMgXWl8+e*2g2=N|%#PrbLMwr;My zw$~fSEatI@WqcfWVm}r!kK;IsdxvYUZQZ;z^>oZ6gAnfg@Z67^(?M#ii&RvT$PkrI znN^5}{q~PO`;#Y`Q~-Z!E&gWp>m+Ithe|;}m}E(zCCC-n_+#fgU+qo+{=9hZ?!q~O z)FFbpL{L+g7|TVaQZATAf&F&-#>=}Tgk=1SnR8=fxxS*>EtjjQNi^gNr4og)+}POk za`y9(P(wKVkzJ=J+9>oT3R!g$^$H|Rc0W=Y zoMe!Pwj~|26Z{jAsmgeI_Ry3^M#%(VDDl80j5X9h=5}O88dC30z-thO<*6N|)KxR) zZ3%)@GU4U;aF-qEdC!Jf9jn6$!09woQ@7`y6&EaOxNqd8N{@fV4TZh~ZF_>TD;3K% zDC$$LyDzO|!km^(f0I^+nCQ4{q*NH;vdrVUcnlCn25N5D(9t%h5OkIDioQfmS1_<{ z-!oP{G^+m9V4OAT+eXg%l$JaGq2)j(ksw!ynzlt>(6j5`>iTB03BY{OsrSrz%Z5+5 zZrgd!C>%J%oH;ceZRb7qNZVLlVm6-uICWXmP-?8_z$Hs|ZL2wjeLD)vKBpsB%9NTe zYd4?L!I619aG)ncYD-r!;UKkR8GUVqYl2PL2?zb6nZ9qq^G5cp+0(NxMH0)SF=?*Kq*D7f zl~VI&ENW;-j0}u4)%EoZ6oQeLY&D zMsbqC`)e1jtvl;EL+==xwxq9AD8;I^^d0(-9T#2o&;zC2x5cWde)IXS-QKFL>RWY1 zLs!?Kp-dr4)l4~~WnR>F-@ZBP*}WHEzcXp;-}w9sf4H==r(wm4GiF4&hJT7)G?b~U z8`!dC)6n42?VG><*Z)}C7n?8q>f$THMdz(J>uo#ANePK1pR{e$hWFz3?{0thje!## zz#D_`>MNf<`-A?sij{>B^^}TA2Pt|o@5Z~EZ{6DY>Ff7S9P$I^8Uo+Ge&&0NpP9ap zEHzrS8AeB0cepd&*uT5~%U|F7pjU^FcJ9j9pE%p`@R3XZ3%msZw|{wOI{*Lx07*qo IM6N<$f-aytd;kCd literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/.action/FBrowser.action b/app/examples/Database/MySQLExample/.action/FBrowser.action new file mode 100644 index 00000000..96092003 --- /dev/null +++ b/app/examples/Database/MySQLExample/.action/FBrowser.action @@ -0,0 +1,70 @@ +# Gambas Action File 3.0 + +{ Actions + { Action About + Text = "About" + Shortcut = "Alt+A" + Picture = "icons/16/About.png" + } + { Action Backup + Text = "Backup" + Shortcut = "Alt+B" + Picture = "icons/16/Backup.png" + } + { Action Catalogs + Text = "Catalogs" + Shortcut = "Alt+C" + Picture = "icons/16/Catalogs.png" + } + { Action CloseAllForms + Text = "Close all forms" + Shortcut = "Ctrl+Alt+Esc" + Picture = "icon:/16/close" + } + { Action Closeform + Text = "Close form" + Shortcut = "Alt+Esc" + Picture = "icon:/16/close" + } + { Action Help + Text = "Help" + Shortcut = "F1" + Picture = "icon:/16/help" + } + { Action Hidepanels + Text = "Hide panels" + Shortcut = "F8" + Picture = "icon:/16/view-split-h" + } + { Action Preferences + Text = "Preferences" + Shortcut = "Alt+P" + Picture = "icon:/16/options" + } + { Action Query + Text = "Query" + Shortcut = "Alt+Q" + Picture = "icons/16/Query.png" + } + { Action Restore + Text = "Restore Backup" + Shortcut = "Alt+R" + Picture = "icons/16/Restore.png" + } + { Action Script + Text = "Script" + Shortcut = "Alt+S" + Picture = "icons/16/Script2.png" + } + { Action Server + Text = "Server Information" + Shortcut = "Alt+I" + Picture = "icons/16/Server.png" + } + { Action Users + Text = "User Administration" + Shortcut = "Alt+U" + Picture = "icons/16/UserAdmin.png" + } +} + diff --git a/app/examples/Database/MySQLExample/.action/FNewIndex.action b/app/examples/Database/MySQLExample/.action/FNewIndex.action new file mode 100644 index 00000000..08278fb9 --- /dev/null +++ b/app/examples/Database/MySQLExample/.action/FNewIndex.action @@ -0,0 +1,10 @@ +# Gambas Action File 3.0 + +{ Actions + { Action Delete + Text = "Delete" + Shortcut = "Del" + Picture = "icon:/16/trash" + } +} + diff --git a/app/examples/Database/MySQLExample/.action/FNewRoutine.action b/app/examples/Database/MySQLExample/.action/FNewRoutine.action new file mode 100644 index 00000000..0ffc0ee4 --- /dev/null +++ b/app/examples/Database/MySQLExample/.action/FNewRoutine.action @@ -0,0 +1,9 @@ +# Gambas Action File 3.0 + +{ Actions + { Action Show a clue + Text = "Create Routine" + Picture = "icons/16/Routine.png" + } +} + diff --git a/app/examples/Database/MySQLExample/.action/FQuery.action b/app/examples/Database/MySQLExample/.action/FQuery.action new file mode 100644 index 00000000..aa989eea --- /dev/null +++ b/app/examples/Database/MySQLExample/.action/FQuery.action @@ -0,0 +1,14 @@ +# Gambas Action File 3.0 + +{ Actions + { Action NewTab + Text = "New Query" + Shortcut = "Ctrl+N" + Picture = "icons/16/ResultTab.png" + } + { Action Run + Text = "Run query" + Picture = "icons/16/Query.png" + } +} + diff --git a/app/examples/Database/MySQLExample/.action/FResult.action b/app/examples/Database/MySQLExample/.action/FResult.action new file mode 100644 index 00000000..7b32bcaa --- /dev/null +++ b/app/examples/Database/MySQLExample/.action/FResult.action @@ -0,0 +1,29 @@ +# Gambas Action File 3.0 + +{ Actions + { Action AddRow + Text = "Add row" + Picture = "icon:/16/add" + } + { Action Edit + Text = "Edit" + Picture = "icons/16/Edit.png" + } + { Action ExecuteStatements + Text = "Save to file" + Picture = "icon:/16/save" + } + { Action Lock + Text = "Lock" + Picture = "icons/16/Lock.png" + } + { Action Refresh + Text = "Refresh" + Picture = "icons/16/Refresh.png" + } + { Action RemoveRow + Text = "Remove row" + Picture = "icon:/16/remove" + } +} + diff --git a/app/examples/Database/MySQLExample/.action/FScript.action b/app/examples/Database/MySQLExample/.action/FScript.action new file mode 100644 index 00000000..ed443aa6 --- /dev/null +++ b/app/examples/Database/MySQLExample/.action/FScript.action @@ -0,0 +1,15 @@ +# Gambas Action File 3.0 + +{ Actions + { Action NewTab + Text = "New Script" + Shortcut = "Ctrl+N" + Picture = "icons/16/ScriptTab.png" + } + { Action Run + Text = "Run script" + Shortcut = "Ctrl+Return" + Picture = "icons/16/Script2.png" + } +} + diff --git a/app/examples/Database/MySQLExample/.action/FTables.action b/app/examples/Database/MySQLExample/.action/FTables.action new file mode 100644 index 00000000..00b8459d --- /dev/null +++ b/app/examples/Database/MySQLExample/.action/FTables.action @@ -0,0 +1,37 @@ +# Gambas Action File 3.0 + +{ Actions + { Action DeleteDatabase + Text = "Delete Database" + Picture = "icon:/16/remove" + } + { Action DeleteItem + Text = "Delete Item" + Shortcut = "Ctrl+Del" + Picture = "icon:/16/trash" + } + { Action EditItem + Text = "Edit Item" + Shortcut = "Ctrl+E" + Picture = "icon:/16/edit" + } + { Action Lock + Text = "Lock" + Picture = "icons/16/Lock.png" + } + { Action NewDatabase + Text = "New Database" + Picture = "icons/16/Database.png" + } + { Action NewItem + Text = "New Item" + Shortcut = "F2" + Picture = "icons/16/New.png" + } + { Action Refresh + Text = "Refresh" + Shortcut = "F5" + Picture = "icons/16/Refresh.png" + } +} + diff --git a/app/examples/Database/MySQLExample/.directory b/app/examples/Database/MySQLExample/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Database/MySQLExample/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Database/MySQLExample/.icon.png b/app/examples/Database/MySQLExample/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0f8466493051fa0414212f65152a1637de7d3239 GIT binary patch literal 4409 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 literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/.lang/ca.mo b/app/examples/Database/MySQLExample/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..0db2b8732b2dd53cf0d2b29062107ea83f6fa704 GIT binary patch literal 6760 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\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FNewField.class:114 +msgid "Acepts NULLs:" +msgstr "Accepta NULLs:" + +#: FNewField.class:79 +msgid "Add Field" +msgstr "Afegeix camp" + +#: .project:2 +msgid "A simple GB.MYSQL Example" +msgstr "Un exemple sensill de GB.MYSQL" + +#: FNewTable.class:122 +#: FNewTrigger.class:146 +#: FNewView.class:101 +msgid "&Cancel" +msgstr "&Canceŀla" + +#: FTables.class:12 +msgid "Charset" +msgstr "Joc de caràcters" + +#: FNewDatabase.class:90 +#: FNewTable.class:112 +msgid "Charset:" +msgstr "Joc de caràcters:" + +#: FTables.class:23 +msgid "Check" +msgstr "Verificació" + +#: FConnect.class:142 +msgid "&Clear" +msgstr "&Neteja" + +#: FTables.class:13 +msgid "Collation" +msgstr "Coŀlació" + +#: FNewDatabase.class:79 +msgid "Collation:" +msgstr "Coŀlació:" + +#: FTables.class:30 +msgid "Column" +msgstr "Columna" + +#: FNewEvent.class:102 +#: FNewRoutine.class:119 +msgid "Comma separated values: Hola1 INT,Hola2 CHAR(5)" +msgstr "Valors separats per comes: Hola1 INT,Hola2 CHAR(5)" + +#: FTables.class:43 +msgid "Comment" +msgstr "Comentari" + +#: FNewField.class:146 +msgid "Comment:" +msgstr "Comentari:" + +#: FConnect.class:135 +msgid "&Connect" +msgstr "&Connecta" + +#: FConnect.class:86 +msgid "Connect to a MySQL Server" +msgstr "Connecta a un servidor MySQL" + +#: FNewDatabase.class:100 +msgid "&Create" +msgstr "&Crea" + +#: FTables.class:79 +msgid "Created" +msgstr "Creat" + +#: FNewDatabase.class:53 +msgid "Create Database" +msgstr "Crea una base de dades" + +#: FNewEvent.class:71 +msgid "Create Event" +msgstr "Crea un esdeveniment" + +#: FNewIndex.class:101 +msgid "Create Index" +msgstr "Crea un índex" + +#: FNewRoutine.class:88 +msgid "Create Routine" +msgstr "Crea una rutina" + +#: FNewTable.class:75 +msgid "Create Table" +msgstr "Crea una taula" + +#: FNewTrigger.class:84 +msgid "Create Trigger" +msgstr "Crea un activador" + +#: FNewView.class:63 +msgid "Create View" +msgstr "Crea una vista" + +#: FTables.class:39 +msgid "Creation Time" +msgstr "Hora de creació" + +#: FTables.class:85 +msgid "Database Collation" +msgstr "Coŀlació de la base de dades" + +#: FTables.class:51 +msgid "DataType" +msgstr "Tipus de dada" + +#: FNewField.class:104 +msgid "Data Type:" +msgstr "Tipus de dada:" + +#: FTables.class:49 +msgid "Default" +msgstr "Per defecte" + +#: FNewField.class:126 +msgid "Default:" +msgstr "Per defecte:" + +#: FTables.class:21 +msgid "Definer" +msgstr "Definidor" + +#: FNewIndex.class:106 +msgid "Delete" +msgstr "Suprimeix" + +#: FTables.class:1336 +msgid "Delete Database" +msgstr "Suprimeix la base de dades" + +#: FTables.class:900 +msgid "Delete Event" +msgstr "Suprimeix l'esdeveniment" + +#: FTables.class:891 +msgid "Delete Field" +msgstr "Suprimeix el camp" + +#: FTables.class:888 +msgid "Delete Index" +msgstr "Suprimeix l'índex" + +#: FTables.class:1267 +msgid "Delete Item" +msgstr "Suprimeix l'element" + +#: FTables.class:894 +msgid "Delete Routine" +msgstr "Suprimeix la rutina" + +#: FTables.class:882 +msgid "Delete Table" +msgstr "Suprimeix la taula" + +#: FTables.class:897 +msgid "Delete Trigger" +msgstr "Suprimeix l'activador" + +#: FTables.class:885 +msgid "Delete View" +msgstr "Suprimeix la vista" + +#: FTables.class:931 +msgid "Do you realy want to delete the database: &1?" +msgstr "Segur que voleu suprimir la base de dades: &1?" + +#: FTables.class:1035 +msgid "Do you realy want to delete the event: &1?" +msgstr "Segur que voleu suprimir l'esdeveniment: &1?" + +#: FTables.class:1029 +msgid "Do you realy want to delete the field: &1?" +msgstr "Segur que voleu suprimir el camp: &1?" + +#: FTables.class:1027 +msgid "Do you realy want to delete the index: &1?" +msgstr "Segur que voleu suprimir l'índex: &1?" + +#: FTables.class:1031 +msgid "Do you realy want to delete the routine: &1?" +msgstr "Segur que voleu suprimir la rutina: &1?" + +#: FTables.class:1023 +msgid "Do you realy want to delete the table: &1?" +msgstr "Segur que voleu suprimir la taula: &1?" + +#: FTables.class:1033 +msgid "Do you realy want to delete the trigger: &1?" +msgstr "Segur que voleu suprimir l'activador: &1?" + +#: FTables.class:1025 +msgid "Do you realy want to delete the view: &1?" +msgstr "Segur que voleu suprimir la vista: &1?" + +#: FNewEvent.class:19 +msgid "Edit Event" +msgstr "Edita l'esdeveniment" + +#: FNewField.class:22 +msgid "Edit Field" +msgstr "Edita el camp" + +#: FNewIndex.class:24 +msgid "Edit Index" +msgstr "Edita l'índex" + +#: FTables.class:1260 +msgid "Edit Item" +msgstr "Edita l'element" + +#: FNewRoutine.class:19 +msgid "Edit Routine" +msgstr "Edita la rutina" + +#: FNewTable.class:21 +msgid "Edit Table" +msgstr "Edita la taula" + +#: FNewTrigger.class:28 +msgid "Edit Trigger" +msgstr "Edita l'activador" + +#: FNewView.class:18 +msgid "Edit View" +msgstr "Edita la vista" + +#: FTables.class:77 +msgid "Ends" +msgstr "Extrems" + +#: FTables.class:11 +msgid "Engine" +msgstr "Motor" + +#: FNewTable.class:101 +msgid "Engine:" +msgstr "Motor:" + +#: FTables.class:61 +msgid "Event" +msgstr "Esdeveniment" + +#: FNewTrigger.class:122 +msgid "Event:" +msgstr "Esdeveniment:" + +#: FTables.class:1416 +msgid "Events" +msgstr "Esdeveniments" + +#: FTables.class:72 +msgid "Execute At" +msgstr "Executa a" + +#: FTables.class:53 +msgid "Extra" +msgstr "-" + +#: FNewField.class:136 +msgid "Extra:" +msgstr "-" + +#: FTables.class:116 +msgid "Fields" +msgstr "Camps" + +#: FNewIndex.class:138 +msgid "Fields:" +msgstr "Camps:" + +#: FTables.class:149 +msgid "Fields on:" +msgstr "Camps a:" + +#: FTables.class:115 +msgid "Indexes" +msgstr "Índexs" + +#: FTables.class:367 +msgid "Indexes on:" +msgstr "Índexs a:" + +#: FTables.class:74 +msgid "Interval Field" +msgstr "Interval del camp" + +#: FTables.class:73 +msgid "Interval Value" +msgstr "Interval del valor" + +#: FTables.class:1248 +msgid "Item" +msgstr "Element" + +#: FTables.class:1038 +msgid "Item deleted." +msgstr "Element suprimit." + +#: FTables.class:52 +msgid "Key" +msgstr "Clau" + +#: FTables.class:80 +msgid "Last Altered" +msgstr "L'últim modificat" + +#: FTables.class:81 +msgid "Last Executed" +msgstr "L'últim executat" + +#: FTables.class:1292 +msgid "Lock" +msgstr "Bloqueig" + +#: FMessage.class:33 +msgid "Message" +msgstr "Missatge" + +#: .project:1 +#: FConnect.class:79 +msgid "MySQL Example" +msgstr "Exemple de MySQL" + +#: FConnect.class:71 +msgid "MySQL GUI" +msgstr "Interfície del MySQL" + +#: FTables.class:9 +msgid "Name" +msgstr "Nom" + +#: FNewTrigger.class:100 +#: FNewView.class:79 +msgid "Name:" +msgstr "Nom:" + +#: FTables.class:1329 +msgid "New Database" +msgstr "Base de dades nova" + +#: FTables.class:899 +msgid "New Event" +msgstr "Nou esdeveniment" + +#: FTables.class:890 +msgid "New Field" +msgstr "Nou camp" + +#: FTables.class:887 +msgid "New Index" +msgstr "Nou índex" + +#: FTables.class:1253 +msgid "New Item" +msgstr "Nou element" + +#: FTables.class:893 +msgid "New Routine" +msgstr "Nova rutina" + +#: FTables.class:881 +msgid "New Table" +msgstr "Nova taula" + +#: FTables.class:896 +msgid "New Trigger" +msgstr "Nou activador" + +#: FTables.class:884 +msgid "New View" +msgstr "Nova vista" + +#: FNewField.class:120 +msgid "No" +msgstr "-" + +#: FTables.class:931 +msgid "&No" +msgstr "-" + +#: FTables.class:334 +msgid "Not aviable" +msgstr "No disponible" + +#: FTables.class:50 +msgid "Nullable" +msgstr "Anuŀlable" + +#: FNewView.class:94 +msgid "&Ok" +msgstr "D'ac&ord" + +#: FTables.class:78 +msgid "On Completion" +msgstr "En finalitzar" + +#: FTables.class:82 +msgid "Originator" +msgstr "Original" + +#: FNewRoutine.class:114 +msgid "Parameters:" +msgstr "Paràmetres:" + +#: FConnect.class:123 +msgid "Password:" +msgstr "Contrasenya:" + +#: FConnect.class:101 +msgid "Port:" +msgstr "-" + +#: FNewIndex.class:151 +msgid "Primary Key" +msgstr "Clau primària" + +#: FTables.class:1274 +msgid "Refresh" +msgstr "Actualitza" + +#: FTables.class:38 +msgid "Returns" +msgstr "Retorna" + +#: FNewRoutine.class:125 +msgid "Returns:" +msgstr "Retorna:" + +#: FTables.class:1396 +msgid "Routines" +msgstr "Rutines" + +#: FTables.class:10 +msgid "Rows" +msgstr "Files" + +#: FNewEvent.class:97 +msgid "Schedule:" +msgstr "Planificador:" + +#: FTables.class:22 +msgid "Security" +msgstr "Seguretat" + +#: FConnect.class:90 +msgid "Server:" +msgstr "Servidor:" + +#: FNewView.class:88 +msgid "Show a clue" +msgstr "Mostrar una pista" + +#: FTables.class:14 +msgid "Size" +msgstr "Mida" + +#: FTables.class:75 +msgid "SQL Mode" +msgstr "Mode SQL" + +#: FTables.class:76 +msgid "Starts" +msgstr "Inici" + +#: FTables.class:62 +msgid "Table" +msgstr "Taula" + +#: FTables.class:1243 +msgid "Tables" +msgstr "Taules" + +#: FNewTrigger.class:110 +msgid "Time:" +msgstr "Hora:" + +#: FTables.class:70 +msgid "Time Zone" +msgstr "Zona horària" + +#: FTables.class:63 +msgid "Timing" +msgstr "Temps" + +#: FTables.class:1406 +msgid "Triggers" +msgstr "Activadors" + +#: FTables.class:32 +msgid "Type" +msgstr "Tipus" + +#: FTables.class:20 +msgid "Updatable" +msgstr "Actualitzable" + +#: FTables.class:15 +msgid "Update Time" +msgstr "Hora d'actualització" + +#: FConnect.class:112 +msgid "User:" +msgstr "Usuari:" + +#: FTables.class:1367 +msgid "Views" +msgstr "Vistes" + +#: FNewField.class:120 +msgid "Yes" +msgstr "Sí" + +#: FTables.class:931 +msgid "&Yes" +msgstr "&Sí" + diff --git a/app/examples/Database/MySQLExample/.lang/cs.mo b/app/examples/Database/MySQLExample/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..99ea5c78165c4ff27d10739a5fc87bec0cf25f87 GIT binary patch literal 6581 zcmb7{ZH!!18OM(mLDquuVhf6Lu&}h2*}hSMq1$%4JMC_}J3H)~c0tSCxo3B8=gvLD zy?17JmbVJgRHcFvqYcKmvBn@&CBy`Yi-g$?Cg=xaqaPp`1NuS0L$oXFfKM0)s7={-fZ<;MYL* zJ7Mb|xB5>(+P4UD{Us3p#3{=^TK*N}dhdX==R7QiqF4h`Uk9>%D|kJ)6WjtumfyGh zJ-7|^=VK7tyFuz2+zfsXr2VHX|6utSka2$t-#K+K(04#+Z|g!Z5i118pwH$SRMuG??WI=5#P3a4CMMxgK$;+0_3^+HAp*u zW9wfA+3$6b^Zw1&zYWrl^FCzSF9K=z8j$0!0NK95a+_s0$o<|2(ypSdzs>4Xwmk$_ zq5V!)?mM ze}S~;(o4-e>p=GF0_o>2kn8q=YrrDNb*DkDdxzEU0=W-g1Udc~$aTL1(vKg3^y6ue z>pc(h{QS=9uY$DqOzh1NaJIJ`a4Kfa^)*7w`x!xL(`+lwE z1~56NAjfS6dA@D}Iqxozaoh{izFR=fa|mSrD#*ArK=ywCq@53ga9RAo)-QszcL}6F zzXEB;OSb)G+x{op{su_@{|R#a)t8xlS_`s%9mu$C1!>nVkmqL5)*k|y7krTWt8M+A zAjcmC8J};0oPPnN{f~q6|4ESZ{S2htFIfH-q~9-s?Ek8*f6ekuTmKHobywjrAw-d4K0X+IN@bS3&yoU66MD5M;k6K*pIi zbV2TbFkbwdVlRYulKs-}b`(Bu73PC$A)eJ!@Vsw_VCv+X#cu^tFlzEkn{Tx`{aE>J zTdAOoqbfp(5225@K_(!$YU0z7{g5jmqmW@p`pwwF)!-atpVfDP{g6$N9*7J16r>yS z?r$CygOF(mpF(mzzEnEZgzVCqSgdBuyfEXN z=F*m7U5mY-)p69Bgs*m%(a@WoZcjSysae|uw>cNwlcQcm39ctc8&#D}iYz4s+92DZ z=v7lr&5wm8I;$xx9)iBYRv5Icx4N<3DkkbyF|MRatSadgsav+mxRol_EvqJO^=jGB zH3_HuhMaYRn6Y&et7JT*q-z73l{+SPc5d0RX>#W-@&5)XM*XZtOz}va)d-K9c~&Dr zv*Twq2B*naXE(wG`K-p&allR+kaxXUn%z%|X77`t*?Cjsz9*%0*AvC;xhdKmPn2Z8 z^MMA~6h^#4>5?4`N(T z$G0h?rH=FBi+}-WfRV%WTVM)5=Us3G++ zjSL02`%Iom9vs3jFK}WViXswU1s6UK+?2$e_4j2lKh%%-~R_Hz)gsvV;8_Cx#07 z4xWU}Xy^nHo)jHsrrCTyO7ieq>}Px(3rVoQlk-aw=IlkSpvP zimky`feX_D+uzxV$wr@+<<_a>lXn=01;br+SXMP(t#`k~ z2jEMHmy{<<*MsH)mt9fDsql5Js!F0yW7W7Jwd*cD;={a0<9YHoAE*HOHp%1fT=_OOtT^~!l-XR_v( zSJp&Tjkw24k1QQqG11a7CrVX?FfTpeM6zx+AlVJ@?u8t2)1*>chJGy7)C#@BX(o`+vnwSHSIb=s;cJ#G!3@sTx$s-aVN zYdE?kWE&YJ##QuQ0MT#F!#*pK&Wsib2gRm|`rw zHfbR%(vp`X2pLt)jo|gq;J+fX1%G7V@Y&&m(a)RKut@4#uQWWsBb`3sGbUAV82#ei zH-+SQA;&m)zWwG&T)(1@5J4t_dUG+tUk@|voz|_iY659QmBmCgY%bK%sg?MW=Nu;Q zuQVTJc1(Z=UDulnt%HnbKG9p-LiJH|xmaA3W=l*jxMK`AP04mRb_TN#ZcPhy%#c=t@b?ufAaP#PgjjZTXk*^u~^ub4} zP4cCQ+t6g7am+*k!NC6^rNpz(^w$u}-)b(nWxVZp_bb{JMIT|$ialz+s+NugOrhzs zKUQ{@pOG-2c``0*F|K^C%=QU~CyCh`Z+((O9m__N8H=higM9sD;d4@*XalzUDJi@pPK4vX{EWvS(d{HT@wNL8w)ql( literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/.lang/cs.po b/app/examples/Database/MySQLExample/.lang/cs.po new file mode 100644 index 00000000..97aeac61 --- /dev/null +++ b/app/examples/Database/MySQLExample/.lang/cs.po @@ -0,0 +1,536 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FConnect.form:28 +msgid "MySQL Example" +msgstr "Příklad MySQL" + +#: .project:2 +msgid "A simple GB.MYSQL Example" +msgstr "Jednoduchý příklad GB.MYSQL" + +#: FConnect.form:20 +msgid "MySQL GUI" +msgstr "-" + +#: FConnect.form:35 +msgid "Connect to a MySQL Server" +msgstr "Připoejní k MySQL Serveru" + +#: FConnect.form:39 +msgid "Server:" +msgstr "Server:" + +#: FConnect.form:50 +msgid "Port:" +msgstr "Port:" + +#: FConnect.form:61 +msgid "User:" +msgstr "Uživate:" + +#: FConnect.form:72 +msgid "Password:" +msgstr "Heslo:" + +#: FConnect.form:84 +msgid "&Connect" +msgstr "&Připojení" + +#: FConnect.form:91 +msgid "&Clear" +msgstr "&Vyčistit" + +#: FConnect.form:97 FNewDatabase.form:70 FNewEvent.form:65 FNewField.form:105 +#: FNewIndex.form:80 FNewRoutine.form:82 FNewTable.form:63 FNewTrigger.form:80 +#: FNewView.form:52 +msgid "&Cancel" +msgstr "&Zrušit" + +#: FMessage.form:8 +msgid "Message" +msgstr "Zpráva" + +#: FNewDatabase.form:16 +msgid "Create Database" +msgstr "Vytvořit databázi" + +#: FNewDatabase.form:32 FNewEvent.form:32 FNewField.form:37 FNewIndex.form:45 +#: FNewRoutine.form:35 FNewTable.form:32 FNewTrigger.form:34 FNewView.form:30 +msgid "Name:" +msgstr "Jméno:" + +#: FNewDatabase.form:42 +msgid "Collation:" +msgstr "Porovnání:" + +#: FNewDatabase.form:53 FNewTable.form:53 +msgid "Charset:" +msgstr "Charset:" + +#: FNewDatabase.form:63 +msgid "&Create" +msgstr "&Vytvořit" + +#: FNewEvent.class:19 +msgid "Edit Event" +msgstr "Upravit událost" + +#: FNewEvent.form:16 +msgid "Create Event" +msgstr "Vytvořit událost" + +#: FNewEvent.form:42 +msgid "Schedule:" +msgstr "Plán:" + +#: FNewEvent.form:47 FNewRoutine.form:50 +msgid "Comma separated values: Hola1 INT,Hola2 CHAR(5)" +msgstr "Hodnoty oddělené čárkami: Hola1 INT,Hola2 CHAR(5)" + +#: FNewEvent.form:52 FNewRoutine.form:69 FNewTrigger.form:67 FNewView.form:39 +msgid "Show a clue" +msgstr "Ukaž vodítko" + +#: FNewEvent.form:58 FNewField.form:98 FNewIndex.form:73 FNewRoutine.form:75 +#: FNewTable.form:70 FNewTrigger.form:73 FNewView.form:45 +msgid "&Ok" +msgstr "-" + +#: FNewField.class:22 +msgid "Edit Field" +msgstr "Upravit pole" + +#: FNewField.form:22 +msgid "Add Field" +msgstr "Přidej pole" + +#: FNewField.form:47 +msgid "Data Type:" +msgstr "Typ dat:" + +#: FNewField.form:57 +msgid "Acepts NULLs:" +msgstr "Akceptuj NULL:" + +#: FNewField.form:63 +msgid "No" +msgstr "Ne" + +#: FNewField.form:63 +msgid "Yes" +msgstr "Ano" + +#: FNewField.form:69 +msgid "Default:" +msgstr "Výchozí:" + +#: FNewField.form:79 +msgid "Extra:" +msgstr "-" + +#: FNewField.form:89 +msgid "Comment:" +msgstr "Poznámka:" + +#: FNewIndex.class:24 +msgid "Edit Index" +msgstr "Upravit index" + +#: FNewIndex.form:18 +msgid "Create Index" +msgstr "Vytvořit index" + +#: FNewIndex.form:23 +msgid "Delete" +msgstr "Smazat" + +#: FNewIndex.form:55 +msgid "Fields:" +msgstr "Pole:" + +#: FNewIndex.form:68 +msgid "Primary Key" +msgstr "Primární klíč" + +#: FNewRoutine.class:19 +msgid "Edit Routine" +msgstr "Upravit rutinu" + +#: FNewRoutine.form:19 +msgid "Create Routine" +msgstr "Vytvořit rutinu" + +#: FNewRoutine.form:45 +msgid "Parameters:" +msgstr "Parametry:" + +#: FNewRoutine.form:56 +msgid "Returns:" +msgstr "Návrat:" + +#: FNewTable.class:21 +msgid "Edit Table" +msgstr "Upravit tabulku" + +#: FNewTable.form:16 +msgid "Create Table" +msgstr "Vytvořit tabulku" + +#: FNewTable.form:42 +msgid "Engine:" +msgstr "Motor:" + +#: FNewTrigger.class:28 +msgid "Edit Trigger" +msgstr "Upravit spouštěč" + +#: FNewTrigger.form:18 +msgid "Create Trigger" +msgstr "Vytvořit spouštěč" + +#: FNewTrigger.form:44 +msgid "Time:" +msgstr "Čas:" + +#: FNewTrigger.form:56 +msgid "Event:" +msgstr "Událost:" + +#: FNewView.class:18 +msgid "Edit View" +msgstr "Upravit pohled" + +#: FNewView.form:14 +msgid "Create View" +msgstr "Vytvořit čas" + +#: FTables.class:9 +msgid "Name" +msgstr "Jméno" + +#: FTables.class:10 +msgid "Rows" +msgstr "Řádky" + +#: FTables.class:11 +msgid "Engine" +msgstr "Motor:" + +#: FTables.class:12 +msgid "Charset" +msgstr "Charset" + +#: FTables.class:13 +msgid "Collation" +msgstr "Porovnání" + +#: FTables.class:14 +msgid "Size" +msgstr "Velikost" + +#: FTables.class:15 +msgid "Update Time" +msgstr "Aktualizace času" + +#: FTables.class:20 +msgid "Updatable" +msgstr "Aktualizovatelné" + +#: FTables.class:21 +msgid "Definer" +msgstr "Definovat" + +#: FTables.class:22 +msgid "Security" +msgstr "Bezpečnost" + +#: FTables.class:23 +msgid "Check" +msgstr "Kontrola" + +#: FTables.class:30 +msgid "Column" +msgstr "Sloupec" + +#: FTables.class:32 +msgid "Type" +msgstr "Typ" + +#: FTables.class:38 +msgid "Returns" +msgstr "Návrat" + +#: FTables.class:39 +msgid "Creation Time" +msgstr "Čas vytvoření" + +#: FTables.class:43 +msgid "Comment" +msgstr "Poznámka" + +#: FTables.class:49 +msgid "Default" +msgstr "Výchozí" + +#: FTables.class:50 +msgid "Nullable" +msgstr "Nulovatelné" + +#: FTables.class:51 +msgid "DataType" +msgstr "Typ dat" + +#: FTables.class:52 +msgid "Key" +msgstr "Klíč" + +#: FTables.class:53 +msgid "Extra" +msgstr "-" + +#: FTables.class:61 +msgid "Event" +msgstr "Událost" + +#: FTables.class:62 +msgid "Table" +msgstr "Tabulka" + +#: FTables.class:63 +msgid "Timing" +msgstr "Časování" + +#: FTables.class:70 +msgid "Time Zone" +msgstr "Časová zóna" + +#: FTables.class:72 +msgid "Execute At" +msgstr "Při spuštění" + +#: FTables.class:73 +msgid "Interval Value" +msgstr "Interval hodnoty" + +#: FTables.class:74 +msgid "Interval Field" +msgstr "Interval pole" + +#: FTables.class:75 +msgid "SQL Mode" +msgstr "SQL mod" + +#: FTables.class:76 +msgid "Starts" +msgstr "Začínáme" + +#: FTables.class:77 +msgid "Ends" +msgstr "Končí" + +#: FTables.class:78 +msgid "On Completion" +msgstr "Na dokončení" + +#: FTables.class:79 +msgid "Created" +msgstr "Vytvořeno" + +#: FTables.class:80 +msgid "Last Altered" +msgstr "Poslední výstraha" + +#: FTables.class:81 +msgid "Last Executed" +msgstr "Poslední spuštění" + +#: FTables.class:82 +msgid "Originator" +msgstr "Průvodce" + +#: FTables.class:85 +msgid "Database Collation" +msgstr "Porovnání databází" + +#: FTables.class:112 +msgid "Fields" +msgstr "Pole" + +#: FTables.class:330 +msgid "Not aviable" +msgstr "Nedostupné" + +#: FTables.class:363 +msgid "Indexes on:" +msgstr "Indexy na:" + +#: FTables.class:880 +msgid "New View" +msgstr "Nový pohled" + +#: FTables.class:881 +msgid "Delete View" +msgstr "Smazat pohled" + +#: FTables.class:883 +msgid "New Index" +msgstr "Nový index" + +#: FTables.class:884 +msgid "Delete Index" +msgstr "Smazat index" + +#: FTables.class:886 +msgid "New Field" +msgstr "Nové pole" + +#: FTables.class:887 +msgid "Delete Field" +msgstr "Smazat pole" + +#: FTables.class:889 +msgid "New Routine" +msgstr "Nový rutina" + +#: FTables.class:890 +msgid "Delete Routine" +msgstr "Smazat rutinu" + +#: FTables.class:892 +msgid "New Trigger" +msgstr "Nový spouštěč" + +#: FTables.class:893 +msgid "Delete Trigger" +msgstr "Smazat spouštěč" + +#: FTables.class:895 +msgid "New Event" +msgstr "Nová událost" + +#: FTables.class:896 +msgid "Delete Event" +msgstr "Smazat událost" + +#: FTables.class:927 +msgid "&No" +msgstr "&Ne" + +#: FTables.class:927 +msgid "&Yes" +msgstr "&Ano" + +#: FTables.class:927 +msgid "Do you realy want to delete the database: &1?" +msgstr "Opravdu chcete smazat databázi: &1?" + +#: FTables.class:1019 +msgid "Do you realy want to delete the table: &1?" +msgstr "Opravdu chcete smazat tabulku: &1?" + +#: FTables.class:1021 +msgid "Do you realy want to delete the view: &1?" +msgstr "Opravdu chcete smazat pohled: &1?" + +#: FTables.class:1023 +msgid "Do you realy want to delete the index: &1?" +msgstr "Opravdu chcete smazat index: &1?" + +#: FTables.class:1025 +msgid "Do you realy want to delete the field: &1?" +msgstr "Opravdu chcete smazat pole: &1?" + +#: FTables.class:1027 +msgid "Do you realy want to delete the routine: &1?" +msgstr "Opravdu chcete smazat rutinu: &1?" + +#: FTables.class:1029 +msgid "Do you realy want to delete the trigger: &1?" +msgstr "Opravdu chcete smazat spouštěč: &1?" + +#: FTables.class:1031 +msgid "Do you realy want to delete the event: &1?" +msgstr "Opravdu chcete smazat událost: &1?" + +#: FTables.class:1034 +msgid "Item deleted." +msgstr "Položka smazána." + +#: FTables.form:35 +msgid "Tables" +msgstr "Tabulky" + +#: FTables.form:40 +msgid "Item" +msgstr "Položka" + +#: FTables.form:45 +msgid "New Item" +msgstr "Nová položka" + +#: FTables.form:52 +msgid "Edit Item" +msgstr "Upravit položku" + +#: FTables.form:59 +msgid "Delete Item" +msgstr "Smazat polozku" + +#: FTables.form:66 +msgid "Refresh" +msgstr "Obnovit" + +#: FTables.form:84 +msgid "Lock" +msgstr "Zámek" + +#: FTables.form:96 +msgid "New Table" +msgstr "Nová tabulka" + +#: FTables.form:103 +msgid "Delete Table" +msgstr "Smazat tabulku" + +#: FTables.form:121 +msgid "New Database" +msgstr "Nová databáze" + +#: FTables.form:128 +msgid "Delete Database" +msgstr "Smazat databázi" + +#: FTables.form:159 +msgid "Views" +msgstr "Pohled" + +#: FTables.form:169 +msgid "Indexes" +msgstr "Indexy" + +#: FTables.form:179 +msgid "Fields on:" +msgstr "Pole na:" + +#: FTables.form:188 +msgid "Routines" +msgstr "Rutiny" + +#: FTables.form:198 +msgid "Triggers" +msgstr "Spouštěče" + +#: FTables.form:208 +msgid "Events" +msgstr "Události" diff --git a/app/examples/Database/MySQLExample/.lang/de.mo b/app/examples/Database/MySQLExample/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..66cd4abd27975056ad42a303bd3d0b0a3e26a0b9 GIT binary patch literal 6656 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 z\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FConnect.form:28 +msgid "MySQL Example" +msgstr "MySQL-Beispiel" + +#: .project:2 +msgid "A simple GB.MYSQL Example" +msgstr "Ein einfaches gb.mysql-Beispiel" + +#: FConnect.form:20 +msgid "MySQL GUI" +msgstr "-" + +#: FConnect.form:35 +msgid "Connect to a MySQL Server" +msgstr "Zu einem SQL-Server verbinden" + +#: FConnect.form:39 +msgid "Server:" +msgstr "-" + +#: FConnect.form:50 +msgid "Port:" +msgstr "-" + +#: FConnect.form:61 +msgid "User:" +msgstr "Benutzer" + +#: FConnect.form:72 +msgid "Password:" +msgstr "Passwort:" + +#: FConnect.form:84 +msgid "&Connect" +msgstr "&Verbinden" + +#: FConnect.form:91 +msgid "&Clear" +msgstr "&Löschen" + +#: FConnect.form:97 FNewDatabase.form:70 FNewEvent.form:65 FNewField.form:105 +#: FNewIndex.form:80 FNewRoutine.form:82 FNewTable.form:63 FNewTrigger.form:80 +#: FNewView.form:52 +msgid "&Cancel" +msgstr "&Abbrechen" + +#: FMessage.form:8 +msgid "Message" +msgstr "Nachricht" + +#: FNewDatabase.form:16 +msgid "Create Database" +msgstr "Datenbank erstellen" + +#: FNewDatabase.form:32 FNewEvent.form:32 FNewField.form:37 FNewIndex.form:45 +#: FNewRoutine.form:35 FNewTable.form:32 FNewTrigger.form:34 FNewView.form:30 +msgid "Name:" +msgstr "-" + +#: FNewDatabase.form:42 +msgid "Collation:" +msgstr "Sortierfolge:" + +#: FNewDatabase.form:53 FNewTable.form:53 +msgid "Charset:" +msgstr "Zeichensatz:" + +#: FNewDatabase.form:63 +msgid "&Create" +msgstr "&Erstellen" + +#: FNewEvent.class:19 +msgid "Edit Event" +msgstr "Ereignis bearbeiten" + +#: FNewEvent.form:16 +msgid "Create Event" +msgstr "Ereignis erstellen" + +#: FNewEvent.form:42 +msgid "Schedule:" +msgstr "Zeitplan:" + +#: FNewEvent.form:47 FNewRoutine.form:50 +msgid "Comma separated values: Hola1 INT,Hola2 CHAR(5)" +msgstr "-" + +#: FNewEvent.form:52 FNewRoutine.form:69 FNewTrigger.form:67 FNewView.form:39 +msgid "Show a clue" +msgstr "Hinweis anzeigen" + +#: FNewEvent.form:58 FNewField.form:98 FNewIndex.form:73 FNewRoutine.form:75 +#: FNewTable.form:70 FNewTrigger.form:73 FNewView.form:45 +msgid "&Ok" +msgstr "-" + +#: FNewField.class:22 +msgid "Edit Field" +msgstr "Feld bearbeiten" + +#: FNewField.form:22 +msgid "Add Field" +msgstr "Feld hinzufügen" + +#: FNewField.form:47 +msgid "Data Type:" +msgstr "Datentyp:" + +#: FNewField.form:57 +msgid "Acepts NULLs:" +msgstr "Leere Eingabe möglich:" + +#: FNewField.form:63 +msgid "No" +msgstr "Nein" + +#: FNewField.form:63 +msgid "Yes" +msgstr "Ja" + +#: FNewField.form:69 +msgid "Default:" +msgstr "Standardwert:" + +#: FNewField.form:79 +msgid "Extra:" +msgstr "-" + +#: FNewField.form:89 +msgid "Comment:" +msgstr "Kommentar:" + +#: FNewIndex.class:24 +msgid "Edit Index" +msgstr "Index bearbeiten" + +#: FNewIndex.form:18 +msgid "Create Index" +msgstr "Index erstellen" + +#: FNewIndex.form:23 +msgid "Delete" +msgstr "Löschen" + +#: FNewIndex.form:55 +msgid "Fields:" +msgstr "Felder:" + +#: FNewIndex.form:68 +msgid "Primary Key" +msgstr "Primärschlüssel" + +#: FNewRoutine.class:19 +msgid "Edit Routine" +msgstr "Routine bearbeiten" + +#: FNewRoutine.form:19 +msgid "Create Routine" +msgstr "Routine erstellen" + +#: FNewRoutine.form:45 +msgid "Parameters:" +msgstr "Parameter:" + +#: FNewRoutine.form:56 +msgid "Returns:" +msgstr "Rückgabe:" + +#: FNewTable.class:21 +msgid "Edit Table" +msgstr "Tabelle bearbeiten" + +#: FNewTable.form:16 +msgid "Create Table" +msgstr "Tabelle erstellen" + +#: FNewTable.form:42 +msgid "Engine:" +msgstr "-" + +#: FNewTrigger.class:28 +msgid "Edit Trigger" +msgstr "Trigger bearbeiten" + +#: FNewTrigger.form:18 +msgid "Create Trigger" +msgstr "Trigger erstellen" + +#: FNewTrigger.form:44 +msgid "Time:" +msgstr "Zeit:" + +#: FNewTrigger.form:56 +msgid "Event:" +msgstr "Ereignis:" + +#: FNewView.class:18 +msgid "Edit View" +msgstr "Ansicht bearbeiten" + +#: FNewView.form:14 +msgid "Create View" +msgstr "Ansicht erstellen" + +#: FTables.class:9 +msgid "Name" +msgstr "-" + +#: FTables.class:10 +msgid "Rows" +msgstr "Zeilen" + +#: FTables.class:11 +msgid "Engine" +msgstr "-" + +#: FTables.class:12 +msgid "Charset" +msgstr "Zeichensatz" + +#: FTables.class:13 +msgid "Collation" +msgstr "Sortierfolge" + +#: FTables.class:14 +msgid "Size" +msgstr "Größe" + +#: FTables.class:15 +msgid "Update Time" +msgstr "Update-Zeit" + +#: FTables.class:20 +msgid "Updatable" +msgstr "Aktualisierbar" + +#: FTables.class:21 +msgid "Definer" +msgstr "Bestimmer" + +#: FTables.class:22 +msgid "Security" +msgstr "Sicherheit" + +#: FTables.class:23 +msgid "Check" +msgstr "Überprüfen" + +#: FTables.class:30 +msgid "Column" +msgstr "Spalte" + +#: FTables.class:32 +msgid "Type" +msgstr "Typ" + +#: FTables.class:38 +msgid "Returns" +msgstr "Rückgabe" + +#: FTables.class:39 +msgid "Creation Time" +msgstr "Erstellungszeit" + +#: FTables.class:43 +msgid "Comment" +msgstr "Kommentar" + +#: FTables.class:49 +msgid "Default" +msgstr "Standardwert" + +#: FTables.class:50 +msgid "Nullable" +msgstr "Nulleingabe möglich" + +#: FTables.class:51 +msgid "DataType" +msgstr "Datentyp" + +#: FTables.class:52 +msgid "Key" +msgstr "Schlüssel" + +#: FTables.class:53 +msgid "Extra" +msgstr "-" + +#: FTables.class:61 +msgid "Event" +msgstr "Ereignis" + +#: FTables.class:62 +msgid "Table" +msgstr "Tabelle" + +#: FTables.class:63 +msgid "Timing" +msgstr "Zeitplanung" + +#: FTables.class:70 +msgid "Time Zone" +msgstr "Zeitzone" + +#: FTables.class:72 +msgid "Execute At" +msgstr "Ausführen ab" + +#: FTables.class:73 +msgid "Interval Value" +msgstr "Intervall" + +#: FTables.class:74 +msgid "Interval Field" +msgstr "Autoeingabefeld" + +#: FTables.class:75 +msgid "SQL Mode" +msgstr "SQL-Modus" + +#: FTables.class:76 +msgid "Starts" +msgstr "Beginnt" + +#: FTables.class:77 +msgid "Ends" +msgstr "Endet" + +#: FTables.class:78 +msgid "On Completion" +msgstr "Bei Vervollständigung" + +#: FTables.class:79 +msgid "Created" +msgstr "Erstellt" + +#: FTables.class:80 +msgid "Last Altered" +msgstr "Zuletzt geändert" + +#: FTables.class:81 +msgid "Last Executed" +msgstr "Zuletzt ausgeführt" + +#: FTables.class:82 +msgid "Originator" +msgstr "Bearbeiter" + +#: FTables.class:85 +msgid "Database Collation" +msgstr "Datenbank-Sortierfolge" + +#: FTables.class:116 +msgid "Fields" +msgstr "Felder" + +#: FTables.class:334 +msgid "Not aviable" +msgstr "Nicht verfügbar" + +#: FTables.class:367 +msgid "Indexes on:" +msgstr "Indexes in:" + +#: FTables.class:884 +msgid "New View" +msgstr "Neue Ansicht" + +#: FTables.class:885 +msgid "Delete View" +msgstr "Ansicht löschen" + +#: FTables.class:887 +msgid "New Index" +msgstr "Neuer Index" + +#: FTables.class:888 +msgid "Delete Index" +msgstr "Index löschen" + +#: FTables.class:890 +msgid "New Field" +msgstr "Neues Feld" + +#: FTables.class:891 +msgid "Delete Field" +msgstr "Feld löschen" + +#: FTables.class:893 +msgid "New Routine" +msgstr "Neue Routine" + +#: FTables.class:894 +msgid "Delete Routine" +msgstr "Routine löschen" + +#: FTables.class:896 +msgid "New Trigger" +msgstr "Neuer Trigger" + +#: FTables.class:897 +msgid "Delete Trigger" +msgstr "Trigger löschen" + +#: FTables.class:899 +msgid "New Event" +msgstr "Neues Ereignis" + +#: FTables.class:900 +msgid "Delete Event" +msgstr "Ereignis löschen" + +#: FTables.class:931 +msgid "&No" +msgstr "&Nein" + +#: FTables.class:931 +msgid "&Yes" +msgstr "&Ja" + +#: FTables.class:931 +msgid "Do you realy want to delete the database: &1?" +msgstr "Wollen Sie wirklich die Datenbank &1 löschen?" + +#: FTables.class:1023 +msgid "Do you realy want to delete the table: &1?" +msgstr "Wollen Sie wirklich die Tabelle &1 löschen?" + +#: FTables.class:1025 +msgid "Do you realy want to delete the view: &1?" +msgstr "Wollen Sie wirklich die Ansicht &1 löschen?" + +#: FTables.class:1027 +msgid "Do you realy want to delete the index: &1?" +msgstr "Wollen Sie wirklich den Index &1 löschen?" + +#: FTables.class:1029 +msgid "Do you realy want to delete the field: &1?" +msgstr "Wollen Sie wirklich das Feld &1 löschen?" + +#: FTables.class:1031 +msgid "Do you realy want to delete the routine: &1?" +msgstr "Wollen Sie wirklich die Routine &1 löschen?" + +#: FTables.class:1033 +msgid "Do you realy want to delete the trigger: &1?" +msgstr "Wollen Sie wirklich den Trigger &1 löschen?" + +#: FTables.class:1035 +msgid "Do you realy want to delete the event: &1?" +msgstr "Wollen Sie wirklich das Ereignis &1 löschen?" + +#: FTables.class:1038 +msgid "Item deleted." +msgstr "Datensatz gelöscht" + +#: FTables.form:35 +msgid "Tables" +msgstr "Tabellen" + +#: FTables.form:40 +msgid "Item" +msgstr "Datensatz" + +#: FTables.form:45 +msgid "New Item" +msgstr "Neuer Datensatz" + +#: FTables.form:52 +msgid "Edit Item" +msgstr "Datensatz bearbeiten" + +#: FTables.form:59 +msgid "Delete Item" +msgstr "Datensatz löschen" + +#: FTables.form:66 +msgid "Refresh" +msgstr "Erneuern" + +#: FTables.form:84 +msgid "Lock" +msgstr "Sperren" + +#: FTables.form:96 +msgid "New Table" +msgstr "Neue Tabelle" + +#: FTables.form:103 +msgid "Delete Table" +msgstr "Tabelle löschen" + +#: FTables.form:121 +msgid "New Database" +msgstr "Neue Datenbank" + +#: FTables.form:128 +msgid "Delete Database" +msgstr "Datenbank löschen" + +#: FTables.form:159 +msgid "Views" +msgstr "Ansichten" + +#: FTables.form:169 +msgid "Indexes" +msgstr "-" + +#: FTables.form:179 +msgid "Fields on:" +msgstr "Felder in:" + +#: FTables.form:188 +msgid "Routines" +msgstr "Routinen" + +#: FTables.form:198 +msgid "Triggers" +msgstr "-" + +#: FTables.form:208 +msgid "Events" +msgstr "Ereignisse" diff --git a/app/examples/Database/MySQLExample/.lang/es.mo b/app/examples/Database/MySQLExample/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..f4058b43421468665b2df09ad811eb500f3b2972 GIT binary patch literal 6546 zcmb7`Ym8l0700(&sf<_wu@pqu&^or1J8fwzaGAE;nYnF;-g{>{FKIz#&pmr?PkZK^ z;hb}4Iy_2|7*Ih_!v_f;`|1!w+9tYQePkEJxCHuI(YJ_R12IJ10CK(-Nc$3y>)sA>{(Hc4z%PL8_qfzQ zDf+iT+IJY_`rilfr;ZALC42+qddEQ8vlxq^sFs4%mxFA-0^9&@2G@a^@TX$ z1_rTx6G%M)*Mg6MwEw8^RpB2&?)zIH*Zr5&p9!O>p9ivi1;}&j0_oR!kbajzo==a| zZx;@LTyIR;jnub0AY%HSoj3U^}h?kRrNf`d-WrbcK$@_UjfBr);eEZoT?OqCU{DmOfR|(e(H-S9wZ6NI$l=>S*pOE$xJPqx)O8o(; z|13y*zXWnW9+CDZL9X{KNIQ;5{R<%NJ}P_}?fWeOIsQD5 z_FfFK|7wuuwie`iSBky~V%o6#Cylj$876@8?+{^<}~f zr2ay1J?dA0-1qB2+T8>h#}W{K>R$Zt{J#Qny+fjZ4`lxrr2eSruYt7x4Un$B1@fLQ zKG%<53ex@+AnSWTTs^fDr2huw_2J$|hcAjSk$a&8Jd2Sa8HOT&#f;_KPAjfS0Ie#}u`>G&Z zRYSrS$a!u9>Bj+(_wrs4N29(7;>xJ6ft=?#knvSyhb}j?iz8qxxT9EDCqF)DcoiVAu9;6);q9-8zy&dE{_Xr;ZY3HLL&*PgQ z_w_lcp94A1i=rO|>G#W0{~E|~e*!t~AHqdQioCZ=K+dm0`n?k5_%)($1UbH2>UV(j zW2bN!grN3B{Q?K%K*-n&7@h0lW=&o#RsH$%8je2ZHlAB3=9@!f>NouXU> zegqPVUf?nmHbOA9`)2Sv32AuM{qjt15S{)V|E)h>L7DcakO)GbZiMWH@ViaMkP;rUb{Z3sUD;T>UI;deXa z0AvcnGx#LrE(kyF{c=}QYFMbj9T2{Qt0Ci%8iZdDqykw9VVuG>ad~VVcpHS@r5-8) zk#7Fuo0ghB5YIzpkZU2GkZU0Pu7vO&@*9I(0hxt-05r-q$ANAmrnaeUP1y z0m!EycR=R9&R!EYZKOJTBWqG1iDTQ$QAn-Ht?Fzfs&inU>fB{BRnb{EITcyGt*5KL zYj|f(SN9uMs7ljL<(Y1b)@oTUr|eUGw|h1{We1zq;rW;?gK zlB1ZX#{ZU;vm_JfBra;o${B`8B6My2agOI{A_*Du>KGrs>ZkqukqZ}esU3D!)l8P_ zN`#R%@QN4%EkT5-+blCJtNi;f!j#vxjrOaC!NcTFSvBm;!lR<%U1iB{DNBAwU5N&< zQ0ynmemh-Bc9UhdnFyebB-ds-bkU>HM(AT08Hn-r89ZGa9Kf(JHhGe&K?J@@Joprm zbkJnkOp*pDB`IRtU>Z)EbXIc}+8NhI zr(7_Af39SG1RTw{3b!LuOeBM8vKLvV)E|__;3pzwSr1lvcT~1j^;mUixPPG0F*r~f zveO|OONgL&V7sc7p;CRI)K}5l2g>#S)w>58)ehc-(nxCJ3@If^%X%x4kZx4!Roz{! zSNdz$R{Hves>8#Z(KT+haVttA)=ZXlt??3>8gc(G;6}9rE1(v0_;{%0~VP2%+o7*;VYxult6y95cR!O}inUzYdg2aSbU?puOHZBJ6eC7PJ88O}W&OmKsJG%$M&T_+)#o0$xy^@C6eMvcK4s%+dV`R=5rEsy!q`Uv9u^*=gSN=ozF1ab zXEtA0XvMvXkQ(#3Z!m!;K7^cDS@P-IUA`dcQnkoTTQ&Dklp~k<94^nZsB^aY7uyph z2vm4absJ}9uXVOIlF9KD>DvJbyltmXe6FlZirx2$-+W&#_TFpmm9c1wIO7$+@xD*7 z@4m%vdkH4!Ak3zcIAlt%#BHD3t1;syb30>&9ivicd6hJ#%Xd|3LUHWv*`_*9Rs* z$%o-G`es--MQk>D%0VE9D($=mMk ZaRZ8oJ74J!aWrfDyx_JC|B@i7{TphyuTKB~ literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/.lang/es.po b/app/examples/Database/MySQLExample/.lang/es.po new file mode 100644 index 00000000..fd15a823 --- /dev/null +++ b/app/examples/Database/MySQLExample/.lang/es.po @@ -0,0 +1,533 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FNewView.form:52 +msgid "&Cancel" +msgstr "&Cancelar" + +#: FConnect.form:91 +msgid "&Clear" +msgstr "&Limpiar" + +#: FConnect.form:84 +msgid "&Connect" +msgstr "&Conectar" + +#: FNewDatabase.form:63 +msgid "&Create" +msgstr "&Crear" + +#: FTables.class:931 +msgid "&No" +msgstr "-" + +#: FNewTable.form:70 FNewTrigger.form:73 FNewView.form:45 +msgid "&Ok" +msgstr "-" + +#: FTables.class:931 +msgid "&Yes" +msgstr "&Sí" + +#: .project:2 +msgid "A simple GB.MYSQL Example" +msgstr "Un simple Ejemplo de GB.MYSQL" + +#: FNewField.form:57 +msgid "Acepts NULLs:" +msgstr "Acepta NULOs:" + +#: FNewField.form:22 +msgid "Add Field" +msgstr "Agregar Campo" + +#: FTables.class:12 +msgid "Charset" +msgstr "Conjunto de caracteres" + +#: FNewDatabase.form:53 FNewTable.form:53 +msgid "Charset:" +msgstr "-" + +#: FTables.class:23 +msgid "Check" +msgstr "Chequeo" + +#: FTables.class:13 +msgid "Collation" +msgstr "-" + +#: FNewDatabase.form:42 +msgid "Collation:" +msgstr "-" + +#: FTables.class:30 +msgid "Column" +msgstr "Columna" + +#: FNewEvent.form:47 FNewRoutine.form:50 +msgid "Comma separated values: Hola1 INT,Hola2 CHAR(5)" +msgstr "Valores separados por coma: Hola1 INT,Hola2 CHAR(5)" + +#: FTables.class:43 +msgid "Comment" +msgstr "Comentario" + +#: FNewField.form:89 +msgid "Comment:" +msgstr "Comentario:" + +#: FConnect.form:35 +msgid "Connect to a MySQL Server" +msgstr "Conectar a un Servidor MySQL" + +#: FNewDatabase.form:16 +msgid "Create Database" +msgstr "Crear Base de Datos" + +#: FNewEvent.form:16 +msgid "Create Event" +msgstr "Crear Evento" + +#: FNewIndex.form:18 +msgid "Create Index" +msgstr "Crear Índice" + +#: FNewRoutine.form:19 +msgid "Create Routine" +msgstr "Crear Rutina" + +#: FNewTable.form:16 +msgid "Create Table" +msgstr "Crear Tabla" + +#: FNewTrigger.form:18 +msgid "Create Trigger" +msgstr "Crear Trigger" + +#: FNewView.form:14 +msgid "Create View" +msgstr "Crear Vista" + +#: FTables.class:79 +msgid "Created" +msgstr "Creado" + +#: FTables.class:39 +msgid "Creation Time" +msgstr "Hora de creación" + +#: FNewField.form:47 +msgid "Data Type:" +msgstr "Tipo de Datos:" + +#: FTables.class:85 +msgid "Database Collation" +msgstr "Colación de la Base de Datos" + +#: FTables.class:51 +msgid "DataType" +msgstr "Tipo de Datos" + +#: FTables.class:49 +msgid "Default" +msgstr "Predeterminado" + +#: FNewField.form:69 +msgid "Default:" +msgstr "Predeterminado:" + +#: FTables.class:21 +msgid "Definer" +msgstr "Definidor" + +#: FNewIndex.form:23 +msgid "Delete" +msgstr "Borrar" + +#: FTables.form:128 +msgid "Delete Database" +msgstr "Borrar Base de Datos" + +#: FTables.class:900 +msgid "Delete Event" +msgstr "Borrar Evento" + +#: FTables.class:891 +msgid "Delete Field" +msgstr "Borrar Campo" + +#: FTables.class:888 +msgid "Delete Index" +msgstr "Borrar Índice" + +#: FTables.form:59 +msgid "Delete Item" +msgstr "Borrar Item" + +#: FTables.class:894 +msgid "Delete Routine" +msgstr "Borrar Rutina" + +#: FTables.form:103 +msgid "Delete Table" +msgstr "Borrar Tabla" + +#: FTables.class:897 +msgid "Delete Trigger" +msgstr "Borrar Trigger" + +#: FTables.class:885 +msgid "Delete View" +msgstr "Borrar Vista" + +#: FTables.class:931 +msgid "Do you realy want to delete the database: &1?" +msgstr "¿Realmente desea borrar la base de datos: &1?" + +#: FTables.class:1035 +msgid "Do you realy want to delete the event: &1?" +msgstr "¿Realmente desea borrar el evento: &1?" + +#: FTables.class:1029 +msgid "Do you realy want to delete the field: &1?" +msgstr "¿Realmente desea borrar el campo: &1?" + +#: FTables.class:1027 +msgid "Do you realy want to delete the index: &1?" +msgstr "¿Realmente desea borrar el índice: &1?" + +#: FTables.class:1031 +msgid "Do you realy want to delete the routine: &1?" +msgstr "¿Realmente desea borrar la rutina: &1?" + +#: FTables.class:1023 +msgid "Do you realy want to delete the table: &1?" +msgstr "¿Realmente desea borrar la tabla: &1?" + +#: FTables.class:1033 +msgid "Do you realy want to delete the trigger: &1?" +msgstr "¿Realmente desea borrar el trigger: &1?" + +#: FTables.class:1025 +msgid "Do you realy want to delete the view: &1?" +msgstr "¿Realmente desea borrar la vista: &1?" + +#: FNewEvent.class:19 +msgid "Edit Event" +msgstr "Editar Evento" + +#: FNewField.class:22 +msgid "Edit Field" +msgstr "Editar Campo" + +#: FNewIndex.class:24 +msgid "Edit Index" +msgstr "Editar Índice" + +#: FTables.form:52 +msgid "Edit Item" +msgstr "Editar item" + +#: FNewRoutine.class:19 +msgid "Edit Routine" +msgstr "Editar rutina" + +#: FNewTable.class:21 +msgid "Edit Table" +msgstr "Editar Tabla" + +#: FNewTrigger.class:28 +msgid "Edit Trigger" +msgstr "Editar Trigger" + +#: FNewView.class:18 +msgid "Edit View" +msgstr "Editar Vista" + +#: FTables.class:77 +msgid "Ends" +msgstr "Finaliza" + +#: FTables.class:11 +msgid "Engine" +msgstr "Máquina" + +#: FNewTable.form:42 +msgid "Engine:" +msgstr "Máquina:" + +#: FTables.class:61 +msgid "Event" +msgstr "Evento" + +#: FNewTrigger.form:56 +msgid "Event:" +msgstr "Evento:" + +#: FTables.form:208 +msgid "Events" +msgstr "Eventos" + +#: FTables.class:72 +msgid "Execute At" +msgstr "Ejecutar En" + +#: FTables.class:53 +msgid "Extra" +msgstr "-" + +#: FNewField.form:79 +msgid "Extra:" +msgstr "-" + +#: FTables.class:116 +msgid "Fields" +msgstr "Campos" + +#: FTables.form:179 +msgid "Fields on:" +msgstr "Campos en:" + +#: FNewIndex.form:55 +msgid "Fields:" +msgstr "Campos:" + +#: FTables.form:169 +msgid "Indexes" +msgstr "Índices" + +#: FTables.class:367 +msgid "Indexes on:" +msgstr "Índices en:" + +#: FTables.class:74 +msgid "Interval Field" +msgstr "Campo del Intérvalo" + +#: FTables.class:73 +msgid "Interval Value" +msgstr "Valor del Intérvalo" + +#: FTables.form:40 +msgid "Item" +msgstr "-" + +#: FTables.class:1038 +msgid "Item deleted." +msgstr "Item borrado." + +#: FTables.class:52 +msgid "Key" +msgstr "Llave" + +#: FTables.class:80 +msgid "Last Altered" +msgstr "Última Alteración" + +#: FTables.class:81 +msgid "Last Executed" +msgstr "Última Ejecución" + +#: FTables.form:84 +msgid "Lock" +msgstr "Bloquear" + +#: FMessage.form:8 +msgid "Message" +msgstr "Mensaje" + +#: .project:1 FConnect.form:28 +msgid "MySQL Example" +msgstr "-" + +#: FConnect.form:20 +msgid "MySQL GUI" +msgstr "-" + +#: FTables.class:9 +msgid "Name" +msgstr "Nombre" + +#: FNewRoutine.form:35 FNewTable.form:32 FNewTrigger.form:34 FNewView.form:30 +msgid "Name:" +msgstr "Nombre:" + +#: FTables.form:121 +msgid "New Database" +msgstr "Nueva Base de Datos" + +#: FTables.class:899 +msgid "New Event" +msgstr "Nuevo Evento" + +#: FTables.class:890 +msgid "New Field" +msgstr "Nuevo Campo" + +#: FTables.class:887 +msgid "New Index" +msgstr "Nuevo Índice" + +#: FTables.form:45 +msgid "New Item" +msgstr "Nuevo Item" + +#: FTables.class:893 +msgid "New Routine" +msgstr "Nueva Rutina" + +#: FTables.form:96 +msgid "New Table" +msgstr "Nueva Tabla" + +#: FTables.class:896 +msgid "New Trigger" +msgstr "Nuevo Trigger" + +#: FTables.class:884 +msgid "New View" +msgstr "Nueva Vista" + +#: FNewField.form:63 +msgid "No" +msgstr "-" + +#: FTables.class:334 +msgid "Not aviable" +msgstr "No disponible" + +#: FTables.class:50 +msgid "Nullable" +msgstr "Anulable" + +#: FTables.class:78 +msgid "On Completion" +msgstr "En completado" + +#: FTables.class:82 +msgid "Originator" +msgstr "Originador" + +#: FNewRoutine.form:45 +msgid "Parameters:" +msgstr "Parámetros:" + +#: FConnect.form:72 +msgid "Password:" +msgstr "Contraseña:" + +#: FConnect.form:50 +msgid "Port:" +msgstr "Puerto:" + +#: FNewIndex.form:68 +msgid "Primary Key" +msgstr "Llave primaria" + +#: FTables.form:66 +msgid "Refresh" +msgstr "Refrescar" + +#: FTables.class:38 +msgid "Returns" +msgstr "Retorna" + +#: FNewRoutine.form:56 +msgid "Returns:" +msgstr "Retorna:" + +#: FTables.form:188 +msgid "Routines" +msgstr "Rutinas" + +#: FTables.class:10 +msgid "Rows" +msgstr "Filas" + +#: FNewEvent.form:42 +msgid "Schedule:" +msgstr "Programación:" + +#: FTables.class:22 +msgid "Security" +msgstr "Seguridad" + +#: FConnect.form:39 +msgid "Server:" +msgstr "Servidor:" + +#: FNewEvent.form:52 FNewRoutine.form:69 FNewTrigger.form:67 FNewView.form:39 +msgid "Show a clue" +msgstr "Mostrar una pista" + +#: FTables.class:14 +msgid "Size" +msgstr "Tamaño" + +#: FTables.class:75 +msgid "SQL Mode" +msgstr "Modo SQL" + +#: FTables.class:76 +msgid "Starts" +msgstr "Inicia" + +#: FTables.class:62 +msgid "Table" +msgstr "Tabla" + +#: FTables.form:35 +msgid "Tables" +msgstr "Tablas" + +#: FTables.class:70 +msgid "Time Zone" +msgstr "Zona de Tiempo" + +#: FNewTrigger.form:44 +msgid "Time:" +msgstr "Hora:" + +#: FTables.class:63 +msgid "Timing" +msgstr "-" + +#: FTables.form:198 +msgid "Triggers" +msgstr "-" + +#: FTables.class:32 +msgid "Type" +msgstr "Tipo" + +#: FTables.class:20 +msgid "Updatable" +msgstr "Actualizable" + +#: FTables.class:15 +msgid "Update Time" +msgstr "Hora de Actualización" + +#: FConnect.form:61 +msgid "User:" +msgstr "Usuario:" + +#: FTables.form:159 +msgid "Views" +msgstr "Vistas" + +#: FNewField.form:63 +msgid "Yes" +msgstr "Sí" + diff --git a/app/examples/Database/MySQLExample/.lang/fr.mo b/app/examples/Database/MySQLExample/.lang/fr.mo new file mode 100644 index 0000000000000000000000000000000000000000..f72c65f58a23a59f6ce2ab45815e38f0f44ef392 GIT binary patch literal 6143 zcmb7{ZHygN8OIOMiY_4XB2u(?z`DHNmbMnlZ7E!Kx9!62-DTgTSitGMXLko?XO@|{ z+b$*;A!@487>ovuFHMZv{Xn!)Vxkd9u7(dvqD12xAvJ1%#E5=?L^09df9BkKw~$D7 za-aE~_w$_RJm)!cpFe-y7Y$Dfc@Of_GmQBfjLzi6bMkG*DETM&7Wfi;EBuEq{}-f- zIjzr_b#OgA2lhd#%*9asFNf-PHCzw3`}!LoKQq8f_YHU2huKHLS>uI|fceEuFN{pO(N zkD=y&(DO5%k3xRtTfC&t319w{&p!kCndf+Y0KN#f!i@|jj(L6@ZYTeJpMSyUH=z7= zl#fE$`#~tZ9){BU(@^$(#Fszn`3q1rD)&HJz~KMFO^=Y9Q`q56Lls@-=zABVE{ zkKrruNvM90zoX<6Q0si!=bwd||Bs$0p~n3y6kk z1giht@Le#5n)j1X^F9Kl-!UltR-od=3CPdndm2ig-$JeXIf#np zWvF)R*bMP(cpAJA%Fix>n*S0gyIuy>e;3reb*Ob5gtA)*rN9LMYQu$d>cH97Uo;E?*aWho^DpdOcpT8BV-6T}M zyL^5g%04Mn`-gn_Q7C=C0A-hBP~#qln*Rip9iD`k&^+tQUxc#rt5EBF4a%M)2umpL z+ACn^*+oKruSa^&yzr|O&LMhkFJbP1+S`vHn~)YVjLaYj;t=Vi=l#h0kN~+C(KA)T zTn&d1tz#Ow4q5%kjyJC=$VhKSB1AUXg4~4cN9K`z$W}!5k?-~+O+-(kC!6y zqa8?&=xhxl@)_)U7w;~IO9_{DFMXJ18CwzJv+=*O^Xm9m= z2$@8*M|!R-QM@nU9V7Q42arn;#k3H)2GL`Y_afT!8uDR8kIwvU$gDCvcOiR_Ta@vS z;)$NU$Uu=QUpgP7K7YtlG4v{=f!vK$kj^7Nz5}_;7u*jI`t%u|rhg!a8!m#83sR88 zv1{ZcQWxaT^w$&9f52s?)^Lk?X6w@5g=2CX!Yj#@&Wf z!J(*^NyE9h-k>v~JM6o$Y>mb8>{PhmjOMdb%ZsjBB$X->(!usjG3aK4R+Jl08oOC0 zPhsRRc8Q+$K3{6@Go{sird&!Vle>k^5S?ZDs&ZMKj9-?|7Rwq;>~hkwEGk;Ihl4nm zZJUKwHlKI4=?z-7yASQ@-@1Ftp*=U5H|ykN^)>aFl}oLuN2twQQ;)RV@iq0qnjJhlf z=A0={>AvYp{t(c zHdqRaQ(bQnl61VsV$S5kB1cZ~X^d`R9OOxA#tBFZ9K@6eHy&i!;UsO6N>XCZcp5GQ z>9WJN$9_-dy-b|UakULuZ<4b-9w|b;gOqbgM$-8laqUB8qc-Z^~#hB z7OHlnR^K z{;^ib)uy}6Svr~8y@Uss+E%Pt7Zy#W(z)6!mTj-3AwepoPvJZzs3FMfQZgJC*Ge;+ zwO6>_m?*Q5Gn3h+Fr7oK%)aSO7lbL7P;eSlnDRK&ktfo--|8j*`}>z3}^?&M2& zagjCqsX+;68`K7ct+cNy@tU!y*Oi!CbX~4$5n$vcXLT+$C6{NuI?V`!l!kGbn$AwY zZivh}xc~|hINK}b;yY!XPBuc#Vzd1yHoBs;N-xi$3|VroOVG7bw^3kAJ9P_K%xXh* z%L6Mz%_Mlk19mBC;TvhdG3VNKrN`PRc*9QOH>)n+|J%Fa@?y6;3tmgl(iCf)tT5Nw z`f%X9r>(hPVdgdUI%Om5n!4sF%66UFXKzT@A5C)kzemr%R=b++=#Jm%qQ)PJ_|iV6 z$Wq+aI^FUpHhu3FyPKK86Gsd=yU>^lBH)L!9J zF^vgj+V6O-j0#0j`#2}pm73~;yd_HRZ_&PUl~=e`xrezaQKFu-mR!UEdPCePYgW5q z**IwV!{!^5C(UO%$E-VSCm3}L$|F`}8R?Ys)or8GrRa{>W|%D|aj1Kr4**5gMHXLt zy8El2e?J76V4;0H=T>J;Y^b8pB3}SnqV9j0n{IYdUjd5F6TvJ;wlPoW;VU5FGXM*9 zQ#9{>3XFtvF=1FA0+ufVvp=C@5z{B-415jHv-lh+cC|J;%dk!{K2KW>GZloGSAmYt zkJP0`9|d;49K?rNR1V`-uJ0_`bt5yAhOB@KgG(*-!KoXV7gp7o={$_WjN8A^VTQkB IMmOXC0Qb+9hyVZp literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/.lang/fr.po b/app/examples/Database/MySQLExample/.lang/fr.po new file mode 100644 index 00000000..c555a1e6 --- /dev/null +++ b/app/examples/Database/MySQLExample/.lang/fr.po @@ -0,0 +1,1546 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FConnect.class:71 +msgid "MySQL GUI" +msgstr "-" + +#: FConnect.class:79 +#, fuzzy +msgid "MySQL Example" +msgstr "Exemple :" + +#: FConnect.class:86 +msgid "Connect to a MySQL Server" +msgstr "Connecter à un serveur Mysql" + +#: FConnect.class:90 +msgid "Server:" +msgstr "Serveur :" + +#: FConnect.class:101 +msgid "Port:" +msgstr "Port :" + +#: FConnect.class:112 +msgid "User:" +msgstr "Utilisateur :" + +#: FConnect.class:123 +msgid "Password:" +msgstr "Mot de passe :" + +#: FConnect.class:135 +msgid "&Connect" +msgstr "&Connecter" + +#: FConnect.class:142 +msgid "&Clear" +msgstr "&Effacer" + +#: FConnect.class:148 FNewDatabase.class:107 FNewEvent.class:120 +#: FNewField.class:162 FNewIndex.class:163 FNewRoutine.class:151 +#: FNewTable.class:122 FNewTrigger.class:146 FNewView.class:101 +msgid "&Cancel" +msgstr "&Annuler" + +#: FMessage.class:33 +msgid "Message" +msgstr "Message" + +#: FNewDatabase.class:53 +msgid "Create Database" +msgstr "Créer une base de données" + +#: FNewDatabase.class:69 FNewEvent.class:87 FNewField.class:94 +#: FNewIndex.class:128 FNewRoutine.class:104 FNewTable.class:91 +#: FNewTrigger.class:100 FNewView.class:79 +msgid "Name:" +msgstr "Name :" + +#: FNewDatabase.class:79 +msgid "Collation:" +msgstr "Action :" + +#: FNewDatabase.class:90 FNewTable.class:112 +msgid "Charset:" +msgstr "Charset :" + +#: FNewDatabase.class:100 +msgid "&Create" +msgstr "& Créer" + +#: FNewEvent.class:19 +#, fuzzy +msgid "Edit Event" +msgstr "Evénement" + +#: FNewEvent.class:71 +msgid "Create Event" +msgstr "Créer un évènement" + +#: FNewEvent.class:97 +msgid "Schedule:" +msgstr "Horaire :" + +#: FNewEvent.class:102 FNewRoutine.class:119 +msgid "Comma separated values: Hola1 INT,Hola2 CHAR(5)" +msgstr "Valeurs séparées par des virgules : Hola1 INT,Hola2 CHAR(5)" + +#: FNewEvent.class:107 FNewRoutine.class:138 FNewTrigger.class:133 +#: FNewView.class:88 +msgid "Show a clue" +msgstr "Affiche un truc" + +#: FNewEvent.class:113 FNewField.class:155 FNewIndex.class:156 +#: FNewRoutine.class:144 FNewTable.class:129 FNewTrigger.class:139 +#: FNewView.class:94 +msgid "&Ok" +msgstr "" + +#: FNewField.class:22 +#, fuzzy +msgid "Edit Field" +msgstr "Ajouter un champ" + +#: FNewField.class:79 +msgid "Add Field" +msgstr "Ajouter un champ" + +#: FNewField.class:104 +msgid "Data Type:" +msgstr "Type de donnée :" + +#: FNewField.class:114 +msgid "Acepts NULLs:" +msgstr "Accepte nul" + +#: FNewField.class:120 +msgid "No" +msgstr "Non" + +#: FNewField.class:120 +msgid "Yes" +msgstr "Oui" + +#: FNewField.class:126 +msgid "Default:" +msgstr "Par défaut :" + +#: FNewField.class:136 +msgid "Extra:" +msgstr "Extra :" + +#: FNewField.class:146 +msgid "Comment:" +msgstr "Commentaire :" + +#: FNewIndex.class:24 +#, fuzzy +msgid "Edit Index" +msgstr "Créer un index" + +#: FNewIndex.class:101 +msgid "Create Index" +msgstr "Créer un index" + +#: FNewIndex.class:106 +msgid "Delete" +msgstr "Effacer" + +#: FNewIndex.class:138 +msgid "Fields:" +msgstr "Champs :" + +#: FNewIndex.class:151 +msgid "Primary Key" +msgstr "Clé primaire" + +#: FNewRoutine.class:19 +#, fuzzy +msgid "Edit Routine" +msgstr "Créer une routine" + +#: FNewRoutine.class:88 +msgid "Create Routine" +msgstr "Créer une routine" + +#: FNewRoutine.class:114 +msgid "Parameters:" +msgstr "Paramètres :" + +#: FNewRoutine.class:125 +msgid "Returns:" +msgstr "Retourné :" + +#: FNewTable.class:21 +#, fuzzy +msgid "Edit Table" +msgstr "Créer une table" + +#: FNewTable.class:75 +msgid "Create Table" +msgstr "Créer une table" + +#: FNewTable.class:101 +msgid "Engine:" +msgstr "Moteur" + +#: FNewTrigger.class:28 +#, fuzzy +msgid "Edit Trigger" +msgstr "Déclencheurs" + +#: FNewTrigger.class:84 +msgid "Create Trigger" +msgstr "Créer un déclencheur" + +#: FNewTrigger.class:110 +msgid "Time:" +msgstr "Heure :" + +#: FNewTrigger.class:122 +msgid "Event:" +msgstr "Evénement :" + +#: FNewView.class:18 +#, fuzzy +msgid "Edit View" +msgstr "Editer" + +#: FNewView.class:63 +msgid "Create View" +msgstr "Créer une vue" + +#: FTables.class:9 +msgid "Name" +msgstr "Nom" + +#: FTables.class:10 +msgid "Rows" +msgstr "Lignes" + +#: FTables.class:11 +msgid "Engine" +msgstr "Moteur" + +#: FTables.class:12 +msgid "Charset" +msgstr "--" + +#: FTables.class:13 +msgid "Collation" +msgstr "Action" + +#: FTables.class:14 +msgid "Size" +msgstr "Taille" + +#: FTables.class:15 +msgid "Update Time" +msgstr "Heure de mise à jour" + +#: FTables.class:20 +msgid "Updatable" +msgstr "Mise à jour table" + +#: FTables.class:21 +msgid "Definer" +msgstr "Définir" + +#: FTables.class:22 +msgid "Security" +msgstr "Sécurité" + +#: FTables.class:23 +msgid "Check" +msgstr "Vérifier" + +#: FTables.class:30 +msgid "Column" +msgstr "Colonne" + +#: FTables.class:32 +msgid "Type" +msgstr "" + +#: FTables.class:38 +msgid "Returns" +msgstr "Retourné" + +#: FTables.class:39 +msgid "Creation Time" +msgstr "Heure de création" + +#: FTables.class:43 +msgid "Comment" +msgstr "Commentaire" + +#: FTables.class:49 +msgid "Default" +msgstr "Par défaut" + +#: FTables.class:50 +msgid "Nullable" +msgstr "Nul" + +#: FTables.class:51 +msgid "DataType" +msgstr "Type de donnée" + +#: FTables.class:52 +msgid "Key" +msgstr "Clé" + +#: FTables.class:53 +msgid "Extra" +msgstr "--" + +#: FTables.class:61 +msgid "Event" +msgstr "Evénement" + +#: FTables.class:62 +msgid "Table" +msgstr "" + +#: FTables.class:63 +msgid "Timing" +msgstr "Minutage" + +#: FTables.class:70 +msgid "Time Zone" +msgstr "Zone horaire" + +#: FTables.class:72 +msgid "Execute At" +msgstr "Exécuter à" + +#: FTables.class:73 +msgid "Interval Value" +msgstr "Valeur d'interval" + +#: FTables.class:74 +msgid "Interval Field" +msgstr "Champ d'interval" + +#: FTables.class:75 +msgid "SQL Mode" +msgstr "Mode SQL" + +#: FTables.class:76 +msgid "Starts" +msgstr "Démarrer" + +#: FTables.class:77 +msgid "Ends" +msgstr "Fins" + +#: FTables.class:78 +msgid "On Completion" +msgstr "Complétion " + +#: FTables.class:79 +msgid "Created" +msgstr "Crée" + +#: FTables.class:80 +msgid "Last Altered" +msgstr "Dernier changement" + +#: FTables.class:81 +msgid "Last Executed" +msgstr "Dernière exécution" + +#: FTables.class:82 +msgid "Originator" +msgstr "Originel" + +#: FTables.class:85 +msgid "Database Collation" +msgstr "Base de donnée active " + +#: FTables.class:115 +msgid "Indexes" +msgstr "Indexs" + +#: FTables.class:116 +msgid "Fields" +msgstr "Champs" + +#: FTables.class:149 +msgid "Fields on:" +msgstr "Champs actuel :" + +#: FTables.class:334 +msgid "Not aviable" +msgstr "Non disponible" + +#: FTables.class:367 +msgid "Indexes on:" +msgstr "Indexé sur :" + +#: FTables.class:881 +msgid "New Table" +msgstr "Nouvelle table" + +#: FTables.class:882 +msgid "Delete Table" +msgstr "Effacer la table" + +#: FTables.class:884 +msgid "New View" +msgstr "Nouvelle vue" + +#: FTables.class:885 +msgid "Delete View" +msgstr "Effacer la vue" + +#: FTables.class:887 +msgid "New Index" +msgstr "Nouvel index" + +#: FTables.class:888 +msgid "Delete Index" +msgstr "Effacer l'index" + +#: FTables.class:890 +msgid "New Field" +msgstr "Nouveau champ" + +#: FTables.class:891 +msgid "Delete Field" +msgstr "Effacer le champ" + +#: FTables.class:893 +msgid "New Routine" +msgstr "Nouvelle routine" + +#: FTables.class:894 +msgid "Delete Routine" +msgstr "Effacer la routine" + +#: FTables.class:896 +msgid "New Trigger" +msgstr "Nouveau déclencheur" + +#: FTables.class:897 +msgid "Delete Trigger" +msgstr "Effacer le déclencheur" + +#: FTables.class:899 +msgid "New Event" +msgstr "Nouvel événement" + +#: FTables.class:900 +msgid "Delete Event" +msgstr "Evénement effacé" + +#: FTables.class:931 +msgid "&No" +msgstr "&Non" + +#: FTables.class:931 +msgid "&Yes" +msgstr "&Oui" + +#: FTables.class:931 +msgid "Do you realy want to delete the database: &1?" +msgstr "Voulez vous vraiment effacer la base de données : &1?" + +#: FTables.class:1023 +msgid "Do you realy want to delete the table: &1?" +msgstr "Voulez vous vraiment effacer cette table : &1?" + +#: FTables.class:1025 +msgid "Do you realy want to delete the view: &1?" +msgstr "Voulez vous vraiment effacer cette vue : &1?" + +#: FTables.class:1027 +msgid "Do you realy want to delete the index: &1?" +msgstr "Voulez vous vraiment effacer cet index : &1?" + +#: FTables.class:1029 +msgid "Do you realy want to delete the field: &1?" +msgstr "Voulez vous vraiment effacer ce champ : &1?" + +#: FTables.class:1031 +msgid "Do you realy want to delete the routine: &1?" +msgstr "Voulez vous vraiment effacer cette routine : &1?" + +#: FTables.class:1033 +msgid "Do you realy want to delete the trigger: &1?" +msgstr "Voulez vous vraiment effacer ce déclencheur : &1?" + +#: FTables.class:1035 +msgid "Do you realy want to delete the event: &1?" +msgstr "Voulez vous vraiment effacer cet événement : &1?" + +#: FTables.class:1038 +msgid "Item deleted." +msgstr "Article effacé" + +#: FTables.class:1243 +msgid "Tables" +msgstr "" + +#: FTables.class:1248 +msgid "Item" +msgstr "Article" + +#: FTables.class:1253 +msgid "New Item" +msgstr "Nouvel article" + +#: FTables.class:1260 +#, fuzzy +msgid "Edit Item" +msgstr "Editer" + +#: FTables.class:1267 +msgid "Delete Item" +msgstr "Effacer l'article" + +#: FTables.class:1274 +msgid "Refresh" +msgstr "Rafraichir" + +#: FTables.class:1292 +msgid "Lock" +msgstr "Bloqué" + +#: FTables.class:1329 +msgid "New Database" +msgstr "Nouvelle base de données" + +#: FTables.class:1336 +msgid "Delete Database" +msgstr "Base de données effacée" + +#: FTables.class:1367 +msgid "Views" +msgstr "Vues" + +#: FTables.class:1396 +msgid "Routines" +msgstr "Routines" + +#: FTables.class:1406 +msgid "Triggers" +msgstr "Déclencheurs" + +#: FTables.class:1416 +msgid "Events" +msgstr "Evénements" + +#~ msgid "&Replace" +#~ msgstr "&Remplace" + +#~ msgid "Are you sure to want to replace all?" +#~ msgstr "Etes vous sûr de vouloir tout remplacer ?" + +#~ msgid "MySQL is great!!!" +#~ msgstr "Mysql est super !!!" + +#~ msgid "Gambas is great!!!" +#~ msgstr "Gambas est super !!!" + +#~ msgid "Wellcome!!! Have a nice day." +#~ msgstr "Bienvenue !!! Bonne continuation." + +#~ msgid "In a world without walls and fences, who needs windows and gates?" +#~ msgstr "Dans un monde sans murs et barrières, qui a besoin de fenetres et de portes ?" + +#~ msgid "In God we trust" +#~ msgstr "Grace à Dieu !" + +#~ msgid "Do you want to save the changes?" +#~ msgstr "Voulez vous sauver les changements ?" + +#~ msgid "Go to line" +#~ msgstr "Aller à la ligne " + +#~ msgid "Search string replaced &1 times." +#~ msgstr "Chaine de recherche remplacée &1 fois" + +#~ msgid "Search string replaced once." +#~ msgstr "Chaine de recherche remplacée une fois." + +#~ msgid "Search string cannot be found." +#~ msgstr "Chaine de recherche non trouvée." + +#~ msgid "Reached the beginning of the document." +#~ msgstr "Le début du document a été atteind" + +#~ msgid "Reached the end of the document." +#~ msgstr "La fin du document a été atteind " + +#~ msgid "Revision:" +#~ msgstr "Révision :" + +#~ msgid "Workig Version:" +#~ msgstr "Version de travail :" + +#~ msgid "Modified" +#~ msgstr "Modifié" + +#~ msgid "Missing or incomplete" +#~ msgstr "Manquant ou incomplet" + +#~ msgid "Not under version control" +#~ msgstr "Pas de CVS" + +#~ msgid "Existed" +#~ msgstr "Sortie" + +#~ msgid "Conflicting changes" +#~ msgstr "Changements pour éviter les conflits" + +#~ msgid "Merged" +#~ msgstr "fusionné" + +#~ msgid "Replaced" +#~ msgstr "Remplacé" + +#~ msgid "Deleted" +#~ msgstr "Effacé" + +#~ msgid "Added" +#~ msgstr "Ajouté" + +#~ msgid "Updated" +#~ msgstr "Mis à jour" + +#~ msgid "Status:" +#~ msgstr "Etats :" + +#~ msgid "Do you really want to revert the changes?" +#~ msgstr "Voulez vous vraiment rétablir les changements ?" + +#~ msgid "File saved." +#~ msgstr "Fichier sauvé." + +#~ msgid "Entire word" +#~ msgstr "Mot entier" + +#~ msgid "Case sensitive" +#~ msgstr "police sensitive" + +#~ msgid "Replace all" +#~ msgstr "Tout remplacer " + +#~ msgid "Replace current" +#~ msgstr "Remplacer l'actuel" + +#~ msgid "Find previous" +#~ msgstr "Recherche le précédent" + +#~ msgid "Find next" +#~ msgstr "Recherche le suivant" + +#~ msgid "Close" +#~ msgstr "Fermer" + +#~ msgid "Update" +#~ msgstr "Mise à jour" + +#~ msgid "Status" +#~ msgstr "Etats" + +#~ msgid "Revert" +#~ msgstr "Retour" + +#~ msgid "Go to symbol" +#~ msgstr "Aller au symbol" + +#~ msgid "Go to line..." +#~ msgstr "Aller à la ligne..." + +#~ msgid "Horizontal" +#~ msgstr "Horizontal" + +#~ msgid "View" +#~ msgstr "Vue" + +#~ msgid "Load" +#~ msgstr "Charger" + +#~ msgid "Save" +#~ msgstr "Sauver" + +#~ msgid "Show/Hide" +#~ msgstr "Afficher/Cacher" + +#~ msgid "Replace" +#~ msgstr "Remplacer" + +#~ msgid "Find" +#~ msgstr "Recherche" + +#~ msgid "Clean" +#~ msgstr "Nettoyer" + +#~ msgid "Show numbers" +#~ msgstr "Affiche les nombres" + +#~ msgid "Completion" +#~ msgstr "Complétion" + +#~ msgid "Uncomment" +#~ msgstr "Décommenter" + +#~ msgid "Load query" +#~ msgstr "Charger la requete" + +#~ msgid "Show/Hide tool bar" +#~ msgstr "Afficher/Cacher la barre d'outils" + +#~ msgid "Find & Replace" +#~ msgstr "Recherche et remplace" + +#~ msgid "Clean editor" +#~ msgstr "Nettoyer l'éditeur" + +#~ msgid "Save query" +#~ msgstr "Sauver une requete" + +#~ msgid "Columns with * are Key fields.
They will be used to perform any operation." +#~ msgstr "Les colonnes avec * sont des champs clés.
Ils seront utilisés comme jokers." + +#~ msgid "User
Schema (database) privileges assigned to the user" +#~ msgstr "Utilisateur
Schéma (base de données) des privilèges assignés à l'utilisateur" + +#~ msgid "Schema Privileges" +#~ msgstr "Droits du schéma" + +#~ msgid "New" +#~ msgstr "Nouveau" + +#~ msgid "&Change" +#~ msgstr "&Changer" + +#~ msgid "Icon:" +#~ msgstr "Icones :" + +#~ msgid "Contact Information:" +#~ msgstr "Information du contact :" + +#~ msgid "E-Mail:" +#~ msgstr "Courriel :" + +#~ msgid "Description:" +#~ msgstr "Description :" + +#~ msgid "Full name:" +#~ msgstr "Nom :" + +#~ msgid "Additional Information" +#~ msgstr "Information additionnelles" + +#~ msgid "Confirm Password:" +#~ msgstr "Confirmer le mot de passe :" + +#~ msgid "New Password:" +#~ msgstr "Nouveau mot de passe :" + +#~ msgid "MySQL User:" +#~ msgstr "Utilisateur Mysql :" + +#~ msgid "Login Information" +#~ msgstr "Information d'entrée" + +#~ msgid "User
Login and additional information on the user" +#~ msgstr "Utilisateur
Login et informations additionnelles de l'utilisateur" + +#~ msgid "User Information" +#~ msgstr "Info utilisateur" + +#~ msgid "Information strored successfully." +#~ msgstr "Information sauvegardée avec succès." + +#~ msgid "Please select a table." +#~ msgstr "SVP sélectionnez une table" + +#~ msgid "Item created." +#~ msgstr "Article créer." + +#~ msgid "Selected Tables:" +#~ msgstr "Tables sélectionnées :" + +#~ msgid "Tables:" +#~ msgstr "Tables :" + +#~ msgid "Do not write the command to the binary log file." +#~ msgstr "Ne pas écrire cette commande dans un fichier binaire" + +#~ msgid "This will repair the selected table(s)." +#~ msgstr "Ceci réparera les tables sélectionnées." + +#~ msgid "This will check whether the selected table(s) are corrupted or have other errors." +#~ msgstr "Ceci vérifiera si les tables sont corrompues ou ont d'autres erreurs." + +#~ msgid "If necessary this will repair the selected table(s), sort index pages and update statistics." +#~ msgstr "Si nécessaire, ceci réparera les tables sélectionnées, trier les index et mettre à jour les statistiques." + +#~ msgid "Repair Tables" +#~ msgstr "Réparer les tables" + +#~ msgid "Check Tables" +#~ msgstr "Vérifier les tables" + +#~ msgid "Optimize Tables" +#~ msgstr "Tables optimisées" + +#~ msgid "Tasks" +#~ msgstr "Taches" + +#~ msgid "Table Maintenance
Please select the task you want to perform on the selected table(s)." +#~ msgstr "Maintenance de la table
SVP sélectionner la tâche que vous vouler effectuée sur la/les table(s) sélectionnée(s)." + +#~ msgid "Tasks and Tables Selection" +#~ msgstr "Sélection des tables et des taches" + +#~ msgid "&Execute" +#~ msgstr "&Exécuter" + +#~ msgid "Do not show this message again" +#~ msgstr "Ne plus montrer ce message" + +#~ msgid "Statements to execute" +#~ msgstr "Commande à exécuter" + +#~ msgid "Server Parameters" +#~ msgstr "Paramètres serveur" + +#~ msgid "Server Parameters (local)" +#~ msgstr "Paramètres serveur local" + +#~ msgid "Replication Status" +#~ msgstr "Etat de réplication" + +#~ msgid "System Variables" +#~ msgstr "Variables système" + +#~ msgid "Server Status:" +#~ msgstr "Etat du serveur :" + +#~ msgid "Clock:" +#~ msgstr "Horloge :" + +#~ msgid "Memory:" +#~ msgstr "Mémoire :" + +#~ msgid "Operating System:" +#~ msgstr "Système d'exploitation :" + +#~ msgid "Processor:" +#~ msgstr "Processeur :" + +#~ msgid "Version:" +#~ msgstr "Version :" + +#~ msgid "Client Information" +#~ msgstr "Information Client" + +#~ msgid "Network Name:" +#~ msgstr "Nom de réseau :" + +#~ msgid "IP:" +#~ msgstr "IP :" + +#~ msgid "MySQL Version:" +#~ msgstr "Version Mysql :" + +#~ msgid "Host:" +#~ msgstr "Serveur :" + +#~ msgid "Socket:" +#~ msgstr "Prise :" + +#~ msgid "Connected to MySQL Server Instance" +#~ msgstr "Connecté à l'instance du serveur Mysql" + +#~ msgid "" +#~ "Server Status:
\n" +#~ "     Server is running" +#~ msgstr "" +#~ "Etat du serveur :
\n" +#~ "     Le serveur est lancé" + +#~ msgid "Couldn't find &1 in &2 or &3" +#~ msgstr "Impossible de trouver &1 dans &2 ou &3" + +#~ msgid "New Script" +#~ msgstr "Nouveau script" + +#~ msgid "Run script" +#~ msgstr "Exécuter un script" + +#~ msgid "Script executed successfully." +#~ msgstr "Script exécuté avec succès" + +#~ msgid "Empty table." +#~ msgstr "Table vide." + +#~ msgid "Paso #5" +#~ msgstr "Passe #5" + +#~ msgid "Local Test" +#~ msgstr "Test local" + +#~ msgid "Changes the restore path" +#~ msgstr "Changement du chemin de restauration" + +#~ msgid "Source schema:" +#~ msgstr "Source du schéma :" + +#~ msgid "Source Schema" +#~ msgstr "Source du schéma" + +#~ msgid "SMaRT" +#~ msgstr "Pratique" + +#~ msgid "Schema:" +#~ msgstr "Schéma :" + +#~ msgid "david123" +#~ msgstr "--" + +#~ msgid "3306" +#~ msgstr "-" + +#~ msgid "localhost" +#~ msgstr "Serveur local" + +#~ msgid "Main server" +#~ msgstr "Serveur principal" + +#~ msgid "Backup Main Server" +#~ msgstr "Sauvegarde du serveur principal" + +#~ msgid "Read this first!!!" +#~ msgstr "A lire en premier !!!!" + +#~ msgid "Remove row" +#~ msgstr "Effacer une ligne" + +#~ msgid "Add row" +#~ msgstr "Ajouter une ligne" + +#~ msgid "Save to file" +#~ msgstr "Sauver dans un fichier" + +#~ msgid "ResultSet" +#~ msgstr "Résultat" + +#~ msgid "Ignore SQL errors" +#~ msgstr "Ignorer les erreurs SQL" + +#~ msgid "Options:" +#~ msgstr "Options :" + +#~ msgid "Total data length:" +#~ msgstr "Longueur totale des données :" + +#~ msgid "Total tables:" +#~ msgstr "Total des tables :" + +#~ msgid "Total schemas:" +#~ msgstr "Schémas totaux :" + +#~ msgid "Character Set:" +#~ msgstr "jeu de caractères :" + +#~ msgid "File:" +#~ msgstr "Fichier " + +#~ msgid "Information:" +#~ msgstr "Informations :" + +#~ msgid "Restore backup" +#~ msgstr "Restauration de la sauvegarde" + +#~ msgid "Restore executed successfully." +#~ msgstr "Restauration effectuée avec succès" + +#~ msgid "Opens the output file" +#~ msgstr "Ouvre le fichier de sortie" + +#~ msgid "Output to file" +#~ msgstr "Fichier de sortie" + +#~ msgid "Prints the output to a specified file. By default /home/user/Tmp.sql." +#~ msgstr "Affiche la sortie d'un fichier spécifié. Par défaut /home/user/Tmp.sql." + +#~ msgid "-t" +#~ msgstr "-" + +#~ msgid "Output in table format" +#~ msgstr "Format de la table de sortie" + +#~ msgid "-vvv" +#~ msgstr "-" + +#~ msgid "Shows the Query and Rows in Set in Table Format" +#~ msgstr "Affiche la requete et les lignes ensemble dans une table " + +#~ msgid "-vv" +#~ msgstr "-" + +#~ msgid "Shows the Query and Rows in Set" +#~ msgstr "Affiche la requete et les lignes ensemble" + +#~ msgid "-v" +#~ msgstr "-" + +#~ msgid "Shows the Query" +#~ msgstr "Affiche la requete" + +#~ msgid "New result tab" +#~ msgstr "Nouveau signet" + +#~ msgid "New Query" +#~ msgstr "Nouvelle requete" + +#~ msgid "Run query" +#~ msgstr "exécuter une requete" + +#~ msgid "Program set to write mode" +#~ msgstr "Programme configuré en modede lecture " + +#~ msgid "Program set to read only mode" +#~ msgstr "Programme configuré en mode de lecture seul" + +#~ msgid "Nothing to show." +#~ msgstr "Rien à afficher" + +#~ msgid "Database changed." +#~ msgstr "La base de données a changé" + +#~ msgid "Action not allowed in read only mode." +#~ msgstr "Action non permise en mode lecture seule" + +#~ msgid "Kill process" +#~ msgstr "Tuer le process" + +#~ msgid "Process" +#~ msgstr "Tache" + +#~ msgid "System Processes" +#~ msgstr "Taches système" + +#~ msgid "Info" +#~ msgstr "Info" + +#~ msgid "State" +#~ msgstr "Etat" + +#~ msgid "Time" +#~ msgstr "Heure" + +#~ msgid "Command" +#~ msgstr "Commande" + +#~ msgid "Database" +#~ msgstr "Base de données" + +#~ msgid "Host" +#~ msgstr "Serveur" + +#~ msgid "Id" +#~ msgstr "ID" + +#~ msgid "Show statements before execute them:" +#~ msgstr "Afficher les commandes avant de les exécuter :" + +#~ msgid "Show statements before execute them" +#~ msgstr "Afficher les commandes avant de les exécuter" + +#~ msgid "Show the information message before start editing:" +#~ msgstr "Affiche le message d'information avant de démarrer l'édition :" + +#~ msgid "Show the information message before start editing" +#~ msgstr "Affiche le message d'information avant de démarrer l'édition" + +#~ msgid "Result:" +#~ msgstr "Résultat :" + +#~ msgid "Result" +#~ msgstr "Résultat" + +#~ msgid "Numbers:" +#~ msgstr "Numéro :" + +#~ msgid "Sets the color for numbers in the editor" +#~ msgstr "Configure la couleur des nombres dans l'éditeur" + +#~ msgid "Font" +#~ msgstr "Police" + +#~ msgid "Font:" +#~ msgstr "Police :" + +#~ msgid "Sets the font type for the editor" +#~ msgstr "Configure le type de police de l'éditeur" + +#~ msgid "Keywords:" +#~ msgstr "Mots clés :" + +#~ msgid "Sets the color for keywords in the editor" +#~ msgstr "Configure la couleur des mots clés dans l'éditeur" + +#~ msgid "Tab size:" +#~ msgstr "Taille tab :" + +#~ msgid "Sets the tab size for the editor" +#~ msgstr "Configure la taille de tabulation de l'éditeur" + +#~ msgid "Strings:" +#~ msgstr "Chaines :" + +#~ msgid "Sets the color for strings in the editor" +#~ msgstr "Configure la couleur des chaines dans l'éditeur" + +#~ msgid "Symbols:" +#~ msgstr "Symboles :" + +#~ msgid "Sets the color for symbols in the editor" +#~ msgstr "Configure la couleur des symbols dans l'éditeur" + +#~ msgid "Functions:" +#~ msgstr "Functions :" + +#~ msgid "Sets the color for functions in the editor" +#~ msgstr "Configure la couleur des fonctions dans l'éditeur" + +#~ msgid "Operators:" +#~ msgstr "Opérateurs :" + +#~ msgid "Sets the color for operators in the editor" +#~ msgstr "Configure la couleur des opérateurs dans l'éditeur" + +#~ msgid "Data Types:" +#~ msgstr "Type des données :" + +#~ msgid "Sets the color for datatypes in the editor" +#~ msgstr "Configure la couleur du type de données dans l'éditeur" + +#~ msgid "&Restore" +#~ msgstr "&Restauration" + +#~ msgid "Show modified lines:" +#~ msgstr "Affiche les lignes modifiées :" + +#~ msgid "Show modified lines" +#~ msgstr "Afficher les lignes modifiées" + +#~ msgid "Comments:" +#~ msgstr "Commentaires :" + +#~ msgid "Sets the color for comments in the editor" +#~ msgstr "Configure la couleur des commentaites dans l'éditeur" + +#~ msgid "Editor:" +#~ msgstr "Editeur :" + +#~ msgid "Editor" +#~ msgstr "Editeur" + +#~ msgid "Path for restore files:" +#~ msgstr "Chemin des fichiers de restauration :" + +#~ msgid "Change the path to look for restore files" +#~ msgstr "Cherche le chemin pour restaurer les fichiers" + +#~ msgid "Dif Color" +#~ msgstr "Dif couleur" + +#~ msgid "Color for rows in results:" +#~ msgstr "Couleur des lignes pour le résultat" + +#~ msgid "Sets the color for rows in results" +#~ msgstr "Configure la couleur des lignes de résultats" + +#~ msgid "Custom messages" +#~ msgstr "Messages personnalisés" + +#~ msgid "Custom messages color:" +#~ msgstr "Couleur de messages personnalisés" + +#~ msgid "Sets the color for custom messages" +#~ msgstr "Configure la couleur des messages personnalisés" + +#~ msgid "Warning messages" +#~ msgstr "Messages d'avertissement" + +#~ msgid "Warning messages color:" +#~ msgstr "Couleur des messages d'avertissement" + +#~ msgid "Sets the color for warning messages" +#~ msgstr "Configure la couleur des avertissements de messages" + +#~ msgid "Error messages" +#~ msgstr "Messages d'erreur" + +#~ msgid "Error messages color:" +#~ msgstr "Couleur des messages d'erreur" + +#~ msgid "Sets the color for error messages" +#~ msgstr "Configure la couleur des messages d'erreur" + +#~ msgid "Information messages" +#~ msgstr "Messages d'information" + +#~ msgid "Information messages color:" +#~ msgstr "Couleur des messages d'information :" + +#~ msgid "Sets the color for information messages" +#~ msgstr "Configure la couleur des messages d'information" + +#~ msgid "Remember last windows opened:" +#~ msgstr "Rappel de la dernière fenetre ouverte :" + +#~ msgid "Remember last windows opened" +#~ msgstr "Rappel de la dernière fenetre ouverte" + +#~ msgid "Show error messages in a popup window:" +#~ msgstr "Affiche tous les messages dans un popup :" + +#~ msgid "Show error messages in a popup window" +#~ msgstr "Affiche les messages d'erreur dans une fenetre surgissante" + +#~ msgid "Store passwords:" +#~ msgstr "Enregistre le mot de passe :" + +#~ msgid "Store passwords" +#~ msgstr "Enregistre le mot de passe " + +#~ msgid "All keywords in uppercase:" +#~ msgstr "Tous les mots clés en majuscule :" + +#~ msgid "Show all Keywords in Uppercase" +#~ msgstr "Affiche tous les mots clés en majuscule" + +#~ msgid "Restore default values" +#~ msgstr "Restauration des valeurs par défaut" + +#~ msgid "Close forms by pessing Alt+Esc:" +#~ msgstr "Fermer le formulaire en appuant sur les touches Alt+Esc" + +#~ msgid "All Tabs/Forms will close if you press Esc key" +#~ msgstr "Tous les formulaires fermeront si vous appuyez sur la touche Esc" + +#~ msgid "General:" +#~ msgstr "Général :" + +#~ msgid "General" +#~ msgstr "Général" + +#~ msgid "Default values restored." +#~ msgstr "Valeurs restaurées par défaut." + +#~ msgid "UPDATE" +#~ msgstr "MAJ" + +#~ msgid "INSERT" +#~ msgstr "INSERT" + +#~ msgid "DELETE" +#~ msgstr "EFFACER" + +#~ msgid "BEFORE" +#~ msgstr "AVANT" + +#~ msgid "AFTER" +#~ msgstr "APRES" + +#~ msgid "URL:" +#~ msgstr "-" + +#~ msgid "Syntax:" +#~ msgstr "Syntaxe :" + +#~ msgid "" +#~ "Could not find any help.\n" +#~ "Please check those tables:\n" +#~ msgstr "" +#~ "Impossible de trouver de l'aide .\n" +#~ "SVP Vérifier ces tables :\n" + +#~ msgid "In this option you will find the information about the application." +#~ msgstr "Dans cette option vous trouverez les informations à propos de cette application" + +#~ msgid "Shows this help" +#~ msgstr "Affiche cet aide" + +#~ msgid "In this option you will find this help" +#~ msgstr "Dans cette option vous trouverez cette aide " + +#~ msgid "Runs a script" +#~ msgstr "Exécute un script" + +#~ msgid "Runs a query" +#~ msgstr "Exécute une requete" + +#~ msgid "Shortcuts" +#~ msgstr "Raccourcis" + +#~ msgid "Generate a Gambas module to create the Database, Tables, Routines..." +#~ msgstr "Générer un module Gambas pour créer la base de données, tables, routines..." + +#~ msgid "Click the status bar to see the complete message in a new window." +#~ msgstr "Cliquer sur la barre d'état afin de voir le message complet dans la nouvelle fenêtre" + +#~ msgid "Features" +#~ msgstr "Caractéristiques" + +#~ msgid "is an application to interact with a MySQL Server and is based on gb.mysql. Its propose is to provide a friendly interface to MySQL, it is the fusion of: gb.database-manager, mysql-query-browser and mysql-administrator" +#~ msgstr "Ceci est une application pour gérer le serveur Mysql, elle est basée sur gb.mysql. Son propos est de procurer une interface conviviale à Mysql, c'est la fusion entre : gb.database-manager, mysql-query-browser et mysql-administrator" + +#~ msgid "General Information:" +#~ msgstr "Information générale :" + +#~ msgid "Generate" +#~ msgstr "Générer" + +#~ msgid "Destination:" +#~ msgstr "Destination" + +#~ msgid "TXT" +#~ msgstr "-" + +#~ msgid "XML" +#~ msgstr "-" + +#~ msgid "HTML" +#~ msgstr "HTML" + +#~ msgid "Format Selection:" +#~ msgstr "Sélection du format :" + +#~ msgid "Procedures" +#~ msgstr "Procédures" + +#~ msgid "Functions" +#~ msgstr "Functions" + +#~ msgid "Databases" +#~ msgstr "Base de données" + +#~ msgid "Object Selection:" +#~ msgstr "Sélection d'objet :" + +#~ msgid "Select the databases:" +#~ msgstr "Base de données sélectionnées :" + +#~ msgid "Generate Documentation" +#~ msgstr "Générer la documentation" + +#~ msgid "Diff" +#~ msgstr "Diff" + +#~ msgid "No difference" +#~ msgstr "Aucune différence" + +#~ msgid "Cancel" +#~ msgstr "Annuler" + +#~ msgid "Changes" +#~ msgstr "Changements" + +#~ msgid "Password" +#~ msgstr "Mot de passe " + +#~ msgid "User" +#~ msgstr "Utilisateur" + +#~ msgid "Authentification" +#~ msgstr "--" + +#~ msgid "Previous description" +#~ msgstr "Description précédentes" + +#~ msgid "Clean up" +#~ msgstr "Nettoyer tout" + +#~ msgid "Unindent" +#~ msgstr "Désindenter" + +#~ msgid "Indent" +#~ msgstr "Indenter" + +#~ msgid "Redo" +#~ msgstr "Refaire " + +#~ msgid "Undo" +#~ msgstr "Défaire" + +#~ msgid "Paste" +#~ msgstr "Coller" + +#~ msgid "Copy" +#~ msgstr "Copier" + +#~ msgid "Cut" +#~ msgstr "Couper" + +#~ msgid "Commit description" +#~ msgstr "Description de validation" + +#~ msgid "Description" +#~ msgstr "Description" + +#~ msgid "Commit" +#~ msgstr "Valider" + +#~ msgid "Please enter the password" +#~ msgstr "SVP entrez le mot de passe" + +#~ msgid "Please enter the change log" +#~ msgstr "SVP entrez le fichier log " + +#~ msgid "Please enter the versioning user name" +#~ msgstr "SVP entrez le nom de version utilisateur" + +#~ msgid "Server" +#~ msgstr "Serveur" + +#~ msgid "Close all forms" +#~ msgstr "Fermer tous les formulaires" + +#~ msgid "Close form" +#~ msgstr "Fermer le formulaire" + +#~ msgid "Hide panels" +#~ msgstr "Cacher le panneau" + +#~ msgid "Main" +#~ msgstr "Principal" + +#~ msgid "File" +#~ msgstr "Fichier" + +#~ msgid "Processes" +#~ msgstr "Taches" + +#~ msgid "Preferences" +#~ msgstr "Préferrences" + +#~ msgid "Documentation" +#~ msgstr "Documentation" + +#~ msgid "Help" +#~ msgstr "Aide" + +#~ msgid "MySQL Help" +#~ msgstr "Aide Mysql" + +#~ msgid "Script" +#~ msgstr "Script" + +#~ msgid "Table Maintenance" +#~ msgstr "Table de maintenance" + +#~ msgid "Query" +#~ msgstr "Requete" + +#~ msgid "Schemas" +#~ msgstr "Schémas" + +#~ msgid "Schema" +#~ msgstr "Schéma" + +#~ msgid "Restore Backup" +#~ msgstr "Restauration de la sauvergarde" + +#~ msgid "User Administration" +#~ msgstr "Administrateur " + +#~ msgid "Server Information" +#~ msgstr "Info serveur" + +#~ msgid "Backup Content" +#~ msgstr "Contenu de la sauvegarde" + +#~ msgid "Catalogs" +#~ msgstr "Catalogues" + +#~ msgid "&Start Backup" +#~ msgstr "&Démarrer la sauvegarde" + +#~ msgid "Project Name:" +#~ msgstr "Nom du projet :" + +#~ msgid "Backup Project
Define the project name and the content of the backup" +#~ msgstr "Projet de sauvegarde
Défini le nom du projet et le contenu de la sauvegarde" + +#~ msgid "Backup" +#~ msgstr "Sauvegarde" + +#~ msgid "Done" +#~ msgstr "Fait" + +#~ msgid "Please select a database" +#~ msgstr "SVP sélectionnez une base de données" + +#~ msgid "Data Length" +#~ msgstr "Longueur des données" + +#~ msgid "Table Type" +#~ msgstr "Type de table" + +#~ msgid "Objects" +#~ msgstr "Objets" + +#~ msgid "" +#~ "MYSQL-GUI MySQL Database administrator for Gambas.\n" +#~ "Copyright (C) 2008-2010 David Villalobos Cambronero.\n" +#~ "\n" +#~ "This program is free software: you can redistribute it and/or modify\n" +#~ "it under the terms of the GNU General Public License as published by\n" +#~ "the Free Software Foundation, either version 3 of the License, or\n" +#~ "(at your option) any later version.\n" +#~ "\n" +#~ "This program is distributed in the hope that it will be useful,\n" +#~ "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +#~ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +#~ "GNU General Public License for more details.\n" +#~ "\n" +#~ "You should have received a copy of the GNU General Public License\n" +#~ "along with this program. If not, see ." +#~ msgstr "" +#~ "MYSQL-GUI MySQL administrateur de base de données pour Gambas.\n" +#~ "Copyright (C) 2008-2010 David Villalobos Cambronero.\n" +#~ "\n" +#~ " (traduction française Henri Girard). Ce programme est un logiciel libre : Vous pouvez le distribuer et/ou le modifier\n" +#~ "Selon les termes de GNU General Public License, telle que publiée part\n" +#~ "the Free Software Foundation, Soit en version 3 de la License, ou\n" +#~ "(selon votre choix) dans n'importe-quelle version ultérieure.\n" +#~ "\n" +#~ "Ce programme est distribué avec l'espoir qu'il soit utile,\n" +#~ "mais absolument sans aucune garantie ; Meme sans aucune garantie que l'on pourrait attendre d'un logiciel, de commercialisation ou d'utilité dans un domaine précis.\n" +#~ "Voir GNU General Public License pour plus de détail.\n" +#~ "\n" +#~ "Vous devriez avoir reçu une copie de GNU General Public License\n" +#~ "avec ce programme. Sinon voyez ." + +#~ msgid "License" +#~ msgstr "License" + +#~ msgid "About" +#~ msgstr "A propos" + +#~ msgid "A simple Graphic User Interface for MySQL" +#~ msgstr "Un simple GUI pour MySQL" + +#~ msgid "mysql-gui" +#~ msgstr "-" diff --git a/app/examples/Database/MySQLExample/.lang/nl.mo b/app/examples/Database/MySQLExample/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..24a018d3096f8bf90b70714cc4b63bc01f0becd6 GIT binary patch literal 6578 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\n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FConnect.form:28 +msgid "MySQL Example" +msgstr "MySQL Voorbeeld" + +#: .project:2 +msgid "A simple GB.MYSQL Example" +msgstr "Een eenvoudig GB.MYSQL voorbeeld" + +#: FConnect.form:20 +msgid "MySQL GUI" +msgstr "-" + +#: FConnect.form:35 +msgid "Connect to a MySQL Server" +msgstr "Verbind met een MySQL Server" + +#: FConnect.form:39 +msgid "Server:" +msgstr "-" + +#: FConnect.form:50 +msgid "Port:" +msgstr "Poort:" + +#: FConnect.form:61 +msgid "User:" +msgstr "Gebruiker:" + +#: FConnect.form:72 +msgid "Password:" +msgstr "Wachtwoord:" + +#: FConnect.form:84 +msgid "&Connect" +msgstr "&Verbind" + +#: FConnect.form:91 +msgid "&Clear" +msgstr "&Opschonen" + +#: FNewView.form:52 +msgid "&Cancel" +msgstr "&Annuleren" + +#: FMessage.form:8 +msgid "Message" +msgstr "Bericht" + +#: FNewDatabase.form:16 +msgid "Create Database" +msgstr "Creëer Database" + +#: FNewRoutine.form:35 FNewTable.form:32 FNewTrigger.form:34 FNewView.form:30 +msgid "Name:" +msgstr "Naam:" + +#: FNewDatabase.form:42 +msgid "Collation:" +msgstr "Collatie:" + +#: FNewDatabase.form:53 FNewTable.form:53 +msgid "Charset:" +msgstr "Karakterset:" + +#: FNewDatabase.form:63 +msgid "&Create" +msgstr "&Creëer" + +#: FNewEvent.form:16 +msgid "Create Event" +msgstr "Creëer Gebeurtenis" + +#: FNewEvent.form:42 +msgid "Schedule:" +msgstr "Schema:" + +#: FNewEvent.form:47 FNewRoutine.form:50 +msgid "Comma separated values: Hola1 INT,Hola2 CHAR(5)" +msgstr "Comma gesepareerde waarden: Hola1 INT, Hola2 CHAR(5)" + +#: FNewEvent.form:52 FNewRoutine.form:69 FNewTrigger.form:67 FNewView.form:39 +msgid "Show a clue" +msgstr "Aanwijzing weergeven" + +#: FNewTable.form:70 FNewTrigger.form:73 FNewView.form:45 +msgid "&Ok" +msgstr "&Ok" + +#: FNewEvent.class:19 +msgid "Edit Event" +msgstr "Bewerk Gebeurtenis" + +#: FNewField.form:22 +msgid "Add Field" +msgstr "Veld toevoegen" + +#: FNewField.form:47 +msgid "Data Type:" +msgstr "-" + +#: FNewField.form:57 +msgid "Acepts NULLs:" +msgstr "Accepteert NULL's" + +#: FNewField.form:63 +msgid "No" +msgstr "Neen" + +#: FNewField.form:63 +msgid "Yes" +msgstr "Ja" + +#: FNewField.form:69 +msgid "Default:" +msgstr "Standaard:" + +#: FNewField.form:79 +msgid "Extra:" +msgstr "-" + +#: FNewField.form:89 +msgid "Comment:" +msgstr "Commentaar:" + +#: FNewField.class:22 +msgid "Edit Field" +msgstr "Bewerk Veld" + +#: FNewIndex.form:18 +msgid "Create Index" +msgstr "Creëer Index" + +#: FNewIndex.form:23 +msgid "Delete" +msgstr "Verwijder" + +#: FNewIndex.form:55 +msgid "Fields:" +msgstr "Velden:" + +#: FNewIndex.form:68 +msgid "Primary Key" +msgstr "Primaire Sleutel" + +#: FNewIndex.class:24 +msgid "Edit Index" +msgstr "Bewerk Index" + +#: FNewRoutine.form:19 +msgid "Create Routine" +msgstr "Creëer Routine" + +#: FNewRoutine.form:45 +msgid "Parameters:" +msgstr "-" + +#: FNewRoutine.form:56 +msgid "Returns:" +msgstr "Retourneren:" + +#: FNewRoutine.class:19 +msgid "Edit Routine" +msgstr "Bewerk Routine" + +#: FNewTable.form:16 +msgid "Create Table" +msgstr "Creëer Tabel" + +#: FNewTable.form:42 +msgid "Engine:" +msgstr "Motor:" + +#: FNewTable.class:21 +msgid "Edit Table" +msgstr "Bewerk Tabel" + +#: FNewTrigger.form:18 +msgid "Create Trigger" +msgstr "Creëer Trigger" + +#: FNewTrigger.form:44 +msgid "Time:" +msgstr "Tijd:" + +#: FNewTrigger.form:56 +msgid "Event:" +msgstr "Gebeurtenis:" + +#: FNewTrigger.class:28 +msgid "Edit Trigger" +msgstr "Bewerk Trigger" + +#: FNewView.form:14 +msgid "Create View" +msgstr "Creëer Weergave" + +#: FNewView.class:18 +msgid "Edit View" +msgstr "Bewerk Weergave" + +#: FTables.form:35 +msgid "Tables" +msgstr "Tabellen" + +#: FTables.form:40 +msgid "Item" +msgstr "Element" + +#: FTables.form:45 +msgid "New Item" +msgstr "Nieuw Element" + +#: FTables.form:52 +msgid "Edit Item" +msgstr "Bewerk Element" + +#: FTables.form:59 +msgid "Delete Item" +msgstr "Verwijder Element" + +#: FTables.form:66 +msgid "Refresh" +msgstr "Vernieuwen" + +#: FTables.form:84 +msgid "Lock" +msgstr "Slot" + +#: FTables.form:96 +msgid "New Table" +msgstr "Nieuwe Tabel" + +#: FTables.form:103 +msgid "Delete Table" +msgstr "Verwijder Tabel" + +#: FTables.form:121 +msgid "New Database" +msgstr "Nieuwe Database" + +#: FTables.form:128 +msgid "Delete Database" +msgstr "Verwijder Database" + +#: FTables.form:159 +msgid "Views" +msgstr "Weergaven" + +#: FTables.form:169 +msgid "Indexes" +msgstr "Indices" + +#: FTables.form:179 +msgid "Fields on:" +msgstr "Velden aan:" + +#: FTables.form:188 +msgid "Routines" +msgstr "-" + +#: FTables.form:198 +msgid "Triggers" +msgstr "-" + +#: FTables.form:208 +msgid "Events" +msgstr "Gebeurtenissen" + +#: FTables.class:9 +msgid "Name" +msgstr "Naam" + +#: FTables.class:10 +msgid "Rows" +msgstr "Rijen" + +#: FTables.class:11 +msgid "Engine" +msgstr "Motor" + +#: FTables.class:12 +msgid "Charset" +msgstr "Karakterset" + +#: FTables.class:13 +msgid "Collation" +msgstr "Collatie" + +#: FTables.class:14 +msgid "Size" +msgstr "Maat" + +#: FTables.class:15 +msgid "Update Time" +msgstr "Update tijd" + +#: FTables.class:20 +msgid "Updatable" +msgstr "Actualiseerbaar" + +#: FTables.class:21 +msgid "Definer" +msgstr "Definiëerder" + +#: FTables.class:22 +msgid "Security" +msgstr "Beveiliging" + +#: FTables.class:23 +msgid "Check" +msgstr "Controleer" + +#: FTables.class:30 +msgid "Column" +msgstr "Kolom" + +#: FTables.class:32 +msgid "Type" +msgstr "-" + +#: FTables.class:38 +msgid "Returns" +msgstr "Retourneren" + +#: FTables.class:39 +msgid "Creation Time" +msgstr "Creatietijd" + +#: FTables.class:43 +msgid "Comment" +msgstr "Commentaar" + +#: FTables.class:49 +msgid "Default" +msgstr "Standaard" + +#: FTables.class:50 +msgid "Nullable" +msgstr "-" + +#: FTables.class:51 +msgid "DataType" +msgstr "-" + +#: FTables.class:52 +msgid "Key" +msgstr "Sleutel" + +#: FTables.class:53 +msgid "Extra" +msgstr "-" + +#: FTables.class:61 +msgid "Event" +msgstr "Gebeurtenis" + +#: FTables.class:62 +msgid "Table" +msgstr "Tabel" + +#: FTables.class:63 +msgid "Timing" +msgstr "-" + +#: FTables.class:70 +msgid "Time Zone" +msgstr "Tijd Zone" + +#: FTables.class:72 +msgid "Execute At" +msgstr "Uitvoeren op" + +#: FTables.class:73 +msgid "Interval Value" +msgstr "Interval Waarde" + +#: FTables.class:74 +msgid "Interval Field" +msgstr "Interval Veld" + +#: FTables.class:75 +msgid "SQL Mode" +msgstr "SQL Modus" + +#: FTables.class:76 +msgid "Starts" +msgstr "-" + +#: FTables.class:77 +msgid "Ends" +msgstr "-" + +#: FTables.class:78 +msgid "On Completion" +msgstr "Bij voltooiing" + +#: FTables.class:79 +msgid "Created" +msgstr "Gecreëerd" + +#: FTables.class:80 +msgid "Last Altered" +msgstr "Laast gewijzigd" + +#: FTables.class:81 +msgid "Last Executed" +msgstr "Laatst uitgevoerd" + +#: FTables.class:82 +msgid "Originator" +msgstr "-" + +#: FTables.class:85 +msgid "Database Collation" +msgstr "Database collatie" + +#: FTables.class:112 +msgid "Fields" +msgstr "Velden" + +#: FTables.class:330 +msgid "Not aviable" +msgstr "Niet beschikbaar" + +#: FTables.class:363 +msgid "Indexes on:" +msgstr "Indices aan:" + +#: FTables.class:880 +msgid "New View" +msgstr "Nieuwe Weergave" + +#: FTables.class:881 +msgid "Delete View" +msgstr "Verwijder Weergave" + +#: FTables.class:883 +msgid "New Index" +msgstr "Nieuwe Index" + +#: FTables.class:884 +msgid "Delete Index" +msgstr "Verwijder Index" + +#: FTables.class:886 +msgid "New Field" +msgstr "Nieuw Veld" + +#: FTables.class:887 +msgid "Delete Field" +msgstr "Verwijder Veld" + +#: FTables.class:889 +msgid "New Routine" +msgstr "Nieuwe Routine" + +#: FTables.class:890 +msgid "Delete Routine" +msgstr "Verwijder Routine" + +#: FTables.class:892 +msgid "New Trigger" +msgstr "Nieuwe Trigger" + +#: FTables.class:893 +msgid "Delete Trigger" +msgstr "Verwijder Trigger" + +#: FTables.class:895 +msgid "New Event" +msgstr "Nieuwe Gebeurtenis" + +#: FTables.class:896 +msgid "Delete Event" +msgstr "Verwijder Gebeurtenis" + +#: FTables.class:927 +msgid "Do you realy want to delete the database: &1?" +msgstr "Wil je werkelijk de database &1 verwijderen?" + +#: FTables.class:927 +msgid "&Yes" +msgstr "&Ja" + +#: FTables.class:927 +msgid "&No" +msgstr "&Neen" + +#: FTables.class:1019 +msgid "Do you realy want to delete the table: &1?" +msgstr "Wil je werkelijk de tabel &1 verwijderen?" + +#: FTables.class:1021 +msgid "Do you realy want to delete the view: &1?" +msgstr "Wil je werkelijk de weergave &1 verwijderen?" + +#: FTables.class:1023 +msgid "Do you realy want to delete the index: &1?" +msgstr "Wil je werkelijk de index &1 verwijderen?" + +#: FTables.class:1025 +msgid "Do you realy want to delete the field: &1?" +msgstr "Wil je werkelijk het veld &1 verwijderen?" + +#: FTables.class:1027 +msgid "Do you realy want to delete the routine: &1?" +msgstr "Wil je werkelijk de routine &1 verwijderen?" + +#: FTables.class:1029 +msgid "Do you realy want to delete the trigger: &1?" +msgstr "Wil je werkelijk de trigger &1 verwijderen?" + +#: FTables.class:1031 +msgid "Do you realy want to delete the event: &1?" +msgstr "Wil je werkelijk de gebeurtenis &1 verwijderen?" + +#: FTables.class:1034 +msgid "Item deleted." +msgstr "Element verwijdert:" + diff --git a/app/examples/Database/MySQLExample/.project b/app/examples/Database/MySQLExample/.project new file mode 100644 index 00000000..c52728fa --- /dev/null +++ b/app/examples/Database/MySQLExample/.project @@ -0,0 +1,26 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=MySQL Example +Startup=modMain +Icon=icons/16/Admin.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.db +Component=gb.db.mysql +Component=gb.settings +Component=gb.form.mdi +Component=gb.mysql +Description="A simple GB.MYSQL Example" +Authors="David Villalobos Cambronero\ndavid.villalobos.c@gmail.com" +TabSize=2 +Translate=1 +Language=en +SourcePath=/media +Maintainer=David Villalobos Cambronero +Address=david_villalobos_c@yahoo.com +License=General Public License +Packager=1 +Systems=autotools diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.class b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.class new file mode 100644 index 00000000..ab7f91bf --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.class @@ -0,0 +1,33 @@ +' Gambas class file + +Public Sub Form_Open() + + cmbCharset.List = modMain.$Connection.MySQL.Charsets + cmbCharset.Index = cmbCharset.Find("utf8") + Me.Center() + Catch + modMain.Error() + +End + +Public Sub btnCancel_Click() + + Me.Close() + +End + +Public Sub btnCreate_Click() + + modMain.$Connection.MySQL.DataBase.Add(txtNombre.Text, cmbCharset.Text, cmbCollations.Text) + modMain.$Connection.MySQL.Use(txtNombre.Text) + Me.Close() + Catch + modMain.Error() + +End + +Public Sub cmbCharset_Click() + + cmbCollations.List = modMain.$Connection.MySQL.CharsetCollations(cmbCharset.Text) + +End diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.form b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.form new file mode 100644 index 00000000..3f1c8c49 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewDatabase.form @@ -0,0 +1,54 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,36,28) + Text = ("Create Database") + Icon = Picture["icons/16/Database.png"] + Resizable = False + { lblTitle Label + MoveScaled(1,1,34,5) + Font = Font["+4"] + Background = Color.LightBackground + Text = ("Create Database") + Alignment = Align.Center + Border = Border.Sunken + } + { lblName Label + MoveScaled(1,8,11,3) + Font = Font["+1"] + Text = ("Name:") + } + { txtNombre TextBox + MoveScaled(13,8,22,3) + } + { lblCollation Label + MoveScaled(1,18,11,3) + Font = Font["+1"] + Text = ("Collation:") + } + { cmbCollations ComboBox + MoveScaled(13,18,22,3) + ReadOnly = True + } + { lblCharset Label + MoveScaled(1,13,11,3) + Font = Font["+1"] + Text = ("Charset:") + } + { cmbCharset ComboBox + MoveScaled(13,13,22,3) + ReadOnly = True + } + { btnCreate Button + MoveScaled(1,23,13,4) + Text = ("&Create") + Picture = Picture["icon:/16/new"] + Default = True + } + { btnCancel Button + MoveScaled(22,23,13,4) + Text = ("&Cancel") + Picture = Picture["icon:/16/cancel"] + Cancel = True + } +} diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.class b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.class new file mode 100644 index 00000000..497e99c3 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.class @@ -0,0 +1,51 @@ +' Gambas class file + +Private $sEvent As String +Private $bEdit As Boolean = False + +Public Sub _new(Optional {Event} As String) + + If {Event} Then + $sEvent = {Event} + $bEdit = True + Endif + +End + +Public Sub Form_Open() + + If $bEdit Then + txtName.Enabled = False + Me.Text = ("Edit Event") + lblTitle.Text = Me.Text + txtName.Text = $sEvent + txtSchedule.Text = modMain.$Connection.MySQL.Event.Schedule($sEvent) + txtData.Text = modMain.$Connection.MySQL.Event.Definition($sEvent) + Else + txtData.Text = "BEGIN\n \nEND" + Endif + +End + +Public Sub btnCancel_Click() + + Me.Close() + +End + +Public Sub tbnOK_Click() + + modMain.$Connection.MySQL.Event.Add(txtName.Text, txtSchedule.Text, txtData.Text, IIf($bEdit, True, False)) + Me.Close() + Catch + modMain.Error() + +End + +Public Sub btnClue_Click() + + txtName.Text = "FooEvent" + txtSchedule.Text = "EVERY 1 DAY STARTS NOW()" + txtData.Text = "BEGIN\n SELECT `User` FROM `mysql`.`user`;\nEND" + +End diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.form b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.form new file mode 100644 index 00000000..434054ee --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewEvent.form @@ -0,0 +1,53 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,73,54) + Text = ("Create Event") + Icon = Picture["icon:/16/clock"] + Resizable = False + { lblTitle Label + MoveScaled(1,1,71,5) + Font = Font["+4"] + Background = Color.LightBackground + Text = ("Create Event") + Alignment = Align.Center + Border = Border.Sunken + } + { lblName Label + MoveScaled(1,8,15,3) + Font = Font["+1"] + Text = ("Name:") + } + { txtName TextBox + MoveScaled(17,8,55,3) + } + { lblSchedule Label + MoveScaled(1,12,15,3) + Font = Font["+1"] + Text = ("Schedule:") + } + { txtSchedule TextBox + MoveScaled(17,12,55,3) + ToolTip = ("Comma separated values: Hola1 INT,Hola2 CHAR(5)") + } + { btnClue Button + MoveScaled(1,49,5,4) + ToolTip = ("Show a clue") + Picture = Picture["icon:/22/help"] + } + { tbnOK Button + MoveScaled(44,49,13,4) + Text = ("&Ok") + Picture = Picture["icon:/16/ok"] + Default = True + } + { btnCancel Button + MoveScaled(59,49,13,4) + Text = ("&Cancel") + Picture = Picture["icon:/16/cancel"] + Cancel = True + } + { txtData TextArea + MoveScaled(1,16,71,32) + } +} diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.class b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.class new file mode 100644 index 00000000..afa34505 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.class @@ -0,0 +1,53 @@ +' Gambas class file + +Private $sTable As String +Private $sFlied As String +Private $bEdit As Boolean = False + +Public Sub _new(Table As String, Optional Field As String) + + If Field Then + $sFlied = Field + $bEdit = True + Endif + $sTable = Table + +End + +Public Sub Form_Open() + + txtName.Text = $sFlied + If $bEdit Then + txtName.Enabled = False + Me.Text = ("Edit Field") + lblTitle.Text = Me.Text + txtDatatype.Text = modMain.$Connection.MySQL.Field.FieldEspecifications($sFlied, $sTable).Datatype() + txtComment.Text = modMain.$Connection.MySQL.Field.FieldEspecifications($sFlied, $sTable).Commnet() + txtDefault.Text = modMain.$Connection.MySQL.Field.FieldEspecifications($sFlied, $sTable).DefaultValue() + txtExtra.Text = modMain.$Connection.MySQL.Field.FieldEspecifications($sFlied, $sTable).Extra() + cmbNull.Index = CInt(modMain.$Connection.MySQL.Field.FieldEspecifications($sFlied, $sTable).IsNullable()) + Endif + +End + +Public Sub btnCancel_Click() + + Me.Close() + +End + +Public Sub tbnOK_Click() + + Dim sDefinition As String + + sDefinition = txtDatatype.Text & " " + If cmbNull.Index = 0 Then sDefinition &= "NOT NULL" + If txtDefault.Text <> "" Then sDefinition &= " DEFAULT '" & txtDefault.Text & "'" + If txtExtra.Text <> "" Then sDefinition &= " " & txtExtra.Text + If txtComment.Text <> "" Then sDefinition &= " COMMENT '" & txtComment.Text & "'" + modMain.$Connection.MySQL.Table.ModifyColumn($sTable, IIf($bEdit, "MODIFY", "ADD"), txtName.Text, sDefinition) + Me.Close() + Catch + modMain.Error() + +End diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form new file mode 100644 index 00000000..c467814d --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewField.form @@ -0,0 +1,77 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,40,37) + Text = ("Add Field") + Icon = Picture["icons/16/Field.png"] + { lblTitle Label + MoveScaled(1,1,38,5) + Font = Font["+4"] + Background = Color.LightBackground + Text = ("Add Field") + Alignment = Align.Center + Border = Border.Sunken + } + { lblName Label + MoveScaled(1,8,11,3) + Font = Font["+1"] + Text = ("Name:") + } + { txtName TextBox + MoveScaled(13,8,26,3) + } + { lblDatatype Label + MoveScaled(1,12,15,3) + Font = Font["+1"] + Text = ("Data Type:") + } + { txtDatatype TextBox + MoveScaled(17,12,22,3) + } + { lblNull Label + MoveScaled(1,16,15,3) + Font = Font["+1"] + Text = ("Acepts NULLs:") + } + { cmbNull ComboBox + MoveScaled(17,16,8,3) + ReadOnly = True + List = [("No"), ("Yes")] + } + { lblDefault Label + MoveScaled(1,20,11,3) + Font = Font["+1"] + Text = ("Default:") + } + { txtDefault TextBox + MoveScaled(13,20,26,3) + } + { lblExtra Label + MoveScaled(1,24,11,3) + Font = Font["+1"] + Text = ("Extra:") + } + { txtExtra TextBox + MoveScaled(13,24,26,3) + } + { lblComment Label + MoveScaled(1,28,15,3) + Font = Font["+1"] + Text = ("Comment:") + } + { txtComment TextBox + MoveScaled(17,28,22,3) + } + { tbnOK Button + MoveScaled(1,32,13,4) + Text = ("&Ok") + Picture = Picture["icon:/16/ok"] + Default = True + } + { btnCancel Button + MoveScaled(26,32,13,4) + Text = ("&Cancel") + Picture = Picture["icon:/16/cancel"] + Cancel = True + } +} diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.class b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.class new file mode 100644 index 00000000..c5b9242b --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.class @@ -0,0 +1,79 @@ +' Gambas class file + +Private $sTable As String +Private $sIndex As String +Private $bEdit As Boolean = False + +Public Sub _new(Table As String, Optional Index As String) + + If Index Then + $sIndex = Index + $bEdit = True + Endif + $sTable = Table + cmbFields.List = modMain.$Connection.MySQL.Table.Fields(Table) + If Index = "PRIMARY" Then chkPrimaryKey.Value = True + +End + +Public Sub Form_Open() + + Dim sField As String + + If $bEdit Then + Me.Text = ("Edit Index") + lblTitle.Text = Me.Text + If $sIndex <> "PRIMARY" Then txtName.Enabled = False + txtName.Text = $sIndex + For Each sField In modMain.$Connection.MySQL.Index.Columns($sTable, $sIndex) + lstFields.Add(sField) + Next + Endif + +End + +Public Sub btnCancel_Click() + + Me.Close() + +End + +Public Sub tbnOK_Click() + + If $bEdit Then + If chkPrimaryKey.Value Then + modMain.$Connection.MySQL.Index.Delete($sTable, "PRIMARY") + modMain.$Connection.MySQL.Index.PrimaryKey($sTable, lstFields.List) + Else + modMain.$Connection.MySQL.Index.Modify(txtName.Text, lstFields.List, $sTable) + Endif + Else + If chkPrimaryKey.Value Then + modMain.$Connection.MySQL.Index.PrimaryKey($sTable, lstFields.List) + Else + modMain.$Connection.MySQL.Index.Add(txtName.Text, lstFields.List, $sTable) + Endif + Endif + Me.Close() + Catch + modMain.Error() + +End + +Public Sub cmbFields_Click() + + lstFields.Add(cmbFields.Text) + +End + +Public Sub lstFields_Menu() + + mnuDelete.Popup() + +End + +Public Sub Action_Activate(Name As String) As Boolean + + If Name = "Delete" Then lstFields.Remove(lstFields.Index) + +End diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.form b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.form new file mode 100644 index 00000000..1b732e44 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewIndex.form @@ -0,0 +1,70 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,36,46) + Text = ("Create Index") + Icon = Picture["icons/16/Index.png"] + Resizable = False + { mnuDelete Menu + Text = ("Delete") + Visible = False + { mnuDelete2 Menu + Action = "Delete" + Text = ("Delete") + Picture = Picture["icon:/16/trash"] + Shortcut = "Del" + } + } + { lblTitle Label + MoveScaled(1,1,34,5) + Font = Font["+4"] + Background = Color.LightBackground + Text = ("Create Index") + Alignment = Align.Center + } + { lblName Label + MoveScaled(1,8,11,3) + Font = Font["+1"] + Text = ("Name:") + } + { txtName TextBox + MoveScaled(13,8,22,3) + } + { lblFields Label + MoveScaled(1,12,11,3) + Font = Font["+1"] + Text = ("Fields:") + } + { lstFields ListBox + MoveScaled(13,12,22,19) + } + { cmbFields ComboBox + MoveScaled(13,32,22,3) + } + { chkPrimaryKey CheckBox + MoveScaled(1,37,34,3) + Text = ("Primary Key") + } + { tbnOK Button + MoveScaled(1,41,13,4) + Text = ("&Ok") + Picture = Picture["icon:/16/ok"] + Default = True + } + { btnCancel Button + MoveScaled(22,41,13,4) + Text = ("&Cancel") + Picture = Picture["icon:/16/cancel"] + Cancel = True + } +} + +# Gambas Action File 3.0 + +{ Actions + { Action Delete + Text = "Delete" + Shortcut = "Del" + Picture = "icon:/16/trash" + } +} diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.class b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.class new file mode 100644 index 00000000..e0bdb26b --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.class @@ -0,0 +1,65 @@ +' Gambas class file + +Private $sRutine As String +Private $bEdit As Boolean = False + +Public Sub _new(Optional Rutine As String) + + If Rutine Then + $sRutine = Rutine + $bEdit = True + Endif + txtName.Text = $sRutine + +End + +Public Sub Form_Open() + + If $bEdit Then + Me.Text = ("Edit Routine") + lblTitle.Text = Me.Text + txtName.Enabled = False + txtParameters.Text = modMain.$Connection.MySQL.Routines.Parameters($sRutine) + txtReturns.Text = modMain.$Connection.MySQL.Routines.Returns($sRutine) + txtData.Text = modMain.$Connection.MySQL.Routines.Definition($sRutine) + Else + txtData.Text = "BEGIN\n \nEND" + Endif + +End + +Public Sub btnCancel_Click() + + Me.Close() + +End + +Public Sub tbnOK_Click() + + If $bEdit Then + modMain.$Connection.MySQL.Routines.Modify($sRutine, txtData.Text, Split(Trim(txtParameters.Text)), txtReturns.Text) + Else + modMain.$Connection.MySQL.Routines.Add(txtName.Text, txtData.Text, Split(Trim(txtParameters.Text)), txtReturns.Text) + Endif + Me.Close() + Catch + modMain.Error() + +End + +Public Sub btnClue_Click() + + If txtName.Text = "FooProcedure" Then + txtName.Text = "FooFunction" + txtParameters.Text = "Param1 INT,Param2 VARCHAR(200)" + txtReturns.Text = "INT" + txtData.Text = "BEGIN\n SELECT `User` FROM `mysql`.`user` WHERE `User` = `Param2`;\n" + txtData.Text &= " RETURN `Param1`\nEND" + Else + txtName.Text = "FooProcedure" + txtParameters.Text = "Param1 INT,Param2 VARCHAR(200)" + txtReturns.Text = "" + txtData.Text = "BEGIN\n SELECT `User` FROM `mysql`.`user` WHERE `User` = `Param2`;\nEND" + Endif + +End diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.form b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.form new file mode 100644 index 00000000..6db63ba7 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewRoutine.form @@ -0,0 +1,71 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,73,58) + Action = "Show a clue" + Text = ("Create Routine") + Icon = Picture["icons/16/Routine.png"] + Resizable = False + { lblTitle Label + MoveScaled(1,1,71,5) + Font = Font["+4"] + Background = Color.LightBackground + Text = ("Create Routine") + Alignment = Align.Center + Border = Border.Sunken + } + { lblName Label + MoveScaled(1,8,11,3) + Font = Font["+1"] + Text = ("Name:") + } + { txtName TextBox + MoveScaled(15,8,57,3) + } + { lblParameters Label + MoveScaled(1,12,13,3) + Font = Font["+1"] + Text = ("Parameters:") + } + { txtParameters TextBox + MoveScaled(15,12,57,3) + ToolTip = ("Comma separated values: Hola1 INT,Hola2 CHAR(5)") + } + { lblReturns Label + MoveScaled(1,16,10,3) + Font = Font["+1"] + Text = ("Returns:") + } + { txtReturns TextBox + MoveScaled(15,16,57,3) + } + { txtData TextArea + MoveScaled(1,20,71,32) + } + { btnClue Button + MoveScaled(1,53,5,4) + ToolTip = ("Show a clue") + Picture = Picture["icon:/22/help"] + } + { tbnOK Button + MoveScaled(44,53,13,4) + Text = ("&Ok") + Picture = Picture["icon:/16/ok"] + Default = True + } + { btnCancel Button + MoveScaled(59,53,13,4) + Text = ("&Cancel") + Picture = Picture["icon:/16/cancel"] + Cancel = True + } +} + +# Gambas Action File 3.0 + +{ Actions + { Action Show a clue + Text = "Create Routine" + Picture = "icons/16/Routine.png" + } +} diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.class b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.class new file mode 100644 index 00000000..13070aeb --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.class @@ -0,0 +1,55 @@ +' Gambas class file + +Private $sTable As String +Private $bEdit As Boolean = False + +Public Sub _new(Optional Table As String) + + If Table Then + $sTable = Table + $bEdit = True + Endif + txtName.Text = $sTable + +End + +Public Sub Form_Open() + + cmbEngine.List = modMain.$Connection.MySQL.Engines + cmbCharset.List = modMain.$Connection.MySQL.Charsets + If $bEdit Then + Me.Text = ("Edit Table") + lblTitle.Text = Me.Text + txtName.Enabled = False + cmbEngine.Index = cmbEngine.Find(modMain.$Connection.MySQL.Table.Engine($sTable)) + cmbCharset.Index = cmbCharset.Find(modMain.$Connection.MySQL.Table.Charset($sTable)) + Else + cmbEngine.Index = cmbEngine.Find("InnoDB") + cmbCharset.Index = cmbCharset.Find("utf8") + Endif + Catch + modMain.Error() + +End + +Public Sub btnCancel_Click() + + Me.Close() + +End + +Public Sub tbnOK_Click() + + If $bEdit Then + modMain.$Connection.MySQL.Table.Modify($sTable,, cmbEngine.Text, cmbCharset.Text) + Else + modMain.$Connection.MySQL.Field.Add("Field01", modMain.$Connection.MySQL.DataTypes.Serial) + modMain.$Connection.MySQL.Table.Add(txtName.Text, cmbEngine.Text, cmbCharset.Text) + Endif + modMain.$hFBrowser.LoadDatabases() + modMain.$hFTables.SearchInfo() + Me.Close() + Catch + modMain.Error() + +End diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.form b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.form new file mode 100644 index 00000000..a28aa27e --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTable.form @@ -0,0 +1,54 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,36,28) + Text = ("Create Table") + Icon = Picture["icons/16/Table.png"] + Resizable = False + { lblTitle Label + MoveScaled(1,1,34,5) + Font = Font["+4"] + Background = Color.LightBackground + Text = ("Create Table") + Alignment = Align.Center + Border = Border.Sunken + } + { lblName Label + MoveScaled(1,8,11,3) + Font = Font["+1"] + Text = ("Name:") + } + { txtName TextBox + MoveScaled(13,8,22,3) + } + { lblEngine Label + MoveScaled(1,13,11,3) + Font = Font["+1"] + Text = ("Engine:") + } + { cmbEngine ComboBox + MoveScaled(13,13,22,3) + ReadOnly = True + } + { lblCharset Label + MoveScaled(1,18,11,3) + Font = Font["+1"] + Text = ("Charset:") + } + { cmbCharset ComboBox + MoveScaled(13,18,22,3) + ReadOnly = True + } + { btnCancel Button + MoveScaled(22,23,13,4) + Text = ("&Cancel") + Picture = Picture["icon:/16/cancel"] + Cancel = True + } + { tbnOK Button + MoveScaled(1,23,13,4) + Text = ("&Ok") + Picture = Picture["icon:/16/ok"] + Default = True + } +} diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.class b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.class new file mode 100644 index 00000000..dc57a785 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.class @@ -0,0 +1,62 @@ +' Gambas class file + +Private $sTable As String +Private $sTrigger As String +Private $bEdit As Boolean = False + +Public Sub _new(Table As String, Optional Trigger As String) + + $sTable = Table + $sTrigger = Trigger + cmbTime.Add("AFTER") + cmbTime.Add("BEFORE") + cmbEvent.Add("DELETE") + cmbEvent.Add("INSERT") + cmbEvent.Add("UPDATE") + If Trigger Then + txtName.Text = Trigger + cmbTime.Text = modMain.$Connection.MySQL.Trigger.Time($sTrigger) + cmbEvent.Text = modMain.$Connection.MySQL.Trigger.Event($sTrigger) + $bEdit = True + Endif + +End + +Public Sub Form_Open() + + If $bEdit Then + Me.Text = ("Edit Trigger") + lblTitle.Text = Me.Text + txtName.Enabled = False + Endif + If $sTrigger Then txtData.Text = modMain.$Connection.MySQL.Trigger.Info($sTrigger) + +End + +Public Sub btnCancel_Click() + + Me.Close() + +End + +Public Sub tbnOK_Click() + + If $bEdit Then + modMain.$Connection.MySQL.Trigger.Modify($sTrigger, $sTable, cmbTime.Text, cmbEvent.Text, txtData.Text) + Else + modMain.$Connection.MySQL.Trigger.Add(txtName.Text, $sTable, cmbTime.Text, cmbEvent.Text, txtData.Text) + Endif + Me.Close() + Catch + modMain.Error() + +End + +Public Sub btnClue_Click() + + txtName.Text = "FooTrigger" + cmbEvent.Index = 0 + cmbTime.Index = 0 + txtData.Text = "BEGIN\n INSERT INTO `MyTABLE` VALUES ('VALUES');\nEND)" + +End diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.form b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.form new file mode 100644 index 00000000..1b38930e --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewTrigger.form @@ -0,0 +1,64 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,73,55) + Text = ("Create Trigger") + Icon = Picture["icons/16/Trigger.png"] + Resizable = False + { lblTitle Label + MoveScaled(1,1,71,5) + Font = Font["+4"] + Background = Color.LightBackground + Text = ("Create Trigger") + Alignment = Align.Center + Border = Border.Sunken + } + { lblName Label + MoveScaled(1,8,11,3) + Font = Font["+1"] + Text = ("Name:") + } + { txtName TextBox + MoveScaled(13,8,59,3) + } + { lblTime Label + MoveScaled(1,13,11,3) + Font = Font["+1"] + Text = ("Time:") + } + { cmbTime ComboBox + MoveScaled(13,13,15,3) + ReadOnly = True + List = [] + } + { lblEvent Label + MoveScaled(30,13,11,3) + Font = Font["+1"] + Text = ("Event:") + } + { cmbEvent ComboBox + MoveScaled(42,13,15,3) + ReadOnly = True + List = [] + } + { btnClue Button + MoveScaled(1,50,5,4) + ToolTip = ("Show a clue") + Picture = Picture["icon:/22/help"] + } + { tbnOK Button + MoveScaled(44,50,13,4) + Text = ("&Ok") + Picture = Picture["icon:/16/ok"] + Default = True + } + { btnCancel Button + MoveScaled(59,50,13,4) + Text = ("&Cancel") + Picture = Picture["icon:/16/cancel"] + Cancel = True + } + { txtData TextArea + MoveScaled(1,17,71,32) + } +} diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.class b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.class new file mode 100644 index 00000000..c81ca558 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.class @@ -0,0 +1,45 @@ +' Gambas class file + +Private $bEdit As Boolean = False + +Public Sub _new(Optional View As String) + + If View Then + txtName.Text = View + $bEdit = True + txtData.Text = modMain.$Connection.MySQL.View.Definition(txtName.Text) & "\n" + Endif + +End + +Public Sub Form_Open() + + If $bEdit Then + Me.Text = ("Edit View") + lblTitle.Text = Me.Text + txtName.Enabled = False + Endif + +End + +Public Sub btnCancel_Click() + + Me.Close() + +End + +Public Sub tbnOK_Click() + + modMain.$Connection.MySQL.View.Add(txtName.Text, txtData.Text, $bEdit) + Me.Close() + Catch + modMain.Error() + +End + +Public Sub btnClue_Click() + + txtName.Text = "FooView" + txtData.Text = "SELECT * FROM `mysql`.`user`" + +End diff --git a/app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.form b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.form new file mode 100644 index 00000000..38e94b7d --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/CreateObjects/FNewView.form @@ -0,0 +1,44 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,73,50) + Text = ("Create View") + Icon = Picture["icons/16/View.png"] + Resizable = False + { lblTitle Label + MoveScaled(1,1,71,5) + Font = Font["+4"] + Background = Color.LightBackground + Text = ("Create View") + Alignment = Align.Center + Border = Border.Sunken + } + { lblName Label + MoveScaled(1,8,11,3) + Font = Font["+1"] + Text = ("Name:") + } + { txtName TextBox + MoveScaled(13,8,59,3) + } + { btnClue Button + MoveScaled(1,45,5,4) + ToolTip = ("Show a clue") + Picture = Picture["icon:/22/help"] + } + { tbnOK Button + MoveScaled(44,45,13,4) + Text = ("&Ok") + Picture = Picture["icon:/16/ok"] + Default = True + } + { btnCancel Button + MoveScaled(59,45,13,4) + Text = ("&Cancel") + Picture = Picture["icon:/16/cancel"] + Cancel = True + } + { txtData TextArea + MoveScaled(1,12,71,32) + } +} diff --git a/app/examples/Database/MySQLExample/.src/FConnect.class b/app/examples/Database/MySQLExample/.src/FConnect.class new file mode 100644 index 00000000..f4e5f7e9 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/FConnect.class @@ -0,0 +1,46 @@ +' Gambas class file + +Public Sub btnCancel_Click() + + Me.Close(True) + +End + +Public Sub btnConnect_Click() + + modMain.$Host = txtServer.Text + modMain.$Password = txtPassword.Text + modMain.$Port = txtPort.Text + modMain.$User = txtUser.Text + Me.Close(False) + Catch + Message.Error(Error.Text) + +End + +Public Sub btnClear_Click() + + txtPassword.Text = Null + txtPort.Text = Null + txtServer.Text = Null + txtUser.Text = Null + txtPassword.SetFocus() + +End + +Public Sub Form_Open() + + txtServer.Text = Settings["/Conecction/Host", "localhost"] + txtUser.Text = Settings["/Conecction/User", "root"] + txtPort.Text = Settings["/Conecction/Port", "3306"] + txtPassword.SetFocus() + +End + +Public Sub Form_Close() + + If txtServer.Text Then Settings["/Conecction/Host"] = txtServer.Text + If txtUser.Text Then Settings["/Conecction/User"] = txtUser.Text + If txtPort.Text Then Settings["/Conecction/Port"] = txtPort.Text + +End diff --git a/app/examples/Database/MySQLExample/.src/FConnect.form b/app/examples/Database/MySQLExample/.src/FConnect.form new file mode 100644 index 00000000..dea5fa31 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/FConnect.form @@ -0,0 +1,73 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,44,33) + Text = ("MySQL GUI") + Icon = Picture["icons/16/Admin.png"] + Resizable = False + { lblTitle Label + MoveScaled(1,1,42,5) + Font = Font["+4"] + Background = Color.LightBackground + Text = ("MySQL Example") + Alignment = Align.Center + Border = Border.Sunken + } + { frmMain Frame + MoveScaled(1,8,42,19) + Text = ("Connect to a MySQL Server") + { lblServer Label + MoveScaled(1,3,19,3) + Text = ("Server:") + Alignment = Align.Right + } + { txtServer TextBox Texts + Name = "txtServer" + MoveScaled(21,3,20,3) + } + { lblPort Label + MoveScaled(1,7,19,3) + Text = ("Port:") + Alignment = Align.Right + } + { txtPort TextBox Texts + Name = "txtPort" + MoveScaled(21,7,20,3) + } + { lblUser Label + MoveScaled(1,11,19,3) + Text = ("User:") + Alignment = Align.Right + } + { txtUser TextBox Texts + Name = "txtUser" + MoveScaled(21,11,20,3) + } + { lblPassword Label + MoveScaled(1,15,19,3) + Text = ("Password:") + Alignment = Align.Right + } + { txtPassword TextBox + MoveScaled(21,15,20,3) + Password = True + } + } + { btnConnect Button + MoveScaled(1,28,13,4) + Text = ("&Connect") + Picture = Picture["icon:/16/connect"] + Default = True + } + { btnClear Button + MoveScaled(16,28,12,4) + Text = ("&Clear") + Picture = Picture["icon:/16/clear"] + } + { btnCancel Button + MoveScaled(30,28,13,4) + Text = ("&Cancel") + Picture = Picture["icon:/16/cancel"] + Cancel = True + } +} diff --git a/app/examples/Database/MySQLExample/.src/FMessage.class b/app/examples/Database/MySQLExample/.src/FMessage.class new file mode 100644 index 00000000..8cdb33f7 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/FMessage.class @@ -0,0 +1,21 @@ +' Gambas class file + +Public Sub _new(Message As String) + + txaMesage.Text = Message + +End + +Public Sub Form_KeyPress() + + If Key.Code = Key.Esc Then Me.Close() + +End + +Public Sub txaMesage_KeyPress() + + If Key.Code = 81 Then '81 is q (in lowercase) + If Key.Alt Then txaMesage.Text &= "\n\n\n" & modMain.$Connection.MySQL.Query + Endif + +End diff --git a/app/examples/Database/MySQLExample/.src/FMessage.form b/app/examples/Database/MySQLExample/.src/FMessage.form new file mode 100644 index 00000000..e0a2267c --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/FMessage.form @@ -0,0 +1,12 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,67,39) + Text = ("Message") + Icon = Picture["icons/16/Warning.png"] + Resizable = False + { txaMesage TextArea + MoveScaled(1,1,65,37) + Wrap = True + } +} diff --git a/app/examples/Database/MySQLExample/.src/FTables.class b/app/examples/Database/MySQLExample/.src/FTables.class new file mode 100644 index 00000000..091f34bf --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/FTables.class @@ -0,0 +1,1186 @@ +' Gambas class file + +Private $sColumnSort As String + +Public Procedure _new() + + 'tvwTables definition + tvwTables.Columns.Count = 7 + tvwTables.Columns[0].Title = ("Name") + tvwTables.Columns[1].Title = ("Rows") + tvwTables.Columns[2].Title = ("Engine") + tvwTables.Columns[3].Title = ("Charset") + tvwTables.Columns[4].Title = ("Collation") + tvwTables.Columns[5].Title = ("Size") + tvwTables.Columns[6].Title = ("Update Time") + + 'tvwViews definition + tvwViews.Columns.Count = 7 + tvwViews.Columns[0].Title = ("Name") + tvwViews.Columns[1].Title = ("Updatable") + tvwViews.Columns[2].Title = ("Definer") + tvwViews.Columns[3].Title = ("Security") + tvwViews.Columns[4].Title = ("Check") + tvwViews.Columns[5].Title = ("Charset") + tvwViews.Columns[6].Title = ("Collation") + + 'tvwIndex definition + tvwIndexes.Columns.Count = 4 + tvwIndexes.Columns[0].Title = ("Name") + tvwIndexes.Columns[1].Title = ("Column") + tvwIndexes.Columns[2].Title = ("Collation") + tvwIndexes.Columns[3].Title = ("Type") + + 'tvwProcedures definition + tvwProcedures.Columns.Count = 8 + tvwProcedures.Columns[0].Width = 30 + tvwProcedures.Columns[1].Title = ("Name") + tvwProcedures.Columns[2].Title = ("Returns") + tvwProcedures.Columns[3].Title = ("Creation Time") + tvwProcedures.Columns[4].Title = ("Update Time") + tvwProcedures.Columns[5].Title = ("Type") + tvwProcedures.Columns[6].Title = ("Definer") + tvwProcedures.Columns[7].Title = ("Comment") + + 'tvwFields definition + tvwFields.Columns.Count = 10 + tvwFields.Columns[0].Width = 30 + tvwFields.Columns[1].Title = ("Name") + tvwFields.Columns[2].Title = ("Default") + tvwFields.Columns[3].Title = ("Nullable") + tvwFields.Columns[4].Title = ("DataType") + tvwFields.Columns[5].Title = ("Key") + tvwFields.Columns[6].Title = ("Extra") + tvwFields.Columns[7].Title = ("Charset") + tvwFields.Columns[8].Title = ("Collation") + tvwFields.Columns[9].Title = ("Comment") + + 'tvwTrigger definition + tvwTriggers.Columns.Count = 5 + tvwTriggers.Columns[0].Title = ("Name") + tvwTriggers.Columns[1].Title = ("Event") + tvwTriggers.Columns[2].Title = ("Table") + tvwTriggers.Columns[3].Title = ("Timing") + tvwTriggers.Columns[4].Title = ("Definer") + + 'tvwEvents definition + tvwEvents.Columns.Count = 19 + tvwEvents.Columns[0].Title = ("Name") + tvwEvents.Columns[1].Title = ("Definer") + tvwEvents.Columns[2].Title = ("Time Zone") + tvwEvents.Columns[3].Title = ("Type") + tvwEvents.Columns[4].Title = ("Execute At") + tvwEvents.Columns[5].Title = ("Interval Value") + tvwEvents.Columns[6].Title = ("Interval Field") + tvwEvents.Columns[7].Title = ("SQL Mode") + tvwEvents.Columns[8].Title = ("Starts") + tvwEvents.Columns[9].Title = ("Ends") + tvwEvents.Columns[10].Title = ("On Completion") + tvwEvents.Columns[11].Title = ("Created") + tvwEvents.Columns[12].Title = ("Last Altered") + tvwEvents.Columns[13].Title = ("Last Executed") + tvwEvents.Columns[14].Title = ("Originator") + tvwEvents.Columns[15].Title = ("Charset") + tvwEvents.Columns[16].Title = ("Collation") + tvwEvents.Columns[17].Title = ("Database Collation") + tvwEvents.Columns[18].Title = ("Comment") + + Catch + modMain.Error() + +End + +Public Procedure SearchInfo() 'Search for the info in the database + + Inc Application.Busy + tvwTables.Clear() + tvwTables.Rows.Count = 0 + tvwViews.Clear() + tvwViews.Rows.Count = 0 + tvwIndexes.Clear() + tvwIndexes.Rows.Count = 0 + tvwFields.Clear() + tvwFields.Rows.Count = 0 + tvwProcedures.Clear() + tvwProcedures.Rows.Count = 0 + tvwTriggers.Clear() + tvwTriggers.Rows.Count = 0 + tvwEvents.Clear() + tvwEvents.Rows.Count = 0 + + tabData[2].Text = ("Indexes") + tabData[3].Text = ("Fields") + 'Database's info + textResult.Text = modMain.$Connection.MySQL.DataBase.Info(modMain.$Connection.Name) + + TableInfo() + + 'Returns if the database is information_schema + If modMain.$Connection.Name = "information_schema" Then + Dec Application.Busy + Return + Endif + + ViewInfo() + + IndexInfo() + + RoutinesInfo() + + TriggersInfo() + + If modMain.$bSchemaOk Then EventInfo() + + Dec Application.Busy + Catch + modMain.Error() + +End + +Public Procedure FieldInfo(Optional Column As Integer) 'Field's Info + + Dim iCounter As Integer + + If tvwTables.Rows.Count = 0 Or tvwTables.Row = -1 Then Return 'Empty set or not table selected + tabData[3].Text = ("Fields on:") & " " & tvwTables[tvwTables.Row, 0].Text + Select (Column) + Case 1 + $sColumnSort = "COLUMN_NAME" + Case 2 + $sColumnSort = "COLUMN_DEFAULT" + Case 3 + $sColumnSort = "IS_NULLABLE" + Case 4 + $sColumnSort = "COLUMN_TYPE" + Case 5 + $sColumnSort = "COLUMN_KEY" + Case 6 + $sColumnSort = "EXTRA" + Case 7 + $sColumnSort = "CHARACTER_SET_NAME" + Case 8 + $sColumnSort = "COLLATION_NAME" + Case 9 + $sColumnSort = "COLUMN_COMMENT" + Default + $sColumnSort = "COLUMN_NAME" + End Select + modMain.$hResult = modMain.$Connection.Exec("SELECT `COLUMN_KEY`, `DATA_TYPE`, `EXTRA`, `COLUMN_NAME`, `COLUMN_DEFAULT`, `IS_NULLABLE`, `COLUMN_TYPE`, `CHARACTER_SET_NAME`, `COLLATION_NAME`, `COLUMN_COMMENT` FROM `information_schema`.`COLUMNS` WHERE `TABLE_NAME` = '" & tvwTables[tvwTables.Row, 0].Text & "' AND `TABLE_SCHEMA` = '" & modMain.$Connection.Name & "' ORDER BY `" & $sColumnSort & "`") + tvwFields.Rows.Count = modMain.$hResult.Count + For iCounter = 0 To modMain.$hResult.Max + Select (Upper(modMain.$hResult!DATA_TYPE)) + Case "INT", "MEDIUMINT", "TINYINT", "BOOL", "BOOLEAN", "SMALLINT", "INTEGER", "FLOAT", "BIGINT", "SERIAL", "DOUBLE", "DOUBLEPRECISION", "DECIMAL", "DEC" + tvwFields[iCounter, 0].Picture = Picture["icons/16/Numeric.png"] + Case "VARCHAR", "CHAR", "BIT", "BINARY", "VARBINARY", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "SET", "ENUM" + tvwFields[iCounter, 0].Picture = Picture["icons/16/String.png"] + Case "DATE", "DATETIME", "TIME", "TIMESTAMP", "YEAR" + tvwFields[iCounter, 0].Picture = Picture["icons/16/Datetime.png"] + Case "TINYBLOB", "MEDIUMBLOB", "LONGBLOB", "BLOB" + tvwFields[iCounter, 0].Picture = Picture["icons/16/Blob.png"] + Default + tvwFields[iCounter, 0].Picture = Picture["icons/16/Field.png"] + End Select + Select (Upper(modMain.$hResult!COLUMN_KEY)) + Case "PRI" + tvwFields[iCounter, 0].Picture = Picture["icons/16/Primarykey.png"] + Case "MUL" + tvwFields[iCounter, 0].Picture = Picture["icons/16/Column_FK.png"] + Default + End Select + tvwFields[iCounter, 0].Alignment = Align.Center + tvwFields[iCounter, 1].Text = modMain.$hResult!COLUMN_NAME + tvwFields[iCounter, 2].Text = modMain.$hResult!COLUMN_DEFAULT + tvwFields[iCounter, 3].Text = modMain.$hResult!IS_NULLABLE + tvwFields[iCounter, 4].Text = modMain.$hResult!COLUMN_TYPE + tvwFields[iCounter, 5].Text = modMain.$hResult!COLUMN_KEY + tvwFields[iCounter, 6].Text = modMain.$hResult!EXTRA + tvwFields[iCounter, 7].Text = modMain.$hResult!CHARACTER_SET_NAME + tvwFields[iCounter, 8].Text = modMain.$hResult!COLLATION_NAME + tvwFields[iCounter, 9].Text = modMain.$hResult!COLUMN_COMMENT + If (iCounter Mod 2) = 0 Then + tvwFields[iCounter, 0].Background = Color.RGB(239, 243, 247) + tvwFields[iCounter, 1].Background = Color.RGB(239, 243, 247) + tvwFields[iCounter, 2].Background = Color.RGB(239, 243, 247) + tvwFields[iCounter, 3].Background = Color.RGB(239, 243, 247) + tvwFields[iCounter, 4].Background = Color.RGB(239, 243, 247) + tvwFields[iCounter, 5].Background = Color.RGB(239, 243, 247) + tvwFields[iCounter, 6].Background = Color.RGB(239, 243, 247) + tvwFields[iCounter, 7].Background = Color.RGB(239, 243, 247) + tvwFields[iCounter, 8].Background = Color.RGB(239, 243, 247) + tvwFields[iCounter, 9].Background = Color.RGB(239, 243, 247) + Endif + modMain.$hResult.MoveNext() + Next + tvwFields.Columns[0].Width = -1 + tvwFields.Columns[1].Width = -1 + tvwFields.Columns[2].Width = -1 + tvwFields.Columns[3].Width = -1 + tvwFields.Columns[4].Width = -1 + tvwFields.Columns[5].Width = -1 + tvwFields.Columns[6].Width = -1 + tvwFields.Columns[7].Width = -1 + tvwFields.Columns[8].Width = -1 + tvwFields.Columns[9].Width = -1 + + Catch + modMain.Error() + +End + +Public Procedure TableInfo(Optional Column As Integer) 'Table's Info + + Dim iCounter As Integer + + tvwTables.Clear() + Select (Column) + Case 0 + $sColumnSort = "TABLE_NAME" + Case 1 + $sColumnSort = "TABLE_ROWS" + Case 2 + $sColumnSort = "ENGINE" + Case 3 + $sColumnSort = "CHARSET" + Case 4 + $sColumnSort = "TABLE_COLLATION" + Case 5 + $sColumnSort = "DATA_LENGTH" + Case 6 + $sColumnSort = "UPDATE_TIME" + Default + $sColumnSort = "TABLE_NAME" + End Select + + modMain.$hResult = modMain.$Connection.Exec("SELECT `a`.`TABLE_NAME` AS `TABLE_NAME`, `a`.`ENGINE` AS ENGINE, `a`.`TABLE_ROWS` AS TABLE_ROWS, `a`.`UPDATE_TIME` AS UPDATE_TIME, `b`.`CHARACTER_SET_NAME` AS CHARSET, `a`.`TABLE_COLLATION` AS TABLE_COLLATION FROM `information_schema`.`TABLES` `a`, `information_schema`.`COLLATIONS` `b` WHERE `a`.`TABLE_SCHEMA` = '" & modMain.$Connection.Name & "' AND `a`.`TABLE_COLLATION` = `b`.`COLLATION_NAME` ORDER BY `" & $sColumnSort & "`") + tvwTables.Rows.Count = modMain.$hResult.Count + For iCounter = 0 To modMain.$hResult.Max + tvwTables[iCounter, 0].Text = modMain.$hResult!TABLE_NAME + If modMain.$hResult!TABLE_ROWS <> Null Then tvwTables[iCounter, 1].Text = Format(modMain.$hResult!TABLE_ROWS, "#,#") + tvwTables[iCounter, 1].Alignment = Align.Right + tvwTables[iCounter, 2].Text = modMain.$hResult!ENGINE + tvwTables[iCounter, 3].Text = modMain.$hResult!CHARSET + tvwTables[iCounter, 4].Text = modMain.$hResult!TABLE_COLLATION + If modMain.$hResult!UPDATE_TIME = Null + tvwTables[iCounter, 6].Picture = Picture["icons/24/Null.png"] + Else + tvwTables[iCounter, 6].Text = modMain.$hResult!UPDATE_TIME + Endif + tvwTables[iCounter, 5].Text = Format((modMain.$Connection.Exec("SELECT `DATA_LENGTH` FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA` = '" & modMain.$Connection.Name & "' AND `TABLE_NAME` = '" & modMain.$hResult!TABLE_NAME & "'")!DATA_LENGTH / 1024), "#,#.00 KB") + tvwTables[iCounter, 5].Alignment = Align.Right + If (iCounter Mod 2) = 0 Then + tvwTables[iCounter, 0].Background = Color.RGB(239, 243, 247) + tvwTables[iCounter, 1].Background = Color.RGB(239, 243, 247) + tvwTables[iCounter, 2].Background = Color.RGB(239, 243, 247) + tvwTables[iCounter, 3].Background = Color.RGB(239, 243, 247) + tvwTables[iCounter, 4].Background = Color.RGB(239, 243, 247) + tvwTables[iCounter, 5].Background = Color.RGB(239, 243, 247) + tvwTables[iCounter, 6].Background = Color.RGB(239, 243, 247) + Endif + modMain.$hResult.MoveNext() + Next + tvwTables.Columns[0].Width = -1 + tvwTables.Columns[1].Width = -1 + tvwTables.Columns[2].Width = -1 + tvwTables.Columns[3].Width = -1 + tvwTables.Columns[4].Width = -1 + tvwTables.Columns[5].Width = -1 + tvwTables.Columns[6].Width = -1 + + Catch + modMain.Error() + +End + +Public Procedure ViewInfo(Optional Column As Integer) 'View's Info + + Dim iCounter As Integer + + Select (Column) + Case 0 + $sColumnSort = "TABLE_NAME" + Case 1 + $sColumnSort = "IS_UPDATABLE" + Case 2 + $sColumnSort = "DEFINER" + Case 3 + $sColumnSort = "SECURITY_TYPE" + Case 4 + $sColumnSort = "CHECK_OPTION" + Case 5 + $sColumnSort = "CHARACTER_SET_CLIENT" + Case 6 + $sColumnSort = "COLLATION_CONNECTION" + Default + $sColumnSort = "TABLE_NAME" + End Select + modMain.$hResult = modMain.$Connection.Exec("SELECT * FROM `information_schema`.`VIEWS` WHERE `TABLE_SCHEMA` = '" & modMain.$Connection.Name & "' ORDER BY `" & $sColumnSort & "`") + tvwViews.Rows.Count = modMain.$hResult.Count + If modMain.$hResult.Available Then + tvwViews.Rows.Count = modMain.$hResult.Count + For iCounter = 0 To modMain.$hResult.Max + tvwViews[iCounter, 0].Text = modMain.$hResult!TABLE_NAME + tvwViews[iCounter, 1].Text = modMain.$hResult!IS_UPDATABLE + tvwViews[iCounter, 2].Text = modMain.$hResult!DEFINER + tvwViews[iCounter, 3].Text = modMain.$hResult!SECURITY_TYPE + tvwViews[iCounter, 4].Text = modMain.$hResult!CHECK_OPTION + If modMain.$bSchemaOk Then + tvwViews[iCounter, 5].Text = modMain.$hResult!CHARACTER_SET_CLIENT + tvwViews[iCounter, 6].Text = modMain.$hResult!COLLATION_CONNECTION + Else + tvwViews[iCounter, 5].Text = ("Not aviable") + tvwViews[iCounter, 6].Text = ("Not aviable") + Endif + If (iCounter Mod 2) = 0 Then + tvwViews[iCounter, 0].Background = Color.RGB(239, 243, 247) + tvwViews[iCounter, 1].Background = Color.RGB(239, 243, 247) + tvwViews[iCounter, 2].Background = Color.RGB(239, 243, 247) + tvwViews[iCounter, 3].Background = Color.RGB(239, 243, 247) + tvwViews[iCounter, 4].Background = Color.RGB(239, 243, 247) + tvwViews[iCounter, 5].Background = Color.RGB(239, 243, 247) + tvwViews[iCounter, 6].Background = Color.RGB(239, 243, 247) + Endif + modMain.$hResult.MoveNext() + Next + Endif + tvwViews.Columns[0].Width = -1 + tvwViews.Columns[1].Width = -1 + tvwViews.Columns[2].Width = -1 + tvwViews.Columns[3].Width = -1 + tvwViews.Columns[4].Width = -1 + tvwViews.Columns[5].Width = -1 + tvwViews.Columns[6].Width = -1 + + Catch + modMain.Error() + +End + +Public Procedure IndexInfo(Optional Column As Integer) 'Index's Info + + Dim iCounter As Integer + + If tvwTables.Rows.Count = 0 Or tvwTables.Row = -1 Then Return 'Empty set or not table selected + tabData[2].Text = ("Indexes on:") & " " & tvwTables[tvwTables.Row, 0].Text + tvwIndexes.Clear() + ' Select (Column) + ' Case 0 + ' $sColumnSort = "Key_name" + ' Case 1 + ' $sColumnSort = "Column_name" + ' Case 2 + ' $sColumnSort = "Collation" + ' Case 3 + ' $sColumnSort = "Index_type" + ' Default + ' $sColumnSort = "Key_name" + ' End Select + + modMain.$hResult = modMain.$Connection.Exec("SHOW INDEX FROM `" & modMain.$Connection.Name & "`.`" & tvwTables[tvwTables.Row, 0].Text & "`") + If modMain.$hResult.Available Then + tvwIndexes.Rows.Count = modMain.$hResult.Count + For iCounter = 0 To modMain.$hResult.Max + tvwIndexes[iCounter, 0].Text = modMain.$hResult!Key_name + tvwIndexes[iCounter, 1].Text = modMain.$hResult!Column_name + tvwIndexes[iCounter, 2].Text = modMain.$hResult!Collation + tvwIndexes[iCounter, 3].Text = modMain.$hResult!Index_type + If (iCounter Mod 2) = 0 Then + tvwIndexes[iCounter, 0].Background = Color.RGB(239, 243, 247) + tvwIndexes[iCounter, 1].Background = Color.RGB(239, 243, 247) + tvwIndexes[iCounter, 2].Background = Color.RGB(239, 243, 247) + tvwIndexes[iCounter, 3].Background = Color.RGB(239, 243, 247) + Endif + modMain.$hResult.MoveNext() + Next + Endif + tvwIndexes.Columns[0].Width = -1 + tvwIndexes.Columns[1].Width = -1 + tvwIndexes.Columns[2].Width = -1 + tvwIndexes.Columns[3].Width = -1 + + Catch + modMain.Error() + +End + +Public Procedure RoutinesInfo(Optional Column As Integer) 'Rotines's Info + + Dim iCounter As Integer + + tvwProcedures.Clear() + Select (Column) + Case 0 + $sColumnSort = "ROUTINE_TYPE" + Case 1 + $sColumnSort = "ROUTINE_NAME" + Case 2 + $sColumnSort = "DTD_IDENTIFIER" + Case 3 + $sColumnSort = "CREATED" + Case 4 + $sColumnSort = "LAST_ALTERED" + Case 5 + $sColumnSort = "ROUTINE_TYPE" + Case 6 + $sColumnSort = "DEFINER" + Case 7 + $sColumnSort = "ROUTINE_COMMENT" + Default + $sColumnSort = "ROUTINE_NAME" + End Select + + modMain.$hResult = modMain.$Connection.Exec("SELECT * FROM `information_schema`.`ROUTINES` WHERE `ROUTINE_SCHEMA` = '" & modMain.$Connection.Name & "' ORDER BY `" & $sColumnSort & "`") + If modMain.$hResult.Available Then + tvwProcedures.Rows.Count = modMain.$hResult.Count + For iCounter = 0 To modMain.$hResult.Max + If modMain.$hResult!ROUTINE_TYPE = "FUNCTION" Then + tvwProcedures[iCounter, 0].Picture = Picture["icons/16/Function.png"] + Else + tvwProcedures[iCounter, 0].Picture = Picture["icons/16/Routine.png"] + Endif + tvwProcedures[iCounter, 0].Alignment = Align.Center + tvwProcedures[iCounter, 1].Text = modMain.$hResult!ROUTINE_NAME + tvwProcedures[iCounter, 2].Text = modMain.$hResult!DTD_IDENTIFIER + tvwProcedures[iCounter, 3].Text = modMain.$hResult!CREATED + tvwProcedures[iCounter, 4].Text = modMain.$hResult!LAST_ALTERED + tvwProcedures[iCounter, 5].Text = modMain.$hResult!ROUTINE_TYPE + tvwProcedures[iCounter, 6].Text = modMain.$hResult!DEFINER + tvwProcedures[iCounter, 7].Text = modMain.$hResult!ROUTINE_COMMENT + If (iCounter Mod 2) = 0 Then + tvwProcedures[iCounter, 0].Background = Color.RGB(239, 243, 247) + tvwProcedures[iCounter, 1].Background = Color.RGB(239, 243, 247) + tvwProcedures[iCounter, 2].Background = Color.RGB(239, 243, 247) + tvwProcedures[iCounter, 3].Background = Color.RGB(239, 243, 247) + tvwProcedures[iCounter, 4].Background = Color.RGB(239, 243, 247) + tvwProcedures[iCounter, 5].Background = Color.RGB(239, 243, 247) + tvwProcedures[iCounter, 6].Background = Color.RGB(239, 243, 247) + tvwProcedures[iCounter, 7].Background = Color.RGB(239, 243, 247) + Endif + modMain.$hResult.MoveNext() + Next + Endif + tvwProcedures.Columns[0].Width = -1 + tvwProcedures.Columns[1].Width = -1 + tvwProcedures.Columns[2].Width = -1 + tvwProcedures.Columns[3].Width = -1 + tvwProcedures.Columns[4].Width = -1 + tvwProcedures.Columns[5].Width = -1 + tvwProcedures.Columns[6].Width = -1 + tvwProcedures.Columns[7].Width = -1 + + Catch + modMain.Error() + +End + +Public Procedure EventInfo(Optional Column As Integer) 'Event's Info + + Dim iCounter As Integer + + tvwEvents.Clear() + Select (Column) + Case 0 + $sColumnSort = "EVENT_NAME" + Case 1 + $sColumnSort = "DEFINER" + Case 2 + $sColumnSort = "TIME_ZONE" + Case 3 + $sColumnSort = "EVENT_TYPE" + Case 4 + $sColumnSort = "EXECUTE_AT" + Case 5 + $sColumnSort = "INTERVAL_VALUE" + Case 6 + $sColumnSort = "INTERVAL_FIELD" + Case 7 + $sColumnSort = "SQL_MODE" + Case 8 + $sColumnSort = "STARTS" + Case 9 + $sColumnSort = "ENDS" + Case 10 + $sColumnSort = "ON_COMPLETION" + Case 11 + $sColumnSort = "CREATED" + Case 12 + $sColumnSort = "LAST_ALTERED" + Case 13 + $sColumnSort = "LAST_EXECUTED" + Case 14 + $sColumnSort = "ORIGINATOR" + Case 15 + $sColumnSort = "CHARACTER_SET_CLIENT" + Case 16 + $sColumnSort = "COLLATION_CONNECTION" + Case 17 + $sColumnSort = "DATABASE_COLLATION" + Case 18 + $sColumnSort = "EVENT_COMMENT" + Default + $sColumnSort = "EVENT_NAME" + End Select + + modMain.$hResult = modMain.$Connection.Exec("SELECT * FROM `information_schema`.`EVENTS` WHERE `EVENT_SCHEMA` = '" & modMain.$Connection.Name & "' ORDER BY `" & $sColumnSort & "`") + If modMain.$hResult.Available Then + tvwEvents.Rows.Count = modMain.$hResult.Count + For iCounter = 0 To modMain.$hResult.Max + tvwEvents[iCounter, 0].Text = modMain.$hResult!EVENT_NAME + tvwEvents[iCounter, 1].Text = modMain.$hResult!DEFINER + tvwEvents[iCounter, 2].Text = modMain.$hResult!TIME_ZONE + tvwEvents[iCounter, 3].Text = modMain.$hResult!EVENT_TYPE + tvwEvents[iCounter, 4].Text = modMain.$hResult!EXECUTE_AT + tvwEvents[iCounter, 5].Text = modMain.$hResult!INTERVAL_VALUE + tvwEvents[iCounter, 6].Text = modMain.$hResult!INTERVAL_FIELD + tvwEvents[iCounter, 7].Text = modMain.$hResult!SQL_MODE + tvwEvents[iCounter, 8].Text = modMain.$hResult!STARTS + tvwEvents[iCounter, 9].Text = modMain.$hResult!ENDS + tvwEvents[iCounter, 10].Text = modMain.$hResult!ON_COMPLETION + tvwEvents[iCounter, 11].Text = modMain.$hResult!CREATED + tvwEvents[iCounter, 12].Text = modMain.$hResult!LAST_ALTERED + tvwEvents[iCounter, 13].Text = modMain.$hResult!LAST_EXECUTED + tvwEvents[iCounter, 14].Text = modMain.$hResult!ORIGINATOR + tvwEvents[iCounter, 15].Text = modMain.$hResult!CHARACTER_SET_CLIENT + tvwEvents[iCounter, 16].Text = modMain.$hResult!COLLATION_CONNECTION + tvwEvents[iCounter, 17].Text = modMain.$hResult!DATABASE_COLLATION + tvwEvents[iCounter, 18].Text = modMain.$hResult!EVENT_COMMENT + If (iCounter Mod 2) = 0 Then + tvwEvents[iCounter, 0].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 1].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 2].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 3].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 4].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 5].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 6].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 7].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 8].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 9].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 10].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 11].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 12].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 13].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 14].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 15].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 16].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 17].Background = Color.RGB(239, 243, 247) + tvwEvents[iCounter, 18].Background = Color.RGB(239, 243, 247) + Endif + modMain.$hResult.MoveNext() + Next + Endif + tvwEvents.Columns[0].Width = -1 + tvwEvents.Columns[1].Width = -1 + tvwEvents.Columns[2].Width = -1 + tvwEvents.Columns[3].Width = -1 + tvwEvents.Columns[4].Width = -1 + tvwEvents.Columns[5].Width = -1 + tvwEvents.Columns[6].Width = -1 + tvwEvents.Columns[7].Width = -1 + tvwEvents.Columns[8].Width = -1 + tvwEvents.Columns[9].Width = -1 + tvwEvents.Columns[10].Width = -1 + tvwEvents.Columns[11].Width = -1 + tvwEvents.Columns[12].Width = -1 + tvwEvents.Columns[13].Width = -1 + tvwEvents.Columns[14].Width = -1 + tvwEvents.Columns[15].Width = -1 + tvwEvents.Columns[16].Width = -1 + tvwEvents.Columns[17].Width = -1 + tvwEvents.Columns[18].Width = -1 + + Catch + modMain.Error() + +End + +Public Procedure TriggersInfo(Optional Column As Integer) 'Table's Info + + Dim iCounter As Integer + + tvwTriggers.Clear() + Select (Column) + Case 0 + $sColumnSort = "TRIGGER_NAME" + Case 1 + $sColumnSort = "EVENT_MANIPULATION" + Case 2 + $sColumnSort = "EVENT_OBJECT_TABLE" + Case 3 + $sColumnSort = "ACTION_TIMING" + Case 4 + $sColumnSort = "DEFINER" + Default + $sColumnSort = "TRIGGER_NAME" + End Select + + 'Triggers Info + modMain.$hResult = modMain.$Connection.Exec("SELECT `TRIGGER_NAME`, `EVENT_MANIPULATION`, `EVENT_OBJECT_TABLE`, `ACTION_TIMING`, `DEFINER` FROM `information_schema`.`TRIGGERS` WHERE `TRIGGER_SCHEMA` = '" & modMain.$Connection.Name & "' ORDER BY `" & $sColumnSort & "`") + If modMain.$hResult.Available Then + tvwTriggers.Rows.Count = modMain.$hResult.Count + For iCounter = 0 To modMain.$hResult.Max + tvwTriggers[iCounter, 0].Text = modMain.$hResult!TRIGGER_NAME + tvwTriggers[iCounter, 1].Text = modMain.$hResult!EVENT_MANIPULATION + tvwTriggers[iCounter, 2].Text = modMain.$hResult!EVENT_OBJECT_TABLE + tvwTriggers[iCounter, 3].Text = modMain.$hResult!ACTION_TIMING + tvwTriggers[iCounter, 4].Text = modMain.$hResult!DEFINER + If (iCounter Mod 2) = 0 Then + tvwTriggers[iCounter, 0].Background = Color.RGB(239, 243, 247) + tvwTriggers[iCounter, 1].Background = Color.RGB(239, 243, 247) + tvwTriggers[iCounter, 2].Background = Color.RGB(239, 243, 247) + tvwTriggers[iCounter, 3].Background = Color.RGB(239, 243, 247) + tvwTriggers[iCounter, 4].Background = Color.RGB(239, 243, 247) + Endif + modMain.$hResult.MoveNext() + Next + Endif + tvwTriggers.Columns[0].Width = -1 + tvwTriggers.Columns[1].Width = -1 + tvwTriggers.Columns[2].Width = -1 + tvwTriggers.Columns[3].Width = -1 + tvwTriggers.Columns[4].Width = -1 + + Catch + modMain.Error() + +End + +Public Sub Form_Open() + + Me.Text = "mysql://" & modMain.$Connection.User & "@" & modMain.$Connection.Host & "/" & modMain.$Connection.Name + cmbDatabases.List = modMain.$Connection.MySQL.Databases + cmbDatabases.Index = cmbDatabases.Find(modMain.$Connection.Name) + SearchInfo() + +End + +'***************************** Procedures for tables ************************** +Public Sub tvwTables_Select() + + If tvwTables.Row = -1 Then Return + textResult.Clear() + textResult.Text = modMain.$Connection.MySQL.Table.Info(tvwTables[tvwTables.Row, 0].Text) + FieldInfo() + IndexInfo() + Select (tvwTables.Column) + Case 0 + tvwTables.Edit() + Case 1, 5 + Return + Case 2 + tvwTables.Edit(modMain.$Connection.MySQL.Engines, True) + Case 3 + tvwTables.Edit(modMain.$Connection.MySQL.Charsets, True) + Case 4 + tvwTables.Edit(modMain.$Connection.MySQL.Collations, True) + Default + tvwTables.Edit() + End Select + Catch + modMain.Error() + +End + +Public Sub tvwTables_Save(Row As Integer, Column As Integer, Value As String) + + 'Alters the table definition, the value is never saved into the TableView, it is used to make the update + If tbtLock.Value Then Return + Select (Column) + Case 0 + modMain.$Connection.MySQL.Table.Rename(tvwTables[Row, 0].Text, Value) + Case 1, 5 + Return + Case 2 + modMain.$Connection.MySQL.Table.ModifyColumn(tvwTables[Row, 0].Text, "ENGINE",, Value) + Case 3 + modMain.$Connection.MySQL.Table.ModifyColumn(tvwTables[Row, 0].Text, "CHARACTER SET",, Value & " COLLATE " & Value & "_bin") + Case 4 + modMain.$Connection.MySQL.Table.ModifyColumn(tvwTables[Row, 0].Text, "CHARACTER SET",, tvwTables[Row, 3].Text & " COLLATE " & Value) + Default + End Select + RefreshData(tvwTables.Row, tabData.Index) + Catch + modMain.Error() + +End + +Public Sub tvwTables_DblClick() + + tabData.Index = 3 'Fields + +End +'***************************** End of Procedures for tables ******************* + +'***************************** Procedures for views *************************** +Public Sub tvwViews_Click() + + If tvwViews.Row = -1 Then Return + textResult.Text = modMain.$Connection.MySQL.View.Info(tvwViews[tvwViews.Row, 0].Text) + ViewInfo() + Catch + modMain.Error() + +End + +Public Sub tvwViews_DblClick() + + modMain.$hFNewView = New FNewView(Null, tvwViews[tvwViews.Row, 0].Text) + modMain.$hFNewView.ShowModal() + Catch + modMain.Error() + +End + +Public Sub tvwViews_Select() + + tvwViews_Click() + +End +'***************************** End of Procedures for views ******************** + +'***************************** Procedures for fields ********************* +Public Sub tvwFields_Click() + + 'If the tbtLock is true then the change is not made + If tbtLock.Value Then Return + Select (tvwFields.Column) + Case 0, 5, 6 + Return + Case 1, 2, 9 + tvwFields.Edit() + Case 3 + tvwFields.Edit(["YES", "NO"], True) + Case 7 + tvwFields.Edit(modMain.$Connection.MySQL.Charsets, True) + Case 8 + tvwFields.Edit(modMain.$Connection.MySQL.Collations, True) + Default + tvwFields.Edit() + End Select + Catch + modMain.Error() + +End + +Public Sub tvwFields_DblClick() + + 'If the tbtLock is true then the change is not made + If tbtLock.Value Then Return + If ValidateTableSelected() Then + modMain.$hFNewField = New FNewField(tvwTables[tvwTables.Row, 0].Text, Null, tvwFields[tvwFields.Row, 1].Text) + modMain.$hFNewField.ShowModal() + Endif + FieldInfo() + Catch + modMain.Error() + +End + +Public Sub tvwFields_Save(Row As Integer, Column As Integer, Value As String) + + Dim sDefinition As String + + Select (Column) + Case 0, 5, 6, 7, 8 + Return + Case 1 'Field name + modMain.$Connection.MySQL.Field.Rename(tvwTables[tvwTables.Row, 0].Text, tvwFields[Row, Column].Text, Value) + Case 2 'Default + If Value = "" Then + modMain.$Connection.MySQL.Table.ModifyColumn(tvwTables[tvwTables.Row, 0].Text, "ALTER", tvwFields[Row, 1].Text, "DROP DEFAULT") + Else + modMain.$Connection.MySQL.Table.ModifyColumn(tvwTables[tvwTables.Row, 0].Text, "ALTER", tvwFields[Row, 1].Text, "SET DEFAULT '" & Value & "'") + Endif + Case 3 'Nullable + sDefinition = tvwFields[Row, 4].Text + If Value = "NO" Then sDefinition &= " NOT NULL" + If tvwFields[Row, 2].Text <> "" Then sDefinition &= " DEFAULT '" & tvwFields[Row, 2].Text & "'" + If tvwFields[Row, 9].Text <> "" Then sDefinition &= " COMMENT '" & tvwFields[Row, 2].Text & "'" + modMain.$Connection.MySQL.Table.ModifyColumn(tvwTables[tvwTables.Row, 0].Text, "MODIFY", tvwFields[Row, 1].Text, sDefinition) + Case 4 'Datatype + sDefinition = Value + If tvwFields[Row, 3].Text = "NO" Then sDefinition &= " NOT NULL" + If tvwFields[Row, 2].Text <> "" Then sDefinition &= " DEFAULT '" & tvwFields[Row, 2].Text & "'" + If tvwFields[Row, 9].Text <> "" Then sDefinition &= " COMMENT '" & tvwFields[Row, 2].Text & "'" + modMain.$Connection.MySQL.Table.ModifyColumn(tvwTables[tvwTables.Row, 0].Text, "MODIFY", tvwFields[Row, 1].Text, sDefinition) + Case 9 'Comment + sDefinition = tvwFields[Row, 4].Text + If tvwFields[Row, 3].Text = "NO" Then sDefinition &= " NOT NULL" + If tvwFields[Row, 2].Text <> "" Then sDefinition &= " DEFAULT '" & tvwFields[Row, 2].Text & "'" + If Value <> "" Then sDefinition &= " COMMENT '" & Value & "'" + modMain.$Connection.MySQL.Table.ModifyColumn(tvwTables[tvwTables.Row, 0].Text, "MODIFY", tvwFields[Row, 1].Text, sDefinition) + Default + End Select + RefreshData(tvwTables.Row, tabData.Index) + Catch + modMain.Error() + +End +'***************************** End of Procedures for fields ******************** + +'***************************** Procedures for procedures *********************** +Public Sub tvwProcedures_Click() + + If tvwProcedures.Row = -1 Then Return + textResult.Text = modMain.$Connection.MySQL.Routines.Info(tvwProcedures[tvwProcedures.Row, 1].Text, modMain.$Connection.Name) + Catch + modMain.Error() + +End + +Public Sub tvwProcedures_DblClick() + + modMain.$hFNewRoutine = New FNewRoutine(Null, tvwProcedures[tvwProcedures.Row, 1].Text) + modMain.$hFNewRoutine.ShowModal() + RoutinesInfo() + Catch + modMain.Error() + +End + +Public Sub tvwProcedures_Select() + + tvwProcedures_Click() + +End +'***************************** End of Procedures for procedures **************** + +'***************************** Procedures for triggers ************************* +Public Sub tvwTriggers_Click() + + If tvwTriggers.Row = -1 Then Return + textResult.Text = modMain.$Connection.MySQL.Trigger.Info(tvwTriggers[tvwTriggers.Row, 0].Text, modMain.$Connection.Name) + Catch + modMain.Error() + +End + +Public Sub tvwTriggers_DblClick() + + modMain.$hFNewTrigger = New FNewTrigger(tvwTables[tvwTables.Row, 0].Text, Null, tvwTriggers[tvwTriggers.Row, 0].Text) + modMain.$hFNewTrigger.ShowModal() + TriggersInfo() + Catch + modMain.Error() + +End + +Public Sub tvwTriggers_Select() + + tvwTriggers_Click() + +End +'***************************** End of Procedures for triggers ****************** + +Public Sub tabData_Click() + + Select (tabData.Index) + Case 0 'Tables + tbtNew.ToolTip = ("New Table") + tbtDelete.ToolTip = ("Delete Table") + Case 1 'Views + tbtNew.ToolTip = ("New View") + tbtDelete.ToolTip = ("Delete View") + Case 2 'Indexes + tbtNew.ToolTip = ("New Index") + tbtDelete.ToolTip = ("Delete Index") + Case 3 'Fileds + tbtNew.ToolTip = ("New Field") + tbtDelete.ToolTip = ("Delete Field") + Case 4 'Routines + tbtNew.ToolTip = ("New Routine") + tbtDelete.ToolTip = ("Delete Routine") + Case 5 'Triggers + tbtNew.ToolTip = ("New Trigger") + tbtDelete.ToolTip = ("Delete Trigger") + Case 6 'Events + tbtNew.ToolTip = ("New Event") + tbtDelete.ToolTip = ("Delete Event") + Default + End Select + Catch + modMain.Error() + +End + +Public Sub Action_Activate(Name As String) As Boolean + + Select (Name) + Case "NewItem" 'tbtNew, F2 + If tbtLock.Value Then Return + NewItem() + RefreshData(tvwTables.Row, tabData.Index) + Case "EditItem" 'tbtNew, Ctrl + E + If tbtLock.Value Then Return + EditItem() + RefreshData(tvwTables.Row, tabData.Index) + Case "DeleteItem" 'tbtDelete, Del + If tbtLock.Value Then Return + DeleteItem() + RefreshData(tvwTables.Row, tabData.Index) + Case "Refresh" 'F5, tbtRefresh + RefreshData(tvwTables.Row, tabData.Index) + Case "NewDatabase" 'tbtNewDatabase + modMain.$hFNewDatabase = New FNewDatabase + modMain.$hFNewDatabase.ShowModal() + RefreshData(tvwTables.Row, tabData.Index) + Form_Open() + Case "DeleteDatabase" 'tbtDeleteDatabase + If Message.Question(Subst(("Do you realy want to delete the database: &1?"), modMain.$Connection.Name), ("&Yes"), ("&No")) = 1 Then + modMain.$Connection.MySQL.DataBase.Delete(modMain.$Connection.Name, False) + modMain.$Connection.MySQL.Use("mysql") + Form_Open() + Endif + RefreshData(tvwTables.Row, tabData.Index) + Case "Lock" + tbtDeleteDatabase.Enabled = Not tbtDeleteDatabase.Enabled + tbtNewDatabase.Enabled = Not tbtNewDatabase.Enabled + tbtNew.Enabled = Not tbtNew.Enabled + tbtDelete.Enabled = Not tbtDelete.Enabled + Default + End Select + Catch + modMain.Error() + +End + +Private Function ValidateTableSelected() As Boolean + + If tvwTables.Row = -1 Then Return False + Return True + +End + +Public Procedure NewItem() + + 'Add the item + Select (tabData.Index) + Case 0 'Tables + modMain.$hFNewTable = New FNewTable + modMain.$hFNewTable.ShowModal() + Case 1 'Views + modMain.$hFNewView = New FNewView(Null) + modMain.$hFNewView.ShowModal() + Case 2 'Indexes + If ValidateTableSelected() Then + modMain.$hFNewIndex = New FNewIndex(tvwTables[tvwTables.Row, 0].Text) + modMain.$hFNewIndex.ShowModal() + Endif + Case 3 'Fileds + If ValidateTableSelected() Then + modMain.$hFNewField = New FNewField(tvwTables[tvwTables.Row, 0].Text) + modMain.$hFNewField.ShowModal() + Endif + Case 4 'Routines + modMain.$hFNewRoutine = New FNewRoutine + modMain.$hFNewRoutine.ShowModal() + Case 5 'Triggers + If ValidateTableSelected() Then + modMain.$hFNewTrigger = New FNewTrigger(tvwTables[tvwTables.Row, 0].Text) + modMain.$hFNewTrigger.ShowModal() + Endif + Case 6 'Events + modMain.$hFNewEvent = New FNewEvent + modMain.$hFNewEvent.ShowModal() + Default + End Select + RefreshData(tvwTables.Row, tabData.Index) + +End + +Public Procedure EditItem() + + Select (tabData.Index) + Case 0 'Tables + modMain.$hFNewTable = New FNewTable(Null, tvwTables[tvwTables.Row, 0].Text) + modMain.$hFNewTable.ShowModal() + TableInfo() + Case 1 'Views + tvwViews_DblClick() + Case 2 'Indexes + tvwIndexes_DblClick() + Case 3 'Fileds + tvwFields_DblClick() + Case 4 'Routines + tvwProcedures_DblClick() + Case 5 'Triggers + tvwTriggers_DblClick() + Case 6 'Events + tvwEvents_DblClick() + Default + End Select + Catch + modMain.Error() + +End + +Public Procedure DeleteItem() + + Select (tabData.Index) + Case 0 'Tables + If Message.Question(Subst(("Do you realy want to delete the table: &1?"), tvwTables[tvwTables.Row, 0].Text), ("&Yes"), ("&No")) = 1 Then modMain.$Connection.MySQL.Table.Delete([tvwTables[tvwTables.Row, 0].Text], False) + Case 1 'Views + If Message.Question(Subst(("Do you realy want to delete the view: &1?"), tvwViews[tvwViews.Row, 0].Text), ("&Yes"), ("&No")) = 1 Then modMain.$Connection.MySQL.View.Delete([tvwViews[tvwViews.Row, 0].Text], False) + Case 2 'Indexes + If Message.Question(Subst(("Do you realy want to delete the index: &1?"), tvwIndexes[tvwIndexes.Row, 0].Text), ("&Yes"), ("&No")) = 1 Then modMain.$Connection.MySQL.Index.Delete(tvwTables[tvwTables.Row, 0].Text, tvwIndexes[tvwIndexes.Row, 0].Text) + Case 3 'Fileds + If Message.Question(Subst(("Do you realy want to delete the field: &1?"), tvwFields[tvwFields.Row, 1].Text), ("&Yes"), ("&No")) = 1 Then modMain.$Connection.MySQL.Table.ModifyColumn(tvwTables[tvwTables.Row, 0].Text, "DROP", tvwFields[tvwFields.Row, 1].Text) + Case 4 'Routines + If Message.Question(Subst(("Do you realy want to delete the routine: &1?"), tvwProcedures[tvwProcedures.Row, 1].Text), ("&Yes"), ("&No")) = 1 Then modMain.$Connection.MySQL.Routines.Delete(tvwProcedures[tvwProcedures.Row, 1].Text, False) + Case 5 'Triggers + If Message.Question(Subst(("Do you realy want to delete the trigger: &1?"), tvwTriggers[tvwTriggers.Row, 0].Text), ("&Yes"), ("&No")) = 1 Then modMain.$Connection.MySQL.Trigger.Delete(tvwTriggers[tvwTriggers.Row, 0].Text, False) + Case 6 'Events + If Message.Question(Subst(("Do you realy want to delete the event: &1?"), tvwEvents[tvwEvents.Row, 0].Text), ("&Yes"), ("&No")) = 1 Then modMain.$Connection.MySQL.Event.Delete(tvwEvents[tvwEvents.Row, 0].Text, False) + Default + End Select + modMain.$hFBrowser.SetMessage(("Item deleted."), 1) + +End + +Public Procedure RefreshData(Table As Integer, Tab As Integer) + + textResult.Clear() + SearchInfo() + If Table = -1 Then Table = 0 + tvwTables.Row = Table + FieldInfo() + IndexInfo() + tabData.Index = Tab + +End + +Public Procedure SetEditor() + + textResult.SetStyle() + +End + +'***************************** Procedures for events ************************* +Public Sub tvwEvents_Click() + + textResult.Text = modMain.$Connection.MySQL.Event.Info(tvwEvents[tvwEvents.Row, 0].Text, modMain.$Connection.Name) + Catch + modMain.Error() + +End + +Public Sub tvwEvents_DblClick() + + If Not modMain.$bSchemaOk Then Return + modMain.$hFNewEvent = New FNewEvent(Null, tvwEvents[tvwEvents.Row, 0].Text) + modMain.$hFNewEvent.ShowModal() + EventInfo() + Catch + modMain.Error() + +End + +Public Sub tvwEvents_Select() + + tvwEvents_Click() + +End + +'***************************** Procedures for indexes ************************* +Public Sub tvwIndexes_DblClick() + + modMain.$hFNewIndex = New FNewIndex(tvwTables[tvwTables.Row, 0].Text, Null, tvwIndexes[tvwIndexes.Row, 0].Text) + modMain.$hFNewIndex.ShowModal() + IndexInfo() + Catch + modMain.Error() + +End +'***************************** End of Procedures for indexes ************************* + +'**************************** Procedures for sorting info ******************** +Public Sub tvwFields_ColumnClick(Column As Integer) + + FieldInfo(Column) + +End + +Public Sub tvwTables_ColumnClick(Column As Integer) + + TableInfo(Column) + +End + +Public Sub tvwViews_ColumnClick(Column As Integer) + + ViewInfo(Column) + +End + +Public Sub tvwIndexes_ColumnClick(Column As Integer) + + IndexInfo(Column) + +End + +Public Sub tvwProcedures_ColumnClick(Column As Integer) + + RoutinesInfo(Column) + +End + +Public Sub tvwTriggers_ColumnClick(Column As Integer) + + TriggersInfo(Column) + +End + +Public Sub tvwEvents_ColumnClick(Column As Integer) + + EventInfo(Column) + +End +'**************************** End of Procedures for sorting info ************** + +Public Sub tvwTables_Menu() + + mnuItem.Popup() + +End + +Public Sub tvwViews_Menu() + + mnuItem.Popup() + +End + +Public Sub tvwIndexes_Menu() + + mnuItem.Popup() + +End + +Public Sub tvwFields_Menu() + + mnuItem.Popup() + +End + +Public Sub tvwProcedures_Menu() + + mnuItem.Popup() + +End + +Public Sub tvwTriggers_Menu() + + mnuItem.Popup() + +End + +Public Sub tvwEvents_Menu() + + mnuItem.Popup() + +End + +Public Sub cmbDatabases_Click() + + modMain.$Connection.MySQL.Use(cmbDatabases.Text) + Me.Text = "mysql://" & modMain.$Connection.User & "@" & modMain.$Connection.Host & "/" & modMain.$Connection.Name + SearchInfo() + +End diff --git a/app/examples/Database/MySQLExample/.src/FTables.form b/app/examples/Database/MySQLExample/.src/FTables.form new file mode 100644 index 00000000..fb079f0a --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/FTables.form @@ -0,0 +1,206 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,99,72) + Tag = "Catalogs" + Text = ("Tables") + Icon = Picture["icons/16/Table.png"] + Arrangement = Arrange.Fill + { mnuItem Menu + Text = ("Item") + Visible = False + { mnuNewItem Menu + Action = "NewItem" + Text = ("New Item") + Picture = Picture["icons/16/New.png"] + Shortcut = "F2" + } + { mnuEditItem Menu + Action = "EditItem" + Text = ("Edit Item") + Picture = Picture["icon:/16/edit"] + Shortcut = "Ctrl+E" + } + { mnuDeleteItem Menu + Action = "DeleteItem" + Text = ("Delete Item") + Picture = Picture["icon:/16/trash"] + Shortcut = "Ctrl+Del" + } + { mnuRefresh Menu + Action = "Refresh" + Text = ("Refresh") + Picture = Picture["icons/16/Refresh.png"] + Shortcut = "F5" + } + } + { vplMain VSplit + MoveScaled(0,0,98,71) + Expand = True + { vbxTables VBox + MoveScaled(1,1,97,37) + { tbrMain ToolBar + MoveScaled(0,1,84,4) + { tbtLock ToolButton + MoveScaled(0,0,4,4) + ToolTip = ("Lock") + Action = "Lock" + Picture = Picture["icons/16/Lock.png"] + Toggle = True + } + { Separator2 Separator + MoveScaled(5,0,1,4) + } + { tbtNew ToolButton + MoveScaled(7,0,4,4) + ToolTip = ("New Table") + Action = "NewItem" + Picture = Picture["icons/16/New.png"] + } + { tbtDelete ToolButton + MoveScaled(11,0,4,4) + ToolTip = ("Delete Table") + Action = "DeleteItem" + Picture = Picture["icon:/16/trash"] + } + { tbtRefresh ToolButton + MoveScaled(15,0,4,4) + ToolTip = ("Refresh") + Action = "Refresh" + Picture = Picture["icons/16/Refresh.png"] + } + { Separator1 Separator + MoveScaled(19,0,1,4) + } + { tbtNewDatabase ToolButton + MoveScaled(21,0,4,4) + ToolTip = ("New Database") + Action = "NewDatabase" + Picture = Picture["icons/16/Database.png"] + } + { tbtDeleteDatabase ToolButton + MoveScaled(27,0,4,4) + ToolTip = ("Delete Database") + Action = "DeleteDatabase" + Picture = Picture["icon:/16/remove"] + } + { Separator3 Separator + MoveScaled(32,0,1,4) + } + { cmbDatabases ComboBox + MoveScaled(35,0,24,4) + ReadOnly = True + Sorted = True + } + } + { tabData TabStrip + MoveScaled(1,8,95,26) + Expand = True + Arrangement = Arrange.Fill + Count = 7 + Index = 0 + Text = ("Tables") + Picture = Picture["icons/16/Table.png"] + { tvwTables TableView + MoveScaled(1,3,41,16) + Mode = Select.Single + Header = GridView.Both + } + Index = 1 + Text = ("Views") + Picture = Picture["icons/16/View.png"] + { tvwViews TableView + MoveScaled(1,2,35,13) + Expand = True + Mode = Select.Single + Header = GridView.Both + } + Index = 2 + Text = ("Indexes") + Picture = Picture["icons/16/Index.png"] + { tvwIndexes TableView + MoveScaled(1,3,40,15) + Expand = True + Mode = Select.Single + Header = GridView.Both + } + Index = 3 + Text = ("Fields on:") + Picture = Picture["icons/16/Field.png"] + { tvwFields TableView + MoveScaled(2,3,39,16) + Mode = Select.Single + Header = GridView.Both + } + Index = 4 + Text = ("Routines") + Picture = Picture["icons/16/Routine.png"] + { tvwProcedures TableView + MoveScaled(1,3,41,16) + Expand = True + Mode = Select.Single + Header = GridView.Both + } + Index = 5 + Text = ("Triggers") + Picture = Picture["icons/16/Trigger.png"] + { tvwTriggers TableView + MoveScaled(1,2,41,16) + Expand = True + Mode = Select.Single + Header = GridView.Both + } + Index = 6 + Text = ("Events") + Picture = Picture["icon:/16/clock"] + { tvwEvents TableView + MoveScaled(2,2,41,16) + Expand = True + Mode = Select.Single + Header = GridView.Both + } + Index = 0 + } + } + { textResult TextArea + MoveScaled(1,39,96,32) + } + } +} + +# Gambas Action File 3.0 + +{ Actions + { Action DeleteDatabase + Text = "Delete Database" + Picture = "icon:/16/remove" + } + { Action DeleteItem + Text = "Delete Item" + Shortcut = "Ctrl+Del" + Picture = "icon:/16/trash" + } + { Action EditItem + Text = "Edit Item" + Shortcut = "Ctrl+E" + Picture = "icon:/16/edit" + } + { Action Lock + Text = "Lock" + Picture = "icons/16/Lock.png" + } + { Action NewDatabase + Text = "New Database" + Picture = "icons/16/Database.png" + } + { Action NewItem + Text = "New Item" + Shortcut = "F2" + Picture = "icons/16/New.png" + } + { Action Refresh + Text = "Refresh" + Shortcut = "F5" + Picture = "icons/16/Refresh.png" + } +} diff --git a/app/examples/Database/MySQLExample/.src/modMain.module b/app/examples/Database/MySQLExample/.src/modMain.module new file mode 100644 index 00000000..36e62068 --- /dev/null +++ b/app/examples/Database/MySQLExample/.src/modMain.module @@ -0,0 +1,69 @@ +' Gambas module file + +Public $Connection As New Connection +Public $hResult As Result +Public $Host As String +Public $Password As String +Public $Port As String +Public $User As String +Public $bSchemaOk As Boolean = True +Public $hFTables As FTables +Public $hFNewTable As FNewTable +Public $hFNewView As FNewView +Public $hFNewDatabase As FNewDatabase +Public $hFNewIndex As FNewIndex +Public $hFNewRoutine As FNewRoutine +Public $hFMessage As FMessage +Public $hFNewTrigger As FNewTrigger +Public $hFNewField As FNewField +Public $hFNewEvent As FNewEvent +Public $hFConnect As FConnect + +Public Procedure Main() + + $hFConnect = New FConnect + If $hFConnect.ShowModal() Or Not $Host Or Not $Password Or Not $Port Or Not $User Then Return + With $Connection + .Type = "mysql" + .Name = "mysql" + .Host = $Host + .Password = $Password + .Port = $Port + .User = $User + .Open() + End With + If Not $Connection.Opened Then Return + If CInt($Connection.MySQL.Version.MinorVersion()) < 1 Then + $bSchemaOk = False + Message.Warning("Seems that your MySQL version is lower than 5.1.\ntherefore you will get some error and warning messages.") + Endif + Try $Connection.MySQL.Use(Settings["/General/LastDatabase", "mysql"]) + $hFTables = New FTables + $hFTables.ShowModal() + Catch + Message.Error(Error.Where & "::" & Error.Text) + +End + +Public Procedure Error(Optional Message As String) + + Dec Application.Busy + If Error.Text Like "*MySQL server has gone away" Then + $Connection.Close() + $Connection = Null + $Connection = New Connection + With $Connection + .Type = "mysql" + .Name = "mysql" + .Host = $Host + .Password = $Password + .Port = $Port + .User = $User + .Open() + End With + Endif + If Not Message Then Message = Error.Where & "::" & Error.Text + modMain.$hFMessage = New FMessage(Message) + modMain.$hFMessage.ShowModal() + +End diff --git a/app/examples/Database/MySQLExample/icons/16/Admin.png b/app/examples/Database/MySQLExample/icons/16/Admin.png new file mode 100644 index 0000000000000000000000000000000000000000..44ff99df10f2fb1f32768671d75eca895e88652e GIT binary patch literal 1099 zcmV-R1ho5!P)w{4;0HGr`YL|;LMybw1`OLe3ZZj=>olLreB5j#*3GDHojh)b0s;gJ z4iQapgE(An1qTTN0Rk;MMO%TBZl=F$qr6aeiXJO900000000320SgTe7ab!N8zBe^ z3;+ND0001ox5KN>*tON)v(?|H&DeObz&nSZjJ?c}!_$tz(vri~lf~7P#nqa~*oC~! zE{duhcamhMzG zz|(EC%x$&JZnVv2ugG1g!aSL{2y>Spgr+QwuQ!&ra<$C6+vc~~gv&~zq z$V{TYHZ~)W+cH#Ng@2;p(l_;eWoXORcaJbP>ro%9qy(W&b9fqnwoV#JL%cj!d_WAqr`1;!H z^SR&bp3~)u$K7$c(NU(uGMKy`h^(j5;OX=Cyx!}k*XX3z=$X*sgv8ovw9ZPU#6zIL zE03{EqrrE&(1gO*h{oKL&ESyB--g87bh^`7tjJ5K$xEijIGDIkrNU*h&2_rdd%o3p zywq*C&|k33T(Z$+u*_4d%1@`nR;tHbuF7Ds%xAgOXRpg~vB}p~F}eT%07-OGPE!B? z0000000000Gm_Bo>BxvC0)PAOW&@gndtn~w*AH=6q>Z|vuAGE?Jo@%NsHg-d+}toT z%gj9&@U}WOr=Hcbz7Z7A-VPysK zg@i>sy}W&V{Y1ssfCB8|68-^!LBS!RVc`*y9IOnSQjt;7F|l#+35iL`DblPAGP0>@ z>9HA^S=l+cdHHhk3Ma3nhW#tu>Rn;}MiVRA1^$m?p%`L6XZS5VMUCIn9-95d1 z{SzimnmlFdwCOWc8PsOZnmuRky!i_jE?T@~=`wW&jpZv=u3Eij?Yi|FHg4LyMUz2G zduwmsw(UE1?%KU)?>-$}23EcO2M!)OeB|h{<0np@(l=mWH8eVX=Ipui7cO49e8t#= zm4TJj)a>fD>o;!Rx_!sof)ymdYH4L{bN8;ToxKAqM1a-N$=Su#&D{gU1^}|JV=qq- Rh@b!f002ovPDHLkV1n0V@Bf6>|CG+}V~oa(oW?P&LPD);d(ORUy{(+*|8u?XN}S#RO0|lT#6q<~2y+M! zdk_e75CB?x0At1gW6lg=ylQZLTD4jLQUCx-003hEDU_5DgtZ8S&IonPV|awDxVRyE zApki5LZw1uy<=avU}JoFxz5h#|Np|+*bs9N4PXyofOwP#+`_`r(#o0%EMQ}6 zXK&)*=;Z9;%FF^1aI>La-;UEwZ85JE9 z8yBCz$sm)Ml$4a5lA4yDk&&6j#mxY8v1WF5PHtX)L17UOFCWxLjQj$ELdC)&qGB)s gpoqAHq^KAp0BThwQ%&gz2mk;807*qoM6N<$f+9>2VgLXD literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/Column_FK.png b/app/examples/Database/MySQLExample/icons/16/Column_FK.png new file mode 100644 index 0000000000000000000000000000000000000000..3a2999f124ac2d04088eadd26b5394dc115eeb0c GIT binary patch literal 529 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMf&j6ng*Z=?j1DQaOlbV_y6Jw;M zl@b?Mk)K}}6Jx5T1{BXoNT|rmEA;R%k(MqKt3=I1Ysj&>9m2Pv?%t#onm zla^*>VgeZi22SGQC7PPvQc^68j4&}MZ73+1p{mM)q!=QhFCgFrRF5H!tbNC#Mdxj=FZOld-nWaz50J==ikW4_aPx`Y;Eu4=KfDe z_!JuYI3Qr3v-5Fx_q%?6TO1swo0%=nWSs>x+NvbT4;UECiib319^TVnlz#Y7UGm;T zy%WdetlmD$l4aqJ`TSx+oHhr$O7WNeTsJ8$jp92!?rfStNy-K?tV!*=ih!o{db&7< zNL)@%aA5DrU{E{IqITfSp)REZ$MTLIJlQJn;MvA)8=pRX+{F2T`^HUc3oA~pLPyq? zxv?>K@7UV7y6jY7N_rG`&u(9x(4?SC0*sQ8mj4?ZDp_4sQ#%^ER?VuKu%~U=G_w%a z>3!?w?Q2}NaEjoWC_UL^;b0E=R|a~r#>L#qDa(r`Uq&pd;^SjrT*3d}drF}QDC9g{ L{an^LB{Ts5Hh!x| literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/Database.png b/app/examples/Database/MySQLExample/icons/16/Database.png new file mode 100644 index 0000000000000000000000000000000000000000..a4f2fa4cdd49b69bfef57ea38b3c825fcdd07eda GIT binary patch literal 805 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GXl47&iy_gt-3y{~suFVXgY*^=fBV zDxFv=e`LP&{#g>cr-}CEgS6k@rE_4=?tYK`^DKZ25c`1Z?x~6p_Ol}{ zCufPm*sdpMi2&J84m+QiDSUgo@s2+J$GbK6&y)rV-rQ=uXM!+9<-Oew$EJgH-Q8_} zWSYp8^?Ez{gzjtsI>un{46S9YQb4JmVyTO(RW7bnJhx2##C+)!3uI0%lsz(EA>L0A zVhA$%;zIf7yOaJtTlDwQw0{q${(C&<|$|NCm)-zT&GJz4ng z<;MT-kN^90>F0|ze;-Z#cfaq~lhw~pCqF!q_4Y>J_opkr-JACORN#-rgVZ@UZ8%+gblV9R2_0hFgIf1XYJ{bt|A^`?){)_r__=;xwLi}$y}CZ}=HcoKJ5moU zve`4+@Ypi*Q!9<{?DPHm?%2o-$I?t6Kl3vG`6I{i{f9cw_a7SHe|-A7dMEeWkE&eX zf2e)`@$OUBzki)<1}pEs`1j)BdVd`WVR>ymaW)nQpn-QhT^vIsE+;1>q@<)Ir6r~& zKY#FqX_KUc#Lmi?yLW7D_U@^z`1!+;iD_TmKgM^Bjt3PNHaQ(~V4P@RKf$BIC1A;l z85RK!N?KNG*X$Ma>=XloyreV~CQq3&Y1XuP6K77<)n*r(;D74m+0*(OI$Gz#7`Qd2 z9boaAdVuLv>Qm9FKUqVsnr_t!TIImFb=R-9W6PdRySA;Yl-GAbgV-UST-ga+u`La9 zH+cT?&2U)G7;)gjiq*T-_b=GK{{9V?oVJxSceXCQ%91S|ZC%YCyntc)j>VIwZ0_#f eJZ17?3vmYfr4ApfrT=~hB^FOtKbLh*2~7a3w3D>} literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/Datetime.png b/app/examples/Database/MySQLExample/icons/16/Datetime.png new file mode 100644 index 0000000000000000000000000000000000000000..0baf9a18ad1aa2cc5d161065067a22196763c43d GIT binary patch literal 1140 zcmV-)1dIELP)zrFwf z0313xCQVHS4GqP_!q?>N%hlP*&CtTo+L@S^xw^U3=I`$L|NZsr^YrPk-sO&^vxS9* z$=KoW`~Um@|Lp(&z2V|ze21*m?2DPBgMx(rnMMD5DgSB~|7H;XXcwrOlhNAX`R?QC z|Npnu)JsoKX@{Q3 zz=yWb(Bkm4r>%>Om*VE-v)1Lj;J0b{KM=2 z;`98Any$Xz@5tiu(%$Fd+}h#F!SUP9x}TVrueZGB`MJW&eSmZ|S z=JMC!=JxUM(>gVO=&cpQY z>*(9l+uGN0eSNvk&gcLC!r0if)%4cj=lb^Z?&96>@bKp1;N#-p&BnZJgoLBZ%hCS+ z)BgU@{{F1j*VyCi<>cex(8cKH?R4zyWzNpv=gw$7+}z>C#?Gm! zvRbybYS-6WxVXW@&bYY1m6f1KlaoFGP(%O#07`UHPE!B?00000004UV`T6;X0dac` zC^vP9GGBVE(Bu91`M~`^{c`>JPvk7|!PDDD*Y?=>-A@sY)|*=<8ea0+{a&vTbc6WGrdV73*egJ=ffr5jCg@%WSiHeJijUNCYj*pO$l9QB`mY0~B znwud2BAlI`pP-?lqok##r>G+a03@lZtE{cAuduPQv$Q291^~6Tx45~wyS%-=zrex5 zCnx|Z!^FkL$H>Xb%goKr&(JCWE78)^)YaD4*xB0J+uSTI0509$-{9fmFEBCVSAIB`3o8JOr5%W@7_nRUr$)OcH52}Kfiwm`gF_p?}78@Z{D`;``52m|Nrm*`#0tK zb*Hwr-r2KnzkmPv|Nm3}|4;b#E$!B=^lR5*_wF@JNon7|fA;U+wVyvHUAdBS_H6R; z?A>cyTWfpF^ghrL9wkA3zyL-Ard2AM#}erUe1!GU=OEK_TEU2mQY6Nq?pqC}sYEAip+Qgg=G2mg*1a>lPJH|60oV+#2D z`YzC<<(@8%ArhC96BL*Xj0`t(aB*(lw6VCriFJpIi|vskJ0CLH2u+$7cQ5GDo&ZKW z$w>Kl`~M7;M1%>*wwy2}x}2w+ES3 z666OoCaq(p(%pB0F1tfl8J{UXwdX(+P%hEa#W6(U^3=ZDd<_O1&f(>Wvz>qckJrB4 zW2koQ1oy*$jJdycbe6a;==W6ONfBJIfk``>{S3B)nzX w*L>#NvIC!wzr57u9e-9P^_j$jd0*;fJ6c$LcPa(H1X{-6>FVdQ&MBb@0MLYGXaE2J literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/Index.png b/app/examples/Database/MySQLExample/icons/16/Index.png new file mode 100644 index 0000000000000000000000000000000000000000..391fd0041c3e5d65b852d9b9db2fe6896fdca447 GIT binary patch literal 1122 zcmV-o1fBbdP)_9tq*ECvaF40s;X5007{wE5e>Kj&d3o5(WSO z0Ih`?n0q90S{7kU5+xoA4h#kW000000JVq+(5pI(a2|DC7g0eJDiPMBrl$S*v;Y6P|NpW7 z|H{9@#s2^Q=>Pwi`u#(dng9Q*|NoT#{+Iv%oaE-Ih>wW0zQyo!;S)!qJby(2U8# ziQLnk^}3;v}ga7J<|LlkV?uY;IiU073|L%eD%18L&r2puU|KooD z=7Rs_d;j5c^wwtX$57U^M*ixl{@HH%&sFBYOU|i1yOblhlr26yxK9880Ah4fPE!B? z0000000000001+E2?h%b2ru211uE9_^WIbrK;D%HpYhA|lLPdpC+~po`+M)uJn#L1 z7VpzH2=DiM^Ug8{=J?uQ7lZP>u5v^h1X2Fx(57fqE*=m923mquDIf*|+Tq&g0003M zNklt)JUABD1%2lh^tX;R> z#MF$ze8a{~b(=SD*}84}j-3{kRt(m=cJJA{Z{NQC2M!*xu{GmhuseL@=&|D`PM$h_ z=B&MgBL{=ix$_q;Ub=kc>NRHPx# literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/Lock.png b/app/examples/Database/MySQLExample/icons/16/Lock.png new file mode 100644 index 0000000000000000000000000000000000000000..a39c1a9afa9c5d31e1d7892a1ffccef8416dc2a0 GIT binary patch literal 1195 zcmV;c1XTNpP)f0008bP)t-s00018 zlPr0dq3#GSLnp0mcBvBj0Ez>uZ9ewD6pikg0y zwwbTQtG>;+%G0K|%ay0WhMKc&gPnbrww$rWx60F*tHghjtYvwUdXu!1sllY969@OXZ$*8@_tHR8YtGtArw9nV#tGmjEp{a+Ws;aum zk)^zuufm(J$cme`uEWflv%-a-vW}&=+~MfG$kwB@#HF>x;pOX^t-+D0yq2uQlBL0@ zz{-!RxqX?dsJO`V_4e%V^3vJj?C$aP_4c*G(TbzCkf^_nqQ0KC!ib}@d6lN7wZ-%F z_3G{Lth>kS?eO&U^|8OsgrBjBq`HTlxsIl^g`ci>lA@!t!t(O-^7He!#L%_D&hYW_ zuDr{Co2`SMwSJYcft#v!k)WZlzx4F~9Jac;@Vi85LvF{{Gu<9s&XrA1FP0wAkCZe?lrD9~C(Rh;je`0W3*G zK~xyi0U-h2{r~^~009940|W&I2LS;900000009UI3JVMk4i69!0RR91000sb6cu4& zV`LT=7#RQn000^r9A#!_XlZI|Y#kmS001B%B5iJOaB*^Tbai$kBqabQCwF*xdV73* zet&?0f+#5fDuaZDhKGoWii>lMjgF5i04$J^l9QB`mX~RmnVOrNEdVZ^o}ZwhqNAgv zrKYE-sV@L9s;jK6uCK7Lv9hzYwYD(;GPk(7y1Tr+zQ4S{!NS8c05mnk#m2wK$jQ9Q z%goI-Hvl*}&d<=%($mz{*4NnCIy(S7JwDpo+}+;a;NjxqKR`hM0YXDWTV?=P#TeawY{tz*&@4h`4B3xO|0n{1s-P1LEQ<+>;2@kF=Pxt?S{gM zX*=H9Fkyk%gck;kT5z>*Mlu)BcroL|I~ybxyjsAR5h4Sg>M&wLXYcg5+E2P6wPMnY zQ3Iao@kED58VC)zbYI3{b3{t-lmoBL_}_#PJ%n03)L>8rfgT+_Q%~fc2`gS2@LZ4o zbQsoPNDZC}{YnhXfz>S?lD-#P@tpps$73A?8t^sXs_|eJ1A4IT$=K~|_BG*O7kd7_ zc)zLnPG@JQf|T(Gxq>0y5T8quWIdYl={BrvI9ysrq3J(!^Y`8v z7jMb>x2ICxXQM{<=pH4w$}WZyzh~>?uD!Fa-Z&KmFtSH@1d-`)8hYqrs`3N_*RYj zTax`?nEzv(|8$=4m!7iE-v5ND>VB&EhOzmKv-ym$?~t?bpuY5(q_xZ3|ES6Kp}+2= z!SJBF>8#B5xX9ML$>W!!x5e21uFLqa%k8Pf@wU+Msk_(etANy~m5!OMzt;bux#hCU z_LQvMldtBPuh`0sa=N6cjhnK-)&G{V=6saUpuF*utJt5t@2JG*qrmR0xVX8`|Ea_D zx6tpJwd1wS^^>jKnzQGOrP*wGe7Vlf=l}o0*x0qq|CX@fm$2)Os^ou{(~+*~fSAT( zeuSgT%hCS+)BgU@{{F1j*Rsq1nz!wWq}86g@u|V*n6Teff{JIbu$uk-q5l4({{El- z{+a##Zoa;*#{Z$Z?6uGLjiua{ujYuN-(-NSI&XAh$;ou>?R4zyWzNp9$^Vh8>3)^m zj;!!@j?jCN&1iY1QDS#mwzg{5*IT%_k*MK>n$>oPwsnA^dx4dOhmJ^-lPI0cB>(^b zLUdA2Qvd)5H%3rZaFM8~Vy*i8`S|ez^d`Y9?f&E6D(v7T<=`yo!QtCR&_W?^Mx=iubxW`F=5UMFW4 zS2uSLPpE*Gw~w!%e?VYRFjRmqBs45MA~GsE1}eZG8yBCDn3SB73KbAYOV7y6%FfBn zg9-@d7Zes1mz0*3GYT;R0fTTwWmR=eZC!nXh^QDyK)kW3xuvzOy+cB>Q%V{rAk)>| z)7#fSVdA98Q>MzwF*3+cn?7Uatl4ws&YQnr;UWb^2BpPImM&YqV&$sUYu2t?zd@Nn zW#gvJTefc7zGLUEUAyV|I)z#BCIA>^N rY{Fn_W^Q3=Wo=_?XYb%>Y{Cct>$+5S?~kx!00000NkvXXu0mjf`SVAR literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/Primarykey.png b/app/examples/Database/MySQLExample/icons/16/Primarykey.png new file mode 100644 index 0000000000000000000000000000000000000000..e09d139ed0f17256939edef506853bb342c5410f GIT binary patch literal 585 zcmV-P0=E5$P)GV;?QwU!0sy$1H-N%Pe(^4mT0&mePO1M=1{(YPbm!9dcl z5&!@I_u^0Aw-*2a0QBBO(ySB!0088~7W?a0kar0H000000OH3a`sPjg(dPI6at8S0OX{Oef#;!pk5 zDE;zq{q9)(>1h4hF#Xad{qbr2>{9*bLjB-1{n#u0&?EfmO#I+G{Mau1(kA@QA^qh` z{M|PG^N07}JpJ%={qAA?>rwsZL;d48{oXH>xYH^C002RBQchC<000Mb!^wdT0=W0| z&k<0{wu1wb#0`bM5it3D2?25W{j4Mc(Yh(m>^TL`@EXtSG0*Nr&%PMX`$Jmwh8iaq zT3hA40001#NklQT8CjTsoV-GKgrbtN9NbhD)yOC{bzy|P3>wjz zTG~i_2AvpPJw{{!{a6D-WIlsYoUw@tvVdv4nK=`(fJK6(l^n8wwT-PEihzo}1Jnor Xs^=3K{;}=3xW13 zmjw9%?U(Ji{43?(EE5UynSaBbKhJja+4fUh@cSVy_RSBAtPInye|7r(0jRvz)5S4F z;&N&KOQ9wMo|ftfP3#>_>KZ5Z{kNX9q|D9jrv1sf$xEV>|DF8Vqw!hqr$a!XK|ueW z8#(2_WjL5*rcULP%bLK@|2X~Yk?a_qSuei!F=SubP0l+XkK*n639 literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/Routine.png b/app/examples/Database/MySQLExample/icons/16/Routine.png new file mode 100644 index 0000000000000000000000000000000000000000..7a83453bbaf40f2db075fcfd4470ae438f6b9541 GIT binary patch literal 827 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 B`jG$t literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/Trigger.png b/app/examples/Database/MySQLExample/icons/16/Trigger.png new file mode 100644 index 0000000000000000000000000000000000000000..0ef24449c87af539ad5b2c1b38747f8b327e8da7 GIT binary patch literal 930 zcmV;T16}-yP)aSRvYASqRTZLqr zVMA#|I6~K%Yn6&$8LA-~y&DM93Ujz~MRYJEsv#hvA-j|9}A3aDca>$gkw z!%y$LL)WoYwUkD>hZoJ77tNUtq+kH{#8B$DJ?pkJ>$EK4t|{889oM4~zKboKZWrRN zGv~H3sACARatP3v34?2dJU=_wrY8}#6b#S~cffd%fR95;K>(2h0Imuf%ODEQ3wgVH zk9?0pM?nOR1puuFCf6h&&>#)X4_doihqzVt103psI8P6LQ z$r>-JGZ&N^0G9v&rU(F;1r2oo9?~Bh&K?cQ5d_N$0L%se!3qGh3jm}B8O<6Epa2V# z01C~j1a{8+#y>dW`ut-LjilB2`ttpHdh9+Q+|TF`EeW>akA=~vw6-v2Jxcb^ zyzXnB=^B-~&bI&n0OLtSK~xyiV_<*+MkZz!RyKARmxGhbKY*Kumk-M47YGar4hant z6oT-DMMT3RBBP>X#Ka}Q0+O+D@d=4Z$tkI6QeXk;^o-1`?3~=Z`~n#eUsg_Dp|Gg9 zq_nJDQ3)iVtfHz`QCU@8Q(LFbz@VY2rCr~kqpPRiXkch$%)nsM)ZEhA*4|-iW^Tb? zX~n=`-PzUM)7#f?V`~R-!-R>GCQq3`AhW>=`c+}u4pw|IH``1(Qk01}Wtqwq6OJOBUy07*qoM6N<$ Eg0Fg!u>b%7 literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/View.png b/app/examples/Database/MySQLExample/icons/16/View.png new file mode 100644 index 0000000000000000000000000000000000000000..255a8e23be181824fda3537107fb9df1e694b637 GIT binary patch literal 697 zcmV;q0!ICbP)T)mm)jm%;hw>;2^F{o?8U;pzR}=ltE}{MzOG*W>)v;r!Cz z`_JC{%-#ErxAL9I`R(!jJ4M&&^Zw@X{o?NZ-|YR{>iv+r?wrT>_WJ*!#`@gl{LbC` zm$~od@crKG{o3mN*693>xa*q5_VoGxr_J~J{QvX#{_6Ao;qLv{==_$t=-ur7+3Ee( z=ls&;{EfBenZxt(_x_~E^3&q`&EouuvE-M)@b2~fpTX>wxbMl}{D`gKmA&oi^Zc5* z<;vjvmAU7|-us5C+>^TM=kWZ5rr3g`)q$YWfS=Kkvf;to`-G<1k+$aO^8Uo#`;4pF zz1jPZv*YCO{oU*R(d7HP*!zg3)wB9eVNOAm&y3p@2vm;00wkYPE#B|PZm5oAm{B=0002oNklB?5Kc|frFEii<^g+k6%DgNLWOai9t*pqEWq~w&;G@!QhjLfX;943a`y!?W~A}7z{lGxJhvT`Pdipr{L|A3nG+PeDkhRjAL f24)r}WWWjly{9gAMsoG@00000NkvXXu0mjfQWmu$ literal 0 HcmV?d00001 diff --git a/app/examples/Database/MySQLExample/icons/16/Warning.png b/app/examples/Database/MySQLExample/icons/16/Warning.png new file mode 100644 index 0000000000000000000000000000000000000000..4997cb9222cb0ed478b3c36eae0b36fc0ff1186b GIT binary patch literal 543 zcmV+)0^t3LP)A{= zedXol@$vD9h=_J}cK!YRxw*N5f`Y=r!j+Ykh=_=ZiHTBDQn|Ug^78VFi;GZDP}SAd zprD{QI5^(k-p
XJ=3G605<>O=l}2T@3642hlhv9$H!`FYQDa{VPRpBk&%0Q zd)wpx;Nal;`uc-|gWKEN^YinFh=`Y$m(kJDot>Sks;X~qZ(UtoUS3{pZEa>qnH2y4 z04{V=PE!B?06~bAm6erYYTEqmrekgD?O|c+rUI4irj?bI?WSSsrfur&V{Pi~remb0 z4|6Y{0001SQ3;>{)AYA$?_0a$T002ovPDHLkV1nCU*Nd$B zVb9#-lCGGcGWox7z7aFCa^R_ld1)$6hTK}>NzBT3GJ8BsLJXCetvWp?%aky@*XN$b T_|VJ>XcmK~tDnm{r-UW|sJS1i literal 0 HcmV?d00001 diff --git a/app/examples/Database/PictureDatabase/.directory b/app/examples/Database/PictureDatabase/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Database/PictureDatabase/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Database/PictureDatabase/.icon.png b/app/examples/Database/PictureDatabase/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..983d37059f21a3a981c41ead71ba92c498b2a8c2 GIT binary patch literal 4228 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, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: PictureDatabase\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 21:43+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Picture Database Viewer" +msgstr "Visualitzador de la base de dades d'imatges" + +#: FormPictureDatabase.class:68 +msgid "Thumb" +msgstr "Miniatura" + +#: FormPictureDatabase.class:70 +msgid "Description" +msgstr "Descripció" + +#: FormPictureDatabase.class:101 +msgid "Add image to database" +msgstr "Afegeix una imatge a la base de dades" + +#: FormPictureDatabase.class:113 +msgid "Are you sure you want to delete image " +msgstr "Segur que voleu suprimir la imatge" + +#: FormPictureDatabase.class:114 +msgid "" +" from the database:\n" +"\n" +msgstr "" +"de la base de dades:\n" +"\n" + +#: FormPictureDatabase.class:115 +msgid "Cancel" +msgstr "Canceŀla" + +#: FormPictureDatabase.class:115 +msgid "Delete" +msgstr "Suprimeix" + +#: FormPictureDatabase.class:127 +msgid "Save image from database" +msgstr "Desa la imatge de la base de dades" + +#: FormPictureDatabase.class:131 +msgid "" +"Error saving image:\n" +"\n" +msgstr "" +"Error en desar la imatge:\n" +"\n" + +#: FormPictureDatabase.class:252 +msgid "Picture Database" +msgstr "Base de dades d'imatges" + +#: FormPictureDatabase.class:276 +msgid "Remove Image from database" +msgstr "Suprimeix la imatge de la base de dades" + +#: FormPictureDatabase.class:286 +msgid "Export image from database" +msgstr "Exporta la imatge de la base de dades" + +#: FormPictureDatabase.class:296 +msgid "Update image description" +msgstr "Actualitza la descripció de la imatge" + diff --git a/app/examples/Database/PictureDatabase/.lang/cs.mo b/app/examples/Database/PictureDatabase/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..05fbd49aae6b671453c54bf83a05894634d0bafa GIT binary patch literal 1213 zcmZXSyKmD#9LEnRl!o#U5~AXz6B0-WT*_9ZrK**=mr7JLAH0B6Ckpzi-2)cHTbyWlVICO9`U(s2*e>nq?L@Fl4IuRvYr zFq-#4NF{TyOE5jOUyq9;gkRlL?^BQSu=((hkXS^m8Pda{bwLh6X+r3(^jcKaIklN$ z73TAJEJjS*rlQ4aoGS01gP4-jt`|^0h<^5%tHKEGaK)nxl3;F?J0uw{0)ADBwi2F8 zEGa!neby0fZ4|AWS^I5Iswg{@GnPW~qN;SWWTfrC1-dQx9+#xRTV99LRH*r6jUt0% zw3(BRQaxx@NV$z};{ba~Y+iZ|UR6fQHnzF+p+8RZW@5|SwCJ`~D5i4RSUzVIct>cb z0gXIPUsz_r$mEQqNuTB7nbgCbT-J)EnXinJWUlWp<;ghRD5ujjYi2CEYGuq+dfiMW z3s$kXhETT_;N*=GXRSC*o7v5>Zn>IHuRCJVleM*2CY7-!qMj@+#S)&YxQia$Hbf}C zuO7D@CfpTTtuyI!^|V~tFqS8ATFWk%hUHd08`lx1%N3zwg!V@FJ?QtsmyjPG3Y#~m zS5bPYN$u!C=oxwLJKpd(bdI(>btEJ^=+_-?6SJuT=7=7va@y9L3C6tw@)u}OP7(kB literal 0 HcmV?d00001 diff --git a/app/examples/Database/PictureDatabase/.lang/cs.po b/app/examples/Database/PictureDatabase/.lang/cs.po new file mode 100644 index 00000000..944ccb3e --- /dev/null +++ b/app/examples/Database/PictureDatabase/.lang/cs.po @@ -0,0 +1,75 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Picture Database Viewer" +msgstr "Prohlížeč obrázkové databáze" + +#: FormPictureDatabase.class:68 +msgid "Thumb" +msgstr "Náhled" + +#: FormPictureDatabase.class:70 +msgid "Description" +msgstr "Popis" + +#: FormPictureDatabase.class:113 +msgid "Are you sure you want to delete image " +msgstr "Jste si jisti, že chcete smazat obrázek" + +#: FormPictureDatabase.class:114 +msgid "" +" from the database:\n" +"\n" +msgstr "" +" z databáze:\n" +"\n" + +#: FormPictureDatabase.class:115 +msgid "Cancel" +msgstr "Zrušit" + +#: FormPictureDatabase.class:115 +msgid "Delete" +msgstr "Smazat" + +#: FormPictureDatabase.class:127 +msgid "Save image from database" +msgstr "Uložit obázek z databáze" + +#: FormPictureDatabase.class:131 +msgid "" +"Error saving image:\n" +"\n" +msgstr "" +"Chyba při ukládání obrázku:\n" +"\n" + +#: FormPictureDatabase.form:21 +msgid "Picture Database" +msgstr "Obrázková databáze" + +#: FormPictureDatabase.form:39 +msgid "Add image to database" +msgstr "Přidej obtázek do databáze" + +#: FormPictureDatabase.form:45 +msgid "Remove Image from database" +msgstr "Odstranit obrázek z databáze" + +#: FormPictureDatabase.form:55 +msgid "Export image from database" +msgstr "Export obrázku z databáze" + +#: FormPictureDatabase.form:65 +msgid "Update image description" +msgstr "Aktualizace popisu obrázku" diff --git a/app/examples/Database/PictureDatabase/.lang/de.mo b/app/examples/Database/PictureDatabase/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..18bcd1532f3b814e26358c6e397fef9471ba558c GIT binary patch literal 1244 zcmZva%Wl*#6ow77T!yQV5C|bS0tpGBaM_{ZBGOI@qh@BRnYI$$Imzk7G~nLt|=OU@VD$N%|{?eFKO-U+m`=vUC+qF+QmbqF7{ z6ubbcny4JP&YB`C1x zHRyES91-Fw_#V6segaQ`KfvSQFYpxj8@vHdPYH1yyak>HL(t_}0SPR?Yv5bZ?f(Ee z{VyQWR@!Gk*N#n4>D0ef&8|+@+$>#qSgm0L(@{)?O{@IY)RuP=B<$8yhl{)53w5myaRsm1_|glN&1J zy3)f*v`ZFjj&oz{-IblcR>XpetxMLby&agKWyKq8M4w0bI#avyykBD=u`nrS|H5Kg zjv}!%Mz+5J$sV%{CV$DX_1cNI%%*__cyy~b>=Eo-FP8pDs}O?!8o zNu?iBoJv!$eY~_d=RMesbGBC5c!7@d1UJ*92a&Q3L9#3;VdGApbtLtgyR8|OCA3}c znWa?e?Q(VJ^N_U|_)%myPO&SjR`4@} z3rG#CsMJF-$7zQ25vvgm#tacQ8jdE)T>P_hz$kI`rW6~e3CHe^s}lB)ycxDe!V{iQ p<@P^H+Tw6@t#j~9s2IB%j26qefq7Y0O3|J|NUqsZW_Tu*pFb*lMtuMP literal 0 HcmV?d00001 diff --git a/app/examples/Database/PictureDatabase/.lang/de.po b/app/examples/Database/PictureDatabase/.lang/de.po new file mode 100644 index 00000000..5a0fb81b --- /dev/null +++ b/app/examples/Database/PictureDatabase/.lang/de.po @@ -0,0 +1,76 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Picture Database Viewer" +msgstr "Bilddatenbank-Betrachter" + +#: FormPictureDatabase.class:68 +msgid "Thumb" +msgstr "Vorschau" + +#: FormPictureDatabase.class:70 +msgid "Description" +msgstr "Beschreibung" + +#: FormPictureDatabase.class:113 +msgid "Are you sure you want to delete image " +msgstr "Soll dieses Bild wirklich gelöscht werden " + +#: FormPictureDatabase.class:114 +msgid "" +" from the database:\n" +"\n" +msgstr "" +" aus der Datenbank:\n" +"\n" + +#: FormPictureDatabase.class:115 +msgid "Cancel" +msgstr "Abbrechen" + +#: FormPictureDatabase.class:115 +msgid "Delete" +msgstr "Löschen" + +#: FormPictureDatabase.class:127 +msgid "Save image from database" +msgstr "Bild aus der Datenbank spechern" + +#: FormPictureDatabase.class:131 +msgid "" +"Error saving image:\n" +"\n" +msgstr "" +"Fehler beim Speichern des Bildes:\n" +"\n" + +#: FormPictureDatabase.form:21 +msgid "Picture Database" +msgstr "Bilddatenbank" + +#: FormPictureDatabase.form:39 +msgid "Add image to database" +msgstr "Bild zur Datenbank hinzufügen" + +#: FormPictureDatabase.form:45 +msgid "Remove Image from database" +msgstr "Bild aus der Datenbank entfernen" + +#: FormPictureDatabase.form:55 +msgid "Export image from database" +msgstr "Bild aus der Datenbank exportieren" + +#: FormPictureDatabase.form:65 +msgid "Update image description" +msgstr "Bildbeschreibung aktualisieren" diff --git a/app/examples/Database/PictureDatabase/.lang/es.mo b/app/examples/Database/PictureDatabase/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..b9661b28951d041e957edc2b830c98a9ad6769ba GIT binary patch literal 784 zcmZuv%Wl*#6g5yl8a4=oK!}Awhz;@pyDGF*O(z{9l9^HFA;AW@$!##2u_MPVg%4oG zmJjGRuz__q?Dzvd0M@M7a557ZTCj3*a_>FYKJv}iODpdg)_LFtFajip$Xj9mk_!9T!V@J;agX~yn=pMz`N+a^8)-$MKud>i~7jF$O2 zi#I}cZV}GCox%4?6VFNJIoC5)5cihn%Bvk!5epAW>wHt#b#}Ha4Rq%B9P!R??a2h6 zXnIACr8Kkq#H0D<$M+bQIEk9=bE-0@ORueA17}}S=0%i?3AqZXfDh!>6WI;O|geN5F=8vfXA}e9oI*$#qs7;x2-EST1k|IOH&Wp_gh=mcrrNS zbqN!m4*b1xsW=m;CJRS}a*O*gT=A*m z^%!`L2i~h@9!m-C47Gn`kN$3|yY=koupmPfo9N2AI(4$?xNI@BKUV&uXA<*QQ~d^R Rn$czY=!0qYp8rdR{Q^o++3WxS literal 0 HcmV?d00001 diff --git a/app/examples/Database/PictureDatabase/.lang/es.po b/app/examples/Database/PictureDatabase/.lang/es.po new file mode 100644 index 00000000..65910ccc --- /dev/null +++ b/app/examples/Database/PictureDatabase/.lang/es.po @@ -0,0 +1,36 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FormPictureDatabase.class:270 +msgid "Add image to database" +msgstr "Agregar imagen a la base de datos" + +#: FormPictureDatabase.class:288 +msgid "Export image from database" +msgstr "Exportar imagen desde la base de datos" + +#: FormPictureDatabase.class:252 +msgid "Picture Database" +msgstr "Base de datos de imágenes" + +#: .project:1 +msgid "Picture Database Viewer" +msgstr "Visor de la base de datos de imágenes" + +#: FormPictureDatabase.class:277 +msgid "Remove Image from database" +msgstr "Remover imagen de la base de datos" + +#: FormPictureDatabase.class:299 +msgid "Update image description" +msgstr "Actualizar descripción de la imagen" + diff --git a/app/examples/Database/PictureDatabase/.lang/nl.mo b/app/examples/Database/PictureDatabase/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..337fdc5b6f8edd9c10f934118862fcfb46a5ef34 GIT binary patch literal 1259 zcmZvayKWOf6ov-~ge>7A5F$bd4ir?Zu|pyy=4vI56hTN7oTMImPwX}8nbqv9oge`P zZ-7Y6OHd+F&>?C{dL$}JM8QAnwVgmln*H{iIhTJf-_MWj35>ItS1{jUUc#I_f)7Ru zyaOuIUzB+Y+%QrRqmBetAphy|${9Eruu2_y{@#x~SSRE^Pw64?z^ zvmfo6Rd-@-Y@VIT8AuUpVZ*v+i@CP{9JCp7kBwO8j_xuoXPOVzcqXyX-^{^?QWtuLQX8hv3 zKRrv+rE;-E8?^=RiA=0tGg2jywAz$uD~zIDT9<4Sy4Q2Yqky{{>A1ks*jCaCV@{2Z zxu8w+0Sc&Qid!j@ioBKOmFh@YsZjJ5w6d(wVmC&JL@eplQax;wj&W*e?4*q< zj@}Py?DJC^+Mz~K87fEyS9h3i@R){DXq1>0JS6!*qk+imnd+vIoes`rR7CIF8Kv37 GQ{pd&fKgNc literal 0 HcmV?d00001 diff --git a/app/examples/Database/PictureDatabase/.lang/nl.po b/app/examples/Database/PictureDatabase/.lang/nl.po new file mode 100644 index 00000000..bf304c93 --- /dev/null +++ b/app/examples/Database/PictureDatabase/.lang/nl.po @@ -0,0 +1,67 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PictureDatabase 3.5.90\n" +"PO-Revision-Date: 2014-09-25 21:01 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Picture Database Viewer" +msgstr "AfbeeldingsDatabase Viewer" + +#: FormPictureDatabase.form:21 +msgid "Picture Database" +msgstr "AfbeeldingsDatabase" + +#: FormPictureDatabase.form:39 +msgid "Add image to database" +msgstr "Afbeelding aan database toevoegen" + +#: FormPictureDatabase.form:45 +msgid "Remove Image from database" +msgstr "Verwijder Afbeelding uit Database" + +#: FormPictureDatabase.form:55 +msgid "Export image from database" +msgstr "Exporteer afbeelding uit database" + +#: FormPictureDatabase.form:65 +msgid "Update image description" +msgstr "Afbeeldingomschrijving updaten" + +#: FormPictureDatabase.class:68 +msgid "Thumb" +msgstr "Pictogrammen" + +#: FormPictureDatabase.class:70 +msgid "Description" +msgstr "Omschrijving" + +#: FormPictureDatabase.class:113 +msgid "Are you sure you want to delete image " +msgstr "Weet je zeker dat je de afbeelding wilt verwijderen?" + +#: FormPictureDatabase.class:114 +msgid " from the database:\n\n" +msgstr " van de database:\n\n" + +#: FormPictureDatabase.class:115 +msgid "Delete" +msgstr "Verwijder" + +#: FormPictureDatabase.class:115 +msgid "Cancel" +msgstr "Annuleer" + +#: FormPictureDatabase.class:127 +msgid "Save image from database" +msgstr "Afbeelding uit database opslaan" + +#: FormPictureDatabase.class:131 +msgid "Error saving image:\n\n" +msgstr "Fout bij opslaan van afbeelding:\n\n" + diff --git a/app/examples/Database/PictureDatabase/.project b/app/examples/Database/PictureDatabase/.project new file mode 100644 index 00000000..1f962604 --- /dev/null +++ b/app/examples/Database/PictureDatabase/.project @@ -0,0 +1,25 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=Picture Database Viewer +Startup=FormPictureDatabase +Icon=Images/image-x-generic.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.db +Authors="Timothy Marshal-Nichols\ntimothy.marshal-nichols@ntlworld.com" +Environment="GB_GUI=gb.gtk" +TabSize=2 +Translate=1 +Language=fr +Maintainer=fabien +Vendor=Princeton +Address=fabien@arcalis +License=General Public Licence +Packager=1 +Systems=slackware +Menus=slackware:"Electronics" +Categories=slackware:"Database;Documentation" +Groups=slackware:"Development/Tools" diff --git a/app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class b/app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class new file mode 100644 index 00000000..153e28e7 --- /dev/null +++ b/app/examples/Database/PictureDatabase/.src/FormPictureDatabase.class @@ -0,0 +1,227 @@ +' Gambas class file + +''' +' Name: FormPictureDatabase +' Author: Timothy Marshal-Nichols +' eMail: timothy.marshal-nichols@ntlworld.com +' Version: 1.0 +' Version Date: April 2007 +' Version History: +' +''' +' Licence Information +' +' This program is free software; you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation; either version 2 of the License, or +' (at your option) any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY; without even the implied warranty of +' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +' GNU General Public License for more details. +' +' You should have received a copy of the GNU General Public License +' along with this program; if not, write to the Free Software +' Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +' +' http://www.gnu.org/licenses/gpl.html +' +''' +' Description: +' +' Provides the interface to the pictures database. +' +''' +' Developed using Gambas2 Version +' +' Version: 1.9.48 +' +' Gambas Components Used: +' +' gb - Gambas internal native classes +' gb.qt - Graphical QT toolkit component +' +' Look in the Project menu then Properties... and select +' the Components tab. Check that the listed components +' are in the project. +' +''' +' External Dependencies: +' +' None. +' +''' +' Class Usage: +' +' Set as startup class +' +''' + +Public Sub Form_Open() + + 'DB.Debug = True + + Me.Center() + Dialog.Path = User.Home + GridViewImages.Columns.Count = 2 + GridViewImages.Columns[0].Text = ("Thumb") + GridViewImages.Columns[0].Width = 1.5 * ModuleDatabase.ThumbSize + GridViewImages.Columns[1].Text = ("Description") + GridViewImages.Columns[1].Width = 300 + ' Open database + ' + ' Use somthing like this for a SQLite3 database + ModuleDatabase.OpenDatabase("sqlite3", User.Home, Application.Name & ".db", "", "") + ' + ' Use somthing like this for a MySQL database + 'ModuleDatabase.OpenDatabase("mysql", "", Application.Name, "root", "") + ' + ' Use somthing like this for a PostgreSQL database + 'ModuleDatabase.OpenDatabase("postgresql", "localhost", Application.Name, "timothy", "password") + ' + ' Display the database content + ModuleDatabase.Select() + DisplayImages() +Catch + PanelButtons.Enabled = False + Message.Warning(ERROR.Text) +End + +Public Sub Form_Close() + ModuleDatabase.CloseDatabase() +End + +''' +''' Buttons +''' + +Public Sub ToolButtonAdd_Click() + Dialog.Filter = FileFilter(True) + Dialog.Title = ("Add image to database") + If Dialog.OpenFile() Then Return + ModuleDatabase.Add(Dialog.Path) + ModuleDatabase.Select() + DisplayImages() +Catch + Message.Warning(ERROR.Text) +End + +Public Sub ToolButtonRemove_Click() + Dim m As String + If GridViewImages.Row >= 0 Then + m = ("Are you sure you want to delete image ") & (GridViewImages.Row + 1) + m &= (" from the database:\n\n") & TextAreaDescription.Text + If Message.Question(m, ("Delete"), ("Cancel")) = 1 Then + ModuleDatabase.Delete(GridViewImages.Row) + ModuleDatabase.Select() + DisplayImages() + End If + End If +Catch + Message.Warning(ERROR.Text) +End + +Public Sub ToolButtonExport_Click() + Dialog.Filter = FileFilter(False) + Dialog.Title = ("Save image from database") + If Dialog.SaveFile() Then Return + PictureBoxImage.Picture.Save(Dialog.Path) +Catch + Message.Warning(("Error saving image:\n\n") & ERROR.Text) +End + +Public Sub ToolButtonUpdate_Click() + ModuleDatabase.Update(GridViewImages.Row, TextAreaDescription.Text) + GridViewImages[GridViewImages.Row, 1].Text = TextAreaDescription.Text +Catch + Message.Warning(ERROR.Text) +End + +''' +''' GridView events +''' + +Public Sub GridViewImages_Click() + If GridViewImages.Row >= 0 Then + ShowImage(GridViewImages.Row) + End If +End + +''' +''' Functions +''' + +Private Sub DisplayImages() + Dim i As Integer + Dim tempFile As String + Dim tempPicture As String + GridViewImages.Clear() + GridViewImages.Rows.Count = ModuleDatabase.ResultPictures.Count + If ModuleDatabase.ResultPictures.Count > 0 Then + tempFile = Temp() & ".png" + For Each ModuleDatabase.ResultPictures + i = ModuleDatabase.ResultPictures.Index + tempPicture = ModuleDatabase.ResultPictures["thumb"].Data + If tempPicture Then + File.Save(tempFile, tempPicture) + GridViewImages[i, 0].Picture = Picture.Load(tempFile) + End If + GridViewImages[i, 1].Text = ModuleDatabase.ResultPictures["description"] + Next + GridViewImages.Row = 0 + ShowImage(0) + Else + TextAreaDescription.Text = "" + PictureBoxImage.Picture = Null + PictureBoxImage.Resize(1, 1) + End If + If Exist(tempFile) Then Kill tempFile + GridViewImages.Rows.Height = ModuleDatabase.ThumbSize + ToolButtonRemove.Enabled = (GridViewImages.Rows.Count > 0) + ToolButtonExport.Enabled = ToolButtonRemove.Enabled + ToolButtonUpdate.Enabled = ToolButtonRemove.Enabled +End + +Private Sub ShowImage(Row As Integer) + Dim tempFile As String + Dim tempPicture As String + tempFile = Temp() & ".png" + ModuleDatabase.ResultPictures.MoveTo(Row) + TextAreaDescription.Text = ModuleDatabase.ResultPictures["description"] + If tempFile Then + 'tempPicture = ModuleDatabase.ResultPictures["image"].Data + File.Save(tempFile, ModuleDatabase.ResultPictures["image"].Data) + PictureBoxImage.Picture = Picture.Load(tempFile) + PictureBoxImage.Resize(PictureBoxImage.Picture.Width, PictureBoxImage.Picture.Height) + Else + PictureBoxImage.Picture = Null + End If + If Exist(tempFile) Then Kill tempFile +End + +' Filter for our user file open dialog. All image types supported by Gambas +Private Function FileFilter(Optional All As Boolean = False) As String[] + Dim filter As New String[] + If All Then + filter.Add("*.png;*.jpeg;*.jpg;*.bmp;*.gif;*.xpm") + filter.Add("All Graphics") + End If + filter.Add("*.png") + filter.Add("Portable Network Graphics") + filter.Add("*.jpeg *.jpg") + filter.Add("Joint Photographic Experts Group") + filter.Add("*.bmp") + filter.Add("Windows Bitmap") + filter.Add("*.gif") + filter.Add("Graphics Interchange Format") + filter.Add("*.xpm") + filter.Add("X PixMap") + If All Then + filter.Add("*") + filter.Add("All Files") + End If + Return filter +End + +''' End of class FormPictureDatabase ''' diff --git a/app/examples/Database/PictureDatabase/.src/FormPictureDatabase.form b/app/examples/Database/PictureDatabase/.src/FormPictureDatabase.form new file mode 100644 index 00000000..49edc095 --- /dev/null +++ b/app/examples/Database/PictureDatabase/.src/FormPictureDatabase.form @@ -0,0 +1,67 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,62,47) + Text = ("Picture Database") + Icon = Picture["Images/image-x-generic.png"] + Arrangement = Arrange.Vertical + { HSplit1 HSplit + MoveScaled(0,0,60,46) + Expand = True + { PanelButtons Panel + MoveScaled(0,0,21,42) + Arrangement = Arrange.Vertical + { HPanel1 HPanel + MoveScaled(0,0,21,5) + AutoResize = True + { ToolButtonAdd ToolButton + MoveScaled(0,0,5,5) + ToolTip = ("Add image to database") + Picture = Picture["Images/list-add.png"] + } + { ToolButtonRemove ToolButton + MoveScaled(5,0,5,5) + ToolTip = ("Remove Image from database") + Picture = Picture["Images/list-remove.png"] + } + { Separator1 Separator + MoveScaled(8,0,2,4) + } + { ToolButtonExport ToolButton + MoveScaled(10,0,5,5) + ToolTip = ("Export image from database") + Picture = Picture["Images/document-save-as.png"] + } + { Separator2 Separator + MoveScaled(14,0,2,4) + } + { ToolButtonUpdate ToolButton + MoveScaled(16,0,5,5) + ToolTip = ("Update image description") + Picture = Picture["Images/document-save.png"] + } + } + { GridViewImages GridView + MoveScaled(1,6,19,28) + Expand = True + Header = GridView.Both + Border = False + } + } + { VSplit1 VSplit + MoveScaled(23,1,34,44) + { TextAreaDescription TextArea + MoveScaled(0,0,23,7) + Wrap = True + Border = False + } + { ScrollView1 ScrollView + MoveScaled(0,7,24,35) + { PictureBoxImage PictureBox + MoveScaled(0,0,18,25) + Stretch = True + } + } + } + } +} diff --git a/app/examples/Database/PictureDatabase/.src/ModuleDatabase.module b/app/examples/Database/PictureDatabase/.src/ModuleDatabase.module new file mode 100644 index 00000000..935fe6b1 --- /dev/null +++ b/app/examples/Database/PictureDatabase/.src/ModuleDatabase.module @@ -0,0 +1,215 @@ +' Gambas module file + +''' +' Name: ModuleDatabase +' Author: Timothy Marshal-Nichols +' eMail: timothy.marshal-nichols@ntlworld.com +' Version: 1.0 +' Version Date: April 2007 +' Version History: +' +''' +' Licence Information +' +' This program is free software; you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation; either version 2 of the License, or +' (at your option) any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY; without even the implied warranty of +' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +' GNU General Public License for more details. +' +' You should have received a copy of the GNU General Public License +' along with this program; if not, write to the Free Software +' Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +' +' http://www.gnu.org/licenses/gpl.html +' +''' +' Description: +' +' Provides the interface to the pictures database. +' +''' +' Developed using Gambas2 Version +' +' Version: 1.9.48 +' +' Gambas Components Used: +' +' gb - Gambas internal native classes +' gb.db - Database access +' +' Look in the Project menu then Properties... and select +' the Components tab. Check that the listed components +' are in the project. +' +''' +' External Dependencies: +' +' The required type of database must exist. +' +''' +' Class Usage: +' +' Open the pictures database. If the database or table does not +' exist then they are created. +' +' Use somthing like this for a SQLite3 database +' +' ModuleDatabase.OpenDatabase("sqlite3", User.Home, Application.Name, "", "") +' +' Use somthing like this for a MySQL database +' +' ModuleDatabase.OpenDatabase("mysql", "localhost", Application.Name, "mysql", "password") +' +' Use somthing like this for a PostgreSQL database +' +' ModuleDatabase.OpenDatabase("postgresql", "localhost", Application.Name, "timothy", "password") +' +' Call the Select() method to update items and then use ResultPictures to access the data. +' +' ModuleDatabase.Select() +' ModuleDatabase.ResultPictures.MoveTo(Row) +' +' You can then use the Add(), Update() and Delete() methods to change the database. +' +' ModuleDatabase.Add(Path) +' ModuleDatabase.Update(Row, NewDescription) +' ModuleDatabase.Delete(Row) +' +' Call the CloseDatabase method before you quit your application +' +' ModuleDatabase.CloseDatabase() +' +''' + +Public Const ThumbSize As Integer = 36 +Public ResultPictures As Result + +Private databaseConnection As New Connection +Private tempFile As String + +Public Sub _init() + tempFile = Temp() & ".png" +End + +' Opens the pictures database. If the database or table does not +' exist then they are created. +Public Sub OpenDatabase(DBType As String, DBHost As String, DBName As String, UserName As String, UserPassword As String) + Dim pictureTable As Table + Dim errorMessageHeader As String + ' If you wnat to see the commands sent to the + ' database then uncommant this line + ' DB.Debug = TRUE + ' DBName = Lower(DBName) + ' Open a connection (to the database server only) + databaseConnection.Type = Lower(DBType) + databaseConnection.Host = DBHost + databaseConnection.Name = "" + databaseConnection.Login = UserName + databaseConnection.Password = UserPassword + databaseConnection.Port = "" + ' Open the connection + Try databaseConnection.Open() + If Error Then + errorMessageHeader = "Could not open database connection " & DBHost + Error.Raise(Error.Text) + End If + ' Check if the server connection has a database with the + ' required database name. + If Not databaseConnection.Databases.Exist(DBName) Then + Print "Database not found. Creating new database" + ' Create a new database + databaseConnection.Databases.Add(DBName) + ' I found I needed this with a SQLite database + ' (but not with a MySQL database) + Wait 0.5 + End If + ' Close the server connection + databaseConnection.Close() + ' Open a connection to the database + databaseConnection.Host = DBHost + databaseConnection.Name = DBName + Try databaseConnection.Open() + If Error Then + errorMessageHeader = "Could not open database " & DBName & " on " & DBHost + Error.Raise(Error.Text) + End If + ' Check if the database has a pictures table + If Not databaseConnection.Tables.Exist("pictures") Then + Print "Database tables not found. Creating new pictures table" + ' Add a picture table to the database + pictureTable = databaseConnection.Tables.Add("pictures") + pictureTable.Fields.Add("id", db.Serial) ' id field as autoinc integer + pictureTable.Fields.Add("thumb", db.Blob) ' thumb field as blob + pictureTable.Fields.Add("image", db.Blob) ' ' image field as blob + pictureTable.Fields.Add("description", db.String, 0) ' description field as unlimited string + pictureTable.PrimaryKey = ["id"] + pictureTable.Update() + End If +Catch + If errorMessageHeader = "" Then + errorMessageHeader = "Database connection error: " & DBName & " on " & DBHost + End If + Error.Raise("" & errorMessageHeader & "


Error:
" & DConv(Error.Text)) +End + +Public Sub Add(ImagePath As String) + Dim img As Image + Dim newPicture As Result + 'Dim pictureData As String + Dim scale As Float + Dim eTime As Float + newPicture = databaseConnection.Create("pictures") + ' Save temp image as png file + img = Image.Load(ImagePath) + img.Save(tempFile) + newPicture["image"] = File.Load(tempFile) + ' Create image thumb + If img.Width > thumbSize Or img.Height > thumbSize Then + ' Calc factor to scale isotropic + scale = Min(ThumbSize / img.Width, ThumbSize / img.Height) + img = img.Stretch(img.Width * scale, img.Height * scale) + img.Save(tempFile) + End If + newPicture["thumb"] = File.Load(tempFile) + ' Add description and update + newPicture["description"] = "Image " & File.BaseName(ImagePath) & " added: " & Format(Now, "dddd, dd mmmm yyyy hh:nn:ss") + eTime = Timer + newPicture.Update() + Print "Done in "; Format(Timer - eTime, "#.###"); " s" + If Exist(tempFile) Then Kill tempFile +Catch + Error.Raise("Add database record error
Error:
" & DConv(Error.Text)) +End + +Public Sub Select() + ResultPictures = databaseConnection.Edit("pictures") +Catch + Error.Raise("Select database records error
Error:
" & DConv(Error.Text)) +End + +Public Sub Update(Row As Integer, Description As String) + ResultPictures.MoveTo(Row) + ResultPictures["description"] = Conv(Description, Desktop.Charset, databaseConnection.Charset) + ResultPictures.Update() +Catch + Error.Raise("Update database record error
Error:
" & DConv(Error.Text)) +End + +Public Sub Delete(Row As Integer) + ResultPictures.MoveTo(Row) + ResultPictures.Delete() +Catch + Error.Raise("Delete database record error
Error:
" & DConv(Error.Text)) +End + +Public Sub CloseDatabase() + Try databaseConnection.Close() + If Error Then Print "Error closing database" +End + +''' End of ModuleDatabase ''' diff --git a/app/examples/Database/PictureDatabase/Images/document-save-as.png b/app/examples/Database/PictureDatabase/Images/document-save-as.png new file mode 100644 index 0000000000000000000000000000000000000000..fcd8c919711476175e6a2d5cb21df82ec8bcbb74 GIT binary patch literal 845 zcmV-T1G4;yP)hVPfM9)Y zTY7SQfo*PZYj}udZg6XEaBFUGYi@9BZg6XEaBJu1=iuPrZg6X7X=G+-WXsFT$jHc5 zS5sA2Q#fafkG9)&snuhk&O2<8M}4DosMKqv(@KD-UYf^SmcdhuwOEq9V4cfuaBFpW zY-ov-RC0TAb!+za_F$aKgN1rjcZW1-O4+Sr?&ndsQhplq= z?d`a@xasNW=jZ3s*v#VM;jyu>(9qCrbY*UIWzNpd-rwKf-rnBc-`?Kd-{0N<007_L z-{Ilm=;-Lm%F6EU?(p#M@bK~Q@bLQi`v3p`I}llY0000tbW%=J0G;jh$~!Rn^Y#7R zhzxxLg`Nb&yt@eAFq8FSO2vV?YNL@;MowS)ftkFlKo{#H373$lv<00B8k zL_t&-({0Z8Q^GJ5!11{E-g^(MxHSq6YK^A=pBB4$i4gI zT`mYAl$DL4oZLKQEx(|!sCahHQBqoF)$)qUD(AdoVR5OtCe5{V^$n=8soCX{TUy&v z+}_a%XP0wXkypBVQrz1|pjE6uYwKd}7d)^*MG0217>VFPw?-LbSn(*zCT47lxoyBh z+u9C=e+%BlI^JXZ67cZBA?&K^k>^;Ey(h4LY6m7~z~{PZ_zaKX)h`&+;Bnh!;%Prt z4a4Wfe#WSFWkXUx3sQP5JfV~ZwSa`|HzT)qa6v;@)kA>6`_YFnH2z4EJ>lo%^fG}6 zCE>^`5A!JJ;RuiM=o=6saq|9=>NEEB{R1TUH_c|uG|eQNmMol+;K_JKQv^+uf13FN Xun>Ufps_sO00000NkvXXu0mjf*fGY; literal 0 HcmV?d00001 diff --git a/app/examples/Database/PictureDatabase/Images/document-save.png b/app/examples/Database/PictureDatabase/Images/document-save.png new file mode 100644 index 0000000000000000000000000000000000000000..a89f010f2137b11b520783313bc5fdf2ed1837d2 GIT binary patch literal 903 zcmV;219<$2P)hVPfM9)Y zTY7SQfo*PZYj}udZg6XEaBFUGYi@9BZg6XEaBJu1=iuPrZg6X7X=G+-WXsFT$jHc5 zS5sA2Q#fafkG9)&snuhk&O2<8M}4DosMKqv(@KD-UYf^SmcdhuwOEq9V4cfuaBFpW zY-ov-RC0TAb!+za_F$aKgN1rjcZW1-O4+Sr?&ndsQhplq= z?d`a@xasNW=jZ3s*v#VM;jyu>(9qEH^Yg~cz{$?T;^X1*@$t^i&f($V&CSis%*@Eh z$lKf7k&%&@n3$uaqOh>Ava+(fySu!+yv4=E(b3Vsz`)kl*4Nk9l$4a0mzSlbrKzc@ ztgNiMy1K!^!NkPG$;rvZ#l*+Q$1kRBK>z>%H*``?QvjXq^vXLh`t$Yu-Q-){`~Cj* zNx8k$SxE8z{qYOkt{HRFp|XT_X+$t*R<(ov{*SSo{{B`uBMY*N0003-NkliqvqbO1bxWT>a7L_M9Y3~qE{@f+GFS%R*03ad~ z(xRecKxnaX@d=6DJ;tQulu%7gOV2R%8vFVOGP8a;J0~{}T{hx}W1OlPl?)z5< d0-Y#KT|XI$hBxuiCnf*@002ovPDHLkV1n+n60fcu?H8t$*42J5P7M z`+R!Ij+MEcn={)srZ%rnY*-styE>|BWmx(0ppvEjg^RuO7TN{oH_Y67l={b^p)b zcNgz$>{)oQW8T5GIs2Pu?Q58^w|2^oyvnZRtdhLW%~@?5J;IA;tvNM!{qfC*wjaH? zapC5}GghCRw(`W(6~`wpJ2r92(URKk?9#Ttz`(rPsrA$Lctw?ZMi#HwesRT)OUt)k zT(<4v(ybSkY&pMZ^SPdx>w|)WOY0{VRCY{Re%wE<+%+U`{>t6+R_>a&V%MDIJEtz# z(mQi)d}3O7L{vdpV`x}-OaH>YS?j$c3m!dsWbU41>z8ZmpKI-tt?!hiY!<0t60U9$ zVrb)UXzgca?`Lf5t7H;p>=gU>@ngr35{KXt+kip~pF9(fEMxa9BiD2-t8i5lUoGnh zRTDo0=M)R~Br~@px5!$jkTNT;jO>!;qMF|Nwh5=toU-&xjm)0l5mmo=`|cx0j+{Js zvY?{VAt1{!AbZEIU55@Gdi(b6^B2!wynL~4{o1XiqNjn$PNgKsFPNdR>0jP##%cV` z9XdR0ykc_7>RP%6CWdNCvclXf99*32%uL^SZ5}f)Fcx{bIEF}EPEJtZG+33u=)vrfHr7|Kq_lXnW<}kyvbwda>K7YZo7*w9WoggEra2^PXG=$0SF^jTr>BdDo0s$Z z>*vc)*wDCse!W9NK!JxxMnr{6N=OM$M1;PXo}HqhrlqQ?sjjWEv9>jPO2;z4ytq2Y z#K1yN&&5VO+>E{`nc7A@QAF-j};v-e>>ALY7g;e%reVt{x`ue)){fUQ<`Oe;+b9YY;rxeig0sW%Q>Ja%l^k|jJWISveq a9FiD(XU>n6v6Zg`6%wAVelF{r5}E)!3h_Pw literal 0 HcmV?d00001 diff --git a/app/examples/Database/PictureDatabase/Images/list-add.png b/app/examples/Database/PictureDatabase/Images/list-add.png new file mode 100644 index 0000000000000000000000000000000000000000..d4d751c9e4c163533061b83a0a1577f145d6acd3 GIT binary patch literal 366 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H3?x5i&EW)6(*k@#T!FMn>JlJH3;?R!dgket zGfy`I(dnlLF2C4)@%f&M&-Yz^x#`r?-4~v3Klg0g*=JkNJll5W+0^Z~r);}DY0Iq% zn{V}QyxFz>X4~4E&8u$Iuee^j?E3tD50)H!R5_tMtW&-3P7y8Ph% zsw0oJ9!50+9mHP}2G25qZ=bq5x;i_#y*<16?rmaW(_g$u61{f$xJX*kGjoF{>4~Y%VkyrbJYixLHRso77w#%Dvr|*E cGh@qQVkkA{aBa={%?1iDPgg&ebxsLQ06^xHVE_OC literal 0 HcmV?d00001 diff --git a/app/examples/Database/PictureDatabase/Images/list-remove.png b/app/examples/Database/PictureDatabase/Images/list-remove.png new file mode 100644 index 0000000000000000000000000000000000000000..902624de5a28d1191b6f4ce9e6163cea028fcd6d GIT binary patch literal 282 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 M07*qoM6N<$f=#3V(f|Me literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/AnalogWatch/.project b/app/examples/Drawing/AnalogWatch/.project new file mode 100644 index 00000000..e2be2c41 --- /dev/null +++ b/app/examples/Drawing/AnalogWatch/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Analog watch +Startup=FrmClock +Icon=timer.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Description="Analog watch example.\n\nThis example uses the Paint class to draw an anti-aliased analog watch." +Environment="GB_GUI=gb.qt" +TabSize=2 +Maintainer=fabien +Vendor=Example +Address=fabien@arcalis +License=General Public Licence +Packager=1 +Screenshot=2014-12-14.png diff --git a/app/examples/Drawing/AnalogWatch/.src/FrmClock.class b/app/examples/Drawing/AnalogWatch/.src/FrmClock.class new file mode 100644 index 00000000..a009b612 --- /dev/null +++ b/app/examples/Drawing/AnalogWatch/.src/FrmClock.class @@ -0,0 +1,152 @@ +' Gambas class file + +'***********Analog Clock Example Program************************ +'***********By: Ahmad Kamal ****************** +'***********Written for Gambas: gambas.sourceforge.net********** +'***********V1.0: 22-July-2003*************************************** +'***********V1.1: 24-July-2003 Optimized by Benoit himself ;)***** + +Private $iLast As Integer +Private $dNow As Date + +' Fixed coordinate system +' We use the transformation matrix for scaling +Private Const W As Integer = 1024 +Private Const H As Integer = 1024 + +Public Sub TimerClk_Timer() + + If Second(Now) = $iLast Then Return + $dNow = Now + + dwgArea.Refresh + +End + +' Main routine that updates the clock + +Public Sub DrawClock() + + Dim angle As Float + Dim dNow As Date + Dim eScale As Float + Dim sTime As String + + eScale = Min(dwgArea.Width / W, dwgArea.Height / H) + + Paint.Translate(dwgArea.W / 2, dwgArea.H / 2) + Paint.Scale(eScale, eScale) + + dNow = $dNow + $iLast = Second(dNow) + + Paint.Brush = Paint.Color(&HDCDCDC&) 'Light gray color + Paint.Arc(0, 0, W / 2) + Paint.Fill + Paint.Brush = Paint.Color(dwgArea.Background) + Paint.Arc(0, 0, W / 2 * 0.9) + Paint.Fill + + sTime = Format(dNow, "hh:nn:ss") + + Paint.Font.Bold = True + Paint.Font.Size = 60 + Paint.Brush = Paint.Color(Color.Black) + Paint.Text(sTime, - W / 2, - H * 0.4, W, H * 0.1, Align.Top) + Paint.Fill + + ' Draw seconds + angle = Second(dNow) / 60 * Pi(2) - Pi(0.5) 'The angle that the arm makes, with a line from clock center to 12O'clock + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.Red, 128)) + Paint.LineWidth = 6 + Paint.MoveTo(0, 0) + Paint.LineTo(Cos(angle) * W / 2, Sin(angle) * H / 2) + Paint.Stroke + + ' Draw minutes + angle = CFloat(Time(dNow)) * 24 * Pi(2) - Pi(0.5) + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.Black, 128)) + Paint.MoveTo(Cos(angle) * W * 0.45, Sin(angle) * H * 0.45) + Paint.LineTo(Sin(angle) * W * 0.05, - Cos(angle) * H * 0.05) + Paint.LineTo(- Sin(angle) * W * 0.05, Cos(angle) * H * 0.05) + Paint.Fill + + ' Draw hours + angle = CFloat(Time(dNow)) * 2 * Pi(2) - Pi(0.5) + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.Black, 128)) + Paint.MoveTo(Cos(angle) * W * 0.35, Sin(angle) * H * 0.35) + Paint.LineTo(Sin(angle) * W * 0.05, - Cos(angle) * H * 0.05) + Paint.LineTo(- Sin(angle) * W * 0.05, Cos(angle) * H * 0.05) + Paint.Fill + + ' Draw circle on the center of the clock to hide the arms intersection + + Paint.Brush = Paint.Color(Color.Black) + Paint.Arc(0, 0, 0.1 * W) + Paint.Fill + + DrawFrame + +End + +' Draw the clock frame + +Public Sub DrawFrame() + + Dim I As Integer + Dim angle As Float + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.Black, 64)) + + For I = 0 To 59 + If I % 5 = 0 Then + Paint.LineWidth = 12 + Else + Paint.LineWidth = 2 + Endif + angle = Pi(2) * I / 60 + Paint.MoveTo(Cos(angle) * 0.45 * W, Sin(angle) * 0.45 * H) + Paint.LineTo(Cos(angle) * W / 2, Sin(angle) * H / 2) + Paint.Stroke + Next + +End + + +Public Sub MenuAbout_Click() + + Dim AboutMessage As String + AboutMessage = "Analog Clock Example Program for Gambas\nWritten by: Ahmad Kamal and Benoît Minisini" + + Message.info(AboutMessage) + +End + + +Public Sub MenuExit_Click() + + Me.Close + +End + + +Public Sub DwgArea_Menu() + + MenuPopUp.popup + +End + +Public Sub DwgArea_Draw() + + DrawClock + +End + +Public Sub Form_Open() + + TimerClk_Timer + +End diff --git a/app/examples/Drawing/AnalogWatch/.src/FrmClock.form b/app/examples/Drawing/AnalogWatch/.src/FrmClock.form new file mode 100644 index 00000000..c8746890 --- /dev/null +++ b/app/examples/Drawing/AnalogWatch/.src/FrmClock.form @@ -0,0 +1,30 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(52.2857,29,39,39) + Text = ("Analog clock") + Arrangement = Arrange.Fill + { MenuPopUp Menu + Text = ("File") + Visible = False + { MenuAbout Menu + Text = ("&About") + Shortcut = "F11" + } + { MenuSep Menu + } + { MenuExit Menu + Text = ("E&xit") + } + } + { DwgArea DrawingArea + MoveScaled(2,0,21,24) + Background = Color.LightBackground + ToolTip = ("Resize Window and the clock will follow \nRight click for a menu") + } + { TimerClk #Timer + #MoveScaled(17.1429,2.2857) + Enabled = True + Delay = 250 + } +} diff --git a/app/examples/Drawing/AnalogWatch/timer.png b/app/examples/Drawing/AnalogWatch/timer.png new file mode 100644 index 0000000000000000000000000000000000000000..6708fdd27788c95fe589ee232dff093831b8041a GIT binary patch literal 2778 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Barcode/.directory b/app/examples/Drawing/Barcode/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Drawing/Barcode/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Drawing/Barcode/.icon.png b/app/examples/Drawing/Barcode/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a4e3ec072e2c2cc337c467973d2ee992b03419c1 GIT binary patch literal 3990 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 HDnk2zd799x literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Barcode/.lang/ca.po b/app/examples/Drawing/Barcode/.lang/ca.po new file mode 100644 index 00000000..39da41ed --- /dev/null +++ b/app/examples/Drawing/Barcode/.lang/ca.po @@ -0,0 +1,77 @@ +msgid "" +msgstr "" +"Project-Id-Version: Barcode\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 17:11+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FMain.class:280 +msgid "About..." +msgstr "Quant a..." + +#: .project:2 +msgid "A module to print EAN-13 barcodes. The barcode is constructed from first principles (http://en.wikipedia.org/wiki/European_Article_Number). The barcode can be formatted for screen or printer." +msgstr "Un mòdul per a imprimir codis de barres EAN-13. Els codis de barres es construeixen des de zero (http://en.wikipedia.org/wiki/European_Article_Number). El codi de barres pot tenir format per pantalla o per impressora." + +#: FMain.class:221 +msgid "Barcode" +msgstr "Codi de barres" + +#: .project:1 +msgid "Barcode Printing" +msgstr "Impressió de codi de barres" + +#: FMain.class:285 +msgid "E&xit" +msgstr "&Surt" + +#: FMain.class:236 +msgid "Height" +msgstr "Alçada" + +#: FMain.class:72 +msgid "Please enter a valid 13 digit EAN-13 number" +msgstr "Introduïu un número EAN-13 vàlid de 13 dígits" + +#: FMain.class:226 +msgid "PosX" +msgstr "-" + +#: FMain.class:231 +msgid "PosY" +msgstr "-" + +#: FMain.class:275 +msgid "&Print Barcode" +msgstr "Im&primeix el codi de barres" + +#: FMain.class:269 +msgid "&Refresh Barcode" +msgstr "&Actualitza el codi de barres" + +#: FMain.class:246 +msgid "Size for printer" +msgstr "Mida per la impressora" + +#: FMain.class:251 +msgid "Size for screen" +msgstr "Mida per la pantalla" + +#: FMain.class:153 +msgid "" +"This program was made by Charles Guerin and\n" +"modified by Benoît Minisini." +msgstr "" +"Aquest programa va ser creat per Charles Guerin i\n" +"modificat per Benoît Minisini." + +#: FMain.class:241 +msgid "Width" +msgstr "Amplada" + diff --git a/app/examples/Drawing/Barcode/.lang/cs.mo b/app/examples/Drawing/Barcode/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..f264f6051b593140e06100273af2386f54bc3ca8 GIT binary patch literal 1609 zcmbW1zi%T&6vqcRewm-5fQI5#7N^s$?}U&}CZOyko5bK>N}NEhImWwVd*bzsnVt2; zxeC!-!IcyzQIPVAa0N71X`)Z{F-5Z(aJ9 zp}mRzF8X)q@3{A8@IX5RUk9h4n{yZZ8N3g^1nxh}*o$BnboK-AdGHSS0{9hp1-uJh z0)JZY5$NVW2HyaG18d-)p!2`{93I%C0y;fc;q15t{tkWtz6P#fveWko=WBbC##o?OM@0wfRt-Wk6j#to~9=I2J zUIkq%%~3pENG?`{$XK~;q_T8_n@GokmA6IDh;;DeLqH=PXNe%IXcaMFxBaaFx z%Wcm;%oVZD0ErnJ-8Xzhw|Pn<9wTW}S{rcVQnHx|B$v2~OUSqENhA;E8$#*)2TKj9 zWQy*y&tz-|tZnpxh^$wOy**)4sa2KQ!P=+6W=MPC_D-$UEVWzSwwS<8D{q}!QKkJb z*!CJNZ#|%`R<%*Pa;dKNbRjD2{H+S8ZEA{$nBHa<5+WQEP\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Barcode Printing" +msgstr "Tisk čárových kódů" + +#: .project:2 +msgid "A module to print EAN-13 barcodes. The barcode is constructed from first principles (http://en.wikipedia.org/wiki/European_Article_Number). The barcode can be formatted for screen or printer." +msgstr "Modul pro tisk čárových kódů EAN-13. Čárového kódu je postaven z prvních principů (http://en.wikipedia.org/wiki/European_Article_Number). Čárový kód může být formátovány pro obrazovku nebo tiskárnu." + +#: FMain.class:72 +msgid "Please enter a valid 13 digit EAN-13 number" +msgstr "Zadejte prosím platné 13-ti místné číslo EAN-13" + +#: FMain.class:153 +msgid "" +"This program was made by Charles Guerin and\n" +"modified by Benoît Minisini." +msgstr "" +"Tento program byl vytvořil Charles Guerin a\n" +"upravil Benoît Minisini." + +#: FMain.form:57 +msgid "Barcode" +msgstr "Čárový kód" + +#: FMain.form:62 +msgid "PosX" +msgstr "Pozice X" + +#: FMain.form:67 +msgid "PosY" +msgstr "Pozice Y" + +#: FMain.form:72 +msgid "Height" +msgstr "Výška" + +#: FMain.form:77 +msgid "Width" +msgstr "Šířka" + +#: FMain.form:83 +msgid "Size for printer" +msgstr "Velikost pro tiskárnu" + +#: FMain.form:89 +msgid "Size for screen" +msgstr "Velikost pro obrazovku" + +#: FMain.form:107 +msgid "&Refresh Barcode" +msgstr "&Obnovit čárový kód" + +#: FMain.form:114 +msgid "&Print Barcode" +msgstr "&Tisk čárového kódu" + +#: FMain.form:119 +msgid "About..." +msgstr "O programu..." + +#: FMain.form:124 +msgid "E&xit" +msgstr "&Ukončit" diff --git a/app/examples/Drawing/Barcode/.lang/de.mo b/app/examples/Drawing/Barcode/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..f6a788a45fc8297c5159e245006ec71c28d3b4d2 GIT binary patch literal 1567 zcmbW0JCEB&5XU!(^HQEL0@y{0se~8~DI0DMiW8VR(RWJpf~0dIH^zz_k?W(D!g6^J zyK?6y2vVd7U?5G9GF4z8fZ-JS0O?aCUnT$LNfyqf7u@}k%h{d(%+CIM@xo66?PK&$ z(Vw7Sa__HUKsy0H1W!R{^BDXK{26=)oW3r^+u#gz|L=ouf)ByBz(?R!@G*D+{A0~O zL1+IQ{0RIT?12A)ZvB-vFu+FxbavOU_#zm9|9}(F`ES4&W?~0)`!qo}|22q5j4|8* zr=YX@e$5}iPw@RC=;HkioPy6mr~5JnXYclM->y0RuDy%yV%{{T?>+Pn(6PMA*ZZJr zwKG(s3(09jh(gqdMrlheX`(!4Q6KT#aFM>42xyVVWyWN4T2`iE(D$z2pxN1q!ly~f zn=euYMY%4lDI?1<%}u_bxiW>V7DsBCaY3J@)-Ick25bLV9jYbAO8U7;8t$tRmL^}a z)b|2oRh03)ez};jxpLl7BsI;L=DAr&Te->&6_H`qgttm68(##oytKaWi%o=UQWdw- zi3sZ_%8GqfNovI~V_7hliYR41iO*qg9i zG^rc(_YR!Po89iMOt~Z6+BB)w>-55{zU%(A+HQ`-YPdUHLXYJW+gN5&=`SckA_}%& z9!zd~J6k&^YtF_CHOdpG)uf%7vbD`=Qawhc9kSj|Xf7#3cC`qR(4H&aE&&Tf7D z1wANfz1F8T4trA<$I8T{Wt0;3pb;mW=~t{+N_eKSnC*EFhf-^rub!F8Lkku~lq$0z zTtKt^(XJxH#94_eHkOXrK$S06PjDk#ReLW2-z1{m!WD4g@2{SwELsY;gAz3(RvIs> zXPH$Aqo%#Wa+bOGby?q)Y38i3sVDZ#>gnniRJGH&mU4b_D_pb{7bs#>r7Lh$cnO(w ll\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Barcode Printing" +msgstr "Barcode drucken" + +#: .project:2 +msgid "A module to print EAN-13 barcodes. The barcode is constructed from first principles (http://en.wikipedia.org/wiki/European_Article_Number). The barcode can be formatted for screen or printer." +msgstr "Ein Modul, um EAN-13 Barcodes zu drucken. Der Barcode wird nach den Regeln (http://en.wikipedia.org/wiki/European_Article_Number) gebildet. Der Barcode kann für den Bildschirm oder für den Drucker formattiert werden." + +#: FMain.class:72 +msgid "Please enter a valid 13 digit EAN-13 number" +msgstr "Bitte gib eine gültige 13-stellige EAN-13 Zahl ein" + +#: FMain.class:153 +msgid "" +"This program was made by Charles Guerin and\n" +"modified by Benoît Minisini." +msgstr "" +"Dieses Programm wurde von Charles Guerin geschrieben\n" +"und von Benoît Minisini verändert." + +#: FMain.form:57 +msgid "Barcode" +msgstr "-" + +#: FMain.form:62 +msgid "PosX" +msgstr "-" + +#: FMain.form:67 +msgid "PosY" +msgstr "-" + +#: FMain.form:72 +msgid "Height" +msgstr "Höhe" + +#: FMain.form:77 +msgid "Width" +msgstr "Breite" + +#: FMain.form:83 +msgid "Size for printer" +msgstr "Größe für Drucker" + +#: FMain.form:89 +msgid "Size for screen" +msgstr "Größe für Bildschirm" + +#: FMain.form:107 +msgid "&Refresh Barcode" +msgstr "Barcode &neu laden" + +#: FMain.form:114 +msgid "&Print Barcode" +msgstr "Barcode &drucken" + +#: FMain.form:119 +msgid "About..." +msgstr "Über..." + +#: FMain.form:124 +msgid "E&xit" +msgstr "&Beenden" diff --git a/app/examples/Drawing/Barcode/.project b/app/examples/Drawing/Barcode/.project new file mode 100644 index 00000000..5bbad76a --- /dev/null +++ b/app/examples/Drawing/Barcode/.project @@ -0,0 +1,21 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=Barcode Printing +Startup=FMain +Icon=barcode.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Description="A module to print EAN-13 barcodes. The barcode is constructed from first principles (http://en.wikipedia.org/wiki/European_Article_Number). The barcode can be formatted for screen or printer." +Environment="GB_GUI=gb.gtk" +TabSize=2 +Translate=1 +Language=de +Maintainer=charles +Address=c_guerin@o2.co.uk +License=General Public Licence +Packager=1 +Systems=ubuntu +Menus=ubuntu:"Apps/Programming" +Groups=ubuntu:"Development/Databases" diff --git a/app/examples/Drawing/Barcode/.src/FMain.class b/app/examples/Drawing/Barcode/.src/FMain.class new file mode 100644 index 00000000..9207a99b --- /dev/null +++ b/app/examples/Drawing/Barcode/.src/FMain.class @@ -0,0 +1,161 @@ +' Gambas class file + +Public bcHeight As Integer +Public barThickness As Integer +'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +Public Sub SizeForScreen() + 'set up typical defaults for barcode size on screen + txtPosX.Text = "60" + txtPosY.text = "8" + txtHeight.Text = "150" + txtWidth.Text = "5" +End +'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +Public Sub SizeForPrinter() + 'set up typical defaults for barcode size on printer + txtPosX.Text = "500" + txtPosY.text = "400" + txtHeight.Text = "250" + txtWidth.Text = "7" +End +'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +Public Sub ChkInput() + + ' make sure only valid characters + Select Key.Code + Case Key.BackSpace, Key.Tab, Key.Delete, Key.Enter, Key.Return, Key.Escape + Default + If Key.Text And If InStr("0123456789", Key.Text) = 0 Then Stop Event + End Select + +End +'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + + +Public Sub Form_Open() + 'DrawingArea1.Cached = TRUE + txtBarcode.SetFocus + SizeForScreen + txtBarcode.Text = "8711577011208" 'sample barcode (so what's it for then?) + txtBarcode.Select +End +'------------------------------------------------------------------- +Public Sub btnExit_Click() + + FMain.Close +End + +Private Sub DrawBarCode(Optional bPrinter As Boolean) + + If Not bPrinter Then + + 'DrawingArea1.Refresh + 'Draw.Begin(DrawingArea1) + Draw.Clear + modCrBcode.PrintBarcode(txtBarcode.Text, Val(txtPosX.text), Val(txtPosY.text), Val(txtHeight.text), Val(txtWidth.text), True) + 'Draw.End() + + Else + + Draw.Begin(printer) + modCrBcode.PrintBarcode(txtBarcode.Text, Val(txtPosX.text), Val(txtPosY.text), Val(txtHeight.text), Val(txtWidth.text), False) + Draw.End() + + Endif + +End + + +Public Sub btnRun_Click() + + If Len(txtBarcode.Text) <> 13 Then + Message.Error(("Please enter a valid 13 digit EAN-13 number")) + Else + If Val(txtPosX.Text) = 0 Then + If rbutScreen Then + txtPosX.Text = "40" + Else + txtPosX.Text = "500" + Endif + Endif + If Val(txtPosY.Text) = 0 Then + If rbutScreen Then + txtPosY.Text = "0" + Else + txtPosY.Text = "300" + Endif + Endif + If Val(txtHeight.Text) = 0 Then + If rbutScreen Then + txtHeight.Text = "150" + Else + txtHeight.Text = "250" + Endif + Endif + If Val(txtWidth.Text) = 0 Then + If rbutScreen Then + txtWidth.Text = "5" + Else + txtWidth.Text = "7" + Endif + Endif + + 'DrawBarCode + DrawingArea1.Refresh + + Endif + +End +'------------------------------------------------------------------- +Public Sub rbutPrinter_Click() + SizeForPrinter +End +'------------------------------------------------------------------- +Public Sub rbutScreen_Click() + SizeForScreen +End +'------------------------------------------------------------------- +Public Sub txtPosX_KeyPress() + ChkInput +End +'------------------------------------------------------------------- +Public Sub txtBarcode_KeyPress() + ChkInput +End +'------------------------------------------------------------------- +Public Sub txtPosY_KeyPress() + ChkInput +End +'------------------------------------------------------------------- +Public Sub txtHeight_KeyPress() + ChkInput +End +'------------------------------------------------------------------- +Public Sub txtWidth_KeyPress() + ChkInput +End +'------------------------------------------------------------------- + +' PUBLIC SUB Form_Resize() +' +' DrawBarCode +' +' END + +Public Sub btnPrint_Click() + + DrawBarCode(True) + +End + +Public Sub btnAbout_Click() + + Message.Info(("This program was made by Charles Guerin and\nmodified by Benoît Minisini.")) + +End + +Public Sub DrawingArea1_Draw() + + DrawBarCode + +End diff --git a/app/examples/Drawing/Barcode/.src/FMain.form b/app/examples/Drawing/Barcode/.src/FMain.form new file mode 100644 index 00000000..04aeb601 --- /dev/null +++ b/app/examples/Drawing/Barcode/.src/FMain.form @@ -0,0 +1,88 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,106,62) + Icon = Picture["barcode.png"] + Arrangement = Arrange.Vertical + Spacing = True + Margin = True + Padding = 8 + { Panel1 Panel + MoveScaled(1,1,95,8) + { txtBarcode TextBox + MoveScaled(0,4,24,4) + } + { txtPosX TextBox + MoveScaled(25,4,8,4) + } + { txtPosY TextBox + MoveScaled(34,4,8,4) + } + { txtHeight TextBox + MoveScaled(43,4,8,4) + } + { txtWidth TextBox + MoveScaled(52,4,8,4) + } + { Label1 Label + MoveScaled(0,0,18,4) + Text = ("Barcode") + } + { Label2 Label + MoveScaled(25,0,7,4) + Text = ("PosX") + } + { Label3 Label + MoveScaled(34,0,6,4) + Text = ("PosY") + } + { Label4 Label + MoveScaled(43,0,8,4) + Text = ("Height") + } + { Label5 Label + MoveScaled(52,0,8,4) + Text = ("Width") + } + { rbutPrinter RadioButton + MoveScaled(63,3,27,4) + Visible = False + Text = ("Size for printer") + } + { rbutScreen RadioButton + MoveScaled(63,0,27,4) + Visible = False + Text = ("Size for screen") + Value = True + } + } + { DrawingArea1 DrawingArea + MoveScaled(1,10,104,45) + Background = Color.TextBackground + Expand = True + Border = Border.Sunken + } + { HBox1 HBox + MoveScaled(1,56,69,5) + Spacing = True + { btnRun Button + MoveScaled(0,0,19,4) + Text = ("&Refresh Barcode") + Default = True + } + { btnPrint Button + MoveScaled(20,0,19,4) + Visible = False + Text = ("&Print Barcode") + } + { btnAbout Button + MoveScaled(40,0,11,4) + Text = ("About...") + } + { btnExit Button + MoveScaled(52,0,11,4) + Text = ("E&xit") + Cancel = True + } + } +} diff --git a/app/examples/Drawing/Barcode/.src/modCrBcode.module b/app/examples/Drawing/Barcode/.src/modCrBcode.module new file mode 100644 index 00000000..453a0e5a --- /dev/null +++ b/app/examples/Drawing/Barcode/.src/modCrBcode.module @@ -0,0 +1,98 @@ +' Gambas module file + +Public Sub PrintBarcode(bcode As String, posX As Integer, posY As Integer, bcHeight As Integer, barThickness As Integer, SorP As Boolean) + Dim structure As New String[10] + 'DIM enc AS String[10, 3]. I did it the following way 'cos I get not get the prg to read a multi-dimension array! + Dim enc1 As New String[10] + Dim enc2 As New String[10] + Dim enc3 As New String[10] + Dim fontSizeScreen As New Integer[10] + Dim fontSizePrinter As New Integer[10] + + Dim first6 As String ' encoding for first 6: l-code(1), g-code(2) + Dim j As Integer + Dim l As Integer + Dim k As Integer + Dim frst As Integer 'the first digit + Dim num As Integer 'each digit in turn + Dim bars As String ' the bars for the digit + 'Dim thkns As Integer 'thickness of each bar + Dim LinePos As Integer 'used to horizontaly locate the next bar + + ' + structure = ["111111", "112122", "112212", "112221", "121122", "122112", "122211", "121212", "121221", "122121"] + enc1 = ["0001101", "0011001", "0010011", "0111101", "0100011", "0110001", "0101111", "0111011", "0110111", "0001011"] + enc2 = ["0100111", "0110011", "0011011", "0100001", "0011101", "0111001", "0000101", "0010001", "0001001", "0010111"] + enc3 = ["1110010", "1100110", "1101100", "1000010", "1011100", "1001110", "1010000", "1000100", "1001000", "1110100"] + fontSizeScreen = [6, 9, 12, 16, 20, 24, 26, 28, 28, 30] + fontSizePrinter = [2, 2, 2, 4, 4, 6, 6, 8, 8, 10] + + frst = Val(Left(bcode, 1)) + first6 = structure[frst] + + 'draw 1st 2 deep bars----------------------------------------------- + For l = 1 To barThickness + Draw.Line(l + posX, 1 + posY, l + posX, bcHeight * 1.2 + posY) + Next + LinePos = LinePos + barThickness * 2 'allows for following gap + For l = 1 To barThickness + Draw.Line(LinePos + l + posX, 1 + posY, LinePos + l + posX, bcHeight * 1.2 + posY) + Next + LinePos = LinePos + barThickness + '--------------------------------------------------------------- + For j = 2 To 13 'loop through each of the 12 digits + num = Val(Mid$(bcode, j, 1)) 'find the individual number IN the first 6 + If j < 8 Then 'different for first 6 + If Val(Mid(first6, j - 1, 1)) = 1 Then 'find the bars making up that number (L,G or R) + bars = enc1[num] + Else + bars = enc2[num] + Endif + Else + bars = enc3[num] + Endif + + 'put the 2 middle deep bars---------------------------------------- + If j = 8 Then + LinePos = LinePos + barThickness + For l = 1 To barThickness + Draw.Line(linepos + l + posX, 1 + posY, linepos + l + posX, bcHeight * 1.2 + posY) + Next + LinePos = LinePos + barThickness * 2 'allows for following gap + For l = 1 To barThickness + Draw.Line(LinePos + l + posX, 1 + posY, LinePos + l + posX, bcHeight * 1.2 + posY) + Next + LinePos = LinePos + barThickness * 2 'allows for following gap + Endif + '--------------------------------------------------------------- + For k = 1 To 7 'draw bars for single digit + If Mid(bars, k, 1) = "1" Then + For l = 1 To barThickness + Draw.Line(linepos + l + posX, 1 + posY, LinePos + l + posX, bcHeight + posY) + Next + Endif + linepos = LinePos + barThickness + Next + Next + + 'draw last 2 deep bars------------------------------------------------ + For l = 1 To barThickness + Draw.Line(linepos + l + posX, 1 + posY, linepos + l + posX, bcHeight * 1.2 + posY) + Next + LinePos = LinePos + barThickness * 2 'allows for following gap + For l = 1 To barThickness + Draw.Line(LinePos + l + posX, 1 + posY, LinePos + l + posX, bcHeight * 1.2 + posY) + Next + '--------------------------------------------------------------- + 'write the barcode text + If SorP Then + Draw.Font.Size = fontSizeScreen[barThickness - 1] + Else + Draw.Font.Size = fontSizePrinter[barThickness - 1] + Endif + Draw.Text(Left(bcode, 1), posX - barThickness * 6, posY + bcHeight * 1.02) + Draw.Text(Mid(bcode, 2, 6), posX + barThickness * 9, posY + bcHeight * 1.02) + Draw.Text(Right(bcode, 6), posX + barThickness * 58, posY + bcHeight * 1.02) + + +End diff --git a/app/examples/Drawing/Barcode/barcode.png b/app/examples/Drawing/Barcode/barcode.png new file mode 100644 index 0000000000000000000000000000000000000000..a62216848a8334bb7eddc90a77d633afcbfa8ccb GIT binary patch literal 765 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Chart/.lang/ca.mo b/app/examples/Drawing/Chart/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..bbe183bdf60adad6e63c295ff57550e5c49b5cc4 GIT binary patch literal 977 zcmYk(J!}&(6bEnvNH{8yg!Nf5$Gx(#!8&Z28%KKSoDhF^ofqV~BOc5yZJI_`!Gz zcfe=C7jPHy*KiwLgZtomnEwEGBL5ijPr)z2ufcD@@4=tJU%}t-DE9dS32}IAzX*$!nWTm%QC-$^{Q8hp5!el`UypIvvV_>wc~Onn%2rng-urnsA}bk zJwM1qs-~!e=gFn5-Dx%Wx zGoRO%(hlCd>wD&KJ%g@i!1WBco&nc0;CcpJ&w%S0a6S9CGj2wO+9=>^wS_S, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Chart\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-16 23:34+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FormChart.class:129 +msgid "Example to make a Bar Chart" +msgstr "Exemple de fer un gràfic de barres" + +#: FormChart.class:146 FormData.class:172 +msgid "&Close" +msgstr "&Tanca" + +#: FormChart.class:151 +msgid "&About" +msgstr "&Quant a" + +#: FormChart.class:158 +msgid "(%)" +msgstr "-" + +#: FormData.class:62 +msgid "Chart example" +msgstr "Exemple de gràfic" + +#: FormData.class:67 +msgid "Data 1" +msgstr "Dada 1" + +#: FormData.class:72 +msgid "Data 2" +msgstr "Dada 2" + +#: FormData.class:77 +msgid "Data 3" +msgstr "Dada 3" + +#: FormData.class:82 +msgid "Data 4" +msgstr "Dada 4" + +#: FormData.class:87 +msgid "Data 5" +msgstr "Dada 5" + +#: FormData.class:92 +msgid "Data 6" +msgstr "Dada 6" + +#: FormData.class:97 +msgid "Data 7" +msgstr "Dada 7" + +#: FormData.class:102 +msgid "Data 8" +msgstr "Dada 8" + +#: FormData.class:107 +msgid "Data 9" +msgstr "Dada 9" + +#: FormData.class:112 +msgid "Data 10" +msgstr "Dada 10" + +#: FormData.class:167 +msgid "&Draw it" +msgstr "&Dibuixa'l" + +#~ msgid "Bar Chart" +#~ msgstr "Gràfic de barres" diff --git a/app/examples/Drawing/Chart/.lang/cs.mo b/app/examples/Drawing/Chart/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..6264a99f9cb883bef006bc5592d5630d9a5fda5b GIT binary patch literal 1002 zcmZ9}zi-n(6bJBwmR~79%Y*>w76PbpXn{g%0;);e64i;LB&|djxF(mx)bU03CA2f* zPv}rFF(6c-3L!vDAW*2p#>#{knEMwn@V)q|AYSzR@i{N|Ud}(qM>h!KH1Zts9r8TV z*@p|_Gdu{tXurY3Sbv9y;14(gf59X0H#`P+q59n)sOm=c$Llfexb}>eYA{NU-MvcmTH#(OmUyK-H%M6L1Zpj68uS;8X2}e*QxHQoE&nrQO!P*1plc)xL-7 z|9yZH@RNRy+d+S&3N@RWQ;n_8A-b%n&=?j!Hq>Y?V_+hYd@ic*JYc$k&Y}9!8!BOj@$C3)Z|%m+W#S z?-mm!*C_Lzr@k~W5;y66+bSCc*T`A)o@*BJ6U%PVPB<)*Mm1zX+NjzxA9&ULLT`ZNf!IhTcRF(eMSsY@kO^&6TSXN+6-BPlvM4i&+3C^7Zv}1 e2Cr`BpTdCA-n#6GolPF}w`sJu<5o-PP5uHYM9Mk< literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Chart/.lang/cs.po b/app/examples/Drawing/Chart/.lang/cs.po new file mode 100644 index 00000000..b785f3f3 --- /dev/null +++ b/app/examples/Drawing/Chart/.lang/cs.po @@ -0,0 +1,84 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Bar Chart" +msgstr "Sloupcový graf" + +#: FormChart.form:13 +msgid "Example to make a Bar Chart" +msgstr "Příklad na vytvoření sloupcového grafu" + +#: FormChart.form:30 FormData.form:130 +msgid "&Close" +msgstr "&Zavřít" + +#: FormChart.form:35 +msgid "&About" +msgstr "&O..." + +#: FormChart.form:42 +msgid "(%)" +msgstr "-" + +#: FormData.form:30 +msgid "Chart example" +msgstr "Příklad grafu" + +#: FormData.form:35 +msgid "Data 1" +msgstr "-" + +#: FormData.form:40 +msgid "Data 2" +msgstr "-" + +#: FormData.form:45 +msgid "Data 3" +msgstr "-" + +#: FormData.form:50 +msgid "Data 4" +msgstr "-" + +#: FormData.form:55 +msgid "Data 5" +msgstr "-" + +#: FormData.form:60 +msgid "Data 6" +msgstr "-" + +#: FormData.form:65 +msgid "Data 7" +msgstr "-" + +#: FormData.form:70 +msgid "Data 8" +msgstr "-" + +#: FormData.form:75 +msgid "Data 9" +msgstr "Data 9" + +#: FormData.form:80 +msgid "Data 10" +msgstr "-" + +#: FormData.form:125 +msgid "&Draw it" +msgstr "&Kresli" + +#: FormData.form:135 +msgid "&Random" +msgstr "&Náhodně" diff --git a/app/examples/Drawing/Chart/.lang/de.mo b/app/examples/Drawing/Chart/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..49cc21e76de33a654bb8f8e0bca76542036aafcc GIT binary patch literal 1008 zcmY+>zi$&U6bEn@3Y6nVp#uw4@&FO2x|04*kAiy5UE7FDQYCE>WpWZXF}>VHj$6uq zz{Z4F5KL?xKr9F$A=IS<5*s5E8zUPNU)PQzp7icx-#LCS_Ro>Q7X&eayoNkLjv^NZ z@IbtSr{R0+M>vG}7kCmL!L#riya0bdz5W+G1Ap83AM0Q1zzMT%$U1DjVjZ%=^9<^Kp4<6L>uc*<>w)!y^^^6h^$1ez z^BwAXI&}SB=(ExFbi3z~`b=~`y7ncc?r9j=3&#%hf9VCbCWA&h)O+)XaKB27HH=I8JGDvQt$sJn9L3X*iyRp9P=KAY&TiaZ^VNy>UB58UwR};!1nZs8#E=i_ZZ^eQizTpYU zY;jR\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Bar Chart" +msgstr "Balkendiagramm" + +#: FormChart.form:13 +msgid "Example to make a Bar Chart" +msgstr "Beispiel für ein Balkendiagramm" + +#: FormChart.form:30 FormData.form:139 +msgid "&Close" +msgstr "&Schließen" + +#: FormChart.form:35 +msgid "&About" +msgstr "&Über" + +#: FormChart.form:42 +msgid "(%)" +msgstr "-" + +#: FormData.form:29 +msgid "Chart example" +msgstr "Diagramm-Beispiel" + +#: FormData.form:34 +msgid "Data 1" +msgstr "Wert 1" + +#: FormData.form:39 +msgid "Data 2" +msgstr "Wert 2" + +#: FormData.form:44 +msgid "Data 3" +msgstr "Wert 3" + +#: FormData.form:49 +msgid "Data 4" +msgstr "Wert 4" + +#: FormData.form:54 +msgid "Data 5" +msgstr "Wert 5" + +#: FormData.form:59 +msgid "Data 6" +msgstr "Wert 6" + +#: FormData.form:64 +msgid "Data 7" +msgstr "Wert 7" + +#: FormData.form:69 +msgid "Data 8" +msgstr "Wert 8" + +#: FormData.form:74 +msgid "Data 9" +msgstr "Wert 9" + +#: FormData.form:79 +msgid "Data 10" +msgstr "Wert 10" + +#: FormData.form:134 +msgid "&Draw it" +msgstr "&Zeichnen" + diff --git a/app/examples/Drawing/Chart/.lang/es.mo b/app/examples/Drawing/Chart/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..59c0ff07235135a235a5c1c4958dcf24d0683902 GIT binary patch literal 946 zcmYk(yKfUQ90%|N$}7APj{$^!3sI|brR5opQuUf#OT^_t9u=@4CvnmXmviKEpooo$ zZY+our2YkLjEIGejSVI?HY5f%zV=xvmVWnT#3pw6SVQD;$?cHx8Z z3hskj*4OX==5OI1_zoU{+xGtlxF7S6cK*rw#roCy&HCN?)B4N$8=k{Hf8if^dbi`` z;2|8U_df&m`~uW{%;0khx={DK0`p`Pgu2pmOn%m${BmL7wOWdP}f!}Z|HMiu`-I`Y^&pxVE zgIvf`<+dbC(wHeBJz8ytAyxfKK#M`eFNe#1sniIX%_S^N1`{^mZi};#N1r$1#hN+tkIorCi-g?\n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FormChart.class:129 +msgid "Example to make a Bar Chart" +msgstr "Ejemplo para hacer un Gráfico de Barras" + +#: FormChart.class:146 FormData.class:172 +msgid "&Close" +msgstr "&Cerrar" + +#: FormChart.class:151 +msgid "&About" +msgstr "&Acerca de" + +#: FormChart.class:158 +msgid "(%)" +msgstr "(%)" + +#: FormData.class:62 +msgid "Chart example" +msgstr "Ejemplo de gráficos" + +#: FormData.class:67 +msgid "Data 1" +msgstr "Dato 1" + +#: FormData.class:72 +msgid "Data 2" +msgstr "Dato 2" + +#: FormData.class:77 +msgid "Data 3" +msgstr "Dato 3" + +#: FormData.class:82 +msgid "Data 4" +msgstr "Dato 4" + +#: FormData.class:87 +msgid "Data 5" +msgstr "Dato 5" + +#: FormData.class:92 +msgid "Data 6" +msgstr "Dato 6" + +#: FormData.class:97 +msgid "Data 7" +msgstr "Dato 7" + +#: FormData.class:102 +msgid "Data 8" +msgstr "Dato 8" + +#: FormData.class:107 +msgid "Data 9" +msgstr "Dato 9" + +#: FormData.class:112 +msgid "Data 10" +msgstr "Dato 10" + +#: FormData.class:167 +msgid "&Draw it" +msgstr "&Dibujarlo" + +#~ msgid "Bar Chart" +#~ msgstr "Gráfico de barras" diff --git a/app/examples/Drawing/Chart/.project b/app/examples/Drawing/Chart/.project new file mode 100644 index 00000000..a1c1a304 --- /dev/null +++ b/app/examples/Drawing/Chart/.project @@ -0,0 +1,16 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.0 +Title=Bar Chart +Startup=FormData +Icon=graph.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +TabSize=2 +Translate=1 +Language=fr +Maintainer=fabien +Vendor=Princeton +Address=fabien@arcalis +License=General Public Licence diff --git a/app/examples/Drawing/Chart/.src/FormChart.class b/app/examples/Drawing/Chart/.src/FormChart.class new file mode 100644 index 00000000..3947795f --- /dev/null +++ b/app/examples/Drawing/Chart/.src/FormChart.class @@ -0,0 +1,112 @@ +' Gambas class file + +' ====================================== +' This example is to make a bar chart +' using DrawingArea +' may be it can help you to make a chart +' if you have any question you can send to +' yudi@kecoak.or.id +' Thank You + + +Public total As Integer +Public value As Float[] + +Public Sub btnClose_Click() + + Me.Close + +End + +Public Sub _new() + + Me.Center + +End + + +Public Sub Form_Open() + + Draw_Chart + +End + +Public Sub Draw_Chart() + + Dim i As Integer + + Dim skala_1 As Integer + Dim skala_2 As Integer + + Dim distance_x As Float + Dim distance_y As Float + + Dim width_draw As Integer + Dim tot As Integer + Dim colors As Integer[] + Dim bottom As Integer + Dim sumdata As Integer + + colors = New Integer[] + colors.Resize(total + 1) + + For i = 1 To total + sumdata = sumdata + value[i] + Next + + If sumdata = 0 Then sumdata = 1 + + For i = 1 To total + value[i] = (value[i] / sumdata) * 10 + Next + + drwchart.Clear + + draw.Begin(drwchart) + + skala_1 = drwchart.ClientH / 11 + distance_y = drwchart.ClientH - skala_1 + bottom = distance_y + 8 + + For i = 0 To 100 Step 10 + draw.Foreground = color.black + draw.Text(i, 0, distance_y) + draw.Line(25, distance_y + 8, drwchart.clientw, distance_y + 8) + distance_y = distance_y - skala_1 + Next + + draw.Line(30, 0, 30, drwchart.ClientH) + + skala_2 = (drwchart.ClientW - 30) \ 10 + distance_x = skala_2 + 30 + width_draw = skala_2 / 2 + + For i = 1 To Total + draw.LineWidth = 1 + draw.Foreground = color.Black + draw.Text(i, distance_x - (width_draw / 2) - 16, drwchart.ClientH - drwChart.Font.Height, 32, drwChart.Font.Height, Align.Center) + draw.Line(distance_x - (width_draw / 2), 0, distance_x - (width_draw / 2), drwchart.ClientH - skala_1 + 8) + draw.LineWidth = width_draw + draw.Foreground = color.RGB(i * 100, i * 10, i * 50) + colors[i] = draw.Foreground + tot = skala_1 * value[i] + skala_1 - 8 + draw.Line(distance_x - (width_draw / 2), bottom, distance_x - (width_draw / 2), drwchart.ClientH - tot) + distance_x = distance_x + skala_2 + Next + + Draw.End + +End + +Public Sub btnAbout_Click() + + Dim i As String + i = "

Example to make bar chart

\n" + i = i & "This example has made by : " & Chr(10) + i = i & " Yudi Astira" & Chr(10) + i = i & " yudi@kecoak.or.id" & Chr(10) + i = i & " necrose #hdteam on Dal.Net" & Chr(10) + i = i & "Thank You" + message.Info(i, "&Close") + +End diff --git a/app/examples/Drawing/Chart/.src/FormChart.form b/app/examples/Drawing/Chart/.src/FormChart.form new file mode 100644 index 00000000..0720a835 --- /dev/null +++ b/app/examples/Drawing/Chart/.src/FormChart.form @@ -0,0 +1,32 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,72,51) + Background = &HFFEFBF& + Text = ("Example to make a Bar Chart") + Resizable = False + { bg PictureBox + MoveScaled(1,1,70.1667,43.1667) + Background = &HFFFFFF& + Border = Border.Plain + } + { drwChart DrawingArea + MoveScaled(2,2,69,41) + Background = &HFFFFFF& + Cached = True + } + { btnClose Button + MoveScaled(56,45,15,5) + Text = ("&Close") + } + { btnAbout Button + MoveScaled(39.6667,45,15,5) + Text = ("&About") + Picture = Picture["graph.png"] + } + { TextLabel1 TextLabel + MoveScaled(2,2,4,3.5) + Background = &HFFFFFF& + Text = ("(%)") + } +} diff --git a/app/examples/Drawing/Chart/.src/FormData.class b/app/examples/Drawing/Chart/.src/FormData.class new file mode 100644 index 00000000..971cb001 --- /dev/null +++ b/app/examples/Drawing/Chart/.src/FormData.class @@ -0,0 +1,43 @@ +' Gambas class file + +Public Sub btnClose_Click() + + Me.Close + +End + +Public Sub btnDraw_Click() + Dim total As Integer + total = 10 + With FormChart + .value = New Float[] + .total = total + .value.Resize(total + 1) + Try .value[1] = Val(textbox1.Text) + Try .value[2] = Val(textbox2.Text) + Try .value[3] = Val(textbox3.Text) + Try .value[4] = Val(textbox4.Text) + Try .value[5] = Val(textbox5.Text) + Try .value[6] = Val(textbox6.Text) + Try .value[7] = Val(textbox7.Text) + Try .value[8] = Val(textbox8.Text) + Try .value[9] = Val(textbox9.Text) + Try .value[10] = Val(textbox10.Text) + .Show + End With + +End + +Public Sub btnRandom_Click() + + Dim hCtrl As Control + + For Each hCtrl In Me.Children + + If hCtrl Is TextBox Then + TextBox(hCtrl).Text = CStr(Int(Exp(Rnd(Log(2), 6)))) + Endif + + Next + +End diff --git a/app/examples/Drawing/Chart/.src/FormData.form b/app/examples/Drawing/Chart/.src/FormData.form new file mode 100644 index 00000000..3a003188 --- /dev/null +++ b/app/examples/Drawing/Chart/.src/FormData.form @@ -0,0 +1,89 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(29.7143,16.1429,50,53) + Text = ("Chart example") + Resizable = False + { TextLabel1 TextLabel + MoveScaled(3,2,6.7143,3.1429) + Text = ("Data 1") + } + { TextLabel2 TextLabel + MoveScaled(3,7,6.7143,3.1429) + Text = ("Data 2") + } + { TextLabel3 TextLabel + MoveScaled(3,12,6.7143,3.1429) + Text = ("Data 3") + } + { TextLabel4 TextLabel + MoveScaled(3,17,6.7143,3.1429) + Text = ("Data 4") + } + { TextLabel5 TextLabel + MoveScaled(3,22,6.7143,3.1429) + Text = ("Data 5") + } + { TextLabel6 TextLabel + MoveScaled(3,27,6.7143,3.1429) + Text = ("Data 6") + } + { TextLabel7 TextLabel + MoveScaled(3,32,6.7143,3.1429) + Text = ("Data 7") + } + { TextLabel8 TextLabel + MoveScaled(3,37,6.7143,3.1429) + Text = ("Data 8") + } + { TextLabel9 TextLabel + MoveScaled(3,42,6.7143,3.1429) + Text = ("Data 9") + } + { TextLabel10 TextLabel + MoveScaled(3,47,9,3) + Text = ("Data 10") + } + { TextBox1 TextBox + MoveScaled(13,2,14,4) + } + { TextBox2 TextBox + MoveScaled(13,7,14,4) + } + { TextBox3 TextBox + MoveScaled(13,12,14,4) + } + { TextBox4 TextBox + MoveScaled(13,17,14,4) + } + { TextBox5 TextBox + MoveScaled(13,22,14,4) + } + { TextBox6 TextBox + MoveScaled(13,27,14,4) + } + { TextBox7 TextBox + MoveScaled(13,32,14,4) + } + { TextBox8 TextBox + MoveScaled(13,37,14,4) + } + { TextBox9 TextBox + MoveScaled(13,42,14,4) + } + { TextBox10 TextBox + MoveScaled(13,47,14,4) + } + { btnDraw Button + MoveScaled(30,2,19,4) + Text = ("&Draw it") + } + { btnClose Button + MoveScaled(30,12,19,4) + Text = ("&Close") + } + { btnRandom Button + MoveScaled(30,7,19,4) + Text = ("&Random") + } +} diff --git a/app/examples/Drawing/Chart/graph.png b/app/examples/Drawing/Chart/graph.png new file mode 100644 index 0000000000000000000000000000000000000000..dfa033e005c085fcbae902f0900eb747abae34f4 GIT binary patch literal 225 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv=>VS)S0MfW|9>EP;J^Ve<3B?K z1Calp;s236#?e3trjj7PUA|@^hjBG|MB2#)B4;(#u zQ-X&_LCeL-**Rd#1EGS#LKi^+PjByoN(@_AGh8z=3@&tS*^)9rSLx|fRgI_u&K)~; zEZ{0B*^(g6a4RS-j%Ul##fv4cip_~SA+TVwu9t>GBcrg!0wo>>hC9aP7QZ8UCj+f# N@O1TaS?83{1OT}0NqhhR literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Clock/.directory b/app/examples/Drawing/Clock/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Drawing/Clock/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Drawing/Clock/.icon.png b/app/examples/Drawing/Clock/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..273c0703b97d94a5a105c6a5c20f9824f26e6697 GIT binary patch literal 4367 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 JPDHLkV1i|-R1^RJ literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Clock/.icon/16.png b/app/examples/Drawing/Clock/.icon/16.png new file mode 100644 index 0000000000000000000000000000000000000000..b298e08375a4526c31c5182fa0986fc20aeb0dba GIT binary patch literal 949 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 QLjV8(07*qoM6N<$f~@Ra7ytkO literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Clock/.lang/ca.mo b/app/examples/Drawing/Clock/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..d99976911e4feb7e139b401ead7f7a29fa86eb6b GIT binary patch literal 860 zcmZ9J!EO^V5QYsD3JXZkD;%bmUXVjJiBPp^5TP`ph(Ow;z=4ai>m*wod*$`g265uT z10YVk3Xg(#1CHGJz1cJpBTc@U+3`Pi=J(ddcLuu!zYqTmzYG5b_ePf(+X1)1tKf6+ zHu$Q_--DaT#}!X2+KQjR4SfFs-UPo^^>3ip{sFI}{`2xu=MU)T{{^pun^zbMz&jQ1 zRotl1fy, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Clock\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-16 23:34+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FClock.class:224 +msgid "About" +msgstr "Quant a" + +#: .project:2 +msgid "A clock example made by Fabien Bodard." +msgstr "Un exemple de rellotge fet per en Fabien Bodard." + +#: FClock.class:212 +msgid "Always on top" +msgstr "Sempre per damunt" + +#: FClock.class:179 +msgid "central" +msgstr "central" + +#: .project:1 +msgid "Clock" +msgstr "Rellotge" + +#: FClock.class:184 +msgid "Clock 1" +msgstr "Rellotge 1" + +#: FClock.class:190 +msgid "Clock 2" +msgstr "Rellotge 2" + +#: FClock.class:196 +msgid "Clock 3" +msgstr "Rellotge 3" + +#: FClock.class:203 +msgid "Clock 4" +msgstr "Rellotge 4" + +#: FClock.class:232 +msgid "Quit" +msgstr "Surt" + +#: FClock.class:216 +msgid "Show Window" +msgstr "Mostra la finestra" + diff --git a/app/examples/Drawing/Clock/.lang/cs.mo b/app/examples/Drawing/Clock/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..b81a191f4de2d9c99d724d7e9d15dd50ea39b746 GIT binary patch literal 803 zcmZ9JJ8u&~5XTo1UPmZMLqjo8(4aNsQA9^TIqY+6apyCBArQ^_Zm};u?^^3Mju90N z6&0e0py31LD}=(h;S*3GXlZGwn2p!6#7O`B?PF$VX7}g%>IZ_hhPsY=jk=2ZfiebX z2)Tv4fjoySA+I1ScKsZA0oQHI150K33b~5sZ;+RfZ|(kjBtd;d-NfmqvxGRvFGzF$ zH{^NbcjQInFU#MSe=Pr6uB^=C*N`U9HKa*lVi#t5T$ssa4BbxqGBx5=z4>g6AK`#!Y%TD=@qoN7dC z{AFqmX@P0(!c*U?(J-O~4@!|6mTy0cD!vo2p{5PR#4ux8Di?~)Ab^S&`mpPVUOCwF z3Wb_quRlOn(HXH0rwz{fE(Bg>ziCqL2EksI=A`N@eOz$Da_BGX+sNH@@=|DQfi@-s ztVi=heP@udRNRBO%hZtT`^`p??kxRGSuePvzKCU#ijE6A?NmFYIy;&k_cE41*X&O~ zCjUQs1Op{IiuFgNH9JWrAXrz\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Clock" +msgstr "Hodiny" + +#: .project:2 +msgid "A clock example made by Fabien Bodard." +msgstr "Příklad hodiny od Fabien Bodard." + +#: FClock.form:25 +msgid "central" +msgstr "centrální" + +#: FClock.form:30 +msgid "Clock 1" +msgstr "Hodiny 1" + +#: FClock.form:36 +msgid "Clock 2" +msgstr "Hodiny 2" + +#: FClock.form:42 +msgid "Clock 3" +msgstr "Hodiny 3" + +#: FClock.form:49 +msgid "Clock 4" +msgstr "Hodiny 4" + +#: FClock.form:57 +msgid "Always on top" +msgstr "Vždy nahoře" + +#: FClock.form:62 +msgid "Show Window" +msgstr "Ukaž okno" + +#: FClock.form:70 +msgid "About" +msgstr "O programu" + +#: FClock.form:77 +msgid "Quit" +msgstr "Ukončit" diff --git a/app/examples/Drawing/Clock/.lang/de.mo b/app/examples/Drawing/Clock/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..1b8eaa544a18e50ec5b4dea6fd0cc7aeb8287055 GIT binary patch literal 800 zcmZ9JJ8u&~5XUzV9x)FIL_t9@G&JZ8c@)tRP%id4vADC1UkF6A=bP9Y?sl!cbsSq% zG<*O=MLr5$Ix0Q@qNAXqqM~6o*~k(j{pYu{JG(Qx`{VZVCq{OibdU6obc^(zWDS-W zdqBKTyhPk1-XJzy{Dydi;>7X5Q9I6w%e4NUc#ZhM&3_~^(r1!K!{ZBQ=YJ*Mq4*o| zBJro2|K<4G@sHzQqOHF~$v25BL>u2F+7eeu=VtGCZY4LcHSE1?k8~7UAzdcfUhO$7 zfI+4PZvl^ZKF$zwo+2bOsPROggdLS~ot9aUsEJ`gHs!Mdlmw&3tYSBwd02H^b6j`a zU@s=Zu+B(L;iZtNnz8{(qj|lPc=golqb`J!KD2_$^I$iGe%S8Rn~hSd>9z4t z*hR0(4f^mZ4BB4Y^r`{uHT}5$@O85hmLgsludBH%GH#Uiq1KBcXasQxTVWj3qwSzt zZHJxCGfI`i2_K@@MV|W*1&!UFEx8p%+nFdUJzV7YP>Snuxae=S{J2z6($E#W?rco` z7(6nM#u*p#2@FPD7igaLx;1Zek!Rc5N9~1jpi&`+K5Qn!lvpU}eNhx+ff?+de9?eH zBJ@TY4(W;i`+<|=1U0MYIcg9&^p#Fg5A{T*jCNSjCs@CB>eo;G2HQcDDN0sDSs2\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Clock" +msgstr "Uhr" + +#: .project:2 +msgid "A clock example made by Fabien Bodard." +msgstr "Ein Beispiel für eine Uhr von Fabien Bodard." + +#: FClock.form:25 +msgid "central" +msgstr "zentral" + +#: FClock.form:30 +msgid "Clock 1" +msgstr "Uhr 1" + +#: FClock.form:36 +msgid "Clock 2" +msgstr "Uhr 2" + +#: FClock.form:42 +msgid "Clock 3" +msgstr "Uhr 3" + +#: FClock.form:49 +msgid "Clock 4" +msgstr "Uhr 4" + +#: FClock.form:57 +msgid "Always on top" +msgstr "Immer im Vordergrund" + +#: FClock.form:62 +msgid "Show Window" +msgstr "Fenster zeigen" + +#: FClock.form:70 +msgid "About" +msgstr "Über" + +#: FClock.form:77 +msgid "Quit" +msgstr "Beenden" + diff --git a/app/examples/Drawing/Clock/.lang/es.mo b/app/examples/Drawing/Clock/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..ffe6db87f817bd7849366f5f9ebd4067b8565427 GIT binary patch literal 654 zcmYL_&2H2%5P%J|6oM)Q5;u_M!Ug0)+ZC!pS=HK2OGL6+lHKxis+)Cp-MV(G0>a8oU z2R{jZhF-z@=jaFM7a{)!{TI(&^nG;aHX)Dv8SU#0gFgo!1<%mFe}jIAUWEL2G?u!o zg(CM5OkMW&@9!eM?j6Mc+jX#GGk1ikCBJCFXmF+>J!R@M*bLqZ-VW}Nk8|lrH8V^2 zB=y8BNiDRqT#=zQr=oT=ozO#JTWNF*L)QDi4iY#_$|@}e(XgPUSV(_K``n2bz9g)q zc|rRO-WPG6zWiDYk|^V?qa(|8tGF{ZhW&At!GPrn>?S!&vpv@Dmq}H1@u{B9`Bc!6 z;O8-9Y;Z94EqAkQPst5qr`;$|^W8r zjYs=*=jNTC\n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FClock.class:195 +msgid "central" +msgstr "central" + +#: FClock.class:200 +msgid "Clock 1" +msgstr "Reloj 1" + +#: FClock.class:206 +msgid "Clock 2" +msgstr "Reloj 2" + +#: FClock.class:212 +msgid "Clock 3" +msgstr "Reloj 3" + +#: FClock.class:219 +msgid "Clock 4" +msgstr "Reloj 4" + +#: FClock.class:228 +msgid "Always on top" +msgstr "Siempre al frente" + +#: FClock.class:233 +msgid "Show Window" +msgstr "Mostrar ventana" + +#: FClock.class:242 +msgid "About" +msgstr "Acerca de" + +#: FClock.class:250 +msgid "Quit" +msgstr "Salir" + +#~ msgid "Clock" +#~ msgstr "Reloj" + +#~ msgid "A clock example made by Fabien Bodard." +#~ msgstr "Un ejemplo de reloj hecho por Fabien Bodard." diff --git a/app/examples/Drawing/Clock/.project b/app/examples/Drawing/Clock/.project new file mode 100644 index 00000000..265c5cb9 --- /dev/null +++ b/app/examples/Drawing/Clock/.project @@ -0,0 +1,21 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.8.90 +Title=Clock +Startup=FClock +Icon=img/clock_bg_big4.png +Version=3.8.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Description="A clock example made by Fabien Bodard." +Environment="GB_GUI=gb.qt5" +TabSize=2 +Translate=1 +Language=fr +Maintainer=fabien +Vendor=Princeton +Address=fabien@arcalis +License=General Public Licence +Prefix=1 +Packager=1 +Systems=mandrake diff --git a/app/examples/Drawing/Clock/.src/FClock.class b/app/examples/Drawing/Clock/.src/FClock.class new file mode 100644 index 00000000..f05f081e --- /dev/null +++ b/app/examples/Drawing/Clock/.src/FClock.class @@ -0,0 +1,170 @@ +' Gambas class file + +Private picHour As Image +Private picMinute As Image +Private picSecond As Image +Private Clock As Image +Private Buffer As Picture +Private HE As Integer +Private WI As Integer +'PRIVATE BackPicture AS String +Private $MX As Integer +Private $MY As Integer +Private $hMenu As Menu + + +Public Sub DrawTime() + + Dim tmpImg As Image + 'Dim hPict As Picture + Dim hPict As Image + Dim I As Integer + + WI = Clock.Width + HE = Clock.Height + + ' hPict = Clock.Picture + ' Draw.Begin(hPict) + ' tmpImg = picHour.Rotate(- Hour(Now) * Pi(2 / 12)) + ' Draw.Image(tmpImg, WI / 2 - tmpImg.Width / 2, HE / 2 - tmpImg.Height / 2) + ' tmpImg = picMinute.Rotate(- Minute(Now) * Pi(2 / 60)) + ' Draw.Image(tmpImg, WI / 2 - tmpImg.Width / 2, HE / 2 - tmpImg.Height / 2) + ' tmpImg = picSecond.Rotate(- Second(Now) * Pi(2 / 60)) + ' Draw.Image(tmpImg, WI / 2 - tmpImg.Width / 2, HE / 2 - tmpImg.Height / 2) + ' Draw.End + ' + ' Me.Picture = hPict + + 'hPict = New Image(Clock.W, Clock.H, Color.Transparent) + 'hPict.Draw(Clock, 0, 0) + hPict = Clock.Copy() + + 'Draw.Begin(hPict) + 'tmpImg = picHour.Rotate(- Hour(Now) * Pi(2 / 12)) + tmpImg = picHour.Rotate(- ((Hour(Now) * 60) + Minute(Now)) * Pi(1 / 360)) + hPict.PaintImage(tmpImg, WI / 2 - tmpImg.Width / 2, HE / 2 - tmpImg.Height / 2) + tmpImg = picMinute.Rotate(- Minute(Now) * Pi(2 / 60)) + hPict.PaintImage(tmpImg, WI / 2 - tmpImg.Width / 2, HE / 2 - tmpImg.Height / 2) + tmpImg = picSecond.Rotate(- Second(Now) * Pi(2 / 60)) + hPict.PaintImage(tmpImg, WI / 2 - tmpImg.Width / 2, HE / 2 - tmpImg.Height / 2) + 'Draw.End + + Me.Picture = hPict.Picture + 'Me.Mask = Not mnuShowWindow.Value + +End + +Private Sub SetClock(iClock As Integer) + + Dim hImage As Image + Dim hBuffer As Image + Dim hMenu As Menu + Dim X, Y As Integer + Dim iColor As Integer + Dim iGray As Integer + + hImage = Image.Load("img/clock_bg_big" & iClock & ".png") + + ' hBuffer = NEW Image(hImage.Width + 2, hImage.Height + 2, TRUE) + ' hBuffer.Draw(hImage, 1, 0) + ' hBuffer.Draw(hImage, 1, 2) + ' hBuffer.Draw(hImage, 0, 1) + ' hBuffer.Draw(hImage, 2, 1) + ' + ' FOR X = 0 TO hImage.Width - 1 + ' FOR Y = 0 TO hImage.Height - 1 + ' iColor = hBuffer[X, Y] + ' iGray = &H80 'Color[iColor].Value + ' iColor = Color.RGB(iGray, iGray, iGray, Color[iColor].Alpha) + ' hBuffer[X, Y] = iColor + ' NEXT + ' NEXT + ' + ' hBuffer.Draw(hImage, 1, 1) + ' hImage = hBuffer + + Clock = hImage '.Picture + DrawTime + + For Each hMenu In mnuPopup.Children + If hMenu.Tag Then + hMenu.Checked = Val(hMenu.Tag) = iClock + Endif + Next + +End + + +Public Sub Form_Open() + + picMinute = Image.Load("img/arrow_min.png") + picHour = Image.Load("img/arrow_hour.png") + picSecond = Image.Load("img/arrow_sec.png") + + SetClock(3) + Timer1.Enabled = True + +End + +Public Sub Timer1_Timer() + + DrawTime() + +End + + +Public Sub Form_Menu() + + mnuPopup.Popup + +End + +Public Sub mnuClock_Click() + + SetClock(Val(Last.Tag)) + +End + + +Public Sub mnuQuit_Click() + + Me.Close + +End + +Public Sub mnuAbout_Click() + + Message("This exemple was made by Fabien BODARD\nand was optimized by Benoît MINISINI\n\nNote that the 3rd clock is Microsoft copyrighted.\nYou will find it on the future version of windows.") + +End + +Public Sub Form_MouseDown() + + $MX = Mouse.ScreenX - Me.X + $MY = Mouse.ScreenY - Me.Y + +End +' +Public Sub Form_MouseMove() + + If Mouse.Left Then Me.Move(Mouse.ScreenX - $MX, Mouse.ScreenY - $MY) + +End + +Public Sub mnuOntop_Click() + + 'mnuOnTop.Checked = Not mnuOntop.Checked + Me.TopOnly = Not Me.TopOnly + +End + + +Public Sub mnuShowWindow_Click() + + 'mnuShowWindow.Checked = Not mnuShowWindow.Checked + 'ME.Border = If(mnuShowWindow.Checked, Window.Fixed, Window.None) + Me.Mask = Not mnuShowWindow.Value + Me.SkipTaskbar = Not mnuShowWindow.Value + Me.Border = mnuShowWindow.Value + +End diff --git a/app/examples/Drawing/Clock/.src/FClock.form b/app/examples/Drawing/Clock/.src/FClock.form new file mode 100644 index 00000000..798c6fb6 --- /dev/null +++ b/app/examples/Drawing/Clock/.src/FClock.form @@ -0,0 +1,55 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(80.1429,67,43,43) + Mask = True + Border = False + SkipTaskbar = True + { mnuPopup Menu + Text = ("central") + Visible = False + { mnuClock1 Menu mnuClock + Name = "mnuClock1" + Text = ("Clock 1") + Tag = "1" + } + { mnuClock2 Menu mnuClock + Name = "mnuClock2" + Text = ("Clock 2") + Tag = "2" + } + { mnuClock3 Menu mnuClock + Name = "mnuClock3" + Text = ("Clock 3") + Checked = True + Tag = "3" + } + { mnuClock4 Menu mnuClock + Name = "mnuClock4" + Text = ("Clock 4") + Tag = "4" + } + { mn2 Menu + } + { mnuOntop Menu + Text = ("Always on top") + Toggle = True + } + { mnuShowWindow Menu + Text = ("Show Window") + Toggle = True + } + { mnu1 Menu + } + { mnuAbout Menu + Text = ("About") + } + { mn Menu + } + { mnuQuit Menu + Text = ("Quit") + } + } + { Timer1 #Timer + } +} diff --git a/app/examples/Drawing/Clock/img/arrow_hour.png b/app/examples/Drawing/Clock/img/arrow_hour.png new file mode 100644 index 0000000000000000000000000000000000000000..ec7462df9d539033373b317298a4d4c236a140bd GIT binary patch literal 396 zcmeAS@N?(olHy`uVBq!ia0vp^CxG}E2Mdt2jb=|}U| xd-_3~>}@wGCJn6edX4(dbMqKcy^NK&VUGO{6~WgZ6D5m5@}91KF6*2UngC$Cg9-ou literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Clock/img/arrow_min.png b/app/examples/Drawing/Clock/img/arrow_min.png new file mode 100644 index 0000000000000000000000000000000000000000..af461315674bbc32c6b0b9dec173040777d67ea9 GIT binary patch literal 420 zcmeAS@N?(olHy`uVBq!ia0vp^CxG}E2Mdt2jb=|}U|`Jlba4!+xb^n#Lf=+HiMEUU zO-d})=1J@-(?kU{(h`N$b~M>KFiV}4|Fy(2y=i;prI0c#vTtUn4*_E?q zPQQNss`tNlSzj5R|9D&Um7z{p#chIwV-icJ;Gkoq^}m0%?zxHa_00$E+p6zOXx}+| zcV&d_V~%Q;$~l+kJS+U#qx?qjN$tL8+4sWFTO5+hKk@m_xig<%NxI)qK2dou?b)8H sV}jo}KH16}f8JY#2oPAnVA2Q5>KHr>1dVDrS(rhxp00i_>zopr0GpYIG5`Po literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Clock/img/arrow_sec.png b/app/examples/Drawing/Clock/img/arrow_sec.png new file mode 100644 index 0000000000000000000000000000000000000000..e5c42ad358eba98bf9d357e753223a6f0ada4260 GIT binary patch literal 1253 zcmeAS@N?(olHy`uVBq!ia0vp^e}VWn2Q!eIwkhc{0|VpH0G|+7AU9?E|4}rgL*UYz zN8-SUoL&;-7tFxK&Lb!;qo}5BU~1*y>g694!WzaA!5zgH6DN|8B$Xb5TR9bFPXT(c<0a*Adv&6W%u=cG8sOX=yWO=ggg7v~Y37(&Z}~SFdef zzp;1A#BDpK@0zt|-+}{&mK`~^=ESK@XSbfexcl=&!JBu_ zKD_+&<^8uGUw{4i{jYxSHA@BtCJRp&$B>F!Z*Sgo>SGjfdnj-*W(r%qR;S5DkNt}( z9TY`USDAPUab>Exx`teTEoK$Xe!ps6!rXV|?k*maR6Jpb^}3=JL+K1Y2ib+p0TW9Y zUTMfSSh=w;SoDJ7N+D3DiO<3HGUEyfD+W<-_63h#Fof91Hh3*y4(Kdl(9*oXUiZm! zS3`+btDeHrdCRyxGGCT3YARoN?DtG^SAz)Gmn~CEr|BuAKEJ@ab*^@!zunR zYbqZq#X4y1a1o!t)%}7gsE`xLym-R{~Y~ zsmD6J(ptdnQT`q%^m{4L%-@W$4pkA3;uGXTfkN}*b~Svl02(##3y}A(3ds8+2~_Z} zNl#&K&SGv4=H?Q{%on1u4pQ$HaeF8)2J+U-)>G)+5A>|CEzpXyDnRA8{lzCNy9YGk z<#(XaVTM?TUlz{d6VlvYFkM=H9w>C0CDviqZ!hr)&z1v)4w=L{M8yEjJG~ibUg}gm zh1%IbyY7nvu8U@~X$boFyt=akR{0B_@vFaQ7m literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Clock/img/clock_bg_big1.png b/app/examples/Drawing/Clock/img/clock_bg_big1.png new file mode 100644 index 0000000000000000000000000000000000000000..3001659b1c75dc56c6aac7aa1975eb9dac7525ba GIT binary patch literal 19642 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

QG{Eg$ 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 Y&*?+QoVXDF=Y$Pfr*zZ{)huuQAHMzFDF6Tf literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Clock/img/clock_bg_big3.png b/app/examples/Drawing/Clock/img/clock_bg_big3.png new file mode 100644 index 0000000000000000000000000000000000000000..53f378eb97ee763fffb6385fa08cf6f02b949237 GIT binary patch literal 50841 zcmW(-WmJ^g8XhE-4gu+M;2^?)bT`rx(p>`5-JQ~)bR!@g(%m7F1Jcsn(tS5~Eg1j6 zV!qjLJhct~pdf|$g5(7R0>PAiFRl#!y$9d7Xei({$0~jW0`Y@Li;Jkb&F$a2H@qJF zNcrV~W^3V8@btshWcIF7l$P>m2&;g;{t1QRCgPyzGtFmA`t76Lt(UVz^F;F+2396W zXmtL5ZAWzw<4UA>;c8eNhWUP?M{#jEMM=}r$^1YEQhsIeiK_OiR0;ujZ`KZ~c>4-b7 zupYdo)FQRqdcMw7v%rwpgZL)(=+>}#5sNn?ng~gRR5*cEZJNDIt7M8XO49hJa#5+R zwRPDa%i6Ruwa-QH9i2<>$6c1ke-(PIQ=^YE^SZ|M9*~Nf&I3fss}84}4{pwlI&Ghf zj8@CT!^8RhUEh_krTiYJLc>_P9N%#o+i_ZUr|?=r-6AVfLpLN2aKwU)v139yc%+C! z#+-E@B%@fNqmH^nsRf-djO2pnL)8YvC{DVe1s3M)BtH4djSbb+4nEG#=e=cRW$$XW z4z4BhPWCGvZz`5rrcN(p_LlUhZ{~m8ud+O>cK!bSn^ZP`0?~ERd3w)Dw>^uMbIY9~ zbr?z#g)(=*>9rKP<<3fD8aa5BS|Bp*Jj-(5M;t+p*aMBF^MN>=zV|VwwI@SuD(|kd z=8i_3uj?cZJi0yhe(Xzm_bF3LTbs{)tuKnczOIhK_qY>PKMunt;o`;kQrAT%w$Cv~ zC^qH8-{9cjBj#^1OTEN=6X5Y}>Z|JN{)i=m$77c#4xw&jw%7YM6TbY>sIZM)9?_Ud za{H`aW&whOgb+X#8$=P3bN6@02?xo4fo%H*hJhPG4TVBu2Thi_vA_eUsi`sRHaYE( zPM7O?HYgW)9pq8oZ|`UeJT8?Vyv+4|KHq-L^=P(ob+TrwtD|$5mXMI(n+MO8$2}# zb|WIi$VD$}o+F7+aUm-Ul~W$Sz(RD|d2oD3 zpGM8Hz~Yy&cq}7z0#ddNx^OzYBuyfIh`yaZKiZ|4+*4tMCxFUt%l)7J=;`-Xkwu&U zDq+E_{+Su#>RG0`x;neVg+_X2orYtgl9CeNhw&JnvvKYFzir><--!D{4yJt$)Y~uL z+FobYe@?yVPCqpsHp8#g!fZQ|%}X}bjDLe)RU%$tp(Tf;9%b27p0{YzAI8`CACkye zUO!?Oxr(n!I5|$*mzy-kO?sYoOW1eN9^b!?+te#nOoP{aB;}GRaMUaA6jt+nS6AE) zjbyv$yquhz%w9XP`Zqd-@N0OC*E+3!^VHF)c3G?F+3lpy{j|_sYMxX~o2QM

Ctj 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Clock/img/clock_bg_big4.png b/app/examples/Drawing/Clock/img/clock_bg_big4.png new file mode 100644 index 0000000000000000000000000000000000000000..91bdb307ec43d140ab3fcaa753d85bdf8ebe076d GIT binary patch literal 21425 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

fW?v-S#gJ_5ey@ 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Fractal/.directory b/app/examples/Drawing/Fractal/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Drawing/Fractal/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Drawing/Fractal/.hidden/screenshots/2014-12-14.png b/app/examples/Drawing/Fractal/.hidden/screenshots/2014-12-14.png new file mode 100644 index 0000000000000000000000000000000000000000..be2de25ed3710725ae1ee2e61e0bbf5d24ab7a92 GIT binary patch literal 152730 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

wP{jbT0hbxc`gsE+TZ7XbB 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 JPDHLkV1hqvs7C+* literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Fractal/.lang/cs.mo b/app/examples/Drawing/Fractal/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..ed0c78ee927d01aa8395817a0eb01395756343ca GIT binary patch literal 1127 zcma)*O>fgc5QY~hUjYu~4AP1N0)ayc0#)gUYBf#?*h%ChmFS^pV=u|#kBz*Ww&|HG zM=l^FIC98O;N;%<0Z9A;u5jkvBv6oA)R9Jy*PeHFc4qzU>f9Rwy8^!ke+8e1e}@|@ zog!ooTma95kH8YR3Z4Naco$S)5&Qt&1i#Gq6EynkrwKU=-k9+gXxY3!5J$`Tf3>&`CsGe}K2B&9=O1^t8d0CB zD9b-SZdDVP}@r~9&-vHg5}thgx?77wSrlK4;Zw~>rT G=)y1J4LO_u literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Fractal/.lang/cs.po b/app/examples/Drawing/Fractal/.lang/cs.po new file mode 100644 index 00000000..92f007d1 --- /dev/null +++ b/app/examples/Drawing/Fractal/.lang/cs.po @@ -0,0 +1,60 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Fractal" +msgstr "-" + +#: .project:2 +msgid "Mandelbrot Fractal with Just-In-Time compilation" +msgstr "Mandelbrot Fractal s Just-In-Time kompilací" + +#: FFractal.class:95 +msgid "Press F to deactivate Just-In-Time compilation" +msgstr "Stiskněte tlačítko F pro deaktivaci Just-In-Time kompilace" + +#: FFractal.class:97 +msgid "Press F to activate Just-In-Time compilation" +msgstr "Stiskněte tlačítko F pro aktivaci Just-In-Time kompilace" + +#: FFractal.class:102 +msgid "Press R to hide rectangle optimization" +msgstr "Stisknutím klávesy R skrýt obdélník optimalizace" + +#: FFractal.class:104 +msgid "Press R to show rectangle optimization" +msgstr "Stisknutím tlačítka R se zobrazí obdélník optimalizace" + +#: FFractal.class:108 +msgid "Zoom" +msgstr "-" + +#: FFractal.class:108 +msgid "Speed" +msgstr "Rychlost" + +#: FFractal.class:108 +msgid "Fast" +msgstr "Rychle" + +#: FFractal.class:108 +msgid "Slow" +msgstr "Pomalu" + +#: FFractal.class:108 +msgid "Max" +msgstr "-" + +#: FFractal.class:108 +msgid "Tasks" +msgstr "Úkoly" + diff --git a/app/examples/Drawing/Fractal/.lang/fr.mo b/app/examples/Drawing/Fractal/.lang/fr.mo new file mode 100644 index 0000000000000000000000000000000000000000..8a36317354a753944b14b3bc633332a171b16efb GIT binary patch literal 1155 zcmbV~&2G~`5XToNUjYtq25Hp;Ai;)KR8ZVf)oPp)u#?J3E79B5-XvQbueG~LKZH2( z3fwDhJOK~T2jC5O0?s}2-^7qsqEf}k);D;4qO4RfC_vH8n6l;fe*nSB~L)7e{h+QtKe+O$Dn&(1qouFgV*r5fTXU@ z61V`afv(OzZXWm%Tmip>FToS=6<9?wm%k4>eO__|;v%1LLm5(m--O?SLpnAW@673M z!`&=gTQ}f;%;j+Xkvcb))HP2m&q&1cRAf6^Sz5lLd(!sl+o7?nk+Y5*2%4xtAv12J z$|2Y%8(J7c>(nZWEONxHIA3roI29KSk6rG*Oa;{fedfJPP*qqtkcX2tCRxW}rmyzS z3u|X;kF*ORQqtk(gCW~W4bZ>ZMOxNK*_O}-zva;lf9;*W9?-2IZZ}%Z%0`RDVkF&1 zR>Kc^^nKvRENZcuPv5k>sPSaG)eI^jrpY>*=LQ2(+N1T&Fr-aC3g~hW`HgVZuhrt9 z-CjXx-W&3sU>(5+9u58G`lc(n9EPiztSa4`#(1<6HKJhJ-+XnhvZit?a+EtL(2o`S z_F0i}nJ>|#&$SWu^=7Be7N>Ept*+24$P<;yyywxy9ce2h<^^W^cL~aq7Zmd@7eZ-E z`A8(E&;HCAj~Br&is6Acq-LnG4ut~7CoDA0_!;T!E*47A<0EFppfI!KAJX*roB1Dk xJgJ=Ur0)tpnkCi6`FFNeglJMIg0=Q literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Fractal/.lang/fr.po b/app/examples/Drawing/Fractal/.lang/fr.po new file mode 100644 index 00000000..aa2462dc --- /dev/null +++ b/app/examples/Drawing/Fractal/.lang/fr.po @@ -0,0 +1,61 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Fractal" +msgstr "Fractal" + +#: .project:2 +msgid "Mandelbrot Fractal with Just-In-Time compilation" +msgstr "Fractale de Mandelbort avec compilation Just-In-Time" + +#: FFractal.class:95 +msgid "Press F to deactivate Just-In-Time compilation" +msgstr "Appuyez sur F pour désactiver la compilation \"juste à-temps\"" + +#: FFractal.class:97 +msgid "Press F to activate Just-In-Time compilation" +msgstr "Appuyez sur F pour activer la compilation \"juste à-temps\"" + +#: FFractal.class:102 +msgid "Press R to hide rectangle optimization" +msgstr "Appuyez sur R pour cacher l'optimisation par rectangles" + +#: FFractal.class:104 +msgid "Press R to show rectangle optimization" +msgstr "Appuyez sur R pour afficher l'optimisation par rectangles" + +#: FFractal.class:108 +msgid "Fast" +msgstr "Rapide" + +#: FFractal.class:108 +msgid "Max" +msgstr "Max" + +#: FFractal.class:108 +msgid "Slow" +msgstr "Lente" + +#: FFractal.class:108 +msgid "Speed" +msgstr "Vitesse" + +#: FFractal.class:108 +msgid "Tasks" +msgstr "Tâches" + +#: FFractal.class:108 +msgid "Zoom" +msgstr "Zoom" + diff --git a/app/examples/Drawing/Fractal/.project b/app/examples/Drawing/Fractal/.project new file mode 100644 index 00000000..4132dc62 --- /dev/null +++ b/app/examples/Drawing/Fractal/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Fractal +Startup=FFractal +Icon=icon.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.form +Description="Mandelbrot Fractal with Just-In-Time compilation.\n\nThis example allows to freely zoom into the Mandelbrot fractal in real-time. You can toggle just-in-time compilation by hitting the \"F\" key." +Authors="Benoît Minisini" +TabSize=2 +Translate=1 +Language=en +Vendor=Example +Packager=1 +Tags=JustInTime +Screenshot=2014-12-14.png diff --git a/app/examples/Drawing/Fractal/.src/FFractal.class b/app/examples/Drawing/Fractal/.src/FFractal.class new file mode 100644 index 00000000..be4dad74 --- /dev/null +++ b/app/examples/Drawing/Fractal/.src/FFractal.class @@ -0,0 +1,245 @@ +' Gambas class file + +Private $hRose As Image +Private $fScale As Float = 0.0078125 +Private ITER_MAX As Integer = 128 +Private $aColor As New Integer[64] +Private $XC As Float +Private $YC As Float +Private $MX As Integer +Private $MY As Integer +Private $XX As Float +Private $YY As Float +Private $bFast As Boolean +Private $bRect As Boolean + +Private NTASK As Integer = 8 +Private $aTask As New FractalTask[NTASK] +Private $aResult As New Image[NTASK] + +Public Sub FractalTask_Kill() + + Dim hTask As FractalTask = Last + Dim aResult As Integer[] + Dim hImage As Image + + 'Print hTask.Index; ": *KILL*" + Try aResult = hTask.Value + If aResult Then + hImage = New Image(hTask.Width, hTask.Height) + hImage.Pixels = aResult + $aResult[hTask.Index] = hImage + dwgFractal.Refresh + Endif + +End + +' Public Sub FractalTask_Read(Data As String) +' +' Dim hTask As FractalTask = Last +' Print hTask.Index; ": "; Data +' +' End +' +' Public Sub FractalTask_Error(Data As String) +' +' Dim hTask As FractalTask = Last +' Print hTask.Index; "= "; Data +' +' End + +Public Sub dwgFractal_Draw() + + Dim hImage As Image + Dim X, Y, I, J As Float + Dim YT As Integer + Dim HT As Integer + Dim H As Integer + + 'hImage = New Image(Draw.Clip.W, Draw.Clip.H) + For I = 0 To Paint.W Step $hRose.W + For J = 0 To Paint.H Step $hRose.H + 'hImage.DrawImage($hRose, I, J) + Paint.DrawImage($hRose, I, J) + Next + Next + + X = $XC - (dwgFractal.W / 2) * $fScale + Y = $YC - (dwgFractal.H / 2) * $fScale + + 'If $bFast Then + + ' FastDrawFractalRect(hImage, X, Y, $fScale, 0, 0, hImage.W, hImage.H) + ' Draw.Image(hImage, Draw.Clip.X, Draw.Clip.Y) + + 'Else + + 'Draw.Image(hImage, Draw.Clip.X, Draw.Clip.Y) + HT = CInt(Paint.H) \ NTASK + For I = 0 To NTASK - 1 + If $aResult[I] Then + If I < (NTASK - 1) Then + H = HT + Else + H = Paint.H - YT + Endif + Paint.DrawImage($aResult[I], 0, YT, Paint.W, H) + Endif + 'RunTask(0, X, Y, 0, YT, hImage.W, HT) + 'Y += HT * $fScale + YT += HT + Next + 'RunTask(I, X, Y, 0, YT, hImage.W, hImage.H - HT) + 'DrawFractalRect(hImage, X, Y, $fScale, 0, 0, hImage.W, hImage.H) + 'Endif + + + Paint.Background = Color.SetAlpha(Color.White, 128) + Paint.Rectangle(4, 4, Draw.Font.Height * 26, Draw.Font.Height * 3 + 32) + Paint.Fill + + YT = 12 + + If $bFast Then + Draw.Text(("Press F to deactivate Just-In-Time compilation"), 12, YT) + Else + Draw.Text(("Press F to activate Just-In-Time compilation"), 12, YT) + Endif + + YT += Draw.Font.Height + 8 + If $bRect Then + Draw.Text(("Press R to hide rectangle optimization"), 12, YT) + Else + Draw.Text(("Press R to show rectangle optimization"), 12, YT) + Endif + + YT += Draw.Font.Height + 8 + Draw.Text(("Zoom") & ": " & CStr((Log2($fScale) + 6) * 8) & " " & ("Speed") & ": " & If($bFast, ("Fast"), ("Slow")) & " " & ("Max") & ": " & ITER_MAX & " " & ("Tasks") & ": " & NTASK, 12, YT) + +End + +Public Sub dwgFractal_MouseWheel() + + Dim fNewScale As Float + + If Mouse.Delta < 0 Then + If Log2($fScale) >= -6 Then Return + fNewScale = $fScale * Sqr(Sqr(Sqr(2))) + ITER_MAX -= 4 + Else + If Log2($fScale) < -50 Then Return + fNewScale = $fScale / Sqr(Sqr(Sqr(2))) + ITER_MAX += 4 + Endif + + $XC += $fScale * (Mouse.X - dwgFractal.W / 2) + $YC += $fScale * (Mouse.Y - dwgFractal.H / 2) + + $fScale = fNewScale + + $XC -= $fScale * (Mouse.X - dwgFractal.W / 2) + $YC -= $fScale * (Mouse.Y - dwgFractal.H / 2) + + If timRedraw.Enabled Then Return + timRedraw.Start + +End + +Public Sub Form_Open() + + Dim I As Integer + + $hRose = Image.Load("rose.jpg") + For I = 0 To $aColor.Max + $aColor[I] = Color.HSV(360 * I / $aColor.Max, 255, 255) + Next + + Me.Center + +End + +Public Sub dwgFractal_MouseDown() + + $MX = Mouse.X + $MY = Mouse.Y + $XX = $XC + $YY = $YC + +End + +Public Sub dwgFractal_MouseMove() + + $XC = $XX + ($MX - Mouse.X) * $fScale + $YC = $YY + ($MY - Mouse.Y) * $fScale + + If timRedraw.Enabled Then Return + timRedraw.Start + +End + + +Public Sub dwgFractal_KeyPress() + + If UCase(Key.Text) = "F" Then + $bFast = Not $bFast + If $bFast Then + FractalTask.FastDrawFractalRect(Null, 0, 0, 0, 0, 0, 0, 0) + Endif + Redraw(False) + Else If UCase(Key.Text) = "R" Then + $bRect = Not $bRect + Redraw(False) + Else If Key.Code = Key.Esc Then + Me.Close + Endif + +End + +Private Sub Redraw(bClear As Boolean) + + Dim I As Integer + Dim XO As Float + Dim YO As Float + Dim HT As Integer + + XO = $XC - (Me.ClientW / 2) * $fScale + YO = $YC - (Me.ClientH / 2) * $fScale + + HT = Me.ClientH \ NTASK + + For I = 0 To $aTask.Max + + If $aTask[I] Then Try $aTask[I].Stop + If bClear Then $aResult[I] = Null + + If I = $aTask.Max Then HT = Me.ClientH - HT * $aTask.Max + + $aTask[I] = New FractalTask(XO, YO, $fScale, Me.ClientW, HT, ITER_MAX, $aColor, $bFast, $bRect) As "FractalTask" + $aTask[I].Index = I + + YO += HT * $fScale + + Next + + dwgFractal.Refresh + +End + +Public Sub Form_Resize() + + Redraw(False) '(True) + +End + +Public Sub timRedraw_Timer() + + Dim I As Integer + + For I = 0 To $aTask.Max + If $aTask[I].Running Then Return + Next + + Redraw(False) + timRedraw.Stop + +End diff --git a/app/examples/Drawing/Fractal/.src/FFractal.form b/app/examples/Drawing/Fractal/.src/FFractal.form new file mode 100644 index 00000000..5ab7ed63 --- /dev/null +++ b/app/examples/Drawing/Fractal/.src/FFractal.form @@ -0,0 +1,18 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,80,64) + Arrangement = Arrange.Fill + { dwgFractal DrawingArea + MoveScaled(6,6,24,24) + Font = Font["+2"] + Background = &HFFFFFF& + Foreground = &H000000& + Focus = True + NoBackground = True + } + { timRedraw #Timer + #MoveScaled(58,26) + Delay = 100 + } +} diff --git a/app/examples/Drawing/Fractal/.src/FractalTask.class b/app/examples/Drawing/Fractal/.src/FractalTask.class new file mode 100644 index 00000000..debfe3a3 --- /dev/null +++ b/app/examples/Drawing/Fractal/.src/FractalTask.class @@ -0,0 +1,324 @@ +' Gambas class file + +Inherits Task + +Public Width As Integer +Public Height As Integer +Public Index As Integer + +Private $XO As Float +Private $YO As Float +Private $SF As Float +Private $bFast As Boolean +Private $iIterMax As Integer +Private $bRect As Boolean +Private $aColor As Integer[] + +Static Private ITER_MAX As Integer +Static Private DRAW_RECT As Boolean +Static Private COLORS As Integer[] + +Public Sub _new(XO As Float, YO As Float, SF As Float, W As Integer, H As Integer, iIterMax As Integer, aColor As Integer[], bFast As Boolean, bRect As Boolean) + + $XO = XO + $YO = YO + $SF = SF + Width = W + Height = H + $aColor = aColor + $bFast = bFast + $bRect = bRect + $iIterMax = iIterMax + +End + +Public Sub Main() As Variant + + Dim hImage As New Image(Width, Height, Color.Transparent) + + ITER_MAX = $iIterMax + DRAW_RECT = $bRect + COLORS = $aColor + + If $bFast Then + FastDrawFractalRect(hImage, $XO, $YO, $SF, 0, 0, Width, Height) + Else + DrawFractalRect(hImage, $XO, $YO, $SF, 0, 0, Width, Height) + Endif + Return hImage.Pixels + +End + +Static Private Sub DrawFractalRect(hImage As Image, XO As Float, YO As Float, SF As Float, X As Integer, Y As Integer, W As Integer, H As Integer) + + Dim I, J, K, C, CC As Integer + Dim XF, YF, XF0, YF0, XF1, YF1 As Float + Dim ZX, ZY, T As Float + Dim bSame As Boolean + Dim bRect As Boolean = DRAW_RECT + + XF0 = XO + X * SF + YF0 = YO + Y * SF + + If W <= 4 And If H <= 4 Then Goto CALC_ALL + + XF1 = XF0 + (W - 1) * SF + YF1 = YF0 + (H - 1) * SF + + If Sgn(XF0) + Sgn(XF1) Or If Sgn(YF0) + Sgn(YF1) Then + + C = 0 + + XF = XF0 + YF = YF0 + I = X + J = Y + GoSub CALC_POINT + CC = C + bSame = True + + XF += SF + For I = X To X + W - 1 + YF = YF0 + J = Y + GoSub CALC_POINT + YF = YF1 + J = Y + H - 1 + GoSub CALC_POINT + XF += SF + Next + + YF = YF0 + SF + For J = Y + 1 To Y + H - 2 + XF = XF0 + I = X + GoSub CALC_POINT + XF = XF1 + I = X + W - 1 + GoSub CALC_POINT + YF += SF + Next + + If bSame Then + If CC Then hImage.FillRect(X + 1, Y + 1, W - 2, H - 2, CC) + If bRect Then + hImage.PaintRect(X + 1, Y + 1, W - 2, H - 2, &HC0FFFFFF&) + Endif + Return + Endif + + Inc X + Inc Y + W -= 2 + H -= 2 + + Endif + + If W >= H Then + DrawFractalRect(hImage, XO, YO, SF, X, Y, W \ 2, H) + DrawFractalRect(hImage, XO, YO, SF, X + (W \ 2), Y, W - (W \ 2), H) + Else + DrawFractalRect(hImage, XO, YO, SF, X, Y, W, H \ 2) + DrawFractalRect(hImage, XO, YO, SF, X, Y + (H \ 2), W, H - (H \ 2)) + Endif + + Return + +CALC_ALL: + + XF = XF0 + For I = X To X + W - 1 + YF = YF0 + For J = Y To Y + H - 1 + + ZX = 0 + ZY = 0 + + For K = 0 To ITER_MAX - 1 + + T = ZX * ZX - ZY * ZY + XF + ZY = 2 * ZX * ZY + YF + + If ((T * T) + (ZY * ZY)) > 4 Then Break + + ZX = T + + Next + + If K < ITER_MAX Then hImage[I, J] = COLORS[K And 63] + + YF += SF + Next + XF += SF + Next + Return + +CALC_POINT: + + ZX = 0 + ZY = 0 + + For K = 0 To ITER_MAX - 1 + + T = ZX * ZX - ZY * ZY + XF + ZY = 2 * ZX * ZY + YF + + If ((T * T) + (ZY * ZY)) > 4 Then Break + + ZX = T + + Next + + If K < ITER_MAX Then + K = K And 63 + C = COLORS[K] + If C <> CC Then bSame = False + hImage[I, J] = C + Else + C = 0 + If C <> CC Then bSame = False + Endif + + Return + +End + +Fast Static Public Sub FastDrawFractalRect(hImage As Image, XO As Float, YO As Float, SF As Float, X As Integer, Y As Integer, W As Integer, H As Integer) + + Dim I, J, K, C, CC As Integer + Dim XF, YF, XF0, YF0, XF1, YF1 As Float + Dim ZX, ZY, T As Float + Dim bSame As Boolean + Dim bRect As Boolean = DRAW_RECT + + If Not hImage Then Return + + XF0 = XO + X * SF + YF0 = YO + Y * SF + + If W <= 4 And If H <= 4 Then Goto CALC_ALL + + XF1 = XF0 + (W - 1) * SF + YF1 = YF0 + (H - 1) * SF + + If Sgn(XF0) + Sgn(XF1) Or If Sgn(YF0) + Sgn(YF1) Then + + C = 0 + + XF = XF0 + YF = YF0 + I = X + J = Y + GoSub CALC_POINT + CC = C + bSame = True + + 'XF += SF + For I = X To X + W - 1 + YF = YF0 + J = Y + GoSub CALC_POINT + YF = YF1 + J = Y + H - 1 + GoSub CALC_POINT + XF += SF + Next + + YF = YF0 + SF + For J = Y + 1 To Y + H - 2 + XF = XF0 + I = X + GoSub CALC_POINT + XF = XF1 + I = X + W - 1 + GoSub CALC_POINT + YF += SF + Next + + If bSame Then + If CC Then hImage.FillRect(X + 1, Y + 1, W - 2, H - 2, CC) + If bRect Then + hImage.PaintRect(X + 1, Y + 1, W - 2, H - 2, &HC0FFFFFF&) + Endif + Return + Endif + + Inc X + Inc Y + W -= 2 + H -= 2 + + Endif + + If W >= H Then + FastDrawFractalRect(hImage, XO, YO, SF, X, Y, W \ 2, H) + FastDrawFractalRect(hImage, XO, YO, SF, X + (W \ 2), Y, W - (W \ 2), H) + Else + FastDrawFractalRect(hImage, XO, YO, SF, X, Y, W, H \ 2) + FastDrawFractalRect(hImage, XO, YO, SF, X, Y + (H \ 2), W, H - (H \ 2)) + Endif + + ' FastDrawFractalRect(hImage, XO, YO, SF, X, Y, W \ 2, H \ 2) + ' FastDrawFractalRect(hImage, XO, YO, SF, X + (W \ 2), Y, W - (W \ 2), H \ 2) + ' FastDrawFractalRect(hImage, XO, YO, SF, X, Y + (H \ 2), W \ 2, H - (H \ 2)) + ' FastDrawFractalRect(hImage, XO, YO, SF, X + (W \ 2), Y + (H \ 2), W - (W \ 2), H - (H \ 2)) + Return + +CALC_ALL: + + XF = XF0 + For I = X To X + W - 1 + YF = YF0 + For J = Y To Y + H - 1 + + ZX = 0 + ZY = 0 + + For K = 0 To ITER_MAX - 1 + + T = ZX * ZX - ZY * ZY + XF + ZY = 2 * ZX * ZY + YF + + If ((T * T) + (ZY * ZY)) > 4 Then Break + + ZX = T + + Next + + If K < ITER_MAX Then hImage[I, J] = COLORS[K And 63] + + YF += SF + Next + XF += SF + Next + Return + +CALC_POINT: + + ZX = 0 + ZY = 0 + + For K = 0 To ITER_MAX - 1 + + T = ZX * ZX - ZY * ZY + XF + ZY = 2 * ZX * ZY + YF + + If ((T * T) + (ZY * ZY)) > 4 Then Break + + ZX = T + + Next + + If K < ITER_MAX Then + K = K And 63 + C = COLORS[K] + If C <> CC Then bSame = False + hImage[I, J] = C + Else + C = 0 + If C <> CC Then bSame = False + Endif + + Return + +End diff --git a/app/examples/Drawing/Fractal/icon.png b/app/examples/Drawing/Fractal/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..03e7cbbfd0279c192012d0b74085e4db69e21eea GIT binary patch literal 4737 zcmV-{5`OK8P)`@$aO_enMMjFYIW;D{wo9X?d`+44sMwcy? z%H~%!HSg&6x_{r_(ZAoX`w2ku7fi-yJ%G=eT=v1D)u09_|E!09;v{yd{}a=IS_kug z<_6?i^b>VJ8IS^m{^g=C&-|YQ5Clpc%+JQS)c0M08!&-onYl+-qr#GC;3qa%nMd}$j3L*-O0wY=&0S&|vef=b~ zVSWIrHIqr*i9P6ipc1HJb_vhLv>+|niu5&61|63hyx}e5(%~WM4+A|w0mxbqVPHtN zZ`A-CMVW?V(Ll_tKt7b++svi(6O}-lgZbH*&BeUb@2%AgR5}0*s%x#D6#q5V$_vyX zPlw?PAyp5;(&K5?ph=YGw4t=nO{TLvDFB3ism)jl<)~VKWhhm&8CnS0(y0a-H3K%N zNyV59DkdyoL*YIiO`gMdje2g9mvHnvp#X|&Mr(iu{a!<#x1vlZxfB2dP-<=u-Vdzc zxNR<4@+Q!PV#4E)R0=3mpa|6QQ7;6|5rzwUd3fu0`CQri_==?Jztr(;0Og--Abnau zWeXQfZU9{RCAQbMUz}^InvZexbFF&7GV8ts2IeR#k^E~$mL2}q6Om3aHK%8mr~0ARE%}g@7eRpZ|HvgMg0G| zf?Yjlvd;&y0fYgvN{`KUhIJZbXC&IFd6~l}%$x?@D4R6(3p|_{^9@@1PD+CGf~Z&T z7JASVE*NmDX})l{7Wslj=dpM7L!FHL_YO8(sf0OP0To^l10cshxV3-=^mzAau59O6 ziwdnHxp|lgKu|ZVMcF3{WZq`nMA>OEX=i2Vo+?39!^jGgo>G(ESRsYpF!=&{EjL>Y zi)}RrIj6lD;~kTf7M-!WiI>0n0!>MS3BzE$Pg38Q#1Jr^bTc^NrTN_n%x;OOKp|yo zn+N414xy%(EM8^;P=ZqZG6!Sx0pJXd8ufotNtZnRt`Y@`Y=OHb+u8BX7m4<@(|FM@ ziB)gK{N*eKB2WZ}GDM=qA)8vX#&N4J}=$THiR7nfB&ecx zMSwv4a{RYcaO}p-oc+dyj7~KY9;@cED}K&xJ3C+-&TgL>ovC3`lU54I-QVz{snw68^A)jJPIePdOglQ5 zf+>(wFiZmIVQe25`*%^kqmm3YrZe`46a;Z7Q75Tm3Q^^#5(vHy9^Au_GcFg%{4ag^V%Igde zO#MUwh)RergQ<2XDgu!Jq9{iL2vdQPssqb_2@@Wnn%xiGz;N$r)E^w=`PaXfb!eW?-p*Hqy7$q(r7?muDXZL-3HBp-Z<(8kkw z{PZwu zu*rmg341(6JhSaOzJ2CnoPx4|um*kjt&5rbO@z*&aVC;eCk+6=;l2)1-!Er${VjB? zH@KmCySibA!cJ4R3n<0804O2t5sJtxg4m!mE1;yIRBYOSiz{E?$+sS0Pe@{1_%vPR zdsw^WChjZgrz$ndx@EiY=1tKJ?C~`tx4cJWe0WKi2Z+UCa%&f5-&n=^;C>}Uo7qBU z20#W%lV<=>sBZ>I=lM=2QVUdQPz{I|!=!+-{D=7Cx_c=3_J3f$atWOJE%LVfp2p|T zXY!hQUReJgzI9JA<~C^Bc8q=9A1`?&Jz<_f>Wnwp*wP6*96~EX)e*BDx+AH}bb>V~ zKVaAAY^njOK}rxwpbUls&?{h9xCSe99kPEf+qe812^ryi9RdcoS5S1$#ngW78us5@ zfp7Ow!jpqb%sz<7Hp(Blg2Q)3Su@?2X4sd-hfSXJVH$v1#aPDtp9C5~mV*!={E#e% z$1004jByTl7F)1c5ak&H zF6!qCqAy)Fwo~m~0JzhKX#jLxPD&7ViE25p8bm2bH;lTV+XF8~1H2z!OUxg|Kd^}d zTOXt+vMcA{62L%TC;p#4O6%ycT+!%BQ~c6)u3EjH!tpGIXDjzO0-+2IkS!p*5U~V< zK^MhM2?|mWPYFiyBw+*olyWnOWZA#}6IXtCfKJiCt2=iwA3YU&mDLK=Tq%b9<@hteFG9-!2Hj(WgxC6Rz9 zrWzL`DECcKVp>$BV5K`kDS`$h)n+e-r;liPJ!Kv*?V^4`gS9Z26rKgOT_{N@84D+g z#PdlyD@}`_bT;@CR4VHND+;k1$U9A$L<$%+VaNqLB|ILj;^5Pdv9WKM9dZ>Dcc01r zc{-}fgW4)TA01v=E%2KjuD-90J8#=ZYp;bo1fmNSmILf!W2SFzf{wz&x9>jJ=R&z)3ISQk<~uxcURJV@RK-xE!Cq&xOUxnQh>8PbN`Z3G{v;Pz2`&T9D|>|xeh{Ygv>T~4T4{Gx<0>qo z+%0G;KbQJ1|1*`pXlBdxFH&;>6wDCE$&)pb$HeX?-b&P{h}Ks5D7hV@l|D>QgHyn; zzPSvgHg>@yteS`heLV_=VJNPIQ$10Bn|z<*hQFij{;zRxg_lcyWisN8Gk!%WzVm;L ztNJZgrabstuf^}JBO2>jQUFGq&tT}bk7&yqfIJ<`2+~1OHaSWUX#gfP!=tDKqJebM zAT&4vRr~;45h$#L-ws|#=+KpPedjr<8eYcjpCEbg7esEjpS4yuD@(_at^_?y^Uirb zirZTVeblq0`9SI67YW?;Ri3!Dp8Ep7Q#=dOWw8YTFoarUG*LEkKm$;LimBbY&6F-{ zN5Hh8Yq*`;*5AS9C?~lGgD1{^j(aOUq*?br1XLrK9^1x&zqy`@@9!owJj7Cv-|M34 z)@z7eS;!~RAh>_0I!&M!O>*sJn8*ddsD7YOpBwtTTetFSKq3kRCRE$6w?EC*DQL7H z6@#`|iYscK!x&ZNsc8d>1-MPgN9XLNB77Orw+c^X7e_)TYYKw8&z(Wt?LBO2dX6=t zHLy#Yz*x@a$}x`T0w9KRctvE^1WkQDu77%VAO!wV_{WB0nD!EP9IznIE~t2cda%3@ zNJ0l*1}D2Y{Nt+#yyK$!!GGbX%r(-5k*lN}gYHOqL;rrNd_Ln%Lu7n*E7qEGvS^1|4lzAqYSqU1@V-}h3Ow*WFoiS8R zZN7yOdn??a?z9dyjSirENeRb`sj>q^6I4{f*l>`7==ro=Sw;88DE(guar5nKd1+uP z$-X2Lkx8n@6MT|?gaWr4zpO_3iwU+?QPo&S;ui34_*-gQFQ;eZ90C&|0^Vsgh5^zu znsZ@f8Wrbc1#6iAjBBR*QJ&ug3tAep_xW|9Vu%t4kb1+DC%Wa2&-M~(e2f;G}%@Kw7Nb?T* zzU1fB_G<_f7gJum8qb<_Y>jOq00fQz2SLVE)=H-yZ(R3hkJZrEdYIw(oRoW?;0S8* zAfY>G)aUapm^w8|N&B zDXNJI!S%A+NE_@dgcn3UufBIP)qP&hz3yk+oJ!H$g>rN~sA$xFe;>yaHqf9SWcFl; z`Su8<$P6UnN?JIpt_uOec77?8+8@ojZil9w`hOBikHRTQSiv;$mM%W>H_;tkPML*D zD!Nd1ZPTE>ZZG6aX^`e%K3mqgX@YcF$xbdhbZ3_1$;E7x>_e&QWEM!%>fKVRKC7mn zBUMM;sGA>d`VRgvAMucYJ(_t#0~SI}*|yz}0s9xuyxsjw6R^pJTJv`@Yi~ITk~WFO z?9ol3OsW_)UFMUvW4#SFxdtwZtmm8NZf>?B#K*z&KFC46DQZFsVpNYYiqeGe!pCBp zz}x_U3Thfj2N6!YE=KtZd)iqj5cWIl9cfjhH)It+_eCcCWgb=y#0d0rd^VEPAO=yA zw#U;;+S#(e{uc!Rq#al`llF9D2Tx{>`wQJ*m516c@7Ge+%iA&xVj3lF=d#x@%BL)n z@e>2UL~TmU&vDPWpP-z~{RC##i{}C)YYR@slx7z)5-8~pYQ~MlGk#(KfXWRTnf(g` zdyw|}d-f+Yo-9D-`=~08^62*WLs8OOaEw`=IRO9`{>{)NO6BUA{o@Fm!6@kf#6?;g z+@zPLv$J34M^ieTCPDgxDQAIZ32AX(joEu{8kjvQbbcZuH4}-fd%&E_#JRNNQ}|?V z@c`H$2$W;A2Rl~*c&05$E{Uw`EKlI+ijPH;{2{}27ee?9)cB-v}^N)Ya8 P00000NkvXXu0mjfyoc68 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Fractal/rose.jpg b/app/examples/Drawing/Fractal/rose.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8913cdf64fa15bc387b556aeedc69d3de576eec8 GIT binary patch literal 26938 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 VW=mx72`jhVa^=bx)Kl9(|JejX?3(}p literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/GSLSpline/.directory b/app/examples/Drawing/GSLSpline/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Drawing/GSLSpline/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Drawing/GSLSpline/.icon.png b/app/examples/Drawing/GSLSpline/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..884121225d557ea52e34561a8e0f9ef4a73db60d GIT binary patch literal 4045 zcmV;;4>ItHP)R;f zH`CK^exLdMx_{HtBe=wrxp4^@?sk_6=mJK6M%$RC$sas$e6Tq zZqvHhMomV!_wPh&ff9mm-;%?RHWpHQGQjGGd^pmV6KUHEK>8hj0AZo+2>!}f@E`y6 zr41ne8y}zz7!4b)N#pUa7h+p3UMh#eq*xL>7NPw0MB4U}xp6zjHl!su2?P|8td(%7 z0w{R!eT+~T4U3DsJbqsxv+}*XT?y}3Lo^C|YT&ApRJL{&leKXhN-4f~XFfl;vw-jX z{4=&TS54}_zEA*){$>wG+ZY2`i7tM8Pd>LSPi03fyt)elp;L835lv56;!e$B_2Lws zd~hZ$oe@_3_$W0e`a!xbEdVpWyBlL5KGw}ucPt`ry)JaX{ z39=IfFaOg*igRQ6=?jf)`SSoM7qP-kM7hr%z+;=cHW?uvvy{o1*J&wK3(BzjQ+J-|xp5 zFvhTQej-1)E0=^s7hCJ0vI8P|6x~3SBcCC5UJLgbZZL$6t=YofRZ}L)=XK;3P{*jE4G~p!rXt@3(|qnP?G22Ctu0oilSIP zY=%!igYc+3ZrgDDXggJvjV#Rb@a$t(U@SM^e-$d4Kx>GLae`1_Y(QaY3AR8OP>BoA z4uCcgwawHC0E_cX!l(6O+fO%lu6=qOgpx-NVzePO&cUO%XR%>fBA<4^3x~lU9W-cI z&5eB=C~w3gZGQfZ8C)|riD!4hn`L9`Io&QW0)d4w2FnJc4QL0#mHHR2S4_0?pSN_F zd6{PiU>IOkyWniR=zK_P!eN^?pSdsftK*NI-UIa60{|#WJ-73xS+zqUjBMK>~Q1;@>f(Fx2QIG|=;)VF@xBFwNADM_pE~$#yvMo+gkyw?l1YNgD+B-{w&_0g%S3$s z=oazsZ#VTW%#T&0LtYzVFZRWr4@tqmsBI3_57^cxCj$NZ>%wF50f!QoC4h)+e4~GB zZ5u0M3%}b^V+5xv>m0c`eSqyj2ufqN*S;mQU$GX9w4R( zfYvs9OJBmwp3AJc*V0;7PDgVkS6u(~i(|hT3&0_D&{CERC^-(mG~@tU+Z^1x88LT0 zk>j7yE0gF?izmjdP(qz^`)4CFu6I|K9k48+{OWT-f9`2;(}^_;j=*zd!NiABl_31h6CmQa36DH$B^0hn_e2czL2CwuB}ELyQXnsbs)XQUu0FfH{jU;EjL# z8bA2f2JZjW3vB!Culd>Ub`qXZjEadR%>n%)%I-aHVl^Gak(Wtqk{e6O3p(VEPwC-Y;eVfhj&MdZ)p#eMah z=nW#psm{k3L-=F^^K<8ukvsds-TH|=KqzT^ma=4<6bLaY`!N&r0lUk0k?-}<>r7!F zCY2N+SvWJ3gPlRNfoEUb!q;xSnV6LiP%ndV~;qTxYR$*KfFI-4fnDs^|{bXc5LO3=&Ns z5=BR&sM*W-uO}YGku?J`a}lnrLUb@dc=sRh&RK%M;B9T@`t_TTQwV%vN6Fgr4lP%2 zzzjV@7z2TbKuLkbASBpEqBSU~ahxW%8tv)EAROp+OKPiCT3SlM@Fbwnqpol7jxF4K z<4g_&U33};0}U0#FSwQgUpM{XD9N69EF}>_kbU(!o`3yG!ewu8LRh4_6i;7!AC2KK zne8pCx$S#XzQeAM-=TfUD%^YDA++KS0D1->A=Zpb*$1~HQ3%-rkK^+mp(G;!v_Z^CA8roaBX)_iXb;ntI+a`wFol1#Zw*hr<7wxmI$Bsk@M#1GA zbmxicyPQx_QOSul%ld>4Cyahxc9kMuPg@%Q z-}#Aj@cl|M7Z;NhCr<0;07s4-p|`t-Yp%O?%zlTZ+Jq1`fYyeG*TiI70G1R1A%%N5 zzHA`~h5Wc9?Py&@XF@Kb>oB4ACL-NVyv57ttFFMCn!%2$HsXUvvAjw2R(^`Z?ZI2H z09R55-&u4c4%g%sDojR;SIwiLs-1?uIA-OYzTNpHB@^yfQi$QsPC@{N-U}#6KuFv} zu_2@cL6nnCHMBcp2=qJf7B400>%w>JQ;d$1o|8{(e>Z&&CtZCI>r@oD!&nLuQnR_@ zs+*?G@X;!mlg7z+w=?fxIbEJi+Sg2eZb3@KaQ|+`08k=0Oh#ygk^<#$;!V%N5g$+g zzTJeveuNOXvI@xzbRjK^)`-TgK7gLJgoGKzm{=F?Xg^shN@YbU^B1l-KM=fqB)#-J zU9+!7t$kq9=c%D&2*3z}qX6I^9{2=fPVylB?&N!fYRVii0ANOAIG2nZ&Z9H-Hj~}4P!}IwR{~P)>g96f)-B#Q8Soo zDq%3j(9jbgF*~2~V+V)|#f+i_NHvWUAE{}u<}OMihMga@aC1gHauN?TQXxjFnNcAi zl?V<`nHpn8lAL2?8Tlz^1|Y@gC&C~M z%@J8cPlQ8bs~Fz)CM2>TtB{vp`VH2Btt6$UqJ`kC_qJfiry!TDW!ai_(9?=B0%Ocb zO6s%}2O2wYlw8L^M-xAM#a0CrG(=J=*D^rltuu)#jQ8p$1%yu1h7_l~+mfS30jXqQIIXrNVxoDv#FTVau3As-kurYY(10^7 z7oRY+wYTu)HD7@_^C>#KpFbZxM4O7ioi`hU!Bw)HzTf~!QnF%B3E747s5|}{%2HFB z59~QvO;=|Een%!T4w#oC@y3ijKYZm%U?nyae+Mwe1B5sg0uD)FczK@Gs53pR&6u-* zg#8~8Ov}Z1lQ}LVM_zn}Z*I7kxeJz&lUK<8@{ig6(OY;57vM_C#OF=oh%r>x)>3@z zAYWR4@08vBp1anab*KzWNHa1RS&SJ4@C!XcQ;IXZ9(Foi+_C0%p8wqoI7+T2>gyrR z=^_#dBZMG6F@;qtH*n>W71Y!mVgG^M)VH@1)|%{;H1cm;kGN>BpT&r6QW`%Dja%;? z(+~HB#UlWLP&7Ge>&Y#(zQklH8FIUE_-mdQIs3Xzo4@G4#7*@s@E=K}F2xVhj9+#ziyLzgJxFCSk zS3Yd`z!{f3(>BA4r7k}J07{rhxWB9VfgAEx0_`IJfb~Ez-~pU`enG{hE*ppd1He(> zkHY`}TtE&m8^{8Z05_M|XLVZpj)l}45{OcP{ zY$@%atG$)3{mTN?KCFV=JZf9pu$+mw;?mKP9@3HwLO_huI<4_AjV_;Y%L{li zXCN&_ps9?L?>&wV^__R)hqdCq&1jW?)9HrbaTXSZ$;c_-@xNPt#`C6ay;|$L-@0JN zPNekII9yf;+YYk%`ERrF)t#iKp8Lnum%aWU--ie@ExErN00000NkvXXu0mjf1R#a= literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/GSLSpline/.project b/app/examples/Drawing/GSLSpline/.project new file mode 100644 index 00000000..c8b13404 --- /dev/null +++ b/app/examples/Drawing/GSLSpline/.project @@ -0,0 +1,14 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.4.90 +Title=Gnu Scientific Library demo +Startup=FMain +Icon=spline.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Authors="Juergen Zdero\nBenoît Minisini" +TabSize=3 +SourcePath=/home/op/SDB/Programming/gambas/gambas3_projects/GSL/demo +Packager=1 diff --git a/app/examples/Drawing/GSLSpline/.src/FMain.class b/app/examples/Drawing/GSLSpline/.src/FMain.class new file mode 100644 index 00000000..c5da05b0 --- /dev/null +++ b/app/examples/Drawing/GSLSpline/.src/FMain.class @@ -0,0 +1,130 @@ +' Gambas class file + +Library "libgsl:0" + +Private Extern gsl_spline_alloc(pInterpType As Pointer, iSize As Integer) As Pointer +Private Extern gsl_spline_init(pSpline As Pointer, ax As Pointer, ay As Pointer, iSize As Integer) As Integer +Private Extern gsl_spline_eval(pSpline As Pointer, x As Float, pAcc As Pointer) As Float +Private Extern gsl_interp_accel_alloc() As Pointer +Private Extern gsl_spline_free(pSpline As Pointer) +Private Extern gsl_interp_accel_free(pAcc As Pointer) + +Public pointsArr_x As New Float[] +Public pointsArr_y As New Float[] +Public splineArr_x As New Integer[] +Public splineArr_y As New Integer[] + +Public Sub btnCubicSpline_Click() + + Dim xi As Integer + Dim acc As Pointer + Dim spline As Pointer + Dim gsl_interp_cspline As Pointer + + If pointsArr_x.Length < 3 Then Return + + gsl_interp_cspline = Pointer@(System.GetExternSymbol("libgsl:0", "gsl_interp_cspline")) + acc = gsl_interp_accel_alloc() + spline = gsl_spline_alloc(gsl_interp_cspline, pointsArr_x.Count) + gsl_spline_init(spline, pointsArr_x.Data, pointsArr_y.Data, pointsArr_x.Count) + + xi = pointsArr_x[0] + While xi < pointsArr_x[pointsArr_x.Max] + splineArr_x.Add(xi) + splineArr_y.Add(gsl_spline_eval(spline, xi, acc)) + xi += 1 + Wend + dwgGraph.Refresh + + gsl_spline_free(spline) + gsl_interp_accel_free(acc) + +End + +Public Sub dwgGraph_MouseDown() + + splineArr_x.Clear + splineArr_y.Clear + If (pointsArr_x.Length = 0) Or If (Mouse.X > pointsArr_x[pointsArr_x.Max]) Then + pointsArr_x.Add(Mouse.X) + pointsArr_y.Add(Mouse.Y) + Else + dwgGraph.Background = &HFF8080 + Endif + dwgGraph.Refresh + +End + +Public Sub dwgGraph_MouseUp() + + If dwgGraph.Background <> Color.White Then dwgGraph.Background = Color.White + +End + +Public Sub dwgGraph_Draw() + + Dim i As Integer + + 'lines + paint.Brush = paint.Color(Color.DarkGray) + Paint.LineWidth = 0.5 + paint.Font.Size = 10 + For i = 0 To pointsArr_x.Max + If i = 0 Then + Paint.MoveTo(pointsArr_x[i], pointsArr_y[i]) + Else + Paint.LineTo(pointsArr_x[i], pointsArr_y[i]) + Endif + Next + paint.Stroke + + 'numbers + For i = 0 To pointsArr_x.Max + paint.Text(i + 1, pointsArr_x[i], pointsArr_y[i] - 8) + Next + paint.Fill + + 'points + paint.Brush = paint.Color(Color.red) + For i = 0 To pointsArr_x.Max + Paint.Arc(pointsArr_x[i], pointsArr_y[i], 3) + Paint.Fill + Next + + 'spline + If splineArr_x.Count Then + paint.Brush = paint.Color(Color.DarkMagenta) + Paint.LineWidth = 1.0 + For i = 0 To splineArr_x.Max + If i = 0 Then + Paint.MoveTo(splineArr_x[i], splineArr_y[i]) + Else + Paint.LineTo(splineArr_x[i], splineArr_y[i]) + Endif + Next + paint.Stroke + Endif + +End + +Public Sub btnClear_Click() + + pointsArr_x.Clear + pointsArr_y.Clear + splineArr_x.Clear + splineArr_y.Clear + dwgGraph.Refresh + +End + +Public Sub btnAbout_Click() + + Message.Info("

Gnu Scientific Library example

Made by Juergen Zdero (juergen@zdero.eu) and Benoît Minisini (gambas@users.sourceforge.net)") + +End + +Public Sub btnClose_Click() + + Me.Close + +End diff --git a/app/examples/Drawing/GSLSpline/.src/FMain.form b/app/examples/Drawing/GSLSpline/.src/FMain.form new file mode 100644 index 00000000..2a79dee1 --- /dev/null +++ b/app/examples/Drawing/GSLSpline/.src/FMain.form @@ -0,0 +1,45 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,93,60) + Text = ("Gnu Scientific Library Example") + Icon = Picture["spline.png"] + Arrangement = Arrange.Vertical + Spacing = True + Margin = True + { dwgGraph DrawingArea + MoveScaled(2,2,61,36) + Background = &HFFFFFF& + Expand = True + Border = Border.Sunken + } + { HBox1 HBox + MoveScaled(2,41,87,6) + Spacing = True + { btnCubicSpline Button + MoveScaled(0,0,19,6) + Text = ("Cubic &spline") + Picture = Picture["icon:/medium/pen"] + } + { btnClear Button + MoveScaled(20,0,18,6) + Text = ("&Clear") + Picture = Picture["icon:/medium/erase"] + } + { Panel1 Panel + MoveScaled(44,1,1,5) + Expand = True + } + { btnAbout Button + MoveScaled(50,0,15,6) + Text = ("About...") + Picture = Picture["icon:/medium/info"] + } + { btnClose Button + MoveScaled(66,0,15,6) + Text = ("Close") + Picture = Picture["icon:/medium/close"] + Cancel = True + } + } +} diff --git a/app/examples/Drawing/GSLSpline/spline.png b/app/examples/Drawing/GSLSpline/spline.png new file mode 100644 index 0000000000000000000000000000000000000000..0f2bf708db45123cb5529b36d97bd931528fadd7 GIT binary patch literal 1632 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, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Gravity\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-16 23:34+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FMain.class:248 +msgid "0" +msgstr "-" + +#: FAbout.class:24 +msgid "About gbGravity" +msgstr "Quant a gbGravity" + +#: FMain.class:302 +msgid "About gbGravity..." +msgstr "Quant al gbGravity..." + +#: FMain.class:167 +msgid "Add a Ball" +msgstr "Afegeix un bola" + +#: FMain.class:192 +msgid "Add more Balls" +msgstr "Afegeix més boles" + +#: FMain.class:289 +msgid "Ball out of Range" +msgstr "Bola fora de rang" + +#: FAbout.class:51 +msgid "by Iman Karim" +msgstr "per Iman Karim" + +#: FMain.class:216 +msgid "Choose Point:" +msgstr "Punt triat:" + +#: FMain.class:313 +msgid "Clear each Frame" +msgstr "Neteja cada marc" + +#: FMain.class:204 +msgid "Click and hold on Gamefield and move your Mouse!" +msgstr "Fes un clic mantingut dins del camp de joc i mou el teu ratolí!" + +#: FMain.class:221 +msgid "ComboBox1" +msgstr "ComboBox1" + +#: FMain.class:265 +msgid "Current Ball aX:" +msgstr "Bola actual aX:" + +#: FMain.class:276 +msgid "Current Ball aY:" +msgstr "Bola actual aY:" + +#: FMain.class:243 +msgid "Current Ball X:" +msgstr "Bola actual X:" + +#: FMain.class:254 +msgid "Current Ball Y:" +msgstr "Bola actual Y:" + +#: FMain.class:179 +msgid "Floor Slide 0.9" +msgstr "Terra relliscós 0.9" + +#: FMain.class:228 +msgid "Focus Point" +msgstr "Punt amb el focus" + +#: FAbout.class:36 +msgid "gbGravity" +msgstr "gbGravity" + +#: FMain.class:155 +msgid "gbGravity - Iman Karim" +msgstr "gbGravity - Iman Karim" + +#: FMain.class:307 +msgid "Gravity" +msgstr "Gravetat" + +#: FMain.class:161 +msgid "Gravity 0.9" +msgstr "Gravity 0.9" + +#: FAbout.class:41 +msgid "Gravity like Simulator" +msgstr "Simulador de gravetat" + +#: .project:1 +msgid "Gravity Simulator" +msgstr "Simulador de gravetat" + +#: FAbout.class:56 +msgid "Ok - kool!" +msgstr "D'acord - Guai!" + +#: FMain.class:212 +msgid "Point Setup" +msgstr "Configuració del punt" + +#: FMain.class:234 +msgid "Randomize Kick" +msgstr "Sortida aleatoria" + +#: FMain.class:319 +msgid "Sky" +msgstr "Cel" + +#: FAbout.class:62 +msgid "Thanks to the Gambas team!" +msgstr "Gràcies a l'equip del Gambas!" + +#: FAbout.class:46 +msgid "Written in Gambas 1.9.46" +msgstr "Escrit amb Gambas 1.9.46" + diff --git a/app/examples/Drawing/Gravity/.lang/cs.mo b/app/examples/Drawing/Gravity/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..22bd8f0fce88e2fea6ae9e13ff88ddbf8f2d962e GIT binary patch literal 1885 zcmZXTO=x3P6vwYR>KJF#jyishoFXnnUu;2i8b_HlO>4+kY1(S5E^hK}^IqS_4eui} z8F1ysg$fEThJs9Rr4j@M!G+ztM%<~m7k47=X5~V}|9O3#g!Yh|-@WIachAQ?_xE$B zJ{6FsFrLIXbDI#)g8zanH*Ob#r$g|5@N@7S_$`}csKZEw*CfWKl|W$@E4Hd_%qwT335FDf%u8D zr-XPMoB+$^25*2I$9Ew6{|S5q z{0)2={2SzRp1w__cO@x`~|Z8 zKOozkMv*WT=Rx*&0c87mkoT(~$Fr63t&Bh_XYkMWz%}D~;rh}zk%l;p!NPvo$F#67 z7QQPkA;-kM^aREQ3>Nl(BwQoTkMDx(&b8zGSh$|tgDf07_Zga}*w%4G-S$c#`*t)q zzRu_KqG*~VX;HdvI`wp*(p4x}NBq6sp*87ul_*(Shl*CU?MDSsa+M57$&N+Kf%LHL z+8u|aZ<3{5leAAOQ193Z3)XtNuV|p-fa*F9)vPFKudNsL)p=2hgFyLF>Y3ie+0*N- zV{3WbK*D0#)jFV-YnzJZ@(W^FcjE9cRB@Dk#&rK^-m<;em5~mPtjM(;MH6+?A%h&P z-C2<)PAwJ1J%Ngt+Ow}Isv>LAat31ElD-pCq$#o#=iZiBDe29MHv&6~luveA6V}h? z7xFK@B-#V2c+#h;3~WzKZtB(y&Zi7k1HG#{kx?;?O%;T;_6xLHELDpuW!fySwJOa< zZnbHwslLsd#*&Owfws!UHKX1%mWuRxvrw-*x7}=%b2S-8#(E(AFwMU}%Nw;CHH!5z zT`Jd$mD=Uv($ZSF)p`x4em9m~WvnBc0@aF*l?`@zsaCt}+IbyxCpHDj)hqS#B)<9l zT&|>jqz`w49^#4A)#!zuD{cQ3>R2)eRrKn{`m%9xV#i_asK6-u9c|ivw?G%$c9au_ zXioL>)SP>Hb$lJyze`>+I{5I`s-KKH>QFcSTGV-uJloS0=xd?0#O9!_0%~isAPzpT z!zlTI968=M!O%}eo^nJLJ>ehhMG8-)r_ErzkEt)o3S0%GZ!jLt zBHE%8#mUgcIfoH&bDz+meNX7P$xkX_xh=I5=-6_VKZ!7H>22v9>>&i!Bb}_FWk*eB zQ>_m@g|;8N$q3yaPuc8BM`H``A{p|-NMAtc^bhvjts(k44~ qbWb$fuC}CDN=D9rP)je)UIKsE3zMO*sh_FhqgSBK=_WT&xBmeOC)}O@ literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Gravity/.lang/cs.po b/app/examples/Drawing/Gravity/.lang/cs.po new file mode 100644 index 00000000..a55f0a13 --- /dev/null +++ b/app/examples/Drawing/Gravity/.lang/cs.po @@ -0,0 +1,128 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Gravity Simulator" +msgstr "Gravitace sinulítoru" + +#: FAbout.form:14 +msgid "About gbGravity" +msgstr "O gbGravity" + +#: FAbout.form:26 +msgid "gbGravity" +msgstr "-" + +#: FAbout.form:31 +msgid "Gravity like Simulator" +msgstr "Gravitace jako simulátor" + +#: FAbout.form:36 +msgid "Written in Gambas 1.9.46" +msgstr "Napsáno v Gambasu 1.9.46" + +#: FAbout.form:41 +msgid "by Iman Karim" +msgstr "od Iman Karim" + +#: FAbout.form:46 +msgid "Ok - kool!" +msgstr "Ok - skvělé!" + +#: FAbout.form:52 +msgid "Thanks to the Gambas team!" +msgstr "Díky týmu Gambas!" + +#: FMain.form:35 +msgid "gbGravity - Iman Karim" +msgstr "-" + +#: FMain.form:41 +msgid "Gravity 0.9" +msgstr "Gravitace 0.9" + +#: FMain.form:47 +msgid "Add a Ball" +msgstr "Přidej míč" + +#: FMain.form:59 +msgid "Floor Slide 0.9" +msgstr "Zaokrouhlený snímek 0.9" + +#: FMain.form:72 +msgid "Add more Balls" +msgstr "Přidej více míčů" + +#: FMain.form:84 +msgid "Click and hold on Gamefield and move your Mouse!" +msgstr "Klikněte a podržte na hracím poli a přesuňte myš!" + +#: FMain.form:92 +msgid "Point Setup" +msgstr "Nastavení bodu" + +#: FMain.form:96 +msgid "Choose Point:" +msgstr "Vyber bod:" + +#: FMain.form:103 +msgid "ComboBox1" +msgstr "-" + +#: FMain.form:108 +msgid "Focus Point" +msgstr "Zaměřený bod" + +#: FMain.form:114 +msgid "Randomize Kick" +msgstr "Náhodný kop" + +#: FMain.form:123 +msgid "Current Ball X:" +msgstr "Aktuální míč X:" + +#: FMain.form:128 +msgid "0" +msgstr "-" + +#: FMain.form:134 +msgid "Current Ball Y:" +msgstr "Aktuální míč Y:" + +#: FMain.form:145 +msgid "Current Ball aX:" +msgstr "Aktuální míč aX:" + +#: FMain.form:156 +msgid "Current Ball aY:" +msgstr "Aktuální míč aY:" + +#: FMain.form:169 +msgid "Ball out of Range" +msgstr "Míč mimo rozsah" + +#: FMain.form:182 +msgid "About gbGravity..." +msgstr "O gbGravity..." + +#: FMain.form:187 +msgid "Gravity" +msgstr "Gravitace" + +#: FMain.form:193 +msgid "Clear each Frame" +msgstr "Čistý každý snímek" + +#: FMain.form:199 +msgid "Sky" +msgstr "Obloha" diff --git a/app/examples/Drawing/Gravity/.lang/de.mo b/app/examples/Drawing/Gravity/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..47f84e85ffb9a91030e88336eac0fd380a1f6596 GIT binary patch literal 1895 zcmZvcO>7%Q6vqcBU#108zCZPHMD4nXgivcj)r~(=D|Q^kNudP}v)-|H@Ombi-A$Sb z&b@I#To9t5_5?SO5C;SYOWZ2piUi_@IMNdbRPleCO|ywQ^7uDz=H-3N{GC6JANg2d zyo~uG=FxkEcm@0q#4RW3zz^B1qK(6Dj;`|oK_1p&W5yy@Q z@jN&KJ`0`%xxOaI_4GlG^DfAZrXcs5-M?GsHpu;61-Z^oz$d`3K#uz($oYN)p9KE~ zPlC5Wj&tIH-E*(t0?7R@7knG!xMPs}cpv1xuNLPYfgJaG!S6thdlTe7eg?Uo-$8uD zKlpkM{1@c+JdVrb`c8rDe-`ArJ0Qma@^KX3TnFD7pX2%Ryl6u_gvo>BaE|ie+&s8u zzC!Mewe$k!OPD-3@7{O@3%+-L3%+;m`6)~u{H|Ox5AK2Q9pdo=o24`gmYm$u>CR!h zTCEB{3`x?WjN^Pcu}%N|3u}rPo^P*Uf742{L#^ll1+IUvv zjZqCegd>yb3$gi=dsq`3D$!cuZFE8cWEO_d>?-wE%}SI<;m zeN6;A)SgH~%hKtI*ng=zDerY3r6yfyuJzl!mC9<* zTT@$_550y=RgEq-{WY)K^BO*#@722P6PJ1`%}PfmskiQ=N%H<{)Y|BDXvOa~X`$Km z+nsZMqp{ZP_s_!Bj50Y=-a5LeQO93d+TfH6ozA&fSFIcE`_!n?ZFigd_03o3Ds^kn zKGN-MqMlS;PET#dQk&Ol5J{J)^o@=6mUnvJkINb==QYj1hT4p3bUM&!MR;QRX`oz5 z(}!%`fW0Z5Yo?v3H3K2?jh@Y%-Y8TXV7`56?G-l7pQH8^s}hCp;dyi^Q4e( z*@Sq@Cae2^jq^ntD&v$6GBe86yNF+l+2k@se-LdeH+FJ(V4b^_&XPMizw4$~H0@`} zwv1wN30HR`j`avd`$3Z0={0d)PbMlB4X9k<2TdVlT&SFNF)Nl7?#5{q+)e3Q_\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Gravity Simulator" +msgstr "Schwerkraftsimulator" + +#: FAbout.form:14 +msgid "About gbGravity" +msgstr "Über gbGravity" + +#: FAbout.form:26 +msgid "gbGravity" +msgstr "gbSchwerkraft" + +#: FAbout.form:31 +msgid "Gravity like Simulator" +msgstr "Schwerkraftsimulator" + +#: FAbout.form:36 +msgid "Written in Gambas 1.9.46" +msgstr "Geschrieben in Gambas 1.9.46" + +#: FAbout.form:41 +msgid "by Iman Karim" +msgstr "von Iman Karim" + +#: FAbout.form:46 +msgid "Ok - kool!" +msgstr "-" + +#: FAbout.form:52 +msgid "Thanks to the Gambas team!" +msgstr "Danke an das Gambas Team!" + +#: FMain.form:35 +msgid "gbGravity - Iman Karim" +msgstr "gbSchwerkraft - Iman Karim" + +#: FMain.form:41 +msgid "Gravity 0.9" +msgstr "Schwerkraft 0.9" + +#: FMain.form:47 +msgid "Add a Ball" +msgstr "Ball hinzufügen" + +#: FMain.form:59 +msgid "Floor Slide 0.9" +msgstr "Bodenreibung 0.9" + +#: FMain.form:72 +msgid "Add more Balls" +msgstr "Mehrere hinzufügen" + +#: FMain.form:84 +msgid "Click and hold on Gamefield and move your Mouse!" +msgstr "Aufs Spielfeld klicken und die Maus bewegen!" + +#: FMain.form:92 +msgid "Point Setup" +msgstr "Punkt Auswahl" + +#: FMain.form:96 +msgid "Choose Point:" +msgstr "Punkt wählen:" + +#: FMain.form:103 +msgid "ComboBox1" +msgstr "-" + +#: FMain.form:108 +msgid "Focus Point" +msgstr "Brennpunkt" + +#: FMain.form:114 +msgid "Randomize Kick" +msgstr "Zufälliger Abstoß" + +#: FMain.form:123 +msgid "Current Ball X:" +msgstr "Dieser Ball X:" + +#: FMain.form:128 +msgid "0" +msgstr "-" + +#: FMain.form:134 +msgid "Current Ball Y:" +msgstr "Dieser Ball Y:" + +#: FMain.form:145 +msgid "Current Ball aX:" +msgstr "Dieser Ball aX:" + +#: FMain.form:156 +msgid "Current Ball aY:" +msgstr "Dieser Ball aY:" + +#: FMain.form:169 +msgid "Ball out of Range" +msgstr "Ball außerhalb des Feldes" + +#: FMain.form:182 +msgid "About gbGravity..." +msgstr "Über gbGravity..." + +#: FMain.form:187 +msgid "Gravity" +msgstr "Schwerkraft" + +#: FMain.form:193 +msgid "Clear each Frame" +msgstr "Alle Frames löschen" + +#: FMain.form:199 +msgid "Sky" +msgstr "Himmel" + diff --git a/app/examples/Drawing/Gravity/.lang/es.mo b/app/examples/Drawing/Gravity/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..c2cf2604dc41333a3bb7c8e8dd6eca5123fe0855 GIT binary patch literal 1872 zcmZvbO>7%Q6vw9!D46f?4MlnpCseJQAcPt>t-5s_*Tjx3J1Gf5NaNk{dg|RBGrJqr z9N>mHArR^TZh^XuZmO@Z+O zzBBk1P6_c6_!r3Iqq~IQ>BrzB;3wcB_yt%3zXcx#e*hl??|=`1zk!c}|A5Qjsk?=^ zAAA|)IBNx)An$v#U{v_u1s{O_L+~{CSz&(;J_Y-0kn{go`0s#R$1foM#Gm;09QZf* z3|Ly2*7YLDb*z9K=K}aI*af-ItM>@;IQSaK>u-TvUj}mD?-%Qz7WP+#{XKXQ>pz1W zfAQXFJ!cA@Em$eo0H4IZU6A9);CdH7!qB=AtQPeRyPAj4sDrrecZ%7+fMpzEDHzFDPG&G@4CZ;;v z1Fc}NH!}N*CML7gGFhUQMAbxnbH&_P5!K9E6{mS-+C}Vnd+&rTPi~Odu@M?$sTXQr z(Qmy4i$IWm#~zKmc;8;r>Tlb=O@7amGWx&l~+Z7LXAkqv?;BQ#L>MR z&X9a=L&uuy%1fPw?_5(h(I&1?$E|L<^%`BPb$gBWR;kl=x@uqZqO&ejRiV9_+jUxP zXWgZ%?Mkb0_Ko&dt<;oB>TFvXCwZe4+Sq9}Y0GWZXsys`XuILKt6 zoNaVdp{Bc4-{F*N&F1A$myHcBm0FEf?Wm}g@^YzaV)Tq0lQD`&)s6JrIFvfRK;BT= zM5PyZwl|#fNBg*zfwE35_KdINphD;SIxVqmJ!L&fxXHtV`9EJS-@vK>GR$I0eG^LI z7LMrlL6UnQWcX>2DVv8x+Q5jO3YF*K(D2)F7N@2nnmQV5n4a_{d71RB5w{MeqMlG< z60{dcO!{~tD#X5MY`DnlnF7^PQpx${C+MPG$?s0Pu zI<%ffJfsR~tP_*#x5Gtqb`=lI+nLHmu6*eqPL9dkGm)Vrm$^Ta`K^PxinU~^&w_a- z9_S#mGS4XLCLAl0c<6W=+K4LNG9L;akEbDP8B$=g=_yQKz>=sX9^`LO*X&Ug;veIE O&WZczY7b|})&C#P!oK1F literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Gravity/.lang/es.po b/app/examples/Drawing/Gravity/.lang/es.po new file mode 100644 index 00000000..eb75a96f --- /dev/null +++ b/app/examples/Drawing/Gravity/.lang/es.po @@ -0,0 +1,126 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FAbout.class:26 +msgid "About gbGravity" +msgstr "Acerca de gbGravity" + +#: FAbout.class:38 +msgid "gbGravity" +msgstr "gbGravity" + +#: FAbout.class:43 +msgid "Gravity like Simulator" +msgstr "Gravity como simulador" + +#: FAbout.class:48 +msgid "Written in Gambas 1.9.46" +msgstr "Escrito en Gambas 1.9.46" + +#: FAbout.class:53 +msgid "by Iman Karim" +msgstr "por Iman Karim" + +#: FAbout.class:58 +msgid "Ok - kool!" +msgstr "Ok - ¡Genial!" + +#: FAbout.class:64 +msgid "Thanks to the Gambas team!" +msgstr "¡Gracias al grupo de Gambas!" + +#: FMain.class:270 +msgid "gbGravity - Iman Karim" +msgstr "gbGravity - Iman Karim" + +#: FMain.class:276 +msgid "Gravity 0.9" +msgstr "Gravedad 0.9" + +#: FMain.class:282 +msgid "Add a Ball" +msgstr "Agregar una bola" + +#: FMain.class:294 +msgid "Floor Slide 0.9" +msgstr "Porción del piso 0.9" + +#: FMain.class:307 +msgid "Add more Balls" +msgstr "Agregar más bolas" + +#: FMain.class:319 +msgid "Click and hold on Gamefield and move your Mouse!" +msgstr "¡Click y sostenga sobre el campo de juego y mueva su ratón!" + +#: FMain.class:327 +msgid "Point Setup" +msgstr "Punto de configuración" + +#: FMain.class:331 +msgid "Choose Point:" +msgstr "Seleccionar punto:" + +#: FMain.class:336 +msgid "ComboBox1" +msgstr "ComboBox1" + +#: FMain.class:343 +msgid "Focus Point" +msgstr "Punto de enfoque" + +#: FMain.class:349 +msgid "Randomize Kick" +msgstr "Golpe aleatorio" + +#: FMain.class:358 +msgid "Current Ball X:" +msgstr "Bola activa X:" + +#: FMain.class:363 +msgid "0" +msgstr "0" + +#: FMain.class:369 +msgid "Current Ball Y:" +msgstr "Bola activa Y:" + +#: FMain.class:380 +msgid "Current Ball aX:" +msgstr "Bola activa aX" + +#: FMain.class:391 +msgid "Current Ball aY:" +msgstr "Bola activa aY" + +#: FMain.class:404 +msgid "Ball out of Range" +msgstr "Bola fuera de rango" + +#: FMain.class:417 +msgid "About gbGravity..." +msgstr "Acerca de gbGravity..." + +#: FMain.class:422 +msgid "Gravity" +msgstr "Gravedad" + +#: FMain.class:428 +msgid "Clear each Frame" +msgstr "Limpiar cada cuadro" + +#: FMain.class:434 +msgid "Sky" +msgstr "Cielo" + +#~ msgid "Gravity Simulator" +#~ msgstr "Simulador de gravedad" diff --git a/app/examples/Drawing/Gravity/.project b/app/examples/Drawing/Gravity/.project new file mode 100644 index 00000000..f1ba2915 --- /dev/null +++ b/app/examples/Drawing/Gravity/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.1.90 +Title=Gravity Simulator +Startup=FMain +Icon=gravity.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Environment="GB_GUI=gb.qt4" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Drawing/Gravity/.src/FAbout.class b/app/examples/Drawing/Gravity/.src/FAbout.class new file mode 100644 index 00000000..1d524d09 --- /dev/null +++ b/app/examples/Drawing/Gravity/.src/FAbout.class @@ -0,0 +1,8 @@ +' Gambas class file + + +Public Sub Button1_Click() + + FAbout.Hide + +End diff --git a/app/examples/Drawing/Gravity/.src/FAbout.form b/app/examples/Drawing/Gravity/.src/FAbout.form new file mode 100644 index 00000000..26e26203 --- /dev/null +++ b/app/examples/Drawing/Gravity/.src/FAbout.form @@ -0,0 +1,39 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,48,21) + Text = ("About gbGravity") + Resizable = False + { PictureBox1 PictureBox + MoveScaled(1,1,9,9) + Picture = Picture["gravity.png"] + Stretch = True + } + { Label1 Label + MoveScaled(11,1,14,4) + Font = Font["+2"] + Text = ("gbGravity") + } + { Label2 Label + MoveScaled(25,1,22,4) + Text = ("Gravity like Simulator") + } + { Label3 Label + MoveScaled(21,5,25,4) + Text = ("Written in Gambas 1.9.46") + } + { Label4 Label + MoveScaled(31,9,15,3) + Text = ("by Iman Karim") + } + { Button1 Button + MoveScaled(1,13,46,4) + Text = ("Ok - kool!") + } + { Label5 Label + MoveScaled(11,17,26,3) + Font = Font["-1"] + Text = ("Thanks to the Gambas team!") + Alignment = Align.Center + } +} diff --git a/app/examples/Drawing/Gravity/.src/FMain.class b/app/examples/Drawing/Gravity/.src/FMain.class new file mode 100644 index 00000000..b65eb5b4 --- /dev/null +++ b/app/examples/Drawing/Gravity/.src/FMain.class @@ -0,0 +1,232 @@ +' Gambas class file + +' Written by Iman Karim +' http://home.inf.fh-rhein-sieg.de/~ikarim2s/ +' 14.11.2006 + +Private Objects As New Collection +Private world_Gravity As Float = 0.9 +Private world_Bounce As Float = 0.7 +Private world_SlideFloor As Float = 0.9 +Private isDrawing As Boolean + +Private Sub AddBall(Optional x As Integer = -1, Optional y As Integer = -1) + Dim cBall As New CBall + If x = -1 Then + cBall.x = Rnd(1, 100) + cBall.y = Rnd(1, 100) + Else + cBall.x = x + cBall.y = y + End If + cBall.ax = Rnd(-100, 100) + cBall.col = Rnd(1, 90000000) + objects.Add(cBall, Str(objects.Count + 1)) + cB.Add(Str(objects.Count)) +End + + +Private Sub DoGravity() + Dim myBall As CBall + For Each myBall In Objects + If togGrav.value Then myBall.ay = myBall.ay + world_Gravity '// Make Gravity + myBall.x = myBall.x + myBall.ax '// Move Ball + myBall.y = myBall.y + myBall.ay + If myBall.x >= dW.width - 5 Or myBall.x <= 0 Then '// Ball collidated on wall (left/right) + myBall.ax = (myBall.ax * world_Bounce) * -1 '// Reverse Ball direction and include world_Bounce + End If + If myBall.y > dW.Height - 10 Or myBall.y < 10 Then '// Ball collidated on Floor or Sky + If myball.y < 10 And togSky.value = False Then myball.ay = (myball.ay * world_Bounce) * -1 '// If Sky is disabled no not bounce + If myball.y > 10 Then myball.ay = (myball.ay * world_Bounce) * -1 '// On floor make bounce for sure + End If + If (((dW.Height - 10) - myBall.y) <= 1) And (Abs(myBall.ay) <= 2) And togGrav.Value Then '// If ball is n floor and to slow to jump up again stop the ball (y) + myball.y = (dW.Height - 10) + myball.ay = 0 + End If + + If myball.ay = 0 And myball.y = (dW.Height - 10) Then '// If ball is already on floor decrease the roll speed depending on world_SlideFloor factor. + If myball.ax < 0.1 Then + myball.ax = Abs(myball.ax) * world_SlideFloor * -1 + Else If myball.ax > 0.1 Then + myball.ax = Abs(myball.ax) * world_SlideFloor + Else + myball.ax = 0 '// If ball is moving to slow stop it + End If + End If + + If myball.y > dW.Height - 10 Then '// Make sure befor painting that the ball is inside your viewport. (floor) + myball.y = dW.Height - 10 + Else If myball.y <= 10 And togSky.Value = False Then + myball.y = 12 + End If + If myball.x > dW.width - 5 Then '// Make sure befor painting that the ball is inside your viewport. (left\right wall) + myball.x = dW.Width - 5 + Else If myball.x < 0 Then + myball.x = 0 + End If + Next +End + +Private Sub DrawWorld() + Dim myBall As CBall + Dim index As Integer + If isDrawing = False Then + isDrawing = True + Try Draw.begin(dW) + 'Draw.Rect(3, 12, dW.Width - 6, dW.Height - 17) + Draw.FillStyle = 1 + Draw.FillColor = Color.White + For Each myBall In Objects + index = index + 1 + Draw.Foreground = myBall.col + Draw.Ellipse(myBall.x, myBall.y, 5, 5) + If Str(index) = cB.Text Then + Draw.FillStyle = 0 + Draw.Foreground = Color.Red + If togFocus.value Then Draw.Ellipse(myBall.x - 3, myBall.y - 3, 11, 11) + lblX.Caption = Str(Round(myBall.x)) + lblY.Caption = Str(Round(myBall.y)) + If myball.y <= 0 Then + lblOutOfRange.Visible = True + Else + lblOutOfRange.Visible = False + End If + lblaX.Caption = Str(Round(myBall.ax)) + lblaY.Caption = Str(Round(myBall.ay)) + Draw.FillStyle = 1 + End If + Next + Draw.End + If cB.Text = "ALL" Then + lblX.Caption = "%null%" + lblY.Caption = "%null%" + lblaX.Caption = "%null%" + lblaY.Caption = "%null%" + lblOutOfRange.Visible = False + End If + isDrawing = False + End If +End + +Private Sub Render() + + DoGravity + If togClear.Value Then dW.Clear() + Wait 0.001 + DrawWorld + +End + + +Public Sub Form_Open() + cb.Add("ALL") +End + +Public Sub Button1_Click() + AddBall +End + +Public Sub Timer1_Timer() + + Render + +End + +Public Sub Form_Resize() + Timer1.Enabled = False + + 'DO + ' WAIT 1 + 'LOOP WHILE modGravity.isDrawing + + dw.Width = Me.Width - dw.Left - 10 + dw.Height = Me.Height - dw.top - 10 + Timer1.Enabled = True + + +End + + +Public Sub Slider1_Change() + + lblGrav.Caption = "Gravity: " & Str(Slider1.Value / 100) + world_Gravity = Slider1.Value / 100 + +End + + +Public Sub togGrav_Click() + + If Not togGrav.Value Then + lblGrav.Caption = "Gravity: off" + lblSlide.Caption = "Floor Slide: off" + Slider1.Enabled = False + Slider2.Enabled = False + world_SlideFloor = 1 + Else + Slider1.Enabled = True + Slider2.Enabled = True + lblGrav.Caption = "Gravity: " & Str(Slider1.Value / 100) + lblSlide.Caption = "Floor Slide: " & Str(Slider2.Value / 100) + world_SlideFloor = Slider2.Value / 100 + End If + +End + +Public Sub cmdRandomize_Click() + Dim myBall As CBall + Dim index As Integer + cmdRandomize.Enabled = False + Draw.begin(dW) + Draw.FillStyle = 1 + For Each myBall In Objects + index = index + 1 + Draw.Foreground = myBall.col + Draw.Ellipse(myBall.x, myBall.y, 5, 5) + If Str(index) = cB.Text Or cB.Text = "ALL" Then + myBall.ax = Rnd(1, 100) + myBall.ay = Rnd(1, 100) + End If + Next + Draw.End + cmdRandomize.Enabled = True +End + + + +Public Sub dW_MouseMove() + If togAddMore.Value Then + AddBall(Mouse.x, Mouse.Y) + End If +End + +Public Sub Slider2_Change() + + lblSlide.Caption = "Floor Slide: " & Str(Slider2.Value / 100) + world_SlideFloor = Slider2.Value / 100 + +End + + +Public Sub togAddMore_Click() + + If togAddMore.Value Then + lbladdmore.visible = True + Else + lblAddMore.visible = False + End If + +End + +Public Sub dW_MouseUp() + + togAddMore.Value = False + lblAddMore.visible = False + +End + +Public Sub lblAbout_MouseDown() + + FAbout.ShowModal + +End diff --git a/app/examples/Drawing/Gravity/.src/FMain.form b/app/examples/Drawing/Gravity/.src/FMain.form new file mode 100644 index 00000000..ece60668 --- /dev/null +++ b/app/examples/Drawing/Gravity/.src/FMain.form @@ -0,0 +1,144 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,112,73) + Text = ("gbGravity - Iman Karim") + Icon = Picture["gravity.png"] + Resizable = False + { lblGrav TextLabel + MoveScaled(53,1,14,3) + Text = ("Gravity 0.9") + Alignment = Align.Left + } + { Button1 Button + MoveScaled(1,1,20,6) + Text = ("Add a Ball") + } + { Slider1 Slider + MoveScaled(34,1,18,3) + MinValue = 1 + Step = 10 + Value = 90 + } + { lblSlide TextLabel + MoveScaled(53,4,14,3) + Text = ("Floor Slide 0.9") + Alignment = Align.Center + } + { Slider2 Slider + MoveScaled(34,4,18,3) + MinValue = 50 + Step = 10 + Value = 90 + } + { togAddMore ToggleButton + MoveScaled(1,8,20,6) + Text = ("Add more Balls") + } + { dW DrawingArea + MoveScaled(22,8,89,64) + Border = Border.Plain + Cached = True + { lblAddMore TextLabel + MoveScaled(1,1,24,10) + Visible = False + Background = &HFFFFDF& + Text = ("Click and hold on Gamefield and move your Mouse!") + Alignment = Align.Center + Border = Border.Plain + } + } + { Frame1 Frame + MoveScaled(1,15,20,53) + Text = ("Point Setup") + { TextLabel1 TextLabel + MoveScaled(1,3,13,4) + Text = ("Choose Point:") + } + { cB ComboBox + MoveScaled(1,7,18,4) + ReadOnly = True + List = Null + Text = ("ComboBox1") + } + { togFocus ToggleButton + MoveScaled(1,12,18,4) + Text = ("Focus Point") + Value = True + } + { cmdRandomize Button + MoveScaled(1,17,18,5) + Text = ("Randomize Kick") + } + { Separator1 Separator + MoveScaled(1,22,15,2) + } + { Label1 Label + MoveScaled(2,24,15,3) + Text = ("Current Ball X:") + } + { lblX Label + MoveScaled(1,27,14,3) + Text = ("0") + Alignment = Align.Center + } + { Label2 Label + MoveScaled(2,30,15,3) + Text = ("Current Ball Y:") + } + { lblY Label + MoveScaled(1,33,14,3) + Text = ("0") + Alignment = Align.Center + } + { Label6 Label + MoveScaled(2,36,16,3) + Text = ("Current Ball aX:") + } + { lblAX Label + MoveScaled(1,39,14,3) + Text = ("0") + Alignment = Align.Center + } + { Label4 Label + MoveScaled(2,42,16,3) + Text = ("Current Ball aY:") + } + { lblAY Label + MoveScaled(1,45,14,3) + Text = ("0") + Alignment = Align.Center + } + { lblOutOfRange Label + MoveScaled(1,48,18,4) + Visible = False + Foreground = &HFF0000& + Text = ("Ball out of Range") + } + } + { Timer1 #Timer + #MoveScaled(54,20) + Enabled = True + Delay = 50 + } + { lblAbout Label + MoveScaled(1,68,17,4) + Font = Font["Underline"] + Mouse = Mouse.Pointing + Text = ("About gbGravity...") + } + { togGrav ToggleButton + MoveScaled(22,1,11,6) + Text = ("Gravity") + Value = True + } + { togClear ToggleButton + MoveScaled(68,1,20,6) + Text = ("Clear each Frame") + Value = True + } + { togSky ToggleButton + MoveScaled(89,1,11,6) + Text = ("Sky") + } +} diff --git a/app/examples/Drawing/Gravity/.src/cBall.class b/app/examples/Drawing/Gravity/.src/cBall.class new file mode 100644 index 00000000..27e0ae75 --- /dev/null +++ b/app/examples/Drawing/Gravity/.src/cBall.class @@ -0,0 +1,9 @@ +' Gambas class file + +' This Class represents a single ball. +Public x As Float +Public y As Float +Public col As Integer +'PUBLIC weight AS Float 'Weight not implemented ;) +Public ax As Float +Public ay As Float diff --git a/app/examples/Drawing/Gravity/gravity.png b/app/examples/Drawing/Gravity/gravity.png new file mode 100644 index 0000000000000000000000000000000000000000..e4e425e74166b10fb70f988c22f6b679a4b3afc1 GIT binary patch literal 7084 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/OnScreenDisplay/.lang/ca.mo b/app/examples/Drawing/OnScreenDisplay/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..b4cb25ba685636a4a4076e6397e6c026014fe103 GIT binary patch literal 495 zcmYL_OG*Pl5Qf{=W;UYWPOw}{$4q=66QhDgPz*+mkEP{InY7MShwhF^j^F`2gr{)f zJ@hKpC~DCk|6koj@zWpk(=SSDN>~)ug&E;QDC<^O5Kx#ChQh{#QqQ7G!khS6(N9r1 zYEme7{Z$M7BV{l&oY^7J2#bDB>bi#kf++}$2NPgdwVi#SH0YyDUy<|1assD(kvbwi zFutGTIBK8jb9yr6qdveu3Dl}_RmU|QZ$fP?S=(5Vz8amN54z)!eU8Dp1dgrC3|!#Y z=CIwZ_|oi+v1e_C{Ep0XcYs~e9l|1kzg}$XsMY)r6j!RzzU4qH`^S9=5NH%uC7j_p zq&;+=LgTV?sJH&-${hFPbdyt?8SW;qb!S4<&Ib;2eP~_b6X5=L>l?K?{MZJ=as%Re zbfeprG86P~%mnr^V2&KA=7`GwgpA, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: OnScreenDisplay\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-16 23:35+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Masked window example" +msgstr "Exemple de finestra enmascarada" + +#: FOnScreenDisplay.form:8 +msgid "What time is it ?" +msgstr "Quina hora és?" diff --git a/app/examples/Drawing/OnScreenDisplay/.lang/cs.mo b/app/examples/Drawing/OnScreenDisplay/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..ed421ef5bf010f80c3203fef3cdf6da4b1fae34a GIT binary patch literal 439 zcmYL_OHRWu5QfcblNBJbg2n8S3rH-gP@qxcmZ*7D=>x$E)0oz6VpoZ2%MoDDf(szA z;1m_t;4Dlw^p`*VmOSJ6HNX4Y?*z4l93kh(4&o!F9*_fsBYVgKAsd9eq8%b{c;7|; zM8`umk@By91-zJw1Qt>ydI4g|vso(0?U)-dG7}&RkOreGam`s6FjrQcfwY`99i@IdMYbAAFXR_umUe&>wIUG zQ>&qs5emn>%rK7;OLHw((1adjP)dP%G6LNQjKh=K1ySe&d\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Masked window example" +msgstr "Příklad maskovaného okna" + +#: FOnScreenDisplay.form:8 +msgid "What time is it ?" +msgstr "Kolik je hodin ?" diff --git a/app/examples/Drawing/OnScreenDisplay/.lang/de.mo b/app/examples/Drawing/OnScreenDisplay/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..3a6343ba72ff5c01a5ad6e6a5edc4959d9853aad GIT binary patch literal 454 zcmYL_zfQw25XQ~FB^w|%hT9mB3rGxAD72wVY^QB(X~@3AVx5JIN98LH$1I7b!iq zNffjGE0*>dC$Lm{k}UyOa*?OVMsw-FsRe?vpd3_L$7D}9c08Q$AsVYP?L#N1-v%uK zL(%PpN!#lryo;}@_~Z@g(1$w_ba|ZcMgTX7ABV^HNn3c4w2t?U)HanaGd?s2Q3UNE z7Elp!5Jt72(ddd^?~1H?_9AD<`zRMaL_xbXC{|XYsFtcyW@c-T4_+L`Vx8}-bn4YJ z?NHOYx191gT)C4xl}ca0crJ}acRA=cd3o(CVolL-p~u-o>6s7ZN98\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Masked window example" +msgstr "Beispiel für ein transparentes Fenster" + +#: FOnScreenDisplay.form:8 +msgid "What time is it ?" +msgstr "Wieviel Uhr ist es?" + diff --git a/app/examples/Drawing/OnScreenDisplay/.lang/es.mo b/app/examples/Drawing/OnScreenDisplay/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..aef438830ae941c1886ca410e4f6f446d2d3bf67 GIT binary patch literal 361 zcmYMuOHRWu5C-6YV3Q4kHH+DU3+$>;+FFgEIXXlwR3}5nXoP-HfsW98&-3v8rT+V$yQOg8)LMe7 zKso4mnKe(ca3orgnY5KMI)IG!Zs~x*jOF7fP5dk+xqMdjkc7g?0OpM5Bu+_4VVDMS zbatO6%;%zVWMYM`R>Bz@K!3_PBs6BwVlj<)mxf`^#^W}w>T)YeNhVUP1K>0nOzV>^ z&buqsFt%*_aTK#%sjJ4N-!s}tjW=F5Si;E{cd=OsrLUn_3R_8cGoAEFbGKJ_d6bqg cU6_T^WdO|w<$Uk+W3>Hxho!LsWYzJ00YJ`P_5c6? literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/OnScreenDisplay/.lang/es.po b/app/examples/Drawing/OnScreenDisplay/.lang/es.po new file mode 100644 index 00000000..79e8bc33 --- /dev/null +++ b/app/examples/Drawing/OnScreenDisplay/.lang/es.po @@ -0,0 +1,15 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FOnScreenDisplay.class:79 +msgid "What time is it ?" +msgstr "¿Qué hora es?" diff --git a/app/examples/Drawing/OnScreenDisplay/.project b/app/examples/Drawing/OnScreenDisplay/.project new file mode 100644 index 00000000..77a12470 --- /dev/null +++ b/app/examples/Drawing/OnScreenDisplay/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.0 +Title=Masked window example +Startup=FOnScreenDisplay +Icon=icon.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Environment="GB_GUI=gb.qt4" +TabSize=2 +Translate=1 +Language=fr +ExecPath=/home/benoit/gambas/gambas.link/share/gambas/examples/Drawing/OnScreenDisplay/OnScreenDisplay +Maintainer=fabien +Vendor=Princeton +Address=fabien@arcalis +License=General Public Licence diff --git a/app/examples/Drawing/OnScreenDisplay/.src/FOnScreenDisplay.class b/app/examples/Drawing/OnScreenDisplay/.src/FOnScreenDisplay.class new file mode 100644 index 00000000..c42367af --- /dev/null +++ b/app/examples/Drawing/OnScreenDisplay/.src/FOnScreenDisplay.class @@ -0,0 +1,67 @@ +' Gambas class file + +Private $sLast As String +Private $MX As Integer +Private $MY As Integer + +Public Sub Form_Open() + + Me.Font = Font["64"] + Me.Resize(Me.Font.TextWidth("00:00:00") + 64, Me.Font.Height + 16) + Redraw +End + +Public Sub Timer1_Timer() + + Redraw + +End + +Private Sub Redraw() + + Dim hImage As Image + Dim sText As String + Dim iInd As Integer + + sText = Str(Time) + If sText = $sLast Then Return + + hImage = New Image(Me.Width, Me.Height, Color.Transparent) + + Paint.Begin(hImage) + Paint.Font = Font["64"] + Paint.LineWidth = 4 + + For iInd = 8 To 0 Step -1 + Paint.Brush = Paint.Color(Color.RGB(&h43 - iInd * &h43 / 8, &hC7 - iInd * &hC7 / 8, &hFF - iInd * &HFF / 8)) + Paint.DrawText(sText, iInd, iInd - 8, hImage.W, hImage.H, Align.Center) + Paint.Rectangle(iInd + 4, iInd + 4, hImage.Width - 14, hImage.Height - 14) + Paint.Stroke + Next + + Paint.End + + Me.Picture = hImage.Picture +' Me.Mask = True + $sLast = sText + +End + +Public Sub Form_KeyPress() + + If Key.Code = Key["Esc"] Then Me.Close + +End + +Public Sub Form_MouseDown() + + $MX = Mouse.ScreenX - Me.X + $MY = Mouse.ScreenY - Me.Y + +End + +Public Sub Form_MouseMove() + + Me.Move(Mouse.ScreenX - $MX, Mouse.ScreenY - $MY) + +End diff --git a/app/examples/Drawing/OnScreenDisplay/.src/FOnScreenDisplay.form b/app/examples/Drawing/OnScreenDisplay/.src/FOnScreenDisplay.form new file mode 100644 index 00000000..4c71a2e5 --- /dev/null +++ b/app/examples/Drawing/OnScreenDisplay/.src/FOnScreenDisplay.form @@ -0,0 +1,12 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(27,61,49,13) + Text = ("What time is it ?") + Mask = True + Border = False + { Timer1 #Timer + Enabled = True + Delay = 250 + } +} diff --git a/app/examples/Drawing/OnScreenDisplay/icon.png b/app/examples/Drawing/OnScreenDisplay/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..26cc8686565b8f4a62a9948c453962381e83725d GIT binary patch literal 1300 zcmV+v1?&2WP) zO-xi*7>1v7e}+LF6=4J<)P@=)DJeVB6q+=(EE-Kp6Y5S&BxzlMwe7;1F2scfY!fxu zw5E+wNmES<8^S_aAzd{ZQ)$xRPZg`kARsdg^LOt(Ul#*|KWKp#-Sl1EbM8IgdB5-d z&U+91j~5K}b4QoFTvCQImy>cF5P9MwEucLoyvQd3Trwvo|9s9Z0y-# z1YCKjo(q7Nasfyrk|<>$B4qhYjG4?(3d1lK9C)F~UbX^d7$BfMVd-~#3S_#9&z*o$ z3eg(T8pBj`6HrRAd-rbo`ufCTjafdLVb%y{R{ogyOA(9j@& zY~K8Vw6y&qAAS0R)PLM8b5>y6wrv3P_4NU8_3BkdMn>4aeLDcLSPX!!t}be7YS^=9 z&#bnOIcaBS=OZy^^w-*ZdJp91=K~OlL^5;XaF~LE0swaG*g;1}2i4WpoIih_{{DU{ zDk|pGe0+QyfZW_%04&=^3y5e;)0mq;C=>!95C~-E0)YU-!@~gN<>gUVS4VMiF(*%+ zq_nh@wzjr839Mba7J$LQ!Rh{*-O@Ck(uD2p?Eq9&RRK^^QbIvN0jE!&2H@DSV+4ai zhK7b{YH9)?nM}?}ptiOafaAxHKk-gPW}Q$e$@tr;Q>Vzu$zku_y;N3KQdL#Op+kpg zZf*vkyStmSXV22x+e=wl8RyQOqp`7(O`A4l#jYjfh5T zjj0ss#FlaYN@bwYXh&n@gX;^T_M{JzgQBrl6hyO zltL}wc4QeIu_TZhMMSVI1IuwWjw^VcM#LlK%?AH3APAPSFqFyUR1`Jc=%PJP3hUnR zB$3DjQ_%-$olFzIe+B;R0Jbj+!!Q@Ou?Vi4M0=VW53ZA%2=Patf~4}Oj)#avCm^^Q z%ZXj%=JiM2&!6QdxUQ6dnY)<#Cjg#yh)mpP^xhy>e!EC^u8o-o&SZ?+$v8QMtI1jA z$9gFrf~$&fT#cG3y;9s9njkb3#&wbl_Xh=@>o64^XVo%`%JK~q6t5vX79~4Bz+}{B zy{#oPPLbrbXt^7W4pIAJJ@h@7z(@+>(#y3a4d_D%V82k+oBq+c}ribtV0000< KMNUMnLSTaPhgkvu literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Painting/.directory b/app/examples/Drawing/Painting/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Drawing/Painting/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Drawing/Painting/.hidden/screenshots/2014-12-14.png b/app/examples/Drawing/Painting/.hidden/screenshots/2014-12-14.png new file mode 100644 index 0000000000000000000000000000000000000000..2ec5e978ff0e8c1516da613a362ec3b1c34db905 GIT binary patch literal 90069 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 Fe*nDbN8SJc literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Tablet/.icon.png b/app/examples/Drawing/Tablet/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d18cb788766d05515525542556383861ffcb9801 GIT binary patch literal 3536 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 KMNUMnLSTZgF~HFP literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Tablet/.project b/app/examples/Drawing/Tablet/.project new file mode 100644 index 00000000..00bb71cc --- /dev/null +++ b/app/examples/Drawing/Tablet/.project @@ -0,0 +1,15 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Tablet +Startup=FMain +Icon=Icon.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.form +Description="Tablet event example.\n\nThis example shows how to get extended event information about tablet pens. It draws colored lines whose width depends on the pen pressure." +Authors="Benoît Minisini, from an idea of Ricardo Díaz Martín" +TabSize=2 +Vendor=Example +Packager=1 +Screenshot=2014-12-14.png diff --git a/app/examples/Drawing/Tablet/.src/FMain.class b/app/examples/Drawing/Tablet/.src/FMain.class new file mode 100644 index 00000000..eef526de --- /dev/null +++ b/app/examples/Drawing/Tablet/.src/FMain.class @@ -0,0 +1,178 @@ +' Gambas class file + +Private Const MAX_LINE_WIDTH As Float = 20 + +Private $hBuffer As Picture +Private $X As Float +Private $Y As Float +Private $aCoord As Float[] +Private $iColor As Integer +Private $hBound As Rect +Private $fPressure As Float + +Public Sub _new() + + Dim sLabel As String + Dim Y As Integer + + $hBuffer = New Picture(1024, 768) + $hBuffer.Fill(Color.White) + dwgDraw.ResizeContents($hBuffer.Width, $hBuffer.Height) + +End + +Public Sub Form_Open() + +End + +Private Sub PaintStroke(hDest As Picture) + + Dim I As Integer + Dim hTemp As Image + + If Not $aCoord Then Return + If $aCoord.Count < 6 Then Return + + hTemp = New Image($hBound.W, $hBound.H, Color.Transparent) + + Paint.Begin(hTemp) + Paint.LineCap = Paint.LineCapRound + Paint.Brush = Paint.Color($iColor) + Paint.Translate(- $hBound.X, - $hBound.Y) + + For I = 0 To $aCoord.Max - 3 Step 3 + + Paint.MoveTo($aCoord[I], $aCoord[I + 1]) + Paint.LineTo($aCoord[I + 3], $aCoord[I + 4]) + Paint.LineWidth = Max(0.5, $aCoord[I + 2] * MAX_LINE_WIDTH) + Paint.Stroke + + Next + + Paint.End + + 'hTemp.Opacity(0.5) + Paint.Begin(hDest) + Paint.DrawImage(hTemp, $hBound.X, $hBound.Y,,, 0.5) + Paint.End + +End + + +Public Sub dwgDraw_Draw() + + Dim hDraw As Picture + + hDraw = $hBuffer.Copy() + PaintStroke(hDraw) + + Draw.Picture(hDraw, - dwgDraw.ScrollX, - dwgDraw.ScrollY) + +End + +Private Sub UpdateInfo(Optional bUp As Boolean) + + Dim iColUp, iColDown As Integer + + Select Case Pointer.Type + Case Pointer.Cursor + lblType.Text = "Cursor" + Case Pointer.Eraser + lblType.Text = "Eraser" + Case Pointer.Mouse + lblType.Text = "Mouse" + Case Pointer.Pen + lblType.Text = "Pen" + End Select + + lblX.Text = Format($X, "0.000") + lblY.Text = Format($Y, "0.000") + lblXTilt.Text = Pointer.XTilt + lblYTilt.Text = Pointer.YTilt + lblPressure.Text = Pointer.Pressure + lblRotation.Text = Pointer.Rotation + + iColUp = Color.Default + iColDown = Color.LightForeground + + If bUp Then + If Mouse.Left Then panButton1.Background = iColUp + If Mouse.Middle Then panButton2.Background = iColUp + If Mouse.Right Then panButton3.Background = iColUp + Else + panButton1.Background = If(Mouse.Left, iColDown, iColUp) + panButton2.Background = If(Mouse.Middle, iColDown, iColUp) + panButton3.Background = If(Mouse.Right, iColDown, iColUp) + Endif + +End + + +Public Sub dwgDraw_MouseDown() + + $X = dwgDraw.ScrollX + Pointer.X + $Y = dwgDraw.ScrollY + Pointer.Y + $fPressure = 0.1 + + UpdateInfo + timScroll.Start + + If Not Mouse.Left Then Return + + $hBound = New Rect($X - MAX_LINE_WIDTH, $Y - MAX_LINE_WIDTH, MAX_LINE_WIDTH * 2, MAX_LINE_WIDTH * 2) + + $aCoord = [$X, $Y, If(Pointer.Type = Pointer.Mouse, $fPressure, Pointer.Pressure)] + +End + +Public Sub dwgDraw_MouseMove() + + $X = dwgDraw.ScrollX + Pointer.X + $Y = dwgDraw.ScrollY + Pointer.Y + + UpdateInfo + + If Not Mouse.Left Then Return + + $fPressure = Min($fPressure + 0.01, 1) + + $aCoord.Add($X) + $aCoord.Add($Y) + $aCoord.Add(If(Pointer.Type = Pointer.Mouse, $fPressure, Pointer.Pressure)) + + $hBound = $hBound.Union(Rect($X - MAX_LINE_WIDTH, $Y - MAX_LINE_WIDTH, MAX_LINE_WIDTH * 2, MAX_LINE_WIDTH * 2)) + + 'Debug $X;; $Y + + dwgDraw.View.Refresh($hBound.X - dwgDraw.ScrollX, $hBound.Y - dwgDraw.ScrollY, $hBound.W, $hBound.H) + +End + +Public Sub dwgDraw_MouseUp() + + UpdateInfo(True) + timScroll.Stop + + PaintStroke($hBuffer) + $aCoord.Clear + +End + +Public Sub timScroll_Timer() + + dwgDraw.EnsureVisible($X - 16, $Y - 16, 32, 32) + +End + +Public Sub btnClear_Click() + + $hBuffer.Fill(Color.White) + dwgDraw.View.Refresh + +End + +Public Sub btnColor_Click() + + $iColor = Last.Background + +End diff --git a/app/examples/Drawing/Tablet/.src/FMain.form b/app/examples/Drawing/Tablet/.src/FMain.form new file mode 100644 index 00000000..54a38ae5 --- /dev/null +++ b/app/examples/Drawing/Tablet/.src/FMain.form @@ -0,0 +1,162 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,90,71) + Icon = Picture["Icon.png"] + Arrangement = Arrange.Horizontal + { dwgDraw ScrollArea + MoveScaled(1,1,52,47) + Expand = True + Border = False + NoBackground = True + Tablet = True + } + { timScroll #Timer + #MoveScaled(30,54) + Delay = 50 + } + { Separator1 Separator + MoveScaled(54,27,0,12) + Visible = False + } + { Panel2 Panel + MoveScaled(56,8,30,59) + Arrangement = Arrange.Vertical + Margin = True + { panInfo Panel + MoveScaled(0,0,28,58) + Margin = True + Border = Border.Plain + { ToggleButton4 ToggleButton btnColor + Name = "ToggleButton4" + MoveScaled(4,43,6,6) + Background = &HFF0000& + Radio = True + } + { Label1 Label + MoveScaled(1,1,10,3) + Foreground = &H7F7F7F& + Text = ("Type") + Alignment = Align.Right + } + { lblType Label + MoveScaled(13,1,14,3) + Expand = True + } + { Label2 Label + MoveScaled(1,4,10,3) + Foreground = &H7F7F7F& + Text = ("X") + Alignment = Align.Right + } + { lblX Label + MoveScaled(13,4,14,3) + Expand = True + } + { Label3 Label + MoveScaled(1,7,10,3) + Foreground = &H7F7F7F& + Text = ("Y") + Alignment = Align.Right + } + { lblY Label + MoveScaled(13,7,14,3) + Expand = True + } + { Label4 Label + MoveScaled(1,10,10,3) + Foreground = &H7F7F7F& + Text = ("XTilt") + Alignment = Align.Right + } + { lblXTilt Label + MoveScaled(13,10,14,3) + Expand = True + } + { Label5 Label + MoveScaled(1,13,10,3) + Foreground = &H7F7F7F& + Text = ("YTilt") + Alignment = Align.Right + } + { lblYTilt Label + MoveScaled(13,13,14,3) + Expand = True + } + { Label6 Label + MoveScaled(1,16,10,3) + Foreground = &H7F7F7F& + Text = ("Pressure") + Alignment = Align.Right + } + { lblPressure Label + MoveScaled(13,16,14,3) + Expand = True + } + { Label7 Label + MoveScaled(1,19,10,3) + Foreground = &H7F7F7F& + Text = ("Rotation") + Alignment = Align.Right + } + { lblRotation Label + MoveScaled(13,19,14,3) + Expand = True + } + { Panel1 Panel + MoveScaled(9,25,10,5) + { panButton3 Panel + MoveScaled(8,0,2,5) + Border = Border.Plain + } + { panButton1 Panel + MoveScaled(0,0,2,5) + Border = Border.Plain + } + { panButton2 Panel + MoveScaled(4,0,2,5) + Border = Border.Plain + } + } + { btnClear Button + MoveScaled(5,34,18,7) + Text = ("Clear") + Picture = Picture["icon:/large/delete"] + } + { Separator2 Separator + MoveScaled(0,32,28,1) + } + { ToggleButton1 ToggleButton btnColor + Name = "ToggleButton1" + MoveScaled(18,50,6,6) + Background = &H000000& + Radio = True + Value = True + } + { ToggleButton2 ToggleButton btnColor + Name = "ToggleButton2" + MoveScaled(4,50,6,6) + Background = &HFF7F00& + Radio = True + } + { ToggleButton3 ToggleButton btnColor + Name = "ToggleButton3" + MoveScaled(11,50,6,6) + Background = &HFFFF00& + Radio = True + } + { ToggleButton5 ToggleButton btnColor + Name = "ToggleButton5" + MoveScaled(18,43,6,6) + Background = &H007FFF& + Radio = True + } + { ToggleButton6 ToggleButton btnColor + Name = "ToggleButton6" + MoveScaled(11,43,6,6) + Background = &H7FFF00& + Radio = True + } + } + } +} diff --git a/app/examples/Drawing/Tablet/Icon.png b/app/examples/Drawing/Tablet/Icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a526512bb5a2c1d369e4aefc5d39b60c5914ee9f GIT binary patch literal 3753 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 V*+a}sDck@6002ovPDHLkV1oU`2~Yq4 literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/.directory b/app/examples/Games/BeastScroll/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Games/BeastScroll/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Games/BeastScroll/.icon.png b/app/examples/Games/BeastScroll/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7de9e813eee880887318fe133be91adb7c569805 GIT binary patch literal 3371 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 Bc>DkW literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/.project b/app/examples/Games/BeastScroll/.project new file mode 100644 index 00000000..dd0bc9a7 --- /dev/null +++ b/app/examples/Games/BeastScroll/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=BeastScroll +Startup=MMain +Icon=logo.png +Version=1.1.0 +Component=gb.image +Component=gb.sdl2 +Component=gb.sdl2.audio +Description="Parallax scrolling demo.\n\nThis example shows a parallax scrolling with a music playing in the background. It is based on the SDL2 components." +TabSize=2 +Language=fr +Maintainer=benoit +Vendor=Example +Address=benoit@localhost +License=General Public Licence +Packager=1 +GambasVersion=3.7 diff --git a/app/examples/Games/BeastScroll/.src/MMain.module b/app/examples/Games/BeastScroll/.src/MMain.module new file mode 100644 index 00000000..cb9fad40 --- /dev/null +++ b/app/examples/Games/BeastScroll/.src/MMain.module @@ -0,0 +1,215 @@ +' Gambas module file + +'Public Screen As New Window As "Screen" +Public ciel As Image +Public montagnes As Image +Public barriere As Image +Public sol1 As Image +Public sol2 As Image +Public sol3 As Image +Public nuage1 As Image +Public nuage2 As Image +Public nuage3 As Image +Public nuage4 As Image +Public arbre As Image +Public fireworks As Image +Public scrolltext As Image + +Public scroll As Integer +Public scrollb As Integer +Public scroll1 As Integer +Public scroll2 As Integer +Public scroll3 As Integer +Public scroll4 As Integer +Public scroll5 As Integer +Public scroll5b As Integer +Public scroll6 As Integer +Public speed As Integer + +Private $hWindow As Window +Private $nFrame As Integer + +Public Sub Main() + + $hWindow = New Window As "Window" + + With $hWindow + .Resize(640, 480) + .Framerate = 100 + .Resizable = False + .Show() + End With + + Music.Load("b-title.mod") + + ciel = image.Load("bgd1_ciel.png") + nuage1 = image.Load("sprite_nuages1.png") + montagnes = image.Load("bgd2_montagnes.png") + sol1 = image.Load("bgd3_sol1.png") + sol2 = image.Load("bgd4_sol2.png") + sol3 = image.Load("bgd5_sol3.png") + nuage1 = image.Load("sprite_nuages1.png") + nuage2 = image.Load("sprite_nuages2.png") + nuage3 = image.Load("sprite_nuages3.png") + nuage4 = image.Load("sprite_nuages4.png") + barriere = image.Load("sprite_barriere.png") + arbre = image.Load("sprite_arbre.png") + fireworks = image.Load("fireworks.png") + scrolltext = image.Load("scrolltext.png") + + speed = 2 + scroll = 0 + scrollb = 0 + scroll1 = 0 + scroll2 = 0 + scroll3 = 0 + scroll4 = 0 + scroll5 = Rnd(0, 640) + scroll5b = Rnd(0, 640) + scroll6 = 0 + + Music.Play(-1, 1) + +End + +Private Sub DrawText(sText As String, X As Integer, Y As Integer) As Integer + + Dim I As Integer + Dim J As Integer + + Draw.Background = Color.Black + For I = -1 To 1 + For J = -1 To 1 + Draw.Text(sText, X + I, Y + J) + Next + Next + Draw.Background = Color.White + Draw.Text(sText, X, Y) + + Return Y + Draw.Font.TextHeight(" ") + 4 + +End + +Public Sub Window_Draw() + + 'Dim hImage As Image + + '$hWindow.Clear + + '$hWindow.Clear + 'Goto PRINT_TEXT + + Dim Y As Integer + + scroll = scroll + speed + Dec (scroll1) + scroll2 = scroll2 - 2 + scroll3 = scroll3 - 3 + scroll4 = scroll4 - 4 + scroll5 = scroll5 - 5 + scroll5b = scroll5b - 2 + scroll6 = scroll6 - 5 + + If (scroll = 320) Then speed = -2 + If (scroll = -960) Then speed = 2 + scrollb = scroll + If (scrollb < - 640) Then scrollb = - 640 + If (scrollb > 0) Then scrollb = 0 + If (scroll1 = - 640) Then scroll1 = 0 + If (scroll2 = - 640) Then scroll2 = 0 + If (scroll3 < - 640) Then scroll3 = scroll3 + 640 + If (scroll4 < - 640) Then scroll4 = scroll4 + 640 + If (scroll5 < - 640) Then scroll5 = scroll5 + 1280 + If (scroll5b < - 640) Then scroll5b = scroll5b + 1280 + If (scroll6 < - 640) Then scroll6 = scroll6 + 640 + + #If False + + hImage = New Image($hWindow.Width, $hWindow.Height) + + hImage.DrawImage(ciel, 0, 0) + hImage.DrawImage(montagnes, scroll1, 200) + hImage.DrawImage(montagnes, scroll1 + 640, 200) + hImage.DrawImage(sol1, scroll2, 420) + hImage.DrawImage(sol1, scroll2 + 640, 420) + hImage.DrawImage(sol2, scroll3, 430) + hImage.DrawImage(sol2, scroll3 + 640, 430) + hImage.DrawImage(sol3, scroll4, 450) + hImage.DrawImage(sol3, scroll4 + 640, 450) + + hImage.PaintImage(nuage1, scroll6, 0) + hImage.PaintImage(nuage1, scroll6 + 640, 0) + hImage.PaintImage(nuage2, scroll4, 82) + hImage.PaintImage(nuage2, scroll4 + 640, 82) + hImage.PaintImage(nuage3, scroll3, 120) + hImage.PaintImage(nuage3, scroll3 + 640, 120) + hImage.PaintImage(nuage4, scroll2, 138) + hImage.PaintImage(nuage4, scroll2 + 640, 138) + hImage.PaintImage(barriere, scroll5, 440) + hImage.PaintImage(arbre, scroll5b, 140) + hImage.PaintImage(fireworks, scrollb, 0) + hImage.PaintImage(scrolltext, scrollb + 640, 0) + + Draw.Image(hImage, 0, 0) + + #Else + + Draw.Image(ciel, 0, 0) + Draw.Image(montagnes, scroll1, 200) + Draw.Image(montagnes, scroll1 + 640, 200) + Draw.Image(sol1, scroll2, 420) + Draw.Image(sol1, scroll2 + 640, 420) + Draw.Image(sol2, scroll3, 430) + Draw.Image(sol2, scroll3 + 640, 430) + Draw.Image(sol3, scroll4, 450) + Draw.Image(sol3, scroll4 + 640, 450) + Draw.Image(nuage1, scroll6, 0) + Draw.Image(nuage1, scroll6 + 640, 0) + Draw.Image(nuage2, scroll4, 82) + Draw.Image(nuage2, scroll4 + 640, 82) + Draw.Image(nuage3, scroll3, 120) + Draw.Image(nuage3, scroll3 + 640, 120) + Draw.Image(nuage4, scroll2, 138) + Draw.Image(nuage4, scroll2 + 640, 138) + Draw.Image(barriere, scroll5, 440) + Draw.Image(arbre, scroll5b, 140) + Draw.Image(fireworks, scrollb, 0) + Draw.Image(scrolltext, scrollb + 640, 0) + + #Endif + +PRINT_TEXT: + + Inc $nFrame + 'If $nFrame = 1000 Then $hWindow.Close + + 'Print $hWindow.Framerate; " FPS\r"; + + Y = 10 + Draw.Font.Size = Font.DefaultHeight * 2 + Y = DrawText($hWindow.Framerate & " FPS", 10, Y) + Draw.Font.Size = Font.DefaultHeight + Y = DrawText("[F1] Toggle fullscreen", 10, Y) + Y = DrawText("[F5] Take screenshot to ~/BeastScroll.png", 10, Y) + Y = DrawText("[ESC] Quit", 10, Y) + +End + +Public Sub Window_Close() + + Music.Stop() + +End + +Public Sub Window_KeyPress() + + Select Case Key.Code + Case Key.F1 + $hWindow.FullScreen = Not $hWindow.FullScreen + Case Key.Esc + $hWindow.Close + Case Key.F5 + $hWindow.Screenshot().Save("~/BeastScroll.png") + End Select + +End diff --git a/app/examples/Games/BeastScroll/b-title.mod b/app/examples/Games/BeastScroll/b-title.mod new file mode 100644 index 0000000000000000000000000000000000000000..8eee2218f08f32104332087ef5f84dfd06a1f84c GIT binary patch literal 56838 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 I(C}yfU+STyG5`Po literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/bgd1_ciel.png b/app/examples/Games/BeastScroll/bgd1_ciel.png new file mode 100644 index 0000000000000000000000000000000000000000..f0a38160c3808fbbf328c2e6b84f31b3df599800 GIT binary patch literal 6797 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/bgd2_montagnes.png b/app/examples/Games/BeastScroll/bgd2_montagnes.png new file mode 100644 index 0000000000000000000000000000000000000000..37ca6393ded1fa08b5503ff78879b22c9e87bf1a GIT binary patch literal 177395 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/fireworks.png b/app/examples/Games/BeastScroll/fireworks.png new file mode 100644 index 0000000000000000000000000000000000000000..f22f50eb033a38f56d3a4d3fd614432ed2dc52de GIT binary patch literal 1560 zcmeAS@N?(olHy`uVBq!ia0y~yU}|7sV4T3g3>4wiRTKwuyaIeeT!GxpQ@b`z=>(D+ zCwII%w0ra9?p+<#TS`+lW(KcMa@>&Pw7b;fP&-nfzx>SX z;tRc5hq{Zlv=pu_O1(6pa9wj&kDvYocg=odky)CoOLf^c7&31$<=kS-vPz#}nGVxx z1CDh@!i)8U=NL*Y@YY?O?YE&RbDopxY&*FHCL#;fIVM=kFDQ!NRui|-U3itR(sD=H zrQRB=k{o6QShecPE{gQtP!hMwOM97(=t^7hjaHKD9ORaH%CGQ}U*@f_I#_#On&ah` zqz5x9x3wm(E^wb8sXjAMVwSJ;Jb&q>VakicRMuu%9;i!Lk?gr9KyR&+XY{+8|`-HI~{JeI?8rsb;{kvWhWbhcf=WN4AGqHE~)y|gt? z!a=~{pn)+IJpX??WMY!#rWlo~O{T%`Uaq@QoqPIwj>Z zDvUuw3Yv_QgcMc=%0GCb8NA$gzG3mTHALY7i>|&h&Az_&{)WQK&vc`=^IF{9m;8L)-Rf_Adp70&Z)|So`=1-?-X|w*VqN#> z$;!*==jK`0KYXwST|;J#Js|Y(M)CVJ9XPRfnKaVl)g!(*ZrR!B_74Jh8W` S#k5L5MT)1ZpUXO@geCyxqq<}O literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/logo.png b/app/examples/Games/BeastScroll/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ef6427938a7b4bd52f271e6ba61d4484042b270f GIT binary patch literal 4768 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 BB@_Sv literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/sprite_arbre.png b/app/examples/Games/BeastScroll/sprite_arbre.png new file mode 100644 index 0000000000000000000000000000000000000000..a117f15c8611aa3428d53f567caf9baeb5717b5d GIT binary patch literal 73213 zcmV*SKwZCyP)F004jhNkl;Mo6}7xj`U$P(veuTEGahkBo@d+;{IiXYaM?0fCEyhbC;@x|iz4w}H&hPwwzY`v>$LsNWydJN|>&0B(_+4-EH-Gn={4@PI9KOr{ooHg4g{a2tA3_G!0*=e*6)6u zQ;cH3T=>cV@gK@3`7Pv~{t@GpIl_u&eujeqvefAAT*F5mP$U*$R) zwO00rj*9@F^rd;cK1J*Co%saT+rIO)Zo7%P8=w!&*BzS*KleX=Vw-na-|*e9^`UO~ z>A(6*AJ^-={$KhcZ^IRv6R1^Ss*HYzpZ%-9`f0ttZ~2}#xzxsHJJ1dKr2O;`|NY0$ z*k|#2oB@0yYiJC+0@dj_T?Lm>!{b2mukNnz4-8`@V1F!K` zuc;$QG0HG93?uZ$TmJZ0`m{Ix-hcOxKRuhUHtM$E#jx)D^bdc0f$Z`6^smPlz$dbH z6{-M^n^9!o7ygG|{)8X)w|w`TyfdR!D5Fz0M%jGymwVkiUgI+d>L3&a5kW-I>gdp6 zoT2nj>Z|^-AN;P5xqjCHv>Ir`#g7aI@g_1 z2M2fNgYeUT`4c~3-}>EeavM52C|V4uGl*ex!S^FS^TWULg0KCJf9PxdefQtPcwX6@ zLLD1*aOzM|DOgw9q)gX_Oe4SaH~-1U{TaXU`(EdzD$~Akd2xVA`L&<@=P&p=Z~gw) zxo#U}1Q(}If?d@QTZVl7cf7&>&fomxN~Xu_lV6WZfRAH+;~)J}@8yb8M%)Y5jb`l= zf4%Q|qt8r;7uwvAK2y9!I$w>Zop1X7H~CNh@bj~aH~+z}@%Oyz-Ap!7Hj129N*O4{ z5rp1g-ot$8^ckkZ6|a5!Yy2Pm?SKApZ8%XuRq4&RzC18pPkhB&zQ|wtIWOZU{@b5l zU+XP@>`T4NC8ZWjEPl5rvmW@`@A)#nbN(b}r#p%&Km6zaSNZ1edz0_`^qIb$j~9vp0JAX;!!4%g20qiZ-Q=k-av7cTY>@UgMojSM>SbN~H^Il;gC4c`2~ zd@v6CPPazcbauC3b5jOp6(!KUGi_Lw*3AR7uJ=7Q}jKlj%j;{>0{^%w*A7}i_9 z_YF>gIt)lrTsqwwhy8&^&+fVR^abyE|9g1vGw(2^+cc@Y*G9X^?rt}8WzHwG=t(zav0M2QPx|(`%TW!*la3D2WB7uE~O&~VhTkfpArxp)EQ0`Wah0u@Rk1F z2mc!Y-}D1t>6DoPWu862GM!O%v@6_r8}MMQA` zchoyYUpTh^{lES3)ZK<^XWS{nZlHC+q~q$8N&=5@CET28>P(kW5{;*oaf~m}Tj%Q0 zAui9kFzggmsXL)h6JPgzU+R4pe(3-H7anH=pXT*A0DOe?)_>{sj?C1dprv8b@YrUk zz_2Zx-r9228i%U}B5Y0ucDF0T2~cM!AZ{5O1ji98NEe1-;MP-Hwzm{;hOIK}gi;0f zMmND3z!W#AL%d&~Iv`%hf)pY$m>{C47B;()gI|5p-+ylxYr9jmQ{6EyAfOVPJFbS& z4GxctN6$=vaqc7a&IrJKFs|=K&>U;WHXW%mGC)J(PpC#g_B!b#*;$b3cWSjq2XeTrx8f-nYaGQ*LvR@KmS)h z{tm!fzxVatH&@8$bc}+d3X)Z0gslAIU>*7_v^n0Y%|`2nsDdkaJZkO*a|fU34!9s* z5K-TC(=FYV5TwP4eGmNJr;?8`S zxxARslYwz`%sbr-5uui7(H*Xh%V!%AX?tqa&d>56)xd*jW2_>KPa z|HH35&H_G7>u~`1DC?*GyLcGC|IhvbM;br;pZvJI^-sLUXTQd+BWesg<>Wk4V}Ce< z8t$F8hyC@8nX}oB)RTf}#l2zT2pto{y&x{QIF4gDW*sx5nc%LpK4ZP3rBJtple+`k zI~9{oznYo*0o>`W(5zCd)ApU~i^dzj?Th@ZJ^?1&zqpSh{G*?LeoOEz-}?rqHgs@Y zuMuf&uc#;sC|f`e9YX|&$4 z9;bhsX6#^y6tqr!;~#mW|J>hr90Wcc>+wbY1lOCs>vMeAC~Y#JFpd=j=0gLFGAjG) z#>KOHyeVgQPC2`^rPRVa&m5)+EwI^D#!W$sMF!BF!}ZM7#h%_fRK;7NUn;ap9Y(fi zh3&aeMrh61Ume(AUm-Z#lM^Ifpt(+noueey29@)J=SeDn9e z(ObD@*aF4q-k94&Ycpmo@_kbd51suzqm;s}mu%SG9uVNsy~cCz*>m|!WDDC{8}7dR zg!3mV^#stRLJB0adt;s_+yt@0^w7DwXQ&LE-#KB}R0iGf(?9b37U7@$KmFVO=l`$& z#p7r8MO=?FfKOn(M|DaaRxqx+(Lapysp#93D7+XlQAau~Sc- zx)aJ^pw99VsuCx>4m67%`gGMnp$vuX*~oYX3^N@A5z0o`4h0-a5k!Ke=;pW!Iw+gl zL)7pW4L4wW;2Zz=S9qxkIx!Rj?YJb4fg>u2R0PEl#2vDn!8P#!MFfBGg(@*EqFX#l4~%VSEy#!mtBz#d~2W1GjEhd@A&g-+%9gTs(V?R!4>n zS_*{-ZX%$f)LQA*Xl9h1aPm~en$V>&wTW>U=yT`#(E)JueEXTc?Y}42;{fn6u5bDy zuXU*nugYY5rhR9gjm>Fc92L8k@f|)aSm@Xadh3&~e-6?2i-e=$6l+DS8TQ3n@I-8S7Y1+ZKx<6qkyR(7q zw#4U^P)`b#QaZW?cF-qhI?QyULlT{q9-mB|w|vhV{Ad6AWBtI3xgKB4k72#_dtT=@ zTr=(h{eSdu;^LWWz&L+u!^xe2QsYwg2AB6H?!SBD^0|haQ@6tQR`mGmAm|2k(FNXP&$Q-T-J7}pz6pBe&XcE{+}Xr+c(CWuv)5D=&YwD??h0*x z%`{E)S(vYlX*y80&h3|OIJ>i<4DtE~n0sUxfaCPmnD&O+$XIvuCLFF0D9*UuMqqN` z=YQnWByawGupVasA7#Dud*9$}57Z5~HoQ9!K%bjqv(ab6Bu-8`%-4-a_pbQBZ(Rz& z`AfH6HyciF2XI!l5obvr|-(NmnSPQg(%L>nQmiDj;&>+*XiEQ9wFI z$Gqcoq;znj`;2u%Tu@O|Jz0F&*2`e9lrliF9AhJ2-P<$m!Di=h9fYXM`+F|$Uo&n7 zw&y!`cPg9Hfo-qM2jS|$p85VBvqo!$*($iA`PqvIE|R=}z(UFh#Ue16MJv(8Feb*a z(#`q$@A)$SJAeJ3J`Mse!g?G4KEis-_q@S<*i$x!WUZ_c$URRv)}5*)(tLNcNU-*R zo5|J1(|Z{=>cghQ#x509kOqy0i=eI?_RiJ42^HZS7;8alq{8BWWt*@^wH~EH0CIGt z4kM>eIL?F)PPYT1v61y6YIi6KOVfmEmNU@HM2GIUy-ajBFDEkKZnx*NUC7>0;q7Nyhm!_M`? zYos*n?uIh#C}m()8L^B+A90-xl18~{Gj8XLn7To-c-LA5e&ow8A^89rst zeh$Q4DAaMw=^c<)4Q?$?+B!tds}wYAd?~?@>kHAJIa!3kv2`3XW1ft|MWbK0OctVL zP{+!!t>8Uu$qeeYAmf0`M7J5Aojw^?7c}C~Pew3d zx}ItK8Jm^q${Dr;FMZV=M+}jU_oLRp&1lw{TBp_)DkT-l1~zrTQ3PkYHtb@;W={s7 z;O=zq^giP_sugjkPmSrS(++VDFv;wpFm4Ka`{ayH9oIDFT(CA-9?RMd#w_nd|!oedhEIR2k3$ z7x!ka9$bUN?v8W*Nv1FA2~VSu-$E_I)Z8F z9v%x>UzGJY0DPGBw(otD3lnZ|n2hU>y&M9__rq|>ii|RB;j5y`ut>l>;`R)KN|Hx)XEO#p%8@N z=zEV^-lv&+@4k?sZoJ*ua`=AD;o>i)#%ock~i;KqDt z+<)Kk?{2^1+|QppVRyP^95?jYsSNzg$7!1SczyWwH~_rB`lj!CqvI3BooRCR7c-BZ zJFrn@^U_Y;4RjGYk-5)P=kh@V>(SoQiR%Y5h;X(Y*_4WRnD!Hop1X!(oZgL1d-dFm zxpDebVS74IhY-v3)$_>!~d#XSqS$>E571-6*BN=5)lhA%md zWe5P~aNXGL?tZ2pkKa@4aRB(B_15otlgrSkB{=HWR})u{X4;{zct9ZK!GZ%>t+VFI zxxU!rVzkMqg9d7T*_f_7mk%cPR};fda37+tFCCHCuthalcD}afR0_j(WULi$5~@3b zuG?v-KrM#UimPDOaEae{3vNLf%PK@t4S;Rl&s-4!td5o4Nwg`obb41#?~H6tVS9UI zcULJJ52m1X=0j(G)RA5oww3K>p!-CdCq!#912sN_bWlTTVb}^McQ?GW+;(?kduz+t zQzx9?K8c{QtBAhLuP&|tXM4V3b6SD}p&pt9f9x6ZVy?%j)Q$DF?|7rzwo}G}&7I2! z*W7>hA%}Kgd#iH(#K7r!rH+bubkrT89fbV@=jyqctBWgK8{6~B$yv1WrhVt)-kxdd z)E%7M-Eek$M-gQ{blf9d9=3{BrF+>UHF`I^ zJD!R=@hwb+!)4>}aN=+^(dGsM+gn@iy!4dQI~5%o)4p+dcOKoF zxp;a8tK5F-jFZ~~O=kA{J?+pa)!3XA$_U+Ox*7CB-w*V?Vl7&!qJ=VU*lacoRq4HP z*k3apg8f&=kweG%5<4B+M(X6mLe%`=w|Cmud? z#e-+Ak+HLTvQl>ibK&|?XPV+v+MEoG!-jgQv`5Cp!Rcb0JPE^@fOp!}Ot+2}XLCBR zJF9HAg;HYEPX}kd?zGt$cLg`+uyRN4`0A{$&0XB*x zSJ^Q1$n|mZ?;ZCJVnHq{PTeY^&^$7pQi4T@INgnDKXLV74?c2ozQb(@*5XE~Nm4=^ ztsC7MhNJ2XyTW!iQgp!G*k8?DKbo2MjooQsvn!n5898MD!r(+Sv<7D3ko5S-dU4m| z0B~dZ0JBeAULJVx%ry_6z2ez-UC5KK+eMoz~qUW~F8>WLwx;VEVI+QVL~dXy)t-C1IhSqM3f zt0QWNcg&_0Ls-aCqDqnQ4y@`Ffj(DkZd^aO#%#uQK*lYjY$!S+qVc`FhsR1&Dm#AW zZ~U_S0{`$c{iyt&S&sw2jdhqC)762ihu5@rK*!FLuiEVp)rKC^Nc&@vN{pt;p!;;0y!t_e55JSY!o3e_CHpR2X-alRE?A z%CL*<1b61Cv%j3c0#Gbl$zS!=J&5G@$BQ{G4dXzw&i>k&T71Wv9*k|CLIhPe{GGr4 zi4R|Syk2-c4gfdS{$b4{0WHQ*Dq8AFtL-iH{+G{9sKUt;BV_}-yJ7CPIf)itt%{-4onjc)diV*}03dt* zdnpAWka-B(F=3$7Fgjw6Swq-Uw!-NXU|kVySTV*?88;e?La~*iOD)lIG>`J7N^l=k z*q&EJx8Y8-bEJ0KWE?J>NB24^o%6C`+{S131TE>ZQkh*lbvJWz7g{gq7+sR-zJWK! z(-E`I;nAL|%IWRr->X0MzyCh}i~sSD2*98I5B`+@g+KQf9|wm|U_A~1H`hGS4o=w) zoZKB4&q2IV5QOd4NC8)m$U4w}iT>Tb^zA1I6 zY{#(N8fu}cGVGM@@w(jtLl70*BTHyD2O{p4WST(URST{_OUF(LU4XfvqXeJ62Q}D= zAUdKds2=kM5lJ3FU2FJe4W~v+Z-h-*{0A0oL?#ZN7J!l(gT23LWB4Xg8=G^d z_DVOU&C$8&hsHd2rhVh-fBvl8eZ{%ANqF5`zSQsBxServMU$<%qlIjucz_-lZs2gZB>$i7&vxG8lRuh?-VO4nU>o(mh|?c7rEx-Vu)y!OUax zO6vN_6Z^*RJ2Aazxw`=?1ej(E#-M;YW;1R5VxHWC-2D0f%@wyff`G<%?cN}{{8}6r zDBH%if|=mz7)Al420<^v8etur z|E=Hi)&AVYeQYr58My4(Urb!zYgDb=dSb`0EzDVakGnAEEUA8r!5GHjGUT_zHXLwk zDX1t0%)K$qd#nXZP*tfqQ0ou|AhK%Yg6-GCo6kKa5KAez<#{b707y4f3#F7K60sVK z;vuJPZ3Of7_-qdFf^B!+KERYu4FeA)KG-QSDhMSS~ z2qJ~51%VL!SO}J6`3(8CV$rJ5GBW4}pJBQ>u)pfe2ch?dl*YKJY)?vS30dRUCq4neg6WaFrJS{BlP-H@b($NN~fMgn(pr5;49(oqsayw z$HGwa3}1HG(djETmsVPh|57#e{<-*lhF2tiZkv6wt)EB z2~dd=L`wzhh{x`eb)G?5hkLM#M8P#c%@2qQX9W1mG?w}<&2N5{t=yYrfz|v?QBg?8dT5I<2my#uR?!qH5 zJI?b|DO4RIC@r`JH++IJb1=I>>CcK{Vdx4`ek_?y(Xzfqb)$(z-EZ-8rPOtNnD<7b zBc3$t0=fw+G)wPPZ_c!zxqW)_A@}=*Huw+zyMNMu{Wt$9KlVTW;^WQ#0_(Fn0KD#- zU**60Q@{0LZ`@e69Oml}`}?xgyAmgK9h5TG@YSo~YSR^ZF}yTf7Jt4%kugp~ciO)5 zs88%4_Sod3Qpeor-6&ERcOkl=2%Tu1wd=;bkF>teE7eQ;3KU!;cI?`du{5VQYBs>$4;6L;|?b*QTlYxlKaI_c`0$9|kTqDa^f=xo7vFTYEjDe9(V}49gXV{ zz~^cjI0mG(XQgBfYm*KT{A|qupL{05!x}g>fI+gWQ#5#2g zXHSjn&WE(X5~K$EZzuz8o}$K2(u*$%l?bI)c4tCS$9kA^ws{U9u#_zO*GAa!q|G$g zr6Y6B6jVd%fMn}0-a_J3+<-P>h)AOBiUo3>rB7K54JDj(%sO2SrDV#U{DB(*#}p8z5M(VKx+f@^<(OpWqW2cRTNiUvMSX~rqL91>MKu%$ zq6r3CxCd!1w0Y*@;{DJo!!Uf*=jjLk%%Acf`cH#2^p5ZQM!)y)-tX(p51-`$0HSYv z=gyrE`R}j)=2!Z=-~TQi-aqi%yDtS%-_O@LaJZOx=C>XQz)N5Kgx5`t(`!`B!$qgh zv2htIIW;Sob|Rs7-v@QIj>`6=B2vS7x90fn4$}d)cVc4>+jx=1<+rG!J4I_}f7Ll$ zHQK?E^FrBa5{Nu#y&jlBpBsIOVWO%QCZnF9WeKl5Pf)Rl+?XFgW-+j!?42H}K@TTj z%ML>cKeHG>B16jOP`#5&bCD>>Ix3i~QGr$5rlJeu!OPXr&HU!uM4?O*JNw#Q1P`vHJwe)BoG z_3~2}6&}3j(RyQ_{*CA4iO;{|+w+0(B+}mfMdR>jkGrwGJ92V&U?`zzYt7g{I&l4L zN4j!)dqi&$qM72LV-B`x45!YmvWbqtSn191-as8K@j$voFuAnc&I1vj80pkCRhs0-TY0)12lW({~-j@eM|;24$TD(5kU1Ek?RSi33%5T`Uh5XGQUmqxZC=xMk{_@zqnaioISDQ>#3?Z}a!P`@P(M_Br#JH_M%xltYEqwwkG9P6H7geLhrwihSq{W!w))EVe;8k)$ zPupu}m29^d+#IdVK60Nb&@NBorxrYr_7K^WeRg0*`q4mp|$5 z8_q=WQ)MVIF$q!JE7m&Gr7>U6)WO)EC~yA$*Z9uw(i>b%_nG?4yWamh9QJ#f&Rq71 zb~x~hf9=;FZ~k1LRRN%EN8bJ1JE>>FcnWm{x5{+ry!KmP?WK*p` zHQbD}jLyZVQJ; z#)J2UC*b@3$+M#6mWS2Z2)mJ?L{Rjm*a4;s=j!1^83@pH;~eG>0D^1`**ErAkyXjg z*ql^`Qu&te{W|}NzxH<@2LV3I0sv#Do(Y>?;{bB59=Fu|GkesG>3G>CxM%IVEjD^C;beK3?ciIHolR&qxE@rMS4ot0~RoU!DHe*FroyHcnr_Q_sjp0{D!bFduq|{6`m^f+8H{>b(J?Z$_rB-7pXn$4vu1t9 z0>HO?PxS78;;;XmbyHrpXW5)OI&`E9{SvO8owEHQ~3w{65a{zqPcYKXIjYsn{^m0Yr0$T8*${F1kmJwGu+A7j3aYI5!SXS1$C}Zx>@ei8bIBH@N7ux~S0dBGX$Xx3)WO-D zDyL74xWndbL~!;GHT(#gvA>$I>(0e9doG{7Mij=A9WKK3aKgJW-|e^$s0zboaOUg=;%fNog1?XYKuC!HXlazz5K7vOE%O5d zgD~rf?nn1wk@ha)rW-dP{Ro6o^O|HvqQv*C8{_1zFzhyfFboCjFpQOE9oZO%>uVl; zp!1$zeJ;wt&pnSqCpiijxM}=xC|Z$HncB>Jwa1!sdcI?KHpW4N^L5|(mHuU~d;@>+ z&;K{SF9X4+EC77z*L;p&?XMY5H#~Ro9B=uKuXgv&#eAPbzr=GsT-^@nF8TQyH&dvH z4wT)%MvU$Lg?Zo4zU#sIAneXJ=%Adx{M0W#I9pK3T3i@TLueubI)t5-i^YYAdE6ZK z6YZ)qT{`{H0i)EYjYUJ>E{nhZ)lBOR8N(!WJ(WG)x0D(+unOZ=aT(}z>KMa0%TUl^ z%Xk7qQHHJKI%D2&?`aT{CuT~BqvstQ{-u`pZtI(?miRdl+>G8DbDyyufvQ>b=Sv;d zbib8>Rzh?Gk--Fow;|ou(On`1ab39rUBaUPI?Y0?V`WGq6!N5BGL!%-qp@M(ZX_Bk zM)j~CUs|LlSQpAr!xN!*9$hx}7l-w}>R9M=v@Oe~GVBUcV75l-P`n@vlp@q&rWQEA zy=8m8f!gD34*n_G{NM65Z}Ok``=9+DhM$rE@FidON`LV1B2LMT<8saQ^+V8(jxk5> z`cVfTIIRN(1siayp(95zuc3(3=tY0<#{S$ddzlMx_3#RJQ`npc!wBH)uR9Mv zaA58;ySvKvOel3xxeYPsGB-$@;{+Xc!lo)(9HC>QP)22YI>a1+ozdlh^_Uo>377?_ z=txnj7n*>n<>)WR?2r~+g}Tvf9d@iYnzbkah<3T9J=aoN3|e7~llNLe9b8ot(eE@% z0+0#K1uaqE7p)i_O%{@+gD)0Seru?MB4`w)0EG-ny%EgHxL@7_OOR3!FLWcysNxJL z=7ow;G-e<^^{TsG24&jMAj|K^8JK>+y5cf7_gudgU$4W(J>)U8ssadNskhs*ru9_e%6 z8@)wMTek&QVcyTE3H3%=ysW#gh!ekz1szC_zTgA&TN~=D?>efq=>|W(I+RXl!ulbm z)p*5yq}GaPK{W?fc+|);Dw|u%=60bBg`ycqqAiY@F-=CdoOY{APyPnVZ7?4i*B3Kx zusaXFfiXv`Pzz-Y-+dvZvfX;pZx=p5SZ=$Kbl3nHHEqXj4$Cj|U?9evzt~{m>DQAI zOm+&RMHzb_vWjtxS%;2evj(ES0DwpkZqg4RP>&QAso<{BL5Ph|Ajf_r$WF#u6 z#2mtA!Br?88BUUqadXrJH)WnW?a-k|u+cJba=zsiuNmA$n5WLOzx6<#eC4^1CtJ4X zN*SjJc9SyCGq#tU+}wj2?uIou91cOa(t&UI?l<@^{CEH0#o4eqYvJ3!{p%a6nFUo7br~%+BzvWAO_CuIu>VRmcY%Ngz64X`|jY&5SHVajSzK;zO ze?PE2J>l;89i%CjhesT|QR@wmPaLmKJyA}_Evm|97k>I$;PSzqtBZ-}{zc#cQXBFg_!y(CVWKoUL)-mfs$;)?hWrfWXMT17Gi?3YYEA(cz&PI z_nU>7$Zd)Ns5-z97(${aYY_5M!1B9VJeu>N^XQoa?cfZ%kzqTqy|tl~&gI3#v%eKB zVJ$FjJx8~JHK84p`H``|f64SHY}!uFH_+ocTwPpYX6(*)*cQJ2kG#SE&foa(LEs1f z%nvv!{Llv}3;*>0?%#5eqZh#s{iiXp=xy(K{fn+JeACx|u|IR~0dB_YzxtK_4}bBu zKj|Rwq6UCp`-vmN>RbNkoBg?q_cPnW@PaRnm(Y%uLD`-uK1hU`nc}+YFN`S>uNSA=t9lBuz$;iy1M(a?ulJkN?jk%bZ znPa`-bcASiwmz}H?i{AZco-S$7}!xMy!4BnaDd&d%CH&GA{Ygqdu;v-XFkkyH|WCQ zsv{ggoWtRORAX>uI}RW--|$^;^sSuH51o4tp2dN?=XcqN@~gl0E4Y5>o?hk4AGCh* z$N!;x!#lphf8obp9I??`zx^xx;_`wz3S&9psVAS}2mjq~_8fxgu`wzpujx9T%Z^5LTk=E2W3<${L>$O=ZAc*FZudc`TctjIb0mr?1U%2 z;DpncjIpPzGRg)mg=USb%WE#~U2(YX)H-l-K5+g-VRI_9Zd^XM=l*-IxxC*HubkZ4 zar(pt$GE=OgLF=x7}(w_ARQ^mwv!;abdQnRYLZ5vhD&GwV#6J!wbp~sGF>+y?7kUh zrujgdj{`D#dg&DnvkXU8>nw=OJ-q*op;k6^ht?7`exK7kG~Im`J$*UKf<=^Wjj08Q zHr5SeVicZThVbtXe#-6`90LZ5`I`xBS<_UtbYNC|A_e0RoE zr+0CjNAsRv__2?zzJKGleTk1{%isN9e)W^yXK(q=FZKO2@#x};m)-sx-tsly!vE#} z{2zUGWdI+t9K>s+q87kYH^KHszYKTV;bfqmRK$#V-xwHQ@TFeyxvyZBi8?B^I&~AM zyo=Ef6PJ67dU^w8gx&4RaHf=+bNIG;LwPHZpUW3l3yYe%XiXxIe$W?*gw>qmgNnq- zE`i#|4jh)>0HSM9w}K8yqAB2126sf}Y_CC;@VoP)x^`74BB1Vw3oa#f{2gCr@7Y2Y z%J0`bO8rFLgB=-T!-Bczv}ua=YaOzVw^Z?ED=>-sA|7)Qa_X-wLY(F_@4$>oOg~a( z9a*FzSgjNs)gUbeUHOLO*g{lf-HzXz#l_g6Ks^!0sW4wUhl`m9&jd^GbHDuMUN>d6 zCR0KWp+(U_DWg&c<@AXy+mj7#5-#uW1ACFqcvjh*7s|#FX+(%ExJTA-_2|I38#ukY z`KrN>)m+r7aJj$Yb#Hl%pP$@gfu7VDJ;}p58&F zaLpB+(1vg7mS6dwURa{}Q$O-^@}+P8LLY{LsH0V}9;BD)dgki62`KDtj|^uL7SrAl zohgGw`^|{9)(E@1Tgoob^6h5B$=MdIo$*8v_2?!PqqE0-rd!nBnm}K;?|ACmdzyF> z-zC%NB7#e(^Ex@%M>M?m$^h=nIJ!FQ9yhA1&0ks#c~=@|hm17CUtbULm* zn1I9}k>||}w7?OXO_bt{8gwP=J==pR;4w!^bYM!UYmn%@)A~%CXXf4!afT`|M22AI z=}1(mOW?508iait*B&ScZx$s$OsL$69m>#&gA*a`jBqi|_s??~|8&;cc&D zWp`_!?lx>ykt%F<74Cxgz~*EZcnOA|HM|aV4}O7&romwdw5Dt0^1(G%53jlRo(J;M zFMM+KS-H7xZvKDgKlnG@UHQ-d zx>|Ya&dd2*f8}S_3--0&`8q$$SG?@@OFrW3#ZU^*wwmBq;B#kx(V6xEGWHGwQTN8l zJz>_Fr=Z}v3w12$t%1#H2v$mok>6U3x{FgpWC)&o*YLq}kMz8RsHfXZ6vK<9cBB{U z$xqG!@J{bDt@r5TyR2z_oX|B*LnD}|1^5?&nLxh7eRmE&K!+H*89_seM(d0cb$JqU zM8dAyu>=wui-&O$u+>xWIFrzC5PQr6q31?@YaZx*&kSL~7>d@w5k(%l~&5TJ? z100{gEk=czTS%E9l!J1#EO&=CIr~SB^+?4x(Yw?F$4(<(8TE*DL@&<OkrzX7yWupw_QEXZNbyie3FC8?3K}yI+`{v~B}mF` zHs;HYP#8}~c6SH*-NLj_yLObJ?AV@+YvyL=Ojn)D=Vrtl`he58d~i*(#&}vVQ}z#M z5ZKBudk;8~o6(>27P+<(c2OCwD$4)u`_c zKFj9w2lqkIO+{*B_b?c@4#9#iUkNAOq^>6>PkNSm%T4Galqn7CDw%KWuA&cE~aeOuo*NKJ= z1%`CpTX^&0Ai+M962O}EV9c3Ms5+(3*=GmPG_&kkV+Z5SJ>PFR{hdg4ZKBVT#vy)0 ziLGN@){Payn<4!30vw=uOS2AH$AoqFHJx8xY?%?*dsu8QDEiU*E3%fr6?A1UiYk)y zVz=ljSO>ZJC?pnW<(=Bamk>A9SE@E9ZAkIK8tA?}ru#o)+jj@!X^L@uolUN>>~C#lQ8*c^iC6 z1AtB7T0ZLL|5(?!8&+mr-Il0DrvdBXs8g#Ixk2xYL&YsjLKXl|OUcccO1+*+!CD!|As#Xk^qN~j z`-vHN5O!)EGCPRW+)IZXD(dc7iyFRTD`3~=ECnP!gKBmTiqSm`J&G2p)Zk9wxlt`8 z)7f}Ty}t`9NT@dyfDwcY^&`p2BRZ^vYtJ9984*-y-SSgBIDCxXAJzZ%?7ar0bCtPw`^y)26w{=NMb#tnZx@5{7 zUVZp72OtbT7JvMnz5Mm-FSL4#Q=#{kwX?1|0lA4qEN;I^`)gSa^sM#Kpe@Ijap;}i z!rH5pA%=w1?AXs!0HLWfK!zK>3n5ZkL`I7k=*WZ!O1G5!e3_+av=xvHR;G$#c#q#2 zHVQ-dC(00|MDN-4&_%I1<;=yvB-9IKtZYsPrfcJHeMoM_hH)#DjfXa1Q!*oR+#96^ zI}&w_7DT5ibTPjA`@Y2UQsW)k)OqK-e}_JGsungUBfHZr2<$KS9ImI7YzGch^O}v@ zfzNsMlRoc*6t+7JxwV(bNQbR3)=gx4HM2)N7`2jte!0fOKsUiZw($rbc71jkf_#)U zT}@m(xTc$Pa(lyY5>s)v=OepwMQfvM3`99imYj?ZHTvX9R6o}6i;@n38tlbd%IZO= z!$47``%I~l{p=w7jKhF=iPO(LC9=v;!6~_cGy+4<_x6k(lEXbkR<|77qQ^aqbd3|W z^%iPBQM49nQfpg31j)t%Lnt|3E4CtO)>(7x*uq~3%dT2tuQy^4O4X9&9b@ik;3-0t znA#HosohZY^#Bq}`1!Af{*j~xi70z{o~4T<6H&5Uki|*abJUn;3(w0s_H5}0Sn!4E zG_ZwQ9N!NiGNNQhF1`s!rIlb&Ufpk)cXsFD`XnYuRfch()F5feNuj(X!q~8r(2Ka? z1uVy;ORW?N)*QR;94-!AKRh4?XLn8*wBX`gJbT5X=dQ388I`C|ON@OVwj-x^x0Jd? zFzRMxJSm`t6S7%9g0>1va~qq9UXFjC))5AP!MqJM_6>5?uM`5xRs#tK&QkGhNNo zjI`x`jM2?YI__jN>sAPa-=2 zjgwM^VgXDB5EnbqE!dU|*Pk12RYY=XaAE*NdOc|5-d(a*&kV#ud=NHeqIp{O6#Gbg zuAD%$NQ3*ThO``Hsu}b&+Li%MWM?Bf3L>Ch5Ob`BS}|nKdi)aP_tFLB*L4s}BxL1A z=fiRBLE@6IMRRF%cdF+|^P;PRk!~w>Q{uae#Z==G?cE?lhDSowSBC3^d#6vGhwr_V zs_KW+C@J<2uDSO?wf^1DJ@c-@I5^{NWp`R4@Tj1hc>aQ&8UBm&p~WB>36DwhIYH+L z*1Z|UM}FbI|KtTnzpDbkVm{&ScyrpJGhOWwZAekZ(E^#5oPJXR@s-wTmMN8S^shH( zKOb-cR78Tq-kYtu_F8jzWuC-U%32eP2Po!9pyh-we|Y;J=q*QGDXR`XRCAGzue@DJ z@Gl)UJX>ZtCIGSM>lZC(8A4fCD&~tdc(4xx>I^f^un=503CHI|mQq261ltzB0f!>V z*)n>YnU@Yo5~GSLEe%O#3rAiZB4915!&PO7Q_u`rF&)^f#RL@M97M@w#Xatj7S61VM$w$8ySWDFZdJm)XMH#G-u>bmhe` zbT-^(C@`MH5Eo=fn8VgG(0XS=5y@bXn>R{~oVTDLIjv|lO6>q8T%%mk8h(*7Ix3aT z`3`spz}?R|^V2&goISat4h1QO3^T4Ro-NN{dH_2#szj&DEk1*Jcp?O)-09Pd9Xj5e zvkx;H@hji{nvZ$&!|%2Luq&ME4l`%79nk8GCx)vawb3P2e$uhh>1d3$@&xG?;Z1kU zCi>K|*&>C`8F|7nJq}Y%SvS$ry7R@8Udg~9R82yT3RJ^tviIC_qje;)$Hlrs7RxNr zP}?o;x%qw9e_mLE@j=3&w}rZ^AI)4mbItx@LQUD63_RG}`;h(dhClG7e&`22p7pyb05Ax{AoSVj zb40++SwT0ORQUzzgNE-qy>qy3w3b13G;|Z*3}RtbE&{{0vhy8vrwm(JxeU|6nJ$AC zt|v}8jUZ=p=lcFczn<|~Ie&>WoEL^7%m-oKcdj2ExcANr0eHzPpYY~0-|(HU^WFA@ zZpNd-Lz>PEUif&{@0tMcZ9nkM{(;MPfyM-#!#$(a!udF|+bPrm>4mz^v2&UQ07rDZ zVowW!%KAX~?(Q)OrJa;^EX%VqhCuIOy3x|4V zhv3a?T8WvZ%+~{9?jH4ENOMC=5aoE>noh&jb3kZNZWq_S<&WI}+~C-X6Opv;iuV?a zI=hkI(}MnsM!>WlnT;)JZz)tIrX|aQUl`N8%seQJ!-i4|i9TdZ3;ZPpdy;K6OYk); z!3>bzDNCi|N8!_AFzgoDoLD;of7x1F7>u}ZbIUG8Q4BDR4Hiu8q*q7tfX19SPYuwu z$Lv8o<}vt?fNNUr z#l0(BCQgTfGT;az4R_(0cU=gGdu^Q7kzqGr9eVH2!xr44mFacj{`?T5F`QJkWnjAg z_-qJ%*93s)?mq)HbHhtt09P) zr2$B4Q*PGM-DoJ)?U)^Sun3)C<;8mot#zh(qW6Yo))4B$9O3OX`nnQ~I7@JGtyLsG zk0(=3TEmNm=|~YOlKunWMRV$puA5;wDJ9!&^VDK;&_dN^HL$_0)0%~h*IIy5MWX#! zg4n|vG#szCwo#YQ`aD=--Hi#hv3w7|A$HPZ8h<0;-o(*DYMfez)F!k!sle9JTEkax zX|pECxlTCp8+HBK1z|1owA8##5)x-Kns%y7n!C0Pqzl=r$A%>E5mb{&8F~(>hK|DS zZe@F}c%3mh;4Nt3%@2sQ)mza`DZ|M2+O`T{gV>SO*dhPDCgr&Q&diDy-Dj!yz8%p_X_Ul3F{>2M+U61KXOLwq~&r$&R^% zJysz~hE-$Wfl)*oD^8`>4eK$9Xc!C2TtAWQ`KKUAB@*_S{+sB&L+fFX(&jnIP1%cw z^*s|B4Z>4%s2=IRR3IiDsYG2}skj8LFt;gA;ZkC%5b>g|_u%47G8Zkbf%Q2{1kJH$ ztf&5s;mSA2LaB1>eU)7Id8V~_#ZWX=h+(`^QmwgoEj)h`(gN_Ua_{A@N~O5rENGMFVcPm3DT!k~+&N#++stlWfQekrfbrU>>c$ONn>1^(XC~X)u>D)>kMs7Vh zU}pg~j=ONEI_zhrM|;|2Y|lns^6Dr21HbW{h#IDcI0)zg7-uOeB7EB)f2;qkzw$G` zce()0Ci-Nw{Q=oFbd;cW5-vOz#`ZBM+Y$s)qRrCf_>^aEZ@wtnGEGzsfUmVxAQOKt z;000ygjOhIDG;Z=;Ncjo0?R4UjMh5holEsm13Bzx;b-i zxOF5mf*>+QE$`kEsb1WUmhNUxur4lRPkXT7wL8NQW-#t)&RR4BiLB6vdwhRQ{(xJ! zD9vrAcca!C{q#kZ$S^m-chS9TQuJqI70ta5h8ItV?!1Jjc$9Fm8qMbYOE*5?6vcG&l*{`*G8- z&%fm^o$iNVW@Z++BT4?hLmA3@pXKCtLyq)5TGO zsOdu9I_HDsu|ZrZ^k{MA$!50H_$`8m*=n=hmV$JM(N{7SG=Y|w-A6uLnJs8V)iB%B z8~Hl^yc$kL8OMQf+@!BxZ2oDQILuR=?q!p&J;$c$76hbZ{z0cT=yRt_)bT^gbAg!k zmqBl-^Tjo#$f*l(PsW-+RhC(Vr4nGGXat`YB$`r$!3H!Lj)+id$%im5yvY%mTP~fg zRgJH~*q4E%Jx6~b)Iq69BW`X?Q_FIsFpP`hu;-*8T|Y;`^V%P$DaXdljtQhs!Ex&G zQoi*?V{xOr5e?>{YFGd;5T2u?NnN@qpZYVMa(2pWU`;0btEb=*u$OHX8II5{4!_^*&wK@ zRl``smS=KVo)Gww{E1+Mqpnn;P{Y}5sZ}KbDeI6axQJ7W;9i0Y5M<+YMe-E{T>QJN zdyEG^9B8dE4r7RvG>FPeufkR_#LcS{ZN_q78Fw0rYISMy!lRS+2=)k8l1@mSK@G zbX+az&hZ;X3w5YyjeqYRS9tl%i}O);klb|fo%Be+yzytbf1btT641)R2Y_tRhI28R zR%GHadR0IrI3nG{Y zaj-7*IcoP(HE{r0A0%C%YgtaWo+2P))`n`Z|HiSdq#Vyd7p>1&YfOj35%uoQG&iQ! z;zTUIN@7d0+}wKxi$>4MyG6kV#d`pF*aSqTW69l@WmBoFB1E?!Gl>erI5G@Fpxi#8 zM8+{Qp+jU7sx_UIjoEvOm*P6gpli>5onh$Eq$dCe%ZvitqtCC=E(ND++&#^9WwV~w zD>DVsCcU>bTP^4yl(DXWZfubbsHfAH_ zu++#UqSVxq=Bq893Ju7(rPyj=JXw$7wpY(Kt{-%~3)?f{j7`k79E`FtPM?ZS%ed1V z=$a%l5lljmraR;0NyU6*7%FWqOc$L}JMt0)!Di|J5J==SLR!;EdjjoDy=JXYW{ybZuus4tUjHUp3RTahqvVNPDp>;f5JcpjR zB+Zcb-Drc<$n9#&giIg|l$><-7G@ zXnS&IdWG@W04qh(iE26e$f%rkd%M9TSrXC!0ajXTw0*M2z8M1W6V1{i6U!<=H?`=a zhYhx2;_2!WNWAsrsHxx!V73rW+*h-V);hl5Ggyg!y2PwJxnXy;jMfg=qb;#&H6;n7 zO{WVHNRlK|v>=t02JF_EdJYDi8rFMy0mcbfvS-(%QhjNddFDfz7cF3;Ikdi#epXiB zkcv77OM#7LM+TOD{V}*LS=OjQ;|(Bj`MtPPy+xqzX?f;7Xu;8qP*ysiMj&cEJQTH7 z%o_7yqV>QvVlK(CHLAI>*P7i4vzf*6-APGxTg_o@VJ0R15Ei4M(I{sl#R|nHK;5;a z!ctczXtbAmX0c1a6Oa&+)gkmGZsU3{2T{ghiwbPEhLjl4YV*wLaO=|>0N(ia*ZRBP z|1P%Mk#Sd;WqRJK7XOX+{4;uQe9QNLqyNMY|J)h`wsp(Q1SoB8VV|jcp1F88 zA{}a@)(+ZlP_yLtzX=1I8FQQ0jj;@jO#N>F1p$+1&7Lo{#0*uAv=UOopMk(5q-(K! z{C)jBS-`i1q!&KB6UH4OlP$r{vxSvM`q-y#V6E??rP*7y5Y@vQ&`atAqvo?DMiP)mYBox`MlFx)UES0VXdd|CT1R#QWmDsdXZMjtZ0aGJoNj)h>Rc? z6N84J)QTpLesLhm;FMZ`k{Cn~l{`EbaLQ04cxHCe7gS#|cv#qpcb{W2af=c08%;X4 zr&E$I!;pGrND-Xd6#fS0g*pU^FUks{#>Nf?sRNXS-MCVV)_$d%W73lrkY!UoouWeB zDYeEhR@a&R!6Re4DvV_Wf&JAC-r1fOI4vpI^p)9I(~@2?^Ffq)Dr~ord1$tcMPi<0 z`E-^5Z+zQV_)8@)w&DX`J!@rIwd-XVVy>So z@iH_MF#$NuH%qA5dXH<*5D2^$sHHLt!NHg8>9w{{vOR&i)IEq(g%!IYxfpn!j7d8V zGEcU<7vDhh_*{JseY}i0@FWOcVM)iDU0CawO{*?p8LbAAZNy_{W!{d@3;CWg<^+*K zL8HAX;Q4H>+71?<2MbI?X38LJZVR_{gWm64Kb*KS7^sLiSND&5L~0tY7AG%YWK~x1 zuJK2fk}geSdf%9Vnyl?EMewQ_2zNMrI(8dzS(-BidGRTa0~9TBvBw%kvzJ5$2!KZ(k^G9C^TN zb+F>x96hL(GGXas-DIdNfuqwW$F1T;v;SXcXqs`xFwaOj5}<00!Ie608O9NjcwlL^ zs=m-5*u<>DMHU=iHa1=ZO`@+VuE7CVeE1-c_*&BM&u@^LSWc3qH0Vk9U8KJGa7jo~ zZ~FN+|s;OWbE(8ZNE1ERG~n`fG(VW>D#HH}N6_NP1UDbr@j z1UjBxj-n~cwqwtJDM-kFw}p!>Cw!5 z9bc$!VRI&oy9!>IuAS+!MG)N__}cIOa+kUCcmL*xA58K|1%Nlc?JNBK?|(nuJ&X)L zZ|4{9x)y+^f8$wcpWppeZ~6k??Y6x4UGL?vpBT;uggsyWjxX|;KJ{`6@Z7^^+3XY- zN5||5?`9}5ZD;KcEpL3IZ|4boSPzBVQj@KB^#%$aMdWhR^jH$Ofa*&1WHFnNn?1rM z5@&nLGd&)n?Pp8BKkbNn3(6=Rsvdp(SO$M4O}T2JD-5}bx)Y}l(b8(o!J;;-sxT&- zF~@I*04cv8N7}G0W5=XWExR2klu8wtNqKIwO#36{uUQkv@KayEhr3hiz;?5xmcT%I zi}sx7`X)kWrSF6 zYOSGsoRh)Vd-w$|8+LhI;&cWpikL+Jl%6Sj0*Xr)B7sh!q{C1;Ib-o1F*@_Ar$AN` z<2uyU%@dT&84lv{{yyLy8#X{fVcdD__bb<3~~m*@TE%)_TIQSF>OIf4jJZeRWp_wXm30Vpx!zMc*A(YWAx{k!Mhd0!~_ z{d@Ph_bFWY&&+b(bUfSIVkZ zG#^lH%+nm4`otPkNqeyDr>9MKk%EpvSsjKn7;OodiGn1snvx%-MrI+Ane}DEWihhM z6G*GXHGoI<97T&IEqQ5`l377(Au$fCf-qiH(IYBvJw~>9v^u-TCt4;MDTHKC~ya96XtgP6^!uxU}C@wBYqQbDLoZQ}qpWv=WskPbNz@N zK>FFoX;oHhse-kr`CDtNu_a=O>K~|SHI6R^qf3uD@QH%JP}Ux_S#bD+)Zys_EbG%+ ziX8n4($}K5#21=nunZ)dF{T;UVL-G{dk#vucKY0C;;1P#b?;^~y?2TTwT{&EM|3>c zZHFnGhl`>f8@#2pcU8w~Wij693*l%PMrux@#i^Sb11}4aCubZ=Ok0k`&6vAk&6)em z+@ii8hB3@QmUd#M=S$@mqri{dkj#PNwqz?fKvGFCcOf?gdLNYH7-3g5x(iGDxHIGQ z9P|<+=%UJO!IM}@Rw7Uh>CuQPe#JCgs_r~xeu=0 z#ZqgpP@(s4Gg zm`Q+&lH@1BY_VyxeEpsSQu7{|9SKGn;;LE}WgIa{X`MM1x{zpp20vn%WGs?~F6mBG z7wdC(yv=lo4ZKV`%JM)x+MrbmnwD&agdr6DINUWg3PZ9Py(r_hFki!bH8an~aAuT| zP%CCfqE9eiX0|gpTAh>I6=dN2i5;8mhWXl=t|!VE;NkXmfQsX;usIo^8E^fbH~G)} z^%wRJd{MdpKle9&QQrJrZ}JZuo?HLhQ(t)7cegiqfz9dQANciq#}6PKXTrQk?bn4~ zBH_Z0c3mw`@~Rp}mc$0@%L$isxp?Su#MSbEIC;wE1fLqz-0l)Bz-8~l%H&(R15RiO z*4JBWi)4{mcn3xtNv^nC_R#|f43bWy@wRN-7Du;g=;#HQr$Yp@lI^GNG=#B2tplW+ zZkwkZ@0DOw`W7-4hU^R=7XEmWBAc9EW)c)%rl{42OsA}8z)iALpJxtv;w@8tnh9nH zY@Ue;L}{{W4pL|>TX{tg&k0GgUa6>@6428gn=<$nk?DD6KfT4+ure$f`?$9YO}Dok zFq#>cEJIG`Dc?`2i*#AQq-AgkZGb@9iXZWpVFfD-L#?O`$+cL5XkXj2i|pGhCps;x zQ%$eNY>NiLahVvsxKJG(j}VWO+B@^yxx9bC#W|}3C!1h7F3-XKs`23IiQa{iyU||1 z_0p3#AT}fI+Ie*En%+CxTLZ&M;nvHxRCUH(a8^)dnr7boop12he8Fq^um01&{Cp11 zixB_-2H(>5#-sNK2#A7?O4%w}oR_@j)DMqhpyqIDl(A)Bzvd>=jL?=shaOHkNHF)Z zW#bEICz^dvVUgnAP@TZSsb2qG*2%RC$g=`~J*En+T@iicHa)M6a4GUmU0~JQKWgE zp0XODMEO&orX_g-dQCdo=72jqTZu`~PyZ2mOH)qC7L*X439TL5sw=P>MxjFqd-kr5 zwEjUfOh5iv)OA7zWLdF>MSbZK-#^-zWfd*7o^68s;Tq3XyuI`4(!fxNl8x62KLw=nz3q%jzzSAMMDF%!%Vw=$?m72JmJn z6Bdb5lvEk!0o3+0R}c1d6E>%XdaF`Sb6ATjZ9fN=p&coeaa%%;ticprT%0}W)_wy# zf6)U#p9Pzb!DW9ng)u^@loL4FRyKEI!f6a(Y-n8uZU*qvGsSB?{q8&-#ITh!q-6hn z>C-2XB+^7ri?;>bOP+U4K&>rPZ;zP+>a0wU8#5O}R=QvUisBLFYYZ0&gT}PMCN%4H z92r!CtM4tru_YF!?6x_WtX5=8YpS=!tTMZjrnEL?`kDkA7WJMdjWw=Kn=A=T`E@)X z>9R_LWu-hUps6h)A(mkiX`+$WX&IcNrAE1uSct8eQ+_X1HrowjDHz#NsK@vantVl) z8&HOLJ;~r<=Cs2heF9RmG4;rLM5$^q7(^AV$6zjbk6a0`IQ!>w4=scPhk1@HZE5W~ zEaTzIAn|#UXW&^DcnZOy%nbj)#m6t&yuJ=p>DienS{7PZW(ad~0OGJ(Sbf0i%@`o2 zp`){VBFc?nmjQ_wsdfyZODKshlVzY$@1Y@hqJkENtukyQupZ0scy6Y-aeMPJ{^ASF zIQ+N*z-!<3D*s16`CIZreKID0I)ir!=2rlE9 zp3`L)p~lSv#9RD({Ei|$IcY^RGCM4@oR?l~-h7Af*za~cotBz({Wdq}QYCw@1U68R zb@+yPyk96@a{6znF=o4Djo6pk*Mn(ig4<%|6->B!idlQjj?BTu9}q%Dny_)y#wl)U zbDnB>qI$MS7A`*vM_!izl<0Md6x7U_X{+nZ>XIWzVHFy!x|;Okc7^#CEsTDUl7K?6}ycW%IRoYNO!I^&-`${>tm?1LD7vbY)%ouOUf2Rdg z{FxXv7C==G0#CB(77wkZ)Nl`zlNHmz*70Uby<1Z6ACg~IQc*2IrfQ2Po;g~>^KhsI zGAK8UTJjX?wq`BXW+vBg2{w=6CWMPoZ}H!>l%N)C$-aNb`kdD3!41G9ly!0}8J6}h zW|=ZafUc>$E2ZEhcfQ1AQ`wG4|1M_Ln1^wbxWK=GHX zjkF$H2th}Jw?rE>DZ$;A_Ge0`wNO$p=H#qOX50cpL52)^&af+-zGRC&h(RIOeM;@W zvwzSKSI(c@pt}JrL7G}TFg^J$;xKF~SM%k13I3h`;NSFRtog?r0G@sD9;h>iw&#oA z@|6GP&pz|KAaHO0K89PyxQUv6Zh=de5|eu@>AI_L{h|W9aetzv6SYg!&f)aMjm;b6 zkELf%GTaKLhF3kJ{8M8MnRCuEPfi5b9E~c2sn@bU4W)zu#5xZkMM3MR?X{AW&;q`(`e646J+M2OS^U1B-xpW{=6Y3Y3Lc= zgFv=Y3sO7HvOLL|g@PoI8yG=!R}>RR7B?eL%Qji2GLbX&GWFN|yd{De7T12B;~K3V zjIMM6wP?wy)=+p_(3Fc-MqP%&By~H#q4}bsOrw%=l;etJwKNjf+>kLHr$u+dbJUhOHphI0sH_$W zGR)?P2i6i;Oj1+Qy=Xz+=lw(!fJB}QpS(3|sK$AfK2zxC8El#~N|&#sCj zW>8W9lemDJ<>1UHGqo1nOJoP`%sr6&T9YqAqWDP8ORT6a66dJJSMu*-I)Wzvtm~YC z@H#AO0MLa%w9IG70Vts*scCl>wrIhTfWu|u(FZ1qSI+K^>`q7OSb!D-Qz@uyz+%{G z_ZbbiC9;`Ejf-av;Dxi>p&q<@dQ=Dg=YQ_MTo3P$8UWt-ZLjo$Uw{~$DJYFw=v?xF z%im_u5g&y?1S<`r;oVd2m;xXzX|0sUl^h>hYm3TjiR2OG-J^x(9pc%H;<}3f$&Js= z8dGs=IQg2yK*^gYp%IzEy3udo3kyy7NE2LHM?v*W>|o7SZr}z>AuMsjr6{v32=emt zWC~u;nx7XrI_KP6ODSX>NxeQP0agc7iIZ$8Ka|zFEOs0VJrB)MWR_06-i{JyysYO= zw5bIh6t#lQ@w2l{v9Yyeo30(@%D_S00iz zQBB$C%6N)@SXwh{jVz%;^EBCB?78EDN7_e^8(KB^`6bJKYr;Gk^FHgrA%MPO(eJJ$ zoqd#;Q^zu$cSQjS&@KAeF*EMw$sa!_i77;yAE#_@$t$p>{79^EIb+%YV|UujfI7Z7LMk@C#T!H^&yH#*HpDdjBSt_*CEm zp7r|jHG7{RP9+N&vJEwvnNzs+Ozp7rcpED{_&f_51Uqt98eE9XDw~h#Umiwa|`woS35Hwi%m6*8)~UZnYOSvTYMg14V=MIs6qUx$LYI~ zWySo0IGv-t8gh-CW9Y(AHO8@tMkl7t;p&vpq9wrSs|!%hV^n20+8f_U(iel7>=bFJ zK&%D1ts786~*|(AQi^MEQvpGBrt+U_HT;HE4Dx5vFVKbEAid3VX6bfTvFUq6+ zL;lqt`~m+k}dScvw7q~%JE?>daTVY5&sUZ z!FBgJYCfA`+8fhktb=vLL-!1*Db0h<*(F$Bu@M%{bTZ>4#xGOieU2dD!WJZ%S__x^ zh$Js{^x9+d8|B0EF5`_VE=?h8UAt+kZS-QK(YrCvF?(+@6O z#6)oc5(6kpZ#*rw`qD#>?tx{ev-k82tR>y6GPSoJn`FsKR6$Y-+asO6SstzYY{?#1 znr3vi{gtEn z&YS+jul*1rz=sV0-||P_;=l9W-{J6RqS=9RIy3BiZ432&qh?NY89XhQ3tntxQHd@i ztb_1lsW3B7)Ysx6GVAyhbxdn8?=7>3;}uOZRhAcEljn^SR63horPL$qPRcSQ!awNb zl&s}rvn*b5(O+Jaf6I+se*dPY0Bg9jxpByiHcvs_EER^>x7iNrv^jdsed-(zu{mpv zFV@>qn+Mh*J?+G;MVm;HrMPZDk}n_ica;c+QCg&>wSyIh7MTq$(UQ#|AdBVoy19~% zzkrM%6ePKs4M_xZ%`Spu_P9AG zcQ))!Hf+WLk)Aw)h!pK$Jh{E|yv+~rpZ!OF;bZ3keBl23(pWLwY67i`VUc=c5M$oZ zJ}2=x2`H<2F2A@IWhjwK^<;xJNdm|s%Uw>HjM!R*K{Z@-EQ}e~_BKF|?n3V&UoE21 z{$i=gG>jXGoC>sKc?gZFWp&ZZc{hVQ->0J;ejmEdk7u8(&D4 zUt~4rFt-SBe%v#3i=PkRDIS{OlBefYbQm}=bU)!am(vJ@1m)EqSttwIsO#6u{Q+>%l(n`D9X zXPXCc37p2`I*nVU)G>lux}Y^liMTGv^G9AU7y!QfTffM!+9PySHn$4qBQhE~*pYyt zY1Pz|mTK95Oy4+P(y_(m=EmkOs`xOF!8%a>lwg)pkU<&Dv5jPp-IIIY^I%#mzmDLX z=0@u+U1t)^b6HY*Uy=JbIdwgu7Oyu5XVsDqf_0@c)93WhTT>Cu7EY?kgdFS{8-Ilf zl3}+{l$~UrwoqK{J;A1fB8dB z{f`&`zT#V7?FYX^N=Kzp#-x-k2yBI+=1ES>Vz<=r;|l@fM%eQJy37i#J5>X~QxBua zNW&wwtXh&TTGsGs77y*@hV>vCxsa|l0k$o)|E0~6BinL=-$4B(N&-#7jY_)F5T~|g zDX<<)*+J;N(dI}gm(LsiaT1tA36`IxZB^8$ns`Zs=CGr(M4)fb2$oi1fm($+#9a6} z+g0)4d+L-Df4Difz+)qtx1_CyY_`ua^YG?5RtFf27bPo^&3?1=5$P)NF2P@kU{p-W&8SvEGZ3)HiFV6PW5d9x zLv}E7lv|6Is#Fp{kJ9JnxOvM?lOXiTI6Ml{NEqZ+Hk%q2XIZ8x!nheIqf$DwG?=j_ zv>E2>nR)M+6-upiJ@C;s|Az#C*M934`@t?j8_ppljmuNEAZSw78?7REsT3B}_(uAd zC*`7j4+?8c))_P~Ib2|<=rYNL=zEK#e^wO!bc!@6cX~}xpJZAqbMx1L6?1HuEC--Cr zroSkqvI1vy5Qa^mZc8u-=Y`rzVjLMcRxwlDmu29lwLJ0h8-4mzMghuKRtx!)3u8MBn}c2dsBao)|b$ zp$uc_B|JvFEt7;j2ZJ&z^opSzE*cllPF!CcFjqEbBfIm>N89@!8USXUC`0PUeU5r> z+0AJ6$1?$##DkiReDW>9aAIr%2tSAhjDA)$&E-1aL`Z+}bW# zb^~kKVLe!tmh9?wulV z2yWaKvk6O4l2U4Hq_&LWPS=-GOOht~2kGuTQEbU*F7a>+t3oZ$EUd%fx^p-LPutQ8 zO2{0EK!(s1~ zIXBWxs;_l4orL;qQ9>*N8rB1uk}X3`tVe_x3nrj7fhNz>e?3op4J_R|s}=cL`pO!7 zT_TUOe%$;{IE#tEAf#hX4-BeCpWRxThdOb}rk%OvB;|FoPCo7NeQfRU#EiXiR1@|f zIW>af3aowmrFSnLSjutZlFx}Eb*Y<<*C2ckIzA`JYL16Qkj2jZ*gIq;jfJaJKmMg3 zDdO(Vd>vF=6=Qo#DMKm*D?;t#`I(TkyvI{)9JYJlU|jvkPrOVI8CPaRmUC zrENCkQ5P(%){K70=)0?UJJzUP4XGBENhPhUFuF#|Wxc?Os@trq%Pl>(mgR*=n721$ zI&`LeKQiioWqkEQLRq?U&wF6<-@6grgX~5`BOp!+nOVCDftxKvm#8OOjldTVl%&z) zv9u{k)Aa=ui+B+ylI2Es3qVkhr~ZOX2!hSoBb1|blm(|ukHR&5?@QmEl_|Fp2NveV z@yoC>9+Nz@XaQoPnDfDh!;-c)hvLE1Y`xLu7(nX%h`@{YAoXN(V6pv=H z)`^-vGrS&LhK09&lh&=vp3khKNly%9={W^&!u<%|s)E(UZMH695wy;D2Lu^rj z-so6p{;UUxZ3aFE^KwzCPCblkiWbINVH|1Z3GxOevOb$1+|rWre2irEw+rl~?t;>b zrw5vX*+jEO>k-tZ*06(db?-oTqiia;v%lPfE4#Zp&Ys+X!Zgij`vfKf^%-;l=34r~ zwZ?qanID8JY?U1@!r^kF`%FDE#`7G$S!z%~7lY1aLoVs4rR!?96lp}tU6!75+GK?b zjHC`bIp8;p5|%cbu(;PAuk{-3B_8}B$v`7gqn<-F%a9x5Mv)rNjnliv2{A{sj}>~IuupM~*GD?<#W0ZW zHyj<6mQrqM*Yza9X28^PbYzND9Bhtjs`YrdyLA>NuOt^^nLkiUlBrVfQF4H4&upQj z32bi?rN|B9l4sVon#3+Ve2+~gd4tQQAm>4)E*n6?k*H~BEFQj!TS2=e=$Ea?O#d~- zLYm%^A_I$gP4M4ZI8{lw2}uc!g!n>^YBcTCov=-Dnn`1tV(2CGW_l=WHiDd0$|mNr_ob1& zf;=JdU3%-SYO>dISEkPTX2Bk#slyJeTN*A}47wPq(ArV_vj)|bXts7(F*8cBXjx@h zp=sC<<`{}U9cpr{Quee(_qW4>%J5Gtv#G`?|m;PBxZ0BXHt|% zg5W%X6hLUQt@6<2a#efiC=asavg?DRBf2BHqoTW`qdz!0%C3@!YFBuu?yk1WwxqIT zNfN==iK*z1dt#BVsP#VN{M_g?>+b2v)5k3 zf0*8G>c7pG;2p6KxM;y3r#z?OHCwGMhm2X~vULhIDlO}54`D;df=s4zr#y& z6{4~4n#`AawjHXL0nHJnv`8k}Gy$LtrUo1qj@Y}#EN#7}D!8Q?p-zR5_tC&Pf zqyxjRhovnahqj|Obu&k+5*g3uTIx<=(fhrdMkP3823jugW@_*zi`CPa082D z5iO1oEmQAY$KAjFg^SDoxxg6k!~g1&^0uFRlb&6lwM5{Ykj@DQ4^~-RYc!I+cR1f! z8PFM@xg-NBNThXn9k@WIP|_ABFri3gYG`R=e5rMGbE0v?9d6;#jg6WnSiy7(7wlo* zx}XX^K-U|qE2YW$%XWyV_hPB2(!Atb$GRmX;w>l9Qc11D?fo`|pEj{I@XXI%yyAQ? z4Z~U{yp(An_+~aSm9EogU=ls4INwgD+%g|6_|W<#4JlWvl^W7spo);k1{0;?8xcp^ z9NijF+w#e^jLZcGs}52o4Xp_^iGG%1+03CSZY$qyT9LOwp!Mq-kaFvJe;5TbA?tFo zOdyMmIen<+o~+x1E<)7-Hrjr^ibE!R!h=(!sv zs8@1@oEj*lZ@%?m5@&2pp2DGwL!V~6x+cg)w>)Hd_blH-Ui!q%gT7$5v zl|x#sEVG%v{Twg=4P|C!`CzA36>!mlNeC7^Cft2Hu5fsyT3xDbqFOSPp*6&6p}{O> zl8!Wnp*Clo2@51b(*(5JXtrQxu#%HDRHWjYBJs4z5EHhj)r3L}(O27dWuI~CUr1rVdFm|@JziPyXO=6sn&#s)bKl5gNVDCN_$1F9CKC(P+0+S&+VyED; zA>4lYBg2263m`2SN=$2dYZuXk<4R|WI{P~H z9VUQ|lE!L549j|a$Y^KOQXd8Btp{PG5_{;wL@>d!mk@vnf!CUTKDGc*+k5(kduViI zoBnK{C!yg4LYsED?f!KRy6N@W_-f1MI|PfIv>kL#JGSugHl-*{)T8wWE!A#mzt%eI z0Mz!{=BW@+Ux;eG8vp=+07*naRFR#QAvqx~+bA|zx)=M#3Fytih-`h0;+ZAhm&alw zNeYXdj@WgC#-O!~)h1%K=}1haqTSf-xV+Zs{VDXg48b;Qm}B_&f*;@FOD zet>^@5x}3v1b~;k`5JXGv*?u&;l90Zfv@;zUx*VSIKfSF%{5VqlIlp+c1#{}ADQTz zW+LhE8tgQYmLAha6uwz;Wu3u*)`YT*Az9qEhsT?C9!|_t&Ivx4I;-Yb_6$oj zStxZ=H@*RJg0lcsK&rpldzn;c&50+3j#|t-BkM>>?O~ZX1e1t$B){++rpCP*5Y4v0$F64(m$U7#<-mV6h{F-qe8!cJOpDm`ZYsaz!DHFzneinvECay<~?; zRHWIlei6Ay{o4ymhR7toSyYSKXfQ(i$`_dIKnl zBCc#1u1<`KDv*<%w6$6@u=9Qvn3!?ut;b+ZyK32kRMR;~tqvED3jtqk&hCTK^^v~o z@V;lv1)5UMq@OY!l$s8GnG;#DZCJ%p@R`J?Hi4)~W?L}ljgOzQwuj!&!J%XuyLkt# za+)zaQ#mAU^jVtK*3{S()Y?FCn+41b6m;7z5EB)hotbKr)tczSrJWA#gg-(Yo4Uah zjzC%fMod;aP3%L9kTNx%Cw_?@3R=#_f!c1aYu&pl&HXt;-c;6SZEPVY9olO%rP=m* z(3`m)JWq;&J#>t)_p(($byF0$mK$2CnlBR5S^bqIG_4qX&;j@C4_H$9uAF zU4Z}kfBjeWfB0|z=~K!ALdpr<6g!c`(}`M#Q9YA#%mjGEf-=>Y?XH{aF`G#nOnm|l zwegjAgwC8}4QOLQLW<3B%b3Wi9+^*AXQ~UGRuB#UdFIZ?fcW8Y3QA(@eNMFtydDVT#U1Pc5m{+U<5|Xf>fUC7u?{nq!vP zc_&lMsSV1hpic2TE1a#?X&evNbm1WSmmyq;x`%o@!gi$R4_H(aSQeZtiu?^S-13-T~m^#e5i7YyY?8N3ZFZO`=it`2U zl;R+`<`^Rrh{@R*}hqLvGY%a~_roofEo6AjV)DYfL29P;D*n`et}i z#`dN&s*Q#i-EQhh$W$jtG_?A>jaTc0(_o7uNX_@5S)3c$<BwVNCl4mlN24M>O#`@-gt z0bF5qnc$8)ipvP*sZcShdsqmoQ^7Kw-b&%cNh<|htZ4OUZc!{+DejwGd%=20?$9J^ ztC(c>xC=cW|JBdQXZXTX4*>QNE$x%)pm-P@sX$EzUhoTN>6|aEerls3PfM9vw9-1e z#!iQM@J+^9!XaSLlyz0SS3*>BWajnT$vv^-%q>NoHFB!vHfyh2$6%dmT7xE=t(eur zS_X0wL#k;jVXAf8CSnfQ`-&5M@lDUaHt@-zfFzKeWh8kNa*?Ci!;E8SHRzn^wm_F{ zS`xfB_gvTBGunyfqV*n1ft(9z$Ur8>CRyfM3dUnnmKI3uN3MZmoHto8sbx-ub8Uc< zl4gl5t-dMMns|o^kJjy+J}Wg?HZaxUW_RA2fo&u==^0E2#oE-3?fx}@PnS+bWb7Km+wd1nLC49=as<=3 zkRoI(E0Z=_jSC^fV9eIhu|6p5oz3`I=#NE2jP{#L!cUq9*uRU>Z+jbp0gPJHuZ+sL zZ{VnYxAjW=;pd0BsVUO<2hwcm$$b3Ze(5PU|KQsYI8JQ0@ zlbxue6GYfjT0_to3r!I%4Qt;zy$wJktph*ip=rfK)iiVR_0TD5Cz%6{4gG&)-e`QB ztmt6l7^*idLRCa6G1xfJT!^j996IX-j7iC9n)NrFDTZ>H1GAU(~HE;V$Mg>>! z698aB)wMC-Y@U3m(~nsBQ&Q{SwdP@MuF2*g)GXkq0N&Jnrc6K)N|+FTuJJs!BOBWu zx;dl(~Z

FXBSb%O6J8er)Jx8umU5c`T-6g0FapxGD8 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 b*5UsHy$eSe!oUrs00000NkvXXu0mjfG4u@( literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/sprite_barriere.png b/app/examples/Games/BeastScroll/sprite_barriere.png new file mode 100644 index 0000000000000000000000000000000000000000..61a159e7be6c4d5da399810301428d089b6488dd GIT binary patch literal 6455 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/sprite_nuages1.png b/app/examples/Games/BeastScroll/sprite_nuages1.png new file mode 100644 index 0000000000000000000000000000000000000000..5e27ccc73ec3c95ea26267177b11915769c15a22 GIT binary patch literal 14285 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/sprite_nuages3.png b/app/examples/Games/BeastScroll/sprite_nuages3.png new file mode 100644 index 0000000000000000000000000000000000000000..65375fe4246537dbd4b0481c14baf77fd073c704 GIT binary patch literal 2389 zcmV-b399yqP)Ta^wY9azl$52ty~f7I&ZV5Ky|uNJoScM&jJ3VS&c@cx#?G~^t*xb{jFgnU zz0RDJl%KjrLDcKrMMn*>Rj*N;d*J>;|fL&s6o9vVp z@@i&wH}n7h(ta}Jak@PzuQZvnz0NV%0qH)!`(4xN>?9=U(%ruK_YxBJ*h}lMv+g{4 zExP~XH^JhAuGnW0lP_)7PY@aWeeINQ1|fNACGJFo^nkhuC1{dRfp;%$*t3dOY5X=b zFj76o^5UI7I!bl=EV}2Zh?D2jeH&4^5;kO3>jN#QIdz6bJyuZuJl=a90y~c_iw4Zb z!J&M2FH#I?Fbc*&$&a2v_dK8$F&YFFWzVtx7Gf!_nPHRefJZ0$+4<=0k%D-mD?@pije;_xsX9<6S+C=U zg^v*uusiTEs$a;JtxDSPEMbhZIGFHakCiMpU_*L3>rDb=G)tz@H0sT=og*W)wS}u< zR7O+j*TPm*GmfE)?#)h;0A{ieQ8b9A-QMmPsO`u5cXn9YVptV8oU583wk8O|k{_l7 zDK)2UjHV@HK1PGypf~NFxfgS%M2uq=SB9C&@MLE@RY#dD1CNZmvq6>^6PF*7B#OHK zoRvFfwbIU5vL>eAYrTplqMSR3g)13gx%s?P zyHiyvb#JEwD0M!>)+Ka~lkSEwAVe~5(#BTcB)!Zy}wrPg*55l6FbIVnS+ z6d{*U{;e=QLmYH6@~n-ggsFE^`ds zmp%a*LYBRsnM3(&GpEvoH#+w|B~l8J1OJ*i^I=BAqZ?+RO#S@ga?(+#Rw-Np9Ee!l z`m6#zc!tC<4byF&l&RXJQWVB{{F{RrILFw$#X`9NW!R7R(-4g(p5|s)qu*RJF1T=F~ zEie?$DPvZ1N%V5oU7E1PWrL+hhETH>EeNTnOAI)ax`$tf*#WtkDD zWZ_)Q{Frar8gYw{&QM%{sBw5>>u?CIsj&}$4d$nO1rvc_7?C^S%UfgSraL^+5V zlr-h&2V;Y5d76?)VN{l76YA78SQ~mTdb!7~xDvjzJEV`bz^tt2qqC6%rRqae|JsM( zvbuzmzz3^kwY09~h1MUK5^Yi2W;_~wt@CeIu)&DO#g=ok9S39UlUms^FOYsTIP1~A zvZl!Ur?_9R65JMjvGN*uJ7YG7o#+Jq8q?~SpY?_tSa@%#kAal^mIgi8PjIIX`}@x6 zKK`ZrN8k|*D4WC7I^e30T~3u_W$1w2#(96@Z%M;dj6_@E21T8nobW5~#4WRHBhQZb zeMhn$|KhMhGXVnn1iV?HzOYNEl0p4Y_GkqOWwC=IGN+ z!%s?%u2{c2(pOjYyC1BmQtxdv3@^Gp=ZiGQS?Cl;euSE;EsnZ&-$B5K8EXqE=oKKK zPSv|&JVGWGUmI!xir7#2R8~5s7Fe}N7pVXY0<-eiowiZdQ0~Z5+noLJ z7z4UK1l5-9RuCy_0i(G|dR`ei!WfPJ=Kd@8Aw_g-JM*4}+# zcaeVHL>Kl@OY~#ip*dnia*MIex0E(a|2q3O|If#Lf5XZ>)(tCo^b~$ADjL#C`IxRW zxLGCO?HvXZSLH-3eIGT$9d^rGN6CEE_-<}H-)|Rch#Jc`#t7u0@F(Avd^Gm-$#?fq z>fY5Vt$(mZu3NLDc5SP={nk#~e6_FOaplvxul)bl_hW>@uIW|1IgUS$%KH7?9ZyaQ z1n?BZUgua+roONq=mrwy7Ox%#QICGuTA;h-8*crr;_VsC5HGif`H9=3sHaHE<6&RP z=bqH$1|)0s33X6Af0nGRgCk|D`vLrk`R38&RefCDyT7A^=dV73YbvqIzu(pfj1~;} z>rOYHJ!A`_`%8XF=X*?Ov!c9N7r@O+TkuQ%Uq!C4_(}OMkZ#RH#g$%`00000NkvXX Hu0mjfl0vVg literal 0 HcmV?d00001 diff --git a/app/examples/Games/BeastScroll/sprite_nuages4.png b/app/examples/Games/BeastScroll/sprite_nuages4.png new file mode 100644 index 0000000000000000000000000000000000000000..db1a428ade08f138f8e9c82ce27e5d52277c4bfd GIT binary patch literal 1036 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 QLjV8(07*qoM6N<$f~@Ra7ytkO literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/.lang/ca.mo b/app/examples/Games/Concent/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..23c6b86944fbbf971332c057034853c71e072757 GIT binary patch literal 6958 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/.lang/ca.po b/app/examples/Games/Concent/.lang/ca.po new file mode 100644 index 00000000..807ad63d --- /dev/null +++ b/app/examples/Games/Concent/.lang/ca.po @@ -0,0 +1,217 @@ +# Catalan translation of Concent +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the Concent package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Concent\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 17:26+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Concéntrese Geográfico" +msgstr "Concentri's Geogràfic" + +#: .project:2 +msgid "Esta versión incorpora soporte para el idioma inglés y español (nativo), disfrute este juego!, se han incorporado las correcciones sugeridas por el director del proyecto Gambas, el proyecto ha sido traducido al inglés usando la herramienta integrada de Gambas Ide" +msgstr "Esta versión incorpora soporte para el idioma inglés y español (nativo), disfrute este juego!, se han incorporado las correcciones sugeridas por el director del proyecto Gambas, el proyecto ha sido traducido al inglés usando la herramienta integrada de Gambas IdeAquesta versió incorpora suport per a l'idioma anglès i castellà (nadiu), gaudiu del joc! s'han incorporat les correccions suggerides pel director del projecte Gambas, el projecte s'ha traduït a l'anglès tot fent servir l'eina integrada a l'ide del Gambas" + +#: fotos.class:303 +msgid "" +"Estimados amigos, este formulario cumple la única función de\n" +" cargar en 40 botones, las imágenes de las caritas que servirán \n" +"para el juego, es posible que exista una mejor manera de \n" +"hacer esto, por eso... escucho sugerencias, posteriormente \n" +"en el código se crea un array de objetos a fin de referencialos \n" +"como botoncito[i].Picture y copiar este atributo en particular al \n" +"tablero de juego. Att: Jairo Alonso Badillo\n" +" jbadbe@gmail.com" +msgstr "" +"Benvolgut amic, aquest formulari té com a única funció \n" +"carregar en 40 botons les imatges de les cares que serviran \n" +"en el joc. És possible que hi hagi una manera millor de fer això, \n" +"per tant... escolto suggeriments. Posteriorment en el codi es \n" +"crea una matriu d'objectes per tal de referenciar-los com a \n" +"botoncito[i].Picture i copiar aquest atribut en particular al \n" +"tauler de joc. A: Jairo Alonso Badillo\n" +" jbadbe@gmail.com" + +#: frmAcerca.class:30 +msgid "" +"Concéntrese Geográfico v 0.3\n" +"\n" +"Programado para Gambas por:\n" +"Jairo Alonso Badillo Bedoya - jbadbe@gmail.com\n" +"Publicado bajo los términos de la licencia GPL\n" +"Viva Colombia!!!!!\n" +"Visite http://www.linuxeam.tk -Una comunidad Linux" +msgstr "" +"Concentris Geogràfic v 0.3\n" +"\n" +"Programat per a Gambas per:\n" +"Jairo Alonso Badillo Bedoya - jbadbe@gmail.com\n" +"Publicat sota els termes de la llicència GPL\n" +"Visca Colòmbia!!!!!\n" +"Visiteu http://www.linuxeam.tk -Una comunitat Linux" + +#: frmAcerca.class:36 +#: frmInstrucciones.class:43 +msgid "&Aceptar" +msgstr "&Accepta" + +#: frmInstrucciones.class:30 +msgid "COMO JUGAR CONCÉNTRESE?" +msgstr "COM JUGAR A CONCENTRIS?" + +#: frmInstrucciones.class:36 +msgid "" +"Se trata de un Juego Bastante sencillo: \n" +"\n" +"1.) El objetivo del juego es probar tu retentiva y nivel de concentración, para lo cual\n" +" debes encontrar los pares de rostros que hay ocultos en el tablero de\n" +" concéntrese. \n" +" \n" +"2.) El juego tiene tres niveles (Principiante, Medio, Experto), que te permite\n" +" escoger entre tres tamaños del tablero para añadir emoción y dificultad al juego,\n" +" para Acceder a estas opciones debes ingresar por el menu Configuración - Nivel,\n" +" y seleccionar uno de los tres preestablecidos. \n" +" \n" +"3). Así mismo, tienes la posibilidad de incrementar tus conocimientos al jugar en\n" +" Modo Pregunta, lo que incrementa tu nivel educativo, esto se logra mediante un\n" +" cuestionario emergente que deberás responder con cada acierto en el tablero, si\n" +" contestas correctamente tendrás un puntaje más alto y podrás competir con tus\n" +" amigos. (Esta función se encuentra en construcción debido a la migracion de\n" +" Visual Basic hacia Gambas, y experimentar el poder de Linux, viva el\n" +" OpenSource!!! \n" +" \n" +"4.) El juego también te permite activar o desactivar los sonidos de ambientación,\n" +" para lo cual debes cambiar el estado del menú Configuración - Sonido. \n" +" \n" +"5.) Recuerda: Para redistribuir el Tablero Pulsa F2 , para comenzar a jugar pulsa\n" +" F4, los rostros serán cargados aleatoriamente, por lo que no debes intentar\n" +" aprenderte el tablero de memoria...!, Esto sí que va a ser un verdadero reto!. \n" +" \n" +"6.) Estoy actualmente diseñando los modulos de aprendizaje que podrás conseguir\n" +" muy pronto; para comentarios y / o sugerencias escribe a: jbadbe@gmail.com.\n" +msgstr "" +"Es tracta d'un joc bastant senzill: \n" +"\n" +"\n" +"1.) L'objectiu del joc és posar a prova la teva retentiva i nivell de concentració,\n" +" així que has de trobar les parelles de rostres que hi ha amagats al tauler del\n" +" concentris. \n" +"\n" +"2.) El jo te tres nivells (Principiant, Mig, Expert), que et permet escollir\n" +" entre tres mides de tauler per posar més emoció i dificultat al joc.\n" +" Per accedir a aquestes opcions has d'entrar al menú Configuració - Nivell,\n" +" i seleccionar-ne un dels tres preestablerts. \n" +"\n" +" \n" +"3). Així mateix, tens la possibilitat d'incrementar els teus coneixements al jugar\n" +" en Mode Pregunta, cosa que incrementa el teu nivell educatiu, això s'aconsegueix\n" +" per mitjà d'un qüestionari emergent que hauràs de respondre amb un encert\n" +" en el tauler, si respons correctament tindràs una puntuació més alta i podràs\n" +" competir amb els teus amics. (Aquesta funció es troba en construcció degut\n" +" a la migració de Visual Basic a Gambas, i experimentar el poder de Linux.\n" +" Visca el Codi Obert!!! \n" +" \n" +"4.) El joc també et permet activar o desactivar els sons d'ambient, i que per\n" +" fer-ho has de canviar l'estat del menú Configuració -So. \n" +" \n" +"5.) Recordeu: Per a tornar a distribuir el tauler premeu F2, per començar a jugar\n" +" premeu F4, els rostres es carregaran aleatòriament, per tant no heu de provar\n" +" d'aprendre-vos el tauler de memòria...! Això sí que serà un autèntic repte! \n" +" \n" +"6.) Actualment estic dissenyant els mòduls d'aprenentatge que podreu aconseguir\n" +" molt aviat; per a comentaris i / o suggeriments escriviu a: jbadbe@gmail.com.\n" + +#: principal.class:166 +msgid "Jugada Errada" +msgstr "Jugada Errada" + +#: principal.class:202 +msgid "Felicitaciones, has cumplido la mision" +msgstr "Felicitats, has acomplert la missió" + +#: principal.class:247 +msgid "PULSE F2 PARA REPARTIR DE NUEVO" +msgstr "PREMEU F2 PER A TORNAR A REPARTIR" + +#: principal.class:384 +msgid "PULSE F4 PARA COMENZAR LA MISION" +msgstr "PREMEU F4 PER A COMENÇAR LA MISSIÓ" + +#: principal.class:439 +msgid "Enjoy Open Source!" +msgstr "Gaudiu del Codi Obert!" + +#: principal.class:444 +msgid "&Configuración" +msgstr "&Configuració" + +#: principal.class:447 +msgid "&Nuevo Juego" +msgstr "&Joc nou" + +#: principal.class:452 +msgid "&Mejores Puntajes" +msgstr "&Millors puntuacions" + +#: principal.class:458 +msgid "Nivel" +msgstr "Nivell" + +#: principal.class:462 +msgid "&Principiante" +msgstr "&Principiant" + +#: principal.class:470 +msgid "M&edio" +msgstr "M&ig" + +#: principal.class:477 +msgid "E&xperto" +msgstr "E&xpert" + +#: principal.class:484 +msgid "&Sonido" +msgstr "&So" + +#: principal.class:494 +msgid "&Salir" +msgstr "S&urt" + +#: principal.class:499 +msgid "&Ayuda" +msgstr "&Ajuda" + +#: principal.class:502 +msgid "A&cerca de" +msgstr "&Quant a" + +#: principal.class:506 +msgid "&Instrucciones" +msgstr "&Instruccions" + +#: principal.class:645 +msgid "Jugadas: " +msgstr "Jugades:" + +#: principal.class:653 +msgid "Tiempo: " +msgstr "Temps:" + +#: principal.class:677 +msgid "0" +msgstr "-" + diff --git a/app/examples/Games/Concent/.lang/cs.mo b/app/examples/Games/Concent/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..d91a34d94348fbb170f6cf975ff713c5a043fc45 GIT binary patch literal 5602 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 XO$*a`L-=CKsAI`Yu=jIaECl}p7eQW= literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/.lang/cs.po b/app/examples/Games/Concent/.lang/cs.po new file mode 100644 index 00000000..ba617c89 --- /dev/null +++ b/app/examples/Games/Concent/.lang/cs.po @@ -0,0 +1,224 @@ +# #-#-#-#-# fotos.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/fotos.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# frmAcerca.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/frmAcerca.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# frmInstrucciones.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/frmInstrucciones.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# funciones.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/funciones.module +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# principal.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/principal.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# #project.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent_gb/.project +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-04-19 15:59+0100\n" +"Last-Translator: geniv \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Concéntrese Geográfico" +msgstr "Geografické zaměření" + +#: .project:2 +msgid "Esta versión incorpora soporte para el idioma inglés y español (nativo), disfrute este juego!, se han incorporado las correcciones sugeridas por el director del proyecto Gambas, el proyecto ha sido traducido al inglés usando la herramienta integrada de Gambas Ide" +msgstr "Tis verze obsahuje anglickou a španělskou verzi, užíjte si tuto hru, byly dokončeny opravy a navržené provedené Gambas vývojové skupiny (Benoit Missini)." + +#: fotos.form:292 +msgid "" +"Estimados amigos, este formulario cumple la única función de\n" +" cargar en 40 botones, las imágenes de las caritas que servirán \n" +"para el juego, es posible que exista una mejor manera de \n" +"hacer esto, por eso... escucho sugerencias, posteriormente \n" +"en el código se crea un array de objetos a fin de referencialos \n" +"como botoncito[i].Picture y copiar este atributo en particular al \n" +"tablero de juego. Att: Jairo Alonso Badillo\n" +" jbadbe@gmail.com" +msgstr "" +"Tento formulář splňuje s jedinečnou funkcí \n" +" zatížení 40 tlačítek s obrázky pro hru \n" +", může být řešena jinou metodou, ale nevím jakou." + +#: frmAcerca.form:14 +msgid "" +"Concéntrese Geográfico v 0.3\n" +"\n" +"Programado para Gambas por:\n" +"Jairo Alonso Badillo Bedoya - jbadbe@gmail.com\n" +"Publicado bajo los términos de la licencia GPL\n" +"Viva Colombia!!!!!\n" +"Visite http://www.linuxeam.tk -Una comunidad Linux" +msgstr "" +"Geografické zaměření v 0.3\n" +"\n" +"Napsáno pro Grambas od:\n" +"Jairo Alonso Badillo Bedoya-jbadbe@gmail.com\n" +"Publikováno pod licencí GPL\n" +"Žiji v Kolumbii!!!!!!\n" +"Prosím navštivte http://www.linuxeam.tk - Linuxovou komunitu" + +#: frmAcerca.form:20 frmInstrucciones.form:28 +msgid "&Aceptar" +msgstr "&OK" + +#: frmInstrucciones.form:15 +msgid "COMO JUGAR CONCÉNTRESE?" +msgstr "Jak hrát tuto hru?" + +#: frmInstrucciones.form:21 +msgid "" +"Se trata de un Juego Bastante sencillo: \n" +"\n" +"1.) El objetivo del juego es probar tu retentiva y nivel de concentración, para lo cual\n" +" debes encontrar los pares de rostros que hay ocultos en el tablero de\n" +" concéntrese. \n" +" \n" +"2.) El juego tiene tres niveles (Principiante, Medio, Experto), que te permite\n" +" escoger entre tres tamaños del tablero para añadir emoción y dificultad al juego,\n" +" para Acceder a estas opciones debes ingresar por el menu Configuración - Nivel,\n" +" y seleccionar uno de los tres preestablecidos. \n" +" \n" +"3). Así mismo, tienes la posibilidad de incrementar tus conocimientos al jugar en\n" +" Modo Pregunta, lo que incrementa tu nivel educativo, esto se logra mediante un\n" +" cuestionario emergente que deberás responder con cada acierto en el tablero, si\n" +" contestas correctamente tendrás un puntaje más alto y podrás competir con tus\n" +" amigos. (Esta función se encuentra en construcción debido a la migracion de\n" +" Visual Basic hacia Gambas, y experimentar el poder de Linux, viva el\n" +" OpenSource!!! \n" +" \n" +"4.) El juego también te permite activar o desactivar los sonidos de ambientación,\n" +" para lo cual debes cambiar el estado del menú Configuración - Sonido. \n" +" \n" +"5.) Recuerda: Para redistribuir el Tablero Pulsa F2 , para comenzar a jugar pulsa\n" +" F4, los rostros serán cargados aleatoriamente, por lo que no debes intentar\n" +" aprenderte el tablero de memoria...!, Esto sí que va a ser un verdadero reto!. \n" +" \n" +"6.) Estoy actualmente diseñando los modulos de aprendizaje que podrás conseguir\n" +" muy pronto; para comentarios y / o sugerencias escribe a: jbadbe@gmail.com.\n" +msgstr "" +"Tato hra je velmi jednoduchá: \n" +"\n" +"\n" +"1.) Cílem hry je najít tváře pod každým tlačítkem, tváře mají duplikáty, \n" +" v pohybu máte najít dvě stejné tváře. \n" +"\n" +"2.) Můžete si vybrat obtížnost úroveňě v konfiguračním menu, v každé úrovni hrací desky\n" +"více tlačítek, užíjte si tuto hru!\n" +"\n" +"3). Vlastně jsem rozvoje zeptat modul, tento modul umožní spravovat řadu věci\n" +" ve hře, přírustek vaších bodů. \n" +"\n" +" 4.) můžete aktivovat nebo deaktivovat zvuky v konfiguračním menu \n" +" (Konfigurace - zvuky). \n" +"\n" +" 5) Pamatujte:. pro start mise stiskněte klávesu F4, chcete-li distribuovat desku stiskněte klávesu F2 \n" +"v každém okamžiku, bude hra obnovovat. tváře jsou zatížení náhodným režimem, je \n" +"nemožné zapamatovat si hrací desku ...!!!! \n" +"\n" +" 6.) Pro připomínky se dotazy, se mě prosím napište mi na jbadbe@gmail.com, prosím omluvte \n" +" mou angličtinu, jsem Kolumbijec a já nemluvím anglicky. \n" +"\n" + +#: principal.class:166 +msgid "Jugada Errada" +msgstr "Špatný tah!" + +#: principal.class:202 +msgid "Felicitaciones, has cumplido la mision" +msgstr "Gratulujeme, mise byla splněna." + +#: principal.class:247 +msgid "PULSE F2 PARA REPARTIR DE NUEVO" +msgstr "Stiskněte F2 začnete hru znovu" + +#: principal.form:45 +msgid "Enjoy Open Source!" +msgstr "Užijte si Open Source" + +#: principal.form:50 +msgid "&Configuración" +msgstr "&Konfigurace" + +#: principal.form:53 +msgid "&Nuevo Juego" +msgstr "&Nová hra" + +#: principal.form:58 +msgid "&Mejores Puntajes" +msgstr "&Nejlepší skóre" + +#: principal.form:64 +msgid "Nivel" +msgstr "Úroveň" + +#: principal.form:68 +msgid "&Principiante" +msgstr "&Začítečník" + +#: principal.form:76 +msgid "M&edio" +msgstr "&Střední" + +#: principal.form:83 +msgid "E&xperto" +msgstr "E&xpert" + +#: principal.form:90 +msgid "&Sonido" +msgstr "&Zvuk" + +#: principal.form:100 +msgid "&Salir" +msgstr "&Ukončit" + +#: principal.form:105 +msgid "&Ayuda" +msgstr "&Nápověda" + +#: principal.form:108 +msgid "A&cerca de" +msgstr "&O této Hře" + +#: principal.form:112 +msgid "&Instrucciones" +msgstr "&Pokyny" + +#: principal.form:251 +msgid "Jugadas: " +msgstr "Pohyby" + +#: principal.form:259 +msgid "Tiempo: " +msgstr "Čas:" + +#: principal.form:268 +msgid "PULSE F4 PARA COMENZAR LA MISION" +msgstr "Stiskněte F4 pro start mise" + +#: principal.form:283 +msgid "0" +msgstr "0" diff --git a/app/examples/Games/Concent/.lang/de.mo b/app/examples/Games/Concent/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..8300b2277a97dc9f16073a6f84a3b3195cd2dd0b GIT binary patch literal 2582 zcmb7FO^hQ)6)qrz&I8nVyLk|H(g#G6+Pu`?+1-c9m0I z?b(Tt5ZpL{5O9iw7IE1N2t`5yZ7wtyM1V2u2=Q` zzW4mQM;`dL!uSB@2QmMQ`4P;Q-+%|k7vHGVLqH9D5V!(9415mw4&YaS?*x7W_%`78 zfNux>5Ge950p9}rIZ&>A85jY71$-~?)gAs3_%d2_{tNH|4nFjNQulz5 zzFDaTzW)Z7i=Mv$-wJ#XN<`m9AVkz-zz+jI1SH^7K=I=XKrti(;%c?TM>&@R#h(Hw zetZ=uetmtnz5^7ye*nZ!{R9s=_cI`_Q@;R;9lr#=2l&cv|8?M(vHlYfKUF~J-M}va z<=i)S_$}Zb*53h&{t77f{~9PJ|8}>24R{gjKL90ue+7!a7h#;-hcMQ;1l0bCPja8g zNZuaB6#W-4Wk?LgF46TE=KC=JeIWGpct4(!6B+Nq6g$LMiHpQa>=9ef2TVh_~N&Mr(=dYzc+MXMTJTLfPwiE)+-)#&rec`j&F+MrkRWl-|g(T$Qbry3(~ zY+^P>+rX+Z(Ye9R6K758)D_ifBHR}TFrGj=WE$oo4l2{bu=pHxk==vxn4QST|uF`>(|EOEoI6zVV~hC)Qo!3 zO|#Xs?{c@E8+}OzJ{2Y~EkoF}FJC@6Icep_mbY22Tj3TpXI2w5l?aSZsV7@1Zrt9m z4^G8)A;!+bF-?U7Ku_&4=VVgj)*1p?UfnI|l(^XF z>PIf8$E^-#>pr?isVNq|#33&5SV`h8?Gd8A)c)2PRm5zG_2j3Sr*#X`e?}>IW!D%VK*=`nhIae zv)Ki85DOz|E(nsDE1*amSvbEgbM1|zq+Dv8z z+yx=_UhS+|S9dbT@+`^l9@ho9`&`Kgklz}R(Nc6C?K*a%9xN-&94bYSZyU*0i4@aX z%7xb2GLi*1qNPR&h{0}8sEIe%YPA4KnJnE|gll5KBwQL0Z}^3H;4CagNR#Tt6mCnx z63?QCG=g?2*Sqi2U8jhlxaC_~4i#j2+>A#g&^dF_A4jGk$n4a~MJ#Y?DmUIav zP*-czjkD@TnMvYfIEJQlwkq1B`V9ud)xc~ySEE^P64T))Xw(^ZXdDBk-7y`+G?>LV zhv)lO&-T%A;=wWcO|L`!?xZ^$sHx%g#^GA@juoie?553|Qe@7yacKWDouim;#$yqV zM#JWqx3ziA10A?c&&Hi`vp;Mebm*C3yWjoT@o*4FJza$6)N5Ph8l};u!&$FKgHAuD zC*yvn+q>2|I6$wPd>W!QD|N=rDf&Vih7FEpqVmaJ?^!+7$J>{rJ9pA8%cW z&@2PE(VU)cU{B!N;qoTe#(s*DrS=7fr)JZ`<`ehj#4IqP8KZ-wD8Dv6F*hMnjo~fT zc*f)lbnV!n<#V2w7PWx?1$)A#*e$Ntr55{)=hhSjqz7Dr|C5bDLLj@SzK>&Q`q!Cl zD(BI{Y^s{7QN1u{53;Mzc4L~1x^Ztx{my{;aep{|_G#7UwezPtIPdC*)jfY+{Aec9 z;n5y^pe=Ir|Mqp{w)*j6frx>G%Yf2BeSyf(FzPjX=6SZypSNLFM{EsO-{pVp#7($z zlvnqXTPLM0k|p|SDJ%|XhD?0~1?+)!c`p8kmO9iq#GvE2%+zspFXJ};MomD7YaZ)T kfx?H?3u79@v&j@BR)<&Vc-E(S0X~m~ur*_k*@U+GJHRQ_m;e9( literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/.lang/de.po b/app/examples/Games/Concent/.lang/de.po new file mode 100644 index 00000000..62bf2ce3 --- /dev/null +++ b/app/examples/Games/Concent/.lang/de.po @@ -0,0 +1,168 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Concéntrese Geográfico" +msgstr "Memory" + +#: .project:2 +msgid "Esta versión incorpora soporte para el idioma inglés y español (nativo), disfrute este juego!, se han incorporado las correcciones sugeridas por el director del proyecto Gambas, el proyecto ha sido traducido al inglés usando la herramienta integrada de Gambas Ide" +msgstr "-" + +#: fotos.form:292 +msgid "" +"Estimados amigos, este formulario cumple la única función de\n" +" cargar en 40 botones, las imágenes de las caritas que servirán \n" +"para el juego, es posible que exista una mejor manera de \n" +"hacer esto, por eso... escucho sugerencias, posteriormente \n" +"en el código se crea un array de objetos a fin de referencialos \n" +"como botoncito[i].Picture y copiar este atributo en particular al \n" +"tablero de juego. Att: Jairo Alonso Badillo\n" +" jbadbe@gmail.com" +msgstr "-" + +#: frmAcerca.form:14 +msgid "" +"Concéntrese Geográfico v 0.3\n" +"\n" +"Programado para Gambas por:\n" +"Jairo Alonso Badillo Bedoya - jbadbe@gmail.com\n" +"Publicado bajo los términos de la licencia GPL\n" +"Viva Colombia!!!!!\n" +"Visite http://www.linuxeam.tk -Una comunidad Linux" +msgstr "" +"Memory v 0.3\n" +"\n" +"Für Gambas programmiert von:\n" +"Jairo Alonso Badillo Bedoya - jbadbe@gmail.com\n" +"Veröffentlicht unter der GPL-Lizenz" + +#: frmAcerca.form:20 frmInstrucciones.form:28 +msgid "&Aceptar" +msgstr "&Ok" + +#: frmInstrucciones.form:15 +msgid "COMO JUGAR CONCÉNTRESE?" +msgstr "WIE SPIELT MAN MEMORY?" + +#: frmInstrucciones.form:21 +msgid "" +"Se trata de un Juego Bastante sencillo: \n" +"\n" +"1.) El objetivo del juego es probar tu retentiva y nivel de concentración, para lo cual\n" +" debes encontrar los pares de rostros que hay ocultos en el tablero de\n" +" concéntrese. \n" +" \n" +"2.) El juego tiene tres niveles (Principiante, Medio, Experto), que te permite\n" +" escoger entre tres tamaños del tablero para añadir emoción y dificultad al juego,\n" +" para Acceder a estas opciones debes ingresar por el menu Configuración - Nivel,\n" +" y seleccionar uno de los tres preestablecidos. \n" +" \n" +"3). Así mismo, tienes la posibilidad de incrementar tus conocimientos al jugar en\n" +" Modo Pregunta, lo que incrementa tu nivel educativo, esto se logra mediante un\n" +" cuestionario emergente que deberás responder con cada acierto en el tablero, si\n" +" contestas correctamente tendrás un puntaje más alto y podrás competir con tus\n" +" amigos. (Esta función se encuentra en construcción debido a la migracion de\n" +" Visual Basic hacia Gambas, y experimentar el poder de Linux, viva el\n" +" OpenSource!!! \n" +" \n" +"4.) El juego también te permite activar o desactivar los sonidos de ambientación,\n" +" para lo cual debes cambiar el estado del menú Configuración - Sonido. \n" +" \n" +"5.) Recuerda: Para redistribuir el Tablero Pulsa F2 , para comenzar a jugar pulsa\n" +" F4, los rostros serán cargados aleatoriamente, por lo que no debes intentar\n" +" aprenderte el tablero de memoria...!, Esto sí que va a ser un verdadero reto!. \n" +" \n" +"6.) Estoy actualmente diseñando los modulos de aprendizaje que podrás conseguir\n" +" muy pronto; para comentarios y / o sugerencias escribe a: jbadbe@gmail.com.\n" +msgstr "" + +#: principal.class:166 +msgid "Jugada Errada" +msgstr "Falscher Zug" + +#: principal.class:202 +msgid "Felicitaciones, has cumplido la mision" +msgstr "Glückwunsch, du hast die Mission vollendet." + +#: principal.class:247 +msgid "PULSE F2 PARA REPARTIR DE NUEVO" +msgstr "F2 FÜR NEUSTART" + +#: principal.form:45 +msgid "Enjoy Open Source!" +msgstr "Genieße Open Source!" + +#: principal.form:50 +msgid "&Configuración" +msgstr "&Einstellungen" + +#: principal.form:53 +msgid "&Nuevo Juego" +msgstr "&Neues Spiel" + +#: principal.form:58 +msgid "&Mejores Puntajes" +msgstr "&Beste Ergebnisse" + +#: principal.form:64 +msgid "Nivel" +msgstr "Niveau" + +#: principal.form:68 +msgid "&Principiante" +msgstr "&Anfänger" + +#: principal.form:76 +msgid "M&edio" +msgstr "&Mittel" + +#: principal.form:83 +msgid "E&xperto" +msgstr "E&xperte" + +#: principal.form:90 +msgid "&Sonido" +msgstr "&Sound" + +#: principal.form:100 +msgid "&Salir" +msgstr "&Beenden" + +#: principal.form:105 +msgid "&Ayuda" +msgstr "&Hilfe" + +#: principal.form:108 +msgid "A&cerca de" +msgstr "&Über..." + +#: principal.form:112 +msgid "&Instrucciones" +msgstr "&Anweisungen" + +#: principal.form:251 +msgid "Jugadas: " +msgstr "Züge:" + +#: principal.form:259 +msgid "Tiempo: " +msgstr "Zeit:" + +#: principal.form:268 +msgid "PULSE F4 PARA COMENZAR LA MISION" +msgstr "F4 ZUM START DER MISSION" + +#: principal.form:283 +msgid "0" +msgstr "-" diff --git a/app/examples/Games/Concent/.lang/en.mo b/app/examples/Games/Concent/.lang/en.mo new file mode 100644 index 0000000000000000000000000000000000000000..98fac835ece4f03f60440370e54de3e8343e00ba GIT binary patch literal 2997 zcmaJ@O^hQ)6)qrz(92Hh79$dAqBe zs%p=eTMisJ0S9gfDI5@c3Mdi>M1+JnAQT}uA|b?$3nv5;-1uJGW;fb|mfBx;^{e;) zzVcrldEom3*C%m*3im&8e-`%}@4yePZ@g28hkz3JAaDWvAn+yN2Y@dFKLq?P@O{7^ z0pAaN70B|h1K$Ju4Ukp70gQmZ2Yw9rryc$S_*cANc-Pk6-+_%Rm3 z1o&s*4}sr8Ql0{S=EBzAS9X{Jd0z=+y8+1hzX^OC_`Tiwt3dVxX>4i*%8S-eEv!?XCbbE2!7HO$tt{QFRbfSI z;Te!_Dr#iE5`+-(;x%*TUO&nxpC38zl=_DP=DwUp=*7c&BQaej=X^ppZ zP3F2RfhxCaNgZ0GGEddjqLRAoX10nZbz16-HKlT4skENL=8mhh!BeiNl!WgJk;2Sm z5Z%%%Ne8yHRjTDhzOdwVP&5x=*}HVYNx!uv8;4GN%E55=rZP zsa&ukF$=q<@lqL@+S+C6qDXv@v{KGF0&^{7S_WM2Xbm%RhQCfOIckl%ce}5UlSAm4}!`M_RyjFe`Z*K&l- z1W zZ;E7aI;~Ml*hI8JXPjEDSrjA!#WX>C6MvWD5PI}hBNp8^TH?CG+=HgK?74i{P6!x3Vs3~e$sLl+$(1T;6qZ2my%wTY})LrX}yD~kBhW%l3*T2tq zFGnEDfZpiL)=T&k)OonHETuMIq--voSK--{+4auTcjeeEbVMfsgX9>$9zC7v5Q*0K zwrJf@=2vGtmmvCUs?Y{KE?UXC4$Szq$wBz7IJI@4 zFxP%7d1L%M&Bint#4ph7Mt@32$jfu0;du#~2a6gIYq)=9PxH@XB2~`QzUPE={eKC* z^Van4=g!!{iH5t zbHAl@dk^hm&chrW0%p{HPT)lXgByPK^^(aReAAo{O;QV_zuoNE5_MCi?JnkOOg_j7 zWT_}!6NLFh%oT>mE}2xQaJF936W5fnh-#?4*G4}HLII!RiIdcYqbMKK>uyq^@=S+$ zV-_bEa~=~c+a`NT5e`foYD}(m0M+cI!Py0Na^W~a_B?9Cw>81?nXStlbZaPO((<-k zp*)Qh3u#cO3UiycSta%9wy|eqY{1qo|1rQZ5kOE)^xJfRM|g}Ien}NrOlBZI(9AX@ z{0E@Qu5hvr@yn_3TW+Z, YEAR. +# +# #-#-#-#-# frmAcerca.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/frmAcerca.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# frmInstrucciones.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/frmInstrucciones.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# funciones.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/funciones.module +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# principal.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent/principal.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# #project.pot (PACKAGE VERSION) #-#-#-#-# +# /home/jbadbe/concent_gb/.project +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Concéntrese Geográfico" +msgstr "Geografic Concent" + +#: .project:2 +msgid "Esta versión incorpora soporte para el idioma inglés y español (nativo), disfrute este juego!, se han incorporado las correcciones sugeridas por el director del proyecto Gambas, el proyecto ha sido traducido al inglés usando la herramienta integrada de Gambas Ide" +msgstr "Ths version incorporate the english and spanish support, enjoy this game, the corrections and sugerences made by gambas development group (Benoit Missini) are finished." + +#: fotos.form:292 +msgid "" +"Estimados amigos, este formulario cumple la única función de\n" +" cargar en 40 botones, las imágenes de las caritas que servirán \n" +"para el juego, es posible que exista una mejor manera de \n" +"hacer esto, por eso... escucho sugerencias, posteriormente \n" +"en el código se crea un array de objetos a fin de referencialos \n" +"como botoncito[i].Picture y copiar este atributo en particular al \n" +"tablero de juego. Att: Jairo Alonso Badillo\n" +" jbadbe@gmail.com" +msgstr "" +"This form cumplies with the unique function of \n" +" load 40 buttons with images for the game\n" +", may it would can be resolved with other method, but I know not it." + +#: frmAcerca.form:14 +msgid "" +"Concéntrese Geográfico v 0.3\n" +"\n" +"Programado para Gambas por:\n" +"Jairo Alonso Badillo Bedoya - jbadbe@gmail.com\n" +"Publicado bajo los términos de la licencia GPL\n" +"Viva Colombia!!!!!\n" +"Visite http://www.linuxeam.tk -Una comunidad Linux" +msgstr "" +"Geografic Concent v 0.3\n" +"\n" +"Writed for Gambas by:\n" +"Jairo Alonso Badillo Bedoya-jbadbe@gmail.com\n" +"Published under GPL terms license\n" +"Live Colombia!!!!!!\n" +"Please visit http://www.linuxeam.tk - A Linux comunity" + +#: frmAcerca.form:20 frmInstrucciones.form:28 +msgid "&Aceptar" +msgstr "&Ok" + +#: frmInstrucciones.form:15 +msgid "COMO JUGAR CONCÉNTRESE?" +msgstr "HOW TO PLAY THIS GAME?" + +#: frmInstrucciones.form:21 +#, fuzzy +msgid "" +"Se trata de un Juego Bastante sencillo: \n" +"\n" +"1.) El objetivo del juego es probar tu retentiva y nivel de concentración, para lo cual\n" +" debes encontrar los pares de rostros que hay ocultos en el tablero de\n" +" concéntrese. \n" +" \n" +"2.) El juego tiene tres niveles (Principiante, Medio, Experto), que te permite\n" +" escoger entre tres tamaños del tablero para añadir emoción y dificultad al juego,\n" +" para Acceder a estas opciones debes ingresar por el menu Configuración - Nivel,\n" +" y seleccionar uno de los tres preestablecidos. \n" +" \n" +"3). Así mismo, tienes la posibilidad de incrementar tus conocimientos al jugar en\n" +" Modo Pregunta, lo que incrementa tu nivel educativo, esto se logra mediante un\n" +" cuestionario emergente que deberás responder con cada acierto en el tablero, si\n" +" contestas correctamente tendrás un puntaje más alto y podrás competir con tus\n" +" amigos. (Esta función se encuentra en construcción debido a la migracion de\n" +" Visual Basic hacia Gambas, y experimentar el poder de Linux, viva el\n" +" OpenSource!!! \n" +" \n" +"4.) El juego también te permite activar o desactivar los sonidos de ambientación,\n" +" para lo cual debes cambiar el estado del menú Configuración - Sonido. \n" +" \n" +"5.) Recuerda: Para redistribuir el Tablero Pulsa F2 , para comenzar a jugar pulsa\n" +" F4, los rostros serán cargados aleatoriamente, por lo que no debes intentar\n" +" aprenderte el tablero de memoria...!, Esto sí que va a ser un verdadero reto!. \n" +" \n" +"6.) Estoy actualmente diseñando los modulos de aprendizaje que podrás conseguir\n" +" muy pronto; para comentarios y / o sugerencias escribe a: jbadbe@gmail.com.\n" +msgstr "" +"This Game is very easy:\n" +"\n" +"\n" +"1.) The Game Objetive is find the faces under each button, the faces have duplicates,\n" +" in a movement you have find two equal faces.\n" +"\n" +" 2.) You can choose the dificult level in the config menu, in each level the board have\n" +" more buttons, enjoy this game!\n" +"\n" +" 3). Actually I am development the ask module, this module permit answer many asks\n" +" in the game for increment your points.\n" +"\n" +" 4.) You can activate or desactivate the sounds in the configuration menu\n" +" (Configuration - Sound).\n" +"\n" +" 5.) Remember: for init a mision press F4, if you want redistribute the board press F2\n" +" in any moment, the game will go to reset. The faces are load in rand mode, Is \n" +" imposible remember the board...!!!!\n" +"\n" +" 6.) For comments and ask, plese write me to jbadbe@gmail.com, please excuseme \n" +" for my English, I am Colombiano and I dont speak english.\n" +"\n" + +#: principal.class:166 +msgid "Jugada Errada" +msgstr "Bad move!" + +#: principal.class:202 +msgid "Felicitaciones, has cumplido la mision" +msgstr "Congratulations, the mission has been accomplished." + +#: principal.class:247 +msgid "PULSE F2 PARA REPARTIR DE NUEVO" +msgstr "PRESS F2 TO PLAY AGAIN" + +#: principal.form:45 +msgid "Enjoy Open Source!" +msgstr "Enjoy Open Source" + +#: principal.form:50 +msgid "&Configuración" +msgstr "&Configuration" + +#: principal.form:53 +msgid "&Nuevo Juego" +msgstr "&New Game" + +#: principal.form:58 +msgid "&Mejores Puntajes" +msgstr "&Best Scores" + +#: principal.form:64 +msgid "Nivel" +msgstr "Level" + +#: principal.form:68 +msgid "&Principiante" +msgstr "&Beginner" + +#: principal.form:76 +msgid "M&edio" +msgstr "M&edium" + +#: principal.form:83 +msgid "E&xperto" +msgstr "E&xpert" + +#: principal.form:90 +msgid "&Sonido" +msgstr "&Sound" + +#: principal.form:100 +msgid "&Salir" +msgstr "&Exit" + +#: principal.form:105 +msgid "&Ayuda" +msgstr "&Help" + +#: principal.form:108 +msgid "A&cerca de" +msgstr "&About this Game" + +#: principal.form:112 +msgid "&Instrucciones" +msgstr "&Instructions" + +#: principal.form:251 +msgid "Jugadas: " +msgstr "Movements" + +#: principal.form:259 +msgid "Tiempo: " +msgstr "Time:" + +#: principal.form:268 +msgid "PULSE F4 PARA COMENZAR LA MISION" +msgstr "PRESS F4 TO START THE MISSION" + +#: principal.form:283 +msgid "0" +msgstr "0" diff --git a/app/examples/Games/Concent/.lang/es.mo b/app/examples/Games/Concent/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..b3f76f0945d0ab724d3cd3215ae80cff56e3dbd1 GIT binary patch literal 6698 zcmeH~PmCN_5yqQfL+J3=KnMhhS_w9`*csc2LD9qs9@b;CW@mS_e=JDE;Z673*|+rc z8^7*edkqH=ryLRDPL4isfZU=;kuq>W3a1>SC`ep5BW^kHtJkx!gULCUZK>y*neMK7 zRrS?Zuirg$>h}r9Hy7vo;CDFR`2-&vvqzHTQScV{Dex}%1@PD4=fU5CUjqLCeir;Q z_&M+mQ15>W{51GBs3zY5Q}A8z3GiPFeB_f!@*cmR1l8aB;2H4rsU&%f2cH062fuwf zNtXHl9~f6Zk7E2Y;NzhB6Odbyr@(K49ZjX#`i0nEpz?(FSmYjP~!`r`uioQ@%;wW_+ADDya#H0e*@M2(l^@mklt8ZT?;9V zF~vCe&Qg-EaDJ8ZF-|>VPC@ncHO{Yd{_jC4;(?1;BmIHfgbOGnZ~WTIX*Ow zWVxT&S#1Jej+)#g%d5WJbCV{R%*|gdljXItszZ}y&X=}ImWTGh2V2RgDQk0}|72jZ3Nczi}4VjVLCaasnO}Vjqy}u=^n;Wb1 zmp68{2HS%lB>2zf_sSY4R<2t=3G-L>T;@OWf*i_)?lWmR3fya^CigNk!N_$p9h*vK zKJ?NX#sx3^!k3koADY}11!!}BWTYbpW0Q~VmC4k&qMP|?I%>v+%hb}?9C#^wCH4GX zn7Wd0xs}2Q*DaCaX0)E(bcaS(ec`8LXO{HfugcX{_Un4qJAeN6?c3eLmCYS%rrrAI z(%CJI;He=oCYN=+lnj>d%xtKAGAIxHk!;RvDcimYnO#Z-Rc++ZhDwVf|X zT1g>guc20&x!k6bnJ_Uy$nK>JGWN9+dnE?zrt?=OmY;2ri5_z`;ofR22?~b}GM17a zXFKLj-NUlVjd7_r+B;6G)lg!YYP-nPl$K%xS_p|QEWpxuQOrg~DZCslbQwKM0s3ks$wsx7OR%@2lhCt~itjGAf(y!~D{J$1U zA8M`Snx%8dX{#qIaE>f%xs($0)s(*RWpbmLXyFG0hA6eas-7gnWedZT4R>gZWVE}! zJ&cZrq?>;i zl1g1wo)Dm{F-0mIxgEnH+e~*4rw%H_vNN{1gR88a!lJ{$YIDZot5}vq8Rd&y#Nb+`i7Od;C_V~yqN5YR@iDpgZ76?Q z&R8Tes&$mM*y@E2H=@nsXeEwlGHX~p{SsA=l?2p_YH87wQ>ax1C99b)HKi;MMWe0g zJ{>e4rVt~*wMJoKoBLM$DQ>h1v-4f+l^MO$8l1$!axF)3PctyGGXZ-ZC95eBXJhTk-ws2 zPYLW6rGb;IjRa7#IBHkqP+Qct-KT?gF=v;Sv}Z1TsAsV0XsHLffc$BT2T?9n$3K)) zRov;s@u80?Ga~C-Dn8ivu|7Irbjv_{6p`)_L_3Ie&tH3()$Kkiw)OW|;+7?GAvZl4 zsh|Y7LGF-kZMQ;p7CJp@ipr=;E`*2j*zztu4ZWxIoaqfQrPnSgEuqXpau|6!@8ZCW z^R}>tY42J>=zvnKigb&8r7EUVTN|RSI{l^8gOjZ2SQse76g|KWqmQizlz`*(oAQ>r zuvMT~u#624ouwd$|_xPN^hHwRlfgVNDvXUiVOsn@w`YTJ{a4*FZ2;b!M*Uw*XN z8?Jr##m$XDy3SbY>;zL*h1Q@a*LK&}WureF$mPMXzqWp*fAuO`=n7oh<*M@`pi@MmokgjUmm-slEv54B}!oyj8sU%GKkIziQ0t zVjI~qo~C+`;VO)m~}_GTyV7ANl(C+`*~?-nQT79ab& G#eV@%=8I4O literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/.lang/es.po b/app/examples/Games/Concent/.lang/es.po new file mode 100644 index 00000000..f6c79229 --- /dev/null +++ b/app/examples/Games/Concent/.lang/es.po @@ -0,0 +1,120 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: principal.class:694 +msgid "0" +msgstr "0" + +#: frmAcerca.class:37 frmInstrucciones.class:40 +msgid "&Aceptar" +msgstr "&Aceptar" + +#: principal.class:503 +msgid "A&cerca de" +msgstr "A&cerca de" + +#: principal.class:500 +msgid "&Ayuda" +msgstr "&Ayuda" + +#: frmInstrucciones.class:30 +msgid "COMO JUGAR CONCÉNTRESE?" +msgstr "¿CÓMO JUGAR CONCÉNTRESE?" + +#: .project:1 +msgid "Concéntrese Geográfico" +msgstr "Concéntrese Geográfico" + +#: frmAcerca.class:31 +msgid "Concéntrese Geográfico v 0.3\n\nProgramado para Gambas por:\nJairo Alonso Badillo Bedoya - jbadbe@gmail.com\nPublicado bajo los términos de la licencia GPL\nViva Colombia!!!!!\nVisite http://www.linuxeam.tk -Una comunidad Linux" +msgstr "Concéntrese Geográfico v 0.3\n\nProgramado para Gambas por:\nJairo Alonso Badillo Bedoya - jbadbe@gmail.com\nPublicado bajo los términos de la licencia GPL\nViva Colombia!!!!!\nVisite http://www.linuxeam.tk -Una comunidad Linux" + +#: principal.class:444 +msgid "&Configuración" +msgstr "&Configuración" + +#: principal.class:439 +msgid "Enjoy Open Source!" +msgstr "¡Disfrute del Código Abierto!" + +#: .project:2 +msgid "Esta versión incorpora soporte para el idioma inglés y español (nativo), disfrute este juego!, se han incorporado las correcciones sugeridas por el director del proyecto Gambas, el proyecto ha sido traducido al inglés usando la herramienta integrada de Gambas Ide" +msgstr "Esta versión incorpora soporte para el idioma inglés y español (nativo), disfrute este juego!, se han incorporado las correcciones sugeridas por el director del proyecto Gambas, el proyecto ha sido traducido al inglés usando la herramienta integrada de Gam" + +#: fotos.class:304 +msgid "Estimados amigos, este formulario cumple la única función de\n cargar en 40 botones, las imágenes de las caritas que servirán \npara el juego, es posible que exista una mejor manera de \nhacer esto, por eso... escucho sugerencias, posteriormente \nen el código se crea un array de objetos a fin de referencialos \ncomo botoncito[i].Picture y copiar este atributo en particular al \ntablero de juego. Att: Jairo Alonso Badillo\n jbadbe@gmail.com" +msgstr "Estimados amigos, este formulario cumple la única función de\n cargar en 40 botones, las imágenes de las caritas que servirán \npara el juego, es posible que exista una mejor manera de \nhacer esto, por eso... escucho sugerencias, posteriormente \nen el código se crea un array de objetos a fin de referencialos \ncomo botoncito[i].Picture y copiar este atributo en particular al \ntablero de juego. Att: Jairo Alonso Badillo\n jbadbe@gmail.com" + +#: principal.class:477 +msgid "E&xperto" +msgstr "E&xperto" + +#: principal.class:202 +msgid "Felicitaciones, has cumplido la mision" +msgstr "Felicitaciones, has cumplido la mision" + +#: principal.class:507 +msgid "&Instrucciones" +msgstr "&Instrucciones" + +#: principal.class:166 +msgid "Jugada Errada" +msgstr "Jugada Errada" + +#: principal.class:663 +msgid "Jugadas: " +msgstr "Jugadas: " + +#: principal.class:470 +msgid "M&edio" +msgstr "M&edio" + +#: principal.class:452 +msgid "&Mejores Puntajes" +msgstr "&Mejores Puntajes" + +#: principal.class:458 +msgid "Nivel" +msgstr "Nivel" + +#: principal.class:447 +msgid "&Nuevo Juego" +msgstr "&Nuevo Juego" + +#: principal.class:462 +msgid "&Principiante" +msgstr "&Principiante" + +#: principal.class:247 +msgid "PULSE F2 PARA REPARTIR DE NUEVO" +msgstr "PULSE F2 PARA REPARTIR DE NUEVO" + +#: principal.class:384 +msgid "PULSE F4 PARA COMENZAR LA MISION" +msgstr "PULSE F4 PARA COMENZAR LA MISION" + +#: principal.class:495 +msgid "&Salir" +msgstr "&Salir" + +#: frmInstrucciones.class:35 +msgid "Se trata de un Juego Bastante sencillo: \n\n\n1.) El objetivo del juego es probar tu retentiva y nivel de concentración, para lo cual\n debes encontrar los pares de rostros que hay ocultos en el tablero de\n concéntrese. \n \n2.) El juego tiene tres niveles (Principiante, Medio, Experto), que te permite\n escoger entre tres tamaños del tablero para añadir emoción y dificultad al juego,\n para Acceder a estas opciones debes ingresar por el menu Configuración - Nivel,\n y seleccionar uno de los tres preestablecidos. \n\n \n3). Así mismo, tienes la posibilidad de incrementar tus conocimientos al jugar en\n Modo Pregunta, lo que incrementa tu nivel educativo, esto se logra mediante un\n cuestionario emergente que deberás responder con cada acierto en el tablero, si\n contestas correctamente tendrás un puntaje más alto y podrás competir con tus\n amigos. (Esta función se encuentra en construcción debido a la migracion de\n Visual Basic hacia Gambas, y experimentar el poder de Linux, viva el\n OpenSource!!! \n \n4.) El juego también te permite activar o desactivar los sonidos de ambientación,\n para lo cual debes cambiar el estado del menú Configuración - Sonido. \n \n5.) Recuerda: Para redistribuir el Tablero Pulsa F2 , para comenzar a jugar pulsa\n F4, los rostros serán cargados aleatoriamente, por lo que no debes intentar\n aprenderte el tablero de memoria...!, Esto sí que va a ser un verdadero reto!. \n \n6.) Estoy actualmente diseñando los modulos de aprendizaje que podrás conseguir\n muy pronto; para comentarios y / o sugerencias escribe a: jbadbe@gmail.com.\n" +msgstr "Se trata de un Juego Bastante sencillo: \n\n\n1.) El objetivo del juego es probar tu retentiva y nivel de concentración, para lo cual\n debes encontrar los pares de rostros que hay ocultos en el tablero de\n concéntrese. \n \n2.) El juego tiene tres niveles (Principiante, Medio, Experto), que te permite\n escoger entre tres tamaños del tablero para añadir emoción y dificultad al juego,\n para Acceder a estas opciones debes ingresar por el menu Configuración - Nivel,\n y seleccionar uno de los tres preestablecidos. \n\n \n3). Así mismo, tienes la posibilidad de incrementar tus conocimientos al jugar en\n Modo Pregunta, lo que incrementa tu nivel educativo, esto se logra mediante un\n cuestionario emergente que deberás responder con cada acierto en el tablero, si\n contestas correctamente tendrás un puntaje más alto y podrás competir con tus\n amigos. (Esta función se encuentra en construcción debido a la migracion de\n Visual Basic hacia Gambas, y experimentar el poder de Linux, viva el\n OpenSource!!! \n \n4.) El juego también te permite activar o desactivar los sonidos de ambientación,\n para lo cual debes cambiar el estado del menú Configuración - Sonido. \n \n5.) Recuerda: Para redistribuir el Tablero Pulsa F2 , para comenzar a jugar pulsa\n F4, los rostros serán cargados aleatoriamente, por lo que no debes intentar\n aprenderte el tablero de memoria...!, Esto sí que va a ser un verdadero reto!. \n \n6.) Estoy actualmente diseñando los modulos de aprendizaje que podrás conseguir\n muy pronto; para comentarios y / o sugerencias escribe a: jbadbe@gmail.com.\n" + +#: principal.class:484 +msgid "&Sonido" +msgstr "&Sonido" + +#: principal.class:671 +msgid "Tiempo: " +msgstr "Tiempo: " + diff --git a/app/examples/Games/Concent/.lang/fr.mo b/app/examples/Games/Concent/.lang/fr.mo new file mode 100644 index 0000000000000000000000000000000000000000..54269de3b4ad974a783e0ba485dab39b836c8f51 GIT binary patch literal 1873 zcmaKsy^kA37{(_MKH~5hz9kTEkXR92)(Ia)WI~d~w|Cg_7q<5x>BhTr&Y1PgYIfF_ zOM?`N0s#_DBsxTS1r-eiojXLC3M2&o0SzS*&+EI(2?-c^_UGN1edn2X-e-S3b^MM% zdk+0Y^t=KLlC#3}k&fAnUmeB82!1L&L>))=7vY5_KI$JUo_60 z+u7Nv=h{>^m7Lf8M-(PT5>~3Pdzn#(heRCQER^$B#Aar9sb45VBU`ytofaDk3u=$d zbFRo|>0MBUb?)$fV1R`bFlEK)vGsSjIOxTPE`XyB#h z)o3%Ot$4_jYlD6`REw3oa9w)Uplfk747>esJ)-yejc#l0dcPOfI7`n?)s2rt zy7zie{=3!TNG< zQhB&T1n=V&ulDUBA}X9MR)tru*V!L9kTJFUNj#*8nlZKGgf5GfB_teyN;D`=e`t(2 zb=t!RmAmH&`Tw2>|J@PGJt}Qg?$u$OS@ul#UO{c2VHD1TM)Zf_BvJht+mMxewf zb^@#*E12w`Uf>Gb#{0M6I;?A_FoDWh!Gymw@Wpa(-x#Z$pQQ!slMB4yP@}m%@CKJ= zU359!bIuIDMAkW!E<)UUT2ccaF}thG$|$lD(O{5Vi?7ot8Ls@&2Z!\n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: fotos.class:303 +msgid "" +"Estimados amigos, este formulario cumple la única función de\n" +" cargar en 40 botones, las imágenes de las caritas que servirán \n" +"para el juego, es posible que exista una mejor manera de \n" +"hacer esto, por eso... escucho sugerencias, posteriormente \n" +"en el código se crea un array de objetos a fin de referencialos \n" +"como botoncito[i].Picture y copiar este atributo en particular al \n" +"tablero de juego. Att: Jairo Alonso Badillo\n" +" jbadbe@gmail.com" +msgstr "" + +#: frmAcerca.class:30 +msgid "" +"Concéntrese Geográfico v 0.3\n" +"\n" +"Programado para Gambas por:\n" +"Jairo Alonso Badillo Bedoya - jbadbe@gmail.com\n" +"Publicado bajo los términos de la licencia GPL\n" +"Viva Colombia!!!!!\n" +"Visite http://www.linuxeam.tk -Una comunidad Linux" +msgstr "" +"Geografic Concent v 0.3\n" +"\n" +"Ecrit pour Gambas par:\n" +"Jairo Alonso Badillo Bedoya-jbadbe@gmail.com\n" +"Publié sous license GNU\n" +"Vive la Colombie!!!!!!\n" +"Veuillez visiter http://www.linuxeam.tk - Une communauté Linux" + +#: frmAcerca.class:36 frmInstrucciones.class:43 +msgid "&Aceptar" +msgstr "&OK" + +#: frmInstrucciones.class:30 +msgid "COMO JUGAR CONCÉNTRESE?" +msgstr "COMMENT JOUER A CE JEU ?" + +#: frmInstrucciones.class:36 +msgid "" +"Se trata de un Juego Bastante sencillo: \n" +"\n" +"1.) El objetivo del juego es probar tu retentiva y nivel de concentración, para lo cual\n" +" debes encontrar los pares de rostros que hay ocultos en el tablero de\n" +" concéntrese. \n" +" \n" +"2.) El juego tiene tres niveles (Principiante, Medio, Experto), que te permite\n" +" escoger entre tres tamaños del tablero para añadir emoción y dificultad al juego,\n" +" para Acceder a estas opciones debes ingresar por el menu Configuración - Nivel,\n" +" y seleccionar uno de los tres preestablecidos. \n" +" \n" +"3). Así mismo, tienes la posibilidad de incrementar tus conocimientos al jugar en\n" +" Modo Pregunta, lo que incrementa tu nivel educativo, esto se logra mediante un\n" +" cuestionario emergente que deberás responder con cada acierto en el tablero, si\n" +" contestas correctamente tendrás un puntaje más alto y podrás competir con tus\n" +" amigos. (Esta función se encuentra en construcción debido a la migracion de\n" +" Visual Basic hacia Gambas, y experimentar el poder de Linux, viva el\n" +" OpenSource!!! \n" +" \n" +"4.) El juego también te permite activar o desactivar los sonidos de ambientación,\n" +" para lo cual debes cambiar el estado del menú Configuración - Sonido. \n" +" \n" +"5.) Recuerda: Para redistribuir el Tablero Pulsa F2 , para comenzar a jugar pulsa\n" +" F4, los rostros serán cargados aleatoriamente, por lo que no debes intentar\n" +" aprenderte el tablero de memoria...!, Esto sí que va a ser un verdadero reto!. \n" +" \n" +"6.) Estoy actualmente diseñando los modulos de aprendizaje que podrás conseguir\n" +" muy pronto; para comentarios y / o sugerencias escribe a: jbadbe@gmail.com.\n" +msgstr "" + +#: principal.class:166 +msgid "Jugada Errada" +msgstr "Mouvement incorrect !" + +#: principal.class:202 +msgid "Felicitaciones, has cumplido la mision" +msgstr "Félicitations, vous avez rempli la mission." + +#: principal.class:247 +msgid "PULSE F2 PARA REPARTIR DE NUEVO" +msgstr "APPUYEZ SUR F2 POUR RECOMMENCER" + +#: principal.class:384 +msgid "PULSE F4 PARA COMENZAR LA MISION" +msgstr "APPUYEZ SUR F4 POUR COMMENCER" + +#: principal.class:439 +msgid "Enjoy Open Source!" +msgstr "Profitez des logiciels libres !" + +#: principal.class:444 +msgid "&Configuración" +msgstr "&Configuration" + +#: principal.class:447 +msgid "&Nuevo Juego" +msgstr "&Nouveau jeu" + +#: principal.class:452 +msgid "&Mejores Puntajes" +msgstr "Meilleurs &scores" + +#: principal.class:458 +msgid "Nivel" +msgstr "Niveau" + +#: principal.class:462 +msgid "&Principiante" +msgstr "&Débutant" + +#: principal.class:470 +msgid "M&edio" +msgstr "&Moyen" + +#: principal.class:477 +msgid "E&xperto" +msgstr "E&xpert" + +#: principal.class:484 +msgid "&Sonido" +msgstr "&Son" + +#: principal.class:494 +msgid "&Salir" +msgstr "&Quitter" + +#: principal.class:499 +msgid "&Ayuda" +msgstr "&Aide" + +#: principal.class:502 +msgid "A&cerca de" +msgstr "A propos de..." + +#: principal.class:506 +msgid "&Instrucciones" +msgstr "&Instructions (non traduites)" + +#: principal.class:645 +msgid "Jugadas: " +msgstr "Mouvements :" + +#: principal.class:653 +msgid "Tiempo: " +msgstr "Temps :" + +#: principal.class:677 +msgid "0" +msgstr "" + +#~ msgid "Concéntrese Geográfico" +#~ msgstr "Concentré Géographique" diff --git a/app/examples/Games/Concent/.project b/app/examples/Games/Concent/.project new file mode 100644 index 00000000..3b78d34e --- /dev/null +++ b/app/examples/Games/Concent/.project @@ -0,0 +1,22 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Concéntrese Geográfico +Startup=principal +Icon=imagenes/logo.png +Version=3.6.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.db +Component=gb.sdl.sound +Description="Esta versión incorpora soporte para el idioma inglés y español (nativo), disfrute este juego!, se han incorporado las correcciones sugeridas por el director del proyecto Gambas, el proyecto ha sido traducido al inglés usando la herramienta integrada de Gambas Ide" +Authors="Jairo Alonso Badillo Bedoya" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 +Systems=redhat diff --git a/app/examples/Games/Concent/.src/fotos.class b/app/examples/Games/Concent/.src/fotos.class new file mode 100644 index 00000000..5c4d1122 --- /dev/null +++ b/app/examples/Games/Concent/.src/fotos.class @@ -0,0 +1,7 @@ +' Gambas class file + + + +PUBLIC SUB Form_Open() + +END diff --git a/app/examples/Games/Concent/.src/fotos.form b/app/examples/Games/Concent/.src/fotos.form new file mode 100644 index 00000000..0e79fe06 --- /dev/null +++ b/app/examples/Games/Concent/.src/fotos.form @@ -0,0 +1,210 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(54.3333,9.6667,56.6667,57) + Text = ("") + Resizable = False + { Button1 Button + MoveScaled(1,22.8333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an1.gif"] + } + { Button2 Button + MoveScaled(8,22.8333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an11.gif"] + } + { Button3 Button + MoveScaled(14.8333,22.8333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an12.gif"] + } + { Button4 Button + MoveScaled(21.6667,22.8333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an13.gif"] + } + { Button5 Button + MoveScaled(28.5,22.8333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an14.gif"] + } + { Button6 Button + MoveScaled(35.3333,22.8333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an15.gif"] + } + { Button7 Button + MoveScaled(42.1667,22.8333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an16.gif"] + } + { Button8 Button + MoveScaled(49,22.8333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an17.gif"] + } + { Button9 Button + MoveScaled(28.5,29.6667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an21.gif"] + } + { Button10 Button + MoveScaled(21.6667,29.6667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an20.gif"] + } + { Button11 Button + MoveScaled(14.8333,29.6667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an2.gif"] + } + { Button12 Button + MoveScaled(8,29.6667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an19.gif"] + } + { Button13 Button + MoveScaled(1,29.6667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an18.gif"] + } + { Button14 Button + MoveScaled(35.3333,29.6667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an22.gif"] + } + { Button15 Button + MoveScaled(42.1667,29.6667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an23.gif"] + } + { Button16 Button + MoveScaled(49,29.6667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an24.gif"] + } + { Button17 Button + MoveScaled(28.5,36.5,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an29.gif"] + } + { Button18 Button + MoveScaled(21.6667,36.5,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an28.gif"] + } + { Button19 Button + MoveScaled(14.8333,36.5,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an27.gif"] + } + { Button20 Button + MoveScaled(8,36.5,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an26.gif"] + } + { Button21 Button + MoveScaled(1,36.5,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an25.gif"] + } + { Button22 Button + MoveScaled(35.3333,36.5,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an3.gif"] + } + { Button23 Button + MoveScaled(42.1667,36.5,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an30.gif"] + } + { Button24 Button + MoveScaled(49,36.5,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an31.gif"] + } + { Button25 Button + MoveScaled(28.5,43.3333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an36.gif"] + } + { Button26 Button + MoveScaled(21.6667,43.3333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an35.gif"] + } + { Button27 Button + MoveScaled(14.8333,43.3333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an34.gif"] + } + { Button28 Button + MoveScaled(8,43.3333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an33.gif"] + } + { Button29 Button + MoveScaled(1,43.3333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an32.gif"] + } + { Button30 Button + MoveScaled(35.3333,43.3333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an37.gif"] + } + { Button31 Button + MoveScaled(42.1667,43.3333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an38.gif"] + } + { Button32 Button + MoveScaled(49,43.3333,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an39.gif"] + } + { Button33 Button + MoveScaled(28.5,50.1667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an7.gif"] + } + { Button34 Button + MoveScaled(21.6667,50.1667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an6.gif"] + } + { Button35 Button + MoveScaled(14.8333,50.1667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an5.gif"] + } + { Button36 Button + MoveScaled(8,50.1667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an40.gif"] + } + { Button37 Button + MoveScaled(1,50.1667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an4.gif"] + } + { Button38 Button + MoveScaled(35.3333,50.1667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an8.gif"] + } + { Button39 Button + MoveScaled(42.1667,50.1667,6.3333,6.3333) + Text = ("") + Picture = Picture["imagenes/an9.gif"] + } + { Button40 Button + MoveScaled(49,50.1667,6.3333,6.3333) + Text = ("") + } + { Label1 Label + MoveScaled(1,1,53.6667,20.5) + Text = ("Estimados amigos, este formulario cumple la \xC3\xBAnica funci\xC3\xB3n de\n cargar en 40 botones, las im\xC3\xA1genes de las caritas que servir\xC3\xA1n \npara el juego, es posible que exista una mejor manera de \nhacer esto, por eso... escucho sugerencias, posteriormente \nen el c\xC3\xB3digo se crea un array de objetos a fin de referencialos \ncomo botoncito[i].Picture y copiar este atributo en particular al \ntablero de juego. Att: Jairo Alonso Badillo\n jbadbe@gmail.com") + } +} diff --git a/app/examples/Games/Concent/.src/frmAcerca.class b/app/examples/Games/Concent/.src/frmAcerca.class new file mode 100644 index 00000000..191c36df --- /dev/null +++ b/app/examples/Games/Concent/.src/frmAcerca.class @@ -0,0 +1,12 @@ +' Gambas class file + +PUBLIC SUB Button1_Click() + + ME.Close + +END + +PUBLIC SUB Form_Open() + + +END diff --git a/app/examples/Games/Concent/.src/frmAcerca.form b/app/examples/Games/Concent/.src/frmAcerca.form new file mode 100644 index 00000000..bb87ee31 --- /dev/null +++ b/app/examples/Games/Concent/.src/frmAcerca.form @@ -0,0 +1,20 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(59.5714,26.7143,67,31) + Resizable = False + { Label1 Label + MoveScaled(1,1,65,24) + Text = ("Concéntrese Geográfico v 0.3\n\nProgramado para Gambas por:\nJairo Alonso Badillo Bedoya - jbadbe@gmail.com\nPublicado bajo los términos de la licencia GPL\nViva Colombia!!!!!\nVisite http://www.linuxeam.tk -Una comunidad Linux") + Alignment = Align.Center + } + { Button1 Button + MoveScaled(24,26,19,4) + Text = ("&Aceptar") + } + { PictureBox1 PictureBox + MoveScaled(8,4,6.7143,4) + Expand = True + Picture = Picture["imagenes/colombia.gif"] + } +} diff --git a/app/examples/Games/Concent/.src/frmInstrucciones.class b/app/examples/Games/Concent/.src/frmInstrucciones.class new file mode 100644 index 00000000..e0dfd43d --- /dev/null +++ b/app/examples/Games/Concent/.src/frmInstrucciones.class @@ -0,0 +1,11 @@ +' Gambas class file + +Public Sub Form_Open() + +End + +Public Sub Button1_Click() + + Me.Close + +End diff --git a/app/examples/Games/Concent/.src/frmInstrucciones.form b/app/examples/Games/Concent/.src/frmInstrucciones.form new file mode 100644 index 00000000..c24e0c50 --- /dev/null +++ b/app/examples/Games/Concent/.src/frmInstrucciones.form @@ -0,0 +1,22 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(63.8571,1.7143,86,77) + Resizable = False + { Label1 Label + MoveScaled(1,1,83,4) + Font = Font["Bold,+2"] + Text = ("COMO JUGAR CONCÉNTRESE?") + } + { Label2 Label + MoveScaled(1,6,84,65) + Padding = 8 + Text = ("Se trata de un Juego Bastante sencillo: \n\n1.) El objetivo del juego es probar tu retentiva y nivel de concentración, para lo cual\n debes encontrar los pares de rostros que hay ocultos en el tablero de\n concéntrese. \n \n2.) El juego tiene tres niveles (Principiante, Medio, Experto), que te permite\n escoger entre tres tamaños del tablero para añadir emoción y dificultad al juego,\n para Acceder a estas opciones debes ingresar por el menu Configuración - Nivel,\n y seleccionar uno de los tres preestablecidos. \n \n3). Así mismo, tienes la posibilidad de incrementar tus conocimientos al jugar en\n Modo Pregunta, lo que incrementa tu nivel educativo, esto se logra mediante un\n cuestionario emergente que deberás responder con cada acierto en el tablero, si\n contestas correctamente tendrás un puntaje más alto y podrás competir con tus\n amigos. (Esta función se encuentra en construcción debido a la migracion de\n Visual Basic hacia Gambas, y experimentar el poder de Linux, viva el\n OpenSource!!! \n \n4.) El juego también te permite activar o desactivar los sonidos de ambientación,\n para lo cual debes cambiar el estado del menú Configuración - Sonido. \n \n5.) Recuerda: Para redistribuir el Tablero Pulsa F2 , para comenzar a jugar pulsa\n F4, los rostros serán cargados aleatoriamente, por lo que no debes intentar\n aprenderte el tablero de memoria...!, Esto sí que va a ser un verdadero reto!. \n \n6.) Estoy actualmente diseñando los modulos de aprendizaje que podrás conseguir\n muy pronto; para comentarios y / o sugerencias escribe a: jbadbe@gmail.com.\n") + Alignment = Align.TopNormal + Border = Border.Plain + } + { Button1 Button + MoveScaled(34,72,18,4) + Text = ("&Aceptar") + } +} diff --git a/app/examples/Games/Concent/.src/funciones.module b/app/examples/Games/Concent/.src/funciones.module new file mode 100644 index 00000000..6cbc1714 --- /dev/null +++ b/app/examples/Games/Concent/.src/funciones.module @@ -0,0 +1,162 @@ +' Gambas module file + +'Arreglos para guardar info de las imagenes a cargar y el estado de los botones +'................................................................................ +'Arrays for save images information to load. +Private botones_numero_foticos As New Integer[64] 'Almacena el numero de foto (indice) que llevara cada botoncito al descubrirse + 'Almacenate the image number (index). +Private boton_lleno As New Boolean[64] 'para especificar si un boton ya recibio asignacion de fotico + 'Specify if a button have a image asigned + +'Variables de juego +Public jugadas As Integer 'contador del numero de jugadas durante una partida (counter for number of movements) +Public tiempo As Integer 'contador del numero de segundos durante una partida (counter for game time) +Public aciertos As Integer 'Contar el numero de aciertos del jugador, es decir, cada vez que el jugador logra en una misma + 'jugada encontrar dos botones con el mismo dibujito + '.............................................................................................. + 'Count the acerts for player. +Public num_movimiento_actual As Integer 'Siempre tendra valor de 0, o 1. se usa para saber si el jugador ya destapo un boton el turno actual + 'es decir, una jugada consta de dos movimientos + '.................................................................................. + 'Controle the set of button moved in a movement + +Public Sub distrib(tam_tablero As Integer, tam_botones As Integer) + Dim i As Integer + Dim j As Integer + Dim izq As Integer + Dim arr As Integer + Dim pos As Integer + + izq = (principal.Width - tam_botones * tam_tablero) / 2 + arr = (principal.Height - tam_botones * tam_tablero) / 2 + + pos = 0 + + For I = 0 To tam_tablero - 1 + For J = 0 To tam_tablero - 1 + principal.botones[pos].visible = True + principal.botones[pos].width = tam_botones + principal.botones[pos].height = tam_botones + principal.botones[pos].x = izq + principal.botones[pos].y = arr + principal.botones[pos].picture = principal.initialPicture 'cada vez que se distribuye el tablero, se tapas los botone + 'con el dibujito del signo de interrogacion + '........................................................ + 'Restore the buttons to initial state, when the game is + 'restarted + 'principal.botones[pos].enabled=TRUE + izq = izq + tam_botones + pos = pos + 1 + Next + arr = arr + tam_botones + izq = (principal.Width - tam_botones * tam_tablero) / 2 + + Next + + 'Se ocultan los botones que no sean necesarios de acuerdo al nivel + 'de juego, esto pasa cuando se ha jugado en un nivel superior y + 'luego se vuelve hacia un nivel inferior + '.................................................................. + 'The not necesary button are oculted + For I = tam_tablero * tam_tablero To 63 + principal.botones[pos].visible = False + pos = pos + 1 + Next + + 'se asigna false al array boton_lleno para iniciar una nueva asignacion de foticos + '.................................................................................. + 'The state for each button is restored, it is for permit movement with each button + For I = 0 To 63 + boton_lleno[I] = False + Next + + 'Se cargan las imagenes en cada botoncito + '......................................... + 'Load image for each button + repartir(tam_tablero * tam_tablero) + +End + + +'Este procedimiento busca en el directorio imagenes para capturar los +'nombres de los dibujitos que se van a mostrar en los botones, esta +'funcionalidad permite que el usuario incluya luego sus propias imagenes +'PUBLIC SUB cargar_imagenes(Directory AS String) + + 'DIM File AS String + 'DIM indice AS Integer + 'indice=0 + + 'FOR EACH File IN Dir(Directory, "an*.gif") + 'imagenes[indice]=File + 'indice=indice+1 + 'NEXT + +'END + +Public Sub repartir(tam_tablero As Integer) + Dim i As Integer + Dim j As Integer + Dim aleatorio As Integer + Dim aleatorio1 As Integer + Dim listo As Boolean 'Bandera para informar cuando se encuentre un boton libre para asignacion + 'Variable for controlate the flow in nex loop + + 'Ciclar la mitad de la dimension para ubicar las imagenes en cada boton, se cicla la mitad ya que cada imagen + 'escogida en un ciclo se asigna a una pareja de botoncitos, la imagen escogida se representa como un numero + 'entero, que es utilizado luego como indice del array botones_foticos[] para copiar la propiedad picture, este + 'numero se guarda en el arreglo botones_numero_foticos[64]. + '.............................................................................................................. + 'This For loop permit locate the images in each button + For i = 0 To Int(tam_tablero - 1) / 2 + aleatorio = Int(Rnd() * 40) 'se obtiene un numero entre (0, 39) que es el rango de indices del array botones_foticos + 'Obtain a random number in 0-39 range (there is 40 images for use) + 'IF aleatorio = 40 THEN message ("salio un numero malo") + 'en cada ciclo se ubica dos veces la misma fotico en dos cuadros aleatorios + '.......................................................................... + 'For each loop a image is locate at two times + For j = 0 To 1 + listo = False + While listo = False 'se anida este ciclo para forzar la busqueda de dos cuadritos que no hayan sido asignados + 'This loop is nested for force the search + aleatorio1 = Int(Rnd() * tam_tablero) 'se busca un indice en el rango (0, tam_tablero) + 'An index in 0,tam_table range is searched + If (boton_lleno[aleatorio1]) = False Then 'se verifica que el boton no tenga fotico asignada + 'Verify the button for stablish it is free + botones_numero_foticos[aleatorio1] = aleatorio + boton_lleno[aleatorio1] = True + listo = True + 'ACTIVAR LA LINEA SIGUIENTE PARA DEPURACION, ENTONCES SE PODRA VER LA IMAGEN OCULTA DE LOS BOTONES + 'principal.botones[aleatorio1].picture=principal.botones_foticos[aleatorio].picture + End If + Wend + + Next + + Next + + 'Se inicializan las variables de juego + '.................................... + 'Initialize game variables + jugadas = 0 + tiempo = 0 + aciertos = 0 + num_movimiento_actual = 0 + +End + +'Se hace necesario este metodo para notificar al formulario principal sobre cual imagen debe colocar en un botoncito +'al ser descubierto, esto debido a que el arreglo botones_numero_foticos[] es pridado en este modulo +Public Function devolver_indice_defotico(tag As Integer) As Integer + Return botones_numero_foticos[tag] +End + +'Devuelve un valor booleano indicando si los dos movimientos de una jugada son validos +Public Function comparar_botones_jugados(btjugado1 As Integer, btjugado2 As Integer) As Boolean + Return botones_numero_foticos[btjugado1] = botones_numero_foticos[btjugado2] +End + + + + + diff --git a/app/examples/Games/Concent/.src/principal.class b/app/examples/Games/Concent/.src/principal.class new file mode 100644 index 00000000..964c0d85 --- /dev/null +++ b/app/examples/Games/Concent/.src/principal.class @@ -0,0 +1,390 @@ +' Gambas class file + +' Gambas class +Public boton As Button 'object variable for charge buttons on ejecution time +Public sonido As Sound 'objeto que permite lanzar los sonidos(Play the Sounds) +Public aplauso As Sound 'Sonido de aplauso cuando se acierta en un par de caritas (Applause Sound at each movement) +Public botones As Object[] 'Arreglo de botones del tablero de juego (Buttons array for game board) +Public botones_foticos As Object[] 'Arreglo de botones con las figuritas (Buttons array with images loaded) + 'en el form fotos +'variables del juego +Public tam_tablero As Integer 'para conocer en todo momento cual es el tamaño del tablero (filas y columnas) + 'It is for know in all moment the boad size(row and columns) + +Public habilitar As Boolean 'para establecer si el jugador puede empezar a descubrir los botoncitos + 'Stablish if player can move + + +Private anterior_boton_jugado As Integer 'se utiliza para recordar el numero del ultimo boton pulsado, a fin de poder + 'restaurarlo, en el caso del primer movimiento de una jugada, cuando no hay coincidencia + 'de las imagenes destapadas (remember last pushed button) + +'Las siguientes dos variables fueron necesarias a fin de preservar la informacion contenida en la variable anterior_boton_jugado +'y en LAST.tag durante la ejecucion del evento click sobre los botones, esto debido a que la ejecucion de codigo no se +'interrumpe con la activacion del timer, de esta forma se permite un retardo en la visualizacion de pares de botones descubiertos +'para que el jugador pueda reconocer la figura oculta en caso de que ambos dibujos no coincidan +'............................................................................................... +'Next variables would be needed for save information in variable anterior_boton_jugado and LAST.tag (for grupo_botones group) +'for click event, it is for permit a delay in each movement, so the player can visualize every faces. + +Private boton_actual_paratimer As Integer +Private boton_ultimo_paratimer As Integer + +'FUNCIONALIDAD AÑADIDA DE ACCESO A BASE DE DATOS PARA ALMACENAR LOS DATOS DEL +'JUEGO, MEJORES PUNTAJES POR CATEGORIA +'PRIVATE myarchivo AS File +'PRIVATE mylinea AS String + +'Variable para almacenar el idioma en uso +'........................................ +'Capture and almacenate the language in use +Public language As String + +Public initialPicture As Picture + +Public Sub _new() + +Dim i As Integer + +Randomize + +sonido = New Sound("Wallhit.wav") 'Instancia del objeto sonido - Sound object instance +aplauso = New Sound("applause.wav") + +initialPicture = Picture["imagenes/inter.jpg"] + + 'IF NOT Access(system.Home &/"myarchivo.ini") THEN + ' message ("no existe") + ' OPEN system.Home &/"myarchivo.ini" FOR CREATE AS myarchivo + ' 'WHILE NOT Eof(myarchivo) + ' 'LINE INPUT #myarchivo, mylinea + ' 'message (mylinea) + ' 'WEND + ' myarchivo.Save("hola") + ' + ' CLOSE myarchivo + 'END IF + + + 'Se crea el array de objetos botones, esto con el fin de facilitar la manipulacion + 'de eventos comunes a un grupo de botones. + '.................................................................................. + 'Create the buttons to be used for next game + botones = New Object[] 'Array of buttons + + For i = 1 To 64 + boton = New Button(Me) As "grupobotones" + boton.tag = i - 1 + 'The button object array is created, it is for manipulate the common events to group buttons + botones.Add(boton) ' + Next + + 'Se crea este arreglo para tomar referencia de las foticos de los botones + 'en el form fotos + '.......................................................................... + 'This array contain a reference for images to visualice in the game + botones_foticos = New Object[] + For i = 1 To 40 + boton = New Button(Me) + boton.Visible = False + boton.Picture = Picture["imagenes/an" & CStr(i) & ".gif"] + botones_foticos.Add(boton) + Next + ' botones_foticos.Add(fotos.Button1) + ' botones_foticos.Add(fotos.Button2) + ' botones_foticos.Add(fotos.Button3) + ' botones_foticos.Add(fotos.Button4) + ' botones_foticos.Add(fotos.Button5) + ' botones_foticos.Add(fotos.Button6) + ' botones_foticos.Add(fotos.Button7) + ' botones_foticos.Add(fotos.Button8) + ' botones_foticos.Add(fotos.Button9) + ' botones_foticos.Add(fotos.Button10) + ' botones_foticos.Add(fotos.Button11) + ' botones_foticos.Add(fotos.Button12) + ' botones_foticos.Add(fotos.Button13) + ' botones_foticos.Add(fotos.Button14) + ' botones_foticos.Add(fotos.Button15) + ' botones_foticos.Add(fotos.Button16) + ' botones_foticos.Add(fotos.Button17) + ' botones_foticos.Add(fotos.Button18) + ' botones_foticos.Add(fotos.Button19) + ' botones_foticos.Add(fotos.Button20) + ' botones_foticos.Add(fotos.Button21) + ' botones_foticos.Add(fotos.Button22) + ' botones_foticos.Add(fotos.Button23) + ' botones_foticos.Add(fotos.Button24) + ' botones_foticos.Add(fotos.Button25) + ' botones_foticos.Add(fotos.Button26) + ' botones_foticos.Add(fotos.Button27) + ' botones_foticos.Add(fotos.Button28) + ' botones_foticos.Add(fotos.Button29) + ' botones_foticos.Add(fotos.Button30) + ' botones_foticos.Add(fotos.Button31) + ' botones_foticos.Add(fotos.Button32) + ' botones_foticos.Add(fotos.Button33) + ' botones_foticos.Add(fotos.Button34) + ' botones_foticos.Add(fotos.Button35) + ' botones_foticos.Add(fotos.Button36) + ' botones_foticos.Add(fotos.Button37) + ' botones_foticos.Add(fotos.Button38) + ' botones_foticos.Add(fotos.Button39) + ' botones_foticos.Add(fotos.Button40) +'Pensaba permitir al usuario normal el cambio de las imagenes de los +'botoncitos pero, parece mejor que el usuario que quiera modificar estos +'dibujos, lo haga en el proyecto fuente +'....................................................................... +'Maybe is possible permit to user manipulate the images, but for now it is +'done only by code +tam_tablero = 4 +funciones.distrib(tam_tablero, 45) + +End + +Public Sub grupobotones_Click() + + Dim jugada_valida As Boolean + Dim continuar As Boolean + + 'message(LAST.tag) + + continuar = True + 'Se verifica si el tablero acepta jugadas, es decir si el juego esta en curso + '............................................................................ + 'Verify if board is active for game + If habilitar = True Then + 'Antes que nada se comprueba que el boton pulsado no se haya jugado antes + 'utilizo el atributo btnInicial.picture porque resulta sencillo comparar de esta forma si el cuadrito ya no tiene + 'el dibujo del signo de interrogacion + '............................................................................ + 'Comprobe is current pushed button haven't been discovered. + If (Last.picture <> initialPicture) Then + 'Ojo, si el boton pulsado ya habia sido descubierto durante el primer movimiento de esta jugada + '............................................................................................... + 'Inhabilite the movement for a button when it have been discovered. + continuar = False + message(("Jugada Errada")) + End If + + If continuar Then + sonar(1) + + Last.picture = botones_foticos[funciones.devolver_indice_defotico(Last.tag)].picture + If funciones.num_movimiento_actual = 1 Then 'Se esta haciendo el segundo movimiento de una jugada + + 'Se debe comparar la imagen, para saber si hubo coincidencia + '............................................................ + 'Compare the imagen for stablish if movement is correct + jugada_valida = funciones.comparar_botones_jugados(anterior_boton_jugado, Last.tag) + + 'Se aumenta el contador de jugadas (cada jugada corresponde a 2 movimientos), se actualiza el label + '.................................................................................................. + 'Increase the play counter (each turne have two movements), label is refresh + funciones.jugadas = funciones.jugadas + 1 + lblJugadas.Caption = funciones.jugadas + + If jugada_valida Then + sonar(2) + 'depuracion + 'ME.botones[LAST.tag].enabled=FALSE + 'ME.botones[anterior_boton_jugado].enabled=FALSE + + funciones.aciertos = funciones.aciertos + 1 'El jugador tuvo un acierto mas (player have acerted) + 'se verifica si el numero de aciertos indica el fin del juego, en todo caso el numero de aciertos para + 'finalizar el juego debe ser igual a pow(tam_tablero,2)/2, por ejemplo si el jugador escogio el nivel + 'experto, el tamaño del tablero sera de 8*8, entonces los aciertos necesarios para finalizar el juego son: + '64/2=32 + '................................................................................................... + 'Verify numbers of acerts, for stablish the game end, for end the game acerts maybe be equal to + 'pow(tam_tablero,2)/2 + If funciones.aciertos = tam_tablero * tam_tablero / 2 Then + timer2.Enabled = False 'se termina el conteo de tiempo + message.Info(("Felicitaciones, has cumplido la mision")) + End If + Else + habilitar = False + boton_actual_paratimer = Last.tag + boton_ultimo_paratimer = anterior_boton_jugado + timer1.enabled = True + End If + End If + funciones.num_movimiento_actual = funciones.num_movimiento_actual + 1 'Se ha hecho un movimiento + 'message(ultimo_boton_jugado) + anterior_boton_jugado = Last.tag + If funciones.num_movimiento_actual = 2 Then + funciones.num_movimiento_actual = 0 'Si se hizo el segundo movimiento, se reinicia el turno + End If + End If + End If + +End + + +Public Sub mnuConfiguracionSalir_Click() + Me.Close +End + + +Public Sub mnuConfiguracionNuevoJuego_Click() + Dim tam_botones As Integer 'para seleccionar el tamaño adecuado del boton de acuerdo al nivel de juego escogido + 'Choose the board size + + If tam_tablero = 8 Then + tam_botones = 40 + Else + tam_botones = 45 + End If + funciones.distrib(tam_tablero, tam_botones) + +End + + +Public Sub grupobotones_KeyPress() + + If key.code = key.f4 Then + habilitar = True + timer2.Enabled = True + label5.caption = ("PULSE F2 PARA REPARTIR DE NUEVO") + End If + + + +End + +Public Sub Timer1_Timer() + + + timer1.Enabled = False + botones[boton_ultimo_paratimer].picture = initialPicture + botones[boton_actual_paratimer].picture = initialPicture + 'ME.Label5.Caption="anterior: " & anterior_boton_jugado & " ultimo: " & ultimo_boton_jugado + 'message("hola") + habilitar = True + +End + +'Habilita o deshabilita el menu sonido, esto permite posteriormente en el procedimiento sonar, escoger si se emiten o no +'los sonidos +'................................................................................................ +'Activate or desactivate the sound in menu Configuration. +Public Sub mnuConfiguracionSonido_Click() + mnuConfiguracionSonido.Checked = Not mnuConfiguracionSonido.Checked +End + +'Para emitir los sonidos, si el menu Sonido no esta checkeado, se omite el sonidito +'.................................................................................. +'Play sound only if Sound configuration menu is checked +Public Sub sonar(evento As Integer) + + If mnuConfiguracionSonido.Checked Then + If evento = 1 Then + sonido.Play() + End If + + If evento = 2 Then + aplauso.Play() + End If + End If + +End + + +Public Sub Timer2_Timer() + 'Este timer sirve para llevar la cuenta del tiempo de juego + '.......................................................... + 'This timer count the game time + funciones.tiempo = funciones.tiempo + 1 + lblTiempo.Caption = funciones.tiempo +End + +Public Sub nivel_Click() + + Last.checked = Not Last.checked + If Last.tag = "p" Then + 'message ("Has escogido el nivel Principiante") + mnuConfigNivelExp.Checked = False + mnuConfigNivelMedio.Checked = False + tam_tablero = 4 + funciones.distrib(tam_tablero, 45) + End If + + If Last.tag = "m" Then + mnuConfigNivelExp.Checked = False + mnuConfigNivelPrinci.Checked = False + 'message ("Has escogido el nivel Medio") + tam_tablero = 6 + funciones.distrib(tam_tablero, 45) + End If + + If Last.tag = "e" Then + mnuConfigNivelPrinci.Checked = False + mnuConfigNivelMedio.Checked = False + 'message ("Has escogido el nivel Experto, suerte jugadorazo!") + tam_tablero = 8 + funciones.distrib(tam_tablero, 40) + End If + + + +End + +Public Sub mnuAyudaAcerca_Click() + frmAcerca.ShowModal + +End + +Public Sub mnuAyudaInstr_Click() + + frmInstrucciones.ShowModal + +End + + + +' PUBLIC SUB mnuConfigIdiomaEspanol_Click() +' 'Cambiar el idioma +' '.................. +' 'Set the language +' language = "espanol" +' mnuConfigIdiomaEnglish.Checked = FALSE +' mnuConfigIdiomaEspanol.Checked = NOT mnuConfigIdiomaEspanol.Checked +' 'File.Save("language.txt","espanol") +' +' set_enviroment_language +' +' END +' +' PUBLIC SUB mnuConfigIdiomaEnglish_Click() +' 'Cambiar el idioma +' '.................. +' 'Set the language +' language = "english" +' mnuConfigIdiomaEspanol.Checked = FALSE +' mnuConfigIdiomaEnglish.Checked = NOT mnuConfigIdiomaEnglish.Checked +' 'File.Save("language.txt","espanol") +' set_enviroment_language +' +' END + +Public Sub Initialize() + + 'El timer del contador de segundos ha de estar en ceros + '...................................................... + 'Restore the time game timer + Timer2.Enabled = False + + 'Se inicializan los labels de conteo del juego + '............................................. + 'Initialize labels + lblJugadas.Caption = 0 + lblTiempo.Caption = 0 + 'Se actualiza el mensaje en la barra de estado + '.............................................. + 'Change message in status bar + Label5.caption = ("PULSE F4 PARA COMENZAR LA MISION") + + habilitar = False 'El jugador no puede descubrir las fichas hasta que pulse F4 + '......................................................... + 'Enable game only when player press F4 + +End diff --git a/app/examples/Games/Concent/.src/principal.form b/app/examples/Games/Concent/.src/principal.form new file mode 100644 index 00000000..5156d342 --- /dev/null +++ b/app/examples/Games/Concent/.src/principal.form @@ -0,0 +1,220 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(58.7143,5,49,70) + Mouse = Mouse.Pointing + Text = ("Enjoy Open Source!") + Picture = Picture["imagenes/tierra3.jpg"] + Resizable = False + { mnuConfiguracion Menu + Text = ("&Configuración") + { mnuConfiguracionNuevoJuego Menu + Text = ("&Nuevo Juego") + Shortcut = "F2" + } + { mnuConfiguracionMejorPuntaje Menu + Text = ("&Mejores Puntajes") + Enabled = False + Shortcut = "F3" + } + { mnuConfiguracionNivel Menu + Text = ("Nivel") + { mnuConfigNivelPrinci Menu nivel + Name = "mnuConfigNivelPrinci" + Text = ("&Principiante") + Checked = True + Tag = "p" + Shortcut = "Ctrl+Alt+P" + } + { mnuConfigNivelMedio Menu nivel + Name = "mnuConfigNivelMedio" + Text = ("M&edio") + Tag = "m" + Shortcut = "Ctrl+Alt+M" + } + { mnuConfigNivelExp Menu nivel + Name = "mnuConfigNivelExp" + Text = ("E&xperto") + Tag = "e" + Shortcut = "Ctrl+Alt+X" + } + } + { mnuConfiguracionSonido Menu + Text = ("&Sonido") + Checked = True + Shortcut = "Ctrl+Alt+R" + } + { mnuSep Menu + Tag = "-" + } + { mnuConfiguracionSalir Menu + Text = ("&Salir") + } + } + { mnuAyuda Menu + Text = ("&Ayuda") + { mnuAyudaAcerca Menu + Text = ("A&cerca de") + } + { mnuAyudaInstr Menu + Text = ("&Instrucciones") + } + } + { Menu1 Menu + } + { Button49 Button grupobotones + Name = "Button49" + MoveScaled(2,67.1429,6.1429,6.1429) + Visible = False + Tag = "48" + Picture = Picture["imagenes/inter.jpg"] + } + { Button50 Button grupobotones + Name = "Button50" + MoveScaled(9,67.1429,6.1429,6.1429) + Visible = False + Tag = "49" + Picture = Picture["imagenes/inter.jpg"] + } + { Button51 Button grupobotones + Name = "Button51" + MoveScaled(16,67.1429,6.1429,6.1429) + Visible = False + Tag = "50" + Picture = Picture["imagenes/inter.jpg"] + } + { Button52 Button grupobotones + Name = "Button52" + MoveScaled(22.7143,67.1429,6.1429,6.1429) + Visible = False + Tag = "51" + Picture = Picture["imagenes/inter.jpg"] + } + { Button53 Button grupobotones + Name = "Button53" + MoveScaled(29.7143,67.1429,6.1429,6.1429) + Visible = False + Tag = "52" + Picture = Picture["imagenes/inter.jpg"] + } + { Button54 Button grupobotones + Name = "Button54" + MoveScaled(36.1429,67.1429,6.1429,6.1429) + Visible = False + Tag = "53" + Picture = Picture["imagenes/inter.jpg"] + } + { Button55 Button grupobotones + Name = "Button55" + MoveScaled(2,74,6.1429,6.1429) + Visible = False + Tag = "54" + Picture = Picture["imagenes/inter.jpg"] + } + { Button56 Button grupobotones + Name = "Button56" + MoveScaled(9,74,6.1429,6.1429) + Visible = False + Tag = "55" + Picture = Picture["imagenes/inter.jpg"] + } + { Button57 Button grupobotones + Name = "Button57" + MoveScaled(16,74,6.1429,6.1429) + Visible = False + Tag = "56" + Picture = Picture["imagenes/inter.jpg"] + } + { Button58 Button grupobotones + Name = "Button58" + MoveScaled(22.7143,74,6.1429,6.1429) + Visible = False + Tag = "57" + Picture = Picture["imagenes/inter.jpg"] + } + { Button59 Button grupobotones + Name = "Button59" + MoveScaled(29.7143,74,6.1429,6.1429) + Visible = False + Tag = "58" + Picture = Picture["imagenes/inter.jpg"] + } + { Button60 Button grupobotones + Name = "Button60" + MoveScaled(36.1429,74,6.1429,6.1429) + Visible = False + Tag = "59" + Picture = Picture["imagenes/inter.jpg"] + } + { Button61 Button grupobotones + Name = "Button61" + MoveScaled(2,81,6.1429,6.1429) + Visible = False + Tag = "60" + Picture = Picture["imagenes/inter.jpg"] + } + { Button62 Button grupobotones + Name = "Button62" + MoveScaled(9,81,6.1429,6.1429) + Visible = False + Tag = "61" + Picture = Picture["imagenes/inter.jpg"] + } + { Button63 Button grupobotones + Name = "Button63" + MoveScaled(16,81,6.1429,6.1429) + Visible = False + Tag = "62" + Picture = Picture["imagenes/inter.jpg"] + } + { Button64 Button grupobotones + Name = "Button64" + MoveScaled(22.7143,81,6.1429,6.1429) + Visible = False + Tag = "63" + Picture = Picture["imagenes/inter.jpg"] + } + { Label1 Label + MoveScaled(1,1,12.1429,3.1429) + Background = &H000000& + Foreground = &HFFFFFF& + Text = ("Jugadas: ") + Alignment = Align.Left + } + { Label2 Label + MoveScaled(1,4.1429,12.1429,3.1429) + Background = &H000000& + Foreground = &HFFFFFF& + Text = ("Tiempo: ") + } + { Label5 Label + MoveScaled(0,61,49,5) + Font = Font["10"] + Background = &H000000& + Foreground = &HFFFFFF& + Padding = 8 + Text = ("PULSE F4 PARA COMENZAR LA MISION") + Alignment = Align.Left + } + { Timer1 #Timer + #MoveScaled(9,19) + Delay = 500 + } + { Timer2 #Timer + #MoveScaled(24,18) + } + { lblTiempo Label + MoveScaled(12.1429,4.1429,4.1429,3.1429) + Background = &H000000& + Foreground = &HFFFFFF& + Text = ("0") + Alignment = Align.Right + } + { lblJugadas Label + MoveScaled(12.1429,1,4.1429,3.1429) + Background = &H000000& + Foreground = &HFFFFFF& + Text = ("0") + Alignment = Align.Right + } +} diff --git a/app/examples/Games/Concent/Blockhit.wav b/app/examples/Games/Concent/Blockhit.wav new file mode 100644 index 0000000000000000000000000000000000000000..e4b4834a8ffae709a298b1c951a845f2e1a9bfca GIT binary patch literal 1148 zcmW-hNsDDQ9K~OQ3l}c*8`Q#`(5O3?qFqoT3U;IG(xS2Lx*03Oox)A-kQ*}I_lmOn z+nO>V$$xS9<#00mc5{9G@X6`)$M?U#dfPl)eSSKfUex}@SEv8Si_@p4x3_;iOHQBF z>F)O7_IcG9J$+1v@#+X{_!y$S=drH zLrv&r6XCua&|;1`20=+!xl(DAKtQwxi+f`X>}_x{N!a>9N$FQY5d=Cbyr{)Mtq&a) z3|z!93Ge;=y$)mx_v3;r>M`j{vZ#>OySG|{Cy|yI=B_BWxq0>X4U}=FiCrXh zS@0tJloDQhw`f|HWRGKr1ID-vlyc7lqapKjmijP^isQ(B-Pb@DJEJ~AuVRD%LXSP| zgr!Hp!309*lA{bFCPo4QR>LVo42Ks;yGp&8|zupeT8CX#?%y-A@a-)s<~ z#3oF0SG^`)T0$ASoJA=a?9UKA)j%g)e zNL8+L#tBs!i-Pg9H;2rm1Ty8yN7SnH%tqdc#zEltglt9-w>j0Y5T 0.0-3 +- Correccion de bugs, se añade temporizador + +* Wed Jun 29 2005 Jairo Alonso Badillo Bedoya 0.0-3 +-Versión Inicial + diff --git a/app/examples/Games/Concent/Missed.wav b/app/examples/Games/Concent/Missed.wav new file mode 100644 index 0000000000000000000000000000000000000000..9235541ecdd572be9434e15367a828e66f7f3c7c GIT binary patch literal 15612 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 b|MKg?&cFQKUq=7QZ~lUjKiT*H_XqzMIE7Em literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/Newlevel.wav b/app/examples/Games/Concent/Newlevel.wav new file mode 100644 index 0000000000000000000000000000000000000000..7db791b441d037648f96cdb810c4a480846f0b78 GIT binary patch literal 19842 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/Paddle.wav b/app/examples/Games/Concent/Paddle.wav new file mode 100644 index 0000000000000000000000000000000000000000..38ade2780d4d1626c6a8ac84f92ff1066752393e GIT binary patch literal 1754 zcmeH{OK;;;6vsUh8nJ|Lz@8;5SRk=r1Y%}`Ak(3h+GNtCNz=ry_}ab?-|K5Tb{yN8 zH1(tpI&?ZP0#Oz)AhAXQ)-2fb8CbAk!Lc(uBqa7AY}xnkbC0j=bN;{k_M10u{BT`S zZok%ey>no`@SLJ3*Wi5qMWw#3DbFgM=3Vm{h+jZnyJgNGO3pcDJirXo zU`hq{JsA*Y+k8I65zk}B5E@(d5acif!y}h;BXl&fM-Rx@eUZ{0v{;oh zs`d5_k(Fg3t%H4yCW~beI_l6M@%*p|v9208%;s50Ox?u6G@Ay*GAtsKG?v5~BjQiu zNDyS%#E&MSppNZO5k!HYuH#Z611T8Bgb67nX9N`Yr4M~0)Z@MnicwGk6laWq5(0V) zs*;?q6?C|2cvZyTRW0k-I{Ft`C;fl>Z&sl42H3>+J<3ENJ+Ks5fq}z>=Ya=(>bleu z;5wH;9*5K@4t;n8I|dQF;Mxcw+aY2y%kwOqCX)dC=%TSXwjk~}7&{0#goo34d3Juf zOgu}|Z5~F|+OTMKz{1oWYpQM-x~8g{K6Y4`A3yr+tFNDYSfEzp&K^5FKg)6Vy^W?8 zEKkeC?loHnG+Ui40uy$OHKA1P^>uG{bd(8eIM8rZEbkz9>@Z8i43}9_fb7<19;rYZ{1!=`8WFWg^%V(?k*kw8}V{NLp=wP!#x` zn$|D^ZfjbDu;v#3!7Z+7h3b-uTi@rb=2if#9<8~pX#JDhT2nh7p^LJTQ9I(Wb|lL#D;*L>9F&zTDJxl4 zl!9SHWW^GRtSE}D37 zPH=ViwQKE7Z?A{D_5HJ(=##LIzg-^fR65c1Nt@RP7Z;x!Tkh;~(b}#aF3$(`@@{xO zYq70vwCvXnYq*?x<#Ky|+OKJ%e>(9@V{Z}#qQnnRAlUpkS_Fc!?tJVSv~e^KMA7U_ zAy8|BSs+xsH4Z(4RC?2(MoP_5*fR3l2NMX?re9UFsiZ8+$z(E>N~P23jSYmd*<20- z`9h&URG?U-l$J`HfTn4>t{aA70%hO>Pywn{xByJ$|7IQb|Le)W?f(nt;{C^-{Y5+= z6UMayzKnBjIgVu#qM1ZdsO|d>r-V>YP57-5YuZuh2PTL{PH%B$B1BLym$G9fG{u5iudjL0$~SWz%R3bDgQ&*spO LGiK72{!gdhd3 literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/Wallhit.wav b/app/examples/Games/Concent/Wallhit.wav new file mode 100644 index 0000000000000000000000000000000000000000..858bbc32ed3fac0689ee3194ba35e0ff83bd2234 GIT binary patch literal 768 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an1.gif b/app/examples/Games/Concent/imagenes/an1.gif new file mode 100644 index 0000000000000000000000000000000000000000..0fca6237a252997af37a9d221464b304afed168a GIT binary patch literal 1601 zcmd7P=~EL27zXeSSs=j$vSBkQEHor*6bzU^RKTbqEcfDIMJ*T=BwTIOfYD;hv>_}J zNSuj?q7n#ERH^~NiFi>A7#O6eQPFzD84t8*vBsm0wG@l}EBgEJ%roz&cjie-NsNxk z4F|%3K?0aYqXHoG9|RGBAc8>1XDEbD2SR`#grPVDV$%l{L#8_dicp9^AQ1-PD1`C( zDhTBBF%ciZ`A8;)5`2Wq8EM-f~sl8MBbV!lEomW%mvu}qGORAMnfWLhyyfvZRY zSI7t>L6plfNkyhgrK(qutqNJaN?xzhlO$;@Z#C-6TlIP?skhXVV=Bvt-e|E{tX6A} zwROT`9kW{OcKfK^GSOq5w2n@5I-QfFW78(boD&lhpZ>=}rdaW%`MIimIs4mURS`S0 zD4(5_lAZzZKTq>{B*1e@O{W3?Rl8FQ)#`K%FE2V>zo))7lmOh}Mt67WL8Afms6J9n zQupc_8ewfydwa8~%a6msc;_#0JlghJI$GdB3}c<@>|%}h4O}>n`H=MuOe^d+ct6mu zv*{8%@?_-P1c_f&=WG9OYQ&tbuM*Zn#D;&pZzG2|~&fS$s)kGlk zOt!dNL;GRf{Mmr=;c+uOCzEvDq4cHu18(HLZ?+Xzc(tWNXYDzR2s$7=JO1$TP#sqo z>>Ml682Yy#-W6&D!hcorYQGBHs$?h9c;naa2S<_#)p2Hj9@K6)(ek#0u;%Z_b6tzFpAU#iPP4S=>u?tFtHrL~%AX0sa zk=&UD!V(B3&5EX3E5U7EN_s@ltU9A9Mx~`f+;MFBhpX zrq{-yQv%U1l{4m!Ip#Iqe*Y2ZJhs^tOxdSi;{?p!Hbpx=Cg}I>)ug`cxZM#5gd8l2 zAC4Wk;@3x=ZO@oz{+OtBfKQ+t;m;jYRq%arXyj2;o4`0cBEq>u z_2v%Ry}KP%OBeUkV)WUY;8pNpb=ocUnSDD;spp_kO5Cmb7M z7$w_h_$yo#xZv$~VM0Ze0qCLaS>WyNXIQsz7%x}${bq_<9)7ZTixOC`DZ4Bzqk^_W zw7i1hGII^fVRM_IaA>X-?@^;6M-w7)c}>v_X(5hXkk__8NDAlTc{b*qhj*MqIah6BmQ}piaOqa0ZKq} zFOg0uJE?q>hr;99$mVoKQemz0Z(fOos+nK$<;uJEh-Z5*|FubjV7j5$n+KlncVw|$ zrZDRWy<01-6zwo(OYUTvIu1+Qn!Bh&c_+RXA B*j)es literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an10.gif b/app/examples/Games/Concent/imagenes/an10.gif new file mode 100644 index 0000000000000000000000000000000000000000..e541a123423dd4dbe95ecef4b01f4991f1730fb1 GIT binary patch literal 2209 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 mzp0#)CB0GiNOO#ZpGIi%9fDRqH(1YJ7 zYj3U*(i$)-MYLEi5^HMNN@uE8v+f3>LyHdDY;`TVh@2ffWu2a}+4V5CxW8hr^E|I} zp6B`TeI9E?neioOHpm7yt3U!F3=s5x;J5)na2&;-qej&U;iyJ!K@p7_MKv0=T7xG> z95E!08V#=3*b;ZWUZXc8q)te0Fz79b))X3SiFIKDgQd_=V=>rlHQQ{Knr$@=4NXlg z+ge&$n%WXHcsz;nwE3RX=WFZk?)LTd4R(9_5)QNl0)f84(BR-;C=?2Z0^x9YEF1_& zpA(A?j*X2?k9EglvDsK?HWr?ooSdGXo}UiS&d$!yPR`BE&CgB$=l`4Zg~jIAwmVz4 zHxT=GwloqojoS%p#X39CKab{lNIjnyW7&^{Gk5;^G3GAW04~D@yeR6_hbItLUA) zOnH)!vJ_KSDlBX~-`Iiait@FYnFNM_TUB|M(yFTbi`oLUT1#3Px;)-F>J?>r^|xNF z_s|NXtuf9*7Lh8a92kcJ9U{D9&%fhtGt9$vZ_l}&rc=i=WJ;5Y>~EJywHpd;3ONnV zifn}kTmz51DMX3()GJel6+v?hbSh( z7fg~D?~v8vFSCzDbXie zignGuFMQT1smY85M+B{QK*%*2Zy{K?T z&_3K*-BiWJH;Mq>+Ni)5Y=pZ}JLaGuaBsg(@l-W54Hv5)GJG}5jw}Av63B`oV?&J! zfzBR*`cJuFgpc@bMj31el6)OG0`kaOhCj9MM2Ihk9b8#4gi!$VY$}oF3;%j)6Uw

lR-^*L9GJzu3aV?4*s z6g$ONOGrwv^$nLomh3yo!J68U%`!n~=-y+wFMe5+E_DopUh4h{fP-i5zvKq2U9F^z z<=`?uYeKd2CXa|`+-ni#F#5J&Mk4|0_kR6e{;s^>eprmD%)^R>gU9c|O+54;X7Q2F z3f%Ht$oKwaVXQ`~~S0R{xWFgv~NBxv{KrUPBI)ctyL{|7t&(uk`eEjF+{ZmL# z2;9s1I6lnAkcIu84mE)sSq<@(04(?2s$O#SjQH=D`=16JIq+x%F>w$VAfQ#{B~rxC zL%L}S1M8}B!lUHA+t>2=E?UPi=XdS`E3pXhyBHDX0LRM9lzWU9UuR8QUp+)CO6)vz zz7%?61qa1~z`G8N7Dv7WUQ%eR{&idq&V_r+IbnrNV==>wxgSILYT#Gym!vqahp z!Og3?u$;^}&`GYQ?7v!=;$7277GbbSF!d1bB%HRQ6b@^}%^9wE=SzT-w6Q8F7euh{ zf$X@wD_IY1Ngm}^yiCxmKH!9|Bfop(ag>dVoc#4EC$IvN9x#2|WS)quoT~5rGhsHs zx_6YVCA^HeDEN31lD3fuKUxpcy%5iZT|mY#=&s{W@?9(>nJWk9B0l8~9=%jY?NfON z_}fTN`&#z&8k%#r*cpjF4xjiTDrJUFik^r7E2_K3mLr+HNx(!7V}N893nQK^)w?3u zO%d*KAT!>c+e$W|>;rsaltUPAr;^^;2S{EjwfN{jl;#0Pm?+Piu1IX?|+PT5e!(L^puR?rugTuXik`{`e=!9ab1THmJN2>R|}c-&Xo6xVl^u^9-?c<<;Uc)aps29Z0be^rdkbD%(f9woFv+nb>eh z<|hyVNBvr6fA?^>lmDqnC^Y^6meghp&8+*V^;Df33v^LQD)%prkK;l-T__16vfG>B dv@szM$cAVE>UiJ8HEHeM;-YqjX9D2m{{nb(fu{ff literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an14.gif b/app/examples/Games/Concent/imagenes/an14.gif new file mode 100644 index 0000000000000000000000000000000000000000..8c218ce0c877fdc92c0574884da7efefa5321dc0 GIT binary patch literal 1924 zcmd7P{ZkWn0>|<1?j~UqHn4%<#Sm~I3*jMjLWnIw+HN2`C_#e+20VHVB?{VjrCwUG zlXf-_MX0F+43DP0pc#>aR&1FT$BcwPJ**i@<-yvaSFqHogLl+&N~az@yuadJKfLGj z`=?-Me&&wqG>`_a7J*F+2FAethrbDX8ome+k`8w} zkBzW#-YG)ZCv2BU2sVoLicbk$uFme$9bM9%D8kzkHGE}&JQpRFN(2nHcsTl#&r-g; z+UdJA*53KWD33>`ON7zFl=NZZ%GG{J+NF~t_b-I9g$Q6<5tjG|Jh0m{pj9m<7l^?t zZ%s4{*$kKg1X_XzxL3EV3wbtsqcuA6P({)}TW&K_K3S1`wy~%)HOGW2+SKq~ah1D? z8n|IaB^3_A{kkOpiOc6lde8RHoO|xjB+j@!(Zt}U+_P)hIc2@MsdQ;MMZMRdIePx3 z%Uk}|+`Lhi`pqvt-TvCDOL4r9hsKvOLG7z|^_M#e!ebAe{*s>4->|ScDkjz|%U*kz zDX_Kyef5vrKsNSRQ2bT_m)TorA*QFk$gjDtXq#4)^v_IK^G?Nnlq;pt+&Elz@YTHx zB3KSRk%wbqNLnu;h$wTI>$n+r2?sF|s4cRtBqy zDn!rIe#XPd-SrvY1KJT`xu@0WEgypscVp?+HM#q6U3{>pgQ%74f2B6~JV1pdswH+DLI|DuLrwxUO{)P)Sc^n%8Nw-ou{r%;^Ozrm zd4Kk(xamDN(L@e<14daJlYV4W>~-dxVBTCr!o81%#t%0_=|V<^cUr)hnVSC&Jd>yY zxlZ1+YWWNL7@zxl*1z(FIE6qet{9_=u5@?9w0``bGGTaOJZ1_nZhll5o>&tze2o{> z+5qaLTVPT^zZ@!;`MvYUiw_?uJO?_X_T&z`bLkC|vI-MQF_k|z% z>cOMg=NCSJA$UE$@N1q9d>WKb$E?+*DNbzXm3*gtc!yfmzRut!bEzD#So>?`*^G~K zLyy1xXTgH8r5tfbBs~5D)FQwi01WlCeYK~Lvd^gTF@%@3EEE1f%~Ajwy%umI-n;w!-+@$Zv?|}E3E9S zuCc*qYv^0SJrJh%1H#ktpx&Rbg)9jtbO^CR)nr#J-F@cH3m1bd23JeN&JxGy`I_XJ z@o(|Y*{!ej(nw_L?zS^G5=HvY=Eoc%DTbvm<&al(5oJzY^mFom3P2T8 z+r(Y3NTyK0a_KXXkui?NX%#jM97x;OimGYr)H1=$#bF%Gt#;nhgJi)-xqNMwu7fanplWGD6P>qZ+R;+l8q&3aqe5gKBleUQL!*=Xlful1rV1$27XP-4q1gxNh68D0Jo(t6#fJo8T1^aO+9A1{sIoUBB0ereLnV}k(ZUGGGgkR;_^ m+RJWbKJu_^2eeodFLeIncFMP~`EXG~d$b)0pi2}0+5ZJ}(=-SG literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an15.gif b/app/examples/Games/Concent/imagenes/an15.gif new file mode 100644 index 0000000000000000000000000000000000000000..ea0ecb950e1b7678aeac55f015375022035b5876 GIT binary patch literal 1132 zcmbu)k5f{2008hW4}33%DB_Pe1AT%px1I|PD>m8ypZH^#9LLSvbceroTAF4of1sX- zg?5Bb&&-q(71K&x8K%3KV`5?3!Qd5BI zH0he0oC2LrS5R5mqBFPXtaWvD)=InCY_^*1R;zVjV8G#U%nZ1lPUnon?VNng?RGCL zEON{kok|L9Art5daV?GBva!WpYES>O(HV00SXXP`BOm@1w$639PS;&ebQ~gW09BqAr zmSg7V8;J_sH`vTdi6gb+qYDm5geLU< z-N4;2RtCTXk&K@5X%c4M+c_)o2bGe3NLouC1S|%m_L}yVmu)S;Anr&ISMeJ~wP=&j z5Pzs__IV!yj{GVHTE;PTSO%R72$IJ+%oDOBRxH`wl=o_!vy&6q9m{1|jHY-94GZ9;-z=uNSUl>_ z!8uj8Nsl^tNt-VRCm*&z39G`D@9`?k165ioX zn@<~+j~9($5eE%XQ?98=DYHPA>ctXjUFD4hDp{1hE8(12v3UTHnJQ@ryubAe$jg3a z*i_;1^#86_Xp5)e3FW$hDyUR}d`CX|l;2Fim=C{7mW6yQ9%GVyODVd&f&6+Oxq((d4b=TYB4yOjdm%PF*{6pJ2+_2a zFTKxtL+5VWFNMbS;8+7=o_ZvFC{|NPNjE`3SG23Xd2Hy}(CvsU zs8CJ9HxxYoyn&BHI7QE+#_X4+zJ1`B2mqgodl;=qS)$h`B* zv@V~LjR_);GU@RkS{}$sQmSdsb#%_|5s0AUjB0Xb=rOZ0^9t|MJbs|G|DxA@e)#)stOCX>fJY$YKPYN}AQVLq3`H>%Au$p|P!e;XkOf05B;v*plZmvLP=^V1 zlbFkdx=f^-LS2-}O__WalA%!M(RWz<6y`Ikd)Ym9A8ICiGY5Oy&YU``2tFUNqm6xz$)!&^T9LctNTs^U6 zVo zZtx4z>5MgI=!09&8U3=Po!+LxowS*9{?xA0EoG{;vj<9nap(jx1~ zB1rn7YG+L|Lw{_MgOi%VKT+wEQq(cCu(DseC$u*l8&%FzqhjmLGQCQvo%H`S> z{`~LYru*$_YYS&Y`|3!T&>Q%T_aq|=50ZRt{f#8l^cNnQ&#{%jbsly3ZEbMBj;8Uq z_RB#Uz8c#-CmL7QavLTeVbV57U zo#{&qgp?s5Sbn^5_(nR^T9g}niv7WUf{WGDz=UTWd;!xlrvuq=!ASb&u02O9bH)#c z+bDwpRM>9O?+y^*`M|;2<(oTuO3?tg#9}c{h9-upZx8;NRSl*-;qBfIq)_M8ht>wR z3=Xy~iV~=|K2MR)W}JUsM)Y(cDTyoe>3yDsa4^jt6U%CizmPkX>K)OWg=**Yoz#|( zze3*o{SQ?DUNB{QZM<&h8=GAA<4FXa`YdbrV z|J%OB4ry8jdTAt`Xzl~oTsQa*m&X^nUW9k%m4Q#@eL7>8+AO9j`ug+%7*JrN*N_#0 zp5AQ?hXZ>FMr_%;{A{=xPz(p- SO{X68hQp=Ter%P2K=Hqmc2fob literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an17.gif b/app/examples/Games/Concent/imagenes/an17.gif new file mode 100644 index 0000000000000000000000000000000000000000..01402272d4d8c94fc23196110d81c152284435c2 GIT binary patch literal 1845 zcmb`H{Zo^N0mq*wdGbPDA3_L8dD#=mDC{0FcoBg{?+GM8Fl7;9DUxoXhRHNC&SlDV zx%0^j5G3?KQc5LOEDuaw+JWX&%F`|hX`o%UVw>wZm+cVKH%(it?z-8GF79vG=ZDXI z@4i2N@4gM%`l{;A?Z9>*+6`oYIR8VKf%8Pd0!~3jLXNCJmWc&2p)4brLdKDys0>wP zq(Frzs!*UxH7Zn~>P*sQ8f6BBTB*<}QMJ-wP@-lfYEUc8YK2v))aeXHo!X)+FdNhc zqrqa-*^CCOS!XjVy=Jw=YIIo)R;$%w%V73e%|4sX}g`FuXoYm0a-q}N3dM9gQ6_&g+G^Z5fY!shq;V}vIV2#^6H;ddnh9+D(u zBoPb5<(g`w|Or{g@bdpRb z6RA`xok~1<^yr8G6Vk2Ry5n7))-E%0@|V_b#L(S^G-#jI1L%`!o{R)Aws4Na>FOUC zbPr`^4Gr@7Ts|zKOD05UA!J9MeWdOYFTy?B{^!JmHlUun(PvM?H?pun;;ki~hqaD`X>euiu_>Hv82iy);eEZJZIGRtb08?IXsV+E zIVWgUFou2pVd;yhP5a{&-rP?QZ}u*1I2mw(yz$kd3*|VIbNni6{?B&jhiI!ikDp&d z=Y3JFheGfEr~f+LO`shXE^3ozIIvfW&Lq2jj8iUah%-{BNqquvg^ zPLl=cJIFN(gJp^b@fjEgD3B+TRRcouAi%eFvv z3x!>;4~?azTL{28sQ6MN0m!-_cQ=lY0ecD_M1;m>unVeXE*yj&(r_I5O8Z^&L_KY2vuYEf4!bB)YRg?k_VLsx;f$o7|(MvP)UzPyKn%N~BSwwS;ndQp*Y@Mn{?;yH*wz4|b{je;b!@d5vRD7)PfqKVC09u-~8`Tcrm^qnR+ zMU$<{UAfQcLTXm6LE6u3!Y;_g%}B)`nmY^&@8z02ss%~MI#LUqgJ6^{AU2nC4yr3O zg3{H`p5l*&??G^RLJ#G(-?GspnG=eNcc&9WzY5hOPJZhs`G}n}v@Zw7pw2p7Wfgc| z^nyCp{};3Ef|E&p`{MbPY+`;H-tYBA_&IN`yv~riryXqNs-}PGVZE1pUk zo

O%wjdNsOgb#gJp2OE2Ga?Ak!+>z7|7i%xvYEg${{^?O1q=WH literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an18.gif b/app/examples/Games/Concent/imagenes/an18.gif new file mode 100644 index 0000000000000000000000000000000000000000..f148f8c3e7615a277b76d1942b8fd2420e676c37 GIT binary patch literal 1571 zcmci9`&W{80LSq!@D!vE`lLXYVUj(S7iw7#MT+fFK+!|S5>Mt#v1nRRVX>@@Iw-hu zYGM+{Obd;!l3G!A9}t=?Gdst5DLhuvZCS_Cj>pY(x<6vCAKpKFe*MJ6MsruDF9M4| zQ!hOuc(jE%8*0yYg} z3#2G>Vq)_qU?@*5<E# zwpm?avAWDWi^bxy3anPE%PPM;!8yt8RJF9U3^@zkEe2=1YqVX^-rhdiZg#m`qi(g^ z?H+aC?(gp(8X6k^-_0GP^LK4dS8bNFvbU%*S<=kSteDu<62P9kX7VIJ9@BtatuF7^ zgNO1x3JQx37wh$29$pCIRa#PhytvG0q*qlLk5!y0I*L@EI(dPPoHdl4F0s?6}u>^6mp)thKk51C?B?dEIfrNCGG~B8-cD z{@BW`YtR3p>|s(P1eR61?i|?&^g#*n&|4EpIfeWHSZtzALf7n|Eh?q``cH`gNp;O9 ziV_EV^K%zqr|c{oBTbD8_=q%c^1%$*8e_zi_qiZG%U4;YZ8}Jp2JfwSe!X(*%cgH& zme2g$|-p2zOolIA#IMLgHJMD>dW=? zu9x)EqktrD=a#F{OzO^K9I%00oh_pYU!@f8q=(Erv)9$h1E#oI}$?lz9fA*}>K0 zU|KEvodfdZ%m6@wd2KdmMeq|RLd0y5mm^gPNoF9*Is%qUN*a9@Fam=123urS&*+-@ zH5RI=UPoi9HObz$D6+gg?}JJN)2Vdx>Gi=p5uYkA?J-u{H|nJa;=d?E>loe+8#%4i z6!j#gVA12QbCrH;$ll06PC{6UpACtjIPji5ecOlnx^C7tbZ_U(3;ZKKC?^j~-A1|A zSNz6r#GSFiH$H_&VSyfH4*A-X?E9%Ich|>9!wJuaaP41>BWF+d+xYRfJ{XRBwZ`7P zwPV=B79Ri0htlEI-rNWObaDqT9lYxpYPLI|!Bua|v+>acXB~5>|t5b^u5dJR%;lrZ< literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an19.gif b/app/examples/Games/Concent/imagenes/an19.gif new file mode 100644 index 0000000000000000000000000000000000000000..21adb25555637be4b1bc32575b7bd858c8e7b07a GIT binary patch literal 1849 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 fqZ<#Bj!b*KMTzW?4e8=bi<&zp%qh1;pQ&PYg literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an21.gif b/app/examples/Games/Concent/imagenes/an21.gif new file mode 100644 index 0000000000000000000000000000000000000000..f970b811c335423f4cedea60570950a118df354a GIT binary patch literal 1720 zcmd7Pi(k?Q0swFjyBIWN=MM0t<6Z;{* z!(y=%ES5^aFo>v7DjLfOpfCbLX=FYPN8_+!!*St}thdS(_u_0w#zk^bYk&BYaqm40)14)79iB#3T;G<&y18H&P`zYmdzD^cgq)a%L zg?34OjagB~%+#)|H=9d*drI)u5ByI2IjOrezEP8T;R90pp)yx(Bv@N8QL)ugpW0tt zIadcCKU~*iR8ATsQ%$J8rqUar@COUpW2a1l?Q7t!H9Mv6jB(1TU3SLayjVVQ>G)Uu z?aPA+uLr0IjaItPK|ae0wjxBwvJ2%B3uJYKV`9cbgAe00)y z5hO>VUE~pur(HFFnzO%U#BMn`T*lwLcE6;Q6jQV8&Vcb6{C1GTnr`8bCgW{L|AHP}su@BeoIj2roJ~4f?9=h!; zyMJ3N`a-_98el~!@+H{WKOs-mWpG#XDZi2yfr8-GM7t6;>lULMX?_OWdfr30AtPC} zWM<2t-lVv}`jnkkHz-ClT4{FgUCBw~dfb^+mpOy4li&h-(%Y1M%FR0uFs2;Gd{Zcp zv1G{v7U6^t>R)o|Z}Cs)(4NwV2kQ3i`>hb%8=w*wHkEpt$%3gDO3m#rHpZ=)z$-YI zwR;wh3gb`Q#xW1ANHBqOw0A!g?5H_YmrCh#ky07ZRMltCvyazv2f_BTxCK$3tJn+e zv`HyW1M#<^F*0+`12S4z;j@O?PV($HIW76og703Ucnsfm8U*%U`qt>v_qjH^B`ond zC?O@nV+za}$sd1%VNTh^=C>7fQhK67)8rPBWKG{4UX@}eK$|hMq&BI{o3Z&e~TVf^Nt>ZlJH~K zIyJ}%qQvJqogd@v7ovwcQJ!=d#$k>OATvL$VG?#aL?Q@;5-);oC*)2knRbWQ43R+- z&B7V9!_~M`YNqeYXHVaOCb~cgJ_g=k!B7@j3F_O+JSQV%AXR)urw`A z2amWEpl-g;b27x7qU{wTcGX6-`n? zPO#cK_T9oA%nXuFiqS$6s$8knUYLXhcSSdjOn-K*>UvOc198bc`g4tdrq__Ov(L|u zenVWb#-C)LvWT0^^UzI=G%ev}6@d;s&Y0oFcd&sGgJ~~YboQ_7&4eOsh{6(h_u1mt zU(SC&HnLi2q;0%9U%736nmQfaeek~o?fT+~kJSGY#|=jY0rSrvGj|&Nhw~9vTIXSV zSZ5F@pgWbB44@v?_S68go$Fq<%R_Zfewoljx#Au%(PpOgq_4K S_!@LH9Qp3Uh9oQ;BKRMkt_>#u literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an22.gif b/app/examples/Games/Concent/imagenes/an22.gif new file mode 100644 index 0000000000000000000000000000000000000000..6022f7746e72ce377d484360990e67595737aa03 GIT binary patch literal 1332 zcmdVX3r`bw008j+wb$3}T01PZb|6=&ObXR%i$bi@*O7 zTCgYrN{LcYON)$6P;8jn9O&^_Ej|)O*qky04h+7Q649t}F#Q<&eTAPPRUaFds{~5m zO91boD9Q=qi%?V~5=lfzxP*twL*5g~L&CA3Sd1SN$&ZYVSgW3=)yUJdB0a9$sz=tR zsj>~BxtqeWH)?lgMdxKD**;U2=gAB6i9M!_-6chiuNE9DG*p|78uXo_ORSk{K`gkkz01{m5 zK7ogaENIZF&NxomnQo7`0|yPCv^Q$wd+K8jKs92~ywgCoYF#5AYLIa2*UAwJ5-wcP zBd(lZ!>)pvFm>Fq`k<8+c9mj_pFd_Vb5erEiZ^c~#RvqZ1$@;Qpn9!r(L2A9$ym2f zD08+ECS9*pqD}yvokO+upyWLP7c`^?;+P*aTvFdEd837mnw$t3C1E=Jau4HYvyL|c z7vFSQPBs`(G9c@3Hn4#H<$x=xI)Swp&qR934YfV{o1$&u$p?cJ8Z-xtQy)oZHN}#l zSK}^Q&6U>;^t3GVX0wff5@WRRa4jcljPFiNMm}aCNyo`48Sv=-lp31`D=wqLR>X}Z zZbd_q)wA<8bqT&-o!Uf4hIAXc2wwzph}<)~xQ8Np;l_>kTNYB%xKij&yB&=xoEajg zpSDU@c(1lu?r)B9_fxjeu#5lTPk+?>n}XXZ&)B)FESej?_Pdd}2ftLzdDs^mff8ct zl6}D|(~9FK0^BV^6F4nkw|sgJ6U1&}WcQR45L0AySvZQoZVS?pN%~dG%rl8-4M70A z!ryt zRBqf8R6XfjdRGiH+K&YKPuiCvG*iq~qujrzgG>>jpm=;<#Tn~k3_;b2{GixQ5_%mM zz$2sJuczQjDYl*`@n51l2LwCFWsOscl+Tg&myt|Vmm_Qv$2xd;7dim` zsE&yhUf*`9BYh5+Wba+ra_dO*wR2|ytTBP*-`0Z?#`Z4Q zGTH-d5ACI5>pQrpi=E^HOC)I&PCy}a8|LkLYtQJ$k23hmv0JzMF2WI{80000C3=9+$6dW8JBqSs$EG9HGG(0>!KutkINl#Q% zR9swKWMpJ_acz8iae;+=jEsz*n3tofr>v~3w6v_m#Kg_b+0@k3+}zyc_CX>@2HRA^-&M@dak04x9i001ojEdT%o{vaTZ zq*zQ9H8F)@6oI%%m6f8|#_fDxmWIe8Fi0>O5DBHi5EOVD9cQD_^sbzrDBw^K8CTuph1R?@|0|Fug6Bij24tzT#0|gAl2MP=d2!OfK(gOhq4Gj+w5D^m+4h@drJR<@a zNFd?>1q&Gx(u#0EfCDHL=0%WzFhi9n8fs0LkO816Uj!!P*?~bqg9UW{HdKf};lju( zBLf0JP(h8i1`I5$xlmz{nx9jg84!s@B?AHvdr+wY6ex)XIY9WvdsuJ~ zVO17fKL(sc04l%<$93-!oixH1F_2}J;>B9P(gKDYS1uel-~g0ZfDp}Thy2DEF4;DEt`1?mozld#+-wB+xRAOJ^E=+#381~~rEA1uz0KtKeuB-RuJ z0z3dd^REX9PH*Km-;@xE>{a?X!+&kjaS8ENpor-~q^K^1uQKP@@e2#UZdj z0~BEflm-(}P(lH$rE!x23-rc-0}t54VOSYBk^yc8%#acz_dsZolRsU+Pm@0wv%qEs z0=2;k3jCHUf|yd*GC1UXponJx>mm5~Gs6tG_oQXE+v1TI}g z1qLheNM<>6Fb5eN1}N|V3Km>ogFX>F&_Dv>F(ad<7%(2Gj55m%@CO01CQ076)-W(? jTt|LO9tkxrvj|q!qATqH@RBKQpcAmw?!EYq5CQ-@J33%z literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an24.gif b/app/examples/Games/Concent/imagenes/an24.gif new file mode 100644 index 0000000000000000000000000000000000000000..7cbc5d78008b457bdedddf51b749931eeecea156 GIT binary patch literal 1627 zcmd7PjXTo`0KoB|ZEP5O@w3hB-8!*xGgE7_Hk)msYaE5dh|pa{s8f2{_=$PYdeNyH zt(r~`WxJ@Ox2+eqO5KulSGtFEh3ZaEDp%sV|KdJ>!S{K-%H;~-lB^IQ1n64)1&dN@~I4%wyz&%`v2g;MG})bR#!sb0WAoUZ{(u9}m-2uX2Z0g+NJh zt*iVmigxbJ^Etk9)A^K;+p$RpiU}tRqD~d#e)x9N@g3a0LceQ8<<@%oFO@P!1IpS^ zaNQtwws_fEmJS=imxd*dquQrOBS!ahKR)9*+!=nqYyEV$*ZdjMt8+=i7oh1u#Qae3 z%YNgtyXKdJ=J{t$v(x?a|5wa8U)h%2tdiUu#|X{_7x^R~wA63Yl2i4U%83Ns_?Nv8Fd8OdH(J$_aI%m%^`)&(@QnBmmO zF>FxKHaMl}+v@|SU30!(_XJ4@5o{WKtG&L5-tNIy@v^IL=3-c;$Q*Xnz{bF|{tsd6 zvTA)qrTQjAO_gT_deKv}+K}AqiB^+t*f$)C$eR;Dq?1d=CNCrPC|`7s4zia_kP4%& zWIg7WQ@Tz9Z{f)LT|qgNCD~@eLxiVzM3aDR79m=V!KLkATC3L-=K=9Y*SWfOxhdG> zXf<^N232$|xSd7sBIuJ|h^8xjt6XaEud!wR2lhSQwBwDBotbbJOs}{6JY|yo!5W@F z!Qs7dcwW7`^Y1Zrg%jJN7nc7-%P5DGt+l3QFF(S%kS0N^4#`n(mBRHkti;q;*kec5 z5LKVEe(aZ^u%dqo5w17H~hXw(#v*FOiSF%yD znPi0_se>ib0RC>(I##S?Zafp3WA46LMN65vYJji_If)%;g9;lP-L?|Kv?bqMP@JrE zc*MZoX%9lPSh@S!eKjdtPox)D9#XV10yG^XLn8#(OQu+TP%O7RLsjHvOpat?g`Uzf)38E@aqIWVyuN zshDfhIJ>F*BKnoSUSF5%4WS7s?TZr(p}B*2s3;((^?r@HWiM7^{Ro6Tz*K$G^1U$~ zMo0Dg2v%)gY!tv}h8G&51BmhMYXK_RD%<$Yvu;G>B#13#(elk4cxZR3G$XR`eX3R7 z*<pA2}kTZ7Z_e;>6D6k9iVJ8k`(;X;JZL+w;$tXmlG+-dZxW+Dp1(Aa*fditnN zd@7VzDK&{cR3fMr&3iaf_`yCFez~MU>~!pNtPYMmj7s($441+hp4i^S;XVv?i=P@f zAbR7$E#Ix6woPsi;K*x4%b0IX1J5yL(CQw?Et+|K{V&t)hW>E1i+8)P#hVp@OUnF| zv%lw(d{O1(ouw{yexnZ>2Rba#*r8jm#!Wq)&o50vZND}SM-vc2&3f4j`WwQyjyYTJ zn{|jT<*Gx@Cv$@Gi?nd@2(aOT8y%Bx-^J5b_>Ie4gqCK{)1~`6)7xQO%L9Pm?D?pQ zC*R}QF=>T{JEK|`{FHk#Nz##s22u$q$@w}p;P7*C18h3)G@CB4r#c9d*zol>K!tz- Gk^cgv$mc=; literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an25.gif b/app/examples/Games/Concent/imagenes/an25.gif new file mode 100644 index 0000000000000000000000000000000000000000..842fde89b07f902e2419fbb3ec524ce2284a3bb1 GIT binary patch literal 1694 zcmdVX3s+Ki008j+y@0^=f+*;8Fb^r2&c|?Kq0t6WK1z@HTF%H}LZW7&Vfbhb@R?}c zrKPPhE3<4`=vk$y4K*yCZnO;3Xmib6ZKm$bd2(yE_A&PR3cv8kFn&m?Kkx^GBJc)^ zqC_VO14S7OhARVMFj$_hWR$~tgTY~WvYi-yoHy9MUOs+aLEhdho*zfRdqcqEga-Qv z1cES;zc5-Pj*Qt7#Z8G3BqSv4jpECto1{|duH>YWgspP9JX0abSH@~`vJa|btJDbx zigK``ox0KkSlOwl|r$@pGMHy^7_hF zluAEK#zPQ*V65Wutx{t-;&_ct2E$Na-{&S%#Xt$_=t#v6cbiZ;b_` zWyfDA9456Fl@CvuYh0PC-R8X(zijB}V|%WwuZ-fJJJZwB*N=m7>JzgJ=$w~k4N++c zIrp68% zD(7;ArDD&HkM+d7H07O~;*#Z4Q<1!j+3Q0c+`k{XBMVFuUm*r6C7;=*^u(^W{Zu-8 z{zL9DB3Z)K@4gm!W1vXz3=`|J+lBW6g0MRSf zI5VnQ1$->BfwFa{6*2Zt3xp<|{NDH!)&DP<+z>Lh0+Q`%J#=xN4*>mkxf3;Kh)f3C zZTq7QNgSH+<-$2ikR@C%S^ZEYMppp|d@KNK9JR|XSTMmeZJU|YO-oY)4ylzxII3c1 zU2;(nMgYiGb6(;;U-qTG_`+Yue>oF|RN^QBt=OtPf%O_3cweg$A%|ZqjRF3jtL4;C z#)%9=@qU+LvJWFY1zEcwyo@j_UR*SmdrvFqK9d}ZF*r{h^S--Efy z=|OiJjcEP^(*zF{N}TuJTZs;LS1L11AC;|F+XBJK)n4LK&Th$futO4dKmO9UOIC7- zze#Ur-M`$PW*9v-LSS3f+RuDzWE&H=U3dWL-C3BPu!gTGraI_tEUBMKAcWtZg*~G2 z64m|jIK}fFx#Uh2w0|ayQh%OAmFP6wUU57mdR#L_vR6uV`$Bq%rm^k@)%WpJ%3b(v}?@4!*&@qlq+O(D&po|ia?Is#U)(!rD z2mwWC8kvQXFl_)u`*Qh6dR(Le{q%KPCV{MU|5!-w+_whcD;oZevDB?-7afe4mY|AE zdG*O$Xpsy8NyuV6xGTI`1Xwa`1h6eDx*1!V5lA_-`K+wiL@sQtd$jpQEuf}7=XBq`sOBK>gs%vE;K%nxZD=39IAZT1oKYGGH@}! zYf~H4OlCwlQHFYseT3Iqlb%+^v9>1sbrinDxTp^_`pe+c&#Q8XP93gWT!!w3WqQ)U`hNijv?K}u literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an26.gif b/app/examples/Games/Concent/imagenes/an26.gif new file mode 100644 index 0000000000000000000000000000000000000000..ef51d69e820ccb5859317741e29bcbd1e8b8a49b GIT binary patch literal 1931 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an28.gif b/app/examples/Games/Concent/imagenes/an28.gif new file mode 100644 index 0000000000000000000000000000000000000000..10f6ee31942468b08b661b06904c5663a350cbaf GIT binary patch literal 1944 zcmV;J2WR+4Nk%w1VJ!eH0LFd*0000HAT}c)5;!0Y4lYwQDGezsCL}OM4={i|GY>R0 zG(0pUIyEUcHbz-A2qZjwJUl!YMruqv6h=WUA4Zm6MhZYpK|@JTN=z_>F(h6`9U@Yg zJW7QlTAg81A6`#9R!eSgQWR8FR3c=XL0X?|Tr6ZEQo{DgcjH<_jL&%N@ke7a>lyt?AK25Cfgr2b9j|rccm(ZRKz@9v^ zoNfG!1l*hy?3@&_riApI1c|W7qpGLmp*5_ml>eXy-LnhauQST7Z?v?m)wE{xwGHI7 zME|oG%)E^4w^Yf-q4d-Q|JEwz(WKPW)a=xx_C zX>@2HRA^-&M@dak04x9i001ojEdT%q{(t~9gM>PUN{ESFX=!+lkC1(mla!Txf?Z-_ zYjd5Dk9VPQqok##r*mUkV{?L>p?9NWN=HFKI<~n%wYt5&y+Bs3vAIA%D=Hiy92^_Y z&Mqz;F3%esBhlG5+uSY$K>#{ycN`QH59kQ!>+BEB)z;Gw5Ft23M@=r!6Ce&A7Z7|Paq&RQs2w&Q$c)p*Cb|d>79>a@K|+NPF($ORL%`94 z4`0^&7{MXVoiRyi0O)cDOvecR2LvRja0Qm#P&n-LMfn6wJBf<%oaQ<&t)!EahLCvzs} zkS1ar6&IoLOkt-ugwGutC=e(>H3SM2A+CmC(F2#0CLO#X}r>c)?F{A3Zb83$rz0)*WBqfPx1VK;aWL8RB6C1++zA zfCN{NXv7O05cI$WA7K8Vfd?hPfY1mm+Tda-ci@qP3mm8=+7cZ+fF2QZgbfP$z_MAm^b5lnDL6CKPp0uXDgp=c;yJczTDd%u3<(OR}eu)IGFBMtPgYGL0du(oDc^OKy(m640q&8VF=@h znL`Uskl}0_TK-79h8Xy&VFQ+=$}z(TBc!{*4`f_{0|q|aK?MXAu5J26K2IBg`+u) zU{oD$k15Rs5`g)}ZqX!lgA%UP!FLXR9}&mgb1w1X6*GqVQ)?iIu;j0IO!2XGNdbX5 z4Pw->a|C3NI0UWy43WfEqQqfFJvG~F{u{?v`PbIcuKC6n+;K(@d~;I~Hyb#y1|D0uol^Mv@FVB=K$QPH^y_0^;#L3z zC+uKn9r(dG*td=RRSs+V*hUBXCkWB#LmxSq!ER1~1G0^R4YTQh3>N6V@HOy+5)^{} zPUpT2hyYA^*wqs5@Pv=Sz$^^xfC)x&!4gW4g0%YyKiD@cDbVkJ9f$)M*vEl!T;~BE zK-v|qfD|9vV1g^$-^Pygxx_VrFKvK>1|NXK=&^xuJD`{quc*Qb;-Ll6c)`z3a0fKx zU;|R{K@8?#$07Lb4o=`)8{)8p5j0^AC1l_TexMff!7&>Fi-Q-~_XIh>;S4DmLIl?7 zcAX)dfeR0i!X7l}z6@fqf^~QlB+Jr<*(6~LTZq(3F0clPflwtQD8U@K0KLs=a0E_h zWbNQ+n+J@b2xK5a6L!YL5)k1ISO^vyj&Ov!z=4_A9KjL%R|jIcAY>FsNfe~O10VPR z2Rz_G7e1GSA*7KNb%><~v8k+WqLKt4%cnE2mjo_^;h%1>!x>xkOM|H`iMDTjCP&y1V=|2(s-HlVw#8-#xjrrLKK1qIzR*s eQjmg~(v+qw%|I8rpaetcU=Tk2=}%o62mm|KqCRE- literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an29.gif b/app/examples/Games/Concent/imagenes/an29.gif new file mode 100644 index 0000000000000000000000000000000000000000..c5a845030f248fd8fe3401bd0cff86f4f0b72ce8 GIT binary patch literal 1414 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an31.gif b/app/examples/Games/Concent/imagenes/an31.gif new file mode 100644 index 0000000000000000000000000000000000000000..aec9e5268040b0c50ecd816fdebe7d0755a1a6c0 GIT binary patch literal 1910 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 J4h(?o{{dXF4H*0{1F0r%1 z(~KObTUw~(Bi*u15Xne8&Bs^HOv<|MJG)OE&E3uE-TsFC{)Jy^I*XE!9|MR1xMhI< z`*07kh=|N4!*b$wo*-j#S%G>ojFEm=&cqH9AqHCbvBOC`9#77uXg_#MCX5ydQ-nfc zB|o5rg(^HM(DP&TybO^@R3;)8i#bzN_*Z=VDGB#N5$d)e##R)fR(@>y1XEv}+Ad8t zs(2lrAj>{ezRvn%?PmpU1x{O0W~s=SE`zKWhuHK&qtV!OPI0%KF?t?WTYvWEd16#zZdk0$Yxvegj%Qp>~@w6}8 zw~miL-kObC9y8BPeLp=ty*%BzG(9*!KfgRbwzRag^|Ilo7w&&AKilw)ZLR#%=kslB z__ltB9h+f{lj3}pSVT}uRT2VEA||A!XL111zt#Mf1b}X8igo4s3PWX872u0n)NaXB|78JJWeNV*hNde zJDQX7#TBRk98K$(nQbBqYJ=aE$F4OMaM*!yq*U~dg<=Oc&%YaJQwpd zHR{stOZyUA{%f8)u4}j<_MDxpGZkLmy(uW3JlL>;t>Q55uaGQ;%(>2tr+idm_Yb7g z1X%q3<1XP-n`S02<-h>*T-e@|qCI0{RmtL{wlemIHFrVS^F~)vzbZ2hwdWRMj=hGJ zh-Z%CUMt*P-58uiM}Fqb=)BtZFRk0#(sUABqTb|3C6pqnYn<0RZNTH4Z29z!esVGdr$mwAKo88s*z5=3B>5+aqV|OK7t7$Lg$B+EsGNN*)B= zbXu*`V!z>!!yyTO$kMG&KF@)ju>!}H{i^^vL`J&6D!IAR8;EhW`Om3~kfjBm_V?L` z!lk}`)2CeRt8ul+w>jbm!w5&pgFNgBZ7xa@E8&GCN=xc~~%UC0uNN z=!bnbDm&lur*G|4*tbddhJhLFN~WA+CuE{MqfRiY#ID3&V?0r4{O`QYWdy!00bC=C z*%m#y&e;QF2}ToQSrNHq-U=LJkm7ltV%t+=OK#>`_Xi1-b%DgeML)iUOEQR{ ztbAI?Hv}&j`QD-ed?)H>#c;6SDPM433ZRva(} zg_eRol!9bN9O@mU0cF&5mxHi(nGWt)ls&)F_eeGM>sqk*duq?M%G!1#JdMLu1F`JMlRzf%*I1xTzM>6|#L>>U9JOr65bpTtB zwlw%+E6o9uDF})9bU6Y5&tN~HhV(xF&VgXS7k>dB@zuEzFW*hN!UU%pZ#qNBsa_o@ zfc@hyDTUKH{cxmtoep{y|9bcb>HH#NHs6vRE~ILMd@s~zq>UiY8&HT{<43(cclz8< zz%+~IM7RnVh(`2%r_pLgGr-VjfZ_964eBm}UN95vM>6KaLu)~em%U(28$G;@ zQyy-i6&;G?-#<}F!$lws_(z(@gYU#Q|HlGft8Vtc@Uu4R=;XOwDgYQkKv!I~g2E=K zaK1u^k^qc(G7V&Q)EyVUSQ8AR!AOMEgL6th`hfEc%lb}il{e@tycIi2N@HQy5C9ki GkoZ4oMhkxc literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an37.gif b/app/examples/Games/Concent/imagenes/an37.gif new file mode 100644 index 0000000000000000000000000000000000000000..32bc6a9d1d3f8ac97e3d1b7dba6ca06a157673b5 GIT binary patch literal 2003 zcmbuXS$i>ePW!_f**$9TU69`lNeoAAQ=v?BB4@zwmvY?}p6u z#J6nGAR64<1Ktpe#Y-?AAr=z^p(KO^q0&h41PLCd6sS}x8M%hgsKQ8jl2SxOY2HXA zC1m7^+)#1CD#@y-74cfFN*|RHAGmpyOp~mCBU!IWN?5f^BU@owlboEKolI;?Se|Z- zeqSH7JzeP3<5Z3@H8VFWTjz|^6m3}26SL@|Mq|rgR+O7umQPqLmQrh!)oMLlAaE8$ zJ1rZFi;L@uHG6iNE&FpmEeJiaYtf&#L|5RNwhNWAy4wBcYNJ|f z-?i7*arGOAt1Bwo4-6e$*3!~4aSU&5KQwe~_Xj-(x?Js(os!SnPoL{QKQJ(G@#4VC zOOk~@D!;pInYrrpdcFVnOZTYdiweFC$lr&nVFf{+1Z7st{?w( z^TqQgFJHb~_`j*=6{!_FZB;vq$?DxzC8VWfC%GXr#|)x=N%Ko2z@KLTfG|BWXQzF!Np#F4t?79E&z<3Mg^E{|OOq zyt()t5CW%qj8j-9kd0;$@Y3u*kvtY+3eM+;#24FUgs8g2q|;6STx#&8LDK2jQ_2X5 zCD8ffQ#gwnx@uk(GbK5b@aco)W zi!Wy%AO2@@{g$sQAO-(OowMtTds zxnm=D7kY8wc1v#^T9@r93qJc|4or&kH;9;XSs{uK@c zquryw!{SEnT5(`@D{>NXN}vXyAy`L*WuP1N5RVV|MeyX+`{UrCh#l&ZL%ph0|g923N7w)Bkb+L zwBT*Gb#~lglG=G8H9jyB^6Xou!XjM+hMG&bPWb*Nze{d&+Un&2tv*lf(&@oeNrG9# z;DuXkXdEJCPLuqOa;JPHPP6>~%)^{1*k&@Ob42B>_tvsCIyB_R{?M-FNQZCq0#x-* z+_hqB_RUivy*|$kINN8JY1)Ryj*BTjgRQ(>J}NjiGzp6gp;TFL))pfcqCmhVuOHr3-@gk_6*=w7scNqz1ElWq~=uoX*moMTpum(jy@=9?ebi zVZ37iNG81FoWY)Q;;+9A@aGd8EisNXf3N}1T)WQTP-f6_L`mTfhT)OA(#>avJAVKi zM3{|y(XBwBT%JJaC??kL(~wdicGIZJ=iVs4$EEbO)-Rj1%hzZx9Aqeg`#vz6xKv&# zDT^tGkYXQ83>-g_)^W;uq{oTjlS{f%-I9~Oc%J!tHF5-MwK5Lj1mM?L?Z=f!3J`yb z878*AQ2D>WUc}a5J{wv`pESNYi*bE`UzBhj&5Hz|dD_2o%|8W54X43tJY!eLaf!5> zy;Nb8CEk~yv>UBe1&e-~{3?tDq zZ9EGLE_@6?tiO2riW`An`H<~DXQNTT9y|be>*UrZy|2^gB^74~lVTI6EnP)H8@RcI z{POYW)2R)md)|v{S>3+rH5ud8I_RLU(BIs4^A(fgr8_yMK1PD?nhd_J7=B{c!*Q literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an38.gif b/app/examples/Games/Concent/imagenes/an38.gif new file mode 100644 index 0000000000000000000000000000000000000000..9aa66afb3da19501fe0fea21c813687549754270 GIT binary patch literal 1782 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an4.gif b/app/examples/Games/Concent/imagenes/an4.gif new file mode 100644 index 0000000000000000000000000000000000000000..2deb615b9247c2b6f95904d5a04ce0c1ac9f1f72 GIT binary patch literal 1227 zcmb8q|5K880KoCj^92w@MkQ7V^HCoV^`V@53NvQNP$|D*(M0ENpkVl=Wku!+3Lf!W zXN@lMX1S)#?Od0QPS$xhuM_56xmM(ta=X@98MCf7YhLF%&&}Fju-7l|pWj)VGgCGg zq(BP3Tnm<v$J7XBXrTM*911#BD_2yg+8hZ8tX5I8{!NP-|pl8}>xYAN_q~bvvC-YwIg+x4X6Dwe#L314BdCu3Z}%zq4d| zdU|ed?w|iJdXe|;9+SahGGhBHEM`n=Hep$tb2Wf?wf1Tf&=&`f)~E^oCs%O&)#}vG zbD{eC7>Xj4(!iC=0-n0~00&SixGJgR^whJz60RSB9FF4MJBA;Yye>~F+y%x1&7&H;5BPZ`~YC_g)x$NJc8DCCeKt^$sWIw)w_pX#IqgoOX?mR|yM#nC`BQE4@sK6EJ2uC#ZbIG%9t~MHaYiH3^Y`ntZJeZi*EOAvOYqqw*o>A4vb{zKicU%vo2ZzIj^G~+Bz}9iK@vii( zDF8d^Hj%gXY3e0&e$<$^4+g96G1QwZW{tw%#3R~9=3EBSw&i9}nPZyK1fZVg4Z+YIV_yJ^;E6B*56faXkAAa(1y07{d%GWIss*!`?1BB?!2 z+x7d|!$B?v7oCyrrI(B+*ppDDP?RlpRx1CGXHyss36%P(Ml zLv_V0#1LBa-N^>?=q(Uh)9wg~w`v@Scpnx~EwIi)D+@p-z!6vZ1Q>X#Q`TZu4V9Lt zQ+K;09Z64w4<}ya0e`qh!C3kDfwV8;Qc^;#L;@z){7Q9>X5sh;brhXL1VFEPT~*hG z$@C-#J?-48>=i4fV_|e=%S#9I9`(JFHBsU6MLN0moG6yN7^-Z2b`gcb%=)#Vy%#|d zjYobA;1Db}BoUc!9L;?D=HF`=FODQd%R2PC2RBx-Q|;)U=d!Vbyt9~}RaV}YzD|R8 zrqd$d&w6B|kA#27l6tt;;hyq6iUibJb|TSNeSpjw`3V25{p%QRF1)5(kqL7!QQM)o p8UD=+vmFcj#32^xq)eFO8G<<8px-Z2%Wib0e$i^*8Hqw*jnS- literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an40.gif b/app/examples/Games/Concent/imagenes/an40.gif new file mode 100644 index 0000000000000000000000000000000000000000..b4ae4a6eedb8fc378965d2bb9b7b40ccfb2184a1 GIT binary patch literal 2582 zcmeH`=~q*C0)~J0-YhpHgd|*&Kmy!ISVYjsVuMD{4Qs%t$fD=~Cdd*f4M9OrDRapJ zxHeJ?u~Gq{6kDoLnYt9`+|-C@(b7UIwoE5F*s2FLb?7qf437OL=F>c%-Vg7m=bR@k zJyn^Yj|S1;2OD?^hr0>4FAS5)c_>qu81IdSh*=!D^d)?J6(*O4 z;DXg+wi5TomxXbcXK#{l;+J|ajS7p6h>$B%*T(u}FXhL@#i`=ttK*lJ$@$yjP`Ro> zrBaDgE3_)CAbv@zI@%Z~3{Gv|k?LiQLx~OQr1VW0T7`9`yf7_rNtQ#EVXI0JkIHHN z%YF4(!Odz>;U-m87M7EfQ>9g|Pe0$35v<9B9Nd`%@D3eP+tb2+<8L~x!$1Z)cm6-iOuj1i&Nl~ zjm-qDSbBGJQh-pvQ&KTawg5qNDQ3Va4S?i1HAmgf>J)l2jfJv!C`er5cLXsW zo%8VS-){8{&5z(WdrM+A8fV?)c?cY!zjmK*EW0Cnk1u}LuB`n%Zo?1uyk++37Gqc z>Ofq(b)s(J;n%yve<{6P+KdV#zDz7SmuIEv?yoxZ&!|`6%7>rz*X{WB%-!Pe8d7Qy z1Z-UVCG`B;PEMHC7{~24UzW~EJTVdaomUxt8|P+^h0uF9gcUxz^KGMO^yhCH?*86D@mHudSh+ank5_1^v4hN-;Q&s${Df7w!!g_(aN({}XOv{6oj%Z&%U1&_kD}U2 zXRAvNG-kf?AkZ}TaoStb$6ufH4v(lM#Mw{#zpX0S^4>dJ4>*Tgf(mp^Hs6E+ULr~Z z3bg#&EojP)BC@;n&vv{CWbb-`fAchQeE&L^$sp}KvGniVm zmNsPJuQ5?hj;rFmB>altOJ;C6<^as-^D-B0{sy0b|1vzmPcKmDDIdj5cZy$nj{YhX zIipME$9}G&15v|IZ~OWxplfv6_^`UQQbA-b0-?_7q7B^a*;z);u@PkZVV-NlvP*-2$YghPWB3oo$}6eP$d7-j|0~&SI!K#1n<#$Qci;?GT|$821s$7JAM_YOfGTo_qN(*6Hj<@v59a<19Z)c>rS z@Y^+T&l%7tmO+(gj>)J>K}`+RcdSC@WK<0CQ@P9;($VOSg$JBIdjSP4f761-$ zW)@uE^5SoYkj3;#Eo%oL6>|m#OH4A30843s4$*(6W_h{D;!r0eZ?tIOa+Y}@&rQ#L z`PCYdiBSrMBAjjCUu|QVbTHF?-HR>M9dCpgZeT2dsO?v*7qawVi2B6h^k$WKj@TJ40e0 zO!nPTw6S6z!kA+ffOWT7J#zB%<~Bq^0s6Cj`OADf0PO_aOb6kY;kK^D4Cpd9K<1|` zK@(+g7xt!N^CZldI+-zKFYLoJvv8cQ@u+!oLbD=sihFMHhWS;DX9+(Hykb$HECU%T zQ#o^!+njjG#8@8T1~i8g$?DGZp9k$y9{m3IJiy@SQOFS?njNASo2Ss+A&WYJpvheX zBj(``nrt3eqH=ndO;WVt?5omoHCyUU(4r>Ap)ShXI76ORgxl7-^(WW0s_D<$AU>|| z)6^oOk0&>T)t_Ir4^;R)*kkcm;9emglBYAttThvQSlp`i^2!|e@FKy`)Wd>g9gV!% ze~_a?@R1>A>?ot-qoqllfX5`;N}+j*tJ38Jd?|4S$JY}wZOprd7SG3XrYyL_w7Q)Y z-woND5|5dhx^iV4-NlC_n#Ds928(tAA$c=yE9`GTiZ5F51&S7K_@IEXgk%;xN)OlFL(XIYQ*Y2f6P?bfdlw|w&N!3zuTZeeY8TzxcI!<6*l^e1mY44#12QQ^)fl}(zB1XDe) zW*fgSx26p?uTetmZN4kbpV-3^l5zTcu3`T%(t3$YXz%{U$Hzd?1#2E#A5$$#jVij( hSDq9(KyVr;q~mlp=Xd|iqxHwn2K_D`N4!w5=D+CWtbYIi literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an5.gif b/app/examples/Games/Concent/imagenes/an5.gif new file mode 100644 index 0000000000000000000000000000000000000000..19e3dde6d1ab4f2edd8faa9981123d2e3a01773d GIT binary patch literal 1628 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 W3cWp-(!r9{`m&`5FpvNO(*FXC;qg@f literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/an9.gif b/app/examples/Games/Concent/imagenes/an9.gif new file mode 100644 index 0000000000000000000000000000000000000000..89b2b7b390d5abd8c9026cb2bda5d50c9e77008b GIT binary patch literal 2486 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 A#Q*>R literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/colombia.gif b/app/examples/Games/Concent/imagenes/colombia.gif new file mode 100644 index 0000000000000000000000000000000000000000..60c8c145103debd0f90600660178e3ac373d962d GIT binary patch literal 145 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 Mhx%D#LlVsXe|2a0c>n+a literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/logo.png b/app/examples/Games/Concent/imagenes/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..27a8611ce32d3d795ebfe834f506adbad7c8c71c GIT binary patch literal 1591 zcmV-72FUq|P)NPN5d`x^~JY+m9R6Hz9JS;>k6m)cSY;;@{92A6vguKMW+}zys^z`iP z?1Y45YNsyyVQ3 zlw?F~OdR~=-0ak}#FT9O?A+AEto-D>q@<+G#N2c|JnX!*%(T?})TI2>v{YPF%*@Ob zP}2PX0004WQchC32z1d<*{0UbC%mF18PMNzEH zNUSkk(xzs?Km%%0+G+d$f5E&YCYp4aPjk)VWj^3@pLabZllebHQCVsw_hfZ-HJ8h+ z{+oLxyGAdISzL85!YjfQq8|%OJudh5f9hlXW`Wf+b9h6&P=*LPC=7+3)^erRn zYv>UHPxDN6Bb&|MIM5?vb%TBk{87klY&<@>$Z~vX9)o#MM!EcS<2z^M8fcs(F9;=8fk#CBk~p1thFRb zYTKr=DG{*4r)e9InNF=Ng($G>YApiypXlMzj}garNWRGE-V3s0^_>801Zmj;lSsCIN?@2&*KtZE-!Vd zz~o(uq8!(C;72&q^E#d=&PQ(0d5jr)sY~T@89i~`V$pT>9nAA3B=-9+y&?eFTgVs; zjKV#TXBZ}*r;76f=bMOLzC;JZ!&k3C$ao{^_>L$eyuZKi@B{Sr=;-M1H7uXeXb6C# zckhpfpw~36*L4Zs)jV*ZGRs2ryZ4|EG`$IXAuHYjC{S;T!bn870CI#ALGV2K4iI#G zdqkQVLT^vcj)xy!9n$okVT?}h04$_^u>eSQM04Oj_x&9Uq5G?`=TM{G63}zflpt&P z!)UrR8earqFP%ocUWaWNk&l;`;(QW_f(l@y(0I(UT1PeOqX-mFVqa-YNEn839K`C$ zAfLG_0t3Jcn%S+{nl>8iqwzF~f>{)k-7p>p(Nz=#vrg}R*2r-zo>i|ueHyl__1P$q z&Zkj8cEXU5cou&Su7YS9n+bEjOi+PSZ5tVOsH9JYFlaG@LTsuO-IP~Lb;OmQ&GRSZ+)>PFz>(k+`MZtFrG#!|IKn}n6DffeU;|ZMl3%DY^fU~jF`1YNei$Gv+~WHOee=19 zG4546nx3w;A3*xs`t!v5!Jqcl;rh^|_Nm>qCCP?M5x|l+Wm8MspZI}57_(;AK0iA< zp3M%wU&Jw#EvsBo$oyP+cmd+{oov=1#0;XyI@qbqFI8sff#F*4KZxt>fncjHU6lE) p;HAc(QmI%h8t`=skGuKb>mNB=@4jy!9+dz9002ovPDHLkV1kqJ{~Q1S literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/ok.gif b/app/examples/Games/Concent/imagenes/ok.gif new file mode 100644 index 0000000000000000000000000000000000000000..e3529cad3c9b6fa67f348da6830e742b375bcc2f GIT binary patch literal 1355 zcmW+#e{9o56u))_3u$%GvK8F08&%3SlC3Ob3>J$UGUgy93{nY%!N`QTkP7`{B#qAT z1vXe_jm1o}j3yNFHGCx(ILU=v%;9c$;1RyS7yU|*3^Q>8CK7*5!w(b!HGkxgpYOh!V^&Hy}a{ zOiW4yBP=T*3WvgCL~v|^O^qidW;*AB5Q9^~Xxi2=6PyUb7^9G_I8j-ZlY|N)hRh+t zB;~pxNKR!r<&r>8Dk$YtFlus!p)RL%L4ktgR7xVT0A|ECB%_uf7Gs=K#h{KEh8o2G z7js6j%rPbaOc7y7OqoeJ+c+aG^jJIDpNiV32Py5F(%@ zm+;JiFgTC_)j*sPrAsEUnT7d*Jjs*}3IKx($xPkONG@~>n+%v@i<*|xGx z^Om$Fx5rG&RMt3ofJ^>yO`DC}r~hM$)QH|d`SyS zJ>I>)W9Fv?s|;;k-n45AHus0?&$a%1L$A@k+hgJ=?DxXPqy7@s)^oA0`L`19+{!NOeBr0gJ3a2ASP*%ijB#&4y#J$tm-OnYmYVkQ zN@w0&#`k$<*G3)Azl5);V9f9MApZxm=TPBDYTJdSQ!-=Su1KpHbx#Y$_IW3lE%Gio z!rvJU6Sem8jIaGySD+VLV(+^`$!PTO-=3=@HMb|zFBJ9Yk>*)Pp>4 Rcd07A;J*K$*x^7+{{v}U)Eoc+ literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/imagenes/tierra.gif b/app/examples/Games/Concent/imagenes/tierra.gif new file mode 100644 index 0000000000000000000000000000000000000000..97532a23d4a9b28de6114076ad7b42b2731dfa7b GIT binary patch literal 32485 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/Concent/move.wav b/app/examples/Games/Concent/move.wav new file mode 100644 index 0000000000000000000000000000000000000000..d6d5436d3e7ca871ab44631250cb682ec572daa4 GIT binary patch literal 13318 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/DeepSpace/.lang/ca.mo b/app/examples/Games/DeepSpace/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..22a89354b906947dbd21162d94cc5808659948d0 GIT binary patch literal 758 zcmZXR!EO^V5QbeS6c!<*g2V;HAkcC^-X^I8WkV3CNu>f&QxYn!PS$LUw)SFsL-P`d z3tV{t-hy{PoDjSOXMTsYE5S&=z8P!$k9X(C`szo9I1gQeGUy`o1q$&Kx&r-zBIq}C z23lER>@>IrUIs6Km%tlf@Y(>cf)Bvk(EhTXox)w5zXyZQ2k;#D3A_b<2G_x_VCee> zUI)J~>ow%Zv5Mp@6nsLUW$~uq7v8Xl_{rE*p1M+&!%|EnB}u~Klaj@~Bjs7V_l(8E zsd-0c{6U$>{9iZP>>gil*$p+R01HPN_18JQyI<1FCgFXI2&Qv(& zUEyU)twyuKn=RhFN3G3t^Zs4bjp(^>o{y~1t`IO!>8Y`qqM?|Zf*$6H3(4cTm>QEv z_XzzuuS72SNQ%RhqW*5bSI=)IjcD6wFEvc(C8m0L$KYRH|=P;*`a1Tdc_AuX3F#9x23c# zyeNc@SiD~e?MW2%uUYJcV+obTI=Xkn1YgYJzH#0PDx@Rn)!RyXWEoboT$FU|e=o*a R--Mma7OPR1gDdqc_6PjsyxRZ( literal 0 HcmV?d00001 diff --git a/app/examples/Games/DeepSpace/.lang/ca.po b/app/examples/Games/DeepSpace/.lang/ca.po new file mode 100644 index 00000000..2f860fbc --- /dev/null +++ b/app/examples/Games/DeepSpace/.lang/ca.po @@ -0,0 +1,51 @@ +# Catalan translation of DeepSpace +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the DeepSpace package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: DeepSpace\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-16 23:37+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FMain.class:109 +msgid "&About DeepSpace ..." +msgstr "&Quant al DeepSpace ..." + +#: FAbout.class:31 +msgid "About DeepSpace" +msgstr "Quant al DeepSpace" + +#: .project:1 +msgid "DeepSpace" +msgstr "-" + +#: FMain.class:102 +msgid "&DeepSpace" +msgstr "-" + +#: FMain.class:97 +msgid "DeepSpace Vector Engine" +msgstr "Motor vectorial del DeepSpace" + +#: FMain.class:117 +msgid "&Exit" +msgstr "&Surt" + +#: FAbout.class:41 +msgid "&OK" +msgstr "&D'acord" + +#: FMain.class:105 +msgid "&Show object labels" +msgstr "&Mostra les etiquetes dels objectes" + diff --git a/app/examples/Games/DeepSpace/.lang/cs.mo b/app/examples/Games/DeepSpace/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..433847b601a5201960eec07d11c1bb2c5a879365 GIT binary patch literal 691 zcmZ9I&u-K(5QkkT6jC7&Ab}7PKIMX(!mWgY8Z~Z-nrxQrABo<)S(AoM?IrP+mS=z? z65_@k#6v(|1Hnsh=I7PYppNwIH?tYrqxpG#`2#^*26w?2TmfHzjbGp<_zfKJ2V4M4 zON5+W;9Kw`n1JWt8q)0Cyd*hn`{bh5@Ez2z;Z^wE;{T8EZPcF@ z^$PNTv5e#*u=ChJ%Y3Fwu)TX8ZZOg_LqjR2y{X70^*oQbrzLUuQE7;q?h?1RuisET zdL?s1D>0H)L(Uu~?YYy84$-e`%2iP+NxHSR7qDo|hO%x-t$f-IwswOZPKP|}MQO+B zrYw_(rTxf4VWdxA@E~JJ%EEv?OZ_Cezn6Bn6N|>Mel1i}2|VG`?Li#VPLOcA!IL10 zH-j+Dc(3;qt*V%bLbART2R@C1&d$I-xe>>kRq5%vXnTC>BvHcK`5t-?oGq=4RCw-q zig}DYGHcUHl\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "DeepSpace" +msgstr "-" + +#: FAbout.form:10 +msgid "About DeepSpace" +msgstr "O DeepSpace" + +#: FAbout.form:20 +msgid "&OK" +msgstr "-" + +#: FMain.form:14 +msgid "DeepSpace Vector Engine" +msgstr "DeepSpace vektor motorů" + +#: FMain.form:19 +msgid "&DeepSpace" +msgstr "-" + +#: FMain.form:22 +msgid "&Show object labels" +msgstr "&Zobrazit Å¡títky objektu" + +#: FMain.form:26 +msgid "&About DeepSpace ..." +msgstr "&O DeepSpace ..." + +#: FMain.form:33 +msgid "&Exit" +msgstr "&Ukončit" diff --git a/app/examples/Games/DeepSpace/.lang/de.mo b/app/examples/Games/DeepSpace/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..dd9ac65022bd72245050fdfb12dea95bd5998566 GIT binary patch literal 676 zcmZ9I!EV$r5QbeS6oL>EAaMW*pK?J?;Z{OH4UJo(CYvSMU5Vb3>@;f#&MNUjc?&Mw zI3Uiv1@D32B{=hYwREK&>C@-2$K#QI-QNDf5ZA#2Faf4S>T`Ns&*tNESh^AsfU9Vd0^I>p!5*$lDky#$41810uj6NuP zD8j;!&)>)(6GnqhfL aswSS|r{9^9`fApxGqcb&PB$*2TCjh\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "DeepSpace" +msgstr "-" + +#: FAbout.form:10 +msgid "About DeepSpace" +msgstr "Über DeepSpace" + +#: FAbout.form:20 +msgid "&OK" +msgstr "-" + +#: FMain.form:14 +msgid "DeepSpace Vector Engine" +msgstr "-" + +#: FMain.form:19 +msgid "&DeepSpace" +msgstr "-" + +#: FMain.form:22 +msgid "&Show object labels" +msgstr "&Zeige Objektbeschriftungen" + +#: FMain.form:26 +msgid "&About DeepSpace ..." +msgstr "&Über DeepSpace ..." + +#: FMain.form:33 +msgid "&Exit" +msgstr "&Beenden" diff --git a/app/examples/Games/DeepSpace/.lang/es.mo b/app/examples/Games/DeepSpace/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..062987bed1482af61f986f2d15f3fe39004b4020 GIT binary patch literal 722 zcmaKp!EO^V5QbeS6c!;QK!O7j=CT*GTey{_t!g!HSamlWvPmR3;cU#-bg_fiDdjgZJ?B_(00S>k8;=7ZI|FnLFB%a#qenC21VTBwSue$WN6cVR1;p^2ofW zX7WZ>jy7TSlsvm(jUhcAmB4>HlZXlR9Q8-W%HJs4#f?XjHGZJx6< z+hd*1fS2ViQguBSwTy;RyiI7vdfkz4xt(Qujf#z}cY{35`D&=g@uOhhXeTv#e3)Sf zCr{nxtPx5-rPYzJt#mu1;X$;u%JZ|lmNw$LGE=4Ngl\n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FMain.class:111 +msgid "&About DeepSpace ..." +msgstr "&Acerca de DeepSpace ..." + +#: FAbout.class:33 +msgid "About DeepSpace" +msgstr "Acerca de DeepSpace ..." + +#: .project:1 +msgid "DeepSpace" +msgstr "DeepSpace" + +#: FMain.class:104 +msgid "&DeepSpace" +msgstr "&DeepSpace" + +#: FMain.class:99 +msgid "DeepSpace Vector Engine" +msgstr "DeepSpace Vector Engine" + +#: FMain.class:119 +msgid "&Exit" +msgstr "&Salir" + +#: FAbout.class:43 +msgid "&OK" +msgstr "&OK" + +#: FMain.class:107 +msgid "&Show object labels" +msgstr "&Mostrar etiquetas de los objetos" + diff --git a/app/examples/Games/DeepSpace/.project b/app/examples/Games/DeepSpace/.project new file mode 100644 index 00000000..2a30f7cd --- /dev/null +++ b/app/examples/Games/DeepSpace/.project @@ -0,0 +1,16 @@ +# Gambas Project File 3.0 +Title=DeepSpace +Startup=MMain +Icon=images/deepspace.png +Version=3.11.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +TabSize=4 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Games/DeepSpace/.src/CBullet.class b/app/examples/Games/DeepSpace/.src/CBullet.class new file mode 100644 index 00000000..bdf6bdda --- /dev/null +++ b/app/examples/Games/DeepSpace/.src/CBullet.class @@ -0,0 +1,11 @@ +' Gambas class file +' +' Copyright (C) 2004, Michael Isaac. All rights reserved. +' + +Public X As Float 'Center X +Public Y As Float 'Center Y +Public Direction As Float +Public Owner As String 'The array space of the owning object +Public Damage As Integer +'PUBLIC Destroy AS Boolean diff --git a/app/examples/Games/DeepSpace/.src/CObject.class b/app/examples/Games/DeepSpace/.src/CObject.class new file mode 100644 index 00000000..21f7f12a --- /dev/null +++ b/app/examples/Games/DeepSpace/.src/CObject.class @@ -0,0 +1,87 @@ +' Gambas class file + +' +' Copyright (C) 2004, Michael Isaac. All rights reserved. +' + +Public ID As String + +Public X As Float 'Center X +Public Y As Float 'Center Y + +Public MX As Float 'Motion X +Public MY As Float 'Motion Y + +Public Size As Float + +Public Points As Integer[] +Public Distance As Float[] +Public Degree As Float[] + +Public Direction As Float +Public Agility As Float +Public Acceleration As Float +Public Torque As Float + +Public Hull As Integer +Public Shield As Integer + +Public ShieldOn As Boolean + +Public Thrust As Boolean +Public Attack As Boolean +Public TurnRight As Boolean +Public TurnLeft As Boolean + +Public Sub _new() + Points = New Integer[] + Distance = New Float[] + Degree = New Float[] +End + +Public Sub Load2DObject(sFilename As String, sID As String, X As Integer, Y As Integer) + 'DIM F AS File + Dim I As Integer + Dim sData As String + + Dim aLine As New String[] + + sData = File.Load(Application.Path &/ "object.data/" &/ sFilename) + 'OPEN Application.Path &/ "object.data/" &/ sFilename FOR READ AS #F + 'READ #F, sData, Lof(F) + + 'Split this into an array and remove the CR character + aLine = Split(Replace(sData, Chr$(13), Null), "\n") + + With Me + .X = X + .Y = Y + .ID = sID + + If sID Like "Object*" Then + .Torque = -1 + '.Attack = TRUE + .MX = Rnd(-4, +5) + .MY = Rnd(-4, +5) + End If + + .Agility = 5 + .Acceleration = 0.75 + .Hull = 100 + + .Direction = Rad(180) + + For I = 0 To aLine.Count - 1 + If (Not (Left$(aLine[I], 1) = "'")) And (Not (aLine[I] = "")) Then + .Degree.Add(CFloat(Split(aLine[I], ",")[1])) + .Distance.Add(CFloat(Split(aLine[I], ",")[0])) + + If .Distance[.Distance.Count - 1] > .Size Then + .Size = .Distance[.Distance.Count - 1] + End If + End If + Next + End With + + 'CLOSE #F +End diff --git a/app/examples/Games/DeepSpace/.src/FAbout.class b/app/examples/Games/DeepSpace/.src/FAbout.class new file mode 100644 index 00000000..a5f4fa0c --- /dev/null +++ b/app/examples/Games/DeepSpace/.src/FAbout.class @@ -0,0 +1,19 @@ +' Gambas class file + +' +' Copyright (C) 2004, Michael Isaac. All rights reserved. +' + +Public Sub cmdOK_Click() + Me.Close() +End + +Public Sub Form_Open() + + Me.Center + + lblInfo.Text = + "DeepSpace  " & Application.Version & "
" + "A vector engine written in Gambas.

Contact subjugator@gmail.com with questions.
" + "Copyright (C) 2004, Michael Isaac. All rights reserved." +End diff --git a/app/examples/Games/DeepSpace/.src/FAbout.form b/app/examples/Games/DeepSpace/.src/FAbout.form new file mode 100644 index 00000000..5d9934a7 --- /dev/null +++ b/app/examples/Games/DeepSpace/.src/FAbout.form @@ -0,0 +1,19 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(51.6667,32.1667,54,26) + Text = ("About DeepSpace") + Resizable = False + { lblInfo TextLabel + MoveScaled(1,1,35,19) + Text = ("") + } + { cmdOK Button + MoveScaled(19,21,13,4) + Text = ("&OK") + } + { pbIcon PictureBox + MoveScaled(36,1,17,16) + Picture = Picture["images/deepspace.png"] + } +} diff --git a/app/examples/Games/DeepSpace/.src/FMain.class b/app/examples/Games/DeepSpace/.src/FMain.class new file mode 100644 index 00000000..f685e24a --- /dev/null +++ b/app/examples/Games/DeepSpace/.src/FMain.class @@ -0,0 +1,81 @@ +' Gambas class file + +' +' Copyright (C) 2004, Michael Isaac. All rights reserved. +' + +Private Attacking As Boolean + +Public Sub _new() + mMain.Canvas = daCanvas +End + +Public Sub tmrMainLoop_Timer() + mMain.MainLoop() +End + +Public Sub Form_KeyPress() + + Select Case Key.Code + Case Key.Left + MMain.Obj[0].TurnLeft = True + Case Key.Right + MMain.Obj[0].TurnRight = True + Case Key.Up + MMain.Obj[0].Thrust = True + Case Key["F"] + MMain.Obj[0].Attack = True + Case Key["D"] + MMain.Obj[0].MX = 0 + MMain.Obj[0].MY = 0 + End Select +End + +Public Sub Form_KeyRelease() + + Select Case Key.Code + Case Key.Left + MMain.Obj[0].TurnLeft = False + Case Key.Right + MMain.Obj[0].TurnRight = False + Case Key.Up + MMain.Obj[0].Thrust = False + Case Key["F"] + MMain.Obj[0].Attack = False + End Select +End + +Public Sub Form_Close() + mMain.Exit() +End + +Public Sub Form_Resize() + mMain.SCREEN_WIDTH = Me.ClientW + mMain.SCREEN_HEIGHT = Me.ClientH + + daCanvas.H = Me.ClientH + daCanvas.W = Me.ClientW +End + +Public Sub daCanvas_KeyPress() + Form_KeyPress() +End + +Public Sub daCanvas_KeyRelease() + Form_KeyRelease() +End + +Public Sub mnuMainExit_Click() + Me.Delete +End + +Public Sub mnuMainAbout_Click() + FAbout.Show() +End + +Public Sub mnuShowText_Click() + + mnuShowText.Checked = Not mnuShowText.Checked + MMain.ShowObjectLabel = mnuShowText.Checked + +End diff --git a/app/examples/Games/DeepSpace/.src/FMain.form b/app/examples/Games/DeepSpace/.src/FMain.form new file mode 100644 index 00000000..761e7034 --- /dev/null +++ b/app/examples/Games/DeepSpace/.src/FMain.form @@ -0,0 +1,35 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(19,0,75.2857,70.7143) + Text = ("DeepSpace Vector Engine") + Icon = Picture["images/deepspace.png"] + Resizable = False + { mnuMain Menu + Text = ("&DeepSpace") + { mnuShowText Menu + Text = ("&Show object labels") + } + { mnuMainAbout Menu + Text = ("&About DeepSpace ...") + } + { mnuMainSep1 Menu + } + { mnuMainExit Menu + Text = ("&Exit") + Shortcut = "Ctrl+Q" + } + } + { daCanvas DrawingArea + MoveScaled(0,0,54,50) + Font = Font["Monospace"] + Background = &H000000& + Cached = True + Focus = True + } + { tmrMainLoop #Timer + #MoveScaled(65,43) + Enabled = True + Delay = 20 + } +} diff --git a/app/examples/Games/DeepSpace/.src/MMain.module b/app/examples/Games/DeepSpace/.src/MMain.module new file mode 100644 index 00000000..0835022d --- /dev/null +++ b/app/examples/Games/DeepSpace/.src/MMain.module @@ -0,0 +1,348 @@ +' Gambas module file + +' +' Copyright (C) 2004, Michael Isaac. All rights reserved. +' + +Public Obj As Object[] +Public Bullet As Object[] + +Public Canvas As DrawingArea + +Public BULLET_SIZE As Float +Public BULLET_SPEED As Float + +Public KEY_LEFT As Boolean +Public KEY_RIGHT As Boolean +Public KEY_UP As Boolean +Public KEY_DOWN As Boolean + +Public KEY_FIRE As Boolean +Public KEY_FIRESTATE As Boolean + +Public SCREEN_WIDTH As Integer +Public SCREEN_HEIGHT As Integer + +Public BOT_SPACE As Integer 'Which Obj[] is the bot + +Public FPS_TIME As Float +Public FPS_COUNTER As Integer +Public FPS_COUNT As Integer + +Public ShowObjectLabel As Boolean + +Public Sub Main() + + System.Profile = False + MMath.InitializeSineTable() + ShowObjectLabel = False + + Obj = New Object[] + Bullet = New Object[] + + FPS_TIME = Timer + + BULLET_SIZE = 7 + BULLET_SPEED = 15 + + SCREEN_WIDTH = FMain.ClientW + SCREEN_HEIGHT = FMain.ClientH + + LoadObjectList() + + FMain.Show() +End + +Public Sub Exit() + Obj = Null + Bullet = Null + Canvas = Null +End + +Sub LoadObjectList() + Dim I As Integer + Dim J As Integer + Dim sData As String + Dim tmpO As CObject + + 'DIM F AS File + + Dim V As New String[] + Dim aLine As New String[] + + sData = File.Load(Application.Path &/ "object.data/main.lst") + + 'OPEN Application.Path &/ "object.data/main.lst" FOR READ AS #F + 'READ #F, sData, Lof(F) + + 'Split this into an array and remove the CR character + aLine = Split(Replace(sData, Chr$(13), Null), "\n") + + For I = 0 To aLine.Count - 1 + If (Not (Left$(aLine[I], 1) = "'")) And (Not (aLine[I] = "")) Then + V = Split(aLine[I], ",") + If V.Count = 4 Then + For j = 1 To If(v[1] = "Object1", 100, 1) + tmpO = New CObject + tmpO.Load2DObject(V[0], V[1], CInt(V[2]), CInt(V[3])) + Obj.Add(tmpO) + Next + End If + End If + Next + + 'CLOSE #F +End + +Sub ApplyPhysics() + Dim I As Integer + Dim U As Integer + Dim Ob As CObject + + If Obj = Null Then Return + + For Each Ob In Obj + With Ob + .Direction = .Direction + Rad(.Torque) + + If .TurnLeft = True Then .Direction = .Direction + Rad(.Agility) + If .TurnRight = True Then .Direction = .Direction - Rad(.Agility) + + If .Direction > Rad(360) Then .Direction = Rad(0) + If .Direction < Rad(0) Then .Direction = Rad(360) + + If .Thrust = True Then + .MX = .MX + ((Sin(.Direction)) * .Acceleration) + .MY = .MY + ((Cos(.Direction)) * .Acceleration) + End If + + If (.MX <> 0) Or (.MY <> 0) Then + .X = .X + (.MX / 20) + .Y = .Y + (.MY / 20) + End If + + If .Attack Then + AddBullet(CInt(.X), CInt(.Y), .Direction, .ID, 10) + End If + End With + Next +End + +Function IsObjectCollision(O1 As CObject, O2 As CObject) As Boolean + If GetDistance(O1.X, O1.Y, O2.X, O2.Y) <= O2.Size Then + Return True + End If +End Function + +Function IsBulletCollision(O As CObject, B As CBullet) As Boolean + If GetDistance(O.X, O.Y, B.X, B.Y) <= O.Size Then + Return True + End If +End Function + +Sub CollisionHandler() + + Dim I As Integer + Dim J As Integer + Dim hBullet As CBullet + Dim hObj As CObject + + If Obj = Null Then Return + If Bullet = Null Then Return + + For I = 0 To Obj.Count - 1 + For J = 0 To Bullet.Count - 1 + + If J > Bullet.Count - 1 Then Break + If I > Obj.Count - 1 Then Break + + hObj = Obj[I] + hBullet = Bullet[J] + + If Not (hBullet.Owner = hObj.ID) Then + If IsBulletCollision(hObj, hBullet) Then + + hObj.Hull = hObj.Hull - hBullet.Damage + Bullet.Remove(J) + + If hObj.Hull <= 0 Then + If I = 0 Then + 'Message("Player was just killed.") + Else + Obj.Remove(I) + End If + End If + End If + End If + Next + Next + +' FOR J = 0 TO (Obj.Count - 1) +' FOR I = 0 TO (Obj.Count - 1) +' IF I <> J THEN +' IF IsObjectCollision(Obj[J], Obj[I]) THEN +' 'This is where Objects collide, and we need +' 'calculate forces here. +' END IF +' END IF +' NEXT +' NEXT +End + +Public Sub AddBullet(CX As Integer, CY As Integer, D As Float, OwnMe As String, Dmg As Integer) + Dim B As New CBullet + + With B + .X = CX + .Y = CY + .Direction = D + .Owner = OwnMe + .Damage = Dmg + End With + + Bullet.Add(B) +End + +Sub MoveBullets() + Dim I As Integer + + If Bullet = Null Then Return + + For I = 0 To Bullet.Count - 1 + If I > Bullet.Count - 1 Then Break + + With Bullet[I] + .X = .X + ((Sin(.Direction)) * BULLET_SPEED) + .Y = .Y + ((Cos(.Direction)) * BULLET_SPEED) + End With + + If ((Bullet[I].X > SCREEN_WIDTH) Or (Bullet[I].X < 0)) Or ((Bullet[I].Y > SCREEN_HEIGHT) Or (Bullet[I].Y < 0)) Then + Bullet.Remove(I) + End If + Next +End + +Sub RenderBullets() + Dim B As CBullet + + Draw.Foreground = Color.Cyan + Draw.FillColor = Color.Blue + Draw.FillStyle = 1 + + Randomize + + For Each B In Bullet + Draw.Ellipse(B.X, B.Y, BULLET_SIZE, BULLET_SIZE) + Next +End + +Public Sub RenderObjects() + Dim I, J As Integer + Dim Ob As CObject + Dim Size As Integer + Dim aPoint As Integer[] + Dim A, D As Float + Dim Tag As String + + Draw.Foreground = Color.Cyan + For Each Ob In Obj + With Ob + Size = .Degree.Count + + aPoint = .Points + + aPoint.Resize(Size * 2) + + J = 0 + For I = 0 To Size - 1 + A = .Direction + Rad(.Degree[I]) + D = .Distance[I] + aPoint[J] = .X + (Sin(A) * D) + Inc J + aPoint[J] = .Y + (Cos(A) * D) + Inc J + Next + + Draw.Polygon(aPoint) + End With + Next + + If ShowObjectLabel Then + Draw.Foreground = Color.Green + For Each Ob In Obj + With Ob + Tag = .ID & " " & .Hull & "%" + Draw.Text(Tag, .X - (Draw.TextWidth(Tag) / 2), .Y - (35)) + End With + Next + Endif +End + +Sub CheckObjectWarp() + Dim Ob As CObject + + For Each Ob In Obj + With Ob + If .Y > SCREEN_HEIGHT Then .Y = 0 + If .Y < 0 Then .Y = SCREEN_HEIGHT + + If .X > SCREEN_WIDTH Then .X = 0 + If .X < 0 Then .X = SCREEN_WIDTH + End With + Next +End + +Public Sub MainLoop() + + Dim eTime As Float + + ApplyPhysics() + CollisionHandler() + + MoveBullets() + + If Canvas = Null Then Return + + CheckObjectWarp() + + 'Canvas.Clear() + + Draw.Begin(Canvas) + Draw.FillRect(0, 0, Draw.W, Draw.H, Draw.Background) + + eTime = Timer + If (eTime - FPS_TIME) > 1 Then + FPS_TIME = eTime + FPS_COUNT = FPS_COUNTER + FPS_COUNTER = 0 + Else + Inc FPS_COUNTER + Endif + + RenderObjects() + RenderBullets() + + Draw.Foreground = Color.Red + + Draw.Font.Bold = True + Draw.Text("Object Count: " & Obj.Count, 1, 0) + Draw.Text("Bullet Count: " & Bullet.Count, 1, Draw.Font.Height * 1) + Draw.Text("FPS : " & FPS_COUNT, 1, Draw.Font.Height * 2) + + Draw.End() +End + +Function GetDistance(X1 As Float, Y1 As Float, X2 As Float, Y2 As Float) As Float + 'DIM A AS Integer + 'DIM B AS Integer + + 'A = (X1 - X2) + 'B = (Y1 - Y2) + + 'I'm not sure about gambas, but in VB the ^ operator + 'makes calculations we dont really need. This might speed up + 'the process. And we need as much speed as we can get. + + 'RETURN Sqr((A * A) + (B * B)) + Return Hyp(X1 - X2, Y1 - Y2) +End diff --git a/app/examples/Games/DeepSpace/.src/MMath.module b/app/examples/Games/DeepSpace/.src/MMath.module new file mode 100644 index 00000000..32348369 --- /dev/null +++ b/app/examples/Games/DeepSpace/.src/MMath.module @@ -0,0 +1,64 @@ +' Gambas module file + +' +' Copyright (C) 2004, Michael Isaac. All rights reserved. +' + +'PUBLIC CONST Radian AS Float = 0.01745329252 + +Public SineArray As New Float[] +Public CoSineArray As New Float[] + +Public Sub InitializeSineTable() + Dim I As Integer + + 'We create the cosine/sine array so we dont have to call + 'a function which had to calculate a value everytime we call it. + 'These calls are made once here, and then from now we can just + 'point at the value we want. Should be much faster this way. + + For I = 0 To 359 + SineArray.Add(Sin(I * (Pi / 180))) + CoSineArray.Add(Cos(I * (Pi / 180))) + Next +End + + +Function GetDistance(X1 As Float, Y1 As Float, X2 As Float, Y2 As Float) As Float + 'RETURN Sqr((X1 - X2) ^ 2 + (Y1 - Y2) ^ 2) + Return Hyp(X1 - X2, Y1 - Y2) +End + + +' FUNCTION CosE(A AS Float) AS Float +' DIM Angle AS Integer +' +' 'Convert our radian angle to a degree value +' Angle = CInt(A * 180 / Pi) +' +' 'Ensure we're between 0 and 359 +' DO WHILE ((Angle < 0) OR (Angle > 359)) +' IF Angle > 359 THEN Angle = Angle - 360 +' IF Angle < 0 THEN Angle = Angle + 360 +' LOOP +' +' 'Return the value +' RETURN CoSineArray[Angle] +' END +' +' +' FUNCTION SinE(A AS Float) AS Float +' DIM Angle AS Integer +' +' 'Convert our radian angle to a degree value +' Angle = CInt(A * (180 / Pi)) +' +' 'Ensure we're between 0 and 359 +' DO WHILE ((Angle < 0) OR (Angle > 359)) +' IF (Angle > 359) THEN Angle = Angle - 360 +' IF (Angle < 0) THEN Angle = Angle + 360 +' LOOP +' +' 'Return the value +' RETURN SineArray[Angle] +' END diff --git a/app/examples/Games/DeepSpace/doc/.html_files/eg3.gif b/app/examples/Games/DeepSpace/doc/.html_files/eg3.gif new file mode 100644 index 0000000000000000000000000000000000000000..afdb4e80a294d9f3ec99e8995581727e23f8f5a9 GIT binary patch literal 2782 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 + + + Polar Coordinates + + + +
+ + + + + + +
+
Polar +Coordinates
+ converting +(r, d) to (x, y)
+
+
+
+ + + + + + +

+
+
Here's a +point shown using polar +coordinates.
+ It would be labeled (r, d) + or (4, 300°) for this point.
+

+
+
+
+
+
+
+
+ + + + + + + +
+
+
The +point (x, y) +can be located by finding the horizontal and vertical components +of r.  We can do that by +substituting the radius and the degree + (r, d) into the +following formulas.
+
+
+
+
+
x += r * cos(d)
+ y = r * +sin(d)
+
+
+
So we end up with;
+
+
+
+
x += 4 * cos(300) =     2
+y = 4 * sin(300) = -3.46
+
+
+
As we can +see, the +point (4, 300°) +in +polar coordinates is equivalent to a point with the rectangular +coordinates (2, -3.46)
+
+

+
+

+x = r * cos(d)

+ y = r * +sin(d)
+
+
+
+
+
+ + + + + + +
Content by Michael Isaac, 2004
+
Original Graphics, & +Design by Bill Willis 2001
+
+
+
+
+
+
+ + diff --git a/app/examples/Games/DeepSpace/doc/howto.txt b/app/examples/Games/DeepSpace/doc/howto.txt new file mode 100644 index 00000000..7b77c1ba --- /dev/null +++ b/app/examples/Games/DeepSpace/doc/howto.txt @@ -0,0 +1,16 @@ + + +HOWTO make objects float around the screen + + set the Torque property of a CObject which will determine its + spin. and then set its motion path, which is defined by its + MX and MY properties. + +HOWTO make objects fire + + this is useful only if you plan to write bot ai. + you can use the Attack property to tell an object to release a + bullet. One bullet will be released every cycle of the mainloop + so make sure you set Attack to false or you will get auto fire + like the ship has. + diff --git a/app/examples/Games/DeepSpace/doc/todo.txt b/app/examples/Games/DeepSpace/doc/todo.txt new file mode 100644 index 00000000..b10d6d4e --- /dev/null +++ b/app/examples/Games/DeepSpace/doc/todo.txt @@ -0,0 +1,17 @@ + +Things that need to be finished or started. + + - object collision. i removed the old object collision cause it + sucked. new collision code needs to be written which includes + elastic collisions so asteroids that bump eachother or the ship + will around + + - some kind of asteroids style game as an example of what + this little engine is for + + - write bot ai, so there is an opponent for everyone to fight + + - single bullet fire mode, not full on automatic + - network play mode, where players can fight head to head + - more stuff i havent thought of + diff --git a/app/examples/Games/DeepSpace/images/deepspace.png b/app/examples/Games/DeepSpace/images/deepspace.png new file mode 100644 index 0000000000000000000000000000000000000000..cb4454f5e59f283251010558593f3a4d2750973a GIT binary patch literal 14195 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/DeepSpace/object.data/kite.2do b/app/examples/Games/DeepSpace/object.data/kite.2do new file mode 100644 index 00000000..02cee166 --- /dev/null +++ b/app/examples/Games/DeepSpace/object.data/kite.2do @@ -0,0 +1,8 @@ +' Generated with the DeepSpace Polygon Factory. + +11,5 +12,85 +20,177 +20,183 +12,275 +11,355 diff --git a/app/examples/Games/DeepSpace/object.data/main.lst b/app/examples/Games/DeepSpace/object.data/main.lst new file mode 100644 index 00000000..110a7135 --- /dev/null +++ b/app/examples/Games/DeepSpace/object.data/main.lst @@ -0,0 +1,12 @@ +' +' Object List File. +' +' This file is used to tell the game which files to load on start up. +' The first object is the player object. +' +' data_file.2do,ID,X,Y +' + +ship.2do,Subjugator,320,240 +triangle.2do,Object1,200,100 +triangle.2do,Object2,300,100 diff --git a/app/examples/Games/DeepSpace/object.data/ship.2do b/app/examples/Games/DeepSpace/object.data/ship.2do new file mode 100644 index 00000000..8615f9e8 --- /dev/null +++ b/app/examples/Games/DeepSpace/object.data/ship.2do @@ -0,0 +1,13 @@ +' +' All ordered pairs are Polar Coordinates (Radius, Degree) +' See doc/coordinates.html for more information +' + +8,000 +8,120 +20,040 +15,140 +20,180 +15,220 +20,320 +8,240 diff --git a/app/examples/Games/DeepSpace/object.data/triangle.2do b/app/examples/Games/DeepSpace/object.data/triangle.2do new file mode 100644 index 00000000..85c2fc8d --- /dev/null +++ b/app/examples/Games/DeepSpace/object.data/triangle.2do @@ -0,0 +1,8 @@ +' +' All ordered pairs are Polar Coordinates (Radius, Degree) +' See doc/coordinates.html for more information +' + +30,000 +30,120 +30,240 diff --git a/app/examples/Games/DeepSpace/object.data/x-wing.2do b/app/examples/Games/DeepSpace/object.data/x-wing.2do new file mode 100644 index 00000000..f777e8d2 --- /dev/null +++ b/app/examples/Games/DeepSpace/object.data/x-wing.2do @@ -0,0 +1,38 @@ +' +' All ordered pairs are Polar Coordinates (Radius, Degree) +' See doc/coordinates.html for more information +' + +18,3 +16,10 +16,7 +6,38 +5,53 +15,82 +16,79 +18,60 +19,59 +22,50 +19,59 +20,63 +18,96 +17,100 +16,97 +6,116 +6,141 +6,218 +6,243 +16,262 +17,259 +18,263 +20,296 +19,300 +22,309 +19,300 +18,299 +16,280 +15,277 +5,306 +16,349 +16,352 +18,356 diff --git a/app/examples/Games/GNUBoxWorld/.directory b/app/examples/Games/GNUBoxWorld/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Games/GNUBoxWorld/.hidden/screenshots/2014-12-14.png b/app/examples/Games/GNUBoxWorld/.hidden/screenshots/2014-12-14.png new file mode 100644 index 0000000000000000000000000000000000000000..c5b1b7648619c004bc0b3a08fc1f2fdf2e08382f GIT binary patch literal 185109 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�- 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

7=SStZ1=X?AZb9iC858}c}1<{7*u#UE&kVJ@C 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�Ai9aA*8b$jtFEnA_e-HG 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 z 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�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

P~ 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/icon.png b/app/examples/Games/GNUBoxWorld/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d1737db0f210850e690a3280d53d731ac058d6c6 GIT binary patch literal 5889 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 Xbp(Gj=?hmn00000NkvXXu0mjf2*@sw literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/izquierda.png b/app/examples/Games/GNUBoxWorld/izquierda.png new file mode 100644 index 0000000000000000000000000000000000000000..4424149335a9c5e67a7b5b1dcd352f916fd5aa60 GIT binary patch literal 4350 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/logo.png b/app/examples/Games/GNUBoxWorld/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1124d398c176adb4b36132f69d04bf00fb5d66a0 GIT binary patch literal 108170 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∋|_EhT@74dls>cXL(_ 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/movible.png b/app/examples/Games/GNUBoxWorld/movible.png new file mode 100644 index 0000000000000000000000000000000000000000..7d63ed940c23180fe9e1fed18c201cc1ca8dcebf GIT binary patch literal 3831 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/obstaculo-r.png b/app/examples/Games/GNUBoxWorld/obstaculo-r.png new file mode 100644 index 0000000000000000000000000000000000000000..fda4df00bd665740b2a2df6a6166c580bfd9047b GIT binary patch literal 1627 zcmb`Ic~H`69L6cT#@2KQNHsI8>i-sYPc@{zhIh+I!Ai?2eID!m^lMo0J0!~K26H%x{ zG&&K5rlHV@XcP^FibEoIZNsPH@u?9JsrZQ02>dQkc>HcWXd;ra*w`RsI+>DAp`=r( z05~bsbSfo{NKC?FVv$H18qME9=hNxgbUKd?nw|UNQy03c*AlmdZLC{zjsN`bIUC@2#Oi}`$Rd_rtckXj;9 zOC@TFR4tWOip3>-o&?~q355F6()vUp*UVzdRzfYp_j`m<`{lH0HhI!$r!L? z&|V5f#AKbWu5JcZ1FGgsHK^*7s&c&y@$IEqH{y*wj zkL&sF`oxY?EHind58?AmquP%iS4d@iNy3-VIBK?0iG-Vp@9I%_|H)-Lk&Jv^l>u4PdJFCx2{*C)n?7 z00Wiq?>0?NRnUthtS_lwfTNbD8-87ZE0}vk4{7+_rSR(M+o_kQu$P~uZxJB8VM?oQ zprX^9)P|<{*kS951HCU8+nu8@%9n=w_pHpt;|TRa`%_$+o1?5I&!F_ooA!*3(jnK! zxR2aP)^sawvs}xm*{>(h(*RY8Sov% z~C>B488fB+r<1z8kx+7 z;-dQl`!rs44o?I7@|Y8V7eh!2nrzR@hOW)HbpA=>{p+odkM%wjIX2~Yc8mf1OD;=0 zgO^JUkGz|$n}d5k>#rbWt!@v8_NIJ=lD}FrJcex=)IqY(kk_~D!-R)21{WXO60&*R zhUmsGjGhg-w|K`pQn16syqi^S1%E@CZ~lRF>M79)dqU3)NP6wgT{UP^=J$W#R}d3m kh5v5fFxmL89-n7|TVo#NFrM0-0Y420E<7r%8NE~d7fVJ3mH+?% literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/obstaculo.png b/app/examples/Games/GNUBoxWorld/obstaculo.png new file mode 100644 index 0000000000000000000000000000000000000000..8ee35f44bf47b01b373908189a1a435b0118e8cf GIT binary patch literal 1575 zcmchY|2NZn9LLwKi|UHhJ$2osBIy#Uiw(2d&e~$q#-^Fgkg&0dEnC{KVY`}a!+hNs zvhtnHx2{sI%83dk^hITEb@C+?CrNcb{S)_`$LsZe{_s4{*X#Y$D--93G2LLf0R#e> zdLKji1MiU^WwI9N>UQA_2(+dj=Sw&m0C5X|KnM^h0RjntLIa=>0u(|;AgD+r6@jE6 zkW?gsfID9x7SkmhZktmF& z*B2Kz0F(ffG!_FC*C_MLrGQ^3;{vnDF?d`U8lx{O(`(A~Wg1;+X*dS0F9mdIjZ&$} zk`@Yu*}!xri-E^!vaO`O0dgQpV+Kq*>WqE{7akC=wP3MHD0=gT>5XGWn-Yae~QgauDML z?xZJ-Vt#S~1X{Pv8-*Z{dX)KJxRyt^rp_UIL@rK)_8r=Tl~+=mQ#pTd5DqeVL~ff! zh<&qdM(VgZ3cJ?-czEmMJ8sO@sj_MHYp^UTx#-WHnR_=OlB*v@W=C{SY`r~fp1j#3 zp;j=z{N`QE9&2K@KkInzL@C!z2EUvq9^siIV)qyYK;MZ4St*L z=g$4+T2`^os&VdYqhTp&z~LK6mNs5}t-L0#W@AlKcVA1BJ(0?f$()Cdy$BrUisp>y zNzU6>Q@$qe1Mh}yN-Q{Gn%uNHacFPtvNJVH9KHaO^{8foO`KBlIxg9|I9Ai2R-q16 zQ@iTu)tr4`GazUDf+c07fikq@iyla>i_!&AzoSMQwH0M|tlh1Q?@r0@{k!q@s!tb5 zL)=Z9{^(|JF1n-SZ1+rX+2sDd!(S1B3^@{3`fT z?X^SGgL9wWFD1R{9@}sbR`fQ$=`P#@=@u88@^Wryt~a7h`>D}@2$F5Fl3dmIS*s`e zTKB3Ab&}6L4@P9$4Poi?P;ihmZ#aIP!>#jURdH>1dTXJ`IzOI@Rrzap_j2ZgY^TlH z2ezA?ti{9VYpf(Tms|Ua4Cvg3!MZK)KQcXfNve71H|wAsi>ou5dqxM1J%eYBc@GWg zz3-eY5|=+0NYIa5b{5?zb}2DFt+Fvy%tMbkB?Y-$=T)8^WSEj~m>Q*xO!ihjTz|cR zXW_02+3Rpi`5~HORgkjt_~q9_9GIh55ccrJv+HG()OiEkZZWL7#T5n@xLr$Gnb|r$ z>Errzy|XJ19P2c#YjLQxvs3zXCiv6a9h7#3^@2^n|Gaw5owfdt8B^U-MjwMP8WQ@o zH}-dTuvrdwcVRZV@?Erty3J=r-1hyK`V%Iy9z}mGe3la;_E6*O(rrh|3j!a1t{pK2 z79a5HY#!gPJpk`)?jg;xps@mf&(%&W54dheW74Jy s_{{|Kr8W6!SInBgC_(D$|HHCM)1nL`gb$0)fdc^YKI(^RK!%F`1JGmal>h($ literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/piso.png b/app/examples/Games/GNUBoxWorld/piso.png new file mode 100644 index 0000000000000000000000000000000000000000..e35e5a442aaca2d400a5e038c3235126e476f16f GIT binary patch literal 10046 zcmW-ndpy(q`^U?v$@!3mAtC1wYSo-`TB+!EHnrriL@lQcIpy4%!@V3vlk-hXIp>^V zWkeP$VpvFXNF!%{pYQLFZJ)QPaQd;%4>VY>>tdyWvgqA&%^MkjfgTsk=6R7%AK^AD~->2Y?8semxC`) z+R9GY-BYzMImH7?;WoB>!lQguO6p=vj98o!p(Zx|NK*asp^Ibd{ndTEKZ()*o)e1x zr*dR!gWNFXJGuD;!B~}wUeh_4b6^PB-O{&{FcWof;tg>O3JQ8An(3W;5kj)fJYk1; z>-O+J_Hin>58bojF^@Gb+Wx)O<bl=J$>x< zK@J_2w#w^Qz2$t!yj?r>AejQLmV+>6_9^b3?K?PLmwD+N+kb^Db3ca|MTzR#DRF&- zi7ahKG`7mQ+2YH&vGy79VrG)8ck(Q#u8zbn-{@77sQtb59-jz`!S#F{>-rJ3(wzAe z+IIZHG0+|Gv3b-LR8paWsJZTSCDO?^r!Y}b%DB_G(ElcmGX-?<&oVR7kPNuqa|zO_ z{itEng|MErRN5Sl<@X2X+xqQs&@LCX*}yKr;W(?gYQ)ers?6V?Fj5~tS;O3EpL*3$ zxavNS{qefyAa(boj*HJD9OZt4Py4uNuU^2!aN`MqS15c#o2l!(^4jrg z!+?pAFG4mh^9h{o7|GO=IwofD_XodmN5)FKZ zYIkdGea}X+SFPG5EB9)YPwTfgAirOU$R+-#$t`4;EZ(7wn-0bGeP}_7uI|t)LZWve zO7>GWcOSU1)x8Y$YW3I=!k4z_!z^s>@4Xn^WHV47Srt+3*DA%rs8wSk4^73(F=nQc zR#kLExcWTG%70K-SW7?+1uoC1ta=2=MuV}KEqqzUnn#*i>Y9-PHAL;T_{OfuVNy*} z#X{tzKfmTQyVVNGn!hT$H@Q8(x1auPG-A*Tws4ecCO;h$77!pU&>R2e(Kv!bLF(+| zM~Rpqu|x`@S#>oLbw(ftCF8eJPMXE}64^?=U)Gc-%}qVdr0y1K%1k zE~dx_4n$GKO|y2X8!BW|Dr!E`_;SB*KZeXcVlyk5l|X*@;dMT3t~>j7WC{xt{DcO@ z1qVEr%v8gt6oXh$AU9(`pmWRly&y0KB}A^W^2+hSsKfY$mS%Di8X|Qxo1Tl3M?a_S zZmxn8rMv{mY@iJGztS7D3!eOu2C1b&j#LW?h+6AWIV3dXj;pA7QPcs)B-}{QC|1FN zVQ9e==Tqcxb)1}ro^=f^R6CbgXrZ34%8MaeBeLu5p0iuNGp8gH#i%TK6(KUq;e-mB zD4rZcl1-R?kDfJl+&~b!RZ%+`Hg5oslFxf?F z_Fix8wcr36?$qzdBDMKU#Mi*aEi;>m(QB-0SucnCkfbw&kv8nEb_nA1!P1SxyC@>D zXdtBO2#oL#;ka>Wd{ zaFNFz&K&KrHAjqk{1NDo9WgL;;NmLMEv-D>QaJb6xR@~i_pdwy{a>EUMqR=Fd$w+} z4PCNOxtx`umb(0}^Pim_Ww`(KT_y7BP05RH{$(gG9!J;0=fDYEx1iB>vx6z7?hKm> zq-V~2teW8GaU{_<5!uBtsvof_{h36X$h!<LUcSROCw%O+Hh#@-JQbV&EikNPG1Z&&?w)V=sV)vzuTy zGZ!8d(ZNZ@(eYccrg9%Tr!N*Qol=}*F!}f+-UII&FK!SrxWBI%$+Y&jaFYDzg?2nV zO|8O)Zb%uX|I^qmQ@xl(DrBP(1>^wNtQ4$NTU)16s`%M&yv0He8ass+@Pvi!zpIF= zSqdo>$zsEyp)_T5_sN38Bg^2*%96ACMbP&lJRy#SgfsYC-D-Cs);m$As%yv9RaU1W zlN9)>Muezz{SZeloE0dA)uN1!(>v=L^*-o{(?`Byh|_BVG9S8BaObF(N4-Pyr!J^4 zhSdJ9P~m6NQ5JaKyD_2GA2pHH_5rjPGkV{S-q02usf-to)G4WaTszynydpm&g5k`| zo@dJ;!5J;GW!SsXm_ANqx9$vPyw3F@8lgaK{>L$PHA&&Nqq^Y}W-)=w=gaE;l#Zu0 z(p)s&hTkDZoT%)oXD1PI%L`}0+-zJ{v&(v!E4|j9jv_7Y%Cnk-1Gpaphc?pWRi%b; zB!)V;B1PQ*C&^bB0A*C6nW?M-GA9rQINh}%2(?#FIEhZe%G*j@{%BjR_9uTikh+K| zVat}>gyNT%&5sKwSvO9&3n%&Zy``dI#Iiz#%M)Wy(_6?n3mGib&_cnJ*sX==gHCM( zGYAtk-%E48#<>#pF_|u@qD=M&m*)Zp`Adi$IPt-lyXnmfC+JYU+nV)bGU_MXG^aZD z<*Ch87u@H)%h+y_IzJwTmsH3&y*5O*6=?+(sUbTL?<8piOt>)`N>FfnAA=BDVA$1( z^B6KxEVFC#XA6n7W|~P}oRBgBT`Um3MjP;HXXQOSAnM|pTU*P?{y@lm8ob**KZ{6s zJHDc#%{3{ZVHU10Fr*uziS+@`SGNt8yJj%TDbDNrrW0e==*I&f?&$;9=;y(#N5%!N zHO|2Q%gb_UX$s&(`BEO&(0T0PhlhJVn{wt88mnEiq_YyV>bT+H(tF=>tDq9wdUS)j ziItzT8JulXNZA)VOVY`uz)+7*((J*kea^8y)Ln{sW*LL{dA7yap1>u5l+?#5IlybR ztZkpK?))VC|4fR7!qsVUuujnge#)>?`uC+qn+l-iOs?=T(9(URN-g{T#@eC}%focT z_?fHM7xWkJKMyZm9M6J!&V(Nw8d6Yuor$fL_@TS*XU&2q0pY(!?+R@yHVJvqc%d18 zMn|*fbaHn*XRgPmwNRF?=+s@(($nvg-xU+?5v~93`UXB+UHcy6T;sX6zAiu1S_-|b zi>JT_Awv-@9SbXeA0PhwWbs%@qrYj|zcIoeV*(Jx?up9+mP8whXr zenAFNa%#157E)le=6k+4r{E{lk(2CR| z*h{|swv=3y!$PL*h#c%QvCG)AuSbb_d9pax+X0Cqj;^!E#(wj+IGvA~?QZYgQFhA~trTmj<-s@3~oJs?E)>@KV06&CDEStcf~&^E7$B zPU-rZ1|`!B@Pc$e7(8f_C4~Cz;yac<_W>eE3_cxU?Dp+*ixxb~`!4uc|2b>h%Y95H z)Bb{CIXy!+IUO#RDE2EmdMnNIq~+0=^thxjk+$#+{wPr-&I}Q;8rcq2KS5$=l<}L} z%tGfLVSI?z_2)o}Umt|lBWS~E&Tqd=V~y$ebRIw+9OHkQUe(;y_6GERcoRb!qH~Uw z)YmveN>A~A+m3UPsWwPr>6?Q*7)r;zD*Jc%9QgZRyJ$Yk91N#H?Q9f*35SVF_A$>&th<25cfQ`v7I@( zju_%_BphtHOil?}03)lqhDzV4U}x$kW2Q`M2b{R5_Hj4k_7B9EB7T)X79ucfas}zZuqd?3~u`~iN_x2MBE7X5=t+7FA%wdELpwu8lJJ$ku)N+ z!6`UYt=avg(GE@hy_eqT50xh^CaqM`EpX_HJQ?PiOLf!UubKmeqEkI+#E5cl^I0od z&yL8CYZy~aArQ_VQ2dJ1+lv{NF~x$5;S>r*wmvl7p$^Vq>~Qt@^`TFtivO84XEcGA zb)PFCMG-d$jwweF?oZPR-k+duZ|O}&`bq(G7p%uUn32(JGqKU%mM#Q}-Dxk_n+=(8 zY9?8mTu3kjg^Rfh|Et0jp z@i&1UB$^of6>X@XCZ2$@9#NHIaL zp>u=EYyt`lrlz!z(;aF(SC2po(v%lv)K$39`$bZOkpX&~UTV^&$bZbk{phVrt@k4T zEczM*Sc;c=)Jh7=eWv!{qdjoAr*S`A=eQ4NtBhuAp-W%w6fA$_cABvhjI3OV*7Ypzui;3Q@n8>J8 zc4Oe`3bqXEQZjG1+UX(vER^DP@htCo4D8SPI!WPh>%!VzOiSe&>?wiB+5SMwE*K7$ zb3`WCO?(-+2B;o`r%uOt0R7SDM{Jybd@1N~_5|xIClXVnt{Hk^6YMp&(KdjiB<}$G z#T@&zCa62TtOC3c>sV`b+%lrNRz!qZNB}|=+A2Gbop}WNiW&?N8wuwkvt3s^Ke?(n zIi3$042^iEm)Kab26Mk%;vngLT{Iq~?pkvJq%OUCl!~sXOl|Z}VG$bH;FhUNV|J8* z56df3CdujDE$v=RrIIV4SSLP4h;$;}z!u0xSwOnO=@+K{Nkt!koe5QsBp$0gXQAHf z(@?BnExhY{K2Hv=8O6D97*Ox^Re=PgPFd!?E?2)m;;!Z4VC?`85A;Gc52OOcC zxqGtaW`D=OX5d>+yp|bC@g=L_@&)o$)$?0`61P8bZ~I%+t?{>BVHupsyqd)wDMkqY z)A+V}QUdzHroCGo=(YCs1*0bq2fr*(^br&2hzQ@gFnapJm~^K3tEe6N?Ka^!kQXFz z=DxG$^HulTCbVj&&Gz&SpWcIQ>C!56iwUH;{arB%KGn9Jwd`Zf3>%XemSKv36Y*`$ zkLp_ICRRJ|%2<&^Za~t8t{{6}I~RE> zVUNX#J$brhdTq@J&wewp@(7WQl6j!vC1~3+8*B_Pn2=lgg4|pw4*mFcRx%p{RG5i@ zP3eFByYiY9k8saE%arn3T?SQ%4giQnKtQd!+2bK5U$qR~hkn5K=p!D-vH|m5s zq{kM{46a82;OA7bUfkY|Xk03)WFgRy^_hCwRPFaN* z7Y_XhBBIEf%}F`AxfzcX9N_7=ky%Cl6);oNxx)ZSjRf~WRLv^ z=%64XdQ?-w@+Cb~2yS!vUk}xRhn@SWx`G*SK--=ApyS(fxM#AbK%2q52|B#1+JS~( zFo!_h*k($W3R1q%!?bB}+Lz#e_JD43)9aDkknwU$(Q>%cVk)|R8zS(1s$-gxbgT$^ z7orNGB0a6*htH48geOLIZNU0rlxCS2m`?M%ch;cyFuN}y9~XmX=Y5WufBs_V=vezL zu;yZ-g^4h%J$mS`Pm%*sIn{4B_Vmzy?99>oPtOa}V8sW2A0AFJ3Ka_L4ULWv8|VJc zlPb?%eEEP}{^HDU?s&;-bezA32eTLLQKVZ&)+CkzaAgL1*82tP-BAQH)Au4$vYU&& zOl%CuEzj*}%5(%x&?d;edNQnLD86A}LAqa~TndUzv11x-w}vMK$` z{1S1xxDT{^DDLC4d@K=-U|$GrKVj>D`Vm-HyaJ!<*p+8CofH(f9nH}s+}!(V-DxIi zDdRnedSHp!u3-Dj;5(r$(O*A4+7sPMQ78c;q($&{rH$qB^C5XXMfBY|R zc%;>I9-JVp*10|WTGe=s*0YGYQ}^!uCTyJi_I6LL$HmyJ(=_bHFa5Yv)B7xLot4!L zINd-fZzFN=Z=xeHz0ALbJRfY_-6mSDsr+WD$~IFdO<`(W&jS@0xN_5EXQp%GQfdI# zzh#>9*xBuEZZeyGUd|r;dp~dWk;l`8RyFVBe@|HEdes*y@RgyhG_O9p_RP=>HssYA zZT?DhjQi_myhE8{bcgYcS%FFTF%k8 zZ;hTnFk{oTIB~2UaOq;XSoH(i*!b{zT9k#K6#OH$LPvyhQIQg$w@t7Bl8&3ud>53LmOfa zZWk+?Q!8^|L%MKuM}7=11D(Q(2S^iy86Mv&tuEm4=d`1=oXZ0 zJ$+2;?mXs=s=H0BB$pyDx@~r_wZ{H%dZ9D2-Q!;(TE@h94hjAm7$(RvWz^eLe_P!= zKZ-V*2^x@ju0Cw(2%r*1kPN1W+sX#X8z{Qbj#FevMq-5*FBP< z;_9mR;*9@z*?Y7HOlLaMcz9Fp!Udh*`!UM4UjrYir|Q!UX0T(~!H8@u3Dqjac&mE; zH+O$!3~#+21V6-M*0Vfl6pmCB^h!kjXg&lG_1xo&r%Ue;xvwVy5NAd!arAyK^!fnp z%EXrkjqYXG#t?IOeEo%jljn^ulm3EP>B^dMT}_L6F2hCY(l^f^ajiBXDml6iHyF8% zf_NPfMGa?c$Fyi&Gh5dl8v*NhM%iVTGrt<|$yY}_nKDsQZYEOzeP81lm1=r!P-3I^ zSB%zWSNEmz!_PXNvaBNlwwZB9e3ED5i?l@VzDGk!G9|IsMSHdAxO1r}V!AoPKgllo z&uI%=0hf1VU|pxpJw5|E?LjRKa2GH}QD(N{_4snO4wl0nFl2XU%%|U-MYoyDT4ea* z=pRDcRRovQU-(KV(dAj-`tumiBj&$nW`sU!SlW0$zd5uJIdvP=lwcW?8K(xfrJwjE zayXrKTeB2gE>>z#PM<0>MOaT5DBJd{Tn-8gdY1lIR<=)!+6)mM%Euq;Rx9&&2bSC4 z`{{+N46voiDb4kH3PYnQWQVe zFZ-MoPdLuxtZ}E&XL^G7FH{Sm+wQ(7YsPPibsz6eMncs4aR#mv7?|Ki$fh4VZ3FD} z;g0~BWpAhwfZ+6=G~qde)BE9_lG*rm{fV{bOV?8Wo}E-GuQAXTk>IT#qpFhLA6fK> z9dbidA^WXczy}TvfB=}W-5?xIg{BoV(+s)c+gCx|RN5gIR%zCUeyGD`>)d|ak+05< zTMnoCw7-W6GoNq_?H>f0F*m;{$bSo19qxYwTM>hc^Xk=jM#2%@10YGM9ZeGs60x32^Eo4MKYg4w?>G=XCVlWL@GLvVGa*BKuwC z1yN=u!9b@#IP#eJJu4s>1Pl(YB-U2_Joc<=uLTTO0UU_^gA}Z1a+F+$U#~7628+A_ zQ3VRN#K7aMDqEb&A9AY>E}n>*r6h$(_5U$1tFXR5*m{8}(JKbO6uGmg8o4pZyi<>? zn?m9W12y_j`!=;&XxL6Pe8DCxM65rR%V}wRi)S$RUlw%Yi`R>7M77!?Hid!2Pkw*g z0kJ81(aVt362N(->7BLA)fap^_rxQOvmKwVi}fKtwX$M z{wG!37^7(M_xy60P2o+eAn1}U-H_Jydwmb!%V-=eG{OQxagKXp{yH>stS5=o#KzvW*IY*oRF zC%*Nn8z25_q$LomQ0w_{?P(0h@a*e6KJ^o1Ht?#b@l@SZdk6GKykEeL7oQW6KLdJh zPe?q#QJ}(AWB%&~8(Pnjianw>oDWlVI<~}z1YD{^VJ0STC&};L)g=~<5RprwBc23N zfx^&$EAm$i0-mwL&ufoKAe`+b4LL9|%u#_?OdJ2o&4zonXs2yE5EQ=r$_rA0S2HtuVs{(h5$;3GoXP`Ecp?9 z=~aVck>~U+kGf72>B0WjB^MVc%!sucYiKeXuO{fqqxI~R-Gv_e0q6vbf|l!h%c3NS zfQ#)9f2JMM8K_M0-nYv`Q5eh5S_AjUo}Qu@$cw2OYpY~Z8a`vP*sL> zv}o~T!sh0rxhg0Bve)>}!fTQtr5na3?bhqum64kRBltS zPQ?T^G)GC=RMU2?9>aK=BAkDC$7YHLXoWcwq__WPS{)>#mH_LQoZNa9G2z_R&UZS% zIp|^m(EQOd;NADd%U7bVE^4HvJHPmpa9{YRZmvx-3)&9C_Ck7!UrSif-jORRKbw?`UPV0od zql%;JIlP1!xOPC_K>#0d*{jA`b|l!&b$%)I@+>AmqNuj5`}xuB(X|Hrn>(F{reL0R z3+e@Cw51HSq~gg3EJ-l|V3U=V^`5;NDuQ0ZNg{p$H_hFKAA>(z(G5-{=OV%U#p#um zRV&evKb{SBnyT5k-oq8|=OTZ8b}PzjnO#{?K}-JL+8Y=a*@`lqo*1qRkR5w1hYdZg z3HaQ*88puAM1WXx-cdJTsYN~S;2w+(dxwl{=>Ye&KXaO8-^7tw`tvQzNP!8J%k;G3 zUN1;m?T3oWI0IJ}RM<7^Xr{rW-VN^TCEUU;U=3;@qCM#RabW7t*WNk++sy_%#ZOnz z19_{R6sSx91ZQi1xL&Y#vL>(53M8v5kB^u~y{gpY()p43(ltgGXFL1laXdDbjp^z( zaKC!_xR=M!zTZFhwZ+WEE%o|BP``-e^?z>cUL08A%kSH&K~tLu@eu%?{DIo!EKRKE5+(*VTp==cWCsB)5p>6{Wg zEw8{VKKLnexaO~@jAw~&$sdfeo^>a0Dop9(m8>okys9BgYXxyJDQF{h%O{Rw z*3Os)`*Ily7*y;%(EGW&dVhl(`t*eR(AdbmuEk)$bB)tI-9EmUM2X!9573MtzItw^ z9-y*ZdrH)%)Yyty&dG4r@pP-)AZoa-*v>{AwTI9Ro(GKfA&F-oH;SV+SNEp;6}2Dn z=+ph)6?8P^xuA1&KN*EM0%51are&_a7!9#=%mTAqczh1y+3$dd@PSQKWhxRv7P!T2 zH&HwAOwKA%j1gjUOS7|J^TrkXxbwnDutU~Gx`CdKz>^E7N==X0e5ex$nD9iD=1z{q zkr4iG5sL9I;YshsfBpMn0E;zK|ESXXC@d)L`-8CWOI0_e6I1Bheit;3Fg9-Xy(*m< zRV{FMp^`T`?{)6SwxS*5%^mHEY#rmHiU5lfdJ^poD&`sDZ2dlv8&{iED_bS9eYF%= zXet`BJ$oXX6I-c~vyeb$FSg(p%B}I%vi*F>E0R}~U!OorK87x%e#wa1C<5)g;~^+6 z1`0J|58{mWYk6n$9S#Y3-eucbvjQ`q}W#j(0n z3!**^4r-$^b#;{N!RPG$x>Z)*1z(a0L1@J)>jQZ$d`p1g%z-SJ7T__<40HB`*=%s6_{oVE^N!$53y`6Dc1&wf!Y;3%Pi8 z=e4Yt-E~F?786N<4FE{EyqRoGy6y6i@dJB(;tRpsHnU`)ZXY1li!G$dzr4tF2SeWM zrGbTl{g?!P#>Ri-*O=_3E6kAZ-^agqp&{;2p!443S@e3t{T;1!asP^HIL$0As>7>}Qv(n`$I>v`_S3}MA|1u9= zGGIhQWm*!PFl72d!9 z<3!#I2hWhsvMS9iZ1)D2i&P)#jfJD@=>!!7W!N)YukReCAo_rNR@qzLw@CL#lF5!x zEv!!IzY##&HUU$EaUo`_StP$ezgungnND=F17N#j(1-k%8Jro)Wm%`@qnlwm(z~(2 z4Mptx1ScNPg~{raLz)sS*UNMH^4w4H(i14t5}kfhab{52I?DzhzLyi>l6lvz8}-qG(^ z_OWnv3~q0rP)>2ZHw>p4OARzV>?~fE@$A4@g8L*bqj+n1=ywI!j}i0hPYNzPocg_} zTA-)%43TwjCet@^=cBJ6CHeo|XdQ3IHv^%gArPyTl~pnop+BVWXrE5L@Q?RVtz+ei+=1=xJkZ_)02||zj$p z-R@MYXEnLJx(|1Ht&Nx{BD9fz@ko(!eBJLn=b(8upQM!qe5}RQez2hvuxmP)1+Wb;o~qnYVBicX1I#QldyvD`i=VV^{JLSz**T4eHo!nxriX6h>R1t>FSeo4^%} zqD9cS0onxp;x$Db*RpFnaMDJ$>o~4#S)xqQ+9>YgChmG%-prdfGq)eOh@>c&S}fHn z|G)*_o8`=$|2^m2bIx2?#xj<%jAbn2|1HE~2fzE$@FRhc-_gnxFGYv}C+>1ARV4p- z|3d{0H(RM|Cg86QjCOBZy)@-#5EwYzMl5Rid!G2sydyVTv86&Tc2LQn*bfX>GK?{7 zFEg2OR~`KB%Y(PyY{jmLfWUy~n=2kcV5lt(GA9$_;I}^`BrNIze|ccYtY2xaT4-RP zWl-P;H(R|a_d|F;(z}E?J7kJ!A_KSInT}r^I4WctMM%=!& zd|+ro!l_?sz@2+s-1|i157Sfhg92gAvZ7>`;MQ^hfTV9mXMP(@ay;%G5Z`;fJGNv~QqE6&T`w8eQb=3N`7z-2f8cv`w!TlS^E6xc{2_twA(g&%!B+vZKrtnit+iB+CFj48kk@Ak3<94I z4?TyetY&TX9rSlLFw}d7&3FCIO(8%A^9e{w<7lO&t)!YyfVg%7g21Qo!v zASSbz$jcxSRVbmZS^m}F5)072tu3kIQqMneJv^YN{TSofX$a57baim9G@!C-JLX6| zQVHDn7&?-QlsAqHvp_S;Q*o`7bS|*~b8Qyjxen(qbYhj3f-zXTzC^6Ig`L?J9c7Yylp$kOkY8ebV7(b!E8WiF*S}BuP!r|b?nmCh6-O*+fVoQ8S_@%ck9Ggm@BJ2k^85Sv`aeIz z!59CPzj^t;aLcMtkyP?5m=GQx9DWVm(}=ZdC8=3aw34Wsm~&_$Y^}7i)q)scuFwGD zV?+GQkM{GO;v!!7-EVR5`R6EIQ_Y_}^(yDb;>2_EN!fBAc{@|4r$VC6o+7064Nbbl z!XOwZTT5x|1$huxQV9F;L1=n~w|?(Y4%XJPJ{BWQNVaaMV(XW_M*WC~GCqOpKoEcs zB!+q@&&Vg5l6FH0SZHlh8e3XY*-{GT4RQHle2>xL9(o7bX*>BgHQ7C^x%XZ&fYkha zdin==;vb$Q8A&7Q`2_X^f$Jcq1CZ}Jn65SovrEw0`ow2l_pv}vODbCnVaAj|7%nyd z%w=Y!x4E7%D@i;^aHg-D2iD~gq>9f<%88MUF>R^&N{o<8s3! z6SOzQnCKh9PuN#<@4PWbMzm;CZOZ+xW-7{tK+aGQ^rKgbT|F92@t8pOe0>7J(tXzn8n}9=W6}{onBO zH;1uwB@}@(*u&=9ltr(bh)rOR`v@73ou7)-q4_aD2_dE}JqQp|`GeXB(XS*@%Bk2V z3}UVeeE9YO9^SQv<4%}iV=>uvl8lXaFgZ5LgzJ%=l7UtdAp}J`_ww|=?Z-X#D(ym( z8&>@79luSt>#}mNkNdv-M^|jN&crCk&j@5HbPj@RLrFmoMk;JKg@^^289v=T4o$5E ztSe1p$9njvsh5(<03C|V%F+U1F82DQ7l?lCrAGTB$%L4*M9kJg9nB4_UZr`zwTBZp zSg9F!W5f6pF+3~6p|j^$Uuh8ySxm+!8R_lf2U%6L26SmjRNqTMpZ?_DP;WVMsvxuGf z2rHUG`s$5@vkLfwExWM7(W_Q|2KVmS#Jzhi{ui$_bLeB2wm}%TVRCc;oL*vfIVXA` zEex&)`*t|UIr+a5vkMYZ2pguq3n*c1l!#7Q073>J@HpSo#$YIdJz}G;cLym^MS10&mvo+#5VP$*BZPC20BPbgzJZ*wfRI#}lmbfDR#7nmF(-kLlKx-3 zK+*PHr2B@H++y17F(L)UBwUy7go~1b@r+dRQikYjJ4W%EZP%TE_Te({`Tz@TAA|%G zP`%b-%eJ+by-$ma7GQ*lUnD?b&t4@#8A#8};MA!xnxFnBRK`k1yZgX%(Gu?7v6o+U zoMDrOzLZQnGnHy8F||{q>w?XSqEZ^p9>)`kvWksJb$uEiCmTCBH@pT?vtaThsEnx- z!e#4*G(PDuQqQZ0=D2_o&P=z&_+~axY~Hkk_we}Iof~+g$EEq`A*@IZ=RQ8f$mjs= zK@7XA50$+VX<5wll9UuQbPut$x|Y{oeSzDPBXl+#<$*7LgN&R)t`z5cFVNH+r=?5M z*jkA399CuyQnS5)RI8t#9$5#W9EL8mQ2q(+g_#EE&t9N}nA<&FU0Fk&mK^HoWUY?#UUNG+chnJt zQfO=IL`2hwoogXHIYDaW7BEw8AcVkD5`|AgM?a@t_)p4Kt|UI#K{5zfU%hon1dNWl zB;pZ}E{IfiZZ*_wDw;ii}ZTShvGlZP=ufS8WHLRq*c z4zangm>nSqpYKH^6=zNz#hn~mQUOy@kwtO(8JIi`P6s=-rp!Cv)jdwkfg`Q(?n(I7 zX-!*aa#2}APQ?HzrgWaQ0I8&{rn^9aLHLXG=~m?Av-|EkN=_f+m98#Ac_oYq!@yu4 z_uuy$ux}CXQ&Vy=n*pjVy$f5 zMl$Q)!^tbbq-WDCB&VN!l5g#MnCgwUQM{_0`iA#;>%G@WDc?vqXC-6lS)4Y8){YLU z&NgyS-NQ?c0RStq(s=Np#a*WE%eP^L8##Ky5K=C6yP~Wr-@LdWVWghcQ<*t2Kq?uZ z*%D;%rTB!;!=X@^uiW4^Lx*HgrB><=cs(=(A1l*iYxSt+;Aj#B5sV3k*W(a_(ZKTCOB~S=t0Z}fq zoBT6~fT`kV6zBt5fNmf$Lx5mv#-r0@gr=3hRrbo5X%gJ&VFMmu7AknFD7+Lxma&Xw cEMpn}1B5z$V^NmcqyPW_07*qoM6N<$f=}a|YybcN literal 0 HcmV?d00001 diff --git a/app/examples/Games/GameOfLife/.lang/ca.mo b/app/examples/Games/GameOfLife/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..da732f13b36653c134274006d8131bfed8f8b2c8 GIT binary patch literal 2176 zcmaiz&u<$=6vwAfC>Y9*7AU`jUJq5p52v=viJtbEMhNdpk4VeednO9}n$+ zPhdP<%-_Ldm_O{p10%gdi2K0pg71L$!@pMWdchk7KQ8!L!LJH_1Kx#mKY{zfU%-dK zUkmQLQ;5gl9|l?fIj{uIfrr7f-~q4=u7GcWZ-O6y4}tq3%I6;i9|T_j?*SViueS=e z!4!TMJcb;<1$o~e2<~q103Pf=Q1D1WD)?-{TET^aO%S1oH4u{GRqz?G4RZV$_yl+j zd;q)wa$P?G9|1oHIlr&LHuyalfi=A3I$Z}@?EsSY zDoLngU96lJt$|Ilpsk^%O60Ike}6Musr0>Ak$pvWi!SIbl{*)=a`PqY^xM`1nUEj$ z`zpAhxR|n{7aJ;3QJ~OMv}`jI`224Ao_2c5DU#!Wb~K`jHNN5Kic%@*;K>IW)C_b2 z<$GBvb+#irI?-VWWf>r_%o*hZ9mSa$7BM#P7Yq$;Mn080CJ$swJ0EC88RaBL3H83j z@$T*$v5{{+_m_=gBS;r$w_su;a|1n)iP%I`x0R=@T+q?DB6&excRB=wq785CNZ;tV zrBY`|jn2|)UmAK@I^A!a=q%+6(!{)SRvA0`FwmMd+GFy1sgYvprOlo+S3CuqCp8wO zpk)7O&{+A>C3Be8NqnuSGtP)Jmop#W z%I$1)i!MYoEUk5EQJ-%d3tRQ{z) a_5bzg3aS@r{P;Yw?M$br&qRFOr+)%9{z(D= literal 0 HcmV?d00001 diff --git a/app/examples/Games/GameOfLife/.lang/ca.po b/app/examples/Games/GameOfLife/.lang/ca.po new file mode 100644 index 00000000..f86c8b36 --- /dev/null +++ b/app/examples/Games/GameOfLife/.lang/ca.po @@ -0,0 +1,131 @@ +msgid "" +msgstr "" +"Project-Id-Version: GameOfLife\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 17:58+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FMain.class:265 +msgid "0 Neighbour" +msgstr "0 veïns" + +#: FMain.class:258 +msgid "1 Neighbour" +msgstr "1 veí" + +#: FMain.class:251 +msgid "2 Neighbours" +msgstr "2 veïns" + +#: FMain.class:245 +msgid "3 Neighbours" +msgstr "3 veïns" + +#: FMain.class:238 +msgid "4 Neighbours" +msgstr "4 veïns" + +#: FMain.class:231 +msgid "5 Neighbours" +msgstr "5 veïns" + +#: FMain.class:224 +msgid "6 Neighbours" +msgstr "6 veïns" + +#: FMain.class:217 +msgid "7 Neighbours" +msgstr "7 veïns" + +#: FMain.class:210 +msgid "8 Neighbours" +msgstr "8 veïns" + +#: FMain.class:291 +msgid "Alive" +msgstr "Viu" + +#: FMain.class:296 +msgid "Dead" +msgstr "Mort" + +#: FMain.class:302 +msgid "Draw cell borders" +msgstr "Dibuixa la vora de les ceŀles" + +#: FMain.class:72 +msgid "Evolution Delay: " +msgstr "Retard d'evolució:" + +#: FMain.class:199 +msgid "Evolution Delay: 500ms" +msgstr "Retard d'evolució: 500ms" + +#: .project:1 +msgid "Game of Life" +msgstr "Joc de la Vida" + +#: FMain.class:147 +msgid "GameOfLife" +msgstr "JocDeLaVida" + +#: FMain.class:317 +msgid "Horizontal symmetry" +msgstr "Simetria horitzontal" + +#: FMain.class:276 +msgid "Options" +msgstr "Opcions" + +#: FMain.class:272 +msgid "Select the Count of Neighbours where a cell will die or keep its state." +msgstr "Seleccioneu el comptatge de veïns on una cèŀlula morirà o mantindrà el seu estat." + +#: FMain.class:280 +msgid "Set here the probability that a Cell will be born or not when you spawn the first Generation." +msgstr "Ajusteu aquí la probabilitat de que una ceŀlula neixi o no quan s'engendra la primera generació." + +#: FMain.class:307 +msgid "Small generation" +msgstr "Generació petita" + +#: FMain.class:176 +msgid "Spawn First Generation" +msgstr "Engendra la primera generació" + +#: FMain.class:194 +msgid "Start Evolution" +msgstr "Inicia l'evolució" + +#: FMain.class:206 +msgid "Survival" +msgstr "Supervivència" + +#: FMain.class:136 +msgid "The Game of Life" +msgstr "El Joc de la Vida" + +#: FMain.class:312 +msgid "Vertical symetry" +msgstr "Simetria vertical" + +#: FMain.class:164 +msgid "" +"Written in Gambas
\n" +"by Iman Karim
\n" +"and Benoît Minisini\n" +"

\n" +"Thanks to the Gambas team!" +msgstr "" +"Escrit al Gambas
\n" +"per Iman Karim
\n" +"i Benoît Minisini\n" +"

\n" +"Gràcies a l'equip del Gambas!" + diff --git a/app/examples/Games/GameOfLife/.lang/cs.mo b/app/examples/Games/GameOfLife/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..64dc7b0f0bb4b484bca01a9ed895a4f16da4937f GIT binary patch literal 2110 zcmaiz&u<$=6vwAO_+==Cwm|uDcnEPot<$vhM{rG46T3-N|A^uw2&5in?Qt^oddAF- zH@Ffv4j{x41S*#b^-!cxE*ul7axEnOfpS1x;DnGka^b-Dt(Vv>l^A*anYa7)z3+Rw z@0UaSKNDClrTcI2RqUVl;18?2SBS^K_fmcUJ^}mVl%J>kI^}mMw^RO@@-Fxgp8E~l z5B?554gQ&O&psiZfqfX{_^*K(@GN*3EP#)ItKd9%8N3YM0G|T)A?UN<5%3^*415$k z5AywD%2luny9El=co*b6KS3P({|d7GN6Noa?tOT4{Xoj2DPKuB1+u<3K>S1je@DRu z@DM0L*5^U4?~{~YfX~6c0X_z9gIwPapaKsf=#yX%WWNu=1K?L6>-+}f_wzl-{ksje zz`G#7LVkwp;uzcq?&S;EywLhY3&&xt{0;DW9-H6I5p1+KS!hGDIM<8V+&->{bMivE zWO46#L4LBhcGkeLfOyJf3KXH?nGit57vO`3*&Lwr4y)zgvAon;Xh0k|RBv8lsvSIUKc>>XP;m zMP7O}g*cuPUyjsuHjsf1weKUc^awD=1tkFj#f2FTVyx#3hWfTgQCDu7L`X}!$kV(s z%1Ks<=pBj2*LSaodg6TXK1Wga(s|nLm#Ft#tYaC96{xzWJ!{H&-57NwX~^47dk-nv zuwx*jeBfp?L7(!$>|#e6x+IW5c&G$3;F1DUs*^s6k zk+(@vqoCxK?7W;mp=ZUCvscji>|!$uF%jy_(NbaVQenPCYo+D-Vy&84s%4i|jL6nx zi|BKX-YFH9vz1!5SfGVkuCjRS-CDJjDa*)bSDZ9aD7|$#y0}^{Q?*bjQNC0uES4`6 zipAwpy*>+9v)+^ID!T$DIVu;b^Q)XPUoKw=^^|q%W1k#lDvOoUSl`L1>C7CyBxR7U z--RBp-uEZEq15I)H8!M+lz($|ZHKF*AfRXN(3SGkjHAiHaTW7#n#l z*7^ayIShxr^C^bq-`bB;yNQYD4L@p2x~e+EFBKUTSc)`UjcRNRu2U2bZ(ZS0i>E{t z?^?zPbQRBaos64zu61qG2At+N#3_o>fPFF&wGFn8k3t&7+4{pB_E;NS?@-)F$w<(3 zG0PrJSOfE1k>TLxt_|_G6x literal 0 HcmV?d00001 diff --git a/app/examples/Games/GameOfLife/.lang/cs.po b/app/examples/Games/GameOfLife/.lang/cs.po new file mode 100644 index 00000000..7bf1906a --- /dev/null +++ b/app/examples/Games/GameOfLife/.lang/cs.po @@ -0,0 +1,130 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Game of Life" +msgstr "Hra o život" + +#: FMain.class:72 +msgid "Evolution Delay: " +msgstr "Prodleva evoluce: " + +#: FMain.form:37 +msgid "The Game of Life" +msgstr "Hra života" + +#: FMain.form:48 +msgid "GameOfLife" +msgstr "Hra o život" + +#: FMain.form:65 +msgid "" +"Written in Gambas
\n" +"by Iman Karim
\n" +"and Benoît Minisini\n" +"

\n" +"Thanks to the Gambas team!" +msgstr "" +"Napsano v Gambas
\n" +"od Iman Karim
\n" +"a Benoît Minisini\n" +"

\n" +"Díky Gambas týmu!" + +#: FMain.form:78 +msgid "Spawn First Generation" +msgstr "Vyvolat první generace" + +#: FMain.form:96 +msgid "Start Evolution" +msgstr "Start evoluce" + +#: FMain.form:101 +msgid "Evolution Delay: 20ms" +msgstr "Prodleva evoluce: 20ms" + +#: FMain.form:108 +msgid "Survival" +msgstr "Přežití" + +#: FMain.form:112 +msgid "8 Neighbours" +msgstr "8 sousedů" + +#: FMain.form:119 +msgid "7 Neighbours" +msgstr "7 sousedů" + +#: FMain.form:126 +msgid "6 Neighbours" +msgstr "6 sousedů" + +#: FMain.form:133 +msgid "5 Neighbours" +msgstr "5 sousedů" + +#: FMain.form:140 +msgid "4 Neighbours" +msgstr "4 sousedé" + +#: FMain.form:147 +msgid "3 Neighbours" +msgstr "3 sousedé" + +#: FMain.form:153 +msgid "2 Neighbours" +msgstr "2 sousedé" + +#: FMain.form:160 +msgid "1 Neighbour" +msgstr "1 soused" + +#: FMain.form:167 +msgid "0 Neighbour" +msgstr "0 sousedů" + +#: FMain.form:174 +msgid "Select the Count of Neighbours where a cell will die or keep its state." +msgstr "Vyberte počet sousedů, kde buňka zemře nebo si ponechá svůj stav." + +#: FMain.form:178 +msgid "Options" +msgstr "Možnosti" + +#: FMain.form:182 +msgid "Set here the probability that a Cell will be born or not when you spawn the first Generation." +msgstr "Nastavte zde pravděpodobnost, že se buňky narodí, nebo ne, když se Vám vyvolá první generace." + +#: FMain.form:193 +msgid "Alive" +msgstr "Naživu" + +#: FMain.form:198 +msgid "Dead" +msgstr "Mrtvý" + +#: FMain.form:204 +msgid "Draw cell borders" +msgstr "Kreslení okrajů buněk" + +#: FMain.form:209 +msgid "Small generation" +msgstr "Malé generace" + +#: FMain.form:214 +msgid "Vertical symetry" +msgstr "Vertikální symetrie" + +#: FMain.form:219 +msgid "Horizontal symmetry" +msgstr "Horizontální symetrie" diff --git a/app/examples/Games/GameOfLife/.lang/de.mo b/app/examples/Games/GameOfLife/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..52183339d3d6ded88b448c10b2e7986972de9696 GIT binary patch literal 2092 zcmai!&u?2r5XZNq{4!9WEk6nz76}fhb&?inBiC3p@r!H4j-%LVpwPoUdu%Ux-e-2# zHjcy<#1#n;61Tz?xWR=}Zv6+!0dav75`rTK4t(e3IW{2@E3ZF0Z|BX#CLjKMuZJOsX*@_q1O*dL|*H0AA--=_Q_<(-sw!F%!Eui#1WH}G-r zkCZ1Cgm?n>8Ia?@1ZKd?;2E$8-VbhrW$+ew3;YUv3|v6aC&9Dequ_b)0q`ow`=ylI z-~e_96sYko$mjfs+g=aCjeOw>s&I89KgL}^d zX(xkge*}|zn~Y_-LAu)u3bws1O)VB{noN;gnrlO)p7)_GYbLsoH? zh~8Ity?gjXG!o~vBaWismG$zlU!pOzBRx`)XdKmEOdUlIOh#Z@234PAV zH-q96rSF4=AS>FgURporPX_ax2^0Cdywe4zEZWL>Q%m=k%rFyMcDs2Ug zMggv-J5*iDHjznAR*M_uZ9XzztzL`tVr;uJpPbCpDz#vi-^In{%qngY85}nmARkZf z`K3Xmw7Du6OzOAGX@16 z^C%HlW1tDQpPWphI*}DKU6E`Y2uo9{EX3Z~|FRcRBABAtQ$2p$9wv7|j&YzQZAd1` zK}p(%eo^eGo^`Drb|PfCOWI$MZROm&ZHlg5#a%8^Z#(_`${g7a`e*I|}t3=|!TZU4?3AeJB%(u$o^uBB~Mo$&Odm V@c&g+f~fG1gnE@rl{zP?e*hDP9IOBU literal 0 HcmV?d00001 diff --git a/app/examples/Games/GameOfLife/.lang/de.po b/app/examples/Games/GameOfLife/.lang/de.po new file mode 100644 index 00000000..75523a22 --- /dev/null +++ b/app/examples/Games/GameOfLife/.lang/de.po @@ -0,0 +1,121 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Game of Life" +msgstr "Spiel des Lebens" + +#: FMain.class:72 +msgid "Evolution Delay: " +msgstr "Evolutionsspanne: " + +#: FMain.form:37 +msgid "The Game of Life" +msgstr "Spiel des Lebens" + +#: FMain.form:48 +msgid "GameOfLife" +msgstr "-" + +#: FMain.form:65 +msgid "Written in Gambas
\nby Iman Karim
\nand Benoît Minisini\n

\nThanks to the Gambas team!" +msgstr "Geschrieben in Gambas
\nvon Iman Karim
\nund Benoît Minisini\n

\nDanke an das Gambas team!" + +#: FMain.form:78 +msgid "Spawn First Generation" +msgstr "Erste Generation erzeugen" + +#: FMain.form:96 +msgid "Start Evolution" +msgstr "Evolution starten" + +#: FMain.form:101 +msgid "Evolution Delay: 20ms" +msgstr "Evolutionsspanne: 20ms" + +#: FMain.form:108 +msgid "Survival" +msgstr "Überleben" + +#: FMain.form:112 +msgid "8 Neighbours" +msgstr "8 Nachbarn" + +#: FMain.form:119 +msgid "7 Neighbours" +msgstr "7 Nachbarn" + +#: FMain.form:126 +msgid "6 Neighbours" +msgstr "6 Nachbarn" + +#: FMain.form:133 +msgid "5 Neighbours" +msgstr "5 Nachbarn" + +#: FMain.form:140 +msgid "4 Neighbours" +msgstr "4 Nachbarn" + +#: FMain.form:147 +msgid "3 Neighbours" +msgstr "3 Nachbarn" + +#: FMain.form:153 +msgid "2 Neighbours" +msgstr "2 Nachbarn" + +#: FMain.form:160 +msgid "1 Neighbour" +msgstr "1 Nachbar" + +#: FMain.form:167 +msgid "0 Neighbour" +msgstr "0 Nachbarn" + +#: FMain.form:174 +msgid "Select the Count of Neighbours where a cell will die or keep its state." +msgstr "Wähle die Anzahl der Nachbarn, wann eine Zelle stirbt." + +#: FMain.form:178 +msgid "Options" +msgstr "Optionen" + +#: FMain.form:182 +msgid "Set here the probability that a Cell will be born or not when you spawn the first Generation." +msgstr "Wahrscheinlichkeit, dass eine Zelle geboren wird, wenn du die erste Generation erzeugst." + +#: FMain.form:193 +msgid "Alive" +msgstr "Lebend" + +#: FMain.form:198 +msgid "Dead" +msgstr "Tot" + +#: FMain.form:204 +msgid "Draw cell borders" +msgstr "Zellengrenzen zeichnen" + +#: FMain.form:209 +msgid "Small generation" +msgstr "Kleine Generation" + +#: FMain.form:214 +msgid "Vertical symetry" +msgstr "Vertikale Symmetrie" + +#: FMain.form:219 +msgid "Horizontal symmetry" +msgstr "Horizontale Symmetrie" + diff --git a/app/examples/Games/GameOfLife/.project b/app/examples/Games/GameOfLife/.project new file mode 100644 index 00000000..250cecc3 --- /dev/null +++ b/app/examples/Games/GameOfLife/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +Title=Game of Life +Startup=FMain +Icon=glob2-icon-48x48.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Description="The Game Of Life.\n\nThis example runs the Game Of Life cellular automaton. It allows to define many parameters of the automaton." +TabSize=2 +Translate=1 +Language=en +Maintainer=benoit +Vendor=Example +Address=benoit@localhost +License=General Public Licence +Packager=1 +Tags=Example diff --git a/app/examples/Games/GameOfLife/.src/CGameField.class b/app/examples/Games/GameOfLife/.src/CGameField.class new file mode 100644 index 00000000..3f998436 --- /dev/null +++ b/app/examples/Games/GameOfLife/.src/CGameField.class @@ -0,0 +1,236 @@ +' Gambas class file + +Fast + +'PRIVATE Game AS Boolean[] +Private Game As New Image +Private MaxX As Integer +Private MaxY As Integer +Private dW As DrawingArea +Private $iZoom As Integer + +Public Function Init(bSmall As Boolean, iLife As Integer, bSymetryH As Boolean, bSymetryV As Boolean) As Integer + + Dim i, j, n, size As Integer + + Randomize + + $iZoom = 3 + MaxX = dw.W \ $iZoom - 3 + MaxY = dw.H \ $iZoom - 3 + + 'Game = NEW Boolean[MaxX + 1, MaxY + 1] + Game = New Image(MaxX + 1, MaxY + 1) + Game.Fill(Color.White) + + If bSmall Then + + size = 12 + + For i = MaxX \ 2 - size To MaxX \ 2 + size + For j = MaxY \ 2 - size To MaxY \ 2 + size + If Rnd(1, 100) >= iLife Then Game[i, j] = Color.Red + Next + Next + + If bSymetryH Then + For i = MaxX \ 2 - size To MaxX \ 2 + size + n = size * 2 + 1 + For j = MaxY \ 2 - size To MaxY \ 2 + Game[i, j] = Game[i, j + n] + n -= 2 + Next + Next + Endif + + If bSymetryV Then + For i = MaxX \ 2 - size To MaxX \ 2 + size + n = size * 2 + 1 + For j = MaxY \ 2 - size To MaxY \ 2 + Game[j, i] = Game[j + n, i] + n -= 2 + Next + Next + Endif + + Else + + For i = 0 To MaxX + For j = 0 To MaxY + If Rnd(1, 100) >= iLife Then Game[i, j] = Color.Red + Next + Next + + Endif + + 'PRINT MaxX + 1;; "x";; MaxY + 1; " game" + +End + +Public Sub SetDrawArea(pdW As DrawingArea) + + dW = pdW + +End + +Public Sub DrawGame(bBorder As Boolean) + + Dim SquareSize As Integer + + 'IF GameRow.Count = 0 OR GameRow[1].Count = 0 THEN RETURN -1 + + SquareSize = 4 'dw.Height / GameRow.Count + + Draw.Begin(dw) + + 'Draw.FillStyle = Fill.Solid + 'Draw.LineStyle = Line.None + 'Draw.FillColor = Color.LightBackground + 'Draw.Rect(4, 4, Game.Width * $iZoom, Game.Height * $iZoom) + + If bBorder Then + Draw.LineStyle = Line.Solid + Else + 'Draw.FillRect(0, 0, dw.W, dw.H, Color.Background) + Draw.LineStyle = Line.None + Endif + + Draw.FillColor = Color.Red + Draw.Foreground = Color.LightBackground + + Draw.Zoom(Game, $iZoom, 4, 4) + + Draw.End + +End + +Private Function CountNeighboursOnBorder(x As Integer, y As Integer) As Integer + + Dim iTot As Integer + Dim i, j, ti, tj As Integer + + For i = x - 1 To x + 1 + If i < 0 Then + ti = MaxX + Else If i > MaxX Then + ti = 0 + Else + ti = i + Endif + For j = y - 1 To y + 1 + If j < 0 Then + tj = MaxY + Else If j > MaxY Then + tj = 0 + Else + tj = j + Endif + iTot += Game[ti, tj] + 'IF Game[ti, tj] = Color.Red THEN INC count + Next + Next + + iTot = iTot - Game[x, y] - Color.White * 8 + + Return iTot \ (Color.Red - Color.White) + +End + +' Private Function CountNeighbours(x As Integer, y As Integer) As Integer +' +' Dim iTot As Integer +' +' iTot = Game[x - 1, y - 1] + Game[x, y - 1] + Game[x + 1, y - 1] + +' Game[x - 1, y] + Game[x + 1, y] + +' Game[x - 1, y + 1] + Game[x, y + 1] + Game[x + 1, y + 1] +' +' iTot -= Color.White * 8 +' 'DEBUG iTot / (Color.Red - Color.Background) +' Return iTot \ (Color.Red - Color.White) +' +' End + +Public Sub SpawnNextGeneration(Live As Boolean[], Keep As Boolean[]) + + Dim x As Integer + Dim y As Integer + Dim myCount As Integer + Dim newGame As Image + Dim myCountSub As Integer = Color.White * 8 + + newgame = Game.Copy() + + For x = 1 To MaxX - 1 + For y = 1 To MaxY - 1 + + 'myCount = CountNeighbours(x, y) + + myCount = Game[x - 1, y - 1] + Game[x, y - 1] + Game[x + 1, y - 1] + + Game[x - 1, y] + Game[x + 1, y] + + Game[x - 1, y + 1] + Game[x, y + 1] + Game[x + 1, y + 1] + + myCount -= myCountSub + myCount \= (Color.Red - Color.White) + + If keep[myCount] Then Continue + If live[myCount] + newGame[x, y] = Color.Red + Else + newGame[x, y] = Color.White + Endif + Next + Next + + For x = 0 To MaxX + y = 0 + myCount = CountNeighboursOnBorder(x, y) + If Not keep[myCount] Then + If live[myCount] + newGame[x, y] = Color.Red + Else + newGame[x, y] = Color.White + Endif + Endif + y = MaxY + myCount = CountNeighboursOnBorder(x, y) + If keep[myCount] Then Continue + If live[myCount] + newGame[x, y] = Color.Red + Else + newGame[x, y] = Color.White + Endif + Next + + For y = 1 To MaxY - 1 + x = 0 + myCount = CountNeighboursOnBorder(x, y) + If Not keep[myCount] Then + If live[myCount] + newGame[x, y] = Color.Red + Else + newGame[x, y] = Color.White + Endif + Endif + x = MaxX + myCount = CountNeighboursOnBorder(x, y) + If keep[myCount] Then Continue + If live[myCount] + newGame[x, y] = Color.Red + Else + newGame[x, y] = Color.White + Endif + Next + ' IF Rnd < 0.1 THEN + ' x = Int(Rnd(0, MaxX - 30)) + ' y = Int(Rnd(0, MaxY - 30)) + ' FOR i = x TO x + 29 + ' FOR j = y TO y + 29 + ' newGame[i, j] = Rnd(1, 100) >= FMain.Slider2.Value + ' NEXT + ' NEXT + ' ENDIF + + Game = newGame + +End + diff --git a/app/examples/Games/GameOfLife/.src/FMain.class b/app/examples/Games/GameOfLife/.src/FMain.class new file mode 100644 index 00000000..bcb0f785 --- /dev/null +++ b/app/examples/Games/GameOfLife/.src/FMain.class @@ -0,0 +1,95 @@ +' Gambas class file + +Private Game As New CGameField + +Private Sub DrawGame() + + Game.DrawGame(chkBorder.Value) + +End + + +Public Sub Form_Open() + + 'System.Profile = False + 'Randomize 1972 + Button1_Click + Slider1_Change + 'togEvolution.Value = True + +End + +Public Sub Button1_Click() + + Game.SetDrawArea(dwgGame) + Game.Init(chkSmall.Value, Slider2.Value, chkSymetryH.Value, chkSymetryV.Value) + DrawGame + +End + +Public Sub Button2_Click() + + tmrEvolution_Timer + 'Game.SpawnNextGeneration + 'DrawGame + +End + +Public Sub tmrEvolution_Timer() + + Dim Live As New Boolean[9] + Dim Keep As New Boolean[9] + + If Not togEvolution.Value Then Return + + live[0] = chkZero.Value = CheckBox.False + live[1] = chkOne.Value = CheckBox.False + live[2] = chkTwo.Value = CheckBox.False + live[3] = chkThree.Value = CheckBox.False + live[4] = chkFour.Value = CheckBox.False + live[5] = chkFive.Value = CheckBox.False + live[6] = chkSix.Value = CheckBox.False + live[7] = chkSeven.Value = CheckBox.False + live[8] = chkEight.Value = CheckBox.False + + keep[0] = chkZero.Value = CheckBox.None + keep[1] = chkOne.Value = CheckBox.None + keep[2] = chkTwo.Value = CheckBox.None + keep[3] = chkThree.Value = CheckBox.None + keep[4] = chkFour.Value = CheckBox.None + keep[5] = chkFive.Value = CheckBox.None + keep[6] = chkSix.Value = CheckBox.None + keep[7] = chkSeven.Value = CheckBox.None + keep[8] = chkEight.Value = CheckBox.None + + Game.SpawnNextGeneration(Live, Keep) + DrawGame + +End + +Public Sub Slider1_Change() + + lblDelay.Caption = ("Evolution Delay: ") & Str(slider1.Value) & "ms" + tmrEvolution.Delay = slider1.Value + +End + +Public Sub chkBorder_Click() + + If Not togEvolution.Value Then + DrawGame + Endif + +End + +Public Sub togEvolution_Click() + + tmrEvolution.Enabled = togEvolution.Value + +End + +' PUBLIC SUB dW_Draw() +' +' Game.DrawGame(chkBorder.Value) +' +' END diff --git a/app/examples/Games/GameOfLife/.src/FMain.form b/app/examples/Games/GameOfLife/.src/FMain.form new file mode 100644 index 00000000..7306fe9b --- /dev/null +++ b/app/examples/Games/GameOfLife/.src/FMain.form @@ -0,0 +1,162 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,125,95) + Text = ("The Game of Life") + Icon = Picture["glob2-icon-48x48.png"] + Resizable = False + { Panel1 Panel + MoveScaled(1,1,29,23) + Border = Border.Plain + { Label3 Label + MoveScaled(1,1,16,4) + Font = Font["Bold,+2"] + Text = ("GameOfLife") + } + { Label7 Label + MoveScaled(1,4,18,4) + Text = ("The Game of Life") + Alignment = Align.Left + } + { PictureBox1 PictureBox + MoveScaled(20,1,8,8) + Picture = Picture["glob2-icon-48x48.png"] + } + { Label4 TextLabel + MoveScaled(1,10,26,12) + Font = Font["-1"] + Text = ("Written in Gambas
\nby Iman Karim
\nand Benoît Minisini\n

\nThanks to the Gambas team!") + } + } + { dwgGame DrawingArea + MoveScaled(31,1,93,93) + Background = &HFFFFFF& + Border = Border.Plain + Cached = True + } + { Button1 Button + MoveScaled(1,26,29,6) + Text = ("Spawn First Generation") + } + { Slider1 Slider + MoveScaled(1,44,29,4) + MinValue = 20 + MaxValue = 1000 + Step = 10 + PageStep = 50 + Value = 20 + } + { tmrEvolution #Timer + #MoveScaled(39,20) + Delay = 300 + } + { togEvolution ToggleButton + MoveScaled(1,33,29,6) + Text = ("Start Evolution") + } + { lblDelay TextLabel + MoveScaled(1,40,25,4) + Text = ("Evolution Delay: 20ms") + } + { TabStrip1 TabStrip + MoveScaled(1,49,29,45) + Count = 2 + Index = 0 + Text = ("Survival") + { chkEight CheckBox + MoveScaled(2,36,20,3) + Text = ("8 Neighbours") + Tristate = True + Value = CheckBox.True + } + { chkSeven CheckBox + MoveScaled(2,33,20,3) + Text = ("7 Neighbours") + Tristate = True + Value = CheckBox.True + } + { chkSix CheckBox + MoveScaled(2,30,20,3) + Text = ("6 Neighbours") + Tristate = True + Value = CheckBox.True + } + { chkFive CheckBox + MoveScaled(2,27,20,3) + Text = ("5 Neighbours") + Tristate = True + Value = CheckBox.True + } + { chkFour CheckBox + MoveScaled(2,24,20,3) + Text = ("4 Neighbours") + Tristate = True + Value = CheckBox.True + } + { chkThree CheckBox + MoveScaled(2,21,20,3) + Text = ("3 Neighbours") + Tristate = True + } + { chkTwo CheckBox + MoveScaled(2,18,20,3) + Text = ("2 Neighbours") + Tristate = True + Value = CheckBox.None + } + { chkOne CheckBox + MoveScaled(2,15,20,3) + Text = ("1 Neighbour") + Tristate = True + Value = CheckBox.True + } + { chkZero CheckBox + MoveScaled(2,12,20,3) + Text = ("0 Neighbour") + Tristate = True + Value = CheckBox.True + } + { TextLabel1 TextLabel + MoveScaled(1,1,25,10) + Text = ("Select the Count of Neighbours where a cell will die or keep its state.") + Alignment = Align.Top + } + Index = 1 + Text = ("Options") + { TextLabel3 TextLabel + MoveScaled(1,1,26,12) + Text = ("Set here the probability that a Cell will be born or not when you spawn the first Generation.") + Alignment = Align.Top + } + { Slider2 Slider + MoveScaled(1,14,25,4) + Value = 50 + } + { Label1 Label + MoveScaled(1,18,7,3) + Text = ("Alive") + } + { Label2 Label + MoveScaled(19,18,7,3) + Text = ("Dead") + Alignment = Align.Right + } + { chkBorder CheckBox + MoveScaled(1,36,24,4) + Text = ("Draw cell borders") + } + { chkSmall CheckBox + MoveScaled(1,24,24,4) + Text = ("Small generation") + } + { chkSymetryV CheckBox + MoveScaled(1,32,24,4) + Text = ("Vertical symetry") + } + { chkSymetryH CheckBox + MoveScaled(1,28,24,4) + Text = ("Horizontal symmetry") + } + Index = 0 + } +} diff --git a/app/examples/Games/GameOfLife/glob2-icon-48x48.png b/app/examples/Games/GameOfLife/glob2-icon-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..5ab8fccb1727f6c93db8e5488cc8ac2d0f4ca335 GIT binary patch literal 3390 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 $aEnemies[$aEnemies.Max].Y Or If iY < $aEnemies[0].Y Then Return Null + For iInd = 0 To $aEnemies.Max + If iY = $aEnemies[iInd].Y Then Break + Next + For iInd = iInd To $aEnemies.Max + If iY <> $aEnemies[iInd].Y Then Break + ' The ship look strings have spaces around them but a 'hit' is + ' only to hit the ship, not the spaces ;-) + If iX > $aEnemies[iInd].X And If iX < $aEnemies[iInd].X + $aEnemies[iInd].Width - 1 Then Return $aEnemies[iInd] + Next + Return Null +End + +Public Sub Enemy_Destroyed() + Dim iInd, iJ As Integer + Dim iAdd As Integer + Dim hEnemy As Enemy + Dim iShift As Integer = 0 + + ' Higher level ships crumble into lower level ones >:-) + iInd = $aEnemies.FindByRef(Last) + $aEnemies.Remove(iInd) + Last.Undraw() + iAdd = Last.Type - 1 + If Not iAdd Then Goto _Out + ' Insert the new ships directly where the destroyed one was + ' shifting all successors forward which is even more diabolic + For iJ = 0 To iAdd + hEnemy = New Enemy(Last.Type - 1) As "Enemy" + hEnemy.Init($hWnd, Last.Shifted + iShift) + $aEnemies.Add(hEnemy, iInd + iJ) + iShift += hEnemy.Width + Next + For iInd += iAdd + 1 To $aEnemies.Max + $aEnemies[iInd].Move(iShift - Last.Width) + Next +_Out: + If $aEnemies.Count = 0 Then Raise Triumph +End + +Public Sub Enemy_SuperDestroyed() + $aEnemies.Remove($aEnemies.FindByRef(Last)) + Last.Undraw() + If $aEnemies.Count = 0 Then Raise Triumph +End + +Public Sub PlayerDestroyed() + Raise GameOver +End + +Public Sub Draw() + Dim hEnemy As Enemy + + For Each hEnemy In $aEnemies + hEnemy.Draw() + Next +End diff --git a/app/examples/Games/Invaders/.src/Enemy.class b/app/examples/Games/Invaders/.src/Enemy.class new file mode 100644 index 00000000..8e83aaa7 --- /dev/null +++ b/app/examples/Games/Invaders/.src/Enemy.class @@ -0,0 +1,161 @@ +' Gambas class file + +' Types in ascending difficulty +Public Enum Normal = 1, Tough, Borg, BorgQueen +' See looking strings below +Public Const AverageWidth As Integer = 5 + +Property X As Integer +Property Y As Integer +'' Shift distance from 0,0 (may *not* equal Y * Window.Width + X) +Property Read Shifted As Integer +Property Read Width As Integer +Property Read Type As Integer + +Event Destroyed +Event SuperDestroyed + +Private $iType As Integer +Private $sLook As String +Private $iWidth As Integer +Private $iColor As Integer + +Private $hWnd As Window +Private $iX As Integer +Private $iY As Integer +Private $iShifted As Integer + +Public Sub _new(Optional iType As Integer) + ' Calculate a fair share of enemies + If Not iType Then + Select Case CInt(Rnd(0, 100)) + Case 0 To 69 + iType = Normal + Case 70 To 94 + iType = Tough + Case 95 To 98 + iType = Borg + Case 99 + iType = BorgQueen + End Select + Endif + Select Case iType + Case Normal + $sLook = " -o- " + $iColor = Color.Green + Case Tough + $sLook = " ,^, " + $iColor = Color.Cyan + Case Borg + $sLook = " [%] " + $iColor = Color.Magenta + Case BorgQueen + $sLook = " " + $iColor = Color.Red + End Select + $iWidth = Len($sLook) + $iType = iType +End + +Public Sub Init(hWnd As Window, Optional iShift As Integer) + $hWnd = hWnd + $iX = 0 + $iY = 0 + $iShifted = 0 + If iShift Then Shift(iShift) +End + +Public Sub Draw() + $hWnd.Print($sLook, $iX, $iY,, Pair[$iColor, $hWnd.Background]) +End + +Public Sub Undraw() + $hWnd.Print(Space$($iWidth), $iX, $iY) +End + +Public Sub Move(Optional iShift As Integer = 1) + Undraw() + Shift(iShift) + '' FIXME: Value is arbitrary but works kind of + If CInt(Rnd(0, 2000)) = 0 Then + ' Resistance is futile + Missiles.Shoot(Missile.Borg, $iX, $iY + 1) + Endif +End + +'' Shift this ship some places in its row (probably into some other row). +'' Even very huge values are supported. +'' +'' The mathematics in this function should guarantee that no ship ever +'' covers another (accidentally) and that they always stay togehter +'' tightly. +Public Sub Shift(iShift As Integer) + Dim iCur As Integer + + $iShifted += iShift + While iShift <> 0 + ' Handle one row at max at a time + If iShift > $hWnd.Width Then + iCur = $hWnd.Width + Else + iCur = iShift + Endif + iShift -= iCur + ' All even rows go to the right, all odd lines to the left + Select Case $iY % 2 + Case 0 + $iX += iCur + If $iX + $iWidth >= $hWnd.Width Then + ' Goes to an odd row, must start at the end of the row + While $iX + $iWidth >= $hWnd.Width + $iX = ($iX + $iWidth) % $hWnd.Width + Wend + $iX = $hWnd.Width - $iX - $iWidth + Inc $iY + Endif + Case 1 + $iX -= iCur + If $iX < 0 Then + ' Goes to an even row, must start at the beginning + $iX = (- $iX) % $hWnd.Width + Inc $iY + Endif + End Select + Wend +End + +Public Sub Destroy() + Raise Destroyed +End + +Public Sub SuperDestroy() + Raise SuperDestroyed +End + +Private Function X_Read() As Integer + Return $iX +End + +Private Sub X_Write(Value As Integer) + $iX = Value +End + +Private Function Y_Read() As Integer + Return $iY +End + +Private Sub Y_Write(Value As Integer) + $iY = Value +End + +Private Function Width_Read() As Integer + Return $iWidth +End + +Private Function Type_Read() As Integer + Return $iType +End + +Private Function Shifted_Read() As Integer + Return $iShifted +End diff --git a/app/examples/Games/Invaders/.src/MMain.module b/app/examples/Games/Invaders/.src/MMain.module new file mode 100644 index 00000000..7cde3c93 --- /dev/null +++ b/app/examples/Games/Invaders/.src/MMain.module @@ -0,0 +1,139 @@ +' Gambas module file + +' TODO: Make real setup for playing +' TODO: There is an occasional flickering when shooting and the ships are already near the player and few + +Public X As Integer +Public Y As Integer +' Hardcoded corresponding to the strings in Draw() below! +Public Width As Integer = 3 +Public Height As Integer = 2 + +Private $hEnemies As Timer +Private $hMissiles As Timer + +Public Sub Main() + ' Debug aid. Watch /tmp/pipe with "tail -f" + ' Dim hPipe As Stream + ' + ' hPipe = Pipe "/tmp/pipe" For Write + ' Error To #hPipe + + Screen.Cursor = Cursor.Hidden + Screen.Echo = False + Window.Attributes = Attr.Bold + Object.Attach(Window, Me, "Window") + Window.SetFocus() + + X = (Window.Width - Width) \ 2 + Y = Window.Height - Height + + Object.Attach(Enemies, Me, "Enemies") + + Draw() + ' Populate ca. 1/3 of the screen with enemies. It will actually look more + ' because of the enemy arrangement. But that's good :-) + Enemies.Init(Window, Window.Width * Window.Height / 3 / Enemy.AverageWidth) + Missiles.Init(Window) + Window.Buffered = True + + $hEnemies = New Timer As "Enemies" + ' TODO: The Timers are made for a 80*24 terminal. + ' Make it 100 and you'll likely win, 75 really depends on the amount of Borg for me... + $hEnemies.Delay = 75 + $hMissiles = New Timer As "Missiles" + $hMissiles.Delay = 50 + + $hEnemies.Start() + $hMissiles.Start() +End + +Public Sub Window_Read() + Select Window.Read() + ' Navigate left/right + Case Key.Left + If X = 0 Then Return + Undraw() + Dec X + Draw() + Case Key.Right + If X + Width >= Window.Width Then Return + Undraw() + Inc X + Draw() + ' Navigate up/down + Case Key.Up + If Y <= 0 Then Return + Undraw() + Dec Y + Draw() + Case Key.Down + If Y >= Window.Height - Height Then Return + Undraw() + Inc Y + Draw() + ' Shoot normal missile + Case Key[" "] + Missiles.Shoot(Missile.Normal, X, Y - 1) + 'If Not $hMissiles.Enabled Then $hMissiles.Start() + ' Shoot super missile + Case Key["v"] + Missiles.Shoot(Missile.Super, X, Y - 1) + 'If Not $hMissiles.Enabled Then $hMissiles.Start + ' Of course, manipulate the timers + Case Key["+"] + $hEnemies.Delay -= 10 + $hMissiles.Delay -= 5 + Case Key["-"] + $hEnemies.Delay += 10 + $hMissiles.Delay += 5 + End Select +End + +Public Sub Enemies_Triumph() + End("TRIUMPH!", Color.Yellow) +End + +Public Sub Enemies_GameOver() + End("GAME OVER!", Color.Blue) +End + +Private Sub End(sMessage As String, iColor As Integer) + $hEnemies.Stop() + $hMissiles.Stop() + '' TODO: The message display is glitchy + Window.Buffered = False + Window.PrintCenter(sMessage,, Pair[iColor, Window.Background]) + '' TODO: What now? Note that the user can still interact at this point... +End + +Private Sub Draw() + Window.Print(" | ", X, Y) + Window.Print("^'^", X, Y + 1) +End + +Private Sub Undraw() + Window.Print(Space$(Width), X, Y) + Window.Print(Space$(Width), X, Y + 1) +End + +Public Sub Enemies_Timer() + Enemies.Move() + If Not $hMissiles.Enabled Then + Enemies.Draw() + Screen.Refresh() + Endif +End + +Public Sub Missiles_Timer() + If Missiles.Move() And Missiles.Count = 0 Then + '$hMissiles.Stop() + Return + Endif + ' Missiles draw at a higher rate so we can safely redraw everything + ' here. Note that, thanks to ncurses' buffering, only the changed + ' parts are redrawn so that there is no overhead in more frequent + ' Screen.Refresh()es here. + Enemies.Draw() + Screen.Refresh() +End diff --git a/app/examples/Games/Invaders/.src/Missile.class b/app/examples/Games/Invaders/.src/Missile.class new file mode 100644 index 00000000..d06f6af0 --- /dev/null +++ b/app/examples/Games/Invaders/.src/Missile.class @@ -0,0 +1,84 @@ +' Gambas class file + +Public Enum Normal, {Super}, Borg + +Property Read Type As Integer + +Event Destroyed + +Private $iType As Integer +Private $sLook As String +Private $iColor As Integer +Private $iDir As Integer + +Private $hWnd As Window +Private $iX As Integer +Private $iY As Integer + +Public Sub _new(iType As Integer, iX As Integer, iY As Integer) + Select Case iType + Case Normal + $sLook = "|" + $iColor = Color.Yellow + $iDir = -1 + Case {Super} + $sLook = "*" + $iColor = Color.Red + $iDir = -1 + Case Borg + $sLook = "*" + $iColor = Color.Green + $iDir = 1 + End Select + $iType = iType + $iX = iX + $iY = iY +End + +Public Sub Init(hWnd As Window) + $hWnd = hWnd +End + +Public Function Move() As Boolean + Dim hEnemy As Enemy + + Undraw() + $iY += $iDir + If $iType <> Borg Then + hEnemy = Enemies.Hit($iX, $iY) + If hEnemy Then + If $iType = Normal Then + hEnemy.Destroy() + Else + hEnemy.SuperDestroy() + Draw() + Endif + Raise Destroyed + Return True + Endif + Else + If $iY >= MMain.Y And If $iY < MMain.Y + MMain.Height And If + $iX >= MMain.X And If $iX < MMain.X + MMain.Width Then + Enemies.PlayerDestroyed() + Draw() + Endif + Endif + If $iY = -1 Or $iY = $hWnd.Height Then + Raise Destroyed + Return True + Endif + Draw() + Return False +End + +Public Sub Draw() + Window.Print($sLook, $iX, $iY,, Pair[$iColor, $hWnd.Background]) +End + +Public Sub Undraw() + Window.Print(" ", $iX, $iY) +End + +Private Function Type_Read() As Integer + Return $iType +End diff --git a/app/examples/Games/Invaders/.src/Missiles.class b/app/examples/Games/Invaders/.src/Missiles.class new file mode 100644 index 00000000..1219bfb8 --- /dev/null +++ b/app/examples/Games/Invaders/.src/Missiles.class @@ -0,0 +1,65 @@ +' Gambas class file + +Create Static + +'' Maximum number of missiles concurrently displayed +Public Const MaxMissiles As Integer = 3 + +Property Read Count As Integer + +Private $aMissiles As Missile[] +Private $hWnd As Window +Private $iCount As Integer + +Public Sub _new() + $aMissiles = New Missile[] + $iCount = 0 +End + +Public Sub Init(hWnd As Window) + $hWnd = hWnd +End + +Public Function Move() As Boolean + Dim hMissile As Missile + Dim bRes As Boolean = False + + For Each hMissile In $aMissiles + bRes = bRes Or hMissile.Move() + Next + Return bRes +End + +Public Sub Shoot(iType As Integer, iX As Integer, iY As Integer) + Dim hMissile As Missile + + Select Case iType + Case Missile.Normal + ' Consumes one missile + If $iCount >= MaxMissiles Then Return + Inc $iCount + Case Missile.Super + ' Consumes all missiles + If $iCount Then Return + $iCount = MaxMissiles + Case Missile.Borg + ' Consumes none of the (player's) missiles + End Select + hMissile = New Missile(iType, iX, iY) As "Missile" + hMissile.Init($hWnd) + $aMissiles.Add(hMissile) +End + +Public Sub Missile_Destroyed() + Last.Undraw() + $aMissiles.Remove($aMissiles.FindByRef(Last)) + If Last.Type = Missile.Normal Then + Dec $iCount + Else If Last.Type = Missile.Super Then + $iCount = 0 + Endif +End + +Private Function Count_Read() As Integer + Return $iCount +End diff --git a/app/examples/Games/Invaders/invaders.png b/app/examples/Games/Invaders/invaders.png new file mode 100644 index 0000000000000000000000000000000000000000..cfad58b988e93f734bb650be4630ac68286af67c GIT binary patch literal 125 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH3?%tPCZz)@&H$efS0K$$&+xy2q4t+i1(2=p z>Eak7aXC3bfp-Jj1~!3&jy5Jo4o0_YS2j#-k=U`dy@`>TIho@`P$0XJqzePXvzyAh TnC9;RnaAMi>gTe~DWM4fgJd8H literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/.directory b/app/examples/Games/MineSweeper/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Games/MineSweeper/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Games/MineSweeper/.hidden/screenshots/2014-12-14.png b/app/examples/Games/MineSweeper/.hidden/screenshots/2014-12-14.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b09fa6cff0de41ae3a53448ee3113bb68fa60 GIT binary patch literal 48121 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 M07*qoM6N<$f}|9lUH||9 literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/.lang/cs.mo b/app/examples/Games/MineSweeper/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..ac2e993bca4627690d2c2b3fc51e9d0437e8702a GIT binary patch literal 703 zcmYMwzi$&U6bEnv<<~Jl8DL@XbP__>!hlp=L)BAqO`_h9kY9)_$W2}^M-pG~IfX7P z%o#f>{UsMkb~v8-D};1v_7-Cu-^GbIBW(61tQl@TM3zuh0c`A_c}qqBQNxM$q&7Hue)S zWGM)C$LU6mRh0004=(P8ot;;&o z%ONSpm;G#jTf2PgAk`MLdy~ayDfsl_LDljHAF5+!*OXv(XOzjs9Pipwv*ju5o}VpG cM}j%5kuBz=&0`{oa9wdk^85@Hzg89h0Bv`ly8r+H literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/.lang/cs.po b/app/examples/Games/MineSweeper/.lang/cs.po new file mode 100644 index 00000000..dece6115 --- /dev/null +++ b/app/examples/Games/MineSweeper/.lang/cs.po @@ -0,0 +1,52 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FMain.form:24 +msgid "Minesweeper" +msgstr "-" + +#: FMain.form:30 +msgid "&Game" +msgstr "&Hra" + +#: FMain.form:33 +msgid "&Change Game Parameters" +msgstr "&Změna parametrů hry" + +#: FMain.form:38 +msgid "&Quit" +msgstr "&Ukončit" + +#: FSettings.form:21 +msgid "Settings" +msgstr "Nastavení" + +#: FSettings.form:37 +msgid "Width" +msgstr "Výška" + +#: FSettings.form:54 +msgid "Height" +msgstr "Víška" + +#: FSettings.form:71 +msgid "Number of mines" +msgstr "Počet min" + +#: FSettings.form:91 +msgid "OK" +msgstr "-" + +#: FSettings.form:97 +msgid "Cancel" +msgstr "Zrušit" + diff --git a/app/examples/Games/MineSweeper/.lang/ja.mo b/app/examples/Games/MineSweeper/.lang/ja.mo new file mode 100644 index 0000000000000000000000000000000000000000..b745bb188d330f05e974554c097148fbc75037a6 GIT binary patch literal 766 zcmYL_ON-M`7>18J-lC!-h#MDY6U2mJ(z zII#zz)4*}yeqcJ`B=7)yUr3k%iXA%eIM4>}1zrap0@i?H{w5HCZh^$T0Gf^g9{?rb zQ=pjd1CIjNfTw^zlJ8$Yv0@u2?*B>hf0I0g)zC_cWh+??&)TY$KYPtC7?Mdsrc?srg)PGTK*cjv)5HbMGH}MQw7fa3WwRxt zT)qsgp3@}`RVtLURcva7TveRRnC5iLo$v!^)T3g_%3H?B-}#AilJ0wqdT{Pu2mCO4 zmrZtB#Pu%XdXoepWtXaz8D(nJCu}X!Kry_!-+&*W;;EX;B*f!o9>3x73m$*t(RUs_ z-0rVz3?8LqJ&kzuUZmsaDLI!$TOXtKFZWaOyeO>j_$7}&@aQX#-=VFw*L-QP)BD7i nda&{tk6sOnpN9|Fu#7gItnNI9`e3uSiWIba1#R}\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FSettings.form:97 +msgid "Cancel" +msgstr "キャンセル" + +#: FMain.form:26 +msgid "&Change Game Parameters" +msgstr "パラメータを設定(&C)" + +#: FMain.form:23 +msgid "&Game" +msgstr "ゲーム(&G)" + +#: FSettings.form:54 +msgid "Height" +msgstr "縦の長さ" + +#: .project:1 FMain.form:19 +msgid "Minesweeper" +msgstr "マインスイーパ" + +#: FSettings.form:71 +msgid "Number of mines" +msgstr "地雷の数" + +#: FSettings.form:91 +msgid "OK" +msgstr "-" + +#: FMain.form:30 +msgid "&Quit" +msgstr "終了(&Q)" + +#: FSettings.form:21 +msgid "Settings" +msgstr "設定" + +#: FSettings.form:37 +msgid "Width" +msgstr "横の長さ" + diff --git a/app/examples/Games/MineSweeper/.lang/zh.mo b/app/examples/Games/MineSweeper/.lang/zh.mo new file mode 100644 index 0000000000000000000000000000000000000000..a101fd0eb13f5a0e9a2de4e64b157607f3ae73ef GIT binary patch literal 714 zcmYMvy>HV%6aerClrMt>?EnkIO;ID{76zos4OK12O{3aznvWt_&?UJ#hU6UV3m*$Y zNJwQ00|f*KiGePaOs7&l3Ii*D0~2=IKY*Qg#n2}`{qoN5-o5-B8(9U66PV{P1?DK` z4@?=S4*;A+N72J*Hgys`g7u};94coR=qc1e52Dx6V`u}F_iv)V(Oc*l)Ws$_=RT^S zkJ9={>hsh$=yCl1F0J3AC$Zj4>+dLz+zYwKzc`fskZ+7&${pl8awoj9H}fd^KQcy} za+i?;?J`oP0U}rsLPj1yX0h)HFeq=b4$L#pZ40nH&cZv4^;iI2J^4}1Q^7QH z)zmAh?WlR3%sZN0Jb%q8nTkb2q1FP*!wwaGppm({Ws#C@n=_WR^6%Pde$-t=4euQf8&@Pq=tzD3E?kIB|nvNDJV50ll6@r;p$ zzXvc}`y79HFrHb=LOghuY_7oW`iuC}8YGX`cVB&g`1J;Ymtgodildj3w{R7FXxa0r; literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/.lang/zh.po b/app/examples/Games/MineSweeper/.lang/zh.po new file mode 100644 index 00000000..183467dc --- /dev/null +++ b/app/examples/Games/MineSweeper/.lang/zh.po @@ -0,0 +1,52 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FSettings.form:97 +msgid "Cancel" +msgstr "取消" + +#: FMain.form:26 +msgid "&Change Game Parameters" +msgstr "修改游戏参数(&C)" + +#: FMain.form:23 +msgid "&Game" +msgstr "游戏" + +#: FSettings.form:54 +msgid "Height" +msgstr "高度" + +#: FMain.form:19 .project:1 +msgid "Minesweeper" +msgstr "扫雷" + +#: FSettings.form:71 +msgid "Number of mines" +msgstr "地雷数" + +#: FSettings.form:91 +msgid "OK" +msgstr "确定" + +#: FMain.form:30 +msgid "&Quit" +msgstr "离开(&Q)" + +#: FSettings.form:21 +msgid "Settings" +msgstr "参数设置" + +#: FSettings.form:37 +msgid "Width" +msgstr "宽度" + diff --git a/app/examples/Games/MineSweeper/.lang/zh_TW.mo b/app/examples/Games/MineSweeper/.lang/zh_TW.mo new file mode 100644 index 0000000000000000000000000000000000000000..fd66bd6b12d17f29fa64795c42dd26f8311cf248 GIT binary patch literal 726 zcmYMwy>HV%6aerClrJ+t8DL?!$x4LW!hlqnfND8z8r6=|d=$ZguF2IgB3^U8tTTm}&;~o|*}-{Ox@l{U5sBqC8-v zKs$_-sX!zXG3e?5^x0l0!KS>y+Az(+pe4ZzIg1}K)@1@pz0N!nBw8RHbqf9rRGE|^ z4`R3xHf0OSBD%vG(kM2KIqVxo+$3eop0NrJnR6=DqF>U=zENTKL-oxwbx7eV);0vyd#U_CGJe;N6R4cLVlz-X^a;4%W8e0K5x-)_0Swm$3I{Q;~dl HuL}18s&Bd_ literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/.lang/zh_TW.po b/app/examples/Games/MineSweeper/.lang/zh_TW.po new file mode 100644 index 00000000..681019e6 --- /dev/null +++ b/app/examples/Games/MineSweeper/.lang/zh_TW.po @@ -0,0 +1,52 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FMain.form:19 +msgid "Minesweeper" +msgstr "踩地雷" + +#: FMain.form:23 +msgid "&Game" +msgstr "遊戲(&G)" + +#: FMain.form:26 +msgid "&Change Game Parameters" +msgstr "修改遊戲參數(&C)" + +#: FMain.form:30 +msgid "&Quit" +msgstr "離開(&Q)" + +#: FSettings.form:21 +msgid "Settings" +msgstr "設定" + +#: FSettings.form:37 +msgid "Width" +msgstr "寬度" + +#: FSettings.form:54 +msgid "Height" +msgstr "高度" + +#: FSettings.form:71 +msgid "Number of mines" +msgstr "地雷數" + +#: FSettings.form:91 +msgid "OK" +msgstr "確定" + +#: FSettings.form:97 +msgid "Cancel" +msgstr "取消" diff --git a/app/examples/Games/MineSweeper/.project b/app/examples/Games/MineSweeper/.project new file mode 100644 index 00000000..eabe3b13 --- /dev/null +++ b/app/examples/Games/MineSweeper/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Minesweeper +Startup=FMain +Icon=image/expr_win.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.form +Description="Minesweeper.\n\nThis example is an implementation of the Minesweeper game." +Authors="Timothy Lin" +TabSize=3 +Translate=1 +Language=en +Vendor=Example +Packager=1 +Tags=Game,LogicGame +Screenshot=.hidden/screenshots/2014-12-14.png +CreateMenu=1 diff --git a/app/examples/Games/MineSweeper/.src/FMain.class b/app/examples/Games/MineSweeper/.src/FMain.class new file mode 100644 index 00000000..224b4e6a --- /dev/null +++ b/app/examples/Games/MineSweeper/.src/FMain.class @@ -0,0 +1,249 @@ +' Gambas class file + +Private img_cover As Picture = Picture.Load("image/cover.png") +Private img_cover_hovered As Picture = Picture.Load("image/coveron.png") +Private img_false As Picture = Picture.Load("image/false.png") +Private img_flag As Picture = Picture.Load("image/flag.png") +Private img_face_normal As Picture = Picture.Load("image/expr_normal.png") +Private img_face_win As Picture = Picture.Load("image/expr_win.png") +Private img_face_lose As Picture = Picture.Load("image/expr_lose.png") +Private img_face_o As Picture = Picture.Load("image/expr_o.png") +Private img_mine As Picture +Private img_num[10] As Picture + +Private dlgSettings As FSettings = New FSettings + +Private game_width As Integer = 20 +Private game_height As Integer = 15 +Private game_mines As Integer = game_width * game_height / 10 + +Private game As MineSweeperGame +Private blocks As PictureBox[] + +Private play_time As Integer = 0 + +Public Sub _new() + ' img_num[0] is none, img_num[9] is mine + ' img_num[1~8] is the number of adjacent mines + ' load number images + Dim i As Integer + Dim a As Integer[] + img_num[0] = Picture.Load("image/empty.png") + img_num[9] = Picture.Load("image/mine.png") + For i = 1 To 8 + img_num[i] = Picture.Load("image/number_" & Str(i) & ".png") + Next + img_mine = img_num[9] + + btnStatus.Resize(img_face_normal.Width + 10, img_face_normal.Height + 10) + btnStatus.Picture = img_face_normal + + Timer1.Enabled = False +End + +' ----- Window Events ----- + +Public Sub Form_Open() + ReDimGame(game_width, game_height, game_mines) + 'Me.Maximized = True +End + +' Public Sub Form_Resize() +' adjust_components() +' End + +Public Sub Block_MouseDown() + Dim index As Integer = Last.Tag + Dim x As Integer = index Mod game_width + Dim y As Integer = index / game_width + + If Mouse.Left Then + If game.Status = game.GAME_STARTED And game.GetBlockStatus(x, y) = game.BLOCK_COVERED Then + btnStatus.Picture = img_face_o + Endif + Endif +End + + +Public Sub Block_MouseUp() + Dim index As Integer = Last.Tag + Dim x As Integer = index Mod game_width + Dim y As Integer = index / game_width + 'Message("Clicked (" & x & "," & y & ")") + + If game.Status = game.GAME_STARTED Then + btnStatus.Picture = img_face_normal + Endif + + ' Skip the event if the mouse button is released outside the block it is pressed + If Mouse.X > blocks[0, 0].Width Or Mouse.y > blocks[0, 0].Height Then Return + If Mouse.X < 0 Or Mouse.y < 0 Then Return + + If Mouse.Right Then + game.ToggleFlag(x, y) + Else If Mouse.Left Then + game.OpenBlock(x, y) + Endif +End + +' highlight the block under cursor +Public Sub Block_Enter() + Dim index As Integer = Last.Tag + Dim x As Integer = index Mod game_width + Dim y As Integer = index / game_width + + If game.Status = game.GAME_WIN Or game.Status = game.GAME_LOSE Then Return + + If game.GetBlockStatus(x, y) = game.BLOCK_COVERED Then + blocks[x, y].Picture = img_cover_hovered + Endif +End + +' un-highlight the block under cursor +Public Sub Block_Leave() + Dim index As Integer = Last.Tag + Dim x As Integer = index Mod game_width + Dim y As Integer = index / game_width + + If game.Status = game.GAME_WIN Or game.Status = game.GAME_LOSE Then Return + + If game.GetBlockStatus(x, y) = game.BLOCK_COVERED Then + blocks[x, y].Picture = img_cover + Endif +End + +Public Sub btnStatus_Click() + game.Reset() +End + +Public Sub Game_OnStart() + Timer1.Enabled = True +End + +Public Sub Game_OnEnd() + Timer1.Enabled = False +End + +Public Sub Game_OnRefresh() + Dim x, y As Integer + Dim s As Integer + For x = 0 To game.Width - 1 + For y = 0 To game.Height - 1 + Select Case game.Status + Case game.GAME_READY + blocks[x, y].Picture = img_cover + btnStatus.Picture = img_face_normal + play_time = 0 ' reset the timer + Timer1.Enabled = False + refresh_timebox() + Case game.GAME_STARTED + s = game.GetBlockStatus(x, y) + If s = game.BLOCK_COVERED Then + blocks[x, y].Picture = img_cover + Else If s = game.BLOCK_FLAGGED Then + blocks[x, y].Picture = img_flag + Else + blocks[x, y].picture = img_num[game.GetBlockNumber(x, y)] + Endif + Case game.GAME_WIN, game.GAME_LOSE + If game.GetBlockStatus(x, y) = game.BLOCK_FLAGGED Then + If game.GetBlockNumber(x, y) = game.NUM_MINE Then + blocks[x, y].Picture = img_flag + Else + blocks[x, y].Picture = img_false + Endif + Else ' the block has been uncovered + blocks[x, y].Picture = img_num[game.GetBlockNumber(x, y)] + Endif + btnStatus.Picture = IIf(game.Status = game.GAME_WIN, img_face_win, img_face_lose) + End Select + Next + Next + refresh_flagbox() +End + +Public Sub Timer1_Timer() + play_time += 1 + refresh_timebox() +End + +Public Sub menuQuit_Click() + Me.Close() +End + +Public Sub menuReDim_Click() + dlgSettings.FieldWidth = game_width + dlgSettings.FieldHeight = game_height + dlgSettings.MineCount = game_mines + If dlgSettings.ShowModal() Then + game_width = dlgSettings.FieldWidth + game_height = dlgSettings.FieldHeight + game_mines = dlgSettings.MineCount + ReDimGame(game_width, game_height, game_mines) + Endif +End + + + +' ----- Private Functions ----- + + +' Generate the a width x height board and adjust control positions +Private Sub ReDimGame(width As Integer, height As Integer, mines As Integer) + Dim x, y As Integer + Dim blk As PictureBox + + blocks = New PictureBox[width, height] + + For x = 0 To width - 1 + For y = 0 To height - 1 + blk = New PictureBox(panelBoard) + With blk + .Picture = img_cover + .Resize(img_cover.Width, img_cover.Height) + .Move(x * .Width, y * .Height) + .Tag = x + y * width + Object.Attach(blk, Me, "Block") + End With + blocks[x, y] = blk + Next + Next + + game = New MineSweeperGame(width, height, mines) + Object.Attach(game, Me, "Game") + game.Reset() + + panelBoard.Resize(width * img_cover.Width, height * img_cover.Height) + 'adjust_components() +End + +' Private Sub adjust_components() +' center_control(panelBoard) +' center_control_horizontal(btnStatus) +' btnStatus.top = panelBoard.top - btnStatus.Height +' TimeBox.Left = 0 +' TimeBox.Top = panelBoard.Top - TimeBox.Height +' FlagBox.Left = Me.Width - FlagBox.Width +' FlagBox.Top = panelBoard.Top - FlagBox.Height +' End + +Private Sub refresh_timebox() + lblTime.Text = Str(play_time) +End + +Private Sub refresh_flagbox() + lblFlags.Text = Str(game.FlagCount) +End + + + +' Center the control in the main window +' Private Sub center_control(c As Control) +' c.Move((Me.Width - c.Width) / 2, (Me.Height - c.Height) / 2) +' End +' +' ' Center the control horizontally in the main window +' Private Sub center_control_horizontal(c As Control) +' c.left = (Me.Width - c.Width) / 2 +' End + diff --git a/app/examples/Games/MineSweeper/.src/FMain.form b/app/examples/Games/MineSweeper/.src/FMain.form new file mode 100644 index 00000000..7e6d2cd6 --- /dev/null +++ b/app/examples/Games/MineSweeper/.src/FMain.form @@ -0,0 +1,77 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,60,66) + Text = ("Minesweeper") + Arrangement = Arrange.Vertical + Spacing = True + Margin = True + { menuGame Menu + Text = Shortcut(("Game"), "G") + { menuReDim Menu + Text = Shortcut(("Change Game Parameters"), "C") + Picture = Picture["icon:/32/edit"] + } + { menuQuit Menu + Text = Shortcut(("Quit"), "Q") + Picture = Picture["icon:/32/quit"] + } + } + { HBox1 HBox + MoveScaled(0,2,59,15) + Spacing = True + Margin = True + { PictureBox2 PictureBox + MoveScaled(0,0,13,13) + Picture = Picture["icon:/64/clock"] + Stretch = True + } + { lblTime Label + MoveScaled(13,2,12,7) + Font = Font["20"] + AutoResize = True + } + { Panel2 Panel + MoveScaled(25,3,1,7) + Expand = True + } + { btnStatus Button + MoveScaled(26,2,7,7) + } + { Panel3 Panel + MoveScaled(33,3,1,7) + Expand = True + } + { PictureBox1 PictureBox + MoveScaled(33,0,13,13) + Picture = Picture["image/bigflag.png"] + Stretch = True + } + { lblFlags Label + MoveScaled(44,5,15,4) + Font = Font["20"] + AutoResize = True + } + } + { Panel6 Panel + MoveScaled(25,17,10,1) + } + { Panel4 HBox + MoveScaled(1,19,58,35) + Expand = True + { Panel1 Panel + MoveScaled(4,7,1,13) + Expand = True + } + { panelBoard Panel + MoveScaled(7,6,47,25) + } + { Panel5 Panel + MoveScaled(55,7,1,20) + Expand = True + } + } + { Timer1 #Timer + #MoveScaled(2,54) + } +} diff --git a/app/examples/Games/MineSweeper/.src/FSettings.class b/app/examples/Games/MineSweeper/.src/FSettings.class new file mode 100644 index 00000000..23366286 --- /dev/null +++ b/app/examples/Games/MineSweeper/.src/FSettings.class @@ -0,0 +1,66 @@ +' Gambas class file + +Property FieldWidth As Integer +Property FieldHeight As Integer +Property MineCount As Integer + +Public Sub Run() As Boolean + + Return Not Me.ShowModal() + +End + +Public Sub btnOK_Click() + + Me.Close(True) + +End + +Public Sub btnCancel_Click() + + Me.Close + +End + +Public Sub spinWidth_Change() + UpdateMineRange() + spinMines.Value = spinWidth.Value * spinHeight.Value / 10 +End + +Public Sub spinHeight_Change() + UpdateMineRange() + spinMines.Value = spinWidth.Value * spinHeight.Value / 10 +End + +Public Sub Form_Open() + UpdateMineRange() +End + +Private Sub UpdateMineRange() + spinMines.MinValue = 1 + spinMines.MaxValue = spinWidth.Value * spinHeight.Value - 1 +End + +Private Function FieldWidth_Read() As Integer + Return spinWidth.Value +End + +Private Sub FieldWidth_Write(Value As Integer) + spinWidth.Value = Value +End + +Private Function FieldHeight_Read() As Integer + Return spinHeight.Value +End + +Private Sub FieldHeight_Write(Value As Integer) + spinHeight.Value = Value +End + +Private Function MineCount_Read() As Integer + Return spinMines.Value +End + +Private Sub MineCount_Write(Value As Integer) + spinMines.Value = Value +End diff --git a/app/examples/Games/MineSweeper/.src/FSettings.form b/app/examples/Games/MineSweeper/.src/FSettings.form new file mode 100644 index 00000000..0c4c815a --- /dev/null +++ b/app/examples/Games/MineSweeper/.src/FSettings.form @@ -0,0 +1,71 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,61,30) + Text = ("Settings") + Resizable = False + Arrangement = Arrange.Vertical + Spacing = True + Margin = True + { VBox1 VBox + MoveScaled(1,1,54,22) + { HBox2 HBox + MoveScaled(2,1,50,6) + { lblX Label + MoveScaled(1,1,24,4) + Expand = True + AutoResize = True + Text = ("Width") + } + { spinWidth SpinBox + MoveScaled(14,1,9,4) + MinValue = 5 + MaxValue = 50 + } + } + { HBox4 HBox + MoveScaled(2,8,50,6) + { lblY Label + MoveScaled(1,1,24,4) + Expand = True + AutoResize = True + Text = ("Height") + } + { spinHeight SpinBox + MoveScaled(26,1,9,4) + MinValue = 5 + MaxValue = 25 + } + } + { HBox6 HBox + MoveScaled(2,15,50,6) + { lblMines Label + MoveScaled(0,1,14,4) + Expand = True + AutoResize = True + Text = ("Number of mines") + } + { spinMines SpinBox + MoveScaled(37,1,9,4) + } + } + } + { HBox1 HBox + MoveScaled(1,25,59,4) + Spacing = True + { Panel1 Panel + MoveScaled(4,0,4,4) + Expand = True + } + { btnOK Button + MoveScaled(24,0,16,4) + Text = ("OK") + Default = True + } + { btnCancel Button + MoveScaled(41,0,16,4) + Text = ("Cancel") + Cancel = True + } + } +} diff --git a/app/examples/Games/MineSweeper/.src/MineSweeperGame.class b/app/examples/Games/MineSweeper/.src/MineSweeperGame.class new file mode 100644 index 00000000..209d035d --- /dev/null +++ b/app/examples/Games/MineSweeper/.src/MineSweeperGame.class @@ -0,0 +1,194 @@ +' Gambas class file + +Property Read Width As Integer +Property Read Height As Integer +Property Read Status As Integer +Property Read FlagCount As Integer + +Event OnStart() +Event OnEnd() +Event OnRefresh() + +Public Enum BLOCK_COVERED, BLOCK_FLAGGED, BLOCK_REVEALED +Public Enum GAME_READY, GAME_STARTED, GAME_WIN, GAME_LOSE +Public Enum NUM_MINE = 9 + +Private $width As Integer +Private $height As Integer +Private $blocks As New Integer[] +Private $status As New Integer[] +Private $mine_count As Integer +Private $flag_count As Integer +Private $covered_block_count As Integer +Private $game_state As Integer + +Public Sub _new(w As Integer, h As Integer, n As Integer) + $width = w + $height = h + $mine_count = n + $covered_block_count = 0 + $blocks = New Integer[w, h] + $status = New Integer[w, h] + Reset() +End + +Public Function Reset() + Dim x, y As Integer + For x = 0 To $width - 1 + For y = 0 To $height - 1 + $status[x, y] = BLOCK_COVERED + $blocks[x, y] = 0 + Next + Next + $game_state = GAME_READY + $covered_block_count = $width * $height + $flag_count = $mine_count + Raise OnRefresh() +End + +Public Function GetBlockNumber(x As Integer, y As Integer) As Integer + Return $blocks[x, y] +End + +Public Function GetBlockStatus(x As Integer, y As Integer) As Integer + Return $status[x, y] +End + +Public Sub OpenBlock(x As Integer, y As Integer) + If $game_state = GAME_READY Then + $game_state = GAME_STARTED + GenerateRandomMines($mine_count, x, y) + Raise OnStart() + Endif + + If $game_state = GAME_STARTED And $status[x, y] = BLOCK_COVERED + If $blocks[x, y] = NUM_MINE Then ' stepped on a mine + $game_state = GAME_LOSE + Raise OnEnd() + Else + RecursiveOpen(x, y) + If AllMinesFound() Then + FlagAllMines() + $game_state = GAME_WIN + Raise OnEnd() + Endif + Endif + Endif + + Raise OnRefresh() +End + +Public Sub ToggleFlag(x As Integer, y As Integer) + If $game_state = GAME_STARTED Then + If $status[x, y] = BLOCK_COVERED Then + $status[x, y] = BLOCK_FLAGGED + $flag_count -= 1 + Else If $status[x, y] = BLOCK_FLAGGED Then + $status[x, y] = BLOCK_COVERED + $flag_count += 1 + Else + Return + Endif + Raise OnRefresh() + Endif +End + + +Private Function Width_Read() As Integer + Return $width +End + +Private Function Height_Read() As Integer + Return $height +End + +Private Function Status_Read() As Integer + Return $game_state +End + +Private Function FlagCount_Read() As Integer + Return $flag_count +End + +' Generate n mines in the field and guarantee that (nx, ny) does not contain a mine. +Private Sub GenerateRandomMines(n As Integer, nx As Integer, ny As Integer) + Dim i As Integer + Dim x, y As Integer + Dim x2, y2 As Integer + Dim count As Integer = $width * $height + Dim index As Integer + + ' check if the range is correct + ' note that the field should contain at most (w*h-1) mines, not w*h + If n >= count Then Error.Raise("Index out of bounds") + + ' put n mines into the first blocks, and fill in zero in the rest + For i = 0 To count - 1 + x = i Mod $width + y = i / $width + $blocks[x, y] = IIf(i < n, NUM_MINE, 0) + Next + + ' if a mine is located in (nx, ny), then move the mine to the last block + If $blocks[nx, ny] = NUM_MINE Then Swap $blocks[nx, ny], $blocks[$width - 1, $height - 1] + + ' shuffle + For i = 0 To count - 1 + x = i Mod $width + y = i / $width + x2 = Int(Rnd(0, $width)) + y2 = Int(Rnd(0, $height)) + If (x = nx And y = ny) Or (x2 = nx And y2 = ny) Then Continue ' skip (nx, ny) + Swap $blocks[x, y], $blocks[x2, y2] + Next + + ' generate the numbers + For x = 0 To $width - 1 + For y = 0 To $height - 1 + If $blocks[x, y] = NUM_MINE Then + For x2 = x - 1 To x + 1 + For y2 = y - 1 To y + 1 + Try IncrementBlockMineCount(x2, y2) + Next + Next + Endif + Next + Next +End + +Private Sub IncrementBlockMineCount(x As Integer, y As Integer) + If $blocks[x, y] <> NUM_MINE Then $blocks[x, y] += 1 +End + +Private Sub RecursiveOpen(x As Integer, y As Integer) + Dim x2, y2 As Integer + If $status[x, y] = BLOCK_COVERED Then + $status[x, y] = BLOCK_REVEALED + $covered_block_count -= 1 + If $blocks[x, y] = 0 Then + For x2 = x - 1 To x + 1 + For y2 = y - 1 To y + 1 + RecursiveOpen(x2, y2) + Next + Next + Endif + Endif +Catch + +End + +Private Function AllMinesFound() As Boolean + Return $covered_block_count = $mine_count +End + +' Automatically mark all mines as flag +Private Sub FlagAllMines() + Dim x, y As Integer + For x = 0 To $width - 1 + For y = 0 To $height - 1 + If $blocks[x, y] = NUM_MINE Then + $status[x, y] = BLOCK_FLAGGED + Endif + Next + Next +End diff --git a/app/examples/Games/MineSweeper/image/bigflag.png b/app/examples/Games/MineSweeper/image/bigflag.png new file mode 100644 index 0000000000000000000000000000000000000000..334b47127d5a9adb5d4b4e2945f86defc2cb1678 GIT binary patch literal 6066 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/image/cover.png b/app/examples/Games/MineSweeper/image/cover.png new file mode 100644 index 0000000000000000000000000000000000000000..c366339327dbbbd66eae130f249411c7bf59be01 GIT binary patch literal 331 zcmV-R0kr;!P)xN#0001lP)t-sOw033 z%k)gR^Gvw(+wlC`@%>7*^wRA7O11Mxvh>8}`$w|#M6UC*-ughQ^PklDpVat3s`5Ri z^NY&(i^}&srt&$V^L4%Vb-ngEq4G4F@?Nm@FqZO3qx3A1@;8_BH<$7(k?|*s@+XV& zBZl!Gg7F-F@f?2e7<%y-dhix@@Dz0Ln=38B0001jNklGSSqJkr$LX;^X5X;#-^^1!ovqD^Nm@Q(pV=BabE$fJs2gGBe znut;-#B;0Lh}IXx?#gtC{f+4n@54Am#&JS4#+ryV7V(imd}a`x^Dg3@M-1VW3W)Cv dE`Kug*B3jkAOVA4(5V0b002ovPDHLkV1i*{rYisd literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/image/coveron.png b/app/examples/Games/MineSweeper/image/coveron.png new file mode 100644 index 0000000000000000000000000000000000000000..95f9be6eb96d6ccc33088f3c87606d73e9a425e7 GIT binary patch literal 238 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/image/false.png b/app/examples/Games/MineSweeper/image/false.png new file mode 100644 index 0000000000000000000000000000000000000000..0f649881a704583e26366d324ff53ff8c5eb0c97 GIT binary patch literal 1021 zcmVk#hqzPa`LXtvA$p!^Q3POc0A|Z8ERjSk<(EbqAU(iLH4T~)L z50bJepoBJn1tffv#D>He`x>x)@7yk?fkx?~%8{-#I#=_YbKZ9}qfJds1$=4*0fy&c zmCFzWR2&C`AfUdc1|cO<2&7^BFP88;!beBMwzr97GDyQns|DFH@ZKF{TJn%L$hqEf-1 zogp|qg1I@ka)s}79d6y?*M$Z2YL)u^eUgidB+_YW4-OE%PoV3RrKDop7-qRls8GOm zUCiTS{C8Qdg~IUgA@ujdg$rPseB=8(cU_#B8KN&`H~u+qYD+do;>0Y;3@*SJ2S`u^1GK+{oo1mjg|Ma2OgIpsS1G*cfkufMPC( zaa>4geeJXN;t7!|pC* zGLX%xQH4;SX{thti%_kq+{KF=OL@i+?>mgg<7}p%(N*-}%^S7p@K9~4t%c@hrCp%_ z^Yg0M_BQnQF4xwKBl6AeDmAQD|vV|8hCl+XKsDr@bB9 zw$C&a??+x=nkSXIN%{8~s%vYiGsjWKW?}-y$03nW1vCv@7a|d4CWB+!ACJ88e2$y3 z&!~I+7YHOAFBO}n*s6fdiw709Z=fV0q(bZLK&->66+p@_1HP2UVw{e!1&^!;> z)I@P~l+(#c;@)pOv>l?5W755kRm1P2Gmo*uNJA&k1Zk1s;Q zfX+^Wi3tK-2j53)Z^sCQ&VgTk`vaAdi?G8`t)hp+=#Hb@{I6(efZ<`ZL;_qF(b0iu rY63v}5q(4|E4eWYO^g#h4hk(GTiNwyp000000NkvXXu0mjf%y;Pi literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/image/flag.png b/app/examples/Games/MineSweeper/image/flag.png new file mode 100644 index 0000000000000000000000000000000000000000..47a05b3183b7806f597b77a6e91bb7640d12a50d GIT binary patch literal 765 zcmWlX>rYYv0L3qrW;9!wE44n=m#ntzu{E`o7Fa6gT|Sc3LNNnpR-zP^Gww7WQ>OU> zlt$O2F)&3%&>1B*n;YgFIy3BHr86OVShJejNzZnE=MVUua`NnHZ)O0KAPDcsGputf z>$_U7wJYp9|MdhxFdmYec(zO?Q!14zm1=Hs%zE`AY&MLK(=^?umDgQl!)gUh!*17T zG!2Cro$4xdbzxut8V%ao(AsLXSadpFe%zsHa|e`4$mM8i!p)mVOG9BH#>Ox@3WMRz z$cVvU;015}```iedR(~zi3CYWh>b-<15_&Lbf~Pv@#A><)ND4}o_C8Aj-b6A_4UZf zL1H2zBGA%;Yu9k#z=!d1i^Za?D^e9^LL@?XID&&wRD{QmVK$?<7+mhhsi~o%q0+3F zm(>(=w(cFHli2 zGc%)WprBB|XheQKZr^s!ujZB2gz0JY^`WK)4##)bm!IEUZnt}JaZxE1E-fwn`r&pj z%q=f3&qs?>R%C6DWQE83+4;QZOuvH!SzTUUt|+Om+Q7I=xm>QD%_;inW3C{=_w(%R z?5FLUeSNlVnJppIYPno)E`C(kU8!%b?x*PL`+g+J<#O2xF{!8GqLWXbkBWUC=D$ZE z5M*ShXJzK3y=vPh5MI8@_wwyF8oMSZMA!3j#Wxc2M5dn|9?XJ*0*UkQpMTsx>(_aD zlosEUtnnD!y{m=h>-nEjKKAl`d}^(OQffG`Ml1Z{zd^wjP6f+)CBwsE174fj%kF@( z(&9}0znWJYCEB=` tUU*7*}phM6%Dao}A=S{{hlKt``6R literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/image/mine.png b/app/examples/Games/MineSweeper/image/mine.png new file mode 100644 index 0000000000000000000000000000000000000000..b0d8b18c0221a2ac44acbbe0b5ba483030f1125d GIT binary patch literal 755 zcmVxN#00057P)t-sj*gC+ znwp)RouZpozPrjm^BQ+|R??&ce~bvBa>O#>B(M#>Tm%j)Pq|j%-Yi zZB3AGPL6C!jA=)UXhn-?MtoK(@(a_r0)w`mK(!#UJwxheFjI^79xSxi=sg%U8oXfeW z%DAS*u$;lHnA_Xiqlj&$if_KEmcOc&!L6CWt(w5Am%XTzy{VR_jB>7)f8pWb+|tQy zMIUrdDR)pScTp>MP%3s%Ds@gMa!Vy^LmO;G9>}<==;-L()y!x?8(1v^TQUerLCi@r*8n{0tF2X4ULRVOwG*9EiA39Z47MaAPR=f_ZtfnQ zUfyh5U*0ia+n l1^^27VE~|DKL)@a835KzR1(yVKIi}d002ovPDHLkV1gF%VZ#6b literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/image/number_1.png b/app/examples/Games/MineSweeper/image/number_1.png new file mode 100644 index 0000000000000000000000000000000000000000..e5bad5a5de6ac307ce01d14ff1ef6f79f4f0139e GIT binary patch literal 388 zcmV-~0ek+5P)xN#00024P)t-sj*gC+ znwp)RouZbSVLhlk5CG3>s+zKe^`Gc)eS#>SD6)ipKn%*@P|mfAKp@zc}O zo15P_IP=@v+o7T5Iy&{?;o+sF={!95=;-LGs_s5M`R?xSzP|B4Kl(5*{16cSZ*TYW z^YgN@_CZ1X_xJbw{QNqE+4BGZ0Fg;VK~xyiZPCXT!Y~j+(NF`3P(rVv_hvf(|6|C< z+BtWndD{tsR2dIr*29>Se4(ht7+R%r1*nD&V`SA(Z=e}DjIq^f#~pNIhp{Jp473=B zGQwDkaUxSBT8uN9W1+>kloi%mj2qcvr^UFJ1CCmZCpqJy#dx)D#@z8R=4lVcm-UA8 iu?OSV!}#;0KjRPRWg|ZpO3Fh30000xN#0002AP)t-sj*gC+ znwp)RouZ~gv9X!3q@}Q{Q>%Vntc19@xMHw~zP`RQtZtaOsK&;|k-euVtZ2;4%%jA! z4yjqw)6=QSxelpXf5D&I+uN_uzYnTfgT|!c;o-E^#1E=lhsmet=;*rJ%8Smd?(Xgo zt6M+0d%ob$^Yinv-pKd&_x$|)WBG8%0001EF3pd1l=x~%%v?997*^`SK zLGxN#0002JP)t-sj*gC+ znwp)RouZZRR?|69V=;-dMs_NO<{2w3io153&#sB~Ss!2paR2b83(MJM;Pz*&; z3!qUHdj)$JD0K+Fq8)ihNJcfT)19B26Nhh^h!6JU$Gz8E4n#i`1i^3f5kCp(KjRCX W&?F-bpPs`20000xN#0001=P)t-sj*gC+ znwp)RouZVvYDEIdo%F4On;cCpx%;e;4+S-=W)6?ACsp{%t z=;(3V+uQT=N#*6C;o;%&@oD$>Jp20|=jXKO=;-tFeD3b<_V$1C^Yi)ng7^3L{QUfu zzQqLq005s!L_t&-({0h)0)apjh2an_=R=~9L{3E#@BeJpY&AF2cQJeK|8^{^QOm&C zHZXQH+wRI_?A08=k%h5e8DNN!Y>eZ|WIDrKHpa!Di=_)!Rz^>(u=afTvNHx^v)u(D zLfIMjN`!-YA~_giI^x7PL86YB@k|%guDlp;bWfEGS^D6`_|$UbG8tc5fkGzZTPsn@ hWc(NyzlP?|_yIdMAPZ2QuyX(a002ovPDHLkV1gD?)1UwV literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/image/number_5.png b/app/examples/Games/MineSweeper/image/number_5.png new file mode 100644 index 0000000000000000000000000000000000000000..f7e88fa2e1b94a1a482082c6ccf4ebeb777f8a40 GIT binary patch literal 381 zcmV-@0fPRCP)xN#0001)P)t-sj*gC+ znwp)RouZ? z#)4>1zrDK5%*>jHc8hdfXG0%$Ryfns)62=mZ%!?(pp)C%+oF$t;o;$&hjXQqf#~Sy z&BeXn+}deHA@1((teS{vMIxDjYxDE-!nLSsMIx@8iud>T&&0af(aHS$`~t5m3;+NC zpGibPR2b83(Mb}5KoCSxoJvJRoNyLYR0a|B{)aOw+kgeU1)cpj5P@a2>NFsB8xVWc zH@Fc8BnCI)kYqGg+=w>G#I7JZBvYq?=#tDlZ*HcGxFEh}x#C7#lkfYRt?alF_m#s@ zPI@AqYk>rMAcnPzT=hV_spzhcheUcoe5yD}<1~?!7sO1xa(q$#=FSicqbxuFRUm#F b+JD9mxN#0002eP)t-sj*gC+ znwp)RouZ?gbwirl+uLqAmb|UU;o;$jU8b;^!P(W^=;-LQoyCAsqN|a=k!!B* z?(Tt9qCnO4+Rx+s{QO3+ z2mSy60Jlj*K~xyiZPCYa!cY)IQNVzqMHV@Ou?^Vdob&(x65S8MMx4cH=5C}ac|31< zT^M~Xj2{kvAQ=2K8)N7p3^6OCWke8-SytTaj0s5ssl*ebEoo%z7sjmQkS`RAd9!C3 zOHxLq%=c7lIvDH1&}e?O(9ps7Ex-@wFWNd7JJQ9U9)Kp}zx2@O9H6U%aVR5xN#0001-P)t-sj*gC+ znwp)RouZvq$tDvi}eXPAutZE;)}Y=#>U31!^;}6>def{Qo7-Kz}nN()3?so4zTUT(be1A+jYj| zX2a&;;o;KW;t#Rz!`kEM=;(aS>ps2h?(Xg&x9^|X?(_5WlGpJdxA6D(_x$|)Zd7m5 z0001vNklRNGk$peAJOc2R6PIy N002ovPDHLkV1gp*!rK4< literal 0 HcmV?d00001 diff --git a/app/examples/Games/MineSweeper/image/number_8.png b/app/examples/Games/MineSweeper/image/number_8.png new file mode 100644 index 0000000000000000000000000000000000000000..d4f0896f0c6a4ee0c9d4fd22e218c75c3f189479 GIT binary patch literal 454 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

Qid@c*Q=tJRs+sk=fNd$&W z!3{u@{TrZ0%+ z%)zUpCGDRhdHsYi+HasIOwtz!u=hDXptvcn17YIT7a!o$=f0<4lCDrYbQpZypM7-S zHa60-y+XJJM{ONM6VEoLn#E{q=|kn4L|81Ak+JcnHFod`frHYr$B{R>+S<-h0wI@@ zcB2G`A>)Jy1Y?=vK%nFkVylPyfBe{D0W6NOJW$66uD>d@!e!C0{Xe=XTsHek-xd`P zgJGi?@x}H6PYXx=%LjeSC;_1TJC*7&9Wu65ce!$?B?_W%q1-{&7#B?{Z9|{Z*}Ef2 zN*>sA!U#~fwhyuwM@;wD28W@5$;a>imSFou;N!8inH&)yNeU`_m)I72>LHl>On`~$ zvCWwrMTM^in5*4;K|ig2yPBJu&ofw2D^u~C491*soS|I#ByHjQcMAq> z(z&F+@GvIxyg-FzSofrF?EsAtYXz6vEosaHCx9!BPM-XN(t37yMOzV`-wF6$kf$5u z+MbSFG4b~H{^_%UvOGQqg+dp&9M;dt92O(0>4qe8t2#V#8d`(GC(0Z<#4RUY05nr zdqxOT+4NrAIbk>88Xv7vB?!9h(;WmW4c3y$tbfuo`!S4j`+j=5HC?bz_agWfrM2?# zPPk>*#K+Fg&XzeX&1)y_Vf2DktE?R;jwl0M+-O-XV?7npY><$wMohdxEmKqXxn z@6b65a`$g)(BGL zgJlGRIlMW^Lx9A&+ggak+%yr=Y6ZXJ+S>j&e*F0S#Qv#ILDGBvS3v%0u=4_@;~+!p zW%ry<;rz<_Z%TE7?f19!=kOn?`6)^NKM0M*MKV%Yf9^{Djm9<>Kytp5!)Sc=vi~TQ8Xw;LJ8&`= hP1.Y And If $iY < hP1.Y + hP1.Height Then GoSub _OnPaddle + If hP2 And If $iX = hP2.X And If $iY >= hP2.Y And If $iY < hP2.Y + hP2.Height Then GoSub _OnPaddle + ' Flip at top/bottom border? + If $iY < 0 Or $iY >= $hWindow.Height Then + GoSub _UndoMovement + $iVD = - $iVD + Move(hP1, hP2) + Endif + ' Made a point (left/right border)? + If $iX < 0 Or $iX >= $hWindow.Width Then + GoSub _UndoMovement + Return $iHD + Endif + Return 0 + +_UndoMovement: + $iX -= $iHD + $iY -= $iVD + Return + +_OnPaddle: + GoSub _UndoMovement + $iHD = - $iHD + Move(hP1, hP2) + $bHitPaddle = True + +End + +Public Sub Draw() + + If $bSimulate Then Return + $hWindow.Print("o", $iX, $iY) + +End + +Public Sub Undraw() + + If $bSimulate Then Return + $hWindow.Print(" ", $iX, $iY) + +End + +Private Sub HDir_Read() As Integer + + Return $iHD + +End + +Private Sub HDir_Write(Value As Integer) + + $iHD = Value + +End + +Private Sub VDir_Read() As Integer + + Return $iVD + +End + +Private Sub VDir_Write(Value As Integer) + + $iVD = Value + +End + +Private Function X_Read() As Integer + + Return $iX + +End + +Private Sub X_Write(Value As Integer) + + $iX = Value + +End + +Private Function Y_Read() As Integer + + Return $iY + +End + +Private Sub Y_Write(Value As Integer) + + $iY = Value + +End + +Private Sub HitPaddle_Read() As Boolean + + Return $bHitPaddle + +End diff --git a/app/examples/Games/Pong/.src/MMain.module b/app/examples/Games/Pong/.src/MMain.module new file mode 100644 index 00000000..486dcb4c --- /dev/null +++ b/app/examples/Games/Pong/.src/MMain.module @@ -0,0 +1,191 @@ +' Gambas module file + +Private $hConfig As Window +Private $hTimer As Timer +' Scores +Private $iS1 As Integer +Private $iS2 As Integer +' Players' paddles +Private $hP1 As Paddle +Private $hP2 As Paddle +' If Paddle2 shall be controlled by an NPC object +Private $bNPC As Boolean +Private $hNPC As NPC +' The ball +Private $hBall As Ball + +Public Sub Main() + + ' Make our Window raise events + Object.Attach(Window, Me, "Window") + Window.SetFocus() + + Screen.Cursor = Cursor.Hidden + Screen.Echo = False + Screen.Input = Input.CBreak + + Window.Border = Border.Ascii + Window.Caption = Subst$(("Pong v&1 - gb.ncurses Example"), Application.Version) + Window.Clear() + + $hConfig = New Window(True, 0, 0, 30, 5) + $hConfig.Border = Border.Ascii + $hConfig.Center() + + $hTimer = New Timer As "Timer" + + $iS1 = 0 + $iS2 = 0 + + $bNPC = True + GameInit() + +End + +Private Sub Configure() + + $hConfig.Show() + $hConfig.Clear() + $hConfig.PrintCenter(("What to do?\nPlay again: [P]\nChange opponent: [o]\n\nQuit [q]")) + Select Case Window.Ask(("Poq")) + Case ("o") + Configure_Opponent() + Case ("q") + Quit + End Select + $hConfig.Hide() + +End + +Private Sub Configure_Opponent() + + $hConfig.Clear() + $hConfig.PrintCenter(("Play against whom?\n[h]uman, [N]ormal, [m]aster")) + $bNPC = True + Select Case Window.Ask(("hNm")) + Case ("h") + $bNPC = False + Case ("n") + $hNPC.Mode = NPC.Normal + Case ("m") + $hNPC.Mode = NPC.Master + End Select + +End + +Private Sub GameInit() + + ' If both as 'master' NPCs we could make a screensaver out of it :-) + $hP1 = New Paddle(Window, 1) + $hP2 = New Paddle(Window, -1) + $hBall = New Ball(Window) + $hNPC = New NPC(Window, $hBall, $hP2) + GameStart() + +End + +Private Sub GameStart() + + Window.Buffered = False + Configure() + $hP1.Reset() + $hP2.Reset() + $hBall.Reset() + $hNPC.Init() + Window.Buffered = True + Redraw() + ' See SPEED. The width means actually the intra-paddle width: + $hTimer.Delay = 4000 / Sqr(2 * (Window.Width - 4) ^ 2) + $hTimer.Start() + +End + +Public Sub Timer_Timer() + + If $bNPC Then $hNPC.Move() + Select Case $hBall.Move($hP1, $hP2) + Case -1 + Inc $iS2 + Goto _NewGame + Case 1 + Inc $iS1 + Goto _NewGame + End Select + ' I suppose that this does not go down to zero + If $hBall.HitPaddle Then Dec $hTimer.Delay + Redraw() + Return + +_NewGame: + ' We want to see it + Redraw() + $hTimer.Stop() + ' FIXME: Makes stack overflow eventually + GameStart() + +End + +Public Sub Window_Read() + + Dim iKey As Integer + + iKey = Window.Read() + Window.Drain() + ' Go two steps per keystroke + ' FIXME: Can't reach some coordinate with odd Window.Height + Select Case iKey + ' Player 1 + Case Key.Up + If $hP1.Y > 0 Then $hP1.Y -= 2 + Case Key.Down + If $hP1.Y + $hP1.Height < Window.Height Then $hP1.Y += 2 + ' Human player 2 + Case Asc("j") + If Not $bNPC And $hP2.Y > 0 Then $hP2.Y -= 2 + Case Asc("k") + If Not $bNPC And $hP2.Y + $hP2.Height < Window.Height Then $hP2.Y += 2 + ' Change ball speed :-) + Case Asc("+") + If $hTimer.Delay > 10 Then $hTimer.Delay -= 10 + Case Asc("-") + $hTimer.Delay += 10 + ' Pause + Case Asc(("p")) + Pause() + ' Quit + Case Asc(("q")) + Quit + Case Else + Return + End Select + Redraw() + +End + +Private Sub Redraw() + + Dim iX As Integer = Window.Width / 4 + + ' Scores and net + Window.Print(" " & Str$($iS1) & " ", iX, 1, Attr.Reverse) + Window.Print(" " & Str$($iS2) & " ", iX * 3, 1, Attr.Reverse) + Window.DrawVLine(Window.Width / 2 + 1, 0, Window.Height, ".") + ' Players and ball may overwrite the unimportant stuff above + $hP1.Draw() + $hP2.Draw() + $hBall.Draw() + Screen.Refresh() + +End + +Private Sub Pause() + + $hTimer.Stop() + Window.PrintCenter(("PAUSE")) + Screen.Refresh() + Window.Ask(("p")) + Window.Clear() + Redraw() + $hTimer.Start() + +End diff --git a/app/examples/Games/Pong/.src/NPC.class b/app/examples/Games/Pong/.src/NPC.class new file mode 100644 index 00000000..68e2389c --- /dev/null +++ b/app/examples/Games/Pong/.src/NPC.class @@ -0,0 +1,116 @@ +' Gambas class file + +Public Const Normal As Integer = 0 +Public Const Master As Integer = 1 + +Property Mode As Integer + +Private $hWindow As Window +' Ball is needed to calculate movements, of course >:-) +Private $hBall As Ball +Private $hPaddle As Paddle +Private $iMode As Integer + +' This is the Y coordinate we wish to reach with the middle of our paddle +Private $iY As Integer +Private $bReady As Boolean + +' Master's control data +Private $iLastDir As Integer +Private $hMyPaddle As Paddle + +Public Sub _new(hWnd As Window, hBall As Ball, hPaddle As Paddle, Optional iMode As Integer = Normal) + + $hWindow = hWnd + $hBall = hBall + $hPaddle = hPaddle + $iMode = iMode + +End + +Public Sub Init() + + $bReady = False +$iLastDir = $hBall.HDir + ' We need to insert an opponent paddle that would block everything to not + ' go into an infinite loop when trying to calculate the ball's positions + $hMyPaddle = New Paddle($hWindow, - $hPaddle.Dir) + $hMyPaddle.Reset() + $hMyPaddle.Y = 0 + $hMyPaddle.Height = $hWindow.Height + +End + +Public Sub Move() + + If $iMode = Normal Then + Move_Normal() + Else If $iMode = Master Then + Move_Master() + Endif + +End + +Private Sub Move_Normal() + + ' Make him beatable... + If CInt(Rnd(0, 2)) Then Return + ' Just follow the ball + $iY = $hBall.Y + Move_Generic() + +End + +Private Sub Move_Master() + + ' Calculate the future ball's position and move accordingly + If Not $bReady Then MasterCalc() + Move_Generic() + ' If the ball hits this paddle, we can begin calculating again + If $hBall.HDir <> $iLastDir And $iLastDir = - $hPaddle.Dir Then + $bReady = False + Endif + $iLastDir = $hBall.HDir + +End + +Private Sub Move_Generic() + + Dim iMid As Integer = $hPaddle.Y + ($hPaddle.Height / 2) + Dim iDiff As Integer = $iY - iMid + + If Abs(iDiff) <= 1 Then Return + $hPaddle.Y += 2 * Sgn(iDiff) + +End + +Private Sub MasterCalc() + + Dim hMyBall As New Ball($hWindow, True) + + ' Use the CBall class to sneakily get the position we have to sit on + ' when the ball arrives at this end + hMyBall.HDir = $hBall.HDir + hMyBall.VDir = $hBall.VDir + hMyBall.X = $hBall.X + hMyBall.Y = $hBall.Y + ' Simulate the ball flying thither and back again + While hMyBall.X <> $hPaddle.X + hMyBall.Move($hMyPaddle, Null) + Wend + $iY = hMyBall.Y + $bReady = True + +End + +Private Sub Mode_Read() As Integer + + Return $iMode + +End + +Private Sub Mode_Write(Value As Integer) + + $iMode = Value + +End diff --git a/app/examples/Games/Pong/.src/Paddle.class b/app/examples/Games/Pong/.src/Paddle.class new file mode 100644 index 00000000..09a745fb --- /dev/null +++ b/app/examples/Games/Pong/.src/Paddle.class @@ -0,0 +1,96 @@ +' Gambas class file + +Property X As Integer +Property Y As Integer +Property Height As Integer +Property Read Dir As Integer + +' Direction in which to play +Private $iDir As Integer +Private $hWindow As Window +Private $iOrigX As Integer +Private $iOrigY As Integer +Private $iX As Integer +Private $iY As Integer +Private $iHeight As Integer + +Public Sub _new(hWnd As Window, iDir As Integer) + + $hWindow = hWnd + $iHeight = hWnd.Height / 6 + 1 + $iDir = iDir + If iDir > 0 Then + $iOrigX = 1 + Else + $iOrigX = hWnd.Width - 2 + Endif + $iOrigY = (hWnd.Height - $iHeight) / 2 + If Odd($iOrigY) Then Inc $iOrigY + +End + +Public Sub Reset() + + Undraw() + $iX = $iOrigX + $iY = $iOrigY + +End + +Public Sub Draw() + + $hWindow.DrawVLine($iX, $iY, $iHeight, "|") + +End + +Public Sub Undraw() + + $hWindow.DrawVLine($iX, $iY, $iHeight, " ") + +End + +Private Sub X_Read() As Integer + + Return $iX + +End + +Private Sub X_Write(Value As Integer) + + If Value < 0 Or If Value + 1 > $hWindow.Width Then Return + Undraw() + $iX = Value + +End + +Private Sub Y_Read() As Integer + + Return $iY + +End + +Private Sub Y_Write(Value As Integer) + + If Value < 0 Or If Value + $iHeight > $hWindow.Height Then Return + Undraw() + $iY = Value + +End + +Private Function Height_Read() As Integer + + Return $iHeight + +End + +Private Sub Height_Write(Value As Integer) + + $iHeight = Value + +End + +Private Function Dir_Read() As Integer + + Return $iDir + +End diff --git a/app/examples/Games/Pong/SPEED b/app/examples/Games/Pong/SPEED new file mode 100644 index 00000000..2d7658ab --- /dev/null +++ b/app/examples/Games/Pong/SPEED @@ -0,0 +1,47 @@ +Different people have different terminal dimensions. The following shall +outline my attempt to make Pong soundly playable with every one. + +Consider this picture. Assume that it is your terminal: + +........... Legend: +, -, | = Real border (of terminal) +. '. . = Imaginary border +. ' . o = Ball +. ' . " = Real flying behaviour +. ' . ' = Equivalent imaginary flying behaviour ++----'----+ The real and imaginary border make a square w^2. +| " | \ +| " " "| | +| " " " | | Height h +|o " | / ++---------+ + \_______/ + Width w + +We clearly want that the ball gets some velocity linearly proportional to +the distance it has to fly so that the time the ball needs (and the human +player has time to react) is constant. + +As you can see, the distance from paddle 1 to paddle 2 is sqrt(2w^2) units. +The ball goes one unit per Timer event. We need a Timer.Delay that makes the +constant time elapse after sqrt(2w^2) Timer events. + +Let's arbitrarily set the time from one paddle to the other to 4s: + + Timer.Delay = 4000 / Sqr(2 * w ^ 2). + +This is the starting delay. With each hit on a paddle, it decrements. + +This was tested with ANSI's 80*24 terminal dimensions in xterm and my home +180*74 on the Linux console. Feel free to set the LINES and COLUMNS +environment variables to try different proportions. + +One may see the crux with this approach: The larger/smaller the terminal, +the faster/slower the ball. I found both scenarios equally hard: with a +large terminal, the paddle is large; with a small terminal, the paddle is +small. It compensates. + +TODO: The above is not true for terminals which are much _wider_ than high. + I found it problematic to play "LINES=10" but easy with "COLUMNS=30". + +BTW: The NPCs make one step per each ball step. You may have to resize your + terminal to be able to win against 'master' ;-) diff --git a/app/examples/Games/Pong/pong.png b/app/examples/Games/Pong/pong.png new file mode 100644 index 0000000000000000000000000000000000000000..e17e6d06a6a181530d684b20f09fbc2135c32732 GIT binary patch literal 89 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk~Bp9L@-6KI9PZ!4!j_b(@4b0qsTo^lEI4Gq! jBpE40F5+Ml;^bgpFyQ6bn!qvh4M>lttDnm{r-UW|)npR= literal 0 HcmV?d00001 diff --git a/app/examples/Games/Puzzle1To8/.directory b/app/examples/Games/Puzzle1To8/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Games/Puzzle1To8/.icon.png b/app/examples/Games/Puzzle1To8/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8974e481fb528ee79b660581b3a16beef405f087 GIT binary patch literal 4303 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/Puzzle1To8/.lang/ca.mo b/app/examples/Games/Puzzle1To8/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..89fc7b990e9d98ebbef53082961a7532b53e5723 GIT binary patch literal 3803 zcmb_fO>ZMf87^S?G9L>Ihzm$>7KzM=wDYkb1?OYec%6w?iQ};yhFp-!-DSI??k;at zwG%HwAP#VVy`WtQi3@@QJ3=GS%mJ~Q3+i2Q;D%OO!2yAUIPx2K-nJbl!%W~nE9J-C zuB!K`_v`8W^_iz$7q~uy@i~mQFg}kFzXu<<{sw&Qy+ZsQ_yOFHpAzCnz;6RBu=IW* zKE(IHj{(0ARnD@Be!s&wCq)Kk-L=@IHSAvWxx!dkUYaBGX zCro><(QVc{9ojo;Q*ug3y%~5u%yJhjkT5OyW+r8a8asty9M0{m?wYR(7v&HBlw#W#p(q%uLfUxPY zhR=G+pDA{zDLjy7Ne?+dDn(e?w{SWR=0@0ZHb+~M(W%j?2KSyhR}M4{V!7-{#<4k5 zQF%^Ar%#|mb3^N#>$Jh-zzjzUGcW^LJmqJ|na5k>YJ!UvCaXPUN4Z=URV?Gea+y`n zi)m%f;A(-E*+0Y|97?W3m-P}PWs&7CXUcJ&^YOC5;Nqs24e&_$aAoeazTsoDJL#G+meP>^>?04@YtKmh}%O_bcYuP{Hz?B6l^gWvnVoZ>VCuw^?)E<$}2UCE_2Zy^Ax&e&mgT(M2IHHq<)$@ z<%`-|I8mKTRJL*z(aK(VC04Y~8*N^_qks-fh~{%SZI{IDOnY&noM>5dikuA^QSkL? zQ$y^F?bf?yv(!2Y+G?!1*#$3RmkRgs^&T|_3%W{ zos2-ht8>4K(CKuG!lATI`JLnLUa+~C$2|JV26dEGguqm2v!{Kj6=x8DV9!7qDwK|j zTeq{Zg-qaIc2Zi}q4llOH-eUd()eIbvqClLWh~RuwbJ%DWvdqTG1oRp`P~bllU7q4 zYn0y>shCD4Qk<<*fkXSyt7a-~I+PZ@!%Iwag|Z>5A_Y<5^1QnOvZtB{-#G0!2$C7 z)TuW+b*fccbgRlj*r;w%s&Q^qkTdWa3WWxe1yJJ~Yhr#c)E?hi3wgys2WO~vTJ>7~ z>R#jaalJ#WdYg`$)M_8K@*j5^U3#(Jt+x*w&1x4FsR_qI$P$jJP9c4X40Zls1xFp= z`TrK4P{9xTGPC(T4*xQ1q=lBS!e=S^MSg#T=uRE}B#}hhI%O9n+%ec|@@m=@Y*Ryh z1M$)4`MniNw1vI@DwwCNndKV?dzP(um&@O9g4u3sj|8~atBw0P1%lHirbZU@USLmm)ET4}yfrWauiUL>ZSfiXdAIGQ! z_=Brufan!(2rQvia~xSct%V#EDq4ng%%UpcEvxch7D-XOIAriKx;P3U)#3j`PMgZB zQz`Zo+6zY#Qo4^AEx{(M(3vXLY>{9SK=F51M ziJ_@T7VbZs;#X6_74PM&s$Saw?`$}KT9kgVfMX`{O2aW_)kQP7NUp*eEdB^{(L3PW zS&YXj|8, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Puzzle1To8\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 21:45+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Puzzle 1 to 8" +msgstr "Puzzle 1 a 8" + +#: .project:2 +#: FrmAyuda.class:41 +msgid "Place the numbers 1 to 8 in each of the boxes without repeating, so that each box does not contain a correlative number to its neighbors or adjacent boxes. See the following examples:" +msgstr "Poseu els números de l'1 al 8 a cada una de les caixes sense repetir-los, en que cada caixa no contingui números correlatius amb les caixes veïnes o adjacents. Mireu l'exemple següent:" + +#: FMain.class:61 +msgid "Puzzle 1 to 8 - Locate the numbers!" +msgstr "Puzle 1 a 8 - Troba els números!" + +#: FMain.class:69 +msgid "Are you sure?" +msgstr "N'esteu segurs?" + +#: FMain.class:69 +msgid "No" +msgstr "-" + +#: FMain.class:69 +msgid "Yes" +msgstr "Sí" + +#: FMain.class:90 +msgid "Congratulations! You did it!" +msgstr "Felicitats! Ho heu fet!" + +#: FMain.class:142 +msgid "Game" +msgstr "Joc" + +#: FMain.class:145 +msgid "Clear" +msgstr "Neteja" + +#: FMain.class:150 +msgid "Quit" +msgstr "Surt" + +#: FMain.class:156 +msgid "Help" +msgstr "Ajuda" + +#: FMain.class:159 +#: FrmAyuda.class:36 +msgid "How to play?" +msgstr "Com jugar-hi?" + +#: FMain.class:164 +#: FrmAbout.class:34 +msgid "About..." +msgstr "Quant a..." + +#: FrmAbout.class:43 +msgid "Author" +msgstr "Autor" + +#: FrmAbout.class:47 +msgid "" +"Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n" +"
pablomileti@gmail.com" +msgstr "" +"Autor: Pablo Mileti.

Podeu enviar les vostres preguntes, suggeriments, errors, etc. a aquesta adreça de correu electrònic:\n" +" pablomileti@gmail.com" + +#: FrmAbout.class:51 +msgid "License" +msgstr "Llicència" + +#: FrmAbout.class:55 +msgid "" +"\n" +" Copyright (C) 2010. Author: Pablo Mileti \n" +"\n" +"This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License along with this program. If not, see ." +msgstr "" +"\n" +" Copyright (C) 2010. Autor: Pablo Mileti \n" +"\n" +"Aquest programa és programari lliure; el podeu distribuir i/o modificar sota els termes de la llicència GNU General Public License tal i com està publicada per la Free Software Foundation; ja sigui la versió 3 de la llicència, o bé (si ho preferiu) qualsevol versió posterior.\n" +"\n" +"Aquest programa es distribueix amb la voluntat que pugui ser útil però SENSE CAP GARANTIA; ni tant sols les garanties implícites MERCANTILS o ESPECÍFIQUES PER UN PROPÒSIT DETERMINAT. Si voleu més informació, vegeu la llicència GNU General Public Licence.\n" +"\n" +"Hauríeu d'haver rebut una còpia de la GNU General Public Licence juntament amb aquest programa. Si no fos així, mireu ." + +#: FrmAbout.class:65 +#: FrmAyuda.class:65 +msgid "&Close" +msgstr "&Tanca" + +#: FrmAyuda.class:53 +msgid "Way incorrect:

The numbers 1 and 2 are consecutive and are in adjoining boxes, so it is not allowed." +msgstr "Manera incorrecta:

Els números 1 i 2 són consecutius i estan en caixes adjacents, o sigui que no està permès." + +#: FrmAyuda.class:70 +msgid "Way correct:

There aren't correlativity between to the numbers entered with respect to their adjoining boxes, so it's allowed." +msgstr "Manera correcta:

No hi ha correlativitat entre els números respecte de les caixes adjacents, o sigui que està permès." + diff --git a/app/examples/Games/Puzzle1To8/.lang/cs.mo b/app/examples/Games/Puzzle1To8/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..ac347cdc88f26abf00644ceac9404cab76ba6a86 GIT binary patch literal 3007 zcmb_e-ESjT6~E8|#eBBF3j*n+NZVG#&Te^GxoLKX#7W%x3&+kXeW-NDb8TOHJa@Qr zXRs}&M?=tCuFADK*;FocqJuSpN;4gs|Soo3<&+t9)8^GTH zS?(R+$G`_be8dqpS^o(TM@6>8UjV<3`(FdU0=y4|Iq@!#&%Xy`y)8Wa5%5hQpMMMZ z6!5ov2;ANVGUa<9^Ht#QfxiR32TXw**knC_0Dcqr0g!q975EVNHy}RZZb69e1K$F& z{yzfo5ob&MGmuSw3}l{v2Yv$l2ax^v)z^f$27DU`)8a2c*7FIF`LPV2XAa-N;_KU3 z%!PCCEiC2%@tiLpk0&EvZ1XuR*1ft||97#T#R841i{m5;glpHtQ>zCW0RasU`i;P+f=0X`(`*Gt>#i)(oti5PtTpQk0m!JC#REfl2Nc_EKN+H`%~UXqd-}5Lq$&6NkXQd-yAf$bf6+-Wk{{m4|R`fx~HN#gsM0A9(aBM#(oc%ll)`|9GE+!mPw5PVB=t}t zs}O#UQW=_PkUIwR>p}seYM&zGHYowK?V)pVd28$R^t3dH(vq=*Eo7RroNVos#I@bf zBuZ@iJ9p$1rY(rL^7fX$v+dh_b&y+BgjLZCZXtlQ=}aSLzH+CE18VXJMCrsw4UmeE zR*o%#4#$!hDwi_ak_<*hM;hFHb5|vxX^_ho5|UwPPE}A^krGvrMjh_y%rCe`w6|}x#w>cb@VjaZ+#sOlMlndLEhF0}oeSU&% zXUZ{;heFxns{9B8zZ+YkqGqQs#J zV{vFsnNu9f=`FErUC}V270TYC!q;Jk+)a+IlD*-oRP$_JHJVkig1u;v=QnTITo_Um z7_|sLa)y3Dk{+omwahMa(GGG7)6zF6D>M=6IbkD#w3V8Px<|Btws&6UZL$u z-K*AadwY9r%$OaBMuSuiRI#Jvq)auhanNOx+qK&5P;=|9Wy(~jSL>BEzZ<3N1!USG zI>pX31|O&Hx-FzrN3T$CC~czLtKH6i@#b2NY4w#YR?unzl2xXgzIFvs6xZI6+2#B5 zbI09y6x;b(7R}G7hf0=@jfaMc=I1m~GfiV-RX!UclQ?Wfl4dfHeD31@{2T!wWN{d( zKu!4JNR2e=C%$|iMLi7XA5v^Wh0c)U%TE;^bLs+GO}v3+hBt7^rA)A4B4JdT}4_p!5&&DUpu!2JM;g{A`9l25{Dy z4RL6tXw1)IlU+Orq;WQj(!^@HkPy z6eoPluw_N9i>gG&diL3HLTr9M4rMTXWLnG~9TvF3tjPVk{9s9RVOoV7qB}MbV&{Yx G8Sx*?)3TcY literal 0 HcmV?d00001 diff --git a/app/examples/Games/Puzzle1To8/.lang/cs.po b/app/examples/Games/Puzzle1To8/.lang/cs.po new file mode 100644 index 00000000..b0cf6198 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.lang/cs.po @@ -0,0 +1,104 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Puzzle 1 to 8" +msgstr "Puzzle 1 z 8" + +#: .project:2 FrmAyuda.form:19 +msgid "Place the numbers 1 to 8 in each of the boxes without repeating, so that each box does not contain a correlative number to its neighbors or adjacent boxes. See the following examples:" +msgstr "Umístěte čísla 1 až 8 v každém z polí bez opakování, takže každý box neobsahuje souvztažnosti čísla na své sousedy nebo přilehlehlé pole. Viz následující příklady:" + +#: FMain.class:69 +msgid "Are you sure?" +msgstr "Jsi si jistý?" + +#: FMain.class:69 +msgid "No" +msgstr "Ne" + +#: FMain.class:69 +msgid "Yes" +msgstr "Ano" + +#: FMain.class:90 +msgid "Congratulations! You did it!" +msgstr "Gratuluji! Dokázal si to!" + +#: FMain.form:22 +msgid "Puzzle 1 to 8 - Locate the numbers!" +msgstr "Puzzle 1 z 8 - Umísťování čísel!" + +#: FMain.form:27 +msgid "Game" +msgstr "Hra" + +#: FMain.form:30 +msgid "Clear" +msgstr "Vyčistit" + +#: FMain.form:35 +msgid "Quit" +msgstr "Ukončit" + +#: FMain.form:41 +msgid "Help" +msgstr "Nápověda" + +#: FMain.form:44 FrmAyuda.form:14 +msgid "How to play?" +msgstr "Jak hrát?" + +#: FMain.form:49 FrmAbout.form:12 +msgid "About..." +msgstr "O pravramu..." + +#: FrmAbout.form:21 +msgid "Author" +msgstr "Autor" + +#: FrmAbout.form:25 +msgid "" +"Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n" +" pablomileti@gmail.com" +msgstr "" +"Autor: Pablo Mileti.

Můžete odeslat Vaše dotazy, připomínky, chyby, atd., na následující e-mailovou adresu:\n" +" pablomileti@gmail.com" + +#: FrmAbout.form:29 +msgid "License" +msgstr "Kicence" + +#: FrmAbout.form:33 +msgid "" +"\n" +" Copyright (C) 2010. Author: Pablo Mileti \n" +"\n" +"This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License along with this program. If not, see ." +msgstr "-" + +#: FrmAbout.form:43 FrmAyuda.form:43 +msgid "&Close" +msgstr "&Zavřít" + +#: FrmAyuda.form:31 +msgid "Way incorrect:

The numbers 1 and 2 are consecutive and are in adjoining boxes, so it is not allowed." +msgstr "Špatná cesta:

Čísla 1 a 2 jsou po sobě jdoucí a jsou v přilehlých polí, tak to není dovoleno." + +#: FrmAyuda.form:48 +msgid "Way correct:

There aren't correlativity between to the numbers entered with respect to their adjoining boxes, so it's allowed." +msgstr "Správná cesta:

Není mezi korelativností na zadaná čísla s ohledem na jejich přilehlé pole, takže je to dovoleno." diff --git a/app/examples/Games/Puzzle1To8/.lang/de.mo b/app/examples/Games/Puzzle1To8/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..ff09cf892e32496733908f2ee64099ca4e0c1c64 GIT binary patch literal 3599 zcmeHJ&5t8T6|cbZ5hoB5J|sjCZ-SH$W$f8WE+c0$*`68Cj9J@bY|pMH2c&BEb9ZIh zT~2kiy`Cr%;s6K2i3A~m#APM!9N?1MAaO(d14POHz=;#T*S6QYVY4SBE@@5qH(g!z z-ml(!_1^pFnQI>?Jm1E67UQoN-^WNF!w;U1f$x7osZW4k#{2Y|QttuZ1v+5mOG z(esbx_kS(%-@vb9UdVuwdI~5!z61Od@Owb{{&(Q>z<&aT+BHndd7cD{Ng<}3m$l!- zkmnm1!bN;gas$d4AfkLk-|7)r&tQ~iSx$-_zJ>8LMrAoCApBH?@Eq95+?hD>^y0xw z^vd<^>op1rpIBF?t`1X6ZIiM$M3u@QF*!|~jh!A7{vA1H%I(O{wBtI>Z9zkwk>kkZ z-kGrQOvaPWqAlxaY$G$8%S4ey?8qlfp4~VnJ1Sp}J7;vv89SX)w+K@+q?Q?SmNRMS zm6<%@h{Acf{7AI-m)hybLaO%0W}CzYvK>v?<#?x8R|=NWHVJHA)LsZXvJ=_#5?IbD z)gZLmSNrb+ug?{c$x0T9o!}S=q~TyQlcp3h6*-TJbdz8Ky4@U{oSqFR=$zB-pw|mJ zgY(z05+=r1p7KJD8BbD!!@-nJXMQf6+l}7A3DyVu%~o@8F0_xDgHEI0r=!yz1=J0C zgXY0mE9g=8tk*s5H)=$Em_=;+2y>1~cQ~eqy*6o%qn^WgIVhzOC3?zm;*iZ0gftBi zA}bR2+fq`tK^o6Tx*R@AJEx8P~$_Xw-frm1F@SFo5stGL{9*yzW~K%<3)#=}Tn#_QEXzy?inijr%At#-tcE(X{MaE>GL5Eg~wk6i-6(I*^ zIMT2HGmyos%pqnTu1#(eOte-Rgout>t)>EOlZ%xXj^9v=o4JGsFKA8dL-Hf8lzW&L z;TS<#M)}(X=Mv{~ydsY8y@W-KDfYw7mC;64jxFYBRNDv+Fpgleyk6PWlsF!}{@fVT z-qt6Q9>!&MxDm@-ZD_~O?a@Q$cenJOI!IYNb$~c=+85$BFxns?92s0GzoCxxnAHiV z6Ln%|!fBG~`5S6^bydfzZmNfi2w$5W>vqJttcS@eR7-E(yJ(Il41HN#`nSBW}O557^AT6T!!bAQ-ws2>-`t8>m(*V(rav(;u)y|qYc@O^M_GdOP0twyikJndAv zr_~-$jbyy)Aw<>byb<)O?bGUEKqsg5cJt*oPdklDOXt2iK#s{_f64MkXRQ`>f_8&; z8||Rkx)B^6_K;)tur!Mc9dmWSdR(Vg&^bO6mAkFhjnv51U9YKArQK{d*8H|>*DLU} zhjprh`2>7C-|<^;r^#NUVWM5m{`IrLQFUj%PH2tTRU5c!5u8=0ozVEoBTLoimFn|K z^?#{U{b7QVef0;Ve9%l0yHppUFKf|RcJUBhhlW=|x23~E8OjX+wccaz2x`me&s3#T z6uu&huRct@iEHZW_b8rHg7$?#gc4+iD7|G7vQBEyr*Gc61RWN1p|cG8jvNvQ4sr__ z6ZX2g8}laZqw1hZn$=Jlk@*=N-lEu4gR<73t1PYbzo%eK@?B6&YSP z2n-WsLPF+8HAFfZjZl616T_)GG3kgG|LZJ8Hwmkt0$p-;sXB0_>a)K@^R%}36m8}< zg{a~?WE1%OHhMXBiA}M!Y$(ce(#@;8xfeF2XLGuMG{}*_AuCNWm^j!7c8Ss8Kw|`g z5)Ei-A@;r?DV;5?O>%WN^(Z|CkyLV82iZQGTu#LIRiBdNy8J9SR|!?sUmEGG*2mgV N`{\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Puzzle 1 to 8" +msgstr "Puzzle 1 bis 8" + +#: .project:2 FrmAyuda.form:19 +msgid "Place the numbers 1 to 8 in each of the boxes without repeating, so that each box does not contain a correlative number to its neighbors or adjacent boxes. See the following examples:" +msgstr "Setze die Zahlen von 1 bis 8 ohne Wiederholung so in die Kästchen, dass kein direkt oder schräg angrenzendes Feld eine benachbarte Zahl enthält. Siehe die folgenden Beispiele:" + +#: FMain.class:69 +msgid "Are you sure?" +msgstr "Bist du sicher?" + +#: FMain.class:69 +msgid "No" +msgstr "Nein" + +#: FMain.class:69 +msgid "Yes" +msgstr "Ja" + +#: FMain.class:90 +msgid "Congratulations! You did it!" +msgstr "Glückwunsch! Du hast es geschafft!" + +#: FMain.form:22 +msgid "Puzzle 1 to 8 - Locate the numbers!" +msgstr "Puzzle 1 bis 8 - Platziere die Zahlen!" + +#: FMain.form:27 +msgid "Game" +msgstr "Spiel" + +#: FMain.form:30 +msgid "Clear" +msgstr "Löschen" + +#: FMain.form:35 +msgid "Quit" +msgstr "Beenden" + +#: FMain.form:41 +msgid "Help" +msgstr "Hilfe" + +#: FMain.form:44 FrmAyuda.form:14 +msgid "How to play?" +msgstr "Spielanleitung" + +#: FMain.form:49 FrmAbout.form:12 +msgid "About..." +msgstr "Über..." + +#: FrmAbout.form:21 +msgid "Author" +msgstr "Autor" + +#: FrmAbout.form:25 +msgid "" +"Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n" +" pablomileti@gmail.com" +msgstr "" +"Autor: Pablo Mileti.

Du kannst Fragen, Vorschläge, Bugs usw. an diese E-Mail-Adresse senden:\n" +" pablomileti@gmail.com" + +#: FrmAbout.form:29 +msgid "License" +msgstr "Lizenz" + +#: FrmAbout.form:33 +msgid "" +"\n" +" Copyright (C) 2010. Author: Pablo Mileti \n" +"\n" +"This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License along with this program. If not, see ." +msgstr "" +"\n" +" Copyright (C) 2010. Autor: Pablo Mileti \n" +"\n" +"This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License along with this program. If not, see ." + +#: FrmAbout.form:43 FrmAyuda.form:43 +msgid "&Close" +msgstr "&Schließen" + +#: FrmAyuda.form:31 +msgid "Way incorrect:

The numbers 1 and 2 are consecutive and are in adjoining boxes, so it is not allowed." +msgstr "Unkorrekt:

Die Zahlen 1 und 2 sind fortlaufend und in benachbarten Feldern, also ungültig." + +#: FrmAyuda.form:48 +msgid "Way correct:

There aren't correlativity between to the numbers entered with respect to their adjoining boxes, so it's allowed." +msgstr "Korrekt:

Keine aufeinanderfolgenden Zahlen stehen in benachbarten Feldern, das ist erlaubt." diff --git a/app/examples/Games/Puzzle1To8/.lang/es_AR.mo b/app/examples/Games/Puzzle1To8/.lang/es_AR.mo new file mode 100644 index 0000000000000000000000000000000000000000..bdb317ace290bcb79df9181994df299df55b0aa6 GIT binary patch literal 3064 zcmbVOOKdAi6)j-+ng75Nq?jir8!Ty5=MP-;-*QBA2GfO>_ zaWF)++IVI>70ynbo)f-K95Z=430K;2la{umMCar!y3iv}D=u9W~Umt;~8>hw;&4y@Y88BB6P>7atwQ zt{P+H78T(*e=e*RAZ=!05VM%W6-z*E6@k2*$EX3~9BGxG)-}>dZCVZ_tLshvY+2DShb6c#fj1s{Dr~d#Q7AubfVAU*d9# zDGtf*CTJ5YZc8}Y^&W}?j8nwSH*1HQW{xKhpP6Gi*k&Q}Ft60%(NyLdiJd=lMDII) zxTlX)D`V|c3w7djC?#!Rv_nETHE5}@qfYgl)fs1nIaBxmE5XZtHoGFspVDdkuWp^GIXb?c%51yVglANq%yXs zDRdB02{L(D)lK8J@wO47VGK5D%Zown)K_UODb=+}mW`iK`s>tojQCUv9! z>4lg)>~Id6( z!ezp)-bQn!$WW6GViRhr4kchnp_A@AHomalBI2~*SH4*USY=L z`qC)ZJB$k4VkUr7B^CF`-fG5uzTLZ=_Gt;t@Sp=qYt6v#OW1-#4PyzqGgCQoQ>b?D6O*G#;G3D> v{3bX%Vc(!Z?AhVQk)pqiK5oD#3?Z@*qWXKpN9q;2a^K?}buP7`&W-vnIuNns literal 0 HcmV?d00001 diff --git a/app/examples/Games/Puzzle1To8/.lang/es_AR.po b/app/examples/Games/Puzzle1To8/.lang/es_AR.po new file mode 100644 index 00000000..35060de0 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.lang/es_AR.po @@ -0,0 +1,97 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Puzzle 1 to 8" +msgstr "-" + +#: .project:2 FrmAyuda.class:41 +msgid "Place the numbers 1 to 8 in each of the boxes without repeating, so that each box does not contain a correlative number to its neighbors or adjacent boxes. See the following examples:" +msgstr "Ubique los números del 1 al 8 en cada uno de los casilleros sin repetirlos, de manera tal que cada casillero no contenga un número consecutivo al de sus casilleros vecinos o limítrofes. Observe los siguientes ejemplos:" + +#: FMain.class:65 +msgid "Puzzle 1 to 8 - Locate the numbers!" +msgstr "Puzzle 1 to 8 - Disperse los números!" + +#: FMain.class:73 +msgid "Are you sure?" +msgstr "Desea salir?" + +#: FMain.class:73 +msgid "Yes" +msgstr "Si" + +#: FMain.class:94 +msgid "Congratulations! You did it!" +msgstr "Felicitaciones! Lo lograste!" + +#: FMain.class:146 +msgid "Game" +msgstr "Juego" + +#: FMain.class:149 +msgid "Clear" +msgstr "Limpiar" + +#: FMain.class:154 +msgid "Quit" +msgstr "Salir" + +#: FMain.class:160 +msgid "Help" +msgstr "Ayuda" + +#: FMain.class:163 FrmAyuda.class:36 +msgid "How to play?" +msgstr "Cómo jugar?" + +#: FMain.class:168 FrmAbout.class:34 +msgid "About..." +msgstr "Acerca de..." + +#: FrmAbout.class:43 +msgid "Author" +msgstr "Autor" + +#: FrmAbout.class:47 +msgid "" +"Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n" +" pablomileti@gmail.com" +msgstr "Autor: Pablo Mileti.

Pueden enviar sus dudas, sugerencias, bugs, etc, a la siguiente dirección de correo electrónico: pablomileti@gmail.com" + +#: FrmAbout.class:51 +msgid "License" +msgstr "Licencia" + +#: FrmAbout.class:55 +msgid "" +"\n" +" Copyright (C) 2010. Author: Pablo Mileti \n" +"\n" +"This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License along with this program. If not, see ." +msgstr "-" + +#: FrmAbout.class:65 FrmAyuda.class:65 +msgid "&Close" +msgstr "&Cerrar" + +#: FrmAyuda.class:53 +msgid "Way incorrect:

The numbers 1 and 2 are consecutive and are in adjoining boxes, so it is not allowed." +msgstr "Disposición incorrecta:

Los números 1 y 2 son consecutivos y estan en casilleros lindantes, por lo tanto es una disposición no permitida." + +#: FrmAyuda.class:70 +msgid "Way correct:

There aren't correlativity between to the numbers entered with respect to their adjoining boxes, so it's allowed." +msgstr "Disposición válida:

No existe correlatividad en los números ingresados con respecto a sus casilleros lindantes, por lo tanto es una disposición permitida." diff --git a/app/examples/Games/Puzzle1To8/.project b/app/examples/Games/Puzzle1To8/.project new file mode 100644 index 00000000..251fb3d8 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Puzzle 1 to 8 +Startup=FMain +Icon=logo.png +Version=3.6.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.vb +Description="Place the numbers 1 to 8 in each of the boxes without repeating, so that each box does not contain a correlative number to its neighbors or adjacent boxes. See the following examples:" +Authors="Pablo Mileti" +Environment="GB_GUI=gb.gtk" +TabSize=2 +Translate=1 +Language=en_US +Packager=1 diff --git a/app/examples/Games/Puzzle1To8/.src/Casillero.class b/app/examples/Games/Puzzle1To8/.src/Casillero.class new file mode 100644 index 00000000..3d3fc714 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.src/Casillero.class @@ -0,0 +1,66 @@ +' Gambas class file + +' Copyright(C)2010. Autor: Pablo Mileti + +'This program Is free software: you can redistribute it And / Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option)any later version. + +'This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.See the GNU General Public License For more details. + +'You should have received a Copy Of the GNU General Public License along With this program.IfNot, see < http: / / www.gnu.org / licenses / > . + + +'****English documentation***** +'This class represents each box of the game (8) + +' Public Var: +' Value ---> Contains the number that was put in a box +' isEmpty ---> Return true if value is 0, else return false +' isInvalid ---> Return true if Value there are a conflict with the value of another box + +' Public Methods: +'setValue(nValue As Integer) ---> Put a value in a box +'validateWith(nBox As Casillero) ---> Return true if there are a conflict with a neighbor box + + + +'* * * * Documentación en español******* +' Esta clase corresponde a representar cada uno de los 8 casilleros del juego + +' Variables públicas: +' Value ---> Contiene el numero que se ingreso en el casillero +' isEmpty ---> True si no hay valor, False si lo tiene +' isInvalid ---> True si hay un numero que esta en conflicto con otro casillero + +' Metodos públicos: +'setValue(nValue As Integer) - - > Se encarga de almacenar el nro de un casillero +'validateWith(nBox As Casillero) ---> Devuelve true si hay conflicto con otro casillero + +Public Value As Integer +Public isEmpty As Boolean +Public isInvalid As Boolean + +Public Sub _new() + Value = 0 + isEmpty = True + isInvalid = True +End + +Public Sub setValue(nValue As Integer) + Value = nvalue + If nvalue = 0 Then + isEmpty = True + Else + isEmpty = False + End If +End + +Public Function validateWith(nBox As Casillero) As Boolean + If isEmpty Or nBox.isEmpty Then Return False + If value + 1 = nBox.Value Then Return True + If value - 1 = nBox.Value And Value > 1 Then Return True +End + + + + + diff --git a/app/examples/Games/Puzzle1To8/.src/Esquema.class b/app/examples/Games/Puzzle1To8/.src/Esquema.class new file mode 100644 index 00000000..17cc9657 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.src/Esquema.class @@ -0,0 +1,110 @@ +' Gambas class file + +' Copyright(C)2010. Autor: Pablo Mileti + +'This program Is free software: you can redistribute it And / Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option)any later version. + +'This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.See the GNU General Public License For more details. + +'You should have received a Copy Of the GNU General Public License along With this program.IfNot, see < http: / / www.gnu.org / licenses / > . + + + +'****English documentation***** +'This class represents the gameboard, all boxes of the game. + +'Public var: +'Boxes---> Array of Casillero (box), there are 9 elements, it uses from 1 to 8. + +'Public methods: +'Set---> Put a value in a object Casillero (box). Receive 2 parameters: the position of the box in the gameboard and the value to put, return true if there are a conflict with another box. +'isRepeats---> Return true if value already exists in another box. +'isInvalid---> Compare a determinated box (parameter), with your neighbor boxes. Return true if there are conflicts. +'isWin---> If all boxes have a correct value and nothing is empty then return true - Win! + + + + +'* * * * * Documentación en español ****** +'Esta clase representa el juego en conjunto, es decir integro los 8 casilleros + +'Variables públicas: +'Boxes---> Array de Casilleros, son 9 elementos, se usan del 1 al 8. + +'Métodos públicos: +'Set---> Se encarga de darle un valor a cada Casillero. Recibe 2 parametros el nro de casillero y el valor, devuelve true si ese valor entra en conflicto con otro casillero +'isRepeats--->Devuelve true si el valor de un casillero ya esta en otro. +'isInvalid---> Compara un determinado casillero, segun su parametro, con sus casilleros vecinos. Devuelve true si hay conflicto +'isWin---> Verifica si todos los casilleros tienen un valor correcto, si todos son correctos y no hay ninguno vacio devuelve true. + +Public Boxes As New Casillero[9] + +Public Sub _new() + Dim i As Integer + For i = 1 To 8 + Boxes[i] = New Casillero + Next +End + +Public Function Set(nBox As Integer, nValue As Integer) As Boolean + Boxes[nbox].setValue(nValue) + If (isRepeats(nBox) And nValue <> 0) Or isInvalid(nBox) Then + Boxes[nBox].isInvalid = True + Return True + Else + Boxes[nBox].isInvalid = False + Return False + End If +End + +Private Function isRepeats(nBox As Integer) As Boolean + Dim i As Integer + For i = 1 To 8 + If i <> nBox Then + If Boxes[nBox].Value = Boxes[i].Value Then + Return True + End If + End If + Next + Return False +End + +Private Function isInvalid(nBox As Integer) As Boolean +Select Case nBox +Case 1 + Boxes[1].isInvalid = Boxes[1].validateWith(Boxes[2]) Or Boxes[1].validateWith(Boxes[3]) Or Boxes[1].validateWith(Boxes[4]) + Return Boxes[1].isInvalid +Case 2 + Boxes[2].isInvalid = Boxes[2].validateWith(Boxes[1]) Or Boxes[2].validateWith(Boxes[3]) Or Boxes[2].validateWith(Boxes[5]) Or Boxes[2].validateWith(Boxes[6]) + Return Boxes[2].isInvalid +Case 3 + Boxes[3].isInvalid = Boxes[3].validateWith(Boxes[1]) Or Boxes[3].validateWith(Boxes[2]) Or Boxes[3].validateWith(Boxes[4]) Or Boxes[3].validateWith(Boxes[5]) Or Boxes[3].validateWith(Boxes[6]) Or Boxes[3].validateWith(Boxes[7]) + Return Boxes[3].isInvalid +Case 4 + Boxes[4].isInvalid = Boxes[4].validateWith(Boxes[1]) Or Boxes[4].validateWith(Boxes[3]) Or Boxes[4].validateWith(Boxes[6]) Or Boxes[4].validateWith(Boxes[7]) + Return Boxes[4].isInvalid +Case 5 + Boxes[5].isInvalid = Boxes[5].validateWith(Boxes[2]) Or Boxes[5].validateWith(Boxes[3]) Or Boxes[5].validateWith(Boxes[6]) Or Boxes[5].validateWith(Boxes[8]) + Return Boxes[5].isInvalid +Case 6 + Boxes[6].isInvalid = Boxes[6].validateWith(Boxes[2]) Or Boxes[6].validateWith(Boxes[3]) Or Boxes[6].validateWith(Boxes[4]) Or Boxes[6].validateWith(Boxes[5]) Or Boxes[6].validateWith(Boxes[7]) Or Boxes[6].validateWith(Boxes[8]) + Return Boxes[6].isInvalid +Case 7 + Boxes[7].isInvalid = Boxes[7].validateWith(Boxes[3]) Or Boxes[7].validateWith(Boxes[4]) Or Boxes[7].validateWith(Boxes[6]) Or Boxes[7].validateWith(Boxes[8]) + Return Boxes[7].isInvalid +Case 8 + Boxes[8].isInvalid = Boxes[8].validateWith(Boxes[5]) Or Boxes[8].validateWith(Boxes[6]) Or Boxes[8].validateWith(Boxes[7]) + Return Boxes[8].isInvalid +End Select +End + + +Public Function isWin() As Boolean + Dim i As Integer + For i = 1 To 8 + If Boxes[i].isInvalid Or Boxes[i].isEmpty Then + Return False + End If + Next + Return True +End diff --git a/app/examples/Games/Puzzle1To8/.src/FMain.class b/app/examples/Games/Puzzle1To8/.src/FMain.class new file mode 100644 index 00000000..de579899 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.src/FMain.class @@ -0,0 +1,111 @@ +' Gambas class file + +' Copyright(C)2010. Autor: Pablo Mileti + +'This program Is free software: you can redistribute it And / Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option)any later version. + +'This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.See the GNU General Public License For more details. + +'You should have received a Copy Of the GNU General Public License along With this program.IfNot, see < http: / / www.gnu.org / licenses / > . + + + +'****English documentation***** +'Public sub +'Winner() ---> Procedure to Finish the game and write a congratulation message! +'updateBoxes()---> Rewrite the values in each box to compare with the last value wrote. If it presents conflict, mark it with red color. + + +'* * * * * Documentación en español ****** +'Public sub +'Winner() ---> Ha ganado el juego, finalizarlo y felicitar! +'updateBoxes()---> Volver a escribir los valores en cada casillero para que se compare con el ultimo valor ingresado y se coloree si ha conflicto + + +Public Game As Esquema + +Public Sub Form_Open() + Game = New Esquema + Me.Window.Center +End + +Public Sub TXT_Change() + Dim value As Integer + If Last.text = "" Then + value = 0 + Else + value = Val(Last.text) + End If + Game.Set(Last.tag, value) + updateBoxes() + If Game.isWin() Then Winner() +End + +Public Sub TXT_KeyPress() + If Not (Key.code = Key.BackSpace Or Key.code = Key.Delete Or Key.Code = Key.F10) Then + If InStr("12345678", Key.Text) = 0 Then Stop Event + End If +End + +Public Sub MnuReiniciar_Click() + Dim control As Object + Game = New Esquema + For Each control In Me.Children + If Object.Type(control) = "TextBox" Then + control.text = "" + control.foreground = &H000000& + control.background = &HFFFFFF& + control.enabled = True + End If + Next + Me.Title = ("Puzzle 1 to 8 - Locate the numbers!") +End + +Public Sub MnuSalir_Click() + Me.Close +End + +Public Sub Form_Close() + If Message.Question(("Are you sure?"), ("Yes"), ("No")) = 2 Then + Stop Event + End If +End + +Public Sub MnuAutor_Click() + FrmAbout.Showmodal() +End + +Public Sub MnuComoJugar_Click() + FrmAyuda.Show() +End + +Public Sub Winner() + Dim control As Object + For Each control In Me.Children + If Object.Type(control) = "TextBox" Then + control.background = &FFFF9F& + control.enabled = False + End If + Next + Me.Title = ("Congratulations! You did it!") +End + +Private Sub updateBoxes() + Dim control As Object + Dim value As Integer + For Each control In Me.Children + If Object.Type(control) = "TextBox" Then + If control.text = "" Then + value = 0 + Else + value = Val(control.text) + End If + If Game.Set(control.tag, value) Then + control.foreground = &HFF0000& + Else + control.foreground = &000000& + End If + End If + Next +End + diff --git a/app/examples/Games/Puzzle1To8/.src/FMain.form b/app/examples/Games/Puzzle1To8/.src/FMain.form new file mode 100644 index 00000000..f11da74a --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.src/FMain.form @@ -0,0 +1,103 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,62,50) + Background = Color.LightForeground + Text = ("Puzzle 1 to 8 - Locate the numbers!") + Icon = Picture["logo.png"] + Resizable = False + { MnuJuego Menu + Text = ("Game") + { MnuReiniciar Menu + Text = ("Clear") + Picture = Picture["icon:/small/refresh"] + } + { MnuSalir Menu + Text = ("Quit") + Picture = Picture["icon:/small/quit"] + } + } + { Mnuayuda Menu + Text = ("Help") + { MnuComoJugar Menu + Text = ("How to play?") + Picture = Picture["icon:/small/help"] + } + { MnuAutor Menu + Text = ("About...") + Picture = Picture["icon:/small/gambas"] + } + } + { TextBox1 TextBox TXT + Name = "TextBox1" + MoveScaled(25,5,12,9) + Font = Font["Bitstream Charter,+8"] + Tag = "1" + Text = ("") + Alignment = Align.Center + MaxLength = 1 + } + { TextBox2 TextBox TXT + Name = "TextBox2" + MoveScaled(13,14,12,9) + Font = Font["Bitstream Charter,+8"] + Tag = "2" + Text = ("") + Alignment = Align.Center + MaxLength = 1 + } + { TextBox3 TextBox TXT + Name = "TextBox3" + MoveScaled(25,14,12,9) + Font = Font["Bitstream Charter,+8"] + Tag = "3" + Text = ("") + Alignment = Align.Center + MaxLength = 1 + } + { TextBox4 TextBox TXT + Name = "TextBox4" + MoveScaled(37,14,12,9) + Font = Font["Bitstream Charter,+8"] + Tag = "4" + Text = ("") + Alignment = Align.Center + MaxLength = 1 + } + { TextBox5 TextBox TXT + Name = "TextBox5" + MoveScaled(13,23,12,9) + Font = Font["Bitstream Charter,+8"] + Tag = "5" + Text = ("") + Alignment = Align.Center + MaxLength = 1 + } + { TextBox6 TextBox TXT + Name = "TextBox6" + MoveScaled(25,23,12,9) + Font = Font["Bitstream Charter,+8"] + Tag = "6" + Text = ("") + Alignment = Align.Center + MaxLength = 1 + } + { TextBox7 TextBox TXT + Name = "TextBox7" + MoveScaled(37,23,12,9) + Font = Font["Bitstream Charter,+8"] + Tag = "7" + Text = ("") + Alignment = Align.Center + MaxLength = 1 + } + { TextBox8 TextBox TXT + Name = "TextBox8" + MoveScaled(25,32,12,9) + Font = Font["Bitstream Charter,+8"] + Tag = "8" + Text = ("") + Alignment = Align.Center + MaxLength = 1 + } +} diff --git a/app/examples/Games/Puzzle1To8/.src/FrmAbout.class b/app/examples/Games/Puzzle1To8/.src/FrmAbout.class new file mode 100644 index 00000000..217d0bdd --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.src/FrmAbout.class @@ -0,0 +1,18 @@ +' Gambas class file + +' Copyright(C)2010. Autor: Pablo Mileti + +'This program Is free software: you can redistribute it And / Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option)any later version. + +'This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.See the GNU General Public License For more details. + +'You should have received a Copy Of the GNU General Public License along With this program.IfNot, see < http: / / www.gnu.org / licenses / > . + + +Public Sub Form_Open() + Me.Window.Center +End + +Public Sub Button1_Click() + Me.Close +End diff --git a/app/examples/Games/Puzzle1To8/.src/FrmAbout.form b/app/examples/Games/Puzzle1To8/.src/FrmAbout.form new file mode 100644 index 00000000..7359e72f --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.src/FrmAbout.form @@ -0,0 +1,39 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,75,36) + Text = ("About...") + Resizable = False + { TabStrip1 TabStrip + MoveScaled(1,1,52,34) + Arrangement = Arrange.Fill + Margin = True + Count = 2 + Index = 0 + Text = ("Author") + { TextLabel1 TextLabel + MoveScaled(2,2,41,21) + Text = ("Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n pablomileti@gmail.com") + Alignment = Align.Justify + } + Index = 1 + Text = ("License") + { TextArea1 TextArea + MoveScaled(2,3,42,22) + Text = ("\n Copyright (C) 2010. Author: Pablo Mileti \n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see .") + ReadOnly = True + Wrap = True + ScrollBar = Scroll.Vertical + } + Index = 0 + } + { Button1 Button + MoveScaled(54,31,20,4) + Text = ("&Close") + } + { PictureBox1 PictureBox + MoveScaled(54,1,20,20) + Picture = Picture["logo.png"] + Stretch = True + } +} diff --git a/app/examples/Games/Puzzle1To8/.src/FrmAyuda.class b/app/examples/Games/Puzzle1To8/.src/FrmAyuda.class new file mode 100644 index 00000000..42f43ffd --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.src/FrmAyuda.class @@ -0,0 +1,18 @@ +' Gambas class file + + +' Copyright(C)2010. Autor: Pablo Mileti + +'This program Is free software: you can redistribute it And / Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option)any later version. + +'This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.See the GNU General Public License For more details. + +'You should have received a Copy Of the GNU General Public License along With this program.IfNot, see < http: / / www.gnu.org / licenses / > . + +Public Sub Button1_Click() + Me.close +End + +Public Sub Form_Open() + Me.Window.center +End diff --git a/app/examples/Games/Puzzle1To8/.src/FrmAyuda.form b/app/examples/Games/Puzzle1To8/.src/FrmAyuda.form new file mode 100644 index 00000000..18c08bd5 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/.src/FrmAyuda.form @@ -0,0 +1,37 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,68,66) + Background = Color.LightForeground + Text = ("How to play?") + Resizable = False + { TextLabel1 TextLabel + MoveScaled(2,2,63,11) + Text = ("Place the numbers 1 to 8 in each of the boxes without repeating, so that each box does not contain a correlative number to its neighbors or adjacent boxes. See the following examples:") + Alignment = Align.Justify + } + { PictureBox1 PictureBox + MoveScaled(32,15,32,21) + Picture = Picture["ejemplo1.png"] + Stretch = True + } + { TextLabel3 TextLabel + MoveScaled(2,15,28,21) + Text = ("Way incorrect:

The numbers 1 and 2 are consecutive and are in adjoining boxes, so it is not allowed.") + Alignment = Align.Justify + } + { PictureBox2 PictureBox + MoveScaled(32,38,32,21) + Picture = Picture["ejemplo2.png"] + Stretch = True + } + { Button1 Button + MoveScaled(23,60,22,5) + Text = ("&Close") + } + { TextLabel2 TextLabel + MoveScaled(2,39,28,18) + Text = ("Way correct:

There aren't correlativity between to the numbers entered with respect to their adjoining boxes, so it's allowed.") + Alignment = Align.Justify + } +} diff --git a/app/examples/Games/Puzzle1To8/Licence b/app/examples/Games/Puzzle1To8/Licence new file mode 100644 index 00000000..b17f91b9 --- /dev/null +++ b/app/examples/Games/Puzzle1To8/Licence @@ -0,0 +1,189 @@ +GNU GENERAL PUBLIC LICENSE + +Version 3, 29 June 2007 + +Copyright © 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. +Preamble + +The GNU General Public License is a free, copyleft license for software and other kinds of works. + +The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. +TERMS AND CONDITIONS +0. Definitions. + +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on the Program. + +To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. +1. Source Code. + +The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. + +The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. +2. Basic Permissions. + +All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. +3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. +4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. +5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: + + * a) The work must carry prominent notices stating that you modified it, and giving a relevant date. + * b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. + * c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. + * d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. +6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: + + * a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. + * b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. + * c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. + * d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. + * e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. + +A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. +7. Additional Terms. + +“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: + + * a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or + * b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or + * c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or + * d) Limiting the use for publicity purposes of names of licensors or authors of the material; or + * e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or + * f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. +8. Termination. + +You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. +9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. +10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. + +An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. +11. Patents. + +A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. + +In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. +12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. +13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. +14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. +15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. +16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/app/examples/Games/Puzzle1To8/ejemplo1.png b/app/examples/Games/Puzzle1To8/ejemplo1.png new file mode 100644 index 0000000000000000000000000000000000000000..fa41d8272840de5b877321438986b3efa602b9d2 GIT binary patch literal 5594 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 Uxya5^Ea2e44NVO2H*Y`qC#&<-5dZ)H literal 0 HcmV?d00001 diff --git a/app/examples/Games/Puzzle1To8/ejemplo2.png b/app/examples/Games/Puzzle1To8/ejemplo2.png new file mode 100644 index 0000000000000000000000000000000000000000..4fd108c2eb12eeded962c8024fb618748d72a5a3 GIT binary patch literal 5481 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 Warw)WKiOF&z~qMIb?i0Ignt2a#J73? literal 0 HcmV?d00001 diff --git a/app/examples/Games/Puzzle1To8/logo.png b/app/examples/Games/Puzzle1To8/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bd7a6ecb124c71d724093c1265d18f04e6d6abda GIT binary patch literal 41279 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

    aoPw@^hX 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/RobotFindsKitten/.directory b/app/examples/Games/RobotFindsKitten/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Games/RobotFindsKitten/.icon.png b/app/examples/Games/RobotFindsKitten/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..22619f882e95662d476a4c1fc66c62e6b446aed6 GIT binary patch literal 4141 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/RobotFindsKitten/.lang/ca.mo b/app/examples/Games/RobotFindsKitten/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..cbd0f0d82a90c794d98590fe150482e09c628749 GIT binary patch literal 1230 zcmY*YO>Yx15M5{iDZKz^4iohNh;B%NKvhaAN=Zeb6;T@~9G%@s;%3(lwl{Po{s#Pp z_P~i7;@1BGe}!kKNsFb~v3K9R`S9C6cbC6KST|w!V0*9?*jJcer?4g18LV+FihjVa z!v11^3x4T(6y1hjfq#Z~_uxJL5RXlqkKjH2`*IZF82y631ONNt40y;57vWV{KK|;u z;SU{~M7xUINLm_-oY$z-1qs7sbYDAqu==n@pLAj9SoaAar)eNnN@M99s~Yuuq!V`R zKPEb#WHJ$sQ|gysW#SWQ0Zf?mfTqGoT>w9lYG~=pNG79@Od-{r@gOA53+s@q06;p< zL*kSjn_Ma;Iq>9Vh@HieHUt)55Qk2PRq9i)x@g6hZ5LU#GH@y3= zv+ym9&?agP?=UMGb6KlI7zhn?u^2;@ju4B{4E5S9TQveh-wei4m}AI2D>UjFeawlA zchmR-8!NSHQRsQc7x*^JwbFeTAMjNAukkC)vqjB%qY>Ai#LYT28?C47kLrzjy|O2) zi+e^Wn+c~)i{5CHN;(v!&gl8DX8q?0W+~Y|mq}rflB#dR(e&5@P*DrCtjscsvqY|BBY%N;vOIPV;g%Mf2 zqfKsGq$c5L+szG{ZQF{fZE9>(KE_?msdVwY%oc46Co-WbL8%=@;&Z`J#<5P;$l@9} zyQ, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: RobotFindsKitten\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 21:58+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "gambrfk" +msgstr "-" + +#: Frfk.class:180 +msgid "You found kitten! Way to go, robot!" +msgstr "Heu trobat el gatet! Ben fet robot!" + +#: Frfk.class:203 +msgid "robotfindskitten" +msgstr "robottrobaelgatet" + +#: Frfk.class:211 +msgid "Esc" +msgstr "Esc" + +#: Frfk.class:231 +msgid "In this game, you are robot (#). Your job is to find kitten. This task is complicated by the existence of various things which are not kitten. Robot must touch items to determine if they are kitten or not. The game ends when robot finds kitten. Alternatively, you may end the game by hitting the Esc key.\n" +msgstr "En aquest joc, sou el robot (#). La vostra missió es trobar el gatet. Aquesta tasca es complica per l'existència de diverses coses que no son el gatet. El robot ha de tocar les coses per determinar si son el gatet o no. El joc acaba quan el robot troba el gatet. També podeu acabar el joc tot prement la tecla Esc.\n" + diff --git a/app/examples/Games/RobotFindsKitten/.lang/cs.mo b/app/examples/Games/RobotFindsKitten/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..e49c6a9cf1e8e046bf9ec68b218012d1e3cd2cdd GIT binary patch literal 1157 zcmYjPO^+Kz5FJPW$+;lTLDVV-Ki6btE z^By^%oN{9iR_;jTGWOqqzrySGvYVD_YP!3sUe)XO&qud^kFef{-GvQccVNH3{Q47i z3w8;+|4tOG;qSrz#s3HJx89AS58>~?M>x9+4;Oumq_6Sw3H+znpWWV_|9a(ryYfEG zWBY}pusq(|>pj0oZ5H(vxk6f+i<^ze^x3`7yL6@-Lkm461UXGJsd6f% zbF8{_>@%IPrN7K{eO}2-IL>LhLRBVyE-eBxCOxC2Fj6;&FQl4V`nix<5x^9n{;xO* zz;$CCz#0Td$8`YC*|DjmVv;jod=+wcd87@Yif@R+B*ZHB5Uj37@on2{l%zr}C7fJx zwc1Txid|5H_=n_aN~h`Ms6QTc4#)9?m(pLx2cWY@XK6BthvWDl zp=aaXu>ZwR<5Aif2CG}ha>&ZHE0t~>o$E?(-*8DKHD+_c%2)7-aV2-TC^Xghbm^H9 zI8vL7fReAaZ|KDW!Vg>D^=`l}-N;Viu-?7~f@3O7Lro|k9SO2d+R%ings\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "gambrfk" +msgstr "-" + +#: Frfk.class:180 +msgid "You found kitten! Way to go, robot!" +msgstr "NaÅ¡el jsi kočku! Jdi do cesty, robot!" + +#: Frfk.form:13 +msgid "robotfindskitten" +msgstr "-" + +#: Frfk.form:21 +msgid "Esc" +msgstr "-" + +#: Frfk.form:40 +msgid "In this game, you are robot (#). Your job is to find kitten. This task is complicated by the existence of various things which are not kitten. Robot must touch items to determine if they are kitten or not. The game ends when robot finds kitten. Alternatively, you may end the game by hitting the Esc key.\n" +msgstr "V této hře, jsi robot (#). Vaším úkolem je najít kotě. Tato úloha je komplikována existencí různých věcí, které nejsou kotě. Robot se musí dotknout položek k určení, jsou-li kotě nebo ne. Hra končí, když zjistí, kotě robot. Alternativně můžete hru ukončit a stisknouttlačítko Esc.\n" diff --git a/app/examples/Games/RobotFindsKitten/.lang/de.mo b/app/examples/Games/RobotFindsKitten/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..d6a6ab9aab13b2b96d9e5bfe18e32e844e7ff265 GIT binary patch literal 1233 zcmZ8g&2G~`5H_@c6fTG}h^e@MhyxsuQ1}s6V@lMdsge`~x3xX7m&Ci(?z(L%UIXz0 z9N@qkaO)ZI2Jk9;Yqt%s(rE1U%=dls<6qa;KL$9LVK-m{*j3mEm_J`&Yp}1dhZln2 z8~kn9Z#-XtU%MCt*WjV2B$_6Jw>@qB-u zOa3LcY0y{X3TY`9CAVp*D-wpu=tMiZbNge<4%hIHP07J(^~p3#9YQdfvCq{=NF7BVerG6kwL#BohrR@Q;6LV$E! z*2Eb*HlVv1+bKG@_-l1rs`5GeNrvk z_rjM@piF43@6aon^Rm+l&=8vG>bwmb^ai-;F^dG|>+S#SIaWoExqp%mzt5J8@zxQsm z6SoG!x^QZQvJ2s~>C(%+!GLz6VN6fsVbmYIhS_os=J=>eU3?HxJ zysR0UFjlF~(8Ml1PNZuEq2IVnvgMK{`;r%wU`Hsch^3->{A`-d>*0nes%E)JFm*(+ zZBr&JZJ&I0$Elz0F=m0$`ep3lgAbb5dw49_xHcWKT+jhJofeW2#?*tD)6GN(D^oM}%WMkA zx^XR0p(6-5EB;@AoM+fIlv)U6uQr$FLaC;6KB0juCBy\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "gambrfk" +msgstr "-" + +#: Frfk.class:180 +msgid "You found kitten! Way to go, robot!" +msgstr "Du hast das Kätzchen gefunden. Gut gemacht, Roboter!" + +#: Frfk.form:13 +msgid "robotfindskitten" +msgstr "-" + +#: Frfk.form:21 +msgid "Esc" +msgstr "-" + +#: Frfk.form:40 +msgid "In this game, you are robot (#). Your job is to find kitten. This task is complicated by the existence of various things which are not kitten. Robot must touch items to determine if they are kitten or not. The game ends when robot finds kitten. Alternatively, you may end the game by hitting the Esc key.\n" +msgstr "In diesem Spiel bist du ein Roboter (#). Deine Aufgabe ist es, das Kätzchen zu finden. Das ist kompliziert, weil verschiedene Dinge existieren, die kein Kätzchen sind. Du musst die Dinge berühren, um zu bestimmen, ob sie das Kätzchen sind oder nicht. Das Spiel endet, wenn der Roboter das Kätzchen gefunden hat. Alternativ kannst du das Spiel mit der ESC-Taste beenden.\n" diff --git a/app/examples/Games/RobotFindsKitten/.lang/es.mo b/app/examples/Games/RobotFindsKitten/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..ddde869d45f225a38cc9298daef73777eb07676e GIT binary patch literal 1080 zcmaJeF{|nydJ=v=Bp26d~I-U#R09V!VbwO;?prgpmq@}4? z@;0q>Ny0E0J=Tt%KYh`p3tbwT>oFn8X_`ot(Ly@Ms!1bX>4aVQV5*lzE>qz+qwxx; zOnfCR5>qBUp-W+;E|EWzYHI1@Os2DnOo3{RIH-u1rF9@Hksuu}E8>hDo267ta^k^P zRc&2I+7M8DLtITltTIo*svgC6ZPy%84kRU7Bj|am}zt=eJhXcNpJ{Y#K zs1{ws(ID*h!*)dP`mJu~<@K(lsgx#fliuO!JbbdYkZePypN8A{v(D zj2Mf#_5mmY{6-iObaE}F*g63z85TZct))y*fw#?Zc`YR=DC0^?!lzk$Ka`BuC#%&B zS}miD#|#;zAj)sD3t>D&Eha>)yj4jWNKK0KdKj;Ek;_kl(o(2QQ;m!MOKcA~oBlVV if_$*t>opW5qum=aUIQ0~y$UK&Oy@Yo7b^Ro!u|nIhEU1? literal 0 HcmV?d00001 diff --git a/app/examples/Games/RobotFindsKitten/.lang/es.po b/app/examples/Games/RobotFindsKitten/.lang/es.po new file mode 100644 index 00000000..463341c0 --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/.lang/es.po @@ -0,0 +1,28 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: Frfk.class:220 +msgid "Esc" +msgstr "Esc" + +#: .project:1 +msgid "gambrfk" +msgstr "gambrfk" + +#: Frfk.class:211 +msgid "In this game, you are robot (#). Your job is to find kitten. This task is complicated by the existence of various things which are not kitten. Robot must touch items to determine if they are kitten or not. The game ends when robotfindskitten. Alternatively, you may end the game by hitting the Esc key.\n" +msgstr "En este juego, usted es robot (#). Su trabajo es encontrar a Kitten. Esta tarea es complicada por la existencia de varias cosas que no son Kitten. Robot debe tocar las cosas para determinar si son Kitten o no. El juego finaliza cuando Robot encuentra a Kitten. Alternativamente usted puede terminar el juego presionando la tecla Esc.\n" + +#: Frfk.class:204 +msgid "robotfindskitten" +msgstr "robotfindskitten" + diff --git a/app/examples/Games/RobotFindsKitten/.project b/app/examples/Games/RobotFindsKitten/.project new file mode 100644 index 00000000..4d55a227 --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.0 +Title=gambrfk +Startup=Frfk +Icon=heart.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +TabSize=2 +Translate=1 +Language=fr +ExecPath=/home/benoit/gambas/gambas.link/share/gambas/examples/Games/RobotFindsKitten/RobotFindsKitten +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence diff --git a/app/examples/Games/RobotFindsKitten/.src/Frfk.class b/app/examples/Games/RobotFindsKitten/.src/Frfk.class new file mode 100644 index 00000000..74867b74 --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/.src/Frfk.class @@ -0,0 +1,186 @@ +' Gambas class file +Public NKIs As Object[] +Public Robot As Object +Public OrigHeight As Integer +Public OrigWidth As Integer +Public OrigFontSize As Integer +Public KittenIsFound As Boolean +Public NumNKIs As Integer +Public KittenIndex As Integer + +Public Sub Timer1_Timer() + + Dim middle As Float + Dim kitten As Object + + Try TextLabel1.setfocus() + If kittenisfound Then + kitten = nkis[kittenindex] + middle = CFloat(kitten.x + robot.x) / 2 + robot.x = robot.x + Round(CFloat(middle - 8 - robot.x) / 2) + kitten.x = kitten.x + Round(CFloat(middle + 8 - kitten.x) / 2) + End If +End + +Public Sub TextLabel1_KeyPress() + + Dim i As Integer + + If Key.Code = Key.Escape Then Esc_Click + If kittenisfound Then Return + + Select Case Key.Code + Case Key.Up + For i = 0 To nkis.count - 1 + If Abs(robot.y - (nkis[i].y + nkis[i].height)) < 4 And Abs(robot.x - nkis[i].x) < 4 Then + Textlabel1.text = nkis[i].tag + If nkis[i].tag = "kitten" Then FoundKitten(i) + Return + Endif + Next + If robot.y - robot.height >= Textlabel1.height Then + robot.y = robot.y - robot.height + Endif + Case Key.Down + For i = 0 To nkis.count - 1 + If Abs(robot.y + robot.height - nkis[i].y) < 4 And Abs(robot.x - nkis[i].x) < 4 Then + TextLabel1.text = nkis[i].tag + If nkis[i].tag = "kitten" Then FoundKitten(i) + Return + Endif + Next + If robot.y + robot.height + robot.height < Me.clientheight Then + robot.y = robot.y + robot.height + Endif + Case Key.Left + For i = 0 To nkis.count - 1 + If Abs(robot.x - (nkis[i].x + nkis[i].width)) < 4 And Abs(robot.y - nkis[i].y) < 4 Then + TextLabel1.text = nkis[i].tag + If nkis[i].tag = "kitten" Then FoundKitten(i) + Return + Endif + Next + If robot.x - robot.width >= 0 Then + robot.x = robot.x - robot.width + Endif + Case Key.Right + For i = 0 To nkis.count - 1 + If Abs(robot.x + robot.width - nkis[i].x) < 4 And Abs(robot.y - nkis[i].y) < 4 Then + TextLabel1.text = nkis[i].tag + If nkis[i].tag = "kitten" Then FoundKitten(i) + Return + Endif + Next + If robot.x + robot.width + robot.width < Me.clientwidth Then + robot.x = robot.x + robot.width + Endif + End Select + +End + +Public Sub Esc_Click() + + Me.close + +End + + +Public Sub Form_Open() + + Dim tmp As Label + Dim i As Integer + Dim nkistring As String + Dim nkitext As String[] + NKIs = New Object[] + nkitext = New String[] + + Randomize + origheight = Me.height + origwidth = Me.width + origfontsize = 12 + Try nkistring = File.load("nkis.txt") + nkitext = Split(nkistring, "\n") + KittenisFound = False + numnkis = 20 + + For i = 1 To NumNKIs + tmp = New Label(Me) + tmp.x = Int(Rnd(0, Me.width / 16)) * 16 + tmp.y = Int(Rnd(5, Me.height / 16)) * 16 + tmp.AutoResize = False + tmp.width = 16 + tmp.height = 16 + tmp.font.size = 12 + tmp.Foreground = Int(Rnd(32, 255)) * 65536 + Int(Rnd(32, 255)) * 256 + Int(Rnd(32, 255)) + tmp.alignment = Align.center + tmp.text = "#" + Do While tmp.text = "#" + tmp.text = Chr(Rnd(33, 126)) + Loop + tmp.tag = nkitext[Int(Rnd(0, nkitext.count))] + NKIs.add(tmp) + Next + nkis[nkis.count - 1].tag = "kitten" + robot = New Label(Me) + robot.x = 192 + robot.y = 192 + robot.AutoResize = False + robot.height = 16 + robot.width = 16 + robot.font.size = 12 + robot.Background = Color.white + robot.Foreground = Color.black + robot.alignment = Align.center + robot.text = "#" + + +End + +Public Sub Form_Resize() + + Dim scalex As Float + Dim scaley As Float + Dim i As Integer + + 'STOP + scalex = CFloat(Me.width) / origwidth + scaley = CFloat(Me.height) / origheight + If nkis.count < NumNKIs Then Return + For i = 0 To nkis.count - 1 + NKIs[i].x = NKIs[i].x * scalex + NKIs[i].y = NKIs[i].y * scaley + NKIs[i].width = Round(NKIs[i].width * scalex) + NKIs[i].height = Round(NKIs[i].height * scaley) + If nkis[i].y < TextLabel1.height Then nkis[i].y = Round(TextLabel1.height / nkis[i].height) * nkis[i].height + NKIs[i].font.size = Round(NKIs[i].font.size * scaley) + Next + robot.x = robot.x * scalex + robot.y = robot.y * scaley + robot.width = Round(robot.width * scalex) + robot.height = Round(robot.height * scaley) + robot.font.size = Round(robot.font.size * scaley) + origwidth = Me.width + origheight = Me.height + +End + +Sub FoundKitten(nki As Integer) + + Dim i As Integer + For i = 0 To nkis.count - 1 + If i <> nki Then + nkis[i].visible = False + nkis[i].x = Me.width + 100 + nkis[i].y = Me.height + 100 + Endif + Next + heart.visible = True + KittenIsFound = True + kittenindex = nki + TextLabel1.text = ("You found kitten! Way to go, robot!") + nkis[nki].y = heart.y + heart.height + robot.y = heart.y + heart.height + robot.x = heart.x - robot.width + nkis[nki].x = heart.x + heart.width + +End diff --git a/app/examples/Games/RobotFindsKitten/.src/Frfk.form b/app/examples/Games/RobotFindsKitten/.src/Frfk.form new file mode 100644 index 00000000..8246f54f --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/.src/Frfk.form @@ -0,0 +1,32 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(25.1429,22.5714,110,79) + Background = &H000000& + Foreground = &HFFFFFF& + Text = ("robotfindskitten") + Resizable = False + { Esc Button + MoveScaled(102,1,7,5) + Visible = False + Background = &H000000& + Foreground = &HFFFFFF& + Text = ("Esc") + Border = False + Cancel = True + } + { Timer1 #Timer + Enabled = True + Delay = 200 + } + { Heart PictureBox + MoveScaled(40,20,29.1429,24) + Visible = False + Foreground = &HFFFFFF& + Picture = Picture["heart.png"] + } + { TextLabel1 TextLabel + MoveScaled(1,1,100,10) + Text = ("In this game, you are robot (#). Your job is to find kitten. This task is complicated by the existence of various things which are not kitten. Robot must touch items to determine if they are kitten or not. The game ends when robot finds kitten. Alternatively, you may end the game by hitting the Esc key.\n") + } +} diff --git a/app/examples/Games/RobotFindsKitten/COPYING b/app/examples/Games/RobotFindsKitten/COPYING new file mode 100644 index 00000000..d60c31a9 --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/app/examples/Games/RobotFindsKitten/heart.png b/app/examples/Games/RobotFindsKitten/heart.png new file mode 100644 index 0000000000000000000000000000000000000000..80a25fecc65eb6b55acfd0f06dbe059898e11113 GIT binary patch literal 9149 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 Hu0mjf?sI`2 literal 0 HcmV?d00001 diff --git a/app/examples/Games/RobotFindsKitten/nkis.txt b/app/examples/Games/RobotFindsKitten/nkis.txt new file mode 100644 index 00000000..743506b4 --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/nkis.txt @@ -0,0 +1,622 @@ +"201 Kitten Verbs, Fully Conjugated". You look for "find". +"50 Years Among the Non-Kitten Items", by Ann Droyd. +99 bottles of beer are on a wall here. +A 100 meter long chain of jumbo paper clips. +A 256 kilobyte write-only memory chip. +A 3-inch floppy disk. +A 3-sided Monty Python record. +A 540Hz tuning fork. +A baboon with a bassoon hoots angrily at you. +A badly dented high-hat cymbal lies on its side here. +A bag of groceries taken off the shelf before the expiration date. +A ball of pocket fluff. +A ball of yarn. +A big bass drum bearing a hole and suspicious clawmarks. +A big chunk of frozen chocolate pudding. +A bitchin' homemade tesla coil. +A blank deposit slip. +A bobolink is twittering a happy tune here. +A book: Feng Shui, Zen: the art of randomly arranging items that are not kitten. +A book with "Don't Panic" in large friendly letters across the cover. +A bottle of distilled water. +A bottle of hair tonic. +A bottle of oil! Refreshing! +A bottle of smelling salts. +A bowling ball with the name "Bob" inscribed on it. +A bowl of cherries. +A box of brand-new nixie tubes. +A box of dancing mechanical pencils. They dance! They sing! +A box of fumigation pellets. +A brain cell. Oddly enough, it seems to be functioning. +A breadbox. Nope, Kitten isn't in the breadbox. +A briefcase filled with spy stuff. +A broken metronome sits here, it's needle off to one side. +A Buttertonsils bar. +A caboodle. +A canister of pressurized whipped cream, sans whipped cream. +A can of Spam Lite. +A card shark sits here, practicing his Faro shuffle. He ignores you. +A card sharp sits here, practicing his Faro shuffle. He ignores you. +A chain hanging from two posts reminds you of the Gateway Arch. +A cluster of cattails are growing here. +A coat hanger hovers in thin air. Odd. +A cockatoo shrieks at you. +A compendium of haiku about metals. +A copy of DeCSS. They're a dime a dozen these days. +A copy of the Weekly World News. Watch out for the chambered nautilus! +A coupon for one free steak-fish at your local family diner. +A crouton. +A crowd of people, and at the center, a popular misconception. +A crystal ball. It doesn't seem to know where Kitten is. +A cyclops glowers angrily at you. +A dangly thing mangled by Kitten. +A dark-emitting diode. +"Address Allocation for Private Internets" by Yakov Rekhter et al. +A demonic voice proclaims "There is no kitten, only Zuul". You flee. +A digital clock. It's stuck at 2:17 PM. +A discarded bagpipe chanter reed. +A discarded pop bottle. +A discarded refrigerator box. Nope, Kitten isn't in the box. +A discredited cosmology, relic of a bygone era. +A dodecahedron bars your way. +A dumpster full of hair. +A family of integrals are here integrating. +A flamboyant feather boa. Now you can dress up like Carol Channing! +A flyer advertising a sale at Spatula City. +A flyer reads, "Please donate hydraulic fluid" +A forgotten telephone switchboard. +A forgotten telephone switchboard operator. +A freshly-baked pumpkin pie. +A frosted pink party-cake, half eaten. +A "Get Out of Jail Free" card. +A geyser sprays water high into the air. +A glorious fan of peacock feathers. +A gravestone stands here. "Izchak Miller, ascended." +A green yo-yo. +A grin. +A haircut and a real job. Now you know where to get one! +A hairless rat. +A half-eaten cheese sandwich. +A hammock stretched between a tree and a volleyball pole. +A hedgehog. It looks like it knows something important. +A helicopter has crashed here. +A herd of wild coffee mugs slumber here. +A herd of wild coffee mugs slumbers here. +A historical marker showing the actual location of /dev/null. +A hollow voice says "Fool." +A hollow voice says "Plugh". +Ah, the skirl of the pipes and the rustle of the silicon... +Ah, the uniform of a Revolutionary-era minuteman. +Air. +Air Guitar!!! NA na NA na!! +A jar of dehydrated water. +A jar of library paste. +A jar of Vegemite is playing hopscotch here. +A ketchup bottle (nearly empty). +A kitten sink, for washing kitten (if only kitten liked water). +A kitten source (to match the kitten sink). +A knight who says "Either I am an insane knave, or you will find kitten." +A large blue eye floats in midair. +A large pile of rubber bands. +A large snake bars your way. +A largish bath towel. +A leather pouch filled with multisided dice. +Alien underwear. +A little glass tub of Carmex. ($.89) Too bad you have no lips. +A livery stable! Get your livery! +A lone, forgotten comma, sits here, sobbing. +A lotus. You make an interesting pair. +A magical... magic thing. +A marijuana brownie. +A mason jar lies here open. It's label reads: "do not open!". +A mathematician calculates the halting probability of a Turing machine. +A meerkat... not even close. +A Mentos wrapper. +A mere collection of pixels. +A milk carton, with a black and white picture of kitten on the side. +A mouse. +An 80286 machine. +An abandoned used-car lot. +An AK-47 rifle. +An AK-97 rifle. +An animate blob of acid. Being metallic, you keep well away. +An aromatherapy candle burns with healing light. +An atomic vector plotter. +An autographed copy of "Primary Colors", by Anonymous. +An automated robot-doubter. It doesn't believe in you. +An automated robot-hater. It frowns disapprovingly at you. +An automated robot-liker. It smiles at you. +An eminently forgettable zahir. +An empty Altoids tin. +An empty coaxial cable spool. +An empty Penguin Mints tin. +An empty shopping bag. Paper or plastic? +An Enfield Mk3 rifle. +A neural net -- maybe it's trying to recognize kitten. +An expired transistor. +An FN-FAL rifle. +An ice cube. +An incredibly expensive "Mad About You" collector plate. +An M16 rifle. +An M1911A1 pistol. +An M9 pistol. +An old bootable business card, unfortunately cracked down the middle. +An old pattern is here going on and on. +An old rusty revolver. +A non-descript box of crackers. +Another rabbit? That's three today! +An oven mitt with kittens on it. +An overflowing bit bucket. +An overturned bottle of ink and lots of kitten pawprints. +A number of short theatrical productions are indexed 1, 2, 3, ... n. +An unlicensed nuclear accelerator. +Any ordinary robot could see from a mile away that this wasn't kitten. +A packet of catnip. +A packet of pipe cleaners. +A pair of combat boots. +A pair of saloon-style doors swing slowly back and forth here. +A paper shopping bag. Nope, Kitten isn't in the bag. +A parrot, kipping on its back. +A patch from the Mammoth Caves. +A patch of grape jelly grows here. +A patch of mushrooms grows here. +A pile of coaxial plumbing lies here. +A pile of coconuts. +A pink plastic watering can. +A piping-hot pizza. Useless. +A pizza, melting in the sun. +A plastic model of Kitten. +A plush Chewbacca. +Apparently, it's Edmund Burke. +Approaching. One car. J. Followed by. Two car. M, M. In five. Minutes. +A puddle of chocolate sauce. +A puddle of mud, where the mudskippers play. +A punch bowl, filled with punch and lemon slices. +A radio hisses away. Kitten must have been here. +A rancid corn dog. +A Remington 870 shotgun. +A robot comedian. You feel amused. +A rusty melon-baller. +A rusty slinky. It was such a wonderful toy! +A sack of doorknobs. +A sack of hammers. +A sack of wet mice. +A salmon hatchery? Look again. It's merely a single salmon. +A Sanrio catalog. +A scrap of parchment bears the single word, "meow". +A scratching-post. +A screwdriver. +A section of glowing phosphor cells sings a song of radiation to you. +A set of keys to a 2001 Rolls Royce. Worthless. +A shameless plug for Crummy: http://www.crummy.com/ +A shameless plug for Frotz: http://www.cs.csubak.edu/@@126dgriffi/proj/frotz/ +A shameless plug for the UCLA Linux Users Group: http://linux.ucla.edu/ +Ash is mumbling "KLAATU BARATA NI" here. +A signpost saying "TO KITTEN". It points in no particular direction. +A sign reads "Don't step on the Mome Raths". +A sign reads: "Go home!" +A sign reads: "No robots allowed!" +A singing frog. Useless. +A slightly-used smellovision set. +A smoking branding iron shaped like a 24-pin connector. +A spindle, and a grindle, and a bucka-wacka-woom! +A stack of 7 inch floppies wobbles precariously. +A statue of a girl holding a goose like the one in Gottingen, Germany. +A steam-powered bunnytron. +A stegosaurus, escaped from the stegosaurusfindsrobot game. It finds you. +A sub-atomic particle languishes here all alone. +A Swiss-Army knife. All of its appendages are out. (toothpick lost) +A team of arctic explorers is camped here. +A technical university in Australia. +A tetradrachm dated "42 B.C." +A Texas Instruments of Destruction calculator. +A tiny ceramic Kitten. It's probably not the Kitten you're looking for. +A toenail? What good is a toenail? +A toilet bowl occupies this space. +A ton of feathers. +A traffic signal. It appears to have been recently vandalized. +A train of thought chugs through here. +A trash compactor, compacting away. +A travel-sized cyclotron. +A tree with some jelly nailed to it. +A tribe of cannibals lives here. They eat Malt-O-Meal for breakfast, you know. +A troll. Ewww!!! +A tube of toothpaste. Too bad you have no teeth. +A tube of white lithium grease. Perfect for your robotic joints. +A tuft of kitten fur, but no kitten. +A vase full of artificial flowers is stuck to the floor here. +A vase of roses. +A voice booms out "Onward, kitten soldiers..." +A willing, ripe tomato bemoans your inability to digest fruit. +A wireframe model of a hot dog rotates in space here. +A wondrous and intricate golden amulet. Too bad you have no neck. +A zorkmid coin. +Baling wire and chewing gum. +Bibbidy bibbidy bibbidy bibbidy bibbidy bibbidy... +Big Bird is here looking for Mr. Looper. +Billions and billions of things that aren't Kitten. +Biscuits. +"Blup, blup, blup", says the mud pot. +Bright copper kettles. +BURRRRP!!!! Flavorful and full of protein! +Butane!!! +Carbonated Water, High Fructose Corn Syrup, Color, Phosphoric Acid, Flavors, Caffeine. +Carlos Tarango stands here, doing his best impression of Pat Smear. +Catsup and Mustard all over the place! It's the Human Hamburger! +Chewing gum and baling wire. +Clang, clang, clang goes the tranny! +Clifford Stoll is here selling Klein bottles. +Conan O'Brian, sans jawbone. +Could it be... a big ugly bowling trophy? +Daily hunger conditioner from Australasia +Dancing cold water pipes. Mikey must have been here. +Darth Vader is here looking for his Teddywookie. +"Dear robot, you may have already won our 10 MILLION DOLLAR prize..." +Definitely not Kitten. +Dinsdale! +Diogenes is here demanding whisky. +Dirty socks. +"Dogbert's tech support, how may I abuse you?" +Doodles Weaver is here looking over a horse race schedule. +Ed McMahon stands here, lost in thought. Seeing you, he bellows, "YES SIR!" +Ed Witten sits here, pondering string theory. +Empty jewelboxes litter the landscape. +Faboo! +Fonzie sits here, mumbling incoherently about a shark and a pair of waterskis. +For a moment, you feel something in your hands, but it disappears! +Free Dmitry Sklyarov! +Free Jon Johansen! +Gibble, Gobble, we ACCEPT YOU ... +"Go back to Libraria!", says Pat Schroeder. +Grind 'em up, spit 'em out, they're twigs. +Heart of Darkness brand pistachio nuts. +Heeeeeeeeeeeeres Johnny! +Hello, nurse! +Hello, sailor! +Here is a book about Robert Kennedy. +Here is no kitten but only rock, rock and no kitten and the sandy road. +Here's Cal Worthington and his dog "Spot"! +Here's Pete Peterson. His batteries seem to have long gone dead. +Hey, I bet you thought this was kitten. +Hey, look, it's war. What is it good for? Absolutely nothing. Say it again. +Hey, robot, leave those lists alone. +"Hi, I'm Anson Williams, TV's 'Potsy'." +Ho hum. Another synthetic a posteriori. +"How in heck can I wash my neck if it ain't gonna rain no more?" asks Farmer Al. +Hydraulic fluid and jagged metal bits. You recoil from the scene of carnage. +I don't know what that is, but it's not kitten. +If it's not one thing, it's another. +If it's one thing, it's not another. +"I pity the fool who mistakes me for kitten!", sez Mr. T. +Is that an elephant's head or a winged sandal? +It is a cloud shaped like an ox. +It is a marzipan dreadnought that appears to have melted and stuck. +It is an ancient mariner, and he stoppeth one of three. +It is a set of wind-up chatter teeth. +It is -- I just feel something wonderful is about to happen. +It pleases you to be kind to what appears to be kitten -- but it's not! +It's 1000 secrets the government doesn't want you to know! +It's a banana! Oh, joy! +It's a big block of ice. Something seems to be frozen inside it. +It's a big smoking fish. +It's a black hole. Don't fall in! +It's a blatant plug for Ogg Vorbis, http://www.vorbis.com/ +It's a blind man. When you touch, he exclaims "It's a kitten prospecting robot!" +It's a blob of white goo. +It's a bottle of nail polish remover. +It's a bug. +It's a burrito stand flyer. "Taqueria El Ranchito". +It's a business plan for a new startup, kitten.net. +It's a cardboard box full of 8-tracks. +It's a Cat 5 cable. +It's a catalog from some company called Infocom. +It's a charcoal briquette, smoking away. +It's a clue! +It's a cookie shaped like a kitten. +It's a copy of Knuth with the chapter on kitten-search algorithms torn out. +It's a copy of the robotfindskitten EULA. +It's a copy of "The Rubaiyat of Spike Schudy". +It's a copy of "Zen and The Art of Robot Maintenance". +It's a dark, amphorous blob of matter. +It's a DVD of "Crouching Monkey, Hidden Kitten", region encoded for the moon. +It's a Dvorak keyboard. +It's a fly on the wall. Hi, fly! +It's a free beer cozy (with your paid subscription.) +It's a free Dmitry Sklyarov! +It's a free Jon Johansen! +It's a funky beat! +It's a giant slorr! +It's a gun of some sort. +It's a hologram of a crashed helicopter. +It's a "HOME ALONE 2: Lost in New York" novelty cup. +It's a hundred-dollar bill. +It's a Java applet. +It's a limbo bar! How low can you go? +It's a Linux install CD. +IT'S ALIVE! AH HA HA HA HA! +It's all a dream. +It's a lost wallet. It's owner didn't have pets, so you discard it. +It's a mighty zombie talking about some love and prosperity. +It's a moment of silence. +It's a mousetrap, baited with soap. +It's an altar to the horse god. +It's an autographed copy of "Secondary Colors," by Bob Ross. +It's an autographed copy of "Secondary Colors", by Bob Ross. +It's an automated robot-disdainer. It pretends you're not there. +It's Andrew Plotkin plotting something. +It's a NetBSD install CD. +It's an Internet chain letter about sodium laureth sulfate. +It's an inverted billiard ball! +It's an old Duke Ellington record. +It's an ordinary bust of Beethoven... but why is it painted green? +It's another robot, more advanced in design than you but strangely immobile. +It's a perpetual immobility machine. +It's a piece of cloth used to cover a stage in between performances. +It's a pool with a straw in it. +It's a portable hole. A sign reads: "Closed for the winter". +It's a Quaker Oatmeal tube, converted into a drum. +It's a recursive recursive recursive recursive recursive... +It's a revised business plan for a new startup, my.kitten.net. +It's a rim shot. Ba-da-boom! +It's a roll of industrial-strength copper wire. +It's a rotten old shoe. +It's a segmentation fault. Core dumped, by the way. +It's a Shamrock Shake(tm). At least I hope it is. +It's Asimov's Laws of Robotics. You feel a strange affinity for them. +It's a solitary vaccuum tube. +It's a solitary vacuum tube. +It's a squad of Keystone Kops. +It's a square. +It's a stupid mask, fashioned after a beagle. +It's a symbol. You see in it a model for all symbols everywhere. +It's a synthetic a priori truth! Immanuel would be so pleased! +It's a tape of '70s rock. All original hits! All original artists! +It's a tribute to fishnet stockings. +It's a U.S. president. +It's a wonderful life. +It's a zen simulation, trapped within an ASCII character. +It's Babe Flathead's favorite bat. +It's Bach's Mass in B-minor! +It's Brian Kernigan. +It's "Chicken Soup for the Kitten-seeking Soulless Robot." +It's creepy and it's kooky, mysterious and spooky. It's also somewhat ooky. +It's cute like a kitten, but isn't a kitten. +It's Death. +It's Dennis Ritchie. +It seems to be a copy of "A Tail of Two Kitties". +It's either a mirror, or another soulless kitten-seeking robot. +It's Emporer Shaddam the 4th's planet! +It's "Finding kitten", published by O'Reilly and Associates. +It's Grundle, the Green Dragon. +It's just an object. +It's Kieran Hervold. Damn dyslexia! +It's KITT, the talking car. +It's Lucy Ricardo. "Aaaah, Ricky!", she says. +It's Mary Poppins! +It's nothing but a corrupted floppy. Coaster anyone? +It's nothing but a G-thang, baby. +It's nothing in particular. +It's Princess Leia, the yodel of life. +It's Professor Feedlebom. +It's Rhindle, the Red Dragon. +It's Richard Nixon's nose! +It's Roya Naini. +It's scenery for "Waiting for Godot". +It's Sirhan-Sirhan, looking guilty. +It's some compromising photos of Babar the Elephant. +It's the amazing self-referential thing that's not kitten. +It's the ASCII Floating Head of Seth David Schoen! +It's the astounding meta-object. +It's the author of "Randomness and Mathematical Proof". +It's the Bass-Matic '76! Mmm, that's good bass! +It's the constellation Pisces. +It's the cork to someone's lunch. +It's the crusty exoskeleton of an arthropod! +It's the Donation of Constantine! +It's the embalmed corpse of Vladimir Lenin. +It's the Golden Banana of Discord! +It's the handheld robotfindskitten game, by Tiger. +It's the horizon. Now THAT'S weird. +It's the instruction manual for a previous version of this game. +It's the local draft board. +It's the missing chapter to "A Clockwork Orange". +It's the phrase "and her", written in ancient Greek. +It's the proverbial wet blanket. +It's the Tiki Room. +It's the triangle leg adjacent to an angle divided by the leg opposite it. +It's the Will Rogers Highway. Who was Will Rogers, anyway? +It's this message, nothing more. +It's TV's lovable wisecracking Crow! "Bite me!", he says. +It's Uncle Doctor Hurkamur! +It's "War and Peace" (unabridged, very small print). +It's Yorgle, the Yellow Dragon. +It's your favorite game -- robotfindscatan! +Judith Platt insults librarians. +Just a big brick wall. +Just a box of backscratchers. +Just a broken hard drive containg the archives of Nerth Pork. +Just a cage of white mice. +Just a man selling an albatross. +Just a moldy loaf of bread. +Just a monitor with the blue element burnt out. +Just an autographed copy of the Kama Sutra. +Just a pincushion. +Just some glop of some sort. +Just some old play by a Czech playwright, and you can't read Czech. +Just some stuff. +Just some swamp gas. +Just the empty husk of a locust. +Just Walter Mattheau and Jack Lemmon. +"Kibo was here" +"Kilroy was here" +Kitten is the letter 'Q'. Oh, wait, maybe not. +"Lend us a fiver 'til Thursday", pleas Andy Capp. +Leonard Richardson is here, asking people to lick him. +Long lost needle nose pliers. +Look at that, it's the Crudmobile. +Look, it's Fanny the Irishman! +Look out! Exclamation points! +Lysine, an essential amino acid. Well, maybe not for robots. +"Mail Routing and the Domain System" by Craig Partridge. +Marvin is complaining about the pain in the diodes down his left side. +"Meow meow meow meow..." How discouraging! It's only a recording. +More grist for the mill. +"Move along! Nothing to see here!" +Mr. Hooper is here, surfing. +Mr. Kamikaze and Mr. DNA are here drinking tea. +Ne'er but a potted plant. +Nipples, dimples, knuckles, NICKLES, wrinkles, pimples!! +No kitten here. +"No!" says the bit. +Not kitten, just a packet of Kool-Aid(tm). +Oh boy! Grub! Er, grubs. +ONE HUNDRED THOUSAND CARPET FIBERS!!!!! +One of the few remaining discoes. +One of those stupid "Homes of the Stars" maps. +One person shouts "What do we want?" The crowd answers "Free Dmitry!" +"On this spot in 1962, Henry Winkler was sick." +Ooh, shiny! +Paul Moyer's necktie. +Plenty of nothing. +"Plexar was here" +Preoccupation with finding kitten prevents you from investigating further. +Pumpkin pie spice. +Quidquid Latine dictum sit, kitten non est. +Rene Descarte is whistling a happy tune here. +"Robot may not injure kitten, or, through inaction, ..." +Robot should not be touching that. +Roger Avery, persona un famoso de los Estados Unidos. +Run away! Run away! +Seargent Duffy is here. +Seven 1/4" screws and a piece of plastic. +Several meters of cat5 cable. +Sigmund Freud is here asking about your mother. +Slack! +Snarf? +Some coconut crabs are milling about here. +Someone has written "ad aerarium" on the ground here. +Someone is talking to Ralph on the big white phone here. +Someone's identity disk lies here. +Some sort of electronic handheld game from the 1970s. +Something is written here in the dust. You read: "rJbotfndQkttten". +So, THAT's what an invisible barrier looks like! +Spoon!!! +Stimutacs. +"Sure hope we get some rain soon," says Farmer Joe. +Sutro Tower is visible at some distance through the fog. +Talcum powder. +Tea and/or crumpets. +Ten yards of avocado-green shag carpet. +Thar's Mobius Dick, the convoluted whale. Arrr! +That's just a charred human corpse. +That's just an old tin can. +The boom box cranks out an old Ethel Merman tune. +The Digital Millennium Copyright Act of 1998. +The dirty old tramp bemoans the loss of his harmonica. +The ghost of your dance instructor, his face a paper-white mask of evil. +The Inform Designer's Manual (4th edition) +The intermission from a 1930s silent movie. +The ionosphere seems charged with meaning. +The letters O and R. +The Monolith of Spam towers above you. +The non-kitten item bites! +The non-kitten item like this but with "false" and "true" switched is true. +The non-kitten item like this but with "true" and "false" switched is false. +The object pushes back at you. +There is an opulent throne here. +"There is no kitten!" cackles the old crone. You are shocked by her blasphemy. +There is no tea here. +There's nothing here; it's just an optical illusion. +The rothe hits! The rothe hits! +The score for a Czech composer's "Kitten-Finding Symphony in C". +These aren't ordinary beans. They're magic beans! +The spectre of Sherlock Holmes wills you onwards. +The swampy ground around you seems to stink with disease. +"The Theory and Practice of Oligarchical Collectivism" by Emmanuel Goldstein. +The United States Court of Appeals for the Federal Circuit. +The World's Biggest Motzah Ball! +The world's largest ball of pocket lint. +...thingy??? +This appears to be a rather large stack of trashy romance novels. +This appears to be a statue of Perseus. +This balogna has a first name, it's R-A-N-C-I-D. +This copy of "Steal This Book" has been stolen from a bookstore. +This corroded robot is clutching a mitten. +This grain elevator towers high above you. +This invisible box contains a pantomime horse. +This is a Lagrange point. Don't come too close now. +This is a large brown bear. Oddly enough, it's currently peeing in the woods. +This is an anagram. +This is another fine mess you've gotten us into, Stanley. +This is a porcelain kitten-counter. 0, 0, 0, 0, 0... +This is a remote control. Being a robot, you keep a wide berth. +This is a tasty-looking banana creme pie. +This is a television. On screen you see a robot strangely similar to yourself. +This isn't the item you're looking for. +This is the chapter called "A Map of the Cat?" from Feynman's autobiography. +This is the forest primeval. +This is the tenth key you've found so far. +This is the world-famous Chain of Jockstraps. +This jar of pickles expired in 1957. +This jukebox has nothing but Cliff Richards albums in it. +This kind of looks like kitten, but it's not. +This kit is the fourteenth in a series of kits named with Roman letters. +This looks like Bradley's "Appearance and Reality", but it's really not. +This map is not the territory. +This might be the fountain of youth, but you'll never know. +This non-kitten item no verb. +This nonkitten may contain peanuts. +This object here appears to be Louis Farrakhan's bow tie. +This object is like an analogy. +This particular monstrosity appears to be ENIAC. +This peg-leg is stuck in a knothole! +This place is called Antarctica. There is no kitten here. +This seems to be junk mail addressed to the finder of the Eye of Larn. +This smiling family is happy because they eat LARD. +This subwoofer was blown out in 1974. +This toaster strudel is riddled with bullet holes! +This tomography is like, hella axial, man! +This TRS-80 III is eerily silent. +This was no boating accident! +Three half-pennies and a wooden nickel. +Thunder, Thunder, Thunder, Thunder Cats!!! +Tigerbot Hesh. +"Topsoil's all gone, ma," weeps Lil' Greg. +TV says donuts are high in fat. +'Twas brillig in the slivey-toves... +Two crepes, two crepes in a box. +Vladimir Lenin's casket rests here. +Wait! This isn't the poker chip! You've been tricked! DAMN YOU, MENDEZ! +Werner's "Pocket Field Guide to Things That Are Not Kitten". +We wish you a merry kitten, and a happy New Year! +What in blazes is this? +What's that blue thing doing here? +Why are you touching this when you should be finding kitten? +YEEEEEEEEEEEHAAAAAAAA!!!!! +"Yes!" says the bit. +You can see right through this copy of Brin's "Transparent Society". +You disturb a murder of crows. +You feel strangely unfulfilled. +You find a bright shiny penny. +You find a fraud scheme in which loans are used as security for other loans. +You found Chinchilla! Too bad this isn't "Robot Finds Chinchilla". +You found kitten! No, just kidding. +You found netkit! Way to go, robot! +You found nettik, but that's backwards. +You found Parakeet. Too bad this isn't "Robot Finds Parakeet". +You found Puppy! Too bad this isn't "Robot Finds Puppy". +You found the marble in the oatmeal! +You have found some zinc, but you must not stop here, for you must find kitten. +You have new mail in /var/spool/robot +You hit the non-kitten item. The non-kitten item fails to yowl. +You really don't want to know what this is. +Your pal Floyd is here and wants to play Hucka-Bucka-Beanstalk. +Your State Farm Insurance(tm) representative! +You see a snowflake here, melting slowly. +You stumble upon Bill Gates' stand-up act. +You suddenly yearn for your distant homeland. +You've found Harvey, the Wonder Hamster! +You've found... Oh wait, that's just a cat. +You've found the fabled America Online disk graveyard! +You've found the fish! Not that it does you much good in this game. +You've found the snows of yesteryear! So that's where they all went to. diff --git a/app/examples/Games/RobotFindsKitten/readme.txt b/app/examples/Games/RobotFindsKitten/readme.txt new file mode 100644 index 00000000..67d00522 --- /dev/null +++ b/app/examples/Games/RobotFindsKitten/readme.txt @@ -0,0 +1,24 @@ +gambrfk 1.00 +Copyright 2003 Rob Kudla + +In this game, you are robot (#). Your job is to find kitten. This task +is complicated by the existence of various things which are not +kitten. Robot must touch items to determine if they are kitten or +not. The game ends when robotfindskitten. Alternatively, you may end +the game by hitting the Esc key. + +Please see http://www.robotfindskitten.org for more information. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA diff --git a/app/examples/Games/Snake/.directory b/app/examples/Games/Snake/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Games/Snake/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Games/Snake/.icon.png b/app/examples/Games/Snake/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..401780e5af5caa4c9594f42894a573f8567d3c74 GIT binary patch literal 3584 zcmV+b4*&6qP)vQ&5VcV8S1LNys?yQ|M3?@gtLag-)tK+%gW3J8}$qv z9}rycQq#-8^^Bc%ccCgyU+H)*#&UHbuaat zFD?WE=Pv-6#CAEsJqMjL1$gj5l}dX=68}d6_#z>00}mR-Lo$WfnF2JekhZ)C@x1@WCyMXLhg@(KZfv};BtUK>n$H12GZM_%rXteTgj zy-AK?Ap25J_O}6nv5D78!6$7o*ZQa>8Qb4xPpJp|mbxqjupKWt`Mu|P#&SG~2Q6y~ zVUd{`{-QvssfDlYq~n$nm9W&*6<2CTcE2#NrlmC{Dq(4@;@X0a|F92p5F|BmMvXoBx)YJ)yx;VE$&vV(k z^J&cDr7T*yp1!sQ2D=Y))s5f0BmyXJssL$al9skqLMuI00GYi2p67C4@AJsg6{L?H zB%%t4mE;l(X|(8B+rJQ;;Q^MLuzX6#l{NK)ixB}`E%l5R<{*xZ>1d_B+GBC;TFgiL zQChI$QLJD#N_~D}m}$&>j*eSetK=CTU}on5j-90UWE;NfYA^=h#xE1;KF)Q8K3eBr zj~31G{jX7T_3dQmm41E#OuN5p?IkVIlCVTg`{IRdgbuv@6urmy;~H?*tRpX+kC(Q| zDl8^>qMlFpwNp~PmOU>&O3UC87OeRa>T|r~$g~ESttDYuB8iJ}-+%Fuuk+`Bw1sc{ z{3*8o=YR0i7v901UyBZAQ{sb|IPBi@5?0p%d~?glE(l?1g}#I}hb5A*w51bzS`IKX zbbxqtkbnQThj^f}f@lBWJ8XaEX{r}4_PRQjMIz*gV)cqzR)6_k_76K~2=J z45u3a<{YcieQZBbUz)g=;&4wVcP}ZyE2-j{`Y!H2)Wa{Ie1iH{Uqh;w@r!-!v_z69 z;S_me40dk^E2>s7r)u%%xAYS$J}Ck+(=t)Zr>%ZbM${w$&ejXr-S95e;V2P*5o5t( zibSz$VHpR8l6VFl|HU@GzV%LmH-C%zqaAb~=pf~Rm6d}#Hi~GAR9`z4Nr&1s>(R4F z{;s64B}UqcgA#mh76B)WGM`;jOb!79;?q{Y70~X0h?a?1G$6)Io!y)7KF3$@sNm((E`!hPM`xcUAw zwxu({VEPbN#|Km-S{ba3^46+8EHjsiu{Bf=ZJ;n-b8Z2&h>S0<=OIeF1C|l#(+Z_j zCijU!CWpY@9ox8X<3bvf0fvl^v5rIJu3XPpbcC4gP?(jAr4=GV#dUY^92 zVo@B>{NwuHr_;768|Y!vU4MGP#0?%t60h@d>`}HpeUL476@}NWV2A^}bNw*}3f`xs z;x(>4{ypZ6+;CPKmLTSIuTMFi^eHDh)MqI~D?uwkX%VF|J%RQU4a}QsvFk(^dyjXZ z16erHA>3F5#}{Ph(RQ-ZqlElETsuL3cL)Dmu!xnd?cCGa%s|fw{l;QW+c9px>wYHe zUdT8ad7U@n4{#!PH;r@0c%{9S9Ut!|f8H84hc|Qm$QtsqN7=pX5xNWZpWTj@Q#>t& zIN6~-0eJMSWcNf5z%#~jT(`urQw$F@v81ks=ilB*MyoXozm<&x{Dn(?~oiTXVc-oCpT3##g5O?{erjvu4nkG+kzDqfKN+8 zgb;u-;CVtS6(^J)!1HPt%Bw<#n@A0Gksk3AuDyoQ6Nd;F&tb>$esYsXu)+mI4u6a< zltp;nN&*FQ_>-$Q;tPakMc(rqT82KPJJJX_ASpqTf=VhZ%Oaoy>QiX z;3)mlO<#8-{sA9BVl2+LIOvYhA8w*{coUWvJY`Gtq+-xDlLW9-azYu;MLZ>FpPz7P zCBEETV*7TJvg3#df${~EC5BO!MPJ&(J<*7_sE)k(wV3PxAty$;b~t=!Co5KMn6&^d zNk-$nkOaoS#0+l4Wh`fm$jMIBkB$=PRFoE%lY7%m)YPtK)A4S0)b?U3QfN+lpcXmR z?lU6E(*h_Hmx;Zy%6NcZX-w7}sutB!GYpYr3Q>x_H=m_q?MA|`A*;BO=0ud>oJvx* zO=rqRD`7M@o06Ffc=#!_(0 zx;uEk^)Ra}=*h~%F_}_ROXj3V$H@fw71cBxZNw4H{F;?0ePNA{U?48eoNEXIf!rh4U7$T@%0z33H#?Wd|Fdm zb|dqPuR#z~7^z0kVFDoNH&`R3#ShjTad3!-w{vx)n{xONYCmHT&;Je#}@8?}b*4mY9ZF`R0 zr4Cpi#frsd19z4GHF>(^3_F&}6hawqAb#2d6iFqSgrNlVv@y44DVtZW!JiIMnU{|W zhVarhdHrqN6w++H?e|!d8zyyV561J5@xH%kANIkWTvcAlIzI$@yOFf!aMM2QvFI89 z^I2m>#m#(k{i7@|yOkj;mpA%a`KW#mIqe4N6~c*P>e5^JcInTUo3;F`HbiA|fRYU7 zQy!qSO6c(}kY^D0EPcBCqEc?YWiwTu*7K{54*VrmM8(iQ(8HHD{Vpt7K~2+sK0NXX z{W?f!?qUpv!18sBCdVjHiVaJaQ?XzfZO0CxEj{CYU~TyZitc=z{+eAJXldkwR5z*e zwQR|&VqS3_OG7Zk~MM zDSXSX!-<}x#2+A?wh<9>^NYCU<}IwP+dy;kr|fUsP5VF}w&zh%R6_N}&A)M?zX_r> zAsr^<56|$2qGY@SK9K_i;MK$C2cv0z2#`#r3mwk{W@h<|0ZU0L81M}Pgnj(emZkET z1z^M-OE?;1JV~Tn?HR*mcS1%$iD*wL5fPK)H)AlK1S3NyPdNTue>~-?WFj?QvR#G> zfOa5|70_N}D0*VzssP2m{B`@U1L4Aju2H_rSOBzm>D1`(iEnSLz8M%eB>=D)s0FeB zKX7Rt;r{mE0%Oz<)tD5H}FrNQvy#Mvs z{tk@a{QzD8e+1tGe+Hic{{&;4zrh6jXS{y)DWzV6eF0noFN2ps4NgP*w`2P!1b-Im zzkzM=4>0Py1gDs)cfqJ@YRs$P1=!ce`%B~XRS-Yb#}IXFf|o!BuY;e1QTIbI#`^_~ z{vUzQgTI6E++Sck_b+%0d;`HdAcHaG@ytc^7tmu~qPE!LT~FFen8Z7rw5T^WxNKs? z`FZph7wKyB0L>e}3?en^RrD9pUqO#~R!QH9_w*5$Lg`JqE&CKKRRtds4_TYGYAtNp zIsi2`7V;6BOlVMv5>{4-jm~zhEZb^6dUVsf3I$iIU%o0xacO$vX5sbJ$YLf zw6BYTxY*}IPg_DxN-4L*N#0JH{M{Leo0v#GMPMIe=J37YrIF9hAp?7Yqex?EN5{*J zf7N$(SF(_<=IN$5udV4)qc-W^N$<%>$E9?M1FkXKnVCsD*V5Sz&CYhOy+7TV>2#7c z_90zI%6-AXx-NZeU9M@shqj;(hHW1wA0G0KwQcb?@x2+=Jd|`@xa`tn8`IuuZ~0Wl zOuLgTS`);;|6v6IK@P%I6o@u+lAucS9lzl%Dy#ih$8v$;0IF5SSRCH*F#;l@Dr%Bbz5K|2L&=>o~^CkVc0; z$Zd;hQOQtyibYqklYr7Tnk1k!yq95(J*H5jsDtQ(SmnL=KdUL=V)2kIF=h}{L2sR? zer-a{nLc{hRReLAiYY?Wg2_kov9zvQ!%2fj0K7HWHuq84;!*PhDJ%R3oS=Sl=qjn0 zJbGYs+1z04tDQvOobeV1)cYEQ%802tRe-t=y3pZ11jj1|$s!\n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FrmMain.form:90 +msgid "&About" +msgstr "Qu&ant a..." + +#: FrmMain.form:72 +msgid "&Auto Advance Speed (Challenge ME)" +msgstr "&Increment automàtic de velocitat (Desafia'm)" + +#: FrmMain.form:64 +msgid "&Cool" +msgstr "&Moderat" + +#: FrmMain.class:318 +msgid "Do not Hit the walls, or bite yourself!\n\n" +msgstr "No colpegis les parets, ni et mosseguis a tu mateix!\n\n" + +#: FrmMain.class:317 +msgid "Eat as many apples as you can\n" +msgstr "Mengeu tantes pomes com podeu\n" + +#: FrmMain.form:34 +msgid "&Game" +msgstr "&Joc" + +#: FrmMain.form:60 +msgid "&GrandMa" +msgstr "À&via" + +#: FrmMain.form:78 +msgid "&Help" +msgstr "A&juda" + +#: FrmMain.form:81 +msgid "&How To Play" +msgstr "Com &jugar-hi" + +#: FrmMain.form:37 +msgid "&New" +msgstr "&Nou" + +#: FrmMain.class:245 +msgid "\nPress Game -> New to play again." +msgstr "\nPrem Joc -> Nou per tornar a jugar." + +#: FrmMain.form:42 +msgid "&Pause" +msgstr "&Pausa" + +#: FrmMain.form:51 +msgid "&Quit" +msgstr "&Surt" + +#: FrmMain.class:358 +msgid "Score: " +msgstr "Puntuació:" + +#: FrmMain.class:58 +msgid "Snake" +msgstr "Serp" + +#: .project:1 +msgid "Snake game" +msgstr "Joc de la serp" + +#: FrmMain.form:57 +msgid "&Speed" +msgstr "&Velocitat" + +#: FrmMain.class:359 +msgid "Speed: " +msgstr "Velocitat:" + +#: FrmMain.form:68 +msgid "&Speed Freak" +msgstr "&Velocitat fora de control" + +#: FrmMain.class:316 +msgid "The Rules are simple:\n" +msgstr "Les normes son senzilles:\n" + +#: FrmMain.class:245 +msgid "The snake's soul will always be remembered.\nGame Over!!\nYou scored: " +msgstr "Sempre recordarem l'ànima de la serp.\nJoc acabat!!\nLa teva puntuació: " + +#: FrmMain.class:319 +msgid "Use the arrow keys to move the snake" +msgstr "Utilitzeu les tecles de fletxa per moure la serp" + diff --git a/app/examples/Games/Snake/.lang/cs.mo b/app/examples/Games/Snake/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..fe4b1f47e8738084634ae486ac0b968b7352cb5e GIT binary patch literal 1581 zcmYL|J&YSg6vqchLb4=$gpU-U@D2r&kn9Cg1rwr}^Iq-}f8mcv3Dwy9Vy|~Sv)Zr2 zhiK@K2&n)e3RtB+35kM+q}g*x1vM=V4HBZD;y=DUZsfPWnVmQ9y?yid?re_^Vw10~$Y*%;Y%^ntD%_JPn$2ua~?CK9BV~pba*`m%)!gQ}@@P zDdjuRoO@7m2Q+>^ftSEv!3uZ;$xO>lO($W`z)W9E4Q7~c-5<|kVtTzlPLvabTGnos zCg;;Iv}^CXKy$~YW>eRTuxDW}z)Zcu>O>sJbY0Gv?5ot`ElMD4YXC#%|l%lf1L5Qc`qQ0gM0*eZ= zh)PGMG5kJAJp^CID?#fKOJCGADV@-Umrycc+CrxI0_li`UcxljX%usCcg3=VBNLJ_ z&7?xnVHj|1mhhmFRF>$Cbi`E>JtckKd(zm{xJ-(TKEij?<*|q<_GSoCwU)1AbLNeh zVx0!GH&Dv(;TCHC1I=$XixT+2T=^WJe_Uc>KnpO!bq??DRC*hBhi%^cI z-lfq*;(C(z2K{yW@`0Zz>n=yOqej~G)VNBQahsM=p}Fl0ots3d5#pU$k?-E~l}8(q zgb3TX*}(JV$Sak&oy`}y&WcZfRloQV zS`{18HHI&npzQtRRR~39o*PJ-LXb3NS7|pLO^O_Ht*48eX(C-o!_;L;1C2yVu39&> t;t?XvA\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Snake game" +msgstr "Hadí hra" + +#: FrmMain.class:58 +msgid "Snake" +msgstr "Had" + +#: FrmMain.class:245 +msgid "" +"\n" +"Press Game -> New to play again." +msgstr "" +"\n" +"Pro novou hru -> Zmáčkni Hra." + +#: FrmMain.class:245 +msgid "" +"The snake's soul will always be remembered.\n" +"Game Over!!\n" +"You scored: " +msgstr "" +"DuÅ¡e hada bude uložena.\n" +"Konec hry!!\n" +"Tvoje skore:" + +#: FrmMain.class:316 +msgid "The Rules are simple:\n" +msgstr "Pravidla jsou jednoduchá:\n" + +#: FrmMain.class:317 +msgid "Eat as many apples as you can\n" +msgstr "MužeÅ¡ jíst mnoho jablek\n" + +#: FrmMain.class:318 +msgid "" +"Do not Hit the walls, or bite yourself!\n" +"\n" +msgstr "" +"Nenařážejte do zdí, nebo se nekousejte!\n" +"\n" + +#: FrmMain.class:319 +msgid "Use the arrow keys to move the snake" +msgstr "Použij klávesy Å¡ipek pro pohyb hada" + +#: FrmMain.class:358 +msgid "Score: " +msgstr "Skóre:" + +#: FrmMain.class:359 +msgid "Speed: " +msgstr "Rychlost:" + +#: FrmMain.form:34 +msgid "&Game" +msgstr "&Hra" + +#: FrmMain.form:37 +msgid "&New" +msgstr "&Nová" + +#: FrmMain.form:42 +msgid "&Pause" +msgstr "&Pauza" + +#: FrmMain.form:51 +msgid "&Quit" +msgstr "&Ukončit" + +#: FrmMain.form:57 +msgid "&Speed" +msgstr "&Rychlost" + +#: FrmMain.form:60 +msgid "&GrandMa" +msgstr "&Babička" + +#: FrmMain.form:64 +msgid "&Cool" +msgstr "&Užasný" + +#: FrmMain.form:68 +msgid "&Speed Freak" +msgstr "&Šílená rychlost" + +#: FrmMain.form:72 +msgid "&Auto Advance Speed (Challenge ME)" +msgstr "&Auto zvyÅ¡ování rychlosti (moje výzva)" + +#: FrmMain.form:78 +msgid "&Help" +msgstr "&Nápověda" + +#: FrmMain.form:81 +msgid "&How To Play" +msgstr "&Jak hrát" + +#: FrmMain.form:90 +msgid "&About" +msgstr "&O aplikaci" diff --git a/app/examples/Games/Snake/.lang/de.mo b/app/examples/Games/Snake/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..20309f89e3b869f4de8e01b531128d85d1af3879 GIT binary patch literal 1630 zcma)*yN?@19LEPpAlVQe$4f{EeitG`$n7PeicE-N&U<%O{KCEq3RGk7&+Cb2XRV#t zGlxWjXpj)8paCgGQt}@_=N39@N-9JZ35f#no%NnhP%+Z%XI{U@{^mQsuATT=puL3t z3i>bTuc3eW2tH`19u;C8JPke$UIfp9Z-Xbn{fZxfr!be*{0{ge=5z1__$lb^Kdbh? zsOEP;f9@Ob0(c*M75o`|0{k8HaUOyJ_*b=l@-ZP^#QZGS1kZ!#K?Mrr{uQ`^;avpx zaeoBg0+*oA^N(u%PteEzyPBUtu%|I!1O56r(3j#8=>W48hAt(#Js>Vh!Er;N$z;ee?f%)Skt_`#x%Cs{ve< zHS*Kb=sqsWCC;Gx=I{79bYG$u(0xA7qx*bC(9hXg+L0+!c%8buAeYfx%R`chRAy7u z8l$XmK!J@$d?3x3X*g$&>CLUF)S6Ag)QK*L+E$inQS-j=&83MuQq*=?&+*L`w3ksI zjztYoM6E9ii*xT63eHP^6=*wWITOu{Oy+1;IdW5`1tQrE%5oYh$8?w#xn(_B4+0TM zN77O%4U(SEHCsQ$2^vcih~YTPxlUqeuGP;BhM{h%n#awe-%xTpu^vh_A^ zSh886X`!?xsTcCl(ugVNl+zLC9B&3?{dxzSudfHU5!m{eF?{b^F4LEJjta~erYK{Y z9jum1|DvB~cX;f=Rvg~q+^Wpfso&VT+1QEbRx}v4dflMk3kQ6l{ASpc4nK4|Y7D|o zFKjkww^#49E`HeSMnPLz7w)0>R!f)Vb=ux+c{$k1jAMgvhjZlP_+xho%2DPW z8c!vz$GyA1w;f(RKIhY#a2`fxoW;r{b-Ie%3`Fq3^1)PlSt-^l9Tpo@q)@?F$_R&wPaug>zp@}5e(z~ID-Gh5zE z3+KF_U8N@wV!0)ahH+tVw95sAv@!@q?LB4_vw`iu1K}C}f2FqY)NDov{!eK6`Fz59 z1F~X@3z|t|ENam&%na8H*-kYs2r{h(rmTSYPyD)Q!hXOBYXj|?m{^&K98aNOzcXYX zD@KKm{Ll#hIC4}N=rzIXM^S_FKS^1\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Snake game" +msgstr "Schlangenspiel" + +#: FrmMain.class:58 +msgid "Snake" +msgstr "Schlange" + +#: FrmMain.class:245 +msgid "\nPress Game -> New to play again." +msgstr "\nWähle Spiel -> Neu, um nochmal zu spielen." + +#: FrmMain.class:245 +msgid "The snake's soul will always be remembered.\nGame Over!!\nYou scored: " +msgstr "Die Seele der Schlange bleibt unvergessen.\nSpiel ist aus!!\nDeine Punkte: " + +#: FrmMain.class:316 +msgid "The Rules are simple:\n" +msgstr "Die Regeln sind einfach:\n" + +#: FrmMain.class:317 +msgid "Eat as many apples as you can\n" +msgstr "Iss so viele Äpfel, wie du kannst\n" + +#: FrmMain.class:318 +msgid "Do not Hit the walls, or bite yourself!\n\n" +msgstr "Do not Hit the walls, or bite yourself!\n\n" + +#: FrmMain.class:319 +msgid "Use the arrow keys to move the snake" +msgstr "Die Schlange wird mit den Pfeiltasten bewegt." + +#: FrmMain.class:358 +msgid "Score: " +msgstr "Punkte:" + +#: FrmMain.class:359 +msgid "Speed: " +msgstr "Geschwindigkeit:" + +#: FrmMain.form:34 +msgid "&Game" +msgstr "&Spiel" + +#: FrmMain.form:37 +msgid "&New" +msgstr "&Neues Spiel" + +#: FrmMain.form:42 +msgid "&Pause" +msgstr "-" + +#: FrmMain.form:51 +msgid "&Quit" +msgstr "&Beenden" + +#: FrmMain.form:57 +msgid "&Speed" +msgstr "&Geschwindigkeit" + +#: FrmMain.form:60 +msgid "&GrandMa" +msgstr "&Großmutter" + +#: FrmMain.form:64 +msgid "&Cool" +msgstr "&Mäßig" + +#: FrmMain.form:68 +msgid "&Speed Freak" +msgstr "-" + +#: FrmMain.form:72 +msgid "&Auto Advance Speed (Challenge ME)" +msgstr "&Automatischer Geschwindigkeitsfortschritt" + +#: FrmMain.form:78 +msgid "&Help" +msgstr "&Hilfe" + +#: FrmMain.form:81 +msgid "&How To Play" +msgstr "&Spielanleitung" + +#: FrmMain.form:90 +msgid "&About" +msgstr "&Über" + diff --git a/app/examples/Games/Snake/.project b/app/examples/Games/Snake/.project new file mode 100644 index 00000000..915eafcc --- /dev/null +++ b/app/examples/Games/Snake/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Snake game +Startup=FrmMain +Icon=head.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.sdl.sound +Environment="GB_GUI=gb.qt" +TabSize=2 +Translate=1 +Language=en +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Games/Snake/.src/FrmMain.class b/app/examples/Games/Snake/.src/FrmMain.class new file mode 100644 index 00000000..8c5877e4 --- /dev/null +++ b/app/examples/Games/Snake/.src/FrmMain.class @@ -0,0 +1,430 @@ +' Gambas class file + +'***********Snake Example Game********************************* +'***********By: Ahmad Kamal ****************** +'***********Written for Gambas: gambas.sourceforge.net********* +'***********V1.0: 02-Aug-2003********************************** +'***********V1.1: 16-Apr-2004********************************** +'***********Added SDL sound support**************************** + +Public Head_Direction As String + +Public Xpos As New Integer[] 'Snake's Xpositions, Xpos[0] = head +Public Ypos As New Integer[] 'Snake's Ypositions, Ypos[0] = head +Public Tailpos As New Integer[] 'Array holding the index of the square to be moved. Xpos[ TailPos[0] ] is Xposition of the tail. TailPos keeps rotating +Public AppleXpos As New Integer[] 'Apples Xpos +Public AppleYpos As New Integer[] 'Apples Ypos + +Public Score As Integer +Public Speed As String + +Public hPictHead As Picture +Public hPictBody As Picture +Public hPictApple As Picture + +Public StartSound As Sound +Public EatSound As Sound +Public DeadSound As Sound + +Private $eLast As Float + +Public Sub Form_Open() + + Dim i As Integer + + Randomize + + StartSound = New Sound("start.wav") 'Pre-load all sounds FOR speed issues + EatSound = New Sound("eat.wav") 'Although, one SDL component could handle + DeadSound = New Sound("dead.wav") 'All of them! + + MenuGrandMa_Click() + InitSnake + UpdateScoreBoard + Play("start.wav") + + Me.Text = "Snake v1.0" + hPictHead = Picture.Load("head.png") + ' ME.Icon = hPictHead 'The same as head icon but in a standard size + + Dwg.background = Color.Black 'BM: ?????????? Doesn't work + + Draw.Begin(dwg) + Draw.Foreground = &HAA0000& 'Faded red + Draw.Font.Name = "Serif" + Draw.Font.Size = 120 'Give me real big font + For i = 1 To 10 'Trick to draw 3D text + If i = 10 Then Draw.Foreground = &HFF0000& 'This is the last layer, so make it pure red + Draw.text(("Snake"), (dwg.clientW - Draw.TextWidth("Snake")) / 2 - i, (dwg.Height + Draw.TextHeight("Snake")) / 2 - i - 50) + Next + Draw.Font.Size = 18 + Draw.Text("ver 1.0 by: Ahmad Kamal", (dwg.clientW - Draw.TextWidth("ver 1.0 by: Ahmad Kamal")) / 2, 100) + Draw.End + + hPictHead = Picture.Load("head.png") 'Load images + 'hPictHead.Mask(Color.white) + hPictBody = Picture.Load("body.png") + hPictApple = Picture.Load("apple.png") + + TimerApple_Timer() 'Init apples location array + +End + + +Public Sub Form_KeyPress() +'This function stores the snake direction taken from the keyboard in head_direction + + Dim tmp As Integer + + Select Key.Code + Case Key.Left + If (Head_Direction <> "right") Then Head_Direction = "left" 'Snake isn't supposed to go backwards! + Case Key.Right + If (Head_Direction <> "left") Then Head_Direction = "right" + Case Key.Up + If (Head_Direction <> "down") Then Head_Direction = "up" + Case Key.Down + If (Head_Direction <> "up") Then Head_Direction = "down" + End Select + +End + +Public Sub TmrEngine_Timer() +'This is the main game engine. With every tick, the game status get updated. + + Dim i As Integer + Dim hPictHeadRotated As New Picture + + 'Here we will move the snake. Instead of moving each & every square of the snake + 'We will use an optimized method. Only the head moves one step forward & the + 'Tail moves to where the head previously was. That way, all the snake seems to be moving! + +' If $eLast Then Debug TmrEngine.Delay;; Format(Timer - $eLast, "0.###") + $eLast = Timer + + Xpos[TailPos[0]] = Xpos[0] 'Tail goes to head + Ypos[TailPos[0]] = Ypos[0] + TailPos.add(TailPos.pop(), 0) 'Array is rotated => Last element is put as first element, i.e. new tail is calculated + + Select Head_Direction 'See the current head direction and start moving + Case "left" + Xpos[0] = Xpos[0] - 20 + hPictHeadRotated = hPictHead.Image.Rotate(Pi).Picture 'Snake looks to where it's going :) + Case "right" + Xpos[0] = Xpos[0] + 20 + hPictHeadRotated = hPictHead + Case "up" + Ypos[0] = Ypos[0] - 20 + hPictHeadRotated = hPictHead.Image.Rotate(Pi / 2).Picture + Case "down" + Ypos[0] = Ypos[0] + 20 + hPictHeadRotated = hPictHead.Image.Rotate(- Pi / 2).Picture + End Select + + + 'Begin drawing + Dwg.Clear 'Clear old game drawings + Draw.Begin(Dwg) + + For i = 1 To Xpos.count - 1 'Draw snake + Draw.Picture(hPictBody, Xpos[i], Ypos[i]) + Next + + For i = 0 To 2 'Draw apples + Draw.Picture(hPictApple, AppleXpos[i], AppleYpos[i]) + Next + + Draw.Picture(hPictHeadRotated, Xpos[0], Ypos[0]) 'BM: Why doesn't the head appear on top? put hPictHead, and it will !!! + + Draw.End + + If (SnakeHasEatenApple()) Then + Play("eat") + MakeSnakeLonger '4th Generation programming :) + Inc Score + End If + + UpdateSpeed 'Should snake go faster? + UpdateScoreBoard 'Write score & speed on screen + + If (SnakeHitTheWall()) Then SnakeDies 'G4 again ;) + + If (SnakeBitHimself()) Then SnakeDies 'So cool + + If (SnakeAteAllApples()) Then 'If you've eaten the 3 apples, throw some more apples + TimerApple_Timer() 'Call the timer manually + TimerApple.Enabled = False 'Timer is disabled and re-enabled to reset it's internal timer + TimerApple.Enabled = True + End If + +End + +Public Sub TimerApple_Timer() + + Dim I, J As Integer 'Throw 3 random apples all over the screen + Dim X, Y As Integer + + AppleXpos.Clear + AppleYpos.Clear + + For I = 0 To 2 + + ' Do not add an apple on the snake + Do + X = Int(Rnd(1, 30)) * 20 + Y = Int(Rnd(1, 20)) * 20 + + For J = 0 To Xpos.Max + If X = Xpos[J] And Y = Ypos[J] Then Break + Next + + If J = Xpos.Count Then Break + Loop + + AppleXpos.Add(X) + AppleYpos.Add(Y) + + Next + +End + +Public Sub MakeSnakeLonger() + + Xpos.add(Xpos[Tailpos[0]]) 'Add a new square to where the tail square is, to make the snake longer + Ypos.add(Ypos[Tailpos[0]]) + + TailPos.add(TailPos[0]) 'Remember when we said, tailpos keeps rotating. Now we have put an extra + TailPos.remove(0) 'Square, to make it move instead of the real tail, we make a back rotation (in + TailPos.add(Xpos.Length - 1, 0) 'opposite direction). First element => Last position, then index of new tail + 'gets added at location 0. Remember, TailPos[0] is the index of the tail!!! + +End + +Public Function SnakeHasEatenApple() As Boolean + + Dim RetVal As Boolean + Dim TmpAppleX As Integer + Dim TmpAppleY As Integer + Dim i As Integer + + RetVal = False + + For i = 0 To 2 + + TmpAppleX = AppleXpos[i] + TmpAppleY = AppleYpos[i] + If (Xpos[0] = (TmpAppleX) And Ypos[0] = (TmpAppleY)) Then 'Snake head on an apple? + AppleXpos[i] = -100 'Hide eaten apples + AppleYpos[i] = -100 + RetVal = True + End If + + Next + + + Return RetVal + +End + + +Public Function SnakeHitTheWall() As Boolean + + If (Xpos[0] < 0 Or Ypos[0] < 0 Or Xpos[0] >= 600 Or Ypos[0] >= 400) Then 'Duh + Return True + Else + Return False + End If + +End + +Public Sub SnakeDies() + + TmrEngine.Enabled = False + TimerApple.Enabled = False + Play("dead") + Message(("The snake's soul will always be remembered.\nGame Over!!\nYou scored: ") & score & ("\nPress Game -> New to play again.")) + +End + + +Public Sub MenuNew_Click() + + Dim i As Integer + + Head_Direction = "" 'Reset everything + Score = 0 + MenuGrandMa_Click() 'Reset speed + + InitSnake + TimerApple_Timer + + TmrEngine.Enabled = True + TimerApple.Enabled = True + +End + +Public Sub MenuPause_Click() + + If (MenuPause.Caption = "&Pause" And (TmrEngine.Enabled = True)) Then + TmrEngine.Enabled = False + TimerApple.Enabled = False + MenuPause.Caption = "Un&Pause" + Else If (MenuPause.Caption = "Un&Pause" And (TmrEngine.Enabled = False)) Then + TmrEngine.Enabled = True + TimerApple.Enabled = True + MenuPause.Caption = "&Pause" + End If + +End + +Public Sub MenuQuit_Click() + + FrmMain.Close + +End + +Public Sub MenuGrandMa_Click() + + TmrEngine.delay = 200 + TimerApple.delay = 8000 + Speed = "GrandMa" + +End + + +Public Sub MenuCool_Click() + + TmrEngine.delay = 150 + TimerApple.delay = 5000 + Speed = "Cool" + +End + + +Public Sub MenuSpeedFreak_Click() + + TmrEngine.delay = 100 + TimerApple.delay = 2500 + Speed = "Speed Freak" + +End + +Public Sub MenuHowToPlay_Click() + + Dim Help As String + + Help = ("The Rules are simple:\n") + Help = Help & ("Eat as many apples as you can\n") + Help = Help & ("Do not Hit the walls, or bite yourself!\n\n") + Help = Help & ("Use the arrow keys to move the snake") + + Message(Help) + +End + + +Public Sub InitSnake() + + Dim i As Integer + + Xpos.Clear() + Ypos.Clear() + TailPos.Clear() + + For i = 0 To 3 + Xpos.add(0, i) + Ypos.add(0, i) + Next 'BM: Here i exists with value 4, shouldn't it be 3?! + + TailPos.add(3, 0) + TailPos.add(2, 1) + TailPos.add(1, 2) + +End + +Public Function SnakeAteAllApples() As Boolean + + 'IF ( AppleXpos[0] = AppleXpos[1] = AppleXpos[2] = -100) THEN 'BM: Doesn't work + If ((AppleXpos[0] = -100) And (AppleXpos[1] = -100) And (AppleXpos[2] = -100)) Then + Return True + Else + Return False + End If + +End + +Public Sub UpdateScoreBoard() + + LblScore.Text = ("Score: ") & Score + LblSpeed.Text = ("Speed: ") & Speed + +End + +Public Sub UpdateSpeed() + + If MenuAutoAdvanceSpeed.checked Then + If (Score > 19 And Speed = "GrandMa") Then MenuCool_Click + If (Score > 79 And Speed = "Cool") Then MenuSpeedFreak_Click 'Let's kill ur snake :) + End If + +End + +Public Sub MenuAutoAdvanceSpeed_Click() + + MenuAutoAdvanceSpeed.Checked = Not MenuAutoAdvanceSpeed.Checked 'Toggle menu checking + +End + +Public Function SnakeBitHimself() As Boolean + + Dim RetVal As Boolean + Dim TmpAppleX As Integer + Dim TmpAppleY As Integer + Dim i As Integer + + RetVal = False + + For i = 1 To (Xpos.Length - 1) + + TmpAppleX = Xpos[i] + TmpAppleY = Ypos[i] + If (Xpos[0] = (TmpAppleX) And Ypos[0] = (TmpAppleY) And Xpos[0] <> 0) Then 'Snake head on part of its body? + RetVal = True + End If + + Next + + Return RetVal + +End + +Public Sub MenuAbout_Click() + + Dim About As String + + About = "Snake v1.0\nBy: Ahmad Kamal \nWritten for Gambas http://gambas.sf.net" + Message(About) + +End + +Public Sub Play(sound As String) +'I know it's slow, but I had to give it sound!!! +'Now I can safely move on to writing Quake17 ;) + +' EXEC [ "playwave", "/tmp/" & sound & ".wav" ] + Select Case sound + Case "start" + startsound.Play() + Case "eat" + eatsound.Play() + Case "dead" + deadsound.Play() + End Select +End + + +Public Sub dwg_KeyPress() + + Form_KeyPress + +End diff --git a/app/examples/Games/Snake/.src/FrmMain.form b/app/examples/Games/Snake/.src/FrmMain.form new file mode 100644 index 00000000..c506eab1 --- /dev/null +++ b/app/examples/Games/Snake/.src/FrmMain.form @@ -0,0 +1,89 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(34.3333,27.3333,105.3333,81.6667) + Font = Font["Bold"] + Background = &H000000& + Foreground = &HFF0000& + Text = ("") + Icon = Picture["head.png"] + Resizable = False + { MenuGame Menu + Text = ("&Game") + { MenuNew Menu + Text = ("&New") + Shortcut = "Ctrl+N" + } + { MenuPause Menu + Text = ("&Pause") + Shortcut = "F3" + } + { MenuSep Menu + Text = ("") + } + { MenuQuit Menu + Text = ("&Quit") + Shortcut = "Ctrl+Q" + } + } + { MenuSpeed Menu + Text = ("&Speed") + { MenuGrandMa Menu + Text = ("&GrandMa") + } + { MenuCool Menu + Text = ("&Cool") + } + { MenuSpeedFreak Menu + Text = ("&Speed Freak") + } + { MenuAutoAdvanceSpeed Menu + Text = ("&Auto Advance Speed (Challenge ME)") + Checked = True + } + } + { MenuHelp Menu + Text = ("&Help") + { MenuHowToPlay Menu + Text = ("&How To Play") + Shortcut = "F1" + } + { Menu1 Menu + Text = ("") + } + { MenuAbout Menu + Text = ("&About") + } + } + { LblScore Label + MoveScaled(3,1,49.3333,6.6667) + Font = Font["16"] + Background = &H000000& + Foreground = &HFF0000& + Text = ("") + } + { LblSpeed Label + MoveScaled(54,1,49.3333,6.6667) + Font = Font["16"] + Background = &H000000& + Foreground = &HFF0000& + Text = ("") + } + { dwg DrawingArea + MoveScaled(3,8,100,66.6667) + Font = Font["14"] + Mouse = Mouse.Blank + Cached = True + Focus = True + } + { TmrEngine #Timer + #X = 0 + #Y = 432 + Delay = 100 + } + { TimerApple #Timer + #X = 592 + #Y = 432 + Delay = 10000 + } +} diff --git a/app/examples/Games/Snake/apple.png b/app/examples/Games/Snake/apple.png new file mode 100644 index 0000000000000000000000000000000000000000..cef23a2c1546d91cc63572321f1533d24469d25f GIT binary patch literal 1141 zcmV-*1d98KP)W@%!HPTf4Ck8T+tRM`bCn!V- z6nZYRpwd5jqES?W5(Xj>N-~?y&`sUF+r8U;_&t74e_T`i!=Ur`IWIVe563hjym$N{ zjwhyWx_ehstGaVavzm18qS{=#c}+C**Gc|Qsji+xZU1?Hr5nf^uf$AIm?%V73Jg!I z*RQXBxZ&`t^0MtdwwU|AJxCX?{5SYOr&(F@qi&^0+lczWCsnB+p^p=WxS^{)Tyoaw zoiI^HgT>%V(6J)F^XxWx@goO{3pui4!j0e~Yim=5u(TzDE~{{CNILQnu}~@y3(G`z zpM_hJT)CK|@pc$TaXPQ`k?f@8!P&dkUHTxkRz^D?tbzdh-n~a3`r*W^Vp*CpS=y#w zvd%YVS}NP5Nb@3^c*m82+qU!X|4CfGqd&_-v2_BUA6%O@ZC3qvSQ1V zxy2t3$ijYqZ)=|BnIqI@#z<-xJshE}!DL$SOdQqb5rq-41k@t3C|?@!<~1((ZRf8) zRzZLTK~3V0Qv-)4i_DFI3PCF{Nyus;DMGAVB;MMH9?z0Y2wnwr1;i6+A31U4=$Cu% zzkCf`eg5*QjI~>1pcGoibzH}lR*tCDG!(5kL9=hmPL3fXcmc!$NE9F{gSe4QtIHR@ zG60XySgU5`PQKQFYSUZ#CZVW|KhO2SAo>z|K|;U@KrBcEIsj8vGDibA zpB$};!9Z_;oLp!C}(3W~jTh1xLq}sm8(z5P^yy=7G{+LQrK<^$x(~w)!AC zI~)oag+j!t;S0Q~Cl zl?$o})&(;L+6C)r_4&4?HoOLQtlFCIi^n%zgz-_V9m7fqOR0*fGAO@_i3>6cQUsHS zs00IUZS8^m-_PJ0xNXl%GXKqvqy6z%*Eu9t02u*Bs##NIP-8$AxCC~ry0Sk8m)*GO z{-Wi%v19aI2LZPC{45PU2YRoh5-Tq%Ih6xD3ThO{0i#t=^1vXtS@_#^RM(>qJ@fnG zMLTZ(BzGM6R6W+zRJTs+?HgcdGXxvK>w;i8c(Wnb4DU}(#;0%W+zlev|KZKhO;0~d zygRM_SoTKR^W6^LN_!GgIT-X3=BFtuJJ0QFfhBK!cEj910HYLlJcP&F00000NkvXX Hu0mjfGj0~# literal 0 HcmV?d00001 diff --git a/app/examples/Games/Snake/body.png b/app/examples/Games/Snake/body.png new file mode 100644 index 0000000000000000000000000000000000000000..1baff801f471c73643bd9d2194766d5d84cff58c GIT binary patch literal 1138 zcmX|93s96*6#nl2FT2Y=1QCM)gnS^5q96<^ln}T(f+;Nsp@PO(L$S2vA`LHbcquSL zMkoo&64_vyqpmumi<%ngG=*cEC=W@%p&>vKBU&Ejp8j2{nRCB;&Ufa{Irp5eQWLvg zDf5s4P{u^7;)UDQcBH(3my<>#0PeVEV}e=;(Fz;{(t$MKJ}?Maz^sfB0|7?hN61O; zKn@&nCdH@UO+G*`_ewJH~F&9|4E_5BgRkglD{QNQaX_nT7bI{jfj*pO0O4Q0Td8U z;b9cEl$PQv;2QW2UN$E4dF~Tt4NIYvLMkkxB0cFov7qA2VKn!rp<`ipeV$eiHG;N%y{2k5{}x;a1t9n@hWla{o8@F6CxY zriL`WG@7 zE*X+=0~~~vWdmh4fw5zZJInD`c+o8p-^TN1o|zeL14Tds@HLo`v5O3sflQzpr~?{- z9GeENcqa>gMPzn->}H!{v8J%Dde^+9PHRh$sBtE(&blRj{MGiu!B6RYb?FtZ2$j$lmy2 z%ATm5@mU+RN6HpOSE-k-T^PLVHNVvX*PDzdOe&8>OEzzNdq>>X#O<4tllshE_xzVh zTn~MeuFDu3dNT5Kc+>FXF9P*7oJOM`KXkJF_|J8r8J32YKL-Q0n4%q*oOnI&kDBJT z&(CgY(f)O$JEQY-UlZq=V7RJpA6%Y%N!NF7cjwc!1(TymyzHB|8h_2|@Bi|2o0Csy z{*$T7gC40l+StZDBNO!(e!ugMDR+3h_SCl2NZq{Z2N`dk?K9;)w&=F6tvEd4$KUTw z-*mlTZ14W!`TnY^s_$Bzyfas=RF@g*TO2gG=bLLoHZ1AsdeGgrroU1whI(60@965| Pf=GZEb*$=4RBFjT&knM( literal 0 HcmV?d00001 diff --git a/app/examples/Games/Snake/dead.wav b/app/examples/Games/Snake/dead.wav new file mode 100644 index 0000000000000000000000000000000000000000..f8d4292d0fb411872b8676e3ed8fd1938e03d40e GIT binary patch literal 10026 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 L|42+7neY4`JMe+U literal 0 HcmV?d00001 diff --git a/app/examples/Games/Snake/eat.wav b/app/examples/Games/Snake/eat.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc8ff1e38449462023247971bd26d878c3060d6c GIT binary patch literal 1192 zcmeHG&uM5-wza#KGb~%o34a5}&PX%wy^+3e=6&B> zoyue$$N&oy^XcMO>&yZGEM{aLV0b|!vJ4D5Qa=ECf8>f4wjcqv*#AU^`#t)^E#_~n z3lR7P0~GS>c@PQ4=O7M0xFvK6-GOYuvpMj)cO|~@&5LgC=_@_5+G%vNr);%yCAR$^ zi#R(zmVDkA!4Ss!&nJ%ISxY4-H|X@5J^=fSWS55b72|nL<>TMIT$G9CsK9prQFEQg z1ET?3)f|QVdJr;q8~08F4`~4WN#ll~NW@yb=BMpY^~aA{v_4>AF#h_jq0s8}KCUbU z_m4v1de2pjwKuhqlw9cwHdWImI;w`Y4nz3Sfu4-kkM3TU_Fn0j_ohcBuFQ-{_fHVS z6=G7yC1sDj<7L#@yD~Z2Rs_K2fnrE5Ssg}p2=UrOZv;DPJ}y;!VDB9E5@~UXg2z?? z@rGhrDVeSH^B$0DL%(&w59DT_&5v=K)7N5cm4xfvN=*-2e|#H{Viw(f_}1uvy*OsI zpInOJjgNpJf^B^+u;pbD@p4!>h1_wOnA-Npnhq=^{S9MTdYG3Wdw z;z!I+rTl9Zk+psVxjurxOpUhBte$pMquA~lgQD3mAp>WTZIuNpH_{R$Cx;wC#n4ax zF7*q4ATR>^dy9j0#Y_dQ`c60ZW>XG@O()zrWfeRYkMp znPNbr2uQfi6B%4t9215T8c~2`8b3M=MKMX;nfD`L(N@hDrj$l;IC&N^%Js?FvzVB- z)5i|}e|)G8d^R;Zml0r7+067*>guFd#OB3|7tR~jc_S|B>Fj0kuOa#6@-kZ}Ef-7W af~d}B=7^}ymTrGtV3|_+i_ckX#DI5=O%5^u literal 0 HcmV?d00001 diff --git a/app/examples/Games/Snake/head.png b/app/examples/Games/Snake/head.png new file mode 100644 index 0000000000000000000000000000000000000000..c6576d3fc8568e7ca518403fbbf437409b797b78 GIT binary patch literal 1197 zcmYjR3ozAL82_FB|2}f>&DG7svU@&HjcxN-kDFaAxo7XS)6C9%=XbvIeRIBZ&hPx@OY_?1K3iZU z05IFr!_`|e>ubj>R)cr_2Au(>*=x&AH_Z}f0Sbfzp+Ggz3}GiQ1~N?uCE8RGtiJ}2 z@)*h>5x4*em2fE+a%eFU`c$8#EF!?Cd`%G%6=^qy;y<)((E29LGxd9BumfnFacX8z zbM77B=NUg2`2Iss6Hl74P#ABZ3k-nFv z(X4+44~*A-3i*NX%6Ymix@K?9Tqax&s6Wr@lvS6=pInQ5P#ANeBEY@RT1>?_2t)&^ zAi0DsW_2Fw7<3!hJ}`}4A}A!JT__y4x2JMB&CjP%QI)$4w4`N#xz2EZ<%%AQ7FW*o zwdEgSkW5G}hock-YQ^GOi7->j(Xt%_l0}$&hNtAseK~i*OCzfX#x_brtMYkEo=8M0 z9cGw8EN~LY26Kzy{KDba@P*AnahF*4jjpI)#INGBZ&d4qBK;#@|QwO?z^dfruaLf+Bs6Xs8E zt{xK|-H;lR5)|&ROvQhgQs+~*zh*`a_Fn?lo-9FKH2n-z{r?W`*DiVBoBN)&;DCFVgp{C5TU z?Dp$wY-Qv2=`IIrRS2w>c~Kl%5#8JFiXk!l5pESEus$ zz}V%o@ge8N*5qTJiS5Ow&&+o|KB>6hQ<}7B&4%>!t-<$Xe^1tzBu8zn*V}vW$lia3 rrca0aKC?By8X0kLxV!h{QX@lDElMy8>071gKY*v(HrJmvhRFW~B!a^H literal 0 HcmV?d00001 diff --git a/app/examples/Games/Snake/start.wav b/app/examples/Games/Snake/start.wav new file mode 100644 index 0000000000000000000000000000000000000000..a23faacb29c03708040e51ad597a5ac191f5c0da GIT binary patch literal 31810 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 LFFLto!{+}FrKSsa literal 0 HcmV?d00001 diff --git a/app/examples/Games/Solitaire/.directory b/app/examples/Games/Solitaire/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Games/Solitaire/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Games/Solitaire/.icon.png b/app/examples/Games/Solitaire/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fa870190609f8e8604fbe60579cb818f75902353 GIT binary patch literal 4209 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 Hu0mjfc$N3r literal 0 HcmV?d00001 diff --git a/app/examples/Games/Solitaire/.lang/ca.mo b/app/examples/Games/Solitaire/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..28fd6b8ab8f0c5e5a9aa754ad128e12ac554257b GIT binary patch literal 1343 zcmb7?&yN&E6vs*us_VV?t?KvU{E6=r>;lFD z#?KfZVmx{U6YM$o2KWp3I{4d+FJ}A$d=u+`&iuc?x8VN`o&it2+Qz>Jo`Sys;!z)A zIssmqF$GV;?}HMTgOYax9D`fn2jEYj#Qh2`qQ>u_9P~t~3IuMU~f=Tjx1)c@J0ndZqf|B=p@BsV~l>2)hMI_G^5c1SH z4DpXmG>AcVlUMFa>}?FuB{jrOV@Ms*_8Nv*f+3paeo$_1qC@^)W1SoOu8yj+ZYoxt z4L!m9IagEF`GTKN>u=QtJ9fC@BGOH#eW{w6?&u>4UeUJTO3efA-BwM=HkVFqbBNkU zH4EQw!gpL{qBfq@{EZ%G-OI_%qntLp9zzE`7@NrS*{F4|dmecXp*waHu8ge@yUvfU zB@eubku43K7rFa7&^X3vzPLDQylxnFEu%idC^#R<4Z&G`!X{RG7D5N!9pHfJtunpO zJ{V_nnyX6&_tI^CY-BlI)sb`RXT4sUT~7NM_51m!*(X^q%aS`f#B|qd8!8>0&uP>7 z($EfWs=`^iL0+yO>9KQN4%e{Xj%q#PbeHucr@4>UySMJHy_C}HX32`PI4bfVO(7ui zVO*S6+Sny3#@Yvt%X_=)>D8C}M8}YQx@HSk8avAA>b{A|psKyD(sk!2AtyU++_2nV zqV{#!(#2)!EhP`rf#cG|^jKz2D>~{*+eCG?YHcHF>797UUTt<Jn?4t|04b zv`vkl-!@Ny%j24&t}ASp>N--%)$vNxN6)^lv^tJn{a6>cKh-4J=Hb&Hg2Js@O`41~ zh0(rktbeC&f15Y6?((K>t^wnJ{J-v63kCX%rtC>q|Jh%A5H(>;CDQ>8FXw983_4ah HG}`|GybVDL literal 0 HcmV?d00001 diff --git a/app/examples/Games/Solitaire/.lang/ca.po b/app/examples/Games/Solitaire/.lang/ca.po new file mode 100644 index 00000000..c9e2e690 --- /dev/null +++ b/app/examples/Games/Solitaire/.lang/ca.po @@ -0,0 +1,96 @@ +# Catalan translation of Solitaire +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the Solitaire package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Solitaire\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 22:40+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FGameArea.form:48 +msgid "&About" +msgstr "&Quant a" + +#: FBoardSelect.form:21 +msgid "Board Layout" +msgstr "Format del tauler" + +#: FBoardSelect.form:31 +msgid "Cancel" +msgstr "Canceŀla" + +#: FGameArea.form:25 +msgid "&File" +msgstr "&Fitxer" + +#: FGameArea.form:37 +msgid "&Game" +msgstr "J&oc" + +#: FGameArea.form:45 +msgid "&Help" +msgstr "&Ajuda" + +#: FGameArea.form:28 +msgid "&New Game" +msgstr "&Joc nou" + +#: FGameArea.form:57 +msgid "New Game" +msgstr "Joc nou" + +#: FBoardSelect.form:26 +msgid "OK" +msgstr "D'acord" + +#: FGameArea.form:87 +msgid "Quit" +msgstr "Surt" + +#: FGameArea.form:32 +msgid "&Quit" +msgstr "&Surt" + +#: FGameArea.form:80 +msgid "Redo" +msgstr "Refés" + +#: FGameArea.form:65 +msgid "Restart" +msgstr "torna a iniciar" + +#: FBoardSelect.form:11 +msgid "Select board" +msgstr "Seleccioneu tauler" + +#: FGameArea.form:40 +msgid "&Select Board" +msgstr "&Seleccioneu tauler" + +#: FGameArea.form:20 +msgid "Solitaire" +msgstr "Solitari" + +#: FGameArea.class:259 +msgid "Solitaire v0.3\nBy: Grahame White \nWritten for Gambas http://gambas.sf.net" +msgstr "Solitari v0.3\nPer: Grahame White \nEscrit per al Gambas http://gambas.sf.net" + +#: .project:1 +msgid "Some sort of solitaire game" +msgstr "Una mena de joc del solitari" + +#: FGameArea.form:72 +msgid "Undo" +msgstr "Desfés" + diff --git a/app/examples/Games/Solitaire/.lang/cs.mo b/app/examples/Games/Solitaire/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..d1e4468cdc6be04137bc3c9046dea294e2284ea4 GIT binary patch literal 1258 zcmb7?J8u&~5XTn?5KMRlNJEg80t*S9L!b#qh;m6T#=?)pj*W;YSlb)>lFRLBZyjR3 z10@AYm6C!lfPltHM*$5&0;PchQBgoc#eeK0K@>=gJp0?(o!6dcXRz-vgPeg5LtjA8 zL+|aw0(k=-0^foM!S^wDVtxjX;QVWBe+Q4j{s|6%{rh|IXTg5h!yta_B9=bzQcMdx z4EribaW+VK(_j;v2hV}eL5h0^GSv709>wMbp8WOkSh);-vCJV5O@N- z08-u2`2ISGv}_ureqE5>osZ8KK+02z?fakyy9y40uR;1=pW^$I*i-)BLb*t&ee$Dk zMRFW^8cIEpQ163Ks)Z+Xh59}PrSF6EyFz`^|JRkIQ&la)lCz#KSu!nFvEGt?$dY;a zfcNbAmIsfL^kvQPDJ68BbyuDhZ3>H=uBvbwi4>u2@k3czIJ*9MGB5_B1#>jv# zDNXHGMZ}xNg!aftqidz2rBonMA7MOF+Hkc5#`QUzBr6AKbWW>fJkZM2t%}r%H-} zkIfUgOwQfwZ!~o!F|7i;ALZI1`Z03djD)`Mf*ZWn6graTW_e-O8r$=uww9!}+@PlF zUeK`lSk*HLmMm})`reADdHAVU3ibaTh`b0R5p-Vi bP^*7VM1E^yS*&!P{xJ>IaV6Y-BJ1oIC&wu` literal 0 HcmV?d00001 diff --git a/app/examples/Games/Solitaire/.lang/cs.po b/app/examples/Games/Solitaire/.lang/cs.po new file mode 100644 index 00000000..24468c0b --- /dev/null +++ b/app/examples/Games/Solitaire/.lang/cs.po @@ -0,0 +1,94 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Some sort of solitaire game" +msgstr "Nějaké solitaire hry" + +#: FBoardSelect.form:11 +msgid "Select board" +msgstr "Vyber hru" + +#: FBoardSelect.form:21 +msgid "Board Layout" +msgstr "Rozložení hry" + +#: FBoardSelect.form:26 +msgid "OK" +msgstr "-" + +#: FBoardSelect.form:31 +msgid "Cancel" +msgstr "ZruÅ¡it" + +#: FGameArea.class:259 +msgid "" +"Solitaire v0.3\n" +"By: Grahame White \n" +"Written for Gambas http://gambas.sf.net" +msgstr "" +"Solitaire v0.3\n" +"Od: Grahame White \n" +"Napsaný pro Gambas http://gambas.sf.net" + +#: FGameArea.form:20 +msgid "Solitaire" +msgstr "-" + +#: FGameArea.form:25 +msgid "&File" +msgstr "&Soubor" + +#: FGameArea.form:28 +msgid "&New Game" +msgstr "&Nová hra" + +#: FGameArea.form:32 +msgid "&Quit" +msgstr "&Ukončit" + +#: FGameArea.form:37 +msgid "&Game" +msgstr "&Hra" + +#: FGameArea.form:40 +msgid "&Select Board" +msgstr "&Vyber hru" + +#: FGameArea.form:45 +msgid "&Help" +msgstr "&Nápověda" + +#: FGameArea.form:48 +msgid "&About" +msgstr "&O aplikaci" + +#: FGameArea.form:57 +msgid "New Game" +msgstr "Nová hra" + +#: FGameArea.form:65 +msgid "Restart" +msgstr "-" + +#: FGameArea.form:72 +msgid "Undo" +msgstr "Vpřed" + +#: FGameArea.form:80 +msgid "Redo" +msgstr "Zpět" + +#: FGameArea.form:87 +msgid "Quit" +msgstr "Ukončit" diff --git a/app/examples/Games/Solitaire/.lang/de.mo b/app/examples/Games/Solitaire/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..8a0b0187e26143559bbe178bdb21f0cabfdee2ac GIT binary patch literal 1301 zcmb7?&u<$=6vu~>w!zRJ1j2XJHAm7yi$bP?p2K*iT0(=SP=f-96caG&eXTo>L z!tHb4d{-s*;=AhgoTF<3~^;0P4N11csb8%0(l!IP6cA~tY6B6Z`I>fq3 z$w-u2^iWRi?XiYOmlEnbxt1zB5cw(_>X?JqmFZI=&I8)_MIO?jK`J_wIc1$F!h3mm zH%+ut+LAc`Cyy2@-6y`4I16D>907TS08wLWHX%_ zUv35K_k&s}_rgxM-foq)+Fpl_G=KD}%8@T0gh9t^w!LZ~H`{)*{>@MAR#g6E8{X1|AGb9m>xHJD z4z!7Uxzy9HB+8S=J+h+A2h(`(k;bEzH+3?^OtffBnJo6!h|GXY;WMOUAk{c~I5~+E z?B-6qDS}?lQa{Gt<#11&#qVnxkd1NMjuH-qo6N`Ma+=Gzy>|y@kDuxPO-zllerz@M lcxoo6?;o2`8s`EZqb>X_79@TiHUS1-U0wOHrW6G literal 0 HcmV?d00001 diff --git a/app/examples/Games/Solitaire/.lang/de.po b/app/examples/Games/Solitaire/.lang/de.po new file mode 100644 index 00000000..1ccd7676 --- /dev/null +++ b/app/examples/Games/Solitaire/.lang/de.po @@ -0,0 +1,89 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Some sort of solitaire game" +msgstr "Eine Solitär-Variante" + +#: FBoardSelect.form:11 +msgid "Select board" +msgstr "Spielfeld auswählen" + +#: FBoardSelect.form:21 +msgid "Board Layout" +msgstr "Spielfeld" + +#: FBoardSelect.form:26 +msgid "OK" +msgstr "-" + +#: FBoardSelect.form:31 +msgid "Cancel" +msgstr "Abbrechen" + +#: FGameArea.class:259 +msgid "Solitaire v0.3\nBy: Grahame White \nWritten for Gambas http://gambas.sf.net" +msgstr "Solitaire v0.3\nVon: Grahame White \nGeschrieben für Gambas http://gambas.sf.net" + +#: FGameArea.form:20 +msgid "Solitaire" +msgstr "-" + +#: FGameArea.form:25 +msgid "&File" +msgstr "&Datei" + +#: FGameArea.form:28 +msgid "&New Game" +msgstr "&Neues Spiel" + +#: FGameArea.form:32 +msgid "&Quit" +msgstr "&Beenden" + +#: FGameArea.form:37 +msgid "&Game" +msgstr "&Spiel" + +#: FGameArea.form:40 +msgid "&Select Board" +msgstr "&Spielfeld auswählen" + +#: FGameArea.form:45 +msgid "&Help" +msgstr "&Hilfe" + +#: FGameArea.form:48 +msgid "&About" +msgstr "&Über" + +#: FGameArea.form:57 +msgid "New Game" +msgstr "Neues Spiel" + +#: FGameArea.form:65 +msgid "Restart" +msgstr "Neu starten" + +#: FGameArea.form:72 +msgid "Undo" +msgstr "Rückgängig" + +#: FGameArea.form:80 +msgid "Redo" +msgstr "Wiederherstellen" + +#: FGameArea.form:87 +msgid "Quit" +msgstr "Beenden" + diff --git a/app/examples/Games/Solitaire/.lang/es.mo b/app/examples/Games/Solitaire/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..6640cb33feea4de36ba0cb110310892e3377c1f5 GIT binary patch literal 1004 zcmZ9JJ#Q015QY~>AQ-}jp+X>SWeT`J8iN(M|##pP4iF>NKK6Hbs0|c z)7u;v^*gkZ+tVY#~;u?#JLq=kvtn$gcV?{rms<5)mpL%ZKG+Iv8<*Ti7qkijItLYVdky@u?M3TnBYE$Mb zUEk+Tx8d=M*Kq6pl3T3?Uc0@B)TBQWed%qy literal 0 HcmV?d00001 diff --git a/app/examples/Games/Solitaire/.lang/es.po b/app/examples/Games/Solitaire/.lang/es.po new file mode 100644 index 00000000..bc88c09b --- /dev/null +++ b/app/examples/Games/Solitaire/.lang/es.po @@ -0,0 +1,79 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FBoardSelect.class:50 +msgid "Select board" +msgstr "Seleccionar tablero" + +#: FBoardSelect.class:60 +msgid "Board Layout" +msgstr "Tablero de diseño" + +#: FBoardSelect.class:65 +msgid "OK" +msgstr "OK" + +#: FBoardSelect.class:70 +msgid "Cancel" +msgstr "Cancelar" + +#: FGameArea.class:392 +msgid "Solitaire" +msgstr "&Seleccionar tablero" + +#: FGameArea.class:397 +msgid "&File" +msgstr "&Archivo" + +#: FGameArea.class:400 +msgid "&New Game" +msgstr "&Nuevo juego" + +#: FGameArea.class:404 +msgid "&Quit" +msgstr "&Salir" + +#: FGameArea.class:409 +msgid "&Game" +msgstr "&Juego" + +#: FGameArea.class:412 +msgid "&Select Board" +msgstr "&Seleccionar tablero" + +#: FGameArea.class:417 +msgid "&Help" +msgstr "&Ayuda" + +#: FGameArea.class:420 +msgid "&About" +msgstr "&Acerca de" + +#: FGameArea.class:429 +msgid "New Game" +msgstr "Nuevo juego" + +#: FGameArea.class:437 +msgid "Restart" +msgstr "Reiniciar" + +#: FGameArea.class:444 +msgid "Undo" +msgstr "Deshacer" + +#: FGameArea.class:452 +msgid "Redo" +msgstr "Rehacer" + +#: FGameArea.class:459 +msgid "Quit" +msgstr "Salir" diff --git a/app/examples/Games/Solitaire/.project b/app/examples/Games/Solitaire/.project new file mode 100644 index 00000000..06212eb4 --- /dev/null +++ b/app/examples/Games/Solitaire/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.0 +Title=Some sort of solitaire game +Startup=FGameArea +Icon=ball.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +TabSize=2 +Translate=1 +Language=fr +ExecPath=/home/benoit/gambas/gambas.link/share/gambas/examples/Games/Solitaire/Solitaire +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence diff --git a/app/examples/Games/Solitaire/.src/CBoardDesign.class b/app/examples/Games/Solitaire/.src/CBoardDesign.class new file mode 100644 index 00000000..3e2c55ef --- /dev/null +++ b/app/examples/Games/Solitaire/.src/CBoardDesign.class @@ -0,0 +1,9 @@ +' Gambas class file + +' each row is stored as a number between 0-1023 (inc) +' each column numbered in 2^ and the row is the sum of the active columns +' leftmost column is 512, rightmost is 1 +Public Name As String ' Board desidn name +Public Row As New Byte[] ' Board layout +Public Placed As New Byte[] ' The cells with balls on them +Public Finish As Byte ' The cell that the last ball must be on diff --git a/app/examples/Games/Solitaire/.src/CMove.class b/app/examples/Games/Solitaire/.src/CMove.class new file mode 100644 index 00000000..6698dd82 --- /dev/null +++ b/app/examples/Games/Solitaire/.src/CMove.class @@ -0,0 +1,5 @@ +' Gambas class file + +Public Source As Byte ' Start cell +Public Target As Byte ' Finish cell +Public Captured As Byte ' Ball taken diff --git a/app/examples/Games/Solitaire/.src/FBoardSelect.class b/app/examples/Games/Solitaire/.src/FBoardSelect.class new file mode 100644 index 00000000..3b3404cd --- /dev/null +++ b/app/examples/Games/Solitaire/.src/FBoardSelect.class @@ -0,0 +1,35 @@ +' Gambas class file + +PUBLIC SUB cmdCancel_Click() + + FBoardSelect.Close + +END + +PUBLIC SUB Form_Show() + + DIM i AS Byte + + ' Fill in the combo box text section + cboLayout.Text = Global.boarddesign[Global.selectedlayout].Name + + ' Fill in the combo box dropdown section + FOR i = 0 TO Global.boarddesign.Count - 1 + cboLayout.Add(Global.boarddesign[i].Name) + NEXT + +END + + +PUBLIC SUB cmdOK_Click() + + ' Set board layout index + Global.selectedlayout = cboLayout.Index + + ' Reset board + FGameArea.reset_board() + + ' Close this window + FBoardSelect.Close + +END diff --git a/app/examples/Games/Solitaire/.src/FBoardSelect.form b/app/examples/Games/Solitaire/.src/FBoardSelect.form new file mode 100644 index 00000000..3d2c9094 --- /dev/null +++ b/app/examples/Games/Solitaire/.src/FBoardSelect.form @@ -0,0 +1,23 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(42.6667,7.5,40,11) + Text = ("Select board") + Resizable = False + { cboLayout ComboBox + MoveScaled(16,1,23,4) + Text = (" ") + } + { lblLayout TextLabel + MoveScaled(1,1,14,4) + Text = ("Board Layout") + } + { cmdOK Button + MoveScaled(1,6,11,4) + Text = ("OK") + } + { cmdCancel Button + MoveScaled(28,6,11,4) + Text = ("Cancel") + } +} diff --git a/app/examples/Games/Solitaire/.src/FGameArea.class b/app/examples/Games/Solitaire/.src/FGameArea.class new file mode 100644 index 00000000..c2312114 --- /dev/null +++ b/app/examples/Games/Solitaire/.src/FGameArea.class @@ -0,0 +1,368 @@ +' Gambas class file + +Public board As New Object[] + +Public Sub Form_Open() + + Dim i As Byte + Dim j As Byte + + Global.POW2 = [64, 32, 16, 8, 4, 2, 1] + Global.Ball = Picture.Load("ball.png") + + board.Resize(49) + + 'FGameArea.Border = window.Resizable + FGameArea.Resize((8 * 2) + (48 * 7), (8 * 2) + (48 * 8) + panToolBar.Height) + 'FGameArea.Border = window.Fixed + Global.selectedlayout = 0 + + ' Create master geometry (7 x 7 grid) + For i = 0 To 6 + For j = 0 To 6 + Wait + board[(i * 7) + j] = New PictureBox(Me) As "GameBoard" + board[(i * 7) + j].Alignment = Align.Center + board[(i * 7) + j].Border = Border.Plain + board[(i * 7) + j].height = 48 + board[(i * 7) + j].width = 48 + board[(i * 7) + j].x = 8 + (48 * j) + board[(i * 7) + j].y = 8 + (48 * i) + panToolBar.Height + Next + Next + + ' Create board layouts + MBoards.make_boards() + MBoards.fill_boards() + + reset_board() + +End + +Private Sub set_row(row_value As Byte, row_no As Byte) + + Dim i As Byte + + ' Just to be sure + If row_value > 127 Then row_value = 127 + + For i = 0 To 6 + If row_value >= global.POW2[i] Then + board[(row_no * 7) + i].visible = True + board[(row_no * 7) + i].border = border.Raised + row_value = row_value - global.POW2[i] + board[(row_no * 7) + i].Tag = (row_no * 7) + i + Else + board[(row_no * 7) + i].visible = False + Endif + Next + +End + +Private Sub place_balls(row_value As Byte, row_no As Byte) + + Dim i As Byte + + ' Just to be sure + If row_value > 127 Then row_value = 127 + + For i = 0 To 6 + If row_value >= global.POW2[i] Then + If board[(row_no * 7) + i].visible = True Then + board[(row_no * 7) + i].picture = Global.Ball + row_value = row_value - global.POW2[i] + Global.BallCount = Global.BallCount + 1 + Endif + Else + board[(row_no * 7) + i].picture = Null + Endif + Next + +End + +Public Sub GameBoard_MouseUp() + + ' If not enough balls left then return + If Global.BallCount <= 1 Then Return + ' If a ball is not already selected then select it + If Last.Picture = global.Ball Then + global.Selected = True + If global.ClickedBall <> Null Then + board[global.ClickedBall].background = 15658726 + Endif + global.ClickedBall = CByte(Last.Tag) + Last.background = &HFF0000& + Else + If global.Selected = True Then + try_take(global.ClickedBall, CByte(Last.tag)) + Endif + Endif + +End + +Private Sub try_take(source_cell As Byte, target_cell As Byte) + + Dim current_row As Byte + Dim target_row As Byte + Dim current_col As Byte + Dim target_col As Byte + Dim i As Byte + + + If board[target_cell].visible = False Then Return + + current_row = source_cell \ 7 + target_row = target_cell \ 7 + + ' Are both cells on the same row + If current_row = target_row Then + ' (YES) Check that they are close enough and have a ball in between + ' Check to the right + If source_cell + 2 < 49 Then + If source_cell + 2 = target_cell Then + ' In range + If board[source_cell + 1].picture = global.Ball Then + ' Ah good! A move can be made + ' Record move + set_move(Global.totalballs - Global.ballcount, source_cell, target_cell, source_cell + 1) + + ' clean up move recorder to make undo/redo work correctly + For i = (Global.totalballs - Global.ballcount) + 1 To Global.totalballs - 1 + set_move(i, 0, 0, 0) + Next + + ' Move pieces + board[source_cell].picture = Null + board[source_cell].background = 15658726 + board[source_cell + 1].picture = Null + board[target_cell].picture = global.Ball + global.BallCount = global.BallCount - 1 + + ' Finally activate Undo seeing as a move has been made + tbtnUndo.Enabled = True + tbtnRedo.Enabled = False + + Return + Endif + Endif + Endif + + ' Then to the left + If source_cell - 2 >= 0 Then + If source_cell - 2 = target_cell Then + ' In range + If board[source_cell - 1].picture = global.Ball Then + ' Ah good! A move can be made + ' Record move + set_move(Global.totalballs - Global.ballcount, source_cell, target_cell, source_cell - 1) + + ' clean up move recorder to make undo/redo work correctly + For i = (Global.totalballs - Global.ballcount) + 1 To Global.totalballs - 1 + set_move(i, 0, 0, 0) + Next + + ' Move pieces + board[source_cell].picture = Null + board[source_cell].background = 15658726 + board[source_cell - 1].picture = Null + board[target_cell].picture = global.Ball + global.BallCount = global.BallCount - 1 + + ' Finally activate Undo seeing as a move has been made + tbtnUndo.Enabled = True + tbtnRedo.Enabled = False + + Return + Endif + Endif + Endif + Endif + + ' Ok, so not on the same row ... how about the same column? + current_col = source_cell Mod 7 + target_col = target_cell Mod 7 + + If current_col = target_col Then + If source_cell + 14 < 49 Then + If source_cell + 14 = target_cell Then + If board[source_cell + 7].picture = global.Ball Then + ' Record move + set_move(Global.totalballs - Global.ballcount, source_cell, target_cell, source_cell + 7) + + ' clean up move recorder to make undo/redo work correctly + For i = (Global.totalballs - Global.ballcount) + 1 To Global.totalballs - 1 + set_move(i, 0, 0, 0) + Next + + ' Move pieces + board[source_cell].Picture = Null + board[source_cell].background = 15658726 + board[source_cell + 7].Picture = Null + board[target_cell].picture = global.Ball + global.BallCount = global.BallCount - 1 + + ' Finally activate Undo seeing as a move has been made + tbtnUndo.Enabled = True + tbtnRedo.Enabled = False + + Return + Endif + Endif + Endif + + If source_cell - 14 >= 0 Then + If source_cell - 14 = target_cell Then + If board[source_cell - 7].picture = global.Ball Then + ' Record move + set_move(Global.totalballs - Global.ballcount, source_cell, target_cell, source_cell - 7) + + ' clean up move recorder to make undo/redo work correctly + For i = (Global.totalballs - Global.ballcount) + 1 To Global.totalballs - 1 + set_move(i, 0, 0, 0) + Next + + ' Move pieces + board[source_cell].picture = Null + board[source_cell].background = 15658726 + board[source_cell - 7].picture = Null + board[target_cell].picture = global.Ball + global.BallCount = global.BallCount - 1 + + ' Finally activate Undo seeing as a move has been made + tbtnUndo.Enabled = True + tbtnRedo.Enabled = False + + Return + Endif + Endif + Endif + Endif + +End + +Public Sub mnuQuit_Click() + + Me.Close + +End + +Public Sub mnuNew_Click() + + reset_board() + +End + +Public Sub mnuAbout_Click() + + Dim About As String + + About = ("Solitaire v0.3\nBy: Grahame White \nWritten for Gambas http://gambas.sf.net") + Message(About) + +End + +Public Sub tbtnQuit_Click() + + Me.Close + +End + +Public Sub tbtnNewGame_Click() + + reset_board() + +End + +Public Sub reset_board() + + Dim j As Byte + + ' Clear ball count + Global.BallCount = 0 + + ' Display the board layout + For j = 0 To 6 + set_row(Global.boarddesign[Global.selectedlayout].Row[j], j) + place_balls(Global.boarddesign[Global.selectedlayout].Placed[j], j) + Next + + Global.totalballs = Global.ballcount + + ' Make sure there is enough room for all the moves (number of balls, which leaves 1 extra just in case) + Global.gamemove.Resize(Global.ballcount) + + ' Reset the move recorder + For j = 0 To Global.ballcount - 1 + Global.gamemove[j] = New CMove + set_move(j, 0, 0, 0) + Next + + ' Disable Undo/Redo buttons + tbtnUndo.Enabled = False + tbtnRedo.Enabled = False + +End + +Private Sub set_move(movenumber As Byte, source As Byte, target As Byte, capture As Byte) + + With Global.gamemove[movenumber] + .Source = Source + .Target = Target + .Captured = capture + End With + +End + +Private Sub undo_move(movenumber As Byte) + + tbtnUndo.Enabled = False + + ' Put balls in correct places + board[Global.gamemove[movenumber].target].Picture = Null + board[Global.gamemove[movenumber].captured].picture = Global.ball + board[Global.gamemove[movenumber].source].picture = Global.ball + + ' update ball counter + global.ballcount = global.ballcount + 1 + + If global.ballcount < global.totalballs Then tbtnUndo.Enabled = True + tbtnRedo.Enabled = True + +End + +Public Sub tbtnUndo_Click() + + undo_move((global.totalballs - global.ballcount) - 1) + +End + +Private Sub redo_move(movenumber As Byte) + + tbtnRedo.Enabled = False + + ' Put balls in correct places + board[Global.gamemove[movenumber].target].picture = Global.ball + board[Global.gamemove[movenumber].captured].Picture = Null + board[Global.gamemove[movenumber].source].Picture = Null + + ' Update ball counter + global.ballcount = global.ballcount - 1 + + If movenumber + 1 < global.totalballs Then + If global.gamemove[movenumber + 1].target <> global.gamemove[movenumber + 1].source Then tbtnRedo.Enabled = True + Endif + tbtnUndo.Enabled = True + +End + +Public Sub tbtnRedo_Click() + + redo_move(global.totalballs - global.ballcount) + +End + +Public Sub mnuBoardSelect_Click() + + FBoardSelect.ShowModal + +End diff --git a/app/examples/Games/Solitaire/.src/FGameArea.form b/app/examples/Games/Solitaire/.src/FGameArea.form new file mode 100644 index 00000000..b70731c2 --- /dev/null +++ b/app/examples/Games/Solitaire/.src/FGameArea.form @@ -0,0 +1,64 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(57.5,37.1667,82.6667,94.6667) + Text = ("Solitaire") + Icon = Picture["ball.png"] + Resizable = False + { mnuFile Menu + Text = ("&File") + { mnuNew Menu + Text = ("&New Game") + } + { mnuQuit Menu + Text = ("&Quit") + } + } + { mnuGame Menu + Text = ("&Game") + { mnuBoardSelect Menu + Text = ("&Select Board") + } + } + { mnuHelp Menu + Text = ("&Help") + { mnuAbout Menu + Text = ("&About") + } + } + { panToolBar Panel + MoveScaled(0,0,82.6667,4) + { tbtnNewGame ToolButton + MoveScaled(0,0,4,4) + ToolTip = ("New Game") + Text = ("") + Picture = Picture["new.png"] + } + { tbtnRestartGame ToolButton + MoveScaled(4,0,4,4) + Enabled = False + ToolTip = ("Restart") + Text = ("") + } + { tbtnUndo ToolButton + MoveScaled(4,0,4,4) + Enabled = False + ToolTip = ("Undo") + Text = ("") + Picture = Picture["undo.png"] + } + { tbtnRedo ToolButton + MoveScaled(8,0,4,4) + Enabled = False + ToolTip = ("Redo") + Text = ("") + Picture = Picture["redo.png"] + } + { tbtnQuit ToolButton + MoveScaled(12,0,4,4) + ToolTip = ("Quit") + Text = ("") + Picture = Picture["quit.png"] + } + } +} diff --git a/app/examples/Games/Solitaire/.src/Global.class b/app/examples/Games/Solitaire/.src/Global.class new file mode 100644 index 00000000..4c3d6ca2 --- /dev/null +++ b/app/examples/Games/Solitaire/.src/Global.class @@ -0,0 +1,11 @@ +' Gambas class file + +Static Public boarddesign As New Object[] ' The game grid +Static Public POW2 As Integer[] ' Array for display algo +Static Public ball As Picture ' The ball graphic +Static Public selected As Boolean ' Is a ball selected? +Static Public clickedball As Byte ' Which ball, ignored if above is false +Static Public ballcount As Byte ' The number of balls in play +Static Public totalballs As Byte ' The number of balls at the start +Static Public gamemove As New Object[] ' Stores the moves made to allow for undoing +Static Public selectedlayout As Byte ' The active board layout diff --git a/app/examples/Games/Solitaire/.src/MBoards.module b/app/examples/Games/Solitaire/.src/MBoards.module new file mode 100644 index 00000000..9e2e96e7 --- /dev/null +++ b/app/examples/Games/Solitaire/.src/MBoards.module @@ -0,0 +1,69 @@ +' Gambas module file + +PUBLIC SUB make_boards() + + DIM i AS Byte + DIM j AS Byte + + ' Create the board design objects + + Global.boarddesign.Resize(2) + + FOR i = 0 TO Global.boarddesign.Count - 1 + Global.boarddesign[i] = NEW CBoardDesign + FOR j = 1 TO 7 + Global.boarddesign[i].Row.Add(0) + Global.boarddesign[i].Placed.Add(0) + NEXT + NEXT + +END + +PUBLIC SUB fill_boards() + + ' Fill the board design Array + + WITH Global.boarddesign[0] + .Name = "Original" + + .Row[0] = 28 + .Row[1] = 28 + .Row[2] = 127 + .Row[3] = 127 + .Row[4] = 127 + .Row[5] = 28 + .Row[6] = 28 + + .Placed[0] = 28 + .Placed[1] = 28 + .Placed[2] = 127 + .Placed[3] = 119 + .Placed[4] = 127 + .Placed[5] = 28 + .Placed[6] = 28 + + .Finish = 28 + END WITH + + WITH Global.boarddesign[1] + .Name = "Plus" + + .Row[0] = 28 + .Row[1] = 28 + .Row[2] = 127 + .Row[3] = 127 + .Row[4] = 127 + .Row[5] = 28 + .Row[6] = 28 + + .Placed[0] = 0 + .Placed[1] = 8 + .Placed[2] = 8 + .Placed[3] = 62 + .Placed[4] = 8 + .Placed[5] = 8 + .Placed[6] = 0 + + .Finish = 28 + END WITH +END diff --git a/app/examples/Games/Solitaire/ball.png b/app/examples/Games/Solitaire/ball.png new file mode 100644 index 0000000000000000000000000000000000000000..5e5e042e2f7b73ca288b0cbbe68961df1796dd3c GIT binary patch literal 3327 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 VZ-apY?{=W+44$rjF6*2UngFwdJ>38R literal 0 HcmV?d00001 diff --git a/app/examples/Games/Solitaire/redo.png b/app/examples/Games/Solitaire/redo.png new file mode 100644 index 0000000000000000000000000000000000000000..72b4ce5cc8cdaa52f3740cfde3b48acbfdd4d9c9 GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9oB=)|uJ`WUYi?|2U})Abigp7E zGL{7S1v5B2yO9RuXn49fhHzX@PB_3S5zfeI*2c=kBf}%Z-M}K;@gRyp4;#Cuxa@2uUG{2v!Lei3nK-7lsZF#x6qx1IvWV a%nW69%#vXnX3PibVeoYIb6Mw<&;$Sq{2`wJ literal 0 HcmV?d00001 diff --git a/app/examples/Games/StarField/.directory b/app/examples/Games/StarField/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Games/StarField/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Games/StarField/.icon.png b/app/examples/Games/StarField/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..236e2ed08cdf5d55458da6809196a99a16380280 GIT binary patch literal 3140 zcmV-K47>A*P)=h(Q=m1hIJ^E=OX-!|REu1WywAdK9Uo3)Ph^|pLCFFXJIV1Z$9!!e5Wjo@7zu1Q5~54cxln*d9#Q#%Wx)R>ao}r&Yydps6pzXTViyW%iItrP_`&371t~4oK9sSpuz% zczf5=xTPytvf>Upn-9>_ew3R(`J2-sfU*+>C@7aR+9+QuJy8I;dI7ABIQaH+$cohz zPQ1^6s$i(Pj6_nSMPGIQSAq*Hz(l_Bl+G$M@y50AfVQUn3|5vRVSsBn!`XVv(&Za* zdmB+&2(lSWB85`dPYgGSnHTD;(OTs$umE>;77zwG&VA5~S6>g#;cfXW1MMfdz0%{% z!e2p)=49i~Xjr$6RC&$y6JXN%uC>h>(c&9XlYV$*8=-@*KFPV0jYJN@4Vx%W&%zc0 zN-C?!o!ZZleP^kz-^iYqzDHBfF&1t3H0nC8I5MdK7HIK}5%I6Zy8ps=zseu}?tOgi zAD?9Bzx*?Q{rrCuEL@IGq^R~_NJ4h+c@fig5bvhBq$-jat2(!gvb8DYeB~+aD5a_Q!xkpA8_+Pj)Kedsk-RkpGC?z<@i zQZ+TSb$0RC-#tztQA#0KG>$RVQvC<=18meb6uoR2Zyhd%3?w@ExbrF|pfGFlMF z`2v8u#H_TRXe8qm$l5$dJ6gGaSv9tLKF{oL9;9Lu<=dWhY{H51#>Wecz&I3k<AtKb?elJh_>!Sqw zU!X}0RdLPV-0>T<1_5)sJJ|a9-=7xZMHoTc`C6X}t#q0&mF!Fu@-l9T7~2b-IdOmm zHyPeK)yDhe@KR-jnO>ry0YWdquH$D}xzr;W^BB$!)7Rd@_bZn0!$Z5+Ah4sXiY{lE z(|m$$4?aAt4`=U&}K%RrVy zRV^l!rhotIl&{)Ernj3f)yLVBE2U}IE7WX#fb3uf^@|M(vhR2s*cls zhQypY@;T_fxlc!__IEH9@wYq24oe*#bcrBC#?uy&dF+-{BM6 zew|JmCsVVL(uJGRCWiK67wyW(c?)pjvgZZR_+!6PpdF;k%Q$>E!-*&U0bMqi{?-l% z1B`;ZHr>nroH@!`105yhgl;6&)G~5Xq~!yjS+(^XIQ}l7XcjiCLFp?id}R8&3DYYf zy%JFh;~XEf?j`AU(B9Lef?cD*#Z2P4s_*Ql;=^bC21ubXzk&a73+ENg=e{~ z&`0xuecb<kJpB{qRF_gwwUEqkj+b89LG7o$h+4dfTbFfmps5{~ zXaL;kOBEqzH1D?d;H~-u!#%D1(ZhejzrFM_3kDi!Sh98s5(q}0f3@#Yc>!oB&jQ(g-Rr`g@E#|W_~rP`P@BUWJ6h+{GmNK zYmw2qzi5x({axHVuZ~SIh@Wdm3Yw#b_Yn+dE_lvI-9(MV03{=w$1Ff;1pWn))r*81ZkY8dLCHX|$>jOx8{Ox@!&+;Ask1eA!jN{NWL(wG~D zQ5P8K{oqs>D~o0Gk;?h`(Uk25Q~-1c@shZ<1HGA3#j65T{rHWuZ^YA;iz8QDXm3aX z&|(Yu!M;CiyR>qBr5R5h-2mv>l;Eh7~as-*d e3}!Hc8T=n6Y29`>ozrOm0000 screen.Width Then + Stars.x = 0 + Stars.y = Int(Rnd(screen.Height)) + Stars.speed = Int(Rnd(1, 4)) + Endif + Next + + ' Attention la couleur affecte aussi les images ! + Draw.ForeColor = &hFFFFFF& + + Draw.Font.Size = Font.DefaultFontSize * 2 + Draw.Text(Screen.Framerate, 10, Screen.Height - 40) + + ' on applique la rotation et on affiche le logo + Draw.Rotate(rotation) + Draw.Image(img, Screen.Width \ 2 - 64, Screen.Height \ 2 - 64) + + ' rotation de 0 a 360 :-) + rotation = (rotation + 1) Mod 360 + +End diff --git a/app/examples/Games/StarField/enterprise.png b/app/examples/Games/StarField/enterprise.png new file mode 100644 index 0000000000000000000000000000000000000000..e76709e4227732091a19a2b225498f8413006efb GIT binary patch literal 4623 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 literal 0 HcmV?d00001 diff --git a/app/examples/Image/ImageViewer/.directory b/app/examples/Image/ImageViewer/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Image/ImageViewer/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Image/ImageViewer/.icon.png b/app/examples/Image/ImageViewer/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0ca93bd80534e39fec653bd33b959a86998f0765 GIT binary patch literal 4419 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 JPDHLkV1lkhc-8;_ literal 0 HcmV?d00001 diff --git a/app/examples/Image/ImageViewer/.lang/ca.mo b/app/examples/Image/ImageViewer/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..9136532d5da70ea1cb6658124e83903c7c256e63 GIT binary patch literal 978 zcmaiy?P?S;6oykkr+J9D2I)=k7C#3#gU#7~4@e1@@0;5>L8 zd<({U{JVUO0DR(!tAlhxpT)YX(p7oD}4@=f7HAymL37VcL, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: ImageViewer\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:07+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FViewer.form:48 +msgid "&?" +msgstr "-" + +#: FViewer.form:51 +msgid "&About..." +msgstr "&Quant a..." + +#: FViewer.class:101 +msgid "A simple image viewer example by\nBenoit Minisini (gambas@users.sourceforge.net)" +msgstr "Un exemple senzill de visualitzador d'imatges per\n Benoit Minisini (gambas@users.souceforge.net)" + +#: FViewer.form:40 +msgid "&Draw" +msgstr "&Dibuixa" + +#: FViewer.form:23 +msgid "&File" +msgstr "&Fitxer" + +#: .project:1 +msgid "Image viewer example" +msgstr "Exemple de visor d'imatges" + +#: FViewer.form:26 +msgid "&Open..." +msgstr "&Obre..." + +#: FViewer.form:34 +msgid "&Quit" +msgstr "&Surt" + +#: FViewer.form:18 +msgid "Simple Image Viewer" +msgstr "Visor d'imatges senzill" + +#: FViewer.form:43 +msgid "&Start" +msgstr "&Inicia" + +#: FViewer.class:51 +msgid "&Stop" +msgstr "&Atura" + diff --git a/app/examples/Image/ImageViewer/.lang/cs.mo b/app/examples/Image/ImageViewer/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..7ef78b1478b3d90649b33287a95cb043410e97a9 GIT binary patch literal 945 zcmb7>%Wl&^6ov;Vm%0>*%MyvHgdz~K0f|Lr3e+@iF}356G?B_~l1x(*XT}IQ^3)~|2Q%<;MCe7i99ioni7m!TKXdFU%- zj2bN5=|;i%~T1Q5s&7{FiSa2hOE!& zR>HTrqI`#ymz`bfAr~^yG)hF0L-bmo4LdAfEOM@LCzpll@(ro_+!0(~C&3@mkmk6= z_^n-*Aay05@vgRmp4~>KL<*PIy`@!e#iwn*(G236Rgdik-$Gp}>?+gTrR%=eu%p_-+Isqn1%*5T{X z{&=CSffNUO*q@E>G4Bc4E4rJb4^*(sL=95bGx;{t(f;8_ez3=>?5NS3=Yzv{RQCRN Rk@bJ&B&~r-&i+=D{070A`WyfN literal 0 HcmV?d00001 diff --git a/app/examples/Image/ImageViewer/.lang/cs.po b/app/examples/Image/ImageViewer/.lang/cs.po new file mode 100644 index 00000000..6b8d73b6 --- /dev/null +++ b/app/examples/Image/ImageViewer/.lang/cs.po @@ -0,0 +1,60 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Image viewer example" +msgstr "Příklad prohlížeče obrázků" + +#: FViewer.class:53 +msgid "&Stop" +msgstr "-" + +#: FViewer.class:103 +msgid "" +"A simple image viewer example by\n" +"Benoit Minisini (gambas@users.sourceforge.net)" +msgstr "" +"Jednoduchý například prohlížeče obrázků od\n" +"Benoit Minisini (gambas@users.sourceforge.net)" + +#: FViewer.form:19 +msgid "Simple Image Viewer" +msgstr "Ukázka prohlížeče obrázků" + +#: FViewer.form:24 +msgid "&File" +msgstr "&Soubor" + +#: FViewer.form:27 +msgid "&Open..." +msgstr "&Otevřít..." + +#: FViewer.form:35 +msgid "&Quit" +msgstr "&Ukončit" + +#: FViewer.form:41 +msgid "&Draw" +msgstr "&Kresli" + +#: FViewer.form:44 +msgid "&Start" +msgstr "-" + +#: FViewer.form:49 +msgid "&?" +msgstr "-" + +#: FViewer.form:52 +msgid "&About..." +msgstr "&O programu..." diff --git a/app/examples/Image/ImageViewer/.lang/de.mo b/app/examples/Image/ImageViewer/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..1786666c7f2d5967dee3603a70aa318a53b3b24e GIT binary patch literal 928 zcmb7?%Wl&^6o!XVZY~=x3l=N}ge(xU0f|Lr3e;)bVqwQ2X(Q-vlAI)ioUt{Y^dhn6 z31G#Vmw;U#0K~dt$&LqL$3JeMQpJXmjz0f$&YXXa#@{b5zGGMy5!VrK5SI{#2)p zXRkFP?L}%(45biuX0T_+nvYKR<8bWKr~ zERu?oR3kB%P{kWEANI*;C\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Image viewer example" +msgstr "Beispiel für einen Bildbetrachter" + +#: FViewer.class:53 +msgid "&Stop" +msgstr "&Stopp" + +#: FViewer.class:103 +msgid "A simple image viewer example by\nBenoit Minisini (gambas@users.sourceforge.net)" +msgstr "Ein einfaches Bildbetrachter-Beispiel von\nBenoit Minisini (gambas@users.sourceforge.net)" + +#: FViewer.form:19 +msgid "Simple Image Viewer" +msgstr "Einfacher Bildbetrachter" + +#: FViewer.form:24 +msgid "&File" +msgstr "&Datei" + +#: FViewer.form:27 +msgid "&Open..." +msgstr "&Öffnen..." + +#: FViewer.form:35 +msgid "&Quit" +msgstr "&Beenden" + +#: FViewer.form:41 +msgid "&Draw" +msgstr "&Zeichnen" + +#: FViewer.form:44 +msgid "&Start" +msgstr "-" + +#: FViewer.form:49 +msgid "&?" +msgstr "-" + +#: FViewer.form:52 +msgid "&About..." +msgstr "&Über..." + diff --git a/app/examples/Image/ImageViewer/.lang/es.mo b/app/examples/Image/ImageViewer/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..9271fd8a6abde0bbdfdb93a7e0a96c7f358b1669 GIT binary patch literal 624 zcmYL_%Wl*#6ow5gmqtQDumTmy3zSXe0VEbx=q+k8LqsmaWF`{qJQ+82m9a;Tr-cXM z0od~vyaxh_H7nS#;&&LSNBZ;GIp_R7I^P~_eC6WNh?>s4YtttP-Y^S_O0*p1ui3F|FcBnH0GoYAkM-hJqe>|5|`x>N>#JQ z7;Gx|GN5Ugk7n-4gET#9nu;JJ6Il@$tad|Y7&D?Zp~tCmakiq_}c zbiy9arbD)WUFT*w6NbgA)(fecfbP$w^+>\n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FViewer.class:147 +msgid "Simple Image Viewer" +msgstr "Visor de imágenes simple" + +#: FViewer.class:151 +msgid "&File" +msgstr "&Archivo" + +#: FViewer.class:154 +msgid "&Open..." +msgstr "&Abrir..." + +#: FViewer.class:163 +msgid "&Quit" +msgstr "&Salir" + +#: FViewer.class:169 +msgid "&Draw" +msgstr "&Dibujar" + +#: FViewer.class:172 +msgid "&Start" +msgstr "&Inicio" + +#: FViewer.class:177 +msgid "&?" +msgstr "&?" + +#: FViewer.class:180 +msgid "&About..." +msgstr "&Acerca de..." diff --git a/app/examples/Image/ImageViewer/.lang/nl.mo b/app/examples/Image/ImageViewer/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..63b2f257397f26092a9decf01c945fa4ef836895 GIT binary patch literal 907 zcmbV}yKdA#6o!WoZia@7prIHfWInO&dV9q`UrGh=Te z@fy4WB`-jUgy;|*1wx{whl+xKyoLmciX$I={%6MLKc4gD!t6_iIETE7e1SZV{EYO$ zk1;j_UIWj7*TEBDTyq{g4V~1w4W5MV)Oritgx&)$fv>=e;2ZD<_yNSj-oeDzc@O&U zKi2wF&97j9^>6k5chL9o6MO>B9B1q@_!PVXTF}@19lrP3TKIqUfln(hUl+&OS)>o& zhp&Cgi|_#pTRmO4IF4DkYQ%(v>oOx2Zsw$_#Ycs7EZlLzRM0tVaVy7pMqG}?ka#I+ zLWa}6s1|z%!3rrY9p8{jS|qdbrt_Ha_ai%!ITc3d6r=h3D4>P~ zZXn?*3pP3%YlmIU#f_k?l_Ld\n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Image viewer example" +msgstr "Image viewer voorbeeld" + +#: FViewer.class:53 +msgid "&Stop" +msgstr "&Stop" + +#: FViewer.class:103 +msgid "A simple image viewer example by\nBenoit Minisini (gambas@users.sourceforge.net)" +msgstr "Een eenvoudig Image Viewer voorbeeld door\nBenoît Minisini (gambas@users.sourceforge.net)" + +#: FViewer.form:19 +msgid "Simple Image Viewer" +msgstr "Eenvoudige Image Viewer" + +#: FViewer.form:24 +msgid "&File" +msgstr "&Bestand" + +#: FViewer.form:27 +msgid "&Open..." +msgstr "&Open..." + +#: FViewer.form:35 +msgid "&Quit" +msgstr "&Afsluiten" + +#: FViewer.form:41 +msgid "&Draw" +msgstr "&Teken" + +#: FViewer.form:44 +msgid "&Start" +msgstr "&Start" + +#: FViewer.form:49 +msgid "&?" +msgstr "-" + +#: FViewer.form:52 +msgid "&About..." +msgstr "&Over..." + diff --git a/app/examples/Image/ImageViewer/.project b/app/examples/Image/ImageViewer/.project new file mode 100644 index 00000000..f969dc2a --- /dev/null +++ b/app/examples/Image/ImageViewer/.project @@ -0,0 +1,20 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=Image viewer example +Startup=FViewer +Icon=image.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.form.dialog +Environment="GB_GUI=gb.gtk" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Image/ImageViewer/.src/FViewer.class b/app/examples/Image/ImageViewer/.src/FViewer.class new file mode 100644 index 00000000..520345a3 --- /dev/null +++ b/app/examples/Image/ImageViewer/.src/FViewer.class @@ -0,0 +1,106 @@ +' Gambas class file + +Public Sub mnuOpen_Click() + + Dim hImage As Image + + Dialog.Path = "/usr/share/wallpapers" + If Not Exist(Dialog.Path) Then + Dialog.Path = User.Home + Endif + + 'Dialog.Path = Application.Path + + Dialog.Filter = ["*.jpg;*.jpeg;*.png;*.bmp", "Picture files"] + + If Dialog.OpenFile() Then Return + + hImage = Image.Load(Dialog.Path) + + dwgImage.Clear() + dwgImage.Resize(hImage.Width, hImage.Height) + + Draw.Begin(dwgImage) + Draw.FillRect(0, 0, hImage.Width, hImage.Height, Color.Gray) + Draw.Image(hImage, 0, 0) + Draw.End + + dwgImage.Visible = True + +Catch + + Message.Warning(Error.Text & " !") + +End + +Public Sub mnuQuit_Click() + + Me.Close + +End + +'PUBLIC SUB dwgImage_Draw() +' +' IF hPict THEN Draw.Picture(0, 0, hPict) +' +'END + +Public Sub mnuStart_Click() + + timDraw.Enabled = Not timDraw.Enabled + + If timDraw.Enabled Then + mnuStart.Text = ("&Stop") + Else + mnuStart.Text = ("&Start") + Endif + +End + +Public Sub timDraw_Timer() + + Dim iInd As Integer + Dim X1 As Float + Dim Y1 As Float + Dim X2 As Float + Dim Y2 As Float + Dim W As Float + Dim H As Float + Dim eTime As Float + + Paint.Begin(dwgImage) + + Paint.LineWidth = 1 + + For iInd = 1 To 16 + + Paint.Brush = Paint.Color(Color.RGB(Int(Rnd(256)), Int(Rnd(256)), Int(Rnd(256)), 128)) + + X1 = Rnd(dwgImage.Width) + Y1 = Rnd(dwgImage.Height) + W = Rnd(16, 32) + H = Rnd(16, 32) + + Paint.Rectangle(X1, Y1, W, H) + Paint.Fill() + + Paint.Rotate(Pi(0.125)) + + 'Paint.Brush = Paint.Color(Color.Black) + 'Paint.Stroke + + Next + + Paint.End + + 'dwgImage.Refresh + +End + + +Public Sub mnuAbout_Click() + + Message.Info(("A simple image viewer example by\nBenoit Minisini (gambas@users.sourceforge.net)")) + +End + diff --git a/app/examples/Image/ImageViewer/.src/FViewer.form b/app/examples/Image/ImageViewer/.src/FViewer.form new file mode 100644 index 00000000..91b3c7a5 --- /dev/null +++ b/app/examples/Image/ImageViewer/.src/FViewer.form @@ -0,0 +1,52 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(34.4286,25.4286,106,81) + Text = ("Simple Image Viewer") + Icon = Picture["image.png"] + Arrangement = Arrange.Vertical + { mnuFile Menu + Text = ("&File") + { mnuOpen Menu + Text = ("&Open...") + Shortcut = "Ctrl+O" + } + { Menu1 Menu + } + { mnuQuit Menu + Text = ("&Quit") + Shortcut = "Ctrl+Q" + } + } + { Menu3 Menu + Text = ("&Draw") + { mnuStart Menu + Text = ("&Start") + } + } + { Menu2 Menu + Text = ("&?") + { mnuAbout Menu + Text = ("&About...") + } + } + { Separator1 Separator + MoveScaled(42,1,33,0) + } + { svwImage ScrollView + MoveScaled(1,4,52,46) + Background = Color.LightForeground + Expand = True + Border = False + { dwgImage DrawingArea + MoveScaled(0,0,29,27) + Visible = False + Background = &H00AA7F& + Cached = True + } + } + { timDraw #Timer + #MoveScaled(51,10) + Delay = 50 + } +} diff --git a/app/examples/Image/ImageViewer/image.png b/app/examples/Image/ImageViewer/image.png new file mode 100644 index 0000000000000000000000000000000000000000..a415746af95a1a1ce9ea0520657bfd2b61dc19d9 GIT binary patch literal 8428 zcmVQ;9xU3=Z-Ec3^$Hj1&0A%@)Nsi&)}p1SwG z@A;PZ`@VC9wU+w+|B(;~1VLC}eDx-x;!rsp82$UzD_4Jzq4VnhULt-R1mwWW2a9v%Gw*3s&%U=& zJ~M8uB^Ve(x*kGE9O+=KqBb{$(ka&1{!cBA>oYd77M=A-+f}R;ghOM=;K;92NYZUd$S&T`+ zfVLJ1cz($8$xS42lU`#1ohpnmv^!ml(Tpr#QyX7C@s8D({><0=1pGjNepCbqxLHi^ zSo_Y}!rVtX&3Xw7!jVM9L4sllF9`8G7uWHzXcj9APXHGPEEeAnS-I|fRN5wOR>-tQ zXNqQ{LDK6XeIM8HutwW~k>Sq`kB+=+#YJ!H{vag$CqG+ zp_HBArB`eqiF+ue@I&vgR~r9`7u@i^^0Oh~xgtOu_~Ol;HzNMFR%yIFi8EZs!3}(( zLPTkB5XT4>PtG&Eb_D~=M@SPzyWPWRor5^8NOqA@n+b(W{fzANs|OO@TZDJ_r+UY{s(j4kA&w!2Z*VAKC(g% z*l$$l8<(f4#`Qe>AfPl*BrFwq{7d(9%g4V?GgY`!@RI-hdail>Pk=Qb1lAhB;)f9{ zCO4teHY#obf>=9*VS#S1OB6;7lm-BZqKGt2nOJ=u_w2lrOD}u{GIqf)k@S+a?PmAe zcmC6#UV8o8-uH|#LH>KePv8Hi7c}aNk5$U`%QI#00-vxDF*q`Wqb;}p=Eu3`FTX+~ zRkW;SK^yM<_}8c{Ho=M~?#+t|ET7znPMWA*3t@8&kag>5rAXogor3QN_`Z+px`bhf zln%|F!jB3>gDWVMh6p`H5+|F-N|i6X=}m8XMvTt;-~av}8vN;p{_o|ra_#PBtG&z^ zf$w=lg@~cyAw+7q_gDUsHT9SqvW&o5Rtv##Ye5_0;Sv`-Z#@#n=*DXrj#wE!`F|A$^$UZ~vNY<5Ph5%|7GR4gzuIznNv#L+MBVAbli-1zQy zv+kywaIK|jEzcJMB?M=VoW^1aqlmGU>oL7LD(>`8Z>iR5Xl*DIBi1c5T(C+}UO03HM}`Q(2sbRUeBB229hf6Nco3&jp#bYWcTH2 zzVZX5B7;K%U@b;juofv`)u_wlid6_9QTTv^G+i*3EY=t!kgkjG`=p_dm!`PU;OMpM z3jYWAO+f$91RT5bL+7_zwQn`*y<*#p;S5cphDPxRV#1(>Qy9PrMzOkwikn>dOE+*= zr^)jlJI#xM-Ss*TcDqDr!X?+eC|~)c_k`J3s*7l?DGd}E8qQbVsD7aYAS}{#5JF(A z1OX0Ru$DAQa|tCKq!vgi@jZ`$wHt|hDcw%v-QW1wyFV&~IAZg(^Mes^{JsyYX|+0c zHyZ8XmKntvT*L6_a>CLei5HL-S`7LctGno=MYq#MDb2ON`qN}8;~zhJ7su{?j1C^- zKlu~9{*{-Zk{Hon`AW5l(uRRjf#KmntOcz#!opHLB^-&6`Fkl0lDue2vn>A}@kCWA zJco(pTS(FjrJLxMb;d`dk6jM06hhb~S9n%JXzv|=TD0BT?nekU&OdgjE)#@Tz8;Zq{;n5KQ zjJ9BwbV3f^anFs}0&5IOoFNcsW58;(QD8I!qpLt#k~pC{f10Vohj2W{F0|b(UwhwQ zpZtQg8lFWK5JCu_qDbU|_RUtu2`ETTmHHNsC^=pW{2s8);lA+~m zaKaEF9CXs9F+YoRBxx@eYnKPVv3oP_%|giUOMnnUJbwH8|FBxFzp7^nxTQ%3Mn)(O z4C93b{2+q9Nh)P{>C0Zl&f^+wG+2!>24QpXrLmP{ohmBnVXeVfO|?=cOH-oIWn^R! zqZK;Ma)pS+T7$449fxz1m@$@KFUh}0VCY+kg@Lhgr1ZcT(q4<|>?x!paiqjrP+9s` zXK~y=@W$M)o-qa-x&7wXmglR#n<+!dPw+-uL^OyKKC20&6#1zhb*(T2De z=W#(u1YoRT{>;D7n12w`4o$t1i(mMAbnEjuD*~h|a}uo?`TgH|&6R)r>0S3Dgn3#E zIC19(Hq{rKpYQfE;kuH6!2;t!nSlY1FbDt#AuU-3n>TL(1ZQSv*|>2dGc`Cl*ToM5 zN+T1fc9}Hpg4I~7DVG<}X+jh@jE)W=M21NdtWL3g>%nS-1R>~?k_W|JFUDBNy9NRa zELOJDuHAtrJ2($HB1is7Y_%PBcbev%_^#pLE6+##g25U9e8mv*o?HH>qdBn;8X31Hk zx}D+C0XAo2QcBBI3m_P3GWLx2!MgpU4` zdcCy{pil}a6pFZoF^u)7)$1JF^$ouD)z1^B3LymDZkIwK!jTRDFM8n%(YSp6);(l# z2Wu48svNvZ2_l#A<--VTNz)XiGAtHr4A$uUu0;O7AdeT_Ru_%QS98fd5ExM>Dy`t6 zYp!Q(!wV6z#mHEM>jVjZ7{cDDH1Z42z$n^BBrx&6*h^0FH*=CaE!X4|%_`YFp21OczP@kYM< z)vxiuz6w`Af0Rn4iZ+TU3|Y2p41`4|8P?=0i7|POmmu7JQ_LESf^N5i#>JI6aD&Cc zAsie>#2aa3n^3Jqtnu*!AarpGl6qTGh(f|Bq}PiP{aAuA1H$qB!DiEO&jo+obvV{B zAM5lIFDwK^k&hD%5oaOq_{|EB&7X&zuV8H*sBrLDg-3Tc__?2NAPiWxd>nxxOH|$h`tA*YB+vEMV*3`*Y{m%JMdpeK zYb{bpf}wE?mLOU~`3NkWdYBhHZw1AX_4H!FOm%_P6EC1tDxh?pRWP73g~7U+GG14w z@`-Zz;K7@no8R%@xAi)m4@e>Kd=IZML|7Q&@4lpH*;U-|uAgG<)i37It_8&TtFg7C zblYVj8Vn6f_C9``<0nqCWz!~tAYk?K^d;R#T$xDO!QYi)44C7liD2D!SS3Y zrU%ACtI@?;i@?owfRs3{OJQǵhm?bQk81p5xpaeDS7vuB&!c3*?TGZ*o$H{SpT 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 O0000mpL{Qv*oQPxBdD5C7? u;uzv_JUKx@_QZd8Mte6_6u61uB?Ci?u-2BWn_*=@Dmax?k@*z0uu3H%qfBiYOq62qYL6Bt{b_%VeA+Gs#4gN#@8o8Yag~ zCYsC{&E#Z`5}hO@LK2swNl?TsC`h->-q18lZ_xYxmiz9lnm>APfv|~z?>YCpck9-z zTi@^fEmigE2`)2at$rP&HE4s~SwqLx`!A?H`Ee6E7i6#53P56 zNu3RHKeH9B1xg6Mup);CRu^*YWQfJz=*E*VmuT}205WdKifYGq>+H}_5Q;2K%c%~8xljBH?wTR>|C)&J&tkqjEt|2|qODLp>W-o-xRe*x~ z-oyxn(J-?pp0#%uG9f>nm#W~+8gLxgegv+blE&t?60%pnic*TN+?LPRZY$u6KiJFW z#=~dztiLG%ivE5(M!OgT*-1XWeP=!^=BDxbG5FOR5Q+@fjYKt_w#1*7!{V8#JaS(# zC)%Pc`u0JN9PI_^ySxM_{_=Ym0|{|n{$bfTzO*cx%2s&vU1;wE#~rSFvZ0M5RYw_@ zBzX2;%P7f>>pQ!SmzW@(yY`@2I^XzYE}cg4vprDP)o<>D=*{l9iY$v>523>afr zI5mmy+@4Efl8?=`P}K@iJwn}(!-2gGG#)?6>Kih6;I>I@tFc)AIP|uo^==!w_PT>vF@j;lFm%qbvo=P~i>nM5f4=N%wD7!6bMV)@Rea+qEe$J>qY z!CtUOym8ls>VqvDuBxXjFP0}CnvSvjeDznbuK~1%K#UiJ0^kQVUptyS5qoRh={t+0MS1f70DgJ`Vp zVOM26vC`!SpB>M&rO7GuzdExQ9)9xC3?erF)&tCw5qO=RU|02VCs7bCm)rC_F;P(69 zl>}ip<-p+70$^}2&;tI}aJrE8VF4a~SVp3nuF&6z179QrZQx-ed031hHcEiSPqpy+ zpSFtQ30(w3SC$X7_Db0IrUlp|%f;e#yPr&mweAGr8B#^bWWkl?0sv9hbPS)FXm`-r zB>wZIhOV;wI5pDcwIS}~?(9baDd-#VojtXEuJ!)WP;dEhd(?Qqqr?>{K-4weBhR&V zjTLo8&~K?xgoQY_hA$V$F$;i^s1906%8-&W?o1W06sWQ1Qid$0R8T23=EBF_)sF~c zEL$lZv}C^pjJhDj#mfNNbvR!0E?vhDFmvgb(XNA3AJaOF9Ib$qGGuwA3R`0=KwKmN zw07CC?HSC(QYMtnqv?1ht&LSozy6+&hrbyufJf@Er7Rgza*O~MVGN+P%kCW;5v5a! zR_~=tCexdqK#X6Zgu3AN&j)5qbXS%evMiy3>O)R{;ltpDqZRa|1Q3pmsXIn(zGh;{ zRhak7kxF0(yRl;8kn&QBF`B0(RM1jNhR0d}=ECxzV~07}c^prEJ{W^%!3w$>4|7e5 zhhyWfMG3{>@>eLDv6Q&Pj7!H~j21vCH*85ILuyQLFy|@2?k(#%dAJ4(zNYJ^w1UzxWtOTMtq=`(~uPbb}5t#s#F3AxlXS8tVb(JVLowvloTP$ zQZl5}=qvghLqM>*m0$k!5gy7J$APxTFG%oW&J^d6FFTaAwpUf|I)^fBf zj1eg zv?wV@W(c9j1dGjY{DwQ1&ElnlijI(r7B+rikZ3|t2kkhhiF5exhrfjFq;ChPjNibYl=6bc-dm)g&z~rC@LoQ0Ni;+wuA))+{JySJ+3J@z7VdkA!LS z={|q6c^tB4EnsW_l*?GLv zGRg&p1keU49jnPQKGCEEVlYJ*gBW~4$EqtS$g_C2rh&bL@x&$IbhqL5cHwwp*mkIv z$rC;Jy&n34eY7{$@ss2U{B++oW((MqkVdny$UI)n(lrmBGx@<+`r?+iXivEgA6?8Y zt7YERGugJUiUOHUSR~Wl)4$U7&i+lLiC8j>MQ2|D@p1X1vW%2s$kK&2BI*-O*4mpr z;m5ax?ZJx17@;LNc7*tVVbZJ{*|=pJc2_e}S)^XKl8%ZuNSrd49c_lZlXZBa0Xp8_ zLgv!D*naITT>a)I^6d}@k=#0WC7Jo9=SO}3i>{lEiS?jF3|XlO0F2K_fM`7d6(JB? z&|em~f%c9TVkV9w#v4EiG0N|iG(!PdfW3B!Cu{*&QiOyQ{s9ZX6@o}4h(Fqb) z<|5kn6KQE6+TkU>WDY$w`-o4=9Z6kyrGa(fJ>}({b&EdvJA7kG9g^Zl&se!=|fDwd82taVKnxJ1LuT+@W zOmZibP}B}x;Rr%Xnl^7>+*J#RcMY*=IUEUf6O)-k#I~uA*eEIJNr)powv`h{D#$6G zeS!Q9#<;xv(r-C>xQeK~41m@q3xA-KaY=b3ruNSgrA z7-OjG43RW0pUOkKaD-xf(KMtw&xH>mxOdI%Xst2kPV(~d09g0HV>ph3F=nXjj#Zy0 z8jS)Bm5qF?REVLVXhyhzR3bb$gfPYorHbigb9o1c)$^zFa)V9v&TV*Nrck?Q8|@v< z9MxTf>Q10ivXCARgKa1&sH|^gX6Y^0x{tJJ2 z@ns6SiYS^;#@WIc!@k;XYz?8_4)QV*m|UF4h7B9&>+9oW-9eTty_u!A+{VBE@PF7< z@h;U>dsw&bdn{bEWK2q1S`lp)kjI4 zw;b(_<;aoa2!DXC+QazzdWoAjW58$o^Q}io6fTv=n%KYL&y3H?BG___sMbs_oq3iX zAb9cj|4aUaX>b`i>hy>}ba^Cyzvg|3s02+K#+{+$%(72yvgoRtXI&7Mh5PcLa{0dh0@*AQpS zoQ@NTFmKK@CQhCO!2Ef$iSfsB!@S9qPMUyWG#4p`ty_(B0jXqYa4zUd#HrTll2S8R zxM(@K2P*h|T^-)^T)Ks!x#a{mFZmQqno80Ba{h8~56vnDf8In42H%vq^o09JmXi6C zrZBE>GRLd;qAYb*>w!`Trca+u)N#ydacL=BFV&YS%7Lo(Mc%09zTtn%!IppLO zQeOEkTi$t**urV}QnToePv(Fz)EqlT$)VlcxO~l7y?KE0id|q_{C+>$b*ZgC(eGX! z53znfLO^F%?}yg=9LFVWJIE1R<%Bdt$;e_<79c3}Pz!SG>gh;$y*_STvWlmFw;s=w zYjCa*KweVM^hGUE4H?Evv! z^P?4$O&>vfKfQdjhkPP^_%>Gt)259=zUI%aj zI1JPSkr8u1)BtH8{T3iO&2I9`5N1FGd*BEG4yX7X{7NBwHiBF{!g-(Bj4vsL855Ft z?(4<0blE)e+gkoy(MGU-C#`R-!|7}K$k=@1z8|r_x*N+I!1Bi8^(RqU2#XfY!BT?s zWIwlFUvSEMFUoGEQ2jQ9TFky$i~o4Oflb?5X>Vzwz5FS<_dWNK5g$ZBZXU;)nz6h| z_yQT|Xea5({ZsQ8uXS4Ta1l*DbH#dMv&JJWMW~^Ildr8sM|wVT@`IRv_eQiz#Ow7# zxSFyeo6MX7*8b!2Gv0U6_Nz6$^7%_f_EgA73*fUNxNew@Pk({czuHP#+J*nQ`ijT@ Y12T8Jaaw$ZcK`qY07*qoM6N<$f>!i~0{{R3 literal 0 HcmV?d00001 diff --git a/app/examples/Image/Lighttable/.lang/ca.mo b/app/examples/Image/Lighttable/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..439a3b1bea7ea022c0fe1e956f51130ba87eff07 GIT binary patch literal 6923 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 literal 0 HcmV?d00001 diff --git a/app/examples/Image/Lighttable/.lang/ca.po b/app/examples/Image/Lighttable/.lang/ca.po new file mode 100644 index 00000000..c3c49747 --- /dev/null +++ b/app/examples/Image/Lighttable/.lang/ca.po @@ -0,0 +1,337 @@ +# Catalan translation of Lighttable +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the Lighttable package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Lighttable\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-01-01 02:59+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Lighttable" +msgstr "-" + +#: .project:2 +msgid "A tool to sort photographs" +msgstr "Una eina per a ordenar les fotografies" + +#: FHelp.form:11 +msgid "Help" +msgstr "Ajuda" + +#: FHelp.form:31 FInfo.form:17 +msgid "&Close" +msgstr "&Tanca" + +#: FInfo.class:10 +msgid "There are no Exif informations in this file." +msgstr "No hi ha informació Exif en aquest fitxer." + +#: FInfo.class:29 +msgid "&Less" +msgstr "&Menys" + +#: FInfo.form:11 +msgid "Picture Informations" +msgstr "Informacions de la imatge" + +#: FInfo.form:24 +msgid "&More" +msgstr "&Més" + +#: FMain.class:33 +msgid "Lighttable - " +msgstr "-" + +#: FMain.class:47 +msgid "Right-click for Main Menu" +msgstr "Menú principal amb un clic del botó dret" + +#: FMain.class:48 +msgid " pictures" +msgstr "Imatge" + +#: FMain.class:49 +msgid "sorted alphabetically" +msgstr "Ordenat alfabèticament" + +#: FMain.class:75 +msgid "Right-click on the background or on a frame for menus." +msgstr "Menú amb un clic del botó dret al fons o a un marc" + +#: FMain.class:95 +msgid "Right-click for Picture Menu" +msgstr "Menú de la imatge amb un clic del botó dret" + +#: FMain.class:174 +msgid "&Rename all files..." +msgstr "Canvia de nom tots els &fitxers..." + +#: FMain.class:269 +msgid "Loading picture..." +msgstr "Carregant imatges..." + +#: FMain.class:344 +msgid "sorted chronologically" +msgstr "Ordenat cronològicament" + +#: FMain.class:384 +msgid "The file &1 already exists in the current directory!" +msgstr "El fitxer &1 ja existeix al directori actual!" + +#: FMain.class:436 FRename.form:32 FRenameAll.form:80 +#: FRenameAllWarning.form:17 FStart.form:26 FTime.form:51 +msgid "&Cancel" +msgstr "&Canceŀla" + +#: FMain.class:436 FRename.form:26 FRenameAll.form:74 FStart.form:20 +#: FTime.form:45 +msgid "&OK" +msgstr "D'ac&ord" + +#: FMain.class:436 +msgid "The file &1 will be deleted." +msgstr "El fitxer &1 serà esborrat." + +#: FMain.class:729 +msgid "Loading of pictures is being aborted..." +msgstr "La càrrega d'imatges s'està canceŀlant..." + +#: FMain.class:780 +msgid "Files are being renamed..." +msgstr "S'està canviant el nom dels fitxers..." + +#: FMain.class:809 +msgid "&1 files renamed" +msgstr "&1 fitxers han canviat de nom" + +#: FMain.class:825 +msgid "Setting time informations in all files..." +msgstr "Establint la informació de l'hora a tots els fitxers..." + +#: FMain.form:44 +msgid "Main menu" +msgstr "Menú principal" + +#: FMain.form:48 +msgid "Sorted &alphabetically" +msgstr "Ordenat &alfabèticament" + +#: FMain.form:55 +msgid "Sorted &chronologically" +msgstr "Ordenat &cronològicament" + +#: FMain.form:61 +msgid "&Slideshow" +msgstr "Presentació de &diapositives" + +#: FMain.form:67 +msgid "Abort &Loading" +msgstr "Atura &la Càrrega" + +#: FMain.form:73 +msgid "&Time correction..." +msgstr "Correcció de &temps..." + +#: FMain.form:79 +msgid "&Open folder..." +msgstr "&Obre la carpeta..." + +#: FMain.form:85 +msgid "&Help" +msgstr "&Ajuda" + +#: FMain.form:91 +msgid "&Quit" +msgstr "&Surt" + +#: FMain.form:98 +msgid "Picture menu" +msgstr "Menú de la imatge" + +#: FMain.form:102 +msgid "(Move picture: drag frame with mouse)" +msgstr "(Mou la imatge: arrossega el marc amb el ratolí)" + +#: FMain.form:107 +msgid "&View picture (click on picture)" +msgstr "&Visualitza la imatge (clic a la imatge)" + +#: FMain.form:113 +msgid "&Full Screen View" +msgstr "&Vista de pantalla completa" + +#: FMain.form:119 +msgid "&Close view" +msgstr "&Tanca la vista" + +#: FMain.form:125 +msgid "&Next picture" +msgstr "&Propera imatge" + +#: FMain.form:131 +msgid "&Previous picture" +msgstr "Imatge &anterior" + +#: FMain.form:137 +msgid "Picture &informations" +msgstr "&Informacions de la imatge" + +#: FMain.form:143 +msgid "&Rename picture" +msgstr "Canvia de nom la &imatge" + +#: FMain.form:149 +msgid "&Delete picture" +msgstr "&Suprimeix la imatge" + +#: FRename.class:43 +msgid "Old and new filename are identical." +msgstr "El nom de fitxer antic i el nou són idèntics." + +#: FRename.form:11 +msgid "Rename" +msgstr "Canvi de nom" + +#: FRename.form:17 +msgid "New filename:" +msgstr "Nom de fitxer nou:" + +#: FRenameAll.class:11 +msgid "MyPicture.JPG" +msgstr "LaMevaImatge.JPG" + +#: FRenameAll.form:22 +msgid "Rename all pictures" +msgstr "Canvia de nom totes les imatges" + +#: FRenameAll.form:41 +msgid "1" +msgstr "-" + +#: FRenameAll.form:47 +msgid "Keep original filename as suffix" +msgstr "Manté el nom original del fitxer com a sufix" + +#: FRenameAll.form:53 +msgid "Number format" +msgstr "Format del nombre" + +#: FRenameAll.form:58 +msgid "digits" +msgstr "dígits" + +#: FRenameAll.form:63 +msgid "Prefix" +msgstr "Prefix" + +#: FRenameAll.form:68 +msgid "Start value" +msgstr "Valor inicial" + +#: FRenameAll.form:88 +msgid "This function will rename all .jp(e)g files in the directory and add serial numbers to the filenames.
    The pictures will be handled in the order they are shown right now, in rows from left to right, rows from top to bottom." +msgstr "Aquesta funció canviarà el nom de tots els fitxers del directori i afegirà un número de serie al nom dels fitxers.
    Les imatges es gestionaran en el mateix ordre en que es mostren ara, en files d'esquerra a dreta, files de dalt a baix." + +#: FRenameAll.form:94 +msgid "Options" +msgstr "Opcions" + +#: FRenameAll.form:99 +msgid "Example:" +msgstr "Exemple:" + +#: FRenameAllWarning.form:12 +msgid "File Conflicts" +msgstr "Conflictes de fitxers" + +#: FRenameAllWarning.form:24 +msgid "Continue &anyway" +msgstr "Continua de totes m&aneres" + +#: FRenameAllWarning.form:29 +msgid "The following files can't be renamed, because the target filenames already exist in the directory.
    If you continue, only the files without conflicts will be renamed.
    If you cancel, you can choose new filename options." +msgstr "No es pot canviar el nom d'aquests fitxers perquè els noms de fitxer de destí ja existeixen al directori.
    Si continueu, només els fitxers sense conflicte canviaran de nom.
    Si canceŀleu, podreu triar noves opcions de nom de fitxer." + +#: FSlideshow.form:38 +msgid "Pause between pictures:" +msgstr "Pausa entre imatges:" + +#: FSlideshow.form:49 +msgid "sec" +msgstr "seg" + +#: FSlideshow.form:54 +msgid "Stop" +msgstr "Atura" + +#: FStart.class:54 +msgid "The folder &1 doesn't contain any jp(e)g files." +msgstr "La carpeta &1 no conté cap fitxer jp(e)g." + +#: FStart.form:15 +msgid "Lighttable - Select picture folder" +msgstr "Lighttable - Trieu una carpeta d'imatges " + +#: FStart.form:41 +msgid "New selection" +msgstr "Nova selecció" + +#: FStart.form:51 +msgid "Last selections" +msgstr "Últimes seleccions" + +#: FTime.class:21 +msgid "There is no time correction selected." +msgstr "No hi ha correcció de temps seleccionat." + +#: FTime.form:18 +msgid "Time correction" +msgstr "Correcció de temps" + +#: FTime.form:39 +msgid "earlier" +msgstr "previ" + +#: FTime.form:39 +msgid "later" +msgstr "posterior" + +#: FTime.form:59 +msgid "This function will correct all date/time informations in the EXIF section of all .jp(e)g files in the folder.
    A backup of the files will be made with file names like 'MyPicture.jpg_original'.
    If there are no EXIF date/time informations in a file, nothing will be done." +msgstr "Aquesta funció corregirà les informacions de data/hora dins de la secció Exif de tots els fitxers .jp(e)g de la carpeta.
    Es farà una còpia de seguretat dels fitxers amb noms de fitxers com «LaMevaImatge.jpg_original».
    Si no hi ha informacions Exif de data/hora dins el fitxer, no es farà res." + +#: FTime.form:65 +msgid "Set the original time of all pictures to" +msgstr "Estableix l'hora original de totes les imatges de" + +#: FTime.form:70 +msgid "day(s)" +msgstr "dia(es)" + +#: FTime.form:75 +msgid "hour(s)" +msgstr "Hora(es)" + +#: FTime.form:80 +msgid "minute(s)" +msgstr "minut(s)" + +#: MMain.module:12 +msgid "To run this program, exiftool must be installed." +msgstr "Per poder executar aquest programa, l'exiftool ha d'estar instaŀlat." + +#: MMain.module:18 +msgid "To run this program, convert must be installed." +msgstr "Per poder executar aquest programa, el convertidor ha d'estar instaŀlat." diff --git a/app/examples/Image/Lighttable/.lang/cs.mo b/app/examples/Image/Lighttable/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..daa3a49b3777890f798796cc084e890bc5bce504 GIT binary patch literal 6606 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 LG4;B(=B57u(\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Lighttable" +msgstr "-" + +#: .project:2 +msgid "A tool to sort photographs" +msgstr "Nástroj na třídění fotografií" + +#: FHelp.form:11 +msgid "Help" +msgstr "Nápověda" + +#: FHelp.form:31 FInfo.form:17 +msgid "&Close" +msgstr "&Zavři" + +#: FInfo.class:10 +msgid "There are no Exif informations in this file." +msgstr "O tomto souboru nejsou Exif informace." + +#: FInfo.class:29 +msgid "&Less" +msgstr "&Méně" + +#: FInfo.form:11 +msgid "Picture Informations" +msgstr "Informace o obrázku" + +#: FInfo.form:24 +msgid "&More" +msgstr "&Více" + +#: FMain.class:32 +msgid "Lighttable - " +msgstr "-" + +#: FMain.class:46 +msgid "Right-click for Main Menu" +msgstr "Klikni pravým pro menu" + +#: FMain.class:47 +msgid " pictures" +msgstr " obrázek" + +#: FMain.class:48 +msgid "sorted alphabetically" +msgstr "seřadit abecedně" + +#: FMain.class:74 +msgid "Right-click on the background or on a frame for menus." +msgstr "Klikni pravým na pozadí a nebo rám pro menu." + +#: FMain.class:94 +msgid "Right-click for Picture Menu" +msgstr "Klikni pravým pro menu obrázku" + +#: FMain.class:173 +msgid "&Rename all files..." +msgstr "&Přejmenovat všechny soubory..." + +#: FMain.class:268 +msgid "Loading picture..." +msgstr "Načítání obrázku..." + +#: FMain.class:343 +msgid "sorted chronologically" +msgstr "seřadit chronologicky" + +#: FMain.class:383 +msgid "The file &1 already exists in the current directory!" +msgstr "Soubor &1 již existuje v aktuální složce!" + +#: FMain.class:435 FRename.form:32 FRenameAll.form:80 +#: FRenameAllWarning.form:17 FStart.form:26 FTime.form:51 +msgid "&Cancel" +msgstr "&Zrušit" + +#: FMain.class:435 FRename.form:26 FRenameAll.form:74 FStart.form:20 +#: FTime.form:45 +msgid "&OK" +msgstr "-" + +#: FMain.class:435 +msgid "The file &1 will be deleted." +msgstr "Soubor &1 bude smazán." + +#: FMain.class:727 +msgid "Loading of pictures is being aborted..." +msgstr "Načítání obrázků bylo přerušeno..." + +#: FMain.class:778 +msgid "Files are being renamed..." +msgstr "Soubor byl přejmenován..." + +#: FMain.class:807 +msgid "&1 files renamed" +msgstr "Soubor &1 přejmenován" + +#: FMain.class:823 +msgid "Setting time informations in all files..." +msgstr "Nastavení časové informace ve všech souborech..." + +#: FMain.form:44 +msgid "Main menu" +msgstr "Hlavní menu" + +#: FMain.form:48 +msgid "Sorted &alphabetically" +msgstr "Seřadit &abecedně" + +#: FMain.form:55 +msgid "Sorted &chronologically" +msgstr "Seřadit &chronologicky" + +#: FMain.form:61 +msgid "&Slideshow" +msgstr "&Promítání" + +#: FMain.form:67 +msgid "Abort &Loading" +msgstr "Přerušit &načítání" + +#: FMain.form:73 +msgid "&Time correction..." +msgstr "&Korekce času..." + +#: FMain.form:79 +msgid "&Open folder..." +msgstr "&Otevřít složku..." + +#: FMain.form:85 +msgid "&Help" +msgstr "&Nápověda" + +#: FMain.form:91 +msgid "&Quit" +msgstr "&Ukončit" + +#: FMain.form:98 +msgid "Picture menu" +msgstr "Menu obrázku" + +#: FMain.form:102 +msgid "(Move picture: drag frame with mouse)" +msgstr "(Přesun obrázku: uchop s myší rám)" + +#: FMain.form:107 +msgid "&View picture (click on picture)" +msgstr "&Zobrazení obrázku (klikni na obrázek)" + +#: FMain.form:113 +msgid "&Full Screen View" +msgstr "&Plný náhled obrázku" + +#: FMain.form:119 +msgid "&Close view" +msgstr "&Zavřít pohled" + +#: FMain.form:125 +msgid "&Next picture" +msgstr "&Další obrázek" + +#: FMain.form:131 +msgid "&Previous picture" +msgstr "&Předchozí obrázek" + +#: FMain.form:137 +msgid "Picture &informations" +msgstr "&Informace o obrázku" + +#: FMain.form:143 +msgid "&Rename picture" +msgstr "&Přejmenovat obrázek" + +#: FMain.form:149 +msgid "&Delete picture" +msgstr "&Smazat obrázek" + +#: FRename.class:43 +msgid "Old and new filename are identical." +msgstr "Starý a nový název je identický." + +#: FRename.form:11 +msgid "Rename" +msgstr "Přejmenovat" + +#: FRename.form:17 +msgid "New filename:" +msgstr "Nový název souboru:" + +#: FRenameAll.class:11 +msgid "MyPicture.JPG" +msgstr "-" + +#: FRenameAll.form:22 +msgid "Rename all pictures" +msgstr "Přejmenovat všechy obrázky" + +#: FRenameAll.form:41 +msgid "1" +msgstr "-" + +#: FRenameAll.form:47 +msgid "Keep original filename as suffix" +msgstr "Nechat originální název jako příponu" + +#: FRenameAll.form:53 +msgid "Number format" +msgstr "Číselný formát" + +#: FRenameAll.form:58 +msgid "digits" +msgstr "číslice" + +#: FRenameAll.form:63 +msgid "Prefix" +msgstr "Předpona" + +#: FRenameAll.form:68 +msgid "Start value" +msgstr "Startovní hodnota" + +#: FRenameAll.form:88 +msgid "This function will rename all .jp(e)g files in the directory and add serial numbers to the filenames.
    The pictures will be handled in the order they are shown right now, in rows from left to right, rows from top to bottom." +msgstr "Tato funkce přejmenuje všechny .jp(e)g soubory v adresáři a přidat sériová čísla do názvu souborů.
    Fotografie budou zpracovány v pořadí, v jakém jsou právě teď zobrazeny, v řádcích zleva doprava, řádky od shora dolů." + +#: FRenameAll.form:94 +msgid "Options" +msgstr "Možnosti" + +#: FRenameAll.form:99 +msgid "Example:" +msgstr "Příklad:" + +#: FRenameAllWarning.form:12 +msgid "File Conflicts" +msgstr "Konflikt souboru" + +#: FRenameAllWarning.form:24 +msgid "Continue &anyway" +msgstr "Pokračuj &stejně" + +#: FRenameAllWarning.form:29 +msgid "The following files can't be renamed, because the target filenames already exist in the directory.
    If you continue, only the files without conflicts will be renamed.
    If you cancel, you can choose new filename options." +msgstr "Následující soubory nelze přejmenovat, protože cílový souborů již v adresáři existuje.
    Budete-li pokračovat, budou přejmenovány pouze soubory bez konfliktů.
    Pokud zrušíte, můžete zvolit možnost nového jména souboru." + +#: FSlideshow.form:38 +msgid "Pause between pictures:" +msgstr "Pauza mezi obrázky:" + +#: FSlideshow.form:49 +msgid "sec" +msgstr "-" + +#: FSlideshow.form:54 +msgid "Stop" +msgstr "-" + +#: FStart.class:54 +msgid "The folder &1 doesn't contain any jp(e)g files." +msgstr "Složka &1 neobsahuje žádný soubor typu jp(e)g." + +#: FStart.form:15 +msgid "Lighttable - Select picture folder" +msgstr "Lighttable - Výběr složky obrázků" + +#: FStart.form:41 +msgid "New selection" +msgstr "Nový výběr" + +#: FStart.form:51 +msgid "Last selections" +msgstr "Poslední výběry" + +#: FTime.class:18 +msgid "There is no time correction selected." +msgstr "Není vybrán čas korekce." + +#: FTime.form:18 +msgid "Time correction" +msgstr "Korekce času" + +#: FTime.form:39 +msgid "earlier" +msgstr "dříve" + +#: FTime.form:39 +msgid "later" +msgstr "později" + +#: FTime.form:59 +msgid "This function will correct all date/time informations in the EXIF section of all .jp(e)g files in the folder.
    A backup of the files will be made with file names like 'MyPicture.jpg_original'.
    If there are no EXIF date/time informations in a file, nothing will be done." +msgstr "Tato funkce opraví všechny informace o datu/času v sekci EXIF všechny. jp(e)g soubory ve složce.
    Záloha souborů s názvy souborů bude jako 'MyPicture.jpg_original'.
    Pokud není EXIF informace o datu/času v souboru, nebude dělat nic." + +#: FTime.form:65 +msgid "Set the original time of all pictures to" +msgstr "Nastav původní čas všech obrázků na" + +#: FTime.form:70 +msgid "day(s)" +msgstr "dní" + +#: FTime.form:75 +msgid "hour(s)" +msgstr "hodin" + +#: FTime.form:80 +msgid "minute(s)" +msgstr "minut" + +#: MMain.module:12 +msgid "To run this program, exiftool must be installed." +msgstr "Aby program běžel, musí být nainstalován exiftool." + +#: MMain.module:18 +msgid "To run this program, convert must be installed." +msgstr "Aby program běžel, musí být nainstalován convert." diff --git a/app/examples/Image/Lighttable/.lang/de.mo b/app/examples/Image/Lighttable/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..69bec35ada3bb10a7a5e67ef4984cb42e2ff1175 GIT binary patch literal 6654 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 R=FGU>+>_5H_2!=L{{VMUP2B(h literal 0 HcmV?d00001 diff --git a/app/examples/Image/Lighttable/.lang/de.po b/app/examples/Image/Lighttable/.lang/de.po new file mode 100644 index 00000000..a30ab99d --- /dev/null +++ b/app/examples/Image/Lighttable/.lang/de.po @@ -0,0 +1,330 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Lighttable" +msgstr "Leuchttisch" + +#: .project:2 +msgid "A tool to sort photographs" +msgstr "Fotos sortieren" + +#: FHelp.form:11 +msgid "Help" +msgstr "Hilfe" + +#: FHelp.form:31 FInfo.form:17 +msgid "&Close" +msgstr "&Schließen" + +#: FInfo.class:10 +msgid "There are no Exif informations in this file." +msgstr "Diese Datei entält keine Exif-Informationen." + +#: FInfo.class:29 +msgid "&Less" +msgstr "&Weniger" + +#: FInfo.form:11 +msgid "Picture Informations" +msgstr "Bildinformationen" + +#: FInfo.form:24 +msgid "&More" +msgstr "&Mehr" + +#: FMain.class:33 +msgid "Lighttable - " +msgstr "Leuchttisch - " + +#: FMain.class:47 +msgid "Right-click for Main Menu" +msgstr "Rechtsklick für das Hauptmenü" + +#: FMain.class:48 +msgid " pictures" +msgstr " Bilder" + +#: FMain.class:49 +msgid "sorted alphabetically" +msgstr "alphabetisch sortiert" + +#: FMain.class:75 +msgid "Right-click on the background or on a frame for menus." +msgstr "Menüs: auf den Hintergrund oder auf einen Rahmen rechtsklicken." + +#: FMain.class:95 +msgid "Right-click for Picture Menu" +msgstr "Rechtsklick für das Bildmenü" + +#: FMain.class:174 +msgid "&Rename all files..." +msgstr "Alle Dateien &umbenennen..." + +#: FMain.class:269 +msgid "Loading picture..." +msgstr "Bild wird geladen..." + +#: FMain.class:344 +msgid "sorted chronologically" +msgstr "chronologisch sortiert" + +#: FMain.class:384 +msgid "The file &1 already exists in the current directory!" +msgstr "Die Datei &1 existiert im aktuellen Verzeichnis bereits!" + +#: FMain.class:436 FRename.form:32 FRenameAll.form:80 +#: FRenameAllWarning.form:17 FStart.form:26 FTime.form:51 +msgid "&Cancel" +msgstr "&Abbrechen" + +#: FMain.class:436 FRename.form:26 FRenameAll.form:74 FStart.form:20 +#: FTime.form:45 +msgid "&OK" +msgstr "-" + +#: FMain.class:436 +msgid "The file &1 will be deleted." +msgstr "Die Datei &1 wird gelöscht." + +#: FMain.class:729 +msgid "Loading of pictures is being aborted..." +msgstr "Das Laden der Bilder wird abgebrochen..." + +#: FMain.class:780 +msgid "Files are being renamed..." +msgstr "Dateien werden umbenannt..." + +#: FMain.class:809 +msgid "&1 files renamed" +msgstr "&1 Dateien umbenannt" + +#: FMain.class:825 +msgid "Setting time informations in all files..." +msgstr "Die Zeitinformationen in allen Dateien werden geändert..." + +#: FMain.form:44 +msgid "Main menu" +msgstr "Hauptmenü" + +#: FMain.form:48 +msgid "Sorted &alphabetically" +msgstr "&Alphabetisch sortiert" + +#: FMain.form:55 +msgid "Sorted &chronologically" +msgstr "&Chronologisch sortiert" + +#: FMain.form:61 +msgid "&Slideshow" +msgstr "Dia&show" + +#: FMain.form:67 +msgid "Abort &Loading" +msgstr "&Laden abbrechen" + +#: FMain.form:73 +msgid "&Time correction..." +msgstr "&Zeitkorrektur..." + +#: FMain.form:79 +msgid "&Open folder..." +msgstr "&Ordner öffnen..." + +#: FMain.form:85 +msgid "&Help" +msgstr "&Hilfe" + +#: FMain.form:91 +msgid "&Quit" +msgstr "&Beenden" + +#: FMain.form:98 +msgid "Picture menu" +msgstr "Bildermenü" + +#: FMain.form:102 +msgid "(Move picture: drag frame with mouse)" +msgstr "(Bild verschieben: Rahmen mit Maus ziehen)" + +#: FMain.form:107 +msgid "&View picture (click on picture)" +msgstr "Vergrößerte &Ansicht (Bild anklicken)" + +#: FMain.form:113 +msgid "&Full Screen View" +msgstr "&Vollbildansicht" + +#: FMain.form:119 +msgid "&Close view" +msgstr "&Ansicht schließen" + +#: FMain.form:125 +msgid "&Next picture" +msgstr "&Nächstes Bild" + +#: FMain.form:131 +msgid "&Previous picture" +msgstr "Vor&heriges Bild" + +#: FMain.form:137 +msgid "Picture &informations" +msgstr "&Bildinformationen" + +#: FMain.form:143 +msgid "&Rename picture" +msgstr "Bild &umbenennen" + +#: FMain.form:149 +msgid "&Delete picture" +msgstr "Bild &löschen" + +#: FRename.class:43 +msgid "Old and new filename are identical." +msgstr "Alter und neuer Dateiname sind gleich." + +#: FRename.form:11 +msgid "Rename" +msgstr "Umbenennen" + +#: FRename.form:17 +msgid "New filename:" +msgstr "Neuer Dateiname:" + +#: FRenameAll.class:11 +msgid "MyPicture.JPG" +msgstr "MeinBild.JPG" + +#: FRenameAll.form:22 +msgid "Rename all pictures" +msgstr "Alle Bilder umbenennen" + +#: FRenameAll.form:41 +msgid "1" +msgstr "-" + +#: FRenameAll.form:47 +msgid "Keep original filename as suffix" +msgstr "originalen Dateinamen als Suffix behalten" + +#: FRenameAll.form:53 +msgid "Number format" +msgstr "Zahlenformat" + +#: FRenameAll.form:58 +msgid "digits" +msgstr "Stellen" + +#: FRenameAll.form:63 +msgid "Prefix" +msgstr "Präfix" + +#: FRenameAll.form:68 +msgid "Start value" +msgstr "Startwert" + +#: FRenameAll.form:88 +msgid "This function will rename all .jp(e)g files in the directory and add serial numbers to the filenames.
    The pictures will be handled in the order they are shown right now, in rows from left to right, rows from top to bottom." +msgstr "Diese Funktion benennt alle .jp(e)g-Dateien im Verzeichnis um und fügt Seriennummern zu den Dateinamen hinzu.
    Die Dateien werden in der Reihenfolge bearbeitet, in der sie jetzt gezeigt werden, die Reihen von oben nach unten, in der Reihe von links nach rechts." + +#: FRenameAll.form:94 +msgid "Options" +msgstr "Optionen" + +#: FRenameAll.form:99 +msgid "Example:" +msgstr "Beispiel:" + +#: FRenameAllWarning.form:12 +msgid "File Conflicts" +msgstr "Dateikonflikte" + +#: FRenameAllWarning.form:24 +msgid "Continue &anyway" +msgstr "&Trotzdem fortfahren" + +#: FRenameAllWarning.form:29 +msgid "The following files can't be renamed, because the target filenames already exist in the directory.
    If you continue, only the files without conflicts will be renamed.
    If you cancel, you can choose new filename options." +msgstr "Folgende Dateien können nicht umbenannt werden, weil die Zielnamen im Verzeichnis bereits existieren.
    Wenn Sie fortfahren, werden nur die Dateien ohne Konflikt umbenannt.
    Wenn Sie abbrechen, können Sie neue Optionen für die Dateinamen einstellen." + +#: FSlideshow.form:38 +msgid "Pause between pictures:" +msgstr "Pause zwischen Bildern:" + +#: FSlideshow.form:49 +msgid "sec" +msgstr "Sek." + +#: FSlideshow.form:54 +msgid "Stop" +msgstr "-" + +#: FStart.class:54 +msgid "The folder &1 doesn't contain any jp(e)g files." +msgstr "Das Verzeichnis &1 enthält keine jp(e)g-Dateien." + +#: FStart.form:15 +msgid "Lighttable - Select picture folder" +msgstr "Leuchttisch - Bilderverzeichnis auswählen" + +#: FStart.form:41 +msgid "New selection" +msgstr "Neue Auswahl" + +#: FStart.form:51 +msgid "Last selections" +msgstr "Zuletzt ausgewählt" + +#: FTime.class:21 +msgid "There is no time correction selected." +msgstr "Es ist keine Zeitkorrektur gesetzt." + +#: FTime.form:18 +msgid "Time correction" +msgstr "Zeitkorrektur" + +#: FTime.form:39 +msgid "earlier" +msgstr "früher" + +#: FTime.form:39 +msgid "later" +msgstr "später" + +#: FTime.form:59 +msgid "This function will correct all date/time informations in the EXIF section of all .jp(e)g files in the folder.
    A backup of the files will be made with file names like 'MyPicture.jpg_original'.
    If there are no EXIF date/time informations in a file, nothing will be done." +msgstr "Diese Funktion korrigiert alle Datums-/Zeitinformationen im EXIF-Abschnitt aller .jp(e)g-Dateien im Verzeichnis.
    Es werden Sicherungskopien mit Namen wie 'MeinBild.jpg_original' erstellt.
    Falls eine Datei keine Zeitinformationen enthält, wird nichts unternommen." + +#: FTime.form:65 +msgid "Set the original time of all pictures to" +msgstr "Die Originalzeit aller Bilder ändern auf" + +#: FTime.form:70 +msgid "day(s)" +msgstr "Tag(e)" + +#: FTime.form:75 +msgid "hour(s)" +msgstr "Stunde(n)" + +#: FTime.form:80 +msgid "minute(s)" +msgstr "Minute(n)" + +#: MMain.module:12 +msgid "To run this program, exiftool must be installed." +msgstr "Für dieses Programm muss exiftool installiert sein." + +#: MMain.module:18 +msgid "To run this program, convert must be installed." +msgstr "Für dieses Programm muss convert installiert sein." diff --git a/app/examples/Image/Lighttable/.lang/en.mo b/app/examples/Image/Lighttable/.lang/en.mo new file mode 100644 index 0000000000000000000000000000000000000000..53aa5683acbb80dd5ea81b6a9256f037f1dd4c79 GIT binary patch literal 303 zcmYMuy-ve06b0ZSm@+amcwhq;*n%llt;Q`8b{yrW5^N?l!6?|3V-P$Dug6=kbG^_b zebYVCJwL}s-xJ6II02{N5S)XU6#Rl~5Ig@Vy?fPN!1)$$v>%PL1eJ7oPuC1>SXJk; zNJ@z-{bAw}XDVod9vH2#kT|30P7;xyJ<5V5T#W%YUfFR_!FhsKE$679f}t4`H0KMN zWfiOIIaF=`u6m6dt=)c)neE)x*q)&D$%JI`2iJyCT>t<8 literal 0 HcmV?d00001 diff --git a/app/examples/Image/Lighttable/.lang/en.po b/app/examples/Image/Lighttable/.lang/en.po new file mode 100644 index 00000000..9ebff4fb --- /dev/null +++ b/app/examples/Image/Lighttable/.lang/en.po @@ -0,0 +1,264 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FMain.class:40 +msgid "Lighttable" +msgstr "" + +#: FHelp.class:30 +msgid "Help" +msgstr "" + +#: FHelp.class:38 +msgid "&Close" +msgstr "" + +#: FMain.class:67 +msgid "Choose picture folder" +msgstr "" + +#: FMain.class:76 +msgid "The folder &1 doesn't contain any jp(e)g files." +msgstr "" + +#: FMain.class:80 +#, fuzzy +msgid "Lighttable - " +msgstr "Lighttable - " + +#: FMain.class:81 +msgid " pictures" +msgstr "" + +#: FMain.class:82 +msgid "sorted alphabetically" +msgstr "" + +#: FMain.class:100 +msgid "To see the menus, right-click on the background or on a frame." +msgstr "" + +#: FMain.class:189 +msgid "&Rename all..." +msgstr "" + +#: FMain.class:332 +msgid "sorted chronologically" +msgstr "" + +#: FMain.class:389 +msgid "This filename already exists in the current directory !" +msgstr "" + +#: FMain.class:421 FRename.class:71 FRenameAll.class:69 +msgid "&Cancel" +msgstr "" + +#: FMain.class:421 FRename.class:65 FRenameAll.class:76 +#, fuzzy +msgid "&OK" +msgstr "-" + +#: FMain.class:421 +msgid "The file &1 will be deleted." +msgstr "" + +#: FMain.class:787 +msgid "Main menu" +msgstr "" + +#: FMain.class:792 +msgid "Sorted &alphabetically" +msgstr "" + +#: FMain.class:799 +msgid "Sorted &chronologically" +msgstr "" + +#: FMain.class:805 +msgid "Abort &Loading" +msgstr "" + +#: FMain.class:810 +msgid "&Help" +msgstr "" + +#: FMain.class:816 +msgid "&Quit" +msgstr "" + +#: FMain.class:823 +msgid "Frames menu" +msgstr "" + +#: FMain.class:827 +msgid "(Move picture: drag frame with mouse)" +msgstr "" + +#: FMain.class:832 +msgid "&View picture (click on picture)" +msgstr "" + +#: FMain.class:838 +msgid "&Full Screen View" +msgstr "" + +#: FMain.class:844 +msgid "&Close view" +msgstr "" + +#: FMain.class:850 +msgid "&Next picture" +msgstr "" + +#: FMain.class:856 +msgid "&Previous picture" +msgstr "" + +#: FMain.class:862 +#, fuzzy +msgid "Picture &informations" +msgstr "Picture informations" + +#: FMain.class:868 +msgid "&Rename picture" +msgstr "" + +#: FMain.class:874 +msgid "&Delete picture" +msgstr "" + +#: FRename.class:43 +msgid "Rename" +msgstr "" + +#: FRename.class:50 +msgid "New filename:" +msgstr "" + +#: FRename.class:56 +msgid "lblHidden" +msgstr "" + +#: FRenameAll.class:65 +msgid "Rename all pictures" +msgstr "" + +#: FRenameAll.class:83 +msgid "This function will number serially all the pictures.
    The pictures will be handled in the order they are
    shown right now, in rows from left to right,
    rows from top to bottom." +msgstr "" + +#: FRenameAll.class:94 +msgid "&Add number, keep the filename" +msgstr "" + +#: FRenameAll.class:100 +msgid "&Replace filename with number" +msgstr "" + +#: FRenameAll.class:111 +msgid "Number format:" +msgstr "" + +#: FRenameAll.class:116 +msgid "digits" +msgstr "" + +#: FRenameAll.class:123 +msgid "Options" +msgstr "" + +#: FRenameAll.class:129 +msgid "TextBox1" +msgstr "" + +#: FRenameAll.class:136 +msgid "Example:" +msgstr "" + +#~ msgid "sortiert nach Dateinamen" +#~ msgstr "Sorted by file names" + +#~ msgid "sortiert nach Aufnahmedatum und -zeit" +#~ msgstr "Sorted by picture date/time" + +#~ msgid "abbrechen" +#~ msgstr "Cancel" + +#~ msgid "Ansicht schließen" +#~ msgstr "Close current view" + +#~ msgid "beenden" +#~ msgstr "Quit" + +#~ msgid " Bilder" +#~ msgstr " pictures" + +#~ msgid "Bilderverzeichnis auswählen" +#~ msgstr "Choose picture folder" + +#~ msgid "Bild löschen" +#~ msgstr "Delete picture" + +#~ msgid "Bild umbenennen" +#~ msgstr "Rename picture" + +#~ msgid "(Bild verschieben: Rahmen mit der Maus ziehen)" +#~ msgstr "(Move picture: drag frame with mouse)" + +#~ msgid "Das Verzeichnis &1 enthält keine jp(e)g-Dateien." +#~ msgstr "The folder &1 doesn't contain .jp(e)g files." + +#~ msgid "Die Datei &1 wird gelöscht." +#~ msgstr "The file &1 will be deleted." + +#~ msgid "Dieser Dateiname existiert im aktuellen Verzeichnis schon!" +#~ msgstr "This filename already exists in the current directory!" + +#~ msgid "Für die Menüs mit der rechten Maustaste überall klicken!" +#~ msgstr "Just right-click everywhere to get the menus!" + +#~ msgid "Größeres Bild (Bild anklicken)" +#~ msgstr "View picture (click picture)" + +#~ msgid "Hauptmenü" +#~ msgstr "-" + +#~ msgid "Hilfe" +#~ msgstr "Help" + +#~ msgid "Label1" +#~ msgstr "-" + +#~ msgid "Leuchttisch" +#~ msgstr "Lighttable" + +#~ msgid "Nächstes Bild" +#~ msgstr "Next picture" + +#~ msgid "neuer Dateiname:" +#~ msgstr "New filename:" + +#~ msgid "Rahmenmenü" +#~ msgstr "-" + +#~ msgid "schließen" +#~ msgstr "Close" + +#~ msgid "sortiert nach Aufnahmedatum/-zeit" +#~ msgstr "Sorted by picture date/time" + +#~ msgid "Vollbildansicht" +#~ msgstr "Full Screen View" + +#~ msgid "Vorheriges Bild" +#~ msgstr "Previous picture" diff --git a/app/examples/Image/Lighttable/.lang/nl.mo b/app/examples/Image/Lighttable/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..3ef616a52763395cce1de40d2d4ccd93c49ace45 GIT binary patch literal 6754 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 EAJa2p7XSbN literal 0 HcmV?d00001 diff --git a/app/examples/Image/Lighttable/.lang/nl.po b/app/examples/Image/Lighttable/.lang/nl.po new file mode 100644 index 00000000..3c79a5c3 --- /dev/null +++ b/app/examples/Image/Lighttable/.lang/nl.po @@ -0,0 +1,327 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Lighttable 3.5.90\n" +"PO-Revision-Date: 2014-09-25 21:48 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Lighttable" +msgstr "Lichttabel" + +#: .project:2 +msgid "A tool to sort photographs" +msgstr "Gereedschap om foto's mee te sorteren" + +#: FHelp.form:11 +msgid "Help" +msgstr "Help" + +#: FHelp.form:31 FInfo.form:17 +msgid "&Close" +msgstr "&Sluiten" + +#: FInfo.class:10 +msgid "There are no Exif informations in this file." +msgstr "Er is geen Exif informatie in dit bestand." + +#: FInfo.class:29 +msgid "&Less" +msgstr "&Minder" + +#: FInfo.form:11 +msgid "Picture Informations" +msgstr "Afbeeldingsinformatie" + +#: FInfo.form:24 +msgid "&More" +msgstr "&Meer" + +#: FMain.class:32 +msgid "Lighttable - " +msgstr "Lichttabel - " + +#: FMain.class:46 +msgid "Right-click for Main Menu" +msgstr "Rechts klikken voor Hoofdmenu" + +#: FMain.class:47 +msgid " pictures" +msgstr " afbeeldingen" + +#: FMain.class:48 +msgid "sorted alphabetically" +msgstr "alfabetisch gesorteerd" + +#: FMain.class:74 +msgid "Right-click on the background or on a frame for menus." +msgstr "Rechts klikken op achtergrond of frame voor menus." + +#: FMain.class:94 +msgid "Right-click for Picture Menu" +msgstr "Rechts klikken voor Afbeeldingsmenu" + +#: FMain.class:173 +msgid "&Rename all files..." +msgstr "&Hernoem alle bestanden..." + +#: FMain.class:268 +msgid "Loading picture..." +msgstr "Afbeelding laden..." + +#: FMain.class:343 +msgid "sorted chronologically" +msgstr "chronologisch gesorteerd" + +#: FMain.class:383 +msgid "The file &1 already exists in the current directory!" +msgstr "Het bestand &1 bestaat reeds in de huidige map!" + +#: FRenameAllWarning.form:17 FStart.form:26 FTime.form:51 +msgid "&Cancel" +msgstr "&Annuleer" + +#: FTime.form:45 +msgid "&OK" +msgstr "&OK" + +#: FMain.class:435 +msgid "The file &1 will be deleted." +msgstr "Het bestand &1 wordt verwijdert." + +#: FMain.class:727 +msgid "Loading of pictures is being aborted..." +msgstr "Laden van afbeeldingen wordt afgebroken..." + +#: FMain.class:778 +msgid "Files are being renamed..." +msgstr "Bestanden worden hernoemt..." + +#: FMain.class:807 +msgid "&1 files renamed" +msgstr "&1 bestanden hernoemt" + +#: FMain.class:823 +msgid "Setting time informations in all files..." +msgstr "Instellen van de tijd informatie in alle bestanden ..." + +#: FMain.form:44 +msgid "Main menu" +msgstr "Hoofdmenu" + +#: FMain.form:48 +msgid "Sorted &alphabetically" +msgstr "&Alfabetisch gesorteerd" + +#: FMain.form:55 +msgid "Sorted &chronologically" +msgstr "&Chronologisch gesorteerd" + +#: FMain.form:61 +msgid "&Slideshow" +msgstr "&Diavoorstelling" + +#: FMain.form:67 +msgid "Abort &Loading" +msgstr "Laden &Afbreken" + +#: FMain.form:73 +msgid "&Time correction..." +msgstr "&Tijd correctie..." + +#: FMain.form:79 +msgid "&Open folder..." +msgstr "&Open map..." + +#: FMain.form:85 +msgid "&Help" +msgstr "&Help" + +#: FMain.form:91 +msgid "&Quit" +msgstr "&Afsluiten" + +#: FMain.form:98 +msgid "Picture menu" +msgstr "Afbeeldingsmenu" + +#: FMain.form:102 +msgid "(Move picture: drag frame with mouse)" +msgstr "(Verplaats afbeelding: sleep frame met muis)" + +#: FMain.form:107 +msgid "&View picture (click on picture)" +msgstr "&Afbeelding weergeven (klik op afbeelding)" + +#: FMain.form:113 +msgid "&Full Screen View" +msgstr "&Volledig scherm weergave" + +#: FMain.form:119 +msgid "&Close view" +msgstr "&Sluit weergave" + +#: FMain.form:125 +msgid "&Next picture" +msgstr "&Volgende afbeelding" + +#: FMain.form:131 +msgid "&Previous picture" +msgstr "&Vorige afbeelding" + +#: FMain.form:137 +msgid "Picture &informations" +msgstr "Afbeeldings&informatie" + +#: FMain.form:143 +msgid "&Rename picture" +msgstr "&Hernoem afbeelding" + +#: FMain.form:149 +msgid "&Delete picture" +msgstr "&Verwijder afbeelding" + +#: FRename.class:43 +msgid "Old and new filename are identical." +msgstr "Oude en nieuwe bestandnaam zijn identiek." + +#: FRename.form:11 +msgid "Rename" +msgstr "Hernoem" + +#: FRename.form:17 +msgid "New filename:" +msgstr "Nieuwe bestandsnaam:" + +#: FRenameAll.class:11 +msgid "MyPicture.JPG" +msgstr "MijnAfbeelding.JPG" + +#: FRenameAll.form:22 +msgid "Rename all pictures" +msgstr "Hernoem alle afbeeldingen" + +#: FRenameAll.form:41 +msgid "1" +msgstr "-" + +#: FRenameAll.form:47 +msgid "Keep original filename as suffix" +msgstr "Bewaar originele bestandnaam as achtervoegsel" + +#: FRenameAll.form:53 +msgid "Number format" +msgstr "Nummerformaat" + +#: FRenameAll.form:58 +msgid "digits" +msgstr "cijfers" + +#: FRenameAll.form:63 +msgid "Prefix" +msgstr "Prefix" + +#: FRenameAll.form:68 +msgid "Start value" +msgstr "Aanvangswaarde" + +#: FRenameAll.form:88 +msgid "This function will rename all .jp(e)g files in the directory and add serial numbers to the filenames.
    The pictures will be handled in the order they are shown right now, in rows from left to right, rows from top to bottom." +msgstr "Deze functie hernoemt alle .jp(e)g bestanden in de map en voegt serieële nummers toe aan de bestandnamen.
    De afbeeldingen worden behandelt in de volgorde waarop nu weergegeven, in rijen van links naar rechts, rijen van boven naar beneden." + +#: FRenameAll.form:94 +msgid "Options" +msgstr "Opties" + +#: FRenameAll.form:99 +msgid "Example:" +msgstr "Voorbeeld:" + +#: FRenameAllWarning.form:12 +msgid "File Conflicts" +msgstr "Bestandsconflicten" + +#: FRenameAllWarning.form:24 +msgid "Continue &anyway" +msgstr "Tock &doorgaan" + +#: FRenameAllWarning.form:29 +msgid "The following files can't be renamed, because the target filenames already exist in the directory.
    If you continue, only the files without conflicts will be renamed.
    If you cancel, you can choose new filename options." +msgstr "De volgende bestanden kunnen niet hernoemt worden, omdat de doel bestandnamen reeds bestaan in de map.
    Indien je verder gaat worden enkel de bestand zonden conflicten hernoemt.
    Indien je annuleert, kun je nieuwe bestandnamen kiezen." + +#: FSlideshow.form:38 +msgid "Pause between pictures:" +msgstr "Pause tussen afbeeldingen:" + +#: FSlideshow.form:49 +msgid "sec" +msgstr "sec" + +#: FSlideshow.form:54 +msgid "Stop" +msgstr "Stop" + +#: FStart.class:54 +msgid "The folder &1 doesn't contain any jp(e)g files." +msgstr "De map &1 bevat geen jp(e)g bestanden." + +#: FStart.form:15 +msgid "Lighttable - Select picture folder" +msgstr "Lichttabel - Selecteer afbeeldingsmap" + +#: FStart.form:41 +msgid "New selection" +msgstr "Nieuwe selectie" + +#: FStart.form:51 +msgid "Last selections" +msgstr "Laatste selecties" + +#: FTime.class:18 +msgid "There is no time correction selected." +msgstr "Er is geen tijdscorrectie geselecteerd." + +#: FTime.form:18 +msgid "Time correction" +msgstr "Tijdscorrectie" + +#: FTime.form:39 +msgid "earlier" +msgstr "eerder" + +#: FTime.form:39 +msgid "later" +msgstr "-" + +#: FTime.form:59 +msgid "This function will correct all date/time informations in the EXIF section of all .jp(e)g files in the folder.
    A backup of the files will be made with file names like 'MyPicture.jpg_original'.
    If there are no EXIF date/time informations in a file, nothing will be done." +msgstr "Deze functie zal alle datum/tijd informatie in de EXIF sectie van alle .jp(e)g bestanden in de map corrigeren.
    Een backup van de bestanden zal worden gemaakt met bestandnamen zoals 'MijnAfbeelding.jpg_original'.
    Indien er geen EXIF datum/tijd informatie beschikbaar is in het bestand, wordt er niets gedaan." + +#: FTime.form:65 +msgid "Set the original time of all pictures to" +msgstr "Stel de oorspronkelijke tijd van alle afbeeldingen in" + +#: FTime.form:70 +msgid "day(s)" +msgstr "dag(en)" + +#: FTime.form:75 +msgid "hour(s)" +msgstr "U(u)r(en)" + +#: FTime.form:80 +msgid "minute(s)" +msgstr "minu(u)t(en)" + +#: MMain.module:12 +msgid "To run this program, exiftool must be installed." +msgstr "Op dit programma uit te voeren dient exiftool geïnstalleert te zijn." + +#: MMain.module:18 +msgid "To run this program, convert must be installed." +msgstr "Op dit programma uit te voeren dient convert geïnstalleert te zijn." + diff --git a/app/examples/Image/Lighttable/.project b/app/examples/Image/Lighttable/.project new file mode 100644 index 00000000..6207ad36 --- /dev/null +++ b/app/examples/Image/Lighttable/.project @@ -0,0 +1,27 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.4.0 +Title=Lighttable +Startup=MMain +Icon=lighttable.png +Version=3.6.2 +Component=gb.image +Component=gb.gui.qt +Component=gb.form +Component=gb.form.dialog +Component=gb.settings +Component=gb.gui.qt.webkit +Description="A tool to sort photographs" +Authors="Matti, with tips and some changes from Benoît" +TabSize=2 +Translate=1 +Language=en +ExecPath=/home/mathias/aaaTemp/Lighttable.gambas +SourcePath=/home/mathias/Basic +Maintainer=mathias +Address=mathias@mattitux +License=General Public Licence +Packager=1 +Systems=suse +Menus=suse:"Graphics/Photograph" +Categories=suse:"Graphics" +Groups=suse:"Productivity/Graphics/Viewers" diff --git a/app/examples/Image/Lighttable/.src/FHelp.class b/app/examples/Image/Lighttable/.src/FHelp.class new file mode 100644 index 00000000..3c836336 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FHelp.class @@ -0,0 +1,28 @@ +' Gambas class file + +Public Sub _new() + + Dim sLanguage As String + Dim sPath As String + Dim sDest As String + + sLanguage = Left$(System.Language, 2) + sPath = "Help_" & sLanguage & ".html" + sDest = File.SetExt(Temp$("help"), "html") + Try Copy sPath To sDest + + If Error Then + sPath = "Help_en.html" + sDest = File.SetExt(Temp$("help"), "html") + Try Copy sPath To sDest + Endif + + WebViewHelp.Url = sDest + +End + +Public Sub btnClose_Click() + + Me.Close + +End diff --git a/app/examples/Image/Lighttable/.src/FHelp.form b/app/examples/Image/Lighttable/.src/FHelp.form new file mode 100644 index 00000000..588decc0 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FHelp.form @@ -0,0 +1,26 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,84,73) + Text = ("Help") + Arrangement = Arrange.Vertical + { WebViewHelp WebView + MoveScaled(2,1,80,64) + Expand = True + } + { Panel1 HBox + MoveScaled(2,66,80,6) + Background = Color.TextBackground + Margin = True + { Panel2 Panel + MoveScaled(0,1,16,2) + Expand = True + } + { btnClose Button + MoveScaled(64,1,15,4) + Text = ("&Close") + Default = True + Cancel = True + } + } +} diff --git a/app/examples/Image/Lighttable/.src/FInfo.class b/app/examples/Image/Lighttable/.src/FInfo.class new file mode 100644 index 00000000..7d6c1424 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FInfo.class @@ -0,0 +1,37 @@ +' Gambas class file + +sCommon As String = "" +sDetail As String = "" + +Public Sub Form_Open() + + sCommon = FMain.GetExifInfoCommon(Me.Tag) + If sCommon = "" Then + Message.Info(("There are no Exif informations in this file.")) + Me.Close + Endif + txlExif.Text = sCommon + +End + +Public Sub btnClose_Click() + + Me.Close + +End + +Public Sub btnDetails_Click() + + If btnDetails.Text = ("&More") Then + If sDetail = "" Then sDetail = FMain.GetExifInfoAll(Me.Tag) + txlExif.Text = sDetail + txlExif.Adjust + btnDetails.Text = ("&Less") + Else + txlExif.Text = sCommon + txlExif.Width = 567 + txlExif.Height = 259 + btnDetails.Text = ("&More") + Endif + +End diff --git a/app/examples/Image/Lighttable/.src/FInfo.form b/app/examples/Image/Lighttable/.src/FInfo.form new file mode 100644 index 00000000..8cda99a1 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FInfo.form @@ -0,0 +1,25 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,84,50) + Text = ("Picture Informations") + Resizable = False + Utility = True + { btnClose Button + MoveScaled(66,44,15,4) + Text = ("&Close") + Default = True + Cancel = True + } + { btnDetails Button + MoveScaled(50,44,15,4) + Text = ("&More") + } + { ScrollView1 ScrollView + MoveScaled(1,2,82,41) + { txlExif TextLabel + MoveScaled(0,0,81,37) + Background = &HFFFCCC& + } + } +} diff --git a/app/examples/Image/Lighttable/.src/FMain.class b/app/examples/Image/Lighttable/.src/FMain.class new file mode 100644 index 00000000..a832e8c2 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FMain.class @@ -0,0 +1,903 @@ +' Gambas class file + +Private aFiles As String[] +Private aPFiles As New String[] +Private aThumbs As New String[] +Private aTime As New String[] +Private aSort As New String[] +Private aFrame As New Object[] +Private aPicture As New Object[] +Private aOri As New String[] +Private alblFile As New Object[] +Private alblTime As New Object[] +Private bStop As Boolean = False +Private hCurClose As Cursor +Private hCurHand As Cursor +Private hCurZoom As Cursor +Private iDelay As Integer +Private iPicCount As Integer +Private iPicRow As Integer +Private iRow As Integer +Private iOffset As Integer +Private iMark As Integer +Private iNr As Integer +Private sPath As String + +Public Sub Form_Open() + + sPath = Me.Tag + aFiles = MMain.GetFiles() + iPicCount = aFiles.Count + + Me.Caption = ("Lighttable - ") & sPath + Me.Move(Desktop.X, Desktop.Y, Desktop.Width, Desktop.Height) + + mnuSortA.Enabled = False + mnuSortT.Enabled = False + mnuDelete.Enabled = False + mnuTimecorr.Enabled = False + mnuSlide.Enabled = False + mnuOpen.Enabled = False + hCurClose = New Cursor(Picture.Load("close.png")) + hCurHand = New Cursor(Picture.Load("hand1.png")) + hCurZoom = New Cursor(Picture.Load("zoom-in.png")) + PicBox.Mouse = -2 + PicBox.Cursor = hCurZoom + Me.Tooltip = ("Right-click for Main Menu") + lblCount.Text = iPicCount & (" pictures") + lblSort.Text = ("sorted alphabetically") + aPFiles.Resize(iPicCount) + aThumbs.Resize(iPicCount) + aOri.Resize(iPicCount) + aTime.Resize(iPicCount) + aFrame.Resize(iPicCount + 1) + alblFile.Resize(iPicCount) + alblTime.Resize(iPicCount) + aPicture.Resize(iPicCount) + + Timer1.Trigger ' Finish this sub, so the user can do something + +End + +Public Sub Timer1_Timer() ' Load the pictures in the background + + Dim PicInfo As String + Dim i As Integer + Dim hImg As Image + + If iPicRow = 0 Then +' Print Me.Width + iPicRow = Int((Me.Width) / 200) + iOffset = (Me.Width + 30 - iPicRow * 200) / 2 + Endif + + lblStatus.Text = ("Right-click on the background or on a frame for menus.") + For i = 0 To iPicCount - 1 + aPFiles[i] = sPath & "/" & aFiles[i] + aThumbs[i] = Thumb(aFiles[i]) + Shell "exiftool -b -orientation -createdate '" & aPFiles[i] & "'" To PicInfo ' Get orientation and create time of the picture + aOri[i] = Left$(PicInfo, 1) + aTime[i] = Right$(PicInfo, 19) + + aFrame[i] = New Panel(ScrollView1) As "Framegroup" ' Make frame + With aFrame[i] + .Width = 140 + .Height = 175 + .Border = 3 + .Background = &CFCFCF& + .Tag = i + .Mouse = -2 + .Cursor = hCurHand + iRow = Int((i) / iPicRow) + .X = (i - (iPicRow * iRow)) * 200 + iOffset + .Y = iRow * 210 + 30 + .Tooltip = ("Right-click for Picture Menu") + End With + + alblFile[i] = New Label(aFrame[i]) As "Framegroup" ' Label filename + With alblFile[i] + .Width = 130 + .Height = 16 + .X = 5 + .Y = 140 + .Font.Grade = -1 + .Alignment = 3 + .Tag = i + .Text = aFiles[i] + If Len(.Text) > 22 Then + .Alignment = 1 + .ToolTip = .Text + Endif + End With + + alblTime[i] = New Label(aFrame[i]) As "Framegroup" ' Label Timestamp + With alblTime[i] + .Width = 130 + .Height = 16 + .X = 5 + .Y = 155 + .Font.Grade = -1 + .Alignment = 3 + .Tag = i + .Text = aTime[i] + End With + If i = 0 Then + iNr = 0 + Mark_Frame(iNr) + Endif + + aPicture[i] = New PictureBox(aFrame[i]) As "Thumbnail" ' Thumbnail + With aPicture[i] + .Mouse = -2 + .Cursor = hCurZoom + .Tag = i + .Alignment = 3 + .X = 5 + .Y = 5 + .Width = 128 + .Height = 128 + + Try hImg = Image.Load(aThumbs[i]) ' Image, because it might have to be rotated + If Error Then ' If thumbnail doesn't exist, it has to be created + Shell "convert -define jpeg:size=150x150 '" & aPFiles[i] & "' -auto-orient -thumbnail 128x128 '" & aThumbs[i] & "'" Wait + hImg = Image.Load(aThumbs[i]) + Endif + + If aOri[i] > "1" And hImg.Width > hImg.Height Then ' If a system's thumbnail isn't rotated correctly + If aOri[i] = 6 Then + hImg = hImg.Rotate(Rad(-90)) + Else If aOri[i] = 8 Then + hImg = hImg.Rotate(Rad(90)) + Endif + Endif + .Picture = hImg.Picture + End With + + ProgressBar1.Value = (i + 1) / iPicCount + If bStop = True Then 'if loading is stopped + iPicCount = i + 1 + lblCount.Text = iPicCount & (" pictures") + Break + Endif + Wait 0.01 + Next + + iRow = Int(iPicCount / iPicRow) + 1 + panEmpty.Y = iRow * 210 ' Insert an empty panel to get some space at the bottom + ProgressBar1.Visible = False + lblStatus.Text = "" + mnuSortA.Enabled = False + mnuSortT.Enabled = True + mnuDelete.Enabled = True + mnuTimecorr.Enabled = True + mnu_StopLoad_RenameAll.Caption = ("&Rename all files...") + mnu_StopLoad_RenameAll.Picture = Stock["16/save-as"] + mnu_StopLoad_RenameAll.Shortcut = "Ctrl+N" + mnuSlide.Enabled = True + mnuOpen.Enabled = True + bStop = True + Me.Tag = "alpha" + Me.Tooltip = "" + For i = 0 To iPicCount - 1 + aFrame[i].Tooltip = "" + Next + +End + +Public Sub Framegroup_MouseDown() ' a frame is clicked + + iNr = Last.Tag + Mark_Frame(iNr) + If PicBox.Visible = True And PicBox.Tag <> aFiles[iNr] Then + If PicBox.Cursor = hCurClose Then + mnuFullscreen_Click + Else + mnuView_Click + Endif + Endif + +End + +Public Sub Framegroup_MouseMove() ' Move a frame + + iNr = Last.Tag + PicBox.Visible = False + FInfo.Close + aFrame[iNr].X += Mouse.X - Mouse.StartX + aFrame[iNr].Y += Mouse.Y - Mouse.StartY + If aFrame[iNr].Y < ScrollView1.ScrollY Then + ScrollView1.Scroll(ScrollView1.ScrollX, ScrollView1.ScrollY - 10) + Endif + If aFrame[iNr].Y > Me.Height + ScrollView1.ScrollY - 175 Then + ScrollView1.Scroll(ScrollView1.ScrollX, ScrollView1.ScrollY + 10) + Endif + Mark_Frame(iNr) + +End + +Public Sub Framegroup_Menu() ' Context menu of the frames + + iNr = Last.Tag + FInfo.Close + mnuDrag.Enabled = True + mnuView.Enabled = True + mnuESC.Enabled = False + mnuFramegroup.Popup + +End + +Public Sub Thumbnail_MouseDown() ' a thumbnail is clicked + + iNr = Last.Tag + FInfo.Close + Framegroup_MouseDown + If Mouse.Left Then ' view picture + If PicBox.Visible = False Then mnuView_Click + Else ' Show menu + mnuFramegroup.Popup + Endif + +End + +Public Sub mnuView_Click() ' Show picture + + Dim iFormat As Single + + Load_Picture + iFormat = aPicture[iNr].Picture.Width / aPicture[iNr].Picture.Height + If iFormat > 1 Then ' Landscape + PicBox.Width = 800 + PicBox.Height = 800 / iFormat + Else ' Portrait + PicBox.Height = 800 + PicBox.Width = 800 * iFormat + Endif + PicBox.X = (Me.Width - PicBox.Width) / 2 ' center picture + PicBox.Y = (Me.Height - PicBox.Height) / 2 + mnuView.Enabled = False + mnuESC.Enabled = True + PicBox.Visible = True + PicBox.SetFocus + +End + +Public Sub Load_Picture() + + Dim hImg As Image + + lblStatus.Text = ("Loading picture...") + Wait + hImg = Image.Load(aPFiles[iNr]) ' Image, because it might have to be rotated + If aOri[iNr] = 6 Then + hImg = hImg.Rotate(Rad(-90)) + Else If aOri[iNr] = 8 Then + hImg = hImg.Rotate(Rad(90)) + Endif + PicBox.Picture = hImg.Picture + PicBox.Tag = aFiles[iNr] + lblStatus.Text = "" + Wait + +End + +Public Sub Form_Menu() ' Main context menu + + PicBox.Visible = False + FInfo.Close + mnuMain.Popup + +End + +Public Sub mnuSortA_Click() ' Sort pictures alphabetically by file names + + Dim i, pos As Integer + Dim n As String + + PicBox.Visible = False + FInfo.Close + aSort.Resize(iPicCount) + For i = 0 To iPicCount - 1 + aSort[i] = aFiles[i] & "/" & i ' Workaround. A 2-dim array would be correct, but I can't sort it... + Next + aSort.Sort(1) + + For i = 0 To iPicCount - 1 + pos = InStr(aSort[i], "/") + n = Right$(aSort[i], Len(aSort[i]) - pos) + iRow = Int((i) / iPicRow) + aFrame[n].X = (i - (iPicRow * iRow)) * 200 + iOffset + aFrame[n].Y = iRow * 210 + 30 + Next + mnuSortT.Checked = False + mnuSortT.Enabled = True + mnuSortA.Checked = True + mnuSortA.Enabled = False + lblSort.Text = ("sorted alphabetically") + Me.Tag = "alpha" + Show_marked_frame + +End + +Public Sub mnuSortT_Click() ' Sort pictures chronologically by timestamp + + Dim i, pos As Integer + Dim n As String + + aSort.Resize(iPicCount) + For i = 0 To iPicCount - 1 + aSort[i] = aTime[i] & "/" & i ' Workaround. A 2-dim array would be correct, but I can't sort it... + Next + aSort.Sort() + + For i = 0 To iPicCount - 1 + pos = InStr(aSort[i], "/") + n = Right$(aSort[i], Len(aSort[i]) - pos) + iRow = Int((i) / iPicRow) + aFrame[n].X = (i - (iPicRow * iRow)) * 200 + iOffset + aFrame[n].Y = iRow * 210 + 30 + Next + mnuSortA.Checked = False + mnuSortA.Enabled = True + mnuSortT.Checked = True + mnuSortT.Enabled = False + lblSort.Text = ("sorted chronologically") + Me.Tag = "chron" + Show_marked_frame + +End + +Public Sub Show_marked_frame() + + If iMark > -1 Then ' Only if a picture is selected + If ScrollView1.ScrollY > (aFrame[iMark].Y - 30) Then ' If necessary, scroll up + ScrollView1.Scroll(0, (aFrame[iMark].Y - 30)) + Return + Endif + If ScrollView1.ScrollY + Me.Height < aFrame[iMark].Y + 210 Then ' If necessary, scroll down + ScrollView1.Scroll(0, aFrame[iMark].Y + 230 - Me.Height) + Endif + Endif + +End + +Public Sub mnuRename_Click() ' Show the form FRename (the renaming itself is done in the function FileRename) + + If iMark = -1 Then Return ' Only if a picture is selected + PicBox.Visible = False + FInfo.Close + mnuESC.Enabled = False + mnuView.Enabled = True + mnuFullscreen.Enabled = True + PicBox.Cursor = hCurZoom + FRename.Tag = aFiles[iNr] + FRename.ShowModal + +End + +Public Function TestRenameOne(sOldname As String, sNewname As String) As Boolean + + Dim n As Integer + + For n = 0 To iPicCount - 1 ' Check if new name already exists + If aFiles[n] = sNewname Then + Message.Error(Subst(("The file &1 already exists in the current directory!"), sNewname)) + Return False ' Give error back to FRename + Endif + Next + Return True + +End + +Public Function FileRename(sOldname As String, sNewname As String) As Boolean ' Rename a file + + Dim sThumbname As String + + Try Move sPath & "/" & sOldname To sPath & "/" & sNewname ' Rename file + If Error Then Return False + aFiles[iNr] = sNewname ' Rename all entries in arrays and labels + alblFile[iNr].Text = sNewname + alblFile[iNr].ToolTip = "" + alblFile[iNr].Alignment = 3 + If Len(sNewname) > 20 Then + alblFile[iNr].Alignment = 1 + alblFile[iNr].ToolTip = sNewname + Endif + Wait + aPFiles[iNr] = sPath & "/" & sNewname + sThumbname = Thumb(aFiles[iNr]) ' Rename thumbnail + Try Move aThumbs[iNr] To sThumbname + If Error Then + Kill sThumbname + Wait + Move aThumbs[iNr] To sThumbname + Endif + aThumbs[iNr] = sThumbname + Return True ' give ok back + +End + +Public Sub mnuDelete_Click() ' Delete a picture + + Dim i, x, y As Integer + Dim sPicState As String + + If iMark = -1 Then Return ' Only if a picture is selected + FInfo.Close + If PicBox.Visible = True Then ' Remember the state of PictureBox + If PicBox.Cursor = hCurZoom Then + sPicState = "on" + Else + sPicState = "full" + Endif + Else + sPicState = "off" + Endif + If Message.Warning(Subst(("The file &1 will be deleted."), aFiles[iNr]), ("&OK"), ("&Cancel")) = 2 Then Return ' If user aborts, cancel + + Try Shell "kioclient move '" & aPFiles[iNr] & "' trash:/" ' Move file to trash - kioclient preferred, because it writes the restore infos + If Error Then ' Otherwise move directly to trash + Move aPFiles[iNr] To "trash:/" + Endif + Try Shell "kioclient move '" & aThumbs[iNr] & "' trash:/" ' Move Thumbnail to trash + If Error Then + Move aThumbs[iNr] To "trash:/" + Endif + + x = aFrame[iNr].X ' Remember position of deleted frame + y = aFrame[iNr].Y + iPicCount = iPicCount - 1 + lblCount.Text = iPicCount & (" pictures") + + aFrame[iNr].Delete ' Remove picture + aFiles.Remove(iNr) ' Delete all array-elements, arrays are 1 smaller + aPFiles.Remove(iNr) + aThumbs.Remove(iNr) + aTime.Remove(iNr) + aFrame.Remove(iNr) + aPicture.Remove(iNr) + alblFile.Remove(iNr) + alblTime.Remove(iNr) + aOri.Remove(iNr) + + For i = iNr To iPicCount - 1 ' Set the tags of all next frames in the arrays -1 + aFrame[i].Tag = i + aPicture[i].Tag = i + alblFile[i].Tag = i + alblTime[i].Tag = i + Next + + If FindNextFrame(x, y) = False Then + iMark = -1 + PicBox.Visible = False + mnuFullscreen.Enabled = True + Return + Endif + mnuView.Enabled = True + mnuFullscreen.Enabled = True + Mark_Frame(iNr) + Show_marked_frame + Select Case sPicState ' Show PictureBox again with next picture + Case "on" + mnuView_Click + Case "full" + mnuFullscreen_Click + End Select +End + +Public Sub mnuInfo_Click() ' Show picture infos + + Dim posx, posy As Integer + + If iMark = -1 Then Return + posx = aFrame[iNr].X + posy = aFrame[iNr].Y - ScrollView1.ScrollY + If posx < Me.Width - 740 Then ' Calculate position for FInfo + FInfo.X = posx + 145 + Else + FInfo.X = posx - 595 + Endif + If posy < Me.Height - 350 Then + FInfo.Y = posy + 24 + Else + FInfo.Y = posy - 180 + Endif + FInfo.Tag = iNr + FInfo.Show + +End + +Public Sub GetExifInfoCommon(iNumber As Integer) As String + + Dim sExif As String + + Shell "exiftool -common -h '" & aPFiles[iNumber] & "'" To sExif + Return sExif + +End + +Public Sub GetExifInfoAll(iNumber As Integer) As String + + Dim sExif As String + + Shell "exiftool -a -h '" & aPFiles[iNumber] & "'" To sExif + Return sExif + +End + +Public Sub mnuHelp_Click() ' Show help + + FHelp.X = (Me.Width - FHelp.Width) / 2 + FHelp.Show + +End + +Public Sub PicBox_MouseDown() ' Click on PictureBox + + If Mouse.Left = True Then + If PicBox.Cursor = hCurZoom Then ' if smaller picture + mnuFullscreen_Click + Else ' if full screen + BackToSmallView + Endif + Else ' right-klick + mnuDrag.Enabled = False + mnuFramegroup.Popup + Endif + +End + +Public Sub mnuFullscreen_Click() ' Full screen view (we ignore that a picture might be smaller) + + Dim iFormat As Single + + If PicBox.Visible = False Or PicBox.Tag <> aFiles[iNr] Then + Load_Picture + Endif + iFormat = aPicture[iNr].Picture.Width / aPicture[iNr].Picture.Height + PicBox.Y = 0 + PicBox.Height = Me.Height + PicBox.Width = Me.Height * iFormat + If PicBox.Width > Me.Width Then ' If picture format is 16:9 or wider + PicBox.Width = Me.Width + PicBox.Height = Me.Width / iFormat + Endif + PicBox.X = (Me.Width - PicBox.Width) / 2 + PicBox.Visible = True + mnuFullscreen.Enabled = False + mnuView.Enabled = False + mnuESC.Enabled = True + PicBox.Cursor = hCurClose + PicBox.SetFocus + +End + +Public Sub BackToSmallView() ' Close FullScreen view + + Dim w, h As Integer + + w = PicBox.Width + h = PicBox.Height + If w / h > 1 Then + PicBox.Width = 800 + PicBox.Height = 800 / w * h + Else + PicBox.Width = 800 * w / h + PicBox.Height = 800 + Endif + PicBox.X = (Me.Width - PicBox.Width) / 2 ' centered + PicBox.y = (Me.Height - PicBox.Height) / 2 + PicBox.Cursor = hCurZoom + mnuView.Enabled = False + mnuFullscreen.Enabled = True + mnuESC.Enabled = True + +End + +Public Sub mnuESC_Click() ' ESC is pressed + + If PicBox.Visible = True Then + If PicBox.Cursor = hCurClose Then ' we have full screen + BackToSmallView + Else ' Close small view + PicBox.Visible = False + mnuView.Enabled = True + mnuFullscreen.Enabled = True + mnuESC.Enabled = False + aFrame[iNr].SetFocus + Endif + Endif + +End + +Public Sub mnuNext_Click() ' Move to next picture + + If iMark = -1 Then Return ' Only if a picture is selected + If FindNextFrame(aFrame[iNr].X, aFrame[iNr].Y) = False Then Return ' if no next picture is found, exit + Mark_Frame(iNr) + Show_marked_frame + If PicBox.Visible = True Then + If PicBox.Cursor = hCurClose Then ' if fullscreen + mnuFullscreen_Click + Else + mnuView_Click + Endif + Endif + +End + +Public Function FindNextFrame(x As Integer, y As Integer) As Boolean + + Dim i As Integer + + Do + x = x + 70 + If x > (ScrollView1.ScrollWidth - 150) Then ' if we are at the end of a line, go to next line + x = 0 + y = y + 210 + Endif + If y > panEmpty.Y + 200 Then Break ' if we are in last line, end + For i = 0 To iPicCount - 1 + If aFrame[i].X > x And aFrame[i].X < x + 140 And aFrame[i].Y > y - 100 And aFrame[i].Y < y + 100 Then + iNr = i + Return True ' found + Endif + Next + Loop + Return False 'none found + +End + +Public Sub mnuPrevious_Click() ' Move to previous picture + + Dim x, y, i As Integer + + If iMark = -1 Then Return ' Only if a picture is selected + x = aFrame[iNr].X + y = aFrame[iNr].Y + Do + x = x - 70 + If x < 70 Then ' if we are at the beginning of a line, go to end of previous line + x = Me.Width + y = y - 210 + Endif + If y < 0 Then Break ' if we are at top, end + For i = 0 To iPicCount - 1 + If aFrame[i].X < x And aFrame[i].X > x - 140 And aFrame[i].Y > y - 100 And aFrame[i].Y < y + 100 Then + iNr = i + Mark_Frame(iNr) + Show_marked_frame + If PicBox.Visible = True Then + If PicBox.Cursor = hCurClose Then 'fullscreen + mnuFullscreen_Click + Else + mnuView_Click + Endif + Endif + Goto found + Endif + Next + Loop + found: + +End + +Public Sub Mark_Frame(iNumber As Integer) 'Mark selected frame + + If iMark > -1 Then + aFrame[iMark].Background = &CFCFCF& + alblFile[iMark].Foreground = &000000& + alblTime[iMark].Foreground = &000000& + Endif + aFrame[iNumber].Background = &0000FF& + alblFile[iNumber].Foreground = &FFFFFF& + alblTime[iNumber].Foreground = &FFFFFF& + aFrame[iNumber].Raise + iMark = iNumber + If FInfo.Visible = True Then + FInfo.Close + mnuInfo_Click + Endif + +End + +Public Function Thumb(sFilename As String) As String ' Get the file name for thumbnail using md5sum + + Dim sThumb As String + Dim i As Integer + + sFilename = sPath & "/" & sFilename + sFilename = Replace$(sFilename, " ", "%20") ' we have to replace spaces and special characters + For i = 123 To 255 + sFilename = Replace$(sFilename, Chr(i), "%" & Hex$(i)) + Next + sFilename = "file://" & sFilename + Shell "echo -n '" & sFilename & "' | md5sum" To sThumb + sThumb = Left$(sThumb, 32) + sThumb = User.Home & "/.thumbnails/normal/" & sThumb & ".png" + Return sThumb + +End + +Public Sub mnu_StopLoad_RenameAll_Click() 'Abort Loading or Rename all + + PicBox.Visible = False + FInfo.Close + + If bStop = False Then ' abort + lblStatus.Text = ("Loading of pictures is being aborted...") + Wait + bStop = True + Return + Endif + FRenameAll.ShowModal ' rename + +End + +Public Function TestRenameAll(sPref As String, sFmt As String, iStart As Integer, bKeep As Boolean) As String ' Check if target filenames already exist + + Dim x, y, i, n As Integer + Dim sResult As String = "" + Dim aOldname As New String[iPicCount] + Dim aNewname As New String[iPicCount] + + x = -60 + y = 100 + i = iStart + n = 0 + Do While FindNextFrame(x, y) + aOldname[n] = aFiles[iNr] + aNewname[n] = sPref & Format(i, sFmt) + If bKeep = True Then ' keep old filename + aNewname[n] = aNewname[n] & "_" & aOldname[n] + Else ' just number + aNewname[n] = aNewname[n] & ".jpg" + Endif + x = aFrame[iNr].X + y = aFrame[iNr].Y + n = n + 1 + i = i + 1 + Loop + Mark_Frame(iNr) + Show_marked_frame + For i = 0 To iPicCount - 1 ' Check if there would be a conflict with existing filenames + For n = i + 1 To iPicCount - 1 + If aNewname[i] = aOldname[n] Then + sResult = sResult & aOldname[i] & " --> " & aOldname[n] & "\n" + Endif + Next + Next + Return sResult + +End + +Public Function RenameAll(sPref As String, sFmt As String, iStart As Integer, bKeep As Boolean) As Boolean ' Rename all files + + Dim x, y, n, i As Integer + Dim oldname, newname As String + + lblStatus.Text = ("Files are being renamed...") + Wait + x = -60 + y = 100 + n = 0 + i = iStart + + Do While FindNextFrame(x, y) + oldname = aFiles[iNr] + newname = sPref & Format(i, sFmt) + If bKeep = True Then ' keep old filename + newname = newname & "_" & oldname + Else ' just number + newname = newname & ".jpg" + Endif + If FileRename(oldname, newname) = False Then + n = n - 1 + Endif + x = aFrame[iNr].X + y = aFrame[iNr].Y + n = n + 1 + i = i + 1 + Wait + Loop + + Mark_Frame(iNr) + Show_marked_frame + lblStatus.Text = "" + Dec Application.Busy + Message.Info(Subst(("&1 files renamed"), n)) + +End + +Public Sub mnuTimecorr_Click() ' Time correction + + FTime.Tag = sPath + FTime.ShowModal + +End + +Public Sub TimeCorrection(idays As Integer, ihours As Integer, iminutes As Integer, PlusMinus As String) + + Dim i As Integer + Dim Hrs As Single + + lblStatus.Text = ("Setting time informations in all files...") + Wait + If PlusMinus = "0" Then + PlusMinus = "-" + Else + PlusMinus = "+" + Endif + Hrs = idays * 24 + ihours + iminutes / 60 + + Shell "exiftool -alldates" & PlusMinus & "=" & Hrs & " '" & sPath & "'" Wait + For i = 0 To iPicCount - 1 + Shell "exiftool -b -createdate '" & aPFiles[i] & "'" To alblTime[i].Text + Next + lblStatus.Text = "" + +End + +Public Sub mnuSlide_Click() ' Slide Show + + Dim i, iLast As Integer + + iLast = iNr + FSlideshow.Show + + Do + FSlideshow.PictureLoad(aPFiles[iNr], aOri[iNr]) + FSlideshow.Title = aFiles[iNr] + i = 1 + Do + If i > iDelay Then Break + If FSlideshow.Visible = False Or FSlideshow.Minimized = True Then Break + Wait 1 + Inc i + Loop + + If FSlideshow.Visible = False Or FSlideshow.Minimized = True Then Break + mnuNext_Click + If iNr = iLast Then Break + iLast = iNr + Loop + + FSlideshow.Close + Settings["SlideShow/Delay"] = iDelay + +End + +Public Function GetDelay(iSec As Integer) As Boolean + + iDelay = iSec + +End + +Public Sub mnuOpen_Click() ' Open another folder + + MMain.Main + Me.Close + +End + +Public Sub mnuQuit_Click() ' Quit + + Me.Close + +End + +Public Sub Form_Close() + + If bStop = False Then ' if loading of pictures is still in progress + bStop = True + Stop Event + Wait + Timer2.Enabled = True + Endif + +End + +Public Sub Timer2_Timer() + + Me.Close + +End diff --git a/app/examples/Image/Lighttable/.src/FMain.form b/app/examples/Image/Lighttable/.src/FMain.form new file mode 100644 index 00000000..41224a39 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FMain.form @@ -0,0 +1,162 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,116,65) + Background = Color.Background + Icon = Picture["LTicon.png"] + Arrangement = Arrange.Vertical + { mnuMain Menu + Text = ("Main menu") + Visible = False + { mnuSortA Menu + Text = ("Sorted &alphabetically") + Picture = Picture["icon:/16/sort-ascent"] + Checked = True + Shortcut = "Ctrl+A" + } + { mnuSortT Menu + Text = ("Sorted &chronologically") + Picture = Picture["icon:/16/clock"] + Shortcut = "Ctrl+C" + } + { mnuSlide Menu + Text = ("&Slideshow") + Picture = Picture["icon:/16/video"] + Shortcut = "Ctrl+S" + } + { mnu_StopLoad_RenameAll Menu + Text = ("Abort &Loading") + Picture = Picture["icon:/16/cancel"] + Shortcut = "End" + } + { mnuTimecorr Menu + Text = ("&Time correction...") + Picture = Picture["icon:/16/camera"] + Shortcut = "Ctrl+T" + } + { mnuOpen Menu + Text = ("&Open folder...") + Picture = Picture["icon:/16/open"] + Shortcut = "Ctrl+O" + } + { mnuHelp Menu + Text = ("&Help") + Picture = Picture["help-contents.png"] + Shortcut = "F1" + } + { mnuQuit Menu + Text = ("&Quit") + Picture = Picture["icon:/16/quit"] + Shortcut = "Ctrl+End" + } + } + { mnuFramegroup Menu + Text = ("Picture menu") + Visible = False + { mnuDrag Menu + Text = ("(Move picture: drag frame with mouse)") + Picture = Picture["move.png"] + } + { mnuView Menu + Text = ("&View picture (click on picture)") + Picture = Picture["icon:/16/zoom-in"] + Shortcut = "Enter" + } + { mnuFullscreen Menu + Text = ("&Full Screen View") + Picture = Picture["icon:/16/fullscreen"] + Shortcut = "Alt+Enter" + } + { mnuESC Menu + Text = ("&Close view") + Picture = Picture["icon:/16/close"] + Shortcut = "Esc" + } + { mnuNext Menu + Text = ("&Next picture") + Picture = Picture["icon:/16/next"] + Shortcut = "Right" + } + { mnuPrevious Menu + Text = ("&Previous picture") + Picture = Picture["icon:/16/previous"] + Shortcut = "Left" + } + { mnuInfo Menu + Text = ("Picture &informations") + Picture = Picture["icon:/16/info"] + Shortcut = "I" + } + { mnuRename Menu + Text = ("&Rename picture") + Picture = Picture["icon:/16/save-as"] + Shortcut = "N" + } + { mnuDelete Menu + Text = ("&Delete picture") + Picture = Picture["icon:/16/delete"] + Shortcut = "Del" + } + } + { ScrollView1 ScrollView + MoveScaled(27,13,41,32) + Background = Color.TextBackground + Expand = True + Border = False + { panEmpty Panel + MoveScaled(4,10,16,16) + } + } + { Timer1 #Timer + #MoveScaled(105,22) + Delay = 10 + } + { Timer2 #Timer + #MoveScaled(105,30) + Delay = 100 + } + { Separator1 Separator + MoveScaled(27,56,14,0) + } + { StatusBar Panel + MoveScaled(2,60,99,4) + Arrangement = Arrange.Horizontal + { lblCount Label + MoveScaled(1,0.5714,13,3) + Font = Font["-1"] + Alignment = Align.Center + Border = Border.Sunken + } + { lblSort Label + MoveScaled(15,0.5714,29,3) + Font = Font["-1"] + Alignment = Align.Center + Border = Border.Sunken + } + { Panel1 Panel + MoveScaled(45,0,1,4) + } + { lblStatus Label + MoveScaled(47,0,27,4) + Font = Font["-1"] + Expand = True + Alignment = Align.Center + } + { Panel2 Panel + MoveScaled(75,0,22,4) + Arrangement = Arrange.Fill + Margin = True + Padding = 4 + { ProgressBar1 ProgressBar + MoveScaled(0,0,20,3) + } + } + } + { PicBox PictureBox + MoveScaled(7,37,9,9) + Visible = False + Ignore = True + Stretch = True + Border = Border.Etched + } +} diff --git a/app/examples/Image/Lighttable/.src/FRename.class b/app/examples/Image/Lighttable/.src/FRename.class new file mode 100644 index 00000000..05d57c47 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FRename.class @@ -0,0 +1,58 @@ +' Gambas class file + +Private sOldName As String + +Public Sub Form_Open() + + txtRename.Text = Me.Tag + sOldName = Me.Tag + MarkFilename + +End + +Public Sub MarkFilename() + + Dim lentext, posp As Integer + Dim right3 As String + + right3 = Right$(txtRename.Text, 3) + lentext = Len(txtRename.Text) + If right3 = "jpg" Or right3 = "JPG" Then + posp = lentext - 4 + Else + posp = lentext - 5 + Endif + Me.SetFocus + txtRename.SetFocus + txtRename.Select(0, posp) + +End + +Public Sub btnCancel_Click() + + Me.Close + +End + +Public Sub btnOK_Click() + + Dim sNewName As String + + sNewName = txtRename.Text + If sOldName = sNewName Then + Message.Error(("Old and new filename are identical.")) + MarkFilename + Return + Endif + If FMain.TestRenameOne(sOldName, sNewName) = False Then ' if name already exists, cancel + txtRename.Text = sOldName + MarkFilename + Return + Endif + FMain.FileRename(sOldName, sNewName) + If FMain.Tag = "alpha" Then ' if sorted alphabetically, sort anew + FMain.mnuSortA_Click + Endif + Me.Close + +End diff --git a/app/examples/Image/Lighttable/.src/FRename.form b/app/examples/Image/Lighttable/.src/FRename.form new file mode 100644 index 00000000..58fa4574 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FRename.form @@ -0,0 +1,25 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,51,15) + Text = ("Rename") + Resizable = False + SkipTaskbar = True + { lblRename Label + MoveScaled(2,1,23,3) + Text = ("New filename:") + } + { txtRename TextBox + MoveScaled(1,4,48,4) + } + { btnOK Button + MoveScaled(34,10,15,4) + Text = ("&OK") + Default = True + } + { btnCancel Button + MoveScaled(18,10,15,4) + Text = ("&Cancel") + Cancel = True + } +} diff --git a/app/examples/Image/Lighttable/.src/FRenameAll.class b/app/examples/Image/Lighttable/.src/FRenameAll.class new file mode 100644 index 00000000..b6a78ecf --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FRenameAll.class @@ -0,0 +1,101 @@ +' Gambas class file + +Private sExpl As String +Private sDigits As String = "000" +Private sOldStart As String = "1" +Private iStartVal As Integer = 1 + +Public Sub Form_Open() + + FMain.Enabled = False + sExpl = ("MyPicture.JPG") + SpinBox1.Value = 3 + RenewExample + +End + +Public Sub txtPre_Change() + + RenewExample + +End + +Public Sub SpinBox1_Change() + + If SpinBox1.Value < Len(txtStart.Text) Then + SpinBox1.Value = Len(txtStart.Text) + Return + Endif + sDigits = String$(SpinBox1.Value, "0") + RenewExample + +End + +Public Sub CheckFile_Click() + + RenewExample + +End + +Public Sub txtStart_Change() + + Dim sNewStart As String + Dim iLenStart As Integer + + sNewStart = txtStart.Text + If Not IsDigit(sNewStart) Then + txtStart.Text = sOldStart + Return + Endif + + iLenStart = Len(sNewStart) + iStartVal = Val(sNewStart) + If iLenStart > SpinBox1.Value Then SpinBox1.Value = iLenStart + sOldStart = sNewStart + RenewExample + +End + +Public Sub RenewExample() + + lblExample.Text = txtPre.Text & Format$(iStartVal, sDigits) + If CheckFile.Value = True Then + lblExample.Text = lblExample.Text & "_" & sExpl + Else + lblExample.Text = lblExample.Text & ".jpg" + Endif + +End + +Public Sub btnCancel_Click() + + Me.Close + +End + +Public Sub btnOK_Click() + + Dim sProblems As String + Dim iContinue As Integer + + sProblems = FMain.TestRenameAll(txtPre.Text, sDigits, iStartVal, CheckFile.Value) + If sProblems <> "" Then + FRenameAllWarning.Tag = sProblems + iContinue = FRenameAllWarning.ShowModal() + Else + iContinue = 1 + Endif + + If iContinue = 0 Then Return + + Inc Application.Busy + FMain.RenameAll(txtPre.Text, sDigits, iStartVal, CheckFile.Value) + Me.Close + +End + +Public Sub form_Close() + + FMain.Enabled = True + +End diff --git a/app/examples/Image/Lighttable/.src/FRenameAll.form b/app/examples/Image/Lighttable/.src/FRenameAll.form new file mode 100644 index 00000000..03d5da86 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FRenameAll.form @@ -0,0 +1,77 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,64,59) + Text = ("Rename all pictures") + Resizable = False + { Panel1 Panel + MoveScaled(2,21,60,22) + Border = Border.Sunken + { txtPre TextBox + MoveScaled(17,2,41,4) + } + { SpinBox1 SpinBox + MoveScaled(17,7,6,4) + MinValue = 1 + MaxValue = 5 + } + { txtStart TextBox + MoveScaled(17,12,10,4) + Text = ("1") + MaxLength = 5 + } + { CheckFile CheckBox + MoveScaled(2,17,55,3) + Text = ("Keep original filename as suffix") + Value = CheckBox.True + } + { Label2 Label + MoveScaled(2,7,16,4) + Text = ("Number format") + } + { Label3 Label + MoveScaled(24,7,13,4) + Text = ("digits") + } + { Label5 Label + MoveScaled(2,2,11,4) + Text = ("Prefix") + } + { Label6 Label + MoveScaled(2,12,14,4) + Text = ("Start value") + } + } + { btnOK Button + MoveScaled(46,53,16,4) + Text = ("&OK") + Default = True + } + { btnCancel Button + MoveScaled(28,53,16,4) + Text = ("&Cancel") + Cancel = True + } + { TextLabel1 TextLabel + MoveScaled(2,2,60,14) + Background = &HFFFCCC& + Padding = 7 + Text = ("This function will rename all .jp(e)g files in the directory and add serial numbers to the filenames.
    The pictures will be handled in the order they are shown right now, in rows from left to right, rows from top to bottom.") + Border = Border.Etched + } + { Label1 Label + MoveScaled(2,18,19,3) + Text = ("Options") + } + { Label4 Label + MoveScaled(2,46,11,4) + Text = ("Example:") + } + { lblExample Label + MoveScaled(13,46,49,4) + Font = Font["Bold"] + Background = Color.TextBackground + Alignment = Align.Center + Border = Border.Etched + } +} diff --git a/app/examples/Image/Lighttable/.src/FRenameAllWarning.class b/app/examples/Image/Lighttable/.src/FRenameAllWarning.class new file mode 100644 index 00000000..8c4626e7 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FRenameAllWarning.class @@ -0,0 +1,19 @@ +' Gambas class file + +Public Sub Form_Open() + + txaList.Text = Me.Tag + +End + +Public Sub btnCancel_Click() + + Me.Close(0) + +End + +Public Sub btnContinue_Click() + + Me.Close(1) + +End diff --git a/app/examples/Image/Lighttable/.src/FRenameAllWarning.form b/app/examples/Image/Lighttable/.src/FRenameAllWarning.form new file mode 100644 index 00000000..e5ae683a --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FRenameAllWarning.form @@ -0,0 +1,29 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,68,54) + Text = ("File Conflicts") + Resizable = False + { btnCancel Button + MoveScaled(51,48,15,4) + Text = ("&Cancel") + Default = True + Cancel = True + } + { btnContinue Button + MoveScaled(28,48,21,4) + Text = ("Continue &anyway") + } + { TextLabel1 TextLabel + MoveScaled(11,2,54,14) + Text = ("The following files can't be renamed, because the target filenames already exist in the directory.
    If you continue, only the files without conflicts will be renamed.
    If you cancel, you can choose new filename options.") + } + { txaList TextArea + MoveScaled(2,18,64,28) + ReadOnly = True + } + { PictureBox1 PictureBox + MoveScaled(2,3,7,7) + Picture = Picture["icon:/48/warning"] + } +} diff --git a/app/examples/Image/Lighttable/.src/FSlideshow.class b/app/examples/Image/Lighttable/.src/FSlideshow.class new file mode 100644 index 00000000..73752d11 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FSlideshow.class @@ -0,0 +1,56 @@ +' Gambas class file + +Public Sub Form_Open() + + Dim sDelay As String + + Me.Width = Desktop.Width + Me.Height = Desktop.Height + panControl.Y = Me.Height - 55 + panControl.Width = Me.Width + sDelay = Settings["SlideShow/Delay"] + If sDelay = "" Then sDelay = "5" + spinDelay.Value = sDelay + FMain.GetDelay(sDelay) + btnStop.SetFocus + +End + +Public Function PictureLoad(sPath As String, iOri As String) As Boolean + + Dim hPic As Image + Dim iFormat As Single + + hPic = Image.Load(sPath) + If iOri = 6 Then + hPic = hPic.Rotate(Rad(-90)) + Else If iOri = 8 Then + hPic = hPic.Rotate(Rad(90)) + Endif + PicBox1.Picture = hPic.Picture + Wait + + iFormat = PicBox1.Picture.Width / PicBox1.Picture.Height + PicBox1.Y = 0 + PicBox1.Height = Me.Height - 35 + PicBox1.Width = Me.Height * iFormat + If PicBox1.Width > Me.Width Then + PicBox1.Width = Me.Width + PicBox1.Height = Me.Width / iFormat + Endif + PicBox1.X = (Me.Width - PicBox1.Width) / 2 + PicBox1.Visible = True + +End + +Public Sub spinDelay_Change() + + FMain.GetDelay(spinDelay.Value) + +End + +Public Sub btnStop_Click() + + Me.Close + +End diff --git a/app/examples/Image/Lighttable/.src/FSlideshow.form b/app/examples/Image/Lighttable/.src/FSlideshow.form new file mode 100644 index 00000000..337e6046 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FSlideshow.form @@ -0,0 +1,46 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,81,37) + Background = Color.Background + Resizable = False + SkipTaskbar = True + { PicBox1 PictureBox + MoveScaled(22,12,10,9) + Visible = False + Stretch = True + } + { panControl Panel + MoveScaled(0,31,79,4) + Background = Color.Background + Arrangement = Arrange.Horizontal + Border = Border.Raised + { Panel1 Panel + MoveScaled(0,1,7,2) + Expand = True + } + { Label1 Label + MoveScaled(8,0,25,4) + Text = ("Pause between pictures:") + Alignment = Align.Right + } + { spinDelay SpinBox + MoveScaled(34,0,7,4) + MaxValue = 60 + } + { Label2 Label + MoveScaled(42,0,10,4) + Text = ("sec") + } + { btnStop Button + MoveScaled(59,0,15,4) + Text = ("Stop") + Picture = Picture["icon:/16/stop"] + Default = True + Cancel = True + } + { Panel2 Panel + MoveScaled(75,0,4,3) + } + } +} diff --git a/app/examples/Image/Lighttable/.src/FStart.class b/app/examples/Image/Lighttable/.src/FStart.class new file mode 100644 index 00000000..c93266a5 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FStart.class @@ -0,0 +1,135 @@ +' Gambas class file + +Private iNr As Integer +Private aRecentDirs As New String[] +Private atxLabel As New TextLabel[] + +Public Sub Form_Open() + + Dim i, j, pos, lastpos As Integer + Dim sShort, sPath As String + + aRecentDirs.Resize(10) + atxLabel.Resize(10) + + i = 0 + For j = 0 To 9 + aRecentDirs[i] = Settings["Recent/" & j] + If aRecentDirs[i] = "" Then Break + If Not Exist(aRecentDirs[i]) Then + i -= 1 + Goto directory_does_not_exist + Endif + pos = -1 + Do While pos <> 0 + lastpos = pos + pos = InStr(aRecentDirs[i], "/", pos + 1) + Loop + sShort = Right$(aRecentDirs[i], Len(aRecentDirs[i]) - lastpos) + sPath = Left$(aRecentDirs[i], Len(aRecentDirs[i]) - Len(sShort)) + + atxLabel[i] = New TextLabel(svwLast) As "lblRecent" + With atxLabel[i] + .Border = Border.Raised + .Font.Grade = -1 + .Padding = 1 + .Tag = i + .Text = "" & sShort & "
    " & sPath + .AutoResize = True + End With + directory_does_not_exist: + i += 1 + Next + Me.Center + +End + +Public Sub btnOK_Click() + + Dim i, j As Integer + Dim sDir As String + + sDir = DirChooser1.SelectedPath + If MMain.ReadDir(sDir) = False Then + Message.Error(Subst(("The folder &1 doesn't contain any jp(e)g files."), sDir)) + Return + Endif + + For i = 0 To 8 ' write recent selections into settings + If aRecentDirs[i] = sDir Then + For j = i To 8 + aRecentDirs[j] = aRecentDirs[j + 1] + Next + Break + Endif + Next + For i = 9 To 1 Step -1 + aRecentDirs[i] = aRecentDirs[i - 1] + Next + aRecentDirs[0] = sDir + For i = 0 To 9 + Settings["Recent/" & i] = aRecentDirs[i] + Next + + MMain.GoAhead(True) + +End + +Public Sub btnCancel_Click() + + MMain.GoAhead(False) + +End + +Public Sub lblRecent_Enter() + + iNr = Last.Tag + If atxLabel[iNr].Background = -1 Then + atxLabel[iNr].Background = Color.LightBackground + Endif + +End + +Public Sub lblRecent_Leave() + + If atxLabel[iNr].Background = Color.LightBackground Then + atxLabel[iNr].Background = -1 + Endif +End + +Public Sub lblRecent_MouseDown() + + DirChooser1.SelectedPath = aRecentDirs[iNr] + +End + +Public Sub lblRecent_DblClick() + + lblRecent_MouseDown + btnOK_Click + +End + +Public Sub DirChooser1_Change() + + Dim i As Integer + + For i = 0 To 9 + If Not Exist(aRecentDirs[i]) Then Break + If aRecentDirs[i] = "" Then Break + If aRecentDirs[i] = DirChooser1.SelectedPath Then + atxLabel[i].Background = Color.SelectedBackground + atxLabel[i].Foreground = Color.SelectedForeground + Else + atxLabel[i].Background = -1 + atxLabel[i].Foreground = -1 + Endif + Next + +End + +Public Sub DirChooser1_Activate() + + btnOK_Click + +End diff --git a/app/examples/Image/Lighttable/.src/FStart.form b/app/examples/Image/Lighttable/.src/FStart.form new file mode 100644 index 00000000..357c0d8b --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FStart.form @@ -0,0 +1,42 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,101,65) + Text = ("Lighttable - Select picture folder") + Resizable = False + { btnOK Button + MoveScaled(69,60,15,4) + Text = ("&OK") + Default = True + } + { btnCancel Button + MoveScaled(85,60,15,4) + Text = ("&Cancel") + Cancel = True + } + { DirChooser1 DirChooser + MoveScaled(2,6,51,51) + } + { Separator1 Separator + MoveScaled(0,58,102,1) + } + { Label1 Label + MoveScaled(2,2,27,3) + Font = Font["Bold,+1"] + Text = ("New selection") + } + { Separator2 Separator + MoveScaled(54,0,2,58) + } + { Label2 Label + MoveScaled(58,2,23,3) + Font = Font["Bold,+1"] + Text = ("Last selections") + } + { svwLast ScrollView + MoveScaled(58,7,41,49) + Arrangement = Arrange.Vertical + Border = False + ScrollBar = Scroll.Vertical + } +} diff --git a/app/examples/Image/Lighttable/.src/FTime.class b/app/examples/Image/Lighttable/.src/FTime.class new file mode 100644 index 00000000..d807f8f8 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FTime.class @@ -0,0 +1,27 @@ +' Gambas class file + +Public Sub Form_Open() + + Me.Center + +End + +Public Sub btnCancel_Click() + + Me.Close + +End + +Public Sub btnOK_Click() + + If SpinDay.Value + SpinHrs.Value + SpinMins.Value = 0 Then + Message.Info(("There is no time correction selected.")) + Return + Endif + + Inc Application.Busy + FMain.TimeCorrection(SpinDay.Value, SpinHrs.Value, SpinMins.Value, cmbPlusMinus.Index) + Dec Application.Busy + Me.Close + +End diff --git a/app/examples/Image/Lighttable/.src/FTime.form b/app/examples/Image/Lighttable/.src/FTime.form new file mode 100644 index 00000000..b74acc77 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/FTime.form @@ -0,0 +1,58 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,71,40) + Text = ("Time correction") + Resizable = False + { SpinDay SpinBox + MoveScaled(2,25,8,4) + MaxValue = 365 + } + { SpinHrs SpinBox + MoveScaled(19,25,7,4) + MaxValue = 23 + } + { SpinMins SpinBox + MoveScaled(36,25,7,4) + MaxValue = 59 + } + { cmbPlusMinus ComboBox + MoveScaled(54,25,15,4) + ReadOnly = True + List = [("earlier"), ("later")] + Text = ("later") + } + { btnOK Button + MoveScaled(54,34,15,4) + Text = ("&OK") + Default = True + } + { btnCancel Button + MoveScaled(37,34,15,4) + Text = ("&Cancel") + Cancel = True + } + { TextLabel1 TextLabel + MoveScaled(2,3,67,15) + Background = &HFFFCCC& + Padding = 7 + Text = ("This function will correct all date/time informations in the EXIF section of all .jp(e)g files in the folder.
    A backup of the files will be made with file names like 'MyPicture.jpg_original'.
    If there are no EXIF date/time informations in a file, nothing will be done.") + Border = Border.Etched + } + { Label1 Label + MoveScaled(2,21,60,4) + Text = ("Set the original time of all pictures to") + } + { Label2 Label + MoveScaled(10,25,9,4) + Text = ("day(s)") + } + { Label3 Label + MoveScaled(26,25,10,4) + Text = ("hour(s)") + } + { Label4 Label + MoveScaled(43,25,11,4) + Text = ("minute(s)") + } +} diff --git a/app/examples/Image/Lighttable/.src/MMain.module b/app/examples/Image/Lighttable/.src/MMain.module new file mode 100644 index 00000000..22771979 --- /dev/null +++ b/app/examples/Image/Lighttable/.src/MMain.module @@ -0,0 +1,53 @@ +' Gambas module file + +Private sPath As String +Private aFiles As String[] + +Public Sub Main() + + Dim sTest As String + + Try Shell "exiftool -ver" To sTest + If Error Or sTest = "" Then + Message.Error(("To run this program, exiftool must be installed.")) + Return + Endif + + Try Shell "convert -version" To sTest + If Error Or sTest = "" Then + Message.Error(("To run this program, convert must be installed.")) + Return + Endif + + FStart.Show + +End + +Public Sub ReadDir(sDir As String) As Boolean + + Dim iCount As Integer + + aFiles = Dir(sDir, "*.{jpg,jpeg}").Sort(1) ' Sorted by file names, 1 = case insensitive + iCount = aFiles.Count + If iCount > 0 Then + sPath = sDir + Return True + Endif + +End + +Public Sub GoAhead(bOk As Boolean) + + FStart.Close + If bOk = False Then Return + FMain.Tag = sPath + FMain.Show + +End + +Public Function GetFiles() As String[] + + Return aFiles + aFiles.Delete + +End diff --git a/app/examples/Image/Lighttable/CHANGELOG b/app/examples/Image/Lighttable/CHANGELOG new file mode 100644 index 00000000..5cb10caf --- /dev/null +++ b/app/examples/Image/Lighttable/CHANGELOG @@ -0,0 +1,3 @@ +* Sun Feb 28 2010 mathias 0.1 +- Initial release + diff --git a/app/examples/Image/Lighttable/FStart.class b/app/examples/Image/Lighttable/FStart.class new file mode 100644 index 00000000..c3a9c61f --- /dev/null +++ b/app/examples/Image/Lighttable/FStart.class @@ -0,0 +1,92 @@ +' Gambas class file + +aRecentDirs As New String[] +iNr As Integer +iLast As Integer +atxLabel As New Object[] + + +Public Sub Form_Open() + Dim i, pos, lastpos As Integer + Dim sShort, sPath As String + + aRecentDirs.Resize(10) + atxLabel.Resize(10) + + For i = 0 To 9 + aRecentDirs[i] = Settings["Recent/" & i] + If aRecentDirs[i] = "" Then Break + pos = -1 + Do While pos <> 0 + lastpos = pos + pos = InStr(aRecentDirs[i], "/", pos + 1) + Loop + sShort = Right$(aRecentDirs[i], Len(aRecentDirs[i]) - lastpos) + sPath = Left$(aRecentDirs[i], Len(aRecentDirs[i]) - Len(sShort)) + + atxLabel[i] = New TextLabel(Me) As "lblRecent" + With atxLabel[i] + .X = 406 + .Y = 49 + (i * 35) + .Width = 287 + .Height = 35 + .Border = 4 + .Font.Grade = -1 + .Padding = 1 + .Tag = i + .Text = "" & sShort & "
    " & sPath + End With + Next + Me.Center +End + + +Public Sub btnOK_Click() + Dim i, j As Integer + + For i = 0 To 8 + If aRecentDirs[i] = DirChooser1.SelectedPath Then + For j = i To 8 + aRecentDirs[j] = aRecentDirs[j + 1] + Next + Break + Endif + Next + For i = 9 To 1 Step -1 + aRecentDirs[i] = aRecentDirs[i - 1] + Next + aRecentDirs[0] = DirChooser1.SelectedPath + For i = 0 To 9 + Settings["Recent/" & i] = aRecentDirs[i] + Next + Message.Info(aRecentDirs[0] & " wird geöffnet.") + Me.Close +End + +Public Sub btnCancel_Click() + Me.Close +End + +Public Sub lblRecent_Enter() + iNr = Last.Tag + If atxLabel[iNr].Background = -1 Then + atxLabel[iNr].Background = Color.LightBackground + Endif +End + +Public Sub lblRecent_Leave() + If atxLabel[iNr].Background = Color.LightBackground Then + atxLabel[iNr].Background = -1 + Endif +End + +Public Sub lblRecent_MouseDown() + atxLabel[iLast].Background = -1 + atxLabel[iLast].Foreground = -1 + atxLabel[iNr].Background = Color.SelectedBackground + atxLabel[iNr].Foreground = Color.SelectedForeground + iLast = iNr + DirChooser1.SelectedPath = aRecentDirs[iNr] +End + + diff --git a/app/examples/Image/Lighttable/FStart.form b/app/examples/Image/Lighttable/FStart.form new file mode 100644 index 00000000..b1309cd7 --- /dev/null +++ b/app/examples/Image/Lighttable/FStart.form @@ -0,0 +1,35 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,101,66) + Text = ("Lighttable - Choose picture folder") + { btnOK Button + MoveScaled(84,60,15,4) + Text = ("&OK") + Default = True + } + { btnCancel Button + MoveScaled(67,60,15,4) + Text = ("&Cancel") + Cancel = True + } + { DirChooser1 DirChooser + MoveScaled(2,6,51,51) + } + { Separator1 Separator + MoveScaled(2,58,97,1) + } + { Label1 Label + MoveScaled(3,2,27,3) + Font = Font["Bold,+1"] + Text = ("New selection") + } + { Separator2 Separator + MoveScaled(54,0,2,58) + } + { Label2 Label + MoveScaled(58,2,23,3) + Font = Font["Bold,+1"] + Text = ("Last selections") + } +} diff --git a/app/examples/Image/Lighttable/Help_ca.html b/app/examples/Image/Lighttable/Help_ca.html new file mode 100644 index 00000000..8bf512d5 --- /dev/null +++ b/app/examples/Image/Lighttable/Help_ca.html @@ -0,0 +1,60 @@ + + + + + + + + + + + +AjudaCàrrega de les imatges
    Les imatges es carregen en segon pla.
    Mentre es van carregant, ja podeu treballar amb les imatges que es mostren.
    Excepció: algunes de les funcions, con ordenar cronològicament, no estan disponibles fins que s'han carregat totes les imatges.
    +La càrrega es pot cancel·lar via el menú del programa (Fi).
     
    +Ús
    Totes les funcions es poden cridar des dels menús de context:
    Clic amb el botó secundari al fons del lighttable: Menú de programa
    Clic amb el botó secundari en un marc o a sobre d'una imatge: Menú de la imatge

    +Dreceres de teclat
    Hi ha dreceres per a totes les tasques, tal com es mostra als menús de context:
    +Ctrl+A Ordena les imatges alfabèticament
    +Ctrl+C Ordena les imatges cronològicament
    +Ctrl+S Projecció amb diapositives, començant per la imatge marcada
    +Ctrl+N Reanomena totes les imatges
    +Ctrl+T Correcció de l'hora
    + +F1 Ajuda
    + +Ctrl+End Surt del programa
    Arrossega un marc amb el ratolí: podeu moure el marc a qualsevol posició dins del lighttable
    Enter o fer clic sobre una imatge: Amplia la imatge
    +Alt+Enter o fer clic sobre una imatge ampliada: Vista a pantalla completa
    +Dreta Mou a la imatge següent
    + + +Esquerra Mou a la imatge anterior
    I Informacions de la imatge
    + +N Reanomena la imatge
    +Del Suprimeix la imatge
    + +ESC ho cancel·la i ho tanca tot
    +
    Visualitza i compara imatges
    +Quan estigui oberta la vista ampliada o la pantalla completa, mou-te amb les tecles per les fotografies (els fitxers grans triguen un moment a carregar-se).
    + +O salta a una altra imatge: Feu clic en una altra imatge de «sota». També podeu desplaçar-vos per la «taula».
    +
    +Ordena les imatges
    Si teniu imatges de més d'una càmera, és útil primer ordenar-les cronològicament (Menú de programa). Aquesta funció llegeix la Exif-metadata de les fotografies.
    Per ordenar-les manualment, només heu de moure els marcs al voltant de la taula. Llavors el podeu reanomenar manualment com per exemple «005 la meva millor imatge».

    +Reanomena totes les imatges (Ctrl+N)
    +Una bona alternativa a reanomenar-les manualment. Si he mogut totes les fotografies als llocs desitjats, els fitxers poden ser numerats i reanomenats en aquest ordre exactament. Podeu triar entre afegir o esborrar els noms de fitxer antics.
    +
    +Correcció de l'hora (Ctrl+T)
    +Quan etiqueteu geogràficament les fotografies o quan compareu fotos de dues càmeres, observeu que una càmera té una hora incorrecte? Cap problema. Amb aquesta funció podeu corregir la Exif-metadata de la data i la hora de totes les fotografies a dins la carpeta.

    +Nota: Manipulació de imatges i miniatures
    +Quan s'editen les fotografies amb un programa d'edició d'imatges (p.e. GIMP), les imatges en miniatura de les fotos no es modifiquen. O sigui que el Lighttable seguirà mostrant les miniatures de les imatges abans dels canvis. +Solució:
    +a) aneu a previsualitza al Gwenview, Dolphin, Konqueror o qualsevol programa similar, així les miniatures queden actualitzades,
    +b) elimina totes les miniatures (de la carpeta /home/usurai/.thumbnails/normal), així les miniatures es crearan posteriorment.
    +
    +
    + diff --git a/app/examples/Image/Lighttable/Help_de.html b/app/examples/Image/Lighttable/Help_de.html new file mode 100644 index 00000000..5c7ef22d --- /dev/null +++ b/app/examples/Image/Lighttable/Help_de.html @@ -0,0 +1,74 @@ + + + + + + + + + + + + + HilfeLaden der Bilder
    Das Laden der Bilder läuft im Hintergrund.
    +Währenddessen kann man schon mit den bereits angezeigten Bildern arbeiten.
    Allerdings sind einige Funktionen wie chronologisches Sortieren erst verfügbar, wenn alle Bilder geladen sind.
    +Das Laden der Bilder lässt sich im Programm-Menü abbrechen (Ende).
     
    Bedienung
    Alle Funktionen lassen sich über Kontextmenüs aufrufen:
    Rechtsklick auf den Hintergrund des Leuchttischs: Programm-Menü
    +Rechtsklick auf einen Rahmen oder ein Bild: Bild-Menü

    +Kurzbefehle
    Für alle Funktionen gibt es Kurzbefehle, wie in den Kontextmenüs angegeben:
    +Strg+A Bilder alphabetisch sortieren
    + +Strg+C Bilder chronologisch sortieren
    +Strg+S Diashow ab dem markierten Bild
    + +Strg+N Alle Bilder umbenennen
    +Strg+T Zeitkorrektur
    + +F1: Hilfe
    + +Strg+Ende Programm beenden
    Ziehen eines Rahmens mit der Maus: Bild lässt sich beliebig verschieben
    +Enter oder Mausklick auf ein Bild: vergrößerte Ansicht
    +Alt+Enter oder auf das Großbild klicken: Vollbildansicht
    Rechts gehe zum nächsten Bild
    +Links    gehe zum vorherigen Bild
    I Bildinformationen
    +N Bild umbenennen
    +Entf: Bild löschen
    + +ESC schließt alle Ansichten und bricht alle Vorgänge ab

    +Bilder ansehen und vergleichen
    +Bei geöffneter Großbild- oder Vollbildansicht einfach mit den Tasten die Bilder durchgehen (bei großen Bilddateien dauert das Laden etwas).
    +Oder zu einem anderen Bild springen: "unten" ein anderes Bild anklicken. Man kann den "Tisch" auch scrollen.

    +Bilder sortieren
    Hat +man Bilder von mehreren Kameras im Verzeichnis, ist es nützlich, die +Bilder zunächst einmal chronologisch zu sortieren (Programm-Menü). Diese Funktion wertet die Exif-Metadaten der Fotos aus.
    Manuell lassen sich die Bilder zum Sortieren beliebig +auf dem Tisch herumschieben. Dann kann man sie einzeln umbenennen, zB +"005 mein liebstes Foto".

    +Alle Bilder umbenennen (Strg+N)
    +Eine gute Alternative zum manuellen Umbenennen. Hat man alle Fotos in +die gewünschte Reihenfolge verschoben, lassen sich die Dateien in genau +dieser Reihenfolge mit Nummern versehen und umbenennen. Die alten +Dateinamen werden hierbei nach Wunsch entweder angehängt oder +überschrieben.
    +
    +Zeitkorrektur (Strg+T)
    +Beim Geokodieren von Fotos oder beim Vergleich von Fotos zweier Kameras +stellt sich heraus, dass die Zeit bei einer Kamera falsch eingestellt +war? Kein Problem. Mit dieser Funktion lassen sich die Exif-Metadaten +zu Datum und Zeit von allen Bildern im Verzeichnis korrigieren.
    +
    +Anmerkung: Bildbearbeitung und Vorschaubilder
    +Wenn man Fotos mit einem Bildbearbeitungsprogramm (z.B. GIMP) +bearbeitet, werden die zugehörigen Vorschaubilder nicht aktualisiert. +Leuchttisch zeigt daher weiterhin die alten Miniaturansichten an. +Abhilfe:
    +a) in Gwenview, Dolphin, Konqueror o.ä. die Vorschauansicht wählen und die Thumbnails aktualisieren lassen,
    +b) die Thumbnails (im Verzeichnis /home/user/.thumbnails/normal) löschen, so dass sie danach neu erstellt werden.
    +
    +
    + \ No newline at end of file diff --git a/app/examples/Image/Lighttable/Help_en.html b/app/examples/Image/Lighttable/Help_en.html new file mode 100644 index 00000000..03fd030f --- /dev/null +++ b/app/examples/Image/Lighttable/Help_en.html @@ -0,0 +1,71 @@ + + + + + + + + + + + +HelpLoading the pictures
    The pictures are loaded in the background.
    While loading goes on, +you can already work with the pictures that are displayed.
    Exception: some of the functions like sorting chronologically are not available before all pictures are loaded.
    +The loading can be aborted via the program menu (End).
     
    +Usage
    All functions can be called by context menus:
    Right-click on the background of the lighttable: Program menu
    Right-click on a frame or on a picure: Picture menu

    +Shortcuts
    For all tasks there are shortcuts as shown in the context menus:
    +Ctrl+A Sort pictures alphabetically
    +Ctrl+C Sort pictures chronologically
    +Ctrl+S Sildeshow, beginning with the marked picture
    +Ctrl+N Rename all pictures
    +Ctrl+T Time correction
    + +F1 Help
    + +Ctrl+End Quit program
    Drag a frame with the mouse: you can move the frame to any position on the lighttable
    Enter or click on a picture: Enlarge picture
    +Alt+Enter or click on an enlarged picture: Fullscreen view
    +Right Move to next picture
    + + +Left   Move to previous picture
    I Picture informations
    + +N Rename picture
    +Del Delete picture
    + +ESC will close and cancel everything
    +
    View and compare pictures
    +When view or fullscreen view is open, just move with the keys through the photos (big files take a moment to load).
    + +Or jump to another picture: click "down" on another picture. You can also scroll the "table".
    +
    +Sort pictures
    If +you have pictures of more than one camera, it is useful to first sort +them chronologically (Program menu). This function reads the Exif-metadata of the photographs.
    To sort manually, just  move the frames around on the table. Then you can rename them manually like "005 my best picture".

    +Rename all pictures (Ctrl+N)
    +A good alternative to renaming manually. If you have moved all +photographs to the desired places, the files can be numbered and renamed +exactly in this order. As you like, the old filenames are either +attatched or overwritten.
    +
    +Time correction (Ctrl+T)
    +When geotagging your photographs or when comparing photos of two +cameras, you notice that a camera had a wrong time? +No problem. With this function you can correct the Exif-metadata of +date and time of all the photographs in the folder.

    +Note: Image manipulation and thumbnails
    +When editing photographs with an image manipulation program (i.e. +GIMP), the thumbnail pictures of the photos are not changed. So, +Lighttable will still show the old miniature pictures before the +changes. Solution:
    +a) go to preview in Gwenview, Dolphin, Konqueror or any similar program, so the thumbnails are updated,
    +b) delete all the thumbnails (in the folder /home/user/.thumbnails/normal), so they will be newly created afterwards.
    +
    +
    + \ No newline at end of file diff --git a/app/examples/Image/Lighttable/LTicon.png b/app/examples/Image/Lighttable/LTicon.png new file mode 100644 index 0000000000000000000000000000000000000000..3faf640ba74b45e8899eee605064ef163a5ee6f8 GIT binary patch literal 296 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvtpJ}8R|bav_V)Jw|NjRHI3NGN zfB$|4AP2~+X8;oa8-N(d1cLf^h88A3dZ(w0V~EE2xX{p0 zAU7%|B0ec5Au$e!`X+YAC&k4l#56S5#U;chB*k|2w)IcyO-zadvfDaaTG|`q6Jr`0 z8WIwtdna`EPwI(JiUo>v_Ox|$wZ_HA#3jb|P3W37bIQ~ile&64fJOmDrq7-_b>^fv z5Ibhll>Uj6`+zEeMopPEamv&QO|5k?anV4PO)a%uJ#Fy`QLzcpZJo^xP1P}p(LiU^ zHdM8DHpeDJ#l%Oqv^CZ>Rs(GWip0f7H@7v!#zzC)21Id*(GCs{KoTepmdLvBSsq9X)yQ@bUfo5AWW4aObYQTX*i+vSs`Fja$})zd6kT z3=Y4NAirRSu9wT08TY=P@aprrRi9tY`V6E#eV)y<_VefU>pp#1x8cj@&tEoe{`zI> z$}eA6fBN?A)9U4)zU($#)Mhy_1_4m#}+JZB6G# aErzuT!ofv?v$%oIWbkzLb6Mw<&;$SrEfq2V literal 0 HcmV?d00001 diff --git a/app/examples/Image/Lighttable/hand1.png b/app/examples/Image/Lighttable/hand1.png new file mode 100644 index 0000000000000000000000000000000000000000..ab56c037b5d150241f983b5b94a24abef1f33c57 GIT binary patch literal 176 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#=`Bp4pvH+~MJm`Z~Df*FpnOj)%T$jkI}aSV~T z+&b|z?*Ri2XWuC+Z^W;8Di`#TGvsMYoKA9+#ImT1tK9ZC3o%6;b@%K@6;?g{dslm) zd(98FE9_TQS{M`@j=%POAlB#mOqYFM!A_MsPi?o)c&zsRRgZJsz7NiC(^tIvRDC*z Xe~-9$n@f1N8OSM~u6{1-oD!M<4sk;u literal 0 HcmV?d00001 diff --git a/app/examples/Image/Lighttable/help-contents.png b/app/examples/Image/Lighttable/help-contents.png new file mode 100644 index 0000000000000000000000000000000000000000..917ef83e7f1ca4ff020a5bd33b979e698cc1b0e7 GIT binary patch literal 742 zcmV0u zcgvZ(<>v1BHhIhr8!IM8Zk4&_|NsAlt>HR-&^dh2Nru$^{{MTZ-whfnDoAijh}Nmb z>{pN3PKwt+fz*t%<^KQwU6k7m8YwMHaz=*PO^Vr5j@#?<`;oWkNQc=E8YwYPbx@Aq zuFCMI#_v&&;1L`vI8=LFm*;4n>B!voUzz7#m*-uV=M^C^NM(#?pzepX@|(f*X`%0F zq3&m(?HVLBQEilQr}d1u_kXbVbg1-lr}c5C^d2ZUQ*4uZuKS0!`+l+ed$0O?uKOV@ zJ4eBfp@I=fwKAD>;A&p{4+X27$Pn(OL9<&*Gh)d zUAoL~Ve&)?raATTJ{KZFM? z5E>Q^1QC(p;ZeL`f#{gn*x0!Egv8h+KCnP?N@{9qT6#uiRw_SOAUh{FH#aXox1g{{ z04z{kQd(MCR$f|KQ7H%(sH(21sjaK8scC2s0t+-YH8;1kwlxERFi=3Gy`!@W3c5uZ Y0CxpDmCMY#o&W#<07*qoM6N<$g1#V7)&Kwi literal 0 HcmV?d00001 diff --git a/app/examples/Image/Lighttable/lighttable.png b/app/examples/Image/Lighttable/lighttable.png new file mode 100644 index 0000000000000000000000000000000000000000..4497010b76ecf7b2e5e8fe76bdbf3efe37aaa9b1 GIT binary patch literal 6536 zcmV;38F%K1P)hFSBq+&aUxjv!qy-n>0pjD~V#+bsRf(&^l>^#%--ME}Eifks>Wn zplK7d=)VPAAVnIdD4fP{T`x+l$g-ue)>$;88ER%Y94pA`7ksLmfPmb&w zP=9}r_nv#-_n!N^zwP{fzayUg{!7*($kGh$I55DII35Q3j13BdjY@C1-$8F3V0 zu_&c6S+;$gr71!RT*pDTQbo@9DOYmT>#MA{bDLlWlUalWghXHw0@4h?A^^uxwA&F< zDiGjkMV6%)BM?%eg~b3uNU}6TDFH%~Bq<1i5{4ubxQ;>qX=XNGz=9A6A+W}R=)4pN zjI~H9z=E*`f#{qIK^(`pj)S!zg=L^`0BbF+R*RrmLTkMFy7!?dG_^?Hb&U05NB zBgO{GT%BEHdE+8>vC2J{y` z;*_81E3&Y(%;H)T9krV%$E8#$A#X2n_V5D)_f%S7&9h)YV`(e%uq`u+IT#2-$}mn;zfeM z$D|QTE0Pq34^86)d9sx^S*ZhQgDBHX92#bRehIBLAV{KwgNKHgn_C1S7#bd9WoeGg zDBQrOl=le}L%Z3e9B69Ikp4=6dMq$mMrm*qYe7j#ni-^&NCBBK2q7@0GbT3Gg-vxc zCL>D|JkQ5iLlQ+83!dlEZZ+tuR7jGP)PnfquYK31Ns7Q?5kz4NB^6%2#Cmmw%D_-Z z+_vu!7?S~(Bu;UC4-g0-Y`5@(92QGQjYVfHmjiqE`WA)l7M>p84 zSZjBfwRc9=WC&{jFj(X!#un48wFvE?Cq@Q%?12+MQEUHhqbQDYmp@8E!d&b|DcoqtUBFptP^lqNs zxpN1l)OLij^SZ!-O%a&8Tt#Aq#`C;A6W)nut;J*+0LElkYth=-o?w=x+h0g3h38G| zi?^__h&3IHWH#@&R$#3GEJ{kGl3NqT8jw;jab(B*#MU2+R#J97;OpP`U;Lw|e+L z^3xQHWxn;bKV$C3HH3ioJop%mMx9FkAV2%Q`}q9do!%F3X5k!PJ@;=g(GUklk0XL6 zh4M1x{1IyN0hi8SVP&~atC1nCp-xk-6h|-b2tsKa1r8L!)Ir@WChfwWt0Gp5v0s2l$@L ziK&CU`22TRUteQ+X`ah3J&V$kdnfx4Lhg$vgr#kk*$A7su}8aciQ4s5s<&U{cRu!S zS&$ptbMg!|v&ww@BBK8)ZTkUW2cGS?%QVZ-)|##dCZl6B29x{u1ZQpslOjDCwc9)?{n(x2WZ4ilx$NOo8<9NP7_5T zH)gIdHhGeX$zx277CY*q^&UJc1(_T|8yA^0s5Tc-agon|q~wXE@i0_EXJ8fif6jd110Y)8_xLo zUYjQ;JmzZ+jvjrG#f=55EV7z4sV6u1`CYcoSc|~!WdIwuIJo{5a$|i+5pXd7GUobq z+M&hjAspJQ)MoLrbq=vYgC=JE0*DXoJK=UaCCwi|jh&&b0^*Gol#*zr5V9i$N^6?6 zRqCw<>(|eLeTZIEtJYb+o$|o@j`G|$FVd-%bkmu-q)A*jx z+U4@FuJuFtl!muO+DDlfF#aH(~RosBKPLL z&X6w&Gs)_Prcwwf1O@WGhbbjoUA@j*#B6Lddxq;-no+%enSQ59KG%;PK1e)%lok#q zHISs3)fpTq@YEU`D>qRmKTDU@-YHS72A5ucga7lVZ;+)aVGV|kSt3@rws4N|$|Nc$ zm>3)4D}Vknx0WNOet9oL8_gy{ioHhg#EBF9?mzzLIF3uDB+*jPN-{=9JTQVJfg}Uh zRh+&*VSO#(^yz!ZvTSn|*i9!-p5z}q^+o#o%bQDCOXe0)zK_-}B8y0ygFuFm0+R`* zj+`Jf8O}Z$dhE~x{Ikz|39U75U}-nNaRrViQA#0Dc%DPE)!?(g@P52NQ3{4hO^ibT zfL4mZ{xV^;jgFJ7>jC3qV|?tFK8pBjro*hYxUNfw8LSoR` z1DjEAG&^5A327P0^pPV>Pw)SNNfLAM!UdEP#72_~92_ZVx1&yaOM56Av$9s_=+t2x zFONwgrl+TS;+=o}JjP@Mg+5#dvN&RGy-vPTfuxNwf?TmeDd%wG`YfXd4{&rJJPSxE zy8W$`lK%cajLBG8Tcdwq04Wu|mqSX4!r(wZ#_aF69mi$j$OK4@ ztHBY13`wQGk8-6SN5lxnXLMwcwOWHTi5Ne45MxYFyveB}ctL>4LWD77LNPczM5$1~ zbu?a($8(@sYceoA#L?+#vM#*4bJa?Ae>tTTzV92{XEx91T0%727W_L1_(&X~_9|6x`jm7l?a()L-DoLRrNYZ3~ zJljzP-DpKSbi;D$)G2;cvetGz=-rM77U}NF2mCm+`}gm5JOG`~1ziKZtoaY4t>olx zdROCF&~X%Xja`1@4U93J#3mg*vDS2o4Lj_`mR?y4S}TqmnIxCX?RoI!%a=O^76d|c z+>aEUN{2C>B8TnNK`@*Dm6A+MOzewy>Cz?AG<`?X;5ZJGlY8f9v1rHhy9Sq6*SU1% zDnr9VC@IP3iwq48QYZw>-Ch6$S(Z_6)TpkmlFR3rICPN79urzxo#(BU=V`Uttk+Tw z9T?#Hjg?L?QYtccdxL>dpL#8%(kIziwVXKeUZ$p|_QkW-k|fDHVt`U=7Y3UjSfF>q z+?t!?zkT^D962(ktxi!~B2Mwj|IvO_$j>KxwCE)iex4X5M@gFJGX)q_9HM zPT`&?;R|U(#b&H$h7HG|S*UP+ah}H?f4pZAR=4WwT?1*FvT|phxnhZ_spAX{kI`y1 zX*63zVaT;>Z}9A2K7-Pl#6)CC%$=q8Amsjwm-)Gb^Dn@oKQ+W~d5p(@)?;~n2`L5F zZqIS#SOdSW%<^)B3$JE8^q_~(z0==9W=PqwAHV$sc*<~LN%P-dFY)T~3TN7uWh}Kc z$o)5 zH}Ks&2MLzf|R!Z{5LjQr@}{^30cGo_VH4 zQ0|#x1Zlvt6#6_~pGkS`T8o)QgGx5|pGD1|6cm3^fR_dwYFMJMO`JsZBE3h}Y^P{A zF?oz2$Z`Ajb*ih&j31oj2j`yS@`dw+NsBvo7Ra)U+WHzxi}T1H65!?~?jaB@!3*D; z;Zq;K!OwsC5dZM0vwZI3WBkTnta0TpGqN>>Z@8Q3G>4W^8 zUw@7;N$EeTz=L8z^p*fDnQYSbz_A4~&u7fbF`jFav@?EC&oGWeI~rvmSMpF^ZFSMhkV0%nms=AAxNmav;6br%`Lxp;&zNjG z5b8vt-QvA_Y%5{Yi?q8d63o(+TeoHr+C`X@3_&6AX@xPKtH_d!6iXJRNG)-KfP(KK z#N@sYu+~tkuCU$=DHZYvEUk76sT_RQAv2aRY-5Z;YmL^L!NI|vDrK#uTCI{KF(yq( zjm7hGL}3$*L28XLmdscxl@d`HF*r2T6Hg~e(#=6B>FXb0tFmYSB?QWGkwReHtW&ls zbncf*<9iN0NeKL0o&hiDWPq_4YY3wR-}kYA=eYk zxm&N_s@jf?jcuOmo_N;UuBx%#KHmEggvdb&T-WJ|r={x7yq-FB>K!*C2qE@0fd4<> z>2TL_c)yzbk3;WvJn87WfbZ6pmY)a%xrd$bvoA&omxL)|MjS~7AYm8qoV{tu;;ptqOfbA z9mYgSr`EUAa|qGtsu04GWt|o_0Z1h}y|_JIvejzSZnyS*S!?Zf0mNEMnk85ZrDBQk z@$r4}E?>UPi!Z&*(G&O5+}I$FTGXmbtgTn~{WwAh9)9>?o_zAjeerZIm+u;AW&yY3 z0dgK>rc=k?dU0$@!AYq{bvEiP)*1;>6moRb=a}4IWEK|Y`S!QJy>Bp*WeL@l1+;Kj zoV(7<{8d(#7x;U>`7~$G{=vR@*RIX*)vrI#nfW}`m21SwDz(*BF2DXlr_pmCdiv?7 z`NStau|ETLYenEnrYjy{oY0C5sY&USlMU8ntb_?B6^sw$2;>^|w(eOJfle~gb=^J2 zYs;Mt42_dTEj-_4sFI`P)u*q}$Xkx&ae(v@X`NK2Af&}yu6dvO`0W~egq4*TBib_Pib zL#=ilWvUb%4@W9oBYXeSTBMNl_l?r5y9DD!uGCvZ?TEp#!{jS5rBaSot3?<_Jzb!n zlZ4elzFP%u%`fu88@D+))X&6tCs~rqb&OFfOmH26wHdXjMZLDn+^rj=>j(Mi2T$_X zp{?SC6&7hTqUI9S#W&fgtyL}TV{D}1A)QwGrY1OSFk=`~VgdH&RwU}=W^&^ftbRRm&{~7UqA+L)&9!BUo}zEWN0>H&4jC%=KQ!oh=3D=p&wTn*{EL72 z`y4;>C`$_qtW~F(K68^B*KcDnoVo8o%7Y^~TJ_Ef>s=Y4Am=lA^blF>vA%ARDr0?N z4zYNPaPAex#>Qy7eQYG)lm=-=3BsgrGgZ-9O^VIdR6vOi6O{sFL}_dkLWL47r1tUh zeLWelm3!1$bM?vvKKmeQ+2NU+&#QC(f;=5h;dck#v; zLl}me8JA9uP>7xTu{N2oBlO&F`Fs~IBMhVM`*&t+wrhf$QKZ(L-CfSxWpK3W5M+G! zd(TtMd)Q1+>?>2RHOZCB)K}+01{f2Q&y^^aiu6@Vy%P@Fp;XB8{g=*B%z2dh$7t7A zNK(V_*kR(v3UhZF3{>)*f9*WYu*FB8c%tXp!u0}9pT4WI<747(IcTkAXlUpif$#qP?v|E!BVL-O z+`M@cn;S&#w5IR&mX`llc#i9k&liwb^zw3*Z_Zw$+~0q<({po6iMV^XeZF=5uBOqI z=ifTnc9fg8A&z4zrTks@W$im#KmC|59>ocBbMur+rOsT+W$N`9Ed(e>lf*Ge9Pi=s zw!7IC&u^8(yB*|?#m$Q&lOCj+*_ea?d_#>0a45d#GwK^H^7(*fvqhLB43vv( zG}<_hrc?-6sa7eK2AMc~VA~Q~Ylz~cXGQrZMLSb$LO?FaA-?h5S8Y*eq-lz^5T_}6 zbH{j^W_X^5Rzopr09m~(;s5{u literal 0 HcmV?d00001 diff --git a/app/examples/Image/Lighttable/zoom-in.png b/app/examples/Image/Lighttable/zoom-in.png new file mode 100644 index 0000000000000000000000000000000000000000..660d752a49824dd934984b4579b91b727b68d0f9 GIT binary patch literal 710 zcmV;%0y+JOP)C@hm6V8zg@}uZhl_`XjEjehh=+`ehm4Dei-)ng z!Lqx-h>M4cjEZq{Zyurl1!o`Vekr zi;annk&SS2ZdX}VSzA|CS5sMAS5;S2R#;S4SX9l{-Q?%#&)DG8-Q}de+?l!Bnz`Df zzunK;<+RD)oxR<)&E{N$xLbs{u*&3`y4{$$+`Q4~Ux&H>|NmNqx2wkDk+j;KyV|zR z=3RxjUWT|?g11+Jwx7M;p1azPvesFHw@`hwhOE?yuGCI@vr2ff&)D9l!QFkl(yQIx7$*FwNrkzm$=*7;^^1i;>y_Oq`=>%!r-UF;n?2e+TY{h z=IY1S=)KbBzS8E%*y!Ko>f7Mtm6(>?;Nt1)>$$wPy}!JAdw7S6gtxf1c6f83qMvpi zRo4Ij04{V=PE!C6gU0hn?E6CX4(fly^25e|`}>3H8}=BIg`#6(;~X91?Gh4&ML^by zCMBn&dU>U#Ca1HAfgL8Ek(rg9lbe@cAR#FQ7Lk@IEGjCLm6IOhX;xHfAqy}lYU}6%01S317%7GyO8@`>07*qoM6N<$f`^TGzyJUM literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/.directory b/app/examples/Image/PhotoTouch/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Image/PhotoTouch/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Image/PhotoTouch/.hidden/screenshots/phototouch.jpg b/app/examples/Image/PhotoTouch/.hidden/screenshots/phototouch.jpg new file mode 100644 index 0000000000000000000000000000000000000000..477d6bafb546b62f41548761b9e297c9e4412666 GIT binary patch literal 150309 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%0AWaO?PkFjX*LXB1j2(n$c-E@-_^!N1m;n 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 O0000+?tpk`#oI4} zy#E^@@Ao!%J9r7a1N;bNzn_A5=rg=-1wRMx2fqP1&mTd~>lcvy{0?$Gu7Nwioj2wC zOo1HNZjjf_fV|%!kR2Td+208S!<+-zPsxv80eRi4Ahsq2vY&Gx*XvD?_kS1U_3wGU z4?c$R2jD~C74QM@C;$Jd=bs?Q^OqlA^SllU*x5PfxeMg|p8;7PGaw#1?d>g)>(B=8 z0yW6~&U?NC^7_jl*ZUKY>+}V<7yK6FIQ{@11pfx7z)2W6kLN-5+X8u?1mwIe$njkS zSx0aA|NL*<_W6$|K`uMjmt)}b=9+TjSm1J`e;#w=e7M$}2e)Z-&W-(XO<8v+()q7;9C>X%o{N&B;i{9Yu3dW@*lvl~jpjV4O*5-kL<{q3kP~&m5H~qy_DS zGh#)@U9&<9kxsN*mM)UnfXU#MOWQ|QG^J8Du>4b8F=X`(u>MJmNO9hx9ALH+(%4HbjFyWI5<1IC;!rMPJ;qAtT~4W3gvELN!b+jFN#Jme>ylBilwmG zD%Wchje4P}mNow@l#o|Zyc`D2LbYBf1>#h_SS{~AQ?G>+6`8uiqLpzPNrYDv3++lp z)Pia#j)v8sTsaYxO3kpGK45a+1$>mQ0p z3MX}h)9OMO(^!RoAQy)nW31j(tQsP(Eq}(!&FvfFO_d$VH^@3!th|J~NK$l^uu}u#$9jTF~9i}xYj3WiVqeR{M$DILW)Kp!K&{zcs zIF~iP1?HukUSDRI_g{P08tou?Y-&|sr_N&aBD5#e7Q_6&Kw#`~Yw(EFSGsRyGSCR4 zrkqo5eT{-%Ps1UW$ow$;6ynRFGCRtWZ;Q8!ly8vkX{;>|6O*QzFAr+Lis|!4{se\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Simple Photo Editor" +msgstr "Editeur de photographie simplifié" + +#: .project:2 +msgid "This is a simple photo editor I made for my father. It is too complex for him, but it is still a good Gambas example!" +msgstr "" + +#: FBrightness.form:40 FMain.class:200 +msgid "Balance" +msgstr "Balance" + +#: FMain.class:107 +msgid "No image in directory" +msgstr "Aucune image dans ce répertoire" + +#: FMain.class:109 +msgid "Unable to load image" +msgstr "Impossible de charger l'image" + +#: FMain.class:200 +msgid "Automatic correction" +msgstr "Correction automatique" + +#: FMain.class:200 +msgid "Blur" +msgstr "Flou" + +#: FMain.class:200 +msgid "Browse photos" +msgstr "Parcourir les photos" + +#: FMain.class:200 +msgid "Crop image" +msgstr "Découper l'image" + +#: FMain.class:200 +msgid "Fit to window" +msgstr "Zoom à la taille de l'écran" + +#: FMain.class:200 +msgid "Flip horizontally" +msgstr "Retourner horizontalement" + +#: FMain.class:200 +msgid "Flip vertically" +msgstr "Retourner verticalement" + +#: FMain.class:200 +msgid "Invert" +msgstr "Inverser" + +#: FMain.class:200 +msgid "Normalize" +msgstr "Normaliser" + +#: FMain.class:200 +msgid "Oil painting effect" +msgstr "Effet de peinture à l'huile" + +#: FMain.class:200 +msgid "Quit" +msgstr "Quitter" + +#: FMain.class:200 +msgid "Remove speckles" +msgstr "Enlever les tâches" + +#: FMain.class:200 FResize.form:42 +msgid "Resize" +msgstr "Redimensionner" + +#: FMain.class:200 +msgid "Rotate left" +msgstr "Rotation vers la gauche" + +#: FMain.class:200 +msgid "Rotate right" +msgstr "Rotation vers la droite" + +#: FMain.class:200 +msgid "Save" +msgstr "Enregistrer" + +#: FMain.class:200 +msgid "Save all" +msgstr "Tout enregistrer" + +#: FMain.class:200 +msgid "Select photo directory" +msgstr "Choisir le répertoire des photographies" + +#: FMain.class:200 +msgid "Sharpen" +msgstr "Netteté" + +#: FMain.class:200 +msgid "Show photo" +msgstr "Afficher la photographie" + +#: FMain.class:200 +msgid "Undo all changes" +msgstr "Annuler tous les changements" + +#: FMain.class:200 +msgid "Zoom 100%" +msgstr "Zoom 100%" + +#: FMain.class:200 +msgid "Zoom in" +msgstr "Zoom avant" + +#: FMain.class:200 +msgid "Zoom out" +msgstr "Zoom arrière" + +#: FMain.class:222 +msgid "*" +msgstr "*" + +#: FMain.class:222 +msgid "/" +msgstr "/" + +#: FMain.class:222 +msgid "B" +msgstr "F" + +#: FMain.class:222 +msgid "D" +msgstr "T" + +#: FMain.class:222 +msgid "E" +msgstr "E" + +#: FMain.class:222 +msgid "H" +msgstr "H" + +#: FMain.class:222 +msgid "I" +msgstr "I" + +#: FMain.class:222 +msgid "M" +msgstr "M" + +#: FMain.class:222 +msgid "N" +msgstr "N" + +#: FMain.class:222 +msgid "O" +msgstr "P" + +#: FMain.class:222 +msgid "R" +msgstr "R" + +#: FMain.class:222 +msgid "S" +msgstr "S" + +#: FMain.class:222 +msgid "V" +msgstr "V" + +#: FScissors.form:24 +msgid "Cut" +msgstr "Découper" diff --git a/app/examples/Image/PhotoTouch/.lang/nl.mo b/app/examples/Image/PhotoTouch/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..8d575baccb63d5624a83379e10ca87f658d6e1e1 GIT binary patch literal 2345 zcmZ{kO^j4U5XW1^4@N{jL{U*26;WY^-4zm8)VQCpk!4^%AbRVaS2M47-s^sL_sp=w zlLzr&ycs=tFq#-|CMF*IypW(qkHmw~#7OjjF&;EtjQ;Dr9?*ktU-xgit6p_gb=7{i zW!9U=92m z#4BnLvYm~Qn?Tmv0&WH+cq=#-<&%-q;0@THjqCZyCb$jzC&Be#C+u$NvGyeqI1Kf)_#d?-Gbt{Dg_+zk>YTUm)MdCIrd)J3!9E9`GDE3GM{H0Ph1Y zgKTdDoM1mTVuA^=9fU|okmW2JK6~Pi2SB8~w9jX9e7HvO5C;3scDNsg59fhv{*Jiu zFvvb~9o&h*dB!z}`!RN7@WVAde0Jdn`}AnsxMwH;IVNs3&H?9DVsKu#XSg5l!r+Hv z=I8DTVmtUC#v>T_^4_CjpP1+co2#H(vSpp4R?yanNinH1Wm+UAvz`-^&JH}uBDKMK zG39I_b*?%jrg}k4iy1MigA7&Fb z?fIJp8&n{fQ5#CtZ>KwH5Q}0_^{JdBoDqwZ!CDpEzfNCFl`F`I#ncWep;**;k&!&X zX^=CChL>U~)n4LN(pSyboRhgqNVct$`I>C2kdkZ2xxilpYmsucc&1cHb-q`w z^dKw6KIkli*^ae|JfiXy)e$WTARz! zk&#{1FKsGXT-ym}R}pu8E~%d)=W*-fRb)%H*GlDMjRTECBefIF`U3Sei|f5A2uAOH;LD%7^-rQ^scsSs9n7;ryChP!zm;e87vR66#ZCiv}qd zQlILAdMG4nSl4+H227`iiW-+DtIf~N&s=qCv@uefLLrgC5o-lh0-Xu_iVWp&m}7OG z!V}9&v-N{l_1Kj*x%!N0*#tE+E)TBgP!o0h4J+QaQo2&~1VLHzNE^96TtOF9W#7hi z@AhJ%y+V|+G3kw7O`!0!(FNB?X;qm*c~3?vWn`a>59GwrVHcCl%18 zt7kRGcTAhEm(IdbtQM%1;b^&GIUDil)geu0*ePB^u>wwozg-!+}-sF=M!S VShAQCzT#YUA+P^mkMTdJe*hf#6`}wD literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/.lang/nl.po b/app/examples/Image/PhotoTouch/.lang/nl.po new file mode 100644 index 00000000..0676709e --- /dev/null +++ b/app/examples/Image/PhotoTouch/.lang/nl.po @@ -0,0 +1,183 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PhotoTouch 3.5.90\n" +"POT-Creation-Date: 2014-09-27 00:14 UTC\n" +"PO-Revision-Date: 2014-09-25 21:54 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Simple Photo Editor" +msgstr "Eenvoudige Foto Editor" + +#: .project:2 +msgid "This is a simple photo editor I made for my father. It is too complex for him, but it is still a good Gambas example!" +msgstr "Dit is een eenvoudige foto editor die ik voor mijn vader maakte. Het is te complex voor hem, maar nog steeds een goed Gambas voorbeeld!" + +#: FBrightness.form:40 FMain.class:200 +msgid "Balance" +msgstr "Balans" + +#: FMain.class:107 +msgid "No image in directory" +msgstr "Geen afbeeldingsmap" + +#: FMain.class:109 +msgid "Unable to load image" +msgstr "Onmogelijk om afbeelding te laden" + +#: FMain.class:200 +msgid "Automatic correction" +msgstr "Automatische correctie" + +#: FMain.class:200 +msgid "Blur" +msgstr "Vervagen" + +#: FMain.class:200 +msgid "Browse photos" +msgstr "Browse foto's" + +#: FMain.class:200 +msgid "Crop image" +msgstr "Afbeelding snijden" + +#: FMain.class:200 +msgid "Fit to window" +msgstr "Aanpassen aan venster" + +#: FMain.class:200 +msgid "Flip horizontally" +msgstr "Horizontaal spiegelen" + +#: FMain.class:200 +msgid "Flip vertically" +msgstr "Verticaal spiegelen" + +#: FMain.class:200 +msgid "Invert" +msgstr "Omkeren" + +#: FMain.class:200 +msgid "Normalize" +msgstr "Normaliseren" + +#: FMain.class:200 +msgid "Oil painting effect" +msgstr "Olieverf effect" + +#: FMain.class:200 +msgid "Quit" +msgstr "Afsluiten" + +#: FMain.class:200 +msgid "Remove speckles" +msgstr "Verwijder spikkels" + +#: FMain.class:200 FResize.form:42 +msgid "Resize" +msgstr "Formaat wijzigen" + +#: FMain.class:200 +msgid "Rotate left" +msgstr "Links roteren" + +#: FMain.class:200 +msgid "Rotate right" +msgstr "Rechts roteren" + +#: FMain.class:200 +msgid "Save" +msgstr "Opslaan" + +#: FMain.class:200 +msgid "Save all" +msgstr "Alles opslaan" + +#: FMain.class:200 +msgid "Select photo directory" +msgstr "Selecteer foto map" + +#: FMain.class:200 +msgid "Sharpen" +msgstr "Verscherpen" + +#: FMain.class:200 +msgid "Show photo" +msgstr "Foto weergeven" + +#: FMain.class:200 +msgid "Undo all changes" +msgstr "Alle wijzigingen ongedaan maken" + +#: FMain.class:200 +msgid "Zoom 100%" +msgstr "Zoom 100%" + +#: FMain.class:200 +msgid "Zoom in" +msgstr "Zoom in" + +#: FMain.class:200 +msgid "Zoom out" +msgstr "Zoom uit" + +#: FMain.class:222 +msgid "*" +msgstr "-" + +#: FMain.class:222 +msgid "/" +msgstr "-" + +#: FMain.class:222 +msgid "B" +msgstr "-" + +#: FMain.class:222 +msgid "D" +msgstr "-" + +#: FMain.class:222 +msgid "E" +msgstr "-" + +#: FMain.class:222 +msgid "H" +msgstr "-" + +#: FMain.class:222 +msgid "I" +msgstr "-" + +#: FMain.class:222 +msgid "M" +msgstr "-" + +#: FMain.class:222 +msgid "N" +msgstr "-" + +#: FMain.class:222 +msgid "O" +msgstr "-" + +#: FMain.class:222 +msgid "R" +msgstr "-" + +#: FMain.class:222 +msgid "S" +msgstr "-" + +#: FMain.class:222 +msgid "V" +msgstr "-" + +#: FScissors.form:24 +msgid "Cut" +msgstr "Knippen" diff --git a/app/examples/Image/PhotoTouch/.project b/app/examples/Image/PhotoTouch/.project new file mode 100644 index 00000000..aa188e8f --- /dev/null +++ b/app/examples/Image/PhotoTouch/.project @@ -0,0 +1,24 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Simple Photo Editor +Startup=FMain +Icon=icon.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.desktop.x11 +Component=gb.desktop +Component=gb.form.dialog +Component=gb.settings +Component=gb.image.effect +Description="Photo editor example.\n\nThis is a simple photo editor I made for my father. It has an overlay GUI with transparent buttons and state transitions. It is too complex for him, but it is still a good Gambas example!" +Authors="Benoît Minisini" +TabSize=2 +Translate=1 +Language=fr +Vendor=Example +Packager=1 +Tags=Example,2DGraphics,ImageProcessing,Graphics +Screenshot=.hidden/screenshots/phototouch.jpg +CreateMenu=1 diff --git a/app/examples/Image/PhotoTouch/.src/CAnimation.class b/app/examples/Image/PhotoTouch/.src/CAnimation.class new file mode 100644 index 00000000..6884983b --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/CAnimation.class @@ -0,0 +1,97 @@ +' Gambas class file + +Static Private All As New CAnimation[] + +Property Read Object As Object + +Private $hObject As Object +Private $sProperty As String +Private $fTarget As Float +Private $fTime As Float +Private $hTimer As Timer + +Static Public Sub Start(hObject As Control, sProperty As String, fTarget As Float, iTime As Integer) + + Dim hAnim As CAnimation + Dim aStop As New CAnimation[] + + For Each hAnim In All + If hAnim.Object = hObject Then aStop.Add(hAnim) + Next + + For Each hAnim In aStop + hAnim.Stop + Next + + hAnim = New CAnimation(hObject, sProperty, fTarget, iTime) + All.Add(hAnim) + +End + +Static Public Sub Exit() + + While All.Count + All[0].Stop + Wend + +End + + + +Public Sub _new(hObject As Control, sProperty As String, fTarget As Float, iTime As Integer) + + 'Debug hObject.Tag;; sProperty;; fTarget + + $hObject = hObject + $sProperty = sProperty + $fTarget = fTarget + $fTime = Timer + iTime / 1000 + + $hTimer = New Timer As "Timer" + $hTimer.Delay = 50 + $hTimer.Start + +End + +Public Sub Timer_Timer() + + Dim fValue As Float + Dim iSign As Integer + + fValue = Object.GetProperty($hObject, $sProperty) + + If Abs(fValue - $fTarget) < 1E-6 Then + Object.SetProperty($hObject, $sProperty, $fTarget) + {Stop} + Else + iSign = Sgn($fTarget - fValue) + fValue += ($fTarget - fValue) / (1000 * ($fTime - Timer) / $hTimer.Delay) + If Sgn($fTarget - fValue) <> iSign Then + Object.SetProperty($hObject, $sProperty, $fTarget) + {Stop} + Else + Object.SetProperty($hObject, $sProperty, fValue) + Endif + Endif + +Catch + + {Stop} + +End + +Public Sub Stop() + + $hTimer.Stop + $hTimer = Null + $hObject = Null + + All.Remove(All.Find(Me)) + +End + +Private Function Object_Read() As Object + + Return $hObject + +End diff --git a/app/examples/Image/PhotoTouch/.src/CButton.class b/app/examples/Image/PhotoTouch/.src/CButton.class new file mode 100644 index 00000000..d4313720 --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/CButton.class @@ -0,0 +1,189 @@ +' Gambas class file + +Export + +Inherits DrawingArea + +Event Click + +Property Image As Image +Property Opacity As Float +Property Highlight As Boolean +Property Shortcut As String + +Public Const MIN_OPACITY As Float = 0.3 +Public Const MAX_OPACITY As Float = 0.8 + +Private $hObs As Observer +Private $hImage As Image +Private $hDraw As Image +'Private $bInside As Boolean +'Private $hTimer As Timer +Private $fOpacity As Float = MIN_OPACITY +Private $bHighlight As Boolean +Private $hTimer As Timer +Private $sShortcut As String + +Public Sub _new() + + $hObs = New Observer(Me) As "DrawingArea" + '$hTimer = New Timer As "Timer" + '$hTimer.Delay = 50 + Me.Mouse = Mouse.Pointing + $hTimer = New Timer As "Timer" + +End + +Public Sub DrawingArea_Enter() + + 'Debug + + If Not Me.Enabled Then Return + CAnimation.Start(Me, "Opacity", MAX_OPACITY, 250) + +End + +Public Sub DrawingArea_Leave() + + 'Debug + + If Not Me.Enabled Then Return + CAnimation.Start(Me, "Opacity", MIN_OPACITY, 250) + +End + +Public Sub DrawingArea_MouseUp() + + If Not Me.Enabled Then Return + Raise Click + Stop Event + +End + +'Public Sub DrawingArea_DblClick() +' +' Raise Click +' Stop Event +' +'End + + +' Public Sub Timer_Timer() +' +' If $bInside Then +' $fOpacity = Min(MAX_OPACITY, $fOpacity + 0.1) +' If $fOpacity >= MAX_OPACITY Then +' $hTimer.Stop +' Endif +' Else +' $fOpacity = Max(MIN_OPACITY, $fOpacity - 0.1) +' If $fOpacity <= MIN_OPACITY Then +' $hTimer.Stop +' Endif +' Endif +' +' SetOpacity +' +' End + + +Public Sub DrawingArea_Draw() + + If $hDraw Then Draw.Image($hDraw, 2, 2) + + If $bHighlight Then + Paint.Begin(Me) + Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 192)) + Paint.Rectangle(0, 0, Me.W, Me.H) + Paint.Fill + Paint.End + Endif + +End + +Private Function Image_Read() As Image + + Return $hImage + +End + +Private Sub Image_Write(Value As Image) + + $hImage = Value.Stretch(Me.W - 4, Me.H - 4) + SetOpacity + +End + +Private Sub SetOpacity() + + Dim X, Y As Integer + + $hDraw = $hImage.Copy() + If $sShortcut Then + + Paint.Begin($hDraw) + + Paint.Font = Font["Bold,+3"] + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 64)) + 'For X = -2 To 2 + ' For Y = -2 To 2 + ' Paint.DrawText($sShortcut, $hDraw.W - 16 + X, $hDraw.H - 16 + Y, 16, 16, Align.Center) + ' Next + 'Next + Paint.Arc($hDraw.W - 12, $hDraw.H - 12, 12) + Paint.Fill + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.Black, 64)) + Paint.DrawText($sShortcut, $hDraw.W - 24, $hDraw.H - 24, 24, 24, Align.Center) + + Paint.End + + Endif + + $hDraw.Opacity($fOpacity) + Me.Refresh + +End + +Private Function Opacity_Read() As Float + + Return $fOpacity + +End + +Private Sub Opacity_Write(Value As Float) + + 'Debug Value + $fOpacity = Max(0, Min(1, Value)) + SetOpacity + +End + +Private Function Highlight_Read() As Boolean + + Return $bHighlight + +End + +Private Sub Highlight_Write(Value As Boolean) + + $bHighlight = Value + Me.Refresh + +End + + +Private Function Shortcut_Read() As String + + Return $sShortcut + +End + +Private Sub Shortcut_Write(Value As String) + + $sShortcut = Value + SetOpacity + Me.Refresh + +End diff --git a/app/examples/Image/PhotoTouch/.src/FBrightness.class b/app/examples/Image/PhotoTouch/.src/FBrightness.class new file mode 100644 index 00000000..8a9bc6df --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/FBrightness.class @@ -0,0 +1,146 @@ +' Gambas class file + +Private $bNoChange As Boolean + +Private $MX As Integer +Private $MY As Integer +Private $hRect As Rect +Private $bBegin As Boolean + +Private btnUndo As CButton +Private btnApply As CButton +Private btnBrightness As CButton +Private btnContrast As CButton +Private btnGamma As CButton + +Public Sub _new() + + btnUndo = New CButton(Me) As "btnUndo" + btnUndo.Move(Me.W - 96 - Desktop.Scale * 4, Desktop.Scale * 3, 48, 48) + btnUndo.Image = Image.Load("undo.png") + + btnApply = New CButton(Me) As "btnApply" + btnApply.Move(btnUndo.X + btnUndo.W + Desktop.Scale, btnUndo.Y, 48, 48) + btnApply.Image = Image.Load("ok.png") + + btnBrightness = New CButton(Me) + btnBrightness.Enabled = False + btnBrightness.Move(Desktop.Scale * 3, Desktop.Scale * 10, 48, 48) + btnBrightness.Image = Image.Load("brightness.png") + btnBrightness.Shortcut = "B" + sldBrightness.Move(btnBrightness.X + 64, btnBrightness.Y + 4) + sldBrightness.Tag = btnBrightness + + btnContrast = New CButton(Me) + btnContrast.Enabled = False + btnContrast.Move(btnBrightness.X, btnBrightness.Y + 64, 48, 48) + btnContrast.Image = Image.Load("contrast.png") + btnContrast.Shortcut = "C" + sldContrast.Move(btnContrast.X + 64, btnContrast.Y + 4) + sldContrast.Tag = btnContrast + + btnGamma = New CButton(Me) + btnGamma.Enabled = False + btnGamma.Move(btnBrightness.X, btnContrast.Y + 64, 48, 48) + btnGamma.Image = Image.Load("gamma.png") + btnGamma.Shortcut = "G" + sldGamma.Move(btnGamma.X + 64, btnGamma.Y + 4) + sldGamma.Tag = btnGamma + +End + + +Public Sub Form_Open() + + Me.Center + +End + +Public Sub btnUndo_Click() + + $bNoChange = True + sldBrightness.Value = 50 + sldContrast.Value = 50 + sldGamma.Value = 50 + $bNoChange = False + Balance_Change + +End + +Public Sub btnApply_Click() + + If $bBegin Then FMain.Apply + +End + +Public Sub Balance_Change() + + If $bNoChange Then Return + If Not $bBegin Then + FMain.Begin + $bBegin = True + Endif + FMain.Balance(sldBrightness.Value, sldContrast.Value, sldGamma.Value) + +End + +Public Sub panBrightness_MouseDown() + + $MX = Mouse.ScreenX + $MY = Mouse.ScreenY + $hRect = Rect(Me.X, Me.Y, Me.W, Me.H) + +End + +Public Sub panBrightness_MouseMove() + + Dim X, Y As Integer + + X = Min(Max($hRect.X + Mouse.ScreenX - $MX, 0), FMain.W - $hRect.W) + Y = Min(Max($hRect.Y + Mouse.ScreenY - $MY, 0), FMain.H - $hRect.H) + Me.Move(X, Y) + +End + +Public Sub Form_KeyPress() + + If Key.Code = Key.Backspace Then + btnUndo_Click + Stop Event + Return + Endif + + If Key.Code = Key.Return Or If Key.Code = Key.Enter Then + btnApply_Click + Stop Event + Return + Endif + + Select Case UCase(Key.Text) + Case "B" + sldBrightness.SetFocus + Case "C" + sldContrast.SetFocus + Case "G" + sldGamma.SetFocus + Case Else + FMain.Form_KeyPress + Return + End Select + + Stop Event + +End + +Public Sub Balance_GotFocus() + + CAnimation.Start(Last.Tag, "Opacity", CButton.MAX_OPACITY, 250) + +End + +Public Sub Balance_LostFocus() + + CAnimation.Start(Last.Tag, "Opacity", CButton.MIN_OPACITY, 250) + +End + diff --git a/app/examples/Image/PhotoTouch/.src/FBrightness.form b/app/examples/Image/PhotoTouch/.src/FBrightness.form new file mode 100644 index 00000000..37326354 --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/FBrightness.form @@ -0,0 +1,37 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,74,40) + Background = &HFFFFFF& + Border = False + Utility = True + Stacking = Window.Above + SkipTaskbar = True + Opacity = 50 + { panBrightness Panel + MoveScaled(1,1,72,38) + Background = &H000000& + { sldBrightness Slider Balance + Name = "sldBrightness" + MoveScaled(13,13,56,3) + Value = 50 + } + { sldContrast Slider Balance + Name = "sldContrast" + MoveScaled(13,22,56,3) + Value = 50 + } + { Label1 Label + MoveScaled(2,2,52,6) + Font = Font["Bold,+5"] + Foreground = Color.TextBackground + AutoResize = True + Text = ("Balance") + } + { sldGamma Slider Balance + Name = "sldGamma" + MoveScaled(13,31,56,3) + Value = 50 + } + } +} diff --git a/app/examples/Image/PhotoTouch/.src/FMain.class b/app/examples/Image/PhotoTouch/.src/FMain.class new file mode 100644 index 00000000..f0c33a07 --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/FMain.class @@ -0,0 +1,1117 @@ +' Gambas class file + +Private CACHE_ROOT As String = "~/.cache/PhotoTouch" +Private CACHE_DIR As String + +Private $aPath As String[] +Private $sPath As String +Private $sDir As String +Private $iIndex As Integer +Private $hImage As Image +Private $hZoom As Image +Private $hTemp As Image +Private $fZoom As Float = 1 +'Private $sInfo As String +Private $sMode As String +Private $bModify As Boolean +Private $cButton As New Collection +Private $bFilm As Boolean = True +Private $bReloadFilm As Boolean +Private $cShortcut As New Collection +Private $hSave As Image +Private $hUndoStack As Image[] + +Private $MX As Integer +Private $MY As Integer + +Private btnPrev As CButton +Private btnNext As CButton +Private dwgPath As DrawingArea + +Private Sub UpdateSaveIcon() + + $cButton["save"].Visible = Exist(CACHE_DIR &/ File.Name($sPath)) Or $bModify + +End + +Private Sub UpdateFilmMode() + + Dim sTag As String + Dim hCtrl As Control + + If $bFilm Then + 'ivwImage.IconSize = 128 + svwImage.Hide + panToolbar.Hide + panBrowser.Show + panToolbarBrowser.Show + ivwImage.SetFocus + Else + svwImage.Show + panToolbar.Show + panBrowser.Hide + panToolbarBrowser.Hide + $cButton["film"].Opacity = CButton.MAX_OPACITY + svwImage.SetFocus + Endif + + btnPrev.Visible = Not $bFilm + btnNext.Visible = Not $bFilm + + If $bFilm Then + $hImage = Null + FillImageBrowser + Else + Try $iIndex = CInt(ivwImage.Key) + LoadImage + Endif + +End + +Private Sub GetImage(Optional bReset As Boolean) As Boolean + + Dim hImage As Image + + If Not bReset Then + Try $sPath = CACHE_DIR &/ $aPath[$iIndex] + If Exist($sPath) Then Try hImage = Image.Load($sPath) + Endif + + If Not hImage Then + Try $sPath = $sDir &/ $aPath[$iIndex] + If $sPath Then Try hImage = Image.Load($sPath) + Endif + + $hImage = hImage + +End + + +Private Sub LoadImage(Optional bReset As Boolean) As Boolean + + Inc Application.Busy + + Me.End + SaveImage + + GetImage(bReset) + + If $hImage Then + svwImage.ResizeContents($hImage.W, $hImage.H) + svwImage.Show + lblError.Hide + Else + svwImage.Hide + lblError.Show + If $aPath.Count = 0 Then + lblError.Text = ("No image in directory") + Else + lblError.Text = ("Unable to load image") + Endif + Endif + + $bModify = False + $hUndoStack = New Image[] + SetZoom(0) + UpdateSaveIcon + dwgInfo.Raise + dwgInfo.Refresh + Me.Refresh + + Dec Application.Busy + +End + +Private Sub CreateButtons(aButton As String[], cTooltip As Collection, hParent As Container) + + Dim sKey As String + Dim sImg As String + Dim hPanel As Panel + Dim hButton As CButton + Dim iPos As Integer + + For Each sKey In aButton + + If sKey = "<->" Then + hPanel = New Panel(hParent) + hPanel.Expand = True + hPanel.Resize(8, 48) + Else If sKey = "-" Then + hPanel = New Panel(hParent) + hPanel.Resize(8, 48) + Else + + sImg = sKey + iPos = InStr(sImg, "#") + If iPos Then sImg = Left(sImg, iPos - 1) + + hButton = New CButton(hParent) As "Button" + hButton.Resize(48, 48) + hButton.Image = Image.Load(sImg & ".png") + hButton.Tag = sKey + hButton.Tooltip = cTooltip[sImg] + hButton.Shortcut = $cShortcut[sImg] + $cButton[hButton.Tag] = hButton + Endif + Next + +End + + +Public Sub _new() + + Dim hCtrl As Control + Dim sImg As String + Dim hButton As CButton + Dim cTooltip As Collection + Dim aButton As String[] + + Application.MainWindow = Me + + Try Mkdir File.Dir(File.Dir(CACHE_ROOT)) + Try Mkdir File.Dir(CACHE_ROOT) + Try Mkdir CACHE_ROOT + + cTooltip = [ + "film": ("Browse photos"), + "photo": ("Show photo"), + "usb": ("Select photo directory"), + "zoom-in": ("Zoom in"), + "zoom-out": ("Zoom out"), + "zoom-original": ("Zoom 100%"), + "zoom-fit": ("Fit to window"), + "hflip": ("Flip horizontally"), + "vflip": ("Flip vertically"), + "rotate-left": ("Rotate left"), + "rotate-right": ("Rotate right"), + "magic": ("Automatic correction"), + "invert": ("Invert"), + "blur": ("Blur"), + "sharpen": ("Sharpen"), + "normalize": ("Normalize"), + "despeckle": ("Remove speckles"), + "oil": ("Oil painting effect"), + "scissors": ("Crop image"), + "balance": ("Balance"), + "resize": ("Resize"), + "save": ("Save"), + "save-all": ("Save all"), + "undo": ("Undo all changes"), + "quit": ("Quit")] + + $cShortcut = [ + "usb": "!", + "zoom-in": "+", + "zoom-out": "-", + "zoom-original": "1", + "zoom-fit": "0", + "hflip": ("H"), + "vflip": ("V"), + "rotate-left": "(", + "rotate-right": ")", + "magic": ("M"), + "invert": ("I"), + "blur": ("B"), + "sharpen": ("N"), + "normalize": ("E"), + "despeckle": ("D"), + "oil": ("O"), + "scissors": ("/"), + "resize": ("R"), + "balance": ("*"), + "save": ("S")] + + aButton = ["film", "-", "zoom-in", "zoom-out", "zoom-original", "zoom-fit", "-", "hflip", "vflip", "rotate-left", "rotate-right", "-", + "magic", "invert", "blur", "sharpen", "normalize", "despeckle", "oil", "-", "scissors", "resize", "balance", "<->", "save", "undo", "quit"] + CreateButtons(aButton, cTooltip, panToolbar) + + aButton = ["photo", "usb", "save-all", "-"] + CreateButtons(aButton, cTooltip, panToolbarBrowser) + + dwgPath = New DrawingArea(panToolbarBrowser) As "dwgPath" + dwgPath.Expand = True + dwgPath.Font = Font["Bold,+5"] + + aButton = ["hflip#2", "vflip#2", "rotate-left#2", "rotate-right#2", "-", "save#2", "undo#2", "quit#2"] + CreateButtons(aButton, cTooltip, panToolbarBrowser) + + btnPrev = New CButton(Me) As "Button" + btnPrev.Resize(64, 64) + btnPrev.Ignore = True + btnPrev.Tag = "previous" + btnPrev.Image = Image.Load("previous.png") + + btnNext = New CButton(Me) As "Button" + btnNext.Resize(64, 64) + btnNext.Ignore = True + btnNext.Tag = "next" + btnNext.Image = Image.Load("next.png") + + panToolbar.Raise + panToolbarBrowser.Raise + +End + + +Public Sub svwImage_Draw() + + Dim X As Integer + Dim Y As Integer + Dim XR As Integer + Dim YR As Integer + Dim SX, SX2 As Integer + Dim SY, SY2 As Integer + Dim DX As Integer + Dim DY As Integer + Dim C As Integer + Dim SW, SH As Integer + Dim hZoom As Image + Dim iZoom As Integer + + If Not $hImage Then Return + + If $fZoom > 1 Then + + iZoom = $fZoom + + Draw.LineStyle = Line.None + Draw.FillStyle = Fill.Solid + + DX = Max(0, (Me.W - $hImage.W * iZoom) / 2) + DY = Max(0, (Me.H - $hImage.H * iZoom) / 2) + + SX = (Draw.Clip.X - DX) \ iZoom + SY = (Draw.Clip.Y - DY) \ iZoom + SX2 = (Draw.Clip.X - DX + Draw.Clip.W - 1) \ iZoom + SY2 = (Draw.Clip.Y - DY + Draw.Clip.H - 1) \ iZoom + + SX = Max(0, SX) + SX2 = Min($hImage.Width - 1, SX2) + SW = SX2 - SX + 1 + + SY = Max(0, SY) + SY2 = Min($hImage.Height - 1, SY2) + SH = SY2 - SY + 1 + + 'If $fZoom > 5 Then + ' Draw.LineStyle = Line.Solid + ' Draw.Foreground = &H989898 + 'Else + ' Draw.LineStyle = Line.None + 'Endif + + Draw.Zoom($hImage, iZoom, SX * iZoom + DX, SY * iZoom + DY, SX + svwImage.ScrollX \ iZoom, SY + svwImage.ScrollY \ iZoom, SW, SH) + + Else If $fZoom = 1 Then + + If $hImage.W < Me.W Then + X = (Me.W - $hImage.W) / 2 + Else + X = - svwImage.ScrollX + Endif + + If $hImage.H < Me.H Then + Y = (Me.H - $hImage.H) / 2 + Else + Y = - svwImage.ScrollY + Endif + + Draw.Image($hImage, X, Y) + + Else + + If $hZoom.W < Me.W Then + X = (Me.W - $hZoom.W) / 2 + Else + X = - svwImage.ScrollX + Endif + + If $hZoom.H < Me.H Then + Y = (Me.H - $hZoom.H) / 2 + Else + Y = - svwImage.ScrollY + Endif + + Draw.Image($hZoom, X, Y) + + Endif + +End + +Public Sub Form_Resize() + + panToolbar.Move(0, 0, Me.W, 48 + Desktop.Scale * 2) + panToolbarBrowser.Move(0, 0, Me.W, 48 + Desktop.Scale * 2) + panMargin.H = panToolbar.H + dwgInfo.Move(8, Me.H - dwgInfo.H, Me.W, dwgInfo.H) + btnPrev.Move(8, (Me.H - btnPrev.H) / 2) + btnNext.Move(Me.W - 8 - btnNext.W, (Me.H - btnNext.H) / 2) + +End + +Public Sub Button_Click() + + Action(Last.Tag) + +End + +Private Sub UpdateZoom() + + Dim X, Y As Float + + If Not $hImage Then Return + + If $fZoom < 1 Then + $hZoom = $hImage.Stretch($hImage.W * $fZoom, $hImage.H * $fZoom) + Else + $hZoom = Null + Endif + + X = (svwImage.ScrollX + svwImage.ClientW / 2) / svwImage.ScrollW + Y = (svwImage.ScrollY + svwImage.ClientH / 2) / svwImage.ScrollH + + svwImage.ResizeContents($hImage.W * $fZoom, $hImage.H * $fZoom) + svwImage.Scroll(X * svwImage.ScrollW - svwImage.ClientW / 2, Y * svwImage.ScrollH - svwImage.ClientH / 2) + + dwgInfo.Refresh + Me.Refresh + +End + +Private Sub SetZoom(fZoom As Float) + + If Not $hImage Then Return + + If fZoom = 0 Then + $fZoom = 0 + fZoom = Min(Me.W / $hImage.W, Me.H / $hImage.H) + fZoom = Min(1, fZoom) + Endif + + fZoom = Max(1 / 32, Min(32, fZoom)) + If fZoom = $fZoom Then Return + If fZoom <> 1 Then + If ($hImage.W * fZoom) < 96 Or If ($hImage.H * fZoom) < 96 Then Return + Endif + + $fZoom = fZoom + UpdateZoom + +End + +Public Sub SetMode(sMode As String) + + If $sMode Then + $cButton[$sMode].Highlight = False + Cancel + Select Case $sMode + Case "balance" + FBrightness.Close + Case "scissors" + FScissors.Close + Case "resize" + FResize.Close + End Select + Endif + + If sMode = $sMode Then + $sMode = "" + Return + Endif + + If sMode Then + + $cButton[sMode].Highlight = True + + Select Case sMode + Case "balance" + FBrightness.Show + Case "scissors" + FScissors.Show + Case "resize" + FResize.Show + End Select + + Endif + + $sMode = sMode + +End + + +Private Sub Action(sAction As String) + + Dim iPos As Integer + + Select Case sAction + + Case "usb" + SetMode("") + Dialog.Title = ("Select photo directory") + Dialog.Path = $sDir + If Dialog.SelectDirectory() Then Return + + End Select + + Inc Application.Busy + + iPos = RInStr(sAction, "#") + If iPos Then sAction = Left(sAction, iPos - 1) + + Select Case sAction + + Case "film" + Me.End + SaveImage + $bFilm = True + UpdateFilmMode + + Case "photo" + If $aPath.Count Then + $bFilm = False + UpdateFilmMode + Endif + + Case "previous" + Dec $iIndex + If $iIndex < 0 Then $iIndex = $aPath.Max + LoadImage + + Case "next" + Inc $iIndex + If $iIndex > $aPath.Max Then $iIndex = 0 + LoadImage + + Case "zoom-in" + SetZoom(2 ^ Int(Log2($fZoom)) * 2) + + Case "zoom-out" + SetZoom(2 ^ Int(Log2($fZoom) - 0.001)) + + Case "zoom-original" + SetZoom(1) + + Case "zoom-fit" + SetZoom(0) + + Case "quit" + SetMode("") + SaveImage + Me.Close + + Case "balance", "scissors", "resize" + 'If Not $hImage Then Return + SetMode(sAction) + + Case "usb" + SetDir(Dialog.Path) + + Case "undo" + + If Not $hImage Then + + ivwImage.MoveFirst + While ivwImage.Available + If ivwImage.Item.Selected Then + $iIndex = CInt(ivwImage.Item.Key) + GetImage + RemoveImage + Endif + ivwImage.MoveNext + Wend + + FillImageBrowser(True) + $hImage = Null + + Else + + SetMode("") + If $hUndoStack.Count Then + $hImage = $hUndoStack[$hUndoStack.Max] + $hUndoStack.Remove($hUndoStack.Max) + $bModify = $hUndoStack.Count > 0 + If Not $bModify Then RemoveImage + UpdateZoom + Else + LoadImage(True) + RemoveImage + Endif + + Endif + + Case "save" + + If Not $hImage Then + + $iIndex = CInt(ivwImage.Key) + $sPath = CACHE_DIR &/ File.Name($aPath[$iIndex]) + If Exist($sPath) Then + SaveImageDefinitely + FillImageBrowser(True) + Endif + $hImage = Null + + Else + + SetMode("") + SaveImageDefinitely + + Endif + + Case "save-all" + + ivwImage.MoveFirst + While ivwImage.Available + 'If ivwImage.Item.Selected Then + $iIndex = CInt(ivwImage.Item.Key) + GetImage + SaveImageDefinitely + 'Endif + ivwImage.MoveNext + Wend + + FillImageBrowser(True) + $hImage = Null + + Case Else + + If Not $hImage Then + + ivwImage.MoveFirst + While ivwImage.Available + If ivwImage.Item.Selected Then + $iIndex = CInt(ivwImage.Item.Key) + GetImage + Select Case sAction + Case "hflip" + $hImage.Mirror(True, False) + Case "vflip" + $hImage.Mirror(False, True) + Case "rotate-left" + $hImage = $hImage.Rotate(Pi(0.5)) + Case "rotate-right" + $hImage = $hImage.Rotate(Pi(-0.5)) + End Select + $bModify = True + SaveImage + Endif + ivwImage.MoveNext + Wend + + FillImageBrowser(True) + $hImage = Null + + Else + + SetMode("") + + Select Case sAction + Case "hflip" + PushUndo() + $hImage.Mirror(True, False) + Case "vflip" + PushUndo() + $hImage.Mirror(False, True) + Case "rotate-left" + PushUndo() + $hImage = $hImage.Rotate(Pi(0.5)) + Case "rotate-right" + PushUndo() + $hImage = $hImage.Rotate(Pi(-0.5)) + Case "oil" + PushUndo() + $hImage = ImageMagick("-paint " & CStr(Max(3, Min($hImage.W, $hImage.H) \ 256))) + Case "magic" + PushUndo() + $hImage = ImageMagick("-auto-gamma -auto-level") + Case "invert" + PushUndo() + $hImage.Invert() + 'Case "equalize" + ' $hImage = ImageMagick("-equalize") + Case "despeckle" + PushUndo() + $hImage = ImageMagick("-despeckle") + Case "normalize" + PushUndo() + $hImage = ImageMagick("-normalize") + Case "blur" + PushUndo() + $hImage = ImageMagick("-blur 8") '$hImage.OilPaint() + Case "sharpen" + PushUndo() + $hImage = ImageMagick("-sharpen 8") '$hImage.OilPaint() + + End Select + + $bModify = True + UpdateSaveIcon + UpdateZoom + + Endif + + End Select + + Dec Application.Busy + +End + +Public Sub svwImage_MouseDown() + + $MX = Mouse.X + svwImage.ScrollX + $MY = Mouse.Y + svwImage.ScrollY + 'Debug $MX;; $MY + +End + +Public Sub svwImage_MouseMove() + + If Mouse.Left Then + 'Debug Mouse.X - $MX;; Mouse.Y - $MY + svwImage.Scroll($MX - Mouse.X, $MY - Mouse.Y) + Endif + +End + +Private Sub PaintOutlineText(sText As String, X As Float, Y As Float, W As Float, H As Float) + + ' Dim I, J As Integer + ' + ' Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 128)) + ' For I = -1 To 1 + ' For J = -1 To 1 + ' Paint.DrawRichText(sText, X + I + 2, Y + J + 2, 4096, H - 4, Align.Left) + ' Next + ' Next + + Paint.Brush = Paint.Color(Color.Black) + Paint.DrawRichTextShadow(sText, X + 2, Y + 2, 4096, H - 4, Align.Left, 4) + Paint.Brush = Paint.Color(Color.White) + Paint.DrawRichText(sText, X + 2, Y + 2, 4096, H - 4, Align.Left) + +End + + +Public Sub dwgInfo_Draw() + + Dim sText As String + Dim sSpace As String + + If Not $hImage Then Return + + With Stat($sPath) + sSpace = String$(6, " ") + sText = "" & Html(File.Name($sPath)) & "" & sSpace & Html(Format(.LastModified, gb.LongDate)) + sText &= sSpace & $hImage.W & " x " & $hImage.H & " (" & Format($fZoom, "0%") & ")" + End With + + PaintOutlineText(sText, 0, 0, dwgInfo.W, dwgInfo.H) + +End + +Public Sub Form_KeyPress() + + Dim sShortcut As String + + Select Case Key.Code + + Case Key.Esc + If $sMode Then + SetMode("") + Else + Action("quit") + Endif + Stop Event + Return + + Case Key.BackSpace + Action("undo") + Stop Event + Return + + End Select + + If Key.Text Then + For Each sShortcut In $cShortcut + If String.UCase(Key.Text) = sShortcut Then + If $cButton[$cShortcut.Key].Parent.Visible Then + Action($cShortcut.Key) + Stop Event + Return + Endif + Endif + Next + Endif + +End + +Public Sub Begin() + + $hTemp = $hImage.Copy() + +End + +Public Sub End() + + $hTemp = Null + UpdateZoom + SetMode("") + 'SaveImage + Me.Refresh + +End + +Public Sub Apply() + + $hTemp = $hImage + {End} + +End + +Public Sub Cancel() + + If Not $hTemp Then Return + $hImage = $hTemp.Copy() + UpdateZoom + Me.Refresh + +End + +Public Sub GetCurrentImage() As Image + + Return $hImage + +End + +Public Sub Balance(iBrightness As Integer, iContrast As Integer, iGamma As Integer) + + $hImage = $hTemp.Copy() + $hImage.Balance((iBrightness - 50) / 50, (iContrast - 50) / 50, (iGamma - 50) / 50) + $bModify = True + UpdateZoom + Me.Refresh + +End + +Public Sub Cut() + + Dim X, Y, W, H As Integer + Dim hRect As Rect + + W = FScissors.W / $fZoom + H = FScissors.H / $fZoom + X = (svwImage.ScrollX + FScissors.X) / $fZoom + Y = (svwImage.ScrollY + FScissors.Y) / $fZoom + + If $hImage.W * $fZoom <= Me.W Then X -= (Me.W / $fZoom - $hImage.W) / 2 + If $hImage.H * $fZoom <= Me.H Then Y -= (Me.H / $fZoom - $hImage.H) / 2 + + hRect = Rect(X, Y, W, H) + hRect = hRect.Intersection(Rect(0, 0, $hImage.W, $hImage.H)) + + If Not hRect Then Return + + $hImage = $hImage.Copy(hRect.X, hRect.Y, hRect.W, hRect.H) + $bModify = True + $hTemp = $hImage + Me.End + SetZoom(0) + +End + +Public Sub Stretch(W As Integer, H As Integer) + + Inc Application.Busy + $hImage = $hImage.Stretch(W, H) + $bModify = True + $hTemp = $hImage + Me.End + SetZoom(0) + Dec Application.Busy + +End + + +Public Sub Form_Activate() + + If $sDir = "" Then SetDir(Settings["Directory", User.Home]) + +End + + +Private Sub SetDir(sDir As String) + + Inc Application.Busy + + 'Shell "cd " & Shell(CACHE_DIR) & "; rm -f *" Wait + + CACHE_DIR = CACHE_ROOT &/ Replace(sDir, "/", ":") + + $sDir = sDir + Settings["Directory"] = $sDir + $aPath = New String[] + Try $aPath = Dir(sDir, "*.{jpg,JPG,jpeg,JPEG,png,PNG,bmp,BMP,gig,GIF}").Sort() + $iIndex = 0 + $bReloadFilm = True + UpdateFilmMode + + Dec Application.Busy + +End + +Private Sub SaveImage() As String + + If Not $hImage Then Return + If Not $bModify Then Return + + Try Mkdir CACHE_DIR + Try Kill CACHE_DIR &/ ".thumb." & File.Name($sPath) + $bReloadFilm = True + $hImage.Save(CACHE_DIR &/ File.Name($sPath), 80) + $bModify = False + UpdateSaveIcon + Return CACHE_DIR &/ File.Name($sPath) + +Catch + + Message.Error(Error.Text) + +End + +Private Sub RemoveImage() + + Try Kill CACHE_DIR &/ File.Name($sPath) + Try Kill CACHE_DIR &/ ".thumb." & File.Name($sPath) + $bReloadFilm = True + If Dir(CACHE_DIR).Count = 0 Then Rmdir CACHE_DIR + UpdateSaveIcon + +Catch + + Message.Error(Error.Text) + +End + +Private Sub SaveImageDefinitely() + + Dim sPath As String + + SaveImage + If Exist(CACHE_DIR &/ File.Name($sPath)) Then + sPath = $sDir &/ File.Name($sPath) + Try Kill sPath & "~" + Move sPath To sPath & "~" + Copy CACHE_DIR &/ File.Name($sPath) To sPath + RemoveImage + Endif + +Catch + + Message.Error(Error.Text) + +End + + +Private Sub ImageMagick(sCommand As String) As Image + + Dim sPath, sPath2 As String + Dim hImage As Image + + Inc Application.Busy + + sPath = File.SetExt(Temp$("image"), File.Ext($sPath)) + sPath2 = File.SetExt(Temp$("image2"), File.Ext($sPath)) + + $hImage.Save(sPath, 100) + + Shell "convert " & Shell(sPath) & " " & sCommand & " " & Shell(sPath2) Wait + hImage = Image.Load(sPath2) + Kill sPath + Kill sPath2 + + Dec Application.Busy + UpdateSaveIcon + + Return hImage + +End + +Private Sub GetThumb(sPath As String) As Image + + Dim sName As String + Dim sThumb As String + Dim hImage As Image + Dim bModified As Boolean + + sName = File.Name(sPath) + + If Exist(CACHE_DIR &/ sName) Then + sPath = CACHE_DIR &/ sName + bModified = True + Endif + + sThumb = CACHE_DIR &/ ".thumb." & sName + If Exist(sThumb) Then + 'If Stat(sThumb).LastModified >= Stat(sPath).LastModified Then + hImage = Image.Load(sThumb) + 'Endif + Endif + + If Not hImage Then + hImage = Image.Load(sPath) + If hImage.W > 256 Or If hImage.H > 256 Then + If hImage.W > hImage.H Then + hImage = hImage.Stretch(256, 256 * hImage.H / hImage.W) + Else + hImage = hImage.Stretch(256 * hImage.W / hImage.H, 256) + Endif + Endif + Paint.Begin(hImage) + Paint.Rectangle(0, 0, hImage.W, hImage.H) + Paint.LineWidth = 0.5 + Paint.Brush = Paint.Color(Color.Black) + Paint.Stroke + If bModified Then + If Not $hSave Then $hSave = Image.Load("save.png").Stretch(24, 24) + Paint.DrawImage($hSave, Paint.W - 24 - 8, 8) + Endif + Paint.End + Try Kill sThumb + hImage.Save(sThumb) + Endif + + Return hImage + +End + +Private Sub GetSize(iSize As Long) As String + + Dim sSize As String + + If iSize < 1024 Then Return iSize & " b" + Return Format(iSize \ 1024, "#,##0") & " K" + +End + + +Private Sub FillImageBrowser(Optional bNoWait As Boolean) + + Dim I As Integer + Dim hImage As Image + Dim sText As String + Dim fLastTime As Float + Dim sKey As String + + If $bReloadFilm Then + + Inc Application.Busy + + fLastTime = Timer + + Try Mkdir CACHE_DIR + + sKey = ivwImage.Key + + ivwImage.Clear + ivwImage.GridSize = 280 \ Desktop.Scale + + For I = 0 To $aPath.Max + + Try hImage = GetThumb($sDir &/ $aPath[i]) + If Error Then + Error Error.Where; ": "; Error.Text + hImage = Picture["icon:/256/image"].Image + Endif + With Stat($sDir &/ $aPath[I]) + sText = "" & Html(File.Name($aPath[I])) & "
    " & Format(.LastModified, gb.LongDate) & "
    " & GetSize(.Size) + End With + ivwImage.Add(I, "", hImage.Picture).RichText = sText + If (Timer - fLastTime) > 1 Then + fLastTime = Timer + Wait + Endif + Next + + Try ivwImage.Key = sKey + + Dec Application.Busy + $bReloadFilm = False + + Endif + + If ivwImage.Count Then + ivwImage.Key = $iIndex + ivwImage[$iIndex].EnsureVisible + panBrowser.Show + '$cButton["photo"].Show + Else + '$cButton["photo"].Hide + panBrowser.Hide + lblError.Text = ("No image in directory") + lblError.Show + Endif + +End + +Public Sub ivwImage_KeyPress() + + If Key.Code = Key.Enter Or If Key.Code = Key.Return Then ivwImage_Activate + +End + + +Public Sub ivwImage_Activate() + + $bFilm = False + $iIndex = CInt(ivwImage.Key) + UpdateFilmMode + +End + +Public Sub Form_Close() + + CAnimation.Exit + +End + +Public Sub svwImage_KeyPress() + + Select Case Key.Code + + Case Key.Up + svwImage.Scroll(svwImage.ScrollX, svwImage.ScrollY - 64) + + Case Key.Down + svwImage.Scroll(svwImage.ScrollX, svwImage.ScrollY + 64) + + Case Key.Left + svwImage.Scroll(svwImage.ScrollX - 64, svwImage.ScrollY) + + Case Key.Right + svwImage.Scroll(svwImage.ScrollX + 64, svwImage.ScrollY) + + Case Key.PageUp + Action("previous") + + Case Key.PageDown + Action("next") + + Case Key.Enter, Key.Return + Action("film") + + End Select + +End + +Public Sub PushUndo() + + $hUndoStack.Add($hImage.Copy()) + +End + +Public Sub dwgPath_Draw() + + 'Debug Paint.Font.ToString() + PaintOutlineText($sDir, 0, 0, Paint.W, Paint.H) + +End diff --git a/app/examples/Image/PhotoTouch/.src/FMain.form b/app/examples/Image/PhotoTouch/.src/FMain.form new file mode 100644 index 00000000..cc67ddd8 --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/FMain.form @@ -0,0 +1,65 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,82,72) + Icon = Picture["icon.png"] + Border = False + Maximized = True + Arrangement = Arrange.Fill + { svwImage ScrollArea + MoveScaled(6,25,24,24) + Visible = False + Background = Color.Background + Mouse = Mouse.SizeAll + Border = False + ScrollBar = Scroll.None + } + { panToolbar HBox + MoveScaled(1,1,75,5) + Ignore = True + AutoResize = True + Spacing = True + Margin = True + } + { lblError Label + MoveScaled(15,52,46,9) + Visible = False + Font = Font["Bold,+5"] + Background = Color.Background + Foreground = Color.LightForeground + Alignment = Align.Center + } + { panBrowser Panel + MoveScaled(48,17,24,30) + Background = Color.Background + Arrangement = Arrange.Vertical + Margin = True + { panMargin Panel + MoveScaled(4,1,12,4) + } + { ivwImage IconView + MoveScaled(3,11,16,16) + Font = Font["+2"] + Background = Color.Background + Foreground = Color.LightForeground + Expand = True + Mode = Select.Multiple + IconLines = 3 + Border = False + } + } + { panToolbarBrowser HBox + MoveScaled(1,7,75,5) + Visible = False + Ignore = True + AutoResize = True + Spacing = True + Margin = True + } + { dwgInfo DrawingArea + MoveScaled(5,64,20,6) + Font = Font["+5"] + Foreground = Color.LightForeground + Ignore = True + } +} diff --git a/app/examples/Image/PhotoTouch/.src/FResize.class b/app/examples/Image/PhotoTouch/.src/FResize.class new file mode 100644 index 00000000..7ee42411 --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/FResize.class @@ -0,0 +1,57 @@ +' Gambas class file + +Private $aSize As Float[] = [1 / 4, 1 / 3, 1 / 2, 2 / 3, 3 / 4, 1] +Private btnApply As CButton + +Public Sub sldResize_Change() + + Dim W, H As Integer + + With FMain.GetCurrentImage() + W = .W * $aSize[sldResize.Value] + H = .H * $aSize[sldResize.Value] + End With + lblSize.Text = Format($aSize[sldResize.Value], "0%") & " - " & W & " x " & H + +End + +Public Sub _new() + + btnApply = New CButton(Me) As "btnApply" + btnApply.Move(Me.W - 48 - Desktop.Scale * 2, Desktop.Scale * 2, 48, 48) + btnApply.Image = Image.Load("ok.png") + +End + +Public Sub Form_Open() + + Me.Center + sldResize.SetFocus + sldResize_Change + +End + +Public Sub btnApply_Click() + + Dim W, H As Integer + + With FMain.GetCurrentImage() + W = .W * $aSize[sldResize.Value] + H = .H * $aSize[sldResize.Value] + End With + + FMain.Stretch(W, H) + +End + +Public Sub Form_KeyPress() + + If Key.Code = Key.Enter Or If Key.Code = Key.Return Then + btnApply_Click + Stop Event + Return + Endif + + FMain.Form_KeyPress + +End diff --git a/app/examples/Image/PhotoTouch/.src/FResize.form b/app/examples/Image/PhotoTouch/.src/FResize.form new file mode 100644 index 00000000..931fdfa3 --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/FResize.form @@ -0,0 +1,40 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,55,24) + Background = &HFFFFFF& + Border = False + Utility = True + Stacking = Window.Above + SkipTaskbar = True + Opacity = 50 + { panBrightness Panel + MoveScaled(1,1,53,22) + Background = &H000000& + { PictureBox1 PictureBox + MoveScaled(2,11,8,8) + Picture = Picture["resize.png"] + Stretch = True + Alignment = Align.Center + } + { sldResize Slider + MoveScaled(13,11,36,3) + MaxValue = 5 + PageStep = 1 + Value = 5 + } + { lblTitle Label + MoveScaled(2,2,52,6) + Font = Font["Bold,+5"] + Foreground = Color.TextBackground + AutoResize = True + Text = ("Resize") + } + { lblSize Label + MoveScaled(14,14,34,5) + Font = Font["Bold,+5"] + Foreground = Color.TextBackground + AutoResize = True + } + } +} diff --git a/app/examples/Image/PhotoTouch/.src/FScissors.class b/app/examples/Image/PhotoTouch/.src/FScissors.class new file mode 100644 index 00000000..a2a11522 --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/FScissors.class @@ -0,0 +1,260 @@ +' Gambas class file + +Private $aScissors As Panel[] + +Private Enum S_N, S_S, S_W, S_E, S_NW, S_NE, S_SW, S_SE +Private HANDLE_SIZE As Integer + +Private $aMouse As Integer[] +Private $aOpacity As Integer[] + +Private $MX As Integer +Private $MY As Integer +Private $hRect As Rect +Private $bResize As Boolean + +Private btnCut As CButton + +Public Sub Form_Resize() + + $aScissors[S_NW].Move(0, 0, HANDLE_SIZE, HANDLE_SIZE) + $aScissors[S_NE].Move(Me.W - HANDLE_SIZE, 0, HANDLE_SIZE, HANDLE_SIZE) + $aScissors[S_SW].Move(0, Me.H - HANDLE_SIZE, HANDLE_SIZE, HANDLE_SIZE) + $aScissors[S_SE].Move(Me.W - HANDLE_SIZE, Me.H - HANDLE_SIZE, HANDLE_SIZE, HANDLE_SIZE) + $aScissors[S_N].Move(HANDLE_SIZE, 0, Me.W - HANDLE_SIZE * 2, HANDLE_SIZE) + $aScissors[S_S].Move(HANDLE_SIZE, Me.H - HANDLE_SIZE, Me.W - HANDLE_SIZE * 2, HANDLE_SIZE) + $aScissors[S_W].Move(0, HANDLE_SIZE, HANDLE_SIZE, Me.H - HANDLE_SIZE * 2) + $aScissors[S_E].Move(Me.W - HANDLE_SIZE, HANDLE_SIZE, HANDLE_SIZE, Me.H - HANDLE_SIZE * 2) + + btnCut.Move(Me.W - btnCut.W - Desktop.Scale * 3, Desktop.Scale * 3) + + dwgNW.Move(HANDLE_SIZE, HANDLE_SIZE) + dwgNE.Move(Me.W - HANDLE_SIZE - dwgNE.W, dwgNW.Y) + dwgSE.Move(dwgNE.X, Me.H - HANDLE_SIZE - dwgSE.H) + dwgSW.Move(dwgNW.X, dwgSE.Y) + +End + + +Public Sub Form_Open() + + Dim I As Integer + + $aMouse = [Mouse.SizeN, Mouse.SizeS, Mouse.SizeW, Mouse.SizeE, Mouse.SizeNW, Mouse.SizeNE, Mouse.SizeSW, Mouse.SizeSE] + HANDLE_SIZE = Desktop.Scale + + $aScissors = New Panel[8] + + For I = 0 To 7 + $aScissors[I] = New Panel(Me) As "Panel" + With $aScissors[I] + .Mouse = $aMouse[I] + .Background = Color.White 'IIf(I >= S_NW, Color.RGB(192, 192, 192), Color.White) + .Tag = I + End With + Next + + btnCut = New CButton(Me) As "btnCut" + btnCut.Resize(48, 48) + btnCut.Image = Image.Load("scissors.png") + + Me.Move(FMain.ClientW \ 4, FMain.ClientH \ 4, FMain.ClientW \ 2, FMain.ClientH \ 2) + 'Form_Resize + +End + +Public Sub Panel_MouseDown() + + $MX = Mouse.ScreenX + $MY = Mouse.ScreenY + $hRect = Rect(Me.X, Me.Y, Me.W, Me.H) + +End + +Public Sub Panel_MouseMove() + + Dim X, Y As Integer + Dim iTag As Integer = Last.Tag + Dim MIN_HEIGHT As Integer = btnCut.W + Desktop.Scale * 6 + Dim MIN_WIDTH As Integer = btnCut.H + Desktop.Scale * 6 + + Select Case Last.Tag + + Case S_N + Y = $hRect.Y + Mouse.ScreenY - $MY + Y = Min($hRect.Bottom - MIN_HEIGHT, Max(0, Y)) + Me.Move(Me.X, Y, Me.W, $hRect.H + $hRect.Y - Y) + + Case S_S + Y = $hRect.H + Mouse.ScreenY - $MY + Y = Min(FMain.H - $hRect.Y, Max(MIN_HEIGHT, Y)) + Me.Move(Me.X, Me.Y, Me.W, Y) + + Case S_W + X = $hRect.X + Mouse.ScreenX - $MX + X = Min($hRect.Right - MIN_WIDTH, Max(0, X)) + Me.Move(X, Me.Y, $hRect.W + $hRect.X - X, Me.H) + + Case S_E + X = $hRect.W + Mouse.ScreenX - $MX + X = Min(FMain.W - $hRect.X, Max(MIN_WIDTH, X)) + Me.Move(Me.X, Me.Y, X, Me.H) + + Case S_NW + Y = $hRect.Y + Mouse.ScreenY - $MY + Y = Min($hRect.Bottom - MIN_HEIGHT, Max(0, Y)) + X = $hRect.X + Mouse.ScreenX - $MX + X = Min($hRect.Right - MIN_WIDTH, Max(0, X)) + Me.Move(X, Y, $hRect.W + $hRect.X - X, $hRect.H + $hRect.Y - Y) + + Case S_NE + Y = $hRect.Y + Mouse.ScreenY - $MY + Y = Min($hRect.Bottom - MIN_HEIGHT, Max(0, Y)) + X = $hRect.W + Mouse.ScreenX - $MX + X = Min(FMain.W - $hRect.X, Max(MIN_WIDTH, X)) + Me.Move(Me.X, Y, X, $hRect.H + $hRect.Y - Y) + + Case S_SW + Y = $hRect.H + Mouse.ScreenY - $MY + Y = Min(FMain.H - $hRect.Y, Max(MIN_HEIGHT, Y)) + X = $hRect.X + Mouse.ScreenX - $MX + X = Min($hRect.Right - MIN_WIDTH, Max(0, X)) + Me.Move(X, Me.Y, $hRect.W + $hRect.X - X, Y) + + Case S_SE + Y = $hRect.H + Mouse.ScreenY - $MY + Y = Min(FMain.H - $hRect.Y, Max(MIN_HEIGHT, Y)) + X = $hRect.W + Mouse.ScreenX - $MX + X = Min(FMain.W - $hRect.X, Max(MIN_WIDTH, X)) + Me.Move(Me.X, Me.Y, X, Y) + + End Select + + 'Form_Resize + +End + + +Public Sub Form_MouseDown() + + $MX = Mouse.ScreenX + $MY = Mouse.ScreenY + $hRect = Rect(Me.X, Me.Y, Me.W, Me.H) + +End + +Public Sub Form_MouseMove() + + Dim X, Y As Integer + + X = Min(Max($hRect.X + Mouse.ScreenX - $MX, 0), FMain.W - $hRect.W) + Y = Min(Max($hRect.Y + Mouse.ScreenY - $MY, 0), FMain.H - $hRect.H) + Me.Move(X, Y) + +End + +Public Sub btnCut_Click() + + FMain.Cut + +End + +Public Sub dwgCorner_Draw() + + Dim hCtrl As DrawingArea = Last + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 192)) + + Select Case Last.Tag + + Case "NW" + + If Not $bResize Then Paint.Brush = Paint.Color(Color.White) + + Paint.MoveTo(2, 2) + Paint.LineTo(hCtrl.W - 2, 2) + Paint.LineTo(2, hCtrl.H - 2) + + Case "NE" + + Paint.MoveTo(hCtrl.W - 2, 2) + Paint.LineTo(hCtrl.W - 2, hCtrl.H - 2) + Paint.LineTo(2, 2) + + Case "SW" + + Paint.MoveTo(2, hCtrl.H - 2) + Paint.LineTo(2, 2) + Paint.LineTo(hCtrl.W - 2, hCtrl.H - 2) + + Case "SE" + + If $bResize Then Paint.Brush = Paint.Color(Color.White) + + Paint.MoveTo(hCtrl.W - 2, hCtrl.H - 2) + Paint.LineTo(2, hCtrl.H - 2) + Paint.LineTo(hCtrl.W - 2, 2) + + End Select + + Paint.Fill + +End + +Public Sub Form_KeyPress() + + Dim MIN_HEIGHT As Integer = btnCut.W + Desktop.Scale * 6 + Dim MIN_WIDTH As Integer = btnCut.H + Desktop.Scale * 6 + Dim X, Y, W, H As Integer + + X = Me.X + Y = Me.Y + W = Me.W + H = Me.H + + Select Case Key.Code + Case Key.Up + If $bResize Then + H = Max(MIN_HEIGHT, H - 16) + Else + Y = Max(0, Y - 16) + Endif + Case Key.Down + If $bResize Then + H = Min(FMain.H - Me.Y, H + 16) + Else + Y = Min(FMain.H - Me.H, Y + 16) + Endif + Case Key.Left + If $bResize Then + W = Max(MIN_WIDTH, W - 16) + Else + X = Max(0, X - 16) + Endif + Case Key.Right + If $bResize Then + W = Min(FMain.W - Me.X, W + 16) + Else + X = Min(FMain.W - Me.W, X + 16) + Endif + Case Key.Space + $bResize = Not $bResize + dwgNW.Refresh + dwgSE.Refresh + Stop Event + Return + Case Key.Return, Key.Enter + btnCut_Click + Stop Event + Return + End Select + + If X <> Me.X Or If Y <> Me.Y Or If W <> Me.W Or If H <> Me.H Then + Me.Move(X, Y, W, H) + Stop Event + Return + Endif + + FMain.Form_KeyPress + +End diff --git a/app/examples/Image/PhotoTouch/.src/FScissors.form b/app/examples/Image/PhotoTouch/.src/FScissors.form new file mode 100644 index 00000000..cfddd6ce --- /dev/null +++ b/app/examples/Image/PhotoTouch/.src/FScissors.form @@ -0,0 +1,38 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,64,64) + Background = &H000000& + Mouse = Mouse.SizeAll + Border = False + Utility = True + Stacking = Window.Above + SkipTaskbar = True + Opacity = 50 + { Label1 Label + MoveScaled(3,3,52,6) + Font = Font["Bold,+5"] + Foreground = Color.TextBackground + Text = ("Cut") + } + { dwgNW DrawingArea dwgCorner + Name = "dwgNW" + MoveScaled(7,31,4,4) + Tag = "NW" + } + { dwgNE DrawingArea dwgCorner + Name = "dwgNE" + MoveScaled(14,31,4,4) + Tag = "NE" + } + { dwgSW DrawingArea dwgCorner + Name = "dwgSW" + MoveScaled(7,38,4,4) + Tag = "SW" + } + { dwgSE DrawingArea dwgCorner + Name = "dwgSE" + MoveScaled(14,38,4,4) + Tag = "SE" + } +} diff --git a/app/examples/Image/PhotoTouch/balance.png b/app/examples/Image/PhotoTouch/balance.png new file mode 100644 index 0000000000000000000000000000000000000000..c66bf62b495459636ab7806b76a3042558eaa662 GIT binary patch literal 1125 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 literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/contrast.png b/app/examples/Image/PhotoTouch/contrast.png new file mode 100644 index 0000000000000000000000000000000000000000..49bda45dcbac7c6144da67e8853dfbbae71e1fa9 GIT binary patch literal 1340 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 literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/film.png b/app/examples/Image/PhotoTouch/film.png new file mode 100644 index 0000000000000000000000000000000000000000..58ce250007f96276517d235a8080365108fdb568 GIT binary patch literal 756 zcmV_}6?;+-5iRkg zL`nhyp;SD0@Zza>G1>+jYoCYBFL!scGrP&AYQBN6%gny-H}l?nGqVE&hShl^7Fi}Z zOsr3Abv{wyg(aS?a!8gW3cQsAUi4$~Arg8Kk!6WDc}H3bu5&E1#Ghi$Enqq_`ajrO zMJPWQwXe#2?i1@llFiF?E8%FAU(E#Ngg$+8?JB6dQ!pUxP&LeQg)Q##5`fcOW!xZa zl?9eP;&g#C1sftF@=*|Ezf_H9+E>}@ier?5SjTsgG2@{fC*_Lc1+5wg=FFp7y_5ph z0ayz}Z4yUWggG(@`OpIV;wS|aD1;{0i8F8?Pzso(z# z5Fgy;D3IZy=?M7EUtQP{)KhZ%*1$jG19N1tcx^7AS3Xdp)U|#4*1#0^xYJ%j1x8>W zcuE||a3Z9Dz&>Cv-0hWPu(|LZ=#dW;d2dX=LQ$)qG6i*FfW2~BC&h%is|PlzP-{F3 zfqdWw7Z4sb?$&r?JO(!We4vHlzQBnovTX5~a!3NdIGR3?;GE&cWT{bpetx19(CGtl zO2&q?LXw&zju*6QU^0Loo@7$1pHe`*<2#`h%8A{sI9|~7f#C3qqZBaDd}wl=I71)k z`+C4Hj@Ei040_@N{e+eegu&1U{$3wQa~>g2B_u(LX-3HN%2dEJ_5hbdze0b&abTwL z*W`vW1@;3bix6-0#0P>+#tp4rN&%Yy`~;xPH)Hy0V^>`F5u>buTcm;K0IZTGXSgwW z!WRH;GJ~b_rs==k7Yypire?*R0^V^Z!XI9-&e|946f8tY@5Ls&q#W77Wl?r|h#Jh% mt=Hl8zCI59YL~<7!QeODA$j&oiky%D00006INX4#5Pc|g6Y5|)oj-7<5~jb6I(h|<>W5lU&;0~m@? z=jP)QKP;twp{BXu4K~ME<;Ej{*aHlCVItwhGe)uOvS9n1&&3=V1f`bNa?b)lBlk

    FQEESF+nDiVPs^4*Is+ADlf2X8vx}w!0$!N-TPlL0Q>`e_uY4y zn3$lqx0l)3StchZNhXuL{r1}cy#N0Dq*5uiZQDjBlVNOZjJCEm_U+pTz_DY;NT<^b z3=ELTWSE$kAel@uI5-#p_TmA5R5EnBv5<;ssA&Auw*1<)YYBxyfgR=L*npZ#Vx@Vw|5~gb_^`|W-qP=mz`y`P2(DkhUeOEq$y)3QTF5W=bg^Y80x`03E0s8XEd4kx0BOgwW^b=ZQoj zfpa^x4%~n5*94&Kc&uOTZQCZF&y&ey$Ye6tfOfzH3c&9Q0DyMp%$YmMWb(f!Cnt9| zH#a9jp-^BD%RLk12cgovB7fl}m&=Li>FL`){P4qn0pa_TwIsD%vKP=a%>e4}BisSB zJo3mR30CO;$GWaRbO1sK36RU>9LutDz%+0Nm;mONVh5@LKx08;>VO6y4j4;nygK9p i7LWzu-P|Y4BA#)o50000Nkl|#x2k!=s)q6qE2HuvE4dt)6n0Yew_(%3pj$M7XF6{N=U*w`#td za;Mh8k$xetGfl6Ez&fws^Wx)QYW%|FYvxh}d^>dk0NNU+cmGvJ_|RE(|HUp`Tz~+A zV5Z;s>OvrL`2tW3Y}XTlOVGJcfJYut`GO^t{UveWtAvaPJmM6ON(HeC1=zPnx@uBn zve%an42&q)_frFS<>%$j$98`^88@F7Bs8vyR<*+Qrw#Ex9RfV)>>zLE19S< z6@*2utmBUgl&Va8J-?7KN>tXU$`M!Uiu(P?z)T8jN>tWptuoqFGS=!kH36wqK>I3V zw9?thrg1g-+rX#m=v$<^_H*MszmE-kls41vcnWZnh-sy>#weW&smd1;b#Qv3MvI0EG;g-4(Cl>z- zS~N#GeoWi?dq~vGy>Hf=9fr zf!xtf_U|}JW9uf~dFh)RA2`U8jh{eW!x={=6~H_#Sz|=9S7Y9P;TvD%kAC+)zVuH| z^2)#d3xD(cf8#A)iH;^{48e%_y#3CLn4aB)7S1P88^dUYp23{Mh-8g6I;$te0C$B3 zkVy~l&)<2JuQfOE>~DXCSDtyA)}`(I$#-AiY&t`xelGF#chPuDyfhRFeP|zHW3Fk^ zC6j}o)mfvI%TCIJxRQl%km-kmKjgLF`6{n$*ubh(iYiguxMn3ce(C`_27R;(uwD+V z1rhQCJuIo7ODtY>O$C^2ZA!VU3MrjcN~maZ>E;5Tp>sW)?mN!0y|1&nu7{;}-bpo( zm^+uA-aa1t`^PCnt0?#ZHamj#a!4rv1zrx6Q>8ufPX)TCj zq5$A7F)F7IcaRPh$k;pw&Ya@@6^+=&7M|(s;i0`}_}&xWrSs(gM$BZt@mPyoz9S7}17&?@5|+J}Wogik^Yx zM!5?(kdQWgCZdCW5iMiB=s=vSe7m2#@qIpj?*{(upk^o=V8tUQ4uvIK@Ugy+UVan* z_}703SjymSA&Pytn^@NJS1XiK#n>kfDR+T)U*E=qTb8mb7vY=>G1|SC>b18rnjT`r^Qnth zW3)md*F!wz8@RY~{_6FgV4NaItlA+Ews9C+4^tpaM*BarSTouQ+zslUZ9w0NEM*Vaf ztzMRcC_eM)hnUyeK4tO%ftO`PqL;fjwiGFsQg!EwgspQ}YjM^RSW9oJz`q|bMB+6S z{e+C}6C?l#tmDt_jy4&9kd~|nF#zSjS|Oj$5G(X!Z5!umT9Cng00cQg)PhJbnRrS0X>w z0&7_ui!$1oB9@qx_{jK#gc8X@X_!VC^WBCJ^y^Dr7}8k)G_=DVj&93UAgK)!%earShjDPN}*kBhG0 zoGl7c+R>_wLrtW>aACaKkLa>wFmPo87?m4S#s-L0f)0gA&TS@CUCqdjx5;}MM1;tK zCCtwbqKu)pU!UFAcVDEO;th;U60_-`NV>qkOQp4`S!Ufk| z#leI+!`U#&kt|D_!(|pANfrkZ{G(j0!`Tj=Wf44payzK2n8$L4$a zpA!dIXW&e{2HzD^O)W*A>^`4mPE#v85ADJi&EmGTC_S~pM>1}(1ZXsgXmMJTi`MPO zVgtn-L41;NL-}KssaOw{xqz13Shd6jZoK~+)@|lZd>*)M4KMe29Nw{=P;@mX_iSfy zsE^||MYj74x^6xy6k@EFq?NGq)BrcOZ{Wojp5^AkAYD6maQ`R2Om+Q&D-=MmPGQ86 zh?Qk)})BvH?zsl&qDgOB3KjlX+z09Ii z8*R(hUA6%6h$3)~u&4Rbs@1TQYViP866?9e0?!NaSjn7q!pTgohN$P z_w0YMc>a7c{U<0`%c}MpFG~PLKq8_LXQ?U^z)cch{Cr7?T*0CUDk@G!iHrq7rI?5T zMv?PNH!9(Rh04OVcDB~mvujTW%{6mKM`PH6M_peR>)UUjaQA09cl3RB?|p|z`zFMM ziqnY>;l00|8(NoB9fs7|(};<1VBZdwE^H%KRsEp?44qGtN}tC$$3SFq5WMtyAmeJ{={ta5)Y~Wc6l-bEDKM#ceBA)3jt2U5DRC8+}>zz^dvwR8`Np zENl5NR9Zk;B4mWDl=emfhyI|&+Ts;!sp)u=Tw@C^S;t{f?0@QUzH;A#w6DF1=7me> z*!dQ(z4;RHC2NV)&nKO%WxsPAJ#m7Shj#Pvtq)!mLRSoN%9MC&R~7@5R++I+f*gU8 z57-_Kha-IEuKRi7`6mgjz6C#hp2l#5Lcv2ssGd{L9k<`drVY1o?AU%fcD>EX{$4z5 zX{v9eb<0*{rna9&l$0vqV=+JgUOwQyI$YpkfLy*%=UW$<@^C7Tblb&l)q3mE|=qODRQBCaazMxo>asdML#`r+zuCLgF=Hebq} zu0sVt`w)pov`w8$A00a@K*RTbcJgPDWZlxhsnB&S09tGzKRkH!x3;w24)l);0Bi+T z0&yS=%*+<g+Fhf!nlnFtUl1N2Fgtk-_J7?xLF`C$s?P(KU zfG6NJSh8XTJ61dZ;u&DYf(^%MN|CU1^!a+OkMHPwUs`-8up;OhR6^&Vk5E`Ypfk`< z=mhk8&V>abPGf%xTmsL7m%*E0=x>6Tz&3ard&X_|_-qe>R> zi%NMBZ}6^VHx%)<;(Z#bqF_zCXmFxNL+Pl`IIyhchzs(DYNFGgj7wwLieN;2YxW&m z;)xuMVZ5FjJu{mh(7sjP!=yir93p(frFNzC#%A=?*h0~!oSKpz3{n?1 zkEU{HOv>&d`t_ia15UPBjxzdNW7}QtwvQ%hr1facXbcbk=@AB z-+H;#Nmh>f!W_G7leW%Hq4Xf5mA>*(udJ*rla8??mysUNO4nL$(!sW+=|+nh&1fg- z87`Dh4#&)BO?p{M9fi=O_9W*q`UygaH!F)ES94#Bi+oE@>tku{Ck H{-5+8V{qoM literal 0 HcmV?d00001 diff --git a/app/examples/Misc/Explorer/.lang/ca.po b/app/examples/Misc/Explorer/.lang/ca.po new file mode 100644 index 00000000..abfa4c6b --- /dev/null +++ b/app/examples/Misc/Explorer/.lang/ca.po @@ -0,0 +1,56 @@ +# Catalan translation of Explorer +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the Explorer package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Explorer\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:09+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FExplorer.form:35 +msgid "&About..." +msgstr "&Quant a..." + +#: FExplorer.form:14 +msgid "FExplorer" +msgstr "-" + +#: FExplorer.form:19 +msgid "&File" +msgstr "&Fitxer" + +#: .project:1 +msgid "File browser example" +msgstr "Exemple de navegador de fitxers" + +#: FExplorer.class:158 +msgid "' has been renamed to '" +msgstr "' s'ha canviat el nom a '" + +#: FExplorer.class:152 +msgid "IconView example written by\nBenoît Minisini" +msgstr "Exemple de visualitzador d'icones escrit per\nBenoît Minisini" + +#: FExplorer.form:39 +msgid "&Quit" +msgstr "&Surt" + +#: FExplorer.form:26 +msgid "&Refresh" +msgstr "&Actualitza" + +#: FExplorer.form:22 +msgid "&View hidden files" +msgstr "&Mostra els fitxers ocults" + diff --git a/app/examples/Misc/Explorer/.lang/cs.mo b/app/examples/Misc/Explorer/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..270fe315d0a58cbfa4bc02debcad9ecf9c588a11 GIT binary patch literal 816 zcmZXRzi-n(6vwYne$)~OAtVGs;v^(@00-DAx1gHFDG_!YlBN>f&ha(5#<{c3m$u;_ zfTbfsoe*P(GB6{Sv>Sf|D`JD0h3|!mBJrfJKTmhxd-u-o>($ve1mQrppd7jceS&QK zfaai|&^hSW31?;qxrqD%conoYhDz6GuS9cb$v zfH%O8;7#xgXpepaZ-d`K7o0o$XB{b-wI2kwrZsInY)|_=58Ar+ebaD*REa5ct+3sG46E#oSdV*aoDF>%1&!5?y>cmvmUB^2x_9dF zsT0@Z@U*{&l?Tp>l6VrX8yD!u@LhAi$eECfwA*L8!J9`fOX9TzjGHs!Yh9 zPZv^Q9O5RF6}cF&u9(iItwyQR#O(}}JleApN>Zt|ZL*\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "File browser example" +msgstr "Příklad prohlížeče souboru" + +#: FExplorer.class:152 +msgid "" +"IconView example written by\n" +"Benoît Minisini" +msgstr "" +"Příklad IconView od\n" +"Benoît Minisini" + +#: FExplorer.class:158 +msgid "' has been renamed to '" +msgstr "' bylo přejmenováno na '" + +#: FExplorer.form:14 +msgid "FExplorer" +msgstr "-" + +#: FExplorer.form:19 +msgid "&File" +msgstr "&Soubor" + +#: FExplorer.form:22 +msgid "&View hidden files" +msgstr "&Zobrazit skryté soubory" + +#: FExplorer.form:26 +msgid "&Refresh" +msgstr "&Obnovit" + +#: FExplorer.form:35 +msgid "&About..." +msgstr "&O aplikaci..." + +#: FExplorer.form:39 +msgid "&Quit" +msgstr "&Ukončit" diff --git a/app/examples/Misc/Explorer/.lang/de.mo b/app/examples/Misc/Explorer/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..c1e2da89cf0ce98c6ed4b6fdf863a262ecc2e771 GIT binary patch literal 828 zcmZXRzityj5XLtUNH__E5Q2tcB&170&VXp5OF+5A=U{PX2j`DOx;uMpFFEg8dwX$` z7l0ax0*Q(TK%!5Vo=2cVv{2LVtqF3$O5goXJF~Mh`+d7L|6X8u&^@Stu0v;#TR)*| z&@bo;^!tKyb3!cQe*wG&-URQ0_d%Dx3f=}|a2Y%R1@s1T@wb=jre$6Xv0RhLX;al zDcKMoXQMF+H@jL*i(PEc#7JvVBtP*sSn2c6mQtx?35ji^Uvt+6&0et2rj%NRv>k1} zh_+(dk2^cfcFWst2OT~_zE(j!vmDZ^IO+sxJE%wWyd9>^2M6s|>?K)ggI$xUvdFA9 zAvN}rgj!J=(^{NH&15~Q*E{jf&NHN{L6r?S*yU^#QWCYc_S}_gNwQwZnl^)3PDozb zOygO9k86*-O|38zt~(i{AIrz~;kd}8TBYtVGbP)nd%KNbWtQjK>az)A)zv+z1|hBF z(t5%_|CqCx+Tv!D!r#Dy!vx_+hqlTJ%*}Xy7=-t++R(@eZ*rcPN75L}{3WqM52 yD2G!@SvsxYR3J83mSf2U_0P{>kyJP}J$>QY%&mW!%QWD!J2a9}ijK7U-?Tp;rPKug literal 0 HcmV?d00001 diff --git a/app/examples/Misc/Explorer/.lang/de.po b/app/examples/Misc/Explorer/.lang/de.po new file mode 100644 index 00000000..ed66f8d6 --- /dev/null +++ b/app/examples/Misc/Explorer/.lang/de.po @@ -0,0 +1,49 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "File browser example" +msgstr "Beispiel für einen Dateibrowser" + +#: FExplorer.class:152 +msgid "IconView example written by\nBenoît Minisini" +msgstr "IconView Beispiel, geschrieben von\nBenoît Minisini" + +#: FExplorer.class:158 +msgid "' has been renamed to '" +msgstr "' wurde umbenannt zu '" + +#: FExplorer.form:14 +msgid "FExplorer" +msgstr "-" + +#: FExplorer.form:19 +msgid "&File" +msgstr "&Datei" + +#: FExplorer.form:22 +msgid "&View hidden files" +msgstr "&Versteckte Dateien anzeigen" + +#: FExplorer.form:26 +msgid "&Refresh" +msgstr "&Aktualisieren" + +#: FExplorer.form:35 +msgid "&About..." +msgstr "&Über..." + +#: FExplorer.form:39 +msgid "&Quit" +msgstr "&Beenden" + diff --git a/app/examples/Misc/Explorer/.lang/es.mo b/app/examples/Misc/Explorer/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..f0d462942caf7e0783b81d0f81ec51692b2b44b9 GIT binary patch literal 564 zcmY+AyKdVs6o%Q{+9=2pq+1708d6Y6w?MfdL`=IvCCimBK(jH@vCTjw4T?^jcj%gT zY3ELvx_0Udbneithq)ll0Y3gD_#d2u|JT;a4Z(Veyg>%Y2J!{*^%Yr1z9Fl~_XoX2 zeTDo$wveBQ_x(oxJh18CFYbMRQP=Rjv_wdNx{mr9bsN?9?VzINS^1ro5#Q%$qvftv z{2B6dGj~xGk+7|5AmPWkawIHZY@wNwaHQahPE}QbreicTq%9V++E}opXUzqajwKZv zfo+u0G41iw54zEiCu>OuG5sWX!LppSIDMbTS@QNQ?}{Lmjbj5V zbyG`cY)sq3G^Jgh33@0po}?$d)ha~4e~eZ=nac^V0m#oWO?kI7^dk?`^rTjiv6JH< zOEU4a)VpXeI5paV#u?W$T*AS^?apec^Z_lW(l+3ZhJ!ZSfAaZV#$cJyrKyyj#B~2$ rxd0O@!IqL%aF-gl|=7J#4CnE<^6X\n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FExplorer.class:190 +msgid "FExplorer" +msgstr "FExplorer" + +#: FExplorer.class:195 +msgid "&File" +msgstr "&Archivo" + +#: FExplorer.class:198 +msgid "&View hidden files" +msgstr "&Ver archivos ocultos" + +#: FExplorer.class:202 +msgid "&Refresh" +msgstr "&Refrescar" + +#: FExplorer.class:212 +msgid "&About..." +msgstr "&Acerca de..." + +#: FExplorer.class:216 +msgid "&Quit" +msgstr "&Salir" diff --git a/app/examples/Misc/Explorer/.project b/app/examples/Misc/Explorer/.project new file mode 100644 index 00000000..101a7269 --- /dev/null +++ b/app/examples/Misc/Explorer/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.0 +Title=File browser example +Startup=FExplorer +Icon=folder.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Environment="GB_GUI=gb.gtk" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Misc/Explorer/.src/FExplorer.class b/app/examples/Misc/Explorer/.src/FExplorer.class new file mode 100644 index 00000000..1ab8c315 --- /dev/null +++ b/app/examples/Misc/Explorer/.src/FExplorer.class @@ -0,0 +1,175 @@ +' Gambas class file + +Private $sPath As String +Private $bHidden As Boolean +Private $bCtrl As Boolean + +Static Public Sub Main() + + Dim hForm As Form + + hForm = New FExplorer(System.User.Home) + Application.MainWindow = hForm + hForm.Show + +End + +Public Sub _new(sPath As String) + + $sPath = sPath + RefreshExplorer + +End + +Private Sub RefreshExplorer() + + Dim sFile As String + Dim hPictDir As Picture + Dim hPictFile As Picture + Dim cDir As New String[] + Dim sName As String + + Inc Application.Busy + + Me.Title = Conv($sPath, System.Charset, Desktop.Charset) + + ivwExplorer.Clear + 'ivwExplorer.Arrangement = IconView.Row + + hPictDir = Picture["icon:/48/directory"] + hPictFile = Picture["icon:/48/file"] + + If $sPath <> "/" Then ivwExplorer.Add("D..", "..", hPictDir) + + For Each sFile In Dir($sPath) + + If Not $bHidden Then + If Stat($sPath &/ sFile).Hidden Then + Continue + Endif + Endif + + If IsDir($sPath &/ sFile) Then + cDir.Add("D" & sFile) + Else + cDir.Add("F" & sFile) + Endif + + Next + + cDir.Sort + + For Each sFile In cDir + + sName = Mid$(sFile, 2) + + If Left$(sFile) = "D" Then + ivwExplorer.Add(sFile, sName, hPictDir) + Else + ivwExplorer.Add(sFile, sName, hPictFile) + Endif + + 'ivwExplorer.Item.Editable = TRUE + + Next + + 'ivwExplorer.Sorted = FALSE + 'ivwExplorer.Ascending = TRUE + 'ivwExplorer.Sorted = TRUE + +Finally + + Dec Application.busy + +Catch + + Message.Error(Error.Text) + +End + + +Public Sub mnuQuit_Click() + + Me.Close + +End + +Public Sub mnuViewRefresh_Click() + + RefreshExplorer + +End + +Public Sub ivwExplorer_Activate() + + Dim sNewPath As String + Dim hForm As Form + + Debug Last.Current.Key + + If Last.Current.Key = "D.." Then + If $sPath = "/" Then Return + sNewPath = File.Dir($sPath) + Else + sNewPath = $sPath &/ Mid$(Last.Current.Key, 2) + Endif + + If IsDir(sNewPath) Then + + If $bCtrl Then + $bCtrl = False + hForm = New FExplorer(sNewPath) + hForm.Move(Me.X + 16, Me.Y + 16, Me.W, Me.H) + hForm.Show + Else + $sPath = sNewPath + RefreshExplorer + Endif + + Endif + +End + + +Private Sub ToggleViewHidden() + + $bHidden = Not mnuViewHidden.Checked + + mnuViewHidden.Checked = $bHidden + + RefreshExplorer + +End + + +Public Sub mnuViewHidden_Click() + + ToggleViewHidden + +End + +Public Sub mnuAbout_Click() + + Message(("IconView example written by\nBenoît Minisini")) + +End + +Public Sub ivwExplorer_Rename() + + Message("'" & Mid$(Last.Item.Key, 2) & ("' has been renamed to '") & Last.Item.Text & "'") + +End + +Public Sub ivwExplorer_KeyPress() + + If Key.Control Then $bCtrl = True + +End + +Public Sub ivwExplorer_KeyRelease() + + If Key.Control Then $bCtrl = False + +End + + diff --git a/app/examples/Misc/Explorer/.src/FExplorer.form b/app/examples/Misc/Explorer/.src/FExplorer.form new file mode 100644 index 00000000..8d50e1c4 --- /dev/null +++ b/app/examples/Misc/Explorer/.src/FExplorer.form @@ -0,0 +1,38 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(27.7143,27.7143,61.5714,46.7143) + Text = ("FExplorer") + Icon = Picture["icon:/16/directory"] + Arrangement = Arrange.Vertical + { mnuFile Menu + Text = Shortcut(("File"), "F") + { mnuViewHidden Menu + Text = Shortcut(("View hidden files"), "V") + } + { mnuViewRefresh Menu + Text = Shortcut(("Refresh"), "R") + Picture = Picture["icon:/16/refresh"] + Shortcut = "F5" + } + { Menu1 Menu + } + { mnuAbout Menu + Text = Shortcut(("About"), "A") & "..." + } + { mnuQuit Menu + Text = Shortcut(("Quit"), "Q") + Picture = Picture["icon:/16/quit"] + } + } + { Separator1 Separator + MoveScaled(27,4,19,0) + } + { ivwExplorer IconView + MoveScaled(2,9,44,30) + Expand = True + Mode = Select.Multiple + Editable = True + Border = False + } +} diff --git a/app/examples/Misc/Explorer/folder.png b/app/examples/Misc/Explorer/folder.png new file mode 100644 index 0000000000000000000000000000000000000000..0e6b21c1ef6a6b34c0f1f598a1d6e1a2ef23a552 GIT binary patch literal 2450 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 literal 0 HcmV?d00001 diff --git a/app/examples/Misc/Notepad/.lang/ca.po b/app/examples/Misc/Notepad/.lang/ca.po new file mode 100644 index 00000000..54ec8e22 --- /dev/null +++ b/app/examples/Misc/Notepad/.lang/ca.po @@ -0,0 +1,148 @@ +# Catalan translation of Notepad +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the Notepad package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Notepad\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:09+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FNotepad.form:122 +msgid "&?" +msgstr "-" + +#: FNotepad.form:125 +msgid "&About..." +msgstr "&Quant a..." + +#: FAbout.form:10 +msgid "About..." +msgstr "Quant a..." + +#: .project:1 +msgid "A little text editor" +msgstr "Un petit editor de text" + +#: FNotepad.class:118 +msgid "All files" +msgstr "Tots els fitxers" + +#: FNotepad.class:66 +msgid "Cancel" +msgstr "Canceŀla" + +#: FNotepad.class:118 +msgid "C/C++ files" +msgstr "Fitxers C/C++" + +#: FNotepad.form:111 +msgid "Choose &Font..." +msgstr "&Tria el tipus de lletra..." + +#: FNotepad.form:42 +msgid "Close" +msgstr "&Tanca" + +#: FNotepad.form:75 +msgid "&Copy" +msgstr "&Copia" + +#: FNotepad.form:81 +msgid "C&ut" +msgstr "&Retalla" + +#: FNotepad.class:118 +msgid "Desktop files" +msgstr "Fitxers d'escriptori" + +#: FNotepad.form:72 +msgid "&Edit" +msgstr "&Edita" + +#: FNotepad.form:33 +msgid "&File" +msgstr "&Fitxer" + +#: FNotepad.form:28 +msgid "Little notepad" +msgstr "Petit bloc de notes" + +#: FNotepad.class:30 +msgid "(New document)" +msgstr "(Document nou)" + +#: FNotepad.class:66 +msgid "\n\nFile has been modified. Do you want to save it ?" +msgstr "\n\nS'ha modificat el fitxer. El voleu desar?" + +#: FNotepad.class:66 +msgid "No" +msgstr "-" + +#: FNotepad.class:97 +msgid "\nUnable to load file.\n" +msgstr "\nNo es pot carregar el fitxer.\n" + +#: FAbout.form:21 +msgid "OK" +msgstr "D'acord" + +#: FNotepad.form:36 +msgid "&Open..." +msgstr "&Obre..." + +#: FNotepad.form:87 +msgid "&Paste" +msgstr "&Enganxa" + +#: FNotepad.form:65 +msgid "&Quit" +msgstr "&Surt" + +#: FNotepad.form:102 +msgid "&Redo" +msgstr "Re&fés" + +#: FNotepad.form:51 +msgid "&Save" +msgstr "&Desa" + +#: FNotepad.form:57 +msgid "S&ave As..." +msgstr "D&esa com..." + +#: FNotepad.class:118 +msgid "Text files" +msgstr "Fitxers de text" + +#: FAbout.form:28 +msgid "This is a little notepad sample program." +msgstr "Aquest és un petit programa exemple de bloc de notes." + +#: FNotepad.form:133 +msgid "txtNotepad" +msgstr "-" + +#: FNotepad.form:96 +msgid "&Undo" +msgstr "&Desfés" + +#: FNotepad.form:115 +msgid "&Wrap text" +msgstr "&Ajusta el text" + +#: FNotepad.class:66 +msgid "Yes" +msgstr "Sí" + diff --git a/app/examples/Misc/Notepad/.lang/cs.mo b/app/examples/Misc/Notepad/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..90a47cc6b4cbfca64fdb28fd7d9d7ead7a294e9e GIT binary patch literal 1818 zcmZ9LOKclO7{>=%C@hpmDbSVzQ>j`Ng>`sHs4|6S03(0??R@ji_u6l7pE&vv z!#W2!3psxWW6R*Pcj6E0H}D1UcknUr>=DN3=tb~e@FnmraK*4_SO)Kbz7F0EzG3to z!@gl)s6h*MQ}6-sUGOCMAxM69K#KPTcoh5!ybt^aB>&%o4}w2}l+VxLaqth2{QU(| z-p6p6Xn}P9I7o3%gBQT_AWT`s=&yndJ$Vyc!sABjZHType{|2d!Blx2_Ebt-l4EQLx1d<=u=xZRwdE4kckm7BDl+Qcn z`~#5ueGEPhehyOow?NAOTae=H8U6%1*#81jA5Ow4*_|>ZUynfO3@N8I18Ge}nh{!0 zLg@Wb`|gKOY>LYv|6gaIpxIlj(|Dj-sqUvB)bGVQfd{IS7S&0M`haxPI?jO)L!N*< z4WW0NF3T!awSp-GFmuw*q>8 zJrW_;^15ahs&b3>R5uyPKtID=?t5BOU>tG@*D7KM5nbO;Gl^NwPBhCc=a!Zh3K4Xr z&vJdGV#)2I3ep^NKDI0`_S}%dtm?PF`r4<<|;s-Ko@^yvtv!WhR_-g;_`LI=`Kb1TXd+*P7W6k6ACdSRdL~#-N z-cWSCbgouaINOcXSYWJLVI;;hU-L+?)pET5$!vE%o$a}QX0MC6mf7uSwyo(#>)@WqI{E{0N#XE!mHLo=2Y;bTwjNDs_h{x`Sw{QHuc`}@BPltk!P|8kU@qam_ExAlG%ncO0{sUx_n&JQe literal 0 HcmV?d00001 diff --git a/app/examples/Misc/Notepad/.lang/cs.po b/app/examples/Misc/Notepad/.lang/cs.po new file mode 100644 index 00000000..2cb92fb1 --- /dev/null +++ b/app/examples/Misc/Notepad/.lang/cs.po @@ -0,0 +1,150 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "A little text editor" +msgstr "Malý textový editor" + +#: FAbout.form:10 +msgid "About..." +msgstr "O aplikaci..." + +#: FAbout.form:21 +msgid "OK" +msgstr "-" + +#: FAbout.form:28 +msgid "This is a little notepad sample program." +msgstr "Toto je ukázka malého poznámkového bloku." + +#: FNotepad.class:30 +msgid "(New document)" +msgstr "(Nový dokument)" + +#: FNotepad.class:66 +msgid "" +"\n" +"\n" +"File has been modified. Do you want to save it ?" +msgstr "" +"\n" +"\n" +"Soubor byl změněn. Chcete jej uložit ?" + +#: FNotepad.class:66 +msgid "Cancel" +msgstr "Zrušit" + +#: FNotepad.class:66 +msgid "No" +msgstr "Ne" + +#: FNotepad.class:66 +msgid "Yes" +msgstr "Ano" + +#: FNotepad.class:97 +msgid "" +"\n" +"Unable to load file.\n" +msgstr "" +"\n" +"Nelze načíst soubor.\n" + +#: FNotepad.class:118 +msgid "All files" +msgstr "Všechny soubory" + +#: FNotepad.class:118 +msgid "C/C++ files" +msgstr "C/C++ soubory" + +#: FNotepad.class:118 +msgid "Desktop files" +msgstr "Desktop soubory" + +#: FNotepad.class:118 +msgid "Text files" +msgstr "Textové soubory" + +#: FNotepad.form:28 +msgid "Little notepad" +msgstr "Malý poznámkový blok" + +#: FNotepad.form:33 +msgid "&File" +msgstr "&Soubor" + +#: FNotepad.form:36 +msgid "&Open..." +msgstr "&Otevřít..." + +#: FNotepad.form:42 +msgid "Close" +msgstr "Zavřít" + +#: FNotepad.form:51 +msgid "&Save" +msgstr "&Uložit" + +#: FNotepad.form:57 +msgid "S&ave As..." +msgstr "Uložit &jako..." + +#: FNotepad.form:65 +msgid "&Quit" +msgstr "&Zavřít" + +#: FNotepad.form:72 +msgid "&Edit" +msgstr "&Upravit" + +#: FNotepad.form:75 +msgid "&Copy" +msgstr "&Kopírovat" + +#: FNotepad.form:81 +msgid "C&ut" +msgstr "&Vyjmout" + +#: FNotepad.form:87 +msgid "&Paste" +msgstr "&Vložit" + +#: FNotepad.form:96 +msgid "&Undo" +msgstr "&Zpět" + +#: FNotepad.form:102 +msgid "&Redo" +msgstr "&Vrátit" + +#: FNotepad.form:111 +msgid "Choose &Font..." +msgstr "Vyber &Font..." + +#: FNotepad.form:115 +msgid "&Wrap text" +msgstr "&Zalamovat text" + +#: FNotepad.form:122 +msgid "&?" +msgstr "-" + +#: FNotepad.form:125 +msgid "&About..." +msgstr "&O aplikaci..." + +#: FNotepad.form:133 +msgid "txtNotepad" +msgstr "-" diff --git a/app/examples/Misc/Notepad/.lang/de.mo b/app/examples/Misc/Notepad/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..f34ba0fa059f8ad64bc1f9dcb4dbf6dca8e43304 GIT binary patch literal 1833 zcmZ9LJ!~9B6vqb$1QtRdd?o~#AZsx)dj=vwbOhqf-8vZGhjSmXVxXA4eRnr_Z+5ji z7snKIbTmki3W+8S1r0(dg$vL%2#F3Mnm|KA=#VIABJqFcy|cu~&%fO_Z{GXQ+jqYm zJM@{rIt4okTew4rW$?v2@ek`y@D=bD_&9j-E+Kg9Gy=4hx!s7IYBz0r(*J5%?(hDad)g1i9YV;304iydV4?RlXt<>czh4Zp94Pu;TGS3tn1r?-+`R> zN04>>QoP>;xy~(+@AD7%FnGK0A4M?hJpr=bMUeBn26EpkAfMX;9{_tG$EO891X;(& zAje+=x!zYG_vJ>xA3(m+uOREc39^pgL9X{V=z_OF?$g5I{kZ1~a;`^U+#^Vzo&lz{ znOvLK(=h&iT#NNS0polU_WyMP4xYW)I*tdv+hee2U{Atk>lhv&WxBXeytprrD;|QK zg7JNxf;|i49!!_xtm>HLP~|ctGIEqey00lJ$!a2Zld;@U#!8#WTwNiltvn~3u2CV* zz!@hhl6@p7Il?_BTt7_4wp1z!x00m0!VMyAh07I$+f2z!w>DI6@qTg4do79*;kJ=n zxLw1~o0&?brK?sfHfTpiNpCzNV_y)yjJ35~7@LxiI?2Smq94aoCAp}$V=F4lm8GSb zLzy1MqB2a9oTR&&n5o7}j8{>meA^~z5n9`?U=mBIibNw3&2`as`R;zs>N{MUZ=8$H zQ0EeVYNo3=g;tGH$V{_jkf~8gTt=|HY8yq;jVyVede&Quyi1hjIx%Ir;aArE3xT{8 zwAyRUhO^Q1T69J8rdL&#%JOpHx4e4OtNQY-X1Tt$wAE|`P7S^EI+-$gtk90KTT5SdG_L~SbgRvTr*C913%bMS~(B+b6YqgbFmy&F75L1>;eXSlG z=zF>Jl2bw7@FBd;ZVEm85th?fY4f`54ONzteWTl1_0Al`v8+C2USN8dDl;g{GoiMQ za2&ppmOJAtA~~SRbrVr$OL8-bW6WS%lg!yt(!C*NhKFwd$hI;@8h9&FZ9kTT#)s&GD?s-yKNE*_GJ57SHYyG|{rZLc_KiM1XM-qe}Y!g2K%1{}D z%8D?cGbxzZ$zE@J0L=qE5bhRX7R-1QX5-#aEOt#u1{yK0NG!Kw(uOk0Q&SA=Tp|XR zHhCfA3k%$J2X)-?G75wmHYon4;_ u$mMMI8qkAA&95U}70>57;;+ICGoaGrvCt`<&q%) literal 0 HcmV?d00001 diff --git a/app/examples/Misc/Notepad/.lang/de.po b/app/examples/Misc/Notepad/.lang/de.po new file mode 100644 index 00000000..201f2c8c --- /dev/null +++ b/app/examples/Misc/Notepad/.lang/de.po @@ -0,0 +1,141 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "A little text editor" +msgstr "Ein kleiner Texteditor" + +#: FAbout.form:10 +msgid "About..." +msgstr "Über..." + +#: FAbout.form:21 +msgid "OK" +msgstr "-" + +#: FAbout.form:28 +msgid "This is a little notepad sample program." +msgstr "Dies ist ein kleines Notepad-Beispiel." + +#: FNotepad.class:30 +msgid "(New document)" +msgstr "(Unbenannt)" + +#: FNotepad.class:66 +msgid "\n\nFile has been modified. Do you want to save it ?" +msgstr "\n\nDatei wurde geändert. Wollen Sie sie speichern ?" + +#: FNotepad.class:66 +msgid "Cancel" +msgstr "Abbrechen" + +#: FNotepad.class:66 +msgid "No" +msgstr "Nein" + +#: FNotepad.class:66 +msgid "Yes" +msgstr "Ja" + +#: FNotepad.class:97 +msgid "\nUnable to load file.\n" +msgstr "\nDatei kann nicht geladen werden.\n" + +#: FNotepad.class:118 +msgid "All files" +msgstr "Alle Dateien" + +#: FNotepad.class:118 +msgid "C/C++ files" +msgstr "C/C++ Dateien" + +#: FNotepad.class:118 +msgid "Desktop files" +msgstr "Desktop Dateien" + +#: FNotepad.class:118 +msgid "Text files" +msgstr "Textdateien" + +#: FNotepad.form:28 +msgid "Little notepad" +msgstr "Kleines Notepad" + +#: FNotepad.form:33 +msgid "&File" +msgstr "&Datei" + +#: FNotepad.form:36 +msgid "&Open..." +msgstr "&Öffnen..." + +#: FNotepad.form:42 +msgid "Close" +msgstr "Schließen" + +#: FNotepad.form:51 +msgid "&Save" +msgstr "&Speichern" + +#: FNotepad.form:57 +msgid "S&ave As..." +msgstr "Speichern &als..." + +#: FNotepad.form:65 +msgid "&Quit" +msgstr "&Beenden" + +#: FNotepad.form:72 +msgid "&Edit" +msgstr "&Bearbeiten" + +#: FNotepad.form:75 +msgid "&Copy" +msgstr "&Kopieren" + +#: FNotepad.form:81 +msgid "C&ut" +msgstr "&Ausschneiden" + +#: FNotepad.form:87 +msgid "&Paste" +msgstr "&Einfügen" + +#: FNotepad.form:96 +msgid "&Undo" +msgstr "&Rückgängig" + +#: FNotepad.form:102 +msgid "&Redo" +msgstr "&Wiederherstellen" + +#: FNotepad.form:111 +msgid "Choose &Font..." +msgstr "&Schriftart wählen..." + +#: FNotepad.form:115 +msgid "&Wrap text" +msgstr "&Zeilenumbruch" + +#: FNotepad.form:122 +msgid "&?" +msgstr "-" + +#: FNotepad.form:125 +msgid "&About..." +msgstr "&Über..." + +#: FNotepad.form:133 +msgid "txtNotepad" +msgstr "-" + diff --git a/app/examples/Misc/Notepad/.lang/es.mo b/app/examples/Misc/Notepad/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..41b9834550ba4bbd7843d78ceed74e9e045c4e57 GIT binary patch literal 1210 zcmY+COK;Oa5XToNukb46AyootPZkn5JT6qBlv<7xqB@V}Q3SV*vrSw%Ub9|@R^rM> z;A>F9jT;gd4jj00LEN|@kU-oJ|4Ciit~~yEcXns~JL4~NGcOs&W!M$iN7z-^^Fw%H z`~VMwKf#mWFYqY%H|GqTG%tWBz*+DZIG?ZcoL0^oIq!f4>{|fooD0r?K6nmX1u3oo z&w@h`51Zh13fuy(fiFOc^9uY8z6J5H?IVnx0pEbf!FM@7fRyhONIE~~{F?Jy?*E?i z4@mbWaipUFo(AVZife&XyCO*OynOD1q$2=l!FBL1_zWby+aO9f9TcDLO8ZG0#iK!W zpj;PV=V6y%G)M>Ko}&o{<)xZXT*`&e>>`ZrzklE=5d45yPG4oFSS&KjRmm2!yg_J~ zRS6@>tY#wPsjDSYgZXMkzP21FW_82_E4`S$A8C}-thvHE7J_jnrCi-nm~uRVM+f%tRE!fGOSBqlF)lK*X&QNmmR1`w zFm`QVuSuPTDlYMsEXPC|0pWnpBAKlYrqU2ocDhd*qV>b2`n znhmeuTaIr|$7ZukO_RWv5(jd!5b_w#LIu45i(bVqD_B)2Q|+ z<)ZH|M`2Ov;ZmVqt9yH*ZWnJATogmb$g!0`1Xb;35{WRr$Agj3sWkU{-HN@iw~w@} zOKp2`pax+)Eb)bYXbL;s3#1MNAIRMfhC=W3km%@O6i(0}r>{fITXIOt9TCy2Ek^>! znN`h%9+0Cf(|pO<$WkNpRGwna{w%JgG+eABBN+rJC$?GZ8Ll)H;YzLHZONyZeE(YU wexw5Eqjrmwnq9Sa((yoz70LGSZ*iIOEau7XDY+rhZH0r;ZpX#JPzQd1f7o^aPyhe` literal 0 HcmV?d00001 diff --git a/app/examples/Misc/Notepad/.lang/es.po b/app/examples/Misc/Notepad/.lang/es.po new file mode 100644 index 00000000..f7343279 --- /dev/null +++ b/app/examples/Misc/Notepad/.lang/es.po @@ -0,0 +1,95 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FAbout.class:39 +msgid "About..." +msgstr "Acerca de..." + +#: FAbout.class:50 +msgid "OK" +msgstr "OK" + +#: FAbout.class:57 +msgid "This is a little notepad sample program." +msgstr "Este es un pequeño ejemplo del programa bloc de notas." + +#: FNotepad.class:256 +msgid "Little notepad" +msgstr "Pequeño bloc de notas" + +#: FNotepad.class:262 +msgid "&File" +msgstr "&Archivo" + +#: FNotepad.class:265 +msgid "&Open..." +msgstr "&Abrir" + +#: FNotepad.class:271 +msgid "Close" +msgstr "Cerrar" + +#: FNotepad.class:280 +msgid "&Save" +msgstr "&Guardar" + +#: FNotepad.class:285 +msgid "S&ave As..." +msgstr "G&uardar como..." + +#: FNotepad.class:294 +msgid "&Quit" +msgstr "&Salir" + +#: FNotepad.class:300 +msgid "&Edit" +msgstr "&Editar" + +#: FNotepad.class:303 +msgid "&Copy" +msgstr "&Copiar" + +#: FNotepad.class:308 +msgid "C&ut" +msgstr "C&ortar" + +#: FNotepad.class:313 +msgid "&Paste" +msgstr "&Pegar" + +#: FNotepad.class:322 +msgid "&Undo" +msgstr "&Deshacer" + +#: FNotepad.class:327 +msgid "&Redo" +msgstr "&Rehacer" + +#: FNotepad.class:336 +msgid "Choose &Font..." +msgstr "Seleccionar &fuente..." + +#: FNotepad.class:340 +msgid "&Wrap text" +msgstr "&Ajustar texto" + +#: FNotepad.class:346 +msgid "&?" +msgstr "&?" + +#: FNotepad.class:349 +msgid "&About..." +msgstr "&Acerca de..." + +#: FNotepad.class:357 +msgid "txtNotepad" +msgstr "txtNotepad" diff --git a/app/examples/Misc/Notepad/.project b/app/examples/Misc/Notepad/.project new file mode 100644 index 00000000..95a127d4 --- /dev/null +++ b/app/examples/Misc/Notepad/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=A little text editor +Startup=FNotepad +Icon=notepad.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Environment="GB_GUI=gb.gtk" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Misc/Notepad/.src/FAbout.class b/app/examples/Misc/Notepad/.src/FAbout.class new file mode 100644 index 00000000..4eb7eefb --- /dev/null +++ b/app/examples/Misc/Notepad/.src/FAbout.class @@ -0,0 +1,25 @@ +' Gambas class file + + +Static Public Sub Run() + + Dim hForm As Form + + hForm = New FAbout + hForm.ShowModal + +End + + +Public Sub _new() + + Me.Center + +End + + +Public Sub btnOK_Click() + + Me.Close + +End diff --git a/app/examples/Misc/Notepad/.src/FAbout.form b/app/examples/Misc/Notepad/.src/FAbout.form new file mode 100644 index 00000000..ccca69c6 --- /dev/null +++ b/app/examples/Misc/Notepad/.src/FAbout.form @@ -0,0 +1,22 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,29,17) + Text = ("About...") + Resizable = False + { Image1 PictureBox + MoveScaled(1,1,4,4) + Picture = Picture["notepad.png"] + Stretch = True + } + { btnOK Button + MoveScaled(7,12,15,4) + Text = ("OK") + Default = True + Cancel = True + } + { TextLabel1 TextLabel + MoveScaled(7,1,21,9) + Text = ("This is a little notepad sample program.") + } +} diff --git a/app/examples/Misc/Notepad/.src/FNotepad.class b/app/examples/Misc/Notepad/.src/FNotepad.class new file mode 100644 index 00000000..26000b5f --- /dev/null +++ b/app/examples/Misc/Notepad/.src/FNotepad.class @@ -0,0 +1,222 @@ +' Gambas class file + +Private $sPath As String +Private $bModify As Boolean + +Static Public Sub Main() + + FNotepad.Show + +End + + +Public Sub _new() + + txtNotepad.Text = "" + $bModify = False + RefreshTitle + txtNotePad.SetFocus + +End + + +Private Function GetName() As String + + If $sPath Then Return $sPath + + Return ("(New document)") + +End + + +Private Sub RefreshTitle() + + Dim sTitle As String + + If $bModify Then sTitle = "*" + sTitle = sTitle & GetName() + + Me.Title = sTitle + +End + + +Private Sub SetPath(sPath As String) + + $sPath = sPath + RefreshTitle + +End + +Private Sub SetModify(bModify As Boolean) + + If $bModify = bModify Then Return + $bModify = bModify + RefreshTitle + +End + + +Private Function CloseDoc() As Boolean + + If $bModify Then + Select Case Message.Question(GetName() & ("\n\nFile has been modified. Do you want to save it ?"), ("Yes"), ("No"), ("Cancel")) + Case 1 + Save + Case 3 + Return True + End Select + Endif + + $sPath = "" + txtNotepad.Text = "" + $bModify = False + RefreshTitle + +End + + + +Public Sub LoadFile(sPath As String) + + Dim sData As String + + If CloseDoc() Then Return + + sData = File.Load(sPath) + Try txtNotepad.Text = Conv(sData, System.Charset, Desktop.Charset) + If Error Then txtNotepad.Text = Conv(sData, "ISO_8859-1", Desktop.Charset) + $bModify = False + SetPath(sPath) + +Catch + + Message.Error(sPath & ("\nUnable to load file.\n") & Error.Text) + +End + + +Public Sub Save(Optional bSaveAs As Boolean) + + If bSaveAs Or Not $sPath Then + If Dialog.SaveFile() Then Return + SetPath(Dialog.Path) + Endif + + File.Save($sPath, txtNotepad.Text) + +End + + +Public Sub mnuOpen_Click() + + Dim sPath As String + + Dialog.Filter = ["*", ("All files"), "*.{c;cpp;h}", ("C/C++ files"), "*.txt", ("Text files"), "*.desktop", ("Desktop files")] + + If Dialog.OpenFile() Then Return + LoadFile(Dialog.Path) + +End + + +Public Sub mnuSave_Click() + + Save + +End + + +Public Sub mnuSaveAs_Click() + + Save(True) + +End + + +Public Sub mnuQuit_Click() + + Me.Close + +End + + +Public Sub txtNotepad_Change() + + SetModify(True) + +End + + +Public Sub mnuClose_Click() + + CloseDoc + +End + + +Public Sub Form_Close() + + If CloseDoc() Then Stop Event + +End + + +Public Sub mnuAbout_Click() + + 'Inc Application.Busy + FAbout.Run + 'Dec Application.Busy + +End + + +Public Sub mnuCopy_Click() + + txtNotepad.Copy + +End + + +Public Sub mnuPaste_Click() + + txtNotepad.Paste + +End + + +Public Sub mnuCut_Click() + + txtNotepad.Cut + +End + + +Public Sub mnuUndo_Click() + + txtNotepad.Undo + +End + + +Public Sub mnuRedo_Click() + + txtNotepad.Redo + +End + + +Public Sub mnuFont_Click() + + Dialog.Font = txtNotepad.Font + If Dialog.SelectFont() Then Return + txtNotepad.Font = Dialog.Font + +End + +Public Sub mnuWrap_Click() + + mnuWrap.Checked = Not mnuWrap.Checked + txtNotePad.Wrap = mnuWrap.Checked + +End diff --git a/app/examples/Misc/Notepad/.src/FNotepad.form b/app/examples/Misc/Notepad/.src/FNotepad.form new file mode 100644 index 00000000..ac8112d0 --- /dev/null +++ b/app/examples/Misc/Notepad/.src/FNotepad.form @@ -0,0 +1,93 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(12.5714,12.5714,50,50) + Text = ("Little notepad") + Icon = Picture["notepad.png"] + Arrangement = Arrange.Fill + { mnuFile Menu + Text = Shortcut(("File"), "F") + { mnuOpen Menu + Text = Shortcut(("Open"), "O") & "..." + Picture = Picture["icon:/16/new"] + Shortcut = "Ctrl+O" + } + { mnuClose Menu + Text = ("Close") + Picture = Picture["icon:/16/close"] + Shortcut = "Ctrl+W" + } + { Menu1 Menu + } + { mnuSave Menu + Text = Shortcut(("Save"), "S") + Picture = Picture["icon:/16/save"] + Shortcut = "Ctrl+S" + } + { mnuSaveAs Menu + Text = Shortcut(("Save As"), "a") & "..." + Shortcut = "Ctrl+Shift+S" + } + { Menu2 Menu + } + { mnuQuit Menu + Text = Shortcut(("Quit"), "Q") + Picture = Picture["icon:/16/quit"] + Shortcut = "Ctrl+Q" + } + } + { mnuEdit Menu + Text = Shortcut(("Edit"), "E") + { mnuCopy Menu + Text = Shortcut(("Copy"), "C") + Picture = Picture["icon:/16/copy"] + Shortcut = "Ctrl+C" + } + { mnuCut Menu + Text = Shortcut(("Cut"), "u") + Picture = Picture["icon:/16/cut"] + Shortcut = "Ctrl+X" + } + { mnuPaste Menu + Text = Shortcut(("Paste"), "P") + Picture = Picture["icon:/16/paste"] + Shortcut = "Ctrl+V" + } + { Menu3 Menu + } + { mnuUndo Menu + Text = Shortcut(("Undo"), "U") + Picture = Picture["icon:/16/undo"] + Shortcut = "Ctrl+Z" + } + { mnuRedo Menu + Text = Shortcut(("Redo"), "R") + Picture = Picture["icon:/16/redo"] + Shortcut = "Ctrl+Y" + } + { Menu4 Menu + } + { mnuFont Menu + Text = Shortcut(("Choose Font"), "F") & "..." + } + { mnuWrap Menu + Text = Shortcut(("Wrap text"), "W") + Picture = Picture["icon:/16/text-fill"] + Checked = True + } + } + { mnuHelp Menu + Text = Shortcut(("?"), "?") + { mnuAbout Menu + Text = Shortcut(("About"), "A") & "..." + } + } + { txtNotepad TextArea + MoveScaled(1,1,35,26) + Font = Font["Liberation Mono,9"] + Expand = True + Text = ("txtNotepad") + Wrap = True + Border = False + } +} diff --git a/app/examples/Misc/Notepad/notepad.png b/app/examples/Misc/Notepad/notepad.png new file mode 100644 index 0000000000000000000000000000000000000000..3be5a715d315f1ae71f5aa52871fef3547f1e4de GIT binary patch literal 4015 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 literal 0 HcmV?d00001 diff --git a/app/examples/Misc/PDFViewer/.directory b/app/examples/Misc/PDFViewer/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Misc/PDFViewer/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Misc/PDFViewer/.icon.png b/app/examples/Misc/PDFViewer/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ea9495756338ebad0b64dc3c63afb034bfde3afd GIT binary patch literal 4253 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, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: PDFViewer\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 21:38+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "PdfViewer" +msgstr "VisualitzadorPdf" + +#: .project:2 +msgid "A simple PDF Viewer, used as example of gb.pdf capabilities" +msgstr "Un visualitzador simple de PDF, com a exemple de les capacitats de gb.pdf" + +#: FMain.class:40 +msgid "Pdf documents" +msgstr "Documents pdf" + +#: FMain.class:114 +msgid "From" +msgstr "Des de" + +#: FMain.class:253 +msgid "Simple PDF document viewer" +msgstr "Visualitzador simple de documents PDF" + +#: FMain.class:261 +msgid "Open file..." +msgstr "Obre fitxer..." + +#: FMain.class:268 +msgid "About..." +msgstr "Quant a..." + +#: FMain.class:276 +msgid "Rotate" +msgstr "Rota" + +#: FMain.class:284 +msgid "Zoom out" +msgstr "Allunya" + +#: FMain.class:292 +msgid "Zoom in" +msgstr "Apropa" + +#: FMain.class:300 +msgid "Find text" +msgstr "Cerca el següent" + +#: FMain.class:319 +msgid "Previous page" +msgstr "Pàgina anterior" + +#: FMain.class:327 +msgid "Next page" +msgstr "Pàgina següent" + +#: Fabout.class:61 +msgid "OK" +msgstr "D'acord" + +#: Fabout.class:68 +msgid "Simple PDF document viewer.

    Gambas example by Daniel Campos Fernández " +msgstr "Visualitzador simple de documents PDF.

    Exemple del Gambas per Daniel Campos Fernández " + diff --git a/app/examples/Misc/PDFViewer/.lang/cs.mo b/app/examples/Misc/PDFViewer/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..2cca4d287daf7e266d10bd5515c12e0bb38b273f GIT binary patch literal 1407 zcmZ{jzmFS56vqb$zs!%2fP@r*^dt&|$T~n3IgXX(ygr=RUdz5ai73r@cWh6*p4sfq zn&dia1RX_0!_{#7=%65tV>QK+Zy6 zgMJNt9a?=1H^_IO0>1|@fv-L;#4}(Yd=|U`o(6|t37oC>KL&A$PjGu4{0uw=egVD! zehKpVZ`SJ{z^AbO89WF60zM7?4syIdL4h3p0&n5*?)|ifo#ux<2A=-!S=Er zoG#lV&1D|Ii6HceaE&a_LAj0>q1=}<(2Z~&CsG~1SL^F(nkeel8x-59m@4CFrqxF( zt6sqCYFgWs(}B!M-Ihzs!5ykgqg6sX*swV@RA%N!CRT?;(!}&W*0^WdOs3Kp;Zwei zaj^pICOq|u<9#zTr^DhE2zPODt_16#OqxhxtQKApPl#I7ZWYpiohq?7&Dc9oS+1?AP}kqN=I;h{5cK-Zuv6-WUJqgTrB_FQ3Vj&(J+B>lb)P;6 zEA8gn*TYUwYRTMrdzmzOBAv}D)YxyesN=T-ss?Sp*}CG_>%E}guff!e3KZt;DLJiB z%kS*&bI59|btTbdn~kbVyXALEJJvXSZ*T8TihQ`c7t%y(bBUs{%yQ)} z@9#Cdi(5NRYp60WFp-V58CB@wK)aIQLAt79V_gxA=RZ-J*>N&IUffgnKV6U3WI0-k zZ~rq@>EiDEc#_DNZ2aiClK&g3{mK03a3Yt^AC9KZ)7E9NxToFxnDS_BQ)6?d*Fz>k zl4+tRGSWO99eFd>tLeIm2P4T9n6cbtnCnR)>M~gz!Hg;jB9a} z+hSld_S;`MkYe?Bty%!v?&7YBk<%gPwz1*4iPx# literal 0 HcmV?d00001 diff --git a/app/examples/Misc/PDFViewer/.lang/cs.po b/app/examples/Misc/PDFViewer/.lang/cs.po new file mode 100644 index 00000000..6e4fb0fb --- /dev/null +++ b/app/examples/Misc/PDFViewer/.lang/cs.po @@ -0,0 +1,80 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "PdfViewer" +msgstr "-" + +#: .project:2 +msgid "A simple PDF Viewer, used as example of gb.pdf capabilities" +msgstr "Ukázka PDF prohlížeče, příklad užití schopnosti gb.pdf" + +#: FMain.class:52 +msgid "Pdf documents" +msgstr "PDF dokument" + +#: FMain.form:30 +msgid "Simple PDF document viewer" +msgstr "Ukázka prohížeče PDF dokumentů" + +#: FMain.form:38 +msgid "Open file..." +msgstr "Otevřít soubor..." + +#: FMain.form:44 +msgid "About..." +msgstr "O aplikaci..." + +#: FMain.form:51 +msgid "Rotate" +msgstr "Rotace" + +#: FMain.form:58 +msgid "Zoom out" +msgstr "Oddálit" + +#: FMain.form:65 +msgid "Zoom in" +msgstr "Přiblížit" + +#: FMain.form:76 +msgid "Find text" +msgstr "Najdi text" + +#: FMain.form:90 +msgid "Previous page" +msgstr "Předchozí stránka" + +#: FMain.form:99 +msgid "Next Found" +msgstr "Další nalezeno" + +#: FMain.form:119 +msgid "Page" +msgstr "Stránka" + +#: FMain.form:130 +msgid "Go to this page" +msgstr "Jdi na tuto stránku" + +#: FMain.form:143 +msgid "Next page" +msgstr "Další stránka" + +#: Fabout.form:20 +msgid "

    Simple PDF document viewer

    Gambas example by Daniel Campos Fernández and Bernd Brinkmann" +msgstr "

    Jednotuchý prohlížeč PDF dokumentů

    Gambas příklad od Daniel Campos Fernández a Bernd Brinkmann" + +#: Fabout.form:34 +msgid "OK" +msgstr "-" diff --git a/app/examples/Misc/PDFViewer/.lang/de.mo b/app/examples/Misc/PDFViewer/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..cd94fa0a33fd01ec36c0ed712adea7feab2363bc GIT binary patch literal 1402 zcmZ{jzmFS56vrnJ2$&!FRUjddo+zLo>yT85V@}ze*T#x}$@ZN@6bX&JZ|&jMGppJ4 zk))$WqN4%{kRs6_M4E^KDoZqoe*s!5lol$!Z|$4|=$>}=vorJN{hGJGojduNf}F#6 z4dYvk*D>lR@Pd2~GWYm{Ry4{AA!$+C-9PUr$Es=4@y37fD+GT@GbBvD0yg5^erg%q;3+Qr8;)JF;(-V`L!f>v_iCY%cYE?B2Y!-Z{P%TFVp1H{>(=%2* zEK<`gZM^<07yCC=KVwUYNm-5$L~{V^k8{jrwhl%OJRcgj+}InsLKm!#uZz0n+=7f< z6Y;@-`;eza(2j$B_PKGkMuTwoX4r~oKN^nO{a$6z4~B3fn?VB(YV>{-4uft#XoU1$ zzt(NPeXHM#Djl5{!Psk?r@C;yM$NrWhk9W*qI%Q~+nwuSqcMy|qaCQ)*%FBbW7dlr zb;4e2Ph8eJo$INoIzQX$)Tq*JccX25m#XhncAYJ7-@*89hIpj97qe6wdyS@Z?Q<^P z-5WQ9D_cDYD`6i*cIskdXEnMqF-1j5BSpq0dOBw>y%gNw!edtaw~er2@=Utt(Anb~ z3Ag#bK7s?{W1L)o_Qr0q`q5Ktn3}8mb2B?&gB(k5>jqPAuD+PCm#!E~>m=wbwyMQ* zrh7{oiN5Ns?oa1=0V9Y6Ow%XU)+&LLYc#Zam?W01*jsO)iOU-{u=?*(sD0;AAT#5Q pn}&y1tG1=vmW|3h\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "PdfViewer" +msgstr "PDF-Betrachter" + +#: .project:2 +msgid "A simple PDF Viewer, used as example of gb.pdf capabilities" +msgstr "Ein einfacher PDF-Betrachter, Beispiel für die Fähigkeiten von gb.pdf" + +#: FMain.class:52 +msgid "Pdf documents" +msgstr "pdf-Dateien" + +#: FMain.form:30 +msgid "Simple PDF document viewer" +msgstr "Einfacher Betrachter für PDF-Dokumente" + +#: FMain.form:38 +msgid "Open file..." +msgstr "Datei öffnen..." + +#: FMain.form:44 +msgid "About..." +msgstr "&Über..." + +#: FMain.form:51 +msgid "Rotate" +msgstr "Drehen" + +#: FMain.form:58 +msgid "Zoom out" +msgstr "Verkleinern" + +#: FMain.form:65 +msgid "Zoom in" +msgstr "Vergrößern" + +#: FMain.form:76 +msgid "Find text" +msgstr "Text finden" + +#: FMain.form:90 +msgid "Previous page" +msgstr "Vorherige Seite" + +#: FMain.form:99 +msgid "Next Found" +msgstr "Nächste Fundstelle" + +#: FMain.form:119 +msgid "Page" +msgstr "Seite" + +#: FMain.form:130 +msgid "Go to this page" +msgstr "Gehe zu Seite" + +#: FMain.form:143 +msgid "Next page" +msgstr "Nächste Seite" + +#: Fabout.form:20 +msgid "

    Simple PDF document viewer

    Gambas example by Daniel Campos Fernández and Bernd Brinkmann" +msgstr "

    Einfacher PDF-Betrachter

    Gambas-Beispiel von Daniel Campos Fernández und Bernd Brinkmann" + +#: Fabout.form:34 +msgid "OK" +msgstr "-" diff --git a/app/examples/Misc/PDFViewer/.lang/es.mo b/app/examples/Misc/PDFViewer/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..4f5eac7c009f9dca8fc0af176e4fc3618047e6f9 GIT binary patch literal 1120 zcmaiyyKfUg5XKh>FY^$O4k0u>-0`S_6QUfxb7I9W*^VXX)^}@Xqr2VH-d#v2=%_%@ zK#NF6MGbW%Iyx!}YW@KVBnrN@F9{8>()j1Oot^o1XTDvUd(E)U!>+=f!!E;q!R%T+ z!q@`X08fKY!8!0{>AwPx!hZvv1$WEu@4<8MKY%B|Pv8~sGuQyXgD1f|ID87MftpHffyd#00k45y!9MsCv~^k#Z{vp`Butl$ zx7yFl+=E{mXSLgVRABavY;Ajgn+qwY+5pBp%4Cwpg1Ua4hEhBd<^~m+h=^w-w)u2V zk7+V;(`ZZ~Px(m3GM6G_b*UoC#dgl>MklO|f0XixV4XGANrj@ZjD_pEtQ(DIsae;E zEvbua7T41`&jsrrDu{GgBtqr1HI?#@Eq5_pTH(p)51FGK^0|^CrW$nXjOxOuyLP~}y(z-nX++LYm`rL_rLI?7^Hu^H2EBfx)2?(oP7i|ZrsG3ul^z6M z&uMiW-=oz|wbi)(u+t7IO`he>z;KntJlCd5_4Q_x+FmQ5#h~Rin#-Q=_kw8|ICSG{jA~A-A+0 zg|cciJ~+e?Z=^QNPJ<}}%ArKb{fR(3q4hu8b|`}K1}Qh3$_Ytv)4z>|%e?8xp|&rx JM5;m>_8WBeB?kZi literal 0 HcmV?d00001 diff --git a/app/examples/Misc/PDFViewer/.lang/es.po b/app/examples/Misc/PDFViewer/.lang/es.po new file mode 100644 index 00000000..73d95061 --- /dev/null +++ b/app/examples/Misc/PDFViewer/.lang/es.po @@ -0,0 +1,64 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:2 +msgid "A simple PDF Viewer, used as example of gb.pdf capabilities" +msgstr "Un simple Visor PDF, usado como ejemplo de las capacidades de gb.pdf" + +#: FMain.class:276 +msgid "Find text" +msgstr "Buscar texto" + +#: FMain.class:110 +msgid "From" +msgstr "de" + +#: FMain.class:321 +msgid "Next page" +msgstr "Página siguiente" + +#: Fabout.class:61 +msgid "OK" +msgstr "Aceptar" + +#: FMain.class:255 +msgid "Open file..." +msgstr "Abrir archivo..." + +#: .project:1 +msgid "PdfViewer" +msgstr "Visor Pdf" + +#: FMain.class:313 +msgid "Previous page" +msgstr "Página anterior" + +#: FMain.class:283 +msgid "Rotate" +msgstr "Rotar" + +#: FMain.class:245 +msgid "Simple PDF document viewer" +msgstr "Visor simple de documentos PDF" + +#: Fabout.class:68 +msgid "Simple PDF document viewer.

    Gambas example by Daniel Campos Fernández " +msgstr "Visor simple de documentos PDF.

    Ejemplo para Gambas por Daniel Campos Fernández." + +#: FMain.class:299 +msgid "Zoom in" +msgstr "Aumentar" + +#: FMain.class:291 +msgid "Zoom out" +msgstr "Disminuir" + diff --git a/app/examples/Misc/PDFViewer/.project b/app/examples/Misc/PDFViewer/.project new file mode 100644 index 00000000..76c413c1 --- /dev/null +++ b/app/examples/Misc/PDFViewer/.project @@ -0,0 +1,22 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=PdfViewer +Startup=FMain +Icon=pdf.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.pdf +Description="A simple PDF Viewer, used as example of gb.pdf capabilities" +Authors="Daniel Campos Fernández " +Environment="GB_GUI=gb.qt4" +TabSize=2 +Translate=1 +Language=en +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Misc/PDFViewer/.src/FMain.class b/app/examples/Misc/PDFViewer/.src/FMain.class new file mode 100644 index 00000000..8aebfde6 --- /dev/null +++ b/app/examples/Misc/PDFViewer/.src/FMain.class @@ -0,0 +1,472 @@ +' Gambas class file + +'*************************************************************************** +' +' FMain.class +' +' PdfViewer gb.pdf component example +' +' (C) 2007 Daniel Campos Fernández +' 2012 Bernd Brinkmann (modifications on the search and zoom function) +' +' This program is free software; you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation; either version 1, or (at your option) +' any later version. +' +' This program is free software; you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation; either version 1, or (at your option) +' any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY; without even the implied warranty of +' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +' GNU General Public License for more details. +' +' You should have received a copy of the GNU General Public License +' along with this program; if not, write to the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** + +Public hPdf As New PdfDocument +Public cIndex As Integer +Public CurrentPage As Integer +Public CurrentZoom As Float = 1.0 +Private bExit As Boolean +Private currentSearchResult As Short = 0 'Contains the information which of the search results on this page is marked +Private NumberOfSearchResults As Integer +Private currentSearchResultSynonyms As Short = 0 +Private ScrollXPositionBeforeZoom As Float +Private ScrollYPositionBeforeZoom As Float + + +Public Sub BtOpen_Click() + + Dialog.Filter = ["*.pdf", ("Pdf documents")] + If Dialog.OpenFile(False) Then Return + + hPdf.Close() + Try hPdf.Open(Dialog.Path) + If Error Then + Message.Error(Error.Text) + Return + End If + + CurrentZoom = 1 + CurrentPage = 1 + RenderPage() + BtPrev.Enabled = False + If hPdf.Count > 1 Then + BtNext.Enabled = True + Else + BtNext.Enabled = False + End If + txtGotoPage.Enabled = True + btZoomIn.Enabled = True + btZoomOut.Enabled = True + btRotate.Enabled = True + pBox.Visible = True + txtFind.Enabled = True + + tvIndex.Clear() + If hPdf.HasIndex Then + tvIndex.Visible = True + AddIndex(0, "") + splIndex.Layout = [1, 3] + Else + tvIndex.Visible = False + End If + +End + +Public Function AddIndex(nItem As Integer, pItem As String) As Integer + + Dim pR As String + Dim iPage As Integer + + Do + iPage = 1 + Try iPage = hPdf.Index.Data.Page + If Error Then Print Error.Text + pR = nItem & "-" & iPage + tvIndex.Add(nItem & "-" & iPage, hPdf.Index.Title, Null, pItem) + Inc nItem + If hPdf.Index.HasChildren Then + hPdf.Index.MoveChild() + nItem = AddIndex(nItem + 1, pR) + hPdf.Index.MoveParent() + End If + Loop Until hPdf.Index.MoveNext() + + Return nItem + +End + +Public Sub tvIndex_Click() + + CurrentPage = Mid(tvIndex.Current.Key, InStr(tvIndex.Current.Key, "-") + 1) + BtPrev.Enabled = True + BtNext.Enabled = True + If CurrentPage = 1 Then BtPrev.Enabled = False + If CurrentPage = hPdf.Count Then BtNext.Enabled = False + RenderPage() + +End + +Public Sub RenderPage(Optional FoundText As String, Optional Casesensetivity As Boolean) + '--------------------------------------------------------------------------- + 'This function is called everytime something changed for example a new search result has to be displayed or the page + 'has canged the parameters FoundText and Casesensetivity are only of interrest if the funktion is called from a search frunction + '-------------------------------------------------------------------------- + Dim hPic As Picture + + ScrollXPositionBeforeZoom = ViewPort.ScrollX / ViewPort.ScrollWidth + ScrollYPositionBeforeZoom = ViewPort.ScrollY / ViewPort.ScrollHeight + + If CurrentZoom > 0.0 Then + hPdf.Zoom = CurrentZoom + Else + Message.Error("Can't set zoom to " & CurrentZoom, "OK") + Endif + + lblInfo.Text = " of" & " " & hPdf.Count + If txtGotoPage.text <> CurrentPage Then txtGotoPage.text = CurrentPage 'if the parameter currentPage is different from the current page the current page gets changed + If currentSearchResult = 0 Then 'if a search result is highlighted the focus will not be changed to the top + ViewPort.Scroll(0, 0) + Endif + + hPic = hPdf[CurrentPage].Image.Picture + + 'the search result gets highlighted here and the focus is set to the right position again if something has changed for example the zoomfactor + If currentSearchResult > 0 And FoundText <> "" Then ' resets the mark on the current search result + hPdf[CurrentPage].Find(FoundText, Casesensetivity) + Paint.Begin(hPic) + Paint.Brush = Paint.Color(Color.RGB(0, 0, 255, 192)) + Paint.Rectangle(hPdf[CurrentPage].Result[currentSearchResult - 1].Left, hPdf[CurrentPage].Result[currentSearchResult - 1].Top, hPdf[CurrentPage].Result[currentSearchResult - 1].Width, hPdf[CurrentPage].Result[currentSearchResult - 1].Height) + ViewPort.ScrollY = ViewPort.ScrollHeight * (hPdf[CurrentPage].Result[currentSearchResult - 1].Top / Paint.Height) + Paint.Fill + Paint.End + PBox.Picture = hPic + Endif + + PBox.Picture = hPic + PBox.Resize(hPdf[CurrentPage].Width, hPdf[CurrentPage].Height) + Form_Resize() + +Catch + Message.Info("An error occurred whilst trying to view the document.\n\nIf this persists please report this problem.") + +End + + +Public Sub Form_Resize() + + If CurrentPage = 0 Then Return + ViewPort.ScrollX = ScrollXPositionBeforeZoom * ViewPort.ScrollWidth + ViewPort.Scrolly = ScrollYPositionBeforeZoom * ViewPort.ScrollHeight + +End + + +Public Sub splIndex_Resize() + + Form_Resize() + +End + +Public Sub BtNext_Click() + + Inc CurrentPage + currentSearchResult = 0 + currentSearchResultSynonyms = 0 + If CurrentPage = hPdf.Count Then + BtNext.Enabled = False + End If + BtPrev.Enabled = True + BtPrev.SetFocus + txtGotoPage.text = CurrentPage 'this automatically calls the function txtGotoPage_Change and changes the page to the new "currentPage" + +End + +Public Sub BtPrev_Click() + + Dec CurrentPage + currentSearchResult = 0 + currentSearchResultSynonyms = 0 + If CurrentPage = 1 Then + BtPrev.Enabled = False + End If + BtNext.Enabled = True + BtNext.SetFocus + txtGotoPage.text = CurrentPage 'this automatically calls the function txtGotoPage_Change and changes the page to the new "currentPage" + +End +Public Sub btZoomIn_Click() + + If CurrentZoom < 3 Then CurrentZoom += 0.1 + If CurrentZoom = 3 Then btZoomIn.Enabled = False + btZoomOut.Enabled = True + RenderPage() + +End + +Public Sub btZoomOut_Click() + + If CurrentZoom > 0.5 Then CurrentZoom -= 0.1 + If CurrentZoom = 0.5 Then btZoomOut.Enabled = False + btZoomIn.Enabled = True + RenderPage() + +End + +Public Sub Form_Close() + + hPdf.Close() + +End + +Public Sub Button1_Click() + + Fabout.ShowDialog() + +End + +Public Sub btRotate_Click() + + Select Case hPdf.Orientation + Case PdfDocument.Normal + hPdf.Orientation = PdfDocument.Sideways + Case PdfDocument.Sideways + hPdf.Orientation = PdfDocument.Inverted + Case PdfDocument.Inverted + hPdf.Orientation = PdfDocument.SidewaysInverted + Case PdfDocument.SidewaysInverted + hPdf.Orientation = PdfDocument.Normal + End Select + + RenderPage() + +End + +Public Sub txtGotoPage_Change() + + '--------------------------------------------------------------------------- + 'This function changes the page to the page number written in the textbox "txtGotoPage" + 'the text in this textbox can be changed by the user for example by klicking on the next button or other funktions such as + 'a search funktion + '-------------------------------------------------------------------------- + ' + If Bexit Or Last.text = "" Then Return + 'the last search results get removed by the next four lines + BtSearchNext.Enabled = False + BtSearchPrev.Enabled = False + currentSearchResult = 0 + If Val(Last.text) > hPdf.Count Or Val(Last.text) = hPdf.Count Then 'hPdf.count contains the length of the pdf document + bExit = True + txtGotoPage.text = hPdf.Count + BtNext.Enabled = False 'because the last page is now displayed the next page button gets disabled + BtPrev.Enabled = True + bExit = False + Else + BtNext.Enabled = True + If Val(Last.text) = 1 Or Val(Last.text) < 1 Then + bExit = True + txtGotoPage.text = 1 + bExit = False + BtPrev.Enabled = False + Else + BtPrev.Enabled = True + End If + End If + currentPage = Val(Last.text) + Bexit = False + RenderPage() + +End + +Public Sub txtGotoPage_KeyPress() + + ' If modUtil.AllowKeys(const.AllowKeys_NumbersOnly, Key.code) = False Then + ' Stop Event + ' Return + ' End If + +End + +Public Sub txtFind_Activate() + + '--------------------------------------------------------------------------- + 'This function is called if the user wants fo find the string inside the textbox "TxtFind" by hitting the enter key + '-------------------------------------------------------------------------- + + If Bexit Then Return + If txtFind.Text <> "" Then + If currentSearchResult > 0 Then + If currentSearchResult = NumberOfSearchResults Then + If currentpage = hPdf.Count Then + CurrentPage = 1 + Else + CurrentPage = CurrentPage + 1 + Endif + FindNext() + Else + currentSearchResult = currentSearchResult + 1 + RenderPage(txtFind.Text) + Endif + Else + FindNext() + Endif + End If + +End + +Public Sub txtFind_Click() + + txtFind_Activate + +End + + +Private Sub FindNext() + '--------------------------------------------------------------------------- + ' This function finds the next string in the pdf matching the search string located after the current search result + '--------------------------------------------------------------------------- + Dim hPic As Picture + Dim currentSearchPage As Short = CurrentPage ' contains the information on which page the search funktion searched the last time + Dim LastPageToSearch As Short ' contains the information which page is the last page for the find function + + If CurrentPage = 1 Then + LastPageToSearch = 1 + Else + LastPageToSearch = CurrentPage + + Endif + currentSearchResultSynonyms = 0 + currentSearchResult = 0 + BtSearchNext.Enabled = False + BtSearchPrev.Enabled = False + Repeat + hPic = hPdf[currentSearchPage].Image.Picture + hPdf[currentSearchPage].Find(txtFind.Text, False) + + If hPdf[currentSearchPage].Result.Count > 0 Then + CurrentPage = currentsearchPage + currentSearchResult = 1 + Else + If currentSearchPage = hPdf.Count Then + currentSearchPage = 1 + Else + currentSearchPage = currentSearchPage + 1 + Endif + Endif + Until currentSearchResult <> 0 Or currentSearchPage = LastPageToSearch + If hPdf[currentSearchPage].Result.Count > 0 Then + CurrentPage = currentSearchPage + + txtGotoPage.text = CurrentPage + currentSearchResult = 1 + NumberOfSearchResults = hPdf[currentSearchPage].Result.Count + BtSearchNext.Enabled = True + BtSearchPrev.Enabled = True + + + RenderPage(txtFind.Text) + + Else + txtFind.Background = Color.Lighter(16711680) '16711680 = Red + RenderPage() + + Endif + +Catch + Message.Info("An error occurred whilst trying to view the document.\n\nIf this persists please report this problem.\n\n" & Error.Where & ": " & Error.Text) + +End + +Private Sub FindPrevious() + '--------------------------------------------------------------------------- + ' This function finds the next string in the pdf matching the search string located before the current search result + '--------------------------------------------------------------------------- + Dim hPic As Picture + Dim currentSearchPage As Short = CurrentPage ' contains the information on which page the search funktion searched the last time + Dim LastPageToSearch As Short ' contains the information which page is the last page for the find function + + If CurrentPage = hPdf.Count Then + LastPageToSearch = 0 + Else + LastPageToSearch = CurrentPage + Endif + currentSearchResultSynonyms = 0 + currentSearchResult = 0 + BtSearchNext.Enabled = False + BtSearchPrev.Enabled = False + + Repeat + hPic = hPdf[currentSearchPage].Image.Picture + hPdf[currentSearchPage].Find(txtFind.Text, False) + If hPdf[currentSearchPage].Result.Count > 0 Then + CurrentPage = currentsearchPage + currentSearchResult = 1 + Else + If currentSearchPage = 1 Then + currentSearchPage = hPdf.Count + Else + currentSearchPage = currentSearchPage - 1 + Endif + Endif + Until currentSearchResult <> 0 Or currentSearchPage = LastPageToSearch + + If hPdf[currentSearchPage].Result.Count > 0 Then + CurrentPage = currentSearchPage + + txtGotoPage.text = CurrentPage + currentSearchResult = hPdf[currentSearchPage].Result.Count + NumberOfSearchResults = hPdf[currentSearchPage].Result.Count + BtSearchNext.Enabled = True + BtSearchPrev.Enabled = True + RenderPage(txtFind.Text) + Endif + +Catch + Message.Info("An error occurred whilst trying to view the document.\n\nIf this persists please report this problem.") + +End + +Public Sub BtSearchNext_Click() + '--------------------------------------------------------------------------- + 'This funktion is highlighting the next seach string located after the current search string + '--------------------------------------------------------------------------- + If currentSearchResult = NumberOfSearchResults Then + If currentpage = hPdf.Count Then + CurrentPage = 1 + Else + CurrentPage = CurrentPage + 1 + Endif + FindNext() + Else + currentSearchResult = currentSearchResult + 1 + RenderPage(txtFind.Text) + Endif + +End + +Public Sub BtSearchPrev_Click() + '--------------------------------------------------------------------------- + 'This funktion is highlighting the next seach string located before the current search string + '--------------------------------------------------------------------------- + If currentSearchResult > 0 Then + If currentpage = 1 Then + currentpage = hPdf.Count + Else + CurrentPage = CurrentPage - 1 + Endif + FindPrevious() + Else + currentSearchResult = currentSearchResult - 1 + RenderPage(txtFind.Text) + Endif + +End + diff --git a/app/examples/Misc/PDFViewer/.src/FMain.form b/app/examples/Misc/PDFViewer/.src/FMain.form new file mode 100644 index 00000000..fb055f2c --- /dev/null +++ b/app/examples/Misc/PDFViewer/.src/FMain.form @@ -0,0 +1,126 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,106,99) + Text = ("Simple PDF document viewer") + Arrangement = Arrange.Vertical + { HBox1 HBox + MoveScaled(1,2,95,4) + { BtOpen ToolButton + MoveScaled(0,0,4,4) + ToolTip = ("Open file...") + Picture = Picture["icon:/small/open"] + } + { Button1 ToolButton + MoveScaled(4,0,4,4) + ToolTip = ("About...") + Picture = Picture["icon:/small/question"] + } + { btRotate ToolButton + MoveScaled(8,0,4,4) + Enabled = False + ToolTip = ("Rotate") + Picture = Picture["icon:/small/rotate-right"] + } + { btZoomOut ToolButton + MoveScaled(12,0,4,4) + Enabled = False + ToolTip = ("Zoom out") + Picture = Picture["icon:/small/zoom-out"] + } + { btZoomIn ToolButton + MoveScaled(16,0,4,4) + Enabled = False + ToolTip = ("Zoom in") + Picture = Picture["icon:/small/zoom-in"] + } + { Separator3 Separator + MoveScaled(22,0,0,4) + } + { txtFind ButtonBox + MoveScaled(24,0,15,4) + Enabled = False + ToolTip = ("Find text") + Expand = True + Picture = Picture["icon:/small/find"] + Border = False + } + { Panel1 Panel + MoveScaled(40,0,8,4) + Visible = False + Background = Color.TextBackground + { BtSearchPrev Button + MoveScaled(0,0,4,4) + Enabled = False + ToolTip = ("Previous page") + Picture = Picture["icon:/small/left"] + Border = False + } + { BtSearchNext Button + MoveScaled(4,0,4,4) + Enabled = False + Background = Color.TextBackground + ToolTip = ("Next Found") + Picture = Picture["icon:/small/right"] + Border = False + } + } + { Separator1 Separator + MoveScaled(51,0,0,4) + } + { BtPrev ToolButton + MoveScaled(54,0,4,4) + Enabled = False + ToolTip = ("Previous page") + Picture = Picture["icon:/small/left"] + } + { Page Label + MoveScaled(59,0,4,4) + AutoResize = True + Text = ("Page") + Alignment = Align.Right + } + { Panel2 Panel + MoveScaled(64,0,1,4) + } + { txtGotoPage TextBox + MoveScaled(66,0,9,4) + Enabled = False + ToolTip = ("Go to this page") + Alignment = Align.Center + Border = False + } + { lblInfo Label + MoveScaled(75,0,7,4) + AutoResize = True + } + { BtNext ToolButton + MoveScaled(84,0,4,4) + Enabled = False + ToolTip = ("Next page") + Picture = Picture["icon:/small/right"] + } + } + { Separator2 Separator + MoveScaled(36,8,27,0) + } + { splIndex HSplit + MoveScaled(9,11,81,60) + Expand = True + { tvIndex TreeView + MoveScaled(0,0,19,25) + Visible = False + Border = False + } + { ViewPort ScrollView + MoveScaled(36,5,30,32) + Background = Color.TextBackground + Expand = True + Border = False + { PBox PictureBox + MoveScaled(3,3,24,21) + Visible = False + } + } + } +} diff --git a/app/examples/Misc/PDFViewer/.src/Fabout.class b/app/examples/Misc/PDFViewer/.src/Fabout.class new file mode 100644 index 00000000..ddfa3fb9 --- /dev/null +++ b/app/examples/Misc/PDFViewer/.src/Fabout.class @@ -0,0 +1,43 @@ +' Gambas class file + +'*************************************************************************** +' +' FAbout.class +' +' PdfViewer gb.pdf component example +' +' (C) 2007 Daniel Campos Fernández +' +' +' This program is free software; you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation; either version 1, or (at your option) +' any later version. +' +' This program is free software; you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation; either version 1, or (at your option) +' any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY; without even the implied warranty of +' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +' GNU General Public License for more details. +' +' You should have received a copy of the GNU General Public License +' along with this program; if not, write to the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** + +PUBLIC SUB btOK_Click() + + ME.Close() + +END + +PUBLIC SUB Form_Open() + + + +END diff --git a/app/examples/Misc/PDFViewer/.src/Fabout.form b/app/examples/Misc/PDFViewer/.src/Fabout.form new file mode 100644 index 00000000..1b8565a2 --- /dev/null +++ b/app/examples/Misc/PDFViewer/.src/Fabout.form @@ -0,0 +1,32 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,48,22) + Resizable = False + Arrangement = Arrange.Vertical + Spacing = True + Margin = True + { TextLabel1 TextLabel + MoveScaled(2,2,45,13) + Expand = True + Text = ("

    Simple PDF document viewer

    Gambas example by Daniel Campos Fernández and Bernd Brinkmann") + Alignment = Align.Center + } + { Panel1 HBox + MoveScaled(13,16,32,4) + { Panel2 Panel + MoveScaled(4,0,2,4) + Expand = True + } + { btOK Button + MoveScaled(10,0,15,4) + Text = ("OK") + Default = True + Cancel = True + } + { Panel3 Panel + MoveScaled(26,0,2,4) + Expand = True + } + } +} diff --git a/app/examples/Misc/PDFViewer/pdf.png b/app/examples/Misc/PDFViewer/pdf.png new file mode 100644 index 0000000000000000000000000000000000000000..651ded047317f9ff354a60a1ef9fd656a2720939 GIT binary patch literal 4588 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 literal 0 HcmV?d00001 diff --git a/app/examples/Misc/SystemTray/.project b/app/examples/Misc/SystemTray/.project new file mode 100644 index 00000000..0d68af75 --- /dev/null +++ b/app/examples/Misc/SystemTray/.project @@ -0,0 +1,14 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=SystemTray +Startup=FMain +Icon=icon.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.desktop.x11 +Description="X11 System tray example.\n\nThis example shows how to create a system tray with the gb.desktop.x11 component." +TabSize=2 +Vendor=Example +Packager=1 diff --git a/app/examples/Misc/SystemTray/.src/FMain.class b/app/examples/Misc/SystemTray/.src/FMain.class new file mode 100644 index 00000000..87e76fb6 --- /dev/null +++ b/app/examples/Misc/SystemTray/.src/FMain.class @@ -0,0 +1,64 @@ +' Gambas class file + +'Private $hImage As Image + +Public Sub _new() + + 'Me.Background = &H007FFF 'Color.SetAlpha(&H007FFF, 192) + +End + +Public Sub Form_Open() + + X11Systray.Show(dwgSystemTray.Handle) + +End + +Static Public Sub X11Systray_Arrange() + + FMain.ArrangeTray + +End + +Public Sub ArrangeTray() + + Dim I As Integer + Dim X, Y, H As Integer + + Debug + X = 2 + Y = 2 + For I = 0 To X11Systray.Count - 1 + With X11Systray[I] + Debug I;; .IconW;; .IconH + If (X + .IconW) >= (Me.ClientW - 2) Then + X = 2 + Y += H + 2 + H = 0 + Endif + .Move(X, Y, .IconW, .IconH) + H = Max(H, .IconH) + X += .IconW + 2 + End With + Next + +End + +' Public Sub DrawingArea1_Draw() +' +' Paint.DrawImage($hImage, 0, 0) +' +' End + +Public Sub Form_Resize() + + ' Dim DH As Integer + ' + ' Paint.Begin(draSystemTray) + ' DH = 1 + draSystemTray.H / $hImage.H + ' Paint.DrawImage($hImage, 0, - DH, draSystemTray.W, draSystemTray.H + DH * 2) + ' Paint.End + ArrangeTray + +End + diff --git a/app/examples/Misc/SystemTray/.src/FMain.form b/app/examples/Misc/SystemTray/.src/FMain.form new file mode 100644 index 00000000..160008fc --- /dev/null +++ b/app/examples/Misc/SystemTray/.src/FMain.form @@ -0,0 +1,14 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,48,27) + Text = ("System tray") + Arrangement = Arrange.Fill + Margin = True + { dwgSystemTray DrawingArea + MoveScaled(8,2,24,24) + Background = &HFFFF00& + Border = Border.Plain + Cached = True + } +} diff --git a/app/examples/Misc/SystemTray/bg.png b/app/examples/Misc/SystemTray/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..7aa2ddb11f4b1cbde5ba7af09c5fb5b1379f47ae GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^3JeU43>+*#R>g`ve}Pn%r;B4q#jUsJ4stRm@US?F za&J$zp0H<)g21O4zwaA;o;UHzCz2f|g6G~Y?TNylE L{an^LB{Ts5O#vu< literal 0 HcmV?d00001 diff --git a/app/examples/Misc/SystemTray/icon.png b/app/examples/Misc/SystemTray/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7b22ad890590a54e372bb70ab931baf3249b907b GIT binary patch literal 4363 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/examples/Multimedia/CDPlayer/.directory b/app/examples/Multimedia/CDPlayer/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Multimedia/CDPlayer/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Multimedia/CDPlayer/.icon.png b/app/examples/Multimedia/CDPlayer/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..66db8113c4362dc5ddc299203d0108488652af6a GIT binary patch literal 4311 zcmV;|5Ge17P)7=uhg=`Q+6fhAnARtOa+!%GnVbmENx6vu6f}$*SMwzLa zVak61VN_J216rc81r$RPlF$iB$lB>ONiXS6Z|}Z$mviQi?oPTpO-wo=n0%{F)qC&W zbMF0q?{7Khch3`CV5nI00@fHX7XR1*de+@_R^!RHo6wn{^7eHA6r@~cPfa7^WoJF6 ze!EGX4r)HO4r2sL2tIQ|HD6s*N8=HXm0#||DPKX*u?c|kTYd*ZW1@h}?jMuc_tOho zfZ9*~5o5s`xbYoj-1G4|A|2z=dZ;T(QIOQ+YF86d$1k|?ca<&qNa zyK^Roj|N=(jXg9R8~_=+umqU-xeZtg`6-vbzrKpUy}pwAF8IzX&^-uYbmH72ZAWR? zeUPd`!DIijh`BW>zV%2eYyUh5Dn?2q2vlP70^I#X_gz+qyS1{_r+NR`jL+7%ioS6F zZDsXmLVVU;8$n2sBdmFMIbXeX4(ktSRzCm(T@j&; z0S1IXSb;Jw)=Hep_Yxd_?#u;H(o{!=Gsg016l`u3&z?$zdH=Ktq=Pjue`=C%ez2NL zXQcSkA$V;E_@kb96v4hdo$TM;%A%=B9=!WftWNNSpTN#GFb4ACE(irS0uiLAUiY1GBFz8VcC4|K<~e-*=1Oi{UdU@*@W?L6 zghPZzaHzGPZS}1rWyE(sIh}XRE8@X*@YL3^=eda(Sb;!etwl#*jRoT%Vx@oae#JyT z{_EN@PG0!^I?#y!BL-iy?HRwuO9RUwlw);<^zrr7o`9} zWcx;cHzu+=h(so#)fmD;oY}w^3gox~U@kBjt)%pnl&Mplc(Fi@|1PDcl~Ng{)VKq0 z_pQD~7-!i^nT(c07BJ?36z47jm?)&_z$^4Ny}^>}K8J}yq0AnIHt$!3ddmgjqT}gXWJza-(bLqQ2{`T;Ko`i zw4W=A9U7;<10@vuw>-^^#n+K4C_jJv#aIE9in3ZN>8WwS!A??u?ax2Lk^NhUEQHHe zP>?Rf1U|{)QnClO^2X+7%4#oTq~b zloX!UN_t9-Jz~x<1Z4WU`0@YT$KBOcJbve=c=oYJshv5GFF*7oNBS~kO3FztUPajz z$>BSZsJ**zwLY(tuJP8UmGra}cxUqzqD&|3`4!Lq%~yDK=~6D~?Ill0mMoadk{i~r zr8`8~2;*mmlof(pR~vQtC4PgB2asP|_EViYA%HkTdc20S%bKb`B)Jl0; zN!zES5O5;-zgY}8v~LT2PC&-w*nPN_o9C2a%4&FQYa4g%Jk0+-{197z`!u5V5`MI~ znS;Gqq&N|LthM+@T3ArCfGIVz&)=h;*aC!-wofZ9JETB}QL`UAK@8YX|01>NK6>2} z2IHla2+5+E6>LA6#aMXoM{D`$2i{NonoqEGZwrUEw~#ZSlX*mg{RpMWwKr3h4Vk;_ zO4Qi~f4>ir>&BW85zEKM%XpJ2|AYcqi%?p2Xh+#jCFST0AOt6F63D_ zz8-91ZhjY)rGlcOB3vg$5aj6W*~4px1U>FdgmfnCJKEB%ML3vFEsbuM+DIuFDFO;J z>i#ypu$J4ep2@asjHA|Juw^It3$J9buZICYq&S(6RuUluRaack!@sx>f9sPR6q?eQ z;yYK~L96dm(Rr9vH+_E6#I3ay4b-!^I>2HO!eyX8L(BnhiU4vu$8H z!c9+P0mlU}7AZsBE^SP-D~V7_pp-yLA*39t2{a$5XWCTFD+k)xw7&%vOA_`SB^u}@ zbmFYv+e|X(C*e9ozDLKQ7XGtn77H7j`EX+corinqu$n1N`?&6=yBHaFGOftl$z^qr z@iO?ifY6T!a+bh1U<4+zn9%UfOJ}k;uMt5o!8qbLPbCpHCfWtS1J7pF-0n zw3b@mV-J#BZn}#pweu$ZVL&8nKiL{Sbelf}AjK_|w{;AlY&qI(y)&NhM` zm-O7_^dHzsx^xOJ?C&5yy9b>vqIdUeIEf_bX$y%JP2sbPuf~a;-bRH}p-gvBP>^C^ z0HQ$PWd=Fc-Ay_#A8d@l!4U9q!w5@2EUuVenqx~2Mh8YEg%}BT5f80f+S5WrTg*^zD5OYeLGaYe^0C(C@f(_e086OiTD^1qG#5TzdI? z&zkrMrWIMV*2LTdfiKBsdocv;A50MG6j4tPvI`0dlN1!hL0Kxw3K9Fe5Xlo;>7+!A z#CJPJ041^`$_Rr{QlK1{ba^#Sem(=6H<0r)2qB17)=}YgBekYIFhmEoVP-9*VESBa zDn=q4pi+hG-npIyi>^8+0wf(n%#D-thTi+fV`;hr7g4?f4K#G^qR!25Y#@t{I|vP_ zl#9*vqVrCyHr4QC2*3)mqXZx`Vkm+km0YQ?$tl##n#+uC=*{L3Qqum+<5XRCHR;Hb zEUl)&>mxp;nw;;`n)6Xo(4U{8EZN23hOJc3Tz1w1NbTafF=8?B96)GX5g=^@Qjv55 z9DR(?rpe^uNXH_zMQLYD6->z?>mIg%%y9v<#AB2K%0RjxpIy89*!S>%qw*{0X*~>~ zkCt%litG7P<8BsdIGijXv_n%(B`nrjT8?=XR@G9!cN?KlOrNn3sU~0e=*@~D0bEF> zB!xv~6ck`$v?gjpD_5$WL-p&H7UV&rqWppb=?bv*Xy&(PbRp{TSJBLu&GZY@!M z33B;$EMIj!9Bao~fwlH{LDkiYZLM86^WVu}R~ujY+CNj@u!*LI*BKfAbXzVpjU9Uq z;qFQvG&g#7$+j1h!kh%r84kfB>toHULU_Bb~!Mp=MXB4dtQfDohC z&C_PjXSJ5BZ);+f&hYZSgOpsk8sjEuXlOzt^5|{ePi%02)a=Dz?XVC6M@bYB^^NW9 zdi)Peuc#o?*+^gvm&{vo`Z2(bQ{S1Qw4j}m;wiYHU@#+a6G==I5(XBDCCY`ED5AAb z5UZYX(l|;AF;e}K!!bY($AIh!l|YSaQ)kR$^}=PiL4xXnLS#IF3497Vns|3Y@qzc= z%Ch`4xt$xa#vq34{z5tUJJxe)Wi=~Yh#fhE2o$?_ZN?w$JLx*7YED5F&+QA?wRaCO z7o3H%%BQT6*(wsxF^!3@J`nx0a)_fK<{OSqnxIF;Shau%w)8u3b&d z8(aBROABsU4SmAW(RrBnt@sbmvBc)Dpg%iEk(69DXFgSR zm(a9t2TH3mss~0aKX>QT+BdtYWsz9JouW35l`;2<5nK4D%Rsgi1p3D7EbQVnp7 z1qgA%1ssVtvOP~4)TtJ0)8{OtV9U#7%WAOcV)hBi8$Wu0Pu+Ms^A;|rdTJe8>R;jc zmw!#NZXvOf3i{GTykRW|8XK9rcRTM{efycjz~KOllc#8B!}`XurILfYfWNHZo2kP+s12PsbRdf0Vi+_LIs9{%McIP^nx88zTsO zgb?HxmT>JgH*(q1t7vF=gDu-O(A?RMZwyr>Wz=518ZpV&&q`^O(m1g=q$e|iIL=K< zn}OlH_4Hozm407*4ZzEV#bIPlZ>#kM2CZZ+5!2lOY1e-LhB@*94PdMp^a6#oR%Eh~ zGS+a>?T`^5g;iEaA%w6g#|*--3-osP90;94HEEf=E#LI@RMW678{z1_zS z4C{gbN`L&PmOr|A#WN#2vRUfl697O78~6j=2R`xcsn-CV#{~dZ19O2S;BxYYiVGbP z5CDU~9^m&Q0szE-YG5`{2^0YdF0jAVodSlTwATY12KEE3KyK6;5H(CX#*hU_{!*R! z!hszY!5?lO5Hi7g@Qa1;=?HS}2v7akGsFsuSv;$dUwmmMoxMKy{k)k!Y&}Y*bu(Rm zyq|Ee{Vikjfjhs?u6=!IHxKP5aTA5itAlH=U5-|QvZ4gHyldKoeb32mC9icoLak(H zv*vqGwy}177u}uhbZ>cxzMa2#%ZQI)TFq1%+dI&1A+fx2OmK{{B8w0ZclDc+hjVE1 zQ*L;KWW{u(R(Ne&Ir7{+m|Xu`PJRSe-SGrQ72vuF$nIm&44*00)41m!R-dxpIoq$+ z{`9BM8`+6aUYbWt=ZK;#Pdxk?*8F50rKM;8=jw|d{||W6nBl=`Z*%|v002ovPDHLk FV1i$pHsk;R literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/CDPlayer/.lang/ca.mo b/app/examples/Multimedia/CDPlayer/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..54ba17a7f57e21256863ca0740eb28de67ba1291 GIT binary patch literal 883 zcmZ9K&u$Yj5XKFZf6Jj1aX<*rfM74+u&IiOwgeHSDJr56wP^+7WOj#S!Le6fZ`3>p z;u&}jBu+>?01^k@1BuT`+5#hue>2|k=aFZBURwGnU>D%m;2ypL{{|0s@q`d(z!mT` zcxAyG;4<>t;5l#&ybiX(p!Eg}^+Pb6AA`3*4>sVR;NjfYlk@uTV5s{6UIl-Fp^x8S z=xh0u5SPJwV9@O>_!PW@d>e%1KemJn+q3%fV9{Pui-SvRpblrrZ@17D0jHfm7KBF<~8s%K@u# zN=G@u?XB(JaVO125_PTdY@k0a(SYXze;?0Nn03lV%2n*!dxK5cI<5, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: CDPlayer\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-04-07 05:18+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:2 +msgid "A Tiny CDplayer" +msgstr "Un petit reproductor de CD" + +#: .project:1 Fcdplayer.form:16 +msgid "CDplayer" +msgstr "Reproductor de CD" + +#: Fcdplayer.form:33 +msgid "&Eject" +msgstr "&Expulsa" + +#: Fcdplayer.class:22 +msgid "I Could not load cd-rom drive" +msgstr "No es pot carregar el cd-rom" + +#: Fcdplayer.form:28 +msgid "&Play" +msgstr "&Reprodueix" + +#: Fcdplayer.form:48 +msgid "Play &Track" +msgstr "Reprodueix la &pista" + +#: Fcdplayer.form:43 +msgid "&Stop" +msgstr "&Atura" + +#: Fcdplayer.form:22 +msgid "Track" +msgstr "Pista" + +#: Fcdplayer.form:57 +msgid "Volume" +msgstr "Volum" + +#: Fcdplayer.class:26 +msgid "Your PC doen not have cd-rom drive" +msgstr "El vostre PC no té unitat de cd-rom." + diff --git a/app/examples/Multimedia/CDPlayer/.lang/cs.mo b/app/examples/Multimedia/CDPlayer/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..8fdcc96640ce85dae2d2a0657cebf2058b0f2bfe GIT binary patch literal 798 zcmZ9Jzi-n(6vq!JzrsL^1Y!X6gisjJ6_6Mz)1q35+eX-NlwVR|I^rWSvF{@L9FaeQ z5dmWmV&gvm7SoNDm63^=neUa*AfEN^bMO3p=db*}Us!p`5a*CrkOsMi{ED=(dW^AC z;2L-myu9Ew@I2}p;2H29cop0N?Ol&Ss~>{4KLxLY24u)r$g|kKK7LrTw+p@lFXH_D zqW%b8Lj4(B1;2sz-XEat{|Vj%e}VSue`5v338d9qt@Q!fQB<(AUafx{r|nYpp2>k> z)m9>AtlBYp!d&XcYDTsC1ZOg1N1F!Kbdf})w4p?chz1eQbV^YcPbITAP_>(h!I=G7 z`#LF7$@X=TQL9Fgmg=x~B&PDe-b`B?ybRFM8+f$o1#ZLNcI)-F z*XcZfR1FI;l)NiN>X7d?cYF4eo4&uD#Opd6E;SBSf=1vi{oP%^Q>kfXq{8QB6ZkRm zxw(y>7OVR-7>O*GW~LpcUmFgr%U_7aiDM(2k0%lW~a XCfc?cG?S8PIsZF?RKl!^#|8ThPVK`Y literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/CDPlayer/.lang/cs.po b/app/examples/Multimedia/CDPlayer/.lang/cs.po new file mode 100644 index 00000000..32003b3d --- /dev/null +++ b/app/examples/Multimedia/CDPlayer/.lang/cs.po @@ -0,0 +1,52 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 Fcdplayer.form:16 +msgid "CDplayer" +msgstr "-" + +#: .project:2 +msgid "A Tiny CDplayer" +msgstr "Lehký CDpřehlávač" + +#: Fcdplayer.class:22 +msgid "I Could not load cd-rom drive" +msgstr "Nemohu načíst CD-ROM" + +#: Fcdplayer.class:26 +msgid "Your PC doen not have cd-rom drive" +msgstr "Váš počítač nemá cd-rom mechaniku" + +#: Fcdplayer.form:22 +msgid "Track" +msgstr "Stopa" + +#: Fcdplayer.form:28 +msgid "&Play" +msgstr "-" + +#: Fcdplayer.form:33 +msgid "&Eject" +msgstr "&Vysunout" + +#: Fcdplayer.form:43 +msgid "&Stop" +msgstr "-" + +#: Fcdplayer.form:48 +msgid "Play &Track" +msgstr "&Přehrát stopu" + +#: Fcdplayer.form:57 +msgid "Volume" +msgstr "Hlasitost" diff --git a/app/examples/Multimedia/CDPlayer/.lang/es.mo b/app/examples/Multimedia/CDPlayer/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..a9fa63aeac25c5e5909c402dc4d469b3b40a5356 GIT binary patch literal 627 zcmYk2&2H2%6om~8e?b)zVuMr(ud+cdAhD=IscKE!5|K=zOlBn5xfwHrrgrLhQ0GB- z3pR*H=zAd8v0?>#jyt0%S33FpbA9g3{r+I^nIP^$+t3W!g1$gLen7XNpU_R{*P6e< z`_Lcg4m7w%$USfi^m&g!|L!sP7YxD68rN6n;hGG5i2E0y_entSTdw!V;BD+rKv)o9 z6aM@LESa&XvVCK8L<>HA!;b`=igKLfqoBxHsXjISlEu=gh`trPWLeH)PG9Fy zmOOcvk3^8l-Z8lAzLn0{h#pSTl#X~N=$^=Uk{<9lF2#7fk5oNh$hl&blBW?(`RHik zOYWuVLEG#YJKqnoBokLnJ>Pj242^b5qsFpB6HcAGr})uEzohjCX?x{fO{zn-dzI&V zd9N%Jx;C>$&m+2f+_(UvJMR{4FD(fxQ+pKB0uxO;b+%9!Uk!%nhxY#vFH>nal)7#% MzG<>qtY)dnKjbWrAOHXW literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/CDPlayer/.lang/es.po b/app/examples/Multimedia/CDPlayer/.lang/es.po new file mode 100644 index 00000000..bfb15eaa --- /dev/null +++ b/app/examples/Multimedia/CDPlayer/.lang/es.po @@ -0,0 +1,44 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:2 +msgid "A Tiny CDplayer" +msgstr "Un pequeño CDPlayer" + +#: Fcdplayer.class:151 .project:1 +msgid "CDplayer" +msgstr "CDplayer" + +#: Fcdplayer.class:169 +msgid "&Eject" +msgstr "&Expulsar" + +#: Fcdplayer.class:164 +msgid "&Play" +msgstr "&Tocar" + +#: Fcdplayer.class:186 +msgid "Play &Track" +msgstr "Tocar &Canción" + +#: Fcdplayer.class:181 +msgid "&Stop" +msgstr "&Parar" + +#: Fcdplayer.class:157 +msgid "Track" +msgstr "Canción" + +#: Fcdplayer.class:196 +msgid "Volume" +msgstr "Volumen" + diff --git a/app/examples/Multimedia/CDPlayer/.project b/app/examples/Multimedia/CDPlayer/.project new file mode 100644 index 00000000..3bec5b6e --- /dev/null +++ b/app/examples/Multimedia/CDPlayer/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.0 +Title=CDplayer +Startup=Fcdplayer +Icon=cdrom.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.qt4 +Component=gb.qt4.ext +Component=gb.sdl.sound +Description="A Tiny CDplayer" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence diff --git a/app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class b/app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class new file mode 100644 index 00000000..55a4997a --- /dev/null +++ b/app/examples/Multimedia/CDPlayer/.src/Fcdplayer.class @@ -0,0 +1,139 @@ +' Gambas class file + +' Simple CDplayer +' Carlier Laurent - (c) 2005 +' Under GNU GPL V2 or Later +' +' Done for Testing the sdl component +' CDROM part + +Static mycd As CDRom +Static HaveCD As Boolean + +Public Sub Form_open() + If CDRoms.Count > 0 Then + Try mycd = New CDRom + If Not IsNull(mycd) Then + Volume.Value = Abs(mycd.Volume - Volume.MaxValue) + TrackPos.Value = 0 + Me.Center + Timer1.Enabled = True + Else + Message.Warning(("I Could not load cd-rom drive")) + Me.Close + Endif + Else + Message.Warning(("Your PC doen not have cd-rom drive")) + Me.Close + Endif + +End + +Public Sub SButton_Click() + + mycd.Stop() + PButton.Text = "&Play" + TrackPos.Value = 0 + +End + +Public Sub PTButton_Click() + + If Not HaveCD Then + Return + Endif + + mycd.Tracks[TrackInfo.Index + 1].Play() + +End + +Public Sub Timer1_Timer() + + Dim trckloop As Integer + Dim string1 As String + Dim string2 As String + + If Not mycd.Ready Then + TrackInfo.Clear() + TextBox1.Text = "No CD in Drive" + HaveCD = False + Return + Endif + + If mycd.Stopped Then + string1 = mycd.Tracks.Count & " Tracks" + string2 = "Total Length : " & ToTime(mycd.Length) + TextBox1.Text = string1 & Space$(41 - (Len(string1) + Len(string2))) & string2 + TrackPos.Value = 0 + Endif + + If mycd.Playing Or mycd.Paused Then + TrackPos.Value = (mycd.Tracks[mycd.Tracks.Current].Position) / mycd.Tracks[mycd.Tracks.Current].length + TextBox1.Text = "Playing Track " & mycd.Tracks.Current + Endif + + If HaveCD Then + Return + Endif + + TrackInfo.Clear() + For trckloop = 1 To mycd.Tracks.Count + string1 = "Track " & trckloop + string2 = "<" & ToTime(mycd.Tracks[trckloop].Length) & ">" + TrackInfo.Add(string1 & Space$(49 - (Len(string1) + Len(string2))) & string2) + Next + + Volume.Value = Abs(mycd.Volume - 255) + HaveCD = True + +End + +Public Function ToTime(length As Integer) As String + + Dim myString As String + + mystring = Format$(length \ 60, "00") & ":" & Format$((length Mod 60), "00") + Return myString + +End + +Public Sub Volume_Change() + + mycd.Volume = Abs(Volume.Value - Volume.MaxValue) + +End + +Public Sub PButton_Click() + + If Not HaveCD Then + PButton.Text = "&Play" + Return + Endif + + If mycd.Stopped Then + mycd.Play() + PButton.Text = "&Pause" + Return + Endif + + If mycd.Playing Then + mycd.Pause() + PButton.Text = "&Resume" + Return + Endif + + If mycd.Paused Then + mycd.Resume() + PButton.Text = "&Pause" + Endif + +End + +Public Sub EButton_Click() + + Try mycd.Eject + HaveCD = False + PButton.Text = "&Play" + TrackPos.Value = 0 + +End diff --git a/app/examples/Multimedia/CDPlayer/.src/Fcdplayer.form b/app/examples/Multimedia/CDPlayer/.src/Fcdplayer.form new file mode 100644 index 00000000..e52dc819 --- /dev/null +++ b/app/examples/Multimedia/CDPlayer/.src/Fcdplayer.form @@ -0,0 +1,47 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(69.7143,114.7143,50,26) + Text = ("CDplayer") + Icon = Picture["cd.png"] + Resizable = False + { TrackInfo ComboBox + MoveScaled(1,1,48,4) + ToolTip = ("Track") + ReadOnly = True + } + { PButton Button + MoveScaled(1,11,21,4) + Text = ("&Play") + } + { EButton Button + MoveScaled(1,16,21,4) + Text = ("&Eject") + } + { TextBox1 TextBox + MoveScaled(1,6,43,4) + ReadOnly = True + } + { SButton Button + MoveScaled(23,11,21,4) + Text = ("&Stop") + } + { PTButton Button + MoveScaled(23,16,21,4) + Text = ("Play &Track") + } + { Timer1 #Timer + #MoveScaled(36,1) + } + { Volume Slider + MoveScaled(46,6,3,17) + Mouse = Mouse.SizeS + ToolTip = ("Volume") + MaxValue = 255 + PageStep = 30 + Mark = True + } + { TrackPos ProgressBar + MoveScaled(1,21,43,4) + } +} diff --git a/app/examples/Multimedia/CDPlayer/cdrom.png b/app/examples/Multimedia/CDPlayer/cdrom.png new file mode 100644 index 0000000000000000000000000000000000000000..fa1dbc8b79bbe3c7bfd5cba80cca665c66f7266f GIT binary patch literal 3987 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 literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/.directory b/app/examples/Multimedia/MediaPlayer/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Multimedia/MediaPlayer/.hidden/screenshots/MediaPlayer.jpg b/app/examples/Multimedia/MediaPlayer/.hidden/screenshots/MediaPlayer.jpg new file mode 100644 index 0000000000000000000000000000000000000000..65929eb055a6e80d4aa6e5a7a5cdf909b56cfc1a GIT binary patch literal 144138 zcmeEu2T)Vp_U}odcL*9VKqvwUMM^-bgc1lHLN6*Ef+9_%h|&=Q(m{xbp@d#U0l^AN zM>;5?C<210sE7p-;T`n*?)~ol&zt{!ciww5@6E+I!*I?%d#|=xL#NluvQtH}jN?P*DIHlc5AS^5_>}>2v4i2P}C`wf6fB9#p1K?qX z?_r9CLBs$k4+O>o+35mA006=OQoBp=A3qQ%49>vF#LU9V1`cT92A~ia3<`%aFu>v9 z=riDR0M5g}E2^l=$Y<@&Bo>TNipwft7T0U)AuK>>V7PoISm~eSH1M{-GzsPK8HAMkORBC7(+z}`T{kHLabC)j&0Q=3>-#GgxzIZ^spl~=0&a}%H1R4%rFdjIAs3Ie;t~HZ;FrS!G z95X^MtE8!uMO>Nmk>4X^jCGF$?&bcqUDkea_OCG(|8H^jH^%PKovR&9%V~IIHlgghrsgyZ@M<-U4%i0Eyx%hsJJ>yA`rYb5N%M5dAJ^4+NAJH~hh zg!TU)7ytbuM|J>@jmOqgvjxr s7Cpu(={4bpR)!Rc!_wK(@%P5jZ8qy9XRJqk&g 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

    F;%X(yiUoBjvLC;R35KMLJn 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 literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/.lang/fr.mo b/app/examples/Multimedia/MediaPlayer/.lang/fr.mo new file mode 100644 index 0000000000000000000000000000000000000000..455dc986710cf5de67a8b518347b96098c8f7469 GIT binary patch literal 1032 zcmZ{iyKWOf6ov;#xEQ2}i&7wp;nE=301`!H5|mB6Ni244%XT7>ZsI+*2kp+7nb{~o zl)M5ZqBauL^llMPfM|FD5EUK&*h(CUaHQkUGjsO*XZN4)S7zQa#3ken8@C)d*et-=56Y2Z^Iz8$0 z2fTpwESjGM=Rn_o9u(j$@H+Sg^!?w1x4{qK9q=o74g3vy3EvBU*$mR_%p!e2X8az0 z79U>Ahwph9UYD(kOsEdARU>2O05QVJYQW@r{-bm9$7!(|k{+qf4Iy=hIEmy&7{P3L>G@UY~+ zIVs2;3^0$QefIz#R;nkw(-+2)Ti$A~2aD4>KUSAa5UGw%rRtUW;;wY1BR}IJmr7gd zM#CdRiM2^W=l`Y_3P#e{NO@xV2=lMaVbMX~ z!DwjYpznWQ7Bn&bxZ`Vm4NI)0R~*${+38E%9gCZ!3tv4NYIbrBCbhb-0q%q$%Ni#M F{RIwT8K(dM literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/.lang/fr.po b/app/examples/Multimedia/MediaPlayer/.lang/fr.po new file mode 100644 index 00000000..d930dc66 --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.lang/fr.po @@ -0,0 +1,64 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Media player based on GStreamer" +msgstr "" + +#: .project:2 +msgid "This example is a demonstration of how to use GStreamer with Gambas to play any sound or video file." +msgstr "" + +#: FControl.form:39 +msgid "MediaPlayer, a media player example based on GStreamer made by Benoît Minisini" +msgstr "MediaPlayer, un exemple de lecteur multimedia basé sur GStreamer programmé par Benoît Minisini" + +#: FMain.class:176 +msgid "Select a media file" +msgstr "Choisissez un fichier son ou vidéo" + +#: FMain.class:197 FTags.form:109 +msgid "Video device" +msgstr "Périphérique video" + +#: FMain.class:223 +msgid "Select a subtitle file" +msgstr "Choisissez un fichier de sous-titres" + +#: FTags.class:12 +msgid "Hue" +msgstr "Teinte" + +#: FTags.class:14 +msgid "Saturation" +msgstr "Saturation" + +#: FTags.class:16 +msgid "Contrast" +msgstr "Contraste" + +#: FTags.class:18 +msgid "Brightness" +msgstr "Luminosité" + +#: FTags.class:109 +msgid "No video device" +msgstr "Aucun périphérique video" + +#: FTags.form:45 +msgid "Information" +msgstr "Information" + +#: FTags.form:78 +msgid "Balance" +msgstr "Balance" diff --git a/app/examples/Multimedia/MediaPlayer/.project b/app/examples/Multimedia/MediaPlayer/.project new file mode 100644 index 00000000..5dc68c7b --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.project @@ -0,0 +1,55 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.8.90 +Title=Media player based on GStreamer +Startup=FMain +Icon=icon.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.desktop.x11 +Component=gb.desktop +Component=gb.form.dialog +Component=gb.settings +Component=gb.media +Description="Media player example.\n\nThis example is a demonstration of how to use GStreamer with Gambas to play any sound or video file." +Environment="GB_GUI=gb.qt4" +TabSize=2 +Translate=1 +Language=en +Maintainer=benoit +Vendor=Example +Address=benoit@black-tower +Url=www.endoftheinternet.com +License=General Public License +PackageName=mediaplayer-3.5.0 +PackageVersion=2 +CreateEachDirectory=1 +Packager=1 +Systems=slackware +Menus=archlinux:"Applications/Video" +Categories=archlinux:"Video" +Groups=archlinux:"video" +Menus=debian:"Applications/Video" +Categories=debian:"Video" +Groups=debian:"video" +Menus=fedora:"Audio Video/Video/Player" +Categories=fedora:"AudioVideo;Player;Video" +Groups=fedora:"Applications/Multimedia" +Menus=mageia:"Multimedia/Video" +Categories=mageia:"AudioVideo;Video" +Groups=mageia:"Video" +Menus=mandriva:"Multimedia/Video" +Categories=mandriva:"AudioVideo;Video" +Groups=mandriva:"Video" +Menus=slackware:"Audio Video/Video/Player" +Categories=slackware:"AudioVideo;Player;Video" +Groups=slackware:"Applications/Multimedia" +Menus=suse:"Audio Video/Video/Player" +Categories=suse:"AudioVideo;Player;Video" +Groups=suse:"Productivity/Multimedia/Video/Players" +Menus=ubuntu:"Applications/Video" +Categories=ubuntu:"Video" +Groups=ubuntu:"video" +Tags=Audio,AudioVideo,Video,Multimedia +CreateMenu=1 diff --git a/app/examples/Multimedia/MediaPlayer/.src/CAnimation.class b/app/examples/Multimedia/MediaPlayer/.src/CAnimation.class new file mode 100644 index 00000000..a756ab55 --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.src/CAnimation.class @@ -0,0 +1,101 @@ +' Gambas class file + +Static Private All As New CAnimation[] + +Event Stop + +Property Read Object As Object + +Private $hObject As Object +Private $sProperty As String +Private $fTarget As Float +Private $fTime As Float +Private $hTimer As Timer + +Static Public Sub Start(hObject As Control, sProperty As String, fTarget As Float, iTime As Integer, Optional hParent As Object) + + Dim hAnim As CAnimation + Dim aStop As New CAnimation[] + + For Each hAnim In All + If hAnim.Object = hObject Then aStop.Add(hAnim) + Next + + For Each hAnim In aStop + hAnim.Stop + Next + + hAnim = New CAnimation(hObject, sProperty, fTarget, iTime) + All.Add(hAnim) + + If hParent Then Object.Attach(hAnim, hParent, "Animation") + +End + +Static Public Sub Exit() + + While All.Count + All[0].Stop + Wend + +End + + + +Public Sub _new(hObject As Control, sProperty As String, fTarget As Float, iTime As Integer) + + 'Debug hObject.Tag;; sProperty;; fTarget + + $hObject = hObject + $sProperty = sProperty + $fTarget = fTarget + $fTime = Timer + iTime / 1000 + + $hTimer = New Timer As "Timer" + $hTimer.Delay = 50 + $hTimer.Start + +End + +Public Sub Timer_Timer() + + Dim fValue As Float + Dim iSign As Integer + Dim fDiff As Float + + fValue = Object.GetProperty($hObject, $sProperty) + + iSign = Sgn($fTarget - fValue) + fDiff = $fTime - Timer + fValue += ($fTarget - fValue) / (1000 * fDiff / $hTimer.Delay) + + If fDiff <= 0 Or If iSign = 0 Or If iSign > 0 And fValue >= $fTarget Or If iSign < 0 And fValue <= $fTarget Then + Object.SetProperty($hObject, $sProperty, $fTarget) + {Stop} + Else + Object.SetProperty($hObject, $sProperty, fValue) + Endif + +Catch + + {Stop} + +End + +Public Sub Stop() + + $hTimer.Stop + $hTimer = Null + $hObject = Null + + All.Remove(All.Find(Me)) + + Raise Stop + +End + +Private Function Object_Read() As Object + + Return $hObject + +End diff --git a/app/examples/Multimedia/MediaPlayer/.src/CButton.class b/app/examples/Multimedia/MediaPlayer/.src/CButton.class new file mode 100644 index 00000000..37c68705 --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.src/CButton.class @@ -0,0 +1,215 @@ +' Gambas class file + +Export + +Inherits DrawingArea + +Event Click + +Property Image As Image +Property Opacity As Float +Property Highlight As Boolean +Property Shortcut As String +Property Text As String +Property Font As Font + +Public Const MIN_OPACITY As Float = 0.2 +Public Const MAX_OPACITY As Float = 0.8 + +Private $hObs As Observer +Private $hImage As Image +Private $hDraw As Image +'Private $bInside As Boolean +'Private $hTimer As Timer +Private $fOpacity As Float = MIN_OPACITY +Private $bHighlight As Boolean +'Private $hTimer As Timer +Private $sShortcut As String +Private $sText As String +Private $hFont As Font + +Public Sub _new() + + $hObs = New Observer(Me) As "DrawingArea" + '$hTimer = New Timer As "Timer" + '$hTimer.Delay = 50 + Me.Mouse = Mouse.Pointing + '$hTimer = New Timer As "Timer" + +End + +Public Sub DrawingArea_Enter() + + If Not Me.Enabled Then Return + CAnimation.Start(Me, "Opacity", MAX_OPACITY, 250) + +End + +Public Sub DrawingArea_Leave() + + If Not Me.Enabled Then Return + CAnimation.Start(Me, "Opacity", MIN_OPACITY, 250) + +End + +Public Sub DrawingArea_MouseUp() + + If Not Me.Enabled Then Return + Raise Click + Stop Event + +End + +Public Sub DrawingArea_Draw() + + If $hDraw Then + Draw.Image($hDraw, 2, 2) + Endif + + If $bHighlight Then + Paint.Begin(Me) + Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 192)) + Paint.Rectangle(0, 0, Me.W, Me.H) + Paint.Fill + Paint.End + Endif + +End + +Private Function Image_Read() As Image + + Return $hImage + +End + +Private Sub Image_Write(Value As Image) + + $hImage = Value '.Stretch(Me.W - 4, Me.H - 4) + SetOpacity + +End + +Private Sub SetOpacity() + + Dim W, H As Integer + Dim hFont As Font + + If Not $hImage Then Return + + $hDraw = $hImage.Stretch(Me.W - 4, Me.H - 4) + Paint.Begin($hDraw) + + If $sShortcut Then + + Paint.Font = Font["Bold,+2"] + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 64)) + 'For X = -2 To 2 + ' For Y = -2 To 2 + ' Paint.DrawText($sShortcut, $hDraw.W - 16 + X, $hDraw.H - 16 + Y, 16, 16, Align.Center) + ' Next + 'Next + Paint.Arc($hDraw.W - 12, $hDraw.H - 12, 12) + Paint.Fill + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.Black, 64)) + Paint.DrawText($sShortcut, $hDraw.W - 24, $hDraw.H - 24, 24, 24, Align.Center) + + Endif + + If $sText Then + Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 64)) + If $hFont Then + hFont = $hFont + Else + hFont = Font["Bold,+1"] + Endif + Paint.Font = hFont + W = hFont.TextWidth($sText) + 4 + H = hFont.TextHeight($sText) + 2 + Paint.Rectangle(($hDraw.W - W) / 2, $hDraw.H - H, W, H) + Paint.Fill + Paint.Brush = Paint.Color(Color.SetAlpha(Color.Black, 64)) + Paint.DrawText($sText, ($hDraw.W - W) / 2, $hDraw.H - H, W, H, Align.Center) + Endif + + Paint.End + + $hDraw.Opacity($fOpacity) + Me.Refresh + +End + +Private Function Opacity_Read() As Float + + Return $fOpacity + +End + +Private Sub Opacity_Write(Value As Float) + + $fOpacity = Max(0, Min(1, Value)) + SetOpacity + +End + +Private Function Highlight_Read() As Boolean + + Return $bHighlight + +End + +Private Sub Highlight_Write(Value As Boolean) + + $bHighlight = Value + Me.Refresh + +End + + +Private Function Shortcut_Read() As String + + Return $sShortcut + +End + +Private Sub Shortcut_Write(Value As String) + + $sShortcut = Value + SetOpacity + Me.Refresh + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + SetOpacity + Me.Refresh + +End + +Private Function Font_Read() As Font + + Return $hFont + +End + +Private Sub Font_Write(Value As Font) + + $hFont = Value.Copy() + +End + +Public Sub DrawingArea_Arrange() + + SetOpacity + +End + diff --git a/app/examples/Multimedia/MediaPlayer/.src/FControl.class b/app/examples/Multimedia/MediaPlayer/.src/FControl.class new file mode 100644 index 00000000..2e0f4c0f --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.src/FControl.class @@ -0,0 +1,133 @@ +' Gambas class file + +Private sldTime As DrawingArea +Private lblTime As DrawingArea + +Private $sInfo As String +Private $iMinOpacity As Integer = 70 +Private $sTitle As String + +Public Sub SetMinOpacity(iOpacity As Integer) + + $iMinOpacity = iOpacity + If Me.Opacity < $iMinOpacity Then + CAnimation.Start(Me, "Opacity", $iMinOpacity, 250) + Endif + +End + + +Public Sub _new() + + panToolbar.H = 48 + Desktop.Scale * 2 + + FMain.CreateButtons(["eject", "video", "config", "play", "pause", "stop", "screenshot", "fullscreen", "subtitle", "visualisation", "-"], panToolbar) + + sldTime = New DrawingArea(panToolbar) As "sldTime" + sldTime.Resize(8, 8) + sldTime.Expand = True + sldTime.Tracking = True + sldTime.Mouse = Mouse.Pointing + + lblTime = New DrawingArea(panToolbar) As "lblTime" + lblTime.Resize(8, 8) + lblTime.Font = Font["Bold,+5"] + lblTime.W = lblTime.Font.TextWidth("99:99:99 / 99:99:99") + 16 + + FMain.CreateButtons(["-", "volume", "quit"], panToolbar) + + Me.H = 48 + Desktop.Scale * 6 + lblTitle.Font.Height + Me.Arrangement = Arrange.Vertical + panToolbar.Arrangement = Arrange.Horizontal + + FMain.GetButton("visualisation").Font = Font["-2"] + +End + +Public Sub lblTime_Draw() + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 128)) + Paint.Font = lblTime.Font + Paint.DrawText($sInfo, 0, 0, Paint.W, Paint.H, Align.Center) + +End + +Public Sub sldTime_Draw() + + Dim fLength As Float = FMain.GetLength() + If fLength = 0 Then Return + + Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 192)) + Paint.Rectangle(0, (Paint.H - 16) / 2, Paint.W, 16) + Paint.Fill + Paint.Brush = Paint.Color(Color.SetAlpha(Color.White, 128)) + Paint.Rectangle(0, (Paint.H - 16) / 2, Paint.W * FMain.GetPos() / fLength, 16) + Paint.Fill + +End + +Public Sub sldTime_MouseDown() + + If Mouse.Left Then FMain.SetPos(Mouse.X / sldTime.W) + +End + +Public Sub sldTime_MouseMove() + + If Mouse.Left Then FMain.SetPos(Mouse.X / sldTime.W) + +End + +Public Sub SetInfo(sInfo As String) + + $sInfo = sInfo + lblTime.Refresh + sldTime.Refresh + +End + +Public Sub Form_KeyPress() + + FMain.Form_KeyPress + +End + +Public Sub SetTitle(sTitle As String) + + $sTitle = sTitle + lblTitle.Text = sTitle + lblTitle.Foreground = &HBFBFBF& + +End + +Public Sub SetError(sError As String) + + If lblTitle.Foreground = Color.Red Then + sError = Trim(lblTitle.Text & " " & sError) + Endif + + lblTitle.Text = sError + lblTitle.Foreground = Color.Red + timError.Start + +End + +Public Sub timError_Timer() + + SetTitle($sTitle) + timError.Stop + +End + + +' Public Sub Form_Move() +' +' Debug Me.X;; Me.Y;; Me.W;; Me.H;; "->";; Me.Y + Me.H +' +' End +' +' Public Sub Form_Resize() +' +' Debug Me.X;; Me.Y;; Me.W;; Me.H;; "->";; Me.Y + Me.H +' +' End diff --git a/app/examples/Multimedia/MediaPlayer/.src/FControl.form b/app/examples/Multimedia/MediaPlayer/.src/FControl.form new file mode 100644 index 00000000..b32b80ee --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.src/FControl.form @@ -0,0 +1,37 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,64,23) + Background = &H000000& + Resizable = False + Border = False + Utility = True + Opacity = 70 + Spacing = True + { Panel3 Panel + MoveScaled(25,0,12,1) + Background = &H3F3F3F& + } + { Panel1 HBox + MoveScaled(1,3,61,6) + AutoResize = True + { Panel2 Panel + MoveScaled(2,0,2,4) + } + { lblTitle Label + MoveScaled(13,1,43,2) + Font = Font["Bold,+5"] + Foreground = &HBFBFBF& + Expand = True + Text = ("MediaPlayer, a media player example based on GStreamer made by Benoît Minisini") + } + } + { panToolbar Panel + MoveScaled(1,10,57,9) + Margin = True + } + { timError #Timer + #MoveScaled(54,13) + Delay = 5000 + } +} diff --git a/app/examples/Multimedia/MediaPlayer/.src/FMain.class b/app/examples/Multimedia/MediaPlayer/.src/FMain.class new file mode 100644 index 00000000..581ca152 --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.src/FMain.class @@ -0,0 +1,616 @@ +' Gambas class file + +'Property TagsX As Integer + +Private $hPlayer As MediaPlayer +Private $hImage As MediaControl +Private $hFilter As MediaFilter +Private $hOutput As MediaContainer +Private $aVisualisation As MediaControl[] + +Private $fPos As Float +Private $fLength As Float +Private $iVisualisation As Integer +Private $fVolume As Float +Private $bSuspend As Boolean +Private $bShowTags As Boolean +'Private $iTagsX As Integer + +Public Sub _new() + + Application.MainWindow = Me + '$iTagsX = - FTags.W + +End + +Private Sub AddVisualisation(sType As String, sTitle As String) + + Dim hVisualisation As MediaControl + + If sType Then + hVisualisation = New MediaControl($hPlayer, sType) + hVisualisation.Tag = sTitle + Endif + + $aVisualisation.Add(hVisualisation) + +Catch + + Error sType; ": "; Error.Text + +End + +Private Sub MakeMediaPlayer() + + $hPlayer = New MediaPlayer As "MediaPlayer" + + '$hOutput = New MediaContainer($hPlayer) + '$hOutput.Name = "MyOutput" + + $hFilter = New MediaFilter($hPlayer) + $hImage = New MediaControl($hPlayer, "ximagesink") + '$hFilter.LinkTo($hImage) + + '$hOutput.AddInput($hImage) + + $hPlayer.Video.Output = $hImage + + $aVisualisation = New MediaControl[] + AddVisualisation("", "") + AddVisualisation("goom", "Goom") + AddVisualisation("goom2k1", "Goom2") + AddVisualisation("libvisual_bumpscope", "Bump") + AddVisualisation("libvisual_corona", "Corona") + AddVisualisation("libvisual_infinite", "Infinite") + AddVisualisation("libvisual_jakdaw", "Jakdaw") + AddVisualisation("libvisual_jess", "Jess") + AddVisualisation("monoscope", "Mono") + AddVisualisation("libvisual_oinksie", "Oinksie") + AddVisualisation("libvisual_lv_analyzer", "Analyzer") + AddVisualisation("libvisual_lv_scope", "Scope") + AddVisualisation("spacescope", "Space") + AddVisualisation("spectrascope", "Spectra") + AddVisualisation("synaescope", "Synae") + AddVisualisation("wavescope", "Wave") + + $iVisualisation = 0 + UpdateVisualisation + +End + +Public Sub GetButton(sKey As String) As CButton + + Return FControl.Controls["#" & sKey] + +End + + +Public Sub CreateButtons(aButton As String[], hParent As Container) + + Dim sKey As String + Dim sImg As String + Dim hPanel As Panel + Dim hButton As CButton + Dim iPos As Integer + + For Each sKey In aButton + + If sKey = "<->" Then + hPanel = New Panel(hParent) + hPanel.Expand = True + hPanel.Resize(8, 48) + Else If sKey = "-" Then + hPanel = New Panel(hParent) + hPanel.Resize(8, 48) + Else + + sImg = sKey + iPos = InStr(sImg, "#") + If iPos Then sImg = Left(sImg, iPos - 1) + + hButton = New CButton(hParent) As "Button" + hButton.Resize(48, 48) + hButton.Image = Image.Load(sImg & ".png") + hButton.Tag = sKey + hButton.Name = "#" & sKey + 'If cTooltip Then hButton.Tooltip = cTooltip[sImg] + 'If $cShortcut Then hButton.Shortcut = $cShortcut[sImg] + '$cButton[hButton.Tag] = hButton + Endif + Next + +End + + +Public Sub Form_Open() + + MakeMediaPlayer + + FControl.Show + FControl.Raise + + RefreshVolume + +End + +Public Sub Form_KeyPress() + + If Key.Code = Key.Escape Then Action("quit") + +End + +Private Sub GetDevice(sName As String) As String + + Try Return Scan(sName, "* (*)")[1] + +End + +' Private Sub FindChildFromType(hCont As MediaContainer, sType As String) As MediaControl +' +' Dim hCtrl As MediaControl +' +' For I = 0 To hCont.Children.Count - 1 +' hCtrl = hCont.Children[I] +' If hCtrl.Type = sType Then Return hCtrl +' Next +' +' End + +Private Sub Dump(hCont As MediaContainer, Optional iIndent As Integer) + + Dim I As Integer + Dim hCtrl As MediaControl + Dim sOutput As String + + If iIndent = 0 Then + Print "Source: "; $hPlayer.Input.Name + Print "[-------------------------------------------" + Endif + For I = 0 To hCont.Children.Count - 1 + hCtrl = hCont.Children[I] + Print Space$(iIndent * 2); hCtrl;; hCtrl.Name;; "["; hCtrl.Type; "]";; "=>";; hCtrl.Parent.Name + + For Each sOutput In hCtrl.Inputs + With hCtrl.GetLink(sOutput) + If Not .Peer Then Continue + Print Space$(iIndent * 2); "| "; sOutput; " <--- "; .Peer.Name; "."; .Output + End With + Next + + For Each sOutput In hCtrl.Outputs + With hCtrl.GetLink(sOutput) + If Not .Peer Then Continue + Print Space$(iIndent * 2); "| "; sOutput; " ---> "; .Peer.Name; "."; .Input + End With + Next + + If hCtrl Is MediaContainer Then + Dump(hCtrl, iIndent + 1) + Endif + Next + If iIndent = 0 Then Print "-------------------------------------------]" + +End + +Private Sub Action(sAction As String) + + Dim fPos As Float + Dim iState As Integer + Dim sName As String + Dim iName As Integer + Dim sVideo As String + + Select sAction + + Case "eject" + Dialog.Title = ("Select a media file") + If Not Dialog.OpenFile() Then + FControl.SetTitle(File.Name(Dialog.Path)) + Action("stop") + $hPlayer.URL = Media.URL(Dialog.Path) + $hPlayer.Subtitles.Enabled = False + If Exist(File.SetExt(Dialog.Path, "srt")) Then + $hPlayer.Subtitles.URL = Media.URL(File.SetExt(Dialog.Path, "srt")) + $hPlayer.Subtitles.Enabled = True + Endif + UpdateSubtitle + FTags.Clear($hPlayer) + + Action("play") + + Endif + + Case "video" + + sVideo = FTags.GetVideoDevice() + If sVideo Then + FControl.SetTitle(("Video device") & " " & sVideo) + Action("stop") + $hPlayer.Subtitles.Enabled = False + $hPlayer.URL = "v4l2://" & GetDevice(sVideo) + UpdateSubtitle + FTags.Clear($hPlayer) + Action("play") + Endif + + Case "info", "config" + If $bShowTags Then + CAnimation.Start(FTags, "Opacity", 0, 250, Me) + 'CAnimation.Start(Me, "TagsX", - FTags.W, 250) + Else + FTags.X = - FTags.W + FTags.Show + CAnimation.Start(FTags, "Opacity", 70, 250) + 'CAnimation.Start(Me, "TagsX", 0, 250) + Endif + $bShowTags = Not $bShowTags + + Case "subtitle" + 'If $hPlayer.State = Media.Playing Or If $hPlayer.State = Media.Paused Then + ' $hPlayer.Subtitles.Enabled = Not $hPlayer.Subtitles.Enabled + ' UpdateSubtitle + 'Else + Dialog.Title = ("Select a subtitle file") + If Not Dialog.OpenFile() Then + $hPlayer.Subtitles.URL = Media.URL(Dialog.Path) + $hPlayer.Subtitles.Enabled = True + UpdateSubtitle + Endif + 'Endif + + Case "play" + SuspendScreenSaver + $hImage.SetWindow(dwgVideo) ', panLeft.X + panLeft.W + 8, panLeft.Y + 8, Me.W - panLeft.X - panLeft.W - 16, Me.H - panLeft.Y - 16) + EnableVideoFilter + Sleep 0.1 + Try $hPlayer.Play + If Not Error Then + dwgVideo.Mouse = Mouse.Blank + timTime.Start + Dump($hPlayer) + Else + ResumeScreenSaver + Try $hPlayer.Stop + $fLength = 0 + Endif + + Case "stop" + ResumeScreenSaver + Try $hPlayer.Stop + $fLength = 0 + timTime.Stop + dwgVideo.Mouse = Mouse.Default + + Case "pause" + ResumeScreenSaver + $hPlayer.Pause + dwgVideo.Mouse = Mouse.Default + timTime.Stop + + Case "fullscreen" + Me.FullScreen = Not Me.FullScreen + Me.Maximized = Not Me.FullScreen + Form_Resize + + Case "volume" + $hPlayer.Audio.Mute = Not $hPlayer.Audio.Mute + RefreshVolume + + 'Case "balance" + 'FBalance.Visible = Not FBalance.Visible + + Case "visualisation" + + iState = $hPlayer.State + If iState <> Media.Null And If iState <> Media.Ready Then + fPos = $hPlayer.Position + $hPlayer.Stop + $hPlayer.Close + timTime.Stop + 'FadeOut + Endif + + Inc $iVisualisation + If $iVisualisation >= $aVisualisation.Count Then $iVisualisation = 0 + UpdateVisualisation + + If iState <> Media.Null And If iState <> Media.Ready Then + $hPlayer.Pause + $hPlayer.Position = fPos + $hPlayer.State = iState + If iState = Media.Playing Then timTime.Start + 'FadeIn + Endif + + Case "quit" + Me.Close + + Case "screenshot" + + Do + Inc iName + sName = "~/MediaPlayerScreenshot" + If iName > 1 Then sName &= "-" & CStr(iName) + sName &= ".jpg" + If Not Exist(sName) Then Break + Loop + Try $hPlayer.Video.Image.Save(sName) + If Error Then FControl.SetError(Error.Text) + + Case "seek-forward" + FControl.Y = Screen.H - FControl.H + + Case "seek-backward" + FControl.Y = 600 + + End Select + +End + +Public Sub Button_Click() + + Action(Last.Tag) + +End + +' Public Sub Form_Arrange() +' +' panToolbar.Move(0, Me.H - panToolbar.H, Me.W, panToolbar.H) +' +' End + +Public Sub timTime_Timer() + + $fPos = $hPlayer.Position + If $fLength = 0 Then $fLength = $hPlayer.Duration + FControl.SetInfo(Format(CDate(($fPos + 0.5) / 86400), "hh:nn:ss") & " / " & Format(CDate($fLength / 86400), "hh:nn:ss")) + +End + +Public Sub GetLength() As Float + + Return $fLength + +End + +Public Sub GetPos() As Float + + Return $fPos + +End + +Public Sub SetPos(fPos As Float) + + If $hPlayer.State = Media.Paused Or If $hPlayer.State = Media.Playing Then + If $fLength Then + $fPos = fPos * $fLength + '$hPlayer.Pause + 'FadeOut + $hPlayer.Position = $fPos + '$hPlayer.Play + 'FadeIn + Endif + Endif + +End + +Public Sub Form_Resize() + + 'Debug Me.X;; Me.Y;; Me.W;; Me.H;; FControl.H;; Me.Y + Me.H - FControl.H + 'Debug "FControl.Move:";; Me.X;; Me.Y + Me.H - FControl.H;; Me.W;; FControl.H + FControl.Move(dwgVideo.ScreenX, dwgVideo.ScreenY + dwgVideo.H - FControl.H, dwgVideo.W, FControl.H) + FTags.Move(dwgVideo.ScreenX, dwgVideo.ScreenY, FTags.W, dwgVideo.H - FControl.H) + +End + +Public Sub Form_Close() + + ResumeScreenSaver + CAnimation.Exit + +End + +Public Sub Button_MouseWheel() + + $hPlayer.Audio.Mute = False + + If Mouse.Delta > 0 Then + $hPlayer.Audio.Volume = Min(1, (Sqr($hPlayer.Audio.Volume) + 0.05) ^ 2) + Else + $hPlayer.Audio.Volume = Max(0, (Sqr($hPlayer.Audio.Volume) - 0.05) ^ 2) + Endif + + RefreshVolume + +End + +Private Sub RefreshVolume() + + Dim sImage As String + Dim fVolume As Float + + With GetButton("volume") + + If $hPlayer.Audio.Mute Then + sImage = "mute" + .Text = "" + Else + fVolume = Sqr($hPlayer.Audio.Volume) + .Text = Format(fVolume, "0%") + sImage = "volume-" & Min(3, CInt(fVolume * 4)) + Endif + + .Image = Image.Load(sImage & ".png") + + End With + +End + +Private Sub UpdateSubtitle() + + GetButton("subtitle").Text = If($hPlayer.Subtitles.Enabled, "ON", "") + +End + +Private Sub UpdateVisualisation() + + Dim hVis As MediaControl = $aVisualisation[$iVisualisation] + + $hPlayer.Video.Visualisation = hVis + If hVis Then + GetButton("visualisation").Text = hVis.Tag + $hImage.SetWindow(dwgVideo) + $hPlayer.Position = $hPlayer.Position + Else + GetButton("visualisation").Text = "" + $hImage.SetWindow(Null) + Endif + +End + +Public Sub dwgVideo_Draw() + + If $hPlayer.State = Media.Null Then + Draw.Font = Font["+16"] + Draw.Foreground = Color.Gray + Draw.RichText("Gambas Almost Means Basic!", 0, 0, dwgVideo.Width, dwgVideo.Height, Align.Center) + Endif + +End + +Public Sub MediaPlayer_End() + + Action("stop") + +End + +Public Sub MediaPlayer_Message((Source) As MediaControl, Type As Integer, Message As String) + + Select Case Type + Case Media.Info + Print "(i)"; + Case Media.Warning + Print "/!\\"; + Case Media.Error + Print "[*]"; + End Select + Print " "; Message + + If Type = Media.Error Then + FControl.SetError(Message) + Action("stop") + Endif + +End + + +Public Sub MediaPlayer_Tag(TagList As MediaTagList) + + Dim sTag As String + Dim vTag As Variant + Dim I As Integer + + For Each sTag In TagList.Tags + vTag = TagList[sTag] + If TypeOf(vTag) = gb.Object And If vTag Is Array Then + For I = 0 To vTag.Max + FTags.AddTag(sTag & "[" & CStr(I) & "]", Str(vTag[I])) + Next + Else + FTags.AddTag(sTag, Str(vTag)) + Endif + Next + +End + + +Private Sub FadeOut() + + $fVolume = $hPlayer.Audio.Volume + + Do + Debug $hPlayer.Audio.Volume + $hPlayer.Audio.Volume = Max(0, $hPlayer.Audio.Volume - 0.05) + If $hPlayer.Audio.Volume = 0 Then Break + Sleep 0.01 + Loop + +End + +Private Sub FadeIn() + + Do + Debug $hPlayer.Audio.Volume + $hPlayer.Audio.Volume = Min($fVolume, $hPlayer.Audio.Volume + 0.05) + If $hPlayer.Audio.Volume >= $fVolume Then Break + Sleep 0.01 + Loop + +End + +Private Sub SuspendScreenSaver() + + If $bSuspend Then Return + Desktop.ScreenSaver.Suspend(Me) + +End + +Private Sub ResumeScreenSaver() + + If Not $bSuspend Then Return + Desktop.ScreenSaver.Resume(Me) + +End + +Public Sub Form_Enter() + + If $hPlayer.State = Media.Playing Then + CAnimation.Start(FControl, "Opacity", 0, 400) + CAnimation.Start(FTags, "Opacity", 0, 400) + Endif + +End + +Public Sub Form_Leave() + + If $hPlayer.State = Media.Playing Then + CAnimation.Start(FControl, "Opacity", 70, 250) + If $bShowTags Then CAnimation.Start(FTags, "Opacity", 70, 250) + Endif + +End + +Public Sub Animation_Stop() + + FTags.Hide + +End + +' Private Function TagsX_Read() As Integer +' +' Return $iTagsX +' +' End +' +' Private Sub TagsX_Write(Value As Integer) +' +' $iTagsX = Value +' FTags.X = Me.X + $iTagsX +' +' End + +Public Sub SetBalance(iIndex As Integer, iValue As Integer) + + $hPlayer.Balance[iIndex].Value = iValue + +End + +Private Sub EnableVideoFilter() + + 'If $hPlayer.URL Begins "v4l2://" Then + ' $hFilter.Filter = "video/x-raw,width=640,height=480,framerate=30/1" + 'Else + ' $hFilter.Filter = "video/x-raw" + 'Endif + +End diff --git a/app/examples/Multimedia/MediaPlayer/.src/FMain.form b/app/examples/Multimedia/MediaPlayer/.src/FMain.form new file mode 100644 index 00000000..673e70e3 --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.src/FMain.form @@ -0,0 +1,18 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,110,85) + Icon = Picture["icon.png"] + Border = False + FullScreen = True + Arrangement = Arrange.Vertical + { dwgVideo DrawingArea + MoveScaled(12,27,24,24) + Background = &H000000& + Expand = True + } + { timTime #Timer + #MoveScaled(85,47) + Delay = 200 + } +} diff --git a/app/examples/Multimedia/MediaPlayer/.src/FTags.class b/app/examples/Multimedia/MediaPlayer/.src/FTags.class new file mode 100644 index 00000000..130275cc --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.src/FTags.class @@ -0,0 +1,147 @@ +' Gambas class file + +Private $cTags As New Collection +Private $aTags As New String[] + +Private btnResetBalance As CButton + +Private Sub GetBalanceName(sName As String) As String + + Select Case LCase(sName) + Case "hue" + Return ("Hue") + Case "saturation" + Return ("Saturation") + Case "contrast" + Return ("Contrast") + Case "brightness" + Return ("Brightness") + Case Else + Return sName + End Select + +End + +Public Sub Clear(hPlayer As MediaPlayer) + + Dim I As Integer + Dim hLabel As Label + Dim hSlider As Slider + Dim H As Integer + + $cTags.Clear + $aTags.Clear + gvwTags.Rows.Count = 0 + + panBalance.Children.Clear + + For I = 0 To hPlayer.Balance.Count - 1 + + With hPlayer.Balance[I] + + hLabel = New Label(panBalance) + hLabel.Text = GetBalanceName(.Name) + hLabel.Foreground = lblBalance.Foreground + hLabel.ResizeScaled(16, 4) + + hSlider = New Slider(panBalance) As "sldBalance" + hSlider.ResizeScaled(4, 4) + hSlider.Expand = True + hSlider.MinValue = .Min + hSlider.MaxValue = .Max + hSlider.Value = .Value + hSlider.Tag = I + + H += Desktop.Scale * 5 + + End With + + Next + + panBalance.Parent.H = H + lblBalance.H + Desktop.Scale * 4 + panBalance.Parent.Show + +End + +Public Sub AddTag(sTag As String, sValue As String) + + $cTags[sTag] = sValue + + If Not $aTags.Exist(sTag) Then + $aTags.Add(sTag) + $aTags.Sort + gvwTags.Rows.Count = $aTags.Count + gvwTags.Columns[0].Width = -1 + Endif + + gvwTags.Refresh + +End + + +Public Sub gvwTags_Data(Row As Integer, Column As Integer) + + If Column = 0 Then + gvwTags.Data.Text = $aTags[Row] + Else + gvwTags.Data.Text = $cTags[$aTags[Row]] + gvwTags.Data.WordWrap = True + Endif + +End + +Public Sub _new() + + Dim sFile As String + + gvwTags.Columns.Count = 2 + + btnResetBalance = New CButton(panLabel) As "btnResetBalance" + btnResetBalance.Resize(panLabel.H, panLabel.H) + btnResetBalance.Image = Image.Load("undo.png") + + cmbVideoDevice.Clear + If Exist("/sys/class/video4linux") Then + For Each sFile In Dir("/sys/class/video4linux") + cmbVideoDevice.Add(Trim(File.Load("/sys/class/video4linux" &/ sFile &/ "name")) & " (/dev/" & sFile & ")") + Next + Else + cmbVideoDevice.Add(("No video device")) + cmbVideoDevice.Enabled = False + Endif + +End + +Public Sub Form_Resize() + + panTags.Move(0, 0, Me.W - Desktop.Scale, Me.H) + +End + +Public Sub sldBalance_Change() + + FMain.SetBalance(Last.Tag, Last.Value) + +End + + +Public Sub btnResetBalance_Click() + + Dim hCtrl As Control + Dim hSlider As Slider + + For Each hCtrl In panBalance.Children + If hCtrl Is Slider Then + hSlider = hCtrl + CAnimation.Start(hSlider, "Value", 0, 250) + Endif + Next + +End + +Public Sub GetVideoDevice() As String + + If cmbVideoDevice.Enabled Then Return cmbVideoDevice.Text + +End + diff --git a/app/examples/Multimedia/MediaPlayer/.src/FTags.form b/app/examples/Multimedia/MediaPlayer/.src/FTags.form new file mode 100644 index 00000000..4c47b166 --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.src/FTags.form @@ -0,0 +1,91 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,82,82) + Background = &H3F3F3F& + Resizable = False + Border = False + Utility = True + SkipTaskbar = True + Opacity = 0 + Spacing = True + Margin = True + { panTags Panel + MoveScaled(2,3,75,78) + Background = &H000000& + Arrangement = Arrange.Vertical + { Panel2 VBox + MoveScaled(1,1,59,29) + Expand = True + Spacing = True + Margin = True + { Label1 Label + MoveScaled(0,0,47,5) + Font = Font["Bold,+5"] + Foreground = &HBFBFBF& + Text = ("Information") + } + { gvwTags GridView + MoveScaled(2,7,56,21) + Font = Font["+2"] + Background = &H000000& + Foreground = &HBFBFBF& + NoTabFocus = True + Expand = True + Border = False + Grid = False + } + } + { Panel1 Panel + MoveScaled(2,31,18,1) + Background = &H3F3F3F& + } + { VBox1 VBox + MoveScaled(3,33,59,7) + Spacing = True + Margin = True + { panLabel HBox + MoveScaled(0,0,57,5) + { lblBalance Label + MoveScaled(1,0,47,5) + Font = Font["Bold,+5"] + Foreground = &HBFBFBF& + Expand = True + Text = ("Balance") + } + } + { panBalance HPanel + MoveScaled(0,5,57,4) + Font = Font["+2"] + Expand = True + Spacing = True + Indent = True + } + } + { panVideo VBox + MoveScaled(3,59,68,8) + { Panel3 Panel + MoveScaled(0,0,18,1) + Background = &H3F3F3F& + } + { VBox3 HBox + MoveScaled(1,1,66,7) + Spacing = True + Margin = True + { lblBalance2 Label + MoveScaled(0,0,24,5) + Font = Font["Bold,+5"] + Foreground = &HBFBFBF& + AutoResize = True + Text = ("Video device") + } + { cmbVideoDevice ComboBox + MoveScaled(35,0,24,5) + Font = Font["+2"] + Expand = True + ReadOnly = True + } + } + } + } +} diff --git a/app/examples/Multimedia/MediaPlayer/.src/MTest.module b/app/examples/Multimedia/MediaPlayer/.src/MTest.module new file mode 100644 index 00000000..a0ce9bef --- /dev/null +++ b/app/examples/Multimedia/MediaPlayer/.src/MTest.module @@ -0,0 +1,67 @@ +' Gambas module file + +Private $hPlayer As MediaPipeline +Private $bStop As Boolean + +Public Sub Main() + + Dim hSrc As MediaControl + Dim hFilter As MediaFilter + Dim hSink As MediaControl + Dim I As Integer + + $hPlayer = New MediaPipeline As "MediaPlayer" + + hSrc = New MediaControl($hPlayer, "v4l2src") + 'hSrc["norm"] = "SECAM" + Print hSrc["norm"] + + hFilter = New MediaFilter($hPlayer) + + hSink = New MediaControl($hPlayer, "xvimagesink") + + hSrc.LinkTo(hFilter) + hFilter.LinkTo(hSink) + + hFilter.Filter = "video/x-raw,width=640,height=480,framerate=30/1" + + $hPlayer.Play + + Print "I will take a screenshot in 5 seconds..." + Repeat + Wait 1 + Inc I + If I <= 5 Then + Print "..."; I; + Flush + If I = 5 Then + Print + hSink.GetLastImage().Save("~/screenshot.png") + Print "Screenshot saved to '~/screenshot.png'!" + Endif + Endif + Until $bStop + +End + +Public Sub MediaPlayer_Message((Source) As MediaControl, Type As Integer, Message As String) + + Select Case Type + Case Media.Info + Print "(i)"; + Case Media.Warning + Print "/!\\"; + Case Media.Error + Print "[*]"; + $bStop = True + End Select + Print " "; Message + +End + +Public Sub MediaPlayer_State() + + Debug Last.State + +End + diff --git a/app/examples/Multimedia/MediaPlayer/brightness.png b/app/examples/Multimedia/MediaPlayer/brightness.png new file mode 100644 index 0000000000000000000000000000000000000000..f3621e244a2516609358815cefb1aed6d0f8d7af GIT binary patch literal 1534 zcmVTp$#z;vE7PZjKt1i`lc|6Y z^R9+B&T>|8LJ%0?6Azz2oxm$22$suzP#>?ofDKG43Xz8ZZle|F1NZd&5V{eXOt)br z#sMUv1xqxA2yz0$x^@uxSb>?Cz$N?~xbL-%i7ex7sZ|kv$08;I0N)ka+lkm$Ecd}X z0Kg<}WdkEpejh(zf*1G%{%ASDx_Oujc~SfAWr|K9$bW=hJM&o1>y|doc%Z=j^dv`g zco3}gR*5WigHL9!7dxZOHW0|>s5b(AOg18_U9uT-wB${+;I0kPAJA|3@5gBy%~7=A zmiW!a^BxD_7PANjS;((>$JCHDhW3NtDw9r>UvV>o1ZT*Rd!I9bk2Sqk$#@n^_;)P7 zhn0NN1P_9kW_Y25OAIa+@N$zwe zP#`q7xynWKJeu#ZL}Y*JBg%@HocNgC3kkvxDwyA{oJ-a27Ud zRPQOSrhPfAW0z^lb+VT8E%oPeOsJiLo5=n3p2^q@);M_l2&qkg(t}Y78txjl7k-L0WJ`Gj4IvG z8V-AcC&?kM*6ZyN*%J)AgNL|}mqeXyF*7rH*)u#zcJR?y&BYv-v-k25d+<4|Gpyje zriY!Aalw;hz1|0n#)1fInQaEoVvRZEm&Hshw1tAD71ZflJwHW#a#Z;yOK&T882lE|Eru={#m$ zjI`arJVxV&AJTV}MdrdND=n^bIGdkxNY=ZdpA9PFCU8g}fHNwyI*a94C!uUeUXK|G zS&xyeTwtc%h9m?xVV;PU)p|Q}%`??j`lQ2~v?rcmt|#x6FMU=^j`XbOP3bdIt*7Lo zGB5B+;&$V_62lkpf-8J8bCgia=t9|_dJk;iD5bSa4`8^)4gM1%+Jy6F`06M zil;1COhdnRI6Z|o9EfU%Q=lK0(d2kIZ4k%PA!Lh7Kdbf^2(pDXt9kt2OLA8!$8+_L zXqlE6#cxW{MjXeRrnOQv_W}-JgOd9zq((1uG~j+kbDQ4jzAlZ8DRAo#FH({&w-b(siE-C|KsCD9}C6e%#p3lr4!GbY_U$gn=kIKXth{TcEq(atarsi&=&oc4BKp2$+eKt$NULi2U~ z4xNC-b$ueYu+gx)xW)Ze@OBRtDJa4s=Ld`HSd0+>>FB^bO(BB$fvB!6KUnzA0f6Z& k;W-X*n7?y7Q~lcc52WTH)a{vGU07*qoM6N<$f-MH;`Tzg` literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/config.png b/app/examples/Multimedia/MediaPlayer/config.png new file mode 100644 index 0000000000000000000000000000000000000000..b4248ceb8dc1bb60b208bca3d79c15d328de2f9b GIT binary patch literal 831 zcmV-F1Hk-=P)8RH)C42qCIXM6?OYh;&gwL9~iCZKQ=1VpOTl_p+N~^ zq^n@Ae$Gv56Yk~&J7Gm`GRK+K=$#D91nuN6s9<$ z^@9eog^!f@>>eP*VJ(~n+_t(OqKO_RJ@A)9IhWXFDuUEdi{#6Uu`u(}C;`F4D?=X2 zyqO6R$wh9O<2p~B8&`uh8E+u%hTJddz7Pn=kV&9Tr_@-IX6Ag!y(rD*phPFnkiYxl z9w20j1v;6AJnI{Qh$&|2sm)vB%h#gxcxUzs1iZxtfi(9YS@mB=Ae{>GNe7^q?J?~X zKLMCyoX<&qbFL6L#B+ivYZO7~DkC;8qGxXunGyBw~0L!eaIa=$kO z)YHh$a@9Y>4FcAOT_Sn5}>hq6Y%hJY?_l|tGfx(ftslJE*A zl|p}t%RIBuA7y7u8sUA)r`QlUuDtpuYzf@rLQHz0P@dT&VP?un2C|eb9TD&s8wA!O zfxpU=)h!(n=uw{B?G1sa3@c3?HeR zE+QmZQg5cCr`%N^A0Rs7H? z;bRu%qrZut+8w_oNB7IPk(;=Q;0g~B4><>;C(l6e4cU5#YqGj5<`%xiL2E(G=ZCfY z$u8kFFZ#!2%YG)aoZ7`vhgb6tMUwq?$o5?HwY;WVony?_O&Pm+!!arS2>@d}CxPRv zcB40VR=4?)6}B!m^LM(NzYj9WOA^%QvO2O=*H5xufDOLxwzvcm0Isu7%bia83XW;l zZWX{HzDqCjKP?}Xun{Kh0y=fw2?0`kj|@owcv1rQN%}Iq?oYBxKn^gU1rbmsf34$; zYXL9n@)rbPTaK}xFHnGuh-}vaQf66Ntoh41lH;ul7Z8^|xM0%XEe1w3?#}D}-~t?j zx^Y3kVKYC+)mFnA&PF2OPPwb?S`aW`=D!dP-}FTvTmaas!RG<6QV;2KR-;?8w?m-` zXxHF5Rw%USePN&VSw4s|AA5(wtXC%$S`?bi+$gRH0nyn2#&xDyp;1@RDsPEU05PC5 zjSBTs|-H8`#2391>P2SS#|hRazM-Jusz{2?gs2`KMJH z&Z#oDX(p5^@#Xz5rnDUWcg#dgn$XEE|@3qQ5)1OA@ zcArkpqhA@L+6UXLnpvEROu$y1yl9NWz9p-+whg!OdL$c?8o8zI0u+EAQ;@<_mbgD~ zG?Mpkm2okXS)Ux!;!GUp)zCcsjhX57v?T`dKAz>p-I7UYXD5kkq z01fW(h|&9{Z(v5kc4mDZ_7KgWBSdrm5<@gA?Xj{x*8sLXNcCmjAeDasF@7Z>?m;R5 z;AXi@4RDpCqr$>;tM%cNB-L#wo#-ojGvKNytIm=7IkRAO81UV zgz}-~S7jVS?3U8MgfZ0000N7)M%hhqo_GlL0e)ANlyV0EG38pJs1lTJs63Ig1vbVFV%w- z^&mZYFzKbGRG})gJ@^MyB;X;@h^dmKYyN07*^h_K?tI^-K>3*)_3qiv@&U0NYGv={0X~to73WjXg20108Zo&k8sAU zag3&E@j^5)W?MK-Bp-Z~i)L+cw#Y0n0PNe@c&8>N>HU@Iu76VO!|oliBd=d1{vUmC~H? zLX`5GZQ&{FXToo?)9odS^TG>J%q82xAe*Mcr=;6+6y=QTb&mc7?9Vk{UPI}!(O)) z(_&9XPBl(j8cQtW4F5WZ5Ah?_p||(+vd)N@uLd<}sg^mSJ<)*v&{c0dT?Z7=5kdz5 z^mFK){Sr&K8VcOk?<_X)#as&&5AY}Rq^Ws8M^qpB=-Z-0Q-hG09&4e0e@r(Ncf>5y zmnsdyxK?<>lGqWnODBt*CgT{LW;Zk^yI`YlX+S%j=0Yj1of-_njY$I)e*9M`>w&+6 zpfYKo$d6w-$4k;^@GWK%(?gv6R1tg}OB#6O$6siz zbNo6P=Mz1WB)&G+3N3k@3C(tuvCDqpQlHF*5#3j9ZvQ7Q-BX>Hh)7;S?dJY4B%y;| zB8YwneMNLU)zCg(h6W#{HC>ym;wOfj!-qJ_%7B_O_b^X2s#3OR+nH)akJRl=gg)N> a*Ln*hs%v809a`i70000_}6?;+-5iRkg zL`nhyp;SD0@Zza>G1>+jYoCYBFL!scGrP&AYQBN6%gny-H}l?nGqVE&hShl^7Fi}Z zOsr3Abv{wyg(aS?a!8gW3cQsAUi4$~Arg8Kk!6WDc}H3bu5&E1#Ghi$Enqq_`ajrO zMJPWQwXe#2?i1@llFiF?E8%FAU(E#Ngg$+8?JB6dQ!pUxP&LeQg)Q##5`fcOW!xZa zl?9eP;&g#C1sftF@=*|Ezf_H9+E>}@ier?5SjTsgG2@{fC*_Lc1+5wg=FFp7y_5ph z0ayz}Z4yUWggG(@`OpIV;wS|aD1;{0i8F8?Pzso(z# z5Fgy;D3IZy=?M7EUtQP{)KhZ%*1$jG19N1tcx^7AS3Xdp)U|#4*1#0^xYJ%j1x8>W zcuE||a3Z9Dz&>Cv-0hWPu(|LZ=#dW;d2dX=LQ$)qG6i*FfW2~BC&h%is|PlzP-{F3 zfqdWw7Z4sb?$&r?JO(!We4vHlzQBnovTX5~a!3NdIGR3?;GE&cWT{bpetx19(CGtl zO2&q?LXw&zju*6QU^0Loo@7$1pHe`*<2#`h%8A{sI9|~7f#C3qqZBaDd}wl=I71)k z`+C4Hj@Ei040_@N{e+eegu&1U{$3wQa~>g2B_u(LX-3HN%2dEJ_5hbdze0b&abTwL z*W`vW1@;3bix6-0#0P>+#tp4rN&%Yy`~;xPH)Hy0V^>`F5u>buTcm;K0IZTGXSgwW z!WRH;GJ~b_rs==k7Yypire?*R0^V^Z!XI9-&e|946f8tY@5Ls&q#W77Wl?r|h#Jh% mt=Hl8zCI59YL~<7!QeODA$j&oiky%D0000c^-+qOMoWW66vOH$nce$T9 z0mpJBVV$NaPlDwbJpyOFj}L5V82LFr=yH=%_cYkU?;uJ9&@BsK0W3hK_0+dqZQE{% z>GS}-^t27I8G2%T3XB8zQ*eVp`siwE>F*rC1)ec(asL@8KP|u=W)uJ{r3X-}_xwCj|JG@MRaO%+3@yT&W&vEL z{BTF}6x`!h=vgH5lE|ZO0rAlO?6t%w}6*-V>)I}1KlbUvbiRhaw1j%bgD2WpffaIho kYf@KkU6pw$#NXxp1%f`{@Ox$XPXGV_07*qoM6N<$f|Xbe#sB~S literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/gamma.png b/app/examples/Multimedia/MediaPlayer/gamma.png new file mode 100644 index 0000000000000000000000000000000000000000..3a2cefd4a4fea964560dca5c37b1f9b28fd591aa GIT binary patch literal 693 zcmV;m0!safP)6INX4#5Pc|g6Y5|)oj-7<5~jb6I(h|<>W5lU&;0~m@? z=jP)QKP;twp{BXu4K~ME<;Ej{*aHlCVItwhGe)uOvS9n1&&3=V1f`bNa?b)lBlk

    }2e9Ov(6AVbpkamGdlF7kjJedK(sov;DWN-NMqmoe8#CrJSE zJmoQuYy!+{n-OQYVvHJr%N%0{_~83`W#0uUBi^~>Y!|%%ytW7MI~Z5Z90amV1_4l7 z=STpr!UI^(o-zQ+EJsZyqMFGKsqLeH0O0pt~=W6?V^io++c#ZrFUMSnhp+|HPGUK`mOXQ z{Ahp%2egR+)`VdHl?G=x2F@sLOa*SAr`;JYj#RI~KzpVyw0#kf934=frmS0k*Ijj} bScg9WsNVZDMo|oq00000NkvXXu0mjfq5LxQ literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/icon.png b/app/examples/Multimedia/MediaPlayer/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d321ebccd32db289220ce9d45efcac9476a02086 GIT binary patch literal 2509 zcmV;;2{QJHP)nv46CsojO{#Y2B(b z&UE@m?X-51GHMo!6|$)Rx$ow|>cR#kE&EwW7{g zYnDTWS#8|R9HwaslRlp36a%c_yP(Hv_~;T6s0>f36Ls2hvC1wAIdfIHoI;eaQOW{L zdNEPF%3%Q7(I~S!YcC0bZ?0Rnx6~%6Zypr=4HW@B8lb{`WAz z9PQj1o|`;@fY6F7tt$OCswmXKB*BC1VGw{4YW<&dvEQ$Euue3uLc{r*1pJI%W0h&D z*tt>`QbM6Voh2mb$Hd_X@A>{WA^rNlP+m>LU^#Yl>4F2FO^|?_(b^3sR?GYXdo|1D ze1^pVIYEK87`rW@B_}#66`VPWWQbiS&tiT3TQO*!tJDWPS+Nie{DLKnrtS=Dt z%#Z>Llb`V}JpfGQTRyzS4*%EHTn&IEL_go542=Q4F12&IU7L2w@tLWGbg_Hlw%E?8g6hzNIBAJM3q4;&8&l#2izZNkC9;(fNT5r_Hw5sMg5 zNev26DBhredCcWXD!2>j3w<>0y%LIDfW$xn8$qsiC87XOUhJ-Rmd3Vactvg6>r=YbL89=JP==dzX$ zt5#KAg}Z_}%-{+%kAH|0Sb&L+q=X9cym57Nj1b@;fEy_A>@oS%Th$g`N+AGx%~o+6 zD=`@%pRas-gtZLNz|E9;7Dc@WBYhn!J*=bDYf*G^jM0>lZ1wylHGEi|mII*l z2o?&HsnoO0w=c%vB0ap#8GmWp&9feipp0f3eeXSNLqj0YxRIy0=(pst1%ZY{7oLDH zRskHx8{dThJp6}dG}dF2=iA<*8NB@go~6KV+084ST~W@{fqovR!?O#aeB6Kn!U4f7 z4$zk&b-zS%3vc+>Pw{v1Jw;~mAkX?OzoMQp+}-0I{>?j{{VRULb_AdyGAf`P;4x;i zpBDeqP7giwAbFTaxypAw%kMnB6pDLT?zar_guizvUhp#qcJUV!3N3&e%mK$iFppoe z)9c(VFHqvwU*u^N5JIPeE~;@eTks-J2B!C6YP_+Q@e057?JeBFYzR#MAc5XCrHSdl zJs3jgXuSjv~HvaHu|01 z?pjp9$MqPs2ue6Y*Pb&gGlT%_9_5tdkO&Y+?tHS}!CsOrT59v>po}n^n86hoe8r3`BQIdigl9!s4yZts7H}`K(1BZ6#30vkidV6S zPzb~WyJIqdPLynA0tDPY0UYHKB)7APBMei)R2K0ZFpnPqiwN@xtrUMjQDEN;AMK4C4M4u7CssS(QjrU@QfrUMj^< zR8WM)Heh;EH+g9tE1-a3&(AG`9sz|&O7e^Z#zZ>n+s@F)=UGKghM&R~%6T|LXfjX3 zY@0S?X{dOd?J3dw26rSaR%1X#+(;1%c>;IG4KqI}qf(dRGzDz;+?r%FldcpCd5TWn zqKZwNq!EoDQp8b?(?~Z^$kRxQ*~|58;N`&b?&QjW+|vo54?!`C2bjw;Hgk$Drn8Dp z1j7sw#RMvNh&Oq19Qm^<;FkF*H+{jfpC+zh4?9?b&0Z3G04S<}A)@qg7)c9ZmH-L- zBVd{a8MkBCrI&D40`8-_eJI+~G)n-jfv2X5SimD^4vcj){-Yk1v95qODd8VjDQ%gz z_>Akos8gnQ8ERaMTW`oXJx9M)t`JQ85SDvQNVa6wAV|P)LI~yW1+#MkC+K0kU}XI- z2{YoR!zckkm^>98O%eqd}IezstYLqk%4h5$^L zK!^}|M3mtwF!A$lb}HFv02*Nw4mOrcK%%3gD~V8q150OW2dRmaS`&d=Ohh1DJ1;Qm@yPv)fX6^jsrU~mm+0r%m zhfzcv;e{aLeZqR7%tt5i6z5VEVI#0H6TQTV6C*~9_^1ML1mIv! z7A7Mkh!ZCsTyHhPga~0Ek%+*1w@XzVFY`^0!U&`;_!=Q}BpTu}D4?)$--=i+IKck^ X_fr*v`{lr800000NkvXXu0mjfMIWNp literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/info.png b/app/examples/Multimedia/MediaPlayer/info.png new file mode 100644 index 0000000000000000000000000000000000000000..71e6db8e1c73b506b1e80f25d59cbc7aa9b280e5 GIT binary patch literal 2413 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 literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/pause.png b/app/examples/Multimedia/MediaPlayer/pause.png new file mode 100644 index 0000000000000000000000000000000000000000..db2961a31799fb220a5fe10112df2924cff448ba GIT binary patch literal 393 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7`%AFa~(KIEG~0dppzDkI7M>b#=PB z*B%Du&JDrB{*D$co!qf61Hb6Fm3Thk%Sd1q`pV+={ru66tDh&A1p5>`tlqq6()VAl zU!?`>e!Bm(i&e%e`clK|*tolEZ(mgL_?O|O2WNK28m=?{VHNlMgUa#1=oS9<{9>z} zci#N;N$L8|_-F2`t2eK&$i8@>;O&g)Hxad>wvJ8QI*Xj2S^TfvVe-=`)c)=})^qQ@ f@7>uVC;yfA=*xeb;RGS;GoR*@VW0wkGo@N|O@8l(Z-pawBg1x!YW;)ovsy7b6lSi6taU zv3u$&OwrxN^^{ayZ9n5ky*XexdNPJ?E1zg7xDFEuMrMK59ly?Dcbqu zMKnhnUuu%Nn2M0#FhiQq#)RkqOHEE4^84D#cTKp>l&ApPOiJM&fUt^>n$XGgXaL(y zhN!E87n<;j1x^9NY@TYu2bMbp2$Q+42`M%>1qel))`TJUIt2(Zn!KA+vr~W|9A!i! zS}1S|5Vm-ChAT{R3J})tSrZ=QWM}XX5Ejv^3Ej+b3J_-TL=)by#3?{1Awg6!_gPPDr+$lh)r(Y9pS+p%-075Nq zHQ{F9<}DmxjJBo4F+iBXqtSptDzate>i^4ANm(s7%rY+f9RoyC(jeVtS?e4il9qF3 zF+UFgkzpS>5dspj%PhLHeJV0Qx!jd1vwR%r%i$lxdAuC`7^a!&^bS-@hg6P!;bg4O z;T>4d*L-fpI~maN=P3?+3+(|--j3invB2@RfC5@|_%H`7e?&HbiCot4gEU&k+W<-gu)4HX{@u+lp>y3e$b@j(C${Lt~&Cx2R1eOO&%GR(L>qt&=z(Q5bvl$b}nlQPCUu&`@2d=v6}WFx`~vR9Z^BLE@4 n$9Ci3;L_F6?RY-Cc2mCqDrG93~DH&)Bw&~?s175mg;NO7LbqSnDmQTX_ra`x{_3dbQlJ=B`q5@ z2*@mX5evL6jROf-f;A9+6t|gfm)khSBLh>Qlp1Q>N;g&3{Xr}sdu^gU@`YG}u`4C7 zTpOGjWC&!KJhrvA8w9tn)h>ZPnGiYzQqCQKmz450#Gu6_NKitO@gylBL4qWs+02m~ zLvf#3b|Gc`;t~k%1C#CBT@QXL^GT8*K`9BwQ^MGg*MFLmz)hFHq)c0ofKz|tHMBDU zQ_`(K9hR$;Ie4|?BoMd!PvCta(5GlS%TlU28j(Peo?0D0_!lqmGj#y`%2U3H-vXU1 z;>CcLX0P;pBnxQIfd`~W`|6pks$2IJ(Fnj5*86HJywYcp2>9lJjHRAhyIjss3gyF; zvCS!O;4v-qvVwh)2zbY_ER?QrH>T1#QY|?Ws?(D5KyGnWn= zU>QEs>~@(|^v6L!Urw;2umoT)2MbF8n)s)%1hm&~JOsXCPh|KA1X3I@0G~I31svyt zI0%f>>NJg$j5GqD*$JILu_orxqfq;jhdz?627gyv0=HNIP|QN5+Gwxz5$Bj`3_ddi zyZKX(?~hc4Jo`He0qZF#onXj8mV`XHm*>9dxn$OL`Gk6Es0QfbtkeFvPkASe%9o_s z$$onCA`py_EVF&43!WR5amm(%#O;2QIhrw&L0q!jQp=%0I18YUUCgtCDtU_LpHU^> zDK?(pY2aD}aM0cfTe6Owe!it5GQ740g7T0C+9HDw!2*5}nwe_|J`W3Y(8vWt;CWc! z5amYUhh%{+*3oJdT+0F-&ixEkF~DmOSVuQ6X`q(BVt~&c0w);sq1SMpsTAuSl{v+8 zVtg(~kU*z>o6QI>TKvS=*Lnwer>`+^7!|qMlKsZR^&8xys8^Cc^sIzMvm4akWHe#1 zQeG<)7TGDA67JwIY~z&jXMN~nznH~tC0EJ@WRax*yQGymZMR(hjRia{Uk8vv6PIPu zY8<{@Dzb30=Ya3wY^s@VD4hL>i>lPE6h3)v6*_(Y4s_;d&yA|7)&Kwi07*qoM6N<$ Eg7$gZqW}N^ literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/screenshot.png b/app/examples/Multimedia/MediaPlayer/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..8ef46d2a63a7ab20664728c05e9d81c4e38ce9d1 GIT binary patch literal 1090 zcmV-I1ikx-P)7+`B+r&Zf+WF!&tv`aT)7oSfv&f9{mvY2bP8tyC zaS7PGD;=KUK65K%WOFjzM)OV1U8uD$7y-m znSP4&huaUyWMcwla?6CmHJLhm40kdud}sGEV6gwgxM7s6RIm$>Ihy$2D2Ds~sR`j2|1NANz+CQqQ%tylnD;cccfkx5J; z%P-nkifzUOkU=LOvjKTKL+)z%zF$hU@wBvgPQ;P`Z_Bh(U=)E>TJzY&8g6Oh_h_PB zyRs(PEqGOtbFAlX0h?y+k=+n*taSoLb)ZI(J3Tw@V1UmQt&+!+i@;+{P_(1Ic1JX= z%GWM(9jMT>tG;&nh<~FQIRb;4R_1G07I4>?2>eY>(dPTw)o9vZ$q~qLQPCQBw4jwu zqoVywUvdQCOGPf{oq{&6Ge^-n1A2oP1omrM3$Kl`Sj;X>|02MfUW%_cMR8+5@wDWW zR%r9`u}mKsEtjpHQr#yL0-G8ahCQLs!ZAg+c!Mlw=q1OKEaDN5eQRKIroEj@pTng& zq>=i_^mR)PODfRQ^U)th02?@j34UUgc5H)lAOe9LYxyR0`W{{>*rX&t0IpNd_Q2JB z&I?-2NSFZJV-q!eU2yCii)r-q3E>1{ycu;Q|Alxk*TbD4{Dae!{a`@IAry%#w6 zjW*gM*!vTRxCO~T?+0QZJ^l0sf~C?Bk) zLOw0(^gm1O@XX4Ql&7fWxtM>?m}7`@oaIOB&O_Svag4)@`;buGAuSC!LI3~&07*qo IM6N<$g4h!bDF6Tf literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/seek-backward.png b/app/examples/Multimedia/MediaPlayer/seek-backward.png new file mode 100644 index 0000000000000000000000000000000000000000..d108a3f171ac516949c28353ec4d4ec35ff0d624 GIT binary patch literal 1316 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 literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/skip-backward.png b/app/examples/Multimedia/MediaPlayer/skip-backward.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d4f073317572bb781964bee8066acd6506692a GIT binary patch literal 1093 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 DM)e0F literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/stop.png b/app/examples/Multimedia/MediaPlayer/stop.png new file mode 100644 index 0000000000000000000000000000000000000000..9ac59d99b338bb0ee4ee9317190fd38c7ee453b0 GIT binary patch literal 495 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7`%AFmCg7aSX}0_jcxaFQ!0=qvuz2 zFHpI>vE$xCd+G46Os=^~N*U`LyYDTT>gX>pRZL>b%8o2i*WA~PPR^GPbcr3zG`)9m zby{J*V{N+G^VvJ^eBNzbZg!v5<$tW->e+9O$}F4M(7mFuhwIdi1B{=}Ws5JCOzCpH z70~F(X4ftMXyw!b|KmO7UHu==W!ZS%GrFc{ccl3BN0rKB6TH4gE-_r$B~`S;<>Udm z9_7Z$FDs>kJ(L!&w?E^~y7Qz&;)kN0&+FM$zrA8O{jcJ9h7AYXj>qm1ml$82*Y1(% zXFtdoCVF7n^ws}P8=lX3eDc8b>!Ge;qKmB#Y)H>rbHx0t%_8P2+cU39IYi#v!1uvh zh=EZ68wy}JKhN;u{5;+8o3r==KAhQM+Q6CmzO*6xypGbfr!DcZ|C_X4WpXLJpUH7e zH>a1u?85p}d(0MW44u6=dyA*!^Ss$%Yu_JfSsf7^${QK;aP7j`e`WVBUcNJucgb9z z?u9&`Ubx@-+Rn3WVd@umJ{_yS&t;x;Uj3oG>xB;6&O_|l4_%jinSTG>jXW{qWBqTx h%@t?f-1%>g9Yfofu;uq+4n7A(q^GN&%Q~loCID#<-xdG> literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/subtitle.png b/app/examples/Multimedia/MediaPlayer/subtitle.png new file mode 100644 index 0000000000000000000000000000000000000000..a124f785039b1c809ace5af38ab01c77ad675730 GIT binary patch literal 1174 zcmV;H1Zn$;P)(X@-G^x zmP&0K75sl{!L-Fj`~ztrM2&)4(Ik8Nu-^IZPG;}lZl1gY%iY=Ue&3mM?%X|d2YB6E z4K<;B1>nL6a|(DD)Q%9`R+9t5unfIBoS z_$CP!;BD1C;tgLL(SrG54D3^v@RE(*3^Z~`IQS-{d^HD>71g^uShRuca7<1ktEv<5i#!xfXi96GA1FlrL!9}09~qmX16?!%9jOM<|OMb_*9-2 z%y+=Y+GnYzWr*;Jn*fOk`O`STYF{lh&pF;0_m|O}jAB4HAzenGi!crVFb<21aaxuP zb23_j4G9z+b`ZcWLYF>|Ho)BUX=^hfy$Q3|ERmWyxyRTHV6nt*H70G+Ojn*w#@KE8 zYLL<^jpsAf8A7*30P$H7J~GB0!*E6gHCDg=8uVjgq2GgL#-#0N)4tunAI9h&nJ0s2 zie8oyV5Km%G5Ak7>)51=u%h$~DBFaTI-JZ2>-a>)bXBxG2p$yxPgpBe^+~t}iE9Zr zA*Gs@u^wx(8AwdX16Eg@foeW5n-g~v5_)8t6Oo`3RT*@RjR;lf9L+$o6Y>w^F<~I) zzrsO!=Y&&bW!*a_K5MzCz^QtU+(O&5Fi5)1>B3wgr#`&9= zXNAxw3wUodTLhRT+ycI()$io!*LbqN%U2?1a*c6Q5GWKL979}@yby3tOx zTMiR3ZBAkU^F>no1e}|xf1$Nvj&;0Np&9QN03-s~Oh}665p2U|e1ToayNgg{PSycn z4F?q`_MMO-0nVsAJg-jOHa`7QDoKx(ybfqj zmnVvT`d)Qf9YoM7O$To&Fuz!y@#^$f2Sc1x#K0tR6BjH}j_0r1(gFU!kajkGRXiJ% z=Op-H(7+|504xd0vk(AJdk@Qe85Qty>!-rNoY+yPb}IXgU>SKlUQ8NaNQUHzWH8#$ oijtek0EThf{z|q|d0wCPKeKj|gU!d-<^TWy07*qoM6N<$f>iq#YXATM literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/undo.png b/app/examples/Multimedia/MediaPlayer/undo.png new file mode 100644 index 0000000000000000000000000000000000000000..76e6a8a29d19659b27cebc4115f558933b07e768 GIT binary patch literal 1091 zcmV-J1ibr+P)*R=Z;HR=trhbqc zt5T0IX0uG(%M3mh%_FJC&)^T)%Ke;48kN9A%DsEG7mmbK$rdGA@)rUznH(pIKR16f!7c~ zlJ7V-JbsxIzgUUa6hL-xkBZ{E30aDZ$g*1suz)wB zIzTDF`hrbJBvIL@HZ0+%LV(A(Sq@sQM$f}eF>phGPN#$>78w(;R!UC-U6m>?luLo2 zo7GB<1eE}vl+3CmaLV4I*|>})xa}i$f8xNlqXo@)iAvO)*t5EO!JCxUGgsRK0WsIA z#9GAdR+!K<7zeed85s1f7%F55`k`sI83BM5!*=n8$i`(&FAOmy0I({tRY=hFg8u!d z#(UQ(AuvF9ce;}k;FX}e5N^v7)wWOql3fV5N#E&BAq8l9A+hx8wHO#hfY%E_ap9Bl z>3C879NK;VX6A{GnS4gC9aBca`=UegS8Q$xPZju&?61J*$e*culNR8X;I&sAt(^Y~ zxl>^T0l^ZgIMnB*RV6w#GBc6@KM9WmK4wSD-~8tz;sJoA{4T@aa~(4Rl94L8^Fjjx z01guOC^hUGm z!K+BDW*pDqPWCN~#VF^a&zNi(Nf^5X6b45GkF~Pc82q1v<00_xnQs^mFpDQO@i(ZS zr-c-;fiD8lovdZ4-_bL2B55HH`Iv+p&!3v{0ynL9DVnK!SohAGL5TOg#hi2RyXX7vIrm=XJ_r$Fnc?Jb zR-8EX)GXDX!3-n3;lA^+sG1a4pKS0;Nv$g$eL{0q^l1!hV{zyY>30G2k~E~s;Pe$5 zEs$Q4t`x$Vwa9dmg&^}h=Npe1MtKDm+F zjpz|tp)pUuIeX4Xmbcn9hK&QvGUPdxK?QtvjRO`T<~*}3sDNJAILrl1&zAY6qS-?A z>NVYQUwy0Ksn?v+4OM5NIciatCUS;Os7uYc~iU>-I+7t6Ywav`?5y<8RE2`ey{BTJn0kgz;39QsTe>MR7TRjd}c}gbG^*??4NuC zDr~pF8XD+^D6p9=ZtEZkUNCDF=_mj(_LF48qQ1LaTh!9&nbmFqj=Ncz)+XZV!Z literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/visualisation.png b/app/examples/Multimedia/MediaPlayer/visualisation.png new file mode 100644 index 0000000000000000000000000000000000000000..a97ee5fa15543f2d885e2c359697c9314f4cf9c0 GIT binary patch literal 1093 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 P00000NkvXXu0mjfY;kTe literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MediaPlayer/volume-2.png b/app/examples/Multimedia/MediaPlayer/volume-2.png new file mode 100644 index 0000000000000000000000000000000000000000..3fd2482db747bd838eaf51d23c7b59c19dc35d64 GIT binary patch literal 1052 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 literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MoviePlayer/.directory b/app/examples/Multimedia/MoviePlayer/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Multimedia/MoviePlayer/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Multimedia/MoviePlayer/.icon.png b/app/examples/Multimedia/MoviePlayer/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c308b41be2fb91db340ecbfb89035b67961777d4 GIT binary patch literal 4342 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, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: MoviePlayer\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:12+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FMoviePlayer.form:20 +msgid "

    Gambas Movie Player

    \n\n

    This example is based on the MPlayer movie player and was made by Benoît Minisini

    " +msgstr "

    Reproductor de peŀlícules del Gambas

    \n\n

    Aquest exemple està basat en el reproductor de peŀlícules MPlayer i ha estat fer per en Benoît Minisini

    " + +#: FMoviePlayer.form:15 +msgid "Movie player" +msgstr "Reproductor de peŀlícules" + +#: .project:1 +msgid "Movie player example" +msgstr "Exemple de reproductor de peŀlícules" + diff --git a/app/examples/Multimedia/MoviePlayer/.lang/cs.mo b/app/examples/Multimedia/MoviePlayer/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..7938b9cc62d18c826926988e21aaefbc5c28c293 GIT binary patch literal 708 zcmZvZ&u-H|5XKiM7t;%fBZs*oH}qB+6V)_su&`q*e=32vOyX&>#ojgc;x;@17mhvf z1e|~;fJ?}0aOVX$Gj386Bv$%q)-&JCx7Pf;w{t1@>@W`)m$}WnVwzJh=gb>sm-)rq zXMQu{h7fm{_k7*GDa2ilmmHfppEzJ~j72~#w zwpf|1I{x{%_7X{{&uC)oAhl1ZtfW>BB;L_;uj|8!-yZ~V&q`vuPc=`~%I=^c2VVGI z-;QFtva15V9;i%n!d#; z*+atIfza!9M~$T$hKHHl)8%xdalnd#$lttgfA5iXq?IAXcJqSYW9Y)P_&23`43im_ z6`3ca;jw+N(KXLHrPB7*M5j_s9XJ?EV{NadSbn5gi8cQC0H-p`mtVIQ#AX^;vH1Kt z!xYX4-eIPf-$^aLfx@PA-7MGLe;0?mpbYAHZ8WRY|96#a20$%SLcy7}+tzSe7ykf` CYTLE| literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MoviePlayer/.lang/cs.po b/app/examples/Multimedia/MoviePlayer/.lang/cs.po new file mode 100644 index 00000000..f7cb6247 --- /dev/null +++ b/app/examples/Multimedia/MoviePlayer/.lang/cs.po @@ -0,0 +1,30 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Movie player example" +msgstr "Příklad přehravače videa" + +#: FMoviePlayer.form:15 +msgid "Movie player" +msgstr "Přehravač videa" + +#: FMoviePlayer.form:20 +msgid "" +"

    Gambas Movie Player

    \n" +"\n" +"

    This example is based on the MPlayer movie player and was made by Benoît Minisini

    " +msgstr "" +"

    Gambas přehravač filmů

    \n" +"\n" +"

    Tento příklad je založený na přehravači filmů MPlayer a byl vyvtořen Benoît Minisini

    " diff --git a/app/examples/Multimedia/MoviePlayer/.lang/es.mo b/app/examples/Multimedia/MoviePlayer/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..cc4789522eb22ea504f351c6658943fbccd563c7 GIT binary patch literal 611 zcma)&%}(4f5XVy}m#C+nIn2E>3sMg#q!B4=mWX(>+Aj%C^=^iZQDR57`N)Ix0r~=c z37(=-)nng)JF~k7E=cW2KiMA5e`fsu^YQ)nfMS=}Cq5HA{V|_R5-QcJJGnwOMcG7\n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FMoviePlayer.class:172 +msgid "Movie player" +msgstr "Movie player" + +#: FMoviePlayer.class:177 +msgid "" +"

    Gambas Movie Player

    \n" +"\n" +"

    This example is based on the MPlayer movie player and was made by Benoît " +"Minisini

    " +msgstr "" +"

    Gambas Movie Player

    \n" +"\n" +"

    Este ejemplo está basado en el MPlayer movie player y fue echo por Benoît " +"Minisini

    " diff --git a/app/examples/Multimedia/MoviePlayer/.project b/app/examples/Multimedia/MoviePlayer/.project new file mode 100644 index 00000000..3f71780e --- /dev/null +++ b/app/examples/Multimedia/MoviePlayer/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +Title=Movie player example +Startup=FMoviePlayer +Icon=video.png +Version=3.11.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Environment="GB_GUI=gb.gtk3" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.class b/app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.class new file mode 100644 index 00000000..582bce0d --- /dev/null +++ b/app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.class @@ -0,0 +1,136 @@ +' Gambas class file + +Private $hProcess As Process +Private $bQuit As Boolean +Private $sPath As String +Private $bShow As Boolean + +Public Sub Form_Resize() + + embPlayer.Move(8, 8, Me.ClientW - 16, Me.ClientH - panButton.H - 8) + panButton.Move(0, Me.CLientH - panButton.H, Me.CLientW) + txtAbout.Move(16, 16, embPlayer.W - 16, embPlayer.H - 16) + +End + +Public Sub btnPlay_Click() + + If $hProcess Then + Print #$hProcess, " "; + btnPlay.Enabled = False + btnPause.Enabled = True + 'PRINT "CONTINUE" + Return + Endif + + txtAbout.Visible = False + + With embPlayer + Form_Resize + .Show + .Enabled = False + ' '.Mouse = Mouse.Default + ' Form_Resize + ' '.Enabled = FALSE + End With + + $bShow = True + + $hProcess = Exec ["mplayer", "-wid", CStr(embPlayer.Handle), Conv$($sPath, Desktop.Charset, System.Charset)] For Input Output As "Process" + + btnStop.Enabled = True + btnPlay.Enabled = False + btnPause.Enabled = True + + 'embPlayer.Hide + 'timShow.Enabled = True + +End + +Public Sub btnPause_Click() + + If Not $hProcess Then Return + Print #$hProcess, " "; + '$hProcess.Send(" ") '("pause\n") + btnPlay.Enabled = True + btnPause.Enabled = False + 'PRINT "PAUSE" + +End + +Public Sub btnStop_Click() + + If Not $hProcess Then Return + If $bQuit Then + $hProcess.Kill + Else + Print #$hProcess, "q"; + $bQuit = True + Endif + +End + +Public Sub Process_Read() + + Dim sData As String + + sData = Read #Last, -255 + Print sData; + +End + + +Public Sub Process_Kill() + + $hProcess = Null + timShow.Enabled = False + $bQuit = False + $bShow = False + btnPause.Enabled = False + btnPlay.Enabled = True + btnStop.Enabled = False + embPlayer.Hide + txtAbout.Show + 'PRINT "STOP" + +End + + +Private Sub StopMovie() + + If Not $hProcess Then Return + + $hProcess.Kill + 'While $hProcess + ' Wait + 'Wend + +End + +Public Sub Form_Close() + + StopMovie + +End + + +Public Sub btnOpen_Click() + + Dialog.Path = $sPath + If Dialog.OpenFile() Then Return + $sPath = Dialog.Path + + StopMovie + btnPlay.Enabled = True + btnPlay_Click + +End + +Public Sub timShow_Timer() + + embPlayer.Resize(1, 1) + embPlayer.Show + Form_Resize + timShow.Enabled = False + +End diff --git a/app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.form b/app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.form new file mode 100644 index 00000000..40f5631d --- /dev/null +++ b/app/examples/Multimedia/MoviePlayer/.src/FMoviePlayer.form @@ -0,0 +1,42 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(33.1429,15,51,45) + Text = ("Movie player") + Icon = Picture["video.png"] + { txtAbout TextLabel + MoveScaled(2,1,46,20) + Text = ("

    Gambas Movie Player

    \n\n

    This example is based on the MPlayer movie player and was made by Benoît Minisini

    ") + } + { panButton Panel + MoveScaled(2,35,46,6) + { btnPlay Button + MoveScaled(1,1,4,4) + Enabled = False + Picture = Picture["icon:/small/play"] + } + { btnPause Button + MoveScaled(6,1,4,4) + Enabled = False + Picture = Picture["icon:/small/pause"] + } + { btnStop Button + MoveScaled(11,1,4,4) + Enabled = False + Picture = Picture["icon:/small/stop"] + } + { btnOpen Button + MoveScaled(16,1,4,4) + Picture = Picture["icon:/small/open"] + } + } + { timShow #Timer + #MoveScaled(32,17) + Delay = 500 + } + { embPlayer Embedder + MoveScaled(7,20,23,13) + Visible = False + Background = &H000000& + } +} diff --git a/app/examples/Multimedia/MoviePlayer/video.png b/app/examples/Multimedia/MoviePlayer/video.png new file mode 100644 index 0000000000000000000000000000000000000000..687755442f5c765a8a93804555ac3c833994c690 GIT binary patch literal 5244 zcmV-?6oc!DP)sa%=XT^-*Zm?xc9y9eP<>VoeA~nd7d-p zz4xB?zUTY>t!Ejmwfujt8+Z4$ZraoT;p@Km<;Rz;IJfY{8*ZImd*<^0ux!}fmvWSx z?I>C0C|Q+FIm;a-&ZU=M!BdYvO0`nF7PtXGthL8=FzIi2wnb9(?Jb}?Ej06P`(69Y zc>cz%-?-b!59U7jvCFSo7=b?5QDht?I7)Dws8z!Dt(*C``|sa&*SEfQDsbF2K&DS= zVb;VG*m&R|ngE1913oy`7jM|z_s(yB{kAV1Xy0?;-o3BREaYE7#f=@gT<_M6&;R_DpZ@fbPM`$j zfY&0x6`%lMM^U-AfB*+qIWc|_QK=JL(w@8M@q_n2dv)6vaUF%Dgb!YPF^3QBWBmj7 z<2VjR!{6MphQD9?Ei$Q$?KyJz;Fgzu(ecdFPi&-7b{Lq6t_wL>MY9!I#2(TLg?oI%%{xV}6R|0;%?dXY)lJ_`DE^w5r zN_pM_h2TkD12C4LYA|A0wq!atJ##$|e*7_9M^P;1bGu*J(LQVT^p4G&U+7x@AOHMZ z1h@!n1~L)g4xpyal?3=-I;j~`BJ||TJsC&IIW@qJk~177s%EQ7;Iiizv1r;%9(v>@ z^83oj1i_oBc>BT$oHlDZukPq!`jOLLss5z$)K8yyA`k2a27wnN0a-$O7C;f&_^%9j zco~xh?ArYNp8xpa_h)#X%ZhVW@|&Ny49|5MytNXAqv~PMilsVa7|4}4G&sQW)858| zPyTFvuT#7w0&jpF5nv&5VB-aNcu`~d@>7pJ++Ql@nLN3bCmwy6yS{n{1ZA=*hiu9r z<14EW+=LGRuw_fGzf5O;j@NR%tXw|Fk@IB@XLAVn1)-En*4n&Hl<@{XW&)ETYsdb5?ww!%#{9eg@w=~f^>nRMICzq< zv<}`!z!OxV@OqWQhlkkHeUwU*rn%L>LcS`Oz7@b@9AnWe5ouX?@@a#OH^Zi_Z7Nq@ zbp>l%ZeiKXx!n8UljQc6xUl#AoUwE%XMS`!H$L+hY#-c)njrWs4sJ>@vDn6w7d^){ zpZYYFN(Eyq?>YZ`zVO8_Me}>YN>nYBiUqFt?Mo;W@&K$j>m2_4<~thtgN?tp(=q~(@(K! z(`-mMlRzF6mSgH?FvuzgqrPl&H}E8+cqlip_@NI%V;xyzBI(Xk*ca zsI?daCi;BeJ>QPsf70Cfob~SaMt$nF_Px%|L;UQCN8|ouPJ~LOV#9SyOA9yNcq5;@ z@=CNZC{cLgB9U!ee90vk3y$mJJ1$fdA9}}ck*nspb;peW7++I9y@daCickE`RcJFz z^%JW7;>PFLx&7t%_bh>t8~~ty!BNr&XGVQ& zEa~BGUDAy5j1kY0T0dq2j5UY_D-yOb^zg5P3N@hCUmHXOM6f387Yf7zLi4_6L<`oj zqhX5G@JNdy^J{u7!#>j)YhxU#!fJ)0iiltlc+Y*8Ud*~WDYg~ zptAu%rh4o(e~#5A?Y>4}tOgAlFlGXhb{1Ew<9iNHlm{gG8-uYnswO4{YY{Bj_NKUh z>BJ(&+Q?bf0GfpKhqsK>nurja(1uzWy9pRm_W_X<2#XO zQ>M_|+=6nPctFM&%B3=W{r#N%Skq(joQX*9VJ6!CcqjUM}Tmyn;-x| zTd109d_Oc9K}jN(4_D=cno3bcjxy25472TqYU*b22GOHd#w`leJijr9Qx+}c*4x(b zkd7lcBe_hkSmBVlmI& zz3mC$$0ZMtIE(?LQYqTo_wvxg|4HzELq!MFaxY^nA{L2LU^_x$#u}88@H++A-o3p6 zeBBgCIM3RB%X{9ll0u=tg%_^ITFcOU?~pq2&J^nAmH4Q$x30a3!F_tBsM z)@V?IH3q1g00|RtvkoQ`QZ_X+jcxzEeVDnJxUU(m{?@X3^=h_l+s13Jy~dH(kBl(0 zWEr14U|6lG@`MQ!&_+|y)z}G!ee4JmsO1C37%uyxOEDN!Rq?UYKFYRj+ed;o^_XAR zj<7CRuz)kpID?Lk{oHfU-Sw1vlvTFz@c~vh05`_a($dVgzI7+s8Y(u3)gw7c!O{TS z8pBmreG-38sF?E#^T@i{kmxvrOn7dU49wMmMtA2`SByb zj!cCoPMpXO|LH#RLqi1CL}8E^Nc^3Mz@!#@@{^adt#3O3H(hiSBEl`VtO-pZ)|AGq z&zMH-FxF5mm)W*0Tqh`%O2;H%oO}S`Qm??f-gPQ#*M5`k?ry&Fw_ihlJd6Tyf?_OI zLcdX)o+h2(c`MJS=jy{KrO>+#|JC(heC9Ks8yV`1trM7dcIl;;(%;|5q)C&x;f6nB zh4qoaen?z)S6?ztyUdX5VL));IY0fZ$> zmc`FRgzRmaiV5oJt~K#`K+<+=Q`pkN0?tu~^s{MJp7-AHH%9UF^~h1n48zZ#{`6-W zvc>rv?3yCYr-%{Epah`iU((+a9GRiD)Y-^|D_o!Bs62#M5m_q5Cb1h(3=52 zE(Gj9uz%Ev2a!}3-#@#-39kLl-83~fGkf+)SYXGVeQ`Kkzvo}+8tmkf%Rf$YOAF~t z77d@3BFg!g?v#pYM`aq{e`jao25;`Qq{Q>?Y)vnX221mjdV z$!=r<6;pSDwIwTK1SJw{i*7Nvo4go+O#rfx$oPg$jE4J#U$Tso6O6<@?Ds{4z?gVF zASO(}7ztD2Xx)wh8}ipMX^9q4qVHiXSAFVAIyyR-GiMG10|Qj6Rr>q;(ONTi?p(UM zy6`-YojZ53bLY;L7cXYhrcJC^ zv4Urwd2@$R-?R@93H5c}qxcisvZs~?s%MmPdRcLcd^Q*of?f!Eg&;539tHb7s3`cu zfTm2Ao>zL&-J0~1Swui7)c~|{v!lL=;rl6GeyxjnGfn_t`SN9ScZWxU1_lPO)-rW! z=pW|HnZvGKyAToD+uPq*b0Z({rU(#3UD!484^Sx%h6mJAKvO7K zC17T1Oiq*U*H|Z+2MAF@7)yGzZL9_-Xw0|(A6`Z!KoD8})6_*?ll#r3tt)3;H026h zNl}$D1#-+xzXKCk$}L3-#T-M0L2M;_n-x$BY!iqBqQDH9@H8Lrm2Z5Li4!L=|D6k& z*4BparxJGFxOq|EHG(~zLnC&2lAE4MO-e4BHuN9%{t*O-i120PjMKeOO`6cQ_M%Bw zy7O93VS-G-h^8PG-|IOH++%(5uLRMM{0?m*!0FEJW%NPQxP7&o~+OOh8d`4638#qz~g(8Ipd)Hz*&(2jxdZltEmO3MdC^Sd%NX{}+Hq zB|!c~oYoYr*7C=tszszo_h1)Ish#N5AQPF4gM)Pg`tVESidUe=9O8)TB<~c!w}=mpFBl(;Z$lDNU@M@iperC% zDC{Y4!x#QwM8^1kRK!|qU5mBSC7$?vUI*d9dQqTFh0dXN3T_Xrx|x(tQLg66`Mub5 zfJzIFFL=I#lUA6jpsE&I)@Wbjlt8;-L{I_flE5MR>jECY6_FtH0jE$lyO?G*;D8Rm zv4B{HssogZgLpV76vojw+973J+;kd8yI654V<_4xZV;fV6>PDBb}R-Et+0n|$nLLC zNdtwDfE`$%M>Nfj4y9J9Kw5)T1+;^QPm_}+?RunKpC%_o)=yzYQC6A(TO_9|sA`#@ zs41reaV@suU=Q0Kz02+*42HR=)>cW>D78Kzs2>S5qg49_*E*_I!Ym|Dc+CC);eV2Z zY5>4ZSh=Z71c4z4!VM0$>QM2jxGrRk4~|7SP_`BND>;tj za+La^a>UW)o#utY6J`U3KA;oW3v@*Ts}WE;2A}+VEDcCX*dC)zey(XiFpN9{SZl2n zYb_$;0omyvVMXd>wN5KF)thASS}jh-;x!AYCYMZ;Ppa7?mGSU03X~9d0l9%9gFTvo zcFW*yC#bgT1A~tRFY4EICx#=yp-AlxMOu)L29zvvjO*cUb2K;lMt$OJYqS&RH3G6g z7HDc-!bvB*+xb13oD0O43F)aC{{(~61l~jguEI^b;VMqmp_mhjhaD=fOHg{Xa-_((L_mBY z;u2{Bkj5vA>owuqEQ%DpL6r&q1cVfU_0h(mpfz=yD-$}Je4-Wzoz4(Y{&fL9Dgg}- z!X;amaHj7kKD&vks5_gQqp5|RGEjZ1{_){gE&l_be$b7_rR^sG0000rIPF> z>{jivm4AT1CLxKPEQAdS<}eTD||&6{~w?;koW$p=1!9s66=HS^|m z_v_!ce@FN4^$0F7>~{fZrgNL{mJt!(CMJyu5AFMM}sUXjv-TW&$>^2 zzJ)p!6n%0VzAsQh@cEkx`R2xQnz}63eSMff-ddcV9RTFr_D2vJ-*br9|D5>2UtZV* zlziq@d;`XZo8OnqeV;1F(;*(OgYwKM=@Ct`WCKpm4)Qm?i17@$=|L<@;S^j47b*dz z_q>7;3gg4diWv9ZUCzRi7|+zhD-Gbfu)Ps3Uy{St{z?iqK95q0FWp|kS8gxm3qO2^ ztsRF>=~;hO1XTRpc8u>~3>0L9_|Bar+_W}_mzv-gFN2jlS~uzV9I++goI=*E%;tf6 z7SP%6aQ%1o(|C9kWaz>oV8IvPz!*r22KoCN=kvEW7Esp*KX@Gm#=!NC*6nKVr?LJp z^D_ib{PS`ui=uq*u{O5+Weik^s0ibz@R<{E@8*F`Mu>a0GKHr&{zTlSHbr?~e(=uR zy3-MS;il~%BvQb~ugc}?w-j;2D1Q1jv<{7{`y`GG#@YXN6RtJNw?9_Q-5)66C;uDg zj_MM35q8zJ5s@B0{7f0|tIFizZSZvM%zD9a2#i3WF~*=hFunmlfC%Nh>v+ZSe*Bj$ zeddzJkTv~J>HGu z6X1>8-^B!BIPSvW_yl0^&Qb&Z+HfM5b}Rw+-!GGn&ye+3V!(5RxDDKIB=?IMg3Tac z-6Orc^rvm2Ic8H=uV}!Y{<~QB;O>XhBKl4cfhks$%oJQ)CIH}gX7K2h zafajW7V&S-v=1#WiK^))?;E1$d$8{Xq+o2?ciwIp^YojCtDRvR8SQu?9@c7xz(Sl}#}^9ZtOQ`v@#9)aX(=hAC#!g|K+V3E($Y$)xKe7?jq^R! z_Xx8zTPZ)T<+ujSxFN;dMF8J(X>NF(q2_(8yy1)Zo{LoHDGwuOG9ab2bU>o9pQ7sAt91pE^JCo0$Xzq@U1AOH0XF1kAx2!1q0N@7Ro} zT8eY<9fo8kqq%9MgcVAtv+n;)U}kxDr9De)q2lToqd)s)aQop}MzT^7u8nDJqNT)V zQRQWrH*1heV8@5iDN&?6mt@S8QwtT>O3B1*4ZxgT9CYmjT}PS;l$3xm1g^b_p^igb zkrkk+?0qPqI8^g26_?&XG(GR!$uDLSpp=);Qb|kAat`JU3E2JOV{{#=!872lUQ2o` z1K+WUWaW@(sAb>I7II52WBc!a!r{LCl&}5}Ql7g-hnQsoQb|iIDXiHRV9vk=JoT@i z;47cInNR-oF`obRFZup&w_%r6qEe#d24GaUys`ahbo*`s#rZ@t!)PVXX~>;tnl2zd+{e%V`vLAPoX?Z@e3s{*c$|_2Reb%Cr|24v6VJ{ga_M?H zM4}GtC8+f|U3AUXHm#(krNBCyS@7b$u>W_w_&49+`Bkg9WN3&~Az8V!l9e}Yq-MZH z84ur1j58|)$-Z{V)A9&MQqL&?v*nZmOKPPoDFri%IB_wa%V2*y9X*F>-18Dkvf5d& zZXIbrG%t_#?j9ce_lIy&QgK`l-x|fY6I1r**$GT*BL!JSXdO7|v*x_al2a>XX(i3D zl0v}I;C~_?&~dPa;ebQjPg396#;uEU@pFrKqPCq)dph}_M<1c~56>b>F5zc8TR1$F zK#HTz#~6d%)ymSMrOYc@bnd49_!=OTG{ahH*&_u)Ok4e!RzTKHq1~ko3WG} zvV~;%f_!%OC-4nC{Ie~5;v*j><+@K(d!Ut$-K`{j(2-QUu@QvQB)eOfpKz&MeI@EV z>^P?rU<^WO*`ou>^eQQ*M+o81au!=({uOuLxQb`?D+VnOU)Y3&LE>XMF23uc7Omkw z@BcP|f-=N{O9>T}<0mZaH~vU$@hSv{Sa%0kZ`g#K1H;ePon(eAn#Zz;lX1*_<9i4> zdy{um(~Us{@O!oNb+^>Ml!D15pzx=SZ^uhpxa-;l>`H{_Hvz_4_mH;iO2&o<8MR%q zB57zP5kfHkiW_ zFQ9?dSTb`)9S;M>L8+-sA4)PsfNzk})!ovBM7NR%r36X|q!dER@tiR{96^h^v_v4KY;RaHuKF~tM86g}D;Mo>E9j*LF=0cV=wQxsMBfXu2^cc;&=7ZdD z%O)oKJ)08INiHu@Jh$~Z`uqE+{>!Trs>T_0z$KtHzL%WUUQEfUB*KJ5w*YwP#YD$s zj5Nk*$MteuJIO%rUKX#aVDpRBv<}5d$(fIi#u%)9ne-)V8Sd}pj*<}D6R8}oeu2F8 z8;Oq$BVXANW20jf7nd?pd}|&x3=*qc!$`v(Vmb48=}-@8iT&tUCPVda5eP?!l`bQcIgc+~dM$y_sU1`}8LFZ! z#Jmm$hDN9;DWRsei*PuMloHo*85tQSlKfs0j!$>Fgv}F!5eM zNh~1=PuPZ#5(F+??Tz#XQ?Nz@#41R= zJ!d{EF8|=1%(*>ym{m*DdFKD>x#sF?NZLtSTiXZ*HEHST*h!nQu~9OV&4_CwLm`AP zNIAauneK0;L`?d3GlKvn5|hjbAEBf`1%kx#3JIj8F}m{&l6D*+1fha*@~r`+)^s~Q zUc)Z@g{w#}tHeY@gxygJluP}dYL+g)>Z}PEYpZ3Te+Av$-2?&w`Um=vN|K$O&5@xY z0>J?BcpT-&5lWyYV!E1G&`saysfoo9fDt675kP#hnqZuzpj4R1Jc<@pQZWESi6lZw zy0<>b{L8K-<{2V6g)~~jq|7TMY1_0VZIl#@q(#Y%^wHT^OW}gmXQu&EKk60To+BY$qHXSs&XNII^WU|SfBF|;1B$e3S3-GN=WLQz(+45`j6 z@sU}!iiU=lv8)93_4_C;DJDBBn{XtByu3UH2L}m-LbwD`5|kQO`{|!kCZG^giy<@3 z1f&v)$&{%vW@=HiV)TrOzi}WYv*g<`X}ldchKDUHk19I zYMHm{cFI2dT}nRiRqESDh(=OLB$9Zpi|_kLr3hx05l$J`f?z<6%OcE-exxu{vS~3* z1GExxe@X*{n7(f=U9^M^T2kHK%t9UKwS$MrzH$S8FhXNvGa{VIP|G1gW1~bDT?)ob z5FrRCiNd3H9;p#VR{BKC5-Rb zr1vy)O<3`f58lq|v>3@f+cCb6n8^DJ6~KO{niU0wtPMh_s{`RE>i6!%9veR9IVUOs zt%*dUq^72mm0w2VkreWBa|wqdL}O`?vwEtomV$tkc#cCX8lKUQ6i2mN%V|+SDrrsb z1wDy4UY;%^JCEzG-$2p6T7K8sN-(#GVPWX$?c_u2KMISNQn9y&zwCdT9+g74co7Ce zXvtbe5@Tda$yJM&Fu(i~nh(B%((3f{fl>jASjE*GXnqsv8%*Xx((=lVsTvGv)NwXL zQyL(|Q4R5$5TCvIE~=KTp|H4|n!49{ z@wML*DPKk?JD=fLCi{$`p{a?=1H1XahPzHr@M9pztXTUlrgLo65zu%<F_WD^&$tIPBnh6SqnTfd06V=BLF}NiU42(PzgkUAjcl4xX|GN4loAn2mUlk06+*R1Qr1WKqe680{dItN#HXc_6C7Y z;1JLTB&Y2GQ4>Ok7}o&F2~Lw=fG`sz*b~J9T#oY|{9+OKR0x?nfr~%6l~6_&moCiU zH(y&o?~u&{zi#2V+J53~JL!A%LEN$K_e`3P-1B4h9vnsoQ_;Z)!Egpu<#7G=YtTxN zn;GV|t4ojDc1}(!S#8w_wT?Y4njbyY&X(#v270?0sCk6pJ->O+1fN7{Q87*3J?LNt zq0~Hl=Lor(1|c9Ns81+7%%RH9yXi3^`DIA0u-a?sdf`6&;M1%flNk5MuyN(AcJAm zDh8m4y*lqk;%IYUfn2?{JfB*jdgoK3p`u_(G9I&?sitj&v z{``gw8yp-Q7A#oM(9jSN5YQQ%AOtjxtt7}VnBjoKfrbOR@2k%MMT$IK977~7$M)YA za&h2sQBD$XmNV3A{{P=^HP_~g{wF=HVimnko#^=7|L6PtO=r$_iTE696A1tMxcvFa zWpCff&2yQN&%HQYa+mgDOGo*TV=twu<(~IL@>h`kkWV71?L dxqHWQ8-}}lEN*Kj2N!_6=jrO_vd$@?2>?}8ZeIWZ literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MusicPlayer/.icon/32.png b/app/examples/Multimedia/MusicPlayer/.icon/32.png new file mode 100644 index 0000000000000000000000000000000000000000..66d2fcb0726bcd0d1237048f1dcd26019d10980c GIT binary patch literal 272 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv(Ey(iS0D`n|Nn#V|NjRL9B620 z*t+I*1W=H%B*-tA!Qt7BG$3cCr;B5V#`)Nb2YH(fcvvq~F#C9(*OdzsawOGXgZ_rKn`cYgw~BxF|uuv(h4uNCbF(5D4!7I z#S$vQe%{%akN-yKVX?MVr&lz;l{djBF0xe)K3GxeOXgJ_-;6Ov4MUMy2$^uUp z#}JR>Q>R`mYgXWCn|(`nL(}Z79@}5O`ECESiN*W8rcm*Y4@&cEo;-i?FhKV})%oE6 zh3XAExEVsc6ghTFH%z|A%#ieAI^z<#-BaBpKP>zjxpMP0^#h9=PQ>d=i=0iLDwyhi zK-%H(`9A@S_cj-Ho|`-CLc~QwiHrq*w>JM2 z3twmChca#`*FI3`xFf7}PifpU~>F| zc&!Y3xQyUE8&%O?b+vpVhr7J=*4KT0<(@DnCMGFiMuu;F-!z_xu6uH3D;YjpoZ~St z=5FZB;&7&ATrlmcnGSQsd|_^w&&(Z@ zeh}iG<5%X9b(iBa$F|=2CND54 zBqt^{nYNZY`-h4kk4kw)&MRxOb>q4($|v%K{;IYh_c2fgy(CR!+LP%wNYX6r?Qot% zzt9Icb!fcCU|j~s)|Co=1*<*%Z8kcNLZz)@KltnOj}u1y|yh^8f$< literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MusicPlayer/.lang/ca.po b/app/examples/Multimedia/MusicPlayer/.lang/ca.po new file mode 100644 index 00000000..88daf3d6 --- /dev/null +++ b/app/examples/Multimedia/MusicPlayer/.lang/ca.po @@ -0,0 +1,32 @@ +# Catalan translation of MusicPlayer +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the MusicPlayer package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: MusicPlayer\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:12+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FSoundPlayer.form:26 +msgid "Choose a music file..." +msgstr "Trieu un fitxer de música..." + +#: .project:1 FSoundPlayer.form:18 +msgid "Gambas Music Player" +msgstr "Reproductor de música del Gambas" + +#: FSoundPlayer.form:59 +msgid "Open..." +msgstr "Obre..." + diff --git a/app/examples/Multimedia/MusicPlayer/.lang/cs.mo b/app/examples/Multimedia/MusicPlayer/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..09722c2672698486d4a902c24b8c3e28307475d5 GIT binary patch literal 488 zcmYL_&1%~~5XZOqARux|A>@$5+{-G>Ehuh@u-A^LWEp=!LoO?OVk^kI%4%)uH^?Q& z9s;=p(qmqs7dKB4$g^}drFhsM|7GUCJHx!~ZT(~vTf`wzBesc4!bM5!6F0<1;+EJU z9tiey;7T5SSo4&oTvVIrU=k}!(T!bYPjdcCh?Rg<}#An&7` z1rUer*1)x_#qn8@``S#_o&dZgO2m4;WB;>P*UF-zcFU6HvG~Itm4(dJDU7DlRA|2p z`b~ba_PMdH&~TwfdYr3C04G^)JvLlsXka=YW2RQmQ0aN5joZxm|5W*Mk5jXHT*yCn R@NbtawYYe>Up-sr{0mbPg;)Rp literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MusicPlayer/.lang/cs.po b/app/examples/Multimedia/MusicPlayer/.lang/cs.po new file mode 100644 index 00000000..c11a030f --- /dev/null +++ b/app/examples/Multimedia/MusicPlayer/.lang/cs.po @@ -0,0 +1,24 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FSoundPlayer.form:18 +msgid "Gambas Music Player" +msgstr "Hudební přehrávač Gambas" + +#: FSoundPlayer.form:26 +msgid "Choose a music file..." +msgstr "Vyber hudební soubor..." + +#: FSoundPlayer.form:59 +msgid "Open..." +msgstr "Otevřít..." diff --git a/app/examples/Multimedia/MusicPlayer/.lang/es.mo b/app/examples/Multimedia/MusicPlayer/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..3c932549543a3ac582a89491cbc3fd258f3c5329 GIT binary patch literal 453 zcmZ{gy-ve05XZyUk`W{(hLaeP14s;2D5z2EmWZT@`X#}Hn_g3+)UF)2YTp4y9)y*Z z_uyH$bf6O_|8&;*ue-nR=lJl$p*SGUh#TUFh=?j)h*LrkC&Zd?_8jMp(mC-?_p1I$ zna;M)KIOk?&a^HO1k9JE%wQ@D^gPe$i1}EQ5O0fVAvS27)aX~tEDI-Wgr<~Q`H%+9 z$DqSu#C!cP>AGpcdbpC+BWnqZK0NWD$Kr&w0(eOLIJ|gHy4;OKY1zOCRTjc(<3oEG zMbHgm4s{*}Vblm(tsd|9?@6lirO1&DP|SUZf^KJ6jjTseqmZ69`A*}58;3F9`MdV6 z+@@9*6^+|0$dARfy<8MRs$0lr!jx$5hJ!Y%?R1r`DH_IArYBP6KGeq2x=tSp%rg2K X!cu`S*-Wl9Oc3VZU$i{&-{O7&mtJ|I literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MusicPlayer/.lang/es.po b/app/examples/Multimedia/MusicPlayer/.lang/es.po new file mode 100644 index 00000000..5186acbb --- /dev/null +++ b/app/examples/Multimedia/MusicPlayer/.lang/es.po @@ -0,0 +1,24 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 FSoundPlayer.form:18 +msgid "Gambas Music Player" +msgstr "Gambas Music Player" + +#: FSoundPlayer.form:26 +msgid "Choose a music file..." +msgstr "Seleccione un archivo de música..." + +#: FSoundPlayer.form:59 +msgid "Open..." +msgstr "" diff --git a/app/examples/Multimedia/MusicPlayer/.lang/fr.mo b/app/examples/Multimedia/MusicPlayer/.lang/fr.mo new file mode 100644 index 0000000000000000000000000000000000000000..840b337693fa269043e0e38a555b06b72a8bb60f GIT binary patch literal 493 zcmYk2%}T>S5XaXq#2gg_!NY(@*{C;(RSfO629u`rLr}b|>DaEMn|602>J#_^-aP8V z_!3@x5hwLfhyC$iW`>>F|M%wV8>3hyc8MCXM%)rrOo?q`Mr;wU#5(a#uqDPmNcM&#TO_wS(c3z8W`f-!&x~uFeZnv{GWwCPgz<%8f7XYe*v6I Bg6RMN literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MusicPlayer/.lang/fr.po b/app/examples/Multimedia/MusicPlayer/.lang/fr.po new file mode 100644 index 00000000..bedef664 --- /dev/null +++ b/app/examples/Multimedia/MusicPlayer/.lang/fr.po @@ -0,0 +1,35 @@ +# #-#-#-#-# FSoundPlayer.pot (PACKAGE VERSION) #-#-#-#-# +# /home/benoit/gambas/2.0/src/examples/examples/Sound/MusicPlayer/FSoundPlayer.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# #project.pot (PACKAGE VERSION) #-#-#-#-# +# /home/benoit/gambas/2.0/link/share/gambas2/examples/Sound/MusicPlayer/.project +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FSoundPlayer.form:26 +msgid "Choose a music file..." +msgstr "Choisissez un fichier de musique..." + +#: .project:1 FSoundPlayer.form:18 +msgid "Gambas Music Player" +msgstr "Lecteur de musique Gambas" + +#: FSoundPlayer.form:59 +msgid "Open..." +msgstr "Ouvrir..." + diff --git a/app/examples/Multimedia/MusicPlayer/.project b/app/examples/Multimedia/MusicPlayer/.project new file mode 100644 index 00000000..430d9819 --- /dev/null +++ b/app/examples/Multimedia/MusicPlayer/.project @@ -0,0 +1,25 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.90 +Title=Gambas Music Player +Startup=FSoundPlayer +Icon=sound.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.sdl.sound +TabSize=2 +Translate=1 +Language=fr +ExecPath=/home/benoit/gambas/gambas.link/share/gambas/examples/Sound/MusicPlayer/MusicPlayer +Maintainer=fabien +Vendor=Princeton +Address=fabien@arcalis +License=General Public Licence +Systems=mandrake +Menus=Multimedia/Sound +Categories= +Groups=Sound +ExtraDependencies= +ExtraFiles= diff --git a/app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.class b/app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.class new file mode 100644 index 00000000..642a6be4 --- /dev/null +++ b/app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.class @@ -0,0 +1,145 @@ +' Gambas class file + +Static Private $bDoNotMove As Boolean +Private $iLength As Integer + +Private Sub GetMusicLength(sPath As String) As Integer + + Dim sOutput As String + Dim aScan As String[] + + Select Case LCase(File.Ext(sPath)) + + Case "mp3" 'mp3info -p "%S\n" + Exec ["mp3info", "-p", "%S", sPath] To sOutput + Return CInt(sOutput) + + Case "ogg", "flac" + Exec ["ogginfo", sPath] To sOutput + For Each sOutput In Split(sOutput, "\n") + sOutput = Trim(sOutput) + aScan = Scan(sOutput, "*: *m:*s") + If aScan.Count = 3 Then + Return CInt(CInt(aScan[1]) * 60 + CFloat(Replace(aScan[2], ",", ".")) + 0.5) + Endif + Next + + End Select + +Catch + +End + +Public Sub btnOpen_Click() + + If Dialog.OpenFile() Then Return + + lblTitle.Text = File.Name(Dialog.Path) + ' Approximation... + $iLength = GetMusicLength(Dialog.Path) + If $iLength = 0 Then $iLength = 600 + sldPos.MaxValue = $iLength + + Music.Load(Dialog.Path) + btnPlay_Click + +Catch + + Message.Error(Error.Text) + +End + +Public Sub btnPlay_Click() + + timMusic.Enabled = True + Music.Play + +End + +Public Sub btnPause_Click() + + Music.Pause + +End + +Public Sub btnStop_Click() + + Music.Stop + timMusic.Enabled = False + lblPos.Text = "" + sldPos.Value = 0 + +End + +Private Sub FormatTime(iPos As Integer) As String + + Dim iInd As Integer + Dim iVal As Integer + Dim sPos As String + + For iInd = 0 To 2 + + iVal = iPos Mod 60 + iPos = iPos \ 60 + If iInd Then + sPos = Format(iVal, "00") & ":" & sPos + Else + sPos = Format(iVal, "00") + Endif + + Next + + Return sPos + +End + + +Public Sub timMusic_Timer() + + Dim iPos As Integer + Dim sPos As String + + iPos = Music.Pos + + If Not $bDoNotMove Then + Object.Lock(sldPos) + If iPos > sldPos.MaxValue Then + sldPos.MaxValue = sldPos.MaxValue * 2 + Endif + sldPos.Value = iPos + Object.Unlock(sldPos) + Endif + + lblPos.Text = FormatTime(iPos) & " / " & FormatTime($iLength) + +End + +Public Sub sldPos_Change() + + Music.Pos = sldPos.Value + +End + +Public Sub sldPos_MouseDown() + + $bDoNotMove = True + +End + +Public Sub sldPos_MouseUp() + + $bDoNotMove = False + +End + +Public Sub sldVolume_Change() + + Music.Volume = sldVolume.Value / sldVolume.MaxValue + +End + +Public Sub Form_Open() + + Dialog.Path = User.Home + +End diff --git a/app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.form b/app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.form new file mode 100644 index 00000000..e8d6be34 --- /dev/null +++ b/app/examples/Multimedia/MusicPlayer/.src/FSoundPlayer.form @@ -0,0 +1,62 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(41.5714,35.1429,66,16) + Text = ("Gambas Music Player") + Icon = Picture["sound.png"] + Resizable = False + { lblTitle TextLabel + MoveScaled(1,1,59,5) + Font = Font["+2"] + Padding = 4 + Text = ("Choose a music file...") + Alignment = Align.Normal + Border = Border.Plain + } + { sldPos Slider + MoveScaled(1,7,59,4) + MaxValue = 3600 + Step = 2 + } + { timMusic #Timer + #MoveScaled(33,0) + } + { sldVolume Slider + MoveScaled(61,1,4,10) + MaxValue = 128 + PageStep = 8 + Value = 128 + } + { PictureBox1 PictureBox + MoveScaled(61,11,4,4) + Picture = Picture["icon:/small/volume"] + Alignment = Align.Center + } + { HBox1 HBox + MoveScaled(1,11,59,4) + { btnOpen ToolButton + MoveScaled(0,0,12,4) + AutoResize = True + Text = ("Open...") + Picture = Picture["icon:/16/open"] + } + { btnPlay ToolButton + MoveScaled(13,0,4,4) + Picture = Picture["icon:/16/play"] + } + { btnPause ToolButton + MoveScaled(17,0,4,4) + Picture = Picture["icon:/16/pause"] + } + { btnStop ToolButton + MoveScaled(21,0,4,4) + Picture = Picture["icon:/16/stop"] + } + { lblPos Label + MoveScaled(27,0,27,4) + Font = Font["14,Bold"] + Expand = True + Alignment = Align.Right + } + } +} diff --git a/app/examples/Multimedia/MusicPlayer/sound.png b/app/examples/Multimedia/MusicPlayer/sound.png new file mode 100644 index 0000000000000000000000000000000000000000..034a6cfa1adec8a9d80cec81c4f9f7e2ba7f29e7 GIT binary patch literal 3551 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 literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MyWebCam/.lang/ca.po b/app/examples/Multimedia/MyWebCam/.lang/ca.po new file mode 100644 index 00000000..52a59884 --- /dev/null +++ b/app/examples/Multimedia/MyWebCam/.lang/ca.po @@ -0,0 +1,107 @@ +# Catalan translation of MyWebCam +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the MyWebCam package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: MyWebCam\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:12+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Webcam example" +msgstr "Exemple de càmera web" + +#: Form1.class:40 +msgid "Unable to open video device" +msgstr "No es pot obrir el dispositiu de vídeo" + +#: Form1.class:45 +msgid "Stop" +msgstr "Atura" + +#: Form1.form:59 +msgid "Bright" +msgstr "Lluminositat" + +#: Form1.form:70 +msgid "Contrast" +msgstr "Contrast" + +#: Form1.form:87 +msgid "Whiteness" +msgstr "Blancor" + +#: Form1.form:92 +msgid "Hue" +msgstr "Matís" + +#: Form1.form:103 +msgid "Color" +msgstr "Color" + +#: Form1.form:115 +msgid "160x120" +msgstr "-" + +#: Form1.form:121 +msgid "320x240" +msgstr "-" + +#: Form1.form:128 +msgid "176x144" +msgstr "-" + +#: Form1.form:134 +msgid "352x288" +msgstr "-" + +#: Form1.form:140 +msgid "128x96" +msgstr "-" + +#: Form1.form:146 +msgid "640x480" +msgstr "-" + +#: Form1.form:156 +msgid "Tuner frequency:" +msgstr "Freqüència de sintonització:" + +#: Form1.form:162 +msgid "+" +msgstr "-" + +#: Form1.form:168 +msgid "-" +msgstr "-" + +#: Form1.form:174 +msgid "Device Information" +msgstr "Informació del dispositiu" + +#: Form1.form:180 +msgid "Take a shot" +msgstr "Fes una captura" + +#: Form1.form:190 +msgid "Device:" +msgstr "Dispositiu:" + +#: Form1.form:196 +msgid "/dev/video" +msgstr "-" + +#: Form1.form:201 +msgid "Capture" +msgstr "Captura" diff --git a/app/examples/Multimedia/MyWebCam/.lang/cs.mo b/app/examples/Multimedia/MyWebCam/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..b4bcfe918326459ec03e20e613251a028e765cfc GIT binary patch literal 1288 zcmZXSJ&YSg6vrPVgv8-92@pPtAre)zIj`e5W^;+oKJVpB)*qbhLtMq!p0oG7-Z8sl zvv(CGbTlAPBq$>a*9ZwAq)Qswl#xP1gOrw@|2xg;3ZC`mx3ly1|K6MDrx#9tOR#s~ z@4=tJKY;)C92Qvnc_M!N*s=rWu%Q zA;P?4xn^0ltXXbYZd!h18CW(!)^CGc=MKpI^zHc=Xn%1TTWmK>m)Ca6B8&j(cTeZCt^Tam#F%;jhErgue`DeXfb^5}b88 z*Rk<@_)d?F>*iUoac`WTduBs9^cI}|6K})mDmk<=REw2GI8>V4e4&`#ES1SEl(P>@ zRD^Hfd3(b&+soSAu|XkAA-!8+~h zs?JVyYajGj?fPLz-$9cx3~d8`t#o2bv{ zDjw{6)Qe?5Qo?AVXDSv)r3*9xY5GL<2XZP@Ca1Fq+i_@AtkRU)iQZEKs3-Ds#Kn-Ehh$aa`7}N*q5P z=wTR-JW=k4CWk@)|JPYOyl*0n$?P01(}^@xmB~UP^4{^t?5r@g1NrFp!{vdB50|tf zAN^pTJ!%b8bGVFYTSei7#T@DlAKs7Do=6cqRTB#2Ff=^F$pSMBJ!-1xKnZQsf`tr= d9}6B0R8pH?Od>fHU#R{>PF1o`Rj6`;{{RZ93C;ij literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MyWebCam/.lang/cs.po b/app/examples/Multimedia/MyWebCam/.lang/cs.po new file mode 100644 index 00000000..72aa7ce7 --- /dev/null +++ b/app/examples/Multimedia/MyWebCam/.lang/cs.po @@ -0,0 +1,112 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Webcam example" +msgstr "Příklad webkamery" + +#: Form1.class:36 +msgid "Unable to open video device" +msgstr "Nelze otevřít zařízení videa" + +#: Form1.class:41 +msgid "Stop" +msgstr "-" + +#: Form1.form:54 +msgid "Bright" +msgstr "Světlost" + +#: Form1.form:65 +msgid "Contrast" +msgstr "Kontrast" + +#: Form1.form:82 +msgid "Whiteness" +msgstr "Bělost" + +#: Form1.form:87 +msgid "Hue" +msgstr "Odstín" + +#: Form1.form:98 +msgid "Color" +msgstr "Barva" + +#: Form1.form:110 +msgid "1024x768" +msgstr "-" + +#: Form1.form:110 +msgid "128x96" +msgstr "-" + +#: Form1.form:110 +msgid "160x120" +msgstr "-" + +#: Form1.form:110 +msgid "176x144" +msgstr "-" + +#: Form1.form:110 +msgid "320x240" +msgstr "-" + +#: Form1.form:110 +msgid "352x288" +msgstr "-" + +#: Form1.form:110 +msgid "640x480" +msgstr "-" + +#: Form1.form:111 +msgid "ComboBox1" +msgstr "-" + +#: Form1.form:116 +msgid "Size" +msgstr "Velikost" + +#: Form1.form:126 +msgid "Tuner frequency:" +msgstr "Ladit frekvenci:" + +#: Form1.form:132 +msgid "+" +msgstr "-" + +#: Form1.form:138 +msgid "-" +msgstr "-" + +#: Form1.form:144 +msgid "Device Information" +msgstr "Informace zažízení" + +#: Form1.form:150 +msgid "Take a shot" +msgstr "Udělej snímek" + +#: Form1.form:160 +msgid "Device:" +msgstr "Zařízení:" + +#: Form1.form:166 +msgid "/dev/video" +msgstr "-" + +#: Form1.form:171 +msgid "Capture" +msgstr "Získat" diff --git a/app/examples/Multimedia/MyWebCam/.lang/es.mo b/app/examples/Multimedia/MyWebCam/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..d09fcca5269105b47140ef55d565978c58275314 GIT binary patch literal 1177 zcmbu7J#Q015QY~B1k6Vue53%xb}7&$wquNQgeXURNi6KxN}M35*w|}(;j-)Otu04_ zzkq1?6A_|;h7O5_f`XC?QBv}uG*rA3P72U6((H4yH#ht4%zYXidd6TE;FsX<;g{i$ z4q$dJf$RQS0*`1DZQp-4IHESNohVx2IZ(Z6L$N0P!%-;?cL%tratd^?Fsan-r z!FTl1D+(ulem4wOyh_z827IaNS4vm!RhPq58M?i?6*`WDGuGz|YvnRu4k{s^2`fRV zJQoy;E8*(uEK+s5C)(1hOR?$mazNpMZIz`Qq8fR>G zA;QUlo9sqH=^MP!5jK|Y=30HhE9~cymX@?$s2irKblc~J4dqh*Ke#9okdn+!{4+br zlBqS++m{+Wk9bqXT@x#(`i2$vpM6$08atAvVG(Q!%X?bzmT`s!Eu}@%SX7|FHfXR) d_^h-^wP?6v!z#=Fu20#&|Ht`ASk)9w_6uc#^+*5! literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/MyWebCam/.lang/es.po b/app/examples/Multimedia/MyWebCam/.lang/es.po new file mode 100644 index 00000000..46ab59c7 --- /dev/null +++ b/app/examples/Multimedia/MyWebCam/.lang/es.po @@ -0,0 +1,95 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: Form1.class:14 +msgid "Capture" +msgstr "Captura" + +#: Form1.class:39 +msgid "Unable to open video device" +msgstr "Imposible abrir el dispositivo de video" + +#: Form1.class:44 +msgid "Stop" +msgstr "Detener" + +#: Form1.class:292 +msgid "Bright" +msgstr "Brillante" + +#: Form1.class:303 +msgid "Contrast" +msgstr "Contraste" + +#: Form1.class:320 +msgid "Whiteness" +msgstr "Claridad" + +#: Form1.class:325 +msgid "Hue" +msgstr "Tono" + +#: Form1.class:336 +msgid "Color" +msgstr "Color" + +#: Form1.class:348 +msgid "160x120" +msgstr "160x120" + +#: Form1.class:354 +msgid "320x240" +msgstr "320x240" + +#: Form1.class:361 +msgid "176x144" +msgstr "176x144" + +#: Form1.class:367 +msgid "352x288" +msgstr "352x288" + +#: Form1.class:373 +msgid "128x96" +msgstr "128x96" + +#: Form1.class:379 +msgid "640x480" +msgstr "640x480" + +#: Form1.class:389 +msgid "Tuner frequency:" +msgstr "Afinador de frecuencia:" + +#: Form1.class:395 +msgid "+" +msgstr "+" + +#: Form1.class:401 +msgid "-" +msgstr "-" + +#: Form1.class:407 +msgid "Device Information" +msgstr "Información del dispositivo" + +#: Form1.class:413 +msgid "Take a shot" +msgstr "Tomar una foto" + +#: Form1.class:423 +msgid "Device:" +msgstr "Dispositivo:" + +#: Form1.class:429 +msgid "/dev/video" +msgstr "/dev/video" diff --git a/app/examples/Multimedia/MyWebCam/.project b/app/examples/Multimedia/MyWebCam/.project new file mode 100644 index 00000000..831cb11a --- /dev/null +++ b/app/examples/Multimedia/MyWebCam/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.5.90 +Title=Webcam example +Startup=Form1 +Icon=camera.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.v4l +TabSize=2 +Translate=1 +Language=fr +ExecPath=/home/dcampos/MyWebCam/MyWebCam +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/Multimedia/MyWebCam/.src/Form1.class b/app/examples/Multimedia/MyWebCam/.src/Form1.class new file mode 100644 index 00000000..8f1faf2a --- /dev/null +++ b/app/examples/Multimedia/MyWebCam/.src/Form1.class @@ -0,0 +1,203 @@ +' Gambas class file + +Private hWebcam As VideoDevice +Private OnSet As Boolean +Private Fps As Date +Private nFps As Integer + +Public Sub Button1_Click() + + + Dim num As Integer + Dim Buf As String + Dim sSize As String + + If hWebCam Then + Button1.Caption = ("Capture") + Bright.Enabled = False + Contrast.Enabled = False + Hue.Enabled = False + Whiteness.Enabled = False + Colour.Enabled = False + cmbSize.Enabled = False + FreqUp.Enabled = False + FreqDown.Enabled = False + TxtDevice.Enabled = True + BtnTakeShot.Enabled = False + Button2.Enabled = False + hWebCam = Null + Tmr.Enabled = False + Return + End If + + + Try hWebCam = New VideoDevice(TxtDevice.Text) + If Error Then + Message.Error(("Unable to open video device")) + Return + End If + hWebCam.Source = hWebCam.TV + hWebCam.PAL + + Button1.Caption = ("Stop") + BtnTakeShot.Enabled = True + Button2.Enabled = True + Bright.Enabled = True + Contrast.Enabled = True + Hue.Enabled = True + Whiteness.Enabled = True + Colour.Enabled = True + cmbSize.Enabled = True + sSize = CStr(hWebCam.Width) & "x" & CStr(hWebCam.Height) + If cmbSize.Find(sSize) < 0 Then cmbSize.Add(sSize) + Try cmbSize.Text = sSize + FreqUp.Enabled = True + FreqDown.Enabled = True + TxtDevice.Enabled = False + OnSet = True + Bright.Value = hWebcam.Bright + Contrast.Value = hWebcam.Contrast + Hue.Value = hWebCam.Hue + Whiteness.Value = hWebCam.Whiteness + Colour.Value = hWebCam.Color + LblFreq.Text = "Tuner frequency: " & hWebCam.Tuner.Frequency + + Wait 0.001 + OnSet = False + Tmr.Delay = 10 + Tmr.Enabled = True + Me.Caption = hWebCam.Name + Fps = Now() + nFps = 0 + + +End + + + + + +Public Sub Bright_Change() + + If OnSet Then Return + hWebCam.Bright = Bright.Value + +End + +Public Sub Contrast_Change() + + If OnSet Then Return + hWebCam.Contrast = Contrast.Value + +End + +Public Sub Whiteness_Change() + + If OnSet Then Return + hWebCam.Whiteness = Whiteness.Value + +End + +Public Sub Colour_Change() + + If OnSet Then Return + hWebcam.Color = Colour.Value + +End + +Public Sub Hue_Change() + + If OnSet Then Return + hWebCam.Hue = Hue.Value + +End + +Public Sub cmbSize_Click() + + Dim aSize As String[] + + aSize = Split(cmbSize.Text, "*x*") + hWebcam.Resize(CInt(aSize[0]), CInt(aSize[1])) + +End + + +Public Sub Tmr_Timer() + + Dim T1 As Date + Dim sBuf As String + Dim hPict As Picture + + Tmr.Enabled = False + + 'Try PictureBox1.Picture = hWebCam.Picture + Draw.Begin(dwgVideo) + hPict = hWebCam.Image.Picture + Draw.Picture(hPict, (dwgVideo.W - hPict.W) \ 2, (dwgVideo.H - hPict.H) \ 2) + Draw.End + + If Not Error Then + nFps = nFps + 1 + T1 = Now() - Fps + If Second(T1) >= 1 Then + Me.Caption = hWebCam.Name & " (" & nFps & " fps)" + Fps = Now() + nFps = 0 + End If + End If + Tmr.Enabled = True + +End + + +Public Sub Form_Close() + + Tmr.Enabled = False + hWebCam = Null + +End + + + + + +Public Sub FreqUP_Click() + + hWebCam.Tuner.Frequency = hWebCam.Tuner.Frequency + 5 + LblFreq.Text = "Tuner frequency: " & hWebCam.Tuner.Frequency + +End + + + +Public Sub FreqDown_Click() + + hWebCam.Tuner.Frequency = hWebCam.Tuner.Frequency - 5 + LblFreq.Text = "Tuner frequency: " & hWebCam.Tuner.Frequency + +End + +Public Sub Button2_Click() + + Dim sCad As String + + sCad = "Device Bus: " & hWebCam.Bus & "\n" + sCad = sCad & "Device Driver: " & hWebCam.Driver & " Version: " & hWebCam.Version & "\n" + sCad = sCad & "Device Name: " & hWebCam.Name & "\n" + sCad = sCad & "Max.Resolution: " & hWebCam.MaxWidth & "x" & hWebCam.MaxHeight & "\n" + sCad = sCad & "Min. Resolution: " & hWebCam.MinWidth & "x" & hWebCam.MinHeight & "\n" + + Message.Info(sCad) + +End + +Public Sub BtnTakeShot_Click() + + Try hWebCam.Save(User.Home & "/webcam_shot.png") + If Not Error Then Message.Info("Image saved as " & User.Home & "/webcam_shot.png") +End + +Public Sub Panel2_MouseDown() + + + +End diff --git a/app/examples/Multimedia/MyWebCam/.src/Form1.form b/app/examples/Multimedia/MyWebCam/.src/Form1.form new file mode 100644 index 00000000..53e3fecb --- /dev/null +++ b/app/examples/Multimedia/MyWebCam/.src/Form1.form @@ -0,0 +1,122 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(38,2.4286,76,90) + Resizable = False + { dwgVideo DrawingArea + MoveScaled(0,0,75,51) + Background = &H000000& + Cached = True + } + { Tmr #Timer + #MoveScaled(1.1429,20.5714) + Delay = 10 + } + { Panel2 Panel + MoveScaled(0,52,75,37) + { Panel7 Panel + MoveScaled(1,0,74,15) + Border = Border.Raised + { Label1 Label + MoveScaled(2,1,10,3) + Text = ("Bright") + } + { Bright Slider + MoveScaled(13,1,25,3) + Enabled = False + MaxValue = 65535 + } + { Label2 Label + MoveScaled(2,5,9,3) + Text = ("Contrast") + } + { Contrast Slider + MoveScaled(13,5,25,3) + Enabled = False + MaxValue = 65535 + } + { Whiteness Slider + MoveScaled(13,9,25,3) + Enabled = False + MaxValue = 65535 + } + { Label3 Label + MoveScaled(2,9,11,3) + Text = ("Whiteness") + } + { Label5 Label + MoveScaled(40,1,7,4) + Text = ("Hue") + } + { Hue Slider + MoveScaled(47,1,25,3) + Enabled = False + MaxValue = 65535 + } + { Label4 Label + MoveScaled(40,5,6,4) + Text = ("Color") + } + { Colour Slider + MoveScaled(47,5,25,3) + Enabled = False + MaxValue = 65535 + } + { cmbSize ComboBox + MoveScaled(47,9,25,4) + ReadOnly = True + List = [("128x96"), ("160x120"), ("176x144"), ("320x240"), ("352x288"), ("640x480"), ("1024x768")] + Text = ("ComboBox1") + } + { Label7 Label + MoveScaled(40,9,6,4) + Text = ("Size") + } + } + { Panel3 Panel + MoveScaled(1,16,74,13) + Border = Border.Raised + { LblFreq Label + MoveScaled(1.1429,1.1429,36,4) + Text = ("Tuner frequency:") + } + { FreqUP Button + MoveScaled(1.1429,5.1429,6.4286,5.1429) + Enabled = False + Text = ("+") + } + { FreqDown Button + MoveScaled(9.1429,5.1429,6.4286,5.1429) + Enabled = False + Text = ("-") + } + { Button2 Button + MoveScaled(44,7,29.1429,5.1429) + Enabled = False + Text = ("Device Information") + } + { BtnTakeShot Button + MoveScaled(44,1,29.1429,5.1429) + Enabled = False + Text = ("Take a shot") + } + } + { Panel1 Panel + MoveScaled(1,30,74,7) + Border = Border.Raised + { Label6 Label + MoveScaled(1.1429,1.1429,10.4286,4) + Text = ("Device:") + } + { TxtDevice TextBox + MoveScaled(11,1,32,5) + Expand = True + Text = ("/dev/video") + } + { Button1 Button + MoveScaled(44,1,29,5) + Text = ("Capture") + } + } + } +} diff --git a/app/examples/Multimedia/MyWebCam/camera.png b/app/examples/Multimedia/MyWebCam/camera.png new file mode 100644 index 0000000000000000000000000000000000000000..84b228200af8f4e1cc179f2cb6d2ae75e404cf5c GIT binary patch literal 5363 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 literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/WaveGenerator/.directory b/app/examples/Multimedia/WaveGenerator/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Multimedia/WaveGenerator/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Multimedia/WaveGenerator/.icon.png b/app/examples/Multimedia/WaveGenerator/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a778a55df6f9b359f982fb7cfa071d0720a555bc GIT binary patch literal 4098 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 A-T(jq literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/WaveGenerator/.project b/app/examples/Multimedia/WaveGenerator/.project new file mode 100644 index 00000000..5bb10468 --- /dev/null +++ b/app/examples/Multimedia/WaveGenerator/.project @@ -0,0 +1,13 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.8.90 +Title=WaveGenerator +Startup=FMain +Icon=audio-headphones.png +Version=3.8.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.media +TabSize=2 +Packager=1 diff --git a/app/examples/Multimedia/WaveGenerator/.src/FMain.class b/app/examples/Multimedia/WaveGenerator/.src/FMain.class new file mode 100644 index 00000000..6803f17e --- /dev/null +++ b/app/examples/Multimedia/WaveGenerator/.src/FMain.class @@ -0,0 +1,102 @@ +' Gambas class file + +Private $bPlay As Boolean +Private $hAudio As MediaControl +Private $hConv As MediaControl +Private $hPlayer As MediaPipeline +Private $hOutput As MediaControl +Private $hFilter As MediaControl +Private $hEncoder As MediaControl + +Public Sub Form_Open() + + ' audiotestsrc is-live=1 ! + ' audio / x - raw, channels = 2, rate = 48000!flacenc!filesink + ' location = generated.flac + + $hPlayer = New MediaPipeline As "Pipeline" + + $hAudio = New MediaControl($hPlayer, "audiotestsrc") + $hAudio["is-live"] = True + + $hFilter = New MediaControl($hPlayer, "audio/x-raw,channels=2,rate=48000") + 'Print $hFilter["caps"] + + '$hEncoder = New MediaControl($hPlayer, "flacenc") + + + $hConv = New MediaControl($hPlayer, "audioconvert") + $hOutput = New MediaControl($hPlayer, "autoaudiosink") + ' + + '$hOutput = New MediaControl($hPlayer, "filesink") + '$hOutput["location"] = User.Home &/ "output.flac" + + '$hAudio.LinkTo($hConv) + '$hConv.LinkTo($hOutput) + + Media.Link($hAudio, $hFilter, $hConv, $hOutput) + + SetFreq(440) + +End + +Public Sub Form_Close() + + $hPlayer.Stop + +End + + +Public Sub btnPlay_Click() + + $bPlay = Not $bPlay + + If $bPlay Then + $hPlayer.Play + Else + $hPlayer.Pause + Endif + +End + +Private Sub SetFreq(fFreq As Float) + + $hAudio["freq"] = fFreq + Object.Lock(sldFrequency) + sldFrequency.Value = (Log(fFreq) - Log(20)) / (Log(20000) - Log(20)) * 1000 + Object.Unlock(sldFrequency) + lblFrequency.Text = Format(fFreq, "0.000") & " Hz" + +End + +Public Sub sldFrequency_Change() + + SetFreq(Exp(Log(20) + (Log(20000) - Log(20)) * sldFrequency.Value / 1000)) + +End + +Public Sub sldVolume_Change() + + $hAudio["volume"] = sldVolume.Value / 1000 + +End + +Public Sub cmbType_Click() + + $hAudio["wave"] = cmbType.Index + +End + +Public Sub Pipeline_State() + + Debug Last.State + +End + + +Public Sub Pipeline_End() + + Debug + +End diff --git a/app/examples/Multimedia/WaveGenerator/.src/FMain.form b/app/examples/Multimedia/WaveGenerator/.src/FMain.form new file mode 100644 index 00000000..d2c78583 --- /dev/null +++ b/app/examples/Multimedia/WaveGenerator/.src/FMain.form @@ -0,0 +1,61 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,62,20) + Text = ("Wave generator") + Icon = Picture["audio-headphones.png"] + Arrangement = Arrange.Vertical + Spacing = True + Margin = True + { Panel1 HBox + MoveScaled(2,2,59,4) + { Label1 Label + MoveScaled(0,0,11,4) + Text = ("Frequency") + } + { sldFrequency Slider + MoveScaled(14,0,44,4) + Expand = True + MaxValue = 1000 + } + } + { Panel2 HBox + MoveScaled(2,7,59,4) + { Label2 Label + MoveScaled(0,0,11,4) + Text = ("Volume") + } + { sldVolume Slider + MoveScaled(14,0,44,4) + Expand = True + MaxValue = 1000 + Value = 800 + } + } + { Panel5 Panel + MoveScaled(31,12,13,1) + Expand = True + } + { Panel3 HBox + MoveScaled(2,14,58,4) + Spacing = True + { cmbType ComboBox + MoveScaled(0,0,27,4) + ReadOnly = True + List = [("Sine"), ("Square"), ("Saw"), ("Triangle"), ("Silence"), ("White uniform noise"), ("Pink noise"), ("Sine table"), ("Periodic ticks"), ("White gaussian noise"), ("Red brownian noise"), ("Blue noise"), ("Violet noise")] + } + { lblFrequency Label + MoveScaled(27,0,13,4) + Text = ("440 Hz") + Alignment = Align.Right + } + { Panel4 Panel + MoveScaled(28,0,3,4) + Expand = True + } + { btnPlay Button + MoveScaled(41,0,16,4) + Text = ("Play / Stop") + } + } +} diff --git a/app/examples/Multimedia/WaveGenerator/audio-headphones.png b/app/examples/Multimedia/WaveGenerator/audio-headphones.png new file mode 100644 index 0000000000000000000000000000000000000000..7d16771fa0e36a2a2e8b5b5957bf207f8d438d18 GIT binary patch literal 2578 zcmV+t3hniYP)mQn? z`C&-c_ejUN_?-fCl**V>)&V7R<{+4x>(e-|>FnxSe7>cHG=Q;K_9<{g6tky*a|Ema za4MBBzNzlYcDogNt2JnqulA#L+gBPu4A}5ZgHA~YFyJW$Jj=SP1lG?k#hM`A4Y99w z06Ks$00|%$z<2yXIBjx-T@s`(mlV3Pe$tUPK4TvTl z5~)1w2Gd_$y~zgvOdih4mTJXL4L%06BoyO=Z{6|I68}j6iUAh@WT*3vDgnkh0BXsS z;(uSsJlB;vdT8CcU30Fx{+^2$FCGd6gM|Qix|5G-`;_1CGgvhwY8XFHJ2oH=v2v9XaB78Tz7@WX3UYTNbRf%nX+swzF*Ne&_R z*|TS5Rn@!0+E*CvX;a&^>$TmvGF{&LGd7B4QQvLF3$z>SkZ z7r}~(ij7%WS%3Ka^Uu-H&@ic~si`B~$z6%&%$YNF-n@Bj1qB7rb^ZRAUV3Th>z9=) zR{{Wb?%a8}y1JS+Y}jxTfYXOGq&l_!pWc0GW4 z0FR|R*_gHgSXf?O-h9ig^Sy0tZJS+M*2Uc1+|b&!Yw5Y?o_qW5Wy{J8)B4n3;vf9Z zweidu`s~zh~&5zea z_w0R(w!iW+H8(d?X=&+<($Z1@KvQF5!>NW-lsGZzSwJe)DukE-05vr=SigS#rxPbm zM3yVd0n=bvrb-xoN4m2IaA)kBMIsT$>+!;I9GRV+jn%7HAFZjW0RW7jFky^kT77Qb zk^*pSCul^Cd;nTeQPCd@3JNB%*q$yC!4~!y^laKStS5;TlTt!T z2_d*9rNe}BE5hM0EX#rr0#U<&<2aB~QZnb60KkYwi3<+Sc~9XYkq9{FV2nZ6bpYtuDY*UuYi}b>)6mh;0V0BBSqKCI;M^i9CD8Z!A+rD&2Drr`I490IEXzV95`mO5o(lme zj*q`=5xj8WLQz{=8zUm*<>i5h5Q#)!Sr(b53DpfCE1C0506;`8(=@veS<52MISYrw z@&6$d3NZjn;^Qyt0HxHdU@!8}>QX&SU=hU5S5!9C7TMA9nuz71aqR}V}!+>R3&~+U`*gys!0svT7C=5k}Xfz7W zIa-=qux!&+6}XyRo_aNw_o@q>_CmChLGcG>HhDbLhGb!!U+)o)G{H6Q*IH zv$GRI2vGNnR+l9|5P+us0E7@dYXO1!kIy&&rfI-5<7-wgfCQZz48T9pd!y3>z%UG0 zrUkm3K*j;EZJW5o$uNv$00_Xp1_SUB5?1**0M0ptBZ!oWy{MJ30F>i6j0+ASL^1#j z-9;V{z@PfyiUANpKspk!J40sx=a~S&5yI{+SaJXW@Y-Mi0N~z40C8i|k?~96jP8K$ zn;^TVbNc~!F!7Ip0K6B2AY&asoO3wRfszX7eyuH20Z7NTVaKX3grEcf4+Gde8~||$ zS)B97ZQEu-2uP{mNT)x5On6${69Z#R*|q~=+a!d5ZQIN+jAzk(?C!0DrOZ-F!ke9)jow#)pB~D_2*iM$N>u7ClE%f{S-^_YRNeKX8 z=Bycum)==&(B~Ul0bn~a>q+r60NZ^&-%|?;Z#k4ZA-5O+mMvSBIuAv+rQ=PwtJ9*Q o@5E~iqZ-wyMm4HYjVg`$FEg;6C=gT&f&c&j07*qoM6N<$f>E&KaR2}S literal 0 HcmV?d00001 diff --git a/app/examples/Multimedia/WebCam/.directory b/app/examples/Multimedia/WebCam/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Multimedia/WebCam/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Multimedia/WebCam/.icon.png b/app/examples/Multimedia/WebCam/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..97c27e7ffd7533ed15bbc52401313fe66e6169e2 GIT binary patch literal 4272 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 SuD5Cc0000= 3 Then + frame_rate.Text = Format($fps / 3, "#.00 fps") + $fps = 0 + $fps_now = at + Endif + + currentTime.Text = Format(at, $date_format) + Try pic.Picture = $device.Image.Picture + If Error Then + Message("Can't recover picture from Camera!") + Else + Wait + Try Refresh.Delay = refrate.Value + Try Refresh.Start + Endif + +End + +Public Sub picture_size_Click() + + Select picture_size.Index + Case 0 + $device.Resize(160, 120) + Case 1 + $device.Resize(320, 240) + Case 2 + $device.Resize(640, 480) + + End Select + ResizeDevice() + +End + +Public Sub slider_bright_Change() + + $device.Bright = slider_bright.Value + +End + +Public Sub slider_contrast_Change() + + $device.Contrast = slider_contrast.Value + +End + +Public Sub slider_colour_Change() + + $device.Color = slider_colour.Value + +End + +Public Sub hue_Change() + + $device.Hue = slider_hue.Value + +End + +Public Sub whiteness_Change() + + $device.Whiteness = slider_whiteness.Value + +End + +Public Sub refrate_Change() + + Refresh.Stop + Refresh.Delay = refrate.Value + Refresh.Start + +End + +Public Sub button_hide_Click() + + tools.Hide() + ResizeDevice() + +End + +Public Sub button_close_Click() + + button_hide_Click() + Refresh.Stop + Wait 0.2 + Refresh.Stop + Me.Hide + +End + +Public Sub button_pause_Click() + + button_pause.Hide + button_play.Show + Refresh.Stop + Wait + Refresh.Stop + frame_rate.Text = "** Paused **" + +End + +Public Sub button_play_Click() + + button_pause.Show + button_play.Hide + Refresh.Start + +End + +Public Sub button_snap_Click() + + Dialog.Filter = ["*", "All files", "*.desktop", "Desktop files"] + + If Dialog.SaveFile() Then Return + $device.Save(Dialog.Path) + Message("Saved as (" & Dialog.Path & ")") + +End + +Public Sub buttonSettings_Click() + + If tools.Visible Then + tools.Hide + Else + tools.Show + Endif + ResizeDevice() + + Select $device.Width + Case 160 + picture_size.Index = 0 + Case 320 + picture_size.Index = 1 + Case 640 + picture_size.Index = 2 + End Select + +End + +Public Sub button_reset_Click() + + slider_colour.Value = $device.ColorDefault + slider_bright.Value = $device.BrightDefault + slider_contrast.Value = $device.ContrastDefault + slider_whiteness.Value = $device.WhitenessDefault + slider_hue.Value = $device.HueDefault + +End + +Public Sub Form_Close() + + Refresh.Stop + $device.Close + $device = Null + +End diff --git a/app/examples/Multimedia/WebCam/.src/FDevice.form b/app/examples/Multimedia/WebCam/.src/FDevice.form new file mode 100644 index 00000000..b3530bfb --- /dev/null +++ b/app/examples/Multimedia/WebCam/.src/FDevice.form @@ -0,0 +1,151 @@ +# Gambas Form File 3.0 + +{ Form Form + Move(0,0,408,726) + #Scaled = False + Font = Font["Bitstream Vera Sans"] + Icon = Picture["camera.png"] + Resizable = False + Border = False + { outer Panel + Move(24,12,348,666) + Background = &HBFFFFF& + Foreground = &H000000& + Border = Border.Sunken + { container Panel + Move(36,18,276,240) + Background = Color.Background + Border = Border.Sunken + { pic PictureBox + Move(42,72,186,144) + Alignment = Align.Center + } + { title Panel + Move(12,6,240,32) + Background = Color.ButtonForeground + { devName Label + Move(40,-1,186,18) + Font = Font["Bitstream Vera Sans Mono,-1"] + Foreground = Color.TextBackground + Text = ("devName") + Alignment = Align.Left + } + { currentTime Label + Move(40,17,174,14) + Font = Font["Bitstream Vera Sans Mono,-2"] + Background = Color.Foreground + Foreground = Color.TextBackground + Text = ("currentTime") + Alignment = Align.Left + } + { buttonSettings Button + Move(4,4,30,24) + Picture = Picture["settings.png"] + } + } + } + { Refresh #Timer + #Move(224,175) + } + { tools Panel + Move(21,280,276,270) + Visible = False + Font = Font["-1"] + Background = Color.Background + Border = Border.Sunken + { Label1 Label + Move(14,14,70,21) + Text = ("Picture Size") + } + { picture_size ComboBox + Move(98,14,119,21) + ReadOnly = True + List = [("160 x 120"), ("320 x 240"), ("640 x 480")] + Text = ("320 x 240") + } + { slider_bright Slider + Move(98,35,112,21) + } + { Label2 Label + Move(14,35,70,21) + Text = ("Brightness") + } + { slider_contrast Slider + Move(98,56,112,21) + } + { Label3 Label + Move(14,56,70,21) + Text = ("Contrast") + } + { slider_colour Slider + Move(98,77,112,21) + } + { Label4 Label + Move(14,77,70,21) + Text = ("Colour") + } + { slider_hue Slider + Move(98,98,112,21) + } + { Label5 Label + Move(14,98,70,21) + Text = ("Hue") + } + { slider_whiteness Slider + Move(98,119,112,21) + } + { Label6 Label + Move(14,119,70,21) + Text = ("Whiteness") + } + { button_hide Button + Move(14,189,70,28) + Text = ("&Hide") + Picture = Picture["icon:/16/apply"] + } + { button_close Button + Move(14,224,70,28) + Text = ("&Close") + Picture = Picture["icon:/16/close"] + } + { Label7 Label + Move(14,140,84,21) + Text = ("Refresh Rate") + } + { refrate Slider + Move(98,140,112,21) + MinValue = 20 + MaxValue = 1999 + Value = 200 + } + { button_pause Button + Move(91,189,70,28) + Text = ("&Pause") + Picture = Picture["icon:/16/pause"] + } + { button_play Button + Move(91,189,70,28) + Visible = False + Text = ("&Play") + Picture = Picture["icon:/16/play"] + } + { button_snap Button + Move(91,224,70,28) + Text = ("&Snap") + Picture = Picture["icon:/16/camera"] + } + { frame_rate Label + Move(105,161,96,18) + Font = Font["-1"] + Foreground = Color.SelectedBackground + Text = ("0.00 fps") + } + { button_reset Button + Move(168,189,70,28) + Text = ("&Reset") + Picture = Picture["icon:/16/apply"] + Default = True + } + } + } +} diff --git a/app/examples/Multimedia/WebCam/.src/FMain.class b/app/examples/Multimedia/WebCam/.src/FMain.class new file mode 100644 index 00000000..f5045476 --- /dev/null +++ b/app/examples/Multimedia/WebCam/.src/FMain.class @@ -0,0 +1,126 @@ +' Gambas class file + +' +' WebCam +' Demo program to show off the Gambas V4L module +' (c) Gareth Bult, Encryptec Ltd 2009 +' +' License: as-is +' +Private $windows As Form[] + +Public Sub ScanDevices() + + Dim i As Integer + Dim dev As VideoDevice + Dim myWin As Form + Dim count As Integer + Dim item As Menu + Dim status As String + Dim sDevice As String + + $windows = New Form[] + count = 0 + Me.Show + + status = "Searching: " + For i = 0 To 20 + sDevice = "/dev/video" & Str(i) + Print sDevice + Try dev = New VideoDevice(sDevice) + If Not Error Then + status &= "!" + myWin = New FDevice(dev, count) + $windows.Add(myWin) + item = New Menu(tooltray_popup) As "Camera" + item.Text = dev.Card + item.Picture = Picture["icon:/16/camera"] + item.Tag = i + count += 1 + Else + status &= "." + Endif + Wait 0.1 + text_status.Text = status + Next + text_status.Text = "Found (" & Str($windows.Count) & ") devices" + Wait 2 + Me.Hide + + For Each myWin In $windows + myWin.Show + Next + +End + +Public Sub menu_scan_Click() + + Dim item As Menu + Dim items As Menu[] + + While $windows.Count > 0 + $windows.Pop().Close + Wend + $windows.Clear + + items = New Menu[] + For Each item In tooltray_popup.Children + If item.Tag <> "X" + items.Add(item) + Endif + Next + + For Each item In items + item.Delete + Next + + ScanDevices() + +End + +Public Sub Camera_Click() + + $windows[Last.Tag].Show + $windows[Last.Tag].SetFocus + +End + +Public Sub tooltray_Menu() + + tooltray_popup.Popup + +End + +Public Sub menu_quit_Click() + + While $windows.Count > 0 + $windows.Pop().Close + Wend + Me.Close + tooltray.Hide + +End + +Public Sub menu_save_Click() + + Dim mywin As FDevice + + For Each myWin In $windows + myWin.Save() + Next + Settings.Save + Message("Settings Saved!") + +End + +Public Sub Form_Open() + + Me.Center + +End + +Public Sub Form_Activate() + + If Not $windows Then ScanDevices + +End diff --git a/app/examples/Multimedia/WebCam/.src/FMain.form b/app/examples/Multimedia/WebCam/.src/FMain.form new file mode 100644 index 00000000..bf61c8ec --- /dev/null +++ b/app/examples/Multimedia/WebCam/.src/FMain.form @@ -0,0 +1,68 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,41,15) + Background = Color.SelectedBackground + Icon = Picture["camera.png"] + Border = False + Arrangement = Arrange.Fill + Margin = True + Padding = 4 + { tooltray_popup Menu + Tag = "X" + { Menu3 Menu + Text = ("WebCam Options") + Enabled = False + Tag = "X" + } + { menu_scan Menu + Text = ("&Scan for Devices") + Picture = Picture["icon:/16/find"] + Tag = "X" + } + { menu_save Menu + Text = ("Save Settings") + Picture = Picture["icon:/16/floppy"] + Tag = "X" + } + { menu_quit Menu + Text = ("&Quit") + Picture = Picture["icon:/16/quit"] + Tag = "X" + } + { Menu1 Menu + Tag = "X" + } + } + { Panel1 Panel + MoveScaled(0.7143,0,39,13) + Background = Color.LightBackground + Arrangement = Arrange.Vertical + Margin = True + Border = Border.Sunken + { TextLabel1 TextLabel + MoveScaled(2,1,36,5) + Font = Font["Impact,+4"] + Text = ("Encryptec WebCam Viewer") + } + { TextLabel2 TextLabel + MoveScaled(2,5.1429,35,3) + Foreground = Color.SelectedBackground + Text = ("(c) Encryptec Limited 2009") + Alignment = Align.Center + } + { text_status TextLabel + MoveScaled(2,8,36,4) + Font = Font["-1"] + Foreground = &H600080& + Text = ("Scanning for Devices ...") + Alignment = Align.Left + } + } + { tooltray #TrayIcon + #MoveScaled(21,7) + Visible = True + Tooltip = "Gambas Webcam Demo (V4L2)" + Picture = Picture["camera.png"] + } +} diff --git a/app/examples/Multimedia/WebCam/camera.png b/app/examples/Multimedia/WebCam/camera.png new file mode 100644 index 0000000000000000000000000000000000000000..c8c343ec895f7bde3361df4cf2f8ebf819fc92e8 GIT binary patch literal 1027 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 Z@qcqIe_a>Ugr5Ka002ovPDHLkV1hp@x1#_6 literal 0 HcmV?d00001 diff --git a/app/examples/Networking/ClientSocket/.lang/ca.mo b/app/examples/Networking/ClientSocket/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..3a45b78e1856e3c6f22f41e91522f68d1f7f38ae GIT binary patch literal 2155 zcmaKsO>Z1U5QZBF5SH(RuRsEY1vmoFX4gg#O=1+0tc6iNR`!}C$M#HlcaqueO?Qv+ zO8fx?91zNl3*rzEijaU?Brb~(2RL&8!3p>YC=v(WnwhmN5R7)_nXazsu6ny_f7!R^ zs={@!zW)NB#r^Xwc;Fh{s?-7S3V1vC4)`$mKKKZDt+sy+-ih%y;6CtsP`>*WoB?mv z0f#SDR^CEZ$#yKe8dGJ2)J@8TR!+QQJ@Ij1kfOmmEfcU8&@t6aD0ww;v zdz8XYHSv(~0(blz;9l@4P~tfZN?s?x z!(al+I`4v#-)G?S;0;jX`W=*g_d_J{I|M!s9tRJB=Rvrr0+jvV0`CT|f)dw9;4|RI zU=#eVw*Lvr`ZvK>!RHX9_-8dmLs?I>o?fD_XeQSaxFw$k?)|v2%=RVuN(^$|_tw^j zK+#_=i5JIO<|SjvN3@b!5)B@}eGhI(TwYQyiXvL`ndwt*6P=SzQ^(#@Olo0%;l-9Z znP+SQt=i-=2kLC;;V9>+)mK)otOT?5n6$=l5~k#of~AoT12|MumC5nY2Bv}aVS6UC zhRRh^S|8Jfb)5B0>C&8jEG~&`RJs(NojRq1rWH;&+k`b%9p|irL8p?L%+{v!E(_bH z*JR^Zl~}TcNt;y12bX5ESqlYYRUF&3>RnCz$jhqcOv3Y4R zX|;RrWLNbDOn&SG4=J_mNfy?5Zr>Dn62}@^XsRSty==&~2o&5{wuFZddOgqlfRai> z$e`BFE?-b<1`{kUb@V%FK&chpfJCX1%%L?qrc~6D71^Y76b5V%v>f`(DlBEdGpZ~H zIU3b<_7W0{mec4Bb{_l2I3lTvswf)WbI}TKW-=2&YmRBY)ow?vg=oG-?U&>B{0psi ztJOHIeTaHa8=vc7T}-cAmu9r8$588dzv<=W#8?lkZL)s_{-$5(K1V&)!9{vVx>%CI=Y^P zMmH~<&Z8ykhCU`UDu;e?{uouSi-w+Fr1r7Kg{W&e%|bK{6RJSiT$_f9Xi1s|rMa0o z%4ap2YI03iJr=7-b+%agh*GB2$rn;Bm7=>Rd^M4jQmdXeiJrEAR;#lEJ*@;Sbwr6C zPOksUj?G16qf<0Q*>KewTiN7G3bJOZX~d2SLpzQ-&nBPzhhsil_(Ho(ozf|)*ztNH zMEy%hsQ$E=d>&*yF1rxbmFL_S9pIMANB`r>K5}ku(?-VMiP_9_9Xie~C!a|hYpf;O z4e`e){acDyoto<5e39)edpMM`tjqplDEk;{)aNHBfr^+0KeBzaecj{Q)j5{OB~ph= rI55=_l{T}2^p#?AeMhVEBw3<$bEqvpa+6QJH1`lG&Fo5J20Q)@czHg! literal 0 HcmV?d00001 diff --git a/app/examples/Networking/ClientSocket/.lang/ca.po b/app/examples/Networking/ClientSocket/.lang/ca.po new file mode 100644 index 00000000..a58fa3ed --- /dev/null +++ b/app/examples/Networking/ClientSocket/.lang/ca.po @@ -0,0 +1,127 @@ +# Catalan translation of ClientSocket +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the ClientSocket package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: ClientSocket\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 17:12+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Client Socket Example" +msgstr "Exemple de Sòcol Client" + +#: FrmMain.class:26 +msgid "Connected to path " +msgstr "S'ha connectat al camí" + +#: FrmMain.class:28 +msgid "Connected to remote host " +msgstr "S'ha connectat a l'ordinador remot" + +#: FrmMain.class:29 +msgid " - Using local address : " +msgstr "- Fent servir l'adreça local:" + +#: FrmMain.class:42 +msgid "Connection Closed by foreign host." +msgstr "Connexió tancada per un ordinador aliè" + +#: FrmMain.class:54 +msgid "Host Found. Connecting..." +msgstr "Orinador trobat. Connectant..." + +#: FrmMain.class:84 +msgid "The system does not allow to create a socket" +msgstr "El sistema no permet crear un sòcol" + +#: FrmMain.class:86 +msgid "Host not Found" +msgstr "No s'ha trobat l'ordinador" + +#: FrmMain.class:88 +msgid "Unable to Connect. Connection Refused" +msgstr "No s'ha pogut connectar. Connexió rebutjada" + +#: FrmMain.class:90 +msgid "Error Reading Data" +msgstr "S'ha produït un error en llegir les dades" + +#: FrmMain.class:92 +msgid "Error Writing Data" +msgstr "S'ha produït un error en escriure les dades" + +#: FrmMain.class:158 +msgid "Looking up host name..." +msgstr "cercant el nom d'ordinador..." + +#: FrmMain.class:176 +msgid "Timeout trying to stablish connection" +msgstr "Temps d'espera probant d'establir la connexió" + +#: FrmMain.class:189 +msgid "Connection closed by user" +msgstr "Connexió tancada per l'usuari" + +#: FrmMain.form:23 +msgid "Client socket example " +msgstr "Exemple de sòcol client" + +#: FrmMain.form:29 +msgid "Connect" +msgstr "Connecta" + +#: FrmMain.form:40 +msgid "Close" +msgstr "Tanca" + +#: FrmMain.form:45 +msgid "localhost" +msgstr "-" + +#: FrmMain.form:50 +msgid "Host :" +msgstr "Servidor:" + +#: FrmMain.form:55 +msgid "Port :" +msgstr "Port :" + +#: FrmMain.form:60 +msgid "32340" +msgstr "-" + +#: FrmMain.form:71 +msgid "Send Data" +msgstr "Envia dades" + +#: FrmMain.form:76 +msgid "Write here data to send, then press \"Send Data\"" +msgstr "Escriviu aquí les dades per enviar, després premeu \"Envia dades\"" + +#: FrmMain.form:81 +msgid "Select protocol :" +msgstr "Seleccioneu el protocol:" + +#: FrmMain.form:87 +msgid "TCP" +msgstr "-" + +#: FrmMain.form:87 +msgid "UNIX" +msgstr "-" + +#: FrmMain.form:102 +msgid "Data Received :" +msgstr "Dades rebudes :" diff --git a/app/examples/Networking/ClientSocket/.lang/cs.mo b/app/examples/Networking/ClientSocket/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..77ae7c5556e9709a8a67ac9a9e14cf69bdc12c34 GIT binary patch literal 2000 zcmZ9L&u<$=6vqcBP|S~(@*5%;A}S8ex`kAzG6mI2oHo>Utk?-8x5@5Hyz%bLvO8;P z^ACVfkPvVv;y_M_LoX=ef|QHn8}x+40R#smuAD1@;QMyhNyNyrAJ2Pl=KYv?znwYp zxxknz=HK91%wO-q2P3*$h-bie!F$1vz$d^@z$d}43;8~HKh_7}8Sn>?_x%n|gLjJc z)ICC+!umCk?G_5If%jn@fV?jO9|3p3r@$`@{T}!z*588Cy?{~qtO2Yp2qrL@Iml2ipu+@z;od9;92mk zLLPxHV;zID;1`Abc0u;>G$zM18SIa1#^X6m&bNg57$&wI4$hln;Cvr0q>qD~7Y~Fe zaIINgc>OFU%96b$zGKegAxthc4}LE~I&vlP^_C2bs{*M!FQz1s6)9$4oxONL%mqGa zE8E8Hk(IR@DvSb}NRwPjxl}@I5-5$<VaCKQW#WtChem;64Y6;X?06GNap&WyE%=G`kYoW*i|je@ z)reMXwvm{#=sBwtC-7U5t?JzM>eZTDtu40~mzt&4lCw;GpOp^EO%=ITt1df@C1<`W zuPs#?i|5vtnzd40CD!T0N+*G`Ca%bZm3mz^tBsnxTx(Pp>sPAt^UJk%`%ReYtyFE1 z(;*dBWWCzFy236m*XvgTzii^IBb$mWH5MDSqxfDaUnrp(7Ejmd3?js1>4rTY1Z)aT1l4@n@Ty+)9eM}Mg#wt5u!?aMj!0(DXu>p-3l{13h_vDPuP9jh_mYBz5)vKV7Ehs z^YbTC5p&(ahLQ(6s3W%F-4v4HXgBhcG?O?*o2Y<>q|35!Wcs6BY&vQq(z%6L9drXi zCNRju<215Ha-iFwIB%g|)\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Client Socket Example" +msgstr "Příklad Soket Klienta" + +#: FrmMain.class:26 +msgid "Connected to path " +msgstr "Připojeno na cestu " + +#: FrmMain.class:28 +msgid "Connected to remote host " +msgstr "Připojen ke vzdálenému hostiteli " + +#: FrmMain.class:29 +msgid " - Using local address : " +msgstr " - Použita lokální adresa : " + +#: FrmMain.class:42 +msgid "Connection Closed by foreign host." +msgstr "Připojení ukončeno cizím hostem." + +#: FrmMain.class:54 +msgid "Host Found. Connecting..." +msgstr "Host nalezen. Připojování..." + +#: FrmMain.class:84 +msgid "The system does not allow to create a socket" +msgstr "Tento systém neumožňuje vytvořit socket" + +#: FrmMain.class:86 +msgid "Host not Found" +msgstr "Host nenalezen" + +#: FrmMain.class:88 +msgid "Unable to Connect. Connection Refused" +msgstr "Nelze se připojit. Spojení odmítnuto" + +#: FrmMain.class:90 +msgid "Error Reading Data" +msgstr "Chyba čtení dat" + +#: FrmMain.class:92 +msgid "Error Writing Data" +msgstr "Chyba zápisu dat" + +#: FrmMain.class:158 +msgid "Looking up host name..." +msgstr "Vyhledávám hostitele..." + +#: FrmMain.class:176 +msgid "Timeout trying to stablish connection" +msgstr "Timeout snažím založit připojení" + +#: FrmMain.class:189 +msgid "Connection closed by user" +msgstr "Připojení ukončeno uživatelem" + +#: FrmMain.form:23 +msgid "Client socket example " +msgstr "Příklad coket klienta" + +#: FrmMain.form:29 +msgid "Connect" +msgstr "Připojit" + +#: FrmMain.form:40 +msgid "Close" +msgstr "Zavřít" + +#: FrmMain.form:45 +msgid "localhost" +msgstr "-" + +#: FrmMain.form:50 +msgid "Host :" +msgstr "-" + +#: FrmMain.form:55 +msgid "Port :" +msgstr "-" + +#: FrmMain.form:60 +msgid "32340" +msgstr "-" + +#: FrmMain.form:71 +msgid "Send Data" +msgstr "Pošli data" + +#: FrmMain.form:76 +msgid "Write here data to send, then press \"Send Data\"" +msgstr "Zde zapiš data, pošli zmáčknutím \"Poslat data\"" + +#: FrmMain.form:81 +msgid "Select protocol :" +msgstr "Vyber protokol :" + +#: FrmMain.form:87 +msgid "TCP" +msgstr "-" + +#: FrmMain.form:87 +msgid "UNIX" +msgstr "-" + +#: FrmMain.form:102 +msgid "Data Received :" +msgstr "Příjmané data :" diff --git a/app/examples/Networking/ClientSocket/.lang/de.mo b/app/examples/Networking/ClientSocket/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..8085f09a79d9f18d526da8a967c8b19d89813c37 GIT binary patch literal 1989 zcmZ{k&u<$=6vwAfpt$@j<#z>-AUKq)8wf&`DX4DZq*m?3@{dA#8}Gh&m#k;Z>^NzP z_yZ8)fS@-dE=X`f96%h9xCDs{T)1!ml^YUgPDn`beY@*eMPg+8lX>sWoA4EWd>?!W{Is-x3*LeGci;)|2T<1i1}=bq zmGe`#Ds?aBFM$%bR&oox9rF~Fbs2aM_#XH$_)+Qq61*StZ@@djA3=Q7&-hsce*vZb zV@H+3M^*77^JVZ1xCP3(cf^SvKLBOF57C^o{5dFneNpmzu!8vwP}cncNt9Pazx(JMEe*4g{v?*9$U{8+sq$&5^E^W z647v&cs`qUUP{*G+mU9 zYbwW@otmghWqk;#R;z`_SWmfEwXEHd#nVYqv(aOgu#R<-r^hL}riruOhBnPDCMtIC zuXI#DX3F+6&tr-#XC#Srn%X^)Cv>c#g{DGM)lbH3r=Gmqmn{*Ic|A;%ET*u~5HhI2 zM)P$wFmSMhl-1AMfKpxFhD4E)&Y?9srbyJ1jBKJs@-Z6}Er-6)gC#F`LFLy$jz)Ey zeFKdJ%_z9cF2lYx>eN>*)z=$zxzX)4H#aJs&7jM>i8uqu%{AI;)Vo1@Ggz(D#m!o~ z`P`MwjYg%VGavMwHd(5@bv0TWv|6-LZ#U>%qg`*d&evC0yNzD&RfL++RF635vmV!| zRo_@2$d>0?t@CM8wQe+zsZpifY&YihJzrg_Kn;(p8}#=lsK@hFe`b>E#Jo<X;dKTjF}M|-Z5>JW|Ju97JIU|q++8M^;&$xXG3DM7d&kE3 zS@xOp0-LG7#*PjHdKH7LR~54*{S7VsvAH~E9&rHpXZ zq@ya$nwvkOB#b?FF9MY4fTPWemRCeZhp_|)a@)M!v1v+s2cMuMlvdO#w-ssym}b-~ z-VxM@MMUQpa}CYx|I{^T>hRQ3BshFa=q+z*u3U!qkVio%WWJ=0A(NMi%*8R(X3dC} ZdN{%(k0CG)ttcqsLL}LL#LxwK{sje!{f__u literal 0 HcmV?d00001 diff --git a/app/examples/Networking/ClientSocket/.lang/de.po b/app/examples/Networking/ClientSocket/.lang/de.po new file mode 100644 index 00000000..2e085c9d --- /dev/null +++ b/app/examples/Networking/ClientSocket/.lang/de.po @@ -0,0 +1,120 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Client Socket Example" +msgstr "Client-Socket-Beispiel" + +#: FrmMain.class:26 +msgid "Connected to path " +msgstr "Zu einem Pfad verbinden" + +#: FrmMain.class:28 +msgid "Connected to remote host " +msgstr "Zu einem Remote Host verbinden" + +#: FrmMain.class:29 +msgid " - Using local address : " +msgstr "Lokale Adresse verwenden:" + +#: FrmMain.class:42 +msgid "Connection Closed by foreign host." +msgstr "Verbindung vom Host getrennt" + +#: FrmMain.class:54 +msgid "Host Found. Connecting..." +msgstr "Host gefunden. Verbinde..." + +#: FrmMain.class:84 +msgid "The system does not allow to create a socket" +msgstr "Das System verhindert, einen Socket zu erstellen" + +#: FrmMain.class:86 +msgid "Host not Found" +msgstr "Host nicht gefunden" + +#: FrmMain.class:88 +msgid "Unable to Connect. Connection Refused" +msgstr "Kann nicht verbinden: Verbindung abgelehnt" + +#: FrmMain.class:90 +msgid "Error Reading Data" +msgstr "Fehler beim Lesen der Daten" + +#: FrmMain.class:92 +msgid "Error Writing Data" +msgstr "Fehler beim Schreiben der Daten" + +#: FrmMain.class:158 +msgid "Looking up host name..." +msgstr "Schlage Hostname nach..." + +#: FrmMain.class:176 +msgid "Timeout trying to stablish connection" +msgstr "Timeout bei der Verbindungsherstellung" + +#: FrmMain.class:189 +msgid "Connection closed by user" +msgstr "Verbindung vom Benutzer getrennt" + +#: FrmMain.form:23 +msgid "Client socket example " +msgstr "Client-Socket-Beispiel" + +#: FrmMain.form:29 +msgid "Connect" +msgstr "Verbinden" + +#: FrmMain.form:40 +msgid "Close" +msgstr "Schließen" + +#: FrmMain.form:45 +msgid "localhost" +msgstr "-" + +#: FrmMain.form:50 +msgid "Host :" +msgstr "-" + +#: FrmMain.form:55 +msgid "Port :" +msgstr "-" + +#: FrmMain.form:60 +msgid "32340" +msgstr "-" + +#: FrmMain.form:71 +msgid "Send Data" +msgstr "Daten senden" + +#: FrmMain.form:76 +msgid "Write here data to send, then press \"Send Data\"" +msgstr "Zu sendende Daten eingeben" + +#: FrmMain.form:81 +msgid "Select protocol :" +msgstr "Protokoll wählen:" + +#: FrmMain.form:87 +msgid "TCP" +msgstr "-" + +#: FrmMain.form:87 +msgid "UNIX" +msgstr "-" + +#: FrmMain.form:102 +msgid "Data Received :" +msgstr "Daten empfangen:" diff --git a/app/examples/Networking/ClientSocket/.lang/es.mo b/app/examples/Networking/ClientSocket/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..826574d30fe6de84793c1d3e44e45dde58cab43f GIT binary patch literal 946 zcmYk4J8#rL5XYAg9ywkjNJvN&qlk_;fhv&+A=aIfSn)k{j{s@Vo;}<-INoD-?-V*Z z8bn7=iIAY-Bk(yaW!x8{i1M2)>>19e5M_58wj$2|N#e0q4PQ;41iic7Fo?!TvXhB~Iq1 z_51=|+&MU31{XjVcM+_B_h#p-;2rGO!K+{!#1b#?aQPDO2{;5@J)c3B$Hj1hZE5Fx zT|OsQpk+?*nmd?@`)=0**E5til{Hb#GcGI{mAqn0d@PSfITMbRVNq2@!HE?$X(hEe z;cUVw`C?ZY3$&DW=x`1r8fj%!qVn>jNXa>gZnY(Pjp&(psx!+pWX+ViXjYNoB3&js zMrY3JBmy?WED1+?4r>(Tv#W+SY`(paynWnpq@mG)`77su2H>LG1}^&pD3f?BN| zb~>9dEe2ybV6V&a$fr1H?DSm9jX2)QvlXQWo0WQ04^NAFxbmQaj#>26>%JMG2+PNI zb(G7jctpuiYQy$%ueTLb5qe1ylOY6(&zuA5;Tcv?3z9un8Hn1Ofsq6Xp}h} nT4)lT^(D#I;~!rMzr@slBynlKO!=4xsvKV#b?I!JvqSs`*cslV literal 0 HcmV?d00001 diff --git a/app/examples/Networking/ClientSocket/.lang/es.po b/app/examples/Networking/ClientSocket/.lang/es.po new file mode 100644 index 00000000..4ecb347f --- /dev/null +++ b/app/examples/Networking/ClientSocket/.lang/es.po @@ -0,0 +1,63 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FrmMain.class:245 +msgid "Client socket example " +msgstr "Ejemplo de Cliente de puerto" + +#: FrmMain.class:250 +msgid "Connect" +msgstr "Conectar" + +#: FrmMain.class:266 +msgid "Close" +msgstr "Cerrar" + +#: FrmMain.class:271 +msgid "localhost" +msgstr "localhost" + +#: FrmMain.class:276 +msgid "Host :" +msgstr "Servidor :" + +#: FrmMain.class:281 +msgid "Port :" +msgstr "Puerto :" + +#: FrmMain.class:286 +msgid "7" +msgstr "7" + +#: FrmMain.class:291 +msgid "Data Received :" +msgstr "Datos recibidos :" + +#: FrmMain.class:303 +msgid "Send Data" +msgstr "Enviar datos" + +#: FrmMain.class:308 +msgid "Write here data to send, then press \"Send Data\"" +msgstr "Escriba aquí los datos a enviar, luego presione \"Enviar datos\"" + +#: FrmMain.class:313 +msgid "Select protocol :" +msgstr "Seleccione un protocolo :" + +#: FrmMain.class:320 +msgid "TCP" +msgstr "TCP" + +#: FrmMain.class:320 +msgid "UNIX" +msgstr "UNIX" diff --git a/app/examples/Networking/ClientSocket/.lang/nl.mo b/app/examples/Networking/ClientSocket/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..ad9ce25f9e5ab0b4d6592358731853b9101ab311 GIT binary patch literal 2064 zcmZ9Mzi%8x6vqb$U>p)c_zfu@mRumbm-7!nI0CX|&cVX5Pxjd%<=pP`-THQCHnVf* zI3*oI6bR9v2uccC3KS3xl%%1dAR>PN9aIPkzHfi{ELOMs(Y$#x^WJyfyWh{B`b^=u zP`&?xvv}`4fDfMRL8V>*Z-5VjAA!$+pMcMTUsd)Wz(+Cu2|N$}49dFu;0*Y8HNNnW zQcqy~8Yq5i6|aMjV4Q%mE(gzoAA`?O4txx}3*x7K#b*xu4V3t&PAP?- zYTzT|Merhc9h7w+f~S$=r{MD#e~IKIdB|DyBl?PF@<@!5?=;@0@M8J#gDuo)yb|A;$||u*{_-GX zg=;PJl<|vrAxlw9a>t^BQkTqq_dK=oLu$ZVky}Q~` z-2qd6lzUDovYeA_tdqnZiaeoX4J|a4lB#Z;vK@Ky?nsV=NA7hmiSq%4rG}6}?QX8W zuXYV4SX`>;Cuu;bZQh4Ok&?`zH9MwA)RG<9M04Z^Y!I|u`pgb2MZhzvCS7?Va_l&FRiou+8IGCIiULEm~;KF9ppvf`tWYHd_m? zzuKH{Hm5gq?t`w=CQr1tu0_}4BsrpO%|55ghcZ}>c+82-8myfk7=j!^^_9WE#Mspg%cvNxFJ<1T8=bQdwmgv~L zN#Q`doc&w7-L>G-#7`pJXBV_hXr+c)bg38n>66_u)J3~MIBAxkZi%W<_N;Y1<|L}^ zRjZ6^JE@&ygex%$qV{4F6|^S8ZKUY5OxKpmp!fSIus%k`t<;su)aN`w9~o2?ihk^v z)2QsKNz44-tIs_*iidUUK%Oy##U3q%TKCk84^S(f621i^=8B@3%;n+0#Rub)b3xfI zeI6G{G^ol;$~V\n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Client Socket Example" +msgstr "Client Socket Voorbeeld" + +#: FrmMain.form:23 +msgid "Client socket example " +msgstr "Client socket voorbeeld " + +#: FrmMain.form:29 +msgid "Connect" +msgstr "Verbind" + +#: FrmMain.form:40 +msgid "Close" +msgstr "Sluiten" + +#: FrmMain.form:45 +msgid "localhost" +msgstr "-" + +#: FrmMain.form:50 +msgid "Host :" +msgstr "-" + +#: FrmMain.form:55 +msgid "Port :" +msgstr "Poort :" + +#: FrmMain.form:60 +msgid "32340" +msgstr "-" + +#: FrmMain.form:71 +msgid "Send Data" +msgstr "Verzend Data" + +#: FrmMain.form:76 +msgid "Write here data to send, then press \"Send Data\"" +msgstr "Noteer hier data om te verzenden, druk vervolgens \"Verzend Data\"" + +#: FrmMain.form:81 +msgid "Select protocol :" +msgstr "Selecteer protocol :" + +#: FrmMain.form:87 +msgid "TCP" +msgstr "-" + +#: FrmMain.form:87 +msgid "UNIX" +msgstr "-" + +#: FrmMain.form:102 +msgid "Data Received :" +msgstr "Data ontvangen :" + +#: FrmMain.class:26 +msgid "Connected to path " +msgstr "Verbonden met pad" + +#: FrmMain.class:28 +msgid "Connected to remote host " +msgstr "Verbonen met afgelegen host" + +#: FrmMain.class:29 +msgid " - Using local address : " +msgstr " - Using lokaal adres : " + +#: FrmMain.class:42 +msgid "Connection Closed by foreign host." +msgstr "Connectie gesloten door vreemde host." + +#: FrmMain.class:54 +msgid "Host Found. Connecting..." +msgstr "Host gevonden. Verbinding maken..." + +#: FrmMain.class:84 +msgid "The system does not allow to create a socket" +msgstr "Het systeem staat niet toe een socket te creëren" + +#: FrmMain.class:86 +msgid "Host not Found" +msgstr "Host niet gevonden" + +#: FrmMain.class:88 +msgid "Unable to Connect. Connection Refused" +msgstr "Onmogelijk om te verbinden. Verbinding geweigert" + +#: FrmMain.class:90 +msgid "Error Reading Data" +msgstr "Fout bij het lezen van data" + +#: FrmMain.class:92 +msgid "Error Writing Data" +msgstr "Fout bij schrijven van data" + +#: FrmMain.class:158 +msgid "Looking up host name..." +msgstr "Host naam opzoeken..." + +#: FrmMain.class:176 +msgid "Timeout trying to stablish connection" +msgstr "Timeout tijdens het trachten een connectie te maken" + +#: FrmMain.class:189 +msgid "Connection closed by user" +msgstr "Connectie gesloten door gebruiker" + diff --git a/app/examples/Networking/ClientSocket/.project b/app/examples/Networking/ClientSocket/.project new file mode 100644 index 00000000..dbf9a2f1 --- /dev/null +++ b/app/examples/Networking/ClientSocket/.project @@ -0,0 +1,20 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Client Socket Example +Startup=FrmMain +Icon=socket.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.net +Description="Client socket example.\n\nThis example shows how to use a socket to connect to a server. You should use the \"ServerSocket\" example as a testing peer." +Authors="Daniel Campos Fernández\nBenoît Minisini" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Example +Address=benoit@desnouettes +License=General Public Licence +Packager=1 +Tags=Network diff --git a/app/examples/Networking/ClientSocket/.src/FrmMain.class b/app/examples/Networking/ClientSocket/.src/FrmMain.class new file mode 100644 index 00000000..34780572 --- /dev/null +++ b/app/examples/Networking/ClientSocket/.src/FrmMain.class @@ -0,0 +1,231 @@ +' Gambas class file + +'******************************************** +' THIS EXAMPLE SHOWS HOW TO USE THE 'Socket' +' CLASS FROM 'gb.net' COMPONENT +' +' (C) 2003-2004 Daniel Campos Fernández +' (danielcampos@netcourrier.com) +'******************************************** +Private CurProtocol As Integer + +Public Sub Form_Open() + + CurProtocol = 0 + +End + +Public Sub MySocket_Ready() + '*********************************** + ' When connection proccess has finished + ' successfully, "Connected" event will raise + '*********************************** + + timTimeout.Enabled = False + If CurProtocol = 1 Then + lblMessage.Text = ("Connected to path ") & MySocket.Path + Else + lblMessage.Text = ("Connected to remote host ") & MySocket.Path + lblMessage.Text = lblMessage.Text & (" - Using local address : ") & MySocket.LocalHost & ":" & MySocket.LocalPort + End If + Me.Enabled = True + Set_Interface(True) + +End + +Public Sub MySocket_Closed() + '******************************************** + ' this event will raise when foreing host + ' closes the socket by any reason + '******************************************** + + lblMessage.Caption = ("Connection Closed by foreign host.") + Me.Enabled = True + Set_Interface(False) + +End + +Public Sub MySocket_Found() + '******************************************** + ' this event will raise when foreing host + ' name has been translated to IP + '******************************************** + + lblMessage.Caption = ("Host Found. Connecting...") + +End + +Public Sub MySocket_Read() + '**************************************** + ' When some data arrives from the remote + ' part of the socket, "DataAvailable" event + ' is raised + '**************************************** + + Dim S As String + + If MySocket.Status = Net.Connected Then + Read #MySocket, S, Lof(MySocket) + txtReceive.Pos = txtReceive.Length + txtReceive.Insert(S) + End If + +End + +Public Sub MySocket_Error() + '********************************** + ' this is the function to + ' handle Errors when trying + ' to read or write to the socket + '********************************** + + Select Case MySocket.Status + Case Net.CannotCreateSocket + lblMessage.Text = ("The system does not allow to create a socket") + Case Net.HostNotFound + lblMessage.Text = ("Host not Found") + Case Net.ConnectionRefused + lblMessage.Text = ("Unable to Connect. Connection Refused") + Case Net.CannotRead + lblMessage.Text = ("Error Reading Data") + Case Net.CannotWrite + lblMessage.Text = ("Error Writing Data") + End Select + Set_Interface(False) + +End + +Private Sub Set_Interface(bState As Boolean) + '**************************************** + ' An auxiliar method to enable/disable + ' controls in the formulary , when + ' connection is stablished or closed + '**************************************** + + btnConnect.Enabled = Not bState + txtHost.Enabled = Not bState + txtPort.Enabled = Not bState + cmbProtocol.Enabled = Not bState + Label5.Enabled = Not bState + btnClose.Enabled = bState + timTimeout.Enabled = bState + 'TextArea1.Enabled=bState + btnSend.Enabled = bState + txtSend.Enabled = bState + + If bState Then + txtReceive.Text = "" + txtReceive.SetFocus + Endif + +End + +Public Sub btnConnect_Click() + + '****************************************** + ' To connect to remote host we call to + ' connectsocket method, passing Host Name + ' and port as arguments + '****************************************** + Dim RetVal As Integer + + btnConnect.Enabled = False + Select Case CurProtocol + Case 0 + ' Stablishing a TCP connection. + ' Here we use Host and + ' Port properties, we could also + ' do directly MySock.Connect(TextBox1.Text,VAL(TextBox2.Text)) + MySocket.Host = txtHost.Text + MySocket.Port = Val(txtPort.Text) + MySocket.Connect() + Case 1 + ' Stablishing a Local connection. + ' Here we use Path and + ' Port properties, we could also + ' do directly MySock.Connect(TextBox1.Text,0) + MySocket.Path = txtHost.Text + MySocket.Port = Net.Local + MySocket.Connect() + End Select + If MySocket.Status > Net.Inactive Then + If CurProtocol = 0 Then + '************************** + ' TCP : connection in progress... + '************************** + btnClose.Enabled = True + 'ME.Enabled=FALSE + lblMessage.Text = ("Looking up host name...") + timTimeout.Delay = 10000 ' we'll wait a maximun time of 10 seconds + timTimeout.Enabled = True + End If + End If + +End + +Public Sub timTimeout_Timer() + + '*************************** + ' timeout trying to connect + '*************************** + Me.Enabled = True + timTimeout.Enabled = False + If MySocket.Status <> Net.Connected Then + Close MySocket + Set_Interface(False) + lblMessage.Text = ("Timeout trying to stablish connection") + End If + +End + +Public Sub btnClose_Click() + + '********************************** + ' Here we close the connection + ' to remote host + '********************************** + Close MySocket + Set_Interface(False) + lblMessage.Text = ("Connection closed by user") + +End + +Public Sub btnSend_Click() + '**************************************************** + ' Here we send data to the remote part of the socket. + ' We have to be sure that connection is active + '**************************************************** + + If MySocket.Status = Net.Connected Then + Write #MySocket, txtSend.Text, Len(txtSend.Text) + txtSend.Text = "" + End If + +End + +Public Sub Form_Close() + + '********************* + ' Close possible Stablished connections + '********************* + If MySocket.Status > 0 Then Close #MySocket + +End + +Public Sub cmbProtocol_Click() + '************************************************** + ' Here we select protocol to use : TCP,UDP or UNIX + '************************************************** + + CurProtocol = cmbProtocol.Index + If CurProtocol = 0 Then + Label2.Visible = True + txtPort.Visible = True + Label1.Text = "Host :" + Else + Label2.Visible = False + txtPort.Visible = False + Label1.Text = "Path :" + End If + +End diff --git a/app/examples/Networking/ClientSocket/.src/FrmMain.form b/app/examples/Networking/ClientSocket/.src/FrmMain.form new file mode 100644 index 00000000..65a32b7a --- /dev/null +++ b/app/examples/Networking/ClientSocket/.src/FrmMain.form @@ -0,0 +1,75 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(34,23.4286,49,60) + Text = ("Client socket example ") + Icon = Picture["socket.png"] + Resizable = False + { btnConnect Button + MoveScaled(31,7,17,4) + Text = ("Connect") + } + { txtReceive TextArea + MoveScaled(1,44,47,15) + ReadOnly = True + } + { btnClose Button + MoveScaled(31,12,17,4) + Enabled = False + Text = ("Close") + } + { txtHost TextBox + MoveScaled(11,7,18,4) + Text = ("localhost") + } + { Label1 Label + MoveScaled(1,7,9,4) + Text = ("Host :") + } + { Label2 Label + MoveScaled(1,12,9,4) + Text = ("Port :") + } + { txtPort TextBox + MoveScaled(11,12,18,4) + Text = ("32340") + } + { txtSend TextArea + MoveScaled(1,27,47,6) + Enabled = False + } + { btnSend Button + MoveScaled(13,34,23,4) + Enabled = False + Text = ("Send Data") + } + { Label4 Label + MoveScaled(1,24,47,3) + Text = ("Write here data to send, then press \"Send Data\"") + } + { Label5 Label + MoveScaled(1,1,17,4) + Text = ("Select protocol :") + } + { cmbProtocol ComboBox + MoveScaled(19,1,29,4) + ReadOnly = True + List = [("TCP"), ("UNIX")] + } + { lblMessage TextLabel + MoveScaled(1,17,47,6) + Font = Font["Bold"] + Alignment = Align.Center + Border = Border.Sunken + } + { timTimeout #Timer + #MoveScaled(26,47) + } + { Label3 Label + MoveScaled(1,40,47,4) + Text = ("Data Received :") + } + { MySocket #Socket + #MoveScaled(38,35) + } +} diff --git a/app/examples/Networking/ClientSocket/socket.png b/app/examples/Networking/ClientSocket/socket.png new file mode 100644 index 0000000000000000000000000000000000000000..bf16a70ad3137ebc3e6c1a9e01f81d4f07d97ffe GIT binary patch literal 2200 zcmV;J2xs?+P) zX>45O6^5U?%y{<2cH%8|91>-52yvVRk`l&1;x=grMJ=LDlL3J!st_$m6{u7yR*9&P zDnP1)LMsxHwm}fc&el}L6O)7{!NIYT;w7;?W5=F-JnQvb&rE-eJ+a53^anqCq@#Ov z@6|oeIp2BD_X&JU*4EZmxQ|34&)96X=0qa#;ra9D|J>Z%oGt#oxw#nt`P%}RGM~@4 zucf7Bm(^;OqS5HqTrOt=UjDl8#cw0?sqEcu_r7i0wmB^pi=-$DfNV0ET8cBDUakLH zf&E}8gNIAOp#_rwq6(oTj7H#%&eqmm0EffDo;`c^Ih{_YUauE3nGErGobmB-LZQ&L z2Of9;m&-LH@HYY+g?nn@rR8wD7C<8a0;u`rybh1nwzjr=;jLF+d1d=!k3F_ar_+h5 zsuBzaNu^SpJ9qBF*x1;wT`m_6hvR<-I0CmW6@)hz=W_K}WVo#q)|7)*hNud`ajHj> z_Nwm4_M>-izt6dICkBH-NT<{I{eBch;mnyc7XpF6mNjeE1RM?rE|=@3iOvGp5At%S z^Zi91e_T&G0y`H#jRmw?5Jjj05Xfb7e4wsiz4JGM$znlK6ns7(ilT7p)T#4cuXig@ z2>x{y5Q-n1)ajnRr@j4UaqU{zTm@Seg5Cs@lm`$ca)Q8#a6L^AK7-9(imIykd_GdC z6zvBNBu0-P+pKx$$7f%7;e|qQm&=96<7uj^tJ`g{SStN~|B05Cmfy-n*=y@pu6#s{ zMnNM&VRfnTMHH~#3u5kmn4v6TtPbv?{#7@nTf~a3=ab+5?I-KkuRqk-*jNh?i^Yh?<6OITEffd@ z-jS!3f9_nk9iI<08ZehZFah(*3u_hK53yFJFG~i6@?DF91})Lp4y%l`9a7 z!MufVZ3I@`GTDF<^EE;GdV4V#jf{_vbM?!18nu0tnl#wWdhBK$B_$dV1cnTy^gsJ3 z>%X^Em&s%Zhr`5TF+3hm_|m0IyPtmg>DOk+uoN0ZfMgQBya)~le6lw$lu3Z5xoJ)h zpJXs?rzD$TYjucfdpRXKEeJxvJyDT9>z&+j_ZBjl48dTKSS-fi;9&UT#f!Vs>GZ2p zaSJ_Qg2l)ZXk?g}fDIcU40H_V6)@Qg-0X5qobpQLLqv)x-w_m+kgN3^UuFBYY^=RtzHxtBFiwp4r*%h*=*bl?oL>jnH)qn zrB1GKibQe1%=qa>CiOK`^>5Rb$A z2jRZ$Ffa}s-W$Os@rE)A&m;!NBs$X#G_Jn~MNt?T86gx3F>tkqRKJUc-pi8NX8A$! z;1dDNu+$*SMzmU3)&Qs5;ggS`w+FV~54~eB5H6^paGeJ7`KXDN>+T|zO5yc-35UZB zUh5@&{Tz#{a!_x>T4r7My1=4Y06I}vrqjq!TMO9?w7Wo-;NaVETO;V^L02#@%VLO0 zkaSMN*w|PhxVyiPjIV>mb5xKuh&6VWRn47KVYBX-y%`BYDkDpXMgx4=0g94u#$*!S zdJ}d%0(<`iUwFZx2b-w~LZnh3qqnCUli9-cp+U5sVl|ntTl82O>k&PDc&(;+ zZHJG}1jvFXEeL2zw9wz5&wk2Bufx&z;HOSF_&3P*LsK;@th@msNo>AV=F0FDIt9i71uJxznPEQI;VdodzK1-Q$K+|AZZnz==cf znHzjj*tk631A+j8NJFijhC0KHnNToK0PGf!Bz2YwlG$8WaAIPJj0(A2{-<)0y9eO- z`|#cGgQfzyBJlQUNGW+4io(t1Id$&|f?kK3$@*tC;jUaR7t3a!Bbm(0OqPlu^1C64 z7<_mbR;+?`n;>q4H(c-+7X+gDjEf;e5l&nHRZln?S3`mD!RZHC`IJFVyV*#V+*4{a zibkF$FX<*A0B72uq7pW40`GP3^uxOyV3k0p0izx&tPmTA0Y7B*ENML z(@#c1ZrLrMDw3@tYH2HJj^s7d`;j#>sRZzD8(&L~c^PbH>JOr5> zlo-fNOmOMbf1U5|>bbA$nO}LQ_keJw+*X)wX?5a{i)@z1Ys&2<1qkhA&>29}U}PCI zQx(#H!I*~;0YSj;9vS=UbjOK-&hDQ%AvtRjZU{v9`_F&DhU#CyC%4_z*}d~-!Qm!{Qh?(|{hf2| zmOrdmvS6LwY!qfL$<3D~CrX6;L3Ox$;PRL+c-q%LwD(8hXb0b5!q>gn4<$CU@xk&k zTeH<>S!uGFt92%WS!*(w1dS}jQ_5H(kqW2PiC{938i)lWyBr*8l61HOr+4ztx%ewE&dXnBb)4hM_uq0$-%UA4gRhKl= z(>?wB_V4IlPd~wBhRpTbF zF11i+g51yUKx=^#f-l{i!?)KLQr8w_)q{Rq8B2+F>;WL-_CJHL&`y*<)vpLt|N8PK zApi4kp$!-fw_KafBli{JSRP)egu>)l5`7lo{Ix_o_K>-L8^$rDC%Op+7168}aJdpt zaQ~Yap)eX27RB?(y@kxkkLQn7@MaCzHtaqQADffL*6tFr*1wKYim$E9=Nsz^`0`H< zu(jpb8NKW8ih!bj-i^@?#z0n*hllRW=jNqpY_EeCcS10H^4V}yb0Q+~rsc3|VJeT^ zUrcLvl$8%1;dt!;NYCX(K=D_0VGJb1y7}swZ2oCY7L{G_yE?u!949_cM9idqWmK`$K>` ze$+tNu|au32$c2^l`&H%zzFg1;({|49w0^={$Wq8+WE+)?i(LppZd3tYEbeoMX|iy z0`DJy$e0;-9H>6h$+4;?=I8l%`r)fEmY1);2nU-%Ylw?+gHT``Kw)VKjzAbtN%N;h zfHq(|=Ij{&7Ur46_Z!6PKiJfLp08lb060622wqt>&@~|8;F}f@8GB!>dVK%W2|nvi5Ux>HluQ;}StbAwbxiNcD--nx z?9JkdKQ{Nx&yQ7OOR7QFz8r+s|^m6*GI;!2V6>Akpx5?;~#si zwPUQPBLZGajT2aiv+MYBft;`b7>??IrKAihDPvFf;FSV3@mk8DrIZRNr6yEd>Y-jB zOweqlbik5B8ZfRxit`r%v}03W^B#Tmhgo>zSI~})RF`NUMviAdN*T0VQiZGu8X(RS z0a`okDSH7kvy>U7*U?^INmol1S6zSKrIFu^C%`3j$WoRJDmj6G^RNcc+F}2mO^DLD zM5_3J&jWAU*$TcE9>FYP*h5xcHMudGQt2g64Y?B+cuI<( zWhog{YJ818OBWFEck!#AKgPp3*}QcB7kK@bFOXkc%7f2tq0Jv4keY#S!D`a4@ePke zqK+QIZCMv}(KS)qEG2`M6a>#@7MwsQ9Qi%l{_$J9zGxA%`}&9zl7(|iSa|b#%6n{- zanO;_5VJxM?rNqmAp@^3?xGSfQBElcN=vDrl!9?ZoVpmtrnkG9mX2DEAKcEIlxB)o zts()4&B&m+y@SV}dXi{N98udr2M5rR(5U@6kr1ZwI9VyVSeEOw?>g^gf}EC8K}$*F zS5gQ#8T?P>16r!f@w=h~beO8vCT^RRj!w_zmle%yIM~YnJ@+gXe|im(KbzwziC|-aks}LOu64~`<;%6;FV2E#T;rg{3 zkW)1LxTUq_7uT5>*f$IJH`1GO_W)~-O;BYMwTa%HNLULjpj@AgH z`RclbJn{UyWG-AbX~Lx>qXcM!l(yAw8INdJ0x=RIj6sY{P+fH;1$h?l)iiT}5U$t+ zY=1Y-Kp(a%hO(m#%%17O>vl017^J7Ak^f4b!Osttu~@+7gfu#g#kBfrZoK!V#oyh@9r+%1 zhvKL$dxMPC>k0Jxk#BA$KN92!lG{JEfob`rXD;moJY=qgGdt@risZxDe7|TjV#3nb zWvLD!gae?p;ajU?vMm5hilC6fJFEdXLJ$rI@J2h)x`^(?Ttv?y!kx`Td)>sBETO;V zAn|F_*nX^ogwPSJ_+xV(Hf_zvsocMh;J^U!C5!P+%Ol*~LZrKujGTPZqX8_Jo1Q4dx)mkf2(wgxS!vl^ zf9)DPUN3T}&ncG?tV>+YD}6ip?er&D8Fw{L(gMh>oo1P1o*A{+@Igus(kNM^7HX<4*K zHBQYw^o&I$PA|d4dhprd zN&D89$i8|x@s7clmc#L&pO|Smgd-7}!V#1d^e4oU?(3rUcm+Adi_Z%IIj#&g=Knj} z26h4Pn*SY^$1efkrub!aM7!y-Pk#4Ec{h`97C$p?7fi{M4&FdulmJT-WDEmJLwsTa zhYtCve(rxz37PaZwSpbNlCW~=8s4s}V!j2fzC>&@wA56>V2q*hM3AKHd@7Ib!xoC^ zMe~qqiW?up!8!B54Iip{#Xod%^i}`ParaZ-&E&yZ&yecN9(P}=Arly8;G_viB|;-x zrpB1jP;u4#rM!d9`ek$ZQ*(ssy=Ay!=Fsp#89lun)apKhjjgDZOr*=jNFPcHDx11k zSh|Q;w!Fm0qCM1C?&Y?dK1V`o)>+BF>d-wj^;e@Eo0f()+-`-X)KF?W9Mz2hM{9y% zo8;J79{9u)q{U``sIQS6U>G4rM*L!V^fyL`l?s z#C6Y*2`CZJqZ%N@*u1%5<{Z{qlCtJ{W>^8lT}ba?IqU-@fF%iyhCu;pLdh#CW$nDhxT9Wj5|fZIUUW1< zVn;n6_bP7v#5xuy#1lTa8>2O1co`~`i^zd8uFA?`sT(|PEr_V1>d;;ygZ>Y_=c9@f z4&b&d@@CDUxU_)VJ~@ND>G90H`eWo5m!cy5{eSv*wXl{tlt^Tugil)k1Rk`6v0pEq7BoZwWbh zg_Kvm$F_G~#aB2FPiiLq_+$f3nkcN=ldxdz*Rf^@fs zXf%Qlf`p`0R<5{(s~0Wf`0>M(@7qN~XFCzC$xcltfB9O(6h1$DVtmY9dB>!6g=gH0 z7?n**6M$iN{n}Cet^SDk4nQz$r`V1@%k<;&fTd*E>#=$O;@#$_H_wt!S^(PU!C+Kj zj1hs5qqNanaVKO1NMV!_QV1bTtZOK7umJjcdTVS~k~G^ zbQ5UWOV?YEV-L1pFllbR|EC1?N`{8{`5U%p_1kDwqokGl2_EO!!~xD0gk1nJ2`sdg+Z#;qy_g`?~N3iVPO=y*f+wFx=HS>!iOv@?Ykq6eE zw%vI=l?{s}Wdb8=Jk`GLo!9VQ_p`f) z-H8`Zp1gYVuYi+h^x_|2;>GBZcsF`5;ajtBXCY9NnooDVs_N?M`s2ir9~9(m=xONB z&@<5TIs8HX1doD$fvS5;0f^P^Gdx5o&sM6KLlR@KLTF|FE$*4689ze z0hojDgWrSifcL@Iz+XYh_dED1_&4|l_#Y_wT1YPYQ{Xw&unfw%Ume-6Q-gBO*I*0$ z29!H~*YHPB?r{$kzn?(4$AgByfl|jq@ICNfa2b5}MWt|@y3+7!!>xv+hBq3H8{TRt zJ$VZ%BIk;ngdT@VJqIFZi%3l(a%bs>^g`|ArT4!Sc@5_Eked=SP zEv^hDPs5QKj%2a0sx6;r(@&jNp9h-^6($%u4^LvPgNe~ZN`-M$%Z4srUwYE9ko*q$ zpK_aUwau{|{WRoe$A^h&JCoGz?rvL~ZOg6=+kCr&)J0`DEgbGlb;*nG6+0dHj%nw< zng@is+ObYA++)eH&_2y0M#0}UdCG=5yJbSO-t}nFq}J$qcEjAYV$L==nI5flx}B`M zl6B8hr`zjXT*kiBy2>$SENlMg+*;)AwylV`r9wMje5;*%K{-nUKs82)ZD<%!A0 zhN~Ve_E=sUUK<>i(rtHIeeV+9FdNTm6p+kbI#-u$-74h;hiKCJXXDN6!l9qkv139u zaJkpkO?q@;+osm2oCYqlO&_Y*BUjJ2aeZZ#_Lu99me#4e+WI0Jd82L04$ACNpA(nt zTFvBH#^^=s^6=oEv-$qa{qk+ehpVIpdq!rCv^khU4CJ${E~-m8Z!~e-DWyWJvi_}u1NZ, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: DnsClient\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:09+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FMain.form:164 +msgid "Asynchronous" +msgstr "Assincrònic" + +#: FMain.form:95 +msgid "Cancel" +msgstr "Canceŀla" + +#: FMain.class:251 +msgid "Cancelled" +msgstr "Canceŀlat" + +#: FMain.form:159 +msgid "<< Copy" +msgstr "<< Copia" + +#: FMain.form:29 +msgid "DnsClient Example" +msgstr "Exemple de client Dns" + +#: .project:1 +msgid "DNS client example" +msgstr "Exemple de client DNS" + +#: FMain.class:217 +msgid "Finished" +msgstr "Acabat" + +#: FMain.form:44 +msgid "gambas.sourceforge.net" +msgstr "-" + +#: FMain.form:34 +msgid "Go!" +msgstr "Endavant!" + +#: FMain.form:154 +msgid "Host Name to IP" +msgstr "Nom de domini a IP" + +#: FMain.form:154 +msgid "IP to Host Name" +msgstr "IP a nom de domini" + +#: FMain.class:14 +msgid "Not Found" +msgstr "No s'ha trobat" + +#: FMain.class:295 +msgid "Write here 7 host names then press GO!" +msgstr "Escriu 7 noms d'ordinador i pitja Endavant!" + +#: FMain.class:297 +msgid "Write here 7 IP addresses then press GO!" +msgstr "Escriu 7 adreces ip i pitja Endavant!" + +#: FMain.form:110 +msgid "www.debian.org" +msgstr "-" + +#: FMain.form:54 +msgid "www.freshmeat.net" +msgstr "-" + +#: FMain.form:49 +msgid "www.kudla.org" +msgstr "-" + +#: FMain.form:115 +msgid "www.mandrake.com" +msgstr "-" + +#: FMain.form:105 +msgid "www.rpmfind.net" +msgstr "-" + +#: FMain.form:100 +msgid "www.slashdot.org" +msgstr "-" + diff --git a/app/examples/Networking/DnsClient/.lang/cs.mo b/app/examples/Networking/DnsClient/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..96076430a907e14eaf64c5abebfe046b561e6d44 GIT binary patch literal 1307 zcmZXSyKfUg5XKh>FY^emfQUd^8XgK~pg<%hAWrrA?=jQW?p;rtxf^`mS3+p^q zxC?)GXD!2!{2@Zo3DN7)odjwtvJ@6v< z3_K0K2akduL5lYYJOcgzkAc5HikCrf+K*Ba)G!88eOHHioB*lrB1rjMknZ>pJOwH+ z13m>Q#0!w}zD&8EzJCW^g8u_}1^fmg9XqnO$KjO6Q=UvYlJZQ-vneUpNh~s|i;U_y zghhS*Yg7vv^_Gn8O#Pr(&~xlQrcvVy$#L zVR@lE8KwxO&kF7=_rgFb!{w@oTA^eGmE?OKC-oEq6(kL)r}PDOUnhpUB9h!_UaGND zjUM`5=4!)>x~+WnAP$V=4H-**3ra40KbA=%6K)z(@fL0PRQ1BY!Un}xi2t9gE}}(| zWE0(vJ-MXgy38tR*viUE)|ZQcP+9o8+a-i|IDzy)Ej}nv){R@v`ks%~Q(Ba`|2uP-O#6j%O;RiZiJ1M)rCpua&`vwdU4ZsK>}v zbFCGMK;7nEL&S+Rcjo7c*7!h2Wi83ra+Ih2K-F`6d@(Q?8l`Eyxe@DSVFvT0w%)ME zaeI3`Fg;4O`IgnTUvF+SL*a88t!8(S#7?hDrc-A@H?`V&E|ne(7JYc$N$0B58!PfL zS1J+84vAgS3bxmI$M4S-J~tP\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "DNS client example" +msgstr "Příklad DNS klienta" + +#: FMain.class:14 +msgid "Not Found" +msgstr "Nenalezeno" + +#: FMain.class:217 +msgid "Finished" +msgstr "Dokončeno" + +#: FMain.class:251 +msgid "Cancelled" +msgstr "Zrušené" + +#: FMain.class:295 +msgid "Write here 7 host names then press GO!" +msgstr "Napiš zde 7 host jmen a pak zmačkni GO!" + +#: FMain.class:297 +msgid "Write here 7 IP addresses then press GO!" +msgstr "Napiš zde 7 IP adres a pak zmačni GO!" + +#: FMain.form:29 +msgid "DnsClient Example" +msgstr "Příklad DnsKlienta" + +#: FMain.form:34 +msgid "Go!" +msgstr "Go!" + +#: FMain.form:44 +msgid "gambas.sourceforge.net" +msgstr "-" + +#: FMain.form:49 +msgid "www.kudla.org" +msgstr "-" + +#: FMain.form:54 +msgid "www.freshmeat.net" +msgstr "-" + +#: FMain.form:95 +msgid "Cancel" +msgstr "Zrušit" + +#: FMain.form:100 +msgid "www.slashdot.org" +msgstr "-" + +#: FMain.form:105 +msgid "www.rpmfind.net" +msgstr "-" + +#: FMain.form:110 +msgid "www.debian.org" +msgstr "-" + +#: FMain.form:115 +msgid "www.mandrake.com" +msgstr "-" + +#: FMain.form:154 +msgid "Host Name to IP" +msgstr "Host name z IP" + +#: FMain.form:154 +msgid "IP to Host Name" +msgstr "IP z Host name" + +#: FMain.form:159 +msgid "<< Copy" +msgstr "<< Koírovat" + +#: FMain.form:164 +msgid "Asynchronous" +msgstr "Asynchronně" diff --git a/app/examples/Networking/DnsClient/.lang/de.mo b/app/examples/Networking/DnsClient/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..fd8e0976943efa2719842efd9cccf2ccc53bf4e4 GIT binary patch literal 1349 zcmaiyOOG2x5XTz`B$!7+LLw0X>Rbp>G7dRJ**F`Li9L3#j33%ZAl%wBWlv|v-Bx#- z&3*^2@sz%@5G@d1#yAUJS9+>tmS@vrgh14R*K*ZlfXQ&m${znnhywSb(3z7PEY z`T?|j2@lAx;BoLb@J;X!@HOx+@OAJX@HBY*Wg*@H&x0qxE8r{Ob?_v3vt}RU_a1>C zgBkb{_!W2#{0@8@{1Ie-KZ9?9e}bpLzd`obfOFoT=OD=8GWZ6TSB|axuhqN<@_7n= z2!3937v%4F3gR!m!Q(yf+nUcn&f_QW68JlK5j^>76;9l!d9&uNnzw7-sTtR7*W`51 zKv}pBEbl@&N6u$oI5rE{mW98W>%p~XKv|Gd)f--;ECL~_+H;?{pIjfYzAoE#u_t1` zXR^XsW0zjEmC2~AQBp2C!%dl$noN-NM9mi^iH`B@)k#vHbhXjGfOXGa5cjPQa;WAc zgO&ZU=#P1Ea4Lp2$gW+QTzuwqAX$(jc>|VG<+&s8$;(iXkqh3)-sr-A4GZ>f;Qu+Z znVL_OZ+g3Q8BML5QPWTmySuy1oF-bCCeEtu6t0Uosc>Mhv&>6%a6eZjcWQ^4nVnZJ zxW#;`O@3hROXZ8)hU$&6vyUkYQ9q9!lJnY{mK?|J2XQZv50j0}{%F`3kD?9wQuAfh zQGr_WQ4()NgHhCp<^54>(7*h}XqYro\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "DNS client example" +msgstr "Beispiel für einen DNS-Client" + +#: FMain.class:14 +msgid "Not Found" +msgstr "Nicht gefunden" + +#: FMain.class:217 +msgid "Finished" +msgstr "Fertig" + +#: FMain.class:251 +msgid "Cancelled" +msgstr "Abgebrochen" + +#: FMain.class:295 +msgid "Write here 7 host names then press GO!" +msgstr "Gib 7 Hostnamen ein und klicke auf Los!" + +#: FMain.class:297 +msgid "Write here 7 IP addresses then press GO!" +msgstr "Gib 7 IP-Adressen ein und klicke auf Los!" + +#: FMain.form:29 +msgid "DnsClient Example" +msgstr "Beispiel für einen DNS-Client" + +#: FMain.form:34 +msgid "Go!" +msgstr "Los!" + +#: FMain.form:44 +msgid "gambas.sourceforge.net" +msgstr "-" + +#: FMain.form:49 +msgid "www.kudla.org" +msgstr "-" + +#: FMain.form:54 +msgid "www.freshmeat.net" +msgstr "-" + +#: FMain.form:95 +msgid "Cancel" +msgstr "Abbrechen" + +#: FMain.form:100 +msgid "www.slashdot.org" +msgstr "-" + +#: FMain.form:105 +msgid "www.rpmfind.net" +msgstr "-" + +#: FMain.form:110 +msgid "www.debian.org" +msgstr "-" + +#: FMain.form:115 +msgid "www.mandrake.com" +msgstr "-" + +#: FMain.form:154 +msgid "Host Name to IP" +msgstr "Host Name nach IP-Adresse" + +#: FMain.form:154 +msgid "IP to Host Name" +msgstr "IP-Adresse nach Host Name" + +#: FMain.form:159 +msgid "<< Copy" +msgstr "<< Kopieren" + +#: FMain.form:164 +msgid "Asynchronous" +msgstr "Asynchron" + diff --git a/app/examples/Networking/DnsClient/.lang/es.mo b/app/examples/Networking/DnsClient/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..fe91aed56aecd4d0fa597452711ebbf4c6225322 GIT binary patch literal 1038 zcmc(cJ8u&~5XUbNUM9R!Ktr*iL+A`BO=L`zlYL1}oX?i+M1qR--P+#dZrALs^JvoX z4R{n()QE}_D(ESw5fUGO3Q;2QpR;iaJ^&-l{dRU{cmB8YZE^m&1G@yj4u1x}249%N z2Q~!HfD&8)pMa;qBk&6N6g&yO1nG`fQ@#POB7P5E1iye({~f#l{sb?Bb0;R}7D0+{ zfWN?H@Emv(ms8(m@GQ6n(!BwA3VaNb-u{$F)BFpN`o0CJ?+1|jeg>(}SCI7nn8v?B zNGfWm&v`f*{X5-5wJ0f!`jU}8vJxC}3!N1IwRtS)tqQAFSxt`)9WOsr@z7|c$GKDE zDi*0zS9vX!LRsb?@NAR{XG1SJn>x2F;+bGpv!LY!En3WWodM5!JTK>ZY+}*ZW+2K+ zSZ9BKznqAkQ5-tYCMXqg4Lsr^YL|H cmYhguJtHWW3$rH^Z5W@9lr!~D^1qGt4>*VY7XSbN literal 0 HcmV?d00001 diff --git a/app/examples/Networking/DnsClient/.lang/es.po b/app/examples/Networking/DnsClient/.lang/es.po new file mode 100644 index 00000000..2e67b97d --- /dev/null +++ b/app/examples/Networking/DnsClient/.lang/es.po @@ -0,0 +1,67 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FMain.class:356 +msgid "DnsClient Example" +msgstr "Ejemplo de Cliente DNS" + +#: FMain.class:361 +msgid "Go!" +msgstr "¡Ir!" + +#: FMain.class:371 +msgid "gambas.sourceforge.net" +msgstr "gambas.sourceforge.net" + +#: FMain.class:376 +msgid "www.kudla.org" +msgstr "www.kudla.org" + +#: FMain.class:381 +msgid "www.freshmeat.net" +msgstr "www.freshmeat.net" + +#: FMain.class:422 +msgid "Cancel" +msgstr "Cancelar" + +#: FMain.class:427 +msgid "www.slashdot.org" +msgstr "www.slashdot.org" + +#: FMain.class:432 +msgid "www.rpmfind.net" +msgstr "www.rpmfind.net" + +#: FMain.class:437 +msgid "www.debian.org" +msgstr "www.debian.org" + +#: FMain.class:442 +msgid "www.mandrake.com" +msgstr "www.mandrake.com" + +#: FMain.class:481 +msgid "Host Name to IP" +msgstr "Nombre de servidor a IP" + +#: FMain.class:481 +msgid "IP to Host Name" +msgstr "IP a nombre de servidor" + +#: FMain.class:486 +msgid "<< Copy" +msgstr "<< Copiar" + +#: FMain.class:491 +msgid "Asynchronous" +msgstr "Asincrónimo" diff --git a/app/examples/Networking/DnsClient/.lang/nl.mo b/app/examples/Networking/DnsClient/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..1d0fd80a95cc1db8a1a327e3f68f22ecb4a83cc2 GIT binary patch literal 1318 zcmZXS&ud z|0c+9jKQnm805I`KpYo8fPCMN6@LQx-e17a!8hP-@Gp?#KRtEG>5A7Y-mEAqepc~z zMefNJ3^wiq8`r}%a$nyY_kfLi%f@f!`nV>pn+>Iu--hRCi@+7-oB2EOTe&}CeqOe1 zF%~U9HgW2#u_G_q%EXja$S4sD{WTe9noN-NO65gHV!`@ru3;a?y@VA~u~4BvW!EA0kq!Byr?Dc^OhNvfzd6uG~5vm~wuD z{QnCZs(efNy0;@2)3$X(sv8PoZ*Q-j(3V!F4qv(4M(Q*t6(#{Yqa;(4{al&EsU51v zHZKpjBHz{~nS}dH`82ViJTY+gCB-4?CD8^sudQjyL96|&)$PcQ&gxokrC%GYM60x` zc{5s2ftqr*F*6r69!ImYve9VH&fROwG#a%fhrg0D-enohKJun%$VTI;rmGI&dQmClWc^C=zdqyKssM0BVyqf`zV zC|7B$oR%u%9nsem\n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "DNS client example" +msgstr "DNS cliënt voorbeeld" + +#: FMain.class:14 +msgid "Not Found" +msgstr "Niet gevonden" + +#: FMain.form:29 +msgid "DnsClient Example" +msgstr "DnsCliënt Voorbeeld" + +#: FMain.form:34 +msgid "Go!" +msgstr "-" + +#: FMain.form:44 +msgid "gambas.sourceforge.net" +msgstr "-" + +#: FMain.form:49 +msgid "www.kudla.org" +msgstr "-" + +#: FMain.form:54 +msgid "www.freshmeat.net" +msgstr "-" + +#: FMain.form:95 +msgid "Cancel" +msgstr "Annuleer" + +#: FMain.form:100 +msgid "www.slashdot.org" +msgstr "-" + +#: FMain.form:105 +msgid "www.rpmfind.net" +msgstr "-" + +#: FMain.form:110 +msgid "www.debian.org" +msgstr "-" + +#: FMain.form:115 +msgid "www.mandrake.com" +msgstr "-" + +#: FMain.form:154 +msgid "Host Name to IP" +msgstr "Host naam naar IP" + +#: FMain.form:154 +msgid "IP to Host Name" +msgstr "IP naar Host naam" + +#: FMain.form:159 +msgid "<< Copy" +msgstr "<< Kopiëer" + +#: FMain.form:164 +msgid "Asynchronous" +msgstr "Asynchrone" + +#: FMain.class:217 +msgid "Finished" +msgstr "Beëindigt" + +#: FMain.class:251 +msgid "Cancelled" +msgstr "Geannuleerd" + +#: FMain.class:279 +msgid "Write here 7 host names then press GO!" +msgstr "Noteer hier 7 host namen en druk GO!" + +#: FMain.class:281 +msgid "Write here 7 IP addresses then press GO!" +msgstr "Noteer hier 7 IP adressen en druk GO!" + diff --git a/app/examples/Networking/DnsClient/.project b/app/examples/Networking/DnsClient/.project new file mode 100644 index 00000000..07834f95 --- /dev/null +++ b/app/examples/Networking/DnsClient/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.0 +Title=DNS client example +Startup=FMain +Icon=dnsclient.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.net +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@desnouettes +License=General Public Licence diff --git a/app/examples/Networking/DnsClient/.src/FMain.class b/app/examples/Networking/DnsClient/.src/FMain.class new file mode 100644 index 00000000..146aa10a --- /dev/null +++ b/app/examples/Networking/DnsClient/.src/FMain.class @@ -0,0 +1,308 @@ +' Gambas class file +'*********************************** +' We define 7 dns objects +'*********************************** +Private A As DnsClient +Private B As DnsClient +Private C As DnsClient +Private D As DnsClient +Private E As DnsClient +Private F As DnsClient +Private G As DnsClient + +Private Working As Integer +Private sNoResult As String = ("Not Found") + +Public Sub Button1_Click() + Label2.Text = "" + Label3.Text = "" + Label4.Text = "" + Label6.Text = "" + Label7.Text = "" + Label8.Text = "" + Label9.Text = "" + Working = 7 + Button1.Enabled = False + Button1.Enabled = False + ComboBox1.Enabled = False + Button3.Enabled = False + CheckBox1.Enabled = False + Label5.Text = "|" + Timer1.Delay = 200 + Timer1.Enabled = True + Select Case ComboBox1.Index + Case 0 + A.HostName = TextBox1.Text + B.HostName = TextBox2.Text + C.HostName = TextBox3.Text + D.HostName = TextBox4.Text + E.HostName = TextBox5.Text + F.HostName = TextBox6.Text + G.HostName = TextBox7.Text + A.GetHostIP() + B.GetHostIP() + C.GetHostIP() + D.GetHostIP() + E.GetHostIP() + F.GetHostIP() + G.GetHostIP() + Case 1 + A.HostIP = TextBox1.Text + B.HostIP = TextBox2.Text + C.HostIP = TextBox3.Text + D.HostIP = TextBox4.Text + E.HostIP = TextBox5.Text + F.HostIP = TextBox6.Text + G.HostIP = TextBox7.Text + A.GetHostName() + B.GetHostName() + C.GetHostName() + D.GetHostName() + E.GetHostName() + F.GetHostName() + G.GetHostName() + End Select + +End + +Public Sub A_Finished() + + Select Case ComboBox1.index + Case 0 + If A.HostIP = "" Then + Label2.Text = sNoResult + Else + Label2.Text = A.HostIP + End If + Case 1 + If A.HostName = "" Then + Label2.Text = sNoResult + Else + Label2.Text = A.HostName + End If + End Select + Working = working - 1 + +End + +Public Sub B_Finished() + + Select Case ComboBox1.index + Case 0 + If B.HostIP = "" Then + Label3.Text = sNoResult + Else + Label3.Text = B.HostIP + End If + Case 1 + If B.HostName = "" Then + Label3.Text = sNoResult + Else + Label3.Text = B.HostName + End If + End Select + Working = working - 1 + +End + +Public Sub C_Finished() + + Select Case ComboBox1.index + Case 0 + If C.HostIP = "" Then + Label4.Text = sNoResult + Else + Label4.Text = C.HostIP + End If + Case 1 + If C.HostName = "" Then + Label4.Text = sNoResult + Else + Label4.Text = C.HostName + End If + End Select + Working = working - 1 + +End + +Public Sub D_Finished() + + Select Case ComboBox1.index + Case 0 + If D.HostIP = "" Then + Label6.Text = sNoResult + Else + Label6.Text = D.HostIP + End If + Case 1 + If D.HostName = "" Then + Label6.Text = sNoResult + Else + Label6.Text = D.HostName + End If + End Select + Working = working - 1 + +End + +Public Sub E_Finished() + + Select Case ComboBox1.index + Case 0 + If E.HostIP = "" Then + Label7.Text = sNoResult + Else + Label7.Text = E.HostIP + End If + Case 1 + If E.HostName = "" Then + Label7.Text = sNoResult + Else + Label7.Text = E.HostName + End If + End Select + Working = working - 1 + +End + +Public Sub F_Finished() + + Select Case ComboBox1.index + Case 0 + If F.HostIP = "" Then + Label8.Text = sNoResult + Else + Label8.Text = F.HostIP + End If + Case 1 + If F.HostName = "" Then + Label8.Text = sNoResult + Else + Label8.Text = F.HostName + End If + End Select + Working = working - 1 + +End + +Public Sub G_Finished() + + Select Case ComboBox1.index + Case 0 + If G.HostIP = "" Then + Label9.Text = sNoResult + Else + Label9.Text = G.HostIP + End If + Case 1 + If G.HostName = "" Then + Label9.Text = sNoResult + Else + Label9.Text = G.HostName + End If + End Select + Working = working - 1 + +End + + + + + + +Public Sub Timer1_Timer() + + Timer1.Enabled = False + If Working = 0 Then + Label5.Text = ("Finished") + Button1.Enabled = True + Button3.Enabled = True + ComboBox1.Enabled = True + CheckBox1.Enabled = True + Else + Select Case Label5.Text + Case "|" + Label5.Text = "/" + Case "/" + Label5.Text = "-" + Case "-" + Label5.Text = "\\" + Case "\\" + Label5.Text = "|" + End Select + Timer1.Delay = 200 + Timer1.Enabled = True + + End If + +End + +Public Sub Button2_Click() + + If Timer1.Enabled = True Then + Timer1.Enabled = False + A.Stop() + B.Stop() + C.Stop() + D.Stop() + E.Stop() + F.Stop() + G.Stop() + Label5.Text = ("Cancelled") + Working = 0 + Button1.Enabled = True + ComboBox1.Enabled = True + Button3.Enabled = True + CheckBox1.Enabled = True + End If + +End + +Public Sub Form_Open() + + A = New DnsClient As "A" + B = New DnsClient As "B" + C = New DnsClient As "C" + D = New DnsClient As "D" + E = New DnsClient As "E" + F = New DnsClient As "F" + G = New DnsClient As "G" + CheckBox1_Click + ComboBox1_Click + Working = 0 +End + +Public Sub ComboBox1_Click() + + Select Case ComboBox1.Index + Case 0 + Label1.Text = ("Write here 7 host names then press GO!") + Case 1 + Label1.Text = ("Write here 7 IP addresses then press GO!") + End Select +End + +Public Sub Button3_Click() + + TextBox1.Text = Label2.Text + TextBox2.Text = Label3.Text + TextBox3.Text = Label4.Text + TextBox4.Text = Label6.Text + TextBox5.Text = Label7.Text + TextBox6.Text = Label8.Text + TextBox7.Text = Label9.Text + + +End + +Public Sub CheckBox1_Click() + + A.ASync = CheckBox1.Value + B.ASync = CheckBox1.Value + C.ASync = CheckBox1.Value + D.ASync = CheckBox1.Value + E.ASync = CheckBox1.Value + F.ASync = CheckBox1.Value + G.ASync = CheckBox1.Value + +End diff --git a/app/examples/Networking/DnsClient/.src/FMain.form b/app/examples/Networking/DnsClient/.src/FMain.form new file mode 100644 index 00000000..1427b81d --- /dev/null +++ b/app/examples/Networking/DnsClient/.src/FMain.form @@ -0,0 +1,122 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(26,20,63,51) + Text = ("DnsClient Example") + Resizable = False + { Button1 Button + MoveScaled(1,45,16,4) + Text = ("Go!") + } + { Label1 Label + MoveScaled(1,6,61,4) + Text = ("") + } + { TextBox1 TextBox + MoveScaled(1,11,28,4) + Text = ("gambas.sourceforge.net") + } + { TextBox2 TextBox + MoveScaled(1,15,28,4) + Text = ("www.kudla.org") + } + { TextBox3 TextBox + MoveScaled(1,19,28,4) + Text = ("www.freshmeat.net") + } + { Label2 Label + MoveScaled(30,11,32,4) + Font = Font["Bold"] + Background = &H5500FF& + Foreground = &HFFFF00& + Text = ("") + } + { Label3 Label + MoveScaled(30,15,32,4) + Font = Font["Bold"] + Background = &H5500FF& + Foreground = &HFFFF00& + Text = ("") + } + { Label4 Label + MoveScaled(30,19,32,4) + Font = Font["Bold"] + Background = &H5500FF& + Foreground = &HFFFF00& + Text = ("") + } + { Timer1 #Timer + #X = 204 + #Y = 300 + } + { Label5 Label + MoveScaled(18,45,27,4) + Font = Font["12,Bold"] + Background = &HAA0000& + Foreground = &HFFFFFF& + Text = ("") + Alignment = Align.Center + } + { Button2 Button + MoveScaled(46,45,16,4) + Text = ("Cancel") + } + { TextBox4 TextBox + MoveScaled(1,23,28,4) + Text = ("www.slashdot.org") + } + { TextBox5 TextBox + MoveScaled(1,27,28,4) + Text = ("www.rpmfind.net") + } + { TextBox6 TextBox + MoveScaled(1,31,28,4) + Text = ("www.debian.org") + } + { TextBox7 TextBox + MoveScaled(1,35,28,4) + Text = ("www.mandrake.com") + } + { Label6 Label + MoveScaled(30,23,32,4) + Font = Font["Bold"] + Background = &H5500FF& + Foreground = &HFFFF00& + Text = ("") + } + { Label7 Label + MoveScaled(30,27,32,4) + Font = Font["Bold"] + Background = &H5500FF& + Foreground = &HFFFF00& + Text = ("") + } + { Label8 Label + MoveScaled(30,31,32,4) + Font = Font["Bold"] + Background = &H5500FF& + Foreground = &HFFFF00& + Text = ("") + } + { Label9 Label + MoveScaled(30,35,32,4) + Font = Font["Bold"] + Background = &H5500FF& + Foreground = &HFFFF00& + Text = ("") + } + { ComboBox1 ComboBox + MoveScaled(1,1,37,4) + Text = (" ") + ReadOnly = True + List = [("Host Name to IP"), ("IP to Host Name")] + } + { Button3 Button + MoveScaled(24,40,12,4) + Text = ("<< Copy") + } + { CheckBox1 CheckBox + MoveScaled(41,1,21,4) + Text = ("Asynchronous") + } +} diff --git a/app/examples/Networking/DnsClient/dnsclient.png b/app/examples/Networking/DnsClient/dnsclient.png new file mode 100644 index 0000000000000000000000000000000000000000..5c2ca2807566beed571e49547ce8f3e792b3848b GIT binary patch literal 1808 zcmV+r2k-caP)aj`L&SAZrIN1%`ZksFDo;Sqrnzfd(QYSXk)(H$=t;7KT%88Y#!_^EENu1q;4fO6LrO;4iv zg3Vi>m=@5TE+NEriJUK?R{AokC?XFsA>pzjlIAAO3QS@TJ7IZ46z-&=sUFIeBT23;k(?P^bv&R2TtHe%$Au zJKt|f-+wJ!rR{oW|8v$o0W99UcScCp4=-<@)%LBOkA!L}LnMcar1B0mVTFbo17$e! z?Nj|+ixiC|`w5Z0_fk6i1c`B=7^j^=9&K1a+7SC*GLlp{^>of45 zA%1uGl>4{SSK_X8mhM~~y$wPgKWW*tE9lwU@n@f0J9kIdGA(CG@?k`mTd3Rcb54sMrWa5QxNxMx#Wcm8hzQ?}M*sDAJ{|#^9s# zeGFzx+~3+jb)`W-6NG~rt#cd1u@mR3GFf-l`khbz;nMqm8VB%J2Rv0r{q?Qwnoxgw z;H9SxLzn1*06|TnDn4e>CXvdLw|qk3DB+-nloH$ZupO6FDnV5U9_w7h!nrf3t_+P9 zkWwQtwc^CjOvM?0T_dWN_20a$h@H`JiQ4os2_aR>> z@vp%G>1+Yl1J8$mrlP3=RS2TdN?MxhC|M4U>)$3c*LCPzF`soS=IBC-eMMzVdIF%~ zhmY1G>3n=`yC@d(Xj-5g=((J`*h}~20U8>bXlR~YmaS6WnUoTMwwVUE;t786%Okw` z=M%jDj|*HK82qH#rNh?8R*FDC?L2eldhLi91%PkcU7f4iWKcLrDN)t3G%oe_6OL5U z+4&HS^|crQ&{fa`*p7$m`M93Pdmo&p47zF_NrCD_q zBy$$0ds9S1L5zUUeJu+ZGD|qFM?fo|O3v{pST^~Bg;}%-xr5BCTR7ShBc#(TU7;b^**G3JEc&0#QBup?;MI<(pe9NMuYB8I$CYNhVh$n=jEn&`-yL`Q!8;krIZb!TdS(T)cV%%d&Xts}GROb&ZHHGf-{tm(|>`%b}0000_H=XZYh!c|~ACz^m(^DdSBj0mz&u6LY}KrNnPw| z!)%%{hK-9&R?JbmzyDJ2HJ6*%oCMGYT+ht9X>F)3)mbA6vHLGSBO;vB1^#MRpIK38 zE_hIF;8d@`)0dlG3Z6Dj(&d%MzmoUh)N|HM1YCXL0st({o!jp(GW;jI=cuTO&6f9N;GFh zh}l<`5A;PP)V*r~&cuGP_0hvW3x}-x1;RJ3ijvuaE6W7{lAakHJ2sf)aLnB$e)MWv zWc8v9r6(AMf%MCLv%d`p7#Ta35{@)SJ?o>Uc=TYiGbtbNTk?t&z;X4^#P3>bW4T(y z(kyEdVIgKW@Mi^5PELFsHyN{}khUc!&$yJc8uv2;Yf4y?LfV#6GN!D_jFr4XO+ZXm zK)EtzDXHwKrg1U(YtN;*;R7Pg$5?mE!)VV%Dr@coUjWP$VoEC8vZRXpuFj_HRTl> zF&`a7DuELl!b(d=%1bAPnZnHHs+gsej8C%wGdBxxoj6@*n(>vEf-(5E+)kw96xZhX zIKB8flu(>H_&Q71-a>j-{-qOO%KEO9j$1+r+Y)lhg%`FFI{emCbe%egXTV**iLCHE zbkZS|lS{mzhGPd>$Sd8*{#PHPsqX|!)_(zc30E93r2ytAVOy3E_QhEDU;fdT_}1^= z!B?JmirxSAul(&x@8B%1K&7RV=Yyzl*|+}{toFnB78a789mG-+bs2LGO9`E7PrSF<7#Awx*kRaLO=_6ImP;G&F& zcH*El2tlH+oh6z11Vb5@RDh}0CZ(~ZPbynV!K5Z;ZpL#N>~E)|r-{a+Z?iI|owBW4 z$pq5#^J(wy;nBZ;oMc)CN!LT$QM40Bj0PajiDOzDS&&nLW%)k!zb<;4!qX>}Z3$t9 zlt38H768l)v(iz2kRe}^m`-rKvyHo#=b`gTc($gU9Y;HP?#Z81^V;i((iJ>?poOML z94QzlZ;Zj|YNe{Aih`2zOE>jnGd{rsQnzJ>ET6J^gfv1<5a3+3kbSj#C=Cx0@y}-@ zEtmO1vbwC0!~JozfuB9Si!a@I8)-Lwm70^SbR2Fap}`7e;EfC;ltrSug`&7i#rn;t zIRyV?B948go1~LKC`BL?CX|_r_znIv(FF_$pR#(afbx2UP~*QfAdH!OcW>-@f%|W* z=9ecFgSLkj4nbj%XzZkmc3o8Y27da3zreR(F`{fOfdxy@aT{mfe-U0@jldA@?%;;) zJEm<*&lj=bGsJsNvT1o1ODnUPmyylK^}V!qx6;vA$B4|Qq-^E5#Z41H2_Z(OS8Ie+ zUaw_@=vERbWh(XwgP7<7AHKbdhqjb)C?25S_!wzDO6ID~j0_DDbzE{nnOI69grMl! zTY2)|cjDB%Op~z44JiI@^OtFJ9143o*>=|-U+}DVwluLQ(8=%Kw+`F!$V?LqM`Og3 zF8#xCZdhAPPv0=_{Q7lPUw<1)P0uhbfiP!lebUvUTe|7NZc8GR6euZ>N(d=aHG!6f zS{5y|_@JSk4^OqC0wLU?e!OS|*O$iLlP!dj!vy_4JjbS|qm`dzFJsl|7VbOUNN?vL zJ;tJ-xt?3@+QHbkvu%;VKISDGc<|OW1pR`X%pgfEh?yW6K|j@HIfQ)hz}5;LzH>SI z-u~se$5LXPrzH_?dazpnJoaY1W2^?Cjj>$M%X6Is1HE-DuU^WFZ|$Wu5+g0Q2rE6z zV9g$~R&HXbznA+;1MH7y(6skW^0z%eYe`=9)9 z$7Vvo013zAbZ?CEvNDFFQI0pXQCSosJ1ZSXJ*55Vb8mAdyoREMizbcZvs8~jcmSR@ zeCOu0A`8H$ge?R?0BJyLK_U?&nCwOCrSxZ&AO`A4^tO{6^b@Ywz;MG+!np;!eX57d z_zA3VHj(2W;|qoeFIq()yMRAfy9Hk$I4APMkqFn8hY@&;#BADnN625e3?WjhieDr- zRNqcx`vCd#LfrnjD!#aNC4cim9VMlUC&vJBHatinY#4nmpoFneBABuOgw!B#>1uDJ z*Pn(R^%1UEk2^HL(8-T6+9kiZl=SEz!#+O)!;tP*EDAbU3bJyGSaZ$Ib0R*5mZo|> ze?tLv&4U=NS-q+Xzfa*L;tWJ$92rSwc-n20b%D z087Tll+hkSOM&wF3FjB%%giKtU>^x5h7f|lf+ZB%14zrFJE`#+4xyJ-leM@4lO7=G zMp>X-jvw7i)#~f#EI@2z7*~gg+8!!x9*u4N1pF3p$EB^kgYDOqp|vJ0;HS7?9`Efx zOk+zAMtjprdrFA$J|jf@tN_x)#MoY0rYyiO6(&?b$+8NT4nQQHKuAgV8_!d;aSLJ3 z5Xvp4(Hw&c+&w%hu0HfV!f$_+h7?kmZpR^hbLo+Zv&fDtBkRse-Pew74eAe@y+UEL7%PyPcc zvyj2IPH-J82{&!JmH#*8f$OVy zt=*yiz+QZ5D``2hmw~|^nsfxawG)+7i1hgwt0gHZsBPdN@&e1iqHCBc%ZNU3BhZ4774W7UEsJp0TGScl#qJ2w|C1h2li3omm%a>Ffb z*mf(N>Bbm=F=o7;=(iMy+WPRV{2fO6+WFJ({yDXcAJW`-gt76@r%e>$ri^QNca-?f zu78?zJZjkE*PmKn%TMZeOd3mI#28O&q6Ju%i0RSU(@Kbm)zd}gE7@*I_O>^(%!=`T zeG~IHZ%6w>G&VLPf*C|wP7xT1l3uYo1SsGld-* zj@9ss)>Z;}C5WLuDqNc{Z2KYrC+iOK>k~)lQE3DhmV=a-fi5!RBb0`MtSDVd(UKK3 z*MCAm@nX_K8NYGnzwzttVo4W3ZGV#FQ`8E zV8Tdil&3NiVt`aKHug%8!IR=s9uNEd0q)s$H&4Fw6uy<$;tri5&mSO}bPz(2Id48U z-FOEZtFNcA@fZgW?W3i)8%Jx3=I2qmWjkUPkDt^1nXD|>I6D)V2|`FQS^*!60RrH) z%D0f4ZNAKy_a9=8CD@J4J`VT=}b!c*E9uDCa3 z1V|y2mQn~IOorc#z^DsE`p-1D{!D)?;mNq27|oxqKm|a#5C{d7j`R;TjP(j2_qq34 z-V20t$~+@|SFivmp_7T>frhVbDZLTs9Tx!D4paakzzDz4%x{ud?Rt7)8&Y8LZ1E7{?8iw00%BXoZ!L%apn-XA@K?52_bF>i4Va4*`1^n-tqkW*kk*7`S0yBr#=^G zFQC7O{xAAD^k4763+;n@g?Iv#;AzlT{1m(&_N{9D%gX*1d>QlKgO7s0fotIJ-~-^f z`^KCGPr<$j;t_LrJp*2@<`WQG#Czbg;QQc1U?1drKL**a&pdiv*07(8z5W~Es*bB2eCy+kk8LS&Z`DFj+-DJ@eN+Q z{#`{3M|rF90PPI=Q|KJ)LdSooCso8rOy! z=Y-?n8nOS6p|k(u)n!|Bb>4R}Xj|0lbMrE`+q`p^9EF0^Uz+r_-gUY7qK7X5+0{Dc$CkaWm8Y&Yiq7jU z4Q-!{lKYBupj{)2>vScAa@lBjKnL0wDr}&hEM%@IwWIA9#LD_wtCNV9bGS>awUSA( z*6D~VNz$ftycd(C2C`QgCEiXpHpSL8(dI)6r7}+vL0YG8jALv|?+>iYMB7MRh;5ZB zz0a{^GDvp36KpAVwl^piJ0&Jhd>MrzcD$mtvx6azoY+GM@ytvvd%MzSHmzHi&+I8v z3Vie-(iN>|(L2g{6d)${dQF8|dn?*j`!5;dA}v+)}2Mr}j- z5G76)-pF8GOjqH#rj8t1LznZq=fPV;xo2%%`8V->k@pdKlqlJY>BPoxbA7XQ$B|~e zQA51~QXeJ55)K442s0(;WS-JJ>AVUHJIQJ^cjr6yqpMuhDpH#vWiid|Y6N2-FcYm> z*YhzI<;e64v-30_E)+DmK+XBuyHVS!Oo!;$&6t*vbV#9gmsC@I^aIx+Vs(zi$x~wK z=c zVSqC;MY%L8K)vU^CS@?A2dYo17zsC>#7C6#Ha@TC|MH3ystC-$23&odZ-g;b!VL1Nc;yA CZkP4| literal 0 HcmV?d00001 diff --git a/app/examples/Networking/HTTPGet/.lang/ca.po b/app/examples/Networking/HTTPGet/.lang/ca.po new file mode 100644 index 00000000..0f3e76a4 --- /dev/null +++ b/app/examples/Networking/HTTPGet/.lang/ca.po @@ -0,0 +1,124 @@ +# Catalan translation of HTTPGet +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the HTTPGet package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: HTTPGet\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:10+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FConfig.form:46 +msgid "127.0.0.1:3128" +msgstr "-" + +#: FConfig.form:87 +msgid "Authentication" +msgstr "Autenticació" + +#: F.form:82 +msgid "Basic" +msgstr "Bàsic" + +#: F.class:243 +msgid "Cancelled by user" +msgstr "Canceŀlat per l'usuari" + +#: FConfig.form:65 +msgid "Close" +msgstr "Tanca" + +#: F.form:91 +msgid "&Configuration..." +msgstr "&Configuració..." + +#: F.class:142 +msgid "Connected, waiting for reply..." +msgstr "Connectat, s'està esperant resposta..." + +#: F.class:101 +msgid "Connecting..." +msgstr "S'està connectant..." + +#: FConfig.form:60 +msgid "Cookies file (if you leave it blank HttpClient will not manage cookies)" +msgstr "Fitxer de galetes (si el deixeu en blanc, l'HttpClient no gestionarà les galetes)" + +#: F.form:82 +msgid "DIGEST" +msgstr "-" + +#: F.class:188 +msgid "Error " +msgstr "-" + +#: F.form:24 +msgid "GET!" +msgstr "OBTÉ!" + +#: F.form:82 +msgid "GSS" +msgstr "-" + +#: .project:1 +msgid "HTTP client GET example" +msgstr "Exemple GET de client HTTP" + +#: F.form:43 +msgid "http://gambasdoc.org/help" +msgstr "-" + +#: F.form:82 FConfig.form:25 +msgid "NTLM" +msgstr "-" + +#: F.class:203 +msgid "OK" +msgstr "D'acord" + +#: F.form:62 +msgid "Page needs authorization" +msgstr "La pàgina necessita autorització" + +#: F.form:74 FConfig.form:73 +msgid "Password" +msgstr "Contrasenya" + +#: FConfig.form:25 +msgid "Plain" +msgstr "Pla" + +#: F.class:157 +msgid "Receiving data..." +msgstr "S'està rebent dades..." + +#: F.form:57 +msgid "Stop" +msgstr "Atura" + +#: FConfig.form:55 +msgid "Update cookies file" +msgstr "Actualitza el fitxer de galetes" + +#: F.form:38 +msgid "URL :" +msgstr "-" + +#: FConfig.form:41 +msgid "Use Proxy" +msgstr "Usa servidor intermediari" + +#: F.form:68 FConfig.form:80 +msgid "User" +msgstr "Usuari" + diff --git a/app/examples/Networking/HTTPGet/.lang/cs.mo b/app/examples/Networking/HTTPGet/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..06006593e382822707e1f1c0582dba3d5961df16 GIT binary patch literal 1601 zcmY+C&u<$=6vqcBzucBW`B4f;3_?Vy=sI-~YGg{OvA2#DJ2w7NiEtY4PV9--JIm~> z6X$Q>#|fdT;*cv+1VTMkCNbKdYG{%z>mRaz#Z@@@RO7`!AGHgo$h~|>OX+5!u}(868sG;gMWaJ zfma@x(*=(~uY&lp5Absa?4SK_f!3+ls|(nziq&6-d6QHuzX4Jdk1}xw5<)h;YDHtc{9+LIiwE1pyDWUkcVx4A{=UFF$@!wcxsjyBgdBf=KG5s^Fj}-DtOFc2{ zirCj)R-4{ZUj`Awp-q&_srb%yX<{@W&l`o}YN71%>u$4Esjp=kb*CvuiZ-1Rp33tN z-9po;)typ-uhjFk%A4!;H8)cgv31%;gmECOHhF%nQ?2r~Le1ru-CChiy;>-hnr^Fg z1xLePg5;gH6vI5P7S_rgdh&9$dNoiK-qD#n&(tb4_b9$x_Cf}2wCIS_9!H4B%5A$8 zQK{bHUSF74+IKtcYtHh~ISOk-8pjPi?W2-;zT8zd!yLA-dN_Zcq|!PxaI;o;63V(s zrn@rKJelsR5z-Ijkgbc+^u}b*vc~j=icrE(pXUdk%E4rJ$V0)kAL-G-&4bU$VR}Z7 zCcEKeZ`M7XbTKIN@!^XDe>~mC9PEhc?!XK}iFR7?zsbX9WQ3P&9ya(1u z#AI&}2%poK4Q3NJ-#N;K;)<<75Xfsq#(|z+RGX8%A;o<-Tja1Xu!#uNPO{14{|3uC Q(|zR4qxo\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTTP client GET example" +msgstr "Příklad GET klienta" + +#: F.class:101 +msgid "Connecting..." +msgstr "Připojování..." + +#: F.class:142 +msgid "Connected, waiting for reply..." +msgstr "Připojeno, čekám na odpověď..." + +#: F.class:158 +msgid "Receiving data..." +msgstr "Přijímám data..." + +#: F.class:162 +msgid "&1 bytes" +msgstr "-" + +#: F.class:195 +msgid "Error " +msgstr "Chyba" + +#: F.class:210 +msgid "OK" +msgstr "-" + +#: F.class:250 +msgid "Cancelled by user" +msgstr "Zrušeno uživatelem" + +#: F.form:24 +msgid "GET!" +msgstr "-" + +#: F.form:38 +msgid "URL :" +msgstr "-" + +#: F.form:43 +msgid "http://gambasdoc.org/help" +msgstr "-" + +#: F.form:48 +msgid "Stop" +msgstr "-" + +#: F.form:53 +msgid "Page needs authorization" +msgstr "Autorizace na stránce" + +#: F.form:59 FConfig.form:80 +msgid "User" +msgstr "Uživatel" + +#: F.form:65 FConfig.form:73 +msgid "Password" +msgstr "Heslo" + +#: F.form:73 +msgid "Basic" +msgstr "-" + +#: F.form:73 +msgid "DIGEST" +msgstr "-" + +#: F.form:73 +msgid "GSS" +msgstr "-" + +#: F.form:73 FConfig.form:25 +msgid "NTLM" +msgstr "-" + +#: F.form:82 +msgid "&Configuration..." +msgstr "&Konfigurace..." + +#: FConfig.form:25 +msgid "Plain" +msgstr "-" + +#: FConfig.form:41 +msgid "Use Proxy" +msgstr "Užít proxy" + +#: FConfig.form:46 +msgid "127.0.0.1:3128" +msgstr "-" + +#: FConfig.form:55 +msgid "Update cookies file" +msgstr "Aktualizuj soubor cookie" + +#: FConfig.form:60 +msgid "Cookies file (if you leave it blank HttpClient will not manage cookies)" +msgstr "Soubor cookie (když nezaškrknete tak HttpClient nezpracuje cookie)" + +#: FConfig.form:65 +msgid "Close" +msgstr "Zavřít" + +#: FConfig.form:87 +msgid "Authentication" +msgstr "Autorizace" + diff --git a/app/examples/Networking/HTTPGet/.lang/de.mo b/app/examples/Networking/HTTPGet/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..751c9c77910a3d869ae7153917e4e8be9819da3a GIT binary patch literal 1608 zcmZ9L&u<$=6vqc>`C%wOLMgw+t}n&X4aaW z&94Io#6Q3RIB=+X1BAqh8xn_dLKXZ4ls^F>IB?|qoK2!K>$e}jnRzqs*WRBy^SQ!! z74jP7AIL?>y@&9^`0!z+o&z=bG?*3q1Uw7=sb#!g1Xqgr9tab43w#NDAAB6lLAm#1Q2hE7lykl)=Dz};hkgfq0{o#^ z{|S5<`p=+3Z1+L2{{zMn>R+Jfe}j*KXK~3Z;5o1aUIrzuB~Z@of+6?;DE7BOiSJIK ze_ilf@O8|8SLnYKyay8Ie+4D3KS8Op>4DA-R*f%8>j>Y%-*FPeG(FBxN$hKU_2OLv$HZ8yPP^o`i@` zQb+L-p(u4}Ewv*v&YkuqwdHbIEna)Od=<}PWohx+vI=uQVe3si+n`o;W@5FbZOlo+ zLmC`Vp0QJFNt&_3VU}ah!+F}%#v41PQR>L?G&zv7i*?K&Y3a7%j7BD5x@<;tkmi)I z-eoeL28p)YwBh}9Eiv$b_DqtHO+D>stH(@nIy1kbs`Xmb?WxE)+@)$!?@d+fcGX6& z*QWT$7L&yLdS{xj+Uhl$sdfI$jBTA|d#M|$cA||{9gf-TN-RU| zwK(4O=~VSQ4XUXA6q6^u%tBH9jH&I?{R8>u)C3_^78b^OXP~oT8kbWyUYKw)RoL_q z(iPN)!A*7<3Q(bTxV9PABDxuMy7kspsoe@XylZ4JsN$*$-HO6a&};?OkTzPCX8o=A zT3b=6p)()!oVHn_ed;Q--fuK$D{Mxz5;enm<3?DmcA{?gU2L`E9Ki=Y);kqygj=<~ zT)EO{+(?YLcec$fk`tEJ~MJ5Ni9kP`kbf}HaM-*C*ih3!YtQ%QJH%bG|vTEq^9$SmeH` z?Dllx+0zjBNFN174{o!uOh<;<6&0v;o+PMofM=CWHZ`2k=}8VDTXA1*dcbkZ_-M4mGwZpT^E`|1I8(6r53~z!hX4Qo literal 0 HcmV?d00001 diff --git a/app/examples/Networking/HTTPGet/.lang/de.po b/app/examples/Networking/HTTPGet/.lang/de.po new file mode 100644 index 00000000..baf3c9c0 --- /dev/null +++ b/app/examples/Networking/HTTPGet/.lang/de.po @@ -0,0 +1,117 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTTP client GET example" +msgstr "Beispiel für HTTP-Client GET" + +#: F.class:101 +msgid "Connecting..." +msgstr "Verbinden" + +#: F.class:142 +msgid "Connected, waiting for reply..." +msgstr "Verbunden, warte auf Antwort..." + +#: F.class:157 +msgid "Receiving data..." +msgstr "Empfange Daten..." + +#: F.class:188 +msgid "Error " +msgstr "Fehler" + +#: F.class:203 +msgid "OK" +msgstr "-" + +#: F.class:243 +msgid "Cancelled by user" +msgstr "Durch Benutzer abgebrochen" + +#: F.form:24 +msgid "GET!" +msgstr "-" + +#: F.form:38 +msgid "URL :" +msgstr "-" + +#: F.form:43 +msgid "http://gambasdoc.org/help" +msgstr "-" + +#: F.form:57 +msgid "Stop" +msgstr "-" + +#: F.form:62 +msgid "Page needs authorization" +msgstr "Seite verlangt Authentifizierung" + +#: F.form:68 FConfig.form:80 +msgid "User" +msgstr "Benutzer" + +#: F.form:74 FConfig.form:73 +msgid "Password" +msgstr "Passwort" + +#: F.form:82 +msgid "Basic" +msgstr "Einfach" + +#: F.form:82 +msgid "DIGEST" +msgstr "-" + +#: F.form:82 +msgid "GSS" +msgstr "-" + +#: F.form:82 FConfig.form:25 +msgid "NTLM" +msgstr "-" + +#: F.form:91 +msgid "&Configuration..." +msgstr "&Konfiguration..." + +#: FConfig.form:25 +msgid "Plain" +msgstr "Einfach" + +#: FConfig.form:41 +msgid "Use Proxy" +msgstr "Proxy verwenden" + +#: FConfig.form:46 +msgid "127.0.0.1:3128" +msgstr "-" + +#: FConfig.form:55 +msgid "Update cookies file" +msgstr "Cookie-Datei neu laden" + +#: FConfig.form:60 +msgid "Cookies file (if you leave it blank HttpClient will not manage cookies)" +msgstr "Cookie-Datei (wenn leer, verwaltet der HTTP-Client keine Cookies)" + +#: FConfig.form:65 +msgid "Close" +msgstr "Schließen" + +#: FConfig.form:87 +msgid "Authentication" +msgstr "Authentifizierung" + diff --git a/app/examples/Networking/HTTPGet/.lang/en_GB.mo b/app/examples/Networking/HTTPGet/.lang/en_GB.mo new file mode 100644 index 0000000000000000000000000000000000000000..d113d6840b74a30f365e9cf8784499d4bcdbbda0 GIT binary patch literal 1326 zcmbV~&yy2H6vta!5F;Y4hzOKCDcnM3vIGxqjX*IZLpH_ahslI0z4cBy*_O%7*wfkF z@HbfAC=V9@02e)Q^5}_^XAkO`zW^wTvix@PWbgFMn@_*@`p5e*|E#Xu6BuV$UVxuK z{(21`jN*V0hr#c`cfq^hQSb-m&)`Am57_@J>%W6%VSfa^4L$~&;1lo=cxe*#@x_d$2gFKmAZz6bp`@J;X!_Wucf2>owR zAjhYmOZ6ExJ3eQ=VE)T=mpT6d<_hx=^Dwi*JiUJ7(9PgFwbj{##xuZy1+Dc`1hc>M(>ZwKqw5!vUvfR>CW^$q^ z$rsx{6^&Li9K<3t26u^O7=J99gMrwL;~pi;y)cs6lhYzqq7z4L(Y+;l?vPBWQArX> z=0@LL=Gc>Extp7j=%rF;qOTIAcU&$bX{8H%VDmx@`w{tKSitx)mPIOJSSsq7d~fb% zMr!-r(3cdGzz_u5^r5zuDWZ?``Gw%Z9Sku0qj8<~}E3ZV+^;Em?_Sg7SX}8+petfm+r3%_;(Gf46 w7l_BIJ-c4GQhi0qwlt-(SBLRUZ)1O*3u~;57iLL5LM46L*wVHlyjObu1M8*sS^xk5 literal 0 HcmV?d00001 diff --git a/app/examples/Networking/HTTPGet/.lang/en_GB.po b/app/examples/Networking/HTTPGet/.lang/en_GB.po new file mode 100644 index 00000000..e1dc855c --- /dev/null +++ b/app/examples/Networking/HTTPGet/.lang/en_GB.po @@ -0,0 +1,120 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTTP client GET example" +msgstr "-" + +#: F.class:101 +msgid "Connecting..." +msgstr "-" + +#: F.class:142 +msgid "Connected, waiting for reply..." +msgstr "-" + +#: F.class:158 +msgid "Receiving data..." +msgstr "-" + +#: F.class:162 +msgid "&1 bytes" +msgstr "-" + +#: F.class:195 +msgid "Error " +msgstr "-" + +#: F.class:210 +msgid "OK" +msgstr "-" + +#: F.class:250 +msgid "Cancelled by user" +msgstr "-" + +#: F.form:24 +msgid "GET!" +msgstr "-" + +#: F.form:38 +msgid "URL :" +msgstr "-" + +#: F.form:43 +msgid "http://gambasdoc.org/help" +msgstr "-" + +#: F.form:48 +msgid "Stop" +msgstr "-" + +#: F.form:53 +msgid "Page needs authorization" +msgstr "-" + +#: F.form:59 FConfig.form:80 +msgid "User" +msgstr "-" + +#: F.form:65 FConfig.form:73 +msgid "Password" +msgstr "-" + +#: F.form:73 +msgid "Basic" +msgstr "-" + +#: F.form:73 +msgid "DIGEST" +msgstr "-" + +#: F.form:73 +msgid "GSS" +msgstr "-" + +#: F.form:73 FConfig.form:25 +msgid "NTLM" +msgstr "-" + +#: F.form:82 +msgid "&Configuration..." +msgstr "-" + +#: FConfig.form:25 +msgid "Plain" +msgstr "-" + +#: FConfig.form:41 +msgid "Use Proxy" +msgstr "-" + +#: FConfig.form:46 +msgid "127.0.0.1:3128" +msgstr "-" + +#: FConfig.form:55 +msgid "Update cookies file" +msgstr "-" + +#: FConfig.form:60 +msgid "Cookies file (if you leave it blank HttpClient will not manage cookies)" +msgstr "-" + +#: FConfig.form:65 +msgid "Close" +msgstr "-" + +#: FConfig.form:87 +msgid "Authentication" +msgstr "-" diff --git a/app/examples/Networking/HTTPGet/.lang/es.mo b/app/examples/Networking/HTTPGet/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..d72526ce5a87dc02a37404554e28a014bb6400e6 GIT binary patch literal 1308 zcmaKqJ!}+56vrpV3FJrum_!K{Mneh&iuUX?MXU`$Uf-?HC-y#cA4o|z-W%VIz1}&` z&iR8j1q~fam&R!*Xh4yYDh&rs3Mv{J1SM4pD@{a^|68vy0+Dw&``b7B_RW80|Ia4I ze`bg;FwSB;#F)Z}KEMU>96SZS06zowGLDTg_96098NUR}$fv-MKnae65qJdL$nqih z74jS4G4M7>eSXQF|C-I;$>#5Yr}6xE@MG{VkorCW&wx+Cf51I(0(^r7Yh7bI7+r()kmJKf8&`Iq(*E9J~vX&Obov_a{jDA7%58!Aay#KO*@vf$<3j1!<>#Cow+9prAh3RvPpTKF#iF9|u8aLb_=!ER_a* zOVXUig`$pERBvd6RXWb+^Q%B>cwwSy{aP)RIhH{k*jv{5(q6*NSWY-!VFjYz1NwX7bnw%oQ0db_|n&49bC zGw2HYdVy3Q>m-sljNaa%JHz_W_ zM8az0^2JUN@S0ck`4zwFRf2i1RBHOI)>V|oy&?AOw53>gdEnK`9a{2A5X?u4qcR3Sf;!`HJoTc^D$rve8TT`%!;rECJ$UP6h`U)G0K0fRW-T* literal 0 HcmV?d00001 diff --git a/app/examples/Networking/HTTPGet/.lang/es.po b/app/examples/Networking/HTTPGet/.lang/es.po new file mode 100644 index 00000000..3f8781f1 --- /dev/null +++ b/app/examples/Networking/HTTPGet/.lang/es.po @@ -0,0 +1,91 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FConfig.class:106 +msgid "Plain" +msgstr "Plano" + +#: FConfig.class:106 F.class:382 +msgid "NTLM" +msgstr "NTLM" + +#: FConfig.class:112 +msgid "Authentication" +msgstr "Autenticación" + +#: FConfig.class:118 F.class:367 +msgid "User" +msgstr "Usuario" + +#: FConfig.class:124 F.class:373 +msgid "Password" +msgstr "Contraseña" + +#: FConfig.class:142 +msgid "Use Proxy" +msgstr "Usar Proxy" + +#: FConfig.class:147 +msgid "127.0.0.1:3128" +msgstr "127.0.0.1:3128" + +#: FConfig.class:152 +msgid "TextBox1" +msgstr "TextBox1" + +#: FConfig.class:157 +msgid "Update cookies file" +msgstr "Actualizar achivos cookies" + +#: FConfig.class:162 +msgid "Cookies file (if you leave it blank HttpClient will not manage cookies)" +msgstr "Archivos Cookies (si lo deja en blanco HttpClient no manejará cookies)" + +#: FConfig.class:167 +msgid "Close" +msgstr "Cerrar" + +#: F.class:322 +msgid "GET!" +msgstr "¡Obtener!" + +#: F.class:337 +msgid "URL :" +msgstr "URL :" + +#: F.class:342 +msgid "http://gambasdoc.org/help" +msgstr "http://gambasdoc.org/help" + +#: F.class:356 +msgid "Stop" +msgstr "Detener" + +#: F.class:361 +msgid "Page needs authorization" +msgstr "Página necesita autorización" + +#: F.class:382 +msgid "Basic" +msgstr "Básico" + +#: F.class:382 +msgid "DIGEST" +msgstr "DIGEST" + +#: F.class:382 +msgid "GSS" +msgstr "GSS" + +#: F.class:390 +msgid "&Configuration..." +msgstr "&Configuración..." diff --git a/app/examples/Networking/HTTPGet/.lang/nl.mo b/app/examples/Networking/HTTPGet/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..1af67485f292bcf0464a181c1ac75ccfdd383325 GIT binary patch literal 1628 zcmZXSJ#$+{6owa)5K#Cukc5vJmLdtBaAiqm1|vcywxTE|vMnqrnW0ZS!SDy5fP#uLP{1%yQvfY6^w3c79_cz6V5QZgvmfWY=j{1q>G%f%*B%hz_sA_TEgVPx$$6a zTtC*t@5Z%-Bq7dMg}#$_bn%u*aEd0Fl&9kdwMzAssPbA= ztI6tWy}Ej_Qms@UBBvFslq%Pa*+pRtexzui7F?w-kYjdl4PinPXDd9x}9w+L}Qb-GQ5LQyI z+UqhNE1yw#qu;w0t=&7v3WwyQrj1>KqSocwK!=iu{?&@aWsbP{j(U$^wn$l<8zP^` z#Nn|=H1N6JLy@~C*ElOs$APm6S?<7i9HdL7vSA;m6ZXmb+HzpD(H>b|Yb+W`4k!!C zCTK+*Ad6O{kvvpJ9(B@JpP~&%gsr0YaAi!M>@vh%b9n%w|MlGzu?IvZneU?Ov{iD7 vYdQ-@otL5WNZew@bE({VLG0Kt#jD)yJNEvchAT9qH1kRneOFkhOC\n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTTP client GET example" +msgstr "HTTP client GET voorbeeld" + +#: F.form:24 +msgid "GET!" +msgstr "-" + +#: F.form:38 +msgid "URL :" +msgstr "-" + +#: F.form:43 +msgid "http://gambasdoc.org/help" +msgstr "-" + +#: F.form:48 +msgid "Stop" +msgstr "-" + +#: F.form:53 +msgid "Page needs authorization" +msgstr "Pagina vereist authorisatie" + +#: F.form:59 FConfig.form:80 +msgid "User" +msgstr "Gebruiker" + +#: F.form:65 FConfig.form:73 +msgid "Password" +msgstr "Wachtwoord" + +#: F.form:73 +msgid "Basic" +msgstr "-" + +#: F.form:73 FConfig.form:25 +msgid "NTLM" +msgstr "-" + +#: F.form:73 +msgid "DIGEST" +msgstr "-" + +#: F.form:73 +msgid "GSS" +msgstr "-" + +#: F.form:82 +msgid "&Configuration..." +msgstr "&Configuratie..." + +#: F.class:101 +msgid "Connecting..." +msgstr "Verbinding maken..." + +#: F.class:142 +msgid "Connected, waiting for reply..." +msgstr "Verbonden, wacht op antwoord..." + +#: F.class:158 +msgid "Receiving data..." +msgstr "Ontvang data..." + +#: F.class:162 +msgid "&1 bytes" +msgstr "-" + +#: F.class:195 +msgid "Error " +msgstr "Fout" + +#: F.class:210 +msgid "OK" +msgstr "-" + +#: F.class:250 +msgid "Cancelled by user" +msgstr "Geannuleerd door gebruiker" + +#: FConfig.form:25 +msgid "Plain" +msgstr "Plat" + +#: FConfig.form:41 +msgid "Use Proxy" +msgstr "Gebruik Proxy" + +#: FConfig.form:46 +msgid "127.0.0.1:3128" +msgstr "-" + +#: FConfig.form:55 +msgid "Update cookies file" +msgstr "Update cookies bestand" + +#: FConfig.form:60 +msgid "Cookies file (if you leave it blank HttpClient will not manage cookies)" +msgstr "Cookies bestand (indien leeg zal HTTP cliënt geen cookies beheren)" + +#: FConfig.form:65 +msgid "Close" +msgstr "Sluiten" + +#: FConfig.form:87 +msgid "Authentication" +msgstr "Autenticatie" + diff --git a/app/examples/Networking/HTTPGet/.project b/app/examples/Networking/HTTPGet/.project new file mode 100644 index 00000000..44bf68b9 --- /dev/null +++ b/app/examples/Networking/HTTPGet/.project @@ -0,0 +1,22 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=HTTP client GET example +Startup=FHttpGet +Icon=httpclient.png +Version=1.0.0 +Component=gb.image +Component=gb.qt4 +Component=gb.net +Component=gb.net.curl +Component=gb.qt4.ext +Description="HttpClient GET request example.\n\nThis example how to send a GET HTTP request to a server and get the response. You can configure an HTTP proxy if needed." +TabSize=2 +Translate=1 +Language=fr +ControlPublic=1 +Maintainer=benoit +Vendor=Example +Address=benoit@desnouettes +License=General Public Licence +Packager=1 +Tags=Network diff --git a/app/examples/Networking/HTTPGet/.src/ClsParams.class b/app/examples/Networking/HTTPGet/.src/ClsParams.class new file mode 100644 index 00000000..ceea7236 --- /dev/null +++ b/app/examples/Networking/HTTPGet/.src/ClsParams.class @@ -0,0 +1,8 @@ +' Gambas class file +Static Public UseProxy As Boolean +Static Public ProxyHost As String +Static Public ProxyUser As String +Static Public ProxyPwd As String +Static Public ProxyAuth As Integer +Static Public CookiesFile As String +Static Public UpdateCookies As Boolean diff --git a/app/examples/Networking/HTTPGet/.src/FConfig.class b/app/examples/Networking/HTTPGet/.src/FConfig.class new file mode 100644 index 00000000..61d97c8b --- /dev/null +++ b/app/examples/Networking/HTTPGet/.src/FConfig.class @@ -0,0 +1,75 @@ +' Gambas class file + +PUBLIC SUB ChkProxy_Click() + + + IF ChkProxy.Value = TRUE THEN + TxtProxy.Enabled = TRUE + LblAuth.Enabled = TRUE + LblProxyUser.Enabled = TRUE + TxtProxyUser.Enabled = TRUE + LblProxyPassword.Enabled = TRUE + TxtProxyPassword.Enabled = TRUE + CmbProxyAuth.Enabled = TRUE + ELSE + TxtProxy.Enabled = FALSE + LblAuth.Enabled = FALSE + LblProxyUser.Enabled = FALSE + TxtProxyUser.Enabled = FALSE + LblProxyPassword.Enabled = FALSE + TxtProxyPassword.Enabled = FALSE + CmbProxyAuth.Enabled = FALSE + END IF + +END + + +PUBLIC SUB Form_Open() + + + TxtCookies.Text = ClsParams.CookiesFile + ChkUpdate.Value = ClsParams.UpdateCookies + + ChkProxy.Value = ClsParams.UseProxy + TxtProxy.Text = ClsParams.ProxyHost + TxtProxyUser.Text = ClsParams.ProxyUser + TxtProxyPassword.Text = ClsParams.ProxyPwd + + + SELECT CASE ClsParams.ProxyAuth + CASE Net.AuthBasic + CmbProxyAuth.Index = 0 + CASE Net.AuthNTLM + CmbProxyAuth.Index = 1 + END SELECT + + +END + +PUBLIC SUB Form_Close() + + ClsParams.CookiesFile = TxtCookies.Text + ClsParams.UpdateCookies = ChkUpdate.Value + + ClsParams.UseProxy = ChkProxy.Value + ClsParams.ProxyHost = TxtProxy.Text + ClsParams.ProxyUser = TxtProxyUser.Text + ClsParams.ProxyPwd = TxtProxyPassword.Text + + IF ClsParams.ProxyUser = "" AND ClsParams.ProxyPwd = "" THEN + ClsParams.ProxyAuth = Net.AuthNone + ELSE + SELECT CASE CmbProxyAuth.Index + CASE 0 + ClsParams.ProxyAuth = Net.AuthBasic + CASE 1 + ClsParams.ProxyAuth = Net.AuthNTLM + END SELECT + END IF +END + +PUBLIC SUB Button1_Click() + + ME.Close + +END diff --git a/app/examples/Networking/HTTPGet/.src/FConfig.form b/app/examples/Networking/HTTPGet/.src/FConfig.form new file mode 100644 index 00000000..9aa7da91 --- /dev/null +++ b/app/examples/Networking/HTTPGet/.src/FConfig.form @@ -0,0 +1,64 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(34.5714,30.7143,38,45) + Resizable = False + { CmbProxyAuth ComboBox + MoveScaled(16,6,21,4) + Enabled = False + ReadOnly = True + List = [("Plain"), ("NTLM")] + } + { TxtProxyPassword TextBox + MoveScaled(16,16,21,4) + Enabled = False + Password = True + } + { TxtProxyUser TextBox + MoveScaled(16,11,21,4) + Enabled = False + } + { ChkProxy CheckBox + MoveScaled(1,1,14,4) + Text = ("Use Proxy") + } + { TxtProxy TextBox + MoveScaled(16,1,21,4) + Text = ("127.0.0.1:3128") + } + { TxtCookies TextBox + MoveScaled(1,29,36,4) + } + { ChkUpdate CheckBox + MoveScaled(1,34,27,4) + Text = ("Update cookies file") + } + { TextLabel1 TextLabel + MoveScaled(1,22,36,7) + Text = ("Cookies file (if you leave it blank HttpClient will not manage cookies)") + } + { Button1 Button + MoveScaled(11,40,16,4) + Text = ("Close") + Default = True + Cancel = True + } + { LblProxyPassword Label + MoveScaled(1,16,13,4) + Enabled = False + Text = ("Password") + Transparent = True + } + { LblProxyUser Label + MoveScaled(1,11,13,4) + Enabled = False + Text = ("User") + Transparent = True + } + { LblAuth Label + MoveScaled(1,6,13,4) + Enabled = False + Text = ("Authentication") + Transparent = True + } +} diff --git a/app/examples/Networking/HTTPGet/.src/FHttpGet.class b/app/examples/Networking/HTTPGet/.src/FHttpGet.class new file mode 100644 index 00000000..f3b0584b --- /dev/null +++ b/app/examples/Networking/HTTPGet/.src/FHttpGet.class @@ -0,0 +1,285 @@ +' Gambas class file + +'/////////////////////////////////////// +' Here we define a HttpClient object +'/////////////////////////////////////// +'PRIVATE MyHTTP AS HttpClient +'/////////////////////////////// +' This is a buffer I use when +' a link is clicked (ugly hack:) +'/////////////////////////////// +Private CurHost As String + +Public Sub Form_Open() + + '/////////////////////////////////// + ' We set Default configuration values + '/////////////////////////////////// + ClsParams.ProxyHost = "127.0.0.1:3128" + ClsParams.ProxyUser = "" + ClsParams.ProxyPwd = "" + ClsParams.ProxyAuth = Net.AuthNone + ClsParams.CookiesFile = User.Home & "/gbcookies.txt" + '//////////////////////////////////// + ' Now we create the HttpClient object + '//////////////////////////////////// + 'MyHTTP=NEW HttpClient AS "MyHTTP" + +End + +Public Sub Button1_Click() + '///////////////////////////////////////////////// + ' When we press 'Get' button we start 'getting' + ' proccess + '///////////////////////////////////////////////// + + '///////////////////////////////////////////// + ' If user has configured a proxy, we set + ' that values in HttpClient object + '///////////////////////////////////////////// + If ClsParams.UseProxy And ClsParams.ProxyHost <> "" Then + '////////////////////////////////// + ' Host address and port + '////////////////////////////////// + MyHTTP.Proxy.Host = ClsParams.ProxyHost + MyHTTP.Proxy.Type = Net.ProxyHTTP + '////////////////////////////////// + ' If Proxy needs authentication, + ' we set it + '////////////////////////////////// + If ClsParams.ProxyUser <> "" Or ClsParams.ProxyPwd <> "" Then + MyHTTP.Proxy.User = ClsParams.ProxyUser + MyHTTP.Proxy.Password = ClsParams.ProxyPwd + MyHTTP.Proxy.Auth = ClsParams.ProxyAuth + Else + '/////////////////////////////////// + ' No authentication needed + '/////////////////////////////////// + MyHTTP.Proxy.Auth = Net.AuthNone + End If + Else + '////////////////////////////////////// + ' No proxy selected (direct connection) + '////////////////////////////////////// + MyHTTP.Proxy.Host = "" + ' todo authnone + End If + + '///////////////////////////////////////////////// + ' User and Password settings to access to that + ' document (this is a HTTP server authorization, + ' not a Proxy authorization) + '///////////////////////////////////////////////// + ' 1º User and password + If ChkPassword.Value Then + MyHTTP.User = TxtUser.Text + MyHTTP.Password = TxtPassword.Text + ' 2º Authorization type + Select Case CmbAuth.Index + Case 0 + MyHTTP.Auth = Net.AuthBasic + Case 1 + MyHTTP.Auth = Net.AuthNTLM + Case 2 + MyHTTP.Auth = Net.AuthDigest + Case 3 + MyHTTP.Auth = Net.AuthGSSNEGOTIATE + End Select + Else + MyHTTP.User = "" + MyHTTP.Auth = Net.AuthNone + End If + + '////////////////////////////////// + ' A bit of feedback for user... + '////////////////////////////////// + TextArea1.Text = "" + txtInfo.Text = ("Connecting...") + Navigator.Text = "" + + '//////////////////////////////////// + ' URL to Get + '//////////////////////////////////// + MyHTTP.Url = TxtHost.Text + + '//////////////////////////////////// + ' a little buffer for me + '//////////////////////////////////// + CurHost = Mid(MyHTTP.Url, 8) + + '//////////////////////////////////// + ' Cookies + '//////////////////////////////////// + MyHTTP.CookiesFile = ClsParams.CookiesFile + MyHTTP.UpdateCookies = ClsParams.UpdateCookies + + '//////////////////////////////////// + ' Let's get it! + '//////////////////////////////////// + Print MyHTTP.Proxy.Auth + MyHTTP.Get() + + '/////////////////////////////////////////// + ' If we'd want to download remote document + ' to a file, instead of receving it in + ' memory, we could do that: + ' MyHTTP.Get( "FilePath" ) + '/////////////////////////////////////////// + +End + +Public Sub MyHTTP_Connect() + + '/////////////////////////////////////////////// + ' This event from HttpClient raises when + ' we connect successfully with remote server + ' and allows us to give more feed-back to user + '/////////////////////////////////////////////// + txtInfo.Text = ("Connected, waiting for reply...") + +End + +Public Sub MyHTTP_Read() + '/////////////////////////////////////////// + ' This event raises when a new piece of data + ' arrives to us from server, so we read that + ' part of the document + '/////////////////////////////////////////// + + Dim sBuf As String + Dim sText As String + + '///////////////////////////////// + ' more feedback... + '///////////////////////////////// + sText = ("Receiving data...") & " " + If MyHttp.TotalDownloaded Then + sText &= Format(MyHttp.Downloaded / MyHttp.TotalDownloaded, "0 %") + Else + sText &= Subst(("&1 bytes"), MyHttp.Downloaded) + Endif + txtInfo.Text = sText + + '///////////////////////////////// + ' Header of HTTP document received + ' from server + '///////////////////////////////// + If TextArea1.Text = "" Then + TextArea1.Text = MyHTTP.Headers.Join("\n") + End If + + '///////////////////////////////// + ' If really there's data to read, + ' we read it and place it in our + ' "navigator" screen + '////////////////////////////////// + If Lof(MyHTTP) Then + Read #MyHTTP, sBuf, Lof(MyHTTP) + Navigator.Text = Navigator.Text & sBuf + End If + +End + +Public Sub MyHTTP_Error() + + '//////////////////////////// + ' If something fails, this + ' event raises and connection + ' is stopped + '//////////////////////////// + CurHost = "" + txtInfo.Text = ("Error ") & MyHTTP.Status + +End + +Public Sub MyHTTP_Finished() + + '///////////////////////////////////// + ' When all document has been received, + ' this event raises + '///////////////////////////////////// + Dim sBuf As String + '////////////////////////////// + ' feeback... + '////////////////////////////// + txtInfo.Text = ("OK") + If TextArea1.Text = "" Then + TextArea1.Text = MyHTTP.Headers.Join("\n") + End If + + '/////////////////////////////////// + ' we extract all possible data + ' buffered in HttpClient + '/////////////////////////////////// + If Lof(MyHTTP) Then + Read #MyHTTP, sBuf, Lof(MyHTTP) + Navigator.Text = Navigator.Text & sBuf + End If + +End + +Public Sub mnuOptions_Click() + '//////////////////////////////////// + ' If user wants to modify parameters + '//////////////////////////////////// + + FConfig.ShowModal + +End + +Public Sub Form_Close() + + '////////////////////////////////////// + ' When program finishes, we must ensure + ' that we close HttpClient object + '/////////////////////////////////////// + MyHTTP.Stop() + +End + +Public Sub BtnStop_Click() + + '/////////////////////////////////////// + ' If user wants to close the + ' connection... + '/////////////////////////////////////// + If MyHttp.Status > 0 Then txtInfo.Text = ("Cancelled by user") + MyHTTP.Stop() + +End + +Public Function Correct_Url(sCad As String) As String + + If InStr(sCad, "://") Then Return sCad + + If Left(sCad, 1) = "/" Then + If InStr(CurHost, "/") Then CurHost = Left(CurHost, InStr(CurHost, "/") - 1) + Return CurHost & sCad + Else + If Right(CurHost, 1) = "/" Then Return CurHost & sCad + Return CurHost & "/" & sCad + End If + +End + +Public Sub Navigator_Link(Path As String) + + If MyHTTP.Status > 0 Then MyHTTP.Stop() + TxtHost.Text = Correct_Url(Path) + Button1_Click() + +End + +Public Sub ChkPassword_Click() + + If ChkPassword.Value Then + TxtUser.Enabled = True + TxtPassword.Enabled = True + CmbAuth.Enabled = True + Else + TxtUser.Enabled = False + TxtPassword.Enabled = False + CmbAuth.Enabled = False + End If + +End diff --git a/app/examples/Networking/HTTPGet/.src/FHttpGet.form b/app/examples/Networking/HTTPGet/.src/FHttpGet.form new file mode 100644 index 00000000..a01343fc --- /dev/null +++ b/app/examples/Networking/HTTPGet/.src/FHttpGet.form @@ -0,0 +1,65 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(30,14,96,73) + Resizable = False + { Button1 Button + MoveScaled(1,6,14,4) + Text = ("GET!") + } + { TextArea1 TextArea + MoveScaled(1,11,63,14) + } + { Navigator TextEdit + MoveScaled(1,26,94,46) + ReadOnly = True + } + { Label1 Label + MoveScaled(1,1,7,4) + Text = ("URL :") + } + { TxtHost TextBox + MoveScaled(8,1,56,4) + Text = ("http://gambasdoc.org/help") + } + { BtnStop Button + MoveScaled(16,6,14,4) + Text = ("Stop") + } + { ChkPassword CheckBox + MoveScaled(66,6,29,4) + Text = ("Page needs authorization") + } + { TxtUser TextBox + MoveScaled(66,16,29,4) + Enabled = False + Text = ("User") + } + { TxtPassword TextBox + MoveScaled(66,21,29,4) + Enabled = False + Text = ("Password") + Password = True + } + { CmbAuth ComboBox + MoveScaled(66,11,29,4) + Enabled = False + ReadOnly = True + List = [("Basic"), ("NTLM"), ("DIGEST"), ("GSS")] + Text = (" ") + } + { MyHttp #HttpClient + #MoveScaled(27,36) + } + { mnuOptions Button + MoveScaled(66,1,29,4) + Text = ("&Configuration...") + } + { txtInfo Label + MoveScaled(32,6,32,4) + Font = Font["Bold"] + Background = Color.SelectedBackground + Foreground = &HFFFF00& + Padding = 8 + } +} diff --git a/app/examples/Networking/HTTPGet/httpclient.png b/app/examples/Networking/HTTPGet/httpclient.png new file mode 100644 index 0000000000000000000000000000000000000000..613d858f2cbcc49382048288ca7a5b04e9db60be GIT binary patch literal 1768 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 a6W^(qkFgtZ9-u$#im{h88vJkmXy!lP_ft3k literal 0 HcmV?d00001 diff --git a/app/examples/Networking/HTTPPost/.lang/ca.po b/app/examples/Networking/HTTPPost/.lang/ca.po new file mode 100644 index 00000000..cbb000f9 --- /dev/null +++ b/app/examples/Networking/HTTPPost/.lang/ca.po @@ -0,0 +1,80 @@ +# Catalan translation of HTTPPost +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the HTTPPost package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: HTTPPost\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:10+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: F.form:114 +msgid "127.0.0.1:3128" +msgstr "-" + +#: F.form:63 +msgid "5" +msgstr "-" + +#: F.class:72 +msgid "Connecting..." +msgstr "S'està connectant..." + +#: F.class:7 +msgid "Error " +msgstr "-" + +#: F.form:73 +msgid "Finally press the Post button >>>" +msgstr "Finalment premeu el botó Envia >>>" + +#: .project:1 +msgid "HTTP client POST example" +msgstr "Exemple POST de client HTTP" + +#: F.form:48 +msgid "HTTP Data" +msgstr "Dades HTTP" + +#: F.form:42 +msgid "HTTP Headers" +msgstr "Capçaleres HTTP" + +#: F.form:23 +msgid "Internet Calculator" +msgstr "Calculadora d'Internet" + +#: F.form:28 +msgid "Post" +msgstr "Envia" + +#: F.form:108 +msgid "Use Proxy" +msgstr "Usa servidor intermediari" + +#: F.form:81 +msgid "Using the power of internet to have a minimal calculator machine!" +msgstr "Usant el poder d'Internet per tenir una mínima màquina de calcular!" + +#: F.class:22 +msgid "Waiting for reply..." +msgstr "S'està esperant resposta..." + +#: F.form:58 +msgid "Write Here Another Number :" +msgstr "Escriviu aquí un altre nombre:" + +#: F.form:53 +msgid "Write Here a Number :" +msgstr "Escriviu aquí un nombre:" + diff --git a/app/examples/Networking/HTTPPost/.lang/cs.mo b/app/examples/Networking/HTTPPost/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..f61934392b8a7a04921ae00b22fae8f62ecd9da8 GIT binary patch literal 1209 zcmYk4&u;a}DJ4uff~k9qUPr=W?*$NZnv*G%9 zu52hHpBu0FesAvPo#G8VbHS~-oAan;$!IN`PU)RuvFJtC8cW`a(jrOvlv(_e3X6q*u~TS}h$diKN+0 zgfrIToZd!;oppMe2BNA%9&qE#2Y%48iy@x_^ zBeabktW1+WxAf2|Cuvn$Nuf6A#ZtA~*#d&&XJYylM0hPn*{SCfypqok2S{PhF^IOUl zJReW};H65a7jfQa|T9hj%*G8P{M7-Aq-$k)VV(26P5vnlVgTIg)N@R)TTjfSW%0@%z^PN7r zq>q#_8YC$9SeEXrH~vA!c^4dITB+%c_s2hSOOHgV4hHn3C23b44BU91xRoq15XZ5@ G>BqMm%|5aK literal 0 HcmV?d00001 diff --git a/app/examples/Networking/HTTPPost/.lang/cs.po b/app/examples/Networking/HTTPPost/.lang/cs.po new file mode 100644 index 00000000..03288a56 --- /dev/null +++ b/app/examples/Networking/HTTPPost/.lang/cs.po @@ -0,0 +1,72 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTTP client POST example" +msgstr "Příklad HTTP POST klienta" + +#: F.class:7 +msgid "Error " +msgstr "Chyba" + +#: F.class:22 +msgid "Waiting for reply..." +msgstr "Čekání na odpověď..." + +#: F.class:72 +msgid "Connecting..." +msgstr "Připojování..." + +#: F.form:23 +msgid "Internet Calculator" +msgstr "Internetová kalkulačka" + +#: F.form:28 +msgid "Post" +msgstr "-" + +#: F.form:42 +msgid "HTTP Headers" +msgstr "HTTP Hlavička" + +#: F.form:48 +msgid "HTTP Data" +msgstr "-" + +#: F.form:53 +msgid "Write Here a Number :" +msgstr "Zapiš zde číslo :" + +#: F.form:58 +msgid "Write Here Another Number :" +msgstr "Zapiš zde ještě číslo :" + +#: F.form:63 +msgid "5" +msgstr "-" + +#: F.form:73 +msgid "Finally press the Post button >>>" +msgstr "Nakonec zmáčkni tlačítko Post >>>" + +#: F.form:81 +msgid "Using the power of internet to have a minimal calculator machine!" +msgstr "Použití síly internetu na minimální kalkulačku!" + +#: F.form:108 +msgid "Use Proxy" +msgstr "Použít proxy" + +#: F.form:114 +msgid "127.0.0.1:3128" +msgstr "-" diff --git a/app/examples/Networking/HTTPPost/.lang/de.mo b/app/examples/Networking/HTTPPost/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..d6f87cc60fe7ad8ab68bebd6a6707b279691ebf2 GIT binary patch literal 1202 zcmZ9K%Wl&^6ow5gw{TS{0tt{#*T|tQLR1DSOykms+|;J6=x*Yi#6z8NG@d}i3ly~Hm-PYMV0+*x za0tq)r?{O2pMi(L7vLQD20R9S0#ATnzzg74@GSTfoCXga9PcZDA)aT!S#S%y2=0RC zzfQ%uz&D`i9EFSFab!}Xn4~9qr{PE7$KhgfsGKD5X zEtYD7UJD5R{-!2xk>SAWQlRW_ys^w}l$#0=JU%*j1G~RCx`^d-fp1ayl zl{PmiPL$2r-QH}j`b(32l2(VUADK97qj3Q(wX`dEJ}y+=(x%Nudbr9-%GN6>V@M4; z^!X+F*VE2%YG^mr@h<;67HlAKmv!FPoKolWM=Jy4uS_Q6&qSb)ablqUx2QK732Lz! zxF--RX{IWU*-@Kw`aMkfNSPRiFKLX~RC%1Rv13&l3&?C(jDaK{8uvJ}PU@;_J!MNs_j)h{DSDgpoi literal 0 HcmV?d00001 diff --git a/app/examples/Networking/HTTPPost/.lang/de.po b/app/examples/Networking/HTTPPost/.lang/de.po new file mode 100644 index 00000000..ec7afa61 --- /dev/null +++ b/app/examples/Networking/HTTPPost/.lang/de.po @@ -0,0 +1,73 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTTP client POST example" +msgstr "Beispiel fürHTTP-Client POST" + +#: F.class:7 +msgid "Error " +msgstr "Fehler" + +#: F.class:22 +msgid "Waiting for reply..." +msgstr "Warte auf Antwort..." + +#: F.class:72 +msgid "Connecting..." +msgstr "Verbinden..." + +#: F.form:23 +msgid "Internet Calculator" +msgstr "Internet-Rechner" + +#: F.form:28 +msgid "Post" +msgstr "Übermitteln" + +#: F.form:42 +msgid "HTTP Headers" +msgstr "-" + +#: F.form:48 +msgid "HTTP Data" +msgstr "HTTP Daten" + +#: F.form:53 +msgid "Write Here a Number :" +msgstr "Eine Zahl eingeben" + +#: F.form:58 +msgid "Write Here Another Number :" +msgstr "Noch eine Zahl eingeben" + +#: F.form:63 +msgid "5" +msgstr "-" + +#: F.form:73 +msgid "Finally press the Post button >>>" +msgstr "Dann auf Übermitteln klicken >>>" + +#: F.form:81 +msgid "Using the power of internet to have a minimal calculator machine!" +msgstr "Die Macht des Internets für einen kleinen Taschenrechner nutzen!" + +#: F.form:108 +msgid "Use Proxy" +msgstr "Proxy benutzen" + +#: F.form:114 +msgid "127.0.0.1:3128" +msgstr "-" + diff --git a/app/examples/Networking/HTTPPost/.lang/es.mo b/app/examples/Networking/HTTPPost/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..8f538d8fba05f8d59c8bd5cd695d6c55bc797ed2 GIT binary patch literal 1011 zcmZ{iyKWOf6ov;#2$+im2nwJ$p#*J+gHWO+L|Mg~c)>S0b`a1oUQg^nyED%0Y)B9^ z)U-T69v~GRbhxLcKs*3F)coVbBtqis9)IJxoXh1pf|qMkg4% z2u_1|*(`qCzB%w5SO+hF&%rC81Si0^;92k;7=Z7=8{k*)D)FI5M~2)#T1Qn}Q?lD9@yu zfVrNWoq?ZrgW}x$%q*Uwf3G;V!0xkEsd$p?Qp1QWqlV5bb#iO9B5#S6Tdf9_xaH&@ zjLU*|g~7i_Sz(m0w8E1(Pq@{Fxx=iT3B=MnyBJB;Bik1=(64YvZ;~9JvYPsQTM(yI zDw*8+8GsaY>gkyBJ)cTTQBN*8^UB#E1w#{ z%Hv=yq>Zq-9@T4wM%`ry7R1F3kwyVS+8Zd%0}(^ zwA!vzs1{U1S`4c}R9Oy6rDnLkzJ#r+m-C+RT7suORf5`D+ofErRF)GtqfKwAP>rhL zQBlR2*}{s(btvT69iRv+cI@3j!limZai5z^*oW=bs<&{o&(*Rij2Eg{cX7EsEp((U z{72Ljv2pj%6`cwa33ap`epDlo|4OT~v9^fHJK{C(YRnwU+SP`-g2rl(CFbT(<1c&d zj4PZ#^nrGt#fyfA8r<8;@!5?vM-o2G!Ynqj!-;R@!+p}$Xi~#Z NsQ~?d7ISrM=?|qX0rLO= literal 0 HcmV?d00001 diff --git a/app/examples/Networking/HTTPPost/.lang/es.po b/app/examples/Networking/HTTPPost/.lang/es.po new file mode 100644 index 00000000..0901fdfa --- /dev/null +++ b/app/examples/Networking/HTTPPost/.lang/es.po @@ -0,0 +1,56 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: F.class:132 +msgid "Internet Calculator" +msgstr "Calculador de Internet" + +#: F.class:137 +msgid "Post" +msgstr "Post" + +#: F.class:152 +msgid "HTTP Header :" +msgstr "HTTP Encabezado :" + +#: F.class:157 +msgid "HTTP Data :" +msgstr "HTTP Datos :" + +#: F.class:162 +msgid "Write Here a Number :" +msgstr "Escriba aquí un número :" + +#: F.class:167 +msgid "Write Here Another Number :" +msgstr "Escriba aquí otro número :" + +#: F.class:172 +msgid "5" +msgstr "5" + +#: F.class:182 +msgid "Finally Press Post button ->" +msgstr "Finalmente presione el botón Post" + +#: F.class:190 +msgid "Using the power of internet to have a minimal calculator machine!" +msgstr "" +"¡Usando el poder de Internet para tener una mínima máquina calculadora!" + +#: F.class:224 +msgid "Use Proxy" +msgstr "Usar Proxy" + +#: F.class:230 +msgid "127.0.0.1:3128" +msgstr "127.0.0.1:3128" diff --git a/app/examples/Networking/HTTPPost/.lang/nl.mo b/app/examples/Networking/HTTPPost/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..afbff0ce17d6bf42f9ccdce238e7f141048f5206 GIT binary patch literal 1255 zcmZva-EJF26vwyF@?pLzXekJUPQ@i!cWo$5wjqj@IEjUmSh15_kL{D}#Je-A*;yxk zf>zuk^`aFIzy;5MxUAHxs_GR_z)jzP|5?WkQaP*H-+Z1q|1;)xZ|U zH^3X9-0`2m5D|%gjMPM1bi3m12nPP993?vp)YwALwMD_ z(wBS&jpn_2gs%~Q-)JrccY}3n43E4vC-r(gXgg;e1)JKaG@ViIxG2acOg&q88kXK$ zL#wN+!S-O#qYdTNytmD2%&wRZMyY1wskhf35TC0oPdVrq&(5%?b(N0FRC((H@fjQx zaPI8+3_S!{NFWoIg4;kk&>`84i6zpTN6{Swf=K!t4)XP{eEX}x7OPW z_j#&iF+>_Tre@UmA&i#8W|N{QZY+NnHKM52RfP`+PMM<89Mh4O#O^Eh1+ARPU~SA( zPHkRi<$O{kI_J{aoa@$|tiI`B^Pt*f`h8JBQ)}IjIUQFQZ1K>QdU8=Ri_w^A;!p*irpi!)GJ9P)HX~-UC>#Et xx`AdK84lS<1;GinBO+z|8Ll3Sg#IXT`t+C*jj|DU3Q`=IGRxS-Z!Rwu{ssI7Inw|D literal 0 HcmV?d00001 diff --git a/app/examples/Networking/HTTPPost/.lang/nl.po b/app/examples/Networking/HTTPPost/.lang/nl.po new file mode 100644 index 00000000..bbb2ca66 --- /dev/null +++ b/app/examples/Networking/HTTPPost/.lang/nl.po @@ -0,0 +1,72 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"PO-Revision-Date: 2014-09-22 00:19+0100\n" +"Last-Translator: Willy Raets \n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "HTTP client POST example" +msgstr "HTTP cliënt POST voorbeeld" + +#: F.form:23 +msgid "Internet Calculator" +msgstr "-" + +#: F.form:28 +msgid "Post" +msgstr "-" + +#: F.form:42 +msgid "HTTP Headers" +msgstr "-" + +#: F.form:48 +msgid "HTTP Data" +msgstr "-" + +#: F.form:53 +msgid "Write Here a Number :" +msgstr "Schrijf hier een nummer:" + +#: F.form:58 +msgid "Write Here Another Number :" +msgstr "Schrijf hier een ander nummer:" + +#: F.form:63 +msgid "5" +msgstr "-" + +#: F.form:73 +msgid "Finally press the Post button >>>" +msgstr "Uiteindelijk druk the Post knop >>>" + +#: F.form:81 +msgid "Using the power of internet to have a minimal calculator machine!" +msgstr "Gebruik de kracht van het internet om een minimale calculator machine te hebben!" + +#: F.form:108 +msgid "Use Proxy" +msgstr "Gebruik Proxy" + +#: F.form:114 +msgid "127.0.0.1:3128" +msgstr "-" + +#: F.class:7 +msgid "Error " +msgstr "Fout" + +#: F.class:22 +msgid "Waiting for reply..." +msgstr "Wachten op antwoord..." + +#: F.class:72 +msgid "Connecting..." +msgstr "Verbinding maken..." + diff --git a/app/examples/Networking/HTTPPost/.project b/app/examples/Networking/HTTPPost/.project new file mode 100644 index 00000000..149b45db --- /dev/null +++ b/app/examples/Networking/HTTPPost/.project @@ -0,0 +1,20 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=HTTP client POST example +Startup=FHttpPost +Icon=httpclient.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.net +Component=gb.net.curl +Description="HttpClient POST request example.\n\nThis example how to send a POST HTTP request to a server and get the response. You can configure an HTTP proxy if needed." +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Example +Address=benoit@desnouettes +License=General Public Licence +Packager=1 +Tags=Network diff --git a/app/examples/Networking/HTTPPost/.src/FHttpPost.class b/app/examples/Networking/HTTPPost/.src/FHttpPost.class new file mode 100644 index 00000000..89f8cd9c --- /dev/null +++ b/app/examples/Networking/HTTPPost/.src/FHttpPost.class @@ -0,0 +1,105 @@ +' Gambas class file + +Public P As HttpClient + +Public Sub P_Error() + + LblInfo.Text = ("Error ") & P.Status + +End + + +Public Sub Form_Open() + + TextArea1.Text = "" + P = New HttpClient As "P" + +End + + +Public Sub P_Connect() + + LblInfo.Text = ("Waiting for reply...") + +End + + + +Public Sub P_Finished() + + Dim sBuf As String + Dim MyLoop As Integer + + LblInfo.Text = "OK" + + TextArea2.Insert(P.Headers.Join("\n") & "\n") + + If Lof(P) Then + sBuf = Read #P, Lof(P) + TextArea1.Text = sBuf + Endif + + If (InStr(sBuf, "")) Then + sBuf = Mid(sBuf, InStr(sBuf, "") + 12) + MyLoop = 1 + Label6.Text = TextBox1.Text & " + " & TextBox2.Text & " = " + Do While Mid(sBuf, MyLoop, 1) <> "<" + Label6.Text = Label6.Text & Mid(sBuf, MyLoop, 1) + MyLoop = MyLoop + 1 + Loop + If (InStr(sBuf, "")) Then + sBuf = Mid(sBuf, InStr(sBuf, "") + 12) + MyLoop = 1 + Label7.Text = TextBox1.Text & " - " & TextBox2.Text & " = " + Do While Mid(sBuf, MyLoop, 1) <> "<" + Label7.Text = Label7.Text & Mid(sBuf, MyLoop, 1) + MyLoop = MyLoop + 1 + Loop + Else + Message.Error("Error") + End If + Else + Message.Error("Error") + End If + + +End + + +Public Sub Button1_Click() + + Dim sCad As String + LblInfo.Text = ("Connecting...") + TextBox1.Text = Val(Trim(TextBox1.Text)) + TextBox2.Text = Val(Trim(TextBox2.Text)) + Wait + TextArea1.Text = "" + sCad = "" & Chr(13) & Chr(10) + sCad = sCad & "" '& Chr(13) & Chr(10) + sCad = sCad & "sample.sumAndDifference" '& Chr(13) & Chr(10) + sCad = sCad & "" + sCad = sCad & "" + sCad = sCad & "" & TextBox1.Text & "" + sCad = sCad & "" + sCad = sCad & "" + sCad = sCad & "" & TextBox2.Text & "" + sCad = sCad & "" + sCad = sCad & "" + sCad = sCad & "" + If ChkProxy.Value Then + P.Proxy.Host = TxtProxy.Text + Else + P.Proxy.Host = "" + End If + P.URL = "xmlrpc-c.sourceforge.net/api/sample.php" + + P.Post("text/xml", sCad) + +End + + +Public Sub ChkProxy_Click() + + TxtProxy.Enabled = ChkProxy.Value + +End diff --git a/app/examples/Networking/HTTPPost/.src/FHttpPost.form b/app/examples/Networking/HTTPPost/.src/FHttpPost.form new file mode 100644 index 00000000..b9824444 --- /dev/null +++ b/app/examples/Networking/HTTPPost/.src/FHttpPost.form @@ -0,0 +1,85 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(38.1429,12.7143,95,85) + Text = ("Internet Calculator") + Resizable = False + { Button1 Button + MoveScaled(37,18,14.7143,4) + Text = ("Post") + } + { TextArea1 TextArea + MoveScaled(1,56,93,28) + } + { TextArea2 TextArea + MoveScaled(1,35,93,16) + } + { Label1 Label + MoveScaled(1,31,89.1429,4) + Font = Font["Bold"] + Text = ("HTTP Headers") + } + { Label2 Label + MoveScaled(1,52,90.7143,4) + Font = Font["Bold"] + Text = ("HTTP Data") + } + { Label3 Label + MoveScaled(4,8,32,4) + Text = ("Write Here a Number :") + } + { Label4 Label + MoveScaled(4,13,32,4) + Text = ("Write Here Another Number :") + } + { TextBox1 TextBox + MoveScaled(37,8,14.7143,4) + Text = ("5") + } + { TextBox2 TextBox + MoveScaled(37,13,14.7143,4) + Text = ("5") + } + { Label5 Label + MoveScaled(4,18,32,4) + Text = ("Finally press the Post button >>>") + } + { TextLabel1 TextLabel + MoveScaled(0,0,96,6) + Font = Font["Bold"] + Background = &H101A9E& + Foreground = &HF7F02A& + Text = ("Using the power of internet to have a minimal calculator machine!") + Alignment = Align.Center + } + { Label6 Label + MoveScaled(60,8,34,5) + Font = Font["12,Bold"] + Background = &H092773& + Foreground = &HFFFFFF& + Padding = 4 + } + { Label7 Label + MoveScaled(60,14,34,5) + Font = Font["12,Bold"] + Background = &H092773& + Foreground = &HFFFFFF& + Padding = 4 + } + { LblInfo Label + MoveScaled(60,20,34,4) + Font = Font["Bold"] + Background = &H003DB6& + Foreground = &HFFFF00& + Padding = 4 + } + { ChkProxy CheckBox + MoveScaled(1,26,18.7143,4) + Text = ("Use Proxy") + } + { TxtProxy TextBox + MoveScaled(18,26,77,4) + Enabled = False + Text = ("127.0.0.1:3128") + } +} diff --git a/app/examples/Networking/HTTPPost/httpclient.png b/app/examples/Networking/HTTPPost/httpclient.png new file mode 100644 index 0000000000000000000000000000000000000000..613d858f2cbcc49382048288ca7a5b04e9db60be GIT binary patch literal 1768 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 Net.Connected Then + FSettings.ShowModal() + Endif + +End + +Public Sub btnConnect_Click() + + Connect() + +End + +Private Sub Connect() + + 'Wait 0.5 + + Inc Application.Busy + + If MailClient.Host Then + btnSettings.Enabled = False + MailClient.Open() + Endif + + If MailClient.Status = Net.Connected Then + LoadInbox() + btnDisconnect.Enabled = True + Else + btnSettings.Enabled = True + Message.Warning("Login failed!") + Endif + + Dec Application.Busy + +End + +Private Sub LoadInbox() + + Dim oMail As _Pop3Client_Message + + If MailClient.Status <> Net.Connected Then Return + + For Each oMail In MailClient + + If oMail.Index = 3 Then Return + + lvInbox.Add(oMail.Index, Subst("&1 &2", oMail.Message.Sender, oMail.Message.Subject)) + + Wait + + Next + +End + +Public Sub btnDisconnect_Click() + + If MailClient.Status <> Net.Connected Then Return + + lvInbox.Clear() + + MailClient.Close() + + btnDisconnect.Enabled = False + + btnSettings.Enabled = True + +End + +Public Sub lvInbox_Select() + + ''Please note that this is not an example of the gb.mime component!!! + txtContent.Text = MailClient[lvInbox.Key].Message.Body[0].ToString() + + ''THIS SHOULD WORK!!! + 'txtContent.Text = Mime.Decode(MailClient[lvInbox.Key].Message.Body[0].ToString(), MailClient[lvInbox.Key].Message.Body[0].ContentEncoding) + +End + +Public Sub Form_Close() + + If btnDisconnect.Enabled Then + MailClient.Close() + Endif + + Settings.Write(FMain) + +End diff --git a/app/examples/Networking/POPMailbox/.src/FMain.form b/app/examples/Networking/POPMailbox/.src/FMain.form new file mode 100644 index 00000000..2c8f7391 --- /dev/null +++ b/app/examples/Networking/POPMailbox/.src/FMain.form @@ -0,0 +1,50 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,77,38) + Text = ("POP3 Mailbox Example") + Arrangement = Arrange.Vertical + Spacing = True + Margin = True + Padding = 10 + { !MailClient #Pop3Client + #MoveScaled(23,1) + #Public = True + } + { HSplit1 HSplit + MoveScaled(1,9,69,19) + Expand = True + { lvInbox ListView + MoveScaled(2,3,29,14) + } + { txtContent TextArea + MoveScaled(33,1,31,16) + ReadOnly = True + Wrap = True + ScrollBar = Scroll.Vertical + } + } + { HBox1 HBox + MoveScaled(3,30,67,4) + { btnConnect Button + MoveScaled(0,0,16,4) + Text = ("Connect") + } + { Panel2 Panel + MoveScaled(16,0,7,4) + } + { btnDisconnect Button + MoveScaled(23,0,16,4) + Enabled = False + Text = ("Disconnect") + } + { Panel1 Panel + MoveScaled(39,0,7,4) + Expand = True + } + { btnSettings Button + MoveScaled(46,0,16,4) + Text = ("Settings") + } + } +} diff --git a/app/examples/Networking/POPMailbox/.src/FSettings.class b/app/examples/Networking/POPMailbox/.src/FSettings.class new file mode 100644 index 00000000..cd595c6c --- /dev/null +++ b/app/examples/Networking/POPMailbox/.src/FSettings.class @@ -0,0 +1,68 @@ +' Gambas class file + +Public Sub Form_Close() + + Settings.Write(FSettings) + +End + +Public Sub Form_Open() + + Settings.Read(FSettings) + + txtUsername.Text = Settings["LastUser", FMain.MailClient.User] + + txtPassword.Text = FMain.MailClient.Password + + txtServer.Text = Settings["LastServer", FMain.MailClient.Host] + + vbPort.Value = IIf(FMain.MailClient.Port, FMain.MailClient.Port, vbPort.Value) + +End + +Public Sub btnSave_Click() + + Balloon.Delay = 3000 + + If Not txtUsername.Text Then + Balloon.Error("Missing username", txtUsername) + Return + Endif + + If Not txtPassword.Text Then + Balloon.Error("Missing password", txtPassword) + Return + Endif + + If Not txtServer.Text Then + Balloon.Error("Missing server", txtServer) + Return + Endif + + FMain.MailClient.User = txtUsername.Text + Settings["LastUser"] = FMain.MailClient.User + + FMain.MailClient.Password = txtPassword.Text + + FMain.MailClient.Host = txtServer.Text + Settings["LastServer"] = FMain.MailClient.Host + + FMain.MailClient.Port = vbPort.Value + + Me.Close + +End + +Public Sub chbSSL_Click() + + Select Case chbSSL.Value + + Case -1 + If vbPort.Value = 110 Then vbPort.Value = 995 + + Case 0 + If vbPort.Value = 995 Then vbPort.Value = 110 + + End Select + +End diff --git a/app/examples/Networking/POPMailbox/.src/FSettings.form b/app/examples/Networking/POPMailbox/.src/FSettings.form new file mode 100644 index 00000000..cbf0e538 --- /dev/null +++ b/app/examples/Networking/POPMailbox/.src/FSettings.form @@ -0,0 +1,66 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,48,52) + Text = ("Settings") + Resizable = False + Arrangement = Arrange.Vertical + AutoResize = True + Spacing = True + Margin = True + { Frame1 Frame + MoveScaled(3,2,44,18) + Text = ("Login Details") + { Label1 Label + MoveScaled(1,5,12,4) + AutoResize = True + Text = ("Username") + } + { txtUsername TextBox + MoveScaled(17,5,24,4) + Expand = True + } + { Label2 Label + MoveScaled(1,11,12,4) + AutoResize = True + Text = ("Password") + } + { txtPassword TextBox + MoveScaled(17,10,24,4) + Expand = True + Password = True + } + } + { Frame2 Frame + MoveScaled(3,26,44,21) + Text = ("Server Details") + { Label3 Label + MoveScaled(1,5,12,4) + AutoResize = True + Text = ("Server") + } + { txtServer TextBox + MoveScaled(19,5,24,4) + Expand = True + } + { Label4 Label + MoveScaled(1,11,12,4) + AutoResize = True + Text = ("Port") + } + { vbPort SpinBox + MoveScaled(34,11,9,4) + MaxValue = 999999 + Value = 110 + } + { chbSSL CheckBox + MoveScaled(28,16,15,4) + AutoResize = True + Text = ("Use SSL") + } + } + { btnSave Button + MoveScaled(29,47,16,4) + Text = ("Save") + } +} diff --git a/app/examples/Networking/POPMailbox/pop3client.png b/app/examples/Networking/POPMailbox/pop3client.png new file mode 100644 index 0000000000000000000000000000000000000000..3ac6e3cd827550e3f24ac0cfd9e9c3e8f1ff1707 GIT binary patch literal 957 zcmV;u148_XP)Z0B9%xbu9o!8~^|R$;!_5_V)Vv z`uX|!_4W0~$;{^I?X|bO+uPgz{{8p&_x}F=+S=Q+x4Y`?@!a3z!^X(E|<=^7v)7ILks;%VZ<@@{l@$vEU^78!r{Nv>0?Curta_W?d|RE?(XjH@1mrp@ACEI=IP|- z>et!aqot?D$H?jF>FVn0#>U6-^Y-QE>gDI_y1c%kqonxw`2GF;>+9>HqNMZn_~+^D z=jrR{>g?p^=&rA@goK6J+1c3G*sia!_4fMd>h0<4?%?6%y1TvS=;*Djt*or9tE;Q$ z=jh<#g?|6?C#Lg)XdGz=;-L=VJkiRI^yi;0hljDU!RffXbqfRoc}dAyA}U zQ(IA9R$5vDR!~>p(Ad-*+*0iT2h}aX%}tFB^>ttct!?ccol*{tD8NCgv!lJO6{w&K zClK!L=>;k1!v*@4TPEOD093)x4psn4I(=XYmEG6R!3j|S62U?ORWRUC&<7*?k*t6! zfEeBfAwep-kah4w*$5N3uq%MOfr|m5pbu^l!WUeO_``vVfdOwwa)YyGKTf~{&Ahx^ f*a08md=CZy+L9lmVaOD500000NkvXXu0mjfUIQU6 literal 0 HcmV?d00001 diff --git a/app/examples/Networking/SerialPort/.directory b/app/examples/Networking/SerialPort/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Networking/SerialPort/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Networking/SerialPort/.icon.png b/app/examples/Networking/SerialPort/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7cda40d9199bbf380bda87817e786e26a2413869 GIT binary patch literal 4320 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 O0000 Chr$(13) Then + 'Amend character + .TextArea1.insert(R) + Endif + Next + + ' 'Use the following alternatively to the above. It displays ASCII-values in [] if it is a control character (ASCII-value < 32) + ' For i = 1 To Len(RX) + ' R = Mid$(RX, i, 1) + ' If Asc(R) > 31 Then + ' .TextArea1.insert(R) + ' Else + ' If R <> Chr$(13) 'Ommit CR because the TextArea control display it as CR+LF + ' .TextArea1.insert("[" & Format(Asc(R), "0") & "]" & R) + ' Else + ' .TextArea1.insert("[" & Format(Asc(R), "0") & "]") + ' Endif + ' Endif + ' Next + + End With + +End + +Public Sub CheckRS232Status() + 'This dipslays the status of the RS232 handshake lines + + With FMain.SerialPort1 + FMain.CheckDSR.Value = .DSR + FMain.CheckDTR.Value = .DTR + FMain.CheckCTS.Value = .CTS + FMain.CheckRTS.Value = .RTS + FMain.CheckDCD.Value = .DCD + FMain.CheckRNG.Value = .RNG + End With + +End diff --git a/app/examples/Networking/SerialPort/serialport.png b/app/examples/Networking/SerialPort/serialport.png new file mode 100644 index 0000000000000000000000000000000000000000..e1d380b9de706bf37bfce82f0019b9b266f556c9 GIT binary patch literal 2498 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 c8O-2+0W=z3E-pISDk_#s; zAWlf!kl+jSg2aIfmwXMx$3O@n#DA0N1F36|e>1bQCfHrRxya}!nWAeWNZN9f;zazrM9LNQ$(6axrJ-Yt;??I5!e}`#koWJVf88u+wb!bJ0IpV-(vyGvwV=B@z@Nz$7b0a zE3o^l6e}%RHPS|=Y(?~PJnbf%GUIBCH{(dAMzdAL<0y#?s|mA>PZuX@Dl@DuvmJ!i zRr7^3yxbE>Czg9&J@QKOHcBo)SkF& zawoDo?xHX<&H(UxvQiX`OwHq@1+mGVI~e$I1BDn(n6A9aV^$ey{|iA9vo^X9h5 zv^2%_M#U{0#MwHwWagIBrfNlLJLHAU$T+pQn~B)1s4USTPdh_X7lU~|Qj00SRpkD> zv*Fg1Y(>W1D>LLJVMHuahq*^S4UsklE9<6;jliUm%}nxS@TnIiye0QXSeL1mJRa=Y rF_uI!*d5MMAZNKJ?bvLz#yq2z%7*&jGjjg7JM1yyF*(=?8 literal 0 HcmV?d00001 diff --git a/app/examples/Networking/ServerSocket/.lang/ca.po b/app/examples/Networking/ServerSocket/.lang/ca.po new file mode 100644 index 00000000..3a2d824a --- /dev/null +++ b/app/examples/Networking/ServerSocket/.lang/ca.po @@ -0,0 +1,112 @@ +# Catalan translation of ServerSocket +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the ServerSocket package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: ServerSocket\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 22:37+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Server socket example" +msgstr "Exemple de sòcol de servidor" + +#: FrmMain.class:200 +msgid "Server Socket Example" +msgstr "Exemple de servidor de sòcol" + +#: FrmMain.class:206 +msgid "Listen" +msgstr "Escolta" + +#: FrmMain.class:211 +msgid "32340" +msgstr "-" + +#: FrmMain.class:221 +msgid "Close" +msgstr "Tanca" + +#: FrmMain.class:226 +msgid "Port" +msgstr "-" + +#: FrmMain.class:231 +msgid "Pause" +msgstr "Pausa" + +#: FrmMain.class:236 +msgid "Max. number of clients" +msgstr "Nombre màxim de clients" + +#: FrmMain.class:243 +msgid "1" +msgstr "-" + +#: FrmMain.class:243 +msgid "10" +msgstr "-" + +#: FrmMain.class:243 +msgid "2" +msgstr "-" + +#: FrmMain.class:243 +msgid "3" +msgstr "-" + +#: FrmMain.class:243 +msgid "4" +msgstr "-" + +#: FrmMain.class:243 +msgid "5" +msgstr "-" + +#: FrmMain.class:243 +msgid "6" +msgstr "-" + +#: FrmMain.class:243 +msgid "7" +msgstr "-" + +#: FrmMain.class:243 +msgid "8" +msgstr "-" + +#: FrmMain.class:243 +msgid "9" +msgstr "-" + +#: FrmMain.class:243 +msgid "No limit" +msgstr "Sense límit" + +#: FrmMain.class:248 +msgid "Type" +msgstr "Tipus" + +#: FrmMain.class:254 +msgid "TCP" +msgstr "-" + +#: FrmMain.class:254 +msgid "UNIX" +msgstr "-" + +#: FrmMain.class:259 +msgid "Path" +msgstr "Camí" + diff --git a/app/examples/Networking/ServerSocket/.lang/cs.mo b/app/examples/Networking/ServerSocket/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..508bde62925f7c7490c8b4dd5df1092c50fe945b GIT binary patch literal 1118 zcmZvZy>HV{5Wo*wz5)~|cX#i6_j9oSG2pt0GK}&A z`tC4HQjLmooAN%|CVnphyt5a)=ukvh&n9!8dt=aGxZqsTV0 zAGv|le)ma#fINr#L*g@JAL_5@{5A0nQWw63)b($pljd9EJK}rd2jWNKC*o(~7vfjq zH>9rfJ2Hj*g&acuCcO`f*L4pNhmiXIOGw@4Vd4bY=aCo+3#8vAqG>n(8NH#UXQg|l z?>kGj0i^zA4BNf;2vXN~3`OTXj&cG;ufZPRBvIqkzWx5~1yppuPowDhKn5~t7=+Gi`RB1vhxzA#b$3N9mtDBaXpny ljyZiDzsAD`)5D2nckB78WQoK9b?7kU4VL_SXz1}^hChgS(1!p3 literal 0 HcmV?d00001 diff --git a/app/examples/Networking/ServerSocket/.lang/cs.po b/app/examples/Networking/ServerSocket/.lang/cs.po new file mode 100644 index 00000000..3eb2bf05 --- /dev/null +++ b/app/examples/Networking/ServerSocket/.lang/cs.po @@ -0,0 +1,104 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Server socket example" +msgstr "Příklad servrového soketu" + +#: FrmMain.form:20 +msgid "Server Socket Example" +msgstr "Příkald Servrového soketu" + +#: FrmMain.form:26 +msgid "Listen" +msgstr "Naslouchat" + +#: FrmMain.form:31 +msgid "32340" +msgstr "-" + +#: FrmMain.form:41 +msgid "Close" +msgstr "Zavřír" + +#: FrmMain.form:46 +msgid "Port" +msgstr "-" + +#: FrmMain.form:51 +msgid "Pause" +msgstr "Pauza" + +#: FrmMain.form:56 +msgid "Max. number of clients" +msgstr "Max. počet klientů" + +#: FrmMain.form:63 +msgid "1" +msgstr "-" + +#: FrmMain.form:63 +msgid "10" +msgstr "-" + +#: FrmMain.form:63 +msgid "2" +msgstr "-" + +#: FrmMain.form:63 +msgid "3" +msgstr "-" + +#: FrmMain.form:63 +msgid "4" +msgstr "-" + +#: FrmMain.form:63 +msgid "5" +msgstr "-" + +#: FrmMain.form:63 +msgid "6" +msgstr "-" + +#: FrmMain.form:63 +msgid "7" +msgstr "-" + +#: FrmMain.form:63 +msgid "8" +msgstr "-" + +#: FrmMain.form:63 +msgid "9" +msgstr "-" + +#: FrmMain.form:63 +msgid "No limit" +msgstr "Bez limitu" + +#: FrmMain.form:68 +msgid "Type" +msgstr "Typ" + +#: FrmMain.form:74 +msgid "TCP" +msgstr "-" + +#: FrmMain.form:74 +msgid "UNIX" +msgstr "-" + +#: FrmMain.form:79 +msgid "Path" +msgstr "Cesta" diff --git a/app/examples/Networking/ServerSocket/.lang/es.mo b/app/examples/Networking/ServerSocket/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..24dfebd13857bb3a68114f7c99129ce2b28d610f GIT binary patch literal 1055 zcmaKp%WD)t5XM`LCfWFmF~Ju$h-e6tcQlSMWY|pN;_QQcB~=OCcczz~-VpKN z&5Is9s377&L_{Qb^Weop{soWX#j98Eezj={UM%LDUsp{HRbBO?x8pg34M1n0Z_qjD z$sW95U3(czf!!d5`anFa-})ylPlNjrKWqK-mKQ89S`JzcSvny7Hv%32r@&L-jP;i- z?}8@~7vMhdfsH=|JK#UEd<_1E{}en#aU@gU&p?FO3+unMd<9bd*Vcb)`OfmaBJP(MU>Iu4Q1eUCy^hwemmsJCv2cGv?QgsAQj=pVZjD;~&5nn5H`q%?#bi=loOh}dRZ0P@#Cth$++(F=@n^G z>{23Q-q_y4_{IO{FSHId^OI_eO0eAamq=*EqwP1?h2k|~cObBYV#|cIgqF}o;oz?e b48EO28<92jcPBf$kWI>J@4`=-f(rcs11P+E literal 0 HcmV?d00001 diff --git a/app/examples/Networking/ServerSocket/.lang/es.po b/app/examples/Networking/ServerSocket/.lang/es.po new file mode 100644 index 00000000..9cf52137 --- /dev/null +++ b/app/examples/Networking/ServerSocket/.lang/es.po @@ -0,0 +1,99 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FrmMain.class:161 +msgid "Server Socket Example" +msgstr "Ejemplo de Servidor Socket" + +#: FrmMain.class:166 +msgid "Listen" +msgstr "Escuchar" + +#: FrmMain.class:171 +msgid "32340" +msgstr "32340" + +#: FrmMain.class:181 +msgid "Close" +msgstr "Cerrar" + +#: FrmMain.class:186 +msgid "Port :" +msgstr "Puerto :" + +#: FrmMain.class:191 +msgid "Wait" +msgstr "Esperar" + +#: FrmMain.class:196 +msgid "Maximum number of clients:" +msgstr "Número máximo de clientes:" + +#: FrmMain.class:202 +msgid "(no Limit)" +msgstr "(sin límite)" + +#: FrmMain.class:202 +msgid "1" +msgstr "1" + +#: FrmMain.class:202 +msgid "2" +msgstr "2" + +#: FrmMain.class:202 +msgid "3" +msgstr "3" + +#: FrmMain.class:202 +msgid "4" +msgstr "4" + +#: FrmMain.class:202 +msgid "5" +msgstr "5" + +#: FrmMain.class:202 +msgid "6" +msgstr "6" + +#: FrmMain.class:202 +msgid "7" +msgstr "7" + +#: FrmMain.class:202 +msgid "8" +msgstr "8" + +#: FrmMain.class:202 +msgid "9" +msgstr "9" + +#: FrmMain.class:202 +msgid "10" +msgstr "10" + +#: FrmMain.class:207 +msgid "Type :" +msgstr "Tipo :" + +#: FrmMain.class:214 +msgid "TCP" +msgstr "TCP" + +#: FrmMain.class:214 +msgid "UNIX" +msgstr "UNIX" + +#: FrmMain.class:219 +msgid "Path :" +msgstr "Ruta :" diff --git a/app/examples/Networking/ServerSocket/.lang/nl.mo b/app/examples/Networking/ServerSocket/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..953ddafa32d4ed4d1dd1440e8422870c0bf62f10 GIT binary patch literal 1113 zcmZwEOK;Oa5CGr-$}8nr9z_DAl@O=O!A{zex&^hQX$hohlt&B4Z8A*^j&0eS&|Wxk z;RkR-;s70&lkvB^UfZ)fUq*(W0JU=z7breaT&8%s zhc49Yy#U;GNcuxo#*<=>+)wkajAz9g;=H&Z-V|?<{9K7VKvv1Km^hQ^P@XXHK_zmWSc#aHAoCB8*+{;z2f`$l{#z7yYzAHn1D&R z3OSg9ydNeAB`+};z>?lbtDx6i!&rsus_Ah{1MokgL<8H&w!aE7PaB PHAKJlS8aCY2RZx(_rS|5 literal 0 HcmV?d00001 diff --git a/app/examples/Networking/ServerSocket/.lang/nl.po b/app/examples/Networking/ServerSocket/.lang/nl.po new file mode 100644 index 00000000..dbe1f7b7 --- /dev/null +++ b/app/examples/Networking/ServerSocket/.lang/nl.po @@ -0,0 +1,104 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2014-09-21 23:59+0100\n" +"Last-Translator: Willy Raets \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Server socket example" +msgstr "Server socket voorbeeld" + +#: FrmMain.form:20 +msgid "Server Socket Example" +msgstr "Server Socket voorbeeld" + +#: FrmMain.form:26 +msgid "Listen" +msgstr "Luister" + +#: FrmMain.form:31 +msgid "32340" +msgstr "-" + +#: FrmMain.form:41 +msgid "Close" +msgstr "Sluiten" + +#: FrmMain.form:46 +msgid "Port" +msgstr "Poort" + +#: FrmMain.form:51 +msgid "Pause" +msgstr "-" + +#: FrmMain.form:56 +msgid "Max. number of clients" +msgstr "Max. aantal cliënten" + +#: FrmMain.form:63 +msgid "No limit" +msgstr "Geen limiet" + +#: FrmMain.form:63 +msgid "1" +msgstr "-" + +#: FrmMain.form:63 +msgid "2" +msgstr "-" + +#: FrmMain.form:63 +msgid "3" +msgstr "-" + +#: FrmMain.form:63 +msgid "4" +msgstr "-" + +#: FrmMain.form:63 +msgid "5" +msgstr "-" + +#: FrmMain.form:63 +msgid "6" +msgstr "-" + +#: FrmMain.form:63 +msgid "7" +msgstr "-" + +#: FrmMain.form:63 +msgid "8" +msgstr "-" + +#: FrmMain.form:63 +msgid "9" +msgstr "-" + +#: FrmMain.form:63 +msgid "10" +msgstr "-" + +#: FrmMain.form:68 +msgid "Type" +msgstr "-" + +#: FrmMain.form:74 +msgid "TCP" +msgstr "-" + +#: FrmMain.form:74 +msgid "UNIX" +msgstr "-" + +#: FrmMain.form:79 +msgid "Path" +msgstr "Pad" + diff --git a/app/examples/Networking/ServerSocket/.project b/app/examples/Networking/ServerSocket/.project new file mode 100644 index 00000000..a78baf7b --- /dev/null +++ b/app/examples/Networking/ServerSocket/.project @@ -0,0 +1,21 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Server socket example +Startup=FrmMain +Icon=serversocket.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.net +Component=gb.net.curl +Description="Server socket example.\n\nThis example shows how to implement a TCP server accepting and managing multiple connections in the same process. You can use the 'ClientSocket' example as a testing peer." +Environment="GB_GUI=gb.qt4" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Example +Address=benoit@desnouettes +License=General Public Licence +Packager=1 +Tags=Network diff --git a/app/examples/Networking/ServerSocket/.src/FrmMain.class b/app/examples/Networking/ServerSocket/.src/FrmMain.class new file mode 100644 index 00000000..bb590872 --- /dev/null +++ b/app/examples/Networking/ServerSocket/.src/FrmMain.class @@ -0,0 +1,176 @@ +' Gambas class file + +Private Waiting As Boolean +Private $iId As Integer + +Public Sub btnListen_Click() + + If cmbType.Index = 0 Then + 'TCP + MyServerSocket.Type = Net.Internet + ' The port to listen to + MyServerSocket.Port = Val(txtPort.Text) + ' we start listening + MyServerSocket.Listen(cmbMaxClient.Index) + Else + ' UNIX + MyServerSocket.Type = Net.Local ' You could also use Net.Unix + MyServerSocket.Path = txtPath.Text + MyServerSocket.Listen(cmbMaxClient.Index) + End If + If MyServerSocket.Status = Net.Active Then + ' listening + btnListen.Enabled = False + btnClose.Enabled = True + cmbMaxClient.Enabled = False + cmbType.Enabled = False + txtPath.Enabled = False + End If + +End + +Public Sub MyServerSocket_Error() + + Message.Error("Unable to bind socket") + +End + +Public Sub MyServerSocket_Connection(sHost As String) + '******************************* + ' A client has arrived! + ' let's accept it + + Dim Obj As Socket + + If MyServerSocket.Status <= Net.Inactive Then Return + If cmbType.Index = 0 Then + txtLog.Text = txtLog.Text & "Connection request from : " & sHost & Chr(13) & Chr(10) + Else + txtLog.Text = txtLog.Text & "Connection request accepted" & Chr(13) & Chr(10) + End If + + Obj = MyServerSocket.Accept() + Obj.Blocking = False + Inc $iId + Obj.Tag = [$iId, 0, ""] + + If Obj.Status = Net.Connected And cmbType.Index = 0 Then + txtLog.Text = txtLog.Text & "Connection from " & Obj.RemoteHost & ":" & Obj.RemotePort & " accepted (local port " & Obj.LocalPort & ")" & Chr(13) & Chr(10) + End If + +End + +Public Sub Socket_Write() + + Dim hSocket As Socket = Last + Dim iInd As Integer + + 'Debug hSocket;; hSocket.Tag + iInd = hSocket.Tag[1] + If iInd < 0 Then Return + + Do + Inc iInd + If iInd > 10 Then + hSocket.Tag[1] = -1 + Return + Endif + + 'Debug iInd + Try Print #hSocket, iInd & ":" & hSocket.Tag[2] & Space$(512) & "\n"; + If Error Then + Debug Error.Text + Break + Endif + Loop + + hSocket.Tag[1] = iInd + +Catch + +End + +Public Sub Socket_Read() + + Dim sBuf As String + Dim iInd As Integer + + '****************************** + ' When some data arrives to + ' our server, we respond with + ' an echo + '***************************** + + If Last.Status <> Net.Connected Then Return + Read #Last, sBuf, Lof(Last) + txtLog.Text &= "Socket #" & Last.Tag[0] & " --> " & sBuf & "\n" + + Last.Tag[1] = 0 + Last.Tag[2] = sBuf + + Socket_Write + +End + +Public Sub Socket_Ready() + + txtLog.Text &= "-- Client working --\n" + +End + +Public Sub Socket_Closed() + + txtLog.Text &= "Client #" & Last.Tag[0] & " Closed\n" + +End + +Public Sub Form_Open() + + txtPath.Text = Application.Path & "/" & "gambas" + txtPath.Enabled = False + TextLabel3.Text = "Path" + +End + +Public Sub btnClose_Click() + + MyServerSocket.Close() + btnClose.Enabled = False + btnListen.Enabled = True + cmbMaxClient.Enabled = True + cmbType.Enabled = True + cmbType_Click() + +End + +Public Sub Form_Close() + + ' We have to be sure of closing the server before exiting + MyServerSocket.Close() + +End + +Public Sub btnPause_Click() + + If Waiting Then + MyServerSocket.Resume() + btnPause.Text = "Pause" + Else + MyServerSocket.Pause() + btnPause.Text = "Resume" + End If + Waiting = Not Waiting + +End + +Public Sub cmbType_Click() + + If cmbType.Index = 0 Then + txtPort.Enabled = True + txtPath.Enabled = False + Else + txtPort.Enabled = False + txtPath.Enabled = True + End If + +End diff --git a/app/examples/Networking/ServerSocket/.src/FrmMain.form b/app/examples/Networking/ServerSocket/.src/FrmMain.form new file mode 100644 index 00000000..67e2895e --- /dev/null +++ b/app/examples/Networking/ServerSocket/.src/FrmMain.form @@ -0,0 +1,61 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(36,28,63,51) + Text = ("Server Socket Example") + Icon = Picture["serversocket.png"] + Resizable = False + { btnListen Button + MoveScaled(44,1,18,4) + Text = ("Listen") + } + { txtPort TextBox + MoveScaled(22,6,21,4) + Text = ("32340") + } + { txtLog TextArea + MoveScaled(1,22,61,28) + } + { btnClose Button + MoveScaled(44,6,18,4) + Enabled = False + Text = ("Close") + } + { TextLabel1 TextLabel + MoveScaled(1,6,15,4) + Text = ("Port") + } + { btnPause Button + MoveScaled(1,17,16,4) + Text = ("Pause") + } + { Label1 Label + MoveScaled(20,17,26,4) + Text = ("Max. number of clients") + Alignment = Align.Right + } + { cmbMaxClient ComboBox + MoveScaled(47,17,15,4) + ReadOnly = True + List = [("No limit"), ("1"), ("2"), ("3"), ("4"), ("5"), ("6"), ("7"), ("8"), ("9"), ("10")] + } + { TextLabel2 TextLabel + MoveScaled(1,1,15,4) + Text = ("Type") + } + { cmbType ComboBox + MoveScaled(22,1,21,4) + ReadOnly = True + List = [("TCP"), ("UNIX")] + } + { TextLabel3 TextLabel + MoveScaled(1,11,20,4) + Text = ("Path") + } + { txtPath TextBox + MoveScaled(22,11,40,4) + } + { MyServerSocket #ServerSocket + #MoveScaled(13,8) + } +} diff --git a/app/examples/Networking/ServerSocket/serversocket.png b/app/examples/Networking/ServerSocket/serversocket.png new file mode 100644 index 0000000000000000000000000000000000000000..9049f7acf57321531fc3f227f17e209bcabc3054 GIT binary patch literal 2122 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 literal 0 HcmV?d00001 diff --git a/app/examples/Networking/UDPServerClient/.lang/ca.mo b/app/examples/Networking/UDPServerClient/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..13710adea70bf32e2f5eb399238f300cd5b3a98f GIT binary patch literal 1541 zcmYk5zi%8x6vrndA>sHHl0aiX&nKiHp*?QTAV+H)e%NQl5izISIYD&e-4}acZ)Ta9 zJKGWx2qBs@G)OdQXb=Sr9kdo5;!j9HO9L$_!uRdo+05PB&-~i=zHjF3gNtXrRcJ53 zUWEM)dkOa0DLl~r2A=@`0ndUD!Nl!JP-Z}UIPD`tv>=g7%!Yw>P=|70Rrj^I1zpc zO1!VY=fHcQ#JdlQ&L2SW_bd1;_y;I)AAtC)fANsKAAyp`Sqvoa1yKB40cAbKqYW;D zuYezcvhQ?g1RvNZ(1{#`z)w+(FlH?kxzW2`4Eza3Y zjol?#nN1Ga7ROW9U5KxcKA9Eg)%_6xCp~l%v&Z3dq97t>ML4q`lEG9Th&ZyT-yGhzh(q-zVS=xOK<8JGH z9bz)_+J;g`=QFzDd|_y)4_!%b?RJ86-Z|8J&UH9k!+yIv(Yu_CSdTM09;176^Jf1< zP}=FX)|`#F@?>;aK|th#_UR{x;@%RuAb}@9e3F$eVaSE^Ng8@H@T%~lS^Kl%}VJdb%o?f)g$R{sHdM}C?B literal 0 HcmV?d00001 diff --git a/app/examples/Networking/UDPServerClient/.lang/ca.po b/app/examples/Networking/UDPServerClient/.lang/ca.po new file mode 100644 index 00000000..9decadc0 --- /dev/null +++ b/app/examples/Networking/UDPServerClient/.lang/ca.po @@ -0,0 +1,104 @@ +# Catalan translation of UDPServerClient +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the UDPServerClient package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: UDPServerClient\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:10+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FrmClient.form:54 +msgid "127.0.0.1" +msgstr "-" + +#: FrmClient.form:59 FrmServer.form:42 +msgid "32340" +msgstr "-" + +#: FrmClient.form:18 +msgid "Client" +msgstr "Client" + +#: FrmClient.form:30 +msgid "Client Side" +msgstr "Costat del client" + +#: FrmClient.class:60 FrmServer.class:73 +msgid "Data --> " +msgstr "Dades -->" + +#: FrmClient.class:50 FrmServer.class:59 +msgid "Error Receiving Data" +msgstr "Error rebent dades" + +#: FrmClient.class:48 FrmServer.class:57 +msgid "Error Sending Data" +msgstr "Error enviant dades" + +#: FrmClient.form:39 +msgid "Host IP" +msgstr "IP del servidor" + +#: FrmServer.form:19 +msgid "New UDP Client window" +msgstr "Nova finestra de client UDP" + +#: FrmClient.form:48 +msgid "Port" +msgstr "Port" + +#: FrmServer.form:30 +msgid "Port :" +msgstr "Port:" + +#: FrmClient.class:59 FrmServer.class:72 +msgid "Received data from : " +msgstr "Dades rebudes des de:" + +#: FrmClient.form:76 +msgid "Send Data" +msgstr "Envia dades" + +#: FrmClient.form:64 FrmServer.form:47 +msgid "Start !" +msgstr "Endavant!" + +#: FrmClient.class:15 FrmServer.class:24 +msgid "Stop" +msgstr "Atura" + +#: FrmServer.form:24 +msgid "Super String Reverse Server" +msgstr "Super servidor de cadenes inverses" + +#: FrmClient.class:46 FrmServer.class:55 +msgid "System does not allow to create a socket" +msgstr "El sistema no permet crear un sòcol" + +#: .project:1 +msgid "UDP sockets example" +msgstr "Exemple de sòcols UDP" + +#: FrmServer.form:14 +msgid "UDP Test" +msgstr "Prova l'UDP" + +#: FrmClient.class:44 FrmServer.class:53 +msgid "Unable to Bind to that port" +msgstr "No es pot vincular amb aquell port" + +#: FrmClient.form:91 +msgid "Write Here data to send then press \"Send Data\"" +msgstr "Escriviu aquí dades per enviar i després premeu \"Envia dades\"" + diff --git a/app/examples/Networking/UDPServerClient/.lang/cs.mo b/app/examples/Networking/UDPServerClient/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..709f29b672b83b752d7657bf89133534854d7df9 GIT binary patch literal 1439 zcmYk4zi%8x6vrndKsbJf@IwlqhYN&+*4Qx;(IrGVeCPP$`=#4EBhrm`C%*OV?W|_T z=h&j6phKcT!m=o?bYUSOghWBLc9%Z@Aw)q-12qMF-|pJZtloZh-n@Cg_Wg0?(l-L@ zHOQNgN07H5pI^WW>u>NG@E`C=@L%vL@WK;9JPTe1`TQz)6?_}K0xp41gDc>xU7x6-F_dr0b!3nbs za=b41GPqvqN8sz&Z-HF<7o~o`+#i(tA4>iRaveW`T=%0=|7*$Lz;~hl9rVD9Xcos` z1lj)*_y%|nya@*7IX-iL*yerU7`(Vf?&EV1uKPs@*KxWk_;?AzWpiB|gV*yA?jzbF zUVtE-eDS}@*W&e$<`?iRilysIA1;XHI8uo#L4Fh}QI}4V=iMgJFh(2dt3XB5DA^=F zDV4rT!ZX#XwvL)T(NQ}ztoNvJN;^>!>K)P3#^o=nio%x)DdZpzjGj=H1UoC7`%W51 z?+D-NRQTJeGUPkMf%D=hO0VmojEE0C^ioynvqr7&wYy%uMyuUw zyZQe8Zl_UcN$b3Uk%^6^)22!*!&ZwrwRVGUHQKdi>rSm+?>Bt^HcXSvZMmtufs&Id zwQ8NaLk@YX)w&Z$^V)2l+f=F2Znhie`7O>bRF-w(Fah3RH$^^9J#-(Wv5btHW&v^?6~kd&V6XGdET znPk5NBkt>|Ufb*_eoXC&j-F22zoq0Jd{*4YXTRqL~$`!d)F+Mh* z7fK}Q4U^3Rm1IYBvaj6n!JZ0EzRxEqJDgCWCdfBW(7NrE#x&h^(`*J?zG5||Y5O$z zIF@0amQC^7agN!K_Y@|CSQ9z2`4p*%xG&S_WQI}N3(HADE-7-&X6#qe9^xK\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "UDP sockets example" +msgstr "Příklad UDP spojení" + +#: FrmClient.class:15 FrmServer.class:24 +msgid "Stop" +msgstr "-" + +#: FrmClient.class:44 FrmServer.class:53 +msgid "Unable to Bind to that port" +msgstr "Nelze se spojit s portem" + +#: FrmClient.class:46 FrmServer.class:55 +msgid "System does not allow to create a socket" +msgstr "Systém nemá přístup k vytváření soketu" + +#: FrmClient.class:48 FrmServer.class:57 +msgid "Error Sending Data" +msgstr "Chyba posílaných dat" + +#: FrmClient.class:50 FrmServer.class:59 +msgid "Error Receiving Data" +msgstr "Chyba příjmaných dat" + +#: FrmClient.class:59 FrmServer.class:72 +msgid "Received data from : " +msgstr "Příjmané data od :" + +#: FrmClient.class:60 FrmServer.class:73 +msgid "Data --> " +msgstr "-" + +#: FrmClient.form:18 +msgid "Client" +msgstr "Klient" + +#: FrmClient.form:30 +msgid "Client Side" +msgstr "Klientská strana" + +#: FrmClient.form:39 +msgid "Host IP" +msgstr "-" + +#: FrmClient.form:48 +msgid "Port" +msgstr "-" + +#: FrmClient.form:54 +msgid "127.0.0.1" +msgstr "-" + +#: FrmClient.form:59 FrmServer.form:42 +msgid "32340" +msgstr "-" + +#: FrmClient.form:64 FrmServer.form:47 +msgid "Start !" +msgstr "-" + +#: FrmClient.form:76 +msgid "Send Data" +msgstr "Poslat data" + +#: FrmClient.form:91 +msgid "Write Here data to send then press \"Send Data\"" +msgstr "Zapište zde data na poslání a zmáčkněte \"Poslat data\"" + +#: FrmServer.form:14 +msgid "UDP Test" +msgstr "-" + +#: FrmServer.form:19 +msgid "New UDP Client window" +msgstr "Nové okno UDP klienta" + +#: FrmServer.form:24 +msgid "Super String Reverse Server" +msgstr "Super server na obrácení řetězeců" + +#: FrmServer.form:30 +msgid "Port :" +msgstr "-" diff --git a/app/examples/Networking/UDPServerClient/.lang/es.mo b/app/examples/Networking/UDPServerClient/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..58639a4f269852970614b916ab09104b9b5fcdad GIT binary patch literal 1428 zcmZvaO^+Kz5QZCAAQ%!Ld`O5ORTgQ56lU2?A`*tMinE?%NAbtV;|&VpkhZ6?+c54n zJ#Cx4A#vftkvp6a2gHGYKoCCwjz}n6xNzct$T4r(lWdUER{80v?yh>L=l$dO%2x{G zCBzBD&xqF${sTS@-l^AD9#!f^JRbv(gRg^6f^UOTdkb6v z2cVq4UgvLsQhx@@{?92vEF_-(2)r_Y?-P}`+p8){H)3RW#0G)2)CslG-^Pqj8lPV8x zqVnm4J>|hmqdw2rS4=s3OjLeQ1s+r3xFYKU>9TZl3XVpebzss|xzWcQl;|07g^AU} zl4Z*!=n{r~9cUuz-uDJ)cG&Y$7#1th6!zKD#B){A>V1)`D)a6m9)&0^qH7RkoQ)}K zZeML)N$6VA&(mJFk@X@-HnJGu-Z-Y~NwXhydQq!MJH5D*p8BBIO&V=og=pZltx6r7 zkLmJmyG`9@C!q^TrnISBOE%@ZFs?%&|Mjs~Aj0!GEe5ATlo@tsv zvbGxOd!@x$Dx0zou0||YMYm;-0<(gvf@76Pbze99=^IL|8A;-#%Cy%oj~J_hkxT)9 zqeW`PT;|q~R-U$X!B#_RNtF?sX;_SKWGpTbgL#@-vJeWNENvzhTPRG&6IU5=njU)N o39mI-icYGLH$zSOlj*JRsT8>j`I;8D;pqT7OIUpMLH(=hE*>6NH~;_u literal 0 HcmV?d00001 diff --git a/app/examples/Networking/UDPServerClient/.lang/es.po b/app/examples/Networking/UDPServerClient/.lang/es.po new file mode 100644 index 00000000..71c1f02b --- /dev/null +++ b/app/examples/Networking/UDPServerClient/.lang/es.po @@ -0,0 +1,101 @@ +# #-#-#-#-# FrmClient.pot (PACKAGE VERSION) #-#-#-#-# +# /home/daniel/GAMBAS/gambas-0.93pre1/examples/Networking/UDPServerClient/FrmClient.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +# #-#-#-#-# FrmServer.pot (PACKAGE VERSION) #-#-#-#-# +# /home/daniel/GAMBAS/gambas-0.93pre1/examples/Networking/UDPServerClient/FrmServer.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FrmClient.class:14 FrmServer.class:22 +msgid "Stop" +msgstr "Parar" + +#: FrmClient.class:20 FrmServer.class:27 +msgid "Start !" +msgstr "¡ Iniciar !" + +#: FrmClient.class:42 FrmServer.class:51 +msgid "Unable to Bind to that port" +msgstr "Imposible abrir el puerto" + +#: FrmClient.class:44 FrmServer.class:53 +msgid "System does not allow to create a socket" +msgstr "El sistema no permite crear un nuevo socket" + +#: FrmClient.class:46 FrmServer.class:55 +msgid "Error Sending Data" +msgstr "Error enviando datos" + +#: FrmClient.class:48 FrmServer.class:57 +msgid "Error Receiving Data" +msgstr "Error recibiendo datos" + +#: FrmClient.class:57 FrmServer.class:70 +msgid "Received data from : " +msgstr "Datos recibidos desde:" + +#: FrmClient.class:58 FrmServer.class:71 +msgid "Data --> " +msgstr "Datos -->" + +#: FrmClient.class:93 +msgid "Client" +msgstr "Cliente" + +#: FrmClient.class:105 +msgid "Client Side" +msgstr "Lado cliente" + +#: FrmClient.class:114 +msgid "Host IP" +msgstr "IP del Host" + +#: FrmClient.class:123 +msgid "Port" +msgstr "Puerto" + +#: FrmClient.class:129 +msgid "127.0.0.1" +msgstr "127.0.0.1" + +#: FrmClient.class:134 FrmServer.class:129 +msgid "32340" +msgstr "32340" + +#: FrmClient.class:151 +msgid "Send Data" +msgstr "Enviar datos" + +#: FrmClient.class:165 +msgid "Write Here data to send then press \"Send Data\"" +msgstr "Escriba aquí los datos a enviar y pulse \"Enviar datos\"" + +#: FrmServer.class:101 +msgid "UDP Test" +msgstr "Test UDP" + +#: FrmServer.class:106 +msgid "New UDP Client window" +msgstr "Nueva ventana de cliente UDP" + +#: FrmServer.class:111 +msgid "Super String Reverse Server" +msgstr "Superservidor de Inversión de Cadenas" + +#: FrmServer.class:117 +msgid "Port :" +msgstr "Puerto:" diff --git a/app/examples/Networking/UDPServerClient/.lang/nl.mo b/app/examples/Networking/UDPServerClient/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..1e2722dcc9dfedc83eb4d77e14b9b0844fb68a49 GIT binary patch literal 1425 zcmY+DJC7Vi6ot!RzzlC=UNWG|CV)^4^Dwe$jPbIbT`yj*N87V&WXVCz^xd6Wdb(QG zH8Wm|n1F}`iG+x7e*v*Ygd_g|0s;~e5V8;=fODtXHq@y4>#oPGb8nsb>G;vF721pF zucF^Ze+~WfBY2?w20jk{4n6|@4L%AUc}S@zz+<4yp8!vQuYN3IFG1P=1t{yk0%hGd;7RaX@GbCr@EG_r_&oS)J^v5b#P`XEp$FPN0RgpxAi_^U ziF*xv3LMtsCHNA)J@_*CB`E8@1786TLCNiQAb1+Rm$Uz*erpNl%kT4{2Y)KSioyq`fA9rs!TFVCVQUG*Hg+@Cbb zQSv}p)YIsYv1oEPi`MR(Znp4it8S-zs-;$P%SEU`sm)kjFu{=4=ScOucb>L*#CB$j zJ(8JvFy$h%ZC6G}))uoyfI z#s_*srNNadotEq=4PFk~;u-sjDP@lrl^#@qCzLs^sBnQyp1U~(MVR?Dm_;fE!`Ks zr25|N^C;-`Oz%Lbbwxt`<&{gzYdzZOZKdm1HXHpb8fjRW)TkvV)M>R(>DK$Y)22>0 zdGGW)t#+%`*f3Sl18<5dH^KRYuG&05pe@6pqO)`PIG>>&xhtEDf{m-OEqUr)$xY|? z&LKdtH#K{#2W%z@Z7grDZHthz8yn|y8`oT3zubEuwcTtrR$LKa20b_^AuaHY@NStK zTYNyHu|e&_huecyedfVBQMk)q_ll7dHzagsXhQ@404jh-qLE-O#=<> zek>|ZoIxwD=l|ol;+xT7Y7NC$n9K4s+G`X;(~Jw4z{6Y7VR$Cdy>(bz6rv{z7b4N3 z#YCKr{(*t2U~u7u<$&8~Vr+>fLH^>UTZf(ttQQ;IbI%Ot4Vm*yT{1=7JA{3(b%HRN hWf({lrK7R8`@1x@uyIz~2#!bOIzMb#`meYp^)Iu-RfPZm literal 0 HcmV?d00001 diff --git a/app/examples/Networking/UDPServerClient/.lang/nl.po b/app/examples/Networking/UDPServerClient/.lang/nl.po new file mode 100644 index 00000000..34cb63bc --- /dev/null +++ b/app/examples/Networking/UDPServerClient/.lang/nl.po @@ -0,0 +1,96 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2014-09-21 23:57+0100\n" +"Last-Translator: Willy Raets \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "UDP sockets example" +msgstr "UDP sockets voorbeeld" + +#: FrmClient.form:18 +msgid "Client" +msgstr "Cliënt" + +#: FrmClient.form:30 +msgid "Client Side" +msgstr "Cliënt zijde" + +#: FrmClient.form:39 +msgid "Host IP" +msgstr "Gast IP" + +#: FrmClient.form:48 +msgid "Port" +msgstr "Poort" + +#: FrmClient.form:54 +msgid "127.0.0.1" +msgstr "-" + +#: FrmClient.form:59 FrmServer.form:42 +msgid "32340" +msgstr "-" + +#: FrmClient.form:64 FrmServer.form:47 +msgid "Start !" +msgstr "-" + +#: FrmClient.form:76 +msgid "Send Data" +msgstr "Verzend Data" + +#: FrmClient.form:91 +msgid "Write Here data to send then press \"Send Data\"" +msgstr "Schrijf hier de data om te verzenden \"Verzend Data\"" + +#: FrmClient.class:15 FrmServer.class:24 +msgid "Stop" +msgstr "-" + +#: FrmClient.class:44 FrmServer.class:53 +msgid "Unable to Bind to that port" +msgstr "Kan niet binden met die poort" + +#: FrmClient.class:46 FrmServer.class:55 +msgid "System does not allow to create a socket" +msgstr "Systeem staat niet toe een socket te creëren" + +#: FrmClient.class:48 FrmServer.class:57 +msgid "Error Sending Data" +msgstr "Fout bij data verzending" + +#: FrmClient.class:50 FrmServer.class:59 +msgid "Error Receiving Data" +msgstr "Fout bij data ontvangst" + +#: FrmClient.class:59 FrmServer.class:72 +msgid "Received data from : " +msgstr "Data ontvangen van:" + +#: FrmClient.class:60 FrmServer.class:73 +msgid "Data --> " +msgstr "-" + +#: FrmServer.form:14 +msgid "UDP Test" +msgstr "-" + +#: FrmServer.form:19 +msgid "New UDP Client window" +msgstr "Nieuw UDP cliënt venster" + +#: FrmServer.form:24 +msgid "Super String Reverse Server" +msgstr "-" + +#: FrmServer.form:30 +msgid "Port :" +msgstr "Poort:" + diff --git a/app/examples/Networking/UDPServerClient/.project b/app/examples/Networking/UDPServerClient/.project new file mode 100644 index 00000000..24f98966 --- /dev/null +++ b/app/examples/Networking/UDPServerClient/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.0 +Title=UDP sockets example +Startup=FrmServer +Icon=udpsocket.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.net +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@desnouettes +License=General Public Licence diff --git a/app/examples/Networking/UDPServerClient/.src/FrmClient.class b/app/examples/Networking/UDPServerClient/.src/FrmClient.class new file mode 100644 index 00000000..dd1ba40e --- /dev/null +++ b/app/examples/Networking/UDPServerClient/.src/FrmClient.class @@ -0,0 +1,73 @@ +' Gambas class file +Public UDPClient As UdpSocket +Public Sub Form_Open() + UDPClient = New UdpSocket As "UDPClient" +End +'/////////////////////////////////////////////////// +' Client stuff +'/////////////////////////////////////////////////// +Public Sub Button2_Click() + + If UDPClient.Status <= Net.Inactive Then + 'UDPClient.Path = "/tmp/gambas-udp-client" + UDPClient.Bind + If UDPClient.Status = Net.Active Then + Button2.Text = ("Stop") + TxtData.Enabled = True + Button3.Enabled = True + End If + Else + Close #UDPClient + Button2.Text = ("Start !") + TxtData.Enabled = False + Button3.Enabled = False + End If + + +End + +Public Sub Button3_Click() + + UDPCLient.TargetPort = TxtPort.Text + UDPCLient.TargetHost = TxtIP.Text + 'UDPClient.TargetPath = "/tmp/gambas-udp-socket" + Write #UDPCLient, TxtData.Text, Len(TxtData.Text) +End + +Public Sub UDPClient_Error() + + Button2.Text = ("Start !") + TxtData.Enabled = False + Button3.Enabled = False + Select Case UDPClient.Status + Case Net.CannotBindSocket + Message.Error(("Unable to Bind to that port")) + Case Net.CannotCreateSocket + Message.Error(("System does not allow to create a socket")) + Case Net.CannotRead + Message.Error(("Error Sending Data")) + Case Net.CannotWrite + Message.Error(("Error Receiving Data")) + End Select + +End + +Public Sub UDPClient_Read() + + Dim sCad As String + Read #UDPClient, sCad, Lof(UDPClient) + TxtRecClient.Text = TxtRecClient.Text & ("Received data from : ") & UDPClient.SourceHost & ":" & UdpClient.SourcePort & Chr(13) & Chr(10) + TxtRecClient.Text = TxtRecClient.Text & ("Data --> ") & sCad & Chr(13) & Chr(10) + +End + + +Public Sub Form_Close() + + If UDPClient.Status > 0 Then Close #UDPClient + + +End + + + diff --git a/app/examples/Networking/UDPServerClient/.src/FrmClient.form b/app/examples/Networking/UDPServerClient/.src/FrmClient.form new file mode 100644 index 00000000..fdf39dd6 --- /dev/null +++ b/app/examples/Networking/UDPServerClient/.src/FrmClient.form @@ -0,0 +1,70 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(10.1667,48.5,59.6667,43) + Text = ("Client") + Resizable = False + { Panel2 Panel + MoveScaled(0,0,60,43) + Background = &H4F87E7& + { Label3 Label + MoveScaled(0,1,29,4) + Font = Font["Bold"] + Background = &H4F87E7& + Foreground = &HFFFF7F& + Text = ("Client Side") + Alignment = Align.Center + } + { Label4 Label + MoveScaled(1,5,12,4) + Font = Font["Bold"] + Background = &H4F87E7& + Foreground = &HFFFF7F& + Text = ("Host IP") + Alignment = Align.Center + } + { Label5 Label + MoveScaled(1,9,12,4) + Font = Font["Bold"] + Background = &H4F87E7& + Foreground = &HFFFF7F& + Text = ("Port") + Alignment = Align.Center + } + { TxtIP TextBox + MoveScaled(14,5,14,4) + Text = ("127.0.0.1") + } + { TxtPort TextBox + MoveScaled(14,10,8,4) + Text = ("32340") + } + { Button2 Button + MoveScaled(35,5,11,4) + Text = ("Start !") + } + { TxtData TextBox + MoveScaled(2,19,42,4) + Enabled = False + Text = ("") + } + { Button3 Button + MoveScaled(45,19,13,4) + Enabled = False + Text = ("Send Data") + Default = True + } + { TxtRecClient TextArea + MoveScaled(2,24,56,17) + Text = ("") + ReadOnly = True + } + { Label1 Label + MoveScaled(2,15,53,3) + Font = Font["Bold"] + Background = &H4F87E7& + Foreground = &HFFFF00& + Text = ("Write Here data to send then press \"Send Data\"") + } + } +} diff --git a/app/examples/Networking/UDPServerClient/.src/FrmServer.class b/app/examples/Networking/UDPServerClient/.src/FrmServer.class new file mode 100644 index 00000000..c4ea51ab --- /dev/null +++ b/app/examples/Networking/UDPServerClient/.src/FrmServer.class @@ -0,0 +1,86 @@ +' Gambas class file + +'PRIVATE UDPServer AS UdpSocket +Public Sub Button1_Click() + + Dim MyFrm As FrmClient + MyFrm = New FrmClient + MyFrm.Visible = True + + + +End + +'////////////////////////////////////////////////// +' Server Stuff +'////////////////////////////////////////////////// +Public Sub Button2_Click() + + If UDPServer.Status <= Net.Inactive Then + UDPServer.Port = CInt(TextBox1.Text) + 'UDPServer.Path = "/tmp/gambas-udp-server" + UDPServer.Bind + If UDPServer.Status = Net.Active Then + Button2.Text = ("Stop") + TextBox1.Enabled = False + End If + Else + Close #UDPServer + Button2.Text = ("Start !") + TextBox1.Enabled = True + End If + +End + +'PUBLIC SUB Form_Open() +' +' UDPServer = NEW UdpSocket AS "UDPServer" +' +'END + +Public Sub Form_Close() + + If UDPServer.Status > 0 Then Close #UDPServer + +End + +Public Sub UDPServer_Error() + + Button2.Text = "Start !" + TextBox1.Enabled = True + Select Case UDPServer.Status + Case Net.CannotBindSocket + Message.Error(("Unable to Bind to that port")) + Case Net.CannotCreateSocket + Message.Error(("System does not allow to create a socket")) + Case Net.CannotRead + Message.Error(("Error Sending Data")) + Case Net.CannotWrite + Message.Error(("Error Receiving Data")) + End Select + +End + +Public Sub UDPServer_Read() + + Dim sCadRet As String + Dim sCad As String + Dim MyLoop As Integer + + sCadRet = "" + Read #UDPServer, sCad, Lof(UDPserver) + TextArea1.Text = TextArea1.Text & ("Received data from : ") & UDPServer.SourceHost & ":" & UDPServer.SourcePort & Chr(13) & Chr(10) + TextArea1.Text = TextArea1.Text & ("Data --> ") & sCad & Chr(13) & Chr(10) + If Len(sCad) > 0 Then + For myloop = String.Len(sCad) To 1 Step -1 + sCadRet = sCadRet & String.Mid(sCad, myloop, 1) + Next + UDPServer.TargetHost = UDPServer.SourceHost + UDPServer.TargetPort = UDPServer.SourcePort + 'UDPServer.TargetPath = UDPServer.SourcePath + Write #UDPServer, sCadRet, Len(sCadRet) + End If + +End + + diff --git a/app/examples/Networking/UDPServerClient/.src/FrmServer.form b/app/examples/Networking/UDPServerClient/.src/FrmServer.form new file mode 100644 index 00000000..3c4a97dc --- /dev/null +++ b/app/examples/Networking/UDPServerClient/.src/FrmServer.form @@ -0,0 +1,38 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(9,10.5,37,41) + Text = ("UDP Test") + Resizable = False + { Button1 Button + MoveScaled(4,1,30,4) + Text = ("New UDP Client window") + } + { Label1 Label + MoveScaled(2,7,33,4) + Text = ("Super String Reverse Server") + Alignment = Align.Center + } + { Label2 Label + MoveScaled(1,12,10,4) + Text = ("Port :") + Alignment = Align.Center + } + { TextArea1 TextArea + MoveScaled(1,17,35,23) + Text = ("") + ReadOnly = True + } + { TextBox1 TextBox + MoveScaled(12,12,10,4) + Text = ("32340") + } + { Button2 Button + MoveScaled(23,12,13,4) + Text = ("Start !") + } + { UDPServer #UdpSocket + #X = 132 + #Y = 174 + } +} diff --git a/app/examples/Networking/UDPServerClient/udpsocket.png b/app/examples/Networking/UDPServerClient/udpsocket.png new file mode 100644 index 0000000000000000000000000000000000000000..d53f902f8dab20080e181ae74c0bb13501ba6203 GIT binary patch literal 2161 zcmV-%2#)uOP)}EH0W7bP50d0vU8YQ4D7^EVhl+ubt2(=ViCAE}7 zg|ww!h?G(i+mcvpB-NOt2Gk}}@Eea@UYr+=)qBD6F^UU>QV z{`h!*JkRreoCDWTM~@!u86O`%G&wnW?D+BHv1>W|-%9@c<`4+o5b%QvP=Hzv3J-vN z|F8ASeQ{WH?8{_f4owKQ@eK!QcjD|o{aMo%=+N|}}%@00UOe7M) zY&I**%*>>wr>CDV4C90A2GHSh4Rqaq{|~*#ejDb&jo|ZuEK{^KAsyYJ^>5#;<@0%_ zE?j6_dhflTXW*6n`}f<24av?hX1MYWbpNX`$|6yQY z;Crg7x|f%iE4f^5t|E04xwmJ6hTq21&xaAr@pev(n1CX+9v)9GI~1>k{gT0XCmFMt#Q6Ewd>zxhv+6KVQIgZ_k$ zs`+sQiBzK#?V)>-TrTnI^t3rKF>xrfwDhCL9(!!LDFC-9Ch+?p9D?{}NM1y^#ZRZp zqBD>MSpreV7DST8AoAc2WKWQ*$t3AFUeBjqKk?%C$3AFkIDqI@Sc%4h3DA%NL?50;=YIdO%#^QVZ6o$z%p%x-;NKx`U7)m^Q$N8s;o!O7QQXe(SU zLB#?|21x-)X4!OOS`O)%t7N9e=+c*I83?KIP|MvNEkRGS0J5#A4OW+7brmE5u6zvL zF?fF-w)H@p3uKwTV3|wFF$6QmXtYQq7{T4Jh1Cz2dhND=##*k>6o4tqSxUwA;hF-o zDbQ5t-T?2UVRH?Jy6A3`2nP*BQ9@B#5Cj2g7DCX6xMuL@7QfgYj|E&Fy;qUtyWAc< zp}W*}pG)&QRnso78Af3(H)E_BCyK?=`**GyMF82b8y|-(a~rbsspm3e7S@4!<8W>c z77Tp{AzQ?mHLzrPY9*I<58r;vL#pitW7YYq0fTLS^Xtc!(AEnlgc znR2NzRky6aRZEprW_s4>>5R0>mX#1<0SM?g4U2l+#V-hRBCmSIhm~8Lcc3Zhg?DEZ-Lj&gTEE>1ntN+6sx3*8kL@%L^^v&E?%Ow@)j-?Xe@!>N6qgyOWEwZO#uuK4?o`9 z+xu`h9G1)F^3wUY-~NsI+gH9}+#cI#4@ZR+J%$tCP9pIoT&hBLE{|x{z^;SWhI)-c zEOxn`&7Ny21HoXhI}{4JOw%k+PEJ03F_U>_E|Y&^@pN)><#H8yU?=h6EriY0}qTs)0Wtbt`gp#&c-QRyqqO`bdVyZfLnG#kP%WI_xC0$-2E(J29 zi{0JbL667dT)cSkFEims%lM5O`V*WnE3AA%s44&=d{1LNPruNy$6Qu%8pleyb8&HVWI n_>cGO*)t390N8->zs~*(t`eRES13jV00000NkvXXu0mjfthg;C literal 0 HcmV?d00001 diff --git a/app/examples/Networking/WebBrowser/.directory b/app/examples/Networking/WebBrowser/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Networking/WebBrowser/.icon.png b/app/examples/Networking/WebBrowser/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..891cc0287affd5cb5a64dc2bb2120470c7e9f51e GIT binary patch literal 3585 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 g#~S$xE2k, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: WebBrowser\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 23:31+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FBrowser.form:100 +msgid "0" +msgstr "-" + +#: FBrowser.form:95 +msgid "-1" +msgstr "-" + +#: FBrowser.form:105 +msgid "+1" +msgstr "-" + +#: FBrowser.form:90 +msgid "-2" +msgstr "-" + +#: FBrowser.form:110 +msgid "+2" +msgstr "-" + +#: FBrowser.form:115 +msgid "+3" +msgstr "-" + +#: FBrowser.form:120 +msgid "+4" +msgstr "-" + +#: FBrowser.form:262 FEditable.form:18 +msgid "Bold" +msgstr "Negreta" + +#: FAuth.form:78 FOption.form:68 +msgid "Cancel" +msgstr "Canceŀla" + +#: FBrowser.form:439 +msgid "Case sensitive" +msgstr "Sensible a caixa" + +#: FBrowser.form:306 +msgid "Centered" +msgstr "Centrat" + +#: FBrowser.form:218 +msgid "Copy" +msgstr "Copia" + +#: FBrowser.form:226 +msgid "Cut" +msgstr "Retalla" + +#: FDownloadList.form:9 +msgid "Download manager" +msgstr "Gestor de baixades" + +#: FBrowser.form:384 +msgid "Font family" +msgstr "Família de la font" + +#: FBrowser.form:394 +msgid "Font size" +msgstr "Mida de la font" + +#: FBrowser.form:74 +msgid "Gambas WebKit" +msgstr "-" + +#: FBrowser.form:446 +msgid "Highlight" +msgstr "Ressaltat" + +#: FOption.form:33 +msgid "Host" +msgstr "Amfitrió" + +#: FOption.form:81 +msgid "HTTP proxy" +msgstr "Servidor intermediari HTTP" + +#: FBrowser.form:334 +msgid "Indent" +msgstr "Sagnat" + +#: FBrowser.form:270 +msgid "Italic" +msgstr "Cursiva" + +#: FBrowser.form:322 +msgid "Justified" +msgstr "Justificat" + +#: FBrowser.form:298 +msgid "Left aligned" +msgstr "Alineat a l'esquerra" + +#: .project:1 +msgid "Light browser based on WebKit component" +msgstr "Navegador lleuger basat en el component WebKit" + +#: FOption.form:81 +msgid "No proxy" +msgstr "Sense servidor intermediari" + +#: FAuth.form:72 FOption.form:62 +msgid "OK" +msgstr "D'acord" + +#: FBrowser.form:354 +msgid "Ordered list" +msgstr "Llista ordenada" + +#: FAuth.form:50 FOption.form:48 +msgid "Password" +msgstr "Contrasenya" + +#: FBrowser.form:234 +msgid "Paste" +msgstr "Enganxa" + +#: FAuth.form:17 +msgid "Please authenticate" +msgstr "Si us plau, autentiqueu" + +#: FOption.form:38 +msgid "Port" +msgstr "-" + +#: FOption.form:21 +msgid "Proxy configuration" +msgstr "Configuració del servidor intermediari" + +#: FBrowser.form:250 +msgid "Redo" +msgstr "Refés" + +#: FBrowser.form:159 +msgid "Refresh" +msgstr "Refresca" + +#: FBrowser.form:314 +msgid "Right aligned" +msgstr "Alineat a la dreta" + +#: FOption.form:81 +msgid "SOCKS5 proxy" +msgstr "Servidor intermediari SOCKS5" + +#: FDownload.form:18 +msgid "Starting download..." +msgstr "Iniciant la descàrrega..." + +#: FBrowser.form:286 +msgid "Strikethrough" +msgstr "Ratllat" + +#: FBrowser.form:378 +msgid "Text background" +msgstr "Fons del text" + +#: FBrowser.form:372 +msgid "Text color" +msgstr "Color del text" + +#: FBrowser.form:165 +msgid "Toggle edit mode" +msgstr "Commuta el mode d'edició" + +#: FOption.form:28 +msgid "Type" +msgstr "Tipus" + +#: FBrowser.form:278 +msgid "Underline" +msgstr "Subratllat" + +#: FBrowser.form:242 +msgid "Undo" +msgstr "Desfés" + +#: FBrowser.form:342 +msgid "Unindent" +msgstr "Desfés sagnat" + +#: FBrowser.form:362 +msgid "Unordered list" +msgstr "Llista desordenada" + +#: FAuth.form:39 FOption.form:43 +msgid "User" +msgstr "Usuari" + diff --git a/app/examples/Networking/WebBrowser/.lang/cs.mo b/app/examples/Networking/WebBrowser/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..ce195aa32e79dea4ce56ed821b5ef9a3f00b28c8 GIT binary patch literal 2362 zcmZXUO>7%Q6vu~BzEUXAmeQ8e4&|ez>84F7QZS{|P242djw9Pi)f|u}-if{OddAGo zI*kQKH~>ANs;Z(MEODShR>cLRN~tQXjl>1P0U>cf0wHljfPWMdl)Q& zJHgA~qhJlh#ooh>T(!Wbz&5x8`~-XqybeAAcEADfwp+gg;$l7A$o_YZKY+CFXYg_G zH}EL<7f62hKD3_CVUXS*0crgth>MNjwi`SLVhby}`6V~6gO6an2~u2FK(g}@xCi{q zt$zVNiTT$c#r-Wv`TYoz-QPiq^G`Sb8>BeygH)$~-TF>S4h7u}QeF4JIdQM!KF0%& z2OSSNau63g;&>dSI8TFA=W&qYD}m(yUH85UQhx7)6wk*X`Mu$|;@;nM^DjZlrwfvu zZ$R>M52XD+foH%!+U|tU%Iq8n6*dO$1#=+jRluh~36kCtNO~WDROb&t z(z^!k2d{(V_bZU{xCc_c--FMBzkt+t|G4%0ATG8INzlF>;4|O>kn%c(8`-Bl^!+G? z0}vV%=MaRxFYO(KPzW)XMS&(Fnrj_2(#POExYV^CIGxgq)RASA z%7+U!X+;=R*gJ7#gM|S8a&p0PtW_0FNT1Iey%b5qVORQGD`%Wnbfc*iS((z#Ng>M$ z#;3q}7{JT4h@vHJe0s2wO^1@w6tS&gTTl_SrVaEcL>N>HK{YnQ23oPA^fhh^Mn*MO zT#IBQu2Nw%TYBC3ELmY}psL(=6&e~EVkK*Wy0kT;<7$nSuNQWT>63K4ed)-UT#V@gX)+tP-{;b(_p<^GYZLUrZ5U=l6u+EF7rlJJ8%y(vLMlHwI9Hg;4CGKIuWW>h zLSeN@@ryIL9G^<(GyH5OpPtN}PmhfiGo{iwsH$p=M)1l~G*Ud5o*JJamuGXi^ICBb#~@)`B!% zMpd+rf~WY%d|(G~{h}rdvrzC0$s8SHTw$cmFn#@mdITtz^}Rw(6~r zy2lE>Njm+zb-}W+X(!#5U~>YV6xr*bU-*2e>#lUC`5*|Cz5R?8f-#hh0T&FR0fR`CEW%c z2W^6OylCkk0C8tG&Ot>wb92q!ie-g)rCZpzmW06RZ#9iTz#dj=NoNt^b+=ZG4#IT* zhU|Ch#;Vw&DAT$vd}z_\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Light browser based on WebKit component" +msgstr "Lehký prohlížeč založený na komponentě WebKit" + +#: FAuth.form:17 +msgid "Please authenticate" +msgstr "Prosím autorizujte se" + +#: FAuth.form:39 FOption.form:43 +msgid "User" +msgstr "Uživatel" + +#: FAuth.form:50 FOption.form:48 +msgid "Password" +msgstr "Heslo" + +#: FAuth.form:72 FOption.form:62 +msgid "OK" +msgstr "-" + +#: FAuth.form:78 FOption.form:68 +msgid "Cancel" +msgstr "Zrušit" + +#: FBrowser.form:74 +msgid "Gambas WebKit" +msgstr "-" + +#: FBrowser.form:90 +msgid "-2" +msgstr "-" + +#: FBrowser.form:95 +msgid "-1" +msgstr "-" + +#: FBrowser.form:100 +msgid "0" +msgstr "-" + +#: FBrowser.form:105 +msgid "+1" +msgstr "-" + +#: FBrowser.form:110 +msgid "+2" +msgstr "-" + +#: FBrowser.form:115 +msgid "+3" +msgstr "-" + +#: FBrowser.form:120 +msgid "+4" +msgstr "-" + +#: FBrowser.form:144 +msgid "Refresh" +msgstr "Obnovit" + +#: FBrowser.form:165 +msgid "Toggle edit mode" +msgstr "Přepnout režim úprav" + +#: FBrowser.form:218 +msgid "Copy" +msgstr "Kopírovat" + +#: FBrowser.form:226 +msgid "Cut" +msgstr "Výjmout" + +#: FBrowser.form:234 +msgid "Paste" +msgstr "Vložit" + +#: FBrowser.form:242 +msgid "Undo" +msgstr "Dopředu" + +#: FBrowser.form:250 +msgid "Redo" +msgstr "Zpět" + +#: FBrowser.form:262 FEditable.form:18 +msgid "Bold" +msgstr "Tučné" + +#: FBrowser.form:270 +msgid "Italic" +msgstr "Kurzíva" + +#: FBrowser.form:278 +msgid "Underline" +msgstr "Podtržené" + +#: FBrowser.form:286 +msgid "Strikethrough" +msgstr "Přeškrtnutí" + +#: FBrowser.form:298 +msgid "Left aligned" +msgstr "Zarovnat vlevo" + +#: FBrowser.form:306 +msgid "Centered" +msgstr "Vycentrovat" + +#: FBrowser.form:314 +msgid "Right aligned" +msgstr "Zarovant vpravo" + +#: FBrowser.form:322 +msgid "Justified" +msgstr "Zarovnáná do bloku" + +#: FBrowser.form:334 +msgid "Indent" +msgstr "Odrážka" + +#: FBrowser.form:342 +msgid "Unindent" +msgstr "Neodrážkovat" + +#: FBrowser.form:354 +msgid "Ordered list" +msgstr "Číslovaný seznam" + +#: FBrowser.form:362 +msgid "Unordered list" +msgstr "Nečíslovaný seznam" + +#: FBrowser.form:372 +msgid "Text color" +msgstr "Barva textu" + +#: FBrowser.form:378 +msgid "Text background" +msgstr "Pozadí textu" + +#: FBrowser.form:384 +msgid "Font family" +msgstr "Rodina fontů" + +#: FBrowser.form:394 +msgid "Font size" +msgstr "Velikost fontu" + +#: FBrowser.form:439 +msgid "Case sensitive" +msgstr "Rozlišovat velikost" + +#: FBrowser.form:446 +msgid "Highlight" +msgstr "Zvýraznění" + +#: FDownload.form:18 +msgid "Starting download..." +msgstr "Zahájení stahování..." + +#: FDownloadList.form:9 +msgid "Download manager" +msgstr "Manažer stahování" + +#: FOption.form:21 +msgid "Proxy configuration" +msgstr "Konfigurace proxy" + +#: FOption.form:28 +msgid "Type" +msgstr "Typ" + +#: FOption.form:33 +msgid "Host" +msgstr "-" + +#: FOption.form:38 +msgid "Port" +msgstr "-" + +#: FOption.form:81 +msgid "HTTP proxy" +msgstr "-" + +#: FOption.form:81 +msgid "No proxy" +msgstr "Žádné proxy" + +#: FOption.form:81 +msgid "SOCKS5 proxy" +msgstr "-" diff --git a/app/examples/Networking/WebBrowser/.lang/de.mo b/app/examples/Networking/WebBrowser/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..beac4279cac36123a83dfc7b98f217bbd48d66cf GIT binary patch literal 2368 zcmZ9MO>7%Q6vqcBUx5~AODUzLL!q=Z-J~fXf+?kr<0Qs*9Q@I!<+R=%?+#wixU;iK zlOsr6I3R=&w-%%xkT`HFD(cM@QpE)h2ysE89JnEI;liQ(pFQ~y6TkiK%+9>8c`v{1 z-tig3n1URHT)T&{0(kO1JTRtrFm^Bay5kwgw;X32i;fE*RIoCLAFJZ=09XfiffvAs zz!=1jUBZJ*t$>e%>)<2cN8rQY=ipB8OK=SQ#;tz`;>T{`LHchy{s_{!U%*Gf-@#+x zUm)4tcmF7!!ytV>0@C^hf)8T750YP(LDKUfxEK7?t$zhR zhWT}n{Qe%K_LsLC0qu zIY{=7fMo9&co>{=^LIe9`z}avgdq8O$<5yf$)ArwvU>%jc)kGXp1yYL-@5f1Ah~=C zBzr%Ar1xhq2i^hc{2>@6eaAt{(+QCL$l*cf3XWBf_AP^SFBd?v+jHO7K)QzyK(g}* zcmTWtlHMDRw?WeTEBF-n2S|1IFG%O_f^>c-Hj#e^z$d_?AU?6zK=OmmP(E=zLpuQ5 z2bq9S-Y8e&kf$LpLQX+w9ETi%kZ!0N4$@CP?uJm0QCu`U2*tX6JdX+G73pJ>+dsH> z>fP;QKPD$26x&J2F$l%+5`@Mf$Wh3%5UPjgAXLBff8ZGi<%mOGfY6}aY>X%AWqgW_ zpJwAPv+-A$H^scu2z5p$AuISQ5DAb9o(h#pD^~<72xWy4_^kVDtdLnYt5;Q`{gC&3 zIjt=3_&u4djowqaF4(-^Yx^l*7VWaMY@yj);(enpudxLg#R&w9I<>5%LYQDB z>nAc`Z)d5M9SM6C(XkwBk)jFh=WU}`Q(-vtijZsNl=DFM`dX2eMeU5#%B*HWa-1g; zR+juUUDYO}7c1COA}CBhvoVgzz^65Bu#a4XLe-H`W_&BPVs#N}JUT|CF{=+f+3>4T zE0i0rI-3pa8!J`BLzkh6i3!%QMs|gbjn1N&HN|Cw;RoFaa}|!}fljnxO&vvv;3AZk z_jD*&bFD8}3)wM=RKquosT`%Nr4YeHd0|qxp5sgTLODNQ2yn@4-sr(qZ?c>SsnQ@eLbfy9Ra^K$4D;+FTrg6g2Fkh*Vkw`c z*r|j>g>II;S9N9IfD+lEJI7ZyiOk^o;HrQhRPf%gn%IIcE84`OjeQ;~N@WH&gDxiJ z47aqxW)dBAQ{S$m%vCpS4{oYZMzoQ`eB!GvYJO%^`zNXRhu(##-VR3%xGwO@8(kn+ z88PyaagWUll!_8o@CrHsbv|kZO9ncFn-LtztDZG*3KQ!DdlyB< z6Q43tM`G=!yQ5ZcA#eVVSd}K^kw}MWveXc>nF(TAF>-h>)Dp3ze=?ETXo-$*+JY6) y3J4X=kfl89As2o^xkjC!5pZBrN1YNH4X&vOdBd%3B)Fxb{{=rIRA$%FEdK$nUHTaS literal 0 HcmV?d00001 diff --git a/app/examples/Networking/WebBrowser/.lang/de.po b/app/examples/Networking/WebBrowser/.lang/de.po new file mode 100644 index 00000000..e3b6b13c --- /dev/null +++ b/app/examples/Networking/WebBrowser/.lang/de.po @@ -0,0 +1,205 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Light browser based on WebKit component" +msgstr "Ein schlanker Browser, basierend auf der WebKit-Komponente" + +#: FAuth.form:17 +msgid "Please authenticate" +msgstr "Anmeldung" + +#: FAuth.form:39 FOption.form:43 +msgid "User" +msgstr "Benutzer" + +#: FAuth.form:50 FOption.form:48 +msgid "Password" +msgstr "Passwort" + +#: FAuth.form:72 FOption.form:62 +msgid "OK" +msgstr "-" + +#: FAuth.form:78 FOption.form:68 +msgid "Cancel" +msgstr "Abbrechen" + +#: FBrowser.form:74 +msgid "Gambas WebKit" +msgstr "-" + +#: FBrowser.form:90 +msgid "-2" +msgstr "-" + +#: FBrowser.form:95 +msgid "-1" +msgstr "-" + +#: FBrowser.form:100 +msgid "0" +msgstr "-" + +#: FBrowser.form:105 +msgid "+1" +msgstr "-" + +#: FBrowser.form:110 +msgid "+2" +msgstr "-" + +#: FBrowser.form:115 +msgid "+3" +msgstr "-" + +#: FBrowser.form:120 +msgid "+4" +msgstr "-" + +#: FBrowser.form:144 +msgid "Refresh" +msgstr "Neu laden" + +#: FBrowser.form:165 +msgid "Toggle edit mode" +msgstr "Editiermodus umschalten" + +#: FBrowser.form:218 +msgid "Copy" +msgstr "Kopieren" + +#: FBrowser.form:226 +msgid "Cut" +msgstr "Ausschneiden" + +#: FBrowser.form:234 +msgid "Paste" +msgstr "Einfügen" + +#: FBrowser.form:242 +msgid "Undo" +msgstr "Rückgängig" + +#: FBrowser.form:250 +msgid "Redo" +msgstr "Wiederholen" + +#: FBrowser.form:262 FEditable.form:18 +msgid "Bold" +msgstr "Fett" + +#: FBrowser.form:270 +msgid "Italic" +msgstr "Kursiv" + +#: FBrowser.form:278 +msgid "Underline" +msgstr "Unterstrichen" + +#: FBrowser.form:286 +msgid "Strikethrough" +msgstr "Durchgestrichen" + +#: FBrowser.form:298 +msgid "Left aligned" +msgstr "Linksbündig" + +#: FBrowser.form:306 +msgid "Centered" +msgstr "Zentriert" + +#: FBrowser.form:314 +msgid "Right aligned" +msgstr "Rechtsbündig" + +#: FBrowser.form:322 +msgid "Justified" +msgstr "Blocksatz" + +#: FBrowser.form:334 +msgid "Indent" +msgstr "Einrücken" + +#: FBrowser.form:342 +msgid "Unindent" +msgstr "Ausrücken" + +#: FBrowser.form:354 +msgid "Ordered list" +msgstr "Geordnete Liste" + +#: FBrowser.form:362 +msgid "Unordered list" +msgstr "Ungeordnete Liste" + +#: FBrowser.form:372 +msgid "Text color" +msgstr "Textfarbe" + +#: FBrowser.form:378 +msgid "Text background" +msgstr "Texthintergrund" + +#: FBrowser.form:384 +msgid "Font family" +msgstr "Schriftarten" + +#: FBrowser.form:394 +msgid "Font size" +msgstr "Schriftgröße" + +#: FBrowser.form:439 +msgid "Case sensitive" +msgstr "Groß-/Kleinschreibung beachten" + +#: FBrowser.form:446 +msgid "Highlight" +msgstr "Hervorheben" + +#: FDownload.form:18 +msgid "Starting download..." +msgstr "Download wird gestartet..." + +#: FDownloadList.form:9 +msgid "Download manager" +msgstr "Download-Manager" + +#: FOption.form:21 +msgid "Proxy configuration" +msgstr "Proxy-Konfiguration" + +#: FOption.form:28 +msgid "Type" +msgstr "Typ" + +#: FOption.form:33 +msgid "Host" +msgstr "-" + +#: FOption.form:38 +msgid "Port" +msgstr "-" + +#: FOption.form:81 +msgid "HTTP proxy" +msgstr "HTTP-Proxy" + +#: FOption.form:81 +msgid "No proxy" +msgstr "Kein Proxy" + +#: FOption.form:81 +msgid "SOCKS5 proxy" +msgstr "SOCKS5-Proxy" + diff --git a/app/examples/Networking/WebBrowser/.lang/es.mo b/app/examples/Networking/WebBrowser/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..37e3b42d131562f418ffc95078d7926662763405 GIT binary patch literal 314 zcmYL@%}&EG5QK~1lp|*jd*B8xa7#@=wHmiX*h!S1N^mo&2}Z%L9820($+DYx>es{i) olRxfe*9)cZq5TkcknW+WS12BRq1L;!i0amCmF_UaZ^}iJ16IpZ@c;k- literal 0 HcmV?d00001 diff --git a/app/examples/Networking/WebBrowser/.lang/es.po b/app/examples/Networking/WebBrowser/.lang/es.po new file mode 100644 index 00000000..e077297d --- /dev/null +++ b/app/examples/Networking/WebBrowser/.lang/es.po @@ -0,0 +1,62 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Light browser based on WebKit component" +msgstr "" + +#: FAuth.class:59 +msgid "Please authenticate" +msgstr "" + +#: FAuth.class:81 +msgid "User" +msgstr "" + +#: FAuth.class:92 +msgid "Password" +msgstr "" + +#: FAuth.class:114 +msgid "OK" +msgstr "" + +#: FAuth.class:120 +msgid "Cancel" +msgstr "" + +#: FBrowser.class:473 +msgid "Gambas WebKit" +msgstr "" + +#: FBrowser.class:587 +msgid "Case sensitive" +msgstr "" + +#: FBrowser.class:594 +msgid "Highlight" +msgstr "" + +#: FDownload.class:72 +msgid "Starting download..." +msgstr "" + +#: FDownloadList.class:33 +msgid "Download manager" +msgstr "" + +#~ msgid "Browsing the web with Gambas!" +#~ msgstr "¡Buscando en la red con Gambas!" + +#~ msgid "Print selection" +#~ msgstr "Imprimir selección" diff --git a/app/examples/Networking/WebBrowser/.lang/nl.mo b/app/examples/Networking/WebBrowser/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..f69baa9b05966930e9603efdae0a356958d15a2c GIT binary patch literal 2363 zcmZvbPmB~r6vj&h|11g$ii+qKAR-Pu%q$R>5rJiP*j2i6HPQR#>9j1hMIUVk&`zPy>KxmUbyP_W(syUv8G@D=2dmSdhhjD zw|8#2E-=cly|B+75TXhmc?dU*@hw6;2%hvj<$1>QoadD142Te-2I3MIaC;bRg4@A& zz(>IZ#3e4{##^m|Pl8v$ZQw`XW8lZ&6W|SS1pM01e-GjkKjOymfARbc7{_fFcpAhjqUrrb?{~pRFrR^(*AKFlH{j%p8$oakx za=dHazYcPJpL_pX@BaiogZWz^=Y0p{JpTf@?{~rH!L69&xT7GimvQ6x+)I9sbJ_#r zu^U!|ah)8a0OJ_1z_?}}ufq1jxJHi0gX44UTo<1)&X-33V-0Q`2jFmDxi?2QF8Kd^ zb~g_0-D@!Z)<+U!9hm#9XY|;pb)Bnto1JA}&SJXdhg5%t~f7N4aSHCXJeC*oYICslqOE zVV?JmjrflvF_#jT8TMR)*HkCuIb-o0r-*=hNyR-II%TwIQe<#jvXm#HImqN*Uh|D= zZT^JcbKZs4DILqmcc@q_ig{;Mmt0~^FHS^@uAq#t(~aTlXy|uLYOH9PI8I4Yq?}wa z5sB7XMq&Zov8mF7e+I4^wrfG7nDz1&&=$^pnK~|*je<*LbEIC8a}(9t#PpQBG}WA+ zZCo6gYXnVNRlFEXqUwqqE0vE2rIW!}S&kj6oER&V%B9js9c>C)Hq?0aHS&a6Bnl!IFVEJ`q@+s zpSdu5VQM|qaBISq&HN&qK#Adss`(Wy%&^&pc9mXqcG5Uo>^oFf4-CSlok_c5oNqqSGt?a zo^mmAT81v_gE#LBJ|>ntGLR;d2{{&i7dlxN{xp&HG7L}?ivvP|_LmWUF+?7HGmw88 zcDtMsHp1sai`NeU\n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Light browser based on WebKit component" +msgstr "Lichte browser gebaseerd op het Webkit component" + +#: FAuth.form:17 +msgid "Please authenticate" +msgstr "Gelieve te authenticeren" + +#: FAuth.form:39 FOption.form:43 +msgid "User" +msgstr "Gebruiker" + +#: FAuth.form:50 FOption.form:48 +msgid "Password" +msgstr "Wachtwoord" + +#: FAuth.form:72 FOption.form:62 +msgid "OK" +msgstr "-" + +#: FAuth.form:78 FOption.form:68 +msgid "Cancel" +msgstr "Annuleer" + +#: FBrowser.form:74 +msgid "Gambas WebKit" +msgstr "-" + +#: FBrowser.form:90 +msgid "-2" +msgstr "-" + +#: FBrowser.form:95 +msgid "-1" +msgstr "-" + +#: FBrowser.form:100 +msgid "0" +msgstr "-" + +#: FBrowser.form:105 +msgid "+1" +msgstr "-" + +#: FBrowser.form:110 +msgid "+2" +msgstr "-" + +#: FBrowser.form:115 +msgid "+3" +msgstr "-" + +#: FBrowser.form:120 +msgid "+4" +msgstr "-" + +#: FBrowser.form:144 +msgid "Refresh" +msgstr "Ververs" + +#: FBrowser.form:165 +msgid "Toggle edit mode" +msgstr "Wissel bewerken modus" + +#: FBrowser.form:218 +msgid "Copy" +msgstr "Kopiëer" + +#: FBrowser.form:226 +msgid "Cut" +msgstr "Knippen" + +#: FBrowser.form:234 +msgid "Paste" +msgstr "Plakken" + +#: FBrowser.form:242 +msgid "Undo" +msgstr "Ongedaan maken" + +#: FBrowser.form:250 +msgid "Redo" +msgstr "Opnieuw" + +#: FBrowser.form:262 FEditable.form:18 +msgid "Bold" +msgstr "Vet" + +#: FBrowser.form:270 +msgid "Italic" +msgstr "Schuin" + +#: FBrowser.form:278 +msgid "Underline" +msgstr "Onderlijn" + +#: FBrowser.form:286 +msgid "Strikethrough" +msgstr "Doorstreept" + +#: FBrowser.form:298 +msgid "Left aligned" +msgstr "Links uitgelijnd" + +#: FBrowser.form:306 +msgid "Centered" +msgstr "Gecentreerd" + +#: FBrowser.form:314 +msgid "Right aligned" +msgstr "Rechts uitgelijnd" + +#: FBrowser.form:322 +msgid "Justified" +msgstr "Aangepast" + +#: FBrowser.form:334 +msgid "Indent" +msgstr "Inspringen" + +#: FBrowser.form:342 +msgid "Unindent" +msgstr "Ongedaan maken inspringen" + +#: FBrowser.form:354 +msgid "Ordered list" +msgstr "Geordende lijst" + +#: FBrowser.form:362 +msgid "Unordered list" +msgstr "Ongeordende lijst" + +#: FBrowser.form:372 +msgid "Text color" +msgstr "Tekst kleur" + +#: FBrowser.form:378 +msgid "Text background" +msgstr "Tekst achtergrond" + +#: FBrowser.form:384 +msgid "Font family" +msgstr "Lettertype familie" + +#: FBrowser.form:394 +msgid "Font size" +msgstr "Lettertype grootte" + +#: FBrowser.form:439 +msgid "Case sensitive" +msgstr "Case gevoelig" + +#: FBrowser.form:446 +msgid "Highlight" +msgstr "Uitlichten" + +#: FDownload.form:18 +msgid "Starting download..." +msgstr "Start download..." + +#: FDownloadList.form:9 +msgid "Download manager" +msgstr "Donload beheerder" + +#: FOption.form:21 +msgid "Proxy configuration" +msgstr "Proxy configuratie" + +#: FOption.form:28 +msgid "Type" +msgstr "Type" + +#: FOption.form:33 +msgid "Host" +msgstr "Gast" + +#: FOption.form:38 +msgid "Port" +msgstr "Poort" + +#: FOption.form:81 +msgid "No proxy" +msgstr "Geen proxy" + +#: FOption.form:81 +msgid "HTTP proxy" +msgstr "-" + +#: FOption.form:81 +msgid "SOCKS5 proxy" +msgstr "-" + diff --git a/app/examples/Networking/WebBrowser/.project b/app/examples/Networking/WebBrowser/.project new file mode 100644 index 00000000..e1b37c20 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.project @@ -0,0 +1,26 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.7.90 +Title=Light browser based on WebKit component +Startup=FBrowser +Icon=icon.png +Version=1.0.3 +Component=gb.image +Component=gb.gui.qt +Component=gb.form +Component=gb.desktop.x11 +Component=gb.desktop +Component=gb.form.dialog +Component=gb.gui.qt.webkit +Description="Light browser based on the QT4 or QT5 Webkit component" +TabSize=2 +Translate=1 +Language=fr +Maintainer=benoit +Vendor=Example +Address=benoit@desnouettes +License=General Public Licence +Packager=1 +Systems=mandriva +Menus=mandriva:"More Applications/Development/Other" +Groups=mandriva:"Development/Other" +Tags=Network,Web,WebBrowser,Example diff --git a/app/examples/Networking/WebBrowser/.src/FAuth.class b/app/examples/Networking/WebBrowser/.src/FAuth.class new file mode 100644 index 00000000..01e7ed69 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FAuth.class @@ -0,0 +1,38 @@ +' Gambas class file + +Static Public User As String +Static Public Password As String + +'Private $sUrl As String +'Private $sRealm As String + +Public Sub Run(sUrl As String, sRealm As String) As Boolean + + lblRealm.Text = sRealm + Me.Title = sUrl + '$sUrl = sUrl + '$sRealm = sRealm + Return Not Me.ShowModal() + +End + +Public Sub btnOK_Click() + + User = txtUser.Text + Password = txtPassword.Text + Me.Close(True) + +End + +Public Sub btnCancel_Click() + + Me.Close + +End + + +Public Sub Form_Open() + + txtUser.SetFocus + +End diff --git a/app/examples/Networking/WebBrowser/.src/FAuth.form b/app/examples/Networking/WebBrowser/.src/FAuth.form new file mode 100644 index 00000000..737dd05d --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FAuth.form @@ -0,0 +1,60 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,54,26) + Text = ("Please authenticate") + Icon = Picture["icon:/small/unlock"] + Resizable = False + Arrangement = Arrange.Vertical + AutoResize = True + Spacing = True + Margin = True + { HPanel1 HPanel + MoveScaled(1,1,52,18) + AutoResize = True + Spacing = True + { lblRealm TextLabel + MoveScaled(2,1,22,4) + Expand = True + AutoResize = True + Text = ("") + } + { Label1 Label + MoveScaled(2,6,13,3) + Text = ("User") + } + { txtUser TextBox + MoveScaled(19,6,32,4) + Expand = True + Text = ("") + } + { Label2 Label + MoveScaled(2,11,13,3) + Text = ("Password") + } + { txtPassword TextBox + MoveScaled(19,11,32,4) + Expand = True + Text = ("") + Password = True + } + } + { HBox1 HBox + MoveScaled(1,20,52,4) + Spacing = True + { Panel1 Panel + MoveScaled(4,0,4,4) + Expand = True + } + { btnOK Button + MoveScaled(17,0,16,4) + Text = ("OK") + Default = True + } + { btnCancel Button + MoveScaled(34,0,16,4) + Text = ("Cancel") + Cancel = True + } + } +} diff --git a/app/examples/Networking/WebBrowser/.src/FBrowser.class b/app/examples/Networking/WebBrowser/.src/FBrowser.class new file mode 100644 index 00000000..a84520bb --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FBrowser.class @@ -0,0 +1,584 @@ +' Gambas class file + +'Static Private $aZoom As Float[] + +'Private $sStatus As String +'Private $iZoom As Integer +'Private $iHidden As Integer +Private $sLastLink As String + +' Static Public Sub _init() +' +' Dim iInd As Integer +' +' $aZoom = New Float[17] +' +' For iInd = 0 To $aZoom.Max +' $aZoom[iInd] = 2 ^ (- (iInd - $aZoom.Max / 2) / 4) +' Next +' +' End + +Public Sub Form_Open() + + WebSettings.IconDatabase.Path = File.Dir(File.Dir(Temp$())) + WebSettings.Fonts.FixedFont = "Monospace" + WebSettings[WebSettings.PluginsEnabled] = True + WebSettings[WebSettings.JavascriptEnabled] = True + WebSettings[WebSettings.JavaEnabled] = True + WebSettings.Fonts.DefaultFontSize = 12 + WebSettings.Fonts.DefaultFixedFontSize = 12 + + CreateView() + btnZoomNormal_Click + txtURL.SetFocus + tabBrowser_Click + + txtURL.Text = "http://gambas.sourceforge.net" + txtURL_Activate + +End + +Private Sub GetView() As WebView + + Try Return tabBrowser[tabBrowser.Index].Children[0] + +End + +Private Sub IsLastCurrentView() As Boolean + + Dim hView As WebView = GetView() + Return hView = Last + +End + +Public Sub WebView_Link(Url As String) + + If Not IsLastCurrentView() Then Return + $sLastLink = Url + lblStatus.Text = Url + +End + +Public Sub WebView_Status() + + If Not IsLastCurrentView() Then Return + lblStatus.Text = GetView().Status + +End + +Public Sub WebView_Progress() + + GetView().Status = "Loading..." + If Not IsLastCurrentView() Then Return + + lblStatus.Text = GetView().Status + pgbLoad.Value = GetView().Progress + panLoad.Show + +End + +Public Sub WebView_Error() + + Dim hView As WebView = GetView() + Dim sUrl As String + + sUrl = $sLastLink + If Not sUrl Then sUrl = txtURL.Text + + ' If Not (sUrl Begins "www.") Then + ' txtURL.Text = "www." & sUrl + ' btnGo.Value = True + ' Return + ' Endif + + hView.Status = "Unable to load: " & sUrl + hView.HTML = "

    Unable to find the following URL:

    " & sUrl + If Not IsLastCurrentView() Then Return + + lblStatus.Text = GetView().Status + pgbLoad.Hide + +End + +Public Sub WebView_Load() + + 'Dim iInd As Integer + Dim hView As WebView = GetView() + Dim hIcon As Picture + + hView.Status = "" + hIcon = hView.Icon 'WebSettings.IconDatabase[hView.Url] + tabBrowser[hView.Tag].Picture = hIcon + + If Not IsLastCurrentView() Then Return + + lblStatus.Text = "" + txtURL.Text = hView.Url + pgbLoad.Hide + + UpdateIcon + + ' Debug GetView().Frame + ' For iInd = 0 To GetView().Frame.Children.Count - 1 + ' Debug "["; iInd; "] "; GetView().Frame.Children[iInd] + ' Next + +End + +Public Sub btnGo_Click() + + Dim sText As String = txtURL.Text + + If InStr(sText, "://") = 0 Then sText = "http://" & sText + + $sLastLink = sText + GetView().Url = sText + +End + +Public Sub txtURL_Activate() + + btnGo.Value = True + +End + +Public Sub btnBack_Click() + + GetView().Back + +End + +Public Sub btnForward_Click() + + GetView().Forward + +End + +Public Sub btnStop_Click() + + GetView().Stop + +End + +Public Sub btnReload_Click() + + Dim hView As WebView = GetView() + + hView.Reload + +End + +Public Sub btnZoomIn_Click() + + GetView().Zoom = Round(GetView().Zoom * 1.25, -2) + +End + +Public Sub btnZoomOut_Click() + + GetView().Zoom = Round(GetView().Zoom / 1.25, -2) + +End + +Public Sub btnZoomNormal_Click() + + GetView().Zoom = 1 + +End + +Public Sub WebView_Title() + + Dim hView As WebView = Last + tabBrowser[hView.Tag].Text = hView.Title + If Not IsLastCurrentView() Then Return + Me.Title = hView.Title & " - Gambas WebKit" + +End + +Public Sub WebView_Icon() + + Dim hView As WebView = Last + 'hView.Icon.Save("~/icon.png") + tabBrowser[hView.Tag].Picture = hView.Icon + UpdateIcon + +End + +Public Sub btnClear_Click() + + txtURL.Clear + txtURL.SetFocus + +End + +Public Sub WebView_NewWindow((Modal) As Boolean) + + CreateView() + + Last.NewView = GetView() + +End + +Private Sub CreateView() + + Dim iLast As Integer = tabBrowser.Count - 1 + Dim hView As WebView + + Object.Lock(tabBrowser) + Inc tabBrowser.Count + Object.Lock(tabBrowser) + tabBrowser[iLast + 1].Picture = tabBrowser[iLast].Picture + tabBrowser[iLast + 1].Text = tabBrowser[iLast].Text + tabBrowser[iLast].Text = "" + tabBrowser[iLast].Picture = Null + tabBrowser[iLast].Closable = True + Object.Lock(tabBrowser) + tabBrowser.Index = iLast + hView = New WebView(tabBrowser) As "WebView" + hView.Tag = tabBrowser.Index + hView.Editable = btnEdit.Value + hView.Resize(1, 1) + 'hView.Show + 'Print WebSettings.Fonts.FixedFont + Object.Unlock(tabBrowser) + Object.Unlock(tabBrowser) + Object.Unlock(tabBrowser) + tabBrowser_Click + txtURL.SetFocus + +End + + +Public Sub tabBrowser_Click() + + Dim iLast As Integer = tabBrowser.Count - 1 + Dim hView As WebView + + If tabBrowser.Index = iLast Then + CreateView() + Else + hView = GetView() + If hView.Title Then + Me.Title = hView.Title & " - Gambas WebKit" + Else + Me.Title = "Gambas WebKit" + Endif + tabBrowser.Text = hView.Title + UpdateIcon + tabBrowser.Picture = hView.Icon + txtURL.Text = hView.Url + lblStatus.Text = hView.Status + pgbLoad.Value = hView.Progress + If hView.Progress > 0 And If hView.Progress < 1 Then + panLoad.Show + Else + panLoad.Hide + Endif + Endif + +End + +Public Sub btnDownloadList_Click() + + FDownloadList.Show + +End + +Public Sub WebView_Auth() + + Dim hView As WebView = Last + + If Not FAuth.Run(hView.Auth.Url, hView.Auth.Realm) Then + + hView.Auth.User = FAuth.User + hView.Auth.Password = FAuth.Password + 'Debug hView.Auth.Url;; hView.Auth.User;; hView.Auth.Password + + Endif + +End + +Public Sub WebView_Click(Frame As WebFrame) + + Dim sName As String = Frame.Name + If sName Then sName &= ": " + Debug sName; Frame.Url + +End + +Public Sub WebView_NewFrame(Frame As WebFrame) + + Debug Frame.Name + +End + +Public Sub WebView_Download(Download As WebDownload) + + Dialog.Path = System.User.Home &/ File.Name(Download.Url) + If Not Dialog.SaveFile() Then + Download.Path = Dialog.Path + FDownloadList.AddDownload(Download) + FDownloadList.Show + Endif + +End + +Public Sub WebView_MouseDown() + + Dim hView As WebView = Last + Dim hTest As WebHitTest = hView.HitTest(Mouse.X, Mouse.Y) + Dim sMsg As String + + If hTest.Document Then sMsg &= "DOCUMENT " + If hTest.Link Then sMsg &= "LINK " + If hTest.Image Then sMsg &= "IMAGE " + If hTest.Selected Then sMsg &= "SELECTED " + If hTest.Editable Then sMsg &= "EDITABLE " + Debug sMsg; hTest.Url + +End + +Public Sub tabBrowser_Close(Index As Integer) + + Dim hView As WebView + + Try hView = tabBrowser[Index].Children[0] + If Not hView Then Return + + hView.Delete + + Object.Lock(tabBrowser) + tabBrowser[Index].Delete + Object.UnLock(tabBrowser) + If Index = tabBrowser.Index Then + If tabBrowser.Index = (tabBrowser.Count - 1) And If tabBrowser.Index > 0 Then + tabBrowser.Index = tabBrowser.Index - 1 + Else + tabBrowser_Click + Endif + Endif + +End + +Public Sub btnFind_Click() + + Dim hView As WebView = GetView() + + panFind.Visible = btnFind.Value + If btnFind.Value Then + DoFind + txtFind.SetFocus + Else + hView.FindText("") + Endif + +End + +Private Sub DoFind(Optional bBackward As Boolean) + + Dim hView As WebView = GetView() + Dim sText As String + + sText = Trim(txtFind.Text) + + If sText And If hView.FindText(sText, bBackward, chkCaseSensitive.Value, True) Then ', chkHighlight.Value) Then + panFind.Background = &HFFDFDF + Else + panFind.Background = Color.Default + If Not sText Then hView.FindText("") + Endif + +End + + +Public Sub txtFind_Change() + + DoFind + +End + +Public Sub Form_KeyPress() + + If Key.Control And If Key.Code = Key["F"] Then + btnFind.Value = True + Else If Key.Code = Key.Escape Then + btnFind.Value = False + Else If Key.Code = Key.F3 Then + DoFind(Key.Shift) + Endif + +End + +Public Sub chkCaseSensitive_Click() + + DoFind + +End + +Public Sub chkHighlight_Click() + + Dim hView As WebView + + If Not chkHighlight.Value Then + hView = GetView() + hView.FindText("",,,, True) + Else + DoFind + Endif + +End + +Public Sub btnNext_Click() + + DoFind + +End + +Public Sub btnPrevious_Click() + + DoFind(True) + +End + +Public Sub btnClearFind_Click() + + txtFind.Text = "" + +End + +Public Sub txtFind_Activate() + + DoFind + +End + +Private Sub UpdateIcon() + + Dim hView As WebView = GetView() + Dim hIcon As Picture + + hIcon = hView.Icon 'WebSettings.IconDatabase[hView.Url] + + If hIcon Then + Me.Icon = hIcon + Else + Me.Icon = Picture["icon:/16/internet"] + Endif + +End + +Public Sub btnConfig_Click() + + With WebSettings.Proxy + + FOption.Type = .Type + FOption.Host = .Host + FOption.Port = .Port + FOption.User = .User + FOption.Password = .Password + + If FOption.Run() Then Return + + .Type = FOption.Type + .Host = FOption.Host + .Port = FOption.Port + .User = FOption.User + .Password = FOption.Password + + End With + +End + +Public Sub btnEdit_Click() + + Dim hWebView As WebView + + For Each hWebView In tabBrowser.Children + hWebView.Editable = btnEdit.Value + Next + + WebSettings[WebSettings.JavascriptCanAccessClipboard] = btnEdit.Value + panEdit.Visible = btnEdit.Value + +End + +Public Sub btnAction_Click() + + Dim hWebView As WebView = GetView() + + Debug "Action " & Last.Tag & ": "; hWebView.Eval(Subst("document.execCommand('&1', false, false)", Last.Tag)) + +End + +Public Sub btnColor_Click() + + Dim hWebView As WebView = GetView() + + If Dialog.SelectColor() Then Return + hWebView.Eval(Subst("document.execCommand('forecolor', false, '&1')", "#" & Hex$(Dialog.Color, 6))) + +End + +Public Sub mnuFont_Show() + + Dim sFont As String + Dim hMenu As Menu + + If mnuFont.Children.Count > 1 Then Return + + mnuFont.Children.Clear + + For Each sFont In Fonts + + hMenu = New Menu(mnuFont) As "mnuSelectFont" + hMenu.Text = sFont + + Next + +End + + +Public Sub mnuSelectFont_Click() + + Dim hWebView As WebView = GetView() + Dim sFont As String = Last.Text + + hWebView.Eval(Subst("document.execCommand('fontname', false, '&1')", sFont)) + +End + + +Public Sub btnBackground_Click() + + Dim hWebView As WebView = GetView() + + Dialog.Title = "Select a color" + If Dialog.SelectColor() Then Return + hWebView.Eval(Subst("document.execCommand('backcolor', false, '&1')", "#" & Hex$(Dialog.Color, 6))) + +End + +Public Sub mnuSelectSize_Click() + + Dim hWebView As WebView = GetView() + Dim sSize As String = Last.Text + + hWebView.Eval(Subst("document.execCommand('fontsize', false, '&1')", sSize)) + +End + + + +Public Sub btnInsertImage_Click() + + Dim hWebView As WebView = GetView() + + Dialog.Title = "Select an image" + Dialog.Filter = ["*.jpg;*.jpeg;*.png;*.gif;*.xpm;*.bmp", "Image files"] + If Dialog.OpenFile() Then Return + + Print Dialog.Path + hWebView.Eval(Subst("document.execCommand('insertImage', false, '&1')", "file://" & Replace(Dialog.Path, "'", "\\'"))) + +End diff --git a/app/examples/Networking/WebBrowser/.src/FBrowser.form b/app/examples/Networking/WebBrowser/.src/FBrowser.form new file mode 100644 index 00000000..4fd51e3b --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FBrowser.form @@ -0,0 +1,352 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,169,98) + Text = ("Gambas WebKit") + Icon = Picture["icon:/small/gambas"] + Arrangement = Arrange.Vertical + { mnuFont Menu + { mnuSelectFont Menu mnuSelectFont + Name = "mnuSelectFont" + } + } + { mnuSize Menu + { mnuSizeM2 Menu mnuSelectSize + Name = "mnuSizeM2" + Text = ("-2") + } + { mnuSizeM1 Menu mnuSelectSize + Name = "mnuSizeM1" + Text = ("-1") + } + { mnuSize0 Menu mnuSelectSize + Name = "mnuSize0" + Text = ("0") + } + { mnuSizeP1 Menu mnuSelectSize + Name = "mnuSizeP1" + Text = ("+1") + } + { mnuSizeP2 Menu mnuSelectSize + Name = "mnuSizeP2" + Text = ("+2") + } + { mnuSizeP3 Menu mnuSelectSize + Name = "mnuSizeP3" + Text = ("+3") + } + { mnuSizeP4 Menu mnuSelectSize + Name = "mnuSizeP4" + Text = ("+4") + } + } + { HBox2 HBox + MoveScaled(1,1,99,4) + { btnBack ToolButton + MoveScaled(0,0,4,4) + Picture = Picture["icon:/small/left"] + } + { btnForward ToolButton + MoveScaled(4,0,4,4) + Picture = Picture["icon:/small/right"] + } + { Separator9 Separator + MoveScaled(9,0,0,4) + } + { Panel2 HBox + MoveScaled(12,0,44,4) + Background = Color.TextBackground + Expand = True + { txtURL TextBox + MoveScaled(0,0,28,4) + Expand = True + Border = False + } + { btnClear ToolButton + MoveScaled(28,0,4,4) + Picture = Picture["icon:/small/clear"] + } + { btnStop ToolButton + MoveScaled(33,0,4,4) + Picture = Picture["icon:/small/delete"] + } + { btnReload ToolButton + MoveScaled(38,0,4,4) + ToolTip = ("Refresh") + Picture = Picture["icon:/small/refresh"] + } + } + { btnGo ToolButton + MoveScaled(57,0,4,4) + Visible = False + Picture = Picture["icon:/small/apply"] + } + { Separator1 Separator + MoveScaled(62,0,0,4) + } + { btnEdit ToolButton + MoveScaled(64,0,4,4) + ToolTip = ("Toggle edit mode") + Picture = Picture["icon:/small/edit"] + Toggle = True + } + { btnZoomIn ToolButton + MoveScaled(69,0,4,4) + Picture = Picture["icon:/small/zoom-in"] + } + { btnZoomOut ToolButton + MoveScaled(73,0,4,4) + Picture = Picture["icon:/small/zoom-out"] + } + { btnZoomNormal ToolButton + MoveScaled(77,0,4,4) + Picture = Picture["icon:/small/zoom-normal"] + } + { Separator2 Separator + MoveScaled(81,0,0,4) + } + { btnFind ToolButton + MoveScaled(83,0,4,4) + Picture = Picture["icon:/small/find"] + Toggle = True + } + { btnDownloadList ToolButton + MoveScaled(87,0,4,4) + Picture = Picture["icon:/small/archive"] + } + { btnConfig ToolButton + MoveScaled(91,0,4,4) + Picture = Picture["icon:/small/options"] + } + } + { panEdit HBox + MoveScaled(1,6,97,4) + Visible = False + { btnCopy ToolButton btnAction + Name = "btnCopy" + MoveScaled(0,0,4,4) + Tag = "Copy" + ToolTip = ("Copy") + Picture = Picture["icon:/small/copy"] + } + { btnCut ToolButton btnAction + Name = "btnCut" + MoveScaled(4,0,4,4) + Tag = "Cut" + ToolTip = ("Cut") + Picture = Picture["icon:/small/cut"] + } + { btnPaste ToolButton btnAction + Name = "btnPaste" + MoveScaled(8,0,4,4) + Tag = "Paste" + ToolTip = ("Paste") + Picture = Picture["icon:/small/paste"] + } + { btnUndo ToolButton btnAction + Name = "btnUndo" + MoveScaled(12,0,4,4) + Tag = "Undo" + ToolTip = ("Undo") + Picture = Picture["icon:/small/undo"] + } + { btnRedo ToolButton btnAction + Name = "btnRedo" + MoveScaled(16,0,4,4) + Tag = "Redo" + ToolTip = ("Redo") + Picture = Picture["icon:/small/redo"] + } + { Separator7 Separator + MoveScaled(20,0,1,4) + } + { btnBold ToolButton btnAction + Name = "btnBold" + MoveScaled(21,0,4,4) + Tag = "bold" + ToolTip = ("Bold") + Picture = Picture["icon:/small/text-bold"] + } + { btnItalic ToolButton btnAction + Name = "btnItalic" + MoveScaled(25,0,4,4) + Tag = "italic" + ToolTip = ("Italic") + Picture = Picture["icon:/small/text-italic"] + } + { btnUnderline ToolButton btnAction + Name = "btnUnderline" + MoveScaled(29,0,4,4) + Tag = "underline" + ToolTip = ("Underline") + Picture = Picture["icon:/small/text-underline"] + } + { btnStrike ToolButton btnAction + Name = "btnStrike" + MoveScaled(33,0,4,4) + Tag = "strikethrough" + ToolTip = ("Strikethrough") + Picture = Picture["icon:/small/text-strike"] + } + { Separator3 Separator + MoveScaled(37,0,1,4) + } + { btnLeft ToolButton btnAction + Name = "btnLeft" + MoveScaled(39,0,4,4) + Tag = "justifyleft" + ToolTip = ("Left aligned") + Picture = Picture["icon:/small/text-left"] + } + { btnCenter ToolButton btnAction + Name = "btnCenter" + MoveScaled(43,0,4,4) + Tag = "justifycenter" + ToolTip = ("Centered") + Picture = Picture["icon:/small/text-center"] + } + { btnRight ToolButton btnAction + Name = "btnRight" + MoveScaled(47,0,4,4) + Tag = "justifyright" + ToolTip = ("Right aligned") + Picture = Picture["icon:/small/text-right"] + } + { btnFill ToolButton btnAction + Name = "btnFill" + MoveScaled(51,0,4,4) + Tag = "justifyfull" + ToolTip = ("Justified") + Picture = Picture["icon:/small/text-fill"] + } + { Separator5 Separator + MoveScaled(55,0,1,4) + } + { btnIndent ToolButton btnAction + Name = "btnIndent" + MoveScaled(56,0,4,4) + Tag = "indent" + ToolTip = ("Indent") + Picture = Picture["icon:/small/indent"] + } + { btnUnindent ToolButton btnAction + Name = "btnUnindent" + MoveScaled(60,0,4,4) + Tag = "outdent" + ToolTip = ("Unindent") + Picture = Picture["icon:/small/unindent"] + } + { Separator6 Separator + MoveScaled(64,0,1,4) + } + { btnOrdered ToolButton btnAction + Name = "btnOrdered" + MoveScaled(65,0,4,4) + Tag = "insertOrderedList" + ToolTip = ("Ordered list") + Picture = Picture["list-ordered.png"] + } + { btnUnordered ToolButton btnAction + Name = "btnUnordered" + MoveScaled(69,0,4,4) + Tag = "insertUnorderedList" + ToolTip = ("Unordered list") + Picture = Picture["list-unordered.png"] + } + { Separator4 Separator + MoveScaled(73,0,1,4) + } + { btnColor ToolButton + MoveScaled(74,0,4,4) + ToolTip = ("Text color") + Picture = Picture["icon:/small/color"] + } + { btnBackground ToolButton + MoveScaled(78,0,4,4) + ToolTip = ("Text background") + Picture = Picture["icon:/small/fill"] + } + { btnFont MenuButton + MoveScaled(82,0,6,4) + ToolTip = ("Font family") + Picture = Picture["icon:/small/font"] + Border = False + Menu = "mnuFont" + MenuOnly = True + } + { btnFont2 MenuButton + MoveScaled(88,0,6,4) + Font = Font["Bold,-1"] + ToolTip = ("Font size") + Text = ("+1") + Border = False + Menu = "mnuSize" + MenuOnly = True + } + } + { Separator8 Separator + MoveScaled(8,12,17,0) + } + { tabBrowser TabPanel + MoveScaled(1,14,76,40) + Expand = True + Arrangement = Arrange.Fill + Border = False + Index = 0 + Text = ("") + Picture = Picture["icon:/small/add"] + Index = 0 + } + { panFind HBox + MoveScaled(1,54,101,4) + Visible = False + { btnClearFind ToolButton + MoveScaled(4,0,4,4) + Picture = Picture["icon:/small/clear"] + } + { txtFind TextBox + MoveScaled(8,0,32,4) + Expand = True + } + { btnNext ToolButton + MoveScaled(41,0,4,4) + Picture = Picture["icon:/small/down"] + } + { btnPrevious ToolButton + MoveScaled(45,0,4,4) + Picture = Picture["icon:/small/up"] + } + { chkCaseSensitive CheckBox + MoveScaled(54,0,17,4) + AutoResize = True + Text = ("Case sensitive") + } + { chkHighlight CheckBox + MoveScaled(70,0,20,4) + Visible = False + AutoResize = True + Text = ("Highlight") + } + { Panel1 Panel + MoveScaled(91,1,1,2) + } + } + { HBox1 HBox + MoveScaled(1,61,66,2) + { lblStatus Label + MoveScaled(5,0,21,2) + Font = Font["-2"] + Expand = True + } + { panLoad Panel + MoveScaled(46,0,19,2) + Visible = False + Arrangement = Arrange.Fill + Padding = 4 + { pgbLoad ProgressBar + MoveScaled(1,0,17,2) + Label = False + } + } + } +} diff --git a/app/examples/Networking/WebBrowser/.src/FDownload.class b/app/examples/Networking/WebBrowser/.src/FDownload.class new file mode 100644 index 00000000..c4953e11 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FDownload.class @@ -0,0 +1,50 @@ +' Gambas class file + +Private $hDownload As WebDownload + +Public Sub Init(hDownload As WebDownload) + + $hDownload = hDownload + Redraw + +End + + +Public Sub Redraw() + + Dim sTitle As String + Dim sStatus As String + + If Not $hDownload Then Return + + With $hDownload + + sTitle = .Url + If .Status = .Cancelled Then + sStatus = "Cancelled" + Else If .Status = .Finished Then + sStatus = "Finished" + Else If .Status = .Error Then + sStatus = "Error: " & .ErrorText + Else + sStatus = "Downloading..." + Endif + lblTitle.Text = sTitle + lblStatus.Text = sStatus + + pgbDownload.Value = .Progress + + End With + +End + +Public Sub btnAbort_Click() + + If $hDownload.Status = WebDownload.Downloading Then + $hDownload.Cancel + Else + $hDownload.Delete + Me.Delete + Endif + +End diff --git a/app/examples/Networking/WebBrowser/.src/FDownload.form b/app/examples/Networking/WebBrowser/.src/FDownload.form new file mode 100644 index 00000000..969b8e99 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FDownload.form @@ -0,0 +1,34 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,69,13) + Arrangement = Arrange.Fill + Margin = True + { Panel1 Panel + MoveScaled(1,1,67,11) + Background = Color.Background + Arrangement = Arrange.Vertical + Margin = True + Border = Border.Plain + { lblTitle Label + MoveScaled(1,1,48,3) + Expand = True + Text = ("Starting download...") + } + { lblStatus Label + MoveScaled(1,4,11,2) + Font = Font["Italic,-2"] + } + { HBox2 HBox + MoveScaled(1,6,65,4) + { pgbDownload ProgressBar + MoveScaled(0,0,56,4) + Expand = True + } + { btnAbort ToolButton + MoveScaled(61,0,4,4) + Picture = Picture["icon:/small/close"] + } + } + } +} diff --git a/app/examples/Networking/WebBrowser/.src/FDownloadList.class b/app/examples/Networking/WebBrowser/.src/FDownloadList.class new file mode 100644 index 00000000..eed15dfe --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FDownloadList.class @@ -0,0 +1,20 @@ +' Gambas class file + +Public Sub AddDownload(hDownload As WebDownload) + + Dim hForm As FDownload + + hForm = New FDownload(svwDownload) + hForm.Init(hDownload) + +End + +Public Sub timRefresh_Timer() + + Dim hForm As FDownload + + For Each hForm In svwDownload.Children + hForm.Redraw + Next + +End diff --git a/app/examples/Networking/WebBrowser/.src/FDownloadList.form b/app/examples/Networking/WebBrowser/.src/FDownloadList.form new file mode 100644 index 00000000..baaaf84c --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FDownloadList.form @@ -0,0 +1,20 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,68,39) + Text = ("Download manager") + Icon = Picture["icon:/small/archive"] + Persistent = True + Arrangement = Arrange.Fill + { svwDownload ScrollView + MoveScaled(1,1,66,36) + Background = Color.SelectedForeground + Arrangement = Arrange.Vertical + Border = False + ScrollBar = Scroll.Vertical + { timRefresh #Timer + #MoveScaled(52,15) + Enabled = True + } + } +} diff --git a/app/examples/Networking/WebBrowser/.src/FEditable.class b/app/examples/Networking/WebBrowser/.src/FEditable.class new file mode 100644 index 00000000..98308606 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FEditable.class @@ -0,0 +1,15 @@ +' Gambas class file + + +Public Sub Form_Open() + + WebView1.Url = "http://gambasdoc.org/" + +End + +Public Sub Button1_Click() + + Print WebView1.Frame.EvalJavaScript("1+2") + Print WebView1.Frame.EvalJavaScript("document.execCommand('bold', false, false)") + +End diff --git a/app/examples/Networking/WebBrowser/.src/FEditable.form b/app/examples/Networking/WebBrowser/.src/FEditable.form new file mode 100644 index 00000000..640874b7 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FEditable.form @@ -0,0 +1,19 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,68,64) + Arrangement = Arrange.Vertical + Margin = True + { HBox1 HBox + MoveScaled(2,1,63,4) + { Button1 Button + MoveScaled(0,0,16,4) + Text = ("Bold") + } + } + { WebView1 WebView + MoveScaled(4,7,54,40) + Expand = True + Editable = True + } +} diff --git a/app/examples/Networking/WebBrowser/.src/FOption.class b/app/examples/Networking/WebBrowser/.src/FOption.class new file mode 100644 index 00000000..d6a6cc34 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FOption.class @@ -0,0 +1,67 @@ +' Gambas class file + +Static Public Type As Integer +Static Public Host As String +Static Public Port As Integer +Static Public User As String +Static Public Password As String + +Public Sub Run() As Boolean + + Return Not Me.ShowModal() + +End + +Public Sub btnOK_Click() + + Select Case cmbType.Index + Case 0 + Type = WebSettings.NoProxy + Case 1 + Type = WebSettings.HttpProxy + Case 2 + Type = WebSettings.Socks5Proxy + End Select + + Host = Trim(txtHost.Text) + Port = txtPort.Value + User = Trim(txtUser.Text) + Password = txtPassword.Text + + Me.Close(True) + +End + +Public Sub btnCancel_Click() + + Me.Close + +End + + +Public Sub cmbType_Click() + + txtHost.Enabled = cmbType.Index > 0 + txtPassword.Enabled = cmbType.Index > 0 + txtPort.Enabled = cmbType.Index > 0 + txtUser.Enabled = cmbType.Index > 0 + +End + +Public Sub Form_Open() + + Select Type + Case WebSettings.NoProxy + cmbType.Index = 0 + Case WebSettings.HttpProxy + cmbType.Index = 1 + Case WebSettings.Socks5Proxy + cmbType.Index = 2 + End Select + + txtHost.Text = Host + txtPort.Value = Port + txtUser.Text = User + txtPassword.Text = Password + +End diff --git a/app/examples/Networking/WebBrowser/.src/FOption.form b/app/examples/Networking/WebBrowser/.src/FOption.form new file mode 100644 index 00000000..1faee652 --- /dev/null +++ b/app/examples/Networking/WebBrowser/.src/FOption.form @@ -0,0 +1,69 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,61,35) + Text = ("Proxy configuration") + Resizable = False + Spacing = True + Margin = True + { Label1 Label + MoveScaled(1,1,15,4) + Text = ("Type") + } + { Label2 Label + MoveScaled(1,6,15,4) + Text = ("Host") + } + { Label3 Label + MoveScaled(1,11,15,4) + Text = ("Port") + } + { Label4 Label + MoveScaled(1,16,15,4) + Text = ("User") + } + { Label5 Label + MoveScaled(1,21,15,4) + Text = ("Password") + } + { HBox1 HBox + MoveScaled(1,30,59,4) + Spacing = True + { Panel1 Panel + MoveScaled(4,0,4,4) + Expand = True + } + { btnOK Button + MoveScaled(11,0,16,4) + Text = ("OK") + Default = True + } + { btnCancel Button + MoveScaled(28,0,16,4) + Text = ("Cancel") + Cancel = True + } + } + { txtHost TextBox + MoveScaled(17,6,43,4) + Enabled = False + } + { cmbType ComboBox + MoveScaled(17,1,43,4) + ReadOnly = True + List = [("No proxy"), ("HTTP proxy"), ("SOCKS5 proxy")] + } + { txtPort SpinBox + MoveScaled(17,11,12,4) + Enabled = False + } + { txtUser TextBox + MoveScaled(17,16,43,4) + Enabled = False + } + { txtPassword TextBox + MoveScaled(17,21,43,4) + Enabled = False + Password = True + } +} diff --git a/app/examples/Networking/WebBrowser/icon.png b/app/examples/Networking/WebBrowser/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..268485b35ff1ec22b2fde11239acf698bd053acb GIT binary patch literal 2932 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 SXLkZ!#^CAd=d#Wzp$Pz4)fQ0z literal 0 HcmV?d00001 diff --git a/app/examples/Networking/WebBrowser/list-unordered.png b/app/examples/Networking/WebBrowser/list-unordered.png new file mode 100644 index 0000000000000000000000000000000000000000..5e6ec74c95807328dc700cfdf4dcdb5f177af8fb GIT binary patch literal 237 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 literal 0 HcmV?d00001 diff --git a/app/examples/OpenGL/3DWebCam/.project b/app/examples/OpenGL/3DWebCam/.project new file mode 100644 index 00000000..935c58f4 --- /dev/null +++ b/app/examples/OpenGL/3DWebCam/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.0 +Title=3DWebCam +Startup=Mmain +Icon=webcam.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.image.io +Component=gb.sdl +Component=gb.opengl +Component=gb.opengl.glu +Component=gb.v4l +TabSize=2 +Maintainer=Benoît Minisini +Vendor=ASAP +Address=bminisini@asap-info.com +License=General Public Licence diff --git a/app/examples/OpenGL/3DWebCam/.src/Mmain.module b/app/examples/OpenGL/3DWebCam/.src/Mmain.module new file mode 100644 index 00000000..44884dfc --- /dev/null +++ b/app/examples/OpenGL/3DWebCam/.src/Mmain.module @@ -0,0 +1,256 @@ +' Gambas module file + +Private hWebCam As VideoDevice +Private Const ScrWidth As Integer = 640 +Private Const ScrHeight As Integer = 480 + +' Needed for frame count +Private Frames As Integer +Private CTime As Float + +' Rotations +Private xrot As Float +Private yrot As Float +Private zrot As Float + +' texture +Private textures As New Integer[] +Private Screen As New Window(True) As "Screen" + +Private logo As Image +Private tmpLogo As Image +Private hTimer As New Timer As "Timer1" +Private count As Integer +Private UpdateLogo As Boolean + +Public Sub Main() + + Try hWebCam = New VideoDevice("/dev/video0") + If Error Then + Print ("Unable to open video device") + Return + + End If + hWebCam.Hue = 10 + hWebCam.Color = 10 + hWebcam.Resize(320, 240) + + logo = hWebCam.Image + logo.Resize(256, 256) + + screen.Width = ScrWidth + screen.Height = ScrHeight + Screen.show() + Screen.Resizable = True + InitGL() + textures = Gl.GenTextures(1) + LoadTextures() + Screen_resize() + CTime = Timer() + hTimer.Delay = 200 + hTimer.Enabled = True + +End + +Public Sub LoadTextures() + + Gl.BindTexture(gl.TEXTURE_2D, textures[0]) + Gl.TexImage2D(logo) + Gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) + Gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) + +End + +Public Sub InitGL() + + ' Enable smooth shading + Gl.ShadeModel(gl.SMOOTH) + ' Set the background black + Gl.ClearColor(0.0, 0.0, 0.0, 0.5) + ' Depth buffer setup + Gl.ClearDepth(1.0) + ' Enables Depth Testing + Gl.Enable(gl.DEPTH_TEST) + ' Enable texturing + Gl.Enable(gl.TEXTURE_2D) + ' The Type OF Depth Test TO DO + Gl.DepthFunc(gl.LESS) + ' Really Nice Perspective Calculations + Gl.Hint(gl.PERSPECTIVE_CORRECTION_HINT, gl.NICEST) + +End + +Public Sub Screen_close() + + ' Delete textures if needed + If (textures.count > 0) Then Gl.DeleteTextures(textures) + +End + +Public Sub Screen_resize() + + ' Width/Height Ratio + Dim ratio As Float + Dim Height As Integer + + Height = Screen.Height + ' Protect against a divide by zero + If Height = 0 Then Height = 1 + + ratio = Screen.Width / Height + + ' Setup our viewport + Gl.Viewport(0, 0, Screen.Width, Screen.Height) + ' change to the projection matrix AND set our viewing volume. + Gl.MatrixMode(gl.PROJECTION) + Gl.LoadIdentity() + + ' Set our perspective + Glu.Perspective(45.0, ratio, 0.1, 100.0) + + ' Make sure we're changing the model view and not the projection + Gl.MatrixMode(gl.MODELVIEW) + GL.LoadIdentity() + +End + +Public Sub Screen_Draw() + + Dim calc As Float + Inc count + If UpdateLogo Then + 'count = 0 + 'logo = tmpLogo + logo.Resize(256, 256) + LoadTextures() + UpdateLogo = False + Endif + + Gl.Clear(gl.COLOR_BUFFER_BIT Or gl.DEPTH_BUFFER_BIT) + + Gl.LoadIdentity() + Gl.Translatef(0.0, 0.0, -5.0) + + Gl.Rotatef(xrot, 1.0, 0.0, 0.0) ' Rotate On The X Axis + Gl.Rotatef(yrot, 0.0, 1.0, 0.0) ' Rotate On The Y Axis + Gl.Rotatef(zrot, 0.0, 0.0, 1.0) ' Rotate On The Z Axis + + ' Select our texture + Gl.BindTexture(gl.TEXTURE_2D, textures[0]) + + Gl.Begin(gl.QUADS) + ' front face + ' Bottom Left OF The Texture AND Quad + Gl.TexCoord2f(0.0, 1.0) + Gl.Vertex3f(-1.0, -1.0, 1.0) + ' Bottom Right OF The Texture AND Quad + Gl.TexCoord2f(1.0, 1.0) + Gl.Vertex3f(1.0, -1.0, 1.0) + ' Top Right OF The Texture AND Quad + Gl.TexCoord2f(1.0, 0.0) + Gl.Vertex3f(1.0, 1.0, 1.0) + ' Top Left OF The Texture AND Quad + Gl.TexCoord2f(0.0, 0.0) + Gl.Vertex3f(-1.0, 1.0, 1.0) + + ' Back face + ' Bottom Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 0.0) + Gl.Vertex3f(-1.0, -1.0, -1.0) + ' Top Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 1.0) + Gl.Vertex3f(-1.0, 1.0, -1.0) + ' Top Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 1.0) + Gl.Vertex3f(1.0, 1.0, -1.0) + ' Bottom Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 0.0) + Gl.Vertex3f(1.0, -1.0, -1.0) + + ' Top face + ' Top Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 1.0) + Gl.Vertex3f(-1.0, 1.0, -1.0) + ' Bottom Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 0.0) + Gl.Vertex3f(-1.0, 1.0, 1.0) + ' Bottom Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 0.0) + Gl.Vertex3f(1.0, 1.0, 1.0) + ' Top Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 1.0) + Gl.Vertex3f(1.0, 1.0, -1.0) + + ' Bottom ace + ' Top Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 1.0) + Gl.Vertex3f(-1.0, -1.0, -1.0) + ' Top Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 1.0) + Gl.Vertex3f(1.0, -1.0, -1.0) + ' Bottom Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 0.0) + Gl.Vertex3f(1.0, -1.0, 1.0) + ' Bottom Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 0.0) + Gl.Vertex3f(-1.0, -1.0, 1.0) + + ' Right face + ' Bottom Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 0.0) + Gl.Vertex3f(1.0, -1.0, -1.0) + ' Top Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 1.0) + Gl.Vertex3f(1.0, 1.0, -1.0) + ' Top Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 1.0) + Gl.Vertex3f(1.0, 1.0, 1.0) + ' Bottom Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 0.0) + Gl.Vertex3f(1.0, -1.0, 1.0) + + ' Left face + ' Bottom Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 0.0) + Gl.Vertex3f(-1.0, -1.0, -1.0) + ' Bottom Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 0.0) + Gl.Vertex3f(-1.0, -1.0, 1.0) + ' Top Right OF The Texture AND Quad + Gl.TexCoord2f(0.0, 1.0) + Gl.Vertex3f(-1.0, 1.0, 1.0) + ' Top Left OF The Texture AND Quad + Gl.TexCoord2f(1.0, 1.0) + Gl.Vertex3f(-1.0, 1.0, -1.0) + + Gl.End() + + Inc (Frames) + If (Timer() > CTime + 5) Then + calc = Timer() - CTime + Print CStr(Frames) & " frames in " & Format$(calc, "#.0") & " seconds = " & Format$((Frames / calc), "######.000") & " FPS" + Frames = 0 + CTime = Timer() + Endif + + xrot += 0.3 ' X Axis Rotation + yrot += 0.2 ' Y Axis Rotation + zrot += 0.4 ' Z Axis Rotation + Sleep 0.05 + +End + +Public Sub Screen_keyPress() + + If key.Code = key.F1 Then Screen.Fullscreen = Not Screen.Fullscreen + If key.Code = key.Esc Then Screen.Close() + +End + +Public Sub Timer1_Timer() + + logo = hWebCam.Image + + UpdateLogo = True + +End diff --git a/app/examples/OpenGL/3DWebCam/webcam.png b/app/examples/OpenGL/3DWebCam/webcam.png new file mode 100644 index 0000000000000000000000000000000000000000..e90b27a8e5be2e8b39ac125fbbf4b67e20ff510b GIT binary patch literal 6442 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 literal 0 HcmV?d00001 diff --git a/app/examples/OpenGL/GambasGears/.directory b/app/examples/OpenGL/GambasGears/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/OpenGL/GambasGears/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/OpenGL/GambasGears/.icon.png b/app/examples/OpenGL/GambasGears/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f4ee5b433917b29da800bcd02cbc5488c809c0c0 GIT binary patch literal 3450 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 literal 0 HcmV?d00001 diff --git a/app/examples/OpenGL/GambasGears/.project b/app/examples/OpenGL/GambasGears/.project new file mode 100644 index 00000000..ce12a78d --- /dev/null +++ b/app/examples/OpenGL/GambasGears/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +Title=GambasGears +Startup=Module1 +Icon=gears.png +Version=3.11.90 +VersionFile=1 +Component=gb.image +Component=gb.sdl2 +Component=gb.opengl +Description="glxgears ported to Gambas!" +Authors="Fabien Bodard\nLaurent Carlier" +TabSize=2 +Language=fr +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/app/examples/OpenGL/GambasGears/.src/Module1.module b/app/examples/OpenGL/GambasGears/.src/Module1.module new file mode 100644 index 00000000..de45376d --- /dev/null +++ b/app/examples/OpenGL/GambasGears/.src/Module1.module @@ -0,0 +1,256 @@ +' Gambas module file + +' GambasGears +' Released under GPL v2 or later +' aka glxgears for gambas :) +' based on gears.c by Brian Paul / Mark J. Kilgard +' +' Code : Bodard Fabien & Carlier Laurent +' + +Private screen As New Window(True) As "Screen" +Private gearlists As Integer + +Private angle As Float +Private Frames As Integer +Private fTime As Float + +Public Sub Main() + + With screen + '.Resizable = True + .Resize(480, 480) + .Show() + End With + + fTime = Timer + +End + +Public Sub Screen_Open() + + Dim red As Float[] + Dim green As Float[] + Dim blue As Float[] + Dim pos As Float[] + + red = [0.8, 0.1, 0.0, 0.2] + green = [0.0, 0.8, 0.2, 0.8] + blue = [0.1, 0.1, 0.8, 1.0] + + ' we enable lights, depth test, cull face + Gl.Lightfv(Gl.LIGHT0, Gl.POSITION, [5.0, 5.0, 10.0, 0.0]) + Gl.Enable(Gl.CULL_FACE) + Gl.Enable(Gl.LIGHTING) + Gl.Enable(Gl.LIGHT0) + Gl.Enable(Gl.DEPTH_TEST) + Gl.ClearDepth(1.0) + + ' We need 3 displaylists for the 3 gears + gearlists = Gl.GenLists(3) + + Gl.NewList(gearlists, Gl.COMPILE) + Gl.Materialfv(Gl.FRONT, Gl.AMBIENT_AND_DIFFUSE, red) + Gear(1.0, 4.0, 1.0, 20, 0.7) + Gl.EndList() + Gl.NewList(gearlists + 1, Gl.COMPILE) + Gl.Materialfv(Gl.FRONT, Gl.AMBIENT_AND_DIFFUSE, green) + Gear(0.5, 2.0, 2.0, 10, 0.7) + Gl.EndList() + Gl.NewList(gearlists + 2, Gl.COMPILE) + Gl.Materialfv(Gl.FRONT, Gl.AMBIENT_AND_DIFFUSE, blue) + Gear(1.3, 2.0, 0.5, 10, 0.7) + Gl.EndList() + Gl.Enable(Gl.NORMALIZE) + +End + +Public Sub Screen_Resize() + + Gl.Viewport(0, 0, Screen.Width, Screen.Height) + Gl.MatrixMode(Gl.PROJECTION) + Gl.LoadIdentity() + Gl.Frustum(-1.0, 1.0, - (Screen.Height / Screen.Width), (Screen.Height / Screen.Width), 5.0, 60.0) + Gl.MatrixMode(Gl.MODELVIEW) + Gl.LoadIdentity() + Gl.Translatef(0.0, 0.0, -40.0) + +End + +Public Sub Screen_Draw() + + Dim calc As Float + + angle += 0.05 + + Gl.Clear(Gl.COLOR_BUFFER_BIT Or Gl.DEPTH_BUFFER_BIT) + + Gl.PushMatrix() + + Gl.Rotatef(20, 1.0, 0.0, 0.0) + Gl.Rotatef(30, 0.0, 1.0, 0.0) + Gl.Rotatef(0, 0.0, 0.0, 1.0) + + Gl.PushMatrix() + Gl.Translatef(-3.0, -2.0, 0.0) + Gl.Rotatef(angle, 0.0, 0.0, 1.0) + Gl.CallList(gearlists) + Gl.PopMatrix() + + Gl.PushMatrix() + Gl.Translatef(3.1, -2.0, 0.0) + Gl.Rotatef((-2.0 * angle) - 9.0, 0.0, 0.0, 1.0) + Gl.CallList(gearlists + 1) + Gl.PopMatrix() + + Gl.PushMatrix() + Gl.Translatef(-3.1, 4.2, 0.0) + Gl.Rotatef((-2.0 * angle) - 25.0, 0.0, 0.0, 1.0) + Gl.CallList(gearlists + 2) + Gl.PopMatrix() + + Gl.PopMatrix() + + If (Timer > (fTime + 1)) Then + Inc fTime + Print Screen.Framerate; " FPS" + Endif + +End + +Public Sub Screen_Close() + + Gl.DeleteLists(gearLists, 3) + +End + +Public Sub Screen_KeyPress() + + If (key.code = key.F1) Then Screen.Fullscreen = Not Screen.Fullscreen + If (key.Code = key.Esc) Then Screen.Close() + +End + +Public Sub Screen_MouseMove() + + If Mouse.Button = 0 Then Return + + Gl.Rotatef(Mouse.StartY - Mouse.Y, 0, 0, 1) + Gl.Rotatef(Mouse.StartX - Mouse.X, 1, 0, 0) + +End + +Public Sub Gear(inner_radius As Float, outer_radius As Float, width As Float, teeth As Integer, tooth_depth As Float) + + Dim i As Integer + Dim r0 As Float + Dim r1 As Float + Dim r2 As Float + Dim angle As Float + Dim da As Float + Dim u As Float + Dim v As Float + Dim fLen As Float + + r0 = inner_radius + r1 = outer_radius - tooth_depth / 2.0 + r2 = outer_radius + tooth_depth / 2.0 + + da = 2.0 * Pi / teeth / 4.0 + + Gl.ShadeModel(Gl.FLAT) + Gl.Normal3f(0.0, 0.0, 1.0) + + ' Draw front face + Gl.Begin(Gl.QUAD_STRIP) + For i = 0 To teeth + angle = i * 2.0 * Pi / teeth + Gl.Vertexf(r0 * Cos(angle), r0 * Sin(angle), width * 0.5) + Gl.Vertexf(r1 * Cos(angle), r1 * Sin(angle), width * 0.5) + If i < teeth Then + Gl.Vertexf(r0 * Cos(angle), r0 * Sin(angle), width * 0.5) + Gl.Vertexf(r1 * Cos(angle + 3 * da), r1 * Sin(angle + 3 * da), width * 0.5) + Endif + Next + Gl.End() + + ' Draw front sides of teeth + Gl.Begin(Gl.QUADS) + da = 2.0 * Pi / teeth / 4.0 + For i = 0 To teeth - 1 + angle = i * 2.0 * Pi / teeth + Gl.Vertexf(r1 * Cos(angle), r1 * Sin(angle), width * 0.5) + Gl.Vertexf(r2 * Cos(angle + da), r2 * Sin(angle + da), width * 0.5) + Gl.Vertexf(r2 * Cos(angle + 2 * da), r2 * Sin(angle + 2 * da), width * 0.5) + Gl.Vertexf(r1 * Cos(angle + 3 * da), r1 * Sin(angle + 3 * da), width * 0.5) + Next + Gl.End() + + Gl.Normal3f(0.0, 0.0, -1.0) + + ' Draw back face + Gl.Begin(Gl.QUAD_STRIP) + For i = 0 To teeth + angle = i * 2.0 * Pi / teeth + Gl.Vertexf(r1 * Cos(angle), r1 * Sin(angle), - width * 0.5) + Gl.Vertexf(r0 * Cos(angle), r0 * Sin(angle), - width * 0.5) + If i < teeth Then + Gl.Vertexf(r1 * Cos(angle + 3 * da), r1 * Sin(angle + 3 * da), - width * 0.5) + Gl.Vertexf(r0 * Cos(angle), r0 * Sin(angle), - width * 0.5) + Endif + Next + Gl.End() + + ' Draw back sides of teeth + Gl.Begin(Gl.QUADS) + da = 2.0 * Pi / teeth / 4.0 + For i = 0 To teeth - 1 + angle = i * 2.0 * Pi / teeth + Gl.Vertexf(r1 * Cos(angle + 3 * da), r1 * Sin(angle + 3 * da), - width * 0.5) + Gl.Vertexf(r2 * Cos(angle + 2 * da), r2 * Sin(angle + 2 * da), - width * 0.5) + Gl.Vertexf(r2 * Cos(angle + da), r2 * Sin(angle + da), - width * 0.5) + Gl.Vertexf(r1 * Cos(angle), r1 * Sin(angle), - width * 0.5) + Next + Gl.End() + + ' Draw outward faces of teeth + Gl.Begin(Gl.QUAD_STRIP) + For i = 0 To teeth - 1 + angle = i * 2.0 * Pi / teeth + Gl.Vertexf(r1 * Cos(angle), r1 * Sin(angle), width * 0.5) + Gl.Vertexf(r1 * Cos(angle), r1 * Sin(angle), - width * 0.5) + u = r2 * Cos(angle + da) - r1 * Cos(angle) + v = r2 * Sin(angle + da) - r1 * Sin(angle) + fLen = Sqr(u * u + v * v) + u /= fLen + v /= fLen + Gl.Normal3f(v, - u, 0.0) + Gl.Vertexf(r2 * Cos(angle + da), r2 * Sin(angle + da), width * 0.5) + Gl.Vertexf(r2 * Cos(angle + da), r2 * Sin(angle + da), - width * 0.5) + Gl.Normal3f(Cos(angle), Sin(angle), 0.0) + Gl.Vertexf(r2 * Cos(angle + 2 * da), r2 * Sin(angle + 2 * da), width * 0.5) + Gl.Vertexf(r2 * Cos(angle + 2 * da), r2 * Sin(angle + 2 * da), - width * 0.5) + u = r1 * Cos(angle + 3 * da) - r2 * Cos(angle + 2 * da) + v = r1 * Sin(angle + 3 * da) - r2 * Sin(angle + 2 * da) + Gl.Normal3f(v, - u, 0.0) + Gl.Vertexf(r1 * Cos(angle + 3 * da), r1 * Sin(angle + 3 * da), width * 0.5) + Gl.Vertexf(r1 * Cos(angle + 3 * da), r1 * Sin(angle + 3 * da), - width * 0.5) + Gl.Normal3f(Cos(angle), Sin(angle), 0.0) + Next + Gl.Vertexf(r1 * Cos(0), r1 * Sin(0), width * 0.5) + Gl.Vertexf(r1 * Cos(0), r1 * Sin(0), - width * 0.5) + Gl.End() + + Gl.ShadeModel(Gl.SMOOTH) + + ' Draw inside radius cylinder + Gl.Begin(Gl.QUAD_STRIP) + For i = 0 To teeth + angle = i * 2.0 * Pi / teeth + Gl.Normal3f(- Cos(angle), - Sin(angle), 0.0) + Gl.Vertexf(r0 * Cos(angle), r0 * Sin(angle), - width * 0.5) + Gl.Vertexf(r0 * Cos(angle), r0 * Sin(angle), width * 0.5) + Next + Gl.End() + +End diff --git a/app/examples/OpenGL/GambasGears/gears.png b/app/examples/OpenGL/GambasGears/gears.png new file mode 100644 index 0000000000000000000000000000000000000000..6eed1fa86ad3f75a71131ccd7b9349be424d80c4 GIT binary patch literal 3488 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 literal 0 HcmV?d00001 diff --git a/app/examples/OpenGL/Md2Model/.directory b/app/examples/OpenGL/Md2Model/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/OpenGL/Md2Model/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/OpenGL/Md2Model/.icon.png b/app/examples/OpenGL/Md2Model/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..22e52cbf7cf15fa7739846c602b7c482a5da47d8 GIT binary patch literal 3521 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 D Then + D += 100 + X = - D + Z -= 100 + Inc I + If I = 10 Then Break + Endif + + Loop + + Print $aObject.Count; " objects" + + $iEndWidth = D + $iEndZ = Z + + sldFrame.MinValue = 0 + sldFrame.MaxValue = $aModel[0].Count + + $iDisk = Gl.GenLists(1) + $hQuadric = Glu.NewQuadric() + + Gl.NewList($iDisk, Gl.COMPILE) + Gl.Rotatef(90, 1, 0, 0) + Glu.Disk($hQuadric, 0, 20, 30, 1) + Gl.EndList + +End + + +Public Sub glaScreen_Draw() + + Dim fTime As Float = Timer + Dim I, N As Integer + + Gl.Clear(Gl.COLOR_BUFFER_BIT Or Gl.DEPTH_BUFFER_BIT) ' Clear The Screen And The Depth Buffer + + Gl.PushMatrix + + Gl.Disable(Gl.TEXTURE_2D) + + Glu.Color(&HD96800&) + Gl.Begin(Gl.QUADS) + Gl.Vertex3f(-100, -34.2, -100) + Gl.Vertex3f(100, -34.2, -100) + Gl.Vertex3f($iEndWidth, -34.2, $iEndZ) + Gl.Vertex3f(- $iEndWidth, -34.2, $iEndZ) + Gl.End + + Glu.Color(Color.Lighter(&HD96800&)) + For I = 0 To $aObject.Max + + Gl.PushMatrix() + Gl.Translatef($aObject[I].X, -34, $aObject[I].Z) + Gl.CallList($iDisk) + Gl.PopMatrix() + + Next + + Gl.Enable(Gl.TEXTURE_2D) + Gl.Color3f(1, 1, 1) + For I = 0 To $aObject.Max + N += $aObject[I].Draw() + Next + + ' You can use this code to get FPS printed in terminal + + Inc $iFrames + If Timer >= ($fTime + 1) Then + $fFrameRate = $iFrames / (Timer - $fTime) + $iFrames = 0 + Inc $fTime + Endif + + lblInfo.Text = Format($aObject[0].Frame, "0.00") & " / " & $aObject[0].Model.Count & " ( " & CInt($fFramerate) & " FPS )" + + Gl.PopMatrix + + Inc $nDraw + Print "\r"; Format($nDraw, "#####0"); ": "; N; " vertices in "; Format(Timer - fTime, "0.000000"); " seconds"; + +End + +Public Sub Form_KeyPress() + + If Key.code = Key.F1 Then + Me.FullScreen = Not Me.FullScreen + sldFrame.Visible = Not Me.FullScreen + Else If Key.Code = Key.Esc Then + Me.Close + Else If Key.code = Key.Space Then + timAnim.Enabled = Not timAnim.Enabled + Else If LCase(Key.Text) = "w" Then + Gl.PolygonMode(Gl.FRONT_AND_BACK, Gl.LINE) + Else If LCase(Key.Text) = "f" Then + Gl.PolygonMode(Gl.FRONT_AND_BACK, Gl.FILL) + Endif + +End + +Public Sub timAnim_Timer() + + ' At every timer call we increase interpolation. It makes Frame number increase every 10 calls. + ' You can control frame flow any way you want. The smaller incrementation, the smoother movement. + + Dim I As Integer + + For I = 0 To $aObject.Max + With $aObject[I] + .Frame += 0.1 + If .Frame >= .Count Then .Frame = 0 + End With + Next + + Object.Lock(sldFrame) + sldFrame.Value = CInt($aObject[0].Frame) + Object.Unlock(sldFrame) + + glaScreen.Refresh + +End + +' Just the subroutine to load textures for our models +Private Sub LoadTexture(sPath As String) As Integer + + Dim iTex As Integer + Dim hImage As Image + + iTex = Gl.GenTextures(1)[0] + hImage = Image.Load(sPath) + Gl.BindTexture(Gl.TEXTURE_2D, iTex) + Gl.TexImage2D(hImage) + Glu.Build2DMipmaps(hImage) + Gl.Texparameteri(Gl.TEXTURE_2D, Gl.TEXTURE_MIN_FILTER, Gl.LINEAR_MIPMAP_LINEAR) + Gl.Texparameteri(Gl.TEXTURE_2D, Gl.TEXTURE_MAG_FILTER, Gl.LINEAR) + Return iTex + +End + + +Public Sub sldFrame_Change() + + $aObject[0].Frame = sldFrame.Value + +End + +' Public Sub glaScreen_MouseMove() +' +' $fRotX = $fStartX + 180 * (Mouse.X - Mouse.StartX) / glaScreen.Width +' $fRotY = $fStartY + 180 * (Mouse.Y - Mouse.StartY) / glaScreen.Height +' glaScreen.Refresh +' +' End +' +' Public Sub glaScreen_MouseDown() +' +' $fStartX = $fRotX +' $fStartY = $fRotY +' +' End + +' Public Sub Form_Activate() +' +' While $nDraw < 10 +' glaScreen.Refresh +' Wait 1 +' Wend +' Me.Close +' +' End diff --git a/app/examples/OpenGL/Md2Model/.src/FMain.form b/app/examples/OpenGL/Md2Model/.src/FMain.form new file mode 100644 index 00000000..6ca8c351 --- /dev/null +++ b/app/examples/OpenGL/Md2Model/.src/FMain.form @@ -0,0 +1,31 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,160,72) + Arrangement = Arrange.Vertical + { Panel1 HBox + MoveScaled(1,2,116,4) + Spacing = True + { sldFrame Slider + MoveScaled(0,0,24,4) + Expand = True + } + { Separator1 Separator + MoveScaled(54,0,0,4) + } + { lblInfo Label + MoveScaled(64,0,18,4) + Padding = 4 + AutoResize = True + } + } + { timAnim #Timer + #MoveScaled(52,13) + Delay = 20 + } + { glaScreen GLArea + MoveScaled(4,14,16,16) + Background = &H7FBFFF& + Expand = True + } +} diff --git a/app/examples/OpenGL/Md2Model/Weapon.md2 b/app/examples/OpenGL/Md2Model/Weapon.md2 new file mode 100644 index 0000000000000000000000000000000000000000..560facea70735ad5e0232ba4fb9b71401fe6efd0 GIT binary patch literal 71708 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 z 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 z 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 literal 0 HcmV?d00001 diff --git a/app/examples/OpenGL/Md2Model/Weapon.png b/app/examples/OpenGL/Md2Model/Weapon.png new file mode 100644 index 0000000000000000000000000000000000000000..e2eab7addb7e11aa441c9551dd5707a94cca1d63 GIT binary patch literal 17376 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 X&M~jdJ_R2(00000NkvXXu0mjfA%%l8 literal 0 HcmV?d00001 diff --git a/app/examples/OpenGL/Md2Model/bauul.jpg b/app/examples/OpenGL/Md2Model/bauul.jpg new file mode 100644 index 0000000000000000000000000000000000000000..403d146f618d76dffa9a6e376cd59dc3711181ea GIT binary patch literal 25801 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 )d@x@=n>pwd* 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

    " Then + If Not Trim(aLine[iLine - 1]) And If iLine < aLine.Max And If GetIndent(aLine[iLine + 1]) >= hMarkupList.iIndent Then + bJustList = False + Endif + aResult.Remove(aResult.Max) + Else + GoSub CLOSE_PARA + aResult.Add(Space$(iCurrentIndent) & "

    }2e9Ov(6AVbpkamGdlF7kjJedK(sov;DWN-NMqmoe8#CrJSE zJmoQuYy!+{n-OQYVvHJr%N%0{_~83`W#0uUBi^~>Y!|%%ytW7MI~Z5Z90amV1_4l7 z=STpr!UI^(o-zQ+EJsZyqMFGKsqLeH0O0pt~=W6?V^io++c#ZrFUMSnhp+|HPGUK`mOXQ z{Ahp%2egR+)`VdHl?G=x2F@sLOa*SAr`;JYj#RI~KzpVyw0#kf934=frmS0k*Ijj} bScg9WsNVZDMo|oq00000NkvXXu0mjfq5LxQ literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/hflip.png b/app/examples/Image/PhotoTouch/hflip.png new file mode 100644 index 0000000000000000000000000000000000000000..72f46a74625595b9b4575f93bd99043804e858db GIT binary patch literal 953 zcmV;q14jIbP)7NO6vzK>gepNAgh(Z&iACwoqC_MlB`gThg$N5>kyz=14M`=eDpoHfzC(3mYf&sp z5G)$RM^TArd_;92ZK&$|*}T`ecji8wnfE3VdFO53%$$4vznQsn&$&mcSj8$j z4%5pJAIP$zs8=IdJ}^WthnQVX`FdXB+X&m7UfUTlyk4-hT=I1cVB09mLSD-l4R{Su zR}T5PJSS`yLw=VWUeB3Zs(cMkaoO9DXTQ_$2{on2S8>~ncF+Gi9`YS8T)J-~+@dPE ze770tI4#Ce$a}(CIAJ4nCqlrZ(~NSOOk{uzXKaK{y!Zeo%?Mq1C^CTX=&}(y;>sU0 zN8ey7iUlC1a>GVwk0ak^j=akZ6dORy;I56(7FE8LoIk3UIr)Hb0mK}7O@JJ&QRFxC z%OCTOMHAs{fa2C-j9A1w^U^OiO(ws_T+csJZv;yMAnN&KUL0Y~-{e=D@m)B zU2s4yP-BE?{7hdQXZPxCK}?cfXl>@3fA_O^WCHGU+(YTV{q}=;$gG0=bUWuANkv|W zqc%bzPjcBj+mV{Q5FIwcB}r^B&zvKZDnN$wHbS$w=^tHZN~-cgOyRl-&@Da}{`XLw zy1Wq8^!NauJWZ4eW6DMN$V#`I#{2_M+%ldA(^9|xK%I9pFbS{Xr79n&^PoBO8D>)j z$c8_|@Ius{ntZ!^Aqp?W2UC%6v*q(ku`?Ovv`bx%`CF@15oY-V4LHv3)D;=Ext)w8 ziTS+p@qV+zaSzGU@eY2QF0YvHU}Q1xeVxK+e0Cpg)oiny5*2&z4W#q>?16xr594LY}Xk zemzu|ATRy%Mx_+YlK>1;8}h2Px4;z3d5IAIZL?a+x9cC%Pv4~N`ab%3T;_It#VS^@ b|Jwcl^XW?!Ud0Rp00000NkvXXu0mjf@VU3= literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/icon.png b/app/examples/Image/PhotoTouch/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3107012937f062a452029c64c7469d837a07f718 GIT binary patch literal 4904 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 literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/magic.png b/app/examples/Image/PhotoTouch/magic.png new file mode 100644 index 0000000000000000000000000000000000000000..a97ee5fa15543f2d885e2c359697c9314f4cf9c0 GIT binary patch literal 1093 zcmV-L1iJf)P)`VYx+lh!jRHokw%f{THdSuG>IkPTWH|JHe7DJ=jSu!6DA@XFwiHSiuG&E-^|J+2wXi zyUZ5?{*}r(btJrnbR^5YGBrmb*i75AdT~ zyf4pP3kQ`Z5vpBfKA@jA1{~{_TVdsyniumukN6yQoJ#r)ylrZ{%6Ior#wsIk6fnbg z_W*_I3kVYd2pFYX9cEz*xWE$1DP##C^RW?x_h?b8!5 zz=%i9RhILG#;m7&bl*Y3%5nal=>DaH13Ji_fH{UOrF;st&v+!-Io&4>TGFF z+5C4&bb11IraZh}Mny#AuoP%~fxKrte_WGw*$X%+wn2Zrh%}0&aS{1yl0(p5nURfv zUu3q&zII|v^Q2QIM~Bv3ssPVfaGe{YKFoJiPBD`n=TnL@nTBI*;0MRD zIKoHdDYY&tXd^oHT&9L+G7+$yPZX=3I@Yky&~7bll&0Z+JPadDW1vbKVo%>0_aa*- z^!;-(DCr}X{unr~{I8X9*hy0Zo3wUuZloE#H;e+@b$=$un5`snKTj#`x~bESGrkFM z9Uh`wWAZu5V~X9%Mr}y?EZ}wOQn&~ovp})mXGc&1R?}$Y7xI~QZfxLNe+1|!w}pH_ zp2tH=IZD1`348nz@Vi16af}Bhd90QeWnTZ`kAM<|-aqByx{-RPym}e}lq?rJr6@0@ z@#qfnY2qd?v&EkbxLA^=_U$AX;b5AlY4dr}DBv4@okmP%#QEAI3?C3_T7A3`<<`BeaXCXxVOQHA!TG#~}%QXIrV8BX{YjYNM5@Qo$Kr2xMQ9(>Z zEK*27Nk9utVG%H(CLtj~{JYmeHoLbobGv)r+=!5Go1NL8_xZk=eP?%p)%bsdB_80q zehdJ*b zffJ!#AHiMxB1CAB;Y{KvzD7-j{=5i606BsfK#Jf1K!)H1z<_Wgt1mbL&=7#D*$@J~ ziwcLZ2OH69ykb*5gy+~0byT>h26E;>#p8UzZEtd= zOdklA!?Qn#<+CHijYzGaWSlkiV;o14?r1=8=7gru40=GHj!gwH7<7dvyaE`iFv#6r z0W^d)xJ4B;Jplamg@vfug)Ywkz&MV@jcvbo0N@>#qeg6@_XJSD6BMK7$7namf3y5i z!f1Th=P>HE0ttEGvijx`K$E|thU3j918DO1ai;c+rUDq`d(sq8mOdy9g*0!WY1wAN42yKK2O%5gGl0@_nd+MQu9@~b}<`QgA zt;K_l9wfOGdZ;u|XcMeC2t^W%F=9e4(IzR1uF-!Fb@q)rv(F~(yuJ9}%X{B{=6%1J z_h$C(eux5m-c-LPwWR;_P*?Sls{Y@(J5OGt%Zk#HK9(0hz&_npehsN4UVy#U^pbQj zUVsbQP8({rkC711tt9tJ`JHh_nMaiII^DcvjvD5qb(9@k|8aAn16wA$M$^VxRYhD= z@TH$)LCGAeD>x1B*hcZ5e+H=a8xt(&Sic23`)z~l1YnQfSPWTz2Rr3)0Te{L0HqFc zl<+Ps?56@)VUBBz@zf>2H|BMumg!;Km>muPMp@s|UuV>G z>Ky>g+ai??0A!66{kx#(0MGImR?pHBiobG*L$xkYS!b&9A@s0-ixyr%#Q$@=I`iqwyN_mYBOgGQ|Jyw8T^Rmgie~(cf z?S9j`e~%I1RljN7zsCshqTjUc-(v*G%D#Vh08kw50_+0p0(fSxl?s``NL9#9N3^1j zwvM~%nb%!w=)hp4EyqjJm~A(b%yHZDj1r2>p%K5Zv969w>WEt0v%;Xkbfi z%2Kkik2{!9CqMZ(8w~LWkAM`doXS}s5BZ)cG9;+u1kE{DmA7bRAGbXrkH4s=IJ!Zj z#|CE$w8ei`Ez`|r)$Ev7^Ohc0b;VMcoYJht9m^K9!&=heVA-xkQ+X1!2=!2NRl#|(TF;;_3sySRzK^(<^q1K>-s|bvgg{%_iY-~ kEv-nBR&+~)YBLYTzs=P?KoiDjVE_OC07*qoM6N<$f;|SVB>(^b literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/oil.png b/app/examples/Image/PhotoTouch/oil.png new file mode 100644 index 0000000000000000000000000000000000000000..a19068ff368e5d0a59d7a4190a2f65d886f5f2bf GIT binary patch literal 1189 zcmV;W1X}xvP)2*I+eKJeTcMFE8u`(PjS-EgO_iXgrbLZ5^g<(QP3nb^YRINyVghQ2VvHiB zDiWFSgs6b9QHYc4u0@b2Ddm=6&98 z&N<)AIU){&DxRgAetu#N7r0aohhNKCRP=DqbiyNSBR5i1P7glDJLHeQSUDZp@5CHq zyg9}9125>jUY_Ah&_p+PP96RcophMX5f<}x?mTQKF%|e)hIIVJ2!aq{9fLs=UEJXs zALn7x*%MQ<|O3Fc=UbJf1rbE%pW)b-e~7K66yl;Wu!aDm#3PBTDU?N$`SD#(MpM zfd}jiH0fHmg)krpHT0=Lo9wQCms08oi5DsvR>Rh1v?3)rxK|dalh;EJYmk^a>9CoB z&s8G*#6!=(4{F#Sc%|bPF{+fdg~s2c-!{+LC<9?sJhi!DUHJ?`F6L++Ml{q!Yo$YusU)RU7Ge$j z)V~bdy)AL0uJ!Gtc#n7e`1h>vA|!RSt&)$wjj-Jv$!Bt>$5Py`p_W9T8ispS_lvjfI5pFT5mKEXP2K~gep{bHx za%=F%r@im2^B3W5J*m}sv%l7Vn`Gh*2XGOVa#UBnDhD26qrU0P#xAS1ZyfHC3~DEt z<-=b@8+qaj|I$+$KEjTC+C8kDcpL#vvypV}8UZj`@ynulgrFxJ#f%`sAGFa~c`nk&qTm( zw%sm$hF-&}+2$Q^YCvw5i5*PJ*r{AK2aCrSHgJEoF(MxsJIzK#+|Y!P85{km$qq5jRM)&JEq z&1G&3T(2}w@g&h4bbeU)xh-2(vC8d4l&WSwb zli5-)buw2fBq6gUE+Rvcmb9d$SN6+3@9-4l{r|;(7n5AF;0)RI00000NkvXXu0mjf Dh?qrE literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/ok.png b/app/examples/Image/PhotoTouch/ok.png new file mode 100644 index 0000000000000000000000000000000000000000..0bf4198acefa8cab107b35aadbcaf7c38f806df2 GIT binary patch literal 655 zcmV;A0&x9_P)L%DwOnbX_9-B_kQKR_tYUL>zngDzjNM%OCk+xSchDtq5+I)PNQn-ot8`c z#_1^Ylx}@w)E0k)_k?SciNnNODn#I|6e#mL$ov*o*kIf={W3>+VmAdBUdT$Jcld3* zL!pR8#NnL^VWclD-Vk_L~PKxx4h_n1~Ou=OMiwAF>>{Boq{=GnhNq&aM zw~_-NG5j9<{hlW0!U`GiGlSqCI2ySXlJU(H_z~8EryfQ$JI}9tc=zrl9tBS=#WV~n zcu6lwtfs>YaiOE&DZM1oXrFqKfEVH_4U)fhF0-93y(ICaeXh>M6bje)kbu9E1+UQK z$qrK~6yjf%w7c&JoaDPdg-ZI}2NdRM1r?_4(svXVd?*Zz_oI*t?^2k_gsSJ zb$c{3C{zczdj1r=;av(9TYUdrNK&Zz*!|#BsIlueeKv){@Gga_Ene!s3)MKhpWP=- zp=Ouft+0EbaNP>;R=B}mJG|6;7aD1v^NXE=Y5G_NJG@iD7B5NM3_R>^GwZ8whCi)1 prdgeq^jU99K7QXD*0BDM`Um~?H5zz-WO4ui002ovPDHLkV1kE*I>!J2 literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/photo.png b/app/examples/Image/PhotoTouch/photo.png new file mode 100644 index 0000000000000000000000000000000000000000..eb7c3c23ab9113faacaa337822aa7b678741ee21 GIT binary patch literal 1068 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 Eg7$gZqW}N^ literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/redo.png b/app/examples/Image/PhotoTouch/redo.png new file mode 100644 index 0000000000000000000000000000000000000000..c8a64d046861d4a66ddc24edea617f9d822bb950 GIT binary patch literal 1042 zcmV+t1nv8YP)qi6Xdw3o42tf?L3Y;6YJAgD5DjC>}&ah;K-Xn(kKx{AaV$d6;ob z!$`z&7uT%T$l1Ay?`c~T7ep<;kE*qEO_cJJsk5!o0bHiuwnro1m(t|_P!Tpp0WeeO zm9v6PD#E7B11u71AUK*IRfH`O1Aygxts?A*7yul?1{Gmf!~o!6zETnPL<|7-;|mpG zulHC+-vVZ#0z*-RV)Q~M0HqkOAD}O;qQBg+-`bE~tZ~j`4d2n#;JEtmnd)$W!9RFh z!l(2wBwb}rgz7*DEMc9OxE=s3b8;W@jlgkiG?CW>fW>^Mx*c{yh->A?;}UY!zBar> zGn&zC6u?`|#uW?{9V?KVB2-iJFBPW|_$6@V5NaIhOjC zwy)lNtRfs`%o|{v@NeVVz~cX|5*Y z6`|I3EilG<+pB?HLjnMM^1h0&-VI=u@L3m{JP79Th79nN1$uxZw#P3PgbD!m;2jxZ zp{=RT#zebu9!*B?40HR08K@Vx6EpzqhH|@oHDviDu|K*Dj1_mcLl@6S9m>S*O^pjz zi8YcP@(RdTSHMs9v{mJgQK8*vK)74oVqC#6xnr~SK{o~6?8cYO%AZ2DxRC+!G69ez zUyEfH^#F+q;A?2|`MBVeuT+mR7P zjQVc>@+v^${gV@j%5U=jnLGvG?MANGQz1p~%)mDhi_WCHCoP9y(K`Ve?ZzPOf_v*a zh@by`3gaA6vh>zeHB+?P^Wn~Fhdi5d?~?*xfEZ)3wy-*;0eHjb;m8BD#97cWEkF)W z(!+ww1CT;&a@m;0xWRci9iBYEN-sQJ0OG6L`0QTZnG3se{P5}^Oi)OQ#KEi4_uzQ@#xXV9cm+Uw+s9w@5 zj}~e%U+(#g2l(yq>WEQd9|L?tcf%t%qa5U^=VS&}6PkDe;i3=ucPt7S0AP%}*=i(T z&td87=^;k&IDe%S|HA#uOHak4&|@y9q5>tFpPu6uu3Ie`iat8~3u81;%~hE31poj5 M07*qoM6N<$f^dBExBvhE literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/resize.png b/app/examples/Image/PhotoTouch/resize.png new file mode 100644 index 0000000000000000000000000000000000000000..b822742094700595336daa8127a20b5d464e0e32 GIT binary patch literal 852 zcmV-a1FQUrP)dkwWQ{xR|oI^hkkMzC>n?i97Z|PB`-U8ITtdc`Jv&2BK7J8uM_g z;XYVW&H_1(Z-GJP5lxB~r)lDp>xdIU?zlM_kX%9YFINV=!*C0E>9pr0N4tCiVcrFJ zVY?X_WW&hN$9d`=L~M>a?XO1U0lv7+V>Xu4-t_5ONI~Fb3J(n6$+T^Ubu40<8H#;l z54fl#8zBHij8Q`kV?GF7bSRMz0Z7OLR5Q+z9kD3wisS_HfzhZh9F^QnWIFpeBd*%LLIMCyz1pc z-hx#w_#hYrFoh6W2~8l_;o(&-v6M*wxJjv2fb8&am%^fZ0D!2{qPuT^ZpB4+p8&ne zi>|-kk2r~X;1yA4QI*9(s+lq6?swXKDL% z`pPQ;NN*iP10qHM+UX9A831UbCnTyJpq0MRxYpNDXh|cl8DNQlw0*P!lriP5e`Epx z&ZPXaHVlwJC=Q7mmX-9Zu03nyh-dvAbQqDZNNg$rENS-F~bxzXkm0u?UNN+}V z4Mju}-)mAJ?=m7kEBV`*kP>-gT>gD>>vlT}{jy<9{EtMPySF?LiS7F`j%(b$|IQ*~ eJhYOlpML?dnOYZ)U)^p100006o!#P=62mOY;oOkPRWE(YKUKl&k>(|{{nh&6#eMO5U3L(?f=w86jZgg+pIByhZMkiUJ;E)*=h{;6QDZ@ccIob zlW4(jR&&U@aHUY5=A%}<_ZtDd<*-wj9q7Bey$85l=>EaF_hClhIoHUdGxF{Q3Sb;< zu6+PUBNq_73f7>?HTM{Wu0azLxF2y;qXKcrq;BM|?R9-KVM3S9v!M61OC_?@~hBfl^q2tjk; zA=em3ztZtlY%N}K86z0Q4U8a-G5myX9CxHt6eM82Yn(+^;Wh_vcyA2j0D5r<15QeF zuzeoEK6l){RSy5B@(?Gvk>#PVl7K!p>RSqY9d8Q!InFg91nq=%u5nS})P{S+#b)R9 z9TAXJIIR)pU-;StA0U8L$UnP%X?#ekoO?aS~kyi;@Zc z>YqUpwn^wsDn~g#0-S;#%=35jAAx{fw-Y6J&A(cx+oHm&1O%q`4nC|S;AwS~5CTfk zqN;RDemNh(U=c@u0ewCaR*BPG6M~EQ*mQ{xhB|&s{5s`{xLrrU9^rqAbs_N^nUe*q z)e$g@qoPF*%R}Iw@qS3zr-26mZjxJd(Xqs@m0wTDbDX0o00n!>4lDd}rsW`4xKJAp z(DA<9wHxD?@LxH=Fc%r%0f1%vQM4cDY9su7CT096n+@>*z*_z(VvKN+Hok#>%lMmI zZGi^>Hu8dq^AGE_@H03d<$srMhn(BzZ< SSoQ$`0000UB7qPD zA2b*nHA;<9QGBW<+6OA&M^cPnYeK5UTCvdDE%x*815D@6vO9ZccDkNE;B->2UgD zBhhkQPsk z0dTK6y{wURh$jmOfY4&?(Ub`=se1|vfY1h|OoX#qT3`UQT7xD+d%*$F?HcpCnbF-g zx`_zOC98eMQ(nViXTe7Rp$>Bz?keyI0BmQ*Ydrct0Q^XY*Vv?Fu6VB&q};v05&9S= z#lH+Q#_tSpl5-&tnt9J_G&-MKrs-Hvsq0ajnpA4F3Q9EWCGN83)^oWl?uhoQ#sc9} zFXgyBzyiA+!H8ON0&Fu&Q<*(NwT{{#NU!94AsM#DauEV3!CD%*mLv-)BgrDlsK}JI zg?#~?7L%gbZESYlvL1ts<5H?S)vEV(&iq+pIo3k27rG-ac>`p=T6EUy_dp=PVK4Bb zm;eA&>lM5US{wkd*8}JZ?laVz(CKBX>nC|QAacNSt6~HI4$|+s>jJ_CJ-0Ge0B4#{ zT))8%phSz}90BNbohq)*8uYi>D32H5gy*iz8Z>S;<`H1w0eX?^)?^KuFdOq@1#rf_ z@vXK42poaHPdxzDvXMF#X8J`}X3g?V!7jPmi3tYzjIW&;>`V#m3@S=Hvc8Cx2Bm$g zYFmKa!BufjR)C7#;}7O)0%6I+4-vv2q&f)i`mW&yzZTq`6p0BmrbAc-4YXNWH| z2K*2J@L`6#E2zJ$?*P+_-lT2M>tp5ORcBW7Q#Jtid+{@RJWu&0<|ajY+X4V|$lP6S z%tJn@PE&qV*VzFqGmnDCwJxH3LI?eiVT+YKfNs%{mzUC-NB}SStB@8&knws#lmrS30e0VwML`g?{oh%u=0JbVqdS9LNJ`iB6i&SW~8= zJFc~JBoCk(oi?@ZQ$p?jwg1>0%0qgo9@YWBe7K?;5ux6Ha%5Qeq8kyxSE^?%TG5S& z;49UV@+rCz5jL5Bl&D+F3MvnvReD{g^sA2PnGi>_0Q?77-u`9h26vuzlPFp2JGLg(AqDYnaim!$EXxbnqNQ597($uPnNQ7X+!b%8>CM-rQm<5rj zZwp1V8bLx-L}F4+szJ~M@!6^3SWIW$ee?D1n|b4P;eQwJp7Y+h=l|}x@7{av^9Tu) zvWOtdnMtnr&OO>_;~@juHWDkP5|(e`6nbT@pjTp-Vrh2Fc?_SWTAzSNPC4veMC6C8 z&?RuR0PaOZe(D6G2tV6$9(37J!dmNxyws@T0L|RwPmUgaG%!O|1leo-6@-jFi%Ny= z;LTW?)FAwkG1g-sVmyB)Z}&)r{8A}wvK|AIjoxT?An-kOsp53&F`$NH@LI-a+m4U2 zJE;`_JhliJR%Nycl-Rb2p(sZ?$MBzk>jb>{H<1-fXi_BMIc;?QO9Jzlm!f;i9S-w2 zCb$uT02^tg+C2gQqqxorHwi#Fmsp=+3<&RSxA(<9g%S>NE-it6{CT`uglL94MNBon z@dPFn;44^@{1oMe>?`>vWFL{VtJMxC}%BWOq%bU$eV(2k^_cVlZC)NW+iDN z2?DEFWZU&VegW`NRgTC=U^26S7A__<$CSHE*>0P_{y_p}WF$}yKnKSYnkL&Wb>RAH z_@BTKAfW7`i(Mh`*br8_K)_22bK^pgDMSYAeE`g+QF*i=kZLuR>sHd!j+BJ|dHNJo z6T=2=8%Gd6#6)w(JagZ+pd0Q2mvxm zP0>X}dStVdIgqNSFXj+<1fYsL$(x`Lyh-4j^J4Ug9ZctnelO&Y)cLiO)y!uOV;K&>!Zhh+K?wjnQ0AF~g(WZq z_5(wnK(A5t7ho!~CUC>3j;7890?&A32>Yo>)H?v^OO$n4f{FEtrTt2>8-~4xxu_xcjtMie@1OAqGREe_GAiGc^v0Q)uuuoK?8msGFDam z<{DSiCY#w%#4^^IkG1Rc2tXyRW(p1=pgR$DQOj$`-5=wfo$yu4D@T6k#c@hJqV*LR zWun}4%zd*I$9`JbEvu6E&^h3ngrzjrp6(!2;bklJj7!Q|^zf7xu5&k2lHdaV0-sT8 U4^P$zJOBUy07*qoM6N<$f`Zr282|tP literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/save.png b/app/examples/Image/PhotoTouch/save.png new file mode 100644 index 0000000000000000000000000000000000000000..122f75e862ef0915c988c450c71df14ce6dc0574 GIT binary patch literal 674 zcmV;T0$u%yP)P1=ZJd;rl1mA9+*T$-wE98e%|egHzIvY13<3(xeyB=hp3kN zTVMe!fCb0^_$!>~Z?9f}b>Yn5cq#xNgp;Asvj_bEpv$)j$v^WI`%lW{d;;>Xgx>$tb(!l%<=@Z=2LK)3 z=Z<#aeR_P%i&CC&LjFYMFoAO#r^r9CieJ4!C++EZ>c5Eo0mQyS|JW=jdH?_b07*qo IM6N<$f*6%9ga7~l literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/scissors.png b/app/examples/Image/PhotoTouch/scissors.png new file mode 100644 index 0000000000000000000000000000000000000000..de748cfbef2a7dc2d990364465bb49ea257747eb GIT binary patch literal 1549 zcmV+o2J-odP)rM5f-n?^CBlmGz?Mgk;CurUF_A{d{E`avO9q6mB#P-95Y2ofVA zrUVl|L=vzGzJ;g_5JdsQL&c&kv_NQ~t(3NR`O%rP_nA3o=FF{$dp>6FnYH#>_x7B9 z9wqvpBNYcgIW45o)Iq!H+f>>uDYEDus-(fxKp&-k?}t0&U?Vv8A>WBrfc=8C5jmO4 zXJZHSs7GNes}S`8)`!T>%)r|S$GfqtEe6)+OynPjDbkn~$(jXoJeE{`B-)sLQKmYI zfmYT)9J8=mQIgC+Epvatjrdt6TV-XfX3$}j;HYG6Ng`i?UZ2;3kswS&52xxxxsg?l ze|$(c?gU{J&NHhAQugg}q{%*gtj0h>J563MD{l^=q3pR?afM99Y(nQU>0 zCA3Z>Pw_-aXvbhT1B;n6S0y4WkOt0SsKyzJW`6mgU{y-iA~yr7{4^s+q#}GGlW7$} zj{cn*X8syh1g`_7&7=Y@fo9M`OP~5fL`~hC49C_?-x!hQ9wE3$lQ_2=Jzxfw|0B zZb>@|mo@oTlv+AIIhQUzb3h z2!n8vx$jsc3eki{6k6VD%i~iHB`9EFCeLudQj5p}ZSI%Xaw(t2%!KkpMA*a`dN9u- zWWE;483TAL19>9k%BimhPddv#rOO{cZbk-(Fa~EiPcK#?6)T>?OS*gu$}*HEBEkf@ zK>_wyyPV%G14a4@Cig?0hzPTJb_9S!xY;USh97nLj*L2Az(6V1%6bBHqT2Y=SL;rJ zYgmWU{)x}gcKRYd2pOn_CHCRI%*2ba3)Xlwy6OjGm%~ylAU_FB_Ixd>D=xzl@2Lna zQ5G68A&LBCo{UWKj3+u6m(i#;FV7+}+EC#tUm{080Gz~L>{XM)5oCLkeG6`X@t&i$ z<2qi&Uyk!3c1zZ7j8}i>3hV(eLw()~0C-P*o?*nxzThG+7G%As4%EIB9#+r(!s)!& zK~{zE%k{c=f4qT%jIOcF^L=-A>hB*4jQFL}Kz*G2M8?*s6j`_n(?avWR)q|1VuScP zX<<^df%QHvjaxkW2=9dGFh(c=LY5eI&0dQQKUOX+)<+vqr%_+2zI{M(4~oC`6s>ph zs?d!8f}uY}$q(Tk_RhtJy*;IG;ZT;xapX!p1V_%-l0Bm4!sfd+o` z|9W*Apf2z>Bm8Ny2O9m+vdtSn#p2Za5{8AUU}Ag&MFk&HwyVlsiE99WYp7If-zy;l z&iLrVN-Sg~FT3u*25i8%nB|uveqOYJTt4&d!ery@Rv{P3XA|O^Fo2>1D;a%GO-Ou= z)fbC3N%0gH$ir`pZ^I*ET20|meWP>=gOcKddWN%GOsB6&oaUUMkLeF0Dx>*ys|5Da z?9?~O@eYAyaEY2s@h%ot%Qb`{(s-dicoz#*XtkzmMOAhbQ_M2l8sVJ(62H00000NkvXXu0mjfq_)>v literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/sharpen.png b/app/examples/Image/PhotoTouch/sharpen.png new file mode 100644 index 0000000000000000000000000000000000000000..7d6f67e7375afc3fe4c80c47865be5d91ef85c2b GIT binary patch literal 1301 zcmV+w1?u{VP)Xv59?(t!>?fl(4bGN(kyAONserL`( zvvKZwnF8cVa++=PH4x!of#Uhtt8zvH zlv@N}V}mJi9m7ZRX*f0nGZ5vN%y5~j0*u{t{6{a!1wjlPR^{sg zR&R@LVOK5{a+r*k;Q~T)mor#_GYTY(`>_%$QHvx_;v_CPBCIL z@QjEbky7tC$dg&N3T?vtS57yTSxPW2UJME6x1z&o1`FYUaFtODIBql0EM6p}M7fgz zpcc1#Z~$nw8Hjorc*Id&#VQ7(HUm9>@u9lCb4GF9WW!(#ybp{ioc^M%J*A zPw_zo+|1t%@+YM60)l%~NC!REgrBO&v|))U9t-&vzv3Sx$qdi2pD(dY!K_k${&enc z>TErF@1M4=+j7TkT*1Z0OdT()w0*ghgZ#6O^4`aCU4AsbWEOED9#C;N`Mf7>tCM(` zJN4b13|sO5QU0JV|AG(o2Ry12rGC^hVJeW$ImjILpl)-MFZpJYlkCz)d;X|rKW~`i z2gR+s{7Hu{|B9t|&&?u#W3n7Bgfgl)^F2XE?M_91O}UoeEf z9ae0-AX>h|(*OOYejc*kHJ=0W2HbtV=zS*nFSMCApn+kL?Fk=jR|(_^Wufw;C*Q{< zkte_hWX|nI+=nHS&pio$5?RxHOhfH1nS9Q`yE1c{+Xe1$Nrs+mpo-%v{|T<)yA9-;}_K}nPKyl^eRq^}Q7jhlN6;};_kE?G+gt%h) zdsGwyV3cq3tR_Cq9xf?`yw@9(2E2^bSc*v`kwg+_G2yr!5;*9j;6vzL!Mf$Wp>N7T?L9jW-n>u_xI~Z$cQ-xo~A5>Wnr=|kGnC&vP zNgfkwDBuZ zFXImx!D~l=g}94osfnN*H;mvlBfvGNmGS$G;k6>bBGh2C!0$n|*VF=51BjpvZTNyF zJhQqJR>)%h5^3JgQo_}$>1n;Z$3q;G2!1_(%lMr!cwYhktb?bS86SQldu4YvEAScu zNV1Vd!SP$1_>D@urT{X+N)^R33Ar6NG0GkdU@vMtW2AHIx##LQn@X8;d^Me=PMyz#m05%YSu)({>9ugWnf z@Dflj=5%toK|DBJUfHl39}>CW#nndfpu@X%Wg>Egm|-&bcH1d0!d#mwEBPb3+6Sco z_6QV%d{qQ8*e8~tFbOgEBqDH8;i}b(2X;RTcTs3O1}{Z-jyl#y5k0|Vp$=;e3-~O$ z6O{t2kEjrt5JHV%37w)l-g6QwoVC#8emEj-+~K2>`(+6x({ZcQ-ZlDluCtQ znAJ*-1eE}fBbgN@aM@1L5N=}=9(#yA8Qrk`=s`E$BaAi^Ijfrsv4_$&=4wa4r{-E= z9K@hK3R0RT;f%JMzD3_HLBBMuG9$n(A%xqSQJ8E>K)g}V|Ng1*+;vC6r$7(4yWn5xz#OIjCGsTwQW;c26{mPSw5p`@ z5CeRn4Itr_&&zC%{LXLUGb2mbDZ?9C#x$S9NR>Rf*nj|lS;DDWj`2MU9hC8}nb1#V zLcjS!x6i0Ade5;ZzTJ9z zvB+vQ7l927W=tpNBYE;ft@7~&mG{C!R{jt2l*Ea&@S;7_^DYwMZWk>GqR p#{&7tGi2R(i-%TYQhYaKe*q^8PQ?h+p4|Wd002ovPDHLkV1j`T>&5^8 literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/usb.png b/app/examples/Image/PhotoTouch/usb.png new file mode 100644 index 0000000000000000000000000000000000000000..848aa05735c945e4a7e7555154b08584524772b1 GIT binary patch literal 739 zcmV<90v!E`P)^hwihNcRqHW-()AV?+y?vR;*a-T~<&d zJJ5?1wknPE70<1LAu3wj&+Giaa9ZXacA2oB;LDKKQg68%4@iJ(vcAm8kmi~IV7k=VXp~5PPqX0Wz9J)doZQg zFTvyfwf-0YlPrt!Gdh|8VXi9@;EMy`Bpk68&ZDBe0Ssf8La!`7j#Q##gr4jeda>au+os2Sc*(z~I0cFFK3iKmf0G3GWR8-1VaIKmZ$qBAf>e0;tM{ z_x^s}0(-zgrwG3p9eBozM(O}~@Be~PfQ({48;QmMsLGZ(Q7e`pV9SX|&BgTOpn&ae z*lBd&tQU=7XB~is`~u@%G!wWWZF~~pUZVi2vauk-A#7*?fP<$Z+=4v@cf+Gz6r3eE z{_G%)X=4Cc#eTMxM@|$@!!`z>DhI$VTyP707{e3op|1M&|2nglApx+5DB_DEk z;{i$>^{vd>&+`iBIK_S@8U^s3him>EJjkEAk20i|c!^2Zwfcx=6tf17Xwvu**rSLS z*2@3xZu%kBS`ZU_rEMSenC>5fdr-L;(p>AHGWe)=GkVdZm?qHRQEjYPv0|+&{RNwB V{H;l`n7{x4002ovPDHLkV1g&0TFn3e literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/vflip.png b/app/examples/Image/PhotoTouch/vflip.png new file mode 100644 index 0000000000000000000000000000000000000000..63d2ad2c276b38b178b1a0031e518f5ddb72d66e GIT binary patch literal 1018 zcmV_wC#L zz2EOiiGu<|Ofkg}oyk>_!t)GZ#tHIsiC@fb_~QbL=L)}!F~Z^xmdyozH4{{e307r| zf1gQ{ILxqZVUpEZ;yai!X+N`ooNqNUMMqZntxTJwBX|hmam2PT&DJdN+bNoq9%NiS zwuL8bt2O>JW-9t64qA924pK5L%uuK$zS~rPL014TL>Dhi3njX1h3_?WpYcg3UWiY5 zW`7mE>ERyVn~IOwQWY=679QIczQap{&vC+3dqDfFcp=((U|Z-XmlWT?8B^&lYa{VO ztmUq4;S3E)@VEyPb$v@mWeTxGd4UWnyfwJpR;RJ8DyIeL^OvG78? z!>DaxoEBGntDSIuVNqjcR>J#II zsOPwC;frVkS1RxBE9bj3mq(vz;CDYS@IS@0n*M+Fr@j>-7Dsibiuo1#FyyJHLt6j( zX4-3xnt|&?L}%4c+Zi^kO#jpnUDB9()$W~6p%Hfxy6V=szA>wqH!yEt-ax1U)^U{EW-;deyMCi# z#tgSP$_K7PciClV&9Y&UoencVn+DXF$!s_3ByG`O!C@`T1TRSobtu}vN15S6)o0m~ zUDP+BTe5NeTSfg{tqXbT>1%CzedDktyIff{{p3<_N^|s^3-MCq9NF9QAsSNQef6!= zw%k{k{7Q3Td@~o~7bd~d>&|Em0Yv&?!~DT><-+dEf&6w zn>K!!rWklhBF~_yaE}en_zm2%@q>w)`7GckQ{^F>obVs>(8eEkT6&=te3fO9?UDE$ z{!G@Fu21G`)6Gzr6<@G(c-l5y*~9@}RTN*cw<>-gFHQI>_NRuIE$rcWMYqJ4q4*xV zTl}2eHNs2&W;OKsS{I((&#iT{T9VkzBa?89x;OYbez5V6*j!`0B(ahECS{?4AMloIr1i$6#iy-bVmq5g(sATtK-9nw1u7;g=nq*;K#dYL z1+A$|j7yYi)KH{oA?%bO1#F;F*4om(E}uW_%sqGB%)EK;wP*gEd+xdC`|iGsfk47^ z6yh$7$ITdO8?WLZ4qy*<8V$jsBqYq>JXSJ7KRL{m9M&7)gZL^hGnjO;g17Y~cp6Lj zZ$NNMx>&*CJp*6F<8gvp(##b!gCiL(;3;?$OQLgHP>mxvhRbL{2m_FVu_(k~x2V5j zj!~az-81;Lvr?D&E{m8-xP19s$=c|4IK%wJ;0N$GJyIQ)GSd}WeFh)k&rS*#cxU|Z z{a7tyTw^KIVt}i!geRS&VN!hXA$}{%?_y41@c+LwuGcA?0W zVivQ2*SnHWkBTK?tl-B@Q!78{sQ>E#~U;Ij_?wY?PUM*(Vl!n5}B)=4r#~ ziEyIg=-OfzKYy@A>B51%dJC%oh=xZfTV64&MUH=TV zGY8O7NjOl`xMj&jdCv4*wwS*)j>M-<| zG>6@MO=MZ>727x@DR^1_qBQq&Cy;5W&9*TU$%$Pj5z{NIX-`@)@7s`^sMRD*nq9-1 zXo}{2AxQ$Nyw>VXZoVciS*p6y98Z#fDz9CWTXV=GgFOZKC6^0dPmE-or`#`yUaZ< z`9-{{Q&>;Ki$b1r07tlu)5N9I`Q`F8t>MdJrAwtl1~tt&qx1YHf^Pp7o$GnsvJ43x4aut*lCvKo_6#pl~Di zO2-xs_M;Hud>)Mhw@s;?ob5SRw71~r;4d^eljUrQS>-A&W)A;WrdCe%ppYdy;(G)U z_Tvn$=N{)=VuZ(7#wVEF?UyglAWfX;K|#C;o;Ct8P*lPgf-Lj}a0yK~X|y}?3s9+U zBNuTm4!T5{j}I+1f}R!@$z`wJb*mCDsjbN)MpGz}i~k9RqX@hwvU`C5z7Oidoio5EW$m#*Hc zKLF6+R$ut7y$TNi?8EKe@J#L{EO0Bh{qH#M_XnZpNepEv_jmt)Z{{ytLiHMi{{T#$ VPtIeME!O}5002ovPDHLkV1k6y^sxW{ literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/zoom-in.png b/app/examples/Image/PhotoTouch/zoom-in.png new file mode 100644 index 0000000000000000000000000000000000000000..1e8ed3aae01a38a66f880b6bbe4e113b221b69e6 GIT binary patch literal 1634 zcmV-o2A%ndP)OyqxoI-*?t~&KU!~gnf{U z@fd^KktURlsKS0!V5d>A>@Dlw=Crf$v^#o61A^-CU zZb}zRIrRF#=kZ9C;HF&R5(fMu84lnHcoz%omS*h1KRAp_XhsnIk%5uO#UQ7w+cDEP z6>Hm*`J=T_m-rF$m`FJMvbmJi_I{{kc5LwdxkJxX!-Y(7gqA;pkMa*Ih4UO2J$zs8 zQFB~nA(JA2%U=OcSZBk8=-`9=MUCIZ4Bz10za*~KDV*W3Xb7xQV}D>D2Z&^5GoSg) zXAW<2B=I6U)inE=>=%BL8d=PsC2-g zXNd(`l}bKtp)gP1oF4K+V7ZJq$W(2HFX7dQyU%yLL8Gu%HST3EcVq(q7{YNRb}O2Z zgM-4Hgc3X{YIWG91*c)WC|6+$YD6soJCG|K=Nd)c?SDtoeO+lsBDZSo++^wMshYK( zS&D76`T;%4pcj0y{NmLp>)zldOGkSXAgeh*v2IXJQ@jW~EB&5V5-hfjbR+^~83V$a z#ujPV;zi(h)2l&gf2JDln*cdSv41JmtsLNt9$*T@GATxjFf27^Wt6f^iTgyV6Oivh z029#Ltb8lfX_yfcJdlC8qP!hvr6JFafcR!LDT{0tyr!+U%n_w7lu5PAw=sxOX7O)y z2z3Df`?}v{39iFtt=_#aGv&01N#tSZxT+30`38_`7B36s;JDz`_zSAKuQP!Zvv@@)QxKnsHYqvX z(gx6K7UlYFMSLQ6lO$E`m^OeWJMZ&x5|Hs)uQxgQ25`|V$}7#0I0?vj?V9A+t{@I$ zWOp%B@vKHBfFYQwIeVQ40J+zc3ysW)4rQ6CB9Ee{mDAmN$8(RLkCAJhqt3k*K&4q6 zj4Yupj$;5ef}*T^04klm1K1}0<*A_@J7a`zgFFTgH%Au$wz?27&S9Tf2`h#bF%tL; zwW1tWZ@AlBL-?8;sL9F*Z;oLO)N`P)rm{gA%H7Zd0E*FRR+lKIFe;*KeKQ_IgQz`+ z8>Q(h&p>`JsXnFMhU%m0fv{q`nHQv?omph!vDX}~lehzo!aM|9aF3|%#(P@u zI4l#3RKDkbWH~6hY%FvEw$Q#dBzuReGuZmD%}s5 z5apQ=oW>0{U6}1^+Ac11ABGY7y4%AsQGoa7ha?*;Zwnw;V7=fMx>K0I%c^HP*D>7( zcpuK^nQJlkJJ|Ess8d)?!;Qjd>jZA&Mn0lkI<3DfzO8lKDo3xeJg28Ea=o(iHdfl_ zdp%1ypLq;1nP89uIhM1ymIrK}hg^t`vbOAN_g+ZkI(9K;dD(@+2-fJ1uX^nZcdY>H4{1di+>!6qczt)m+5;IFT#$zadXC zwzoi^6yA@xb@g2P0e}XJJmHJt$B0Cs7famX8Q)8o?_O~FzvCR|#Gl(gi8K~+-?gX5 gEBuoSC|`r{KS$aeF1a)NJOBUy07*qoM6N<$g6W0+ZU6uP literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/zoom-original.png b/app/examples/Image/PhotoTouch/zoom-original.png new file mode 100644 index 0000000000000000000000000000000000000000..61f9dec6378c622b7e0af94f7378ea00b51410a5 GIT binary patch literal 1615 zcmV-V2C(^wP)p1>SXHEcOzj@{T%L{++>}PHq0c*#;Q-zTZ(~JhPZJK|Br0(gO^8E(q~Uht zV2D%JpRmBF?dsSQ`K5hOSNR@u8BaL0EUsl$=yN#FtggZL=g)ek8m?rLBeZG;ALI{q z3YU3L^zeOoNanb~0{SC>tCq)Vdo@gs4nB@w%lLy#^9=rf^RrN=aFHXUA+SNle!&C> zh(u;_CTDUcvpL9-#B*$yX)2iL6@G?{+{HLs;xxX>GPY?0tmZaOqha%0pnGld2A{-p zDyEpdw3MT{#|AB>oDbV5EY&|w)4dQ_ry@=;S*!3hyba-SlB0yGgfau>QyXJ%=uvqy$EfTzFS-lbrF^D}$9I#?5{Ur$tmWZjoicBA#`nQmvw}EinHM5l*@hQ2=Q>cL%r0xA z4`WUBH?&#iye`87*<$0YhJ*~~eUcAjO!cH?^yBUr2mopoKB0^Wf|o`hV5(;)`r zL_lqZ{*vZ^ldliSrh3gX4vh(3#y3cFUuObIrrKy3lMtI&V-h*t()!S1s_~Yw1+hu9 zrTL}VF|7~5P~MkgB%tE8ryg|j_2G)Csw2(m7zwC&?U>{^E*~mQHC=dC#YCXg(o{~g ziD~9cAV(aA_QwE!0mm(Uk}{t)TAc}$o9a+xSms?Z5ZDb`dag2;ySQ@@JIwWBwO9>* z5zEVQjjQNM@Qy~aEBnXs8Jd)H{zT1WM4Y^2;awo7_ zdCeB#pLT&j9S2&rWY(+1rEnJp)TV#GNSa_B6ulB?1v|!(;f& z>P$l!1_KD9EJEQ;fgiwP?6fR{aSFGZKiV+Ps0mmsb@|&0x9xBYL%AE;5_@uUz1jDVm~b+iR*bj)G^2h z!cllddA9R$Tao#>iboy5|KS#nbScbC7Vwzv-okl~MF;@&;Tz1heqkNfg^*0eRE)+j z^urC*p$3Ohj&_^R0DTjJ0=zDNwqdEU6VAc_FG#xYvQNb6XK!=4EyR|uZ}B_0()~4z zksEpP5XVOW-k)1Z*4yqDKrqjKz%Ov8Fqzk+=S^;5iU;rnF5|_{m?;kZxxB7ZD5T*= zA=6&Kt=!F7;?QYt8GK87a2p)GCi0S=c8BYcougPDTJLq-!)456fQba-IFJ*$kehit z#PgI3(XsZCeeC`e;<-hxq|wUrE)>RyRg)z7Qn>HJohJA($$+9-s6G z0e~xHt^O)*pHuXG#k|wiPmGCp=BcM9)B{B-r)_yK?hcZ7xC z(WCGHz+T+r4$s(b!aP3(r~f-HMf?Y$TS*LO0graxNH_9FuAsUN!hcM%A>ivStNj1~ N002ovPDHLkV1k#a2Y>(o literal 0 HcmV?d00001 diff --git a/app/examples/Image/PhotoTouch/zoom-out.png b/app/examples/Image/PhotoTouch/zoom-out.png new file mode 100644 index 0000000000000000000000000000000000000000..19f24bb0d7c265e6b9311545d5db9f279b825f9e GIT binary patch literal 1568 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 literal 0 HcmV?d00001 diff --git a/app/examples/Misc/Console/.lang/fr.po b/app/examples/Misc/Console/.lang/fr.po new file mode 100644 index 00000000..1260fac5 --- /dev/null +++ b/app/examples/Misc/Console/.lang/fr.po @@ -0,0 +1,19 @@ +# /home/benoit/gambas/examples/Console/FConsole.class +# Generated by Gambas compiler +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FConsole.class:122 +msgid "Console" +msgstr "" diff --git a/app/examples/Misc/Console/.project b/app/examples/Misc/Console/.project new file mode 100644 index 00000000..cb72d11c --- /dev/null +++ b/app/examples/Misc/Console/.project @@ -0,0 +1,16 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.0 +Title=Console example +Startup=FConsole +Icon=terminal.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Environment="GB_GUI=gb.qt4" +TabSize=2 +ExecPath=/home/benoit/gambas/gambas.link/share/gambas/examples/Exec/Console/Console +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence diff --git a/app/examples/Misc/Console/.src/FConsole.class b/app/examples/Misc/Console/.src/FConsole.class new file mode 100644 index 00000000..d9a9947f --- /dev/null +++ b/app/examples/Misc/Console/.src/FConsole.class @@ -0,0 +1,162 @@ +' Gambas class file + + +Private $hProcess As Process +Private $sText As String + +Static Public Sub Main() + + Dim hForm As Form + + hForm = New FConsole + hForm.Show + +End + + +Public Sub _new() + + $hProcess = Exec ["bash", "--noediting"] For Input Output As "Process" + +End + + +Public Sub Form_Close() + + $hProcess.Kill + +End + + +Public Sub Process_Read() + + Dim sStr As String + + 'Debug Eof(Last);; Lof(Last);; + 'While Not sStr + Read #$hProcess, sStr, -256 + 'Wend + 'Error sStr + $sText = $sText & sStr + 'Debug Quote(sStr) + UpdateConsole + +End + + +Public Sub Process_Error(sStr As String) + + $sText = $sText & sStr + UpdateConsole + +End + +Private Sub UpdateConsole() + + Dim iPos As Integer + Dim sStr As String + + While Len($sText) + + iPos = InStr($sText, "\n") + If iPos = 0 Then iPos = Len($sText) + + sStr = Normalize(Left$($sText, iPos)) + 'Debug sStr + $sText = Mid$($sText, iPos + 1) + + txtConsole.Pos = txtConsole.Length + txtConsole.Insert(sStr) + + Wend + +End + + + +Public Sub Process_Kill() + + 'hProcess = NULL + Try Me.Close + +End + + + +Public Sub txtCommand_Activate() + + Dim sLig As String + + sLig = txtCommand.Text & gb.NewLine + + 'txtConsole.Pos = txtConsole.Length + 'txtConsole.Insert("# " & sLig) + txtCommand.Clear + + sLig = Conv$(sLig, Desktop.Charset, System.Charset) + + Print #$hProcess, sLig; + +End + + +Static Private Function Normalize(sStr As String) As String + + Dim sNorm As String + Dim iInd As Integer + Dim iCar As Integer + Dim bEsc As Boolean + + ' For iInd = 1 To Len(sStr) + ' + ' iCar = Asc(sStr, iInd) + ' + ' If iCar = 27 Then + ' bEsc = True + ' Continue + ' Endif + ' + ' If bEsc Then + ' If iCar < 32 Then bEsc = False + ' Continue + ' Endif + ' + ' If iCar < 32 And iCar <> 10 Then iCar = 32 + ' + ' sNorm = sNorm & Chr$(iCar) + ' + ' Next + + sNorm = sStr + + If System.Charset = Desktop.Charset Then + Return sNorm + Else + Return Conv$(sNorm, System.Charset, Desktop.Charset) + Endif + +End + +Public Sub Form_Open() + + txtCommand.SetFocus + +End + +Public Sub btnCtrlC_Click() + + Print #$hProcess, Chr$(3); + +End + +Public Sub btnCtrlD_Click() + + Print #$hProcess, Chr$(4); + +End + +Public Sub btnCtrlZ_Click() + + Print #$hProcess, Chr$(26); + +End diff --git a/app/examples/Misc/Console/.src/FConsole.form b/app/examples/Misc/Console/.src/FConsole.form new file mode 100644 index 00000000..6f20aa1a --- /dev/null +++ b/app/examples/Misc/Console/.src/FConsole.form @@ -0,0 +1,37 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(38.4286,16.1429,99,50) + Text = ("Console") + Icon = Picture["terminal.png"] + Arrangement = Arrange.Vertical + { HBox1 HBox + MoveScaled(1,1,85,4) + { txtCommand TextBox + MoveScaled(0,0,23,4) + Font = Font["Monospace,10"] + Expand = True + } + { btnCtrlC ToolButton + MoveScaled(31,0,4,4) + Font = Font["Bold"] + Text = ("^C") + } + { btnCtrlD ToolButton + MoveScaled(36,0,4,4) + Font = Font["Bold"] + Text = ("^D") + } + { btnCtrlZ ToolButton + MoveScaled(41,0,4,4) + Font = Font["Bold"] + Text = ("^Z") + } + } + { txtConsole TextArea + MoveScaled(1,7,47,37) + Font = Font["Monospace,10"] + Expand = True + ReadOnly = True + } +} diff --git a/app/examples/Misc/Console/terminal.png b/app/examples/Misc/Console/terminal.png new file mode 100644 index 0000000000000000000000000000000000000000..3c032bff6147b835d505cd4cc3d3502e2b5b5124 GIT binary patch literal 2312 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⁢g}1bbI5i*2d^#x{`rcPI1*xMPaC?MH{xu)~ZDS~t zUmuY);(xR#ACyU~l0f?;J|2td6_d~5n(|_<(1K^8J0?TizC-JVd(qNy+|*6+j7o#^ z8naF*d2({H=MkUHDEgyEkH&3ldhGmNTzFFXon&I@vvYDjynnyEHCAA`z<~|-+}kKG ze+`N>pCQxnO_Jr^SRHLr^QlfH0hX4wLfURI%Z9ylOZM;#R(tU`DfQ{^m1~mvi!hBP z&*-98%1rjjTo#)PP5kHkOZ?ZuKGB=AJ`dM=_IS1S#)$2WJ{E|R=iVyM{#C5rGfq?u zSl(QAD>kZV-xbUh@kV@;2>7m5_`;$up|h(ic(KL%c=;C^%48YDX=GneOxWw>F}Qo+ zw;aD~RZASMuB^;@75DHaJ_{ z+}_%ZWIy!3Jx!e7>j-{ov)C*EZf$LVe`$4lq9|~3%An3sz0T37xuxZSTA>y;g<#xR zfvRTqTg+Il+=2pb({^-JRMf|hAAi%UvOJQmQHXy@F5-pX-SEc4+nd{VoVj7}SHPn_ zqUT1XkTw-HwPzN+aWwSwU&QWj@IGexU4;bUklC)Co}8>kirtCF&@(Ehyq);*u28?` zCGr8!_ZGb1<%fT}`W!a4+srycdF-d7&>rD-Uz(YjZ7#M5z5Ai{YiTJnfz6<|(%M+g zHL;g_Dj+}A@kcYmR?6+fO&V2>v5xCSpYbXG;}iQx!^ z&cYR&K;{660o3C7n2h&a_*HQa#m{j_K@5Y11f#BnzGOSRNlbi8n} zyZ*{#i%J~j-QC^kDyztZh`XC(Rkptq<2CocL-6cpt63l_^e}RThFCqL1zW$tTQTS@ zZe?Xz{@-ZSW}YW7*~sdqlJYAv?Oq;lsK1Oq*&Lx$ZB5+L z8WKaPaBV%Dmi z-I*#TtiaF58$+=3J>*VBl$5X;HM%@NqQ+6fg=(G}{08t&l?>sYm5yLctlp3sAz;!` z`CZWTC30*Wt@W|$SAX@py?RbU(v!k#cW`>T1}bv-dcUn}b~Yu9h-0)^kK1vn6-QiG zm$;zF-QDS%ipxe)=1GSUdacb?KpzRY?Jk1@adCZ}O5pGI z=Qk#w{j|7;#~X0Gpof1ti}k8xUPZR1c7gkmcXs9iFaO;f$w?D*RZuage{Y&mG1KT; zq`vRFCy+ank(F9rcrq{8-(%PNw@6QiJ&3({UFk)5O^(4Nd56MO(#Wah_F^3N?Nmv} zwEsfw=|mQXs;Mga(cfs^grRA7*y@OTf7D9;kQ|RYs!7Pk!nA-U@hMsCLRzcA4^639 zHs2-JF`gZ+Q*WMA@4DX~r~cKI-f=)$mF8+44i65#xonLpr3=1mC1hk`8mn`H%+Jq% z*T};%sI~v~>({5Ve{*P2@$sGg$=rqD)zZ?^FJHdAKiz(ZkM&SBLm2rM0F0vdH?ZdB zX0VACi_M;uHvnE<#lO-)3=F_UeXpWYQhqSZ@h|xV0K5i!H8ICwBqFTngK&D@CPB)q zK_D0MA~__P-h%NkZ9{mKM*VHHJR|O#_aoc1k@%7Qjrpk6)+3+6*_WTpk<3~y$n?wh zJYLuL8mwWkoh+6Ir$3n!iUG=T$?qBtstvX~&AB~Q#*rxCbMg9gdm=}xP{U<=T)wrS zATN*e!=Javt4j21tS5^}HU`sU*JjI2(I$#?3S{7<*u<|tp4_)g+w_K@9|((nIw13> zwaBH*%ZrYWQIb*M!rZdWu39E}hLejXjsN}?U24JB42CNdo1dRw^m><@-MG2?Z&&20 z;5#ZYKcz@Yk=53qz`(=Z8EzB|T=MR6(~ixFA|f~`Z#JmU-NhDz();$nCp=DzhLga3 z=U%He?Fiy@+tnrIvzHsn5W&R4qUGd_7IfV%ciGYaD{S5(5dqiyp~b?*#nr#J#L#A` zMC|o(-(YBEhHC03BME-RH>H)HyAEu!`UKJrCT^3TC{;Vnp2v*JseGNkem(e@Egg1r zd%o1?whngIW}%Tc3fiQUAxuI=MFsAtGloI&yK#$P@KY*AF0L1#vG6%9Dp^}I18Ak; z;n8x_EdUVivZ?k`r&OsklmMWA!!AA>^FzL)U|fpOf&!+DjEwrmMlSR2XULlH%&)TO zBLpN)1#H8E?*?`3pFVv8(v_5x@8sm9%PnMn96*QwIDT|8;o^B3rVb7RQ8ZGkZ`_|g zeOhYR(BA5M4H}Ty&yNp$ZVtPgjYPaoeojmfe5_maQuO<+LHs2s$a%HOBgLJ zt(=NVK4_X=YgkX7bbyZ!l$2=myS)JGq+T>3bFBfI7ih3Eph}{nqsI$1gTd9wC(o!v zeUjLX8ciG=;0k%!{`Ys{gPr}u)0<5pq_(nCWZB7%g*0WUQ{-rn%!=auD$yCXWMSRU zrCB-q`F7n=ug{;iiiRsc4qqEl=AO*_>;Z=>mxiA*i8W5KI&CLe>M71~k8j+BLnJu! zWfgZg^QnAQC6$yPE7e#1rFif3r1Ea3Wca8bOEEkyP2-thO23oNwTkjUcCCUt7Mq?9Pfw>nxjx^QEzz#> zxpW4r+5QD{zRf>#bJN=EbX(fYj21v|?uWmKBX6SyXD?4rWQU($SQr?+Oy3lev&DQN zp%B#_+DFSE8pxEaT94q3_z+G@4*hnJ>Y?eXha5!LXxM zdB?@(o^&C1-6l7-sJ~G{em5SJ!G;P7SYUs!DTR|BJ|TR+HOdf#P11gMd#!v%ljyCQ z8JnKRD8!pw97oR;`$s-BWNoCwqx5aTVXFQg^!I{f8T8^Z`hrhK`_JF*#MnJqoz-kq znRtMPp$2vz7&bp=s;ofM#HJE8WUsOrV??sy3TW5Gda9Ghe#<$iQd&{bj9Q1e(JCud zHs6g|DNy0|bG2}SSK7$OOkg((&B>v&8p_B6mGVYNsP8!?3gDXgc2lL()6+={brPCw zCey#ScpM=iqeibDREU%74LQMV#xQ--;-?u9`X}Sr;OcO4ftX}&E9a{d8-M}t-@lKd zd&4tRV@G5+S-b)u1lcEr-1iO+R-^z#zkD}{msF%vRtfFGRml_`0Gx0YW)QB0~C2dh1JhzN=>K)b;J5IqJpGkfE4Kn)N*;ofn+ zUc$wtdhsfhh@g9i`5od$!Oao!lDXms6?S!(qKU|lXxLQ6+ib7c!0)~YIQ0DyFW z`GJCnPD$xS(gIc;Bjq~c` z8oQ}=IRByvKzPriE#q7yqjz zbgif92;<}(=@oJ8zW}6<)2T%OqlqS9$xsRc5rrZW1XR)cap*=$=3@2}JP-r%opGi% zAv(9xrlr{Hy*43(;UrfHe*9qW+lZ@pMgj9jYg}{RUsMKxs)Z)^0^=4hY%)GsEH7$Ou_!RppNJ2oM3=mT#Bj}>vHC+q$`9GvUzNz zyLS%`t|Q=NXX=F- z44^vJngO^==4l-6HSK4sF*B=Yql47g}cF<0Dj6RVAmOC^Tx~ z1IEqYtJ9tFQlr!+_kHJ^iw|IOvL4Ha|MKG936F@7Db)J&=g()2!ts<}mKGM#F)_6M zPjPV>-@HjGE@rK@pOFNF9XTIPUO#Naf2AFMap7?plz-V?A(=HynhK^2TR2<*(Hi7doV(=}<^$#iQNe?2Ha$zV{| zm!rdkfAaiBc%b~t&B?-OIkDnZQJJygAk)I}yE+l*Mr{QjjKX-y_nZR2+aqc^IXo>ij?~YE`&Tn377bh78E9AvM zmf_*yzXt}Ccl0;^{AmZL^lL;!!fA3?{;|r^Wzl8R@u^FfLKd|_dNOpis93(Pk``A^ zQJI0AkuW}hZbtj1#wfR(s=_xPHvg)j!}BG7h;hSkXJ;Vbv^|LG(qhmVQS{G!baZq; z+xQw57TacbR%AVr?cCqS;=8jg9SdlD>)n-IhNy2ZfKE_G)YQ~Jcl}V!^K##_s{dLT zQW-nAwKqhCF-B-BJRE5<{Zr4X&?b};O7Kcw%LxnZ9*dW8X>c$CG`FM6 zjm+as^+D3S{pMrbwwt4{8%cXDEqnk;YlG=Rj5{zFms~IcfF>e)cUWTBpb>FOD(X{D zp18lcDF>F;xEDSKrbs{oURw`Sdot#07tH`hn8*Rimk7XM+}WJ2pcA{h$RUrdG4FW+ zh)m(TB0>~U1AlkQ+8AHIewL>p|Ni}JJye^7(c*L4#)%)E)8lyG884-R;6hA~9)O_C{IKJ_mf8CRx@A;T}zb=X5 zb+M|9uSK{g&1oRcmS5dT**E;4bK@#pU;i?VT0^WKP)$>=AjHD(sYIsFRqtle+%!<# zjJZXoxUcT6_hH-PEGp@OonZK%X>#X?f|6U9n?EA}WH)OWfS3O1<;BG}jY1^r1=%?K z0@ciQFq!~@okE^z7{Jbd@^JXal2+#F7Gh#C-lWZ>8}-G#Si0hN@c z628q)X3WV2&4@dl2jwA=y8Auj)}aV$+*bXpK9oI$V{x(7Hx=+NV(g})g9DT(=mAZa zaaC1SEE89Xjh>#~@6Jy3W{(d*s zVPP4pb$cQ!Ng ztDL70{~f4cfbFrLSR`XtILz^ZCqt@rl$4ZUHtIf%X^a#)<@C9D|2ZH4Ia*Rve*}~S z$z}yGN@Q4?H2cTGwe}4{U z=xorR0MCd3GOT945(!XIZ%}o>eEVIygcRtGP#{F>SGoBjM@&!y0PVUBoLZ!dv`apw zg{ACGgTtg?XW#D`?(niqv_my><;T#0|A`Ktv{<0$b>7g8m#6C-mz$Cfp{C51|JIP&lJeHbG^b*w%;x%(JMMOd zW@FnESlXj2KQs_%_W|9qKV2xX0k(0p;J#FW98Y=ef>Kk-ywCU6fbs|u?9t#jAQuHR z3L-sX5icGjnIOvO&I51xJ^{o7Jt+W2+?M^z;H)I3i!@%1?Rv|}J$7?*D~7aX0MhZ= z|ITMHO&}-U9jpP+V&!mGcq~p{;bZ03qzA!!8JX;vC_S)7p&`YxdghBm^vA}G+upWX z?pi+TyJ0?C4UjfJwO3>Fk1rIh7Cw-dHOZB`krKb_T3pG-e2MHhbD+{63aHtXJllG zHMSE#Ohz6tFof7j>*Iex4cs9fyPDeg{yGo=KjOa+pnzT{1aCDhTJu;xR6;2 zVW?M)xxLba&5w-F33Xa-z8l3fQVU5;^TV72OSO4Ru1pk-nWt-GQ>|IB8)V~5d!w#+ z2KaO;IQ$i`4HeP53#ixGZU}fB(88bx%fFcMv5>m>+qdXQr4l@DI4N%^uz-w8y~n?+ zn_F5|f{sMUVH{po#(~GA@_TMhC^At`=N$Zsht*7_1@hgXmPTqF4BFeh1ysmqjo84gTh{gPb!AlZZYR~ZT>AME`0blTg*n=_9tLs%=I+*qF zsHjvy)yKr)5!QV8D`RhOe{ghU1_~3KgbQ6h#^ZQ^e`~rTXGYZ@1@)$vb0fenT(>+; z$(3-XM_!I(%bbZR+UWO#Ksei^0t4jKUJ*yNgN7sU*Jz$13Abf9xw=}Ju}H}~=h<3^ zJ}`IHd!4@A+S)4Ctw8nm_D0cLYKR^F_KkW*zEE**bhfp%6(}fi$l*R;nJnlj)gYNu zvnh|#G;zfo|MK%FRPAR~FBk{je_O9F+ng%P>~xK1)r$#u^z>*g5u#CLsjQ)?Sy5a2 zpK%mC;u}Fh+ckeM#$EiYTl%h3M#-QMX99MuzP>(b?MsrVuV5^rGV)P@+ZF`Yd$a*& zrC;aB)@Gdv!3T0dyrh&=4es7_1=<1>^;ci}eAi~6Q5($=Y|jeTmttp6NS|4_pP+)m zdg_vD5X^9z!p*oIp2GnBcDbNSsoB*pQkt4yc8@qBewCvtR+fzmyGo>5NJhRXuTb7y zVn|HU#d7;R=K2eTkl6#gVf{O&WXt_G{UQ8)GQ;)>(VN6Qw1gbg4Sr ztiijbUiQhCi=`)gnKd`zv!XRm{*l@_&a`Kj@3i?`Q&vqn{k>S*G5-NGt0{3P#{6L% zf4Cr3s9x%l)$7Cpe1p!Bk!a9`2%5Rj#*1_!0X~o!(#{iaPwVdCc!X`o?mhR$VC;XN zdsV=FWU;)F2*wS8e!Uoa0d%Bt%hBxM>vj#AD%N?Y!KZJpUr%*Fl1>f&98M*Q5ou=l zL1l6)1pBvQ^C-tZj~7I?^Eif!e0rU)qIJ^#l#S0ULoK3Dn;o0-vENC#ZhOVy^pspTP{$hidbZx48HnDVXo4fy117p; z5pTYa&Fns=ceSUUYXgOS!%n5zh5Ggs_3>Y-(eM23_+#kRv&|^n4RB; zW^SPUdHVB#%sl0kli`X^V1*m+al z8ct0zRlvOknfGOoOob8eTx4;xg5OFWxg#v#+0hdA$zxPd2 z`!Ib^lxZ2qZcoEk?oWAWr<2f^!d+?}Q++&}5Jr_V>F;{2yG4Y5^P=P8-U8A9h7Oap z-uO$(HlUA8E*{X|nDg;6sdyD)h_13`3Id`9hTH&p`8a0v&%Ab%YPsD->A4~ADLyUx z<0GJ6Qiys^j@Clp#|<2+q#Q43`ChfQ6+;6(=e1I3!qcAw2`}UepR5OUZNV|2l!(5|}b z@K&FE;d_MeP0&#*Ynh)Z2)TdGQv1{{SXv~d_$Ll$ZKPNeB80fI-M$E$tZS%DF<3N2 z>zh9yg065iw!MQPj@}Vp1P&D4{UgIl=R6bQnc{Ym!!Rmq;Y{CL$CGJWZd1wP$YObR zqsd7~W#6yMUl7h<)4(shK$e;H{(+*$1m|4BYSmk9MUlpQY!^*_f7oK!FnQI@rV)R? z`0;dG&udP4nBR34Sprl=6zbXUSU$J7wo6xc`J`m$U;|O3AxFn zI*PrI(aTAE>Vz$p&R9na&?dK#vz1@nP8;8%Nk=3;lG2(1>lIKWI)sN{+>Uzld z{h2?8_NUr)r{;9se3{WybQB5Kao#a!p%1Mn#uqn7V+#TfG z*e(}RQ%i*|xm@5}vSNt#%6{d(vJsAFH;J!_OX>WuKi|^^t`zd~D?^MI7A{yh{rdl< zGGGK&2(KsbzBA5zVAB1qqOy`})03A_#y;!Q4Qsx)gkzUSTG2qewLJgu-=6U*Y(>BX;CUl z+V%HXb!*xU+P(}VptOkSM;s-kmVK+3<>=tf+-V+J&@=o`w$^yE#Gv53zGuP}xU*_S z6W_I7+B-NvT*Zu;ZEI4(!?Cqq#y8s~i$_6>Cl!bvMq^?-z0K7_q$Pf4)3cCvufc^n zkO-A>45&>9l%^rtom`k`@{dSHOIVDONy((X5v@j*Oq18y^V?$@JV@TlLz! zF@r+hi+BZT;6MTc)$>mdWP^$H4-2Ab&BF6`0M5eqPCGN&(NL=z*Dk0}TIlZ_1rbj-_(ox8VE z!kr#9x<@hkemgv08aN)M{9Tz6E*q{`ddnm7Ddk^_^qrv$Tt9+|sVgZfwpd zoHML-4GdzzZ@hxw?uhH>`x5_#*u?n)V(@FRm%P1f0+#YV4S8M1V;loZ#TC38%6WF{ zoNMoGLPFN6r@qD~YqUqQKdE3k>7Sm0&7k4+Ai# zkqdj2i@RQ2URL;CyS3N=TwMWukmIc*SOM^y%o@3m30`WMfXe|HRvLsIuPe_)L#?cW zfwusRP;VNqbYaiR$#x0^kdG{Z7aqvo5!N2V$>YY3v^Jm9$uw{8J-$OrS}XOPz}W1& z8>2rD2`VZ1X9wcNcQez|z^uK}Z*`|G$A;nb-w>Z5uzeNBc*fv8)+&ardw8f)p4iH` z;XPKHs#PsIHp^%rwNV8KvO)qY(tXlva&yR68kg+ z4AMa2OB7%w(X+9^r>Av6h-C;2ph(pbSf!)A?jTDrIywr>pbaoUn{5tfr2+dmerpwo z*%;_9jrxT(lt7I@MfQxJcL@nu2JBYGTCo^Kj? z3cxK61fhVKE9$Cxom&hlV17=3_mPoRlO7 z6loNoli~nji4OeP4D1ooD=al?LVXWpS1>dc>Q>MKF6nc3x#3I!1UZnv2*4tEsRjmT zz_aC)l+c~AyMfE54h-HOI%R`^Y}d^4Yo7rE-+YK512?YG=wfqqvL&UVp+VVl1IAC} z^1JS6H@fc+0##rOID29@2WWuGhLdn(`1|{VybhT3|A4%Pcmz$U8Z(G}Vbajh#Bo3} zfOWa)AVmZQOjI;95J-5HrTuT52RDEFy9VqID0D$t4h_h&~3h@$>kX zOxZdy^uT=x2Ezk*8m%I2IE9c~Qrre8RB#%RQHZ_-wn88PfJvAh*w?`BLwd%zRH91a z;wT_B0BC>t`xQxrIObn~V#2q3l9Yh7XEFRu0!+NgKzs(?Odk;XEE>Yo`2n7R5D`8Q zs6kXndVjG+UPWb)I|v0S@%s7sAvyBPcR#Fa9w1W*NJ+mZh5^Y{8$6FKDJd*0ExA08 z%=HqG^AZ`qW72quxCt)pfj;#!e5?dSY8x}OwLJWG-Y38L8quq(F{o>_F35xheen$# z9l&1zIxL3sB&5}4D`v6z@bIwOVJ_jR*i95rE)VCN+K{J|kWC-`V@(Y&PznC_#=k-i zPhdR`z{Mj7#{{F=!B{R*Q+`zaq1zrjeqeejF z1r)*sxD@m0Q!6mN0u74bBQR;f3GYHGFTf)Jk8e4eE03}}-w+FiVdwRJ65y17hQo1y zyx|NUA_Ck1aI-&LpE&@udH>v_SCzIhdL9R`7RV7O$)iq=Ky3*Z-t9kAVk(bbb78PjyE8v5c~P@uIMo? zuH|&ODUaiP5{UemIXK{~DmeoQS^?ZbII=EY#)qEnOmSNclDBtsTml&rIZVpS%QFt8 z4vF2T0W5j;{J8{JU`|d>r4$~PA>s@HSP(cv75?{rVBxwh4*zaV6eUgyd4nyp0Mpl7 z85wZM2{4{grpW?>;gSKkrK{ke&I7Fwkms=y1OBQmed^aikEbe@WvrPq&RnnX9vd@W z!aIkQIk3xo9W#hb4|Wpz)}+7u)xC-vB?ey&HXZ9Du=dnjyj)u@Bgh3l1Md`=WauER z5D!!lTTdP#*X=K{v3MvTxA+Ypqlc$w7BGgmEU-XH{@>DuAzcavjd zvEVR$046!`e?eX47&W<7h9gVYekNulM;4^KmzRGw?YH)U^cCaIPhtke1bTM%2tdQTF)jeV z84xW1rTa0>T@-99qQ(0hd;Q-+Q(bd=DyK+tOd&{2fZ+hE+mjUi86~CTNpp|Gam)E) z$KoO-=xiWm(ys&Yjh7t+rHOQ(!T9pPdg2EG*lDDqHF&(}y}z=)9t5;%0D2oB{^m)z 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 m-%*hwe=_Ghx4%%6Ah=1i!qqJ_WuE~R#>wD literal 0 HcmV?d00001 diff --git a/app/examples/Misc/DBusExplorer/.icon.png b/app/examples/Misc/DBusExplorer/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b39f42a15875f43797677672e52f42894ee11d1f GIT binary patch literal 3487 zcmV;Q4Pf$#P)27a>Y@&cq<{=su8*^oq4 ziWz=^0e0UpZ@%C8{pL6Gn}KC4V;Rd>#xj0yAhM zj4|x2GI?3L)TtyVv7>MH{g2aqKoevwnzQE$&}dt_a74x&g%l-+&662mzhhh z)HiT?NZ|Qbo8A;W-#BTP7aqN@_)81#=?^2|`j;*MKy}gL-oMNUoa&c%o$to^1@OT0 zu6CO*F9ZZHTmYs9wxtB`L+G3~rna87^}878Xkxhc1Y16N|5Xt{`k4ZxrAca~rLCmO6hJIq0N?jG{NA&O`i-Pp zjuMmkOcdvm6;ddn7G3|P;2aCko~jX0xEvnP(|(Zgf*geFV7kuG8TF~H+lhI< z5vc@DB96|=M#?KEhFQSOa#cbrC6nh^fLWXcxK5J(vmIE`C>Vpa`!mFPPjh{N#hI#4 zpoHRd{`*Otn=fAU;zy8xZx$obf zVE=zT#XmjwYn-Y&R8}^{7EB12ciw#g-E$agbs5?DA+(aHt2pP-LfBeqWvc~afVoHm zNW_Qv&mTO@w<^kc`m0}I|Brt}w5FcF`Qh{Q#}gz9O9*ehmE!Be(_JA^r;>~6E@mO@C5GVS&w1@@-(dgt?W~W*$Ptol8|&EinR{p)by3E{caq@y zAOxx59@gZR5DMp9Q2`cOo0P_umQ=Qsf{Y?Qyco}AY@~6PA=M&eeQgrDL>UpIHHE5=9x&A`6W5l~gF+f1=LvAWT*y03ZxD^di3JLo%qpT# zNH*7$ad;$&Z{Yh+?qly~KTXz+U*_PcE_x4lk@7)@bMPj|5lWL9=%hU9QnzCl>MATZ z=DUCaVJSVRgUTBcLWvn)G$4%0Y~3qwJi{07*v@~SRE*gkzHkT$gT%*9yZEk)s=bEq z{o~(Zt*k=SY$dpI4Sv$bdFPiz)@?^%hz#`d$vqF8vn;)2LU=?(oZ?WD$^uDAu4HV| z<3LYz!>yxf82DFSTSbMJQk~nV4j>w~K$vU&{}U zHLyd#zT6@Pji$7th1>6a@REs_s3F!Bd6Yyn!zsgwL5F9WhH35@Bbm;OSv_Nul;wq} ziUc@1o+*G-b5lWy5y|Y(fB^XJtI6J(9Dr|(c0I4ybyAEDHM4GeHP61*Kvyh5R#7=R zJHpt(H^|#?9r2MN?v4g|Hg3sdJl>5IPy39h&nbWk$nDh4^SGdzhv^|dXQ_^ zX7SW}T{N`xlS+H!X9pP`Pi8!~x-f@rl|{Io&&j^CJa}_8zv?Ps{>sMEYEU3N0M9r4 z?QL1*8o*M*7J?9fG~oM!R4PFzJ%sO9Gm^IoG1^ROsE71efJognjJF;mQdG+8rw7SR zox4&Sc3Z=+mF5+3 z7z$)zPgq3icHqWGiJ$raEsFSTuPGRv zOmXvu3U1mkcVyuYYRfp@J4Pz)F*1>&KbAmC$p;<7_y*d~rdgrl44OFsgqk-Q^vpZ~ zv`o$@<9i5S3X~NfQc{7Ho6E$3cSt!2gb)N*uA$5xMQY7J+Q(}>guiw>c~y0o>>wd` zf|bhU#IXi8ZoXmB0`$dv>Q=7cK-(Z+zp;n{Gxjr255%~4a}h@;=IYX7&H{`ui8%oj_Uuy#WU+@+eKG4&e$# zRrMyMy0pYc!bI4VKZz7#w#NaEb&s-RO)*=dh5WLq^PJ7xm3d(t&u9K;rOsFn&2s@I zk~7&7G5aK_Hq~4=E;of0WGnVq;BO3wpA8UUlOJ$OE5O+(h;|a$>NOX zEiK0^EvJb4=FR(e2KH(ItwqA0ZnUq2n4dkZtlhvKEotcKV69H@R!cjDyY}D*!nCz@ zAVN9BI!_avoFKb)E0`%a5JF%niNd4l%mB?#|B9-zG7>{)Nc%qP>$hF70E6iWx2z4* zIa0xq&JijKa@bZ`NPS5*r6D+L`}B`P`)Gg@Iu|1}=yMN1X(`0aASfksV}O<<-Dww+ zf^!xstE=nTlV8Z8BaKw#tstHi!cRLC40f=!ehcZ_zQ9Q9Q4Sw_mtg%)gtn%_iL!8x zHn1gHOKt#S{k;esSV z^^aiPMyi_|`QMXA7*tt=R@Y)M1UFpAcyf|_DY;?Y2Flm0r=#U4N~?>?2imfTLzB$y z@2aUHxbQ4?rUlHK2x);WruRlXgZik&TGhHuM%F&x%YyJUsebs^)ydq=0yRiWMU?Y z7(6LHVtY6c2=e(`@8a?2p1|60J#PFg#epE{w1W_W+!ck~bmN`u+Ca8UWNk$CIORRI+J1VD@=N8cjF}d}nJzZq0%jq>r9${Z1X;#1ma&Xw{2!F3neYCN~b literal 0 HcmV?d00001 diff --git a/app/examples/Misc/DBusExplorer/.project b/app/examples/Misc/DBusExplorer/.project new file mode 100644 index 00000000..994c9dc4 --- /dev/null +++ b/app/examples/Misc/DBusExplorer/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.10.90 +Title=Gambas DBus Explorer +Startup=FVersiongbXML +Icon=dbus64.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.dbus +Component=gb.settings +Component=gb.libxml +Description="DBus explorer.\n\nThis example allows to explore all applications connected to both DBus system and application buses. You can see all exported interfaces, methods, properties and events, with their signature. But you cannot use them." +Authors="Fabien Bodard\nBenoît Minisini" +TabSize=2 +Vendor=Example +Packager=1 +Tags=Example,Utility +CreateMenu=1 diff --git a/app/examples/Misc/DBusExplorer/.src/FVersiongbXML.class b/app/examples/Misc/DBusExplorer/.src/FVersiongbXML.class new file mode 100644 index 00000000..04e6dd80 --- /dev/null +++ b/app/examples/Misc/DBusExplorer/.src/FVersiongbXML.class @@ -0,0 +1,272 @@ +' Gambas class file + +Private $cArgs As New Collection +Private $cType As New Collection + +Public Sub _new() + + $cType["i"] = "Integer" + $cType["u"] = "Integer" + $cType["s"] = "String" + $cType["g"] = "String" + $cType["o"] = "DBusObject" + $cType["b"] = "Boolean" + $cType["y"] = "Byte" + $cType["v"] = "Variant" + $cType["d"] = "Float" + $cType["n"] = "Short" + $cType["q"] = "Short" + $cType["x"] = "Long" + $cType["t"] = "Long" + $cType["a{sv}"] = "Collection" + +End + +Public Sub Form_Open() + + btnRefresh_Click + 'HSplit1.Layout = [1, 1] + +End + +Public Sub ShowPathContent(sPath As String, sBus As String, sApplication As String) + + Dim xmlDoc As New XmlDocument + Dim sFullDbusPath, s, sAppPath As String + Dim hattr, hattr2, hNode, hNode2 As XmlNode + Dim i, j, k As Integer + Dim aArgs As New String[] + Dim sIntr As String + + 'Dim haArgs As New Object[] + + sIntr = DBus[sBus & sApplication]._Introspect(sPath) + Print sIntr + Try xmlDoc.FromString(sIntr) + + If Error Then Return + + For i = 0 To xmlDoc.Root.Children.Count - 1 + hNode = xmlDoc.Root.Children[i] + If hNode.Name = "interface" Then + For Each hattr In hNode.Attributes + sFullDbusPath = [sBus, sApplication, sPath, CStr(hattr.Value)].Join("|") + If Not tvDbus.Exist(sBus & "|" & sApplication & "|" & sPath) Then + tvDbus.Add(sFullDbusPath, hattr.Value) + Else + tvDbus.Add(sFullDbusPath, hattr.Value,, sBus & "|" & sApplication & "|" & sPath) + Endif + + For j = 0 To hNode.Children.Count - 1 + hNode2 = hNode.Children[j] + If hNode2.Name = "method" Or hNode2.Name = "property" Or hNode2.Name = "signal" Then + For Each hattr In hNode2.Attributes + Break + Next + Try tvDbus.Add(sFullDbusPath & "|" & hattr.Value, hattr.Value, Picture[hNode2.Name & ".png"], sFullDbusPath) + If Error Then Continue + For k = 0 To hNode2.Children.Count - 1 + If hNode2.Children[k].Name = "arg" Then + + For Each hattr2 In hNode2.Children[k].Attributes + + aArgs.Add(hattr2.Name & "=" & hattr2.Value) + + Next + + $cArgs[sFullDbusPath & "|" & hattr.Value] &= aArgs.Join() + + If k < hNode2.Children.Count - 2 Then $cArgs[sFullDbusPath & "|" & hattr.Value] &= "|" + aArgs.Clear + Endif + Next + + Endif + + Next + Next + Endif + Next + + For Each s In DBus[sBus & sApplication][sPath].Children + sAppPath = [sBus, sApplication, sPath].join("|") + + If tvDbus.Exist(sAppPath) Then + tvDbus.Add(sAppPath &/ s, s,, sAppPath) + Else + tvDbus.Add(sAppPath &/ s, s) + Endif + tvDbus.Add(sAppPath &/ s & "|child", "child",, sAppPath &/ s) + tvDbus[sAppPath &/ s].Picture = Picture["icon:/small/directory"] + + Next + +Catch + Print Error.Where; ": "; Error.Text + +End + +Public Sub lstb_activate() + + tvDbus.Clear + $cArgs.Clear + ShowPathContent("/", Last.Tag & "://", Last.current.text) + +End + +Public Sub tvDbus_Expand() + + Dim ars As String[] + + If Not tvDbus.Exist(tvDbus.item.Key & "|" & "child") Then Return + tvDbus.Remove(tvDbus.item.Key & "|" & "child") + ars = Split(tvDbus.Item.Key, "|") + ShowPathContent(ars[2], ars[0], ars[1]) + +End + +Private Sub RemoveIds(aList As String[]) + + Dim iInd As Integer + + While iInd < aList.Count + If Left(aList[iInd]) = ":" Then + aList.Remove(iInd) + Else + Inc iInd + Endif + Wend + +End + +Public Sub btnRefresh_Click() + + Dim aList As String[] + + lstbSystem.Clear + lstbSession.Clear + $cArgs.Clear + tvDbus.Clear + + aList = DBus.Session.Applications.Sort(gb.Natural) + If Not btnShowId.Value Then RemoveIds(aList) + lstbSession.List = aList + aList = DBus.System.Applications.Sort(gb.Natural) + If Not btnShowId.Value Then RemoveIds(aList) + lstbSystem.List = aList + +End + +Public Sub lstb_DblClick() + + Try Print DBus[Last.current.text]._Introspect("/") + +End + +Private Sub GetType(sType As String) As String + + If $cType.Exist(sType) Then Return $cType[sType] + + If Left(sType) = "a" Then Return GetType(Mid$(sType, 2)) & "[]" + + Return "Variant" + +End + +Public Function MakeSignature(sKey As String) As String + + Dim ars As String[] + Dim aIn As New String[] + Dim s, t As String + Dim aArg As String[] + Dim sName, sType As String + Dim iArg As Integer + Dim aOut As New String[] + + ars = Split(skey, "|") + If Not $cArgs.Exist(skey) Then + If ars.Max = 4 Then + Return ars[ars.Max] & "()" + Else + Return + Endif + Endif + + For Each s In Split($cArgs[sKey], "|") + + Inc iArg + + sName = "Arg" & CStr(iArg) + For Each t In Split(s) + aArg = Scan(t, "*=*") + Select Case aArg[0] + Case "type" + sType = GetType(aArg[1]) + Case "name" + sName = aArg[1] + End Select + Next + + If InStr(s, "=out") Then + + aOut.Add(sName & " As " & sType) + + Else + + aIn.Add(sName & " As " & sType) + + Endif + + Next + + s = ars[ars.Max] & "(" & aIn.Join(", ") & ")" + If aOut.Count Then + s &= " As " + If aOut.Count = 1 Then + s &= Scan(aOut[0], "* As *")[1] + Else + s &= "[" & aOut.Join(", ") & "]" + Endif + Endif + + Return s + +End + +Public Sub tvDbus_Select() + + MakeSignature(Last.item.key) + +End + +Public Sub tvDbus_MouseMove() + + Dim s As String + Dim ix, iy As Integer + Dim hcont As Object + + If Not tvDbus.FindAt(Mouse.x, Mouse.y) Then + + s = MakeSignature(tvDbus.item.key) + + hcont = tvDbus.Parent + ix = tvDbus.Item.X + tvDbus.Item.w / 2 + iy = tvDbus.Item.Y + + 'lblsignature.Text = s + 'lblsignature.Left = Min(iX, tvDbus.Width - lblsignature.Width) + 'lblsignature.Y = iY + 'lblsignature.Visible = True + 'lblsignature.Refresh + 'Else + 'lblsignature.Visible = False + Endif + lblsignature.Text = s + +End + +Public Sub btnShowId_Click() + + btnRefresh_Click + +End diff --git a/app/examples/Misc/DBusExplorer/.src/FVersiongbXML.form b/app/examples/Misc/DBusExplorer/.src/FVersiongbXML.form new file mode 100644 index 00000000..0bae38e2 --- /dev/null +++ b/app/examples/Misc/DBusExplorer/.src/FVersiongbXML.form @@ -0,0 +1,127 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,107,84) + Text = ("Gambas DBus Explorer") + Icon = Picture["dbus64.png"] + Arrangement = Arrange.Vertical + { Panel1 Panel + MoveScaled(3,0,102,4) + Arrangement = Arrange.Horizontal + { btnRefresh ToolButton + MoveScaled(0,0,11,4) + AutoResize = True + Text = ("Refresh") + Picture = Picture["icon:/small/refresh"] + } + { btnShowId ToolButton + MoveScaled(12,0,15,4) + AutoResize = True + Text = ("Show bus ids") + Picture = Picture["icon:/small/lamp"] + Toggle = True + } + { Panel2 Panel + MoveScaled(37,1,2,3) + Expand = True + } + { HBox1 HBox + MoveScaled(46,0,44,4) + AutoResize = True + Spacing = True + Margin = True + { Label4 Label + MoveScaled(0,0,9,4) + Font = Font["Bold"] + AutoResize = True + Text = ("Legend:") + } + { PictureBox1 PictureBox + MoveScaled(8,0,3,4) + Picture = Picture["method.png"] + AutoResize = True + Alignment = Align.Center + } + { Label1 Label + MoveScaled(11,0,8,4) + AutoResize = True + Text = ("Method") + } + { PictureBox2 PictureBox + MoveScaled(20,0,3,4) + Picture = Picture["property.png"] + AutoResize = True + Alignment = Align.Center + } + { Label2 Label + MoveScaled(25,0,8,4) + AutoResize = True + Text = ("Property") + } + { PictureBox3 PictureBox + MoveScaled(34,0,3,4) + Picture = Picture["signal.png"] + AutoResize = True + Alignment = Align.Center + } + { Label3 Label + MoveScaled(38,0,6,4) + AutoResize = True + Text = ("Signal") + } + } + } + { Separator2 Separator + MoveScaled(20,5,18,0) + } + { HSplit1 HSplit + MoveScaled(0,8,106,74) + Expand = True + { TabStrip3 TabStrip + MoveScaled(2,5,39,57) + Arrangement = Arrange.Fill + Count = 2 + Index = 0 + Text = ("Session") + { lstbSession ListBox lstb + Name = "lstbSession" + MoveScaled(2,1,30,51) + Tag = "session" + Expand = True + } + Index = 1 + Text = ("System") + { lstbSystem ListBox lstb + Name = "lstbSystem" + MoveScaled(3,1,30,50) + Tag = "system" + Expand = True + } + Index = 0 + } + { VBox1 HBox + MoveScaled(46,2,59,65) + { Separator3 Separator + MoveScaled(2,19,0,30) + } + { VBox2 VBox + MoveScaled(3,8,54,56) + Expand = True + { tvDbus TreeView + MoveScaled(7,4,33,32) + Expand = True + Border = False + } + { Separator4 Separator + MoveScaled(12,45,23,0) + } + { lblsignature TextLabel + MoveScaled(5,49,34,3) + Font = Font["Monospace,-2"] + Padding = 4 + AutoResize = True + } + } + } + } +} diff --git a/app/examples/Misc/DBusExplorer/dbus22.png b/app/examples/Misc/DBusExplorer/dbus22.png new file mode 100644 index 0000000000000000000000000000000000000000..eb4592e8a31fdfb42176a2a111be0ead97ce2618 GIT binary patch literal 894 zcmV-^1A+XBP)by}iAU3r;NZr_#=5$?#KgqR%*@{2-r3pNy1Kf_$;s~T@4~{u#>U3R#>Tq3y3EYX z-rnB2y1K{5$Hc_M?eOx--Ra2O=-KA)^7i@d@bS#v>YBaPXq~`6g0MS)u2PS<z%;YYoEeEgRqLT&-L~7$=vB~p~FIiu|9&YQIEG>m%L4kwn~b$@bmS4tIAJ~ zwqBRKe5=T^&EHm$xmJ?7yVK+F_WA$+|CPGa*yirl606@8moIFMz`B&l{!M>*pTZ{&2Ux0@&^|%8Z#OxbFY}0YgbdK~xyiV_-l4MkZz! zRyKAHBq2^NZXRAf4NWa=9ex3bkf4yT2%D&`p1y&hk&%(Hi5N&wTtZUH)Xdz%(g+Hy zq-B7jveq`Xb}%7(2S+DoIe7*K1s7L)H+K(DFOaCWkFTG=6mVwMJuK-(|p~j%9l3rO=ZDdqqVPsTW2XT+5GJ}$241`_ZP}W!pVKzxJ zFenB<*v)}{fni`)ORE9{gS?zGnA6tY(b;8WWM|vmBP-6pAS2xiQQp@-!NT0kR7z4p z926*G6DI)$4Gr}5bVb=jgoOkl0Vu#fdCF894L)8TZZ1xwu;yTAV`X7xVnh-G0F1&; UGQ-lT6aWAK07*qoM6N<$f}{cRmH+?% literal 0 HcmV?d00001 diff --git a/app/examples/Misc/DBusExplorer/dbus64.png b/app/examples/Misc/DBusExplorer/dbus64.png new file mode 100644 index 0000000000000000000000000000000000000000..49d37d24788424d12d195ed72d581cb38295d723 GIT binary patch literal 4222 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 Cs0%Pjt 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 Vv2iCa|DSXJX`*75rOlK%e*iD(30eRE literal 0 HcmV?d00001 diff --git a/app/examples/Misc/Evaluator/.lang/ca.po b/app/examples/Misc/Evaluator/.lang/ca.po new file mode 100644 index 00000000..d89ab13c --- /dev/null +++ b/app/examples/Misc/Evaluator/.lang/ca.po @@ -0,0 +1,76 @@ +# Catalan translation of Evaluator +# Copyright (C) 2000-2010 Benoît Minisini. +# This file is distributed under the same license as the Evaluator package. +# Jordi Sayol , 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Evaluator\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2010-12-17 01:08+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FEval.form:61 +msgid "45" +msgstr "-" + +#: FEval.form:67 +msgid "60" +msgstr "-" + +#: FEval.form:45 +msgid "a =" +msgstr "-" + +#: FEval.form:77 +msgid "a * Cos(Rad(x)) + b * Sin(Rad(y))" +msgstr "-" + +#: FEval.form:40 +msgid "b =" +msgstr "-" + +#: FEval.form:24 +msgid "Evaluator" +msgstr "Avaluador" + +#: FEval.form:72 +msgid "Expression" +msgstr "Expressió" + +#: .project:1 +msgid "Expression evaluation example" +msgstr "Exemple d'avaluació d'expressions" + +#: FEval.form:98 +msgid "&Quit" +msgstr "&Surt" + +#: FEval.form:82 +msgid "Result" +msgstr "Resultat" + +#: FEval.form:108 +msgid "This is an example of how to use the gb.eval expression evaluator component." +msgstr "Això és un exemple de com fer servir el component d'avaluació d'expressions gb.eval." + +#: FEval.form:92 +msgid "&Update result" +msgstr "&Actualitza el resultat" + +#: FEval.form:35 +msgid "x =" +msgstr "-" + +#: FEval.form:30 +msgid "y =" +msgstr "-" + diff --git a/app/examples/Misc/Evaluator/.lang/cs.mo b/app/examples/Misc/Evaluator/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..0383007812b48fced9a43223ae51ec0bcb079d06 GIT binary patch literal 1007 zcmZXR&ubGw6vs!czhYGo!HeSaQX1=~6||H!EnS;!4dz$#3oIf|l4-KZc4yh0jfn?A z5IiXedg#$Vpm_9TFR}lIHxUv42fx#9Y{j?x&1du8n>TOXe7iL9fx#|9SE2XN6=?Dp zeqbH&BUFQd3iuAx>%PbQ5%XuvUon5j)N?p$aPOaY z64Z6|UfL!Oz)pkbAw45~emx`I?>Xonn_7@qG z;u}TAyNpZAfSPj4m!9xcigCJ0D>9re^VakrnWPzNz*BL&ea|qNM8+a(Km%w9l>*rn zO=VwVK{C zoy#q|PD%#t!yJ=!%x;v8tr5Ab*^Hi@Ll=zKr&EQJs3@TZ;a_RzHxa-jOX&Hib9Z MiK|DcxE0;dANwNkMF0Q* literal 0 HcmV?d00001 diff --git a/app/examples/Misc/Evaluator/.lang/cs.po b/app/examples/Misc/Evaluator/.lang/cs.po new file mode 100644 index 00000000..cb920ab0 --- /dev/null +++ b/app/examples/Misc/Evaluator/.lang/cs.po @@ -0,0 +1,68 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Expression evaluation example" +msgstr "Příklad vykonávače výrazů" + +#: FEval.form:24 +msgid "Evaluator" +msgstr "-" + +#: FEval.form:30 +msgid "y =" +msgstr "-" + +#: FEval.form:35 +msgid "x =" +msgstr "-" + +#: FEval.form:40 +msgid "b =" +msgstr "-" + +#: FEval.form:45 +msgid "a =" +msgstr "-" + +#: FEval.form:61 +msgid "45" +msgstr "-" + +#: FEval.form:67 +msgid "60" +msgstr "-" + +#: FEval.form:72 +msgid "Expression" +msgstr "Výraz" + +#: FEval.form:77 +msgid "a * Cos(Rad(x)) + b * Sin(Rad(y))" +msgstr "-" + +#: FEval.form:82 +msgid "Result" +msgstr "Výsledek" + +#: FEval.form:92 +msgid "&Update result" +msgstr "&Vykonej" + +#: FEval.form:98 +msgid "&Quit" +msgstr "&Zavřít" + +#: FEval.form:108 +msgid "This is an example of how to use the gb.eval expression evaluator component." +msgstr "To je příklad, jak použít komponentu gb.eval pro vyhodnocení výrazu." diff --git a/app/examples/Misc/Evaluator/.lang/de.mo b/app/examples/Misc/Evaluator/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..7bcf2db092c5001032faafd1befaed613d28dada GIT binary patch literal 1014 zcmZXROK;Oa5XToNuTVfjNL)Y~E}@|~EfQ3fNvpbz+eURB((2kY9&*vT6Nx+#%UGN1DhVWPTeY3t)fHQRl8i8e^{-! z8PAWk)l_~E4@^&N+H83qRh+U*1-I;!yfvp-thks@^olrcAk1cBl2-&M#&*!a(E6R`W1IJ(@>)AsYBHxJRA7uVU6Kt>&h+JdHDD z?Xj}lpc8gw(6ec|Epv#?Akoh;S5wgV`wij4 B=<5Ig literal 0 HcmV?d00001 diff --git a/app/examples/Misc/Evaluator/.lang/de.po b/app/examples/Misc/Evaluator/.lang/de.po new file mode 100644 index 00000000..eb17d871 --- /dev/null +++ b/app/examples/Misc/Evaluator/.lang/de.po @@ -0,0 +1,69 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Expression evaluation example" +msgstr "Beispiel für einen Ausdrucks-Auswerter" + +#: FEval.form:24 +msgid "Evaluator" +msgstr "Auswerter" + +#: FEval.form:30 +msgid "y =" +msgstr "-" + +#: FEval.form:35 +msgid "x =" +msgstr "-" + +#: FEval.form:40 +msgid "b =" +msgstr "-" + +#: FEval.form:45 +msgid "a =" +msgstr "-" + +#: FEval.form:61 +msgid "45" +msgstr "-" + +#: FEval.form:67 +msgid "60" +msgstr "-" + +#: FEval.form:72 +msgid "Expression" +msgstr "Ausdruck" + +#: FEval.form:77 +msgid "a * Cos(Rad(x)) + b * Sin(Rad(y))" +msgstr "-" + +#: FEval.form:82 +msgid "Result" +msgstr "Resultat" + +#: FEval.form:92 +msgid "&Update result" +msgstr "Be&rechnen" + +#: FEval.form:98 +msgid "&Quit" +msgstr "&Beenden" + +#: FEval.form:108 +msgid "This is an example of how to use the gb.eval expression evaluator component." +msgstr "Dies ist ein Beispiel, wie man die gb.eval -Komponente benutzt." + diff --git a/app/examples/Misc/Evaluator/.lang/es.mo b/app/examples/Misc/Evaluator/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..ef8a90211a7cd7e33b4d741e9ca374b60a1dfdbb GIT binary patch literal 974 zcmbV}O>fgc5QY~hU*Q|6s*nIpFHu8rX{8oLCaG#AZV5O^$wwu)pl!TOT)E!JUYCRm z|9}GmA)y|*b3o$A0VEFm2>t>m?!42w?S&JgoqfE!voqs;e_fw=Ctz3MH{pBmDgHiz z2{r;xgJbYA_zFA)z6LSG8%!s`w;A7q=du0(=D?5O8SoQW1iygO;BS!gT*GDNWX2mA zZ-IZXKMnHzDFjV|w?U3y#Do;02qHw-8OtEoUje!Pqio#;OISY#x&9uAA--n(mhnf% zpCISwcVXi6S{u4EMXCuv|rzB}}#Q535r z4UL$;yCfFG;*z*OFWg-jrqbwGxT7J?lR!tJbr9DX1PNhCO61NkRHV0PpkI*DlqQPI zK#@ASMCvfpoRD;fDms~INq=TkD9|izVy7KM`#a-80TE@f2_M17@by@4tDdoHzO|uZ zZo;N@r}EfYb7{kEwQJ2rZoO%>)Na6sRu#Wv(^J=JS@ot>b?9N!uGeOtH5+culZml9 zv5bBi>rTyEajMmp+iou-H0r0audI%eJ2rVvW39_2OP;q926H;@ zFX!sDx_jKz!rXkWq9das)EE!Zgi#}NZy3rTdO*E_j1y(b-Oj32JdWdDwp47nQBV6p z)VHa)8JL{Nw`CZ_BJcD}im#V4&Lon){)bBbkxIe$&xa~;sGC4sl~5Xy+E$QCldq_! YcUUMSRbi$MGVsZPYIsML{Et|F0c}doW&i*H literal 0 HcmV?d00001 diff --git a/app/examples/Misc/Evaluator/.lang/es.po b/app/examples/Misc/Evaluator/.lang/es.po new file mode 100644 index 00000000..cea4870e --- /dev/null +++ b/app/examples/Misc/Evaluator/.lang/es.po @@ -0,0 +1,71 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: FEval.class:72 +msgid "Evaluator" +msgstr "Evaluador" + +#: FEval.class:78 +msgid "y =" +msgstr "y =" + +#: FEval.class:83 +msgid "x =" +msgstr "x =" + +#: FEval.class:88 +msgid "b =" +msgstr "b =" + +#: FEval.class:93 +msgid "a =" +msgstr "a =" + +#: FEval.class:99 +msgid "0,5" +msgstr "0,5" + +#: FEval.class:105 +msgid "1" +msgstr "1" + +#: FEval.class:111 +msgid "45" +msgstr "45" + +#: FEval.class:117 +msgid "60" +msgstr "60" + +#: FEval.class:122 +msgid "Expression" +msgstr "Expresión" + +#: FEval.class:127 +msgid "a * Cos(Rad(x)) + b * Sin(Rad(y))" +msgstr "a * Cos(Rad(x)) + b * Sin(Rad(y))" + +#: FEval.class:132 +msgid "Result" +msgstr "Resultado" + +#: FEval.class:143 +msgid "&Update result" +msgstr "&Actualizar resultado" + +#: FEval.class:149 +msgid "&Quit" +msgstr "&Salir" + +#: FEval.class:155 +msgid "This is a sample of how to use the expression evaluator." +msgstr "Este es un ejemplo de como usar el evaluador de expresiones." diff --git a/app/examples/Misc/Evaluator/.project b/app/examples/Misc/Evaluator/.project new file mode 100644 index 00000000..39a65dde --- /dev/null +++ b/app/examples/Misc/Evaluator/.project @@ -0,0 +1,18 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.0 +Title=Expression evaluation example +Startup=FEval +Icon=calculator.png +Version=3.6.2 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.eval +TabSize=2 +Translate=1 +Language=fr +ExecPath=/home/benoit/gambas/gambas.link/share/gambas/examples/Miscellaneous/Evaluator/Evaluator +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence diff --git a/app/examples/Misc/Evaluator/.src/FEval.class b/app/examples/Misc/Evaluator/.src/FEval.class new file mode 100644 index 00000000..589d4a5a --- /dev/null +++ b/app/examples/Misc/Evaluator/.src/FEval.class @@ -0,0 +1,53 @@ +' Gambas class file + +Private $hExpr As New Expression +Private $cEnv As New Collection + +Static Public Sub Main() + + Dim hForm As Form + + hForm = New FEval + hForm.Show + +End + + +Public Sub btnUpdate_Click() + + Dim cCol As New Collection + + If $hExpr.Text <> txtExpr.Text Then + $hExpr.Text = txtExpr.Text + Endif + + 'cEnv.Compare = gb.Case + + $cEnv["a"] = CFloat(Val(txtValueA.Text)) + $cEnv["b"] = CFloat(Val(txtValueB.Text)) + $cEnv["x"] = CFloat(Val(txtValueX.Text)) + $cEnv["y"] = CFloat(Val(txtValueY.Text)) + + $hExpr.Compile + $hExpr.Environment = $cEnv + + txtResult.Text = Str($hExpr.Value) + +Catch + + txtResult.Text = Error.Text + +End + +Public Sub btnClose_Click() + + Me.Close + +End + +Public Sub Form_Open() + + txtValueA.Text = Str(0.5) + txtValueB.Text = Str(1) + +End diff --git a/app/examples/Misc/Evaluator/.src/FEval.form b/app/examples/Misc/Evaluator/.src/FEval.form new file mode 100644 index 00000000..9c55dceb --- /dev/null +++ b/app/examples/Misc/Evaluator/.src/FEval.form @@ -0,0 +1,83 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(32,28,51,41) + Text = ("Evaluator") + Icon = Picture["calculator.png"] + Resizable = False + { Label4 Label + MoveScaled(27,17,7,4) + Text = ("y =") + } + { Label3 Label + MoveScaled(27,12,7,4) + Text = ("x =") + } + { Label2 Label + MoveScaled(2,17,7,4) + Text = ("b =") + } + { Label1 Label + MoveScaled(2,12,7,4) + Text = ("a =") + } + { txtValueA TextBox txtValue + Name = "txtValueA" + MoveScaled(6,12,17,4) + } + { txtValueB TextBox txtValue + Name = "txtValueB" + MoveScaled(6,17,17,4) + } + { txtValueX TextBox txtValue + Name = "txtValueX" + MoveScaled(31,12,17,4) + Text = ("45") + } + { txtValueY TextBox txtValue + Name = "txtValueY" + MoveScaled(31,17,17,4) + Text = ("60") + } + { Label6 Label + MoveScaled(2,23,13,4) + Text = ("Expression") + } + { txtExpr TextBox + MoveScaled(15,23,35,4) + Text = ("a * Cos(Rad(x)) + b * Sin(Rad(y))") + } + { Label7 Label + MoveScaled(2,28,9,4) + Text = ("Result") + } + { txtResult TextBox + MoveScaled(15,28,35,4) + ReadOnly = True + } + { btnUpdate Button + MoveScaled(23,36,16,4) + Text = ("&Update result") + Default = True + } + { btnClose Button + MoveScaled(40,36,10,4) + Text = ("&Quit") + Cancel = True + } + { TextLabel1 TextLabel + MoveScaled(1,1,49,7) + Font = Font["Bold,Italic"] + Background = Color.SelectedBackground + Foreground = Color.SelectedForeground + Padding = 6 + Text = ("This is an example of how to use the gb.eval expression evaluator component.") + Alignment = Align.Normal + } + { Separator1 Separator + MoveScaled(1,9,49,1) + } + { Separator2 Separator + MoveScaled(0,34,50,1) + } +} diff --git a/app/examples/Misc/Evaluator/calculator.png b/app/examples/Misc/Evaluator/calculator.png new file mode 100644 index 0000000000000000000000000000000000000000..ed5c2b73885e0daf93305bc16dfcd8fc1c664899 GIT binary patch literal 3612 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

" Then + If Not Trim(aLine[iLine - 1]) Then + If iLine < aLine.Max And If GetIndent(aLine[iLine + 1]) >= hMarkupList.iIndent Then + bJustList = False + Endif + aResult[aResult.Max - 1] = "

\n" & aResult[aResult.Max - 1] + Endif + aResult.Remove(aResult.Max) + Else + GoSub CLOSE_PARA + aResult.Add(Space$(iCurrentIndent) & "
    ") + Endif + iCurrentIndent = hMarkupList.iIndent + aResult.Add(Space$(iCurrentIndent) & "
  1. ") + sLine = Mid$(sLine, 3) + Endif + + Endif + + ' Blockquote again! + + I = iBlockQuote + Do + If Left(sLine) <> ">" Then Break + sCar = Mid$(sLine, 2, 1) + If sCar <> " " And If sCar <> gb.Tab Then Break + Inc I + sLine = LTrim(Mid$(sLine, 3)) + Loop + + If I > iBlockQuote Then + While I > iBlockQuote + aResult.Add("
    ") + Inc iBlockQuote + Wend + Else If I < iBlockQuote Then + While I < iBlockQuote + aResult.Add("
    ") + Dec iBlockQuote + Wend + Endif + + If sLine = "[[" Or If sLine Begins "[[ " Then + + GoSub CLOSE_PARA + iIndent = GetIndent(sLine) + GoSub CLOSE_LIST + GoSub CLOSE_CODE + + $aTable.Push("[[") + sLine = Trim(Mid$(sLine, 3)) + $aTableClass.Push(sLine) + $aTablePos.Push(aResult.Count) + If sLine Then + If sLine Begins "code " Then + Inc Verbatim + Else + aCommand = $hMarkdown.Enter(sLine) + If aCommand Then aResult.Insert(aCommand) + Endif + Endif + 'aResult.Add("") + bIgnorePar = False + bInsidePar = False + Continue + + Endif + + If $aTable.Count Then + + If sLine = "]]" Then + + GoSub CLOSE_PARA + iIndent = GetIndent(sLine) + GoSub CLOSE_LIST + GoSub CLOSE_CODE + + sTable = $aTable.Pop() + sClass = $aTableClass.Pop() + iPos = $aTablePos.Pop() + + aCommand = aResult.Extract(iPos, -1) + + If Left(sClass) <> " " Then + aResult.Add("
    ") + If sClass Begins "code " Then + aTemp = ProcessCode(sClass, aCommand) + Else + aTemp = $hMarkdown.Process(sClass, aCommand) + Endif + If aTemp Then aCommand = aTemp + aResult.Insert(aCommand) + aResult.Add("
    ") + Else + sClass = Trim(sClass) + If Not sClass Then sClass = "table" + aResult.Add("") + If aCommand.Count And If aCommand[0] = "" Then + aCommand.Remove(0) + Else + aResult.Add("") + aResult.Add("
    ") + Endif + aResult.Insert(aCommand) + aResult.Add("
    ") + Endif + + If sClass Begins "code " Then + Dec Verbatim + Else If sClass Then + aCommand = $hMarkdown.Leave(sClass) + If aCommand Then aResult.Insert(aCommand) + Endif + + Continue + + Else If sLine = "--" Then + + GoSub CLOSE_PARA + iIndent = GetIndent(sLine) + GoSub CLOSE_LIST + GoSub CLOSE_CODE + + sTable = $aTable[$aTable.Max] + If sTable = "[[" Then + aResult.Add("") + Else + aResult.Add("") + Endif + bIgnorePar = True + bInsidePar = True + $aTableClass[$aTableClass.Max] = " " & Trim($aTableClass[$aTableClass.Max]) + Continue + + Else If sLine = "==" Then + + GoSub CLOSE_PARA + iIndent = GetIndent(sLine) + GoSub CLOSE_LIST + GoSub CLOSE_CODE + + sTable = $aTable[$aTable.Max] + If sTable = "[[" Then + aResult.Add("") + Else + aResult.Add("") + Endif + aResult.Add("") + $aTable[$aTable.Max] = "==" + $aTableClass[$aTableClass.Max] = " " & Trim($aTableClass[$aTableClass.Max]) + bIgnorePar = True + bInsidePar = True + Continue + + Endif + + Endif + + If sLine Begins "==" And If sLine = String$(Len(sLine), "=") Then + sLine = aResult[aResult.Max] + If sLine Not Begins "" Then + sLine = Mid$(sLine, 4) + bInsidePar = False + Endif + GoSub CLOSE_PARA + aResult[aResult.Max] = "

    " & sLine & "

    " + Endif + Continue + Endif + + If sLine Begins "--" And If sLine = String$(Len(sLine), "-") And If bInsidePar Then + sLine = Trim(aResult[aResult.Max]) + If sLine Then + If sLine Not Begins "" Then + sLine = Mid$(sLine, 4) + bInsidePar = False + Endif + GoSub CLOSE_PARA + aResult[aResult.Max] = "

    " & sLine & "

    " + Endif + Continue + Endif + Endif + + ' Code + + If EnableCode >= 0 Then + If sLine Begins " " Or If sLine Begins gb.Tab Then + + If Left(sLine) = gb.Tab Then + sLine = Mid$(sLine, 2) + Else + sLine = Mid$(sLine, 5) + Endif + If sLine = "----" Then + sLine = "
    " + Else + sLine = Html$(sLine) + Endif + If Not bCode Then + GoSub CLOSE_PARA + bCode = True + sLine = "
    " & sLine
    +        Endif
    +        aResult.Add(sLine)
    +        Continue
    +        
    +      Endif
    +    Endif
    +    
    +    GoSub CLOSE_CODE
    +  
    +    ' Title
    +    
    +    If Left(sLine) = "#" Then
    +      I = InStr(sLine, " ")
    +      If I <= 7 Then
    +        Dec I
    +        If Left(sLine, I) = String$(I, "#") Then
    +          sLine = Mid$(sLine, I + 2)
    +          While sLine Ends "#"
    +            sLine = Left(sLine, -1)
    +          Wend
    +          sLine = RTrim(sLine)
    +          If Left(sLine) = "[" And If Right(sLine) = "]" Then
    +            sLine = ConvertLine(Mid$(sLine, 2, -1))
    +            $aIndex.Add(String$(I - 1, "  ") & "- [" & sLine & "] (#t" & CStr($aIndex.Count + 1) & ")")
    +            sLine = "" & "" & sLine & ""
    +          Else
    +            sLine = "" & ConvertLine(sLine) & ""
    +          Endif
    +          GoSub CLOSE_PARA
    +          aResult.Add(sLine)
    +          Continue
    +        Endif
    +      Endif
    +    Endif
    +      
    +    If Trim(sLine) Then
    +      If Not bInsidePar And If LTrim(sLine) Not Begins "" & LTrim(sLine)
    +      bInsidePar = True
    +      bAddPar = False
    +    Endif
    +    
    +    aResult.Add(sLine)
    +    
    +  Wend
    +  
    +  GoSub CLOSE_PARA
    +  GoSub CLOSE_CODE
    +  GoSub CLOSE_BLOCKQUOTE
    +  iIndent = 0
    +  GoSub CLOSE_LIST
    +  'If $aMarkup.Count Then Error.Raise("Missing markup: " & $aMarkup[$aMarkup.Max])
    +  
    +  If $aIndex.Count And If iIndexPos >= 0 Then
    +    iIndent = GetIndent($aIndex[0])
    +    For I = 1 To $aIndex.Max
    +      iIndent = Min(iIndent, GetIndent($aIndex[I]))
    +    Next
    +    If iIndent Then
    +      For I = 0 To $aIndex.Max
    +        $aIndex[I] = Mid$($aIndex[I], iIndent + 1)
    +      Next
    +    Endif
    +    aResult[iIndexPos] = "
    \n" & ConvertMarkup($aIndex) & "
    \n" + Endif + + $bComment = bSaveComment + $aMarkup = aSaveMarkup + $aTable = aSaveTable + $aTableClass = aSaveTableClass + $aTablePos = aSaveTablePos + Return aResult.Join("\n") + +CLOSE_CODE: + + If bCode Then + aResult.Add("
    ") + bCode = False + Endif + Return + +CLOSE_BLOCKQUOTE: + + While iBlockQuote + aResult.Add("") + Dec iBlockQuote + Wend + Return + +CLOSE_LIST: + + While iIndent < iCurrentIndent + GoSub CLOSE_PARA + GoSub CLOSE_CODE + aResult.Add(Space$(iCurrentIndent) & "
  2. ") + bJustList = False + aResult.Add(Space$(aList[aList.Max].iIndent) & "") + aList.Remove(aList.Max) + If aList.Count Then + iCurrentIndent = aList[aList.Max].iIndent + Else + iCurrentIndent = 0 + Endif + Wend + Return + +CLOSE_PARA: + + If bInsidePar Then + If Not bIgnorePar Then aResult[aResult.Max] &= "

    " + 'aResult.Add("") + bInsidePar = False + bIgnorePar = False + 'Else If iLine > 0 And If aResult[aResult.Max] Then + ' 'aResult.Add("") + Endif + Return + +'Catch + +' Error.Raise("Line " & CStr(iLine + 1) & ": " & Error.Text) + +End + + +Private Sub ConvertLine(sLine As String) As String + + Dim sResult As String + Dim I, L As Integer + Dim sCar As String + Dim I1, I2 As Integer + Dim sPattern As String + Dim bCode As Boolean + Dim bEmph As Boolean + Dim bStrong As Boolean + Dim sText, sTitle, sLink As String + Dim bBlank As Boolean + Dim bUnderline As Boolean + Dim bLimitBefore, bLimitAfter As Boolean + Dim iLen As Integer + + iLen = String.Len(sLine) + +MAIN_LOOP: + + If I >= iLen Then + If bEmph Then + sResult &= "
    " + Else If bStrong Then + sResult &= "" + Endif + If bCode Then sResult &= "" + Return sResult + Endif + + GoSub NEXT_CAR + + If sCar = "\\" Then + If I = iLen Then + sResult &= "
    " + Else + GoSub NEXT_CAR + sResult &= Html(sCar) + Endif + Goto MAIN_LOOP + Endif + + If sCar = "<" Then Goto ENTER_MARKUP + + 'If $aMarkup.Count = 0 Then + + If sCar = "&" Then Goto ENTER_AMPERSAND + + If sCar = "[" And If String.Mid$(sLine, I + 1, 1) <> " " Then Goto ENTER_LINK + + 'Endif + + If I = 1 Or If IsWordLimit(String.Mid$(sLine, I - 1, 1)) Then + bLimitBefore = True + Else + bLimitBefore = False + Endif + + If IsWordLimit(String.Mid$(sLine, I + 1, 1)) Then + bLimitAfter = True + Else + bLimitAfter = False + Endif + + If bLimitBefore Or If bLimitAfter Then + If sCar = "`" And If Not bCode Then Goto ENTER_CODE + If sCar = "'" Then Goto ENTER_LIGHT_CODE + If sCar = "*" Then Goto ENTER_STAR + If sCar = "~" Then Goto ENTER_UNDERLINE + Endif + + If sCar = ">" Then + sCar = ">" + Endif + + sResult &= sCar + + Goto MAIN_LOOP + +NEXT_CAR: + + If I >= iLen Then + I = iLen + 1 + sCar = "" + Else + Inc I + sCar = String.Mid$(sLine, I, 1) + Endif + Return + +LOOK_CAR: + + sCar = String.Mid$(sLine, I + 1, 1) + Return + +ENTER_MARKUP: + + If String.Mid$(sLine, I, 4) = " "; sData + Print #$hStream, sData + Flush #$hStream + Return GetLine(bMulti) + +End + +Public Sub Print(Optional sData As String) + + If Not Me.Connected Then Return + + If _Debug Then Error "--> "; sData + Print #$hStream, sData + Flush #$hStream + +End + + +Public Function GetLine(Optional bMulti As Boolean) As String + + Dim sLine As String + Dim sResponse As String + Dim iPos As Integer + + If Not Me.Connected Then Return + + bMulti = True + + If bMulti Then + + Do + Line Input #$hStream, sLine + sResponse &= sLine & "\n" + If sLine Like "??? *" Then Break + If $hStream.EndOfFile Then Break + Loop + + Else + + Line Input #$hStream, sLine + sResponse = sLine + + Endif + + LastAnswer = sLine + iPos = InStr(sLine, " ") + If iPos = 0 Then + LastCode = "" + Else + LastCode = Left(sLine, iPos - 1) + Endif + + If _Debug Then Error sResponse + Return sResponse + +End + +Private Function Stream_Read() As Stream + + Return $hStream + +End + +Private Sub Stream_Write(Value As Stream) + + $hStream = Value + +End + +Private Function Connected_Read() As Boolean + +End + +Public Sub PrintHeader(sHeader As String, sStr As String) + + If Not sStr Or If IsAscii(sStr) Then + If sStr Not Begins "=?utf-8?q?" Or If sStr Not Ends "?=" Then + Print(sHeader & ": " & sStr) + Return + Endif + Endif + + Encode.PrintQuotedHeader($hStream, sHeader, sStr, _Debug) + +End + +Private Function Client_Read() As SmtpClient + + Return $hClient + +End diff --git a/comp/src/gb.net.smtp/.src/SslSession.class b/comp/src/gb.net.smtp/.src/SslSession.class new file mode 100644 index 00000000..4f5368ea --- /dev/null +++ b/comp/src/gb.net.smtp/.src/SslSession.class @@ -0,0 +1,76 @@ +' Gambas class file + +Inherits SmtpSession + +Property Read Connected As Boolean + +Private $hProcess As Process +Private $sError As String +Private $bConnected As Boolean +Private $bDisconnect As Boolean + +Public Sub Connect(hClient As SmtpClient, sHost As String, iPort As Integer) + + Dim aExec As String[] + + If Not sHost Then sHost = "localhost" + If iPort = 0 Then iPort = 465 + + Super.Connect(hClient, sHost, iPort) + + aExec = [SmtpSession.GetOpenSSLPath(), "s_client", "-quiet", "-connect", sHost & ":" & iPort] + If Me._Debug Then Error "gb.net.smtp: running: "; aExec.Join(" ") + + $hProcess = Exec aExec For Read Write As "OpenSSL" + $hProcess.Blocking = True + $hProcess.EndOfLine = gb.Windows + + $bConnected = False + Repeat + Wait 0.1 + If Not $hProcess Then + If $sError Then + Error.Raise(Trim(Split($sError, "\n")[0])) + Else + Error.Raise("SSL session has stopped unexpectedly") + Endif + Endif + Until $bConnected + + Me.Stream = $hProcess + +End + +Public Sub Disconnect() + + $bDisconnect = True + $hProcess.Kill() + +End + +Public Sub OpenSSL_Error(Text As String) + + If Me._Debug Then Error "openssl: "; Quote(Text) + $sError &= Text + +End + +Public Sub OpenSSL_Read() + + $bConnected = True + +End + + +Public Sub OpenSSL_Kill() + + Super.Disconnect + $hProcess = Null + +End + +Private Function Connected_Read() As Boolean + + If $hProcess And If $hProcess.State = Process.Running Then Return True + +End diff --git a/comp/src/gb.net.smtp/.src/TcpSession.class b/comp/src/gb.net.smtp/.src/TcpSession.class new file mode 100644 index 00000000..c7d4be85 --- /dev/null +++ b/comp/src/gb.net.smtp/.src/TcpSession.class @@ -0,0 +1,89 @@ +' Gambas class file + +Inherits SmtpSession + +Property Read Connected As Boolean + +Private $hSocket As New Socket + +Public Sub _new() + + 'Wait 10 seconds before timing out + $hSocket.Timeout = 10000 + $hSocket.EndOfLine = gb.Windows + +End + +Public Sub Client_Ready() + + 'Debug "Connected to remote host " & sSocket.Path + +End + +Public Sub Client_Closed() + + 'Debug "Connection Closed by foreign host." + +End + +Public Sub Client_Found() + + 'Debug "Host Found. Connecting..." + +End + +Public Sub Connect(hClient As SmtpClient, sHost As String, iPort As Integer) + + If Not sHost Then sHost = "localhost" + If iPort = 0 Then iPort = 25 + + Super.Connect(hClient, sHost, iPort) + + $hSocket.Timeout = 10000 + $hSocket.Connect(sHost, iPort) + $hSocket.Blocking = True + Me.Stream = $hSocket + + Do + 'Print $hSocket.Status + If $hSocket.Status = Net.Connected Or If $hSocket.Status <= 0 Then Break + Wait 0.1 + Loop + + If $hSocket.Status < 0 Then + + Select Case $hSocket.Status + + Case Net.CannotCreateSocket + Error.Raise("Cannot create socket") + + Case Net.HostNotFound + Error.Raise("Host not Found") + + Case Net.ConnectionRefused + Error.Raise("Connection Refused") + + Case Net.CannotRead + Error.Raise("Read error") + + Case Net.CannotWrite + Error.Raise("Write error") + + End Select + + Endif + +End + +Public Sub Disconnect() + + Try $hSocket.Close + Super.Disconnect + +End + +Private Function Connected_Read() As Boolean + + Return $hSocket.Status = Net.Connected + +End diff --git a/comp/src/gb.net.smtp/.src/TlsSession.class b/comp/src/gb.net.smtp/.src/TlsSession.class new file mode 100644 index 00000000..d90a8c08 --- /dev/null +++ b/comp/src/gb.net.smtp/.src/TlsSession.class @@ -0,0 +1,77 @@ +' Gambas class file + +Inherits SmtpSession + +Property Read Connected As Boolean + +Private $hProcess As Process +Private $bConnected As Boolean +Private $sError As String + +Public Sub Connect(hClient As SmtpClient, sHost As String, iPort As Integer) + + Dim aExec As String[] + Dim I As Integer + + If Not sHost Then sHost = "localhost" + If iPort = 0 Then iPort = 587 + + Super.Connect(hClient, sHost, iPort) + + aExec = [SmtpSession.GetOpenSSLPath(), "s_client", "-quiet", "-starttls", "smtp", "-connect", sHost & ":" & iPort] + If Me._Debug Then Error "gb.net.smtp: running: "; aExec.Join(" ") + + $hProcess = Exec aExec For Read Write As "OpenSSL" + $hProcess.Blocking = True + $hProcess.EndOfLine = gb.Windows + + $bConnected = False + For I = 1 To 10 + Wait 0.1 + If Not $hProcess Then + If $sError Then + Error.Raise(Trim(Split($sError, "\n")[0])) + Else + Error.Raise("TLS session has stopped unexpectedly") + Endif + Endif + Next + + $bConnected = True + + Me.Stream = $hProcess + +End + +Public Sub Disconnect() + + $hProcess.Kill() + +End + +Public Sub OpenSSL_Error(Text As String) + + If Me._Debug Then Error "openssl: "; Quote(Text) + $sError &= Text + +End + +Public Sub OpenSSL_Read() + + $bConnected = True + +End + + +Public Sub OpenSSL_Kill() + + Super.Disconnect + $hProcess = Null + +End + +Private Function Connected_Read() As Boolean + + If $hProcess And If $hProcess.State = Process.Running Then Return True + +End diff --git a/comp/src/gb.report/.component b/comp/src/gb.report/.component new file mode 100644 index 00000000..b0a1ee97 --- /dev/null +++ b/comp/src/gb.report/.component @@ -0,0 +1,7 @@ +[Component] +Key=gb.report +Version=3.10.90 +State=3 +Authors=Fabien Bodard +Needs=Form,ImageIO +Requires=gb.form diff --git a/comp/src/gb.report/.connection/Connection1.connection b/comp/src/gb.report/.connection/Connection1.connection new file mode 100644 index 00000000..a918b624 --- /dev/null +++ b/comp/src/gb.report/.connection/Connection1.connection @@ -0,0 +1,10 @@ +[Connection] +Type="mysql" +Host="localhost" +RememberPassword=False +IgnoreCharset=False +Database="test" +User="root" +DisplayMetadata=False +Requests=["select distinct firstname from test"] + diff --git a/comp/src/gb.report/.connection/Connection2.connection b/comp/src/gb.report/.connection/Connection2.connection new file mode 100644 index 00000000..cf224bc1 --- /dev/null +++ b/comp/src/gb.report/.connection/Connection2.connection @@ -0,0 +1,10 @@ +[Connection] +Type="mysql" +Host="localhost" +User="root" +RememberPassword=False +IgnoreCharset=False +Database="Laurux01" +DisplayMetadata=False +Requests=[""] + diff --git a/comp/src/gb.report/.connection/MainConn.connection b/comp/src/gb.report/.connection/MainConn.connection new file mode 100644 index 00000000..c6f0d11e --- /dev/null +++ b/comp/src/gb.report/.connection/MainConn.connection @@ -0,0 +1,9 @@ +[Connection] +Type="sqlite" +Path="/home/fabien/Documents/Gestion/Bases" +RememberPassword=False +IgnoreCharset=False +DisplayMetadata=False +Database="gestion" +Requests=[""] + diff --git a/comp/src/gb.report/.dir_icon.png b/comp/src/gb.report/.dir_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..04f17072df13d7cb5c6a2530f1393161a436fc0c GIT binary patch literal 1571 zcmV+;2Hg3HP)4Z zatrxjnD5M)neR9MxeBEe@pxSEG279jM|)GL)VxtDIDGi<#}+&uk7JtV7GMc%X`8QYs+0Ho(rU z8<{}`$O^wAm8Fn{fgnK^07|I@;JRilz=pyrP)eYbDvqeAT7_TwznT~3Kq&~=g?>zv zjRVZwO!4+#=D2nvO**F$4u{y@+s5<5?F{u`B_Z66T@oZufYg!UWn5FR1dFwxs$f& z9js(CSaugTW**^YQ+WAhJU7GTf3NWUg$%D9?B&HhR^8yG(Yvl&{#Wwr0z6vE@XG5a z=^6esc5^F2(-8=ilC(y1IQcwN|Gb5zIfRTQ(uq6#>^Ba32m5*WV2Q+oEFs$>Xz5sn zj%f&Nvs#&k3XEO2$mXGaXu5&tdKjhvp&>L4%Wg(D0`%>Q(%un6GYssYO(c}#cNgbz zle4_|*59wrvuYcuPF3&BJ&acj+K%nV5x)A^%wRn#YCNJ}; zFMXSbo`Ws@V$gaUbq#=P*Kf0U=MJCv%n(5XethT!oV-u0y_sCzW$cgd^XIAC2m~O| z4U>H@eV4_B8KT|Wxt~~I@}iF}2if1N6BLq0RKy#s0EZ3?6Kf3?RnV|Q5w`B!LogIU(+rj#BzgBw@A1~949mWb5TcwKD&V>WO6#m?CnQ9T+ZXm(!C&X^LhUD_e*>@6Jh`E zE_Oc=rpa1o5iTz;)84FOhiry+bXT2Rm8YC4UOKyaBRt*HijO021XVd#AI3p;qSr-fht z^0!P+FEBjxBp-gX$Y5XBnw<(E@O>YE0|yRZ7zR=nj$@@1rfDLjWMpJyJdsGut_c9J z>xnSeZ>8z(j`6|OIi_ds@||x*8?J%My&j9j$}(5li4!L#&YwU3GLUEp(9&eGvnR~p zzyOJ*O}wzLgZ{qmhO$-ytgNh*)d&O%yW`lgV-u%No%$wltJJ39i)i<@F#U(0W4Ql5 z`}b^GcU~*ab=|V;j^i*gGBR=c^y#YL4HWi>7I0vsuobJv(vk+_}2JYXA(xsC&{q4nXPUn@lD-e*E~a z#>U2e1T3u8rV1dJ%l+Q~B^8exIr5Y7@$nx3D~;Ne0pjua{~WlUjgOBXTQ4}ke*oY4 V*+a}sDck@6002ovPDHLkV1oU`2~Yq4 literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.directory b/comp/src/gb.report/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/comp/src/gb.report/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/comp/src/gb.report/.hidden/control/reportdrawingarea.png b/comp/src/gb.report/.hidden/control/reportdrawingarea.png new file mode 100644 index 0000000000000000000000000000000000000000..cd577a0649a337c0b3ab44729e985cd474d3909c GIT binary patch literal 621 zcmV-z0+RiSP)7yFc8&Z>5i}a$fLzfB%Y# z|M>WJ`iM#4wMf#onsa2NZe+l2f8uw4|GvIR;kI|2ho5d=zixl%d4KK zaACx6fBb%b|FyN&|JJAfr=YpDtZr=Ocz^$af#mrAf876!#pKj< zfB&ni*Yy9cilEMNfB%4h|K{e~_5X>>>#mET#gU=^p`ri5!Snq8fZYGN?EjzT|K|Ar z;P?Nj=l{p>|CQqZ_Wl3T^Z$q5|MmR;_x=CL@c*3T|Dfgnxa|Ms`2U_^$~pi50QN~l zK~y-)V_+Z%81Vx7DPUqo02XWtSlJMOov;E90tz^}xbZ9C;pOARr+{C8msb#<0wG>r zUSSdJ3Pi=kB_ySQ3V5ZlDUgwslb4rQP*egcP*%ZIpsEIxmRHx%)Y1m3(7{k3qYILk z*VETDFfiohH8Mt5APbf^F$F0wGw0>CKv7_6Wo=^%0(SQH4h9AWj=a219H_DB>;eL= z1`yx|bcH*r0uQhPPpASfpaO3n6a~H@c|U(B2nYm%AQT0`AudQD6ibQp#IC@u00000NkvXX Hu0mjf^C?oX literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.hidden/control/reportgridview.png b/comp/src/gb.report/.hidden/control/reportgridview.png new file mode 100644 index 0000000000000000000000000000000000000000..32872a01a296ff50965b0c8f4cb4d0f25053e216 GIT binary patch literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk@BpAX3RW*QAkf)1dh{fsToP-1X5fU;I5_}d4 z+zUVO83-CQG%jFrdvqtDU#H}PT<2wvozfcN2M!f9G&Vj|=(^z;!pg_P!~Q1l$;8SK kj+24$J-?b%;swrBF*rY$xn^-Uc0R}!Pgg&ebxsLQ0J@+nH2?qr literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.hidden/control/reporthbox.png b/comp/src/gb.report/.hidden/control/reporthbox.png new file mode 100644 index 0000000000000000000000000000000000000000..792f8fda3752fec145613e3e18628bb6a932a923 GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH3?%tPCZz)@o&cW^S0Mc#2pSsxGccU9c-IW% zGnEAS1v4;|O+IS@$w9j<+{{sLF0Hu^Ffl>dZeFgADM9Le-N~)I({*yoiaDfNF z0|W2@Po+}nOSWU9ltQ6UnktO}wT4Hj4RfV=Rqgu((RCfuG@k>;jMAKGJ2szx{S&@0 z`763I)8w7&7{wI#qixI@M`*cgW9Ey5YN8Z7o4ITKnL}!vaJZ55O(7l%6L-GIk7GS7 zG(NK}fa#z01DLQD1By}@)TU;M7c)4{H9WtMbXz<1&Mb`XM{`{OqT;dpzQ^H6C%5~P zw50FS^jw^l1)F$%J>gWCMSc6&)-jqfFtuG*wzi4poz6BL)4vPIlLdwr>`%z zObbIaX5tTNkKe=Y-Aiu|=r8t>?~HILJ&vZSZM6c-`ba61xrAAlCfD3d!KfuuUrQt~ zi*6Jj0kA9zy2<;+Pk2A^G6(jh(0lthbgYYsYZv%NXMm$-0!`PpRHfA80F+Wla0Rb6 z&$2&=8*{Mq3=?iQSt9~~&*#JPnPhz?AuYtsrAdB%>je&V*Rzn->F&Ers3}FnXQ4E0 zORdU)Qi?*LmKmd!8>)c;mB<3{}Oy>abTWbIe(b$y}UR(GM9UUF~ z@<$2gV$Tq2Y$eyw$CT$s)76%`5rDCgj^$7vK6je-`8)UuHks~yeD1FSlsF{@ObpS` zMZ;&A99<3*dqeP42P3EN(9+hz+rQpjj`AHzWf1BbPV}4P>vmzStk8QUfD(967vN5_ z@M)7nUiZ*N!>aLoHIwsT2LsgQhz^y)fOgr$|62(Gj=Pw_KzoM)fhlM^3m;m$4Gr8gHO5i zsw>2l$1AB(ULT)9&(AQ_@<;s3I$Bc*=QW287Wm0%cw4`3)Gf1M9id@x@4)xScGb~V z-$3@MB>SUro;4DzIz>V?I=Qw0xu52_)iA-kSAu+OH}B~5nx;|e!UT|tAl&F5N8iij zt8cNm_%@T{DPmun!pnIyCpw7k>tS+0u%}k|&EHSs1hlG`ka!N_IZ$rg!plz<6Q31i&yjC&x$#RBuL_PUU{vpF#4RFT0bBWOAi~;Yt^5@~Q5_d+9xLvllQrkE4vh&e~`(5Z9n* zPYAkdg0004qNklj!v1=+jk{TC+yZAzak0&(z4UTfjL&qHaB} zMkd|K2N6mH>vq~GA_zC`Zsaz?j%9C4dl+vl4^u{nS$0Piu-_Pv(xc|DVl~n_lb;*| zJn`}a+)fmLB)d-aXAr9N7%~yw-57a_kg>^SbPR_Z*-H&!+9n%Uz@9x|1L5Ab#a5op zMt(<#BEp)(fScMw%A8Ijd=PN}#1xPxU}~^{aOvbQJ_?Fhv?ek4U7M?DSAs)xyp4rIBV8FwCF#g2v z{)ZEvn3cM4u+`S{TWhyu^mFh0lU4AEiysK;4-{JJwSW6@pDj?Y_i2*a3kRSX44$rj JF6*2UngH&LBuM}O literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.hidden/control/reportpagebreak.png b/comp/src/gb.report/.hidden/control/reportpagebreak.png new file mode 100644 index 0000000000000000000000000000000000000000..3ca2f103d56721395579e192a2543a595529095c GIT binary patch literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4Ush%#5As)xyUNPiqFyLu@XfIT7 z`FBOVO}}3d%jL5NT|aQXosh$O^16suPsC3-ZobV74TrCY87M9J=D15v$m4o3t%*J6Eu$YKgkNTkipZ{&t;uc GLK6VR!8YRn literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.hidden/control/reportpanel.png b/comp/src/gb.report/.hidden/control/reportpanel.png new file mode 100644 index 0000000000000000000000000000000000000000..fce67f7d052048a841374079576e350d53dc010f GIT binary patch literal 154 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 UirGzFm;e9(07*qoM6N<$g7*PU$p8QV literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.hidden/control/reportvbox.png b/comp/src/gb.report/.hidden/control/reportvbox.png new file mode 100644 index 0000000000000000000000000000000000000000..bc04bb7f24319fc37979abc9713c1a8f99d569b0 GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvp#Yx{S0MfW|NjFA4m310{0AZi zhBM|fJ_1EqN`m}?fi%PB$!W8IRF|iVV~EE2r4tSL8Web3yE}Kw-e^3+T*1tyzo7H` z{;vHCA}%;SPFJY?`_p8~v#5zmp-d0F7~H=0HY{9R(xkz7=P{e8g6)-T)?+yqj7*yz zo+xlxr~dRncI0};f@7y-rYlRh?bUd2v&)fL%=x51!p_1E>J2?*eE;kD{?uEjG>Crq hvt{Q>#_eyn-(qa*xBJd9EhHc0I!{+Wmvv4FO#ng6T^9fV literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.hidden/control/reportview.png b/comp/src/gb.report/.hidden/control/reportview.png new file mode 100644 index 0000000000000000000000000000000000000000..8f07053ba0d28f7cbd0e1f5c778234cfce9b1e17 GIT binary patch literal 732 zcmV<20wev2P)U3Lz`(w~zVGer@$m5S@$vKV@$>TX^z!ob^YicS?*9J%_4W1j^z`=i z_4fAm_xJbs`1$$x_^Yd{>gw$G_xSkt_xbtxqN1YM*xTjh=kM?E_4W4l_V@Vt`HhW@ zva+(5mzTf4#Ldmo*x20O-r?ZjCdX)YR+i>+bIEnwpwbRaNBVFMdy)6><} z)xW>LwY9ajwziCnjF6C!oSdB2*4FCk>bSVL(9qD$&CO?LXHrs9P*6~2W@fv)yR@{l zK|w@MPgT9Wy``n4-rnAuo131Vpy1%*-{0Ymj*i^i-1qkO%*@RE{QUd-``z8$*x1BGx zxR?36(zgJB-26*r1%3bJ!>;i}0ga&bi=c-O#r6wtgMSAh$VtrzmZ&}^6GnY?bBd&o z5NAIiQ*$*Y)4%}eYHgz<2SA1mHh7g+gw_#uB8ACG#-YZh&CgkHp@<-)%&LXKmj{&O zAAcark7l9mlZUewC(lMxKO2}_>wY~69;DWm=Sdy06aAw3|MMmM;rS&1dk$II>AB&s zwtFZ4fLtI2BR1=Hh#;Ig4}2$5{Om{Vq#f)_w`Fd{v%X`@c}J=XQ+44A4RHRw<>4o( z6+d6$UB@UIky$e2 zKW)j656$|85oV1r+a>noIqP;*V~}Ul3d7~0FGU!}0xT|yC_(HbmaU4sOn9f7lhXjn z{e&Djm)JOYd%JhF=D&85djpMHoZa7_5Mfrem$t&ne#voZg)hm8TI%(DI)fH}%)@R% zZjqRsNmi_*-9*?6Qh=J`Iz3Y0U54Zlf=#@5LVPc;F8Pb3M2q9Cp5c(bxAuoYeWCLj zZD-_W_w0t2#1#v|g7@r%oqu>Z*I2s62~+6Op>l=_zQWX09CjcvCD_}Z>q0nW5FvTi z9?%Hbk(iY7xCaSxArzDl4T6qkxaaIEy_GjJj3hgB zD6e~9MM_--v7GrQ_h{MwUCznht!1e6oM+PY33BdkRd&4k?EW}HxR3DuBBb&HoehD- z5pv=Bq9V=P&X$UT_cZ)yJVEqtYiH?wlDL?8YJ7tYjfo#lu_d+MMP!=^;BiuK29EyS zX{^)yRQL#$?SkopIXuT)nF(CNf6pd^&xpq%n~T^z;nrZ(83Id>?T6fzXkVkoJLPhJ zf_5_eGNL>X3icE3xU6^XRD!B#Tps%)%w9?H}ImywtOS zBk7ZWAsbe>ey%dg}CcoL5AGI?*cv%+8wRG6b#KpWkey@ zvQ+Rb6AMmCflr|x_@BdZ7d{MGIC1C+_oqri&LKgPpdl(XKe<<}6-Wf*+Z2zEx_zuAp8 z`QIN4=;SA}t2Pgryjvz=qTdl!kC;$#mkm4HkT(xB7uUc*8?3f7rVMaT_6}!26AE_(dg&-h(kbLUd#hTNSQ8b-ka>6BfrIdg=;VLgpRbh_wsiw8D@Ptn2RymWG~MNTgb z;Q{~OIQ#lY^So^m#sotNV{Mw#{JQ_cQ6~oFtEyjWat9+w&}?YF|9s{$dW-H1kD$-MwJqVYIs45FMjTdKnVhn~6OB zfjHtA^T1pRJM{Hx66Agd`-j_iiMkda^(LuG$>_qx#YeXE-ZMZ_KXGfWIKZGAA|=tE zN4(mh_95EnAf*f&`bRtT$pL0ZV56KI(jnwID)02);cco0Q5X{I%X;spUG6H*2WdI| zs(z-yKAKl$Ka&z2idg@l-Pk_*@8SwWf!e?G6jO_AD~7vy!^R;H_eVrFuzO#E;SSSx zvztiFjd1Q(*+?hjZ(DNDnxl@Odj5NiZxB10E2sn*QM1p6*YLUJgBMp9<^FrNC954o zJ@?4owVtn0?QGmj9r`VW*>z*wZO4DPyf!c$m=`1nU9az%`0@7}M0Sx4_79FlV)xH1 zx)&m^$xzQNEeNNQWc3(`>G!SW|Ds+U#1t3FR1PrKKL9(CN@py=^pqlSdf z&|3krqAo3d&X^Iu`mM=Mu5NYGypjWfu=a$H99_1Ju&qgL2^hBBr^s*WGRzy24A^U;g}6iL69hl;_i~jFIZGFxiMLw%3uvUmI|Z3@szUPs}SN zN}b;k7RO4>+T{qD)hwRQs*=qH1BSlACkSI^xKA|2v7*&U60j|a6B~ih-0#Fi`$A^v zjL`T?Z3(QGT(5s;_X&LFZmjit>u46_?5V#lU9ab~AwX$=sQ`DB4P zfBe$Or>s0nu1g}~jg1LPn*WnsvHRwEsP-E-=S;bsmp#xQKn8J?xxK?UT*kq&5jnbPV%tBcjR98LAD=ud zHLy|J3&^^KbWJ!(a{Tv!m8Gk2ubVMtW%7tW<^=WwXCz-v`E39i-?93syK_W_QKtlpCUCgpdHHH^6Opgn(IP-2=*u(b{>s{-#Yy0w85letuc}A z*bOX7fq#DeyzlO#$cfO#- z^uw^^W&ajse3P!r10(_faK)$d2rcf@Z&{G?cgL)3dh-Raeew`E?9hQWkR|uFS?tR* z8ER7Nz`n8~$mcB&#XRDWt7ShV;&>Q#P96inz$T$3Uw%H4VIs|QXvFtV+d_%F4${t1 zi~lZzIiJK>9CtL@iCW2QI{@#|zbd~wlOM0)Z9@4cG@!tL*swvcALZp!`#r%&9bo9-XkV2_#^xH1L^F>%9G+cFWJsR*g z1-aJqClPzh_AP>?J32E0_9dh5+7y72*WFtuCiRA?|6zvRl)x0(l&AVVe(Xkk$@sox z256af?tK9(`M^$uy<+3;VUtJSAMfk=N&3(XKpL)^<=^qY&btsBEwJsDC^y-=n>Jxc zY5+wd9iT%8f|7ksis%mNXTzTKh`Ck}G-5Dq!jEy)OQ5TJB->XF5Oit){>d@kz}3<) z&S^#%K^S(*Om5J!OLvo<%Jl$$cGxj0BoTo*L}Ybk{3r8hghePCdkOEJX~k}*Ho>-v zQUpU^r2JQB+s#68rL#`otQT#2NbK=RQXV z;LFbksbF~?@1f_N^z63)ZAEVMncG65r%z^u109_ln-rBIbO3Zu28gEP?e$n+rwg7n+5wL>OG)B5Y>1`x)#J@{ zt0f2K!F8PjG9P`w6Ec-elh8pwR~G8!sQ!dM*7ycb##zZ|%fTXy!8-AwA}RZiWI5p+ zfK&MX%N=o;j)u;QG?1A-%{;-9C3}TN+;`n{u!@QGPwmHm^v}7UNWKLtg07k$@HmiB zCNXI?sCIFda`0MnjgMJ#nE@x2abrl`1S@l$U=KAT2K+ScXfQ zpYZ`b&G=foS){Zg%KsL=bEF|NQZuK7D#WBjhV$ESL-Z!qe1W-?22Z7W^K2+*ud()F zf*Nu4-%LO28^n5BEr4{oI>I3OfEam`D&A4N>eAz8{EZp3GYq-$lnF4YLOk^k=XB7~FZM zVKOM}2`zxAI2G_BU3sg5ix3F^SpB)o*_2>dTCha?AT<~BV|($<_c=YX;H#WAm04-?TzMOLyaO#wJsj^a{}aNs=s;#78%j`YujZHe}VKKniFE4F86 zvndv#-VQTV+jPBm8`_5mSlaEG6`ctnM+82zlUNT(7kWic8C<0|(xs2HVZ))f;^HYE zkazk7ryI}^SvL;N`xOtJAj!Ip_ueFQpEwX7OC(2xPL8{kUy$Y=%>`bsLB%nmAzu4k zyDZ3UKyL%i#A_m*dag_PdgRCaAHuQvQtQu&lKRoYVFG}On3Buui%&?F4!{}rnNJe9 zS4;$z;PDZt3Ehrn#g;)aW|#0wP>$pE3slB+`47>LI^o?j`0{qzlj-h{VnP;?!a>L{x5b-PBl;=k7!^>a2!BV3m0-Poyg2tOPvSq1w|T1(?N76i?@ zod{;dEq~`7rXQX3>wU|okIt`=N8r#9{f)w-M`tP{g+O?$@E;OvnjE%E5O7lm+h}>m zu)fdn-&`J*EtWQHc6R|L56|GE_~u(c<_l+K790Gw3mA(X{rZQq9J!wRx|lSeE1+v0 z4%5ND*?RPlG_)paQa^U`-x*uc<>h==bX?x>g61pG%?)laz-PbZU^VE%y*zYk1^dUs zttf0$3b?nyi+`_YD(mNBg9oHx5+-i>#vgga94bQ3ekPHGo2)^%h3>WP+p-#XAarG$ zd=(b3h6}6rm$R?QNe>~!M0-z|e#CO03D}XYKUS2Woy#@Bdvqz&lzTDUu#+70a(!*v-E(|W0+J6~3C4yHi;UA8L`E{(p)k@FTyIs)U~Aww;hC>_M*1I)X8=FA*Iew4bzBW!@A)e(d)?(gFf}O(Qk& zv*-WV*R_8%daMyTK0k2zQ~V*c);Wy*SLuuH3(*DNayV8GeFGn*_Ai|ze_A=rsVR5Z zO4j0+DqvQ{?L&D$;c!I5rYo zJBQIzNcjoNzm%tlXEfIt&HMxpV8xlF;!sr5-O8LlPlCK2ClUXF>Dcgs*T!S_?~H#* zO1M8~9X&hC3N2iKqq<7`a{IO+e1KQ~#NThnIF}Z3JF4t2HrA;mhab*U_~;@~GTsXyTnOEw2)rVZR# zB!g=qtlfD-u$V~%3W>5xyPh7J#(->MY)&dmH_{o_UgK}d_sdECZX|omWf&SES*IzbI}iQsyl?c|jWtK&V`2CuyReGWb^E#=$qia|+}k)p(h*&|UZr zN#v`M$E@d^`1Q!-Bh(TOb0*UiNDJBB3oZ?fqo@-%o!$c{bob*+53qFj4rl!sRbD8n zeK%T6qh6se)JFIjtf~G)vh4!G*YpMJE;s5ospn$VtKgN=6!GWbi6bSnloX^4`uBhu z0~GN^O^Tf9JPf8Hjk=BzeHnB$1d}9u2#zDNzd|u@6S7ibWLHb%u9gd9*Q_|vm{r8~ zqm%0r#XriO4~K*>)AV=}1g;;_=+_lAVJ*G@GjVic(K)!dXhq?gszWA(hx0C(8mW|`3ZCz>Q88GG~yr6Djkx< z3+D45JL}fveG)ZoUbF7f3D96+$xV^}P||A}A@Wt{y6hCdx^Xrc9QmPV8vgQgsd{`| zjE({A1o6JlhXD-|s77CyA~{WCrTMo;%8fMMTvOSq=7SRoGaJX_ZjKv9GQk4YSiu6p zBC<--Qdc{@Z?}6EQ3n#gU(o?tl33026SCU(P5ToG`wiT#PuS;7Iou=oT4GkLKhn-S zEAhFE+C&Jd#|u(ycvj2Ly_tJm_J&E1-P4NjGQq(rI{##hLX!!7U@z1wK)(I}ig5~p zP(7i}r;@l2>dhpv?T!7*Wqm?JRr@tv3O^oS@wlv{x5cs4DI2fqs5F~tIq=1K5ZBx< zfBAy?=FyeU?@Wj%xHNjzAo?<-VJkT2BdJW_-9z?$?GYQsNGjcG)!4W0yS|xbKy2CC z>vto?opvrlztZR{4Vlb!U(?aPrqQp?ql#c4wJFU`uc&zZb6z6sanc%Srz%wYtWyrF zNFrzh*l?#z=mPMXRL#7m15Xe=Byb=Lr;2zKC4Y|IQ&nogvpR=rPwVMlW zI{X~CwG0f5 z&DL}Jmq8Srs7Q+AHR=_*jr-R3OJ(w0W7~O#Bt%?e-hGH<+DTtHD03ToWmavYaV%IA z+seSJ)tmJ%il~jrOZ`5`SNX#bsgD}vU3!XYvwufXof%_CU)p0KA*0sTg3n~FWX-?X z-6I|^RvNk(?l%gJk!=62Ww3Ps<++(Dvk$2e$&sZKi#Yx4KDhuk$(1fhU>c7Yg`a9L zGg2qEAI2s^Lj0C@=o30_@y2GNjfrA5G4}~zkTsrO8)im-Y&IZsf{Mb6fz#NfUBz>P zdn})Ssq*pOAy^Cj>DNJ0=5GLTkn68Et$*^Q?s$HC(q6hAgf-1o9_EhXR{}YgO;{(< z15g$_(mYOQ=er81uvcsgVrtoA>aQRTQ}ZC$tVQ8BNgeH9_4ukM)p@+?aSyAi zv=|Y~b$>ppp=wDNilcQTB;vZg1{ml~A%giA~!jAnA={jeq{5% zf#&zOBmd6IRBQX&wpkUQY6;_iujK-A#TZvM#VE(u4KUP65>M1?o`fbbqH%5yySeoJf zYpQ=#$ZpP7#P_OhTW_zCz}u+t%anRkZWwDs=D!tfiQvH6S=;pRpst0N`;FvZx5&SK zVI~l3?4EFazs{@|4wQ<}UwaOg7yEp6&wfd~qs$fTpO;G`_pYfZm3W5-NIm-EZ~IF9_dlAZ3Dp6dgV~8Z4xj?SBhv96xFp4svhqu~dxt zRSF|&Ej>!h>&1-NFcd`!LL(pw&Qjxo^J@U5_R9)z72Vu){u%|NV=Tk|Bg$K|@W5XE z!tA49)fu@7HHSApruwAv&fHAg*<#~&m)J|E@Hv{-3ul_AN;%uf-qO|<6ENx0uz}oG z{+fUE1|snu{@H0Uy$gJ?y*M$d3w9B#n2#G9^Bc*k=shyKyk$A;wu|0aeQLt|n7+-n zb_pA#4u*DYX+jN_=^f4)qCG+llfw74m2ps@LNu;!a6eYsx zIWS!yF3m{d%q(OYo^c*>kAVp(cR78@HO0M}mqQ#;HZD%l8gnByNn`G={WV5TXV+{n zQf)tfI^1zFQ@wU8)KTf%&~WGp>$Yt5h4gm52JEQjTCWFr+8*y!X_(Em6PKby9F6p2 zz7IJ)(Rp_$7+zZ+CiR(_xYK*{;04}gwP!`TlEAM)TBA1_a~eV)TYk-(2?-&*Pqq$! z4#VQXQ-mN&jR8vDlX8DSr^dW#5WQfP_Mk#M`KljJNZ=#QpYP0gRy3i3yHSh9(7>;9 zJPbZ`Dmi8hl&{=TTfflynZ=klPb zyP1ng)naRbb;Ihrjb0?g^9o~h7igv1_{o5>we+D|Fm&={``x9G{Hh7SQgFI5-#@|x z8nGIp(H8QeDMYDht=6=P6iI$(dA*GmB?^7Xs-UkUhBRdE;GDg!X9nO$uu(m_1>;NH*JI2Pq zS2;K)lA%sS2v)gNd>>ov)cv-e>v5idpbq7-Z1eAeL2!aO#rt6n$;R?4hLhFn-(QCD zCM*HwQe`qMyh-oIii3w+JVL^j?!sG;Yju`2UK4UAJtw7q?%A+3m)6c_JydKn)vZ!D zd^{Yu**+w6_6z7|256Z4Vg~4<&!YxD@h2GT#Ms~D@fTQxeTogGPzAnu`Y}(~by--F z-}1SZ%SqO$07#v(oi&zvt99rX_=)Wz8Dod^OZKF==(Hi-tniuE!qWV51=m;_v$%_A zm1QEEWX?1-xeGx6^Dp3DlLizY<3Ed7!4~t%p1!J(u#PKmMux{P*9GHSeagaa!lzBl zwt~&Pyydh7icO|X=17s@=3fyP#m(TT6m{WW+nq+A-eLKQTmtP1R?hE3 z2Feu-kA^uEX=}NMszfTJ7@R#Rins?@+~~c{VSl#wUA7M*LKl5XWWM#%m`Tzje|2MJ z1_viisthX)D{!fi;$aHP_@>N)?}n@jJaqDXftz%Q&t{3>M^t7Ql~ce3{YXQFS=93z72+j{i2yfrRiY*Ody! z7NkO5TCma#ygAK#VXIu5Bt_jDjz>irL-%KbCK#u zd{z>B-^ES9r6rp~pg5>@%mG$&Yfnxyk-_50IFyygjlb0*!tLOpE^-traZuZ*eW^0H zDb`R5&rLylP4aI|p9(x18rOyqsLcB-B?AOtS}_v9EODqK`}2d~VOm&w_q97Q+J$Z< z)&2pVxjDun8{R0N#purXW|8r7?X`l@6YiqdW-d|UI}EpZMeNWP6+4QYZZd23s9#@S zd=e116;Ls$UvdR<|H2y_rz4~F5pZ15{>GR~yad`|#lMKEDjs0oh;wf=Wh}aUPbbf; ziX9gOe6%@vJyrUKE%Hi-)cVZ~D&hHnJ398T2;hH=hWucPR?-q&bq}6kjLOcRLpZj_DAPWmjYAU_U9gVoD(z*pAR$2^7?<3 zx{@XW$npW!n|V&8iFcUt-N~9-<~WEK*T57jW~Rw=iAmu>3k*4?1qP!GrRECz8l9wk z1lFWEb(z#E`pWkf=+f-Vm8NdU5-N_Rv#etdspXV~GaHa#$BrmEaRWdcb!t6Z7phJ3 zMe~OzpCZq?wTk*}lQrMf;ny`XFiJ3oQ~W$NJ}bSQ>fJEUzEvqa(!%I{e{&t+cO|Uo z_+f}?dEL>*xm6IXdthy^z($nzkItG;IhARO*v3JiqF^A#)5OjA^Zdx%L3ed%xmJ3F z5BsN5I<|Y=^tBGTRKb^Yp^2iEg~cxg?2geMlUi2qR`4?lt(a1=dw6b4$E=F)od$Be zlwZ=xQWRRy^=u^rT0r0Vh7jOxddJgzBU+`$p~ zm7jBDs>(sR)~3#eFWz=jeR-Xd(y-djAdO~$wGCiR00gMN9~sx7{QeIaa+K2raO@!D z7!c3ahU}qFRzqeVGNkre?Fa(;ToiOTGz9>KeCwP2r^en1hn&nH=3vuB4&v})vsKl`#onkK;~@?P9i@BWyTvs7P3NMei>hCVL&b?llmWr^=~aPB_tF_ybV(PK83l4f z9P1}41@k7n~7}B zSx;2w;EZ!?gOSxoj>STn5{Di5#&JAUV(oPa%4Hi}BE(T-AYsM>zy5)Ki|s*#t%@R= z;fBM98|UTfG08)Zqa}G7R^?srJp=^P^THFuIh?Ma%CmG_*gRmo|UjcdK#(NZZOdaNIj{q}>es(HdmThzSZ`7bAaHs%ar{wRaRj#?4Uc!y) zrovN)9ZHJZiy?5{3Im5Z{kYHvPb( z+F2Y35Gnpv(ME9<{Cec#<;(L@3tfXK#`rG+00J$Fn-Rz(?#2>>;Gkm09u%siI|RX; zsBX;BC}MwY(G>OA;}-`r1!L;nm0T8VC%tS)JFHt|21&1ruE}Lrd!i;Dv#g(~)Z`MM zOwqpE`SY9WvIBeS5xSd@XVcPO@m}N<{mb#PUL3+ZMLIPu_Uyq*uI5aBuo0@8h#Da$ z%CbJ2D*mg$N!__I+Pd`!UtaKgpBl*y@quZp`+7SI6O?V!>gMibu<;HP{R$Mip2Xq# zz)7h$I%F%4rcFB{h&3dxtoN0WF@M`yb8fMl9!6z~>!5)Y@QQ5~rbZ&b95%fuRp=>fhH1e^$zqZDKAF~r6I4@S*hsSc!W(y7 zd7`?@skO0$bg8bswDuJPTDNmlY&?ZxyMd1>v!m|Hi$0}ywagjs@y-bz&{SvHfteW{ z(V6FkH5}GOGsIzypA;Nr=Mw5GIHl~B6Krx`j~;@2 zlCPWT1f~fZE6vRJ3HREmLASC#db2l4;VnxbKs~_N7$A5H?Vq2|Rg<9xsJt<$8s*@5 zQe1H-h;C`~j1HO+x z+B=YIpv+;b*M)3j6&HAw!6fGrvqcXWVHRwq9+z{`JSKn=^_Z`G4s7_$ti=qm9KI1g z&wW;3{e*F-K2t8aHGysm^?CgYl=QGRYW@Bo|9#YFF>%Vc(q9aWMoJVjrTVtK*O zLNmFz?uEj4rwI7a{df`l8kz$0wBywUiox2`(NRLf?l;>?aW_GZn)Fxuvw<7W_l4UC z(4K1{;fW!|@as!yys3BL$PEHHw0;z)PfZa#-d66}aqGD2U&>ixji9lc?9sC`pMTj+ zo^AZbpDA7N6VzuL9f=wUdYG+}3vTPgt~Unm5|uzYFo%f2g~`u=%s&{b#j`t|i8b&} zNvx#BEYe}|rDH~Zu|FG7n@D{nDt{U!5*kp*wn0$M6wTVX9)lD2kcUjuM5@*qR8hP+ zbVWl+Uz9w!&idwu4b#VfZ`GCh19rhxV-g#_&NJQ`_6t97#4KISMQ#V4mACD=T^Wom z)U`Z<6QK=g)3+-~qhzZ}PUK?Lm_c8Ae5(S$|M`0reVaus210skxu5MdO-~lg)!a=H zU!@OOHM0?uNbfXHSspDG#40C*YO%YtaiDpcDecxHly%nw!Kl*tjDBpQtB(X z)>mKUX~vM+3YVX&KrU5Olq{RK2>Mo_<+jIa<{G+GdyY~^oif6=qjC8)_un~=$~;@h z6bwg4lTGdq0UnCNFMhhQVZ3R*3nVn*12M7IoR!>PFj|lBO|$XkA?H#icr%}Ysnyed zwkyv#o`_>!MkIdosR#Ugvh}9*D3*c&Wl0oH4eC>OzFK$>>UCVa@}U9Isb?iooWr z63W|eTrr*+INc>zhgJOdGd#42){sO}zBF|+b>czCJ_B`7U;@IY__<^0C57~XdJGEW z>Yrq_gj*Q(;JhxhKdEa;@B>v~`M^*hlHFGqK+6he#O z(!Ly*s}ZU*NbB~T=+@R-ry<1l9m`D~LfW{3`Z%kSdV~&;1O^zUfN|Le`}olQ;P*?= z3XnGS$CS&semnXWuTd5Wg7_>i%05l3R$RJfjmH8lpKZ+m1F?e_1g$naM`Yk*{z65o zC>vFot=1PaVO_A0y8rmoH;@xBW@KEfsO$(!4?5MbMNk8kkEqX)3xKLMK!xY`0&%u&sGz}s7I!Kd#p^^JcK*=w&COs zd(~{83J=Z>F>>4cCzuk2N&6l&`Y53Chig?4uli{HK<24k!F);L{GyxMBKOlv-+v79 ze-FT+Vr6fNV|)*Sr_w$xx{TlcyG`sXuf|v%i)%p`?6rx=v-e%f=7%@W0pIk5#`s9L zngi|+xi>l`4xa?JV^2`q==0&ii=(%D^)xCtsNUs60k7N&XeXk++-nD~j$ptJ2#_I) z*%uE>ruk9-6)A0a`Cxon8zUY8V}1tIX+R)6y}h+oB+1*WwFlYdxRY2J`=#B#-Skdo z(Dg;QqkjCWDHms`5cv3t2=)ogp30v0NkKlrmS_F(|3X zA47y=Z}>=W6ZPpCJnr-W$Wryfn?r}DOIOzqRT)YvFv;_*)^7iM4Md?eXM1|g4CuVCKFn*_p z(@Iov8U1bc#J?u#ZC2)gfldP3nmaIfh^*j0;y4r;$JM_MVUiDPC1wkzyLWfLuCaV0 zV+Ay-e3oJlpuXHVoP+T%1+TKF?*jTi(SpPv_1ak2>@n zd)t5mTemTM-Mq68QuJg$M4>|_|9D#WAgFB*%mGpZpvuD+$AV{~Khc5!Es#aw3nF7; z8lKm_d}`={C)=Y-9RT|b!qE349B8~kj-tf1Sy1L>hc;s01#}L|L+)GFEOx@MxhN#! zY+MQlN!P${ORfsITz-&`xwmX=3?~L(9fvV+x~=#&QKuNpC06FVqLxIP{jOnZ)ONc+ zu%jRw6_aWQwtJ_-KKcq+YEa;)?r1T0-BW|JJ_V@~flwz1>TScH)egVA(~O>f89jI0 z85tRqXuV)vMIv!6F##hZBN`Qj_~iC#-FZ$@YfW2AI#la6BEpH{347K|7ioolDrMC6 z&m}X!;|zgN>xY8CnlK7eq7XHN)@}>R8(7Km82H(~&)J}EsWR*raRe95T4%smhaWNk zi(%?UUp`yw{6ZQ!JduF2u%9RL1tj!Q`F(&?(Qo;di6Mt2H(8KNZ;V4ZtIa$ zwvm}BM)p+^Q%Rp@*gDfOS3ck26MGwM+{EIS@i$oh4+x$Lx5*!dkL|{Kur1 z^)N&U&a2ALUx4r*8k)_q5w+vByiiJQPy8oWR*FLd$~P%5E@ zes#*ERbIImsdd*Yq3^yOZyHSW(g(eL!HMtba?=E$hpcXX|fXNx;+2U_?g^f3R$ z-@hoP{zR+tr`?jyTa-Lcm^Ak-v2$5ABZ9Se@t5&NV}8;U+3l;M5!~75-fC!*Dpz0u zWPP7y`XEMb5V$1)bQh-h%gkW=Xw{g`MI){N^xnU9tlq9kT@mMs>nuwM?Os;|qAbs`CVlDh>HY z9Gw3e8Ymci?q6=p_?qmS4X$m(FE0Agydvn9&11D|%Vl8TD+_d&oczuZkv#Zxn%+ih zwp~K}f)z2RCT(p?1&|Chw<2fPCL^#Twf3jDM;Wb6t|5*OBRl*wbyT`9tJ1|-aQBkw z%NJEM0%8?SBa`90EV+vEl=*V&rag(AeNYry#OnSqZP+IW#DKEY13MuzT5r=p9GU0O z<~iY$S|*(%))0c-l^H&Du^p*zc5}?eyTC*R`l&Q@gkLf;-LKSAvVZ=ViBIz#@FyvG zy&NrW+X{^5{d{6hCy4ASstX$R{D6S1-}HMB>81*s8tNN-Xd2{t2)%iuUex!R#I>H{ zJp;aex_hSNLD7r5j*i2lfKF70dU(+Z7TT?>vT}12l-%q_^d?tvm7FfikTv%{PdRyz zd$pXW&qY{v1-ZCU4-c!H&X5a%j3Z?KtLzZ~IWw44E0&h%2>={QjDuZQxT_R}+E3 z;Ig=bb)0izQM(nM-}t;Ai3^$i?rY-roChfNm1c1*(s=vt`m5H!^90CYMwE$^RzGGd zt|F1G_4*3W2!bKG?CIM}bUlwEHS%`CTLF!R5bSh2AA4gRCx7DbwV-%@aB_l{?J$c( zhkrsN8k(FjOP^Np2UJorC_a)%3YXvP$H2WuNyUyn-urwUxg+SR#d18l6xq6 zz_QPmVuXss>qYf@Va3(49C_Uw;z4(PZ?v56IQ+v=SIun3@AKRkzhg+cuF2}LtU*_- z`;c1EMc045A1+)q^F*V*6K(nXo;PrJ09ab*`n*RWqCnje16b7i4-^DBj81Ncs^_x0?Dwh`Hrmfu!u*ZV`X zEs#qabu)jt*t3StUG5Dqus2dLhSzY2XTF`>^ZaneX0r4+XU+4?rE7wNt+oBVqSOPd z@DEU4KhY{t-CQK86JOh#gp$~x<*gUN9R;ffbM+6^6uxti5JBG(qqaL7|{z zvoS2xQ(k)AwSTCY@5$**Z*V> zM}gr7TfpvsJ-6evjzJNrp$cA9yj(i5CG~=z{Yg^Wuhe%JgkCSxTnLjH+dhE`4L`m{ z!@~stPm5Cx!~gDVHP5D(vhz18lrkwzGjvKhm3v5J@+vd)b5BAy4+{JUD#LdTQZy%aT) z!mG@!9|`=*k%pWJVR-4LBnR#5^|3@=lM5H z#p$n_zc1eJF=7t;tY;`$@2hT4`#7As)~5F1uZc;&DDkTHC)p}8-kQF^BkRmaP;YO0 zD8H)YhkLWOkDVMV=!Y$>6W=_#-@w;qZe;GN-kcpb-@fb(4-;XCuHWq(S>BhwAKbrL=h3W;S1*a<}8I9{}Nq)kjQSG(YJx6)XPV?bCd?NFP0L7bP` zi3=APxi#x|F&n7G6ez50XOoNa#=HEWtJ$%6vk_%&bnusCW09u1h(xfi;%{2Oa~Yy5 z{TKze5ACFl!GskaNv9ya>bmNk*U_5>K6pY*!KkAy6mna4rvqOvP@GSr-o#t4(#gJG z>oTuWvwcxv(;o^a0s;t`C%#NAH@wOpeO zn`FTG@Obi=aRM{9n&5iL;fynQYkK0s;~-;qHmNHomD~W6BPT4z zZ8fwcaS_?mf%!2%9tOqzsV|oEZ~JxjY|`NzU#c0_7DuEL+ifsu64v2M;eq3jcpatv zRDh1mIwSUX#@+F4wj11eYFH!dlNnnrc6V}rRiXnO20e5t1d?=`7BJe-6cn>A1-$m! z%Kv8>I;p}f5%T(jnL%nL5PuL5Rk{uxXbijh9$4*84+wOKV0F1z^~26;%r-WF33F+8XE0(+sGY8zEz-Ai*8oez zS7PFeMq{EcYhr>L6TCbSUwmMqi3txHVu;2l@zHBcjQZl`KRq=p2|ZQ)RdrX_`Oi6Z z=DQWkUL{<+&{v~hxQ?g-K6e9dxE7ZYT@Su#`IhAw@K!v37rY%j2i^#N1}+CLSY8Ct z=o{Sb0>8J|ko|fdWVu!eC}}Dno zMIsZGicFtWR8nCqCi>V8WV#bcqvMDsW!rR9MUy(sjPPVoVkrMwnO}*O4C| zr^#G`Xv*0@(E%CG%8aIz?#!D4RchXJd1j{4sEbldcvZJ9ijA7rboW=wRPm`28Ev{j z?;Wa2xY346j9@&sm>X;G_NFh;Yl*2Q<-+(T@`&W~{!qk`u<`Mh9QNYWq*5F5QXQGG zu`znGE7>s2POAiOg*zK}&0r#VBIF>zd3^0UKaUn-Yv(HJ=tzmVI2E0~=k|q;M4E3s zSec~im{!MWMzuNCNWcsyqp9F*Ng9c$*;%fVnz4}qd;663&3u6l$H?Nu`1tz%NJo9L z>~`EmBP+m5SA+O;~Lk_=mU|bc%(`9$ssWqIE zE2bKST4nTbqh223R5;C4Mp+Zs8*?!m-J1HjSm&$$RN>9b0L8qqfVG@Ng#FfxMsN~tpBCbD-nlZJY)2S$fWTG+O&pM14L-auuag9^HO#u2bZRMS2O1RA)S2\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Report designer" +msgstr "Dissenyador d'informes" + +#: .project:2 +msgid "Report engine for gambas" +msgstr "Motor d'informes pel Gambas" + +#: FExample.form:40 +msgid "Demo 1" +msgstr "-" + +#: FExample.form:47 +msgid "Demo 2" +msgstr "-" + +#: FExample.form:54 +msgid "Cloner" +msgstr "Clonador" + +#: FExample.form:61 myReport2.report:51 +msgid "Facture" +msgstr "Factura" + +#: FExample.form:68 +msgid "Report in report" +msgstr "Informe en l'informe" + +#: FExample.form:98 +msgid "Test Print" +msgstr "Prova d'impressió" + +#: FOptions.form:25 +msgid "Size" +msgstr "Mida" + +#: FOptions.form:34 +msgid "Paper size" +msgstr "Mida del paper" + +#: FOptions.form:41 +msgid "Custom paper size" +msgstr "Misa de paper personalitzada" + +#: FOptions.form:45 +msgid "Width:" +msgstr "Amplada:" + +#: FOptions.form:51 +msgid "Height:" +msgstr "Alçada:" + +#: FOptions.form:67 +msgid "Orientation" +msgstr "Orientació" + +#: FOptions.form:72 +msgid "Landscape" +msgstr "Apaïsat" + +#: FOptions.form:72 +msgid "Portrait" +msgstr "Vertical" + +#: FPreview.form:28 +msgid "Report preview" +msgstr "Vista prèvia de l'informe" + +#: FPreview.form:38 +msgid "Print..." +msgstr "Imprimeix..." + +#: FPreview.form:48 +msgid "First page" +msgstr "Primera pàgina" + +#: FPreview.form:61 +msgid "Page" +msgstr "Pàgina" + +#: FPreview.form:87 +msgid "Last page" +msgstr "Última pàgina" + +#: FPreview.form:97 +msgid "Fit to window" +msgstr "Ajusta a la finestra" + +#: FPreview.form:104 +msgid "Zoom 100%" +msgstr "Escala 100%" + +#: FPreview.form:110 +msgid "Zoom out" +msgstr "Allunya" + +#: FPreview.form:124 +msgid "Zoom in" +msgstr "Apropa" + +#: Report1.report:15 +msgid "#3" +msgstr "-" + +#: Report1.report:17 +msgid "#1" +msgstr "-" + +#: Report1.report:27 +msgid "COUCOU" +msgstr "Cucut" + +#: Report1.report:56 +msgid "#2" +msgstr "-" + +#: Report4.report:30 +msgid "IMPRESSION DU PLAN COMPTABLE" +msgstr "Impressió del plà contable" + +#: Report4.report:54 +msgid "Code" +msgstr "Codi" + +#: Report4.report:62 +msgid "Intitulé" +msgstr "Dret" + +#: Report4.report:105 myReport2.report:254 +msgid "Page $PAGE" +msgstr "Pàgina $PAGE" + +#: Report6.report:13 +msgid "coucou" +msgstr "cucut" + +#: myReport1.report:27 +msgid "First Page" +msgstr "Primera pàgina" + +#: myReport1.report:37 +msgid "Report About" +msgstr "Informe Quant a" + +#: myReport1.report:45 +msgid "All my friends" +msgstr "Tots els meus amics" + +#: myReport1.report:53 +msgid "Friend Table" +msgstr "Taula d'amics" + +#: myReport1.report:59 +msgid "Report about all my frends" +msgstr "Informe sobre tots els meus amics" + +#: myReport1.report:66 +msgid "Test Database" +msgstr "Base de dades de prova" + +#: myReport1.report:89 +msgid "Name" +msgstr "Nom" + +#: myReport1.report:99 +msgid "FirstName" +msgstr "Nom" + +#: myReport1.report:110 +msgid "Birth" +msgstr "Naixement" + +#: myReport1.report:151 +msgid "$PAGE/$NPAGE" +msgstr "-" + +#: myReport2.report:40 +msgid "Section 1" +msgstr "Secció 1" + +#: myReport2.report:131 +msgid "Id" +msgstr "Identificació" + +#: myReport2.report:140 +msgid "Designation" +msgstr "Designació" + +#: myReport2.report:148 +msgid "PU HT" +msgstr "-" + +#: myReport2.report:157 +msgid "Quantité" +msgstr "Quantitat" + +#: myReport2.report:165 +msgid "Total HT" +msgstr "HT total" + +#: myReport5.report:42 +msgid "Gambas" +msgstr "-" + +#: myReport5.report:77 +msgid "gambas" +msgstr "-" + +#: myReport5.report:97 +msgid "Page $PAGE on $NPAGE" +msgstr "Pàgina $PAGE de $NPAGE" diff --git a/comp/src/gb.report/.lang/cs.mo b/comp/src/gb.report/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..da9510a36d9e5397a91034117c8d56469823f258 GIT binary patch literal 2646 zcmZXUO>7%Q6vu~tQ0hV{Enh8P(2}FHCDpDv1E*x5rEKA%FLZ~2BfT{{1qzZ{kB~&5)|MfU-%fxSg^Jd=6 zym@b4e%ZO{LxFY-@+9QyJwg=0k2d0i_R}UIHh@1HUN!s`ydUG&!0q5);AZe&@Lq5e z6m-Ql@F8#qxDDI~ZUN(93_NO>1h+!Zfe(OlAoq7b?sppGx-%gCd>5o&AA?+X-taTS zFTtG{zYIPKeh<>0Kg|3MkpA2R@e>&e`&|dQ{su@tZi3wZmYI*?^5}|PAno>mjK?rO zTz?Sc{1L-(P9m2n5D;k?k&}i~h9^P#l>@o2GUM9tH24I@1EYTca_e&-@3m@p0p$5# zfsFe%AkX{3@JA3oamDDr8D2BI4l*CVgIxc&8Q%=2xX%`l`)>tzfIC3^#9n+je*nZ1 zVS$YAQSdP^WB4}6buEzjeII0A&w;$pry%$F9AsX;F#08s_xu`UKEE^ipJx6akniUf z$bB|obNUej>BlaR`|bl_B8EZcH4gHeagg>|kap9CWy9SVqn|H8xc)f^*Rio?*?6y) zA-wMqNE|W>8HOB$JPYZ!{pd&tb8bPNf%Mzc=+L*@+W{!gLk>Zh7q+1uqWe99{)-US zBpYM#Qq=2xaE&Oh)Q&<9Ls(a=N!Bm_&G5am?SqU#7-zPpAbTNvf6P4_W880yTfdE< z!~ZjE%$FD%6GMl@&|xuLN}kA!3>OHItk-4ZtX%M28iniIjGfNVS4c zH)PXpDqjZfiV|rRxK+;%UG0fUyArm1B_>@z2xZBx_PfpXZozJFFqEN|%dY3>Wic6@ zCd>AGU5OKRW8MzLlya+!VM1i{rRmJfOtx5%=~-FIB?~fD%$Lf^iCjix9g+1yH*D2A z7evm6#})X69uALA%(>_l7i9jd>>Uz_Xh<(e`_7w2ZWYuSv#>~)!rrAyvvR5|qRa3j zqku(vuPaBSiQZsZYHu*O9;%Y|L*I5oQS#N2t6M?O%aZSUVLToeZ?tS)2bY;vP1wrh zyl#bk#ilawJ4%qTbD|t5zi+C%s_QAapnX~G#nTuzd9YeGb~$@q)yKq)sxW(Uy@MfU zwyY4nv>n<=2s9nqbw=V%*9jLB;*`ca7#khk-|M+vk94@ATlficbjjE6-~=n{SaZq` za8g2+lBw76-sD_nnz@aYiqnjmtNmX;+2~yGv?z5~-IZqdVxZxQ)HRQL zSYobosoSpEiAb^Ffb6zAYu(jaOO$j_SI!-Kb48)kUQtUTZP#fzIM-B4MZspK?@YgA zYUNIKP%n3luk=f1?(S>Dzop1yxYu1(PGwQA-0no#4QebPOBAIal}@XtmY2F0{fd&k z0%`T@1F;Q4JWJX1b-zMz!9`rWzn<4oHC!wD$}!P*s#g{c%Yq?+UH5zxIz*G@u%5Ii V_VabjT}H_l9jDVqVqtHI{{ZhsEWrQ( literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.lang/cs.po b/comp/src/gb.report/.lang/cs.po new file mode 100644 index 00000000..bbbb4bb9 --- /dev/null +++ b/comp/src/gb.report/.lang/cs.po @@ -0,0 +1,233 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Report designer" +msgstr "Tvůrce designu" + +#: .project:2 +msgid "Report engine for gambas" +msgstr "Report nástroj pro gambas" + +#: FOptions.form:25 +msgid "Size" +msgstr "Velikost" + +#: FOptions.form:34 +msgid "Paper size" +msgstr "Velikost papíru" + +#: FOptions.form:41 +msgid "Custom paper size" +msgstr "Volitelná velikost papíru" + +#: FOptions.form:45 +msgid "Width:" +msgstr "Šířka:" + +#: FOptions.form:51 +msgid "Height:" +msgstr "Výška:" + +#: FOptions.form:67 +msgid "Orientation" +msgstr "Orientace" + +#: FOptions.form:72 +msgid "Landscape" +msgstr "Na šířku" + +#: FOptions.form:72 +msgid "Portrait" +msgstr "Na výšku" + +#: FPreview.form:28 +msgid "Report preview" +msgstr "Náhled reportu" + +#: FPreview.form:39 +msgid "Print..." +msgstr "Tisk..." + +#: FPreview.form:49 +msgid "First page" +msgstr "První stránka" + +#: FPreview.form:55 +msgid "Previous page" +msgstr "Předchozí stránka" + +#: FPreview.form:62 +msgid "Page" +msgstr "Stránka" + +#: FPreview.form:82 +msgid "Next page" +msgstr "Další stránka" + +#: FPreview.form:88 +msgid "Last page" +msgstr "Poslední stránka" + +#: FPreview.form:98 +msgid "Fit to window" +msgstr "Přizpůsobit oknu" + +#: FPreview.form:105 +msgid "Zoom 100%" +msgstr "100% velikost" + +#: FPreview.form:111 +msgid "Zoom out" +msgstr "Oddálit" + +#: FPreview.form:125 +msgid "Zoom in" +msgstr "Přiblížit" + +#: Report.class:100 +msgid "Section " +msgstr "Sekce" + +#: Report1.report:15 +msgid "#3" +msgstr "-" + +#: Report1.report:17 +msgid "#1" +msgstr "-" + +#: Report1.report:27 +msgid "COUCOU" +msgstr "-" + +#: Report1.report:56 +msgid "#2" +msgstr "-" + +#: Report4.report:31 +msgid "IMPRESSION DU PLAN COMPTABLE" +msgstr "-" + +#: Report4.report:55 +msgid "Code" +msgstr "-" + +#: Report4.report:63 +msgid "Intitulé" +msgstr "-" + +#: Report4.report:106 myReport2.report:253 +msgid "Page $PAGE" +msgstr "Stránka $PAGE" + +#: Report6.report:13 Report7.report:64 +msgid "coucou" +msgstr "-" + +#: Report8.report:24 +msgid "List of all my friends" +msgstr "-" + +#: Report8.report:34 +msgid "List Of My Friends" +msgstr "-" + +#: Report8.report:79 +msgid "Page $PAGE / $NPAGE" +msgstr "Stránka $PAGE / $NPAGE" + +#: Report9.report:13 +msgid "ReportLabel1" +msgstr "-" + +#: myReport1.report:27 +msgid "First Page" +msgstr "První stránka" + +#: myReport1.report:37 +msgid "Report About" +msgstr "O reportu..." + +#: myReport1.report:45 +msgid "All my friends" +msgstr "Všeichni mí přátelé" + +#: myReport1.report:53 +msgid "Friend Table" +msgstr "-" + +#: myReport1.report:59 +msgid "Report about all my frends" +msgstr "-" + +#: myReport1.report:66 +msgid "Test Database" +msgstr "Test databáze" + +#: myReport1.report:89 +msgid "Name" +msgstr "Název" + +#: myReport1.report:99 +msgid "FirstName" +msgstr "Jméno" + +#: myReport1.report:110 +msgid "Birth" +msgstr "Narození" + +#: myReport1.report:151 +msgid "$PAGE/$NPAGE" +msgstr "-" + +#: myReport2.report:40 +msgid "Section 1" +msgstr "Sekce 1" + +#: myReport2.report:51 +msgid "Facture" +msgstr "Faktura" + +#: myReport2.report:130 +msgid "Id" +msgstr "-" + +#: myReport2.report:139 +msgid "Designation" +msgstr "-" + +#: myReport2.report:147 +msgid "PU HT" +msgstr "-" + +#: myReport2.report:156 +msgid "Quantité" +msgstr "-" + +#: myReport2.report:164 +msgid "Total HT" +msgstr "-" + +#: myReport5.report:42 +msgid "Gambas" +msgstr "-" + +#: myReport5.report:77 +msgid "gambas" +msgstr "-" + +#: myReport5.report:96 +msgid "Page $PAGE on $NPAGE" +msgstr "Stránka $PAGE z $NPAGE" + diff --git a/comp/src/gb.report/.lang/es.mo b/comp/src/gb.report/.lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..23728da020a79f05b6c154a1e2c0d003496ec192 GIT binary patch literal 2864 zcmZvcU2IfE6vqca#o`BopC~d?KK$TryA&ucUtLRqCT&^!fk7Y4ba%R)=I))%+`Dbz z!Kfjc5EJ49F=C7oVuHR^V$>KRWPLDEUNFWOqYwIEOnflJM58hB|G#(oAu#Qk-<-KK z=bSk+XMbF=pML5RcT5_D)U+$aR5#EYKCJYNBC#r$!vzwUX;^DWPJz?-n{ zjOY8{ozQ1Mx@dy8fM0n1GS~wBTR;Dk*MIZ+pCIimLdSJWLFy~fG0s&W?XC9Q2x5!K zgZLMnettK2GxP%><0yHKfJ>o2<5>omK@UBrLHa!gt^`kjcY$X-KL+oHehH-CpFytw z6{Oui{QSQl{Vzs$JGcy_pH(2^-vDxdyVtjYcR=p~?*mI9_Zjdua2lljmqGeF0rFg@ z{QTP>?Vbhc=L3-T&w~g>eD3)r$UI&Ke*nJ$7lG$socz%9BhODfKlS{~^MdC^5dWfy zj()xZ9|FGy>Hi8yyMKXjCH@9^&KnTya&QI6^R;^BL9XB9zZXH;9rW{uLE3p1q@A*# zk3Ab8^D_l<-Af?%ANTreAoKHv=er=^`3%T>p9dMwCGZ~bd+=WHif0Rq^F6EudA?4N z=hy|(-vEdsibEi_i044AHz4gk@4vqaa{oz?{@(=Y?<~mkUjXU%BFKH;fwcPzh=1`r zI{Nzus!D!$S$uFD==U2eu9DTJxfHvuN!Q>q^D_J#Fg-2vO`1#Vw+^RXn*>bgQVWL(y&PM#rMN z5hu1L>#DAujLlIk_NqjSo@rffBxX{JeI{h*;zX8I6~jgtE*MRjN-~~KKAn#CtJ8c9RCy92V&qh@QQPHB`I?c}m zs;0$&jkGuz>C7z$bw+Z~8671mF*XtmP5Q%TXM z=Mq-3E^*2vqU22Ed#<2{q;}hA%v3YgL{*;ZsfLOYlQiEGgDS#F2DJ*aq40Y*~x4p|;oNJX-1WO!e&eSRICj%gS4 zL}gpSMHJ-rQIoWY;Htl@K9ox<1W$Qb)l_rF;!e^k2_-H*wvh_WQB|>GpN2n}D7x~v znY6s;ns!Roo2PMSN|Z9nvaQuaohVV9K*=d5QHJC8DlHH_D3DOmS=E^s_frg$hNFX$ zGO?OKeVVv#V^cQGa}ggCVXXS~&JalvjjlxQ-=&jX}RpZiujxF+$ zRTVPhB)?{p*=RN~xX8p*R3$AkJ7?XY*qB7!oI%zcL(5(=k{yUc2A3n#vPy5kf5MMV nMMu&^EHldj%QBWGzKkN9voH4Ac=qz+|0{ST*`2d5q!;%e8pSp8 literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.lang/es.po b/comp/src/gb.report/.lang/es.po new file mode 100644 index 00000000..32e5787c --- /dev/null +++ b/comp/src/gb.report/.lang/es.po @@ -0,0 +1,423 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report 3.6.0\n" +"PO-Revision-Date: 2014-10-13 17:30 UTC\n" +"Last-Translator: jesus \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Report designer" +msgstr "Diseñador de reportes" + +#: .project:2 +msgid "Report engine for gambas" +msgstr "-" + +#: FOptions.form:25 +msgid "Size" +msgstr "Tamaño" + +#: FOptions.form:34 +msgid "Paper size" +msgstr "Tamaño de papel" + +#: FOptions.form:41 +msgid "Custom paper size" +msgstr "Tamaño de papel personalizado" + +#: FOptions.form:45 +msgid "Width:" +msgstr "Ancho:" + +#: FOptions.form:51 +msgid "Height:" +msgstr "" + +#: FOptions.form:67 FPreview.form:208 +msgid "Orientation" +msgstr "Orientación" + +#: FOptions.form:72 FPreview.form:214 +msgid "Landscape" +msgstr "Paisaje" + +#: FOptions.form:72 FPreview.form:214 +msgid "Portrait" +msgstr "Retrato" + +#: FPreview.class:313 FPrint.form:192 +msgid "Cancel" +msgstr "Cancelar" + +#: FPreview.class:327 +msgid "Layout..." +msgstr "Disposición..." + +#: FPreview.form:67 +msgid "Report preview" +msgstr "Vista previa de reporte" + +#: FPreview.form:97 +msgid "Printing..." +msgstr "Impresión..." + +#: FPreview.form:117 +msgid "One page" +msgstr "Una página" + +#: FPreview.form:127 +msgid "Two pages" +msgstr "Dos páginas" + +#: FPreview.form:136 +msgid "Full width" +msgstr "Ajustar ancho" + +#: FPreview.form:145 +msgid "Real size" +msgstr "Tamaño real" + +#: FPreview.form:177 +msgid "Printer" +msgstr "Impresora" + +#: FPreview.form:189 +msgid "File" +msgstr "Archivo" + +#: FPreview.form:223 FPrint.form:109 +msgid "Paper" +msgstr "Papel" + +#: FPreview.form:229 +msgid "A3" +msgstr "-" + +#: FPreview.form:229 +msgid "A4" +msgstr "-" + +#: FPreview.form:229 +msgid "A5" +msgstr "-" + +#: FPreview.form:229 +msgid "B5" +msgstr "-" + +#: FPreview.form:229 Form1.form:78 +msgid "Custom" +msgstr "" + +#: FPreview.form:229 +msgid "Executive" +msgstr "-" + +#: FPreview.form:229 +msgid "Legal" +msgstr "-" + +#: FPreview.form:229 +msgid "Letter" +msgstr "-" + +#: FPreview.form:243 FPrint.form:130 +msgid "Width" +msgstr "Ancho" + +#: FPreview.form:253 FPrint.form:148 +msgid "mm" +msgstr "-" + +#: FPreview.form:258 FPrint.form:135 +msgid "Height" +msgstr "Altura" + +#: FPreview.form:287 +msgid "Recto Verso" +msgstr "" + +#: FPreview.form:293 FPrint.form:100 +msgid "Long side" +msgstr "Lado largo" + +#: FPreview.form:293 FPrint.form:100 +msgid "None" +msgstr "Ninguno" + +#: FPreview.form:293 FPrint.form:100 +msgid "Short side" +msgstr "Lado corto" + +#: FPreview.form:308 +msgid "Gray Scale" +msgstr "Escala de grises" + +#: FPreview.form:315 +msgid "Full Page" +msgstr "Página completa" + +#: FPreview.form:321 +msgid "Reverse order" +msgstr "Orden inverso" + +#: FPreview.form:327 +msgid "Collate copie" +msgstr "" + +#: FPreview.form:348 FPrint.form:71 +msgid "Range" +msgstr "Rango" + +#: FPreview.form:362 +msgid "Copies" +msgstr "Copias" + +#: FPreview.form:385 +msgid "Print" +msgstr "Imprimir" + +#: FPrint.form:38 +msgid "Printer Config" +msgstr "Configurar Impresora" + +#: FPrint.form:58 +msgid "More..." +msgstr "Más..." + +#: FPrint.form:89 +msgid "RectoVerso" +msgstr "" + +#: FPrint.form:178 +msgid "Copies :" +msgstr "" + +#: Form1.form:56 +msgid "One Page" +msgstr "Una página" + +#: Form1.form:64 +msgid "Two Page" +msgstr "Dos páginas" + +#: Form1.form:71 +msgid "FullWidth" +msgstr "Ajustar ancho" + +#: OutputReport.report:74 OutputReport2.report:77 +msgid "Version 1.0" +msgstr "-" + +#: OutputReport.report:81 OutputReport2.report:84 +msgid "Date" +msgstr "Fecha" + +#: OutputReport.report:96 OutputReport2.report:99 +msgid "Project Title:" +msgstr "" + +#: OutputReport.report:103 OutputReport2.report:106 +msgid "Project No.:" +msgstr "" + +#: OutputReport.report:110 OutputReport2.report:113 +msgid "Company:" +msgstr "" + +#: OutputReport.report:117 OutputReport2.report:120 +msgid "Designer:" +msgstr "" + +#: OutputReport.report:131 OutputReport2.report:134 +msgid "Base Plate ID:" +msgstr "" + +#: OutputReport.report:186 OutputReport2.report:185 +msgid "Bearing Pressue" +msgstr "" + +#: OutputReport.report:196 OutputReport2.report:195 +msgid "Node #" +msgstr "" + +#: OutputReport.report:203 OutputReport2.report:202 +msgid "Brg. Press., psi" +msgstr "" + +#: OutputReport.report:268 OutputReport2.report:266 +msgid "Anchor Rod Tension" +msgstr "" + +#: OutputReport.report:278 OutputReport2.report:276 +msgid "Rod #" +msgstr "" + +#: OutputReport.report:285 OutputReport2.report:283 +msgid "Tension, lbs" +msgstr "" + +#: OutputReport.report:350 OutputReport2.report:353 +msgid "Page $PAGE of $NPAGE" +msgstr "Página $PAGE de $NPAGE" + +#: Report.class:108 +msgid "Section " +msgstr "Sección" + +#: Report1.report:16 +msgid "#3" +msgstr "-" + +#: Report1.report:23 +msgid "#1" +msgstr "-" + +#: Report1.report:33 +msgid "COUCOU" +msgstr "" + +#: Report1.report:62 +msgid "#2" +msgstr "-" + +#: Report10.report:27 Report6.report:16 +msgid "ReportLabel1" +msgstr "" + +#: Report10.report:33 +msgid "ReportLabel2" +msgstr "" + +#: Report10.report:39 +msgid "ReportLabel3" +msgstr "" + +#: Report10.report:69 +msgid "Page $NPAGE" +msgstr "Página $NPAGE" + +#: Report11.report:17 +msgid "C'est la maison !" +msgstr "" + +#: Report4.report:31 +msgid "IMPRESSION DU PLAN COMPTABLE" +msgstr "" + +#: Report4.report:55 +msgid "Code" +msgstr "" + +#: Report4.report:63 +msgid "Intitulé" +msgstr "" + +#: Report4.report:106 +msgid "Page $PAGE sur $NPAGE" +msgstr "Page $PAGE de $NPAGE" + +#: Report7.report:64 rpTestShadowGrid.report:19 +msgid "coucou" +msgstr "" + +#: Report8.report:24 +msgid "List of all my friends" +msgstr "" + +#: Report8.report:34 +msgid "List Of My Friends" +msgstr "" + +#: Report8.report:83 +msgid "Page $PAGE / $NPAGE" +msgstr "Página $PAGE / $NPAGE" + +#: myReport1.report:27 +msgid "First Page" +msgstr "Primera página" + +#: myReport1.report:38 +msgid "Report About" +msgstr "" + +#: myReport1.report:46 +msgid "All my friends" +msgstr "" + +#: myReport1.report:55 +msgid "Friend Table" +msgstr "" + +#: myReport1.report:61 +msgid "Report about all my frends" +msgstr "" + +#: myReport1.report:68 +msgid "Test Database" +msgstr "" + +#: myReport1.report:91 +msgid "Name" +msgstr "Nombre" + +#: myReport1.report:101 +msgid "FirstName" +msgstr "" + +#: myReport1.report:112 +msgid "Birth" +msgstr "" + +#: myReport1.report:153 +msgid "$PAGE/$NPAGE" +msgstr "-" + +#: myReport2.report:40 +msgid "Section 1" +msgstr "" + +#: myReport2.report:51 +msgid "Facture" +msgstr "" + +#: myReport2.report:130 +msgid "Id" +msgstr "-" + +#: myReport2.report:139 +msgid "Designation" +msgstr "" + +#: myReport2.report:147 +msgid "PU HT" +msgstr "" + +#: myReport2.report:156 +msgid "Quantité" +msgstr "Cantidad" + +#: myReport2.report:164 +msgid "Total HT" +msgstr "" + +#: myReport2.report:255 +msgid "Page $PAGE" +msgstr "Página $PAGE" + +#: myReport5.report:41 +msgid "Gambas" +msgstr "-" + +#: myReport5.report:75 +msgid "gambas" +msgstr "-" + +#: myReport5.report:94 +msgid "Page $PAGE on $NPAGE" +msgstr "Página $PAGE de $NPAGE" + diff --git a/comp/src/gb.report/.lang/es_ES.mo b/comp/src/gb.report/.lang/es_ES.mo new file mode 100644 index 0000000000000000000000000000000000000000..8e8a88b6ce496c95050a4a7ad7581716f9a5c6d9 GIT binary patch literal 2867 zcmZvcTWnNC7{>>>h()}BctO#Ta`SSwyR=+fE_NvenzXRc7K1UG>F#tp&Dk@XIcH1Z zNfOk=#PFig5O0JR)F;#!VhmAAjEONC67|IpqfZ*&l=xsU{{QDp+ZCAho8NqMX1?#6 zZ|2*}OXj^M(7GWHP;L_95IGMW+Vk^;z?3-VdEE0Q@K(&f?Df|@PkO%P`7XEs>rQ*V z58es=EJznq;4R=+UcUskLI2Uu|KjyOy#6;xdvnln-9nK1Qgn=SIY@geJlBHQA_^e> z#U?+$9lRO(9*}XAJ%_=C(4Y3KfQz7qo|7Q`9tW3#C&0VF)1K$RWzfF?>GxNV>wg1j z_b)$x6{P>U=xzfSf%LN+Wc+JD?$3LDBX~RXZtz~P404|V7lV@^?Y{`p-wBZCI_c-% z0crOPNIxHfw0|B%DB_~$*C6wF3H%xS9-ISy0^{U4&rdx+^E~hQx#tDXiy;2Rm+0u{ zTkt{f2ax`+fVBG$2v_1N$aBs|uy=sVK%Q@nXBWuzJN);ZAngwO`DZ}dc^0IdQ9nQ7 zISDd9M?tQ80p$Kyy#6Z4{JiOT2IM>c7-YV`02$AB;N9S5@E-7r=VBP=dsq$fd_|Dw z=mzO;KZqlWLm;+@5s>Rckamyx?idw?X>*0Oa{Efb@G2=(|kzQX1 zGVZmIEfCfW$C&Sj@J(-m+y{Ycu^Ga)F+-Xo#?SV6hO}o6`Fp|~^IdI+EQc^B zk3d@6dav*v**Y_bM?wDfJqcOi=K`=D((UywumiHq>%J+6eQQ9DOY!E#vs_0Pk*oM)u=xN4@ zt;>dLXeVQHREwP|(V}-!SDJ~L&|;Sf*||88WmUtl8HO#R!={>yrISykqur`LqGGYz zsUvc*qOf<5Hnp)t^i@T_imGu1o-jIMn@KL06aBiTLi9T_Ek6I^dS%UqChSL*9AI7=~?P4vht76xnt}rW7435!Sn$bbt?a*PHC6NOr zF%}20k82CLyvVLF?Tf-Ys@D;^9f!rBuNwH(MOhokxh&Ss+{RqKt-LQl42GrxzM!I9 zpD%O;g?v!xl!Z;j&b%BR>S^m&aS{wU6~&=StSicK9XDgSHAQ#Y2ce0YliRU0sx=W} zQR?`)-odtkzJcCZ{|L7YnN9Elf}tY~tV;A`($NT&iMGhfm_jy^ZNo#mg3Yt*=yz1R zpf{@6D(<2vH;S6$am$;Q+vT$>W*jIv_ujZh~_l*UnY%1Kn>n7u{|L@#P2RCHEzCdL(&!ldcw zpsY-+#!;dsZrIpV%+yB_-xK5SMm&s literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.lang/es_ES.po b/comp/src/gb.report/.lang/es_ES.po new file mode 100644 index 00000000..0d264d89 --- /dev/null +++ b/comp/src/gb.report/.lang/es_ES.po @@ -0,0 +1,436 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report 3.6.0\n" +"POT-Creation-Date: 2015-09-20 17:55 UTC\n" +"PO-Revision-Date: 2014-10-13 17:30 UTC\n" +"Last-Translator: jesus \n" +"Language: es_ES\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Report designer" +msgstr "Diseñador de reportes" + +#: .project:2 +msgid "Report engine for gambas" +msgstr "-" + +#: FOptions.form:25 +msgid "Size" +msgstr "Tamaño" + +#: FOptions.form:34 +msgid "Paper size" +msgstr "Tamaño de papel" + +#: FOptions.form:41 +msgid "Custom paper size" +msgstr "Tamaño de papel personalizado" + +#: FOptions.form:45 +msgid "Width:" +msgstr "Ancho:" + +#: FOptions.form:51 +msgid "Height:" +msgstr "" + +#: FOptions.form:67 FPreview.form:208 +msgid "Orientation" +msgstr "Orientación" + +#: FOptions.form:72 FPreview.form:214 +msgid "Landscape" +msgstr "Paisaje" + +#: FOptions.form:72 FPreview.form:214 +msgid "Portrait" +msgstr "Retrato" + +#: FPreview.class:313 FPrint.form:192 +msgid "Cancel" +msgstr "Cancelar" + +#: FPreview.class:327 +msgid "Layout..." +msgstr "Disposición..." + +#: FPreview.form:67 +msgid "Report preview" +msgstr "Vista previa de reporte" + +#: FPreview.form:97 +msgid "Printing..." +msgstr "Impresión..." + +#: FPreview.form:117 +msgid "One page" +msgstr "Una página" + +#: FPreview.form:127 +msgid "Two pages" +msgstr "Dos páginas" + +#: FPreview.form:136 +msgid "Full width" +msgstr "Ajustar ancho" + +#: FPreview.form:145 +msgid "Real size" +msgstr "Tamaño real" + +#: FPreview.form:177 +msgid "Printer" +msgstr "Impresora" + +#: FPreview.form:189 +msgid "File" +msgstr "Archivo" + +#: FPreview.form:223 FPrint.form:109 +msgid "Paper" +msgstr "Papel" + +#: FPreview.form:229 +msgid "A3" +msgstr "-" + +#: FPreview.form:229 +msgid "A4" +msgstr "-" + +#: FPreview.form:229 +msgid "A5" +msgstr "-" + +#: FPreview.form:229 +msgid "B5" +msgstr "-" + +#: FPreview.form:229 Form1.form:78 +msgid "Custom" +msgstr "" + +#: FPreview.form:229 +msgid "Executive" +msgstr "-" + +#: FPreview.form:229 +msgid "Legal" +msgstr "-" + +#: FPreview.form:229 +msgid "Letter" +msgstr "-" + +#: FPreview.form:243 FPrint.form:130 +msgid "Width" +msgstr "Ancho" + +#: FPreview.form:253 FPrint.form:148 +msgid "mm" +msgstr "-" + +#: FPreview.form:258 FPrint.form:135 +msgid "Height" +msgstr "Altura" + +#: FPreview.form:287 +msgid "Recto Verso" +msgstr "" + +#: FPreview.form:293 FPrint.form:100 +msgid "Long side" +msgstr "Lado largo" + +#: FPreview.form:293 FPrint.form:100 +msgid "None" +msgstr "Ninguno" + +#: FPreview.form:293 FPrint.form:100 +msgid "Short side" +msgstr "Lado corto" + +#: FPreview.form:308 +msgid "Gray Scale" +msgstr "Escala de grises" + +#: FPreview.form:315 +msgid "Full Page" +msgstr "Página completa" + +#: FPreview.form:321 +msgid "Reverse order" +msgstr "Orden inverso" + +#: FPreview.form:327 +msgid "Collate copie" +msgstr "" + +#: FPreview.form:348 FPrint.form:71 +msgid "Range" +msgstr "Rango" + +#: FPreview.form:362 +msgid "Copies" +msgstr "Copias" + +#: FPreview.form:385 +msgid "Print" +msgstr "Imprimir" + +#: FPrint.form:38 +msgid "Printer Config" +msgstr "Configurar Impresora" + +#: FPrint.form:58 +msgid "More..." +msgstr "Más..." + +#: FPrint.form:89 +msgid "RectoVerso" +msgstr "" + +#: FPrint.form:178 +msgid "Copies :" +msgstr "" + +#: Form1.form:56 +msgid "One Page" +msgstr "Una página" + +#: Form1.form:64 +msgid "Two Page" +msgstr "Dos páginas" + +#: Form1.form:71 +msgid "FullWidth" +msgstr "Ajustar ancho" + +#: OutputReport.report:74 OutputReport2.report:77 +msgid "Version 1.0" +msgstr "-" + +#: OutputReport.report:81 OutputReport2.report:84 +msgid "Date" +msgstr "Fecha" + +#: OutputReport.report:96 OutputReport2.report:99 +msgid "Project Title:" +msgstr "" + +#: OutputReport.report:103 OutputReport2.report:106 +msgid "Project No.:" +msgstr "" + +#: OutputReport.report:110 OutputReport2.report:113 +msgid "Company:" +msgstr "" + +#: OutputReport.report:117 OutputReport2.report:120 +msgid "Designer:" +msgstr "" + +#: OutputReport.report:131 OutputReport2.report:134 +msgid "Base Plate ID:" +msgstr "" + +#: OutputReport.report:186 OutputReport2.report:185 +msgid "Bearing Pressue" +msgstr "" + +#: OutputReport.report:196 OutputReport2.report:195 +msgid "Node #" +msgstr "" + +#: OutputReport.report:203 OutputReport2.report:202 +msgid "Brg. Press., psi" +msgstr "" + +#: OutputReport.report:268 OutputReport2.report:266 +msgid "Anchor Rod Tension" +msgstr "" + +#: OutputReport.report:278 OutputReport2.report:276 +msgid "Rod #" +msgstr "" + +#: OutputReport.report:285 OutputReport2.report:283 +msgid "Tension, lbs" +msgstr "" + +#: OutputReport.report:350 OutputReport2.report:353 +msgid "Page $PAGE of $NPAGE" +msgstr "Página $PAGE de $NPAGE" + +#: Report.class:108 +msgid "Section " +msgstr "Sección" + +#: Report1.report:16 +msgid "#3" +msgstr "-" + +#: Report1.report:23 +msgid "#1" +msgstr "-" + +#: Report1.report:33 +msgid "COUCOU" +msgstr "" + +#: Report1.report:62 +msgid "#2" +msgstr "-" + +#: Report10.report:27 Report6.report:16 +msgid "ReportLabel1" +msgstr "" + +#: Report10.report:33 +msgid "ReportLabel2" +msgstr "" + +#: Report10.report:39 +msgid "ReportLabel3" +msgstr "" + +#: Report10.report:69 +msgid "Page $NPAGE" +msgstr "Página $NPAGE" + +#: Report11.report:17 +msgid "C'est la maison !" +msgstr "" + +#: Report14.report:19 myReport2.report:40 +msgid "Section 1" +msgstr "" + +#: Report14.report:39 myReport5.report:41 +msgid "Gambas" +msgstr "-" + +#: Report14.report:59 +msgid "All friends list !" +msgstr "" + +#: Report14.report:77 +msgid "Gambas Report Demo" +msgstr "" + +#: Report14.report:95 +#, fuzzy +msgid "Page $PAGE on $NPAGE " +msgstr "Página $PAGE de $NPAGE" + +#: Report4.report:31 +msgid "IMPRESSION DU PLAN COMPTABLE" +msgstr "" + +#: Report4.report:55 +msgid "Code" +msgstr "" + +#: Report4.report:63 +msgid "Intitulé" +msgstr "" + +#: Report4.report:106 +msgid "Page $PAGE sur $NPAGE" +msgstr "Page $PAGE de $NPAGE" + +#: Report7.report:64 rpTestShadowGrid.report:19 +msgid "coucou" +msgstr "" + +#: Report8.report:24 +msgid "List of all my friends" +msgstr "" + +#: Report8.report:34 +msgid "List Of My Friends" +msgstr "" + +#: Report8.report:83 +msgid "Page $PAGE / $NPAGE" +msgstr "Página $PAGE / $NPAGE" + +#: myReport1.report:27 +msgid "First Page" +msgstr "Primera página" + +#: myReport1.report:38 +msgid "Report About" +msgstr "" + +#: myReport1.report:46 +msgid "All my friends" +msgstr "" + +#: myReport1.report:55 +msgid "Friend Table" +msgstr "" + +#: myReport1.report:61 +msgid "Report about all my frends" +msgstr "" + +#: myReport1.report:68 +msgid "Test Database" +msgstr "" + +#: myReport1.report:91 +msgid "Name" +msgstr "Nombre" + +#: myReport1.report:101 +msgid "FirstName" +msgstr "" + +#: myReport1.report:112 +msgid "Birth" +msgstr "" + +#: myReport1.report:153 +msgid "$PAGE/$NPAGE" +msgstr "-" + +#: myReport2.report:51 +msgid "Facture" +msgstr "" + +#: myReport2.report:130 +msgid "Id" +msgstr "-" + +#: myReport2.report:139 +msgid "Designation" +msgstr "" + +#: myReport2.report:147 +msgid "PU HT" +msgstr "" + +#: myReport2.report:156 +msgid "Quantité" +msgstr "Cantidad" + +#: myReport2.report:164 +msgid "Total HT" +msgstr "" + +#: myReport2.report:255 +msgid "Page $PAGE" +msgstr "Página $PAGE" + +#: myReport5.report:76 +msgid "gambas" +msgstr "-" + +#: myReport5.report:95 +msgid "Page $PAGE on $NPAGE" +msgstr "Página $PAGE de $NPAGE" diff --git a/comp/src/gb.report/.lang/fr.mo b/comp/src/gb.report/.lang/fr.mo new file mode 100644 index 0000000000000000000000000000000000000000..4094fb4d2a962de02315d724d5cb0644a21e9e53 GIT binary patch literal 1530 zcmZ9K&x;&I6vxXXMjexA;;QjSVhMtT(4#pBVT_VyrgwL+XQr7Sk$6xlJ!MZxP1R6c zy*~`(L#4}tf>`aame`a$5oAo)3X zaM!*OxCA~9{b`W)-w67%Ao+U-d=hMdPlIic><2+V3G0u+C!n8#WT%6E1Elz_fycn> zAmwobr1)-y{cVu^eh)I#;75@B{e&Rs{Ldi8cPFfW5BvipyT3rX_aBh%IRIzRfX6}F ze?9O_(4PbGu^2z{y8==?{h+@LQr@3{dBK3%Ap=@VRETi+iESmM*0)BofsuScUI$J|~CQc_<8y z4yDj=`8mxNIK&rDt|_^W<(hP*poB98jd?`(06MqzQ4SB6dYZh53h>LB*Iq&r_Y zHAh&x6M@QvsdRKjf|8(dH2ku$03~-jTM}e3+HL2XilWtT?rfn8S=ZF9Lv^5Fa$7mi PIua>Sx0dMc-9-NbyLLvO literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.lang/fr.po b/comp/src/gb.report/.lang/fr.po new file mode 100644 index 00000000..13b3566b --- /dev/null +++ b/comp/src/gb.report/.lang/fr.po @@ -0,0 +1,454 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2014-12-16 11:06 UTC\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Report designer" +msgstr "" + +#: .project:2 +msgid "Report engine for gambas" +msgstr "" + +#: FOptions.form:25 +msgid "Size" +msgstr "" + +#: FOptions.form:34 +msgid "Paper size" +msgstr "" + +#: FOptions.form:41 +msgid "Custom paper size" +msgstr "" + +#: FOptions.form:45 +msgid "Width:" +msgstr "" + +#: FOptions.form:51 +msgid "Height:" +msgstr "" + +#: FOptions.form:67 FPreview.form:208 +msgid "Orientation" +msgstr "Orientation" + +#: FOptions.form:72 FPreview.form:214 +msgid "Landscape" +msgstr "Paysage" + +#: FOptions.form:72 FPreview.form:214 +msgid "Portrait" +msgstr "Portrait" + +#: FPreview.class:313 FPrint.form:192 +msgid "Cancel" +msgstr "Annuler" + +#: FPreview.class:327 +msgid "Layout..." +msgstr "Mise en page..." + +#: FPreview.form:67 +msgid "Report preview" +msgstr "Aperçu de l'état" + +#: FPreview.form:97 +msgid "Printing..." +msgstr "Imprimer..." + +#: FPreview.form:117 +msgid "One page" +msgstr "Une page" + +#: FPreview.form:127 +msgid "Two pages" +msgstr "Deux pages" + +#: FPreview.form:136 +msgid "Full width" +msgstr "Pleine largeur" + +#: FPreview.form:145 +msgid "Real size" +msgstr "Taille réelle" + +#: FPreview.form:177 +msgid "Printer" +msgstr "Imprimante" + +#: FPreview.form:189 +msgid "File" +msgstr "Fichier" + +#: FPreview.form:223 FPrint.form:109 +msgid "Paper" +msgstr "Papier" + +#: FPreview.form:229 +msgid "A3" +msgstr "" + +#: FPreview.form:229 +msgid "A4" +msgstr "" + +#: FPreview.form:229 +msgid "A5" +msgstr "" + +#: FPreview.form:229 +msgid "B5" +msgstr "" + +#: FPreview.form:229 Form1.form:78 +msgid "Custom" +msgstr "Personnel" + +#: FPreview.form:229 +msgid "Executive" +msgstr "Executive" + +#: FPreview.form:229 +msgid "Legal" +msgstr "Legal" + +#: FPreview.form:229 +msgid "Letter" +msgstr "Lettre" + +#: FPreview.form:243 FPrint.form:130 +msgid "Width" +msgstr "Largeur" + +#: FPreview.form:253 FPrint.form:148 +msgid "mm" +msgstr "" + +#: FPreview.form:258 FPrint.form:135 +msgid "Height" +msgstr "Hauteur" + +#: FPreview.form:287 +msgid "Recto Verso" +msgstr "" + +#: FPreview.form:293 FPrint.form:100 +msgid "Long side" +msgstr "Coté long" + +#: FPreview.form:293 FPrint.form:100 +msgid "None" +msgstr "Aucun" + +#: FPreview.form:293 FPrint.form:100 +msgid "Short side" +msgstr "Coté court" + +#: FPreview.form:308 +msgid "Gray Scale" +msgstr "Niveaux de gris" + +#: FPreview.form:315 +msgid "Full Page" +msgstr "Pleine page" + +#: FPreview.form:321 +msgid "Reverse order" +msgstr "Inverser" + +#: FPreview.form:327 +msgid "Collate copie" +msgstr "Assembler" + +#: FPreview.form:348 FPrint.form:71 +msgid "Range" +msgstr "Echantillon" + +#: FPreview.form:362 +msgid "Copies" +msgstr "Copies" + +#: FPreview.form:385 +msgid "Print" +msgstr "Imprimer" + +#: FPrint.form:38 +msgid "Printer Config" +msgstr "" + +#: FPrint.form:58 +msgid "More..." +msgstr "" + +#: FPrint.form:89 +msgid "RectoVerso" +msgstr "" + +#: FPrint.form:178 +msgid "Copies :" +msgstr "" + +#: Form1.form:56 +msgid "One Page" +msgstr "" + +#: Form1.form:64 +msgid "Two Page" +msgstr "" + +#: Form1.form:71 +msgid "FullWidth" +msgstr "" + +#: OutputReport.report:74 OutputReport2.report:77 +msgid "Version 1.0" +msgstr "" + +#: OutputReport.report:81 OutputReport2.report:84 +msgid "Date" +msgstr "" + +#: OutputReport.report:96 OutputReport2.report:99 +msgid "Project Title:" +msgstr "" + +#: OutputReport.report:103 OutputReport2.report:106 +msgid "Project No.:" +msgstr "" + +#: OutputReport.report:110 OutputReport2.report:113 +msgid "Company:" +msgstr "" + +#: OutputReport.report:117 OutputReport2.report:120 +msgid "Designer:" +msgstr "" + +#: OutputReport.report:131 OutputReport2.report:134 +msgid "Base Plate ID:" +msgstr "" + +#: OutputReport.report:186 OutputReport2.report:185 +msgid "Bearing Pressue" +msgstr "" + +#: OutputReport.report:196 OutputReport2.report:195 +msgid "Node #" +msgstr "" + +#: OutputReport.report:203 OutputReport2.report:202 +msgid "Brg. Press., psi" +msgstr "" + +#: OutputReport.report:268 OutputReport2.report:266 +msgid "Anchor Rod Tension" +msgstr "" + +#: OutputReport.report:278 OutputReport2.report:276 +msgid "Rod #" +msgstr "" + +#: OutputReport.report:285 OutputReport2.report:283 +msgid "Tension, lbs" +msgstr "" + +#: OutputReport.report:350 OutputReport2.report:353 +msgid "Page $PAGE of $NPAGE" +msgstr "" + +#: Report.class:108 +msgid "Section " +msgstr "" + +#: Report1.report:16 +msgid "#3" +msgstr "" + +#: Report1.report:23 +msgid "#1" +msgstr "" + +#: Report1.report:33 +msgid "COUCOU" +msgstr "" + +#: Report1.report:62 +msgid "#2" +msgstr "" + +#: Report10.report:27 Report6.report:16 +msgid "ReportLabel1" +msgstr "" + +#: Report10.report:33 +msgid "ReportLabel2" +msgstr "" + +#: Report10.report:39 +msgid "ReportLabel3" +msgstr "" + +#: Report10.report:69 +msgid "Page $NPAGE" +msgstr "" + +#: Report11.report:17 +msgid "C'est la maison !" +msgstr "" + +#: Report4.report:31 +msgid "IMPRESSION DU PLAN COMPTABLE" +msgstr "" + +#: Report4.report:55 +msgid "Code" +msgstr "" + +#: Report4.report:63 +msgid "Intitulé" +msgstr "" + +#: Report4.report:106 +msgid "Page $PAGE sur $NPAGE" +msgstr "" + +#: Report7.report:64 rpTestShadowGrid.report:19 +msgid "coucou" +msgstr "" + +#: Report8.report:24 +msgid "List of all my friends" +msgstr "" + +#: Report8.report:34 +msgid "List Of My Friends" +msgstr "" + +#: Report8.report:83 +msgid "Page $PAGE / $NPAGE" +msgstr "" + +#: myReport1.report:27 +msgid "First Page" +msgstr "" + +#: myReport1.report:38 +msgid "Report About" +msgstr "" + +#: myReport1.report:46 +msgid "All my friends" +msgstr "" + +#: myReport1.report:55 +msgid "Friend Table" +msgstr "" + +#: myReport1.report:61 +msgid "Report about all my frends" +msgstr "" + +#: myReport1.report:68 +msgid "Test Database" +msgstr "" + +#: myReport1.report:91 +msgid "Name" +msgstr "" + +#: myReport1.report:101 +msgid "FirstName" +msgstr "" + +#: myReport1.report:112 +msgid "Birth" +msgstr "" + +#: myReport1.report:153 +msgid "$PAGE/$NPAGE" +msgstr "" + +#: myReport2.report:40 +msgid "Section 1" +msgstr "" + +#: myReport2.report:51 +msgid "Facture" +msgstr "" + +#: myReport2.report:130 +msgid "Id" +msgstr "" + +#: myReport2.report:139 +msgid "Designation" +msgstr "" + +#: myReport2.report:147 +msgid "PU HT" +msgstr "" + +#: myReport2.report:156 +msgid "Quantité" +msgstr "" + +#: myReport2.report:164 +msgid "Total HT" +msgstr "" + +#: myReport2.report:255 +msgid "Page $PAGE" +msgstr "" + +#: myReport5.report:41 +msgid "Gambas" +msgstr "" + +#: myReport5.report:75 +msgid "gambas" +msgstr "" + +#: myReport5.report:94 +msgid "Page $PAGE on $NPAGE" +msgstr "" + +#~ msgid "Zoom in" +#~ msgstr "Agrandir" + +#~ msgid "Zoom out" +#~ msgstr "Réduire" + +#~ msgid "Zoom 100%" +#~ msgstr "Zoom 100%" + +#~ msgid "Fit to window" +#~ msgstr "Adapter à la fenêtre" + +#~ msgid "Last page" +#~ msgstr "Dernière page" + +#~ msgid "Next page" +#~ msgstr "Page suivante" + +#~ msgid "Page" +#~ msgstr "Page" + +#~ msgid "Previous page" +#~ msgstr "Page précédente" + +#~ msgid "First page" +#~ msgstr "Première page" + +#~ msgid "Print..." +#~ msgstr "Imprimer..." diff --git a/comp/src/gb.report/.lang/nl.mo b/comp/src/gb.report/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..6d6ad5bf149909f22d1c25da0ec25ebe64cd25aa GIT binary patch literal 4609 zcmaKtX^b346@be*AX$!p!{Hr(>?C) z+4WlF4=4%w5#ebb+UcGwr>y0;@HWxpp?uR|VlM)glavGYDC z`+gAem@jMl<51TB4irB>1;ww=Ls|b<%9oUXgW`{WKw0-Hlyz^!_;p6Ycdd|p zm!N#-MaX0RsJx=Qj$n~^xrLW}XEPLgyPDXUW9VqUxj~xkHQV`^B65Wue_lAqVh}1i^_+Uk3hL6Us3%D<&(TG7idq5-9ieUezx^S?4k3H=x+}O(=f;9+dSjY5z}^KUY2r z#op(j*!P0A|6chADEoLBN?g4L;FG1PY3sC0&2B!XpGVY4<@5+BF|E2to@-^iKoGI&F z55>=2P}aFkc?T5xHp9(u2jnp$yu`kfs-J-}@3YE#mG?u*7Z0d@5z6--R{d*G{PGx- z^L|3xf1vykl=Jy9l>J^-zNq~#L)rhIpv-?o+h0}w8_K=9s`@;2DRMTuk;9@;CNr$Q zPf;61WG;Vs=^5Vvr3@eyWDxlP(kT)jopOYV{DWdEt$Q@c4PvPQp;fK%2_D?3!g-Kk<*BrXFqZj`4A%I!^pw3mc2d( za>z+U{*Ss5Ij2t9)2Tp-n-j?Ry0%@^??&!KKCbOka2m-Y5<7>G60!psLq38?*@ny@ z3kYGF-V0#vLL?TX96)YEilR{ZkOJZ(9wLRSnwC%T-if>q=|R?)MOC&y$y4t~-iv$? zsUlku`JWp>K7m-|qsVIM?laxHOn1N8S}qQkdbUmoOtIe-cbnoKQ*1QseB`^J7Tbm& zCw5EP)LiMbnqp902qQZc*6fT6Vm}Pbz#cQ;#m<%+UgGTNv4RA>J>ZVRb)gPKV<`?w(Qr~o;03FYr|f%;>9L|opP-(O6)P$49#%l zt=Q?R#~LHfuP-Euf*BnzPnD*pM<*xju~}OlD^A$K$?@_`abT=uMr&p?Nc^PTc>3#R z%;R*bY>?VY*iQ2Kycu(KufaP>Tx7=N;3nto_=?S}j1b(;OW+DunnG85safMV`X+=DdWWFrV+L0L}{Z>OMtEW-5R<%9Xc3aH3W{xEa z{!gQ|`Yl=C#_ecrFy~F*>ZzA;dF1(tDHDkLmJy;68G~Uk@7FVxK$V@Bqj1qx6FU*+ z3#-bEpEO*-+}ZZT{WyQh3-HC1^BP%nO)(|3b1sTQTB&!c>6sTRoDc1(;)$|$0&>SX zU9~hku}_D(pzh=Dc`jN#izV%7MQ+J=%Q|k%tGGtrdS%yor5|Mp%bX3Pnk>$p?Kaa` z$u+R6l|D1gjZU&Sn3gM}7kj5|RALmOiA&@W(sYFhTj$JYmcuMAq#|(<8`-+7+?Ve) z*+od-1yc>%JVv+DYs&2gM+rZ;NjEc@a)V` zZvWaiF<{;^*|PA<8ivr(z)Rwg6yup~PN&vvUrQnS`_+gf|r>a4i-g>)I&$La(*MZU8BFw-38lGIh1!{j|c%=o8G zsk*??dGk$|$T>^TB+0-=UQ4nOi7cfJrSTeXt@d`pqcdJI{Stv-^*^pNP}(ZaHEL?j zWV7y0<7WOymcm9|5)eIU?vcuI33NS;;(8m?{Nl0%<2s?Yoh-DBmK_QjyVO8ZW)VYX F`7iW$c#i-8 literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.lang/nl.po b/comp/src/gb.report/.lang/nl.po new file mode 100644 index 00000000..f6aff12e --- /dev/null +++ b/comp/src/gb.report/.lang/nl.po @@ -0,0 +1,436 @@ +#Willy Raets < gbWilly@openmailbox.org >, 2014 +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report 3.10.90\n" +"PO-Revision-Date: 2017-08-26 19:45 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project: 1 +msgid "Report designer" +msgstr "Rapport ontwerper" + +#: .project: 2 +msgid "Report engine for gambas" +msgstr "Rapportage voor Gambas" + +#: FOptions.form: 25 +msgid "Size" +msgstr "Afmeting" + +#: FOptions.form: 34 +msgid "Paper size" +msgstr "Papierformaat" + +#: FOptions.form: 41 +msgid "Custom paper size" +msgstr "Aangepast papierformaat" + +#: FOptions.form: 45 +msgid "Width:" +msgstr "Breedte:" + +#: FOptions.form: 51 +msgid "Height:" +msgstr "Hoogte:" + +#: FOptions.form:67 FPreview.form:208 +msgid "Orientation" +msgstr "Oriëntatie" + +#: FOptions.form:72 FPreview.form:214 +msgid "Landscape" +msgstr "Landschap" + +#: FOptions.form:72 FPreview.form:214 +msgid "Portrait" +msgstr "Portret" + +#: FPreview.class:313 FPrint.form:192 +msgid "Cancel" +msgstr "Annuleren" + +#: FPreview.class:327 +msgid "Layout..." +msgstr "Lay-out..." + +#: FPreview.form:67 +msgid "Report preview" +msgstr "Rapport voorbeeld" + +#: FPreview.form:97 +msgid "Printing..." +msgstr "-" + +#: FPreview.form:117 +msgid "One page" +msgstr "Een pagina" + +#: FPreview.form:127 +msgid "Two pages" +msgstr "Twee pagina's" + +#: FPreview.form:136 +msgid "Full width" +msgstr "Volledige breedte" + +#: FPreview.form:145 +msgid "Real size" +msgstr "Echte afmeting" + +#: FPreview.form:177 +msgid "Printer" +msgstr "-" + +#: FPreview.form:189 +msgid "File" +msgstr "Bestand" + +#: FPreview.form:223 FPrint.form:109 +msgid "Paper" +msgstr "Papier" + +#: FPreview.form:229 +msgid "A3" +msgstr "-" + +#: FPreview.form:229 +msgid "A4" +msgstr "-" + +#: FPreview.form:229 +msgid "A5" +msgstr "-" + +#: FPreview.form:229 +msgid "B5" +msgstr "-" + +#: FPreview.form:229 Form1.form:78 +msgid "Custom" +msgstr "Aangepast" + +#: FPreview.form:229 +msgid "Executive" +msgstr "-" + +#: FPreview.form:229 +msgid "Legal" +msgstr "-" + +#: FPreview.form:229 +msgid "Letter" +msgstr "-" + +#: FPreview.form:243 FPrint.form:130 +msgid "Width" +msgstr "Breedte" + +#: FPreview.form:253 FPrint.form:148 +msgid "mm" +msgstr "mm" + +#: FPreview.form:258 FPrint.form:135 +msgid "Height" +msgstr "Hoogte" + +#: FPreview.form:287 +msgid "Recto Verso" +msgstr "-" + +#: FPreview.form:293 FPrint.form:100 +msgid "Long side" +msgstr "Lange zijde" + +#: FPreview.form:293 FPrint.form:100 +msgid "None" +msgstr "Geen" + +#: FPreview.form:293 FPrint.form:100 +msgid "Short side" +msgstr "Korte zijde" + +#: FPreview.form:308 +msgid "Gray Scale" +msgstr "Grijs schaal" + +#: FPreview.form:315 +msgid "Full Page" +msgstr "Volledige pagina" + +#: FPreview.form:321 +msgid "Reverse order" +msgstr "Omgekeerde volgorde" + +#: FPreview.form:327 +msgid "Collate copie" +msgstr "Soteer kopie" + +#: FPreview.form:348 FPrint.form:71 +msgid "Range" +msgstr "Bereik" + +#: FPreview.form:362 +msgid "Copies" +msgstr "Kopiën" + +#: FPreview.form:385 +msgid "Print" +msgstr "-" + +#: FPrint.form:38 +msgid "Printer Config" +msgstr "-" + +#: FPrint.form:58 +msgid "More..." +msgstr "Meer..." + +#: FPrint.form:89 +msgid "RectoVerso" +msgstr "-" + +#: FPrint.form:178 +msgid "Copies :" +msgstr "Kopiën:" + +#: Form1.form:56 +msgid "One Page" +msgstr "Een Pagina" + +#: Form1.form:64 +msgid "Two Page" +msgstr "Twee Pagina's" + +#: Form1.form:71 +msgid "FullWidth" +msgstr "VolledigeBreedte" + +#: OutputReport.report:74 OutputReport2.report:77 +msgid "Version 1.0" +msgstr "-" + +#: OutputReport.report:81 OutputReport2.report:84 +msgid "Date" +msgstr "Datum" + +#: OutputReport.report:96 OutputReport2.report:99 +msgid "Project Title:" +msgstr "-" + +#: OutputReport.report:103 OutputReport2.report:106 +msgid "Project No.:" +msgstr "-" + +#: OutputReport.report:110 OutputReport2.report:113 +msgid "Company:" +msgstr "Bedrijf:" + +#: OutputReport.report:117 OutputReport2.report:120 +msgid "Designer:" +msgstr "Ontwerper:" + +#: OutputReport.report:131 OutputReport2.report:134 +msgid "Base Plate ID:" +msgstr "-" + +#: OutputReport.report:186 OutputReport2.report:185 +msgid "Bearing Pressue" +msgstr "-" + +#: OutputReport.report:196 OutputReport2.report:195 +msgid "Node #" +msgstr "-" + +#: OutputReport.report:203 OutputReport2.report:202 +msgid "Brg. Press., psi" +msgstr "-" + +#: OutputReport.report:268 OutputReport2.report:266 +msgid "Anchor Rod Tension" +msgstr "-" + +#: OutputReport.report:278 OutputReport2.report:276 +msgid "Rod #" +msgstr "-" + +#: OutputReport.report:285 OutputReport2.report:283 +msgid "Tension, lbs" +msgstr "-" + +#: OutputReport.report:350 OutputReport2.report:353 +msgid "Page $PAGE of $NPAGE" +msgstr "Pagina $PAGE van $NPAGE" + +#: Report.class:108 +msgid "Section " +msgstr "Sectie" + +#: Report1.report:16 +msgid "#3" +msgstr "-" + +#: Report1.report:23 +msgid "#1" +msgstr "-" + +#: Report1.report:33 +msgid "COUCOU" +msgstr "-" + +#: Report1.report:62 +msgid "#2" +msgstr "-" + +#: Report10.report:27 Report6.report:16 +msgid "ReportLabel1" +msgstr "-" + +#: Report10.report:33 +msgid "ReportLabel2" +msgstr "-" + +#: Report10.report:39 +msgid "ReportLabel3" +msgstr "-" + +#: Report10.report:69 +msgid "Page $NPAGE" +msgstr "Pagina $NPAGE" + +#: Report11.report:17 +msgid "C'est la maison !" +msgstr "-" + +#: Report14.report:19 myReport2.report:40 +msgid "Section 1" +msgstr "-" + +#: Report14.report:39 myReport5.report:41 +msgid "Gambas" +msgstr "-" + +#: Report14.report:59 +msgid "All friends list !" +msgstr "-" + +#: Report14.report:77 +msgid "Gambas Report Demo" +msgstr "-" + +#: Report14.report:95 +msgid "Page $PAGE on $NPAGE " +msgstr "-" + +#: Report4.report:31 +msgid "IMPRESSION DU PLAN COMPTABLE" +msgstr "-" + +#: Report4.report:55 +msgid "Code" +msgstr "-" + +#: Report4.report:63 +msgid "Intitulé" +msgstr "-" + +#: Report4.report:106 +msgid "Page $PAGE sur $NPAGE" +msgstr "Pagina $PAGE van $NPAGE" + +#: Report7.report:64 rpTestShadowGrid.report:19 +msgid "coucou" +msgstr "-" + +#: Report8.report:24 +msgid "List of all my friends" +msgstr "Lijst van al mijn vrienden" + +#: Report8.report:34 +msgid "List Of My Friends" +msgstr "Lijst Van Mijn Vrienden" + +#: Report8.report:83 +msgid "Page $PAGE / $NPAGE" +msgstr "Pagina $PAGE / $NPAGE" + +#: myReport1.report:27 +msgid "First Page" +msgstr "Eerste Pagina" + +#: myReport1.report:38 +msgid "Report About" +msgstr "Over rapport" + +#: myReport1.report:46 +msgid "All my friends" +msgstr "Al mijn vrienden" + +#: myReport1.report:55 +msgid "Friend Table" +msgstr "Vrienden tabel" + +#: myReport1.report:61 +msgid "Report about all my frends" +msgstr "Rapport over al mijn vrienden" + +#: myReport1.report:68 +msgid "Test Database" +msgstr "-" + +#: myReport1.report:91 +msgid "Name" +msgstr "Naam" + +#: myReport1.report:101 +msgid "FirstName" +msgstr "Voornaam" + +#: myReport1.report:112 +msgid "Birth" +msgstr "Geboorte" + +#: myReport1.report:153 +msgid "$PAGE/$NPAGE" +msgstr "-" + +#: myReport2.report:51 +msgid "Facture" +msgstr "Factuur" + +#: myReport2.report:130 +msgid "Id" +msgstr "-" + +#: myReport2.report:139 +msgid "Designation" +msgstr "Aanwijzing" + +#: myReport2.report:147 +msgid "PU HT" +msgstr "-" + +#: myReport2.report:156 +msgid "Quantité" +msgstr "-" + +#: myReport2.report:164 +msgid "Total HT" +msgstr "Totale HT" + +#: myReport2.report:255 +msgid "Page $PAGE" +msgstr "Pagina $PAGE" + +#: myReport5.report:76 +msgid "gambas" +msgstr "-" + +#: myReport5.report:95 +msgid "Page $PAGE on $NPAGE" +msgstr "Pagina $PAGE op $NPAGE" + diff --git a/comp/src/gb.report/.lang/zh.mo b/comp/src/gb.report/.lang/zh.mo new file mode 100644 index 0000000000000000000000000000000000000000..3e63486688a7d71903421c479495e9821cfff998 GIT binary patch literal 4196 zcmaKtdvH|M9mjuKDiTT&6e$ArV6e23U9t&}xFTiq08+EbnryTvR&RE1Hn+36cjw-_ zV3_j^C*cb>oVJLjJLXz`pLLTZCnK)b#|bO=0l3tmX?-AZ&b_^uVv9J`XMi|0U%`^ZfD!;6jwI0KW>BfVY8V;3BXJWEYx+yTCcH6W{`{4aAVN zTk3x*_OoJlgFH?z$o&Su`QRB44~+@0fOBF00pxbqz#{OkQtyGh&QHYtuhidyM#$14 zko{Z^a=&}T4uCxFdXVF~8RR(b1i9ZXVXH6$avb)8Jl;N#H9-4#7+rfq49bmDr0$c(+3Ua>$$n6b~$9JXt z#~}N&ALRW$1mdASyf|)eNcmZi{r?Tf<6RZ|0}v1W4KE%)4|2bMf_H&z@Htryegj+! z@;ucb9%>RkC2R-zeC!2zo-UC6eIDfbkAOT+KgjVr4dS5@ytva!sG|057frk_apAt`@R?3cwpDtt|N zTe*}5{4}~9tysp24+%7Nnr^5e$+@22)^Jb9S&jGpp zd?{ZdTnggqqh%nsTPa)(^89N-9;ZSW7H$@95pES$3addLZyU(tM8$qgsDbQH8;FN& zytv&nVs{IVgFOEb$m5NG+-_9t-wLmS-0n}pYakx_3toJmOo80*bCBE3#ih%fC!7!R z_zT3o1LU~eDfV4bzd~3n<@X6ogh8n<6>b7~om<4-A$9}E^Y0WsF7;1{jVgMOUy>3t z8L&?BQSc$j^BuwIULUj;ya##!Duwti=Jc=+S_4)~{!wrX^i62Jlra>nXBG<4XZ8>u!| z5T@E_tg)u4skWhBRX3|xU8G)BHAG|aNM&6O)h4OdbPYF~zVd6T(-8CoR)buJm34!` zAl2z9EseM9>NeFG@U@{;MLU%5qT?4?txEgy&ed7i5ywDCqn54nK!t_XBgm?l>Mc{J z1{1r;>*h?Rkl|&!e&AxwYDb*7UdbWx^M&&=n^o&>tX6R6hCT{geq*7NwP#DQi<{`xdmI;?<&h|HB!Eb z)J!b8QPXR;YMpdu~U2$UBLjg8q*7rqgxN)`?s{1aO@QwHMN ziq9wIpAf0Y*!pfm-y`knv=%)b`l3}S#hSmo{Wr5H}6$*t* zH>u`$RZ*ShxPiE>nGQaF%MPnb-L$R@x+-dz=xiElvp@0HtOEywj+M0&IzpS$gQo6o z!2o6|iv@($-nOD>ZM0_AC#<9hN5#d#4#Yb$XyWQmyX!J(%`hKOi8c+vbsubwZwpk+ zwqsXXbvsaFCafe5S6EfF7;X{%Hva$5>+a2+??R*8iGFWrY-(VFyaT7b?$J*!_R-{p zOSzt50T<*u-|`0gCdUrW=BCC+y^ANjQ*V01-IP1>dhXb9jFum~oFDAZ?>kWF)qgnm z@&wP(+c|mRym*xBIXB(^4rUwh1~1JZKvO>(_Qv0!>9=2*1rOr=V%Y0?7lV3TFBV3~ z4ZP+3@&)on_j@Dz_*=@I?eQ)jqREMVuk%u&$#l;|{_+^*j-K;Q^>Gu9MefK6njGE7 za|`?>@Ara;En^>y3BwB+osA_tfz-92c3yU-9JVDL*6y cOdagX9Xrk66gG!9!iVoEVmS2n)W})-AMaU*)Bpeg literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/.lang/zh.po b/comp/src/gb.report/.lang/zh.po new file mode 100644 index 00000000..6c6301f3 --- /dev/null +++ b/comp/src/gb.report/.lang/zh.po @@ -0,0 +1,423 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report 3.6.90\n" +"PO-Revision-Date: 2014-12-16 11:07 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: zh\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Report designer" +msgstr "报表设计器" + +#: .project:2 +msgid "Report engine for gambas" +msgstr "Gambas报表引擎" + +#: FOptions.form:25 +msgid "Size" +msgstr "尺寸" + +#: FOptions.form:34 +msgid "Paper size" +msgstr "纸张尺寸" + +#: FOptions.form:41 +msgid "Custom paper size" +msgstr "自定义纸张大小" + +#: FOptions.form:45 +msgid "Width:" +msgstr "宽度:" + +#: FOptions.form:51 +msgid "Height:" +msgstr "高度:" + +#: FOptions.form:67 FPreview.form:208 +msgid "Orientation" +msgstr "纸张方向" + +#: FOptions.form:72 FPreview.form:214 +msgid "Landscape" +msgstr "横向" + +#: FOptions.form:72 FPreview.form:214 +msgid "Portrait" +msgstr "纵向" + +#: FPreview.class:313 FPrint.form:192 +msgid "Cancel" +msgstr "取消" + +#: FPreview.class:327 +msgid "Layout..." +msgstr "布局..." + +#: FPreview.form:67 +msgid "Report preview" +msgstr "预览报表" + +#: FPreview.form:97 +msgid "Printing..." +msgstr "打印中..." + +#: FPreview.form:117 +msgid "One page" +msgstr "一页" + +#: FPreview.form:127 +msgid "Two pages" +msgstr "两页" + +#: FPreview.form:136 +msgid "Full width" +msgstr "全宽" + +#: FPreview.form:145 +msgid "Real size" +msgstr "实际尺寸" + +#: FPreview.form:177 +msgid "Printer" +msgstr "打印机" + +#: FPreview.form:189 +msgid "File" +msgstr "文件" + +#: FPreview.form:223 FPrint.form:109 +msgid "Paper" +msgstr "纸张" + +#: FPreview.form:229 +msgid "A3" +msgstr "-" + +#: FPreview.form:229 +msgid "A4" +msgstr "-" + +#: FPreview.form:229 +msgid "A5" +msgstr "-" + +#: FPreview.form:229 +msgid "B5" +msgstr "-" + +#: FPreview.form:229 Form1.form:78 +msgid "Custom" +msgstr "自定义" + +#: FPreview.form:229 +msgid "Executive" +msgstr "行政" + +#: FPreview.form:229 +msgid "Legal" +msgstr "法律" + +#: FPreview.form:229 +msgid "Letter" +msgstr "信函" + +#: FPreview.form:243 FPrint.form:130 +msgid "Width" +msgstr "宽度" + +#: FPreview.form:253 FPrint.form:148 +msgid "mm" +msgstr "毫米" + +#: FPreview.form:258 FPrint.form:135 +msgid "Height" +msgstr "高度" + +#: FPreview.form:287 +msgid "Recto Verso" +msgstr "双面" + +#: FPreview.form:293 FPrint.form:100 +msgid "Long side" +msgstr "长边" + +#: FPreview.form:293 FPrint.form:100 +msgid "None" +msgstr "无" + +#: FPreview.form:293 FPrint.form:100 +msgid "Short side" +msgstr "短边" + +#: FPreview.form:308 +msgid "Gray Scale" +msgstr "灰度" + +#: FPreview.form:315 +msgid "Full Page" +msgstr "整页" + +#: FPreview.form:321 +msgid "Reverse order" +msgstr "逆序" + +#: FPreview.form:327 +msgid "Collate copie" +msgstr "校对稿" + +#: FPreview.form:348 FPrint.form:71 +msgid "Range" +msgstr "范围" + +#: FPreview.form:362 +msgid "Copies" +msgstr "份数" + +#: FPreview.form:385 +msgid "Print" +msgstr "打印" + +#: FPrint.form:38 +msgid "Printer Config" +msgstr "打印机设置" + +#: FPrint.form:58 +msgid "More..." +msgstr "更多..." + +#: FPrint.form:89 +msgid "RectoVerso" +msgstr "双面" + +#: FPrint.form:178 +msgid "Copies :" +msgstr "份数:" + +#: Form1.form:56 +msgid "One Page" +msgstr "一页" + +#: Form1.form:64 +msgid "Two Page" +msgstr "两页" + +#: Form1.form:71 +msgid "FullWidth" +msgstr "全宽" + +#: OutputReport.report:74 OutputReport2.report:77 +msgid "Version 1.0" +msgstr "版本1.0" + +#: OutputReport.report:81 OutputReport2.report:84 +msgid "Date" +msgstr "日期" + +#: OutputReport.report:96 OutputReport2.report:99 +msgid "Project Title:" +msgstr "项目标题:" + +#: OutputReport.report:103 OutputReport2.report:106 +msgid "Project No.:" +msgstr "项目编号:" + +#: OutputReport.report:110 OutputReport2.report:113 +msgid "Company:" +msgstr "公司:" + +#: OutputReport.report:117 OutputReport2.report:120 +msgid "Designer:" +msgstr "设计者:" + +#: OutputReport.report:131 OutputReport2.report:134 +msgid "Base Plate ID:" +msgstr "-" + +#: OutputReport.report:186 OutputReport2.report:185 +msgid "Bearing Pressue" +msgstr "-" + +#: OutputReport.report:196 OutputReport2.report:195 +msgid "Node #" +msgstr "-" + +#: OutputReport.report:203 OutputReport2.report:202 +msgid "Brg. Press., psi" +msgstr "-" + +#: OutputReport.report:268 OutputReport2.report:266 +msgid "Anchor Rod Tension" +msgstr "-" + +#: OutputReport.report:278 OutputReport2.report:276 +msgid "Rod #" +msgstr "-" + +#: OutputReport.report:285 OutputReport2.report:283 +msgid "Tension, lbs" +msgstr "-" + +#: OutputReport.report:350 OutputReport2.report:353 +msgid "Page $PAGE of $NPAGE" +msgstr "-" + +#: Report.class:108 +msgid "Section " +msgstr "节" + +#: Report1.report:16 +msgid "#3" +msgstr "-" + +#: Report1.report:23 +msgid "#1" +msgstr "-" + +#: Report1.report:33 +msgid "COUCOU" +msgstr "-" + +#: Report1.report:62 +msgid "#2" +msgstr "-" + +#: Report10.report:27 Report6.report:16 +msgid "ReportLabel1" +msgstr "-" + +#: Report10.report:33 +msgid "ReportLabel2" +msgstr "-" + +#: Report10.report:39 +msgid "ReportLabel3" +msgstr "-" + +#: Report10.report:69 +msgid "Page $NPAGE" +msgstr "-" + +#: Report11.report:17 +msgid "C'est la maison !" +msgstr "-" + +#: Report4.report:31 +msgid "IMPRESSION DU PLAN COMPTABLE" +msgstr "印刷计划" + +#: Report4.report:55 +msgid "Code" +msgstr "-" + +#: Report4.report:63 +msgid "Intitulé" +msgstr "-" + +#: Report4.report:106 +msgid "Page $PAGE sur $NPAGE" +msgstr "-" + +#: Report7.report:64 rpTestShadowGrid.report:19 +msgid "coucou" +msgstr "-" + +#: Report8.report:24 +msgid "List of all my friends" +msgstr "-" + +#: Report8.report:34 +msgid "List Of My Friends" +msgstr "-" + +#: Report8.report:83 +msgid "Page $PAGE / $NPAGE" +msgstr "-" + +#: myReport1.report:27 +msgid "First Page" +msgstr "-" + +#: myReport1.report:38 +msgid "Report About" +msgstr "-" + +#: myReport1.report:46 +msgid "All my friends" +msgstr "-" + +#: myReport1.report:55 +msgid "Friend Table" +msgstr "-" + +#: myReport1.report:61 +msgid "Report about all my frends" +msgstr "-" + +#: myReport1.report:68 +msgid "Test Database" +msgstr "-" + +#: myReport1.report:91 +msgid "Name" +msgstr "-" + +#: myReport1.report:101 +msgid "FirstName" +msgstr "-" + +#: myReport1.report:112 +msgid "Birth" +msgstr "-" + +#: myReport1.report:153 +msgid "$PAGE/$NPAGE" +msgstr "-" + +#: myReport2.report:40 +msgid "Section 1" +msgstr "-" + +#: myReport2.report:51 +msgid "Facture" +msgstr "-" + +#: myReport2.report:130 +msgid "Id" +msgstr "-" + +#: myReport2.report:139 +msgid "Designation" +msgstr "-" + +#: myReport2.report:147 +msgid "PU HT" +msgstr "-" + +#: myReport2.report:156 +msgid "Quantité" +msgstr "-" + +#: myReport2.report:164 +msgid "Total HT" +msgstr "-" + +#: myReport2.report:255 +msgid "Page $PAGE" +msgstr "-" + +#: myReport5.report:41 +msgid "Gambas" +msgstr "-" + +#: myReport5.report:75 +msgid "gambas" +msgstr "-" + +#: myReport5.report:94 +msgid "Page $PAGE on $NPAGE" +msgstr "-" + diff --git a/comp/src/gb.report/.project b/comp/src/gb.report/.project new file mode 100644 index 00000000..749d298d --- /dev/null +++ b/comp/src/gb.report/.project @@ -0,0 +1,25 @@ +# Gambas Project File 3.0 +Title=Report designer +Startup=Test +Icon=printer1.png +Version=3.10.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.db +Component=gb.settings +Component=gb.net +Component=gb.net.curl +Component=gb.report +Description="Report engine for gambas" +Authors="Fabien Bodard" +TabSize=2 +Translate=1 +Language=fr +Type=Component +Maintainer=benoit +Vendor=Princeton +Address=benoit@localhost +License=General Public Licence +Packager=1 diff --git a/comp/src/gb.report/.src/Borders/ReportBorder.class b/comp/src/gb.report/.src/Borders/ReportBorder.class new file mode 100644 index 00000000..a839380b --- /dev/null +++ b/comp/src/gb.report/.src/Borders/ReportBorder.class @@ -0,0 +1,209 @@ +' Gambas class file + +Export + +Static Public Const None As Integer = 0 +Static Public Const Solid As Integer = 1 +Static Private $aShadowStyle As String[] = ["none", "solid", "gradiant"] +'Static Private $aAlign As String[] = Classes["Align"].Symbols +Private hTop As New _ReportBorderSide +Private hBottom As New _ReportBorderSide +Private hLeft As New _ReportBorderSide +Private hRight As New _ReportBorderSide +Private hRCorner As New _ReportRoundCorner + + +Public _Left As Float +Public _Right As Float +Public _Top As Float +Public _Bottom As Float + +Property Read Top As _ReportBorderSide + +Property Read {Left} As _ReportBorderSide + +Property Read bottom As _ReportBorderSide + +Property Read {Right} As _ReportBorderSide + +Property Read RoundCorner As _ReportRoundCorner + +Property Width As String +Property Brush As ReportBrush + +Public Style As Integer + + +' Static Private Sub ResetBorder(hReportBorder As ReportBorder) As Boolean +' +' With hReportBorder +' .Bottom = False +' .Top = False +' .Left = False +' .Right = False +' End With +' +' Return True +' +' End + +Static Public Function _get(sValue As String) As ReportBorder + + Dim hReportBorder As New ReportBorder + + Dim sBorder As String + Dim aScan As String[] + + If Not sValue Then Return hReportBorder + hReportBorder.Style = ReportBorder.Solid + hReportBorder.RoundCorner._Active = False + For Each sBorder In Split(sValue, ";") + + aScan = Scan(sBorder, "*:*") + Select Case LCase(Trim(aScan[0])) + Case "border" + FillObject(hReportBorder, aScan[1]) + Case "left" + FillObject(hReportBorder.Left, aScan[1]) + Case "right" + FillObject(hReportBorder.Right, aScan[1]) + Case "bottom" + FillObject(hReportBorder.bottom, aScan[1]) + Case "top" + FillObject(hReportBorder.Top, aScan[1]) + Case "topleftcorner" + hReportBorder.RoundCorner.TopLeft = aScan[1] + Case "toprightcorner" + hReportBorder.RoundCorner.TopRight = aScan[1] + Case "bottomleftcorner" + hReportBorder.RoundCorner.BottomLeft = aScan[1] + Case "bottomrightcorner" + hReportBorder.RoundCorner.BottomRight = aScan[1] + + + + End Select + + Next + +Finally + Return hReportBorder + +End + +Static Private Sub FillObject(hObj As Object, sValue As String) + + Dim sBrush As String + + For Each sBrush In Split(sValue, " ") + + If IsDigit(Left(sBrush)) Then + Try hObj.Width = sBrush + Else + Try hObj.Brush = ReportBrush[sBrush] + If hObj.Brush = Null Then hObj.Brush = ReportBrush["&H0"] + Endif + Next + +End + + + +Public Function ToString() As String + + Dim aValue As New String[] + + If Me.Style = ReportBorder.None Then Return 'aValue.Add("None") + 'If Me.Style = ReportBorder.Solid Then aValue.Add("Solid") + aValue.Add(Me.Width) + If Me.Top Then aValue.Add("Top") + If Me.Bottom Then aValue.Add("Bottom") + If Me.Left Then aValue.Add("Left") + If Me.Right Then aValue.Add("Right") + aValue.Add("#" & Hex(Me.Color, 6)) + Return aValue.Join() + +End + +Private Function Top_Read() As _ReportBorderSide + + Return hTop + +End + +Private Function Left_Read() As _ReportBorderSide + + Return hLeft + +End + +Private Function bottom_Read() As _ReportBorderSide + + Return hBottom + +End + +Private Function Width_Read() As String + + Return hLeft.Width + +End + +Private Sub Width_Write(Value As String) + + hLeft.Width = Value + hTop.Width = Value + hBottom.Width = Value + hRight.Width = Value + +End + +Private Function Brush_Read() As ReportBrush + + Return hLeft.Brush + +End + +Private Sub Brush_Write(Value As ReportBrush) + + hLeft.Brush = Value + hTop.Brush = Value + hBottom.Brush = Value + hRight.Brush = Value + +End + +Private Function Right_Read() As _ReportBorderSide + + Return hRight + +End + +Private Function RoundCorner_Read() As _ReportRoundCorner + + Return hRCorner + +End + + + +Public Sub _SetUnifiedValues() + + Dim hSizeParse As TSizeParse + + hSizeParse = New TSizeParse(hLeft.Width) + _Left = hSizeParse.GetValue() + hSizeParse = New TSizeParse(hRight.Width) + _Right = hSizeParse.GetValue() + hSizeParse = New TSizeParse(hTop.Width) + _Top = hSizeParse.GetValue() + hSizeParse = New TSizeParse(hBottom.Width) + _Bottom = hSizeParse.GetValue() + + hRCorner._SetUnifiedValues() + + + +End + + diff --git a/comp/src/gb.report/.src/Borders/_ReportBorderSide.class b/comp/src/gb.report/.src/Borders/_ReportBorderSide.class new file mode 100644 index 00000000..968e7c3c --- /dev/null +++ b/comp/src/gb.report/.src/Borders/_ReportBorderSide.class @@ -0,0 +1,16 @@ +' Gambas class file + +Export +Public Width As String +Public Brush As New ReportBrush +Public Round1 As String +Public Round2 As String +Public _Width As Float + +Public Sub _SetUnifiedValues() + Dim hSizeParse As New TSizeParse(Width) + + _Width = hSizeParse.GetValue() + + +End diff --git a/comp/src/gb.report/.src/Borders/_ReportRoundCorner.class b/comp/src/gb.report/.src/Borders/_ReportRoundCorner.class new file mode 100644 index 00000000..2cd2b0fe --- /dev/null +++ b/comp/src/gb.report/.src/Borders/_ReportRoundCorner.class @@ -0,0 +1,136 @@ +' Gambas class file + +Public _Active As Boolean = False +Private $sTopLeft As String = "0cm" +Private $sTopRight As String = "0cm" +Private $sBottomLeft As String = "0cm" +Private $sBottomRight As String = "0cm" + +Property TopLeft As String +Property TopRight As String +Property BottomLeft As String +Property BottomRight As String + +Public _TopLeft1 As Float +Public _TopLeft2 As Float +Public _TopRight1 As Float +Public _TopRight2 As Float +Public _BottomLeft1 As Float +Public _BottomLeft2 As Float +Public _BottomRight1 As Float +Public _BottomRight2 As Float + +Private Function TopLeft_Read() As String + + Return $sTopLeft + +End + +Private Sub TopLeft_Write(Value As String) + + $sTopLeft = Value + _Active = True + +End + +Private Function TopRight_Read() As String + + Return $sTopRight + +End + +Private Sub TopRight_Write(Value As String) + + $sTopRight = Value + _Active = True + +End + +Private Function BottomLeft_Read() As String + + Return $sBottomLeft + +End + +Private Sub BottomLeft_Write(Value As String) + + $sBottomLeft = Value + _Active = True + +End + +Private Function BottomRight_Read() As String + + Return $sBottomRight + +End + +Private Sub BottomRight_Write(Value As String) + + $sBottomRight = Value + _Active = True + +End + +Public Sub _SetUnifiedValues() + + Dim aEl As String[] + + Dim hSizeParse As TSizeParse + + + ael = Split($sTopLeft, " ") + If ael.Count = 1 Then + hSizeParse = New TSizeParse(ael[0]) + _TopLeft1 = hSizeParse.GetValue() + _TopLeft2 = _TopLeft1 + Else + If ael.Count = 2 Then + hSizeParse = New TSizeParse(ael[0]) + _TopLeft1 = hSizeParse.GetValue() + hSizeParse = New TSizeParse(ael[1]) + _TopLeft2 = hSizeParse.GetValue() + Endif + Endif + + ael = Split($sTopRight, " ") + If ael.Count = 1 Then + hSizeParse = New TSizeParse(ael[0]) + _TopRight1 = hSizeParse.GetValue() + _TopRight2 = _TopRight1 + Else + If ael.Count = 2 Then + hSizeParse = New TSizeParse(ael[0]) + _TopRight1 = hSizeParse.GetValue() + hSizeParse = New TSizeParse(ael[1]) + _TopRight2 = hSizeParse.GetValue() + Endif + Endif + ael = Split($sBottomRight, " ") + If ael.Count = 1 Then + hSizeParse = New TSizeParse(ael[0]) + _BottomRight1 = hSizeParse.GetValue() + _BottomRight2 = _BottomRight1 + Else + If ael.Count = 2 Then + hSizeParse = New TSizeParse(ael[0]) + _BottomRight1 = hSizeParse.GetValue() + hSizeParse = New TSizeParse(ael[1]) + _BottomRight2 = hSizeParse.GetValue() + Endif + Endif + ael = Split($sBottomLeft, " ") + If ael.Count = 1 Then + hSizeParse = New TSizeParse(ael[0]) + _BottomLeft1 = hSizeParse.GetValue() + _BottomLeft2 = _BottomLeft1 + Else + If ael.Count = 2 Then + hSizeParse = New TSizeParse(ael[0]) + _BottomLeft1 = hSizeParse.GetValue() + hSizeParse = New TSizeParse(ael[1]) + _BottomLeft2 = hSizeParse.GetValue() + Endif + Endif + +End diff --git a/comp/src/gb.report/.src/BoxShadow/FReportBoxEditor.class b/comp/src/gb.report/.src/BoxShadow/FReportBoxEditor.class new file mode 100644 index 00000000..4d8ed076 --- /dev/null +++ b/comp/src/gb.report/.src/BoxShadow/FReportBoxEditor.class @@ -0,0 +1,26 @@ +' Gambas class file + +Private hBorder As New ReportBorder +Public Sub Form_Open() + + + +End + +Public Sub DrawingArea1_Draw() + + Dim X, Y, Width, Height As Float + + X = Paint.Width / 3 / 2 + Y = Paint.Height / 3 / 2 + Width = Paint.Width / 3 * 2 + Height = Paint.Height / 3 * 2 + + 'Paint.FillRect(0, 0, Paint.Width, Paint.Height, Color.White) + + + Paint.Rectangle(X, Y, Width, Height) + Paint.Stroke + + +End diff --git a/comp/src/gb.report/.src/BoxShadow/FReportBoxEditor.form b/comp/src/gb.report/.src/BoxShadow/FReportBoxEditor.form new file mode 100644 index 00000000..89e4d028 --- /dev/null +++ b/comp/src/gb.report/.src/BoxShadow/FReportBoxEditor.form @@ -0,0 +1,11 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,78,57) + Arrangement = Arrange.Vertical + { DrawingArea1 DrawingArea + MoveScaled(4,5,67,44) + Background = &HFFFFFF& + Expand = True + } +} diff --git a/comp/src/gb.report/.src/BoxShadow/ReportBoxShadow.class b/comp/src/gb.report/.src/BoxShadow/ReportBoxShadow.class new file mode 100644 index 00000000..b8f3e6b9 --- /dev/null +++ b/comp/src/gb.report/.src/BoxShadow/ReportBoxShadow.class @@ -0,0 +1,222 @@ +' Gambas class file + +Private $aBoxShadow As New _ReportBoxShadow[] + +Property XOffset As String +Property YOffset As String +Property Blur As String +Property Spread As String +Property Inset As Boolean +Property Color As Integer +Property Read Count As Integer +Property Read Max As Integer + + +Property Read _XOffset As Float +Property Read _YOffset As Float +Property Read _Blur As Float +Property Read _Spread As Float +Property Read _Active As Boolean + + +Public Sub _new() + + Dim hBoxShadow As New _ReportBoxShadow + $aBoxShadow.Add(hBoxShadow) + +End + +Static Public Function _get(sValue As String) As ReportBoxShadow + + Dim hBoxShadow As New ReportBoxShadow + Dim s As String + Dim i As Integer + For Each s In Split(sValue, " ") + If IsDigit(Left(s)) Then + + Select Case i + Case 0 + hBoxShadow.XOffset = s + Case 1 + hBoxShadow.YOffset = s + Case 2 + hBoxShadow.Blur = s + Case 3 + hBoxShadow.Spread = s + End Select + Inc i + Continue + Endif + If LCase(s) = "inset" Then + hBoxShadow.Inset = True + Continue + Endif + Try hBoxShadow.Color = Val(s) + + Next + Return hBoxShadow + +End + + + +' Public Function _get(Index As Integer) As _ReportBoxShadow +' +' Return $aBoxShadow[Index] +' +' End + +Public Sub Add(Optional XOffset As String, YOffset As String, iColor As Integer, Spread As String, Blur As String, Inset As Boolean) + + Dim hBoxShadow As New _ReportBoxShadow + + If XOffset Then hBoxShadow.XOffset = XOffset + If YOffset Then hBoxShadow.YOffset = YOffset + If iColor Then hBoxShadow.Color = iColor + If SPread Then hBoxShadow.Spread = Spread + If Blur Then hBoxShadow.Blur = Blur + hBoxShadow.Inset = Inset + $aBoxShadow.Add(hBoxShadow) + +End + +Public Sub Remove(Index As Integer) + + $aBoxShadow.Remove(Index) + +End + + + +Private Function XOffset_Read() As String + + Return $aBoxShadow[0].XOffset + +End + +Private Sub XOffset_Write(Value As String) + + $aBoxShadow[0].XOffset = Value + +End + +Private Function YOffset_Read() As String + + Return $aBoxShadow[0].YOffset + +End + +Private Sub YOffset_Write(Value As String) + + $aBoxShadow[0].YOffset = Value + +End + +Private Function Blur_Read() As String + + Return $aBoxShadow[0].Blur + +End + +Private Sub Blur_Write(Value As String) + + $aBoxShadow[0].Blur = Value + +End + +Private Function Spread_Read() As String + + Return $aBoxShadow[0].Spread + +End + +Private Sub Spread_Write(Value As String) + + $aBoxShadow[0].Spread = Value + +End + +Private Function Inset_Read() As Boolean + + Return $aBoxShadow[0].Inset + +End + +Private Sub Inset_Write(Value As Boolean) + + $aBoxShadow[0].Inset = Value + +End + +Private Function Color_Read() As Integer + + Return $aBoxShadow[0].Color + +End + +Private Sub Color_Write(Value As Integer) + + $aBoxShadow[0].Color = Value + +End + +Private Function Count_Read() As Integer + + Return $aBoxShadow.Count + +End + +Private Function Max_Read() As Integer + + Return $aBoxShadow.Max + +End + +Public Sub _SetUnifiedValues() + + Dim i As Integer + Dim SizeParse As TSizeParse + For i = 0 To $aBoxShadow.Max + 'With + $aBoxShadow[i]._XOffset = TSizeParse[$aBoxShadow[i].XOffset].GetValue() + $aBoxShadow[i]._YOffset = TSizeParse[$aBoxShadow[i].YOffset].GetValue() + $aBoxShadow[i]._Spread = TSizeParse[$aBoxShadow[i].Spread].GetValue() + $aBoxShadow[i]._Blur = TSizeParse[$aBoxShadow[i].Blur].GetValue() + + '._Blur = TSizeParse[.Blur].GetValue() + 'End With + + + Next + +End + +Private Function _XOffset_Read() As Float + + Return $aBoxShadow[0]._XOffset + +End + +Private Function _YOffset_Read() As Float + + Return $aBoxShadow[0]._YOffset + +End + +Private Function _Blur_Read() As Float + + Return $aBoxShadow[0]._Blur + +End + +Private Function _Spread_Read() As Float + + Return $aBoxShadow[0]._Spread + +End + +Private Function _Active_Read() As Boolean + + Return Not ($aBoxShadow[0]._XOffset = 0 And $aBoxShadow[0]._YOffset = 0 And $aBoxShadow[0]._Blur = 0 And $aBoxShadow[0]._Spread = 0) + +End diff --git a/comp/src/gb.report/.src/BoxShadow/_ReportBoxShadow.class b/comp/src/gb.report/.src/BoxShadow/_ReportBoxShadow.class new file mode 100644 index 00000000..c204ac4c --- /dev/null +++ b/comp/src/gb.report/.src/BoxShadow/_ReportBoxShadow.class @@ -0,0 +1,13 @@ +' Gambas class file + +Public XOffset As String '= "8mm" +Public YOffset As String '= "2mm" +Public Spread As String +Public Inset As Boolean +Public Color As Integer +Public Blur As String + +Public _XOffset As Float +Public _YOffset As Float +Public _Spread As Float +Public _Blur As Float diff --git a/comp/src/gb.report/.src/Brush/ReportBrush.class b/comp/src/gb.report/.src/Brush/ReportBrush.class new file mode 100644 index 00000000..38f19006 --- /dev/null +++ b/comp/src/gb.report/.src/Brush/ReportBrush.class @@ -0,0 +1,222 @@ +' Gambas class file + +Export + +Public _Type As Integer +Public _X As Float +Public _Y As Float +Public _X2 As Float +Public _Y2 As Float +Public _radius As Float +Public _Image As Image +Public _Color As Integer[] = [0, &HFFFFFF&] +Public _Pos As Float[] = [0, 1] +Private _ImageDir As String + +Public Function _PaintBrush(X1 As Integer, Y1 As Integer, X2 As Integer, Y2 As Integer) As PaintBrush + + Dim hBrush As PaintBrush + Dim fradius As Float + Dim Width As Integer = X2 - X1 + Dim Height As Integer = Y2 - Y1 + + Select Case Me._Type + Case 0 + hBrush = Paint.Color(_Color[0]) + Case 1 + hBrush = paint.Image(_Image, X1, Y1) + Case 2 + hBrush = paint.LinearGradient(Width * _X, Height * _Y, Width * _X2, Height * _Y2, _Color, _Pos) + Case 3 + fradius = Width * _radius + hBrush = paint.RadialGradient(Width * _X, Height * _Y, fradius, Width * _X2, Height * _Y2, _Color, _Pos) + End Select + hBrush.Translate(x1, Y1) + Return hBrush + +End + +Static Public Sub _get(sValue As String) As ReportBrush + + Dim hBrush As New ReportBrush + Dim iPos As Integer + Dim sType As String + + Dim ars As String[] + + + + If Not svalue Then Return Null + + sValue = Trim(sValue) + + iPos = InStr(sValue, "(") + + If iPos Then sType = Left(sValue, iPos - 1) + 'pas de parenthese de fin + If ipos And Not (sValue Ends ")") Then + Error.Raise + Else + If iPos Then svalue = Left(Mid(svalue, ipos + 1), -1) + Endif + ars = Split(sValue, ",", "[]") + Select Case LCase(sType) + Case "image" + hBrush._Type = 1 + + hBrush._Image = Image.Load(ars[0]) + hBrush._ImageDir = ars[0] + Case "radialgradient" + hBrush._Type = 3 + hBrush._X = CFloat(ars[0]) + hBrush._Y = CFloat(ars[1]) + hBrush._Radius = CFloat(ars[2]) + hBrush._X2 = CFloat(ars[3]) + hBrush._Y2 = CFloat(ars[4]) + hBrush._Color = GetIntegerArray(ars[5]) + hBrush._Pos = GetFloatArray(ars[6]) + + Case "lineargradient" + + hBrush._Type = 2 + hBrush._X = CFloat(ars[0]) + hBrush._Y = CFloat(ars[1]) + hBrush._X2 = CFloat(ars[2]) + hBrush._Y2 = CFloat(ars[3]) + hBrush._Color = GetIntegerArray(ars[4]) + hBrush._Pos = GetFloatArray(ars[5]) + + Case Else + 'correction de l'hexon + '= Val(Replace(ars[0], "#", "&H") & "00&") + If ars[0] Begins "#" Then ars[0] = Mid(ars[0], 2) + hBrush._Color[0] = Val("&H" & IIf(Len(ars[0]) = 8, ars[0], 00 & ars[0])) + End Select + Return hBrush + +Finally + If hBrush._Color.Count = 0 Then hBrush._Color = [0, &hFFFFFF&] + If hBrush._Color.Count < 2 Then hBrush._Color.Add(&hFFFFFF&) + If hBrush._Pos.Count < 2 Then hBrush._Pos = [0.0, 1.0] + + +Catch + Return hBrush + +End + +Static Private Function GetIntegerArray(sValue As String) As Integer[] + + Dim ari As New Integer[] + Dim s As String + + For Each s In Split(sValue) + If s Begins "#" Then s = Mid(s, 2) + + ari.Add(Val("&H" & IIf(Len(s) = 8, s, "00" & s))) + Next + + Return ari + +End + +Static Private Function GetFloatArray(sValue As String) As Float[] + + Dim arf As New Float[] + Dim s As String + + For Each s In Split(sValue) + arf.Add(CFloat(s)) + Next + + Return arf + +End + +Public Function ToString() As String + + Dim i As Integer + Dim f As Float + Dim sValue As String + + Select Case _Type + Case 0 + sValue = "&H" & Hex(_Color[0], 8) & "&" + Case 2, 3 + If _Type = 3 Then + sValue = "RadialGradient(" & _X & "," & _Y & "," & _Radius & "," & _X2 & "," & _Y2 & ",[" + + Else + sValue = "LinearGradient(" & _X & "," & _Y & "," & _X2 & "," & _Y2 & ",[" + Endif + + For Each i In _Color + sValue &= "&H" & Hex(i, 6) & "&," + + Next + + sValue = Left(sValue, -1) + + sValue &= "],[" + + For Each f In _Pos + sValue &= f & "," + + Next + sValue = Left(sValue, -1) + + sValue &= "])" + Case 1 + sValue = "Image(" & _ImageDir & ")" + + End Select + + Return svalue + +End + +Static Public Function Color(iColor As Integer) As ReportBrush + + Dim hBrush As New ReportBrush + + hBrush._Color[0] = iColor + hBrush._Type = 0 + Return hBrush + +End + +Static Public Function LinearGradient(X As Float, Y As Float, X2 As Float, Y2 As Float, aColor As Integer[], aPos As Float[]) As ReportBrush + Dim hBrush As New ReportBrush + + hBrush._X = X + hBrush._Y = Y + hBrush._X2 = X2 + hBrush._Y2 = Y2 + hBrush._Color = aColor + hBrush._Pos = aPos + hBrush._Type = 2 + Return hBrush +End + +Static Public Function RadialGradient(X As Float, Y As Float, Radius As Float, X2 As Float, Y2 As Float, aColor As Integer[], aPos As Float[]) As ReportBrush + Dim hBrush As New ReportBrush + + hBrush._X = X + hBrush._Y = Y + hBrush._X2 = X2 + hBrush._Y2 = Y2 + hBrush._radius = Radius + hBrush._Color = aColor + hBrush._Pos = aPos + hBrush._Type = 3 + Return hBrush +End + +Static Public Function Image(hImage As Image) As ReportBrush + Dim hBrush As New ReportBrush + + hBrush._Image = hImage + hBrush._Type = 1 + Return hBrush + +End diff --git a/comp/src/gb.report/.src/Controls/ReportControl.class b/comp/src/gb.report/.src/Controls/ReportControl.class new file mode 100644 index 00000000..acf0c2cb --- /dev/null +++ b/comp/src/gb.report/.src/Controls/ReportControl.class @@ -0,0 +1,596 @@ +' Gambas class file + +Export +Create Private + +Public Const _IsControl As Boolean = True +Public Const _IsContainer As Boolean = False +Public Const _Properties As String = "Left{ReportCoord},Top{ReportCoord},Width{ReportCoord},Height{ReportCoord},Brush{ReportBrush},Visible=True,Fixed,Font,Padding,Ignore,Expand,AutoResize,Tag,Range" +Public Const _Family As String = "Report" +Public _SizeInt As TSizeHint +Public _Count As Integer = 1 +Public _Finished As Boolean +Private $iDataindex As Integer +Property _DataIndex As Integer + +Private $iIndex As Integer + +Public Name As String +'Object Management +Static Public _ObjectFromId As New Collection +Static Public _iCurPagePos As Integer +Static Private $iLastId As Integer + +Private $iMyId As Integer +Private $iParentId As Integer + +Private $vTag As Variant +Private $fPadding As Float +Private $iReportId As Integer + +Private $hBrush As ReportBrush +Private $iColor As Integer + +Private $fLeft As Float = 0.0 +Private $fTop As Float = 0.0 +Private $fWidth As Float = 0.0 +Private $fHeight As Float = 0.0 +Private $sHeight As String = "0cm" +Private $sLeft As String = "0cm" +Private $sWidth As String = "0cm" +Private $sTop As String = "0cm" +Private $hPadding As New ReportPadding +'Private $sPadding As String = "0cm" + +Private $iVisible As Boolean = True +Private $hFont As New Font +Private $bExpand As Boolean +Private $bFixed As Boolean +Private $bAutoresize As Boolean = False +Private $bRelativeLeft As Boolean +Private $bRelativeTop As Boolean +Private $bRelativeWidth As Boolean +Private $bRelativeHeight As Boolean +Private $bRelativePadding As Boolean +Private $bIgnore As Boolean = False +Private $sRange As String +Property Read Id As Integer +Property Read Parent As ReportContainer +Property Tag As Variant +Property Padding As ReportPadding + +Property Brush As ReportBrush +'Property {Color} As Integer + +Property Read _Top As Float ''Top of the control in cm +Property Read _Height As Float ''Height of the control in cm +Property Read _Width As Float ''Width of the control in cm +Property Read _Left As Float ''Left of the control in cm +Property Read _Padding As Float '' Padding of the control in cm +Property Read _RelativeLeft As Boolean ''Use percentage for Left pos? +Property Read _RelativeTop As Boolean ''Use percentage for Top Pos? +Property Read _RelativeWidth As Boolean ''Use percentage for width? +Property Read _RelativeHeight As Boolean ''Use percentage for height ? +Property Read _RelativePadding As Boolean ''Use percentage for padding ? +Property _Index As Integer +Property Range As String +Property Left As String +Property Top As String +Property X As String +Property Y As String + +Property Width As String +Property Height As String + +Property Visible As Boolean +Property {Font} As Font +Property Expand As Boolean +Property Ignore As Boolean +Property Fixed As Boolean + +Property Autoresize As Boolean +Property Read {Report} As Report +Property _ReportId As Integer +Property Read DataIndex As Integer +Property ForceNewPage As Boolean +Private $bForceNewPage As Boolean + +Public Sub _New(Optional Parent As ReportContainer = Null) + + Dim hRep As Report + + $iMyId = $iLastId + Inc $iLastId + If IsNull(Parent) Then + $iParentId = -1 + $iReportId = $iMyId + + Else + If Parent Is Report Then + hRep = Parent + $iParentId = hRep._Container.id + hRep._Container._Add(Me) + $iReportId = hRep.id + Else + $iParentId = Parent.Id + Parent._Add(Me) + $iReportId = Parent._ReportId + Endif + Endif + +End + +Private Function Id_Read() As Integer + + Return $iMyId + +End + +Private Function Parent_Read() As ReportContainer + + Return ReportControl._ObjectFromId[$iParentId] + +End + +Private Function Left_Read() As String + + Return $sLeft + +End + +Private Sub Left_Write(Value As String) + + $sLeft = Value + +End + +Private Function Top_Read() As String + + Return $sTop + +End + +Private Sub Top_Write(Value As String) + + $sTop = Value + +End + +Private Function Width_Read() As String + + Return $sWidth + +End + +Private Sub Width_Write(Value As String) + + $sWidth = Value + +End + +Private Function Height_Read() As String + + Return $sHeight + +End + +Private Sub Height_Write(Value As String) + + $sHeight = Value + +End + +Private Function Expand_Read() As Boolean + + Return $bExpand + +End + +Private Sub Expand_Write(Value As Boolean) + + $bExpand = Value + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, TotalWidth As Float, TotalHeight As Float, DataIndex As Integer) As TSizeHint + + 'Error.Raise("Something goes wrong the _GetSizeHints is not correctly implemented") + 'Implementaion standart du sizeInt hors autoresize et hbox + ' + + Dim hMyHints As New TSizeHint + + If Me._RelativeHeight Then + hMyHints.Height = TotalHeight * Me._Height / 100 + Else + hMyHints.Height = Me._Height + Endif + + If Me._RelativeWidth Then + hMyHints.Width = TotalWidth * Me._Width / 100 + Else + hMyHints.Width = Me._Width + Endif + Return hMyHints + +End + +Public Sub _PaintBeFore((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + +End + +Public Sub _PaintFrame((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + + Me._Paint(Page, X, Y, hControl, DataIndex) + +End + +Public Sub _PaintAfter((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + +End + +Private Function Visible_Read() As Boolean + + Return $iVisible + +End + +Private Sub Visible_Write(Value As Boolean) + + $iVisible = Value + +End + +Public Sub Move(X As Float, Y As Float, W As Float, H As Float) + + $fLeft = X + $fTop = Y + $fWidth = W + $fHeight = H + +End + +Private Function Tag_Read() As Variant + + Return $vTag + +End + +Private Sub Tag_Write(Value As Variant) + + $vTag = Value + +End + +Private Function Font_Read() As Font + + Return $hFont + +End + +Private Sub Font_Write(Value As Font) + + $hFont = Value + +End + +Private Function Padding_Read() As ReportPadding + + Return $hPadding + +End + +Private Sub Padding_Write(Value As ReportPadding) + + $hPadding = Value + +End + +Private Function _Top_Read() As Float + + Return $fTop + +End + +Private Function _Height_Read() As Float + + Return $fHeight + +End + +Private Function _Width_Read() As Float + + Return $fWidth + +End + +Private Function _Left_Read() As Float + + Return $fLeft + +End + +Private Function _Padding_Read() As Float + + Return $fPadding + +End + +Public Function _SetChildGeometry((X) As Float, (Y) As Float, (W) As Float, (H) As Float, (ContPage) As Integer, (TCont) As TControl, (bInFixed) As Boolean) + +End + +Private Function Autoresize_Read() As Boolean + + Return $bAutoresize + +End + +Private Sub Autoresize_Write(Value As Boolean) + + $bAutoresize = Value + +End + +Private Function _RelativeWidth_Read() As Boolean + + Return $bRelativeWidth + +End + +Private Function _RelativeHeight_Read() As Boolean + + Return $bRelativeHeight + +End + +Private Function _RelativePadding_Read() As Boolean + + Return $bRelativePadding + +End + +''Convert recursively string values to unified values in cm +Public Sub _SetUnifiedValues() + + Dim hSizeParse As TSizeParse + + 'Left + hSizeParse = New TSizeParse($sLeft, True) + $fLeft = hSizeParse.GetValue() + $bRelativeLeft = hSizeParse.IsRelative() + + 'Right + hSizeParse = New TSizeParse($sTop, True) + $fTop = hSizeParse.GetValue() + $bRelativeTop = hSizeParse.IsRelative() + + 'Width + hSizeParse = New TSizeParse($sWidth, True) + $fWidth = hSizeParse.GetValue() + $bRelativeWidth = hSizeParse.IsRelative() + + 'Height + hSizeParse = New TSizeParse($sHeight, True) + $fHeight = hSizeParse.GetValue() + $bRelativeHeight = hSizeParse.IsRelative() + + 'Padding + ' hSizeParse = New TSizeParse($sPadding, True) + ' $fPadding = hSizeParse.GetValue() + ' $bRelativePadding = hSizeParse.IsRelative() + + hSizeParse = New TSizeParse($hPadding.Left, True) + $hPadding._Left = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hPadding.Right, True) + $hPadding._Right = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hPadding.Top, True) + $hPadding._Top = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hPadding.Bottom, True) + $hPadding._Bottom = hSizeParse.GetValue() + + ' + +End + +Private Function Ignore_Read() As Boolean + + Return $bIgnore + +End + +Private Sub Ignore_Write(Value As Boolean) + + $bIgnore = Value + +End + +Private Function Report_Read() As Report + + Return ReportControl._ObjectFromId[$iReportId] + +End + +Private Function _ReportId_Read() As Integer + + Return $iReportId + +End + +Private Function Fixed_Read() As Boolean + + Return $bFixed + +End + +Private Sub Fixed_Write(Value As Boolean) + + $bFixed = Value + +End + +Public Sub _Reset() + + Me._DataIndex = 0 + +End + +Private Function X_Read() As String + + Return $sLeft + +End + +Private Sub X_Write(Value As String) + + $sLeft = Value + +End + +Private Function Y_Read() As String + + Return $sTop + +End + +Private Sub Y_Write(Value As String) + + $sTop = Value + +End + +Private Function Brush_Read() As ReportBrush + + Return $hBrush + +End + +Private Sub Brush_Write(Value As ReportBrush) + + $hBrush = Value + +End + +' Private Function Color_Read() As Integer +' +' Return $iColor +' +' End +' +' Private Sub Color_Write(Value As Integer) +' +' $iColor = Value +' +' End + +Public Function _GetActualBrush(X1 As Integer, Y1 As Integer, X2 As Integer, Y2 As Integer) As PaintBrush + + Dim hBrush As PaintBrush + + If $hBrush Then + hBrush = $hBrush._PaintBrush(X1, Y1, X2, Y2) + Else If $iColor Then + hBrush = Paint.Color($iColor) + Else + Try hBrush = Me.Parent._GetActualBrush(X1, Y1, X2, Y2) + If Not hBrush Then + hBrush = Paint.Color(0) + Endif + Endif + Return hBrush + +End + +Private Sub _ReportId_Write(Value As Integer) + + $iReportId = Value + +End + +Private Function _RelativeLeft_Read() As Boolean + + Return $bRelativeLeft + +End + +Private Function _RelativeTop_Read() As Boolean + + Return $bRelativeTop + +End + +Private Function DataIndex_Read() As Integer + + Return Me._Index 'Return Me._GetIndex() + +End + +Private Function Range_Read() As String + + Return $sRange + +End + +Private Sub Range_Write(Value As String) + + $sRange = Value + +End + +Public Function _GetIndex() As Integer + + Return Me._Index + 'Return Me.Parent._GetIndex() + +End + +Private Function _Index_Read() As Integer + + Return $iIndex + +End + +Private Sub _Index_Write(Value As Integer) + + $iIndex = Value + +End + +Private Function _DataIndex_Read() As Integer + + Return $iDataindex + +End + +Private Sub _DataIndex_Write(Value As Integer) + 'If Me.Tag = "Boite 2" And Value = 0 Then Stop + 'If Me.Report.$iCurPage = 0 Then Print "DataIndex: " & value + + $iDataindex = Value + 'Stop + 'Print "_DataIndex " & Me.Tag & " = " & $iDataindex + +End + +Private Function ForceNewPage_Read() As Boolean + + Return $bForceNewPage + +End + +Private Sub ForceNewPage_Write(Value As Boolean) + + $bForceNewPage = Value + +End + +Public Sub _Free() + +End diff --git a/comp/src/gb.report/.src/MainTools/MReport.module b/comp/src/gb.report/.src/MainTools/MReport.module new file mode 100644 index 00000000..a81b4d10 --- /dev/null +++ b/comp/src/gb.report/.src/MainTools/MReport.module @@ -0,0 +1,26 @@ +' Gambas module file + +Public DrawCount As Integer +Public Resolution As Integer +Private Dash As Float[] = [2.0, 1.0] +Private Dot As Float[] = [1.0, 1.0] +Private DashDot As Float[] = [2.0, 1.0, 1.0, 1.0] +Private DashDotDot As Float[] = [2.0, 1.0, 1.0, 1.0, 1.0, 1.0] + + +Public Sub GetBorder(iStyle As Integer) As Float[] + + Select Case iStyle + + Case Line.Dash + Return Dash + Case Line.Dot + Return Dot + Case Line.DashDot + Return DashDot + Case Line.DashDotDot + Return DashDotDot + End Select + +End + diff --git a/comp/src/gb.report/.src/MainTools/ReportUnits.module b/comp/src/gb.report/.src/MainTools/ReportUnits.module new file mode 100644 index 00000000..642cde8b --- /dev/null +++ b/comp/src/gb.report/.src/MainTools/ReportUnits.module @@ -0,0 +1,92 @@ +' Gambas module file + +Fast +Export +Public AllowedUnits As String[] = ["m", "cm", "mm", "in", "pt", "px"] +Property Read DesktopScale As Float +Public _ReportWidth As Float +Public _Scale As Float = 1.0 +'Public Ratio As Float +Public Function GetFactorUnitToInch(Unit As String, Optional Resolution As Integer) As Float + + + Select Case Unit + Case "cm" + Return 0.3937 + Case "ft" + Return 12 + Case "in" + Return 1 + Case "m" + Return 39.37 + Case "mm" + Return 0.03937 + Case "px" + Return 1 / Paint.ResolutionX 'If(Resolution, Resolution, ReportUnits.Resolution) + Case "pt" + Return 1 / 72 + Case Else + Error.Raise("Unknown unit") + End Select + +End + +Public Function ReportUnitsConverter(Value As Float, Unit1 As String, Unit2 As String) As Float + + Dim fInch As Float + Dim f As Float + + f = GetFactorUnitToInch(Unit1) + finch = value * f + f = GetFactorUnitToInch(Unit2) + Return finch / f + +End + + +Public Function UnitToCm(Value As Float, Unit As String) As Float + + Dim fInch As Float + Dim f As Float + + f = GetFactorUnitToInch(Unit) + finch = value * f + f = 0.3937 + Return finch / f + +End + + +Public Function _ReportUnitsToPixels(Value As Float) As Float + + Dim f As Float + +'Return Value * Ratio +' If Value > 0 Then Stop + f = Value / _ReportWidth * (Paint.Width / _Scale) + 'f = 0.3937 * Paint.ResolutionX + 'f = ReportUnits.GetFactorUnitToInch("cm") * Resolution 'ReportResolution + + 'Return Value * f + Return f + +End + +Public Function _PixelsToReportUnits(Value As Float) As Float + + Dim f As Float + + 'f = 0.3937 * Paint.ResolutionX 'ReportResolution + Return (value / Paint.ResolutionX * 2.54) '/ _ReportWidth * Paint.Width + 'Return (Value) / f + +End + + + + +Private Function DesktopScale_Read() As Float + + Return Paint.ResolutionX / Desktop.Resolution + +End diff --git a/comp/src/gb.report/.src/MainTools/Types/TControl.class b/comp/src/gb.report/.src/MainTools/Types/TControl.class new file mode 100644 index 00000000..27bf86d8 --- /dev/null +++ b/comp/src/gb.report/.src/MainTools/Types/TControl.class @@ -0,0 +1,76 @@ +' Gambas class file + +'Fast + + +Property Read RealLeft As Float ''Return the left relative position size in Pixel + +Property Read RealTop As Float ''Return the top relative position size in Pixel + +Property Read RealWidth As Float ''Return the width in pixel + +Property Read RealHeight As Float ''Return the height in pixel + +Public SizeHint As TSizeHint ''Size hints buffer + +Property Ctrl As ReportControl ''Return or set the link between real widget and virtual object + +Public _PageChildren As New Collection ''The links between page and objects + +Public Index As Integer ''Remember the dataindex + +Public Cache As Variant ''For future data cache + +Private $iCtrl As Integer +Private fX As Float +Private fY As Float +Private fW As Float +Private fH As Float + +''Define the virtual widget size in internal unit (cm) +Public Function _SetGeometry(X As Float, Y As Float, W As Float, H As Float) As Float + + fX = X + fY = Y + fW = W + fH = H + +End + + +Private Function Ctrl_Read() As ReportControl + + Return ReportControl._ObjectFromId[$iCtrl] + +End + + +Private Sub Ctrl_Write(Value As ReportControl) + + $iCtrl = Value.id + +End + +Private Function RealLeft_Read() As Float + + Return ReportUnits._ReportUnitsToPixels(fX) + +End + +Private Function RealTop_Read() As Float + + Return ReportUnits._ReportUnitsToPixels(fY) + +End + +Private Function RealWidth_Read() As Float + + Return ReportUnits._ReportUnitsToPixels(fX + fW) - ReportUnits._ReportUnitsToPixels(fX) + +End + +Private Function RealHeight_Read() As Float + + Return ReportUnits._ReportUnitsToPixels(fY + fH) - ReportUnits._ReportUnitsToPixels(fY) + +End diff --git a/comp/src/gb.report/.src/MainTools/Types/TPageColumn.class b/comp/src/gb.report/.src/MainTools/Types/TPageColumn.class new file mode 100644 index 00000000..9ca0701d --- /dev/null +++ b/comp/src/gb.report/.src/MainTools/Types/TPageColumn.class @@ -0,0 +1,8 @@ +' Gambas class file + +Public Exp As Float +Public NExp As Integer +Public X As Float +Public Width As Float +Public TCtrls As New TControl[] +Public TH As Float \ No newline at end of file diff --git a/comp/src/gb.report/.src/MainTools/Types/TSizeHint.class b/comp/src/gb.report/.src/MainTools/Types/TSizeHint.class new file mode 100644 index 00000000..2b5422ff --- /dev/null +++ b/comp/src/gb.report/.src/MainTools/Types/TSizeHint.class @@ -0,0 +1,5 @@ +' Gambas class file + +Public Height As Float = 0 +Public Width As Float = 0 +Public NotFinished As Boolean diff --git a/comp/src/gb.report/.src/MainTools/Types/TSizeParse.class b/comp/src/gb.report/.src/MainTools/Types/TSizeParse.class new file mode 100644 index 00000000..128ad2f6 --- /dev/null +++ b/comp/src/gb.report/.src/MainTools/Types/TSizeParse.class @@ -0,0 +1,70 @@ +' Gambas class file + +'Fast +Public Value As Float +Public Unit As String + +'' Create a new size object from its description. +'' #bAllowRelative# can be TRUE to allow the use of "%" as unit. +Public Sub _new(Size As String, Optional bAllowRelative As Boolean) + + Dim I As Integer + Dim sCar As String + + Size = Trim(Size) + + If Not Size Then + Unit = "px" + Return + Endif + + For I = 1 To Len(Size) + sCar = Mid$(Size, I, 1) + If Not IsDigit(sCar) And If sCar <> "-" And If sCar <> "." Then Break + Next + + Value = CFloat(Left$(Size, I - 1)) + Unit = Trim(Mid$(Size, I)) + + If Not Unit Then Unit = "%" + + If Not ReportUnits.AllowedUnits.Exist(Unit) Then + If Not bAllowRelative Or Unit <> "%" Then + Error.Raise("Unknown unit") + Endif + Endif + +End + +Public Sub ToCm() As Float + + Return ReportUnits.UnitToCm(Value, Unit) + +End + +Public Sub IsRelative() As Boolean + + Return Unit = "%" + +End + +Public Sub GetValue() As Float + + + If IsRelative() Then + Return Value + Else + Return ToCm() + Endif + +End + + +Static Public Sub _get(Size As String) As TSizeParse + + Dim hSize As TSizeParse = New TSizeParse(Size) + + Return hSize + +End + diff --git a/comp/src/gb.report/.src/Optional/Align.class b/comp/src/gb.report/.src/Optional/Align.class new file mode 100644 index 00000000..485bc6a1 --- /dev/null +++ b/comp/src/gb.report/.src/Optional/Align.class @@ -0,0 +1,10 @@ +' Gambas class file + +Export Optional + +' Must be the same values as in gb.form.const.h in gb.qt4 sources + +Public Enum + Normal = &H00, {Left} = &H01, {Right} = &H02, Center = &H03, + TopNormal = &H10, TopLeft = &H11, TopRight = &H12, Top = &H13, + BottomNormal = &H20, BottomLeft = &H21, BottomRight = &H22, Bottom = &H23 diff --git a/comp/src/gb.report/.src/Optional/Arrange.class b/comp/src/gb.report/.src/Optional/Arrange.class new file mode 100644 index 00000000..1090a9e1 --- /dev/null +++ b/comp/src/gb.report/.src/Optional/Arrange.class @@ -0,0 +1,9 @@ +' Gambas class file + +Export Optional + +Public Const {None} As Integer = 0 +Public Const {Horizontal} As Integer = 1 +Public Const {Vertical} As Integer = 2 +Public Const {Column} As Integer = 4 +Public Const {Fill} As Integer = 5 diff --git a/comp/src/gb.report/.src/Optional/Line.class b/comp/src/gb.report/.src/Optional/Line.class new file mode 100644 index 00000000..c345df55 --- /dev/null +++ b/comp/src/gb.report/.src/Optional/Line.class @@ -0,0 +1,13 @@ +' Gambas class file + +Export Optional + + +' Must be the same values as in gb.form.const.h in gb.qt4 sources + +Public Const {None} As Integer = 0 +Public Const {Solid} As Integer = 1 +Public Const {Dash} As Integer = 2 +Public Const {Dot} As Integer = 3 +Public Const {DashDot} As Integer = 4 +Public Const {DashDotDot} As Integer = 5 \ No newline at end of file diff --git a/comp/src/gb.report/.src/Padding/ReportPadding.class b/comp/src/gb.report/.src/Padding/ReportPadding.class new file mode 100644 index 00000000..97c6a169 --- /dev/null +++ b/comp/src/gb.report/.src/Padding/ReportPadding.class @@ -0,0 +1,63 @@ +' Gambas class file + +Export +Public {Left} As String = "0cm" +Public Right As String = "0cm" +Public Top As String = "0cm" +Public Bottom As String = "0cm" + +Public _Left As Float +Public _Right As Float +Public _Top As Float +Public _Bottom As Float +Property Read _Width As Float +Property Read _Height As Float + +Static Public Sub _Get(Value As String) As ReportPadding + + Dim hPadding As New ReportPadding + Dim hSize As TSizeParse + Dim s As String + Dim ars As String[] + + For Each s In Split(Value, ";") + ars = Scan(s, "*:*") + If ars.count > 1 Then + Select Case LCase(ars[0]) + Case "left" + hPadding.Left = ars[1] + Case "top" + hPadding.top = ars[1] + Case "bottom" + hPadding.bottom = ars[1] + Case "right" + hPadding.Right = ars[1] + Case Else + + End Select + Else + Try hSize = New TSizeParse(s) + If hSize <> Null Then + hPadding.Left = s + hPadding.Right = s + hPadding.Bottom = s + hPadding.Top = s + Endif + Endif + + Next + Return hPadding + +End + +Private Function _Width_Read() As Float + + Return _Left + _Right + +End + +Private Function _Height_Read() As Float + + Return _top + _Bottom + +End diff --git a/comp/src/gb.report/.src/Preview/CPrint.class b/comp/src/gb.report/.src/Preview/CPrint.class new file mode 100644 index 00000000..49d00296 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/CPrint.class @@ -0,0 +1,54 @@ +' Gambas class file + +Create Static +Private hPrinter As New Printer As "Printer" +Private $hPrint As Report + +Public Sub PrintReport(hReport As Report, Optional MyPrinter As Printer) + + Dim hSizeParse As TSizeParse + $hPrint = hReport + '$hPrint = hReport.Clone() + If MyPrinter Then + hPrinter = MyPrinter + Else + hPrinter = New Printer + Endif + Object.Attach(hPrinter, Me, "Printer") + + hPrinter.Paper = $hPrint.Paper + hPrinter.Orientation = $hPrint.Orientation + If $hPrint.Paper = Printer.Custom Then + hSizeParse = New TSizeParse($hPrint.Width) + hPrinter.PaperWidth = hSizeParse.ToCm() * 10 + hSizeParse = New TSizeParse($hPrint.Height) + hPrinter.PaperHeight = hSizeParse.ToCm() * 10 + Endif + If Not MyPrinter Then + If Not hPrinter.Configure() Then + hPrinter.Print + Endif + Else + hPrinter.Print + Endif +End + +Public Sub Printer_Draw() + + $hPrint.Paint(hPrinter.Page) + +End + +Public Sub Printer_Begin() + + 'Debug Object.Type($hReport) + $hPrint.Layout + hPrinter.Count = $hPrint.PageCount + +End + +Public Sub Printer_End() + + $hPrint = Null + +End diff --git a/comp/src/gb.report/.src/Preview/FOptions.class b/comp/src/gb.report/.src/Preview/FOptions.class new file mode 100644 index 00000000..bd549416 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/FOptions.class @@ -0,0 +1,9 @@ +' Gambas class file + + +Public Sub Form_Open() + + ComboBox1.List = ["A3", "A4", "A5", "B5", "Executive", "Legal", "Letter", "Custom"] + ComboBox1.Index = 1 + +End diff --git a/comp/src/gb.report/.src/Preview/FOptions.form b/comp/src/gb.report/.src/Preview/FOptions.form new file mode 100644 index 00000000..d6c086a2 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/FOptions.form @@ -0,0 +1,52 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,68,64) + { VBox1 VBox + MoveScaled(0,0,36,52) + Spacing = True + { Frame1 Frame + MoveScaled(0,3,36,38) + Text = ("Size") + { ComboBox1 ComboBox + MoveScaled(2,7,33,4) + ReadOnly = True + } + { Label1 Label + MoveScaled(3,4,19,3) + Text = ("Paper size") + Transparent = True + } + { Frame3 Frame + MoveScaled(2,12,33,22) + Enabled = False + Text = ("Custom paper size") + { Label2 Label + MoveScaled(2,7,9,3) + Text = ("Width:") + Transparent = True + } + { Label3 Label + MoveScaled(2,13,9,3) + Text = ("Height:") + Transparent = True + } + { txtWidth TextBox + MoveScaled(9,6,14,5) + } + { txtHeight TextBox + MoveScaled(9,12,14,5) + } + } + } + { Frame2 Frame + MoveScaled(0,42,36,9) + Text = ("Orientation") + { ComboBox2 ComboBox + MoveScaled(2,4,28,4) + ReadOnly = True + List = [("Portrait"), ("Landscape")] + } + } + } +} diff --git a/comp/src/gb.report/.src/Preview/FPreview.class b/comp/src/gb.report/.src/Preview/FPreview.class new file mode 100644 index 00000000..8b5f9f41 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/FPreview.class @@ -0,0 +1,400 @@ +' Gambas class file + +Private $aModeButtons As New ToolButton[] +Private bFlag As Boolean +Private $hPrinter As New Printer As "Printer" +Private $iCurPrinterResolution As Integer + +Static Public Function Run(hReport As Report) + + Dim hFPreview As FPreview + + hFpreview = New FPreview + + hFPreview.SetReport(hReport) + hFpreview.ShowModal() + +End + +Public Sub _new() + + Dim s As String + Dim hButton As ToolButton + + $aModeButtons = [ToolButton1, ToolButton2, ToolButton4, ToolButton3] + If Component.IsLoaded("gb.settings") Then + 'Settings.Read(Me, "FPreview") + Settings.Read(View, "View") + + If View.ScaleMode = View.Custom Then + Slider1.Value = View.Scale + Else + For Each hButton In $aModeButtons + hButton.Value = False + Next + $aModeButtons[View.ScaleMode - 1].Value = True + + Endif + + ButtonBox1.Text = Settings["FPreview/OutputFile"] + $hPrinter.Name = Settings["FPreview/Printer"] + If Settings["FPreview/PrintToFile"] = True Then TabPanel1.Index = 1 + + Try $hPrinter.Duplex = Settings["FPreview/Duplex"] + Try $hPrinter.GrayScale = Settings["FPreview/GrayScale"] + Try $hPrinter.ReverseOrder = Settings["FPreview/ReverseOrder"] + Try $hPrinter.CollateCopies = Settings["FPreview/CollateCopies"] + + Endif + + For Each s In Printer.List + cmbPrinter.Add(s) + Next + + SelectPrinter($hPrinter.Name) + Button1.Tag = False + +End + +Private Sub SelectPrinter(Optional sName As String) + + If sName Then + $hPrinter.Name = sName + Object.Lock(cmbPrinter) + Try cmbPrinter.Index = cmbPrinter.List.Find(sName) + Object.Unlock(cmbPrinter) + Else + $hPrinter.Name = Printer.Default + Object.Lock(cmbPrinter) + cmbPrinter.Index = cmbPrinter.List.Find(Printer.Default) + Object.Unlock(cmbPrinter) + Endif + + 'NOTE: force the printer fullpage mode because it make error on report placement + $hPrinter.FullPage = True + + tgbGrayScale.Value = $hPrinter.GrayScale + tgbFullPage.Value = $hPrinter.FullPage + tgbReverseCopies.Value = $hPrinter.ReverseOrder + tgbCollateCopies.Value = $hPrinter.CollateCopies + +End + +Public Sub SetReport(hReport As Report) + + View.Report = hReport + GetValuesFromReport() + +End + +Private Sub GetValuesFromReport() + + cmbPaper.Index = View.Report.Paper + $hPrinter.Orientation = View.Report.Orientation + + If View.Report.Paper = Printer.Custom Then + pnlCustom.Enabled = True + $hPrinter.PaperWidth = TSizeParse[View.Report.Width].ToCm() * 10 + $hPrinter.PaperHeight = TSizeParse[View.Report.Height].ToCm() * 10 + Else + $hPrinter.Paper = View.Report.Paper + Endif + + cmbOrientation.Index = $hPrinter.Orientation + cmbDuplex.Index = $hPrinter.Duplex + cmbPaper.Index = $hPrinter.Paper + Object.Lock(spWidth) + spWidth.Value = $hPrinter.PaperWidth + Object.Unlock(spWidth) + Object.Lock(spHeight) + spHeight.Value = $hPrinter.PaperHeight + Object.Unlock(spHeight) + +End + +Public Sub Slider1_Change() + + Dim hButton As ToolButton + + View.Scale = Slider1.Value / 100 + lblZoom.Text = Str(Slider1.Value) & " %" + bFlag = True + For Each hButton In $aModeButtons + hButton.Value = False + Next + + bFlag = False + +End + +Public Sub View_Change() + + Object.Lock(Slider1) + Slider1.Value = View.Scale * 100 + Object.Unlock(Slider1) + lblZoom.Text = Str(Slider1.Value) & " %" + +End + +Public Sub Mode_Click() + + Dim hButton As ToolButton + Dim hCurButton As ToolButton + + If bFlag Then Return + bFlag = True + hCurButton = Last + + For Each hButton In $aModeButtons + If hButton = hCurButton Then + hButton.Value = True + Else + hButton.Value = False + Endif + Next + + bFlag = False + + View.ScaleMode = hCurButton.Tag + +End + +Public Sub tgbGrayScale_Click() + + Last.Background = IIf(Last.Value, Color.Gray, Color.Default) + $hPrinter.GrayScale = Last.Value + View._GrayScale = Last.Value + +End + +Public Sub tgbFullPage_Click() + + Last.Background = IIf(Last.Value, Color.Gray, Color.Default) + $hPrinter.FullPage = Last.Value + +End + +Public Sub tgbReverseCopies_Click() + + Last.Background = IIf(Last.Value, Color.Gray, Color.Default) + $hPrinter.ReverseOrder = Last.value + +End + +Public Sub tgbCollateCopies_Click() + + Last.Background = IIf(Last.Value, Color.Gray, Color.Default) + $hPrinter.CollateCopies = Last.Value + +End + +Public Sub cmbOrientation_Click() + + If View.Report.Orientation = Last.Index Then Return + View.Report.Orientation = Last.Index + 'View.Report.Refresh + View.Refresh + GetValuesFromReport + +End + +Public Sub cmbPaper_Click() + + Dim i As Integer = Last.Index + 'Print "id "; View.Report.paper + If View.Report.Paper = i Then Return + View.Report.Paper = i + If i = 0 Then + pnlCustom.Enabled = True + Else + pnlCustom.Enabled = False + View.Refresh + GetValuesFromReport + Endif + +End + +Public Sub cmbDuplex_Click() + + $hPrinter.Duplex = Last.index + +End + +Public Sub ButtonBox1_Click() + + Dialog.Path = User.Home + Dialog.Filter = ["*.pdf", "Pdf", "*.ps", "Postscript"] + If Not Dialog.saveFile() Then + ButtonBox1.Text = Dialog.Path + $hPrinter.OutputFile = Dialog.Path + Endif + +End + +Public Sub TabPanel1_Click() + + If TabPanel1.Index = 0 Then + $hPrinter.OutputFile = "" + $hPrinter.Name = cmbPrinter.Text + $iCurPrinterResolution = $hPrinter.Resolution + Else + $hPrinter.OutputFile = ButtonBox1.Text + $hPrinter.Resolution = 600 + Endif + +End + +Public Sub TextBox1_Change() + + View.Range = Last.Text + +End + +Public Sub TextBox1_Click() + + Last.Text = "" + +End + +Public Sub spWidth_Change() + + View.Report.Width = Str(Last.Value) & "mm" + View.Refresh + +End + +Public Sub spHeight_Change() + + View.Report.Height = Str(Last.Value) & "mm" + View.Refresh + +End + +Public Sub Button2_Click() + + View._ForceLayout + +End + +Public Sub cmbPrinter_Click() + + SelectPrinter(Last.Text) + +End + +Public Sub Button1_Click() + + Dim iReturn As Integer + + If Button1.Tag Then + $hPrinter.Cancel + Button1.Tag = False + + Else + If Not View.Report Then Return + If TabPanel1.Index = 1 Then + If $hPrinter.OutputFile Then + If Exist($hPrinter.OutputFile) Then + iReturn = Message.Warning("This file already exist.\nDo you want to replace it ?", "Yes", "No") + If iReturn = 2 Then + ButtonBox1.SetFocus + + Return + Endif + Endif + Else + ButtonBox1_Click + Return + Endif + + Endif + Button1.Tag = True + Button1.Picture = Picture["icon:/22/cancel"] + Button1.Text = ("Cancel") + + $hPrinter.Print + Endif + +End + +Public Sub Printer_Begin() + + hbZoom.Hide + hbPrinting.Show + Inc Application.Busy + ProgressBar1.Value = 0 + ProgressBar1.Pulse = True + lblPrint.Text = ("Layout...") + +End + +Public Sub Printer_Paginate() + + If View.Report._LayoutNotFinished Then + View.LockLayout + View.Report._Layout(View.Report.PageCount) + Else + $hPrinter.Count = View.PageCount + ProgressBar1.Pulse = False + lblPrint.Text = ("Printing...") + View.UnlockLayout + Endif + +End + +Public Sub Printer_Draw() + + If View._RangePages.Count = 0 Then + View.Report.Paint($hPrinter.Page) + Else + View.Report.Paint(View._RangePages[$hPrinter.Page - 1]) + Endif + ProgressBar1.Value = $hPrinter.Page / View.PageCount + +End + +Public Sub Printer_End() + + 'View.UnlockLayout + hbPrinting.Hide + hbZoom.Show + Dec Application.Busy + Button1.Picture = Picture["icon:/22/print"] + Button1.Text = ("Print") + Button1.Tag = False + +End + +Public Sub Panel12_MouseDown() + +End + +Public Sub ButtonBox1_Change() + + $hPrinter.OutputFile = Last.Text + +End + +Public Sub TextBox2_Change() + + $hPrinter.NumCopies = Last.Value + +End + +Public Sub Form_Close() + + If Component.IsLoaded("gb.settings") Then + 'Settings.Write(Me, "FPreview") + Settings.Write(View, "View") + Settings["FPreview/OutputFile"] = ButtonBox1.Text + Settings["FPreview/Printer"] = $hPrinter.Name + Settings["FPreview/PrintToFile"] = TabPanel1.Index = 1 + Settings["FPreview/Duplex"] = cmbDuplex.Index + Settings["FPreview/GrayScale"] = tgbGrayScale.Value + Settings["FPreview/ReverseOrder"] = tgbReverseCopies.Value + Settings["FPreview/CollateCopies"] = tgbCollateCopies.Value + + Settings.Save() + Endif + +End diff --git a/comp/src/gb.report/.src/Preview/FPreview.form b/comp/src/gb.report/.src/Preview/FPreview.form new file mode 100644 index 00000000..0610d3d9 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/FPreview.form @@ -0,0 +1,274 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,114.2857,85.7143) + Text = ("Report preview") + Arrangement = Arrange.Vertical + { Panel4 HBox + MoveScaled(1,1,138,86) + Expand = True + { Panel1 VBox + MoveScaled(0,0,73,84) + Expand = True + { View ReportView + MoveScaled(2,0,70,65) + Background = Color.LightForeground + Tag = "3" + Expand = True + } + { hbPrinting HBox + MoveScaled(1,70,71,4) + Visible = False + { Panel18 Panel + MoveScaled(-1,0,19,3) + Expand = True + Arrangement = Arrange.Horizontal + } + { lblPrint Label + MoveScaled(23,0,11,3) + Text = ("Printing...") + } + { ProgressBar1 ProgressBar + MoveScaled(34,0,37,4) + } + } + { hbZoom HBox + MoveScaled(1,80,73,4) + { Panel2 Panel + MoveScaled(-1,0,19,3) + Expand = True + Arrangement = Arrange.Horizontal + { ToolButton1 ToolButton Mode + Name = "ToolButton1" + MoveScaled(0,0,4,4) + Tag = "1" + ToolTip = ("One page") + Picture = Picture["img/22/OnePage.png"] + Toggle = True + Value = True + } + { ToolButton2 ToolButton Mode + Name = "ToolButton2" + MoveScaled(4,0,4,4) + Tag = "2" + ToolTip = ("Two pages") + Picture = Picture["img/22/TwoPage.png"] + Toggle = True + } + { ToolButton4 ToolButton Mode + Name = "ToolButton4" + MoveScaled(8,0,4,4) + Tag = "3" + ToolTip = ("Full width") + Picture = Picture["img/22/FullWidth.png"] + Toggle = True + } + { ToolButton3 ToolButton Mode + Name = "ToolButton3" + MoveScaled(12,0,4,4) + Tag = "4" + ToolTip = ("Real size") + Picture = Picture["img/22/RealSize.png"] + Toggle = True + } + } + { Slider1 Slider + MoveScaled(36,0,26,4) + MinValue = 12 + MaxValue = 200 + } + { lblZoom Label + MoveScaled(63,1,8,2) + Alignment = Align.Center + } + } + } + { Separator1 Separator + MoveScaled(74,23,0,15) + } + { VBox1 VBox + MoveScaled(75,2,38,83) + Spacing = True + Margin = True + { TabPanel1 TabPanel + MoveScaled(0,-1,36,19) + Count = 2 + Index = 0 + Text = ("Printer") + { PictureBox1 PictureBox + MoveScaled(1,1,7,7) + Picture = Picture["icon:/48/printer"] + } + { cmbPrinter ComboBox + MoveScaled(1,9,34,4) + ReadOnly = True + } + Index = 1 + Text = ("File") + { PictureBox2 PictureBox + MoveScaled(1,1,7,7) + Picture = Picture["icon:/48/pdf"] + } + { ButtonBox1 ButtonBox + MoveScaled(1,9,34,4) + Picture = Picture["icon:/16/open"] + } + Index = 0 + } + { Panel14 HBox + MoveScaled(1,20,36,4) + { Label10 Label + MoveScaled(0,0,12,4) + Text = ("Orientation") + } + { cmbOrientation ComboBox + MoveScaled(14,0,21,4) + ReadOnly = True + List = [("Portrait"), ("Landscape")] + } + } + { Panel8 HBox + MoveScaled(1,24,36,4) + { Label3 Label + MoveScaled(0,0,12,4) + Text = ("Paper") + } + { cmbPaper ComboBox + MoveScaled(13,0,21,4) + ReadOnly = True + List = [("Custom"), ("A3"), ("A4"), ("A5"), ("B5"), ("Letter"), ("Executive"), ("Legal")] + } + } + { pnlCust Panel + MoveScaled(2,30,35,16) + { pnlCustom Panel + MoveScaled(2,0,31,15) + Enabled = False + Border = Border.Etched + { Label5 Label + MoveScaled(3,2,7,4) + Text = ("Width") + } + { spWidth SpinBox + MoveScaled(11,2,13,4) + MaxValue = 1000 + } + { Label7 Label + MoveScaled(24,2,4,4) + Text = ("mm") + } + { Label6 Label + MoveScaled(3,9,8,4) + Text = ("Height") + } + { spHeight SpinBox + MoveScaled(11,9,13,4) + MaxValue = 1000 + } + { Label8 Label + MoveScaled(24,9,4,4) + Text = ("mm") + } + } + } + { Panel11 Panel + MoveScaled(2,46,35,3) + Expand = True + } + { Panel16 VBox + MoveScaled(2,48,37,12) + Spacing = True + { Panel7 HBox + MoveScaled(0,1,36,4) + { Label2 Label + MoveScaled(0,0,12,4) + Text = ("Recto Verso") + } + { cmbDuplex ComboBox + MoveScaled(15,0,21,4) + ReadOnly = True + List = [("None"), ("Short side"), ("Long side")] + } + } + { HBox3 HBox + MoveScaled(-1,6,35,6) + Spacing = True + { Panel13 Panel + MoveScaled(1,1,3,4) + Expand = True + } + { tgbGrayScale ToggleButton + MoveScaled(5,0,6,6) + ToolTip = ("Gray Scale") + Picture = Picture["img/32/grayscale.png"] + } + { tgbFullPage ToggleButton + MoveScaled(11,0,6,6) + Visible = False + ToolTip = ("Full Page") + Picture = Picture["img/32/Empty.png"] + } + { tgbReverseCopies ToggleButton + MoveScaled(17,0,6,6) + ToolTip = ("Reverse order") + Picture = Picture["img/32/reverse.png"] + } + { tgbCollateCopies ToggleButton + MoveScaled(24,0,6,6) + ToolTip = ("Collate copie") + Picture = Picture["img/32/Collatecopie.png"] + } + { Panel15 Panel + MoveScaled(32,2,3,3) + Expand = True + } + } + } + { Panel12 Panel + MoveScaled(2,57,35,5) + Expand = True + } + { Panel6 HBox + MoveScaled(2,61,36,4) + { Label1 Label + MoveScaled(0,0,15,4) + Text = ("Range") + } + { TextBox1 ButtonBox + MoveScaled(17,0,18,4) + Picture = Picture["icon:/16/clear"] + } + } + { HBox1 HBox + MoveScaled(2,66,34,4) + { Label4 Label + MoveScaled(0,0,15,4) + Text = ("Copies") + } + { TextBox2 SpinBox + MoveScaled(15,0,12,4) + MinValue = 1 + } + } + { Panel5 Panel + MoveScaled(2,71,33,2) + } + { HBox2 HBox + MoveScaled(2,75,33,6) + { Panel10 Panel + MoveScaled(2,1,10,3) + Expand = True + } + { Button1 Button + MoveScaled(7,0,22,6) + Text = ("Print") + Picture = Picture["icon:/22/print"] + } + { Panel9 Panel + MoveScaled(24,4,10,3) + Expand = True + } + } + } + } +} diff --git a/comp/src/gb.report/.src/Preview/FPrint.class b/comp/src/gb.report/.src/Preview/FPrint.class new file mode 100644 index 00000000..cea78187 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/FPrint.class @@ -0,0 +1,37 @@ +' Gambas class file + + +Public Sub Form_Open() +Dim hprinter As New Printer + +Dim sPrinter As String + For Each sPrinter In Printer.List + ListView1.Add(sPrinter, sPrinter, IIf(sPrinter = Printer.Default, Picture["icon:/16/ok"], Picture["icon:/16/printer"])) + If sPrinter = Printer.Default Then ListView1[sPrinter].RichText = "" & sPrinter & "" + Next + +' hprinter.FirstPage +' hprinter.LastPage +' hprinter.NumCopies +' hprinter.Orientation +' hprinter.Page +' hprinter.Paper +' hprinter.PaperHeight +' hprinter.PaperWidth +' + +' hprinter.ReverseOrder + 'hprinter.GrayScal +' hprinter.FullPage +' hprinter.OutputFile + 'hprinter.CollateCopie +' hprinter.ReverseOrder + + +End + +Public Sub Panel6_Show() + + Me.Refresh + +End diff --git a/comp/src/gb.report/.src/Preview/FPrint.form b/comp/src/gb.report/.src/Preview/FPrint.form new file mode 100644 index 00000000..c9a09105 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/FPrint.form @@ -0,0 +1,132 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,81,67) + Text = ("Printer Config") + Resizable = False + Arrangement = Arrange.Horizontal + AutoResize = True + Spacing = True + Margin = True + { Panel3 VBox + MoveScaled(3,4,53,60) + Expand = True + AutoResize = True + Spacing = True + Padding = 20 + { ListView1 ListView + MoveScaled(0,0,52,20) + } + { Panel6 Expander + MoveScaled(1,21,51,37) + Text = ("More...") + Hidden = True + { Panel7 VBox + MoveScaled(1,2,44,32) + Spacing = True + Padding = 20 + { HBox3 HBox + MoveScaled(1,2,50,4) + { Label8 Label + MoveScaled(1,1,18,3) + Text = ("Range") + } + { Panel10 Panel + MoveScaled(15,1,12,3) + Expand = True + } + { TextBox1 TextBox + MoveScaled(19,0,22,4) + } + } + { HBox1 HBox + MoveScaled(2,8,50,4) + { Label1 Label + MoveScaled(1,1,18,3) + Text = ("RectoVerso") + } + { Panel5 Panel + MoveScaled(15,1,12,3) + Expand = True + } + { ComboBox1 ComboBox + MoveScaled(20,1,18,3) + ReadOnly = True + List = [("None"), ("Long side"), ("Short side")] + } + } + { HBox2 HBox + MoveScaled(2,13,50,4) + { Label3 Label + MoveScaled(1,1,18,3) + Text = ("Paper") + } + { Panel8 Panel + MoveScaled(15,1,12,3) + Expand = True + } + { cmbPaper ComboBox + MoveScaled(20,1,18,3) + ReadOnly = True + List = [("None"), ("Long side"), ("Short side")] + } + } + { Panel9 Panel + MoveScaled(2,19,40,11) + Border = Border.Etched + { Label4 Label + MoveScaled(1,1,13,3) + Text = ("Width") + } + { Label5 Label + MoveScaled(23,1,13,3) + Text = ("Height") + } + { SpinBox2 SpinBox + MoveScaled(1,4,13,4) + } + { SpinBox3 SpinBox + MoveScaled(23,4,13,4) + } + { Label6 Label + MoveScaled(14,4,4,4) + Text = ("mm") + } + { Label7 Label + MoveScaled(36,4,4,4) + Text = ("mm") + } + } + } + } + } + { Separator1 Separator + MoveScaled(58,4,1,16) + } + { Panel1 VBox + MoveScaled(61,6,17,45) + { Button1 Button + MoveScaled(0,0,16,16) + Picture = Picture["icon:/64/printer"] + } + { Panel4 Panel + MoveScaled(1,16,14,5) + } + { Label2 Label + MoveScaled(1,21,10,2) + Text = ("Copies :") + } + { SpinBox1 SpinBox + MoveScaled(0,23,16,4) + } + { Panel2 Panel + MoveScaled(2,30,12,6) + Expand = True + } + { Button2 Button + MoveScaled(2,38,15,5) + Text = Shortcut(("Cancel"), "C") + Picture = Picture["icon:/22/cancel"] + } + } +} diff --git a/comp/src/gb.report/.src/Preview/Form1.class b/comp/src/gb.report/.src/Preview/Form1.class new file mode 100644 index 00000000..126ad137 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/Form1.class @@ -0,0 +1,30 @@ +' Gambas class file + + +Public Sub Form_Open() +Dim hreport As New Report4 + ReportView1.Report = hreport +Slider1.Value = ReportView1.Scale * 100 + +End + +Public Sub Slider1_Change() + + ReportView1.Scale = Last.value / 100 + ReportView1.Refresh + +End + +Public Sub ReportView1_Change() + + Object.Lock(Slider1) + Slider1.Value = Last.Scale * 100 + If ReportView1.ScaleMode = ReportView.Custom Then lblCustom.Value = True + Object.Unlock(Slider1) +End + +Public Sub Radio_Click() + + ReportView1.ScaleMode = Last.tag + +End diff --git a/comp/src/gb.report/.src/Preview/Form1.form b/comp/src/gb.report/.src/Preview/Form1.form new file mode 100644 index 00000000..5253ab7c --- /dev/null +++ b/comp/src/gb.report/.src/Preview/Form1.form @@ -0,0 +1,59 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,113,64) + Arrangement = Arrange.Horizontal + { Panel1 Panel + MoveScaled(2,1,68,51) + Expand = True + Arrangement = Arrange.Vertical + { ReportView1 ReportView + MoveScaled(0,0,48,40) + Expand = True + } + { Slider1 Slider + MoveScaled(8,46,59,4) + MaxValue = 200 + } + } + { PictureBox1 PictureBox + MoveScaled(72,30,8,8) + Background = Color.SelectedForeground + Picture = Picture["icon:/16/printer"] + Alignment = Align.Center + } + { PictureBox2 PictureBox + MoveScaled(74,11,14,14) + Background = Color.SelectedForeground + Picture = Picture["icon:/22/file"] + Alignment = Align.Center + } + { VBox1 VBox + MoveScaled(86,1,25,59) + { RadioButton1 RadioButton Radio + Name = "RadioButton1" + MoveScaled(2,2,27,4) + Tag = "0" + Text = ("One Page") + Value = True + } + { RadioButton2 RadioButton Radio + Name = "RadioButton2" + MoveScaled(2,7,27,4) + Tag = "1" + Text = ("Two Page") + } + { RadioButton3 RadioButton Radio + Name = "RadioButton3" + MoveScaled(3,12,24,4) + Tag = "2" + Text = ("FullWidth") + } + { lblCustom RadioButton Radio + Name = "lblCustom" + MoveScaled(2,16,25,4) + Tag = "3" + Text = ("Custom") + } + } +} diff --git a/comp/src/gb.report/.src/Preview/ReportView.class b/comp/src/gb.report/.src/Preview/ReportView.class new file mode 100644 index 00000000..0d157ae5 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/ReportView.class @@ -0,0 +1,709 @@ +' Gambas class file + +Export + +Inherits UserControl +Public Const _IsControl As Boolean = True +Public Const _IsContainer As Boolean = False +'Public Const _Properties As String = "Left{ReportCoord},Top{ReportCoord},Width{ReportCoord},Height{ReportCoord},Brush{ReportBrush},Visible=True,Fixed,Font,Padding,Ignore,Expand,AutoResize,Tag,Range" +'Public Const _Family As String = "Report" + + + +Private $View As ScrollArea +Private $hReport As Report +Private $fScale As Float = 1.0 +Private fReportWidthCm As Float +Private fReportHeightCm As Float +Private $iNbrePage As Integer +Private $iTotNbrePage As Integer +Private $iNbrePageByLine As Integer +Private $fPageWidth As Float +Private $fPageHeight As Float +Property {Report} As Report +Private $fSpace As Float +Property Scale As Float +Property _GrayScale As Boolean +Private $bGrayScale As Boolean +Property Settings As Variant[] + + +Private $fRealPageHeight As Float +Private $fRealPageWidth As Float +Private $fRealSpace As Float + +Private $cBuffer As New Collection +Private $aImageToLoad As New Integer[] +Private $aImageLoaded As New Integer[] + +Private $aTask As New ReportViewTask[8] +Private $Timer As New Timer As "Timer" +Private $tmrRedraw As New Timer As "tmrRedraw" +Private $hSpinner As Spinner +Property Read LayoutInProgress As Boolean +Private $bLayoutInProgress As Boolean +Private htmpImage As New Image(1, 1) +Private $bLayoutEnd As Boolean +Private bNeedToLoad As Boolean = True +Private $iMinLine As Integer +Private $iMaxLine As Integer +Private $iFirstPage As Integer +Private $iLastPage As Integer +Private $iRealCenter As Integer + +Public Const Custom As Integer = 0 +Public Const Page As Integer = 1 +Public Const DualPage As Integer = 2 +Public Const FullWidth As Integer = 3 +Public Const RealSize As Integer = 4 + +Property ScaleMode As Integer +Private $iScaleMode As Integer = Me.Custom +Private $fCurScale As Float = 1.0 +Private $bForceReload As Boolean +Property ShowPageNumbers As Boolean +Property Range As String +Private $sRange As String +Public _RangePages As New Integer[] +Private $bShowPageNumbers As Boolean +Property Read Count As Integer +' Property Read Status As Integer +' Private $iStatus As Integer + +Private $bLayoutLocked As Boolean + +Property Read PageCount As Integer + +Event Progress +Event Change +Event Layout(LayoutInProgress As Boolean) +Private $bLayout As Boolean +Private $MX As Integer +Private $MY As Integer +Public Sub _new(Optional hReport As Report) + + If hReport Then Me.Report = hReport + '$hReport = hReport + $fCurScale = 0.3 + $fRealSpace = 2 * Desktop.Resolution / 2.54 + + $Timer.Delay = 5 + $View = New ScrollArea(Me) As "View" + $View.ScrollBar = Scroll.Both + 'If Not $hReport Then $view.Enabled = False + '$View.Background = Color.Red + +End + +Public Sub Timer_Timer() + + Dim hImg As New Image($fPageWidth, $fPageHeight, Color.White) + 'Dim hSvg As New SvgImage($fRealPageWidth, $fRealPageHeight) + Dim i As Integer + Dim aDiff As Integer[] + + If $bLayoutLocked Then + $Timer.Stop + Return + Endif + + If $aImageToLoad.Count > 0 Then + i = $aImageToLoad.Pop() + 'If $aImageLoaded.Exist(i) Then Return + 'Print "load : "; i + Paint.Begin(hImg) + ReportUnits._Scale = $fCurScale + Paint.Scale($fCurScale, $fCurScale) + + If _RangePages.Count > 0 Then + + $hReport.Paint(_RangePages[i - 1]) + Else + + $hReport.Paint(i) + + Endif + ReportUnits._Scale = 1.0 + + Paint.End + + ' Paint.Begin(hImg) + ' hsvg.Paint() + ' Paint.End + + If $bGrayScale Then hImg = hImg.Desaturate() + If Not $aImageLoaded.Exist(i) Then $aImageLoaded.Add(i) + $cBuffer[i] = hImg + $bLayoutInProgress = $hReport._LayoutNotFinished + $tmrRedraw.Trigger + + Else + + If $bLayoutInProgress Then + Raise Layout(True) + Paint.Begin(htmpImage) + 'Print "Layout : "; $hReport.PageCount + $hReport._layout($hReport.PageCount) + Paint.End + $iTotNbrePage = $hReport.PageCount + $bLayoutInProgress = $hReport._LayoutNotFinished + Layout + Else + If Not $bLayoutEnd Then + $cBuffer.Clear + $aImageLoaded.Clear + $aImageToLoad.Clear + $Timer.Stop + $bLayoutEnd = True + Layout + $tmrRedraw.Trigger + Raise Layout(False) + Else + $Timer.Stop + Endif + Endif + Endif + +End + +Public Sub tmrRedraw_Timer() + + $View.Refresh + +End + +Public Sub View_Draw() + + Dim i, j, iline As Integer + Dim X As Integer + Dim Y As Integer + Dim hImg As Image + Dim bBreak As Boolean + Dim aDiff As Integer[] + Dim iTextWidth As Integer + Dim sText As String + If Not $hReport Then Return + Paint.Scale($fCurScale, $fCurScale) + i = $iFirstPage + iline = $iMinLine + '$aImageToLoad.Clear + 'PrintIArray($aImageLoaded) + 'Print $aImageLoaded.Count + + Do + + For j = 0 To $iNbrePageByLine - 1 + + X = $iRealCenter + $fRealSpace + (($fRealPageWidth + $fRealSpace) * j) - $View.ScrollX / $fCurScale + Y = $fRealSpace + (($fRealPageHeight + $fRealSpace) * iline) - $View.ScrollY / $fCurScale + + Paint.Rectangle(X + 10, Y + 10, $fRealPageWidth, $fRealPageHeight) + Paint.Brush = Paint.Color(&HA0000000) + Paint.Fill + Paint.Rectangle(X, Y, $fRealPageWidth, $fRealPageHeight) + Paint.LineWidth = 2 + Paint.Brush = Paint.Color(Color.Black) + Paint.Stroke(True) + Paint.Brush = Paint.Color(Color.White) + Paint.Fill + + hImg = $cBuffer[i + 1] + + If hImg Then Draw.Image(hImg, X, Y, $fRealPageWidth, $fRealPageHeight) + + If $bShowPageNumbers Then + Paint.Font.size = 200 + Paint.Font.Bold = True + Paint.Brush = Paint.Color(Color.SetAlpha(Color.Gray, 128)) + Paint.Text(i + 1, X, Y, $fRealPageWidth, $fRealPageHeight, Align.Center) + Paint.Fill + Endif + Inc i + + If i >= $iLastPage + 1 Then + bBreak = True + Break + Endif + Next + + + + If bBreak Then Break + Inc iline + + Loop + 'Draw.Background = Color.Yellow + Paint.Reset + sText = Str($iFirstPage + 1) & "/" & Str($iNbrePage) + iTextWidth = Paint.TextSize(sText).Width + 10 + Paint.Rectangle(Paint.Width - iTextWidth - 10, 10, iTextWidth, 25, 5) + Paint.Brush = Paint.Color(Color.SetAlpha(Color.DarkGray, 125)) + Paint.Fill(True) + Paint.Brush = Paint.Color(Color.DarkGray) + Paint.Stroke + Paint.Brush = Paint.Color(Color.White) + Paint.Font.Bold = True + Paint.Text(sText, Paint.Width - iTextWidth - 10, 10, iTextWidth, 25, Align.Center) + Paint.fill + 'Draw.FillRect(Paint.Width - 50, 10 + (Paint.h - 40) * $View.scrolly / $View.ScrollH, 40, 25, Color.Yellow) + + + bNeedToLoad = False + +End + +Private Function GetDiffArray(Array1 As Integer[], Array2 As Integer[]) As Integer[] + + Dim aRet As New Integer[] + Dim i As Integer + + For Each i In Array2 + If Array1.Exist(i) Then Continue + aRet.Add(i) + Next + Return aRet + +End + +Public Function MakePageImage(Index As Integer) As Image + + Dim hImg As New Image($fRealPageWidth, $fRealPageHeight, Color.White) + Dim hSvg As New SvgImage($fRealPageWidth, $fRealPageHeight) + + Paint.Begin(hsvg) + $hReport.Paint(Index) + Paint.End + + Paint.Begin(hImg) + Paint.Scale($fCurScale, $fCurScale) + hsvg.Paint + Paint.End + + Return hImg + +End + +Public Sub View_Arrange() + + Layout + Raise Change + +End + +Private Sub Layout() + + Dim iH, iW, iiS As Integer + Dim $atmpA As New Integer[] + Dim i As Integer + Dim OldScale As Float = $fCurScale + If Not $hReport Then Return + If _RangePages.Count > 0 Then + $iNbrePage = _RangePages.Count + Else + $iNbrePage = $iTotNbrePage + Endif + $iNbrePageByLine = 0 + $aImageToLoad.Clear + 'Dim hImg As New Image($fRealPageWidth, $fRealPageHeight) + $fRealPageWidth = fReportWidthCm * Desktop.Resolution / 2.54 '* $fCurScale + $fRealPageHeight = fReportHeightCm * Desktop.Resolution / 2.54 '* $fCurScale + 'Print $iNbrePage + 'Paint.Begin(hImg) + + 'Paint.Scale($fCurScale, $fCurScale) + '$hReport._Layout(1) + 'Paint.End + '$iNbrePage = 200 + 'If Error Then Return + Select Case $iScaleMode + Case Me.Page + $fCurScale = $View.ClientHeight / ($fRealPageHeight + $fRealSpace * 2) + $iNbrePageByLine = 1 + Case Me.DualPage + $fCurScale = Min($View.ClientWidth / ($fRealPageWidth * 2 + $fRealSpace * 3), $View.ClientHeight / ($fRealPageHeight + $fRealSpace * 2)) + $iNbrePageByLine = 2 + Case Me.FullWidth + $fCurScale = $View.ClientWidth / ($fRealPageWidth + $fRealSpace * 2) + Case Custom + $fCurScale = $fScale + Case RealSize + $fCurScale = 1 + End Select + + If OldScale <> $fCurScale Then $bForceReload = True + + bNeedToLoad = True + $fPageHeight = Max($fRealPageHeight * $fCurScale, 1) + $fPageWidth = Max($fRealPageWidth * $fCurScale, 1) + $fSpace = Max($fRealSpace * $fCurScale, 1) + If Not $iNbrePageByLine Then $iNbrePageByLine = Max(Floor(($View.ClientWidth - $fSpace) / ($fPageWidth + $fSpace)), 1) + + $iMinLine = FindPageLineByPos($fSpace + $View.ScrollY) - 1 + $iMaxLine = Max(1, FindPageLineByPos($View.ClientHeight + $View.ScrollY)) + 'Print $iMinLine, $iMaxLine + $iFirstPage = $iMinLine * $iNbrePageByLine + $iLastPage = Min($iMaxLine * $iNbrePageByLine, $iNbrePage - 1) + If $iNbrePageByLine <= $iNbrePage Then + $iRealCenter = ($View.ClientWidth - ($fSpace + ($fPageWidth + $fSpace) * $iNbrePageByLine)) / 2 / $fCurScale + Else + $iRealCenter = 0 + Endif + 'Calcule des images a charger + + 'Print "Afficher pages de: "; $iFirstPage + 1; " à "; $iLastPage + 1 + 'PrintCol + ' Print "Pages chargées: " + ' PrintIArray($aImageLoaded) + For i = 0 To $aImageLoaded.Max + + If $aImageLoaded[i] < $iFirstPage Or If $aImageLoaded[i] > $iLastPage + 1 Then + 'Print "supprimer : " & i + $cBuffer[$aImageLoaded[i]] = Null + 'Print "Efface : "; $aImageLoaded[i] + Else + $atmpA.Add($aImageLoaded[i]) + Endif + + Next + + $aImageLoaded = $atmpA + 'If not forcing consign is given + 'Add only the not load page to the loading queue + For i = $iFirstPage To $iLastPage + If Not $bForceReload And If $aImageLoaded.Exist(i + 1) Then + Continue + Else + $aImageToLoad.Add(i + 1) + Endif + Next + + If $bForceReload Then $bForceReload = False + + ' Print "Pages à charger: " + ' PrintIArray($aImageToLoad) +' Print + If Not $Timer.Enabled And If $aImageToLoad.Count > 0 Then + $aImageToLoad = $aImageToLoad.Reverse() + $Timer.Start + Endif + $View.ResizeContents(Max($View.ClientWidth, ($fPageWidth + 2 * $fSpace)), $fSpace + Ceil($iNbrePage / $iNbrePageByLine) * ($fPageHeight + $fSpace)) + '$View.ScrollX = Max($View.ClientWidth, ($fPageWidth + 2 * $fSpace)) / 8 +End + +Public Sub View_MouseWheel() + + If Mouse.Control Then + Stop Event + Me.Scale += (Mouse.Delta * 0.1) + Endif +End + + + +Public Sub View_Scroll() + + '$aImageToLoad.Clear + 'bNeedToLoad = True + '$cBuffer.Clear + '$aImageLoaded.Clear + $Timer.Stop + Layout + +End + +Private Sub FindPageLineByPos(Y As Integer) As Integer + + Dim yPage As Integer + Dim i As Integer + + For i = 1 To Ceil($iNbrePage / $iNbrePageByLine) + yPage = $fSpace + ($fPageHeight + $fSpace) * (i - 1) + 'If i = 3 Then Stop + If Y >= yPage And If Y <= (yPage + $fPageHeight + $fSpace) Then Return i + Next + Return -1 + +End + +Private Function Scale_Read() As Float + + Return $fCurScale + +End + +Private Sub Scale_Write(Value As Float) + + $fScale = Max(0.1, Min(2, Value)) + + '$cBuffer.Clear + $bForceReload = True + '$aImageLoaded.Clear + $iScaleMode = Me.Custom + Layout + +End + +Private Function Report_Read() As Report + + Return $hReport + +End + +Private Sub Report_Write(Value As Report) + + Dim hSizeParse As TSizeParse + + If Not Value Then Return + $hReport = Value + '$hReport.Orientation = Printer.Landscape + Paint.Begin(htmpImage) + $hReport._Layout(0) + Paint.End + + $iTotNbrePage = $hReport.PageCount + $bLayoutInProgress = $hReport._LayoutNotFinished + $bLayoutEnd = False + fReportWidthCm = TSizeParse[$hReport.Width].ToCm() + fReportHeightCm = TSizeParse[$hReport.Height].ToCm() + Layout + '$view.Enabled = True + $tmrRedraw.Trigger + +End + +Public Sub View_MouseUp() + + 'Print FindPageByPos(Mouse.y) + +End + +Private Function LayoutInProgress_Read() As Boolean + + Return $bLayoutInProgress + +End + +Private Sub PrintIArray(Array1 As Integer[]) + + Dim i As Integer + + For Each i In Array1 + Print i; " "; + Next + Print "\n"; + +End + +Public Sub PrintCol() + + Dim o As Object + + Print "Collection: " + For Each o In $cBuffer + Print $cBuffer.Key; + + Next + Print "\n" + +End + +Private Function ScaleMode_Read() As Integer + + Return $iScaleMode + +End + +Private Sub ScaleMode_Write(Value As Integer) + + $iScaleMode = Value + $bForceReload = True + Layout + Raise change + $tmrRedraw.Trigger + +End + +Private Function GetScale() As Float + +End + +Private Sub SetCurScale(Value As Integer) + + If $fCurScale <> Value Then + $fCurScale = Value + $bForceReload = True + Endif + +End + +Private Function ShowPageNumbers_Read() As Boolean + + Return $bShowPageNumbers + +End + +Private Sub ShowPageNumbers_Write(Value As Boolean) + + $bShowPageNumbers = Value + $tmrRedraw.Trigger + +End + + +Public Sub Refresh() + $Timer.Stop + $aImageLoaded.Clear + $aImageToLoad.Clear + $cBuffer.Clear + $bLayoutEnd = False + $hReport.Refresh + Me.Report = Me.Report + +End + +Private Function _GrayScale_Read() As Boolean + + Return $bGrayScale + +End + +Private Sub _GrayScale_Write(Value As Boolean) + + If $bGrayScale = Value Then Return + $bGrayScale = Value + $cBuffer.Clear + $aImageLoaded.Clear + $aImageToLoad.Clear + $Timer.Stop + Layout + $tmrRedraw.Trigger + +End + +Private Function Range_Read() As String + + Return $sRange + +End + +Private Sub Range_Write(Value As String) + + $sRange = Value + SetRangePages + $Timer.Stop + $aImageLoaded.Clear + $aImageToLoad.Clear + $cBuffer.Clear + Layout +End + +Public Sub SetRangePages() + + Dim ars As String[] + Dim ars2 As String[] + Dim s As String + Dim iStart, iEnd, i As Integer + _RangePages.Clear + If Not $sRange Then Return + ars = Split($sRange, ";") + + For Each s In ars + If InStr(s, "-") Then + ars2 = Scan(s, "*-*") + iStart = CInt(Val(ars2[0])) + iEnd = Val(ars2[1]) + If iStart < 0 Or If iEnd < 0 Or If iEnd < iStart Then Goto Fin + If _RangePages.Count > 0 And If iStart < _RangePages[_RangePages.Max] Then Goto Fin + For i = iStart To iEnd + + _RangePages.Add(i) + + Next + Else + iStart = CInt(Val(s)) + If _RangePages.Count > 0 And If iStart < _RangePages[_RangePages.Max] Then Goto Fin + _RangePages.Add(iStart) + Endif + Next + Return +Catch +Fin: +'_RangePages.Clear + +End + + +Private Function PageCount_Read() As Integer + + Return $iNbrePage + +End + +Private Function Count_Read() As Integer + + Return $iTotNbrePage + +End + +Public Sub LockLayout() + + $bLayoutLocked = True + +End + + +Public Sub UnlockLayout() + + $bLayoutLocked = False + +End + +Public Sub _ForceLayout() + $Timer.Stop + Paint.Begin(htmpImage) + $hReport.Layout() + Paint.End + $iTotNbrePage = $hReport.PageCount + Layout +End + +Public Sub View_MouseDown() + + If Mouse.Left Then + $MX = Mouse.X + $MY = Mouse.Y + Endif + + +End + +Public Sub View_MouseMove() + + If Mouse.Left Then + 'Print $MX - Mouse.X + $View.ScrollX += $MX - Mouse.X + $MX = Mouse.X + + $View.ScrollY += $MY - Mouse.Y + $MY = Mouse.Y + + $tmrRedraw.Trigger + + Endif + +End + +Private Function Settings_Read() As Variant[] + + Return [$fScale, CVariant($iScaleMode)] + +End + +Private Sub Settings_Write(Value As Variant[]) + + $fScale = Value[0] + $iScaleMode = Value[1] + +Catch + +End diff --git a/comp/src/gb.report/.src/Preview/ReportViewTask.class b/comp/src/gb.report/.src/Preview/ReportViewTask.class new file mode 100644 index 00000000..22c0f6c0 --- /dev/null +++ b/comp/src/gb.report/.src/Preview/ReportViewTask.class @@ -0,0 +1,33 @@ +' Gambas class file + +Inherits Task +Private $hReport As Report + +Public Width As Integer +Public Height As Integer +Private Scale As Float +Public Page As Integer + +Public Sub _new(hReport As Report, iPage As Integer, fScale As Float, W As Integer, H As Integer) + + $hReport = hReport + Width = W + Height = H + Scale = fScale + Page = iPage +End + + +Public Sub Main() As Variant + + Dim hImage As New Image(Width, Height, Color.White) + + Paint.Begin(hImage) + Paint.Scale(Scale, Scale) + $hReport.Paint(page) + Paint.End + + Return hImage.Pixels + + +End diff --git a/comp/src/gb.report/.src/Report.class b/comp/src/gb.report/.src/Report.class new file mode 100644 index 00000000..644698d2 --- /dev/null +++ b/comp/src/gb.report/.src/Report.class @@ -0,0 +1,794 @@ +' Gambas class file + +Create Static +Export +Inherits ReportSection + +' For Stretch properties +Public Enum None, Proportional, Fill + +Public Const _Properties As String = "*,-Left,-Top,-Width,-Height,Count{Range:1;256}=1,Index,Text," +"Paper{Printer.A3;A4;A5;B5;Letter;Executive;Legal;Custom}=A4," +"Orientation{Printer.Portrait;Landscape}=Portrait" +Public Const _HiddenControls As String = "Report,ReportControl,ReportContainer,ReportFrame,ReportSection" +Public Const _IsForm As Boolean = True +Public Const _IsContainer As Boolean = True +Public Const _IsMultiContainer As Boolean = True +Public Const _DrawWith As String = "TabPanel" +Public Const _DefaultEvent As String = "Open" +Public _LayoutNotFinished As Boolean +Public _Container As ReportSection +'Public Constants +Public {Debug} As Boolean = False +Public _bInExec As Boolean +'Public _cPages As New Object[] + +Private $aSections As New TControl[] +Private iCurPage As Integer = 0 +Private $iPageCount As Integer = -1 +Private $bLayoutIsDirty As Boolean = True +Private $fScale As Float = 1.0 +Private $iIndex As Integer +Private $iResolution As Integer = -1 +Private $iCurSection As Integer +Private $iCurPage As Integer = -1 +Private $iPaper As Integer = 2 +Private $iOrientation As Integer +' Private $hPrinter As New Printer As "Printer" +Private $bOpened As Boolean + +' Property Read Printer As Printer +Property Resolution As Integer +Property Scale As Float +Property Read PageCount As Integer +Property Count As Integer +Property Index As Integer + +'All ReportControl Properties +Property Left As String +Property Top As String +Property X As String +Property Y As String + +Property Width As String +Property Height As String + +Property Visible As Boolean +Property {Font} As Font +Property Expand As Boolean +Property Ignore As Boolean +Property Fixed As Boolean + +Property Autoresize As Boolean +Property Padding As ReportPadding +'All ReportFrame Properties +Property Border As ReportBorder +Property BackGround As ReportBrush + +'All ReportContainer Properties +Property Read Children As ReportControl[] +Property Spacing As String +Property Data As Object + +'All ReportSection Properties +Property Text As String + +Property Paper As Integer +Property Orientation As Integer + +Public _Scale As Float + +Event Open + +Static Public Sub Main() + + Dim hObject As Object + + hObject = Application.Startup.AutoCreate() + If Not hObject Is Report Then Return + FPreview.Run(hObject) + +End + +Public Sub _New() + + Dim hSection As New ReportSection + Dim TSection As New TControl + + object.Attach(Me, Me, "Report") + + '$hReportTControl.RelPage = 0 + ReportControl._ObjectFromId[Me.id] = Me + hSection = New ReportSection + hSection._ReportId = Me.Id + + ReportControl._ObjectFromId[hSection.Id] = hSection + TSection.Ctrl = hSection + $aSections.Add(TSection) + hSection.Text = ("Section ") & $aSections.Max + _Container = hSection + Me.paper = Printer.A4 + 'Me.Orientation = Printer.Landscape +End + +Public Sub Layout() + + _Layout() + +End + + + +Public Sub _Layout(Optional iPage As Integer = -1) + + 'Dim iCurPage As Integer + Dim TSection As TControl + Dim CSection As ReportContainer + Dim CBaseSection As ReportContainer + Dim i As Integer + + If Not $bOpened Then + Raise Open + $bOpened = True + Endif + + 'Print Paint.ResolutionX + 'If iPage < 0 Then Return + If Not $bLayoutIsDirty Then Return + If _bInExec Then Return + If iPage > -1 Then + 'La page a déja été calculée + If iPage <= $iCurPage Then Return + Else + 'Mise a zéro + $iCurPage = -1 + $iCurSection = 0 + $iPageCount = 0 + Me._Reset + Endif + 'Set execution Flag to true + _bInExec = True + CBaseSection = $aSections[0].Ctrl + + 'ReportUnits.Resolution = If($iResolution < 0, Paint.ResolutionX, $iResolution) + + For i = $iCurSection To $aSections.Max + Tsection = $aSections[i] + CSection = TSection.Ctrl + CSection.Padding = CBaseSection.Padding + CSection.Spacing = CBaseSection.Spacing + CSection._SetUnifiedValues() + ReportUnits._ReportWidth = CBaseSection._Width + TSection._SetGeometry(0, 0, CSection._Width, CSection._Height) + Inc $iCurPage + Do + + CSection._SetChildGeometry(0, 0, CSection._Width, CSection._Height, $iCurPage, TSection, False) + + If CSection._CurItem > CSection.Children.Max Then Break + + Inc $iCurPage + $iPageCount = $iCurPage + 'Print "Fin Page " & $iCurPage + If iPage > -1 And If $iCurPage > iPage Then + Dec $iCurPage + _LayoutNotFinished = True + $iCurSection = i + _bInExec = False + Return + + Endif + + Loop + 'Inc iCurPage + Next + + $iPageCount = $iCurPage + 1 + 'Debug Me._PageChildren.Count + _LayoutNotFinished = False + 'ReportUnits.PageCount = $iCount + $iCurSection = 0 + '$iCurPage = -1 + $bLayoutIsDirty = False + + 'End of execution + _bInExec = False + +End + +Public Sub Paint(Page As Integer) + + Dim TSection As TControl + Dim fScale As Float + 'Debug "**** "; Page;; Paint.ResolutionX;; Paint.ResolutionY + + If Me.Parent = Null Then + 'If $sOldWidth <> ((Paint.Width / $fScale) & " px") Then $bLayoutIsDirty = True + 'If $sOldHeight <> ((Paint.Height / $fScale) & " px") Then $bLayoutIsDirty = True + Endif + ReportUnits._ReportWidth = $aSections[0].Ctrl._Width + 'ReportUnits.Resolution = If($iResolution < 0, Paint.ResolutionX, $iResolution) + If $bLayoutIsDirty And $iPageCount = -1 Then Layout() + Paint.Save + 'Set execution Flag to true + _bInExec = True + + ' If page < 1 Or page > $iCount Then + ' Error.Raise("This page does not exist") + ' Endif + 'Dec page + + 'Paint.Clip + 'ReportUnits.DrawCount = 0 + + 'Paint.Reset + ' If Paint.Device Is Printer Then + ' Paint.Scale(Paint.ResolutionX / 1200, Paint.ResolutionY / 1200) + ' Endif + ' If Paint.Device Is Printer Then + ' If ReportUnits._ReportUnitsToPixels($aSections[0].Ctrl._Width) > Paint.Width Then + ' + ' fScale = Paint.Width / ReportUnits._ReportUnitsToPixels($aSections[0].Ctrl._Width) + ' + ' Paint.Scale($fScale, $fScale) + ' Endif + ' Endif + 'Print Paint.Width + 'ReportUnits.Ratio = Paint.Width / $aSections[0].Ctrl._Width + ' With Paint.ClipExtents + ' Debug "ClipExtents: "; .X;; .Y;; .Width;; .Height + ' End With + Dec page + 'Me._ClipChildren(Page, 0, 0, $hReportTControl, -1, $fSCale) + For Each TSection In $aSections + TSection.Ctrl._PaintBefore(Page, 0, 0, TSection, -1) + TSection.Ctrl._Paint(Page, 0, 0, TSection, -1) + TSection.Ctrl._PaintAfter(Page, 0, 0, TSection, -1) + Next + 'Debug "DrawCount = "; ReportUnits.DrawCount + 'End of execution + _bInExec = False + Paint.Restore + +End + +Private Function PageCount_Read() As Integer + + If $bLayoutIsDirty And Not _LayoutNotFinished Then Layout() '$iCount = -1 + + Return $iPageCount + +End + +Private Function Height_Read() As String + + Return $aSections[0].Ctrl.Height + +End + +Private Sub Height_Write(Value As String) + + SetHeight(Value) + $iPaper = Printer.Custom + +End + +Private Sub SetHeight(Value As String) + + Dim TSection As TControl + + If $aSections[0].Ctrl.Height <> Value Then + + For Each TSection In $aSections + TSection.Ctrl.Height = Value + Next + $bLayoutIsDirty = True + Endif + +End + +Private Function Width_Read() As String + + Return $aSections[0].Ctrl.Width + +End + +Private Sub Width_Write(Value As String) + + SetWidth(Value) + $iPaper = Printer.Custom + +End + +Private Sub SetWidth(Value As String) + + Dim TSection As TControl + + If $aSections[0].Ctrl.Width <> Value Then + For Each TSection In $aSections + Tsection.Ctrl.Width = Value + Next + $bLayoutIsDirty = True + Endif + +End + +Private Function Scale_Read() As Float + + Return $fScale + +End + +Private Sub Scale_Write(Value As Float) + + $fScale = Value + +End + +Public Sub Clear() + + Super._Free + iCurPage = 0 + $iPageCount = -1 + '$iResolution = Desktop.Resolution + $bLayoutIsDirty = True + $fScale = 1.0 + 'Me.Brush = Null + 'Me.Border = New ReportBorder + 'Me.Spacing = "0 cm" + 'Me.Padding = New ReportPadding + +End +' + +Public Function Preview() + + FPreview.Run(Me) + +End + +' Public Function Image(Value As String, X As String, Y As String) As ReportBrush +' +' Dim hBrush As New ReportBrush +' +' hBrush._Type = 1 +' hBrush._X = New TSizeParse(X) +' hBrush._Y = New TSizeParse(Y) +' hBrush._Image = Image.Load(Value) +' Return hBrush +' +' End +' +' Public Function LinearGradient(Colors As Integer[], Positions As Float[]) As ReportBrush +' +' Dim hBrush As New ReportBrush +' +' hBrush._Type = 2 +' 'hBrush.X = MRTools.ScanValue(X) +' 'hBrush.Y = MRTools.ScanValue(Y) +' +' hBrush._Color = Colors +' hBrush._Pos = Positions +' Return hBrush +' +' End + +' Public Function RadialGradient(Colors As Integer[], Positions As Float[]) As ReportBrush +' +' Dim hBrush As New ReportBrush +' +' hBrush._Type = 3 +' 'hBrush.X = MRTools.ScanValue(X) +' 'hBrush.Y = MRTools.ScanValue(Y) +' +' hBrush._Color = Colors +' hBrush._Pos = Positions +' Return hBrush +' +' End + +' Public Sub {Color}(iColor As Integer) As ReportBrush +' +' Dim hBrush As New ReportBrush +' +' hBrush._Type = 0 +' hBrush._iValue = iColor +' Return hBrush +' +' End + +Public Sub Refresh() + + $bLayoutIsDirty = True + Me._Reset + +End + +Private Function Count_Read() As Integer + + Return $aSections.Count + +End + +Private Sub Count_Write(Value As Integer) + + Dim i As Integer + Dim hSection As ReportSection + Dim TSection As TControl + + If Value < 0 Then Return + If Value < $aSections.Count Then + $aSections.Resize(Value) + Else If Value > $aSections.Count Then + For i = $aSections.Count To Value - 1 + + TSection = New TControl + hSection = New ReportSection + hSection.Text = "Section " & CStr(i) + hSection._ReportId = Me.Id + ReportControl._ObjectFromId[hSection.Id] = hSection + TSection.Ctrl = hSection + hSection.Padding = Me.Padding + hSection.Height = Me.Height + hSection.Width = Me.Width + $aSections.Add(TSection) + hSection.Text = ("Section ") & $aSections.Max + Next + Endif + +End + +Private Function Index_Read() As Integer + + Return $iIndex + +End + +Private Sub Index_Write(Value As Integer) + + $iIndex = Max(Min($aSections.Max, Value), 0) + _Container = $aSections[$iIndex].Ctrl + +End + +Public Sub _get(Index As Integer) As ReportSection + + Return $aSections[Index].Ctrl + +End + +Private Function Padding_Read() As ReportPadding + + Return $aSections[0].Ctrl.Padding + +End + +Private Sub Padding_Write(Value As ReportPadding) + + Dim TSection As TControl + + If Me.Padding <> Value Then + For Each TSection In $aSections + Tsection.Ctrl.Padding = Value + Next + Endif + +End + +Private Function Resolution_Read() As Integer + + Return $iResolution + +End + +Private Sub Resolution_Write(Value As Integer) + + $bLayoutIsDirty = True + $iResolution = Max(0, Value) + +End + +Private Function Left_Read() As String + + Return _Container.Left + +End + +Private Sub Left_Write(Value As String) + + _Container.Left = Value + +End + +Private Function Top_Read() As String + + Return _Container.Top + +End + +Private Sub Top_Write(Value As String) + + _Container.Top = Value + +End + +Private Function X_Read() As String + + Return _Container.Left + +End + +Private Sub X_Write(Value As String) + + _Container.Left = Value + +End + +Private Function Y_Read() As String + + Return _Container.Top + +End + +Private Sub Y_Write(Value As String) + + _Container.Top = Value + +End + +Private Function Visible_Read() As Boolean + + Return _Container.Visible + +End + +Private Sub Visible_Write(Value As Boolean) + + _Container.Visible = Value + +End + +Private Function Font_Read() As Font + + Return _Container.Font + +End + +Private Sub Font_Write(Value As Font) + + _Container.Font = Value + +End + +Private Function Expand_Read() As Boolean + + Return _Container.Expand + +End + +Private Sub Expand_Write(Value As Boolean) + + _Container.Expand = Value + +End + +Private Function Ignore_Read() As Boolean + + Return _Container.Ignore + +End + +Private Sub Ignore_Write(Value As Boolean) + + _Container.Ignore = Value + +End + +Private Function Fixed_Read() As Boolean + + Return _Container.Fixed + +End + +Private Sub Fixed_Write(Value As Boolean) + + _Container.Fixed = Value + +End + +Private Function Autoresize_Read() As Boolean + + Return _Container.Autoresize + +End + +Private Sub Autoresize_Write(Value As Boolean) + + _Container.Autoresize = Value + +End + +Private Function Border_Read() As ReportBorder + + Return _Container.Border + +End + +Private Sub Border_Write(Value As ReportBorder) + + _Container.Border = Value + +End + +Private Function BackGround_Read() As ReportBrush + + Return _Container.BackGround + +End + +Private Sub BackGround_Write(Value As ReportBrush) + + _Container.BackGround = Value + +End + +Private Function Children_Read() As ReportControl[] + + Return _Container.Children + +End + +Private Function Spacing_Read() As String + + Return _Container.Spacing + +End + +Private Sub Spacing_Write(Value As String) + + Dim TSection As TControl + Dim hCont As ReportContainer + + If Me.Spacing <> Value Then + For Each TSection In $aSections + hCont = Tsection.Ctrl + hCont.Spacing = Value + Next + Endif + +End + +Private Function Data_Read() As Object + + Return _Container.Data + +End + +Private Sub Data_Write(Value As Object) + + _Container.Data = Value + +End + +Private Function Text_Read() As String + + Return _Container.Text + +End + +Private Sub Text_Write(Value As String) + + _Container.Text = Value + +End + +''Return a new report object +Public Sub Clone() As Report + + 'Return Object.New(Object.Type(Me)) + Return Object.Class(Me).New() + +End + +Private Function Paper_Read() As Integer + + Return $iPaper + +End + +Private Sub Paper_Write(Value As Integer) + + $iPaper = Value + SetPaper + +End + +Private Function Orientation_Read() As Integer + + Return $iOrientation + +End + +Private Sub Orientation_Write(Value As Integer) + + $iOrientation = Value + SetPaper + +End + +Public Sub Print(Optional hPrinter As Printer) + + If hPrinter Then + CPrint.PrintReport(Me, hPrinter) + Else + CPrint.PrintReport(Me) + Endif + +End + +Private Sub SetPaper() + + Dim sPrev As String + + Select Case $iPaper + Case Printer.A3 + SetWidth("29.7cm") + SetHeight("42cm") + Case Printer.A4 + SetWidth("21cm") + SetHeight("29.7cm") + Case Printer.A5 + SetWidth("14.8cm") + SetHeight("21cm") + Case Printer.B5 + SetWidth("18.7cm") + SetHeight("25.7cm") + Case Printer.Executive + SetWidth("19.1cm") + SetHeight("25.4cm") + Case Printer.Legal + SetWidth("21.6cm") + SetHeight("35.6cm") + Case Printer.Letter + SetWidth("21.6cm") + SetHeight("27.9cm") + + End Select + 'Print $iOrientation, $iPaper + + If $iOrientation = Printer.Landscape And Not ($iPaper = Printer.Custom) Then + sPrev = Me.Height + SetHeight(Me.Width) + SetWidth(sPrev) + ' Print "width = " & Me.Width + ' Print "height = " & Me.Height + Endif + +End + +Public Sub _Reset() + + Dim hSection As TControl + + For Each hSection In $aSections + hSection.Ctrl._Reset + hSection._PageChildren = New Collection + Next + + $iCurPage = -1 + $iCurSection = 0 + $iPageCount = 0 + +End + +' Private Function Printer_Read() As Printer +' +' Return $hPrinter +' +' End + +' Public Sub Print2() +' +' _Reset +' +' End diff --git a/comp/src/gb.report/.src/ReportContainer.class b/comp/src/gb.report/.src/ReportContainer.class new file mode 100644 index 00000000..c4dee7a7 --- /dev/null +++ b/comp/src/gb.report/.src/ReportContainer.class @@ -0,0 +1,945 @@ +' Gambas class file + +Export +Create Private +Inherits ReportFrame + +Public Const _IsContainer As Boolean = True +Public Const _Group As String = "Container" +Public Const _Properties As String = "*,Spacing{ReportCoord},OnePiece,ForceNewPage" +'Public Const _DefaultEvent As String = "Data" +Private $bDataIsResult As Boolean +'Private $iCount As Integer + +Property _CurItem As Integer +Private $iCurItem As Integer + +Public _Arrangement As Integer +Private $bIndexChange As Boolean +Private $aChild As New ReportControl[] +Private $fSpacing As Float +Private $sSpacing As String +Private $bRelativeSpacing As Boolean + +Private $aChildCopy As TControl[] +Private $bOnePiece As Boolean +Private $iIndex As Integer +Private $hData As Object +Private $fFixedSize As Float +Property _Index As Integer + +Property DataCount As Integer + +Property Read Children As ReportControl[] + +'Property Read Index As Integer +Property Spacing As String +'Property Data As Object '<==== remettre pour automation + +Property Read _Spacing As Float +Property Read _RelativeSpacing As Boolean + +Property OnePiece As Boolean +Public _RealSpacing As Integer +Public _NotFinished As Boolean + +Event BeforeData +Event AfterData + +Public Function _Add(cControl As ReportControl) 'As TControl + + 'Dim hTControl As New TControl + + 'hTControl.Ctrl = cControl + $aChild.Add(cControl) + ReportControl._ObjectFromId[cControl.Id] = cControl + 'Return hTControl + +End + +Public Sub _Remove(id As Integer) + + $aChild.Remove($aChild.Find(ReportControl._ObjectFromId[id])) + +End + +Public Sub _Free() + +End + +Private Function Children_Read() As ReportControl[] + + Return $aChild + +End + +Private Function Spacing_Read() As String + + Return $sSpacing + +End + +Private Sub Spacing_Write(Value As String) + + $sSpacing = Value + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, (DataIndex) As Integer) + + Dim hChild As TControl + + X += hControl.RealLeft + Y += hControl.RealTop + If Not hControl._PageChildren.Exist(Page) Then Return + 'Me._Index = hControl.Index + For Each hChild In hControl._PageChildren[Page] + 'Print object.Type(hchild.Ctrl), hControl.RelPage, hControl.RealPage + hchild.RelPage, Page - 1 + + 'If hchild.RelPage = -1 Or hControl.RealPage + hchild.RelPage = Page - 1 Then + + 'Inc ReportUnits.DrawCount + 'hchild.Ctrl._Index = hchild.Index + '$iIndex = DataIndex + 'If hControl.DataIndex > -1 Then $iIndex = hControl.DataIndex + Me._Index = hchild.Index + If $bIndexChange Then + Raise BeforeData + $bIndexChange = False + Endif + hChild.Ctrl._Paintframe(Page, X, Y, hChild, hChild.Index) + + Next + +End + +Private Function _Spacing_Read() As Float + + Return $fSpacing + +End + +Private Function _RelativeSpacing_Read() As Boolean + + Return $bRelativeSpacing + +End + +Public Sub _SetUnifiedValues() + + Dim hChild As ReportControl + Dim hSizeParse As TSizeParse + + Super._SetUnifiedValues() + + hSizeParse = New TSizeParse($sSpacing, True) + $fSpacing = hSizeParse.GetValue() + $bRelativeSpacing = hSizeParse.IsRelative() + + For Each hChild In $aChild + 'hchild.SizeHint = Null + hChild._SetUnifiedValues() + Next + +End + +Public Sub _GenerateClones() + + Dim hNewTControl, hTc As TControl + + Dim i As Integer + Dim hCont As ReportContainer + Dim aNewChild As TControl[] + + Dim aCopy As TControl[] + + If $aChildCopy Then + $aChild = $aChildCopy.Copy() + Endif + aCopy = $aChild.Copy() + + For Each hTc In aCopy + If hTc.Ctrl Is ReportContainer Then + hCont = hTc.Ctrl + If hCont.Data Then + aNewChild = New TControl[] + + For i = 0 To hCont.Data.Count - 1 + + hNewTControl = New TControl + hNewTControl.Ctrl = hTc.Ctrl + hNewTControl.DataIndex = i + aNewChild.Add(hNewTControl) + + Next + $aChild = $aChild.Insert(aNewChild, $aChild.Find(hTc)) + $aChild.Remove($aChild.Find(hTc)) + + Endif + hCont._GenerateClones + Endif + Next + If $aChild.Count <> aCopy.Count Then $aChildCopy = aCopy + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hMyHints As New TSizeHint + + 'If Me._SizeInt.StoreSize Then Return Me._SizeInt + 'If Me.tag = "*" Then Stop + If Me._CurItem > Me.Children.max Then + + ' If Me Is ReportSection Then Stop + Me._CurItem = 0 + ' 'Me._DataIndex = 0 + Endif + Me._Index = DataIndex + If $bIndexChange Then + Raise BeforeData + $bIndexChange = False + Endif + Select Case _Arrangement + + Case Arrange.Vertical + Return GetVSizeInt(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + Case Arrange.Horizontal + Return GetHSizeInt(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + Case Arrange.Column + Return GetVSizeInt(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + Case Arrange.Fill, Arrange.None + Return Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + 'hMyHints.Height = AvailableH + 'Me._SizeInt.StoreSize = True + 'Me._SizeInt = hMyHints + Return hMyHints + + End Select + Raise AfterData + +End + +Private Function GetHSizeInt((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hChild As ReportControl + Dim fHeight, fWidth As Float + Dim hChildHints As TSizeHint + Dim hMyHints As New TSizeHint + Dim fSpacing As Float + + 'D'abord utiliser la méthode du controle pour définir la taille + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + + 'Puis si besoins voir le besoin des enfants + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + fSpacing = IIf(Me._RelativeSpacing, TotalWidth * Me._Spacing / 100, Me._Spacing) + For Each hChild In Me.Children + hChildHints = hchild._GetSizeHints(AvailableW - fWidth, AvailableH, AvailableW, AvailableH, DataIndex) + fWidth += hChildHints.Width + fSpacing + If fHeight < hChildHints.Height Then fHeight = hChildHints.Height + 'Si l'enfant n'est pas finit alors moi non plus + If hChildHints.NotFinished Then hMyHints.NotFinished = True + Next + If fWidth Then fWidth -= fSpacing + hMyHints.Height = fHeight + Me.Padding._Height + Me.Border._Top + Me.Border._bottom + hMyHints.Width = fWidth + Me.Padding._Width + Me.Border._Left + Me.Border._Right + Endif + + Return hMyHints + +End + +Private Function GetVSizeInt((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hSizeInt As New TSizeHint + Dim htmpInts As TSizeHint + Dim hChild As ReportControl ''Enfants + Dim fHeight, fSpacing, fWidth As Float + Dim i, j As Integer + Dim bExitLoop As Boolean + 'If Me.Tag = "Boite 2" Then Stop + 'D'abord utiliser la méthode du controle pour définir la taille + hSizeInt = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + + fSpacing = IIf(Me._RelativeSpacing, TotalHeight * Me._Spacing / 100, Me._Spacing) + + ' Si ce n'est pas fait on enregistre la place nécéssaire a tout les éléments fixes + + If Not $fFixedSize Then + For i = 0 To Me.Children.Max + hChild = Me.Children[i] + htmpInts = hChild._GetSizeHints(AvailableW, AvailableH, AvailableW, AvailableH, DataIndex) + If hchild.Fixed Then $fFixedSize += htmpInts.Height + fSpacing + + Next + + Endif + + 'La taille ne peut être inférieur a celle de tout les éléments fixes + hSizeInt.Height = Max(hSizeInt.Height, $fFixedSize) + + 'On ajoute la taille des objets fixes précédents + fHeight = $fFixedSize + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + 'On définit la taille au besoins des enfants + For i = Me._CurItem To Me.Children.Max + hChild = Me.Children[i] + For j = hchild._DataIndex To hchild._Count - 1 + 'If hchild.Tag = "head" Then Print "VDataHint = " & DataIndex + If hChild.Ignore Then Continue + If hChild.Fixed Then Continue + hchild._Index = j + htmpInts = hChild._GetSizeHints(AvailableW, AvailableH - fHeight, AvailableW, AvailableH, j) + 'Si l'enfant n'est pas terminé alors moi non plus + If htmpInts.NotFinished Then hSizeInt.NotFinished = True + 'on ajoute la hauteur aux besoins + fHeight += htmpInts.Height + fSpacing + If fHeight - fSpacing > AvailableH Then + 'Les enfants ne loge pas ... on ne peut pas finir + hSizeInt.NotFinished = True + Break + bExitLoop = True + Endif + 'On récupère la largeur de l'enfant le plus large + If fWidth < htmpInts.Width Then fWidth = htmpInts.Width + Next + If bExitLoop Then Break + Next + + 'on enlève le dernier espace + If fHeight > 0 Then fHeight -= fSpacing + 'bogue bizzard + fHeight += 0.01 + + 'On indique la place nécéssaire aux enfants+les éléments fixes + hSizeInt.Height = fHeight + Me.Padding._Height + Me.Border._Top + Me.Border._Bottom + hSizeInt.Width = fWidth + Me.Padding._Width + Me.Border._Left + Me.Border._Right + Endif + + 'On ne peut pas dépasser la taille disponible (report sur prochaine page) + 'FIXME: Onepiece partially desactivated + If Not Me.OnePiece Then + hSizeInt.Height = Min(hSizeInt.Height, AvailableH) + Endif + + hSizeInt.Height = Min(TotalHeight, hSizeInt.Height) + + Me._SizeInt = hSizeInt + ' If Me.Tag = "Boite 2" Then + ' Print hSizeInt.Height + ' Stop + ' Endif + 'If Me.tag = "toto" Then Stop + Return hSizeInt + +End + +Public Sub _Reset() + + Dim hChild As ReportControl + 'mise a zéro de l'index de suivit de progression + Me._CurItem = 0 + 'Mise a zéro de l'index de reproduction + Me._DataIndex = 0 + 'Netoyage du layout précédent + 'Me._PageChildren.Clear + 'Nettoyage récurssif des enfants + For Each hChild In Me.Children + + hChild._Reset + + Next + +End + +Public Sub _SetChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + 'If Me.Tag = "*" Then Stop + 'Print "Geometry " & Object.Type(Me) + + Me._Index = TCont.Index + + If Me._CurItem > Me.Children.max Then + ' If Me Is ReportSection Then Stop + Me._CurItem = 0 + 'Me._DataIndex = 0 + Endif + + ' If $bIndexChange Then + ' Raise BeforeData + ' $bIndexChange = False + ' Endif + + Select Case _Arrangement + Case Arrange.Vertical + SetVChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + Case Arrange.Horizontal + SetHChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + Case Arrange.Column + Me._SetCChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + Case Arrange.Fill + SetFChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + Case Arrange.None + SetNChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + End Select + Raise AfterData + +End + +Private Sub SetVChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + + Dim aPageItems As New TControl[] ''Éléments contenu par cette page + Dim hChildHints As TSizeHint ''Besoins en hauteur/largeur de l'enfant + Dim hChild As ReportControl ''Un enfant reportcontrol + Dim fTH As Float ''Hauteur restante + Dim fSpc As Float ''Taille d'un espace + 'Dim X, Y As Float ''Position Haut gauche de départ + Dim hTItem As TControl ''Un objet virtuel + Dim oChild As Object ''Un objet gambas générique + Dim fExp As Float ''taille des objets étendus + Dim iNExp As Integer ''Nombre d'objets étendus + Dim ftmpHeight As Float ''Tampon pour le calcul de la taille répartie + Dim ftmpY As Float ''Curseur temporaire de position haute + Dim fX, fY As Float ''Tampon de position + Dim i, j As Integer ''Des indexs + Dim bExitLoop As Boolean ''Flag de sortie de traitement + 'Dim iPrevIndex As Integer + Dim bForceNewPage As Boolean ''Flag d'anticipation de sortie 1 par page + Dim iPreIndex As Integer + Dim bFirst As Boolean + 'Détermination de la taille d'un espace + 'et prise en compte de la taille relative + fSpc = IIf(Me._RelativeSpacing, H * Me._Spacing / 100, Me._Spacing) + + 'Retrait du padding et des bordures de la hauteur de travail + H = H - Me.Padding._Top - Me.Border._Top - Me.Border._Bottom - Me.Padding._Bottom + + 'Retrait du padding et des bordures a la largeur de travail + W = W - Me.Padding._Left - Me.Border._Left - Me.Border._Right - Me.Padding._Right + + 'Positioinnement du curseur en haut a gauche + X = Me.Padding._Left + Me.Border._Left + Y = Me.Padding._Top + Me.Border._Top + + 'La hauteur restante est initialisée avec la hauteur de travail + fTH = H + + '***************************************************************************************** + 'On enumere tous les enfants a la recherche des éléments fixes + 'Si des éléments fixes sont trouvés alors leur taille est déduite de la taille restante. + For i = 0 To Me.Children.Max + 'On récupère l'enfant + hchild = Me.Children[i] + 'Si l'enfant est fixe alors on le traite. + If hchild.Fixed Then + ' hchild._Index = TCont.Index + 'on commence par récupérer la taille de l'objet + hChildHints = hchild._GetSizeHints(W, fTH, W, H, TCont.Index) + 'On retire la taille de l'objet a la hau'teur restante + 'si l'objet n'est pas ignoré par l'agencement + If Not hchild.Ignore Then fTH = fTH - hChildHints.Height - fSpc + + 'Si l'énumération n'est pas encore arrivée a l'index sauvegardé + 'alors on ajoute directement l'élément a la page + If i < Me._CurItem Then + + 'Génération de l'objet virtuel + hTItem = New TControl + 'Lier l'objet + hTItem.Ctrl = hchild + 'Associer sa taille + hTItem.SizeHint = hChildHints + + hTItem.Index = TCont.Index + 'L'ajouter a la page + aPageItems.Add(hTItem) + + 'Si l'objet est étendu alors on en tient compte + 'sauf si celui-ci est ignoré par l'arrangement + If hchild.Expand And If Not hchild.Ignore Then + fExp += hChildHints.Height + Inc iNExp + Endif + Endif + Endif + Next + + '********************************************************************************* + 'On parcour a présent le reste des éléments + For i = Me._CurItem To Me.Children.Max + 'On récupère l'enfant + hChild = Me.Children[i] + 'Je traite ici la boucle de clonage + 'On définit le point de départ + 'If ContPage = 1 And hchild.Tag = "**" Then Stop + j = hChild._DataIndex + Do + '## Début boucle répétition + + 'On fixe l'index de données des enfants si + 'on est le duplicateur + If hChild._IsContainer And If hChild._count > 1 Then + iPreIndex = j + Else + iPreIndex = TCont.Index + Endif + 'If hchild.tag = "**" Then Print iPreIndex + 'If iPreIndex = 9 Then Stop + 'on récupère la taille de l'enfant + + hChildHints = hchild._GetSizeHints(W, fTH, W, H, iPreIndex) + + 'Les éléments fixes ont déja été traités on ne tient donc pas compte de leur hauteur + 'car elle a déja été déduite de l'espace restant. De meme on ignore les objets flottants (ignore=true) + If Not hchild.Fixed And If Not hchild.Ignore Then + + 'Si l'élément ne loge pas dans la place restante ou + 'si la place restante est insuffisante + 'on provoque la sortie en fin de boucle + If (fTH - hChildHints.Height) < 0 Or If fTH <= 0 Or bForceNewPage Then + ' If hchild.Tag = "*" Then Stop + If Not bFirst Then + hChildHints.Height = fTH + Else + bForceNewPage = False + hchild._DataIndex = j + + 'If Not bForceNewPage Then + bExitLoop = True + Break + Endif + Endif + bFirst = True + 'On déduit la taille de l'objet courant (et l'espace suivant) + fTH = fTH - hChildHints.Height - fspc + + Endif + 'L'objet loge dans la page, on génère donc une représentation de celui-ci + 'c'est a dire un objet virtuel pointant vers celui-ci + 'et apportant les informations nécéssaire au layout final + 'on génère l'objet virtuel + hTItem = New TControl + 'On associe le controle + hTItem.Ctrl = hchild + 'On associe sa taille + hTItem.SizeHint = hChildHints + 'If hchild.Tag = "head" Then Stop + 'On associe l'index de donnée + hTItem.Index = iPreIndex + 'TItem.Index = IIf(hchild._count > 0, j, TCont.Index) + 'On l'ajoute a la page + aPageItems.Add(hTItem) + + 'Si l'objet est étendu alors on tien compte de sa taille + 'pour le calcul de l'espace réparti + If hchild.Expand Then + fExp += hChildHints.Height + Inc iNExp + Endif + + 'If hChildHints.NotFinished Then hchild._DataIndex = j + 'un élément fixe ou ignore ne peut être répété + If hchild.Fixed Or hchild.Ignore Then Break + + 'Sachant que hChild._count peut être à -1 on le considère dans ce cas la comme étant a 0 + 'si j est égale au compte alors on quitte la boucle + If j >= Max(hchild._Count - 1, 0) Then + If Not hChildHints.NotFinished Then hChild._DataIndex = 0 + Break + Endif + + 'sinon on incrémente le compte a condition que le dernier enfant aie finit d'afficher ses enfants + If Not hChildHints.NotFinished Then Inc j + + 'On prévoie un sortie de boucle si l'enfant demande un affichage 1 par page + If hchild.ForceNewPage Then bForceNewPage = True + + Loop + 'Next '## fin de la boucle de répétition + 'Si la sortie anticipé est demandée alors on sort de la boucle + If bExitLoop Then Break + If i < Me.Children.Max Then + If Me.Children[i + 1] Is ReportPageBreak Then + bForceNewPage = True + Inc Me._CurItem + Inc i + Endif + Endif + 'Tout les objets on été répété alors on remet l'index à 0 + 'hchild._DataIndex = 0 + 'On incrémente le compteur des éléments traités + Inc Me._CurItem + Next + + If (H - fTH) > 0 Then + fTH += fspc 'Heu?????? + Endif + + '*************************************************************************** + 'Pour tous les éléments fixes jusqu'à la fin du document + For i = Max(Me._CurItem, 0) To Me.Children.Max + hchild = Me.Children[i] + If hchild.Fixed Then + hChildHints = hchild._GetSizeHints(W, fTH, W, H, iPreIndex) + 'on génère l'objet virtuel + hTItem = New TControl + 'On associe le controle + hTItem.Ctrl = hchild + 'On associe sa taille + hTItem.SizeHint = hChildHints + 'On l'ajoute a la page + aPageItems.Add(hTItem) + + 'Si l'objet est étendu alors on tien compte de sa taille + 'pour le calcul de l'espace réparti + If hchild.Expand Then + fExp += hChildHints.Height + Inc iNExp + Endif + Endif + Next + + '****************************************************************************** + 'A présent tous les éléments pouvant être placé sur la page ont été marqué. + 'On peut donc procéder a la mise en forme de ceux-ci + + ftmpY = Y 'On définit la position de départ + If iNExp Then + fExp = (fExp + fTH) / iNExp + Endif + + For i = 0 To aPageItems.Max + 'on récupère l'objet virtuel + hTItem = aPageItems[i] + 'on récupère l'instance de l'objet réel + ochild = hTItem.Ctrl + + 'Si l'objet est étendu alors on calcul sa hauteur + 'Un objet flottant ne peut pas être étendu + If Not oChild.Ignore And If ochild.Expand Then + 'Calcul de la taille répartie + ftmpHeight = fExp + Else + 'Si l'objet n'est pas étendu alors sa taille est celle demandée par celui-ci + ftmpHeight = hTItem.SizeHint.Height + Endif + 'oChild._Index = hTItem.Index + 'Traitement des objets ignoré + 'Les propriété de taille sont celle fournie par l'objet + If oChild.Ignore Then + 'Calcul de la position de l'objet + 'si sa position est relative (%) alors on fait le ratio a partir de la largeur ou de la hauteur + 'sinon on utilise la position fournie par l'objet + fX = X + IIf(oChild._RelativeLeft, W * oChild._Left / 100, oChild._Left) + fY = Y + IIf(oChild._RelativeTop, H * oChild._Top / 100, oChild._Top) + 'Fixer la position de l'objet + hTItem._SetGeometry(fX, fY, hTItem.SizeHint.Width, ftmpHeight) + 'L'objet flottant peut être un conteneur il faut donc demander aussi a ses enfants de s 'organiser + oChild._SetChildGeometry(fX, fY, hTItem.SizeHint.Width, ftmpHeight, ContPage, hTItem, bInFixed Or Me.Fixed) + + Else + 'Fixer la position de l'objet + hTItem._SetGeometry(X, fTmpY, W, ftmpHeight) + 'L'objet flottant peut être un conteneur il faut donc demander aussi a ses enfants de s 'organiser + oChild._SetChildGeometry(X, fTmpY, W, ftmpHeight, ContPage, hTItem, bInFixed Or Me.Fixed) + 'on incrémente la position verticale de l'objet + 'pour définir sa place sur la page relativement aux autres éléments + fTmpY += ftmpHeight + fspc + + Endif + + 'Si l'objet est un conteneur alors il faut vérifier si il a finit d'être traité + + If oChild Is ReportContainer Then + 'If oChild._NotFinished Then Me.NotFinished = True + 'Si son traitement n'est pas terminé alors on trouve + 'sa position dans la liste des enfant et on l'assigne a l'index de traitement + 'si sa position est > a l'index courant + If ochild._CurItem <= oChild.Children.Max Then + j = Me.Children.Find(oChild) + Me._CurItem = Min(Me._CurItem, j) + Endif + 'Si on est dans la lignée d'un conteneur fixe on incrémente pas la lecture des enfants + 'En clair ce sont toujours les mêmes éléments qui apparaitrons + If bInFixed Then ochild._CurItem = 0 + Endif + Next + + 'On ajoute cette page au dossier du conteneur + TCont._PageChildren[ContPage] = aPageItems + +End + +Private Function SetFChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + + Dim hChild As ReportControl + Dim hTItem As TControl + Dim hChildHints As TSizeHint + Dim aPageItems As New TControl[] + Dim oChild As Object + Dim j As Integer + + X = Me.Padding._Left + Me.Border._Left + Y = Me.Padding._Top + Me.Border._Top + W = W - Me.Padding._Left - Me.Padding._Right - Me.Border._Left - Me.Border._Right + H = H - Me.Padding._Top - Me.Padding._Bottom - Me.Border._Top - Me.Border._Bottom + For Each hChild In Me.Children + hChildHints = hchild._GetSizeHints(W, H, W, H) + hTItem = New TControl + hTItem.Ctrl = hchild + hTItem.SizeHint = hChildHints + + Inc Me._CurItem + aPageItems.Add(hTItem) + + Next + For Each oChild In aPageItems + oChild._Index = hTItem.Index + hTItem._SetGeometry(X, Y, W, H) + hChild._SetChildGeometry(X, Y, W, H, ContPage, hTItem, bInFixed Or Me.Fixed) + If oChild Is ReportContainer Then + + If oChild._CurItem < oChild.Children.count Then + 'Print ochild.tag & " pas fini" + j = Me.Children.Find(oChild) + Me._CurItem = Min(j, Me._CurItem) + Endif + 'ne pas incrémenter la lecture des enfants si je suis dans un élément fixe + If bInFixed Then ochild._CurItem = 0 + End If + Next + + TCont._PageChildren[ContPage] = aPageItems + +End + +Private Function SetNChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + + Dim hChild As ReportControl + Dim ochild As Object + Dim hTItem As TControl + Dim hChildHints As TSizeHint + Dim aPageItems As New TControl[] + Dim iX, iY As Integer + Dim J As Integer + + X = Me.Padding._Left + Me.Border._Left + Y = Me.Padding._Top + Me.Border._Top + W = W - Me.Padding._Left - Me.Padding._Right - Me.Border._Left - Me.Border._Right + H = H - Me.Padding._Top - Me.Padding._Bottom - Me.Border._Top - Me.Border._Bottom + + For Each hChild In Me.Children + + hChildHints = hchild._GetSizeHints(W, H, W, H) + hTItem = New TControl + hTItem.Ctrl = hchild + hTItem.SizeHint = hChildHints + Inc Me._CurItem + aPageItems.Add(hTItem) + + Next + + For Each hTItem In aPageItems + oChild = hTItem.Ctrl + hChildHints = hTItem.SizeHint + iX = X + IIf(oChild._RelativeLeft, W * oChild._Left / 100, oChild._Left) + iY = Y + IIf(oChild._RelativeTop, H * oChild._Top / 100, oChild._Top) + oChild._Index = hTItem.Index + hTItem._SetGeometry(iX, iY, hChildHints.Width, hChildHints.Height) + hChild._SetChildGeometry(iX, iY, hChildHints.Width, hChildHints.Height, ContPage, hTItem, bInFixed Or Me.Fixed) + + If oChild Is ReportContainer Then + + If oChild._CurItem < oChild.Children.count Then + j = Me.Children.Find(oChild) + Me._CurItem = Min(j, Me._CurItem) + Endif + 'ne pas incrémenterla lecture des enfants si je suis dans un élément fixe + If bInFixed Then ochild._CurItem = 0 + End If + Next + TCont._PageChildren[ContPage] = aPageItems + +End + +Private Sub SetHChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + + Dim aPageItems As New TControl[] ''Éléments contenu par cette page + Dim hChildHints As TSizeHint ''Besoins en hauteur/largeur de l'enfant + Dim hChild As ReportControl ''Un enfant reportcontrol + Dim oChild As Object + Dim TW, fWidth, fSpc As Float + Dim fExp As Float + Dim iNExp As Integer + + Dim i, j As Integer + Dim fTmpX, fX, fY As Float + Dim hTItem As TControl + + 'Initialisation des variables + fSpc = IIf(Me._RelativeSpacing, H * Me._Spacing / 100, Me._Spacing) 'ME._Spacing + 'On retire les marges a la hauteur et les bordures + H = H - Me.Padding._Top - Me.Padding._Bottom - Me.Border._Top - Me.Border._Bottom + 'Print H + 'on retire a la largeur les paddings (et les bordure ?) + W = W - Me.Padding._Left - Me.Padding._Right - Me.Border._Left - Me.Border._Right + + 'On positionne le curseur de position au coin a gauche + X = Me.Padding._Left + Me.Border._Left + Y = Me.Padding._Top + Me.Border._Top + + 'On initialise la largeur totale avec la largeur disponible + TW = W + + For i = 0 To Me.Children.Max + + hChild = Me.Children[i] + + 'hchild._Index = IIf(Me.DataCount > 0, j, TCont.Index) + hChild._Index = TCont.Index + hChildHints = hChild._GetSizeHints(w, h, w, h, TCont.Index) + + 'Si l' éléments ne loge pas on quitte et on oublit + If TW - hChildHints.Width < 0 Then Break + + 'Si l'élément n'est pas ignoré + If Not hChild.Ignore Then + TW = TW - hChildHints.Width - fSpc + 'Si il est étendu on en tient compte + If hChild.Expand Then + Inc iNExp + fExp += hChildHints.Width + Endif + Endif + + 'On ajoute l'élément a la page + hTItem = New TControl + hTItem.Ctrl = hChild + hTItem.SizeHint = hChildHints + hTItem.Index = TCont.Index + aPageItems.Add(hTItem) + + Next + + 'De toute les façon je ne cherche pas a parcourir tout + 'Les objet donc j'indique que j'ai tout vu + Me._CurItem = Me.Children.count + + If (W - TW) > 0 Then + TW += fspc + Endif + + 'On va mettre en page a présent + 'On définit la taille des éléments étendus + If iNexp Then + fExp = (TW + fExp) / iNexp + Endif + + fTmpX = X + For Each hTItem In aPageItems + oChild = hTItem.Ctrl + 'Si l'élément est étendu on lui applique la taille répartie + If oChild.Expand And Not oChild.Ignore Then + 'If Me.Tag = "*" Then Stop + fWidth = fExp + Else + 'sinon il maintien sa taille + fWidth = hTItem.SizeHint.Width + Endif + 'oChild._Index = hTItem.Index + If Not oChild.Ignore Then + hTItem._SetGeometry(fTmpX, Y, fWidth, H) + oChild._SetChildGeometry(fTmpX, Y, fWidth, H, ContPage, hTItem, bInFixed Or Me.Fixed) + fTmpX += fWidth + fspc + Else + fX = X + IIf(oChild._RelativeLeft, W * oChild._Left / 100, oChild._Left) + fY = Y + IIf(oChild._RelativeTop, H * oChild._Top / 100, oChild._Top) + hTItem._SetGeometry(fX, fY, fWidth, hTItem.SizeHint.Height) + oChild._SetChildGeometry(fX, fY, fWidth, hTItem.SizeHint.Height, ContPage, hTItem, bInFixed Or Me.Fixed) + Endif + + If oChild Is ReportContainer Then + If oChild._CurItem <= oChild.Children.max Then + 'Print ochild.tag & " pas fini" + j = Me.Children.Find(oChild) + Me._CurItem = Min(Me._CurItem, j) + Endif + 'ne pas incrémenter la lecture des enfants si je suis dans un élément fixe + If bInFixed Then ochild._CurItem = 0 + + Endif + Next + + 'On ajoute la page a la collection de page du conteneur + TCont._PageChildren[ContPage] = aPageItems + +End + +Private Function DataCount_Read() As Integer + + Return Me._Count + +End + +Private Sub DataCount_Write(Value As Integer) + 'If value = 0 Then value = 1 + + Me._Count = Value + +End + +' Public Function _GetIndex() As Integer +' +' If Me._Count > 0 Then 'Si c'est moi le répéteur alors je fournis mon index +' Return Me._Index +' Else +' Try Return Me.Parent._GetIndex 'Sinon je ontinue a remonter la lignée jusqu'au répéteur +' Endif +' +' End + +Private Function _Index_Read() As Integer + + Return Super._Index + +End + +Private Sub _Index_Write(Value As Integer) + + If Super._Index = Value And If Super._Index > 0 Then Return + $bIndexChange = True + Super._Index = Value + +End + +Private Function _CurItem_Read() As Integer + + Return $iCurItem + +End + +Private Sub _CurItem_Write(Value As Integer) + 'Print "_CurItem = " & Value + 'If Me.tag = "Boite 1" And Value = 0 Then Stop + 'If Me.tag = "Boite 1" And ContPage = 1 Then Print "CurItem: " & Me._CurItem + + $iCurItem = Value + +End + +Private Function OnePiece_Read() As Boolean + + Return $bOnePiece + +End + +Private Sub OnePiece_Write(Value As Boolean) + + $bOnePiece = Value + +End + +Public Sub _SetCChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + +End diff --git a/comp/src/gb.report/.src/ReportDrawingArea.class b/comp/src/gb.report/.src/ReportDrawingArea.class new file mode 100644 index 00000000..53fd2453 --- /dev/null +++ b/comp/src/gb.report/.src/ReportDrawingArea.class @@ -0,0 +1,63 @@ +' Gambas class file + +Export +Inherits ReportFrame +Public Const _Properties As String = "*,Cached" +Public Const _Similar As String = "ReportImage" +Public Const _DefaultEvent As String = "Draw" + +Property Cached As Boolean + +Private $bCached As Boolean +Private $iOldResolution As Integer + +Event Layout(Width As Integer, Height As Integer, Index As Integer) +Event Draw(Width As Integer, Height As Integer, Index As Integer) + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim hRect As Rect + Dim iX, iY, iW, iH As Integer + Dim hImg As Image + + iX = (x + hControl.RealLeft + ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Border._Left)) + iY = (y + hControl.RealTop + ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Border._Top)) + iW = (hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + iH = (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + Paint.Save + + + If $bCached Then + If $iOldResolution <> Paint.ResolutionX Or If hControl.Cache = Null Then + $iOldResolution = Paint.ResolutionX + hImg = New Image(iW, iH, Color.Transparent) + Paint.Begin(hImg) + Raise Draw(iW, iH, DataIndex) + Paint.End + hControl.Cache = himg + Else + If hControl.cache = Null Then Stop + hImg = hControl.Cache + Endif + Draw.Image(hImg, iX, iy) + Else + + Paint.Translate(iX, iY) + Raise Draw(iW, iH, DataIndex) + Paint.Translate(- iX, - iY) + Endif + Paint.Restore + +End + +Private Function Cached_Read() As Boolean + + Return $bCached + +End + +Private Sub Cached_Write(Value As Boolean) + + $bCached = Value + +End diff --git a/comp/src/gb.report/.src/ReportFrame.class b/comp/src/gb.report/.src/ReportFrame.class new file mode 100644 index 00000000..96cd44df --- /dev/null +++ b/comp/src/gb.report/.src/ReportFrame.class @@ -0,0 +1,354 @@ +' Gambas class file + +Export +Create Private +Inherits ReportControl +' Static Private iLevel As Integer +Public Const _Properties As String = "*,Border,Background{ReportBrush}" + +' Private $fLeftBorderWidth As Float +' Private $fRightBorderWidth As Float +' Private $fTopBorderWidth As Float +' Private $fBottomBorderWidth As Float + +Private $hBorder As New ReportBorder + +Private $hBackGround As ReportBrush + +Property Border As ReportBorder +Property BoxShadow As ReportBoxShadow + +Private $hBoxShadow As New ReportBoxShadow +Property BackGround As ReportBrush +'Property Read _BorderWidth As Float + +Private Function Border_Read() As ReportBorder + + Return $hBorder + +End + +Private Sub Border_Write(Value As ReportBorder) + + $hBorder = Value + +End + +Public Sub _PaintBefore((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + + Dim X1, Y1, X2, Y2 As Float + Dim iBorder As Integer + Dim hShadowActive As Boolean = $hBoxShadow._Active + Dim himgShadow As Image + Dim fSpread As Float + Dim fBlur As Float + Dim fbx As Float + Dim fby As Float + Dim TL1, TL2, TR1, TR2, BR1, BR2, BL1, BL2 As Float + 'If Me.tag = "*" Then Stop + + X1 = (x + hControl.RealLeft) + Y1 = (y + hControl.RealTop) + X2 = (x + hControl.RealLeft + hControl.RealWidth) + Y2 = (y + hControl.RealTop + hControl.RealHeight) + 'Clipping + paint.save + + ' Print String(iLevel, " ") & "sauvé" + ' Inc iLevel + ' Paint.Rectangle(X1, Y1, X2 - X1, Y2 - Y1) + ' paint.Clip(True) + + If Report.Debug Then + + Paint.Brush = Paint.Color(Color.Green) + 'Paint.Dash = ReportUnits.GetBorder(Line.Dot) + Paint.Rectangle(X1, Y1, X2 - X1, Y2 - Y1) + Paint.Stroke + + Else + 'If $hBorder.Shadow.Style > ReportShadowStyle.None Then PaintShadow(X1, Y1, hControl.RealWidth, hControl.RealHeight, $hBorder.RoundCorner._Active) + If $hBorder.RoundCorner._Active Then + TL1 = ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._TopLeft1) + TR1 = ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._TopRight1) + BR1 = ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._BottomRight1) + BL1 = ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._BottomLeft1) + TL2 = ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._TopLeft2) + TR2 = ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._TopRight2) + BR2 = ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._BottomRight2) + BL2 = ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._BottomLeft2) + + If hShadowActive Then + fbx = ReportUnits._ReportUnitsToPixels($hBoxShadow._XOffset) + fby = ReportUnits._ReportUnitsToPixels($hBoxShadow._YOffset) + fSpread = ReportUnits._ReportUnitsToPixels($hBoxShadow._Spread) + If $hBoxShadow._Blur > 0 Then + fBlur = ReportUnits._ReportUnitsToPixels($hBoxShadow._Blur) + himgShadow = New Image(hControl.RealWidth + fSpread * 2 + fBlur * 2, hControl.RealHeight + fSpread * 2 + fBlur * 2, Color.Transparent) + Paint.Begin(himgShadow) + Paint.Brush = Paint.Color($hBoxShadow.Color) + RoundRect(fBlur, fBlur, Paint.Width - fBlur * 2, Paint.Height - fBlur * 2, [TL1, TR1, BR1, BL1], [TL2, TR2, BR2, BL2]) + Paint.Fill + Paint.End + himgShadow.Fuzzy(fBlur) + Draw.Image(himgShadow, X1 + fbx - fSpread - fBlur, Y1 + fby - fSpread - fBlur) + Else + + Paint.Brush = Paint.Color($hBoxShadow.Color) + RoundRect(X1 + fbx - fSpread, Y1 + fby - fSpread, hControl.RealWidth + fSpread * 2, hControl.RealHeight + fSpread * 2, [TL1, TR1, BR1, BL1], [TL2, TR2, BR2, BL2]) + Paint.Fill + Endif + Endif + + iBorder = ReportUnits._ReportUnitsToPixels($hBorder._Top) + + RoundRect(X1 + iBorder, Y1 + iBorder, hControl.RealWidth - iBorder * 2, hControl.RealHeight - iBorder * 2, [TL1, TR1, BR1, BL1], [TL2, TR2, BR2, BL2]) + + Else + + If hShadowActive Then + fbx = ReportUnits._ReportUnitsToPixels($hBoxShadow._XOffset) + fby = ReportUnits._ReportUnitsToPixels($hBoxShadow._YOffset) + fSpread = ReportUnits._ReportUnitsToPixels($hBoxShadow._Spread) + If $hBoxShadow._Blur > 0 Then + fBlur = ReportUnits._ReportUnitsToPixels($hBoxShadow._Blur) + himgShadow = New Image(hControl.RealWidth + fSpread * 2 + fBlur * 2, hControl.RealHeight + fSpread * 2 + fBlur * 2, Color.Transparent) + Paint.Begin(himgShadow) + Paint.Brush = Paint.Color($hBoxShadow.Color) + Paint.rectangle(fBlur, fBlur, Paint.Width - fBlur * 2, Paint.Height - fBlur * 2) + Paint.Fill + Paint.End + himgShadow.Fuzzy(fBlur) + Draw.Image(himgShadow, X1 + fbx - fSpread - fBlur, Y1 + fby - fSpread - fBlur) + Else + Endif + Paint.Brush = Paint.Color($hBoxShadow.Color) + Paint.Rectangle(X1 + fbx - fSpread, Y1 + fby - fSpread, hControl.RealWidth + fSpread * 2, hControl.RealHeight + fSpread * 2) + Paint.Fill + Endif + + Paint.Rectangle(X1, Y1, X2 - X1, Y2 - Y1) + + Endif + + If Me.BackGround = Null Then + paint.Clip + Return + Else + paint.Clip(True) + Endif + paint.Brush = Me.BackGround._PaintBrush(X1, Y1, X2, Y2) + + Paint.Fill + + Endif + +End + +Private Sub PaintBoxShadow(X As Integer, Y As Integer, Width As Integer, Height As Integer) + +End + +Public Sub _PaintFrame((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + + _PaintBefore(Page, X, Y, hControl, DataIndex) + Me._Paint(Page, X, Y, hControl, DataIndex) + _PaintAfter(Page, X, Y, hControl, DataIndex) + +End + +Public Sub _PaintAfter((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + + Dim X1, Y1, X2, Y2 As Float + 'Dim fWidth As Float + + Dim fTopWidth, fBottomWidth, fLeftWidth, fRighWidth As Float + + 'Fin du clipping + paint.Restore + + X1 = (x + hControl.RealLeft) + Y1 = (y + hControl.RealTop) + X2 = (x + hControl.RealLeft + hControl.RealWidth) + Y2 = (y + hControl.RealTop + hControl.RealHeight) + 'If Me.Tag = "**" Then Stop + If $hBorder.RoundCorner._Active Then + Paint.AntiAlias = True + fLeftWidth = ReportUnits._ReportUnitsToPixels($hBorder._Left) / 2 + paint.LineWidth = fLeftWidth * 2 + paint.Brush = $hBorder.Left.Brush._PaintBrush(X1, Y1, X2, Y2) + ' RoundRect(Paint.LineWidth / 2, Paint.LineWidth / 2, Paint.Width - Paint.LineWidth, Paint.Height - Paint.LineWidth, aX, aY) + RoundRect(X1 + fLeftWidth, Y1 + fLeftWidth, hControl.RealWidth - fLeftWidth * 2, hControl.RealHeight - fLeftWidth * 2, + [ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._TopLeft1), + ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._TopRight1), + ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._BottomRight1), + ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._BottomLeft1)], + [ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._TopLeft2), + ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._TopRight2), + ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._BottomRight2), + ReportUnits._ReportUnitsToPixels($hBorder.RoundCorner._BottomLeft2)]) + paint.Stroke + + Else + 'Paint.AntiAlias = False + fLeftWidth = ReportUnits._ReportUnitsToPixels($hBorder._Left) + fRighWidth = ReportUnits._ReportUnitsToPixels($hBorder._Right) + fTopWidth = ReportUnits._ReportUnitsToPixels($hBorder._Top) + fBottomWidth = ReportUnits._ReportUnitsToPixels($hBorder._Bottom) + + 'If $hBorder.Style <> Line.None Then + + 'fWidth = ReportUnits._ReportUnitsToPixels($fBorderWidth) + + 'Paint.Brush = Paint.Color($hBorder.Brush._iValue) + + If fTopWidth > 0 Then + paint.Brush = $hBorder.Top.Brush._PaintBrush(X1, Y1, X2, Y2) + paint.Rectangle(X1, Y1, X2 - X1, fTopWidth) + paint.Fill + Endif + If fRighWidth > 0 Then + paint.Brush = $hBorder.Right.Brush._PaintBrush(X1, Y1, X2, Y2) + paint.Rectangle(X2 - fRighWidth, Y1, fRighWidth, Y2 - Y1) + paint.Fill + Endif + If fBottomWidth > 0 Then + paint.Brush = $hBorder.Bottom.Brush._PaintBrush(X1, Y1, X2, Y2) + paint.Rectangle(X1, Y2 - fBottomWidth, X2 - X1, fBottomWidth) + paint.Fill + Endif + If fLeftWidth > 0 Then + paint.Brush = $hBorder.Left.Brush._PaintBrush(X1, Y1, X2, Y2) + paint.Rectangle(X1, Y1, fLeftWidth, Y2 - Y1) + paint.Fill + Endif + 'Paint.AntiAlias = True + ' + + Endif + 'Dec iLevel + 'Print String(iLevel, " ") & "restoré" + + 'Endif + +End + +Private Function BackGround_Read() As ReportBrush + + Return $hBackGround + +End + +Private Sub BackGround_Write(Value As ReportBrush) + + $hBackGround = Value + +End + +Public Sub _SetUnifiedValues() + + Super._SetUnifiedValues() + $hBorder._SetUnifiedValues + $hBoxShadow._SetUnifiedValues + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hMyHints As TSizeHint + + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + + If hMyHints.Height <= 0 Then + hMyHints.Height = Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom + 'hMyHints.Height += Abs($hBoxShadow._YOffset) + $hBoxShadow._Spread + $hBoxShadow._Blur + Endif + + If hMyHints.Width <= 0 Then + hMyHints.Width = Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right + 'hMyHints.Width += Abs($hBoxShadow._xOffset) + $hBoxShadow._Spread + $hBoxShadow._Blur + Endif + + Return hMyHints + +End + +Static Private Sub RoundRect(x As Float, y As Float, w As Float, h As Float, Radius_X As Float[], radius_y As Float[]) + + Dim ARC_TO_BEZIER As Float = 0.55228475 + Dim c1, c2 As Float + Dim i As Integer + + For i = 0 To 3 + If radius_x[i] > w - radius_x[i] Then + radius_x[i] = w / 2 + Endif + + If radius_y[i] > h - radius_y[i] Then + radius_y[i] = h / 2 + Endif + + Next + 'approximate(quite Close )the arc using a bezier curve + + ' A**********B + ' H C + ' * * + ' * * + ' G D + ' F**********E + + '-->A + Paint.MoveTo(x + radius_x[0], y) + + '-->B + Paint.LineTo(x + w - radius_x[1], y) + + '-->C + c1 = ARC_TO_BEZIER * radius_x[1] + c2 = ARC_TO_BEZIER * radius_y[1] + Paint.RelCurveTo(c1, 0.0, radius_x[1], c2, radius_x[1], radius_y[1]) + + '-->D + Paint.LineTo(x + w, y + h - radius_y[2]) + + '-->E + c1 = ARC_TO_BEZIER * radius_x[2] + c2 = ARC_TO_BEZIER * radius_y[2] + Paint.RelCurveTo(0.0, c2, c1 - radius_x[2], radius_y[2], - radius_x[2], radius_y[2]) + + '-->F + Paint.LineTo(x + radius_x[3], y + h) + + '-->G + c1 = ARC_TO_BEZIER * radius_x[3] + c2 = ARC_TO_BEZIER * radius_y[3] + Paint.RelCurveTo(- c1, 0, - radius_x[3], - c2, - radius_x[3], - radius_y[3]) + + '-->H + Paint.LineTo(x, y + radius_y[0]) + + '-->A + c1 = ARC_TO_BEZIER * radius_x[0] + c2 = ARC_TO_BEZIER * radius_y[0] + Paint.relcurveto(0.0, - c2, radius_x[0] - c1, - radius_y[0], radius_x[0], - radius_y[0]) + + 'Paint.closepath() + +End + +Private Function BoxShadow_Read() As ReportBoxShadow + + Return $hBoxShadow + +End + +Private Sub BoxShadow_Write(Value As ReportBoxShadow) + + $hBoxShadow = Value + +End + +Public Sub _Free() + +End diff --git a/comp/src/gb.report/.src/ReportGridView.class b/comp/src/gb.report/.src/ReportGridView.class new file mode 100644 index 00000000..3b61daf1 --- /dev/null +++ b/comp/src/gb.report/.src/ReportGridView.class @@ -0,0 +1,188 @@ +' Gambas class file + +Export +Inherits ReportFrame +Public Const _Properties As String = "*" +Public Const _Similar As String = "ReportTextLabel" +Public Const _DefaultEvent As String = "Data" + +Property Read Columns As _ReportGridViewColumns +Property Read Rows As _ReportGridViewRows +'Property Autoresize As Boolean + +Private $bAutoresize As Boolean +Private $hColumns As _ReportGridViewColumns +Private $hRows As _ReportGridViewRows + + + + +Event Data(Row As Integer, Column As Integer) + +Public Sub _new() + $hColumns = New _ReportGridViewColumns As "Columns" + $hRows = New _ReportGridViewRows As "Rows" + + +End + + +Public Sub _SetUnifiedValues() + + Dim i As Integer + Super._SetUnifiedValues + $hRows._SetUnifiedValues() + $hColumns._SetUnifiedValues() + ' For i = 0 To $hColumns.Max + ' $hColumns[i]._Width = TSizeParse[$hColumns[i].Width].ToCm() + ' Next + ' + ' For i = 0 To $hRows.Max + ' $hRows[i]._Height = TSizeParse[$hRows[i].Height].ToCm() + ' Next + + +End + + + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hMyHints As New TSizeHint + Dim fWidth As Float + Dim i As Integer + 'On Récupère la taille standard + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + If hMyHints.Width = 0 Then hMyHints.Width = 1 + If hMyHints.Height = 0 Then hMyHints.Height = 5 + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + + 'Largeur + + ' For i = 0 To $hColumns.Max + ' + ' fWidth += $hColumns[i].Width + ' + ' Next + + + + Endif + + + + + + Return hMyHints + + End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim iColWidth As Float = ReportUnits._ReportUnitsToPixels(Me.Rows._Width) + Dim iRowHeight As Float = ReportUnits._ReportUnitsToPixels(Me.Rows._Height) + Dim i, j As Integer + Dim fCurY, fCurX, fH, fRow As Float + Dim fRowWidth As Float = ReportUnits._ReportUnitsToPixels($hRows._Width) + Dim fColHeight As Float = ReportUnits._ReportUnitsToPixels($hColumns._Height) + Dim fX As Float = X + hControl.RealLeft + ReportUnits._ReportUnitsToPixels(Me.Border._Left) + Dim fY As Float = Y + hControl.RealTop + ReportUnits._ReportUnitsToPixels(Me.Border._Top) + 'Paint.Brush = Paint.Color(Color.Red) + 'Paint.Rectangle(X + hControl.RealLeft, Y + hControl.RealTop, hControl.RealWidth, hControl.RealHeight) + 'Paint.Fill + Paint.LineWidth = ReportUnits._ReportUnitsToPixels(Me.Border._Top) + + + fCurY = fY + fColHeight + + + For i = 0 To $hRows.Max + + Paint.MoveTo(fX, fCurY) + Paint.LineTo(fX + hControl.RealWidth, fCurY) + fCurY += ReportUnits._ReportUnitsToPixels($hRows[i]._Height) + + Next + + fCurX = fX + fRowWidth + + For i = 0 To $hColumns.max + + Paint.MoveTo(fCurX, fY) + Paint.LineTo(fCurX, fY + hControl.RealHeight) + fCurX += ReportUnits._ReportUnitsToPixels($hColumns[i]._Width) + + Next + + + + + + + Paint.Stroke + + + + + + + + + + + + fCurY = fY + fColHeight + For i = 0 To $hRows.Max + fH = ReportUnits._ReportUnitsToPixels($hRows[i]._Height) + Paint.Text($hRows[i].Text, fX, fCurY, fRowWidth, fH, Align.Right) + Paint.Fill + fCurY += fH + Next + + + + + + + + + + + +End + + + + + + + + + + + + +Private Function Columns_Read() As _ReportGridViewColumns + + Return $hColumns + +End + +Private Function Rows_Read() As _ReportGridViewRows + + Return $hRows + +End + +Private Function Autoresize_Read() As Boolean + + Return $bAutoresize + +End + +Private Sub Autoresize_Write(Value As Boolean) + + $bAutoresize = Value + +End diff --git a/comp/src/gb.report/.src/ReportGridView/_ReportGridViewCell.class b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewCell.class new file mode 100644 index 00000000..f5baf4d7 --- /dev/null +++ b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewCell.class @@ -0,0 +1 @@ +' Gambas class file diff --git a/comp/src/gb.report/.src/ReportGridView/_ReportGridViewColumn.class b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewColumn.class new file mode 100644 index 00000000..f26daa8a --- /dev/null +++ b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewColumn.class @@ -0,0 +1,18 @@ +' Gambas class file + +Property Width As String +Public _Width As Float + + + +Private Function Width_Read() As String + + + +End + +Private Sub Width_Write(Value As String) + + + +End diff --git a/comp/src/gb.report/.src/ReportGridView/_ReportGridViewColumns.class b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewColumns.class new file mode 100644 index 00000000..bc35faca --- /dev/null +++ b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewColumns.class @@ -0,0 +1,117 @@ +' Gambas class file + +Property Count As Integer +Property Read max As Integer + +Property Width As String +Private $sWidth As String +Property Height As String +Private $sHeight As String +Public _Height As Float +Private $aColumns As New _ReportGridViewColumn[] +Event _Foo + + + + + +Public Sub _SetUnifiedValues() + Dim hCol As _ReportGridViewColumn + If $sHeight Then + _Height = TSizeParse[$sHeight].ToCm() + Else + _Height = ReportUnits._PixelsToReportUnits(GetView().Font.TextHeight("")) + Endif + + For Each hcol In $aColumns + + If hcol.Width Then + hcol._Width = TSizeParse[hcol.Width].ToCm() + Else + 'TODO: Calcul de la largeur des colonnes + hcol._Width = 2 + Endif + Next + + + + +End + + + + + + + + +Private Function Count_Read() As Integer + + Return $aColumns.Max + +End + +Public Function _get(Index As Integer) As _ReportGridViewColumn + + Return $aColumns[Index] + +End + + + +Private Sub Count_Write(Value As Integer) +Dim hRow As _ReportGridViewColumn +Dim i As Integer + If Value = $aColumns.Count Then Return + If Value > $aColumns.Count Then + For i = $aColumns.Count To Value + hRow = New _ReportGridViewColumn + $aColumns.Add(hRow) + Next + + Else + + $aColumns.Remove(Value, $aColumns.Count - Value) + + Endif + +End + +Private Function max_Read() As Integer + + Return $aColumns.Max + +End + +Private Function Width_Read() As String + + If $aColumns.Count Then + Return $aColumns[0].Width + Endif + +End + +Private Sub Width_Write(Value As String) + Dim hCol As _ReportGridViewColumn + For Each hcol In $aColumns + hcol.Width = Value + Next +End + +Private Function Height_Read() As String + + Return $sHeight + +End + +Private Sub Height_Write(Value As String) + + $sHeight = Value + +End + +Private Sub GetView() As ReportGridView + + Return Object.Parent(Me) + +End diff --git a/comp/src/gb.report/.src/ReportGridView/_ReportGridViewRow.class b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewRow.class new file mode 100644 index 00000000..153324af --- /dev/null +++ b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewRow.class @@ -0,0 +1,51 @@ +' Gambas class file + +Property Height As String +Property Text As String + +Property _Height As Float + +Public _Row As Integer +Event _Foo + +Private Function Height_Read() As String + + + +End + +Private Sub Height_Write(Value As String) + + + +End + +Private Function Text_Read() As String + + Return GetView()._GetRowText(_Row) + +End + +Private Sub Text_Write(Value As String) + + + +End + +Private Function _Height_Read() As Float + + Return GetView()._GetHeight() + +End + +Private Sub _Height_Write(Value As Float) + + + +End + +Private Sub GetView() As _ReportGridViewRows + + Return Object.Parent(Me) + +End \ No newline at end of file diff --git a/comp/src/gb.report/.src/ReportGridView/_ReportGridViewRows.class b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewRows.class new file mode 100644 index 00000000..5d9fda26 --- /dev/null +++ b/comp/src/gb.report/.src/ReportGridView/_ReportGridViewRows.class @@ -0,0 +1,160 @@ +' Gambas class file + +Property Count As Integer +Property Read Max As Integer + +Private $cHeight As Collection +Private $aHeight As Integer[] +Private $cText As Collection +Private $nRows As Integer + +Property Height As String +Property Width As String + +Private $sHeight As String +Private $sWidth As String + +Private $fWidth As Float +Private $fHeight As Float +Property Read _Height As Float +Property Read _Width As Float + + + +Event _Foo + +Public Sub _SetUnifiedValues() + Dim hView As ReportGridView = GetView() + If $sHeight Then + $fHeight = TSizeParse[$sHeight].ToCm() + Else + $fHeight = ReportUnits._PixelsToReportUnits(hView.Font.Height + 4) + Endif + + If $sWidth Then + $fWidth = TSizeParse[$sWidth].ToCm() + + Else + $fWidth = ReportUnits._PixelsToReportUnits(hView.Font.TextWidth("9") * (1 + CInt(Log10(Max(1, $nRows)))) + 8) + Endif + + +End + +Public Sub _GetHeight() As Float + + Return $fHeight + +End + + + +Public Sub _GetRowText(iRow As Integer) As String + + Dim sText As String + + If $cText Then sText = $cText[iRow] + If Not sText Then sText = CStr(iRow + 1) + Return sText + +End + +Public Sub _SetRowText(iRow As Integer, sText As String) + + Dim W As Float + + If Not $cText Then $cText = New Collection + $cText[iRow] = sText + If sText Then + W = ReportUnits._PixelsToReportUnits(GetView().Font.TextWidth(sText) + 8) + If W > _Width_Read() Then $fWidth = W + Endif + +End + + + +Public Function _get(Index As Integer) As _ReportGridViewRow + Dim hRow As New _ReportGridViewRow As "Row" + hRow._Row = Index + Return hRow + +End + + +Private Function Count_Read() As Integer + + Return $nRows + +End + +Private Sub Count_Write(Value As Integer) +Dim hView As ReportGridView = GetView() + Dim iRow As Integer + + If Value = $nRows Then Return + + $nRows = Value + + If $aHeight Then + While $aHeight.Count + If $aHeight[$aHeight.Max] < $nRows Then Break + iRow = $aHeight[$aHeight.Max] + $cHeight.Remove(iRow) + $aHeight.Remove($aHeight.Max) + 'If hView.Mode = Select.Multiple Then $hSel.UnSelect(iRow) + Wend + Endif + +End + +Private Function Max_Read() As Integer + + Return $nRows - 1 + +End + +Private Sub GetView() As ReportGridView + + Return Object.Parent(Me) + +End + +Private Function Height_Read() As String + + Return $sHeight + +End + +Private Sub Height_Write(Value As String) + + $sHeight = Value + +End + +Private Function _Height_Read() As Float + + + Return $fHeight + +End + +Private Function _Width_Read() As Float + + Return $fWidth + +End + + +Private Function Width_Read() As String + + Return $sWidth + +End + +Private Sub Width_Write(Value As String) + + $sWidth = value + +End + diff --git a/comp/src/gb.report/.src/ReportHBox.class b/comp/src/gb.report/.src/ReportHBox.class new file mode 100644 index 00000000..95bff9e9 --- /dev/null +++ b/comp/src/gb.report/.src/ReportHBox.class @@ -0,0 +1,21 @@ +' Gambas class file + +Export + +Inherits ReportContainer + +Public Const _Properties As String = "*" +Public Const _Similar As String = "ReportVBox" +Public Const _DefaultArrangement As String = "H" + +Public Sub _new() + + Super._Arrangement = Arrange.Horizontal + +End + +Public Sub _Free() + + Super._Free + +End diff --git a/comp/src/gb.report/.src/ReportImage.class b/comp/src/gb.report/.src/ReportImage.class new file mode 100644 index 00000000..78e95f01 --- /dev/null +++ b/comp/src/gb.report/.src/ReportImage.class @@ -0,0 +1,186 @@ +' Gambas class file + +Export +Inherits ReportFrame + +Public Const _Properties As String = "*,Stretch{Report.None;Proportional;Fill}=Proportional,Alignment{Align.*},Image{Image}" +Public Const _Similar As String = "ReportTextLabel" +Public Const _DefaultEvent As String = "Data" + +Private $iAlignment As Integer = Align.Normal +Private $hPic As Image +Private $iStretchMode As Integer = Report.Proportional + +Property Alignment As Integer +Property Image As Image +Property Stretch As Integer + + + +Public Data As Image + +Event Data(Index As Integer) + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hMyHints As New TSizeHint + + + Dim hPic As Image + + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + If $hpic Then + hpic = $hpic + Else + Raise Data(DataIndex) + hpic = Data + Endif + + If hpic Then + hMyHints.Width = Max(hMyHints.Width, Me.Padding._Left + ReportUnits.UnitToCm(hpic.Width, "px") + Me.Padding._Right) + hMyHints.Height = Max(hMyHints.Width, Me.Padding._Top + ReportUnits.UnitToCm(hpic.Height, "px") + Me.Padding._Bottom) + Endif + + Endif + + Return hMyHints + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim ix, iy As Float + Dim hBrush As PaintBrush + Dim hPic As Image + + Dim w, h As Float + + + ix = x + hControl.RealLeft '+ ReportUnits._ReportUnitsToPixels(Me.Padding._Left) + iy = y + hControl.RealTop '+ ReportUnits._ReportUnitsToPixels(Me.Padding._Top) + + If Not $hpic Then + Raise Data(DataIndex) + hpic = Data + If Not hPic Then Return + Else + hPic = $hpic + Endif + + '$hPic = $hPic.Stretch(hControl.RealWidth, hControl.RealHeight) + + hBrush = Paint.Image(hpic) + + If Me.Stretch = Report.Fill Then + iX += ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Border._Left) + iY += ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Border._Top) + w = (hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + h = (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + hBrush.Translate(ix, iy - 1) + hBrush.Scale(w / hPic.Width, h / hPic.Height) + Paint.Brush = hBrush + Paint.Rectangle(ix, iy, w, h) + Else + + If $iStretchMode = Report.Proportional Then + 'on détermine la partie prédominante + If hpic.Width >= hpic.Height + 'C'est la largeur + 'on détermine une hauteur en fonction de la largeur connue + w = hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Width) + h = hPic.H / hpic.W * w + 'si h> a la place disponible alors on adapte en fonction de h en faite + If h > (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Height)) Then + h = (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Height)) + w = hpic.w / hpic.H * h + Endif + + Else + 'C'est la hauteur + h = (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Height)) + w = hpic.w / hpic.H * h + 'si w> la place disponible alors on adapte en fonction de w en faite + If w > (hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Width)) Then + w = hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Width) + h = hPic.H / hpic.W * w + Endif + Endif + + Else + w = hpic.Width + h = hpic.H + + Endif + + Select Case $iAlignment + Case Align.Normal, Align.TopLeft, Align.Left, Align.BottomLeft + 'Gauche + ix += ReportUnits._ReportUnitsToPixels(Me.Padding._Left) + Case Align.Bottom, Align.Center, Align.Top + 'centrée + ix += (hControl.RealWidth - w) / 2 + + Case Align.TopRight, Align.Right, Align.BottomRight + 'Droite + ix += hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Right) - w + End Select + + Select Case $iAlignment + Case Align.TopLeft, Align.Top, Align.TopRight + 'Haut + iy += ReportUnits._ReportUnitsToPixels(Me.Padding._Top) + Case Align.Left, Align.Center, Align.Right + 'Milieu + iy += (hControl.RealHeight - h) / 2 + Case Align.BottomLeft, Align.Bottom, Align.BottomRight + iY += hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Bottom) - h + End Select + hBrush.Translate(ix, iy - 1) + hBrush.Scale(w / hpic.Width, h / hpic.H) + Paint.Brush = hBrush + Paint.Rectangle(ix, iy, w, h - 1) + Endif + Paint.Fill + +End + +Private Function Image_Read() As Image + + Return $hPic + +End + +Private Sub Image_Write(Value As Image) + + $hPic = Value + 'If Left(Me.Width, 1) = "0" Then Me.Width = $hpic.Width & " px" + 'If Left(Me.Height, 1) = "0" Then Me.Height = $hpic.Height & " px" + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End + +Private Function Stretch_Read() As Integer + + Return $iStretchMode + +End + +Private Sub Stretch_Write(Value As Integer) + + $iStretchMode = Value + +End + diff --git a/comp/src/gb.report/.src/ReportLabel.class b/comp/src/gb.report/.src/ReportLabel.class new file mode 100644 index 00000000..c44fb1f7 --- /dev/null +++ b/comp/src/gb.report/.src/ReportLabel.class @@ -0,0 +1,226 @@ +' Gambas class file + +Export +Inherits ReportFrame + +Public Const _Properties As String = "*,Text,Format,Alignment{Align.*},Rotate{Angle:Degrees},UseField" '"*,Text,Key,Format,Alignment{Align.*},UseField" +Public Const _Similar As String = "ReportTextLabel" +Public Const _DefaultEvent As String = "Data" +Private $sText As String +Private $iAlignment As Integer = Align.Normal +Private $bIsSpecial As Boolean + +Private $sKey As String +Private $sFormat As String +Private $bUseField As Boolean +Private $fAngle As Float + +Property Text As String +Property Alignment As Integer +Property IsSpecial As Boolean +'Property Key As String +Property {Format} As String +Property UseField As Boolean +Property Rotate As Float + +Public Data As String +Event Data(Index As Integer) + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + 'Me._Count = 2 + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hMyHints As New TSizeHint + Dim fTextHeight As Float + Dim fTextWidth As Float + Dim hext As PaintExtents + Dim hRect As RectF + Dim sText As String + + 'On Récupère la taille standard + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + + 'if the size choosed by the user is less than the font height, set the object to the font height + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + If Not IsNull(Me.Font) Then Paint.Font = Me.Font + If Not $sText Then + Raise Data(DataIndex) + sText = Data + + Else + sText = $sText + Endif + If $fAngle = 0.0 Then + + hRect = Paint.TextSize(sText) + fTextWidth = Me.Padding._Left + Me.Border._Left + ReportUnits._PixelsToReportUnits(hRect.Width) + Me.Padding._Right + Me.Border._Right + fTextHeight = ReportUnits._PixelsToReportUnits(hRect.Height) + Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom + 'Debug Paint.Font.Height * Paint.ResolutionX / Desktop.Resolution + + Else + Paint.Save + Paint.Rotate(Rad($fAngle)) + Paint.Text(sText, 0, 0) + hExt = Paint.PathExtents + fTextWidth = Me.Padding._Left + Me.Border._Left + ReportUnits._PixelsToReportUnits(hExt.Width) + Me.Padding._Right + Me.Border._Right + fTextHeight = ReportUnits._PixelsToReportUnits(hExt.Height) + Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom + Paint.Restore + Endif + hMyHints.Height = Max(hMyHints.Height, fTextHeight) + hMyHints.Width = Max(hMyHints.Width, fTextWidth) + Endif + 'Me._SizeInt = hMyHints + Return hMyHints + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim sTempText As Variant + Dim iX, iY, iW, iH As Integer + + + If $sText Then + sTempText = $sText + Else + Raise Data(DataIndex) + sTempText = Data + Endif + + If $sFormat Then + Try sTempText = Format(sTempText, $sFormat) + Endif + If $bUseField Then + sTempText = DecodeText(sTempText, Page) + Endif + + + iX = (x + hControl.RealLeft + ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Border._Left)) + iY = (y + hControl.RealTop + ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Border._Top)) + iW = (hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + + iH = (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + + Paint.Brush = Me._GetActualBrush(iX, iY, iX + hControl.RealWidth, iY + hControl.RealHeight) + + If Report.Debug Then + + Paint.Brush = Paint.Color(Color.Red) + Paint.Rectangle(ix, iy, iw, ih) + Paint.Stroke + + Endif + + 'Set the Font if it is initialized + If Not IsNull(Me.Font) Then + Paint.Font = Me.Font + + Endif + + + If $fAngle = 0.0 Then + + Paint.Text(sTempText, iX, iY, iW, iH, $iAlignment) + Else + Paint.Translate(iX + iW / 2, iY + iH / 2) + Paint.Rotate(Rad($fAngle)) + Paint.Translate(- (iX + iW / 2), - (iY + iH / 2)) + Paint.Text(sTempText, iX, iY, iW, iH, Align.Center) + + Endif + Paint.Fill + + 'Paint.ResetClip + 'Paint.Restore + 'Paint.Restore + 'If $sKey Then $sText = Null + 'Draw.Clip.Enabled = False + +End + +Private Function IsSpecial_Read() As Boolean + + Return $bIsSpecial + +End + +Private Sub IsSpecial_Write(Value As Boolean) + + $bIsSpecial = Value + +End + + +Private Function Format_Read() As String + + Return $sFormat + +End + +Private Sub Format_Write(Value As String) + + $sFormat = Value + +End + +Private Function DecodeText(sText As String, Optional Page As Integer, Optional ForSize As Boolean = False) As String + + If ForSize Then + If InStr(sText, "$PAGE") Then sText = Replace(sText, "$PAGE", "999") + If InStr(sText, "$NPAGE") Then sText = Replace(sText, "$NPAGE", "999") + Else + If InStr(sText, "$PAGE") Then sText = Replace(sText, "$PAGE", Str(Page + 1)) + If InStr(sText, "$NPAGE") Then sText = Replace(sText, "$NPAGE", Str(Me.Report.PageCount)) + Endif + + Return sText + +End + +Private Function UseField_Read() As Boolean + + Return $bUseField + +End + +Private Sub UseField_Write(Value As Boolean) + + $bUseField = Value + +End + +Private Function Rotate_Read() As Float + + Return $fAngle + +End + +Private Sub Rotate_Write(Value As Float) + + $fAngle = Value + +End diff --git a/comp/src/gb.report/.src/ReportLine.class b/comp/src/gb.report/.src/ReportLine.class new file mode 100644 index 00000000..de2723e1 --- /dev/null +++ b/comp/src/gb.report/.src/ReportLine.class @@ -0,0 +1,127 @@ +' Gambas class file + +Export +Inherits ReportControl + +Public Const _Properties As String = "*,Direction{Align.TopLeft;Top;TopRight;Left;Right;BottomLeft;Bottom;BottomRight}=BottomRight,LineWidth{ReportCoord}=2 px" +Public Const _Similar As String = "ReportTextLabel" +Property Direction As Integer + +Private $fLineWidth As Float = 1.0 + +Private $iLineStyle As Integer = Line.solid +Private $iDirection As Integer = Align.BottomRight +Private $sLineWidth As String = "2px" +Property LineWidth As String +Property LineStyle As Integer + +Public Sub _new() + + Me.Height = "1cm" + Me.Width = "1cm" + +End + +Private Function LineWidth_Read() As String + + Return $sLineWidth + +End + +Private Sub LineWidth_Write(Value As String) + + $sLineWidth = Value + +End + +Public Sub _SetUnifiedValues() + + Dim hSizeParse As TSizeParse + + Super._SetUnifiedValues() + + hSizeParse = New TSizeParse($sLineWidth) + $fLineWidth = hSizeParse.GetValue() + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hMyHints As New TSizeHint + + hMyHints.Height = Me._Height + hMyHints.Width = Me._Width + Return hMyHints + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim iX, iY, W, H, W2, H2 As Integer + + If Me.LineStyle = Line.None Then Return + If Me.LineStyle <> Line.Solid Then Paint.Dash = ReportUnits.GetBorder(Me.LineStyle) + Paint.LineWidth = ReportUnits._ReportUnitsToPixels($fLineWidth) + + iX = (x + hControl.RealLeft + ReportUnits._ReportUnitsToPixels(Me.Padding._Left)) + iY = (y + hControl.RealTop + ReportUnits._ReportUnitsToPixels(Me.Padding._Top)) + + W = iX + hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Right) + H = iY + hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Bottom) + Paint.Brush = Me._GetActualBrush(iX, iY, W, H) + W2 = (iX + W) / 2 + H2 = (iY + H) / 2 + Select Case $iDirection + Case Align.TopLeft + Paint.MoveTo(W, H) + Paint.LineTo(iX, iY) + Case Align.Top + Paint.MoveTo(W2, H) + Paint.LineTo(W2, iY) + Case Align.TopRight + Paint.MoveTo(iX, H) + Paint.LineTo(W, iY) + Case Align.Left + Paint.MoveTo(W, H2) + Paint.LineTo(iX, H2) + Case Align.Right + Paint.MoveTo(iX, H2) + Paint.LineTo(W, H2) + Case Align.BottomLeft + Paint.MoveTo(W, iY) + Paint.LineTo(iX, H) + Case Align.Bottom + Paint.MoveTo(W2, iY) + Paint.LineTo(W2, H) + Case Align.BottomRight + Paint.MoveTo(iX, iY) + Paint.LineTo(W, H) + End Select + + Paint.Stroke + +End + +Private Function LineStyle_Read() As Integer + + Return $iLineStyle + +End + +Private Sub LineStyle_Write(Value As Integer) + + $iLineStyle = Value + +End + +Private Function Direction_Read() As Integer + + Return $iDirection + +End + +Private Sub Direction_Write(Value As Integer) + + $iDirection = Value + +End diff --git a/comp/src/gb.report/.src/ReportPageBreak.class b/comp/src/gb.report/.src/ReportPageBreak.class new file mode 100644 index 00000000..ea95ef98 --- /dev/null +++ b/comp/src/gb.report/.src/ReportPageBreak.class @@ -0,0 +1,6 @@ +' Gambas class file + +Export +Inherits ReportControl + +Public Const _Properties As String = "-*" diff --git a/comp/src/gb.report/.src/ReportPanel.class b/comp/src/gb.report/.src/ReportPanel.class new file mode 100644 index 00000000..d05ebd32 --- /dev/null +++ b/comp/src/gb.report/.src/ReportPanel.class @@ -0,0 +1,25 @@ +' Gambas class file + +Export +Inherits ReportContainer +Public Const _Properties As String = "*,Arrangement{Arrange.None;Vertical;Horizontal;Column;Fill}=Vertical" +Public Const _Similar As String = "ReportVBox" +Property Arrangement As Integer + +Public Sub _new() + + Super._Arrangement = Arrange.Vertical + +End + +Private Function Arrangement_Read() As Integer + + Return Super._Arrangement + +End + +Private Sub Arrangement_Write(Value As Integer) + + Super._Arrangement = Value + +End diff --git a/comp/src/gb.report/.src/ReportSection.class b/comp/src/gb.report/.src/ReportSection.class new file mode 100644 index 00000000..94224b5b --- /dev/null +++ b/comp/src/gb.report/.src/ReportSection.class @@ -0,0 +1,23 @@ +' Gambas class file + +Export +Inherits ReportVBox +Public Const _Properties As String = "*,Text" +Property Text As String +Private $sText As String + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End + +Public Sub _Free() + +End diff --git a/comp/src/gb.report/.src/ReportSvgImage.class b/comp/src/gb.report/.src/ReportSvgImage.class new file mode 100644 index 00000000..9927574d --- /dev/null +++ b/comp/src/gb.report/.src/ReportSvgImage.class @@ -0,0 +1,185 @@ +' Gambas class file + +Export +Inherits ReportFrame + +Private $hPic As SvgImage +Private $iStretchMode As Integer = Report.Proportional +Private $iAlignment As Integer = Align.Normal +'Private $sPath As String +Public Const _Properties As String = "*,Stretch{Report.None;Proportional;Fill}=Proportional,Alignment{Align.*},Image{SvgImage}" +Public Const _Similar As String = "ReportTextLabel" +Public Data As SvgImage +Property Image As SvgImage +Property Stretch As Integer +Property Alignment As Integer +Event Data(Index As Integer) + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hMyHints As New TSizeHint + 'Dim h As Float = IIf(Me._Height > AvailableH, AvailableH, Me._Height) + 'Dim w As Float = IIf(Me._Width > AvailableW, AvailableW, Me._Width) + + Dim hPic As SvgImage + + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalWidth, DataIndex) + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + If $hpic Then + hpic = $hpic + Else + Raise Data(DataIndex) + hpic = Data + Endif + + If hpic Then + hMyHints.Width = Max(hMyHints.Width, Me.Padding._Left + ReportUnits.UnitToCm(hpic.Width, "px") + Me.Padding._Right) + hMyHints.Height = Max(hMyHints.Width, Me.Padding._Top + ReportUnits.UnitToCm(hpic.Height, "px") + Me.Padding._Bottom) + Endif + + Endif + + Return hMyHints + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim ix, iy As Float + 'Dim hBrush As PaintBrush + Dim hPic As SvgImage + + Dim w, h As Float + + + ix = x + hControl.RealLeft '+ ReportUnits._ReportUnitsToPixels(Me.Padding._Left) + iy = y + hControl.RealTop '+ ReportUnits._ReportUnitsToPixels(Me.Padding._Top) + + If Not $hpic Then + Raise Data(DataIndex) + hpic = Data + If Not hPic Then Return + Else + hPic = $hpic + Endif + + '$hPic = $hPic.Stretch(hControl.RealWidth, hControl.RealHeight) + + 'hBrush = Paint.Image(hpic) + + If $iStretchMode = Report.Fill Then + iX += ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Border._Left) + iY += ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Border._Top) + w = (hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + h = (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + ' hBrush.Translate(ix, iy - 1) + 'hBrush.Scale(w / hPic.Width, h / hPic.Height) + 'Paint.Brush = hBrush + hpic.Width = w + hpic.Height = h + Paint.MoveTo(ix, iy) + hpic.Paint + 'Paint.Rectangle(ix, iy, w, h) + Else + + If $iStretchMode = Report.Proportional Then + 'on détermine la partie prédominante + If hPic.Width >= hPic.Height + 'C'est la largeur + 'on détermine une hauteur en fonction de la largeur connue + w = hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Width) + h = hPic.Height / hPic.Width * w + 'si h> a la place disponible alors on adapte en fonction de h en faite + If h > (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Height)) Then + h = (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Height)) + w = hPic.Width / hPic.Height * h + Endif + + Else + 'C'est la hauteur + h = (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Height)) + w = hPic.Width / hPic.Height * h + 'si w> la place disponible alors on adapte en fonction de w en faite + If w > (hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Width)) Then + w = hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Width) + h = hPic.Height / hPic.Width * w + Endif + Endif + + Else + w = hPic.Width + h = hPic.Height + + Endif + + Select Case $iAlignment + Case Align.Normal, Align.TopLeft, Align.Left, Align.BottomLeft + 'Gauche + ix += ReportUnits._ReportUnitsToPixels(Me.Padding._Left) + Case Align.Bottom, Align.Center, Align.Top + 'centrée + ix += (hControl.RealWidth - w) / 2 + + Case Align.TopRight, Align.Right, Align.BottomRight + 'Droite + ix += hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Right) - w + End Select + + Select Case $iAlignment + Case Align.TopLeft, Align.Top, Align.TopRight + 'Haut + iy += ReportUnits._ReportUnitsToPixels(Me.Padding._Top) + Case Align.Left, Align.Center, Align.Right + 'Milieu + iy += (hControl.RealHeight - h) / 2 + Case Align.BottomLeft, Align.Bottom, Align.BottomRight + iY += hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Bottom) - h + End Select + 'hBrush.Translate(ix, iy - 1) + hpic.Width = W + hPic.Height = H + Paint.MoveTo(iX, iY) + hpic.Paint + 'hBrush.Scale(w / hPic.Width, h / hPic.Height) + 'Paint.Brush = hBrush + 'Paint.Rectangle(ix, iy, w, h - 1) + Endif + +End + +Private Function Image_Read() As SvgImage + + Return $hPic + +End + +Private Sub Image_Write(Value As SvgImage) + + $hPic = Value + +End + +Private Function Stretch_Read() As Integer + + Return $iStretchMode + +End + +Private Sub Stretch_Write(Value As Integer) + + $iStretchMode = Value + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End diff --git a/comp/src/gb.report/.src/ReportTextLabel.class b/comp/src/gb.report/.src/ReportTextLabel.class new file mode 100644 index 00000000..f5e11372 --- /dev/null +++ b/comp/src/gb.report/.src/ReportTextLabel.class @@ -0,0 +1,97 @@ +' Gambas class file + +Export +Inherits ReportFrame + +Public Const _Properties As String = "*,Text,Alignment{Align.*}" +Public Const _Similar As String = "ReportControl" +Public Const _DefaultEvent As String = "Data" +Property Text As String +Property Alignment As Integer + +Private $sText As String +Private $iAlignment As Integer = Align.TopNormal + + +Public Data As String +Event Data(Index As Integer) + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint + + Dim hMyHints As New TSizeHint + Dim fTextHeight As Float + Dim sTmp As String + + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalWidth, DataIndex) + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + If Not $sText Then + Raise Data(DataIndex) + sTmp = Data + Else + sTmp = $sText + Endif + 'La largeur est fixée par l'utilisateur + 'Calcule de la hauteur + If Not IsNull(Me.Font) Then Paint.Font = Me.Font + fTextHeight = ReportUnits._PixelsToReportUnits(Paint.RichTextExtents(sTmp, ReportUnits._ReportUnitsToPixels(hMyHints.Width)).Height) + hMyHints.Height = Max(hMyHints.Height, Me.Border._Top + Me.Padding._Top + fTextHeight + Me.Padding._Bottom + Me.Border._Bottom) + Endif + + Return hMyHints + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim iX, iY, iW, iH As Integer + Dim sTmp As String + + Paint.Brush = Me._GetActualBrush(iX, iY, iX + hControl.RealWidth, iY + hControl.RealHeight) + + If Not $sText Then + Raise Data(DataIndex) + sTmp = Data + Else + sTmp = $sText + Endif + + 'Set the Font if it is initialized + If Not IsNull(Me.Font) Then Paint.Font = Me.Font + + iX = (x + hControl.RealLeft + ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Border._Left)) + iY = (y + hControl.RealTop + ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Border._Top)) + iW = (hControl.RealWidth - ReportUnits._ReportUnitsToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + iH = (hControl.RealHeight - ReportUnits._ReportUnitsToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + + 'Paint.Rectangle(iX, iY, iW, iH) + 'Paint.Clip + Paint.RichText(sTmp, iX, iY, iW, iH, $iAlignment) + Paint.Fill + 'Paint.ResetClip + +End diff --git a/comp/src/gb.report/.src/ReportVBox.class b/comp/src/gb.report/.src/ReportVBox.class new file mode 100644 index 00000000..1cb254c3 --- /dev/null +++ b/comp/src/gb.report/.src/ReportVBox.class @@ -0,0 +1,33 @@ +' Gambas class file + +Export +Inherits ReportContainer +Public Const _Properties As String = "*,ForceNewPage" +Public Const _Similar As String = "ReportVBox" +Public Const _DefaultArrangement As String = "V" + +Public Sub _new() + + Super._Arrangement = Arrange.Vertical + +End + +Public Sub _Free() + +End + +' Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As TSizeHint +' +' 'Print Me.Tag +' If Me.tag Then Print "Besoins de " & Me.Tag & ": ", AvailableW, AvailableH +' Return Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight) +' +' +' End +' +' +' Public Sub _SetChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, bInFixed As Boolean) +' If Me.Tag Then Print "Disponibilité pour " & Me.Tag & ": H= " & H & " W= " & W +' Super._SetChildGeometry(x, y, w, h, ContPage, bInFixed) +' +' End diff --git a/comp/src/gb.report/.src/ReportVPanel.class b/comp/src/gb.report/.src/ReportVPanel.class new file mode 100644 index 00000000..fe439b23 --- /dev/null +++ b/comp/src/gb.report/.src/ReportVPanel.class @@ -0,0 +1,336 @@ +' Gambas class file + +Export +Inherits ReportContainer +Public Const _Properties As String = "*" +Public Const _Similar As String = "ReportVBox" + +Public Sub _new() + + Super._Arrangement = Arrange.Column + +End + +Public Sub _Free() + + Super._Free + +End + +Public Sub _SetCChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + + Dim aPageColumns As New TPageColumn[] + Dim PageColumn As New TPageColumn + Dim fMaxWidth As Float + Dim fColPos As Float + + Dim aPageItems As New TControl[] ''Éléments contenu par cette page + Dim hChildHints As TSizeHint ''Besoins en hauteur/largeur de l'enfant + Dim hChild As ReportControl ''Un enfant reportcontrol + Dim fTH As Float ''Hauteur restante + Dim fSpc As Float ''Taille d'un espace + 'Dim X, Y As Float ''Position Haut gauche de départ + Dim hTItem As TControl ''Un objet virtuel + Dim oChild As Object ''Un objet gambas générique + Dim fExp As Float ''taille des objets étendus + Dim iNExp As Integer ''Nombre d'objets étendus + Dim ftmpHeight As Float ''Tampon pour le calcul de la taille répartie + Dim ftmpY As Float ''Curseur temporaire de position haute + Dim fX, fY As Float ''Tampon de position + Dim i, j As Integer ''Des indexs + Dim bExitLoop As Boolean ''Flag de sortie de traitement + 'Dim iPrevIndex As Integer + Dim bForceNewPage As Boolean ''Flag d'anticipation de sortie 1 par page + Dim iPreIndex As Integer + 'Détermination de la taille d'un espace + 'et prise en compte de la taille relative + fSpc = IIf(Me._RelativeSpacing, H * Me._Spacing / 100, Me._Spacing) + + 'Retrait du padding et des bordures de la hauteur de travail + H = H - Me.Padding._Top - Me.Border._Top - Me.Border._Bottom - Me.Padding._Bottom + + 'Retrait du padding et des bordures a la largeur de travail + W = W - Me.Padding._Left - Me.Border._Left - Me.Border._Right - Me.Padding._Right + + 'Positioinnement du curseur en haut a gauche + X = Me.Padding._Left + Me.Border._Left + Y = Me.Padding._Top + Me.Border._Top + + 'La hauteur restante est initialisée avec la hauteur de travail + fTH = H + fColPos = X + 'aColX.Add(fNextColPos) + '***************************************************************************************** + 'On enumere tous les enfants a la recherche des éléments fixes + 'Si des éléments fixes sont trouvés alors leur taille est déduite de la taille restante. + For i = 0 To Me.Children.Max + 'On récupère l'enfant + hchild = Me.Children[i] + 'Si l'enfant est fixe alors on le traite. + If hchild.Fixed Then + ' hchild._Index = TCont.Index + 'on commence par récupérer la taille de l'objet + hChildHints = hchild._GetSizeHints(W, fTH, W, H, TCont.Index) + 'On retire la taille de l'objet a la hau'teur restante + 'si l'objet n'est pas ignoré par l'agencement + If Not hchild.Ignore Then fTH = fTH - hChildHints.Height - fSpc + + + 'Si l'énumération n'est pas encore arrivée a l'index sauvegardé + 'alors on ajoute directement l'élément a la page + If i < Me._CurItem Then + 'Génération de l'objet virtuel + hTItem = New TControl + 'Lier l'objet + hTItem.Ctrl = hchild + 'Associer sa taille + hTItem.SizeHint = hChildHints + + hTItem.Index = TCont.Index + 'L'ajouter a la page + PageColumn.TCtrls.Add(hTItem) + + 'Si l'objet est étendu alors on en tient compte + 'sauf si celui-ci est ignoré par l'arrangement + If hchild.Expand And If Not hchild.Ignore Then + fExp += hChildHints.Height + Inc iNExp + Endif + Endif + Endif + Next + + + + + '********************************************************************************* + 'On parcour a présent le reste des éléments + For i = Me._CurItem To Me.Children.Max + 'On récupère l'enfant + hChild = Me.Children[i] + 'Je traite ici la boucle de clonage + 'On définit le point de départ + 'If ContPage = 1 And hchild.Tag = "**" Then Stop + j = hChild._DataIndex + Do + '## Début boucle répétition + + 'On fixe l'index de données des enfants si + 'on est le duplicateur + If hChild._IsContainer And If hChild._count > 1 Then + iPreIndex = j + Else + iPreIndex = TCont.Index + Endif + 'If hchild.tag = "**" Then Print iPreIndex + 'If iPreIndex = 9 Then Stop + 'on récupère la taille de l'enfant + + hChildHints = hchild._GetSizeHints(W, fTH, fMaxWidth, H, iPreIndex) + fMaxWidth = Min(Max(fMaxWidth, hChildHints.Width), W) + 'Les éléments fixes ont déja été traités on ne tient donc pas compte de leur hauteur + 'car elle a déja été déduite de l'espace restant. De meme on ignore les objets flottants (ignore=true) + If Not hchild.Fixed And If Not hchild.Ignore Then + + 'Si l'élément ne loge pas dans la place restante ou + 'si la place restante est insuffisante + 'on provoque la sortie en fin de boucle + If (fTH - hChildHints.Height) < 0 Or If fTH <= 0 Or bForceNewPage Then + ' If hchild.Tag = "*" Then Stop + + + aPageColumns.Add(PageColumn) + PageColumn.Exp = fExp + PageColumn.NExp = iNExp + PageColumn.X = fColPos + fColPos += fMaxWidth + fspc + PageColumn.TH = fTh + + iNExp = 0 + fExp = 0 + + fTH = H + PageColumn = New TPageColumn + + 'Si la nouvelle colonne ne loge pas on arrête la + If fColPos + fMaxWidth > x + W Then + bForceNewPage = False + hchild._DataIndex = j + + 'If Not bForceNewPage Then + bExitLoop = True + Break + Else + Continue + End If + Endif + + 'On déduit la taille de l'objet courant (et l'espace suivant) + fTH = fTH - hChildHints.Height - fspc + + Endif + 'L'objet loge dans la page, on génère donc une représentation de celui-ci + 'c'est a dire un objet virtuel pointant vers celui-ci + 'et apportant les informations nécéssaire au layout final + 'on génère l'objet virtuel + + hTItem = New TControl + 'On associe le controle + hTItem.Ctrl = hchild + 'On associe sa taille + hTItem.SizeHint = hChildHints + 'If hchild.Tag = "head" Then Stop + 'On associe l'index de donnée + hTItem.Index = iPreIndex + 'TItem.Index = IIf(hchild._count > 0, j, TCont.Index) + 'On l'ajoute a la page + PageColumn.TCtrls.Add(hTItem) + + 'Si l'objet est étendu alors on tien compte de sa taille + 'pour le calcul de l'espace réparti + If hchild.Expand Then + fExp += hChildHints.Height + Inc iNExp + Endif + + 'If hChildHints.NotFinished Then hchild._DataIndex = j + 'un élément fixe ou ignore ne peut être répété + If hchild.Fixed Or hchild.Ignore Then Break + + 'Sachant que hChild._count peut être à -1 on le considère dans ce cas la comme étant a 0 + 'si j est égale au compte alors on quitte la boucle + If j >= Max(hchild._Count - 1, 0) Then + If Not hChildHints.NotFinished Then hChild._DataIndex = 0 + Break + Endif + + 'sinon on incrémente le compte a condition que le dernier enfant aie finit d'afficher ses enfants + If Not hChildHints.NotFinished Then Inc j + + 'On prévoie une sortie de boucle si l'enfant demande un affichage 1 par page + If hchild.ForceNewPage Then bForceNewPage = True + + Loop + 'Next '## fin de la boucle de répétition + 'Si la sortie anticipé est demandée alors on sort de la boucle + If bExitLoop Then Break + If i < Me.Children.Max Then + If Me.Children[i + 1] Is ReportPageBreak Then + bForceNewPage = True + Inc Me._CurItem + Inc i + Endif + Endif + 'Tout les objets on été répété alors on remet l'index à 0 + 'hchild._DataIndex = 0 + 'On incrémente le compteur des éléments traités + Inc Me._CurItem + Next + + If (H - fTH) > 0 Then + fTH += fspc 'Heu?????? + Endif + + '*************************************************************************** + 'Pour tous les éléments fixes jusqu'à la fin du document + For i = Max(Me._CurItem, 0) To Me.Children.Max + hchild = Me.Children[i] + If hchild.Fixed Then + hChildHints = hchild._GetSizeHints(W, fTH, W, H, iPreIndex) + 'fNextColPos = Max(fNextColPos, fNextColPos + hChildHints.Width) + 'on génère l'objet virtuel + hTItem = New TControl + 'On associe le controle + hTItem.Ctrl = hchild + 'On associe sa taille + hTItem.SizeHint = hChildHints + 'On l'ajoute a la page + PageColumn.TCtrl.Add(hTItem) + + 'Si l'objet est étendu alors on tien compte de sa taille + 'pour le calcul de l'espace réparti + If hchild.Expand Then + fExp += hChildHints.Height + Inc iNExp + Endif + Endif + Next + PageColumn.Exp = fExp + PageColumn.Nexp = iNExp + PageColumn.TH = fTH + aPageColumns.Add(PageColumn) + PageColumn.X = fColPos + '****************************************************************************** + 'A présent tous les éléments pouvant être placé sur la page ont été marqué. + 'On peut donc procéder a la mise en forme de ceux-ci + 'k = 0 + For Each PageColumn In aPageColumns + ftmpY = Y 'On définit la position de départ + If PageColumn.NExp Then + fExp = (PageColumn.Exp + PageColumn.TH) / PageColumn.NExp + Endif + + For i = 0 To PageColumn.TCtrls.Max + 'on récupère l'objet virtuel + hTItem = PageColumn.TCtrls[i] + 'on récupère l'instance de l'objet réel + ochild = hTItem.Ctrl + + 'Si l'objet est étendu alors on calcul sa hauteur + 'Un objet flottant ne peut pas être étendu + If Not oChild.Ignore And If ochild.Expand Then + 'Calcul de la taille répartie + ftmpHeight = fExp + Else + 'Si l'objet n'est pas étendu alors sa taille est celle demandée par celui-ci + ftmpHeight = hTItem.SizeHint.Height + Endif + 'oChild._Index = hTItem.Index + 'Traitement des objets ignoré + 'Les propriété de taille sont celle fournie par l'objet + If oChild.Ignore Then + 'Calcul de la position de l'objet + 'si sa position est relative (%) alors on fait le ratio a partir de la largeur ou de la hauteur + 'sinon on utilise la position fournie par l'objet + fX = X + IIf(oChild._RelativeLeft, W * oChild._Left / 100, oChild._Left) + fY = Y + IIf(oChild._RelativeTop, H * oChild._Top / 100, oChild._Top) + 'Fixer la position de l'objet + hTItem._SetGeometry(fX, fY, hTItem.SizeHint.Width, ftmpHeight) + 'L'objet flottant peut être un conteneur il faut donc demander aussi a ses enfants de s 'organiser + oChild._SetChildGeometry(fX, fY, hTItem.SizeHint.Width, ftmpHeight, ContPage, hTItem, bInFixed Or Me.Fixed) + + Else + + 'Fixer la position de l'objet + hTItem._SetGeometry(PageColumn.X, fTmpY, fMaxWidth, ftmpHeight) + 'L'objet flottant peut être un conteneur il faut donc demander aussi a ses enfants de s 'organiser + oChild._SetChildGeometry(PageColumn.X, fTmpY, fMaxWidth, ftmpHeight, ContPage, hTItem, bInFixed Or Me.Fixed) + 'on incrémente la position verticale de l'objet + 'pour définir sa place sur la page relativement aux autres éléments + fTmpY += ftmpHeight + fspc + + Endif + + 'Si l'objet est un conteneur alors il faut vérifier si il a finit d'être traité + + If oChild Is ReportContainer Then + 'If oChild._NotFinished Then Me.NotFinished = True + 'Si son traitement n'est pas terminé alors on trouve + 'sa position dans la liste des enfant et on l'assigne a l'index de traitement + 'si sa position est > a l'index courant + If ochild._CurItem <= oChild.Children.Max Then + j = Me.Children.Find(oChild) + Me._CurItem = Min(Me._CurItem, j) + Endif + 'Si on est dans la lignée d'un conteneur fixe on incrémente pas la lecture des enfants + 'En clair ce sont toujours les mêmes éléments qui apparaitrons + If bInFixed Then ochild._CurItem = 0 + Endif + Next + 'Inc k + aPageItems.Insert(PageColumn.TCtrls) + Next + 'On ajoute cette page au dossier du conteneur + TCont._PageChildren[ContPage] = aPageItems + +End \ No newline at end of file diff --git a/comp/src/gb.report/.src/Tests/Old/Paints.class b/comp/src/gb.report/.src/Tests/Old/Paints.class new file mode 100644 index 00000000..01960f38 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Paints.class @@ -0,0 +1,33 @@ +' Gambas class file + +'Export +' +' Static Public Sub Begin(Device As Object) +' +' Debug +' Super.Begin(Device) +' +' End +' +' Static Public Sub End(Device As Object) +' +' Debug +' Super.End(Device) +' +' End +' +' ' +' Static Public Sub Rectangle(X As Float, Y As Float, W As Float, H As Float) +' +' Debug CStr(X); ", "; CStr(Y); ", "; CStr(W); ", "; CStr(H) +' Super.Rectangle(X, Y, W, H) +' +' End +' +' +' Static Public Sub Text(Text As String, Optional X As Float, Y As Float, Width As Float, Height As Float, Alignment As Integer) +' +' 'Debug Paint.Font.Size +' Super.Text(Text, X, Y, Width, Height, Alignment) +' +' End diff --git a/comp/src/gb.report/.src/Tests/Old/Report1.class b/comp/src/gb.report/.src/Tests/Old/Report1.class new file mode 100644 index 00000000..5be74ff1 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report1.class @@ -0,0 +1,8 @@ +' Gambas class file + + +Public Sub ReportLabel2_Data(Index As Integer) + + Last.data = "coucou\n c'est
    moi" + +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report1.report b/comp/src/gb.report/.src/Tests/Old/Report1.report new file mode 100644 index 00000000..2051d2ae --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report1.report @@ -0,0 +1,55 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,68,64) + Count = 3 + Index = 0 + Text = ("#3") + { ReportLabel2 ReportLabel + #MoveScaled(3,4,33,8) + AutoResize = True + Background = ReportBrush["#FFFF00"] + } + Index = 1 + Text = ("#1") + { ReportHBox1 ReportHBox + #MoveScaled(3,1,58,16) + Height = "5cm" + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["#00FF00"] + { ReportLabel1 ReportLabel + #MoveScaled(7,4,40,9) + Font = Font["Bold,+10"] + Expand = True + Text = ("COUCOU") + Alignment = Align.Center + } + } + { ReportHBox2 ReportHBox + #MoveScaled(6,21,57,34) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["LinearGradient(0.84,0.6,0,0,[#000000,#FFFFFF],[0,1])"] + { ReportVBox1 ReportVBox + #MoveScaled(0,4,28,20) + Expand = True + } + { ReportVBox2 ReportVBox + #MoveScaled(33,3,18,24) + Fixed = True + Expand = True + { ReportHBox3 ReportHBox + #MoveScaled(3,2,12,14) + Expand = True + } + { ReportVBox3 ReportVBox + #MoveScaled(2,20,16,3) + Height = "60mm" + Background = ReportBrush["#FFFF00"] + } + } + } + Index = 2 + Text = ("#2") + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report10.class b/comp/src/gb.report/.src/Tests/Old/Report10.class new file mode 100644 index 00000000..9673987f --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report10.class @@ -0,0 +1,8 @@ +' Gambas class file + +Public Sub _new() + + ReportHBox2.DataCount = 200 + + +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report10.report b/comp/src/gb.report/.src/Tests/Old/Report10.report new file mode 100644 index 00000000..f5e3d89b --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report10.report @@ -0,0 +1,64 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Padding = ReportPadding["Top:20mm;Bottom:10mm;Left:25mm;Right:15mm"] + Paper = Printer.A3 + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(1,4,59,5) + Height = "1cm" + Fixed = True + { ReportLabel1 ReportLabel + #MoveScaled(1,1,17,4) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000;TopLeftCorner:6mm"] + Text = ("ReportLabel1") + } + { ReportLabel2 ReportLabel + #MoveScaled(21,1,14,4) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000"] + Text = ("ReportLabel2") + } + { ReportLabel3 ReportLabel + #MoveScaled(40,0,13,5) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000;TopRightCorner:6mm"] + Text = ("ReportLabel3") + } + } + { ReportHBox2 ReportHBox + #MoveScaled(1,12,59,5) + Height = "1cm" + { ReportLabel4 ReportLabel + #MoveScaled(1,1,17,4) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Text = ("ReportLabel1") + } + { ReportLabel5 ReportLabel + #MoveScaled(21,1,14,4) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000"] + Text = ("ReportLabel2") + } + { ReportLabel6 ReportLabel + #MoveScaled(40,0,13,5) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Text = ("ReportLabel3") + } + } + { ReportLabel7 ReportLabel + #MoveScaled(3,49,60,4) + Fixed = True + Padding = ReportPadding["Top:10mm;Bottom:10mm;Left:10mm;Right:10mm"] + AutoResize = True + Text = ("Page $NPAGE") + Alignment = Align.Right + UseField = True + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report12.class b/comp/src/gb.report/.src/Tests/Old/Report12.class new file mode 100644 index 00000000..f816f33e --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report12.class @@ -0,0 +1,16 @@ +' Gambas class file + +Public Sub _New() + + ReportHBox1.DataCount = 100 + ReportLabel1.BackGround = ReportBrush.Color(Color.Red) + ReportLabel1.Brush = ReportBrush.Color(Color.Yellow) + +End + + +Public Sub ReportLabel1_Data(Index As Integer) + + Last.Data = Index + +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report12.report b/comp/src/gb.report/.src/Tests/Old/Report12.report new file mode 100644 index 00000000..5f402eed --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report12.report @@ -0,0 +1,17 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Spacing = "1mm" + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(2,3,60,7) + Height = "1cm" + { ReportLabel1 ReportLabel + #MoveScaled(0,0,58,4) + Expand = True + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report13.class b/comp/src/gb.report/.src/Tests/Old/Report13.class new file mode 100644 index 00000000..d2ce3331 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report13.class @@ -0,0 +1,38 @@ +' Gambas class file + +Private hResut As Result +Private hResut2 As Result +Private ColResult As New Collection +Public Sub ReportLabel1_Data(Index As Integer) + + hResut.MoveTo(Index) + Last.Data = hResut!firstname + + +End + +Public Sub Report_Open() +Connections["Connection1"].Open +hResut = db.Exec("select distinct firstname from test") + + ReportVBox1.DataCount = hResut.Count + 'ReportVBox2.DataCount = 10 +End + +Public Sub ReportLabel2_Data(Index As Integer) +hResut2 = ColResult[ReportVBox1.DataIndex] +hResut2.MoveTo(Index) +Last.Data = hResut2!birth +Print index +Catch +End + +Public Sub ReportVBox1_BeforeData() + If Not ColResult.Exist(Last.DataIndex) Then + hResut.MoveTo(Last.DataIndex) + hResut2 = db.Exec("select * FROM test where firstname=&1", hResut!firstname) + ColResult[Last.DataIndex] = hResut2 + ReportVBox2.DataCount = hResut2.Count + Endif +Catch +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report13.report b/comp/src/gb.report/.src/Tests/Old/Report13.report new file mode 100644 index 00000000..4a117ceb --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report13.report @@ -0,0 +1,21 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Index = 0 + Text = ("") + { ReportVBox1 ReportVBox + #MoveScaled(3,5,60,44) + { ReportLabel1 ReportLabel + #MoveScaled(2,1,31,3) + } + { ReportVBox2 ReportVBox + #MoveScaled(2,8,56,12) + { ReportLabel2 ReportLabel + #MoveScaled(0,2,28,3) + Brush = ReportBrush["#00FF00"] + } + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report14.class b/comp/src/gb.report/.src/Tests/Old/Report14.class new file mode 100644 index 00000000..13c1ce07 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report14.class @@ -0,0 +1,32 @@ +' Gambas class file + +'Static Public Type As String + +Public Sub _new(Optional sType As String = "mysql") + + Dim hResult As Result + Dim Rlbl As ReportLabel + Dim hConn As Connection + + hConn = Connections["Connection1"] + hConn.Type = sType + If hConn.Type = "sqlite" Then hConn.Host = User.Home + Try hConn.Open + If Not hConn.Opened Then + Message.Error(" You have to create the test database with the Database example.") + Return + Endif + + hResult = db.Limit(200).Find("test") + If Not hResult.Available Then Return + + For Each hResult + + Rlbl = New ReportLabel(RVBCont) + Rlbl.Autoresize = True + Rlbl.Text = hResult!name & " " & hResult!firstname + Rlbl.Border.bottom.Width = "1px" + + Next + +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report14.report b/comp/src/gb.report/.src/Tests/Old/Report14.report new file mode 100644 index 00000000..e83b2ebe --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report14.report @@ -0,0 +1,90 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,100,77) + Padding = ReportPadding["Top:1cm;Bottom:15mm;Left:2cm;Right:2cm"] + Index = 0 + Text = ("Section 1") + { ReportHBox2 ReportHBox + #MoveScaled(0,0,67,14) + Height = "5cm" + Fixed = True + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["#3398C3"] + { ReportSvgImage1 ReportSvgImage + #MoveScaled(1,1,14,12) + Width = "4cm" + Image = SvgImage.Load("gambas.svg") + } + { ReportLabel3 ReportLabel + #MoveScaled(20,2,43,10) + Brush = ReportBrush["LinearGradient(0.71,0.07,0.34,0.06,[#FFFF00,#FFFFFF],[0,1])"] + Fixed = True + Font = Font["DejaVu Sans,Bold,+14"] + Expand = True + AutoResize = True + Text = ("Gambas") + Alignment = Align.Left + } + } + { ReportHBox1 ReportHBox + #MoveScaled(0,15,87,56) + Padding = ReportPadding["0cm"] + Expand = True + Border = ReportBorder["Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + { ReportVBox1 ReportVBox + #MoveScaled(1,1,25,47) + Width = "6cm" + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Background = ReportBrush["#DF6B00"] + { ReportLabel4 ReportLabel + #MoveScaled(3,2,20,4) + Brush = ReportBrush["#FFFFFF"] + Fixed = True + Font = Font["Bold"] + AutoResize = True + Text = ("All friends list !") + } + } + { ReportVBox2 ReportVBox + #MoveScaled(27,1,60,54) + Expand = True + Border = ReportBorder["Left:1mm #000000"] + Background = ReportBrush["#FFFFFF"] + { RVBCont ReportVBox + #MoveScaled(4,4,53,42) + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Expand = True + Tag = "*" + Spacing = "1mm" + { ReportLabel1 ReportLabel + #MoveScaled(0,0,44,9) + Font = Font["Bitstream Charter,Bold,Italic,+2"] + AutoResize = True + Text = ("Gambas Report Demo") + Alignment = Align.Center + } + { ReportVBox3 ReportVBox + #MoveScaled(0,11,38,4) + Height = "1cm" + AutoResize = True + } + } + { ReportLabel2 ReportLabel + #MoveScaled(7,49,47,3) + Brush = ReportBrush["#FFFFFF"] + Fixed = True + Font = Font["Bold,+1"] + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + AutoResize = True + Border = ReportBorder["Top:1mm #000000"] + Background = ReportBrush["#3398C3"] + Text = ("Page $PAGE on $NPAGE ") + Alignment = Align.Right + UseField = True + } + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report2.class b/comp/src/gb.report/.src/Tests/Old/Report2.class new file mode 100644 index 00000000..ea9925d3 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report2.class @@ -0,0 +1,20 @@ +' Gambas class file + +Public Sub _new() + + Dim Rlbl As ReportLabel + Dim i As Integer + 'Report.Debug = True + For i = 0 To 200 + + Rlbl = New ReportLabel(RVBCont) + Rlbl.Text = "Ligne " & i + Rlbl.Autoresize = True + Rlbl.Border.bottom.Width = "1px" + Rlbl.Padding.Top = "3 mm" + Rlbl.Padding.Bottom = "1mm" + Rlbl.Padding.Left = "2 mm" + + Next + +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report2.report b/comp/src/gb.report/.src/Tests/Old/Report2.report new file mode 100644 index 00000000..0aad391a --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report2.report @@ -0,0 +1,19 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(1,1,61,8) + Height = "10cm" + Fixed = True + Expand = True + Background = ReportBrush["#007FFF"] + } + { RVBCont ReportVBox + #MoveScaled(2,11,56,26) + Expand = True + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report3.class b/comp/src/gb.report/.src/Tests/Old/Report3.class new file mode 100644 index 00000000..8ea03ec7 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report3.class @@ -0,0 +1,20 @@ +' Gambas class file + +Public Sub _new() + + ReportHBox1.DataCount = 300 + +End + +Public Sub ReportLabel1_Data(Index As Integer) + + 'Print "coucou" + Last.data = Index + +End + +Public Sub ReportLabel2_Data(Index As Integer) + + Last.data = Index + +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report3.report b/comp/src/gb.report/.src/Tests/Old/Report3.report new file mode 100644 index 00000000..59d12d97 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report3.report @@ -0,0 +1,42 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Paper = Printer.A3 + Index = 0 + Text = ("") + { ReportHBox2 ReportHBox + #MoveScaled(1,1,61,7) + Height = "18mm" + Fixed = True + Background = ReportBrush["#FFFF00"] + } + { ReportHBox1 ReportHBox + #MoveScaled(1,9,59,6) + Height = "15mm" + { ReportLabel1 ReportLabel + #MoveScaled(0,1,26,5) + Expand = True + } + { ReportLabel2 ReportLabel + #MoveScaled(28,0,30,6) + Font = Font["Bold,+5"] + Expand = True + Alignment = Align.Center + } + } + { ReportPanel1 ReportPanel + #MoveScaled(1,16,60,4) + Fixed = True + Expand = True + Background = ReportBrush["#00FF00"] + Arrangement = Arrange.None + } + { ReportHBox3 ReportHBox + #MoveScaled(0,21,61,7) + Height = "18mm" + Fixed = True + Background = ReportBrush["#FFFF00"] + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report4.class b/comp/src/gb.report/.src/Tests/Old/Report4.class new file mode 100644 index 00000000..e9023949 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report4.class @@ -0,0 +1,46 @@ +' Gambas class file + +Private $hres As Result + +Public Sub _new() + + 'Report.Debug = True + + Try Connections["Connection2"].Open + $hres = db.Exec("SELECT fc.cd_sc as codesoc, fc.int_sc as intsoc, fcc.compte_cc as compte, fcc.intitule_cc as intitule From Fiches_Comptes as fcc " + "Left join Fiches_Societes as fc on fc.cd_sc=1") + ReportHBox4.DataCount = $hres.Count + +End + +Public Sub lblCpte_Data(Index As Integer) + + $hres.MoveTo(Index) + Last.data = $hres!compte + +End + +Public Sub lblcpteLab_Data(Index As Integer) + + $hres.MoveTo(Index) + Last.data = $hres!intitule + +End + +Public Sub lblSoc_Data(Index As Integer) + + Last.data = $hres!codesoc & " " & $hres!intsoc + +End + +Public Sub lblDate_Data(Index As Integer) + + Last.data = Format(Now(), "dddd dd mmmm yyyy") + +End + +Public Sub ReportImage1_Data(Index As Integer) + + Last.Data = Picture["icon:/32/directory"].Image + +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report4.report b/comp/src/gb.report/.src/Tests/Old/Report4.report new file mode 100644 index 00000000..e41447cf --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report4.report @@ -0,0 +1,96 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,92) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Index = 0 + Text = ("") + { ReportLabel3 ReportLabel + #MoveScaled(0,0,58,4) + Height = "2cm" + Fixed = True + Font = Font["Bold,+6"] + Tag = "**" + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000;TopLeftCorner:6mm;BottomLeftCorner:7mm"] + Background = ReportBrush["LinearGradient(0.7,0,0.7,1,[#FFCFBF,#FFFFFF],[0,1])"] + Text = ("IMPRESSION DU PLAN COMPTABLE") + Alignment = Align.Center + } + { ReportPanel1 ReportVBox + #MoveScaled(2,7,50,69) + Padding = ReportPadding["Bottom:2cm;Left:2cm;Right:2cm"] + Expand = True + Tag = "Contenu1" + { lblSoc ReportLabel + #MoveScaled(1,2,53,4) + Height = "2cm" + Fixed = True + Font = Font["Bold,12"] + } + { ReportHBox1 ReportHBox + #MoveScaled(1,14,45,4) + Height = "1cm" + Fixed = True + Background = ReportBrush["#F7DFFF"] + { ReportLabel1 ReportLabel + #MoveScaled(1,0,14,4) + Width = "20%" + Font = Font["Bold,12"] + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000"] + Text = ("Code") + Alignment = Align.Center + } + { ReportLabel2 ReportLabel + #MoveScaled(20,0,14,4) + Font = Font["Bold,12"] + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Intitulé") + Alignment = Align.Center + } + } + { VCont ReportVBox + #MoveScaled(1,20,45,39) + AutoResize = True + Tag = "ListeElements" + { ReportHBox4 ReportHBox + #MoveScaled(1,2,44,7) + Height = "5mm" + { lblCpte ReportLabel + #MoveScaled(8,1,18,4) + Width = "20%" + Padding = ReportPadding["Left:3mm"] + Tag = "*" + } + { lblcpteLab ReportLabel + #MoveScaled(27,0,18,4) + Width = "20%" + Padding = ReportPadding["Left:3mm"] + Expand = True + Border = ReportBorder["Left:1px #000000"] + } + } + } + } + { ReportHBox2 ReportHBox + #MoveScaled(0,77,61,4) + Fixed = True + AutoResize = True + { lblDate ReportLabel + #MoveScaled(0,1,16,4) + Width = "5cm" + } + { ReportHBox3 ReportHBox + #MoveScaled(9,1,24,4) + Expand = True + } + { ReportLabel4 ReportLabel + #MoveScaled(47,1,13,3) + Width = "3cm" + AutoResize = True + Text = ("Page $PAGE sur $NPAGE") + UseField = True + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report5.class b/comp/src/gb.report/.src/Tests/Old/Report5.class new file mode 100644 index 00000000..f5baf4d7 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report5.class @@ -0,0 +1 @@ +' Gambas class file diff --git a/comp/src/gb.report/.src/Tests/Old/Report5.report b/comp/src/gb.report/.src/Tests/Old/Report5.report new file mode 100644 index 00000000..f2bdbb65 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report5.report @@ -0,0 +1,85 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,95) + Padding = ReportPadding["Top:1cm;Bottom:1cm;Left:1cm;Right:1cm"] + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(4,2,56,6) + Height = "12mm" + Expand = True + { ReportLabel9 ReportLabel + #MoveScaled(2,1,20,5) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["#FFFF00"] + } + { ReportHBox2 ReportHBox + #MoveScaled(30,2,24,8) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["#FFFF00"] + } + } + { ReportPageBreak1 ReportPageBreak + #MoveScaled(5,10,60,5) + } + { ReportLabel1 ReportImage + #MoveScaled(1,12,59,8) + Height = "7cm" + Border = ReportBorder["Bottom:2mm #000000"] + Background = ReportBrush["#00FF00"] + } + { ReportLabel2 ReportLabel + #MoveScaled(2,18,59,8) + Height = "2cm" + Border = ReportBorder["Bottom:2mm #000000"] + Background = ReportBrush["LinearGradient(1,0,0.28,0.35,[#00FF00,#FFFFFF],[0,1])"] + } + { ReportLabel4 ReportImage + #MoveScaled(3,23,59,8) + Height = "6cm" + Border = ReportBorder["Bottom:2mm #000000"] + Background = ReportBrush["#00FF00"] + } + { ReportPageBreak2 ReportPageBreak + #MoveScaled(2,31,59,2) + } + { ReportLabel5 ReportLabel + #MoveScaled(2,33,59,8) + Height = "2cm" + Border = ReportBorder["Bottom:2mm #000000"] + Background = ReportBrush["#00FF00"] + } + { ReportPageBreak3 ReportPageBreak + #MoveScaled(2,42,58,3) + } + { ReportLabel3 ReportLabel + #MoveScaled(1,44,59,8) + Height = "2cm" + Border = ReportBorder["Bottom:2mm #000000"] + Background = ReportBrush["#00FF00"] + } + { ReportLabel6 ReportLabel + #MoveScaled(2,52,59,8) + Height = "10cm" + Border = ReportBorder["Bottom:2mm #000000"] + Background = ReportBrush["#00FF00"] + } + { ReportLabel7 ReportLabel + #MoveScaled(2,62,59,8) + Height = "2cm" + Expand = True + Border = ReportBorder["Bottom:2mm #000000"] + Background = ReportBrush["#00FF00"] + } + { ReportLabel8 ReportLabel + #MoveScaled(2,72,59,8) + Height = "2cm" + Expand = True + Border = ReportBorder["Bottom:2mm #000000"] + Background = ReportBrush["#00FF00"] + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report6.class b/comp/src/gb.report/.src/Tests/Old/Report6.class new file mode 100644 index 00000000..0a5fa73a --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report6.class @@ -0,0 +1,2 @@ +' Gambas class file + diff --git a/comp/src/gb.report/.src/Tests/Old/Report6.report b/comp/src/gb.report/.src/Tests/Old/Report6.report new file mode 100644 index 00000000..e4975fe7 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report6.report @@ -0,0 +1,17 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Index = 0 + Text = ("") + { ReportPanel1 ReportPanel + #MoveScaled(4,5,59,31) + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + { ReportLabel1 ReportLabel + #MoveScaled(2,4,53,20) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Text = ("ReportLabel1") + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report7.class b/comp/src/gb.report/.src/Tests/Old/Report7.class new file mode 100644 index 00000000..2aa731de --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report7.class @@ -0,0 +1,29 @@ +' Gambas class file + +Public Sub _new() + + 'Report.Debug = True + ' ReportPanel2.Border.RoundCorner.TopLeft = "5mm/5mm" + ' ReportPanel2.Border.RoundCorner.BottomLeft = "5mm/5mm" + ' ReportPanel2.Border.RoundCorner.TopRight = "5mm/5mm" + ' ReportPanel2.Border.RoundCorner.BottomRight = "5mm/5mm" + +End + +Public Sub ReportImage1_Data(Index As Integer) + + Last.Data = Picture["icon:/32/directory"].Image + +End + +Public Sub ReportImage2_Data(Index As Integer) + + Last.Data = Picture["icon:/32/directory"].Image + +End + +Public Sub ReportImage3_Data(Index As Integer) + + Last.Data = Picture["icon:/32/directory"].Image + +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report7.report b/comp/src/gb.report/.src/Tests/Old/Report7.report new file mode 100644 index 00000000..45cf606d --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report7.report @@ -0,0 +1,59 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Index = 0 + Text = ("") + { ReportPanel1 ReportHBox + #MoveScaled(0,5,61,10) + Height = "4cm" + Padding = ReportPadding["Top:2mm;Bottom:2mm;Left:2mm;Right:2mm"] + { ReportPanel3 ReportPanel + #MoveScaled(1,2,9,8) + Expand = True + } + { ReportPanel2 ReportPanel + #MoveScaled(10,0,41,10) + Width = "10cm" + AutoResize = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000;TopLeftCorner:1cm;TopRightCorner:1cm;BottomRightCorner:1cm;BottomLeftCorner:1cm"] + Arrangement = Arrange.Horizontal + { ReportImage1 ReportImage + #MoveScaled(1,0,11,9) + Width = "3cm" + Height = "6mm" + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Alignment = Align.Center + } + { ReportImage3 ReportImage + #MoveScaled(26,0,11,9) + Width = "3cm" + Height = "6mm" + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Left:1mm #000000"] + Alignment = Align.Center + } + { ReportImage2 ReportImage + #MoveScaled(14,0,11,9) + Width = "3cm" + Height = "6mm" + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Left:1mm #000000"] + Alignment = Align.Center + } + } + { ReportPanel4 ReportPanel + #MoveScaled(52,1,9,9) + Expand = True + } + } + { ReportLabel1 ReportLabel + #MoveScaled(5,18,57,13) + Height = "2cm" + Font = Font["Bold,+6"] + Text = ("coucou") + Alignment = Align.Center + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/Report8.class b/comp/src/gb.report/.src/Tests/Old/Report8.class new file mode 100644 index 00000000..d9c700c7 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report8.class @@ -0,0 +1,56 @@ +' Gambas class file + +Private hResult1 As Result +Private hResult2 As Result + +Public Sub _new() + + Connections["Connection1"].Open + 'Report.Debug = True + hResult1 = db.Exec("select distinct firstname from test") + + ReportPanel1.DataCount = hResult1.Count + 'ReportHBox1.DataCount = 4 + 'ReportHBox1.Border.RoundCorner.TopRight = "5mm 5mm" + 'ReportHBox1.Border.RoundCorner.BottomRight = "5mm/5mm" +'Report.Debug = True +ReportLabel3.BoxShadow.XOffset = "1mm" +ReportLabel3.BoxShadow.YOffset = "1mm" +ReportLabel3.BoxShadow.Blur = "1mm" + + + +End + +Public Sub ReportLabel1_Data(Index As Integer) + + hResult2.MoveTo(Index) + Last.data = hResult2!name + Print "FillData = " & hResult1!firstname +End + +Public Sub ReportPanel1_BeforeData() +If hResult1.Max < ReportPanel1.DataIndex Then + Print "error" & ReportPanel1.DataIndex + Return +Endif + hResult1.MoveTo(ReportPanel1.dataindex) + Print "BeforeData = " & hResult1!firstname + hResult2 = db.Exec("Select * From test where firstname=&1 LIMIT 1,20", hResult1!firstname) + ReportHBox1.DataCount = hResult2.Count + +End + +Public Sub ReportLabel2_Data(Index As Integer) + hResult1.MoveTo(Index) + Last.data = hResult1!firstname + + 'Print hResult1!firstname, Index + +End + +Public Sub Report_Open() + + + +End diff --git a/comp/src/gb.report/.src/Tests/Old/Report8.report b/comp/src/gb.report/.src/Tests/Old/Report8.report new file mode 100644 index 00000000..68d32d17 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/Report8.report @@ -0,0 +1,79 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,88) + Padding = ReportPadding["Top:15mm;Bottom:2cm;Left:2cm;Right:2cm"] + Tag = "\"toto\"" + Spacing = "1cm" + Index = 0 + Text = ("") + { ReportLabel5 ReportLabel + #MoveScaled(2,0,61,4) + Brush = ReportBrush["#9F9F9F"] + Fixed = True + AutoResize = True + Text = ("List of all my friends") + Alignment = Align.Right + } + { ReportLabel3 ReportLabel + #MoveScaled(2,3,60,18) + Font = Font["Bold,+10"] + Padding = ReportPadding["Top:2mm;Bottom:2mm;Left:2mm;Right:2mm"] + AutoResize = True + Border = ReportBorder["Top:1mm #9F9F9F;Bottom:1mm #9F9F9F;Left:1mm #9F9F9F;Right:1mm #9F9F9F;TopLeftCorner:5mm;TopRightCorner:5mm;BottomRightCorner:5mm;BottomLeftCorner:5mm"] + Background = ReportBrush["#1FFF8F"] + Text = ("List Of My Friends") + Alignment = Align.Center + } + { ReportPanel2 ReportVBox + #MoveScaled(0,23,65,54) + Expand = True + Tag = "Boite 1" + Spacing = "1cm" + { ReportPanel1 ReportVBox + #MoveScaled(7,3,64,45) + Width = "4cm" + Height = "5cm" + AutoResize = True + Tag = "Boite 2" + Spacing = "1mm" + ForceNewPage = True + { ReportLabel2 ReportLabel + #MoveScaled(2,1,54,8) + Fixed = True + Font = Font["Bold,+3"] + Padding = ReportPadding["Left:5mm"] + AutoResize = True + Tag = "head" + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000;TopLeftCorner:3mm;TopRightCorner:3mm;BottomRightCorner:3mm;BottomLeftCorner:3mm"] + Background = ReportBrush["#1FFF8F"] + } + { ReportHBox1 ReportHBox + #MoveScaled(2,11,71,31) + Width = "5cm" + Height = "2cm" + Padding = ReportPadding["Left:5mm"] + AutoResize = True + Tag = "Boite 3" + Background = ReportBrush["#FFFFFF"] + { ReportLabel1 ReportLabel + #MoveScaled(5,5,47,17) + Font = Font["+1"] + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + AutoResize = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Background = ReportBrush["#DFDF6F"] + } + } + } + } + { ReportLabel4 ReportLabel + #MoveScaled(0,79,62,4) + Fixed = True + AutoResize = True + Text = ("Page $PAGE / $NPAGE") + Alignment = Align.Right + UseField = True + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/myReport1.class b/comp/src/gb.report/.src/Tests/Old/myReport1.class new file mode 100644 index 00000000..dd781dce --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/myReport1.class @@ -0,0 +1,43 @@ +' Gambas class file + +Private hCon As Connection = Connections["Connection1"] +Private hResult As Result + +Public Sub _new() + + 'Dim hresult As Result + 'Report.debug = True + ' ReportLabel8.Fixed = True + ' ReportHBox4.Fixed = True + 'ReportHBox3._Count = 200 + ' ReportLabel2.Fixed = True + ' hCon.Type = "sqlite" + ' hCon.Host = User.Home + ' hCon.Name = "test" + hresult = db.Exec("SELECT * FROM test") + 'Print hresult.Count + ReportHBox1.DataCount = hresult.Count + 'ReportLabel1.Brush = ReportBrush["&h00FF00&"] + +End + +Public Sub ReportLabel1_Data(Index As Integer) + + hresult.MoveTo(Index) + Last.data = hresult!name + +End + +Public Sub ReportLabel6_Data(Index As Integer) + + hresult.MoveTo(Index) + Last.data = hresult!firstname + +End + +Public Sub ReportLabel7_Data(Index As Integer) + + hresult.MoveTo(Index) + Last.data = hresult!birth + +End diff --git a/comp/src/gb.report/.src/Tests/Old/myReport1.report b/comp/src/gb.report/.src/Tests/Old/myReport1.report new file mode 100644 index 00000000..81683e3d --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/myReport1.report @@ -0,0 +1,142 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,71,97) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Spacing = "2cm" + Count = 3 + Index = 0 + Text = ("First Page") + { ReportVBox2 ReportVBox + #MoveScaled(2,3,51,11) + Expand = True + } + { ReportLabel9 ReportLabel + #MoveScaled(4,16,48,13) + Font = Font["Bitstream Charter,Bold,+9"] + Padding = ReportPadding["Top:6mm;Bottom:6mm;Left:6mm;Right:6mm"] + AutoResize = True + Border = ReportBorder["Top:2mm LinearGradient(0,0,1,1,[#00FF00,#FFFFFF,#FF0000],[0,1,0.59]);Bottom:2mm LinearGradient(0,0,1,1,[#00FF00,#FFFFFF,#FF0000],[0,1,0.59]);Left:2mm LinearGradient(0,0,1,1,[#00FF00,#FFFFFF,#FF0000],[0,1,0.59]);Right:2mm LinearGradient(0,0,1,1,[#00FF00,#FFFFFF,#FF0000],[0,1,0.59]);TopLeftCorner:8mm;TopRightCorner:8mm;BottomRightCorner:8mm;BottomLeftCorner:8mm"] + Text = ("Report About") + Alignment = Align.Center + } + { ReportLabel10 ReportLabel + #MoveScaled(6,35,43,7) + Font = Font["Bitstream Charter,+9"] + AutoResize = True + Border = ReportBorder["Top:4mm #FFAF5F;Bottom:4mm #FFAF5F;Left:4mm #FFAF5F;Right:4mm #FFAF5F;TopLeftCorner:4mm;TopRightCorner:4mm;BottomRightCorner:4mm;BottomLeftCorner:4mm"] + Text = ("All my friends") + Alignment = Align.Center + } + { ReportVBox3 ReportVBox + #MoveScaled(2,47,52,11) + Expand = True + Border = ReportBorder["Top:2px #000000;Bottom:2px #000000;Left:2px #000000;Right:2px #000000"] + } + Index = 1 + Text = ("Friend Table") + { ReportLabel11 ReportLabel + #MoveScaled(5,3,57,2) + Height = "15mm" + Brush = ReportBrush["#BFBFBF"] + Fixed = True + Text = ("Report about all my frends") + Alignment = Align.TopRight + } + { ReportLabel2 ReportLabel + #MoveScaled(5,6,45,5) + Height = "3cm" + Font = Font["Bitstream Charter,+7"] + Text = ("Test Database") + Alignment = Align.Center + } + { ReportVBox1 ReportVBox + #MoveScaled(5,13,45,38) + Expand = True + AutoResize = True + Tag = "Boite 1" + { ReportVBox4 ReportVBox + #MoveScaled(3,12,41,24) + AutoResize = True + Tag = "Boite 2" + { ReportHBox2 ReportHBox + #MoveScaled(0,0,37.7143,5.1429) + Height = "15mm" + Fixed = True + AutoResize = True + { ReportLabel3 ReportLabel + #MoveScaled(1,1,10.2857,3.4286) + Width = "5 cm" + Height = "10mm" + Font = Font["Bitstream Charter,Bold,16"] + AutoResize = True + Text = ("Name") + Alignment = Align.Center + } + { ReportLabel4 ReportLabel + #MoveScaled(12,1,10.2857,3.4286) + Width = "5 cm" + Height = "10mm" + Font = Font["Bitstream Charter,Bold,16"] + Padding = ReportPadding["0.2 mm"] + AutoResize = True + Text = ("FirstName") + Alignment = Align.Center + } + { ReportLabel5 ReportLabel + #MoveScaled(24.8571,0.8571,10.2857,3.4286) + Width = "5 cm" + Height = "10mm" + Font = Font["Bitstream Charter,Bold,16"] + Padding = ReportPadding["0.2 mm"] + Expand = True + AutoResize = True + Text = ("Birth") + Alignment = Align.Center + } + } + { ReportHBox1 ReportHBox + #MoveScaled(1,8,38,4) + Height = "10mm" + Tag = "Boite 3" + { ReportLabel1 ReportLabel + #MoveScaled(0,0,10.2857,3.4286) + Width = "5 cm" + Font = Font["12"] + Padding = ReportPadding["0.2 mm"] + Alignment = Align.Center + UseField = True + } + { ReportLabel6 ReportLabel + #MoveScaled(11,0,10.2857,3.4286) + Width = "5 cm" + Font = Font["12"] + Padding = ReportPadding["0.2 mm"] + Alignment = Align.Center + } + { ReportLabel7 ReportLabel + #MoveScaled(24,0,10.2857,3.4286) + Width = "5 cm" + Font = Font["12"] + Padding = ReportPadding["2.2mm"] + Expand = True + Format = "dd/mm/aaaa" + Alignment = Align.Right + } + } + } + } + { ReportLabel8 ReportLabel + #MoveScaled(2,51,46,3) + Height = "6mm" + Fixed = True + Padding = ReportPadding["2mm"] + AutoResize = True + Text = ("$PAGE/$NPAGE") + Alignment = Align.Right + UseField = True + } + Index = 2 + Text = ("") + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/myReport2.class b/comp/src/gb.report/.src/Tests/Old/myReport2.class new file mode 100644 index 00000000..3588055e --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/myReport2.class @@ -0,0 +1,85 @@ +' Gambas class file + +Private cList As New Collection[] +Private cFactHead As New Collection[] + +Public Sub _new() + + Dim cLine As Collection + Dim i As Integer + + 'Report.Debug = True + + cLine = New Collection + cLine["nom"] = "Berton Toto " + cLine["adresse"] = "La belle route" + cLine["CP"] = "17520" + cLine["ville"] = "Archiac" + cLine["TOTALHT"] = 0 + cFactHead.Add(cLine) + 'Me.Data = cFactHead + For i = 0 To 12 + cLine = New Collection + cLine["designation"] = "Patin a roulette" + cLine["PU"] = 19.90 + cLine["Q"] = 1 + cList.Add(cLine) + cLine = New Collection + cLine["designation"] = "Cuilliere en bois" + cLine["PU"] = 8.90 + cLine["Q"] = 1 + cList.Add(cLine) + cLine = New Collection + cLine["designation"] = "Lapin en peluche" + cLine["PU"] = 19.90 + cLine["Q"] = 5 + cList.Add(cLine) + cLine = New Collection + cLine["designation"] = "Cloche de noël" + cLine["PU"] = 20 + cLine["Q"] = 10 + cList.Add(cLine) + cLine = New Collection + cLine["designation"] = "Cheque en bois" + cLine["PU"] = 1000 + cLine["Q"] = 1 + cList.Add(cLine) + cLine = New Collection + cLine["designation"] = "Pense bête" + cLine["PU"] = 2 + cLine["Q"] = 3 + cList.Add(cLine) + Next + 'ReportHBox3.Data = cList + 'ReportHBox12.Data = cList + 'Compute Summaries + i = 0 + For Each cLine In cList + Inc i + cLine["id"] = i + cLine["THT"] = Round(cLine["PU"] * cLine["Q"], -2) + cFactHead[0]["TOTALHT"] += cLine["THT"] + + Next + ReportHBox3.DataCount = cList.Count + +End + +Public Sub ligne_Data(Index As Integer) + + If Not Last.tag Then Return + Last.data = cList[Index][Last.tag] + +End + +Public Sub entete_Data(Index As Integer) + + Last.data = cFactHead[Index][Last.tag] + +End + +Public Sub ReportVBox1_BeforeArrange() + + Debug "Before Arrange" + +End diff --git a/comp/src/gb.report/.src/Tests/Old/myReport2.report b/comp/src/gb.report/.src/Tests/Old/myReport2.report new file mode 100644 index 00000000..643c85f7 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/myReport2.report @@ -0,0 +1,228 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,106,113) + Padding = ReportPadding["1cm"] + Spacing = "1cm" + Index = 0 + Text = ("Section 1") + { ReportVBox2 ReportVBox + #MoveScaled(4,4,80,42) + Fixed = True + AutoResize = True + { ReportLabel9 ReportLabel + #MoveScaled(0,0,68,7) + Fixed = True + Font = Font["Bitstream Charter,+11"] + AutoResize = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Facture") + Alignment = Align.Center + } + { ReportHBox1 ReportHBox + #MoveScaled(1,9,72,25) + Height = "4cm" + Fixed = True + Expand = True + Border = ReportBorder["Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + { ReportHBox5 ReportHBox + #MoveScaled(2,1,34,22) + Width = "5cm" + Height = "5cm" + Fixed = True + Expand = True + AutoResize = True + { ReportSvgImage1 ReportSvgImage + #MoveScaled(3,2,25,17) + Width = "50mm" + Height = "30mm" + Alignment = Align.Center + Image = SvgImage.Load("img/logo.svg") + } + } + { ReportVBox3 ReportVBox + #MoveScaled(38,1,34,20) + Padding = ReportPadding["Top:4mm;Bottom:4mm;Left:4mm;Right:4mm"] + Expand = True + AutoResize = True + Tag = "*" + { Nom ReportLabel entete + Name = "Nom" + #MoveScaled(2,1,28,3) + AutoResize = True + Tag = "nom" + } + { Adresse ReportLabel entete + Name = "Adresse" + #MoveScaled(2,6,28,3) + AutoResize = True + Tag = "adresse" + } + { ReportHBox6 ReportHBox + #MoveScaled(1,9,30,5) + AutoResize = True + Tag = "*" + Spacing = "2mm" + { CP ReportLabel entete + Name = "CP" + #MoveScaled(2,1,10,3) + Width = "25mm" + Padding = ReportPadding["Right:1mm"] + AutoResize = True + Tag = "CP" + } + { ville ReportLabel entete + Name = "ville" + #MoveScaled(13,1,16,3) + Expand = True + AutoResize = True + Tag = "ville" + } + } + } + } + } + { ReportVBox1 ReportVBox + #MoveScaled(4,49,90,26) + Expand = True + { ReportHBox2 ReportHBox + #MoveScaled(2,0,86,5) + Height = "1cm" + Fixed = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + { ReportLabel11 ReportLabel + #MoveScaled(0,0,21,4) + Width = "15mm" + Font = Font["Bitstream Charter,Bold"] + Border = ReportBorder["Right:1px #000000"] + Text = ("Id") + Alignment = Align.Center + } + { ReportLabel1 ReportLabel + #MoveScaled(17,1,21,4) + Width = "40%" + Font = Font["Bitstream Charter,Bold"] + Expand = True + Border = ReportBorder["Top=1mm &H00000000&; Bottom=1mm &H00000000&; Left=1mm &H00000000&; "] + Text = ("Designation") + Alignment = Align.Center + } + { ReportLabel2 ReportLabel + #MoveScaled(38,1,13,4) + Width = "3cm" + Font = Font["Bitstream Charter,Bold"] + Border = ReportBorder["Left:1px #000000"] + Text = ("PU HT") + Alignment = Align.Center + } + { ReportLabel3 ReportLabel + #MoveScaled(51,1,13,4) + Width = "3cm" + Font = Font["Bitstream Charter,Bold"] + Tag = "*" + Border = ReportBorder["Left:1px #000000"] + Text = ("Quantité") + Alignment = Align.Center + } + { ReportLabel4 ReportLabel + #MoveScaled(68,1,13,4) + Width = "3cm" + Font = Font["Bitstream Charter,Bold"] + Border = ReportBorder["Left:1px #000000"] + Text = ("Total HT") + Alignment = Align.Center + } + } + { ReportHBox3 ReportHBox + #MoveScaled(2,7,86,5) + Height = "8mm" + AutoResize = True + Tag = "*" + { ReportLabel12 ReportLabel ligne + Name = "ReportLabel12" + #MoveScaled(0,0,17,4) + Width = "15mm" + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + AutoResize = True + Border = ReportBorder["Left:1px #000000"] + Alignment = Align.Center + } + { ReportLabel5 ReportLabel ligne + Name = "ReportLabel5" + #MoveScaled(19,0,21,4) + Width = "40%" + Padding = ReportPadding["Top:2mm;Bottom:2mm;Left:2mm;Right:2mm"] + Expand = True + Tag = "designation" + Border = ReportBorder["Left:1px #000000"] + } + { ReportLabel6 ReportLabel ligne + Name = "ReportLabel6" + #MoveScaled(39,1,13,4) + Width = "3cm" + Padding = ReportPadding["Top:2mm;Bottom:2mm;Left:2mm;Right:2mm"] + Tag = "PU" + Border = ReportBorder["Left:1px #000000"] + Alignment = Align.Right + } + { ReportLabel7 ReportLabel ligne + Name = "ReportLabel7" + #MoveScaled(52,1,13,4) + Width = "3cm" + Padding = ReportPadding["Top:2mm;Bottom:2mm;Left:2mm;Right:2mm"] + Tag = "Q" + Border = ReportBorder["Left:1px #000000"] + Alignment = Align.Right + } + { ReportLabel8 ReportLabel ligne + Name = "ReportLabel8" + #MoveScaled(70,1,13,4) + Width = "3cm" + Padding = ReportPadding["Top:2mm;Bottom:2mm;Left:2mm;Right:2mm"] + Border = ReportBorder["Left:1px #000000;Right:1px #000000"] + Format = "#.00 €" + Alignment = Align.Right + } + } + { ReportHBox8 ReportHBox + #MoveScaled(3,13,83,2) + Height = "1mm" + Fixed = True + Border = ReportBorder["Top:1px #000000"] + Background = ReportBrush["#FFFF00"] + } + { ReportHBox4 ReportHBox + #MoveScaled(4,17,81,5) + AutoResize = True + { ReportHBox7 ReportHBox + #MoveScaled(4,2,62,2) + Expand = True + } + { ReportLabel14 ReportLabel + #MoveScaled(68,1,11,4) + Width = "3cm" + Padding = ReportPadding["Top:2mm;Bottom:2mm"] + AutoResize = True + Border = ReportBorder["Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Format = "#.00 €" + Alignment = Align.Right + } + } + } + { ReportHBox9 ReportHBox + #MoveScaled(1,100,101,6) + Fixed = True + AutoResize = True + { ReportHBox10 ReportHBox + #MoveScaled(2,1,31,3) + Expand = True + } + { ReportLabel10 ReportLabel + #MoveScaled(68,0,31,4) + AutoResize = True + Text = ("Page $PAGE") + UseField = True + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Old/myReport5.class b/comp/src/gb.report/.src/Tests/Old/myReport5.class new file mode 100644 index 00000000..391ceb22 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/myReport5.class @@ -0,0 +1,22 @@ +' Gambas class file + +Public Sub _new() + + Dim Rlbl As ReportLabel + Dim i As Integer + 'Report.Debug = True + For i = 0 To 100 + + Rlbl = New ReportLabel(RVBCont) + Rlbl.Autoresize = True + Rlbl.Text = "Ligne " & i + Rlbl.Border.bottom.Width = "1px" + Rlbl.Padding.Top = "3 mm" + Rlbl.Padding.Bottom = "1mm" + Rlbl.Padding.Left = "2 mm" + Rlbl.Top = i & "cm" + Rlbl.Left = i & "cm" + Rlbl.Width = "1 cm" + Next + +End diff --git a/comp/src/gb.report/.src/Tests/Old/myReport5.report b/comp/src/gb.report/.src/Tests/Old/myReport5.report new file mode 100644 index 00000000..4f106dc2 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Old/myReport5.report @@ -0,0 +1,91 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,90,76) + Tag = "*" + Index = 0 + Text = ("") + { ReportHBox2 ReportHBox + #MoveScaled(0,0,77,14) + Height = "4cm" + Fixed = True + Padding = ReportPadding["Top:7mm;Bottom:7mm;Left:7mm;Right:7mm"] + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["#3398C3"] + { ReportSvgImage1 ReportSvgImage + #MoveScaled(2,2,11,11) + Width = "4cm" + Image = SvgImage.Load("img/logo.svg") + } + { ReportLabel3 ReportLabel + #MoveScaled(20,2,48,10) + Left = "1mm" + Top = "1mm" + Width = "3cm" + Brush = ReportBrush["LinearGradient(1,0.04,0,0.04,[#DF6B00,#FFFFFF],[0,1])"] + Fixed = True + Font = Font["DejaVu Sans,Bold,+14"] + Padding = ReportPadding["Top:6mm;Bottom:6mm;Left:6mm;Right:6mm"] + Expand = True + Text = ("Gambas") + Alignment = Align.Left + } + } + { ReportHBox1 ReportHBox + #MoveScaled(0,15,87,56) + Expand = True + Border = ReportBorder["Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["#FF007F"] + { ReportVBox1 ReportVBox + #MoveScaled(1,1,25,47) + Width = "6cm" + Background = ReportBrush["#DF6B00"] + } + { ReportVBox2 ReportVBox + #MoveScaled(28,2,56,54) + Expand = True + Border = ReportBorder["Left:1mm #000000"] + Background = ReportBrush["#FF7F00"] + { RVBCont ReportPanel + #MoveScaled(3,5,45,36) + Expand = True + Tag = "*" + Background = ReportBrush["#FFFF00"] + { ReportLabel1 ReportLabel + #MoveScaled(3,5,43,23) + Left = "25%" + Top = "5cm" + Width = "50%" + Height = "5cm" + Brush = ReportBrush["LinearGradient(0.21,0.91,0.81,0.11,[#FF000000,#FFFFFF],[0,1])"] + Fixed = True + Font = Font["Bold,+15"] + Ignore = True + Background = ReportBrush["#FF1F57"] + Text = ("gambas") + Alignment = Align.Center + } + { ReportImage1 ReportImage + #MoveScaled(10,17,14,8) + Height = "3cm" + Alignment = Align.Center + Image = Image.Load("printer1.png") + } + } + { ReportLabel2 ReportLabel + #MoveScaled(9,47,47,5) + Brush = ReportBrush["#FFFFFF"] + Fixed = True + Font = Font["Bold,+1"] + Padding = ReportPadding["Top:4mm;Bottom:4mm;Left:2mm;Right:2mm"] + AutoResize = True + Border = ReportBorder["Top:1mm #000000"] + Background = ReportBrush["#3398C3"] + Text = ("Page $PAGE on $NPAGE") + Alignment = Align.Right + UseField = True + } + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/OutputReport.class b/comp/src/gb.report/.src/Tests/OutputReport.class new file mode 100644 index 00000000..7fc4dc12 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/OutputReport.class @@ -0,0 +1,146 @@ +' Gambas class file + +Public nnoderows As Integer +Public nanchorrows As Integer + +Public Sub _new() + + 'enter current date on the header + ReportLabel2.Text = Date + + 'enter the project information in the header on the print out page + 'With FMain.Project + ReportLabel3.Text = "Project Title: " ' & .JobTitle + ReportLabel4.Text = "Project No.: " ' & .Number + ReportLabel5.Text = "Company: " ' & .Company + ReportLabel6.Text = "Designer: " '& .Designer + ReportLabel7.Text = "Base Plate ID: " ' & .BasePlateID + 'End With + + 'set count to four to display the results for qmax. Tmax, L, and phi + ReportHBox9.DataCount = 4 + + 'set the number of plate nodes to display in print out tables + 'nnoderows = Module1.RoundUpInt(FMain.ConcreteSection.cNodes.Count / 2) + ReportHBox5.DataCount = 9 + + 'set the number of anchor rods to display in print out tables + ' nanchorrows = Module1.RoundUpInt(FMain.cAnchorRods.Count / 2) + ReportHBox7.DataCount = 3 + FullRepeat.DataCount = 3 + +End + + +Public Sub ReportImage2_Data(Index As Integer) + ' Dim current_results As Results + ' Dim pSection As Picture + ' + ' 'create a new picture object for drawing the base plate + ' pSection = New Picture(300, 300, False) + ' + ' 'get the results for first load case + ' current_results = FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)] + ' + ' 'refresh base plate drawing with results + ' Module1.DrawBasePlate(pSection, 1, current_results) + ' + ' 'add the picture of the base plate to the page + ' ReportImage2.Image = pSection.Image + +End + + +Public Sub ReportLabel9_Data(Index As Integer) + + Last.Data = "Load Case: " '& FMain.cLoads[Str$(ReportHBox3.DataIndex)].label + +End + + +Public Sub ReportLabel14_Data(Index As Integer) + Dim $value As String + + Select Case Index + Case 0 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].Tmax, 2) + Last.Data = "T(max) = " & $value & " lbs" + Case 1 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].maxstress, 2) + Last.Data = "q(max) = " & $value & " psi" + Case 2 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].L, 2) + Last.Data = "L = " & $value & " in" + Case 3 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].phi, 2) + Last.Data = "phi = " & $value & " degrees" + Case Else + End Select + +End + + +Public Sub ReportLabel25_Data(Index As Integer) + + Last.Data = Str(Index + 1) &/ Str(FullRepeat.DataIndex) + +End + +Public Sub ReportLabel29_Data(Index As Integer) + + Last.Data = nnoderows + Index + 1 + +End + +Public Sub ReportLabel26_Data(Index As Integer) + Dim value As Float + + 'value = FMain.AnalysisResults[Str$(ReportVBox4.DataIndex)].stress[Index] + 'Last.Data = Module1.AdvFormat(value, 2) + +End + +Public Sub ReportLabel30_Data(Index As Integer) + ' Dim value As Float + ' Dim current_results As Results + ' + ' current_results = FMain.AnalysisResults[Str$(ReportVBox4.DataIndex)] + ' If nnoderows + Index < current_results.stress.Count Then + ' value = current_results.stress[nnoderows + Index] + ' Last.Data = Module1.AdvFormat(value, 2) + ' Endif + +End + +Public Sub ReportLabel49_Data(Index As Integer) + + Last.Data = Str(Index + 1) &/ Str(FullRepeat.DataIndex) + +End + +Public Sub ReportLabel52_Data(Index As Integer) + + Last.Data = nanchorrows + Index + 1 + +End + +Public Sub ReportLabel50_Data(Index As Integer) + Dim value As Float + + ' value = FMain.AnalysisResults[Str$(ReportVBox6.DataIndex)].T[Index] + ' Last.Data = Module1.AdvFormat(value, 2) + +End + +Public Sub ReportLabel53_Data(Index As Integer) + ' Dim value As Float + ' Dim current_results As Results + ' + ' current_results = FMain.AnalysisResults[Str$(ReportVBox6.DataIndex)] + ' If nanchorrows + Index < current_results.T.Count Then + ' value = current_results.T[nanchorrows + Index] + ' Last.Data = Module1.AdvFormat(value, 2) + ' Endif + +End + diff --git a/comp/src/gb.report/.src/Tests/OutputReport.report b/comp/src/gb.report/.src/Tests/OutputReport.report new file mode 100644 index 00000000..045630c1 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/OutputReport.report @@ -0,0 +1,306 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,76,85) + Fixed = True + Padding = ReportPadding["Top:20mm;Bottom:20mm;Left:20mm;Right:20mm"] + Expand = True + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(2,0,62,11) + Height = "25mm" + Fixed = True + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Bottom:2px #000000"] + { ReportImage1 ReportImage + #MoveScaled(1,1,18,9) + Width = "51mm" + AutoResize = True + } + { ReportLabel1 ReportLabel + #MoveScaled(21,1,10,9) + AutoResize = True + Text = ("Version 1.0") + Alignment = Align.BottomLeft + } + { ReportLabel2 ReportLabel + #MoveScaled(31,1,30,9) + Expand = True + AutoResize = True + Text = ("Date") + Alignment = Align.BottomRight + UseField = True + } + } + { ReportVBox1 ReportVBox + #MoveScaled(2,11,62,9) + Height = "28.01mm" + Fixed = True + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Bottom:2px #000000"] + { ReportLabel3 ReportLabel + #MoveScaled(0,0,12,2) + Height = "6mm" + AutoResize = True + Text = ("Project Title:") + Alignment = Align.TopLeft + } + { ReportLabel4 ReportLabel + #MoveScaled(0,2,12,2) + Height = "6mm" + AutoResize = True + Text = ("Project No.:") + Alignment = Align.TopLeft + } + { ReportLabel5 ReportLabel + #MoveScaled(0,4,12,2) + Height = "6mm" + AutoResize = True + Text = ("Company:") + Alignment = Align.TopLeft + } + { ReportLabel6 ReportLabel + #MoveScaled(0,6,12,2) + Height = "6mm" + AutoResize = True + Text = ("Designer:") + Alignment = Align.TopLeft + } + } + { ReportHBox2 ReportHBox + #MoveScaled(2,20,62,3) + Height = "8mm" + Fixed = True + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Bottom:2px #000000"] + { ReportLabel7 ReportLabel + #MoveScaled(0,0,12,2) + Height = "6mm" + AutoResize = True + Text = ("Base Plate ID:") + Alignment = Align.TopLeft + } + } + { FullRepeat ReportPanel + #MoveScaled(2,25,63,50) + AutoResize = True + Tag = "**" + ForceNewPage = True + { ReportHBox3 ReportHBox + #MoveScaled(0,0,62,17) + Height = "61mm" + Fixed = True + Expand = True + { ReportVBox3 ReportVBox + #MoveScaled(1,1,18,14) + AutoResize = True + { ReportHBox8 ReportHBox + #MoveScaled(1,2,15,4) + Width = "51mm" + Height = "12mm" + { ReportLabel9 ReportLabel + #MoveScaled(1,1,13,2) + Font = Font["+1"] + Expand = True + } + } + { ReportHBox9 ReportHBox + #MoveScaled(1,8,15,4) + Width = "51mm" + Height = "6mm" + { ReportLabel14 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + } + } + } + { ReportImage2 ReportImage + #MoveScaled(31,1,17,15) + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Expand = True + Background = ReportBrush["#0000FF"] + Alignment = Align.Center + } + } + { ReportVBox4 ReportVBox + #MoveScaled(1,18,62,14) + AutoResize = True + Tag = "Serie 1" + { ReportLabel19 ReportLabel + #MoveScaled(2,0,16,4) + Fixed = True + Font = Font["+1"] + Padding = ReportPadding["Top:3mm"] + AutoResize = True + Text = ("Bearing Pressue") + } + { ReportHBox4 ReportHBox + #MoveScaled(1,3,60,4) + Height = "6mm" + Fixed = True + { ReportLabel20 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Node #") + Alignment = Align.Center + } + { ReportLabel21 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Brg. Press., psi") + Alignment = Align.Center + } + { ReportLabel22 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel23 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Node #") + Alignment = Align.Center + } + { ReportLabel24 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Brg. Press., psi") + Alignment = Align.Center + } + } + { ReportHBox5 ReportHBox + #MoveScaled(1,8,60,4) + Height = "6mm" + { ReportLabel25 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel26 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel27 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel29 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel30 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + } + } + { ReportVBox6 ReportVBox + #MoveScaled(1,33,62,14) + AutoResize = True + Tag = "Serie2" + { ReportLabel31 ReportLabel + #MoveScaled(2,0,19,4) + Fixed = True + Font = Font["+1"] + Padding = ReportPadding["Top:3mm"] + AutoResize = True + Text = ("Anchor Rod Tension") + } + { ReportHBox6 ReportHBox + #MoveScaled(1,3,60,4) + Height = "6mm" + Fixed = True + { ReportLabel44 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Rod #") + Alignment = Align.Center + } + { ReportLabel45 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Tension, lbs") + Alignment = Align.Center + } + { ReportLabel46 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel47 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Rod #") + Alignment = Align.Center + } + { ReportLabel48 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Tension, lbs") + Alignment = Align.Center + } + } + { ReportHBox7 ReportHBox + #MoveScaled(1,8,60,4) + Height = "6mm" + { ReportLabel49 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel50 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel51 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel52 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel53 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + } + } + } + { ReportHBox10 ReportHBox + #MoveScaled(2,75,62,4) + Fixed = True + Expand = True + AutoResize = True + { ReportLabel8 ReportLabel + #MoveScaled(22,1,21,2) + Expand = True + AutoResize = True + Text = ("Page $PAGE of $NPAGE") + Alignment = Align.Bottom + UseField = True + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/OutputReport2.class b/comp/src/gb.report/.src/Tests/OutputReport2.class new file mode 100644 index 00000000..4142a133 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/OutputReport2.class @@ -0,0 +1,166 @@ +' Gambas class file + +Public nnoderows As Integer +Public nanchorrows As Integer + +Public Sub _new() +'Report.Debug = True + 'enter current date on the header + ReportLabel2.Text = Date + + 'enter the project information in the header on the print out page + 'With FMain.Project + ReportLabel3.Text = "Project Title: " ' & .JobTitle + ReportLabel4.Text = "Project No.: '" '& .Number + ReportLabel5.Text = "Company: " '& .Company + ReportLabel6.Text = "Designer: " '& .Designer + ReportLabel7.Text = "Base Plate ID: " ' & .BasePlateID + 'End With + + 'set count to four to display the results for qmax. Tmax, L, and phi + ReportHBox9.DataCount = 4 + + 'set the number of plate nodes to display in print out tables + ' nnoderows = Module1.RoundUpInt(FMain.ConcreteSection.cNodes.Count / 2) + ReportHBox5.DataCount = 11 'nnoderows + + 'set the number of anchor rods to display in print out tables + 'nanchorrows = Module1.RoundUpInt(FMain.cAnchorRods.Count / 2) + ReportHBox7.DataCount = 13 'nanchorrows + + 'set the number of report panels - one for each load combination result + ReportPanel1.DataCount = 6 'FMain.AnalysisResults.Count + +End + + +Public Sub ReportImage2_Data(Index As Integer) + 'Dim current_results As Results + Dim pSection As Picture + + 'create a new picture object for drawing the base plate + pSection = New Picture(300, 300, False) + + 'get the results for first load case + 'current_results = FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)] + + 'refresh base plate drawing with results + 'Module1.DrawBasePlate(pSection, 1, current_results) + + 'add the picture of the base plate to the page + ReportImage2.Image = pSection.Image + +End + + +Public Sub ReportLabel9_Data(Index As Integer) + + Last.Data = "Load Case: " '& FMain.cLoads[Str$(ReportHBox3.DataIndex)].label + +End + + +Public Sub ReportLabel14_Data(Index As Integer) + Dim $value As String + + Select Case Index + Case 0 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].Tmax, 2) + Last.Data = "T(max) = " & 3 & " lbs" + Case 1 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].maxstress, 2) + Last.Data = "q(max) = " & 3 & " psi" + Case 2 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].L, 2) + Last.Data = "L = " & 3 & " in" + Case 3 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].phi, 2) + Last.Data = "phi = " & 3 & " degrees" + Case Else + End Select + +End + + +Public Sub ReportLabel25_Data(Index As Integer) + + Last.Data = Index + 1 + +End + +Public Sub ReportLabel29_Data(Index As Integer) + ' Dim current_results As Results + + 'current_results = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)] + + 'If nnoderows + Index < current_results.stress.Count Then + Last.Data = nnoderows + Index + 1 + 'Else + ' Last.Data = Null + ' Endif + +End + +Public Sub ReportLabel26_Data(Index As Integer) + Dim value As Float + + 'value = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)].stress[Index] + Last.Data = "tt" 'Module1.AdvFormat(value, 2) + +End + +Public Sub ReportLabel30_Data(Index As Integer) + Dim value As Float + 'Dim current_results As Results + + 'current_results = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)] + ' If nnoderows + Index < current_results.stress.Count Then + 'value = current_results.stress[nnoderows + Index] + Last.Data = "mm" 'Module1.AdvFormat(value, 2) + ' Else + Last.Data = Null + 'Endif + +End + +Public Sub ReportLabel49_Data(Index As Integer) + + Last.Data = Index + 1 + +End + +Public Sub ReportLabel52_Data(Index As Integer) + 'Dim current_results As Results + + 'current_results = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)] + + 'If nanchorrows + Index < current_results.T.Count Then + Last.Data = nanchorrows + Index + 1 + 'Else + ' Last.Data = Null + ' Endif + +End + +Public Sub ReportLabel50_Data(Index As Integer) + Dim value As Float + + 'value = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)].T[Index] + Last.Data = "kkkk" 'Module1.AdvFormat(value, 2) + +End + +Public Sub ReportLabel53_Data(Index As Integer) + Dim value As Float + 'Dim current_results As Results + + ' current_results = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)] + ' If nanchorrows + Index < current_results.T.Count Then + ' value = current_results.T[nanchorrows + Index] + Last.Data = "qqqqqq" 'Module1.AdvFormat(value, 2) + ' Else + ' Last.Data = Null + 'Endif + +End + diff --git a/comp/src/gb.report/.src/Tests/OutputReport2.report b/comp/src/gb.report/.src/Tests/OutputReport2.report new file mode 100644 index 00000000..f12422c8 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/OutputReport2.report @@ -0,0 +1,308 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,76,88) + Fixed = True + Padding = ReportPadding["Top:20mm;Bottom:10mm;Left:20mm;Right:20mm"] + Ignore = True + AutoResize = True + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(2,0,62,11) + Height = "25mm" + Fixed = True + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Bottom:2px #000000"] + { ReportImage1 ReportImage + #MoveScaled(1,1,18,9) + Width = "51mm" + AutoResize = True + } + { ReportLabel1 ReportLabel + #MoveScaled(21,1,10,9) + Width = "25mm" + AutoResize = True + Text = ("Version 1.0") + Alignment = Align.BottomLeft + } + { ReportLabel2 ReportLabel + #MoveScaled(31,1,30,9) + Expand = True + AutoResize = True + Text = ("Date") + Alignment = Align.BottomRight + UseField = True + } + } + { ReportVBox1 ReportVBox + #MoveScaled(2,11,62,9) + Height = "28.01mm" + Fixed = True + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Bottom:2px #000000"] + { ReportLabel3 ReportLabel + #MoveScaled(0,0,12,2) + Height = "6mm" + AutoResize = True + Text = ("Project Title:") + Alignment = Align.TopLeft + } + { ReportLabel4 ReportLabel + #MoveScaled(0,2,12,2) + Height = "6mm" + AutoResize = True + Text = ("Project No.:") + Alignment = Align.TopLeft + } + { ReportLabel5 ReportLabel + #MoveScaled(0,4,12,2) + Height = "6mm" + AutoResize = True + Text = ("Company:") + Alignment = Align.TopLeft + } + { ReportLabel6 ReportLabel + #MoveScaled(0,6,12,2) + Height = "6mm" + AutoResize = True + Text = ("Designer:") + Alignment = Align.TopLeft + } + } + { ReportHBox2 ReportHBox + #MoveScaled(2,20,62,3) + Height = "8mm" + Fixed = True + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Bottom:2px #000000"] + { ReportLabel7 ReportLabel + #MoveScaled(0,0,12,2) + Height = "6mm" + AutoResize = True + Text = ("Base Plate ID:") + Alignment = Align.TopLeft + } + } + { ReportPanel1 ReportPanel + #MoveScaled(2,25,67,52) + AutoResize = True + ForceNewPage = True + { ReportHBox3 ReportHBox + #MoveScaled(1,1,62,17) + Height = "61mm" + Expand = True + { ReportVBox3 ReportVBox + #MoveScaled(1,1,18,14) + AutoResize = True + { ReportHBox8 ReportHBox + #MoveScaled(1,2,15,4) + Width = "51mm" + Height = "12mm" + { ReportLabel9 ReportLabel + #MoveScaled(1,1,13,2) + Font = Font["+1"] + Expand = True + } + } + { ReportHBox9 ReportHBox + #MoveScaled(1,8,15,4) + Width = "51mm" + Height = "6mm" + { ReportLabel14 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + } + } + } + { ReportImage2 ReportImage + #MoveScaled(31,1,17,15) + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Expand = True + Alignment = Align.Center + } + } + { ReportVBox4 ReportVBox + #MoveScaled(1,20,62,14) + AutoResize = True + { ReportLabel19 ReportLabel + #MoveScaled(2,0,16,4) + Fixed = True + Font = Font["+1"] + Padding = ReportPadding["Top:3mm"] + AutoResize = True + Text = ("Bearing Pressue") + } + { ReportHBox4 ReportHBox + #MoveScaled(1,3,60,4) + Height = "6mm" + Fixed = True + { ReportLabel20 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Node #") + Alignment = Align.Center + } + { ReportLabel21 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Brg. Press., psi") + Alignment = Align.Center + } + { ReportLabel22 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel23 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Node #") + Alignment = Align.Center + } + { ReportLabel24 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Brg. Press., psi") + Alignment = Align.Center + } + } + { ReportHBox5 ReportHBox + #MoveScaled(1,8,60,4) + Height = "6mm" + { ReportLabel25 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel26 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel27 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel29 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel30 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + } + } + { ReportVBox6 ReportVBox + #MoveScaled(1,36,62,14) + AutoResize = True + { ReportLabel31 ReportLabel + #MoveScaled(2,0,19,4) + Fixed = True + Font = Font["+1"] + Padding = ReportPadding["Top:3mm"] + AutoResize = True + Text = ("Anchor Rod Tension") + } + { ReportHBox6 ReportHBox + #MoveScaled(1,3,60,4) + Height = "6mm" + Fixed = True + { ReportLabel44 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Rod #") + Alignment = Align.Center + } + { ReportLabel45 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Tension, lbs") + Alignment = Align.Center + } + { ReportLabel46 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel47 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Rod #") + Alignment = Align.Center + } + { ReportLabel48 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Tension, lbs") + Alignment = Align.Center + } + } + { ReportHBox7 ReportHBox + #MoveScaled(1,8,60,4) + Height = "6mm" + { ReportLabel49 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel50 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel51 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel52 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel53 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + } + } + } + { ReportPanel2 ReportPanel + #MoveScaled(2,77,69,1) + Fixed = True + Expand = True + } + { ReportHBox10 ReportHBox + #MoveScaled(2,79,62,4) + Height = "1cm" + Fixed = True + AutoResize = True + { ReportLabel8 ReportLabel + #MoveScaled(22,1,21,2) + Expand = True + AutoResize = True + Text = ("Page $PAGE of $NPAGE") + Alignment = Align.Bottom + UseField = True + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Paints.class b/comp/src/gb.report/.src/Tests/Paints.class new file mode 100644 index 00000000..01960f38 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Paints.class @@ -0,0 +1,33 @@ +' Gambas class file + +'Export +' +' Static Public Sub Begin(Device As Object) +' +' Debug +' Super.Begin(Device) +' +' End +' +' Static Public Sub End(Device As Object) +' +' Debug +' Super.End(Device) +' +' End +' +' ' +' Static Public Sub Rectangle(X As Float, Y As Float, W As Float, H As Float) +' +' Debug CStr(X); ", "; CStr(Y); ", "; CStr(W); ", "; CStr(H) +' Super.Rectangle(X, Y, W, H) +' +' End +' +' +' Static Public Sub Text(Text As String, Optional X As Float, Y As Float, Width As Float, Height As Float, Alignment As Integer) +' +' 'Debug Paint.Font.Size +' Super.Text(Text, X, Y, Width, Height, Alignment) +' +' End diff --git a/comp/src/gb.report/.src/Tests/Report11.class b/comp/src/gb.report/.src/Tests/Report11.class new file mode 100644 index 00000000..ddd552fa --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report11.class @@ -0,0 +1,29 @@ +' Gambas class file + +' 'Private hMap As New Map +' 'Private hImgCache As Image + Public Sub _new() + ReportDrawingArea1.BoxShadow.XOffset = "1mm" + ReportDrawingArea1.BoxShadow.YOffset = "1mm" + ReportDrawingArea1.BoxShadow.Color = Color.Gray + ReportDrawingArea1.BoxShadow.Blur = "1mm" +' ' +' ' hMap.AddTile("gg", "https://khms{s}.google.fr/kh/v={version}&src=app&x={x}&y={y}&z={z}&s=Galile", ["version": "145"]).SubDomains = ["0", "1", "2"] +' ' +' ' hmap.Zoom = 17 +' ' 'ReportPanel1.DataCount = 200 + End +' ' +' ' + Public Sub ReportDrawingArea1_Draw(width As Integer, Height As Integer, Index As Integer) +' ' +Draw.FillRect(0, 0, width, height, Color.White) +' ' 'Print width, Height +' ' 'hMap.Resize(Width, Height) +' ' 'If Paint.Device Is Printer Then Stop +' ' hMap.Center = MapPoint(Geo.SexToDec("45°31'33,33''N"), Geo.SexToDec("0°18'43,50''W")) +' ' 'hMap.Center = MapPoint(Geo.SexToDec(Str(CInt(Rnd(30, 45))) & "°31'33,33''N"), Geo.SexToDec("0°18'43,50''W")) +' ' 'If Not hImgCache Then +' ' hImgCache = hMap.Grab(hMap.Bounds, width / ReportUnits.DesktopScale, Height / ReportUnits.DesktopScale, 0, 18) +' ' Draw.Image(hImgCache, 0, 0, width, Height) +End diff --git a/comp/src/gb.report/.src/Tests/Report11.report b/comp/src/gb.report/.src/Tests/Report11.report new file mode 100644 index 00000000..5b63930c --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report11.report @@ -0,0 +1,29 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Padding = ReportPadding["Top:3cm;Bottom:3cm;Left:3cm;Right:3cm"] + Spacing = "2cm" + Index = 0 + Text = ("") + { ReportLabel1 ReportLabel + #MoveScaled(7,1,54,8) + Font = Font["Bold,+12"] + AutoResize = True + Text = ("C'est la maison !") + Alignment = Align.Center + } + { ReportPanel1 ReportPanel + #MoveScaled(6,12,53,31) + Padding = ReportPadding["Top:1cm;Bottom:1cm;Left:1cm;Right:1cm"] + AutoResize = True + { ReportDrawingArea1 ReportDrawingArea + #MoveScaled(5,6,52,30) + Width = "7mm" + Height = "6cm" + Border = ReportBorder["Top:2mm #000000;Bottom:2mm #000000;Left:2mm #000000;Right:2mm #000000;TopLeftCorner:1mm;TopRightCorner:1mm;BottomRightCorner:1mm;BottomLeftCorner:1mm"] + Cached = True + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Report41.class b/comp/src/gb.report/.src/Tests/Report41.class new file mode 100644 index 00000000..f19d0fdd --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report41.class @@ -0,0 +1,36 @@ +' Gambas class file + +Private hResult As Result +Private hResult2 As Result + + +Public Sub Report_Open() + Connections["MainConn"].Open + hResult = db.Exec("select * from parc_ilots") + ReportHBox1.DataCount = 3 'hResult.Count + Report.Debug = True +End + +Public Sub ReportLabel1_Data(Index As Integer) + + hResult.MoveTo(Index) + Last.Data = hResult!nom + +End + +Public Sub ReportHBox1_BeforeData() + hResult.MoveTo(ReportHBox1.DataIndex) + Print "Section : " & hResult!id & "/" & ReportHBox1.DataIndex + + hResult2 = db.Exec("select * from liaisonilotparc where id_ilot=&1", hResult!id) + ReportHBox2.DataCount = hResult2.Count + Print "DataCount : " & hResult2.Count +End + +Public Sub ReportLabel2_Data(Index As Integer) + + hResult2.MoveTo(Index) + Print " " & ReportHBox1.DataIndex & "/" & Index + Try Last.Data = hResult2!id_parc + 'If Error Then Stop +End diff --git a/comp/src/gb.report/.src/Tests/Report41.report b/comp/src/gb.report/.src/Tests/Report41.report new file mode 100644 index 00000000..f09d6cdd --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report41.report @@ -0,0 +1,34 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,109,64) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Spacing = "1cm" + Index = 0 + Text = ("") + { ReportVBox1 ReportVBox + #MoveScaled(4,5,101,53) + Spacing = "1cm" + { ReportHBox1 ReportHBox + #MoveScaled(3,4,94,35) + { ReportLabel1 ReportLabel + #MoveScaled(0,0,6,32) + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + Rotate = 90 + } + { ReportVBox2 ReportVBox + #MoveScaled(13,1,79,32) + Expand = True + { ReportHBox2 ReportHBox + #MoveScaled(1,1,77,4) + Tag = "*" + { ReportLabel2 ReportLabel + #MoveScaled(2,0,46,4) + Expand = True + } + } + } + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Report51.class b/comp/src/gb.report/.src/Tests/Report51.class new file mode 100644 index 00000000..a2dec1db --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report51.class @@ -0,0 +1,69 @@ +' Gambas class file + +Private hResult As Result +Private hResult2 As Result + Private hVBox As ReportVBox + +Public Sub Report_Open() + hVBox = New ReportVBox(ReportHBox1) + hVBox.Spacing = "1cm" + Connections["MainConn"].Open + hResult = db.Exec("select * from parc_ilots") + 'Report.Debug = True + 'ReportHBox1.BackGround = ReportBrush.Color(Color.Yellow) + 'hVBox.BackGround = ReportBrush.Color(Color.red) + hVBox.Width = "50%" + For Each hResult + + AddIlot(hResult!nom, hResult!id) + + Next + +End + + +Private Sub AddIlot(sIlot As String, iIlot As Integer) + Dim hHBox1 As New ReportHBox(hVBox) + Dim hLblIlot As New ReportLabel(hHBox1) + Dim hVBoxList As New ReportVBox(hHBox1) + Dim hHBoxLine As ReportHBox + Dim hLblParcelle As ReportLabel + Dim i As Integer + hLblIlot.Rotate = 90 + hVBoxList.Expand = True + + hVBoxList.Border.Top.Width = "0.1mm" +hVBoxList.Border.Right.Width = "0.1mm" +hHbox1.Border.Bottom.Width = "0.1mm" + hLblIlot.Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + + hLblIlot.Text = sIlot + hLblIlot.Border = ReportBorder["Top:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + hResult2 = db.Exec("select p.nom parcelle from liaisonilotparc lip left join parcelles p on p.id=lip.id_parc where id_ilot=&1 order by lip.ordre", iIlot) + + + + For Each hResult2 + hHBoxLine = New ReportHBox(hVBoxList) + hHBoxLine.Expand = True + 'hHBoxLine.Height = "5mm" + hLblIlot = New ReportLabel(hHBoxLine) + hHBoxLine.Border = ReportBorder["Bottom:0.1mm #000000;"] + hLblIlot.Text = hResult2!parcelle + hLblIlot.Padding.Left = "1mm" + hLblIlot.Expand = True + 'hHBoxLine.Expand = True + 'hLblIlot.Border = ReportBorder["Bottom:0.1mm #00000"] + For i = 0 To 3 + hLblIlot = New ReportLabel(hHBoxLine) + hLblIlot.Width = "5mm" + hLblIlot.Border = ReportBorder["Left:0.1mm #000000;"] + + Next + Next + + + + + +End diff --git a/comp/src/gb.report/.src/Tests/Report51.report b/comp/src/gb.report/.src/Tests/Report51.report new file mode 100644 index 00000000..d8c8fed1 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report51.report @@ -0,0 +1,15 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Spacing = "1cm" + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(0,2,62,57) + Height = "10cm" + Expand = True + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Report52.class b/comp/src/gb.report/.src/Tests/Report52.class new file mode 100644 index 00000000..a2dec1db --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report52.class @@ -0,0 +1,69 @@ +' Gambas class file + +Private hResult As Result +Private hResult2 As Result + Private hVBox As ReportVBox + +Public Sub Report_Open() + hVBox = New ReportVBox(ReportHBox1) + hVBox.Spacing = "1cm" + Connections["MainConn"].Open + hResult = db.Exec("select * from parc_ilots") + 'Report.Debug = True + 'ReportHBox1.BackGround = ReportBrush.Color(Color.Yellow) + 'hVBox.BackGround = ReportBrush.Color(Color.red) + hVBox.Width = "50%" + For Each hResult + + AddIlot(hResult!nom, hResult!id) + + Next + +End + + +Private Sub AddIlot(sIlot As String, iIlot As Integer) + Dim hHBox1 As New ReportHBox(hVBox) + Dim hLblIlot As New ReportLabel(hHBox1) + Dim hVBoxList As New ReportVBox(hHBox1) + Dim hHBoxLine As ReportHBox + Dim hLblParcelle As ReportLabel + Dim i As Integer + hLblIlot.Rotate = 90 + hVBoxList.Expand = True + + hVBoxList.Border.Top.Width = "0.1mm" +hVBoxList.Border.Right.Width = "0.1mm" +hHbox1.Border.Bottom.Width = "0.1mm" + hLblIlot.Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + + hLblIlot.Text = sIlot + hLblIlot.Border = ReportBorder["Top:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + hResult2 = db.Exec("select p.nom parcelle from liaisonilotparc lip left join parcelles p on p.id=lip.id_parc where id_ilot=&1 order by lip.ordre", iIlot) + + + + For Each hResult2 + hHBoxLine = New ReportHBox(hVBoxList) + hHBoxLine.Expand = True + 'hHBoxLine.Height = "5mm" + hLblIlot = New ReportLabel(hHBoxLine) + hHBoxLine.Border = ReportBorder["Bottom:0.1mm #000000;"] + hLblIlot.Text = hResult2!parcelle + hLblIlot.Padding.Left = "1mm" + hLblIlot.Expand = True + 'hHBoxLine.Expand = True + 'hLblIlot.Border = ReportBorder["Bottom:0.1mm #00000"] + For i = 0 To 3 + hLblIlot = New ReportLabel(hHBoxLine) + hLblIlot.Width = "5mm" + hLblIlot.Border = ReportBorder["Left:0.1mm #000000;"] + + Next + Next + + + + + +End diff --git a/comp/src/gb.report/.src/Tests/Report52.report b/comp/src/gb.report/.src/Tests/Report52.report new file mode 100644 index 00000000..d8c8fed1 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report52.report @@ -0,0 +1,15 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Spacing = "1cm" + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(0,2,62,57) + Height = "10cm" + Expand = True + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Report9.class b/comp/src/gb.report/.src/Tests/Report9.class new file mode 100644 index 00000000..083224ac --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report9.class @@ -0,0 +1,17 @@ +' Gambas class file + +Public Sub _new() + 'Report.Debug = True + ' Me.Width = "5cm" + ' Me.Height = "10cm" + ReportPanel1.DataCount = 100 +End + +Public Sub Report_Open() + +ReportPanel1.BoxShadow.XOffset = "1mm" +ReportPanel1.BoxShadow.YOffset = "1mm" + 'ReportPanel1.BoxShadow.Spread = "2mm" + ReportPanel1.BoxShadow.Blur = "4mm" + Me.Clear +End diff --git a/comp/src/gb.report/.src/Tests/Report9.report b/comp/src/gb.report/.src/Tests/Report9.report new file mode 100644 index 00000000..c797b67d --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Report9.report @@ -0,0 +1,22 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Index = 0 + Text = ("") + { ReportVPanel1 ReportVPanel + #MoveScaled(4,1,58,51) + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Expand = True + Spacing = "1cm" + { ReportPanel1 ReportPanel + #MoveScaled(12,6,15,16) + Width = "6cm" + Height = "6cm" + Background = ReportBrush["#FFFFFF"] + OnePiece = True + } + } + Index = 0 +} diff --git a/comp/src/gb.report/.src/Tests/Test.module b/comp/src/gb.report/.src/Tests/Test.module new file mode 100644 index 00000000..593c3ae9 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/Test.module @@ -0,0 +1,9 @@ +' Gambas module file + + Public Sub Main() +' + Dim hreport As New Report9 + Dim hprinter As New Printer + hprinter.OutputFile = User.Home &/ "sortie.pdf" + hreport.Print(hprinter) + End diff --git a/comp/src/gb.report/.src/Tests/rpTestShadowGrid.class b/comp/src/gb.report/.src/Tests/rpTestShadowGrid.class new file mode 100644 index 00000000..f3a08abe --- /dev/null +++ b/comp/src/gb.report/.src/Tests/rpTestShadowGrid.class @@ -0,0 +1,23 @@ +' Gambas class file + +Public Sub ReportDrawingArea1_Draw() + + Paint.MoveTo(0, 0) + Paint.LineTo(Paint.Width, Paint.Height) + Paint.Stroke + +End + +Public Sub Report_Open() + + ReportGridView1.Columns.Count = 3 + ReportGridView1.Rows.Count = 20 + + 'Report.Debug = True +End + +Public Sub rpTestShadow_Open() + + + +End diff --git a/comp/src/gb.report/.src/Tests/rpTestShadowGrid.report b/comp/src/gb.report/.src/Tests/rpTestShadowGrid.report new file mode 100644 index 00000000..502101d7 --- /dev/null +++ b/comp/src/gb.report/.src/Tests/rpTestShadowGrid.report @@ -0,0 +1,25 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Font = Font["+9"] + Padding = ReportPadding["Top:6cm;Bottom:6cm;Left:6cm;Right:6cm"] + Spacing = "1cm" + Index = 0 + Text = ("") + { ReportLabel1 ReportLabel + #MoveScaled(3,2,58,9) + Height = "7cm" + Font = Font["Georgia,+12"] + AutoResize = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Text = ("coucou") + Rotate = -1 + } + { ReportGridView1 ReportGridView + #MoveScaled(5,18,52,30) + Height = "5cm" + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + } + Index = 0 +} diff --git a/comp/src/gb.report/ChangeLog b/comp/src/gb.report/ChangeLog new file mode 100644 index 00000000..d2decfd1 --- /dev/null +++ b/comp/src/gb.report/ChangeLog @@ -0,0 +1,6 @@ +[GB.REPORT] +* NEW: Negative numbers are allowed on string size properties. It allow to use + negative value on XOffset or YOffset for shadows. + +* BUG: Resolve the bug on clear (Even if i don't remember for what i've done this property) + diff --git a/comp/src/gb.report/gambas.svg b/comp/src/gb.report/gambas.svg new file mode 100644 index 00000000..5bfafe4d --- /dev/null +++ b/comp/src/gb.report/gambas.svg @@ -0,0 +1,540 @@ + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Gambas 3 + 15/09/2008 + + + Fabien Bodard + + + + + Common Creative + + + + + Fabien Bodard + + + gbFBodard150908 + http://gambas.sf.net + + + gambas3 logo basic shrimp + + + A pretty shrimp, a malicious new gambas logo... we got the power ! + + + Fabien Bodard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/comp/src/gb.report/img/16/red-arrow-h.png b/comp/src/gb.report/img/16/red-arrow-h.png new file mode 100644 index 0000000000000000000000000000000000000000..9ad41c2e596c8b409b5160f89452256327e10a0a GIT binary patch literal 117 zcmeAS@N?(olHy`uVBq!ia0vp^93afZ3?z3ZhDiV^&H$ef*Z)9JU;m$hA?NbP6+l78 zk|4ie28U-i(tsQ(PZ!4!j_b(+i3t-@5+)QSOqj*s#;353gPB3bkttbHsl@}Riow&> K&t;ucLK6VBDIaYB literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/16/red-arrow-v.png b/comp/src/gb.report/img/16/red-arrow-v.png new file mode 100644 index 0000000000000000000000000000000000000000..a787fe89c60ce899a23198ffa582b5bc1540384d GIT binary patch literal 120 zcmeAS@N?(olHy`uVBq!ia0vp^93afZ3?z3ZhDiV^&H$ef*Z)9JU;m$hA?NbP6+l78 zk|4ie28U-i(tsRUPZ!4!j_b(@2?-p$sVAaNDr{|O%w=L;%*+flz<{4Q_>%I`W}rd_ MPgg&ebxsLQ04Dk&EdT%j literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/22/FullWidth.png b/comp/src/gb.report/img/22/FullWidth.png new file mode 100644 index 0000000000000000000000000000000000000000..7882c75930df17079b07d001e364a110e5365cb6 GIT binary patch literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H0wnYHF4+L2f;?RuLp07`y?Bs|L4kw$g6^;X z@!PyzZY;40mHVo=t-(dGLG_KI*@BdFKjhc%jA$usSynLT+l-Lw9oveJz3C~^{wwJc nk{lEgeEII>IaQx_uYbs zFl1V%NP3T!Twqt^hg1L5T@7_ICrx-;9Oc@wv|K?C>pBwQW PXbyv?tDnm{r-UW|Mdc*X literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/22/RealSize.png b/comp/src/gb.report/img/22/RealSize.png new file mode 100644 index 0000000000000000000000000000000000000000..f93d1289b4a9f944672e452e305930159a57637c GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H0wnYHF4+L2CVRR#hG?8mPEcSpFfuUsqwc6X zvFpFFfx*N_JhwE{8ZYJWY~l&xsh8+eKA9+-{NwzA0|LhjraTlrTq@EWaP*(DPgsh@ zkq=#t%&vxJh7}h|7tQR^YBPTEFPXo3lM}P3L6G7c&m!Y9l5#v%JZU_Kcv5-pOR7B< y1e;{g{?J}zUz>D`);D?6IUk!3PImDO;$eteYM}V|B)=EXc?_PeelF{r5}E*|>qzAQ literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/22/TwoPage.png b/comp/src/gb.report/img/22/TwoPage.png new file mode 100644 index 0000000000000000000000000000000000000000..306e7cee92a4e4f56325c4578c0a91fc034eba0e GIT binary patch literal 121 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H0wnYHF4+L296VhdLp07OCnzu)82ve}b*dp$ zhWkgq%OVD~pgD%CDqJ+v9Fv57(seX!-2TimnEO$FRV&BQiYZCYMBG$zOIR2_SaK=7 TmCRcUG>gI0)z4*}Q$iB}hN~o7 literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/32/Collatecopie.png b/comp/src/gb.report/img/32/Collatecopie.png new file mode 100644 index 0000000000000000000000000000000000000000..3c0a284961fe7362bd9bc98c0020bdb460f4f017 GIT binary patch literal 449 zcmV;y0Y3hTP)2wR%gW*5 z;m*v<k@N&FAOl=;!C_>gww0>FMd{?Ca~&(9iAc>+I|5>gnjt%*^fV?BCwq zy1BU0(b3M$&BMRG?(OW*&(Gf7-N(km?(Oa7=H z>F3-1oB03$010$bPE!E*#1F|8st?Je;|_TM006~FL_t(I%e|4=4uU`oMeBkJjE-0x z#SPr|1=RolHB)d52~GOOdF{=4=mFRDd|#<-`RMX?UVk;SkM#IZ%O z4#bH?u@0@na5~G-V|#e~wn0`V09%$`4Y0kn)3+AEf*s rgwz3>g?<5?Ck68?CkCA?CtIC?(OaF?(OgH?eFjI@bB*P^6{yq zq~YM<@9*!aq@v>D;_&b9>+9>|vw6n6|;NR5K)62@r%gf5v*xI9_pv}j|@bK`#z`(JvugS>A!NI`C#>d&% z*xcLOpr4-2$HmUc$ETv8>gnj*+uFdszw`3)-rU@to}BOP?aj-~#KXgzoSDwY#ipX5 z!ok7T*4EwJ+?<@6rlFwL)z!eizuVi|ot&G~)6>z^)}Nl8&Bn#2p`h~c@bvTY>FMjz z)z_n;pSrZNrl6m#r=`rs#BKCF>;M1&0d!JMQvg8b*k%9#0We8KK~y-)jnipUfC*1|J}gbU@9e(o`@c+T7l9ti-wdyw|L!n5^Ds;Ud`%vFB=ECb zfDeX3LbE8gNRlMWa@Z8{lOW2qg4&`ahy~&xB}Rkx4iK^ez7xdKKuvUkSd|8X2GVJe z6uLo?BneXK9#C(J2AMvPP6F(G1OA?4?;9{S=YRV@s1>ThgR(0A`3WQJ5g3@fWPcEp zr7vj=BtV0P6$qdqdc&~tdzjPm`H@lj)JocJYM002ovPDHLkV1nd{ BwRHdh literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/32/grayscale.png b/comp/src/gb.report/img/32/grayscale.png new file mode 100644 index 0000000000000000000000000000000000000000..670e944d6aeb111916160f2bdf358937d265d71c GIT binary patch literal 1564 zcmV+%2IKjOP)gwz3>g?<5?Ck68?CkCA?CtIC?(OaF?(OgH?eFjI@bB*P^6{yq zq~YM<;^N}tlq(=H=(+<>%+-=;!C?=;-O_=;`U{>gnm~>gn(A@2RAs zgn+B@9XR9cq)=I7_<;NRc5xwzoq;Ogq?@$m22+1RqNvAnyx$j8U6 ztgOk&$+0p?K?W&CR>Ix#HpB!ok4M&(8Al@$T*HxVN{O zn3(0`K_n-QDQt=GfQQ?CR>&)6=G-q0P$2hKBL+ z@bmKW^z-xM;^NlU)%ErC>ged0mX?EpfrNvDtgEWy;o)P4ay1BW>#>MjS@R^yI zg@l8KgoNVY;G?3U^YQV#ySm-o+|SOFMjz)z_n;pSrZNrl6m#r=`rs#6oTBNB{r;0d!JMQvg8b*k%9#0@+DKK~y-)V_+Zx zFf!o)j8Fy4EUawo9Gsk7+&nzIe0+TT{QLqSAjk|=AjB#xA}S^>At@y-BO@y-Coiv{ z2m(q%PzB0tBBJ6dl9C{4Igq3(8yKi5!xX4%h-*q}X=%&K>FDa}>9eYU&gJ1@v4$z&0?E6% zJ2-fFdTDZb`{?@efq)-Ofj^h6bUaL6nuWU35%L ztfyC8d_rQ9rJ`DLN=mAML0S|{LAs1=MogwlR(4KqUR-j1N`XvDN}+*)A51}!m7SbK zv9o7hLJ2Sw)$;QB)TF}s3=9N|pbAP^*viTic`GV|tJ120Kp|0~rnXM8roI$r0#8F@ zQ*%jpORIpIni>mBTRWGkPe*4L+yrYiw(g$ZzVQCOuCA^L6DDv4bFmvwG^we9hl3jX zr1Hdw$y26+q^ql?rb*Q#Pgkv(0aGwjfZbAN)@%dzq&a?m)zva{=gqe$n>S;|jF~VK z7I365T(r1dYDrQ+9Q)E`#>=GwSF8jofSa(&a`l?EYuC+NzkI{SO`BJ3*}AQzW&8a3 zH8bG~xOjH#+|06Tx1dX$<+d7`ZGL<9*7EV~gPE{jjf*EjjV0qiO$J-dhx!kWdnDgK^JYN76M1T~40%`vI3CnFB zJ~Ei|_z5E8JT>6i0#r~13Z?n;t5{v0J%8~Mp`U@_6<7fa3q%1+-0L@Q-yzF`6$pS8 z2nx>MY7qAR!^cl3dGm8M%RG>7kpB7e{l0wthL(B1|3C#le-V@AVE_QtsRkb4V6q1Q O0000fzww@9*!fs;R=gys4z3>+9;`;^Oe{@9^*L^YZbkrljlZ>*M3&@$m2L>+9s? zvw6n6|;NR5K)62@r%gf5v*xI9_pv}j|@bK`#z`(JvugS>A!NI`C#>d&% z*xcLOpr4-2$HmUc$ETv8>gnj*+uFdszw`3)-rU@to}BOP?aj-~#KXgzoSDwY#ipX5 z!ok7T*4EwJ+?<@6rlFwL)z!eizuVi|ot&HL=;zbZ)6vw{pPrq~#>JFd(f*Q24Iy0o&Upr5U$rOd{}5gA^>00001bW%=J06^y0W&i*Il1W5CR5;6Z(^pf2 zKoo}I6E%W}N@Njpk4$I3fxT zOskh$!bC)+13Q^ag-Rw36A_gFI956x9;95^LBzm8Mis;1BAY=(j-!yvbHas_mYsx& zuPKQl*C1jxW3WMt7Y_2WBRc?I>pufe^hq{@LSF$(xrMs$8AvQ3Oz77%4PQ&E4bOcq z7I;3P16}8Q2YrAP0utb0P~r`ShK7rp?*$%yYRR%(yoFV5WOQtNVsdJFW_At{Dwy-O z?Yb$~t|l!kE-kODuC13g5b;siRP5T6t?iw%QrRsrWPpSQd)@m7O6BmViVPP4j+I<( z4cjNDXXhn`Py^tiez0|Ub$wG}2s1*1+q?URM}|;?{ufVW_1Q4!^@16%Z|_xv8uT1K yKI{AXW(YOlw=lwtzjF>k4X7<_Kz(83_v8o7|B&yS(h6z-0000|_zC&mB( literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/control/label.png b/comp/src/gb.report/img/control/label.png new file mode 100644 index 0000000000000000000000000000000000000000..7c73e49ff4bcec6c259224ba4ec6ff89e9334bce GIT binary patch literal 466 zcmV;@0WJQCP)kdg0004*NklbO=!sJ`Fy-_?LYs(;F zv-|z&tegtobNcGv%xwNs1${L*U^*-LU_tD#H@aUfEujjGF%iA#vJy^c5x@D}iiq8o zl<-Epr^urinzt0e9r2wavoZ9f48a$%-Cu|t(-|U(1RFp>9-dKoBy+Y-k)WX;=Gi{4WH50LgV@+X?A6xc~qF07*qo IM6N<$f?43scmMzZ literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/control/picturebox.png b/comp/src/gb.report/img/control/picturebox.png new file mode 100644 index 0000000000000000000000000000000000000000..a25a9f4e302f5e8d2abcf6f1d3264f06d8f9c40a GIT binary patch literal 2381 zcmV-T39|NyP) zX>e8L8OML`S?<1Mzgccb2q7$ifNX*h5T&3fTSZ(^sBJ|pR;RT(>P#()%UD-Ns$&a+ zD9U04WKmEe2qI!sAYn@aArQ#Uy~(|~+c|GPARyItYCHYVY5$+jGv|4q`JMm!Jnwtp ze_i6AP0a86upWzJwb|0UYWLo2{Ko+@a!0D+P~a*FHwdAXf|lF+iigyLNuXq;)rpU>_$qpzab0EtjkRLy~AaiUqA=vEiHJ(vB@eXKO)2}g0dn$3h}M>)^AO^}>>ec|J-3KZr-`nGks^suG!-EfRNaAQbz!l2 zvDlNT+Poe`AGX9|@!!!{T^jk90j!R+m-73Kn3LCc7%5r#sH%>X28ycF)fu3^x*j3o z1cG)XHZ+?Xi_MGGnSs@j%AO6o7Pr^mG4s1M?|bW{rRikIIdo`x=-yG@*!KW#ynZ$U z!K$^(XKK2-Xv&)%I`m98j)Wrs-CW>&JzfwUbiI0b(<@ssFr|kcUFH^Hm3?e6b+t8^RWY3!iXH1 zde8HIMNu(L6QL*wArL}2Ln_XgWf%#X8yZm*gH&G@LQ$|-Y-pN7vfD*|kIYERvHGaf zVOz1{y+e<0TQ;NaZ@+rT_<3nIcXFoP<36gX-ee3ZFPr?U=lnuZ=?r#KU3HX#UfI|j z4ouU;YO|xL+8GEb@%gjQ6_e5=($#@OM38O=9fs4^77r)p{e03jE>CiF_~iK;z3I7| zKlt;MbG@e6k5?gfR?3WyKMkE*-nm zi4Y(Z6(JP9H3TUQ!ofDSuUpK%ogZMaB~f(I2up@P)wN^&Du4a{b+fYy#`NiP$&8Mg zij|485W|LC$%(41amDL)A|;cmzOJG2i?8VjhVXbTcvCzyH-<4ylVE#0rjfuf66dz! zYzs>%sjJ*U?Vhb1sr-!1OCP5*5a;sg_mYzBZwB~uYY0qPgNNW!in1-Lq+X{Kq;1#X`(>;CiJBfs+~&qo5h$!TmV zm*1pM95K0khEn&`6Z2X6_8Q_24}yS_rn^CN?uxZ~s)Mq(!LQ;-8E%(`E>ush@hkr0AS%in9adip$+IezxS zZ4WzR0HScvbiE^RGI+%eciKK&JnvTD#W%g7_v?l143VfkhGkHH%utA?s-1*ttI&sB zgi&3KG!2BVqeNmjJEF;ncxZ^jo|b;a^{ zX5pxvs;XV4DC$>gB90m8K(X7fjU5T9ijtN>N@EkYhE9}y0i-jN_V&Zb+)VTX4TMvy z?W#R}vuU+#=HddRreM|`C&NDgSMfmNt<8w{-(c|5FR*Y_-UZHlYnxaN;7ToN_SGLl z358Je{GgtJPn>SYHgpj7yzZEJDFkFIGS!Py^^oz4yAXWLy3NGajRU*{cXm zCD9SY;>*I5Q-HH~3D(RUVh!~uQb9{e#q3I8A2SlWY2yB-l0Y;-%la}r!%DICAIjbp zuQ74$Vb)IUc@97Y=xD78+dV!%cujqDpMB#CdvI#omqhkdA&#HK5;D-b4BXv|Nf|x{ z=fELoZVw1S2wBzOq+NG6_aOBKBn+Iz{J^aiKCU3dvBXP zt$6jjn>lv0j$FT+qM{r;N$G5O_w9ZMzbvZ+`1Sn799+JJXvjk#GaGqP2}-sPRF%Z% z73A*QODGZ}>~_-`Gf2XqTap8JzQ)E4Tj5{b| zCC!Id#!^y~Z#i*f&kFv5Mn3a8iD-b+P4%SuN(e?<*)#XP3vvcMy$C(KJFe_*d~`*@ z4;LCR`qAs>zFNL+==8Ev112qzj-*~6^CL9vxn8 zdqd>+zI3?PH>Q`zH)*3gefT9m2H^ktuhU-vP5hv;;=4p&00000NkvXXu0mjfecGPf literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/control/vbox.png b/comp/src/gb.report/img/control/vbox.png new file mode 100644 index 0000000000000000000000000000000000000000..07004bb15185d11b45ea7631afd59b025b5d220b GIT binary patch literal 250 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvp#Yx{S0MfW|NjFA4m310fY=NS zB|fH8fGn1hAirQB&9HfL+AJV7+0(@_MC1I@iH>}S6?j;Dm)zUT{w_h2{{{c2*059m z_g^wqIT)}+ZkNn~(-|8#ylH;oZEZD?B!eZmjZzx<~rnEmg45%mS+Oix!omvv4FO#p&DT9p6* literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/control/vpanel.png b/comp/src/gb.report/img/control/vpanel.png new file mode 100644 index 0000000000000000000000000000000000000000..64178f994a00f5e6077488054dcee92106a1b5bc GIT binary patch literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4*Z=?j1DOpC4gZ0Nf#KDH zIr2b0b4ie2Fp&OLcH|zAO7(Pc4AD5BY~aSq#?#hhpunKiz{KOxFi}vYVTn_Npf4+P zz=4j&qpTbYR9sjmJY`T-WODFi)8I5^Uc%tibe6%Zi8m#&VYS1?)(s4+tR*hoJ=pM$ pk8RhEWQD`bz6{FWSOXgw7}nlVFYj8dc^haUgQu&X%Q~loCIFnAJpcdz literal 0 HcmV?d00001 diff --git a/comp/src/gb.report/img/logo.svg b/comp/src/gb.report/img/logo.svg new file mode 100644 index 00000000..31e20aa0 --- /dev/null +++ b/comp/src/gb.report/img/logo.svg @@ -0,0 +1,540 @@ + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Gambas 3 + 15/09/2008 + + + Fabien Bodard + + + + + Common Creative + + + + + Fabien Bodard + + + gbFBodard150908 + http://gambas.sf.net + + + gambas3 logo basic shrimp + + + A pretty shrimp, a malicious new gambas logo... we got the power ! + + + Fabien Bodard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/comp/src/gb.report/printer1.png b/comp/src/gb.report/printer1.png new file mode 100644 index 0000000000000000000000000000000000000000..a6ceca7f115a77b989b77315c9d62abff9dbeaf5 GIT binary patch literal 1630 zcmV-k2BGxclxJUNPw!?gxu z3`(i35#akiTI+Sf4N7Ya{Qm{44xf9*7?cXPNw_z$=LN_^hYo#bQxQ0R{P=Ug zKBd%~FTcF^l-3%fSr=S~a}f~2igk8&eiDnte(Sn!FVJ`S^5xPK1)Ml>qFpKVy8{Of zy>{Thw?$*)Gq|qH)vJHS7>#Cq@VOL#=Xt#GMvwK@TW`Oc&1T;n9==6eTl@VRHwHd( zUH8|$y}geX1ax(zkcS-PwYs8SfA09{>2J5#AtgEYpxR$E&`B(icr zDGjwXh3%YC6Xt5qFE*I2EtJ^L!$XAag&D-847t4j=B4AAb13 z5h;XtCK`=mE$OSZ!Sl+@%uG{qU*L9r&gq44J7-AGT1yW3)gm`%CAAaBiK3LIwI)ex zO>)7PNbJD2?G~9xB_5d)m-rhu2m5bH}%d!~FZ>O&x9uzhX2Qav#MhhHkND_$%d_QDz zGRxT56zTK`mSshyl+i~3p68QF4FeF5S5Q}1&5ynn=eglDV;RLn&M;GokPmE%%BG}k z%ElprB4(ATi1<`WpKZ2Jt!qeb7c^B1UTAG(WMq=5sT^UrSR+U&QA*9oQmHTziHN<{ zl3D0^KKJfDpb|J%2i8t-T?f~V5s8$DLDppb9^2#fn%ifjCC4wM$D1pf!DB(DZ zo6Wb#|d2rs>~f5BN>o8ait*GVRuNG6k; zVPgz5H#c+h<}mg3wZ!8NvDlmdr9wtVZrz=pp1vn9Uc7kMaVq|E>eLV0<~NMpyZ2CC zT~9KZAepS$6kI8d5Ee&{bko>akCcM>>V9zW2IJ%7e* za+`#kuUCU22ts!6Zee8P9$=BpW*8j2!Ka`0{_Fbn>%RfiLUQBrxO4E}!JkSgcQrIL zwZHSuFOTitzh@J0z2s6&?F@xAvx_*E`f zFdH$gwF!dIc%E+x1<&Mi1(V6-O`+hKYuEl^y1Kf*zZz@RqV(Bk8-7+*k4UI-V|$As)w*6C_wu8r%MJ{|jC+ z_hCKrPqv;c`KxxTYD0dnC{sGXbZSd~1_Q(Su-MoV91DwxhzLJFzkq-MUteEuZ*L&!@9z&JfdI%22?>F)+uGWuOqr6Em38~}?f*c~ z*cjpDG?kTgXJ+Q_FJHo4TsDV<++Dfy`Sa&pU0to0H$4G*)wU$aFPLGm)Z|HfeSG(v zJ3nRK{0$t8PDQU*$pzjMe0u$^zrgw%Yi@fx7rtC8AM)VI)H!oEa$fxKSowwKOD*kJ zIzNppQsPzs4ch7H;uxZFJ~^R*(JgvS1EXi-TwAsRhrkCfS{UE3w==1wiK*@3inw{h z@`Kicl!OO|RyWLUpU$sxzTdmCVZnqA9U9FuEac)nJiIzRPBVyFSX(Yu>|lzttYmdn zjp1UM5*l*#ilm9j)|d^^3)e1Q&2Hp&|G?F=vir?#ZT4F_JvwT9p<^=w% z98Ip-OwLSPJv|?_4GdEctUhpN4&!*1f;M=;-Ll$;tKg_0`qY<>lqs+1cOU z-wSa8>Hq)$0d!JMQvg8b*k%9#0ES6KK~y-)t&{5xfFKBkLq)Uf`u-2w$k=R&vABA^yn%ahz-Zv@^-ut^c%HpOl7ptwrp!KR-KdVs( XSmOv)KHU<>00000NkvXXu0mjf+iQLg literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/.hidden/control/reporttextlabel.png b/comp/src/gb.report2/.hidden/control/reporttextlabel.png new file mode 120000 index 00000000..2fec7698 --- /dev/null +++ b/comp/src/gb.report2/.hidden/control/reporttextlabel.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/textlabel.png \ No newline at end of file diff --git a/comp/src/gb.report2/.hidden/control/reportvbox.png b/comp/src/gb.report2/.hidden/control/reportvbox.png new file mode 120000 index 00000000..50d7662d --- /dev/null +++ b/comp/src/gb.report2/.hidden/control/reportvbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/vbox.png \ No newline at end of file diff --git a/comp/src/gb.report2/.hidden/control/reportview.png b/comp/src/gb.report2/.hidden/control/reportview.png new file mode 100644 index 0000000000000000000000000000000000000000..2277da00516e1eb44ec56cdc6d418daff8e08855 GIT binary patch literal 233 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 literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/.lang/es.po b/comp/src/gb.report2/.lang/es.po new file mode 100644 index 00000000..b1d24335 --- /dev/null +++ b/comp/src/gb.report2/.lang/es.po @@ -0,0 +1,343 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report2 3.8.90\n" +"PO-Revision-Date: 2015-09-20 17:58 UTC\n" +"Last-Translator: Jesus \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "ReportsEvolution" +msgstr "" + +#: FPreview.form:79 +msgid "Report preview" +msgstr "Vista previa de reporte" + +#: FPreview.form:86 +msgid "Print to file" +msgstr "" + +#: FPreview.form:115 +msgid "Printing..." +msgstr "Impresión..." + +#: FPreview.form:145 +msgid "One page" +msgstr "Una página" + +#: FPreview.form:154 +msgid "Two pages" +msgstr "Dos páginas" + +#: FPreview.form:163 +msgid "Full width" +msgstr "Ajustar ancho" + +#: FPreview.form:172 +msgid "Real size" +msgstr "Tamaño real" + +#: FPreview.form:180 +msgid "Zoom out" +msgstr "" + +#: FPreview.form:193 +msgid "100 %" +msgstr "" + +#: FPreview.form:199 +msgid "Zoom in" +msgstr "" + +#: FPreview.form:208 +msgid "Print" +msgstr "Imprimir" + +#: FPreview.form:216 +msgid "Show options" +msgstr "" + +#: FPreview.form:239 +msgid "Printer" +msgstr "Impresora" + +#: FPreview.form:264 +msgid "Recto Verso" +msgstr "" + +#: FPreview.form:271 +msgid "None" +msgstr "Ninguno" + +#: FPreview.form:271 +msgid "Short side" +msgstr "Lado corto" + +#: FPreview.form:271 +msgid "Long side" +msgstr "Lado largo" + +#: FPreview.form:275 +msgid "File" +msgstr "Archivo" + +#: FPreview.form:301 +msgid "Resolution" +msgstr "" + +#: FPreview.form:308 +msgid "75" +msgstr "" + +#: FPreview.form:308 +msgid "150" +msgstr "" + +#: FPreview.form:308 +msgid "300" +msgstr "" + +#: FPreview.form:308 +msgid "600" +msgstr "" + +#: FPreview.form:308 +msgid "1200" +msgstr "" + +#: FPreview.form:313 +msgid "dpi" +msgstr "" + +#: FPreview.form:336 +msgid "Range" +msgstr "Rango" + +#: FPreview.form:352 +msgid "Copies" +msgstr "Copias" + +#: FPreview.form:372 +msgid "Orientation" +msgstr "Orientación" + +#: FPreview.form:379 +msgid "Portrait" +msgstr "Retrato" + +#: FPreview.form:379 +msgid "Landscape" +msgstr "Paisaje" + +#: FPreview.form:389 +msgid "Paper" +msgstr "Papel" + +#: FPreview.form:396 +msgid "Custom" +msgstr "" + +#: FPreview.form:396 +msgid "A3" +msgstr "-" + +#: FPreview.form:396 +msgid "A4" +msgstr "-" + +#: FPreview.form:396 +msgid "A5" +msgstr "-" + +#: FPreview.form:396 +msgid "B5" +msgstr "-" + +#: FPreview.form:396 +msgid "Letter" +msgstr "-" + +#: FPreview.form:396 +msgid "Executive" +msgstr "-" + +#: FPreview.form:396 +msgid "Legal" +msgstr "-" + +#: FPreview.form:411 +msgid "Width" +msgstr "Ancho" + +#: FPreview.form:422 +msgid "mm" +msgstr "-" + +#: FPreview.form:432 +msgid "Height" +msgstr "Altura" + +#: FPreview.form:455 +msgid "Grayscale" +msgstr "" + +#: FPreview.form:461 +msgid "Full page" +msgstr "" + +#: FPreview.form:466 +msgid "Collate copies" +msgstr "" + +#: FPreview.form:471 +msgid "Reverse order" +msgstr "Orden inverso" + +#: FPreview.class:211 +msgid "PDF files" +msgstr "" + +#: FPreview.class:211 +msgid "Postscript files" +msgstr "" + +#: FPreview.class:226 +msgid "Cancel" +msgstr "Cancelar" + +#: FPreview.class:316 +msgid "This file already exists.\nDo you want to replace it?" +msgstr "" + +#: FPreview.class:316 +msgid "Replace" +msgstr "" + +#: FPreview.class:341 +msgid "Layout..." +msgstr "Disposición..." + +#: OutputReport2.report:77 +msgid "Version 1.0" +msgstr "-" + +#: OutputReport2.report:84 Report51.report:60 +msgid "Date" +msgstr "Fecha" + +#: OutputReport2.report:99 +msgid "Project Title:" +msgstr "" + +#: OutputReport2.report:106 +msgid "Project No.:" +msgstr "" + +#: OutputReport2.report:113 +msgid "Company:" +msgstr "" + +#: OutputReport2.report:120 +msgid "Designer:" +msgstr "" + +#: OutputReport2.report:134 +msgid "Base Plate ID:" +msgstr "" + +#: OutputReport2.report:185 +msgid "Bearing Pressue" +msgstr "" + +#: OutputReport2.report:195 +msgid "Node #" +msgstr "" + +#: OutputReport2.report:202 +msgid "Brg. Press., psi" +msgstr "" + +#: OutputReport2.report:266 +msgid "Anchor Rod Tension" +msgstr "" + +#: OutputReport2.report:276 +msgid "Rod #" +msgstr "" + +#: OutputReport2.report:283 +msgid "Tension, lbs" +msgstr "" + +#: OutputReport2.report:353 +msgid "Page $PAGE of $NPAGE" +msgstr "Página $PAGE de $NPAGE" + +#: Report.class:112 +msgid "Section " +msgstr "Sección" + +#: Report1.report:28 +msgid "toto" +msgstr "" + +#: Report11.report:19 +msgid "Section 1" +msgstr "" + +#: Report11.report:39 +msgid "Gambas" +msgstr "-" + +#: Report11.report:59 +msgid "All friends list !" +msgstr "" + +#: Report11.report:81 +msgid "Gambas Report Demo" +msgstr "" + +#: Report11.report:93 +msgid "DEMO" +msgstr "" + +#: Report11.report:106 +msgid "Page $PAGE on $NPAGE " +msgstr "" + +#: Report2.report:19 +msgid "ReportLabel1" +msgstr "" + +#: Report3.report:17 +msgid "PARCELLAIRE" +msgstr "" + +#: Report5.report:20 +msgid "Coucou" +msgstr "" + +#: Report51.report:30 +msgid "Recto" +msgstr "" + +#: Report51.report:38 +msgid "Verso" +msgstr "" + +#: Report51.report:51 +msgid "N°" +msgstr "" + +#: Report51.report:99 +msgid "Observations" +msgstr "" + +#: Report6.report:12 +msgid "Testtest

    \n\n
    " +msgstr "" + diff --git a/comp/src/gb.report2/.lang/es_ES.mo b/comp/src/gb.report2/.lang/es_ES.mo new file mode 100644 index 0000000000000000000000000000000000000000..670b87b998fd5155437ab5f2381c6999d6a28b16 GIT binary patch literal 1675 zcmZ9KJ#1V>5XT1y0WLsD_zdA=0Y^wk?>IYHc8(9_@STmLvvab~Mkqqb`n~nLJ>Km; zcK6wFI*SG>1QHSj1<=s}p`b&^1r-%NLR2&qR8Rr`xm(MKy_@&jot@d4*`1$HANzr@?Z~CbOW<+ruSE92V_07S?*gyI^$@IKZ9%rb5xEbt z-Mf(=g12M+N!c0T_+?(K?;J4rj@O$t9@JEpSevSLT#q}TH9nk*?vi(0%e*%Yb z+*9Da;92k<@Hvprp9eYL7RYv8ko`A7jydt^~RM zA&5V{jSt)Jf}H0b$a(BX{re!>e-QUS22W!BY25!Z@@tUu{RZT7z5_YmAHci8pTLK~ zUqJlnFMQbUZ;*jG5!ZqZA^z@q2`5;hNn#`xNRG97AD%9(B>Z)88 z_=cbyb*;&T*O{VKYZP5JN|Z9k#A#)M4B8s%K_RAkt)-5z+hS^!XHb@K?^zd|)PZ_V zoA_llue8ZI+?q5vZ%xSpac=Oegp(+p+Ru7*T>b4Mu=sH@Jg1mB$R2TmCjKz$hNj%O~8F7{k;-aL0u2emxa{kMUf69$`3DZ^cR!) zBRfuETe+lTQk&r>n__-ThZ-gEH|YuK#$AE;UQ{>;do7m^^p2(0u<}7Vfz}yV))%32 z60fGc94cySUs|tIeQ?w8Bk{3anORXtms?sz#L8IOFjABUpXJ)f!;TocP;a7)U6G+Z zj=G}2o1!U#rR7oS^hmq$M0tw@Sf%`7Sndr=eL0c`Us>VMBebcJ_oO?dz`>Ju\n" +"Language: es_ES\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "ReportsEvolution" +msgstr "" + +#: FPreview.form:79 +msgid "Report preview" +msgstr "Vista previa de reporte" + +#: FPreview.form:86 +msgid "Print to file" +msgstr "" + +#: FPreview.form:115 +msgid "Printing..." +msgstr "Impresión..." + +#: FPreview.form:145 +msgid "One page" +msgstr "Una página" + +#: FPreview.form:154 +msgid "Two pages" +msgstr "Dos páginas" + +#: FPreview.form:163 +msgid "Full width" +msgstr "Ajustar ancho" + +#: FPreview.form:172 +msgid "Real size" +msgstr "Tamaño real" + +#: FPreview.form:180 +msgid "Zoom out" +msgstr "" + +#: FPreview.form:193 +msgid "100 %" +msgstr "" + +#: FPreview.form:199 +msgid "Zoom in" +msgstr "" + +#: FPreview.form:208 +msgid "Print" +msgstr "Imprimir" + +#: FPreview.form:216 +msgid "Show options" +msgstr "" + +#: FPreview.form:239 +msgid "Printer" +msgstr "Impresora" + +#: FPreview.form:264 +msgid "Recto Verso" +msgstr "" + +#: FPreview.form:271 +msgid "None" +msgstr "Ninguno" + +#: FPreview.form:271 +msgid "Short side" +msgstr "Lado corto" + +#: FPreview.form:271 +msgid "Long side" +msgstr "Lado largo" + +#: FPreview.form:275 +msgid "File" +msgstr "Archivo" + +#: FPreview.form:301 +msgid "Resolution" +msgstr "" + +#: FPreview.form:308 +msgid "75" +msgstr "" + +#: FPreview.form:308 +msgid "150" +msgstr "" + +#: FPreview.form:308 +msgid "300" +msgstr "" + +#: FPreview.form:308 +msgid "600" +msgstr "" + +#: FPreview.form:308 +msgid "1200" +msgstr "" + +#: FPreview.form:313 +msgid "dpi" +msgstr "" + +#: FPreview.form:336 +msgid "Range" +msgstr "Rango" + +#: FPreview.form:352 +msgid "Copies" +msgstr "Copias" + +#: FPreview.form:372 +msgid "Orientation" +msgstr "Orientación" + +#: FPreview.form:379 +msgid "Portrait" +msgstr "Retrato" + +#: FPreview.form:379 +msgid "Landscape" +msgstr "Paisaje" + +#: FPreview.form:389 +msgid "Paper" +msgstr "Papel" + +#: FPreview.form:396 +msgid "Custom" +msgstr "" + +#: FPreview.form:396 +msgid "A3" +msgstr "-" + +#: FPreview.form:396 +msgid "A4" +msgstr "-" + +#: FPreview.form:396 +msgid "A5" +msgstr "-" + +#: FPreview.form:396 +msgid "B5" +msgstr "-" + +#: FPreview.form:396 +msgid "Letter" +msgstr "-" + +#: FPreview.form:396 +msgid "Executive" +msgstr "-" + +#: FPreview.form:396 +msgid "Legal" +msgstr "-" + +#: FPreview.form:411 +msgid "Width" +msgstr "Ancho" + +#: FPreview.form:422 +msgid "mm" +msgstr "-" + +#: FPreview.form:432 +msgid "Height" +msgstr "Altura" + +#: FPreview.form:455 +msgid "Grayscale" +msgstr "" + +#: FPreview.form:461 +msgid "Full page" +msgstr "" + +#: FPreview.form:466 +msgid "Collate copies" +msgstr "" + +#: FPreview.form:471 +msgid "Reverse order" +msgstr "Orden inverso" + +#: FPreview.class:211 +msgid "PDF files" +msgstr "" + +#: FPreview.class:211 +msgid "Postscript files" +msgstr "" + +#: FPreview.class:226 +msgid "Cancel" +msgstr "Cancelar" + +#: FPreview.class:316 +msgid "This file already exists.\nDo you want to replace it?" +msgstr "" + +#: FPreview.class:316 +msgid "Replace" +msgstr "" + +#: FPreview.class:341 +msgid "Layout..." +msgstr "Disposición..." + +#: OutputReport2.report:77 +msgid "Version 1.0" +msgstr "-" + +#: OutputReport2.report:84 Report51.report:60 +msgid "Date" +msgstr "Fecha" + +#: OutputReport2.report:99 +msgid "Project Title:" +msgstr "" + +#: OutputReport2.report:106 +msgid "Project No.:" +msgstr "" + +#: OutputReport2.report:113 +msgid "Company:" +msgstr "" + +#: OutputReport2.report:120 +msgid "Designer:" +msgstr "" + +#: OutputReport2.report:134 +msgid "Base Plate ID:" +msgstr "" + +#: OutputReport2.report:185 +msgid "Bearing Pressue" +msgstr "" + +#: OutputReport2.report:195 +msgid "Node #" +msgstr "" + +#: OutputReport2.report:202 +msgid "Brg. Press., psi" +msgstr "" + +#: OutputReport2.report:266 +msgid "Anchor Rod Tension" +msgstr "" + +#: OutputReport2.report:276 +msgid "Rod #" +msgstr "" + +#: OutputReport2.report:283 +msgid "Tension, lbs" +msgstr "" + +#: OutputReport2.report:353 +msgid "Page $PAGE of $NPAGE" +msgstr "Página $PAGE de $NPAGE" + +#: Report.class:112 +msgid "Section " +msgstr "Sección" + +#: Report1.report:28 +msgid "toto" +msgstr "" + +#: Report11.report:19 +msgid "Section 1" +msgstr "" + +#: Report11.report:39 +msgid "Gambas" +msgstr "-" + +#: Report11.report:59 +msgid "All friends list !" +msgstr "" + +#: Report11.report:81 +msgid "Gambas Report Demo" +msgstr "" + +#: Report11.report:93 +msgid "DEMO" +msgstr "" + +#: Report11.report:106 +msgid "Page $PAGE on $NPAGE " +msgstr "" + +#: Report2.report:19 +msgid "ReportLabel1" +msgstr "" + +#: Report3.report:17 +msgid "PARCELLAIRE" +msgstr "" + +#: Report5.report:20 +msgid "Coucou" +msgstr "" + +#: Report51.report:30 +msgid "Recto" +msgstr "" + +#: Report51.report:38 +msgid "Verso" +msgstr "" + +#: Report51.report:51 +msgid "N°" +msgstr "" + +#: Report51.report:99 +msgid "Observations" +msgstr "" + +#: Report6.report:12 +msgid "Testtest

    \n\n
    " +msgstr "" + diff --git a/comp/src/gb.report2/.lang/fr.mo b/comp/src/gb.report2/.lang/fr.mo new file mode 100644 index 0000000000000000000000000000000000000000..2ddd3bfbf9d65786827e3f991a42803cf52bafe2 GIT binary patch literal 2132 zcmZ9MOKe;<6oyR+j}EU;pe@h3l(s;*!=!0NI;FJ3B%zU#W)vo=#H!A{&Wy_)d$4^o z83YTYg2a*qY$_zWLQ{mq0yeO!W{qG4J5*F59s;RSHwZTT=Uz_};mq;p$F=?Yoa6ko zW9&nQ62TsWUATp@6X2iQP@!DCm9g8vpFtY>HSmwXzXGp=w`2YWcrW-*=x-k@#>c_! z7~cir$M%N)0kDLACG?Mjw9icF&j+r6q~lzeUkl?M5I@#Kbtm{1co+CCNauY3(z=Ub z{t`Hj{^cB_v%vZq& z(65L7S#US{=RopTAKVFE0BPTgAnCgVlHa}ncYt4mbl$fho$~`o_r3;_pZ^AFzX%_p z$)-U1{#4)`NV=8+Nz>Nyc<4L_()oKsA8yFwLt3aj40{qrXVW`*2uAlAl_${Pu!Asq zOB4$#yI`b;G~N$;6!r*=v{E?)JCL`E>e(%Ay8j;7KG;(*iW?P*2jwvNZ$AuAEq?># z$7f(v$X8EOW1>>w`&n(0)sC>*QTFmtRu?*!DXW__6<+e#bd_}I7Ra189X7KeP0D^~PfEHHgqs#e>xd7%>wbR}ASllgMF%;sfFq^P}@mdzX8 z;!Y)!oiSRnMJ7p;=o3$Ki3jpYQ@HXcS+i&Ii#m2T_uOm7bhy$iCmh8( z9Vj;EW$dH5BwB$E99HG6)iSn$ki&ekJW(khtCSjx5rit5kERj6DnDGAcp<7BizX|) zGEtqJ;>*o?X;0Sw)K@=Ko8^1{`6u7O8T>0anhcxUDF@Pik$D<#AXh1W7ZgG)bsi2%4w6$Je(8q;o+3M0EsHbFmPfm9&CGDR<494g zx|x`p<2N&u&2_p~bs%-r;0Zj!GmYFS?odo1S!Di=bQDrj7MsjElGaE`a8sx>mD~;o z5=54E^6Z7<@H&Q{W^~i^;BbKF!{&6J!gh2c+RC%q8l4#&Jj&y`94RRhSn^~zI6wTP uTv{=Ri;LDx<`5EYM+fqgY+Ayz^l!l7g%|#=wGiq=S++DBB$=`jp7{^$6T(#h literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/.lang/fr.po b/comp/src/gb.report2/.lang/fr.po new file mode 100644 index 00000000..d8592051 --- /dev/null +++ b/comp/src/gb.report2/.lang/fr.po @@ -0,0 +1,389 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report2 3.10.90\n" +"POT-Creation-Date: 2017-09-30 01:40 UTC\n" +"PO-Revision-Date: 2017-09-30 01:38 UTC\n" +"Last-Translator: benoit \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "ReportsEvolution" +msgstr "" + +#: FPreview.class:212 +msgid "PDF files" +msgstr "Fichiers PDF" + +#: FPreview.class:212 +msgid "Postscript files" +msgstr "Fichiers Postscript" + +#: FPreview.class:227 +msgid "Cancel" +msgstr "Annuler" + +#: FPreview.class:317 +msgid "Replace" +msgstr "Remplacer" + +#: FPreview.class:317 +msgid "" +"This file already exists.\n" +"Do you want to replace it?" +msgstr "" +"Ce fichier existe déjà.\n" +"Voules-vous le remplacer ?" + +#: FPreview.class:342 +msgid "Layout..." +msgstr "Mise en page..." + +#: FPreview.class:354 +msgid "Printing..." +msgstr "Impression en cours..." + +#: FPreview.form:79 +msgid "Report preview" +msgstr "Aperçu de l'état" + +#: FPreview.form:86 +msgid "Print to file" +msgstr "Imprimer dans un fichier" + +#: FPreview.form:115 +#, fuzzy +msgid "Printing" +msgstr "Impression en cours..." + +#: FPreview.form:145 +msgid "One page" +msgstr "Une page" + +#: FPreview.form:154 +msgid "Two pages" +msgstr "Deux pages" + +#: FPreview.form:163 +msgid "Full width" +msgstr "Pleine largeur" + +#: FPreview.form:172 +msgid "Real size" +msgstr "Taille réelle" + +#: FPreview.form:180 +msgid "Zoom out" +msgstr "Réduire" + +#: FPreview.form:193 +msgid "100 %" +msgstr "-" + +#: FPreview.form:199 +msgid "Zoom in" +msgstr "Agrandir" + +#: FPreview.form:208 +msgid "Print" +msgstr "Imprimer" + +#: FPreview.form:216 +msgid "Show options" +msgstr "Afficher les options" + +#: FPreview.form:239 +msgid "Printer" +msgstr "Imprimante" + +#: FPreview.form:264 +msgid "Two-sided" +msgstr "" + +#: FPreview.form:271 +msgid "Long side" +msgstr "Côté long" + +#: FPreview.form:271 +msgid "None" +msgstr "Aucun" + +#: FPreview.form:271 +msgid "Short side" +msgstr "Côté court" + +#: FPreview.form:275 +msgid "File" +msgstr "Fichier" + +#: FPreview.form:301 +msgid "Resolution" +msgstr "" + +#: FPreview.form:313 +msgid "dpi" +msgstr "" + +#: FPreview.form:336 +msgid "Range" +msgstr "Plage" + +#: FPreview.form:352 +msgid "Copies" +msgstr "Copies" + +#: FPreview.form:372 +msgid "Orientation" +msgstr "Orientation" + +#: FPreview.form:379 +msgid "Landscape" +msgstr "Paysage" + +#: FPreview.form:379 +msgid "Portrait" +msgstr "Portrait" + +#: FPreview.form:389 +msgid "Paper" +msgstr "-" + +#: FPreview.form:396 +msgid "A3" +msgstr "-" + +#: FPreview.form:396 +msgid "A4" +msgstr "-" + +#: FPreview.form:396 +msgid "A5" +msgstr "-" + +#: FPreview.form:396 +msgid "B5" +msgstr "-" + +#: FPreview.form:396 +msgid "Custom" +msgstr "Personnalisé" + +#: FPreview.form:396 +msgid "Executive" +msgstr "-" + +#: FPreview.form:396 +msgid "Legal" +msgstr "-" + +#: FPreview.form:396 +msgid "Letter" +msgstr "-" + +#: FPreview.form:411 +msgid "Width" +msgstr "Largeur" + +#: FPreview.form:422 +msgid "mm" +msgstr "-" + +#: FPreview.form:432 +msgid "Height" +msgstr "Hauteur" + +#: FPreview.form:455 +msgid "Grayscale" +msgstr "Niveaux de gris" + +#: FPreview.form:461 +msgid "Full page" +msgstr "Pleine page" + +#: FPreview.form:466 +msgid "Collate copies" +msgstr "Assembler les copies" + +#: FPreview.form:471 +msgid "Reverse order" +msgstr "Inverser" + +#: OutputReport2.report:77 +msgid "Version 1.0" +msgstr "" + +#: OutputReport2.report:84 Report51.report:60 +msgid "Date" +msgstr "" + +#: OutputReport2.report:99 +msgid "Project Title:" +msgstr "" + +#: OutputReport2.report:106 +msgid "Project No.:" +msgstr "" + +#: OutputReport2.report:113 +msgid "Company:" +msgstr "" + +#: OutputReport2.report:120 +msgid "Designer:" +msgstr "" + +#: OutputReport2.report:134 +msgid "Base Plate ID:" +msgstr "" + +#: OutputReport2.report:185 +msgid "Bearing Pressue" +msgstr "" + +#: OutputReport2.report:195 +msgid "Node #" +msgstr "" + +#: OutputReport2.report:202 +msgid "Brg. Press., psi" +msgstr "" + +#: OutputReport2.report:266 +msgid "Anchor Rod Tension" +msgstr "" + +#: OutputReport2.report:276 +msgid "Rod #" +msgstr "" + +#: OutputReport2.report:283 +msgid "Tension, lbs" +msgstr "" + +#: OutputReport2.report:353 +msgid "Page $PAGE of $NPAGE" +msgstr "" + +#: Report.class:112 +msgid "Section " +msgstr "" + +#: Report1.report:29 +msgid "=\"Page \" & Page" +msgstr "" + +#: Report1.report:46 +msgid "=pi() + 4" +msgstr "" + +#: Report10.report:22 Report14.report:53 +msgid "=index" +msgstr "" + +#: Report10.report:31 +msgid "=Index" +msgstr "" + +#: Report10.report:46 +msgid "=\"Index = \" & index" +msgstr "" + +#: Report10.report:54 +msgid "=page & \" / \" & pages" +msgstr "" + +#: Report11.report:19 Report12.report:17 +msgid "Section 1" +msgstr "" + +#: Report11.report:39 +msgid "Gambas" +msgstr "" + +#: Report11.report:59 +msgid "All friends list !" +msgstr "" + +#: Report11.report:81 +msgid "Gambas Report Demo" +msgstr "" + +#: Report11.report:93 +msgid "DEMO" +msgstr "" + +#: Report11.report:106 +msgid "Page $PAGE on $NPAGE " +msgstr "" + +#: Report13.report:36 +msgid "=\"GAMBAS - \" & index" +msgstr "" + +#: Report13.report:44 +msgid "=\"PAGE \" & Page & \" / \" & pages" +msgstr "" + +#: Report14.report:28 +msgid "Reference" +msgstr "" + +#: Report14.report:35 +msgid "Description" +msgstr "" + +#: Report14.report:42 +msgid "Valeur" +msgstr "" + +#: Report14.report:73 +msgid "=page & \" on \" & pages" +msgstr "" + +#: Report16.report:21 Report17.report:22 Report5.report:20 +msgid "Coucou" +msgstr "" + +#: Report2.report:19 +msgid "ReportLabel1" +msgstr "" + +#: Report3.report:18 +msgid "PARCELLAIRE $NPAGE" +msgstr "" + +#: Report51.report:30 +msgid "Recto" +msgstr "" + +#: Report51.report:38 +msgid "Verso" +msgstr "" + +#: Report51.report:51 +msgid "N°" +msgstr "" + +#: Report51.report:99 +#, fuzzy +msgid "Observations" +msgstr "Orientation" + +#: Report6.report:12 +msgid "" +"Testtest

    \n" +"\n" +"
    " +msgstr "" + +#~ msgid "150" +#~ msgstr "-" + +#~ msgid "300" +#~ msgstr "-" + +#~ msgid "75" +#~ msgstr "-" diff --git a/comp/src/gb.report2/.lang/nl.mo b/comp/src/gb.report2/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..605c52639ac762e341c24d113490b18b02d86802 GIT binary patch literal 4159 zcma)-ZHQb~8Guh?wCNhHrm4nS_2i>zVluNcn`}NNJDbjaWV2?oJ5Dwq#+Yzt=Ird< zy>svM-a9+lloCW*K}7qfAfy&4B7&vXAATVI(6)jgrGJW2f7CxMf>iurL5d(g@11u) ziU^+F_de%+&)0k2bKWy=?%4XYq1-?oM0S3_nA>pshxkyoZ8PSB@E*7o-V5nsb}Dx% zAB0=5TiAnp;ZAr5-UnZVpM$T!d*NG9mUvhBFL*!tmV1o(1l$GrH-mg+-jm7^OPcpI#A-@h1=mOlzlu2 z#lLSuS?>;%^ekR{D`p{(~qDD(XcieJBiGVgDp^m`4;_&+KC z0*|7<3#H#d8ihxo#Jvn@G80hdttu};@#`9tb(&D(*@kk@9)<6~6y60roD(*bPT5lW z%4KC+87UuCCd#g|r(9J&rhHs^TlrPx*OX5{InS@F{*3Zj<@c1&DZdY8pFe;S_aCeM z*J^)B_1{B@->a(s5lYytDEqvdM)C80D1Poy?o;lEvY!L0 z7ogmSA=OVQ%gSs|w9g`vUuTgs$PpxmJb)ZWq*!DhGJ{+|t|7z79z=XEAYVYFoI>Or zq{#1plrJH2$I8fIL`n&nKqMBjKPe}XW5`}nHWk^I#9Ds09zqTvUq&SEgeUt=yAGwy z^Mh+qJ#>)B09g=geAr}!Ta*r-$HDf*v z$B+}qeqQUnfY>H7+-ZND>KUTS5b9Rs2Z~d_8ZepAk+VcBs)ymXlW~h2CN8demFjG6I31c#| zOd=Ii&ZaPBol!dd_n38S6mFiX;~zL^580!pa>P{l4x364*oD}4VKcFTpQLuTsf3Ni zD7LkzY3E&-_)%!ahRv9lI9m<8)Y<8Yk{NSeOjK+&c1hB8W-M;y`%3<>?IgY#_d=p) z#-kt`(}+4gn;DPV9WPuhQBl3yh`MIHo1{_OOiZ4iGZQRfCS1~p{Z1;2Q}W;}M? z@H&p#YSc~h`MjBNEid4krY<%!5j#x$rZclq)7iZsr2f*2X0D#Nc*PU{5;GUNz8iBA z$TYK>YNa+lIWtq4u1(r~vvS0yI&sD>;CN!Ha%lUqbB`9*n>QIlHCjvG4z5($M(p`1 zl_c%QW^FFc0keE<8fC`5CZoj4G0D!VakS(bshy4TrFCWAPXkvnH7_KJHRlCv_md5Da7LV4%_SSv6&G`Vvb-{Kv-X+` ztU))sUMZRj+`F_NaH>5UWfveZdJPZTppLTWC)qBo7sSqMu3C4Ki<{&JCL+r*+Md^s zMBI1U`sryi-;1&%Vt6mg$&ohAC9Xm@Hka^B{3zxNSv@kBCAZDhC~A|oeR4ZYv*Vj~ zdo6Ui>1IxcmTaq@XEmH3vPbg8LjGi7pgNbU5ha;ACs(>;hYH2xxx$Iu&@o#)Svopw zFV2q-%y>zfn~%LPAx)yVWH0+cuxe|bOA|ZNs%PpWQOAXC&kyR+&3qKMMi~~iy4;tN z4TFL6)8{8Qd|@90<2-HL#N7O9hc2nRnGSM*et60@7CjCkEnl2JlRL4|PaNQ|bCY2s zYI0#p_C(!J2l&zafBtwxoL$LY5f}1|EG6mgO#>@o*bN-l@d6Qoc3G1D|B$l zB)9$+c%99U0bcezkB2@qXQQY^&;EO`=yez)e-dP8Z~03+0O!Ie>TqkP9Q|_}`A;%W zvS(eL2Y_wK3n1%CzSsMXGKieVLCyLVZ!N?Bm%X3){#xq0_htUZtd)gk++AK~{`Hy- z+1A>^{U?A~)|=%mkll@u+y1hdY%KDmdkbwR4+VD3df5W^8%JAAUenmEL{VIKE@(v}Zt;@lrV__do+XM~=J1_UPSmD_L+Se15R|aqH7d ytsEvPPCD2nXBS;+c~Sb#Np`!n3-EAaCc1SVBsn%;_LJ>({Vrr#-Q^t>^M3%VK^&_9 literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/.lang/nl.po b/comp/src/gb.report2/.lang/nl.po new file mode 100644 index 00000000..21e31c80 --- /dev/null +++ b/comp/src/gb.report2/.lang/nl.po @@ -0,0 +1,388 @@ +#Willy Raets < gbWilly@openmailbox.org >, 2015 +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.report2 3.10.90\n" +"PO-Revision-Date: 2017-08-26 19:45 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project: 1 +msgid "ReportsEvolution" +msgstr "Rapport Evolutie" + +#: FPreview.class: 212 +msgid "PDF files" +msgstr "PDF bestanden" + +#: FPreview.class: 212 +msgid "Postscript files" +msgstr "Postscript bestanden" + +#: FPreview.class: 227 +msgid "Cancel" +msgstr "Annuleer" + +#: FPreview.class: 317 +msgid "Replace" +msgstr "Vervangen" + +#: FPreview.class: 317 +msgid "This file already exists.\nDo you want to replace it?" +msgstr "Dit bestand bestaat reeds.\nWil je het vervangen?" + +#: FPreview.class: 342 +msgid "Layout..." +msgstr "-" + +#: FPreview.form:79 +msgid "Report preview" +msgstr "Rapport voorbeeld" + +#: FPreview.form:86 +msgid "Print to file" +msgstr "Afdrukken naar bestand" + +#: FPreview.form:115 +msgid "Printing..." +msgstr "Afdrukken..." + +#: FPreview.form:145 +msgid "One page" +msgstr "Een pagina" + +#: FPreview.form:154 +msgid "Two pages" +msgstr "Twee pagina's" + +#: FPreview.form:163 +msgid "Full width" +msgstr "Volledige breedte" + +#: FPreview.form:172 +msgid "Real size" +msgstr "Echte afmeting" + +#: FPreview.form:180 +msgid "Zoom out" +msgstr "Zoom uit" + +#: FPreview.form:193 +msgid "100 %" +msgstr "-" + +#: FPreview.form:199 +msgid "Zoom in" +msgstr "-" + +#: FPreview.form:208 +msgid "Print" +msgstr "Afdrukken" + +#: FPreview.form:216 +msgid "Show options" +msgstr "Opties weergeven" + +#: FPreview.form:239 +msgid "Printer" +msgstr "Printer" + +#: FPreview.form:264 +msgid "Two-sided" +msgstr "Dubbelzijdig" + +#: FPreview.form:271 +msgid "Long side" +msgstr "Lange zijde" + +#: FPreview.form:271 +msgid "None" +msgstr "Geen" + +#: FPreview.form:271 +msgid "Short side" +msgstr "Korte zijde" + +#: FPreview.form:275 +msgid "File" +msgstr "Bestand" + +#: FPreview.form:301 +msgid "Resolution" +msgstr "Resolutie" + +#: FPreview.form:308 +msgid "1200" +msgstr "-" + +#: FPreview.form:308 +msgid "150" +msgstr "-" + +#: FPreview.form:308 +msgid "300" +msgstr "-" + +#: FPreview.form:308 +msgid "600" +msgstr "-" + +#: FPreview.form:308 +msgid "75" +msgstr "-" + +#: FPreview.form:313 +msgid "dpi" +msgstr "-" + +#: FPreview.form:336 +msgid "Range" +msgstr "Bereik" + +#: FPreview.form:352 +msgid "Copies" +msgstr "Kopieën" + +#: FPreview.form:372 +msgid "Orientation" +msgstr "Oriëntatie" + +#: FPreview.form:379 +msgid "Landscape" +msgstr "Landschap" + +#: FPreview.form:379 +msgid "Portrait" +msgstr "Portret" + +#: FPreview.form:389 +msgid "Paper" +msgstr "Papier" + +#: FPreview.form:396 +msgid "A3" +msgstr "-" + +#: FPreview.form:396 +msgid "A4" +msgstr "-" + +#: FPreview.form:396 +msgid "A5" +msgstr "-" + +#: FPreview.form:396 +msgid "B5" +msgstr "-" + +#: FPreview.form:396 +msgid "Custom" +msgstr "Aangepast" + +#: FPreview.form:396 +msgid "Executive" +msgstr "-" + +#: FPreview.form:396 +msgid "Legal" +msgstr "-" + +#: FPreview.form:396 +msgid "Letter" +msgstr "-" + +#: FPreview.form:411 +msgid "Width" +msgstr "Breedte" + +#: FPreview.form:422 +msgid "mm" +msgstr "-" + +#: FPreview.form:432 +msgid "Height" +msgstr "Hoogte" + +#: FPreview.form:455 +msgid "Grayscale" +msgstr "Grijswaarden" + +#: FPreview.form:461 +msgid "Full page" +msgstr "Volledige pagina" + +#: FPreview.form:466 +msgid "Collate copies" +msgstr "Sorteer kopieën" + +#: FPreview.form:471 +msgid "Reverse order" +msgstr "Omgekeerde volgorde" + +#: OutputReport2.report:77 +msgid "Version 1.0" +msgstr "-" + +#: OutputReport2.report:84 Report51.report:60 +msgid "Date" +msgstr "Datum" + +#: OutputReport2.report:99 +msgid "Project Title:" +msgstr "-" + +#: OutputReport2.report:106 +msgid "Project No.:" +msgstr "-" + +#: OutputReport2.report:113 +msgid "Company:" +msgstr "-" + +#: OutputReport2.report:120 +msgid "Designer:" +msgstr "Ontwerper:" + +#: OutputReport2.report:134 +msgid "Base Plate ID:" +msgstr "-" + +#: OutputReport2.report:185 +msgid "Bearing Pressue" +msgstr "-" + +#: OutputReport2.report:195 +msgid "Node #" +msgstr "Knoop #" + +#: OutputReport2.report:202 +msgid "Brg. Press., psi" +msgstr "-" + +#: OutputReport2.report:266 +msgid "Anchor Rod Tension" +msgstr "-" + +#: OutputReport2.report:276 +msgid "Rod #" +msgstr "-" + +#: OutputReport2.report:283 +msgid "Tension, lbs" +msgstr "-" + +#: OutputReport2.report:353 +msgid "Page $PAGE of $NPAGE" +msgstr "Pagina $PAGE van $NPAGE" + +#: Report.class:112 +msgid "Section " +msgstr "Sectie" + +#: Report1.report:29 +msgid "=\"Page \" & Page" +msgstr "-" + +#: Report1.report:46 +msgid "=pi() + 4" +msgstr "-" + +#: Report10.report:22 Report14.report:53 +msgid "=index" +msgstr "-" + +#: Report10.report:31 +msgid "=Index" +msgstr "-" + +#: Report10.report:46 +msgid "=\"Index = \" & index" +msgstr "-" + +#: Report10.report:54 +msgid "=page & \" / \" & pages" +msgstr "-" + +#: Report11.report:19 Report12.report:17 +msgid "Section 1" +msgstr "-" + +#: Report11.report:39 +msgid "Gambas" +msgstr "-" + +#: Report11.report:59 +msgid "All friends list !" +msgstr "Alle vrienden lijst!" + +#: Report11.report:81 +msgid "Gambas Report Demo" +msgstr "Gambas Rapport Demo" + +#: Report11.report:93 +msgid "DEMO" +msgstr "-" + +#: Report11.report:106 +msgid "Page $PAGE on $NPAGE " +msgstr "Pagina $PAGE van $NPAGE " + +#: Report13.report:36 +msgid "=\"GAMBAS - \" & index" +msgstr "-" + +#: Report13.report:44 +msgid "=\"PAGE \" & Page & \" / \" & pages" +msgstr "-" + +#: Report14.report:28 +msgid "Reference" +msgstr "Referentie" + +#: Report14.report:35 +msgid "Description" +msgstr "Omschrijving" + +#: Report14.report:42 +msgid "Valeur" +msgstr "-" + +#: Report14.report:73 +msgid "=page & \" on \" & pages" +msgstr "-" + +#: Report16.report:21 Report17.report:22 Report5.report:20 +msgid "Coucou" +msgstr "-" + +#: Report2.report:19 +msgid "ReportLabel1" +msgstr "-" + +#: Report3.report:18 +msgid "PARCELLAIRE $NPAGE" +msgstr "-" + +#: Report51.report:30 +msgid "Recto" +msgstr "-" + +#: Report51.report:38 +msgid "Verso" +msgstr "-" + +#: Report51.report:51 +msgid "N°" +msgstr "-" + +#: Report51.report:99 +msgid "Observations" +msgstr "Observaties" + +#: Report6.report:12 +msgid "Testtest

    \n\n
    " +msgstr "-" + diff --git a/comp/src/gb.report2/.project b/comp/src/gb.report2/.project new file mode 100644 index 00000000..d5d36385 --- /dev/null +++ b/comp/src/gb.report2/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +Title=ReportsEvolution +Startup=Report13 +Icon=.hidden/control/reportview.png +Version=3.12.90 +VersionFile=1 +Component=gb.image +Component=gb.gui +Component=gb.form +Component=gb.db +Component=gb.eval +Component=gb.settings +Environment="GB_GUI=gb.qt5" +TabSize=2 +Translate=1 +Language=en +Type=Component +Packager=1 +DoNotTranslate=".src/Tests" diff --git a/comp/src/gb.report2/.src/Evaluator/CResult.class b/comp/src/gb.report2/.src/Evaluator/CResult.class new file mode 100644 index 00000000..2903a454 --- /dev/null +++ b/comp/src/gb.report2/.src/Evaluator/CResult.class @@ -0,0 +1,55 @@ +' Gambas class file + +Public Enum TypeValue, TypeString, TypeOp, TypeVar, TypeFunc, TypeBool, TypeDate +Public Value As Variant +Public {Type} As Integer +Public Pos As Integer +Property Read isValue As Boolean +Property Read isOperator As Boolean +Property Read isString As Boolean +Property Read isVariable As Boolean +Property Read isFunction As Boolean +Property Read isBoolean As Boolean + +Public Sub _new(vValue As Variant, iType As Integer) + + Value = vValue + Me.Type = iType + +End + +Private Function isValue_Read() As Boolean + + Return Me.Type = TypeValue + +End + +Private Function isOperator_Read() As Boolean + + Return Me.Type = TypeOp + +End + +Private Function isString_Read() As Boolean + + Return Me.Type = TypeString + +End + +Private Function isVariable_Read() As Boolean + + Return Me.Type = TypeVar + +End + +Private Function isFunction_Read() As Boolean + + Return Me.Type = TypeFunc + +End + +Private Function isBoolean_Read() As Boolean + + Return Me.Type = TypeBool + +End diff --git a/comp/src/gb.report2/.src/Evaluator/_RepExp.class b/comp/src/gb.report2/.src/Evaluator/_RepExp.class new file mode 100644 index 00000000..ea18e606 --- /dev/null +++ b/comp/src/gb.report2/.src/Evaluator/_RepExp.class @@ -0,0 +1,50 @@ +' Gambas class file + +Inherits Expression +Public Data As Variant +Event Data(Name As String) +Static Private $aFunctions As String[] +Static Private $aAllowedIdentifiers As String[] = ["page", "pages", "index"] + +Static Private Sub LoadFunctions() + + $aFunctions = Split(File.Load("FunctionsList"), "\n") + +End + + + + +Static Public Sub IsIdentifier(Name As String) As Boolean + + 'Print "Identifier : " & Name + + +Return $aAllowedIdentifiers.Exist(LCase(Name)) +End + +Static Public Sub IsSubr(Name As String) As Boolean + If Not $aFunctions Then LoadFunctions + ' Return if a the 'Name' Gambas subroutine is allowed. + Return $aFunctions.Exist(LCase(Name)) +'Print Name + +End + +Public Sub GetValue(Name As String) As Variant + + 'Return the value Of the 'Name' identifier. + Data = 0 + 'Print object.parent(Me) + Raise Data(Name) + Return Data + 'Print "Get Value : " & Name + 'Return "2" + +End + +' Public Sub Coucou(Value As String) As String +' +' Return "Coucou " & Value +' +' End diff --git a/comp/src/gb.report2/.src/Optional/Align.class b/comp/src/gb.report2/.src/Optional/Align.class new file mode 100644 index 00000000..485bc6a1 --- /dev/null +++ b/comp/src/gb.report2/.src/Optional/Align.class @@ -0,0 +1,10 @@ +' Gambas class file + +Export Optional + +' Must be the same values as in gb.form.const.h in gb.qt4 sources + +Public Enum + Normal = &H00, {Left} = &H01, {Right} = &H02, Center = &H03, + TopNormal = &H10, TopLeft = &H11, TopRight = &H12, Top = &H13, + BottomNormal = &H20, BottomLeft = &H21, BottomRight = &H22, Bottom = &H23 diff --git a/comp/src/gb.report2/.src/Optional/Arrange.class b/comp/src/gb.report2/.src/Optional/Arrange.class new file mode 100644 index 00000000..20388fdc --- /dev/null +++ b/comp/src/gb.report2/.src/Optional/Arrange.class @@ -0,0 +1,11 @@ +' Gambas class file + +' Gambas class file + +Export Optional + +Public Const {None} As Integer = 0 +Public Const {Horizontal} As Integer = 1 +Public Const {Vertical} As Integer = 2 +Public Const {Column} As Integer = 4 +Public Const {Fill} As Integer = 5 diff --git a/comp/src/gb.report2/.src/Optional/Line.class b/comp/src/gb.report2/.src/Optional/Line.class new file mode 100644 index 00000000..c345df55 --- /dev/null +++ b/comp/src/gb.report2/.src/Optional/Line.class @@ -0,0 +1,13 @@ +' Gambas class file + +Export Optional + + +' Must be the same values as in gb.form.const.h in gb.qt4 sources + +Public Const {None} As Integer = 0 +Public Const {Solid} As Integer = 1 +Public Const {Dash} As Integer = 2 +Public Const {Dot} As Integer = 3 +Public Const {DashDot} As Integer = 4 +Public Const {DashDotDot} As Integer = 5 \ No newline at end of file diff --git a/comp/src/gb.report2/.src/Report.class b/comp/src/gb.report2/.src/Report.class new file mode 100644 index 00000000..9a7bdc36 --- /dev/null +++ b/comp/src/gb.report2/.src/Report.class @@ -0,0 +1,792 @@ +' Gambas class file + +Create Static +Export +Inherits ReportSection + +' For Stretch properties +Public Enum None, Proportional, Fill + +Public Const _Properties As String = "*,-Left,-Top,-Width,-Height,Count{Range:1;256}=1,Index,Text," +"Paper{Printer.A3;A4;A5;B5;Letter;Executive;Legal;Custom}=A4," +"Orientation{Printer.Portrait;Landscape}=Portrait" +Public Const _HiddenControls As String = "Report,ReportControl,ReportContainer,ReportFrame,ReportSection" +Public Const _IsForm As Boolean = True +Public Const _IsContainer As Boolean = True +Public Const _IsMultiContainer As Boolean = True +Public Const _DrawWith As String = "TabPanel" +Public Const _DefaultEvent As String = "Open" +Static Public AllowedUnits As String[] = ["m", "cm", "mm", "in", "pt", "px"] + +Public _LayoutNotFinished As Boolean + +Public _Container As ReportSection + +Public {Debug} As Boolean = False +Public _bInExec As Boolean + + +Private $aSections As New TControl[] +Private iCurPage As Integer = 0 +Private $iPageCount As Integer = -1 +Public _LayoutIsDirty As Boolean = True +Private $fScale As Float = 1.0 +Private $iIndex As Integer +Private $iResolution As Integer = -1 +Private $iCurSection As Integer +Private $iCurPage As Integer = -1 +Private $iPaper As Integer = 2 +Private $iOrientation As Integer +' Private $hPrinter As New Printer As "Printer" +Private $bOpened As Boolean +Private $fCurReportWidth As Float + +' Property Read Printer As Printer +Property Resolution As Integer +Property Scale As Float +Property Read PageCount As Integer +Property Count As Integer +Property Index As Integer + +'All ReportControl Properties +Property Left As String +Property Top As String +Property X As String +Property Y As String + +Property Width As String +Property Height As String + +Property Visible As Boolean +Property {Font} As Font +Property Expand As Boolean +Property Ignore As Boolean +Property Fixed As Boolean + +Property Autoresize As Boolean +Property Padding As ReportPadding +'All ReportFrame Properties +Property Border As ReportBorder +Property BackGround As ReportBrush + +'All ReportContainer Properties +Property Read Children As ReportControl[] +Property Spacing As String +Property Data As Object + +'All ReportSection Properties +Property Text As String + +Property Paper As Integer +Property Orientation As Integer +Property Read _Width As Float +Property Read _Height As Float + +Event Open + +Static Public Sub Main() + + Dim hObject As Object + + hObject = Application.Startup.AutoCreate() + If Not hObject Is Report Then Return + FPreview.Run(hObject) + +End + +Public Sub _New() + + Dim hSection As New ReportSection + Dim TSection As New TControl + + object.Attach(Me, Me, "Report") + + '$hReportTControl.RelPage = 0 + ReportControl._ObjectFromId[Me.id] = Me + hSection = New ReportSection + hSection._ReportId = Me.Id + + ReportControl._ObjectFromId[hSection.Id] = hSection + TSection.Ctrl = hSection + $aSections.Add(TSection) + hSection.Text = ("Section ") & $aSections.Max + _Container = hSection + Me.paper = Printer.A4 + 'Me.Orientation = Printer.Landscape +End + +Public Sub Layout(Optional iPage As Integer = -1) + + + 'Dim iCurPage As Integer + Dim TSection As TControl + Dim CSection As ReportContainer + Dim CBaseSection As ReportContainer + Dim i As Integer + Dim hImg As Image + 'Utilise un device par défaut si nécéssaire + If Not Paint.Device Then + hImg = New Image(1, 1) + Paint.Begin(hImg) + Endif + Paint.Font = Me.Font + If Not $bOpened Then + Raise Open + $bOpened = True + Endif + + + If Not _LayoutIsDirty Then Return + If _bInExec Then Return + If iPage > -1 Then + 'La page a déja été calculée + If iPage <= $iCurPage Then Return + Else + 'Mise a zéro + $iCurPage = -1 + $iCurSection = 0 + $iPageCount = 0 + Me._Reset + Endif + 'Set execution Flag to true + _bInExec = True + CBaseSection = $aSections[0].Ctrl + + + For i = $iCurSection To $aSections.Max + Tsection = $aSections[i] + CSection = TSection.Ctrl + CSection.Padding = CBaseSection.Padding + CSection.Spacing = CBaseSection.Spacing + CSection._NormalizeUnits() + $fCurReportWidth = CBaseSection._Width + TSection._SetGeometry(0, 0, CSection._Width, CSection._Height) + Inc $iCurPage + Do + + CSection._SetChildGeometry(0, 0, CSection._Width, CSection._Height, $iCurPage, TSection, False) + + If CSection._CurItem > CSection.Children.Max Then Break + + Inc $iCurPage + $iPageCount = $iCurPage + 'Print "Fin Page " & $iCurPage + If iPage > -1 And If $iCurPage > iPage Then + Dec $iCurPage + _LayoutNotFinished = True + $iCurSection = i + _bInExec = False + Return + + Endif + + Loop + 'Inc iCurPage + Next + + $iPageCount = $iCurPage + 1 + _LayoutNotFinished = False + $iCurSection = 0 + _LayoutIsDirty = False + + 'End of execution + _bInExec = False + 'Print "Pages: " & $ipageCount +End + +Public Sub Paint(Page As Integer) + + Dim TSection As TControl + 'Dim fScale As Float + + + If Me.Parent = Null Then + + Endif + Paint.Font = Me.Font + $fCurReportWidth = $aSections[0].Ctrl._Width + + If _LayoutIsDirty Or $iPageCount = -1 Then Layout(Page) + Paint.Save + Paint.Scale($fScale, $fScale) + 'Set execution Flag to true + _bInExec = True + + + Dec page + + For Each TSection In $aSections + TSection.Ctrl._PaintBefore(Page, 0, 0, TSection, -1) + TSection.Ctrl._Paint(Page, 0, 0, TSection, -1) + TSection.Ctrl._PaintAfter(Page, 0, 0, TSection, -1) + Next + 'End of execution + _bInExec = False + Paint.Restore + + +End + +Private Function PageCount_Read() As Integer + + If _LayoutIsDirty And Not _LayoutNotFinished Then Layout(1) + + Return $iPageCount + +End + +Private Function Height_Read() As String + + Return $aSections[0].Ctrl.Height + +End + +Private Sub Height_Write(Value As String) + + SetHeight(Value) + $iPaper = Printer.Custom + +End + +Private Sub SetHeight(Value As String) + + Dim TSection As TControl + + If $aSections[0].Ctrl.Height <> Value Then + + For Each TSection In $aSections + TSection.Ctrl.Height = Value + Next + _LayoutIsDirty = True + Me._reset + Endif + +End + +Private Function Width_Read() As String + + Return $aSections[0].Ctrl.Width + +End + +Private Sub Width_Write(Value As String) + + SetWidth(Value) + $iPaper = Printer.Custom + +End + +Private Sub SetWidth(Value As String) + + Dim TSection As TControl + + If $aSections[0].Ctrl.Width <> Value Then + For Each TSection In $aSections + Tsection.Ctrl.Width = Value + Next + _LayoutIsDirty = True + Me._Reset + Endif + +End + +Private Function Scale_Read() As Float + + Return $fScale + +End + +Private Sub Scale_Write(Value As Float) + + $fScale = Value + +End + +Public Sub Clear() + + Super._Free + iCurPage = 0 + $iPageCount = -1 + + _LayoutIsDirty = True + $fScale = 1.0 + +End +' + +Public Function Preview() + + FPreview.Run(Me) + +End + + + +Public Sub Refresh() + + _LayoutIsDirty = True + Me._Reset + +End + +Private Function Count_Read() As Integer + + Return $aSections.Count + +End + +Private Sub Count_Write(Value As Integer) + + Dim i As Integer + Dim hSection As ReportSection + Dim TSection As TControl + + If Value < 0 Then Return + If Value < $aSections.Count Then + $aSections.Resize(Value) + Else If Value > $aSections.Count Then + For i = $aSections.Count To Value - 1 + + TSection = New TControl + hSection = New ReportSection + hSection.Text = "Section " & CStr(i) + hSection._ReportId = Me.Id + ReportControl._ObjectFromId[hSection.Id] = hSection + TSection.Ctrl = hSection + hSection.Padding = Me.Padding + hSection.Height = Me.Height + hSection.Width = Me.Width + $aSections.Add(TSection) + hSection.Text = ("Section ") & $aSections.Max + Next + Endif + +End + +Private Function Index_Read() As Integer + + Return $iIndex + +End + +Private Sub Index_Write(Value As Integer) + + $iIndex = Max(Min($aSections.Max, Value), 0) + _Container = $aSections[$iIndex].Ctrl + +End + +Public Sub _get(Index As Integer) As ReportSection + + Return $aSections[Index].Ctrl + +End + +Private Function Padding_Read() As ReportPadding + + Return $aSections[0].Ctrl.Padding + +End + +Private Sub Padding_Write(Value As ReportPadding) + + Dim TSection As TControl + + If Me.Padding <> Value Then + For Each TSection In $aSections + Tsection.Ctrl.Padding = Value + Next + Endif + +End + +Private Function Resolution_Read() As Integer + + Return $iResolution + +End + +Private Sub Resolution_Write(Value As Integer) + + _LayoutIsDirty = True + $iResolution = Max(0, Value) + +End + +Private Function Left_Read() As String + + Return _Container.Left + +End + +Private Sub Left_Write(Value As String) + + _Container.Left = Value + +End + +Private Function Top_Read() As String + + Return _Container.Top + +End + +Private Sub Top_Write(Value As String) + + _Container.Top = Value + +End + +Private Function X_Read() As String + + Return _Container.Left + +End + +Private Sub X_Write(Value As String) + + _Container.Left = Value + +End + +Private Function Y_Read() As String + + Return _Container.Top + +End + +Private Sub Y_Write(Value As String) + + _Container.Top = Value + +End + +Private Function Visible_Read() As Boolean + + Return _Container.Visible + +End + +Private Sub Visible_Write(Value As Boolean) + + _Container.Visible = Value + +End + +Private Function Font_Read() As Font + + Return _Container.Font + +End + +Private Sub Font_Write(Value As Font) + + _Container.Font = Value + +End + +Private Function Expand_Read() As Boolean + + Return _Container.Expand + +End + +Private Sub Expand_Write(Value As Boolean) + + _Container.Expand = Value + +End + +Private Function Ignore_Read() As Boolean + + Return _Container.Ignore + +End + +Private Sub Ignore_Write(Value As Boolean) + + _Container.Ignore = Value + +End + +Private Function Fixed_Read() As Boolean + + Return _Container.Fixed + +End + +Private Sub Fixed_Write(Value As Boolean) + + _Container.Fixed = Value + +End + +Private Function Autoresize_Read() As Boolean + + Return _Container.Autoresize + +End + +Private Sub Autoresize_Write(Value As Boolean) + + _Container.Autoresize = Value + +End + +Private Function Border_Read() As ReportBorder + + Return _Container.Border + +End + +Private Sub Border_Write(Value As ReportBorder) + + _Container.Border = Value + +End + +Private Function BackGround_Read() As ReportBrush + + Return _Container.BackGround + +End + +Private Sub BackGround_Write(Value As ReportBrush) + + _Container.BackGround = Value + +End + +Private Function Children_Read() As ReportControl[] + + Return _Container.Children + +End + +Private Function Spacing_Read() As String + + Return _Container.Spacing + +End + +Private Sub Spacing_Write(Value As String) + + Dim TSection As TControl + Dim hCont As ReportContainer + + If Me.Spacing <> Value Then + For Each TSection In $aSections + hCont = Tsection.Ctrl + hCont.Spacing = Value + Next + Endif + +End + +Private Function Data_Read() As Object + + Return _Container.Data + +End + +Private Sub Data_Write(Value As Object) + + _Container.Data = Value + +End + +Private Function Text_Read() As String + + Return _Container.Text + +End + +Private Sub Text_Write(Value As String) + + _Container.Text = Value + +End + +''Return a new report object +Public Sub Clone() As Report + + 'Return Object.New(Object.Type(Me)) + Return Object.Class(Me).New() + +End + +Private Function Paper_Read() As Integer + + Return $iPaper + +End + +Private Sub Paper_Write(Value As Integer) + + $iPaper = Value + SetPaper + +End + +Private Function Orientation_Read() As Integer + + Return $iOrientation + +End + +Private Sub Orientation_Write(Value As Integer) + + $iOrientation = Value + SetPaper + +End + +Public Sub Print(Optional hPrinter As Printer) + + If hPrinter Then + CPrint.PrintReport(Me, hPrinter) + Else + CPrint.PrintReport(Me) + Endif + +End + +Private Sub SetPaper() + + Dim sPrev As String + + Select Case $iPaper + Case Printer.A3 + SetWidth("29.7cm") + SetHeight("42cm") + Case Printer.A4 + SetWidth("21cm") + SetHeight("29.7cm") + Case Printer.A5 + SetWidth("14.8cm") + SetHeight("21cm") + Case Printer.B5 + SetWidth("18.7cm") + SetHeight("25.7cm") + Case Printer.Executive + SetWidth("19.1cm") + SetHeight("25.4cm") + Case Printer.Legal + SetWidth("21.6cm") + SetHeight("35.6cm") + Case Printer.Letter + SetWidth("21.6cm") + SetHeight("27.9cm") + + End Select + + + If $iOrientation = Printer.Landscape Then + sPrev = Me.Height + SetHeight(Me.Width) + SetWidth(sPrev) + Endif + +End + +Public Sub _Reset() + + Dim hSection As TControl + + For Each hSection In $aSections + hSection.Ctrl._Reset + hSection._PageChildren = New Collection + Next + + $iCurPage = -1 + $iCurSection = 0 + $iPageCount = 0 + +End + +''Convert Internal units to scaled pixels. + +Fast Public Function _ToPixels(Value As Float) As Float + + Dim f As Float + + f = Value / $fCurReportWidth * (Paint.Width / $fScale) + + Return f + +End + +''Shortcut to Convert a Pixel Value to Internal unit (Need a paint device). +Fast Public Function _FromPixels(Value As Float) As Float + + Return (value / Paint.ResolutionX) '* 2.54) + +End + +Fast Static Private Function GetFactorToInch(Unit As String) As Float + + Select Case Unit + Case "cm" + Return 0.3937 + Case "ft" + Return 12 + Case "in" + Return 1 + Case "m" + Return 39.37 + Case "mm" + Return 0.03937 + Case "px" + If Paint.ResolutionX Then + Return 1 / Paint.ResolutionX + Else + Return 1 / Desktop.Resolution + Endif + Case "pt" + Return 1 / 72 'http://en.wikipedia.org/wiki/Point_(typography)#Current_DTP_point_system + Case Else + Error.Raise("Unknown unit") + End Select + +End + +''Convert any value from any report allowed Unit to another one. +''For Pixel convertions, be carefull to give the Device +''Resolution in parameter. +Fast Static Public Function UnitTo(Value As Float, Unit1 As String, Unit2 As String) As Float + + Dim fInch As Float + Dim f As Float + + f = GetFactorToInch(Unit1) + finch = value * f + f = GetFactorToInch(Unit2) + Return finch / f + +End + +''Shortcut to convert any report allowed unit to Inch. +Fast Static Public Function UnitToInch(Value As Float, Unit As String) As Float + + Dim fInch As Float + Dim f As Float + + f = GetFactorToInch(Unit) + finch = value * f + Return finch + 'f = 0.3937 + 'Return finch / f + +End + +Private Function _Width_Read() As Float + + Return $aSections[0].Ctrl._Width + +End + +Private Function _Height_Read() As Float + + Return $aSections[0].Ctrl._Height + +End diff --git a/comp/src/gb.report2/.src/ReportContainer.class b/comp/src/gb.report2/.src/ReportContainer.class new file mode 100644 index 00000000..3e95708d --- /dev/null +++ b/comp/src/gb.report2/.src/ReportContainer.class @@ -0,0 +1,1080 @@ +' Gambas class file + +Export +Create Private +Inherits ReportFrame + +Public Const _IsContainer As Boolean = True +Public Const _Group As String = "Container" +Public Const _Properties As String = "*,Spacing{ReportCoord},OnePiece,ForceNewPage" +'Public Const _DefaultEvent As String = "Data" +'Private $bDataIsResult As Boolean +'Private $iCount As Integer + +Property _CurItem As Integer +Private $iCurItem As Integer + +Public _Arrangement As Integer +Private $bIndexChange As Boolean +Private $aChild As New ReportControl[] +Private $fSpacing As Float +Private $sSpacing As String +Private $bRelativeSpacing As Boolean + +'Private $aChildCopy As TControl[] +Private $bOnePiece As Boolean +'Private $iIndex As Integer +'Private $hData As Object +Private $fFixedSize As Float +Property _Index As Integer + +Property DataCount As Integer + +Property Read Children As ReportControl[] + +'Property Read Index As Integer +Property Spacing As String +'Property Data As Object '<==== remettre pour automation + +Property Read _Spacing As Float +Property Read _RelativeSpacing As Boolean + +Property OnePiece As Boolean +Public _RealSpacing As Integer +Public _NotFinished As Boolean + +Event BeforeData +Event AfterData + +Public Function _Add(cControl As ReportControl) 'As TControl + + 'Dim hTControl As New TControl + + 'hTControl.Ctrl = cControl + $aChild.Add(cControl) + ReportControl._ObjectFromId[cControl.Id] = cControl + 'Return hTControl + +End + +Public Sub _Remove(id As Integer) + + $aChild.Remove($aChild.Find(ReportControl._ObjectFromId[id])) + +End + +Public Sub _Free() + +End + +Public Sub _Raise(hControl As ReportControl) + + Dim iPos As Integer + + iPos = $aChild.Find(hControl) + If iPos = -1 Then Return + $aChild.Remove(iPos) + $aChild.add(hControl, 0) + +End + +Public Sub _Lower(hControl As ReportControl) + + Dim iPos As Integer + + iPos = $aChild.Find(hControl) + If iPos = -1 Then Return + $aChild.Remove(iPos) + $aChild.add(hControl) + +End + +Private Function Children_Read() As ReportControl[] + + Return $aChild + +End + +Private Function Spacing_Read() As String + + Return $sSpacing + +End + +Private Sub Spacing_Write(Value As String) + + $sSpacing = Value + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, (DataIndex) As Integer) + + Dim hChild As TControl + Dim i As Integer + + X += hControl.RealLeft + Y += hControl.RealTop + ' Paint.Text(DataIndex, X + 20, Y + 20) + ' Paint.Brush = Paint.Color(Color.Black) + ' Paint.Fill + + If Not hControl._PageChildren.Exist(Page) Then Return + 'For Each hChild In hControl._PageChildren[Page] + For i = 0 To hControl._PageChildren[Page].Max + hchild = hControl._PageChildren[Page][i] + Me._Index = hchild.Index + If $bIndexChange Then + Raise BeforeData + $bIndexChange = False + Endif + hChild.Ctrl._Paintframe(Page, X, Y, hChild, hchild.Index) 'hChild.Index) + + Next + +End + +Private Function _Spacing_Read() As Float + + Return $fSpacing + +End + +Private Function _RelativeSpacing_Read() As Boolean + + Return $bRelativeSpacing + +End + +Public Sub _NormalizeUnits() + + Dim hChild As ReportControl + Dim hSizeParse As TSizeParse + + Super._NormalizeUnits() + + hSizeParse = New TSizeParse($sSpacing, True) + $fSpacing = hSizeParse.GetValue() + $bRelativeSpacing = hSizeParse.IsRelative() + + For Each hChild In $aChild + 'hchild.SizeHint = Null + hChild._NormalizeUnits() + Next + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + + Dim hMyHints As New ReportSizeHints + + 'If Me._SizeInt.StoreSize Then Return Me._SizeInt + 'If Me.tag = "*" Then Stop + If Me._CurItem > Me.Children.max Then + + ' If Me Is ReportSection Then Stop + Me._CurItem = 0 + ' 'Me._DataIndex = 0 + Endif + Me._Index = DataIndex + If $bIndexChange Then + Raise BeforeData + $bIndexChange = False + Endif + Select Case _Arrangement + + Case Arrange.Vertical + Return GetVSizeInt(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + Case Arrange.Horizontal + Return GetHSizeInt(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + Case Arrange.Column + Return GetVPSizeInt(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + Case Arrange.Fill, Arrange.None + Return Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + 'hMyHints.Height = AvailableH + 'Me._SizeInt.StoreSize = True + 'Me._SizeInt = hMyHints + Return hMyHints + + End Select + Raise AfterData + +End + +Private Function GetHSizeInt((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + + Dim hChild As ReportControl + Dim fHeight, fWidth As Float + Dim hChildHints As ReportSizeHints + Dim hMyHints As New ReportSizeHints + Dim fSpc As Float + Dim fMaxSpc As Float + Dim i As Integer + 'D'abord utiliser la méthode du controle pour définir la taille + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + + 'Puis si besoins voir le besoin des enfants + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + fSpc = IIf(Me._RelativeSpacing, TotalWidth * Me._Spacing / 100, Me._Spacing) + 'on ajoute la marge supérieur du premier objet + If Me.Children.Max > 0 Then fWidth += Me.Children[0].Margin._Top + For i = 0 To Me.Children.Max + hChild = Me.Children[i] + hChildHints = hchild._GetSizeHints(AvailableW - fWidth, AvailableH, AvailableW, AvailableH, DataIndex) + fMaxSpc = Max(fspc, hchild.Margin._Right) + If i < Me.Children.Max Then fMaxSpc = Max(fMaxSpc, Me.Children[i + 1].Margin._Left) + fWidth += hChildHints.Width + fMaxSpc + If fHeight < hChildHints.Height Then fHeight = hChildHints.Height + 'Si l'enfant n'est pas finit alors moi non plus (:-P) + If hChildHints.NotFinished Then hMyHints.NotFinished = True + Next + 'Retrait du dernier espace + If fWidth Then fWidth -= fSpc + hMyHints.Height = fHeight + Me.Padding._Height + Me.Border._Top + Me.Border._bottom + hMyHints.Width = fWidth + Me.Padding._Width + Me.Border._Left + Me.Border._Right + Endif + + Return hMyHints + +End + +Private Function GetVPSizeInt((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + ' Dim hSizeInt As New ReportSizeHints + ' Dim htmpInts As ReportSizeHints + ' Dim hChild As ReportControl ''Enfants + ' Dim fHeight, fSpc, fWidth As Float + ' Dim i, j As Integer + ' Dim bExitLoop As Boolean + ' Dim fMaxSpc As Float + ' + ' 'D'abord utiliser la méthode du controle pour définir la taille + ' hSizeInt = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + ' 'Circuit court + ' If Me.Children.Count = 0 Then Return hSizeInt + ' 'Largeur d'un espace + ' fSpc = IIf(Me._RelativeSpacing, TotalHeight * Me._Spacing / 100, Me._Spacing) + ' ' Si ce n'est pas fait on enregistre la place nécéssaire a tout les éléments fixes + ' If Not $fFixedSize Then + ' + ' Endif + ' +End + + + + +Private Function GetVSizeInt((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + + Dim hSizeInt As New ReportSizeHints + Dim htmpInts As ReportSizeHints + Dim hChild As ReportControl ''Enfants + Dim fHeight, fSpc, fWidth As Float + Dim i, j As Integer + Dim bExitLoop As Boolean + Dim fMaxSpc As Float + 'Dim IndexKey As String = Str(Me.Id) &/ Str(DataIndex) + 'If Me.Tag = "**" Then Stop + 'D'abord utiliser la méthode du controle pour définir la taille + hSizeInt = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + 'Circuit court + If Me.Children.Count = 0 Then Return hSizeInt + + fSpc = IIf(Me._RelativeSpacing, TotalHeight * Me._Spacing / 100, Me._Spacing) + + ' Si ce n'est pas fait on enregistre la place nécéssaire a tout les éléments fixes + + If Not $fFixedSize Then + 'On ajoute la marge superieur du premier objet7 + 'If Me.tag = "**" Then Stop + For i = 0 To Me.Children.Max + hChild = Me.Children[i] + If hchild.Fixed Then + 'On ajoute la marge supérieur du premier objet fixe + If $fFixedSize = 0 Then $fFixedSize = hchild.Margin._Top + htmpInts = hChild._GetSizeHints(AvailableW, AvailableH, AvailableW, AvailableH, DataIndex) + fMaxSpc = Max(hchild.Margin._Bottom, fspc) + If i < Me.Children.Max Then fMaxSpc = Max(fMaxSpc, Me.Children[i + 1].Margin._Top) + $fFixedSize += htmpInts.Height + fMaxSpc + Endif + Next + 'If $fFixedSize Then $fFixedSize = Me.Children[0].Margin._Top + Endif + + 'La taille ne peut être inférieur a celle de tout les éléments fixes ou a défaut + 'a celle du premier élément. + hSizeInt.Height = Max(hSizeInt.Height, $fFixedSize) + + 'On ajoute la taille des objets fixes précédents + If $fFixedSize > 0 Then + fHeight = $fFixedSize + Else + 'On ajoute la marge supérieur du premier objet listé si aucun objet fixe n'est a l'Horizon + fHeight = Me.Children[Me._CurItem].Margin._Top + Endif + + 'If Me.Tag = "**" Then Stop + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + 'On définit la taille au besoins des enfants + For i = Me._CurItem To Me.Children.Max + hChild = Me.Children[i] + For j = hchild._DataIndex To hchild._Count - 1 + 'If hchild.Tag = "head" Then Print "VDataHint = " & DataIndex + If hChild.Ignore Then Continue + If hChild.Fixed Then Continue + hchild._Index = j + htmpInts = hChild._GetSizeHints(AvailableW, AvailableH - fHeight, AvailableW, AvailableH, j) + 'Si l'enfant n'est pas terminé alors moi non plus + If htmpInts.NotFinished Then hSizeInt.NotFinished = True + fMaxSpc = Max(fspc, hchild.Margin._Bottom) + If hchild._Count <= 0 Or If j = hchild._Count - 1 Then + If i < Me.Children.Max Then + fMaxSpc = Max(fMaxSpc, Me.Children[i + 1].Margin._Top) + Endif + Else + fMaxSpc = Max(fMaxSpc, hchild.Margin._Top) + Endif + + 'on ajoute la hauteur aux besoins + fHeight += htmpInts.Height + fMaxSpc + ' If Me.Tag = "**" Then + ' Print "fHeight , AvailableH : ", fHeight - fMaxSpc, AvailableH + ' Print "fMaxSpc : ", fMaxSpc + ' 'If fMaxSpc = 0 Then Stop + ' Endif + + If fHeight - fMaxSpc > AvailableH Then + 'Les enfants ne loge pas ... on ne peut pas finir + hSizeInt.NotFinished = True + bExitLoop = True + 'Print "Avec Parent : " & DataIndex & " Je loge : " & j + Break + Endif + 'On récupère la largeur de l'enfant le plus large + If fWidth < htmpInts.Width Then fWidth = htmpInts.Width + Next + If bExitLoop Then Break + Next + + 'on enlève le dernier espace + If fHeight > 0 Then fHeight -= fSpc + 'bogue bizzard + fHeight += 0.01 + + 'On indique la place nécéssaire aux enfants+les éléments fixes + hSizeInt.Height = fHeight + Me.Padding._Height + Me.Border._Top + Me.Border._Bottom + hSizeInt.Width = fWidth + Me.Padding._Width + Me.Border._Left + Me.Border._Right + Endif + + 'On ne peut pas dépasser la taille disponible (report sur prochaine page) + 'FIXME: Onepiece partially desactivated + If Not Me.OnePiece Then + hSizeInt.Height = Min(hSizeInt.Height, AvailableH) + Endif + + hSizeInt.Height = Min(TotalHeight, hSizeInt.Height) + + Me._SizeInt = hSizeInt + ' If Me.Tag = "Boite 2" Then + ' Print hSizeInt.Height + ' Stop + ' Endif + 'If Me.tag = "toto" Then Stop + ' If hSizeInt.NotFinished = False Then + ' Print Me.Tag & " : EST FINIT" + ' Endif + Return hSizeInt + +End + +Public Sub _Reset() + + Dim hChild As ReportControl + 'mise a zéro de l'index de suivit de progression + Me._CurItem = 0 + 'Mise a zéro de l'index de reproduction + Me._DataIndex = 0 + 'Netoyage du layout précédent + 'Me._PageChildren.Clear + 'Nettoyage récurssif des enfants + For Each hChild In Me.Children + + hChild._Reset + + Next + +End + + + + + + + + +Public Sub _SetChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + 'If Me.Tag = "*" Then Stop + 'Print "Geometry " & Object.Type(Me) + 'If ContPage > 0 Then Return + + Me._Index = TCont.Index + + If Me._CurItem > Me.Children.max Then + ' If Me Is ReportSection Then Stop + Me._CurItem = 0 + 'Me._DataIndex = 0 + Endif + + ' If $bIndexChange Then + ' Raise BeforeData + ' $bIndexChange = False + ' Endif + + Select Case _Arrangement + Case Arrange.Vertical + SetVChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + Case Arrange.Horizontal + SetHChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + Case Arrange.Column + Me._SetCChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + Case Arrange.Fill + SetFChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + Case Arrange.None + SetNChildGeometry(X, Y, W, H, ContPage, TCont, bInFixed) + End Select + Raise AfterData + +End + +Private Function SetFChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + + Dim hChild As ReportControl + Dim hTItem As TControl + Dim hChildHints As ReportSizeHints + Dim aPageItems As New TControl[] + Dim oChild As Object + Dim j As Integer + + X = Me.Padding._Left + Me.Border._Left + Y = Me.Padding._Top + Me.Border._Top + W = W - Me.Padding._Left - Me.Padding._Right - Me.Border._Left - Me.Border._Right + H = H - Me.Padding._Top - Me.Padding._Bottom - Me.Border._Top - Me.Border._Bottom + For Each hChild In Me.Children + hChildHints = hchild._GetSizeHints(W, H, W, H) + hTItem = New TControl + hTItem.Ctrl = hchild + hTItem.SizeHint = hChildHints + + Inc Me._CurItem + aPageItems.Add(hTItem) + + Next + For Each oChild In aPageItems + oChild._Index = hTItem.Index + hTItem._SetGeometry(X, Y, W, H) + hChild._SetChildGeometry(X, Y, W, H, ContPage, hTItem, bInFixed Or Me.Fixed) + If oChild Is ReportContainer Then + + If oChild._CurItem < oChild.Children.count Then + 'Print ochild.tag & " pas fini" + j = Me.Children.Find(oChild) + Me._CurItem = Min(j, Me._CurItem) + Endif + 'ne pas incrémenter la lecture des enfants si je suis dans un élément fixe + If bInFixed Then ochild._CurItem = 0 + End If + Next + + TCont._PageChildren[ContPage] = aPageItems + +End + +Private Function SetNChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + + Dim hChild As ReportControl + Dim ochild As Object + Dim hTItem As TControl + Dim hChildHints As ReportSizeHints + Dim aPageItems As New TControl[] + Dim iX, iY As Float + Dim J As Integer + + If Me.Children.Count = 0 Then Return + + X = Me.Padding._Left + Me.Border._Left + Y = Me.Padding._Top + Me.Border._Top + W = W - Me.Padding._Left - Me.Padding._Right - Me.Border._Left - Me.Border._Right + H = H - Me.Padding._Top - Me.Padding._Bottom - Me.Border._Top - Me.Border._Bottom + + For Each hChild In Me.Children + + hChildHints = hchild._GetSizeHints(W, H, W, H, TCont.Index) + hTItem = New TControl + hTItem.Ctrl = hchild + hTItem.SizeHint = hChildHints + Inc Me._CurItem + aPageItems.Add(hTItem) + + Next + + For Each hTItem In aPageItems + oChild = hTItem.Ctrl + hChildHints = hTItem.SizeHint + iX = X + IIf(oChild._RelativeLeft, W * oChild._Left / 100, oChild._Left) + iY = Y + IIf(oChild._RelativeTop, H * oChild._Top / 100, oChild._Top) + oChild._Index = hTItem.Index + hTItem._SetGeometry(iX, iY, hChildHints.Width, hChildHints.Height) + hChild._SetChildGeometry(iX, iY, hChildHints.Width, hChildHints.Height, ContPage, hTItem, bInFixed Or Me.Fixed) + + If oChild Is ReportContainer Then + + If oChild._CurItem < oChild.Children.count Then + j = Me.Children.Find(oChild) + Me._CurItem = Min(j, Me._CurItem) + Endif + 'ne pas incrémenterla lecture des enfants si je suis dans un élément fixe + If bInFixed Then ochild._CurItem = 0 + End If + Next + TCont._PageChildren[ContPage] = aPageItems + +End + +Private Sub SetHChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + + Dim aPageItems As New TControl[] ''Éléments contenu par cette page + Dim hChildHints As ReportSizeHints ''Besoins en hauteur/largeur de l'enfant + Dim hChild As ReportControl ''Un enfant reportcontrol + Dim oChild As Object + Dim TW, fWidth, fSpc As Float + Dim fExp As Float + Dim iNExp As Integer + + Dim i, j As Integer + Dim fTmpX, fX, fY As Float + Dim hTItem As TControl + Dim fMaxSpc As Float + Dim fChildY, fChildH As Float + 'Initialisation des variables + fSpc = IIf(Me._RelativeSpacing, H * Me._Spacing / 100, Me._Spacing) 'ME._Spacing + 'On retire les marges a la hauteur et les bordures + H = H - Me.Padding._Top - Me.Padding._Bottom - Me.Border._Top - Me.Border._Bottom + 'Print H + 'on retire a la largeur les paddings (et les bordure ?) + W = W - Me.Padding._Left - Me.Padding._Right - Me.Border._Left - Me.Border._Right + + 'On positionne le curseur de position au coin a gauche + X = Me.Padding._Left + Me.Border._Left + Y = Me.Padding._Top + Me.Border._Top + + 'On initialise la largeur totale avec la largeur disponible + TW = W + + For i = 0 To Me.Children.Max + + hChild = Me.Children[i] + + 'hchild._Index = IIf(Me.DataCount > 0, j, TCont.Index) + hChild._Index = TCont.Index + hChildHints = hChild._GetSizeHints(w, h, w, h, TCont.Index) + + 'Si l' éléments ne loge pas on quitte et on oublit + If TW - hChildHints.Width < 0 Then Break + + 'Si l'élément n'est pas ignoré + If Not hChild.Ignore Then + fMaxSpc = Max(fspc, hchild.Margin._Right) + If i < Me.Children.Max Then fMaxSpc = Max(fMaxSpc, Me.Children[i + 1].Margin._Left) + + TW = TW - hChildHints.Width - fMaxSpc + 'Si il est étendu on en tient compte + If hChild.Expand Then + Inc iNExp + fExp += hChildHints.Width + Endif + Endif + + 'On ajoute l'élément a la page + hTItem = New TControl + hTItem.Ctrl = hChild + hTItem.SizeHint = hChildHints + hTItem.Index = TCont.Index + aPageItems.Add(hTItem) + + Next + + 'De toute les façon je ne cherche pas a parcourir tout + 'Les objet donc j'indique que j'ai tout vu + Me._CurItem = Me.Children.count + + If (W - TW) > 0 Then + TW += fspc + Endif + + fTmpX = X + If aPageItems.Count > 0 Then + fTmpX += aPageItems[0].Ctrl.Margin._Left + fExp = fExp - aPageItems[0].Ctrl.Margin._Left '- aPageItems[aPageItems.Max].Ctrl.Margin._Right + Endif + + 'On va mettre en page a présent + 'On définit la taille des éléments étendus + If iNexp Then + fExp = (TW + fExp) / iNexp + Endif + + For i = 0 To aPageItems.Max + hTItem = aPageItems[i] + oChild = hTItem.Ctrl + 'Si l'élément est étendu on lui applique la taille répartie + If oChild.Expand And Not oChild.Ignore Then + 'If Me.Tag = "*" Then Stop + fWidth = fExp + Else + 'sinon il maintien sa taille + fWidth = hTItem.SizeHint.Width + Endif + 'oChild._Index = hTItem.Index + If Not oChild.Ignore Then + fChildH = H - ochild.Margin._Top - oChild.Margin._Bottom + fChildY = Y + oChild.Margin._Top + hTItem._SetGeometry(fTmpX, fChildY, fWidth, fChildH) + oChild._SetChildGeometry(fTmpX, fChildY, fWidth, fChildH, ContPage, hTItem, bInFixed Or Me.Fixed) + fMaxSpc = Max(fspc, ochild.Margin._Right) + If i < aPageItems.Max Then fMaxSpc = Max(fMaxSpc, aPageItems[i + 1].Ctrl.Margin._Left) + fTmpX += fWidth + fMaxspc + Else + fX = X + IIf(oChild._RelativeLeft, W * oChild._Left / 100, oChild._Left) + fY = Y + IIf(oChild._RelativeTop, H * oChild._Top / 100, oChild._Top) + hTItem._SetGeometry(fX, fY, fWidth, hTItem.SizeHint.Height) + oChild._SetChildGeometry(fX, fY, fWidth, hTItem.SizeHint.Height, ContPage, hTItem, bInFixed Or Me.Fixed) + Endif + + If oChild Is ReportContainer Then + If oChild._CurItem <= oChild.Children.max Then + 'Print ochild.tag & " pas fini" + j = Me.Children.Find(oChild) + Me._CurItem = Min(Me._CurItem, j) + Endif + 'ne pas incrémenter la lecture des enfants si je suis dans un élément fixe + If bInFixed Then ochild._CurItem = 0 + + Endif + Next + + 'On ajoute la page a la collection de page du conteneur + TCont._PageChildren[ContPage] = aPageItems + +End + +Private Function DataCount_Read() As Integer + + Return Me._Count + +End + +Private Sub DataCount_Write(Value As Integer) + 'If value = 0 Then value = 1 + + Me._Count = Value + +End + +' Public Function _GetIndex() As Integer +' +' If Me._Count > 0 Then 'Si c'est moi le répéteur alors je fournis mon index +' Return Me._Index +' Else +' Try Return Me.Parent._GetIndex 'Sinon je ontinue a remonter la lignée jusqu'au répéteur +' Endif +' +' End + +Private Function _Index_Read() As Integer + + Return Super._Index + +End + +Private Sub _Index_Write(Value As Integer) + + If Super._Index = Value And If Super._Index > 0 Then Return + $bIndexChange = True + Super._Index = Value + +End + +Private Function _CurItem_Read() As Integer + + Return $iCurItem + +End + +Private Sub _CurItem_Write(Value As Integer) + 'Print "_CurItem = " & Value + 'If Me.tag = "Boite 1" And Value = 0 Then Stop + 'If Me.tag = "Boite 1" And ContPage = 1 Then Print "CurItem: " & Me._CurItem + + $iCurItem = Value + +End + +Private Function OnePiece_Read() As Boolean + + Return $bOnePiece + +End + +Private Sub OnePiece_Write(Value As Boolean) + + $bOnePiece = Value + +End + +Public Sub _SetCChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + +End + +Private Sub SetVChildGeometry(X As Float, Y As Float, W As Float, H As Float, ContPage As Integer, TCont As TControl, bInFixed As Boolean) + + Dim aPageItems As New TControl[] ''Éléments contenu par cette page + Dim hChildHints As ReportSizeHints ''Besoins en hauteur/largeur de l'enfant + Dim hChild As ReportControl ''Un enfant reportcontrol + Dim fTH As Float ''Hauteur restante + Dim fSpc As Float ''Taille d'un espace + Dim hTItem As TControl ''Un objet virtuel + Dim oChild As Object ''Un objet gambas générique + Dim fExp As Float ''taille des objets étendus + Dim iNExp As Integer ''Nombre d'objets étendus + Dim ftmpHeight As Float ''Tampon pour le calcul de la taille répartie + Dim ftmpY As Float ''Curseur temporaire de position haute + Dim fX, fY As Float ''Tampon de position + Dim i, j As Integer ''Des indexs + Dim bExitLoop As Boolean ''Flag de sortie de traitement + Dim bForceNewPage As Boolean ''Flag d'anticipation de sortie 1 par page + Dim iPreIndex As Integer + Dim fMaxSpc As Float ''Résultat de la comparaison ds espaces (marges/spacing) + Dim fChildX, fChildW As Float + Dim iIndex As Integer + Dim bFirst As Boolean + 'Dim hSizeInt As Boolean + Dim bTop As Boolean + Dim fPrevTH As Float + Dim cChildFixe As New Collection + Dim iVal As Integer + 'Dim IndexKey As String = Str(Me.Id) &/ Str(TCont.Index) + + 'Détermination de la taille d'un espace + 'et prise en compte de la taille relative + fSpc = IIf(Me._RelativeSpacing, H * Me._Spacing / 100, Me._Spacing) + + 'Retrait du padding et des bordures de la hauteur de travail + H = H - Me.Padding._Top - Me.Border._Top - Me.Border._Bottom - Me.Padding._Bottom + + 'Retrait du padding et des bordures a la largeur de travail + W = W - Me.Padding._Left - Me.Border._Left - Me.Border._Right - Me.Padding._Right + + 'Positioinnement du curseur en haut a gauche + X = Me.Padding._Left + Me.Border._Left + Y = Me.Padding._Top + Me.Border._Top + + 'La hauteur restante est initialisée avec la hauteur de travail + fTH = H + + 'On énumère tout les éléments an tête en ne retenant que les fixes + For i = 0 To Me.Children.Max + hchild = Me.Children[i] + If hchild.Fixed Then + cChildFixe[j] = hchild + Inc j + Endif + Next + + j = 0 + + For i = 0 To Me.Children.Max + hchild = Me.Children[i] + 'If Not hchild._DataIndex.Exist(IndexKey) Then hchild._DataIndex[IndexKey] = 0 + If hchild.Fixed Then + + If Not bTop And If i < Me._CurItem Then + fTH = fTH - Me.Children[i].Margin._Top + bTop = True + Endif + 'trouver le fixe suivant + For j = i + 1 To Me._CurItem + If Me.Children[j].Fixed Then + iVal = j + Break + Endif + Next + 'Si le fixe suivant n'est pas définit alors on utlise l'objet suivant + If Not iVal Then iVal = i + 1 + + fMaxSpc = Max(hchild.Margin._Bottom, fspc) + If i < Me.Children.Max Then fMaxSpc = Max(fMaxSpc, Me.Children[i + 1].Margin._Top) + 'Calcul de l'espace interobjet (c'est a dire la plus grande valeur entre la marge basse de l'objet, + 'le spacing, et la marge haute de l'objet suivant.) + + hChildHints = hchild._GetSizeHints(W, fTH, W, H, TCont.Index) + + If Not hchild.Ignore Then fTH -= hChildHints.Height + fMaxSpc + If i < Me._CurItem Then + 'Génération de l'objet virtuel + hTItem = New TControl + 'Lier l'objet + hTItem.Ctrl = hchild + 'Associer sa taille + hTItem.SizeHint = hChildHints + 'TODO: Restorer + 'hTItem.Index = TCont.Index + hTItem.Index = iIndex + Inc iIndex + 'L'ajouter a la page + aPageItems.Add(hTItem) + + 'Si l'objet est étendu alors on en tient compte + 'sauf si celui-ci est ignoré par l'arrangement + If hchild.Expand And If Not hchild.Ignore Then + fExp += hChildHints.Height + Inc iNExp + Endif + + Endif + + Endif + Next + 'If ContPage = 2 And Me.Tag = "**" Then Stop + 'On parcour les élément de l'index a la fin + For i = Me._CurItem To Me.Children.Max + + If Not bTop Then + fTH = fTH - Me.Children[i].Margin._Top + bTop = True + Endif + hchild = Me.Children[i] + 'Je traite ici la boucle de clonage + 'On définit le point de départ + 'If ContPage = 1 And hchild.Tag = "**" Then Stop + j = hChild._DataIndex + 'hchild._PageDataIndex = hchild._DataIndex + Do + 'Calcul de l'espace interobjet (c'est a dire la plus grande valeur entre la marge basse de l'objet, + 'le spacing, et la marge haute de l'objet suivant.) + fMaxSpc = Max(hchild.Margin._Bottom, fspc) + 'Définit l'espace en fonction du contexte + If hchild._Count > 0 And If j <= hchild._count - 1 Then + fMaxSpc = Max(fMaxSpc, Me.Children[i].Margin._Top) + Else + If i < Me.Children.Max Then fMaxSpc = Max(fMaxSpc, Me.Children[i + 1].Margin._Top) + Endif + 'If Me.Tag = "**" And j = 3 Then Stop + 'If TCont.Index = 2 Then Stop + 'If ContPage = 2 And Me.Tag = "**" Then Stop + hChildHints = hchild._GetSizeHints(W, fTH, W, H, TCont.Index) + 'If ContPage = 2 And Me.Tag = "**" Then Print fTH, hChildHints.Height + 'Les éléments fixes ont déja été traités on ne tient donc pas compte de leur hauteur + 'car elle a déja été déduite de l'espace restant. De meme on ignore les objets flottants (ignore=true) + If Not hchild.Fixed And If Not hchild.Ignore Then + 'Si l'élément ne loge pas dans la place restante ou + 'si la place restante est insuffisante + 'on provoque la sortie en fin de boucle + If fTH - hChildHints.Height <= 0 Or If bForceNewPage Then + ' Debug "Lélément index : " & j & " Ne loge pas dans le conteneur" + ' Debug "fTH : " & fTH + ' Debug "fTH - hChildHints.Height : " & (fTH - hChildHints.Height) + ' Print + If Not bFirst Then + 'SI il est le premier de la série alors il doit loger dans la page coute que coute + hChildHints.Height = fTH - hchild.Margin._bottom + Else + + bForceNewPage = False + hchild._DataIndex = j + + bExitLoop = True + Break + Endif + Endif + bFirst = True + fTH = fTH - hChildHints.Height - fMaxSpc + Endif + 'On l'ajoute a la page + 'Génération de l'objet virtuel + hTItem = New TControl + 'Lier l'objet + hTItem.Ctrl = hchild + 'Associer sa taille + hTItem.SizeHint = hChildHints + 'TODO: Restorer + 'hTItem.Index = TCont.Index + 'Print hchild._count + hTItem.Index = IIf(hchild._count = 1, TCont.Index, j) 'iIndex + ' Print hchild.Tag + ' Print "Set J= " & hTItem.Index + Inc iIndex + 'L'ajouter a la page + aPageItems.Add(hTItem) + + 'Si l'objet est étendu alors on en tient compte + 'sauf si celui-ci est ignoré par l'arrangement + If hchild.Expand And If Not hchild.Ignore Then + fExp += hChildHints.Height + Inc iNExp + Endif + 'If hChildHints.NotFinished Then hchild._DataIndex = j + 'un élément fixe ou ignore ne peut être répété + If hchild.Fixed Or hchild.Ignore Then Break + + 'Sachant que hChild._count peut être à -1 on le considère dans ce cas la comme étant a 0 + 'si j est égale au compte alors on quitte la boucle + If j >= Max(hchild._Count - 1, 0) Then + 'Remise a 0 si le contenu de l 'enfant est entièrement affiché + If Not hChildHints.NotFinished Then + hChild._DataIndex = 0 + Endif + Break + Endif + 'sinon on incrémente le compte a condition que le dernier enfant aie finit d'afficher ses enfants + If Not hChildHints.NotFinished Then Inc j + + 'On prévoie un sortie de boucle si l'enfant demande un affichage 1 par page + If hchild.ForceNewPage Then bForceNewPage = True + + ' If fTH = 0 Then + ' hchild._DataIndex = j + ' Break + ' bExitLoop = True + ' Endif + + Loop + 'Si la sortie anticipé est demandée alors on sort de la boucle + 'If bExitLoop Then Stop + If bExitLoop Then Break + + 'Si l'objet suivant est un objet spécial de rupture de page alors + 'On force la rupture de page + If i < Me.Children.Max And If Me.Children[i + 1] Is ReportPageBreak Then + Inc Me._CurItem + bForceNewPage = True + Inc i 'on saute l'objet. + Endif + + 'Enfin on incrémente la variable de saugarde de l'objet en cour. + 'If Me.Tag = "**" Then Debug "Inc" + Inc Me._CurItem + + Next + '*************************************************************************** + 'Pour tous les éléments fixes jusqu'à la fin du document + For i = Max(Me._CurItem, 0) To Me.Children.Max + hchild = Me.Children[i] + If hchild.Fixed Then + fMaxSpc = Max(hchild.Margin._Bottom, fspc) + If i < Me.Children.Max Then fMaxSpc = Max(fMaxSpc, Me.Children[i + 1].Margin._Top) + hChildHints = hchild._GetSizeHints(W, fPrevTH, W, H, iPreIndex) + 'on génère l'objet virtuel + hTItem = New TControl + 'On associe le controle + hTItem.Ctrl = hchild + 'On associe sa taille + hTItem.SizeHint = hChildHints + 'NOTE: Restorer + hTItem.Index = iIndex + Inc iIndex + 'On l'ajoute a la page + aPageItems.Add(hTItem) + + 'Si l'objet est étendu alors on tien compte de sa taille + 'pour le calcul de l'espace réparti + If hchild.Expand Then + fExp += hChildHints.Height + Inc iNExp + Endif + fPrevTH = fPrevTH - fMaxSpc + Endif + Next + + 'Si on est en mode spacing alors le dernier expace doit être enlevé + If fspc > 0 And If fMaxSpc = fspc Then + fTH += fSpc + Endif + + '****************************************************************************** + 'A présent tous les éléments pouvant être placé sur la page ont été marqué. + 'On peut donc procéder a la mise en forme de ceux-ci + + ftmpY = Y 'On définit la position de départ + If aPageItems.Count > 0 Then + fTmpY += aPageItems[0].Ctrl.Margin._Top + 'fExp = fExp - aPageItems[0].Ctrl.Margin._Top '- aPageItems[aPageItems.Max].Ctrl.Margin._Right + Endif + If iNExp Then + fExp = (fExp + fTH) / iNExp + Endif + + For i = 0 To aPageItems.Max + 'on récupère l'objet virtuel + hTItem = aPageItems[i] + 'on récupère l'instance de l'objet réel + ochild = hTItem.Ctrl + + 'Si l'objet est étendu alors on calcul sa hauteur + 'Un objet flottant ne peut pas être étendu + If Not oChild.Ignore And If ochild.Expand Then + 'Calcul de la taille répartie + ftmpHeight = fExp + Else + 'Si l'objet n'est pas étendu alors sa taille est celle demandée par celui-ci + ftmpHeight = hTItem.SizeHint.Height + Endif + 'oChild._Index = hTItem.Index + 'Traitement des objets ignoré + 'Les propriété de taille sont celle fournie par l'objet + If oChild.Ignore Then + 'Calcul de la position de l'objet + 'si sa position est relative (%) alors on fait le ratio a partir de la largeur ou de la hauteur + 'sinon on utilise la position fournie par l'objet + fX = X + IIf(oChild._RelativeLeft, W * oChild._Left / 100, oChild._Left) + fY = Y + IIf(oChild._RelativeTop, H * oChild._Top / 100, oChild._Top) + 'Fixer la position de l'objet + hTItem._SetGeometry(fX, fY, hTItem.SizeHint.Width, ftmpHeight) + 'L'objet flottant peut être un conteneur il faut donc demander aussi a ses enfants de s 'organiser + oChild._SetChildGeometry(fX, fY, hTItem.SizeHint.Width, ftmpHeight, ContPage, hTItem, bInFixed Or Me.Fixed) + + Else + fChildX = X + oChild.Margin._Left + fChildW = W - oChild.Margin._Left - oChild.Margin._Right + 'Fixer la position de l'objet + hTItem._SetGeometry(fChildX, fTmpY, fChildW, ftmpHeight) + 'L'objet flottant peut être un conteneur il faut donc demander aussi a ses enfants de s 'organiser + oChild._SetChildGeometry(fChildX, fTmpY, fChildW, ftmpHeight, ContPage, hTItem, bInFixed Or Me.Fixed) + 'Calcul de l'espace interobjet (c'est a dire la plus grande valeur entre la marge basse de l'objet, + 'le spacing, et la marge haute de l'objet suivant.) + fMaxSpc = Max(ochild.Margin._Bottom, fspc) + If i < aPageItems.Max Then fMaxSpc = Max(fMaxSpc, aPageItems[i + 1].Ctrl.Margin._Top) + 'on incrémente la position verticale de l'objet + 'pour définir sa place sur la page relativement aux autres éléments + fTmpY += ftmpHeight + fMaxSpc + + Endif + + 'Si l'objet est un conteneur alors il faut vérifier si il a finit d'être traité + + If oChild Is ReportContainer Then + 'If oChild._NotFinished Then Me.NotFinished = True + 'Si son traitement n'est pas terminé alors on trouve + 'sa position dans la liste des enfant et on l'assigne a l'index de traitement + 'si sa position est > a l'index courant + 'If oChild.Tag = "VB1" Then Stop + If ochild._CurItem <= oChild.Children.Max Then + j = Me.Children.Find(oChild) + Me._CurItem = Min(Me._CurItem, j) + Endif + 'Si on est dans la lignée d'un conteneur fixe on incrémente pas la lecture des enfants + 'En clair ce sont toujours les mêmes éléments qui apparaitrons + If bInFixed Then ochild._CurItem = 0 + Endif + Next + 'On ajoute cette page au dossier du conteneur + TCont._PageChildren[ContPage] = aPageItems + +End diff --git a/comp/src/gb.report2/.src/ReportControl.class b/comp/src/gb.report2/.src/ReportControl.class new file mode 100644 index 00000000..9e7f9cf2 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportControl.class @@ -0,0 +1,648 @@ +' Gambas class file + +Export +Create Private + +Public Const _IsControl As Boolean = True +Public Const _IsContainer As Boolean = False +Public Const _Properties As String = "Left{ReportCoord},Top{ReportCoord},Width{ReportCoord},Height{ReportCoord},Brush{ReportBrush},Visible=True,Fixed,Font,Padding,Margin,Ignore,Expand,AutoResize,Tag,Range" +Public Const _Family As String = "Report" +Public _SizeInt As ReportSizeHints +Public _Count As Integer = 1 +Public _Finished As Boolean +Private $iDataindex As Integer + +Private $iIndex As Integer +Public _PageDataIndex As Integer +Public Name As String +'Object Management +Static Public _ObjectFromId As New Collection +Static Public _iCurPagePos As Integer +Static Private $iLastId As Integer +Static Private $aFont As String[] = ["DejaVu Serif", "Liberation Serif", "Bitstream Vera Serif", "Serif", "Arial"] + +Property Read Id As Integer +Property Read Parent As ReportContainer +Property Tag As Variant +Property Padding As ReportPadding +Property Margin As ReportMargin +Property Brush As ReportBrush +'Property {Color} As Integer + +Property Read _Top As Float ''Top of the control in cm +Property Read _Height As Float ''Height of the control in cm +Property Read _Width As Float ''Width of the control in cm +Property Read _Left As Float ''Left of the control in cm +Property Read _RelativeLeft As Boolean ''Use percentage for Left pos? +Property Read _RelativeTop As Boolean ''Use percentage for Top Pos? +Property Read _RelativeWidth As Boolean ''Use percentage for width? +Property Read _RelativeHeight As Boolean ''Use percentage for height ? +Property _Index As Integer +Property Range As String +Property Left As String +Property Top As String +Property X As String +Property Y As String +Property Width As String +Property Height As String +Property Visible As Boolean +Property {Font} As Font +Property Expand As Boolean +Property Ignore As Boolean +Property Fixed As Boolean +Property Autoresize As Boolean +Property Read {Report} As Report +Property _ReportId As Integer +Property Read DataIndex As Integer +Property ForceNewPage As Boolean +Property _DataIndex As Integer +'Public _DataIndex As New Collection +Private $bForceNewPage As Boolean +Private $iMyId As Integer +Private $iParentId As Integer +Private $vTag As Variant +Private $iReportId As Integer +Private $hBrush As ReportBrush +Private $iColor As Integer +Private $fLeft As Float = 0.0 +Private $fTop As Float = 0.0 +Private $fWidth As Float = 0.0 +Private $fHeight As Float = 0.0 +Private $sHeight As String = "0cm" +Private $sLeft As String = "0cm" +Private $sWidth As String = "0cm" +Private $sTop As String = "0cm" +Private $hPadding As New ReportPadding +Private $hMargin As New ReportMargin +Private $iVisible As Boolean = True +Private $hFont As New Font +Private $bExpand As Boolean +Private $bFixed As Boolean +Private $bAutoresize As Boolean = False +Private $bRelativeLeft As Boolean +Private $bRelativeTop As Boolean +Private $bRelativeWidth As Boolean +Private $bRelativeHeight As Boolean +Private $bIgnore As Boolean = False +Private $sRange As String + +Public Sub _New(Optional Parent As ReportContainer = Null) + + Dim hRep As Report + + $iMyId = $iLastId + Inc $iLastId + If IsNull(Parent) Then + $iParentId = -1 + $iReportId = $iMyId + + Else + If Parent Is Report Then + hRep = Parent + $iParentId = hRep._Container.id + hRep._Container._Add(Me) + $iReportId = hRep.id + Else + $iParentId = Parent.Id + Parent._Add(Me) + $iReportId = Parent._ReportId + Endif + Endif + +End + +Private Function Id_Read() As Integer + + Return $iMyId + +End + +Private Function Parent_Read() As ReportContainer + + Return ReportControl._ObjectFromId[$iParentId] + +End + +Private Function Left_Read() As String + + Return $sLeft + +End + +Private Sub Left_Write(Value As String) + + $sLeft = Value + Me.Report._LayoutIsDirty = True + +End + +Private Function Top_Read() As String + + Return $sTop + +End + +Private Sub Top_Write(Value As String) + + $sTop = Value + Me.Report._LayoutIsDirty = True + +End + +Private Function Width_Read() As String + + Return $sWidth + +End + +Private Sub Width_Write(Value As String) + + $sWidth = Value + Me.Report._LayoutIsDirty = True + +End + +Private Function Height_Read() As String + + Return $sHeight + +End + +Private Sub Height_Write(Value As String) + + $sHeight = Value + Me.Report._LayoutIsDirty = True + +End + +Private Function Expand_Read() As Boolean + + Return $bExpand + +End + +Private Sub Expand_Write(Value As Boolean) + + $bExpand = Value + Me.Report._LayoutIsDirty = True + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, TotalWidth As Float, TotalHeight As Float, DataIndex As Integer) As ReportSizeHints + + 'Error.Raise("Something goes wrong the _GetSizeHints is not correctly implemented") + 'Implementaion standart du sizeInt hors autoresize et hbox + ' + + Dim hMyHints As New ReportSizeHints + + If Me._RelativeHeight Then + hMyHints.Height = TotalHeight * Me._Height / 100 + Else + hMyHints.Height = Me._Height + Endif + + If Me._RelativeWidth Then + hMyHints.Width = TotalWidth * Me._Width / 100 + Else + hMyHints.Width = Me._Width + Endif + Return hMyHints + +End + +Public Sub _PaintBeFore((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + +End + +Public Sub _PaintFrame((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + + Me._Paint(Page, X, Y, hControl, DataIndex) + +End + +Public Sub _PaintAfter((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + +End + +Private Function Visible_Read() As Boolean + + Return $iVisible + +End + +Private Sub Visible_Write(Value As Boolean) + + $iVisible = Value + Me.Report._LayoutIsDirty = True + +End + +Public Sub Move(X As Float, Y As Float, W As Float, H As Float) + + $fLeft = X + $fTop = Y + $fWidth = W + $fHeight = H + Me.Report._LayoutIsDirty = True + +End + +Private Function Tag_Read() As Variant + + Return $vTag + +End + +Private Sub Tag_Write(Value As Variant) + + $vTag = Value + +End + +Private Function Font_Read() As Font + + Dim hFont As Font + 'Dim sFont As String + Dim s As String + + If Not $hFont Then + If $iParentId = -1 Then + For Each s In $aFont + If Fonts.Exist(s) Then + hFont = New Font + hFont.Name = s + Break + Endif + Next + If hFont Then + hFont.Size = 12 + Else + hFont = Paint.Font.Copy + Endif + + Else + hFont = _ObjectFromId[$iParentId].font + Endif + Return hFont + Else + Return $hFont + Endif + +End + +Private Sub Font_Write(Value As Font) + + $hFont = Value '.Copy() + Me.Report._LayoutIsDirty = True + +End + +Private Function Padding_Read() As ReportPadding + + Return $hPadding + +End + +Private Sub Padding_Write(Value As ReportPadding) + + $hPadding = Value + Me.Report._LayoutIsDirty = True + +End + +Private Function _Top_Read() As Float + + Return $fTop + +End + +Private Function _Height_Read() As Float + + Return $fHeight + +End + +Private Function _Width_Read() As Float + + Return $fWidth + +End + +Private Function _Left_Read() As Float + + Return $fLeft + +End + +Public Function _SetChildGeometry((X) As Float, (Y) As Float, (W) As Float, (H) As Float, (ContPage) As Integer, (TCont) As TControl, (bInFixed) As Boolean) + +End + +Private Function Autoresize_Read() As Boolean + + Return $bAutoresize + +End + +Private Sub Autoresize_Write(Value As Boolean) + + $bAutoresize = Value + Me.Report._LayoutIsDirty = True + +End + +Private Function _RelativeWidth_Read() As Boolean + + Return $bRelativeWidth + +End + +Private Function _RelativeHeight_Read() As Boolean + + Return $bRelativeHeight + +End + +''Convert recursively string values to unified values in cm +Public Sub _NormalizeUnits() + + Dim hSizeParse As TSizeParse + + 'Left + hSizeParse = New TSizeParse($sLeft, True) + $fLeft = hSizeParse.GetValue() + $bRelativeLeft = hSizeParse.IsRelative() + + 'Right + hSizeParse = New TSizeParse($sTop, True) + $fTop = hSizeParse.GetValue() + $bRelativeTop = hSizeParse.IsRelative() + + 'Width + hSizeParse = New TSizeParse($sWidth, True) + $fWidth = hSizeParse.GetValue() + $bRelativeWidth = hSizeParse.IsRelative() + + 'Height + hSizeParse = New TSizeParse($sHeight, True) + $fHeight = hSizeParse.GetValue() + $bRelativeHeight = hSizeParse.IsRelative() + + 'Padding + ' hSizeParse = New TSizeParse($sPadding, True) + ' $fPadding = hSizeParse.GetValue() + ' $bRelativePadding = hSizeParse.IsRelative() + + hSizeParse = New TSizeParse($hPadding.Left, True) + $hPadding._Left = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hPadding.Right, True) + $hPadding._Right = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hPadding.Top, True) + $hPadding._Top = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hPadding.Bottom, True) + $hPadding._Bottom = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hMargin.Left, True) + $hMargin._Left = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hMargin.Right, True) + $hMargin._Right = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hMargin.Top, True) + $hMargin._Top = hSizeParse.GetValue() + + hSizeParse = New TSizeParse($hMargin.Bottom, True) + $hMargin._Bottom = hSizeParse.GetValue() + + ' + +End + +Private Function Ignore_Read() As Boolean + + Return $bIgnore + +End + +Private Sub Ignore_Write(Value As Boolean) + + $bIgnore = Value + Me.Report._LayoutIsDirty = True + +End + +Private Function Report_Read() As Report + + Return ReportControl._ObjectFromId[$iReportId] + +End + +Private Function _ReportId_Read() As Integer + + Return $iReportId + +End + +Private Function Fixed_Read() As Boolean + + Return $bFixed + +End + +Private Sub Fixed_Write(Value As Boolean) + + $bFixed = Value + +End + +Public Sub _Reset() + + Me._DataIndex = 0 + +End + +Private Function X_Read() As String + + Return $sLeft + +End + +Private Sub X_Write(Value As String) + + $sLeft = Value + +End + +Private Function Y_Read() As String + + Return $sTop + +End + +Private Sub Y_Write(Value As String) + + $sTop = Value + +End + +Private Function Brush_Read() As ReportBrush + + Return $hBrush + +End + +Private Sub Brush_Write(Value As ReportBrush) + + $hBrush = Value + +End + +' Private Function Color_Read() As Integer +' +' Return $iColor +' +' End +' +' Private Sub Color_Write(Value As Integer) +' +' $iColor = Value +' +' End + +Public Function _GetActualBrush(X1 As Integer, Y1 As Integer, X2 As Integer, Y2 As Integer) As PaintBrush + + Dim hBrush As PaintBrush + + If $hBrush Then + hBrush = $hBrush._PaintBrush(X1, Y1, X2, Y2) + Else If $iColor Then + hBrush = Paint.Color($iColor) + Else + Try hBrush = Me.Parent._GetActualBrush(X1, Y1, X2, Y2) + If Not hBrush Then + hBrush = Paint.Color(0) + Endif + Endif + Return hBrush + +End + +Private Sub _ReportId_Write(Value As Integer) + + $iReportId = Value + +End + +Private Function _RelativeLeft_Read() As Boolean + + Return $bRelativeLeft + +End + +Private Function _RelativeTop_Read() As Boolean + + Return $bRelativeTop + +End + +Private Function DataIndex_Read() As Integer + + Return Me._Index 'Return Me._GetIndex() + +End + +Private Function Range_Read() As String + + Return $sRange + +End + +Private Sub Range_Write(Value As String) + + $sRange = Value + +End + +Public Function _GetIndex() As Integer + + Return Me._Index + 'Return Me.Parent._GetIndex() + +End + +Private Function _Index_Read() As Integer + + Return $iIndex + +End + +Private Sub _Index_Write(Value As Integer) + + $iIndex = Value + +End + +Private Function _DataIndex_Read() As Integer + + Return $iDataindex + +End + +Private Sub _DataIndex_Write(Value As Integer) + 'If Me.Tag = "Boite 2" And Value = 0 Then Stop + 'If Me.Report.$iCurPage = 0 Then Print "DataIndex: " & value + 'If Value = 0 Then Stop + + $iDataindex = Value + 'Stop + 'Print "_DataIndex " & Me.Tag & " = " & $iDataindex + +End + +Private Function ForceNewPage_Read() As Boolean + + Return $bForceNewPage + +End + +Private Sub ForceNewPage_Write(Value As Boolean) + + $bForceNewPage = Value + +End + +Public Sub _Free() + +End + +Private Function Margin_Read() As ReportMargin + + Return $hMargin + +End + +Private Sub Margin_Write(Value As ReportMargin) + + $hMargin = Value + +End + +Public Sub Raise() + + _ObjectFromId[$iParentId]._Raise(Me) + +End + +Public Sub Lower() + + _ObjectFromId[$iParentId]._Lower(Me) + +End diff --git a/comp/src/gb.report2/.src/ReportDrawingArea.class b/comp/src/gb.report2/.src/ReportDrawingArea.class new file mode 100644 index 00000000..d9b9deae --- /dev/null +++ b/comp/src/gb.report2/.src/ReportDrawingArea.class @@ -0,0 +1,96 @@ +' Gambas class file + +Export +Inherits ReportFrame +Public Const _Properties As String = "*,Cached" +Public Const _Similar As String = "ReportImage" +Public Const _DefaultEvent As String = "Draw" +Public SizeHints As ReportSizeHints +Property Cached As Boolean + +Private $bCached As Boolean +Private $iOldResolution As Integer +Event Layout(Width As Float, Height As Float, Index As Integer) +Event Draw(Width As Float, Height As Float, Index As Integer) + + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + Dim hMySize As ReportSizeHints + SizeHints = Null + Raise Layout(AvailableW, AvailableH, DataIndex) + + hMySize = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + If SizeHints <> Null Then + hMySize.Width = SizeHints.Width + hMySize.Height = SizeHints.Height + Endif + Endif + Try hMySize.NotFinished = SizeHints.NotFinished + Return hMySize + + +End + + + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + 'Dim hRect As Rect + Dim iX, iY, iW, iH As Integer + Dim hImg As Image + + iX = (x + hControl.RealLeft + Me.Report._ToPixels(Me.Padding._Left + Me.Border._Left)) + iY = (y + hControl.RealTop + Me.Report._ToPixels(Me.Padding._Top + Me.Border._Top)) + iW = (hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + iH = (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + Paint.Save + + + If $bCached Then + If $iOldResolution <> Paint.ResolutionX Or If hControl.Cache = Null Then + $iOldResolution = Paint.ResolutionX + hImg = New Image(iW, iH, Color.Transparent) + Paint.Begin(hImg) + Raise Draw(iW, iH, DataIndex) + Paint.End + hControl.Cache = himg + Else + 'If hControl.cache = Null Then Stop + Try hImg = hControl.Cache + Endif + Draw.Image(hImg, iX, iy) + Else + + 'Paint.Translate(iX, iY) + 'Print "Paint.Begin: "; iX;; iY;; iW;; iH;; "(";; Paint.W;; Paint.H; ")" + Paint.Begin(Paint.Device, RectF(iX, iY, iW, iH)) + Raise Draw(iW, iH, DataIndex) + Paint.End + 'Paint.Translate(- iX, - iY) + Endif + Paint.Restore + +End + +Private Function Cached_Read() As Boolean + + Return $bCached + +End + +Private Sub Cached_Write(Value As Boolean) + + $bCached = Value + +End + +' Public Sub SetHints(Width As Integer, Height As Integer, NotFinished As Boolean) +' $hSizeHints = New ReportSizeHints +' $hSizeHints.Width = Width +' $hSizeHints.Height = Height +' $hSizeHints.NotFinished = NotFinished +' +' End + diff --git a/comp/src/gb.report2/.src/ReportFrame.class b/comp/src/gb.report2/.src/ReportFrame.class new file mode 100644 index 00000000..81479f05 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportFrame.class @@ -0,0 +1,366 @@ +' Gambas class file + +Export +Create Private +Inherits ReportControl +' Static Private iLevel As Integer +Public Const _Properties As String = "*,Border,Background{ReportBrush},BoxShadow" +Private $hBorder As New ReportBorder +Private $hBackGround As ReportBrush + +Property Border As ReportBorder +Property BoxShadow As ReportBoxShadow + +Private $hBoxShadow As New ReportBoxShadow +Property BackGround As ReportBrush +'Property Read _BorderWidth As Float + +Private Function Border_Read() As ReportBorder + + Return $hBorder + +End + +Private Sub Border_Write(Value As ReportBorder) + + $hBorder = Value + +End + +Public Sub _PaintBefore((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + + Dim X1, Y1, X2, Y2, W, H As Float + Dim iBorder As Integer + Dim hShadowActive As Boolean = $hBoxShadow._Active + Dim himgShadow As Image + Dim fSpread As Float + Dim fBlur As Float + Dim fbx As Float + Dim fby As Float + Dim TL1, TL2, TR1, TR2, BR1, BR2, BL1, BL2 As Float + ' If Me.Tag = "**" Then Stop + 'If Me.tag = "*" Then Stop + 'Return + X1 = (x + hControl.RealLeft) + Y1 = (y + hControl.RealTop) + W = hControl.RealWidth + H = hControl.RealHeight + X2 = (x + hControl.RealLeft + W) + Y2 = (y + hControl.RealTop + H) + + 'Clipping + paint.save + + If Report.Debug Then + + Paint.Brush = Paint.Color(Color.Green) + + Paint.Rectangle(X1, Y1, X2 - X1, Y2 - Y1) + Paint.Stroke + + Else + + If $hBorder.RoundCorner._Active Then + TL1 = Me.Report._ToPixels($hBorder.RoundCorner._TopLeft1) + TR1 = Me.Report._ToPixels($hBorder.RoundCorner._TopRight1) + BR1 = Me.Report._ToPixels($hBorder.RoundCorner._BottomRight1) + BL1 = Me.Report._ToPixels($hBorder.RoundCorner._BottomLeft1) + TL2 = Me.Report._ToPixels($hBorder.RoundCorner._TopLeft2) + TR2 = Me.Report._ToPixels($hBorder.RoundCorner._TopRight2) + BR2 = Me.Report._ToPixels($hBorder.RoundCorner._BottomRight2) + BL2 = Me.Report._ToPixels($hBorder.RoundCorner._BottomLeft2) + + If hShadowActive Then + fbx = Me.Report._ToPixels($hBoxShadow._XOffset) + fby = Me.Report._ToPixels($hBoxShadow._YOffset) + fSpread = Me.Report._ToPixels($hBoxShadow._Spread) + If $hBoxShadow._Blur > 0 Then + fBlur = Me.Report._ToPixels($hBoxShadow._Blur) + himgShadow = New Image(W + fSpread * 2 + fBlur * 2, H + fSpread * 2 + fBlur * 2, Color.Transparent) + Paint.Begin(himgShadow) + Paint.Brush = Paint.Color($hBoxShadow.Color) + RoundRect(fBlur, fBlur, Paint.Width - fBlur * 2, Paint.Height - fBlur * 2, [TL1, TR1, BR1, BL1], [TL2, TR2, BR2, BL2]) + Paint.Fill + Paint.End + himgShadow.Fuzzy(fBlur) + Draw.Image(himgShadow, X1 + fbx - fSpread - fBlur, Y1 + fby - fSpread - fBlur) + Else + + Paint.Brush = Paint.Color($hBoxShadow.Color) + RoundRect(X1 + fbx - fSpread, Y1 + fby - fSpread, W + fSpread * 2, H + fSpread * 2, [TL1, TR1, BR1, BL1], [TL2, TR2, BR2, BL2]) + Paint.Fill + Endif + Endif + + iBorder = Me.Report._ToPixels($hBorder._Top) + + RoundRect(X1 + iBorder, Y1 + iBorder, W - iBorder * 2, H - iBorder * 2, [TL1, TR1, BR1, BL1], [TL2, TR2, BR2, BL2]) + + Else + + If hShadowActive Then + fbx = Me.Report._ToPixels($hBoxShadow._XOffset) + fby = Me.Report._ToPixels($hBoxShadow._YOffset) + fSpread = Me.Report._ToPixels($hBoxShadow._Spread) + If $hBoxShadow._Blur > 0 Then + fBlur = Me.Report._ToPixels($hBoxShadow._Blur) + himgShadow = New Image(W + fSpread * 2 + fBlur * 2, H + fSpread * 2 + fBlur * 2, Color.Transparent) + Paint.Begin(himgShadow) + Paint.Brush = Paint.Color($hBoxShadow.Color) + Paint.rectangle(fBlur, fBlur, Paint.Width - fBlur * 2, Paint.Height - fBlur * 2) + Paint.Fill + Paint.End + himgShadow.Fuzzy(fBlur) + Draw.Image(himgShadow, X1 + fbx - fSpread - fBlur, Y1 + fby - fSpread - fBlur) + Else + Paint.Brush = Paint.Color($hBoxShadow.Color) + Paint.Rectangle(X1 + fbx - fSpread, Y1 + fby - fSpread, W + fSpread * 2, H + fSpread * 2) + Paint.Fill + Endif + Endif + + Paint.Rectangle(X1, Y1, X2 - X1, Y2 - Y1) + + Endif + + If Me.BackGround = Null Then + If hShadowActive Then + Paint.Clip(True) + Paint.Brush = _GetActualBackGround(X1, Y1, X2, Y2) + Paint.Fill + Else + paint.Clip + Endif + 'Return + Else + paint.Clip(True) + paint.Brush = Me.BackGround._PaintBrush(X1, Y1, X2, Y2) + Paint.Fill + Endif + + Endif + +End + +Public Function _GetActualBackGround(X1 As Integer, Y1 As Integer, X2 As Integer, Y2 As Integer) As PaintBrush + + Dim hBackground As PaintBrush + + If $hBackGround Then + hBackground = $hBackGround._PaintBrush(X1, Y1, X2, Y2) + Else + Try hBackground = Me.Parent._GetActualBackGround(X1, Y1, X2, Y2) + If Not hBackground Then + hBackground = Paint.Color(Color.White) + Endif + Endif + Return hBackground + +End + + +Public Sub _PaintFrame((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + + _PaintBefore(Page, X, Y, hControl, DataIndex) + Me._Paint(Page, X, Y, hControl, DataIndex) + _PaintAfter(Page, X, Y, hControl, DataIndex) + +End + +Public Sub _PaintAfter((Page) As Integer, (X) As Float, (Y) As Float, (hControl) As TControl, (DataIndex) As Integer) + + Dim X1, Y1, X2, Y2, W, H As Float + 'Dim fWidth As Float + + Dim fTopWidth, fBottomWidth, fLeftWidth, fRighWidth As Float + 'Return + 'Fin du clipping + paint.Restore + + X1 = (x + hControl.RealLeft) + Y1 = (y + hControl.RealTop) + X2 = (x + hControl.RealLeft + hControl.RealWidth) + Y2 = (y + hControl.RealTop + hControl.RealHeight) + 'If Me.Tag = "**" Then Stop + If $hBorder.RoundCorner._Active Then + Paint.AntiAlias = True + fLeftWidth = Me.Report._ToPixels($hBorder._Left) / 2 + paint.LineWidth = fLeftWidth * 2 + paint.Brush = $hBorder.Left.Brush._PaintBrush(X1, Y1, X2, Y2) + ' RoundRect(Paint.LineWidth / 2, Paint.LineWidth / 2, Paint.Width - Paint.LineWidth, Paint.Height - Paint.LineWidth, aX, aY) + RoundRect(X1 + fLeftWidth, Y1 + fLeftWidth, hControl.RealWidth - fLeftWidth * 2, hControl.RealHeight - fLeftWidth * 2, + [Me.Report._ToPixels($hBorder.RoundCorner._TopLeft1), + Me.Report._ToPixels($hBorder.RoundCorner._TopRight1), + Me.Report._ToPixels($hBorder.RoundCorner._BottomRight1), + Me.Report._ToPixels($hBorder.RoundCorner._BottomLeft1)], + [Me.Report._ToPixels($hBorder.RoundCorner._TopLeft2), + Me.Report._ToPixels($hBorder.RoundCorner._TopRight2), + Me.Report._ToPixels($hBorder.RoundCorner._BottomRight2), + Me.Report._ToPixels($hBorder.RoundCorner._BottomLeft2)]) + paint.Stroke + + Else + 'Paint.AntiAlias = False + fLeftWidth = Me.Report._ToPixels($hBorder._Left) + fRighWidth = Me.Report._ToPixels($hBorder._Right) + fTopWidth = Me.Report._ToPixels($hBorder._Top) + fBottomWidth = Me.Report._ToPixels($hBorder._Bottom) + + 'If $hBorder.Style <> Line.None Then + + 'fWidth = Me.Report._ToPixels($fBorderWidth) + + 'Paint.Brush = Paint.Color($hBorder.Brush._iValue) + + If fTopWidth > 0 Then + paint.Brush = $hBorder.Top.Brush._PaintBrush(X1, Y1, X2, Y2) + paint.Rectangle(X1, Y1, X2 - X1, fTopWidth) + paint.Fill + Endif + If fRighWidth > 0 Then + paint.Brush = $hBorder.Right.Brush._PaintBrush(X1, Y1, X2, Y2) + paint.Rectangle(X2 - fRighWidth, Y1, fRighWidth, Y2 - Y1) + paint.Fill + Endif + If fBottomWidth > 0 Then + paint.Brush = $hBorder.Bottom.Brush._PaintBrush(X1, Y1, X2, Y2) + paint.Rectangle(X1, Y2 - fBottomWidth, X2 - X1, fBottomWidth) + paint.Fill + Endif + If fLeftWidth > 0 Then + paint.Brush = $hBorder.Left.Brush._PaintBrush(X1, Y1, X2, Y2) + paint.Rectangle(X1, Y1, fLeftWidth, Y2 - Y1) + paint.Fill + Endif + Paint.AntiAlias = True + If Report.Debug Then + Paint.Text(DataIndex, X1, Y1, 30, 30, Align.Center) + Paint.Fill + Endif + Endif + 'Dec iLevel + 'Print String(iLevel, " ") & "restoré" + + 'Endif + +End + +Private Function BackGround_Read() As ReportBrush + + Return $hBackGround + +End + +Private Sub BackGround_Write(Value As ReportBrush) + + $hBackGround = Value + +End + +Public Sub _NormalizeUnits() + + Super._NormalizeUnits() + $hBorder._NormalizeUnits + $hBoxShadow._NormalizeUnits + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + + Dim hMyHints As ReportSizeHints + + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + + If hMyHints.Height <= 0 Then + hMyHints.Height = Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom + 'hMyHints.Height += Abs($hBoxShadow._YOffset) + $hBoxShadow._Spread + $hBoxShadow._Blur + Endif + + If hMyHints.Width <= 0 Then + hMyHints.Width = Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right + 'hMyHints.Width += Abs($hBoxShadow._xOffset) + $hBoxShadow._Spread + $hBoxShadow._Blur + Endif + + Return hMyHints + +End + +Static Private Sub RoundRect(x As Float, y As Float, w As Float, h As Float, Radius_X As Float[], radius_y As Float[]) + + Dim ARC_TO_BEZIER As Float = 0.55228475 + Dim c1, c2 As Float + Dim i As Integer + + For i = 0 To 3 + If radius_x[i] > w - radius_x[i] Then + radius_x[i] = w / 2 + Endif + + If radius_y[i] > h - radius_y[i] Then + radius_y[i] = h / 2 + Endif + + Next + 'approximate(quite Close )the arc using a bezier curve + + ' A**********B + ' H C + ' * * + ' * * + ' G D + ' F**********E + + '-->A + Paint.MoveTo(x + radius_x[0], y) + + '-->B + Paint.LineTo(x + w - radius_x[1], y) + + '-->C + c1 = ARC_TO_BEZIER * radius_x[1] + c2 = ARC_TO_BEZIER * radius_y[1] + Paint.RelCurveTo(c1, 0.0, radius_x[1], c2, radius_x[1], radius_y[1]) + + '-->D + Paint.LineTo(x + w, y + h - radius_y[2]) + + '-->E + c1 = ARC_TO_BEZIER * radius_x[2] + c2 = ARC_TO_BEZIER * radius_y[2] + Paint.RelCurveTo(0.0, c2, c1 - radius_x[2], radius_y[2], - radius_x[2], radius_y[2]) + + '-->F + Paint.LineTo(x + radius_x[3], y + h) + + '-->G + c1 = ARC_TO_BEZIER * radius_x[3] + c2 = ARC_TO_BEZIER * radius_y[3] + Paint.RelCurveTo(- c1, 0, - radius_x[3], - c2, - radius_x[3], - radius_y[3]) + + '-->H + Paint.LineTo(x, y + radius_y[0]) + + '-->A + c1 = ARC_TO_BEZIER * radius_x[0] + c2 = ARC_TO_BEZIER * radius_y[0] + Paint.relcurveto(0.0, - c2, radius_x[0] - c1, - radius_y[0], radius_x[0], - radius_y[0]) + + 'Paint.closepath() + +End + +Private Function BoxShadow_Read() As ReportBoxShadow + + Return $hBoxShadow + +End + +Private Sub BoxShadow_Write(Value As ReportBoxShadow) + + $hBoxShadow = Value + +End + +Public Sub _Free() + +End diff --git a/comp/src/gb.report2/.src/ReportGridView/ReportGridView.class b/comp/src/gb.report2/.src/ReportGridView/ReportGridView.class new file mode 100644 index 00000000..8da07e70 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportGridView/ReportGridView.class @@ -0,0 +1,52 @@ +' Gambas class file + +'Export +Inherits ReportFrame + +Private $hRows As New _ReportGridViewRows +Private $hColumns As New _ReportGridViewColumns +Private $hData As New _ReportGridViewData + +Property Read Columns As _ReportGridViewColumns +Property Read Rows As _ReportGridViewRows +Property Read Data As _ReportGridViewData + +Private Function Columns_Read() As _ReportGridViewColumns + + Return $hColumns + +End + +Private Function Rows_Read() As _ReportGridViewRows + + Return $hRows + +End + +Private Function Data_Read() As _ReportGridViewData + + Return $hData + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, TotalWidth As Float, TotalHeight As Float, DataIndex As Integer) As ReportSizeHints + + Return Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + +End + +Public Sub _NormalizeUnits() + + Super._NormalizeUnits() + + +End + +Public Function _SetChildGeometry((X) As Float, (Y) As Float, (W) As Float, (H) As Float, (ContPage) As Integer, (TCont) As TControl, (bInFixed) As Boolean) + +End + diff --git a/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewColumn.class b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewColumn.class new file mode 100644 index 00000000..84582cfe --- /dev/null +++ b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewColumn.class @@ -0,0 +1,96 @@ +' Gambas class file + +Property Width, W As String +Property Text, Title As String +Property Resizable As Boolean +Property Expand As Boolean +Property Alignment As Integer +Property Background As ReportBrush + +Private $sWidth As String +Private $sText As String +Private $bExpand As String +Private $iAlignment As Integer +Private $hBackGround As New ReportBrush +Private $bResizable As Integer +Public _Width As Float + +Public _Column As Integer ''Column Number + +Private Function Width_Read() As String + + Return $sWidth + +End + +Private Sub Width_Write(Value As String) + + $sWidth = Value + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End + +Private Function Resizable_Read() As Boolean + + Return $bResizable + +End + +Private Sub Resizable_Write(Value As Boolean) + + $bResizable = Value + +End + +Private Function Expand_Read() As Boolean + + Return $bExpand + +End + +Private Sub Expand_Write(Value As Boolean) + + $bExpand = Value + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End + +Private Function Background_Read() As ReportBrush + + Return $hBackGround + +End + +Private Sub Background_Write(Value As ReportBrush) + + $hBackGround = Value + +End + +Public Sub _NormalizeUnits() + + _Width = ReportSizeParser($sWidth) + +End diff --git a/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewColumns.class b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewColumns.class new file mode 100644 index 00000000..cb044baf --- /dev/null +++ b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewColumns.class @@ -0,0 +1,141 @@ +' Gambas class file + +Property Count As Integer +Property Read Max As Integer +Property Read Height, H As String +Property Resizable As Boolean +Property Width, W As String +Property Sort As Integer +Property Ascending As Boolean + +Public _Width As Float +Public _Height As Float + + +Private $iCount As Integer +Private $sHeight As String +Private $bResizable As String +Private $sWidth As String = "2cm" +Private $iSort As Integer +Private $bAscendig As Boolean + +Private $aColumns As New _ReportGridViewColumn[] + + + + +Public Sub _NormalizeUnits() + + Dim i As Integer + + For i = 0 To $aColumns.Max + $aColumns[i]._NormalizeUnits() + Next + + _Width = ReportSizeParser[$sWidth].GetValue() + _Height = ReportSizeParser[$sHeight].GetValue() +End + + + +Private Function Count_Read() As Integer + + Return $aColumns.Count + +End + +Private Sub Count_Write(Value As Integer) + + Dim iOldCount As Integer = $aColumns.Count + Dim i As Integer + Dim hCol As _ReportGridViewColumn + + If Value = iOldCount Then Return + + Try $aColumns.Resize(Value) + If Error Then Error.Raise("Bad argument") + + If Value > iOldCount Then + + For i = iOldCount To Value - 1 + hcol = New _ReportGridViewColumn + hcol.Width = "1cm" + $aColumns[i] = hcol + Next + + Endif + + For i = iOldCount To Value + + Next + + +End + +Private Function Max_Read() As Integer + + Return $aColumns.Max + +End + +Private Function Height_Read() As String + + Return $sHeight + +End + +Private Function Resizable_Read() As Boolean + + Return $bResizable + +End + +Private Sub Resizable_Write(Value As Boolean) + + $bResizable = Value + +End + +Private Function Width_Read() As String + + Return $sWidth + +End + +Private Sub Width_Write(Value As String) + + $sWidth = Value + +End + +Private Function Sort_Read() As Integer + + Return $iSort + +End + +Private Sub Sort_Write(Value As Integer) + + $iSort = Value + +End + +Private Function Ascending_Read() As Boolean + + Return $bAscendig + +End + +Private Sub Ascending_Write(Value As Boolean) + + $bAscendig = Value + +End + +Private Function GetView() As ReportGridView + + Return Object.Parent(Me) + +End + + diff --git a/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewData.class b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewData.class new file mode 100644 index 00000000..0a5fa73a --- /dev/null +++ b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewData.class @@ -0,0 +1,2 @@ +' Gambas class file + diff --git a/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewRow.class b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewRow.class new file mode 100644 index 00000000..69fe1c11 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewRow.class @@ -0,0 +1,52 @@ +' Gambas class file + +Property Height, H As String +Property Text, Title As String +'Property Border As Border + +Public _Height As Float + + + +Private $sHeight As Integer +Private $sText As Integer + +Event _Foo + +Private Function Height_Read() As String + + Return $sHeight + +End + +Private Sub Height_Write(Value As String) + + $sHeight = Value + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End + +Public Sub _NormalizeUnits() + + _Height = ReportSizeParser[$sHeight].GetValue() + +End + + +Private Function GetRows() As _ReportGridViewRows + + Return Object.Parent(Me) + +End + diff --git a/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewRows.class b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewRows.class new file mode 100644 index 00000000..0d896f04 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportGridView/_ReportGridViewRows.class @@ -0,0 +1,120 @@ +' Gambas class file + +Property Count As Integer +Property Read Max As Integer +Property Width, W As String +Property Resizable As Boolean +Property Height, H As String +'Property Border As Border + +Private $iCount As Integer +Private aLineCol As New Collection +Private $sHeight As String +Private $sWidth As String +Private $bResizable As Boolean + + +Private Function Count_Read() As Integer + + Return $iCount + +End + +Private Sub Count_Write(Value As Integer) + + + Dim i As Integer + Value = Max(1, Value) + If Value < $iCount Then + For i = $iCount DownTo Value + aLineCol[i - 1] = Null + Next + Endif + + $iCount = Value + +End + +Private Function Max_Read() As Integer + + Return $iCount - 1 + +End + +Private Function Width_Read() As String + + Return $sWidth + +End + +Private Sub Width_Write(Value As String) + + $sWidth = Value + +End + +Private Function Resizable_Read() As Boolean + + Return $bResizable + +End + +Private Sub Resizable_Write(Value As Boolean) + + $bResizable = Value + +End + +Private Function Height_Read() As String + + Return $sHeight + +End + +Private Sub Height_Write(Value As String) + + $sHeight = Value + +End + +Public Sub _NormalizeUnits() + + Dim hRow As New _ReportGridViewRow + + For Each hRow In aLineCol + + hRow._NormalizeUnits() + + Next + +End + + +Public Function _GetStrRowHeight(iVal As Integer) As String + + If aLineCol.Exist(iVal) Then + Return aLineCol(iVal).Height + Else + Return $sHeight + Endif + +End + +Public Function _SetStrRowHeight(iVal As Integer, sValue As String) + Dim hRow As _ReportGridViewRow + If iVal < 0 Then Return + If iVal > $iCount - 1 Then Return + + hRow = aLineCol[iVal] + If Not hRow Then + hRow = New _ReportGridViewRow + aLineCol[iVal] = hRow + Endif + hRow._Row = iVal + hRow.Height = sValue + + + +End + + diff --git a/comp/src/gb.report2/.src/ReportHBox.class b/comp/src/gb.report2/.src/ReportHBox.class new file mode 100644 index 00000000..95bff9e9 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportHBox.class @@ -0,0 +1,21 @@ +' Gambas class file + +Export + +Inherits ReportContainer + +Public Const _Properties As String = "*" +Public Const _Similar As String = "ReportVBox" +Public Const _DefaultArrangement As String = "H" + +Public Sub _new() + + Super._Arrangement = Arrange.Horizontal + +End + +Public Sub _Free() + + Super._Free + +End diff --git a/comp/src/gb.report2/.src/ReportImage.class b/comp/src/gb.report2/.src/ReportImage.class new file mode 100644 index 00000000..57ca16d0 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportImage.class @@ -0,0 +1,200 @@ +' Gambas class file + +Export +Inherits ReportFrame + +Public Const _Properties As String = "*,Stretch{Report.None;Proportional;Fill}=Proportional,Alignment{Align.*},Image{Image}" +Public Const _Similar As String = "ReportTextLabel" +Public Const _DefaultEvent As String = "Data" + +Private $iAlignment As Integer = Align.Normal +Private $hPic As Image +Private $iStretchMode As Integer = Report.Proportional + +Property Alignment As Integer +Property Image As Image +Property Stretch As Integer + +Public Data As Image + +Event Data(Index As Integer) + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + + Dim hMyHints As New ReportSizeHints + + Dim hPic As Image + Dim fRatio As Float + 'Dim fSize As Float + + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + If $hpic Then + hpic = $hpic + Else + Raise Data(DataIndex) + hpic = Data + Endif + + If hpic Then + If Me.Stretch = Report.Proportional Then + If hpic.Width > hpic.Height Then + fRatio = hpic.Height / hpic.Width + + hMyHints.Width = Min(Report.UnitToInch(hpic.Width, "px"), AvailableW) + hMyHints.Height = ((hMyHints.Width - (Me.Border._Width + Me.Padding._Width)) * fRatio) + (Me.Padding._Height + Me.Border._Height) + Else + fRatio = hpic.Width / hpic.Height + hMyHints.Height = Min(Report.UnitToInch(hpic.Height, "px"), AvailableH) + hMyHints.Width = ((hMyHints.Height - (Me.Border._Height + Me.Padding._Height)) * fRatio) + (Me.Border._Width + Me.Padding._Width) + Endif + Else + hMyHints.Width = Max(hMyHints.Width, Me.Padding._Left + Report.UnitToInch(hpic.Width, "px") + Me.Padding._Right) 'Report.UnitToInch(hpic.Width, "px") + Me.Padding._Right) + hMyHints.Height = Max(hMyHints.Height, Me.Padding._Top + Report.UnitToInch(hpic.Height, "px") + Me.Padding._Bottom) 'Report.UnitToInch(hpic.Height, "px") + Me.Padding._Bottom) + Endif + + Endif + + Endif + + Return hMyHints + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim ix, iy As Float + 'Dim hBrush As PaintBrush + Dim hPic As Image + + Dim w, h As Float + + ix = x + hControl.RealLeft '+ Me.Report._ToPixels(Me.Padding._Left) + iy = y + hControl.RealTop '+ Me.Report._ToPixels(Me.Padding._Top) + + If Not $hpic Then + Raise Data(DataIndex) + hpic = Data + If Not hPic Then Return + Else + hPic = $hpic + Endif + + Paint.Save + Paint.Rectangle(ix, iy, hControl.RealWidth, hControl.RealHeight) + Paint.Clip + + '$hPic = $hPic.Stretch(hControl.RealWidth, hControl.RealHeight) + + 'hBrush = Paint.Image(hpic) + + If Me.Stretch = Report.Fill Then + iX += Me.Report._ToPixels(Me.Padding._Left + Me.Border._Left) + iY += Me.Report._ToPixels(Me.Padding._Top + Me.Border._Top) + w = (hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + h = (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + + Paint.DrawImage(hpic, ix, iy, w, h - 1) + + Else + + If $iStretchMode = Report.Proportional Then + 'on détermine la partie prédominante + If hpic.Width >= hpic.Height + 'C'est la largeur + 'on détermine une hauteur en fonction de la largeur connue + w = hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Width) + h = hPic.H / hpic.W * w + 'si h> a la place disponible alors on adapte en fonction de h en faite + If h > (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Height)) Then + h = (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Height)) + w = hpic.w / hpic.H * h + Endif + + Else + 'C'est la hauteur + h = (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Height)) + w = hpic.w / hpic.H * h + 'si w> la place disponible alors on adapte en fonction de w en faite + If w > (hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Width)) Then + w = hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Width) + h = hPic.H / hpic.W * w + Endif + Endif + + Else + w = hpic.Width + h = hpic.H + + Endif + + Select Case $iAlignment + Case Align.Normal, Align.TopLeft, Align.Left, Align.BottomLeft + 'Gauche + ix += Me.Report._ToPixels(Me.Padding._Left) + Case Align.Bottom, Align.Center, Align.Top + 'centrée + ix += (hControl.RealWidth - w) / 2 + + Case Align.TopRight, Align.Right, Align.BottomRight + 'Droite + ix += hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Right) - w + End Select + + Select Case $iAlignment + Case Align.TopLeft, Align.Top, Align.TopRight + 'Haut + iy += Me.Report._ToPixels(Me.Padding._Top) + Case Align.Left, Align.Center, Align.Right + 'Milieu + iy += (hControl.RealHeight - h) / 2 + Case Align.BottomLeft, Align.Bottom, Align.BottomRight + iY += hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Bottom) - h + End Select + + Paint.DrawImage(hpic, ix, iy, w, h - 1) + + Endif + Paint.Fill + Paint.Restore +End + +Private Function Image_Read() As Image + + Return $hPic + +End + +Private Sub Image_Write(Value As Image) + + $hPic = Value + 'If Left(Me.Width, 1) = "0" Then Me.Width = $hpic.Width & " px" + 'If Left(Me.Height, 1) = "0" Then Me.Height = $hpic.Height & " px" + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End + +Private Function Stretch_Read() As Integer + + Return $iStretchMode + +End + +Private Sub Stretch_Write(Value As Integer) + + $iStretchMode = Value + +End + diff --git a/comp/src/gb.report2/.src/ReportLabel.class b/comp/src/gb.report2/.src/ReportLabel.class new file mode 100644 index 00000000..7db13863 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportLabel.class @@ -0,0 +1,239 @@ +' Gambas class file + +Export +Inherits ReportFrame + +Public Const _Properties As String = "*,Text,Format,Alignment{Align.*},Rotate{Angle:Degrees},UseField" '"*,Text,Key,Format,Alignment{Align.*},UseField" +Public Const _Similar As String = "ReportTextLabel" +Public Const _DefaultEvent As String = "Data" +Public Data As String + +Property Text As String +Property Format As String +Property Alignment As Integer +Property Rotate As Float +Property UseField As Boolean +Private $sText As String +Private $sFormat As String +Private $iAlignment As Integer +Private $fAngle As Integer +Private $bUseField As Boolean +Private $hEval As _RepExp +Static Private $iPage As Integer +Static Private $iIndex As Integer +Event Data(Index As Integer) + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + + Dim hSizeHint As ReportSizeHints + Dim hRect As RectF + Dim fWidth, fHeight As Float + Dim sText As String + Dim hExt As PaintExtents + 'Obtenir la taille de l'objet de base en tenant compte + 'de padding et des dimensions fournies + hSizeHint = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + 'Inutile de chercher a connaitre la taille réclamée par l'objet si elle est + 'soit définie par le parent, soit imposée par l'utilisateur + + If $sText Begins "=" Then + If Not $hEval Or If $hEval.Text <> Right($sText, -1) Then + $hEval = New _RepExp As "Eval" + $hEval.Text = Right($sText, -1) + Endif + Endif + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + 'Sinon on cherche la tailmle du texte + + If $sText Then + sText = $sText + Else + + Raise Data(DataIndex) + sText = Data + Endif + + + If $sFormat Then + Try sText = Format(sText, $sFormat) + Endif + + + + + Paint.Font = Me.Font + 'If sText = "Gambas" Then Stop + If $fAngle = 0 Then + hRect = Paint.TextSize(sText) + hSizeHint = New ReportSizeHints + fWidth = Me.Border._Left + Me.Padding._Left + Me.Report._FromPixels(hRect.Width) + Me.Padding._Right + Me.Border._Right + fHeight = Me.Border._Top + Me.Padding._Top + Me.Report._FromPixels(hRect.Height) + Me.Padding._Bottom + Me.Border._Bottom + Else + Paint.Save + Paint.Rotate(Rad($fAngle)) + Paint.Text(sText, 0, 0) + hExt = Paint.PathExtents + fWidth = Me.Border._Left + Me.Padding._Left + Me.Report._FromPixels(hExt.Width) + Me.Padding._Right + Me.Border._Right + fHeight = Me.Border._Top + Me.Padding._Top + Me.Report._FromPixels(hExt.Height) + Me.Padding._Bottom + Me.Border._Bottom + Paint.Restore + Endif + + Endif + + hSizeHint.Width = Max(hSizeHint.Width, fWidth) + hSizeHint.Height = Max(hSizeHint.Height, fHeight) + + Return hSizeHint + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim sTempText As Variant + Dim iX, iY, iW, iH As Integer + + $iPage = Page + $iIndex = DataIndex + + If $sText Then + sTempText = $sText + Else + Raise Data(DataIndex) + sTempText = Data + Endif + + If sTempText Begins "=" Then + $hEval.Compile() + sTempText = $hEval.Value + Endif + + If $sFormat Then + Try sTempText = Format(sTempText, $sFormat) + Endif + + If $bUseField Then + sTempText = DecodeText(sTempText, Page) + Endif + + iX = (x + hControl.RealLeft + Me.Report._ToPixels(Me.Padding._Left + Me.Border._Left)) + iY = (y + hControl.RealTop + Me.Report._ToPixels(Me.Padding._Top + Me.Border._Top)) + iW = (hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + + iH = (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + + Paint.Brush = Me._GetActualBrush(iX, iY, iX + hControl.RealWidth, iY + hControl.RealHeight) + + If Report.Debug Then + + Paint.Brush = Paint.Color(Color.Red) + Paint.Rectangle(ix, iy, iw, ih) + Paint.Stroke + + Endif + + 'Set the Font if it is initialized + + Paint.Font = Me.Font + + If $fAngle = 0.0 Then + + Paint.Text(sTempText, iX, iY, iW, iH, $iAlignment) + Else + Paint.Translate(iX + iW / 2, iY + iH / 2) + Paint.Rotate(Rad($fAngle)) + Paint.Translate(- (iX + iW / 2), - (iY + iH / 2)) + Paint.Text(sTempText, iX, iY, iW, iH, Align.Center) + + Endif + Paint.Fill + +End + +Private Function DecodeText(sText As String, Optional Page As Integer, Optional ForSize As Boolean = False) As String + + If ForSize Then + If InStr(sText, "$PAGE") Then sText = Replace(sText, "$PAGE", "999") + If InStr(sText, "$NPAGE") Then sText = Replace(sText, "$NPAGE", "999") + Else + If InStr(sText, "$PAGE") Then sText = Replace(sText, "$PAGE", Str(Page + 1)) + If InStr(sText, "$NPAGE") Then sText = Replace(sText, "$NPAGE", Str(Me.Report.PageCount)) + Endif + + Return sText + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End + +Private Function Format_Read() As String + + Return $sFormat + +End + +Private Sub Format_Write(Value As String) + + $sFormat = Value + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End + +Private Function Rotate_Read() As Float + + Return $fAngle + +End + +Private Sub Rotate_Write(Value As Float) + + $fAngle = Value + +End + +Private Function UseField_Read() As Boolean + + Return $bUseField + +End + +Private Sub UseField_Write(Value As Boolean) + + $bUseField = Value + +End + +Public Sub Eval_Data(Value As String) + 'Print "appel" + Select Case LCase(Value) + Case "page" + Last.Data = $iPage + 1 + Case "index" + Last.Data = $iIndex + Case "pages" + Last.Data = Me.Report.PageCount + End Select + +End + diff --git a/comp/src/gb.report2/.src/ReportLine.class b/comp/src/gb.report2/.src/ReportLine.class new file mode 100644 index 00000000..61183082 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportLine.class @@ -0,0 +1,127 @@ +' Gambas class file + +Export +Inherits ReportControl + +Public Const _Properties As String = "*,Direction{Align.TopLeft;Top;TopRight;Left;Right;BottomLeft;Bottom;BottomRight}=BottomRight,LineWidth{ReportCoord}=2 px" +Public Const _Similar As String = "ReportTextLabel" +Property Direction As Integer + +Private $fLineWidth As Float = 0.1 + +Private $iLineStyle As Integer = Line.solid +Private $iDirection As Integer = Align.BottomRight +Private $sLineWidth As String = "2px" +Property LineWidth As String +Property LineStyle As Integer + +Public Sub _new() + + Me.Height = "1cm" + Me.Width = "1cm" + +End + +Private Function LineWidth_Read() As String + + Return $sLineWidth + +End + +Private Sub LineWidth_Write(Value As String) + + $sLineWidth = Value + +End + +Public Sub _NormalizeUnits() + + Dim hSizeParse As TSizeParse + + Super._NormalizeUnits() + + hSizeParse = New TSizeParse($sLineWidth) + $fLineWidth = hSizeParse.GetValue() + +End + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + + Dim hMyHints As New ReportSizeHints + + hMyHints.Height = Me._Height + hMyHints.Width = Me._Width + Return hMyHints + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim iX, iY, W, H, W2, H2 As Integer + + If Me.LineStyle = Line.None Then Return + If Me.LineStyle <> Line.Solid Then Paint.Dash = MUtil.GetBorder(Me.LineStyle) + Paint.LineWidth = Me.Report._ToPixels($fLineWidth) + + iX = (x + hControl.RealLeft + Me.Report._ToPixels(Me.Padding._Left)) + iY = (y + hControl.RealTop + Me.Report._ToPixels(Me.Padding._Top)) + + W = iX + hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Right) + H = iY + hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Bottom) + Paint.Brush = Me._GetActualBrush(iX, iY, W, H) + W2 = (iX + W) / 2 + H2 = (iY + H) / 2 + Select Case $iDirection + Case Align.TopLeft + Paint.MoveTo(W, H) + Paint.LineTo(iX, iY) + Case Align.Top + Paint.MoveTo(W2, H) + Paint.LineTo(W2, iY) + Case Align.TopRight + Paint.MoveTo(iX, H) + Paint.LineTo(W, iY) + Case Align.Left + Paint.MoveTo(W, H2) + Paint.LineTo(iX, H2) + Case Align.Right + Paint.MoveTo(iX, H2) + Paint.LineTo(W, H2) + Case Align.BottomLeft + Paint.MoveTo(W, iY) + Paint.LineTo(iX, H) + Case Align.Bottom + Paint.MoveTo(W2, iY) + Paint.LineTo(W2, H) + Case Align.BottomRight + Paint.MoveTo(iX, iY) + Paint.LineTo(W, H) + End Select + + Paint.Stroke + +End + +Private Function LineStyle_Read() As Integer + + Return $iLineStyle + +End + +Private Sub LineStyle_Write(Value As Integer) + + $iLineStyle = Value + +End + +Private Function Direction_Read() As Integer + + Return $iDirection + +End + +Private Sub Direction_Write(Value As Integer) + + $iDirection = Value + +End diff --git a/comp/src/gb.report2/.src/ReportPageBreak.class b/comp/src/gb.report2/.src/ReportPageBreak.class new file mode 100644 index 00000000..ea95ef98 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportPageBreak.class @@ -0,0 +1,6 @@ +' Gambas class file + +Export +Inherits ReportControl + +Public Const _Properties As String = "-*" diff --git a/comp/src/gb.report2/.src/ReportPanel.class b/comp/src/gb.report2/.src/ReportPanel.class new file mode 100644 index 00000000..f0ba1039 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportPanel.class @@ -0,0 +1,25 @@ +' Gambas class file + +Export +Inherits ReportContainer +Public Const _Properties As String = "*,Arrangement{Arrange.None;Vertical;Horizontal}=None" +Property Arrangement As Integer + +Public Sub New() + + Super._Arrangement = Arrange.None + +End + + +Private Function Arrangement_Read() As Integer + + Return Super._Arrangement + +End + +Private Sub Arrangement_Write(Value As Integer) + + Super._Arrangement = Value + +End diff --git a/comp/src/gb.report2/.src/ReportSection.class b/comp/src/gb.report2/.src/ReportSection.class new file mode 100644 index 00000000..94224b5b --- /dev/null +++ b/comp/src/gb.report2/.src/ReportSection.class @@ -0,0 +1,23 @@ +' Gambas class file + +Export +Inherits ReportVBox +Public Const _Properties As String = "*,Text" +Property Text As String +Private $sText As String + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End + +Public Sub _Free() + +End diff --git a/comp/src/gb.report2/.src/ReportSvgImage.class b/comp/src/gb.report2/.src/ReportSvgImage.class new file mode 100644 index 00000000..31820c81 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportSvgImage.class @@ -0,0 +1,200 @@ +' Gambas class file + +Export +Inherits ReportFrame + +Private $hPic As SvgImage +Private $iStretchMode As Integer = Report.Proportional +Private $iAlignment As Integer = Align.Normal +'Private $sPath As String +Public Const _Properties As String = "*,Stretch{Report.None;Proportional;Fill}=Proportional,Alignment{Align.*},Image{SvgImage}" +Public Const _Similar As String = "ReportTextLabel" +Public Data As SvgImage +Property Image As SvgImage +Property Stretch As Integer +Property Alignment As Integer +Event Data(Index As Integer) + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + + Dim hMyHints As New ReportSizeHints + Dim fRatio As Float + 'Dim h As Float = IIf(Me._Height > AvailableH, AvailableH, Me._Height) + 'Dim w As Float = IIf(Me._Width > AvailableW, AvailableW, Me._Width) + + Dim hPic As SvgImage + + hMyHints = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalWidth, DataIndex) + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + If $hpic Then + hpic = $hpic + Else + Raise Data(DataIndex) + hpic = Data + Endif + + If hpic Then + If Me.Stretch = Report.Proportional Then + If hpic.Width > hpic.Height Then + fRatio = hpic.Height / hpic.Width + + hMyHints.Width = Min(Report.UnitToInch(hpic.Width, "px"), AvailableW) + hMyHints.Height = ((hMyHints.Width - (Me.Border._Width + Me.Padding._Width)) * fRatio) + (Me.Padding._Height + Me.Border._Height) + Else + fRatio = hpic.Width / hpic.Height + hMyHints.Height = Min(Report.UnitToInch(hpic.Height, "px"), AvailableH) + hMyHints.Width = ((hMyHints.Height - (Me.Border._Height + Me.Padding._Height)) * fRatio) + (Me.Border._Width + Me.Padding._Width) + Endif + Else + hMyHints.Width = Max(hMyHints.Width, Me.Padding._Left + Report.UnitToInch(hpic.Width, "px") + Me.Padding._Right) 'Report.UnitToInch(hpic.Width, "px") + Me.Padding._Right) + hMyHints.Height = Max(hMyHints.Height, Me.Padding._Top + Report.UnitToInch(hpic.Height, "px") + Me.Padding._Bottom) 'Report.UnitToInch(hpic.Height, "px") + Me.Padding._Bottom) + Endif + + Endif + Endif + + Return hMyHints + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim ix, iy As Float + 'Dim hBrush As PaintBrush + Dim hPic As SvgImage + + Dim w, h As Float + + ix = x + hControl.RealLeft '+ Me.Report._ToPixels(Me.Padding._Left) + iy = y + hControl.RealTop '+ Me.Report._ToPixels(Me.Padding._Top) + + If Not $hpic Then + Raise Data(DataIndex) + hpic = Data + If Not hPic Then Return + Else + hPic = $hpic + Endif + Paint.Save + Paint.Rectangle(ix, iy, hControl.RealWidth, hControl.RealHeight) + Paint.Clip + '$hPic = $hPic.Stretch(hControl.RealWidth, hControl.RealHeight) + + 'hBrush = Paint.Image(hpic) + + If $iStretchMode = Report.Fill Then + iX += Me.Report._ToPixels(Me.Padding._Left + Me.Border._Left) + iY += Me.Report._ToPixels(Me.Padding._Top + Me.Border._Top) + w = (hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + h = (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + ' hBrush.Translate(ix, iy - 1) + 'hBrush.Scale(w / hPic.Width, h / hPic.Height) + 'Paint.Brush = hBrush + hpic.Width = w + hpic.Height = h + Paint.MoveTo(ix, iy) + hpic.Paint + 'Paint.Rectangle(ix, iy, w, h) + Else + + If $iStretchMode = Report.Proportional Then + 'on détermine la partie prédominante + If hPic.Width >= hPic.Height + 'C'est la largeur + 'on détermine une hauteur en fonction de la largeur connue + w = hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Width) + h = hPic.Height / hPic.Width * w + 'si h> a la place disponible alors on adapte en fonction de h en faite + If h > (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Height)) Then + h = (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Height)) + w = hPic.Width / hPic.Height * h + Endif + + Else + 'C'est la hauteur + h = (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Height)) + w = hPic.Width / hPic.Height * h + 'si w> la place disponible alors on adapte en fonction de w en faite + If w > (hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Width)) Then + w = hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Width) + h = hPic.Height / hPic.Width * w + Endif + Endif + + Else + w = hPic.Width + h = hPic.Height + + Endif + + Select Case $iAlignment + Case Align.Normal, Align.TopLeft, Align.Left, Align.BottomLeft + 'Gauche + ix += Me.Report._ToPixels(Me.Padding._Left) + Case Align.Bottom, Align.Center, Align.Top + 'centrée + ix += (hControl.RealWidth - w) / 2 + + Case Align.TopRight, Align.Right, Align.BottomRight + 'Droite + ix += hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Right) - w + End Select + + Select Case $iAlignment + Case Align.TopLeft, Align.Top, Align.TopRight + 'Haut + iy += Me.Report._ToPixels(Me.Padding._Top) + Case Align.Left, Align.Center, Align.Right + 'Milieu + iy += (hControl.RealHeight - h) / 2 + Case Align.BottomLeft, Align.Bottom, Align.BottomRight + iY += hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Bottom) - h + End Select + 'hBrush.Translate(ix, iy - 1) + hpic.Width = W + hPic.Height = H + Paint.MoveTo(iX, iY) + hpic.Paint + 'hBrush.Scale(w / hPic.Width, h / hPic.Height) + 'Paint.Brush = hBrush + 'Paint.Rectangle(ix, iy, w, h - 1) + Endif + Paint.Restore +End + +Private Function Image_Read() As SvgImage + + Return $hPic + +End + +Private Sub Image_Write(Value As SvgImage) + + $hPic = Value + +End + +Private Function Stretch_Read() As Integer + + Return $iStretchMode + +End + +Private Sub Stretch_Write(Value As Integer) + + $iStretchMode = Value + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End diff --git a/comp/src/gb.report2/.src/ReportTextLabel.class b/comp/src/gb.report2/.src/ReportTextLabel.class new file mode 100644 index 00000000..9f121ffe --- /dev/null +++ b/comp/src/gb.report2/.src/ReportTextLabel.class @@ -0,0 +1,143 @@ +' Gambas class file + +Export +Inherits ReportFrame + +Public Const _Properties As String = "*,Text,Alignment{Align.*}" '"*,Text,Key,Format,Alignment{Align.*},UseField" +Public Const _Similar As String = "ReportTextLabel" +Public Const _DefaultEvent As String = "Data" +Public Data As String + +Property Text As String +Property Alignment As Integer + +Private $sText As String +Private $iAlignment As Integer + +Event Data(Index As Integer) + +Public Sub _GetSizeHints((AvailableW) As Float, (AvailableH) As Float, (TotalWidth) As Float, (TotalHeight) As Float, DataIndex As Integer) As ReportSizeHints + + Dim hSizeHint As ReportSizeHints + Dim hRect As RectF + Dim fWidth, fHeight As Float + Dim sText As String + 'Dim hExt As PaintExtents + 'Obtenir la taille de l'objet de base en tenant compte + 'de padding et des dimensions fournies + hSizeHint = Super._GetSizeHints(AvailableW, AvailableH, TotalWidth, TotalHeight, DataIndex) + 'Inutile de chercher a connaitre la taille réclamée par l'objet si elle est + 'soit définie par le parent, soit imposée par l'utilisateur + + If Me.Autoresize Or If (Me._Width = 0 And Me._Height = 0) Then + 'Sinon on cherche la tailmle du texte + + If $sText Then + sText = $sText + Else + Raise Data(DataIndex) + sText = Data + Endif + + Paint.Font = Me.Font + 'If sText = "Gambas" Then Stop + 'If $fAngle = 0 Then + hRect = Paint.RichTextSize(sText) + hSizeHint = New ReportSizeHints + fWidth = Me.Border._Left + Me.Padding._Left + Me.Report._FromPixels(hRect.Width) + Me.Padding._Right + Me.Border._Right + fHeight = Me.Border._Top + Me.Padding._Top + Me.Report._FromPixels(hRect.Height) + Me.Padding._Bottom + Me.Border._Bottom + + 'Endif + + Endif + + hSizeHint.Width = Max(hSizeHint.Width, fWidth) + hSizeHint.Height = Max(hSizeHint.Height, fHeight) + + Return hSizeHint + +End + +Public Sub _Paint(Page As Integer, X As Float, Y As Float, hControl As TControl, DataIndex As Integer) + + Dim sTempText As Variant + Dim iX, iY, iW, iH As Integer + + If $sText Then + sTempText = $sText + Else + Raise Data(DataIndex) + sTempText = Data + Endif + + iX = (x + hControl.RealLeft + Me.Report._ToPixels(Me.Padding._Left + Me.Border._Left)) + iY = (y + hControl.RealTop + Me.Report._ToPixels(Me.Padding._Top + Me.Border._Top)) + iW = (hControl.RealWidth - Me.Report._ToPixels(Me.Padding._Left + Me.Padding._Right + Me.Border._Left + Me.Border._Right)) + + iH = (hControl.RealHeight - Me.Report._ToPixels(Me.Padding._Top + Me.Padding._Bottom + Me.Border._Top + Me.Border._Bottom)) + + If Report.Debug Then + + Paint.Brush = Paint.Color(Color.Red) + Paint.Rectangle(ix, iy, iw, ih) + Paint.Stroke + + Endif + + 'Set the Font if it is initialized + + 'Paint.Font = Me.Font + 'If InStr($sText, "color=") Then + Draw.Font = Me.Font + Paint.Brush = Me._GetActualBrush(iX, iY, iX + hControl.RealWidth, iY + hControl.RealHeight) + Draw.RichText(sTempText, iX, iY, iW, iH, $iAlignment) + 'Else + ' Paint.Font = Me.Font + ' Paint.RichText(sTempText, iX, iY, iW, iH, $iAlignment) + ' Endif + 'Paint.Fill + +End + +Private Function DecodeText(sText As String, Optional Page As Integer, Optional ForSize As Boolean = False) As String + + If ForSize Then + If InStr(sText, "$PAGE") Then sText = Replace(sText, "$PAGE", "999") + If InStr(sText, "$NPAGE") Then sText = Replace(sText, "$NPAGE", "999") + Else + If InStr(sText, "$PAGE") Then sText = Replace(sText, "$PAGE", Str(Page + 1)) + If InStr(sText, "$NPAGE") Then sText = Replace(sText, "$NPAGE", Str(Me.Report.PageCount)) + Endif + + Return sText + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End + + + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End + + + diff --git a/comp/src/gb.report2/.src/ReportVBox.class b/comp/src/gb.report2/.src/ReportVBox.class new file mode 100644 index 00000000..48a385e1 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportVBox.class @@ -0,0 +1,17 @@ +' Gambas class file + +Export +Inherits ReportContainer +Public Const _Properties As String = "*,ForceNewPage" +Public Const _Similar As String = "ReportVBox" +Public Const _DefaultArrangement As String = "V" + +Public Sub _new() + + Super._Arrangement = Arrange.Vertical + +End + +Public Sub _Free() + +End diff --git a/comp/src/gb.report2/.src/ReportVPanel.class b/comp/src/gb.report2/.src/ReportVPanel.class new file mode 100644 index 00000000..9a4a55c6 --- /dev/null +++ b/comp/src/gb.report2/.src/ReportVPanel.class @@ -0,0 +1,26 @@ +' Gambas class file + +Export +Inherits ReportContainer +Public Const _Properties As String = "*,ForceNewPage" +Public Const _Similar As String = "ReportVBox" +Public Const _DefaultArrangement As String = "V" + + +' Definition +' C'est la largeur maximal des objets qui donnes la largeur de la colonne. +' Mais un objet ne peu être plus large que le container principal... soit une colonne. + + + + + +Public Sub _new() + + Super._Arrangement = Arrange.Column + +End + +Public Sub _Free() + +End \ No newline at end of file diff --git a/comp/src/gb.report2/.src/Tests/Report10.class b/comp/src/gb.report2/.src/Tests/Report10.class new file mode 100644 index 00000000..15819ab8 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report10.class @@ -0,0 +1,15 @@ +' Gambas class file + + +Public Sub Report_Open() + ReportVBox1.DataCount = 100 + ReportVBox2.DataCount = 4 + ReportPanel1.Datacount = 3 + +End + +Public Sub ReportLabel1_Data(Index As Integer) + + 'Last.data = Index + +End diff --git a/comp/src/gb.report2/.src/Tests/Report10.report b/comp/src/gb.report2/.src/Tests/Report10.report new file mode 100644 index 00000000..878e0df4 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report10.report @@ -0,0 +1,50 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,91) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Tag = "Report" + Index = 0 + Text = ("") + { ReportVBox1 ReportVBox + #MoveScaled(1,1,62,42) + Expand = True + { ReportLabel1 ReportLabel + #MoveScaled(1,1,60,6) + Text = ("=index") + } + { ReportVBox2 ReportVBox + #MoveScaled(1,7,60,15) + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["#FF7F00"] + { ReportLabel2 ReportLabel + #MoveScaled(1,1,58,6) + AutoResize = True + Text = ("=Index") + } + { ReportPanel1 ReportPanel + #MoveScaled(1,7,58,6) + Height = "7mm" + Margin = ReportMargin["Top:0mm;Bottom:2mm;Left:1cm;Right:1cm"] + Tag = "hBox" + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["#00FF00"] + Arrangement = Arrange.Vertical + { ReportLabel3 ReportLabel + #MoveScaled(4,2,62,7) + Padding = ReportPadding["Left:5mm"] + Expand = True + Tag = "label" + Text = ("=\"Index = \" & index") + } + } + } + } + { ReportLabel4 ReportLabel + #MoveScaled(1,43,62,3) + Fixed = True + Text = ("=page & \" / \" & pages") + Alignment = Align.Right + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/Report13.class b/comp/src/gb.report2/.src/Tests/Report13.class new file mode 100644 index 00000000..f08416a1 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report13.class @@ -0,0 +1,8 @@ +' Gambas class file + + +Public Sub Report_Open() + + ReportHBox1.DataCount = 10000 + +End diff --git a/comp/src/gb.report2/.src/Tests/Report13.report b/comp/src/gb.report2/.src/Tests/Report13.report new file mode 100644 index 00000000..e608d98f --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report13.report @@ -0,0 +1,48 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,109,91) + Padding = ReportPadding["Top:25mm;Bottom:25mm;Left:25mm;Right:25mm"] + Expand = True + Spacing = "8mm" + Count = 2 + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(1,1,107,20) + Height = "4cm" + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Border = ReportBorder["Top:1mm #7F7F7F;Bottom:1mm #7F7F7F;Left:1mm #7F7F7F;Right:1mm #7F7F7F;TopLeftCorner:5mm;TopRightCorner:5mm;BottomRightCorner:5mm;BottomLeftCorner:5mm"] + Background = ReportBrush["#EFDFFF"] + BoxShadow = ReportBoxShadow["1mm 1mm 2px 0px #C2C2C2"] + { ReportImage1 ReportImage + #MoveScaled(1,1,19,18) + Width = "4cm" + Height = "4cm" + AutoResize = True + Image = Image.Load("gambas.svg") + } + { ReportLabel1 ReportLabel + #MoveScaled(20,1,83,18) + Brush = ReportBrush["LinearGradient(1,0,1,1,[#5F5F5F,#BFBFBF],[0,1])"] + Font = Font["Bold,+10"] + Expand = True + Text = ("=\"GAMBAS - \" & index") + Alignment = Align.Center + } + } + { ReportLabel3 ReportLabel + #MoveScaled(1,22,107,29) + Fixed = True + Font = Font["+12"] + Text = ("=\"PAGE \" & Page & \" / \" & pages") + } + Index = 1 + Text = ("") + { ReportLabel2 ReportLabel + #MoveScaled(1,1,107,29) + Font = Font["+12"] + Text = ("=\"PAGE \" & Page & \" / \" & pages") + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/Report14.class b/comp/src/gb.report2/.src/Tests/Report14.class new file mode 100644 index 00000000..51ffa578 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report14.class @@ -0,0 +1,8 @@ +' Gambas class file + + +Public Sub Report_Open() + + RHBList.DataCount = 300 + +End diff --git a/comp/src/gb.report2/.src/Tests/Report14.report b/comp/src/gb.report2/.src/Tests/Report14.report new file mode 100644 index 00000000..6ae250de --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report14.report @@ -0,0 +1,66 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,91) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(1,1,62,7) + Fixed = True + Margin = ReportMargin["Bottom:5mm"] + BoxShadow = ReportBoxShadow["1mm 1mm 3px 0px #000000"] + { ReportLabel1 ReportLabel + #MoveScaled(1,1,12,5) + Width = "3cm" + Border = ReportBorder["Top:3px #000000;Bottom:3px #000000;Left:3px #000000;Right:3px #000000;TopLeftCorner:5mm"] + Text = ("Reference") + Alignment = Align.Center + } + { ReportLabel2 ReportLabel + #MoveScaled(13,1,25,5) + Expand = True + Border = ReportBorder["Top:3px #000000;Bottom:3px #000000;Left:3px #000000"] + Text = ("Description") + Alignment = Align.Center + } + { ReportLabel3 ReportLabel + #MoveScaled(38,1,21,5) + Width = "5cm" + Border = ReportBorder["Top:3px #000000;Bottom:3px #000000;Left:3px #000000;Right:3px #000000;TopRightCorner:0mm;BottomLeftCorner:0mm"] + Text = ("Valeur") + Alignment = Align.Center + } + } + { RHBList ReportHBox + #MoveScaled(1,8,62,6) + Margin = ReportMargin["Bottom:1mm"] + { ReportLabel4 ReportLabel + #MoveScaled(1,1,12,4) + Width = "3cm" + Border = ReportBorder["Top:0px #000000;Bottom:3px #000000;Left:3px #000000"] + Text = ("=index") + } + { ReportLabel5 ReportLabel + #MoveScaled(13,1,25,4) + Expand = True + Border = ReportBorder["Top:0px #000000;Bottom:3px #000000;Left:3px #000000"] + } + { ReportLabel6 ReportLabel + #MoveScaled(38,1,21,4) + Width = "5cm" + Border = ReportBorder["Top:0px #000000;Bottom:3px #000000;Left:3px #000000;Right:3px #000000"] + } + } + { ReportPanel1 ReportPanel + #MoveScaled(1,14,62,6) + Expand = True + } + { ReportLabel7 ReportLabel + #MoveScaled(1,20,62,3) + Fixed = True + Text = ("=page & \" on \" & pages") + Alignment = Align.Right + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/Report15.class b/comp/src/gb.report2/.src/Tests/Report15.class new file mode 100644 index 00000000..0a5fa73a --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report15.class @@ -0,0 +1,2 @@ +' Gambas class file + diff --git a/comp/src/gb.report2/.src/Tests/Report15.report b/comp/src/gb.report2/.src/Tests/Report15.report new file mode 100644 index 00000000..96cd9a13 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report15.report @@ -0,0 +1,13 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,91) + Index = 0 + Text = ("") + { ReportImage1 ReportImage + #MoveScaled(1,1,62,20) + Expand = True + Image = Image.Load("tortueface.gif") + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/Report16.class b/comp/src/gb.report2/.src/Tests/Report16.class new file mode 100644 index 00000000..74251a7a --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report16.class @@ -0,0 +1,14 @@ +' Gambas class file + + +Public Sub ReportLabel2_Data(Index As Integer) + + Last.Data = "Coucou " & Index + +End + +Public Sub Report_Open() + + ReportHBox1.DataCount = 100 + +End diff --git a/comp/src/gb.report2/.src/Tests/Report16.report b/comp/src/gb.report2/.src/Tests/Report16.report new file mode 100644 index 00000000..76fc659c --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report16.report @@ -0,0 +1,27 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,119,91) + Padding = ReportPadding["Top:1cm;Bottom:1cm;Left:1cm;Right:1cm"] + Index = 0 + Text = ("") + { ReportLabel1 ReportLabel + #MoveScaled(1,1,117,8) + Height = "3cm" + Brush = ReportBrush["LinearGradient(0,0,0,0,[#DFBC53,#FFFFFF],[0.97,0.05])"] + Font = Font["Bold,+8"] + Padding = ReportPadding["Top:1cm;Bottom:1cm;Left:1cm;Right:1cm"] + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000;TopLeftCorner:21mm 6mm;TopRightCorner:21mm 6mm;BottomRightCorner:21mm 6mm;BottomLeftCorner:21mm 6mm"] + Background = ReportBrush["RadialGradient(0.6,0.46,0.5,0.81,0.21,[#FFFFFF,#9F1313,#CF4DCF],[0,1,0.5])"] + BoxShadow = ReportBoxShadow["1mm 1mm 5px 0px #FF5500"] + Text = ("Coucou") + Alignment = Align.Center + } + { ReportHBox1 ReportHBox + #MoveScaled(1,9,117,10) + { ReportLabel2 ReportLabel + #MoveScaled(1,1,112,8) + } + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/Report17.class b/comp/src/gb.report2/.src/Tests/Report17.class new file mode 100644 index 00000000..c689a4b4 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report17.class @@ -0,0 +1,8 @@ +' Gambas class file + + +Public Sub Report_Open() + + ReportPanel1.DataCount = 30 + +End diff --git a/comp/src/gb.report2/.src/Tests/Report17.report b/comp/src/gb.report2/.src/Tests/Report17.report new file mode 100644 index 00000000..e22572c1 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/Report17.report @@ -0,0 +1,24 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,100,91) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Index = 0 + Text = ("") + { ReportPanel1 ReportPanel + #MoveScaled(1,1,98,22) + Height = "5cm" + AutoResize = True + { ReportLabel1 ReportLabel + #MoveScaled(0,0,98,21) + Brush = ReportBrush["RadialGradient(0.95,0.04,0.5,0.53,0.21,[#FFFFFF,#FF007F],[0,1])"] + Font = Font["Bold,+17"] + Border = ReportBorder["Top:1mm LinearGradient(1,0.88,0,0,[#000000,#FFFFFF],[0,1]);Bottom:1mm LinearGradient(1,0.88,0,0,[#000000,#FFFFFF],[0,1]);Left:1mm LinearGradient(1,0.88,0,0,[#000000,#FFFFFF],[0,1]);Right:1mm LinearGradient(1,0.88,0,0,[#000000,#FFFFFF],[0,1]);TopLeftCorner:12mm;BottomRightCorner:12mm"] + Background = ReportBrush["RadialGradient(0.49,0.46,0.5,0.84,0.14,[#3737DF,#FFFFFF],[0,1])"] + BoxShadow = ReportBoxShadow["1mm 1mm 2mm 0mm #000000"] + Text = ("Coucou") + Alignment = Align.Center + } + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/old/FMain.class b/comp/src/gb.report2/.src/Tests/old/FMain.class new file mode 100644 index 00000000..a593d25e --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/FMain.class @@ -0,0 +1,16 @@ +' Gambas class file + +Public Sub _new() +Dim hLabel As New ReportLabel(Report) +hLabel.Text = "Test" +Report.Padding = ReportPadding["1cm"] +hLabel.Border = ReportBorder["border:1px;toprightcorner:5mm 1cm"] +hLabel.BackGround = ReportBrush["image(icon.png)"] +Report.Preview() +End + +Public Sub Form_Open() +GridView1.Columns.Count = 6 +GridView1.Columns[0].Text = "titi" +GridView1.Rows.Count = 6 +End diff --git a/comp/src/gb.report2/.src/Tests/old/FMain.form b/comp/src/gb.report2/.src/Tests/old/FMain.form new file mode 100644 index 00000000..993e7c82 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/FMain.form @@ -0,0 +1,10 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,112,67) + { GridView1 GridView + MoveScaled(17,14,61,36) + Grid = False + Header = GridView.Both + } +} diff --git a/comp/src/gb.report2/.src/Tests/old/Module1.module b/comp/src/gb.report2/.src/Tests/old/Module1.module new file mode 100644 index 00000000..06e1d891 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Module1.module @@ -0,0 +1,19 @@ +' Gambas module file + +Public Sub Main() + + Dim hrc As New Report + Dim vb As ReportVBox + Dim himg As New Image(1, 1) + + hrc.tag = + hrc.Width = "21cm" + hrc.Height = "29.7cm" + vb = New ReportVBox(hrc) + Paint.Begin(himg) + hrc.Layout + Paint.End + Print hrc._Height + Print hrc._Width + +End diff --git a/comp/src/gb.report2/.src/Tests/old/OutputReport2.class b/comp/src/gb.report2/.src/Tests/old/OutputReport2.class new file mode 100644 index 00000000..80e40957 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/OutputReport2.class @@ -0,0 +1,166 @@ +' Gambas class file + +Public nnoderows As Integer +Public nanchorrows As Integer + +Public Sub _new() +'Report.Debug = True + 'enter current date on the header + ReportLabel2.Text = Date + + 'enter the project information in the header on the print out page + 'With FMain.Project + ReportLabel3.Text = "Project Title: " ' & .JobTitle + ReportLabel4.Text = "Project No.: '" '& .Number + ReportLabel5.Text = "Company: " '& .Company + ReportLabel6.Text = "Designer: " '& .Designer + ReportLabel7.Text = "Base Plate ID: " ' & .BasePlateID + 'End With + + 'set count to four to display the results for qmax. Tmax, L, and phi + ReportHBox9.DataCount = 4 + + 'set the number of plate nodes to display in print out tables + ' nnoderows = Module1.RoundUpInt(FMain.ConcreteSection.cNodes.Count / 2) + ReportHBox5.DataCount = 11 'nnoderows + + 'set the number of anchor rods to display in print out tables + 'nanchorrows = Module1.RoundUpInt(FMain.cAnchorRods.Count / 2) + ReportHBox7.DataCount = 13 'nanchorrows + + 'set the number of report panels - one for each load combination result + ReportPanel1.DataCount = 6 'FMain.AnalysisResults.Count + +End + + +Public Sub ReportImage2_Data(Index As Integer) + 'Dim current_results As Results + Dim pSection As Picture + + 'create a new picture object for drawing the base plate + pSection = New Picture(300, 300, False) + + 'get the results for first load case + 'current_results = FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)] + + 'refresh base plate drawing with results + 'Module1.DrawBasePlate(pSection, 1, current_results) + + 'add the picture of the base plate to the page + ReportImage2.Image = pSection.Image + +End + + +Public Sub ReportLabel9_Data(Index As Integer) + + Last.Data = "Load Case: " '& FMain.cLoads[Str$(ReportHBox3.DataIndex)].label + +End + + +Public Sub ReportLabel14_Data(Index As Integer) + 'Dim $value As String + + Select Case Index + Case 0 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].Tmax, 2) + Last.Data = "T(max) = " & 3 & " lbs" + Case 1 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].maxstress, 2) + Last.Data = "q(max) = " & 3 & " psi" + Case 2 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].L, 2) + Last.Data = "L = " & 3 & " in" + Case 3 + '$value = Module1.AdvFormat(FMain.AnalysisResults[Str$(ReportHBox3.DataIndex)].phi, 2) + Last.Data = "phi = " & 3 & " degrees" + Case Else + End Select + +End + + +Public Sub ReportLabel25_Data(Index As Integer) + + Last.Data = Index + 1 + +End + +Public Sub ReportLabel29_Data(Index As Integer) + ' Dim current_results As Results + + 'current_results = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)] + + 'If nnoderows + Index < current_results.stress.Count Then + Last.Data = nnoderows + Index + 1 + 'Else + ' Last.Data = Null + ' Endif + +End + +Public Sub ReportLabel26_Data(Index As Integer) + Dim value As Float + + 'value = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)].stress[Index] + Last.Data = "tt" 'Module1.AdvFormat(value, 2) + +End + +Public Sub ReportLabel30_Data(Index As Integer) + Dim value As Float + 'Dim current_results As Results + + 'current_results = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)] + ' If nnoderows + Index < current_results.stress.Count Then + 'value = current_results.stress[nnoderows + Index] + Last.Data = "mm" 'Module1.AdvFormat(value, 2) + ' Else + Last.Data = Null + 'Endif + +End + +Public Sub ReportLabel49_Data(Index As Integer) + + Last.Data = Index + 1 + +End + +Public Sub ReportLabel52_Data(Index As Integer) + 'Dim current_results As Results + + 'current_results = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)] + + 'If nanchorrows + Index < current_results.T.Count Then + Last.Data = nanchorrows + Index + 1 + 'Else + ' Last.Data = Null + ' Endif + +End + +Public Sub ReportLabel50_Data(Index As Integer) + Dim value As Float + + 'value = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)].T[Index] + Last.Data = "kkkk" 'Module1.AdvFormat(value, 2) + +End + +Public Sub ReportLabel53_Data(Index As Integer) + Dim value As Float + 'Dim current_results As Results + + ' current_results = FMain.AnalysisResults[Str$(ReportPanel1.DataIndex)] + ' If nanchorrows + Index < current_results.T.Count Then + ' value = current_results.T[nanchorrows + Index] + Last.Data = "qqqqqq" 'Module1.AdvFormat(value, 2) + ' Else + ' Last.Data = Null + 'Endif + +End + diff --git a/comp/src/gb.report2/.src/Tests/old/OutputReport2.report b/comp/src/gb.report2/.src/Tests/old/OutputReport2.report new file mode 100644 index 00000000..f12422c8 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/OutputReport2.report @@ -0,0 +1,308 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,76,88) + Fixed = True + Padding = ReportPadding["Top:20mm;Bottom:10mm;Left:20mm;Right:20mm"] + Ignore = True + AutoResize = True + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(2,0,62,11) + Height = "25mm" + Fixed = True + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Bottom:2px #000000"] + { ReportImage1 ReportImage + #MoveScaled(1,1,18,9) + Width = "51mm" + AutoResize = True + } + { ReportLabel1 ReportLabel + #MoveScaled(21,1,10,9) + Width = "25mm" + AutoResize = True + Text = ("Version 1.0") + Alignment = Align.BottomLeft + } + { ReportLabel2 ReportLabel + #MoveScaled(31,1,30,9) + Expand = True + AutoResize = True + Text = ("Date") + Alignment = Align.BottomRight + UseField = True + } + } + { ReportVBox1 ReportVBox + #MoveScaled(2,11,62,9) + Height = "28.01mm" + Fixed = True + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Bottom:2px #000000"] + { ReportLabel3 ReportLabel + #MoveScaled(0,0,12,2) + Height = "6mm" + AutoResize = True + Text = ("Project Title:") + Alignment = Align.TopLeft + } + { ReportLabel4 ReportLabel + #MoveScaled(0,2,12,2) + Height = "6mm" + AutoResize = True + Text = ("Project No.:") + Alignment = Align.TopLeft + } + { ReportLabel5 ReportLabel + #MoveScaled(0,4,12,2) + Height = "6mm" + AutoResize = True + Text = ("Company:") + Alignment = Align.TopLeft + } + { ReportLabel6 ReportLabel + #MoveScaled(0,6,12,2) + Height = "6mm" + AutoResize = True + Text = ("Designer:") + Alignment = Align.TopLeft + } + } + { ReportHBox2 ReportHBox + #MoveScaled(2,20,62,3) + Height = "8mm" + Fixed = True + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Bottom:2px #000000"] + { ReportLabel7 ReportLabel + #MoveScaled(0,0,12,2) + Height = "6mm" + AutoResize = True + Text = ("Base Plate ID:") + Alignment = Align.TopLeft + } + } + { ReportPanel1 ReportPanel + #MoveScaled(2,25,67,52) + AutoResize = True + ForceNewPage = True + { ReportHBox3 ReportHBox + #MoveScaled(1,1,62,17) + Height = "61mm" + Expand = True + { ReportVBox3 ReportVBox + #MoveScaled(1,1,18,14) + AutoResize = True + { ReportHBox8 ReportHBox + #MoveScaled(1,2,15,4) + Width = "51mm" + Height = "12mm" + { ReportLabel9 ReportLabel + #MoveScaled(1,1,13,2) + Font = Font["+1"] + Expand = True + } + } + { ReportHBox9 ReportHBox + #MoveScaled(1,8,15,4) + Width = "51mm" + Height = "6mm" + { ReportLabel14 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + } + } + } + { ReportImage2 ReportImage + #MoveScaled(31,1,17,15) + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Expand = True + Alignment = Align.Center + } + } + { ReportVBox4 ReportVBox + #MoveScaled(1,20,62,14) + AutoResize = True + { ReportLabel19 ReportLabel + #MoveScaled(2,0,16,4) + Fixed = True + Font = Font["+1"] + Padding = ReportPadding["Top:3mm"] + AutoResize = True + Text = ("Bearing Pressue") + } + { ReportHBox4 ReportHBox + #MoveScaled(1,3,60,4) + Height = "6mm" + Fixed = True + { ReportLabel20 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Node #") + Alignment = Align.Center + } + { ReportLabel21 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Brg. Press., psi") + Alignment = Align.Center + } + { ReportLabel22 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel23 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Node #") + Alignment = Align.Center + } + { ReportLabel24 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Brg. Press., psi") + Alignment = Align.Center + } + } + { ReportHBox5 ReportHBox + #MoveScaled(1,8,60,4) + Height = "6mm" + { ReportLabel25 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel26 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel27 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel29 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel30 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + } + } + { ReportVBox6 ReportVBox + #MoveScaled(1,36,62,14) + AutoResize = True + { ReportLabel31 ReportLabel + #MoveScaled(2,0,19,4) + Fixed = True + Font = Font["+1"] + Padding = ReportPadding["Top:3mm"] + AutoResize = True + Text = ("Anchor Rod Tension") + } + { ReportHBox6 ReportHBox + #MoveScaled(1,3,60,4) + Height = "6mm" + Fixed = True + { ReportLabel44 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Rod #") + Alignment = Align.Center + } + { ReportLabel45 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Tension, lbs") + Alignment = Align.Center + } + { ReportLabel46 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel47 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Rod #") + Alignment = Align.Center + } + { ReportLabel48 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Text = ("Tension, lbs") + Alignment = Align.Center + } + } + { ReportHBox7 ReportHBox + #MoveScaled(1,8,60,4) + Height = "6mm" + { ReportLabel49 ReportLabel + #MoveScaled(1,1,13,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel50 ReportLabel + #MoveScaled(14,1,14,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel51 ReportLabel + #MoveScaled(28,1,4,2) + Width = "5mm" + } + { ReportLabel52 ReportLabel + #MoveScaled(32,1,12,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + { ReportLabel53 ReportLabel + #MoveScaled(44,1,15,2) + Expand = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + Alignment = Align.Center + } + } + } + } + { ReportPanel2 ReportPanel + #MoveScaled(2,77,69,1) + Fixed = True + Expand = True + } + { ReportHBox10 ReportHBox + #MoveScaled(2,79,62,4) + Height = "1cm" + Fixed = True + AutoResize = True + { ReportLabel8 ReportLabel + #MoveScaled(22,1,21,2) + Expand = True + AutoResize = True + Text = ("Page $PAGE of $NPAGE") + Alignment = Align.Bottom + UseField = True + } + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report1.class b/comp/src/gb.report2/.src/Tests/old/Report1.class new file mode 100644 index 00000000..414000b0 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report1.class @@ -0,0 +1,43 @@ +' Gambas class file + + +Public Sub Report_Open() +Dim hBox As ReportHBox +Dim i As Integer +'Report.Debug = True +'Me.Spacing = "1cm" + ReportHBox1.Margin.Bottom = "2cm" + 'Report.Debug = True + 'ReportHBox2.Margin.Top = "2cm" + ' ReportHBox1.BoxShadow.Blur = "0.5mm" + ' ReportHBox1.BoxShadow.XOffset = "1mm" + ' ReportHBox1.BoxShadow.YOffset = "1mm" + ' 'ReportHBox1.BoxShadow.Spread = "0.5mm" + ' ReportHBox1.BoxShadow.Color = Color.Gray + + For i = 0 To 14 + hbox = New Reporthbox(ReportVBox1) + HBox.Height = "1cm" + HBox.Border.Width = "2px" + HBox.Margin.Top = "5mm" + HBox.Margin.Bottom = "3mm" + HBox.Margin.Left = "1cm" + HBox.Margin.Right = "2mm" + Next + hBox = New ReportHBox(ReportVBox1) + HBox.BackGround = ReportBrush.Color(Color.Green) + HBox.Height = "3cm" + HBox.Margin.Top = "1cm" + + For i = 0 To 4 + hbox = New Reporthbox(ReportHBox2) + HBox.Width = "1cm" + HBox.Border.Width = "2px" + HBox.Margin.Left = "3mm" + HBox.Margin.Right = "8mm" + HBox.Expand = True + HBox.Margin.Top = "3mm" + HBox.Margin.Bottom = "3px" + Next + ReportHBox2.Padding = ReportPadding["2mm"] +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report1.report b/comp/src/gb.report2/.src/Tests/old/Report1.report new file mode 100644 index 00000000..aef99552 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report1.report @@ -0,0 +1,50 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,93,73) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + BoxShadow = ReportBoxShadow["2mm 2mm 0mm 0px #C05800"] + Spacing = "5mm" + Index = 0 + Text = ("") + { ReportHBox1 ReportHBox + #MoveScaled(1,1,91,14) + Height = "2cm" + Fixed = True + Tag = "**" + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000;TopLeftCorner:3mm;TopRightCorner:3mm;BottomRightCorner:3mm;BottomLeftCorner:3mm"] + BoxShadow = ReportBoxShadow["0mm 3mm 1mm -4px #FF8000"] + { ReportLabel1 ReportLabel + #MoveScaled(1,1,58,12) + Font = Font["+10"] + Expand = True + AutoResize = True + Text = ("=\"Page \" & Page") + Alignment = Align.Center + } + } + { ReportVBox1 ReportVBox + #MoveScaled(1,16,91,10) + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000;TopLeftCorner:10mm;TopRightCorner:20mm;BottomRightCorner:10mm;BottomLeftCorner:1mm"] + Background = ReportBrush["LinearGradient(0.35,0.74,0.95,0.07,[#FFFFFF,#FFFFFF,#007FFF],[0,1,0.36])"] + BoxShadow = ReportBoxShadow["1mm 1mm 1mm 0px #000000"] + } + { ReportHBox2 ReportPanel + #MoveScaled(1,27,91,16) + Height = "20mm" + Fixed = True + { ReportLabel2 ReportLabel + #MoveScaled(10,3,59,9) + Expand = True + Text = ("=pi() + 4") + Format = "0.00" + } + } + { ReportHBox3 ReportHBox + #MoveScaled(1,44,91,6) + Height = "10mm" + Brush = ReportBrush["#FF0000"] + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report11.class b/comp/src/gb.report2/.src/Tests/old/Report11.class new file mode 100644 index 00000000..ebaa73e2 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report11.class @@ -0,0 +1,32 @@ +' Gambas class file + +'Static Public Type As String + +Public Sub _new(Optional sType As String = "sqlite") + + Dim hResult As Result + Dim Rlbl As ReportLabel + Dim hConn As Connection +'Report.Debug = True + hConn = Connections["Connection1"] + hConn.Type = sType + If hConn.Type = "sqlite" Then hConn.Host = User.Home + Try hConn.Open + If Not hConn.Opened Then + Message.Error(" You have to create the test database with the Database example.") + Return + Endif + + hResult = db.Limit(200).Find("test") + If Not hResult.Available Then Return + + For Each hResult + + Rlbl = New ReportLabel(RVBCont) + Rlbl.Autoresize = True + Rlbl.Text = hResult!name & " " & hResult!firstname + Rlbl.Border.bottom.Width = "1px" + + Next + +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report11.report b/comp/src/gb.report2/.src/Tests/old/Report11.report new file mode 100644 index 00000000..3f0ea999 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report11.report @@ -0,0 +1,101 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,100,77) + Padding = ReportPadding["Top:1cm;Bottom:15mm;Left:2cm;Right:2cm"] + Index = 0 + Text = ("Section 1") + { ReportHBox2 ReportHBox + #MoveScaled(0,0,67,14) + Height = "5cm" + Fixed = True + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + Background = ReportBrush["#3398C3"] + { ReportSvgImage1 ReportSvgImage + #MoveScaled(1,1,14,12) + Width = "4cm" + Image = SvgImage.Load("gambas.svg") + } + { ReportLabel3 ReportLabel + #MoveScaled(20,2,40,10) + Brush = ReportBrush["LinearGradient(0.71,0.07,0.34,0.06,[#FFFF00,#FFFFFF],[0,1])"] + Fixed = True + Font = Font["DejaVu Sans,Bold,+14"] + Expand = True + AutoResize = True + Text = ("Gambas") + Alignment = Align.Left + } + } + { ReportHBox1 ReportHBox + #MoveScaled(0,15,87,56) + Padding = ReportPadding["0cm"] + Expand = True + Border = ReportBorder["Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + { ReportVBox1 ReportVBox + #MoveScaled(1,1,25,47) + Width = "6cm" + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Background = ReportBrush["#DF6B00"] + { ReportLabel4 ReportLabel + #MoveScaled(3,2,20,4) + Brush = ReportBrush["#FFFFFF"] + Fixed = True + Font = Font["Bold"] + AutoResize = True + Text = ("All friends list !") + } + } + { ReportVBox2 ReportVBox + #MoveScaled(27,1,60,54) + Expand = True + Border = ReportBorder["Left:1mm #000000"] + Background = ReportBrush["#FFFFFF"] + { RVBCont ReportVBox + #MoveScaled(4,4,53,42) + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Expand = True + Tag = "*" + Spacing = "1mm" + { ReportLabel1 ReportLabel + #MoveScaled(0,1,44,9) + Fixed = True + Font = Font["Bitstream Charter,Bold,Italic,+2"] + Margin = ReportMargin["Bottom:1cm"] + AutoResize = True + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000"] + BoxShadow = ReportBoxShadow["1mm 1mm 0px 0px #000000"] + Text = ("Gambas Report Demo") + Alignment = Align.Center + } + { ReportLabel5 ReportLabel + #MoveScaled(3,17,40,21) + Width = "100%" + Height = "100%" + Brush = ReportBrush["#DFDFDF"] + Visible = False + Fixed = True + Font = Font["Bold,+15"] + Ignore = True + Text = ("DEMO") + Alignment = Align.Center + } + } + { ReportLabel2 ReportLabel + #MoveScaled(7,49,47,3) + Brush = ReportBrush["#FFFFFF"] + Fixed = True + Font = Font["Bold,+1"] + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + AutoResize = True + Border = ReportBorder["Top:1mm #000000"] + Background = ReportBrush["#3398C3"] + Text = ("Page $PAGE on $NPAGE ") + Alignment = Align.Right + UseField = True + } + } + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report12.class b/comp/src/gb.report2/.src/Tests/old/Report12.class new file mode 100644 index 00000000..b833bfd8 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report12.class @@ -0,0 +1,20 @@ +' Gambas class file + + +Public Sub Report_Open() + + TableLine.DataCount = 8 + +End + +Public Sub ReportLabel4_Data(Index As Integer) +Print Index + Last.Data = Index + +End + +Public Sub ReportLabel5_Data(Index As Integer) +Print Index + Last.Data = TableLine.DataIndex + +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report12.report b/comp/src/gb.report2/.src/Tests/old/Report12.report new file mode 100644 index 00000000..ae08ad77 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report12.report @@ -0,0 +1,47 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Index = 0 + Text = ("Section 1") + { ReportHBox1 ReportHBox + #MoveScaled(0,2,60,6) + Height = "1cm" + Border = ReportBorder["Top:2px #000000;Bottom:2px #000000;Left:2px #000000;Right:2px #000000;TopLeftCorner:2mm;TopRightCorner:2mm"] + { ReportLabel1 ReportLabel + #MoveScaled(1,1,16,4) + Width = "5cm" + } + { ReportLabel2 ReportLabel + #MoveScaled(19,1,18,4) + Width = "5cm" + Border = ReportBorder["Left:2px #000000;Right:2px #000000"] + } + { ReportLabel3 ReportLabel + #MoveScaled(39,1,19,4) + Expand = True + } + } + { ReportVBox1 ReportVBox + #MoveScaled(1,12,61,44) + { TableLine ReportHBox + #MoveScaled(0,0,60,4) + Height = "1cm" + Border = ReportBorder["Bottom:2px #000000;Left:2px #000000;Right:2px #000000"] + { ReportLabel4 ReportLabel + #MoveScaled(0,0,17,4) + Width = "5cm" + } + { ReportLabel5 ReportLabel + #MoveScaled(19,1,20,4) + Width = "5cm" + Border = ReportBorder["Left:2px #000000;Right:2px #000000"] + } + { ReportLabel6 ReportLabel + #MoveScaled(42,0,15,4) + } + } + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report2.class b/comp/src/gb.report2/.src/Tests/old/Report2.class new file mode 100644 index 00000000..cc36bd1d --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report2.class @@ -0,0 +1,22 @@ +' Gambas class file + + + + +Public Sub ReportGridView1_Data(Row As Integer, Column As Integer) + + + +End + +Public Sub Report_Open() + + ' ReportGridView1.Columns.Count = 6 + ' ReportGridView1.Rows.Count = 8 + ' ReportGridView1.Columns[2].Width = "6cm" + ' ReportGridView1.Rows[2].Height = "2cm" + ' ReportGridView1.Rows[4].Height = "8px" + ' ReportGridView1.Rows[4].Text = "totolkjdhdolhldhdhl" + ' ReportGridView1.Header = ReportGridView.Both + +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report2.report b/comp/src/gb.report2/.src/Tests/old/Report2.report new file mode 100644 index 00000000..a14f53eb --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report2.report @@ -0,0 +1,26 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Padding = ReportPadding["Top:15mm;Bottom:15mm;Left:15mm;Right:15mm"] + Index = 0 + Text = ("") + { ReportLabel1 ReportLabel + #MoveScaled(1,3,59,12) + Height = "2cm" + Font = Font["+12"] + Margin = ReportMargin["Bottom:1cm;Left:2cm;Right:2cm"] + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000;TopLeftCorner:5mm;TopRightCorner:5mm;BottomRightCorner:5mm;BottomLeftCorner:5mm"] + Background = ReportBrush["LinearGradient(0.49,0,0.49,1,[#00BF5C,#FFE79F],[0,1])"] + BoxShadow = ReportBoxShadow["1mm 1mm 1mm 0mm #000000"] + Text = ("ReportLabel1") + Alignment = Align.Center + } + { ReportGridView1 ReportGridView + #MoveScaled(4,24,46,25) + Expand = True + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + BoxShadow = ReportBoxShadow["0mm 0mm 2mm 0mm #000000"] + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report3.class b/comp/src/gb.report2/.src/Tests/old/Report3.class new file mode 100644 index 00000000..3b57e5de --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report3.class @@ -0,0 +1,26 @@ +' Gambas class file + + +Public Sub _new() + + Dim i As Integer + Dim hCont As ReportLabel + Dim hBreak As ReportPageBreak + 'Report.Debug = True + ReportLabel1.Border.Width = "0.5mm" + + 'ReportVBox1.Border.Left.Width = "1mm" + 'ReportVBox1.DataCount = 3 + +For i = 0 To 800 + 'If i = 3 Then hBreak = New ReportPageBreak(Me) + hCont = New ReportLabel(Me) + hCont.BoxShadow.Blur = "5mm" + hCont.Border.Width = "0.5mm" + hCont.Height = "1cm" + hCont.Margin.Top = "5mm" + hCont.Text = i +Next + + +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report3.report b/comp/src/gb.report2/.src/Tests/old/Report3.report new file mode 100644 index 00000000..8c33cd23 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report3.report @@ -0,0 +1,27 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,57) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + AutoResize = True + Spacing = "5mm" + Paper = Printer.A3 + Index = 0 + Text = ("") + { ReportLabel1 ReportLabel + #MoveScaled(6,0,52,7) + Font = Font["+14"] + BoxShadow = ReportBoxShadow["3mm 3mm 3mm 0mm #FF8000"] + Text = ("PARCELLAIRE $NPAGE") + Alignment = Align.Center + UseField = True + } + { ReportImage1 ReportImage + #MoveScaled(8,10,41,28) + Height = "14cm" + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000"] + BoxShadow = ReportBoxShadow["1mm 1mm 2px 2px #000000"] + Stretch = Report.None + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report4.class b/comp/src/gb.report2/.src/Tests/old/Report4.class new file mode 100644 index 00000000..060350d8 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report4.class @@ -0,0 +1,18 @@ +' Gambas class file + + +Public Sub Report_Open() +'Report.Debug = True +rhh.DataCount = 5 +'rv.DataCount = 10 +rv2.DataCount = 10 +' Me.Layout +' Print Me.PageCount +' rv.DataCount = 4 +' Me.Layout +' Print Me.PageCount +' rv.DataCount = 5 +' Print Me.PageCount + +'rh.Raise +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report4.report b/comp/src/gb.report2/.src/Tests/old/Report4.report new file mode 100644 index 00000000..ac77e4cd --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report4.report @@ -0,0 +1,23 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,111,87) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Index = 0 + Text = ("") + { rv2 ReportVBox + #MoveScaled(3,9,107,51) + Padding = ReportPadding["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Tag = "**" + { rhh ReportHBox + #MoveScaled(12,6,84,7) + Height = "1cm" + Margin = ReportMargin["Top:3mm;Bottom:1mm"] + Background = ReportBrush["#00FF00"] + } + { ReportDrawingArea1 ReportDrawingArea + #MoveScaled(10,16,85,27) + } + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report5.class b/comp/src/gb.report2/.src/Tests/old/Report5.class new file mode 100644 index 00000000..6f5e67c2 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report5.class @@ -0,0 +1,8 @@ +' Gambas class file + + +Public Sub Report_Open() + + + +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report5.report b/comp/src/gb.report2/.src/Tests/old/Report5.report new file mode 100644 index 00000000..4b467152 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report5.report @@ -0,0 +1,34 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,109,80) + Padding = ReportPadding["Top:1cm;Bottom:1cm;Left:1cm;Right:1cm"] + Index = 0 + Text = ("") + { ReportLabel1 ReportLabel + #MoveScaled(16,1,62,12) + Height = "20mm" + Brush = ReportBrush["LinearGradient(0.67,0,0.35,0,[#000000,#FFFFFF],[0.33,0.73])"] + Font = Font["Bold,+10"] + Margin = ReportMargin["Top:5mm;Bottom:5mm;Left:5mm;Right:5mm"] + Border = ReportBorder["Top:1mm #000000;Bottom:1mm #000000;Left:1mm #000000;Right:1mm #000000;TopLeftCorner:5mm;TopRightCorner:5mm;BottomRightCorner:5mm;BottomLeftCorner:5mm"] + Background = ReportBrush["RadialGradient(0.7,0.14,0.5,0.84,0.18,[#FFFFFF,#000000],[0,1])"] + Text = ("Coucou") + Alignment = Align.Center + } + { ReportHBox1 ReportHBox + #MoveScaled(19,19,65,16) + Height = "8cm" + Margin = ReportMargin["Bottom:10mm;Left:2cm;Right:2cm"] + Background = ReportBrush["RadialGradient(0.53,0.53,0.5,0.88,0.18,[#000000,#FFFFFF,#0000FF],[0,1,0.5])"] + BoxShadow = ReportBoxShadow["2px 2px 10px 0px #55FF7F"] + } + { ReportVBox1 ReportVBox + #MoveScaled(32,44,62,12) + Height = "2cm" + Margin = ReportMargin["Bottom:10mm"] + Expand = True + Background = ReportBrush["#FF7F00"] + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report51.class b/comp/src/gb.report2/.src/Tests/old/Report51.class new file mode 100644 index 00000000..4f5e1f23 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report51.class @@ -0,0 +1,207 @@ +' Gambas class file + +Private hResult As Result +Private hResult2 As Result +Private hVBox As ReportVBox +Private $iTotal As Integer +Private $bReel As Boolean ' = True +Public Sub Report_Open() + Dim hPanel As ReportPanel + Dim hHBox1 As ReportHBox + Dim hLine As ReportLine + Dim hLblIlot As ReportLabel + Dim hVBox2 As ReportVBox + Dim i As Integer + 'Report.Debug = True + ReportHBox3.DataCount = 8 + + hVBox = New ReportVBox(ReportHBox1) + hHBox1 = New ReportHBox(hVBox) + hVBox.Expand = True + hHBox1.Height = "1cm" + hLblIlot = New ReportLabel(hHBox1) + hLblIlot.Expand = True + For i = 0 To 2 + hline = New ReportLine(hHBox1) + hLine.Width = "6mm" + hline.LineWidth = "0.1mm" + hline.Direction = Align.topRight + Next + + + + 'hVBox.Spacing = "1mm" + Connections["Connection2"].Open + hResult = db.Exec("select * from secteurs where ordre<=3 order by ordre") + 'Report.Debug = True + 'ReportHBox1.BackGround = ReportBrush.Color(Color.Yellow) + 'hVBox.BackGround = ReportBrush.Color(Color.red) + hVBox.Expand = True + For Each hResult + + AddIlot(hResult!nom, hResult!id_secteur) + If hResult.Index = hResult.Max Then Break + hLblIlot = New ReportLabel(hVBox) + hLblIlot.Height = "4mm" + hLblIlot.Expand = True + Next + + hVBox = New ReportVBox(ReportHBox1) + 'hVBox.Spacing = "2mm" + 'hVBox.Spacing = "1mm" + hResult = db.Exec("select * from secteurs where ordre > 3 order by ordre") + 'Report.Debug = True + 'ReportHBox1.BackGround = ReportBrush.Color(Color.Yellow) + 'hVBox.BackGround = ReportBrush.Color(Color.red) + hVBox2 = New ReportVBox(hVBox) + hVBox2.Border = ReportBorder["Top:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000;Bottom:0.1mm #000000"] + hVBox2.Expand = True + hVBox2.Margin.Bottom = "4mm" + hLblIlot = New ReportLabel(hVBox2) + hLblIlot.Text = "Parcellaire 2015" + hLblIlot.Font = Font["Times,Bold,+3"] + hLblIlot.Margin.Top = "2mm" + hLblIlot.Alignment = Align.Center + hLblIlot = New ReportLabel(hVBox2) + hLblIlot.Text = "Date :" + hLblIlot.Margin.Top = "2mm" + hLblIlot.Padding.Left = "2mm" + 'hLblIlot.Border.bottom.Width = "2px" + hHBox1 = New ReportHBox(hVBox) + hVBox.Expand = True + hHBox1.Height = "1cm" + hLblIlot = New ReportLabel(hHBox1) + hLblIlot.Expand = True + For i = 0 To 2 + hline = New ReportLine(hHBox1) + hLine.Width = "6mm" + hline.LineWidth = "0.1mm" + hline.Direction = Align.topRight + Next + For Each hResult + + AddIlot(hResult!nom, hResult!id_secteur) + hLblIlot = New ReportLabel(hVBox) + hLblIlot.Height = "3mm" + Next + +End + +Private Sub AddIlot(sIlot As String, siIlot As String) + + Dim hHBox1 As New ReportHBox(hVBox) + Dim hLblIlot As New ReportLabel(hHBox1) + Dim hVBoxList As New ReportVBox(hHBox1) + Dim hHBoxLine As ReportHBox + Dim hLblParcelle As ReportLabel + Dim i As Integer + Dim fTot As Float + Dim hLine As ReportLine + + + + hLblIlot.Rotate = 90 + hVBoxList.Expand = True + + hVBoxList.Border.Top.Width = "0.1mm" + hVBoxList.Border.Right.Width = "0.1mm" + hHbox1.Border.Bottom.Width = "0.1mm" + hLblIlot.Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + + hLblIlot.Text = sIlot + hLblParcelle = hLblIlot + hLblIlot.Border = ReportBorder["Top:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + 'ecartement + 'parcelle + 'surface + 'ordre + hResult2 = db.Exec("Select p.ecartement ecartement, p.nom parcelle, p.surface surface, p.ordre ordre from parcelles p where p.id_secteur=&1 order by p.ordre", siIlot) + + +' hResult2 = db.Exec("Select p.ecartement ecartement, p.nom parcelle, sum(lcp.surface) surface, lip.ordre ordre From liaisonilotparc lip " +' "Left join parcelles p On p.id = lip.id_parc " +' "Left join liaisoncadparc lcp On lcp.idparc = p.id " +' "Left Join cadastre c On c.id = lcp.idcad " +' "where id_ilot = &1 group by p.nom order by lip.ordre", iIlot) + + For Each hResult2 + If hResult2!ordre = 0 Then Continue + hHBoxLine = New ReportHBox(hVBoxList) + hHBoxLine.Expand = True + 'hHBoxLine.Height = "5mm" + + + hLblIlot = New ReportLabel(hHBoxLine) + Try hLblIlot.Text = Format(hResult2!ecartement / 100, "0.0") + 'If hResult2!ecartement > 250 Then hLblIlot.Font.Bold = True + hLblIlot.Alignment = Align.Right + hLblIlot.Border.Right.Width = "0.1mm" + hLblIlot.Font.Size = 6 + hLblIlot.Width = "0.5cm" + hLblIlot.Padding.Right = "0.2mm" + 'hLblIlot.Padding.Top = "0.4mm" + + hLblIlot.Brush = ReportBrush.Color(Color.Gray) + + hLblIlot = New ReportLabel(hHBoxLine) + hLblIlot.Font.Size = 9 + hHBoxLine.Border = ReportBorder["Bottom:0.1mm #000000;"] + hLblIlot.Text = hResult2!parcelle + hLblIlot.Padding.Left = "1mm" + hLblIlot.Padding.Top = "0.6mm" + hLblIlot.Padding.Left = "0.5mm" + hLblIlot.Padding.Bottom = "0.6mm" + hLblIlot.Expand = True + 'hHBoxLine.Expand = True + 'hLblIlot.Border = ReportBorder["Bottom:0.1mm #00000"] + hLblIlot = New ReportLabel(hHBoxLine) + hLblIlot.Width = "1.2cm" + hLblIlot.Border.Left.Width = "0.1mm" + hLblIlot.Alignment = Align.Right + + Try hLblIlot.Text = Format(IIf($bReel, hResult2!surface / 10000, NearFive(hResult2!surface)), "0.00") + Try $iTotal += hResult2!surface + hLblIlot.Padding.Right = "1mm" + hLblIlot.Padding.Top = "0.1mm" + hLblIlot.Padding.Left = "0.5mm" + hLblIlot.Padding.Bottom = "0.1mm" + Try fTot += hResult2!surface + hLblIlot.Font.Size = 9 + + For i = 0 To 2 + hLblIlot = New ReportLabel(hHBoxLine) + hLblIlot.Width = "6mm" + hLblIlot.Border = ReportBorder["Left:0.1mm #000000;"] + + Next + + +Next + + ' hLblIlot = New ReportLabel(hVBoxList) + ' hLblIlot.Text = Format(IIf($bReel, fTot / 10000, NearFive(fTot)), "0.00") + ' hLblIlot.Font.Size = 7 + ' hLblIlot.Padding.Top = "0.8mm" + ' hLblIlot.Alignment = Align.Right + hLblParcelle.Text &= " (" & Format(IIf($bReel, fTot / 10000, NearFive(fTot)), "0.00") & ")" + +End + +Public Sub ReportLabel1_Data(Index As Integer) + + Last.Data = "Parcellaire " & Year(Now) + +End + + +Public Function NearFive(Value As Integer) As Float + +Return Round((Value / 10000) * 20, 0) / 20 + +End + +Public Sub ReportLabel21_Data(Index As Integer) + + Last.data = Str($iTotal / 10000) & " ha" + +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report51.report b/comp/src/gb.report2/.src/Tests/old/Report51.report new file mode 100644 index 00000000..174ef1dc --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report51.report @@ -0,0 +1,128 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,95,80) + Padding = ReportPadding["Top:1cm;Bottom:1cm;Left:1.5cm;Right:1cm"] + Spacing = "1cm" + Count = 2 + Index = 0 + Text = ("Recto") + { ReportHBox1 ReportHBox + #MoveScaled(3,15,91,52) + Height = "10cm" + Expand = True + Spacing = "8mm" + } + Index = 1 + Text = ("Verso") + { ReportVBox1 ReportVBox + #MoveScaled(2,1,89,71) + Padding = ReportPadding["Top:2cm;Bottom:1cm;Right:1cm"] + Expand = True + { ReportHBox2 ReportHBox + #MoveScaled(0,0,90,6) + { ReportLabel6 ReportLabel + #MoveScaled(1,1,7,4) + Width = "1cm" + Font = Font["+2"] + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + Text = ("N°") + Alignment = Align.Center + } + { ReportLabel5 ReportLabel + #MoveScaled(9,1,13,4) + Width = "3cm" + Font = Font["+2"] + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + Text = ("Date") + Alignment = Align.Center + } + { ReportLabel7 ReportLabel + #MoveScaled(23,0,8,5) + Font = Font["+2"] + Expand = True + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + } + { ReportLabel8 ReportLabel + #MoveScaled(33,1,5,5) + Font = Font["+2"] + Expand = True + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + } + { ReportLabel9 ReportLabel + #MoveScaled(40,0,4,5) + Font = Font["+2"] + Expand = True + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + } + { ReportLabel11 ReportLabel + #MoveScaled(47,0,4,5) + Font = Font["+2"] + Expand = True + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + } + { ReportLabel12 ReportLabel + #MoveScaled(55,0,4,5) + Font = Font["+2"] + Expand = True + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + } + { ReportLabel10 ReportLabel + #MoveScaled(68,0,14,5) + Width = "5cm" + Font = Font["+2"] + Padding = ReportPadding["Top:1mm;Bottom:1mm;Left:1mm;Right:1mm"] + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + Text = ("Observations") + Alignment = Align.Center + } + } + { ReportHBox3 ReportHBox + #MoveScaled(0,8,89,14) + Expand = True + { ReportLabel13 ReportLabel + #MoveScaled(0,1,7,4) + Width = "1cm" + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + } + { ReportLabel14 ReportLabel + #MoveScaled(8,1,13,4) + Width = "3cm" + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + } + { ReportLabel15 ReportLabel + #MoveScaled(22,0,8,5) + Expand = True + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + } + { ReportLabel16 ReportLabel + #MoveScaled(32,1,5,5) + Expand = True + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + } + { ReportLabel17 ReportLabel + #MoveScaled(39,0,4,5) + Expand = True + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + } + { ReportLabel18 ReportLabel + #MoveScaled(46,0,4,5) + Expand = True + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + } + { ReportLabel19 ReportLabel + #MoveScaled(54,0,4,5) + Expand = True + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + } + { ReportLabel20 ReportLabel + #MoveScaled(67,0,14,5) + Width = "5cm" + Border = ReportBorder["Top:0.1mm #000000;Bottom:0.1mm #000000;Left:0.1mm #000000;Right:0.1mm #000000"] + } + } + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report6.class b/comp/src/gb.report2/.src/Tests/old/Report6.class new file mode 100644 index 00000000..0a5fa73a --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report6.class @@ -0,0 +1,2 @@ +' Gambas class file + diff --git a/comp/src/gb.report2/.src/Tests/old/Report6.report b/comp/src/gb.report2/.src/Tests/old/Report6.report new file mode 100644 index 00000000..a0fcce8b --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report6.report @@ -0,0 +1,14 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Index = 0 + Text = ("") + { ReportLabel1 ReportTextLabel + #MoveScaled(8,4,48,48) + Border = ReportBorder["Top:2mm LinearGradient(1,1,0,0,[#FF0000,#FFFFFF],[1,0]);Bottom:2mm LinearGradient(1,1,0,0,[#FF0000,#FFFFFF],[1,0]);Left:2mm LinearGradient(1,1,0,0,[#FF0000,#FFFFFF],[1,0]);Right:2mm LinearGradient(1,1,0,0,[#FF0000,#FFFFFF],[1,0]);TopLeftCorner:1mm 0cm;TopRightCorner:5mm 1cm;BottomRightCorner:1mm 15mm;BottomLeftCorner:3mm 7mm"] + Text = ("Testtest

    \n\n
    ") + Alignment = Align.Center + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report7.class b/comp/src/gb.report2/.src/Tests/old/Report7.class new file mode 100644 index 00000000..b009d3b2 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report7.class @@ -0,0 +1,30 @@ +' Gambas class file + + +Public Sub ReportDrawingArea_Layout() + + + +End + + +Public Sub ReportDrawingArea1_Layout(Width As Float, Height As Float, Index As Integer) + + Last.SizeHints = ReportSizeHints.FromString("3cm", "4cm", False) + +End + + +Public Sub ReportDrawingArea1_Draw(Width As Float, Height As Float, Index As Integer) + +'Print "Largeur passée = " & Width +'Print "Paint.Width = " & Paint.Width + Draw.line(0, 0, Paint.Width, Paint.Height) + +End + +Public Sub Report_Open() + + + +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report7.report b/comp/src/gb.report2/.src/Tests/old/Report7.report new file mode 100644 index 00000000..85481ab2 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report7.report @@ -0,0 +1,13 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Padding = ReportPadding["Top:2cm;Bottom:2cm;Left:2cm;Right:2cm"] + Index = 0 + Text = ("") + { ReportDrawingArea1 ReportDrawingArea + #MoveScaled(4,8,59,34) + Border = ReportBorder["Top:2px #000000;Bottom:2px #000000;Left:2px #000000;Right:2px #000000"] + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report8.class b/comp/src/gb.report2/.src/Tests/old/Report8.class new file mode 100644 index 00000000..0a5fa73a --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report8.class @@ -0,0 +1,2 @@ +' Gambas class file + diff --git a/comp/src/gb.report2/.src/Tests/old/Report8.report b/comp/src/gb.report2/.src/Tests/old/Report8.report new file mode 100644 index 00000000..8039d12d --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report8.report @@ -0,0 +1,22 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Index = 0 + Text = ("") + { ReportPanel1 ReportPanel + #MoveScaled(1,4,61,53) + Expand = True + { ReportPanel2 ReportPanel + #MoveScaled(16,7,20,14) + Left = "23mm" + Top = "16mm" + Width = "55mm" + Height = "38mm" + Border = ReportBorder["Top:1px #000000;Bottom:1px #000000;Left:1px #000000;Right:1px #000000;TopLeftCorner:5mm 4mm;TopRightCorner:7mm 15mm;BottomRightCorner:5mm;BottomLeftCorner:5mm 2mm"] + Background = ReportBrush["#00FF00"] + BoxShadow = ReportBoxShadow["1mm 1mm 6px -1px #000000"] + } + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tests/old/Report9.class b/comp/src/gb.report2/.src/Tests/old/Report9.class new file mode 100644 index 00000000..fdfb323f --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report9.class @@ -0,0 +1,9 @@ +' Gambas class file + + +Public Sub Report_Open() + + ReportImage1.Image = Image.Load("~/Documents/SCEA BODARD/Banque/Factures Prêt/Prêt N°00000221849/SCB-Distillerie-1-4.jpg") + + +End diff --git a/comp/src/gb.report2/.src/Tests/old/Report9.report b/comp/src/gb.report2/.src/Tests/old/Report9.report new file mode 100644 index 00000000..252cdbd9 --- /dev/null +++ b/comp/src/gb.report2/.src/Tests/old/Report9.report @@ -0,0 +1,12 @@ +# Gambas Form File 3.0 + +{ Report Report + #MoveScaled(0,0,64,64) + Index = 0 + Text = ("") + { ReportImage1 ReportImage + #MoveScaled(1,1,53,51) + Expand = True + } + Index = 0 +} diff --git a/comp/src/gb.report2/.src/Tools/CPrint.class b/comp/src/gb.report2/.src/Tools/CPrint.class new file mode 100644 index 00000000..cc785c31 --- /dev/null +++ b/comp/src/gb.report2/.src/Tools/CPrint.class @@ -0,0 +1,54 @@ +' Gambas class file + +Create Static +Private hPrinter As New Printer As "Printer" +Private $hPrint As Report + +Public Sub PrintReport(hReport As Report, Optional MyPrinter As Printer) + + Dim hSizeParse As TSizeParse + $hPrint = hReport + '$hPrint = hReport.Clone() + If MyPrinter Then + hPrinter = MyPrinter + Else + hPrinter = New Printer + Endif + Object.Attach(hPrinter, Me, "Printer") + + hPrinter.Paper = $hPrint.Paper + hPrinter.Orientation = $hPrint.Orientation + If $hPrint.Paper = Printer.Custom Then + hSizeParse = New TSizeParse($hPrint.Width) + hPrinter.PaperWidth = Report.UnitTo(hSizeParse.ToInch(), "in", "mm") ' * 10 + hSizeParse = New TSizeParse($hPrint.Height) + hPrinter.PaperHeight = Report.UnitTo(hSizeParse.ToInch(), "in", "mm") ' * 10 + Endif + If Not MyPrinter Then + If Not hPrinter.Configure() Then + hPrinter.Print + Endif + Else + hPrinter.Print + Endif +End + +Public Sub Printer_Draw() + + $hPrint.Paint(hPrinter.Page) + +End + +Public Sub Printer_Begin() + + 'Debug Object.Type($hReport) + $hPrint.Layout + hPrinter.Count = $hPrint.PageCount + +End + +Public Sub Printer_End() + + $hPrint = Null + +End diff --git a/comp/src/gb.report2/.src/Tools/MUtil.module b/comp/src/gb.report2/.src/Tools/MUtil.module new file mode 100644 index 00000000..7ff8550f --- /dev/null +++ b/comp/src/gb.report2/.src/Tools/MUtil.module @@ -0,0 +1,24 @@ +' Gambas module file + +Private Dash As Float[] = [2.0, 1.0] +Private Dot As Float[] = [1.0, 1.0] +Private DashDot As Float[] = [2.0, 1.0, 1.0, 1.0] +Private DashDotDot As Float[] = [2.0, 1.0, 1.0, 1.0, 1.0, 1.0] + + +Public Sub GetBorder(iStyle As Integer) As Float[] + + Select Case iStyle + + Case Line.Dash + Return Dash + Case Line.Dot + Return Dot + Case Line.DashDot + Return DashDot + Case Line.DashDotDot + Return DashDotDot + End Select + +End + diff --git a/comp/src/gb.report2/.src/Types/Base/ReportBrush.class b/comp/src/gb.report2/.src/Types/Base/ReportBrush.class new file mode 100644 index 00000000..49b334b2 --- /dev/null +++ b/comp/src/gb.report2/.src/Types/Base/ReportBrush.class @@ -0,0 +1,222 @@ +' Gambas class file + +Export + +Public _Type As Integer +Public _X As Float +Public _Y As Float +Public _X2 As Float +Public _Y2 As Float +Public _radius As Float +Public _Image As Image +Public _Color As Integer[] = [0, &HFFFFFF&] +Public _Pos As Float[] = [0, 1] +Private _ImageDir As String + +Public Function _PaintBrush(X1 As Integer, Y1 As Integer, X2 As Integer, Y2 As Integer) As PaintBrush + + Dim hBrush As PaintBrush + Dim fradius As Float + Dim Width As Integer = X2 - X1 + Dim Height As Integer = Y2 - Y1 + + Select Case Me._Type + Case 0 + hBrush = Paint.Color(_Color[0]) + Case 1 + hBrush = paint.Image(_Image, X1, Y1) + Case 2 + hBrush = paint.LinearGradient(Width * _X, Height * _Y, Width * _X2, Height * _Y2, _Color, _Pos) + Case 3 + fradius = Width * _radius + hBrush = paint.RadialGradient(Width * _X, Height * _Y, fradius, Width * _X2, Height * _Y2, _Color, _Pos) + End Select + hBrush.Translate(x1, Y1) + Return hBrush + +End + +Static Public Sub _get(sValue As String) As ReportBrush + + Dim hBrush As New ReportBrush + Dim iPos As Integer + Dim sType As String + + Dim ars As String[] + + + + If Not svalue Then Return Null + + sValue = Trim(sValue) + + iPos = InStr(sValue, "(") + + If iPos Then sType = Left(sValue, iPos - 1) + 'pas de parenthese de fin + If ipos And Not (sValue Ends ")") Then + Error.Raise + Else + If iPos Then svalue = Left(Mid(svalue, ipos + 1), -1) + Endif + ars = Split(sValue, ",", "[]") + Select Case LCase(sType) + Case "image" + hBrush._Type = 1 + + hBrush._Image = Image.Load(Replace(ars[0], "\"", "")) + hBrush._ImageDir = ars[0] + Case "radialgradient" + hBrush._Type = 3 + hBrush._X = CFloat(ars[0]) + hBrush._Y = CFloat(ars[1]) + hBrush._Radius = CFloat(ars[2]) + hBrush._X2 = CFloat(ars[3]) + hBrush._Y2 = CFloat(ars[4]) + hBrush._Color = GetIntegerArray(ars[5]) + hBrush._Pos = GetFloatArray(ars[6]) + + Case "lineargradient" + + hBrush._Type = 2 + hBrush._X = CFloat(ars[0]) + hBrush._Y = CFloat(ars[1]) + hBrush._X2 = CFloat(ars[2]) + hBrush._Y2 = CFloat(ars[3]) + hBrush._Color = GetIntegerArray(ars[4]) + hBrush._Pos = GetFloatArray(ars[5]) + + Case Else + 'correction de l'hexon + '= Val(Replace(ars[0], "#", "&H") & "00&") + If ars[0] Begins "#" Then ars[0] = Mid(ars[0], 2) + hBrush._Color[0] = Val("&H" & IIf(Len(ars[0]) = 8, ars[0], 00 & ars[0])) + End Select + Return hBrush + +Finally + If hBrush._Color.Count = 0 Then hBrush._Color = [0, &hFFFFFF&] + If hBrush._Color.Count < 2 Then hBrush._Color.Add(&hFFFFFF&) + If hBrush._Pos.Count < 2 Then hBrush._Pos = [0.0, 1.0] + + +Catch + Return hBrush + +End + +Static Private Function GetIntegerArray(sValue As String) As Integer[] + + Dim ari As New Integer[] + Dim s As String + + For Each s In Split(sValue) + If s Begins "#" Then s = Mid(s, 2) + + ari.Add(Val("&H" & IIf(Len(s) = 8, s, "00" & s))) + Next + + Return ari + +End + +Static Private Function GetFloatArray(sValue As String) As Float[] + + Dim arf As New Float[] + Dim s As String + + For Each s In Split(sValue) + arf.Add(CFloat(s)) + Next + + Return arf + +End + +Public Function ToString() As String + + Dim i As Integer + Dim f As Float + Dim sValue As String + + Select Case _Type + Case 0 + sValue = "&H" & Hex(_Color[0], 8) & "&" + Case 2, 3 + If _Type = 3 Then + sValue = "RadialGradient(" & _X & "," & _Y & "," & _Radius & "," & _X2 & "," & _Y2 & ",[" + + Else + sValue = "LinearGradient(" & _X & "," & _Y & "," & _X2 & "," & _Y2 & ",[" + Endif + + For Each i In _Color + sValue &= "&H" & Hex(i, 6) & "&," + + Next + + sValue = Left(sValue, -1) + + sValue &= "],[" + + For Each f In _Pos + sValue &= f & "," + + Next + sValue = Left(sValue, -1) + + sValue &= "])" + Case 1 + sValue = "Image(" & _ImageDir & ")" + + End Select + + Return svalue + +End + +Static Public Function Color(iColor As Integer) As ReportBrush + + Dim hBrush As New ReportBrush + + hBrush._Color[0] = iColor + hBrush._Type = 0 + Return hBrush + +End + +Static Public Function LinearGradient(X As Float, Y As Float, X2 As Float, Y2 As Float, aColor As Integer[], aPos As Float[]) As ReportBrush + Dim hBrush As New ReportBrush + + hBrush._X = X + hBrush._Y = Y + hBrush._X2 = X2 + hBrush._Y2 = Y2 + hBrush._Color = aColor + hBrush._Pos = aPos + hBrush._Type = 2 + Return hBrush +End + +Static Public Function RadialGradient(X As Float, Y As Float, Radius As Float, X2 As Float, Y2 As Float, aColor As Integer[], aPos As Float[]) As ReportBrush + Dim hBrush As New ReportBrush + + hBrush._X = X + hBrush._Y = Y + hBrush._X2 = X2 + hBrush._Y2 = Y2 + hBrush._radius = Radius + hBrush._Color = aColor + hBrush._Pos = aPos + hBrush._Type = 3 + Return hBrush +End + +Static Public Function Image(hImage As Image) As ReportBrush + Dim hBrush As New ReportBrush + + hBrush._Image = hImage + hBrush._Type = 1 + Return hBrush + +End diff --git a/comp/src/gb.report2/.src/Types/Base/ReportMargin.class b/comp/src/gb.report2/.src/Types/Base/ReportMargin.class new file mode 100644 index 00000000..3574c68a --- /dev/null +++ b/comp/src/gb.report2/.src/Types/Base/ReportMargin.class @@ -0,0 +1,40 @@ +' Gambas class file + +Export +Inherits ReportPadding +Static Public Sub _Get(Value As String) As ReportMargin + + Dim hMargin As New ReportMargin + Dim hSize As TSizeParse + Dim s As String + Dim ars As String[] + + For Each s In Split(Value, ";") + ars = Scan(s, "*:*") + If ars.count > 1 Then + Select Case LCase(ars[0]) + Case "left" + hMargin.Left = ars[1] + Case "top" + hMargin.top = ars[1] + Case "bottom" + hMargin.bottom = ars[1] + Case "right" + hMargin.Right = ars[1] + Case Else + + End Select + Else + Try hSize = New TSizeParse(s) + If hSize <> Null Then + hMargin.Left = s + hMargin.Right = s + hMargin.Bottom = s + hMargin.Top = s + Endif + Endif + + Next + Return hMargin + +End diff --git a/comp/src/gb.report2/.src/Types/Base/ReportPadding.class b/comp/src/gb.report2/.src/Types/Base/ReportPadding.class new file mode 100644 index 00000000..6c4ab28a --- /dev/null +++ b/comp/src/gb.report2/.src/Types/Base/ReportPadding.class @@ -0,0 +1,94 @@ +' Gambas class file + +Export + +'>>>>>>>>>>>>>>>>>>>>>> PUBLIC VARIABLES >>>>>>>>>>>>>>>>>>>>>>>>> +Public {Left} As String +Public Right As String +Public Top As String +Public Bottom As String +Public _Left As Float +Public _Right As Float +Public _Top As Float +Public _Bottom As Float + +'>>>>>>>>>>>>>>>>>>>>>>>>>> PROPERTIES >>>>>>>>>>>>>>>>>>>>>>>>>>> +Property Read _Width As Float +Property Read _Height As Float + +'>>>>>>>>>>>>>>>>>>>>>> PRIVATE VARIABLES >>>>>>>>>>>>>>>>>>>>>>>> + +'##################### PUBLIC PROCEDURES ######################### +Public Sub IsActive() As Boolean + + If {Left} Or If Top Or If Right Or If Bottom Then Return True + Return False + +End + +Static Public Sub _Get(Value As String) As ReportPadding + + Dim hPadding As New ReportPadding + Dim hSize As TSizeParse + Dim s As String + Dim ars As String[] + + For Each s In Split(Value, ";") + ars = Scan(s, "*:*") + If ars.count > 1 Then + Select Case LCase(ars[0]) + Case "left" + hPadding.Left = ars[1] + Case "top" + hPadding.top = ars[1] + Case "bottom" + hPadding.bottom = ars[1] + Case "right" + hPadding.Right = ars[1] + Case Else + + End Select + Else + Try hSize = New TSizeParse(s) + If hSize <> Null Then + hPadding.Left = s + hPadding.Right = s + hPadding.Bottom = s + hPadding.Top = s + Endif + Endif + + Next + Return hPadding + +End + +Public Sub _NormalizeUnits() + + Dim hSizeParse As TSizeParse + + hSizeParse = TSizeParse[{Left}] + _Left = hSizeParse.ToInch() + hSizeParse = TSizeParse[Top] + _Top = hSizeParse.ToInch() + hSizeParse = TSizeParse[Right] + _Right = hSizeParse.ToInch() + hSizeParse = TSizeParse[Bottom] + _Bottom = hSizeParse.ToInch() + +End + +'##################### PRIVATE PROCEDURES ######################## + +'######################## PROPERTIES ############################# +Private Function _Width_Read() As Float + + Return _Left + _Right + +End + +Private Function _Height_Read() As Float + + Return _top + _Bottom + +End diff --git a/comp/src/gb.report2/.src/Types/Border/ReportBorder.class b/comp/src/gb.report2/.src/Types/Border/ReportBorder.class new file mode 100644 index 00000000..eb22f118 --- /dev/null +++ b/comp/src/gb.report2/.src/Types/Border/ReportBorder.class @@ -0,0 +1,205 @@ +' Gambas class file + +Export +'>>>>>>>>>>>>>>>>>>>>>> PUBLIC VARIABLES >>>>>>>>>>>>>>>>>>>>>>>>> +Static Public Const None As Integer = 0 +Static Public Const Solid As Integer = 1 + +Public _Left As Float +Public _Right As Float +Public _Top As Float +Public _Bottom As Float +Public Style As Integer +'>>>>>>>>>>>>>>>>>>>>>>>>>> PROPERTIES >>>>>>>>>>>>>>>>>>>>>>>>>>> +Property Read Top As _ReportBorderSide +Property Read {Left} As _ReportBorderSide +Property Read bottom As _ReportBorderSide +Property Read {Right} As _ReportBorderSide +Property Read RoundCorner As _ReportRoundCorner +Property Width As String +Property Brush As ReportBrush +Property Read _Height As Float +Property Read _Width As Float + + +'>>>>>>>>>>>>>>>>>>>>>> PRIVATE VARIABLES >>>>>>>>>>>>>>>>>>>>>>>> +' Static Private $aShadowStyle As String[] = ["none", "solid", "gradiant"] +' Static Private $aAlign As String[] = Classes["Align"].Symbols + +Private hTop As New _ReportBorderSide +Private hBottom As New _ReportBorderSide +Private hLeft As New _ReportBorderSide +Private hRight As New _ReportBorderSide +Private hRCorner As New _ReportRoundCorner + + +'##################### PUBLIC PROCEDURES ######################### + +Static Public Function _get(sValue As String) As ReportBorder + + Dim hReportBorder As New ReportBorder + + Dim sBorder As String + Dim aScan As String[] + + If Not sValue Then Return hReportBorder + hReportBorder.Style = ReportBorder.Solid + hReportBorder.RoundCorner._Active = False + For Each sBorder In Split(sValue, ";") + + aScan = Scan(sBorder, "*:*") + Select Case LCase(Trim(aScan[0])) + Case "border" + FillObject(hReportBorder, aScan[1]) + Case "left" + FillObject(hReportBorder.Left, aScan[1]) + Case "right" + FillObject(hReportBorder.Right, aScan[1]) + Case "bottom" + FillObject(hReportBorder.bottom, aScan[1]) + Case "top" + FillObject(hReportBorder.Top, aScan[1]) + Case "topleftcorner" + hReportBorder.RoundCorner.TopLeft = aScan[1] + Case "toprightcorner" + hReportBorder.RoundCorner.TopRight = aScan[1] + Case "bottomleftcorner" + hReportBorder.RoundCorner.BottomLeft = aScan[1] + Case "bottomrightcorner" + hReportBorder.RoundCorner.BottomRight = aScan[1] + + End Select + + Next + +Finally + Return hReportBorder + +End + +Public Function ToString() As String + + Dim aValue As New String[] + + If Me.Style = ReportBorder.None Then Return 'aValue.Add("None") + 'If Me.Style = ReportBorder.Solid Then aValue.Add("Solid") + aValue.Add(Me.Width) + If Me.Top Then aValue.Add("Top") + If Me.Bottom Then aValue.Add("Bottom") + If Me.Left Then aValue.Add("Left") + If Me.Right Then aValue.Add("Right") + aValue.Add("#" & Hex(Me.Color, 6)) + Return aValue.Join() + +End + +Public Sub _NormalizeUnits() + + Dim hSizeParse As TSizeParse + + hSizeParse = New TSizeParse(hLeft.Width) + _Left = hSizeParse.GetValue() + hSizeParse = New TSizeParse(hRight.Width) + _Right = hSizeParse.GetValue() + hSizeParse = New TSizeParse(hTop.Width) + _Top = hSizeParse.GetValue() + hSizeParse = New TSizeParse(hBottom.Width) + _Bottom = hSizeParse.GetValue() + + hRCorner._NormalizeUnits() + +End + +'##################### PRIVATE PROCEDURES ######################## + +Static Private Sub FillObject(hObj As Object, sValue As String) + + Dim sBrush As String + + For Each sBrush In Split(sValue, " ") + + If IsDigit(Left(sBrush)) Then + Try hObj.Width = sBrush + Else + Try hObj.Brush = ReportBrush[sBrush] + If hObj.Brush = Null Then hObj.Brush = ReportBrush["&H0"] + Endif + Next + +End + +'######################## PROPERTIES ############################# + +Private Function Top_Read() As _ReportBorderSide + + Return hTop + +End + +Private Function Left_Read() As _ReportBorderSide + + Return hLeft + +End + +Private Function bottom_Read() As _ReportBorderSide + + Return hBottom + +End + +Private Function Width_Read() As String + + Return hLeft.Width + +End + +Private Sub Width_Write(Value As String) + + hLeft.Width = Value + hTop.Width = Value + hBottom.Width = Value + hRight.Width = Value + +End + +Private Function Brush_Read() As ReportBrush + + Return hLeft.Brush + +End + +Private Sub Brush_Write(Value As ReportBrush) + + hLeft.Brush = Value + hTop.Brush = Value + hBottom.Brush = Value + hRight.Brush = Value + +End + +Private Function Right_Read() As _ReportBorderSide + + Return hRight + +End + +Private Function RoundCorner_Read() As _ReportRoundCorner + + Return hRCorner + +End + + + +Private Function _Height_Read() As Float + + Return _Top + _Bottom + +End + +Private Function _Width_Read() As Float + + Return _Left + _Right + +End diff --git a/comp/src/gb.report2/.src/Types/Border/_ReportBorderSide.class b/comp/src/gb.report2/.src/Types/Border/_ReportBorderSide.class new file mode 100644 index 00000000..346443be --- /dev/null +++ b/comp/src/gb.report2/.src/Types/Border/_ReportBorderSide.class @@ -0,0 +1,16 @@ +' Gambas class file + +Export +Public Width As String +Public Brush As New ReportBrush +Public Round1 As String +Public Round2 As String +Public _Width As Float + +Public Sub _NormalizeUnits() + Dim hSizeParse As New TSizeParse(Width) + + _Width = hSizeParse.GetValue() + + +End diff --git a/comp/src/gb.report2/.src/Types/Border/_ReportRoundCorner.class b/comp/src/gb.report2/.src/Types/Border/_ReportRoundCorner.class new file mode 100644 index 00000000..e3eb4a5b --- /dev/null +++ b/comp/src/gb.report2/.src/Types/Border/_ReportRoundCorner.class @@ -0,0 +1,137 @@ +' Gambas class file + +Export +Public _Active As Boolean = False +Private $sTopLeft As String = "0cm" +Private $sTopRight As String = "0cm" +Private $sBottomLeft As String = "0cm" +Private $sBottomRight As String = "0cm" + +Property TopLeft As String +Property TopRight As String +Property BottomLeft As String +Property BottomRight As String + +Public _TopLeft1 As Float +Public _TopLeft2 As Float +Public _TopRight1 As Float +Public _TopRight2 As Float +Public _BottomLeft1 As Float +Public _BottomLeft2 As Float +Public _BottomRight1 As Float +Public _BottomRight2 As Float + +Private Function TopLeft_Read() As String + + Return $sTopLeft + +End + +Private Sub TopLeft_Write(Value As String) + + $sTopLeft = Value + _Active = True + +End + +Private Function TopRight_Read() As String + + Return $sTopRight + +End + +Private Sub TopRight_Write(Value As String) + + $sTopRight = Value + _Active = True + +End + +Private Function BottomLeft_Read() As String + + Return $sBottomLeft + +End + +Private Sub BottomLeft_Write(Value As String) + + $sBottomLeft = Value + _Active = True + +End + +Private Function BottomRight_Read() As String + + Return $sBottomRight + +End + +Private Sub BottomRight_Write(Value As String) + + $sBottomRight = Value + _Active = True + +End + +Public Sub _NormalizeUnits() + + Dim aEl As String[] + + Dim hSizeParse As TSizeParse + + + ael = Split($sTopLeft, " ") + If ael.Count = 1 Then + hSizeParse = New TSizeParse(ael[0]) + _TopLeft1 = hSizeParse.GetValue() + _TopLeft2 = _TopLeft1 + Else + If ael.Count = 2 Then + hSizeParse = New TSizeParse(ael[0]) + _TopLeft1 = hSizeParse.GetValue() + hSizeParse = New TSizeParse(ael[1]) + _TopLeft2 = hSizeParse.GetValue() + Endif + Endif + + ael = Split($sTopRight, " ") + If ael.Count = 1 Then + hSizeParse = New TSizeParse(ael[0]) + _TopRight1 = hSizeParse.GetValue() + _TopRight2 = _TopRight1 + Else + If ael.Count = 2 Then + hSizeParse = New TSizeParse(ael[0]) + _TopRight1 = hSizeParse.GetValue() + hSizeParse = New TSizeParse(ael[1]) + _TopRight2 = hSizeParse.GetValue() + Endif + Endif + ael = Split($sBottomRight, " ") + If ael.Count = 1 Then + hSizeParse = New TSizeParse(ael[0]) + _BottomRight1 = hSizeParse.GetValue() + _BottomRight2 = _BottomRight1 + Else + If ael.Count = 2 Then + hSizeParse = New TSizeParse(ael[0]) + _BottomRight1 = hSizeParse.GetValue() + hSizeParse = New TSizeParse(ael[1]) + _BottomRight2 = hSizeParse.GetValue() + Endif + Endif + ael = Split($sBottomLeft, " ") + If ael.Count = 1 Then + hSizeParse = New TSizeParse(ael[0]) + _BottomLeft1 = hSizeParse.GetValue() + _BottomLeft2 = _BottomLeft1 + Else + If ael.Count = 2 Then + hSizeParse = New TSizeParse(ael[0]) + _BottomLeft1 = hSizeParse.GetValue() + hSizeParse = New TSizeParse(ael[1]) + _BottomLeft2 = hSizeParse.GetValue() + Endif + Endif + +End diff --git a/comp/src/gb.report2/.src/Types/BoxShadow/ReportBoxShadow.class b/comp/src/gb.report2/.src/Types/BoxShadow/ReportBoxShadow.class new file mode 100644 index 00000000..95957db3 --- /dev/null +++ b/comp/src/gb.report2/.src/Types/BoxShadow/ReportBoxShadow.class @@ -0,0 +1,225 @@ +' Gambas class file + +Export +Private $aBoxShadow As New _ReportBoxShadow[] + +Property XOffset As String +Property YOffset As String +Property Blur As String +Property Spread As String +Property Inset As Boolean +Property Color As Integer +Property Read Count As Integer +Property Read Max As Integer + + +Property Read _XOffset As Float +Property Read _YOffset As Float +Property Read _Blur As Float +Property Read _Spread As Float +Property Read _Active As Boolean + + +Public Sub _new() + + Dim hBoxShadow As New _ReportBoxShadow + $aBoxShadow.Add(hBoxShadow) + +End + +Static Public Function _get(sValue As String) As ReportBoxShadow + + Dim hBoxShadow As New ReportBoxShadow + Dim s As String + Dim i As Integer + For Each s In Split(sValue, " ") + If IsDigit(Left(s)) Or If Left(s) = "-" Then + + Select Case i + Case 0 + hBoxShadow.XOffset = s + Case 1 + hBoxShadow.YOffset = s + Case 2 + hBoxShadow.Blur = s + Case 3 + hBoxShadow.Spread = s + End Select + Inc i + Continue + Endif + If LCase(s) = "inset" Then + hBoxShadow.Inset = True + Continue + Endif + Try hBoxShadow.Color = Val(Replace(s, "#", "&H")) + + Next + Return hBoxShadow + +End + + + +' Public Function _get(Index As Integer) As _ReportBoxShadow +' +' Return $aBoxShadow[Index] +' +' End + + + +Public Sub Add(Optional XOffset As String, YOffset As String, iColor As Integer, Spread As String, Blur As String, Inset As Boolean) + + Dim hBoxShadow As New _ReportBoxShadow + + If XOffset Then hBoxShadow.XOffset = XOffset + If YOffset Then hBoxShadow.YOffset = YOffset + If iColor Then hBoxShadow.Color = iColor + If SPread Then hBoxShadow.Spread = Spread + If Blur Then hBoxShadow.Blur = Blur + hBoxShadow.Inset = Inset + $aBoxShadow.Add(hBoxShadow) + +End + +Public Sub Remove(Index As Integer) + + $aBoxShadow.Remove(Index) + +End + + + +Private Function XOffset_Read() As String + + Return $aBoxShadow[0].XOffset + +End + +Private Sub XOffset_Write(Value As String) + + $aBoxShadow[0].XOffset = Value + +End + +Private Function YOffset_Read() As String + + Return $aBoxShadow[0].YOffset + +End + +Private Sub YOffset_Write(Value As String) + + $aBoxShadow[0].YOffset = Value + +End + +Private Function Blur_Read() As String + + Return $aBoxShadow[0].Blur + +End + +Private Sub Blur_Write(Value As String) + + $aBoxShadow[0].Blur = Value + +End + +Private Function Spread_Read() As String + + Return $aBoxShadow[0].Spread + +End + +Private Sub Spread_Write(Value As String) + + $aBoxShadow[0].Spread = Value + +End + +Private Function Inset_Read() As Boolean + + Return $aBoxShadow[0].Inset + +End + +Private Sub Inset_Write(Value As Boolean) + + $aBoxShadow[0].Inset = Value + +End + +Private Function Color_Read() As Integer + + Return $aBoxShadow[0].Color + +End + +Private Sub Color_Write(Value As Integer) + + $aBoxShadow[0].Color = Value + +End + +Private Function Count_Read() As Integer + + Return $aBoxShadow.Count + +End + +Private Function Max_Read() As Integer + + Return $aBoxShadow.Max + +End + +Public Sub _NormalizeUnits() + + Dim i As Integer + 'Dim SizeParse As TSizeParse + For i = 0 To $aBoxShadow.Max + 'With + $aBoxShadow[i]._XOffset = TSizeParse[$aBoxShadow[i].XOffset].GetValue() + $aBoxShadow[i]._YOffset = TSizeParse[$aBoxShadow[i].YOffset].GetValue() + $aBoxShadow[i]._Spread = TSizeParse[$aBoxShadow[i].Spread].GetValue() + $aBoxShadow[i]._Blur = TSizeParse[$aBoxShadow[i].Blur].GetValue() + + '._Blur = TSizeParse[.Blur].GetValue() + 'End With + + + Next + +End + +Private Function _XOffset_Read() As Float + + Return $aBoxShadow[0]._XOffset + +End + +Private Function _YOffset_Read() As Float + + Return $aBoxShadow[0]._YOffset + +End + +Private Function _Blur_Read() As Float + + Return $aBoxShadow[0]._Blur + +End + +Private Function _Spread_Read() As Float + + Return $aBoxShadow[0]._Spread + +End + +Private Function _Active_Read() As Boolean + + Return Not ($aBoxShadow[0]._XOffset = 0 And $aBoxShadow[0]._YOffset = 0 And $aBoxShadow[0]._Blur = 0 And $aBoxShadow[0]._Spread = 0) + +End diff --git a/comp/src/gb.report2/.src/Types/BoxShadow/_ReportBoxShadow.class b/comp/src/gb.report2/.src/Types/BoxShadow/_ReportBoxShadow.class new file mode 100644 index 00000000..46f8af6c --- /dev/null +++ b/comp/src/gb.report2/.src/Types/BoxShadow/_ReportBoxShadow.class @@ -0,0 +1,14 @@ +' Gambas class file + +Export +Public XOffset As String '= "8mm" +Public YOffset As String '= "2mm" +Public Spread As String +Public Inset As Boolean +Public Color As Integer +Public Blur As String + +Public _XOffset As Float +Public _YOffset As Float +Public _Spread As Float +Public _Blur As Float diff --git a/comp/src/gb.report2/.src/Types/ReportSizeHints.class b/comp/src/gb.report2/.src/Types/ReportSizeHints.class new file mode 100644 index 00000000..20421e8f --- /dev/null +++ b/comp/src/gb.report2/.src/Types/ReportSizeHints.class @@ -0,0 +1,26 @@ +' Gambas class file + +Export +Public Width As Float +Public Height As Float +Public NotFinished As Boolean + +Static Public Function _call(Width As Float, Height As Float, Optional NotFinished As Float) As ReportSizeHints + Dim hSH As New ReportSizeHints + hSH.Width = Width + hSH.Height = Height + hSH.NotFinished = NotFinished + Return hSH +End + +Static Public Function FromString(Width As String, Height As String, Optional NotFinished As Boolean) As ReportSizeHints + + Dim hSH As New ReportSizeHints + hSH.Width = TSizeParse[Width].ToInch() + hSH.Height = TSizeParse[Height].ToInch() + hSH.NotFinished = NotFinished + Return hSH + +End + + diff --git a/comp/src/gb.report2/.src/Types/ReportSizeParser.class b/comp/src/gb.report2/.src/Types/ReportSizeParser.class new file mode 100644 index 00000000..8ca8b425 --- /dev/null +++ b/comp/src/gb.report2/.src/Types/ReportSizeParser.class @@ -0,0 +1,83 @@ +' Gambas class file + +' Gambas class file + +Export +Fast +'>>>>>>>>>>>>>>>>>>>>>> PUBLIC VARIABLES >>>>>>>>>>>>>>>>>>>>>>>>> +Public Value As Float +Public Unit As String + +'>>>>>>>>>>>>>>>>>>>>>>>>>> PROPERTIES >>>>>>>>>>>>>>>>>>>>>>>>>>> +'>>>>>>>>>>>>>>>>>>>>>> PRIVATE VARIABLES >>>>>>>>>>>>>>>>>>>>>>>> + +'##################### PUBLIC PROCEDURES ######################### +'' Create a new size object from its description. +'' #bAllowRelative# can be TRUE to allow the use of "%" as unit. +Public Sub _new(Size As String, Optional bAllowRelative As Boolean) + + Dim I As Integer + Dim sCar As String + + If Not size Then + Unit = "cm" + Value = 0 + Endif + + Size = Trim(Size) + + If Not Size Then + Unit = "px" + Return + Endif + + For I = 1 To Len(Size) + sCar = Mid$(Size, I, 1) + If Not IsDigit(sCar) And If sCar <> "-" And If sCar <> "." Then Break + Next + + Value = CFloat(Left$(Size, I - 1)) + Unit = Trim(Mid$(Size, I)) + + If Not Unit Then Unit = "px" + + If Not Report.AllowedUnits.Exist(Unit) Then + If Not bAllowRelative Or Unit <> "%" Then + Error.Raise("Unknown unit") + Endif + Endif + +End + +Public Sub ToInch() As Float + + Return Report.UnitToInch(Value, Unit) + +End + +Public Sub IsRelative() As Boolean + + Return Unit = "%" + +End + +Public Sub GetValue() As Float + + If IsRelative() Then + Return Value + Else + Return ToInch() + Endif + +End + +Static Public Sub _get(Size As String) As TSizeParse + + Dim hSize As TSizeParse = New TSizeParse(Size) + + Return hSize + +End + +'##################### PRIVATE PROCEDURES ######################## +'######################## PROPERTIES ############################# diff --git a/comp/src/gb.report2/.src/Types/TControl.class b/comp/src/gb.report2/.src/Types/TControl.class new file mode 100644 index 00000000..071dad65 --- /dev/null +++ b/comp/src/gb.report2/.src/Types/TControl.class @@ -0,0 +1,3 @@ +' Gambas class file + +Inherits _ReportVirtualControl diff --git a/comp/src/gb.report2/.src/Types/TSizeParse.class b/comp/src/gb.report2/.src/Types/TSizeParse.class new file mode 100644 index 00000000..c6ca4e2d --- /dev/null +++ b/comp/src/gb.report2/.src/Types/TSizeParse.class @@ -0,0 +1,5 @@ +' Gambas class file + +Export + +Inherits ReportSizeParser diff --git a/comp/src/gb.report2/.src/Types/_ReportVirtualControl.class b/comp/src/gb.report2/.src/Types/_ReportVirtualControl.class new file mode 100644 index 00000000..ca8fa033 --- /dev/null +++ b/comp/src/gb.report2/.src/Types/_ReportVirtualControl.class @@ -0,0 +1,78 @@ +' Gambas class file + +Export +'Fast +'>>>>>>>>>>>>>>>>>>>>>> PUBLIC VARIABLES >>>>>>>>>>>>>>>>>>>>>>>>> +Public SizeHint As ReportSizeHints ''Size hints buffer +Public _PageChildren As New Collection ''The links between page and objects +Public Index As Integer ''Remember the dataindex +Public Cache As Variant ''For future data cache + +'>>>>>>>>>>>>>>>>>>>>>>>>>> PROPERTIES >>>>>>>>>>>>>>>>>>>>>>>>>>> +Property Read RealLeft As Float ''Return the left relative position size in Pixel +Property Read RealTop As Float ''Return the top relative position size in Pixel +Property Read RealWidth As Float ''Return the width in pixel +Property Read RealHeight As Float ''Return the height in pixel +Property Ctrl As ReportControl ''Return or set the link between real widget and virtual object + +'>>>>>>>>>>>>>>>>>>>>>> PRIVATE VARIABLES >>>>>>>>>>>>>>>>>>>>>>>> +Private $iCtrl As Integer +Private fX As Float +Private fY As Float +Private fW As Float +Private fH As Float + + +'##################### PUBLIC PROCEDURES ######################### +''Define the virtual widget size in internal unit (cm) +Public Function _SetGeometry(X As Float, Y As Float, W As Float, H As Float) As Float + + fX = X + fY = Y + fW = W + fH = H + +End + +'##################### PRIVATE PROCEDURES ######################## + + + +'######################## PROPERTIES ############################# + +Private Function Ctrl_Read() As ReportControl + + Return ReportControl._ObjectFromId[$iCtrl] + +End + + +Private Sub Ctrl_Write(Value As ReportControl) + + $iCtrl = Value.id + +End + +Private Function RealLeft_Read() As Float + + Return ReportControl._ObjectFromId[$iCtrl].Report._ToPixels(fX) 'ReportControl._ObjectFromId[$iCtrl].Report._ToPixels(fX) + +End + +Private Function RealTop_Read() As Float + + Return ReportControl._ObjectFromId[$iCtrl].Report._ToPixels(fY) + +End + +Private Function RealWidth_Read() As Float + + Return ReportControl._ObjectFromId[$iCtrl].Report._ToPixels(fX + fW) - ReportControl._ObjectFromId[$iCtrl].Report._ToPixels(fX) + +End + +Private Function RealHeight_Read() As Float + + Return ReportControl._ObjectFromId[$iCtrl].Report._ToPixels(fY + fH) - ReportControl._ObjectFromId[$iCtrl].Report._ToPixels(fY) + +End diff --git a/comp/src/gb.report2/.src/Viewer/FPreview.class b/comp/src/gb.report2/.src/Viewer/FPreview.class new file mode 100644 index 00000000..b422247a --- /dev/null +++ b/comp/src/gb.report2/.src/Viewer/FPreview.class @@ -0,0 +1,535 @@ +' Gambas class file + +Private $aModeButtons As New ToolButton[] +Private bFlag As Boolean +Private $hPrinter As New Printer As "Printer" +'Private $iCurPrinterResolution As Integer +Private $bVerif As Boolean +Private $bPrinting As Boolean +Private $iFileRes As Integer = 150 + +Static Public Function Run(hReport As Report) + + Dim hFPreview As FPreview + + hFpreview = New FPreview + + hFPreview.SetReport(hReport) + hFPreview.ShowModal() + +End + +Public Sub _new() + + Dim s As String + Dim hMenu As Menu + + For Each s In Printer.List + cmbPrinter.Add(s) + hMenu = New Menu(mnuPrint) As "mnuPrintPrinter" + hMenu.Tag = s + hMenu.Text = s + Next + +End + +Private Sub SelectPrinter(Optional sName As String) + + Dim iPrinter As Integer + + If sName Then + $hPrinter.Name = sName + Else + $hPrinter.Name = Printer.Default + Endif + + Object.Lock(cmbPrinter) + iPrinter = Max(0, cmbPrinter.List.Find($hPrinter.Name)) + Try cmbPrinter.Index = iPrinter + Object.Unlock(cmbPrinter) + + 'NOTE: force the printer fullpage mode because it make error on report placement + $hPrinter.FullPage = True + $hPrinter.Duplex = cmbDuplex.Index + + tgbGrayScale.Value = $hPrinter.GrayScale + tgbFullPage.Value = $hPrinter.FullPage + tgbReverseCopies.Value = $hPrinter.ReverseOrder + tgbCollateCopies.Value = $hPrinter.CollateCopies + +End + +Public Sub SetReport(hReport As Report) + + View.Report = hReport + GetValuesFromReport() + +End + +Private Sub GetValuesFromReport() + + cmbPaper.Index = View.Report.Paper + $hPrinter.Orientation = View.Report.Orientation + + If View.Report.Paper = Printer.Custom Then + pnlCustom.Enabled = True + $hPrinter.PaperWidth = TSizeParse[View.Report.Width].ToInch() * 10 + $hPrinter.PaperHeight = TSizeParse[View.Report.Height].ToInch() * 10 + Else + $hPrinter.Paper = View.Report.Paper + Endif + + cmbOrientation.Index = $hPrinter.Orientation + cmbDuplex.Index = $hPrinter.Duplex + cmbPaper.Index = $hPrinter.Paper + View.Refresh + + Object.Lock(spWidth) + spWidth.Value = $hPrinter.PaperWidth + Object.Unlock(spWidth) + Object.Lock(spHeight) + spHeight.Value = $hPrinter.PaperHeight + Object.Unlock(spHeight) + +End + +Public Sub sldZoom_Change() + + Dim hButton As ToolButton + + View.ScaleMode = View.Custom + View.Zoom = sldZoom.Value / 100 + 'lblZoom.Text = Str(Slider1.Value) & " %" + bFlag = True + For Each hButton In $aModeButtons + hButton.Value = False + Next + + bFlag = False + +End + +Public Sub View_Zoom() + + Object.Lock(sldZoom) + sldZoom.Value = View.Zoom * 100 + Object.Unlock(sldZoom) + + lblZoom.Text = Str(sldZoom.Value) & " %" + +End + +Public Sub Mode_Click() + + Dim hButton As ToolButton + Dim hCurButton As ToolButton + + If bFlag Then Return + bFlag = True + hCurButton = Last + + For Each hButton In $aModeButtons + If hButton = hCurButton Then + hButton.Value = True + Else + hButton.Value = False + Endif + Next + + bFlag = False + + 'View.ScaleMode = hCurButton.Tag + View.ScaleMode = hCurButton.Tag + 'View.Refresh + 'View.Refresh + +End + +Public Sub tgbGrayScale_Click() + + 'Last.Background = IIf(Last.Value, Color.Gray, Color.Default) + $hPrinter.GrayScale = Last.Value + View._GrayScale = Last.Value + +End + +Public Sub tgbFullPage_Click() + + 'Last.Background = IIf(Last.Value, Color.Gray, Color.Default) + $hPrinter.FullPage = Last.Value + +End + +Public Sub tgbReverseCopies_Click() + + 'Last.Background = IIf(Last.Value, Color.Gray, Color.Default) + $hPrinter.ReverseOrder = Last.value + +End + +Public Sub tgbCollateCopies_Click() + + 'Last.Background = IIf(Last.Value, Color.Gray, Color.Default) + $hPrinter.CollateCopies = Last.Value + +End + +Public Sub cmbOrientation_Click() + + If View.Report.Orientation = Last.Index Then Return + View.Report.Orientation = Last.Index + 'View.Report.Refresh + View.Refresh + GetValuesFromReport + +End + +Public Sub cmbPaper_Click() + + Dim i As Integer = Last.Index + 'Print "id "; View.Report.paper + If View.Report.Paper = i Then Return + View.Report.Paper = i + If i = 0 Then + pnlCustom.Visible = True + Else + pnlCustom.Visible = False + View.Refresh + GetValuesFromReport + Endif + +End + +Public Sub cmbDuplex_Click() + + $hPrinter.Duplex = Last.index + +End + +Public Sub txtFile_Click() + + Dialog.Path = txtFile.Text + Dialog.Filter = ["*.pdf", ("PDF files"), "*.ps", ("Postscript files")] + + If Dialog.SaveFile() Then Return + + txtFile.Text = Dialog.Path + $bVerif = True + $hPrinter.OutputFile = Dialog.Path + +End + +Private Sub RefreshPrintButton() + + If $bPrinting Then + + btnPrint.Picture = Picture["icon:/small/cancel"] + btnPrint.Text = ("Cancel") + + Else + + btnPrint.Picture = Picture[If(tabPrint.Index = 0, "icon:/small/printer", "icon:/small/pdf")] + btnPrint.Text = ("Print") + + Endif + + btnPrint2.Picture = btnPrint.Picture + btnPrint2.Text = btnPrint.Text + +End + +Public Sub tabPrint_Click() + + If tabPrint.Index = 0 Then + $hPrinter.OutputFile = "" + $hPrinter.Name = cmbPrinter.Text + $hPrinter.Resolution = 1200 + Else + $hPrinter.OutputFile = txtFile.Text + $hPrinter.Resolution = $iFileRes + Object.Lock(cmbResolution) + pnlResolution.Enabled = True + Try cmbResolution.Index = Max(cmbResolution.Find(Str($iFileRes)), 0) + Object.Unlock(cmbResolution) + Endif + + RefreshPrintButton + +End + +Public Sub TextBox1_Change() + + View.Range = TextBox1.Text + +End + +Public Sub TextBox1_Click() + + Last.Text = "" + 'TextBox1_Change + +End + +Public Sub spWidth_Change() + + View.Report.Width = Str(Last.Value) & "mm" + View.Refresh + +End + +Public Sub spHeight_Change() + + View.Report.Height = Str(Last.Value) & "mm" + View.Refresh + +End + +Public Sub Button2_Click() + + View._ForceLayout + +End + +Public Sub cmbPrinter_Click() + + SelectPrinter(Last.Text) + +End + +Public Sub btnPrint_Click() + + Dim iReturn As Integer + + If btnPrint.Tag Then + + $hPrinter.Cancel + btnPrint.Tag = False + + Else + If Not View.Report Then Return + + If tabPrint.Index = 1 Then + + If Not $hPrinter.OutputFile Then txtFile_Click + If Not $hPrinter.OutputFile Then Return + + If Exist($hPrinter.OutputFile) And Not $bVerif Then + iReturn = Message.Warning("" & $hPrinter.OutputFile & "\n\n" & ("This file already exists.\nDo you want to replace it?"), ("Replace"), ("Cancel")) + If iReturn = 2 Then Return + Try Kill $hPrinter.OutputFile + $bVerif = False + Endif + + Endif + + $bPrinting = True + btnPrint.Tag = True + RefreshPrintButton + + $hPrinter.Print + + Endif + +End + +Public Sub Printer_Begin() + + hbZoom.Hide + hbPrinting.Show + Inc Application.Busy + ProgressBar1.Value = 0 + ProgressBar1.Pulse = True + lblPrint.Text = ("Layout...") + +End + +Public Sub Printer_Paginate() + + If View.Report._LayoutNotFinished Then + View.LockLayout + View.Report.Layout(View.Report.PageCount) + Else + $hPrinter.Count = View.PageCount + ProgressBar1.Pulse = False + lblPrint.Text = ("Printing...") + View.UnlockLayout + Endif + +End + +Public Sub Printer_Draw() + + If View.RangePages.Count = 0 Then + View.Report.Paint($hPrinter.Page) + Else + View.Report.Paint(View.RangePages[$hPrinter.Page - 1] + 1) + Endif + ProgressBar1.Value = $hPrinter.Page / View.PageCount + +End + +Public Sub Printer_End() + + 'View.UnlockLayout + hbPrinting.Hide + hbZoom.Show + Dec Application.Busy + btnPrint.Tag = False + $bPrinting = False + RefreshPrintButton + +End + +Public Sub TextBox2_Change() + + $hPrinter.NumCopies = Last.Value + +End + +Public Sub Form_Close() + + If Component.IsLoaded("gb.settings") Then + 'Settings.Write(Me, "FPreview") + Settings.Write(View, "View") + Settings["FPreview/OutputFile"] = txtFile.Text + Settings["FPreview/Printer"] = $hPrinter.Name + Settings["FPreview/PrintToFile"] = tabPrint.Index = 1 + Settings["FPreview/Duplex"] = cmbDuplex.Index + Settings["FPreview/GrayScale"] = tgbGrayScale.Value + Settings["FPreview/ReverseOrder"] = tgbReverseCopies.Value + Settings["FPreview/CollateCopies"] = tgbCollateCopies.Value + Settings["FPreview/FileResolution"] = $iFileRes + Settings.Save() + Endif + +End + +Public Sub Form_Open() + + Dim hButton As ToolButton + + $aModeButtons = [btnOnePage, btnTwoPages, btnFullWidth, btnRealSize] + + If Component.IsLoaded("gb.settings") Then + 'Settings.Read(Me, "FPreview") + Settings.Read(View, "View") + + If View.ScaleMode = View.Custom Then + sldZoom.Value = View.Zoom + Else + For Each hButton In $aModeButtons + hButton.Value = False + Next + $aModeButtons[View.ScaleMode - 1].Value = True + + Endif + + txtFile.Text = Settings["FPreview/OutputFile", User.Home &/ "report.pdf"] + $hPrinter.OutputFile = txtFile.Text + $hPrinter.Name = Settings["FPreview/Printer"] + If Settings["FPreview/PrintToFile"] = True Then + tabPrint.Index = 1 + Endif + + Try $hPrinter.Duplex = Settings["FPreview/Duplex"] + Try $hPrinter.GrayScale = Settings["FPreview/GrayScale"] + Try $hPrinter.ReverseOrder = Settings["FPreview/ReverseOrder"] + Try $hPrinter.CollateCopies = Settings["FPreview/CollateCopies"] + Try $iFileRes = Settings["FPreview/FileResolution", 150] + Try $hPrinter.Duplex = Settings["FPreview/Duplex", Printer.Simplex] = cmbDuplex.Index + Endif + + SelectPrinter($hPrinter.Name) + btnPrint.Tag = False + $bPrinting = False + + btnOnePage.Value = True + tabPrint_Click +End + +Public Sub btnZoomIn_Click() + + sldZoom.Value = 100 * 2 ^ (Int((Log2(sldZoom.Value / 100) + 0.5) * 2 + 0.5) / 2) + 0.5 + +End + +Public Sub btnZoomOut_Click() + + sldZoom.Value = 100 * 2 ^ (Int((Log2(sldZoom.Value / 100)) * 2 - 0.5) / 2) + 0.5 + +End + +Public Sub panSide_Arrange() + + With btnCloseSide + .Move(.Parent.W - .W, 0, Desktop.Scale * 4, Desktop.Scale * 4) + End With + +End + +Public Sub btnCloseSide_Click() + + panSide.Hide + btnShowSide.Show + btnPrint2.Show + +End + +Public Sub btnShowSide_Click() + + panSide.Show + btnShowSide.Hide + btnPrint2.Hide + +End + +Public Sub mnuPrint_Show() + + Dim hMenu As Menu + + mnuPrintPDF.Checked = tabPrint.Index = 1 + For Each hMenu In mnuPrint.Children + If Not hMenu.Tag Then Continue + If tabPrint.Index = 1 Then + hMenu.Checked = False + Else + hMenu.Checked = hMenu.Text = cmbPrinter.Text + Endif + Next + +End + +Public Sub mnuPrintPDF_Click() + + tabPrint.Index = 1 + txtFile_Click + +End + +Public Sub mnuPrintPrinter_Click() + + tabPrint.Index = 0 + cmbPrinter.Text = Last.Text + +End + +Public Sub spResolution_Change() + +End + +Public Sub SpinBar1_Change() + + Last.Text = "toto" & Last.Value + +End + +Public Sub cmbResolution_Click() + + If tabPrint.Index = 0 Then + $hPrinter.Resolution = 1200 + Else + $iFileRes = cmbResolution.Text + $hPrinter.Resolution = $iFileRes + Endif + +End diff --git a/comp/src/gb.report2/.src/Viewer/FPreview.form b/comp/src/gb.report2/.src/Viewer/FPreview.form new file mode 100644 index 00000000..d4a1520f --- /dev/null +++ b/comp/src/gb.report2/.src/Viewer/FPreview.form @@ -0,0 +1,362 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,107,78) + Text = ("Report preview") + Arrangement = Arrange.Horizontal + { mnuPrint Menu + Visible = False + { mnuPrintPDF Menu + Text = ("Print to file") & "..." + } + { Menu1 Menu + } + } + { Panel1 VBox + MoveScaled(1,1,63,72) + Expand = True + Invert = True + { View ReportView + MoveScaled(1,1,50,48) + Background = Color.LightForeground + Tag = "3" + Mouse = Mouse.SizeAll + Expand = True + } + { hbPrinting HBox + MoveScaled(1,57,55,4) + Visible = False + Spacing = True + Indent = True + { lblPrint Label + MoveScaled(11,0,14,4) + AutoResize = True + Text = ("Printing") & "..." + } + { Panel11 Panel + MoveScaled(26,0,25,4) + Expand = True + Arrangement = Arrange.Fill + Margin = True + Padding = 4 + { ProgressBar1 ProgressBar + MoveScaled(0,0,37,3) + Expand = True + Border = False + } + } + } + { hbZoom HBox + MoveScaled(1,62,62,4) + { Panel2 Panel + MoveScaled(0,0,17,4) + Expand = True + Arrangement = Arrange.Horizontal + { btnOnePage ToolButton Mode + Name = "btnOnePage" + MoveScaled(0,0,4,4) + Tag = "1" + ToolTip = ("One page") + Picture = Picture["16/one-page.png"] + Toggle = True + } + { btnTwoPages ToolButton Mode + Name = "btnTwoPages" + MoveScaled(4,0,4,4) + Tag = "2" + ToolTip = ("Two pages") + Picture = Picture["16/two-pages.png"] + Toggle = True + } + { btnFullWidth ToolButton Mode + Name = "btnFullWidth" + MoveScaled(8,0,4,4) + Tag = "3" + ToolTip = ("Full width") + Picture = Picture["16/full-width.png"] + Toggle = True + } + { btnRealSize ToolButton Mode + Name = "btnRealSize" + MoveScaled(12,0,4,4) + Tag = "4" + ToolTip = ("Real size") + Picture = Picture["16/real-size.png"] + Toggle = True + } + } + { btnZoomOut ToolButton + MoveScaled(18,0,4,4) + ToolTip = ("Zoom out") + Picture = Picture["icon:/small/zoom-out"] + } + { sldZoom Slider + MoveScaled(22,0,19,4) + MinValue = 13 + MaxValue = 400 + Value = 100 + } + { lblZoom Label + MoveScaled(41,0,8,4) + Text = ("100 %") + Alignment = Align.Center + } + { btnZoomIn ToolButton + MoveScaled(48,0,4,4) + ToolTip = ("Zoom in") + Picture = Picture["icon:/small/zoom-in"] + } + { btnPrint2 MenuButton btnPrint + Name = "btnPrint2" + MoveScaled(51,0,8,4) + Visible = False + AutoResize = True + Text = ("Print") + Border = False + Menu = "mnuPrint" + } + { btnShowSide ToolButton + MoveScaled(58,0,4,4) + Visible = False + ToolTip = ("Show options") + Picture = Picture["icon:/small/view-split-h"] + } + } + } + { panSide Panel + MoveScaled(65,1,41,76) + Arrangement = Arrange.Horizontal + Border = Border.Plain + { Panel4 VBox + MoveScaled(1,1,39,73) + Expand = True + { tabPrint TabPanel + MoveScaled(0,0,37,15) + Arrangement = Arrange.Vertical + Spacing = True + Margin = True + Border = False + Count = 2 + Index = 0 + Text = ("Printer") + { pnlResolution HBox + MoveScaled(1,1,37,4) + Spacing = True + { PictureBox1 PictureBox + MoveScaled(0,0,4,4) + Picture = Picture["icon:/32/printer"] + Stretch = True + } + { cmbPrinter ComboBox + MoveScaled(5,0,31,4) + Expand = True + ReadOnly = True + } + } + { Panel7 HBox + MoveScaled(1,6,35,4) + Spacing = True + { Label2 Label + MoveScaled(0,0,12,4) + Text = ("Two-sided") + } + { cmbDuplex ComboBox + MoveScaled(15,0,21,4) + Expand = True + ReadOnly = True + List = [("None"), ("Short side"), ("Long side")] + } + } + Index = 1 + Text = ("File") + { Panel12 HBox + MoveScaled(1,1,35,4) + Spacing = True + { PictureBox2 PictureBox + MoveScaled(0,0,4,4) + Picture = Picture["icon:/48/pdf"] + Stretch = True + } + { txtFile ButtonBox + MoveScaled(5,0,29,4) + Expand = True + Picture = Picture["icon:/16/open"] + ReadOnly = True + } + } + { HBox3 HBox + MoveScaled(1,6,35,4) + Spacing = True + { TextLabel1 TextLabel + MoveScaled(1,1,12,3) + Text = ("Resolution") + Alignment = Align.Normal + } + { cmbResolution ComboBox + MoveScaled(15,0,14,4) + #Translate = False + ReadOnly = True + List = ["75", "150", "300", "600", "1200"] + } + { Label11 Label + MoveScaled(30,0,4,4) + Text = ("dpi") + } + } + Index = 0 + } + { Separator2 Separator + MoveScaled(3,15,32,0) + Visible = False + } + { VBox1 VBox + MoveScaled(3,16,38,59) + Expand = True + Spacing = True + Margin = True + { Panel6 HBox + MoveScaled(2,0,34,4) + Spacing = True + { Label1 Label + MoveScaled(0,0,12,4) + Text = ("Range") + } + { TextBox1 ButtonBox + MoveScaled(15,0,18,4) + Expand = True + Picture = Picture["icon:/16/clear"] + } + } + { HBox1 HBox + MoveScaled(2,5,34,4) + Spacing = True + { Label4 Label + MoveScaled(0,0,12,4) + Text = ("Copies") + } + { TextBox2 SpinBox + MoveScaled(15,0,12,4) + Expand = True + MinValue = 1 + } + } + { Panel5 Panel + MoveScaled(2,10,33,5) + } + { Panel14 HBox + MoveScaled(1,17,36,4) + Spacing = True + { Label10 Label + MoveScaled(0,0,12,4) + Text = ("Orientation") + } + { cmbOrientation ComboBox + MoveScaled(14,0,21,4) + Expand = True + ReadOnly = True + List = [("Portrait"), ("Landscape")] + } + } + { Panel8 HBox + MoveScaled(1,21,36,4) + Spacing = True + { Label3 Label + MoveScaled(0,0,12,4) + Text = ("Paper") + } + { cmbPaper ComboBox + MoveScaled(13,0,21,4) + Expand = True + ReadOnly = True + List = [("Custom"), ("A3"), ("A4"), ("A5"), ("B5"), ("Letter"), ("Executive"), ("Legal")] + } + } + { pnlCustom VBox + MoveScaled(0,25,36,9) + Visible = False + Spacing = True + { Panel3 HBox + MoveScaled(1,0,32,4) + Spacing = True + { Label5 Label + MoveScaled(0,0,12,4) + Text = ("Width") + } + { spWidth SpinBox + MoveScaled(14,0,6,4) + Expand = True + MaxValue = 1000 + } + { Label7 Label + MoveScaled(27,0,4,4) + Text = ("mm") + } + } + { Panel17 HBox + MoveScaled(1,4.125,31,4) + Spacing = True + { Label6 Label + MoveScaled(0,0,12,4) + Text = ("Height") + } + { spHeight SpinBox + MoveScaled(13,0,6,4) + Expand = True + MaxValue = 1000 + } + { Label8 Label + MoveScaled(26,0,4,4) + Text = ("mm") + } + } + } + { Panel13 Panel + MoveScaled(2,34,33,4) + Expand = True + } + { tgbGrayScale CheckBox + MoveScaled(1,36,34,4) + Text = ("Grayscale") + } + { tgbFullPage CheckBox + MoveScaled(1,40,24,4) + Visible = False + Text = ("Full page") + } + { tgbCollateCopies CheckBox + MoveScaled(1,45,35,3) + Text = ("Collate copies") + } + { tgbReverseCopies CheckBox + MoveScaled(1,48,36,4) + Text = ("Reverse order") + } + { Panel15 Panel + MoveScaled(1,52,35,2) + } + { HBox2 HBox + MoveScaled(2,55,33,4) + { Panel10 Panel + MoveScaled(2,1,10,3) + Expand = True + } + { btnPrint Button + MoveScaled(7,0,14,4) + AutoResize = True + Text = ("Print") + Picture = Picture["icon:/small/print"] + } + { Panel9 Panel + MoveScaled(21,1,10,3) + Expand = True + } + } + } + } + { btnCloseSide ToolButton + MoveScaled(35,0,3,3) + Ignore = True + Picture = Picture["icon:/small/close"] + } + } +} diff --git a/comp/src/gb.report2/.src/Viewer/ReportView.class b/comp/src/gb.report2/.src/Viewer/ReportView.class new file mode 100644 index 00000000..72003d97 --- /dev/null +++ b/comp/src/gb.report2/.src/Viewer/ReportView.class @@ -0,0 +1,414 @@ +' Gambas class file + +Export +Inherits UserControl + +Public Const _IsControl As Boolean = True +Public Const _IsContainer As Boolean = False + +Public Enum Custom, Page, DualPage, FullWidth, RealSize + +Private $aRangePage As New Integer[] + +Property Report As Report +Property Read LayoutInProgress As Boolean +Property ScaleMode As Integer +Property ShowPageNumbers As Boolean +Property Range As String +Property _GrayScale As Boolean +Property Zoom As Float +Property Read Count, PageCount As Integer +Private $bGrayScale As Boolean +Private $hReport As Report +Private $hView As New DocumentView(Me) As "View" +Private hOBS As Observer +Private tmrLayout As New Timer As "TmrLayout" +Private $sRange As String +Private $iScaleMode As Integer +Private $bShowPageNumbers As Boolean +Private $bLockLayout As Boolean +Private $bStopLayout As Boolean +Private tmrView As New Timer As "tmrView" +Property Read RangePages As Integer[] + +Event Zoom + +Public Sub _new() + + hOBS = New Observer($hView.Children[0], True) As "Area" + 'tmrLayout.Delay = 5 + Me.Proxy = $hView + +End + + +Private Function _GrayScale_Read() As Boolean + + Return $bGrayScale + +End + +Private Sub _GrayScale_Write(Value As Boolean) + + $bGrayScale = Value + +End + +Public Sub View_Draw((Page) As Integer, Width As Integer, Height As Integer) + Dim hImg As Image + Dim iPage As Integer + + If $aRangePage.Count > 0 Then + Try iPage = $aRangePage[Page] + If Error Then iPage = Page + Else + iPage = Page + Endif + + Paint.AntiAlias = True + If $bGrayScale Then + hImg = New Image(Width, Height, Color.White) + Paint.Begin(hImg) + Endif + + $hReport.Scale = $hView.Zoom + $hReport.Paint(ipage + 1) + If $bShowPageNumbers Then + Paint.Brush = Paint.Color(Color.SetAlpha(Color.Black, 125)) + Paint.Font = Font["Arial,Bold,+20"] + Paint.Text(iPage + 1, 0, 0, Width, Height, Align.Center) + Paint.Fill + Endif + + If $bGrayScale Then + Paint.End + Paint.DrawImage(hImg.Desaturate(), 0, 0) + Endif + $hReport.Scale = 1.0 + +End + +Public Sub View_Zoom() + + 'tmrLayout.Stop + Raise Zoom + +End + +Public Sub View_Finished() + + If $hReport._LayoutNotFinished Then + $bStopLayout = False + tmrLayout.Trigger + Endif + +End + +Public Sub Area_Scroll() + + tmrLayout.Stop + +End + + +Public Sub Area_MouseWheel() + + If Not $hView.Arrangement = Arrange.Row Then + $hView.Arrangement = Arrange.Row + $hView.Column = 0 + Endif + +End + +Public Sub Area_Draw() + + Dim sText As String + Dim iTextWidth As Integer + + sText = Str($hView.FirstVisibleDocument + 1) & "/" & Str($hView.Count) + iTextWidth = Paint.TextSize(sText).Width + 10 + Paint.Rectangle(Paint.Width - iTextWidth - 10, 10, iTextWidth, 25, 5) + Paint.Brush = Paint.Color(Color.SetAlpha(Color.black, 125)) + Paint.Fill(True) + Paint.Brush = Paint.Color(Color.black) + Paint.Stroke + + Paint.Brush = Paint.Color(Color.White) + Paint.Font.Bold = True + Paint.Text(sText, Paint.Width - iTextWidth - 10, 10, iTextWidth, 25, Align.Center) + Paint.fill + + +End + +Public Sub tmrLayout_Timer() + + If $bLockLayout Or If $bStopLayout Then Goto ESCAPE + If Not $hReport._LayoutNotFinished Then Goto ESCAPE + 'Need to add wait to allow refreshing + Wait 0.01 + tmrLayout.Trigger + $hReport.Layout($hReport.PageCount) + 'Add a try because closing the object cause an invalid object error + 'Certainly because the loop come after deleting + Try $hView.Count = $hReport.PageCount + Return + +ESCAPE: + tmrView.Trigger + + +End + +Private Function Report_Read() As Report + + Return $hReport + +End + +Private Sub Report_Write(Value As Report) + + + If Not Value Then Return + + $hReport = Value + $hReport.Layout(1) + $hView.Count = $hReport.PageCount + $hView.Padding = Report.UnitTo(5, "mm", "px") + $hView.Spacing = $hView.Padding + $hView.DocWidth = Report.UnitTo(TSizeParse[Value.Width].ToInch(), "in", "px") + $hView.DocHeight = Report.UnitTo(TSizeParse[Value.Height].ToInch(), "in", "px") + $hView.Reset + + tmrLayout.Trigger + + +End + +Public Sub MoveNext() + + $hView.MoveNext + $hView.Item.EnsureVisible + +End + +Public Sub MovePrevious() + + $hView.MovePrevious + $hView.Item.EnsureVisible + +End + +Public Sub MoveFirst() + + $hView.MoveFirst + $hView.Item.EnsureVisible + +End + +Public Sub MoveLast() + + $hView.MoveLast + $hView.Item.EnsureVisible + +End + +Public Sub MoveTo((Page) As Integer) + + $hView.MoveTo(Page) + $hView.Item.EnsureVisible + +End + + +Private Function LayoutInProgress_Read() As Boolean + + Return $hReport._LayoutNotFinished + +End + +Private Function ScaleMode_Read() As Integer + + Return $iScaleMode + +End + +Private Sub ScaleMode_Write(Value As Integer) + + $iScaleMode = Value + + $hView.AutoCenter = False + + Select Case Value + Case Me.Custom + $hView.Column = 0 + $hView.Arrangement = Arrange.Row + Case Me.Page + $hView.Column = 0 + $hView.Arrangement = Arrange.Fill + $hView.AutoCenter = True + Case Me.DualPage + $hView.Column = 2 + $hView.Arrangement = Arrange.Row + $hView.AutoCenter = True + Case Me.FullWidth + $hView.Column = 0 + $hView.Arrangement = Arrange.Vertical + Case Me.RealSize + $hView.Column = 0 + $hView.Arrangement = Arrange.Row + $hView.Zoom = 1 + End Select + + tmrView.Trigger + +End + +Private Function ShowPageNumbers_Read() As Boolean + + Return $bShowPageNumbers + +End + +Private Sub ShowPageNumbers_Write(Value As Boolean) + + $bShowPageNumbers = Value + tmrView.Trigger + +End + +Private Function Range_Read() As String + + Return $sRange + +End + +Private Sub Range_Write(Value As String) + + + + Dim ars As String[] + Dim ars2 As String[] + Dim s As String + Dim iStart, iEnd, i As Integer + + $sRange = Value + $aRangePage.Clear + + If Not $sRange Then Goto Fin + ars = Split($sRange, ";") + + For Each s In ars + If InStr(s, "-") Then + ars2 = Scan(s, "*-*") + iStart = CInt(Val(ars2[0])) - 1 + iEnd = Val(ars2[1]) - 1 + If iStart < 0 Or If iEnd < 0 Or If iEnd < iStart Then Goto Fin + If $aRangePage.Count > 0 And If iStart < $aRangePage[$aRangePage.Max] Then Goto Fin + For i = iStart To iEnd + + $aRangePage.Add(i) + + Next + Else + iStart = CInt(Val(s)) - 1 + If $aRangePage.Count > 0 And If iStart < $aRangePage[$aRangePage.Max] Then Goto Fin + $aRangePage.Add(iStart) + Endif + Next + +Finally +Fin: + If $aRangePage.Count > 0 Then + $hView.Count = $aRangePage.Count + Me.MoveFirst + Else + Me.MoveFirst + $hView.Count = $hReport.PageCount + Endif + tmrView.Trigger +Catch + +End + +Private Function Zoom_Read() As Float + + Return $hView.Zoom + +End + +Private Sub Zoom_Write(Value As Float) + + $hView.Zoom = Value + +End + +Private Function Settings_Read() As Variant[] + + Return [$hView.Zoom, CVariant($iScaleMode)] + +End + +Private Sub Settings_Write(Value As Variant[]) + + $hView.Zoom = Value[0] + $iScaleMode = Value[1] + +Catch + +End + +Public Sub _ForceLayout() + + Dim htmpImage As New Image(1, 1) + $bStopLayout = True + Paint.Begin(htmpImage) + $hReport.Layout() + Paint.End + $hView.Count = $hReport.PageCount + +End + +Public Sub LockLayout() + + $bLockLayout = True + +End + +Public Sub UnlockLayout() + + + $bLockLayout = False + $bStopLayout = False + tmrLayout.Trigger + +End + +Public Sub Refresh() + + Me.Report = Me.Report + tmrView.Trigger + +End + + + +Private Function Count_Read() As Integer + + If $aRangePage.Count > 0 Then Return $aRangePage.Count + Return $hReport.PageCount + +End + + +Private Function RangePages_Read() As Integer[] + + Return $aRangePage + +End + +Public Sub tmrView_Timer() + + $hView.Refresh + +End diff --git a/comp/src/gb.report2/16/full-width.png b/comp/src/gb.report2/16/full-width.png new file mode 100644 index 0000000000000000000000000000000000000000..f2ad55e4c5d97469dc38c5b21ff753ccbfb1045b GIT binary patch literal 134 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UAm`Z~Df*BafCZDwc^6Wib977~7 z&+YT%V^H8=c0aT~yZ)VH^XZF|0&X$Sytea-@Z6g${dL@%%dc#;IT#u_LBW4oCS&We fs#SCC57zMTsF?GvozTC*8)Si}tDnm{r-UW|hkPpo literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/16/one-page.png b/comp/src/gb.report2/16/one-page.png new file mode 100644 index 0000000000000000000000000000000000000000..c4355cffaba40f228e3652f2651d98a3be78bb57 GIT binary patch literal 126 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UAm`Z~Df*BafCZDwc^2|M5977~7 zpY7ks%V5B9*yrf|CG!ua$2M}m(w(NH+VbP0l+XkK6Q(9p literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/16/real-size.png b/comp/src/gb.report2/16/real-size.png new file mode 100644 index 0000000000000000000000000000000000000000..ddfd33bd6ad9ffe3bc5e0a69eac4467b25fbdb20 GIT binary patch literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UAm`Z~Df*BafCZDwc@+v)D977~7 z=bqij+iW1 K&t;ucLK6VBDIaYB literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/16/red-arrow-v.png b/comp/src/gb.report2/16/red-arrow-v.png new file mode 100644 index 0000000000000000000000000000000000000000..a787fe89c60ce899a23198ffa582b5bc1540384d GIT binary patch literal 120 zcmeAS@N?(olHy`uVBq!ia0vp^93afZ3?z3ZhDiV^&H$ef*Z)9JU;m$hA?NbP6+l78 zk|4ie28U-i(tsRUPZ!4!j_b(@2?-p$sVAaNDr{|O%w=L;%*+flz<{4Q_>%I`W}rd_ MPgg&ebxsLQ04Dk&EdT%j literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/16/two-pages.png b/comp/src/gb.report2/16/two-pages.png new file mode 100644 index 0000000000000000000000000000000000000000..252dbd11e46e32e4e07f7e49855f63b5ffb10e39 GIT binary patch literal 130 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UAm`Z~Df*BafCZDwc@~k~w977~7 zZ|yVWVo=~=UVo|n_MbZkMUDg-oSqmk%Z1UwbTX&HtY&Y1$6uIaQx_uYbs zFl1V%NP3T!Twqt^hg1L5T@7_ICrx-;9Oc@wv|K?C>pBwQW PXbyv?tDnm{r-UW|Mdc*X literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/22/RealSize.png b/comp/src/gb.report2/22/RealSize.png new file mode 100644 index 0000000000000000000000000000000000000000..f93d1289b4a9f944672e452e305930159a57637c GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H0wnYHF4+L2CVRR#hG?8mPEcSpFfuUsqwc6X zvFpFFfx*N_JhwE{8ZYJWY~l&xsh8+eKA9+-{NwzA0|LhjraTlrTq@EWaP*(DPgsh@ zkq=#t%&vxJh7}h|7tQR^YBPTEFPXo3lM}P3L6G7c&m!Y9l5#v%JZU_Kcv5-pOR7B< y1e;{g{?J}zUz>D`);D?6IUk!3PImDO;$eteYM}V|B)=EXc?_PeelF{r5}E*|>qzAQ literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/22/TwoPage.png b/comp/src/gb.report2/22/TwoPage.png new file mode 100644 index 0000000000000000000000000000000000000000..306e7cee92a4e4f56325c4578c0a91fc034eba0e GIT binary patch literal 121 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H0wnYHF4+L296VhdLp07OCnzu)82ve}b*dp$ zhWkgq%OVD~pgD%CDqJ+v9Fv57(seX!-2TimnEO$FRV&BQiYZCYMBG$zOIR2_SaK=7 TmCRcUG>gI0)z4*}Q$iB}hN~o7 literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/32/Collatecopie.png b/comp/src/gb.report2/32/Collatecopie.png new file mode 100644 index 0000000000000000000000000000000000000000..3c0a284961fe7362bd9bc98c0020bdb460f4f017 GIT binary patch literal 449 zcmV;y0Y3hTP)2wR%gW*5 z;m*v<k@N&FAOl=;!C_>gww0>FMd{?Ca~&(9iAc>+I|5>gnjt%*^fV?BCwq zy1BU0(b3M$&BMRG?(OW*&(Gf7-N(km?(Oa7=H z>F3-1oB03$010$bPE!E*#1F|8st?Je;|_TM006~FL_t(I%e|4=4uU`oMeBkJjE-0x z#SPr|1=RolHB)d52~GOOdF{=4=mFRDd|#<-`RMX?UVk;SkM#IZ%O z4#bH?u@0@na5~G-V|#e~wn0`V09%$`4Y0kn)3+AEf*s rgwz3>g?<5?Ck68?CkCA?CtIC?(OaF?(OgH?eFjI@bB*P^6{yq zq~YM<@9*!aq@v>D;_&b9>+9>|vw6n6|;NR5K)62@r%gf5v*xI9_pv}j|@bK`#z`(JvugS>A!NI`C#>d&% z*xcLOpr4-2$HmUc$ETv8>gnj*+uFdszw`3)-rU@to}BOP?aj-~#KXgzoSDwY#ipX5 z!ok7T*4EwJ+?<@6rlFwL)z!eizuVi|ot&G~)6>z^)}Nl8&Bn#2p`h~c@bvTY>FMjz z)z_n;pSrZNrl6m#r=`rs#BKCF>;M1&0d!JMQvg8b*k%9#0We8KK~y-)jnipUfC*1|J}gbU@9e(o`@c+T7l9ti-wdyw|L!n5^Ds;Ud`%vFB=ECb zfDeX3LbE8gNRlMWa@Z8{lOW2qg4&`ahy~&xB}Rkx4iK^ez7xdKKuvUkSd|8X2GVJe z6uLo?BneXK9#C(J2AMvPP6F(G1OA?4?;9{S=YRV@s1>ThgR(0A`3WQJ5g3@fWPcEp zr7vj=BtV0P6$qdqdc&~tdzjPm`H@lj)JocJYM002ovPDHLkV1nd{ BwRHdh literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/32/grayscale.png b/comp/src/gb.report2/32/grayscale.png new file mode 100644 index 0000000000000000000000000000000000000000..670e944d6aeb111916160f2bdf358937d265d71c GIT binary patch literal 1564 zcmV+%2IKjOP)gwz3>g?<5?Ck68?CkCA?CtIC?(OaF?(OgH?eFjI@bB*P^6{yq zq~YM<;^N}tlq(=H=(+<>%+-=;!C?=;-O_=;`U{>gnm~>gn(A@2RAs zgn+B@9XR9cq)=I7_<;NRc5xwzoq;Ogq?@$m22+1RqNvAnyx$j8U6 ztgOk&$+0p?K?W&CR>Ix#HpB!ok4M&(8Al@$T*HxVN{O zn3(0`K_n-QDQt=GfQQ?CR>&)6=G-q0P$2hKBL+ z@bmKW^z-xM;^NlU)%ErC>ged0mX?EpfrNvDtgEWy;o)P4ay1BW>#>MjS@R^yI zg@l8KgoNVY;G?3U^YQV#ySm-o+|SOFMjz)z_n;pSrZNrl6m#r=`rs#6oTBNB{r;0d!JMQvg8b*k%9#0@+DKK~y-)V_+Zx zFf!o)j8Fy4EUawo9Gsk7+&nzIe0+TT{QLqSAjk|=AjB#xA}S^>At@y-BO@y-Coiv{ z2m(q%PzB0tBBJ6dl9C{4Igq3(8yKi5!xX4%h-*q}X=%&K>FDa}>9eYU&gJ1@v4$z&0?E6% zJ2-fFdTDZb`{?@efq)-Ofj^h6bUaL6nuWU35%L ztfyC8d_rQ9rJ`DLN=mAML0S|{LAs1=MogwlR(4KqUR-j1N`XvDN}+*)A51}!m7SbK zv9o7hLJ2Sw)$;QB)TF}s3=9N|pbAP^*viTic`GV|tJ120Kp|0~rnXM8roI$r0#8F@ zQ*%jpORIpIni>mBTRWGkPe*4L+yrYiw(g$ZzVQCOuCA^L6DDv4bFmvwG^we9hl3jX zr1Hdw$y26+q^ql?rb*Q#Pgkv(0aGwjfZbAN)@%dzq&a?m)zva{=gqe$n>S;|jF~VK z7I365T(r1dYDrQ+9Q)E`#>=GwSF8jofSa(&a`l?EYuC+NzkI{SO`BJ3*}AQzW&8a3 zH8bG~xOjH#+|06Tx1dX$<+d7`ZGL<9*7EV~gPE{jjf*EjjV0qiO$J-dhx!kWdnDgK^JYN76M1T~40%`vI3CnFB zJ~Ei|_z5E8JT>6i0#r~13Z?n;t5{v0J%8~Mp`U@_6<7fa3q%1+-0L@Q-yzF`6$pS8 z2nx>MY7qAR!^cl3dGm8M%RG>7kpB7e{l0wthL(B1|3C#le-V@AVE_QtsRkb4V6q1Q O0000fzww@9*!fs;R=gys4z3>+9;`;^Oe{@9^*L^YZbkrljlZ>*M3&@$m2L>+9s? zvw6n6|;NR5K)62@r%gf5v*xI9_pv}j|@bK`#z`(JvugS>A!NI`C#>d&% z*xcLOpr4-2$HmUc$ETv8>gnj*+uFdszw`3)-rU@to}BOP?aj-~#KXgzoSDwY#ipX5 z!ok7T*4EwJ+?<@6rlFwL)z!eizuVi|ot&HL=;zbZ)6vw{pPrq~#>JFd(f*Q24Iy0o&Upr5U$rOd{}5gA^>00001bW%=J06^y0W&i*Il1W5CR5;6Z(^pf2 zKoo}I6E%W}N@Njpk4$I3fxT zOskh$!bC)+13Q^ag-Rw36A_gFI956x9;95^LBzm8Mis;1BAY=(j-!yvbHas_mYsx& zuPKQl*C1jxW3WMt7Y_2WBRc?I>pufe^hq{@LSF$(xrMs$8AvQ3Oz77%4PQ&E4bOcq z7I;3P16}8Q2YrAP0utb0P~r`ShK7rp?*$%yYRR%(yoFV5WOQtNVsdJFW_At{Dwy-O z?Yb$~t|l!kE-kODuC13g5b;siRP5T6t?iw%QrRsrWPpSQd)@m7O6BmViVPP4j+I<( z4cjNDXXhn`Py^tiez0|Ub$wG}2s1*1+q?URM}|;?{ufVW_1Q4!^@16%Z|_xv8uT1K yKI{AXW(YOlw=lwtzjF>k4X7<_Kz(83_v8o7|B&yS(h6z-0000>>>>>>>>>>>>>>>>>>>>> PUBLIC VARIABLES >>>>>>>>>>>>>>>>>>>>>>>>> +'>>>>>>>>>>>>>>>>>>>>>>>>>> PROPERTIES >>>>>>>>>>>>>>>>>>>>>>>>>>> +'>>>>>>>>>>>>>>>>>>>>>> PRIVATE VARIABLES >>>>>>>>>>>>>>>>>>>>>>>> +'##################### PUBLIC PROCEDURES ######################### +'##################### PRIVATE PROCEDURES ######################## +'######################## PROPERTIES ############################# + diff --git a/comp/src/gb.report2/gambas.svg b/comp/src/gb.report2/gambas.svg new file mode 100644 index 00000000..5bfafe4d --- /dev/null +++ b/comp/src/gb.report2/gambas.svg @@ -0,0 +1,540 @@ + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Gambas 3 + 15/09/2008 + + + Fabien Bodard + + + + + Common Creative + + + + + Fabien Bodard + + + gbFBodard150908 + http://gambas.sf.net + + + gambas3 logo basic shrimp + + + A pretty shrimp, a malicious new gambas logo... we got the power ! + + + Fabien Bodard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/comp/src/gb.report2/icon.png b/comp/src/gb.report2/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..622c737863940f037e52c57ec5d213056a5dcf26 GIT binary patch literal 2357 zcmV-53Ci|~P)yzkY*rqhtx$LfYcg~H6-7}floxNYYhhYn$AXb4KF z_KY9|T5CutA%wuz))tgf$Ye5DU0u~$>lxQ|zkc-S(M-1i&`vwhS_1&2l<+*S?Z!Dr zu~R<{6jk_JHlj8PB{EG{lWX$=BE2mz%O;_*0y z5Hy)g#z&x&wIWyvqm^c64?>7P9ZD$?i^V`G17i%7G30VNWU^UU zmPK9H9g5$(I3u3r}$_4g%QQQvh7Z^v)p{iG&+6+#GizDJ3k+@*TGwZ_nnodAG*ev5A8H=b*C%r*cBa3Np|5pudy=Z%CAV2lAM)dY(%Xi7t4 zFvcLuP(05A0C=2(mu>1T050Hp92WC@qqB0#|7JOrTI5fG(d-@&N!LG-f*aF4?(hh=4G_CvujO$6|M)__t35Z{Pir9eHI zCHS=%oFCIX~tZ|t@sK+CW({Z=Ia z1%h^V_}T^_wF(*1jrBmYikbyL8DQD{0U)J>58SAzvJ4%HApEjF)@Lmp&?k7 zRkbp#+%2kfpuU&2PZ2^;EEci3xryu7uY*#GFC0077xuk?ojZ3%0n?8jJ@PV{j1Az8 zGiT2Hv{xuyo;{I<1EtV==Lhd#Vc{C0)i?@tdzVH2*c?5Veh%Mz z`}?r0+KI5ZxQJV~ZWXlFd(NFZcOM9(fMr>qS4zcq@7@j0IYgcNYTL4~YjhX(f9e2~ zQl+A(lz(hm@O!OuwPuf2(&4oqpR1MQVN`NodAL<*hrNr7Xm;C3FT_d z0hNNs1#-FEU;t3E)MVoYky0uY8CK=VW&kKD;T4O>WHN&RKuV}4YJllC7%7nvU?9{S zP$_tv4-5dIR8vI|Xd(iFQ&r7QqyqOiMA7Ss^Q{6QVT&3V>)0C7UaPmHoDv0;OjIazFrB<@X7iO@X=*@c4;r zofMD?Qplbaz*Ygkvbt9Ujq{D}1kK&xAOIkCEK~$N&%@Hv67u;xMn*<3K0Y3@J;cr& zn2VbMAeDqB1a|oeannw<@T*7wx<+4wh9Y2da})U7pRq7Di$m{y1EaZ7IF923XqW=# z*0Pq;&!+MCbpJO*qqH~utZ#EH7Q@G^003~DxL@3(z*a4W%u={s1iW(MI9@q%e7k+F zlCjxw9DMr2*KlC@G;G@jjZ^Dli5>-6CkVnWL7HQCnQw^<1D` z)h&WbDg0`Fu5Jh2ej}tQwLMhzg^aSA5wIquRDby6dmX+y*V<-hzhmd+FCA3?*39={ zoU`%q@g3=j=kG%)kQOY9N|$b`8~rP-Ap!jU>Qx7T03dM-o(1#Bn zN+INy;KDUeevBN)!GQw@000*+Uc}PUQda?Vs#Ge4{rmR=01}A=ip65sf5t$x7RJ|d zQwD(Fik$z3Qab4gVe9gnSO8Ec6p+v70RXP+VsvzLyL}GHbzR@Sd_Iptp#YSliVzCh zu>`pMU8TPkN_EI@_y7ITTTku!#Ek8@q4)9?s6W8e)D)(srncMXDjEB@RG{&MM<|V% zb6m>5Y;6ALo0a|=0MZaySy@@f%F6Ml)9L5qj+53}GuKVN$p!ynKA-Pxwd;~fnm?tFm9#m_Wt3W5T_%P0kzBg1SMa?)91)^2w+l3$le{buqx|dbGmg9}8sBtI? bMA!9Sq6r@{L;RH=00000NkvXXu0mjf^6p<( literal 0 HcmV?d00001 diff --git a/comp/src/gb.report2/tmpJournal b/comp/src/gb.report2/tmpJournal new file mode 100644 index 00000000..eb76ce09 --- /dev/null +++ b/comp/src/gb.report2/tmpJournal @@ -0,0 +1,12 @@ +[GB.REPORT2] +* BUG: The ReportControl font property now make a copy of the font given in argument. +* BUG: Now a too big children element is clipped if it is the first one listed + after the fixed ones (no more infinite loop) +* NEW: ReportUnit module is merged with Report class. All functions for convertion + are now available as static functions in Report. The two internal functions scale and + internal width are now public function of report instance. +* NEW: Return of the ReportImage and ReportSvgImage with no change for now. +* NEW: Continue to improve the ReportGridView widget ... really not ready for use. +* NEW: Restore the UseField property in ReportLabel so for now the component is + backward compatible. + \ No newline at end of file diff --git a/comp/src/gb.report2/tortueface.gif b/comp/src/gb.report2/tortueface.gif new file mode 100644 index 0000000000000000000000000000000000000000..249942783056e283b62934c309699e8229228327 GIT binary patch literal 1019 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", "min : ", hSCan[s].MinValue, "Max :", hSCan[s].MaxValue, "Units : ", hSCan[s].Unit + Else + Print "IsList : ", s, " ", "==>", hSCan[s].List.Join() + Endif + Next + +End diff --git a/comp/src/gb.scanner/.src/Module1.module b/comp/src/gb.scanner/.src/Module1.module new file mode 100644 index 00000000..f70024d8 --- /dev/null +++ b/comp/src/gb.scanner/.src/Module1.module @@ -0,0 +1,28 @@ +' Gambas module file + +Private hscan As New Scanner("hpaio:/net/HP_LaserJet_M1536dnf_MFP?ip=192.168.1.25") As "Scan" +Public Sub Main() +Print Object.Parent(hscan) + hScan.ASync = True + +hScan["Mode"].Value = "Color" +'hScan["Resolution"].Value = 300 + +hscan.Scan() +'hImg.Save("~/fa.png") + +End + + + Public Sub Scan_Progress() + + Print hscan.Progress + +End + +Public Sub Scan_Finished() + + Print "End" + +End + diff --git a/comp/src/gb.scanner/.src/Module2.module b/comp/src/gb.scanner/.src/Module2.module new file mode 100644 index 00000000..1cd27e1b --- /dev/null +++ b/comp/src/gb.scanner/.src/Module2.module @@ -0,0 +1,10 @@ +' Gambas module file + +Public Sub Main() + + Dim s As String + For Each s In Scanners + Print Scanners[s].Name + Next + +End diff --git a/comp/src/gb.scanner/.src/Scanner.class b/comp/src/gb.scanner/.src/Scanner.class new file mode 100644 index 00000000..f04f4d84 --- /dev/null +++ b/comp/src/gb.scanner/.src/Scanner.class @@ -0,0 +1,393 @@ +' Gambas class file + +Export +Private $sVendor As String +Private $sModel As String +Private $sType As String +Private $bDebug As Boolean +Property Debug As Boolean +Property ASync As Boolean +Property Read Name As String +Property Read Vendor As String +Property Read Type As String +Property Read Model As String +Private $sDeviceName As String +Private $bASync As Boolean +'Property _Name As String +Private $sName As String +'Private $Options As _Options + +Property Read Progress As Float +Private bHaveInfo As Boolean + +Private $aOptionsNames As String[] +Private $aOptions As _Option[] +Private $sImageStack As New String[] +Private $sCurImagePath As String + +Public Struct ScannerInfo + Vendor As String + Model As String + Type As String +End Struct + +Private $fProgress As Float +Event Finished +Event Begin +Event Progress + +Public Sub _New(sDevice As String) + + $sDeviceName = "--device-name='" & sDevice & "'" + $sName = sDevice + 'If Not IsAvailable() Then + ' Error.Raise("Unknown Device") + 'Endif + +End + +Private Sub GetInfo() + + Dim hInfo As ScannerInfo + + hInfo = Scanners._GetInfo($sName) + $sVendor = hInfo.Vendor + $sModel = hInfo.Model + $sType = hInfo.Type + bHaveInfo = True + +End + +Private Function Name_Read() As String + + Return $sName + +End + +Private Function Vendor_Read() As String + + If Not bHaveInfo Then GetInfo + Return $sVendor + +End + +Private Function Type_Read() As String + + If Not bHaveInfo Then GetInfo + Return $sType + +End + +Private Function Model_Read() As String + + If Not bHaveInfo Then GetInfo + Return $sModel + +End + +Private Sub GetOptions() + + Dim sRet As String + Dim s, sCurGroup As String + Dim aLine As String[] + Dim hOption As _Option + Dim a As String[] + Dim iTiret As Integer + Dim sName As String + Dim sValues As String + Dim sDefault As String + Dim iStartOptions As Integer + Dim iEndOptions As Integer + + If Not IsAvailable() Then Error.Raise("Unknown device or device not available") + + $aOptionsNames = New String[] + $aOptions = New _Option[] + Shell "scanimage " & $sDeviceName & " -A" To sRet + 'Return + For Each s In Split(sRet, "\n") + s = Trim(s) + If InStr(s, ":") Then + sCurGroup = Trim(Left(s, -1)) + Continue + Endif + + If s Begins "-" Then + 'Correction of some specific mode name + s = Replace(s, "[=(", " ") + s = Replace(s, ")]", "") + iStartOptions = InStr(s, " ") + iEndOptions = RInStr(s, " ") + If iEndOptions = -1 Then iEndOptions = Len(s) + + sName = Left(s, iStartOptions - 1) + sValues = Mid(s, iStartOptions + 1, iEndOptions - iStartOptions - 1) + sDefault = Mid(s, iEndOptions + 1) + 'aLine = Split(Mid(s, iStartOptions), " ", "()") + hOption = New _Option + 'Store the option group + hOption._Group = sCurGroup + 'Manage the option cutting + iTiret = IIf(sName Begins "--", -2, -1) + + 'SOme option are standardised so we can manage them with our + 'own way. + Select Case Right(sName, iTiret) + Case "l" + hOption._Name = "Left" + Case "t" + hOption._Name = "Top" + Case "x" + hOption._Name = "Width" + Case "y" + hOption._Name = "Height" + Case Else + hOption._Name = Right(sName, iTiret) + hOption._Name = UCase(Left(hOption._Name)) & Right(hOption._Name, - 1) + End Select + 'Remember the real option command line + hOption._Command = sName + 'sOptions = Mid(s, iStartOptions, iEndOptions - iStartOptions) + 'Define the option style (Range/List) + hOption._IsRange = InStr(sValues, "..") + 'Manage the range style option + If hOption._IsRange Then + aLine = Split(sValues, " ", "()") + sValues = aLine[0] + a = Scan(sValues, "*..*") + hOption._MinValue = a[0] + s = GetUnit(a[1]) + If s Then + hOption._MaxValue = Left(a[1], - Len(s)) + hOption._Unit = s + Else + hOption._MaxValue = a[1] + Endif + If aLine.Count = 2 Then + hOption._Steps = CInt(Mid(aLine[1], RInStr(aLine[1], " "))) + Endif + + Else + 'If hOption.Name = "Mode" Then Stop + 'Manage the list style option + hOption._List = Split(sValues, "|") + 'If the option list have a unit then use it and remove it from the list + s = GetUnit(hOption._List[hOption._List.Max]) + If s Then + hOption._List[hOption._List.Max] = Left(hOption._List[hOption._List.Max], - Len(s)) + hOption._Unit = s + Endif + Endif + + 'Manage the default values and the not activate fag + If sDefault Begins "[" Then + If InStr(sDefault, "inactive") Then + hOption._IsActive = False + Else + If hOption._IsRange Then + hOption._Value = CFloat(Mid(sDefault, 2, -1)) + Else + hOption._Value = Mid(sDefault, 2, -1) + Endif + Endif + Endif + $aOptionsNames.Add(hOption.Name) + $aOptions.Add(hOption) + '$Options._Add(hOption) + Continue + Endif + + Next + +End + +Private Function GetUnit(sValue As String) As String + + Dim i As Integer + Dim s, sRet As String + + If Not sValue Then Return + If IsLetter(Left(sValue)) Then Return + For i = 1 To Len(sValue) + s = Mid(sValue, i, 1) + If Asc(s) >= 97 And If Asc(s) <= 122 Then + sRet &= s + Endif + Next + Return sRet + +End + + +''Return if the current scanner is available +Public Function IsAvailable() As Boolean + + Dim s As String + + Shell "scanimage " & $sDeviceName & " -n 2>&1" To s + + Return InStr(s, "Error during device I/O") = 0 + +End + +Public Function Scan() As Image + + 'Make the option Line + + Dim ss As String + Dim sOptions As String + Dim sCommand As String + 'Dim sTemp As String = Temp + Dim hImage As Image + Dim hOption As _Option + If Not $aOptionsNames Then GetOptions + $sImageStack.Clear + $sCurImagePath = Temp + For Each hOption In $aOptions + + If hOption.Modified Then + + If hOption._Command Begins "--" Then + sOptions &= hOption._Command & "=" & IIf(hOption.IsRange, hOption.Value, "'" & hOption.Value & "'") & " " + Else + sOptions &= hOption._Command & " " & IIf(hOption.IsRange, hOption.Value, "'" & hOption.Value & "'") & " " + Endif + + Endif + + Next + sCommand = "scanimage " & $sDeviceName & " " & sOptions & "--format=jpeg > " & $sCurImagePath + If $bDebug Then Debug sCommand + If Not $bASync Then + Shell sCommand Wait + 'Print ss + Try hImage = Image.Load($sCurImagePath) + If Error Then + Error.Raise("Can't get the image, something goes wrong" & Error.Text) + Return + Endif + Return hImage + Else + Shell sCommand For Read As "Process" + + $fProgress = 0 + Raise Begin + Endif + 'Shell "scanimage" + +End + +Public Sub Process_Error(sError As String) + + Dim fRet As Float + If sError Begins "Progress:" Then + fRet = Round(CFloat(Left(RTrim(Scan(Split(sError, "\r")[0], "* *")[1]), -1)) / 100, -2) + If fRet > $fProgress Then + $fProgress = fRet + Raise Progress + Endif + Endif + +End + +Public Sub Process_Kill() + + $sImageStack.Push($sCurImagePath) + $fProgress = 1 + Raise Finished + +End + +Private Function Progress_Read() As Float + + Return $fProgress + +End + +''Return recurssively all the options available +Public Function _next() As String + + Dim s As String + + If Not $aOptionsNames Then GetOptions + If IsNull(Enum.Index) Then + Enum.Index = 0 + Else + Inc Enum.Index + Endif + If Enum.Index >= $aOptions.Count Then + Enum.Stop + Return + Endif + + s = $aOptionsNames[Enum.Index] + Return s + +End + +''Find an option from it's name (same as hScan[key]) +Public Sub Find(Key As String) As _Option + + If Not $aOptionsNames Then GetOptions + Try Return $aOptions[$aOptionsNames.Find(Key)] + +End + +''Return if the given option exist +Public Sub Exist(Key As String) As Boolean + + If Not $aOptionsNames Then GetOptions + Return $aOptionsNames.Exist(Key) + +End + +''Return an Option from it's name +Public Function _get(Name As String) As _Option + + If Not $aOptionsNames Then GetOptions + If Not $aOptionsNames.Exist(Name) Then + Error.Raise("Unknown option '" & Name & "' for device : " & $sName) + Return + Endif + Return $aOptions[$aOptionsNames.Find(Name)] + +End + + +''Pop and return the last images. +''Return null if not image available +Public Function Peek() As Image + + Dim hImg As Image + + If $sImageStack.Count > 0 Then + Try hImg = Image.Load($sImageStack.Pop()) + If Error Then Return Null + Return hImg + Endif + +End + +Private Function Debug_Read() As Boolean + + Return $bDebug + +End + +Private Sub Debug_Write(Value As Boolean) + + $bDebug = Value + +End + +Private Function ASync_Read() As Boolean + + Return $bASync + +End + +Private Sub ASync_Write(Value As Boolean) + + $bASync = Value + +End diff --git a/comp/src/gb.scanner/.src/Scanners.class b/comp/src/gb.scanner/.src/Scanners.class new file mode 100644 index 00000000..c7bf1d5e --- /dev/null +++ b/comp/src/gb.scanner/.src/Scanners.class @@ -0,0 +1,167 @@ +' Gambas class file + +Export +Create Static + +Public Struct ScannerInfo + Vendor As String + Model As String + Type As String +End Struct + +Static Private colCache As New Collection + +Static Private $colScannerList As New Collection +Static Private $aScannerNames As String[] +' Static _Vendors As New String[] +' Static _Models As New String[] +' Static _Types As New String[] + +Static hProcess As Process +Static Private $ret As String +Static Private $IsInit As Boolean +Event Found +Static Public Sub _init() + + If Not System.Exist("scanimage") Then + Error.Raise("Scanner CLI tool is not detected.\nPlease Install the Sane package") + Endif + ' GetList(True) + '$IsInit = True +End + + + +Public Sub Search(Optional bWait As Boolean) + + Dim sRet As String + + If hProcess Then Return + $colScannerList.Clear + $aScannerNames = Null + If Not bWait Then + $ret = "" + hProcess = Exec ["scanimage", "--formatted-device-list", "%d|%v|%m|%t%n"] For Read As "Process" + Else + Exec ["scanimage", "--formatted-device-list", "%d|%v|%m|%t%n"] To sRet + FillList(sRet) + Endif + +End + +Public Function _GetInfo(sDeviceName As String) As ScannerInfo + + If Not $aScannerNames Then Search(True) + + If $colScannerList.Exist(sDeviceName) Then + Return $colScannerList[sDeviceName] + Else + 'If Not hProcess Then + 'FillList(GetList(False)) + Search(True) + Return $colScannerList[sDeviceName] + 'Endif + Endif + +End + +Private Sub FillList(sRet As String) + + Dim s As String + Dim ars As New String[] + Dim hScanner As ScannerInfo + + + + $colScannerList.Clear + $aScannerNames = New String[] + For Each s In Split(sRet, "\n") + If Not s Then Break + ars = Split(s, "|") + hScanner = New ScannerInfo + + 'hScanner._Name = ars[0] + hScanner.Vendor = ars[1] + hScanner.Model = ars[2] + hScanner.Type = ars[3] + $colScannerList[ars[0]] = hScanner + $aScannerNames.Add(ars[0]) + Next + +End + + +''List all the existing devices +Public Function _next() As String + + Dim s As String + + + If Not $aScannerNames Then Search(True) + If IsNull(Enum.Index) Then + Enum.Index = 0 + Else + Inc Enum.Index + Endif + If $aScannerNames.Count = 0 Or If Enum.Index >= $aScannerNames.Count Then + Enum.Stop + Return + Endif + + s = $aScannerNames[Enum.Index] + Return s + +End + + +''Return the given device if exist or fail. +Public Function _get(sDeviceName As String) As Scanner + + Dim hScan As Scanner + + If Not $aScannerNames Then Search(True) + + If Not colCache.Exist(sDeviceName) Then + + hScan = New Scanner(sDeviceName) + colCache[sDeviceName] = hScan + Else + hScan = colCache[sDeviceName] + Endif + + Return hScan + +End + +Public Sub Process_Read() + + $ret &= Read #hProcess, Lof(hProcess) + +End + +Public Sub Process_Error(sError As String) + + Print sError + +End + + +Public Sub Process_kill() + + + If hProcess.State <> hProcess.Crashed Then + FillList($ret) + Raise Found + Endif + +End + + +Static Public Sub Close() + + Try hProcess.Kill + +End + + + diff --git a/comp/src/gb.scanner/.src/_Option.class b/comp/src/gb.scanner/.src/_Option.class new file mode 100644 index 00000000..ddb3ce93 --- /dev/null +++ b/comp/src/gb.scanner/.src/_Option.class @@ -0,0 +1,124 @@ +' Gambas class file + +Property Read Group As String ''Return the group option name +Property Read IsRange As Boolean ''Indicate if the option give a range between too value +Property Read MaxValue As Float ''Return the range max value +Property Read MinValue As Float ''Return the range min value +Property Value As Variant ''Return the current value of the option +Property Read List As String[] ''If the value is a string return the list of the available values +Property Read IsActive As Boolean ''Return if this option is activate +Property Read Info As String ''Return some info about the option +Property Read Name As String ''Return the option name +Property Read Unit As String ''Return the Unit if option is numbers +Property Read Steps As Integer ''Return Steps +Property Read Modified As Boolean ''Return if the current value have been modified + +Private $bModified As Boolean + +Public _Group As String +Public _IsRange As Boolean +Public _MaxValue As Float +Public _MinValue As Float +Public _Value As Variant +Public _List As String[] +Public _IsActive As Boolean = True +Public _Info As String +Public _Name As String +Public _Command As String +Public _Unit As String +Public _Steps As Integer + + +Private Function Group_Read() As String + + Return _Group + +End + +Private Function IsRange_Read() As Boolean + + Return _IsRange + +End + +Private Function MaxValue_Read() As Float + + Return _MaxValue + +End + +Private Function MinValue_Read() As Float + + Return _MinValue + +End + +Private Function Value_Read() As Variant + + Return _Value + +End + +Private Sub Value_Write(Value As Variant) + Dim iFind As Integer + _Value = Value + If Me.IsRange Then + If TypeOf(Value) <> gb.Integer And If TypeOf(Value) <> gb.Float Then Error.Raise("This option require a number value") + _Value = Max(Min(Me.MaxValue, _Value), Me.MinValue) + If _Value <> Value Then Error "Warning: Option: '" & Me.Name & "' -> Value out of range... change to : " & _Value + Else + If Not _List.Exist(Value) Then + + Error "Warning: Option '" & Me.Name & "' -> Value not in the list..." + iFind = _List.Find(Value & "*", gb.Like) + If iFind > 0 Then + _Value = _List[iFind] + Error "Warning: Option '" & Me.Name & "' -> Near value found: Set to '" & _Value & "'" + Endif + Endif + Endif + $bModified = True + +End + +Private Function List_Read() As String[] + + Return _List + +End + +Private Function IsActive_Read() As Boolean + + Return _IsActive + +End + +Private Function Info_Read() As String + + Return _Info + +End + +Private Function Name_Read() As String + + Return _Name + +End + +Private Function Unit_Read() As String + + Return _Unit + +End + +Private Function Steps_Read() As Integer + + Return _Steps + +End + +Private Function Modified_Read() As Boolean + + Return $bModified + +End diff --git a/comp/src/gb.scanner/scanner.png b/comp/src/gb.scanner/scanner.png new file mode 100644 index 0000000000000000000000000000000000000000..d5cb25979fead28c9c2f31ebba3bd9deedb6f175 GIT binary patch literal 22747 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 literal 0 HcmV?d00001 diff --git a/comp/src/gb.settings/.component b/comp/src/gb.settings/.component new file mode 100644 index 00000000..344d07b7 --- /dev/null +++ b/comp/src/gb.settings/.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.settings +Version=3.11.90 +Authors=Benoît Minisini diff --git a/comp/src/gb.settings/.directory b/comp/src/gb.settings/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/comp/src/gb.settings/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/comp/src/gb.settings/.icon.png b/comp/src/gb.settings/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a9254f949f86671b58a744a6a93623576acc5217 GIT binary patch literal 10569 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 "/" And If Left(Path) <> "~" Then + If Not Path Then + Path = Settings.DefaultDir &/ Application.Name & ".conf" + Else If Path Begins "./" Then + Path = "." & Path + Else If Path Begins "../" Then + Else + Path = Settings.DefaultDir &/ Path & ".conf" + Endif + If Left(Path) = "/" Then + For Each sElt In Split(Settings.DefaultDir, "/") + sPath &/= sElt + Try Mkdir "/" & sPath + Next + Endif + Endif + + $sTitle = Title + + $sPath = Path + + Load() + +End + +Public Sub _free() + + Try Me.Save + If Error Then Error "gb.settings: warning: unable to save settings: "; $sPath; ": "; Error.Where;; Error.Text + +End + +Public Sub Save() + + Dim aKey As New String[] + Dim cSave As Collection + Dim cSlot As Collection + Dim sKey As String + Dim hFile As File + Dim vVal As Variant + Dim sTemp As String + Dim hLock As File + Dim cModify As Collection + Dim sSlot As String + + If Not $bModify Then Return + + 'Debug $sPath + + $bModify = False + + hLock = Lock $sPath & ".lock" Wait 2 + + sTemp = $sPath & "#" + + If Not $bModifyAll Then + + cSave = $cSlot + cModify = $cModify + Load + For Each cModify + sKey = cModify.Key + sSlot = GetSlot(sKey) + sKey = File.Name(sKey) + If cSave.Exist(sSlot) Then + Me[sSlot &/ sKey] = cSave[sSlot][sKey] + Else + Me[sSlot &/ sKey] = Null + Endif + Next + + Endif + + For Each cSlot In $cSlot + aKey.Add($cSlot.Key) + Next + aKey.Sort + + hFile = Open sTemp For Create + + If $sTitle Then + Print #hFile, "# "; $sTitle + Endif + + For Each sKey In aKey + + cSlot = $cSlot[sKey] + If cSlot.Count Then + Print #hFile, "[" & sKey & "]" + For Each vVal In cSlot + Print #hFile, cSlot.Key; "="; ToString(vVal) + Next + Print #hFile + Endif + Next + + Close #hFile + Try Kill $sPath & "~" + Try Move $sPath To $sPath & "~" + Try Move sTemp To $sPath + + '$dDateLoad = Stat($sPath).LastModified + +Finally + + If Exist($sPath & ".lock") Then Try Kill $sPath & ".lock" + If Exist(sTemp) Then Try Kill sTemp + If Exist($sPath & "~") Then Try Kill $sPath & "~" + Try Unlock #hLock + +Catch + + Error.Raise("Unable to save settings: " & Error.Where & " " & Error.Text) + +End + +Private Function GetSlot(sKey As String) As String + + Dim sSlot As String + + sSlot = File.Dir(sKey) + If Not sSlot Then sSlot = "/" + If sSlot = "/" Then + sSlot = "General" + Endif + If Left(sSlot) = "/" Then sSlot = Mid$(sSlot, 2) + + Return sSlot + +End + +Public Function _get(Key As String, Optional {Default} As Variant) As Variant + + Dim hSlot As Collection + Dim sSlot As String + 'Dim dDate As Date + + sSlot = GetSlot(Key) + Key = File.Name(Key) + + hSlot = $cSlot[sSlot] + If hSlot And If hSlot.Exist(Key) Then + Return hSlot[Key] + Endif + + Return {Default} + +End + +Private Sub SameSetting(vOld As Variant, vNew As Variant) As Boolean + + Dim I As Integer + Dim vOldVal As Variant + Dim vNewVal As Variant + + If IsNull(vOld) And If IsNull(vNew) Then Return True + If TypeOf(vOld) <> gb.Object And If TypeOf(vNew) <> gb.Object Then Return vOld = vNew + + If Not (vOld Is Array) Then Return + If Not (vNew Is Array) Then Return + If vOld.Count <> vNew.Count Then Return + If vOld = vNew Then Return + + For I = 0 To vOld.Max + vOldVal = vOld[I] + vNewVal = vNew[I] + If TypeOf(vOldVal) = gb.Object Or If TypeOf(vNewVal) = gb.Object Then Return + If vOldVal <> vNewVal Then Return + Next + + Return True + +End + + +Public Sub _put(Value As Variant, Key As String) + + Dim hSlot As Collection + Dim sSlot As String + + sSlot = GetSlot(Key) + hSlot = $cSlot[sSlot] + If Not hSlot Then + hSlot = New Collection + $cSlot[sSlot] = hSlot + Endif + + Key = File.Name(Key) + + If Not SameSetting(hSlot[Key], Value) Then + hSlot[Key] = Value + $cModify[sSlot &/ Key] = True + $bModify = True + 'Save() + Endif + +End + + +Public Sub Clear(Optional ParentKey As String) + + Dim sSlot As String + Dim hSlot As Collection + Dim vVal As Variant + + If Not ParentKey Then + If $cSlot.Count Then + $cSlot.Clear + $bModify = True + $bModifyAll = True + Endif + Else + sSlot = GetSlot(ParentKey &/ "g") + hSlot = $cSlot[sSlot] + If hSlot Then + For Each vVal In hSlot + $cModify[sSlot &/ hSlot.Key] = True + Next + $cSlot.Remove(sSlot) + $bModify = True + Endif + Endif + + 'Save() + +End + +Private Sub ReadWindow(hWindow As Window, sKey As String) + + Dim aPos As Integer[] + Dim X, Y, W, H, S As Integer + ' Dim D As Integer + ' Dim hObserver As Observer + + aPos = Me[sKey &/ "Geometry"] + + If Not aPos Or If aPos.Count < 2 Then + Return + Else + X = aPos[0] + Y = aPos[1] + If aPos.Count > 2 Then + W = aPos[2] + H = aPos[3] + If aPos.Count > 4 Then + S = aPos[4] + If S >= Screens.Count Then S = 0 + Endif + Else + W = hWindow.W + H = hWindow.H + Endif + Endif + + X += Screens[S].AvailableX + Y += Screens[S].AvailableY + + ' If Not hWindow.Parent And If Component.IsLoaded("gb.desktop") Then + ' D = -1 + ' Try D = aPos[5] + ' If D >= 0 Then + ' hObserver = New Observer(hWindow, True) As "Window" + ' hObserver.Tag = D + ' $cObserver[hWindow.Id] = hObserver + ' Endif + ' Endif + + ' If Object.Type(hWindow) = "FHelpBrowser" Then + ' Print "ReadWindow: "; X;; Y; " / "; S;; Screens[S].AvailableX;; Screens[S].AvailableY + ' Endif + + If hWindow.Resizable Then + W = Max(32, W) + H = Max(32, H) + If hWindow.Modal Then + hWindow.Resize(W, H) + hWindow.Center + Else + hWindow.Move(X, Y, W, H) + Endif + Else + If Not hWindow.Modal Then hWindow.Move(X, Y) + Endif + +End + +Private Sub WriteWindow(hWindow As Window, sKey As String) + + Dim X, Y, W, H, S As Integer + 'Dim D As Integer + Dim aVal As Integer[] + + X = hWindow.X + Y = hWindow.Y + W = hWindow.Width + H = hWindow.Height + + If hWindow.TopLevel Then + + S = hWindow.Screen + + If S >= 0 Then + X -= Screens[S].AvailableX + Y -= Screens[S].AvailableY + Else + S = 0 + Endif + + Endif + + aVal = [X, Y, W, H, S] + + ' If Not hWindow.Parent And If Component.IsLoaded("gb.desktop") Then + ' hDesktopWin = New DesktopWindow(hWindow.Id) + ' D = -1 + ' Try D = hDesktopWin.Desktop + ' If Error Then Debug Error.Text + ' Debug hWindow.Name;; "Desktop:";; D + ' If D >= 0 Then aVal.Add(D) + ' Endif + + Me[sKey &/ "Geometry"] = aVal + +End + +Private Sub GetTopLevel(hCtrl As Object) As String + + Return Object.Type(hCtrl.Window) + +End + +Public Sub Read(hObject As Object, Optional sKey As String, Optional vDefault As Variant) + + If Object.Is(hObject, "Window") Then + If Not sKey Then sKey = Object.Type(hObject) + ReadWindow(hObject, sKey) + Else + If Not sKey Then + Try sKey = GetTopLevel(hObject) &/ hObject.Name + Else If Left(sKey) <> "/" Then + Try sKey = GetTopLevel(hObject) &/ sKey + Endif + Try hObject.Settings = Me[sKey, vDefault] + Endif + +End + +Public Sub Write(hObject As Object, Optional sKey As String) + + If Object.Is(hObject, "Window") Then + If Not sKey Then sKey = Object.Type(hObject) + WriteWindow(hObject, sKey) + Else + If Not sKey Then + Try sKey = GetTopLevel(hObject) &/ hObject.Name + Else If Left(sKey) <> "/" Then + Try sKey = GetTopLevel(hObject) &/ sKey + Endif + Try Me[sKey] = hObject.Settings + Endif + +End + +Private Function Path_Read() As String + + Return $sPath + +End + +' Static Public Function Array(...) As String[] +' +' Dim aVal As New String[] +' Dim iInd As Integer +' Dim sVal As String +' Dim vVal As Variant +' +' For iInd = 0 To Param.Max +' vVal = Param[iInd] +' If IsBoolean(vVal) Then +' sVal = IIf(vVal, "1", "0") +' Else +' sVal = CStr(vVal) +' Endif +' aVal.Add(sVal) +' Next +' +' Return aVal +' +' End + +' STATIC PUBLIC FUNCTION FromString(Value AS String) AS String[] +' +' DIM aRet AS NEW String[] +' DIM iInd AS Integer +' +' aRet = Split(Value, ",", "\"") +' FOR iInd = 0 TO aRet.Max +' aRet[iInd] = Replace(aRet[iInd], "\"\"", "\"") +' NEXT +' +' RETURN aRet +' +' END +' + +Public Sub Reload() + + Load + +End + +Private Function Keys_Read() As _Settings_Keys + + _Settings_Keys._Slot = $cSlot + _Settings_Keys._Parent = "" + Return _Settings_Keys + +End + +Static Private Function DefaultDir_Read() As String + + Dim sPath As String = Application.Env["XDG_CONFIG_HOME"] + If Not sPath Then sPath = System.User.Home &/ ".config" + Return sPath &/ "gambas" & System.Version + +End + +Static Private Sub WriteValue(vVal As Variant) + + Dim sStr As String + Dim iPos As Integer + Dim aArray As Array + Dim cCol As Collection + + Select Case TypeOf(vVal) + + Case gb.Null + $sStr &= "Null" + + Case gb.Boolean + If vVal Then + $sStr &= "True" + Else + $sStr &= "False" + Endif + + Case gb.Byte, gb.Short, gb.Integer, gb.Long + $sStr &= CStr(vVal) + + Case gb.Float + sStr = CStr(vVal) + If InStr(sStr, ".") = 0 And If InStr(sStr, "E", 1, gb.IgnoreCase) = 0 Then sStr &= ".0" + $sStr &= sStr + + Case gb.Date + $sStr &= Chr$(34) & CStr(vVal) & Chr$(34) + + Case gb.String + sStr = Quote(vVal) + Do + iPos = InStr(sStr, "\\", iPos + 1) + If iPos = 0 Then Break + If Mid$(sStr, iPos + 1, 1) = "x" Then + Mid$(sStr, iPos, 4) = "\\u00" & Mid$(sStr, iPos + 1, 2) + Else + Inc iPos + Endif + Loop + $sStr &= sStr + + Case Else + If vVal Is Array Then + aArray = vVal + $sStr &= "[" + For iPos = 0 To vVal.Max + If iPos Then $sStr &= "," + WriteValue(vVal[iPos]) + Next + $sStr &= "]" + Else If vVal Is Collection Then + cCol = vVal + $sStr &= "{" + For Each vVal In cCol + If iPos Then $sStr &= "," + WriteValue(cCol.Key) + $sStr &= ": " + WriteValue(vVal) + Inc iPos + Next + $sStr &= "}" + Endif + + End Select + +End + +Static Public Sub ToString(Value As Variant) As String + + $sStr = "" + WriteValue(Value) + Return $sStr + +End + +Static Private Sub GetChar() As String + + Dim sCar As String + + If $iPos > Len($sStr) Then Return + sCar = Mid$($sStr, $iPos, 1) + Inc $iPos + Return sCar + +End + + +Static Private Sub ReadChar() As String + + Dim sCar As String + + Do + sCar = GetChar() + If Not sCar Then Return + If sCar > " " Then Return sCar + Loop + +End + + +Static Private Sub ReadToken() As String + + Dim sToken As String + Dim sCar As String + + sCar = ReadChar() + If Not IsLetter(sCar) Then Return sCar + + sToken = sCar + Do + sCar = GetChar() + If Not sCar Then Break + If Not IsLetter(sCar) Then + Dec $iPos + Break + Endif + sToken &= sCar + Loop + + Return sToken + +End + +Static Private Sub ReadString() As String + + Dim sCar As String + Dim sString As String + Dim iPos As Integer + + Do + sCar = GetChar() + If Not sCar Then Error.Raise("Non terminated string") + If sCar = Chr$(34) Then Return sString + If sCar = "\\" Then + sCar = GetChar() + If Not sCar Then Error.Raise("Non terminated string") + iPos = InStr("bfrtn", sCar) + If iPos Then + sCar = Mid$("\b\f\r\t\n", iPos, 1) + ' Else If sCar = "u" Then + ' Try sCar = String.Chr$(Val("&H" & Mid$($sStr, $iPos, 4))) + ' If Not Error Then $iPos += 4 + Else + ' Keep character + Endif + Endif + sString &= sCar + Loop + +End + +Static Private Sub ReadObject() As Collection + + Dim sCar As String + Dim cObject As New Collection + Dim sKey As String + + Do + sCar = ReadChar() + If sCar = "}" Then Return cObject + If sCar <> Chr$(34) Then Error.Raise("String expected") + sKey = ReadString() + sCar = ReadChar() + If sCar <> ":" Then Error.Raise("Colon expected") + cObject[sKey] = ReadValue() + sCar = ReadChar() + If sCar = "}" Then Return cObject + If sCar <> "," Then Error.Raise("Comma expected") + Loop + +End + +Static Private Sub ReadArray() As Array + + Dim sCar As String + Dim aArray As New Variant[] + Dim iInd As Integer + Dim iType As Integer + Dim aTypedArray As Object + + Do + sCar = ReadChar() + If sCar = "]" Then Break + Dec $iPos + aArray.Add(ReadValue()) + sCar = ReadChar() + If sCar = "]" Then Break + If sCar <> "," Then Error.Raise("Comma expected") + Loop + + If aArray.Count = 0 Then Return Null + iType = TypeOf(aArray[0]) + For iInd = 1 To aArray.Max + If TypeOf(aArray[iInd]) <> iType Then Return aArray + Next + + Select Case iType + Case gb.Boolean + aTypedArray = New Boolean[aArray.Count] + Case gb.Integer + aTypedArray = New Integer[aArray.Count] + Case gb.Float + aTypedArray = New Float[aArray.Count] + Case gb.String + aTypedArray = New String[aArray.Count] + Case gb.Object + aTypedArray = New Object[aArray.Count] + Default + aTypedArray = New Variant[aArray.Count] + End Select + + For iInd = 0 To aArray.Max + aTypedArray[iInd] = aArray[iInd] + Next + Return aTypedArray + +End + +Static Private Sub ReadNumber(sNumber As String) As Variant + + Dim sCar As String + Dim vNumber As Variant + Dim bFloat As Boolean + Dim iPos As Integer + + Do + sCar = GetChar() + If Not sCar Then Break + iPos = InStr(".eE-+0123456789", sCar) + If iPos = 0 Then + Dec $iPos + Break + Endif + If iPos <= 3 Then bFloat = True + sNumber &= sCar + Loop + + If bFloat Then + Try vNumber = CFloat(sNumber) + Else + Try vNumber = CInt(sNumber) + Endif + If IsNull(vNumber) Then Error.Raise("Incorrect number") + Return vNumber + +End + +' Static Private Sub ReadType() As Integer +' +' Dim sCar As String +' Dim iPos As Integer +' +' iPos = $iPos +' sCar = ReadToken() +' +' If sCar = "{" Then +' Return gb.Variant +' Else If sCar = "[" Then +' Return gb.Object +' Else If sCar = Chr$(34) Then +' Return gb.String +' Else If sCar = "-" Or If IsDigit(sCar) Then +' Return TypeOf(ReadNumber(sCar)) +' Else If sCar = "null" Then +' Return gb.Null +' Else If sCar == "true" Or If sCar == "false" Then +' Return gb.Boolean +' Else +' Error.Raise("Incorrect token: " & Quote(sCar)) +' Endif +' +' $iPos = iPos +' +' End + +Static Private Sub ReadValue(Optional bNotStrict As Boolean) As Variant + + Dim sCar As String + + sCar = ReadToken() + + If sCar = "{" Then + Return ReadObject() + Else If sCar = "[" Then + Return ReadArray() + Else If sCar = Chr$(34) Then + Return ReadString() + Else If sCar = "-" Or If IsDigit(sCar) Then + Return ReadNumber(sCar) + Else If sCar == "null" Then + Return Null + Else If sCar == "true" Then + Return True + Else If sCar == "false" Then + Return False + Else If bNotStrict Then + Return $sStr + Else + Error.Raise("Incorrect token: " & Quote(sCar)) + Endif + +End + + +Static Public Sub FromString(Value As String) As Variant + + Dim vVal As Variant + $sStr = Value + $iPos = 1 + vVal = ReadValue(True) + $sStr = "" + Return vVal + +End diff --git a/comp/src/gb.settings/.src/_Settings_Keys.class b/comp/src/gb.settings/.src/_Settings_Keys.class new file mode 100644 index 00000000..ea4bf3d0 --- /dev/null +++ b/comp/src/gb.settings/.src/_Settings_Keys.class @@ -0,0 +1,55 @@ +' Gambas class file + +Export +Create Static + +Public _Slot As Collection +Public _Parent As String + +Private $aKeys As String[] + +Public Sub _get(Parent As String) As _Settings_Keys + + Dim hKeys As _Settings_Keys + + hKeys = New _Settings_Keys + hKeys._Slot = _Slot + hKeys._Parent = _Parent &/ Parent + Return hKeys + +End + +Public Sub _next() As String + + Dim vVal As Variant + Dim cCol As Collection + Dim sKey As String + + If IsNull(Enum.Index) Then + If _Parent Then + Try cCol = _Slot[_Parent] + Else + cCol = _Slot + Endif + If Not cCol Then + Enum.Stop + Return + Endif + $aKeys = New String[] + For Each vVal In cCol + If Not _Parent And If vVal.Count = 0 Then Continue + $aKeys.Add(cCol.Key) + Next + $aKeys.Sort + Enum.Index = 0 + Endif + + If Enum.Index >= $aKeys.Count Then + Enum.Stop + Else + sKey = $aKeys[Enum.Index] + Inc Enum.Index + Return sKey + Endif + +End diff --git a/comp/src/gb.term.form/.component b/comp/src/gb.term.form/.component new file mode 100644 index 00000000..f2be9127 --- /dev/null +++ b/comp/src/gb.term.form/.component @@ -0,0 +1,7 @@ +[Component] +Key=gb.term.form +Version=3.10.90 +State=2 +Authors=Fabien Bodard (gambas.fr@gmail.com) +Needs=ImageIO +Requires=gb.term diff --git a/comp/src/gb.term.form/.directory b/comp/src/gb.term.form/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/comp/src/gb.term.form/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/comp/src/gb.term.form/.hidden/CHANGELOG b/comp/src/gb.term.form/.hidden/CHANGELOG new file mode 100644 index 00000000..af8761e0 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/CHANGELOG @@ -0,0 +1,4 @@ +[GB.TERM.FORM] +* OPT: Make the object placement more logical. the 0,0 pos now + is the left corner after the border. + diff --git a/comp/src/gb.term.form/.hidden/control/termbutton.png b/comp/src/gb.term.form/.hidden/control/termbutton.png new file mode 120000 index 00000000..23d989b3 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termbutton.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/button.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termcheckbox.png b/comp/src/gb.term.form/.hidden/control/termcheckbox.png new file mode 120000 index 00000000..b8fe6fbd --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termcheckbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/checkbox.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termframe.png b/comp/src/gb.term.form/.hidden/control/termframe.png new file mode 120000 index 00000000..0f21ed63 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termframe.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/frame.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termhbox.png b/comp/src/gb.term.form/.hidden/control/termhbox.png new file mode 120000 index 00000000..891ef5b1 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termhbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/hbox.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termlabel.png b/comp/src/gb.term.form/.hidden/control/termlabel.png new file mode 120000 index 00000000..4f2f9803 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termlabel.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/label.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termlistbox.png b/comp/src/gb.term.form/.hidden/control/termlistbox.png new file mode 120000 index 00000000..3b461414 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termlistbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/listbox.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termpanel.png b/comp/src/gb.term.form/.hidden/control/termpanel.png new file mode 120000 index 00000000..9c8883d4 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termpanel.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/panel.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termpicturebox.png b/comp/src/gb.term.form/.hidden/control/termpicturebox.png new file mode 120000 index 00000000..10a12fe3 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termpicturebox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/picturebox.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termradiobutton.png b/comp/src/gb.term.form/.hidden/control/termradiobutton.png new file mode 120000 index 00000000..8a6735e3 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termradiobutton.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/radiobutton.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termscrollbar.png b/comp/src/gb.term.form/.hidden/control/termscrollbar.png new file mode 120000 index 00000000..d3308d96 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termscrollbar.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/scrollbar.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termtextbox.png b/comp/src/gb.term.form/.hidden/control/termtextbox.png new file mode 120000 index 00000000..276853b4 --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termtextbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/textbox.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.hidden/control/termvbox.png b/comp/src/gb.term.form/.hidden/control/termvbox.png new file mode 120000 index 00000000..50d7662d --- /dev/null +++ b/comp/src/gb.term.form/.hidden/control/termvbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/vbox.png \ No newline at end of file diff --git a/comp/src/gb.term.form/.icon.png b/comp/src/gb.term.form/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a9254f949f86671b58a744a6a93623576acc5217 GIT binary patch literal 10569 zcmb_?cQl*t`~Q;=d(|%0q_kGmUPW!Cwzpk-6ty=+lUS)$bSY|6vs6*L#HMOj?b@qW zlp+Mbr|-{qobUg?b8?QHJaOIEec#u3U9VSS^>x*$DcC3g0HA)Lq52O1K)|;UfQ$tE zW9eJ$1OR+=4^)+mf~R*f`;u->WX`t=9VN`VZAH&HRzJ|R#L!Dz?|6s~UlS%upw?nx z7p7vR6P7=v#kIBIoooU4_SiKtcymUWq^%7V+#Jch9 zsvRjg9*fT%8C?8H&eOtQA zwe`!zGliCue5Kdnl$3xXML7E%rfqSLjLad4!qnm7hHjE~!-ZeTr;#ad48G5**%`d1 zoZ3p84n3W=4^^W^eswN-jjzAbmRM(1>8(3T=s69HSR2|1%6d2c;|J>L$zE%#-&K^w zQiz`&B>8dHb4=v%kaDP;*9-!K-R{or(|+8>{qc&uiI7=^(}ASl8~F=M zd1M?L`|<=8tXyIDpGA4f>H6WpdcRlG_q_PM9IMU^B~T%H4Ciuc2hQsBJh)6xwHkJX zFA1bk^*=4+V9B3G^ee~yX^V?Q(PZ1W>IPaG>RFJET_^zEll6B3XQ8P_dv6#7h@zQZ zZwbV6jdy<-PE36=SFkp89+WTxzZ5L25SkqmpR67NZ?w$ zP)z$Vu7*SYpwUMqZEdI}a%hy$<<}LuBwe>AbiHCMTIyF`L%_NaQga_zViX*Q_PR_k zxl6n$A#0XqexAL0Ack3H&!1ado)B`WmlvsP&S{VIhY4MKLWCCI_&l5y%Q&Gc-w#LpfWD*H zNT*3{AF!9C`~--mOTczyWJlJAtSK4u&bwlyow}ffVHaX~=j&PP>dZCpbwTa3lWHff z?)2syJf%rnl{kt-kN%J*3ktC9{Clwf$f2bN+Xc{{Zsp`z89JR3%o7l(LHq-omtSmq zBybN#E|fEFR0|e9J$yWneV)Oxju&Zhl97L54(pwfk!Lp`R4_EiOishIes z{~617f|_L!<-{fw=pu?q6PSheCf-+5t1i8Ej1PCXf;4ck$V%s$9^xVLG_Pr$>@hze zER%H0Uq%H8kI=3lpYAsbC@6lbztA4AsZkVsaFFc?)0ac1uF(qslE(&h65?)OC)d?W zrOZ6)XNZE)G+EExSWu_|s<{Fj8R5ih)LVnEu51e!I*bOz4+b zOB+Fod@fCT{!YMk?Qq3$?fNU^y+Tm}#YEP7{M7e7`|U z_4c0r(~RU0N4?mw@LKvQrSR}43%%B2IqG+|&>^GMER2ptwzM!J{k^s>b>Tu35YLi=AXx@bPn@rI#k8k?Hcall|j7Hzf!`+L%__Wlkt6;d@8WGXWV&0V6d&M@I z0FWdca0s3>$qxA({_ay1PDY^ny&-~&#by-Sd9ukxBTOy4T5SA|2jnqf=Lzodxzh60 z7tdz|B>O3L3MQ)hLcb2K8pb`&K*+MbyVjq!uoS%q1q5CN?>R6)0rQLeSe$o8ctaxcc;27n{;OMPyOi%eUvR9GDsl8qH%~_A4n9&N}LlM)z=cqu2x-mjgv93|~w+ zi_)rI1P5Y;nz)68MMt`gL`Po|A&EtV+#W$3QyZZWnjsd$^n01>>VdmaH{m84Yf6%4 zl<6o}tn+a)d9uK`6#Hj$*&^4A*&Q>50X;e%A3I1TuYeVV;hOc$(M_2bZofY)bf+0U zox+zytKt`&Urp$&{i%%g2`ya{3d4vi_`EUGqC|9P+zCf48ES>}j9ieTT8i=26H^N; z4g)qB+!--H*F8ST@$oWHJ>jeL$HgnNPdO{5XV=?*D2FB6{qhYFPN=3k-Tys36~B2i z`-ASW>Zj-JpBv~Eln&Mv98xWzRLN^R6)-msh$Cy`wLqntzo&p4VX@?aS{>DW$_MhS zl;PU}lDE;yp$%)Gdy#sNFCuO!feU>7FeiY0z}g7ym(J3ab@QXfOl^MQwUqf%$umKC z5{EmnA0>1KtZJG8Pn?Zm%4C}~j0|3JebSF>tqWJY*QtAI0yq$ir)okxPv}s{&{$okznp?o5m1SCOy0oLw(Ar@|RN z3Q1~$R=6!6fL5B@u|_Zh&P3-Ej(*Z!nGDy9B~C7FbCT+xufUVJ;g(J6&0NY)>XZ?a zh78RjN_N|Q@J1&+n^(+q8rrMR8u=A%7UNKM`g?buZy?Fu$?o)e0Y28(XBDjO-{>{y zedIzEv<~zhdyf)SuQm`<+CIG>)y^8lk7X#yif_2=LNZsAL%?TG8mg)Ro?KB9W4q`- zgk==?8L`32!%m>kZu0_}L0Qswa8Q6J&+GkM4g6+z@RhBk0>9yt;r1BfD4b$)z|cQI zuq4}dAIBBVyL~*JP_%Tom{Ld&Q!cwatoAGU;N}Kdk?M8&PHUF4#sWl0EAbLvoP>IP zI6Wd?GOG9t6Xv%1b-Pnp0`=`KK^jAcRk(OIAK6F47pV9_VuG7?LBOo^bgW!e(D=ab z#zK#Xyz*Pq=fMt{*vQDya@t4efkx_AL}^K$YV)L;o7MjRo1 zU*M$k2?<8wvU;ygREKQ6uXHaFv6ko0tf*^3vv4h4VTC*|qQPdx0^SlW8|0--MswZY z^l1>W2+Qhcpz31CM~727;^rVx%$WOU_LH{b7B2P!S)gbE?jynb5hhFug(}f=rHLNL zZ=_@3=No#-uq#n0VomtX3X1o2fG#IJx&8HQ*BipZ1bnt;D;!YWR@TQQ)hj2%gw;5u zm628duHF21?bW?#RQ<1AhR%liO2GS-?f7vey`%*-VSyBt+RDNiP9{^f%dC_^Sp`@_ ztj2K5@bf-L;*mXQMyu~T;!hM6=k_`MPSblsh=c6)&ST3c%mJ3DzfUj`XqIU34HHHHO{u&FmW;b~u;gn5-|?s1WJ@ol z>wp|Ggpwt^7lw$TSAn7tn&0rwkcu=~{mb?2ibl##imMlIX-(#$-zYN!O0QYz0b&Dt z-Ydn5)l;2cLXU@CKby=k*+ULP?#UqutmjD7xRp7(wGv=C?`wLzJJ+Fy{LK({IHF#* zOYsS~E*FeWQn+BHh06zrnx|p9N-%g7<=`aq8HlyqL(Bwvt8zSlg z$nrXqf&X@liX4dG6=5X4`BkSc+xa22#_RMdg`pKFooeh2h$7uB4DnwFDWLSLg)2j1 z=6@a40RHd8f4z5MM)OMDLS^+q|60D)7W2|@cRfwuukjkTWL}9!s4PA*jo0s9Dh$;K zVVTgB;Zc;T868PI>aV>!EfH6s8Sw;jCx9_p=@C_WdrldJfJfLy9MsrOe=_8(m7Il) zfku${`ysMIx%DIcO`rlGXTdEI-&1JMVz3<|uiT5;z+ai=4QF`}PUpu56_0KkWi;}K z)mG#^+N?Ja^T~HwavD(VA2k>;;{6puTOho+5qyJF(AJHDzmZ~#;_!@L32IB-C*A`0 zleSC9f-m<{`z&B4{Rj^rblEq6nomG|8U^~!jN**waE-sXHg!scq6OE$*J#CWutw{s z`RmW={K6+Jm^MoP35-7_aZJhhU^15Cq9q_9V<1u3v zFj2w5NNmZq^u1>HBi>bd^I#kDeV7rnIY6(Olx?g2I)1$?l47`X;uV_!Y%h)Z5`qA?56}v^j{kHGQ zx5!;v^s+wOH=a-%N)n;6uQ%}B7t+|bPh?34i zx~1@rP<$$-v|WTj>cTL!2nB;n!VS%pZ|wu=v8B0f`fs_&qLPm{qC}1|fNZ$*i9agl zW24}GJE$NK-Di%5O#<&R#8@_A3Wl(`Ke(wjx>juo)I<`q=t(@8hF#?z$q8yGRQgbv zDxY=FYhvH2A7fzt5-2V_y8cHO$bH*A?ZZG)Hju34XAjm=w+`f5$58{Al zb5QMjd!dDB)F*>F-v3boyt!zkDCm6e&eK5BgmS8)yNOl#=bB@dhpG2xI zh>6h^@Ws8O)8Kkp?1UAqmC*g(yNo1Y5^ymo$0;je`@w3%lss?{^o0zxf$P({x;YQG z(mrsGTsF?$L@Z9iqlFn^GfbqIJgm{GPukUBPUCaY2EOk6{44TL?sl#};{rEKh$2eH zQx)>leg!P4ZUQFgjCH+A*~9%hA#hFLdgq6aFz zAvn0_EO|!_(PsLoorz@! zSKBWjXS(MKqu)s|K(V{SJ@Izvbs{*2P!#(ujVN?_Vn$TId)pI%NcX^;t!F2M;qdaZ z;$Qr(-Cb~&8|XxyI|(*lt}Pvan^J*$hBZ5vw%fXvP%1NHm2=`0wQe4qha#iYIaq80 zEU7wHzO{W6-lUzNrj~N!w)Qf?9bumDltQeGg{ka$1;#6tS4IM?4D`jsH~3ZB->)v- z7iquyff|A(rw<$V84(Yd^_rQi!r#U(rL6_!6LUzlzg6e@n+;MWQwrsHF{5t%5hJ5R zwMJkSne~|2HrBE1G_jeibuTnn2DdoyVzG#iNSEha4Vq*mV+>Ik2xc-^yPSN&;b}F@bmDSF>PH_C`~C%T)|$h)yT-c^2sN>X4yV5 zFbGSWr9++BBP1?!Bfs!vqu7k?irM%}G5ke5EJ|Q+@P(vkbizWA36Tadkp|Keud|^! zSu_{4Uf=h3-ciq%T97#;t`pJP99xF@tmLfs;B7~lFzLBVCIg*K782V=IsmqfSHt4X zLn)aK`j~n}g&3dI3e|1Ah@=OsQD*-9xBrV#UQO>6%*k(;DDWl;tQ+Dna_e+k;bZQkQ5IKWCaPPCp?Hnm#!iAhC8ci=j{a7%C_)>y7)cm;ZL~ zowJcY(?CF9AY#`4L0f061dx?J#`#>Y2O?B$gIE{9 zf`{9@pi7de$oLgMlz87UMQ>nO0}vpi2eeeVFM7kphX7$Cn1@!+oC$1xwsZ;E1hp{T zx8GQPFyCQn#iIT*_@^9KJPWwpSq~GO)65{McJ6Af$gp=Yzdq}lFRbc)pHL?7QR0Kx z8HZmuVr_%eprru67f2fX-3PplV82;egad1Up~~F%9HGZn%2Nn%(-Eo5*cWT4bh7CrYhD+T3=_%$}N+KQz=PO#&L zk`I3b2V@DnVaoMWmNnoMCbQp`g=8IlArQt|o)Ph2u}O41_PZxRue}e?S)VH07hgWB zqL4>+icmRROMuF4o}DZWw?%$|WKla-3fnXY^n-hLy0T^;ApP;lJ55f{;zoA3E+*wmD(w-mHZitUvWa)=<40; zaSRIC$Fe${zm*>cP(`qENOkrbpjn8n#)FH1NQCu7Bttk$b~tG1=@$Pl3<+3EOR2;! zSiqioi*r;P8%6o;6`@QRtMABhx%4+e*_k-NmjNf;{*+;y2n#EQywD9g-56K**2J5Ue5NQ!%{CmVzmKVciE zT_Pj{W}H;MK1uCsWu9AVisQq$M((%$Z0S@PbH!;rFhw;AGzc|=*qLRlqqw9r z3}YwM0mlH^QcPT+n)=ww&s5Bs|4C3V)vkryu$F>Yig9q z6)B$)W1)Se^D=e`J@;5zw?Z~f56X!E$0gkf+I!8_yS9W`f%R;rhuh67Y@?DF#*`X%+nj3@v>2tRqPEit+UzBnQD?8Fg; zwx-M6s*Xj^imX4O?GSf)`}jOgbiR>*(3Scr&9>;Q*LX6J8<{ADQb>s|Df!QY(Efex zgN(1;*K@g7uu4TvL17AqZ5c?*Ze#E?N+LUyQPWhB3}Rur*hTI+(bI%aFP)X<5_`)a zNavHL0G45O&-5bH!a}tQ=@5y$eT#e{>V8U&f3;KLcw(#?SboCmA2VKVRm8u|2XtA7 ztS*|!SbX8(AV9I6ZOc!Lmzt5Z_Ig-vRag0FtC*cFAtGA==Wwll^l9+)F|K~yK8Q7E zSuN&OfB$roHh+4YMF3^pFJ}IO9~@kQ!~;+PI4ztAMnlR3!t#ZO6&l^*)J@24J%ZW%$N3RW{>Mo%Ni3|FmZI}2yg2~EYxKiAG zibT?bh*~TE&!tubc+0(EI1nm!VY8b^7MdNm&L{4~U6!V%M{vww2hzg}OLQ0&ZZK2E zPQmE>5_94CTNtn>&E8!;F&KP)MdBin=_84L}(C$@aLH|F^^2s1v1SJ zaJOp9#6(Y|GxMMN2K+O1DGH_nuwH&?W8%o2KYOy`+0mZa82zD(I7R?Z3G=50_apqDgtZd z!Z*K1ru;7>mGsogFw@jLF* zGVar7UXS#tY$0Sf{Eivq=YLo@u~Czv?O3^D@DKU9-}Hq)6*$>Go=udZqfbN1?qp7e}Zg-U~_MJG((K7yEdECe! zE-E7PU!mq{QRX`GDh9qfUM`vsUzjP)4Oc#9pDf;RF_*CytXx0Y2tHuCD`C{Iu^u?r zxpfB&(wbe=xIUdXM3}p-Zq$e*W#QYrxe4sJy!LtBv}E8jr1k1?7w&iMtjy=3WTG0+ z-a8GF0vzvZzaN1PmzRd8+&cc{kkFR&q>;!Q{jur#eLn&1Qa1jdt~0^T)!<01s%8?Rl@~kO1-B^gh^^fL$jNY>Wc@9f7EJx(-z#}*QYbglnS^aycpV`o-QW8gMwSl%*zA|*cK#Wx*a{tR8C2|RM{v|z*o}H|$(%Br#orv_9-=YbK&UMn*xg03x-!&Y( zoK~s^C>NSn%Oh+2kADhdU$EG*au>6^?}oPn#oFNyy~~6g68emvZz7?3p(g|3uO_5Z z)elUmnLxk{-ayCJ07dN&pSpRl>VPE#P$7Tam&!BGzx2g4 z6UXtxKzXylbO+~zMbWqor*ldM8mR;yVT%3w^`)_=q}kiFAk^sh=ow@cry!zId#JSM zIgZ;61gkZJB<$h`4Lv}qXv$Qh&G9ZJyzxf7rRFRA;ft{YE`qBUaAyu*^l0<;{u}j@ z)=PYNBsdy|jR*LwVEoPqn9>}N$HDo=ZFOEh_J0Q6(F@@IL3vsx0K5?~Ym6(r6|^;1 zOTFE6V^%B>#->KzvfI$GX*u?&201e2YtG~#_415d4u1L<->;LZgw^iZq8JC?#JpbR z5Z9sh@-hNVQhU{UY)_If{yv+a2fd82Yu;87-BBqB?e(!h=?rEme9A?H!$(O&cJwqhIval!-x z1ym6I&G};*|E|~M2JA`}b(sGb{R%p{ML$ab$+<<2L~bZPqH2Vy#y-C6x|g@mCeC9-Xw(GS9-jT25%Bi20=kisS!!clK|3o^&|KRcBk~qz=6(V8?WUKcIsKTshYZZ zroVW=PMf6l7Y1|Y!JaSNM#+PIM-j1+K>kMn7Q}c<+FJ!=9BJl&rVg<5#(`z%+G`eK zMKd~}f)SVsi7=h}s9&+`0Jb;JaqX~5!;U&naopg2CTgw72cc(HS9!_mi3N1pb{eA* zDdT?ibbp8GOov=LL}a8-`^d3F)F;bGlqU<~Y7?MzdTMOIL}Fp+10wdIy&4>;lxuMQ zqkPZDZq>ccp8qwgfCRL^F$n0Rk88Kc7{U!w5t^hca_>9tu9UVR7O{7q{^?f+Nw1V{ ziC@fM2ruS?49I+?fmqGB@zECmB)j`yW}E?r9ibKyyi}~jz7sk3c+h@owfI_b7znoZ z+)eh?>nu*XotUItNnDM_kH0E2g{Q=n@K2Vqku^L|WPv6t)mZVQ-sY~PsIt!qQ0aJ{ z9u6Mep2q~PoIRlVM+2eQ>X6#El0PLBlJrf3YCL$fJ9%s@Hzb@jGDN`N0 z$x8n)hqAb!I@e%S6Q)ULx__?`1Sz*(-s&{=ht&m?+>53g9-WFPdJ2z348EPI_+?to zM^#&q8Z~^U+dFkXWuV5T;(rLMV->P zMi~-g`j;dIFE+jOup!8`>ZSgj@}c=E@#bqVZf=o7N+0I0*e{+-Z-zK;>>z=oz_BV=UAz-$X z=F@TLHIjKY`g)lR3(v&s88Vt}k`#be-UTp#lb7cYZFeJ3McAA%4PY7<&s}%b+qrU} zTn)IXn#_F6)7WW#OzACO!4fP?;Y@)>SV@@G5Lfvwm`5)-Kyw~1-XBn%0hJ76Vaw)0 z?3;rBkoMZO>6Lk9Aj}-mELthFLJO>C7fFz=63!>RvNxd{{MtIZfKj`hUIExQ9jeBo zF#N~3vP^Fd7T-v^#3@jt(gl-L0%4J5@qJbNF!l)uyC=}!{y_}tO=Ua(xBjDZNdJFy zq5lY1|E22~(I@cU1^LRp|53gEueen8zsge?gz~Sr6^yeTpF=QrMv~GFZ~fsOv|3+( za=rMvVt7vye0e!Z;eOfp!vND582Au(vcGH`bcAZj*5Dr2;AS$(@s?u`H+MYO@taq*5P15!~xI#!g z0MEoD@C^K8r-ZIDva_CIg+A5O`1}BkP?^e2!*tNQmkY!-W-#AMv9hS`;ixR?0^>+vr8F`)*XhW{1idmy5!|&*%^duGBcsS(f!~+EVRRJJTfu8dq^& z%~~5nGbO#4;H05W#h_U#jS8V}WmBK@lXt~Re$-yDlwZ-*`A&|4Zd!w6ZEK(uwX-2h z&b^a{(vvE^Vjplhrn<;vE*T;ldYKLL>~%gk|D0Yhh5(QBNJSpgUOs%4=A(3PM8nZ| zKc~yd;oww-m`=R1!Kmo`n7*Kk8@f=8p}nn(?(YZJvTc+$g}ct2uiqog)=O0*t2KkO zle6P(HNz|)9Ad24a5}kZ;S%{e?zTp0`v$XB9*cf=Ir*55wtiB;lzn<^OIK-Ik7-os zI7rgu$g!c7vcN&swPA+, 2017 +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.term.form 3.10.90\n" +"PO-Revision-Date: 2017-08-28 18:40 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:2 +msgid "This components aim to give a set of tools to dialog with a VT102 terminal.\n\nIt provide a way to use common visual objects like windows, standart controls like label, checkbos, list and more... like in graphical mode.\n\nIt will have also a standalone class with common VT100 command to be used for more simple applications." +msgstr "Doel van dit component is om een set instrumenten beschikbaar te stellen om de dialoog met een VT102 terminal aan te gaan.\n\nHet voorziet in een manier om gebruikelijke visuele objecten zoals vensters, standaard controls zoals label, checkbox, lijsten en meer te gebruiken...zoals in grafische modus.\n\nHet zal ook een standalone klasse bevatten met gebruikelijke VT100 comando's voor gebruik in simpelere applicaties." + +#: Termform1.termform:17 +msgid "Bonjour" +msgstr "-" + diff --git a/comp/src/gb.term.form/.project b/comp/src/gb.term.form/.project new file mode 100644 index 00000000..cbe295c9 --- /dev/null +++ b/comp/src/gb.term.form/.project @@ -0,0 +1,16 @@ +# Gambas Project File 3.0 +Startup=Termform1 +UseTerminal=1 +RedirectStderr=1 +Version=3.10.90 +VersionFile=1 +Component=gb.image +Component=gb.image.io +Component=gb.term +Description="This components aim to give a set of tools to dialog with a VT102 terminal.\n\nIt provide a way to use common visual objects like windows, standart controls like label, checkbos, list and more... like in graphical mode.\n\nIt will have also a standalone class with common VT100 command to be used for more simple applications." +Authors="Fabien Bodard (gambas.fr@gmail.com)" +TabSize=2 +Translate=1 +Language=en_US +Type=Component +Packager=1 diff --git a/comp/src/gb.term.form/.src/Align.class b/comp/src/gb.term.form/.src/Align.class new file mode 100644 index 00000000..93d652dc --- /dev/null +++ b/comp/src/gb.term.form/.src/Align.class @@ -0,0 +1,8 @@ +' Gambas class file + +Export + +Public Enum + Normal = &H00, {Left} = &H01, {Right} = &H02, Center = &H03, + TopNormal = &H10, TopLeft = &H11, TopRight = &H12, Top = &H13, + BottomNormal = &H20, BottomLeft = &H21, BottomRight = &H22, Bottom = &H23 diff --git a/comp/src/gb.term.form/.src/Arrange.class b/comp/src/gb.term.form/.src/Arrange.class new file mode 100644 index 00000000..26c19ea6 --- /dev/null +++ b/comp/src/gb.term.form/.src/Arrange.class @@ -0,0 +1,4 @@ +' Gambas class file + +Export +Public Enum None, Vertical, Horizontal, Column, Row, Fill diff --git a/comp/src/gb.term.form/.src/Attr.class b/comp/src/gb.term.form/.src/Attr.class new file mode 100644 index 00000000..c4b9af23 --- /dev/null +++ b/comp/src/gb.term.form/.src/Attr.class @@ -0,0 +1,341 @@ +' Gambas class file + +Private Const CSI As String = "\e[" +Property Read Modified As Boolean + +Property ColorMode As Integer +Property Foreground As Integer +Property Background As Integer +Property Bold As Boolean +Property Dim As Boolean +Property Underline As Boolean +Property Reverse As Boolean +Property Blink As Boolean +Private Enum FLAG_BG, FLAG_FG, FLAG_BOLD, FLAG_DIM, FLAG_REV, FLAG_UND, FLAG_BLK +'Private Enum FLAG_BOLD, FLAG_DIM, FLAG_UND, FLAG_REV, FLAG_BLK, FLAG_FG, FLAG_BG + +Private $aModified As New Boolean[8] +Private $aValues As New Boolean[5] + +Private $iColorMode As Integer = TermColor.Default +Private $iForeground As Integer +Private $iBackGround As Integer +Private $bBold As Boolean +Private $bDim As Boolean +Private $bReverse As Boolean +Private $bUnderline As Boolean +Private $bBlink As Boolean + +Public Sub _new() + + $iForeground = TermColor.Black + $iBackGround = TermColor.White + +End + +Private Function Modified_Read() As Boolean + + Dim b As Boolean + + For Each b In $aModified + If b Then Return True + Next + +End + +Private Function Foreground_Read() As Integer + + Return $iForeground + +End + +Private Sub Foreground_Write(Value As Integer) + + If $iForeground <> Value Then $aModified[FLAG_FG] = True + $iForeground = Value + +End + +Private Function Background_Read() As Integer + + Return $iBackGround + +End + +Private Sub Background_Write(Value As Integer) + + If $iBackGround <> Value Then $aModified[FLAG_BG] = True + $iBackGround = Value + +End + +Private Function Bold_Read() As Boolean + + Return $bBold + +End + +Private Sub Bold_Write(Value As Boolean) + + If $bBold <> Value Then + $aModified[FLAG_BOLD] = True + Endif + $bBold = Value + +End + +Private Function Dim_Read() As Boolean + + Return $bDim + +End + +Private Sub Dim_Write(Value As Boolean) + + If $bDim <> Value Then $aModified[FLAG_DIM] = True + $bDim = Value + +End + +Private Function Underline_Read() As Boolean + + Return $bUnderline + +End + +Private Sub Underline_Write(Value As Boolean) + + If $bUnderline <> Value Then $aModified[FLAG_UND] = True + $bUnderline = Value + +End + +Private Function Reverse_Read() As Boolean + + Return $bReverse + +End + +Private Sub Reverse_Write(Value As Boolean) + + If $bReverse <> Value Then $aModified[FLAG_REV] = True + $bReverse = Value + +End + +Private Function Blink_Read() As Boolean + + Return $bBlink + +End + +Private Sub Blink_Write(Value As Boolean) + + If $bBlink <> Value Then $aModified[FLAG_BLK] = True + $bBlink = Value + +End + +Private Function ColorMode_Read() As Integer + + Return $iColorMode + +End + +Private Sub ColorMode_Write(Value As Integer) + + $iColorMode = Value + '$bModified = True + +End + +Public Sub Send() + + Print Me._GetString(); + +End + +Public Function _GetString(Optional Force As Boolean) As String + + Dim sValue As String + + If $aModified[FLAG_BOLD] Then sValue &= IIf($bBold, "1;", "22;") + If $aModified[FLAG_DIM] Then sValue &= IIf($bDim, "8;", "28;") + If $aModified[FLAG_BLK] Then sValue &= IIf($bBlink, "5;", "25;") + If $aModified[FLAG_UND] Then sValue &= IIf($bUnderline, "4;", "24;") + If $aModified[FLAG_REV] Then sValue &= IIf($bReverse, "7;", "27;") + + If $aModified[FLAG_FG] Or Force Then + Select Case $iColorMode + Case TermColor.Default + sValue &= (30 + Max(Min($iForeground, 8), 0)) & ";" + Case TermColor.Mode256 + sValue &= "38;5;" & Max(Min($iForeground, 255), 0) & ";" + Case TermColor.ModeRGB + sValue &= "38;2;" & Subst("&1;&2;&3;", GetRed($iForeGround), GetGreen($iForeground), GetBlue($iForeground)) + End Select + Endif + + If $aModified[FLAG_BG] Or Force Then + Select Case $iColorMode + Case TermColor.Default + sValue &= (40 + Max(Min($iBackGround, 8), 0)) & ";" + Case TermColor.Mode256 + sValue &= "48;5;" & Max(Min($iBackGround, 255), 0) & ";" + Case TermColor.ModeRGB + sValue &= "48;2;" & Subst("&1;&2;&3;", GetRed($iBackGround), GetGreen($iBackGround), GetBlue($iBackGround)) + End Select + Endif + + If Not sValue Then Return + + If sValue Ends ";" Then sValue = Left(sValue, -1) + + sValue = CSI & sValue & "m" + + $aModified = New Boolean[8] + 'Debug sValue + Return sValue + +End + +Public Function WriteToStream(hStream As Stream) + + Dim bFirst As Boolean + Dim iFlag As Integer + + For iFlag = 0 To 6 + If $aModified[iFlag] Then + If Not bFirst Then + Write #hStream, CSI + bFirst = True + Else + Write #hStream, ";" + Endif + + Select Case iFlag + Case FLAG_BOLD + Write #hStream, IIf($bBold, "1", "22") + Case FLAG_DIM + Write #hStream, IIf($bDim, "8", "28") + Case FLAG_BLK + Write #hStream, IIf($bBlink, "5", "25") + Case FLAG_UND + Write #hStream, IIf($bUnderline, "4", "24") + Case FLAG_REV + Write #hStream, IIf($bReverse, "7", "27") + + Case FLAG_FG + Select Case $iColorMode + Case TermColor.Default + Write #hStream, Str((30 + Max(Min($iForeground, 8), 0))) + Case TermColor.Mode256 + Write #hStream, ("38;5;" & Max(Min($iForeground, 255), 0)) + Case TermColor.ModeRGB + Write #hStream, ("38;2;" & Subst("&1;&2;&3", GetRed($iForeGround), GetGreen($iForeground), GetBlue($iForeground))) + End Select + + Case FLAG_BG + Select Case $iColorMode + Case TermColor.Default + Write #hStream, Str((40 + Max(Min($iBackGround, 8), 0))) + + Case TermColor.Mode256 + Write #hStream, ("48;5;" & Max(Min($iBackGround, 255), 0)) + + Case TermColor.ModeRGB + Write #hStream, ("48;2;" & Subst("&1;&2;&3", GetRed($iBackGround), GetGreen($iBackGround), GetBlue($iBackGround))) + + End Select + End Select + + Endif + + Next + If bFirst Then + Write #hStream, "m" + Endif + + $aModified = New Boolean[8] + +End + +Private Function GetRed(iValue As Integer) As Integer + + Return Lsr(iValue, 16) And 255 + +End + +Private Function GetGreen(iValue As Integer) As Integer + + Return Lsr(iValue, 8) And 255 + +End + +Private Function GetBlue(iValue As Integer) As Integer + + Return iValue And 255 + +End + +Public Sub FillFrom(iAttr As Integer) + + If BTst(iAttr, FLAG_BG) Then + Me.Background = Lsr(iAttr, 16) And 255 + Else + Me.Background = 0 + Endif + + If BTst(iAttr, FLAG_FG) Then + Me.Foreground = Lsr(iAttr, 24) And 255 + Else + Me.Foreground = 0 + Endif + + Me.Bold = BTst(iAttr, FLAG_BOLD) + Me.Dim = BTst(iAttr, FLAG_DIM) + Me.Reverse = BTst(iAttr, FLAG_REV) + Me.Underline = BTst(iAttr, FLAG_UND) + Me.Blink = BTst(iAttr, FLAG_BLK) + +End + +Public Sub Reset() + + $iForeground = -1 + $iBackGround = -1 + $bBold = False + $bDim = False + $bUnderline = False + $bReverse = False + $bBlink = False + $aModified = New Boolean[8] + Print CSI & "0m"; + +End + +Public Sub IsVoid() As Boolean + + If $iForeground >= 0 Then Return + If $iBackground >= 0 Then Return + If $bBold Or If $bDim Or If $bUnderline Or If $bReverse Or If $bBlink Then Return + Return True + +End + +Public Function GetInteger() As Integer + + Dim iAttr As Integer + + If Me.Background <> -1 Then iAttr = BSet(iAttr, FLAG_BG) + Lsl(Me.Background And 255, 16) + If Me.Foreground <> -1 Then iAttr = BSet(iAttr, FLAG_FG) + Lsl(Me.Foreground And 255, 24) + + If Me.Bold Then iAttr = BSet(iAttr, FLAG_BOLD) + If Me.Dim Then iAttr = BSet(iAttr, FLAG_DIM) + If Me.Reverse Then iAttr = BSet(iAttr, FLAG_REV) + If Me.Underline Then iAttr = BSet(iAttr, FLAG_UND) + If Me.Blink Then iAttr = BSet(iAttr, FLAG_BLK) + + Return iAttr + +End diff --git a/comp/src/gb.term.form/.src/Border.class b/comp/src/gb.term.form/.src/Border.class new file mode 100644 index 00000000..4e7c413f --- /dev/null +++ b/comp/src/gb.term.form/.src/Border.class @@ -0,0 +1,4 @@ +' Gambas class file + +Export +Public Enum None, Simple, Double \ No newline at end of file diff --git a/comp/src/gb.term.form/.src/Char.class b/comp/src/gb.term.form/.src/Char.class new file mode 100644 index 00000000..e6c22f72 --- /dev/null +++ b/comp/src/gb.term.form/.src/Char.class @@ -0,0 +1,14 @@ +' Gambas class file + +Export +Public c As Integer +Public Attr As Integer + +Static Public Sub _call(ch As String, iAttr As Integer) As Char + + Dim hChar As New Char + hChar.c = String.Code(ch) + hChar.Attr = iAttr + Return hChar + +End diff --git a/comp/src/gb.term.form/.src/Desktop.class b/comp/src/gb.term.form/.src/Desktop.class new file mode 100644 index 00000000..e7be161b --- /dev/null +++ b/comp/src/gb.term.form/.src/Desktop.class @@ -0,0 +1,45 @@ +' Gambas class file + +Export +Create Static +Property Read Width, W As Integer +Property Read Height, H As Integer +Property Background As Char + +Static Private $hChar As New Char + +Static Public Sub _init() + + Dim hAttr As New Attr + 'hAttr.ColorMode = TermColor.Mode256 + hAttr.Background = TermColor.Desktop + $hChar.Attr = hAttr.GetInteger() + $hChar.c = Asc(" ") + +End + + + +Private Function Width_Read() As Integer + + Return File.Out.Term.Width + +End + +Private Function Height_Read() As Integer + + Return File.Out.Term.Height + +End + +Private Function Background_Read() As Char + + Return $hChar + +End + +Private Function Background_Write(Value As Char) + + $hChar = Value + +End diff --git a/comp/src/gb.term.form/.src/Dialog/Message.class b/comp/src/gb.term.form/.src/Dialog/Message.class new file mode 100644 index 00000000..a5a40b54 --- /dev/null +++ b/comp/src/gb.term.form/.src/Dialog/Message.class @@ -0,0 +1,42 @@ +' Gambas class file + +Create Private + + +Static Public Sub _call(Message As String) + + Dim hLabel As TermLabel + Dim hHbox As TermHBox + Dim hButton As TermButton + Dim $hForm As TermForm + $hForm = New TermForm + $hForm.Resizable = False + hLabel = New TermLabel($hForm) + hHbox = New TermHBox($hForm) + hButton = New TermButton(hHbox) As "btnOk" + + $hForm.resize(30, 5) + $hForm.Center + $hForm.Text = "Message Box" + $hForm._Arrangement = Arrange.Vertical + hHbox._Arrangement = Arrange.Horizontal + hHbox.Invert = True + hLabel.Expand = True + $hForm.Border = Border.Simple + hLabel.Text = Message + hButton.Text = "Ok" + 'hButton._Shadow = True + hButton.Background = TermColor.Yellow + hButton.Alignment = Align.Center + $hForm.Show + +End + + +Static Public Sub btnOk_MouseUp() + + Last.window.Close + +End + + diff --git a/comp/src/gb.term.form/.src/Key.class b/comp/src/gb.term.form/.src/Key.class new file mode 100644 index 00000000..51476d70 --- /dev/null +++ b/comp/src/gb.term.form/.src/Key.class @@ -0,0 +1,65 @@ +' Gambas class file + +Export +Public Enum Normal, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, BackSpace, Enter, {Return}, Tab, BackTab, Up, Down, Left, Right, Home, {End}, Insert, Delete, PageDown, PageUp, Menu +Static Property Read Shift As Boolean +Static Property Read Control As Boolean +Static Property Read Code As Integer +Static Property Read Text As String +Static Property Read Alt As Boolean +Static Private $bControl As Boolean +Static Private $bShift As Boolean +Static Private $bAlt As Boolean +Static Private $iCode As Integer +Static Private $sText As String + +Static Public Sub _SetKey(sKey As String, code As Integer, Alt As Boolean, Control As Boolean, Shift As Boolean) + + $bShift = Shift + $bControl = Control + $bALt = ALt + $iCode = Code + $sText = sKey + If TermWindows.Debug = TermWindows.DebugInput Then Error "Alt: " & alt & " Control: " & Control & " Shift: " & Shift & " Code :" & code & " Text :" & sKey + +End + +Static Private Function Shift_Read() As Boolean + + Return $bShift + +End + +Static Private Function Control_Read() As Boolean + + Return $bControl + +End + +Static Private Function Code_Read() As Integer + + Return $iCode + +End + +Static Private Function Text_Read() As String + + Return $sText + +End + +Static Private Function Alt_Read() As Boolean + + Return $bAlt + +End + +Static Public Sub _Reset() + + $bShift = False + $bControl = False + $bALt = False + $iCode = 0 + $sText = "" + +End diff --git a/comp/src/gb.term.form/.src/Mouse.class b/comp/src/gb.term.form/.src/Mouse.class new file mode 100644 index 00000000..bb5e4920 --- /dev/null +++ b/comp/src/gb.term.form/.src/Mouse.class @@ -0,0 +1,102 @@ +' Gambas class file + +Create Private + +Static Property Read {Left} As Boolean +Static Property Read {Right} As Boolean +Static Property Read {Middle} As Boolean +Static Property Read X, Col As Integer +Static Property Read Y, Row As Integer +Static Property Read ScreenX, ScreenCol As Integer +Static Property Read ScreenY, ScreenRow As Integer + +Static Private $iRow As Integer +Static Private $iCol As Integer +Static Private $iScreenRow As Integer +Static Private $iScreenCol As Integer +Static Private $bLeft As Boolean +Static Private $bMiddle As Boolean +Static Private $bRight As Boolean +Static Private $iDelta As Integer + +Static Private Function Left_Read() As Boolean + + Return $bLeft + +End + +Static Private Function Right_Read() As Boolean + + Return $bRight + +End + +Static Private Function Middle_Read() As Boolean + + Return $bMiddle + +End + +Static Public Sub Hide() + + + +End + +Static Public Sub Show() + + + +End + + +Static Public Sub _SetState(hControl As TermControl, Col As Integer, Row As Integer, Optional btnLeft As Boolean, btnMiddle As Boolean, btnRight As Boolean, Delta As Integer) + + If Not IsMissing(btnLeft) Then $bLeft = btnLeft + If Not IsMissing(btnMiddle) Then $bMiddle = btnMiddle + If Not IsMissing(btnRight) Then $bRight = btnRight + $iDelta = Delta + $iScreenRow = Row + $iScreenCol = Col + If hControl = Null Then + $iRow = $iScreenRow + $iCol = $iScreenCol + Else + $irow = $iScreenRow - hControl.ScreenY + $iCol = $iScreenCol - hControl.ScreenX + Endif + + + + + If TermWindows.Debug = TermWindows.DebugInput Then Error "Mouse State: Row: " & $iRow & " Col: " & $iCol & " btnLeft: " & $bLeft & " btnMiddle: " & $bMiddle & " btnRight: " & $bRight & " Delta: " & $iDelta +End + + + +Static Private Function X_Read() As Integer + + Return $iCol + +End + +Static Private Function Y_Read() As Integer + + Return $iRow + +End + +Static Private Function ScreenX_Read() As Integer + + Return $iScreenCol + +End + + + +Static Private Function ScreenY_Read() As Integer + + Return $iScreenRow + +End + diff --git a/comp/src/gb.term.form/.src/TermButton.class b/comp/src/gb.term.form/.src/TermButton.class new file mode 100644 index 00000000..be3e3016 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermButton.class @@ -0,0 +1,150 @@ +' Gambas class file + +Export +Inherits TermControl + +Class Rect + +Public Const _Properties As String = "*,Text,Shadow" +Public Const _DrawWith As String = "Button" + +Property Text As String +Property Shadow As Boolean +Property Alignment As Integer +Property Read _DefaultBackground As Integer +Property Read _DefaultForeGround As Integer +Private $sText As String +Private $iOldLen As Integer +Private $iAlignment As Integer + + +Public Sub _new() + + Me._UseFocus = True + +End + + + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + + +End + +Public Sub _Render() + Dim hAttr As New Attr + Dim iAttr As Integer + Dim i As Integer + Dim sALign As String + Dim hRectC As Rect = Me._GetClientRect() + If Not Me._NeedRender Then Return + Super._Render + + Select Case $iAlignment + Case Align.Right + sALign = String(hRectC.Width - Len($sText), " ") & $sText + Case Align.Center + sALign = String(CInt(Ceil((hRectC.Width - Len($sText)) / 2)), " ") & $sText + Case Else + sALign = $sText + End Select + If Not Me.HaveFocus Then + hAttr.Background = Me._GetBackGround() + hAttr.Foreground = Me._GetForeground() + Else + hAttr.Background = TermColor.Focus + hAttr.Foreground = TermColor.FocusText + Endif + iAttr = hAttr.GetInteger() + + For i = 1 To Me._Content[0].count + If Me._Content[0][Me._Content[0].Max] And If i > Max($iOldLen, String.Len(sALign)) Then Break + If i <= String.Len(sALign) Then + Me._Content[0][i - 1] = Char(Mid(sALign, i, 1), iAttr) + Else + Me._Content[0][i - 1] = Char(" ", iAttr) + Endif + Next + + Me._NeedRender = False +End + + + + +Private Function Shadow_Read() As Boolean + + Return Super._Shadow + +End + +Private Sub Shadow_Write(Value As Boolean) + + Super._Shadow = Value + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + + +End + +Public Sub _KeyPress() + + If Key.Code = Key.Return Then Raise Click + + Super._KeyPress + +End + + + + +Private Function _DefaultBackground_Read() As Integer + + Return TermColor.ButtonBackground + +End + +Private Function _DefaultForeGround_Read() As Integer + + Return TermColor.ButtonText + +End + +Public Sub _GetBackGround() As Integer + + If Me.Background = -1 Then + Return TermColor.ButtonBackground + Else + Return Me.Background + Endif + +End + + +Public Sub _GetForeGround() As Integer + + If Me.Foreground = -1 Then + Return TermColor.ButtonText + Else + Return Me.Foreground + Endif + +End diff --git a/comp/src/gb.term.form/.src/TermCheckBox.class b/comp/src/gb.term.form/.src/TermCheckBox.class new file mode 100644 index 00000000..9781d28a --- /dev/null +++ b/comp/src/gb.term.form/.src/TermCheckBox.class @@ -0,0 +1,114 @@ +' Gambas class file + +Export +Inherits TermControl +Class Rect +Property Text As String +Property Value As Integer +Property Tristate As Boolean + +Private $sText As String +Private $iOldLen As Integer +Private $iValue As Integer +Private $bTristate As Boolean + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End + +Public Sub _Render() + + Dim hAttr As New Attr + Dim iAttr As Integer + Dim i As Integer + Dim sALign As String + Dim hRectC As Rect = Me._GetClientRect() + Dim sValue As String + Dim iBC As Integer + + If Not Me._NeedRender Then Return + Super._Render + + sValue = Choose($iValue + 2, "x", " ", "/") + + sALign = "[" & sValue & "] " & $sText + + iBC = Me._GetBackGround() + If Me.HaveFocus Then iBC = TermColor.Focus + hAttr.Background = iBC + hAttr.Foreground = Me.Foreground + iAttr = hAttr.GetInteger() + + For i = 1 To Me._Content[0].count + If Me._Content[0][Me._Content[0].Max] And If i > Max($iOldLen, String.Len(sALign)) Then Break + If i <= String.Len(sALign) Then + Me._Content[0][i - 1] = Char(Mid(sALign, i, 1), iAttr) + Else + Me._Content[0][i - 1] = Char(" ", iAttr) + Endif + Next + + Me._NeedRender = False + +End + +Private Function Value_Read() As Integer + + Return $iValue + +End + +Private Sub Value_Write(Value As Integer) + + $iValue = Max(Min(Value, 1), -1) + +End + +Private Function Tristate_Read() As Boolean + + Return $bTristate + +End + +Private Sub Tristate_Write(Value As Boolean) + + $bTristate = Value + +End + +Public Sub _MouseUp() + + ChangeValue + Super._MouseUp + Super._Click + 'Me._NeedRender = True + 'Me.Refresh +End + +Private Sub ChangeValue() + + If Me.Value < IIf($bTristate, 1, 0) Then + Me.Value = $iValue + 1 + Else + Me.Value = -1 + Endif + Me.Refresh +End + + +Public Sub _KeyPress() + + If Key.Text = " " Then + ChangeValue + Endif + Super._Click + Super._KeyPress +End diff --git a/comp/src/gb.term.form/.src/TermColor.class b/comp/src/gb.term.form/.src/TermColor.class new file mode 100644 index 00000000..a5f30b6d --- /dev/null +++ b/comp/src/gb.term.form/.src/TermColor.class @@ -0,0 +1,21 @@ +' Gambas class file + +Export +Public Const Transparent As Integer = -1 +Public Enum Black, Red, Green, Yellow, Blue, Magenta, Cyan, White 'LightGray +'Public Enum DarkGray = 8, LightRed = 9, LightGreen = 10, LightYellow = 11, LightBlue = 12, LightMagenta = 13, LightCyan = 14, White = 15 + +Public Enum {Default}, Mode256, ModeRGB + +Static Public ForeGround As Integer = Black +Static Public Background As Integer = White +Static Public ActiveWindow As Integer = Red +Static Public InactiveWindow As Integer = Magenta +Static Public Focus As Integer = Yellow +Static Public FocusText As Integer = White +Static Public {Desktop} As Integer = Black +Static Public ButtonBackground As Integer = Red +Static Public ButtonText As Integer = White +Static Public Border As Integer = Black +Static Public TextBackground As Integer = Cyan +Static Public TextForeground As Integer = Black diff --git a/comp/src/gb.term.form/.src/TermContainer.class b/comp/src/gb.term.form/.src/TermContainer.class new file mode 100644 index 00000000..cc2706b3 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermContainer.class @@ -0,0 +1,243 @@ +' Gambas class file + +Export +Create Private +Inherits TermControl +Class Rect + +Public Const _IsContainer As Boolean = True +Public Const _Group As String = "Container" +Public Const _Properties As String = "*" + +Property Read Children As TermControl[] +Property Read ClientWidth, CLientW As Integer +Property Read ClientHeight, ClientH As Integer +Property Spacing As Integer +Property Padding As Integer +Property Border As Integer +Property Shadow As Boolean +Property Invert As Boolean + +Private $iSpacing As Integer '= 1 +Private $bInvert As Boolean +Private $aChildren As New Object[] + +Public _Arrangement As Integer '= Arrange.Vertical + +Public Sub _new() + + Me._AllowFocus = False + +End + + +Public Sub _Add(hCtrl As TermControl) + + $aChildren.Add(hCtrl) + +End + +Private Function Children_Read() As TermControl[] + + If Not $aChildren Then $aChildren = New TermControl[] + Return $aChildren + +End + +Public Sub _Render() + + Dim hChild As TermControl + Dim i, j As Integer + Dim hAttr As New Attr + Dim iAttr As Integer + + If Me._NeedRender Then + Super._Render + + Endif + For Each hChild In Me.Children + + hChild._Render + + Next + Me._NeedRender = False +End + +Public Sub _Arrange() + + Dim hChild As Object + Dim H As Integer + Dim iExt As Integer '= Min(5, $yBorder + $iPadding) + Dim iNExp As Integer + Dim iHExp As Integer + Dim hRectClient As Rect = Me._GetClientRect() + Dim iPadding As Integer = Me.Padding + 'If Me.tag = "*" Then Stop + Select Case _Arrangement + Case Arrange.Vertical + + For Each hChild In $aChildren + If Not hChild.Expand Then + iHExp += hChild.Height + If $iSpacing > 0 Then iHExp += $iSpacing + Else + Inc iNExp + Endif + 'If Me Is TermVBox Then Stop + If iHExp >= (hrectClient.Height - iPadding * 2) Then Break + Next + + If iNExp > 0 Then + If iHExp > 0 And If $iSpacing > 0 Then + 'iHExp -= $iSpacing + Endif + iHExp = (hrectClient.Height - iHExp) / iNExp + Endif + + H = 0 'hrectClient.Top + For Each hChild In $aChildren + 'If hChild Is TermPictureBox Then Stop + If Not hChild.Ignore Then + hChild.X = 0 'hrectClient.Left + hChild.Y = H + hChild.Width = hrectClient.Width + If hChild.Expand Then hChild.H = iHExp + H += hChild.H + $iSpacing + Endif + hChild._Arrange() + 'If H >= Me.ClientHeight Then Break + Next + + Case Arrange.Horizontal + For Each hChild In $aChildren + If Not hChild.Expand Then + iHExp += hChild.Width + If $iSpacing > 0 Then iHExp += $iSpacing + Else + Inc iNExp + Endif + 'If Me Is TermVBox Then Stop + If iHExp >= (hrectClient.Width - iPadding * 2) Then Break + Next + + If iNExp > 0 Then + + If iHExp > 0 And If $iSpacing > 0 Then + 'iHExp -= $iSpacing + Endif + iHExp = Ceil((hrectClient.Width - iHExp) / iNExp) + Endif + + If $bInvert Then + H = hrectClient.Right - 1 + Else + H = hrectClient.Width - Me.Width + Endif + For Each hChild In $aChildren + If Not hChild.Ignore Then + If $bInvert Then H -= hChild.W + hChild.Y = hrectClient.Top + + hChild.X = H + hChild.Height = hrectClient.Height + If hChild.Expand Then hChild.W = iHExp + 'f hChild.Expand Then Stop + If $bInvert Then + H -= $iSpacing + Else + H += hChild.W + $iSpacing + Endif + Endif + hChild._Arrange() + 'If H >= Me.ClientHeight Then Break + Next + End Select + +End + +' Public Sub _GetClientRect() As Rect +' +' Return $rectClient.Copy() +' +' End + +Private Function ClientWidth_Read() As Integer + + Return Me._GetClientRect().Width + 'Return $rectClient.Width + 'Return $iCLientW + +End + +Private Function ClientHeight_Read() As Integer + + Return Me._GetClientRect().Height + +End + +Private Function Spacing_Read() As Integer + + Return $iSpacing + +End + +Private Sub Spacing_Write(Value As Integer) + + $iSpacing = Value + +End + +Private Function Padding_Read() As Integer + + Return Me._Padding + +End + +Private Sub Padding_Write(Value As Integer) + + Me._Padding = Value + Me._DefineRect + +End + +Private Function Border_Read() As Integer + + Return Super._Border + +End + +Private Sub Border_Write(Value As Integer) + + Super._Border = Value + + Me._DefineRect + Me.Refresh + + +End + +Private Function Shadow_Read() As Boolean + + Return Super._Shadow + +End + +Private Sub Shadow_Write(Value As Boolean) + + Super._Shadow = True + +End + +Private Function Invert_Read() As Boolean + + Return $bInvert + +End + +Private Sub Invert_Write(Value As Boolean) + + $bInvert = Value + +End + + diff --git a/comp/src/gb.term.form/.src/TermControl.class b/comp/src/gb.term.form/.src/TermControl.class new file mode 100644 index 00000000..d4195665 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermControl.class @@ -0,0 +1,783 @@ +' Gambas class file + +Export +Create Private +Class Rect + +Public Const _IsControl As Boolean = True +Public Const _IsContainer As Boolean = False +Public Const _Properties As String = "X{Position},Y{Position},Width{Dimension},Height{Dimension},Background{Color},Foreground{Color},Ignore" +'"Left{ReportCoord},Top{ReportCoord},Width{ReportCoord},Height{ReportCoord},Brush{ReportBrush},Visible=True,Fixed,Font,Padding,Margin,Ignore,Expand,AutoResize,Tag,Range" +Public Const _Family As String = "TermForm" + +Static Public _IdToControl As New Collection +Static Private $iLastId As Integer = 1 + +Public _Content As Char[][] +Public _NeedRender As Boolean +Public _UseFocus As Boolean +Property Read id As Integer + +Property Read Parent As TermContainer +Property Read ScreenX As Integer +Property Read ScreenY As Integer +Property Read Window As TermWindow + +Property Left, X, Column As Integer +Property Top, Y, {Line} As Integer +Property Width, W As Integer +Property Height, H As Integer +Property Background As Integer +Property Foreground As Integer +Property Tag As Variant +Property Expand As Boolean +Property _Shadow As Boolean +Property _Border As Integer +Property _Padding As Integer +Property _ColorMode As Integer +Property Visible As Boolean +Property Ignore As Boolean + +Property Read Next As TermControl +Property Read Previous As TermControl +Property Read HaveFocus As Boolean +Property Read _DefaultBackground As Integer +Property Read _DefaultForeGround As Integer + +Property Read _ClientHeight As Integer +Property Read _ClientWidth As Integer + +Private $iColorMode As Integer +Private $iBorder As Integer + +Private $bShadow As Boolean +Private $bIgnore As Boolean +Private _RecalcPos As Boolean +Private $rectControl As New Rect +Private $iId As Integer +Private $iParent As Integer +Private $iScreenX As Integer +Private $iScreenY As Integer +Private $iWindow As Integer +Private $iBackGround As Integer = -1 +Private $iForeground As Integer = -1 +Private $vTag As Variant +Private $bExpand As Boolean +Private $yBorder As Byte +Private $iPadding As Integer +Private $rectClient As Rect +Private $iVisible As Boolean = True +Private $bLockRender As Boolean +Public _AllowFocus As Boolean = True +Private $bHaveFocus As Boolean +Event MouseDown +Event MouseUp +Event MouseWheel +Event MouseMove +Event Click +Event KeyPress +Event GotFocus +Event LostFocus + +Public Sub _new(Optional hParent As TermContainer) + + $iId = $iLastId + Inc $iLastId + If hParent Then + $iParent = hParent.Id + hParent._Add(Me) + GetWindow + Else + If Me Is TermWindow Then + TermWindows._Add(Me) + $iWindow = $iId + Endif + Endif + $rectControl.Width = 5 + $rectControl.Height = 1 + _DefineRect + _IdToControl[$iId] = Me + _RecalcPos = True + _NeedRender = True + _AllowFocus = True + If hParent Then + TermWindows.SetRefreshArea(hParent._GetScreenRect()) + 'TermWindows._Render + 'hParent.Refresh + Endif + ' TermWindows._RefreshScreen + 'Me.Window.Refresh + +End + +Private Sub ResizeBuffer(Width As Integer, Height As Integer) + + Dim i As Integer + Dim aLine As Char[] + + 'Debug "Resize", Object.Type(Me) + _Content = New Char[][] + For i = 0 To Height + aLine = New Char[Width] + _Content.Add(aLine) + Next + Me._NeedRender = True + +End + +Public Sub _Render() + + Dim i, j As Integer + Dim hAttr As New Attr + Dim iAttr As Integer + Dim hRectC As Rect + Dim iShadow As Integer = IIf($bShadow, 1, 0) + Dim iBC As Integer + + If Not Me._NeedRender Then Return + 'If Me Is TermContainer Then + hRectC = $rectControl + 'Else + ' hRectC = $rectClient + 'Endif + + If Not _Content Then ResizeBuffer(hRectC.Width, hRectC.Height) + + iBC = Me._GetBackGround() + If Me.HaveFocus Then iBC = TermColor.Focus + hAttr.Background = iBC + + iAttr = hAttr.GetInteger() + + For i = 0 To _Content.Max + hAttr.Foreground = TermColor.Border + For j = 0 To _Content[0].Max + _Content[i][j] = Char(" ", iAttr) + Next + Next + + Select Case $iBorder + Case Border.Simple + hAttr.Background = iBC + + iAttr = hAttr.GetInteger() + For j = 1 To hRectC.width - 2 '_Content[0].Max - 1 + _Content[0][j] = Char(String.Chr(&h2500), iAttr) + Next + For j = 1 To hRectC.width - 2 + _Content[hRectC.Height - 1 - iShadow][j] = Char(String.Chr(&h2500), iAttr) + Next + For i = 1 To hRectC.Height - 2 - iShadow + + _Content[i][0] = Char(String.chr(&H2502), iAttr) + _Content[i][hRectC.Width - 1 - iShadow] = Char(String.chr(&H2502), iAttr) + Next + _Content[0][0] = Char(String.chr(&H250C), iAttr) + _Content[0][hRectC.Width - 1 - iShadow] = Char(String.chr(&H2510), iAttr) + _Content[hRectC.Height - 1 - iShadow][0] = Char(String.chr(&H2514), iAttr) + _Content[hRectC.Height - 1 - iShadow][hRectC.Width - 1 - iShadow] = Char(String.chr(&H2518), iAttr) + + Case Border.Double + hAttr.Background = iBC + + iAttr = hAttr.GetInteger() + For j = 1 To hRectC.width - 2 '_Content[0].Max - 1 + _Content[0][j] = Char(String.Chr(&h2550), iAttr) + Next + For j = 1 To hRectC.width - 2 + _Content[hRectC.Height - 1 - iShadow][j] = Char(String.Chr(&h2550), iAttr) + Next + For i = 1 To hRectC.Height - 2 - iShadow + + _Content[i][0] = Char(String.chr(&H2551), iAttr) + _Content[i][hRectC.Width - 1 - iShadow] = Char(String.chr(&H2551), iAttr) + Next + _Content[0][0] = Char(String.chr(&H2554), iAttr) + _Content[0][hRectC.Width - 1 - iShadow] = Char(String.chr(&H2557), iAttr) + _Content[hRectC.Height - 1 - iShadow][0] = Char(String.chr(&H255A), iAttr) + _Content[hRectC.Height - 1 - iShadow][hRectC.Width - 1 - iShadow] = Char(String.chr(&H255D), iAttr) + Case Else + + End Select + + If $bShadow Then + ' If Me Is TermButton Then Stop + hAttr.Foreground = TermColor.Black + If IsNull(Me.Parent) Then + hAttr.Background = TermColor.Desktop + Else + hAttr.Background = Me.Parent.Background + Endif + + iAttr = hAttr.GetInteger() + For i = 1 To _Content[0].Max + + _Content[_Content.Max - 1][i] = Char(String.chr(&H2580), iAttr) + + Next + For i = 1 To _Content.Max - 2 + _Content[i][_Content[0].Max] = Char(String.chr(&H2588), iAttr) + + Next + 'hAttr.Background = If(IsNull(Me.Parent), Desktop.BackGround, Me.Parent.Background) + _Content[0][_Content[0].Max] = Char(String.chr(&H2584), iAttr) + _Content[_Content.Max - 1][0] = Char(" ", iAttr) + + Endif + +End + +Private Function id_Read() As Integer + + Return $iId + +End + +Private Function Parent_Read() As TermContainer + + Return _IdToControl[$iParent] + +End + +Private Function Left_Read() As Integer + + Return $rectControl.X + +End + +Private Sub Left_Write(Value As Integer) + + If $rectControl.Left = Value Then Return + TermWindows.SetRefreshArea(Rect(Me.ScreenX, Me.ScreenY, $rectControl.Width, $rectControl.Height)) + $rectControl.X = Value + Me._DefineRect + TermWindows.SetRefreshArea(Rect(Me.ScreenX, Me.ScreenY, $rectControl.Width, $rectControl.Height)) + _RecalcPos = True + If Not $bLockRender Then TermWindows._Render + +End + +Private Function Top_Read() As Integer + + 'If Me.tag = "--" Then Stop + + Return $rectControl.Y + +End + +Private Sub Top_Write(Value As Integer) + + If $rectControl.Top = Value Then Return + TermWindows.SetRefreshArea(Rect(Me.ScreenX, Me.ScreenY, $rectControl.Width, $rectControl.Height)) + $rectControl.Y = Value + + Me._DefineRect + _RecalcPos = True + + TermWindows.SetRefreshArea(Rect(Me.ScreenX, Me.ScreenY, $rectControl.Width, $rectControl.Height)) + If Not $bLockRender Then TermWindows._Render + +End + +Private Function Width_Read() As Integer + + Return $rectControl.Width + +End + +Private Sub Width_Write(Value As Integer) + + If $rectControl.Width = Value Then Return + If Value < 1 Then Return + TermWindows.SetRefreshArea(Rect(Me.ScreenX, Me.ScreenY, $rectControl.Width, $rectControl.Height)) + $rectControl.Width = Value + + '$rectDrawing.width = Value + IIf($bShadow, 1, 0) + Me._DefineRect + ResizeBuffer($rectControl.Width, $rectControl.Height) + TermWindows.SetRefreshArea(Rect(Me.ScreenX, Me.ScreenY, $rectControl.Width, $rectControl.Height)) + If Not $bLockRender Then TermWindows._Render 'If Me Is TermContainer Then Me._SetClientRect() + +End + +Private Function Height_Read() As Integer + + Return $rectControl.Height + +End + +Private Sub Height_Write(Value As Integer) + + If $rectControl.Height = Value Then Return + If Value < 1 Then Return + TermWindows.SetRefreshArea(Rect(Me.ScreenX, Me.ScreenY, $rectControl.Width, $rectControl.Height)) + $rectControl.Height = Value + '$rectDrawing.Height = Value + IIf($bShadow, 1, 0) + Me._DefineRect + ResizeBuffer($rectControl.Width, $rectControl.Height) + TermWindows.SetRefreshArea(Rect(Me.ScreenX, Me.ScreenY, $rectControl.Width, $rectControl.Height)) + If Not $bLockRender Then TermWindows._Render 'If Me Is TermContainer Then Me._SetClientRect() + +End + +Private Function ScreenX_Read() As Integer + + Dim hParent As TermControl + Dim iBorder As Integer + + hParent = _IdToControl[$iParent] + If hParent._Border > 0 Then iBorder = 1 + Return _IdToControl[$iParent].ScreenX + iBorder + Me.Left + +End + +Private Function ScreenY_Read() As Integer + + Dim hParent As TermControl + Dim iBorder As Integer + + hParent = _IdToControl[$iParent] + If hParent._Border > 0 Then iBorder = 1 + Return hParent.ScreenY + iBorder + Me.Top + +End + +Private Sub GetWindow() + + Dim hCont As Object = Me.Parent + + Do + If hCont.Parent = Null Then + $iWindow = hCont.Id + Break + Endif + hCont = hCont.Parent + Loop + +End + +Private Sub ComputeScreenXY() + + Dim hC As TermControl = Me.Parent + + If Me Is TermLabel Then Stop + If hC Then + $iScreenX = hC.ScreenX + $rectControl.Left + $iScreenY = hC.ScreenY + $rectControl.Top + Else + $iScreenX = $rectControl.Left + $iScreenY = $rectControl.Top + Endif + _RecalcPos = False + +End + +Private Function Window_Read() As TermWindow + + Return _IdToControl[$iWindow] + +End + +Public Function _GetScreenClientRect() As Rect + + Dim iCorner As Integer = IIf($iBorder > 0, 1, 0) + $iPadding + Dim iSize As Integer = iCorner * 2 + IIf($bShadow, 1, 0) + + Return rect(Me.ScreenX + iCorner, Me.ScreenY + iCorner, $rectControl.Width - iSize, $rectControl.Height - iSize) + +End + +Public Function _GetScreenRect() As Rect + + Return Rect(Me.ScreenX, Me.ScreenY, $rectControl.width, $rectControl.Height) + +End + +Public Function _GetRect() As Rect + + Return $rectControl.Copy() + +End + +Private Function Background_Read() As Integer + + Return $iBackGround + +End + +Private Sub Background_Write(Value As Integer) + + $iBackGround = Value + Me.Refresh + +End + +Private Function Foreground_Read() As Integer + + Return $iForeGround + +End + +Private Sub Foreground_Write(Value As Integer) + + $iForeground = Value + Me.Refresh + +End + +Public Sub _GetChar(c As Integer, l As Integer) As Char + 'If Me.Tag = "#" Then Stop + + Return _Content[l - Me.ScreenY][c - Me.ScreenX] + +End + +Private Function Tag_Read() As Variant + + Return $vTag + +End + +Private Sub Tag_Write(Value As Variant) + + $vTag = Value + +End + +Public Sub _Arrange() + +End + +Public Sub Move(Left As Integer, Top As Integer, Optional Width As Integer, Height As Integer) + + $bLockRender = True + Me.Left = Left + Me.Top = Top + If Not IsMissing(Width) Then Me.Width = Width + If Not IsMissing(Height) Then Me.Height = Height + $bLockRender = False + TermWindows._Render + +End + +Private Function Expand_Read() As Boolean + + Return $bExpand + +End + +Private Sub Expand_Write(Value As Boolean) + + Dim hParent As TermContainer + $bExpand = Value + hParent = Me.Parent + If hParent Then + TermWindows.SetRefreshArea(hParent._GetScreenRect()) + TermWindows._Render + + Endif + +End + +Public Sub _Initialize() + + 'Here generate the content Array to the right size + 'Define the true size for the Client content + +End + +Private Function _Shadow_Read() As Boolean + + Return $bShadow + +End + +Private Sub _Shadow_Write(Value As Boolean) + + $bShadow = Value + Me._NeedRender = True + +End + +Public Sub _DefineRect() + 'If Me Is TermPictureBox Then Stop + + $rectClient = Rect($iPadding + $yBorder, $iPadding + $yBorder, $rectControl.Width - ($iPadding + $yBorder) * 2 - IIf($bShadow, 1, 0), $rectControl.Height - ($iPadding + $yBorder) * 2 - IIf($bShadow, 1, 0)) + +End + +Private Function _Border_Read() As Integer + + Return $iBorder + +End + +Private Sub _Border_Write(Value As Integer) + + $iBorder = Value + $yBorder = IIf($iBorder > Border.None, 1, 0) + Me.Refresh + +End + +Public Sub _GetClientRect() As Rect + ' If Me Is TermFrame Then Stop + '_DefineRect + + Return $rectClient.Copy() + +End + +Private Function _Padding_Read() As Integer + + Return $iPadding + +End + +Private Sub _Padding_Write(Value As Integer) + + $iPadding = Value + +End + +Public Sub _GetBackGround() As Integer + + Dim i As Integer + + If $iBackGround = -1 Then + If $iParent > 0 Then + i = Me.Parent._GetBackGround() + Return i + Else + Return TermColor.Background + Endif + Else + Return $iBackGround + Endif + +End + +Public Sub _GetForeground() As Integer + + Dim i As Integer + + If $iForeground = -1 Then + If $iParent > 0 Then + i = Me.Parent._GetForeground() + Return i + Else + Return TermColor.ForeGround + Endif + Else + Return $iForeground + Endif + +End + +Private Function _ColorMode_Read() As Integer + + Return $iColorMode + +End + +Private Sub _ColorMode_Write(Value As Integer) + + $iColorMode = Value + +End + +Public Sub _MouseDown() + + Dim hwindow As TermWindow + + If TermWindows.Debug = TermWindows.DebugInput Then Error "Mouse Down Event Raised on : " & Me.id & " Type: " & Object.Type(Me) + + If Not Me.Window.Active Then + Me.Window.Activate + Endif + Me.SetFocus + Raise MouseDown + +End + +Public Sub _MouseUp() + + If TermWindows.Debug = TermWindows.DebugInput Then Error "Mouse Up Event Raised on : " & Me.id & " Type: " & Object.Type(Me) + Raise MouseUp + +End + +Public Sub _MouseMove() + + If TermWindows.Debug = TermWindows.DebugInput Then Error "Mouse Move Event Raised on : " & Me.id & " Type: " & Object.Type(Me) + Raise MouseMove + +End + +Public Sub _MouseWheel() + + If TermWindows.Debug = TermWindows.DebugInput Then Error "Mouse Wheel Event Raised on : " & Me.id & " Type: " & Object.Type(Me) + Raise MouseWheel + +End + +Public Sub _Click() + + If TermWindows.Debug = TermWindows.DebugInput Then Error "Mouse Click Event Raised on : " & Me.id & " Type: " & Object.Type(Me) + Raise Click + +End + +Private Function Visible_Read() As Boolean + + Return $iVisible + +End + +Private Sub Visible_Write(Value As Boolean) + + If $iVisible <> Value Then + $iVisible = Value + TermWindows.SetRefreshArea($rectControl) + TermWindows._Render + Endif + +End + +Public Sub Show() + ' If Me Is Termform Then Stop + Me.Visible = True + +End + +Public Sub Hide() + + Me.Visible = False + +End + +Public Sub Resize(Width As Integer, Height As Integer) + + $bLockRender = True + Me.Width = Width + Me.Height = Height + $bLockRender = False + TermWindows._Render + +End + +Public Sub Refresh(Optional hScreenRect As Rect) + + Me._NeedRender = True + If hScreenRect Then + TermWindows.SetRenderArea(hScreenRect) + Else + TermWindows.SetRenderArea(Me._GetScreenRect()) + Endif + TermWindows._Render + +End + +Public Sub _KeyPress() + + Raise KeyPress + +End + +Private Function Next_Read() As TermControl + + Dim iPos As Integer + Dim hChildren As TermControl[] + + hChildren = Me.Parent.Children + ipos = hChildren.Find(Me) + If ipos = hChildren.Max Then Return Null + Return hChildren[iPos + 1] + +End + +Private Function Previous_Read() As TermControl + + Dim iPos As Integer + Dim hChildren As TermControl[] + + hChildren = Me.Parent.Children + iPos = hChildren.Find(Me) + If iPos = 0 Then Return Null + Return hChildren[iPos - 1] + +End + +Public Sub SetFocus() + + Dim hPrev As TermControl + + If Me.HaveFocus Then Return + If _AllowFocus Then + hPrev = TermControl._IdToControl[Me.Window._GetCurrentFocusId()] + hPrev._LostFocus + hPrev.Refresh + Me.Window._SetFocus(Me) + Me._GotFocus + Me.Refresh + Endif + +End + +Private Sub HaveFocus_Read() As Boolean + + Return $bHaveFocus + +End + +Public Sub _LostFocus() + + $bHaveFocus = False + + Raise LostFocus + +End + +Public Sub _GotFocus() + + $bHaveFocus = True + + Raise GotFocus + +End + +Private Function _DefaultBackground_Read() As Integer + + Return TermColor.Background + +End + +Private Function _DefaultForeGround_Read() As Integer + + Return TermColor.ForeGround + +End + +Private Function _ClientHeight_Read() As Integer + + Return $rectClient.Height + +End + +Private Function _ClientWidth_Read() As Integer + + Return $rectClient.Width + +End + +Private Function Ignore_Read() As Boolean + + Return $bIgnore + +End + +Private Sub Ignore_Write(Value As Boolean) + + $bIgnore = Value + +End diff --git a/comp/src/gb.term.form/.src/TermForm.class b/comp/src/gb.term.form/.src/TermForm.class new file mode 100644 index 00000000..d78296cf --- /dev/null +++ b/comp/src/gb.term.form/.src/TermForm.class @@ -0,0 +1,23 @@ +' Gambas class file + +Export +Inherits TermWindow +Public Const _Properties As String = "*,Border{Border.None;Simple;Double}=Simple,Arrangement{Arrange.None;Vertical;Horizontal}=None,Text,Resizable=True" +Public Const _HiddenControls As String = "TermControl,TermForm,TermWindow,TermContainer" +Public Const _IsForm As Boolean = True +Public Const _IsContainer As Boolean = True +Public Const _IsMultiContainer As Boolean = False +Public Const _DrawWith As String = "Form" +Public Const _DefaultEvent As String = "Open" + + +Static Public Sub Main() + + Dim hObject As TermForm + + hObject = Application.Startup.AutoCreate() + hObject.Show + + TermWindows._Render + +End diff --git a/comp/src/gb.term.form/.src/TermFrame.class b/comp/src/gb.term.form/.src/TermFrame.class new file mode 100644 index 00000000..0693b9d7 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermFrame.class @@ -0,0 +1,57 @@ +' Gambas class file + +Export +Inherits TermPanel +Class Rect + +Property Text As String +Private $sText As String +Private $iOldLen As Integer + +Public Sub _Render() + + Dim hAttr As New Attr + Dim iAttr As Integer + Dim i As Integer + Dim iStart As Integer + Dim hRectC As Rect = Me._GetClientRect() + Dim sText As String = " " & $sText & " " + Super._Render + If Not Me._NeedRender Then Return + + ' Select Case $iAlignment + ' Case Align.Right + ' sALign = String(hRectC.Width - Len($sText), " ") & $sText + ' Case Align.Center + iStart = CInt(Ceil((hRectC.Width - Len(sText)) / 2)) + ' Case Else + ' sALign = $sText + ' End Select + + hAttr.Background = Me._GetBackGround() + hAttr.Foreground = Me.Foreground + iAttr = hAttr.GetInteger() + + For i = iStart To Me._Content[0].count + If i - iStart >= String.Len(sText) Then Break + + Me._Content[0][i - 1] = Char(Mid(sText, i - iStart + 1, 1), iAttr) + + + Next + + Me._NeedRender = False + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End diff --git a/comp/src/gb.term.form/.src/TermHBox.class b/comp/src/gb.term.form/.src/TermHBox.class new file mode 100644 index 00000000..e03dfd03 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermHBox.class @@ -0,0 +1,9 @@ +' Gambas class file + +Export +Inherits TermContainer +Public Sub _new() + + Super._Arrangement = Arrange.Horizontal + +End diff --git a/comp/src/gb.term.form/.src/TermLabel.class b/comp/src/gb.term.form/.src/TermLabel.class new file mode 100644 index 00000000..8bff882a --- /dev/null +++ b/comp/src/gb.term.form/.src/TermLabel.class @@ -0,0 +1,73 @@ +' Gambas class file + +Export +Inherits TermControl + +Class Rect + +Public Const _Properties As String = "*,Text" +Public Const _DrawWith As String = "Label" + +Property Text As String + +Private $sText As String +Private $iOldLen As Integer + +Public Sub _new() + + Me._AllowFocus = False + +End + + + +Public Sub _Render() + Dim hAttr As New Attr + Dim iAttr As Integer + Dim i As Integer + If Not Me._NeedRender Then Return + Super._Render + If Me.HaveFocus Then + hAttr.Background = TermColor.Yellow + Else + hAttr.Background = Me._GetBackGround() + Endif + hAttr.Foreground = Me._GetForeground() + iAttr = hAttr.GetInteger() + + For i = 1 To Me._Content[0].count + If Me._Content[0][Me._Content[0].Max] And If i > Max($iOldLen, String.Len($sText)) Then Break + If i <= String.Len($sText) Then + Me._Content[0][i - 1] = Char(Mid($sText, i, 1), iAttr) + Else + Me._Content[0][i - 1] = Char(" ", iAttr) + Endif + Next + + Me._NeedRender = False +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + $iOldLen = String.len($sText) + $sText = Value + TermWindows.SetRenderArea(rect(Me.ScreenX, Me.ScreenY, Max($iOldLen, String.Len($sText)), Me.Height)) + Me._NeedRender = True + TermWindows._Render +End + + +Public Sub _GetForeground() As Integer + + If Me.Foreground = -1 Then + Return TermColor.ForeGround + Else + Return Me.Foreground + Endif + +End diff --git a/comp/src/gb.term.form/.src/TermListBox.class b/comp/src/gb.term.form/.src/TermListBox.class new file mode 100644 index 00000000..024fc06b --- /dev/null +++ b/comp/src/gb.term.form/.src/TermListBox.class @@ -0,0 +1,201 @@ +' Gambas class file + +Export +Inherits TermControl + +Property List As String[] +Property Index As Integer +Property Read Count As Integer +Property Read Text As String +Private $iIndex As Integer +Private $aList As New String[] +Private $iFirstVisible As Integer +Private $iPrevIndex As Integer +Event Activate +Event Change +Class Rect + +Private Function List_Read() As String[] + + Return $aList + +End + +Private Sub List_Write(Value As String[]) + + $aList = Value + +End + +Private Function Index_Read() As Integer + + Return $iIndex + +End + +Private Sub Index_Write(Value As Integer) + + Dim iNewIndex As Integer + iNewIndex = Min($aList.Max, Max(0, Value)) + If iNewIndex <> $iIndex Then + $iPrevIndex = $iIndex + $iIndex = iNewIndex + EnsureVisible + Endif + +End + +Private Function Count_Read() As Integer + + Return $aList.Count + +End + +Public Sub _Render() + + Dim hAttr As New Attr + Dim iAttr, iAttr2 As Integer + Dim i, il, k, ilen As Integer + Dim s As String + Dim hRectC As Rect = Me._GetClientRect() + Dim hRect As Rect = Me._GetClientRect() + Dim iCursorPos As Integer + + If Not Me._NeedRender Then Return + Super._Render + + hAttr.FillFrom(Me._Content[0][0].Attr) + + Swap hAttr.Background, hAttr.Foreground + iAttr = hAttr.GetInteger() + k = $iFirstVisible + For i = 0 To Me._Content.Max + If k > $aList.Max Then Break + s = $aList[k] + ilen = String.Len(s) + For il = 0 To IIf(k = $iIndex, hRect.width - 2, Min(hRect.width - 2, ilen)) + 'iAttr = Me._Content[i][il]. + If k = $iIndex Then Me._Content[i][il].Attr = iAttr + If il < ilen Then Me._Content[i][il].c = String.Code(String.Mid(s, il + 1, 1)) + Next + + Inc k + Next + + ilen = Me._Content[0].Max + 'Render the scrollbar + For i = 1 To Me._Content.Max - 2 + Me._Content[i][ilen].c = &H2591 + Next + Me._Content[0][ilen].c = &H25B2 + Me._Content[Me._Content.Max - 1][ilen].c = &H25BC + + hAttr.Foreground = TermColor.Black + iAttr = hAttr.GetInteger() + iCursorPos = ($iFirstVisible + hRect.height) / $aList.Count * (hRect.Height - 3) + Try Me._Content[iCursorPos][hRect.width - 1] = Char(String.Chr(&H2588), iAttr) + + Me._NeedRender = False +Fin: + +End + +Public Sub _KeyPress() + + Select Key.Code + Case Key.Up + Dec Me.Index + Raise Change + Case Key.Down + Inc Me.Index + Raise Change + Case Key.Return, Key.Enter + Raise Activate + + Default + + End Select + Super._KeyPress + +End + +Public Sub _MouseDown() + + Super._MouseDown + Me.Index = $iFirstVisible + Mouse.Row + Raise Change + 'EnsureVisible + +End + +Public Sub _MouseUp() + + Super._MouseUp + Raise Activate + Raise Click + +End + +Public Sub _MouseMove() + + Super._MouseMove + _MouseDown + +End + +Public Sub EnsureVisible() + + Dim hRect As Rect + Dim hRectSc As Rect + + If IsVisible($iIndex) Then + hRectSC = Me._GetScreenClientRect() + Me.Refresh(Rect(hRectSc.X, GetRowByIndex($iPrevIndex), hRectSc.Width, 1)) + Me.Refresh(Rect(hRectSc.X, GetRowByIndex($iIndex), hRectSc.Width, 1)) + Return + Endif + + hRect = Me._GetClientRect() + If $iFirstVisible + (hRect.Height - 1) < $iIndex Then + $iFirstVisible = $iIndex - hRect.Height + 1 + Endif + If $iIndex < $iFirstVisible Then + $iFirstVisible = $iIndex + Endif + Me.Refresh + +End + +Public Sub _put(Value As String, Index As Integer) + + Dim hRect As Rect = Me._GetScreenClientRect() + $aList[Index] = Value + If IsVisible(Index) Then + Me.Refresh(Rect(hRect.X, hRect.Y + GetRowByIndex(Index), hRect.Width, 1)) + Endif + +End + +Public Function _get(Index As Integer) As String + + Return $aList[Index] + +End + +Private Sub IsVisible(Index As Integer) As Boolean + + If Index >= $iFirstVisible And If Index < $iFirstVisible + Me._ClientHeight Then Return True + +End + +Private Function Text_Read() As String + + Return $aList[$iIndex] + +End + +Private Function GetRowByIndex(Index As Integer) As Integer + + Return Index - $iFirstVisible + +End diff --git a/comp/src/gb.term.form/.src/TermPanel.class b/comp/src/gb.term.form/.src/TermPanel.class new file mode 100644 index 00000000..47a4b240 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermPanel.class @@ -0,0 +1,23 @@ +' Gambas class file + +Export +Inherits TermContainer + +Property Arrangement As Integer + + + + +Private Function Arrangement_Read() As Integer + + Return Super._Arrangement + +End + +Private Sub Arrangement_Write(Value As Integer) + + Super._Arrangement = Value + +End + + diff --git a/comp/src/gb.term.form/.src/TermPictureBox.class b/comp/src/gb.term.form/.src/TermPictureBox.class new file mode 100644 index 00000000..bcb2f890 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermPictureBox.class @@ -0,0 +1,222 @@ +' Gambas class file + +Export +Inherits TermControl +Class Rect +Class Image +Class Color +Private $hImage As Image '= Image.Load("all.png") +Property Image As Image +Property Stretch As Integer +Property Alignment As Integer +Private $iAlignment As Integer +Private $iStretch As Integer +'None: Do nothing +'Proportional: Fit to one near border and adapt the other side +'Cut/Zoom: Adapt to the far border and cut the rest +'Fit: use the PictureBox content size +'If Picture box.None is not set the the ALignment property is set to centered +Public Enum None, Proportional, Cut, Fit +Private $aTermColor As Integer[] = [ + &H000000, &H800000, &H008000, &H808000, &H000080, &H800080, &H008080, &Hc0c0c0, + &H808080, &Hff0000, &H00ff00, &Hffff00, &H0000ff, &Hff00ff, &H00ffff, &Hffffff, + &H000000, &H00005f, &H000087, &H0000af, &H0000d7, &H0000ff, + &H005f00, &H005f5f, &H005f87, &H005faf, &H005fd7, &H005fff, + &H008700, &H00875f, &H008787, &H0087af, &H0087d7, &H0087ff, + &H00af00, &H00af5f, &H00af87, &H00afaf, &H00afd7, &H00afff, + &H00d700, &H00d75f, &H00d787, &H00d7af, &H00d7d7, &H00d7ff, + &H00ff00, &H00ff5f, &H00ff87, &H00ffaf, &H00ffd7, &H00ffff, + &H5f0000, &H5f005f, &H5f0087, &H5f00af, &H5f00d7, &H5f00ff, + &H5f5f00, &H5f5f5f, &H5f5f87, &H5f5faf, &H5f5fd7, &H5f5fff, + &H5f8700, &H5f875f, &H5f8787, &H5f87af, &H5f87d7, &H5f87ff, + &H5faf00, &H5faf5f, &H5faf87, &H5fafaf, &H5fafd7, &H5fafff, + &H5fd700, &H5fd75f, &H5fd787, &H5fd7af, &H5fd7d7, &H5fd7ff, + &H5fff00, &H5fff5f, &H5fff87, &H5fffaf, &H5fffd7, &H5fffff, + &H870000, &H87005f, &H870087, &H8700af, &H8700d7, &H8700ff, + &H875f00, &H875f5f, &H875f87, &H875faf, &H875fd7, &H875fff, + &H878700, &H87875f, &H878787, &H8787af, &H8787d7, &H8787ff, + &H87af00, &H87af5f, &H87af87, &H87afaf, &H87afd7, &H87afff, + &H87d700, &H87d75f, &H87d787, &H87d7af, &H87d7d7, &H87d7ff, + &H87ff00, &H87ff5f, &H87ff87, &H87ffaf, &H87ffd7, &H87ffff, + &Haf0000, &Haf005f, &Haf0087, &Haf00af, &Haf00d7, &Haf00ff, + &Haf5f00, &Haf5f5f, &Haf5f87, &Haf5faf, &Haf5fd7, &Haf5fff, + &Haf8700, &Haf875f, &Haf8787, &Haf87af, &Haf87d7, &Haf87ff, + &Hafaf00, &Hafaf5f, &Hafaf87, &Hafafaf, &Hafafd7, &Hafafff, + &Hafd700, &Hafd75f, &Hafd787, &Hafd7af, &Hafd7d7, &Hafd7ff, + &Hafff00, &Hafff5f, &Hafff87, &Hafffaf, &Hafffd7, &Hafffff, + &Hd70000, &Hd7005f, &Hd70087, &Hd700af, &Hd700d7, &Hd700ff, + &Hd75f00, &Hd75f5f, &Hd75f87, &Hd75faf, &Hd75fd7, &Hd75fff, + &Hd78700, &Hd7875f, &Hd78787, &Hd787af, &Hd787d7, &Hd787ff, + &Hd7af00, &Hd7af5f, &Hd7af87, &Hd7afaf, &Hd7afd7, &Hd7afff, + &Hd7d700, &Hd7d75f, &Hd7d787, &Hd7d7af, &Hd7d7d7, &Hd7d7ff, + &Hd7ff00, &Hd7ff5f, &Hd7ff87, &Hd7ffaf, &Hd7ffd7, &Hd7ffff, + &Hff0000, &Hff005f, &Hff0087, &Hff00af, &Hff00d7, &Hff00ff, + &Hff5f00, &Hff5f5f, &Hff5f87, &Hff5faf, &Hff5fd7, &Hff5fff, + &Hff8700, &Hff875f, &Hff8787, &Hff87af, &Hff87d7, &Hff87ff, + &Hffaf00, &Hffaf5f, &Hffaf87, &Hffafaf, &Hffafd7, &Hffafff, + &Hffd700, &Hffd75f, &Hffd787, &Hffd7af, &Hffd7d7, &Hffd7ff, + &Hffff00, &Hffff5f, &Hffff87, &Hffffaf, &Hffffd7, &Hffffff, + &H080808, &H121212, &H1c1c1c, &H262626, &H303030, &H3a3a3a, + &H444444, &H4e4e4e, &H585858, &H606060, &H666666, &H767676, + &H808080, &H8a8a8a, &H949494, &H9e9e9e, &Ha8a8a8, &Hb2b2b2, + &Hbcbcbc, &Hc6c6c6, &Hd0d0d0, &Hdadada, &He4e4e4, &Heeeeee] +'Private $aaImage As New Byte[][] +Static Public Sub _init() + + ' Component.Load(Component.Path &/ "gb.Image") + ' Component.Load(Component.Path &/ "gb.Image.io") + + +End + + + + + +Public Sub _new() + + ' Dim aLine As Byte[] + ' Dim yPix As Byte + ' Dim y As Integer + ' Dim x As Integer + ' + ' $hImage = $hImage.Stretch(150, 100) + ' + ' For y = 0 To $hImage.Height + ' aline = New Byte[] + ' For x = 0 To $hImage.Width + ' aLine.Add(ConvTermColor(Color.SetAlpha($hImage[x, y], 0))) + ' Next + ' $aaImage.Add(aline) + ' Next + ' + ' + ' _Render + Me._ColorMode = TermColor.Mode256 + +End + +Private Function Image_Read() As Image + + Return $hImage + +End + +Private Sub Image_Write(Value As Image) + + $hImage = Value + +End + +Public Sub _Render() + + Dim img As Image + Dim x, y As Integer + Dim fg, bg As Integer + Dim hattr As New Attr + Dim hClientRect As Rect + Dim hChar As Char + Dim hRect As Rect = Me._GetRect() + + If Not Me._NeedRender Then Return + Super._Render + If $hImage = Null Then Return + 'Return + hClientRect = Me._GetClientRect() + 'img2 = $hImage.Copy() + 'hattr.ColorMode = TermColor.Mode256 + Select Case $iStretch + Case TermPictureBox.Cut + Case TermPictureBox.Fit + img = $hImage.Stretch(hClientRect.Width, (hClientRect.Height) * 2) + + Case TermPictureBox.Proportional + Case Else + 'AKA None + img = $hImage.Copy() + End Select + + Select Case $iAlignment + Case Align.Right + Case Align.Center + Case Else + 'AKA ALign.Left + End Select + + For y = 0 To img.Height - 1 Step 2 + For x = 0 To img.Width - 1 + hChar = New Char + + fg = ConvTermColor(Color.SetAlpha(img[x, y], 0)) + bg = ConvTermColor(Color.SetAlpha(img[x, y + 1], 0)) + hattr.ColorMode = TermColor.Mode256 + hattr.Foreground = fg + hattr.Background = bg + hChar.Attr = hattr.GetInteger() + hChar.c = &H2580 + 'Try + Me._Content[hClientRect.Top + y / 2][hClientRect.Left + x] = hChar + Next + Next + + 'Print + ' For y = 0 To $aaImage.Max Step 2 + ' For x = 0 To $aaImage[0].Max + ' fg = $aaImage[y][x] + ' Try bg = $aaImage[y + 1][x] + ' hattr.Foreground = fg + ' hattr.Background = bg + ' hattr.Send() + ' Print String.Chr(&H2580); + ' Next + ' Print + ' + ' + ' + ' + ' Next + Me._NeedRender = False + +End + +Private Function ConvTermColor(iTermColor As Integer) As Integer + + Dim i As Integer + Dim fMax As Float = 1 + Dim fCur As Float + Dim iCur As Integer + + For i = 0 To $aTermColor.Max + fcur = Color.Distance($aTermColor[i], iTermColor) + If fcur < fMax Then + fMax = fcur + iCur = i + Endif + Next + Return iCur + +End + +Private Function Stretch_Read() As Integer + + Return $iStretch + +End + +Private Sub Stretch_Write(Value As Integer) + + $iStretch = Value + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + +End diff --git a/comp/src/gb.term.form/.src/TermRadioButton.class b/comp/src/gb.term.form/.src/TermRadioButton.class new file mode 100644 index 00000000..6b7e7a75 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermRadioButton.class @@ -0,0 +1,112 @@ +' Gambas class file + +Export +Inherits TermControl +Class Rect +Property Text As String +Property Value As Boolean + + +Private $sText As String +Private $iOldLen As Integer +Private $bValue As Integer + + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + +End + +Public Sub _Render() + + Dim hAttr As New Attr + Dim iAttr As Integer + Dim i As Integer + Dim sALign As String + Dim hRectC As Rect = Me._GetClientRect() + Dim sValue As String + Dim iBC As Integer + + If Not Me._NeedRender Then Return + Super._Render + + sValue = IIf($bValue, "*", " ") + + sALign = "(" & sValue & ") " & $sText + + iBC = Me._GetBackGround() + If Me.HaveFocus Then iBC = TermColor.Focus + hAttr.Background = iBC + hAttr.Foreground = Me.Foreground + iAttr = hAttr.GetInteger() + + For i = 1 To Me._Content[0].count + If Me._Content[0][Me._Content[0].Max] And If i > Max($iOldLen, String.Len(sALign)) Then Break + If i <= String.Len(sALign) Then + Me._Content[0][i - 1] = Char(Mid(sALign, i, 1), iAttr) + Else + Me._Content[0][i - 1] = Char(" ", iAttr) + Endif + Next + + Me._NeedRender = False + +End + +Private Function Value_Read() As Boolean + + Return $bValue + +End + +Private Sub Value_Write(Value As Boolean) + If $bValue = Value Then Return + If Value Then SetChecked + $bValue = Value + Me.Refresh + '$iValue = Max(Min(Value, 1), -1) +End + +Private Sub SetChecked() + + Dim hParent As TermContainer = Me.Parent + Dim hCtrl As Object + + For Each hCtrl In hParent.Children + If hCtrl Is TermRadioButton Then + If hCtrl.Value Then hCtrl.Value = False + Endif + Next + +End + + + + + +Public Sub _MouseUp() + + Me.Value = True + Super._MouseUp + Super._Click + 'Me._NeedRender = True + 'Me.Refresh +End + + + +Public Sub _KeyPress() + + If Key.Text = " " Then + Me.Value = True + Endif + Super._Click + Super._KeyPress +End diff --git a/comp/src/gb.term.form/.src/TermScrollBar.class b/comp/src/gb.term.form/.src/TermScrollBar.class new file mode 100644 index 00000000..cd18cc77 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermScrollBar.class @@ -0,0 +1,173 @@ +' Gambas class file + +Export +Inherits TermControl + +Property MinValue As Integer +Property MaxValue As Integer +Property Value As Integer + +Private $iMaxValue As Integer = 5 +Private $iMinValue As Integer +Private $iValue As Integer +Private $bInCursor As Boolean +Private $iCursorPos As Integer + +Event Change + +Public Sub _new() + + Me.MaxValue = 5 + Me.Value = 5 + +End + +Private Function MinValue_Read() As Integer + + Return $iMinValue + +End + +Private Sub MinValue_Write(Value As Integer) + + $iMinValue = Max(Value, 0) + Me.Refresh + +End + +Private Function MaxValue_Read() As Integer + + Return $iMaxValue + +End + +Private Sub MaxValue_Write(Value As Integer) + + $iMaxValue = Value + Me.Refresh + +End + +Private Function Value_Read() As Integer + + Return $iValue + +End + +Private Sub Value_Write(Value As Integer) + + $iValue = Max(Min($iMaxValue, Value), $iMinValue) + Me.Refresh + Raise Change + +End + +Public Sub _Render() + + Dim hAttr As New Attr + Dim iAttr As Integer + Dim i As Integer + Dim iHeight As Integer + + If Not Me._NeedRender Then Return + + Super._Render + + If Me.HaveFocus Then + hAttr.Background = TermColor.Yellow + Else + hAttr.Background = Me._GetBackGround() + Endif + + hAttr.Foreground = Me._GetForeground() + iAttr = hAttr.GetInteger() + + If Me.Height >= Me.Width Then + iHeight = Me.Height - 3 + + For i = 1 To Me._Content.Count - 3 + Me._Content[i][0] = Char(String.Chr(&H2591), iAttr) + + Next + + Me._Content[0][0] = Char(String.Chr(&H25B2), iAttr) + Me._Content[Me._Content.Count - 2][0] = Char(String.Chr(&H25BC), iAttr) + + If Me.MaxValue > 0 Then + $iCursorPos = Ceil($iValue / ($iMaxValue - $iMinValue) * iHeight) + 1 + Me._Content[$iCursorPos][0] = Char(String.Chr(&H2588), iAttr) + Endif + + Else + iHeight = Me.Width - 3 + + For i = 1 To Me._Content[0].max - 1 + + Me._Content[0][i] = Char(String.Chr(&H2591), iAttr) + + Next + + Me._Content[0][0] = Char(String.Chr(&H25C0), iAttr) + Me._Content[0][Me._Content[0].Max] = Char(String.Chr(&H25BA), iAttr) + + If Me.MaxValue > 0 Then + $iCursorPos = Ceil($iValue / ($iMaxValue - $iMinValue) * iHeight) + 1 + Me._Content[0][$iCursorPos] = Char(String.Chr(&H2588), iAttr) + Endif + + Endif + Me._NeedRender = False + +End + +Public Sub _MouseDown() + + If Me.Height > Me.Width Then + If Mouse.Row = 0 Then + Dec Me.Value + Else + If Mouse.Row = Me.Height - 1 Then + + Inc Me.Value + Else + If Mouse.Row = $iCursorPos Then $bInCursor = True + + Endif + Endif + Else + If Mouse.Col = 0 Then + Dec Me.Value + Else + If Mouse.Col = Me.Width - 1 Then + Inc Me.Value + Else + If Mouse.Col = $iCursorPos Then $bInCursor = True + Endif + Endif + + Endif + Super._MouseDown + +End + +Public Sub _MouseUp() + + $bInCursor = False + Super._MouseUp + +End + +Public Sub _MouseMove() + + Dim iHeight As Integer + + If Me.Height > Me.Width Then + + Me.Value = (Mouse.Row - 1) / (Me.Height - 3) * ($iMaxValue - $iMinValue) + Else + Me.Value = (Mouse.Col - 1) / (Me.Width - 3) * ($iMaxValue - $iMinValue) + Endif + + Super._MouseMove + +End diff --git a/comp/src/gb.term.form/.src/TermTextBox.class b/comp/src/gb.term.form/.src/TermTextBox.class new file mode 100644 index 00000000..9df4c3e6 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermTextBox.class @@ -0,0 +1,187 @@ +' Gambas class file + +Export +Inherits TermControl + +Class Rect + +Public Const _DrawWith As String = "TextBox" + +Property Text As String + +Private $sText As String +Private $iOldLen As Integer +Property Height As Integer +Private $iCursor As Integer +Private $iLenText As Integer + +Event Change +Event Activate + +Public Sub _new() + + Me._AllowFocus = True + 'Me.Height = 1 + +End + +Public Sub _Render() + + Dim hAttr As New Attr + Dim iAttr As Integer + Dim i As Integer + + If Not Me._NeedRender Then Return + Super._Render + If Me.HaveFocus Then + hAttr.Background = TermColor.Yellow + Else + hAttr.Background = Me._GetBackGround() + Endif + hAttr.Foreground = Me._GetForeground() + iAttr = hAttr.GetInteger() + + For i = 1 To Me._Content[0].count + If Me._Content[0][Me._Content[0].Max] And If i > Max($iOldLen, $iLenText) Then Break + If i <= $iLenText Then + Me._Content[0][i - 1] = Char(Mid($sText, i, 1), iAttr) + Else + Me._Content[0][i - 1] = Char(" ", iAttr) + Endif + Next + If Me.HaveFocus Then + hAttr.FillFrom(iAttr) + hAttr.Blink = True + If $iCursor < Me.Width Then + If $iCursor >= $iLenText Then + Me._Content[0][$iLenText] = Char(String.Chr(&H258F), hAttr.GetInteger()) + hAttr.Blink = False + Me._Content[0][$iLenText + 1] = Char(" ", hAttr.GetInteger()) + Else + Swap hAttr.Background, hAttr.Foreground + Me._Content[0][$iCursor].Attr = hAttr.GetInteger() + Endif + Endif + 'Me._Content[0][string.Len($sText)].Attr = hAttr.GetInteger() + Endif + Me._NeedRender = False + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $iOldLen = String.len($sText) + $sText = Value + $iLenText = String.Len(Value) + RefreshText + ' TermWindows.SetRenderArea(rect(Me.ScreenX, Me.ScreenY, Max($iOldLen + 1, $iLenText), Me.Height)) + ' Me._NeedRender = True + ' Termwindows._Render() + +End + +Public Sub _KeyPress() + Dim sText As String = Me.Text + Select Case Key.Code + Case Key.BackSpace + '$iOldLen = $iLenText + If $iCursor >= $iLenText Then + sText = Left(sText, -1) + Else + If $iCursor = 0 Then + + Else + Mid(sText, $iCursor, 1) = "" + Endif + Endif + '$iLenText = String.Len($sText) + + $iCursor = Max(0, Min($iLenText, $iCursor - 1)) + Me.Text = sText + Raise Change + Case Key.Left + $iCursor = Max(0, Min($iLenText, $iCursor - 1)) + RefreshText + Case Key.Right + $iCursor = Max(0, Min($iLenText, $iCursor + 1)) + RefreshText + Case Key.Home + $iCursor = 0 + RefreshText + Case Key.End + $iCursor = $iLenText + RefreshText + Case Key.Return + Raise Activate + Case Else + If $iCursor >= $iLenText Then + sText &= Key.Text + Else + If $iCursor = 0 Then + sText = Key.Text & sText + Else + sText = Left(sText, $iCursor) & Key.Text & Right(sText, - $iCursor) + Endif + Endif + Inc $iCursor + Me.Text = sText + Raise Change + End Select + Super._KeyPress + +End + +Private Sub RefreshText() + + TermWindows.SetRenderArea(rect(Me.ScreenX, Me.ScreenY, Max($iOldLen + 1, $iLenText + 1), Me.Height)) + Me._NeedRender = True + Termwindows._Render() + +End + +Private Function Height_Read() As Integer + + Return Super.Height + +End + +Private Sub Height_Write(Value As Integer) + +End + +Public Sub _GetBackGround() As Integer + + If Me.Background = -1 Then + Return TermColor.TextBackground + Else + Return Me.Background + Endif + +End + +Public Sub _GetForeGround() As Integer + + If Me.Foreground = -1 Then + Return TermColor.TextForeground + Else + Return Me.Foreground + Endif + +End + +Public Sub _MouseDown() + Dim iPos As Integer = Min(Mouse.x, String.Len($sText)) + If iPos <> $iCursor Then + + $iCursor = iPos + RefreshText + Endif + Super._MouseDown + +End diff --git a/comp/src/gb.term.form/.src/TermVBox.class b/comp/src/gb.term.form/.src/TermVBox.class new file mode 100644 index 00000000..cd221cf1 --- /dev/null +++ b/comp/src/gb.term.form/.src/TermVBox.class @@ -0,0 +1,11 @@ +' Gambas class file + +Export +Inherits TermContainer +Public Sub _new() + + Super._Arrangement = Arrange.Vertical + 'Super.Border = 1 + 'Super._Shadow = True + 'Super.Padding = 1 +End diff --git a/comp/src/gb.term.form/.src/TermWindow.class b/comp/src/gb.term.form/.src/TermWindow.class new file mode 100644 index 00000000..dbd0ea7c --- /dev/null +++ b/comp/src/gb.term.form/.src/TermWindow.class @@ -0,0 +1,577 @@ +' Gambas class file + +Create Static +Export +Inherits TermContainer +Class Rect +Private $sText As String +Property Arrangement As Integer +Property Text, Title As String +Property Maximized As Boolean +Property Read Active As Boolean +Property Resizable As Boolean +Property Visible As Boolean +Property Left, X, Column As Integer +Property Top, Y, {Line} As Integer +Property Width, W As Integer +Property Height, H As Integer +Property Persistent As Boolean +Private $bOpen As Boolean +Private $rectOldPos As Rect +Private $bMaximized As Boolean +Private $iInMove As Integer +Private $tmrMove As Timer +Private $iTmpCol As Integer +Private $iTmpRow As Integer +Private $bResizable As Boolean +Private $bInResize As Boolean +Private $iFocusedId As Integer +Private $bPersistent As Boolean +Property Read ScreenY As Integer +Property Read ScreenX As Integer + +Public _IsModal As Boolean + +Event Close +Event Open + +Public Sub _new() + + Me.Visible = False + $iFocusedId = Me.id + Object.Attach(Me, Me, "TermForm") + +End + +' Static Public Sub Main() +' +' Dim hObject As Object +' +' hObject = Application.Startup.AutoCreate() +' 'If Not hObject Is Report Then Return +' 'FPreview.Run(hObject) +' TermWindows._Render +' +' End + +Public Sub _Render() + + Dim i As Integer + Dim hAttr As New Attr + Dim iAttr As Integer + Dim b As Boolean = Me._NeedRender + + Super._Render + If Not b Then Return + + 'Render TitleBar + If Me.Border > 0 Then + hAttr.Background = IIf(Me.Active, TermColor.ActiveWindow, TermColor.InactiveWindow) + hAttr.Foreground = TermColor.White + hAttr.Bold = True + iAttr = hAttr.GetInteger() + + For i = 0 To Me.Width - 1 '- 2 + Me._Content[0][i] = Char(" ", iAttr) + Next + + Me._Content[0][0].c = Asc("[") + Me._Content[0][1].c = Asc("]") + + For i = 1 To Min(Me._Content[0].Max, String.Len($sText)) - 2 + Me._Content[0][i + 2].c = Asc(Mid($sText, i, 1)) + Next + Me._Content[0][Me._Content[0].Max].c = Asc("X") + Endif + Me._NeedRender = False + + If Me.Resizable Then + hAttr.Background = Me.Background + hAttr.Foreground = Me.Foreground + hAttr.Bold = False + iAttr = hAttr.GetInteger() + + If Not $bMaximized Then Me._Content[Me._Content.Max - 1][Me._Content[0].Max].c = &h25E2 + Endif + 'Catch + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + Dim bNeedLayout As Boolean + + If Len($sText) > 0 And Value = "" Then bNeedLayout = True + If $sText = "" And Value <> "" Then bNeedLayout = True + $sText = Value + Me._NeedRender = True + If bNeedLayout Then + TermWindows.SetRefreshArea(Rect(Me.ScreenX, Me.ScreenY, Me.Width, Me.Height)) + Else + TermWindows.SetRenderArea(Rect(Me.ScreenX, Me.ScreenY, Me.Width, 1)) + Endif + TermWindows._Render + +End + +Public Sub _Arrange() + + 'Me._GetClientRect().Move(0, 1, Me.W - 1, Me.Height - 1) + Super._Arrange + +End + +Private Function Maximized_Read() As Boolean + + Return $bMaximized + +End + +Private Sub Maximized_Write(Value As Boolean) + + If $bMaximized = Value And If $bMaximized Then + $bMaximized = False + Me.Move(0, 0, Desktop.Width, Desktop.Height) + $bMaximized = True + Return + Endif + + If Value Then + $rectOldPos = Me._GetRect() + Me.Move(0, 0, Desktop.Width, Desktop.Height) + $bMaximized = Value + Else + If $bMaximized <> value Then + $bMaximized = Value + Me.Move($rectOldPos.Left, $rectOldPos.Top, $rectOldPos.Width, $rectOldPos.Height) + Endif + Endif + +End + +Public Sub _ScreenResize() + + Me.Maximized = $bMaximized + ' If $bMaximized Then + ' Me.Move(0, 0, Desktop.Width, Desktop.Height) + ' + ' Endif + +End + +Public Sub Center() + + Me.Move((Desktop.Width - Me.Width) / 2, (Desktop.Height - Me.Height) / 2, Me.Width, Me.Height) + +End + +Public Sub Close() + + Dim hCont As TermControl + + Me.Visible = False + Raise Close + + If Not Me.Persistent Then + TermWindows.Delete(Me) + Endif + +End + +Public Sub _MouseUp() + + If Mouse.ScreenY = Me.ScreenY Then + If Mouse.ScreenX = Me.ScreenX + Me.Width - 1 Then + Me.Close + Return + Endif + If Mouse.X >= 0 And If Mouse.x <= 1 Then + Me.Maximized = Not Me.Maximized + Endif + Endif + $tmrMove = Null + $iInMove = 0 + $bInResize = False + Super._MouseUp + +End + +Public Sub _MouseDown() + + If Mouse.Left + If Not Me.Active Then Termwindows._RaiseWindow(Me) + If Mouse.y = 0 Then + $iInMove = Mouse.Col + $tmrMove = New Timer As "_tmrMove" + $tmrMove.Delay = 100 + Endif + If Mouse.y = Me.Height - 1 And Mouse.x = Me.Width - 1 Then + If Me.Resizable And If Not Me.Maximized Then + $tmrMove = New Timer As "_tmrMove" + $tmrMove.Delay = 100 + $bInResize = True + Endif + Endif + Endif + Super._MouseDown + +End + +Public Sub _MouseMove() + + If $iInMove > 0 Then + $iTmpCol = Mouse.ScreenCol - $iInMove + $iTmpRow = Mouse.ScreenRow + $tmrMove.Start + 'Me.Move(Mouse.ScreenX - $iInMove, Mouse.screeny) + Endif + + If $bInResize Then + $iTmpCol = Mouse.ScreenCol - Me.ScreenX + $iTmpRow = Mouse.ScreenRow - Me.ScreenY + $tmrMove.Start + Endif + Super._MouseMove + +End + +Private Function Active_Read() As Boolean + + Return TermWindows.GetActiveWindow() = Me + +End + +Public Sub _tmrMove_Timer() + + If $bInResize Then + Me.Move(Me.Left, Me.Top, $iTmpCol, $iTmpRow) + $tmrMove.Stop + Else + Me.Move($iTmpCol, $iTmpRow) + $tmrMove.Stop + Endif + +End + +Public Sub Activate() + + TermWindows._RaiseWindow(Me) + +End + +Private Function Resizable_Read() As Boolean + + Return $bResizable + +End + +Private Sub Resizable_Write(Value As Boolean) + + $bResizable = Value + +End + +Private Function Top_Read() As Integer + + Return Super.Top + +End + +Private Sub Top_Write(Value As Integer) + + If $bMaximized Then Return + Super.Top = Value + +End + +Private Function Left_Read() As Integer + + Return Super.Left + +End + +Private Sub Left_Write(Value As Integer) + + If $bMaximized Then Return + Super.Left = Value + +End + +Private Function Height_Read() As Integer + + Return Super.H + +End + +Private Sub Height_Write(Value As Integer) + + If $bMaximized Then Return + If value < 3 Then Return + Super.H = Value + +End + +Private Function Width_Read() As Integer + + Return Super.W + +End + +Private Sub Width_Write(Value As Integer) + + If $bMaximized Then Return + If value < 5 Then Return + Super.Width = Value + +End + +Public Sub Move(Left As Integer, Top As Integer, Optional Width As Integer, Height As Integer) + + If $bMaximized Then Return + Super.Move(Left, Top, Width, Height) + +End + +Public Sub Resize(Width As Integer, Height As Integer) + + If $bMaximized Then Return + Super.Resize(Width, Height) + +End + +Public Sub GetNextFocusedControl(hCtrl As TermControl, Optional bFirst As Boolean) As TermControl + + Dim hNext As Object + Dim hParent As TermContainer + Dim aChildren As TermControl[] + Dim i As Integer + 'On stock le control passé en argument + Dim hFirst As TermControl = hCtrl + Dim hCont As TermContainer + + hCont = hCtrl.Parent + If Not hCont Then Return Null + 'Trouver le parent du control + aChildren = hCont.Children + 'et la position du control + i = aChildren.Find(hCtrl) + + Do + 'Il n 'y a pas d'enfant a suivre + If i = aChildren.Max Then + 'le control a t'il un parent ? + hParent = aChildren[i].Parent.Parent + If hParent Then + hNext = Null + If hParent.Children.Count > 0 Then hNext = GetNextFocusedControl(hParent.Children[hParent.Children.Find(aChildren[i].Parent)]) + If hNext Then Return hNext + Endif + i = 0 + Else + 'On passe a l'enfant suivant + If Not bFirst Then Inc i + + Endif + + hNext = aChildren[i] + 'Si on reviens au point de départ c'est qu'on a rien trouve + If Not bFirst And If hNext = hFirst Then Return Null + bFirst = False + 'si c'est un conteneur + If hNext Is TermContainer Then + 'parcourir le conteneur + hCtrl = Null + 'si le conteneur a des enfants + If hNext.Children.Count > 0 Then hCtrl = GetNextFocusedControl(hNext.Children[0], True) + 'si on a trouve on Return l'enfant + If hCtrl Then Return hCtrl + 'sinon on passe a l'enfant suivant en repartant de l'enfant courant + Continue + 'si c'est pas un conteneur + Else + 'on verifie si le control accepte le focus + 'si oui on le Return + If hNext._AllowFocus Then Return hNext + 'sinon on passe au control suivant + Continue + Endif + + Loop + +End + +Public Sub GetPrevFocusedControl(hCtrl As TermControl, Optional bFirst As Boolean) As TermControl + + Dim hNext As Object + Dim hParent As TermContainer + Dim aChildren As TermControl[] + Dim i As Integer + 'On stock le control passé en argument + Dim hFirst As TermControl = hCtrl + 'Trouver le parent du control + aChildren = hCtrl.Parent.Children + 'et la position du control + i = aChildren.Find(hCtrl) + + Do + If i = 0 Then + 'le control a t'il un parent ? + hParent = aChildren[i].Parent.Parent + If hParent Then + hNext = Null + If hParent.Children.Count > 0 Then hNext = GetNextFocusedControl(hParent.Children[hParent.Children.Find(aChildren[i].Parent)]) + If hNext Then Return hNext + Endif + i = aChildren.Max + Else + 'On passe a l'enfant suivant + If Not bFirst Then Dec i + + Endif + + hNext = aChildren[i] + 'Si on reviens au point de départ c'est qu'on a rien trouve + If Not bFirst And If hNext = hFirst Then Return Null + + bFirst = False + 'si c'est un conteneur + If hNext Is TermContainer Then + 'parcourir le conteneur + hCtrl = Null + 'si le conteneur a des enfants + If hNext.Children.Count > 0 Then hCtrl = GetNextFocusedControl(hNext.Children[hNext.Children.Max], True) + 'si on a trouve on Return l'enfant + If hCtrl Then Return hCtrl + 'sinon on passe a l'enfant suivant en repartant de l'enfant courant + Continue + 'si c'est pas un conteneur + Else + 'on verifie si le control accepte le focus + 'si oui on le Return + If hNext._AllowFocus Then Return hNext + 'sinon on passe au control suivant + Continue + Endif + + Loop + +End + +Public Sub _SetFocus(hCtrl As TermControl) + + Dim hPrev As TermControl + + hPrev = TermControl._IdToControl[$iFocusedId] + If hPrev Then + hPrev._LostFocus + hPrev.Refresh + Endif + + $iFocusedId = hCtrl.Id + +End + +Public Function _GetCurrentFocusId() As Integer + + Return $iFocusedId + +End + +Public Sub _KeyPress() + + Dim hCtrl As TermControl + + If Key.Code = Key.Tab Then + hCtrl = GetNextFocusedControl(TermControl._IdToControl[$iFocusedId]) + If hCtrl Then hCtrl.SetFocus + Return + Endif + If Key.Code = Key.BackTab Then + GetPrevFocusedControl(TermControl._IdToControl[$iFocusedId]).SetFocus + Return + Endif + Super._KeyPress + hCtrl = TermControl._IdToControl[$iFocusedId] + If hCtrl <> Me Then hCtrl._KeyPress + +End + +Private Function Persistent_Read() As Boolean + + Return $bPersistent + +End + +Private Sub Persistent_Write(Value As Boolean) + + $bPersistent = Value + +End + +Public Sub _GetClientRect() As Rect + ' If Me Is TermFrame Then Stop + '_DefineRect + + Dim hRect As Rect = Super._GetClientRect() + + If Me.Border < 1 And Me.Title <> "" Then + hRect.y += 1 + hRect.Height -= 1 + Endif + Return hRect + +End + +Private Function Visible_Read() As Boolean + + Return Super.Visible + +End + +Private Sub Visible_Write(Value As Boolean) + + If Value And If Not $bOpen Then + Raise Open + $bOpen = True + Endif + + Super.Visible = Value + +End + +Private Function ScreenY_Read() As Integer + + Return Me.Top + +End + +Private Function ScreenX_Read() As Integer + + Return Me.Left + +End + +Public Sub ShowModal() + + Me._IsModal = True + Me.Show + +End + +Private Function Arrangement_Read() As Integer + + Return Me._Arrangement + +End + +Private Sub Arrangement_Write(Value As Integer) + + Me._Arrangement = Value + +End diff --git a/comp/src/gb.term.form/.src/TermWindows.class b/comp/src/gb.term.form/.src/TermWindows.class new file mode 100644 index 00000000..a60f1f1e --- /dev/null +++ b/comp/src/gb.term.form/.src/TermWindows.class @@ -0,0 +1,715 @@ +' Gambas class file + +Export +Create Static +Class Rect +Static Private Obs As Observer +Static Private $aScreen As New Integer[] +Static $hFile As File +Static Private $RectRefreshArea As Rect +Static Private $aChildren As New TermWindow[] +Static Private $hRect As Rect +Static Private $RectRenderArea As Rect +Static Public {Debug} As Integer +Static Private $Index As Integer +Static Private $bThisIsTheEnd As Boolean +Static Private $bInRender As Boolean +Static Private $hCurControl As TermControl +Static Private $iActiveWindow As Integer +Static Public bLock As Boolean +Static Private $hStream As File + +Public Enum DebugNone, DebugInput, DebugOutput + +Static Public Sub _init() + + Dim hRect As Rect + Dim hSetting As TerminalSettings + + Component.Load("gb.geom") + + hRect = New Rect(0, 0, 3, 3) + + $hFile = Open File.In.Term.Name For Read Watch + obs = New Observer(File.In) As "Terminal" + + hSetting = File.In.Term.GetAttr() + 'hSetting.ICANON = False + 'hSetting.ECHO = False + hSetting.MakeRaw + File.In.Term.SetAttr(Term.TCSANOW, hSetting) + 'saveSetting(hSetting) + Print "\e[?1002h\e[?1006h\e[?1049h\e[?25l\e[?2h"; + Print "\e[8;30;200"; + ResizeScreen(File.Out.Term.Width, File.Out.Term.Height) + +End + +Static Private Sub ResizeScreen(Width As Integer, Height As Integer) + + $aScreen = New Integer[Height, Width] + $hRect = Rect(0, 0, Width, Height) + $RectRefreshArea = $hRect.Copy() + +End + +Public Sub _Add(hWin As TermWindow) + + Dim i As Integer + Dim hCurACtive As TermWindow + + + hCurActive = TermControl._IdToControl[$iActiveWindow] + If hCurACtive Then + hCurACtive._NeedRender = True + SetRenderArea(hCurACtive._getrect()) + Endif + $aChildren.Add(hWIn) + $iActiveWindow = hWin.id + + ' For i = 0 To $aChildren.Max - 1 + ' SetRenderArea($aChildren[i]._GetRect()) + ' $aChildren[i]._NeedRender = True + ' Next +End + +Static Public Sub _RefreshScreen() + + $RectRefreshArea = $hRect.Copy() + _Render + +End + +'Recalculate the refresh zone +Static Public Sub DoRefreshArea() + + Dim hChild As TermWindow + Dim hRect As Rect + + If Not $RectRefreshArea Then Return + DoArrange() + 'refresh desktop + 'Try + RefreshDesktop($hRect.InterSection($RectRefreshArea)) + 'Resfresh windows + For Each hChild In $aChildren + hRect = $hRect.InterSection($RectRefreshArea) + 'Try + RefreshChild(hChild, hRect) + Next + + SetRenderArea($RectRefreshArea) + $RectRefreshArea = Null + +End + +Static Private Sub RefreshDesktop(rectUpdate As Rect) + + Dim L, C As Integer + + For L = rectUpdate.Top To rectUpdate.Bottom - 1 + For C = rectUpdate.Left To rectUpdate.Right - 1 + $aScreen[l, C] = 0 + Next + Next + +End + +Static Private Sub RefreshChild(hChild As Object, hRect As Rect) + + Dim rectUpdate As Rect + Dim L, C As Integer + Dim hObj As Object + 'On définit la zone de + rectUpdate = hRect.InterSection(hChild._GetScreenRect()) + 'L 'objet est invisible alors fin + If Not hChild.Visible Then Return + 'La zone n'existe pas alors fin + If Not rectUpdate Then Return + + 'Remplissage de la zone de calepinageavec l'id de l'objet + + For L = rectUpdate.Top To rectUpdate.Bottom - 1 + For C = rectUpdate.Left To rectUpdate.Right - 1 + $aScreen[l, C] = hChild.Id + Next + Next + + 'L'objet est un conteneur ? + If hChild Is TermContainer Then + 'On applique la Client Zone + rectUpdate = hRect.InterSection(hChild._GetScreenClientRect()) + If Not IsNull(rectUpdate) Then + For Each hObj In hChild.Children + + RefreshChild(hObj, rectUpdate) + Next + Endif + Endif + +End + +' Static Private Sub RefreshChild(hChild As Object, hRect As Rect) +' +' Dim iLeft, iRight, iTop, iBottom As Integer +' +' Dim L As Integer +' Dim C As Integer +' Dim hObj As Object +' Dim rectUpdate As Rect +' Dim iMove, iResize As Integer +' 'If hChild Is Window Then Stop +' +' rectUpdate = hRect.Intersection(hChild._GetScreenRect()) +' If hChild.Visible = False Then Return +' If Not rectUpdate Then Return +' +' For L = rectUpdate.Top To rectUpdate.Bottom - 1 +' For C = rectUpdate.Left To rectUpdate.Right - 1 +' $aScreen[l, C] = hChild.Id +' Next +' Next +' +' +' If hChild Is TermContainer Then +' imove = If(hChild.Border > 0, 1, 0) + hChild.Padding +' iResize = iMove * 2 + If(hChild._Shadow, 1, 0) +' rectUpdate.Move(rectUpdate.Left + iMove, rectUpdate.Top + imove, rectUpdate.Width - iResize, rectUpdate.Height - iResize) +' For Each hObj In hChild.Children +' +' RefreshChild(hObj, rectUpdate) +' Next +' +' Endif +' +' End + +Static Public Sub Terminal_Resize() + + Dim hWin As TermWindow + + ResizeScreen(File.Out.Term.Width, File.Out.Term.Height) + For Each hWin In $aChildren + hWin._ScreenResize + Next + 'Try + SetRefreshArea(Rect(0, 0, Desktop.Width, Desktop.Height)) + _Render + +End + +Static Public Sub File_Read() + + Dim s, ss, sss As String + Dim ai As Integer[] + Dim hControl As TermControl + Dim R, C As Integer + Dim i As Integer + Dim b As Byte + Dim ab As Byte[] + Dim bnoesc As Boolean + Dim iPos As Integer = 1 + Dim iPos2 As Integer + Dim bControl As Boolean + Dim bInEscapeSec As Boolean + Dim bFound As Boolean + Dim aa As String[] + Dim bAlt As Boolean + 'Dim bControl As Boolean + Dim bShift As Boolean + + s = Read #$hFile, Lof($hFile) + bnoesc = InStr(s, "\e", 0) = -1 + 'Inc $index + + If s = "" Then Return + Do + ss = Mid(s, iPos, 1) + If ss = "\e" Then + bInEscapeSec = True + Inc ipos + Endif + + If bInEscapeSec Then + 'Is a mouse sequence ? + If Mid(s, ipos, 2) = "[<" Then + 'find the end of the sequence + i = InStr(s, "m", iPos, gb.IgnoreCase) + If i > -1 Then + sss = Mid(s, ipos + 2, i - (ipos + 1)) + ai = Split(Left(sss, -1), ";") + iPos = i + 1 + 'Mouse._setstate(x,y,Left, middle, right,delta) + ai[2] -= 1 + ai[1] -= 1 + Try hControl = TermControl._IdToControl[$aScreen[ai[2], ai[1]]] + If Error Then hControl = Null + Select Case ai[0] + 'Mouse move with left button down + Case 32 + 'Mouse._SetState(hControl, ai[1], ai[2], True,,, 0) + RaiseMouseEvent(hControl, "_MouseMove", ai[1], ai[2], True,,, 0) + 'Mouse move with right button down + Case 34 + 'Mouse._SetState(hControl, ai[1], ai[2],,, True, 0) + RaiseMouseEvent(hControl, "_MouseMove", ai[1], ai[2],,, True, 0) + 'Button left down Or up + Case 0 + 'Mouse._SetState(hControl, ai[1], ai[2], Right(s, 1) = "M",,, 0) + RaiseMouseEvent(hControl, IIf(Right(s, 1) = "M", "_MouseDown", "_MouseUp"), ai[1], ai[2], Right(s, 1) = "M",,, 0) + 'Button middle down Or up + Case 1 + 'Mouse._SetState(hControl, ai[1], ai[2],, Right(s, 1) = "M",, 0) + RaiseMouseEvent(hControl, IIf(Right(s, 1) = "M", "_MouseDown", "_MouseUp"), ai[1], ai[2],, Right(s, 1) = "M",, 0) + 'Button right down or up + Case 2 + 'Mouse._SetState(hControl, ai[1], ai[2],,, Right(s, 1) = "M", 0) + RaiseMouseEvent(hControl, IIf(Right(s, 1) = "M", "_MouseDown", "_MouseUp"), ai[1], ai[2],,, Right(s, 1) = "M", 0) + 'Mouse wheel up or down + Case 64, 65 + 'Mouse._SetState(hControl, ai[1], ai[2],,,, IIf(ai[0] = 64, -1, 1)) + RaiseMouseEvent(hControl, "_MouseWheel", ai[1], ai[2],,,, IIf(ai[0] = 64, -1, 1)) + End Select + + Endif + + bInEscapeSec = False + Continue + Endif + i = 10 + ipos2 = 10 + ' If String.InStr(S, "B") Then Stop + For Each ss In ["~", "A", "B", "C", "D", "H", "F", "P", "Q", "R", "S", "M", "Z"] + i = InStr(s, ss, ipos) + If i = 0 Then Continue + ipos2 = Min(ipos2, i) + Next + Inc ipos2 + sss = Mid(s, ipos, ipos2 - ipos) + aa = Split(sss, ";") + bInEscapeSec = False + bShift = False + bControl = False + bAlt = False + If aa.Count > 1 Then + aa[0] &= Right(aa[1], 1) + aa[1] = Left(aa[1], 1) + If aa[1] = "2" Then bShift = True + If aa[1] = "3" Then bAlt = True + If aa[1] = "5" Then bControl = True + Endif + Select Case aa[0] + Case "OA", "[A" + 'Key.Up + Key._SetKey("", Key.Up, bAlt, bControl, bShift) + bFound = True + + Case "OB", "[B" + 'Key.Down + Key._SetKey("", Key.Down, bAlt, bControl, bShift) + bFound = True + + Case "OC", "[C" + 'Key.Right + Key._SetKey("", Key.Right, bAlt, bControl, bShift) + bFound = True + + Case "OD", "[D" + 'Key.Left + Key._SetKey("", Key.Left, bAlt, bControl, bShift) + bFound = True + + 'Case + 'Key.Enter, Key.Return + ' sText = If(hScreen.NewlineMode, "\r\n", "\n") + + Case "OH", "[H" + 'Key.Home + 'sText="\e[1~" + Key._SetKey("", Key.Home, bAlt, bControl, bShift) + bFound = True + + Case "OF", "[F" + 'Key.End + Key._SetKey("", Key.End, bAlt, bControl, bShift) + bFound = True + Case "OP", "[1P" + 'Key.F1 + Key._SetKey("", Key.F1, bAlt, bControl, bShift) + bFound = True + + Case "OQ", "[1Q" + 'Key.F2 + Key._SetKey("", Key.F2, bAlt, bControl, bShift) + bFound = True + + Case "OR", "[1R" + 'Key.F3 + Key._SetKey("", Key.F3, bAlt, bControl, bShift) + bFound = True + + Case "OS", "[1S" + 'Key.F4 + Key._SetKey("", Key.F4, bAlt, bControl, bShift) + bFound = True + Case "OM" + 'Key.Return, Key.Enter + Key._SetKey("", Key.Return, bAlt, bControl, bShift) + bFound = True + + Case "[Z" + 'Key.BackTab + Key._SetKey("", Key.BackTab, bAlt, bControl, bShift) + bFound = True + + Case "[2~" + 'Key.Insert + Key._SetKey("", Key.Insert, bAlt, bControl, bShift) + bFound = True + + Case "[3~" + 'Key.Delete + Key._SetKey("", Key.Delete, bAlt, bControl, bShift) + bFound = True + + Case "[6~" + 'Key.PageDown + Key._SetKey("", Key.PageDown, bAlt, bControl, bShift) + bFound = True + + Case "[5~" + 'Key.PageUp + Key._SetKey("", Key.PageUp, bAlt, bControl, bShift) + bFound = True + + Case "[15~" + 'Key.F5 + Key._SetKey("", Key.F5, bAlt, bControl, bShift) + bFound = True + + Case "[17~" + 'Key.F6 + Key._SetKey("", Key.F6, bAlt, bControl, bShift) + bFound = True + + Case "[18~" + 'Key.F7 + Key._SetKey("", Key.F7, bAlt, bControl, bShift) + bFound = True + + Case "[19~" + 'Key.F8 + Key._SetKey("", Key.F8, bAlt, bControl, bShift) + bFound = True + + Case "[20~" + 'Key.F9 + Key._SetKey("", Key.F9, bAlt, bControl, bShift) + bFound = True + + Case "[21~" + 'Key.F10 + Key._SetKey("", Key.F10, bAlt, bControl, bShift) + bFound = True + + Case "[23~" + 'Key.F11 + Key._SetKey("", Key.F11, bAlt, bControl, bShift) + bFound = True + + Case "[24~" + 'Key.F12 + Key._SetKey("", Key.F12, bAlt, bControl, bShift) + bFound = True + + Case "[29~" + 'menu + Key._SetKey("", Key.Menu, bAlt, bControl, bShift) + bFound = True + End Select + If bFound Then + ipos = ipos2 + Endif + + bFound = False + + Else + + 'Normal Characters + + ss = Mid(s, iPos, 1) + 'Key._SetKey(s, bControl) + Select Case ss + Case "\n" + Key._SetKey(ss, Key.Return, bAlt, bControl, bShift) + Case "\t" + Key._SetKey(ss, Key.Tab, bAlt, bControl, bShift) + Case Chr(8), Chr(127) + Key._SetKey(ss, Key.BackSpace, bAlt, bControl, bShift) + Case Else + Key._SetKey(ss, 0, bAlt, bControl, bShift) + End Select + + iPos += 1 + Endif + + RaiseKeyEvent() + Key._Reset + + Loop Until iPos > Len(s) + + 'This is the end + + If $bThisIsTheEnd Then + TermControl._IdToControl = Null + hControl = Null + $hFile.Close + + Endif + +End + +Static Private Sub RaiseMouseEvent(hControl As TermControl, sEvent As String, Col As Integer, Row As Integer, Optional btnLeft As Boolean, btnMiddle As Boolean, btnRight As Boolean, Delta As Integer) + + 'Dim hControl As TermControl + + 'hControl = TermControl._IdToControl[$aScreen[Mouse.Row, Mouse.Col]] + + If hControl = Null And If $hCurControl = Null Then Return + If sEvent = "_MouseDown" Then $hCurControl = hControl + + Mouse._SetState($hCurControl, Col, Row, btnLeft, btnMiddle, btnRight, Delta) + + If sEvent = "_MouseUp" Then + If hControl = $hCurControl Then + + Object.Call($hCurControl, "_Click") + Object.Call($hCurControl, sEvent) + $hCurControl = Null + Endif + Endif + If $hCurControl Then Object.Call($hCurControl, sEvent) + +End + +Static Private Sub RaiseKeyEvent() + + Select Case Key.Code + Case Key.Menu + If Key.Alt Then + _RaiseWindow($aChildren[$aChildren.max - 1]) + Endif + Case Else + TermControl._IdToControl[$iActiveWindow]._KeyPress() + End Select + +End + +Static Public Sub _RaiseWindow(hWin As TermWindow) + + Dim hLastActive As TermWindow + Dim i As Integer + Dim bAdded As Boolean + 'If $aChildren.Count = 1 Then Return + 'hWin = $aChildren[0] + hLastActive = $aChildren[$aChildren.Max] + hLastActive._NeedRender = True + hWin._NeedRender = True + + For i = $aChildren.Max DownTo 0 + + If $aChildren[i] <> hwin And If Not $aChildren[i]._IsModal Then + $aChildren.Remove($aChildren.Find(hWIn)) + $aChildren.add(hWin, i + 1) + bAdded = True + Endif + Next + + SetRefreshArea(hWin._GetScreenRect()) + SetRenderArea(hLastActive._GetScreenRect()) + $iActiveWindow = hWin.id + + _Render + +End + +Static Public Sub _Read(sValue As String) + + Write "ok" & sValue + +End + +Static Public Sub SetRenderArea(hRect As Rect) + + If Not $RectRenderArea Then + $RectRenderArea = hRect + Return + Endif + + $RectRenderArea = $RectRenderArea.Union(hRect) + +End + +Static Public Sub SetRefreshArea(hRect As Rect) + + If Not $RectRefreshArea Then $RectRefreshArea = hRect.Copy() 'New Rect + 'If hRect.Top = 3 Then Stop + $RectRefreshArea = $RectRefreshArea.Union(hRect) + 'Error "RefreshARea : " & RectToStr($RectRefreshArea) + +End + +Static Public Sub RectToStr(hRect As Rect) As String + + Error "Top: " & hRect.Top & " Left: " & hRect.Left & " Width: " & hRect.Width & " Height: " & hRect.Height + +End + +Static Public Sub _Render() + + Dim c As Integer + Dim l As Integer + Dim hRect As Rect + Dim hAttr As New Attr + Dim hChar As Char + Dim hCont As TermControl + Dim sAttr As String + Dim sDisplay As String + Dim hOldCont As Object + Dim iOldAttr As Integer + Dim iBgAttr As Integer + Dim iBgChar As Integer + Dim bWroteBg As Boolean + Dim iId As Integer + + If bLock Then Stop + If $bInRender Then Return + + $hStream = Open String For Write + + $bInRender = True + DoRefreshArea() + RenderAll + + If Not IsNull($RectRenderArea) Then hRect = $hRect.Intersection($RectRenderArea) + + If hRect = Null Then + $bInRender = False + Return + Endif + + With Desktop.Background + iBgAttr = .Attr + iBgChar = .c + End With + + sAttr = hAttr._GetString(True) + + Write #$hStream, sAttr + + For l = hRect.Y To hRect.Bottom - 1 + 'Debug "ligne " & l + Write #$hStream, "\e[" & (l + 1) & ";" & (hRect.X + 1) & "H" + iOldAttr = 0 + bWroteBg = False + + For c = hRect.X To hRect.Right - 1 + + iId = $aScreen[l, c] + + If iId Then + + hCont = TermControl._IdToControl[iId] + If hOldCont <> hCont Then + hAttr.Reset + hAttr.ColorMode = hCont._ColorMode + Endif + hOldCont = hCont + hChar = hCont._GetChar(c, l) + If hChar.Attr <> iOldAttr Then + hAttr.FillFrom(hChar.Attr) + hAttr.WriteToStream($hStream) + 'sAttr = hAttr._GetString() + iOldAttr = hChar.Attr + Endif + 'Write #$hStream, sAttr + + Write #$hStream, String.Chr(hChar.c) + bWroteBg = False + + Else + + If Not bWroteBg Then + hAttr.FillFrom(iBgAttr) + hAttr.WriteToStream($hStream) + Endif + + Write #$hStream, String.Chr(iBgChar) + bWroteBg = True + + Endif + + Next + Next + sDisplay = Close #$hStream + If TermWindows.Debug = Me.DebugOutput Then Error Replace(Replace(sDisplay, "\e[", "\n"), "\e", "!"); + Print sDisplay; + Flush + + $RectRenderArea = Null + + $bInRender = False + +End + +Static Private Sub RenderAll() + + Dim hWin As TermWindow + + For Each hWin In $aChildren + hWin._Render + Next + +End + +Static Private Sub DoArrange() + + Dim hChild As TermWindow + + For Each hChild In $aChildren + hChild._Arrange() + Next + +End + +Static Public Sub Delete(hControl As TermControl) + + Dim hObj As Object = hControl + + TermControl._IdToControl[hObj.Id] = Null + hObj._Content = Null + If hObj Is TermContainer Then + For Each hControl In hObj.Children + Delete(hControl) + Next + 'hObj.Children.Clear + Endif + + If hObj Is TermWindow Then + $aChildren.Delete($aChildren.Find(hObj)) + If $aChildren.Count > 0 Then _RaiseWindow($aChildren[$aChildren.Max]) + Endif + + If $aChildren.Count = 0 Then + + $bThisIsTheEnd = True + + Endif + +End + +Static Public Sub GetActiveWindow() As TermWindow + + Return TermControl._IdToControl[$iActiveWindow] + +End diff --git a/comp/src/gb.term.form/.src/Test/FTest2.class b/comp/src/gb.term.form/.src/Test/FTest2.class new file mode 100644 index 00000000..c49d89a7 --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/FTest2.class @@ -0,0 +1,61 @@ +' Gambas class file + +Inherits TermForm +Private aList As New String[] +Private hTim As New Timer As "Tim" +Private hList As TermListBox +Private k As Integer + Private hLbl As TermLabel + +Public Sub _new() + Dim i As Integer +'aList = ["Chien", "Chat", "Eléphant", "Hiène", "Phoque", "Canard", "Hippoppo"] + Me.Spacing = 1 + + hList = New TermListBox(Me) As "ListBox1" + hLbl = New TermLabel(Me) + + For i = 1 To 200 + aList.Add("Item" & i) + Next + 'hList._Shadow = True + hList.List = aList + Me.Move(0, 0, 60, 20) + Me._Arrangement = Arrange.Vertical + hList.Move(1, 1, 3, 3) + 'Me._Arrangement = Arrange.Fill + hList.Expand = True + Me.Border = 0 + hList.Background = termColor.Blue + Me.Center + Me.Show + 'Me.Title = "Test 1" + Me.Maximized = True + hTim.Delay = 500 + hTim.Start + + hLbl.Background = TermColor.Green + hLbl.Foreground = TermColor.Black + TermWindows.Debug = TermWindows.DebugOutput +End + + +Public Sub tim_Timer() + + 'hList[5] = k + Inc k +End + + +Public Sub ListBox1_Change() + + hLbl.Text = Last.Text + +End + +Public Sub Form_close() + + hTim.Stop + +End + diff --git a/comp/src/gb.term.form/.src/Test/Main.module b/comp/src/gb.term.form/.src/Test/Main.module new file mode 100644 index 00000000..333fad3e --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/Main.module @@ -0,0 +1,136 @@ +' Gambas module file + + +Private hTimer As New Timer As "Timer" +Private hWin2 As TermWindow +Private hTermLabel As TermLabel +Private $i As Integer +Private $iBal As Integer = 1 +Private $hCurFocued As Object +'Private hWin2 As TermForm + + Public Sub Main() + + Dim hWin As New TermForm + Dim hTerCheckBox As TermRadioButton + Dim i As Integer + Dim hCont As TermVBox + Dim hCont2 As TermHBox + Dim hTermButton As TermButton + 'TermWindows.Debug = True + + 'Dim hTermLabel As New TermLabel(hWin) + 'Dim hWin2 As New Window + hTermLabel = New TermLabel(hwin) + hTimer.delay = 5 + 'hTimer.Start + + hWin.Left = 3 + hWin.Top = 1 + hWin.Height = 20 + hWin.Width = 70 + hwin.Padding = 1 + hwin.Border = Border.Simple + hTermLabel.Left = 2 + hTermLabel.Top = 2 + hTermLabel.Width = 7 + hTermLabel.tag = "*" + hTermLabel.Background = TermColor.Yellow + hTermLabel.Foreground = TermColor.Green + hTermLabel.Text = "TermLabel 1" + hWin.Text = "Window 1" + hTermLabel = New TermLabel(hWin) + hTermLabel.Width = 15 + hTermLabel.Text = "TermLabel 2" + hTermLabel.Tag = "#" + hTermLabel.SetFocus + + + hWin2 = New TermWindow + hWin2.Background = TermColor.Cyan + hWin2.Border = Border.Simple + hWin2.Width = 50 + hWin2.Spacing = 1 + hWin2.Height = 14 + 'hWin2._Shadow = True + hWin2.top = 6 + hWin2.Left = 15 + 'hWin2.Padding = 1 + 'hWin2.Maximized = True + hWin2.Text = "Window 2" + + 'hCont.Top = 3 + hTermLabel = New TermLabel(hWin2) + hTermLabel.Width = 14 + hTermLabel.Top = 2 + hTermLabel.Left = 2 + hTermLabel.Text = "TermLabel on win 2" + + hCont = New TermVBox(hWin2) + hCont.Expand = True + hcont.Background = TermColor.Green + hcont.Border = Border.Simple + 'hcont.Padding = 1 + hcont.Shadow = True + For i = 0 To 10 + hTerCheckBox = New TermRadioButton(hcont) + hTerCheckBox.Background = TermColor.Green + hTerCheckBox.Text = "TermLabel " & i + + Next + hCont2 = New TermHBox(hWin2) + hCont2.Height = 2 + hCont2.Spacing = 1 + hCont2.Background = TermColor.Cyan + hCont2.Invert = True + 'hCont.Shadow = True + hTermButton = New TermButton(hCont2) As "btnOk" + 'hTermButton.Background = TermColor.Magenta + hTermButton.Text = "Ok" + hTermButton.W = 10 + hTermButton.H = 2 + hTermButton._Shadow = True + hTermButton.Alignment = Align.Center + hTermButton = New TermButton(hCont2) As "btnCancel" + 'hTermButton.Background = TermColor.Magenta + hTermButton.Text = "Cancel" + hTermButton.W = 10 + hTermButton.H = 2 + hTermButton._Shadow = True + hTermButton.Alignment = Align.Center + 'hWin2.Maximized = True + hWin2.Show + hwin.Show + 'TermWindows._Render + $hCurFocued = hTermLabel + hTermButton.SetFocus + End + + +Public Sub Timer_Timer() + + hTermLabel.Text = $i +$hCurFocued = hWin2.GetNextFocusedControl($hCurFocued) + Error Object.Type($hCurFocued) & " "; + Try Error $hCurFocued.Text + $hCurFocued.setFocus() + Inc $i + TermWindows._Render + +End + + +Public Sub btnCancel_Click() + + Last.Window.Visible = False + 'TermWindows._Render + +End + +Public Sub btnOk_Click() + Dim hattr As New Attr + hattr.Background = TermColor.Black + 'Message("Salut a tous !") + Desktop.BackGround = Char(" ", hattr.GetInteger()) + hTimer.Start +End diff --git a/comp/src/gb.term.form/.src/Test/Main2.module b/comp/src/gb.term.form/.src/Test/Main2.module new file mode 100644 index 00000000..13e15453 --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/Main2.module @@ -0,0 +1,61 @@ +' Gambas module file + +Private i As Integer + +Public Sub Main() + Dim hForm As New TermForm + + Dim hCont As TermFrame + Dim hCont2 As TermHBox + Dim hTermButton As TermButton + Dim hLabel As New TermLabel(hForm) + Dim hCheck As TermCheckBox + Dim i As Integer + Dim hPicBox As TermPictureBox + + hLabel.Text = "Hello this is My first terminal window" + + hForm.Padding = 1 + hForm.Height = 15 + hForm.Width = 50 + hForm.Text = "Hi Gambasians" + hForm.Background = TermColor.White + hForm.Border = Border.Double + + hcont = New TermFrame(hForm) + hCont.Expand = True + hCont.Text = "Choose an option" + hCont2 = New TermHBox(hForm) + hCont2.Invert = True + + hTermButton = New TermButton(hCont2) As "btnClose" + hTermButton.Width = 10 + hTermButton.Text = "< Close >" + + hcont.Background = TermColor.Cyan + hcont.Shadow = True + hcont.Border = Border.Simple + hcont.Padding = 1 + For i = 0 To 5 + hCheck = New TermCheckBox(hcont) + + hCheck.Text = "Check me " & i + hCheck.Value = Int(Rnd(-1, 2)) + Next + hPicBox = New TermPictureBox(hcont) + + hPicBox.Expand = True + hPicBox.Image = Image.Load("all.png") + + hForm.Spacing = 1 + hForm.Center + hForm.Show + hForm.Maximized = True + 'TermWindows._Render + +End +Public Sub btnClose_MouseUp() + + Last.Window.Close + +End diff --git a/comp/src/gb.term.form/.src/Test/Main3.module b/comp/src/gb.term.form/.src/Test/Main3.module new file mode 100644 index 00000000..ad16cab8 --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/Main3.module @@ -0,0 +1,7 @@ +' Gambas module file + +Public Sub Main() + + Dim hpicture As New TermPictureBox + Quit +End diff --git a/comp/src/gb.term.form/.src/Test/Module1.module b/comp/src/gb.term.form/.src/Test/Module1.module new file mode 100644 index 00000000..a53e0fc9 --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/Module1.module @@ -0,0 +1,7 @@ +' Gambas module file + +Public Sub Main() + + Print "toto" + +End diff --git a/comp/src/gb.term.form/.src/Test/Termform1.class b/comp/src/gb.term.form/.src/Test/Termform1.class new file mode 100644 index 00000000..64569144 --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/Termform1.class @@ -0,0 +1,8 @@ +' Gambas class file + + +Public Sub TermForm_Open() + + Me.Title = "toto" + +End diff --git a/comp/src/gb.term.form/.src/Test/Termform1.termform b/comp/src/gb.term.form/.src/Test/Termform1.termform new file mode 100644 index 00000000..5b521aa3 --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/Termform1.termform @@ -0,0 +1,16 @@ +# Gambas Form File 3.0 + +{ TermForm TermForm + Move(0,0,52,22) + #Translate = False + { TermTextBox1 TermTextBox + Move(11,7,22,2) + } + { TermLabel2 TermLabel + Move(0,7,11,2) + Text = ("Bonjour") + } + { TermTextBox2 TermTextBox + Move(11,11,22,2) + } +} diff --git a/comp/src/gb.term.form/.src/Test/Termform2.class b/comp/src/gb.term.form/.src/Test/Termform2.class new file mode 100644 index 00000000..2bd7137a --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/Termform2.class @@ -0,0 +1,9 @@ +' Gambas class file + + +Public Sub TermForm_Open() +'Me.Persistent = True + Me.Move(0, 0, 30, 10) + Me.Background = TermColor.Red + +End diff --git a/comp/src/gb.term.form/.src/Test/Termform2.termform b/comp/src/gb.term.form/.src/Test/Termform2.termform new file mode 100644 index 00000000..eee892e7 --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/Termform2.termform @@ -0,0 +1,5 @@ +# Gambas Form File 3.0 + +{ TermForm TermForm + #MoveScaled(0,0,68,37) +} diff --git a/comp/src/gb.term.form/.src/Test/trfTest.class b/comp/src/gb.term.form/.src/Test/trfTest.class new file mode 100644 index 00000000..9af0905b --- /dev/null +++ b/comp/src/gb.term.form/.src/Test/trfTest.class @@ -0,0 +1,85 @@ +' Gambas class file + +Inherits TermForm +Private $hLab2 As TermLabel +'Private hTimer As New Timer As "Timer" + + + +Public Sub _New() + Dim hText As TermTextBox + Dim hBox As New TermHBox(Me) + Dim hLab As New TermLabel(hBox) + Dim hBar As TermScrollBar + Dim bar As TermScrollBar + Dim hbb As TermHBox + 'Dim hlab2 As TermLabel + + 'Me.Maximized = True + 'Me.Background = Color.Yellow + Me.Padding = 1 + Me.Move(3, 3, 50, 20) + Me.Text = "coucou" + Me.Border = Border.Simple + Me.Visible = True + Me.Center + Me._Arrangement = Arrange.Vertical + Me.Spacing = 1 + + hLab.Text = "Votre nom: " + hLab.W = Len(hLab.Text) + + + hText = New TermTextBox(hBox) As "TextBox" + hText.Text = "test" + hText.Expand = True + hText.SetFocus + + hbox = New TermHBox(Me) + hlab = New TermLabel(hbox) + hLab.Text = "Votre prenom: " + hLab.W = Len(hLab.Text) + + + hText = New TermTextBox(hBox) As "TextBox" + hText.Text = "test" + hText.Expand = True + + ' + $hlab2 = New TermLabel(Me) + + $hlab2.Background = TermColor.green + $hlab2.Show + + + hbb = New TermHBox(Me) + hbb.Height = 8 + hbb.Expand = True + hbox = New TermHBox(hbb) + 'hbox.Expand + bar = New TermScrollBar(hbb) As "Bar" + bar.Height = 5 + bar.Width = 1 + bar.Show + bar.Background = TermColor.red + + bar = New TermScrollBar(Me) As "bar" + + bar.Show + bar.MinValue = -20 + bar.MaxValue = 100 +End + + +Public Sub TextBox_Change() + + $hLab2.Text = Last.text + +End + +Public Sub Bar_Change() + + $hlab2.Text = Last.value + +End + diff --git a/comp/src/gb.term.form/all.png b/comp/src/gb.term.form/all.png new file mode 100644 index 0000000000000000000000000000000000000000..bd1c1ce075ebae381f9229434e02249d8096eae8 GIT binary patch literal 1507 zcmV<91swW`P)-)aEgYyjVC0N-l>-)aEgYXIMC0N-l>-)aEAfB@fX0N-l> z-)sWriZ<@BclFP$`Q_97_V51v`{R5b@VSTl@aF&j|KfKP^1+n;{QByhSN!wq;BE!- z!GOrR*G-khGKIW`x7XY1^P|Qq)}hDXDuB6IoXA0r!M4=rhq%{0jKDaFzBq`! zEP=Y*>ht~n{*S!cY^cxhxrXre`i;BV&f@RF+v_!lzKXfo=J5AkpURTH+f0?kxz^}E zjlj*}@66%uH;BDZm&W}4{;JL6Hi*46hQ0Lp`<1}lMUcYb?e*@jb~cB-h`HGH&#b)H z>9o@3a;wq#<q?Wv@%Q@g_WD4M!L`%p{q^qr{{BXh!?@Mx^Z5I>)aNdPyYl$^ zy4L7rq|JA((?^oSLXW{Qg}kWC;-kpnH;BG3gS(Er+DVhc&*Sf4q03U3#&4?7p2gqy z`uxAy>U6BpiMiNwtkE-uyyfurQJBW+^Y~(-%-ZSmJBq*6=JBo0}_SCg6<9{Ro001L&QchC<8d{RO+wl7kZO0E6k@W?4C(kBP z^JxBOc96zdy6^hV1oE$$(}@5815imsK~zY`l~(sd6EPIN0tE|{jqD*%Hi5DcaP$NO zK@c~J6(>Ux1wl{{6=f6$B5qM};vW2gzL!fcxwcpJ{g9^lzIRF9j3l{=!N=Fn=pPW^ zZ}ju^F-U)rb*8{{jRcy4ywHQqA?a=sVh+}!hlY8eQ8wz_Xt<*iS{Y=!Uql9Bf@FjFg#)z@hauWiDUjn ze-%5Cs*Tk(HPyA7H1y4c;LSr6pPSL1%syMT+GHxWZFQVq-vG8k>&DY~!wrfUf4i(o zW0RA=!`2LA3vPjtXhQ!of9;e7sb&1uHhC9%5Pfa}1&rr>c78lh`|dq^_w9$tfrGf# zaR{b|an=dM1jrYRHx@s&$UkD2<)dB4V0s*uPMpNePSLo+NHj=3Fk0B=rvd9GJ+j=} zcLt_sN&mS4db(~3TY#D<`NEjV6wU*70TpB!hnm45cAbk99tNse@`EvpDU47BJ5nx* z*B|Mi@F-A|BqNO3OraUTF;Zvm>9JoH6|N}oI8cp}Ka4rb!bBT@S0{0L?YgLNL&2wj znk)stm`nfPyhYLW>6w{X4S&wTatWv@g2KG4Be#jDz>joCPhp;jN8*>htHZz7PsB@e zvo7c;+;;|{Vpi@rblc(sPF!kieF#58YMd*M00i~_4sb(UXo(} zv!SRhtHr%#wRmh~x^a4qJ{s3r!fT>{L*Qo#i002ov JPDHLkV1kF=GHn0= literal 0 HcmV?d00001 diff --git a/comp/src/gb.util.web/.component b/comp/src/gb.util.web/.component new file mode 100644 index 00000000..ec208aee --- /dev/null +++ b/comp/src/gb.util.web/.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.util.web +Version=3.12.90 +State=1 diff --git a/comp/src/gb.util.web/.directory b/comp/src/gb.util.web/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/comp/src/gb.util.web/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/comp/src/gb.util.web/.hidden/control/ccontainer.png b/comp/src/gb.util.web/.hidden/control/ccontainer.png new file mode 100644 index 0000000000000000000000000000000000000000..0c964749ebf7dbdb57d5191e0da2b88753fc8546 GIT binary patch literal 1606 zcmV-M2D$l(P)0zwsps4b}iQOhG0k04crPgN`N zsVXI3c&LP=0e?VYOQ=ZWQb+^oLv3juG^)tUbz)~@JC42U-SzA|4}iYHXZ5i zy?Zr#e*b&!xo6KsMEJbT*~J<^$9_MDjO^qQAO&c%vGKXU=9_f-IrdBUW#XN9O5F3m z68YeZlHbPjKL&5__u`l$}vUDNf!s`2Jm)yGii+T0i580xW%8Q8i ze+lq%>LFd@d97nZJIkMdmMky|6oDe1SpZsq5*QxF*T2OV)c*T^D81Jf%iO7y>K74r z)&ktO)EZr5yT0JI4sQ4*T(TG#iNRrv>V+Y)WI-xmn_!tFzkEHd6>It9YtQYq9qv?0 z^)(Y=Gy(t2$S(c{H*JSYZv_fdz(Ffv6$(HFoB)nxf^Cvn)`|GCY|HHf2Ud6TfKuvj zfGc%D7YjoFcWQ^0Y~RQ=KZPs53kV`*{PIaWdl=s>;d^DQk+ZmF5zjFh@6WSj-8CRU zbsVhmpbwjQ<~&13FO&|P;5$EfgTDfHR7Cs~?7LRz+zbov1_U$@(ur{O(N+jOQG&sf zFmO8fy>1}B0`XC@@rrhFbr7D zkKvJ@L*Zk%dGl1bKMr;k#!KNws-QzKk}8M?%?I6~B^}R2g#iHCLt_mD=mx039B1DH zUEsRgAz=mZ$Z?}oM2r;5il2aqvFl7R?+0lq0qBjlAeD)&iow5tc@kEx2ctX*uuc5Z z7)nzSconR$x$dNm=t--~i**7dB4FYJtRfulhO0V3*FzCi{Bi{&n+bq+H4x!g0XUhO z1d9Z=1$^HL#*GC4sq`c`D#96t6Gd3G0=xhwo;T@s+X~)Grena^7{M10@R|vTcmNd_ zP}`QF9>~nWFPG4i1Ylnvk*tG;_GA){2)m(TjY(sbxQ6dP_2=IoFW4QBCt*z6GP=J?!WM<~@+Ps=Z zz|c_XvAOT>d#UA)JJ9FNi)=+yL^$Ur!B5W)j(`>y5c}5g6x)yW+D{kvypPpC7@UYH zEgkfUWDo3XC&T zlTICc^piY2T?gqsIB@v)eQe#?!x4hwa!5qXhW7%clme1FzQ?Msb?|oEimzrDu75Bt z!f_zc$3(J0!bJt+(y1Yizw{wz|D8X%Z-A{^KjrTPl}uPlQ56xt>BXcb;(--({A3mH z%wKuyvdcI8I1Ee$P6ZPADe&sx2t7Oh%>TMS8TijAuRVK+-4SpZFafVtk~J+zL`0NQ zcGn?#Q;NGcS3i2!F51f$ZF&~HrH$b4ygC7P%N35lyof0cX7?0WPcyb=Oy<&??A zs^VskQp)(z3Ko5TIlo!3cLEk(RH-IUqT_R~YGXvDtQov`+N1_R|i;N`ox8~^|S07*qoM6N<$ Ef+WBOQvd(} literal 0 HcmV?d00001 diff --git a/comp/src/gb.util.web/.hidden/control/ccontrol.png b/comp/src/gb.util.web/.hidden/control/ccontrol.png new file mode 100644 index 0000000000000000000000000000000000000000..0c964749ebf7dbdb57d5191e0da2b88753fc8546 GIT binary patch literal 1606 zcmV-M2D$l(P)0zwsps4b}iQOhG0k04crPgN`N zsVXI3c&LP=0e?VYOQ=ZWQb+^oLv3juG^)tUbz)~@JC42U-SzA|4}iYHXZ5i zy?Zr#e*b&!xo6KsMEJbT*~J<^$9_MDjO^qQAO&c%vGKXU=9_f-IrdBUW#XN9O5F3m z68YeZlHbPjKL&5__u`l$}vUDNf!s`2Jm)yGii+T0i580xW%8Q8i ze+lq%>LFd@d97nZJIkMdmMky|6oDe1SpZsq5*QxF*T2OV)c*T^D81Jf%iO7y>K74r z)&ktO)EZr5yT0JI4sQ4*T(TG#iNRrv>V+Y)WI-xmn_!tFzkEHd6>It9YtQYq9qv?0 z^)(Y=Gy(t2$S(c{H*JSYZv_fdz(Ffv6$(HFoB)nxf^Cvn)`|GCY|HHf2Ud6TfKuvj zfGc%D7YjoFcWQ^0Y~RQ=KZPs53kV`*{PIaWdl=s>;d^DQk+ZmF5zjFh@6WSj-8CRU zbsVhmpbwjQ<~&13FO&|P;5$EfgTDfHR7Cs~?7LRz+zbov1_U$@(ur{O(N+jOQG&sf zFmO8fy>1}B0`XC@@rrhFbr7D zkKvJ@L*Zk%dGl1bKMr;k#!KNws-QzKk}8M?%?I6~B^}R2g#iHCLt_mD=mx039B1DH zUEsRgAz=mZ$Z?}oM2r;5il2aqvFl7R?+0lq0qBjlAeD)&iow5tc@kEx2ctX*uuc5Z z7)nzSconR$x$dNm=t--~i**7dB4FYJtRfulhO0V3*FzCi{Bi{&n+bq+H4x!g0XUhO z1d9Z=1$^HL#*GC4sq`c`D#96t6Gd3G0=xhwo;T@s+X~)Grena^7{M10@R|vTcmNd_ zP}`QF9>~nWFPG4i1Ylnvk*tG;_GA){2)m(TjY(sbxQ6dP_2=IoFW4QBCt*z6GP=J?!WM<~@+Ps=Z zz|c_XvAOT>d#UA)JJ9FNi)=+yL^$Ur!B5W)j(`>y5c}5g6x)yW+D{kvypPpC7@UYH zEgkfUWDo3XC&T zlTICc^piY2T?gqsIB@v)eQe#?!x4hwa!5qXhW7%clme1FzQ?Msb?|oEimzrDu75Bt z!f_zc$3(J0!bJt+(y1Yizw{wz|D8X%Z-A{^KjrTPl}uPlQ56xt>BXcb;(--({A3mH z%wKuyvdcI8I1Ee$P6ZPADe&sx2t7Oh%>TMS8TijAuRVK+-4SpZFafVtk~J+zL`0NQ zcGn?#Q;NGcS3i2!F51f$ZF&~HrH$b4ygC7P%N35lyof0cX7?0WPcyb=Oy<&??A zs^VskQp)(z3Ko5TIlo!3cLEk(RH-IUqT_R~YGXvDtQov`+N1_R|i;N`ox8~^|S07*qoM6N<$ Ef+WBOQvd(} literal 0 HcmV?d00001 diff --git a/comp/src/gb.util.web/.icon.png b/comp/src/gb.util.web/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a9254f949f86671b58a744a6a93623576acc5217 GIT binary patch literal 10569 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 Len($sStr) Then Return + sCar = Mid$($sStr, $iPos, 1) + Inc $iPos + Return sCar + +End + + +Private Sub ReadChar() As String + + Dim sCar As String + + Do + sCar = GetChar() + If Not sCar Then Return + If sCar > " " Then Return sCar + Loop + +End + + +Private Sub ReadToken() As String + + Dim sToken As String + Dim sCar As String + + GoSub READ_CHAR + If Not IsLetter(sCar) Then Return sCar + + sToken = sCar + Do + GoSub GET_CHAR + If Not sCar Then Break + If Not IsLetter(sCar) Then + Dec $iPos + Break + Endif + sToken &= sCar + Loop + + Return sToken + +READ_CHAR: + + Do + GoSub GET_CHAR + If Not sCar Or If sCar > " " Then Return + Loop + Return + +GET_CHAR: + + If $iPos > Len($sStr) Then + sCar = "" + Return + Endif + sCar = Mid$($sStr, $iPos, 1) + Inc $iPos + Return + +End + +Private Sub ReadString() As String + + Dim sCar As String + Dim sString As String + Dim sBuffer As String + Dim iPos As Integer + + Do + GoSub GET_CHAR + If sCar = Chr$(34) Then Return sString & sBuffer + If sCar = "\\" Then + GoSub GET_CHAR + iPos = InStr("bfrtn", sCar) + If iPos Then + sCar = Mid$("\b\f\r\t\n", iPos, 1) + Else If sCar = "u" Then + Try sCar = String.Chr$(Val("&H" & Mid$($sStr, $iPos, 4))) + If Not Error Then $iPos += 4 + Else + ' Keep character + Endif + Endif + sBuffer &= sCar + If Len(sBuffer) > 512 Then + sString &= sBuffer + sBuffer = "" + Endif + Loop + +GET_CHAR: + + If $iPos > Len($sStr) Then Error.Raise("Non terminated string") + sCar = Mid$($sStr, $iPos, 1) + Inc $iPos + Return + +End + +Private Sub ReadObject() As Collection + + Dim sCar As String + Dim cObject As Collection + Dim sKey As String + + If $bUseNull Then + cObject = New JSONCollection + Else + cObject = New Collection + Endif + + Do + GoSub READ_CHAR + If sCar = "}" Then Return cObject + If sCar <> Chr$(34) Then Error.Raise("String expected") + sKey = ReadString() + GoSub READ_CHAR + If sCar <> ":" Then Error.Raise("Colon expected") + cObject[sKey] = ReadValue() + GoSub READ_CHAR + If sCar = "}" Then Return cObject + If sCar <> "," Then Error.Raise("Comma expected") + Loop + +READ_CHAR: + + Do + GoSub GET_CHAR + If Not sCar Or If sCar > " " Then Return + Loop + Return + +GET_CHAR: + + If $iPos > Len($sStr) Then + sCar = "" + Return + Endif + sCar = Mid$($sStr, $iPos, 1) + Inc $iPos + Return + +End + +Private Sub ReadArray() As Variant[] + + Dim sCar As String + Dim aArray As New Variant[] + + Do + sCar = ReadChar() + If sCar = "]" Then Return aArray + Dec $iPos + aArray.Add(ReadValue()) + sCar = ReadChar() + If sCar = "]" Then Return aArray + If sCar <> "," Then Error.Raise("Comma expected") + Loop + +End + +Private Sub ReadNumber(sNumber As String) As Variant + + Dim sCar As String + Dim vNumber As Variant + Dim bFloat As Boolean + + Do + GoSub GET_CHAR + If Not sCar Then Break + If InStr(".eE", sCar) Then + bFloat = True + Else If InStr("-+0123456789", sCar) = 0 Then + Dec $iPos + Break + Endif + sNumber &= sCar + Loop + + If bFloat Then + Try vNumber = CFloat(sNumber) + If Not Error Then Return vNumber + Else + Try vNumber = CInt(sNumber) + If Not Error Then Return vNumber + Try vNumber = CLong(sNumber) + If Not Error Then Return vNumber + Endif + + Error.Raise("Incorrect number") + +GET_CHAR: + + If $iPos > Len($sStr) Then + sCar = "" + Return + Endif + sCar = Mid$($sStr, $iPos, 1) + Inc $iPos + Return + +End + +Private Sub ReadValue() As Variant + + Dim sCar As String + + sCar = ReadToken() + + If sCar = "{" Then + Return ReadObject() + Else If sCar = "[" Then + Return ReadArray() + Else If sCar = Chr$(34) Then + Return ReadString() + Else If sCar = "-" Or If IsDigit(sCar) Then + Return ReadNumber(sCar) + Else If sCar = "null" Then + Return Null + Else If sCar = "true" Then + Return True + Else If sCar = "false" Then + Return False + Else If Not sCar Then + Return + Else + Error.Raise("Incorrect token: " & Quote(sCar)) + Endif + +End + +Private Sub WriteValue(vVal As Variant) + + Dim sStr As String + Dim iPos As Integer + Dim aArray As Array + Dim cCol As Collection + + Select Case TypeOf(vVal) + + Case gb.Null + Write #$hStream, "null" + + Case gb.Boolean + If vVal Then + Write #$hStream, "true" + Else + Write #$hStream, "false" + Endif + + Case gb.Byte, gb.Short, gb.Integer, gb.Long, gb.Float + Write #$hStream, CStr(vVal) + + Case gb.Date + Write #$hStream, Chr$(34) & CStr(vVal) & Chr$(34) + + Case gb.String + sStr = Quote(vVal) + Do + iPos = InStr(sStr, "\\", iPos + 1) + If iPos = 0 Then Break + If Mid$(sStr, iPos + 1, 1) = "x" Then + Mid$(sStr, iPos, 4) = "\\u00" & Mid$(sStr, iPos + 2, 2) + Else + Inc iPos + Endif + Loop + Write #$hStream, sStr + + Case gb.Pointer + If vVal = JSON.Null Then + Write #$hStream, "null" + Else + Write #$hStream, "undefined" + Endif + + Case Else + If vVal Is Array Then + aArray = vVal + Write #$hStream, "[" + For iPos = 0 To vVal.Max + If iPos Then Write #$hStream, "," + WriteValue(vVal[iPos]) + Next + Write #$hStream, "]" + Else If vVal Is Collection Then + cCol = vVal + Write #$hStream, "{" + For Each vVal In cCol + If iPos Then Write #$hStream, "," + WriteValue(cCol.Key) + Write #$hStream, ":" + WriteValue(vVal) + Inc iPos + Next + Write #$hStream, "}" + Else If IsNull(vVal) Then + Write #$hStream, "null" + Else + Write #$hStream, "undefined" + Endif + + End Select + +End + + +Public Sub Decode(JSONString As String, Optional UseNull As Boolean) As Variant + + Dim vVal As Variant + $sStr = JSONString + $iPos = 1 + $bUseNull = UseNull + vVal = ReadValue() + $bUseNull = False + $sStr = "" + Return vVal + +End + +Public Sub FromString(JSONString As String, Optional UseNull As Boolean) As Variant + + Return Decode(JSONString, UseNull) + +End + + +Public Sub Encode(Value As Variant) As String + + Dim sStr As String + + $hStream = Open String For Write + WriteValue(Value) + sStr = Close #$hStream + Return sStr + +End + +Public Sub ToString(Value As Variant) As String + + Return Encode(Value) + +End + + +Private Function Null_Read() As Variant + + Return $vNull + +End diff --git a/comp/src/gb.util.web/.src/JSONCollection.class b/comp/src/gb.util.web/.src/JSONCollection.class new file mode 100644 index 00000000..02876e56 --- /dev/null +++ b/comp/src/gb.util.web/.src/JSONCollection.class @@ -0,0 +1,29 @@ +' Gambas class file + +Export + +Inherits Collection + +Public Sub _get(Key As String) As Variant + + Dim vVal As Variant + + vVal = Super[Key] + If TypeOf(vVal) = gb.Pointer And If vVal = JSON.Null Then vVal = Null + Return vVal + +End + +Public Sub _put(Value As Variant, Key As String) + + If IsNull(Value) Then Value = JSON.Null + Super[Key] = Value + +End + +Public Sub _next() As Variant + + Super._next() + If Not Enum.Stopped Then Return _get(Me.Key) + +End diff --git a/comp/src/gb.util.web/.src/MMain.module b/comp/src/gb.util.web/.src/MMain.module new file mode 100644 index 00000000..1c702fbf --- /dev/null +++ b/comp/src/gb.util.web/.src/MMain.module @@ -0,0 +1,25 @@ +' Gambas module file + +Public Function GetNullVariant() As Variant + + Return Null + +End + +Public Function GetNullObject() As Object + + Return Null + +End + +Public Sub Main() + + Dim hURL As URL + + hURL = URL.FromString("http://localhost/guygle/find?query=ordre+de+travail+avec+interventions+dont+la+date+planifiée+est+comprise+entre+le+01%2F03%2F2017+et+31%2F03%2F2017+et+type+est+Campagne+dont+agence+est+AG.CIG.GONESSE&format=json&test#f5") + + Print hURL.Query["query"] + + Print hURL.ToString() + +End diff --git a/comp/src/gb.util.web/.src/URL.class b/comp/src/gb.util.web/.src/URL.class new file mode 100644 index 00000000..8f7781bc --- /dev/null +++ b/comp/src/gb.util.web/.src/URL.class @@ -0,0 +1,187 @@ +' Gambas class file + +Export + +Public Protocol As String +Public Host As String +Public Port As String +Public (User) As String +Public Password As String +Public Path As String +Public Query As UrlQuery +Public Hash As String + +Static Public Sub Encode((Path) As String) As String + + Dim iInd As Integer + Dim sRes As String + Dim sCar As String + + For iInd = 1 To Len(Path) + sCar = Mid$(Path, iInd, 1) + If sCar = " " Then + sCar = "+" + Else If IsLetter(sCar) Or If IsDigit(sCar) Or If InStr("-._~,$!", sCar) Then + Else + sCar = "%" & Hex$(Asc(sCar), 2) + Endif + sRes &= sCar + Next + + Return sRes + +End + +Static Public Sub Decode((Path) As String) As String + + Dim iInd As Integer + Dim sRes As String + Dim sCar As String + + For iInd = 1 To Len(Path) + sCar = Mid$(Path, iInd, 1) + If sCar = "%" Then + sCar = Chr$(Val("&H" & Mid$(Path, iInd + 1, 2))) + iInd += 2 + Else If sCar = "+" Then + sCar = " " + Endif + sRes &= sCar + Next + + Return sRes + +End + +Static Public Sub Quote((Path) As String) As String + + Return Encode(Path) + +End + +Static Public Sub UnQuote((Path) As String) As String + + Return Decode(Path) + +End + +Private Sub CreateUrl(sUrl As String) As URL + + Dim iPos As Integer + Dim sTemp As String + Dim sIdent As String + + iPos = InStr(sURL, ":") + If iPos Then + Protocol = Left(sURL, iPos - 1) + sURL = Mid$(sURL, iPos + 1) + Endif + + If sURL Begins "//" Then + + sURL = Mid$(sURL, 3) + iPos = InStr(sURL, "/") + If iPos = 0 Then + sTemp = sURL + sURL = "" + Else + sTemp = Left(sURL, iPos - 1) + sURL = Mid$(sURL, iPos) + Endif + + iPos = InStr(sTemp, "@") + If iPos Then + + sIdent = Left(sTemp, iPos - 1) + sTemp = Mid$(sTemp, iPos + 1) + + iPos = InStr(sIdent, ":") + If iPos Then + User = Left(sIdent, iPos - 1) + Password = Mid$(sIdent, iPos + 1) + Else + User = sIdent + Endif + + Endif + + iPos = InStr(sTemp, ":") + If iPos Then + Host = Left(sTemp, iPos - 1) + Port = Mid(sTemp, iPos + 1) + Else + Host = sTemp + Endif + + Endif + + iPos = InStr(sURL, "#") + If iPos Then + Hash = Mid(sURL, iPos + 1) + sURL = Left(sURL, iPos - 1) + Endif + + iPos = InStr(sURL, "?") + If iPos Then + Path = Left(sURL, iPos - 1) + Query = New URLQuery(Mid$(sURL, iPos + 1)) + Else + Path = sURL + Endif + + User = FromUrl(User) + Password = FromUrl(Password) + Host = FromUrl(Host) + Port = FromUrl(Port) + Path = FromUrl(Path) + Hash = FromUrl(Hash) + +End + +Public Sub _new(URL As String) + + CreateUrl(URL) + +End + +Public Sub ToString() As String + + Dim sURL As String + Dim sQuery As String + + If Protocol Then sURL = Protocol & ":" + + If Host Then + + sUrl &= "//" + + If User Or If Password Then + + sUrl &= Url(User) + If Password Then sUrl &= ":" & Url(Password) + sUrl &= "@" + + Endif + + sUrl &= Url(Host) + + If Port Then sUrl &= ":" & Url(Port) + + Endif + + If Path Then sUrl &/= Url(Path) + + sQuery = Query.ToString() + If sQuery Then sUrl &= "?" & sQuery + + If Hash Then sUrl &= "#" & Url(Hash) + + Return sUrl + +End + +Static Public Sub FromString(URL As String) As URL + + Return New URL(URL) + +End diff --git a/comp/src/gb.util.web/.src/URLQuery.class b/comp/src/gb.util.web/.src/URLQuery.class new file mode 100644 index 00000000..838782bf --- /dev/null +++ b/comp/src/gb.util.web/.src/URLQuery.class @@ -0,0 +1,113 @@ +' Gambas class file + +Export + +Property Read Count As Integer +Property Read Keys As String[] + +Private $cNull As New Collection +Private $cVal As New Collection + +Public Sub _new(Query As String) + + Dim sElt As String + Dim iPos As Integer + Dim sKey As String + Dim sValue As String + Dim cKey As New Collection + + For Each sElt In Split(Query, "&") + + iPos = InStr(sElt, "=") + If iPos Then + sKey = URL.Decode(Left(sElt, iPos - 1)) + sValue = URL.Decode(Mid$(sElt, iPos + 1)) + Else + sKey = sElt + sValue = "" + Endif + + If Not sKey Then Continue + If cKey.Exist(sKey) Then Continue + + If sValue Then + $cVal[sKey] = sValue + Else + $cNull[sKey] = True + Endif + + Next + +End + +Public Sub Exist(Key As String) As Boolean + + If $cNull.Exist(Key) Then Return True + If $cVal.Exist(Key) Then Return True + +End + + +Public Sub _get(Key As String) As String + + Return $cVal[Key] + +End + +Public Sub _put(Value As String, Key As String) + + $cNull.Remove(Key) + $cVal.Remove(Key) + + If Value Then + $cVal[Key] = Value + Else + $cNull[Key] = True + Endif + +End + +Public Sub ToString() As String + + Dim aQuery As New String[] + Dim sVal As String + + For Each sVal In $cVal + aQuery.Add(URL.Encode($cVal.Key) & "=" & URL.Encode(sVal)) + Next + + For Each $cNull + aQuery.Add(URL.Encode($cNull.Key)) + Next + + Return aQuery.Join("&") + +End + +Static Public Sub FromString(Query As String) As URLQuery + + Return New URLQuery(Query) + +End + + +Private Function Count_Read() As Integer + + Return $cVal.Count + $cNull.Count + +End + +Private Function Keys_Read() As String[] + + Dim aKeys As String[] + + aKeys = New String[] + For Each $cVal + aKeys.Add($cVal.Key) + Next + For Each $cNull + aKeys.Add($cNull.Key) + Next + Return aKeys + +End diff --git a/comp/src/gb.util/.component b/comp/src/gb.util/.component new file mode 100644 index 00000000..a711f5d5 --- /dev/null +++ b/comp/src/gb.util/.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.util +Version=3.12.90 +State=1 diff --git a/comp/src/gb.util/.directory b/comp/src/gb.util/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/comp/src/gb.util/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/comp/src/gb.util/.icon.png b/comp/src/gb.util/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a9254f949f86671b58a744a6a93623576acc5217 GIT binary patch literal 10569 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 &H12345678 Then hFile.ByteOrder = 1 - hFile.ByteOrder + Seek #hFile, 12 + iVal = Read #hFile As Integer + bDebug = iVal And 1 + + $iSectionSize = 16 + + GotoNextSection(hFile) ' info + Seek #hFile, $iSectionPos + iParent = Read #hFile As Short + iFlag = Read #hFile As Short + + hStat.Exported = BTst(iFlag, 0) + hStat.AutoCreate = BTst(iFlag, 1) + hStat.Optional = BTst(iFlag, 2) + hStat.NoCreate = BTst(iFlag, 3) + hStat.HasFast = BTst(iFlag, 4) + + GotoNextSection(hFile) ' description + GotoNextSection(hFile) ' constant + GotoNextSection(hFile) ' reference + + If iParent <> -1 Then + + Seek #hFile, $iSectionPos + iParent * 4 + iParent = Read #hFile As Integer + iParent = Abs(iParent) + + Endif + + Do + iStringPos = $iSectionPos + Try GotoNextSection(hFile) + If Error Then Break + Loop + + Seek #hFile, iStringPos + hStat.Name = ReadZeroString(hFile) + + If iParent > 0 Then + + Seek #hFile, iStringPos + iParent + hStat.Parent = ReadZeroString(hFile) + + Endif + + Close hFile + + Return hStat + +End diff --git a/comp/src/gb.util/.src/ClassStat.class b/comp/src/gb.util/.src/ClassStat.class new file mode 100644 index 00000000..8ae56f70 --- /dev/null +++ b/comp/src/gb.util/.src/ClassStat.class @@ -0,0 +1,11 @@ +' Gambas class file + +Export + +Public Name As String +Public Parent As String +Public Exported As Boolean +Public HasFast As Boolean +Public AutoCreate As Boolean +Public NoCreate As Boolean +Public Optional As Boolean diff --git a/comp/src/gb.util/.src/CsvFile.class b/comp/src/gb.util/.src/CsvFile.class new file mode 100644 index 00000000..f06daeb3 --- /dev/null +++ b/comp/src/gb.util/.src/CsvFile.class @@ -0,0 +1,93 @@ +' Gambas class file + +Export + +Private $hFile As File +Private $aField As String[] +Private $sSeparator As String +Private $sEscape As String +Private $iLine As Integer + +Property Read Eof As Boolean +Property Read Fields As String[] +Property Read Line As Integer + +Public Sub _new(Path As String, Optional Separator As String = ",", Optional Escape As String = "\"") + + Dim sLine As String + Dim iInd As Integer + Dim sField As String + + If File.IsRelative(Path) Then + If Path Begins "./" Then + Path = "." & Path + Else If Path Not Begins "../" Then + Path = ".." &/ Path + Endif + Endif + + $hFile = Open Path + + $sSeparator = Separator + $sEscape = Escape + + sLine = $hFile.ReadLine($sEscape) + $aField = Split(sLine, $sSeparator, $sEscape) + + For iInd = 0 To $aField.Max + sField = Trim($aField[iInd]) + sField = Replace(sField, String.Chr(160), " ") + sField = Replace(sField, "\n", " ") + sField = Replace(sField, "\t", " ") + While InStr(sField, " ") + sField = Replace(sField, " ", " ") + Wend + sField = String.LCase(sField) + If Not sField Then sField = "#" & CStr(iInd) + $aField[iInd] = sField + Next + + $iLine = 1 + +End + +Public Sub Read() As Collection + + Dim sLine As String + Dim aLine As String[] + Dim cCol As Collection + Dim iInd As Integer + + If Eof($hFile) Then Return + + cCol = New Collection(gb.IgnoreCase) + + Inc $iLine + sLine = $hFile.ReadLine($sEscape) + aLine = Split(sLine, $sSeparator, $sEscape) + + For iInd = 0 To Min($aField.Max, aLine.Max) + cCol[$aField[iInd]] = Trim(Replace(aLine[iInd], String.Chr(160), " ")) + Next + + Return cCol + +End + +Private Function Eof_Read() As Boolean + + Return Eof($hFile) + +End + +Private Function Fields_Read() As String[] + + Return $aField.Copy() + +End + +Private Function Line_Read() As Integer + + Return $iLine + +End diff --git a/comp/src/gb.util/.src/Date.module b/comp/src/gb.util/.src/Date.module new file mode 100644 index 00000000..ee071853 --- /dev/null +++ b/comp/src/gb.util/.src/Date.module @@ -0,0 +1,154 @@ +' Gambas module file + +Export +Private $aDay As String[] +Private $aMonth As String[] +Private $dEpoch As Date + +Public Sub ToUnixTime({Date} As Date) As Long + + If Not $dEpoch Then $dEpoch = DateAdd(Date(1970, 1, 1), gb.Second, - System.TimeZone) + Return DateDiff($dEpoch, {Date}, gb.Second) + +End + +Public Sub FromUnixTime(UnixTime As Long) As Date + + If Not $dEpoch Then $dEpoch = DateAdd(Date(1970, 1, 1), gb.Second, - System.TimeZone) + Return $dEpoch + UnixTime / 86400 + +End + +' Gregorian algorithm by Aloysius Lilius And Christophorus Clavius. +' Valid For any year since 1583. + +Public Sub EasterDay(Year As Integer) As Date + + Dim A, B, C, D, E, F, G As Integer + + If Year < 1583 Then Error.Raise("Year must be greater or equal than 1583") + + A = Year Mod 19 + 1 + B = Year Div 100 + 1 + C = (3 * B) Div 4 - 12 + D = (8 * B + 5) Div 25 - 5 + E = (Year * 5) Div 4 - 10 - C + F = ((11 * A + 20 + D - C) Mod 30 + 30) Mod 30 + If F = 24 Or (F = 25 And A > 11) Then F = F + 1 + G = 44 - F + If G < 21 Then G = G + 30 + Return DateAdd(Date(Year, 3, 1), gb.Day, G + 7 - (E + G) Mod 7 - 1) + +End + +Private Sub InitDaysMonths() + + If Not $aDay Then + $aDay = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] + $aMonth = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + Endif + +End + +Public Sub ToUTC({Date} As Date) As Date + + Return {Date} + System.TimeZone / 86400 + +End + +Public Sub FromUTC({Date} As Date) As Date + + Return {Date} - System.TimeZone / 86400 + +End + +Private Sub GetRFC822Zone(sZone As String) As Float + + Dim fZone As Float + + Select Case sZone + Case "UT", "GMT", "Z" + fZone = 0 + Case "EDT" + fZone = -4 + Case "EST", "CDT" + fZone = -5 + Case "CST", "MDT" + fZone = -6 + Case "MST", "PDT" + fZone = -7 + Case "PST" + fZone = -8 + Case Like "[A-I]" + fZone = - (Asc(sZone) - 64) + Case Like "[J-M]" + fZone = - (Asc(sZone) - 65) + Case Like "[N-Y]" + fZone = Asc(sZone) - 77 + Case Like "[+-][0-1][0-9][0-5][0-9]" + fZone = CInt(Left(sZone, 3)) + CInt(Mid$(sZone, 4)) / 60 + Case Else + Error.Raise(Subst$("Unknown timezone '&1'", sZone)) + End Select + + Return fZone / 24 + +End + + + +Public Sub ToRFC822({Date} As Date, Optional TimeZone As String = "GMT") As String + + InitDaysMonths + {Date} = ToUTC({Date}) + GetRFC822Zone(TimeZone) + Return $aDay[WeekDay({Date})] & ", " & Format(Day({Date}), "00") & " " & $aMonth[Month({Date}) - 1] & " " & Year({Date}) & " " & Format(Hour({Date}), "00") & ":" & Format(Minute({Date}), "00") & ":" & Format(Second({Date}), "00") & " " & TimeZone + +End + +Public Sub FromRFC822(Value As String, Optional ByRef TimeZone As String) As Date + + Dim aDate As String[] + Dim dDate As Date + Dim iPos As Integer + Dim iWeekDay As Integer + Dim iYear As Integer + + InitDaysMonths + Value = Trim(Value) + + iPos = InStr(Value, ", ") + If iPos Then + iWeekDay = $aDay.Find(Trim(Left(Value, iPos - 1))) + If iWeekDay < 0 Then Error.Raise("Unknown week day") + Value = Trim(Mid$(Value, iPos + 2)) + Else + iWeekDay = -1 + Endif + + aDate = Scan(Value, "* * * *:* *") + If aDate.Count <> 6 Then Return + + iPos = InStr(aDate[4], ":") + If iPos Then + aDate.Add(Mid$(aDate[4], iPos + 1), 5) + aDate[4] = Left(aDate[4], iPos - 1) + Else + aDate.Add("0", 5) + Endif + + iYear = CInt(aDate[2]) + If iYear >= 0 And If iYear <= 99 Then iYear += 1900 + dDate = Date(iYear, $aMonth.Find(aDate[1]) + 1, CInt(aDate[0]), CInt(aDate[3]), CInt(aDate[4]), CInt(aDate[5])) + If iWeekDay >= 0 And If WeekDay(dDate) <> iWeekDay Then Error.Raise("Incorrect week day") + + dDate = FromUTC(dDate) - GetRFC822Zone(aDate[6]) + + TimeZone = aDate[6] + Return dDate + +Catch + + If Error.Class.Name = "Date" Then Error.Propagate + Error.Raise("Not a RFC822 date format") + +End diff --git a/comp/src/gb.util/.src/File.class b/comp/src/gb.util/.src/File.class new file mode 100644 index 00000000..404acac3 --- /dev/null +++ b/comp/src/gb.util/.src/File.class @@ -0,0 +1,29 @@ +' Gambas class file + +Export + +Static Public Sub FormatSize(Size As Long, Optional {Binary} As Boolean) As String + + If {Binary} Then + If Size < 1024 Then + Return Subst(("&1 B"), CStr(Size)) + Else If Size < 1048576 Then + Return Subst(("&1 KiB"), Format(Size / 1024, "0.##")) + Else If Size < 1073741824 Then + Return Subst(("&1 MiB"), Format(Size / 1048576, "0.##")) + Else + Return Subst(("&1 GiB"), Format(Size / 1073741824, "0.##")) + Endif + Else + If Size < 1000 Then + Return Subst(("&1 B"), CStr(Size)) + Else If Size < 1000000 Then + Return Subst(("&1 KB"), Format(Size / 1000, "0.##")) + Else If Size < 1000000000 Then + Return Subst(("&1 MB"), Format(Size / 1000000, "0.##")) + Else + Return Subst(("&1 GB"), Format(Size / 1000000000, "0.##")) + Endif + Endif + +End diff --git a/comp/src/gb.util/.src/MMain.module b/comp/src/gb.util/.src/MMain.module new file mode 100644 index 00000000..b8191d16 --- /dev/null +++ b/comp/src/gb.util/.src/MMain.module @@ -0,0 +1,58 @@ +' Gambas module file + +' Public Sub Main() +' +' Dim I As Integer +' Dim fTimer As Float +' Dim sData As String +' +' sData = File.Load("~/.local/share/gambas3/wiki/data/en/doc/object-model/~page") +' +' fTimer = Timer +' For I = 1 To 1000 +' String.RemoveDiacritics(sData) +' Next +' Print Timer - fTimer +' +' End + +Public Sub Main() + + Print CStr(CDate("1/1/1970")) + RFC822Test() + +End + +Public Sub RFC822Test() + + Dim sTimeZone As String + + ' Must be correct in the local timezone + Print CStr(Now) + Print Format(Now) + Print CStr(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0000")) + Print Format$(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0000")) + Print Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0100") + Print Date.FromRFC822("Tue, 1 Jan 2019 00:00:00 +0800") + Print Date.ToRFC822(Now) + Print Date.ToRFC822(Now, "+0100") + Print "---" + ' Timezone to-from conversion should be the identity mapping + Print Date.ToRFC822(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0000"), "+0000") + Print Date.ToRFC822(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0000"), "+0100") + Print Date.ToRFC822(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0000"), "+0800") + Print Date.ToRFC822(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0100"), "+0000") + Print Date.ToRFC822(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0100"), "+0100") + Print Date.ToRFC822(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0100"), "+0800") + Print Date.ToRFC822(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0800"), "+0000") + Print Date.ToRFC822(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0800"), "+0100") + Print Date.ToRFC822(Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0800"), "+0800") + Print "---" + ' Ability to extract timezone + Date.FromRFC822("Sun, 21 Apr 2019 05:00:00 +0100", ByRef sTimeZone) + Print sTimeZone + ' 21 Apr 2019 is not a Wednesday + Try Date.FromRFC822("Wed, 21 Apr 2019 05:00:00 +0100") + If Error Then Print Error.Text + +End diff --git a/comp/src/gb.util/.src/MPhonetic_French.module b/comp/src/gb.util/.src/MPhonetic_French.module new file mode 100644 index 00000000..51bf13cf --- /dev/null +++ b/comp/src/gb.util/.src/MPhonetic_French.module @@ -0,0 +1,278 @@ +' Gambas module file + +Public Sub Before(sStr As String) As String + + ' R0 substitution des caractères accentués par les voyelles "simples" + + sStr = Replace(sStr, "ç", "ss") + sStr = Replace(sStr, "Ç", "ss") + + Return sStr + +End + +Public Sub Run(sStr As String) As String + + Dim sSuff As String + Dim sPref As String + Dim I As Integer + Dim sPat As String + Dim sResult As String + + If IsDigit(sStr) Then Return "(" & sStr & ")" + + ' R1 remplacement du "y" par un "i" + + sStr = Replace(sStr, "y", "i") + + ' R2 remplacement du son "ph" par un simple "f" + + sStr = Replace(sStr, "ph", "f") + + ' R3 suppression des "h" muets, c'est à dire ceux qui ne sont ni précédés d'un "c" ni d'un "s" + + sStr = Replace(sStr, "ch", "cH") + sStr = Replace(sStr, "sh", "sH") + sStr = Replace(sStr, "h", "") + sStr = Replace(sStr, "H", "h") + + ' R4 remplacement du "g" par "k" devant "an/am/ain/aim" gamin -> kamin + + sStr = Replace(sStr, "gan", "kan") + sStr = Replace(sStr, "gam", "kam") + sStr = Replace(sStr, "gain", "kain") + sStr = Replace(sStr, "gaim", "kaim") + + ' R5 + ' + ' remplacement de aina,eina,aima,eima par yna + ' remplacement de aine,eine,aime,eime par yne + ' remplacement de aini,eini,aimi,eimi par yni + ' remplacement de aino,eino,aimo,eimo par yno + ' remplacement de ainu,einu,aimu,eimu par ynu + + For Each sSuff In ["na", "ne", "ni", "no", "nu"] + For Each sPref In ["a", "e", "i", "o", "u"] + sStr = Replace(sStr, sPref & "i" & sSuff, "y" & sSuff) + Next + Next +' + ' R6 + ' + ' remplacement de eau par o + ' remplacement de oua par 2 + ' remplacement de ein par 4 + ' remplacement de ain par 4 + + sStr = Replace(sStr, "eau", "o") + sStr = Replace(sStr, "oua", "2") + sStr = Replace(sStr, "ein", "4") + sStr = Replace(sStr, "ain", "4") + + ' R7 + ' + ' remplacement de ai par y + ' remplacement de ei par y + ' remplacement de ee par y + ' remplacement de er par yr + ' remplacement de ess par yss + ' remplacement de et par yt + ' remplacement de ez par yz + + sStr = Replace(sStr, "ai", "y") + sStr = Replace(sStr, "ei", "y") + sStr = Replace(sStr, "ee", "y") + sStr = Replace(sStr, "er", "yr") + sStr = Replace(sStr, "ess", "yss") + sStr = Replace(sStr, "et", "yt") + sStr = Replace(sStr, "ez", "yz") + + ' R8 suppression des lettres doublées pelle -> pele + + GoSub REMOVE_DOUBLE + + ' R9 + ' + ' remplacement de "an" par "1" + ' remplacement de "am" par "1" + ' remplacement de "en" par "1" + ' remplacement de "em" par "1" + ' remplacement de "in" par "4" + ' à condition que ces modèles ne soient ni suivis d'une voyelle ni d'un son "1" à "4" + ' patient - > pati1t + + For Each sPat In ["an", "am", "en", "em", "in"] + I = 0 + Do + I = InStr(sStr, sPat, I + 1) + If I = 0 Then Break + If InStr("aeiouy1234", Mid$(sStr, I + 2, 1)) Then + I += 2 + Else + sStr = Left(sStr, I - 1) & If(sPat = "in", "4", "1") & Mid$(sStr, I + 2) + Endif + Loop + Next + + ' R10 remplacement du "z" par "s" s'il est en tête de mot ou précédé et suivi d'une voyelle ou d'un son "1" à "4" + ' zebu -> zebu + ' azteque -> azteque + ' bizarre -> bisarre + + I = 0 + Do + I = InStr(sStr, "z", I + 1) + If I = 0 Then Break + If I = 1 + sStr = "s" & Mid$(sStr, 2) + Else If InStr("aeiouy1234", Mid$(sStr, I - 1, 1)) And If InStr("aeiouy1234", Mid$(sStr, I + 1, 1)) Then + sStr = Left(sStr, I - 1) & "s" & Mid(sStr, I + 1) + I += 1 + Endif + Loop + + ' R11 + ' + ' remplacement de "oe" par "e" + ' remplacement de "eu" par "e" + ' remplacement de "au" par "o" + ' remplacement de "oi" par "2" + ' remplacement de "ou" par "3" + ' + ' heureux -> erex + ' paul -> pol + ' roue -> r3e + + sStr = Replace(sStr, "oe", "e") + sStr = Replace(sStr, "eu", "e") + sStr = Replace(sStr, "au", "o") + sStr = Replace(sStr, "oi", "2") + sStr = Replace(sStr, "ou", "3") + + ' R12 + ' + ' remplacement de "ch" par "5" + ' remplacement de "sch" par "5" + ' remplacement de "sh" par "5" + ' remplacement de "ss" par "s" + ' remplacement de "sc" par "s" si suivi d'un "i" ou d'un "e" + ' + ' chat -> 5at + ' scaralatine -> scarlatine + ' scie -> sie + + sStr = Replace(sStr, "ch", "5") + sStr = Replace(sStr, "sch", "5") + sStr = Replace(sStr, "sh", "5") + sStr = Replace(sStr, "ss", "s") + sStr = Replace(sStr, "sci", "si") + sStr = Replace(sStr, "sce", "se") + + ' R13 remplacement du "c" par "s" s'il est suivi dun "e" ou d'un "i" + ' car -> car + ' innocence -> inosense + + sStr = Replace(sStr, "ce", "se") + sStr = Replace(sStr, "ci", "si") + + ' R14 + ' + ' remplacement de "c" par "k" + ' remplacement de "q" par "k" + ' remplacement de "qu" par "k" + ' remplacement de "gu" par "k" + ' remplacement de "ga" par "ka" + ' remplacement de "go" par "ko" + ' + ' car -> kar + ' gateau -> kato + + sStr = Replace(sStr, "c", "k") + sStr = Replace(sStr, "qu", "k") + sStr = Replace(sStr, "q", "k") + sStr = Replace(sStr, "ga", "ga") + sStr = Replace(sStr, "ge", "je") + sStr = Replace(sStr, "gi", "ji") + sStr = Replace(sStr, "go", "go") + sStr = Replace(sStr, "gu", "g") + + ' R15 + ' + ' remplacement de "a" par "o" + ' remplacement de "d" et "p" par "t" + ' remplacement de "j" par "g" + ' remplacement de "b" et "v" par "f" + ' remplacement de "m" par "n" + ' + ' depart -> tetord + ' homme -> one + + 'sStr = Replace(sStr, "a", "o") + 'sStr = Replace(sStr, "d", "t") + 'sStr = Replace(sStr, "p", "t") + 'sStr = Replace(sStr, "j", "g") + 'sStr = Replace(sStr, "b", "f") + 'sStr = Replace(sStr, "v", "f") + 'sStr = Replace(sStr, "m", "n") + 'sStr = Replace(sStr, "w", "v") + + ' R15.1 + + sStr = Replace(sStr, "w", "3") + + ' R16 (optionnelle) remplacement des "y" (les sons "é") par "e" nez -> nyz -> nez + + 'sStr = Replace(sStr, "y", "e") + + ' R17 + ' + ' suppression des finales "t","x","s","z" + ' (optionnelle) suppression du "e" final après une consonne (fait en deux fois pour eliminer les finales "ez" par exemple) + ' + ' heureux -> ere + ' tetard -> tetor + + If Len(sStr) >= 2 And If InStr("rdptxsz", Right(sStr)) Then sStr = Left(sStr, -1) + + ' If Len(sStr) >= 2 Then + ' If Right(sStr) = "e" And InStr("aeiouy", Left(Right(sStr, 2))) = 0 Then + ' sStr = Left(sStr, -1) + ' Endif + ' Endif + ' + ' If Len(sStr) >= 2 And If InStr("rtxsz", Right(sStr)) Then sStr = Left(sStr, -1) + + ' R18 suppression des lettres doubles (pour la 2ème fois) arrivee -> arive + + GoSub REMOVE_DOUBLE + + ' R19 retourne une chaine d'une taille maximale de 16 caractères et ne contenant que les caractères autorisés. + ' Au vu des traductions décrites, les caractères obtenus font partie de l'alphabet réduit: + ' 12345efghiklnorstuwxyz + ' Les caractères qui n'appartiennent pas à cet ensemble sont supprimés + + sResult = "" + For I = 1 To Len(sStr) + sPat = Mid$(sStr, I, 1) + If InStr("12345abdefghijklmnoprstuvxyz", sPat) Then + sResult &= sPat + 'If Len(sResult) = 16 Then Break + Endif + Next + + Return sResult + +REMOVE_DOUBLE: + + I = 1 + While I < Len(sStr) + If Mid$(sStr, I, 1) = Mid$(sStr, I + 1, 1) Then + sStr = Left(sStr, I) & Mid$(sStr, I + 2) + Else + Inc I + Endif + Wend + Return + +End + diff --git a/comp/src/gb.util/.src/Shell.module b/comp/src/gb.util/.src/Shell.module new file mode 100644 index 00000000..41b26b9e --- /dev/null +++ b/comp/src/gb.util/.src/Shell.module @@ -0,0 +1,59 @@ +' Gambas module file + +Export + +Public Sub MkDir(Path As String) + + Dim sElt As String + Dim sMake As String = "/" + + If Path Begins "~/" Then Path = User.Home &/ Mid$(Path, 3) + + For Each sElt In Split(Path, "/") + sMake &/= sElt + If IsDir(sMake) Then Continue + Try Mkdir sMake + Next + + If Not Exist(Path) Or If Not IsDir(Path) Then Error.Raise("Cannot create directory") + +End + +Public Sub RmDir(Path As String, Optional Force As Boolean) + + Dim sFile As String + + If Len(Path) > 1 And If Right(Path) = "/" Then Path = Left(Path, -1) + + If Not Force Then + If Path = "~" Or If Path = User.Home Or If Path = "/" Then Error.Raise("Removing this directory recursively is a bad idea: " & Path) + Endif + + For Each sFile In Dir(Path) + If IsDir(Path &/ sFile) Then + RmDir(Path &/ sFile) + Else + Kill Path &/ sFile + Endif + Next + + Rmdir Path + +End + +Public Sub Move(Source As String, Destination As String, Optional Force As Boolean) + + If IsDir(Destination) Then Destination &/= File.Name(Source) + + If Force Then + Try Move Source Kill Destination + Else + Try Move Source To Destination + Endif + If Error Then + If Force Then Try Kill Destination + Copy Source To Destination + Kill Source + Endif + +End diff --git a/comp/src/gb.util/.src/String.class b/comp/src/gb.util/.src/String.class new file mode 100644 index 00000000..a68768a1 --- /dev/null +++ b/comp/src/gb.util/.src/String.class @@ -0,0 +1,291 @@ +' Gambas class file + +Export + +Private Const REMOVE_ACC As String = "ÀÁÂÃÄÅĀĂĄǍǞǠȀȂȦȺǺẠẢẬẶḀẮẰẲẴẪẤẦẨ[A]ÈÉÊËĒĖĘĚȄȨɆĔȆḘḜẸẺỆḚẼỄḔḖẾỀỂ[E]ÌÍÎÏĨĪĬĮİƗǏȈȊḬỈỊḮ[I]ŎÒÓÔÖŌŐƠǑǬȌȎȮỌỎỘỚỜỞỢỠȪȰȬÕṌṎṐṒỐỒỔỖ[O]ǗǙǛȖŬŨŮǕÙÚÛÜŪŰŲƯǓȔɄṲṴṶỤỦỨỪỬỮỰƲṺṸ[U]ÝŶŸƳȲɎẎỲỴỶỸỾ[Y]" + "ƁɃḂḄḆ[B]ÇĆĈĊČƇȻḈ[C]ĎÐĐƉḊḌḎḐḒ[D]ƑḞ[F]ĞĜĠĢƓǤǦǴɢʛḠ[G]ĤȞĦḢḤḦḪḨ[H]ĴɈɟʄʝ[J]ĶƘǨḰḲḴ[K]ĹĻĽĿʟŁȽḶḸḺḼ[L]ḾṀṂ[M]ƝÑŃŅŇŊǸṄṆṈṊ[N]ƤṔṖ[P]ŔŖŘȐȒɌṘṚṜṞ[R]ŚŜŞŠȘṠṢṨṤṦ[S]ŢŤŦƮȚȾṪṬṮṰ[T]ṼṾ[V]ŴẀẂẄẆẈ[W]ẊẌ[X]ŹŻŽƵȤẐẒẔ[Z]" + "ÆǢǼ[AE]Œɶ[OE]ßẞ[ss]æǣǽ[ae]œ[oe]" + "àáâãäåāăąǎǟǡǻȁȃȧắằẳẵḁẚạảấầẩậặẫ[a]èéêëēĕėęěȅȇȩɇḕḗḙḛḝẹẻẽếềểệễ[e]īĭìíïîĩįıǐȉȋɨỉḭḯị[i]òóôõöōŏőơǒǫǭȍȏȫȯȱȭṍṏṑṓọỏốồổỗộớờởỡợ[o]ùúûüũūŭůűųưǔǖǘǚǜȕȗṹṳṷṻụủứừửữựṵ[u]ýÿŷƴȳɏỵẏẙỳỷỹỿ[y]" + "ɓƀḃḅḇ[b]çćĉċčƈȼɕḉ[c]ďɖɗđḋḍḏḑḓ[d]ƒḟ[f]ĝğġģǥǧǵɠɡḡ[g]ĥȟɦɧħḣḥḧḫẖḩ[h]ĵǰȷɉ[j]ķƙǩḱḳḵ[k]ĺľŀłļḽḷḹḻ[l]ɱḿṁṃ[m]ñńņňʼnŋǹṅṇṉṋ[n]ƥṕṗ[p]ʠɋ[q]ŕŗřȑȓṙṛṝṟ[r]śŝşšșʂȿṡṣṥṧṩ[s]ţťŧƫƭțṫṭṯṱẗ[t]ʋṽṿ[v]ŵẁẃẅẇẉẘ[w]ẋẍ[x]źżžƶȥɀʐʑẑẓẕ[z]" + +Static Public Sub RemoveDiacritics({String} As String) As String + + If Not {String} Then Return + If IsAscii({String}) Then Return {String} + + Return DoRemoveAccents({String}) + +End + +Static Private Sub DoRemoveAccents(sStr As String) As String + + Dim sCar As String + Dim iPos As Integer + Dim iPosL As Integer + Dim iPosR As Integer + Dim sNewStr As String + Dim I As Integer + Dim L As Integer + + L = 1 + For I = 1 To Len(sStr) + If IsAscii(Mid$(sStr, I, 1)) Then Continue + If I > L Then sNewStr &= Mid$(sStr, L, I - L) + sCar = String.Left(Mid$(sStr, I)) + L = I + Len(sCar) + I = L - 1 + iPos = InStr(REMOVE_ACC, sCar) + If iPos Then + iPosL = InStr(REMOVE_ACC, "[", iPos + 1) + iPosR = InStr(REMOVE_ACC, "]", iPos + 1) + If iPosL <> 0 And If iPosR <> 0 And If iPosL < iPosR Then + sCar = Mid$(REMOVE_ACC, iPosL + 1, iPosR - iPosL - 1) + Endif + Endif + sNewStr &= sCar + + Next + + If I > L Then sNewStr &= Mid$(sStr, L) + + Return sNewStr + +End + +Static Public Sub Distance(String1 As String, String2 As String) As Integer + + Dim L1 As Integer = String.Len(String1) + Dim L2 As Integer = String.Len(String2) + Dim D As New Integer[L1 + 1, L2 + 1] + Dim I, J, C As Integer + Dim C1, C1M, C2, C2M As String + + For I = 0 To L1 + D[I, 0] = I + Next + + For J = 0 To L2 + D[0, J] = J + Next + + For I = 1 To L1 + + C1M = C1 + C1 = String.Mid$(String1, I, 1) + + C2 = "" + + For J = 1 To L2 + + C2M = C2 + C2 = String.Mid$(String2, J, 1) + + If C1 = C2 Then + C = 0 + Else + C = 1 + Endif + + D[I, J] = Min(Min(D[I - 1, J] + 1, D[I, J - 1] + 1), D[I - 1, J - 1] + C) + + If I > 1 And If J > 1 Then + If C1 = C2M And If C1M = C2 Then + D[I, J] = Min(D[I, J], D[I - 2, J - 2] + C) + Endif + Endif + + Next + Next + + Return D[L1, L2] + +End + +Static Public Sub FromHTML(Html As String) As String + + Dim P As Integer + Dim iLen As Integer + Dim C As String + Dim sResult As String + Dim C2 As String + Dim sEntity As String + Dim sMarkup As String + Dim aMarkup As New String[] + + iLen = String.Len(Html) + +READ_TEXT: + + GoSub READ_CHAR + + If C = "<" Then Goto READ_MARKUP + + If C = "&" Then Goto READ_ENTITY + + If aMarkup.Count = 0 Then sResult &= C + Goto READ_TEXT + +READ_MARKUP: + +READ_MARKUP_NAME: + + GoSub READ_CHAR + + If C = " " Then Goto READ_MARKUP_REST + If C = ">" Then Goto READ_MARKUP_END + + sMarkup &= C + + Goto READ_MARKUP_NAME + +READ_MARKUP_REST: + + GoSub READ_CHAR + + If C = ">" Then Goto READ_MARKUP_END + + If C = Chr$(34) Or If C = "'" Then + C2 = C + Goto READ_STRING + Endif + + Goto READ_MARKUP_REST + +READ_MARKUP_END: + + sMarkup = LCase(sMarkup) + If sMarkup Begins "/" Then + sMarkup = Mid$(sMarkup, 2) + If aMarkup.Count Then + If aMarkup[aMarkup.Max] = sMarkup Then aMarkup.Pop() + Endif + Else + If sMarkup = "script" Or If sMarkup = "style" Then + aMarkup.Push(sMarkup) + Else If sMarkup = "br" Then + sResult &= "\n" + Endif + Endif + sMarkup = "" + Goto READ_TEXT + +READ_STRING: + + GoSub READ_CHAR + + If C = C2 Then Goto READ_MARKUP + + Goto READ_STRING + +READ_ENTITY: + + GoSub READ_CHAR + + If IsDigit(C) Or If IsLetter(C) Or If C = "#" Then + sEntity &= C + Goto READ_ENTITY + Endif + + Select Case sEntity + Case "lt" + C = "<" + Case "gt" + C = ">" + Case "amp" + C = "&" + Case "nbsp" + C = String.Chr$(160) + Case "quot" + C = Chr$(34) + Case "apos" + C = "'" + Case Else + If sEntity Begins "#x" Then + Try C = String.Chr(Val("&H" & Mid$(sEntity, 3))) + If Error Then C = "?" + Else If sEntity Begins "#" Then + Try C = String.Chr(CInt(Mid$(sEntity, 2))) + If Error Then C = "?" + Else + C = "&" & sEntity + Dec P + Endif + End Select + + If aMarkup.Count = 0 Then sResult &= C + sEntity = "" + Goto READ_TEXT + +READ_CHAR: + + Inc P + If P > iLen Then + Return sResult + Endif + C = String.Mid$(Html, P, 1) + Return + +End + +Static Public Sub ToPhonetic({String} As String, Optional Language As String = System.Language) As String + + Dim I As Integer + Dim sStr As String + Dim sCar As String + Dim J As Integer + Dim sWord As String + Dim sResult As String + + sStr = Trim({String}) + If Not sStr Then Return + + Try Language = LCase(Split(Language, "-._")[0]) + + Select Case Language + + Case "c", "en" + Error.Raise("Unsupported language") + + Case "fr" + sStr = MPhonetic_French.Before(sStr) + + Default + Error.Raise("Unsupported language") + + End Select + + sStr = LCase(RemoveDiacritics(sStr)) & " " + + For I = 1 To Len(sStr) + + sCar = Mid$(sStr, I, 1) + + If IsLetter(sCar) Or IsDigit(sCar) Then + If J = 0 Then J = I + Continue + Endif + + If J = 0 Then Continue + + sWord = Mid$(sStr, J, I - J) + J = 0 + + If sResult And If Right(sResult) <> " " Then sResult &= " " + Select Case Language + + Case "c", "en" + Error.Raise("Unsupported language") + + Case "fr" + sResult &= MPhonetic_French.Run(sWord) + + Default + Error.Raise("Unsupported language") + + End Select + + Next + + Return sResult + +End diff --git a/comp/src/gb.web.feed/.component b/comp/src/gb.web.feed/.component new file mode 100644 index 00000000..c5196136 --- /dev/null +++ b/comp/src/gb.web.feed/.component @@ -0,0 +1,7 @@ +[Component] +Key=gb.web.feed +Version=3.12.90 +State=1 +Authors=(C) 2017-2019 Tobias Boege +Needs=XML +Requires=gb.xml,gb.util diff --git a/comp/src/gb.web.feed/.directory b/comp/src/gb.web.feed/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/comp/src/gb.web.feed/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/comp/src/gb.web.feed/.hidden/TODO b/comp/src/gb.web.feed/.hidden/TODO new file mode 100644 index 00000000..815805d0 --- /dev/null +++ b/comp/src/gb.web.feed/.hidden/TODO @@ -0,0 +1,10 @@ + o Should I Html$() every string? Or better use CDATA? How to detect when it is necessary? + ~> Adrien said gb.xml will take care of escaping in the future. + + o Rss.ParseDate() ignores the timezone. What should I do and what can I do? + ~> Gambas Dates are always UTC. Probably make an RssDate class which saves a Date and the timezone. + Give it conversion methods. Benoit said these should be provided by gb.(web.)util. + o Should Rss.FormatDate() put the timezone string instead of +nnnn. How? + + o Support Atom. Should there be a basis class which represents the common features of Rss and Atom + so that a user can offer both formats cheaply (without keeping a complete Rss and Atom object around)? diff --git a/comp/src/gb.web.feed/.icon.png b/comp/src/gb.web.feed/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..213a3598f68df49e301f9a304e27809f8f177da3 GIT binary patch literal 12094 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 Vv4Pby55WTfH%+Xs*Ijdu`#&{q5?cTO literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.feed/.lang/nl.mo b/comp/src/gb.web.feed/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..759d32988650beb3cfc466f5240ab53ac1d04a4f GIT binary patch literal 3318 zcmb7_OKclO7{{kjpbb!<<;sa6||R!I29`I2CaQE*y}!bEt&4!HNI3>s>$M7_rjkmzn)$=6lWGzaKsL zslf9b-Y4_H(O1Q)<#;2OyL%ih?5y#535A@DQs8SopBz@Nbvz`sBbJb9N8FM?OV z61d$Pe+gc|_$Tmr@W|ab8$1K@ey@Y4z#Wj|{~WA<-+{c}ZSW*`;-2ihIq){db&&J2 zekk+%9gy>qfIR-QFT7W@$8eBA2s&))Zw_X=?l^J^fl`v_z|zXdrjzk;X0 zzd-`&KJ*575#;^5Am{5VunOJ+4}d&})TA|Lzxm<3piJolv!RD$LQAB%;CSE60O_V;jeV95^bnJCr-udv27^76DyL&CybnJ*pQ-5;;61DQI52sjU-tLZO0dv zjPKc|*VLivN3deLGSq%yMeTf>=~FY)V*k(ZHF4578nU~#rQ6GLHz8zU_{*M&NSh9V zA6gz+<66U0iNZ<#@Z7diTYL7~RWi=Z*5r-bvEB65u4hft~RD+l{8}AcG{Z4m3}L%I2C2Kr9sOQy(Xiq zk5;wW8nbXy?YI@w?l^JXpjWm~Hak2^AoNVE8e_$4mYx--bNkj3+ z60Ir|pwN6xHh0@vWzjCW<4m%S>>HuV^HWQ@^>(gu0eaFcLJ`XY-e)KynHl%o*PLVJ9Tp5(H$ zQ0=;kmEOwL7Cj2b!f;^Oi0;-qnrl3~tT-c~Erb>E&xw>F_2qWZp`P$I2j!G, 2017 +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.web.feed 3.10.90\n" +"PO-Revision-Date: 2017-08-26 19:52 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project: 2 +msgid "Web feed parser and generator" +msgstr "Web feed parser en generator" + +#: Rss.class: 52 +msgid "Date does not conform to RFC 822" +msgstr "Datum conformeert niet met RFC 822" + +#: Rss.class: 75 +msgid "Invalid weekday '&1'" +msgstr "Ongeldige weekdag '&1'" + +#: Rss.class: 76 +msgid "Invalid year '&1'" +msgstr "Ongeldig jaar '&1'" + +#: Rss.class: 77 +msgid "Invalid month '&1'" +msgstr "Ongeldige maand '&1'" + +#: Rss.class: 78 +msgid "Invalid day '&1'" +msgstr "Ongeldige dag '&1'" + +#: Rss.class: 79 +msgid "Invalid minute '&1'" +msgstr "Ongeldige minuut '&1'" + +#: Rss.class:80 +msgid "Invalid second '&1'" +msgstr "Ongeldige seconde '&1'" + +#: Rss.class:85 +msgid "Weekday does not match date: '&1' vs. '&2'" +msgstr "Weekdag komt niet overeen met datum: '&1' vs. '&2'" + +#: Rss.class:285 +msgid "SkipHours may only contain up to 24 elements" +msgstr "SkipHours kan maximaal 24 elementen bevatten" + +#: Rss.class:289 +msgid "SkipHour element must be in the range [0, 23]" +msgstr "SkipHour element dient in het bereik [0, 23] te vallen" + +#: Rss.class:300 +msgid "SkipDays may only contain up to 7 elements" +msgstr "SkipDays mag niet meer dat 7 elementen bevatten" + +#: Rss.class:327 +msgid "Invalid SkipDays day '&1'" +msgstr "Ongeldige SkipDays dag '&1'" + +#: Rss.class:362 +msgid " expected" +msgstr " verwacht" + +#: Rss.class:365 +msgid "End-of-file expected" +msgstr "End-of-file verwacht" + +#: Rss.class:390 +msgid " expected" +msgstr " verwacht" + +#: Rss.class:394 +msgid " expected" +msgstr " verwacht" + +#: Rss.class:453 +msgid "Unexpected element '&1' in " +msgstr "Onverwacht element '&1' in " + +#: Rss.class:474 +msgid " expected" +msgstr " verwacht" + +#: Rss.class:476 +msgid "SkipHours element '&1' out of range [0, 23]" +msgstr "SkipHours element '&1' buiten bereik [0, 23]" + +#: Rss.class:490 +msgid " expected" +msgstr " verwacht" + +#: RssCloud.class:24 +msgid "Domain, Port, Path, RegisterProcedure and Protocol must be set in RssCloud" +msgstr "Domain, Port, Path, RegisterProcedure en Protocol moeten ingesteld zijn in RssCloud" + +#: RssCloud.class:40 +msgid "Invalid RssCloud protocol constant '&1'" +msgstr "Ongeldige RssCloud protocol constante '&1'" + +#: RssCloud.class:52 +msgid "Invalid RssCloud protocol '&1'" +msgstr "Ongeldig RssCloud protocol '&1'" + +#: RssEnclosure.class:15 +msgid "Url, Length and Type must be set in RssEnclosure" +msgstr "Url, Length en Type dienen ingestelt in RssEnclosure" + +#: RssImage.class:37 +msgid "Url, Title and Link must be set in RssImage" +msgstr "Url, Title en Link dienen ingestelt in RssImage" + +#: RssImage.class:44 +msgid "Maximum width of RssImage is 144" +msgstr "Maximale breedte van RssImage is 144" + +#: RssImage.class:48 +msgid "Maximum height of RssImage is 400" +msgstr "Maximale hoogte van RssImage is 400" + +#: RssImage.class:75 +msgid "Unexpected element '&1' in " +msgstr "Onverwacht element '&1' in " + +#: RssItem.class:52 +msgid "Title or Description must be set in RssItem" +msgstr "Title of Description dient ingestelt in RssItem" + +#: RssItem.class:106 +msgid "Unexpected element '&1' in " +msgstr "Onverwacht element '&1' in " + +#: RssSource.class:16 +msgid "Url must be set in RssSource" +msgstr "Url dient ingestelt in RssSource" + +#: RssTextInput.class:21 +msgid "Title, Description, Name and Link must be set in RssTextInput" +msgstr "Title, Description, Name en Link dienen ingestelt in RssTextInput" + +#: RssTextInput.class:47 +msgid "Unexpected element '&1' in " +msgstr "Onverwacht element '&1' in " + diff --git a/comp/src/gb.web.feed/.project b/comp/src/gb.web.feed/.project new file mode 100644 index 00000000..84278822 --- /dev/null +++ b/comp/src/gb.web.feed/.project @@ -0,0 +1,14 @@ +# Gambas Project File 3.0 +Startup=Main +Icon=Feed-icon.svg +Version=3.12.90 +Component=gb.util +Component=gb.xml +Description="Web feed parser and generator" +Authors="(C) 2017-2019 Tobias Boege " +TabSize=2 +Translate=1 +Language=en_US +Type=Component +SourcePath=/tmp +Packager=1 diff --git a/comp/src/gb.web.feed/.src/Main.module b/comp/src/gb.web.feed/.src/Main.module new file mode 100644 index 00000000..93113731 --- /dev/null +++ b/comp/src/gb.web.feed/.src/Main.module @@ -0,0 +1,28 @@ +' Gambas module file + +Public Sub Main() + Dim sRealInFile As String = "test.xml" + Dim sInFile As String = Temp$() + Dim sOutFile As String = Temp$() + Dim sDiff As String + Dim hRss As New Rss + Dim hItem As New RssItem + + Copy sRealInFile To sInFile + hRss.FromString(File.Load(sInFile)) + File.Save(sOutFile, hRss.ToString()) + + System.Shell = "/bin/bash" + Shell Subst$("diff -u <(sort &1 | sed 's/ *$//' | sed 's/^ *//') <(sort &2 | sed 's/^ *//')", sOutFile, sInFile) To sDiff + Print sDiff + Print "---" + + hRss = New Rss + hRss.Title = "Date test" + hRss.Pub = New RssDate(DateAdd(Now, gb.Hour, -11)) + hRss.LastBuild = New RssDate(DateAdd(Now, gb.Hour, -11), "+0800") + hRss.Add(hItem) + hItem.Title = "Blank RssDate" + hItem.Pub = New RssDate + Print hRss.ToString() +End diff --git a/comp/src/gb.web.feed/.src/Rss.class b/comp/src/gb.web.feed/.src/Rss.class new file mode 100644 index 00000000..e8794b90 --- /dev/null +++ b/comp/src/gb.web.feed/.src/Rss.class @@ -0,0 +1,427 @@ +' Gambas class file + +''' This class represents an RSS document. Its properties are those of its single element. +''' It acts like an array of [RssItem](../rssitem) objects. Add your items in order and then call the +''' ToString() method to get the XML document string. +''' +''' The Title, Link and Description are necessary. All other properties are optional. +''' +''' Conforming to the [RSS 2.0 specification](https://cyber.harvard.edu/rss/rss.html). You have to +''' know the document structure and terminology of RSS to make use of this component. + +Export + +Static Private DAY_NAME As String[] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] ' conforms to how WeekDay() works in Gambas +Static Private MONTH_NAME As String[] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + +' Mandatory elements +'' The name or title of the feed. +Public Title As String +'' The URL to the website corresponding to this feed. +Public {Link} As String +'' A description of the feed's contents. +Public Description As String + +' Optional elements +'' The language the feed content is written in. Use one of the [defined](https://cyber.harvard.edu/rss/languages.html) values. +Public Language As String +'' Copyright notice for the feed content. +Public Copyright As String +'' EMail address of the managing editor. +Public ManagingEditor As String +'' EMail address of the web master. +Public WebMaster As String +'' Publication date/timezone of the feed. E.g. a newspaper with daily publication would change this once a day. If unset, defaults to the time the XML document is written in the local timezone. +Public Pub As RssDate +'' The date/timezone of the last change to the feed contents. If unset, defaults to the time the XML document is written in the local timezone. +Public LastBuild As RssDate +'' An array of categories for this feed. +'' +'' ## See also +'' [../../rsscategory] +Public Categories As RssCategory[] +'' The program which generated the feed. +Public Generator As String +'' A link to the RSS specification, to inform future generations of what an RSS file is, if they find one. +Public Docs As String +'' Description of an associated cloud service. +'' +'' ## See also +'' [../../rsscloud] +Public Cloud As RssCloud +'' "Time to live", indicates how long the feed contents may be cached. +Public Ttl As Integer +'' An image associated with the feed. +'' +'' ## See also +'' [../../rssimage] +Public Image As RssImage +'' [PICS](http://www.w3.org/PICS/) rating of the feed. +Public Rating As String +'' Describes a text input box to be displayed with the feed. +'' +'' ## See also +'' [../../rsstextinput] +Public TextInput As RssTextInput +'' A hint for news aggregators. It gives a number of hours in the range of 0 to 23 when no new content is to be expected in the feed. +'' A client may ignore this setting, but can also use it to save traffic. You may only specify up to 24 skip hours. +Public SkipHours As Integer[] +'' A hint for news aggregators. It gives a number of weekdays (gb.Monday, gb.Tuesday, etc.) when no new content is to be expected in +'' the feed. A client may ignore this setting, but can also use it to save traffic. You may only specify up to 7 skip days. +Public SkipDays As Integer[] + +'' Returns the number of RssItems in this document. +Property Read Count As Integer + +Private $aItems As New RssItem[] + +'' Add an RssItem to the document. If ~Index~ is given, the item is inserted at the given +'' position in the array of items. By default it is inserted at the end. +Public Sub Add(Item As RssItem, Optional Index As Integer) + If IsMissing(Index) Then + $aItems.Add(Item) + Else + $aItems.Add(Item, Index) + Endif +End + +Private Function Count_Read() As Integer + Return $aItems.Count +End + +'' Return the RssItem at position ~Index~. +Public Sub _get(Index As Integer) As RssItem + Return $aItems[Index] +End + +'' Replace the RssItem at position ~Index~. +Public Sub _put(Item As RssItem, Index As Integer) + $aItems[Index] = Item +End + +'' Iterate through all RssItems, in the order they are given in the document. +Public Sub _next() As RssItem + Dim iIndex As Integer + Dim hItem As RssItem + + iIndex = 0 + If Not IsNull(Enum.Index) Then iIndex = Enum.Index + If iIndex > $aItems.Max Then + Enum.Stop() + Return + Endif + hItem = $aItems[iIndex] + Enum.Index = iIndex + 1 + Return hItem +End + +'' Remove ~Length~-many RssItems from the item array, beginning at position ~Index~. By default ~Length~ is 1. +Public Sub Remove(Index As Integer, Optional Length As Integer = 1) + $aItems.Remove(Index, Length) +End + +'' Reverse the order of RssItems. +Public Sub Reverse() + $aItems.Reverse() +End + +'' Sort the items by their date. You can pick ~Mode~ as gb.Ascent or gb.Descent. +'' The default is descending sort so that the newest items are first in the document. +Public Sub Sort(Optional Mode As Integer = gb.Descent) + $aItems.Sort(Mode) +End + +'' Clear the Rss object. Sets all properties to Null and clears the item array. +Public Sub Clear() + Title = Null + {Link} = Null + Description = Null + Language = Null + Copyright = Null + ManagingEditor = Null + WebMaster = Null + Pub = Null + LastBuild = Null + Categories = Null + Generator = Null + Docs = Null + Cloud = Null + Ttl = 0 + Image = Null + Rating = Null + TextInput = Null + SkipHours = Null + SkipDays = Null + $aItems.Clear() +End + +'' Write this Rss object as an XML document and return its contents. +Public Function ToString() As String + Dim hWriter As New XmlWriter + + hWriter.Open(Null, True) + _Write(hWriter) + Return hWriter.EndDocument() +End + +Public Sub _Write(hWriter As XmlWriter) + Dim hItem As RssItem + Dim hCat As RssCategory + + hWriter.StartElement("rss", ["version", "2.0"]) + hWriter.StartElement("channel") + With hWriter + .Element("title", Title) + .Element("link", {Link}) + .Element("description", Description) + If Language Then .Element("language", Language) + If Copyright Then .Element("copyright", Copyright) + If ManagingEditor Then .Element("managingEditor", ManagingEditor) + If WebMaster Then .Element("webMaster", WebMaster) + If Pub Then Pub._Write(hWriter, "pubDate") + If LastBuild Then LastBuild._Write(hWriter, "lastBuildDate") + If Categories Then + For Each hCat In Categories + hCat._Write(hWriter) + Next + Endif + If Generator Then .Element("generator", Generator) + If Docs Then .Element("docs", Docs) + If Cloud Then Cloud._Write(hWriter) + If Ttl Then .Element("ttl", Str$(Ttl)) + If Image Then Image._Write(hWriter) + ' For the Rating, see Holzner: "Secrets of RSS", chapter 4 + If Rating Then .Element("rating", Rating) + If TextInput Then TextInput._Write(hWriter) + If SkipHours Then _WriteSkipHours(hWriter) + If SkipDays Then _WriteSkipDays(hWriter) + End With + + For Each hItem In $aItems + hItem._Write(hWriter) + Next + hWriter.EndElement() + hWriter.EndElement() +End + +'' https://cyber.harvard.edu/rss/skipHoursDays.html + +Private Sub _WriteSkipHours(hWriter As XmlWriter) + Dim iHour As Integer + + If Not SkipHours.Count Then Return + If SkipHours.Count > 24 Then Error.Raise(("SkipHours may only contain up to 24 elements")) + With hWriter + .StartElement("skipHours") + For Each iHour In SkipHours + If iHour < 0 Or If iHour > 23 Then Error.Raise(("SkipHour element must be in the range [0, 23]")) + .Element("hour", Str$(iHour)) + Next + .EndElement() + End With +End + +Private Sub _WriteSkipDays(hWriter As XmlWriter) + Dim iDay As Integer + + If Not SkipDays.Count Then Return + If SkipDays.Count > 7 Then Error.Raise(("SkipDays may only contain up to 7 elements")) + With hWriter + .StartElement("skipDays") + For Each iDay In SkipDays + .Element("day", GetSkipDay(iDay)) + Next + .EndElement() + End With +End + +Private Function GetSkipDay(iDay As Integer) As String + Select Case iDay + Case gb.Monday + Return "Monday" + Case gb.Tuesday + Return "Tuesday" + Case gb.Wednesday + Return "Wednesday" + Case gb.Thursday + Return "Thursday" + Case gb.Friday + Return "Friday" + Case gb.Saturday + Return "Saturday" + Case gb.Sunday + Return "Sunday" + End Select + Error.Raise(Subst$(("Invalid SkipDays day '&1'"), iDay)) +End + +Private Function GetDay(sDay As String) As Integer + Select Case sDay + Case "Monday" + Return gb.Monday + Case "Tuesday" + Return gb.Tuesday + Case "Wednesday" + Return gb.Wednesday + Case "Thursday" + Return gb.Thursday + Case "Friday" + Return gb.Friday + Case "Saturday" + Return gb.Saturday + Case "Sunday" + Return gb.Sunday + End Select + Error.Raise(Subst$(("Invalid day '&1'"), sDay)) +End + +'' Read the RSS document in XML format, given in ~Data~, and fill the properties of this Rss object +'' accordingly. +Public Sub FromString(Data As String) + Dim hReader As New XmlReader + + Clear() + hReader.FromString(Data) + _Read(hReader) +End + +Public Sub _Read(hReader As XmlReader) + hReader.Read() + If Not hReader.Node.Name = "rss" Then Error.Raise((" expected")) + _ReadRss(hReader) + hReader.Read() + If Not hReader.Eof Then Error.Raise(("End-of-file expected")) +End + +' This method is to be used as a While condition. In this case, the loop will continue until +' an end tag is found on the same depth as ~iDepth~. +Static Public Function _NotClosed(hReader As XmlReader, iDepth As Integer) As Boolean + ' The depth of the end tag is apparently one less than that of the start tag. + ' TODO: It would be nice if hReader.Node.Name contained the name of an element also when we encounter its closing tag. + Return (hReader.Node.Type <> XmlReaderNodeType.EndElement) Or (hReader.Depth <> iDepth - 1) +End + +Static Public Sub _GetText(hReader As XmlReader) As String + Dim sRes As String + Dim iDepth As Integer = hReader.Depth + + hReader.Read() + While _NotClosed(hReader, iDepth) + If hReader.Node.Type = XmlReaderNodeType.Text Or If hReader.Node.Type = XmlReaderNodeType.CDATA Then sRes &= hReader.Node.Value + hReader.Read() + Wend + Return sRes +End + +Private Sub _ReadRss(hReader As XmlReader) + hReader.Read() + If Not hReader.Node.Name = "channel" Then Error.Raise((" expected")) + _ReadChannel(hReader) + hReader.Read() + ' FIXME: Here, hReader.Depth is 0, instead of -1, as it should be if it was consistent with the observation in _NotClosed() + If hReader.Node.Type <> XmlReaderNodeType.EndElement Or If hReader.Depth <> 0 Then Error.Raise((" expected")) +End + +Private Sub _ReadChannel(hReader As XmlReader) + Dim hCat As RssCategory, aCategories As New RssCategory[] + Dim hItem As RssItem, aItems As New RssItem[] + Dim iDepth As Integer = hReader.Depth + + hReader.Read() + While _NotClosed(hReader, iDepth) + Select Case hReader.Node.Name + Case "title" + Title = _GetText(hReader) + Case "link" + {Link} = _GetText(hReader) + Case "description" + Description = _GetText(hReader) + Case "language" + Language = _GetText(hReader) + Case "copyright" + Copyright = _GetText(hReader) + Case "managingEditor" + ManagingEditor = _GetText(hReader) + Case "webMaster" + WebMaster = _GetText(hReader) + Case "pubDate" + Pub = New RssDate + Pub._Read(hReader) + Case "lastBuildDate" + LastBuild = New RssDate + LastBuild._Read(hReader) + Case "category" + hCat = New RssCategory + hCat._Read(hReader) + aCategories.Add(hCat) + Case "generator" + Generator = _GetText(hReader) + Case "docs" + Docs = _GetText(hReader) + Case "cloud" + Cloud = New RssCloud + Cloud._Read(hReader) + Case "ttl" + Ttl = CInt(_GetText(hReader)) + Case "image" + Image = New RssImage + Image._Read(hReader) + Case "rating" + Rating = _GetText(hReader) + Case "textInput" + TextInput = New RssTextInput + TextInput._Read(hReader) + Case "skipHours" + _ReadSkipHours(hReader) + Case "skipDays" + _ReadSkipDays(hReader) + Case "item" + hItem = New RssItem + hItem._Read(hReader) + aItems.Add(hItem) + Default + Error.Raise(Subst$(("Unexpected element '&1' in "), hReader.Node.Name)) + End Select + ' XXX: Empty tags can be or . If the last thing we read was of the + ' latter kind, we have to Read() one step further to consume its closing tag. If it was + ' a self-closing element, we must not read any further. + ' + ' This is how I check for this currently: we have to be at the end of an element now. + ' XmlReader sets hReader.Node.Type To XmlReaderNodeType.EndElement only if a real + ' ending tag was encountered. A self-closing element is an XmlReaderNodeType.Element. + If hReader.Node.Type = XmlReaderNodeType.EndElement Then hReader.Read() + Wend + If aCategories.Count Then Categories = aCategories + $aItems = aItems +End + +Private Sub _ReadSkipHours(hReader As XmlReader) + Dim iDepth As Integer = hReader.Depth + Dim iHour As Integer, aHours As New Integer[] + + hReader.Read() + While _NotClosed(hReader, iDepth) + If hReader.Node.Name <> "hour" Then Error.Raise((" expected")) + iHour = CInt(_GetText(hReader)) + If iHour < 0 Or If iHour > 23 Then Error.Raise(Subst$(("SkipHours element '&1' out of range [0, 23]"), iHour)) + aHours.Add(iHour) + hReader.Read() + Wend + If aHours.Count > 24 Then Error.Raise(("SkipHours may only contain up to 24 elements")) + If aHours.Count Then SkipHours = aHours +End + +Private Sub _ReadSkipDays(hReader As XmlReader) + Dim iDepth As Integer = hReader.Depth + Dim iDay As Integer, aDays As New Integer[] + + hReader.Read() + While _NotClosed(hReader, iDepth) + If hReader.Node.Name <> "day" Then Error.Raise((" expected")) + iDay = GetDay(_GetText(hReader)) + aDays.Add(iDay) + hReader.Read() + Wend + If aDays.Count > 7 Then Error.Raise(("SkipDays may only contain up to 7 elements")) + If aDays.Count Then SkipDays = aDays +End diff --git a/comp/src/gb.web.feed/.src/RssCategory.class b/comp/src/gb.web.feed/.src/RssCategory.class new file mode 100644 index 00000000..23c2f975 --- /dev/null +++ b/comp/src/gb.web.feed/.src/RssCategory.class @@ -0,0 +1,24 @@ +' Gambas class file + +''' This class represents an RSS category. The RSS specification does not provide a standard +''' set of categories. You can invent your own (site-specific) categories. + +Export + +'' The category description string. Use forward slashes as separators to structure categories hierarchically. +Public Category As String +'' The category domain is an ID for, or a URL to, a category taxonomy description. +Public Domain As String + +Public Sub _Write(hWriter As XmlWriter) + With hWriter + .StartElement("category", IIf(Domain, ["domain", Domain], Null)) + .Text(Category) + .EndElement() + End With +End + +Public Sub _Read(hReader As XmlReader) + Domain = hReader.Node.Attributes["domain"] + Category = Rss._GetText(hReader) +End diff --git a/comp/src/gb.web.feed/.src/RssCloud.class b/comp/src/gb.web.feed/.src/RssCloud.class new file mode 100644 index 00000000..7b9d7da4 --- /dev/null +++ b/comp/src/gb.web.feed/.src/RssCloud.class @@ -0,0 +1,64 @@ +' Gambas class file + +''' This class can be used to specify a web application implementing the rssCloud interface. +''' An rssCloud application notifies its registrees of changes in subscribed RSS channels. +''' +''' See [https://cyber.harvard.edu/rss/soapMeetsRss.html#rsscloudInterface] for an explanation. + +Export + +Public Enum XmlRpc, Soap, HttpPost ' Protocol constants + +'' Domain name of the application server. +Public Domain As String +'' TCP port of the application. +Public Port As Integer +'' The script path on the server. +Public Path As String +'' The name of the registration procedure to be called on the server side. +Public RegisterProcedure As String +'' The rssCloud protocol to use. One of [../xmlrpc], [../soap] or [../httppost]. +Public Protocol As Integer + +Public Sub _Write(hWriter As XmlWriter) + If Not Domain Or If Not Port Or If Not Path Or If Not RegisterProcedure Or If Not Protocol Then Error.Raise(("Domain, Port, Path, RegisterProcedure and Protocol must be set in RssCloud")) + With hWriter + .StartElement("cloud", ["domain", Domain, "port", Str$(Port), "path", Path, "registerProcedure", RegisterProcedure, "protocol", GetProtocol(Protocol)]) + .EndElement() + End With +End + +Private Function GetProtocol(iProto As Integer) As String + Select Case iProto + Case XmlRpc + Return "xml-rpc" + Case Soap + Return "soap" + Case HttpPost + Return "http-post" + End Select + Error.Raise(Subst$(("Invalid RssCloud protocol constant '&1'"), iProto)) +End + +Private Function GetProtocolConst(sProto As String) As Integer + Select Case sProto + Case "xml-rpc" + Return XmlRpc + Case "soap" + Return Soap + Case "http-post" + Return HttpPost + End Select + Error.Raise(Subst$(("Invalid RssCloud protocol '&1'"), sProto)) +End + +Public Sub _Read(hReader As XmlReader) + With hReader.Node.Attributes + Domain = .["domain"] + Port = CInt(.["port"]) + Path = .["path"] + RegisterProcedure = .["registerProcedure"] + Protocol = GetProtocolConst(.["protocol"]) + End With + hReader.Read() +End diff --git a/comp/src/gb.web.feed/.src/RssDate.class b/comp/src/gb.web.feed/.src/RssDate.class new file mode 100644 index 00000000..00a72ee0 --- /dev/null +++ b/comp/src/gb.web.feed/.src/RssDate.class @@ -0,0 +1,38 @@ +' Gambas class file + +''' This class represents a date in RSS. It is comprised of a Gambas Date and +''' a Zone timezone string. Since dates are absolute in Gambas, the timezone is +''' only used during reading and writing the XML document backing an RSS feed. +''' It does not have to "applied" to the Date in any way by the user of this component. + +Export + +' We have a member named "Date", which overshadows the global class of the same name. +' Get a reference to it here. +Static Private DateClass As Object = Classes["Date"].New() + +'' The date represented by this object. +Public {Date} As Date +'' The timezone relative to which the Date was or should be printed. +Public Zone As String + +Public Sub _new(Optional {Date} As Date, Optional Zone As String) + ' Now in the local timezone by default + If IsMissing({Date}) Then {Date} = Now + If IsMissing(Zone) Then Zone = Format$(Now, "tt") + Me.Date = {Date} + Me.Zone = Zone +End + +Public Sub _Read(hReader As XmlReader) + {Date} = DateClass.FromRFC822(Rss._GetText(hReader), ByRef Zone) +End + +' The sTag parameter avoids subclassing RssDate to RssPubDate and RssLastBuildDate. +Public Sub _Write(hWriter As XmlWriter, sTag As String) + With hWriter + .StartElement(sTag) + .Text(DateClass.ToRFC822(Me.Date, Me.Zone)) + .EndElement() + End With +End diff --git a/comp/src/gb.web.feed/.src/RssEnclosure.class b/comp/src/gb.web.feed/.src/RssEnclosure.class new file mode 100644 index 00000000..ca6b0733 --- /dev/null +++ b/comp/src/gb.web.feed/.src/RssEnclosure.class @@ -0,0 +1,29 @@ +' Gambas class file + +''' This class represents a media attachment to an RSS item. + +Export + +'' The URL of the attached file. +Public Url As String +'' The length of the file in bytes. +Public Length As Long +'' The MIME type of the file. +Public Type As String + +Public Sub _Write(hWriter As XmlWriter) + If Not Url Or If Not Length Or If Not Type Then Error.Raise(("Url, Length and Type must be set in RssEnclosure")) + With hWriter + .StartElement("enclosure", ["url", Url, "type", Type, "length", Str$(Length)]) + .EndElement() + End With +End + +Public Sub _Read(hReader As XmlReader) + With hReader.Node.Attributes + Url = .["url"] + Length = CLong(.["length"]) + Type = .["type"] + End With + hReader.Read() +End diff --git a/comp/src/gb.web.feed/.src/RssGuid.class b/comp/src/gb.web.feed/.src/RssGuid.class new file mode 100644 index 00000000..6ae27913 --- /dev/null +++ b/comp/src/gb.web.feed/.src/RssGuid.class @@ -0,0 +1,37 @@ +' Gambas class file + +''' This class represents a GUID (**G**lobally **U**nique **ID**entifier) of an RSS item. +''' The RSS specification does not prescribe a particular format. A news aggregator may use +''' this property, if present, to determine whether the item is new. +''' +''' By default the [IsPermaLink] property is True, meaning permalinks are the default way +''' to assign the item a GUID. + +Export + +'' The GUID string. +Public Guid As String +'' Returns or sets if this GUID is a [permalink](https://en.wikipedia.org/wiki/Permalink). +'' The default value of this property is True. +Public IsPermaLink As Boolean + +Public Sub _new() + IsPermaLink = True +End + +Public Sub _Write(hWriter As XmlWriter) + With hWriter + .StartElement("guid", IIf(IsPermaLink, ["isPermaLink", "true"], Null)) + .Text(Guid) + .EndElement() + End With +End + +Public Sub _Read(hReader As XmlReader) + If hReader.Node.Attributes.Exist("isPermaLink") Then + IsPermaLink = (hReader.Node.Attributes["isPermaLink"] = "true") + Else + IsPermaLink = False + Endif + Guid = Rss._GetText(hReader) +End diff --git a/comp/src/gb.web.feed/.src/RssImage.class b/comp/src/gb.web.feed/.src/RssImage.class new file mode 100644 index 00000000..cebf51c7 --- /dev/null +++ b/comp/src/gb.web.feed/.src/RssImage.class @@ -0,0 +1,79 @@ +' Gambas class file + +''' This class represents an image which is associated with an RSS feed. +''' The Title and Link properties should be the same as the corresponding ones of the +''' parent Rss object. + +Export + +'' The URL of the image file. The specification requires this to be a GIF, JPEG or PNG file. +Public Url As String +'' An image title. It is to be used as the 'alt' attribute of the '' HTML tag if the feed is rendered as HTML. +'' +'' ## See also +'' [../link], [../description] +Public Title As String +'' The URL of the website the feed belongs to. When the feed is rendered as HTML the RssImage becomes a link to this website. +'' +'' ## See also +'' [../title], [../description] +Public {Link} As String +'' The width of the image. The maximum width is 144, the default is 88. +Public Width As Integer +'' The height of the image. The maximum height is 400, the default is 31. +Public Height As Integer +'' If the feed is rendered as HTML this string is to be become the 'title' attribute of the link rendered around the image. +'' +'' ## See also +'' [../title], [../link] +Public Description As String + +Public Sub _new() + Width = 88 + Height = 31 +End + +Public Sub _Write(hWriter As XmlWriter) + If Not Url Or If Not Title Or If Not {Link} Then Error.Raise(("Url, Title and Link must be set in RssImage")) + With hWriter + .StartElement("image") + .Element("url", Url) + .Element("title", Title) + .Element("link", {Link}) + If Width Then + If Width > 144 Then Error.Raise(("Maximum width of RssImage is 144")) + .Element("width", Str$(Width)) + Endif + If Height Then + If Height > 400 Then Error.Raise(("Maximum height of RssImage is 400")) + .Element("height", Str$(Height)) + Endif + If Description Then .Element("description", Description) + .EndElement() + End With +End + +Public Sub _Read(hReader As XmlReader) + Dim iDepth As Integer = hReader.Depth + + hReader.Read() + While Rss._NotClosed(hReader, iDepth) + Select Case hReader.Node.Name + Case "url" + Url = Rss._GetText(hReader) + Case "title" + Title = Rss._GetText(hReader) + Case "link" + {Link} = Rss._GetText(hReader) + Case "width" + Width = CInt(Rss._GetText(hReader)) + Case "height" + Height = CInt(Rss._GetText(hReader)) + Case "description" + Description = Rss._GetText(hReader) + Default + Error.Raise(Subst$(("Unexpected element '&1' in "), hReader.Node.Name)) + End Select + hReader.Read() + Wend +End diff --git a/comp/src/gb.web.feed/.src/RssItem.class b/comp/src/gb.web.feed/.src/RssItem.class new file mode 100644 index 00000000..806030c7 --- /dev/null +++ b/comp/src/gb.web.feed/.src/RssItem.class @@ -0,0 +1,113 @@ +' Gambas class file + +''' This class represents a single news item. Create an object of this class, fill its properties +''' and add it to an existing [Rss](../rss) object. +''' +''' All properties of this class are optional, but you have to fill at least Title *or* Description. + +Export + +'' The title of the news item. +Public Title As String +'' A link to the website containing the full content. +Public {Link} As String +'' Synopsis of the item. You can use entity-encoded HTML. +Public Description As String +'' EMail address of the author. +Public Author As String +'' An array of categories for this item. +'' +'' ## See also +'' [../../rsscategory] +Public Categories As RssCategory[] +'' A URL pointing to a comment page for the item. +Public Comments As String +'' Describes a media attachment to this item. +'' +'' ## See also +'' [../../rssenclosure] +Public Enclosure As RssEnclosure +'' A **G**lobally **U**nique **ID**entifier for this item, e.g. a [permalink](https://en.wikipedia.org/wiki/Permalink) +'' to the item's full content on your website. +'' +'' ## See also +'' [../../rssguid] +Public Guid As RssGuid +'' Publication date/timezone of this item. If unset, defaults to the time the XML document is written in the local timezone. +Public Pub As RssDate +'' If this item comes from another RSS feed, use this property to link to the original feed. +'' +'' ## See also +'' [../../rsssource] +Public Source As RssSource + +Public Sub _compare(Other As RssItem) As Integer + ' Only up to seconds. The time formatting in RSS is not any finer anyway. + Return Sgn(DateDiff(Other.Pub.Date, Pub.Date, gb.Second)) +End + +Public Sub _Write(hWriter As XmlWriter) + Dim hCat As RssCategory + + If Not Title And If Not Description Then Error.Raise(("Title or Description must be set in RssItem")) + hWriter.StartElement("item") + With hWriter + If Title Then .Element("title", Title) + If {Link} Then .Element("link", {Link}) + If Description Then .Element("description", Description) + If Author Then .Element("author", Author) + If Categories Then + For Each hCat In Categories + hCat._Write(hWriter) + Next + Endif + If Comments Then .Element("comments", Comments) + If Enclosure Then Enclosure._Write(hWriter) + If Guid Then Guid._Write(hWriter) + If Pub Then Pub._Write(hWriter, "pubDate") + If Source Then Source._Write(hWriter) + End With + hWriter.EndElement() +End + +Public Sub _Read(hReader As XmlReader) + Dim hCat As RssCategory, aCategories As New RssCategory[] + Dim iDepth As Integer = hReader.Depth + + hReader.Read() + While Rss._NotClosed(hReader, iDepth) + Select Case hReader.Node.Name + Case "title" + Title = Rss._GetText(hReader) + Case "link" + {Link} = Rss._GetText(hReader) + Case "description" + Description = Rss._GetText(hReader) + Case "author" + Author = Rss._GetText(hReader) + Case "category" + hCat = New RssCategory + hCat._Read(hReader) + aCategories.Add(hCat) + Case "comments" + Comments = Rss._GetText(hReader) + Case "enclosure" + Enclosure = New RssEnclosure + Enclosure._Read(hReader) + Case "guid" + Guid = New RssGuid + Guid._Read(hReader) + Case "pubDate" + Pub = New RssDate + Pub._Read(hReader) + Case "source" + Source = New RssSource + Source._Read(hReader) + Default + Error.Raise(Subst$(("Unexpected element '&1' in "), hReader.Node.Name)) + End Select + ' XXX: See Rss._ReadChannel() for this construction. + If hReader.Node.Type = XmlReaderNodeType.EndElement Then hReader.Read() + Wend + If aCategories.Count Then Categories = aCategories +End diff --git a/comp/src/gb.web.feed/.src/RssSource.class b/comp/src/gb.web.feed/.src/RssSource.class new file mode 100644 index 00000000..b7debc58 --- /dev/null +++ b/comp/src/gb.web.feed/.src/RssSource.class @@ -0,0 +1,27 @@ +' Gambas class file + +''' This class describes a foreign source of an RSS item which you included in your RSS feed. + +Export + +'' The feed title of the source RSS feed. +'' +'' ## See also +'' [Rss.Title](../../rss/title) +Public Source As String +'' Link to the foreign RSS feed's XML document. +Public Url As String + +Public Sub _Write(hWriter As XmlWriter) + If Not Url Then Error.Raise(("Url must be set in RssSource")) + With hWriter + .StartElement("source", ["url", Url]) + .Text(Source) + .EndElement() + End With +End + +Public Sub _Read(hReader As XmlReader) + Url = hReader.Node.Attributes["url"] + Source = Rss._GetText(hReader) +End diff --git a/comp/src/gb.web.feed/.src/RssTextInput.class b/comp/src/gb.web.feed/.src/RssTextInput.class new file mode 100644 index 00000000..852d9553 --- /dev/null +++ b/comp/src/gb.web.feed/.src/RssTextInput.class @@ -0,0 +1,51 @@ +' Gambas class file + +''' This class represents a text input field which the news aggregator may display +''' with your feed. The [RSS specification](https://cyber.harvard.edu/rss/rss.html#lttextinputgtSubelementOfLtchannelgt) +''' admits that this element's purpose is unclear and that it may not be widely supported. +''' It can be used as a built-in mechanism to provide feedback to the feed owner's website +''' or as a search box. + +Export + +'' The label of the submit button next to the input box. +Public Title As String +'' Explanation of the input box's purpose. +Public Description As String +'' The name of the input box, for use by the CGI script the input is sent to. +Public Name As String +'' Link to the CGI script which processes the input. +Public {Link} As String + +Public Sub _Write(hWriter As XmlWriter) + If Not Title Or If Not Description Or If Not Name Or If Not {Link} Then Error.Raise(("Title, Description, Name and Link must be set in RssTextInput")) + With hWriter + .StartElement("textInput") + .Element("title", Title) + .Element("description", Description) + .Element("name", Name) + .Element("link", {Link}) + .EndElement() + End With +End + +Public Sub _Read(hReader As XmlReader) + Dim iDepth As Integer = hReader.Depth + + hReader.Read() + While Rss._NotClosed(hReader, iDepth) + Select Case hReader.Node.Name + Case "title" + Title = Rss._GetText(hReader) + Case "description" + Description = Rss._GetText(hReader) + Case "name" + Name = Rss._GetText(hReader) + Case "link" + {Link} = Rss._GetText(hReader) + Default + Error.Raise(Subst$(("Unexpected element '&1' in "), hReader.Node.Name)) + End Select + hReader.Read() + Wend +End diff --git a/comp/src/gb.web.feed/Feed-icon.svg b/comp/src/gb.web.feed/Feed-icon.svg new file mode 100644 index 00000000..11fd98a5 --- /dev/null +++ b/comp/src/gb.web.feed/Feed-icon.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/comp/src/gb.web.feed/test.xml b/comp/src/gb.web.feed/test.xml new file mode 100644 index 00000000..7f981ba3 --- /dev/null +++ b/comp/src/gb.web.feed/test.xml @@ -0,0 +1,41 @@ + + + + Liftoff News + http://liftoff.msfc.nasa.gov/ + Liftoff to Space Exploration. + en-us + Tue, 10 Jun 2003 04:00:00 GMT + Tue, 10 Jun 2003 09:41:01 GMT + http://blogs.law.harvard.edu/tech/rss + Weblog Editor 2.0 + editor@example.com + webmaster@example.com + + Star City + http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp + How do Americans get ready to work with Russians aboard the International Space Station? They take a crash course in culture, language and protocol at Russia's <a href="http://howe.iki.rssi.ru/GCTC/gctc_e.htm">Star City</a>. + Tue, 03 Jun 2003 09:39:21 GMT + http://liftoff.msfc.nasa.gov/2003/06/03.html#item573 + + + Sky watchers in Europe, Asia, and parts of Alaska and Canada will experience a <a href="http://science.nasa.gov/headlines/y2003/30may_solareclipse.htm">partial eclipse of the Sun</a> on Saturday, May 31st. + Fri, 30 May 2003 11:06:42 GMT + http://liftoff.msfc.nasa.gov/2003/05/30.html#item572 + + + The Engine That Does More + http://liftoff.msfc.nasa.gov/news/2003/news-VASIMR.asp + Before man travels to Mars, NASA hopes to design new engines that will let us fly through the Solar System more quickly. The proposed VASIMR engine would do that. + Tue, 27 May 2003 08:37:32 GMT + http://liftoff.msfc.nasa.gov/2003/05/27.html#item571 + + + Astronauts' Dirty Laundry + http://liftoff.msfc.nasa.gov/news/2003/news-laundry.asp + Compared to earlier spacecraft, the International Space Station has many luxuries, but laundry facilities are not one of them. Instead, astronauts have other options. + Tue, 20 May 2003 08:56:02 GMT + http://liftoff.msfc.nasa.gov/2003/05/20.html#item570 + + + \ No newline at end of file diff --git a/comp/src/gb.web.form/.component b/comp/src/gb.web.form/.component new file mode 100644 index 00000000..adb61029 --- /dev/null +++ b/comp/src/gb.web.form/.component @@ -0,0 +1,7 @@ +[Component] +Key=gb.web.form +Version=3.12.90 +State=1 +Authors=Benoît Minisini +Requires=gb.web,gb.util.web,gb.util +Excludes=gb.gui,gb.gui.qt,gb.gtk,gb.gtk3,gb.qt4,gb.qt5,gb.sdl2,gb.sdl diff --git a/comp/src/gb.web.form/.directory b/comp/src/gb.web.form/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/comp/src/gb.web.form/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/comp/src/gb.web.form/.hidden/Uncompressed/gw-style.css b/comp/src/gb.web.form/.hidden/Uncompressed/gw-style.css new file mode 100644 index 00000000..549123a6 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/Uncompressed/gw-style.css @@ -0,0 +1,504 @@ +HTML, BODY { + margin: 0; + padding: 0; + height: 100%; + width: 100%; +} + +HTML, BODY, DIV { + box-sizing: border-box; +} + +SELECT { + min-height: 2rem; + font-size: inherit; +} + +INPUT { + font: inherit; +} + +H1,H2,H3,P { + margin-top: 0.5rem; + margin-bottom: 0.25rem; +} + +/*UL, OL { + padding-left: 2em; + margin-bottom: 0; +} + +P:first-child,UL:first-child,OL:first-child { + margin-top: 0; +}*/ + +.gw-button { + padding: 0; + font: inherit; + padding: 0 0.25em; + /*min-height: 1rem;*/ +} + +.gw-button.gw-noborder { + border: solid 1px transparent; + background: none; + padding: 0; +} + +.gw-button.gw-noborder:hover { + border: solid 1px #C0C0C0; +} + +.gw-button-image { + margin-right: 0.5em; +} + +.gw-button > IMG { + vertical-align: middle; +} + +.gw-button > DIV { + display: inline-block; + vertical-align: middle; +} + +.gw-tab-header { + margin-bottom: -1px; + z-index: 1; + line-height: 2em; +} + +.gw-tab-header.gw-noborder { + margin-bottom: 0; +} + +.gw-tab { + color: gray; + padding: 0 0.5em; + cursor: pointer; + border-top: solid transparent 1px; + border-right: solid transparent 1px; + border-left: solid transparent 1px; +} + +.gw-tab:hover { + color: black; +} + +.gw-tab-selected { + padding: 0 0.5em; + border-left: solid #C0C0C0 1px; + border-top: solid #C0C0C0 1px; + border-right: solid #C0C0C0 1px; + border-top-left-radius: 0.5em; + border-top-right-radius: 0.5em; + background: white; +} + +.gw-tab-header.gw-noborder > div { + border: none; +} + +.gw-tab-contents { + border: solid #C0C0C0 1px; + flex-grow: 1; + display: flex; + flex-flow: column; +} + +.gw-tab-contents > DIV { + flex-grow: 1; +} + +.gw-tab-contents.gw-noborder { + border: none; +} + +.gw-checkbox-label,.gw-radiobutton-label { + vertical-align: middle; +} + +.gw-checkbox-toggle,.gw-radiobutton-toggle { + vertical-align: middle; + margin: 0; + margin-right: 0.5em; +} + +.gw-textbox { + min-height: 2rem; + box-sizing: border-box; + padding: 0 0.25em; +} +.gw-textbox.gw-noborder { + border: none; +} + +.gw-combobox { + display: inline-block; + position: relative; + min-height: 2rem; +} + +.gw-combobox SELECT { + height: 100%; + z-index: 1; + opacity: 0; + float: right; +} + +.gw-combobox INPUT { + width: 100%; + min-height: 2rem; + box-sizing: border-box; + padding: 0 2.25em 0 0.25em; + vertical-align: middle; +} + +.gw-combobox.gw-noborder INPUT { + border: none; +} + +.gw-combobox-arrow { + display: inline-block; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAI0lEQVQYlWNgoAZoYGBg+I8DN+BT1MCABhrwSSIrwilJHgAAbCcP9dwIQGIAAAAASUVORK5CYII='); + background-position: center; + background-repeat: no-repeat; + height: 2rem; + width: 2rem; + margin-left: -2rem; + /*pointer-events: none;*/ + vertical-align: middle; + border: none; + overflow: hidden; +} + +.gw-selectbox { + display: inline-block; + position: relative; + min-height: 2rem; +} + +.gw-selectbox SELECT { + width: 100%; +} + +.gw-selectbox.gw-noborder SELECT { + border: none; +} + +.gw-window { + position: fixed; + border: solid 1px white; + box-shadow: 0 0 0.5em black; + border-radius: 0.5em; + background: white; + z-index: 10; + overflow: hidden; +} + +.gw-window-titlebar { + display: flex; + flex-flow: row; + background: #2980B9; + font-weight: bold; + height: 2em; + border-top-left-radius: 0.5em; + border-top-right-radius: 0.5em; +} + +.gw-window-title { + flex-grow: 1; + color: white; + line-height: 2em; + padding-left: 0.5em; + pointer-events: none; +} + +.gw-window-button { + position: absolute; + padding: 0; + width: 1.5em; + height: 1.5em; + top: 0.25em; + right: 0.25em; +} + +/*@-moz-document url-prefix() { + .gw-window-button { + display: inline-flex; + }; +}*/ + +.gw-window-contents { + display: flex; + flex-flow: column; + flex-grow: 1; + padding: 0.5em; +} + +.gw-label { + display: flex; + align-items: center; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +#gw-modal { + display: none; + position: fixed; + left: 0; + right: 0; + width: 100%; + height: 100%; + background: black; + opacity: 0.05; +} + +.gw-textarea { + font-family: inherit; + resize: none; +} + +.gw-textarea.gw-noborder { + border: none; +} + + +.gw-menu-title { + display: inline-table; + padding: 0.5em; + padding-top: 0.25em; + cursor: default; + border-top: solid 1px transparent; + border-left: solid 1px transparent; + border-right: solid 1px transparent; + /*margin-top: 0.25em; + margin-bottom: 0.25em;*/ +} + +.gw-menu:hover > .gw-menu-title { + border-top: solid 1px #C0C0C0; + border-left: solid 1px #C0C0C0; + border-right: solid 1px #C0C0C0; + box-shadow: 0 0 0.25em #C0C0C0; + background: white; + z-index: 1002; +} + +.gw-submenu { + display: none; + position: absolute; + border: solid 1px #C0C0C0; + background: white; + box-shadow: 0 0.125em 0.25em #C0C0C0; + margin-top: -0.25em; + z-index: 1003; +} + +.gw-menuitem > .gw-menu > .gw-submenu { + margin-top: 0; + top: 0; + left: 100%; +} + +.gw-menu:hover > .gw-submenu { + display: table; +} + +.gw-menu-tape { + display: none; + position: relative; + background: white; + z-index: 1004; + height: 4px; + margin-top: -4px; + margin-left: 1px; + margin-right: 1px; + top: -2px; +} + +.gw-menu:hover > .gw-menu-tape { + display: block; +} + +.gw-submenu > div:hover { + background: #E0E0E0; +} + +.gw-menuitem { + display: flex; + flex-flow: column; +} + +.gw-menuitem > div { + display: table-cell !important; + vertical-align: middle; +} + +.gw-menuitem-text { + flex-grow: 1; + padding: 0.125em 0.25em; + white-space: nowrap; +} + +.gw-menuitem-icon { + padding: 0.125em 0.25em; +} + +.gw-menuitem-icon > IMG { + vertical-align: middle; +} + +.gw-menuitem-shortcut { + text-align: right; + padding: 0.125em 1em; +} + +.gw-separator { + position: relative; + padding: 0 !important; + pointer-events: none; +} + +.gw-separator-hline { + position: absolute; + display: flex; + flex-flow: row; + align-items: center; + height: inherit; + width: 100%; +} + +.gw-separator-hline > div { + height: 1px; + width: 100%; + background: #C0C0C0; +} + +.gw-separator-vline { + position: absolute; + display: flex; + flex-flow: column; + align-items: center; + height: 100%; + width: inherit; +} + +.gw-separator-vline > div { + width: 1px; + height: 100%; + background: #C0C0C0; +} + +.gw-submenu > div { + display: flex !important; + cursor: default; + padding: 0.25em 1em; + margin: 0; +} + +.gw-submenu > .gw-menuitem { + display: table-row !important; + table-layout: fixed; + cursor: default; + padding: 0; + margin: 0; +} + +.gw-submenu > .gw-separator { + display: table-row !important; + cursor: default; + margin: 0; + width: auto; + height: 1em; +} + +.gw-menuitem > .gw-menu { + display: table-cell !important; + position: relative; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAJElEQVQYlWNgQIAGBgLgPyFF/wkp+o9NERMhe0m2AqckAy5JALuADHot/KmNAAAAAElFTkSuQmCC'); + background-position: center; + background-repeat: no-repeat; + width: 1em; +} + +.gw-spinbox.gw-noborder { + border: none; +} + +.gw-expander-header + DIV { + margin-top: 0.5em; +} + +.gw-expander-header > IMG { + vertical-align: middle; +} + +.gw-expander-header > DIV { + display: inline-table; + vertical-align: middle; +} + +.gw-expander-border { + border: solid 1px #C0C0C0; + padding: 0.5em; +} + +.gw-table { + position: relative; + overflow: auto; + min-height: 4em; + border: solid 1px #C0C0C0; +} + +.gw-table.gw-noborder { + border: none; +} + +/*.gw-table-header { + height: 1.5em; + background-color: yellow; +}*/ + +.gw-table-contents { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.gw-table > DIV > TABLE { + border-collapse: collapse; +} + +.gw-table > DIV > TABLE > THEAD > TR > TH { + text-align: left; + background-color: #E0E0E0; + padding: 0.25em 0.5em; + vertical-align: top; + border-bottom: solid 1px #C0C0C0; +} + +.gw-table > DIV > TABLE > THEAD > TR > TH:last-child { + border-right: none; +} + +.gw-table > DIV > TABLE > TBODY > TR > TD { + /*border-right: solid 1px #C0C0C0;*/ + padding: 0.25em 0.5em; + vertical-align: top; +} + +/*.gw-table > DIV > TABLE > TBODY > TR > TD:last-child { + border-right: none; +} + +.gw-table > DIV > TABLE > TBODY > TR:last-child > TD { + border-bottom: solid 1px #C0C0C0; +}*/ + +.gw-table > DIV > TABLE > TBODY > TR:nth-child(even) { + background-color: #F0F0F0; +} diff --git a/comp/src/gb.web.form/.hidden/Uncompressed/lib.js b/comp/src/gb.web.form/.hidden/Uncompressed/lib.js new file mode 100644 index 00000000..164ffbd9 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/Uncompressed/lib.js @@ -0,0 +1,1321 @@ +function $(a) +{ + return document.getElementById(a); +} + +if (!String.prototype.endsWith) +{ + String.prototype.endsWith = function(searchString, position) + { + var subjectString = this.toString(); + if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { + position = subjectString.length; + } + position -= searchString.length; + var lastIndex = subjectString.indexOf(searchString, position); + return lastIndex !== -1 && lastIndex === position; + }; +} + +Element.prototype.hasClass = function(klass) +{ + if (this.classList) + return this.classList.contains(klass); + else + return !!this.className.match(new RegExp('(\\s|^)' + klass + '(\\s|$)')); +}; + +Element.prototype.addClass = function(klass) +{ + if (this.classList) + this.classList.add(klass); + else if (!this.hasClass(klass)) + this.className += " " + klass; +}; + +Element.prototype.removeClass = function(klass) +{ + if (this.classList) + this.classList.remove(klass); + else if (this.hasClass(klass)) + { + var reg = new RegExp('(\\s|^)' + klass + '(\\s|$)'); + this.className = this.className.replace(reg, ' '); + } +}; + +/*Element.prototype.ensureVisible = function() +{ + var parent = this.offsetParent; + + while (parent && parent.clientHeight == parent.scrollHeight && parent.clientWidth == parent.scrollWidth) + parent = parent.offsetParent; + + if (parent) + gw.ensureVisible(this.offsetParent, this.offsetLeft, this.offsetTop, this.offsetWidth, this.offsetHeight); +};*/ + +gw = { + + version: '0', + commands: [], + timers: {}, + windows: [], + form: '', + debug: false, + loaded: {}, + uploads: {}, + autocompletion: [], + focus: false, + lock: 0, + + log: function(msg) + { + if (gw.debug) + { + if (gw.startTime == undefined) + gw.startTime = Date.now(); + console.log(((Date.now() - gw.startTime) / 1000).toFixed(3) + ': ' + msg); + } + }, + + load: function(lib) + { + var elt, src; + + if (gw.loaded[lib]) + return; + + if (lib.endsWith('.js')) + { + elt = document.createElement('script'); + elt.setAttribute("type", "text/javascript"); + src = $root + '/lib:' + lib.slice(0, -3) + ':' + gw.version + '.js'; + elt.setAttribute("src", src); + } + else if (lib.endsWith('.css')) + { + elt = document.createElement('link'); + elt.setAttribute("rel", "stylesheet"); + elt.setAttribute("type", "text/css"); + src = $root + '/style:' + lib.slice(0, -4) + ':' + gw.version + '.css'; + elt.setAttribute("href", src); + } + else + return; + + document.getElementsByTagName("head")[0].appendChild(elt); + gw.loaded[lib] = src; + console.log('load: ' + src); + }, + + setInnerHtml : function(id, html) + { + var oldDiv = $(id); + var newDiv = oldDiv.cloneNode(false); + newDiv.innerHTML = html; + oldDiv.parentNode.replaceChild(newDiv, oldDiv); + }, + + setOuterHtml : function(id, html) + { + if ($(id)) + $(id).outerHTML = html; + else + console.log('setOuterHtml: ' + id + '? ' + html); + }, + + removeElement : function(id) + { + var elt = $(id); + //for (i = 0; i < id_list.length; i++) + //{ + //elt = $(id_list[i]); + if (!elt) + return; + + //console.log(id + " removed"); + + elt.parentNode.removeChild(elt); + //} + }, + + insertElement : function(id, parent) + { + var elt = document.createElement('div'); + elt.id = id; + + $(parent).appendChild(elt); + }, + + setVisible : function(id, visible) + { + var elt = $(id); + if (elt) + { + if (visible) + elt.removeClass('gw-hidden'); + else + elt.addClass('gw-hidden'); + } + }, + + saveFocus: function() { + var active = document.activeElement.id; + var selection; + + if (active) + selection = gw.getSelection($(active)); + + return [active, selection]; + }, + + restoreFocus: function(save) { + var elt; + + if (save[0]) + { + elt = $(save[0]) + if (elt) + { + elt.focus(); + gw.setSelection(elt, save[1]); + } + } + //else + // gw.active = document.activeElement.id; + }, + + wait: function(lock) { + var elt; + + if (lock) + { + if (gw.lock == 0) + { + elt = $('gw-lock'); + elt.style.zIndex = 1000; + elt.style.display = 'block'; + + gw.lock_id = setTimeout(function() { + $('gw-lock').style.opacity = '1'; + }, 500); + } + + gw.lock++; + } + else + { + gw.lock--; + if (gw.lock == 0) + { + if (gw.lock_id) + { + clearTimeout(gw.lock_id); + gw.lock_id = undefined; + } + elt = $('gw-lock'); + elt.style.display = 'none'; + elt.style.opacity = '0'; + } + } + }, + + answer: function(xhr, after) + { + if (xhr.readyState == 4) + { + if (xhr.status == 200 && xhr.responseText) + { + xhr.gw_command && gw.log('==> ' + xhr.gw_command + '...'); + + gw.focus = false; + var save = gw.saveFocus(); + + /*if (gw.debug) + console.log('--> ' + xhr.responseText);*/ + + var r = xhr.responseText.split('\n'); + var i, expr; + + for (i = 0; i < r.length; i++) + { + expr = r[i].trim(); + if (expr.length == 0) + continue; + if (gw.debug) + { + if (expr.length > 1024) + gw.log('--> ' + expr.substr(0, 1024) + '...'); + else + gw.log('--> ' + expr); + } + eval(expr); + } + + //eval(xhr.responseText); + + if (!gw.focus) + gw.restoreFocus(save); + + } + + if (after) + after(); + + xhr.gw_command && gw.log('==> ' + xhr.gw_command + ' done.'); + + if (xhr.gw_command && (xhr.gw_command.length < 5 || xhr.gw_command[4] == undefined || xhr.gw_command[4] == false)) + gw.wait(false); + + gw.commands.splice(0, 2); + gw.sendNewCommand(); + } + }, + + sendNewCommand: function() + { + var command, after, len; + var xhr; + + for(;;) { + + len = gw.commands.length; + + if (len < 2) + return; + + command = gw.commands[0]; + after = gw.commands[1]; + + gw.log('[ ' + command + ' ]'); + + if (command) + { + if (command.length < 5 || command[4] == undefined || command[4] == false) + gw.wait(true); + + xhr = new XMLHttpRequest(); + xhr.gw_command = command; + xhr.open('GET', $root + '/x?c=' + encodeURIComponent(JSON.stringify(command)), true); + xhr.onreadystatechange = function() { gw.answer(xhr, after); }; + xhr.send(null); + return; + } + + after(); + gw.commands.splice(0, 2); + } + }, + + send: function(command, after) + { + gw.log('gw.send: ' + command + ' ' + JSON.stringify(gw.commands)); + + gw.commands.push(command); + gw.commands.push(after); + + if (gw.commands.length == 2) + gw.sendNewCommand(); + }, + + raise: function(id, event, args, no_wait) + { + gw.send(['raise', id, event, args, no_wait]); + }, + + update: function(id, prop, value, after) + { + gw.send(['update', id, prop, value, true], after); + }, + + command: function(action) + { + gw.send(null, action); + }, + + getSelection: function(o) + { + var start, end; + + try + { + if (o.createTextRange) + { + var r = document.selection.createRange().duplicate(); + r.moveEnd('character', o.value.length) + if (r.text == '') + start = o.value.length; + else + start = o.value.lastIndexOf(r.text); + r.moveStart('character', -o.value.length); + end = r.text.length; + return [start, end]; + } + + if (o.selectionStart && o.selectionEnd) + return [o.selectionStart, o.selectionEnd]; + } + catch(e) {}; + + return undefined; + }, + + setSelection: function(o, sel) + { + if (sel) + { + if (o.setSelectionRange) + try { o.setSelectionRange(sel[0], sel[1]) } catch(e) {}; + } + }, + + setFocus: function(id) + { + var elt = $(id + ':entry') || $(id); + + if (elt) + { + elt.focus(); + gw.active = document.activeElement.id; + gw.selection = undefined; + gw.focus = true; + } + }, + + resizeComboBox: function(id) + { + $(id + '-select').onmouseover = function() { $(id + '-select').style.width = $(id).offsetWidth + 'px'; } + }, + + highlightMandatory: function(id) + { + var elt = $(id); + var elt_br; + var div; + var div_br; + + if (elt == undefined || elt.gw_mandatory) + return; + + elt.gw_mandatory = div = document.createElement('div'); + div.className = 'gw-mandatory'; + elt.parentNode.insertBefore(div, elt); + + elt_br = gw.getPos(elt); + div_br = gw.getPos(div); + + div.style.top = (elt_br.top - div_br.top) + 'px'; + div.style.left = (elt_br.left - div_br.left) + 'px'; + div.style.width = elt_br.width + 'px'; + div.style.height = elt_br.height + 'px'; + }, + + addTimer: function(id, delay) + { + gw.removeTimer(id); + gw.timers[id] = setInterval(function() { gw.raise(id, 'timer', [], true); }, delay); + }, + + removeTimer: function(id) + { + var t = gw.timers[id]; + if (t) + { + clearInterval(gw.timers[id]); + gw.timers[id] = undefined; + } + }, + + getTargetId: function(elt) + { + for(;;) + { + if (elt.id) + return elt.id; + elt = elt.parentNode; + if (!elt) + return; + } + }, + + getPos: function(elt) + { + var found, left = 0, top = 0, width = 0, height = 0; + var offsetBase = gw.offsetBase; + + if (!offsetBase && document.body) + { + offsetBase = gw.offsetBase = document.createElement('div'); + offsetBase.style.cssText = 'position:absolute;left:0;top:0'; + document.body.appendChild(offsetBase); + } + + if (elt && elt.ownerDocument === document && 'getBoundingClientRect' in elt && offsetBase) + { + var boundingRect = elt.getBoundingClientRect(); + var baseRect = offsetBase.getBoundingClientRect(); + found = true; + left = boundingRect.left - baseRect.left; + top = boundingRect.top - baseRect.top; + width = boundingRect.right - boundingRect.left; + height = boundingRect.bottom - boundingRect.top; + } + + return { found: found, left: left, top: top, width: width, height: height, right: left + width, bottom: top + height }; + }, + + /*ensureVisible: function(id, x, y, w, h) + { + var elt = typeof(id) == 'string' ? $(id) : id; + var pw, ph,cx, cy, cw, ch; + var xx, yy, ww, hh; + + // WW = W / 2 + ww = w / 2; + //HH = H / 2 + hh = h / 2; + // XX = X + WW + xx = x + ww + // YY = Y + HH + yy = y + hh; + + // PW = Me.ClientW + // PH = Me.ClientH + pw = elt.clientWidth; + ph = elt.clientHeight; + + cx = - elt.scrollLeft; + cy = - elt.scrollTop; + cw = elt.scrollWidth; + ch = elt.scrollHeight; + + //If PW < (WW * 2) Then WW = PW / 2 + //If PH < (HH * 2) Then HH = PH / 2 + if (pw < (ww * 2)) ww = pw / 2; + if (ph < (hh * 2)) hh = ph / 2; + + //If CW <= PW Then + // WW = 0 + // CX = 0 + //Endif + if (cw <= pw) { ww = 0; cx = 0; } + + //If CH <= PH Then + // HH = 0 + // CY = 0 + //Endif + if (ch <= ph) { hh = 0; cy = 0 } + + //If XX < (- CX + WW) Then + // CX = Ceil(- XX + WW) + //Else If XX >= (- CX + PW - WW) Then + // CX = Floor(- XX + PW - WW) + //Endif + if (xx < (- cx + ww)) + cx = - xx + ww; + else if (xx >= (- cx + pw - ww)) + cx = - xx + pw - ww; + + //If YY < (- CY + HH) Then + // CY = Ceil(- YY + HH) + //Else If YY >= (- CY + PH - HH) Then + // CY = Floor(- YY + PH - HH) + //Endif + + if (yy < (- cy + hh)) + cy = - yy + hh; + else if (yy >= (- cy + ph - hh)) + cy = - yy + ph - hh; + + //If CX > 0 + // CX = 0 + //Else If CX < (PW - CW) And If CW > PW Then + // CX = PW - CW + //Endif + if (cx > 0) + cx = 0; + else if (cx < (pw - cw) && cw > pw) + cx = pw - cw; + + //If CY > 0 Then + // CY = 0 + //Else If CY < (PH - CH) And If CH > PH Then + // CY = PH - CH + //Endif + if (cy > 0) + cy = 0; + else if (cy < (ph - ch) && ch > ph) + cy = ph - ch; + + //If $hHBar.Value = - CX And If $hVBar.Value = - CY Then Return True + //Scroll(- CX, - CY) + elt.scrollLeft = - cx; + elt.scrollTop = - cy; + },*/ + + window: + { + zIndex: 0, + + open: function(id, resizable, modal, minw, minh) + { + gw.window.close(id); + + if (gw.windows.length == 0) + { + document.addEventListener('mousemove', gw.window.onMove); + document.addEventListener('mouseup', gw.window.onUp); + gw.log('document.addEventListener'); + } + + gw.windows.push(id); + + $(id).addEventListener('mousedown', gw.window.onMouseDown); + + $(id).gw_resizable = resizable; + $(id).gw_modal = modal; + + if (modal) + $(id).gw_focus = gw.saveFocus(); + + if (minw != undefined) + { + $(id).gw_minw = minw; + $(id).gw_minh = minh; + } + else + { + $(id).gw_minw = $(id).offsetWidth; + $(id).gw_minh = $(id).offsetHeight; + } + + //console.log('gw.window.open: minw = ' + $(id).gw_minw + ' minh = ' + $(id).gw_minh); + + // Touch events + //pane.addEventListener('touchstart', onTouchDown); + //document.addEventListener('touchmove', onTouchMove); + //document.addEventListener('touchend', onTouchEnd); + + gw.window.refresh(); + }, + + popup: function(id, resizable, control, alignment, minw, minh) + { + var pos; + + gw.window.close(id); + + if (gw.windows.length == 0) + { + document.addEventListener('mousemove', gw.window.onMove); + document.addEventListener('mouseup', gw.window.onUp); + gw.log('document.addEventListener'); + } + + gw.windows.push(id); + + $(id).addEventListener('mousedown', gw.window.onMouseDown); + + $(id).gw_resizable = resizable; + $(id).gw_modal = true; + $(id).gw_popup = true; + $(id).gw_focus = gw.saveFocus(); + + if (minw != undefined) + { + $(id).gw_minw = minw; + $(id).gw_minh = minh; + } + else + { + $(id).gw_minw = $(id).offsetWidth; + $(id).gw_minh = $(id).offsetHeight; + } + + pos = gw.getPos($(control)); + //console.log(pos); + + /*$(id).style.left = pos.left + 'px'; + $(id).style.top = pos.bottom + 'px';*/ + $(id).style.transform = 'translate(' + pos.left + 'px,' + pos.bottom + 'px)'; + + gw.window.refresh(); + }, + + close: function(id) + { + var i; + + $(id).removeEventListener('mousedown', gw.window.onMouseDown); + + i = gw.windows.indexOf(id); + if (i >= 0) + { + gw.windows.splice(i, 1); + gw.window.refresh(); + } + + if ($(id).gw_focus) + { + gw.restoreFocus($(id).gw_focus); + $(id).gw_focus = undefined; + } + }, + + refresh: function() + { + var i = 0; + var zi; + + while (i < gw.windows.length) + { + if ($(gw.windows[i])) + { + zi = 11 + i * 2; + if ($(gw.windows[i]).style.zIndex != zi) + $(gw.windows[i]).style.zIndex = zi; + i++; + } + else + gw.windows.splice(i, 1); + } + + gw.window.updateModal(); + + if (gw.windows.length == 0) + { + gw.log('document.removeEventListener'); + document.removeEventListener('mousemove', gw.window.onMove); + document.removeEventListener('mouseup', gw.window.onUp); + } + else + gw.window.updateTitleBars(); + }, + + updateTitleBars: function() + { + var i, win, last; + + for (i = 0; i < gw.windows.length - 1; i++) + { + win = gw.windows[i]; + if ($(win).gw_popup) + continue; + $(win).addClass('gw-deactivated'); + $(win + '-titlebar').addClass('gw-deactivated'); + last = win; + } + + if (last && !$(last).gw_popup) + { + $(last).removeClass('gw-deactivated'); + $(last + '-titlebar').removeClass('gw-deactivated'); + } + }, + + raise: function(id, send) + { + var i = gw.windows.indexOf(id); + if (i < 0) + return; + + gw.windows.splice(i, 1); + gw.windows.push(id); + + for (i = 0; i < gw.windows.length; i++) + $(gw.windows[i]).style.zIndex = 11 + i * 2; + + gw.window.updateTitleBars(); + + if (send) + gw.update('', '#windows', gw.windows); + }, + + updateModal: function() + { + var i, elt = $('gw-modal'); + + for (i = gw.windows.length - 1; i >= 0; i--) + { + if ($(gw.windows[i]).gw_modal) + { + gw.window.zIndex = 10 + i * 2; + elt.style.zIndex = 10 + i * 2; + elt.style.display = 'block'; + /*if ($(gw.windows[i]).gw_popup) + elt.style.opacity = '0'; + else + elt.style.opacity = '';*/ + return; + } + } + + gw.window.zIndex = 0; + elt.style.display = 'none'; + }, + + center: function(id) + { + $(id).style.transform = 'translate(' + ((window.innerWidth - $(id).offsetWidth) / 2 | 0) + 'px,' + ((window.innerHeight - $(id).offsetHeight) / 2 | 0) + 'px)'; + gw.window.updateGeometry(id); + }, + + maximize: function(id) + { + var geom = $(id).gw_save_geometry; + if (geom != undefined) + { + //$(id).style.left = geom[0]; + //$(id).style.top = geom[1]; + $(id).style.transform = geom[0] + $(id).style.width = geom[1]; + $(id).style.height = geom[2]; + $(id).gw_save_geometry = undefined; + } + else + { + $(id).gw_save_geometry = [$(id).style.transform, $(id).style.width, $(id).style.height]; + $(id).style.transform = ''; + $(id).style.width = '100%'; + $(id).style.height = '100%'; + } + //gw.window.updateGeometry(id); + }, + + onMouseDown: function(e) + { + gw.window.onDown(e); + }, + + onDown: function(e) + { + var c, win; + + gw.window.context = undefined; + + if (e.target.className == 'gw-window-button') + return; + + gw.window.onMove(e); + + c = gw.window.context; + if (c == undefined) + return; + + if ($(c.id).gw_save_geometry) + return; + + if (c.isMoving || c.isResizing) + { + gw.window.raise(c.id); + gw.window.downEvent = e; + e.preventDefault(); + } + }, + + onDownModal: function() + { + var win = gw.windows[gw.windows.length - 1]; + + if ($(win).gw_popup) + gw.update(win, '#close'); + }, + + onMove: function(e) + { + var i, id, elt, b, x, y, bx, by, bw, bh, th; + var onTopEdge, onLeftEdge, onRightEdge, onBottomEdge, isResizing; + var MARGINS = 6; + + if (gw.window.downEvent) + { + gw.window.context.cx = e.clientX; + gw.window.context.cy = e.clientY; + gw.window.animate(); + return; + } + + gw.window.context = undefined; + + for (i = 0; i < gw.windows.length; i++) + { + id = gw.windows[gw.windows.length - i - 1]; + elt = $(id); + + if (elt.style.zIndex < gw.window.zIndex) + continue; + + b = elt.getBoundingClientRect(); + + bx = b.left; // - MARGINS; + by = b.top; // - MARGINS; + bw = b.width; // + MARGINS * 2; + bh = b.height; // + MARGINS * 2; + + x = e.clientX - bx; + y = e.clientY - by; + + //console.log(x + ',' + y + ' : ' + bx + ',' + by + ',' + bw + ',' + bh); + + if (x >= 0 && x < bw && y >= 0 && y < bh) + { + if (elt.gw_resizable) + { + onTopEdge = y < MARGINS; + onLeftEdge = x < MARGINS; + onRightEdge = x >= (bw - MARGINS); + onBottomEdge = y >= (bh - MARGINS); + + isResizing = onTopEdge || onLeftEdge || onRightEdge || onBottomEdge; + } + else + onTopEdge = onLeftEdge = onRightEdge = onBottomEdge = isResizing = false; + + if ($(id).gw_popup) + th = 0; + else + th = $(id + '-titlebar').offsetHeight; + isMoving = !isResizing && y < (th + MARGINS); + + gw.window.context = { + id: id, + x: b.left + window.scrollX, + y: b.top + window.scrollY, + cx: e.clientX, + cy: e.clientY, + w: b.width, + h: b.height, + isResizing: isResizing, + isMoving: isMoving, + onTopEdge: onTopEdge, + onLeftEdge: onLeftEdge, + onRightEdge: onRightEdge, + onBottomEdge: onBottomEdge + }; + gw.window.animate(); + break; + } + } + }, + + updateGeometry: function(id) + { + var b = $(id).getBoundingClientRect(); + gw.update(id, '#geometry', [ b.left + 'px', b.top + 'px', b.width + 'px', b.height + 'px']); + }, + + onUp: function(e) + { + var c = gw.window.context; + + gw.window.downEvent = undefined; + + if (c && (c.isMoving || c.isResizing)) + { + var id = gw.window.context.id; + gw.window.context = undefined; + gw.window.raise(id, true); + gw.window.updateGeometry(id); + } + }, + + animate: function() + { + var id, elt, c, e, x, y, w, h; + var minWidth; + var minHeight; + + //requestAnimationFrame(gw.window.animate); + + c = gw.window.context; + if (!c) return; + + elt = $(c.id); + minWidth = elt.gw_minw; + minHeight = elt.gw_minh; //$(c.id + '-titlebar').offsetHeight + 2 + elt.gw_minh; + e = gw.window.downEvent; + + if (c && c.isResizing && e) + { + if (c.onRightEdge) + elt.style.width = Math.max(c.w + c.cx - e.clientX, minWidth) + 'px'; + + if (c.onBottomEdge) + elt.style.height = Math.max(c.h + c.cy - e.clientY, minHeight) + 'px'; + + x = c.x; + y = c.y; + + if (c.onLeftEdge) + { + x = c.x + c.cx - e.clientX; + w = c.x + c.w - x; + if (w >= minWidth) + { + elt.style.width = w + 'px'; + //elt.style.left = x + 'px'; + elt.style.transform = 'translate(' + x + 'px,' + y + 'px)'; + //c.x = x; + } + } + + if (c.onTopEdge) + { + y = c.y + c.cy - e.clientY; + h = c.y + c.h - y; + if (h >= minHeight) + { + elt.style.height = h + 'px'; + //elt.style.top = y + 'px'; + elt.style.transform = 'translate(' + x + 'px,' + y + 'px)'; + } + } + + return; + } + + if (c && c.isMoving && e) + { + /*elt.style.left = (Math.max(0, c.x + c.cx - e.clientX)) + 'px'; + elt.style.top = (Math.max(0, c.y + c.cy - e.clientY)) + 'px';*/ + elt.style.transform = 'translate(' + (Math.max(0, c.x + c.cx - e.clientX)) + 'px,' + (Math.max(0, c.y + c.cy - e.clientY)) + 'px)'; + return; + } + + // This code executes when mouse moves without clicking + + if (c.onRightEdge && c.onBottomEdge || c.onLeftEdge && c.onTopEdge) + elt.style.cursor = 'nwse-resize'; + else if (c.onRightEdge && c.onTopEdge || c.onBottomEdge && c.onLeftEdge) + elt.style.cursor = 'nesw-resize'; + else if (c.onRightEdge || c.onLeftEdge) + elt.style.cursor = 'ew-resize'; + else if (c.onBottomEdge || c.onTopEdge) + elt.style.cursor = 'ns-resize'; + else + elt.style.cursor = ''; + } + }, + + menu: + { + hide: function(elt) + { + elt.style.display = 'none'; + setTimeout(function() { elt.style.display = ''; }, 150); + }, + + click: function(name, event) + { + var id = gw.getTargetId(event.target); + gw.update(name, '#click', id); + event.stopPropagation(); + } + }, + + table: + { + select: function(id, row) + { + var elt = $(id); + var tr = $(id + ':' + row); + var current = elt.gw_current; + + if (tr.tagName == 'TR') + { + if (current !== undefined) + { + if (current >= 0) + $(id + ':' + current) && $(id + ':' + current).removeClass('gw-table-row-selected'); + tr.addClass('gw-table-row-selected'); + elt.gw_current = row; + } + else + { + if (tr.hasClass('gw-table-row-selected')) + tr.removeClass('gw-table-row-selected'); + else + tr.addClass('gw-table-row-selected'); + } + } + + gw.update(id, '$' + row, null); + }, + + check: function(id, row) + { + var elt = $(id + ':' + row); + if (event.target.tagName == 'TD') + elt.checked = !elt.checked; + gw.update(id, '!' + row, elt.checked); + event.stopPropagation(); + }, + + toggle: function(id, row) + { + gw.update(id, '?' + row, false); + }, + + onScroll: function(id, more, timeout) + { + var elt = $(id); + var sw = elt.firstChild; + var last = elt.gw_last_scroll; + + if (last && last[0] == sw.scrollLeft && last[1] == sw.scrollTop) + return; + + elt.gw_last_scroll = [sw.scrollLeft, sw.scrollTop]; + + //console.log('onScroll: ' + id + ' ' + sw.scrollLeft + ',' + sw.scrollTop); + + if (more) + { + //if ((sw.scrollHeight - sw.scrollTop) === (sw.clientHeight)) + if (sw.scrollTop >= (sw.scrollHeight - sw.clientHeight - 16)) + { + /*var wait = document.createElement('div'); + wait.className = 'gw-waiting'; + elt.appendChild(wait);*/ + if (elt.gw_scroll) + { + clearTimeout(elt.gw_scroll); + elt.gw_scroll = undefined; + } + + gw.update(id, '#more', [sw.scrollLeft, sw.scrollTop]); + return; + } + } + + if (elt.gw_headerh) + $(elt.gw_headerh).firstChild.scrollLeft = sw.scrollLeft; + + if (elt.gw_headerv) + $(elt.gw_headerv).firstChild.scrollTop = sw.scrollTop; + + if (elt.gw_noscroll) + { + elt.gw_noscroll = undefined; + return; + } + + if (elt.gw_scroll) + clearTimeout(elt.gw_scroll); + + elt.gw_scroll = setTimeout(function() + { + //console.log("gw.table.onScroll: " + id + ": " + sw.scrollLeft + " " + sw.scrollTop); + var pos = [sw.scrollLeft, sw.scrollTop]; + + clearTimeout(elt.gw_scroll); + + gw.update(elt.id, '#scroll', pos, function() + { + elt.gw_scroll = undefined; + if (pos[0] != sw.scrollLeft || pos[1] != sw.scrollTop) + gw.table.onScroll(id, more, timeout); + }); + + //elt.gw_scroll = undefined; + }, timeout || 250); + }, + + scroll: function(id, x, y) + { + var sw = $(id).firstChild + + //console.log("gw.table.scroll: " + id + ": " + x + " " + y); + + if (x != sw.scrollLeft) + { + $(id).gw_noscroll = true; + sw.scrollLeft = x; + } + if (y != sw.scrollTop) + { + $(id).gw_noscroll = true; + sw.scrollTop = y; + } + if (x != sw.scrollLeft || y != sw.scrollTop) + gw.update(id, '#scroll', [sw.scrollLeft, sw.scrollTop]); + }, + + ensureVisible: function(id, row) + { + var sw = $(id).firstChild; + gw.table.scroll(id, sw.scrollLeft, $(id + ':' + row).offsetTop - sw.clientHeight / 2); + } + }, + + scrollview: + { + setHeaders: function(id, hid, vid) + { + $(id).gw_headerh = hid; + $(id).gw_headerv = vid; + } + }, + + file: + { + select: function(id) + { + var elt = $(id + ':file'); + + if ($(id).gw_uploading) + return; + + elt.focus(); + elt.click(); + }, + + finish: function(xhr) + { + if (xhr.gw_progress) + { + setTimeout(function() { gw.file.finish(xhr); }, 250); + return; + } + + gw.update(xhr.gw_id, '#progress', 1, function() { + gw.answer(xhr); + gw.uploads[xhr.gw_id] = undefined; + gw.raise(xhr.gw_id, 'upload', [], true); + xhr.gw_id = undefined; + }); + }, + + upload: function(id) + { + var elt = $(id + ':file'); + var file = elt.files[0]; + var xhr = new XMLHttpRequest(); + var form = new FormData(); + + if (gw.uploads[id]) + return; + + gw.uploads[id] = xhr; + + //gw.log('gw.file.upload: ' + id + ': ' + file.name); + + xhr.gw_progress = 0; + + xhr.gw_progress++; + gw.update(id, '#progress', 0, function() { xhr.gw_progress--; }); + + form.append('file', file); + form.append('name', file.name); + form.append('id', id); + + //xhr.upload.addEventListener("loadstart", loadStartFunction, false); + //xhr.upload.addEventListener("load", transferCompleteFunction, false); + + xhr.upload.addEventListener("progress", + function(e) + { + //console.log('upload: progress ' + e.loaded + ' / ' + e.total); + + if (xhr.gw_id == undefined) + return; + + if (e.lengthComputable) + { + var t = (new Date()).getTime(); + + if ((xhr.gw_time == undefined || (t - xhr.gw_time) > 250) && xhr.gw_progress == 0) + { + xhr.gw_progress++; + gw.update(xhr.gw_id, '#progress', e.loaded / e.total, function() { xhr.gw_progress--; }); + xhr.gw_time = t; + } + } + }, + false); + + xhr.gw_command = ['upload', id]; + xhr.gw_id = id; + + xhr.open("POST", $root + '/u', true); + + xhr.onreadystatechange = function() + { + if (xhr.readyState == 4) + gw.file.finish(xhr); + }; + + xhr.send(form); + }, + + abort: function(id) + { + if (gw.uploads[id]) + gw.uploads[id].abort(); + } + }, + + autocomplete: function(id) + { + new AutoComplete({ + selector: $(id + ':entry'), + cache: false, + source: function(term, response) { + var xhr = $(id).gw_xhr; + if (xhr) + { + try { xhr.abort(); } catch(e) {} + } + + $(id).gw_xhr = xhr = new XMLHttpRequest(); + + xhr.open('GET', $root + '/x?c=' + encodeURIComponent(JSON.stringify(['raise', id, 'completion', [term]])), true); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) + { + gw.autocompletion = []; + gw.answer(xhr); + response(gw.autocompletion); + } + }; + xhr.send(); + }, + onSelect: function(e, term, item) { + gw.textbox.setText(id, gw.textbox.getText(id)); + } + }); + }, + + textbox: + { + onactivate: function(id, e) + { + gw.log('textbox.onactivate'); + if (e.keyCode == 13) + setTimeout(function() { gw.raise(id, 'activate', [], false); }, 50); + }, + + getText: function(id) + { + return $(id + ':entry').value; + }, + + setText: function(id, text) + { + gw.command(function() { + $(id + ':entry').value = text; + gw.setSelection($(id + ':entry'), [text.length, text.length]); + gw.update(id, 'text', text); + }); + }, + + clear: function(id) + { + gw.textbox.setText(id, ''); + gw.setFocus(id); + gw.raise(id, 'activate', [], false); + } + } +} + diff --git a/comp/src/gb.web.form/.hidden/Uncompressed/style.css b/comp/src/gb.web.form/.hidden/Uncompressed/style.css new file mode 100644 index 00000000..0e8fbb86 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/Uncompressed/style.css @@ -0,0 +1,732 @@ +HTML, BODY { + margin: 0; + padding: 0; + height: 100%; + width: 100%; +} + +HTML, BODY, DIV, INPUT { + box-sizing: border-box; +} + +SELECT { + min-height: 2em; + font-size: inherit; +} + +INPUT { + font: inherit; +} + +H1,H2,H3,P { + margin-top: 0.5rem; + margin-bottom: 0.25rem; +} + +/*UL, OL { + padding-left: 2em; + margin-bottom: 0; +} + +P:first-child,UL:first-child,OL:first-child { + margin-top: 0; +}*/ + +.gw-button { + padding: 0; + font: inherit; + padding: 0.125em 0.25em; + min-height: 2em; +} + +.gw-button.gw-noborder { + border: solid 1px transparent; + background: none; + padding: 0; +} + +.gw-button.gw-noborder:hover { + border: solid 1px #C0C0C0; +} + +.gw-button-image { + margin-right: 0.5em; +} + +.gw-button > DIV { + vertical-align: middle; + position: relative; + display: inline-flex; + justify-content: center; + align-items: center; + flex-flow: row; +} + +.gw-button > DIV > SPAN { + display: inline-block; +} + +.gw-tab-header { + margin-bottom: -1px; + z-index: 1; + line-height: 2em; +} + +.gw-tab-header.gw-noborder { + margin-bottom: 0; +} + +.gw-tab { + color: gray; + padding: 0 0.5em; + cursor: pointer; + border-top: solid transparent 1px; + border-right: solid transparent 1px; + border-left: solid transparent 1px; +} + +.gw-tab:hover { + color: black; +} + +.gw-tab-selected { + padding: 0 0.5em; + border-left: solid #C0C0C0 1px; + border-top: solid #C0C0C0 1px; + border-right: solid #C0C0C0 1px; + border-top-left-radius: 0.5em; + border-top-right-radius: 0.5em; + background: white; +} + +.gw-tab-header.gw-noborder > div { + border: none; +} + +.gw-tab-contents { + border: solid #C0C0C0 1px; + flex-grow: 1; + display: flex; + flex-flow: column; +} + +.gw-tab-contents > DIV { + flex-grow: 1; +} + +.gw-tab-contents.gw-noborder { + border: none; +} + +.gw-checkbox,.gw-radiobutton { + display: flex; + flex-flow: row; + min-height: 2em; +} + +.gw-checkbox-label,.gw-radiobutton-label { + display: flex; + flex-flow: row; + align-items: center; +} + +.gw-checkbox.gw-disabled > .gw-checkbox-label { + opacity: 0.5; +} + +.gw-radiobutton.gw-disabled > .gw-checkbox-label { + opacity: 0.5; +} + +.gw-checkbox-toggle,.gw-radiobutton-toggle { + margin: 0; + margin-right: 0.5em; +} + +.gw-textbox { + min-height: 2em; + /*box-sizing: border-box; firefox bug on refresh !*/ + padding: 0 0.25em; +} + +.gw-textbox.gw-noborder { + border: none; +} + +.gw-combobox { + display: inline-block; + position: relative; + min-height: 2em; +} + +.gw-combobox SELECT { + height: 100%; + z-index: 1; + opacity: 0; + float: right; +} + +.gw-combobox INPUT { + width: 100%; + min-height: 2em; + box-sizing: border-box; + padding: 0 2.25em 0 0.25em; + vertical-align: middle; +} + +.gw-combobox.gw-noborder INPUT { + border: none; +} + +.gw-combobox-arrow { + display: inline-block; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAI0lEQVQYlWNgoAZoYGBg+I8DN+BT1MCABhrwSSIrwilJHgAAbCcP9dwIQGIAAAAASUVORK5CYII='); + background-position: center; + background-repeat: no-repeat; + height: 2rem; + width: 2rem; + margin-left: -2rem; + /*pointer-events: none;*/ + vertical-align: middle; + border: none; + overflow: hidden; +} + +.gw-selectbox { + display: inline-block; + position: relative; + min-height: 2em; +} + +.gw-selectbox SELECT { + width: 100%; + height: 100%; +} + +.gw-selectbox.gw-noborder SELECT { + border: none; +} + +.gw-window-container { +} + +.gw-window { + position: fixed; + top: 0; + left: 0; + border: solid 1px white; + box-shadow: 0 0 0.5em black; + border-radius: 0.5em; + background: white; + z-index: 10; + overflow: hidden; +} + +.gw-popup { + border: solid 1px #C0C0C0; + border-radius: 0; + box-shadow: none; +} + +.gw-window-titlebar { + display: flex; + flex-flow: row; + background: #2980B9; + font-weight: bold; + border-top-left-radius: 0.5em; + border-top-right-radius: 0.5em; + padding: 0.25em; +} + +.gw-window-titlebar.gw-deactivated { + background: white; +} + +.gw-window-title { + flex-grow: 1; + color: white; + padding: 0 1em; + pointer-events: none; +} + +.gw-window-titlebar.gw-deactivated > DIV.gw-window-title { + color: gray; +} + +.gw-window-button { + width: 1.5em; + height: 1.5em; + padding: 0; + display: inline-flex; + justify-content: center; +} + +.gw-window-button > IMG { + width: 0.8em; + height: 0.8em; + margin: auto; +} + +/*@-moz-document url-prefix() { + .gw-window-button { + display: inline-flex; + }; +}*/ + +.gw-window-contents { + display: flex; + flex-flow: column; + flex-grow: 1; + padding: 0.5em; +} + +.gw-popup > .gw-window-contents { + padding: 0; +} + + +.gw-label { + display: flex; + align-items: center; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.gw-label.gw-disabled { + opacity: 0.5; +} + +#gw-modal { + display: none; + position: fixed; + left: 0; + right: 0; + width: 100%; + height: 100%; + background: black; + opacity: 0.05; +} + +.gw-textarea { + font: inherit; + resize: none; +} + +.gw-textarea.gw-noborder { + border: none; +} + + +.gw-menu-title { + display: inline-table; + padding: 0.5em; + padding-top: 0.25em; + cursor: default; + border-top: solid 1px transparent; + border-left: solid 1px transparent; + border-right: solid 1px transparent; + /*margin-top: 0.25em; + margin-bottom: 0.25em;*/ +} + +.gw-menu:hover > .gw-menu-title { + border-top: solid 1px #C0C0C0; + border-left: solid 1px #C0C0C0; + border-right: solid 1px #C0C0C0; + box-shadow: 0 0 0.25em #C0C0C0; + background: white; + z-index: 1002; +} + +.gw-submenu { + display: none; + position: absolute; + border: solid 1px #C0C0C0; + background: white; + box-shadow: 0 0.125em 0.25em #C0C0C0; + margin-top: -0.25em; + z-index: 1003; +} + +.gw-menuitem > .gw-menu > .gw-submenu { + margin-top: 0; + top: 0; + left: 100%; +} + +.gw-menu:hover > .gw-submenu { + display: table; +} + +.gw-menu-tape { + display: none; + position: relative; + background: white; + z-index: 1004; + height: 4px; + margin-top: -4px; + margin-left: 1px; + margin-right: 1px; + top: -2px; +} + +.gw-menu:hover > .gw-menu-tape { + display: block; +} + +.gw-submenu > div:hover { + background: #E0E0E0; +} + +.gw-menuitem { + display: flex; + flex-flow: column; +} + +.gw-menuitem > div { + display: table-cell !important; + vertical-align: middle; +} + +.gw-menuitem-text { + flex-grow: 1; + padding: 0.125em 0.25em; + white-space: nowrap; +} + +.gw-menuitem-icon { + padding: 0.125em 0.25em; +} + +.gw-menuitem-icon > IMG { + vertical-align: middle; +} + +.gw-menuitem-shortcut { + text-align: right; + padding: 0.125em 1em; +} + +.gw-separator { + position: relative; + padding: 0 !important; + pointer-events: none; +} + +.gw-separator-hline { + position: absolute; + display: flex; + flex-flow: row; + align-items: center; + height: inherit; + width: 100%; +} + +.gw-separator-hline > div { + height: 1px; + width: 100%; + background: #C0C0C0; +} + +.gw-separator-vline { + position: absolute; + display: flex; + flex-flow: column; + align-items: center; + height: 100%; + width: inherit; +} + +.gw-separator-vline > div { + width: 1px; + height: 100%; + background: #C0C0C0; +} + +.gw-submenu > div { + display: flex !important; + cursor: default; + padding: 0.25em 1em; + margin: 0; +} + +.gw-submenu > .gw-menuitem { + display: table-row !important; + table-layout: fixed; + cursor: default; + padding: 0; + margin: 0; +} + +.gw-submenu > .gw-separator { + display: table-row !important; + cursor: default; + margin: 0; + width: auto; + height: 1em; +} + +.gw-menuitem > .gw-menu { + display: table-cell !important; + position: relative; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAJElEQVQYlWNgQIAGBgLgPyFF/wkp+o9NERMhe0m2AqckAy5JALuADHot/KmNAAAAAElFTkSuQmCC'); + background-position: center; + background-repeat: no-repeat; + width: 1em; +} + +.gw-spinbox { + min-height: 2em; + padding: 0 0.25em; +} + +.gw-spinbox.gw-noborder { + border: none; +} + +.gw-expander-header + DIV { + margin-top: 0.5em; +} + +.gw-expander-header > IMG { + vertical-align: middle; +} + +.gw-expander-header > DIV { + display: inline-table; + vertical-align: middle; +} + +.gw-expander-border { + border: solid 1px #C0C0C0; + padding: 0.5em; +} + +.gw-table { + position: relative; + min-height: 4em; + border: solid 1px #C0C0C0; + cursor: default; +} + +.gw-table.gw-noborder { + border: none; +} + +/*.gw-table-header { + height: 1.5em; + background-color: yellow; +}*/ + +.gw-table-contents { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: auto; +} + +.gw-table > DIV > TABLE { + border-collapse: collapse; +} + +.gw-table > DIV > TABLE > THEAD > TR > TH { + text-align: left; + background-color: #E0E0E0; + padding: 0.25em 0.5em; + vertical-align: top; + border-bottom: solid 1px #C0C0C0; +} + +.gw-table > DIV > TABLE > THEAD > TR > TH:last-child { + border-right: none; +} + +.gw-table > DIV > TABLE > TBODY > TR > TD { + /*border-right: solid 1px #C0C0C0;*/ + padding: 0.1em 0.5em; + vertical-align: top; +} + +/*.gw-table > DIV > TABLE > TBODY > TR > TD:last-child { + border-right: none; +} + +.gw-table > DIV > TABLE > TBODY > TR:last-child > TD { + border-bottom: solid 1px #C0C0C0; +}*/ + +.gw-table > DIV > TABLE > TBODY > TR:nth-child(even) { + background-color: #F0F0F0; +} + +.gw-table-more { + position: absolute; + padding-left: 4px; +} + +.gw-waiting { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: url('//gw-waiting.gif'); + background-position: center; + background-repeat: no-repeat; +} + +.gw-scrollview { + position: relative; +} + +.gw-scrollview > DIV { + position: absolute !important; + overflow: auto; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.gw-datebox { + position: relative; + display: inline-block; + min-height: 2em; +} + +.gw-datebox INPUT { + width: 100%; + min-height: 2em; + box-sizing: border-box; + padding: 0 2.25em 0 0.25em; + vertical-align: middle; +} + +.gw-datebox.gw-noborder INPUT { + border: none; +} + +TABLE.gw-calendar { + width: 100%; +} + +TABLE.gw-calendar > TBODY > TR > TH { + padding: 0.25em 0.5em; + background: #E0E0E0; +} + +TABLE.gw-calendar > TBODY > TR > TD { + padding: 0.25em 0.5em; + text-align: center; + cursor: pointer; + border: solid 1px transparent; +} + +TABLE.gw-calendar > TBODY > TR > TD:hover { + outline: solid 1px #C0C0C0; +} + +TABLE.gw-calendar > TBODY > TR > TD.gw-date-disabled { + color: #C0C0C0; +} + +TABLE.gw-calendar > TBODY > TR > TD.gw-date-today { + font-weight: bold; + border: solid 1px black; +} + +TABLE.gw-calendar > TBODY > TR > TD.gw-date-current { + background-color: #2980B9; + color: white; +} + +.gw-file-input { + position: absolute; + left: -2000px; +} + +.gw-progressbar { + position: relative; + border: solid 1px #C0C0C0; + min-height: 0.5em; + border-radius: 0.25em; +} + +.gw-progressbar.gw-noborder { + border: solid 1px transparent; +} + +.gw-progressbar-bar { + position: absolute; + height: 100%; + background: #C0C0C0; + border: solid 1px white; + border-radius: 0.25em; + z-index: -1; +} + +.gw-progressbar-label { + position: absolute; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +.gw-form { + flex-grow: 1; +} + +.gw-image { + border: solid 1px #C0C0C0; + box-sizing: border-box; +} + +.gw-image.gw-noborder { + border: none; +} + +.gw-table-row-selected { + background-color: #2980B9 !important; + color: white !important; +} + +.gw-ac-suggestions { + text-align: left; + cursor: default; + border: 1px solid #C0C0C0; + border-top: 0; + background: white; + /*box-shadow: -1px 1px 3px rgba(0,0,0,.1);*/ + + /* core styles should not be changed */ + position: absolute; + display: none; + z-index: 9999; + max-height: 12em; + overflow: hidden; + overflow-y: auto; + box-sizing: border-box; +} + +.gw-ac-suggestion { + position: relative; + padding: 0 0.5em; + line-height: 1.5em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.gw-ac-suggestion b { + font-weight: normal; + color: #2980B9; +} + +.gw-ac-suggestion.selected { + background: #E0E0E0; +} diff --git a/comp/src/gb.web.form/.hidden/calendar.js b/comp/src/gb.web.form/.hidden/calendar.js new file mode 100644 index 00000000..e07da523 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/calendar.js @@ -0,0 +1,1577 @@ + +function getAbsoluteParent(obj) +{ + for(;;) + { + obj = obj.offsetParent; + if (obj == null) + break; + if (obj.style.position == 'absolute') + break; + } + + return obj; +} + +function __getIEVersion() { + var rv = -1; // Return value assumes failure. + if (navigator.appName == 'Microsoft Internet Explorer') { + var ua = navigator.userAgent; + var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); + if (re.exec(ua) != null) + rv = parseFloat(RegExp.$1); + } + return rv; +} + +function __getOperaVersion() { + var rv = 0; // Default value + if (window.opera) { + var sver = window.opera.version(); + rv = parseFloat(sver); + } + return rv; +} + +var __userAgent = navigator.userAgent; +var __isIE = navigator.appVersion.match(/MSIE/) != null; +var __IEVersion = __getIEVersion(); +var __isIENew = __isIE && __IEVersion >= 8; +var __isIEOld = __isIE && !__isIENew; + +var __isFireFox = __userAgent.match(/firefox/i) != null; +var __isFireFoxOld = __isFireFox && ((__userAgent.match(/firefox\/2./i) != null) || + (__userAgent.match(/firefox\/1./i) != null)); +var __isFireFoxNew = __isFireFox && !__isFireFoxOld; + +var __isWebKit = navigator.appVersion.match(/WebKit/) != null; +var __isChrome = window.chrome != null; +var __isOpera = window.opera != null; +var __operaVersion = __getOperaVersion(); +var __isOperaOld = __isOpera && (__operaVersion < 10); + +function __parseBorderWidth(width) { + var res = 0; + if (typeof(width) == "string" && width != null && width != "" ) { + var p = width.indexOf("px"); + if (p >= 0) { + res = parseInt(width.substring(0, p)); + } + else { + //do not know how to calculate other values + //(such as 0.5em or 0.1cm) correctly now + //so just set the width to 1 pixel + res = 1; + } + } + return res; +} + +//returns border width for some element +function __getBorderWidth(element) { + var res = new Object(); + res.left = 0; res.top = 0; res.right = 0; res.bottom = 0; + if (window.getComputedStyle) { + //for Firefox + var elStyle = window.getComputedStyle(element, null); + res.left = parseInt(elStyle.borderLeftWidth.slice(0, -2)); + res.top = parseInt(elStyle.borderTopWidth.slice(0, -2)); + res.right = parseInt(elStyle.borderRightWidth.slice(0, -2)); + res.bottom = parseInt(elStyle.borderBottomWidth.slice(0, -2)); + } + else { + //for other browsers + res.left = __parseBorderWidth(element.style.borderLeftWidth); + res.top = __parseBorderWidth(element.style.borderTopWidth); + res.right = __parseBorderWidth(element.style.borderRightWidth); + res.bottom = __parseBorderWidth(element.style.borderBottomWidth); + } + + return res; +} + +//returns the absolute position of some element within document +function getElementAbsolutePos(element) { + var res = new Object(); + res.x = 0; res.y = 0; + + if (element !== null) { + if (element.getBoundingClientRect) { + var viewportElement = document.documentElement; + var box = element.getBoundingClientRect(); + var scrollLeft = viewportElement.scrollLeft; + var scrollTop = viewportElement.scrollTop; + + res.x = box.left + scrollLeft; + res.y = box.top + scrollTop; + + } + else { //for old browsers + res.x = element.offsetLeft; + res.y = element.offsetTop; + + var parentNode = element.parentNode; + var borderWidth = null; + var offsetParent = element.offsetParent; + + while (offsetParent != null) { + res.x += offsetParent.offsetLeft; + res.y += offsetParent.offsetTop; + + var parentTagName = + offsetParent.tagName.toLowerCase(); + + if ((__isIEOld && parentTagName != "table") || + ((__isFireFoxNew || __isChrome) && + parentTagName == "td")) { + borderWidth = kGetBorderWidth + (offsetParent); + res.x += borderWidth.left; + res.y += borderWidth.top; + } + + if (offsetParent != document.body && + offsetParent != document.documentElement) { + res.x -= offsetParent.scrollLeft; + res.y -= offsetParent.scrollTop; + } + + + //next lines are necessary to fix the problem + //with offsetParent + if (!__isIE && !__isOperaOld || __isIENew) { + while (offsetParent != parentNode && + parentNode !== null) { + res.x -= parentNode.scrollLeft; + res.y -= parentNode.scrollTop; + if (__isFireFoxOld || __isWebKit) + { + borderWidth = + kGetBorderWidth(parentNode); + res.x += borderWidth.left; + res.y += borderWidth.top; + } + parentNode = parentNode.parentNode; + } + } + + if (offsetParent.style.position == 'absolute') + break; + parentNode = offsetParent.parentNode; + offsetParent = offsetParent.offsetParent; + } + } + } + return res; +} + + +function getLeftPos(obj) +{ + return getElementAbsolutePos(obj).x +} + +function getTopPos(obj) +{ + return getElementAbsolutePos(obj).y +} + +function moveElementUnder(obj, under, dx, dy, right) +{ + var pos, opos, x, y; + var absUnder, absObj; + + pos = getElementAbsolutePos(under); + + obj.style.position = 'absolute'; + obj.style.left = '0'; + obj.style.top = '0'; + + opos = getElementAbsolutePos(obj); + + x = pos.x - opos.x + dx; //- getLeftPos(under.offsetParent); + y = pos.y - opos.y + under.offsetHeight + dy; //- getTopPos(under.offsetParent); + + if (right) + x -= obj.offsetWidth - under.offsetWidth; + + obj.style.left = x + 'px'; + obj.style.top = y + 'px'; +} + +/************************************************************************************************************ +JS Calendar +Copyright (C) September 2006 DTHMLGoodies.com, Alf Magne Kalleland + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Dhtmlgoodies.com., hereby disclaims all copyright interest in this script +written by Alf Magne Kalleland. + +Alf Magne Kalleland, 2006 +Owner of DHTMLgoodies.com + +************************************************************************************************************/ + +var turnOffYearSpan = false; // true = Only show This Year and Next, false = show +/- 5 years +var weekStartsOnSunday = false; // true = Start the week on Sunday, false = start the week on Monday +var showWeekNumber = true; // true = show week number, false = do not show week number + +var calendar_display_time = true; + +// Format of current day at the bottom of the calendar +// [todayString] = the value of todayString +// [dayString] = day of week (example: mon, tue, wed...) +// [UCFdayString] = day of week (example: Mon, Tue, Wed...) ( First letter in uppercase) +// [day] = Day of month, 1..31 +// [monthString] = Name of current month +// [year] = Current year +var todayStringFormat = '[todayString] [UCFdayString]. [day] [monthString] [year]'; +var pathToImages = $root; // Relative to your HTML file + +var speedOfSelectBoxSliding = 50; // Milliseconds between changing year and hour when holding mouse over "-" and "+" - lower value = faster +var intervalSelectBox_minutes = 5; // Minute select box - interval between each option (5 = default) + +var calendar_offsetTop = 3; // Offset - calendar placement - You probably have to modify this value if you're not using a strict doctype +var calendar_offsetLeft = 4; // Offset - calendar placement - You probably have to modify this value if you're not using a strict doctype +var calendarDiv; +var must_submit = false; + +var MSIE = false; +var Opera = false; +//if(navigator.userAgent.indexOf('MSIE')>=0 && navigator.userAgent.indexOf('Opera')<0)MSIE=true; +if(navigator.userAgent.indexOf('Opera')>=0)Opera=true; + +var monthArray = ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre']; +var monthArrayShort = ['Jan','Fév','Mar','Avr','Mai','Jun','Jul','Aoû','Sep','Oct','Nov','Déc']; +var dayArray = ['Lun','Mar','Mer','Jeu','Ven','Sam','Dim']; +var weekString = 'Sem'; +var todayString = ""; + + +if (weekStartsOnSunday) { + var tempDayName = dayArray[6]; + for(var theIx = 6; theIx > 0; theIx--) { + dayArray[theIx] = dayArray[theIx-1]; + } + dayArray[0] = tempDayName; +} + +var daysInMonthArray = [31,28,31,30,31,30,31,31,30,31,30,31]; +var currentMonth; +var currentYear; +var currentHour; +var currentMinute; +var calendarContentDiv; +var returnDateTo; +var returnFormat; +var activeSelectBoxMonth; +var activeSelectBoxYear; +var activeSelectBoxHour; +var activeSelectBoxMinute; + +var iframeObj = false; +//// fix for EI frame problem on time dropdowns 09/30/2006 +var iframeObj2 =false; + +function EIS_FIX_EI1(where2fixit) +{ + if (!iframeObj2) + return; + iframeObj2.style.display = 'block'; + iframeObj2.style.height = $(where2fixit).offsetHeight+1; + iframeObj2.style.width= $(where2fixit).offsetWidth; + iframeObj2.style.left=getLeftPos($(where2fixit))+1-calendar_offsetLeft; + iframeObj2.style.top=getTopPos($(where2fixit))-$(where2fixit).offsetHeight-calendar_offsetTop; +} + +function EIS_Hide_Frame() +{ + if(iframeObj2) + iframeObj2.style.display = 'none'; +} + +//// fix for EI frame problem on time dropdowns 09/30/2006 +var returnDateToYear; +var returnDateToMonth; +var returnDateToDay; +var returnDateToHour; +var returnDateToMinute; + +var inputYear; +var inputMonth; +var inputDay; +var inputHour; +var inputMinute; +var calendarDisplayTime = false; + +var selectBoxHighlightColor = '#FF0000'; // Highlight color of select boxes +var selectBoxRolloverBgColor = '#F0F0F0'; // Background color on drop down lists(rollover) + +var selectBoxMovementInProgress = false; +var activeSelectBox = false; + +function cancelCalendarEvent() +{ + return false; +} + +function isLeapYear(inputYear) +{ + return inputYear % 400 == 0 || (inputYear % 4 == 0 && inputYear % 100 != 0); +} + +var activeSelectBoxMonth = false; +var activeSelectBoxDirection = false; + +function selectElt(elt, value) +{ + elt.style.fontWeight = value ? 'bold' : ''; +} + +function scrollMonthYear() +{ + activeSelectBox = this; + + if (this.id.indexOf('UpDiv') >= 0 || this.id.indexOf('DownDiv') >= 0) + { + //if (this.className=='monthYearActive') + selectBoxMovementInProgress = true; + //else + // selectBoxMovementInProgress = false; + + if (this.id.indexOf('UpDiv') >=0 ) + activeSelectBoxDirection = -1; + else + activeSelectBoxDirection = 1; + setTimeout(slideCalendarSelectBox, speedOfSelectBoxSliding); + } + else + selectBoxMovementInProgress = false; +} + +function showMonthDropDown() +{ + if($('monthDropDown').style.display=='block'){ + $('monthDropDown').style.display='none'; + //// fix for EI frame problem on time dropdowns 09/30/2006 + EIS_Hide_Frame(); + }else{ + $('monthDropDown').style.display='block'; + $('yearDropDown').style.display='none'; + $('hourDropDown').style.display='none'; + $('minuteDropDown').style.display='none'; + if (MSIE) + { EIS_FIX_EI1('monthDropDown')} + //// fix for EI frame problem on time dropdowns 09/30/2006 + } +} + +function showYearDropDown() +{ + if($('yearDropDown').style.display=='block'){ + $('yearDropDown').style.display='none'; + //// fix for EI frame problem on time dropdowns 09/30/2006 + EIS_Hide_Frame(); + }else{ + $('yearDropDown').style.display='block'; + $('monthDropDown').style.display='none'; + $('hourDropDown').style.display='none'; + $('minuteDropDown').style.display='none'; + if (MSIE) + { EIS_FIX_EI1('yearDropDown')} + //// fix for EI frame problem on time dropdowns 09/30/2006 + + } + +} +function showHourDropDown() +{ + if($('hourDropDown').style.display=='block'){ + $('hourDropDown').style.display='none'; + //// fix for EI frame problem on time dropdowns 09/30/2006 + EIS_Hide_Frame(); + }else{ + $('hourDropDown').style.display='block'; + $('monthDropDown').style.display='none'; + $('yearDropDown').style.display='none'; + $('minuteDropDown').style.display='none'; + if (MSIE) + { EIS_FIX_EI1('hourDropDown')} + //// fix for EI frame problem on time dropdowns 09/30/2006 + } + +} +function showMinuteDropDown() +{ + if($('minuteDropDown').style.display=='block'){ + $('minuteDropDown').style.display='none'; + //// fix for EI frame problem on time dropdowns 09/30/2006 + EIS_Hide_Frame(); + }else{ + $('minuteDropDown').style.display='block'; + $('monthDropDown').style.display='none'; + $('yearDropDown').style.display='none'; + $('hourDropDown').style.display='none'; + if (MSIE) + { EIS_FIX_EI1('minuteDropDown')} + //// fix for EI frame problem on time dropdowns 09/30/2006 + } + +} + +function selectMonth() +{ + var elt; + + $('calendar_month_txt').innerHTML = this.innerHTML + currentMonth = this.id.replace(/[^\d]/g,''); + + $('monthDropDown').style.display='none'; + //// fix for EI frame problem on time dropdowns 09/30/2006 + EIS_Hide_Frame(); + for(var no=0;no=0){ + currentMonth=currentMonth-1;; + if(currentMonth<0){ + currentMonth=11; + currentYear=currentYear-1; + } + }else{ + currentMonth=currentMonth+1;; + if(currentMonth>11){ + currentMonth=0; + currentYear=currentYear/1+1; + } + } + + updateMonthDiv(); + writeCalendarContent(); +} + +function createMonthDiv(){ + var div = document.createElement('DIV'); + div.className='monthYearPicker'; + div.id = 'monthPicker'; + + for(var no=0;no=0){ + var startYear = yearItems[1].innerHTML/1 -1; + if(activeSelectBoxYear) + selectElt(activeSelectBoxYear, false); + }else{ + var startYear = yearItems[1].innerHTML/1 +1; + if(activeSelectBoxYear) + selectElt(activeSelectBoxYear, false); + } + + for(var no=1;no= 0) + { + var startHour = hourItems[1].innerHTML/1 - 1; + if(startHour < 0) startHour = 0; + } + else + { + var startHour = hourItems[1].innerHTML/1 + 1; + if(startHour >14 ) startHour = 14; + } + + if(activeSelectBoxHour) + selectElt(activeSelectBoxHour, false); + + for (var no = 1; no < (hourItems.length - 1); no++) + { + hourItems[no].innerHTML = padleft(startHour+no-1, '00'); + hourItems[no].id = 'hourDiv' + padleft(startHour+no-1, '00'); + } + + if (activeSelectBoxHour) + { + selectElt(activeSelectBoxHour, false); + if ($('hourDiv'+currentHour)) + { + activeSelectBoxHour = $('hourDiv' + currentHour); + selectElt(activeSelectBoxHour, true); + } + } +} + +function updateYearDiv() +{ + var yearSpan = 5; + if (turnOffYearSpan) { + yearSpan = 0; + } + var div = $('yearDropDown'); + var yearItems = div.getElementsByTagName('DIV'); + for(var no=1;no'; + subDiv.onclick = changeSelectBoxYear; + subDiv.onmouseover = scrollMonthYear; + subDiv.onmouseout = function(){ selectBoxMovementInProgress = false;}; + subDiv.onselectstart = cancelCalendarEvent; + div.appendChild(subDiv); + } else { + startYear = d.getFullYear()/1 - 0; + yearSpan = 2; + } + + for(var no=startYear;no<(startYear+yearSpan);no++){ + var subDiv = document.createElement('DIV'); + subDiv.innerHTML = no; + //subDiv.onmouseover = scrollMonthYear; + //subDiv.onmouseout = scrollMonthYear; + subDiv.onclick = selectYear; + subDiv.id = 'yearDiv' + no; + subDiv.onselectstart = cancelCalendarEvent; + div.appendChild(subDiv); + if(currentYear && currentYear==no){ + selectElt(subDiv, true); + activeSelectBoxYear = subDiv; + } + } + if (! turnOffYearSpan) { + var subDiv = document.createElement('DIV'); + subDiv.id = 'yearDownDiv'; + subDiv.style.height = "13px"; + subDiv.innerHTML = ''; + subDiv.onclick = changeSelectBoxYear; + subDiv.onmouseover = scrollMonthYear; + subDiv.onmouseout = function(){ selectBoxMovementInProgress = false;}; + subDiv.onselectstart = cancelCalendarEvent; + div.appendChild(subDiv); + } + return div; +} + +/* This function creates the hour div at the bottom bar */ + +function slideCalendarSelectBox() +{ + if(selectBoxMovementInProgress){ + if(activeSelectBox.parentNode.id=='hourDropDown') + changeSelectBoxHour(false,activeSelectBox); + else if(activeSelectBox.parentNode.id=='yearDropDown') + changeSelectBoxYear(false,activeSelectBox); + setTimeout(slideCalendarSelectBox, speedOfSelectBoxSliding); + } +} + +function createHourDiv() +{ + if(!$('hourDropDown')){ + var div = document.createElement('DIV'); + div.className='monthYearPicker'; + }else{ + var div = $('hourDropDown'); + var subDivs = div.getElementsByTagName('DIV'); + for(var no=0;no14)startHour=14; + + var subDiv = document.createElement('DIV'); + //subDiv.innerHTML = '  - '; + subDiv.style.height = '13px'; + subDiv.id = 'hourUpDiv'; + subDiv.innerHTML = ''; + subDiv.onclick = changeSelectBoxHour; + subDiv.onmouseover = scrollMonthYear; + subDiv.onmouseout = function(){ selectBoxMovementInProgress = false;}; + subDiv.onselectstart = cancelCalendarEvent; + div.appendChild(subDiv); + + for(var no=startHour;no'; + //subDiv.innerHTML = '  + '; + subDiv.onclick = changeSelectBoxHour; + subDiv.onmouseover = scrollMonthYear; + subDiv.onmouseout = function(){ selectBoxMovementInProgress = false;}; + subDiv.onselectstart = cancelCalendarEvent; + div.appendChild(subDiv); + + return div; +} +/* This function creates the minute div at the bottom bar */ + +function createMinuteDiv() +{ + if(!$('minuteDropDown')){ + var div = document.createElement('DIV'); + div.className='monthYearPicker'; + }else{ + var div = $('minuteDropDown'); + var subDivs = div.getElementsByTagName('DIV'); + for(var no=0;no0){ + calendarContentDiv.removeChild(existingTable[0]); + } + + var calTable = document.createElement('TABLE'); + calTable.width = '100%'; + calTable.cellSpacing = '0'; + calendarContentDiv.appendChild(calTable); + + var calTBody = document.createElement('TBODY'); + calTable.appendChild(calTBody); + var row = calTBody.insertRow(-1); + row.className = 'calendar_week_row'; + if (showWeekNumber) + { + var cell = row.insertCell(-1); + cell.innerHTML = weekString; + cell.className = 'calendar_week_column'; + cell.style.backgroundColor = selectBoxRolloverBgColor; + } + + for(var no=0;no0 && colCounter%7==0){ + var row = calTBody.insertRow(-1); + if (showWeekNumber) { + var cell = row.insertCell(-1); + cell.className = 'calendar_week_column'; + var week = getWeek(currentYear,currentMonth,no); + cell.innerHTML = week; // Week + cell.style.backgroundColor = selectBoxRolloverBgColor; + } + } + var cell = row.insertCell(-1); + if(currentYear==inputYear && currentMonth == inputMonth && no==inputDay) + cell.className='activeDay'; + else + cell.className='normalDay'; + cell.innerHTML = no; + if (calendarDisplayTime) + cell.onclick = setDate; + else + cell.onclick = pickDate; + colCounter++; + } + + if(!document.all){ + if(calendarContentDiv.offsetHeight) + $('topBar').style.top = calendarContentDiv.offsetHeight + $('timeBar').offsetHeight + $('topBar').offsetHeight -1 + 'px'; + else{ + $('topBar').style.top = ''; + $('topBar').style.bottom = '0px'; + } + + } + + if(iframeObj){ + if(!calendarContentDivExists)setTimeout('resizeIframe()',350);else setTimeout('resizeIframe()',10); + } +} + +function resizeIframe() +{ + iframeObj.style.width = calendarDiv.offsetWidth + 'px'; + iframeObj.style.height = calendarDiv.offsetHeight + 'px' ; +} + +function pickTodaysDate(n) +{ + var d = new Date(); + if (n !== undefined) + d.setDate(d.getDate() + n); + + currentMonth = d.getMonth(); + currentYear = d.getFullYear(); + inputDay = d.getDate(); + pickDate(null, inputDay); +} + +function setDate(e, day) +{ + inputDay = this.innerHTML; + inputMonth = currentMonth; + inputYear = currentYear; + writeCalendarContent(); +} + +function pickDate(e, day) +{ + var month; + + month = currentMonth/1 + 1; + if (month < 10) + month = '0' + month; + + if (!day && this && this.innerHTML) + day = this.innerHTML; + else + day = inputDay/1; + + if (day/1 < 10) + day = '0' + day; + + if (returnFormat) + { + returnFormat = returnFormat.replace('dd',day); + returnFormat = returnFormat.replace('mm',month); + returnFormat = returnFormat.replace('yyyy',currentYear); + returnFormat = returnFormat.replace('hh',currentHour); + returnFormat = returnFormat.replace('nn',currentMinute); + returnFormat = returnFormat.replace('d',day/1); + returnFormat = returnFormat.replace('m',month/1); + + returnDateTo.value = returnFormat; + } + else + { + for(var no=0;no= 0 ? 1 : 0)) + 'px'; + //calendarDiv.style.top = (getTopPos(inputObj), calendarDiv) + 'px'; + moveElementUnder(calendarDiv, inputObj, 0, 0); + if(iframeObj){ + iframeObj.style.left = calendarDiv.style.left; + iframeObj.style.top = calendarDiv.style.top; + //// fix for EI frame problem on time dropdowns 09/30/2006 + iframeObj2.style.left = calendarDiv.style.left; + iframeObj2.style.top = calendarDiv.style.top; + } + +} + +function initCalendar() +{ + if(MSIE){ + iframeObj = document.createElement('IFRAME'); + iframeObj.style.filter = 'alpha(opacity=0)'; + iframeObj.style.position = 'absolute'; + iframeObj.border='0px'; + iframeObj.style.border = '0px'; + iframeObj.style.backgroundColor = '#FF0000'; + //// fix for EI frame problem on time dropdowns 09/30/2006 + iframeObj2 = document.createElement('IFRAME'); + iframeObj2.style.position = 'absolute'; + iframeObj2.border='0px'; + iframeObj2.style.border = '0px'; + iframeObj2.style.height = '1px'; + iframeObj2.style.width = '1px'; + //// fix for EI frame problem on time dropdowns 09/30/2006 + // Added fixed for HTTPS + iframeObj2.src = 'blank.html'; + iframeObj.src = 'blank.html'; + document.body.appendChild(iframeObj2); // gfb move this down AFTER the .src is set + document.body.appendChild(iframeObj); + } + + calendarDiv = $("calendarDiv"); + if (calendarDiv == null) + { + calendarDiv = document.createElement('div'); + calendarDiv.id = 'calendarDiv'; + document.getElementsByTagName("body")[0].appendChild(calendarDiv); + } + + writeBottomBar(); + writeTopBar(); + + /*if(!currentYear){ + var d = new Date(); + currentMonth = d.getMonth(); + currentYear = d.getFullYear(); + }*/ + + writeCalendarContent(); +} + +function setTimeProperties() +{ + if (!calendarDisplayTime) + { + $('timeBar').style.display='none'; + $('timeBar').style.visibility='hidden'; + //$('todaysDateString').style.width = '100%'; + $('dayPlus1').style.display = 'inline-table'; + $('dayPlus2').style.display = 'inline-table'; + } + else + { + $('timeBar').style.display='block'; + $('timeBar').style.visibility='visible'; + $('hourDropDown').style.top = $('calendar_minute_txt').parentNode.offsetHeight + calendarContentDiv.offsetHeight + $('topBar').offsetHeight + 'px'; + $('minuteDropDown').style.top = $('calendar_minute_txt').parentNode.offsetHeight + calendarContentDiv.offsetHeight + $('topBar').offsetHeight + 'px'; + $('minuteDropDown').style.right = '50px'; + $('hourDropDown').style.right = '50px'; + //$('todaysDateString').style.width = ''; + $('dayPlus1').style.display = 'none'; + $('dayPlus2').style.display = 'none'; + } +} + +function calendarSortItems(a,b) +{ + return a/1 - b/1; +} + +function trim(str) +{ + return str.replace(/^\s+|\s+$/g, ''); +} + +function padleft(str, fmt) +{ + str = str.toString(); + return fmt.substr(0, fmt.length - str.length) + str; +} + +function displayCalendar(inputField, format, buttonObj, displayTime, submit) +{ + var input, pos, date, time; + var items, d; + + calendarDisplayTime = displayTime == true; + must_submit = submit; + + d = new Date(); + d = new Date(d.getFullYear(), d.getMonth(), d.getDate()); + + input = trim(inputField.value); + + if (input != '') + { + pos = input.indexOf(' '); + if (pos > 0) + { + date = input.substr(0, pos); + time = trim(input.substr(pos)); + } + else + { + date = input; + time = '00:00'; + } + + try + { + items = date.split(/[\/]/gi); + + inputDay = items[0]/1; + currentMonth = items[1]/1; + currentYear = items[2]/1; + + items = time.split(/:/gi); + + currentHour = items[0]/1; + currentMinute = items[1]/1; + + if (isFinite(currentYear) && isFinite(currentMonth) && isFinite(inputDay) && isFinite(currentHour) && isFinite(currentMinute)) + d = new Date(currentYear, currentMonth - 1, inputDay, currentHour, currentMinute, 0); + } + catch(e) + { + } + } + + currentMonth = padleft(d.getMonth() + 1, '00'); + currentYear = padleft(d.getFullYear(), '0000'); + currentHour = padleft(d.getHours(), '00'); + currentMinute = padleft(d.getMinutes(), '00'); + + currentMonth--; + + inputDay = d.getDate()/1; + inputYear = currentYear; + inputMonth = currentMonth; + + if (!calendarDiv) + { + initCalendar(); + } + else + { + if (calendarDiv.style.display == 'block') + { + closeCalendar(); + return false; + } + + writeCalendarContent(); + } + + returnFormat = format; + returnDateTo = inputField; + + calendarDiv.style.visibility = 'visible'; + calendarDiv.style.display = 'block'; + positionCalendar(buttonObj); + + if(iframeObj){ + iframeObj.style.display = ''; + iframeObj.style.height = '140px'; + iframeObj.style.width = '195px'; + iframeObj2.style.display = ''; + iframeObj2.style.height = '140px'; + iframeObj2.style.width = '195px'; + } + + setTimeProperties(); + updateYearDiv(); + updateMonthDiv(); + updateMinuteDiv(); + updateHourDiv(); + + var hShadow = $('gw-modal'); + hShadow.style.visibility = 'visible'; + hShadow.onclick = closeCalendar; +} diff --git a/comp/src/gb.web.form/.hidden/control/webbutton.png b/comp/src/gb.web.form/.hidden/control/webbutton.png new file mode 120000 index 00000000..23d989b3 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webbutton.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/button.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webcheckbox.png b/comp/src/gb.web.form/.hidden/control/webcheckbox.png new file mode 120000 index 00000000..b8fe6fbd --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webcheckbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/checkbox.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webcombobox.png b/comp/src/gb.web.form/.hidden/control/webcombobox.png new file mode 120000 index 00000000..ccfd857b --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webcombobox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/combobox.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webcontainer.png b/comp/src/gb.web.form/.hidden/control/webcontainer.png new file mode 100644 index 0000000000000000000000000000000000000000..0d4551280dc4bc6289f35fc30d164d41118dad3f GIT binary patch literal 109 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnL3?x0byx0z;*aCb)T!HlehK938rg;Kcj3q&S z!3+-1ZlnP@qMj~}Asp9}fAF*L@bd8VH1{)D@-q0@h>HvOP4faOVDNPHb6Mw<&;$Sx Cf*G{{ literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.hidden/control/webdatebox.png b/comp/src/gb.web.form/.hidden/control/webdatebox.png new file mode 120000 index 00000000..119ec46b --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webdatebox.png @@ -0,0 +1 @@ +../../../gb.form/.hidden/control/datebox.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webdatechooser.png b/comp/src/gb.web.form/.hidden/control/webdatechooser.png new file mode 120000 index 00000000..e9aed8c9 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webdatechooser.png @@ -0,0 +1 @@ +../../../gb.form/.hidden/control/datechooser.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webexpander.png b/comp/src/gb.web.form/.hidden/control/webexpander.png new file mode 120000 index 00000000..fbdd33c7 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webexpander.png @@ -0,0 +1 @@ +../../../gb.form/.hidden/control/expander.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webhbox.png b/comp/src/gb.web.form/.hidden/control/webhbox.png new file mode 120000 index 00000000..891ef5b1 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webhbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/hbox.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webhtml.png b/comp/src/gb.web.form/.hidden/control/webhtml.png new file mode 100644 index 0000000000000000000000000000000000000000..ca4ef5d505fce0007a065eeef76a2c232bd5a8ba GIT binary patch literal 403 zcmV;E0c`$>P)kdg00046Nkl1nmSBeE>@(eLy0bY-OP& zg$W^`*TT5&W`8nfvB+1=oSiwdbLMhk=!q$Ugb+b9OvFas5y z5sZIIK;;U3F5qvV`x@vdk>re5rtEpOo4_=0oc1$`u|or)M%t4=8VjMpc5v}6Q$lF7 zPr}yL5stg#1P8PcN-TLdPjbX3!X>6lKS5Y?%uTKkJ~(8;ZD1ihu;MYU`qV(43WDVp zTL|aO_AFiSt${f%5L#|A%{{_9yNo*24MzgsjIl=(;f_V`3v5wGsF1g{LQev#JR;QD z3@SjKB7$&@0cg}52^icU2nA*cB}J_Iro34)n{L;<$wK|IF{+lGOE-tdS3p1$CIs5f+0!3fhj28M3X5&M&Qx|0ne}y zu08851Q*N>Ug7B_F~RZ9gvn~{4e^J4codY6-uV#!V6LRtj~AZ~KEJ;GDD&Ocnulvo4n;lpMibhXYKbLh*2~7ZWni)#~ literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.hidden/control/webmenubar.png b/comp/src/gb.web.form/.hidden/control/webmenubar.png new file mode 100644 index 0000000000000000000000000000000000000000..20648ca34d68a1e43598f122211048c09a8056fe GIT binary patch literal 472 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy+XH+;T!D02S{jgCzkdC@ckkZ5 zeG6oFc6M&rvgO5#7eIDHL&Ls(`<^^`5*iwM`0(M^uU{WIas;UI)TvV+KYqM$;lis| zuYf8SFJ65A{(YeO_V)J6moEbuB_$Y8F@OI2Ns}i1-d?g0sNbL@ z$S;_Il~LetFq70@A7+_<&dk3ZzT12;eQ)sKZyx)Q(p5$u^;Bn2qYW3LWVT(mKm)6sjAO7EedTQ=^{^TT^)oQkx zx1t;D6$}^}3!S(g{&^HT)9XhW`{kMP6CHQXKl{}4Rr13iovEyiTGuy3ZFXrqT-EH- zxHyg5wJ~&eawpUMUA983`@RQ?EIN4M8YlnK8Sale)p%x|dvqi4mdKI;Vst0RBSF!Tr<>lq=?d<>n0Dypi z|NsB?`ux1t>8Hx$yVvRU`TXni`1kw$=H}+z-Q9Gp(KLpQ&TlHHD6y}+uPe@WMoxURXRF4pP!%k`T6?%{foNU zin-a(;_rL0)GC0vd9c*^`~CCt^V!+i{{H@rjg4(>Z4VC*$H&Lz<>g05N2jN!>+|_X zlEX2DyR6OR>+9>UudjG`csDmUN=izmrlvfLzbt~fN|VG`oXAp`$A*T6Mn*=Rot>JR znp<02y1KeZNJ#4P_(PAvkGAsz?Ck88 zmX<9oEki>?Xl1eui!T>p$Vk5v|-{py2uJt_4Ex4jf_=HOwC{l%q@@=sOei;S=-p!**iF5 zSKwse?BeR??&0a>t)k-N>*pV!VjdV291;pu5Ekwk5gFy_=@}gptD+JYpOBcOVxF9m znwAb#kdf&L0a@7~LvwQT@>R?WR8$K6p$hD>yo!qTOG<6ZGJqoG6_r&$1=T7lHMLL$ zb@dI6P0cNOt!*F!+B;M_yAsU1dsKS+pbGjYOq?`%%G7Do!OosBbJpxRp>qr7)z0sL z`C>to!@@<2m&8ImwshI@!WAo*uL?j4NlnMqjceAz!k}kedcpb)J-Fins9;vX#!Wpi z1sGtna{8=UTeenhhD0|eU}OR+n6-_Wg%w8k4UU7jwEAs)w*b9~a){Z~HiAXjmA z#%Kuw?b%Q6VQoIIB_&)WBwS{mxW>z>!^6X^X}Unp_k}{U z?ZsQtk{aI*tzc|E$RNhn)-2J)?rD5L&e{4S=gR{M9L<5uZo)#LhZ3$Zu(7GNUH3nE t?a!3Pho;HN5B|^OS(GyC!z@EthL}F--%iTOCxNbF@O1TaS?83{1OP=SM_K>? literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.hidden/control/webtabpanel.png b/comp/src/gb.web.form/.hidden/control/webtabpanel.png new file mode 120000 index 00000000..7be554f7 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webtabpanel.png @@ -0,0 +1 @@ +../../../gb.form/.hidden/control/tabpanel.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webtextarea.png b/comp/src/gb.web.form/.hidden/control/webtextarea.png new file mode 120000 index 00000000..dd1d94b9 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webtextarea.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/textarea.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webtextbox.png b/comp/src/gb.web.form/.hidden/control/webtextbox.png new file mode 120000 index 00000000..276853b4 --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webtextbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/textbox.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webtimer.png b/comp/src/gb.web.form/.hidden/control/webtimer.png new file mode 120000 index 00000000..0ae99c8a --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webtimer.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/timer.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.hidden/control/webuploadarea.png b/comp/src/gb.web.form/.hidden/control/webuploadarea.png new file mode 100644 index 0000000000000000000000000000000000000000..0fac5b2ec05670b2127600c217651c80383cc7e6 GIT binary patch literal 245 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv#Q>iWS0L>G2KM&$KpH|qME*B4 z02#>vasPpeI7@>3f*E{XybSppvtYj5oiz)9f>oX_jv*T7lV^0YNvIhtVDQW0;?P{g znW5_>9I>_Q9aGOSJ_SaxWX2T?pH~@7aCoJ~yyL*7meAEV7Cm6n30!?BY^_)9)+>x@ z8WZ+ri3W(|aR-zopr02`1^9{>OV literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.hidden/control/webuploadbutton.png b/comp/src/gb.web.form/.hidden/control/webuploadbutton.png new file mode 100644 index 0000000000000000000000000000000000000000..ebd4f966e9b493f33a72ca639fcde4a49bbaeef1 GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4U37#&FAs)w*6C_xf`6Z;8|DR70 zP!0Z`eXOLdExiB#e`R62fA>FhwK|4OdMH1WZ$e6fKx$&b5C1@k4F(f({}&h~mB_4W1h^YilZ^6>ER?(XhnWMpAs zVcy=}?Ck7dU|?NcUEST?>+9>>+}!Eu>Dt=bTwGk|=jYhi*yZKr*4EbI;^NHA%;4bQ zfU4wxwg3PB|K#N4#hA#Y00003bW%=J08#2038Sq5006g1L_t(I%hl4$4uUWch2hfT z1&gS-JqtrSi75+Le(7c={{}KD(k4JhO2>4;S^H=>Gz8F7Vyy_to hjOIUr+0UH&QEyzKNGb36V`BgS002ovPDHLkV1h2Hy|MrR literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.hidden/control/webvbox.png b/comp/src/gb.web.form/.hidden/control/webvbox.png new file mode 120000 index 00000000..50d7662d --- /dev/null +++ b/comp/src/gb.web.form/.hidden/control/webvbox.png @@ -0,0 +1 @@ +../../../../../app/src/gambas3/img/control/vbox.png \ No newline at end of file diff --git a/comp/src/gb.web.form/.icon.png b/comp/src/gb.web.form/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a9254f949f86671b58a744a6a93623576acc5217 GIT binary patch literal 10569 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 literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.lang/fr.po b/comp/src/gb.web.form/.lang/fr.po new file mode 100644 index 00000000..76dfe156 --- /dev/null +++ b/comp/src/gb.web.form/.lang/fr.po @@ -0,0 +1,274 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.web.form 3.9.90\n" +"POT-Creation-Date: 2017-08-25 21:27 UTC\n" +"PO-Revision-Date: 2017-02-23 01:33 UTC\n" +"Last-Translator: Benoît Minisini \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:2 +msgid "Web application GUI controls" +msgstr "" + +#: FHello.webform:14 +msgid "This is a very important message" +msgstr "" + +#: FHello.webform:18 +msgid "" +"

    Welcome to the gb.web.form component!

    \n" +"

    This component aims at making web application as easy as making desktop applications.

    \n" +"

    This goal is difficult to achieve, but I hope to succeed!

    " +msgstr "" + +#: FHello.webform:34 FMessage.webform:50 +msgid "OK" +msgstr "OK" + +#: Message.class:33 +msgid "Information" +msgstr "Information" + +#: Message.class:45 Webform2.webform:169 +msgid "Warning" +msgstr "Avertissement" + +#: Message.class:51 +msgid "Error" +msgstr "Erreur" + +#: Message.class:57 +msgid "Question" +msgstr "Question" + +#: WebUploader.class:36 +msgid "Abort" +msgstr "Annuler" + +#: WebUploader.class:59 +msgid "Upload file..." +msgstr "Envoyer un fichier..." + +#: Webform1.webform:35 +msgid "gb.web.form test" +msgstr "" + +#: Webform1.webform:44 +msgid "Header" +msgstr "" + +#: Webform1.webform:65 +msgid "Hello world!" +msgstr "" + +#: Webform1.webform:69 +msgid "Reset text" +msgstr "" + +#: Webform1.webform:73 +msgid "Change text" +msgstr "" + +#: Webform1.webform:77 +msgid "0" +msgstr "" + +#: Webform1.webform:81 +msgid "Border" +msgstr "" + +#: Webform1.webform:85 +msgid "Toggle enabled" +msgstr "" + +#: Webform1.webform:111 +msgid "Absolute !" +msgstr "" + +#: Webform1.webform:123 +msgid "B1" +msgstr "" + +#: Webform1.webform:128 +msgid "B5" +msgstr "" + +#: Webform1.webform:133 +msgid "B7" +msgstr "" + +#: Webform1.webform:138 +msgid "B6" +msgstr "" + +#: Webform1.webform:143 +msgid "B2" +msgstr "" + +#: Webform1.webform:148 +msgid "B3" +msgstr "" + +#: Webform1.webform:153 +msgid "B4" +msgstr "" + +#: Webform1.webform:158 +msgid "B8" +msgstr "" + +#: Webform2.webform:67 +msgid "This is webform2" +msgstr "" + +#: Webform2.webform:73 +msgid "File" +msgstr "" + +#: Webform2.webform:76 +msgid "New" +msgstr "" + +#: Webform2.webform:82 +msgid "Open" +msgstr "" + +#: Webform2.webform:88 +msgid "Project" +msgstr "" + +#: Webform2.webform:91 +msgid "Compile" +msgstr "" + +#: Webform2.webform:97 +msgid "Compile all" +msgstr "" + +#: Webform2.webform:105 +msgid "Translate" +msgstr "" + +#: Webform2.webform:113 +msgid "Teleport" +msgstr "" + +#: Webform2.webform:121 +msgid "Quit" +msgstr "" + +#: Webform2.webform:159 +msgid "A button" +msgstr "" + +#: Webform2.webform:164 +msgid "Add window" +msgstr "" + +#: Webform2.webform:174 +msgid "Toggle timer" +msgstr "" + +#: Webform2.webform:179 +msgid "Hidden" +msgstr "" + +#: Webform2.webform:202 +msgid "General" +msgstr "" + +#: Webform2.webform:205 +msgid "Hello !" +msgstr "" + +#: Webform2.webform:209 +msgid "Check and uncheck me!" +msgstr "" + +#: Webform2.webform:216 Webform6.webform:12 +msgid "Élément 1" +msgstr "" + +#: Webform2.webform:216 Webform6.webform:12 +msgid "Élément 2" +msgstr "" + +#: Webform2.webform:216 Webform6.webform:12 +msgid "Élément 3" +msgstr "" + +#: Webform2.webform:216 +msgid "Élément 4" +msgstr "" + +#: Webform2.webform:247 +msgid "New element" +msgstr "" + +#: Webform2.webform:251 +msgid "Clear" +msgstr "" + +#: Webform2.webform:268 +msgid "This is an expander container" +msgstr "" + +#: Webform2.webform:277 +msgid "Alpha" +msgstr "" + +#: Webform2.webform:281 +msgid "Beta" +msgstr "" + +#: Webform2.webform:286 +msgid "Gamma" +msgstr "" + +#: Webform2.webform:290 +msgid "Gambas Almost Means Basic!" +msgstr "" + +#: Webform2.webform:299 +msgid "Please enter some text:" +msgstr "" + +#: Webform2.webform:318 +msgid "Options" +msgstr "" + +#: Webform2.webform:321 +msgid "Test" +msgstr "" + +#: Webform2.webform:324 +msgid "Application state" +msgstr "" + +#: Webform3.webform:24 +msgid "Tab 1" +msgstr "" + +#: Webform3.webform:27 +msgid "Message" +msgstr "" + +#: Webform3.webform:36 +msgid "Tab 2" +msgstr "" + +#: Webform4.webform:28 +msgid "Select file" +msgstr "" + +#: Webform4.webform:45 +msgid "Show" +msgstr "" + +#: Webform4.webform:49 +msgid "Hide" +msgstr "" diff --git a/comp/src/gb.web.form/.lang/nl.mo b/comp/src/gb.web.form/.lang/nl.mo new file mode 100644 index 0000000000000000000000000000000000000000..e7ed2355d6482da951551fa71b9106341579bb57 GIT binary patch literal 3096 zcmZvcYiL|W6vszxeXO?XTO({#Iy-EGq*YMM6PBveG4y?6HR?ato0 z%)Og5A|iee710lBeS9cNMG*WXMG!>6QXdo*sr@4O0I3f|DT0Df#Q(XO?XJbyJ@+?f z?mW&pbI$#C<&qZ|S{bqi(t9Ce`@!N8JkVZU%Gd?qYv3vH2zV*Da4}=(vd6#__=M$i zcKk4S6~Z`u$0|x^}XOS==<$>4W#u=koGko{_GJvE&?C7<4=L) z=OK{hzYOBf4&!kx_=eTr2Pu9Zfi&+6kmC0hNc(?h$A1OM{xg<;fwbHOoCUs`@+`3v|P-v0?wKKy`9 ziKjsfv7ao@faK5LAl?5GC^WAJBz?8j*IV{lW-a?I^Ol2_Lzcsq1(4!b1j)}5cpbPK z#L=v7X}~L?KMax`PlM$DGgd!j`4UKeykdC-qp(g$2U5JY+3_im=HGAm0Ej<((CP}j z4SE1lK0FVu0^hdpKeqf7B)?98wEszv;&&Rn0sI4`bCzR}xWaO!YO`B%14?4$$PzN%I)5Sh4NfpD9O5Kk-<6QX7h>MsvMZ*s~j^$j0q4FIO z`#Rtv;!;ElL_5QkQ6tu2_pB&e>y!QJR_}IvE*r1a$P>}2dpAS-WV$q)-BL%NuV?SZ9{($!5<>&0W*u$yr9Lh;?N?yoBk;o>a%)q`<+o zPCwt(AL!~2w!@YgcQZ56Ln+@=3xgZpI$hbB8Sc!ZnAW7pj0TQ& z5yS!?uKIBbpOOq4l>u)AI%K6<)yM`?Yq8)^pJ_HDgNPC<)^biu4=*#FnvFd<4Gpf+V%EQU1IY}p! zahtR$ol#OAiUAA8w62jqYz)(q(n>fljZ|)8zQHGi%*JI_rSUo?hA)LQzEh9cZeO-g zWAr>%WtpVNu#U)CqsKiKJEHH_MRcre! z6c1lNied)S7i$cI(02GSeJtS>uS==0Mj;7|f1n|GRcjY95@GkO|0y)Phpn@g@}, 2017 +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gb.web.form 3.10.90\n" +"PO-Revision-Date: 2017-08-26 19:52 UTC\n" +"Last-Translator: Willy Raets \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project: 2 +msgid "Web application GUI controls" +msgstr "Web applicatie GUI controls" + +#: FHello.webform: 14 +msgid "This is a very important message" +msgstr "Dit is een heel belangrijke boodschap" + +#: FHello.webform: 18 +msgid "

    Welcome to the gb.web.form component!

    \n

    This component aims at making web application as easy as making desktop applications.

    \n

    This goal is difficult to achieve, but I hope to succeed!

    " +msgstr "-" + +#: FHello.webform: 34 FMessage.webform: 50 +msgid "OK" +msgstr "-" + +#: Message.class: 33 +msgid "Information" +msgstr "Informatie" + +#: Message.class: 45 Webform2.webform: 169 +msgid "Warning" +msgstr "Waarschuwing" + +#: Message.class:51 +msgid "Error" +msgstr "-" + +#: Message.class:57 +msgid "Question" +msgstr "Vraag" + +#: WebUploader.class:36 +msgid "Abort" +msgstr "Afbreken" + +#: WebUploader.class:59 +msgid "Upload file..." +msgstr "Upload bestand..." + +#: Webform1.webform:35 +msgid "gb.web.form test" +msgstr "-" + +#: Webform1.webform:44 +msgid "Header" +msgstr "Hoofding" + +#: Webform1.webform:65 +msgid "Hello world!" +msgstr "Hallo wereld !" + +#: Webform1.webform:69 +msgid "Reset text" +msgstr "Tekst resetten" + +#: Webform1.webform:73 +msgid "Change text" +msgstr "Wijzig tekst" + +#: Webform1.webform:77 +msgid "0" +msgstr "-" + +#: Webform1.webform:81 +msgid "Border" +msgstr "Rand" + +#: Webform1.webform:85 +msgid "Toggle enabled" +msgstr "-" + +#: Webform1.webform:111 +msgid "Absolute !" +msgstr "Absoluut !" + +#: Webform1.webform:123 +msgid "B1" +msgstr "-" + +#: Webform1.webform:128 +msgid "B5" +msgstr "-" + +#: Webform1.webform:133 +msgid "B7" +msgstr "-" + +#: Webform1.webform:138 +msgid "B6" +msgstr "-" + +#: Webform1.webform:143 +msgid "B2" +msgstr "-" + +#: Webform1.webform:148 +msgid "B3" +msgstr "-" + +#: Webform1.webform:153 +msgid "B4" +msgstr "-" + +#: Webform1.webform:158 +msgid "B8" +msgstr "-" + +#: Webform2.webform:67 +msgid "This is webform2" +msgstr "Dit is webformulier2" + +#: Webform2.webform:73 +msgid "File" +msgstr "Bestand" + +#: Webform2.webform:76 +msgid "New" +msgstr "Nieuw" + +#: Webform2.webform:82 +msgid "Open" +msgstr "Openen" + +#: Webform2.webform:88 +msgid "Project" +msgstr "-" + +#: Webform2.webform:91 +msgid "Compile" +msgstr "Compileer" + +#: Webform2.webform:97 +msgid "Compile all" +msgstr "Compileer alles" + +#: Webform2.webform:105 +msgid "Translate" +msgstr "Vertaal" + +#: Webform2.webform:113 +msgid "Teleport" +msgstr "Teleporteer" + +#: Webform2.webform:121 +msgid "Quit" +msgstr "Afsluiten" + +#: Webform2.webform:159 +msgid "A button" +msgstr "Een knop" + +#: Webform2.webform:164 +msgid "Add window" +msgstr "Venster toevoegen" + +#: Webform2.webform:174 +msgid "Toggle timer" +msgstr "-" + +#: Webform2.webform:179 +msgid "Hidden" +msgstr "Verborgen" + +#: Webform2.webform:202 +msgid "General" +msgstr "Algemeen" + +#: Webform2.webform:205 +msgid "Hello !" +msgstr "Hallo !" + +#: Webform2.webform:209 +msgid "Check and uncheck me!" +msgstr "Vink me aan, vink me uit!" + +#: Webform2.webform:216 Webform6.webform:12 +msgid "Élément 1" +msgstr "-" + +#: Webform2.webform:216 Webform6.webform:12 +msgid "Élément 2" +msgstr "-" + +#: Webform2.webform:216 Webform6.webform:12 +msgid "Élément 3" +msgstr "-" + +#: Webform2.webform:216 +msgid "Élément 4" +msgstr "-" + +#: Webform2.webform:247 +msgid "New element" +msgstr "Nieuw element" + +#: Webform2.webform:251 +msgid "Clear" +msgstr "Opschonen" + +#: Webform2.webform:268 +msgid "This is an expander container" +msgstr "Dit is een expander container" + +#: Webform2.webform:277 +msgid "Alpha" +msgstr "-" + +#: Webform2.webform:281 +msgid "Beta" +msgstr "-" + +#: Webform2.webform:286 +msgid "Gamma" +msgstr "-" + +#: Webform2.webform:290 +msgid "Gambas Almost Means Basic!" +msgstr "-" + +#: Webform2.webform:299 +msgid "Please enter some text:" +msgstr "Voer wat tekst in:" + +#: Webform2.webform:318 +msgid "Options" +msgstr "Opties" + +#: Webform2.webform:321 +msgid "Test" +msgstr "-" + +#: Webform2.webform:324 +msgid "Application state" +msgstr "Applicatie status" + +#: Webform3.webform:24 +msgid "Tab 1" +msgstr "-" + +#: Webform3.webform:27 +msgid "Message" +msgstr "Bericht" + +#: Webform3.webform:36 +msgid "Tab 2" +msgstr "-" + +#: Webform4.webform:28 +msgid "Select file" +msgstr "Selecteer bestand" + +#: Webform4.webform:45 +msgid "Show" +msgstr "Weergeven" + +#: Webform4.webform:49 +msgid "Hide" +msgstr "Verberg" + diff --git a/comp/src/gb.web.form/.project b/comp/src/gb.web.form/.project new file mode 100644 index 00000000..d3829ce7 --- /dev/null +++ b/comp/src/gb.web.form/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +Startup=Webform6 +UseHttpServer=1 +Version=3.12.90 +VersionFile=1 +Component=gb.util +Component=gb.util.web +Component=gb.web +Description="Web application GUI controls" +Authors="Benoît Minisini" +TabSize=2 +Translate=1 +Language=en_US +RunAfter=cp $(FILE) /home/guygle/guygle/cgi-bin/test +Type=Component +Packager=1 +DoNotTranslate=".src/Test" diff --git a/comp/src/gb.web.form/.public/favicon.png b/comp/src/gb.web.form/.public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..7d6acb4bfb851c591279232c229f84a5f70c129e GIT binary patch literal 16241 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 literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.public/gw-arrow-left.png b/comp/src/gb.web.form/.public/gw-arrow-left.png new file mode 100644 index 0000000000000000000000000000000000000000..0fd95f5830e050ecc0af12332d70f323a64601ba GIT binary patch literal 117 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6Y&=~YLp07OCrB_T94qL1$YS^M zG|zv@a}THQJEPz>ZHvWW(YF6Q`WKeY^eDEE)Ak4|>T6`2FSLizm6?HI?@qDLIu6#` Qf#xuHy85}Sb4q9e0Ht;%YXATM literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.public/gw-arrow-right.png b/comp/src/gb.web.form/.public/gw-arrow-right.png new file mode 100644 index 0000000000000000000000000000000000000000..936584c0ac5b4e55c06f4f328d98cbabf8077723 GIT binary patch literal 114 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6EInNuLp07OCoIsmI9Sl<$TPpG znCHKw-s51UV+Av{yy6W0r0+PSz$cubbXJ~ajlv72rh6(90VYfgPYuOBe<=)I2Q-Ai M)78&qol`;+0MJ%-j5Jq2Iv(>E>T*UG$$$>)dV|ff0B!yeO17v{}5EqyLX=88LbO7hfJh&P zi-?V)Fd?}?93&=c?=L8B?luS7(RZh#5 zHzvwkGv&>x^7dT0K%rcsQ7%%cm+6!Xm8Gz~8-i20KGfa?!KqviXLd9}01Z3N3-_JM z_2Kq*1c!2c$|DK^&m#F zfsqXh@lV(MK!eBhXWu=hGQ~}77x^${eV*gaJ^ON+JyM=6eEqvJ`hGpbbBBWGKeJx- Pg1q4A>gTe~DWM4f=O9|0 literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.public/gw-table-more.gif b/comp/src/gb.web.form/.public/gw-table-more.gif new file mode 100644 index 0000000000000000000000000000000000000000..cad57b9e6459100c1761c989a9e1bdf0a84ac520 GIT binary patch literal 723 zcmZ?wbhEHb6ky zFk#7(B}a}N*|>4zy?gg2O`6o$*tl%jvQM8ry?*`r=g*(BXU~57_U(@!KW^Q+b^iSM zCr_Sy`}U0i1{D8u`?-b$J39ur8tEA@GXljG|4BI)r6!i7rYMwWmSiX-W+hhS zY~S+8;RKIf@w#hVu4|_`VKYG`AyI-gVcv>Nnf8hM4%|Al^3A2?z0cq5eZ4zEUtZ=E6w0k7GR^Oh05+pcfkrLia1{(;>V0gIq~dw;l}*3!`O?bg*0Xq*GoNU&OWNT6 zOSJ#Y#9~Vpsmj&Yx_4>K4N}Brk`W|4u5F5V<`Ou0hsUL)pitM7TVfws#l4+&n=$Qh zeBcXZ#@-XpPOv7=^ICcJs}xqhnL~_GDU5Adu_Z`1Zf z!3WHG(|uL?XP7X>>oS;`onEl-K@bNvC+S1XdIEIMicRlU70IXuZQ*@jy8icenS$xu g-@1;>Dr}Yc6{pK!!sZ;gehJn9BH0XUP!a(H0Loeu2mk;8 literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.public/gw-waiting.gif b/comp/src/gb.web.form/.public/gw-waiting.gif new file mode 100644 index 0000000000000000000000000000000000000000..84d7b8cfdb97a27d75c6d4ca3d2be9b372d2b538 GIT binary patch literal 6968 zcmeI$X;c&Dng{S~WG5i|P7(qH2oRF6$WDL|5HMmyut>uqBBCN9f~DR7NeHV+1Ox;n z3d-UF8nq(!28b4rQb6<~E&~ddw$`DRTD6utFQv6Tw{xdoXU>^?%*mIW^Zz~n=Xt}z zIbLg%0wIBrRtN+ELx_q*A?y%MzlK7lw47{7O-`MAGH0M?P_3xu$M9JU*3!$R$%#qT zMO99A4jzlID65E!h#VOnp*hmdcAU-3$bA0wa|<(z#)ig(EeXEfzVF_?dpP~D_tV~j zg9QjALL?D|a6&Y1T+2@5<@~jxv_TWGB&<_WNni%6`V@jSlm-taAt;CG6>%)B#@xd^> z7>g3v=O{`_Lu$pY4ktb+ZrWd6(Bz1hA=;!hZOp>0&eXP!P-T;WM}oa)8{}fA#)-Ct zX0Cx;5Am_}))*b$3?04MI&RgdV8v-qfAO%)y~#wq$%m|P9XKB8 z|If^*4J3ymS=9atw^>^ng^XPuLWl;e=Z)jDd~h21Yz zsAXc8&#_TbSQoF-WfChEWxuQ{?8M>SrR)@2V*(n#t%z}+H{pz;I27yM<&{&{m(0*Z z$lVG{CjL=zw&1MX(hWaf_flV{tS{P|7`Lvan9>weWmW{exwMe3t=&}DPqwl`d~{6J z_Wc&=*K9Wn=1>|JwP1TXzob`Sez@GxBau6-RDHJF2c4jCS2Z70=r*w|!W0P)_Br9v z;SFw^hxTvptJLer>1Q$of(;y|Jc@eR10ON`*d>6kKu|yV_Ld!7*+~xtIyh$ zH5N}onaKqt^v1Y*T7J4&#!BXm{Bd^Nfz&vSL{dGag|cnij1v7*{`5&XD-|g$Yn8** zmDY(oHloy$C`5W#}ll&zmfCy+> zbp%ZPB_)6eXzJ}*aRf~LB_)6eXabIMa&v&Gztr^b@k0P=#p$5}!0NrJ6*vC}Kd5D4 zU{SG1EGdzemX%jjRvngA1jwsvN>Lu>M`|0Z17HxcyLD4*l_8|v#NYn(d8sS4{gNZR z<-#X{{+D`L7|(0xQN0?v9qw3PO#oIi>gzrCo9@;^0K3-ebqQEozsW=PY~K#J54CW&}aw zA?YIW6kKVo7gJc5z7STF?eDEkJ#dj%&i6L>SvsQ)+Z5s0d(&$PIwK8T4q)Z!Z~3ao zli-073;HgOiNzZ3-2UqR2OoqOKXiA@)V63%8x2`9WL+DBt9vhI6emS>JDK03@QN0m zXSQ42JId&~ED>s)oQ8C%+EVp|#KCZpTDQmuq1I^upy~jHCmNyn37t!1_yKmBO9JyyL zf1-i8Gn_2kQLLHQE~+dL7P`|_7Y7j#gLJO@y}=nPGBj@>IKRXMA{0pYb5Wc3?>Z2b z7nlHHJ4Ua`9>^J`tCMXk589j{-GVmH679zp@MX2VfkIEyUR5y6_0i8`>f*=m;)Y{K z)sQDdlhG&r_#g6%i$teIGZpaQ{rPJkZapuHrM)c&2J?*LUk~H5Tgj$mizZAU+MM%} zeaKy16d&lY^i?>PVpVh}l)5CVBI@8<8~x1auc;(oEzJO0l9p2BN5qfq@aVbERJT_7 z%8F#8Gm;LQfQsh6@_WKJ88-H!EQ%ZS;n&;Y>&A0VT_}0iRS18e6+yFgom2NU-9E?| zs2PvlxrHq=74eA(FHt1q7VL%0gE(YTT3=J?kK#7By z09qIfD^SE>Oo3$pG%u)NFeSjO0F4ZW*#9sqK-nJqrEE1DQ7dJ;z99tz%9d>=DEszc z<+Sq;>pqp-oM$an#9lxA{MAQ?%5Q&_@ss7AZ+iogOuP$3EDD-3-Wo-HH?ex7Kkg(Mm?ugSLoN;O-b%d z!}{JUIGuKk2`)Hy;EM4n5?UY|jndCpbJB`%?s4bkC`X3tHcL{2DJ#;X^F|a>%-jY$ zY0@C-yA>i<;=k$1;Wn3w^_^$xp8G7#mZPJb#@v)ax}`x}6f1FWGe)D2z{q8{4f=3O zF3C$C-%Q*XK(o)_n7N(dEZZ4x6{PF^$T^IW?`#;NeV zCy0I8B$1VRapq_^swGS}oiP)-9y1co)4e=G+Zf?w9~l_IdozeJfN*TC{dk974JnCG zc}zfapW7ii*w}Qr3*=suce)eh`k+W7&lFxM`rttJe~if0T_nfQ24j4#@diTsn@WN< ze`j{mPZ2+vU$7Z^p*!1Zr+?x-txjF6{H6d$^I;A$o<$ zRl$RCR7%Q!ahHEL<27MDhs_xvK?nn&?s?ic9w^D8~}I)A>OeMzp} zlIHcgr-g_Wb8OQ1S_A!Ut$G`1pJvI}oi(RV=X&fACL6bypZ1T7E=&CkvGK7YyDq3Z zT(S|tu0adnuw!;U{KlRrzHs;2wZ}8PY*SPQW*FqtHxn%y)<$y=NBXSEVcgSzbP@9h zft#U`!OO({u@>RWb%#|33(cdGxG%zUN2nZ&pY*O=#i#GuZ*~v!IF=8AxiFe#L2#ce zHB{RTu^2%c7q3yc*-!BvIa3>xPdmQ)=jd|e&NKaCE zdEOGSzl1-Nh0*cbaHJUF6`QohhW{-OiZRN|n#I_tx}XIun7%+8Ox+~`#+EM>RCBu) zEqoGUHyA_gMDb!F#30Si&~Zt3t|6Ek2y5h1xnDNrNlbrRT)qDdB%o;J3d)1KTn=pd6QtkIyRO&V52UGAIMIeg2VRQ(=g9+* z7q5L6YE9g&SzC=Q-18+R5TcW7A(x?58P6i;N^2zqW*7tFCf|(HK6*;xYtZ));8Hz9gjoXyrkNggP7;?1V3JSx7$$vP`^ z8&Z6IXrt*VGy6!EwRzXG4X#ItXVz2KUifZ33tsDN-7>}v%s8PBJqn8scONERH?K|L zL34*@6`0zLggSP2vWFdF&5gLY7f)Tpm3h zxTn`5_*C2YDQ@Mh_+eH1oin^s5ls<442e;3mogre)d&qtnEhoJ|(M?{D4Fnh=+SK|b8FYGjnYz*96cuUJf57v@5{|WY+o#+VF z%(f?G>Ar;5iN%IVRQ-E}t~qtl!^ije7K+E{(88ZcShA{7d;MI9?e`Gh*LPv{AIot_ z{|`G!ecsiWeA_}8ih5(BANq6tUg-8zwIC2>LOY7mGHY8bOx>hCYmT?kub@Nsm`bQm zq?Fq9C#BTWuS0ZyJ_B{I<+-QeK@>7}GI+({%~TNqL4 zlojuct9S6%glw~i)f<+@!P{hdj*));&2rBz&Yolu(RCuq880_caXhdweva;4boipH z++qZ4tIOP^omkPP2i3!GaVFqlU40M9X{N+Yf-F}5t~_eYG}9ihK5`NLb;ujj{mCM#Q2 zTIx!S^?rR2T$Qr2RsC`>{0oznt!nqm$+5avy_d4GSRE?(7bBn#R_>GED&aj7Pz);v z%%>{{%pV^FK{2deKC6q>dnSK;y!eZ^zOL7Ur=oc08d{&NrN#B`J#yw{7UXsBvQ43U zj%#V`b5JbFbI{)_^z$Vf73r4R9rtO#H&|(<9K5Xo_!tcZ;E!{8l&RtKkGgD^J2M^xVwNWgI%-Z?C)&sXFg_>T*iY_p4ySKo*!Iylp|i)z8=Y@21`HQk9a zY{j1MDyYpNoZ*@n{wL?I9l(qPJDykBjvQ!k4>mhTQ+Kv!={Mv>8(i}7Qq0@AkoyKN zPVGx6ZTh}f*6DTm@tyUskSAUB%!H}+j=j=qIK5^l-+vvHRLVE%gNQ!JG%SVPadOO^ zIZjTluS(pTexspmDr z-bJ!YN`zBnfp81wd+ARry`Hr_t@3@fkW%&jtoZ+Dy#GD#egDlfo?LOHx~BGMU46r` z#-`&Znw2M|0s0PB1P_A0EzULIl)@3Bx~w9S-R!%%E8$nvm$A&=8>QB)zF~$>x3#l} zwG=Zvtc#gAVN61(;8IWe-C?X}V}Okr@rq-q-P2)~alMWM?XJBPw>V6BRvq9mrRzD0 zYR8ib;%2(5tc#5m{*$`u!$wt^WruvJL2HheCRaow)V%T7%CG<=#eDOV@p3)ihFRDbH89lv0R6Gd733l_xirgnK5R6@4j)smP!|i+Zarw8l&2{tInJ!iiFasZ4?A zz-%ZPhVhd*)ynCL@MUauw@LcW{uUIq+H@pBH%^QA!wcwG(2#_&MnT~Jgqj;bTcTSy zZw>L{`y9U~=WdKeP84S-EDd?53-6#ThCMGx3r)VJ2ByC{wD_>pi$a44hkA6&@YuB><)oh4&D zPCsFa>veZwW|r*LTGJOT7tyP*RR4>Gkr4Ha8mnjZ5{7xoBPQ4cjG$~FX1uF|=y!Ep z!IfgxQHTw0t!e?ib5SZ&V|5A^2c)h!a2+&KYCQfiqfc0b(CdiP#<&rwLpbC+v>GOD z9FsXSa_Gq+gm(+(WV6YlB2-xJ%V0ua4WjsnNOve$>rMVe=x)PS;ULPKcx`Cq* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/comp/src/gb.web.form/.public/message/error.png b/comp/src/gb.web.form/.public/message/error.png new file mode 100644 index 0000000000000000000000000000000000000000..0b792721102820c1043bddcd132b06d37f60c9e5 GIT binary patch literal 1173 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBgK_U|bU56XFWwUJVU>Y#F-GBIux5 zz-`ay8`gd|T|#bqdfs7I4!Qr0k|k@NL)NJMN(nmA9QijN4%Ccbo!2f^LDgT>T)j_Z&j*I7I;2cbvm+ z0(Aiu0A*oTKo~cjgKoP3-2k*M^tN*#P}$SOv|FwrH(f#>=;+^sNI3&#LAGCVfVdMR z3*_DbIR?T8k|7{n&OjG}89>uOt__741U_5n~1o;I6X%wIyT(M;Hk;TjkQ>Vx?S$+KCsrKlh&Z32kQa2?;e*O{@ z{VmEUDam;M?t+CH+NYvi&N4;DG9GvQocf7LzOd|{{Pc|6qU1?safup5QE_TPi5Z!3 zMdf9gTgwlZGv#joC43XM8~B}YxmDqROyM>Aj3<1SuCr(O?K^jY&E4X=j?0fNX992h zJaU?8ug<4?96wr)oWCFycto|!Ue{u}V*H*}60Lo1F9n5b>wYNkJ>p{Z*HiXr+;LK) z&gaexb>s8t--ToHPc8|&A%CkhDmLzp_=h6pN&d-K{~l-e**UHDUbe}?6A9M88u!&* z&iSdoYu=8Fyl1D0HJ(kG8Mu|vbLLM!+pZ};nPs!I)kA-8oBL6>%JARImj6r5*L`AB zfBNsctaJO1VC~1u^*7n}DrZOT&N+RmZEvl0oNdC-)7z>u&gOIHem4qtU*-D4tZ3Fr z=KufNE3IT~+GI8au+5p%xAD)d54$#0$E8m!-Y?Xz{qOUMnJ@M-Xe=(+7LvLaRK75H My85}Sb4q9e07bkNwg3PC literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.public/message/info.png b/comp/src/gb.web.form/.public/message/info.png new file mode 100644 index 0000000000000000000000000000000000000000..1e7aef08541644e81596a62e2f1b81feca067ad7 GIT binary patch literal 828 zcmV-C1H=4@P)I_nqFpo%jFEyq(>B!ysr5 zL%32cF<1a4#DnI5OA*UsTMf9hM`+&TS!D+np>bDS}`Ly~12eS~5OU+Y@dptF2 zNDauXG>E;~%LA~Wt!iQBgC;=kpx(_;VdIdkLH|@nfZR&GSyetwozP&N^-m`T*6m^~ zAAwOLw~PM9yj^rp1G38N2PZcHq*6?yLUVHELqB6HEu$Zr>j)T6#&;B74?fnG&&n#- z9>(1mps5}$d6Nf#Yf?=TfOX~ga8r%Zg9LzVM|EE10Wh0f2Ozp@s9hw5`kNv#uTW_G z4U7Q{aO@6$$Bulj06^GW?powP_<}C-fH`~~Fal&3d%o+xun6n~10c7F1i)b6CB|YU? zi1VPHfVChu2e6`>{>sjI?o#kYH-O)zZ~zm!0mcCi(1>38%m;8l@nTsQ{bkcIzyWUv zZe0KauEqF2xknd34-f;mM=_q8RvLR7ZG_`p#a&Qd<%v3@P7E+K24`ON`#kTG0D#(o z*}Un`72J-y3BO~xntUk8oF&}+MsU{sJ`DhnTHr*kA8?}Up645vN#_IqC+iIG)`x zmip+j`}5`-8AYTT)=jm7@}qPozVC$r03g???W|d|b6v&&dfXZxnk~!FXfZZ0JK11O zYS30Ng9+waed7&j=L^@_VF*{K`&bb`OU7R(7VfKJdH(^L;^7cA+3XAe0000A^FLgyBSZm=#8cRPyVAd#I6T|fuyV3@Rxq3;+-FGO(<*cPAy zAOq;m{{0}+fX;ww0djXUG|UIdcI;;8gg6yq%T9*g-5_^%0gc)TRu5!@5lAo4K(Jsh z(B54PT{{?7tpck7vmsJIHNAVlvOvLI41GXjAzJo=-4ABBZUvdsvlrxmK1gsu>;mcq zGIoMo1+fko&cFbKuz}8)vWMY6Fq9x#z-mAN4py-0DnlnQNP!Lmx*zOus4k#_Js^?p zqd;>wB8<-i&74#c%_kH(`N)UElR^Y}llJ{d&$6Q z?~k7YQ~&*qdDQJ8$@Ho=!doioPH{)w95;r*N_@!z$U*B!GqUPeSL(Q2xC#UVT zy0n}5%WK={ulLm4RIPQBe_J^$Eix#SPf=FneH|_2C~-8tMZ?usK~!_i291kZdqqU! zoIG?-dt5P4)^*(@9O4v`m6bMg`ZN8qX#q2Dmfw6>bA$i+pL3hvpEEY*Ztw_SWG?X~ zy+>ie=I36geN@zgD_x)dbSjy8Gf8#MnrYluwJLnq_AXepIo{=iH=l6e)Rq5?&d2Zc z;yQKnz^Ci$gn4)7J^5@dy|u#W&a>csC(pW^-JW-UVpP=aTRV^4P5w8po%5ajiJ$zZ zZ?hQ?ElL1^G=_4oVE9X z(Cofhj#EP`JvS@f;|VO5cy6m~BK`DkIKxB3DfPB~Kh3$Uh1$ZV)LT3LJlSDX!T$bY zS=ULD9f_@xr_L|DeU#zzjJ*sSI`Y>`NZjq%%kpEzCyxuBT^EfSWIsO^(?4bI z^<|BsF)O28#Rey*dp@2^<6Yl6u9+}}`}C>p^+&d~t_dis*Vp*tKmV_`#Qaq!#V*CW zT-TUv9eF9-@Lp6=jacnwr`G)C-`#Fay!1@?{^#|}e(n%^KQBdp|JAKu54Fm7{kDkN znHc)q{PK;d@)5Uwb$;G^d)5A_@og8*y|Vx7R+sV+Zr4pu7koMye9xPwE^%lLoS!I$zOx)w}E Wy}$KR&;D@-l{cQQelF{r5}E*r%ox@H literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/.public/message/warning.png b/comp/src/gb.web.form/.public/message/warning.png new file mode 100644 index 0000000000000000000000000000000000000000..c67b58f0561b278ec3918e9ff812506b4d9a2617 GIT binary patch literal 588 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sa{_!qT!Hlc{SMbR``p~>duM0h zjjg^wbakuOjjjGSw))-L;tdhG4&-k0yRpUh<~E=Lum7t;A>#Y@JAlNu`rX(FBtd%b z?hd`N&G-6N-|O20AR@Q7c>)>NxA|WK>Gi$2J@Eg329VTtKZvrMTl}trEqb=u7pNDc zeH+kr-?yQ<;z0Kqmjw9*GjLQ^h<*4ZF=M*s??1Xb_IP?QU%tkYnZf(~6@N#&!uOxb zE0$aQvtc|EdLlC7{v)nqt9)ByTr(LM7%(nLAxEPE1X=xM z)^;$x{(JxIvI{0B{U*=lK3;k?@!Ne#y?vP?f3H8XFz`5c=$-Z5&v5}0n8X#oPY-wa zZ>4Y|;9Xn&{KdB#s_pmKvXy^7lhATmuHyUS<5m^3HZ*BInP>T3f4%RAZ|6AD0?fYO z*PoWPA?D}1>++A*3G}yC{EDe=;{C!9|L%81Hrsvsa|(@;2RIjBUfL+^#wE2QfrHJY z;J}Fl?pujZHn8U!WU-lKG%89?IKa0!(Scc;rY3GO9gwi(0ShnB$pgY35|R~;yvpp= z%*~DEqCJBD_cbjv`mnymkawrJ0iWvyzKiqUi(h)ZC(Z0K?{8qRGkCiCxvXU3O!^8jo|80>LVTlx2eG*P~5UZ=JKxP%k$H&RZ z$;HLR`T6gvnO%fG+B z_4W1a?CjLk)YjJ4!otG6y}k4E^Xu#DzP`S@ySwr6@#*R5xw*OT@9*d5=eM`F?d|R6 z=H`Z|BY>nBgQg(0wYB8rlew;bVyw&*E^^=XKWSbewbASY7WaYp>UO`cb6C|Llq6!9TYVztD zAOTG+Z5Ytf;Q|Wi>gmIPo-Rm*fuRu$7#e^Cj7>}-z|7p-0wiFmXaxb*Hnw&k0ec5W z5O8vKadmTd=4N2<@bm%!Zy#Sj|A0Uq28N*E5D*9r3y+A5isofth=~;ifw=gD#H8eS uJ_d#qNhnB7OV0oq%AW}ZS=reF3=9CqRVq%t%^+X^0000tB2$STtgNhGjw@V^B~yqWudlDEsi|O(Dvh9fuCA`EtgQe4|EQ>_si~>IzrTr` zbE>MUW{fkasHlH`e{zVPf0L?khoND6mQikwO>2rwYKp0;sYYRbNo0fo0000000000 zsHmuupoXl*W&Hg7|NsBj@jiIx?&*8q$;=j-0 zztG~p(c+1nvAfLQma)#3vd)*X&X=;!ma@;2u+5UM%!iw;x60g+t;&(D%a5$ejjPFv zsmF+?#)YNDgQLZSm#ehL+l;Bli>Stkrp1P&#Dt^6fuX^Ep1^yYzj&IycbL6%mb-qE zr>()(f1ba5oW6OQymy$pa+SJnlDKS;wrh^HYL2yPkF{!!v~Px;p|{U!j_vQvg5gL)iZHNwfX_=-tZ!`}+j_0~GuM?BLqc$-}+w{-P5lDtSJ};s5{u>q$gG zR2b7^V1NKdCT1v$fkg!fR9V&7*dcrlPIV0pO)YI5T`q1OUOol}emx*x-@wqw*u>OK zKoBSZ"; + Print "
    " + 'Print "displayCalendar($("; JS(Me.Name & ":entry"); "),'dd/mm/yyyy'"; '"; If(bTime And Not bChooseDate, "dd/mm/yyyy hh:nn", "dd/mm/yyyy"); + 'Print ",$("; JS(Me.Name); "),false,false);"; '; If(bTime And Not bChooseDate, "true", "false"); ","; If(bSubmit, "true", "false"); ")\""; + 'Print "\">" + 'WebForm._AddJavascript("gw.load('calendar.css');") + 'WebForm._AddJavascript("gw.load('calendar.js');") + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + If sProp = "text" Then + Try Me.Text = vValue + If Error Then Me.Refresh + Else If sProp = "#popup" Then + FCalendar.Value = Date_Read() + FCalendar.ShowPopup(Me, Align.Right) + Endif + +End + + +Public Sub Clear() + + Date_Write(Null) + +End + +Private Function ReadOnly_Read() As Boolean + + Return $bReadOnly + +End + +Private Sub ReadOnly_Write(Value As Boolean) + + $bReadOnly = Value + Me._SetProperty("ReadOnly", Value) + +End + +Private Function Date_Read() As Date + + Return $dDate + +End + +Private Sub Date_Write(Value As Date) + + $dDate = Value + Me._SetProperty("Date", Value) + Raise Change + +End + +Private Function Text_Read() As String + + If $bDateTime Then + Return Format($dDate, gb.GeneralDate) + Else + Return Format($dDate, gb.ShortDate) + Endif + +End + +Private Sub Text_Write(Value As String) + + If IsDate(Value) Or If IsNull(Value) Then + Date_Write(Val(Value)) + Else + Error.Raise("Invalid date") + Endif + +End + +Private Function DateTime_Read() As Boolean + + Return $bDateTime + +End + +Private Sub DateTime_Write(Value As Boolean) + + $bDateTime = Value + Me._SetProperty("DateTime", Value) + +End diff --git a/comp/src/gb.web.form/.src/Calendar/WebDateChooser.class b/comp/src/gb.web.form/.src/Calendar/WebDateChooser.class new file mode 100644 index 00000000..d65593cb --- /dev/null +++ b/comp/src/gb.web.form/.src/Calendar/WebDateChooser.class @@ -0,0 +1,238 @@ +' Gambas class file + +Export +Inherits WebContainer + +Public Const _IsContainer As Boolean = False +Public Const _Group As String = "Chooser" +Public Const _Properties As String = "*,Value,Border" +Public Const _DrawWith As String = "DateChooser" + +Event Change +Event Click + +Property Value As Date + +Private $dDate As Date +Private $dMonth As Date +Private $dStart As Date +Private $bLock As Boolean + +Private btnPrev As WebButton +Private btnNext As WebButton +Private cmbMonth As WebComboBox +Private txtYear As WebSpinBox +Private panCalendar As WebContainer +Private $dOldDate As Date + +Public Sub _new() + + Dim hTop As WebHBox + Dim I As Integer + Dim aList As String[] + + Me.Arrangement = Arrange.Vertical + + hTop = New WebHBox(Me) + + btnPrev = New WebButton(hTop) As "btnPrev" + btnPrev.Image = "gw-arrow-left.png" + btnPrev.Border = False + + btnNext = New WebButton(hTop) As "btnNext" + btnNext.Image = "gw-arrow-right.png" + btnNext.Border = False + + cmbMonth = New WebComboBox(hTop) As "cmbMonth" + cmbMonth.ReadOnly = True + cmbMonth.Expand = True + + txtYear = New WebSpinBox(hTop) As "txtYear" + txtYear.MinValue = 1600 + txtYear.MaxValue = 9999 + txtYear.Width = "6em" + txtYear.Border = False + + panCalendar = New WebContainer(Me) As "panCalendar" + + aList = New String[] + For I = 1 To 12 + aList.Add(Format(Date(1972, I, 1), "mmmm")) + Next + cmbMonth.List = aList + + $dDate = Date(Now) + SetDate() + +End + +Private Sub SetDate(Optional iYear As Integer, Optional iMonth As Integer, Optional iDay As Integer) + + Dim dMonth As Date + Dim dDate As Date + + If $bLock Then Return + $bLock = True + + ' If iYear = 0 And If iMonth = 0 And If iDay = 0 Then + ' $dDate = CheckDate($dDate) + ' Endif + + If iYear <= 0 Then iYear = Year($dDate) + If iMonth <= 0 Then iMonth = Month($dDate) + If iDay <= 0 Then iDay = Day($dDate) + + Do + Try dDate = Date(iYear, iMonth, iDay) + If Not Error Then Break + Dec iDay + If iDay < 28 Then + dDate = Null + Break + Endif + Loop + + 'If dDate And If CheckDate(dDate) <> dDate Then dDate = Null + + If dDate Then + $dDate = dDate + dMonth = Date(Year(dDate), Month(dDate), 1) + Else + dMonth = $dMonth + $dMonth = Null + Endif + + If dMonth <> $dMonth Then + + $dMonth = dMonth + + cmbMonth.Index = Month($dMonth) - 1 + txtYear.Value = Year($dMonth) + + Endif + + iDay = WeekDay($dMonth) - System.FirstDayOfWeek + If iDay < 1 Then iDay += 7 + $dStart = $dMonth - iDay + + ' If $dDate <> $dLast Then + ' $dLast = $dDate + ' 'GetParent()._Change + ' Endif + + 'dwgMonth.SetFocus + panCalendar.Refresh + + Me._SetProperty("Value", $dDate) + + $bLock = False + + If $dDate <> $dOldDate Then + $dOldDate = $dDate + Raise Change + Endif + +End + +Public Sub panCalendar_Render() + + Dim Y As Integer + Dim X As Integer + Dim dDate As Date + Dim sClass As String + Dim dNow As Date + + Print "" + + Print "" + For X = 0 To 6 + Print "" + Next + Print "" + + dDate = Date($dStart) + dNow = Date(Now) + + For Y = 1 To 5 + Print "" + For X = 0 To 6 + Print " Month($dDate) Then sClass &= " gw-date-disabled" + sClass = Trim(sClass) + If sClass Then Print " class=\""; sClass; "\""; + If Month(dDate) = Month($dDate) Then + Print Me._GetUpdateJS("onclick", "#date", JS(CStr(dDate))); + Endif + Print ">"; Day(dDate); "" + Inc dDate + Next + Print "" + Next + + Print "
    "; Html(Format(CDate($dStart + X), "ddd")); "
    " + +End + +Public Sub cmbMonth_Click() + + SetDate(0, cmbMonth.Index + 1) + +End + +Public Sub txtYear_Change() + + SetDate(txtYear.Value, 0) + +End + +Public Sub btnPrev_Click() + + If Month($dDate) = 1 Then + SetDate(Year($dDate) - 1, 12) + Else + SetDate(0, Month($dDate) - 1) + Endif + +End + +Public Sub btnNext_Click() + + If Month($dDate) = 12 Then + SetDate(Year($dDate) + 1, 1) + Else + SetDate(0, Month($dDate) + 1) + Endif + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + Select Case sProp + + Case "#date" + $dDate = CDate(vValue) + SetDate() + Raise Click + + Default + Super._UpdateProperty(sProp, vValue) + + End Select + +End + + +Private Function Value_Read() As Date + + Return $dDate + +End + +Private Sub Value_Write(Value As Date) + + SetDate(Year(Value), Month(Value), Day(Value)) + +End diff --git a/comp/src/gb.web.form/.src/Color.class b/comp/src/gb.web.form/.src/Color.class new file mode 100644 index 00000000..8ab3ab84 --- /dev/null +++ b/comp/src/gb.web.form/.src/Color.class @@ -0,0 +1,32 @@ +' Gambas class file + +Export Optional + +Public Const Default As Integer = -1 +Public Const Black As Integer = &H000000& +Public Const White As Integer = &HFFFFFF& +Public Const Gray As Integer = &H808080& +Public Const LightGray As Integer = &HC0C0C0& +Public Const DarkGray As Integer = &H404040& +Public Const Blue As Integer = &HFF& +Public Const Green As Integer = &HFF00& +Public Const Red As Integer = &HFF0000& +Public Const Yellow As Integer = &HFFFF00& +Public Const Magenta As Integer = &HFF00FF& +Public Const Cyan As Integer = &H00FFFF& +Public Const Orange As Integer = &HFF7F00& +Public Const Violet As Integer = &H7F00FF& +Public Const RoyalBlue As Integer = &H007FFF& +Public Const Pink As Integer = &HFF80FF& + +Static Public Sub SetAlpha(Color As Integer, Alpha As Integer) As Integer + + Return Color Or Lsl(Alpha, 24) + +End + +Static Public Sub RGB((Red) As Integer, (Green) As Integer, (Blue) As Integer, Optional Alpha As Integer) As Integer + + Return Blue Or Lsl(Green, 8) Or Lsl(Red, 16) Or Lsl(Alpha, 24) + +End diff --git a/comp/src/gb.web.form/.src/Header.class b/comp/src/gb.web.form/.src/Header.class new file mode 100644 index 00000000..7c6ebdb4 --- /dev/null +++ b/comp/src/gb.web.form/.src/Header.class @@ -0,0 +1,36 @@ +' Gambas class file + +Static Public Form As WebForm + +Private Sub GetTitle() As String + + If Form And If Form.Title Then + Print Html(Form.Title); + Else + Print Html(Application.Title); + Endif + +End + +Private Sub GetJavascript() As String + + Dim aFiles As String[] + + aFiles = Form._GetJavascriptFiles() + If aFiles And If aFiles.Count Then Return ":" & aFiles.Join(":") + +End + +Private Sub PrintJavascriptExternFiles() + + Dim aFiles As String[] + Dim sFile As String + + aFiles = Form._GetJavascriptExternFiles() + If aFiles Then + For Each sFile In aFiles + Print "" + Next + Endif + +End diff --git a/comp/src/gb.web.form/.src/Header.webpage b/comp/src/gb.web.form/.src/Header.webpage new file mode 100644 index 00000000..ea8157f6 --- /dev/null +++ b/comp/src/gb.web.form/.src/Header.webpage @@ -0,0 +1,12 @@ + + + + + + + "> + + + <%PrintJavascriptExternFiles()%> + <%=GetTitle()%> + diff --git a/comp/src/gb.web.form/.src/Main.module b/comp/src/gb.web.form/.src/Main.module new file mode 100644 index 00000000..0ef33860 --- /dev/null +++ b/comp/src/gb.web.form/.src/Main.module @@ -0,0 +1,30 @@ +' Gambas module file + +'Public CurrentForm As WebForm + +Public Sub Main() + + 'Debug Base64(File.Load(".public/waiting.gif")) + 'Debug WebForm._GetColor(Color.Gray) + Debug Base64$(File.Load("shadow.png")) + +End + +Public Sub DumpSession() + + Dim sKey As String + + Debug String$(80, "-") + + If Session.Id Then + + For Each sKey In Session.Keys + Error sKey; ": "; JSON.Encode(Session[sKey]) + Next + + Endif + + Debug String$(80, "-") + +End + diff --git a/comp/src/gb.web.form/.src/Message/FMessage.class b/comp/src/gb.web.form/.src/Message/FMessage.class new file mode 100644 index 00000000..cacaa6ab --- /dev/null +++ b/comp/src/gb.web.form/.src/Message/FMessage.class @@ -0,0 +1,94 @@ +' Gambas class file + +Public Sub SetIcon(sIcon As String) + + imgIcon.Image = "message" &/ sIcon & ".png" + +End + +Public Sub SetText(sText As String) + + txtMessage.Text = sText + +End + +Public Sub SetButton(sButton1 As String, Optional sButton2 As String, sButton3 As String) + + If sButton1 Then + btnButton1.Text = sButton1 + Endif + + If sButton2 Then + btnButton2.Text = sButton2 + btnButton2.Show + Endif + + If sButton3 Then + btnButton3.Text = sButton3 + btnButton3.Show + Endif + +End + +Public Sub _new() + + Dim hCtrl As WebButton + + Me.Style["min-width"] = "24em" + Me.Style["min-height"] = "12em" + + For Each hCtrl In [btnButton1, btnButton2, btnButton3] + hCtrl.Style["min-width"] = "8em" + Next + + imgIcon.Style["margin-right"] = "1em" + +End + +Private Sub HandleClick() + + Dim hForm As WebForm + Dim sName As String + Dim hCtrl As WebControl + Dim sText As String + + sText = Last.Text + sName = Me["#message"] + hCtrl = WebControl.FromName(sName) + + If hCtrl Then + If Object.CanRaise(hCtrl, "Message") Then + Object.Raise(hCtrl, "Message", [hCtrl, sText]) + Else + hForm = WebControl.FromId(Me._Window).Form + Object.Raise(hForm, "Message", [hCtrl, sText]) + Endif + Endif + + Me.Close + +End + +Public Sub btnButton1_Click() + + HandleClick() + +End + +Public Sub btnButton2_Click() + + HandleClick() + +End + +Public Sub btnButton3_Click() + + HandleClick() + +End + +Public Sub WebForm_Open() + + btnButton1.SetFocus + +End diff --git a/comp/src/gb.web.form/.src/Message/FMessage.webform b/comp/src/gb.web.form/.src/Message/FMessage.webform new file mode 100644 index 00000000..bceee02e --- /dev/null +++ b/comp/src/gb.web.form/.src/Message/FMessage.webform @@ -0,0 +1,43 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,77,36) + Arrangement = Arrange.Vertical + Margin = True + Spacing = True + { WebContainer1 WebHBox + #MoveScaled(1,1,75,19) + Expand = True + Spacing = True + { WebContainer2 WebVBox + #MoveScaled(1,1,17,17) + { imgIcon WebImage + #MoveScaled(1,1,15,10) + } + } + { txtMessage WebHtml + #MoveScaled(19,1,33,17) + Expand = True + } + } + { panButton WebHBox + #MoveScaled(1,21,75,7) + Spacing = True + { WebContainer3 WebContainer + #MoveScaled(1,1,5,5) + Expand = True + } + { btnButton3 WebButton + #MoveScaled(7,1,17,5) + Visible = False + } + { btnButton2 WebButton + #MoveScaled(25,1,17,5) + Visible = False + } + { btnButton1 WebButton + #MoveScaled(43,1,17,5) + Text = ("OK") + } + } +} diff --git a/comp/src/gb.web.form/.src/Message/Message.class b/comp/src/gb.web.form/.src/Message/Message.class new file mode 100644 index 00000000..9c27c1ff --- /dev/null +++ b/comp/src/gb.web.form/.src/Message/Message.class @@ -0,0 +1,61 @@ +' Gambas class file + +Export + +Static Public Name As String + +Static Private Sub Open(sIcon As String, sTitle As String, sText As String, Optional sButton1 As String, sButton2 As String, sButton3 As String) + + Dim hMessage As FMessage + Dim sName As String + + hMessage = New FMessage + hMessage.Title = sTitle + hMessage.SetIcon(sIcon) + hMessage.SetText(sText) + hMessage.SetButton(sButton1, sButton2, sButton3) + + If Name Then + sName = Name + Else + Try sName = Last.Name + Endif + + hMessage["#message"] = sName + + hMessage.ShowModal(Null) + +End + + +Static Public Sub _call(Text As String, Optional Button As String) + + Open("info", ("Information"), Text, Button) + +End + +Static Public Sub Info(Text As String, Optional Button As String) + + _call(Text, Button) + +End + +Static Public Sub Warning(Text As String, Optional Button1 As String, Button2 As String, Button3 As String) + + Open("warning", ("Warning"), Text, Button1, Button2, Button3) + +End + +Static Public Sub Error(Text As String, Optional Button As String) + + Open("error", ("Error"), Text, Button) + +End + +Static Public Sub Question(Text As String, Optional Button1 As String, Button2 As String, Button3 As String) + + Open("question", ("Question"), Text, Button1, Button2, Button3) + +End + + diff --git a/comp/src/gb.web.form/.src/Scroll.class b/comp/src/gb.web.form/.src/Scroll.class new file mode 100644 index 00000000..fa298d07 --- /dev/null +++ b/comp/src/gb.web.form/.src/Scroll.class @@ -0,0 +1,8 @@ +' Gambas class file + +Export + +Public Const None As Integer = 0 +Public Const Horizontal As Integer = 1 +Public Const Vertical As Integer = 2 +Public Const Both As Integer = 3 diff --git a/comp/src/gb.web.form/.src/Select.class b/comp/src/gb.web.form/.src/Select.class new file mode 100644 index 00000000..e151860a --- /dev/null +++ b/comp/src/gb.web.form/.src/Select.class @@ -0,0 +1,7 @@ +' Gambas class file + +Export + +Public Const None As Integer = 0 +Public Const Single As Integer = 1 +Public Const Multiple As Integer = 2 diff --git a/comp/src/gb.web.form/.src/Test/FHello.class b/comp/src/gb.web.form/.src/Test/FHello.class new file mode 100644 index 00000000..4e8e8cbe --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/FHello.class @@ -0,0 +1,8 @@ +' Gambas class file + + +Public Sub WebButton1_Click() + + Me.Close + +End diff --git a/comp/src/gb.web.form/.src/Test/FHello.webform b/comp/src/gb.web.form/.src/Test/FHello.webform new file mode 100644 index 00000000..84568cf8 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/FHello.webform @@ -0,0 +1,31 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,82,45) + Expand = True + Arrangement = Arrange.Vertical + Margin = True + Title = ("This is a very important message") + Resizable = True + { WebHtml1 WebHtml + #MoveScaled(1,1,80,11) + Text = ("

    Welcome to the gb.web.form component!

    \n

    This component aims at making web application as easy as making desktop applications.

    \n

    This goal is difficult to achieve, but I hope to succeed!

    ") + } + { WebContainer1 WebContainer + #MoveScaled(1,12,80,20) + Expand = True + } + { WebContainer2 WebContainer + #MoveScaled(1,32,80,8) + Arrangement = Arrange.Horizontal + { WebContainer3 WebContainer + #MoveScaled(1,1,57,6) + Expand = True + } + { WebButton1 WebButton + #MoveScaled(58,1,16,6) + Width = "8em" + Text = ("OK") + } + } +} diff --git a/comp/src/gb.web.form/.src/Test/FTestWebUploader.class b/comp/src/gb.web.form/.src/Test/FTestWebUploader.class new file mode 100644 index 00000000..dffd4a51 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/FTestWebUploader.class @@ -0,0 +1,16 @@ +' Gambas class file + +Export + + +Public Sub WebUploader1_Upload() + + WebImage1.Image = WebUploader1.Path + +End + +Public Sub WebForm_Open() + + WebForm.Debug = True + +End diff --git a/comp/src/gb.web.form/.src/Test/FTestWebUploader.webform b/comp/src/gb.web.form/.src/Test/FTestWebUploader.webform new file mode 100644 index 00000000..8ad84e1c --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/FTestWebUploader.webform @@ -0,0 +1,18 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,64,91) + Arrangement = Arrange.Vertical + Margin = True + Spacing = True + { WebUploader1 WebUploader + #MoveScaled(1,1,62,8) + } + { WebContainer1 WebContainer + #MoveScaled(1,10,62,35) + { WebImage1 WebImage + #MoveScaled(0,0,25,24) + Border = True + } + } +} diff --git a/comp/src/gb.web.form/.src/Test/Webform1.class b/comp/src/gb.web.form/.src/Test/Webform1.class new file mode 100644 index 00000000..170123b2 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform1.class @@ -0,0 +1,83 @@ +' Gambas class file + +Public Sub _new() + + WebContainer2.Style["background-color"] = "#BFDFFF" + WebHTML2.Style["font-size"] = "200%" + WebHTML2.Style["font-weight"] = "bold" + +End + +Public Sub WebButton3_Click() + + Last.Text = "Gambas rules!" + +End + +Public Sub WebForm_Open() + + WebForm.Print("Form is opened") + +End + +Public Sub WebButton2_Click() + + WebButton2.Parent.Border = Not WebButton2.Parent.Border + +End + + +Public Sub WebButton1_Click() + + Randomize + WebHTML2.Style["font-size"] = "100%" + WebHTML2.Html &= "Hello world!" + +End + +Public Sub WebForm_Event() + + WebContainer2.Refresh + +End + +Public Sub WebButton4_Click() + + WebButton4.Text = CStr(CInt(WebButton4.Text) + 1) + +End + +Public Sub WebButton5_Click() + + WebHTML2.Style["font-size"] = "200%" + WebHTML2.Html = "Header" + +End + +Public Sub WebButton6_Click() + + WebButton4.Enabled = Not WebButton4.Enabled + +End + +Public Sub btnClose_Click() + + Me.Close + +End + +Public Sub WebTextBox1_Change() + + Dim sText As String + + sText = Last.Text + + If sText Then + WebHtml1.Text = Html("You have entered: « " & sText & " ».") + WebHtml1.Visible = True + Else + WebHtml1.Text = Html("You have entered nothing.") + WebHtml1.Visible = False + Endif + +End diff --git a/comp/src/gb.web.form/.src/Test/Webform1.webform b/comp/src/gb.web.form/.src/Test/Webform1.webform new file mode 100644 index 00000000..681a765b --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform1.webform @@ -0,0 +1,135 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,98,109) + Height = "100%" + Arrangement = Arrange.Vertical + Margin = True + Spacing = True + Title = ("gb.web.form test") + { WebContainer4 WebContainer + #MoveScaled(1,1,96,6) + Arrangement = Arrange.Horizontal + Margin = True + Border = True + { WebHTML2 WebHTML + #MoveScaled(1,1,27,4) + Expand = True + Text = ("Header") + } + { btnClose WebButton + #MoveScaled(28,1,13,4) + Border = False + Image = "gw-close.png" + } + } + { WebContainer3 WebContainer + #MoveScaled(1,8,96,48) + Expand = True + Arrangement = Arrange.Horizontal + Spacing = True + { WebContainer1 WebContainer + #MoveScaled(1,1,23,46) + Arrangement = Arrange.Vertical + Margin = True + Spacing = True + Border = True + { WebButton1 WebButton + #MoveScaled(1,1,21,5) + Text = ("Hello world!") + } + { WebButton5 WebButton + #MoveScaled(1,7,21,5) + Text = ("Reset text") + } + { WebButton3 WebButton + #MoveScaled(1,13,21,5) + Text = ("Change text") + } + { WebButton4 WebButton + #MoveScaled(1,19,21,5) + Text = ("0") + } + { WebButton2 WebButton + #MoveScaled(1,25,21,5) + Text = ("Border") + } + { WebButton6 WebButton + #MoveScaled(1,31,21,5) + Text = ("Toggle enabled") + } + } + { WebContainer5 WebContainer + #MoveScaled(25,1,65,46) + Expand = True + Arrangement = Arrange.Vertical + Margin = True + Spacing = True + Border = True + { WebTextBox1 WebTextBox + #MoveScaled(1,1,63,5) + Height = "2em" + } + { WebHtml1 WebHtml + #MoveScaled(1,7,63,5) + Visible = False + } + { WebContainer2 WebContainer + #MoveScaled(1,13,63,13) + Expand = True + Margin = True + Border = True + } + { WebHtml3 WebHtml + #MoveScaled(1,27,63,8) + Text = ("Absolute !") + } + } + } + { WebContainer6 WebContainer + #MoveScaled(1,57,96,24) + Arrangement = Arrange.Row + Spacing = True + Border = True + { WebButton7 WebButton + #MoveScaled(1,1,10,10) + Width = "8em" + Text = ("B1") + } + { WebButton11 WebButton + #MoveScaled(12,1,10,10) + Width = "8em" + Text = ("B5") + } + { WebButton13 WebButton + #MoveScaled(23,1,10,10) + Width = "8em" + Text = ("B7") + } + { WebButton12 WebButton + #MoveScaled(34,1,10,10) + Width = "8em" + Text = ("B6") + } + { WebButton8 WebButton + #MoveScaled(45,1,10,10) + Width = "8em" + Text = ("B2") + } + { WebButton9 WebButton + #MoveScaled(56,1,10,10) + Width = "8em" + Text = ("B3") + } + { WebButton10 WebButton + #MoveScaled(67,1,10,10) + Width = "8em" + Text = ("B4") + } + { WebButton14 WebButton + #MoveScaled(78,1,10,10) + Width = "8em" + Text = ("B8") + } + } +} diff --git a/comp/src/gb.web.form/.src/Test/Webform2.class b/comp/src/gb.web.form/.src/Test/Webform2.class new file mode 100644 index 00000000..b347bdae --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform2.class @@ -0,0 +1,146 @@ +' Gambas class file + +Public Sub _new() + + WebLabel1.Style["font-size"] = "140%" + WebLabel1.Style["padding-right"] = "1em" + +End + + +Public Sub WebButton1_Click() + + WebTabPanel1.Index = 1 + +End + +Public Sub WebContainer2_Render() + + Dim sKey As String + Dim bTable As Boolean + Dim bDark As Boolean + + If Session.Id Then + + For Each sKey In Session.Keys + If Not bTable Then + Print "" + bTable = True + Endif + If bDark Then + Print "" + Else + Print "" + Endif + Print ""; + Print "" + Print "" + bDark = Not bDark + Next + + If bTable Then Print "
    "; Html(sKey); ":"; Html(JSON.Encode(Session[sKey])); "
    " + + Endif + +End + + +Public Sub WebForm_Event() + + WebContainer2.Refresh + +End + +Public Sub WebTabPanel1_Click() + + 'If WebTabPanel1.Index = 0 Then WebTextBox1.SetFocus + +End + +Public Sub WebButton4_Click() + + WebComboBox1.Add("Item #" & CStr(WebComboBox1.Count)) + +End + +Public Sub WebButton5_Click() + + WebComboBox1.Clear + +End + +Public Sub WebTimer1_Timer() + + WebLabel1.Text = Format(Now, gb.LongTime) + +End + +Public Sub WebButton2_Click() + + Dim hForm As WebForm + + Randomize + + hForm = New FHello + hForm.Show() + hForm.Move(Rand(16, 24), Rand(4, 16), 32, 32) + +Catch + + Me.Print(Error.Text & ": " & Error.Backtrace.Join(" ")) + +End + +Public Sub WebButton6_Click() + + WebTimer1.Enabled = Not WebTimer1.Enabled + +End + +Public Sub WebSlider1_Change() + + WebSpinBox1.Value = WebSlider1.Value + +End + +Public Sub WebSpinBox1_Change() + + WebSlider1.Value = WebSpinBox1.Value + +End + +Public Sub WebButton3_Click() + + Message("Hello world!") + +End + +Public Sub WebButton7_Click() + + Message.Warning("File has been modified.

    All changes will be lost!", "Cancel", "Delete") + +End + +Public Sub WebMenu3_Click(Target As WebControl) + + Message("You have clicked on '" & Target.Name & "' control.") + +End + +Public Sub WebForm_Message(Source As WebControl, Action As String) + + 'Debug "Action '"; Action; "' on '"; Source.Name; "'" + +End + +Public Sub WebMenu4_Click(Target As WebControl) + + Message("You have clicked on '" & Target.Name & "' control.") + +End + +Public Sub WebMenuItem6_Click() + + Message.Error("Quit !") + +End diff --git a/comp/src/gb.web.form/.src/Test/Webform2.webform b/comp/src/gb.web.form/.src/Test/Webform2.webform new file mode 100644 index 00000000..96afb271 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform2.webform @@ -0,0 +1,274 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,138,122) + Height = "100%" + Arrangement = Arrange.Vertical + Title = ("This is webform2") + { WebMenuBar1 WebMenuBar + #MoveScaled(1,1,136,48) + Spacing = True + { WebMenu3 WebMenu + #MoveScaled(1,1,32,46) + Text = ("File") + { WebMenuItem7 WebMenuItem + #MoveScaled(1,1,30,4) + Text = ("New") & "..." + Image = "new.png" + Shortcut = "CTRL+N" + } + { WebMenuItem8 WebMenuItem + #MoveScaled(1,5,30,4) + Text = ("Open") & "..." + Image = "open.png" + Shortcut = "CTRL+O" + } + { WebMenu4 WebMenu + #MoveScaled(1,9,30,26) + Text = ("Project") + { WebMenuItem9 WebMenuItem + #MoveScaled(1,1,28,4) + Text = ("Compile") + Image = "new.png" + Shortcut = "F7" + } + { WebMenuItem10 WebMenuItem + #MoveScaled(1,5,28,4) + Text = ("Compile all") + Image = "new.png" + } + { WebSeparator4 WebSeparator + #MoveScaled(1,9,28,2) + } + { WebMenuItem11 WebMenuItem + #MoveScaled(1,11,28,4) + Text = ("Translate") + Shortcut = "CTRL+T" + } + { WebSeparator1 WebSeparator + #MoveScaled(1,15,28,2) + } + { WebMenuItem1 WebMenuItem + #MoveScaled(1,17,28,4) + Text = ("Teleport") + } + } + { WebSeparator5 WebSeparator + #MoveScaled(1,35,30,2) + } + { WebMenuItem12 WebMenuItem + #MoveScaled(1,37,30,4) + Text = ("Quit") + } + } + { WebMenu1 WebMenu + #MoveScaled(34,1,30,46) + Text = ("Project") + { WebMenuItem2 WebMenuItem + #MoveScaled(1,1,28,4) + Text = ("Compile") + Image = "new.png" + Shortcut = "F7" + } + { WebMenuItem3 WebMenuItem + #MoveScaled(1,5,28,4) + Text = ("Compile all") + Image = "new.png" + } + { WebSeparator2 WebSeparator + #MoveScaled(1,9,28,2) + } + { WebMenuItem4 WebMenuItem + #MoveScaled(1,11,28,4) + Text = ("Translate") + Shortcut = "CTRL+T" + } + { WebSeparator6 WebSeparator + #MoveScaled(1,15,28,2) + } + { WebMenuItem5 WebMenuItem + #MoveScaled(1,17,28,5) + Text = ("Teleport") + } + } + } + { WebContainer1 WebHBox + #MoveScaled(1,49,136,6) + { WebButton1 WebButton + #MoveScaled(1,1,13,4) + Text = ("A button") + } + { WebButton2 WebButton + #MoveScaled(14,1,13,4) + Border = False + Text = ("Add window") + } + { WebButton7 WebButton + #MoveScaled(27,1,13,4) + Border = False + Text = ("Warning") + } + { WebButton6 WebButton + #MoveScaled(40,1,13,4) + Border = False + Text = ("Toggle timer") + } + { WebButton8 WebButton + #MoveScaled(53,1,13,4) + Visible = False + Text = ("Hidden") + } + { WebContainer6 WebContainer + #MoveScaled(66,1,42,4) + Expand = True + } + { WebLabel1 WebLabel + #MoveScaled(108,1,15,4) + } + } + { WebContainer9 WebContainer + #MoveScaled(1,55,136,88) + Expand = True + Arrangement = Arrange.Vertical + Margin = True + { WebTabPanel1 WebTabPanel + #MoveScaled(1,1,134,82) + Expand = True + Arrangement = Arrange.Vertical + Margin = True + Spacing = True + Count = 3 + Index = 0 + Text = ("General") + { WebTextBox1 WebTextBox + #MoveScaled(1,1,131.7143,4) + Text = ("Hello !") + } + { WebCheckBox1 WebCheckBox + #MoveScaled(1,6,131.7143,4) + Text = ("Check and uncheck me!") + } + { WebContainer5 WebHBox + #MoveScaled(1,11,131.7143,6) + Spacing = True + { WebComboBox2 WebComboBox + #MoveScaled(1,1,22,4) + List = [("Élément 1"), ("Élément 2"), ("Élément 3"), ("Élément 4")] + } + { WebTextBox2 WebTextBox + #MoveScaled(24,1,15,4) + } + { WebDateBox2 WebDateBox + #MoveScaled(40,1,24,4) + } + { WebSpinBox1 WebSpinBox + #MoveScaled(65,1,13,4) + } + { WebSlider1 WebSlider + #MoveScaled(79,1,49,4) + Expand = True + Border = False + } + } + { WebContainer3 WebHBox + #MoveScaled(1,18,131.7143,6) + Spacing = True + { WebDateBox1 WebDateBox + #MoveScaled(1,1,24,4) + } + { WebComboBox1 WebComboBox + #MoveScaled(26,1,65.7143,4) + Width = "30em" + List = [("Élément 1"), ("Élément 2"), ("Élément 3"), ("Élément 4")] + ReadOnly = False + } + { WebButton4 WebButton + #MoveScaled(92.7143,1,19,4) + Text = ("New element") + } + { WebButton5 WebButton + #MoveScaled(112.7143,1,19,4) + Text = ("Clear") + } + { WebSeparator3 WebSeparator + #MoveScaled(132.7143,1,11,4) + Ignore = True + } + } + { WebSeparator7 WebSeparator + #MoveScaled(1,25,131.7143,2) + Height = "1em" + } + { WebContainer7 WebExpander + #MoveScaled(1,28,131.7143,32) + Background = &HA0FF7F00& + Arrangement = Arrange.Horizontal + Spacing = True + Indent = True + Text = ("This is an expander container") + Hidden = True + { WebContainer4 WebVBox + #MoveScaled(1.1429,1.1429,64,25.7143) + Expand = True + Background = &H80FFFF00& + Margin = True + { WebRadioButton1 WebRadioButton + #MoveScaled(1,1,62,4) + Text = ("Alpha") + } + { WebRadioButton2 WebRadioButton + #MoveScaled(1,5,62,4) + Text = ("Beta") + Value = True + } + { WebRadioButton3 WebRadioButton + #MoveScaled(1,9,62,4) + Text = ("Gamma") + } + { WebCheckBox2 WebCheckBox + #MoveScaled(1,13,62,4) + Text = ("Gambas Almost Means Basic!") + } + } + { WebContainer8 WebVBox + #MoveScaled(66.1429,1.1429,58.4286,25.7143) + Expand = True + Spacing = True + { WebLabel2 WebLabel + #MoveScaled(1,1,56.4286,4) + Text = ("Please enter some text:") + } + { WebTextArea1 WebTextArea + #MoveScaled(1,6,56.4286,24) + Height = "20em" + Expand = True + Wrap = True + } + } + } + { WebHBox2 WebHBox + #MoveScaled(1,61,131.7143,11) + { WebImage1 WebImage + #MoveScaled(1,1,9,9) + Width = "8em" + Image = "message/close.svg" + } + } + Index = 1 + Text = ("Options") + { WebButton3 WebButton + #MoveScaled(1,1,131.7143,8) + Text = ("Test") + } + Index = 2 + Text = ("Application state") + { WebContainer2 WebContainer + #MoveScaled(1,1,131.7143,32) + } + Index = 0 + } + } + { WebTimer1 WebTimer + #MoveScaled(70,8) + } +} diff --git a/comp/src/gb.web.form/.src/Test/Webform3.class b/comp/src/gb.web.form/.src/Test/Webform3.class new file mode 100644 index 00000000..46ee490b --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform3.class @@ -0,0 +1,75 @@ +' Gambas class file + + +Public Sub _new() + + Dim I As Integer + + WebForm.Debug = True + + WebTable1.Columns.Count = 12 + For I = 0 To WebTable1.Columns.Count - 1 + WebTable1.Columns[I].Text = Chr$(65 + I) + WebTable1.Columns[I].Wrap = False + WebTable1.Columns[I].Sortable = I Mod 3 + Next + 'WebTable1.Columns[3].Expand = True + + WebTable1.Count = 1000 + 'WebTable1.Select(2, 3) + +End + +Public Sub WebForm_Open() + +End + + +Public Sub WebTable1_Data(Row As Integer, Column As Integer, Data As WebTableData) + + Data.Text = "This is the " & CStr(Row) & ":" & CStr(Column) & " cell" + 'Data.Text = 1 / 0 + +End + +Public Sub WebContainer1_Render() + + Dim sKey As String + Dim bTable As Boolean + Dim bDark As Boolean + + If Session.Id Then + + For Each sKey In Session.Keys + If Not bTable Then + Print "" + bTable = True + Endif + If bDark Then + Print "" + Else + Print "" + Endif + Print ""; + Print "" + Print "" + bDark = Not bDark + Next + + If bTable Then Print "
    "; Html(sKey); ":"; Html(JSON.Encode(Session[sKey])); "
    " + + Endif + +End + +Public Sub WebButton1_Click() + + Message.Warning("Ceci est une table avec " & WebTable1.Count & " éléments !") + +End + +Public Sub WebTable1_Select() + + WebForm.Print("Select: " & WebTable1.Selection.Count) + +End diff --git a/comp/src/gb.web.form/.src/Test/Webform3.webform b/comp/src/gb.web.form/.src/Test/Webform3.webform new file mode 100644 index 00000000..738fed7a --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform3.webform @@ -0,0 +1,41 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,106,85) + Height = "100%" + Arrangement = Arrange.Vertical + Margin = True + Spacing = True + { WebTabPanel1 WebTabPanel + #MoveScaled(1,1,104,69) + Height = "32em" + Expand = True + Arrangement = Arrange.Vertical + Margin = True + Spacing = True + Count = 2 + Index = 0 + Text = ("Tab 1") + { WebButton1 WebButton + #MoveScaled(1,1,101.7143,4) + Text = ("Message") + } + { WebTable1 WebTable + #MoveScaled(1,6,101.7143,32) + Expand = True + Mode = Select.Multiple + Sortable = True + } + Index = 1 + Text = ("Tab 2") + { WebContainer1 WebContainer + #MoveScaled(1,1,101.7143,32) + } + Index = 0 + } + { WebHBox1 WebHBox + #MoveScaled(1,71,104,9) + Height = "6em" + Border = True + } +} diff --git a/comp/src/gb.web.form/.src/Test/Webform4.class b/comp/src/gb.web.form/.src/Test/Webform4.class new file mode 100644 index 00000000..02645880 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform4.class @@ -0,0 +1,41 @@ +' Gambas class file + +Public Sub _new() + + WebForm.Debug = True + +End + + +Public Sub WebUploadArea1_Progress() + + btnSelectFile.Enabled = False + WebProgressBar1.Value = WebUploadArea1.Progress + +End + +Public Sub WebUploadArea1_Upload() + + Dim sPath As String + + btnSelectFile.Enabled = True + + WebForm.Print("upload") + For Each sPath In Request.Files + Message(Request.Files.Key & ": " & sPath) + Next + +End + + +Public Sub WebButton1_Click() + + Webform5.Show + +End + +Public Sub WebButton2_Click() + + Webform5.Hide + +End diff --git a/comp/src/gb.web.form/.src/Test/Webform4.webform b/comp/src/gb.web.form/.src/Test/Webform4.webform new file mode 100644 index 00000000..93e1d329 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform4.webform @@ -0,0 +1,45 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,107,26) + Arrangement = Arrange.Horizontal + Margin = True + Spacing = True + { WebContainer3 WebVBox + #MoveScaled(1,1,60,24) + Spacing = True + { WebContainer2 WebHBox + #MoveScaled(1,1,58,8) + { WebUploadArea1 WebUploadArea + #MoveScaled(1,1,21,6) + { btnSelectFile WebButton + #MoveScaled(1,1,16,4) + Text = ("Select file") & "..." + } + } + } + { WebProgressBar1 WebProgressBar + #MoveScaled(1,10,58,4) + } + } + { WebContainer1 WebContainer + #MoveScaled(62,1,27,24) + Width = "10em" + Arrangement = Arrange.Vertical + { WebDateBox1 WebDateBox + #MoveScaled(1,1,25,4) + } + { WebButton1 WebButton + #MoveScaled(1,5,25,4) + Text = ("Show") + } + { WebButton2 WebButton + #MoveScaled(1,9,25,4) + Text = ("Hide") + } + } + { WebTimer1 WebTimer + #MoveScaled(93,7) + Delay = 500 + } +} diff --git a/comp/src/gb.web.form/.src/Test/Webform5.class b/comp/src/gb.web.form/.src/Test/Webform5.class new file mode 100644 index 00000000..3dab0df7 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform5.class @@ -0,0 +1,16 @@ +' Gambas class file + + +Public Sub WebForm_Open() + + Dim M As Integer + Dim aMonth As String[] + + aMonth = New String[] + + For M = 1 To 12 + aMonth.Add(Format(Date(1972, M, 1), "mmmm")) + Next + cmbMonth.List = aMonth + +End diff --git a/comp/src/gb.web.form/.src/Test/Webform5.webform b/comp/src/gb.web.form/.src/Test/Webform5.webform new file mode 100644 index 00000000..b634e4c6 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform5.webform @@ -0,0 +1,25 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,64,29) + Arrangement = Arrange.Vertical + Spacing = True + Persistent = True + { WebHBox1 WebHBox + #MoveScaled(1,1,62,7) + { WebButton1 WebButton + #MoveScaled(1,1,11,5) + Image = "gw-arrow-left.png" + } + { cmbMonth WebComboBox + #MoveScaled(12,1,24,5) + } + { WebButton2 WebButton + #MoveScaled(36,1,16,5) + Image = "gw-arrow-right.png" + } + } + { WebContainer1 WebContainer + #MoveScaled(1,9,62,18) + } +} diff --git a/comp/src/gb.web.form/.src/Test/Webform6.class b/comp/src/gb.web.form/.src/Test/Webform6.class new file mode 100644 index 00000000..76b3cc4a --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform6.class @@ -0,0 +1,14 @@ +' Gambas class file + + +Public Sub WebForm_Open() + + WebForm.Debug = True + +End + +Public Sub WebComboBox1_Click() + + Message(WebComboBox1.Text) + +End diff --git a/comp/src/gb.web.form/.src/Test/Webform6.webform b/comp/src/gb.web.form/.src/Test/Webform6.webform new file mode 100644 index 00000000..055534f7 --- /dev/null +++ b/comp/src/gb.web.form/.src/Test/Webform6.webform @@ -0,0 +1,25 @@ +# Gambas Form File 3.0 + +{ WebForm WebForm + #MoveScaled(0,0,74,91) + Arrangement = Arrange.Vertical + Margin = True + Spacing = True + { WebComboBox1 WebComboBox + #MoveScaled(1,1,72,4) + List = [(""), ("Élément 1"), ("Élément 2"), ("Élément 3")] + } + { WebTextBox1 WebTextBox + #MoveScaled(1,6,72,4) + } + { WebTextArea1 WebTextArea + #MoveScaled(1,11,72,16) + } + { WebHBox1 WebHBox + #MoveScaled(1,28,72,5) + { WebButton1 WebButton + #MoveScaled(1,1,16,3) + Text = ("Copy") + } + } +} diff --git a/comp/src/gb.web.form/.src/WebButton.class b/comp/src/gb.web.form/.src/WebButton.class new file mode 100644 index 00000000..55ee05d4 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebButton.class @@ -0,0 +1,117 @@ +' Gambas class file + +Export +Inherits WebControl + +Public Const _Properties As String = "*,Border=True,Text,Image{WebImage},Immediate" +'Public Const _DrawWith As String '= "Button" +Public Const _DefaultEvent As String = "Click" +Public Const _DefaultSize As String = "16,4" + +Event Click + +Property Text As String +Property Image As String +Property Immediate As Boolean + +Private $sText As String +Private $sImage As String +Private $bImmediate As Boolean + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + If $sText = Value Then Return + $sText = Value + Me._SetProperty("Text", Value) + +End + +Private Function Image_Read() As String + + Return $sImage + +End + +Private Sub Image_Write(Value As String) + + If $sImage = Value Then Return + $sImage = Value + Me._SetProperty("Image", Value) + +End + +Public Sub _BeforeRender() + + Dim vSave As Variant + + Print ""; + +End + + +Public Sub _Render() + + If $sImage Then + Print "

    "; + Endif + If $sText Then Print ""; Html($sText); ""; + +End + +Public Sub _AfterRender() + + Raise Render + Print "" + +End + +Public Sub Click() + + Object.Raise(Me, "Click") + +End + +Private Function Immediate_Read() As Boolean + + Return $bImmediate + +End + +Private Sub Immediate_Write(Value As Boolean) + + If $bImmediate = Value Then Return + $bImmediate = Value + Me._SetProperty("Immediate", Value) + +End diff --git a/comp/src/gb.web.form/.src/WebCheckBox.class b/comp/src/gb.web.form/.src/WebCheckBox.class new file mode 100644 index 00000000..e008a617 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebCheckBox.class @@ -0,0 +1,67 @@ +' Gambas class file + +Export +Inherits WebControl + +Public Const _Properties As String = "*,Text,Value" +Public Const _DrawWith As String = "CheckBox" +Public Const _DefaultSize As String = "24,4" +Public Const _Similar As String = "WebButton" +Public Const _DefaultEvent As String = "Click" + +Event Click + +Property Value, Checked As Boolean +Property Text As String + +Private $sText As String +Private $bChecked As Boolean + + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + If $sText = Value Then Return + $sText = Value + Me._SetProperty("Text", Value) + +End + +Public Sub _Render() + + If $sText Then Print ""; + Print + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + If sProp = "value" Then Try Value_Write(vValue) + +End + + +Private Function Value_Read() As Boolean + + Return $bChecked + +End + +Private Sub Value_Write(Value As Boolean) + + If $bChecked = Value Then Return + $bChecked = Value + Me._SetProperty("Value", Value) + Raise Click + +End diff --git a/comp/src/gb.web.form/.src/WebComboBox.class b/comp/src/gb.web.form/.src/WebComboBox.class new file mode 100644 index 00000000..9bcdf556 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebComboBox.class @@ -0,0 +1,248 @@ +' Gambas class file + +Export +Inherits WebControl + +Public Const _Properties As String = "*,Border=True,List,ReadOnly=True,PlaceHolder,Text" +Public Const _DrawWith As String = "ComboBox" +Public Const _DefaultSize As String = "24,4" +Public Const _Similar As String = "WebTextBox" +Public Const _DefaultEvent As String = "Click" + +Event Click +Event Change +Event Activate + +Property Text As String +Property Read Count As Integer +Property Index As Integer +Property List As String[] +Property ReadOnly As Boolean +Property PlaceHolder As String + +Private $iIndex As Integer +Private $sText As String +Private $aList As New String[] +Private $bReadOnly As Boolean = True +Private $sPlaceHolder As String + +Public Sub _new() + + Me._Proxy = ":entry" + +End + + +Public Sub _BeforeRender() + + Dim sClass As String + + If $bReadOnly Then + sClass = "gw-selectbox" + Else + sClass = "gw-combobox" + Endif + + Print "" + +End + +Public Sub _Render() + + Dim I As Integer + + If Not $bReadOnly Then + Print ""; + Print "
    "; + Endif + + Print ""; + + If Not $bReadOnly Then + Print "
    "; + WebForm._AddJavascript("gw.resizeComboBox(" & JS(Me.Name) & ")") + Endif + Print + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + If sProp = "index" Then + Try Me.Index = vValue + Else If sProp = "text" Then + Try Me.Text = vValue + Endif + +End + + +Private Function Index_Read() As Integer + + Return $iIndex + +End + +Private Sub Index_Write(Value As Integer) + + If Value < -1 Or If Value >= $aList.Count Then Error.Raise("Out of bounds") + If $iIndex = Value Then Return + + $iIndex = Value + If Not $bReadOnly Then + If $iIndex < 0 Then + $sText = "" + Else + $sText = $aList[$iIndex] + Endif + Me._SetProperty("Text", $sText) + Endif + Me._SetProperty("Index", $iIndex) + Raise Click + +End + +Private Function Count_Read() As Integer + + Return $aList.Count + +End + +Private Function List_Read() As String[] + + Return $aList.Copy() + +End + +Private Sub CheckIndex() + + If $iIndex < 0 Or If $iIndex >= $aList.Count Then + If $aList.Count = 0 Then + Index_Write(-1) + Else + Index_Write(0) + Endif + Endif + +End + +Private Sub List_Write(Value As String[]) + + If Value Then + $aList = Value.Copy() + Else + $aList = New String[] + Endif + CheckIndex + Me._SetProperty("List", $aList) + +End + +Public Sub Clear() + + $aList.Clear + Index_Write(-1) + Me._SetProperty("List", $aList) + If Not $bReadOnly Then Text_Write("") + +End + +Private Function ReadOnly_Read() As Boolean + + Return $bReadOnly + +End + +Private Sub ReadOnly_Write(Value As Boolean) + + $bReadOnly = Value + If $bReadOnly Then + Me._Proxy = "" + Else + Me._Proxy = ":entry" + Endif + Me._SetProperty("ReadOnly", Value) + +End + +Public Sub Add(Item As String, Optional Index As Integer = -1) + + $aList.Add(Item, Index) + CheckIndex + Me._SetProperty("List", $aList) + +End + +Public Sub Remove(Index As Integer) + + $aList.Remove(Index) + CheckIndex + Me._SetProperty("List", $aList) + +End + + +Private Function Text_Read() As String + + If $bReadOnly Then + Try Return $aList[$iIndex] + Return -1 + Else + Return $sText + Endif + +End + +Private Sub Text_Write(Value As String) + + If $bReadOnly Then + + Index_Write($aList.Find(Value)) + + Else + + If $sText = Value Then Return + $sText = Value + + Inc Me._NoRefresh + Me._SetProperty("Text", Value) + Dec Me._NoRefresh + + If Me._CanRefresh() Then WebForm._AddJavascript("$(" & JS(Me.Name & ":entry") & ").value = " & JS($sText)) + + Raise Change + + Endif + +End + +Private Function PlaceHolder_Read() As String + + Return $sPlaceHolder + +End + +Private Sub PlaceHolder_Write(Value As String) + + $sPlaceHolder = Value + Me._SetProperty("PlaceHolder", Value) + +End diff --git a/comp/src/gb.web.form/.src/WebContainer.class b/comp/src/gb.web.form/.src/WebContainer.class new file mode 100644 index 00000000..0d68c02f --- /dev/null +++ b/comp/src/gb.web.form/.src/WebContainer.class @@ -0,0 +1,415 @@ +' Gambas class file + +Export + +Inherits WebControl + +Public Const _IsContainer As Boolean = True +Public Const _Group As String = "Container" +Public Const _Properties As String = "*,Arrangement{Arrange.*},Margin,Spacing,Indent,Border" +Public Const _DefaultArrangement As String = "F" +Public Const _DefaultSize As String = "32,32" + +Property Arrangement As Integer +Property Margin As Boolean +Property Spacing As Boolean +Property Border As Boolean +Property Indent As Boolean + +Property Read Children As WebControl[] + +Public _Container As WebContainer + +Private $aChildren As New String[] +Private $aExtraChildren As New String[][] +Private $iArrangement As Integer +Private $bMargin As Boolean +Private $bSpacing As Boolean +Private $bBorder As Boolean +Private $bIndent As Boolean + +Public Sub _Add(hChild As WebControl) + + Dim aExtraChild As String[] + Dim hParent As WebControl + + $aChildren.Add(hChild.Name) + + Inc Me._NoRefresh + + If WebForm._InExec And If Not Object.IsLocked(Me) Then + 'If Not Object.IsLocked(Me) Then + + 'Debug "Add extra "; hChild.Name; " to "; Me.Name + hChild._Extra = True + aExtraChild = [hChild.Name, Object.Type(hChild)] + + If hChild._EventName Then + aExtraChild.Add(hChild._EventName) + Try hParent = Object.Parent(hChild) + If Not Error Then aExtraChild.Add(hParent.Name) + Endif + + $aExtraChildren.Add(aExtraChild) + + Me._SetProperty("#extra", $aExtraChildren) + + Endif + + Me._SetProperty("#children", $aChildren) + + Dec Me._NoRefresh + + If Me._CanRefresh() Then + WebForm._AddJavascriptBefore("gw.insertElement(" & JS(hChild.Name) & "," & JS(Me.Name) & ")") + hChild.Refresh + Endif + +End + +Public Sub _Remove(hChild As WebControl) + + Dim iPos As Integer + + $aChildren.Remove($aChildren.Find(hChild.Name)) + + Inc Me._NoRefresh + + If hChild._Extra Then + + For iPos = 0 To $aExtraChildren.Max + If $aExtraChildren[iPos][0] = hChild.Name Then + 'Debug "Remove extra "; hChild.Name; " from "; Me.Name + $aExtraChildren.Remove(iPos) + Me._SetProperty("#extra", $aExtraChildren) + Break + Endif + Next + + Endif + + Me._SetProperty("#children", $aChildren) + + Dec Me._NoRefresh + + If Me._CanRefresh() Then + WebForm._AddJavascript("gw.removeElement(" & JS(hChild.Name) & ")") + Endif + +End + +Public Sub _UpdateChildName(sOld As String, sNew As String) + + $aChildren[$aChildren.Find(sOld)] = sNew + +End + + +Public Sub _IsFirstVisibleChild(hChild As WebControl) As Boolean + + Dim I As Integer + Dim hCtrl As WebControl + + For I = 0 To $aChildren.Max + If $aChildren[I] = hChild.Name Then Return True + hCtrl = WebControl.FromName($aChildren[I]) + If hCtrl.Visible And If Not hCtrl.Ignore Then Return + Next + +End + +Public Sub _InitProperties() + + Dim I As Integer + + Super._InitProperties() + + For I = 0 To $aChildren.Max + WebControl.FromName($aChildren[I])._InitProperties() + Next + +End + +Public Sub _Render() + + Dim I As Integer + + For I = 0 To $aChildren.Max + With WebControl.FromName($aChildren[I]) + 'If Not .Visible Then Continue + ._BeforeRender() + ._Render() + ._AfterRender() + End With + Next + +End + +Public Sub _RenderStyleSheet() + + 'Dim I As Integer + + Me._StartStyleSheet + + Super._RenderStyleSheet() + + If Not _Container And If Me.Visible Then + + If $iArrangement Then + + Me._AddStyleSheet("display:flex;") + Select Case $iArrangement + Case Arrange.Horizontal + Me._AddStyleSheet("flex-flow:row;") + Me._AddStyleSheet("overflow-x:hidden;") + Case Arrange.Vertical + Me._AddStyleSheet("flex-flow:column;") + Case Arrange.Column + Me._AddStyleSheet("flex-flow:column wrap;") + 'If $bSpacing And If $aChildren.Count Then Me._AddStyleSheet("margin-right:-0.5em;margin-bottom:-0.5em;") + Case Arrange.Row + Me._AddStyleSheet("flex-flow:row wrap;") + 'If $bSpacing And If $aChildren.Count Then Me._AddStyleSheet("margin-right:-0.5em;margin-bottom:-0.5em;") + End Select + + Endif + + If $bMargin Then Me._AddStyleSheet("padding:0.5rem;") + + If $bIndent Then + If $bMargin Then + Me._AddStyleSheet("padding-left:1rem;") + Else + Me._AddStyleSheet("padding-left:0.5rem;") + Endif + Endif + + If $bSpacing Then + If $iArrangement = Arrange.Column Or If $iArrangement = Arrange.Row Then + Me._AddStyleSheet("padding-bottom: 0;") + Me._AddStyleSheet("padding-right: 0;") + Endif + Endif + + Endif + + If $bBorder Then Me._AddStyleSheet("border:solid #C0C0C0 1px;") + + Me._EndStyleSheet + + ' For I = 0 To $aChildren.Max + ' With WebControl.FromId($aChildren[I]) + ' ._RenderStyleSheet() + ' End With + ' Next + +End + +Private Function Arrangement_Read() As Integer + + Return $iArrangement + +End + +Private Sub Arrangement_Write(Value As Integer) + + $iArrangement = Value + Me._SetProperty("Arrangement", Value) + UpdateContainer() + +End + +Private Function Margin_Read() As Boolean + + Return $bMargin + +End + +Private Sub Margin_Write(Value As Boolean) + + $bMargin = Value + Me._SetProperty("Margin", Value) + UpdateContainer() + +End + +Private Function Spacing_Read() As Boolean + + Return $bSpacing + +End + +Private Sub Spacing_Write(Value As Boolean) + + $bSpacing = Value + Me._SetProperty("Spacing", Value) + UpdateContainer() + +End + +Private Function Border_Read() As Boolean + + Return $bBorder + +End + +Private Sub Border_Write(Value As Boolean) + + $bBorder = Value + Me._SetProperty("Border", Value) + +End + +Public Sub _RefreshReply() As Boolean + + Dim I As Integer + + If Super._RefreshReply() Then Return + + For I = 0 To $aChildren.Max + WebControl.FromName($aChildren[I])._RefreshReply() + Next + +End + +Public Sub _SetContainer(hCont As WebContainer) + + _Container = hCont + UpdateContainer() + +End + +Private Sub UpdateContainer() + + If Not _Container Then Return + + With _Container + .Arrangement = $iArrangement + .Spacing = $bSpacing + .Margin = $bMargin + End With + +End + +Private Function Children_Read() As WebControl[] + + Dim aChildren As WebControl[] + Dim I As Integer + + aChildren = New WebControl[$aChildren.Count] + For I = 0 To $aChildren.Max + aChildren[I] = WebControl.FromName($aChildren[I]) + Next + Return aChildren +End + +Public Sub _InitSpecialProperty(sProp As String, vVal As Variant) + + Dim I As Integer + Dim sClass As String + Dim sName As String + Dim hCtrl As WebControl + Dim hForm As WebForm + Dim sEventName As String + Dim aExtraChildren As String[][] + Dim aExtraChild As String[] + Dim sParentName As String + + If sProp = "#extra" Then + + hForm = Me.Form + aExtraChildren = vVal + + For I = 0 To aExtraChildren.Max + + aExtraChild = aExtraChildren[I] + + sName = aExtraChild[0] + sClass = aExtraChild[1] + + WebControl._NextName = sName + hCtrl = Object.New(sClass, [Me]) + + If aExtraChild.Count = 4 Then + sEventName = aExtraChild[2] + sParentName = aExtraChild[3] + 'Debug sName;; sClass; sEventName;; WebControl.FromName(sParentName) + If sEventName Then Object.Attach(hCtrl, WebControl.FromName(sParentName), sEventName) + Endif + + hCtrl._Extra = True + + 'WebForm.Print(WebControl._GetNames()) + + ' TODO: attach to the real form + 'If Left(sName) <> "$" Then Object.Attach(hCtrl, hForm, sName) + + Next + + $aExtraChildren = aExtraChildren + + Else If sProp = "#children" Then + + $aChildren = vVal + Me.Refresh + + Endif + +End + +Public Sub _Raise(sName As String) + + Dim iPos As Integer + + iPos = $aChildren.Find(sName) + If iPos < 0 Then Return + + $aChildren.Remove(iPos) + $aChildren.Add(sName) + Me._SetProperty("#children", $aChildren) + +End + +Public Sub _Lower(sName As String) + + Dim iPos As Integer + + iPos = $aChildren.Find(sName) + If iPos < 0 Then Return + + $aChildren.Remove(iPos) + $aChildren.Add(sName, 0) + Me._SetProperty("#children", $aChildren) + +End + + +Public Sub DeleteChildren() + + While $aChildren.Count + WebControl.FromName($aChildren[0]).Delete + Wend + +End + +Public Sub Delete() + + Inc WebForm._DisableRefresh + DeleteChildren + Dec WebForm._DisableRefresh + Super.Delete + +End + +Private Function Indent_Read() As Boolean + + Return $bIndent + +End + +Private Sub Indent_Write(Value As Boolean) + + $bIndent = Value + Me._SetProperty("Indent", Value) + +End diff --git a/comp/src/gb.web.form/.src/WebControl.class b/comp/src/gb.web.form/.src/WebControl.class new file mode 100644 index 00000000..60279e4c --- /dev/null +++ b/comp/src/gb.web.form/.src/WebControl.class @@ -0,0 +1,1061 @@ +' Gambas class file + +''' This class is the parent class of every web controls. + +Export +Create Private + +Public Const _IsControl As Boolean = True +Public Const _Properties As String = "Class,Width{WebCoord},Height{WebCoord},Visible=True,Enabled=True,Tag,Expand,Ignore,Background{Color},Foreground{Color},Tooltip,Font{WebFont}" +Public Const _Family As String = "WebForm" +Public Const _DefaultEvent As String = "Render" + +Event Render +Event Message(Source As WebControl, Action As String) +Event Dialog(Dialog As WebForm, Result As Variant) + +'' Return the server control identifier. +Property Read Id As Integer + +'' Return or set the name of the control, used as an identifier on the client side. +Property Name As String + +'' Returns or sets the control tag. +'' +'' This property is intended for the programmer and is never used by the component. It can contain any [/lang/type/variant] value. +Property Tag As Variant + +'' Return the parent container of the control. +Property Read Parent As WebContainer + +'' Return or set if the control should expand its size according to its parent arrangement mode. +Property Expand As Boolean + +'' Return or set if the control is visible. +Property Visible As Boolean + +'' Return a virtual object that allows to define specific style sheet elements of the control. +Property Read Style As WebControlStyle + +'' Return the web form that control belongs to. +Property Read Form As WebForm + +'' Return or set the explicit width of the control. +'' +'' This property can take any valid CSS size. If not set, the control will have its default width. +Property Width, W As String + +'' Return or set the explicit height of the control. +'' +'' This property can take any valid CSS size. If not set, the control will have its default height. +Property Height, H As String + +'' Return or set if the control must be ignored by its parent container in automatic arrangement mode. +Property Ignore As Boolean + +'' Return or set if the control is enabled. +Property Enabled As Boolean + +'' Return or set the background color of the control. +Property Background As Integer + +'' Return or set the foreground color of the control. +Property Foreground As Integer + +'' Return or set if the control has a border. +Property Border As Boolean + +'' Return or set the specific CSS classes added to the control. +Property Class As String + +'' Return or set the tooltip displayed when the mouse cursor hovers the control. +Property Tooltip As String + +'' Return or set the specific font of the control. +Property Font As String + +Static Private $iLastId As Integer +Static Private $iLastFormId As Integer +Static Private $cFromId As New Collection +Static Private $cFromName As New Collection + +Static Private $aStyleSheet As String[] + +Static Public _NextName As String + +Public _Refresh As Boolean +Public _EventName As String +Public _Extra As Boolean +Public _Proxy As String +Public _NoRefresh As Integer +Public _Naked As Boolean + +Private $iId As Integer +Private $iParentId As Integer +Private $iFormId As Integer +Private $sName As String +Private $vTag As Variant + +Private $iStyleSheet As Integer +Private $hStyle As WebControlStyle + +Private $iBackground As Integer = Color.Default +Private $iForeground As Integer = Color.Default + +Private $bExpand As Boolean +Private $bIgnore As Boolean +Private $bDisabled As Boolean +Private $bHidden As Boolean +Private $bNoBorder As Boolean +Private $sClass As String +Private $sTooltip As String + + +Public Sub _new(Optional Parent As WebContainer) + + Dim sName As String + + Inc $iLastId + $iId = $iLastId + + If _NextName Then + sName = _NextName + _NextName = "" + Endif + + If Me Is WebForm Then + Object.Attach(Me, Me, "WebForm") + $iFormId = $iId + Inc $iLastFormId + If Not sName Then sName = "@" & CStr($iLastFormId) + Else + _EventName = Param.EventName + Try $iFormId = Parent.Form.Id + If Not sName Then sName = Param.EventName + If Not sName Then sName = "$" & CStr($iId) + Endif + + SetName(sName) + + $cFromId[$iId] = Me + + Parent_Write(Parent) + + 'Debug Object.Type(Me);; $sName;; $iId + +End + +Private Sub SetName(Optional sName As String) + + Dim iSuffix As Integer + Dim sNameSuffix As String + Dim hParent As WebContainer + + If Left(sName) <> "@" Then sName = Form_Read().Name & "." & sName + + If sName = $sName Then Return + + If $sName Then $cFromName[$sName] = Null + + Do + If iSuffix = 0 Then + sNameSuffix = sName + Else + sNameSuffix = sName & "-" & CStr(iSuffix + 1) + Endif + If Not $cFromName.Exist(sNameSuffix) Then + sName = sNameSuffix + Break + Endif + Inc iSuffix + Loop + + hParent = Parent_Read() + If hParent Then hParent._UpdateChildName($sName, sName) + + $cFromName[sName] = $iId + $sName = sName + +End + +Public Sub _SetForm(hForm As WebForm) + + $iFormId = hForm.Id + +End + +Private Function Id_Read() As Integer + + Return $iId + +End + +Static Public Sub FromId(Id As Integer) As WebControl + + Return $cFromId[Id] + +End + +Static Public Sub FromName(Name As String) As WebControl + + Try Return $cFromId[$cFromName[Name]] + +End + +Private Function Parent_Read() As WebContainer + + Dim hParent As WebContainer + + If $iParentId = 0 Then Return + + hParent = $cFromId[$iParentId] +' If hParent.Parent And If hParent.Parent._Container = hParent Then hParent = hParent.Parent + + Return hParent + +End + +Private Sub Parent_Write(Value As WebContainer) + + Dim hParent As WebContainer + Dim sParent As String + + hParent = Parent_Read() + + If Value = hParent Then Return + + If hParent Then hParent._Remove(Me) + + If Value Then + If Value._Container Then Value = Value._Container + $iParentId = Value.Id + $iFormId = Value.Form.Id + Value._Add(Me) + sParent = Value.Name + Else + $iParentId = 0 + Endif + + '_SetProperty("#parent", sParent) + +End + +Private Function Name_Read() As String + + Return $sName + +End + +Private Function Tag_Read() As Variant + + Return $vTag + +End + +Private Sub Tag_Write(Value As Variant) + + $vTag = Value + _SetProperty("Tag", Value) + +End + +Public Sub _GetClassId(Optional sClass As String) As String + + Dim sResult As String + + If Not sClass Then sClass = "gw-" & LCase(Mid$(Object.Type(Me), 4)) + If $bNoBorder Then sClass &= " gw-noborder" + If Not Me.Enabled Then sClass &= " gw-disabled" + If $sClass Then sClass &= " " & $sClass + If $bHidden Then sClass &= " gw-hidden" + sResult = " class=\"" & sClass & "\" id=\"" & $sName & "\"" + If $sTooltip Then sResult &= " title=\"" & Replace($sTooltip, Chr$(34), """) & "\"" + Return sResult + +End + + +Public Sub _BeforeRender() + + Print ""; + +End + +Public Sub _Render() + +End + +Public Sub _AfterRender() + + Raise Render + Print "
    " + +End + +Public Sub _RenderStyleSheet() + + Dim hParent As WebContainer + + _StartStyleSheet + + If $bIgnore Then + + _AddStyleSheet("position:absolute;") + + Else + + If Me Is WebContainer Then _AddStyleSheet("position:relative;") + + If $bExpand Then + _AddStyleSheet("flex-grow:1;") + Else + _AddStyleSheet("flex-shrink:0;") + Endif + + hParent = Me.Parent + If hParent Then + Select Case hParent.Arrangement + Case Arrange.Vertical + 'If bInline Then _AddStyleSheet("display:block;") + If hParent.Spacing And If Not hParent._IsFirstVisibleChild(Me) Then _AddStyleSheet("margin-top:0.5rem;") + Case Arrange.Horizontal + '_AddStyleSheet("display: block;") + If hParent.Spacing And If Not hParent._IsFirstVisibleChild(Me) Then _AddStyleSheet("margin-left:0.5rem;") + Case Arrange.Column, Arrange.Row + _AddStyleSheet("display:inline-block;") + If hParent.Spacing Then _AddStyleSheet("margin-right:0.5rem;margin-bottom:0.5rem;") + End Select + Endif + + Endif + + If $iBackground <> Color.Default Then _AddStyleSheet("background-color:" & _GetColor($iBackground) & ";") + If $iForeground <> Color.Default Then _AddStyleSheet("color:" & _GetColor($iForeground) & ";") + + If $hStyle And If Not $hStyle.IsVoid() Then $aStyleSheet.Insert($hStyle._GetStyleSheet()) + + _EndStyleSheet + +End + +Public Sub _StartStyleSheet() + + Inc $iStyleSheet + If $iStyleSheet = 1 Then $aStyleSheet = New String[] + +End + +Public Sub _AddStyleSheet(sStyle As String) + + $aStyleSheet.Add(sStyle) + +End + +Public Sub _RemoveStyleSheet(sStyle As String) + + Try $aStyleSheet.Remove($aStyleSheet.Find(sStyle)) + +End + + +Public Sub _EndStyleSheet() + + Dec $iStyleSheet + If $iStyleSheet Then Return + + If $aStyleSheet.Count Then + 'Print "#"; Me.Name; " {" + 'Print " "; $aStyleSheet.Join("\n ") + 'Print "}" + Print " style=\""; $aStyleSheet.Join(""); "\""; + $aStyleSheet.Clear + Endif + +End + + + +Private Function Expand_Read() As Boolean + + Return $bExpand + +End + +Private Sub Expand_Write(Value As Boolean) + + If $bExpand = Value Then Return + $bExpand = Value + _SetProperty("Expand", Value) + Try Me.Parent.Refresh + +End + +Private Function Style_Read() As WebControlStyle + + If Not $hStyle Then $hStyle = New WebControlStyle As "Style" + Return $hStyle + +End + +Private Function Form_Read() As WebForm + + Try Return $cFromId[$iFormId] + +End + +Public Sub _SetProperty(sProp As String, vValue As Variant) + + Dim cProp As Collection + + If Me.Form._Loaded Then + + If WebForm._InExec Then Me.Refresh + + If TypeOf(vValue) = gb.Object And If vValue Is WebControl Then + sProp = "@" & sProp + vValue = vValue.Name + Endif + + cProp = Session[$sName] + If Not cProp Then cProp = New Collection + cProp[sProp] = vValue + Session[$sName] = cProp + + Endif + +End + +Public Sub _InitSpecialProperty((sProp) As String, (vVal) As Variant) + +End + +Public Sub _InitProperties() + + Dim cProp As Collection + Dim vVal As Variant + Dim sProp As String + + If Not Session.Id Then Return + + Try cProp = Session[$sName] + If Not cProp Then Return + + Object.Lock(Me) + For Each vVal In cProp + sProp = cProp.Key + 'WebForm.Print("set " & sProp) + If Left(sProp) = "#" Then + If sProp = "#style" Then + $hStyle = New WebControlStyle As "Style" + $hStyle._Style = vVal + ' Else If sProp = "#parent" Then + ' Parent_Write(FromName(vVal)) + Else + Me._InitSpecialProperty(sProp, vVal) + Endif + Else If Left(sProp) = "@" Then + Object.SetProperty(Me, Mid$(sProp, 2), WebControl.FromName(vVal)) + Else + Try Object.SetProperty(Me, sProp, vVal) + Endif + Next + Object.Unlock(Me) + +End + +Public Sub Style_Change() + + _SetProperty("#style", $hStyle._Style) + +End + +Public Sub IsHidden() As Boolean + + Dim hCtrl As WebControl + + hCtrl = Me + While hCtrl + If Not hCtrl.Visible Then Return True + hCtrl = hCtrl.Parent + Wend + +End + +Public Sub _CanRefresh() As Boolean + + If Not Me.Form._Loaded Then Return + If WebForm._InExec = 0 Then Return + If WebForm._DisableRefresh Then Return + If _NoRefresh Then Return + If IsHidden() Then Return + Return True + +End + + +Public Sub Refresh() + + If WebForm._DisableRefresh Then Return + If _NoRefresh Then Return + If IsHidden() Then Return + If WebForm._InExec Then + _Refresh = True + WebForm._HasRefresh = True + Endif + +End + +Public Sub _RefreshReply() As Boolean + + Dim hFile As File + Dim sResult As String + + If _Refresh Then + + hFile = Open String For Write + Output To #hFile + + If Not _Naked Then Me._BeforeRender() + Me._Render() + If Not _Naked Then Me._AfterRender() + + sResult = Close #hFile + Output To Default + + 'WebForm._AddRefresh("$(" & JS(Me.Name) & ").outerHTML = " & JS(sResult) & ";") + + If _Naked Then + Print "gw.setInnerHtml("; JS(Me.Name); ","; JS(sResult); ");" + Else + Print "gw.setOuterHtml("; JS(Me.Name); ","; JS(sResult); ");" + 'Print "$("; JS(Me.Name); ").outerHTML = "; JS(sResult); ";" + Endif + + _Refresh = False + Return True + + Endif + +Catch + + Output To Default + Error.Propagate() + +End + +Public Sub _GetEventJS(sProp As String, sEvent As String, Optional sArg As String) As String + + Dim sStr As String + + If Not Object.CanRaise(Me, sEvent) Then Return + + sStr = " " & sProp & "=\"gw.raise(" '& JS(Me.Form.Name) & "," + If Me Is WebForm Then + sStr &= "null" + Else + sStr &= JS($sName) + Endif + sStr &= "," & JS(sEvent) + If sArg Then sStr &= "," & sArg + sStr &= ");" + sStr &= "\"" + + Return sStr + +End + +Public Sub _GetUpdateJS(sEvent As String, sProp As String, Optional sGetValue As String = "null", Optional sMore As String) As String + + Dim sStr As String + sStr = " " & sEvent & "=\"gw.update(" & JS($sName) & "," & JS(sProp) & "," & sGetValue & ");" + If sMore Then sStr &= sMore + Return sStr & "\"" + +End + +Public Sub _GetUpdateWaitJS(sEvent As String, sProp As String, Optional sGetValue As String = "null", Optional sMore As String) As String + + Dim sStr As String + sStr = " " & sEvent & "=\"gw.updateWait(" & JS($sName) & "," & JS(sProp) & "," & sGetValue & ");" + If sMore Then sStr &= sMore + Return sStr & "\"" + +End + +Public Sub _UpdateProperty((sProp) As String, (vValue) As Variant) + +End + +Private Function Width_Read() As String + + If $hStyle Then Return $hStyle["width"] + +End + +Private Sub Width_Write(Value As String) + + Style_Read()["width"] = Value + +End + +Private Function Height_Read() As String + + If $hStyle Then Return $hStyle["height"] + +End + +Private Sub Height_Write(Value As String) + + Style_Read()["height"] = Value + +End + +Private Function Visible_Read() As Boolean + + Return Not $bHidden + + ' If Not $hStyle Then Return True + ' Return $hStyle["display"] <> "none" + +End + +Private Sub Visible_Write(Value As Boolean) + + Dim bCanRefresh As Boolean + + If $bHidden <> Value Then Return + + $bHidden = False + bCanRefresh = _CanRefresh() + $bHidden = Not Value + + Inc _NoRefresh + _SetProperty("Visible", Value) + Dec _NoRefresh + If bCanRefresh Then + WebForm._AddJavascript("gw.setVisible(" & JS(Me.Name) & "," & JS(Not $bHidden) & ")") + If Me.Parent Then + Me.Parent.Refresh + Else If Me Is WebContainer Then + Refresh + Endif + Endif + 'Me.Parent.Refresh + + ' If Value Then + ' If $hStyle Then Style_Read()["display"] = "" + ' Else + ' Style_Read()["display"] = "none" + ' Endif + +End + +Private Function Ignore_Read() As Boolean + + Return $bIgnore + +End + +Private Sub Ignore_Write(Value As Boolean) + + If $bIgnore = Value Then Return + $bIgnore = Value + _SetProperty("Ignore", Value) + Me.Parent.Refresh + +End + +Public Sub SetFocus(Optional Highlight As Boolean) + + Dim sName As String + + sName = $sName + If _Proxy Then sName &= _Proxy + + WebForm._AddReply("gw.setFocus(" & JS(sName) & ")") + If Highlight Then WebForm._AddReply("gw.highlightMandatory(" & JS(sName) & ")") + +End + +Private Function Enabled_Read() As Boolean + + If $bDisabled Then Return + If $iParentId = 0 Then Return True + Return Parent_Read().Enabled + +End + +Private Sub Enabled_Write(Value As Boolean) + + If $bDisabled <> Value Then Return + $bDisabled = Not Value + Me._SetProperty("Enabled", Value) + +End + +Private Function Background_Read() As Integer + + Return $iBackground + +End + +Private Sub Background_Write(Value As Integer) + + If $iBackground = Value Then Return + $iBackground = Value + Me._SetProperty("Background", Value) + +End + +Private Function Foreground_Read() As Integer + + Return $iForeground + +End + +Private Sub Foreground_Write(Value As Integer) + + If $iForeground = Value Then Return + $iForeground = Value + Me._SetProperty("Foreground", Value) + + +End + +Static Public Sub _GetColor(iCol As Integer) As String + + Dim A As Integer + Dim R As Integer + Dim G As Integer + Dim B As Integer + + A = Lsr(iCol, 24) + R = Lsr(iCol, 16) And 255 + G = Lsr(iCol, 8) And 255 + B = iCol And 255 + + If A Then + Return "rgba(" & CStr(R) & "," & CStr(G) & "," & CStr(B) & "," & CStr(Round(A / 255, -2)) & ")" + Else + Return "#" & Hex$(R, 2) & Hex$(G, 2) & Hex$(B, 2) + Endif + +End + +Public Sub Raise() + + Dim hParent As WebContainer + + hParent = Parent_Read() + If hParent Then hParent._Raise($sName) + +End + +Public Sub Lower() + + Dim hParent As WebContainer + + hParent = Parent_Read() + If hParent Then hParent._Lower($sName) + +End + +Static Public Sub _GetNames() As String + + Dim aKey As New String[] + + For Each $cFromName + aKey.Add($cFromName.Key) + Next + + Return aKey.Join(",") + +End + +Public Sub Delete() + + Dim hParent As WebContainer + + hParent = Parent_Read() + If hParent Then hParent._Remove(Me) + + Session[$sName] = Null + Session[$sName & ":data"] = Null + + $cFromName[$sName] = Null + $cFromId[$iId] = Null + 'Debug $sName;; $iId;; System.Backtrace.Join(" ") + +End + +Public Sub Show() + + Me.Visible = True + +End + +Public Sub Hide() + + Me.Visible = False + +End + +Public Sub _get(Key As String) As Variant + + Dim cData As Collection + + If Not Key Then Error.Raise("Void key") + + cData = Session[$sName & ":data"] + If cData Then Return cData[Key] + +End + +Public Sub _put(Value As Variant, Key As String) + + Dim cData As Collection + + If Not Key Then Error.Raise("Void key") + cData = Session[$sName & ":data"] + If Not cData Then cData = New Collection + cData[Key] = Value + Session[$sName & ":data"] = cData + +End + +Public Sub Reset() + + Session[$sName & ":data"] = Null + +End + + +' Public Sub _GetBaseName() As String +' +' Dim iPos As Integer +' +' iPos = InStr($sName, ".") +' If iPos Then +' Return Mid$($sName, iPos + 1) +' Else +' Return $sName +' Endif +' +' End + +Private Function Border_Read() As Boolean + + Return $bNoBorder + +End + +Private Sub Border_Write(Value As Boolean) + + If $bNoBorder <> Value Then Return + $bNoBorder = Not Value + Me._SetProperty("Border", Value) + +End + +Private Function Class_Read() As String + + Return $sClass + +End + +Private Sub Class_Write(Value As String) + + If $sClass = Value Then Return + $sClass = Value + Me._SetProperty("Class", Value) + +End + +Private Function Tooltip_Read() As String + + Return $sTooltip + +End + +Private Sub Tooltip_Write(Value As String) + + If $sTooltip = Value Then Return + $sTooltip = Value + Me._SetProperty("Tooltip", Value) + +End + +Public Sub _GetImageLink(sImage As String) As String + + If Left(sImage) = "/" Then + Return Application.Root &/ "img:" & Me.Name & "?z=" & Base64(Format(Now, "hhnnssuuu")) + Else + Return Application.Root &/ sImage + Endif + +End + + +Private Sub Name_Write(Value As String) + + SetName(Value) + +End + +Public Sub AddClass(sClass As String) + + Dim aClass As String[] + + sClass = Trim(sClass) + If Not sClass Then Return + + aClass = Split($sClass, " ") + + For Each sClass In Split(sClass, " ", "", True) + + If aClass.Exist(sClass) Then Continue + aClass.Add(sClass) + + Next + + Class_Write(aClass.Join(" ")) + +End + +Public Sub RemoveClass(sClass As String) + + Dim aClass As String[] + Dim iPos As Integer + + sClass = Trim(sClass) + If Not sClass Then Return + + aClass = Split($sClass, " ") + + For Each sClass In Split(sClass, " ", "", True) + + iPos = aClass.Find(sClass) + If iPos < 0 Then Continue + + aClass.Remove(iPos) + + Next + + Class_Write(aClass.Join(" ")) + +End + +Private Function Font_Read() As String + + Dim aFont As New String[] + Dim sFamily As String + Dim sWeight As String + Dim sStyle As String + Dim sSize As String + Dim sDecoration As String + + If Not $hStyle Then Return + + sFamily = $hStyle["font-family"] + sWeight = $hStyle["font-weight"] + sStyle = $hStyle["font-style"] + sDecoration = $hStyle["font-decoration"] + sSize = $hStyle["font-size"] + + If sFamily Then aFont.Add(sFamily) + + If sWeight = "normal" And If sStyle = "normal" And If sDecoration = "none" Then + + aFont.Add("normal") + + Else + + If sWeight Then + If sWeight <> "bold" Then sWeight = "weight:" & sWeight + aFont.Add(sWeight) + Endif + + If sStyle Then + If sStyle <> "italic" Then sStyle = "style:" & sStyle + aFont.Add(sStyle) + Endif + + If sDecoration Then + If sDecoration = "underline" Then + aFont.Add(sDecoration) + Else If sDecoration = "line-through" Then + aFont.Add("strikeout") + Else + aFont.Add("decoration:" & sDecoration) + Endif + Endif + + Endif + + If sSize Then + If Not IsDigit(Left(sSize)) Then sSize = "size:" & sSize + aFont.Add(sSize) + Endif + + Return aFont.Join() + +End + +Private Sub Font_Write(Value As String) + + Dim sElt As String + + For Each sElt In Split(Value) + + sElt = Trim(sElt) + If Not sElt Then Continue + + If IsDigit(Left(sElt)) Then + Style_Read()["font-size"] = sElt + Continue + Endif + + If sElt Begins "weight:" Then + Style_Read()["font-weight"] = Mid$(sElt, InStr(sElt, ":") + 1) + Continue + Endif + + If sElt Begins "style:" Then + Style_Read()["font-style"] = Mid$(sElt, InStr(sElt, ":") + 1) + Continue + Endif + + If sElt Begins "decoration:" Then + Style_Read()["text-decoration"] = Mid$(sElt, InStr(sElt, ":") + 1) + Continue + Endif + + If sElt Begins "size:" Then + Style_Read()["font-size"] = Mid$(sElt, InStr(sElt, ":") + 1) + Continue + Endif + + Select Case sElt + Case "normal" + Style_Read()["font-weight"] = "normal" + Style_Read()["font-style"] = "normal" + Style_Read()["text-decoration"] = "none" + Case "bold" + Style_Read()["font-weight"] = sElt + Case "italic" + Style_Read()["font-style"] = sElt + Case "italic" + Style_Read()["font-style"] = sElt + Case "underline" + Style_Read()["text-decoration"] = sElt + Case "strikeout" + Style_Read()["text-decoration"] = "line-through" + Case Else + Style_Read()["font-family"] = sElt + End Select + + Next + +End diff --git a/comp/src/gb.web.form/.src/WebControlStyle.class b/comp/src/gb.web.form/.src/WebControlStyle.class new file mode 100644 index 00000000..a459a416 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebControlStyle.class @@ -0,0 +1,46 @@ +' Gambas class file + +Export + +Event Change + +Public _Style As New Collection + +Public Sub _get({Property} As String) As String + + Return _Style[{Property}] + +End + +Public Sub _put(Value As String, {Property} As String) + + _Style[{Property}] = Value + Raise Change + +End + +Public Sub IsVoid() As Boolean + + Return _Style.Count = 0 + +End + +Public Sub Exist({Property} As String) As Boolean + + Return _Style.Exist({Property}) + +End + + +Public Sub _GetStyleSheet() As String[] + + Dim aStyle As New String[] + Dim sValue As String + + For Each sValue In _Style + aStyle.Add(_Style.Key & ":" & sValue & ";") + Next + + Return aStyle + +End diff --git a/comp/src/gb.web.form/.src/WebExpander.class b/comp/src/gb.web.form/.src/WebExpander.class new file mode 100644 index 00000000..6977abec --- /dev/null +++ b/comp/src/gb.web.form/.src/WebExpander.class @@ -0,0 +1,102 @@ +' Gambas class file + +Export +Inherits WebContainer + +Public Const _Properties As String = "*,Text,Hidden" +Public Const _DefaultEvent As String = "Show" +Public Const _Similar As String = "WebContainer" +Public Const _DrawWith As String = "Expander" + +Event Show +Event Hide + +Property Hidden As Boolean +Property Text As String +Property Border As Boolean + +Private $hCont As WebContainer +Private $bHidden As Boolean +Private $sText As String +Private $bBorder As Boolean + +Public Sub _new() + + $hCont = New WebContainer(Me) + Me._Container = $hCont + +End + + +Private Function Hidden_Read() As Boolean + + Return $bHidden + +End + +Private Sub Hidden_Write(Value As Boolean) + + $bHidden = Value + Me._SetProperty("Hidden", Value) + If $bHidden Then + Raise Hide + Else + Raise Show + Endif + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + Me._SetProperty("Text", Value) + +End + + +Public Sub _Render() + + If $bBorder Then Print "
    " + + Print "
    "; + Print ""; + Print "
    "; $sText; "
    "; + Print "
    " + + If Not $bHidden Then + With $hCont + ._BeforeRender() + ._Render() + ._AfterRender() + End With + Endif + + If $bBorder Then Print "
    " + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + If sProp = "hidden" Then Try Me.Hidden = vValue + +End + + +Private Function Border_Read() As Boolean + + Return $bBorder + +End + +Private Sub Border_Write(Value As Boolean) + + $bBorder = Value + Me._SetProperty("Border", Value) + +End diff --git a/comp/src/gb.web.form/.src/WebForm.class b/comp/src/gb.web.form/.src/WebForm.class new file mode 100644 index 00000000..c85e0845 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebForm.class @@ -0,0 +1,940 @@ +' Gambas class file + +Export +Create Static + +Inherits WebContainer + +Public Const _IsForm As Boolean = True +Public Const _HiddenControls As String = "WebControl,WebForm,WebWindow,Timer" +Public Const _Properties As String = "*,Title,Resizable,Persistent" +Public Const _DefaultEvent As String = "Open" + +Static Property Debug As Boolean +Static Property Startup As String + +Static Public _InExec As Integer +Static Public _Current As WebForm + +Static Private $aJavascriptBefore As New String[] +Static Private $aJavascript As New String[] +Static Private $aRefresh As New String[] + +Static Private $cLibrary As New Collection + +Static Private $iDownload As Integer +'Static Private $aPreload As String[] + +Event Open +Event Event +Event Close + +Property Title As String +Property Resizable As Boolean +Property Width, W As String +Property Height, H As String +Property Read Popup As Boolean +Property Read PopupParent As WebControl +Property Persistent As Boolean + +Static Public _DisableRefresh As Integer +Static Public _HasRefresh As Boolean + +Public _Loaded As Boolean +Public _Window As Integer + +Private $aJavascriptFiles As String[] +Private $aJavascriptExternfiles As String[] + +Private $sTitle As String +Private $bResizable As Boolean +Private $sX As String +Private $sY As String +Private $sWidth As String +Private $sHeight As String + +Private $hWindowContainer As WebContainer +Private $bPersistent As Boolean +'Private $bCentered As Boolean + +Static Public Sub _init() + + System.Language = Request.Language + +End + + +Static Public Sub Main() + + Dim sPath As String + Dim hForm As WebForm + Dim aLib As String[] + Dim sFile As String + Dim sKey As String + Dim sErr As String + Dim sVar As String + + 'System.Log(Format(Timer, "#0.000") & ": start request: [" & Session.Id & "] " & URL.Decode(Application.Request)) + + Try Object.Call(Application.Startup, "HandleRequest") + If Response.Done Then Return + + sPath = Mid$(Request.Path, 2) + + If sPath Then + + If sPath Begins "style:" And sPath Ends ".css" Then + + aLib = Split(Left(sPath, -4), ":") + If aLib.Count >= 3 Then sFile = aLib[1] & ".css" + RenderStyleSheet(sFile) + Goto END_REQUEST + + Else If sPath Begins "lib:" And sPath Ends ".js" Then + + aLib = Split(Left(sPath, -3), ":") + aLib.Remove(aLib.Max) + 'If aLib.Count >= 3 Then sFile = aLib[1] & ".js" + RenderJavascript(aLib) + Goto END_REQUEST + + Else If sPath Begins "img:" Then + + Try sPath = Session[Mid$(sPath, 5)]["Image"] + If Not Error Then Response.SendFile(sPath) + Goto END_REQUEST + + Else If sPath Begins "download:" Then + + If DownloadFile(Mid$(sPath, 10)) Then Goto NOT_FOUND + Goto END_REQUEST + + Else If sPath = "~dump" Then + + Response.ContentType = "text/plain;charset=utf-8" + Response.Begin + + For Each sVar In Env + Print sVar; " = "; Env[sVar] + Next + + If Session.Id Then + + Print + For Each sKey In Session.Keys + Print sKey; " : "; JSON.Encode(Session[sKey]) + Next + + Print + Print "size = "; Session.Size + + Endif + + Response.End + Return + + Else If sPath = "~logout" Then + + Session.Abandon + + Else If Exist("../.public/" &/ sPath) Then + + Response.SendFile("../.public/" &/ sPath) + Goto END_REQUEST + + Else If Exist(".public/" &/ sPath) Then + + Response.SendFile(".public/" &/ sPath) + Goto END_REQUEST + + ' Else + ' + ' iPos = InStr(sPath, "/") + ' If iPos = 0 Then + ' sClass = sPath + ' sPath = "" + ' Else + ' sClass = Left(sPath, iPos - 1) + ' sPath = Mid$(sPath, iPos + 1) + ' Endif + ' + ' Try hStat = Class.Stat(".." &/ sClass) + ' If Not hStat Then + ' Try hStat = Class.Stat(sClass) + ' If Not hStat Then Goto NOT_FOUND + ' Endif + ' + ' If LCase(hStat.Parent) <> "webform" Then Goto NOT_FOUND + ' + ' Try hClass = Class.Load(sClass) + ' If Not hClass Then + ' Debug "Class.Load: "; Error.Text + ' Goto NOT_FOUND + ' Endif + ' + ' 'Main.DumpSession + ' hForm = hClass.AutoCreate() + ' + Endif + + Endif + + If Session.Id And If Session["startup"] Then + Try hForm = Class.Load(Session["startup"]).AutoCreate() + Endif + If Not hForm Then + hForm = Application.Startup.AutoCreate() + Endif + _Current = hForm + + If sPath = "x" Then + hForm._Exec(JSON.Decode(Request["c"])) + Else If sPath = "u" And If Request.Method = "POST" Then + hForm._Upload(Request["id"]) + Else + hForm.Render + Endif + + Goto END_REQUEST + +NOT_FOUND: + + Response.Status = "404 NotFound" + Response.Begin + Print "

    404 NotFound

    " + Response.End + +END_REQUEST: + + 'System.Log(Format(Timer, "#0.000") & ": end request: [" & Session.Id & "] " & URL.Decode(Application.Request)) + Return + +Catch + + sErr = Error.Text & "\n" & Error.Backtrace.Join(" ") + + Response.Cancel + Response.Begin + Print "" + Response.End + 'System.Log("End request") + +End + +Static Public Sub _AddRefresh(sStr As String) + + $aRefresh.Add(sStr) + +End + +Static Public Sub _AddReplyBefore(sStr As String) + + $aJavascriptBefore.Add(sStr) + +End + +Static Public Sub _AddReply(sStr As String) + + $aJavascript.Add(sStr) + +End + + +Static Public Sub _AddJavascript(sStr As String) + + _AddReply(sStr) + +End + +Static Public Sub _AddJavascriptBefore(sStr As String) + + _AddReplyBefore(sStr) + +End + +Public Sub _new() + + $hWindowContainer = New WebContainer(Me) + $hWindowContainer.Ignore = True + $hWindowContainer._Naked = True + $hWindowContainer.Class = "gw-window-container" + +End + +Public Sub _ready() + + Me._InitProperties + $hWindowContainer.Raise + _Loaded = True + +End + +Public Sub _BeforeRender() + + Print "
    " + +End + +Static Private Sub PrintJavascript() + + If $aJavascriptBefore.Count Then + Print $aJavascriptBefore.Join(";\n"); ";" + $aJavascriptBefore.Clear + Endif + + If _HasRefresh Then + _HasRefresh = False + Me._RefreshReply() + Endif + + If $aJavascript.Count Then + Print $aJavascript.Join(";\n"); ";" + $aJavascript.Clear + Endif + +End + +Public Sub _BeginJavascript() As Variant + + Dim aJavascript As String[] + Dim aJavascriptBefore As String[] + + aJavascript = $aJavascript + aJavascriptBefore = $aJavascriptBefore + + $aJavascript = New String[] + $aJavascriptBefore = New String[] + + Return [aJavascript, aJavascriptBefore] + +End + +Public Sub _EndJavascript(vSave As Variant) + + Dim aJavascript As String[] + Dim aJavascriptBefore As String[] + + aJavascript = $aJavascript + aJavascriptBefore = $aJavascriptBefore + + $aJavascript = vSave[0] + $aJavascriptBefore = vSave[1] + + If aJavascriptBefore.Count Then Print aJavascriptBefore.Join(";"); ";"; + If aJavascript.Count Then Print aJavascript.Join(";"); ";"; + +End + +Public Sub Render() + + Dim sImage As String + + Response.Buffered = True + Response.Begin + + Header.Form = Me + Header.__Render() + 'Print Header.ToString(); + Header.Form = Null + + $aJavascript.Clear + + Print "" + + Print "
    "; + For Each sImage In ["error.png", "info.png", "question.png", "warning.png"] + Print ""; + Next + Print "
    " + Print "
    " + Print "
    " + "
    " + "
    " + "
    " + "
    " + "
    " + "
    " + + Me._BeforeRender() + Me._Render() + Me._AfterRender() + + Print "" + + Print "" + Print "" + + Response.End + +End + +Static Private Sub RenderStyleSheet(Optional sFile As String) + + If Not sFile Then sFile = "style.css" + + If Not Exist(sFile) Then + Response.Status = "404 NotFound" + Response.Begin + Response.End + Return + Endif + + Response.Buffered = True + Response.ContentType = "text/css;charset=utf-8" + Response.Begin + + Print Replace(File.Load(sFile), "//", Application.Root); + + If Exist("../.public" &/ sFile) Then Print File.Load("../.public/" &/ sFile); + + Response.End + +End + +Static Private Sub RenderJavascript(aLib As String[]) + + Dim sFile As String + + Response.ContentType = "text/javascript;charset=utf-8" + Response.Begin + + For Each sFile In aLib + + sFile &= ".js" + + If sFile = "lib.js" Then + Print "$root = "; JS(Application.Root); ";" + Print File.Load(sFile) + Print File.Load("ac.js") + Else + If Exist(sFile) Then + Print File.Load(sFile) + Else If Exist(".." &/ sFile) Then + Print File.Load(".." &/ sFile) + Endif + Endif + + Next + + Response.End + +End + + +Private Function Title_Read() As String + + Return $sTitle + +End + +Private Sub Title_Write(Value As String) + + $sTitle = Value + Me._SetProperty("Title", Value) + +End + +Static Public Sub Print(Text As String) + + $aJavascript.Add("console.log(" & JS(Text) & ")") + +End + +Static Public Sub Exec(Javascript As String) + + $aJavascript.Add(Javascript) + +End + +Public Sub _Exec(aCmd As Variant[]) + + Dim hCtrl As WebControl + Dim iOldInExec As Integer = _InExec + Dim sErr As String + 'Dim fTimer As Float + + 'fTimer = Timer + + Inc _InExec + + Raise Event + + Select Case aCmd[0] + + Case "raise" + + If aCmd[1] Then + hCtrl = WebControl.FromName(aCmd[1]) + Else + hCtrl = Me + Endif + + If Not hCtrl Then Goto VOID_RESPONSE + + Object.Raise(hCtrl, aCmd[2], aCmd[3]) + + Case "update" + + 'Debug aCmd[1];; aCmd[2];; aCmd[3] + + If aCmd[1] Then + hCtrl = WebControl.FromName(aCmd[1]) + If Not hCtrl Then Goto VOID_RESPONSE + Else + hCtrl = Me + Endif + hCtrl._UpdateProperty(aCmd[2], aCmd[3]) + + End Select + + _InExec = iOldInExec + + Response.Buffered = True + Response.ContentType = "text/javascript;charset=utf-8" + Response.Begin + PrintJavascript + 'Session.Save + 'Print "// WebForm._Exec: " & Format(Timer - fTimer, "0.000 s") + Response.End + Return + +VOID_RESPONSE: + + Response.Begin + 'Print "console.log("; JS("unknown control " & aCmd[1]); ");" + Response.End + Return + +Catch + + sErr = Error.Text & "\n" & Error.Backtrace.Join(" ") + _InExec = iOldInExec + + Response.Cancel + Response.Begin + Print "console.log("; JS(aCmd); ");" + Print "console.log("; JS(sErr); ");" + Print "console.log("; JS($aJavascript.Join("\n")); ");" + Print "alert("; JS(sErr); ");" + Response.End + +End + +Public Sub Close(Optional ReturnValue As Variant) + + ' Dim bCancel As Boolean + Dim hWindow As WebWindow + + Raise Close + + If Me._Window Then + + hWindow = WebControl.FromId(Me._Window) + With hWindow + .Close(ReturnValue) + If $bPersistent Then + .Hide + Else + .Delete + Endif + End With + + Else + + If $bPersistent Then + Me.Hide + Else + Me.Delete + Endif + _AddReply("window.location.reload(true)") + + Endif + +End + +' Public Sub Open(Child As WebForm) +' +' Dim hWindow As WebWindow +' +' If $bOpened Then Return +' +' hWindow = New WebWindow(Me) +' hWindow.Child = Child +' Child._Window = hWindow.Id +' +' $bOpened = True +' +' End + +Public Sub Hide() + + Dim hWindow As WebControl + + If _Window = 0 Then Return + + hWindow = WebControl.FromId(_Window) + If hWindow Then hWindow.Hide + +End + +Private Sub DoShow() + + Dim hWindow As WebWindow + + If _Window Then + hWindow = WebControl.FromId(_Window) + If hWindow Then hWindow.Show + Return + Endif + + hWindow = New WebWindow(WebForm._Current._GetWindowContainer()) + hWindow.Child = Me + hWindow.Resizable = $bResizable + hWindow.Move($sX, $sY, $sWidth, $sHeight) + + _Window = hWindow.Id + +End + + +Public Sub Show() + + DoShow + Raise Open + +End + +Public Sub ShowModal(Optional Parent As WebControl) + + Dim hWindow As WebWindow + + If _Window = 0 Then DoShow() + + hWindow = WebControl.FromId(_Window) + If hWindow Then + hWindow.Modal = True + If IsMissing(Parent) Then Parent = Last + If Parent Then hWindow.ParentControl = Parent + Endif + + Raise Open + +End + +Public Sub ShowPopup(Control As WebControl, Optional Alignment As Integer = Align.Left) + + Dim hWindow As WebWindow + + If _Window Then Return + + hWindow = New WebWindow(WebForm._Current._GetWindowContainer()) + hWindow.Child = Me + hWindow.Resizable = $bResizable + hWindow.PopupAlignment = Alignment + hWindow.PopupControl = Control + hWindow.Modal = True + + _Window = hWindow.Id + + Raise Open + +End + + + +Public Sub Move(X As String, Y As String, Optional Width As String, Optional Height As String) + + Dim hWindow As WebWindow = WebControl.FromId(_Window) + + $sX = X + $sY = Y + If Not IsMissing(Width) Then $sWidth = Width + If Not IsMissing(Height) Then $sHeight = Height + '$bCentered = False + + If hWindow Then hWindow.Move(X, Y, Width, Height) + +End + +Public Sub Resize(Width As String, Height As String) + + Dim hWindow As WebWindow = WebControl.FromId(_Window) + + $sWidth = Width + $sHeight = Height + '$bCentered = False + + If hWindow Then hWindow.Resize(Width, Height) + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + Dim aWindows As String[] + Dim sName As String + + Select Case sProp + + Case "#windows" + aWindows = vValue + Inc _DisableRefresh + For Each sName In aWindows + Try WebControl.FromName(sName).Raise() + Next + Dec _DisableRefresh + + End Select + +End + +Private Function Resizable_Read() As Boolean + + Return $bResizable + +End + +Private Sub Resizable_Write(Value As Boolean) + + Dim hWindow As WebWindow = WebControl.FromId(_Window) + $bResizable = Value + UpdateSize + If hWindow Then hWindow.Resizable = Value + +End + +' Public Sub Center() +' +' Dim hWindow As WebWindow = WebControl.FromId(_Window) +' +' $bCentered = True +' If hWindow Then hWindow.Centered = True +' +' End + +Static Private Function Debug_Read() As Boolean + + Try Return Session["debug"] + +End + +Static Private Sub Debug_Write(Value As Boolean) + + Dim bDebug As Boolean + + Try bDebug = Session["debug"] + If Value = bDebug Then Return + + Session["debug"] = Value + _AddJavascript("gw.debug = " & JS(Value)) + +End + +Private Sub UpdateSize() + + If $bResizable Then + Me.Style["width"] = "" + Me.Style["height"] = "" + Else + Me.Style["width"] = Me.Style["min-width"] + Me.Style["height"] = Me.Style["min-height"] + Endif + +End + +Private Function Width_Read() As String + + Return Me.Style["min-width"] + +End + +Private Sub Width_Write(Value As String) + + Me.Style["min-width"] = Value + UpdateSize + +End + +Private Function Height_Read() As String + + Return Me.Style["min-height"] + +End + +Private Sub Height_Write(Value As String) + + Me.Style["min-height"] = Value + UpdateSize + +End + +Public Sub _GetWindowContainer() As WebContainer + + Return $hWindowContainer + +End + +' Public Sub _Preload(sUrl As String) +' +' If Not $aPreload Then $aPreload = New String[] +' If $aPreload.Exist(sUrl) Then Return +' $aPreload.Add(sUrl) +' +' End +' +Static Public Sub _AddLibrary(sLib As String) + + $cLibrary[sLib] = True + +End + +Private Function Popup_Read() As Boolean + + Dim hWindow As WebWindow = WebForm.FromId(Me._Window) + If hWindow Then Return hWindow.IsPopup() + +End + +Private Function PopupParent_Read() As WebControl + + Dim hWindow As WebWindow = WebForm.FromId(Me._Window) + If hWindow Then Return hWindow.PopupControl + +End + +Public Sub _Upload(Id As String) + + Dim hCtrl As WebUploadArea + Dim iOldInExec As Integer = _InExec + Dim sErr As String + + Inc _InExec + + Try hCtrl = WebControl.FromName(Id) + If hCtrl Then hCtrl._UploadFinish + + _InExec = iOldInExec + + Response.Begin + PrintJavascript + Response.End + +Catch + + sErr = Error.Text & "\n" & Error.Backtrace.Join(" ") + 'Debug sErr + _InExec = iOldInExec + + Response.Begin + Print "console.log("; JS(sErr); ");" + Print "console.log("; JS($aJavascript.Join("\n")); ");" + Response.End + +End + +Public Sub Reload() + + If Me = _Current Then + _AddReply("location.reload(true)") + Else + Me.Refresh + Endif + +End + +Public Sub _GetJavascriptFiles() As String[] + + Return $aJavascriptFiles + +End + +Public Sub _GetJavascriptExternFiles() As String[] + + Return $aJavascriptExternFiles + +End + +Public Sub AddJavascriptFile(sFile As String) + + If sFile Begins "http://" Or If sFile Begins "https://" Then + If Not $aJavascriptExternFiles Then $aJavascriptExternFiles = New String[] + $aJavascriptExternFiles.Add(sFile) + Else + If sFile Ends ".js" Then sFile = Left(sFile, -3) + If Not $aJavascriptFiles Then $aJavascriptFiles = New String[] + $aJavascriptFiles.Add(sFile) + Endif + +End + +Static Public Sub Download(Path As String, Optional ContentType As String, Optional Remove As Boolean) + + Dim cDownload As Collection + + '' TODO: Move file to /tmp/gambas.XXXX/download if it is a temp file + + cDownload = Session["download"] + If Not cDownload Then cDownload = New Collection + Inc $iDownload + cDownload[$iDownload] = [Path, ContentType, Remove] + Session["download"] = cDownload + + _AddJavascript("window.open(" & JS("/" &/ Application.Root &/ "download:" & CStr($iDownload) &/ File.Name(Path)) & ", '_blank');") + +End + +Static Private Sub DownloadFile(sKey As String) As Boolean + + Dim aDownload As String[] + Dim iPos As Integer + + iPos = InStr(sKey, "/") + If iPos Then sKey = Left(sKey, iPos - 1) + + Try aDownload = Session["download"][sKey] + If Error Then Return True + Response.AddHeader("Content-Disposition", "attachment") + Response.SendFile(aDownload[0], aDownload[1]) + If aDownload[2] Then Try Kill aDownload[0] + +End + +Private Function Persistent_Read() As Boolean + + Return $bPersistent + +End + +Private Sub Persistent_Write(Value As Boolean) + + $bPersistent = Value + Me._SetProperty("Persistent", Value) + +End + +Static Private Function Startup_Read() As String + + Dim sStartup As String + + If Session.Id Then sStartup = Session["startup"] + If Not sStartup Then sStartup = Application.Startup.Name + Return sStartup + +End + +Static Private Sub Startup_Write(Value As String) + + Session["startup"] = Value + +End diff --git a/comp/src/gb.web.form/.src/WebHBox.class b/comp/src/gb.web.form/.src/WebHBox.class new file mode 100644 index 00000000..7a2c008b --- /dev/null +++ b/comp/src/gb.web.form/.src/WebHBox.class @@ -0,0 +1,15 @@ +' Gambas class file + +Export + +Inherits WebContainer + +Public Const _Properties As String = "*,-Arrangement" +Public Const _DefaultArrangement As String = "H" +Public Const _Similar As String = "WebContainer" + +Public Sub _new() + + Me.Arrangement = Arrange.Horizontal + +End diff --git a/comp/src/gb.web.form/.src/WebHtml.class b/comp/src/gb.web.form/.src/WebHtml.class new file mode 100644 index 00000000..78f85f90 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebHtml.class @@ -0,0 +1,32 @@ +' Gambas class file + +Export +Inherits WebControl + +Public Const _Properties As String = "*,Text" +Public Const _DrawWith As String = "TextLabel" +'Public Const _DrawWith As String = "Button" + +Property Html, Text As String + +Private $sHTML As String + + +Private Function Html_Read() As String + + Return $sHTML + +End + +Private Sub Html_Write(Value As String) + + $sHTML = Value + Me._SetProperty("Text", Value) + +End + +Public Sub _Render() + + Print $sHTML; + +End diff --git a/comp/src/gb.web.form/.src/WebImage.class b/comp/src/gb.web.form/.src/WebImage.class new file mode 100644 index 00000000..289f7324 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebImage.class @@ -0,0 +1,61 @@ +' Gambas class file + +Export +Inherits WebControl + +Public Const _Properties As String = "*,Border,Image{WebImage}" + +Property Image As String + +Private $sImage As String + +Public Sub _new() + + Me.Border = False + +End + + +Public Sub _BeforeRender() + + If $sImage Then + Print ""; + Else + Super._BeforeRender() + Endif + +End + +Public Sub _AfterRender() + + If Not $sImage Then Super._AfterRender() + +End + +Private Function Image_Read() As String + + Return $sImage + +End + +Private Sub Image_Write(Value As String) + + $sImage = Value + 'If $sImage Then WebForm._Preload($sImage) + Me._SetProperty("Image", Value) + ' Inc $iCpt + ' Me._SetProperty("#count", $iCpt) + +End + + +' Public Sub _InitSpecialProperty(sProp As String, vVal As Variant) +' +' If sProp = "#count" Then $iCpt = vVal +' +' End diff --git a/comp/src/gb.web.form/.src/WebLabel.class b/comp/src/gb.web.form/.src/WebLabel.class new file mode 100644 index 00000000..9d79117d --- /dev/null +++ b/comp/src/gb.web.form/.src/WebLabel.class @@ -0,0 +1,83 @@ +' Gambas class file + +Export +Inherits WebControl + +Public Const _Properties As String = "*,Text,Alignment{Align.*}=Normal,Border" +Public Const _DrawWith As String = "Label" +Public Const _DefaultSize As String = "24,4" + +Property Text As String +Property Alignment As Integer + +Private $sText As String +Private $iAlign As Integer + + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + If $sText = Value Then Return + $sText = Value + Inc Me._NoRefresh + Me._SetProperty("Text", Value) + Dec Me._NoRefresh + + If Me._CanRefresh() Then + WebForm._AddJavascript("gw.setInnerHtml(" & JS(Me.Name) & "," & JS(Replace(Html($sText), "\n", "
    ")) & ")") + Endif + +End + +Public Sub _Render() + + Print Replace(Html($sText), "\n", "
    "); + +End + +Public Sub _RenderStyleSheet() + + If $iAlign Then + + Me._StartStyleSheet + + Super._RenderStyleSheet() + + Select Case $iAlign + Case Align.Left + Me._AddStyleSheet("justify-content:flex-start;") + Case Align.Right + Me._AddStyleSheet("justify-content:flex-end;") + Case Align.Center + Me._AddStyleSheet("justify-content:center;") + End Select + + Me._EndStyleSheet + + Else + + Super._RenderStyleSheet + + Endif + + +End + + +Private Function Alignment_Read() As Integer + + Return $iAlign + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlign = Value + Me._SetProperty("Alignment", Value) + +End diff --git a/comp/src/gb.web.form/.src/WebMenu.class b/comp/src/gb.web.form/.src/WebMenu.class new file mode 100644 index 00000000..ec77f32f --- /dev/null +++ b/comp/src/gb.web.form/.src/WebMenu.class @@ -0,0 +1,133 @@ +' Gambas class file + +Export +Inherits WebContainer + +Public Const _Group As String = "Container" +Public Const _Properties As String = "*,-Arrangement,-Margin,-Spacing,-Border,Text,Image{WebImage)" +Public Const _DefaultArrangement As String = "V" +Public Const _DefaultEvent As String = "Click" + +Event Click(Target As WebControl) + +Property Text, Title As String +Property Image As String + +Private $sText As String +Private $sImage As String + +Public Sub _new() + + 'Me.Arrangement = Arrange.Vertical + +End + + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + Me._SetProperty("Text", Value) + +End + +Public Sub _BeforeRender() + + If Me.Parent Is WebMenu Then + + Print "
    "; + Print "
    "; + If $sImage Then Print ""; + Print "
    "; + Print "
    "; Html($sText); "
    "; + Print "
    "; + + Endif + + Super._BeforeRender + +End + + +Public Sub _Render() + + Dim hChild As WebControl + + If Not Me.Parent Is WebMenu Then + + Print "
    "; + If $sImage Then + Print ""; + Endif + If $sText Then + Print Html($sText); + Else + Print " "; + Endif + Print "
    " + Print "
    " + + Endif + + 'Print "
    " + Print "
    " + + For Each hChild In Me.Children + With hChild + If Not .Visible Then Continue + ._BeforeRender() + ._Render() + ._AfterRender() + End With + Next + + Print "
    "; + +End + +Public Sub _AfterRender() + + Super._AfterRender + + If Me.Parent Is WebMenu Then + + Print "
    "; + + Endif + +End + + + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + Dim hControl As WebControl + + If sProp = "#click" Then + Try hControl = WebControl.FromName(vValue) + If hControl And If hControl <> Me Then + Raise Click(hControl) + Object.Raise(hControl, "Click") + Endif + Endif + +End + + +Private Function Image_Read() As String + + Return $sImage + +End + +Private Sub Image_Write(Value As String) + + $sImage = Value + Me._SetProperty("Image", Value) + +End diff --git a/comp/src/gb.web.form/.src/WebMenuBar.class b/comp/src/gb.web.form/.src/WebMenuBar.class new file mode 100644 index 00000000..cf90d57a --- /dev/null +++ b/comp/src/gb.web.form/.src/WebMenuBar.class @@ -0,0 +1,14 @@ +' Gambas class file + +Export +Inherits WebContainer + +Public Const _Properties As String = "*,-Arrangement" +Public Const _DefaultArrangement As String = "H" +Public Const _Similar As String = "WebContainer" + +Public Sub _new() + + Me.Arrangement = Arrange.Horizontal + +End diff --git a/comp/src/gb.web.form/.src/WebMenuItem.class b/comp/src/gb.web.form/.src/WebMenuItem.class new file mode 100644 index 00000000..b9f361cb --- /dev/null +++ b/comp/src/gb.web.form/.src/WebMenuItem.class @@ -0,0 +1,70 @@ +' Gambas class file + +Export +Inherits WebControl + +Public Const _Properties As String = "*,Text,Image{WebImage},Shortcut" +'Public Const _DrawWith As String = "Label" +Public Const _DefaultSize As String = "24,4" +Public Const _DefaultEvent As String = "Click" + +Event Click + +Property Text As String +Property Image As String +Property Shortcut As String + +Private $sText As String +Private $sImage As String +Private $sShortcut As String + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + Me._SetProperty("Text", Value) + +End + +Public Sub _Render() + + Print "
    "; + If $sImage Then Print ""; + Print "
    "; + Print "
    "; Html($sText); "
    "; + Print "
    "; + If $sShortcut Then Print Html($sShortcut); + Print "
    " + +End + +Private Function Image_Read() As String + + Return $sImage + +End + +Private Sub Image_Write(Value As String) + + $sImage = Value + Me._SetProperty("Image", Value) + +End + +Private Function Shortcut_Read() As String + + Return $sShortcut + +End + +Private Sub Shortcut_Write(Value As String) + + $sShortcut = Value + Me._SetProperty("Shortcut", Value) + +End diff --git a/comp/src/gb.web.form/.src/WebProgressBar.class b/comp/src/gb.web.form/.src/WebProgressBar.class new file mode 100644 index 00000000..451f3ac3 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebProgressBar.class @@ -0,0 +1,58 @@ +' Gambas class file + +Export +Inherits WebControl + +Public Const _Properties As String = "*,Label=True,Border=True" ',Pulse" +Public Const _DefaultSize As String = "24,4" +Public Const _DrawWith As String = "ProgressBar" + +Property Label As Boolean +Property Value As Float +'Property Pulse As Boolean + +Private $bLabel As Boolean = True +Private $fValue As Float + + +Public Sub _Render() + + Print "
    "; "
    " + If $bLabel Then Print "
    "; Html(Format($fValue, "0%")); "
    " + +End + + +Private Function Label_Read() As Boolean + + Return $bLabel + +End + +Private Sub Label_Write(Value As Boolean) + + $bLabel = Value + Me._SetProperty("Label", Value) + +End + +Private Function Value_Read() As Float + + Return $fValue + +End + +Private Sub Value_Write(Value As Float) + + $fValue = Min(1, Max(0, Value)) + + Inc Me._NoRefresh + Me._SetProperty("Value", Value) + Dec Me._NoRefresh + + If Me._CanRefresh() Then + WebForm._AddJavascript("$(" & JS(Me.Name & ":p") & ").style.width = '" & CStr(CInt($fValue * 100)) & "." & CStr(CInt(Frac($fValue * 100) * 10)) & "%'") + If $bLabel Then WebForm._AddJavascript("gw.setInnerHtml(" & JS(Me.Name & ":l") & "," & JS(Html(Format($fValue, "0%"))) & ")") + Endif + +End diff --git a/comp/src/gb.web.form/.src/WebRadioButton.class b/comp/src/gb.web.form/.src/WebRadioButton.class new file mode 100644 index 00000000..668447fe --- /dev/null +++ b/comp/src/gb.web.form/.src/WebRadioButton.class @@ -0,0 +1,81 @@ +' Gambas class file + +Export +Inherits WebControl + +Public Const _Properties As String = "*,Text,Value" +Public Const _DrawWith As String = "RadioButton" +Public Const _DefaultSize As String = "24,4" +Public Const _Similar As String = "WebButton" +Public Const _DefaultEvent As String = "Click" + +Event Click + +Property Value, Checked As Boolean +Property Text As String + +Private $sText As String +Private $bChecked As Boolean + + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + If $sText = Value Then Return + $sText = Value + Me._SetProperty("Text", Value) + +End + +Public Sub _Render() + + If $sText Then Print ""; + Print + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + If sProp = "value" Then Try Value_Write(vValue) + +End + + +Private Function Value_Read() As Boolean + + Return $bChecked + +End + +Private Sub Value_Write(Value As Boolean) + + Dim hCtrl As WebControl + Dim hRadio As WebRadioButton + + If $bChecked <> Value Then + $bChecked = Value + Me._SetProperty("Value", Value) + Endif + + If $bChecked Then + For Each hCtrl In Me.Parent.Children + If hCtrl Is WebRadioButton And If hCtrl <> Me Then + hRadio = hCtrl + hRadio.Value = False + Endif + Next + Endif + + Raise Click + +End diff --git a/comp/src/gb.web.form/.src/WebScrollView.class b/comp/src/gb.web.form/.src/WebScrollView.class new file mode 100644 index 00000000..0fd8c9d3 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebScrollView.class @@ -0,0 +1,188 @@ +' Gambas class file + +Export +Inherits WebContainer + +Public Const _Similar As String = "WebContainer" +Public Const _Properties As String = "*,ScrollBar{Scroll.*}=Both" +Public Const _DrawWith As String = "ScrollView" + +Event Scroll + +Property ScrollX As Integer +Property ScrollY As Integer +Property ScrollBar As Integer + +Private $hCont As WebContainer +Private $iScrollX As Integer +Private $iScrollY As Integer +Private $iScroll As Integer = Scroll.Both + +Private $hHeaderH As WebScrollView +Private $hHeaderV As WebScrollView + +Private $sEnsureVisible As String + +Public Sub _new() + + $hCont = New WebContainer(Me) 'As Me.Name & "-contents" + Me._Container = $hCont + +End + +Public Sub _Render() + + Dim sHeaderH As String + Dim sHeaderV As String + + Print "
    "; + 'Print ""; + $hCont._Render() + 'Print "
    "; + Print "
    "; + + If $hHeaderH Then + sHeaderH = JS($hHeaderH.Name) + Else + sHeaderH = "undefined" + Endif + + If $hHeaderV Then + sHeaderV = JS($hHeaderV.Name) + Else + sHeaderV = "undefined" + Endif + + If $hHeaderH Or If $hHeaderV Then + WebForm._AddJavascript("gw.scrollview.setHeaders(" & JS(Me.Name) & "," & sHeaderH & "," & sHeaderV & ")") + Endif + + If $sEnsureVisible Then + WebForm._AddJavascript("gw.scrollview.ensureVisible(" & JS(Me.Name) & "," & JS($sEnsureVisible) & ")") + $sEnsureVisible = "" + Else + WebForm._AddJavascript("gw.table.scroll(" & JS(Me.Name) & "," & JS($iScrollX) & "," & JS($iScrollY) & "); // _Render") + Endif + +End + + +Private Function ScrollX_Read() As Integer + + Return $iScrollX + +End + +Private Sub ScrollX_Write(Value As Integer) + + If $iScrollX = Value Then Return + $iScrollX = Value + + Inc Me._NoRefresh + Me._SetProperty("ScrollX", Value) + Dec Me._NoRefresh + + If Me._CanRefresh() Then WebForm._AddJavascript("gw.table.scroll(" & JS(Me.Name) & "," & JS($iScrollX) & "," & JS($iScrollY) & "); // _ScrollX") + +End + +Private Function ScrollY_Read() As Integer + + Return $iScrollY + +End + +Private Sub ScrollY_Write(Value As Integer) + + If $iScrollY = Value Then Return + $iScrollY = Value + + Inc Me._NoRefresh + Me._SetProperty("ScrollY", Value) + Dec Me._NoRefresh + + If Me._CanRefresh() Then WebForm._AddJavascript("gw.table.scroll(" & JS(Me.Name) & "," & JS($iScrollX) & "," & JS($iScrollY) & ") // _ScrollY") + +End + +Public Sub EnsureVisible(Child As WebControl) + + Dim hParent As WebContainer + + Try hParent = Child.Parent.Parent + If hParent <> Me Then Error.Raise("Not a direct child") + + $sEnsureVisible = Child.Name + Me.Refresh + +End + +Public Sub _InitSpecialProperty(sProp As String, vVal As Variant) + + Select Case sProp + Case "#headerh" + $hHeaderH = vVal + Case "#headerv" + $hHeaderV = vVal + End Select + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + If sProp = "#scroll" Then + Inc Me._NoRefresh + Me.ScrollX = vValue[0] + Me.ScrollY = vValue[1] + Dec Me._NoRefresh + Raise Scroll + Endif + +End + + +Private Function ScrollBar_Read() As Integer + + Return $iScroll + +End + +Private Sub ScrollBar_Write(Value As Integer) + + If Value < 0 Or If Value > 3 Then Error.Raise("Bad argument") + $iScroll = Value + Me._SetProperty("ScrollBar", Value) + +End + +Public Sub SetHeaders(Horizontal As WebScrollView, Vertical As WebScrollView) + + $hHeaderH = Horizontal + $hHeaderV = Vertical + + Me._SetProperty("#headerh", Horizontal) + Me._SetProperty("#headerv", Vertical) + +End diff --git a/comp/src/gb.web.form/.src/WebSeparator.class b/comp/src/gb.web.form/.src/WebSeparator.class new file mode 100644 index 00000000..e1319a4a --- /dev/null +++ b/comp/src/gb.web.form/.src/WebSeparator.class @@ -0,0 +1,18 @@ +' Gambas class file + +Export +Inherits WebControl + +Public Const _Properties As String = "*" +Public Const _DrawWith As String = "Separator" +Public Const _DefaultSize As String = "24,4" + +Public Sub _Render() + + If Me.Parent.Arrangement = Arrange.Horizontal Then + Print "
    " + Else + Print "
    " + Endif + +End diff --git a/comp/src/gb.web.form/.src/WebSlider.class b/comp/src/gb.web.form/.src/WebSlider.class new file mode 100644 index 00000000..b64b6c84 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebSlider.class @@ -0,0 +1,21 @@ +' Gambas class file + +Export +Inherits WebSpinBox + +Public Const _DrawWith As String = "Slider" +Public Const _DefaultSize As String = "24,4" + +Public Sub _BeforeRender() + + Print " 1 Then Print " step=\""; CStr(Me.Step); "\""; + Print ">" + +End diff --git a/comp/src/gb.web.form/.src/WebSpinBox.class b/comp/src/gb.web.form/.src/WebSpinBox.class new file mode 100644 index 00000000..61de73b3 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebSpinBox.class @@ -0,0 +1,123 @@ +' Gambas class file + +Export +Inherits WebControl + +Public Const _Properties As String = "*,Border=True,MinValue=0,MaxValue=100,Step=1,Value=0" +Public Const _DrawWith As String = "SpinBox" +Public Const _DefaultSize As String = "24,4" +Public Const _DefaultEvent As String = "Change" + +Event Change + +Property Value As Integer +Property MinValue As Integer +Property MaxValue As Integer +Property Step As Integer +Property Read Text As String + +Private $iVal As Integer +Private $iMinVal As Integer = 0 +Private $iMaxVal As Integer = 100 +Private $iStep As Integer = 1 + +Private Function Text_Read() As String + + Return CStr($iVal) + +End + + +Public Sub _BeforeRender() + + Print " 1 Then Print " step=\""; CStr($iStep); "\""; + Print ">" + +End + + +Public Sub _AfterRender() + + 'Raise Render + 'Print "" + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + If sProp = "value" Then Try Me.Value = vValue + +End + + +Private Function Value_Read() As Integer + + Return $iVal + +End + +Private Sub Value_Write(Value As Integer) + + If Value < $iMinVal Then Return + If Value > $iMaxVal Then Return + + If $iVal = Value Then Return + + $iVal = Value + Me._SetProperty("Value", Value) + Raise Change + +End + +Private Function MinValue_Read() As Integer + + Return $iMinVal + +End + +Private Sub MinValue_Write(Value As Integer) + + If Value = $iMinVal Then Return + $iMinVal = Value + Me._SetProperty("MinValue", Value) + + Me.Value = Max(Value, Me.Value) + +End + +Private Function MaxValue_Read() As Integer + + Return $iMaxVal + +End + +Private Sub MaxValue_Write(Value As Integer) + + If Value = $iMaxVal Then Return + $iMaxVal = Value + Me._SetProperty("MaxValue", Value) + + Me.Value = Min(Value, Me.Value) + +End + +Private Function Step_Read() As Integer + + Return $iStep + +End + +Private Sub Step_Write(Value As Integer) + + If Value = $iStep Then Return + $iStep = Value + Me._SetProperty("Step", Value) + +End diff --git a/comp/src/gb.web.form/.src/WebTabPanel.class b/comp/src/gb.web.form/.src/WebTabPanel.class new file mode 100644 index 00000000..87e33deb --- /dev/null +++ b/comp/src/gb.web.form/.src/WebTabPanel.class @@ -0,0 +1,193 @@ +' Gambas class file + +Export +Inherits WebContainer + +Public Const _Properties As String = "*,Border=True,Count{Range:1;256}=1,Index,Text,ShowTabs=True" +Public Const _IsMultiContainer As Boolean = True +Public Const _DrawWith As String = "TabPanel" +Public Const _DefaultEvent As String = "Click" + +Event Click + +Property Count As Integer +Property Text As String +Property Index As Integer +Property Border As Boolean +Property ShowTabs As Boolean + +Private $aTab As New WebContainer[] +Private $aText As New String[] + +Private $iIndex As Integer +Private $bBorder As Boolean = True +Private $bHideTabs As Boolean + +Public Sub _new() + + Count_Write(1) + Index_Write(0) + +End + + +Private Function Count_Read() As Integer + + Return $aTab.Count + +End + +Private Sub Count_Write(Value As Integer) + + Dim iOldSize As Integer + Dim I As Integer + Dim hContainer As WebContainer + Dim sName As String + + If Value < 1 Or If Value > 256 Then Error.Raise("Bad argument") + + iOldSize = $aTab.Count + $aTab.Resize(Value) + $aText.Resize(Value) + + hContainer = Me._Container + Me._Container = Null + + For I = iOldSize To $aTab.Max + sName = Me.Name & "." & CStr(I) + WebControl._NextName = sName + $aTab[I] = New WebContainer(Me) + Object.Attach($aTab[I], Object.Parent(Me), sName) + $aText[I] = "Tab #" & CStr(I) + Next + + Me._Container = hContainer + + Me._SetProperty("Count", $aTab.Count) + +End + +Private Function Text_Read() As String + + Return $aText[$iIndex] + +End + +Public Sub _InitSpecialProperty(sProp As String, vVal As Variant) + + If sProp Begins "#Text[" Then + $aText[CInt(Mid$(sProp, 7, -1))] = vVal + Me.Refresh + Endif + +End + + +Private Sub Text_Write(Value As String) + + $aText[$iIndex] = Value + Me._SetProperty("#Text[" & CStr($iIndex) & "]", Value) + +End + +Private Function Index_Read() As Integer + + Return $iIndex + +End + +Private Sub Index_Write(Value As Integer) + + Dim I As Integer + + If Value < 0 Or If Value >= $aTab.Count Then Error.Raise("Bad argument") + $iIndex = Value + Me._SetContainer($aTab[Value]) + For I = 0 To $aTab.Max + $aTab[I].Visible = I = $iIndex + Next + Me._SetProperty("Index", Value) + + Raise Click + +End + + +Public Sub _Render() + + Dim I As Integer + + If Not $bHideTabs Then + + Print "
    " + For I = 0 To $aTab.Max + Print "
    $iIndex Then Print Me._GetUpdateWaitJS("onclick", "index", CStr(I)); + Print ">"; $aText[I]; "
    " + Next + Print "
    " + + Endif + + Print "
    " + + With $aTab[$iIndex] + ._BeforeRender() + ._Render() + ._AfterRender() + End With + + Print "
    " + +End + +Public Sub _RenderStyleSheet() + + Me._StartStyleSheet + + Super._RenderStyleSheet() + + Me._AddStyleSheet("display:flex;") + Me._AddStyleSheet("flex-flow:column;") + + Me._EndStyleSheet + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + If sProp = "index" Then Try Me.Index = vValue + +End + + +Private Function Border_Read() As Boolean + + Return $bBorder + +End + +Private Sub Border_Write(Value As Boolean) + + $bBorder = Value + Me._SetProperty("Border", Value) + Me.Refresh + +End + +Private Function ShowTabs_Read() As Boolean + + Return Not $bHideTabs + +End + +Private Sub ShowTabs_Write(Value As Boolean) + + $bHideTabs = Not Value + Me._SetProperty("ShowTabs", Value) + +End diff --git a/comp/src/gb.web.form/.src/WebTable/WebTable.class b/comp/src/gb.web.form/.src/WebTable/WebTable.class new file mode 100644 index 00000000..ef9fe29d --- /dev/null +++ b/comp/src/gb.web.form/.src/WebTable/WebTable.class @@ -0,0 +1,644 @@ +' Gambas class file + +Export +Inherits WebControl + +Public Const _Properties As String = "*,Border=True,Mode{Select.*}=None,ShowCheck=True,ShowHeader=True,Sortable" +Public Const _DrawWith As String = "GridView" +Public Const _DefaultSize As String = "24,24" +Public Const _DefaultEvent As String = "Data" + +Event Data(Row As Integer, Column As Integer, Data As WebTableData) +Event Select +Event DblClick +Event Sort + +Property Read Columns As _WebTableColumns +Property Count As Integer +Property Read Max As Integer +Property Mode As Integer +Property Read Selection As Integer[] +Property Current As Integer +Property Display As Integer +Property Step As Integer +Property ScrollX As Integer +Property ScrollY As Integer +Property ShowCheck As Boolean +Property ShowHeader As Boolean +Property Sortable As Boolean +Property SortColumn As Integer +Property SortOrder As Integer + +'Property Fixed As Boolean + +Private $hColumns As _WebTableColumns +Private $iCount As Integer +Private $iMode As Integer +Private $hSelection As WebTableSelection +Private $iStep As Integer = 100 +Private $iDisplay As Integer = 100 + +Private $iScrollX As Integer +Private $iScrollY As Integer +Private $bNoScrolling As Boolean +Private $bNoCheck As Boolean +Private $bNoHeader As Boolean +Private $iEnsureVisible As Integer = -1 +Private $bSortable As Boolean +Private $iSortColumn As Integer = -1 +Private $bSortDescent As Boolean + +Public Sub _new() + + Me._Proxy = ":table" + $hColumns = New _WebTableColumns As "Columns" + +End + + +Private Function Columns_Read() As _WebTableColumns + + Return $hColumns + +End + +Private Sub PrintRow(iRow As Integer) + + Dim iCol As Integer + Dim hCol As _WebTableColumn + Dim hData As WebTableData + Dim sStyle As String + Dim NX As Integer + + If $iMode And If Not $bNoCheck Then + 'Print " Select.Single Then Print ",event"; + 'Print ")\">"; + Print ""; + If $iMode = Select.Single Then + Print ""; + Print ""; + Endif + + If $bNoHeader Then + For iCol = 0 To $hColumns.Count - 1 + If $hColumns[iCol].Expand Then Inc NX + Next + Endif + + For iCol = 0 To $hColumns.Count - 1 + + hCol = $hColumns[iCol] + If hCol.Hidden Then Continue + + hData = New WebTableData + Raise Data(iRow, iCol, hData) + + Print " Color.Default Then sStyle &= "background-color:" & WebControl._GetColor(hData.Background) & ";" + If hData.Foreground <> Color.Default Then sStyle &= "color:" & WebControl._GetColor(hData.Foreground) & ";" + If sStyle Then Print " style=\""; sStyle; "\""; + Print ">"; + If hData.Html Then + Print hData.Html; + Else + Print Html(hData.Text); + Endif + + Next + + +End + +Private Sub PrintBody(iStart As Integer, iEnd As Integer) + + Dim iRow As Integer + + Print "" + For iRow = iStart To iEnd + + Print " Select.Single Then Print ",event"; + Print ");\""; + + Print Me._GetEventJS("ondblclick", "DblClick"); + + Endif + Print ">"; + + PrintRow(iRow) + + Print "" + Next + + Print "" + + If iRow < $iCount Then Print "
    " + +End + + +Public Sub _Render() + + If $hColumns.Count = 0 Then Return + + Print "
    "; + 'If $bFixed Then + ' Print "" + 'Else + Print "
    " + 'Endif + + If Not $bNoHeader Then $hColumns._Render() + + PrintBody(0, Min($iDisplay, $iCount) - 1) + + Print "
    " + + ' If iRow < $iCount Then + ' Print "
    " + ' Endif + + Print "
    " + + If $bNoScrolling Then + $bNoScrolling = False + Else + If $iEnsureVisible > 0 Then + WebForm._AddJavascript("gw.table.ensureVisible(" & JS(Me.Name) & "," & CStr($iEnsureVisible) & ")") + $iEnsureVisible = -1 + Else + WebForm._AddJavascript("gw.table.scroll(" & JS(Me.Name) & "," & JS($iScrollX) & "," & JS($iScrollY) & ")") + Endif + Endif + + If $iMode = Select.Single Then WebForm._AddJavascript("$(" & JS(Me.Name) & ").gw_current = " & Current_Read()) + +End + + +Private Function Count_Read() As Integer + + Return $iCount + +End + +Private Sub Count_Write(Value As Integer) + + If Value < 0 Then Error.Raise("Bad argument") + $iCount = Value + Me._SetProperty("Count", Value) + UnselectAll + 'Me.Display = 100 + +End + +Private Function Max_Read() As Integer + + Return $iCount - 1 + +End + +Private Function Mode_Read() As Integer + + Return $iMode + +End + +Private Sub Mode_Write(Value As Integer) + + If $iMode = Value Then Return + $iMode = Value + Me._SetProperty("Mode", Value) + SetSelection + +End + +Public Sub _InitSpecialProperty(sProp As String, vVal As Variant) + + Dim aProp As String[] + + If sProp = "#count" Then + $hColumns.Count = vVal + Else If sProp = "#selection" Then + $hSelection.SetSelection(vVal[1], vVal[0]) + ' Else If sProp = "#scroll" Then + ' $iScrollX = vVal[0] + ' $iScrollY = vVal[1] + Else + aProp = Scan(sProp, "#\\[*].*") + Object.SetProperty($hColumns[CInt(aProp[0])], aProp[1], vVal) + Endif + + Me.Refresh + +End + +Private Sub SetSelection() + + If $iMode = Select.None Then + $hSelection = Null + 'Me._SetProperty("#selection", Null) + Else + If Not $hSelection Then $hSelection = New WebTableSelection As "TableSelection" + '$hSelection.SetProperty() + Endif + +End + + +Public Sub SelectAll() + + If $hSelection Then + $hSelection.SelectAll + Raise Select + Endif + +End + +Public Sub UnselectAll() + + If $hSelection Then + $hSelection.UnselectAll + Raise Select + Endif + +End + +Public Sub Select(Row As Integer, Optional Length As Integer = 1) + + If $hSelection Then + + If $iMode = Select.Single Then + + $hSelection.UnselectAll + $hSelection.Select(Row) + + Else + + $hSelection.Select(Row, Length) + + Endif + + Raise Select + + Endif + +End + +Public Sub Unselect(Row As Integer, Optional Length As Integer = 1) + + If $hSelection Then + $hSelection.Unselect(Row, Length) + Raise Select + Endif + +End + +Public Sub IsSelected(Row As Integer) As Boolean + + If $hSelection Then Return $hSelection.IsSelected(Row) + +End + +' Public Sub SetFocus() +' +' WebForm._AddReply("gw.setFocus(" & JS(Me.Name & ":table") & ");") +' +' End + +Private Sub SendMoreJavascript() + + Dim hFile As File + Dim sResult As String + + hFile = Open String For Write + Output To #hFile + + PrintBody($iDisplay, Min($iDisplay + $iStep, $iCount) - 1) + + sResult = Close #hFile + Output To Default + + WebForm._AddJavascript("$(" & JS(Me.Name & ":more") & ").outerHTML = " & JS(sResult)) + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + Dim iRow As Integer + Dim iCol As Integer + Dim aProp As String[] + Dim iLength As Integer + + If Left(sProp) = "!" Then + + If sProp = "!!" Then + If vValue Then + SelectAll + Else + UnselectAll + Endif + Else + aProp = Split(Mid$(sProp, 2), ":") + iRow = CInt(aProp[0]) + If aProp.Count = 2 Then + iLength = CInt(aProp[1]) + Else + iLength = 1 + Endif + Inc Me._NoRefresh + If vValue Then + Try Select(iRow, iLength) + Else + Try Unselect(iRow, iLength) + Endif + Dec Me._NoRefresh + Endif + + Else If Left(sProp) = "?" Then + + UnselectAll + iRow = CInt(Mid(sProp, 2)) + Try Select(iRow) + + Else If Left(sProp) = "$" Then + + Inc Me._NoRefresh + iRow = CInt(Mid(sProp, 2)) + If IsSelected(iRow) Then + If $iMode = Select.Multiple Then Try Unselect(iRow) + Else + Try Select(iRow) + Endif + Dec Me._NoRefresh + + Else If sProp = "#more" Then + + Inc Me._NoRefresh + If $iDisplay < $iCount Then + SendMoreJavascript() + Me.Display += $iStep + Me.SetFocus + Endif + Me.ScrollX = vValue[0] + Me.ScrollY = vValue[1] + Dec Me._NoRefresh + + Else If sProp = "#scroll" Then + + Inc Me._NoRefresh + Me.ScrollX = vValue[0] + Me.ScrollY = vValue[1] + Dec Me._NoRefresh + + Else If sProp = "#sort" Then + + iCol = CInt(vValue) + If iCol = $iSortColumn Then + If $bSortDescent Then + Me.SortColumn = -1 + Else + Me.SortOrder = gb.Descent + Endif + Else + Me.SortColumn = iCol + Me.SortOrder = gb.Ascent + Endif + + Endif + +End + +Public Sub _IsEverythingSelected() As Boolean + + If $hSelection Then Return $hSelection.IsEverythingSelected() + +End + +Private Function Selection_Read() As Integer[] + + If $hSelection Then + Return $hSelection.GetSelectedRows() + Else + Return New Integer[] + Endif + +End + +Private Function Display_Read() As Integer + + Return $iDisplay + +End + +Private Sub Display_Write(Value As Integer) + + $iDisplay = Value + Me._SetProperty("Display", Value) + +End + +Private Function ScrollX_Read() As Integer + + Return $iScrollX + +End + +Private Sub ScrollX_Write(Value As Integer) + + $iScrollX = Value + Me._SetProperty("ScrollX", Value) + +End + +Private Function ScrollY_Read() As Integer + + Return $iScrollY + +End + +Private Sub ScrollY_Write(Value As Integer) + + $iScrollY = Value + Me._SetProperty("ScrollY", Value) + +End + +' Private Function Fixed_Read() As Boolean +' +' Return $bFixed +' +' End +' +' Private Sub Fixed_Write(Value As Boolean) +' +' $bFixed = Value +' Me._SetProperty("Fixed", Value) +' +' End + +Public Sub Clear() + + Me.Count = 0 + Me.Display = 100 + Me.ScrollX = 0 + Me.ScrollY = 0 + +End + +Private Function ShowCheck_Read() As Boolean + + Return Not $bNoCheck + +End + +Private Sub ShowCheck_Write(Value As Boolean) + + $bNoCheck = Not Value + Me._SetProperty("ShowCheck", Value) + +End + +Private Function Step_Read() As Integer + + Return $iStep + +End + +Private Sub Step_Write(Value As Integer) + + $iStep = Value + Me._SetProperty("Step", $iStep) + +End + +Private Function Current_Read() As Integer + + If $hSelection Then + Return $hSelection.GetCurrent() + Else + Return -1 + Endif + +End + +Private Sub Current_Write(Value As Integer) + + If $iMode = Select.None Then Return + UnselectAll + Select(Value) + +End + +Private Function ShowHeader_Read() As Boolean + + Return Not $bNoHeader + +End + +Private Sub ShowHeader_Write(Value As Boolean) + + $bNoHeader = Not Value + Me._SetProperty("ShowHeader", Value) + +End + +Public Sub AddColumn(Text As String, Optional Width As String, Alignment As Integer = Align.Normal) As _WebTableColumn + + Dim hCol As _WebTableColumn + + Inc Me.Columns.Count + + hCol = Me.Columns[Me.Columns.Count - 1] + hCol.Text = Text + If Width = "*" Then + hCol.Expand = True + Else If Width Then + hCol.Width = Width + Endif + hCol.Alignment = Alignment + + Return hCol + +End + +Public Sub EnsureVisible(Row As Integer) + + If Row < 0 Or If Row >= $iCount Then Return + + Me.Display = Min($iCount, Max($iStep, ((Row + $iStep - 1) \ $iStep) * $iStep)) + Me.Refresh + $iEnsureVisible = Row + +End + +Private Function Sortable_Read() As Boolean + + Return $bSortable + +End + +Private Sub Sortable_Write(Value As Boolean) + + $bSortable = Value + Me._SetProperty("Sortable", Value) + +End + +Private Function SortColumn_Read() As Integer + + Return $iSortColumn + +End + +Private Sub SortColumn_Write(Value As Integer) + + If $iSortColumn = Value Then Return + $iSortColumn = Value + Me._SetProperty("SortColumn", Value) + Raise Sort + +End + +Private Function SortOrder_Read() As Integer + + Return If($bSortDescent, gb.Descent, gb.Ascent) + +End + +Private Sub SortOrder_Write(Value As Integer) + + If $bSortDescent = (Value = gb.Descent) Then Return + $bSortDescent = Value = gb.Descent + Me._SetProperty("SortOrder", Value) + Raise Sort + +End diff --git a/comp/src/gb.web.form/.src/WebTable/WebTableData.class b/comp/src/gb.web.form/.src/WebTable/WebTableData.class new file mode 100644 index 00000000..265aa8e7 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebTable/WebTableData.class @@ -0,0 +1,8 @@ +' Gambas class file + +Export + +Public Text As String +Public Html As String +Public Background As Integer = Color.Default +Public Foreground As Integer = Color.Default diff --git a/comp/src/gb.web.form/.src/WebTable/WebTableSelection.class b/comp/src/gb.web.form/.src/WebTable/WebTableSelection.class new file mode 100644 index 00000000..ebba52b8 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebTable/WebTableSelection.class @@ -0,0 +1,296 @@ +' Gambas class file + +Property Read Count As Integer + +Private $aSel As New Integer[] +Private $bInvert As Boolean + +Event _Fake + +' Private Sub Dump() +' +' Dim I As Integer +' ' +' ' Print System.Backtrace.Join(" ") +' ' +' Print "Selection = "; +' For I = 0 To $aSel.Max Step 2 +' Print "["; $aSel[I]; ","; $aSel[I + 1]; "] "; +' Next +' Print +' +' End +' + +Private Sub GetTable() As WebTable + + Return Object.Parent(Me) + +End + +Public Sub SetProperty() + + GetTable()._SetProperty("#selection", [$bInvert, $aSel]) + +End + + +Public Sub SetSelection(aSel As Integer[], bInvert As Boolean) + + $aSel = aSel + $bInvert = bInvert + +End + +Public Sub Copy() As WebTableSelection + + Dim hSel As WebTableSelection = New WebTableSelection + + hSel.SetSelection($aSel.Copy(), $bInvert) + Return hSel + +End + +Private Sub Add(iStart As Integer, Optional iLength As Integer = 1) + + Dim I, S, L As Integer + + For I = 0 To $aSel.Max Step 2 + + S = $aSel[I] + L = $aSel[I + 1] + + If iStart >= S And If iStart <= (S + L) Then + $aSel[I + 1] = iStart + iLength - S + 'Dump + Return + Else If (iStart + iLength) >= S And If (iStart + iLength) <= (S + L) Then + $aSel[I + 1] += $aSel[I] - iStart + $aSel[I] = iStart + 'Dump + Return + Endif + + Next + + $aSel.Add(iStart) + $aSel.Add(iLength) + 'Dump + +End + +Private Sub Remove(iStart As Integer, Optional iLength As Integer = 1) + + Dim I, S, L As Integer + + I = 0 + While I < $aSel.Count + + S = $aSel[I] + L = $aSel[I + 1] + + If (iStart + iLength) > S And If iStart < (S + L) Then + + If iStart <= S And If (iStart + iLength) >= (S + L) Then + $aSel.Remove(I, 2) + Continue + Endif + + If iStart <= S Then + $aSel[I + 1] -= iStart + iLength - S + $aSel[I] = iStart + iLength + Else If (iStart + iLength) >= (S + L) Then + $aSel[I + 1] = iStart - S + Else + $aSel[I + 1] = iStart - S + $aSel.Add(iStart + iLength) + $aSel.Add(S + L - iStart - iLength) + Endif + + Endif + + I += 2 + Wend + 'Dump + +End + +Public Sub Select(iStart As Integer, Optional iLength As Integer = 1) + + If $bInvert Then + Remove(iStart, iLength) + Else + Add(iStart, iLength) + Endif + SetProperty + +End + +Public Sub Unselect(iStart As Integer, Optional iLength As Integer = 1) + + If $bInvert Then + Add(iStart, iLength) + Else + Remove(iStart, iLength) + Endif + SetProperty + +End + + +Public Sub SelectAll() + + $aSel.Clear + $bInvert = True + SetProperty + +End + +Public Sub UnselectAll() + + $aSel.Clear + $bInvert = False + SetProperty + +End + +Public Sub IsSelected(iIndex As Integer) As Boolean + + Dim I, S, L As Integer + + For I = 0 To $aSel.Max Step 2 + + S = $aSel[I] + L = $aSel[I + 1] + + If iIndex >= S And iIndex < (S + L) Then Return Not $bInvert + + Next + + Return $bInvert + +End + +Public Function GetSelectedRows() As Integer[] + + Dim aRet As New Integer[] + Dim I, J, S, L As Integer + + If $bInvert Then + + For I = 0 To GetTable().Max + If IsSelected(I) Then aRet.Add(I) + Next + + Else + + For I = 0 To $aSel.Max Step 2 + S = $aSel[I] + L = $aSel[I + 1] + + For J = 0 To L - 1 + aRet.Add(S + J) + Next + Next + + Endif + + Return aRet.Sort() + +End + +Private Sub Count_Read() As Integer + + Dim I, N As Integer + + For I = 0 To $aSel.Max Step 2 + N += $aSel[I + 1] + Next + + If $bInvert Then + + Return GetTable().Count - N + + Else + + Return N + + Endif + +End + + + +Public Sub IsEverythingSelected() As Boolean + + If $bInvert And $aSel.Count = 0 Then Return True + +End + +' Public Sub InsertRows(iStart As Integer, iLength As Integer) +' +' Dim I, S, L As Integer +' +' For I = 0 To $aSel.Max Step 2 +' +' S = $aSel[i] +' L = $aSel[I + 1] +' +' If iStart <= S Then +' $aSel[I] += iLength +' Else If iStart <= (S + L) Then +' $aSel[I + 1] = iStart - S +' $aSel.Add(iStart + iLength) +' $aSel.Add(L - (iStart - S)) +' Endif +' +' Next +' +' 'Dump +' +' End +' +' Public Sub RemoveRows(iStart As Integer, iLength As Integer) +' +' Dim I, S, L As Integer +' +' I = 0 +' While I < $aSel.Count +' +' S = $aSel[I] +' L = $aSel[I + 1] +' +' If (iStart + iLength) <= S Then +' $aSel[I] -= iLength +' Else If iStart >= (S + L) Then +' Else +' If iStart < S Then +' iLength -= (S - iStart) +' $aSel[I] = iStart +' Endif +' $aSel[I + 1] -= iLength - (iStart - S) +' Endif +' +' If $aSel[I + 1] <= 0 Then +' $aSel.Remove(I, 2) +' Else +' I += 2 +' Endif +' +' Wend +' +' 'Dump +' +' End + +Public Sub GetCurrent() As Integer + + Dim iCurrent As Integer + + If $aSel.Count = 2 Then + iCurrent = $aSel[0] + If iCurrent < GetTable().Count Then Return iCurrent + Endif + Return -1 + +End diff --git a/comp/src/gb.web.form/.src/WebTable/_WebTableColumn.class b/comp/src/gb.web.form/.src/WebTable/_WebTableColumn.class new file mode 100644 index 00000000..73db8a29 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebTable/_WebTableColumn.class @@ -0,0 +1,136 @@ +' Gambas class file + +Export + +Property Text As String +Property Expand As Boolean +Property Alignment As Integer +Property Wrap As Boolean +Property Width As String +Property Hidden As Boolean +Property Sortable As Boolean + +Event _Fake + +Public _Column As Integer + +Private $sText As String +Private $bExpand As Boolean +Private $iAlignment As Integer +Private $bNoWrap As Boolean +Private $sWidth As String +Private $bHidden As Boolean +Private $bNotSortable As Boolean + +Private Sub GetTable() As WebTable + + Return Object.Parent(Object.Parent(Me)) + +End + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + GetTable()._SetProperty("#[" & CStr(_Column) & "].text", Value) + +End + +Private Function Expand_Read() As Boolean + + Return $bExpand + +End + +Private Sub Expand_Write(Value As Boolean) + + $bExpand = Value + GetTable()._SetProperty("#[" & CStr(_Column) & "].expand", Value) + +End + +Private Function Alignment_Read() As Integer + + Return $iAlignment + +End + +Private Sub Alignment_Write(Value As Integer) + + $iAlignment = Value + GetTable()._SetProperty("#[" & CStr(_Column) & "].alignment", Value) + +End + +Public Sub _PrintAlignment() + + Select Case $iAlignment + Case Align.Left + Print " align=\"left\""; + Case Align.Center + Print " align=\"center\""; + Case Align.Right + Print " align=\"right\""; + Case Align.Normal + End Select + + If $bNoWrap Then Print " nowrap"; + +End + +Private Function Wrap_Read() As Boolean + + Return Not $bNoWrap + +End + +Private Sub Wrap_Write(Value As Boolean) + + $bNoWrap = Not Value + GetTable()._SetProperty("#[" & CStr(_Column) & "].wrap", Value) + +End + +Private Function Width_Read() As String + + Return $sWidth + +End + +Private Sub Width_Write(Value As String) + + $sWidth = Value + GetTable()._SetProperty("#[" & CStr(_Column) & "].width", Value) + +End + +Private Function Hidden_Read() As Boolean + + Return $bHidden + +End + +Private Sub Hidden_Write(Value As Boolean) + + $bHidden = Value + GetTable()._SetProperty("#[" & CStr(_Column) & "].hidden", Value) + +End + +Private Function Sortable_Read() As Boolean + + Return Not $bNotSortable + +End + +Private Sub Sortable_Write(Value As Boolean) + + $bNotSortable = Not Value + GetTable()._SetProperty("#[" & CStr(_Column) & "].sortable", Value) + +End diff --git a/comp/src/gb.web.form/.src/WebTable/_WebTableColumns.class b/comp/src/gb.web.form/.src/WebTable/_WebTableColumns.class new file mode 100644 index 00000000..b1fe1611 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebTable/_WebTableColumns.class @@ -0,0 +1,115 @@ +' Gambas class file + +Export + +Property Count As Integer + +Event _Fake + +Private $aColumns As New _WebTableColumn[] + +Private Sub GetTable() As WebTable + + Return Object.Parent(Me) + +End + + +Private Function Count_Read() As Integer + + Return $aColumns.Count + +End + +Private Sub Count_Write(Value As Integer) + + Dim iOldCount As Integer = $aColumns.Count + Dim I As Integer + + If Value = iOldCount Then Return + + Try $aColumns.Resize(Value) + If Error Then Error.Raise("Bad argument") + + If Value > iOldCount Then + For I = iOldCount To Value - 1 + $aColumns[I] = New _WebTableColumn As "Column" + $aColumns[I]._Column = I + Next + Endif + + GetTable()._SetProperty("#count", Value) + +End + +Public Sub _Render() + + Dim hTable As WebTable = GetTable() + Dim hCol As _WebTableColumn + Dim iMode As Integer + Dim NX As Integer + Dim bSortable As Boolean = hTable.Sortable + Dim iSortColumn As Integer = hTable.SortColumn + Dim I As Integer + Dim bSort As Boolean + + Print ""; + + iMode = hTable.Mode + If iMode And If hTable.ShowCheck Then + Print "
    "; + If iMode = Select.Multiple Then + Print "" + Endif + Print "
    " + Endif + + For Each hCol In $aColumns + If hCol.Expand Then Inc NX + Next + + For I = 0 To $aColumns.Max + + hCol = $aColumns[I] + If hCol.Hidden Then Continue + + If bSortable And If hCol.Sortable Then + bSort = True + Else + bSort = False + Endif + + Print ""; + If bSort And If I = iSortColumn Then + Print ""; + Endif + Print ""; + Print Html(hCol.Text); + Print "
    " + Next + + Print "" + +End + +Public Sub _get(Index As Integer) As _WebTableColumn + + Return $aColumns[Index] + +End diff --git a/comp/src/gb.web.form/.src/WebTextArea.class b/comp/src/gb.web.form/.src/WebTextArea.class new file mode 100644 index 00000000..078b4dc6 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebTextArea.class @@ -0,0 +1,147 @@ +' Gambas class file + +Export +Inherits WebControl + +Public Const _Properties As String = "*,Border=True,ReadOnly,Wrap,PlaceHolder,Text" +Public Const _DrawWith As String = "TextArea" +Public Const _DefaultSize As String = "16,16" + +Event Change + +Property Text As String +Property ReadOnly As Boolean +Property Wrap As Boolean +Property PlaceHolder As String + +Private $sText As String +Private $bReadOnly As Boolean +Private $bWrap As Boolean +Private $sPlaceHolder As String + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub SetText(Value As String, Optional bNoRefresh As Boolean) + + If $sText = Value Then Return + + $sText = Value + If bNoRefresh Then Inc Me._NoRefresh + Me._SetProperty("Text", Value) + If bNoRefresh Then Dec Me._NoRefresh + + Raise Change + +End + +Private Sub Text_Write(Value As String) + + SetText(Value) + +End + +Public Sub _RenderStyleSheet() + + Me._StartStyleSheet + + Super._RenderStyleSheet() + + If Not $bWrap Then Me._AddStyleSheet("white-space:pre;") + + Me._EndStyleSheet + +End + + +Public Sub _BeforeRender() + + Print ""; + +End + +Public Sub _Render() + + Print Html($sText); + +End + + +Public Sub _AfterRender() + + Print "" + +End + +Private Function ReadOnly_Read() As Boolean + + Return $bReadOnly + +End + +Private Sub ReadOnly_Write(Value As Boolean) + + $bReadOnly = Value + Me._SetProperty("ReadOnly", Value) + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + If sProp = "text" Then + SetText(vValue, False) + Else If sProp = "change" Then + SetText(vValue, True) + Endif + +End + + +Private Function Wrap_Read() As Boolean + + Return $bWrap + +End + +Private Sub Wrap_Write(Value As Boolean) + + $bWrap = Value + Me._SetProperty("Wrap", Value) + +End + +Private Function PlaceHolder_Read() As String + + Return $sPlaceHolder + +End + +Private Sub PlaceHolder_Write(Value As String) + + $sPlaceHolder = Value + Me._SetProperty("PlaceHolder", Value) + +End + +Public Sub Clear() + + Text_Write("") + +End + +Public Sub Copy() + + WebForm._AddJavascript("gw.textarea.copy(" & JS(Me.Name) & ")") + +End + + diff --git a/comp/src/gb.web.form/.src/WebTextBox.class b/comp/src/gb.web.form/.src/WebTextBox.class new file mode 100644 index 00000000..394a3f96 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebTextBox.class @@ -0,0 +1,187 @@ +' Gambas class file + +Export +Inherits WebControl + +Public Const _Properties As String = "*,Border=True,ReadOnly,PlaceHolder,Text,Password,ShowClear" +Public Const _DrawWith As String = "TextBox" +Public Const _DefaultSize As String = "24,4" +Public Const _DefaultEvent As String = "Change" + +Event Change +Event Completion(Text As String) +Event Activate +Event Clear + +Property Text As String +Property ReadOnly As Boolean +Property PlaceHolder As String +Property Password As Boolean +Property ShowClear As Boolean + +'Property Button As String + +Private $sText As String +Private $bReadOnly As Boolean +Private $sPlaceHolder As String +Private $bPassword As Boolean +Private $bShowClear As Boolean +'Private $sButton As String + +Public Sub _new() + + Me._Proxy = ":entry" + +End + + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub SetText(Value As String, Optional bNoRefresh As Boolean) + + If $sText = Value Then Return + + $sText = Value + + Inc Me._NoRefresh + Me._SetProperty("Text", Value) + Dec Me._NoRefresh + + If Not bNoRefresh And If Me._CanRefresh() Then WebForm._AddJavascript("$(" & JS(Me.Name & ":entry") & ").value = " & JS($sText)) + + Raise Change + +End + + +Private Sub Text_Write(Value As String) + + SetText(Value) + +End + +Public Sub Clear() + + Text_Write("") + +End + +Public Sub Copy() + + WebForm._AddJavascript("gw.textbox.copy(" & JS(Me.Name) & ")") + +End + +Public Sub _Render() + + If $bShowClear Then + If Object.CanRaise(Me, "Clear") Then + Print "
    "; + Else + Print "
    "; + Endif + Endif + + Print "" + + If Object.CanRaise(Me, "Completion") Then + WebForm._AddJavascript("gw.autocomplete(" & JS(Me.Name) & ")") + Endif + +End + +Private Function ReadOnly_Read() As Boolean + + Return $bReadOnly + +End + +Private Sub ReadOnly_Write(Value As Boolean) + + $bReadOnly = Value + Me._SetProperty("ReadOnly", Value) + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + If sProp = "text" Then + SetText(vValue) + Else If sProp = "change" + SetText(vValue, True) + Endif + +End + +Public Sub CompleteWith(CompletionList As String[]) + + WebForm._AddReply("gw.autocompletion = " & JSON.Encode(CompletionList)) + +End + +Private Function PlaceHolder_Read() As String + + Return $sPlaceHolder + +End + +Private Sub PlaceHolder_Write(Value As String) + + $sPlaceHolder = Value + Me._SetProperty("PlaceHolder", Value) + +End + +' Private Function Button_Read() As String +' +' Return $sButton +' +' End +' +' Private Sub Button_Write(Value As String) +' +' If $sButton = Value Then Return +' $sButton = Value +' Me._SetProperty("Button", Value) +' +' End + +Private Function Password_Read() As Boolean + + Return $bPassword + +End + +Private Sub Password_Write(Value As Boolean) + + $bPassword = Value + Me._SetProperty("Password", Value) + +End + +Private Function ShowClear_Read() As Boolean + + Return $bShowClear + +End + +Private Sub ShowClear_Write(Value As Boolean) + + $bShowClear = Value + Me._SetProperty("ShowClear", Value) + +End diff --git a/comp/src/gb.web.form/.src/WebTimer.class b/comp/src/gb.web.form/.src/WebTimer.class new file mode 100644 index 00000000..2a8898d8 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebTimer.class @@ -0,0 +1,93 @@ +' Gambas class file + +Export +Inherits WebControl + +Public Const _IsControl As Boolean = True +Public Const _IsVirtual As Boolean = True +Public Const _Properties As String = "*,-Class,-Width,-Height,-Visible,-Expand,-Ignore,Enabled,Delay{Range:0;86400000;10;ms}=1000" +Public Const _Group As String = "Special" +Public Const _DefaultEvent As String = "Timer" + +Event Timer + +Property Delay As Integer +Property Enabled As Boolean + +Private $iDelay As Integer = 1000 +Private $bEnabled As Boolean + +Public Sub _new() + +End + + +Private Function Delay_Read() As Integer + + Return $iDelay + +End + +Private Sub Delay_Write(Value As Integer) + + $iDelay = Value + Me._SetProperty("Delay", Value) + +End + +Public Sub _BeforeRender() + +End + + +Public Sub _Render() + + If $bEnabled Then + WebForm._AddJavascript("gw.addTimer(" & JS(Me.Name) & "," & CStr($iDelay) & ")") + Else + WebForm._AddJavascript("gw.removeTimer(" & JS(Me.Name) & ")") + Endif + +End + +Public Sub _AfterRender() + +End + +Private Function Enabled_Read() As Boolean + + Return $bEnabled + +End + +Private Sub Enabled_Write(Value As Boolean) + + If $bEnabled = Value Then Return + $bEnabled = Value + Me._SetProperty("Enabled", Value) + +End + +Public Sub Refresh() + + If WebForm._InExec Then _Render + +End + +Public Sub _UpdateProperty(sProp As String, (vValue) As Variant) + + If sProp = "#timer" Then Raise Timer + +End + +Public Sub Start() + + Enabled_Write(True) + +End + +Public Sub Stop() + + Enabled_Write(False) + +End diff --git a/comp/src/gb.web.form/.src/WebUploadArea.class b/comp/src/gb.web.form/.src/WebUploadArea.class new file mode 100644 index 00000000..037fc088 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebUploadArea.class @@ -0,0 +1,198 @@ +' Gambas class file + +Inherits WebContainer + +Export + +Public Const _Properties As String = "*" +Public Const _DefaultEvent As String = "Click" + +'Event Click +Event Upload +Event Progress + +Property Progress As Float +Property Read File As String +Property Read Path As String +Property Read Uploading As Boolean + +Private $fProgress As Float + +'Property File As String + +'Private $sFile As String +'Private $bUpload As Boolean + +Public Sub _BeforeRender() + + Print ""; + Print "" + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + If sProp = "#progress" Then + + If vValue = 0 Then + Me.Enabled = False + Endif + If Not Me.Enabled Then + Me.Progress = vValue + Raise Progress + If $fProgress = 1 Then + Me.Enabled = True + Endif + Endif + + Endif + +End + +' Private Function File_Read() As String +' +' Return $sFile +' +' End +' +' Private Sub File_Write(Value As String) +' +' $sFile = Value +' Me._SetProperty("File", Value) +' +' End + +' Public Sub Upload() +' +' If $bUpload Then Error.Raise("Pending upload") +' +' $bUpload = True +' Me._SetProperty("#upload", True) +' +' WebForm._AddReply("gw.file.upload(" & JS(Me.Name) & ");") +' +' End +' +' Public Sub _InitSpecialProperty(sProp As String, vVal As Variant) +' +' If sProp = "#upload" Then +' $bUpload = vVal +' Me.Refresh +' Endif +' +' End +' + +Private Sub GetUploadDir() As String + + Dim sDir As String + + sDir = File.Dir(File.Dir(Temp$())) &/ "upload" + Try Mkdir sDir + sDir &/= Session.Id + Try Mkdir sDir + Return sDir + +End + +Public Sub _UploadFinish() + + Dim sPath As String + Dim sTemp As String + Dim sName As String + + 'Progress_Write(1) + 'Raise Progress + + 'Session.Load + 'Me._InitProperties + + Me["path"] = "" + Me["file"] = "" + + WebForm.Print("_UploadFinish: Request.TempDir = " & Request.TempDir) + + For Each sPath In Request.Files + + WebForm.Print("_UploadFinish: sPath = " & sPath) + sName = Request["name"] + + If Request.TempDir Then + sTemp = sPath + Else + sTemp = GetUploadDir() &/ Me.Name & "." & File.Ext(sName) + Try Kill sTemp + Try Move sPath To sTemp + If Error Then + WebForm.Print("_UploadFinish: " & Error.Text & ": " & sPath & " -> " & sTemp) + Break + Endif + Endif + + Me["path"] = sTemp + Me["file"] = sName + Break + + Next + + Me.Enabled = True + +End + +Private Function Progress_Read() As Float + + Return $fProgress + +End + +Private Sub Progress_Write(Value As Float) + + $fProgress = Value + Me._SetProperty("Progress", Value) + +End + +Private Function File_Read() As String + + Return Me["file"] + +End + +Private Function Path_Read() As String + + Return Me["path"] + +End + +Private Function Uploading_Read() As Boolean + + Return $fProgress > 0 + +End + +Public Sub Abort() + + WebForm._AddReplyBefore("gw.file.abort(" & JS(Me.Name) & ")") + Me.Enabled = True + +End + +Public Sub Clear() + + Dim sPath As String + + Abort + + sPath = Me["path"] + If sPath Then + Try Kill sPath + If Not Request.TempDir Then Try Rmdir File.Dir(sPath) + Me["name"] = "" + Me["path"] = "" + Me["file"] = "" + Endif + +End diff --git a/comp/src/gb.web.form/.src/WebUploadButton.class b/comp/src/gb.web.form/.src/WebUploadButton.class new file mode 100644 index 00000000..3467d396 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebUploadButton.class @@ -0,0 +1,137 @@ +' Gambas class file + +'Export +Inherits WebControl + +Public Const _Properties As String = "*,Border=True,Text,Image{WebImage}" +Public Const _DrawWith As String = "Button" +Public Const _DefaultEvent As String = "Click" +Public Const _DefaultSize As String = "16,4" + +Event Click +Event Upload +Event Progress(Progress As Float) + +Property Text As String +Property Image As String +Property File As String + +Private $sText As String +Private $sImage As String +Private $sFile As String +Private $bUpload As Boolean + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + Me._SetProperty("Text", Value) + +End + +Private Function Image_Read() As String + + Return $sImage + +End + +Private Sub Image_Write(Value As String) + + $sImage = Value + Me._SetProperty("Image", Value) + +End + +Public Sub _BeforeRender() + + Print ""; + + Print ""; + +End + + +Public Sub _Render() + + If $sImage Then + If $sText Then + Print ""; + Else + Print ""; + Endif + Endif + If $sText Then Print "
    "; Html($sText); "
    " + +End + +Public Sub _AfterRender() + + Raise Render + Print "" + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + Dim sFile As String + + If sProp = "#path" Then + Try sFile = File.Name(Replace(vValue, "\\", "/")) + Me.File = sFile + Raise Click + Endif + +End + +Private Function File_Read() As String + + Return $sFile + +End + +Private Sub File_Write(Value As String) + + $sFile = Value + Me._SetProperty("File", Value) + +End + +Public Sub Upload() + + If $bUpload Then Error.Raise("Pending upload") + + $bUpload = True + Me._SetProperty("#upload", True) + + WebForm._AddReply("gw.file.upload(" & JS(Me.Name) & ")") + +End + +Public Sub _InitSpecialProperty(sProp As String, vVal As Variant) + + If sProp = "#upload" Then + $bUpload = vVal + Me.Refresh + Endif + +End + +Public Sub _UploadFinish() + + Raise Progress(1.0) + + $bUpload = False + Me._SetProperty("#upload", False) + + Raise Upload + +End diff --git a/comp/src/gb.web.form/.src/WebUploader.class b/comp/src/gb.web.form/.src/WebUploader.class new file mode 100644 index 00000000..f2cf1d25 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebUploader.class @@ -0,0 +1,135 @@ +' Gambas class file + +Export +Inherits WebContainer + +Public Const _IsContainer As Boolean = False +Public Const _Group As String = "WebForm" +Public Const _Properties As String = "*,Text,Image{WebImage}" +Public Const _DefaultEvent As String = "Upload" +Public Const _DefaultSize As String = "18,6" + +Event Upload + +Property Text As String +Property Image As String +Property Read Path As String +Property Read File As String + +Private uplFile As WebUploadArea +Private btnUpload As WebButton +Private pgbUpload As WebProgressBar +Private btnAbort As WebButton + +Private $sText As String + +Public Sub _new() + + Me.Arrangement = Arrange.Horizontal + Me.Spacing = True + + uplFile = New WebUploadArea(Me) As "uplFile" + btnUpload = New WebButton(uplFile) As "btnUpload" + + btnAbort = New WebButton(Me) As "btnAbort" + btnAbort.Image = "gw-close.png" + btnAbort.Text = ("Abort") + btnAbort.Hide + + pgbUpload = New WebProgressBar(Me) As "pgbUpload" + pgbUpload.Expand = True + pgbUpload.Hide + + UpdateUploadButton + +End + + +Private Function Text_Read() As String + + Return $sText + +End + +Private Sub UpdateUploadButton() + + If $sText Then + btnUpload.Text = $sText + Else + btnUpload.Text = ("Upload file...") + Endif + +End + +Private Sub Text_Write(Value As String) + + $sText = Value + Me._SetProperty("Text", Value) + UpdateUploadButton + +End + +Public Sub uplFile_Upload() + + 'pgbUpload.Hide + btnAbort.Hide + uplFile.Show + + pgbUpload.Value = 1 + + Raise Upload + +End + +Private Function Path_Read() As String + + Return uplFile.Path + +End + +Private Function File_Read() As String + + Return uplFile.File + +End + +Public Sub uplFile_Progress() + + If uplFile.Progress = 0 Then + pgbUpload.Show + btnAbort.Show + uplFile.Hide + Endif + + pgbUpload.Value = uplFile.Progress + +End + +Public Sub btnAbort_Click() + + uplFile.Abort + + pgbUpload.Hide + btnAbort.Hide + uplFile.Show + +End + +Public Sub Clear() + + uplFile.Clear + +End + +Private Function Image_Read() As String + + Return btnUpload.Image + +End + +Private Sub Image_Write(Value As String) + + btnUpload.Image = Value + +End + diff --git a/comp/src/gb.web.form/.src/WebVBox.class b/comp/src/gb.web.form/.src/WebVBox.class new file mode 100644 index 00000000..98991048 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebVBox.class @@ -0,0 +1,15 @@ +' Gambas class file + +Export + +Inherits WebContainer + +Public Const _Properties As String = "*,-Arrangement" +Public Const _DefaultArrangement As String = "V" +Public Const _Similar As String = "WebContainer" + +Public Sub _new() + + Me.Arrangement = Arrange.Vertical + +End diff --git a/comp/src/gb.web.form/.src/WebWindow.class b/comp/src/gb.web.form/.src/WebWindow.class new file mode 100644 index 00000000..47a394e4 --- /dev/null +++ b/comp/src/gb.web.form/.src/WebWindow.class @@ -0,0 +1,372 @@ +' Gambas class file + +Inherits WebControl + +'Public Const _Properties As String = "*,Text" +'Public Const _DefaultEvent As String = "Click" + +'Event Click + +Property X As String +Property Y As String +Property Width, W As String +Property Height, H As String +Property Child As WebForm +Property Resizable As Boolean +Property Modal As Boolean + +Property PopupControl As WebControl +Property PopupAlignment As Integer +Property ParentControl As WebControl + +Private $sX As String +Private $sY As String +Private $sWidth As String +Private $sHeight As String +Private $iFormId As Integer +Private $bResizable As Boolean +Private $bModal As Boolean +Private $iPopupControlId As Integer +Private $iPopupAlignment As Integer +Private $iParentControlId As Integer + +Public Sub IsPopup() As Boolean + + Return $iPopupControlId + +End + +Public Sub _RenderStyleSheet() + + Me._StartStyleSheet + + Me._AddStyleSheet("display:flex;") + Me._AddStyleSheet("flex-flow:column;") + + If Not IsPopup() Then + + If $sX And If $sY Then + Me._AddStyleSheet("transform:translate(" & $sX & "," & $sY & ");") + Else + + ' If $sX Then Me._AddStyleSheet("left:" & $sX & ";") + ' If $sY Then Me._AddStyleSheet("top:" & $sY & ";") + + WebForm._AddJavascript("gw.window.center(" & JS(Me.Name) & ")") + Endif + + Endif + + If $sWidth Then + Me._AddStyleSheet("width:" & $sWidth & ";") + 'Me._AddStyleSheet("max-width:" & $sWidth & ";") + Endif + If $sHeight Then Me._AddStyleSheet("height:" & $sHeight & ";") + + Me._EndStyleSheet + +End + +Private Sub GetTitle() As String + + Dim hForm As WebForm + + Try hForm = WebControl.FromId($iFormId) + If hForm Then Return hForm.Title + +End + +Public Sub _Render() + + Dim sOpen As String + + If Not IsPopup() Then + + Print "
    " + Print "" + Print "
    "; GetTitle(); "
    " + If $bResizable Then + Print "" + Endif + Print "
    " + + Endif + + Print "
    " + If $iFormId Then + With WebControl.FromId($iFormId) + ._BeforeRender() + ._Render() + ._AfterRender() + End With + Endif + Print "
    " + + If IsPopup() Then + + sOpen = "gw.window.popup(" & JS(Me.Name) & "," & JS($bResizable) & "," & JS(WebControl.FromId($iPopupControlId).Name) & "," & If($iPopupAlignment = Align.Right, "'right'", "'left'") + If Me["min-width"] Then sOpen &= "," & JS(Me["min-width"]) & "," & JS(Me["min-height"]) + sOpen &= ");" + + Else + + sOpen = "gw.window.open(" & JS(Me.Name) & "," & JS($bResizable) & "," & JS($bModal) + If Me["min-width"] Then sOpen &= "," & JS(Me["min-width"]) & "," & JS(Me["min-height"]) + sOpen &= ");" + + Endif + + WebForm._AddJavascript(sOpen) + +End + + +Private Function X_Read() As String + + Return $sX + +End + +Private Sub X_Write(Value As String) + + If $sX = Value Then Return + $sX = Value + If $sX And If IsDigit($sX) Then $sX &= "em" + Me._SetProperty("X", Value) + +End + +Private Function Y_Read() As String + + Return $sY + +End + +Private Sub Y_Write(Value As String) + + If $sY = Value Then Return + $sY = Value + If $sY And If IsDigit($sY) Then $sY &= "em" + Me._SetProperty("Y", Value) + +End + +Private Function Width_Read() As String + + Return $sWidth + +End + +Private Sub Width_Write(Value As String) + + If $sWidth = Value Then Return + $sWidth = Value + If $sWidth And If IsDigit($sWidth) Then $sWidth &= "em" + Me._SetProperty("Width", Value) + +End + +Private Function Height_Read() As String + + Return $sHeight + +End + +Private Sub Height_Write(Value As String) + + If $sHeight = Value Then Return + $sHeight = Value + If $sHeight And If IsDigit($sHeight) Then $sHeight &= "em" + Me._SetProperty("Height", Value) + +End + +Public Sub Move(X As String, Y As String, Optional Width As String, Height As String) + + X_Write(X) + Y_Write(Y) + If Not IsMissing(Width) Then Width_Write(Width) + If Not IsMissing(Height) Then Height_Write(Height) + +End + +Public Sub Resize(Width As String, Height As String) + + Width_Write(Width) + Height_Write(Height) + +End + +Public Sub _UpdateProperty(sProp As String, vValue As Variant) + + Dim hChild As WebForm + + If sProp = "#geometry" Then + + If Not Me["min-width"] Then + Me["min-width"] = Left(vValue[2], -2) + Me["min-height"] = Left(vValue[3], -2) + Endif + Inc Me._NoRefresh + Try Move(vValue[0], vValue[1], vValue[2], vValue[3]) + Dec Me._NoRefresh + + Else If sProp = "#close" Then + + hChild = Child_Read() + If hChild Then hChild.Close + + Endif + +End + +Private Function Child_Read() As WebForm + + If $iFormId Then Return WebControl.FromId($iFormId) + +End + +Private Sub Child_Write(Value As WebForm) + + Dim sType As String + + $iFormId = Value.Id + + sType = Object.Type(Value) + If Value = Object.Class(Value).Instance Then sType = "!" & sType + + Me._SetProperty("#child", [sType, Value.Name]) + +End + + +Public Sub _InitSpecialProperty(sProp As String, vVal As Variant) + + Dim hChild As WebForm + Dim aWindow As String[] + Dim sClass As String + + If sProp = "#child" Then + + aWindow = vVal + WebControl._NextName = aWindow[1] + sClass = aWindow[0] + If sClass Begins "!" Then + hChild = Class.Load(Mid$(sClass, 2)).AutoCreate() + Else + hChild = Object.New(sClass) + Endif + hChild._Window = Me.Id + $iFormId = hChild.Id + + Endif + +End + +Private Function Resizable_Read() As Boolean + + Return $bResizable + +End + +Private Sub Resizable_Write(Value As Boolean) + + $bResizable = Value + Me._SetProperty("Resizable", Value) + +End + +Public Sub Delete() + + Dim hChild As WebForm + + hChild = Child_Read() + If hChild Then hChild.Delete + Super.Delete() + +End + +Private Function Modal_Read() As Boolean + + Return $bModal + +End + +Private Sub Modal_Write(Value As Boolean) + + $bModal = Value + Me._SetProperty("Modal", Value) + +End + +Public Sub _RefreshReply() As Boolean + + If Super._RefreshReply() Then Return + Child_Read()._RefreshReply() + +End + +Private Function PopupControl_Read() As WebControl + + If $iPopupControlId Then Return WebControl.FromId($iPopupControlId) + +End + +Private Sub PopupControl_Write(Value As WebControl) + + $iPopupControlId = Value.Id + If IsPopup() Then + Me.Class = "gw-popup" + Else + Me.Class = "" + Endif + Me._SetProperty("PopupControl", Value) + +End + +Private Function PopupAlignment_Read() As Integer + + Return $iPopupAlignment + +End + +Private Sub PopupAlignment_Write(Value As Integer) + + $iPopupAlignment = Value + Me._SetProperty("PopupAlignment", Value) + +End + +Public Sub Close(Optional vReturnValue As Variant) + + If Not Me.Child.Persistent Then + WebForm._AddReplyBefore("gw.window.close(" & JS(Me.Name) & ")") + Endif + WebForm._AddReplyBefore("gw.window.refresh()") + + If IsPopup() Then PopupControl_Read().SetFocus + If $iParentControlId Then Object.Raise(WebControl.FromId($iParentControlId), "Dialog", [Child_Read(), vReturnValue]) + 'If $iParentControlId Then WebForm._AddReply("gw.raise(" & JS(WebControl.FromId($iParentControlId).Name) & ",);") + +End + +Private Function ParentControl_Read() As WebControl + + If $iParentControlId Then Return WebControl.FromId($iParentControlId) + +End + +Private Sub ParentControl_Write(Value As WebControl) + + If Value Then + $iParentControlId = Value.Id + Else + $iParentControlId = 0 + Endif + Me._SetProperty("ParentControl", Value) + +End diff --git a/comp/src/gb.web.form/ac.js b/comp/src/gb.web.form/ac.js new file mode 100644 index 00000000..6f37ed0d --- /dev/null +++ b/comp/src/gb.web.form/ac.js @@ -0,0 +1,225 @@ +/* + JavaScript autoComplete v1.0.4 + Copyright (c) 2014 Simon Steinberger / Pixabay + GitHub: https://github.com/Pixabay/JavaScript-autoComplete + License: http://www.opensource.org/licenses/mit-license.php +*/ + +var AutoComplete = (function(){ + // "use strict"; + function AutoComplete(options){ + if (!document.querySelector) return; + + // helpers + function hasClass(el, className){ return el.classList ? el.classList.contains(className) : new RegExp('\\b'+ className+'\\b').test(el.className); } + + function addEvent(el, type, handler){ + if (el.attachEvent) el.attachEvent('on'+type, handler); else el.addEventListener(type, handler); + } + function removeEvent(el, type, handler){ + // if (el.removeEventListener) not working in IE11 + if (el.detachEvent) el.detachEvent('on'+type, handler); else el.removeEventListener(type, handler); + } + function live(elClass, event, cb, context){ + addEvent(context || document, event, function(e){ + var found, el = e.target || e.srcElement; + while (el && !(found = hasClass(el, elClass))) el = el.parentElement; + if (found) cb.call(el, e); + }); + } + + var o = { + selector: 0, + source: 0, + minChars: 3, + delay: 150, + offsetLeft: 0, + offsetTop: 1, + cache: 1, + menuClass: '', + renderItem: function (item, search){ + // escape special characters + search = search.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + var re = new RegExp("(" + search.split(' ').join('|') + ")", "gi"); + return '
    ' + item.replace(re, "$1") + '
    '; + }, + onSelect: function(e, term, item){} + }; + for (var k in options) { if (options.hasOwnProperty(k)) o[k] = options[k]; } + + // init + var elems = typeof o.selector == 'object' ? [o.selector] : document.querySelectorAll(o.selector); + for (var i=0; i 0) + that.sc.scrollTop = selTop + that.sc.suggestionHeight + scrTop - that.sc.maxHeight; + else if (selTop < 0) + that.sc.scrollTop = selTop + scrTop; + } + } + } + addEvent(window, 'resize', that.updateSC); + document.body.appendChild(that.sc); + + live('gw-ac-suggestion', 'mouseleave', function(e){ + var sel = that.sc.querySelector('.gw-ac-suggestion.selected'); + if (sel) setTimeout(function(){ sel.className = sel.className.replace('selected', ''); }, 20); + }, that.sc); + + live('gw-ac-suggestion', 'mouseover', function(e){ + var sel = that.sc.querySelector('.gw-ac-suggestion.selected'); + if (sel) sel.className = sel.className.replace('selected', ''); + this.className += ' selected'; + }, that.sc); + + live('gw-ac-suggestion', 'mousedown', function(e){ + if (hasClass(this, 'gw-ac-suggestion')) { // else outside click + var v = this.getAttribute('data-val'); + that.value = v; + o.onSelect(e, v, this); + that.sc.style.display = 'none'; + } + }, that.sc); + + that.blurHandler = function(){ + try { var over_sb = document.querySelector('.gw-ac-suggestions:hover'); } catch(e){ var over_sb = 0; } + if (!over_sb) { + that.last_val = that.value; + that.sc.style.display = 'none'; + setTimeout(function(){ that.sc.style.display = 'none'; }, 350); // hide suggestions on fast input + } else if (that !== document.activeElement) setTimeout(function(){ that.focus(); }, 20); + }; + addEvent(that, 'blur', that.blurHandler); + + var suggest = function(data){ + var val = that.value; + that.cache[val] = data; + if (data.length && val.length >= o.minChars) { + var s = ''; + for (var i=0;i 40) && key != 13 && key != 27) { + var val = that.value; + if (val.length >= o.minChars) { + if (val != that.last_val) { + that.last_val = val; + clearTimeout(that.timer); + if (o.cache) { + if (val in that.cache) { suggest(that.cache[val]); return; } + // no requests if previous suggestions were empty + for (var i=1; is01O*-`iGRum*+Lew lg@p31kdp2&nAZpdj12ca@PEA&e-UI1gQu&X%Q~loCIBzQ7Rdkr literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/arrow-right.png b/comp/src/gb.web.form/arrow-right.png new file mode 100644 index 0000000000000000000000000000000000000000..8fdedfa0d24f7bb2106c4e09a2085157058e67ca GIT binary patch literal 94 zcmeAS@N?(olHy`uVBq!ia0vp^93afW0wnX;%77$;s;7%%2*>s01Q5tbaA5v_vGw0# n?!WFFhK32O5nC2Wb{%15sM^d^r8q+>5v0S@)z4*}Q$iB}x%?Pi literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/clear.png b/comp/src/gb.web.form/clear.png new file mode 100644 index 0000000000000000000000000000000000000000..9fb346d438d43f35e7de01b86d0f637d644cebbb GIT binary patch literal 116 zcmeAS@N?(olHy`uVBq!ia0vp^93afW0wnX;%77$;wWo_?2*>s0v<=A}=@us#7)yEn zE4s+eiEd9&P+8y subjectString.length) { + position = subjectString.length; + } + position -= searchString.length; + var lastIndex = subjectString.indexOf(searchString, position); + return lastIndex !== -1 && lastIndex === position; + }; +} + +Element.prototype.hasClass = function(klass) +{ + if (this.classList) + return this.classList.contains(klass); + else + return !!this.className.match(new RegExp('(\\s|^)' + klass + '(\\s|$)')); +}; + +Element.prototype.addClass = function(klass) +{ + if (this.classList) + this.classList.add(klass); + else if (!this.hasClass(klass)) + this.className += " " + klass; +}; + +Element.prototype.removeClass = function(klass) +{ + if (this.classList) + this.classList.remove(klass); + else if (this.hasClass(klass)) + { + var reg = new RegExp('(\\s|^)' + klass + '(\\s|$)'); + this.className = this.className.replace(reg, ' '); + } +}; + +/*Element.prototype.ensureVisible = function() +{ + var parent = this.offsetParent; + + while (parent && parent.clientHeight == parent.scrollHeight && parent.clientWidth == parent.scrollWidth) + parent = parent.offsetParent; + + if (parent) + gw.ensureVisible(this.offsetParent, this.offsetLeft, this.offsetTop, this.offsetWidth, this.offsetHeight); +};*/ + +gw = { + + version: '0', + commands: [], + timers: {}, + windows: [], + form: '', + debug: false, + loaded: {}, + uploads: {}, + autocompletion: [], + focus: false, + lock: 0, + + log: function(msg) + { + if (gw.debug) + { + if (gw.startTime == undefined) + gw.startTime = Date.now(); + console.log(((Date.now() - gw.startTime) / 1000).toFixed(3) + ': ' + msg); + } + }, + + load: function(lib) + { + var elt, src; + + if (gw.loaded[lib]) + return; + + if (lib.endsWith('.js')) + { + elt = document.createElement('script'); + elt.setAttribute("type", "text/javascript"); + src = $root + '/lib:' + lib.slice(0, -3) + ':' + gw.version + '.js'; + elt.setAttribute("src", src); + } + else if (lib.endsWith('.css')) + { + elt = document.createElement('link'); + elt.setAttribute("rel", "stylesheet"); + elt.setAttribute("type", "text/css"); + src = $root + '/style:' + lib.slice(0, -4) + ':' + gw.version + '.css'; + elt.setAttribute("href", src); + } + else + return; + + document.getElementsByTagName("head")[0].appendChild(elt); + gw.loaded[lib] = src; + console.log('load: ' + src); + }, + + setInnerHtml : function(id, html) + { + var oldDiv = $(id); + var newDiv = oldDiv.cloneNode(false); + newDiv.innerHTML = html; + oldDiv.parentNode.replaceChild(newDiv, oldDiv); + }, + + setOuterHtml : function(id, html) + { + if ($(id)) + $(id).outerHTML = html; + else + console.log('setOuterHtml: ' + id + '? ' + html); + }, + + removeElement : function(id) + { + var elt = $(id); + //for (i = 0; i < id_list.length; i++) + //{ + //elt = $(id_list[i]); + if (!elt) + return; + + //console.log(id + " removed"); + + elt.parentNode.removeChild(elt); + //} + }, + + insertElement : function(id, parent) + { + var elt = document.createElement('div'); + elt.id = id; + + $(parent).appendChild(elt); + }, + + setVisible : function(id, visible) + { + var elt = $(id); + if (elt) + { + if (visible) + elt.removeClass('gw-hidden'); + else + elt.addClass('gw-hidden'); + } + }, + + saveFocus: function() { + var active = document.activeElement.id; + var selection; + + if (active) + selection = gw.getSelection($(active)); + + return [active, selection]; + }, + + restoreFocus: function(save) { + var elt; + + if (save[0]) + { + elt = $(save[0]) + if (elt) + { + elt.focus(); + gw.setSelection(elt, save[1]); + } + } + //else + // gw.active = document.activeElement.id; + }, + + wait: function(lock) { + var elt; + + if (lock) + { + if (gw.lock == 0) + { + elt = $('gw-lock'); + elt.style.zIndex = 1000; + elt.style.display = 'block'; + + gw.lock_id = setTimeout(function() { + $('gw-lock').style.opacity = '1'; + }, 500); + } + + gw.lock++; + } + else + { + gw.lock--; + if (gw.lock == 0) + { + if (gw.lock_id) + { + clearTimeout(gw.lock_id); + gw.lock_id = undefined; + } + elt = $('gw-lock'); + elt.style.display = 'none'; + elt.style.opacity = '0'; + } + } + }, + + answer: function(xhr, after) + { + if (xhr.readyState == 4) + { + if (xhr.status == 200 && xhr.responseText) + { + xhr.gw_command && gw.log('==> ' + xhr.gw_command + '...'); + + gw.focus = false; + var save = gw.saveFocus(); + + /*if (gw.debug) + console.log('--> ' + xhr.responseText);*/ + + var r = xhr.responseText.split('\n'); + var i, expr; + + for (i = 0; i < r.length; i++) + { + expr = r[i].trim(); + if (expr.length == 0) + continue; + if (gw.debug) + { + if (expr.length > 1024) + gw.log('--> ' + expr.substr(0, 1024) + '...'); + else + gw.log('--> ' + expr); + } + eval(expr); + } + + //eval(xhr.responseText); + + if (!gw.focus) + gw.restoreFocus(save); + + } + + if (after) + after(); + + xhr.gw_command && gw.log('==> ' + xhr.gw_command + ' done.'); + + if (xhr.gw_command && (xhr.gw_command.length < 5 || xhr.gw_command[4] == undefined || xhr.gw_command[4] == false)) + gw.wait(false); + + gw.commands.splice(0, 2); + gw.sendNewCommand(); + } + }, + + sendNewCommand: function() + { + var command, after, len; + var xhr; + + for(;;) { + + len = gw.commands.length; + + if (len < 2) + return; + + command = gw.commands[0]; + after = gw.commands[1]; + + gw.log('[ ' + command + ' ]'); + + if (command) + { + if (command.length < 5 || command[4] == undefined || command[4] == false) + gw.wait(true); + + xhr = new XMLHttpRequest(); + xhr.gw_command = command; + xhr.open('GET', $root + '/x?c=' + encodeURIComponent(JSON.stringify(command)), true); + xhr.onreadystatechange = function() { gw.answer(xhr, after); }; + xhr.send(null); + gw.log("send XMLHttpRequest..."); + return; + } + + after(); + gw.commands.splice(0, 2); + } + }, + + send: function(command, after) + { + gw.log('gw.send: ' + command + ' ' + JSON.stringify(gw.commands)); + + gw.commands.push(command); + gw.commands.push(after); + + if (gw.commands.length == 2) + gw.sendNewCommand(); + }, + + raise: function(id, event, args, no_wait) + { + gw.send(['raise', id, event, args, no_wait]); + }, + + update: function(id, prop, value, after) + { + gw.send(['update', id, prop, value, true], after); + }, + + updateWait: function(id, prop, value, after) + { + gw.send(['update', id, prop, value, false], after); + }, + + command: function(action) + { + gw.send(null, action); + }, + + getSelection: function(o) + { + var start, end; + + try + { + if (o.createTextRange) + { + var r = document.selection.createRange().duplicate(); + r.moveEnd('character', o.value.length) + if (r.text == '') + start = o.value.length; + else + start = o.value.lastIndexOf(r.text); + r.moveStart('character', -o.value.length); + end = r.text.length; + return [start, end]; + } + + if (o.selectionStart && o.selectionEnd) + return [o.selectionStart, o.selectionEnd]; + } + catch(e) {}; + + return undefined; + }, + + setSelection: function(o, sel) + { + if (sel) + { + if (o.setSelectionRange) + try { o.setSelectionRange(sel[0], sel[1]) } catch(e) {}; + } + }, + + setFocus: function(id) + { + var elt = $(id + ':entry') || $(id); + + if (elt) + { + elt.focus(); + gw.active = document.activeElement.id; + gw.selection = undefined; + gw.focus = true; + } + }, + + resizeComboBox: function(id) + { + $(id + '-select').onmouseover = function() { $(id + '-select').style.width = $(id).offsetWidth + 'px'; } + }, + + highlightMandatory: function(id) + { + var elt = $(id); + var elt_br; + var div; + var div_br; + + if (elt == undefined || elt.gw_mandatory) + return; + + elt.gw_mandatory = div = document.createElement('div'); + div.className = 'gw-mandatory'; + elt.parentNode.insertBefore(div, elt); + + elt_br = gw.getPos(elt); + div_br = gw.getPos(div); + + div.style.top = (elt_br.top - div_br.top) + 'px'; + div.style.left = (elt_br.left - div_br.left) + 'px'; + div.style.width = elt_br.width + 'px'; + div.style.height = elt_br.height + 'px'; + }, + + addTimer: function(id, delay) + { + gw.removeTimer(id); + gw.timers[id] = setInterval( + function() { + if (gw.timers[id + '!']) + return; + gw.timers[id + '!'] = true; + gw.send(['raise', id, 'timer', [], true], + function() { + if (gw.timers[id]) + gw.timers[id + '!'] = undefined; + } + ); + }, + delay); + }, + + removeTimer: function(id) + { + var t = gw.timers[id]; + if (t) + { + clearInterval(gw.timers[id]); + gw.timers[id] = undefined; + gw.timers[id + '!'] = undefined; + } + }, + + getTargetId: function(elt) + { + for(;;) + { + if (elt.id) + return elt.id; + elt = elt.parentNode; + if (!elt) + return; + } + }, + + getPos: function(elt) + { + var found, left = 0, top = 0, width = 0, height = 0; + var offsetBase = gw.offsetBase; + + if (!offsetBase && document.body) + { + offsetBase = gw.offsetBase = document.createElement('div'); + offsetBase.style.cssText = 'position:absolute;left:0;top:0'; + document.body.appendChild(offsetBase); + } + + if (elt && elt.ownerDocument === document && 'getBoundingClientRect' in elt && offsetBase) + { + var boundingRect = elt.getBoundingClientRect(); + var baseRect = offsetBase.getBoundingClientRect(); + found = true; + left = boundingRect.left - baseRect.left; + top = boundingRect.top - baseRect.top; + width = boundingRect.right - boundingRect.left; + height = boundingRect.bottom - boundingRect.top; + } + + return { found: found, left: left, top: top, width: width, height: height, right: left + width, bottom: top + height }; + }, + + copy: function(elt) + { + navigator.clipboard.writeText(elt.value) + .then(() => { + // Success! + }) + .catch(err => { + console.log('Unable to copy to the clipboard: ', err); + }); + }, + /*ensureVisible: function(id, x, y, w, h) + { + var elt = typeof(id) == 'string' ? $(id) : id; + var pw, ph,cx, cy, cw, ch; + var xx, yy, ww, hh; + + // WW = W / 2 + ww = w / 2; + //HH = H / 2 + hh = h / 2; + // XX = X + WW + xx = x + ww + // YY = Y + HH + yy = y + hh; + + // PW = Me.ClientW + // PH = Me.ClientH + pw = elt.clientWidth; + ph = elt.clientHeight; + + cx = - elt.scrollLeft; + cy = - elt.scrollTop; + cw = elt.scrollWidth; + ch = elt.scrollHeight; + + //If PW < (WW * 2) Then WW = PW / 2 + //If PH < (HH * 2) Then HH = PH / 2 + if (pw < (ww * 2)) ww = pw / 2; + if (ph < (hh * 2)) hh = ph / 2; + + //If CW <= PW Then + // WW = 0 + // CX = 0 + //Endif + if (cw <= pw) { ww = 0; cx = 0; } + + //If CH <= PH Then + // HH = 0 + // CY = 0 + //Endif + if (ch <= ph) { hh = 0; cy = 0 } + + //If XX < (- CX + WW) Then + // CX = Ceil(- XX + WW) + //Else If XX >= (- CX + PW - WW) Then + // CX = Floor(- XX + PW - WW) + //Endif + if (xx < (- cx + ww)) + cx = - xx + ww; + else if (xx >= (- cx + pw - ww)) + cx = - xx + pw - ww; + + //If YY < (- CY + HH) Then + // CY = Ceil(- YY + HH) + //Else If YY >= (- CY + PH - HH) Then + // CY = Floor(- YY + PH - HH) + //Endif + + if (yy < (- cy + hh)) + cy = - yy + hh; + else if (yy >= (- cy + ph - hh)) + cy = - yy + ph - hh; + + //If CX > 0 + // CX = 0 + //Else If CX < (PW - CW) And If CW > PW Then + // CX = PW - CW + //Endif + if (cx > 0) + cx = 0; + else if (cx < (pw - cw) && cw > pw) + cx = pw - cw; + + //If CY > 0 Then + // CY = 0 + //Else If CY < (PH - CH) And If CH > PH Then + // CY = PH - CH + //Endif + if (cy > 0) + cy = 0; + else if (cy < (ph - ch) && ch > ph) + cy = ph - ch; + + //If $hHBar.Value = - CX And If $hVBar.Value = - CY Then Return True + //Scroll(- CX, - CY) + elt.scrollLeft = - cx; + elt.scrollTop = - cy; + },*/ + + window: + { + zIndex: 0, + + open: function(id, resizable, modal, minw, minh) + { + gw.window.close(id); + + if (gw.windows.length == 0) + { + document.addEventListener('mousemove', gw.window.onMove); + document.addEventListener('mouseup', gw.window.onUp); + gw.log('document.addEventListener'); + } + + gw.windows.push(id); + + $(id).addEventListener('mousedown', gw.window.onMouseDown); + + $(id).gw_resizable = resizable; + $(id).gw_modal = modal; + + if (modal) + $(id).gw_focus = gw.saveFocus(); + + if (minw != undefined) + { + $(id).gw_minw = minw; + $(id).gw_minh = minh; + } + else + { + $(id).gw_minw = $(id).offsetWidth; + $(id).gw_minh = $(id).offsetHeight; + } + + //console.log('gw.window.open: minw = ' + $(id).gw_minw + ' minh = ' + $(id).gw_minh); + + // Touch events + //pane.addEventListener('touchstart', onTouchDown); + //document.addEventListener('touchmove', onTouchMove); + //document.addEventListener('touchend', onTouchEnd); + + gw.window.refresh(); + }, + + popup: function(id, resizable, control, alignment, minw, minh) + { + var pos; + + gw.window.close(id); + + if (gw.windows.length == 0) + { + document.addEventListener('mousemove', gw.window.onMove); + document.addEventListener('mouseup', gw.window.onUp); + gw.log('document.addEventListener'); + } + + gw.windows.push(id); + + $(id).addEventListener('mousedown', gw.window.onMouseDown); + + $(id).gw_resizable = resizable; + $(id).gw_modal = true; + $(id).gw_popup = true; + $(id).gw_focus = gw.saveFocus(); + + if (minw != undefined) + { + $(id).gw_minw = minw; + $(id).gw_minh = minh; + } + else + { + $(id).gw_minw = $(id).offsetWidth; + $(id).gw_minh = $(id).offsetHeight; + } + + pos = gw.getPos($(control)); + //console.log(pos); + + /*$(id).style.left = pos.left + 'px'; + $(id).style.top = pos.bottom + 'px';*/ + $(id).style.transform = 'translate(' + pos.left + 'px,' + pos.bottom + 'px)'; + + gw.window.refresh(); + }, + + close: function(id) + { + var i; + + $(id).removeEventListener('mousedown', gw.window.onMouseDown); + + i = gw.windows.indexOf(id); + if (i >= 0) + { + gw.windows.splice(i, 1); + gw.window.refresh(); + } + + if ($(id).gw_focus) + { + gw.restoreFocus($(id).gw_focus); + $(id).gw_focus = undefined; + } + }, + + refresh: function() + { + var i = 0; + var zi; + + while (i < gw.windows.length) + { + if ($(gw.windows[i])) + { + zi = 11 + i * 2; + if ($(gw.windows[i]).style.zIndex != zi) + $(gw.windows[i]).style.zIndex = zi; + i++; + } + else + gw.windows.splice(i, 1); + } + + gw.window.updateModal(); + + if (gw.windows.length == 0) + { + gw.log('document.removeEventListener'); + document.removeEventListener('mousemove', gw.window.onMove); + document.removeEventListener('mouseup', gw.window.onUp); + } + else + gw.window.updateTitleBars(); + }, + + updateTitleBars: function() + { + var i, win, last; + + for (i = 0; i < gw.windows.length; i++) + { + win = gw.windows[i]; + if ($(win).gw_popup) + continue; + $(win).addClass('gw-deactivated'); + $(win + '-titlebar').addClass('gw-deactivated'); + last = win; + } + + if (last && !$(last).gw_popup) + { + $(last).removeClass('gw-deactivated'); + $(last + '-titlebar').removeClass('gw-deactivated'); + } + }, + + raise: function(id, send) + { + var i = gw.windows.indexOf(id); + if (i < 0) + return; + + gw.windows.splice(i, 1); + gw.windows.push(id); + + for (i = 0; i < gw.windows.length; i++) + $(gw.windows[i]).style.zIndex = 11 + i * 2; + + gw.window.updateTitleBars(); + + if (send) + gw.update('', '#windows', gw.windows); + }, + + updateModal: function() + { + var i, elt = $('gw-modal'); + + for (i = gw.windows.length - 1; i >= 0; i--) + { + if ($(gw.windows[i]).gw_modal) + { + gw.window.zIndex = 10 + i * 2; + elt.style.zIndex = 10 + i * 2; + elt.style.display = 'block'; + /*if ($(gw.windows[i]).gw_popup) + elt.style.opacity = '0'; + else + elt.style.opacity = '';*/ + return; + } + } + + gw.window.zIndex = 0; + elt.style.display = 'none'; + }, + + center: function(id) + { + $(id).style.transform = 'translate(' + ((window.innerWidth - $(id).offsetWidth) / 2 | 0) + 'px,' + ((window.innerHeight - $(id).offsetHeight) / 2 | 0) + 'px)'; + gw.window.updateGeometry(id); + }, + + maximize: function(id) + { + var geom = $(id).gw_save_geometry; + if (geom != undefined) + { + //$(id).style.left = geom[0]; + //$(id).style.top = geom[1]; + $(id).style.transform = geom[0] + $(id).style.width = geom[1]; + $(id).style.height = geom[2]; + $(id).gw_save_geometry = undefined; + } + else + { + $(id).gw_save_geometry = [$(id).style.transform, $(id).style.width, $(id).style.height]; + $(id).style.transform = ''; + $(id).style.width = '100%'; + $(id).style.height = '100%'; + } + //gw.window.updateGeometry(id); + }, + + onMouseDown: function(e) + { + gw.window.onDown(e); + }, + + onDown: function(e) + { + var c, win; + + gw.window.context = undefined; + + if (e.target.className == 'gw-window-button') + return; + + gw.window.onMove(e); + + c = gw.window.context; + if (c == undefined) + return; + + if ($(c.id).gw_save_geometry) + return; + + if (c.isMoving || c.isResizing) + { + gw.window.raise(c.id); + gw.window.downEvent = e; + e.preventDefault(); + } + }, + + onDownModal: function() + { + var win = gw.windows[gw.windows.length - 1]; + + if ($(win).gw_popup) + gw.update(win, '#close'); + }, + + onMove: function(e) + { + var i, id, elt, b, x, y, bx, by, bw, bh, th; + var onTopEdge, onLeftEdge, onRightEdge, onBottomEdge, isResizing; + var MARGINS = 6; + + if (gw.window.downEvent) + { + gw.window.context.cx = e.clientX; + gw.window.context.cy = e.clientY; + gw.window.animate(); + return; + } + + gw.window.context = undefined; + + for (i = 0; i < gw.windows.length; i++) + { + id = gw.windows[gw.windows.length - i - 1]; + elt = $(id); + + if (elt.style.zIndex < gw.window.zIndex) + continue; + + b = elt.getBoundingClientRect(); + + bx = b.left; // - MARGINS; + by = b.top; // - MARGINS; + bw = b.width; // + MARGINS * 2; + bh = b.height; // + MARGINS * 2; + + x = e.clientX - bx; + y = e.clientY - by; + + //console.log(x + ',' + y + ' : ' + bx + ',' + by + ',' + bw + ',' + bh); + + if (x >= 0 && x < bw && y >= 0 && y < bh) + { + if (elt.gw_resizable) + { + onTopEdge = y < MARGINS; + onLeftEdge = x < MARGINS; + onRightEdge = x >= (bw - MARGINS); + onBottomEdge = y >= (bh - MARGINS); + + isResizing = onTopEdge || onLeftEdge || onRightEdge || onBottomEdge; + } + else + onTopEdge = onLeftEdge = onRightEdge = onBottomEdge = isResizing = false; + + if ($(id).gw_popup) + th = 0; + else + th = $(id + '-titlebar').offsetHeight; + isMoving = !isResizing && y < (th + MARGINS); + + gw.window.context = { + id: id, + x: b.left + window.scrollX, + y: b.top + window.scrollY, + cx: e.clientX, + cy: e.clientY, + w: b.width, + h: b.height, + isResizing: isResizing, + isMoving: isMoving, + onTopEdge: onTopEdge, + onLeftEdge: onLeftEdge, + onRightEdge: onRightEdge, + onBottomEdge: onBottomEdge + }; + gw.window.animate(); + break; + } + } + }, + + updateGeometry: function(id) + { + var b = $(id).getBoundingClientRect(); + gw.update(id, '#geometry', [ b.left + 'px', b.top + 'px', b.width + 'px', b.height + 'px']); + }, + + onUp: function(e) + { + var c = gw.window.context; + + gw.window.downEvent = undefined; + + if (c && (c.isMoving || c.isResizing)) + { + var id = gw.window.context.id; + gw.window.context = undefined; + gw.window.raise(id, true); + gw.window.updateGeometry(id); + } + }, + + animate: function() + { + var id, elt, c, e, x, y, w, h; + var minWidth; + var minHeight; + + //requestAnimationFrame(gw.window.animate); + + c = gw.window.context; + if (!c) return; + + elt = $(c.id); + minWidth = elt.gw_minw; + minHeight = elt.gw_minh; //$(c.id + '-titlebar').offsetHeight + 2 + elt.gw_minh; + e = gw.window.downEvent; + + if (c && c.isResizing && e) + { + if (c.onRightEdge) + elt.style.width = Math.max(c.w + c.cx - e.clientX, minWidth) + 'px'; + + if (c.onBottomEdge) + elt.style.height = Math.max(c.h + c.cy - e.clientY, minHeight) + 'px'; + + x = c.x; + y = c.y; + + if (c.onLeftEdge) + { + x = c.x + c.cx - e.clientX; + w = c.x + c.w - x; + if (w >= minWidth) + { + elt.style.width = w + 'px'; + //elt.style.left = x + 'px'; + elt.style.transform = 'translate(' + x + 'px,' + y + 'px)'; + //c.x = x; + } + } + + if (c.onTopEdge) + { + y = c.y + c.cy - e.clientY; + h = c.y + c.h - y; + if (h >= minHeight) + { + elt.style.height = h + 'px'; + //elt.style.top = y + 'px'; + elt.style.transform = 'translate(' + x + 'px,' + y + 'px)'; + } + } + + return; + } + + if (c && c.isMoving && e) + { + /*elt.style.left = (Math.max(0, c.x + c.cx - e.clientX)) + 'px'; + elt.style.top = (Math.max(0, c.y + c.cy - e.clientY)) + 'px';*/ + elt.style.transform = 'translate(' + (Math.max(0, c.x + c.cx - e.clientX)) + 'px,' + (Math.max(0, c.y + c.cy - e.clientY)) + 'px)'; + return; + } + + // This code executes when mouse moves without clicking + + if (c.onRightEdge && c.onBottomEdge || c.onLeftEdge && c.onTopEdge) + elt.style.cursor = 'nwse-resize'; + else if (c.onRightEdge && c.onTopEdge || c.onBottomEdge && c.onLeftEdge) + elt.style.cursor = 'nesw-resize'; + else if (c.onRightEdge || c.onLeftEdge) + elt.style.cursor = 'ew-resize'; + else if (c.onBottomEdge || c.onTopEdge) + elt.style.cursor = 'ns-resize'; + else + elt.style.cursor = ''; + } + }, + + menu: + { + hide: function(elt) + { + elt.style.display = 'none'; + setTimeout(function() { elt.style.display = ''; }, 150); + }, + + click: function(name, event) + { + var id = gw.getTargetId(event.target); + gw.update(name, '#click', id); + event.stopPropagation(); + } + }, + + table: + { + selectRange: function(id, start, end, checked) + { + var i; + var tr; + + if (end < start) + { + i = start; + start = end; + end = i; + } + + for (i = start; i <= end; i++) + { + tr = $(id + ':' + i); + if (checked) + tr.addClass('gw-table-row-selected'); + else + tr.removeClass('gw-table-row-selected'); + } + + gw.update(id, '!' + start + ':' + (end - start + 1), checked); + }, + + select: function(id, row, event) + { + var elt = $(id + ':' + row); + var last = $(id).gw_current; + var selected = !elt.hasClass('gw-table-row-selected'); + + if (event) + { + if (event.shiftKey && last) + gw.table.selectRange(id, last, row, selected); + else + gw.table.selectRange(id, row, row, selected); + } + else + { + if (last != undefined) + $(id + ':' + last) && $(id + ':' + last).removeClass('gw-table-row-selected'); + elt.addClass('gw-table-row-selected'); + gw.update(id, '$' + row, null); + } + + $(id).gw_current = row; + + $(id).addClass('gw-unselectable'); + setTimeout(function() { $(id).removeClass('gw-unselectable'); }, 0); + }, + + checkRange: function(id, start, end, checked) + { + var i; + if (end < start) + { + i = start; + start = end; + end = i; + } + + for (i = start; i <= end; i++) + $(id + ':' + i).checked = checked; + + gw.update(id, '!' + start + ':' + (end - start + 1), checked); + }, + + check: function(id, row, event) + { + var elt = $(id + ':' + row); + var checked = !elt.checked; + var last = $(id).gw_current; + var len; + + elt.focus(); + + if (event && event.shiftKey && last) + gw.table.checkRange(id, last, row, checked); + else + gw.table.checkRange(id, row, row, checked); + + if (event.target == elt) + elt.checked = !checked; + + $(id).gw_current = row; + + $(id).addClass('gw-unselectable'); + setTimeout(function() { $(id).removeClass('gw-unselectable'); }, 0); + }, + + /*toggle: function(id, row) + { + gw.update(id, '?' + row, false); + },*/ + + onScroll: function(id, more, timeout) + { + var elt = $(id); + + if (!elt) + return; + + var sw = elt.firstChild; + var last = elt.gw_last_scroll; + + if (last && last[0] == sw.scrollLeft && last[1] == sw.scrollTop) + return; + + elt.gw_last_scroll = [sw.scrollLeft, sw.scrollTop]; + + console.log('gw.table.onScroll: ' + id + ' ' + sw.scrollLeft + ',' + sw.scrollTop); + + if (more) + { + //if ((sw.scrollHeight - sw.scrollTop) === (sw.clientHeight)) + if (sw.scrollTop >= (sw.scrollHeight - sw.clientHeight - 16)) + { + /*var wait = document.createElement('div'); + wait.className = 'gw-waiting'; + elt.appendChild(wait);*/ + if (elt.gw_scroll) + { + clearTimeout(elt.gw_scroll); + elt.gw_scroll = undefined; + } + + gw.update(id, '#more', [sw.scrollLeft, sw.scrollTop]); + return; + } + } + + if (elt.gw_headerh) + $(elt.gw_headerh).firstChild.scrollLeft = sw.scrollLeft; + + if (elt.gw_headerv) + $(elt.gw_headerv).firstChild.scrollTop = sw.scrollTop; + + if (elt.gw_noscroll) + { + elt.gw_noscroll = undefined; + return; + } + + if (elt.gw_scroll) + clearTimeout(elt.gw_scroll); + + elt.gw_scroll = setTimeout(function() + { + var pos = [sw.scrollLeft, sw.scrollTop]; + + console.log('gw.table.onScroll (timer): ' + id + ' ' + sw.scrollLeft + ',' + sw.scrollTop); + clearTimeout(elt.gw_scroll); + + gw.update(elt.id, '#scroll', pos, function() + { + elt.gw_scroll = undefined; + if (pos[0] != sw.scrollLeft || pos[1] != sw.scrollTop) + gw.table.onScroll(id, more, timeout); + }); + + //elt.gw_scroll = undefined; + }, timeout || 250); + }, + + scroll: function(id, x, y) + { + var sw = $(id).firstChild + + console.log("gw.table.scroll: " + id + ": " + x + " " + y); + + if (x != sw.scrollLeft) + { + $(id).gw_noscroll = true; + sw.scrollLeft = x; + } + if (y != sw.scrollTop) + { + $(id).gw_noscroll = true; + sw.scrollTop = y; + } + if (x != sw.scrollLeft || y != sw.scrollTop) + gw.update(id, '#scroll', [sw.scrollLeft, sw.scrollTop]); + }, + + ensureVisible: function(id, row) + { + var sw = $(id).firstChild; + gw.table.scroll(id, sw.scrollLeft, $(id + ':' + row).offsetTop - sw.clientHeight / 2); + } + }, + + scrollview: + { + setHeaders: function(id, hid, vid) + { + $(id).gw_headerh = hid; + $(id).gw_headerv = vid; + }, + + ensureVisible: function(id, child) + { + var sw = $(id).firstChild; + child = $(child); + gw.table.scroll(id, child.offsetLeft - (sw.clientWidth - child.offsetWidth) / 2, child.offsetTop - (sw.clientHeight - child.offsetHeight) / 2); + } +}, + + file: + { + select: function(id) + { + var elt = $(id + ':file'); + + if ($(id).gw_uploading) + return; + + elt.focus(); + elt.click(); + }, + + finish: function(xhr) + { + if (xhr.gw_progress) + { + setTimeout(function() { gw.file.finish(xhr); }, 250); + return; + } + + gw.update(xhr.gw_id, '#progress', 1, function() { + gw.answer(xhr); + gw.uploads[xhr.gw_id] = undefined; + gw.raise(xhr.gw_id, 'upload', [], true); + xhr.gw_id = undefined; + }); + }, + + upload: function(id) + { + var elt = $(id + ':file'); + var file = elt.files[0]; + var xhr = new XMLHttpRequest(); + var form = new FormData(); + + if (gw.uploads[id]) + return; + + gw.uploads[id] = xhr; + + //gw.log('gw.file.upload: ' + id + ': ' + file.name); + + xhr.gw_progress = 0; + + xhr.gw_progress++; + gw.update(id, '#progress', 0, function() { xhr.gw_progress--; }); + + form.append('file', file); + form.append('name', file.name); + form.append('id', id); + + //xhr.upload.addEventListener("loadstart", loadStartFunction, false); + //xhr.upload.addEventListener("load", transferCompleteFunction, false); + + xhr.upload.addEventListener("progress", + function(e) + { + //console.log('upload: progress ' + e.loaded + ' / ' + e.total); + + if (xhr.gw_id == undefined) + return; + + if (e.lengthComputable) + { + var t = (new Date()).getTime(); + + if ((xhr.gw_time == undefined || (t - xhr.gw_time) > 250) && xhr.gw_progress == 0) + { + xhr.gw_progress++; + gw.update(xhr.gw_id, '#progress', e.loaded / e.total, function() { xhr.gw_progress--; }); + xhr.gw_time = t; + } + } + }, + false); + + xhr.gw_command = ['upload', id]; + xhr.gw_id = id; + + xhr.open("POST", $root + '/u', true); + + xhr.onreadystatechange = function() + { + if (xhr.readyState == 4) + gw.file.finish(xhr); + }; + + xhr.send(form); + }, + + abort: function(id) + { + if (gw.uploads[id]) + gw.uploads[id].abort(); + } + }, + + autocomplete: function(id) + { + new AutoComplete({ + selector: $(id + ':entry'), + cache: false, + source: function(term, response) { + var xhr = $(id).gw_xhr; + if (xhr) + { + try { xhr.abort(); } catch(e) {} + } + + $(id).gw_xhr = xhr = new XMLHttpRequest(); + + xhr.open('GET', $root + '/x?c=' + encodeURIComponent(JSON.stringify(['raise', id, 'completion', [term]])), true); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) + { + gw.autocompletion = []; + gw.answer(xhr); + response(gw.autocompletion); + } + }; + xhr.send(); + }, + onSelect: function(e, term, item) { + gw.textbox.setText(id, gw.textbox.getText(id)); + } + }); + }, + + textbox: + { + onactivate: function(id, e) + { + if (e.keyCode == 13) + setTimeout(function() { gw.update(id, 'text', $(id + ':entry').value); gw.raise(id, 'activate', [], false); }, 50); + }, + + onchange: function(id) + { + if ($(id).gw_timer) clearTimeout($(id).gw_timer); + $(id).gw_timer = setTimeout(function() { gw.update(id, 'change', $(id + ':entry').value, null); }, 50); + }, + + getText: function(id) + { + return $(id + ':entry').value; + }, + + setText: function(id, text) + { + gw.command(function() { + $(id + ':entry').value = text; + gw.setSelection($(id + ':entry'), [text.length, text.length]); + gw.update(id, 'text', text); + }); + }, + + clear: function(id) + { + gw.textbox.setText(id, ''); + gw.setFocus(id); + gw.raise(id, 'activate', [], false); + }, + + copy: function(id) + { + gw.copy($(id + ':entry')); + } + }, + + textarea: + { + onchange: function(id) + { + if ($(id).gw_timer) clearTimeout($(id).gw_timer); + $(id).gw_timer = setTimeout(function() { gw.update(id, 'change', $(id).value, null); }, 50); + }, + + copy: function(id) + { + gw.copy($(id)); + } + } + +} + diff --git a/comp/src/gb.web.form/shadow.png b/comp/src/gb.web.form/shadow.png new file mode 100644 index 0000000000000000000000000000000000000000..28921896f958a9387f3a0813bf52cf39341cd9e8 GIT binary patch literal 76 zcmeAS@N?(olHy`uVBq!ia0vp^93afW0wnX;%77$;u&0Y-2*>s06oUi@Mjsv?W<>@D W*%u5B?uFcuAbC$$KbLh*2~7aC0u5^b literal 0 HcmV?d00001 diff --git a/comp/src/gb.web.form/style.css b/comp/src/gb.web.form/style.css new file mode 100644 index 00000000..96cf3cd0 --- /dev/null +++ b/comp/src/gb.web.form/style.css @@ -0,0 +1,861 @@ +HTML, BODY { + margin: 0; + padding: 0; + height: 100%; + width: 100%; +} + +HTML, BODY, DIV, INPUT { + box-sizing: border-box; +} + +SELECT { + min-height: 2em; + font-size: inherit; +} + +INPUT { + font: inherit; +} + +H1,H2,H3,P { + margin-top: 0.5rem; + margin-bottom: 0.25rem; +} + +IFRAME { + border: none; +} + +/*UL, OL { + padding-left: 2em; + margin-bottom: 0; +} + +P:first-child,UL:first-child,OL:first-child { + margin-top: 0; +}*/ + +.gw-unselectable { + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; +} + +.gw-hidden { + display: none !important; +} + +.gw-button { + padding: 0; + font: inherit; + padding: 0.125em 0.5em; + min-height: 2em; +} + +.gw-button.gw-noborder { + border: solid 1px transparent; + background: none; + padding: 0; +} + +.gw-button.gw-noborder:hover { + border: solid 1px #C0C0C0; +} + +.gw-button-image { + margin-right: 0.5em; +} + +.gw-button > DIV { + vertical-align: middle; + position: relative; + display: inline-flex; + justify-content: center; + align-items: center; + flex-flow: row; +} + +.gw-button > DIV > SPAN { + display: inline-block; +} + +.gw-tab-header { + margin-bottom: -1px; + z-index: 1; + line-height: 2em; +} + +.gw-tab-header.gw-noborder { + margin-bottom: 0; +} + +.gw-tab { + color: gray; + padding: 0 0.5em; + cursor: pointer; + border-top: solid transparent 1px; + border-right: solid transparent 1px; + border-left: solid transparent 1px; +} + +.gw-tab:hover { + color: black; +} + +.gw-tab-selected { + padding: 0 0.5em; + border-left: solid #C0C0C0 1px; + border-top: solid #C0C0C0 1px; + border-right: solid #C0C0C0 1px; + border-top-left-radius: 0.5em; + border-top-right-radius: 0.5em; + background: white; +} + +.gw-tab-header.gw-noborder > div { + border: none; +} + +.gw-tab-contents { + border: solid #C0C0C0 1px; + flex-grow: 1; + display: flex; + flex-flow: column; +} + +.gw-tab-contents > DIV { + flex-grow: 1; +} + +.gw-tab-contents.gw-noborder { + border: none; +} + +.gw-checkbox,.gw-radiobutton { + display: flex; + flex-flow: row; + min-height: 2em; +} + +.gw-checkbox-label,.gw-radiobutton-label { + display: flex; + flex-flow: row; + align-items: center; +} + +.gw-checkbox.gw-disabled > .gw-checkbox-label { + opacity: 0.5; +} + +.gw-radiobutton.gw-disabled > .gw-checkbox-label { + opacity: 0.5; +} + +.gw-checkbox-toggle,.gw-radiobutton-toggle { + margin: 0; + margin-right: 0.5em; +} + +.gw-textbox { + display: inline-block; + position: relative; + min-height: 2em; +} + +.gw-textbox.gw-noborder { + border: none; +} + +.gw-textbox INPUT { + width: 100%; + min-height: 2em; + box-sizing: border-box; + padding: 0 0.25em; + vertical-align: middle; +} + +.gw-textbox-clear { + position: absolute; + right: 0; + display: inline-block; + z-index: 2; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAQ0lEQVQYlWNmYGCwY2BgiGdgYDjIgArqGBgY/iMz6rBI1mETwKYBRRGKJBO6KlwArxV4HckMZTxgYGBoQlJwECp+AADQjhqXl5Xe2AAAAABJRU5ErkJggg=='); + background-position: center; + background-repeat: no-repeat; + height: 2rem; + width: 2rem; + /*margin-left: -2rem;*/ + /*pointer-events: none;*/ + vertical-align: middle; + border: none; + /*overflow: hidden;*/ +} + +.gw-combobox { + display: inline-block; + position: relative; + min-height: 2em; +} + +.gw-combobox SELECT { + height: 100%; + z-index: 1; + opacity: 0; + float: right; +} + +.gw-combobox INPUT { + width: 100%; + min-height: 2em; + box-sizing: border-box; + padding: 0 2.25em 0 0.25em; + vertical-align: middle; +} + +.gw-combobox.gw-noborder INPUT { + border: none; +} + +.gw-combobox-arrow { + display: inline-block; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAI0lEQVQYlWNgoAZoYGBg+I8DN+BT1MCABhrwSSIrwilJHgAAbCcP9dwIQGIAAAAASUVORK5CYII='); + background-position: center; + background-repeat: no-repeat; + height: 2rem; + width: 2rem; + margin-left: -2rem; + /*pointer-events: none;*/ + vertical-align: middle; + border: none; + overflow: hidden; +} + +.gw-selectbox { + display: inline-block; + position: relative; + min-height: 2em; +} + +.gw-selectbox SELECT { + width: 100%; + height: 100%; +} + +.gw-selectbox.gw-noborder SELECT { + border: none; +} + +.gw-window-container { +} + +.gw-window { + position: fixed; + top: 0; + left: 0; + border: solid 1px white; + box-shadow: 0 0 0.5em black; + border-radius: 0.5em; + background: white; + z-index: 10; + overflow: hidden; +} + +.gw-popup { + border: solid 1px #C0C0C0; + border-radius: 0; + box-shadow: none; +} + +.gw-window-titlebar { + display: flex; + flex-flow: row; + background: #2980B9; + font-weight: bold; + border-top-left-radius: 0.5em; + border-top-right-radius: 0.5em; + padding: 0.25em; +} + +.gw-window-titlebar.gw-deactivated { + background: white; +} + +.gw-window-title { + flex-grow: 1; + color: white; + padding: 0 1em; + pointer-events: none; +} + +.gw-window-titlebar.gw-deactivated > DIV.gw-window-title { + color: gray; +} + +.gw-window-button { + width: 1.5em; + height: 1.5em; + padding: 0; + display: inline-flex; + justify-content: center; +} + +.gw-window-button > IMG { + width: 0.8em; + height: 0.8em; + margin: auto; +} + +/*@-moz-document url-prefix() { + .gw-window-button { + display: inline-flex; + }; +}*/ + +.gw-window-contents { + display: flex; + flex-flow: column; + flex-grow: 1; + padding: 0.5em; +} + +.gw-popup > .gw-window-contents { + padding: 0; +} + + +.gw-label { + display: flex; + align-items: center; + overflow: hidden; + white-space: nowrap; +} + +.gw-label.gw-disabled { + opacity: 0.5; +} + +#gw-modal { + display: none; + position: fixed; + left: 0; + right: 0; + width: 100%; + height: 100%; + background: black; + opacity: 0.05; +} + +#gw-lock { + display: none; + position: fixed; + left: 0; + right: 0; + width: 100%; + height: 100%; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAFUlEQVQYlWNkYGAwYMADmPBJDh8FADoIAECNjQSqAAAAAElFTkSuQmCC'); + opacity: 0; +} + +#gw-lock-animation { + position: absolute; + width: 13.5em; + height: 3em; + margin: auto; + position: absolute; + top: 0; left: 0; bottom: 0; right: 0; +} + +.gw-lock-circle { + background-color:#CCC; + float: left; + height: 3em; + width: 3em; + border-radius: 3em; + margin-left: 0.75em; + margin-right: 0.75em; + animation-name: bounce_circle; + animation-duration: 1.5s; + animation-iteration-count: infinite; + animation-direction: linear; + opacity: 0.3; +} + +#gw-lock-1 { + animation-delay: .3s; + } +#gw-lock-2 { + animation-delay: .7s; +} +#gw-lock-3 { + animation-delay: .9s; +} +@-webkit-keyframes bounce_circle { + 0% { opacity: 0.3; } + 50% { opacity: 1; background-color: #888; } + 100% { opacity: 0.3; } +} + + +.gw-textarea { + font: inherit; + resize: none; +} + +.gw-textarea.gw-noborder { + border: none; +} + +.gw-menubar { + overflow: visible !important; +} + +.gw-menu-title { + display: inline-table; + padding: 0.5em; + padding-top: 0.25em; + cursor: default; + border-top: solid 1px transparent; + border-left: solid 1px transparent; + border-right: solid 1px transparent; + /*margin-top: 0.25em; + margin-bottom: 0.25em;*/ +} + +.gw-menu:hover > .gw-menu-title { + border-top: solid 1px #C0C0C0; + border-left: solid 1px #C0C0C0; + border-right: solid 1px #C0C0C0; + box-shadow: 0 0 0.25em #C0C0C0; + background: white; + z-index: 1002; +} + +.gw-submenu { + display: none; + position: absolute; + border: solid 1px #C0C0C0; + background: white; + box-shadow: 0 0.125em 0.25em #C0C0C0; + margin-top: -0.25em; + z-index: 1003; +} + +.gw-menuitem > .gw-menu > .gw-submenu { + margin-top: 0; + top: 0; + left: 100%; +} + +.gw-menu:hover > .gw-submenu { + display: table; +} + +.gw-menu-tape { + display: none; + position: relative; + background: white; + z-index: 1004; + height: 4px; + margin-top: -4px; + margin-left: 1px; + margin-right: 1px; + top: -2px; +} + +.gw-menu:hover > .gw-menu-tape { + display: block; +} + +.gw-submenu > div:hover { + background: #E0E0E0; +} + +.gw-menuitem { + display: flex; + flex-flow: column; +} + +.gw-menuitem > div { + display: table-cell !important; + vertical-align: middle; +} + +.gw-menuitem-text { + flex-grow: 1; + padding: 0.125em 0.25em; + white-space: nowrap; +} + +.gw-menuitem-icon { + padding: 0.125em 0.25em; +} + +.gw-menuitem-icon > IMG { + vertical-align: middle; +} + +.gw-menuitem-shortcut { + text-align: right; + padding: 0.125em 1em; +} + +.gw-separator { + position: relative; + padding: 0 !important; + pointer-events: none; +} + +.gw-separator-hline { + position: absolute; + display: flex; + flex-flow: row; + align-items: center; + height: inherit; + width: 100%; +} + +.gw-separator-hline > div { + height: 1px; + width: 100%; + background: #C0C0C0; +} + +.gw-separator-vline { + position: absolute; + display: flex; + flex-flow: column; + align-items: center; + height: 100%; + width: inherit; +} + +.gw-separator-vline > div { + width: 1px; + height: 100%; + background: #C0C0C0; +} + +.gw-submenu > div { + display: flex !important; + cursor: default; + padding: 0.25em 1em; + margin: 0; +} + +.gw-submenu > .gw-menuitem { + display: table-row !important; + table-layout: fixed; + cursor: default; + padding: 0; + margin: 0; +} + +.gw-submenu > .gw-separator { + display: table-row !important; + cursor: default; + margin: 0; + width: auto; + height: 1em; +} + +.gw-menuitem > .gw-menu { + display: table-cell !important; + position: relative; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAJElEQVQYlWNgQIAGBgLgPyFF/wkp+o9NERMhe0m2AqckAy5JALuADHot/KmNAAAAAElFTkSuQmCC'); + background-position: center; + background-repeat: no-repeat; + width: 1em; +} + +.gw-spinbox { + min-height: 2em; + padding: 0 0.25em; +} + +.gw-spinbox.gw-noborder { + border: none; +} + +.gw-expander-header + DIV { + margin-top: 0.5em; +} + +.gw-expander-header > IMG { + vertical-align: middle; +} + +.gw-expander-header > DIV { + display: inline-table; + vertical-align: middle; +} + +.gw-expander-border { + border: solid 1px #C0C0C0; + padding: 0.5em; +} + +.gw-table { + position: relative; + min-height: 4em; + border: solid 1px #C0C0C0; + cursor: default; +} + +.gw-table.gw-noborder { + border: none; +} + +/*.gw-table-header { + height: 1.5em; + background-color: yellow; +}*/ + +.gw-table-contents { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: auto; +} + +.gw-table > DIV > TABLE { + border-collapse: collapse; +} + +.gw-table > DIV > TABLE > THEAD > TR > TH { + text-align: left; + background-color: #E0E0E0; + padding: 0.25em 0.5em; + vertical-align: top; + border-bottom: solid 1px #C0C0C0; +} + +.gw-table > DIV > TABLE > THEAD > TR > TH:last-child { + border-right: none; +} + +.gw-table > DIV > TABLE > TBODY > TR > TD { + /*border-right: solid 1px #C0C0C0;*/ + padding: 0.1em 0.5em; + vertical-align: top; + border-bottom: solid 1px #F0F0F0; +} + +.gw-table > DIV > TABLE > TBODY > TR:hover { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAFUlEQVQYlWNkYGDgYcADmPBJDh8FABV4ABwe4TuyAAAAAElFTkSuQmCC'); +} + +/*.gw-table > DIV > TABLE > TBODY > TR > TD:last-child { + border-right: none; +} + +.gw-table > DIV > TABLE > TBODY > TR:last-child > TD { + border-bottom: solid 1px #C0C0C0; +}*/ + +/*.gw-table > DIV > TABLE > TBODY > TR:nth-child(even) { + background-color: #F8F8F8; +}*/ + +.gw-table-more { + position: absolute; + padding-left: 4px; +} + +IMG.gw-table-sort { + position: absolute; + right: 0.1em; + top: 0.25em; +} + +TH.gw-table-sortable { + position: relative; + cursor: pointer; +} + +TH.gw-table-sortable > DIV { + margin-right: 2em; +} + +TH.gw-table-sortable:hover { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAFUlEQVQYlWNkYGDgYcADmPBJDh8FABV4ABwe4TuyAAAAAElFTkSuQmCC'); +} + +.gw-waiting { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: url('//gw-waiting.gif'); + background-position: center; + background-repeat: no-repeat; +} + +.gw-scrollview { + position: relative; +} + +.gw-scrollview > DIV { + position: absolute !important; + overflow: auto; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.gw-datebox { + position: relative; + display: inline-block; + min-height: 2em; +} + +.gw-datebox INPUT { + width: 100%; + min-height: 2em; + box-sizing: border-box; + padding: 0 2.25em 0 0.25em; + vertical-align: middle; +} + +.gw-datebox.gw-noborder INPUT { + border: none; +} + +TABLE.gw-calendar { + width: 100%; +} + +TABLE.gw-calendar > TBODY > TR > TH { + padding: 0.25em 0.5em; + background: #E0E0E0; +} + +TABLE.gw-calendar > TBODY > TR > TD { + padding: 0.25em 0.5em; + text-align: center; + cursor: pointer; + border: solid 1px transparent; +} + +TABLE.gw-calendar > TBODY > TR > TD:hover { + outline: solid 1px #C0C0C0; +} + +TABLE.gw-calendar > TBODY > TR > TD.gw-date-disabled { + color: #C0C0C0; +} + +TABLE.gw-calendar > TBODY > TR > TD.gw-date-today { + font-weight: bold; + border: solid 1px black; +} + +TABLE.gw-calendar > TBODY > TR > TD.gw-date-current { + background-color: #2980B9; + color: white; +} + +.gw-file-input { + position: absolute; + left: -2000px; +} + +.gw-progressbar { + position: relative; + border: solid 1px #C0C0C0; + min-height: 0.5em; + border-radius: 0.25em; +} + +.gw-progressbar.gw-noborder { + border: solid 1px transparent; +} + +.gw-progressbar-bar { + position: absolute; + height: 100%; + background: #C0C0C0; + border: solid 1px white; + border-radius: 0.25em; + z-index: -1; +} + +.gw-progressbar-label { + position: absolute; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +.gw-form { + flex-grow: 1; +} + +.gw-image { + border: solid 1px #C0C0C0; + box-sizing: border-box; +} + +.gw-image.gw-noborder { + border: none; +} + +.gw-table-row-selected { + background-color: #2980B9 !important; + color: white !important; +} + +.gw-table-row-selected > TD { + color: white !important; +} + +.gw-ac-suggestions { + text-align: left; + cursor: default; + border: 1px solid #C0C0C0; + border-top: 0; + background: white; + /*box-shadow: -1px 1px 3px rgba(0,0,0,.1);*/ + + /* core styles should not be changed */ + position: absolute; + display: none; + z-index: 9999; + max-height: 12em; + overflow: hidden; + overflow-y: auto; + box-sizing: border-box; +} + +.gw-ac-suggestion { + position: relative; + padding: 0 0.5em; + line-height: 1.5em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.gw-ac-suggestion b { + font-weight: normal; + color: #2980B9; +} + +.gw-ac-suggestion.selected { + background: #E0E0E0; +} + +.gw-mandatory { + position: absolute; + box-shadow: 0 0 0.5em red; + top: 0; + left: 0; + z-index: -1; +} + diff --git a/comp/src/gb.web/.component b/comp/src/gb.web/.component new file mode 100644 index 00000000..b2e0e97d --- /dev/null +++ b/comp/src/gb.web/.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.web +Version=3.12.90 +Authors=Benoît Minisini +Include=gb.util.web diff --git a/comp/src/gb.web/.directory b/comp/src/gb.web/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/comp/src/gb.web/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/comp/src/gb.web/.hidden/Session_opt b/comp/src/gb.web/.hidden/Session_opt new file mode 100644 index 00000000..a8c5ee0c --- /dev/null +++ b/comp/src/gb.web/.hidden/Session_opt @@ -0,0 +1,527 @@ +' Gambas module file + +Export +'CREATE + +Private $sId As String +Private $sPath As String +Private $hFile As File + +Private $cKey As Collection +Private $cVal As Collection + +Private $bModify As Boolean +Private $eTimeout As Float = 1 +Private $eStartup As Float +Private $hLock As File +Private $sPrefix As String +Private $bUnique As Boolean +Private $sCookiePath As String +Private $bInit As Boolean + +Property Id As String +Property Timeout As Float +Property Prefix As String +Property Unique As Boolean +Property Modified As Boolean +Property CookiePath As String + +Private Sub LockSession() + + Dim iInd As Integer + + For iInd = 1 To 10 + Try $hLock = Lock $sPath & ".lock" + If Not Error Then Return + Sleep 0.1 + Next + + Main.Log("LockSession: unable to lock session") + +End + +Private Sub UnlockSession() + + Try Unlock #$hLock + +End + +' Session file format +' +' 0 Float Session timestamp +' 8 Float Session timeout +' 16 Integer Index offset (IDX) +' 20 Variant[] Session values +' : +' : +' : +' IDX Collection Session index +' String Cookie path + +Private Sub SaveSession() + + Dim hFile As File + Dim sTemp As String + Dim eNow As Float + Dim iPos As Integer + Dim iPosOffset As Integer + Dim vVal As Variant + Dim sKey As String + Dim aRemove As New String[] + + 'PRINT "

    Save session

    " + 'PRINT "

    "; $sId; "
    "; $bModify; "
    "; $sPath; "
    "; $cVal.Count + + Main.Log("SaveSession: " & Application.Request) + + If Not $sId Then Return + + 'Startup time is always modified + If Not $bModify Then + + eNow = Now + If (eNow - $eStartup) < (1 / 86400) Then Return + + Main.Log("SaveSession: " & $sId & ": update timestamp") + + LockSession + + 'Main.Log("SaveSession: " & $sPath & ": Just update time stamp : " & Exist($sPath)) + hFile = Open $sPath For Write + Write #hFile, Now As Float + Close #hFile + + UnlockSession + + Else + + sTemp = Temp$() + + Main.Log("SaveSession: " & $sId) + + 'Main.Log("SaveSession: " & $sPath & ": Save all session : " & Exist($sPath)) + hFile = Open sTemp For Write Create + + Write #hFile, Now As Float + Write #hFile, $eTimeout As Float + + iPosOffset = Seek(hFile) + Write #hFile, 0 As Integer + + For Each iPos In $cKey + + sKey = $cKey.Key + + Main.Log(sKey & " " & CStr(iPos)) + + If Not $cVal.Exist(sKey) Then + If iPos Then + Seek #$hFile, iPos + vVal = Read #$hFile As Variant + $cVal[sKey] = vVal + Else + aRemove.Add(sKey) + Main.Log("DEL : " & sKey) + Continue + Endif + Else + vVal = $cVal[sKey] + Endif + + $cKey[sKey] = Seek(hFile) + Main.Log(Seek(hFile) & ": " & sKey & " = " & Left(Str(vVal), 128)) + Write #hFile, vVal As Variant + Next + + For Each sKey In aRemove + $cKey.Remove(sKey) + Next + + iPos = Seek(hFile) + Write #hFile, $cKey As Collection + + Write #hFile, $sCookiePath As String + + Seek #hFile, iPosOffset + Write #hFile, iPos As Integer + + Close #hFile + + LockSession + Try Kill $sPath + Move sTemp To $sPath + UnlockSession + + Endif + + Main.Log("SaveSession: OK") + $bModify = False + + ' If Exist($sPath) Then + ' Main.Log("SaveSession: " & $sPath & " (" & Stat($sPath).Size & ")") + ' Else + ' Main.Log("SaveSession: " & $sPath & " NOT FOUND!") + ' Endif + +Catch + + Main.Log("SaveSession: " & Error.Where & ": " & Error.Text) + +End + + +Private Sub CheckSession() As Boolean + + 'Main.Log("TimeOut: " & CStr(CDate($eTimeOut)) & " Startup: " & CStr(CDate($eStartup)) & " Now: " & CStr(Now)) + Return (CFloat(Now) - $eStartup) >= $eTimeout + +End + + +Private Sub LoadSession() + + Dim hFile As File + Dim iPos As Integer + + Main.Log("LoadSession: " & $sPath) + ' Main.Log("System.Language = " & System.Language) + ' Main.Log("System.Charset = " & System.Charset) + ' Main.Log(System.Backtrace.Join(" ")) + + If Not Exist($sPath) Then Goto _ABANDON + + 'Main.Log("LoadSession: #2") + + LockSession ' Pour être sur d'ouvrir un fichier complet + + hFile = Open $sPath + + 'IF ReadValue() = 1 THEN GOTO _ABANDON + $eStartup = Read #hFile As Float + $eTimeout = Read #hFile As Float + If CheckSession() Then + Main.Log("LoadSession: timeout: " & CStr(CDate($eTimeOut)) & " Startup: " & CStr(CDate($eStartup)) & " Now: " & CStr(Now)) + Goto _ABANDON + Endif + + iPos = Read #hFile As Integer + Seek #hFile, iPos + $cKey = Read #hFile As Collection + $cVal = New Collection + Try $sCookiePath = Read #hFile As String + $bModify = False + + $hFile = hFile + + UnlockSession + + Main.Log("LoadSession: OK") + + For Each $cKey + Main.Log($cKey.Key) + Next + + Return + +Catch + + Main.Log("LoadSession: " & $sId & ": " & Error.Where & ": " & Error.Text) + 'Try File.Save(File.Dir($sPath) &/ "session.error", Error.Where & ": " & Error.Text & "\n") + 'Try Kill $sPath & ".error" + 'Try Copy $sPath To $sPath & ".error" + +_ABANDON: + + Main.Log("LoadSession: abandon") + + 'Main.Log("LoadSession: #4") + + If hFile Then + Close #hFile + UnlockSession + Endif + + Try Object.Call(Application.Startup, "Session_Abandon") + + Try Kill $sPath + Try Kill $sPath & ".lock" + $sId = "" + +End + +Private Sub GetPath(Optional sId As String) As String + + Return "/tmp/gambas." & System.User.Id &/ "session" &/ sId + +End + +Private Sub CheckUnique() + + Dim hLock As File + Dim sPrefix As String + Dim aKill As String[] + Dim sKill As String + + If Not $bUnique Then Return + + Try hLock = Lock GetPath(".unique.lock") + If Error Then Return + + If $sPrefix Then + sPrefix = $sPrefix + Else + sPrefix = CGI["REMOTE_ADDR"] + Endif + + Do + aKill = Dir(GetPath(), sPrefix & ":*") + If aKill.Count = 0 Then Break + For Each sKill In aKill + Try Kill GetPath(sKill) + Next + Loop + + Unlock #hLock + +End + + +Private Sub CreateSession() + + Dim iInd As Integer + Dim sPrefix As String + + sPrefix = "/tmp/gambas." & System.User.Id + + Repeat + $sId = "" + For iInd = 1 To 6 + $sId &= Hex$(Int(Rnd(65536)), 4) + Next + If $sPrefix Then + $sId = $sPrefix & ":" & $sId + Else + $sId = CGI["REMOTE_ADDR"] & ":" & $sId + Endif + '$sId = "TEST" + $sPath = GetPath($sId) 'sPrefix &/ "session" &/ $sId + Until Not Exist($sPath) + + 'TRY MKDIR $sPrefix + Try Mkdir GetPath() 'sPrefix &/ "session" + + CheckUnique + + Main.Log("CreateSession: " & $sId) + Response.SetCookie("SESSION", $sId, "", GetCookiePath(),, True) + + $cVal = New Collection + $cKey = New Collection + $eStartup = Now + +End + +Private Sub SelectSession() + + $sPath = GetPath($sId) '"/tmp/gambas." & System.User.Id &/ "session" &/ $sId + + LoadSession + +End + + +Private Sub Init() + + If $bInit Then Return + + $bInit = True + + Main.AllowLog = Exist("/tmp/session.debug") + + 'Main.Log("Session.Init") + + 'Main.Log("HTTP_COOKIE = " & CGI["HTTP_COOKIE"] & " / " & Env["HTTP_COOKIE"]) + $sId = Request.Cookies["SESSION"] + Main.Log("--------------- Init: Cookie = " & $sId) + '$sId = "9E2496B3AB6DDED93ABE6F0CF6E071B3@" + If Not $sId Then Return + + SelectSession + +'CATCH +' CGI.Error(Error.Where & ": " & Error.Text) + +End + +Public Sub _exit() + + Main.Log("Session._exit") + + SaveSession + +End + +Private Sub GetCookiePath() As String + + Dim sPath As String + + If $sCookiePath Then Return $sCookiePath + + sPath = CGI["SCRIPT_NAME"] + If sPath = "/." Then sPath = "/" + Return sPath + +End + +Public Sub Abandon() + + 'Main.Log("Abandon") + + If Not $sId Then Return + + Try Object.Call(Application.Startup, "Session_Abandon") + + Try Kill $sPath + Try Kill $sPath & ".lock" + Response.RemoveCookie("SESSION", $sId, "", GetCookiePath()) + $sId = "" + $sPath = "" + $cKey = Null + $cVal = Null + +End + +Public Sub _get(Key As String) As Variant + + Init + + If Not $cVal Then Return + + If Not $cVal.Exist(Key) Then + If $cKey.Exist(Key) Then + Seek #$hFile, $cKey[Key] + $cVal[Key] = Read #$hFile As Variant + Endif + Endif + + Return $cVal[Key] + +End + +Public Sub _put(Value As Variant, Key As String) + + Init + + If Not $cVal Then CreateSession + + $cVal[Key] = Value + + If Not IsNull(Value) Then + $cKey[Key] = 0 + Else 'If $cKey.Exist(Key) And If $cKey[Key] = 0 Then + $cKey.Remove(Key) + Endif + + $bModify = True + + Main.Log(Key & " = " & Str(Value)) + +End + + +Private Function Id_Read() As String + + Init + Return $sId + +End + + +Private Sub Id_Write(Value As String) + + Init + Abandon + $sId = Value + SelectSession + +End + +Private Function Timeout_Read() As Float + + Init + Return Int($eTimeout * 86400 + 0.5) + +End + +Private Sub Timeout_Write(Value As Float) + + Init + $eTimeout = Value / 86400 + +End + +Public Sub Save() + + Init + SaveSession + +End + +Public Sub Load() + + Init + LoadSession + +End + + +Private Function Prefix_Read() As String + + Return $sPrefix + +End + +Private Sub Prefix_Write(Value As String) + + $sPrefix = Value + +End + +Private Function Unique_Read() As Boolean + + Return $bUnique + +End + +Private Sub Unique_Write(Value As Boolean) + + Init + $bUnique = Value + CheckUnique + +End + +Private Function Modified_Read() As Boolean + + Return $bModify + +End + +Private Sub Modified_Write(Value As Boolean) + + Init + $bModify = Value + +End + +Private Function CookiePath_Read() As String + + Return $sCookiePath + +End + +Private Sub CookiePath_Write(Value As String) + + $sCookiePath = Value + +End diff --git a/comp/src/gb.web/.icon.png b/comp/src/gb.web/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a9254f949f86671b58a744a6a93623576acc5217 GIT binary patch literal 10569 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" + Print "

    CGI environment

    " + Print "" + For Each sName In Application.Env + Print "" + Next + Print "
    NameValue
    "; Html(sName); ""; Html(CGI[sName]); "
    " + Response.End + Quit + +End + + +Public Sub Error(Text As String) + + Output To Default + + Response.Buffered = False + Response.Begin + Print "" + Print "

    "; ("Error in CGI script"); "

    " + Print "
    "; Text; "
    " + Response.End + Quit + +End diff --git a/comp/src/gb.web/.src/FileSessionManager.class b/comp/src/gb.web/.src/FileSessionManager.class new file mode 100644 index 00000000..3f5dd3f5 --- /dev/null +++ b/comp/src/gb.web/.src/FileSessionManager.class @@ -0,0 +1,316 @@ +' Gambas class file + +Inherits SessionManager + +Private $sPath As String +Private $hLock As File + +Private $eStartup As Float + +Private $cVal As Collection + +Public Sub GetPath(Optional sId As String) As String + + Return "/tmp/gambas." & System.User.Id &/ "session" &/ sId + +End + +Public Sub _new(sId As String) + + $sPath = GetPath(sId) '"/tmp/gambas." & System.User.Id &/ "session" &/ $sId + +End + +Private Sub LockSession() + + Dim iInd As Integer + + For iInd = 1 To 10 + Try $hLock = Lock $sPath & ".lock" + If Not Error Then Return + Sleep 0.1 + Next + + Main.Log("LockSession: unable to lock session") + +End + +Private Sub UnlockSession() + + Try Unlock #$hLock + +End + +Private Sub CheckSession() As Boolean + + 'Main.Log("TimeOut: " & CStr(CDate($eTimeOut)) & " Startup: " & CStr(CDate($eStartup)) & " Now: " & CStr(Now)) + Return (CFloat(Now) - $eStartup) >= Me.Timeout + +End + +Public Sub Load() As Boolean + + Dim hFile As File + + Main.Log("LoadSession: " & $sPath) + ' Main.Log("System.Language = " & System.Language) + ' Main.Log("System.Charset = " & System.Charset) + ' Main.Log(System.Backtrace.Join(" ")) + + If Not Exist($sPath) Then Goto _ABANDON + + 'Main.Log("LoadSession: #2") + + LockSession + hFile = Open $sPath + + Me.Size = Lof(hFile) + + Input From #hFile + + 'IF ReadValue() = 1 THEN GOTO _ABANDON + $eStartup = Read As Float + Me.Timeout = Read As Float + If CheckSession() Then + Main.Log("LoadSession: timeout: " & CStr(CDate(Me.TimeOut)) & " Startup: " & CStr(CDate($eStartup)) & " Now: " & CStr(Now)) + Goto _ABANDON + Endif + + $cVal = Read #hFile As Variant + Try Session.CookiePath = Read #hFile As String + '$cVal = ReadValue() + Me.Modified = False + Close #hFile + Input From Default + UnlockSession + + '$cNew = New Collection + + Main.Log("LoadSession: OK") + Return + +Catch + + Main.Log("LoadSession: " & Session.Id & ": " & Error.Where & ": " & Error.Text) + 'Try File.Save(File.Dir($sPath) &/ "session.error", Error.Where & ": " & Error.Text & "\n") + 'Try Kill $sPath & ".error" + 'Try Copy $sPath To $sPath & ".error" + +_ABANDON: + + Main.Log("LoadSession: abandon") + + 'Main.Log("LoadSession: #4") + + If hFile Then + Close #hFile + Input From Default + UnlockSession + Endif + + Session.Abandon + Return True + +End + +Public Sub Save() + + Dim hFile As File + Dim sTemp As String + Dim eNow As Float + + 'PRINT "

    Save session

    " + 'PRINT "

    "; $sId; "
    "; $bModify; "
    "; $sPath; "
    "; $cVal.Count + + 'Startup time is always modified + If Not Me.Modified Then + + eNow = Now + If (eNow - $eStartup) < (1 / 86400) Then Return + + Try Main.Log("SaveSession: " & Session.Id & ": update timestamp") + + LockSession + + 'Main.Log("SaveSession: " & $sPath & ": Just update time stamp : " & Exist($sPath)) + hFile = Open $sPath For Write + Write #hFile, Now As Float + Close #hFile + + UnlockSession + + Else + + sTemp = Temp$() + + Try Main.Log("SaveSession: " & Session.Id) + + LockSession + + ' hFile = Open $sPath + ' Seek #hFile, SizeOf(gb.Float) * 2 + ' $cVal = Read #hFile As Variant + ' Close #hFile + ' + ' For Each vVal In $cNew + ' sKey = $cNew.Key + ' If TypeOf(vVal) = gb.Object And If vVal = $cNew Then + ' $cVal[sKey] = Null + ' Else + ' $cVal[sKey] = vVal + ' Endif + ' Next + + 'Main.Log("SaveSession: " & $sPath & ": Save all session : " & Exist($sPath)) + hFile = Open sTemp For Output Create + + Write #hFile, Now As Float + Write #hFile, Me.Timeout As Float + + 'Output To #hFile + 'WriteValue($cVal) + Write #hFile, $cVal As Variant + Write #hFile, Session.CookiePath As String + 'Output To Default + + Me.Size = Lof(hFile) + Close #hFile + + Try Kill $sPath + Move sTemp To $sPath + + UnlockSession + + Me.Modified = False + + Endif + + Try Main.Log("SaveSession: OK") + + ' If Exist($sPath) Then + ' Main.Log("SaveSession: " & $sPath & " (" & Stat($sPath).Size & ")") + ' Else + ' Main.Log("SaveSession: " & $sPath & " NOT FOUND!") + ' Endif + +Catch + + Try Main.Log("SaveSession: " & Error.Where & ": " & Error.Text) + +End + +Public Sub CheckUnique() + + Dim hLock As File + Dim sPrefix As String + Dim aKill As String[] + Dim sKill As String + Dim sDir As String + + If Session.Prefix Then + sPrefix = Session.Prefix + Else + sPrefix = CGI["REMOTE_ADDR"] + Endif + + sDir = GetPath() + + If Session.Unique Then + + Try hLock = Lock GetPath(".unique.lock") + If Error Then Return + + Do + aKill = Dir(sDir, sPrefix & ":*") + If aKill.Count = 0 Then Break + For Each sKill In aKill + Try Kill sDir &/ sKill + Next + Loop + + Unlock #hLock + + Else + + ' dNow = Now + ' For Each sFile In Dir(sDir, sPrefix & ":*") + ' Try hStat = Stat(sDir &/ sFile) + ' If Error Then Continue + ' If DateDiff(hStat.LastModified, dNow, gb.Hour) >= 24 Then + ' Try Kill sDir &/ sFile + ' Endif + ' Next + + Endif + +End + + +Public Sub Create() + + Dim sPrefix As String + + sPrefix = "/tmp/gambas." & System.User.Id + + Repeat + Session._MakeId() + $sPath = GetPath(Session.Id) 'sPrefix &/ "session" &/ $sId + Until Not Exist($sPath) + + 'TRY MKDIR $sPrefix + Try Mkdir GetPath() 'sPrefix &/ "session" + + CheckUnique + + $cVal = New Collection + $eStartup = Now + +End + +Public Sub Abandon() + + $sPath = "" + $cVal = Null + + Try Kill $sPath + Try Kill $sPath & ".lock" + +End + +Public Sub _get(Key As String) As Variant + + If $cVal Then Return $cVal[Key] + +End + +Public Sub Exist(Key As String) As Boolean + + If $cVal Then Return $cVal.Exist(Key) + +End + + +Public Sub _put(Value As Variant, Key As String) + + 'If Not $cVal Then CreateSession + $cVal[Key] = Value + Me.Modified = True + +End + +Public Sub GetKeys() As String[] + + Dim aKey As New String[] + + For Each $cVal + aKey.Add($cVal.Key) + Next + Return aKey + +End + +Public Sub Exit() + + Save() + +End diff --git a/comp/src/gb.web/.src/Main.module b/comp/src/gb.web/.src/Main.module new file mode 100644 index 00000000..58d26771 --- /dev/null +++ b/comp/src/gb.web/.src/Main.module @@ -0,0 +1,82 @@ +' Gambas module file + +Public AllowLog As Boolean + +Public Sub Log(sMsg As String, Optional bForce As Boolean) + + Dim hFile As File + + If Not AllowLog And If Not bForce Then Return + + hFile = Open "/tmp/session.log" For Append + Print #hFile, sMsg + Close #hFile + +End + +Public Sub DecodeURL(sUrl As String, aField As String[], cVal As Collection) + + Dim sElt As String + Dim aElt As String[] + Dim sKey As String + Dim sVal As String + + sUrl = Trim(sUrl) + + For Each sElt In Split(sUrl, Request.Delimiter) + + If Not sElt Then Continue + + aElt = Split(sElt, "=") + If aElt.Count = 2 Then + sKey = CGI.Decode(aElt[0]) + If Not sKey Then Continue + sVal = CGI.Decode(aElt[1]) + Else + sKey = CGI.Decode(aElt[0]) + If Not sKey Then Continue + sVal = "" + Endif + + If Not aField.Exist(sKey) Then + aField.Add(sKey) + cVal[sKey] = sVal + Else If sVal Then + 'IF TypeOf(cVal[sKey]) = gb.String THEN + ' cVal[sKey] = [CStr(cVal[sKey]), sVal] + 'ELSE + ' cVal[sKey].Add(sVal) + 'ENDIF + cVal[sKey] &= "\n" & sVal + Endif + + Next + +' Catch +' +' Error.Raise("Malformed URL:" & Error.Where & ": " & Error.Text) +' +End + +Public Sub GetAbsoluteURL(sPath As String) As String + + Dim sReq As String + Dim sPort As String + + sReq = Application.Protocol & "://" & Application.Host + + sPort = Application.Port + If sPort And If sPort <> "80" Then sReq &= ":" & sPort + + Return sReq &/ sPath + +End + + +Public Sub Main() + + Session.Type = "sqlite" + Session["test"] = "value" + Session.Save + +End diff --git a/comp/src/gb.web/.src/Request.module b/comp/src/gb.web/.src/Request.module new file mode 100644 index 00000000..a1b2b142 --- /dev/null +++ b/comp/src/gb.web/.src/Request.module @@ -0,0 +1,268 @@ +' Gambas module file + +Export + +Property Read Fields As String[] +Property Read Cookies As Collection +Property Read Method As String +Property Path As String +Property Query As String +Property Read Files As Collection +Property Delimiter As String +Property Read InternetExplorer As Integer +Property Read Referer As String +Property Read UserAgent As String +Property Read Language As String +Property TempDir As String +Property MaxFileSize As Long + +Property Read Get As _Request_Get +Property Read Post As _Request_Post +Property Debug As Boolean + +Private $cCookies As Collection +Private $cVal As Collection +Private $cKey As Collection +Private $aField As String[] +Private $sDelimiter As String = "&" +Private $iIE As Integer +Private $bDecoded As Boolean +Private $bPost As Boolean +Private $bDebug As Boolean +Private $sTempDir As String +Private $iMaxFileSize As Long = 128 * 1048576 + +Private Sub Decode() + + If $bDecoded Then Return + $bPost = CGI["REQUEST_METHOD"] = "POST" + $bDecoded = True + +End + +Public Sub _init() + + Dim sAgent As String = CGI["HTTP_USER_AGENT"] + Dim aScan As String[] = Scan(sAgent, "*; MSIE *;*") + Try $iIE = Val(aScan[1]) + +End + +Public Sub _get(Key As String) As String + + Dim sVal As String + + If $cKey And If $cKey.Exist(Key) Then Return $cVal[Key] + + Decode + + If $bPost Then + sVal = _Request_Post[Key] + If sVal Then Return sVal + Endif + + Return _Request_Get[Key] + +End + +Public Sub _put(Value As String, Key As String) + + If Not $cVal Then + $cVal = New Collection + $cKey = New Collection + Endif + + $cVal[Key] = Value + $cKey[Key] = True + +End + +Private Function Fields_Read() As String[] + + Dim sField As String + + Decode + + If Not $aField Then + If Not $bPost Then + $aField = _Request_Get.Fields + Else + $aField = _Request_Get.Fields.Copy() + For Each sField In _Request_Post.Fields + If Not $aField.Exist(sField) Then $aField.Add(sField) + Next + Endif + Endif + + Return $aField + +End + +Private Function Cookies_Read() As Collection + + Dim sCookie As String + Dim aCookie As String[] + + If Not $cCookies Then + + $cCookies = New Collection + + For Each sCookie In Split(CGI["HTTP_COOKIE"], ";") + sCookie = Trim(sCookie) + aCookie = Split(sCookie, "=") + Try $cCookies[aCookie[0]] = aCookie[1] + Next + + Endif + + Return $cCookies + +End + + +Private Function Method_Read() As String + + Return CGI["REQUEST_METHOD"] + +End + +Private Function Path_Read() As String + + Return CGI["PATH_INFO"] + +End + +Private Function Query_Read() As String + + Return CGI["QUERY_STRING"] + +End + +Private Function Files_Read() As Collection + + Return _Request_Post.Files + +End + +Public Sub Exist(Field As String) As Boolean + + Fields_Read() + Return $aField.Exist(Field) + +End + + +Private Function Delimiter_Read() As String + + Return $sDelimiter + +End + +Private Sub Delimiter_Write(Value As String) + + $sDelimiter = Value + +End + +Private Function InternetExplorer_Read() As Integer + + Return $iIE + +End + +Private Function Get_Read() As _Request_Get + + Return _Request_Get + +End + +Private Function Post_Read() As _Request_Post + + Return _Request_Post + +End + +Private Function Debug_Read() As Boolean + + Return $bDebug + +End + +Private Sub Debug_Write(Value As Boolean) + + $bDebug = Value + +End + +Private Sub Reload() + + _Request_Get._Reload + +End + + +Private Sub Path_Write(Value As String) + + CGI["PATH_INFO"] = Value + Reload + +End + +Private Sub Query_Write(Value As String) + + CGI["QUERY_STRING"] = Value + Reload + +End + +Private Function Referer_Read() As String + + Return CGI["HTTP_REFERER"] + +End + +Private Function UserAgent_Read() As String + + Return CGI["HTTP_USER_AGENT"] + +End + +Private Function Language_Read() As String + + Dim aLang As String[] + Dim sLang As String + Dim iPos As Integer + + aLang = Split(CGI["HTTP_ACCEPT_LANGUAGE"], ",") + If aLang.Count = 0 Then Return System.Language + sLang = aLang[0] + iPos = InStr(sLang, ";") + If iPos Then sLang = Left(sLang, iPos - 1) + sLang = Replace(sLang, "-", "_") + Return sLang & ".UTF-8" + +End + +Private Function TempDir_Read() As String + + Return $sTempDir + +End + +Private Sub TempDir_Write(Value As String) + + $sTempDir = Value + +End + +Private Function MaxFileSize_Read() As Long + + Return $iMaxFileSize + +End + +Private Sub MaxFileSize_Write(Value As Long) + + $iMaxFileSize = Value + +End diff --git a/comp/src/gb.web/.src/Response.module b/comp/src/gb.web/.src/Response.module new file mode 100644 index 00000000..55010038 --- /dev/null +++ b/comp/src/gb.web/.src/Response.module @@ -0,0 +1,370 @@ +' Gambas module file + +Export + +Class Compress + +Property Buffered As Boolean +Property ContentType As String +Property Status As String +Property EndOfLine As Integer +Property Read Done As Boolean + +'Property LastModified As Date +'Property Read Cache As _ResponseCache + +Private $bBuffered As Boolean +Private $sHeader As String +Private $hFile As File +Private $sContentType As String = "text/html;charset=utf-8" +Private $sStatus As String +Private $iBegin As Integer +'Private $dLastModified As Date +Private $bCompressionComponentChecked As Boolean +Private $bCanUseCompression As Boolean +Private $hComp As Compress +Private $bDone As Boolean + +Public Sub AddHeader(Name As String, Value As String) + + If Not Value Then Return + $sHeader &= Name & ": " & Value & "\r\n" + +End + +Public Sub Redirect(URL As String) + + If $iBegin Then Return + + If URL Like "*://*" Then + AddHeader("Location", URL) + Else + AddHeader("Location", Main.GetAbsoluteURL(URL)) + Endif + + Response.Buffered = False + Response.Begin + Response.End + +End + +Public Sub SetCookie(Cookie As String, Value As String, Optional Domain As String, Optional Path As String, Optional Expires As Date, Optional HttpOnly As Boolean) + + Dim sVal As String + + sVal = Cookie & "=" & Value + If Domain Then sVal &= ";domain=" & Domain + If Expires Then sVal &= ";expires=" & CGI.FormatDate(Expires) + If Path Then sVal &= ";path=" & Path + If HttpOnly Then sVal &= ";httponly" + + AddHeader("Set-Cookie", sVal) + +End + +Public Sub RemoveCookie(Cookie As String, Value As String, Optional Domain As String, Optional Path As String) + + Dim sVal As String + + sVal = Cookie & "=" & Value + If Domain Then sVal &= ";domain=" & Domain + sVal &= ";expires=" & CGI.FormatDate(Now - 1) + If Path Then sVal &= ";path=" & Path + + AddHeader("Set-Cookie", sVal) + +End + +Public Sub Begin() + + Inc $iBegin + If $iBegin <> 1 Then Return + + If $sStatus Then AddHeader("Status", $sStatus) + AddHeader("Content-type", $sContentType) + 'AddHeader("Cache-control", "private") + + If $bBuffered Then + + $hFile = Open Temp$("response") For Create + $hFile.EndOfLine = File.Out.EndOfLine + Output To #$hFile + + Else + + Print $sHeader + $sHeader = "" + + Endif + +End + +Private Sub CloseTempFile() + + Output To Default + File.Out.EndOfLine = $hFile.EndOfLine + Close #$hFile + $hFile = Null + +End + +Public Sub Cancel() + + If Not $bBuffered Then + If $iBegin Then Error.Raise("Response has been started") + Return + Endif + + If $bDone Then Error.Raise("Response has been sent") + + If $iBegin Then + $iBegin = 0 + Try CloseTempFile + Try Kill Temp$("response") + $sHeader = "" + $sStatus = "" + $sContentType = "text/html;charset=utf-8" + Endif + +End + + +Private Sub ShouldCompress() As Boolean + + Return $sContentType Begins "text/" + +End + +Private Sub CanUseCompression() As Boolean + + If Not $bCompressionComponentChecked Then + Try Component.Load("gb.compress") + $bCanUseCompression = Not Error + $bCompressionComponentChecked = True + $hComp = New Compress + $hComp.Type = "zlib" + Endif + + Return $bCanUseCompression + +End + +Public Sub End() + + Dim sBuffer As String + Dim sFile As String + Dim iSize As Long + + 'Dim hLog As File + + Dec $iBegin + If $iBegin <> 0 Then Return + + If $bBuffered Then + + CloseTempFile + + sFile = Temp$("response") + + If ShouldCompress() Then + If Split(CGI["HTTP_ACCEPT_ENCODING"], ",").Exist("gzip*", gb.Like) Then + iSize = Stat(sFile).Size + If iSize >= 128 Then + If CanUseCompression() Then + AddHeader("Content-Encoding", "gzip") + AddHeader("Vary", "Accept-Encoding") + $hComp.File(sFile, sFile & ".gz", $hComp.Max) + If Exist(sFile & ".gz") Then sFile &= ".gz" + Endif + Endif + Endif + Endif + + $hFile = Open sFile For Read + AddHeader("Content-Length", Lof($hFile)) + Print $sHeader + $sHeader = "" + + While Not Eof($hFile) + sBuffer = Read #$hFile, -65536 + Print sBuffer; + 'Print #hLog, sBuffer; + Wend + Close #$hFile + + Else + + Flush + + Endif + + $bDone = True + +'Catch + +' Main.Log(Error.Where & ": " & Error.Text, True) + +End + +Public Sub GetContentTypeFrom(Path As String) As String + + Select Case Lower(File.Ext(Path)) + Case "css" + Return "text/css" + Case "jpg", "jpeg", "jpe", "thumb" + Return "image/jpeg" + Case "png" + Return "image/png" + Case "gif" + Return "image/gif" + Case "svg" + Return "image/svg+xml" + Case "tiff", "tif" + Return "image/tiff" + Case "odt" + Return "application/vnd.oasis.opendocument.text" + Case "doc" + Return "application/msword" + Case "ods" + Return "application/vnd.oasis.opendocument.spreadsheet" + Case "xls" + Return "application/msexcel" + Case "pdf" + Return "application/pdf" + Case "zip" + Return "application/zip" + Case "html", "htm" + Return "text/html" + Case "txt" + Return "text/plain" + Case "avi" + Return "video/x-msvideo" + Case "mpg", "mpeg" + Return "video/mpeg" + Case "ps" + Return "application/postscript" + Case "dwg" + Return "application/acad" + Case "wav" + Return "audio/x-wav" + Case "ogg" + Return "application/ogg" + Case "jar" + Return "application/x-jar" + Case "json" + Return "application/json;charset=utf-8" + Case "kml" + Return "application/vnd.google-earth.kml+xml" + Case "kmz" + Return "application/vnd.google-earth.kmz" + Case Else + Return "application/octet-stream" + End Select + +End + +Public Sub SendFile(Path As String, Optional ContentType As String) + + Dim sBuffer As String + Dim hFile As File + + $bDone = True + + If Left(Path) <> "/" Then Path = ".." &/ Path + + If Not Exist(Path) Then + Print "Status: 404 Not Found" + Print + Return + Endif + + If Not ContentType Then ContentType = GetContentTypeFrom(Path) + Print "Content-Type: "; ContentType + Print $sHeader; + + hFile = Open Path For Read + + Print "Content-length: "; Lof(hFile) + Print + While Not Eof(hFile) + sBuffer = Read #hFile, -1024 + Print sBuffer; + Wend + Close #hFile + +End + + +Private Function Buffered_Read() As Boolean + + Return $bBuffered + +End + +Private Sub Buffered_Write(Value As Boolean) + + If $bBuffered And If Not Value Then $iBegin = 0 + + $bBuffered = Value + +End + +Private Function ContentType_Read() As String + + Return $sContentType + +End + +Private Sub ContentType_Write(Value As String) + + $sContentType = Value + +End + +Private Function Status_Read() As String + + Return $sStatus + +End + +Private Sub Status_Write(Value As String) + + $sStatus = Value + +End +' +' Private Function Cache_Read() As _ResponseCache +' +' Return _ResponseCache +' +' End +' +' Private Function LastModified_Read() As Date +' +' Return $dLastModified +' +' End +' +' Private Sub LastModified_Write(Value As Date) +' +' $dLastModified = Value +' +' End + +Private Function EndOfLine_Read() As Integer + + Return File.Out.EndOfLine + +End + +Private Sub EndOfLine_Write(Value As Integer) + + File.Out.EndOfLine = Value + +End + +Private Function Done_Read() As Boolean + + Return $bDone + +End diff --git a/comp/src/gb.web/.src/Session.module b/comp/src/gb.web/.src/Session.module new file mode 100644 index 00000000..988b1e10 --- /dev/null +++ b/comp/src/gb.web/.src/Session.module @@ -0,0 +1,287 @@ +' Gambas module file + +Export +'CREATE + +Private $hManager As SessionManager +Private $sType As String = "sqlite" + +Private $sId As String + +Private $sPrefix As String +Private $bUnique As Boolean +Private $sCookiePath As String +Private $bInit As Boolean + +Property Type As String +Property Id As String +Property Timeout As Float +Property Prefix As String +Property Unique As Boolean +Property Modified As Boolean +Property CookiePath As String +Property Read Keys As String[] +Property Read Size As Long +Property Read Path As String + +Private Sub CreateSession() + + If $hManager Then Return + + Select Case $sType + Case "file" + $hManager = New FileSessionManager($sId) + Case Else + $hManager = New SqliteSessionManager($sId) + End Select + + If Not $sId Then + $hManager.Create() + Else + If $hManager.Load() Then Return + Endif + + Main.Log("CreateSession: " & $sId) + Response.SetCookie("GBSESSIONID", $sId, "", GetCookiePath(),, True) + +End + + +Private Sub Init() + + If $bInit Then Return + + $bInit = True + + Main.AllowLog = Exist("/tmp/session.debug") + + 'Main.Log("Session.Init") + + 'Main.Log("HTTP_COOKIE = " & CGI["HTTP_COOKIE"] & " / " & Env["HTTP_COOKIE"]) + $sId = Request.Cookies["GBSESSIONID"] + Main.Log("--------------- Init: Cookie = " & $sId) + + 'Main.Log("Cookie = " & $sId) + '$sId = "9E2496B3AB6DDED93ABE6F0CF6E071B3@" + If Not $sId Then Return + + CreateSession + +'CATCH +' CGI.Error(Error.Where & ": " & Error.Text) + +End + +Public Sub _exit() + + Try Main.Log("Session._exit") + If $hManager Then $hManager.Exit + +End + +Private Sub GetCookiePath() As String + + Dim sPath As String + + If $sCookiePath Then Return $sCookiePath + + sPath = CGI["SCRIPT_NAME"] + If sPath = "/." Then sPath = "/" + Return sPath + +End + +Public Sub Abandon() + + Main.Log("Abandon") + + If Not $sId Then Return + If Not $hManager Then Return + + Try Object.Call(Application.Startup, "Session_Abandon") + + $hManager.Abandon + + Response.RemoveCookie("GBSESSIONID", $sId, "", GetCookiePath()) + + $sId = "" + $hManager = Null + +End + +Public Sub _get(Key As String) As Variant + + Init + If $hManager Then Return $hManager[Key] + +End + +Public Sub Exist(Key As String) As Boolean + + Init + If $hManager Then Return $hManager.Exist(Key) + +End + + +Public Sub _put(Value As Variant, Key As String) + + Init + If Not $hManager Then CreateSession + $hManager[Key] = Value + +End + + +Private Function Id_Read() As String + + Init + Return $sId + +End + + +Private Sub Id_Write(Value As String) + + Init + Abandon + $sId = Value + CreateSession + +End + +Private Function Timeout_Read() As Float + + Init + Return Int($hManager.Timeout * 86400 + 0.5) + +End + +Private Sub Timeout_Write(Value As Float) + + Init + $hManager.Timeout = Value / 86400 + +End + +Public Sub Save() + + Init + $hManager.Save + +End + +Public Sub Load() + + Init + $hManager.Load + +End + + +Private Function Prefix_Read() As String + + Return $sPrefix + +End + +Private Sub Prefix_Write(Value As String) + + $sPrefix = Value + +End + +Private Function Unique_Read() As Boolean + + Return $bUnique + +End + +Private Sub Unique_Write(Value As Boolean) + + $bUnique = Value + If $hManager Then $hManager.CheckUnique + +End + +Private Function Modified_Read() As Boolean + + Return $hManager.Modified + +End + +Private Sub Modified_Write(Value As Boolean) + + Init + $hManager.Modified = Value + +End + +Private Function CookiePath_Read() As String + + Return $sCookiePath + +End + +Private Sub CookiePath_Write(Value As String) + + $sCookiePath = Value + +End + + +Private Function Keys_Read() As String[] + + Return $hManager.GetKeys() + +End + +Private Function Size_Read() As Long + + Try Return $hManager.Size + +End + + +Private Function Type_Read() As String + + Return $sType + +End + +Private Sub Type_Write(Value As String) + + Value = LCase(Value) + Select Case Value + Case "sqlite", "file" + $sType = Value + Case "" + $sType = "sqlite" + Case Else + Error.Raise("Unknown session type") + End Select + +End + +Public Sub _MakeId() + + Dim iInd As Integer + + $sId = "" + For iInd = 1 To 6 + $sId &= Hex$(Int(Rnd(65536)), 4) + Next + If $sPrefix Then + $sId = $sPrefix & ":" & $sId + Else + $sId = CGI["REMOTE_ADDR"] & ":" & $sId + Endif + +End + + +Private Function Path_Read() As String + + Return $hManager.GetPath() + +End diff --git a/comp/src/gb.web/.src/SessionManager.class b/comp/src/gb.web/.src/SessionManager.class new file mode 100644 index 00000000..35533881 --- /dev/null +++ b/comp/src/gb.web/.src/SessionManager.class @@ -0,0 +1,38 @@ +' Gambas class file + +Public Timeout As Float = 1 +Public Modified As Boolean +Public Size As Long + +Public Sub Load() As Boolean +End + +Public Sub Save() +End + +Public Sub Abandon() +End + +Public Sub CheckUnique() +End + +Public Sub _get((Key) As String) As Variant +End + +Public Sub Exist((Key) As String) As Boolean +End + +Public Sub _put((Value) As Variant, (Key) As String) +End + +Public Sub GetKeys() As String[] +End + +Public Sub Create() +End + +Public Sub Exit() +End + +Public Sub GetPath(Optional (sId) As String) As String +End diff --git a/comp/src/gb.web/.src/SqliteSessionManager.class b/comp/src/gb.web/.src/SqliteSessionManager.class new file mode 100644 index 00000000..6bdd5d31 --- /dev/null +++ b/comp/src/gb.web/.src/SqliteSessionManager.class @@ -0,0 +1,372 @@ +' Gambas class file + +Inherits SessionManager + +Private $sPath As String + +Private $eStartup As Float + +Private $hConn As Connection + +Private $cValues As Collection +Private $cDelete As Collection + +Public Sub GetPath(Optional sId As String) As String + + Dim sPath As String + + sPath = "/tmp/gambas." & System.User.Id &/ "session" + If sId Then sPath &/= Replace(sId, ":", "_") & ".db" + Return sPath + +End + +Public Sub _new(sId As String) + + Component.Load("gb.db") + $sPath = GetPath(sId) '"/tmp/gambas." & System.User.Id &/ "session" &/ $sId + +End + +Private Sub CheckSession() As Boolean + + 'Main.Log("TimeOut: " & CStr(CDate($eTimeOut)) & " Startup: " & CStr(CDate($eStartup)) & " Now: " & CStr(Now)) + Return (CFloat(Now) - $eStartup) >= Me.Timeout + +End + +Public Sub Load() As Boolean + + Dim rConf As Result + Dim hSave As Connection + + If Not Exist($sPath) Then Goto _ABANDON + + Me.Size = Stat($sPath).Size + + hSave = DB.Current + $hConn = New Connection + DB.Current = hSave + + $hConn.Type = "sqlite" + $hConn.Host = File.Dir($sPath) + $hConn.Name = File.Name($sPath) + + $hConn.Open + $hConn.Exec("PRAGMA journal_mode=WAL;") + $hConn.Exec("PRAGMA synchronous=OFF;") + + rConf = $hConn.Find("config") + + $eStartup = rConf!fStartup + Me.Timeout = rConf!fTimeout + + If CheckSession() Then Goto _ABANDON + + Session.CookiePath = rConf!sCookiePath + + $cValues = New Collection + $cDelete = New Collection + + Me.Modified = False + +Catch + + Main.Log("LoadSession: " & Session.Id & ": " & Error.Where & ": " & Error.Text) + 'Try File.Save(File.Dir($sPath) &/ "session.error", Error.Where & ": " & Error.Text & "\n") + 'Try Kill $sPath & ".error" + 'Try Copy $sPath To $sPath & ".error" + +_ABANDON: + + Main.Log("LoadSession: abandon") + + 'Main.Log("LoadSession: #4") + + If $hConn Then + $hConn.Close + $hConn = Null + Endif + + Session.Abandon + Return True + +End + +Public Sub Save() + + Dim eNow As Float + Dim rConf As Result + Dim vVal As Variant + + 'PRINT "

    Save session

    " + 'PRINT "

    "; $sId; "
    "; $bModify; "
    "; $sPath; "
    "; $cVal.Count + + 'Startup time is always modified + If Not Me.Modified Then + + eNow = Now + If (eNow - $eStartup) < (1 / 86400) Then Return + + rConf = $hConn.Edit("config") + rConf!fStartup = CFloat(Now) + rConf.Update + + Else + + $hConn.Begin + + rConf = $hConn.Edit("config") + rConf!fStartup = CFloat(Now) + rConf!sCookiePath = Session.CookiePath + rConf.Update + + For Each $cDelete + FlushValue(Null, $cDelete.Key) + Next + + For Each vVal In $cValues + FlushValue(vVal, $cValues.Key) + Next + + $cDelete.Clear + $cValues.Clear + + $hConn.Commit + + $hConn.Close + + $hConn.Open + + Me.Size = Stat($sPath).Size + + Me.Modified = False + + Endif + +Catch + + Try Main.Log("SaveSession: " & Error.Where & ": " & Error.Text) + +End + +Public Sub CheckUnique() + + Dim hLock As File + Dim sPrefix As String + Dim aKill As String[] + Dim sKill As String + Dim sDir As String + + If Session.Prefix Then + sPrefix = Session.Prefix + Else + sPrefix = CGI["REMOTE_ADDR"] + Endif + + sDir = GetPath() + + If Session.Unique Then + + Try hLock = Lock GetPath(".unique.lock") + If Error Then Return + + Do + aKill = Dir(sDir, sPrefix & "_*") + If aKill.Count = 0 Then Break + For Each sKill In aKill + Try Kill sDir &/ sKill + Next + Loop + + Unlock #hLock + + Else + + ' dNow = Now + ' For Each sFile In Dir(sDir, sPrefix & ":*") + ' Try hStat = Stat(sDir &/ sFile) + ' If Error Then Continue + ' If DateDiff(hStat.LastModified, dNow, gb.Hour) >= 24 Then + ' Try Kill sDir &/ sFile + ' Endif + ' Next + + Endif + +End + + +Public Sub Create() + + Dim sPrefix As String + Dim hConn As Connection + Dim hTable As Table + Dim rConf As Result + Dim hSave As Connection + + sPrefix = "/tmp/gambas." & System.User.Id + + Repeat + Session._MakeId() + $sPath = GetPath(Session.Id) + Until Not Exist($sPath) + + 'TRY MKDIR $sPrefix + Try Mkdir GetPath() 'sPrefix &/ "session" + + CheckUnique + + hSave = DB.Current + hConn = New Connection + DB.Current = hSave + + hConn.Type = "sqlite" + hConn.Host = File.Dir($sPath) + + hConn.Open + hConn.Databases.Add(File.Name($sPath)) + hConn.Close + + hConn.Name = File.Name($sPath) + hConn.Open + + hTable = hConn.Tables.Add("config") + hTable.Fields.Add("fStartup", db.Float,, 0) + hTable.Fields.Add("fTimeout", db.Float,, 1) + hTable.Fields.Add("sCookiePath", db.String) + hTable.PrimaryKey = ["fStartup"] + hTable.Update + + rConf = hConn.Create("config") + rConf!sCookiePath = Session.CookiePath + rConf.Update + + hTable = hConn.Tables.Add("values") + hTable.Fields.Add("sKey", db.String, 255) + hTable.Fields.Add("sValue", db.Blob) + hTable.PrimaryKey = ["sKey"] + hTable.Update + + $hConn = hConn + $cValues = New Collection + $cDelete = New Collection + +End + +Public Sub Abandon() + + $sPath = "" + Me.Size = 0 + $cValues = Null + $cDelete = Null + If $hConn Then + $hConn.Close + $hConn = Null + Endif + + Try Kill $sPath + +End + +Public Sub _get(Key As String) As Variant + + Dim rValue As Result + Dim sValue As Variant + Dim hFile As File + Dim vVal As Variant + + If $cDelete.Exist(Key) Then Return + vVal = $cValues[Key] + If Not IsNull(vVal) Then Return vVal + + rValue = $hConn.Find("values", "sKey = &1", Key) + If rValue.Available Then + sValue = rValue!sValue.Data + hFile = Open String sValue + vVal = Read #hFile As Variant + Return vVal + Endif + +End + +Public Sub Exist(Key As String) As Boolean + + If $cDelete.Exist(Key) Then Return + If $cValues.Exist(Key) Then Return True + + Return $hConn.Exec("SELECT 1 FROM \"values\" WHERE sKey = &1", Key).Available + +End + + +Public Sub _put(Value As Variant, Key As String) + + If IsNull(Value) Then + $cDelete[Key] = True + $cValues.Remove(Key) + Else + If $cDelete.Count Then $cDelete.Remove(Key) + $cValues[Key] = Value + Endif + + Me.Modified = True + +End + +Private Sub FlushValue(Value As Variant, Key As String) + + Dim rValue As Result + Dim hFile As File + Dim sValue As String + + If IsNull(Value) Then + $hConn.Delete("values", "sKey = &1", Key) + Return + Endif + + rValue = $hConn.Edit("values", "sKey = &1", Key) + If Not rValue.Available Then + rValue = $hConn.Create("values") + rValue!sKey = Key + Endif + + hFile = Open String For Write + Write #hFile, Value As Variant + sValue = Close #hFile + rValue!sValue = sValue + rValue.Update + +End + +Public Sub GetKeys() As String[] + + Dim rValue As Result + Dim aKey As New String[] + + rValue = $hConn.Find("values") + For Each rValue + aKey.Add(rValue!sKey) + Next + + For Each $cDelete + Try aKey.Remove(aKey.Find($cDelete.Key)) + Next + + For Each $cValues + If Not aKey.Exist($cValues.Key) Then aKey.Add($cValues.Key) + Next + + Return aKey + +End + +Public Sub Exit() + + If $hConn Then + Save() + $hConn.Close + $hConn = Null + Endif + +End diff --git a/comp/src/gb.web/.src/URL.class b/comp/src/gb.web/.src/URL.class new file mode 100644 index 00000000..cb286626 --- /dev/null +++ b/comp/src/gb.web/.src/URL.class @@ -0,0 +1,62 @@ +' Gambas class file + +Export + +Static Private Sub HandleQuery(sURL As String, bClear As Boolean, Field As String, Value As String) As String + + Dim I, iPos As Integer + Dim sElt As String + Dim aQuery As String[] + + iPos = InStr(sURL, "?") + If iPos = 0 Then + If bClear Then + Return sURL + Else + Return sURL & "?" & URL.Encode(Field) & "=" & URL.Encode(Value) + Endif + Endif + + aQuery = Split(Mid$(sURL, iPos + 1), "&") + sURL = Left$(sURL, iPos - 1) + + Field = URL.Encode(Field) + Value = URL.Encode(Value) + + For I = 0 To aQuery.Max + sElt = aQuery[I] + If sElt = Field Or If sElt Begins Field & "=" Then + aQuery.Remove(I) + Break + Endif + Next + + If Not bClear Then + If Value Then + aQuery.Add(Field & "=" & Value) + Else + aQuery.Add(Field) + Endif + Endif + + If aQuery.Count Then + Return sURL & "?" & aQuery.Join("&") + Else + Return sURL + Endif + +End + +' TODO: Use IsMissing() + +Static Public Sub SetQuery(URL As String, Field As String, Value As String) As String + + Return HandleQuery(URL, False, Field, Value) + +End + +Static Public Sub UnsetQuery(URL As String, Field As String) As String + + Return HandleQuery(URL, True, Field, "") + +End diff --git a/comp/src/gb.web/.src/WebPage.class b/comp/src/gb.web/.src/WebPage.class new file mode 100644 index 00000000..8e2657aa --- /dev/null +++ b/comp/src/gb.web/.src/WebPage.class @@ -0,0 +1,104 @@ +' Gambas class file + +Export +Create Static + +Static Private $aParent As New WebPage[] +Static Private $iLevel As Integer + +Property Buffered As Boolean +Property Read Parent As Object + +Private $bBuffered As Boolean + +Static Public Sub Main() + + Dim hObject As WebPage + + Try hObject = Application.Startup.AutoCreate() + If Not hObject Then Return + hObject.Render() + +End + +Public Sub _Render(Optional (_Arg) As Collection) + +End + +Public Sub __Render(Optional (_Arg) As Collection = New Collection) + + $aParent.Push(Me) + Me._Render(_Arg) + $aParent.Pop() + +End + +Public Sub _RenderEnd() + +End + +Public Sub __RenderEnd() + + $aParent.Push(Me) + Me._RenderEnd() + $aParent.Pop() + +End + +Public Sub Render() + + If $iLevel = 0 Then + Response.Buffered = $bBuffered + Response.Begin + Endif + + Inc $iLevel + Me.__Render() + +'Finally + + Dec $iLevel + + If $iLevel = 0 Then + Response.End + Endif + +End + +Public Sub ToString() As String + + Dim hFile As File + Dim sResult As String + + Inc $iLevel + + hFile = Open String For Write + Output To #hFile + Me.__Render() + Output To Default + sResult = Close #hFile + +Finally + + Dec $iLevel + Return sResult + +End + +Private Function Buffered_Read() As Boolean + + Return $bBuffered + +End + +Private Sub Buffered_Write(Value As Boolean) + + $bBuffered = Value + +End + +Private Function Parent_Read() As Object + + If $aParent.Count >= 2 Then Return $aParent[$aParent.Max - 1] + +End diff --git a/comp/src/gb.web/.src/Webpage1.class b/comp/src/gb.web/.src/Webpage1.class new file mode 100644 index 00000000..299db02d --- /dev/null +++ b/comp/src/gb.web/.src/Webpage1.class @@ -0,0 +1,7 @@ +' Gambas class file + +Public Sub _new() + + Me.Buffered = True + +End diff --git a/comp/src/gb.web/.src/Webpage1.webpage b/comp/src/gb.web/.src/Webpage1.webpage new file mode 100644 index 00000000..d12122fb --- /dev/null +++ b/comp/src/gb.web/.src/Webpage1.webpage @@ -0,0 +1,22 @@ +<%Dim i As Integer%> + + + + + +

    <%=Application.Name%>

    +Benoît bébé règle +
      +<% +For i = 1 To 10 + Print "
    • "; i; "
    • " +Next +%> +
    +

    +<%-- "coucou" --%> +Bonjour le monde!
    Comment va ? +

    +<> + + diff --git a/comp/src/gb.web/.src/Webpage2.class b/comp/src/gb.web/.src/Webpage2.class new file mode 100644 index 00000000..0a5fa73a --- /dev/null +++ b/comp/src/gb.web/.src/Webpage2.class @@ -0,0 +1,2 @@ +' Gambas class file + diff --git a/comp/src/gb.web/.src/Webpage2.webpage b/comp/src/gb.web/.src/Webpage2.webpage new file mode 100644 index 00000000..0f483381 --- /dev/null +++ b/comp/src/gb.web/.src/Webpage2.webpage @@ -0,0 +1 @@ +

    <%!title%>

    diff --git a/comp/src/gb.web/.src/_Request_Get.class b/comp/src/gb.web/.src/_Request_Get.class new file mode 100644 index 00000000..27e48af2 --- /dev/null +++ b/comp/src/gb.web/.src/_Request_Get.class @@ -0,0 +1,48 @@ +' Gambas class file + +Export +Create Static + +Property Read Fields As String[] + +Static Private $cVal As Collection +Static Private $aField As New String[] + +Private Sub Decode() + + If $cVal Then Return + $cVal = New Collection + Main.DecodeURL(CGI["QUERY_STRING"], $aField, $cVal) + +End + + +Public Sub _get(Key As String) As String + + Decode + Return $cVal[Key] + +End + +Private Function Fields_Read() As String[] + + Decode + Return $aField + +End + +Public Sub Exist(Field As String) As Boolean + + Decode + Return $aField.Exist(Field) + +End + +Public Sub _Reload() + + If Not $cVal Then Return + + $cVal = Null + Decode + +End diff --git a/comp/src/gb.web/.src/_Request_Post.class b/comp/src/gb.web/.src/_Request_Post.class new file mode 100644 index 00000000..66f7420d --- /dev/null +++ b/comp/src/gb.web/.src/_Request_Post.class @@ -0,0 +1,243 @@ +' Gambas class file + +Export +Create Static + +Property Read Fields As String[] +Property Read Files As Collection +Property Read Contents As String + +Static Private $cVal As Collection +Static Private $cFiles As Collection +Static Private $aField As New String[] + +Static Private $bNoContentLength As Boolean +Static Private $iMaxLength As Integer +Static Private $sBuffer As String +Static Private $bEOF As Boolean + +Static Private $sContents As String + +Private Sub ReadLine() As String + + Dim iPos As Integer + Dim sLine As String + Dim sData As String + + Do + + iPos = InStr($sBuffer, "\r\n") + If iPos Then + sLine = Left($sBuffer, iPos - 1) + $sBuffer = Mid$($sBuffer, iPos + 2) + Break + Endif + + If $iMaxLength = 0 Then + $bEOF = True + sLine = $sBuffer + $sBuffer = "" + Break + Endif + + sData = Read Min(4096, $iMaxLength) + $iMaxLength -= Len(sData) + $sBuffer &= sData + If Request.Debug And If Not $sContents Then $sContents = sData + + Loop + + Return sLine + +End + +Private Sub Decode() + + Dim sStr As String + Dim sType As String + Dim sLine As String + Dim aElt As String[] + Dim sElt As String + Dim sName As String + Dim sFileName As String + Dim sValue As String + Dim bFile As Boolean + Dim bFirst As Boolean + Dim hFile As File + Dim iPos As Integer + Dim sCharset As String + Dim sDir As String + Dim iMaxFileLength As Long + + If $cVal Then Return + + $cVal = New Collection + $cFiles = New Collection + + If CGI["REQUEST_METHOD"] <> "POST" Then Return + + File.In.EndOfLine = gb.Windows + + sType = CGI["CONTENT_TYPE"] + $iMaxLength = 0 + Try $iMaxLength = CInt(CGI["CONTENT_LENGTH"]) + If Error Then $bNoContentLength = True + + iPos = InStr(sType, ";") + If iPos Then + sElt = Trim(Mid$(sType, iPos + 1)) + sType = Left$(sType, iPos - 1) + If sElt Begins "charset=" Then sCharset = Mid$(sElt, 9) + Endif + + If sType = "application/x-www-form-urlencoded" Then + If $bNoContentLength Then + sStr = Read -8192 + Else + sStr = Read $iMaxLength + Endif + If Request.Debug Then $sContents = sStr + If sCharset Then Try sStr = Conv(sStr, sCharset, "UTF-8") + 'LINE INPUT sStr + Main.DecodeURL(sStr, $aField, $cVal) + Return + Endif + + If Not (sType Begins "multipart/form-data") Then Return + + 'SHELL "echo MaxLength = " & CStr($iMaxLength) & " >> /var/log/thttpd/debug.log" WAIT + 'sType = Scan(sType, "*boundary=*")[1] + + $sContents = "" + + sType = ReadLine() + 'IF sLine <> sType THEN RETURN + + While Not $bEOF + + sLine = ReadLine() + + If sLine Begins "Content-Disposition:" Then + bFile = False + sFileName = "" + aElt = Split(sLine, ";", Chr$(34)) + For Each sElt In aElt + sElt = Trim(sElt) + 'SHELL "echo " & Quote.Shell(sElt) & " >> /var/log/thttpd/debug.log" WAIT + If sElt Begins "name=" Then + sName = Split(sElt, "=", Chr$(34))[1] + Else If sElt Begins "filename=" Then + bFile = True + sFileName = Split(sElt, "=", Chr$(34))[1] + Endif + Next + Continue + Endif + + If sLine Then Continue + + If bFile Then + + sValue = sFileName + If sValue Then + sFileName = Temp$() + sDir = Request.TempDir + If sDir Then sFileName = sDir &/ File.Name(sFileName) + $cFiles[sName] = sFileName + + iMaxFileLength = Request.MaxFileSize + + hFile = Open sFileName For Create + + bFirst = True + Do + sLine = ReadLine() + If sLine Begins sType Then Break + If Not bFirst Then Print #hFile, "\r\n"; + Print #hFile, sLine; + bFirst = False + If Lof(hFile) > iMaxFileLength Then Error.Raise("Response too big") + Loop + + Close #hFile + Else + Do + sLine = ReadLine() + If sLine Begins sType Then Break + Loop + Endif + + 'SHELL "echo filename = " & Quote.Shell(sFileName) & " >> /var/log/thttpd/debug.log" WAIT + + Else + + sValue = "" + Do + sLine = ReadLine() + If sLine Begins sType Then Break + If sValue Then sValue &= "\r\n" + sValue &= sLine + Loop + + 'SHELL "echo value = " & Quote.Shell(sValue) & " >> /var/log/thttpd/debug.log" WAIT + + Endif + + If sName Then + If Not $aField.Exist(sName) Then + $aField.Add(sName) + $cVal[sName] = sValue + Else + ' IF TypeOf($cVal[sName]) = gb.String THEN + ' $cVal[sName] = [CStr($cVal[sName]), sValue] + ' ELSE + ' $cVal[sName].Add(sValue) + ' ENDIF + $cVal[sName] &= "\n" & sValue + Endif + Endif + + If Right(sLine, 2) = "--" Then Break + + Wend + + 'SHELL "echo =DONE= >> /var/log/thttpd/debug.log" WAIT + +End + + +Public Sub _get(Key As String) As String + + Decode + Return $cVal[Key] + +End + +Private Function Fields_Read() As String[] + + Decode + Return $aField + +End + + +Private Function Files_Read() As Collection + + Decode + Return $cFiles + +End + +Public Sub Exist(Field As String) As Boolean + + Decode + Return $aField.Exist(Field) + +End + +Private Function Contents_Read() As String + + Decode + Return $sContents + +End diff --git a/comp/src/gb.web/.src/_ResponseCache.module b/comp/src/gb.web/.src/_ResponseCache.module new file mode 100644 index 00000000..7ec53e79 --- /dev/null +++ b/comp/src/gb.web/.src/_ResponseCache.module @@ -0,0 +1,13 @@ +' Gambas module file + +'Export +'Create Static + +Public MaxAge As Integer +Public ProxyMaxAge As Integer +Public {Public} As Boolean +Public NoCache As Boolean +Public NoStore As Boolean +Public MustRevalidate As Boolean +Public ProxyRevalidate As Boolean + diff --git a/comp/src/order b/comp/src/order new file mode 100644 index 00000000..52f75d32 --- /dev/null +++ b/comp/src/order @@ -0,0 +1 @@ +gb.eval.highlight gb.args gb.settings gb.gui.base gb.form gb.form.stock gb.form.dialog gb.form.mdi gb.db.form gb.desktop gb.web gb.report gb.report2 gb.chart gb.mysql gb.net.smtp gb.net.pop3 gb.memcached gb.map gb.logging gb.markdown gb.media.form gb.scanner gb.util gb.util.web gb.form.editor gb.dbus.trayicon gb.web.form gb.form.terminal gb.term.form gb.web.feed diff --git a/component.am b/component.am new file mode 100644 index 00000000..3e00843c --- /dev/null +++ b/component.am @@ -0,0 +1,65 @@ +dist_gblib_DATA = $(COMPONENT).component +gblib_DATA = $(COMPONENT).component + +install-data-hook: + @$(INSTALL) -d $(DESTDIR)$(gbdatadir)/info + @rm -f $(DESTDIR)$(gblibdir)/$(COMPONENT).gambas; + @if test -d $(COMPONENT) && test -e $(COMPONENT)/.project ; then \ + echo; \ + echo "Compiling the $(COMPONENT) project..."; \ + ( \ + $(DESTDIR)$(bindir)/gbi$(GAMBAS_VERSION) -r $(DESTDIR)$(prefix) $(COMPONENT); \ + cd $(COMPONENT); \ + $(DESTDIR)$(bindir)/gbc$(GAMBAS_VERSION) -ag -r $(DESTDIR)$(prefix); \ + if test $$? -eq 0; then \ + $(DESTDIR)$(bindir)/gba$(GAMBAS_VERSION); \ + rm -rf .gambas; \ + $(INSTALL) $(COMPONENT).gambas $(DESTDIR)$(gblibdir); \ + else \ + echo "|| Unable to compile $(COMPONENT)" >> ../../../warnings.log; \ + fi \ + ) \ + fi + @if test -d data; then \ + echo "Installing the $(COMPONENT) extra data files..."; \ + $(INSTALL) -d $(DESTDIR)$(gbdatadir)/$(COMPONENT); \ + $(INSTALL) data/* $(DESTDIR)$(gbdatadir)/$(COMPONENT); \ + fi + @echo "Installing the $(COMPONENT) control icons if needed..."; + @rm -rf $(DESTDIR)$(gbdatadir)/control/$(COMPONENT); + @if test -d control; then \ + $(INSTALL) -d $(DESTDIR)$(gbdatadir)/control; \ + $(INSTALL) -d $(DESTDIR)$(gbdatadir)/control/$(COMPONENT); \ + $(INSTALL) control/*.png $(DESTDIR)$(gbdatadir)/control/$(COMPONENT); \ + fi + @if test -d $(COMPONENT)/.hidden/control; then \ + $(INSTALL) -d $(DESTDIR)$(gbdatadir)/control; \ + $(INSTALL) -d $(DESTDIR)$(gbdatadir)/control/$(COMPONENT); \ + $(INSTALL) $(COMPONENT)/.hidden/control/*.png $(DESTDIR)$(gbdatadir)/control/$(COMPONENT); \ + fi + @echo "Creating the information files for $(COMPONENT) component..." + @if test "$(EXTRACT_HELP)" = "1"; then \ + $(DESTDIR)$(bindir)/gbh$(GAMBAS_VERSION) -r $(DESTDIR)$(prefix) -c $(COMPONENT); \ + fi + @$(DESTDIR)$(bindir)/gbi$(GAMBAS_VERSION) -r $(DESTDIR)$(prefix) > /dev/null + @echo + +uninstall-hook: + @if test -d $(COMPONENT); then \ + rm -f $(DESTDIR)$(gblibdir)/$(COMPONENT).gambas; \ + fi + @rm -rf $(DESTDIR)$(gblibdir)$(COMPONENT).component + @rm -rf $(DESTDIR)$(gbdatadir)/info/$(COMPONENT).info + @rm -rf $(DESTDIR)$(gbdatadir)/info/$(COMPONENT).list + @rm -rf $(DESTDIR)$(gbdatadir)/control/$(COMPONENT); + @rm -rf $(DESTDIR)$(gbdatadir)/$(COMPONENT); + +dist-hook: + @if test -d $(COMPONENT); then \ + ( \ + cp -pR $(COMPONENT) $(distdir)/$(COMPONENT); \ + cd $(distdir)/$(COMPONENT); \ + rm -rf .gambas .action .xvpics */.xvpics */*/.xvpics *~ */*~ */*/*~ *.out .*.out .lang/*.pot *.gambas core.* .svn */.svn */*/.svn */*/*/.svn; \ + ) \ + fi + @rm -f $(distdir)/moc_*.cpp diff --git a/configure.ac b/configure.ac new file mode 100644 index 00000000..fb751f02 --- /dev/null +++ b/configure.ac @@ -0,0 +1,95 @@ +dnl **************************************************************************** +dnl global configure.ac +dnl (c) Benoît Minisini 2000-2009 +dnl **************************************************************************** + +dnl ---- Initialization + +m4_include([version.m4]) +AC_INIT(gambas3, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) +AC_PROG_LN_S + +AC_CONFIG_SUBDIRS(main) + +GB_CONFIG_SUBDIRS(bzlib2, gb.compress.bzlib2) +GB_CONFIG_SUBDIRS(zlib, gb.compress.zlib) +GB_CONFIG_SUBDIRS(mysql, gb.db.mysql) +GB_CONFIG_SUBDIRS(odbc, gb.db.odbc) +GB_CONFIG_SUBDIRS(postgresql, gb.db.postgresql) +GB_CONFIG_SUBDIRS(sqlite2, gb.db.sqlite2) +GB_CONFIG_SUBDIRS(sqlite3, gb.db.sqlite3) +GB_CONFIG_SUBDIRS(net, gb.net) +GB_CONFIG_SUBDIRS(curl, gb.net.curl) +GB_CONFIG_SUBDIRS(mime, gb.mime) +GB_CONFIG_SUBDIRS(pcre, gb.pcre) +GB_CONFIG_SUBDIRS(sdl, gb.sdl) +GB_CONFIG_SUBDIRS(sdlsound, gb.sdl.sound) +GB_CONFIG_SUBDIRS(sdl2, gb.sdl2) +GB_CONFIG_SUBDIRS(libxml, gb.libxml) +GB_CONFIG_SUBDIRS(xml, gb.xml) +GB_CONFIG_SUBDIRS(v4l, gb.v4l) +GB_CONFIG_SUBDIRS(crypt, gb.crypt) +GB_CONFIG_SUBDIRS(qt4, gb.qt4) +GB_CONFIG_SUBDIRS(qt5, gb.qt5) +GB_CONFIG_SUBDIRS(gtk, gb.gtk) +GB_CONFIG_SUBDIRS(gtk3, gb.gtk3) +GB_CONFIG_SUBDIRS(opengl, gb.opengl) +GB_CONFIG_SUBDIRS(x11, gb.desktop.x11) +GB_CONFIG_SUBDIRS(keyring, gb.desktop.gnome.keyring) +GB_CONFIG_SUBDIRS(pdf, gb.pdf) +GB_CONFIG_SUBDIRS(cairo, gb.cairo) +GB_CONFIG_SUBDIRS(imageio, gb.image.io) +GB_CONFIG_SUBDIRS(imageimlib, gb.image.imlib) +GB_CONFIG_SUBDIRS(dbus, gb.dbus) +GB_CONFIG_SUBDIRS(gsl, gb.gsl) +GB_CONFIG_SUBDIRS(gmp, gb.gmp) +GB_CONFIG_SUBDIRS(ncurses, gb.ncurses) +GB_CONFIG_SUBDIRS(media, gb.media) +GB_CONFIG_SUBDIRS(httpd, gb.httpd) +GB_CONFIG_SUBDIRS(openssl, gb.openssl) +GB_CONFIG_SUBDIRS(openal, gb.openal) + +AC_CONFIG_SUBDIRS(comp) +AC_CONFIG_SUBDIRS(app) + +AM_MAINTAINER_MODE + +GB_INIT_AUTOMAKE(gambas3) + +AC_CANONICAL_HOST + +GAMBAS_VERSION=3 +AC_SUBST(GAMBAS_VERSION) + +gbbindir=$bindir/gambas$GAMBAS_VERSION/ +AC_SUBST(gbbindir) +gblibdir=$libdir/gambas$GAMBAS_VERSION/ +AC_SUBST(gblibdir) +gbdatadir=$datadir/gambas$GAMBAS_VERSION/ +AC_SUBST(gbdatadir) + +AC_OUTPUT(Makefile) + +GB_CLEAR_MESSAGES + +for gb_dir in gb.*; do + for gb_comp in $gb_dir/DISABLED.*; do + gb_comp=`basename "$gb_comp"` + gb_comp=${gb_comp#DISABLED.} + if test "$gb_comp" != '*'; then + if ! test -s $srcdir/warnings.log; then + GB_MESSAGE([THESE COMPONENTS ARE DISABLED:]) + fi + GB_MESSAGE([- $gb_comp]) + fi + done +done + +if ! test -s $srcdir/warnings.log; then + GB_MESSAGE([ALL COMPONENTS WILL BE COMPILED]) +fi + +GB_PRINT_MESSAGES +echo +GB_CLEAR_MESSAGES diff --git a/gb.cairo/AUTHORS b/gb.cairo/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.cairo/COPYING b/gb.cairo/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.cairo/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.cairo/ChangeLog b/gb.cairo/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.cairo/INSTALL b/gb.cairo/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.cairo/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.cairo/Makefile.am b/gb.cairo/Makefile.am new file mode 100644 index 00000000..b2463224 --- /dev/null +++ b/gb.cairo/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @CAIRO_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.cairo/NEWS b/gb.cairo/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.cairo/README b/gb.cairo/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.cairo/acinclude.m4 b/gb.cairo/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.cairo/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.cairo/component.am b/gb.cairo/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.cairo/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.cairo/configure.ac b/gb.cairo/configure.ac new file mode 100644 index 00000000..a78124d1 --- /dev/null +++ b/gb.cairo/configure.ac @@ -0,0 +1,17 @@ +dnl ---- configure.ac for gb.cairo + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-cairo, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.cairo) +AC_PROG_LIBTOOL + +GB_COMPONENT_PKG_CONFIG( + cairo, CAIRO, gb.cairo, [src], + 'cairo >= 1.6.0' 'cairo-ft >= 1.6.0' +) + +AC_OUTPUT( Makefile src/Makefile ) + +GB_PRINT_MESSAGES diff --git a/gb.cairo/gambas.h b/gb.cairo/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.cairo/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.cairo/gb.image.h b/gb.cairo/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.cairo/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.cairo/gb_common.h b/gb.cairo/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.cairo/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.cairo/m4 b/gb.cairo/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.cairo/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.cairo/reconf b/gb.cairo/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.cairo/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.cairo/src/Makefile.am b/gb.cairo/src/Makefile.am new file mode 100644 index 00000000..3e972b93 --- /dev/null +++ b/gb.cairo/src/Makefile.am @@ -0,0 +1,11 @@ +COMPONENT = gb.cairo +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.cairo.la + +gb_cairo_la_LIBADD = @CAIRO_LIB@ +gb_cairo_la_LDFLAGS = -module @LD_FLAGS@ +gb_cairo_la_CPPFLAGS = @CAIRO_INC@ + +gb_cairo_la_SOURCES = main.c main.h c_cairo.c c_cairo.h c_surface.c c_surface.h + diff --git a/gb.cairo/src/c_cairo.c b/gb.cairo/src/c_cairo.c new file mode 100644 index 00000000..bc5178b8 --- /dev/null +++ b/gb.cairo/src/c_cairo.c @@ -0,0 +1,1199 @@ +/*************************************************************************** + + c_cairo.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_CAIRO_C + +#include "c_surface.h" +#include "c_cairo.h" + +typedef + struct CAIRO_DRAW { + struct CAIRO_DRAW *previous; + void *device; + cairo_surface_t *surface; + cairo_t *context; + CAIRO_PATTERN *pattern; + char *font_family; + cairo_font_weight_t font_weight; + cairo_font_slant_t font_slant; + double font_size; + } + CAIRO_DRAW; + +CAIRO_DRAW *_current = NULL; + +#define THIS _current +#define CNT (_current->context) +#define SURFACE (_current->surface) + +static void free_image(GB_IMG *img, void *image) +{ + //fprintf(stderr, "free_image: %p %p (%d)\n", img, image, cairo_surface_get_reference_count((cairo_surface_t *)image)); + cairo_surface_destroy((cairo_surface_t *)image); +} + +static void *temp_image(GB_IMG *img) +{ + cairo_surface_t *image; + + if (!img->data) + image = NULL; // TODO: use a static small image surface + else + image = cairo_image_surface_create_for_data(img->data, CAIRO_FORMAT_ARGB32, img->width, img->height, + cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, img->width)); + + //fprintf(stderr, "temp_image: %p -> %p (%d)\n", img, image, cairo_surface_get_reference_count(image)); + return image; +} + +static GB_IMG_OWNER _image_owner = { + "gb.cairo", + GB_IMAGE_BGRP, + free_image, + free_image, + temp_image, + NULL, + }; + +static cairo_surface_t *check_image(void *img) +{ + // TODO: format is endian-dependent + return (cairo_surface_t *)IMAGE.Check((GB_IMG *)img, &_image_owner); +} + +static bool check_device() +{ + if (!_current) + { + GB.Error("No current device"); + return TRUE; + } + else + return FALSE; +} + +#define CHECK_CNT() if (check_device()) return + + +/**** CairoExtents *********************************************************/ + +#define IMPLEMENT_EXTENTS_PROPERTY(_method, _field) \ +BEGIN_PROPERTY(_method) \ + GB.ReturnFloat(THIS_EXTENTS->_field); \ +END_PROPERTY + +IMPLEMENT_EXTENTS_PROPERTY(CAIRO_EXTENTS_x1, x1) +IMPLEMENT_EXTENTS_PROPERTY(CAIRO_EXTENTS_y1, y1) +IMPLEMENT_EXTENTS_PROPERTY(CAIRO_EXTENTS_x2, x2) +IMPLEMENT_EXTENTS_PROPERTY(CAIRO_EXTENTS_y2, y2) + +BEGIN_METHOD(CAIRO_EXTENTS_merge, GB_OBJECT extents) + + CAIRO_EXTENTS *extents = VARG(extents); + + if (GB.CheckObject(extents)) + return; + + if (extents->x1 < THIS_EXTENTS->x1) THIS_EXTENTS->x1 = extents->x1; + if (extents->y1 < THIS_EXTENTS->y1) THIS_EXTENTS->y1 = extents->y1; + if (extents->x2 > THIS_EXTENTS->x2) THIS_EXTENTS->x2 = extents->x2; + if (extents->y2 > THIS_EXTENTS->y2) THIS_EXTENTS->y2 = extents->y2; + +END_METHOD + +GB_DESC CairoExtentsDesc[] = +{ + GB_DECLARE("CairoExtents", sizeof(CAIRO_EXTENTS)), + + GB_PROPERTY_READ("X1", "f", CAIRO_EXTENTS_x1), + GB_PROPERTY_READ("Y1", "f", CAIRO_EXTENTS_y1), + GB_PROPERTY_READ("X2", "f", CAIRO_EXTENTS_x2), + GB_PROPERTY_READ("Y2", "f", CAIRO_EXTENTS_y2), + + GB_METHOD("Merge", "CairoExtents", CAIRO_EXTENTS_merge, "(Extents)CairoExtents;"), + + GB_END_DECLARE +}; + +/**** CairoTextExtents *********************************************************/ + +#define IMPLEMENT_TEXT_EXTENTS_PROPERTY(_method, _field) \ +BEGIN_PROPERTY(_method) \ + GB.ReturnFloat(THIS_TEXT_EXTENTS->e._field); \ +END_PROPERTY + +IMPLEMENT_TEXT_EXTENTS_PROPERTY(CairoTextExtents_XBearing, x_bearing) +IMPLEMENT_TEXT_EXTENTS_PROPERTY(CairoTextExtents_YBearing, y_bearing) +IMPLEMENT_TEXT_EXTENTS_PROPERTY(CairoTextExtents_Width, width) +IMPLEMENT_TEXT_EXTENTS_PROPERTY(CairoTextExtents_Height, height) +IMPLEMENT_TEXT_EXTENTS_PROPERTY(CairoTextExtents_XAdvance, x_advance) +IMPLEMENT_TEXT_EXTENTS_PROPERTY(CairoTextExtents_YAdvance, y_advance) + +GB_DESC CairoTextExtentsDesc[] = +{ + GB_DECLARE("CairoTextExtents", sizeof(CAIRO_TEXT_EXTENTS)), + + GB_PROPERTY_READ("XBearing", "f", CairoTextExtents_XBearing), + GB_PROPERTY_READ("YBearing", "f", CairoTextExtents_YBearing), + GB_PROPERTY_READ("Width", "f", CairoTextExtents_Width), + GB_PROPERTY_READ("Height", "f", CairoTextExtents_Height), + GB_PROPERTY_READ("XAdvance", "f", CairoTextExtents_XAdvance), + GB_PROPERTY_READ("YAdvance", "f", CairoTextExtents_YAdvance), + + GB_END_DECLARE +}; + +/**** CairoFontExtents *********************************************************/ + +#define IMPLEMENT_FONT_EXTENTS_PROPERTY(_method, _field) \ +BEGIN_PROPERTY(_method) \ + GB.ReturnFloat(THIS_FONT_EXTENTS->e._field); \ +END_PROPERTY + +IMPLEMENT_FONT_EXTENTS_PROPERTY(CairoFontExtents_Ascent, ascent) +IMPLEMENT_FONT_EXTENTS_PROPERTY(CairoFontExtents_Descent, descent) +IMPLEMENT_FONT_EXTENTS_PROPERTY(CairoFontExtents_Height, height) +IMPLEMENT_FONT_EXTENTS_PROPERTY(CairoFontExtents_MaxXAdvance, max_x_advance) +IMPLEMENT_FONT_EXTENTS_PROPERTY(CairoFontExtents_MaxYAdvance, max_y_advance) + +GB_DESC CairoFontExtentsDesc[] = +{ + GB_DECLARE("CairoFontExtents", sizeof(CAIRO_TEXT_EXTENTS)), + + GB_PROPERTY_READ("Ascent", "f", CairoFontExtents_Ascent), + GB_PROPERTY_READ("Descent", "f", CairoFontExtents_Descent), + GB_PROPERTY_READ("Height", "f", CairoFontExtents_Height), + GB_PROPERTY_READ("MaxXAdvance", "f", CairoFontExtents_MaxXAdvance), + GB_PROPERTY_READ("MaxYAdvance", "f", CairoFontExtents_MaxYAdvance), + + GB_END_DECLARE +}; + +/**** CairoMatrix **********************************************************/ + +BEGIN_METHOD(CAIRO_MATRIX_new, GB_FLOAT xx; GB_FLOAT xy; GB_FLOAT yx; GB_FLOAT yy; GB_FLOAT x0; GB_FLOAT y0) + + cairo_matrix_init(&THIS_MATRIX->matrix, + VARGOPT(xx, 1.0), + VARGOPT(xy, 0.0), + VARGOPT(yx, 0.0), + VARGOPT(yy, 1.0), + VARGOPT(x0, 0.0), + VARGOPT(y0, 0.0)); + +END_METHOD + +BEGIN_METHOD(CAIRO_MATRIX_call, GB_FLOAT xx; GB_FLOAT xy; GB_FLOAT yx; GB_FLOAT yy; GB_FLOAT x0; GB_FLOAT y0) + + CAIRO_MATRIX *matrix; + + matrix = GB.New(GB.FindClass("CairoMatrix"), NULL, NULL); + + cairo_matrix_init(&matrix->matrix, + VARGOPT(xx, 1.0), + VARGOPT(xy, 0.0), + VARGOPT(yx, 0.0), + VARGOPT(yy, 1.0), + VARGOPT(x0, 0.0), + VARGOPT(y0, 0.0)); + + GB.ReturnObject(matrix); + +END_METHOD + +BEGIN_METHOD(CAIRO_MATRIX_translate, GB_FLOAT tx; GB_FLOAT ty) + + cairo_matrix_translate(&THIS_MATRIX->matrix, VARG(tx), VARG(ty)); + GB.ReturnObject(THIS_MATRIX); + +END_METHOD + +BEGIN_METHOD(CAIRO_MATRIX_scale, GB_FLOAT sx; GB_FLOAT sy) + + cairo_matrix_scale(&THIS_MATRIX->matrix, VARG(sx), VARG(sy)); + GB.ReturnObject(THIS_MATRIX); + +END_METHOD + +BEGIN_METHOD(CAIRO_MATRIX_rotate, GB_FLOAT angle) + + cairo_matrix_rotate(&THIS_MATRIX->matrix, VARG(angle)); + GB.ReturnObject(THIS_MATRIX); + +END_METHOD + +BEGIN_METHOD_VOID(CAIRO_MATRIX_invert) + + if (cairo_matrix_invert(&THIS_MATRIX->matrix) == CAIRO_STATUS_SUCCESS) + GB.ReturnObject(THIS_MATRIX); + else + GB.ReturnNull(); + +END_METHOD + +BEGIN_METHOD(CAIRO_MATRIX_multiply, GB_OBJECT matrix) + + CAIRO_MATRIX *matrix = (CAIRO_MATRIX *)VARG(matrix); + + if (GB.CheckObject(matrix)) + return; + + cairo_matrix_multiply(&THIS_MATRIX->matrix, &THIS_MATRIX->matrix, &matrix->matrix); + GB.ReturnObject(THIS_MATRIX); + +END_METHOD + +GB_DESC CairoMatrixDesc[] = +{ + GB_DECLARE("CairoMatrix", sizeof(CAIRO_MATRIX)), + + GB_METHOD("_new", NULL, CAIRO_MATRIX_new, "[(XX)f(YX)f(XY)f(YY)f(X0)f(Y0)f]"), + GB_STATIC_METHOD("_call", "CairoMatrix", CAIRO_MATRIX_call, "[(XX)f(YX)f(XY)f(YY)f(X0)f(Y0)f]"), + GB_METHOD("Translate", "CairoMatrix", CAIRO_MATRIX_translate, "(TX)f(TY)f"), + GB_METHOD("Scale", "CairoMatrix", CAIRO_MATRIX_scale, "(SX)f(SY)f"), + GB_METHOD("Rotate", "CairoMatrix", CAIRO_MATRIX_rotate, "(Angle)f"), + GB_METHOD("Invert", "CairoMatrix", CAIRO_MATRIX_invert, NULL), + GB_METHOD("Multiply", "CairoMatrix", CAIRO_MATRIX_multiply, "(Matrix)CairoMatrix;"), + + GB_END_DECLARE +}; + +/**** CairoPattern *********************************************************/ + +BEGIN_METHOD_VOID(CAIRO_PATTERN_free) + + cairo_pattern_destroy(THIS_PATTERN->pattern); + if (THIS_PATTERN->ref) + GB.Unref(POINTER(&THIS_PATTERN->ref)); + +END_METHOD + +BEGIN_PROPERTY(CAIRO_PATTERN_filter) + + if (READ_PROPERTY) + GB.ReturnInteger(cairo_pattern_get_filter(THIS_PATTERN->pattern)); + else + cairo_pattern_set_filter(THIS_PATTERN->pattern, VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CAIRO_PATTERN_matrix) + + CAIRO_MATRIX *matrix; + + if (READ_PROPERTY) + { + matrix = GB.New(GB.FindClass("CairoMatrix"), NULL, NULL); + cairo_pattern_get_matrix(THIS_PATTERN->pattern, &matrix->matrix); + GB.ReturnObject(matrix); + } + else + { + matrix = (CAIRO_MATRIX *)VPROP(GB_OBJECT); + if (!matrix) + { + cairo_matrix_t m; + cairo_matrix_init_identity(&m); + cairo_pattern_set_matrix(THIS_PATTERN->pattern, &m); + } + else + cairo_pattern_set_matrix(THIS_PATTERN->pattern, &matrix->matrix); + } + +END_PROPERTY + +GB_DESC CairoPatternDesc[] = +{ + GB_DECLARE("CairoPattern", sizeof(CAIRO_PATTERN)), GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, CAIRO_PATTERN_free, NULL), + + GB_PROPERTY("Filter", "i", CAIRO_PATTERN_filter), + GB_PROPERTY("Matrix", "CairoMatrix", CAIRO_PATTERN_matrix), + + GB_END_DECLARE +}; + + +/**** CairoFont *********************************************************/ + +static void update_font() +{ + char *family = THIS->font_family; + + if (!family) + family = ""; + + cairo_select_font_face(CNT, family, THIS->font_slant, THIS->font_weight); +} + +BEGIN_PROPERTY(CairoFont_Name) + + CHECK_CNT(); + + if (READ_PROPERTY) + GB.ReturnString(THIS->font_family); + else + { + GB.StoreString(PROP(GB_STRING), &(THIS->font_family)); + update_font(); + } + +END_PROPERTY + +BEGIN_PROPERTY(CairoFont_Bold) + + CHECK_CNT(); + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->font_weight != CAIRO_FONT_WEIGHT_NORMAL); + else + { + THIS->font_weight = VPROP(GB_BOOLEAN) ? CAIRO_FONT_WEIGHT_BOLD : CAIRO_FONT_WEIGHT_NORMAL; + update_font(); + } + +END_PROPERTY + +BEGIN_PROPERTY(CairoFont_Weight) + + CHECK_CNT(); + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->font_weight); + else + { + THIS->font_weight = VPROP(GB_INTEGER); + update_font(); + } + +END_PROPERTY + +BEGIN_PROPERTY(CairoFont_Italic) + + CHECK_CNT(); + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->font_slant != CAIRO_FONT_SLANT_NORMAL); + else + { + THIS->font_slant = VPROP(GB_BOOLEAN) ? CAIRO_FONT_SLANT_ITALIC : CAIRO_FONT_SLANT_NORMAL; + update_font(); + } + +END_PROPERTY + +BEGIN_PROPERTY(CairoFont_Slant) + + CHECK_CNT(); + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->font_slant); + else + { + THIS->font_slant = VPROP(GB_INTEGER); + update_font(); + } + +END_PROPERTY + +BEGIN_PROPERTY(CairoFont_Size) + + CHECK_CNT(); + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->font_size); + else + { + THIS->font_size = VPROP(GB_FLOAT); + cairo_set_font_size(CNT, THIS->font_size); + } + +END_PROPERTY + +BEGIN_PROPERTY(CairoFont_Matrix) + + CAIRO_MATRIX *matrix; + + CHECK_CNT(); + + if (READ_PROPERTY) + { + matrix = GB.New(GB.FindClass("CairoMatrix"), NULL, NULL); + cairo_get_font_matrix(CNT, &matrix->matrix); + GB.ReturnObject(matrix); + } + else + { + matrix = (CAIRO_MATRIX *)VPROP(GB_OBJECT); + if (!matrix) + { + cairo_matrix_t identity; + cairo_matrix_init_identity(&identity); + cairo_set_font_matrix(CNT, &identity); + } + else + cairo_set_font_matrix(CNT, &matrix->matrix); + } + +END_PROPERTY + +BEGIN_PROPERTY(CairoFont_Extents) + + CAIRO_FONT_EXTENTS *extents; + + CHECK_CNT(); + + extents = GB.New(GB.FindClass("CairoFontExtents"), NULL, NULL); + + cairo_font_extents(CNT, &extents->e); + + GB.ReturnObject(extents); + +END_PROPERTY + +GB_DESC CairoFontDesc[] = +{ + GB_DECLARE(".Cairo.Font", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("Name", "s", CairoFont_Name), + GB_STATIC_PROPERTY("Bold", "b", CairoFont_Bold), + GB_STATIC_PROPERTY("Italic", "b", CairoFont_Italic), + GB_STATIC_PROPERTY("Weight", "i", CairoFont_Weight), + GB_STATIC_PROPERTY("Slant", "i", CairoFont_Slant), + GB_STATIC_PROPERTY("Size", "f", CairoFont_Size), + GB_STATIC_PROPERTY("Matrix", "CairoMatrix", CairoFont_Matrix), + GB_STATIC_PROPERTY("Extents", "CairoFontExtents", CairoFont_Extents), + + GB_END_DECLARE +}; + + +/**** Cairo ****************************************************************/ + +BEGIN_METHOD(CAIRO_begin, GB_OBJECT device) + + void *device = VARG(device); + CAIRO_DRAW *draw; + + if (GB.CheckObject(device)) + return; + + GB.GetClassInterface(GB.GetClass(device), "Drawable"); + + GB.Alloc(POINTER(&draw), sizeof(CAIRO_DRAW)); + draw->previous = _current; + + if (GB.Is(device, GB.FindClass("Image"))) + { + draw->surface = check_image(device); + draw->context = cairo_create(draw->surface); + } + else if (GB.Is(device, GB.FindClass("CairoSurface"))) + { + draw->surface = ((CAIRO_SURFACE *)device)->surface; + draw->context = cairo_create(draw->surface); + } + else + { + GB.Free(POINTER(&draw)); + GB.Error("Bad device"); + return; + } + + draw->device = device; + GB.Ref(device); + draw->pattern = NULL; + draw->font_family = NULL; + draw->font_slant = CAIRO_FONT_SLANT_NORMAL; + draw->font_weight = CAIRO_FONT_WEIGHT_NORMAL; + draw->font_size = 10.0; + + _current = draw; + +END_METHOD + +static void end_current() +{ + CAIRO_DRAW *draw = _current; + + if (!_current) + return; + + _current = draw->previous; + + GB.FreeString(&draw->font_family); + cairo_destroy(draw->context); + GB.Unref(POINTER(&draw->pattern)); + GB.Unref(POINTER(&draw->device)); + GB.Free(POINTER(&draw)); +} + +BEGIN_METHOD_VOID(CAIRO_end) + + if (check_device()) + return; + + end_current(); + +END_METHOD + +BEGIN_METHOD_VOID(CAIRO_exit) + + while (_current) + end_current(); + +END_METHOD + +BEGIN_PROPERTY(CAIRO_status) + + CHECK_CNT(); + GB.ReturnInteger(cairo_status(CNT)); + +END_PROPERTY + +BEGIN_PROPERTY(CAIRO_device) + + if (_current) + GB.ReturnObject(_current->device); + else + GB.ReturnNull(); + +END_PROPERTY + +#define IMPLEMENT_METHOD(_method, _api) \ +BEGIN_METHOD_VOID(_method) \ + CHECK_CNT(); \ + _api(CNT); \ +END_METHOD + +#define IMPLEMENT_METHOD_PRESERVE(_method, _api) \ +BEGIN_METHOD(_method, GB_BOOLEAN preserve) \ + CHECK_CNT(); \ + if (VARGOPT(preserve, FALSE)) \ + _api##_preserve(CNT); \ + else \ + _api(CNT); \ +END_METHOD + +#define IMPLEMENT_PROPERTY_EXTENTS(_property, _api) \ +BEGIN_PROPERTY(_property) \ + CAIRO_EXTENTS *extents; \ + CHECK_CNT(); \ + extents = GB.New(GB.FindClass("CairoExtents"), NULL, NULL); \ + _api(CNT, &extents->x1, &extents->y1, &extents->x2, &extents->y2); \ + GB.ReturnObject(extents); \ +END_METHOD + +#define IMPLEMENT_METHOD_IN_X_Y(_method, _api) \ +BEGIN_METHOD(_method, GB_FLOAT x; GB_FLOAT y) \ + CHECK_CNT(); \ + GB.ReturnBoolean(_api(CNT, VARG(x), VARG(y))); \ +END_METHOD + +#define IMPLEMENT_PROPERTY_INTEGER(_property, _get, _set) \ +BEGIN_PROPERTY(_property) \ + CHECK_CNT(); \ + if (READ_PROPERTY) \ + GB.ReturnInteger(_get(CNT)); \ + else \ + _set(CNT, VPROP(GB_INTEGER)); \ +END_METHOD + +#define IMPLEMENT_PROPERTY_FLOAT(_property, _get, _set) \ +BEGIN_PROPERTY(_property) \ + CHECK_CNT(); \ + if (READ_PROPERTY) \ + GB.ReturnFloat(_get(CNT)); \ + else \ + _set(CNT, VPROP(GB_FLOAT)); \ +END_METHOD + + +IMPLEMENT_METHOD(CAIRO_save, cairo_save) +IMPLEMENT_METHOD(CAIRO_restore, cairo_restore) +IMPLEMENT_METHOD_PRESERVE(CAIRO_clip, cairo_clip) +IMPLEMENT_METHOD(CAIRO_reset_clip, cairo_reset_clip) +IMPLEMENT_PROPERTY_EXTENTS(CAIRO_clip_extents, cairo_clip_extents) +IMPLEMENT_METHOD_PRESERVE(CAIRO_fill, cairo_fill) +IMPLEMENT_PROPERTY_EXTENTS(CAIRO_fill_extents, cairo_fill_extents) +IMPLEMENT_METHOD_IN_X_Y(CAIRO_in_fill, cairo_in_fill) + +BEGIN_METHOD(CAIRO_mask, GB_OBJECT pattern) + + CAIRO_PATTERN *pattern = (CAIRO_PATTERN *)VARG(pattern); + + CHECK_CNT(); + + if (pattern) + cairo_mask(CNT, pattern->pattern); + +END_METHOD + +BEGIN_METHOD(CAIRO_paint, GB_FLOAT alpha) + + CHECK_CNT(); + + if (MISSING(alpha)) + cairo_paint(CNT); + else + cairo_paint_with_alpha(CNT, VARG(alpha)); + +END_METHOD + +IMPLEMENT_METHOD_PRESERVE(CAIRO_stroke, cairo_stroke) +IMPLEMENT_PROPERTY_EXTENTS(CAIRO_stroke_extents, cairo_stroke_extents) +IMPLEMENT_METHOD_IN_X_Y(CAIRO_in_stroke, cairo_in_stroke) + +BEGIN_PROPERTY(CAIRO_source) + + CHECK_CNT(); + + if (READ_PROPERTY) + GB.ReturnObject(_current->pattern); + else + { + CAIRO_PATTERN *old_pattern = _current->pattern; + CAIRO_PATTERN *new_pattern = (CAIRO_PATTERN *)VPROP(GB_OBJECT); + if (new_pattern) + { + GB.Ref(new_pattern); + cairo_set_source(CNT, new_pattern->pattern); + } + GB.Unref(POINTER(&old_pattern)); + _current->pattern = new_pattern; + } + +END_PROPERTY + +BEGIN_PROPERTY(CAIRO_anti_alias) + + CHECK_CNT(); + + if (READ_PROPERTY) + GB.ReturnInteger(cairo_get_antialias(CNT)); + else + cairo_set_antialias(CNT, VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CAIRO_dash) + + GB_ARRAY array; + + CHECK_CNT(); + + if (READ_PROPERTY) + { + int count = cairo_get_dash_count(CNT); + GB.Array.New(POINTER(&array), GB_T_FLOAT, count); + cairo_get_dash(CNT, (double *)GB.Array.Get(array, 0), NULL); + GB.ReturnObject(array); + } + else + { + array = (GB_ARRAY)VPROP(GB_OBJECT); + if (!array || GB.Array.Count(array) == 0) + cairo_set_dash(CNT, NULL, 0, 0.0); + else + cairo_set_dash(CNT, (double *)GB.Array.Get(array, 0), GB.Array.Count(array), 0.0); + } + +END_PROPERTY + +BEGIN_PROPERTY(CAIRO_dash_offset) + + CHECK_CNT(); + + if (READ_PROPERTY) + { + double offset; + cairo_get_dash(CNT, NULL, &offset); + GB.ReturnFloat(offset); + } + else + { + int count = cairo_get_dash_count(CNT); + double dashes[count]; + cairo_get_dash(CNT, dashes, NULL); + cairo_set_dash(CNT, dashes, count, VPROP(GB_FLOAT)); + } + +END_PROPERTY + +IMPLEMENT_PROPERTY_INTEGER(CAIRO_fill_rule, cairo_get_fill_rule, cairo_set_fill_rule) +IMPLEMENT_PROPERTY_INTEGER(CAIRO_line_cap, cairo_get_line_cap, cairo_set_line_cap) +IMPLEMENT_PROPERTY_INTEGER(CAIRO_line_join, cairo_get_line_join, cairo_set_line_join) +IMPLEMENT_PROPERTY_FLOAT(CAIRO_line_width, cairo_get_line_width, cairo_set_line_width) +IMPLEMENT_PROPERTY_FLOAT(CAIRO_miter_limit, cairo_get_miter_limit, cairo_set_miter_limit) +IMPLEMENT_PROPERTY_INTEGER(CAIRO_operator, cairo_get_operator, cairo_set_operator) +IMPLEMENT_PROPERTY_FLOAT(CAIRO_tolerance, cairo_get_tolerance, cairo_set_tolerance) + +IMPLEMENT_METHOD(CAIRO_new_path, cairo_new_path) +IMPLEMENT_METHOD(CAIRO_new_sub_path, cairo_new_sub_path) +IMPLEMENT_METHOD(CAIRO_close_path, cairo_close_path) + +BEGIN_METHOD(CAIRO_arc, GB_FLOAT xc; GB_FLOAT yc; GB_FLOAT radius; GB_FLOAT angle1; GB_FLOAT angle2) + + CHECK_CNT(); + cairo_arc(CNT, VARG(xc), VARG(yc), VARG(radius), VARGOPT(angle1, 0.0), VARGOPT(angle2, M_PI * 2)); + +END_METHOD + +BEGIN_METHOD(CAIRO_arc_negative, GB_FLOAT xc; GB_FLOAT yc; GB_FLOAT radius; GB_FLOAT angle1; GB_FLOAT angle2) + + CHECK_CNT(); + cairo_arc_negative(CNT, VARG(xc), VARG(yc), VARG(radius), VARGOPT(angle1, 0.0), VARGOPT(angle2, M_PI * 2)); + +END_METHOD + +BEGIN_METHOD(CAIRO_curve_to, GB_FLOAT x1; GB_FLOAT y1; GB_FLOAT x2; GB_FLOAT y2; GB_FLOAT x3; GB_FLOAT y3) + + CHECK_CNT(); + cairo_curve_to(CNT, VARG(x1), VARG(y1), VARG(x2), VARG(y2), VARG(x3), VARG(y3)); + +END_METHOD + +BEGIN_METHOD(CAIRO_line_to, GB_FLOAT x; GB_FLOAT y) + + CHECK_CNT(); + cairo_line_to(CNT, VARG(x), VARG(y)); + +END_METHOD + +BEGIN_METHOD(CAIRO_move_to, GB_FLOAT x; GB_FLOAT y) + + CHECK_CNT(); + cairo_move_to(CNT, VARG(x), VARG(y)); + +END_METHOD + +BEGIN_METHOD(CAIRO_rectangle, GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h) + + CHECK_CNT(); + cairo_rectangle(CNT, VARG(x), VARG(y), VARG(w), VARG(h)); + +END_METHOD + +BEGIN_METHOD(CAIRO_rel_curve_to, GB_FLOAT x1; GB_FLOAT y1; GB_FLOAT x2; GB_FLOAT y2; GB_FLOAT x3; GB_FLOAT y3) + + CHECK_CNT(); + cairo_rel_curve_to(CNT, VARG(x1), VARG(y1), VARG(x2), VARG(y2), VARG(x3), VARG(y3)); + +END_METHOD + +BEGIN_METHOD(CAIRO_rel_line_to, GB_FLOAT x; GB_FLOAT y) + + CHECK_CNT(); + cairo_rel_line_to(CNT, VARG(x), VARG(y)); + +END_METHOD + +BEGIN_METHOD(CAIRO_rel_move_to, GB_FLOAT x; GB_FLOAT y) + + CHECK_CNT(); + cairo_rel_move_to(CNT, VARG(x), VARG(y)); + +END_METHOD + +IMPLEMENT_PROPERTY_EXTENTS(CAIRO_path_extents, cairo_path_extents) + +static void make_pattern(cairo_pattern_t *pattern, void *ref) +{ + CAIRO_PATTERN *pat; + pat = GB.New(GB.FindClass("CairoPattern"), NULL, NULL); + pat->pattern = pattern; + if (ref) + { + pat->ref = ref; + GB.Ref(ref); + } + GB.ReturnObject(pat); +} + +BEGIN_METHOD(CAIRO_color_pattern, GB_INTEGER color) + + cairo_pattern_t *pattern; + double r, g, b, a; + uint rgba = VARG(color); + + a = ((rgba >> 24) ^ 0xFF) / 255.0; + r = ((rgba >> 16) & 0xFF) / 255.0; + g = ((rgba >> 8) & 0xFF) / 255.0; + b = (rgba & 0xFF) / 255.0; + + pattern = cairo_pattern_create_rgba(r, g, b, a); + make_pattern(pattern, NULL); + +END_METHOD + +BEGIN_METHOD(CAIRO_solid_pattern, GB_FLOAT r; GB_FLOAT g; GB_FLOAT b; GB_FLOAT a) + + cairo_pattern_t *pattern; + + if (MISSING(a)) + pattern = cairo_pattern_create_rgb(VARG(r), VARG(g), VARG(b)); + else + pattern = cairo_pattern_create_rgba(VARG(r), VARG(g), VARG(b), VARG(a)); + + make_pattern(pattern, NULL); + +END_METHOD + +BEGIN_METHOD(CAIRO_image_pattern, GB_OBJECT image; GB_FLOAT x; GB_FLOAT y; GB_INTEGER extend; GB_INTEGER filter) + + cairo_surface_t *surface; + cairo_pattern_t *pattern; + GB_IMG *image = (GB_IMG *)VARG(image); + + if (GB.CheckObject(image)) + return; + + surface = check_image(image); + pattern = cairo_pattern_create_for_surface(surface); + + if (!MISSING(x) || !MISSING(y)) + { + cairo_matrix_t matrix; + cairo_matrix_init_translate (&matrix, -VARGOPT(x, 0.0), -VARGOPT(y, 0.0)); + cairo_pattern_set_matrix (pattern, &matrix); + } + + if (!MISSING(extend)) + cairo_pattern_set_extend(pattern, VARG(extend)); + if (!MISSING(filter)) + cairo_pattern_set_filter(pattern, VARG(filter)); + + make_pattern(pattern, image); + +END_METHOD + +static void handle_color_stop(cairo_pattern_t *pattern, GB_ARRAY colors) +{ + GB_ARRAY stop; + int i, ncol; + double *col; + + for (i = 0; i < GB.Array.Count(colors); i++) + { + stop = *((GB_ARRAY *)GB.Array.Get(colors, i)); + col = (double *)GB.Array.Get(stop, 0); + ncol = GB.Array.Count(stop); + if (ncol == 4) + cairo_pattern_add_color_stop_rgb(pattern, col[0], col[1], col[2], col[3]); + else if (ncol == 5) + cairo_pattern_add_color_stop_rgba(pattern, col[0], col[1], col[2], col[3], col[4]); + } +} + +BEGIN_METHOD(CAIRO_linear_gradient_pattern, GB_FLOAT x0; GB_FLOAT y0; GB_FLOAT x1; GB_FLOAT y1; GB_OBJECT colors) + + cairo_pattern_t *pattern; + GB_ARRAY colors; + + colors = (GB_ARRAY)VARG(colors); + if (GB.CheckObject(colors)) + return; + + pattern = cairo_pattern_create_linear(VARG(x0), VARG(y0), VARG(x1), VARG(y1)); + handle_color_stop(pattern, colors); + make_pattern(pattern, NULL); + +END_METHOD + +BEGIN_METHOD(CAIRO_radial_gradient_pattern, GB_FLOAT cx0; GB_FLOAT cy0; GB_FLOAT radius0; GB_FLOAT cx1; GB_FLOAT cy1; GB_FLOAT radius1; GB_OBJECT colors) + + cairo_pattern_t *pattern; + GB_ARRAY colors; + + colors = (GB_ARRAY)VARG(colors); + if (GB.CheckObject(colors)) + return; + + pattern = cairo_pattern_create_radial(VARG(cx0), VARG(cy0), VARG(radius0), VARG(cx1), VARG(cy1), VARG(radius1)); + handle_color_stop(pattern, colors); + make_pattern(pattern, NULL); + +END_METHOD + +BEGIN_METHOD(CAIRO_translate, GB_FLOAT tx; GB_FLOAT ty) + + CHECK_CNT(); + cairo_translate(CNT, VARG(tx), VARG(ty)); + +END_METHOD + +BEGIN_METHOD(CAIRO_scale, GB_FLOAT sx; GB_FLOAT sy) + + CHECK_CNT(); + cairo_scale(CNT, VARG(sx), VARG(sy)); + +END_METHOD + +BEGIN_METHOD(CAIRO_rotate, GB_FLOAT angle) + + CHECK_CNT(); + cairo_rotate(CNT, VARG(angle)); + +END_METHOD + +BEGIN_PROPERTY(CAIRO_matrix) + + CAIRO_MATRIX *matrix; + + CHECK_CNT(); + + if (READ_PROPERTY) + { + matrix = GB.New(GB.FindClass("CairoMatrix"), NULL, NULL); + cairo_get_matrix(CNT, &matrix->matrix); + GB.ReturnObject(matrix); + } + else + { + matrix = (CAIRO_MATRIX *)VPROP(GB_OBJECT); + if (!matrix) + cairo_identity_matrix(CNT); + else + cairo_set_matrix(CNT, &matrix->matrix); + } + +END_PROPERTY + +BEGIN_METHOD(Cairo_TextExtents, GB_STRING text) + + CAIRO_TEXT_EXTENTS *extents; + + CHECK_CNT(); + + extents = GB.New(GB.FindClass("CairoTextExtents"), NULL, NULL); + + cairo_text_extents(CNT, GB.ToZeroString(ARG(text)), &extents->e); + + GB.ReturnObject(extents); + +END_PROPERTY + +BEGIN_METHOD(Cairo_Text, GB_STRING text) + + CHECK_CNT(); + + cairo_text_path(CNT, GB.ToZeroString(ARG(text))); + +END_METHOD + +BEGIN_METHOD(Cairo_DrawText, GB_STRING text) + + CHECK_CNT(); + + cairo_show_text(CNT, GB.ToZeroString(ARG(text))); + +END_METHOD + +BEGIN_METHOD_VOID(Cairo_ShowPage) + + CHECK_CNT(); + cairo_show_page(CNT); + +END_METHOD + +BEGIN_METHOD_VOID(Cairo_CopyPage) + + CHECK_CNT(); + cairo_copy_page(CNT); + +END_METHOD + + +GB_DESC CairoDesc[] = +{ + GB_DECLARE("Cairo", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("StatusSuccess", "i", CAIRO_STATUS_SUCCESS), + GB_CONSTANT("StatusNoMemory", "i", CAIRO_STATUS_NO_MEMORY), + GB_CONSTANT("StatusInvalidRestore", "i", CAIRO_STATUS_INVALID_RESTORE), + GB_CONSTANT("StatusInvalidPopGroup", "i", CAIRO_STATUS_INVALID_POP_GROUP), + GB_CONSTANT("StatusNoCurrentPoint", "i", CAIRO_STATUS_NO_CURRENT_POINT), + GB_CONSTANT("StatusInvalidMatrix", "i", CAIRO_STATUS_INVALID_MATRIX), + GB_CONSTANT("StatusInvalidStatus", "i", CAIRO_STATUS_INVALID_STATUS), + GB_CONSTANT("StatusNullPointer", "i", CAIRO_STATUS_NULL_POINTER), + GB_CONSTANT("StatusInvalidString", "i", CAIRO_STATUS_INVALID_STRING), + GB_CONSTANT("StatusInvalidPathData", "i", CAIRO_STATUS_INVALID_PATH_DATA), + GB_CONSTANT("StatusReadError", "i", CAIRO_STATUS_READ_ERROR), + GB_CONSTANT("StatusWriteError", "i", CAIRO_STATUS_WRITE_ERROR), + GB_CONSTANT("StatusSurfaceFinished", "i", CAIRO_STATUS_SURFACE_FINISHED), + GB_CONSTANT("StatusSurfaceTypeMismatch", "i", CAIRO_STATUS_SURFACE_TYPE_MISMATCH), + GB_CONSTANT("StatusPatternTypeMismatch", "i", CAIRO_STATUS_PATTERN_TYPE_MISMATCH), + GB_CONSTANT("StatusInvalidContent", "i", CAIRO_STATUS_INVALID_CONTENT), + GB_CONSTANT("StatusInvalidFormat", "i", CAIRO_STATUS_INVALID_FORMAT), + GB_CONSTANT("StatusInvalidVisual", "i", CAIRO_STATUS_INVALID_VISUAL), + GB_CONSTANT("StatusFileNotFound", "i", CAIRO_STATUS_FILE_NOT_FOUND), + GB_CONSTANT("StatusInvalidDash", "i", CAIRO_STATUS_INVALID_DASH), + GB_CONSTANT("StatusInvalidDscComment", "i", CAIRO_STATUS_INVALID_DSC_COMMENT), + GB_CONSTANT("StatusInvalidIndex", "i", CAIRO_STATUS_INVALID_INDEX), + GB_CONSTANT("StatusClipNotRepresentable", "i", CAIRO_STATUS_CLIP_NOT_REPRESENTABLE), + GB_CONSTANT("StatusTempFileError", "i", CAIRO_STATUS_TEMP_FILE_ERROR), + GB_CONSTANT("StatusInvalidStride", "i", CAIRO_STATUS_INVALID_STRIDE), + #if CAIRO_VERSION_MAJOR > 1 || (CAIRO_VERSION_MAJOR == 1 && CAIRO_VERSION_MINOR >= 8) + GB_CONSTANT("StatusFontTypeMismatch", "i", CAIRO_STATUS_FONT_TYPE_MISMATCH), + GB_CONSTANT("StatusUserFontImmutable", "i", CAIRO_STATUS_USER_FONT_IMMUTABLE), + GB_CONSTANT("StatusUserFontError", "i", CAIRO_STATUS_USER_FONT_ERROR), + GB_CONSTANT("StatusNegativeCount", "i", CAIRO_STATUS_NEGATIVE_COUNT), + GB_CONSTANT("StatusInvalidClusters", "i", CAIRO_STATUS_INVALID_CLUSTERS), + GB_CONSTANT("StatusInvalidSlant", "i", CAIRO_STATUS_INVALID_SLANT), + GB_CONSTANT("StatusInvalidWeight", "i", CAIRO_STATUS_INVALID_WEIGHT), + #else + GB_CONSTANT("StatusFontTypeMismatch", "i", CAIRO_STATUS_INVALID_STRIDE + 1), + GB_CONSTANT("StatusUserFontImmutable", "i", CAIRO_STATUS_INVALID_STRIDE + 2), + GB_CONSTANT("StatusUserFontError", "i", CAIRO_STATUS_INVALID_STRIDE + 3), + GB_CONSTANT("StatusNegativeCount", "i", CAIRO_STATUS_INVALID_STRIDE + 4), + GB_CONSTANT("StatusInvalidClusters", "i", CAIRO_STATUS_INVALID_STRIDE + 5), + GB_CONSTANT("StatusInvalidSlant", "i", CAIRO_STATUS_INVALID_STRIDE + 6), + GB_CONSTANT("StatusInvalidWeight", "i", CAIRO_STATUS_INVALID_STRIDE + 7), + #endif + + GB_CONSTANT("AntiAliasDefault", "i", CAIRO_ANTIALIAS_DEFAULT), + GB_CONSTANT("AntiAliasNone", "i", CAIRO_ANTIALIAS_NONE), + GB_CONSTANT("AntiAliasGray", "i", CAIRO_ANTIALIAS_GRAY), + GB_CONSTANT("AntiAliasSubPixel", "i", CAIRO_ANTIALIAS_SUBPIXEL), + + GB_CONSTANT("ExtendNone", "i", CAIRO_EXTEND_NONE), + GB_CONSTANT("ExtendRepeat", "i", CAIRO_EXTEND_REPEAT), + GB_CONSTANT("ExtendReflect", "i", CAIRO_EXTEND_REFLECT), + GB_CONSTANT("ExtendPad", "i", CAIRO_EXTEND_PAD), + + GB_CONSTANT("FilterFast", "i", CAIRO_FILTER_FAST), + GB_CONSTANT("FilterGood", "i", CAIRO_FILTER_GOOD), + GB_CONSTANT("FilterBest", "i", CAIRO_FILTER_BEST), + GB_CONSTANT("FilterNearest", "i", CAIRO_FILTER_NEAREST), + GB_CONSTANT("FilterBilinear", "i", CAIRO_FILTER_BILINEAR), + GB_CONSTANT("FilterGaussian", "i", CAIRO_FILTER_GAUSSIAN), + + GB_CONSTANT("FillRuleWinding", "i", CAIRO_FILL_RULE_WINDING), + GB_CONSTANT("FillRuleEvenOdd", "i", CAIRO_FILL_RULE_EVEN_ODD), + + GB_CONSTANT("LineCapButt", "i", CAIRO_LINE_CAP_BUTT), + GB_CONSTANT("LineCapRound", "i", CAIRO_LINE_CAP_ROUND), + GB_CONSTANT("LineCapSquare", "i", CAIRO_LINE_CAP_SQUARE), + + GB_CONSTANT("LineJoinMiter", "i", CAIRO_LINE_JOIN_MITER), + GB_CONSTANT("LineJoinRound", "i", CAIRO_LINE_JOIN_ROUND), + GB_CONSTANT("LineJoinBevel", "i", CAIRO_LINE_JOIN_BEVEL), + + GB_CONSTANT("OperatorClear", "i", CAIRO_OPERATOR_CLEAR), + GB_CONSTANT("OperatorSource", "i", CAIRO_OPERATOR_SOURCE), + GB_CONSTANT("OperatorOver", "i", CAIRO_OPERATOR_OVER), + GB_CONSTANT("OperatorIn", "i", CAIRO_OPERATOR_IN), + GB_CONSTANT("OperatorOut", "i", CAIRO_OPERATOR_OUT), + GB_CONSTANT("OperatorATop", "i", CAIRO_OPERATOR_ATOP), + GB_CONSTANT("OperatorDest", "i", CAIRO_OPERATOR_DEST), + GB_CONSTANT("OperatorDestOver", "i", CAIRO_OPERATOR_DEST_OVER), + GB_CONSTANT("OperatorDestIn", "i", CAIRO_OPERATOR_DEST_IN), + GB_CONSTANT("OperatorDestOut", "i", CAIRO_OPERATOR_DEST_OUT), + GB_CONSTANT("OperatorDestATop", "i", CAIRO_OPERATOR_DEST_ATOP), + GB_CONSTANT("OperatorXor", "i", CAIRO_OPERATOR_XOR), + GB_CONSTANT("OperatorAdd", "i", CAIRO_OPERATOR_ADD), + GB_CONSTANT("OperatorSaturate", "i", CAIRO_OPERATOR_SATURATE), + + GB_CONSTANT("FontSlantNormal", "i", CAIRO_FONT_SLANT_NORMAL), + GB_CONSTANT("FontSlantItalic", "i", CAIRO_FONT_SLANT_ITALIC), + GB_CONSTANT("FontSlantOblique", "i", CAIRO_FONT_SLANT_OBLIQUE), + GB_CONSTANT("FontWeightNormal", "i", CAIRO_FONT_WEIGHT_NORMAL), + GB_CONSTANT("FontWeightBold", "i", CAIRO_FONT_WEIGHT_BOLD), + + GB_STATIC_METHOD("Begin", NULL, CAIRO_begin, "(Device)o"), + GB_STATIC_METHOD("End", NULL, CAIRO_end, NULL), + GB_STATIC_METHOD("_exit", NULL, CAIRO_exit, NULL), + + GB_STATIC_PROPERTY_READ("Status", "i", CAIRO_status), + GB_STATIC_PROPERTY_READ("Device", "o", CAIRO_device), + + GB_STATIC_METHOD("Save", NULL, CAIRO_save, NULL), + GB_STATIC_METHOD("Restore", NULL, CAIRO_restore, NULL), + + GB_STATIC_METHOD("Clip", NULL, CAIRO_clip, "[(Preserve)b]"), + GB_STATIC_METHOD("ResetClip", NULL, CAIRO_reset_clip, NULL), + GB_STATIC_PROPERTY_READ("ClipExtents", "CairoExtents", CAIRO_clip_extents), + + GB_STATIC_METHOD("Fill", NULL, CAIRO_fill, "[(Preserve)b]"), + GB_STATIC_PROPERTY_READ("FillExtents", "CairoExtents", CAIRO_fill_extents), + GB_STATIC_METHOD("InFill", "b", CAIRO_in_fill, "(X)f(Y)f"), + + GB_STATIC_METHOD("Mask", NULL, CAIRO_mask, "(Pattern)CairoPattern;"), + + GB_STATIC_METHOD("Paint", NULL, CAIRO_paint, "[(Alpha)f]"), + + GB_STATIC_METHOD("Stroke", NULL, CAIRO_stroke, "[(Preserve)b]"), + GB_STATIC_PROPERTY_READ("StrokeExtents", "CairoExtents", CAIRO_stroke_extents), + GB_STATIC_METHOD("InStroke", "b", CAIRO_in_stroke, "(X)f(Y)f"), + + GB_STATIC_PROPERTY("Source", "CairoPattern", CAIRO_source), + GB_STATIC_PROPERTY("AntiAlias", "i", CAIRO_anti_alias), + GB_STATIC_PROPERTY("Dash", "Float[]", CAIRO_dash), + GB_STATIC_PROPERTY("DashOffset", "f", CAIRO_dash_offset), + GB_STATIC_PROPERTY("FillRule", "i", CAIRO_fill_rule), + GB_STATIC_PROPERTY("LineCap", "i", CAIRO_line_cap), + GB_STATIC_PROPERTY("LineJoin", "i", CAIRO_line_join), + GB_STATIC_PROPERTY("LineWidth", "f", CAIRO_line_width), + GB_STATIC_PROPERTY("MiterLimit", "f", CAIRO_miter_limit), + GB_STATIC_PROPERTY("Operator", "i", CAIRO_operator), + GB_STATIC_PROPERTY("Tolerance", "f", CAIRO_tolerance), + + GB_STATIC_METHOD("NewPath", NULL, CAIRO_new_path, NULL), + GB_STATIC_METHOD("NewSubPath", NULL, CAIRO_new_sub_path, NULL), + GB_STATIC_METHOD("ClosePath", NULL, CAIRO_close_path, NULL), + + GB_STATIC_METHOD("Arc", NULL, CAIRO_arc, "(XC)f(YC)f(Radius)f[(Angle1)f(Angle2)f]"), + GB_STATIC_METHOD("ArcNegative", NULL, CAIRO_arc_negative, "(XC)f(YC)f(Radius)f[(Angle1)f(Angle2)f]"), + GB_STATIC_METHOD("CurveTo", NULL, CAIRO_curve_to, "(X1)f(Y1)f(X2)f(Y2)f(X3)f(Y3)f"), + GB_STATIC_METHOD("LineTo", NULL, CAIRO_line_to, "(X)f(Y)f"), + GB_STATIC_METHOD("MoveTo", NULL, CAIRO_move_to, "(X)f(Y)f"), + GB_STATIC_METHOD("Rectangle", NULL, CAIRO_rectangle, "(X)f(Y)f(Width)f(Height)f"), + + GB_STATIC_METHOD("RelCurveTo", NULL, CAIRO_rel_curve_to, "(DX1)f(DY1)f(DX2)f(DY2)f(DX3)f(DY3)f"), + GB_STATIC_METHOD("RelLineTo", NULL, CAIRO_rel_line_to, "(DX)f(DY)f"), + GB_STATIC_METHOD("RelMoveTo", NULL, CAIRO_rel_move_to, "(DX)f(DY)f"), + + GB_STATIC_PROPERTY_READ("PathExtents", "CairoExtents", CAIRO_path_extents), + + GB_STATIC_METHOD("ColorPattern", "CairoPattern", CAIRO_color_pattern, "(Color)i"), + GB_STATIC_METHOD("SolidPattern", "CairoPattern", CAIRO_solid_pattern, "(Red)f(Green)f(Blue)f[(Alpha)f]"), + GB_STATIC_METHOD("ImagePattern", "CairoPattern", CAIRO_image_pattern, "(Image)Image;[(X)f(Y)f(Extend)i(Filter)i]"), + GB_STATIC_METHOD("LinearGradient", "CairoPattern", CAIRO_linear_gradient_pattern, "(X0)f(Y0)f(X1)f(Y1)f(Colors)Float[][];"), + GB_STATIC_METHOD("RadialGradient", "CairoPattern", CAIRO_radial_gradient_pattern, "(CX0)f(CY0)f(Radius0)f(CX1)f(CY1)f(Radius1)f(Colors)Float[][];"), + + GB_STATIC_METHOD("Translate", NULL, CAIRO_translate, "(TX)f(TY)f"), + GB_STATIC_METHOD("Scale", NULL, CAIRO_scale, "(SX)f(SY)f"), + GB_STATIC_METHOD("Rotate", NULL, CAIRO_rotate, "(Angle)f"), + GB_STATIC_PROPERTY("Matrix", "CairoMatrix", CAIRO_matrix), + + GB_STATIC_PROPERTY_SELF("Font", ".Cairo.Font"), + GB_STATIC_METHOD("TextExtents", "CairoTextExtents", Cairo_TextExtents, "(Text)s"), + GB_STATIC_METHOD("Text", NULL, Cairo_Text, "(Text)s"), + GB_STATIC_METHOD("DrawText", NULL, Cairo_DrawText, "(Text)s"), + + GB_STATIC_METHOD("ShowPage", NULL, Cairo_ShowPage, NULL), + GB_STATIC_METHOD("CopyPage", NULL, Cairo_CopyPage, NULL), + + GB_END_DECLARE +}; diff --git a/gb.cairo/src/c_cairo.h b/gb.cairo/src/c_cairo.h new file mode 100644 index 00000000..60873e56 --- /dev/null +++ b/gb.cairo/src/c_cairo.h @@ -0,0 +1,85 @@ +/*************************************************************************** + + c_cairo.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_CAIRO_H +#define __C_CAIRO_H + +#include "main.h" + +#ifndef __C_CAIRO_C + +extern GB_DESC CairoExtentsDesc[]; +extern GB_DESC CairoTextExtentsDesc[]; +extern GB_DESC CairoFontExtentsDesc[]; +extern GB_DESC CairoPatternDesc[]; +extern GB_DESC CairoMatrixDesc[]; +extern GB_DESC CairoFontDesc[]; +extern GB_DESC CairoDesc[]; + +#else + +#define THIS_EXTENTS ((CAIRO_EXTENTS *)_object) +#define THIS_TEXT_EXTENTS ((CAIRO_TEXT_EXTENTS *)_object) +#define THIS_FONT_EXTENTS ((CAIRO_FONT_EXTENTS *)_object) +#define THIS_PATTERN ((CAIRO_PATTERN *)_object) +#define THIS_MATRIX ((CAIRO_MATRIX *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + double x1, y1, x2, y2; + } + CAIRO_EXTENTS; + +typedef + struct { + GB_BASE ob; + cairo_text_extents_t e; + } + CAIRO_TEXT_EXTENTS; + +typedef + struct { + GB_BASE ob; + cairo_font_extents_t e; + } + CAIRO_FONT_EXTENTS; + +typedef + struct { + GB_BASE ob; + cairo_pattern_t *pattern; + void *ref; + } + CAIRO_PATTERN; + +typedef + struct { + GB_BASE ob; + cairo_matrix_t matrix; + } + CAIRO_MATRIX; + +#endif \ No newline at end of file diff --git a/gb.cairo/src/c_surface.c b/gb.cairo/src/c_surface.c new file mode 100644 index 00000000..776da510 --- /dev/null +++ b/gb.cairo/src/c_surface.c @@ -0,0 +1,210 @@ +/*************************************************************************** + + c_surface.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_SURFACE_C + +#include +#include +#include + +#include "c_surface.h" + +BEGIN_METHOD_VOID(CairoSurface_free) + + GB.FreeString(&THIS->path); + cairo_surface_destroy(THIS->surface); + +END_METHOD + +BEGIN_PROPERTY(CairoSurface_Status) + + GB.ReturnInteger(cairo_surface_status(THIS->surface)); + +END_PROPERTY + +BEGIN_PROPERTY(CairoSurface_Path) + + GB.ReturnString(THIS->path); + +END_PROPERTY + +BEGIN_METHOD(CairoSurface_Save, GB_STRING path) + + GB.ReturnInteger(cairo_surface_write_to_png(THIS->surface, GB.FileName(STRING(path), LENGTH(path)))); + +END_METHOD + +BEGIN_PROPERTY(CairoSurface_Resolution) + + if (READ_PROPERTY) + { + double x, y; + cairo_surface_get_fallback_resolution(THIS->surface, &x, &y); + GB.ReturnFloat(MAX(x, y)); + } + else + { + double r = VPROP(GB_FLOAT); + cairo_surface_set_fallback_resolution(THIS->surface, r, r); + } + +END_PROPERTY + +BEGIN_METHOD_VOID(CairoSurface_Finish) + + cairo_surface_finish(THIS->surface); + +END_METHOD + +GB_DESC CairoSurfaceDesc[] = +{ + GB_DECLARE("CairoSurface", sizeof(CAIRO_SURFACE)), GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, CairoSurface_free, NULL), + + //GB_STATIC_METHOD("_call", "CairoSurface", CairoSurface_call, "(Path)s(Width)f(Height)f"), + GB_METHOD("Save", "i", CairoSurface_Save, "(Path)s"), + GB_METHOD("Finish", NULL, CairoSurface_Finish, NULL), + + GB_PROPERTY_READ("Status", "i", CairoSurface_Status), + GB_PROPERTY_READ("Path", "s", CairoSurface_Path), + GB_PROPERTY("Resolution", "f", CairoSurface_Resolution), + + GB_END_DECLARE +}; + + +/**************************************************************************/ + +#define CM_TO_PT(_cm) ((_cm) / 25.4 * 72.0) + +BEGIN_METHOD(CairoPdfSurface_new, GB_STRING path; GB_FLOAT width; GB_FLOAT height; GB_STRING version) + + char *version = NULL; + + if (!MISSING(version)) + version = GB.ToZeroString(ARG(version)); + + THIS->path = GB.NewZeroString(GB.FileName(STRING(path), LENGTH(path))); + THIS->surface = cairo_pdf_surface_create(THIS->path, CM_TO_PT(VARG(width)), CM_TO_PT(VARG(height))); + + #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 10, 0) + if (version) + { + if (!strcmp(version, "1.4")) + cairo_pdf_surface_restrict_to_version(THIS->surface, CAIRO_PDF_VERSION_1_4); + else if (!strcmp(version, "1.5")) + cairo_pdf_surface_restrict_to_version(THIS->surface, CAIRO_PDF_VERSION_1_5); + } + #endif + +END_METHOD + +BEGIN_METHOD(CairoPdfSurface_Resize, GB_FLOAT width; GB_FLOAT height) + + cairo_pdf_surface_set_size(THIS->surface, CM_TO_PT(VARG(width)), CM_TO_PT(VARG(height))); + +END_METHOD + +GB_DESC CairoPdfSurfaceDesc[] = +{ + GB_DECLARE("CairoPdfSurface", sizeof(CAIRO_SURFACE)), GB_INHERITS("CairoSurface"), + + GB_METHOD("_new", NULL, CairoPdfSurface_new, "(Path)s(Width)f(Height)f[(Version)s]"), + GB_METHOD("Resize", NULL, CairoPdfSurface_Resize, "(Width)f(Height)f"), + + GB_END_DECLARE +}; + + +/**************************************************************************/ + +BEGIN_METHOD(CairoPsSurface_new, GB_STRING path; GB_FLOAT width; GB_FLOAT height; GB_BOOLEAN eps; GB_STRING level) + + char *level = NULL; + + if (!MISSING(level)) + level = GB.ToZeroString(ARG(level)); + + THIS->path = GB.NewZeroString(GB.FileName(STRING(path), LENGTH(path))); + THIS->surface = cairo_ps_surface_create(THIS->path, CM_TO_PT(VARG(width)), CM_TO_PT(VARG(height))); + + if (level) + { + if (!strcmp(level, "2")) + cairo_ps_surface_restrict_to_level(THIS->surface, CAIRO_PS_LEVEL_2); + else if (!strcmp(level, "3")) + cairo_ps_surface_restrict_to_level(THIS->surface, CAIRO_PS_LEVEL_3); + } + + cairo_ps_surface_set_eps(THIS->surface, VARGOPT(eps, FALSE)); + +END_METHOD + +BEGIN_METHOD(CairoPsSurface_Resize, GB_FLOAT width; GB_FLOAT height) + + cairo_ps_surface_set_size(THIS->surface, CM_TO_PT(VARG(width)), CM_TO_PT(VARG(height))); + +END_METHOD + +GB_DESC CairoPsSurfaceDesc[] = +{ + GB_DECLARE("CairoPsSurface", sizeof(CAIRO_SURFACE)), GB_INHERITS("CairoSurface"), + + GB_METHOD("_new", NULL, CairoPsSurface_new, "(Path)s(Width)f(Height)f[(Encapsulated)b(Level)s]"), + GB_METHOD("Resize", NULL, CairoPsSurface_Resize, "(Width)f(Height)f"), + + GB_END_DECLARE +}; + + +/**************************************************************************/ + +BEGIN_METHOD(CairoSvgSurface_new, GB_STRING path; GB_FLOAT width; GB_FLOAT height; GB_STRING version) + + char *version = NULL; + + if (!MISSING(version)) + version = GB.ToZeroString(ARG(version)); + + THIS->path = GB.NewZeroString(GB.FileName(STRING(path), LENGTH(path))); + THIS->surface = cairo_svg_surface_create(THIS->path, CM_TO_PT(VARG(width)), CM_TO_PT(VARG(height))); + + if (version) + { + if (!strcmp(version, "1.1")) + cairo_svg_surface_restrict_to_version(THIS->surface, CAIRO_SVG_VERSION_1_1); + else if (!strcmp(version, "1.2")) + cairo_svg_surface_restrict_to_version(THIS->surface, CAIRO_SVG_VERSION_1_2); + } + +END_METHOD + +GB_DESC CairoSvgSurfaceDesc[] = +{ + GB_DECLARE("CairoSvgSurface", sizeof(CAIRO_SURFACE)), GB_INHERITS("CairoSurface"), + + GB_METHOD("_new", NULL, CairoSvgSurface_new, "(Path)s(Width)f(Height)f[(Version)s]"), + + GB_END_DECLARE +}; diff --git a/gb.cairo/src/c_surface.h b/gb.cairo/src/c_surface.h new file mode 100644 index 00000000..fa8a5f8f --- /dev/null +++ b/gb.cairo/src/c_surface.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + c_surface.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_SURFACE_H +#define __C_SURFACE_H + +#include "main.h" + +#ifndef __C_SURFACE_C + +extern GB_DESC CairoSurfaceDesc[]; +extern GB_DESC CairoPdfSurfaceDesc[]; +extern GB_DESC CairoPsSurfaceDesc[]; +extern GB_DESC CairoSvgSurfaceDesc[]; + +#else + +#define THIS ((CAIRO_SURFACE *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + cairo_surface_t *surface; + char *path; + } + CAIRO_SURFACE; + +#endif \ No newline at end of file diff --git a/gb.cairo/src/gb.cairo.component b/gb.cairo/src/gb.cairo.component new file mode 100644 index 00000000..57395df7 --- /dev/null +++ b/gb.cairo/src/gb.cairo.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.cairo +Author=Benoît Minisini +Requires=gb.image \ No newline at end of file diff --git a/gb.cairo/src/main.c b/gb.cairo/src/main.c new file mode 100644 index 00000000..e1d24bdd --- /dev/null +++ b/gb.cairo/src/main.c @@ -0,0 +1,57 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" +#include "c_surface.h" +#include "c_cairo.h" + +GB_INTERFACE GB EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CairoSurfaceDesc, + CairoPdfSurfaceDesc, + CairoPsSurfaceDesc, + CairoSvgSurfaceDesc, + CairoPatternDesc, + CairoExtentsDesc, + CairoTextExtentsDesc, + CairoFontExtentsDesc, + CairoMatrixDesc, + CairoFontDesc, + CairoDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.cairo/src/main.h b/gb.cairo/src/main.h new file mode 100644 index 00000000..afa3d710 --- /dev/null +++ b/gb.cairo/src/main.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb.image.h" +#include "gb_common.h" +#include + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.compress.bzlib2/AUTHORS b/gb.compress.bzlib2/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.compress.bzlib2/COPYING b/gb.compress.bzlib2/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.compress.bzlib2/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.compress.bzlib2/ChangeLog b/gb.compress.bzlib2/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.compress.bzlib2/INSTALL b/gb.compress.bzlib2/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.compress.bzlib2/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.compress.bzlib2/Makefile.am b/gb.compress.bzlib2/Makefile.am new file mode 100644 index 00000000..a7bd41ae --- /dev/null +++ b/gb.compress.bzlib2/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @BZLIB2_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.compress.bzlib2/NEWS b/gb.compress.bzlib2/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.compress.bzlib2/README b/gb.compress.bzlib2/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.compress.bzlib2/acinclude.m4 b/gb.compress.bzlib2/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.compress.bzlib2/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.compress.bzlib2/component.am b/gb.compress.bzlib2/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.compress.bzlib2/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.compress.bzlib2/configure.ac b/gb.compress.bzlib2/configure.ac new file mode 100644 index 00000000..859c2547 --- /dev/null +++ b/gb.compress.bzlib2/configure.ac @@ -0,0 +1,25 @@ +dnl ---- Initialization + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-compress-bzlib2, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.compress.bzlib2) +AC_PROG_LIBTOOL + +dnl ---- bzlib2 compression driver + +GB_COMPONENT( + bzlib2, BZLIB2, gb.compress.bzlib2, [src], + [GB_FIND(bzlib.h, $prefix /usr/local/lib /usr/local /opt /usr/lib /usr, include)], + [GB_FIND(libbz2.$SHLIBEXT, $prefix /usr/local /opt /usr, lib)], + [$C_LIB -lbz2]) + +dnl ---- Create makefiles + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.compress.bzlib2/gambas.h b/gb.compress.bzlib2/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.compress.bzlib2/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.compress.bzlib2/gb.compress.h b/gb.compress.bzlib2/gb.compress.h new file mode 120000 index 00000000..7215a72a --- /dev/null +++ b/gb.compress.bzlib2/gb.compress.h @@ -0,0 +1 @@ +../main/lib/compress/gb.compress.h \ No newline at end of file diff --git a/gb.compress.bzlib2/gb_common.h b/gb.compress.bzlib2/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.compress.bzlib2/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.compress.bzlib2/m4 b/gb.compress.bzlib2/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.compress.bzlib2/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.compress.bzlib2/reconf b/gb.compress.bzlib2/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.compress.bzlib2/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.compress.bzlib2/src/Makefile.am b/gb.compress.bzlib2/src/Makefile.am new file mode 100644 index 00000000..d4980ba3 --- /dev/null +++ b/gb.compress.bzlib2/src/Makefile.am @@ -0,0 +1,10 @@ +COMPONENT = gb.compress.bzlib2 +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.compress.bzlib2.la + +gb_compress_bzlib2_la_LIBADD = @BZLIB2_LIB@ +gb_compress_bzlib2_la_LDFLAGS = -module @LD_FLAGS@ @BZLIB2_LDFLAGS@ +gb_compress_bzlib2_la_CFLAGS = $(AM_CFLAGS) @BZLIB2_INC@ + +gb_compress_bzlib2_la_SOURCES = main.h main.c diff --git a/gb.compress.bzlib2/src/gb.compress.bzlib2.component b/gb.compress.bzlib2/src/gb.compress.bzlib2.component new file mode 100644 index 00000000..db7b3a2c --- /dev/null +++ b/gb.compress.bzlib2/src/gb.compress.bzlib2.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.compress.bzlib2 +Author=Daniel Campos Fernández +Require=gb.compress + diff --git a/gb.compress.bzlib2/src/main.c b/gb.compress.bzlib2/src/main.c new file mode 100644 index 00000000..a29ecdb5 --- /dev/null +++ b/gb.compress.bzlib2/src/main.c @@ -0,0 +1,579 @@ +/*************************************************************************** + + main.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +/* Use 64 bits I/O */ +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include + +#include "main.h" + +#define GB_Z_BUFFER 8192 +#define MODE_READ 0 +#define MODE_WRITE 1 +#define P_MODE ((BZ2_STREAM*)stream)->info->mode +#define P_BZ ((BZ2_STREAM*)stream)->info->bz +#define P_FILE ((BZ2_STREAM*)stream)->info->file +#define P_EOF ((BZ2_STREAM*)stream)->info->eof +#define P_POS ((BZ2_STREAM*)stream)->info->pos + + +typedef + struct + { + uint8_t mode; + uint8_t eof; + BZFILE *bz; + FILE *file; + int64_t pos; + } + handleInfo; + +typedef + struct + { + GB_STREAM_BASE base; + handleInfo *info; + } + BZ2_STREAM; + +GB_INTERFACE GB EXPORT; +COMPRESS_INTERFACE COMPRESSION EXPORT; + +static COMPRESS_DRIVER _driver; + +GB_STREAM_DESC BZStream = +{ + open: BZ_stream_open, + close: BZ_stream_close, + read: BZ_stream_read, + write: BZ_stream_write, + seek: BZ_stream_seek, + tell: BZ_stream_tell, + flush: BZ_stream_flush, + eof: BZ_stream_eof, + lof: BZ_stream_lof +}; + +/***************************************************************************** + + The driver interface + +*****************************************************************************/ + +/********************************************************************************* +The Driver must provide this function: + +int max_compression(void) + +It must return the maximum compression level that user can +assign, for instance gzip (in this moment) provides ten values, from zero to +9 , beeing 9 the best compression level (more compression), +so the driver here returns '9' +*********************************************************************************/ + +static int BZ2_max_compression(void) +{ + return 9; +} + +/********************************************************************************* +The Driver must provide this function: + +int min_compression(void) + +It must return the minimum compression level that user can +assign, for instance gzip (in this moment) provides ten values, from zero to +9 , beeing 0 the worst compression level (no compression at all), +so the driver here returns '0' +*********************************************************************************/ +static int BZ2_min_compression(void) +{ + return 1; +} +/********************************************************************************* +The Driver must provide this function: + +int default_compression(void) + +It must return the default compression level, that is, the value to send when +Compression object methods are called without the optional parameter 'Level'. +In this gzip wrapper, for example, this value is -1 +*********************************************************************************/ +static int BZ2_default_compression(void) +{ + return 6; +} +/********************************************************************************* +The Driver must provide this function: + +void c_String(char **target,unsigned long *lent,char *source,unsigned long len,int level) + +It is called to compress a String and return it compressed. The object will +pass the following values: + +target = NULL +lent = 0 +source = a pointer to the original string +len = length of the original string +level = compression level + +You will never receive a zero lenght string, nor a erroneus 'level' value + +The function must store the compressed string in 'target', and its length in 'lent', +or NULL and zero if it fails by any reason + +*********************************************************************************/ +static void BZ2_c_String(char **target,unsigned long *lent,char *source,unsigned long len,int level) +{ + *target=NULL; + *lent=len + (len*0.1) + 600; + GB.Alloc ((void**)target,sizeof(char)*(*lent)); + + if (BZ_OK != BZ2_bzBuffToBuffCompress(*target,(unsigned int*)lent,source,(unsigned int)len,level,0,30)) + { + *lent=0; + GB.Free((void**)target); + target=NULL; + GB.Error("Unable to compress string"); + return; + } + +} +/********************************************************************************* +The Driver must provide this function: + +void u_String(char **target,unsigned long *lent,char *source,unsigned long len,int level) + +It is called to decompress a String and return it decompressed. The object will +pass the following values: + +target = NULL +lent = 0 +source = a pointer to the original string +len = length of the original string +level = compression level + + You will never receive a zero lenght string, nor a erroneus 'level' value + +The function must store the decompressed string in 'target', and its length in 'lent', +or NULL and zero if it fails by any reason +*********************************************************************************/ +static void BZ2_u_String(char **target,unsigned long *lent,char *source,unsigned long len) +{ + int myok=BZ_OUTBUFF_FULL; + + /* we assume src len * 1.8 as target len */ + *lent=1.8*len; + GB.Alloc ((void**)target,(*lent)*sizeof(char)); + + while (myok==BZ_OUTBUFF_FULL) + { + myok=BZ2_bzBuffToBuffDecompress(*target,(unsigned int*)lent,source,(unsigned int)len,0,0); + switch (myok) + { + case BZ_OK: break; + case BZ_DATA_ERROR: + case BZ_DATA_ERROR_MAGIC: + case BZ_UNEXPECTED_EOF: + if (*target)GB.Free((void**)target); + *target=NULL; + *lent=0; + GB.Error ("Invalid compressed string"); + return; + + case BZ_OUTBUFF_FULL: /* test and error method ! */ + if ((*lent)<=10) + (*lent)+=(*lent); + else + (*lent)+=((*lent)*0.5); + GB.Realloc ((void**)target,(*lent)*sizeof(char)); + break; + + case BZ_MEM_ERROR: + if (*target)GB.Free((void**)target); + *target=NULL; + *lent=0; + GB.Error ("Not enough memory: String too long"); + return; + default: + + if (*target)GB.Free((void**)target); + *target=NULL; + *lent=0; + GB.Error ("Unable to inflate string"); + return; + } + } + +} +/********************************************************************************* +The Driver must provide this function: + +static void c_File(char *source,char *target,int level) + +It is called to compress a file. The object will pass the following values: + + +source = path of the file to be compressed +target = path of the new compressed file to create +level = compression level + + You will never receive a erroneus 'level' value +*********************************************************************************/ +static void BZ2_c_File(char *source,char *target,int level) +{ + FILE *src; + FILE *f_dst; + BZFILE *dst; + char buf[GB_Z_BUFFER]; + long len; + int bzerror=BZ_OK; + + if ( (src=fopen(source,"rb"))==NULL) { + GB.Error ("Unable to open file for reading"); + return; + } + + if ( (f_dst=fopen(target,"wb"))==NULL) { + fclose(src); + GB.Error ("Unable to open file for writing"); + return; + } + dst=BZ2_bzWriteOpen ( &bzerror,f_dst,level,0,30); + + if (bzerror!=BZ_OK) { + fclose(src); + fclose(f_dst); + GB.Error ("Unable to open file for writing"); + return; + } + + while (!feof(src)) + { + len=fread((void*)buf,sizeof(char),GB_Z_BUFFER,src); + if (leninfo), sizeof(handleInfo) ); + + P_FILE=fopen(path,"wb"); + if (!P_FILE) + { + GB.Free(POINTER(&stream->info)); + GB.Error("Unable to open file"); + return; + } + + P_MODE=MODE_WRITE; + P_BZ=BZ2_bzWriteOpen(&bzerror,P_FILE,level,0,30); + + + if (bzerror) + { + fclose(P_FILE); + GB.Free( POINTER(&((BZ2_STREAM*)stream)->info) ); + GB.Error("Unable to open file"); + return; + } + + P_EOF=0; + P_POS=0; + stream->base.desc = &BZStream; +} + +static void BZ2_u_Open(char *path,GB_STREAM *stream) +{ + int bzerror; + + GB.Alloc( POINTER(&((BZ2_STREAM*)stream)->info),sizeof(handleInfo) ); + + P_FILE=fopen(path,"rb"); + if (!P_FILE) + { + GB.Free( POINTER(&((BZ2_STREAM*)stream)->info) ); + GB.Error("Unable to open file"); + return; + } + + P_MODE=MODE_WRITE; + P_BZ=BZ2_bzReadOpen (&bzerror,P_FILE,0,0,NULL,0); + + if (bzerror) + { + GB.Free( POINTER(&((BZ2_STREAM*)stream)->info) ); + fclose(P_FILE); + GB.Error("Unable to open file"); + return; + } + + P_EOF=0; + P_POS=0; + stream->desc=&BZStream; +} + +/************************************************************************* +Stream related stuff +**************************************************************************/ +/* not allowed stream methods */ +static int BZ_stream_lof(GB_STREAM *stream, int64_t *len){return -1;} +static int BZ_stream_seek(GB_STREAM *stream, int64_t offset, int whence){ return -1;} +static int BZ_stream_open(GB_STREAM *stream, const char *path, int mode, void *data){return -1;} +/* allowed stream methods */ +static int BZ_stream_tell(GB_STREAM *stream, int64_t *npos) +{ + *npos=P_POS; + return 0; +} + +static int BZ_stream_flush(GB_STREAM *stream) +{ + return 0; +} + +static int BZ_stream_close(GB_STREAM *stream) +{ + int bzerror; + + if (P_MODE == MODE_WRITE) + BZ2_bzWriteClose(&bzerror,P_BZ,0,NULL,NULL); + else + BZ2_bzReadClose(&bzerror,P_BZ); + + fclose(P_FILE); + + GB.Free( POINTER(&((BZ2_STREAM*)stream)->info) ); + stream->desc=NULL; + return 0; +} + +static int BZ_stream_write(GB_STREAM *stream, char *buffer, int len) +{ + int bzerror; + + if (P_MODE==MODE_READ) return -1; + BZ2_bzWrite ( &bzerror,P_BZ, (void*)buffer, len); + if (!bzerror) { P_POS += len; return len; } + + //BZ2_bzWriteClose (&bzerror,P_BZ,0,NULL,NULL); + //fclose(P_FILE); + + //GB.Free( POINTER(&((BZ2_STREAM*)stream)->info) ); + //stream->desc=NULL; + return -1; +} + +static int BZ_stream_eof(GB_STREAM *stream) +{ + return P_EOF; +} + +static int BZ_stream_read(GB_STREAM *stream, char *buffer, int len) +{ + int bzerror; + int len2; + + if (P_MODE == MODE_WRITE) + return -1; + + len2 = BZ2_bzRead (&bzerror, P_BZ, (void*)buffer, len); + + if (!bzerror) + { + P_POS += len2; + return len2; + } + else + { + if ((len2 == len) && (bzerror == BZ_STREAM_END)) + { + P_POS += len2; + P_EOF = 1; + return len2; + } + } + + //BZ2_bzReadClose (&bzerror,P_BZ); + //fclose(P_FILE); + //GB.Free( POINTER(&((BZ2_STREAM*)stream)->info) ); + //stream->desc=NULL; + return -1; +} + + +/**************************************************************************** +This array of functions defines what functions the compression component +must call to perform its actions +****************************************************************************/ +static COMPRESS_DRIVER _driver = +{ + "bzlib2", + + (void*)BZ2_max_compression, + (void*)BZ2_min_compression, + (void*)BZ2_default_compression, + + { + (void*)BZ2_c_String, + (void*)BZ2_c_File, + (void*)BZ2_c_Open, + (void*)BZ_stream_close, + }, + + { + (void*)BZ2_u_String, + (void*)BZ2_u_File, + (void*)BZ2_u_Open, + (void*)BZ_stream_close + } + +}; +/***************************************************************************** + + The component entry and exit functions. + +*****************************************************************************/ + +int EXPORT GB_INIT(void) +{ + /************************************************************************* + When this component is loaded by Gambas runtime, it 'informs' to the + compression component what functions must it call to perform its actions + *************************************************************************/ + GB.GetInterface("gb.compress", COMPRESS_INTERFACE_VERSION, &COMPRESSION); + COMPRESSION.Register(&_driver); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + diff --git a/gb.compress.bzlib2/src/main.h b/gb.compress.bzlib2/src/main.h new file mode 100644 index 00000000..f4f7c9f1 --- /dev/null +++ b/gb.compress.bzlib2/src/main.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + main.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "../gb.compress.h" +#include + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern COMPRESS_INTERFACE COMPRESSION; +extern GB_STREAM_DESC BZStream; +#endif + +static int BZ_stream_lof(GB_STREAM *stream, int64_t *len); +static int BZ_stream_seek(GB_STREAM *stream, int64_t offset, int whence); +static int BZ_stream_open(GB_STREAM *stream, const char *path, int mode, void *data); +static int BZ_stream_tell(GB_STREAM *stream, int64_t *npos); +static int BZ_stream_flush(GB_STREAM *stream); +static int BZ_stream_close(GB_STREAM *stream); +static int BZ_stream_write(GB_STREAM *stream, char *buffer, int len); +static int BZ_stream_eof(GB_STREAM *stream); +static int BZ_stream_read(GB_STREAM *stream, char *buffer, int len); + +#endif /* __MAIN_H */ + diff --git a/gb.compress.zlib/AUTHORS b/gb.compress.zlib/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.compress.zlib/COPYING b/gb.compress.zlib/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.compress.zlib/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.compress.zlib/ChangeLog b/gb.compress.zlib/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.compress.zlib/INSTALL b/gb.compress.zlib/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.compress.zlib/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.compress.zlib/Makefile.am b/gb.compress.zlib/Makefile.am new file mode 100644 index 00000000..1c43b4b8 --- /dev/null +++ b/gb.compress.zlib/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @ZLIB_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.compress.zlib/NEWS b/gb.compress.zlib/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.compress.zlib/README b/gb.compress.zlib/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.compress.zlib/acinclude.m4 b/gb.compress.zlib/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.compress.zlib/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.compress.zlib/component.am b/gb.compress.zlib/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.compress.zlib/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.compress.zlib/configure.ac b/gb.compress.zlib/configure.ac new file mode 100644 index 00000000..90291ead --- /dev/null +++ b/gb.compress.zlib/configure.ac @@ -0,0 +1,23 @@ +dnl ---- Initialization + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-compress-zlib, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.compress.zlib) +AC_PROG_LIBTOOL + +dnl ---- zlib compression driver + +GB_COMPONENT_PKG_CONFIG( + zlib, ZLIB, gb.compress.zlib, [src], + zlib) + +dnl ---- Create makefiles + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.compress.zlib/gambas.h b/gb.compress.zlib/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.compress.zlib/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.compress.zlib/gb.compress.h b/gb.compress.zlib/gb.compress.h new file mode 120000 index 00000000..7215a72a --- /dev/null +++ b/gb.compress.zlib/gb.compress.h @@ -0,0 +1 @@ +../main/lib/compress/gb.compress.h \ No newline at end of file diff --git a/gb.compress.zlib/gb_common.h b/gb.compress.zlib/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.compress.zlib/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.compress.zlib/m4 b/gb.compress.zlib/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.compress.zlib/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.compress.zlib/reconf b/gb.compress.zlib/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.compress.zlib/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.compress.zlib/src/Makefile.am b/gb.compress.zlib/src/Makefile.am new file mode 100644 index 00000000..a4080dc3 --- /dev/null +++ b/gb.compress.zlib/src/Makefile.am @@ -0,0 +1,10 @@ +COMPONENT = gb.compress.zlib +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.compress.zlib.la + +gb_compress_zlib_la_LIBADD = @ZLIB_LIB@ +gb_compress_zlib_la_LDFLAGS = -module @LD_FLAGS@ @ZLIB_LDFLAGS@ +gb_compress_zlib_la_CFLAGS = $(AM_CFLAGS) @ZLIB_INC@ + +gb_compress_zlib_la_SOURCES = main.h main.c diff --git a/gb.compress.zlib/src/gb.compress.zlib.component b/gb.compress.zlib/src/gb.compress.zlib.component new file mode 100644 index 00000000..0da98c8c --- /dev/null +++ b/gb.compress.zlib/src/gb.compress.zlib.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.compress.zlib +Author=Daniel Campos Fernández +Require=gb.compress + diff --git a/gb.compress.zlib/src/main.c b/gb.compress.zlib/src/main.c new file mode 100644 index 00000000..8fc71f30 --- /dev/null +++ b/gb.compress.zlib/src/main.c @@ -0,0 +1,527 @@ +/*************************************************************************** + + main.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gb_common.h" + +// Use 64 bits I/O +#if (__WORDSIZE == 64) && (!_LARGEFILE64_SOURCE) +#define _LARGEFILE64_SOURCE +#endif + +#include +#include +#include + +#include "main.h" + +#define GB_Z_BUFFER 8192 +#define MODE_READ 0 +#define MODE_WRITE 1 + +GB_INTERFACE EXPORT GB; +COMPRESS_INTERFACE EXPORT COMPRESSION; + +static COMPRESS_DRIVER _driver; + +GB_STREAM_DESC ZStream = { + open: CZ_stream_open, + close: CZ_stream_close, + read: CZ_stream_read, + write: CZ_stream_write, + seek: CZ_stream_seek, + tell: CZ_stream_tell, + flush: CZ_stream_flush, + eof: CZ_stream_eof, + lof: CZ_stream_lof +}; + +typedef + struct { + GB_STREAM_BASE base; + int mode; + gzFile handle; + } + STREAM_COMPRESS; + + +/***************************************************************************** + + The driver interface + +*****************************************************************************/ + +/********************************************************************************* +The Driver must provide this function: + +int max_compression(void) + +It must return the maximum compression level that user can +assign, for instance gzip (in this moment) provides ten values, from zero to +9 , beeing 9 the best compression level (more compression), +so the driver here returns '9' +*********************************************************************************/ + +static int max_compression(void) +{ + return Z_BEST_COMPRESSION; +} + +/********************************************************************************* +The Driver must provide this function: + +int min_compression(void) + +It must return the minimum compression level that user can +assign, for instance gzip (in this moment) provides ten values, from zero to +9 , beeing 0 the worst compression level (no compression at all), +so the driver here returns '0' +*********************************************************************************/ +static int min_compression(void) +{ + return Z_NO_COMPRESSION; +} +/********************************************************************************* +The Driver must provide this function: + +int default_compression(void) + +It must return the default compression level, that is, the value to send when +Compression object methods are called without the optional parameter 'Level'. +In this gzip wrapper, for example, this value is -1 +*********************************************************************************/ +static int default_compression(void) +{ + return Z_DEFAULT_COMPRESSION; +} +/********************************************************************************* +The Driver must provide this function: + +void c_String(char **target,unsigned int *lent,char *source,unsigned int len,int level) + +It is called to compress a String and return it compressed. The object will +pass the following values: + +target = NULL +lent = 0 +source = a pointer to the original string +len = length of the original string +level = compression level + +You will never receive a zero lenght string, nor a erroneus 'level' value + +The function must store the compressed string in 'target', and its length in 'lent', +or NULL and zero if it fails by any reason + +*********************************************************************************/ +static void c_String(char **target,unsigned int *lent,char *source,unsigned int len,int level) +{ + unsigned long l; + *lent=0; + *target=NULL; + + if (!len) return; + + *lent=len + (len*0.1) + 15; + + GB.Alloc ((void**)target,sizeof(char)*(*lent)); + + l = *lent; + if (Z_OK != compress2 ((Bytef*)(*target),&l,(Bytef *)source,len,level)) + { + GB.Free((void**)target); + *lent=0; + *target=NULL; + GB.Error("Unable to compress string"); + return; + } + + *lent = (uint)l; +} +/********************************************************************************* +The Driver must provide this function: + +void u_String(char **target,unsigned int *lent,char *source,unsigned int len,int level) + +It is called to decompress a String and return it decompressed. The object will +pass the following values: + +target = NULL +lent = 0 +source = a pointer to the original string +len = length of the original string +level = compression level + + You will never receive a zero lenght string, nor a erroneus 'level' value + +The function must store the decompressed string in 'target', and its length in 'lent', +or NULL and zero if it fails by any reason +*********************************************************************************/ +static void u_String(char **target,unsigned int *lent,char *source,unsigned int len) +{ + int res, try_gzip = 0; + z_stream stream = { + .zalloc = Z_NULL, + .zfree = Z_NULL, + .opaque = Z_NULL, + .avail_in = len, + .next_in = (Bytef *) source, + }; + unsigned long pos = 0; + + *lent = 2 * len; + GB.Alloc((void **) target, *lent); + + stream.avail_out = *lent; + stream.next_out = (Bytef *) *target; + + inflateInit(&stream); + while (1) { + res = inflate(&stream, Z_NO_FLUSH); + switch (res) { + case Z_OK: + break; + case Z_BUF_ERROR: + pos = (unsigned long) (stream.next_out - (unsigned long) *target); + *lent += *lent / 2; + GB.Realloc((void **) target, *lent); + stream.avail_out = *lent - pos; + stream.next_out = (Bytef *) (*target + pos); + break; + case Z_STREAM_END: + pos = (unsigned long) (stream.next_out - (unsigned long) *target); + goto out; + case Z_DATA_ERROR: + /* Maybe we have gzip format? */ + if (try_gzip) { + GB.Error("Invalid compressed string"); + goto error; + } + inflateEnd(&stream); + stream.avail_in = len; + stream.next_in = (Bytef *) source; + stream.avail_out = *lent; + stream.next_out = (Bytef *) *target; + inflateInit2(&stream, 16 + MAX_WBITS); + try_gzip = 1; + continue; + case Z_MEM_ERROR: + GB.Error("Not enough memory: String too long"); + goto error; + default: + GB.Error("Unable to inflate string"); + goto error; + } + } +out: + inflateEnd(&stream); + *lent = pos; + GB.Realloc((void **) target, *lent); + return; + +error: + *lent = 0; + GB.Free((void **) target); +} + +/********************************************************************************* +The Driver must provide this function: + +static void c_File(char *source,char *target,int level) + +It is called to compress a file. The object will pass the following values: + + +source = path of the file to be compressed +target = path of the new compressed file to create +level = compression level + + You will never receive a erroneus 'level' value +*********************************************************************************/ + +static void c_File(char *source,char *target,int level) +{ + FILE *src; + gzFile dst; + size_t len; + char buf[GB_Z_BUFFER]; + char bufmode[4]={'w','b',0,0}; + + if (level != Z_DEFAULT_COMPRESSION ) + bufmode[2] = (char)(level + 48); + + if ((src = fopen(source,"r")) == NULL) + { + GB.Error ("Unable to open file for reading"); + return; + } + + if ((dst = gzopen(target,bufmode)) == NULL) + { + fclose(src); + GB.Error ("Unable to open file for writing"); + return; + } + + while (!feof(src)) + { + len = fread((void*)buf,sizeof(char),GB_Z_BUFFER,src); + + if (len < GB_Z_BUFFER) + { + if (ferror(src)) + { + fclose(src); + gzclose(dst); + GB.Error("Error while reading data"); + return; + } + } + + if (len > 0) + { + if (gzwrite(dst, buf, len) == 0) + { + fclose(src); + gzclose(dst); + GB.Error("Error while writing data"); + return; + } + } + } + + fclose(src); + gzflush(dst, Z_SYNC_FLUSH); + gzclose(dst); +} + +/********************************************************************************* +The Driver must provide this function: + +static void u_File(char *source,char *target,int level) + +It is called to decompress a file. The object will pass the following values: + + +source = path of the file to be decompressed +target = path of the new decompressed file to create +level = compression level + + You will never receive a erroneus 'level' value +*********************************************************************************/ +static void u_File(char *source,char *target) +{ + gzFile src; + FILE *dst; + char buf[GB_Z_BUFFER]; + unsigned int len; + + if ( (src=gzopen(source,"rb"))==NULL) + { + GB.Error ("Unable to open file for reading"); + return; + } + + if ( (dst=fopen(target,"w"))==NULL) + { + gzclose(src); + GB.Error ("Unable to open file for writing"); + return; + } + + while (!gzeof(src)) + { + len=gzread(src,buf,sizeof(char)*GB_Z_BUFFER); + if (len==-1) + { + fclose(dst); + gzclose(src); + GB.Error("Error while reading data"); + return; + } + if (len) + { + if (len != fwrite((void*)buf,sizeof(char),len,dst) ) + { + fclose(dst); + gzclose(src); + GB.Error("Error while writing data"); + return; + } + } + } + + fclose(dst); + gzclose(src); +} + +static void c_Open(char *path,int level, STREAM_COMPRESS *stream) +{ + + char mode[4]={'w','b',0,0}; + + stream->base.desc=&ZStream; + if (level != Z_DEFAULT_COMPRESSION ) mode[2]=(char)(level+48); + stream->mode = MODE_WRITE; + stream->handle = gzopen(path,mode); + + if (stream->handle) return; + + stream->base.desc = NULL; + if ( errno == Z_MEM_ERROR ) + { + GB.Error("Not enough memory to manage selected file"); + return; + } + + GB.Error("Unable to open selected file"); +} + +static void u_Open(char *path, STREAM_COMPRESS *stream) +{ + char mode[3]={'r','b',0}; + + stream->base.desc = &ZStream; + stream->mode = MODE_READ; + stream->handle = gzopen(path, mode); + + if (stream->handle) return; + + stream->base.desc=NULL; + if ( errno == Z_MEM_ERROR ) + { + GB.Error("Not enough memory to manage selected file"); + return; + } + + GB.Error("Unable to open selected file"); +} + +/************************************************************************* +Stream related stuff +**************************************************************************/ +/* not allowed stream methods */ +static int CZ_stream_lof(GB_STREAM *stream, int64_t *len){return -1;} +static int CZ_stream_seek(GB_STREAM *stream, int64_t offset, int whence){ return -1;} +static int CZ_stream_open(GB_STREAM *stream, const char *path, int mode, void *data){return -1;} +/* allowed stream methods */ +static int CZ_stream_tell(GB_STREAM *stream, int64_t *npos) +{ + STREAM_COMPRESS *s = (STREAM_COMPRESS *)stream; + *npos=gztell (s->handle); + if ((*npos)!=-1) return 0; + gzclose (s->handle); + stream->desc=NULL; + return -1; +} + +static int CZ_stream_flush(GB_STREAM *stream) +{ + gzflush(((STREAM_COMPRESS *)stream)->handle,Z_SYNC_FLUSH); + return 0; +} + +int CZ_stream_close(GB_STREAM *stream) +{ + gzclose (((STREAM_COMPRESS *)stream)->handle); + stream->desc=NULL; + return 0; +} + +static int CZ_stream_write(GB_STREAM *stream, char *buffer, int len) +{ + STREAM_COMPRESS *s = (STREAM_COMPRESS *)stream; + + if (s->mode == MODE_READ) + return -1; + + return gzwrite(s->handle, (voidp)buffer, (unsigned)len); +} + +static int CZ_stream_eof(GB_STREAM *stream) +{ + return gzeof (((STREAM_COMPRESS *)stream)->handle); +} + +static int CZ_stream_read(GB_STREAM *stream, char *buffer, int len) +{ + STREAM_COMPRESS *s = (STREAM_COMPRESS *)stream; + + if (s->mode == MODE_WRITE) + return -1; + + return gzread(s->handle, (voidp)buffer, (unsigned)len); +} + + + + +/**************************************************************************** +This array of functions defines what functions the compression component +must call to perform its actions +****************************************************************************/ +static COMPRESS_DRIVER _driver = +{ + "zlib", + + (void*)max_compression, + (void*)min_compression, + (void*)default_compression, + + { + (void*)c_String, + (void*)c_File, + (void*)c_Open, + (void*)CZ_stream_close, + }, + + { + (void*)u_String, + (void*)u_File, + (void*)u_Open, + (void*)CZ_stream_close + } +}; + +/***************************************************************************** + + The component entry and exit functions. + +*****************************************************************************/ + +int EXPORT GB_INIT(void) +{ + /************************************************************************* + When this component is loaded by Gambas runtime, it 'informs' to the + compression component what functions must it call to perform its actions + *************************************************************************/ + GB.GetInterface("gb.compress", COMPRESS_INTERFACE_VERSION, &COMPRESSION); + COMPRESSION.Register(&_driver); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.compress.zlib/src/main.h b/gb.compress.zlib/src/main.h new file mode 100644 index 00000000..4dea215d --- /dev/null +++ b/gb.compress.zlib/src/main.h @@ -0,0 +1,47 @@ +/*************************************************************************** + + main.h + + (c) 2003-2004 Daniel Campos Fern�ndez + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "../gb.compress.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern COMPRESS_INTERFACE COMPRESSION; +extern GB_STREAM_DESC ZStream; +#endif + +static int CZ_stream_lof(GB_STREAM *stream, int64_t *len); +static int CZ_stream_seek(GB_STREAM *stream, int64_t offset, int whence); +static int CZ_stream_open(GB_STREAM *stream, const char *path, int mode, void *data); +static int CZ_stream_tell(GB_STREAM *stream, int64_t *npos); +static int CZ_stream_flush(GB_STREAM *stream); +static int CZ_stream_close(GB_STREAM *stream); +static int CZ_stream_write(GB_STREAM *stream, char *buffer, int len); +static int CZ_stream_eof(GB_STREAM *stream); +static int CZ_stream_read(GB_STREAM *stream, char *buffer, int len); + +#endif /* __MAIN_H */ diff --git a/gb.crypt/AUTHORS b/gb.crypt/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.crypt/COPYING b/gb.crypt/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.crypt/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.crypt/ChangeLog b/gb.crypt/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.crypt/INSTALL b/gb.crypt/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.crypt/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.crypt/Makefile.am b/gb.crypt/Makefile.am new file mode 100644 index 00000000..9b9cbe03 --- /dev/null +++ b/gb.crypt/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @CRYPT_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.crypt/NEWS b/gb.crypt/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.crypt/README b/gb.crypt/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.crypt/acinclude.m4 b/gb.crypt/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.crypt/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.crypt/component.am b/gb.crypt/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.crypt/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.crypt/configure.ac b/gb.crypt/configure.ac new file mode 100644 index 00000000..0858e226 --- /dev/null +++ b/gb.crypt/configure.ac @@ -0,0 +1,27 @@ +dnl ---- configure.ac for gb.crypt + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-crypt, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.crypt) +AC_PROG_LIBTOOL + +GB_COMPONENT( + crypt, CRYPT, gb.crypt, [src], + [], + [GB_FIND(libcrypt.$SHLIBEXT, $prefix /usr/local /usr, lib)], + [-lcrypt], + [], + [Cannot find 'libcrypt' library. It may be merged inside the system C library, so you can ignore that warning...]) + +dnl ---- Remove DISABLED file that could have been generated before + +rm -f DISABLED DISABLED.* FAILED + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.crypt/gambas.h b/gb.crypt/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.crypt/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.crypt/gb_common.h b/gb.crypt/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.crypt/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.crypt/m4 b/gb.crypt/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.crypt/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.crypt/reconf b/gb.crypt/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.crypt/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.crypt/src/Makefile.am b/gb.crypt/src/Makefile.am new file mode 100644 index 00000000..a817be4d --- /dev/null +++ b/gb.crypt/src/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.crypt +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.crypt.la + +gb_crypt_la_LIBADD = @CRYPT_LIB@ +gb_crypt_la_LDFLAGS = -module @LD_FLAGS@ @CRYPT_LDFLAGS@ +gb_crypt_la_CPPFLAGS = @CRYPT_INC@ + +gb_crypt_la_SOURCES = \ + main.h main.c \ + c_crypt.h c_crypt.c + + diff --git a/gb.crypt/src/c_crypt.c b/gb.crypt/src/c_crypt.c new file mode 100644 index 00000000..498034df --- /dev/null +++ b/gb.crypt/src/c_crypt.c @@ -0,0 +1,203 @@ +/*************************************************************************** + + c_crypt.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_CRYPT_C + +#include "gb_common.h" + +#include +#include +#include +#include + +#include "c_crypt.h" +#include "main.h" + +enum { USE_DES, USE_MD5, USE_SHA256, USE_SHA512 }; + +static char *do_crypt(const char *passwd, const char *prefix, int mode) +{ + static char key_table[65] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcedefghijklmnopqrstuvwxyz./"; + static bool init = FALSE; + + int i, n; + char key[18]; + char *result; + char errormsg[64]; + + switch(mode) + { + case USE_MD5: + strcpy(key, "$1$"); + n = 8; + break; + case USE_SHA256: + strcpy(key, "$5$"); + n = 13; + break; + case USE_SHA512: + strcpy(key, "$6$"); + n = 13; + break; + case USE_DES: + default: + n = 2; + } + + if (prefix) + { + if (strlen(prefix) != n) + { + snprintf(errormsg, sizeof(errormsg), "must be %d characters long", n); + goto __BAD_PREFIX; + } + + for (i = 0; i < n; i++) + { + if (strchr(key_table, prefix[i]) == NULL) + { + snprintf(errormsg, sizeof(errormsg), "character '%c' is not allowed" , prefix[i]); + goto __BAD_PREFIX; + } + } + + if (mode == USE_DES) + strcpy(key, prefix); + else + strcat(key, prefix); + } + else + { + if (!init) + { + srandom((unsigned int)time(NULL)); + init = TRUE; + } + + if (mode == USE_DES) + { + for (i = 0; i < 2; i++) + key[i] = key_table[(int)((random() / (RAND_MAX + 1.0)) * sizeof(key_table))]; + key[2] = 0; + } + else if (mode == USE_MD5) + { + for (i = 0; i < 8; i++) + key[i + 3] = key_table[(int)((random() / (RAND_MAX + 1.0)) * sizeof(key_table))]; + key[11] = '$'; + key[12] = 0; + } + else + { + for (i = 0; i < 13; i++) + key[i + 3] = key_table[(int)((random() / (RAND_MAX + 1.0)) * sizeof(key_table))]; + key[16] = '$'; + key[17] = 0; + } + } + + result = crypt(passwd, key); + + if (!result && errno == ENOSYS) + GB.Error("Crypting is not supported on this system"); + + return result; + +__BAD_PREFIX: + + GB.Error("Bad prefix, &1",errormsg); + return NULL; +} + +static bool check_crypt(const char *passwd, const char *crypted) +{ + char *result = crypt(passwd, crypted); + + if (!result && errno == ENOSYS) + GB.Error("Crypting is not supported on this system"); + + if (!result) + return TRUE; + else + return strcmp(result, crypted) != 0; +} + +BEGIN_METHOD(CCRYPT_call_des, GB_STRING password; GB_STRING key) + + char *result; + + result = do_crypt(GB.ToZeroString(ARG(password)), MISSING(key) ? NULL : GB.ToZeroString(ARG(key)), USE_DES); + if (result) + GB.ReturnNewZeroString(result); + +END_METHOD + +BEGIN_METHOD(CCRYPT_call_md5, GB_STRING password; GB_STRING key) + + char *result; + + result = do_crypt(GB.ToZeroString(ARG(password)), MISSING(key) ? NULL : GB.ToZeroString(ARG(key)), USE_MD5); + if (result) + GB.ReturnNewZeroString(result); + +END_METHOD + +BEGIN_METHOD(CCRYPT_call_sha256, GB_STRING password; GB_STRING key) + + char *result; + + result = do_crypt(GB.ToZeroString(ARG(password)), MISSING(key) ? NULL : GB.ToZeroString(ARG(key)), USE_SHA256); + if (result) + GB.ReturnNewZeroString(result); + +END_METHOD + +BEGIN_METHOD(CCRYPT_call_sha512, GB_STRING password; GB_STRING key) + + char *result; + + result = do_crypt(GB.ToZeroString(ARG(password)), MISSING(key) ? NULL : GB.ToZeroString(ARG(key)), USE_SHA512); + if (result) + GB.ReturnNewZeroString(result); + +END_METHOD + +BEGIN_METHOD(CCRYPT_check, GB_STRING password; GB_STRING crypt) + + GB.ReturnBoolean(check_crypt(GB.ToZeroString(ARG(password)), GB.ToZeroString(ARG(crypt)))); + +END_METHOD + +GB_DESC CCryptDesc[] = +{ + GB_DECLARE("Crypt", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_call", "s", CCRYPT_call_md5, "(Password)s[(Prefix)s]"), + GB_STATIC_METHOD("Check", "b", CCRYPT_check, "(Password)s(Crypt)s"), + GB_STATIC_METHOD("DES", "s", CCRYPT_call_des, "(Password)s[(Prefix)s]"), + GB_STATIC_METHOD("MD5", "s", CCRYPT_call_md5, "(Password)s[(Prefix)s]"), + GB_STATIC_METHOD("SHA256", "s", CCRYPT_call_sha256, "(Password)s[(Prefix)s]"), + GB_STATIC_METHOD("SHA512", "s", CCRYPT_call_sha512, "(Password)s[(Prefix)s]"), + + GB_END_DECLARE +}; diff --git a/gb.crypt/src/c_crypt.h b/gb.crypt/src/c_crypt.h new file mode 100644 index 00000000..3a820c51 --- /dev/null +++ b/gb.crypt/src/c_crypt.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + c_crypt.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_CRYPT_H +#define __C_CRYPT_H + +#include "gambas.h" + +#ifndef __C_CRYPT_C +extern GB_DESC CCryptDesc[]; +#endif + +#endif diff --git a/gb.crypt/src/gb.crypt.component b/gb.crypt/src/gb.crypt.component new file mode 100644 index 00000000..1822a822 --- /dev/null +++ b/gb.crypt/src/gb.crypt.component @@ -0,0 +1,7 @@ +[Component] +Key=gb.crypt +Name=MD5/DES crypting component +Name[fr]=Chiffrage MD5/DES +Name[tr]=MD5/DES şifreleme bileşeni +Author=Benoît Minisini + diff --git a/gb.crypt/src/main.c b/gb.crypt/src/main.c new file mode 100644 index 00000000..541b2526 --- /dev/null +++ b/gb.crypt/src/main.c @@ -0,0 +1,47 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "c_crypt.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CCryptDesc, + NULL +}; + + +int EXPORT GB_INIT(void) +{ + return 0; +} + + +void EXPORT GB_EXIT() +{ +} + diff --git a/gb.crypt/src/main.h b/gb.crypt/src/main.h new file mode 100644 index 00000000..ffbea46b --- /dev/null +++ b/gb.crypt/src/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.db.mysql/AUTHORS b/gb.db.mysql/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.db.mysql/COPYING b/gb.db.mysql/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.db.mysql/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.db.mysql/ChangeLog b/gb.db.mysql/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.db.mysql/INSTALL b/gb.db.mysql/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.db.mysql/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.db.mysql/Makefile.am b/gb.db.mysql/Makefile.am new file mode 100644 index 00000000..ee21368f --- /dev/null +++ b/gb.db.mysql/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @MYSQL_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.db.mysql/NEWS b/gb.db.mysql/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.db.mysql/README b/gb.db.mysql/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.db.mysql/acinclude.m4 b/gb.db.mysql/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.db.mysql/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.db.mysql/component.am b/gb.db.mysql/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.db.mysql/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.db.mysql/configure.ac b/gb.db.mysql/configure.ac new file mode 100644 index 00000000..455d4f84 --- /dev/null +++ b/gb.db.mysql/configure.ac @@ -0,0 +1,22 @@ +dnl ---- configure.ac for gb.db.mysql driver + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-db-mysql, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.db.mysql) +AC_PROG_LIBTOOL +dnl LT_INIT + +GB_COMPONENT( + mysql, MYSQL, gb.db.mysql, [src], + [GB_FIND(mysql.h my_config.h, $prefix /opt/local /opt /usr/local/lib /usr/local /usr/lib /usr, include mysql*/include)], + [GB_FIND(libmysqlclient.$SHLIBEXT, $prefix /opt/local /opt /usr/local /usr, lib mysql*/lib)], + [$C_LIB -lmysqlclient -lz]) + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.db.mysql/gambas.h b/gb.db.mysql/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.db.mysql/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.db.mysql/gb.db.h b/gb.db.mysql/gb.db.h new file mode 120000 index 00000000..563bd00c --- /dev/null +++ b/gb.db.mysql/gb.db.h @@ -0,0 +1 @@ +../main/lib/db/gb.db.h \ No newline at end of file diff --git a/gb.db.mysql/gb.db.proto.h b/gb.db.mysql/gb.db.proto.h new file mode 120000 index 00000000..0a8e6920 --- /dev/null +++ b/gb.db.mysql/gb.db.proto.h @@ -0,0 +1 @@ +../main/lib/db/gb.db.proto.h \ No newline at end of file diff --git a/gb.db.mysql/gb_common.h b/gb.db.mysql/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.db.mysql/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.db.mysql/m4 b/gb.db.mysql/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.db.mysql/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.db.mysql/reconf b/gb.db.mysql/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.db.mysql/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.db.mysql/src/Makefile.am b/gb.db.mysql/src/Makefile.am new file mode 100755 index 00000000..dd2502b5 --- /dev/null +++ b/gb.db.mysql/src/Makefile.am @@ -0,0 +1,11 @@ +COMPONENT = gb.db.mysql +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.db.mysql.la + +gb_db_mysql_la_LIBADD = @MYSQL_LIB@ +gb_db_mysql_la_LDFLAGS = -module @LD_FLAGS@ @MYSQL_LDFLAGS@ +gb_db_mysql_la_CPPFLAGS = @MYSQL_INC@ + +gb_db_mysql_la_SOURCES = \ + main.h main.c diff --git a/gb.db.mysql/src/gb.db.mysql.component b/gb.db.mysql/src/gb.db.mysql.component new file mode 100644 index 00000000..19cff9bc --- /dev/null +++ b/gb.db.mysql/src/gb.db.mysql.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.db.mysql +Author=Nigel Gerrard,Benoît Minisini +State=0 +Require=gb.db diff --git a/gb.db.mysql/src/main.c b/gb.db.mysql/src/main.c new file mode 100644 index 00000000..78dec8d5 --- /dev/null +++ b/gb.db.mysql/src/main.c @@ -0,0 +1,2704 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include +#include + +#include + +#include "gb.db.proto.h" +#include "main.h" + +typedef + struct { + const char *pattern; + int type; + } + CONV_STRING_TYPE; + + +GB_INTERFACE GB EXPORT; +DB_INTERFACE DB EXPORT; + +static char _buffer[125]; +static DB_DRIVER _driver; +/*static int _print_query = FALSE;*/ + +// Cached queries +#define CACHE(_db) ((GB_HASHTABLE)(_db)->data) + +typedef + struct { + MYSQL_RES *res; + time_t timestamp; + } + CACHE_ENTRY; + +// mySQL datatypes + +static CONV_STRING_TYPE _types[] = + { + { "tinyint", FIELD_TYPE_TINY }, + { "smallint", FIELD_TYPE_SHORT }, + { "mediumint", FIELD_TYPE_INT24 }, + { "int", FIELD_TYPE_LONG }, + { "bigint", FIELD_TYPE_LONGLONG }, + { "decimal", FIELD_TYPE_DECIMAL }, + { "numeric", FIELD_TYPE_DECIMAL }, + { "float", FIELD_TYPE_FLOAT }, + { "double", FIELD_TYPE_DOUBLE }, + { "real", FIELD_TYPE_DOUBLE }, + { "timestamp", FIELD_TYPE_TIMESTAMP }, + { "date", FIELD_TYPE_DATE }, + { "time", FIELD_TYPE_TIME }, + { "datetime", FIELD_TYPE_DATETIME }, + { "year", FIELD_TYPE_YEAR }, + { "char", FIELD_TYPE_STRING }, + { "varchar", FIELD_TYPE_VAR_STRING }, + { "blob", FIELD_TYPE_BLOB }, + { "tinyblob", FIELD_TYPE_TINY_BLOB }, + { "mediumblob", FIELD_TYPE_MEDIUM_BLOB }, + { "longblob", FIELD_TYPE_LONG_BLOB }, + { "text", FIELD_TYPE_BLOB }, + { "tinytext", FIELD_TYPE_TINY_BLOB }, + { "mediumtext", FIELD_TYPE_MEDIUM_BLOB }, + { "longtext", FIELD_TYPE_LONG_BLOB }, + { "set", FIELD_TYPE_SET }, + { "enum", FIELD_TYPE_ENUM }, + { "bit", FIELD_TYPE_BIT }, + { "null", FIELD_TYPE_NULL }, + { NULL, 0 }, + }; + +/* internal function to quote a value stored as a string */ + +static void quote_string(const char *data, long len, DB_FORMAT_CALLBACK add) +{ + int i; + unsigned char c; + //char buffer[8]; + + (*add)("'", 1); + for (i = 0; i < len; i++) + { + c = (unsigned char)data[i]; + if (c == '\\') + (*add)("\\\\", 2); + else if (c == '\'') + (*add)("''", 2); + else if (c == 0) + (*add)("\\0", 2); + else + (*add)((char *)&c, 1); + } + (*add)("'", 1); +} + +/* internal function to quote a value stored as a blob */ + +#define quote_blob quote_string + +/* Quote a string and returns the result as a temporary string */ + +static char *get_quote_string(const char *str, int len, char quote) +{ + char *res, *p, c; + int len_res; + int i; + + len_res = len; + for (i = 0; i < len; i++) + { + c = str[i]; + if (c == quote || c == '\\' || c == 0) + len_res++; + } + + res = GB.TempString(NULL, len_res); + + p = res; + for (i = 0; i < len; i++) + { + c = str[i]; + if (c == '\\' || c == quote) + { + *p++ = c; + *p++ = c; + } + else if (c == 0) + { + *p++ = '\\'; + *p++ = '0'; + } + else + *p++ = c; + } + *p = 0; + + return res; +} + +// Internal function to convert a database type into a Gambas type +// +// Look at https://dev.mysql.com/doc/refman/5.0/en/c-api-data-structures.html +// for how to make the difference between a text field and a blob field. + +#define IS_BINARY_FIELD(_f) ((_f)->charsetnr == 63) +#define SET_BINARY_FIELD(_f, _v) ((_f)->charsetnr = (_v) ? 63 : 0) + +static GB_TYPE conv_type(const MYSQL_FIELD *f) +{ + switch(f->type) + { + case FIELD_TYPE_TINY: + return (f->max_length == 1 && f->length == 1) ? GB_T_BOOLEAN : GB_T_INTEGER; + + case FIELD_TYPE_INT24: + case FIELD_TYPE_SHORT: + case FIELD_TYPE_LONG: + case FIELD_TYPE_YEAR: + return GB_T_INTEGER; + + case FIELD_TYPE_LONGLONG: + return GB_T_LONG; + + case FIELD_TYPE_FLOAT: + case FIELD_TYPE_DOUBLE: + case FIELD_TYPE_DECIMAL: + return GB_T_FLOAT; + + case FIELD_TYPE_DATE: + case FIELD_TYPE_DATETIME: + case FIELD_TYPE_TIME: + case FIELD_TYPE_TIMESTAMP: + return GB_T_DATE; + + case FIELD_TYPE_LONG_BLOB: + case FIELD_TYPE_TINY_BLOB: + case FIELD_TYPE_MEDIUM_BLOB: + case FIELD_TYPE_BLOB: + if (IS_BINARY_FIELD(f)) + return DB_T_BLOB; + else + return GB_T_STRING; + + case FIELD_TYPE_BIT: + if (f->max_length == 1) + return GB_T_BOOLEAN; + else if (f->max_length <= 32) + return GB_T_INTEGER; + else if (f->max_length <= 64) + return GB_T_LONG; + + case FIELD_TYPE_STRING: + case FIELD_TYPE_VAR_STRING: + case FIELD_TYPE_SET: + case FIELD_TYPE_ENUM: + default: + //fprintf(stderr, "FIELD_TYPE_*: %d\n", len); + return GB_T_STRING; + + } +} + + +// Internal function to convert a string database type +// into a fake MYSQL_FIELD structure + +static void conv_string_type(const char *type, MYSQL_FIELD *f) +{ + CONV_STRING_TYPE *cst; + long l; + + if (strncmp(type, "national ", 9) == 0) + type += 9; + + for (cst = _types; cst->pattern; cst++) + { + if (strncmp(type, cst->pattern, strlen(cst->pattern)) == 0) + break; + } + + if (cst->type) + { + SET_BINARY_FIELD(f, FALSE); + f->max_length = 0; + + if (cst->type == FIELD_TYPE_BLOB || cst->type == FIELD_TYPE_TINY_BLOB || cst->type == FIELD_TYPE_MEDIUM_BLOB || cst->type == FIELD_TYPE_LONG_BLOB) + { + SET_BINARY_FIELD(f, strcmp(&type[strlen(type) - 4], "blob") == 0); + } + else + { + type += strlen(cst->pattern); + if (sscanf(type, "(%ld)", &l) == 1) + { + f->max_length = l; + if (cst->type == FIELD_TYPE_TINY) + f->length = l; + } + } + } + + f->type = cst->type; +} + +#if 0 +static const char *get_type_name(int type) +{ + CONV_STRING_TYPE *cst; + + for (cst = _types; cst->pattern; cst++) + { + if (cst->type == type) + return cst->pattern; + } + + return "?"; +} +#endif + +/* Internal function to convert a database value into a Gambas variant value */ + +static void conv_data(int version, const char *data, long data_length, GB_VARIANT_VALUE *val, MYSQL_FIELD *f) +{ + GB_VALUE conv; + GB_DATE_SERIAL date; + double sec; + int type = f->type; + + switch (type) + { + case FIELD_TYPE_TINY: + + if (f->max_length == 1 && f->length == 1) + { + val->type = GB_T_BOOLEAN; + /*GB.NumberFromString(GB_NB_READ_INTEGER, data, strlen(data), &conv);*/ + val->value._boolean = atoi(data) != 0 ? -1 : 0; + } + else + { + GB.NumberFromString(GB_NB_READ_INTEGER, data, strlen(data), &conv); + + val->type = GB_T_INTEGER; + val->value._integer = conv._integer.value; + } + + break; + + case FIELD_TYPE_INT24: + case FIELD_TYPE_SHORT: + case FIELD_TYPE_LONG: + /*case FIELD_TYPE_TINY:*/ + case FIELD_TYPE_YEAR: + + GB.NumberFromString(GB_NB_READ_INTEGER, data, strlen(data), &conv); + + val->type = GB_T_INTEGER; + val->value._integer = conv._integer.value; + + break; + + case FIELD_TYPE_LONGLONG: + + GB.NumberFromString(GB_NB_READ_LONG, data, strlen(data), &conv); + + val->type = GB_T_LONG; + val->value._long = conv._long.value; + + break; + + case FIELD_TYPE_FLOAT: + case FIELD_TYPE_DOUBLE: + case FIELD_TYPE_DECIMAL: + + GB.NumberFromString(GB_NB_READ_FLOAT, data, strlen(data), &conv); + + val->type = GB_T_FLOAT; + val->value._float = conv._float.value; + + break; + + case FIELD_TYPE_DATE: + case FIELD_TYPE_DATETIME: + case FIELD_TYPE_TIME: + case FIELD_TYPE_TIMESTAMP: + + // TIMESTAMP display format changed since MySQL 4.1! + if (type == FIELD_TYPE_TIMESTAMP && version >= 40100) + type = FIELD_TYPE_DATETIME; + + memset(&date, 0, sizeof(date)); + + switch(type) + { + case FIELD_TYPE_DATE: + + sscanf(data, "%4d-%2d-%2d", &date.year, &date.month, &date.day); + break; + + case FIELD_TYPE_TIME: + + sscanf(data, "%4d:%2d:%lf", &date.hour, &date.min, &sec); + date.sec = (short)sec; + date.msec = (short)((sec - date.sec) * 1000 + 0.5); + break; + + case FIELD_TYPE_DATETIME: + + sscanf(data, "%4d-%2d-%2d %2d:%2d:%lf", &date.year, &date.month, &date.day, &date.hour, &date.min, &sec); + date.sec = (short)sec; + date.msec = (short)((sec - date.sec) * 1000 + 0.5); + break; + + case FIELD_TYPE_TIMESTAMP: + switch(strlen(data)) + { + case 14: + sscanf(data, "%4d%2d%2d%2d%2d%lf", &date.year, &date.month, &date.day, &date.hour, &date.min, &sec); + date.sec = (short)sec; + date.msec = (short)((sec - date.sec) * 1000 + 0.5); + break; + case 12: + sscanf(data, "%2d%2d%2d%2d%2d%lf", &date.year, &date.month, &date.day, &date.hour, &date.min, &sec); + date.sec = (short)sec; + date.msec = (short)((sec - date.sec) * 1000 + 0.5); + break; + case 10: + sscanf(data, "%2d%2d%2d%2d%2d", &date.year, &date.month, &date.day, &date.hour, &date.min ); + break; + case 8: + sscanf(data, "%4d%2d%2d", &date.year, &date.month, &date.day); + break; + case 6: + sscanf(data, "%2d%2d%2d", &date.year, &date.month, &date.day); + break; + case 4: + sscanf(data, "%2d%2d", &date.year, &date.month); + break; + case 2: + sscanf(data, "%2d", &date.year); + break; + } + if (date.year < 100) + date.year += 1900; + break; + } + + GB.MakeDate(&date, (GB_DATE *)&conv); + + val->type = GB_T_DATE; + val->value._date.date = conv._date.value.date; + val->value._date.time = conv._date.value.time; + + break; + + case FIELD_TYPE_BLOB: + case FIELD_TYPE_LONG_BLOB: + case FIELD_TYPE_TINY_BLOB: + case FIELD_TYPE_MEDIUM_BLOB: + if (IS_BINARY_FIELD(f)) + { + // The BLOB are read by the blob_read() driver function + // You must set NULL there. + val->type = GB_T_NULL; + break; + } + // else continue! + + case FIELD_TYPE_STRING: + case FIELD_TYPE_VAR_STRING: + case FIELD_TYPE_SET: + case FIELD_TYPE_ENUM: + default: + val->type = GB_T_CSTRING; + val->value._string = (char *)data; + //val->_string.start = 0; + //if (data && data_length == 0) + // data_length = strlen(data); + //val->_string.len = data_length; + //fprintf(stderr, "conv_data: len = %d\n", len); + /*GB.NewString(&val->_string.value, data, strlen(data));*/ + + break; + } + +} + +/* Internal function to substitute the table name into a query */ + +static char *query_param[3]; + +static void query_get_param(int index, char **str, int *len, char quote) +{ + if (index > 3) + return; + + index--; + *str = query_param[index]; + *len = strlen(*str); + + if (quote == '\'' || quote == '`') + { + *str = get_quote_string(*str, *len, quote); + *len = GB.StringLength(*str); + } +} + +static void check_connection(MYSQL *conn) +{ + unsigned long thread_id; + + thread_id = mysql_thread_id(conn); + + mysql_ping(conn); + + if (mysql_thread_id(conn) != thread_id) + { + if (DB.IsDebug()) + fprintf(stderr, "gb.db.mysql: connection lost\n"); + // Connection has been reestablished, set utf8 again + mysql_query(conn, "set names 'utf8'"); + } +} + +/* Internal function to run a query */ + +static int do_query(DB_DATABASE *db, const char *error, MYSQL_RES **pres, const char *qtemp, int nsubst, ...) +{ + MYSQL *conn = (MYSQL *)db->handle; + va_list args; + int i; + const char *query; + MYSQL_RES *res; + int ret; + + if (nsubst) + { + va_start(args, nsubst); + if (nsubst > 3) + nsubst = 3; + for (i = 0; i < nsubst; i++) + query_param[i] = va_arg(args, char *); + + query = DB.SubstString(qtemp, 0, query_get_param); + } + else + query = qtemp; + + if (DB.IsDebug()) + fprintf(stderr, "gb.db.mysql: %p: %s\n", conn, query); + + check_connection(conn); + + if (mysql_query(conn, query)) + { + ret = TRUE; + if (error) + GB.Error(error, mysql_error(conn)); + } + else + { + res = mysql_store_result(conn); + ret = FALSE; + if (pres) + *pres = res; + else + mysql_free_result(res); + } + + db->error = mysql_errno(conn); + return ret; +} + +static int do_query_cached(DB_DATABASE *db, const char *error, MYSQL_RES **pres, char *key, const char *qtemp, int nsubst, ...) +{ + CACHE_ENTRY *entry; + int len_key; + bool added; + time_t t; + va_list args; + int i; + const char *query; + int ret; + + if (nsubst) + { + va_start(args, nsubst); + if (nsubst > 3) + nsubst = 3; + for (i = 0; i < nsubst; i++) + query_param[i] = va_arg(args, char *); + + query = DB.SubstString(qtemp, 0, query_get_param); + key = DB.SubstString(key, 0, query_get_param); + } + else + query = qtemp; + + len_key = strlen(key); + added = GB.HashTable.Get(CACHE(db), key, len_key, POINTER(&entry)); + if (added) + { + GB.AllocZero(POINTER(&entry), sizeof(CACHE_ENTRY)); + GB.HashTable.Add(CACHE(db), key, len_key, entry); + } + + t = time(NULL); + + //fprintf(stderr, "-- do_query_cached: %s [ %p %ld ]\n", key, entry->res, entry->timestamp); + + if (entry->res && (t - entry->timestamp) < 30) + { + mysql_data_seek(entry->res, 0); + *pres = entry->res; + return 0; + } + + entry->timestamp = t; + + if (entry->res) + mysql_free_result(entry->res); + + ret = do_query(db, error, &entry->res, query, 0); + if (ret == 0) + *pres = entry->res; + return ret; +} + + +/* Internal function to return database version number as a XXYYZZ integer number*/ + +static int db_version(DB_DATABASE *db) +{ + //Check db version + const char *vquery = "select left(version(),6)"; + long dbversion =0; + MYSQL_RES *res; + MYSQL_ROW row; + + if (!do_query(db, NULL, &res, vquery, 0)) + { + unsigned int verMain, verMajor, verMinor; + row = mysql_fetch_row(res); + sscanf(row[0],"%2u.%2u.%2u", &verMain, &verMajor, &verMinor); + dbversion = ((verMain * 10000) + (verMajor * 100) + verMinor); + mysql_free_result(res); + } + return dbversion; +} + +/* Search in the first column a result for a specific name */ + +static bool search_result(MYSQL_RES *res, const char *name, MYSQL_ROW *row) +{ + int i; + MYSQL_ROW r; + + for (i = 0; i < mysql_num_rows(res); i++ ) + { + r = mysql_fetch_row(res); + + if (!strcmp(r[0], name)) + { + if (row) + *row = r; + break; + } + } + + return (i >= mysql_num_rows(res)); +} + +// Clear the query cache + +static void free_cache(void *data) +{ + GB.Free(&data); +} + +static void clear_cache(DB_DATABASE *db) +{ + GB.HashTable.Enum(CACHE(db), free_cache); + GB.HashTable.Free((GB_HASHTABLE *)&db->data); +} + +static void remove_cache_entry(DB_DATABASE *db, char *key) +{ + CACHE_ENTRY *entry; + + if (GB.HashTable.Get(CACHE(db), key, -1, POINTER(&entry))) + return; + + mysql_free_result(entry->res); + GB.Free(POINTER(&entry)); + GB.HashTable.Remove(CACHE(db), key, -1); +} + +static void clear_table_cache(DB_DATABASE *db, const char *table) +{ + char key[strlen(table) + 5]; + + strcpy(key, "sts:"); strcat(key, table); remove_cache_entry(db, key); + //strcpy(key, "sc:"); strcat(key, table); remove_cache_entry(db, key); + strcpy(key, "sfc:"); strcat(key, table); remove_cache_entry(db, key); + strcpy(key, "si:"); strcat(key, table); remove_cache_entry(db, key); +} + +/***************************************************************************** + + get_quote() + + Returns the character used for quoting object names. + +*****************************************************************************/ + +static const char *get_quote(void) +{ + return QUOTE_STRING; +} + +/***************************************************************************** + + open_database() + + Connect to a database. + + points at a structure describing each connection parameter. + + This function must return a database handle, or NULL if the connection + has failed. + +*****************************************************************************/ + +static void set_character_set(DB_DATABASE *db) +{ + MYSQL_RES *res; + MYSQL_ROW row; + +// sys_charset = GB.System.Charset(); +// db_charset = NULL; + +// for (i = 0; i < strlen(sys_charset); i++) +// { +// c = sys_charset[i]; +// if (!c) +// break; +// c = tolower(c); +// if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))) +// continue; +// GB.AddString(&db_charset, (const char *)&c, 1); +// } +// +// db_charset[i] = 0; + + if (do_query(db, NULL, NULL, "set names 'utf8'", 0)) + fprintf(stderr, "WARNING: Unable to set database charset to UTF-8\n"); + +// GB.FreeString(&db_charset); + + if (do_query(db, "Unable to get database charset: &1", &res, "show variables like 'character_set_client'", 0)) + return; + + if (search_result(res, "character_set_client", &row)) + return; + + db->charset = GB.NewZeroString(row[1]); + //fprintf(stderr, "charset is '%s'\n", db->charset); + mysql_free_result(res); +} + +static int open_database(DB_DESC *desc, DB_DATABASE *db) +{ + MYSQL *conn; + char *name; + char *host; + char *socket; + my_bool reconnect = TRUE; + unsigned int timeout; + + conn = mysql_init(NULL); + + // NULL is a possible database name + name = desc->name; + + //mysql_options(conn, MYSQL_READ_DEFAULT_GROUP,"Gambas"); + + //fprintf(stderr, "mysql_real_connect: host = '%s'\n", desc->host); + + host = desc->host; + if (host && *host == '/') + { + socket = host; + host = NULL; + } + else + socket = NULL; + + mysql_options(conn, MYSQL_OPT_RECONNECT, &reconnect); + + timeout = db->timeout; + mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + /*timeout /= 3; + mysql_options(conn, MYSQL_OPT_READ_TIMEOUT, &timeout);*/ + + if (!mysql_real_connect(conn, host, desc->user, desc->password, + name, desc->port == NULL ? 0 : atoi(desc->port), socket, + CLIENT_MULTI_RESULTS | CLIENT_REMEMBER_OPTIONS /*client flag */)){ + mysql_close(conn); + GB.Error("Cannot open database: &1", mysql_error(conn)); + return TRUE; + } + + db->handle = conn; + db->version = db_version(db); + + set_character_set(db); + + GB.HashTable.New(POINTER(&db->data), GB_COMP_BINARY); + /* flags: none at the moment */ + return FALSE; +} + + +/***************************************************************************** + + close_database() + + Terminates the database connection. + + contains the database handle. + +*****************************************************************************/ + +static void close_database(DB_DATABASE *db) +{ + MYSQL *conn = (MYSQL *)db->handle; + + clear_cache(db); + + if (conn) + mysql_close(conn); +} + + +/***************************************************************************** + + get_collations() + + Return the available collations as a Gambas string array. + +*****************************************************************************/ + +static GB_ARRAY get_collations(DB_DATABASE *db) +{ + const char *query = "show collation like '%'"; + + MYSQL_RES *res; + GB_ARRAY array; + MYSQL_ROW row; + int i, n; + + if (do_query(db, "Unable to get collations: &1", &res, query, 0)) + return NULL; + + n = mysql_num_rows(res); + + GB.Array.New(&array, GB_T_STRING, n); + for (i = 0; i < n; i++) + { + row = mysql_fetch_row(res); + *((char **)GB.Array.Get(array, i)) = GB.NewZeroString(row[0]); + } + + return array; +} + +/***************************************************************************** + + format_value() + + This function transforms a gambas value into a string value that can + be inserted into a SQL query. + + points to the value. + is a callback called to insert the string into the query. + + This function must return TRUE if it translates the value, and FALSE if + it does not. + + If the value is not translated, then a default translation is used. + +*****************************************************************************/ + +static int format_value(GB_VALUE *arg, DB_FORMAT_CALLBACK add) +{ + int l; + GB_DATE_SERIAL *date; + + switch (arg->type) + { + case GB_T_BOOLEAN: +/*Note this is likely to go to a tinyint */ + + if (VALUE((GB_BOOLEAN *)arg)) + add("'1'", 3); + else + add("'0'", 3); + return TRUE; + + case GB_T_STRING: + case GB_T_CSTRING: + + quote_string(VALUE((GB_STRING *)arg).addr + VALUE((GB_STRING *)arg).start, VALUE((GB_STRING *)arg).len, add); + return TRUE; + + case GB_T_DATE: + + date = GB.SplitDate((GB_DATE *)arg); + + l = sprintf(_buffer, "'%04d-%02d-%02d %02d:%02d:%02d", + date->year, date->month, date->day, + date->hour, date->min, date->sec); + + add(_buffer, l); + + if (date->msec) + { + l = sprintf(_buffer, ".%03d", date->msec); + add(_buffer, l); + } + + add("'", 1); + + //fprintf(stderr, "format_value: %s / %d %d\n", _buffer, ((GB_DATE *)arg)->value.time, date->msec); + + return TRUE; + + default: + return FALSE; + } +} + + +/***************************************************************************** + + format_blob() + + This function transforms a blob value into a string value that can + be inserted into a SQL query. + + points to the DB_BLOB structure. + is a callback called to insert the string into the query. + +*****************************************************************************/ + +static void format_blob(DB_BLOB *blob, DB_FORMAT_CALLBACK add) +{ + quote_blob(blob->data, blob->length, add); +} + + +/***************************************************************************** + + exec_query() + + Send a query to the server and gets the result. + + is the database handle, as returned by open_database() + is the query string. + will receive the result handle of the query. + is an error message used when the query failed. + + can be NULL, when we don't care getting the result. + +*****************************************************************************/ + +static int exec_query(DB_DATABASE *db, const char *query, DB_RESULT *result, const char *err) +{ + return do_query(db, err, (MYSQL_RES **)result, query, 0); +} + + +/***************************************************************************** + + get_last_insert_id() + + Return the value of the last serial field used in an INSERT statement + + is the database handle, as returned by open_database() + +*****************************************************************************/ + +static int64_t get_last_insert_id(DB_DATABASE *db) +{ + MYSQL_RES *res; + MYSQL_ROW row; + + if (do_query(db, "Unable to retrieve last insert id", &res, "select last_insert_id();", 0)) + return -1; + + row = mysql_fetch_row(res); + return atoll(row[0]); +} + + +/***************************************************************************** + + query_init() + + Initialize an info structure from a query result. + + is the handle of the query result. + points to the info structure. + will receive the number of records returned by the query. + + This function must initialize the info->nfield field with the number of + field in the query result. + +*****************************************************************************/ + +static void query_init(DB_RESULT result, DB_INFO *info, int *count) +{ + MYSQL_RES *res = (MYSQL_RES *)result; + + if (res) + { + *count = mysql_num_rows(res); + info->nfield = mysql_num_fields(res); + } + else + { + *count = 0; + info->nfield = 0; + } +} + + +/***************************************************************************** + + query_release() + + Free the info structure filled by query_init() and the result handle. + + is the handle of the query result. + points to the info structure. + +*****************************************************************************/ + +static void query_release(DB_RESULT result, DB_INFO *info) +{ + mysql_free_result((MYSQL_RES *)result); +} + + +/***************************************************************************** + + query_fill() + + Fill a result buffer with the value of each field of a record. + + is the database handle, as returned by open_database() + is the handle of the result. + is the index of the record in the result. + points to an array having one element for each field in the + result. + is a boolean telling if we want the next row. + + This function must return DB_OK, DB_ERROR or DB_NO_DATA + + This function must use GB.StoreVariant() to store the value in the + buffer. + +*****************************************************************************/ + +static int query_fill(DB_DATABASE *db, DB_RESULT result, int pos, GB_VARIANT_VALUE *buffer, int next) +{ + MYSQL_RES *res = (MYSQL_RES *)result; + MYSQL_FIELD *field; + MYSQL_ROW row; + int i; + char *data; + GB_VARIANT value; + + if (!next) + mysql_data_seek(res, pos);/* move to record */ + + row = mysql_fetch_row(res); + mysql_field_seek(res, 0); + for (i = 0; i < mysql_num_fields(res); i++) + { + field = mysql_fetch_field(res); + data = row[i]; + + value.type = GB_T_VARIANT; + value.value.type = GB_T_NULL; + + if (data) + conv_data(db->version, data, mysql_fetch_lengths(res)[i], &value.value, field); + + GB.StoreVariant(&value, &buffer[i]); + + //fprintf(stderr, "query_fill: %d: (%d, %d) : %s : %d\n", i, field->type, field->length, data, buffer[i].type); + } + + return DB_OK; +} + + +/***************************************************************************** + + blob_read() + + Returns the value of a BLOB field. + + is the handle of the result. + is the index of the record in the result. + points at a DB_BLOB structure that will receive a pointer to the + data and its length. + +*****************************************************************************/ + +static void blob_read(DB_RESULT result, int pos, int field, DB_BLOB *blob) +{ + MYSQL_RES *res = (MYSQL_RES *)result; + MYSQL_ROW row; + + mysql_data_seek(res, pos);/* move to record */ + row = mysql_fetch_row(res); + + blob->data = row[field]; + blob->length = mysql_fetch_lengths(res)[field]; + blob->constant = TRUE; + + //fprintf(stderr, "blob_read: %ld: %s\n", blob->length, blob->data); +} + + +/***************************************************************************** + + field_name() + + Return the name of a field in a result from its index. + + is the result handle. + is the field index. + +*****************************************************************************/ + +static char *field_name(DB_RESULT result, int field) +{ + MYSQL_FIELD *fld; + int i, num_fields = mysql_num_fields((MYSQL_RES *)result); + char *table1 = mysql_fetch_field_direct((MYSQL_RES *)result, 0)->table; + bool MultiTables = FALSE; + + // Need to identify whether multiple tables included + fld = mysql_fetch_fields((MYSQL_RES *)result); + for ( i = 1; i < num_fields; i++ ){ + if (strcmp(table1, fld[i].table) != 0){ + MultiTables = TRUE; + break; + } + } + fld = mysql_fetch_field_direct((MYSQL_RES *)result, field); + // GB.Alloc((void **)&full, strlen(fld->table) + strlen(fld->name)); + if (MultiTables && *fld->table){ + sprintf(_buffer, "%s.%s", fld->table, fld->name); + return _buffer; + } + else { + return fld->name; + } + //return mysql_fetch_field_direct((MYSQL_RES *)result, field)->name; +} + + +/***************************************************************************** + + field_index() + + Return the index of a field in a result from its name. + + is the result handle. + is the field name. + +*****************************************************************************/ + +static int field_index(DB_RESULT Result, const char *name, DB_DATABASE *db) +{ + unsigned int num_fields; + unsigned int i; + MYSQL_FIELD *field; + //char *table = NULL, *fld = NULL; + char *table; + const char *fld; + MYSQL_RES *result = (MYSQL_RES *)Result; + + fld = strchr(name, (int)FLD_SEP); + if (fld) + { /* Field does includes table info */ + table = GB.NewString(name, fld - name); + fld = fld + 1; + } + else + { + table = NULL; + fld = name; + } + + num_fields = mysql_num_fields(result); + + if (strcmp(name,fld)!=0) + { /* table name included */ + mysql_field_seek(result,0); /* start at beginning */ + for (i = 0; i < num_fields; i++) + { + field = mysql_fetch_field(result); + if (strcmp( fld, field->name) == 0 && strcmp( table, field->table) == 0) + { + GB.FreeString(&table); + return i; + } + } + fld = name; + } + + if (table) + GB.FreeString(&table); + + /* Do not consider table name, also reached where table cannot be found. * + * Mysql can include . in the fieldname!! */ + mysql_field_seek(result, 0); /* start at beginning */ + for (i = 0; i < num_fields; i++) + { + field = mysql_fetch_field(result); + if (strcmp( fld, field->name) == 0) + return i; + } + + return -1; +} + + +/***************************************************************************** + + field_type() + + Return the Gambas type of a field in a result from its index. + + is the result handle. + is the field index. + +*****************************************************************************/ + +static GB_TYPE field_type(DB_RESULT result, int field) +{ + MYSQL_FIELD *f = mysql_fetch_field_direct((MYSQL_RES *)result, field); + GB_TYPE type = conv_type(f); + + return type; +} + + +/***************************************************************************** + + field_length() + + Return the length of a field in a result from its index. + + is the result handle. + is the field index. + +*****************************************************************************/ + +static int field_length(DB_RESULT result, int field) +{ + MYSQL_FIELD *f = mysql_fetch_field_direct((MYSQL_RES *)result, field); + GB_TYPE type = conv_type(f); + + if (type != GB_T_STRING) + return 0; + else + return f->max_length; +} + + +/***************************************************************************** + + begin_transaction() + + Begin a transaction. + + is the database handle. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + + In mysql commit/rollback can only be used with transaction safe tables (BDB, + or InnoDB tables) + + ISAM, MyISAM and HEAP tables will commit straight away. The transaction + methods are therefore ignored. + +*****************************************************************************/ + +static int begin_transaction(DB_DATABASE *db) +{ + /* Autocommit is on by default. Lets set it off. */ + /* BM: why not doing that when we open the connection ? */ + //do_query(db, "Unable to set autocommit to 0: &1", NULL, "set autocommit=0", 0); + return do_query(db, "Unable to begin transaction: &1", NULL, "START TRANSACTION", 0); +} + + +/***************************************************************************** + + commit_transaction() + + Commit a transaction. + + is the database handle. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int commit_transaction(DB_DATABASE *db) +{ + bool ret = do_query(db, "Unable to commit transaction: &1", NULL, "COMMIT", 0); + /* Autocommit needs to be set back on. */ + /* BM: and what happens if transactions are imbricated ? */ + //do_query(db, "Unable to set autocommit to On: &1", NULL, "set autocommit=1", 0); + return ret; +} + + +/***************************************************************************** + + rollback_transaction() + + Rollback a transaction. + + is the database handle. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + + In mysql commit/rollback can only be used with transaction safe tables (BDB, + or InnoDB tables) + + ISAM, MyISAM and HEAP tables will commit straight away. Therefore a rollback + cannot occur! + +*****************************************************************************/ + +static int rollback_transaction(DB_DATABASE *db) +{ + bool ret = do_query(db, "Unable to rollback transaction: &1", NULL, "ROLLBACK", 0); + /* Autocommit needs to be set back on. */ + /* BM: and what happens if transactions are imbricated ? */ + //do_query(db, "Unable to set autocommit to On: &1", NULL, "set autocommit=1", 0); + return ret; +} + +/***************************************************************************** + + table_init() + + Initialize an info structure from table fields. + + is the database handle. + is the table name. + points at the info structure. + + This function must initialize the following info fields: + - info->nfield must contain the number of fields in the table. + - info->fields is a char*[] pointing at the name of each field. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int field_info(DB_DATABASE *db, const char *table, const char *field, DB_FIELD *info); + +static int table_init(DB_DATABASE *db, const char *table, DB_INFO *info) +{ + MYSQL_FIELD *field; + + MYSQL *conn = (MYSQL *)db->handle; + MYSQL_RES *res; + int i, n; + DB_FIELD *f; + + /* Nom de la table */ + + info->table = GB.NewZeroString(table); + + check_connection(conn); + + res = mysql_list_fields( conn, table, 0); + if (!res) + return TRUE; + + info->nfield = n = mysql_num_fields(res); + if (n == 0) + return TRUE; + + GB.Alloc((void **)POINTER(&info->field), sizeof(DB_FIELD) * n); + + i = 0; + + while ((field = mysql_fetch_field(res))) + { + f = &info->field[i]; + + if (field_info(db, table, field->name, f)) + { + mysql_free_result(res); + return TRUE; + } + + f->name = GB.NewZeroString(field->name); + + /*f->type = conv_type(field->type, field->length); + f->length = 0; + if (f->type == GB_T_STRING) + f->length = field->length;*/ + + i++; + } + + mysql_free_result(res); + + return FALSE; +} + + +/***************************************************************************** + + table_index() + + Initialize an info structure from table primary index. + + is the database handle. +
    is the table name. + points at the info structure. + + This function must initialize the following info fields: + - info->nindex must contain the number of fields in the primary index. + - info->index is a int[] giving the index of each index field in + info->fields. + + This function must be called after table_init(). + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_index(DB_DATABASE *db, const char *table, DB_INFO *info) +{ + char *qindex = "show index from `&1`"; + + MYSQL_RES *res; + MYSQL_ROW row; + int i, j, n; + + /* Index primaire */ + + if (do_query_cached(db, "Unable to get primary index: &1", &res, "si:&1", qindex, 1, table)) + return TRUE; + + for ( i = 0, n = 0; i < mysql_num_rows(res); i++ ) + { + row = mysql_fetch_row(res); + if (strcmp("PRIMARY", row[2]) == 0) /* Use only Primary key */ + n++; + } + + mysql_data_seek(res, 0);/* move back to first record */ + info->nindex = n; + /* Note: Col 3 is Key_name, Col 4 is Sq_in_index, Col 5 is Field Name */ + + if (n <= 0) + { + GB.Error("Table '&1' has no primary index", table); + return TRUE; + } + + GB.Alloc((void **)POINTER(&info->index), sizeof(int) * n); + + for (i = 0; i < n; i++) + { + row = mysql_fetch_row(res); + if (strcmp("PRIMARY", row[2]) == 0) /* Use only Primary key */ + { + for (j = 0; j < info->nfield; j++) + { + if (strcmp(info->field[j].name, row[4]) == 0) + { + info->index[i] = j; + break; + } + } + } + } + + return FALSE; +} + + +/***************************************************************************** + + table_release() + + Free the info structure filled by table_init() and/or table_index() + + is the database handle. + points at the info structure. + +*****************************************************************************/ + +static void table_release(DB_DATABASE *db, DB_INFO *info) +{ + /* All is done outside the driver */ +} + + +/***************************************************************************** + + table_exist() + + Returns if a table exists + + is the database handle. +
    is the table name. + + This function returns TRUE if the table exists, and FALSE if not. + +*****************************************************************************/ + +static int table_exist(DB_DATABASE *db, const char *table) +{ + MYSQL_RES *res; + + if (do_query_cached(db, "Unable to check table: &1", &res, "st", "show tables", 0)) + return FALSE; + + return !search_result(res, table, NULL); +} + + +/***************************************************************************** + + table_list() + + Returns an array containing the name of each table in the database + + is the database handle. + points to a variable that will receive the char* array. + + This function returns the number of tables, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int table_list(DB_DATABASE *db, char ***tables) +{ + MYSQL_RES *res; + MYSQL_ROW row; + int i; + int rows; + + if (do_query_cached(db, "Unable to get tables", &res, "st", "show tables", 0)) + return -1; + + rows = mysql_num_rows(res); + GB.NewArray(tables, sizeof(char *), rows); + + for (i = 0; i < rows; i++) + { + row = mysql_fetch_row(res); + (*tables)[i] = GB.NewZeroString(row[0]); + } + + return rows; +} + +/***************************************************************************** + + table_primary_key() + + Returns a string representing the primary key of a table. + + is the database handle. +
    is the table name. + points to a string that will receive the primary key. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_primary_key(DB_DATABASE *db, const char *table, char ***primary) +{ + const char *query = "show index from `&1`"; + + MYSQL_RES *res; + MYSQL_ROW row; + int i; + + if (do_query_cached(db, "Unable to get primary key: &1", &res, "si:&1", query, 1, table)) + return TRUE; + + GB.NewArray(primary, sizeof(char *), 0); + + for (i = 0; i < mysql_num_rows(res); i++) + { + row = mysql_fetch_row(res); + if (strcmp("PRIMARY", row[2]) == 0) + *(char **)GB.Add(primary) = GB.NewZeroString(row[4]); + } + + return FALSE; +} + +/***************************************************************************** + + table_is_system() + + Returns if a table is a system table. + + is the database handle. +
    is the table name. + + This function returns TRUE if the table is a system table, and FALSE if + not. + + Note: In mysql the system tables are stored in a separate database. + The tables are mysql.columns_priv, mysql.db, mysql.func, mysql.host, + mysql.tables_priv, mysql.user. This has therefore not been implemented. + +*****************************************************************************/ + +static int database_is_system(DB_DATABASE *db, const char *name); + +static int table_is_system(DB_DATABASE *db, const char *table) +{ + return db->flags.system; +} + + +/***************************************************************************** + + table_type() + + Returns the table type. + + is the database handle. +
    is the table name. + + This function returns a string containing table type or NULL if error. + +*****************************************************************************/ + +static char *table_type(DB_DATABASE *db, const char *table, const char *settype) +{ + static char buffer[16]; + + const char *query = "show table status like '&1'"; + + const char *update = + "alter table `&1` type = &2"; + + MYSQL_RES *res; + MYSQL_ROW row; + + if (settype) + { + clear_table_cache(db, table); + if (do_query(db, "Cannot set table type: &1", &res, update, 2, table, settype)) + return NULL; + } + + if (do_query_cached(db, "Invalid table: &1", &res, "sts:&1", query, 1, table)) + return NULL; + + if (search_result(res, table, &row)) + { + GB.Error("Unable to check table for: &1", table); + return NULL; + } + + if (!row[1]) return "VIEW"; // the table is a view + + strcpy(buffer, row[1]); + return buffer; +} + + +/***************************************************************************** + + table_delete() + + Deletes a table. + + is the database handle. +
    is the table name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_delete(DB_DATABASE *db, const char *table) +{ + clear_table_cache(db, table); + remove_cache_entry(db, "st"); + return do_query(db, "Unable to delete table: &1", NULL, "drop table `&1`", 1, table); +} + +/***************************************************************************** + + table_create() + + Creates a table. + + is the database handle. +
    is the table name. + points to a linked list of field descriptions. + is the primary key. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + + MySql has several different table types: InnoDB and BDB are transaction safe + whilst HEAP, ISAM, MERGE and MYISAM are not. + +TYPE = + +*****************************************************************************/ + +static int table_create(DB_DATABASE *db, const char *table, DB_FIELD *fields, char **primary, const char *tabletype) +{ + DB_FIELD *fp; + char *type = NULL; + int comma; + int i; + + if (db->version < 40100 && !strcasecmp(tabletype, "MEMORY")) + tabletype = "HEAP"; + + DB.Query.Init(); + + DB.Query.Add("CREATE TABLE `"); + DB.Query.Add(table); + DB.Query.Add("` ( "); + + comma = FALSE; + for (fp = fields; fp; fp = fp->next) + { + if (comma) + DB.Query.Add(", "); + else + comma = TRUE; + + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(fp->name); + DB.Query.Add(QUOTE_STRING); + + if (fp->type == DB_T_SERIAL) + DB.Query.Add(" BIGINT UNSIGNED NOT NULL AUTO_INCREMENT "); + else if (fp->type == DB_T_BLOB) + DB.Query.Add(" LONGBLOB "); + else + { + switch (fp->type) + { + case GB_T_BOOLEAN: type = "BOOL"; break; + case GB_T_INTEGER: type = "INT"; break; + case GB_T_LONG: type = "BIGINT"; break; + case GB_T_FLOAT: type = "DOUBLE"; break; + case GB_T_DATE: type = "DATETIME"; break; + case GB_T_STRING: + + if (fp->length <= 0 || fp->length > 255) //mysql supports upto 255 as varchar + type = "TEXT"; + else + { + sprintf(_buffer, "VARCHAR(%d)", fp->length); + type = _buffer; + } + + break; + + default: type = "MEDIUMTEXT"; break; + } + + DB.Query.Add(" "); + DB.Query.Add(type); + + if (fp->collation && *fp->collation) + { + char *p = strchr(fp->collation, '_'); + if (!p || p == fp->collation) + { + GB.Error("Incorrect collation"); + return TRUE; + } + + DB.Query.Add(" CHARACTER SET "); + DB.Query.AddLength(fp->collation, p - fp->collation); + DB.Query.Add(" COLLATE "); + DB.Query.Add(fp->collation); + } + + if (fp->def.type != GB_T_NULL) + { + DB.Query.Add(" NOT NULL DEFAULT"); + DB.FormatVariant(&_driver, &fp->def, DB.Query.AddLength); + } + else if (DB.StringArray.Find(primary, fp->name) >= 0) + { + DB.Query.Add(" NOT NULL"); + } + } + } + + if (primary) + { + DB.Query.Add(", PRIMARY KEY ("); + + for (i = 0; i < GB.Count(primary); i++) + { + if (i > 0) + DB.Query.Add(","); + + DB.Query.Add("`"); + DB.Query.Add(primary[i]); + DB.Query.Add("`"); + + for (fp = fields; fp; fp = fp->next) //Check type of primary field + { + if(strcmp(fp->name, primary[i]) == 0){ + if (fp->length <= 0 || fp->length > 255){ + if (fp->type == GB_T_STRING) + DB.Query.Add("(255)"); + } + } + } + } + + DB.Query.Add(")"); + } + + DB.Query.Add(" )"); + + if (tabletype) + { + if (db->version < 40018) + DB.Query.Add(" TYPE = "); + else + DB.Query.Add(" ENGINE = "); + + DB.Query.Add(tabletype); + } + + remove_cache_entry(db, "st"); + /* printf("table_create syntax: %s\n", DB.Query.Get());*/ + return do_query(db, "Cannot create table: &1", NULL, DB.Query.Get(), 0); +} + +/***************************************************************************** + + field_exist() + + Returns if a field exists in a given table + + is the database handle. +
    is the table name. + is the field name. + + This function returns TRUE if the field exists, and FALSE if not. + +*****************************************************************************/ + +static int field_exist(DB_DATABASE *db, const char *table, const char *field) +{ + const char *query = "show full columns from `&1`"; + + MYSQL_RES *res; + + if (do_query_cached(db, "Unable to check field: &1", &res, "sfc:&1", query, 1, table)) + return FALSE; + + return !search_result(res, field, NULL); +} + + + +/***************************************************************************** + + field_list() + + Returns an array containing the name of each field in a given table + + is the database handle. +
    is the table name. + points to a variable that will receive the char* array. + + This function returns the number of fields, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int field_list(DB_DATABASE *db, const char *table, char ***fields) +{ + const char *query = "show full columns from `&1`"; + + long i, n; + MYSQL_RES *res; + MYSQL_ROW row; + + if (do_query_cached(db, "Unable to get field: &1", &res, "sfc:&1", query, 1, table)) + return -1; + + n = mysql_num_rows(res); + + if (fields) /* (BM) see the function commentary */ + { + GB.NewArray(fields, sizeof(char *), n); + + for (i = 0; i < n; i++){ + row = mysql_fetch_row(res); + (*fields)[i] = GB.NewZeroString(row[0]); + } + } + + return n; +} + + +/***************************************************************************** + + field_info() + + Get field description + + is the database handle. +
    is the table name. + is the field name. + points to a structure filled by the function. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int field_info(DB_DATABASE *db, const char *table, const char *field, DB_FIELD *info) +{ + const char *query = "show full columns from `&1`"; + + MYSQL_RES *res; + MYSQL_ROW row; + MYSQL_FIELD f; + GB_VARIANT def; + char *val; + + if (do_query_cached(db, "Unable to get field info: &1", &res, "sfc:&1", query, 1, table)) + return TRUE; + + if (search_result(res, field, &row)) + { + GB.Error("Unable to find field &2 in table &1", table, field); + return TRUE; + } + + info->name = NULL; + conv_string_type(row[1], &f); + info->type = conv_type(&f); + + if (info->type == GB_T_STRING) + info->length = f.max_length; + else + info->length = 0; + + info->def.type = GB_T_NULL; + + if ((info->type == GB_T_INTEGER || info->type == GB_T_LONG) && strstr(row[6], "auto_increment")) + info->type = DB_T_SERIAL; + else + { + if (!*row[3] || row[3][0] != 'Y') + { + def.type = GB_T_VARIANT; + def.value.type = GB_T_NULL; + + val = row[5]; + + /* (BM) seems there is a bug in mysql */ + if (info->type == GB_T_DATE && val && strlen(val) >= 5 && strncmp(val, "00000", 5) == 0) + val = NULL; + + if (val && *val) + { + conv_data(db->version, val, 0, &def.value, &f); + GB.StoreVariant(&def, &info->def); + } + } + } + + if (row[2] && *row[2]) + info->collation = GB.NewZeroString(row[2]); + else + info->collation = NULL; + + return FALSE; +} + +/***************************************************************************** + + index_exist() + + Returns if an index exists in a given table + + is the database handle. +
    is the table name. + is the index name. + + This function returns TRUE if the index exists, and FALSE if not. + +*****************************************************************************/ + +static int index_exist(DB_DATABASE *db, const char *table, const char *index) +{ + const char *query = "show index from `&1`"; + + MYSQL_RES *res; + MYSQL_ROW row; + int i, n; + + if (do_query_cached(db, "Unable to check index: &1", &res, "si:&1", query, 1, table)) + return FALSE; + + for ( i = 0, n = 0; i < mysql_num_rows(res); i++ ) + { + row = mysql_fetch_row(res); + if (strcmp(index, row[2]) == 0) + n++; + } + + return (n > 0); +} + +/***************************************************************************** + + index_list() + + Returns an array containing the name of each index in a given table + + is the database handle. +
    is the table name. + points to a variable that will receive the char* array. + + This function returns the number of indexes, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int index_list(DB_DATABASE *db, const char *table, char ***indexes) +{ + const char *query = "show index from `&1`"; + + MYSQL_RES *res; + MYSQL_ROW row; + long i, n, no_indexes; + + if (do_query_cached(db, "Unable to get indexes: &1", &res, "si:&1", query, 1, table)) + return -1; + + for (i = 0, no_indexes = 0; i < mysql_num_rows(res); i++ ) + { + /* Count the number of 1st sequences in Seq_in_index to + give nmber of indexes. row[3] */ + row = mysql_fetch_row(res); + if (atoi(row[3]) == 1) + no_indexes++; + } + + + GB.NewArray(indexes, sizeof(char *), no_indexes); + mysql_data_seek(res, 0); /* move back to first record */ + + for (i = 0, n = 0; i < mysql_num_rows(res); i++ ) + { + row = mysql_fetch_row(res); + if (atoi(row[3]) == 1 /* Start of a new index */) + (*indexes)[n++] = GB.NewZeroString(row[2]); /* (BM) The name is row[2], not row[4] */ + } + + return no_indexes; +} + +/***************************************************************************** + + index_info() + + Get index description + + is the database handle. +
    is the table name. + is the index name. + points to a structure filled by the function. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int index_info(DB_DATABASE *db, const char *table, const char *index, DB_INDEX *info) +{ + const char *query = "show index from `&1`"; + + MYSQL_RES *res; + MYSQL_ROW row = 0; + int i, n; + + if (do_query_cached(db, "Unable to get index info: &1", &res, "si:&1", query, 1, table)) + return TRUE; + + n = mysql_num_rows(res); + for (i = 0; i < n; i++ ) + { + row = mysql_fetch_row(res); + if ( strcmp( index, row[2]) == 0) + { /* (BM) With braces, it should work better :-) */ + n = 1; + break; + } + } + + if (n != 1) + { + GB.Error("Unable to find index &2 in table &1", table, index); + return TRUE; + } + + info->name = NULL; + info->unique = strcmp(row[1], "0") == 0; + info->primary = strcmp("PRIMARY", row[2]) == 0 ? TRUE : FALSE; + + DB.Query.Init(); + + i = 0; + /* (BM) row can be null if we are seeking the last index */ + while ( row && strcmp(index, row[2]) == 0 ) + { + if (i > 0) + DB.Query.Add(","); + + DB.Query.Add(row[4]); + row = mysql_fetch_row(res); + i++; /* (BM) i must be incremented */ + } + + info->fields = DB.Query.GetNew(); + + return FALSE; +} + +/***************************************************************************** + + index_delete() + + Deletes an index. + + is the database handle. +
    is the table name. + is the index name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int index_delete(DB_DATABASE *db, const char *table, const char *index) +{ + clear_table_cache(db, table); + return do_query(db, "Unable to delete index: &1", NULL, "drop index `&1` on `&2`", 2, index, table); +} + +/***************************************************************************** + + index_create() + + Creates an index. + + is the database handle. +
    is the table name. + is the index name. + points to a structure describing the index. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int index_create(DB_DATABASE *db, const char *table, const char *index, DB_INDEX *info) +{ + DB.Query.Init(); + + DB.Query.Add("CREATE "); + if (info->unique) + DB.Query.Add("UNIQUE "); + DB.Query.Add("INDEX `"); + DB.Query.Add(index); + DB.Query.Add("` ON "); + DB.Query.Add(table); + DB.Query.Add(" ( "); + DB.Query.Add(info->fields); + DB.Query.Add(" )"); + + clear_table_cache(db, table); + return do_query(db, "Cannot create index: &1", NULL, DB.Query.Get(), 0); +} + +/***************************************************************************** +* +* database_exist() +* +* Returns if a database exists +* +* is any database handle. +* is the database name. +* +* This function returns TRUE if the database exists, and FALSE if not. +* +******************************************************************************/ + +static int database_exist(DB_DATABASE *db, const char *name) +{ + MYSQL *conn = (MYSQL *)db->handle; + MYSQL_RES *res; + int exist; + + check_connection(conn); + res = mysql_list_dbs(conn, name); + if (!res) + { + db->error = mysql_errno(conn); + GB.Error("Unable to check database: &1", mysql_error(conn)); + return FALSE; + } + + exist = mysql_num_rows(res); + mysql_free_result(res); + return exist; +} + +/***************************************************************************** +* +* database_list() +* +* Returns an array containing the name of each database +* +* is any database handle. +* points to a variable that will receive the char* array. +* +* This function returns the number of databases, or -1 if the command has +* failed. +* +* Be careful: can be NULL, so that just the count is returned. +* +******************************************************************************/ + +static int database_list(DB_DATABASE *db, char ***databases) +{ + long i, rows; + MYSQL *conn = (MYSQL *)db->handle; + MYSQL_RES *res; + MYSQL_ROW row; + + check_connection(conn); + res = mysql_list_dbs(conn, 0); + if (!res){ + db->error = mysql_errno(conn); + GB.Error("Unable to get databases: &1", mysql_error(conn)); + return -1; + } + + rows = mysql_num_rows(res); + /*printf("Got %d databases\n", rows); */ + GB.NewArray(databases, sizeof(char *), rows); + + for (i = 0; i < rows; i++){ + row = mysql_fetch_row(res); + (*databases)[i] = GB.NewZeroString(row[0]); + /*printf("%s\n", (*databases)[i]); */ + } + + mysql_free_result(res); + + return rows; +} + +/***************************************************************************** +* +* database_is_system() +* +* Returns if a database is a system database. +* +* is any database handle. +* is the database name. +* +* This function returns TRUE if the database is a system database, and +* FALSE if not. +* +******************************************************************************/ + +static int database_is_system(DB_DATABASE *db, const char *name) +{ + return (strcmp("mysql", name) == 0 || strcmp("information_schema", name) == 0); +} + +/***************************************************************************** +* +* database_delete() +* +* Deletes a database. +* +* is the database handle. +* is the database name. +* +* This function returns TRUE if the command has failed, and FALSE if +* everything was OK. +* +******************************************************************************/ + +static int database_delete(DB_DATABASE *db, const char *name) +{ + if (database_is_system(db, name)) + { + GB.Error("Unable to delete database: &1", "system database"); + return TRUE; + } + + return do_query(db, "Unable to delete database: &1", NULL, "drop database `&1`", 1, name); +} + +/***************************************************************************** +* +* database_create() +* +* Creates a database. +* +* is the database handle. +* is the database name. +* +* This function returns TRUE if the command has failed, and FALSE if +* everything was OK. +* +******************************************************************************/ + +static int database_create(DB_DATABASE *db, const char *name) +{ + return do_query(db, "Unable to create database: &1", NULL, "create database `&1`", 1, name); +} + + +/***************************************************************************** +* +* user_exist() +* +* Returns if a user exists. +* +* is any database handle. +* is the user name. +* +* This function returns TRUE if the user exists, and FALSE if not. +* +******************************************************************************/ + +static int user_exist(DB_DATABASE *db, const char *name) +{ + const char *query = "select user from mysql.user " + "where user = '&1' and host = '&2' "; + MYSQL_RES *res; + int exist; + char *_name, *_host, *_token; + + if (!strrchr(name,'@')){ + //To be done: maybe we should check hostname we are running + //from and use this instead of localhost + /* (BM) you forgot the last 0 character */ + _name = malloc(strlen(name) + strlen("@localhost") + 1); + sprintf(_name,"%s@localhost", name); + } + else { + _name = malloc(strlen(name) + 1); + strcpy(_name,name); + } + + _host = strrchr(_name,'@') + 1; + _token = _host - 1; + _token[0] = 0; + + + if (do_query(db, "Unable to check user: &1@&2", &res, query, 2, _name, _host)) + { + free(_name); + return FALSE; + } + + exist = mysql_num_rows(res) == 1; + + free(_name); + mysql_free_result(res); + + return exist; +} + +/***************************************************************************** +* +* user_list() +* +* Returns an array containing the name of each user. +* +* is the database handle. +* points to a variable that will receive the char* array. +* +* This function returns the number of users, or -1 if the command has +* failed. +* +* Be careful: can be NULL, so that just the count is returned. +* +******************************************************************************/ + +static int user_list(DB_DATABASE *db, char ***users) +{ + const char *query = "select user, host from mysql.user"; + + MYSQL_RES *res; + MYSQL_ROW row; + MYSQL_FIELD *field; + long i, count, length; + char *_username; + + if (do_query(db, "Unable to get users: &1", &res, query, 0)) + return -1; + + count = mysql_num_rows(res); + + if (users) + { + GB.NewArray(users, sizeof(char *), count); + field = mysql_fetch_field(res); //user field + length = field->max_length; + field = mysql_fetch_field(res); //host field + length += field->max_length; + _username = malloc(length + 2); /* (BM) +2 because there is the last 0 character ! */ + + for ( i = 0; i < count; i++ ) + { + row = mysql_fetch_row(res); + sprintf(_username,"%s@%s", row[0], row[1]); + (*users)[i] = GB.NewZeroString(_username); + } + free(_username); + } + + mysql_free_result(res); + + return count; +} + +/***************************************************************************** +* +* user_info() +* +* Get user description +* +* is the database handle. +* is the user name. +* points to a structure filled by the function. +* +* This function returns TRUE if the command has failed, and FALSE if +* everything was OK. +* +* Mysql notes: Privileges set to Y in the mysql.user table are global settings +* and apply to all databases. eg. These are super user privileges. +* mysql.tables_priv lists the access granted to a user on a +* particular table +* mysql.columns_priv grant column specific privileges. +* mysql.db and mysql.host grant database specific privileges. +* +* User may also not be unique as mysql.user also contains +* users from different hosts. e.g host and user columns will +* make it unique! Using 'localhost' here to limit. +* +* The privileges are: grant_priv - allows user to grant +* privileges to others - which +* includes the ability to create +* users; +* create_priv/drop_priv - create database, +* tables etc. +******************************************************************************/ + +static int user_info(DB_DATABASE *db, const char *name, DB_USER *info ) +{ + const char *query = + "select create_priv, drop_priv, grant_priv, password from mysql.user " + "where user = '&1' and host = '&2'"; + + MYSQL_RES *res; + MYSQL_ROW row; + char *_name, *_host, *_token; + + if (!strrchr(name,'@')){ + //To be done: check hostname we are running + //from use instead of localhost + /* (BM) You forgot the last 0 character */ + _name = malloc(strlen(name) + strlen("@localhost") + 1); + sprintf(_name,"%s@localhost", name); + } + else { + _name = malloc(strlen(name) + 1); + strcpy(_name,name); + } + + _host = strrchr(_name,'@') + 1; + _token = _host - 1; + _token[0] = 0; + + if (do_query(db, "Unable to check user info: &1@&2", &res, query, 2, _name, _host)) + { + free(_name); + return TRUE; + } + + if (mysql_num_rows(res) != 1) + { + GB.Error("user_info: Non unique user found"); + free(_name); + mysql_free_result(res); + return TRUE; + } + + row = mysql_fetch_row(res); + + info->name = NULL; + if ( strcmp(row[0], "Y") == 0 || strcmp(row[1], "Y") == 0) + info->admin = 1; + else + info->admin = 0; + + if (row[3]) + info->password = GB.NewZeroString(row[3]); //password is encrypted in mysql + + mysql_free_result(res); + free(_name); + + return FALSE; +} + +/***************************************************************************** + + user_delete() + + Deletes a user. + + is any database handle. + is the user name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int user_delete(DB_DATABASE *db, const char *name) +{ + const char *_delete = + "delete from mysql.user where user = '&1' and host = '&2'"; +// "delete from mysql.user, mysql.db, mysql.columns_priv, mysql.tables_priv " +// "where user = '&1' and host = '&2'"; + char *_name, *_host, *_token; + int _ret; + + if (!strrchr(name,'@')){ + //To be done: maybe hostname we are running + //from should be used rather than localhost + _name = malloc(strlen(name) + strlen("@localhost")) + 1; + sprintf(_name,"%s@localhost", name); + } + else { + _name = malloc(strlen(name) + 1); + strcpy(_name,name); + } + + _host = strrchr(_name,'@') + 1; + _token = _host - 1; + _token[0] = 0; + +//Still need to look at the removal of privileges +// _ret = do_query(db, "Unable to delete user: &1", NULL, +// "revoke all on *.* from &1@&2", 2, _name, _host); + _ret = do_query(db, "Unable to delete user: &1", NULL, _delete, 2, _name, _host); + free(_name); + return _ret; +} + +/***************************************************************************** +* +* user_create() +* +* Creates a user. +* +* is the database handle. +* is the user name. +* points to a structure describing the user. +* +* This function returns TRUE if the command has failed, and FALSE if +* everything was OK. +* +******************************************************************************/ + +static int user_create(DB_DATABASE *db, const char *name, DB_USER *info) +{ + char *_name; + + DB.Query.Init(); + + if (!strrchr(name,'@')){ + _name = malloc(strlen(name) + strlen("@localhost") + 1); + sprintf(_name,"%s@localhost", name); + } + else { + _name = malloc(strlen(name) + 1); + strcpy(_name,name); + } + + if (info->admin) { + DB.Query.Add("GRANT ALL PRIVILEGES ON *.* TO "); + DB.Query.Add(_name); + } + else { + DB.Query.Add("GRANT USAGE ON * TO "); + DB.Query.Add(_name); + } + + if (info->password) + { + DB.Query.Add(" IDENTIFIED BY '"); + DB.Query.Add(info->password); + DB.Query.Add("'"); + } + + if (info->admin) + DB.Query.Add(" WITH GRANT OPTION"); + + free(_name); + + return do_query(db, "Cannot create user: &1", NULL, DB.Query.Get(), 0); +} + +/***************************************************************************** +* +* user_set_password() +* +* Change the user password. +* +* is the database handle. +* is the user name. +* is the new password +* +* This function returns TRUE if the command has failed, and FALSE if +* everything was OK. +* +******************************************************************************/ + +static int user_set_password(DB_DATABASE *db, const char *name, const char *password) +{ + char *_name; + DB.Query.Init(); + + if (!strrchr(name,'@')){ + _name = malloc(strlen(name) + strlen("@localhost") + 1); + sprintf(_name,"%s@localhost", name); + } + else { + _name = malloc(strlen(name) + 1); + strcpy(_name,name); + } + + DB.Query.Add("SET PASSWORD FOR "); + DB.Query.Add(_name); + DB.Query.Add(" = PASSWORD ('"); + DB.Query.Add(password); + DB.Query.Add("')"); + + free(_name); + + return do_query(db, "Cannot change user password: &1", NULL, DB.Query.Get(), 0); +} + + +/***************************************************************************** + + The driver interface + +*****************************************************************************/ + +DECLARE_DRIVER(_driver, "mysql"); + +/***************************************************************************** + + The component entry and exit functions. + +*****************************************************************************/ + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.db", DB_INTERFACE_VERSION, &DB); + DB.Register(&_driver); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.db.mysql/src/main.h b/gb.db.mysql/src/main.h new file mode 100644 index 00000000..1deb59e8 --- /dev/null +++ b/gb.db.mysql/src/main.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "../gb.db.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern DB_INTERFACE DB; +#endif + +#define QUOTE_STRING "`" + +#endif /* __MAIN_H */ diff --git a/gb.db.odbc/AUTHORS b/gb.db.odbc/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.db.odbc/COPYING b/gb.db.odbc/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.db.odbc/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.db.odbc/ChangeLog b/gb.db.odbc/ChangeLog new file mode 100644 index 00000000..f8b868a1 --- /dev/null +++ b/gb.db.odbc/ChangeLog @@ -0,0 +1,37 @@ +gambas ODBC component + + +Tue, 29 Aug 2006 Andrea Bortolan +fixed some bugs using PostgreSQL and Easysoft Oracle ODBC Driver + + +Thu, 17 Nov 2005 Andrea Bortolan +added the BIGINT (64 bit integer) datatype support + + +Tue, 08 Nov 2005 Andrea Bortolan + +moved the SQLFetchScroll test function in the connection section in order to be executed only at the connection time + + +Thu, 03 Nov 2005 Andrea Bortolan + +add a check in order to call the ODBC API Fetch instead of FetchScroll when the driver doesn't support it + + +Fri, 15 Apr 2005 Andrea Bortolan + +no more "printf" in the code + +all the function not implemented by the odbc standard return without rise GB.Error(s) + + +Thu, 14 Apr 2005 Andrea Bortolan + +implementation of many new functions - primary keys - index ecc. + + +Fri, 18 Mar 2005 Andrea Bortolan + +fixed the segfault problem - changed the do_query function + diff --git a/gb.db.odbc/INSTALL b/gb.db.odbc/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.db.odbc/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.db.odbc/Makefile.am b/gb.db.odbc/Makefile.am new file mode 100644 index 00000000..966c550d --- /dev/null +++ b/gb.db.odbc/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @ODBC_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.db.odbc/NEWS b/gb.db.odbc/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.db.odbc/README b/gb.db.odbc/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.db.odbc/acinclude.m4 b/gb.db.odbc/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.db.odbc/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.db.odbc/component.am b/gb.db.odbc/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.db.odbc/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.db.odbc/configure.ac b/gb.db.odbc/configure.ac new file mode 100644 index 00000000..106a9e95 --- /dev/null +++ b/gb.db.odbc/configure.ac @@ -0,0 +1,27 @@ +dnl ---- configure.ac for gb.db.odbc driver + +dnl ---- Initialization + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-db-odbc, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.db.odbc) +AC_PROG_LIBTOOL + +dnl ---- ODBC driver + +GB_COMPONENT( + odbc, ODBC, gb.db.odbc, [src], + [GB_FIND(sql.h sqlext.h sqltypes.h, $prefix /usr/local/lib /usr/local /opt /usr/lib /usr, include odbc*/include)], + [GB_FIND(libodbc.$SHLIBEXT, $prefix /usr/local /opt /usr /opt/local, lib odbc*/lib )], + [$C_LIB -lodbc]) + +dnl ---- Create makefiles + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.db.odbc/gambas.h b/gb.db.odbc/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.db.odbc/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.db.odbc/gb.db.h b/gb.db.odbc/gb.db.h new file mode 120000 index 00000000..563bd00c --- /dev/null +++ b/gb.db.odbc/gb.db.h @@ -0,0 +1 @@ +../main/lib/db/gb.db.h \ No newline at end of file diff --git a/gb.db.odbc/gb.db.proto.h b/gb.db.odbc/gb.db.proto.h new file mode 120000 index 00000000..0a8e6920 --- /dev/null +++ b/gb.db.odbc/gb.db.proto.h @@ -0,0 +1 @@ +../main/lib/db/gb.db.proto.h \ No newline at end of file diff --git a/gb.db.odbc/gb_common.h b/gb.db.odbc/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.db.odbc/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.db.odbc/m4 b/gb.db.odbc/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.db.odbc/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.db.odbc/reconf b/gb.db.odbc/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.db.odbc/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.db.odbc/src/Makefile.am b/gb.db.odbc/src/Makefile.am new file mode 100644 index 00000000..63d35afc --- /dev/null +++ b/gb.db.odbc/src/Makefile.am @@ -0,0 +1,11 @@ +COMPONENT = gb.db.odbc +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.db.odbc.la + +gb_db_odbc_la_LIBADD = @ODBC_LIB@ +gb_db_odbc_la_LDFLAGS = -module @LD_FLAGS@ @ODBC_LDFLAGS@ +gb_db_odbc_la_CPPFLAGS = @ODBC_INC@ + +gb_db_odbc_la_SOURCES = \ + main.h main.c diff --git a/gb.db.odbc/src/gb.db.odbc.component b/gb.db.odbc/src/gb.db.odbc.component new file mode 100644 index 00000000..ddc97981 --- /dev/null +++ b/gb.db.odbc/src/gb.db.odbc.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.db.odbc +Author=Andrea Bortolan,Benoît Minisini +State=0 +Require=gb.db + diff --git a/gb.db.odbc/src/main.c b/gb.db.odbc/src/main.c new file mode 100644 index 00000000..baa7f193 --- /dev/null +++ b/gb.db.odbc/src/main.c @@ -0,0 +1,3380 @@ +/*************************************************************************** + + main.c + + (c) 2004-2007 Andrea Bortolan + (c) 2000-2017 Benoît Minisini + (c) 2015-2017 zxMarce + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +//#define ODBC_TYPE +//#define ODBC_DEBUG +//#define DEBUG_ODBC + +//#ifndef ODBC_DEBUG_HEADER +//#define ODBC_DEBUG_HEADER +//#endif + +//#define ODBC_DEBUG_MEM + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#undef HAVE_SYS_TYPES_H +#endif + +#ifdef HAVE_UNISTD_H +#undef HAVE_UNISTD_H +#endif + +#include "gb.db.proto.h" +#include "main.h" + +// define unixODBC ONLY constants if they are not available +#ifndef SQLTables_TABLE_NAME +#define SQLTables_TABLE_NAME 3 +#define SQLTables_TABLE_TYPE 4 +#define SQLTables_REMARKS 5 +#define SQLColumns_COLUMN_NAME 4 +#define SQLColumns_COLUMN_SIZE 7 +#define SQLColumns_SQL_DATA_TYPE 14 +#endif + +GB_INTERFACE GB EXPORT; +DB_INTERFACE DB EXPORT; + +static char _buffer[32]; +//static char _nullbuffer[10]; +static DB_DRIVER _driver; + +typedef struct + { + // SQLHENV odbcEnvHandle; + // SQLHDBC odbcHandle; + SQLHANDLE odbcEnvHandle; //ODBC environment handle + SQLHANDLE odbcHandle; //ODBC connection handle + SQLUSMALLINT FetchScroll_exist; //Flag + char *dsn_name; //DSN name + char *user_name; //Logged-in user name + } +ODBC_CONN; + + +typedef struct + { + SQLCHAR fieldname[32]; + int fieldid; + SQLSMALLINT type; + SQLINTEGER outlen; + SQLCHAR *fieldata; + struct ODBC_FIELDS *next; + //struct ODBC_FIELDS *prev; + } +ODBC_FIELDS; + + +typedef struct + { + SQLHSTMT odbcStatHandle; + SQLUSMALLINT Function_exist; //Does the Driver supports the SQLFetchScroll ? + SQLUSMALLINT Cursor_Scrollable; //Is it possible to set a Scrollable cursor ? + ODBC_FIELDS *fields; + SQLLEN count; + } +ODBC_RESULT; + + +typedef struct + { + char *tablename; + struct ODBC_TABLES *next; + } +ODBC_TABLES; + +/* +* zxMarce: Routine to print to console the errors from the ODBC subsystem. +* Adapted to obey Gambas' DB.IsDebug(). +* Mostly from http://www.easysoft.com/developer/interfaces/odbc/diagnostics_error_status_codes.html +*/ +void reportODBCError(const char *fn, + SQLHANDLE handle, + SQLSMALLINT type + ) +{ + + SQLINTEGER i = 0; + SQLINTEGER native; + SQLTCHAR state[7]; + SQLTCHAR text[256]; + SQLSMALLINT len; + SQLRETURN ret; + + if(DB.IsDebug()) + { + fprintf(stderr, "gb.db.odbc: %s\n", fn); + do + { + ret = SQLGetDiagRec(type, handle, ++i, state, &native, text, sizeof(text), &len); + + if (SQL_SUCCEEDED(ret)) + fprintf(stderr, "gb.db.odbc: %d:%s:%d:%s\n", (int)i, (char *)state, (int)native, (char *)text); + } + while (ret == SQL_SUCCESS); + } +} + + +/* +* 20170623 zxMarce: Routine to publish underlying ODBC error(s) as a normal +* Gambas error. Based on reportODBCError above, but does NOT obey DB.IsDebug(), +* as this is intended as a component-centric "official" error report routine. +* It is the intention to replace most GB.Error() calls with this routine. +* Thanks to Tobias Boege for native GB string handling tips! +*/ +void throwODBCError(const char *failedODBCFunctionName, + SQLHANDLE handle, + SQLSMALLINT handleType + ) +{ + SQLINTEGER i = 0; + SQLINTEGER native; + SQLTCHAR state[7]; + SQLTCHAR text[512]; + char *errorText = NULL; //GB.NewString("gb.db.odbc: ", 12); + SQLSMALLINT len; + SQLRETURN ret; + + errorText = GB.AddString(errorText, (char *)failedODBCFunctionName, 0), + errorText = GB.AddString(errorText, " failed:", 0); + + do + { + ret = SQLGetDiagRec(handleType, handle, ++i, state, &native, text, sizeof(text), &len); + if (SQL_SUCCEEDED(ret)) + { + errorText = GB.AddString(errorText, "\n", 1); + errorText = GB.AddString(errorText, (char *)state, 0); + errorText = GB.AddString(errorText, (char *)text, len); + } + } + while (ret == SQL_SUCCESS); + + GB.Error(errorText); + GB.FreeString(&errorText); +} + + +/* zxMarce: This is one way -hope there's an easier one- to retrieve a rowset +* count for SELECT statements. Four steps (must have an scrollable cursor!): +* 1- Remember the current row. +* 2- Seek down to the last row in the rowset +* 3- Get the last row's index (recno) +* 4- Seek back to wherever we were at in step 1 +* 20161110 zxMarce: Ok, it did not work that OK for Firebird; it looks like +* the FB driver returns one-less than the record count (record count seems to +* be zero-based), so we will instead do as follows, if we have a scrollable +* recordset: +* 1- Remember the current row. +* 2- Seek up to the first row in the rowset +* 3- Get the first row's index (firstRecNo) +* 4- Seek down to the last row in the rowset +* 5- Get the last row's index (lastRecNo) +* 6- Seek back to wherever we were at in step 1 +* 7- Return (lastRecNo - firstRecNo + 1). +*/ +int GetRecordCount(SQLHANDLE stmtHandle, SQLINTEGER cursorScrollable) +{ + SQLRETURN retcode; //ODBC call return values + int formerRecIdx = 0; //Where we were when this all started. + SQLINTEGER myRecCnt = -1; //Default for when there's no cursor. + SQLINTEGER firstRecNo = 0; //20161111 holder for 1st recno. + SQLINTEGER lastRecNo = 0; //20161111 holder for last recno. + char mssg[128]; //Error reporting text. + + //Make sure the statement has a cursor + if (!(stmtHandle && (cursorScrollable == SQL_TRUE))) + { + if (DB.IsDebug()) + { + fprintf(stderr, "gb.db.odbc: Cannot do GetRecordCount()!\n"); + } + return ((int) myRecCnt); + } + + //Tell ODBC we won't be actually reading data (speeds process up). + //SQL_ATTR_RETRIEVE_DATA = [SQL_RD_ON] | SQL_RD_OFF + retcode = SQLSetStmtAttr(stmtHandle, SQL_ATTR_RETRIEVE_DATA, (SQLPOINTER) SQL_RD_OFF, 0); + if (!SQL_SUCCEEDED(retcode)) + { + reportODBCError("SQLSetStmtAttr SQL_ATTR_RETRIEVE_DATA", + stmtHandle, + SQL_HANDLE_STMT); + } + + //Fetch current row's index so we can return to it when done. + retcode = SQLGetStmtAttr(stmtHandle, SQL_ATTR_ROW_NUMBER, &formerRecIdx, 0, 0); + if (!SQL_SUCCEEDED(retcode)) + { + reportODBCError("SQLGetStmtAttr SQL_ATTR_ROW_NUMBER", + stmtHandle, + SQL_HANDLE_STMT); + } + + //Make sure the statement has a cursor + if (formerRecIdx < 0) + { + if (DB.IsDebug()) + { + fprintf(stderr, "gb.db.odbc.GetRecordCount: Current record returned %d, returning -1 as count.\n", formerRecIdx); + } + return ((int) myRecCnt); + } + + //Try to get (back?) to the first record, abort if not possible. + retcode = SQLFetchScroll(stmtHandle, SQL_FETCH_FIRST, (SQLINTEGER) 0); + if (!SQL_SUCCEEDED(retcode)) + { + reportODBCError("SQLFetchScroll SQL_FETCH_FIRST", stmtHandle, SQL_HANDLE_STMT); + retcode = SQLSetStmtAttr(stmtHandle, SQL_ATTR_RETRIEVE_DATA, (SQLPOINTER) SQL_RD_ON, 0); + return ((int) myRecCnt); + } else { + //Fetch the first record's index + retcode = SQLGetStmtAttr(stmtHandle, SQL_ATTR_ROW_NUMBER, &firstRecNo, 0, 0); + if (SQL_SUCCEEDED(retcode)) + { + //Inform first recno if in Debug mode and carry on + if (DB.IsDebug()) + { + fprintf(stderr, "gb.db.odbc.GetRecordCount: First recno=%d\n", (int) firstRecNo); + } + } else { + //Could not fetch the first recno: Abort! + reportODBCError("SQLFetchScroll SQL_ATTR_ROW_NUMBER (first recno)", stmtHandle, SQL_HANDLE_STMT); + retcode = SQLSetStmtAttr(stmtHandle, SQL_ATTR_RETRIEVE_DATA, (SQLPOINTER) SQL_RD_ON, 0); + return ((int) myRecCnt); + } + } + + //Advance the cursor to the last record. + retcode = SQLFetchScroll(stmtHandle, SQL_FETCH_LAST, (SQLINTEGER) 0); + if (SQL_SUCCEEDED(retcode)) + { + + //Fetch the last record's index + retcode = SQLGetStmtAttr(stmtHandle, SQL_ATTR_ROW_NUMBER, &lastRecNo, 0, 0); + if (SQL_SUCCEEDED(retcode)) + { + //Set ret value + if (DB.IsDebug()) + { + fprintf(stderr, "gb.db.odbc.GetRecordCount: Last recno=%d\n", (int) lastRecNo); + } + + } else { + reportODBCError("SQLGetStmtAttr SQL_ATTR_ROW_NUMBER (last recno)", stmtHandle, SQL_HANDLE_STMT); + } + + //Return cursor to original row. + retcode = SQLFetchScroll(stmtHandle, SQL_FETCH_ABSOLUTE, (SQLINTEGER) formerRecIdx); + //Since we have set the "do not read data" statement attribute, this call (may) return + //code 100 (SQL_NO_DATA) but that's OK for our purposes of just counting rows. + if (!SQL_SUCCEEDED(retcode) && (retcode != SQL_NO_DATA)) + { + snprintf(mssg, sizeof(mssg), "SQLFetchScroll SQL_FETCH_ABSOLUTE (code %d) (rec %d)", + (int)retcode, formerRecIdx); + reportODBCError(mssg, stmtHandle, SQL_HANDLE_STMT); + } + + } else { + reportODBCError("SQLFetchScroll SQL_FETCH_LAST", stmtHandle, SQL_HANDLE_STMT); + } + + //Tell ODBC we will be reading data now. + //SQL_ATTR_RETRIEVE_DATA = [SQL_RD_ON] | SQL_RD_OFF + retcode = SQLSetStmtAttr(stmtHandle, SQL_ATTR_RETRIEVE_DATA, (SQLPOINTER) SQL_RD_ON, 0); + if (!SQL_SUCCEEDED(retcode)) + { + reportODBCError("SQLSetStmtAttr SQL_ATTR_RETRIEVE_DATA", stmtHandle, SQL_HANDLE_STMT); + } + + myRecCnt = (lastRecNo - firstRecNo + 1); + if (DB.IsDebug()) + { + fprintf(stderr, "gb.db.odbc.GetRecordCount: Record count=%d\n", (int) myRecCnt); + } + + return ((int) myRecCnt); + +} + + +/* BM: Replaces malloc() and free() by GB.Alloc() and GB.Free() */ +static void *my_malloc(size_t size) +{ + void *ptr; + GB.Alloc(&ptr, (int)size); + return ptr; +} + +static void my_free(void *_ptr) +{ + GB.Free(POINTER(&_ptr)); +} + +#define malloc(_size) my_malloc(_size) +#define free(_ptr) my_free(_ptr) + + +#if 0 +static void quote(char *data, int len, DB_FORMAT_CALLBACK add) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tquote\n"); +fflush(stderr); +#endif + int i; + unsigned char c; + //char buffer[8]; + + (*add)("'", 1); + for (i = 0; i < len; i++) + { + c = (unsigned char)data[i]; + if (c == '\\') + (*add)("\\\\", 2); + else if (c == '\'') + (*add)("''", 2); + + else + (*add)((char *)&c, 1); + } + (*add)("'", 1); +} +#endif + +/* internal function to quote a value stored as a blob */ + +static void quote_blob(char *data, int len, DB_FORMAT_CALLBACK add) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tquote_blob\n"); +fflush(stderr); +#endif + int i; + unsigned char c; + //char buffer[8]; + + (*add)("'", 1); + for (i = 0; i < len; i++) + { + c = (unsigned char)data[i]; + if (c == '\\') + (*add)("\\\\\\\\", 4); + else if (c == '\'') + (*add)("''", 2); + else if (c == 0) + (*add)("\\\\000", 5); + /*else if (c < 32 || c == 127) + { + int n = sprintf(buffer, "\\\\%03o", c); + (*add)(buffer, n); + }*/ + else + (*add)((char *)&c, 1); + } + (*add)("'", 1); +} + + +/* internal function to unquote a value stored as a string */ + +#if 0 +static int unquote(char *data, int len, DB_FORMAT_CALLBACK add) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tunquote\n"); +fflush(stderr); +#endif + + int i; + char c; + + if (!data || *data != '\'') + return TRUE; + + for (i = 1;; i++) + { + c = data[i]; + if (c == '\'') + break; + if (c == '\\') + i++; + (*add)(&data[i], 1); + } + + return FALSE; +} +#endif + +/* internal function to unquote a value stored as a blob */ + +static int unquote_blob(char *data, int len, DB_FORMAT_CALLBACK add) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tunquote_blob\n"); +fflush(stderr); +#endif + int i; + char c; + + + for (i = 0; i < len; i++) + { + c = data[i]; + + if (c == '\\') + { + i++; + c = data[i]; + if (c >= '0' && c <= '9' && i < (len - 2)) + { + c = ((data[i] - '0') << 6) + ((data[i + 1] - '0') << 3) + (data[i + 2] - '0'); + i += 2; + (*add)(&c, 1); + continue; + } + } + (*add)(&data[i], 1); + } + + return FALSE; +} + +/* +Internal function to check if the .Host property is actually an ODBC connection string. +ODBC ConnStrings have one or more "ParamName=ParamValue" pairs, delimited by semicolons. +The function helps the component know whether to call SQLConnect (when a host/DSN), +or SQLDriverConnect (when a ConnString). +I know there are C functions to locate CHARs in a CHAR[], but I'm not well versed +in C and less in what's available in a Gambas component, so I stuck to whatever other +routines use in this module. +zxMarce, 20150826 +*/ +static bool is_host_a_connstring(const char *host_or_cs) +{ + +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tis_host_a_connstring: '%s'\n", host_or_cs); +fflush(stderr); +#endif + + int counter; + char curChar; + + if (!host_or_cs) + return FALSE; + + for (counter = 0; counter < strlen(host_or_cs); counter++) + { + curChar = host_or_cs[counter]; + if ((curChar == '=') || (curChar == ';')) + return TRUE; + } + + return FALSE; +} + +/* Internal function to convert a database type into a Gambas type */ +static GB_TYPE conv_type(int type) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tconv_type: Field type: %d\n",type); +fflush(stderr); +#endif + switch (type) + { + case SQL_BINARY: + return GB_T_BOOLEAN; + /*case INT8OID: */ + case SQL_NUMERIC: + return GB_T_FLOAT; + case SQL_DECIMAL: + return GB_T_INTEGER; + case SQL_INTEGER: + return GB_T_INTEGER; + case SQL_SMALLINT: + return GB_T_INTEGER; + case SQL_BIGINT: + // New datatype bigint 64 bits + return GB_T_LONG; + case SQL_FLOAT: + return GB_T_FLOAT; + case SQL_REAL: + return GB_T_FLOAT; + case SQL_DOUBLE: + return GB_T_FLOAT; + case SQL_DATETIME: + case SQL_TYPE_DATE: + case SQL_TYPE_TIME: + case SQL_TYPE_TIMESTAMP: + return GB_T_DATE; + case SQL_LONGVARCHAR: + case SQL_VARBINARY: + case SQL_LONGVARBINARY: + // Data type for BLOB + return DB_T_BLOB; + case SQL_CHAR: + default: + return GB_T_STRING; + } +} + + +/* Internal function to convert a database value into a Gambas variant value */ +static void conv_data(char *data, GB_VARIANT_VALUE * val, int type) +{ + GB_VALUE conv; + GB_DATE_SERIAL date; + double sec; + int len; + int bc = 0; + + switch (type) + { + //case SQL_NUMERIC: + case SQL_DECIMAL: + case SQL_INTEGER: + case SQL_SMALLINT: + val->type = GB_T_INTEGER; + if (GB.NumberFromString(GB_NB_READ_INTEGER, data, strlen(data), &conv)) + val->value._integer = 0; + else + val->value._integer = conv._integer.value; + break; + + case SQL_NUMERIC: + case SQL_FLOAT: + case SQL_REAL: + case SQL_DOUBLE: + val->type = GB_T_FLOAT; + if (GB.NumberFromString(GB_NB_READ_FLOAT, data, strlen(data), &conv)) + val->value._float = 0; + else + val->value._float = conv._float.value; + break; + + case SQL_BIGINT: // Data type bigint 64 bits + val->type = GB_T_LONG; + if (GB.NumberFromString(GB_NB_READ_LONG, data, strlen(data), &conv)) + val->value._long = 0; + else + val->value._long = conv._long.value; + break; + + case SQL_LONGVARCHAR: + case SQL_VARBINARY: + case SQL_LONGVARBINARY: // Data type BLOB + // The BLOB are read by the blob_read() driver function + // You must set NULL there. + val->type = GB_T_NULL; + break; + + case SQL_TYPE_DATE: + case SQL_TYPE_TIME: + case SQL_TYPE_TIMESTAMP: + case SQL_DATETIME: // Data type for Time + { + memset(&date, 0, sizeof(date)); + len = strlen(data); + if (len > 3 && strcmp(&data[len - 2], "BC") == 0) + bc != 0; + else + bc = 0; + sscanf(data, "%4d-%2d-%2d %2d:%2d:%lf", &date.year, &date.month, + &date.day, &date.hour, &date.min, &sec); + date.sec = (short) sec; + date.msec = (short) ((sec - date.sec) * 1000 + 0.5); + if (bc) + date.year = (-date.year); + GB.MakeDate(&date, (GB_DATE *) & conv); + val->type = GB_T_DATE; + val->value._date.date = conv._date.value.date; + val->value._date.time = conv._date.value.time; + break; + } + + case SQL_CHAR: + default: + val->type = GB_T_CSTRING; + val->value._string = data; + break; + } + +} + + + +/***************************************************************************** + + get_quote() + + Returns the character used for quoting object names. + +*****************************************************************************/ + +static const char *get_quote(void) +{ + return QUOTE_STRING; +} + +/***************************************************************************** + zxMarce: Added 20160928 to retrieve the connected Database (or 'Catalog') + name directly from unixODBC/driver. + Actually, this routine *should* be called once after every operation, + because SPs or even plaintext queries could change the Catalog (for example + by using the "USE " command), but we don't have access to a + DB_DESC structure from the query-running code. +*****************************************************************************/ +void GetConnectedDBName(DB_DESC *desc, ODBC_CONN *odbc) +{ + + SQLRETURN retcode; + SQLINTEGER charsNeeded = 0; + SQLTCHAR *dbName; + + /*zxMarce: Attribute to fetch is SQL_ATTR_CURRENT_CATALOG + We call the function first with a NULL buffer pointer so as to + retrieve the necessary buffer size. + */ + retcode = SQLGetConnectAttrA(odbc->odbcHandle, SQL_ATTR_CURRENT_CATALOG, + NULL, (SQLINTEGER) 0, + (SQLINTEGER *) &charsNeeded + ); + + if (SQL_SUCCEEDED(retcode)) + { + charsNeeded++; + + dbName = malloc(sizeof(SQLTCHAR) * charsNeeded); + + /*zxMarce: We call the function again, this time specifying a + hopefully big enough buffer for storing the catalog name. + */ + retcode = SQLGetConnectAttr(odbc->odbcHandle, SQL_ATTR_CURRENT_CATALOG, + dbName, charsNeeded, + &charsNeeded + ); + dbName[sizeof(SQLTCHAR) * charsNeeded] = 0; + + GB.FreeString(&desc->name); + desc->name = GB.NewZeroString((char *)dbName); + free(dbName); + } + + if (DB.IsDebug()) + { + if (desc->name) + { + fprintf(stderr, "gb.db.odbc.GetConnectedDBName: desc->name (%d chars):'%s'.\n", (int)charsNeeded, desc->name); + } else { + fprintf(stderr, "gb.db.odbc.GetConnectedDBName: desc->name: NULL.\n"); + } + } + +} + +/***************************************************************************** + open_database() + Connect to a database. + points at a structure describing each connection parameter. + This function must return a database handle, or NULL if the connection + has failed. + The name of the database can be NULL, meaning a default database. +*****************************************************************************/ +static int open_database(DB_DESC *desc, DB_DATABASE *db) +{ + +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\topen_database\n"); +fflush(stderr); +#endif + + //int V_OD_erg; + SQLRETURN retcode; + ODBC_CONN *odbc; + bool hostIsAConnString; + char *host; + char *user; + + host = desc->host; + if (!host) + host = ""; + + user = desc->user; + if (!user) + user = ""; + + hostIsAConnString = is_host_a_connstring(host); + + /* Allocate the ODBC handle */ + odbc = (ODBC_CONN *)malloc(sizeof(ODBC_CONN)); + odbc->odbcHandle = NULL; + odbc->odbcEnvHandle = NULL; + odbc->dsn_name = NULL; + + /* Allocate the Environment handle */ + retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &odbc->odbcEnvHandle); + + if (!SQL_SUCCEEDED(retcode)) + { + free(odbc); + GB.Error("Unable to allocate ODBC environment handle"); + return TRUE; + } + + /* Set the Envoronment attributes */ + retcode = SQLSetEnvAttr(odbc->odbcEnvHandle, SQL_ATTR_ODBC_VERSION,(void *) SQL_OV_ODBC3, 0); + + if (!SQL_SUCCEEDED(retcode)) + { + SQLFreeHandle(SQL_HANDLE_ENV, odbc->odbcEnvHandle); + free(odbc); + GB.Error("Unable to set ODBC environment attributes"); + return TRUE; + } + + /* Allocate the Database Connection handle */ + retcode = SQLAllocHandle(SQL_HANDLE_DBC, odbc->odbcEnvHandle, &odbc->odbcHandle); + + if (!SQL_SUCCEEDED(retcode)) + { + SQLFreeHandle(SQL_HANDLE_ENV, odbc->odbcEnvHandle); + free(odbc); + GB.Error("Unable to allocate ODBC database handle"); + return TRUE; + + } + + /* 20170818 zxMarce: The following timeout was incorrectly set. The right timeout is the CONNECT + * timeout. The LOGIN timeout is actually used once the connection is established. Also took the + * opportunity to make sure ODBC uses cursors either from the Driver or the Driver Manager, thus + * almost ensuring cursors are available. + */ + //SQLSetConnectAttr(odbc->odbcHandle, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER)(intptr_t)db->timeout, 0); + SQLSetConnectAttr(odbc->odbcHandle, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER)(intptr_t)db->timeout, 0); + SQLSetConnectAttr(odbc->odbcHandle, SQL_ATTR_ODBC_CURSORS, (SQLPOINTER)SQL_CUR_USE_IF_NEEDED, 0); + + if (hostIsAConnString) + { + /* zxMarce: Connect to Database (desc->host is an ODBC Connection String) */ + retcode = SQLDriverConnect(odbc->odbcHandle, 0, (SQLCHAR *)host, SQL_NTS, 0, 0, 0, SQL_DRIVER_NOPROMPT); + /* The last three zero params in the call above can be used to retrieve the actual connstring used, + should unixODBC "complete" the passed ConnString with data from a matching defined DSN. Not + doing it here, but maybe useful to fill in the other Gambas Connection object properties (user, + pass, etc) after parsing it. Also note that the ConnString MAY refer to a DSN, and include + user/pass, if desired. + Example - ODBC-ConnString for FreeTDS, all one line (must assign this to the Connection.Host + property in Gambas code and then call Connection.Open): + "Driver=FreeTDS; + TDS_Version=; + Server=; + Port=; + Database=" + UId=; + Pwd=; + */ + + } else { + /* Connect to Database (desc->host is an ODBC Data Source Name) */ + retcode = SQLConnect(odbc->odbcHandle, (SQLCHAR *)host, SQL_NTS, (SQLCHAR *)user, SQL_NTS, (SQLCHAR *) desc->password, SQL_NTS); + } + + //zxMarce: Must bail out NOW if failed to connect, or nonsense errors will appear. + if (!SQL_SUCCEEDED(retcode)) + { + throwODBCError((hostIsAConnString ? "SQLDriverConnect" : "SQLConnect"), odbc->odbcHandle, SQL_HANDLE_DBC); + free(odbc); + //GB.Error("Error connecting to database"); + return TRUE; + } + + retcode = SQLSetConnectAttr(odbc->odbcHandle, SQL_ATTR_AUTOCOMMIT, (void *) SQL_AUTOCOMMIT_ON, SQL_NTS); + + /* desc->name is a pointer intended to point to the database name ('catalog', in + * MSSQL parlance) to which we are connected. But that is NOT the actual database + * name when we use a connstring. When we use a connstring, the '.Database' connection + * property would then have the whole connection string, so we tweak it here to make it + * point to the actual database (catalog?) name. + */ + //odbc->dsn_name = malloc(sizeof(char) * strlen(host)); + //strcpy(odbc->dsn_name, host); + GetConnectedDBName(desc, odbc); + + odbc->user_name = malloc(sizeof(char) * strlen(user)); + strcpy(odbc->user_name, user); + + db->version = 3; + + retcode = SQLGetFunctions(odbc->odbcHandle, SQL_API_SQLFETCHSCROLL, &odbc->FetchScroll_exist); + if (!SQL_SUCCEEDED(retcode)) + { + throwODBCError("SQLGetFunctions SQL_API_SQLFETCHSCROLL", odbc->odbcHandle, SQL_HANDLE_DBC); + free(odbc); + //GB.Error("Error calling the ODBC SQLGetFunctions API"); + return TRUE; + } + + /* flags */ + db->flags.no_table_type = TRUE; + db->flags.no_seek = (odbc->FetchScroll_exist == SQL_FALSE); + db->flags.no_serial = TRUE; // Need to be done! + db->flags.no_blob = FALSE; // Need to be done! + db->flags.no_collation = TRUE; + + db->handle = odbc; + return FALSE; +} + + +/***************************************************************************** + + close_database() + + Terminates the database connection. + + contains the database handle. + +*****************************************************************************/ + +static void close_database(DB_DATABASE *db) +{ + //SQLRETURN retcode; +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tclose_database\n"); +fflush(stderr); +#endif + + ODBC_CONN *conn = (ODBC_CONN *)db->handle; + //SQLRETURN retcode = 0; + + if (conn->odbcHandle) + SQLDisconnect(conn->odbcHandle); + else + GB.Error("ODBC module internal error disconnecting hDBC"); + + if (conn->odbcHandle) + { + SQLFreeHandle(SQL_HANDLE_DBC, conn->odbcHandle); + conn->odbcHandle = NULL; + } + else + GB.Error("ODBC module internal error freeing hDBC"); + + if (conn->odbcEnvHandle) + { + SQLFreeHandle(SQL_HANDLE_ENV, conn->odbcEnvHandle); + conn->odbcEnvHandle = NULL; + } + else + GB.Error("ODBC module internal error freeing hENV"); + + if (conn->dsn_name) + { + free(conn->dsn_name); + conn->dsn_name = NULL; + } + + if (conn->user_name) + { + free(conn->user_name); + conn->user_name = NULL; + } + + if (conn) + { + free(conn); + db->handle = NULL; + } + +} + + +/***************************************************************************** + + get_collations() + + Return the available collations as a Gambas string array. + +*****************************************************************************/ + +static GB_ARRAY get_collations(DB_DATABASE *db) +{ + return NULL; +} + + +/***************************************************************************** + + format_value() + + This function transforms a gambas value into a string value that can + be inserted into a SQL query. + + points to the value. + is a callback called to insert the string into the query. + + This function must return TRUE if it translates the value, and FALSE if + it does not. + + If the value is not translated, then a default translation is used. + +*****************************************************************************/ + +static int format_value(GB_VALUE * arg, DB_FORMAT_CALLBACK add) +{ + +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tformat_value\n"); +fflush(stderr); +#endif + + int l; + GB_DATE_SERIAL *date; + + switch (arg->type) + { + case GB_T_BOOLEAN: +/*Note this is likely to go to a tinyint */ + + if (VALUE((GB_BOOLEAN *) arg)) + add("'1'", 3); + else + add("'0'", 3); + return TRUE; + + case GB_T_STRING: + case GB_T_CSTRING: + + return FALSE; // default + + case GB_T_DATE: + + date = GB.SplitDate((GB_DATE *) arg); + + l = sprintf(_buffer, "'%04d-%02d-%02d-%02d.%02d.%02d", + date->year, date->month, date->day, + date->hour, date->min, date->sec); + + add(_buffer, l); + + if (date->msec) + { + l = sprintf(_buffer, ".%03d", date->msec); + add(_buffer, l); + } + + add("'", 1); + + return TRUE; + + default: + return FALSE; + } +} + +/***************************************************************************** + + format_blob() + + This function transforms a blob value into a string value that can + be inserted into a SQL query. + + points to the DB_BLOB structure. + is a callback called to insert the string into the query. + +*****************************************************************************/ + +static void format_blob(DB_BLOB *blob, DB_FORMAT_CALLBACK add) +{ + // BM: must be done! + #ifdef ODBC_DEBUG_HEADER + fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); + fprintf(stderr,"\tformat_blob\n"); + fflush(stderr); + #endif + quote_blob(blob->data, blob->length, add); + +} + + +static char *query_param[3]; + +static void query_get_param(int index, char **str, int *len, char quote) +{ + if (index > 3) + return; + + index--; + *str = query_param[index]; + *len = strlen(*str); + + if (quote == '\'' || quote == '`') + { + *str = DB.QuoteString(*str, *len, quote); + *len = GB.StringLength(*str); + } +} + +/* Internal function to implement the query execution */ +static int do_query(DB_DATABASE *db, const char *error, ODBC_RESULT **res, const char *qtemp, int nsubst, ...) +{ + +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tdo_query db %p, ODBC_result res %p, db->handle %p, query = '%s'\n", db, res, db->handle, query); +fflush(stderr); +#endif + + va_list args; + ODBC_CONN *handle = (ODBC_CONN *)db->handle; + SQLRETURN retcode = SQL_SUCCESS; + ODBC_RESULT *odbcres; + const char *query; + int i; + + if (nsubst) + { + va_start(args, nsubst); + if (nsubst > 3) + nsubst = 3; + for (i = 0; i < nsubst; i++) + query_param[i] = va_arg(args, char *); + + query = DB.SubstString(qtemp, 0, query_get_param); + } + else + query = qtemp; + + if (DB.IsDebug()) + fprintf(stderr, "gb.db.odbc.do_query: res %p, dbc handle %p, query '%s'\n", res, handle, query); + + GB.AllocZero(POINTER(&odbcres), sizeof(ODBC_RESULT)); + + /* Allocate the space for the result structure */ + retcode = SQLAllocHandle(SQL_HANDLE_STMT, handle->odbcHandle, &odbcres->odbcStatHandle); + if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) + { + //GB.Error("Cannot allocate statement handle"); + throwODBCError("SQLAllocHandle", handle->odbcHandle, SQL_HANDLE_DBC); + return retcode; + } + + retcode = SQLSetStmtAttr(odbcres->odbcStatHandle, SQL_ATTR_CURSOR_SCROLLABLE, (SQLPOINTER) SQL_SCROLLABLE, 0); + //if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) + //{ + // odbcres->Cursor_Scrollable = SQL_FALSE; + //} + //else odbcres->Cursor_Scrollable = SQL_TRUE; + odbcres->Cursor_Scrollable = ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) ? SQL_FALSE : SQL_TRUE; + odbcres->Function_exist = handle->FetchScroll_exist; + + /* Execute the query */ + retcode = SQLExecDirect(odbcres->odbcStatHandle, (SQLCHAR *) query, SQL_NTS); + if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_NO_DATA)) + { + if (DB.IsDebug()) + fprintf(stderr, "gb.db.odbc.do_query: SQLExecDirect() returned code %d\n", (int)retcode); + throwODBCError("SQLExecDirect", odbcres->odbcStatHandle, SQL_HANDLE_STMT); + SQLFreeHandle(SQL_HANDLE_STMT, odbcres->odbcStatHandle); + //GB.Error("Error while executing the statement"); + return retcode; + } + + if (res) + { + + if (retcode == SQL_NO_DATA) + { + odbcres->count = 0; + retcode = SQL_SUCCESS; + } + else + { + odbcres->count = GetRecordCount(odbcres->odbcStatHandle, odbcres->Cursor_Scrollable); + } + *res = odbcres; + + } + else + { + + SQLFreeHandle(SQL_HANDLE_STMT, odbcres->odbcStatHandle); + GB.Free(POINTER(&odbcres)); + + } + + return retcode; +} + + +/* Internal function - free the result structure create to allocate the result row */ +static void query_free_result(ODBC_RESULT * result) +{ + +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tquery_free_result %p\n",result); +fflush(stderr); +#endif + + ODBC_FIELDS *current, *next; + current = (ODBC_FIELDS *) result->fields; + next = (ODBC_FIELDS *) result->fields; + + while(current != NULL) + { + next = (ODBC_FIELDS *) current->next; + + if (current->fieldata != NULL) + { + free(current->fieldata); //091107 + current->fieldata = NULL; //091107 + } + + if(current != NULL) + { + free(current); + current = NULL; //091107 + } + + current = next; + + } + + if(result != NULL) + { + free(result); + result = NULL; //091107 + } + +} + + +/***************************************************************************** + + exec_query() + + Send a query to the server and gets the result. + + is the database handle, as returned by open_database() + is the query string. + will receive the result handle of the query. + is an error message used when the query failed. + + can be NULL, when we don't care getting the result. + +*****************************************************************************/ + +static int exec_query(DB_DATABASE *db, const char *query, DB_RESULT * result, const char *err) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\texec_query\n"); +fflush(stderr); +#endif + return do_query(db, err, (ODBC_RESULT **) result, query, 0); +} + +static int get_num_columns(ODBC_RESULT *result) +{ + +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tget_num_columns\n"); +fflush(stderr); +#endif + + SQLSMALLINT colsNum = 0; + SQLRETURN retcode; + + retcode = SQLNumResultCols(result->odbcStatHandle, &colsNum); + + if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) + GB.Error("ODBC error: Unable to get the number of columns"); + + return colsNum; +} + + +/* Internal function - create the space for the result and bind the column to each field-space allocated */ +static void query_make_result(ODBC_RESULT * result) +{ + +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tquery_make_result result %p, result->odbcStatHandle %p\n", result, result->odbcStatHandle); +fflush(stderr); +#endif + + SQLCHAR colname[32]; + SQLSMALLINT colnamelen; + SQLULEN precision; + SQLSMALLINT scale; + SQLINTEGER i; + SQLLEN displaysize; + ODBC_FIELDS *field, *current; + SQLINTEGER collen; + int nresultcols; + + nresultcols = get_num_columns(result); + + result->fields = NULL; + + if (result->fields == NULL) + { + + field = malloc(sizeof(ODBC_FIELDS)); + + result->fields = field; + current = field; + current->next=NULL; + current->fieldata=NULL; + + } + + for (i = 0; i < nresultcols; i++) + { + + + SQLDescribeCol(result->odbcStatHandle, i + 1, current->fieldname, sizeof(current->fieldname), &colnamelen, ¤t->type, &precision, &scale, NULL); + collen = precision; + + /* Get display length for column */ + SQLColAttribute(result->odbcStatHandle, i + 1, SQL_COLUMN_DISPLAY_SIZE, NULL,0, NULL, &displaysize); + + /* + * Set column length to max of display length, and column name + * length. Plus one byte for null terminator + */ + //printf("collen : %u, display len %u\n", strlen((char *) colname), displaysize); + + if (displaysize >= strlen((char *) colname)) + { + collen = displaysize + 1; + } + else + { + collen = strlen((char *) colname) + 1; + } + + if(collen <= 0){ + collen = 1; + } + current->fieldata = (SQLCHAR *) malloc(collen); + current->outlen = collen; + + if (collen > 0) + current->fieldata[collen-1] = '\0'; + + current->next = NULL; + + { + field = malloc(sizeof(ODBC_FIELDS)); + current->next = (struct ODBC_FIELDS *) field; + current = field; + current->next = NULL; + current->fieldata = NULL; + current->outlen = 0; + } + + } + + +} + + +/***************************************************************************** + + get_last_insert_id() + + Return the value of the last serial field used in an INSERT statement + + is the database handle, as returned by open_database() + +*****************************************************************************/ + +static int64_t get_last_insert_id(DB_DATABASE *db) +{ + GB.Error("Unsupported feature"); + return -1; +} + +/***************************************************************************** + + query_init() + + Initialize an info structure from a query result. + + is the handle of the query result. + points to the info structure. + will receive the number of records returned by the query. + + This function must initialize the info->nfield field with the number of + field in the query result. + + If receives -1, that will mean that the result is "move forward" + only + +*****************************************************************************/ + +static void query_init(DB_RESULT result, DB_INFO * info, int *count) +{ + +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tquery_init\n"); +fflush(stderr); +#endif + + ODBC_RESULT *res = (ODBC_RESULT *) result; + SQLSMALLINT colsNum = 0; + colsNum = get_num_columns(res); + + if (colsNum == 0) + return; + + //SQLRowCount(res->odbcStatHandle, &rowsNum); + + *count = res->count; + info->nfield = colsNum; + query_make_result(res); + +} + + + +/***************************************************************************** + + query_release() + + Free the info structure filled by query_init() and the result handle. + + is the handle of the query result. + points to the info structure. + +*****************************************************************************/ + +static void query_release(DB_RESULT result, DB_INFO *info) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tquery_release\n"); +fflush(stderr); +#endif + + ODBC_RESULT *res = (ODBC_RESULT *) result; + + /*if (res != NULL)*/ //query_free_result(res); + + SQLFreeHandle(SQL_HANDLE_STMT, res->odbcStatHandle); + //free(res->odbcStatHandle); + + query_free_result(res); + //free(res->odbcStatHandle); + //res->odbcStatHandle=NULL; + +} + + + +/***************************************************************************** + + query_fill() + + Fill a result buffer with the value of each field of a record. + + is the database handle, as returned by open_database() + is the handle of the result. + is the index of the record in the result. + points to an array having one element for each field in the + result. + is a boolean telling if we want the next row. + + This function must return DB_OK, DB_ERROR or DB_NO_DATA + + This function must use GB.StoreVariant() to store the value in the + buffer. + +*****************************************************************************/ + + +static int query_fill(DB_DATABASE *db, DB_RESULT result, int pos, GB_VARIANT_VALUE * buffer, int next) +{ + ODBC_RESULT *res = (ODBC_RESULT *) result; + GB_VARIANT value; + SQLRETURN retcode2; + SQLINTEGER i; + ODBC_FIELDS *current; + //SQLRETURN retcode; + int nresultcols; + SQLINTEGER displaysize; + //int V_OD_erg=0; + +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tquery_fill result %p,result->odbcStatHandle %p, pos %d\n",res,res->odbcStatHandle,pos); +fflush(stderr); +#endif + + nresultcols = get_num_columns(res); + if (nresultcols == 0) + return DB_ERROR; + + /*current = res->fields; + + for (i = 0; i < nresultcols; i++) + { + if(current->next) + current = (ODBC_FIELDS *) current->next; + }*/ + + if (res->Function_exist == SQL_TRUE) + { + if(res->Cursor_Scrollable == SQL_TRUE) + { + retcode2 = SQLFetchScroll(res->odbcStatHandle, SQL_FETCH_ABSOLUTE, pos + 1); + } + else + { + retcode2 = SQLFetchScroll(res->odbcStatHandle, SQL_FETCH_NEXT, pos + 1); + } + } + else + { + if (!next) + { + GB.Error("Unable to fetch row"); + return DB_ERROR; + } + + retcode2 = SQLFetch(res->odbcStatHandle); + } + + if ((retcode2 != SQL_SUCCESS) && (retcode2 != SQL_SUCCESS_WITH_INFO) && (retcode2 != SQL_NO_DATA_FOUND) && (retcode2 != SQL_NO_DATA)) + { + GB.Error("Unable to fetch row"); + return DB_ERROR; + } + + if((retcode2 == SQL_NO_DATA_FOUND) || (retcode2==SQL_NO_DATA)) + return DB_NO_DATA; + + current = res->fields; + + for (i = 0; i < nresultcols; i++) + { + displaysize=0; + char * fieldata; + SQLULEN precision=0; + + SQLSMALLINT colnamelen=0,scale=0,type; + SQLLEN read=0; + SQLCHAR namebuff[25]; + + SQLDescribeCol(res->odbcStatHandle, i+1 , namebuff, sizeof(namebuff), &colnamelen,&type,&precision, &scale,NULL); + + SQLColAttribute(res->odbcStatHandle, i+1 , SQL_DESC_LENGTH, "",0, NULL, (SQLPOINTER)&displaysize); + + read=0; + + if (displaysize >= strlen((char *) namebuff)) + { + displaysize = displaysize + 1; + } + else + { + displaysize=strlen((char *) namebuff) + 1; + } + + if (displaysize > 0) + { + if (displaysize < 2) + displaysize = 2; + + if (type != SQL_LONGVARCHAR && type != SQL_VARBINARY && type != SQL_LONGVARBINARY) + { + fieldata=malloc(sizeof(char)*(displaysize)); + SQLGetData(res->odbcStatHandle,i+1,SQL_C_CHAR , fieldata,displaysize,&read); + + } + else + { + //BLOB field, not retrieved here + + //the BLOB field hasn't the string terminator + displaysize=displaysize-1; + + } + + current->outlen=displaysize; + } + + value.type = GB_T_VARIANT; + value.value.type = GB_T_NULL; + + if (current==NULL) + { + GB.Error("ODBC internal error 4"); + return DB_ERROR; + } + + //fprintf(stderr,"Lunghezza letta = %d\n",read); + if (current) + { + if(current->fieldata==NULL) + { + GB.Error("ODBC internal error 5"); + return DB_ERROR; + } + + if (read == -1){ + fieldata[0]=' ';fieldata[1]='\0'; + current->type=SQL_CHAR; + } + conv_data((char *) fieldata, &value.value, (int) current->type); + } + + GB.StoreVariant(&value,&buffer[i]); + + if(displaysize >0 && fieldata !=NULL)free(fieldata); + current = (ODBC_FIELDS *) current->next; + fieldata=NULL; + + }/* for all columns in this row */ + + return DB_OK; +} + + +/***************************************************************************** + + blob_read() + + Returns the value of a BLOB field. + + is the handle of the result. + is the index of the record in the result. + points at a DB_BLOB structure that will receive a pointer to the + data and its length. + +*****************************************************************************/ + +static void blob_read(DB_RESULT result, int pos, int field, DB_BLOB *blob) +{ + int i; + //int outlen; + //int precision; + //int scale; + //int displaysize; + //char * pointer; + ODBC_RESULT * res= (ODBC_RESULT *) result; + ODBC_FIELDS * cfield ; + SQLLEN strlen; + SQLRETURN retcode; + + i = 0; + cfield = res->fields; + +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tblob_read DB_RESULT %p, dbresult->stathandle %p, pos %d , field %d\n",res,res->odbcStatHandle ,pos,field); +fflush(stderr); +#endif + + while (i < field ) + { + if (cfield->next == NULL) + { + GB.Error("ODBC module: Internal error 1"); + return; + } + + cfield=(ODBC_FIELDS *) cfield->next; + + if (cfield == NULL) + { + GB.Error("ODBC module: Internal error 2"); + return; + } + + i++; + } + + if (i > field) + { + GB.Error("ODBC module: Internal error"); + return; + } + + blob->data=NULL; + if (cfield->outlen > 0) + { + blob->data = malloc( sizeof(char)*cfield->outlen); + blob->length = cfield->outlen; + + DB.Query.Init(); + + retcode = SQLGetData(res->odbcStatHandle, field+1, SQL_C_BINARY, blob->data, blob->length, &strlen); + + if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) + { + GB.Error("Unable to retrieve blob data"); + free(blob->data); + blob->length = 0; + blob->data = NULL; + return; + } + } else { + blob->data = NULL; // + blob->length = 0; + return; + } + + char *data; + int len; + + if (!unquote_blob(blob->data, blob->length, DB.Query.AddLength)) + { + len = DB.Query.Length(); + data = DB.Query.GetNew(); + } + else + blob->constant = TRUE; + + free(blob->data);//091107 + blob->data = data; + blob->length = len; +} + + +/***************************************************************************** + + field_name() + + Return the name of a field in a result from its index. + + is the result handle. + is the field index. + +*****************************************************************************/ + +static char *field_name(DB_RESULT result, int field) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tfield_name\n"); +fflush(stderr); +#endif + SQLCHAR colname[32]; + SQLSMALLINT coltype=0; + SQLSMALLINT colnamelen=0; + SQLULEN precision=0; + SQLSMALLINT scale=0; + + ODBC_RESULT *res = (ODBC_RESULT *) result; + + SQLDescribeCol(res->odbcStatHandle, field + 1, colname, sizeof(colname), &colnamelen, &coltype, &precision, &scale, NULL); + //colnamer = malloc(sizeof(char) * strlen((char *) colname) + 1); + strcpy(_buffer, (char *) colname); + return _buffer; +} + + +/***************************************************************************** + + field_index() + + Return the index of a field in a result from its name. + + is the result handle. + is the field name. + is needed by this driver to enable table.field syntax + +*****************************************************************************/ + +static int field_index(DB_RESULT result, const char *name, DB_DATABASE *db) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tfield_index\n"); +fflush(stderr); +#endif + SQLCHAR colname[32]; + SQLSMALLINT coltype; + SQLSMALLINT colnamelen; + SQLULEN precision; + SQLSMALLINT scale; + SQLSMALLINT colsNum; + int field; + ODBC_RESULT *res = (ODBC_RESULT *) result; + + colnamelen = 32; + colsNum = get_num_columns(res); + + for (field = 0; field < colsNum; field++) + { + SQLDescribeCol(res->odbcStatHandle, field + 1, colname, sizeof(colname), + &colnamelen, &coltype, &precision, &scale, NULL); + + if (strcmp(name, (char *)colname) == 0) + { + return (int) (field); + } + + } + + + return (0); +} + + +/***************************************************************************** + + field_type() + + Return the Gambas type of a field in a result from its index. + + is the result handle. + is the field index. + +*****************************************************************************/ + +static GB_TYPE field_type(DB_RESULT result, int field) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tfield_type id %d\n",field); +fflush(stderr); +#endif + SQLCHAR colname[32]; + SQLSMALLINT coltype; + SQLSMALLINT colnamelen; + SQLULEN precision; + SQLSMALLINT scale; + ODBC_RESULT *res = (ODBC_RESULT *) result; + SQLRETURN retcode; + + retcode=SQLDescribeCol(res->odbcStatHandle, field + 1, colname, sizeof(colname), + &colnamelen, &coltype, &precision, &scale, NULL); + + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) + { + GB.Error("Unable to retrieve field type"); + return GB_T_NULL; + } + else + return conv_type(coltype); +} + + +/***************************************************************************** + + field_length() + + Return the length of a field in a result from its index. + + is the result handle. + is the field index. + +*****************************************************************************/ + +static int field_length(DB_RESULT result, int field) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tfield_length\n"); +fflush(stderr); +#endif + SQLCHAR colname[32]; + SQLSMALLINT coltype; + SQLSMALLINT colnamelen; + SQLULEN precision; + SQLSMALLINT scale; + ODBC_RESULT *res = (ODBC_RESULT *) result; + + SQLDescribeCol(res->odbcStatHandle, field + 1, colname, sizeof(colname), &colnamelen, &coltype, &precision, &scale, NULL); + + return colnamelen; + +} + + +/***************************************************************************** + + begin_transaction() + + Begin a transaction. + + is the database handle. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ +static int begin_transaction(DB_DATABASE *db) +{ + return (do_query(db, "Unable to begin transaction: &1", NULL, "BEGIN", 0)); +} + + + + +/***************************************************************************** + + commi_transaction() + + Commit a transaction. + + is the database handle. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int commit_transaction(DB_DATABASE *db) +{ + return (do_query(db, "Unable to commit transaction: &1", NULL, "COMMIT", 0)); +} + + +/***************************************************************************** + + rollback_transaction() + + Rolllback a transaction. + + is the database handle. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ +static int rollback_transaction(DB_DATABASE *db) +{ + return do_query(db, "Unable to rollback transaction: &1", NULL, "ROLLBACK", 0); +} + + + +/***************************************************************************** + + table_init() + + Initialize an info structure from table fields. + + is the database handle. +
    is the table name. + points at the info structure. + + This function must initialize the following info fields: + - info->nfield must contain the number of fields in the table. + - info->fields is a char*[] pointing at the name of each field. + - info->types is a GB_TYPE[] giving the gambas type of each field. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_init(DB_DATABASE *db, const char *table, DB_INFO * info) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\ttable_init\n"); +fflush(stderr); +#endif + SQLCHAR coltype[100]; + SQLCHAR precision[100]; + SQLSMALLINT colsNum; + SQLHSTMT statHandle; + //SQLRETURN V_OD_erg; + SQLRETURN retcode; + int i; + DB_FIELD *f; + ODBC_FIELDS *fieldstr, *current; + ODBC_CONN *han = (ODBC_CONN *)db->handle; + + + info->table = GB.NewZeroString(table); + + retcode =SQLAllocHandle(SQL_HANDLE_STMT, (ODBC_CONN *) han->odbcHandle, &statHandle); + + + if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) + { + return retcode; + } + + if (!SQL_SUCCEEDED + (colsNum = + SQLColumns(statHandle, NULL, 0, NULL, 0, (SQLCHAR *) table, SQL_NTS, NULL, + 0))) + return -1; + + fieldstr = malloc(sizeof(ODBC_FIELDS)); + current = fieldstr; + + colsNum = 0; + while (SQL_SUCCEEDED(SQLFetch(statHandle))) + { + SQLGetData(statHandle, SQLColumns_COLUMN_NAME, SQL_C_CHAR, current->fieldname, + sizeof(current->fieldname), 0); + + if (!SQL_SUCCEEDED + (SQLGetData + (statHandle, SQLColumns_SQL_DATA_TYPE, SQL_C_CHAR, &coltype[0], + sizeof(coltype), 0))) + return TRUE; + + current->type = atol((char *)coltype); + + if (!SQL_SUCCEEDED + (SQLGetData + (statHandle, SQLColumns_COLUMN_SIZE, SQL_C_CHAR, precision, + sizeof(precision), 0))) + return TRUE; + + current->outlen = atol((char *)precision); + colsNum = colsNum + 1; + current->next = malloc(sizeof(ODBC_FIELDS)); + current = (ODBC_FIELDS *) current->next; + } + + info->nfield = colsNum; + GB.Alloc(POINTER(&info->field), sizeof(DB_FIELD) * colsNum); + i = 0; + current = fieldstr; + + for (i = 0; i < colsNum; i++) + { + fieldstr = current; + f = &info->field[i]; + f->name = GB.NewZeroString((char *)current->fieldname); + + f->type = conv_type(current->type); + + f->length = 0; + if (f->type == GB_T_STRING) + f->length = current->outlen; + free(fieldstr); + current = (ODBC_FIELDS *) current->next; + } + if (current != NULL) free(current); + return FALSE; +} + + + +/***************************************************************************** + + table_index() + + Initialize an info structure from table primary index. + + is the database handle. +
    is the table name. + points at the info structure. + + This function must initialize the following info fields: + - info->nindex must contain the number of fields in the primary index. + - info->index is a int[] giving the index of each index field in + info->fields. + + This function must be called after table_init(). + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_index(DB_DATABASE *db, const char *table, DB_INFO * info) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\ttable_index - info %p,info->nindex %d\n",info,info->nindex); +fflush(stderr); +#endif + int inx[256]; + SQLHSTMT statHandle, statHandle2; + //SQLRETURN V_OD_erg; + SQLRETURN nReturn = -1; + SQLRETURN retcode; + SQLCHAR szKeyName[101] = ""; + SQLCHAR szColumnName[101] = ""; + SQLCHAR query[101] = "SELECT * FROM "; + SQLSMALLINT colsNum; + int i, n; + ODBC_CONN *han = (ODBC_CONN *)db->handle; + ODBC_FIELDS *fieldstr, *current; + ODBC_RESULT *res; + + strcpy((char *)&query[14], table); + + + colsNum = 0; + + retcode =SQLAllocHandle(SQL_HANDLE_STMT, (ODBC_CONN *) han->odbcHandle, &statHandle2); + + if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) + { + return retcode; + } + + retcode =SQLColumns(statHandle2, NULL, 0, NULL, 0, (SQLCHAR *) table, SQL_NTS, NULL, 0); + + if (!SQL_SUCCEEDED(retcode)) + { + SQLFreeHandle(SQL_HANDLE_STMT, statHandle2); + return -1; + } + + fieldstr = malloc(sizeof(ODBC_FIELDS)); + + current = fieldstr; + current->next=NULL; + + while (SQL_SUCCEEDED(SQLFetch(statHandle2))) + { + if (!SQL_SUCCEEDED(SQLGetData (statHandle2, SQLColumns_COLUMN_NAME, SQL_C_CHAR, current->fieldname, sizeof(current->fieldname), 0))) + strcpy((char *)current->fieldname, "Unknown"); + + colsNum = colsNum + 1; + current->next = malloc(sizeof(ODBC_FIELDS)); + current = (ODBC_FIELDS *) current->next; + current->next=NULL; + } + + current = fieldstr; + + retcode=SQLNumResultCols(statHandle2, &colsNum); + + SQLFreeHandle(SQL_HANDLE_STMT, statHandle2); + + + res = malloc(sizeof(ODBC_RESULT)); + + retcode = SQLAllocHandle(SQL_HANDLE_STMT, han->odbcHandle, &statHandle); + + if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) + { + return retcode; + } + + + if (!SQL_SUCCEEDED(nReturn = SQLPrimaryKeys(statHandle, 0, 0, 0, SQL_NTS, (SQLCHAR *)table, SQL_NTS))) + { + + free(res); + return TRUE; + } + + retcode=SQLNumResultCols(statHandle, &colsNum); + + i = 0; + + while (SQL_SUCCEEDED(SQLFetch(statHandle))) + { + + if (!SQL_SUCCEEDED(SQLGetData (statHandle, 4, SQL_C_CHAR, szColumnName, sizeof(szColumnName), 0))) + strcpy((char *) szColumnName, "Unknown"); + + if (!SQL_SUCCEEDED(SQLGetData(statHandle, 6, SQL_C_CHAR, szKeyName, sizeof(szKeyName), 0))) + strcpy((char *) szKeyName, "Unknown"); + + current = fieldstr; + + for (n = 0; n < colsNum; n++) + { + + if (strcmp((char *)current->fieldname, (char *)szColumnName) == 0) + { + inx[i] = n; + + break; + } + current=(ODBC_FIELDS *)current->next; + if (current==NULL) break; + + } + + i++; + + } + + GB.Alloc(POINTER(&info->index), sizeof(int) * i); + info->nindex = i; + + SQLFreeHandle(SQL_HANDLE_STMT, statHandle); + + for (n = 0; n < i; n++) + info->index[n] = inx[n]; + + free(res); + + while(current != NULL){ + if (current->next != NULL) + { + fieldstr = (ODBC_FIELDS *)current->next ; + free (current); + current=fieldstr; + } + else + { + free (current); + current =NULL; + } + } + + return FALSE; +} + + +/***************************************************************************** + + table_release() + + Free the info structure filled by table_init() and/or table_index() + + is the database handle. + points at the info structure. + +*****************************************************************************/ + +static void table_release(DB_DATABASE *db, DB_INFO * info) +{ + /* All is done outside the driver */ +} + + +/***************************************************************************** + + table_exist() + + Returns if a table exists + + is the database handle. +
    is the table name. + + This function returns TRUE if the table exists, and FALSE if not. + +*****************************************************************************/ + +static int table_exist(DB_DATABASE *db, const char *table) +{ + +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\ttable_exist\n"); +fflush(stderr); +#endif + SQLHSTMT statHandle; + //SQLRETURN V_OD_erg; + SQLRETURN nReturn = -1; + SQLRETURN retcode; + SQLCHAR szTableName[101] = ""; + SQLCHAR szTableType[101] = ""; + SQLCHAR szTableRemarks[301] = ""; + SQLLEN nIndicatorName; + SQLLEN nIndicatorType; + SQLLEN nIndicatorRemarks; + int compare = -1; + ODBC_CONN *han = (ODBC_CONN *)db->handle; + int len; + + len = strlen(table); + if (len == 0) + return FALSE; + + retcode = SQLAllocHandle(SQL_HANDLE_STMT, han->odbcHandle, &statHandle); + + if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) + { + return FALSE; //V_OD_erg; + } + + // EXECUTE OUR SQL/CALL + if (SQL_SUCCESS != (nReturn = SQLTables(statHandle, 0, 0, 0, 0, 0, 0, 0, 0))) + { + return FALSE; //nReturn; + } + + SQLBindCol(statHandle, SQLTables_TABLE_NAME, SQL_C_CHAR, szTableName, + sizeof(szTableName), &nIndicatorName); + SQLBindCol(statHandle, SQLTables_TABLE_TYPE, SQL_C_CHAR, szTableType, + sizeof(szTableType), &nIndicatorType); + SQLBindCol(statHandle, SQLTables_REMARKS, SQL_C_CHAR, szTableRemarks, + sizeof(szTableRemarks), &nIndicatorRemarks); + // GET RESULTS + nReturn = SQLFetch(statHandle); + while ((nReturn == SQL_SUCCESS || nReturn == SQL_SUCCESS_WITH_INFO) + && compare != 0) + { + //printf("le tabelle in comparazione %s : %s\n",szTableName,table); + compare = strncmp((char *)szTableName, table, len); + szTableName[0] = '\0'; + szTableType[0] = '\0'; + szTableRemarks[0] = '\0'; + nReturn = SQLFetch(statHandle); + } + + // FREE STATEMENT + nReturn = SQLFreeHandle(SQL_HANDLE_STMT, statHandle); + + if (compare == 0) + return TRUE; + else + return FALSE; + +} + + + +/***************************************************************************** + + table_list() + + Returns an array containing the name of each table in the database + + is the database handle. + points to a variable that will receive the char* array. + + This function returns the number of tables, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int table_list(DB_DATABASE *db, char ***tables) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\ttable_list\n"); +fflush(stderr); +#endif + ODBC_TABLES tablelist, *curtable; + SQLHSTMT statHandle; + //SQLRETURN V_OD_erg; + SQLRETURN nReturn = -1; + SQLRETURN retcode; + SQLCHAR szTableName[101] = ""; + SQLCHAR szTableType[101] = ""; + SQLCHAR szTableRemarks[301] = ""; + SQLLEN nIndicatorName; + SQLLEN nIndicatorType; + SQLLEN nIndicatorRemarks; + int tablenum = 0; + int i; + ODBC_CONN *han = (ODBC_CONN *)db->handle; + + retcode = SQLAllocHandle(SQL_HANDLE_STMT, han->odbcHandle, &statHandle); + + if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) + { + return retcode; + } + + curtable = &tablelist; + + // EXECUTE OUR SQL/CALL + if (SQL_SUCCESS != (nReturn = SQLTables(statHandle, 0, 0, 0, 0, 0, 0, 0, 0))) + { + + return nReturn; + } + + SQLBindCol(statHandle, SQLTables_TABLE_NAME, SQL_C_CHAR, szTableName, + sizeof(szTableName), &nIndicatorName); + SQLBindCol(statHandle, SQLTables_TABLE_TYPE, SQL_C_CHAR, szTableType, + sizeof(szTableType), &nIndicatorType); + SQLBindCol(statHandle, SQLTables_REMARKS, SQL_C_CHAR, szTableRemarks, + sizeof(szTableRemarks), &nIndicatorRemarks); + // GET RESULTS + nReturn = SQLFetch(statHandle); + + if (nReturn != SQL_SUCCESS && nReturn != SQL_SUCCESS_WITH_INFO) + { + + SQLFreeHandle(SQL_HANDLE_STMT, statHandle); + return (-1); + + } + + + while (nReturn == SQL_SUCCESS || nReturn == SQL_SUCCESS_WITH_INFO) + { + tablenum = tablenum + 1; + curtable->tablename = malloc(sizeof(szTableName)); + curtable->next = malloc(sizeof(ODBC_TABLES)); + strcpy(curtable->tablename, (char *)szTableName); + curtable = (ODBC_TABLES *) curtable->next; + szTableName[0] = '\0'; + szTableType[0] = '\0'; + szTableRemarks[0] = '\0'; + nReturn = SQLFetch(statHandle); + } + + // FREE STATEMENT + nReturn = SQLFreeHandle(SQL_HANDLE_STMT, statHandle); + + GB.NewArray(tables, sizeof(char *), tablenum); + + curtable = &tablelist; + for (i = 0; i < tablenum; i++) + { + (*tables)[i] = GB.NewZeroString(curtable->tablename); + free(curtable->tablename); + curtable = (ODBC_TABLES *) curtable->next; + + } + + curtable = &tablelist; + int g; + + for (i = tablenum; i > 0; i--) + { + + + for (g = 0; g < i; g++) + { + + curtable = (ODBC_TABLES *) curtable->next; + + } + + free(curtable); + curtable = &tablelist; + } + + + return (tablenum); +} + + +/***************************************************************************** + + table_primary_key() + + Returns a string representing the primary key of a table. + + is the database handle. +
    is the table name. + points to a string that will receive the primary key. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_primary_key(DB_DATABASE *db, const char *table, char ***primary) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\ttable_primary_key\n"); +fflush(stderr); +#endif + SQLHSTMT statHandle; + SQLRETURN retcode; + //SQLRETURN V_OD_erg; + SQLRETURN nReturn = -1; + SQLCHAR szKeyName[101] = ""; + SQLCHAR szColumnName[101] = ""; + SQLCHAR query[101] = "SELECT * FROM "; + SQLSMALLINT colsNum; + int i; + ODBC_CONN *han = (ODBC_CONN *)db->handle; + + // CREATE A STATEMENT + ODBC_RESULT *res; + + strcpy((char *)&query[14], table); + + res = malloc(sizeof(ODBC_RESULT)); + + retcode = SQLAllocHandle(SQL_HANDLE_STMT, han->odbcHandle, &statHandle); + + if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) + { + return retcode; + } + + + /*if (!SQL_SUCCEEDED + (nReturn = SQLPrimaryKeys(statHandle, 0, 0, 0, SQL_NTS, (SQLCHAR *)table, SQL_NTS)))*/ + if (!SQL_SUCCEEDED(nReturn = SQLPrimaryKeys(statHandle, (SQLCHAR *)"", 0, (SQLCHAR *)"", 0, (SQLCHAR *)table, SQL_NTS))) + { + free(res); + fprintf(stderr, "return %d\n", nReturn); + GB.Error("Unable to get primary key: &1", table); + return TRUE; + } + // GET RESULTS + + + + SQLNumResultCols(statHandle, &colsNum); + + GB.NewArray(primary, sizeof(char *), 0); + i = 0; + + while (SQL_SUCCEEDED(SQLFetch(statHandle))) + { + + if (!SQL_SUCCEEDED + (SQLGetData + (statHandle, 4, SQL_C_CHAR, &szColumnName[0], sizeof(szColumnName), 0))) + strcpy((char *) szColumnName, "Unknown"); + + if (!SQL_SUCCEEDED + (SQLGetData + (statHandle, 6, SQL_C_CHAR, &szKeyName[0], sizeof(szKeyName), 0))) + strcpy((char *) szKeyName, "Unknown"); + + *(char **)GB.Add(primary) = GB.NewZeroString((char *)szColumnName); + i++; + } + + + SQLFreeHandle(SQL_HANDLE_STMT, statHandle); + free(res); + return FALSE; + +} + + +/***************************************************************************** + + table_is_system() + + Returns if a table is a system table. + + is the database handle. +
    is the table name. + + This function returns TRUE if the table is a system table, and FALSE if + not. + +*****************************************************************************/ + +static int table_is_system(DB_DATABASE *db, const char *table) +{ + return FALSE; +} + + +/***************************************************************************** + + table_delete() + + Deletes a table. + + is the database handle. +
    is the table name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_delete(DB_DATABASE *db, const char *table) +{ + int exit; + const char *query = "DROP TABLE &1"; + + exit = do_query(db, "Cannot delete table: &1", NULL, query, 1, table); + /* BM: What's that ??? + if (exit == 0) + { + exit = do_query(db, "Cannot delete table:&1", NULL, "COMMIT", 0); + }*/ + + return (exit); +} + + + +/***************************************************************************** + + table_create() + + Creates a table. + + is the database handle. +
    is the table name. + points to a linked list of field descriptions. + is the primary key. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_create(DB_DATABASE *db, const char *table, DB_FIELD *fields, char **primary, const char *type_not_used) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\ttable_create\n"); +fflush(stderr); +#endif + + DB_FIELD *fp; + int comma; + char *type; + int i, exit; + + DB.Query.Init(); + + DB.Query.Add("CREATE TABLE "); + DB.Query.Add(table); + DB.Query.Add(" ( "); + + comma = FALSE; + for (fp = fields; fp; fp = fp->next) + { + if (comma) + DB.Query.Add(", "); + else + comma = TRUE; + + DB.Query.Add(fp->name); + +//AB autoincrement field is mapped to Integer because this is Database dependent + if (fp->type == DB_T_SERIAL) + DB.Query.Add(" INTEGER ");// +//AB Blob field else if (fp->type == DB_T_BLOB) + DB.Query.Add(" LONG VARBINARY "); + + + switch (fp->type) + { + case GB_T_BOOLEAN: + type = "SMALLINT"; // + break; + case GB_T_INTEGER: + type = "INTEGER"; + break; + case GB_T_FLOAT: + type = "DOUBLE"; + break; + case GB_T_DATE: + type = "DATE"; // AB changed the field name used by default from Timestamp to Date + break; +// New datatype BIGINT 64 bits + case GB_T_LONG: + type = "BIGINT"; + break; + case GB_T_STRING: + + if (fp->length <= 0) + type = "TEXT"; + else + { + sprintf(_buffer, "VARCHAR(%d)", fp->length); + type = _buffer; + } + + break; + + default: + type = "TEXT"; + break; + } + + DB.Query.Add(" "); + DB.Query.Add(type); + + if (fp->def.type != GB_T_NULL) + { + DB.Query.Add(" NOT NULL DEFAULT "); + DB.FormatVariant(&_driver, &fp->def, DB.Query.AddLength); + } + else if (DB.StringArray.Find(primary, fp->name) >= 0) + { + DB.Query.Add(" NOT NULL "); + } + } + + if (primary) + { + DB.Query.Add(", PRIMARY KEY ("); + + for (i = 0; i < GB.Count(primary); i++) + { + if (i > 0) + DB.Query.Add(","); + + DB.Query.Add(primary[i]); + } + + DB.Query.Add(")"); + } + + DB.Query.Add(" )"); + + exit = do_query(db, "Cannot create table: &1", NULL, DB.Query.Get(), 0); + + + if (exit == 0) + { + exit = do_query(db, "Cannot create table:&1", NULL, "COMMIT", 0); + } + + return (exit); + +} + + +/***************************************************************************** + + field_exist() + + Returns if a field exists in a given table + + is the database handle. +
    is the table name. + is the field name. + + This function returns TRUE if the field exists, and FALSE if not. + +*****************************************************************************/ + +static int field_exist(DB_DATABASE *db, const char *table, const char *field) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tfield_exist\n"); +fflush(stderr); +#endif + SQLCHAR colname[256]; + SQLSMALLINT colsNum; + SQLHSTMT statHandle; + SQLRETURN retcode; + //SQLRETURN V_OD_erg; + ODBC_CONN *han = (ODBC_CONN *)db->handle; + + + + retcode = + SQLAllocHandle(SQL_HANDLE_STMT, (ODBC_CONN *) han->odbcHandle, &statHandle); + + if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) + { + return FALSE; //V_OD_erg; + } + +//printf("field exist dopo l'handler\n"); + + if (!SQL_SUCCEEDED + (colsNum = + SQLColumns(statHandle, NULL, 0, NULL, 0, (SQLCHAR *) table, SQL_NTS, NULL, + 0))) + { + SQLFreeHandle(SQL_HANDLE_STMT, statHandle); + return FALSE; + } + +//printf("field exist dopo la SQLColumn : %u\n",colsNum); + + while (SQL_SUCCEEDED(SQLFetch(statHandle))) + { + SQLGetData(statHandle, SQLColumns_COLUMN_NAME, SQL_C_CHAR, colname, + sizeof(colname), 0); + +//printf("field exist dopo la get data - field =%s, Colname %s\n",field,colname); + + if (strcmp((char *)colname, field) == 0) + { + +//printf("Trovato il campo\n"); + + SQLFreeHandle(SQL_HANDLE_STMT, statHandle); + return TRUE; + + } + + } + + SQLFreeHandle(SQL_HANDLE_STMT, statHandle); + return FALSE; + +} + + +/***************************************************************************** + + field_list() + + Returns an array containing the name of each field in a given table + + is the database handle. +
    is the table name. + points to a variable that will receive the char* array. + + This function returns the number of fields, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int field_list(DB_DATABASE *db, const char *table, char ***fields) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tfield_list\n"); +fflush(stderr); +#endif + SQLSMALLINT colsNum; + SQLHSTMT statHandle; + SQLRETURN retcode; + //SQLRETURN V_OD_erg; + int i; + ODBC_CONN *han = (ODBC_CONN *)db->handle; + ODBC_FIELDS *fieldstr, *current; + + + colsNum = 0; + retcode = + SQLAllocHandle(SQL_HANDLE_STMT, (ODBC_CONN *) han->odbcHandle, &statHandle); + + if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) + { + return retcode; + } + + + + retcode = + SQLColumns(statHandle, NULL, 0, NULL, 0, (SQLCHAR *) table, SQL_NTS, NULL, 0); + + + if (!SQL_SUCCEEDED(retcode)) + { + + SQLFreeHandle(SQL_HANDLE_STMT, statHandle); + return -1; + } + + + + fieldstr = malloc(sizeof(ODBC_FIELDS)); + + current = fieldstr; + + + + while (SQL_SUCCEEDED(SQLFetch(statHandle))) + { + if (!SQL_SUCCEEDED + (SQLGetData + (statHandle, SQLColumns_COLUMN_NAME, SQL_C_CHAR, current->fieldname, + sizeof(current->fieldname), 0))) + strcpy((char *)current->fieldname, "Unknown"); + + + colsNum = colsNum + 1; + current->next = malloc(sizeof(ODBC_FIELDS)); + current = (ODBC_FIELDS *) current->next; + } + + current = fieldstr; + GB.NewArray(fields, sizeof(char *), colsNum); + + for (i = 0; i < colsNum; i++) + { + (*fields)[i] = GB.NewZeroString((char *) current->fieldname); + + current = (ODBC_FIELDS *) current->next; + free(fieldstr); + fieldstr = current; + } + free(fieldstr); + + SQLFreeHandle(SQL_HANDLE_STMT, statHandle); + + + + return colsNum; + +} + + +/***************************************************************************** + + field_info() + + Get field description + + is the database handle. +
    is the table name. + is the field name. + points to a structure filled by the function. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int field_info(DB_DATABASE *db, const char *table, const char *field, DB_FIELD * info) +{ +#ifdef ODBC_DEBUG_HEADER +fprintf(stderr,"[ODBC][%s][%d]\n",__FILE__,__LINE__); +fprintf(stderr,"\tfield_info\n"); +fflush(stderr); +#endif + SQLCHAR colname[32]; + SQLCHAR coltype[100]; + SQLCHAR precision[100]; + char query[200]; + SQLHSTMT statHandle; + SQLHSTMT statHandle1; + SQLRETURN retcode; + //SQLRETURN V_OD_erg; + SQLLEN auton=SQL_FALSE; + int i; + + ODBC_CONN *han = (ODBC_CONN *)db->handle; + ODBC_CONN *han1 = (ODBC_CONN *)db->handle; + + *precision = 0; + + strncpy((char *)&query[0], "SELECT ",7); + strncpy((char *)&query[7], field,strlen(field)); + strncpy((char *)&query[strlen(field)+7], " FROM ",6); + strncpy((char *)&query[strlen(field)+13], table,strlen(table)); + query[strlen(field)+14+strlen(table)]='\0'; + strncpy((char *)&query[strlen(field)+13+strlen(table)],"\0\n\0\n",4); + + + for (i = 0; i < 100; i++) + coltype[i] = '\0'; + + retcode =SQLAllocHandle(SQL_HANDLE_STMT, (ODBC_CONN *) han->odbcHandle, &statHandle); + + if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) + { + return retcode; + } + + retcode =SQLAllocHandle(SQL_HANDLE_STMT, (ODBC_CONN *) han1->odbcHandle, &statHandle1); + + if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) + { + return retcode; + } + + retcode = SQLExecDirect(statHandle1, (SQLCHAR *) query , SQL_NTS); + if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) + { + return retcode; + } + + retcode=SQLColAttribute (statHandle1,1,SQL_DESC_AUTO_UNIQUE_VALUE,NULL,0,NULL,&auton); + + SQLFreeHandle(SQL_HANDLE_STMT, statHandle1); + + if (!SQL_SUCCEEDED(retcode = SQLColumns(statHandle, NULL, 0, NULL, 0, (SQLCHAR *) table, SQL_NTS, NULL,0))) + return -1; + + + + while (SQL_SUCCEEDED(SQLFetch(statHandle))) + { + SQLGetData(statHandle, SQLColumns_COLUMN_NAME, SQL_C_CHAR, colname, sizeof(colname), 0); + + + if (strcmp((char *) colname, field) == 0) + { + SQL_SUCCEEDED(SQLGetData(statHandle, SQLColumns_SQL_DATA_TYPE, SQL_C_CHAR, coltype, sizeof(coltype), 0)); + SQL_SUCCEEDED(SQLGetData(statHandle, SQLColumns_COLUMN_SIZE, SQL_C_CHAR, precision, sizeof(precision), 0)); + + + break; + } + + } + + info->name = NULL; + info->type = conv_type(atol((char *)coltype)); + info->length = 0; + if (*precision) + info->length = atol((char *) precision); + + if (info->type == GB_T_STRING) + { + if (info->length < 0) + info->length = 0; + } + + if (auton == SQL_TRUE) + info->type = DB_T_SERIAL; + + info->def.type = GB_T_NULL; + + info->collation = NULL; + + SQLFreeHandle(SQL_HANDLE_STMT, statHandle); + + return FALSE; +} + + +/***************************************************************************** + + index_exist() + + Returns if an index exists in a given table + + is the database handle. +
    is the table name. + is the index name. + + This function returns TRUE if the index exists, and FALSE if not. + +*****************************************************************************/ + +static int index_exist(DB_DATABASE *db, const char *table, const char *index) +{ + //GB.Error("ODBC does not implement this function - index_exist"); + return FALSE; +} + + +/***************************************************************************** + + index_list() + + Returns an array containing the name of each index in a given table + + is the database handle. +
    is the table name. + points to a variable that will receive the char* array. + + This function returns the number of indexes, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int index_list(DB_DATABASE *db, const char *table, char ***indexes) +{ + //GB.Error("ODBC does not implement this function - index_list"); + return (-1); +} + + +/***************************************************************************** + + index_info() + + Get index description + + is the database handle. +
    is the table name. + is the index name. + points to a structure filled by the function. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int index_info(DB_DATABASE *db, const char *table, const char *index, DB_INDEX * info) +{ +//GB.Error("ODBC does not implement this function"); + return TRUE; +} + + +/***************************************************************************** + + index_delete() + + Deletes an index. + + is the database handle. +
    is the table name. + is the index name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int index_delete(DB_DATABASE *db, const char *table, const char *index) +{ +//GB.Error("ODBC does not implement this function"); + return TRUE; +} + + +/***************************************************************************** + + index_create() + + Creates an index. + + is the database handle. +
    is the table name. + is the index name. + points to a structure describing the index. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int index_create(DB_DATABASE *db, const char *table, const char *index, DB_INDEX * info) +{ +//GB.Error("ODBC does not implement this function"); + return TRUE; +} + + +/***************************************************************************** + + database_exist() + + Returns if a database exists + + is any database handle. + is the database name. + + This function returns TRUE if the database exists, and FALSE if not. + +*****************************************************************************/ + +static int database_exist(DB_DATABASE *db, const char *name) +{ + //GB.Error("ODBC does not implement this function"); + ODBC_CONN *han = (ODBC_CONN *)db->handle; + + if (strcmp(han->dsn_name, name) == 0) + { + + return TRUE; + } + return FALSE; +} + + + +/***************************************************************************** + + database_list() + + Returns an array containing the name of each database + + is any database handle. + points to a variable that will receive the char* array. + + This function returns the number of databases, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int database_list(DB_DATABASE *db, char ***databases) +{ + ODBC_CONN *han = (ODBC_CONN *)db->handle; + +//GB.Error("ODBC does not implement this function"); + GB.NewArray(databases, sizeof(char *), 1); + (*databases)[0] = GB.NewZeroString(han->dsn_name); + + return (1); +} + + +/***************************************************************************** + + database_is_system() + + Returns if a database is a system database. + + is any database handle. + is the database name. + + This function returns TRUE if the database is a system database, and + FALSE if not. + +*****************************************************************************/ + +static int database_is_system(DB_DATABASE *db, const char *name) +{ + //GB.Error("ODBC does not implement this function"); + return FALSE; +} + +/***************************************************************************** + + table_type() + Not Valid in postgresql + + is the database handle. +
    is the table name. + +*****************************************************************************/ + +static char *table_type(DB_DATABASE *db, const char *table, const char *type) +{ + if (type) + GB.Error("ODBC does not have any table types"); + return NULL; +} + +/***************************************************************************** + + database_delete() + + Deletes a database. + + is the database handle. + is the database name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int database_delete(DB_DATABASE *db, const char *name) +{ + //GB.Error("ODBC does not implement this function"); + return TRUE; +} + + +/***************************************************************************** + + database_create() + + Creates a database. + + is the database handle. + is the database name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int database_create(DB_DATABASE *db, const char *name) +{ + //GB.Error("ODBC does not implement this function"); + return TRUE; +} + + +/***************************************************************************** + + user_exist() + + Returns if a user exists. + + is any database handle. + is the user name. + + This function returns TRUE if the user exists, and FALSE if not. + +*****************************************************************************/ + +static int user_exist(DB_DATABASE *db, const char *name) +{ + ODBC_CONN *han = (ODBC_CONN *)db->handle; + + return strcmp(han->user_name, name) == 0; + // GB.Error("ODBC does not implement this function"); +} + + +/***************************************************************************** + + user_list() + + Returns an array containing the name of each user. + + is the database handle. + points to a variable that will receive the char* array. + + This function returns the number of users, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int user_list(DB_DATABASE *db, char ***users) +{ + ODBC_CONN *han = (ODBC_CONN *)db->handle; + + if (users) + { + GB.NewArray(users, sizeof(char *), 1); + (*users)[0] = GB.NewZeroString(han->user_name); + } + + //GB.Error("ODBC does not implement this function"); + return (1); +} + + +/***************************************************************************** + + user_info() + + Get user description + + is the database handle. + is the user name. + points to a structure filled by the function. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int user_info(DB_DATABASE *db, const char *name, DB_USER *info) +{ + //GB.Error("ODBC does not implement this function"); + return TRUE; +} + + +/***************************************************************************** + + user_delete() + + Deletes a user. + + is any database handle. + is the user name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int user_delete(DB_DATABASE *db, const char *name) +{ + //GB.Error("ODBC can't delete users"); + return TRUE; +} + + +/***************************************************************************** + + user_create() + + Creates a user. + + is the database handle. + is the user name. + points to a structure describing the user. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int user_create(DB_DATABASE *db, const char *name, DB_USER * info) +{ + //GB.Error("ODBC can't create users"); + return TRUE; +} + + +/***************************************************************************** + + user_set_password() + + Change the user password. + + is the database handle. + is the user name. + is the new password + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int user_set_password(DB_DATABASE *db, const char *name, const char *password) +{ + //GB.Error("ODBC can't set user's password"); + return TRUE; +} + +/***************************************************************************** + + The driver interface + +*****************************************************************************/ + +DECLARE_DRIVER(_driver, "odbc"); + +/***************************************************************************** + + The component entry and exit functions. + +*****************************************************************************/ + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.db", DB_INTERFACE_VERSION, &DB); + DB.Register(&_driver); + + return -1; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.db.odbc/src/main.h b/gb.db.odbc/src/main.h new file mode 100644 index 00000000..618d18b4 --- /dev/null +++ b/gb.db.odbc/src/main.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "../gb.db.h" + +#ifndef __MAIN_C +extern "C" GB_INTERFACE GB; +extern "C" DB_INTERFACE DB; +#endif + +#define QUOTE_STRING "" + +#endif /* __MAIN_H */ diff --git a/gb.db.postgresql/AUTHORS b/gb.db.postgresql/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.db.postgresql/COPYING b/gb.db.postgresql/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.db.postgresql/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.db.postgresql/ChangeLog b/gb.db.postgresql/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.db.postgresql/INSTALL b/gb.db.postgresql/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.db.postgresql/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.db.postgresql/Makefile.am b/gb.db.postgresql/Makefile.am new file mode 100644 index 00000000..1cba6627 --- /dev/null +++ b/gb.db.postgresql/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @POSTGRESQL_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.db.postgresql/NEWS b/gb.db.postgresql/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.db.postgresql/README b/gb.db.postgresql/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.db.postgresql/acinclude.m4 b/gb.db.postgresql/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.db.postgresql/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.db.postgresql/component.am b/gb.db.postgresql/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.db.postgresql/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.db.postgresql/configure.ac b/gb.db.postgresql/configure.ac new file mode 100644 index 00000000..658c1363 --- /dev/null +++ b/gb.db.postgresql/configure.ac @@ -0,0 +1,27 @@ +dnl ---- configure.ac for gb.db.postgresql driver + +dnl ---- Initialization + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-db-postgresql, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.db.postgresql) +AC_PROG_LIBTOOL + +dnl ---- PostgreSQL driver + +GB_COMPONENT( + postgresql, POSTGRESQL, gb.db.postgresql, [src], + [GB_FIND(libpq-fe.h postgres.h pg_type.h, $prefix /usr/local/lib /usr/local /opt /usr/lib /usr, include/pgsql* pgsql*/include include/postgresql* postgresql*/include include/postgresql/*/server/catalog include/postgresql/*/server include)], + [GB_FIND(libpq.$SHLIBEXT, $prefix /usr/local /opt /usr, lib pgsql*/lib postgresql*/lib)], + [$C_LIB -lpq]) + +dnl ---- Create makefiles + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.db.postgresql/gambas.h b/gb.db.postgresql/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.db.postgresql/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.db.postgresql/gb.db.h b/gb.db.postgresql/gb.db.h new file mode 120000 index 00000000..563bd00c --- /dev/null +++ b/gb.db.postgresql/gb.db.h @@ -0,0 +1 @@ +../main/lib/db/gb.db.h \ No newline at end of file diff --git a/gb.db.postgresql/gb.db.proto.h b/gb.db.postgresql/gb.db.proto.h new file mode 120000 index 00000000..0a8e6920 --- /dev/null +++ b/gb.db.postgresql/gb.db.proto.h @@ -0,0 +1 @@ +../main/lib/db/gb.db.proto.h \ No newline at end of file diff --git a/gb.db.postgresql/gb_common.h b/gb.db.postgresql/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.db.postgresql/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.db.postgresql/m4 b/gb.db.postgresql/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.db.postgresql/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.db.postgresql/reconf b/gb.db.postgresql/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.db.postgresql/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.db.postgresql/src/Makefile.am b/gb.db.postgresql/src/Makefile.am new file mode 100644 index 00000000..08cb599c --- /dev/null +++ b/gb.db.postgresql/src/Makefile.am @@ -0,0 +1,11 @@ +COMPONENT = gb.db.postgresql +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.db.postgresql.la + +gb_db_postgresql_la_LIBADD = @POSTGRESQL_LIB@ +gb_db_postgresql_la_LDFLAGS = -module @LD_FLAGS@ @POSTGRESQL_LDFLAGS@ +gb_db_postgresql_la_CPPFLAGS = @POSTGRESQL_INC@ + +gb_db_postgresql_la_SOURCES = \ + main.h main.c diff --git a/gb.db.postgresql/src/gb.db.postgresql.component b/gb.db.postgresql/src/gb.db.postgresql.component new file mode 100644 index 00000000..193b278d --- /dev/null +++ b/gb.db.postgresql/src/gb.db.postgresql.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.db.postgresql +Author=Benoît Minisini +State=0 +Require=gb.db + diff --git a/gb.db.postgresql/src/main.c b/gb.db.postgresql/src/main.c new file mode 100644 index 00000000..50a349c9 --- /dev/null +++ b/gb.db.postgresql/src/main.c @@ -0,0 +1,2982 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + (c) 2011-2012 Bruce Bruen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include + +#include +#include +#include + +#ifdef PACKAGE_NAME + #undef PACKAGE_NAME + #undef PACKAGE_BUGREPORT + #undef PACKAGE_STRING + #undef PACKAGE_TARNAME + #undef PACKAGE_VERSION + #undef PACKAGE_URL +#endif + +#ifdef Max + #undef Max +#endif + +#ifdef Min + #undef Min +#endif + +#include "gb.db.proto.h" +#include "main.h" + + +GB_INTERFACE GB EXPORT; +DB_INTERFACE DB EXPORT; + + +static char _buffer[32]; +static DB_DRIVER _driver; +static int _last_error; +/*static int _print_query = FALSE;*/ + +/* Internal function to check the result of a query */ + +static int check_result(PGresult *res, const char *err) +{ + _last_error = 0; + + if (!res) + { + GB.Error("Out of memory"); + return TRUE; + } + + _last_error = PQresultStatus(res); + switch (_last_error) + { + case PGRES_COMMAND_OK: + case PGRES_TUPLES_OK: + return FALSE; + + default: + if (err) + GB.Error(err, PQresultErrorMessage(res)); + PQclear(res); + return TRUE; + } +} + + +/* internal function to quote a value stored as a string */ + +static void quote_string(const char *data, int len, DB_FORMAT_CALLBACK add) +{ + int i; + unsigned char c; + char buffer[8]; + + if (DB.GetCurrentDatabase()->version >= 80200) + (*add)("E", 1); + + (*add)("'", 1); + for (i = 0; i < len; i++) + { + c = (unsigned char)data[i]; + if (c == '\\') + (*add)("\\\\", 2); + else if (c == '\'') + (*add)("''", 2); + else if (c < 32 || c > 127) + { + buffer[0] = '\\'; + buffer[1] = '0' + ((c >> 6) & 0x7); + buffer[2] = '0' + ((c >> 3) & 0x7); + buffer[3] = '0' + (c & 0x7); + (*add)(buffer, 4); + } + else + (*add)((const char *)&c, 1); + } + (*add)("'", 1); +} + +/* Quote a string and returns the result as a temporary string */ + +static char *get_quote_string(const char *str, int len, char quote) +{ + char *res, *p, c; + int len_res; + int i; + + len_res = len; + for (i = 0; i < len; i++) + { + c = str[i]; + if (c == quote || c == '\\' || c == 0) + len_res++; + } + + res = GB.TempString(NULL, len_res); + + p = res; + for (i = 0; i < len; i++) + { + c = str[i]; + if (c == '\\' || c == quote) + { + *p++ = c; + *p++ = c; + } + /*else if (c == 0) + { + *p++ = '\\'; + *p++ = '0'; + }*/ + else + *p++ = c; + } + *p = 0; + + return res; +} + +/* Quote a table name so that schema work, and return a temporary string */ + +static char *get_quoted_table(const char *table) +{ + int len; + char *point; + char *res; + + if (!table || !*table) + return ""; + + len = strlen(table); + point = index(table, '.'); + + if (!point) + { + res = GB.TempString(NULL, len + 2); + sprintf(res, "\"%s\"", table); + } + else + { + res = GB.TempString(NULL, len + 4); + sprintf(res, "\"%.*s\".\"%s\"", (int)(point - table), table, point + 1); + } + + return res; +} + +static bool get_table_schema(const char **table, char **schema) +{ + char *point; + int len; + + //fprintf(stderr, "get_table_schema: %s\n", *table); + + *schema = NULL; + + if (!*table || !**table) + { + //fprintf(stderr, "get_table_schema: -> NULL\n"); + return TRUE; + } + + point = strchr(*table, '.'); + if (!point) + { + *schema = "public"; + } + else + { + len = point - *table; + if (len >= 3 && **table == '"' && (*table)[len - 1] == '"') + *schema = GB.TempString(*table + 1, len - 2); + else + *schema = GB.TempString(*table, len); + + *table = point + 1; + } + + return FALSE; +} + +/* internal function to quote a value stored as a blob */ + +static void quote_blob(const char *data, int len, DB_FORMAT_CALLBACK add) +{ + int i; + unsigned char c; + char buffer[8]; + + if (DB.GetCurrentDatabase()->version >= 80200) + (*add)("E", 1); + + (*add)("'", 1); + for (i = 0; i < len; i++) + { + c = (unsigned char)data[i]; + if (c == '\\') + (*add)("\\\\\\\\", 4); + else if (c == '\'') + (*add)("\\'", 2); + else if (c < 32 || c > 127) + { + buffer[0] = '\\'; + buffer[1] = '\\'; + buffer[2] = '0' + ((c >> 6) & 0x7); + buffer[3] = '0' + ((c >> 3) & 0x7); + buffer[4] = '0' + (c & 0x7); + (*add)(buffer, 5); + } + else + (*add)((const char *)&c, 1); + } + (*add)("'", 1); +} + +/* internal function to unquote a value stored as a string */ + +static int unquote_string(const char *data, int len, DB_FORMAT_CALLBACK add) +{ + int i; + char c; + + if (!data || *data != '\'') + return TRUE; + + for (i = 1;; i++) + { + c = data[i]; + if (c == '\'') + break; + if (c == '\\') + i++; + (*add)(&data[i], 1); + } + + return FALSE; +} + +/* internal function to unquote a value stored as a blob */ + +static int unquote_blob(const char *data, int len, DB_FORMAT_CALLBACK add) +{ + int i; + char c; + + //if (!data || *data != '\'') + // return TRUE; + + for (i = 0; i < len; i++) + { + c = data[i]; + if (c == '\\') + { + i++; + c = data[i]; + if (c >= '0' && c <= '9' && i < (len - 2)) + { + c = ((data[i] - '0') << 6) + ((data[i + 1] - '0') << 3) + (data[i + 2] - '0'); + i += 2; + (*add)(&c, 1); + continue; + } + } + (*add)(&data[i], 1); + } + + return FALSE; +} + +/* Internal function to convert a database type into a Gambas type */ + +static GB_TYPE conv_type(Oid type) +{ + switch(type) + { + case BOOLOID: + return GB_T_BOOLEAN; + + case INT2OID: + case INT4OID: + return GB_T_INTEGER; + + case INT8OID: + return GB_T_LONG; + + case NUMERICOID: + case FLOAT4OID: + case FLOAT8OID: + return GB_T_FLOAT; + + case ABSTIMEOID: + case RELTIMEOID: + case DATEOID: + case TIMEOID: + case TIMESTAMPOID: +#ifdef DATETIMEOID + case DATETIMEOID: +#endif +#ifdef TIMESTAMPTZOID + case TIMESTAMPTZOID: +#endif + return GB_T_DATE; + + case BYTEAOID: + return DB_T_BLOB; + + case CHAROID: + case BPCHAROID: + case VARCHAROID: + case TEXTOID: + case NAMEOID: + case CASHOID: + default: + return GB_T_STRING; + + } +} + + +/* Internal function to convert a database boolean value */ + +static int conv_boolean(const char *data) +{ + return strcasecmp(data, "t") == 0 || strcasecmp(data, "'t'") == 0; +} + +/* Internal function to convert a database value into a Gambas variant value */ + +static void conv_data(const char *data, int len, GB_VARIANT_VALUE *val, Oid type) +{ + GB_VALUE conv; + GB_DATE_SERIAL date; + double sec; + bool bc; + + switch (type) + { + case BOOLOID: + + val->type = GB_T_BOOLEAN; + val->value._boolean = conv_boolean(data) ? -1 : 0; + break; + + case INT2OID: + case INT4OID: + + GB.NumberFromString(GB_NB_READ_INTEGER, data, strlen(data), &conv); + + val->type = GB_T_INTEGER; + val->value._integer = conv._integer.value; + + break; + + case INT8OID: + + GB.NumberFromString(GB_NB_READ_LONG, data, strlen(data), &conv); + + val->type = GB_T_LONG; + val->value._long = conv._long.value; + + break; + + case NUMERICOID: + case FLOAT4OID: + case FLOAT8OID: + + GB.NumberFromString(GB_NB_READ_FLOAT, data, strlen(data), &conv); + + val->type = GB_T_FLOAT; + val->value._float = conv._float.value; + + break; + + case ABSTIMEOID: + case RELTIMEOID: + case DATEOID: + case TIMEOID: + case TIMESTAMPOID: + #ifdef DATETIMEOID + case DATETIMEOID: + #endif + #ifdef TIMESTAMPTZOID + case TIMESTAMPTZOID: + #endif + + memset(&date, 0, sizeof(date)); + + if (len > 3 && strcmp(&data[len - 2], "BC") == 0) + bc = TRUE; + else + bc = FALSE; + + switch(type) + { + case ABSTIMEOID: + case RELTIMEOID: + case DATEOID: + + sscanf(data, "%4d-%2d-%2d", &date.year, &date.month, &date.day); + break; + + case TIMEOID: + + sscanf(data, "%2d:%2d:%lf", &date.hour, &date.min, &sec); + date.sec = (short)sec; + date.msec = (short)((sec - date.sec) * 1000 + 0.5); + break; + + case TIMESTAMPOID: + #ifdef DATETIMEOID + case DATETIMEOID: + #endif + #ifdef TIMESTAMPTZOID + case TIMESTAMPTZOID: + #endif + + sscanf(data, "%4d-%2d-%2d %2d:%2d:%lf", &date.year, &date.month, &date.day, &date.hour, &date.min, &sec); + date.sec = (short)sec; + date.msec = (short)((sec - date.sec) * 1000 + 0.5); + break; + } + + if (bc) + date.year = (-date.year); + + // 4713-01-01 BC is used for null dates + + if (date.year == -4713 && date.month == 1 && date.day == 1) + date.year = date.month = date.day = 0; + + GB.MakeDate(&date, (GB_DATE *)&conv); + + val->type = GB_T_DATE; + val->value._date.date = conv._date.value.date; + val->value._date.time = conv._date.value.time; + + break; + + case BYTEAOID: + // The BLOB are read by the blob_read() driver function + // You must set NULL there. + val->type = GB_T_NULL; + break; + + case CHAROID: + case BPCHAROID: + case VARCHAROID: + case TEXTOID: + case NAMEOID: + case CASHOID: + default: + + val->type = GB_T_CSTRING; + val->value._string = (char *)data; + //val->_string.len = len; + + break; + } + +} + + +/* Internal function to substitute the table name into a query */ + +static char *query_param[3]; + +static void query_get_param(int index, char **str, int *len, char quote) +{ + if (index > 3) + return; + + index--; + *str = query_param[index]; + *len = strlen(*str); + + if (quote == '\'') + { + *str = get_quote_string(*str, *len, quote); + *len = GB.StringLength(*str); + } +} + +/* Internal function to run a query */ + +static int do_query(DB_DATABASE *db, const char *error, PGresult **pres, const char *qtemp, int nsubst, ...) +{ + PGconn *conn = (PGconn *)db->handle; + va_list args; + int i; + const char *query; + PGresult *res; + int ret; + + if (nsubst) + { + va_start(args, nsubst); + if (nsubst > 3) + nsubst = 3; + for (i = 0; i < nsubst; i++) + query_param[i] = va_arg(args, char *); + + query = DB.SubstString(qtemp, 0, query_get_param); + } + else + query = qtemp; + + if (DB.IsDebug()) + { + fprintf(stderr, "gb.db.postgresql: %p: %s\n", conn, query); + fflush(stderr); + } + + res = PQexec(conn, query); + ret = check_result(res, error); + if (!ret) + { + if (pres) + *pres = res; + else + PQclear(res); + } + + db->error = _last_error; + return ret; +} + +/* Internal function to check database version number */ + +static int db_version(DB_DATABASE *db) +{ + //Check db version + const char *vquery = + "select substring(version(),12,5)"; + int dbversion =0; + PGresult *res; + + if (!do_query(db, NULL, &res, vquery, 0)) + { + unsigned int verMain, verMajor, verMinor; + sscanf(PQgetvalue(res, 0, 0), "%2u.%2u.%2u", &verMain, &verMajor, &verMinor); + dbversion = ((verMain * 10000) + (verMajor * 100) + verMinor); + PQclear(res); + } + return dbversion; +} + +/* Internal function that fills field information from a schema request. + + The result columns must be ordered that way (from the specified column index): + - atttypid::int + - atttypmod + - attnotnull + - adsrc + - atthasdef +*/ + +static void fill_field_info(DB_DATABASE *db, DB_FIELD *info, PGresult *res, int row, int col) +{ + char *val; + Oid type; + GB_VARIANT def; + + info->name = NULL; + + type = atoi(PQgetvalue(res, row, col)); + info->type = conv_type(type); + + info->length = 0; + if (info->type == GB_T_STRING) + { + info->length = atoi(PQgetvalue(res, row, col + 1)); + if (info->length < 0) + info->length = 0; + else + info->length -= 4; + } + + info->def.type = GB_T_NULL; + + if (conv_boolean(PQgetvalue(res, row, col + 4)) && conv_boolean(PQgetvalue(res, row, col + 2))) + { + def.type = GB_T_VARIANT; + def.value.type = GB_T_NULL; + + val = PQgetvalue(res, row, col + 3); + if (val && *val) + { + if (strncmp(val, "nextval(", 8) == 0) + { + if (info->type == GB_T_LONG) + info->type = DB_T_SERIAL; + } + else + { + switch(info->type) + { + case GB_T_BOOLEAN: + def.value.type = GB_T_BOOLEAN; + def.value.value._boolean = (val[1] == 't'); + break; + + default: + + DB.Query.Init(); + if (!unquote_string(val, PQgetlength(res, row, col + 3), DB.Query.AddLength)) + val = DB.Query.Get(); + + conv_data(val, -1, &def.value, type); + } + + GB.StoreVariant(&def, &info->def); + } + } + } + + if (db->flags.no_collation) + info->collation = NULL; + else + info->collation = GB.NewZeroString(PQgetvalue(res, row, col + 5)); +} + + + +/***************************************************************************** + + get_quote() + + Returns the character used for quoting object names. + +*****************************************************************************/ + +static const char *get_quote(void) +{ + return QUOTE_STRING; +} + +/***************************************************************************** + + open_database() + + Connect to a database. + + points at a structure describing each connection parameter. + points at the DB_DATABASE structure that must be initialized. + + This function must return TRUE if the connection has failed. + + The name of the database can be NULL, meaning a default database. + +*****************************************************************************/ + +static int open_database(DB_DESC *desc, DB_DATABASE *db) +{ + const char *query = + "show client_encoding"; + + PGconn *conn; + PGresult *res; + int status; + char *name; + char dbname[512]; + + if (desc->name) + name = desc->name; + else + name = "template1"; + + if (snprintf(dbname, sizeof(dbname), "dbname='%s' connect_timeout=%d", get_quote_string(name, strlen(name), '\''), db->timeout) >= sizeof(dbname)) + { + GB.Error("Cannot open database: database name too long"); + return TRUE; + } + + //fprintf(stderr, "gb.db.postgresql: host = `%s` port = `%s` dbnname = `%s` user = `%s` password = `%s`\n", desc->host, desc->port, dbname, desc->user, desc->password); + + conn = PQsetdbLogin(desc->host, desc->port, NULL, NULL, dbname, desc->user, desc->password); + + if (!conn) + { + GB.Error("Out of memory"); + return TRUE; + } + + if (PQstatus(conn) == CONNECTION_BAD) + { + GB.Error("Cannot open database: &1", PQerrorMessage(conn)); + PQfinish(conn); + return TRUE; + } + + res = PQexec(conn, "set datestyle=ISO"); + status = PQresultStatus(res); + + if (status != PGRES_COMMAND_OK) + { + GB.Error("Cannot set 'datestyle' to 'ISO': &1", PQerrorMessage(conn)); + PQclear(res); + PQfinish(conn); + return TRUE; + } + + /* get version */ + + db->handle = conn; + db->version = db_version(db); + db->data = (void *)0; // transaction level + + if (db->version >= 90000) + { + res = PQexec(conn, "set bytea_output=escape"); + status = PQresultStatus(res); + + if (status != PGRES_COMMAND_OK) + { + GB.Error("Cannot set 'bytea_output' to 'escape': &1", PQerrorMessage(conn)); + PQclear(res); + PQfinish(conn); + return TRUE; + } + } + + /* flags */ + + db->flags.no_table_type = TRUE; + //db->flags.no_nest = TRUE; + //db->flags.no_case = TRUE; + db->flags.schema = TRUE; + db->flags.no_collation = db->version < 90100; + + /* encoding */ + + if (PQsetClientEncoding(conn, GB.System.Charset())) + fprintf(stderr, "gb.db.postgresql: warning: cannot set encoding to %s\n", GB.System.Charset()); + + if (!do_query(db, NULL, &res, query, 0)) + { + db->charset = GB.NewZeroString(PQgetvalue(res, 0, 0)); + PQclear(res); + } + else + db->charset = NULL; + + return FALSE; +} + +/***************************************************************************** + + close_database() + + Terminates the database connection. + + contains the database handle. + +*****************************************************************************/ + +static void close_database(DB_DATABASE *db) +{ + PGconn *conn = (PGconn *)db->handle; + + if (conn) + PQfinish(conn); +} + + +/***************************************************************************** + + get_collations() + + Return the available collations as a Gambas string array. + +*****************************************************************************/ + +static GB_ARRAY get_collations(DB_DATABASE *db) +{ + const char *query = + "select collname from pg_collation;"; + + GB_ARRAY array; + PGresult *res; + int i; + + if (db->flags.no_collation) + return NULL; + + if (do_query(db, "Unable to get collations: &1", &res, query, 0)) + return NULL; + + GB.Array.New(&array, GB_T_STRING, PQntuples(res)); + + for (i = 0; i < PQntuples(res); i++) + *((char **)GB.Array.Get(array, i)) = GB.NewZeroString(PQgetvalue(res, i, 0)); + + PQclear(res); + + return array; +} + +/***************************************************************************** + + format_value() + + This function transforms a gambas value into a string value that can + be inserted into a SQL query. + + points to the value. + is a callback called to insert the string into the query. + + This function must return TRUE if it translates the value, and FALSE if + it does not. + + If the value is not translated, then a default translation is used. + +*****************************************************************************/ + +static int format_value(GB_VALUE *arg, DB_FORMAT_CALLBACK add) +{ + int l; + GB_DATE_SERIAL *date; + bool bc; + + switch (arg->type) + { + case GB_T_BOOLEAN: + + if (VALUE((GB_BOOLEAN *)arg)) + add("TRUE", 4); + else + add("FALSE", 5); + + return TRUE; + + case GB_T_STRING: + case GB_T_CSTRING: + + quote_string(VALUE((GB_STRING *)arg).addr + VALUE((GB_STRING *)arg).start, VALUE((GB_STRING *)arg).len, add); + return TRUE; + + case GB_T_DATE: + + date = GB.SplitDate((GB_DATE *)arg); + + // Gambas year is between -4801 and +9999 + // PostgreSQL year is between -4713 and +294276 + // So let's use -4713 for representing null dates. + + if (date->year == 0) + { + l = sprintf(_buffer, "'4713-01-01 %02d:%02d:%02d BC'", date->hour, date->min, date->sec); + add(_buffer, l); + } + else + { + bc = date->year < 0; + + l = sprintf(_buffer, "'%04d-%02d-%02d %02d:%02d:%02d", + abs(date->year), date->month, date->day, + date->hour, date->min, date->sec); + + add(_buffer, l); + + if (date->msec) + { + l = sprintf(_buffer, ".%03d", date->msec); + add(_buffer, l); + } + + if (bc) + add(" BC", 3); + + add("'", 1); + } + + return TRUE; + + default: + return FALSE; + } +} + + +/***************************************************************************** + + format_blob() + + This function transforms a blob value into a string value that can + be inserted into a SQL query. + + points to the DB_BLOB structure. + is a callback called to insert the string into the query. + +*****************************************************************************/ + +static void format_blob(DB_BLOB *blob, DB_FORMAT_CALLBACK add) +{ + quote_blob(blob->data, blob->length, add); +} + + +/***************************************************************************** + + exec_query() + + Send a query to the server and gets the result. + + is the database handle, as returned by open_database() + is the query string. + will receive the result handle of the query. + is an error message used when the query failed. + + can be NULL, when we don't care getting the result. + +*****************************************************************************/ + +static int exec_query(DB_DATABASE *db, const char *query, DB_RESULT *result, const char *err) +{ + return do_query(db, err, (PGresult **)result, query, 0); +} + + +/***************************************************************************** + + get_last_insert_id() + + Return the value of the last serial field used in an INSERT statement + + is the database handle, as returned by open_database() + +*****************************************************************************/ + +static int64_t get_last_insert_id(DB_DATABASE *db) +{ + PGresult *res; + + if (do_query(db, "Unable to retrieve last insert id: &1", &res, "select lastval()", 0)) + return -1; + + return atoll(PQgetvalue(res, 0, 0)); +} + + +/***************************************************************************** + + query_init() + + Initialize an info structure from a query result. + + is the handle of the query result. + points to the info structure. + will receive the number of records returned by the query. + + This function must initialize the info->nfield field with the number of + field in the query result. + +*****************************************************************************/ + +static void query_init(DB_RESULT result, DB_INFO *info, int *count) +{ + PGresult *res = (PGresult *)result; + + *count = PQntuples(res); + + info->nfield = PQnfields(res); +} + + +/***************************************************************************** + + query_release() + + Free the info structure filled by query_init() and the result handle. + + is the handle of the query result. + points to the info structure. + +*****************************************************************************/ + +static void query_release(DB_RESULT result, DB_INFO *info) +{ + PQclear((PGresult *)result); +} + + +/***************************************************************************** + + query_fill() + + Fill a result buffer with the value of each field of a record. + + is the database handle, as returned by open_database() + is the handle of the result. + is the index of the record in the result. + points to an array having one element for each field in the + result. + is a boolean telling if we want the next row. + + This function must return DB_OK, DB_ERROR or DB_NO_DATA + + This function must use GB.StoreVariant() to store the value in the + buffer. + +*****************************************************************************/ + +static int query_fill(DB_DATABASE *db, DB_RESULT result, int pos, GB_VARIANT_VALUE *buffer, int next) +{ + PGresult *res = (PGresult *)result; + int i; + char *data; + GB_VARIANT value; + + for (i = 0; i < PQnfields(res); i++) + { + data = PQgetvalue(res, pos, i); + + value.type = GB_T_VARIANT; + value.value.type = GB_T_NULL; + + if (!PQgetisnull(res, pos, i)) + conv_data(data, PQgetlength(res, pos, i), &value.value, PQftype(res, i)); + + GB.StoreVariant(&value, &buffer[i]); + } + + return DB_OK; +} + + +/***************************************************************************** + + blob_read() + + Returns the value of a BLOB field. + + is the handle of the result. + is the index of the record in the result. + points at a DB_BLOB structure that will receive a pointer to the + data and its length. + +*****************************************************************************/ + +static void blob_read(DB_RESULT result, int pos, int field, DB_BLOB *blob) +{ + PGresult *res = (PGresult *)result; + char *data; + int len; + + data = PQgetvalue(res, pos, field); + len = PQgetlength(res, pos, field); + + DB.Query.Init(); + if (!unquote_blob(data, len, DB.Query.AddLength)) + { + len = DB.Query.Length(); + data = DB.Query.GetNew(); + } + else + blob->constant = TRUE; + + blob->data = data; + blob->length = len; +} + + +/***************************************************************************** + + field_name() + + Return the name of a field in a result from its index. + + is the result handle. + is the field index. + +*****************************************************************************/ + +static char *field_name(DB_RESULT result, int field) +{ + return PQfname((PGresult *)result, field); +} + + +/***************************************************************************** + + field_index() + + Return the index of a field in a result from its name. + + is the result handle. + is the field name. + is needed by this driver to enable table.field syntax + +*****************************************************************************/ + +static int field_index(DB_RESULT result, const char *name, DB_DATABASE *db) +{ + PGresult *res = (PGresult *)result; + + char *fld; + int index; + char *table = NULL; + int numfields, oid; + char *qfield = + "select oid from pg_class where relname = '&1' " + "and ((relnamespace not in (select oid from pg_namespace where nspname = 'information_schema')))"; + + numfields = PQnfields(res); + fld = strrchr(name, (int)FLD_SEP); + + if (fld) + { + //Includes table identity. + //This feature is only available for 7.4 onwards + /* check version */ + + if (db->version > 70399) + { // version 7.4.? + fld[0] = '.'; + table = GB.NewString(name, fld - name); + fld = fld + 1; + + /* Need to find the OID for the table */ + PGresult *oidres; + + if (do_query(db, "Unable to get OID for table &1", &oidres, qfield, 1, table)) + { + GB.FreeString(&table); + return -1; + } + + if ( PQntuples(oidres) != 1) + { + /* Not unique table identifier */ + GB.Error("Table &1 not unique in pg_class", table); + PQclear(oidres); + GB.FreeString(&table); + return -1; + } + + oid = atoi(PQgetvalue(oidres, 0, 0)); + PQclear(oidres); + index = PQfnumber(res, fld); + + if (PQftable(res, index) != oid) + { + while ( ++index < numfields) + { + if (strcasecmp(PQfname(res, index), fld) == 0) + { //Check Fieldname + if (PQftable(res, index) == oid) + { //check oid + break; // is the required table oid + } + } + } + + if ( index == numfields ) + { + /* field not found for OID */ + GB.Error("Field &1.&2 not found", table, fld); + GB.FreeString(&table); + return -1; + } + + } + + GB.FreeString(&table); + } + else + { + /* Using tablename and fieldname in a non supported version */ + GB.Error("Field &1.&2 not supported below 7.4.1", table, fld); + return -1; + } + } + else + { + for (index = 0; index < numfields; index++) + { + if (strcasecmp(PQfname(res, index), name) == 0) + break; + } + if (index >= numfields) + index = -1; + } + + return index; +} + + +/***************************************************************************** + + field_type() + + Return the Gambas type of a field in a result from its index. + + is the result handle. + is the field index. + +*****************************************************************************/ + +static GB_TYPE field_type(DB_RESULT result, int field) +{ + return conv_type(PQftype((PGresult *)result, field)); +} + + +/***************************************************************************** + + field_length() + + Return the length of a field in a result from its index. + + is the result handle. + is the field index. + +*****************************************************************************/ + +static int field_length(DB_RESULT result, int field) +{ + GB_TYPE type = conv_type(PQftype((PGresult *)result, field)); + int len; + + if (type != GB_T_STRING) + return 0; + + len = PQfmod((PGresult *)result, field); + if (len < 0) + len = 0; + else + len -= 4; + + return len; +} + + +/***************************************************************************** + + begin_transaction() + + Begin a transaction. + + is the database handle. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int begin_transaction(DB_DATABASE *db) +{ + int trans = (int)(intptr_t)(db->data) + 1; + db->data = (void *)(intptr_t)trans; + + if (trans == 1) + { + return do_query(db, "Unable to begin transaction: &1", NULL, "BEGIN", 0); + } + else + { + char buffer[8]; + sprintf(buffer, "%d", trans - 1); + return do_query(db, "Unable to begin transaction: Unable to define savepoint: &1", NULL, "SAVEPOINT t&1", 1, buffer); + } +} + + +/***************************************************************************** + + commi_transaction() + + Commit a transaction. + + is the database handle. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int commit_transaction(DB_DATABASE *db) +{ + int trans = (int)(intptr_t)(db->data) - 1; + db->data = (void *)(intptr_t)trans; + + if (trans == 0) + { + return do_query(db, "Unable to commit transaction: &1", NULL, "COMMIT", 0); + } + else + { + char buffer[8]; + sprintf(buffer, "%d", trans); + return do_query(db, "Unable to commit transaction: Unable to release savepoint: &1", NULL, "RELEASE SAVEPOINT t&1", 1, buffer); + } +} + + +/***************************************************************************** + + rollback_transaction() + + Rolllback a transaction. + + is the database handle. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int rollback_transaction(DB_DATABASE *db) +{ + int trans = (int)(intptr_t)(db->data) - 1; + db->data = (void *)(intptr_t)trans; + + if (trans == 0) + { + return do_query(db, "Unable to rollback transaction: &1", NULL, "ROLLBACK", 0); + } + else + { + char buffer[8]; + sprintf(buffer, "%d", trans); + return do_query(db, "Unable to begin transaction: &1", NULL, "ROLLBACK TO SAVEPOINT t&1", 1, buffer); + } +} + + +/***************************************************************************** + + table_init() + + Initialize an info structure from table fields. + + is the database handle. +
    is the table name. + points at the info structure. + + This function must initialize the following info fields: + - info->nfield must contain the number of fields in the table. + - info->fields is a char*[] pointing at the name of each field. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int field_info(DB_DATABASE *db, const char *table, const char *field, DB_FIELD *info); + +static int table_init(DB_DATABASE *db, const char *table, DB_INFO *info) +{ + const char *qfield_all; + char *qfield_schema_all; + + if (db->flags.no_collation) + { + qfield_all= + "SELECT col.attname, col.atttypid::int, col.atttypmod, " + "col.attnotnull, def.adsrc, col.atthasdef " + "FROM pg_catalog.pg_class tbl, pg_catalog.pg_attribute col " + "LEFT JOIN pg_catalog.pg_attrdef def ON (def.adnum = col.attnum AND def.adrelid = col.attrelid) " + "WHERE tbl.relname = '&1' AND " + "col.attrelid = tbl.oid AND " + "col.attnum > 0 AND " + "not col.attisdropped " + "ORDER BY col.attnum ASC;"; + + qfield_schema_all = + "select pg_attribute.attname, pg_attribute.atttypid::int, pg_attribute.atttypmod, " + "pg_attribute.attnotnull, pg_attrdef.adsrc, pg_attribute.atthasdef " + "from pg_class, pg_attribute " + "LEFT JOIN pg_catalog.pg_attrdef ON (pg_attrdef.adnum = pg_attribute.attnum AND pg_attrdef.adrelid = pg_attribute.attrelid) " + "where pg_class.relname = '&1' " + "and (pg_class.relnamespace in (select oid from pg_namespace where nspname = '&2')) " + "and pg_attribute.attnum > 0 and not pg_attribute.attisdropped " + "and pg_attribute.attrelid = pg_class.oid "; + } + else + { + qfield_all= + "SELECT col.attname, col.atttypid::int, col.atttypmod, " + "col.attnotnull, def.adsrc, col.atthasdef, pg_collation.collname " + "FROM pg_catalog.pg_class tbl, pg_catalog.pg_attribute col " + "LEFT JOIN pg_catalog.pg_attrdef def ON (def.adnum = col.attnum AND def.adrelid = col.attrelid) " + "LEFT JOIN pg_collation ON (pg_collation.oid = col.attcollation) " + "WHERE tbl.relname = '&1' AND " + "col.attrelid = tbl.oid AND " + "col.attnum > 0 AND " + "not col.attisdropped " + "ORDER BY col.attnum ASC;"; + + qfield_schema_all = + "select pg_attribute.attname, pg_attribute.atttypid::int, pg_attribute.atttypmod, " + "pg_attribute.attnotnull, pg_attrdef.adsrc, pg_attribute.atthasdef, pg_collation.collname " + "from pg_class, pg_attribute " + "LEFT JOIN pg_catalog.pg_attrdef ON (pg_attrdef.adnum = pg_attribute.attnum AND pg_attrdef.adrelid = pg_attribute.attrelid) " + "LEFT JOIN pg_collation ON (pg_collation.oid = pg_attribute.attcollation) " + "where pg_class.relname = '&1' " + "and (pg_class.relnamespace in (select oid from pg_namespace where nspname = '&2')) " + "and pg_attribute.attnum > 0 and not pg_attribute.attisdropped " + "and pg_attribute.attrelid = pg_class.oid "; + } + + PGresult *res; + int i, n; + DB_FIELD *f; + char *schema; + + // Table name + info->table = GB.NewZeroString(table); + + if (get_table_schema(&table, &schema)) + { + if (do_query(db,"Unable to get table fields: &1", &res, qfield_all, 1, table)) + return TRUE; + } + else + { + if (do_query(db, "Unable to get table fields: &1", &res, qfield_schema_all, 2, table, schema)) + return TRUE; + } + + info->nfield = n = PQntuples(res); + if (n == 0) + { + PQclear(res); + return TRUE; + } + + GB.Alloc(POINTER(&info->field), sizeof(DB_FIELD) * n); + + for (i = 0; i < n; i++) + { + f = &info->field[i]; + + fill_field_info(db, f, res, i, 1); + + f->name = GB.NewZeroString(PQgetvalue(res, i, 0)); + } + + PQclear(res); + return FALSE; +} + + +/***************************************************************************** + + table_index() + + Initialize an info structure from table primary index. + + is the database handle. +
    is the table name. + points at the info structure. + + This function must initialize the following info fields: + - info->nindex must contain the number of fields in the primary index. + - info->index is a int[] giving the index of each index field in + info->fields. + + This function must be called after table_init(). + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_index(DB_DATABASE *db, const char *table, DB_INFO *info) +{ + const char *qindex; + const char *qindex_schema; + + PGresult *res; + int i, j, n; + const char *fulltable = table; + char *schema; + + /* Index primaire */ + + if (db->version < 80200) + { + qindex = "select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname " + "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind " + "where pg_cl.relname = '&1_pkey' AND pg_cl.oid = pg_ind.indexrelid " + "and (pg_cl.relnamespace not in (select oid from pg_namespace where nspname = 'information_schema')) " + "and pg_att2.attrelid = pg_ind.indexrelid " + "and pg_att1.attrelid = pg_ind.indrelid " + "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] " + "order by pg_att2.attnum"; + qindex_schema = "select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname " + "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind " + "where pg_cl.relname = '&1_pkey' AND pg_cl.oid = pg_ind.indexrelid " + "and (pg_cl.relnamespace in (select oid from pg_namespace where nspname = '&2')) " + "and pg_att2.attrelid = pg_ind.indexrelid " + "and pg_att1.attrelid = pg_ind.indrelid " + "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] " + "order by pg_att2.attnum"; + } + else + { + qindex = "select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname " + "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind, pg_class pg_table " + "where pg_table.relname = '&1' AND pg_table.oid = pg_att1.attrelid AND pg_cl.oid = pg_ind.indexrelid " + "and (pg_cl.relnamespace not in (select oid from pg_namespace where nspname = 'information_schema')) " + "and pg_ind.indisprimary " + "and pg_att2.attrelid = pg_ind.indexrelid " + "and pg_att1.attrelid = pg_ind.indrelid " + "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] " + "order by pg_att2.attnum"; + qindex_schema = "select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname " + "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind, pg_class pg_table " + "where pg_table.relname = '&1' AND pg_table.oid = pg_att1.attrelid AND pg_cl.oid = pg_ind.indexrelid " + "and (pg_cl.relnamespace in (select oid from pg_namespace where nspname = '&2')) " + "and pg_ind.indisprimary " + "and pg_att2.attrelid = pg_ind.indexrelid " + "and pg_att1.attrelid = pg_ind.indrelid " + "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] " + "order by pg_att2.attnum"; + } + + if (get_table_schema(&table, &schema)) + { + if (do_query(db, "Unable to get primary index: &1", &res, qindex, 1, table)) + return TRUE; + } + else + { + if (do_query(db, "Unable to get primary index: &1", &res, qindex_schema, 2, table, schema)) + return TRUE; + } + + n = info->nindex = PQntuples(res); + + if (n <= 0) + { + GB.Error("Table '&1' has no primary index", fulltable); + PQclear(res); + return TRUE; + } + + GB.Alloc(POINTER(&info->index), sizeof(int) * n); + + for (i = 0; i < n; i++) + { + for (j = 0; j < info->nfield; j++) + { + if (strcmp(info->field[j].name, PQgetvalue(res, i, 0)) == 0) + { + info->index[i] = j; + /*printf("index[%d] = %d\n", i, j);*/ + break; + } + } + } + + PQclear(res); + + return FALSE; +} + + +/***************************************************************************** + + table_release() + + Free the info structure filled by table_init() and/or table_index() + + is the database handle. + points at the info structure. + +*****************************************************************************/ + +static void table_release(DB_DATABASE *db, DB_INFO *info) +{ + /* All is done outside the driver */ +} + + +/***************************************************************************** + + table_exist() + + Returns if a table exists + + is the database handle. +
    is the table name. + + This function returns TRUE if the table exists, and FALSE if not. + +*****************************************************************************/ + +static int table_exist(DB_DATABASE *db, const char *table) +{ + const char *query = + "select relname from pg_class where (relkind = 'r' or relkind = 'v'or relkind = 'm') " + "and (relname = '&1') " + "and (relnamespace not in (select oid from pg_namespace where nspname = 'information_schema'))"; + + const char *query_schema = + "select relname from pg_class where (relkind = 'r' or relkind = 'v' or relkind = 'm') " + "and (relname = '&1') " + "and (relnamespace in (select oid from pg_namespace where nspname = '&2'))"; + + /*"select pg_class.relname,pg_namespace.nspname from pg_class,pg_namespace where (pg_class.relkind = 'r' or pg_class.relkind = 'v') " + "and (pg_namespace.oid = pg_class.relnamespace) " + "and (pg_class.relname = '&1') " + "and (pg_namespace.nspname = '&2') " + "and (pg_namespace.oid not in (select oid from pg_namespace where nspname = 'information_schema'))";*/ + + PGresult *res; + int exist; + char *schema; + + if (get_table_schema(&table, &schema)) + { + if (do_query(db, "Unable to check table: &1", &res, query, 1, table)) + return FALSE; + } + else + { + if (do_query(db, "Unable to check table: &1", &res, query_schema, 2, table, schema)) + return FALSE; + } + + exist = PQntuples(res) == 1; + + PQclear(res); + + return exist; +} + + + +/***************************************************************************** + + table_list() + + Returns an array containing the name of each table in the database + + is the database handle. + points to a variable that will receive the char* array. + + This function returns the number of tables, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int table_list_73(DB_DATABASE *db, char ***tables) +{ + const char *query = + "select pg_class.relname,pg_namespace.nspname from pg_class,pg_namespace where (pg_class.relkind = 'r' or pg_class.relkind = 'v' or pg_class.relkind = 'm') " + "and (pg_namespace.oid = pg_class.relnamespace) " + "and (pg_namespace.oid not in (select oid from pg_namespace where nspname = 'information_schema'))"; + + PGresult *res; + int i; + int count; + char *schema; + + if (do_query(db, "Unable to get tables: &1", &res, query, 0)) + return -1; + + if (tables) + { + GB.NewArray(tables, sizeof(char *), PQntuples(res)); + + for (i = 0; i < PQntuples(res); i++) + { + schema = PQgetvalue(res, i, 1); + if (!strcmp(schema, "public")) // || !strcmp(schema, "pg_catalog")) + (*tables)[i] = GB.NewZeroString(PQgetvalue(res, i, 0)); + else + { + (*tables)[i] = GB.NewZeroString(schema); + (*tables)[i] = GB.AddChar((*tables)[i], '.'); + (*tables)[i] = GB.AddString((*tables)[i], PQgetvalue(res, i, 0), 0); + } + } + } + + count = PQntuples(res); + + PQclear(res); + + return count; +} + +#if 0 +static int table_list_74(DB_DATABASE handle, char ***tables) +{ + const char *query1 = + "select tablename from pg_tables where schemaname <> 'information_schema'"; + + const char *query2 = + "select viewname from pg_views where schemaname <> 'information_schema'"; + + PGconn *conn = (PGconn *)handle; + PGresult *res1, *res2; + int i, j; + int count; + + if (do_query(conn, "Unable to get tables: &1", &res1, query1, 0)) + return -1; + + if (do_query(conn, "Unable to get tables: &1", &res2, query2, 0)) + return -1; + + count = PQntuples(res1) + PQntuples(res2); + + if (tables) + { + GB.NewArray(tables, sizeof(char *), count); + + j = 0; + + for (i = 0; i < PQntuples(res1); i++, j++) + GB.NewString(&((*tables)[j]), PQgetvalue(res1, i, 0), 0); + + for (i = 0; i < PQntuples(res2); i++, j++) + GB.NewString(&((*tables)[j]), PQgetvalue(res2, i, 0), 0); + } + + PQclear(res1); + PQclear(res2); + + return count; +} +#endif + +static int table_list(DB_DATABASE *db, char ***tables) +{ + //if (version < 70400) + return table_list_73(db, tables); + //else + // return table_list_74(handle, tables); +} + + + +/***************************************************************************** + + table_primary_key() + + Returns a string representing the primary key of a table. + + is the database handle. +
    is the table name. + points to a string that will receive the primary key. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_primary_key(DB_DATABASE *db, const char *table, char ***primary) +{ + const char *query; + const char *query_schema; + PGresult *res; + int i; + char *schema; + + if (db->version < 80200) + { + query = "select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname " + "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind " + "where pg_cl.relname = '&1_pkey' AND pg_cl.oid = pg_ind.indexrelid " + "and (pg_cl.relnamespace not in (select oid from pg_namespace where nspname = 'information_schema')) " + "and pg_att2.attrelid = pg_ind.indexrelid " + "and pg_att1.attrelid = pg_ind.indrelid " + "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] " + "order by pg_att2.attnum"; + query_schema = "select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname " + "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind " + "where pg_cl.relname = '&1_pkey' AND pg_cl.oid = pg_ind.indexrelid " + "and (pg_cl.relnamespace in (select oid from pg_namespace where nspname = '&2')) " + "and pg_att2.attrelid = pg_ind.indexrelid " + "and pg_att1.attrelid = pg_ind.indrelid " + "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] " + "order by pg_att2.attnum"; + } + else + { + query = "select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname " + "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind, pg_class pg_table " + "where pg_table.relname = '&1' AND pg_table.oid = pg_att1.attrelid AND pg_cl.oid = pg_ind.indexrelid " + "and (pg_cl.relnamespace not in (select oid from pg_namespace where nspname = 'information_schema')) " + "and pg_ind.indisprimary " + "and pg_att2.attrelid = pg_ind.indexrelid " + "and pg_att1.attrelid = pg_ind.indrelid " + "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] " + "order by pg_att2.attnum"; + query_schema = "select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname " + "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind, pg_class pg_table " + "where pg_table.relname = '&1' AND pg_table.oid = pg_att1.attrelid AND pg_cl.oid = pg_ind.indexrelid " + "and (pg_cl.relnamespace in (select oid from pg_namespace where nspname = '&2')) " + "and pg_ind.indisprimary " + "and pg_att2.attrelid = pg_ind.indexrelid " + "and pg_att1.attrelid = pg_ind.indrelid " + "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] " + "order by pg_att2.attnum"; + } + + if (get_table_schema(&table, &schema)) + { + if (do_query(db, "Unable to get primary key: &1", &res, query, 1, table)) + return TRUE; + } + else + { + if (do_query(db, "Unable to get primary key: &1", &res, query_schema, 2, table, schema)) + return TRUE; + } + + GB.NewArray(primary, sizeof(char *), PQntuples(res)); + + for (i = 0; i < PQntuples(res); i++) + (*primary)[i] = GB.NewZeroString(PQgetvalue(res, i, 0)); + + PQclear(res); + + return FALSE; +} + + +/***************************************************************************** + + table_is_system() + + Returns if a table is a system table. + + is the database handle. +
    is the table name. + + This function returns TRUE if the table is a system table, and FALSE if + not. + +*****************************************************************************/ + +static int table_is_system(DB_DATABASE *db, const char *table) +{ + const char *query = + "select 1 from pg_class where (relkind = 'r' or relkind = 'v'or relkind = 'm') " + "and (relname = '&1') " + "and (relnamespace in (select oid from pg_namespace where nspname = 'pg_catalog'))"; + + //const char *query1 = + // "select 1 from pg_tables where tablename = lower('&1') and schemaname = 'public'"; + + const char *query2 = + "select 1 from pg_views where viewname = '&1' and schemaname = 'pg_catalog'"; + + PGresult *res; + int exist; + char *schema; + + get_table_schema(&table, &schema); + + if (schema) + return !strcmp(schema, "pg_catalog"); + + if (do_query(db, "Unable to check table: &1", &res, query, 1, table)) + return TRUE; + + exist = PQntuples(res) == 1; + PQclear(res); + + if (exist) + return TRUE; + + if (do_query(db, "Unable to check table: &1", &res, query2, 1, table)) + return TRUE; + + exist = PQntuples(res) == 1; + PQclear(res); + + if (exist) + return TRUE; + + return FALSE; +} + + +/***************************************************************************** + + table_delete() + + Deletes a table. + + is the database handle. +
    is the table name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_delete(DB_DATABASE *db, const char *table) +{ + return + do_query(db, "Unable to delete table: &1", NULL, + "drop table &1", 1, get_quoted_table(table)); +} + + +/***************************************************************************** + + table_create() + + Creates a table. + + is the database handle. +
    is the table name. + points to a linked list of field descriptions. + is the primary key. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_create(DB_DATABASE *db, const char *table, DB_FIELD *fields, char **primary, const char *not_used) +{ + DB_FIELD *fp; + int comma; + char *type; + int i; + + DB.Query.Init(); + + DB.Query.Add("CREATE TABLE "); + DB.Query.Add(get_quoted_table(table)); + DB.Query.Add(" ( "); + + comma = FALSE; + for (fp = fields; fp; fp = fp->next) + { + if (comma) + DB.Query.Add(", "); + else + comma = TRUE; + + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(fp->name); + DB.Query.Add(QUOTE_STRING); + + if (fp->type == DB_T_SERIAL) + { + DB.Query.Add(" BIGSERIAL "); + } + else if (fp->type == DB_T_BLOB) + { + DB.Query.Add(" BYTEA "); + } + else + { + switch (fp->type) + { + case GB_T_BOOLEAN: type = "BOOL"; break; + case GB_T_INTEGER: type = "INT"; break; + case GB_T_LONG: type = "BIGINT"; break; + case GB_T_FLOAT: type = "FLOAT8"; break; + case GB_T_DATE: type = "TIMESTAMP"; break; + case GB_T_STRING: + + if (fp->length <= 0) + type = "TEXT"; + else + { + sprintf(_buffer, "VARCHAR(%d)", fp->length); + type = _buffer; + } + + break; + + default: type = "TEXT"; break; + } + + DB.Query.Add(" "); + DB.Query.Add(type); + + if (fp->collation && *fp->collation) + { + DB.Query.Add(" COLLATE \""); + DB.Query.Add(fp->collation); + DB.Query.Add("\""); + } + + if (fp->def.type != GB_T_NULL) + { + DB.Query.Add(" NOT NULL DEFAULT "); + DB.FormatVariant(&_driver, &fp->def, DB.Query.AddLength); + } + else if (DB.StringArray.Find(primary, fp->name) >= 0) + { + DB.Query.Add(" NOT NULL "); + } + } + } + + if (primary) + { + DB.Query.Add(", PRIMARY KEY ("); + + for (i = 0; i < GB.Count(primary); i++) + { + if (i > 0) + DB.Query.Add(","); + + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(primary[i]); + DB.Query.Add(QUOTE_STRING); + } + + DB.Query.Add(")"); + } + + DB.Query.Add(" )"); + + return do_query(db, "Cannot create table: &1", NULL, DB.Query.Get(), 0); +} + + +/***************************************************************************** + + field_exist() + + Returns if a field exists in a given table + + is the database handle. +
    is the table name. + is the field name. + + This function returns TRUE if the field exists, and FALSE if not. + +*****************************************************************************/ + +static int field_exist(DB_DATABASE *db, const char *table, const char *field) +{ + const char *query = "select pg_attribute.attname from pg_class, pg_attribute " + "where pg_class.relname = '&1' " + "and (pg_class.relnamespace not in (select oid from pg_namespace where nspname = 'information_schema')) " + "and pg_attribute.attname = '&2' " + "and pg_attribute.attnum > 0 and not pg_attribute.attisdropped " + "and pg_attribute.attrelid = pg_class.oid "; + + const char *query_schema = "select pg_attribute.attname from pg_class, pg_attribute " + "where pg_class.relname = '&1' " + "and (pg_class.relnamespace in (select oid from pg_namespace where nspname = '&3')) " + "and pg_attribute.attname = '&2' " + "and pg_attribute.attnum > 0 and not pg_attribute.attisdropped " + "and pg_attribute.attrelid = pg_class.oid "; + + PGresult *res; + int exist; + char *schema; + + if (get_table_schema(&table, &schema)) + { + if (do_query(db, "Unable to check field: &1", &res, query, 2, table, field)) + return FALSE; + } + else + { + if (do_query(db, "Unable to check field: &1", &res, query_schema, 3, table, field, schema)) + return FALSE; + } + + exist = PQntuples(res) == 1; + + PQclear(res); + + return exist; +} + + +/***************************************************************************** + + field_list() + + Returns an array containing the name of each field in a given table + + is the database handle. +
    is the table name. + points to a variable that will receive the char* array. + + This function returns the number of fields, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int field_list(DB_DATABASE *db, const char *table, char ***fields) +{ + const char *query = "select pg_attribute.attname from pg_class, pg_attribute " + "where pg_class.relname = '&1' " + "and (pg_class.relnamespace not in (select oid from pg_namespace where nspname = 'information_schema')) " + "and pg_attribute.attnum > 0 and not pg_attribute.attisdropped " + "and pg_attribute.attrelid = pg_class.oid"; + + const char *query_schema = "select pg_attribute.attname from pg_class, pg_attribute " + "where pg_class.relname = '&1' " + "and (pg_class.relnamespace in (select oid from pg_namespace where nspname = '&2')) " + "and pg_attribute.attnum > 0 and not pg_attribute.attisdropped " + "and pg_attribute.attrelid = pg_class.oid"; + + PGresult *res; + int i; + int count; + char *schema; + + if (get_table_schema(&table, &schema)) + { + if (do_query(db, "Unable to get fields: &1", &res, query, 1, table)) + return -1; + } + else + { + if (do_query(db, "Unable to get fields: &1", &res, query_schema, 2, table, schema)) + return -1; + } + + if (fields) + { + GB.NewArray(fields, sizeof(char *), PQntuples(res)); + + for (i = 0; i < PQntuples(res); i++) + (*fields)[i] = GB.NewZeroString(PQgetvalue(res, i, 0)); + } + + count = PQntuples(res); + + PQclear(res); + return count; +} + + +/***************************************************************************** + + field_info() + + Get field description + + is the database handle. +
    is the table name. + is the field name. + points to a structure filled by the function. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int field_info(DB_DATABASE *db, const char *table, const char *field, DB_FIELD *info) +{ + const char *query; + const char *query_schema; + + if (db->flags.no_collation) + { + query = + "select pg_attribute.attname, pg_attribute.atttypid::int, " + "pg_attribute.atttypmod, pg_attribute.attnotnull, pg_attrdef.adsrc, pg_attribute.atthasdef " + "from pg_class, pg_attribute " + "left join pg_attrdef on (pg_attrdef.adrelid = pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) " + "where pg_class.relname = '&1' " + "and (pg_class.relnamespace not in (select oid from pg_namespace where nspname = 'information_schema')) " + "and pg_attribute.attname = '&2' " + "and pg_attribute.attnum > 0 and not pg_attribute.attisdropped " + "and pg_attribute.attrelid = pg_class.oid"; + + query_schema = + "select pg_attribute.attname, pg_attribute.atttypid::int, " + "pg_attribute.atttypmod, pg_attribute.attnotnull, pg_attrdef.adsrc, pg_attribute.atthasdef " + "from pg_class, pg_attribute " + "left join pg_attrdef on (pg_attrdef.adrelid = pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) " + "where pg_class.relname = '&1' " + "and (pg_class.relnamespace in (select oid from pg_namespace where nspname = '&3')) " + "and pg_attribute.attname = '&2' " + "and pg_attribute.attnum > 0 and not pg_attribute.attisdropped " + "and pg_attribute.attrelid = pg_class.oid"; + } + else + { + query = + "select pg_attribute.attname, pg_attribute.atttypid::int, " + "pg_attribute.atttypmod, pg_attribute.attnotnull, pg_attrdef.adsrc, pg_attribute.atthasdef, pg_collation.collname " + "from pg_class, pg_attribute " + "left join pg_attrdef on (pg_attrdef.adrelid = pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) " + "left join pg_collation on (pg_collation.oid = pg_attribute.attcollation) " + "where pg_class.relname = '&1' " + "and (pg_class.relnamespace not in (select oid from pg_namespace where nspname = 'information_schema')) " + "and pg_attribute.attname = '&2' " + "and pg_attribute.attnum > 0 and not pg_attribute.attisdropped " + "and pg_attribute.attrelid = pg_class.oid"; + + query_schema = + "select pg_attribute.attname, pg_attribute.atttypid::int, " + "pg_attribute.atttypmod, pg_attribute.attnotnull, pg_attrdef.adsrc, pg_attribute.atthasdef, pg_collation.collname " + "from pg_class, pg_attribute " + "left join pg_attrdef on (pg_attrdef.adrelid = pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) " + "left join pg_collation on (pg_collation.oid = pg_attribute.attcollation) " + "where pg_class.relname = '&1' " + "and (pg_class.relnamespace in (select oid from pg_namespace where nspname = '&3')) " + "and pg_attribute.attname = '&2' " + "and pg_attribute.attnum > 0 and not pg_attribute.attisdropped " + "and pg_attribute.attrelid = pg_class.oid"; + } + + PGresult *res; + char *schema; + const char *fulltable = table; + + if (get_table_schema(&table, &schema)) + { + if (do_query(db, "Unable to get field info: &1", &res, query, 2, table, field)) + return TRUE; + } + else + { + if (do_query(db, "Unable to get field info: &1", &res, query_schema, 3, table, field, schema)) + return TRUE; + } + + if (PQntuples(res) != 1) + { + GB.Error("Unable to find field &1.&2", fulltable, field); + return TRUE; + } + + fill_field_info(db, info, res, 0, 1); + + PQclear(res); + return FALSE; +} + + +/***************************************************************************** + + index_exist() + + Returns if an index exists in a given table + + is the database handle. +
    is the table name. + is the index name. + + This function returns TRUE if the index exists, and FALSE if not. + +*****************************************************************************/ + +static int index_exist(DB_DATABASE *db, const char *table, const char *index) +{ + const char *query = + "select pg_class.relname from pg_class, pg_index, pg_class pg_class2 " + "where pg_class2.relname = '&1' " + "and (pg_class2.relnamespace not in (select oid from pg_namespace where nspname = 'information_schema')) " + "and pg_index.indrelid = pg_class2.oid " + "and pg_index.indexrelid = pg_class.oid " + "and pg_class.relname = '&2'"; + + const char *query_schema = + "select pg_class.relname from pg_class, pg_index, pg_class pg_class2 " + "where pg_class2.relname = '&1' " + "and (pg_class2.relnamespace in (select oid from pg_namespace where nspname = '&3')) " + "and pg_index.indrelid = pg_class2.oid " + "and pg_index.indexrelid = pg_class.oid " + "and pg_class.relname = '&2'"; + + /*const char *query = "select relname from pg_class, pg_index " + "where pg_class.relname = '&1' " + "and pg_index.indexrelid = pg_class.oid ";*/ + + PGresult *res; + int exist; + char *schema; + + if (get_table_schema(&table, &schema)) + { + if (do_query(db, "Unable to check index: &1", &res, query, 2, table, index)) + return FALSE; + } + else + { + if (do_query(db, "Unable to check index: &1", &res, query_schema, 3, table, index, schema)) + return FALSE; + } + + exist = PQntuples(res) == 1; + + PQclear(res); + + return exist; +} + + +/***************************************************************************** + + index_list() + + Returns an array containing the name of each index in a given table + + is the database handle. +
    is the table name. + points to a variable that will receive the char* array. + + This function returns the number of indexes, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int index_list(DB_DATABASE *db, const char *table, char ***indexes) +{ + const char *query = "select pg_class.relname from pg_class, pg_index, pg_class pg_class2 " + "where pg_class2.relname = '&1' " + "and (pg_class2.relnamespace not in (select oid from pg_namespace where nspname = 'information_schema')) " + "and pg_index.indrelid = pg_class2.oid " + "and pg_index.indexrelid = pg_class.oid "; + + const char *query_schema = "select pg_class.relname from pg_class, pg_index, pg_class pg_class2 " + "where pg_class2.relname = '&1' " + "and (pg_class2.relnamespace in (select oid from pg_namespace where nspname = '&2')) " + "and pg_index.indrelid = pg_class2.oid " + "and pg_index.indexrelid = pg_class.oid "; + + PGresult *res; + int i; + int count; + char *schema; + + if (get_table_schema(&table, &schema)) + { + if (do_query(db, "Unable to get indexes: &1", &res, query, 1, table)) + return TRUE; + } + else + { + if (do_query(db, "Unable to get indexes: &1", &res, query_schema, 2, table, schema)) + return TRUE; + } + + if (indexes) + { + GB.NewArray(indexes, sizeof(char *), PQntuples(res)); + + for (i = 0; i < PQntuples(res); i++) + (*indexes)[i] = GB.NewZeroString(PQgetvalue(res, i, 0)); + } + + count = PQntuples(res); + PQclear(res); + + return count; +} + + +/***************************************************************************** + + index_info() + + Get index description + + is the database handle. +
    is the table name. + is the index name. + points to a structure filled by the function. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int index_info(DB_DATABASE *db, const char *table, const char *index, DB_INDEX *info) +{ + const char *query = + "select indisunique, indisprimary, indexrelid from pg_class, pg_index, pg_class pg_class2 " + "where pg_class2.relname = '&1' " + "and (pg_class2.relnamespace not in (select oid from pg_namespace where nspname = 'information_schema')) " + "and pg_index.indrelid = pg_class2.oid " + "and pg_index.indexrelid = pg_class.oid " + "and pg_class.relname = '&2'"; + + const char *query_schema = + "select indisunique, indisprimary, indexrelid from pg_class, pg_index, pg_class pg_class2 " + "where pg_class2.relname = '&1' " + "and (pg_class2.relnamespace in (select oid from pg_namespace where nspname = '&3')) " + "and pg_index.indrelid = pg_class2.oid " + "and pg_index.indexrelid = pg_class.oid " + "and pg_class.relname = '&2'"; + + const char *query_field = + "select pg_att1.attname " + "from pg_attribute pg_att1, pg_attribute pg_att2, pg_index pg_ind " + "where pg_ind.indexrelid = &1 " + "and pg_att2.attrelid = pg_ind.indexrelid " + "and pg_att1.attrelid = pg_ind.indrelid " + "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] " + "order by pg_att2.attnum"; + + PGresult *res; + char indexrelid[16]; + int i; + char *schema; + const char *fulltable = table; + + if (get_table_schema(&table, &schema)) + { + if (do_query(db, "Unable to get index info: &1", &res, query, 2, table, index)) + return TRUE; + } + else + { + if (do_query(db, "Unable to get index info: &1", &res, query_schema, 3, table, index, schema)) + return TRUE; + } + + if (PQntuples(res) != 1) + { + GB.Error("Unable to find index &1.&2", fulltable, index); + return TRUE; + } + + info->name = NULL; + info->unique = conv_boolean(PQgetvalue(res, 0, 0)); + info->primary = conv_boolean(PQgetvalue(res, 0, 1)); + + strcpy(indexrelid, PQgetvalue(res, 0, 2)); + + PQclear(res); + + if (do_query(db, "Unable to get index info: &1", &res, query_field, 1, indexrelid)) + return TRUE; + + DB.Query.Init(); + + for (i = 0; i < PQntuples(res); i++) + { + if (i > 0) + DB.Query.Add(","); + DB.Query.Add(PQgetvalue(res, i, 0)); + } + + PQclear(res); + + info->fields = DB.Query.GetNew(); + + return FALSE; +} + + +/***************************************************************************** + + index_delete() + + Deletes an index. + + is the database handle. +
    is the table name. + is the index name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int index_delete(DB_DATABASE *db, const char *table, const char *index) +{ + return + do_query(db, "Unable to delete index: &1", NULL, + "drop index &1", 1, get_quoted_table(index)); +} + + +/***************************************************************************** + + index_create() + + Creates an index. + + is the database handle. +
    is the table name. + is the index name. + points to a structure describing the index. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int index_create(DB_DATABASE *db, const char *table, const char *index, DB_INDEX *info) +{ + DB.Query.Init(); + + DB.Query.Add("CREATE "); + if (info->unique) + DB.Query.Add("UNIQUE "); + DB.Query.Add("INDEX \""); + DB.Query.Add(index); + DB.Query.Add("\" ON "); + DB.Query.Add(get_quoted_table(table)); + DB.Query.Add(" ( "); + DB.Query.Add(info->fields); + DB.Query.Add(" )"); + + return do_query(db, "Cannot create index: &1", NULL, DB.Query.Get(), 0); +} + + +/***************************************************************************** + + database_exist() + + Returns if a database exists + + is the database handle. + is the database name. + + This function returns TRUE if the database exists, and FALSE if not. + +*****************************************************************************/ + +static int database_exist(DB_DATABASE *db, const char *name) +{ + const char *query = + "select datname from pg_database where (datallowconn = 't') " + "and (datname = '&1')"; + + PGresult *res; + int exist; + + if (do_query(db, "Unable to check database: &1", &res, query, 1, name)) + return FALSE; + + exist = PQntuples(res) == 1; + + PQclear(res); + + return exist; +} + + + +/***************************************************************************** + + database_list() + + Returns an array containing the name of each database + + is the database handle. + points to a variable that will receive the char* array. + + This function returns the number of databases, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int database_list(DB_DATABASE *db, char ***databases) +{ + const char *query = + "select datname from pg_database where datallowconn and datname <> 'template1'"; + + PGresult *res; + int i; + int count; + + if (do_query(db, "Unable to get databases: &1", &res, query, 0)) + return -1; + + if (databases) + { + GB.NewArray(databases, sizeof(char *), PQntuples(res)); + + for (i = 0; i < PQntuples(res); i++) + (*databases)[i] = GB.NewZeroString(PQgetvalue(res, i, 0)); + } + + count = PQntuples(res); + + PQclear(res); + + return count; +} + + +/***************************************************************************** + + database_is_system() + + Returns if a database is a system database. + + is the database handle. + is the database name. + + This function returns TRUE if the database is a system database, and + FALSE if not. + +*****************************************************************************/ + +static int database_is_system(DB_DATABASE *db, const char *name) +{ + const char *query = + "select datname from pg_database where datallowconn " + "and (datname = '&1') and datistemplate"; + //"and (datname = lower('&1')) and datistemplate"; + + PGresult *res; + int system; + + if (do_query(db, "Unable to check database: &1", &res, query, 1, name)) + return TRUE; + + system = PQntuples(res) == 1; + + PQclear(res); + + return system; +} + +/***************************************************************************** + + table_type() + + Returns or sets the table type. + + is the database handle. +
    is the table name. + is the new type + + This function returns the table type. + + NOTE: Only mySQL seems to have table types at the moment. + +*****************************************************************************/ + +static char *table_type(DB_DATABASE *db, const char *table, const char *newtype) +{ + if (newtype) + GB.Error("PostgreSQL does not have any table types"); + + return NULL; +} + +/***************************************************************************** + + database_delete() + + Deletes a database. + + is the database handle. + is the database name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int database_delete(DB_DATABASE *db, const char *name) +{ + return + do_query(db, "Unable to delete database: &1", NULL, + "drop database \"&1\"", 1, name); +} + + +/***************************************************************************** + + database_create() + + Creates a database. + + is the database handle. + is the database name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int database_create(DB_DATABASE *db, const char *name) +{ + return + do_query(db, "Unable to create database: &1", NULL, + "create database \"&1\"", 1, name); +} + + +/***************************************************************************** + + user_exist() + + Returns if a user exists. + + is the database handle. + is the user name. + + This function returns TRUE if the user exists, and FALSE if not. + +*****************************************************************************/ + +static int user_exist(DB_DATABASE *db, const char *name) +{ + const char *query = "select usename from pg_user " + "where usename = '&1' "; + + PGresult *res; + int exist; + + if (do_query(db, "Unable to check user: &1", &res, query, 1, name)) + return FALSE; + + exist = PQntuples(res) == 1; + + PQclear(res); + + return exist; +} + + +/***************************************************************************** + + user_list() + + Returns an array containing the name of each user. + + is the database handle. + points to a variable that will receive the char* array. + + This function returns the number of users, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int user_list(DB_DATABASE *db, char ***users) +{ + const char *query = "select usename from pg_user "; + + PGresult *res; + int i; + int count; + + if (do_query(db, "Unable to get users: &1", &res, query, 0)) + return -1; + + if (users) + { + GB.NewArray(users, sizeof(char *), PQntuples(res)); + + for (i = 0; i < PQntuples(res); i++) + (*users)[i] = GB.NewZeroString(PQgetvalue(res, i, 0)); + } + + count = PQntuples(res); + + PQclear(res); + return count; +} + + +/***************************************************************************** + + user_info() + + Get user description + + is the database handle. + is the user name. + points to a structure filled by the function. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int user_info(DB_DATABASE *db, const char *name, DB_USER *info) +{ + const char *query = + "select usecreatedb, usesuper from pg_user " + "where usename = '&1' "; + + const char *query_passwd = + "select passwd from pg_shadow " + "where usename = '&1' "; + + PGresult *res; + + if (do_query(db, "Unable to get user info: &1", &res, query, 1, name)) + return TRUE; + + if (PQntuples(res) != 1) + { + GB.Error("Unable to find user &1", name); + return TRUE; + } + + info->name = NULL; + info->admin = conv_boolean(PQgetvalue(res, 0, 1)); + /* conv_boolean(PQgetvalue(res, 0, 0)) || */ + + PQclear(res); + + if (!do_query(db, NULL, &res, query_passwd, 1, name)) + { + if (*PQgetvalue(res, 0, 0)) + info->password = GB.NewString("***", 3); + } + + return FALSE; +} + + +/***************************************************************************** + + user_delete() + + Deletes a user. + + is the database handle. + is the user name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int user_delete(DB_DATABASE *db, const char *name) +{ + return + do_query(db, "Unable to delete user: &1", NULL, + "drop user \"&1\"", 1, name); +} + + +/***************************************************************************** + + user_create() + + Creates a user. + + is the database handle. + is the user name. + points to a structure describing the user. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int user_create(DB_DATABASE *db, const char *name, DB_USER *info) +{ + DB.Query.Init(); + + DB.Query.Add("CREATE USER "); + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(name); + DB.Query.Add(QUOTE_STRING); + + if (info->admin) + DB.Query.Add(" CREATEDB CREATEUSER"); + else + DB.Query.Add(" NOCREATEDB NOCREATEUSER"); + + if (info->password && *info->password) + { + DB.Query.Add(" PASSWORD '"); + DB.Query.Add(info->password); + DB.Query.Add("'"); + } + + /*_print_query = TRUE;*/ + return do_query(db, "Cannot create user: &1", NULL, DB.Query.Get(), 0); +} + + +/***************************************************************************** + + user_set_password() + + Change the user password. + + is the database handle. + is the user name. + is the new password + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int user_set_password(DB_DATABASE *db, const char *name, const char *password) +{ + DB.Query.Init(); + + DB.Query.Add("ALTER USER \""); + DB.Query.Add(name); + DB.Query.Add("\" PASSWORD '"); + DB.Query.Add(password); + DB.Query.Add("'"); + + return + do_query(db, "Cannot change user password: &1", + NULL, DB.Query.Get(), 0); +} + +/***************************************************************************** + + The driver interface + +*****************************************************************************/ + +DECLARE_DRIVER(_driver, "postgresql"); + +/***************************************************************************** + + The component entry and exit functions. + +*****************************************************************************/ + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.db", DB_INTERFACE_VERSION, &DB); + DB.Register(&_driver); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.db.postgresql/src/main.h b/gb.db.postgresql/src/main.h new file mode 100644 index 00000000..a952b956 --- /dev/null +++ b/gb.db.postgresql/src/main.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "gb.db.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern DB_INTERFACE DB; +#endif + +#define QUOTE_STRING "\"" + +#endif /* __MAIN_H */ diff --git a/gb.db.sqlite2/AUTHORS b/gb.db.sqlite2/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.db.sqlite2/COPYING b/gb.db.sqlite2/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.db.sqlite2/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.db.sqlite2/ChangeLog b/gb.db.sqlite2/ChangeLog new file mode 100644 index 00000000..b5bee161 --- /dev/null +++ b/gb.db.sqlite2/ChangeLog @@ -0,0 +1,38 @@ +02/03/04 - Added sqlite system tables sqlite_master and sqlite_temp_master to +the table list etc. +02/03/04 - Changed default string size to 0, which is unlimited. +22/03/04 - Cleared up some deallocations. +22/03/04 - Allowed for accessing same field name within multiple table + query. e.g. select fred.id, sid.id from fred, sid +01/04/04 - Enabled Field aliasing for exec. +03/04/04 - The host variable within the database class can be used as the + database home, that is, set it to the path where the database files + are located. +03/04/04 - The location of the database files is order by: + a) If fullpath given then use this. + b) If Host variable set, then look at the path set in the variable. + c) If environment variable GAMBAS_SQLITE_DBHOME is set then use. + d) Current working directory. +04/06/04 - Allow memory only database. e.g. :memory: as the database name. + +* 17 Jul 2004 - BM + +- By default, an in-memory database is opened if no database name is specified. +- An in_memory database returns as user-list only the current user, with admin rights. +- The databases are not searched by default in the current working directory anymore. + Instead, they are searched in the /tmp/gambas.%pid%/sqlite/ directory. + +* 29 Aug 2004 - BM + +- Many clean-ups in database_create(). The function FullPath() is not necessary + anymore, and has been removed. +- Fix table_index() so that it returns error if no true primary key is found + in the table. + +* 01 Sep 2004 - NG + +- Checks that a full path points at a real SQLite database in FindDatabase(). + +* 20 Nov 2004 - DC + +- I've replaced all float values for double values in the sqlite driver. diff --git a/gb.db.sqlite2/INSTALL b/gb.db.sqlite2/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.db.sqlite2/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.db.sqlite2/Makefile.am b/gb.db.sqlite2/Makefile.am new file mode 100644 index 00000000..f39e3183 --- /dev/null +++ b/gb.db.sqlite2/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @SQLITE2_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.db.sqlite2/NEWS b/gb.db.sqlite2/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.db.sqlite2/README b/gb.db.sqlite2/README new file mode 100644 index 00000000..b1589b20 --- /dev/null +++ b/gb.db.sqlite2/README @@ -0,0 +1,56 @@ +(Daniel Campos ) - All float values has been replace +by double values, as some extra wrong decimals were added in conversions from sql data +types to Gambas data types. + +Uses the STL and requires the sqliteclient library. Currently tested with +SQLite 2.8.13. from www.sqlite.org + +sqlite is typeless...although in the create you can specify type details (these are +just comments!). These types have been defined to map to gambas, those not defined +will default to string. + +Database/Gambas Type : ft_String/ GB_T_STRING +DDL Value : BLOB, CHAR(, CLOB, TEXT, VARCHAR, ENUM, SET, YEAR, Non specified + +Database/Gambas Type : ft_Boolean/ GB_T_BOOLEAN +DDL Value : CHAR, TINYINT, INT1, BOOL + +Database Type/Gambas : ft_Short/ GB_T_INTEGER +DDL Value : SMALLINT, INT2, MEDIUMINT + +Database/Gambas Type : ft_LongDouble/ GB_T_FLOAT +DDL Value : BIGINT, INT8 + +Database/Gambas Type : ft_Long/ GB_T_INTEGER +DDL Value : INTEGER, INT, INT4 + +Database/Gambas Type : ft_Float/GB_T_FLOAT +DDL Value : DECIMAL, NUMERIC, REAL, FLOAT, FLOAT8, FLOAT4 + +Database/Gambas Type : ft_Date/GB_T_DATE +DDL Value : TIMESTAMP, DATETIME, DATE, TIME + +Database/Gambas Type : ft_Double/GB_T_FLOAT +DDL Value : DOUBLE + +Thanks go to Leo Seib for his SQLiteDataset library ( sqlitedataset.sourceforge.net) +which has been the base for my hacking. + +The order for locating database files is: +1) If database is fullpath qualified +2) If Host is specified as a valid path +3) If set, GAMBAS_SQLITE_DBHOME variable +4) Current working directory. + +The driver will look for databases in the current working directory unless the environment +variable GAMBAS_SQLITE_DBHOME is set. + +There is no concept of users within sqlite. Access is controlled by file permissions +on the database file. + +Creates database /tmp/sqlite.db by default. For the gambas-database-manager this will +allow functionality. + +Sqlite is not case sensative on table or field names. The implementation for Gambas is. + +Database :memory: will load only in memory diff --git a/gb.db.sqlite2/TODO b/gb.db.sqlite2/TODO new file mode 100644 index 00000000..02d6efae --- /dev/null +++ b/gb.db.sqlite2/TODO @@ -0,0 +1,15 @@ +Pragma table_info now shows fields in primary key +field 'pk'. Maybe this feature can be used. + +Aliases do not work - OK for for alias.field but not table alias. + +Needs full testing and cleaning up. + +THe psuedo user management needs to be thought about. + +There may be functions that are provided by sqlitedataset that +I do not use, so could clean. + +There may be issues with memory allocation that need investigation. + +Version 3 of sqlite is imminent. diff --git a/gb.db.sqlite2/acinclude.m4 b/gb.db.sqlite2/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.db.sqlite2/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.db.sqlite2/component.am b/gb.db.sqlite2/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.db.sqlite2/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.db.sqlite2/configure.ac b/gb.db.sqlite2/configure.ac new file mode 100644 index 00000000..02555b57 --- /dev/null +++ b/gb.db.sqlite2/configure.ac @@ -0,0 +1,25 @@ +dnl ---- configure.ac for gb.db.sqlite2 driver + +dnl ---- Initialization + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-db-sqlite2, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.db.sqlite2) +AC_PROG_LIBTOOL + +dnl ---- SQLite driver + +GB_COMPONENT_PKG_CONFIG( + sqlite2, SQLITE2, gb.db.sqlite2, [src], + sqlite) + +dnl ---- Create makefiles + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.db.sqlite2/gambas.h b/gb.db.sqlite2/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.db.sqlite2/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.db.sqlite2/gb.db.h b/gb.db.sqlite2/gb.db.h new file mode 120000 index 00000000..563bd00c --- /dev/null +++ b/gb.db.sqlite2/gb.db.h @@ -0,0 +1 @@ +../main/lib/db/gb.db.h \ No newline at end of file diff --git a/gb.db.sqlite2/gb.db.proto.h b/gb.db.sqlite2/gb.db.proto.h new file mode 120000 index 00000000..0a8e6920 --- /dev/null +++ b/gb.db.sqlite2/gb.db.proto.h @@ -0,0 +1 @@ +../main/lib/db/gb.db.proto.h \ No newline at end of file diff --git a/gb.db.sqlite2/gb_common.h b/gb.db.sqlite2/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.db.sqlite2/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.db.sqlite2/m4 b/gb.db.sqlite2/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.db.sqlite2/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.db.sqlite2/reconf b/gb.db.sqlite2/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.db.sqlite2/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.db.sqlite2/src/Makefile.am b/gb.db.sqlite2/src/Makefile.am new file mode 100755 index 00000000..ef24d21a --- /dev/null +++ b/gb.db.sqlite2/src/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.db.sqlite2 +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.db.sqlite2.la + +gb_db_sqlite2_la_LIBADD = @SQLITE2_LIB@ +gb_db_sqlite2_la_LDFLAGS = -module @LD_FLAGS@ @SQLITE2_LDFLAGS@ +gb_db_sqlite2_la_CXXFLAGS = $(AM_CXXFLAGS) -fexceptions +gb_db_sqlite2_la_CPPFLAGS = @SQLITE2_INC@ + +gb_db_sqlite2_la_SOURCES = \ + main.h main.cpp dataset.h dataset.cpp qry_dat.cpp qry_dat.h sqlitedataset.h \ + sqlitedataset.cpp stringhelper.h stringhelper.cpp diff --git a/gb.db.sqlite2/src/dataset.cpp b/gb.db.sqlite2/src/dataset.cpp new file mode 100644 index 00000000..9d5140bb --- /dev/null +++ b/gb.db.sqlite2/src/dataset.cpp @@ -0,0 +1,585 @@ +/*************************************************************************** + + dataset.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/********************************************************************** + * Copyright (c) 2002, Leo Seib, Hannover + * + * Project: C++ Dynamic Library + * Module: Dataset abstraction later realisation file + * Author: Leo Seib E-Mail: lev@almaty.pointstrike.net + * Begin: 5/04/2002 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **********************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "dataset.h" + + +extern "C" { + +//************* Database implementation *************** + +Database::Database() { + active = false; // No connection yet + error = "";//S_NO_CONNECTION; + host = ""; + port = ""; + db = ""; + login = ""; + passwd = ""; + sequence_table = "db_sequence"; +} + +Database::~Database() { + disconnect(); // Disconnect if connected to database +} + +int Database::connectFull(const char *newHost, const char *newPort, const char *newDb, const char *newLogin, const char *newPasswd) { + host = newHost; + port = newPort; + db = newDb; + login = newLogin; + passwd = newPasswd; + return connect(); +} + +} + + +//************* Dataset implementation *************** + +Dataset::Dataset() { + + db = NULL; + haveError = active = false; + frecno = 0; + fbof = feof = true; + autocommit = true; + + select_sql = ""; + + fields_object = new Fields(); + + edit_object = new Fields(); +} + + + +Dataset::Dataset(Database *newDb) { + + db = newDb; + haveError = active = false; + frecno = 0; + fbof = feof = true; + autocommit = true; + + select_sql = ""; + + fields_object = new Fields(); + + edit_object = new Fields(); + +} + + +Dataset::~Dataset() { + update_sql.clear(); + insert_sql.clear(); + delete_sql.clear(); + + + delete fields_object; + delete edit_object; + +} + + +void Dataset::setSqlParams(const char *sqlFrmt, sqlType t, ...) { + va_list ap; + char sqlCmd[DB_BUFF_MAX+1]; + + va_start(ap, t); + vsnprintf(sqlCmd, DB_BUFF_MAX-1, sqlFrmt, ap); + va_end(ap); + + switch (t) { + case sqlSelect: set_select_sql(sqlCmd); + break; + case sqlUpdate: add_update_sql(sqlCmd); + break; + case sqlInsert: add_insert_sql(sqlCmd); + break; + case sqlDelete: add_delete_sql(sqlCmd); + break; + case sqlExec: sql = sqlCmd; + break; + + } +} + + + +void Dataset::set_select_sql(const char *sel_sql) { + select_sql = sel_sql; +} + +void Dataset::set_select_sql(const string &sel_sql) { + select_sql = sel_sql; +} + + +void Dataset::parse_sql(string &sql) { + string fpattern,by_what; + pars.set_str(sql.c_str()); + for (uint i=0;i< fields_object->size();i++) { + fpattern = ":OLD_"+(*fields_object)[i].props.name; + by_what = "'"+(*fields_object)[i].val.get_asString()+"'"; + //cout << "parsing " << fpattern <size();i++) { + fpattern = ":NEW_"+(*edit_object)[i].props.name; + by_what = "'"+(*edit_object)[i].val.get_asString()+"'"; + sql = pars.replace(fpattern,by_what); + } + +// StringList before_array, after_array; +// int tag = 0; +// bool eol_reached = false, +// was_changed = false, +// flag = false; +// ExtString which_before, which_after; +// ExtString bef, aft, prev_before, right_side, which_field, changed_field, f_value; + +// before_array.add(":NEW_", tag); +// before_array.add(":OLD_", tag); + +// after_array.add(")", tag); +// after_array.add(",", tag); +// after_array.add(" ", tag); + +// sq.squish(); +// bef = sq.before_arr(before_array, which_before); + +// while (!(bef == prev_before)) { +// right_side = sq.after(which_before, flag); +// right_side.squish(); + +// aft = right_side.after_arr(after_array, which_after); +// aft.squish(); +// which_field = right_side.before(which_after); + +// // checking whather we reach end of line +// if ((which_field == "\0") && (which_before != "\0")) { +// which_field = right_side; +// eol_reached = true; +// } + +// // If new field and is in insert or edit mode - looks in edit_object +// if ((which_before == ":NEW_") && (which_field != "\0")) { +// which_field.squish(); +// f_value.assign(fv(which_field.getChars())); +// f_value.addslashes(); +// changed_field.assign("'"); +// changed_field + f_value + "'"; +// } +// else +// // else looking old value in the current result set +// if ((which_before == ":OLD_") && (which_field != "\0")) { +// which_field.squish(); +// f_value.assign(f_old(which_field.getChars())); +// f_value.addslashes(); +// changed_field.assign("'"); +// changed_field + f_value + "'"; +// } + +// if (!eol_reached) { + +// sq.assign(bef + changed_field + which_after + aft); +// } +// else { +// if (!was_changed && (which_field != "\0")) { + +// sq.assign(bef + changed_field + which_after + aft); +// was_changed = true; +// } +// } + + +// prev_before = bef; +// bef = sq.before_arr(before_array, which_before); + +// } + +} + + +void Dataset::close(void) { + + haveError = false; + frecno = 0; + fbof = feof = true; + active = false; +} + + +//bool Dataset::seek(int pos=0) { +bool Dataset::seek(int pos) { + frecno = (pos0)? false : true; + } +} + +void Dataset::next() { + if (ds_state == dsSelect) { + fbof = false; + if (frecno0)? num_rows()-1: 0; + feof = fbof = (num_rows()>0)? false : true; + } +} + +//bool Dataset::goto_rec(int pos=1) { +bool Dataset::goto_rec(int pos) { + if (ds_state == dsSelect) { + return seek(pos - 1); + } + return false; +} + + +/*void Dataset::insert() { + //cout << "insert\n\n"; + for (int i=0; isize(); i++) { + (*edit_object)[i].val = (*fields_object)[i].val; + } + ds_state = dsEdit; +} + + +void Dataset::post() { + if (ds_state == dsInsert) make_insert(); + else if (ds_state == dsEdit) make_edit(); +} + + +void Dataset::deletion() { + if (ds_state == dsSelect) make_deletion(); +} + + +bool Dataset::set_field_value(const char *f_name, const field_value &value) { + bool found = false; + if ((ds_state == dsInsert) || (ds_state == dsEdit)) { + for (uint i=0; i < fields_object->size(); i++) + if ((*edit_object)[i].props.name == f_name) { + (*edit_object)[i].val = value; + found = true; + } + if (!found){ + GB.Error("Field not found: &1",f_name); + } + return found; + } + GB.Error("Not in Insert or Edit state"); + return found; +} + + +const field_value & Dataset::get_field_value(const char *f_name) +{ + static field_value fv; + + if (ds_state != dsInactive) + { + if (ds_state == dsEdit || ds_state == dsInsert) + { + for (uint i = 0; i < edit_object->size(); i++) + if ((*edit_object)[i].props.name == f_name) + return (*edit_object)[i].val; + GB.Error("Field not found: %s", f_name); + } + else + for (uint i = 0; i < fields_object->size(); i++) + if ((*fields_object)[i].props.name == f_name) + return (*fields_object)[i].val; + GB.Error("Field not found: %s", f_name); + } + + GB.Error("Dataset state is Inactive"); + return fv; +} + + +const field_value & Dataset::get_field_value(int index) //const char *f_name) +{ + static field_value fv; + + if (ds_state != dsInactive) + { + if (ds_state == dsEdit || ds_state == dsInsert) + return (*edit_object)[index].val; + else + return (*fields_object)[index].val; + } + + GB.Error("Dataset state is Inactive"); + return fv; +} + + +const field_value Dataset::f_old(const char *f_name) { + if (ds_state != dsInactive) + for (uint i=0; i < fields_object->size(); i++) + if ((*fields_object)[i].props.name == f_name) + return (*fields_object)[i].val; + field_value fv; + return fv; +} + + + +void Dataset::setParamList(const ParamList ¶ms){ + plist = params; +} + + +bool Dataset::locate(){ + bool result; + if (plist.empty()) return false; + + std::map::const_iterator i; + first(); + while (!eof()) { + result = true; + for (i=plist.begin();i!=plist.end();++i) + if (fv(i->first.c_str()).get_asString() == i->second.get_asString()) { + continue; + } + else {result = false; break;} + if (result) { return result;} + next(); + } + return false; +} + +bool Dataset::locate(const ParamList ¶ms) { + plist = params; + return locate(); +} + +bool Dataset::findNext(void) { + bool result; + if (plist.empty()) return false; + + std::map::const_iterator i; + while (!eof()) { + result = true; + for (i=plist.begin();i!=plist.end();++i) + if (fv(i->first.c_str()).get_asString() == i->second.get_asString()) { + continue; + } + else {result = false; break;} + if (result) { return result;} + next(); + } + return false; +} + + +void Dataset::add_update_sql(const char *upd_sql){ + string s = upd_sql; + update_sql.push_back(s); +} + + +void Dataset::add_update_sql(const string &upd_sql){ + update_sql.push_back(upd_sql); +} + +void Dataset::add_insert_sql(const char *ins_sql){ + string s = ins_sql; + insert_sql.push_back(s); +} + + +void Dataset::add_insert_sql(const string &ins_sql){ + insert_sql.push_back(ins_sql); +} + +void Dataset::add_delete_sql(const char *del_sql){ + string s = del_sql; + delete_sql.push_back(s); +} + + +void Dataset::add_delete_sql(const string &del_sql){ + delete_sql.push_back(del_sql); +} + +void Dataset::clear_update_sql(){ + update_sql.clear(); +} + +void Dataset::clear_insert_sql(){ + insert_sql.clear(); +} + +void Dataset::clear_delete_sql(){ + delete_sql.clear(); +} + +int Dataset::field_count() { return fields_object->size();} +int Dataset::fieldCount() { return fields_object->size();} + +const char *Dataset::fieldName(int n) { + if ( n < field_count() && n >= 0) + return (*fields_object)[n].props.name.c_str(); + else + return NULL; +} + +int Dataset::fieldSize(int n) { + if ( n < field_count() && n >= 0) + return (*fields_object)[n].props.field_len; + else + return 0; +} + +int Dataset::fieldIndex(const char *fn) { + int index, length; + + if (strchr(fn, (int)'.')){ + /* table name has been supplied */ + for (uint i=0; i < fields_object->size(); i++) { + //if ((*fields_object)[i].props.name == fn) + if (strcmp((*fields_object)[i].props.name.c_str(),fn) == 0) + return i; + } + } + else { + for (uint i=0; i < fields_object->size(); i++) { + index = (*fields_object)[i].props.name.find('.') + 1; + length = (*fields_object)[i].props.name.length(); + /*printf("Field name [%s] find [%s] fn [%s]\n", + (*fields_object)[i].props.name.c_str(), + (*fields_object)[i].props.name.substr(index, length).c_str(), fn);*/ + + //if ((*fields_object)[i].props.name.substr(index, length) == fn){ + if (strcmp((*fields_object)[i].props.name.substr(index, length).c_str(),fn) == 0){ + return i; + } + } + } + return -1; +} + + +int Dataset::fieldType(int n) { + if ( n < field_count() && n >= 0){ + //cout << (*fields_object)[n].val.gft(); + return (*fields_object)[n].val.get_fType(); + //return (*fields_object)[n].props.type; + } + else + return 0; +} diff --git a/gb.db.sqlite2/src/dataset.h b/gb.db.sqlite2/src/dataset.h new file mode 100644 index 00000000..272c765e --- /dev/null +++ b/gb.db.sqlite2/src/dataset.h @@ -0,0 +1,420 @@ +/*************************************************************************** + + dataset.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/********************************************************************** + * Copyright (c) 2002, Leo Seib, Hannover + * + * Project:Dataset C++ Dynamic Library + * Module: Dataset abstraction layer header file + * Author: Leo Seib E-Mail: lev@almaty.pointstrike.net + * Begin: 5/04/2002 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **********************************************************************/ + + +#ifndef _DATASET_H +#define _DATASET_H + +#include +#include +#include "stringhelper.h" +#include +#include +#include "qry_dat.h" + +#include "gambas.h" +#include "gb_common.h" + +extern "C" { +#include "../gb.db.h" + +extern GB_INTERFACE GB; + + +class Dataset; // forward declaration of class Dataset + + +#define S_NO_CONNECTION "No active connection"; + +#define DB_BUFF_MAX 8*1024 // Maximum buffer's capacity + +#define DB_CONNECTION_NONE 0 +#define DB_CONNECTION_OK 1 +#define DB_CONNECTION_BAD 2 + +#define DB_COMMAND_OK 0 // OK - command executed +#define DB_EMPTY_QUERY 1 // Query didn't return tuples +#define DB_TUPLES_OK 2 // Query returned tuples +#define DB_GOT_ERROR 5 +#define DB_BAD_RESPONSE 6 +#define DB_UNEXPECTED 7 // This shouldn't ever happen +#define DB_UNEXPECTED_RESULT -1 //For integer functions + +/******************* Class Database definition ******************** + + represents connection with database server; + +******************************************************************/ +class Database { +protected: + bool active; + string error, // Error description + host, port, db, login, passwd, //Login info + sequence_table; //Sequence table for nextid + +public: +/* constructor */ + Database(); +/* destructor */ + virtual ~Database(); + virtual Dataset *CreateDataset() const = 0; +/* sets a new host name */ + void setHostName(const char *newHost) { host = newHost; } +/* gets a host name */ + const char *getHostName(void) const { return host.c_str(); } +/* sets a new port */ + void setPort(const char *newPort) { port = newPort; } +/* gets a port */ + const char *getPort(void) const { return port.c_str(); } +/* sets a new database name */ + void setDatabase(const char *newDb) { db = newDb; } +/* gets a database name */ + const char *getDatabase(void) const { return db.c_str(); } +/* sets a new login to database */ + void setLogin(const char *newLogin) { login = newLogin; } +/* gets a login */ + const char *getLogin(void) const { return login.c_str(); } +/* sets a password */ + void setPasswd(const char *newPasswd) { passwd = newPasswd; } +/* gets a password */ + const char *getPasswd(void) const { return passwd.c_str(); } +/* active status is OK state */ + virtual bool isActive(void) const { return active; } +/* Set new name of sequence table */ + void setSequenceTable(const char *new_seq_table) { sequence_table = new_seq_table; }; +/* Get name of sequence table */ + const char *getSequenceTable(void) { return sequence_table.c_str(); } + + +/* virtual methods that must be overloaded in derived classes */ + + virtual int init(void) { return DB_COMMAND_OK; } + virtual int status(void) { return DB_CONNECTION_NONE; } + virtual int setErr(int err_code)=0; + virtual const char *getErrorMsg(void) { return error.c_str(); } + + virtual int connect(void) { return DB_COMMAND_OK; } + virtual int connectFull( const char *newDb, const char *newHost=NULL, + const char *newLogin=NULL, const char *newPasswd=NULL,const char *newPort=NULL); + virtual void disconnect(void) { active = false; } + virtual int reset(void) { return DB_COMMAND_OK; } + virtual int create(void) { return DB_COMMAND_OK; } + virtual int drop(void) { return DB_COMMAND_OK; } + virtual long nextid(const char* seq_name)=0; + +/* virtual methods for transaction */ + + virtual void start_transaction() {}; + virtual void commit_transaction() {}; + virtual void rollback_transaction() {}; + + virtual bool in_transaction() {return false;}; + +}; + +} + + + +/******************* Class Dataset definition ********************* + + global abstraction for using Databases + +******************************************************************/ + +// define Dataset States type +enum dsStates { dsSelect, dsInsert, dsEdit, dsUpdate, dsDelete, dsInactive }; +enum sqlType {sqlSelect,sqlUpdate,sqlInsert,sqlDelete,sqlExec}; + + +typedef std::list StringList; +typedef std::map ParamList; + + +class Dataset { +protected: +/* char *Host = ""; //WORK_HOST; + char *Database = ""; //WORK_DATABASE; + char *User = ""; //WORK_USER; + char *Password = ""; //WORK_PASSWORD; +*/ + + Database *db; // info about db connection + dsStates ds_state; // current state + Fields *fields_object, *edit_object; + + + bool active; // Is Query Opened? + bool haveError; + int frecno; // number of current row bei bewegung + string sql; + + str_helper pars; + + ParamList plist; // Paramlist for locate + bool fbof, feof; + bool autocommit; // for transactions + + +/* Variables to store SQL statements */ + string empty_sql; // Executed when result set is empty + string select_sql; // May be only single string variable + + StringList update_sql; // May be an array in complex queries +/* Field values for updating must has prefix :NEW_ and :OLD_ and field name + Example: + update wt_story set idobject set idobject=:NEW_idobject,body=:NEW_body + where idobject=:OLD_idobject + Essentually fields idobject and body must present in the + result set (select_sql statement) */ + + StringList insert_sql; // May be an array in complex queries +/* Field values for inserting must has prefix :NEW_ and field name + Example: + insert into wt_story (idobject, body) values (:NEW_idobject, :NEW_body) + Essentually fields idobject and body must present in the + result set (select_sql statement) */ + + StringList delete_sql; // May be an array in complex queries +/* Field values for deleing must has prefix :OLD_ and field name + Example: + delete from wt_story where idobject=:OLD_idobject + Essentually field idobject must present in the + result set (select_sql statement) */ + + + + +/* Arrays for searching */ +// StringList names, values; + + +/* Makes direct inserts into database via mysql_query function */ + virtual void make_insert() = 0; +/* Edit SQL */ + virtual void make_edit() = 0; +/* Delete SQL */ + virtual void make_deletion() = 0; + +/* This function works only with MySQL database + Filling the fields information from select statement */ + virtual void fill_fields(void)=0; + +/* Parse Sql - replacing fields with prefixes :OLD_ and :NEW_ with current values of OLD or NEW field. */ + void parse_sql(string &sql); + +/* Returns old field value (for :OLD) */ + virtual const field_value f_old(const char *f); + +public: + +/* constructor */ + Dataset(); + Dataset(Database *newDb); + +/* destructor */ + virtual ~Dataset(); + +/* sets a new value of connection to database */ + void setDatabase(Database *newDb) { db = newDb; } +/* retrieves a database which connected */ + Database *getDatabase(void) { return db; } + +/* sets a new query string to database server */ + void setExecSql(const char *newSql) { sql = newSql; } +/* retrieves a query string */ + const char *getExecSql(void) { return sql.c_str(); } + +/* status active is OK query */ + virtual bool isActive(void) { return active; } + + virtual void setSqlParams(const char *sqlFrmt, sqlType t, ...); + + +/* error handling */ +// virtual void halt(const char *msg); +/* sequence numbers */ + virtual long nextid(const char *seq_name)=0; +/* sequence numbers */ + virtual int num_rows()= 0; + +/* Open SQL query */ + virtual void open(const string &sql) = 0; + virtual void open() = 0; +/* func. executes a query without results to return */ + virtual int exec (const string &sql) = 0; + virtual int exec() = 0; + virtual const void* getExecRes()=0; +/* as open, but with our query exept Sql */ + virtual bool query(const char *sql) = 0; +/* Close SQL Query*/ + virtual void close(); +/* This function looks for field Field_name with value equal Field_value + Returns true if found (position of dataset is set to founded position) + and false another way (position is not changed). */ +// virtual bool lookup(char *field_name, char*field_value); +/* Refresh dataset (reopen it and set the same cursor position) */ + virtual void refresh(); + +/* Go to record No (starting with 0) */ + virtual bool seek(int pos=0); +/* Go to record No (starting with 1) */ + virtual bool goto_rec(int pos=1); +/* Go to the first record in dataset */ + virtual void first(); +/* Go to next record in dataset */ + virtual void next(); +/* Go to porevious record */ + virtual void prev(); +/* Go to last record in dataset */ + virtual void last(); + +/* Check for Ending dataset */ + virtual bool eof(void) { return feof; } +/* Check for Begining dataset */ + virtual bool bof(void) { return fbof; } + +/* Start the insert mode */ + //virtual void insert(); +/* Start the insert mode (alias for insert() function) */ + //virtual void append() { insert(); } +/* Start the edit mode */ + virtual void edit(); + +/* Add changes, that were made during insert or edit states of dataset into the database */ + virtual void post(); +/* Delete statements from database */ + virtual void deletion(); +/* Cancel changes, made in insert or edit states of dataset */ + virtual void cancel() {}; + + virtual void setParamList(const ParamList ¶ms); + virtual bool locate(); + virtual bool locate(const ParamList ¶ms); + virtual bool findNext(); + +/* func. retrieves a number of fields */ +/* Number of fields in a record */ + virtual int field_count(); + virtual int fieldCount(); +/* func. retrieves a field name with 'n' index */ + virtual const char *fieldName(int n); +/* func. retrieves a field index with 'fn' field name,return -1 when field name not found */ + virtual int fieldIndex(const char *fn); +/* func. retrieves a field size */ + virtual int fieldSize(int n); +/* func. retrieves a field type of field index NG 30/08/03*/ + virtual int fieldType(int n); + +/* Set field value */ + virtual bool set_field_value(const char *f_name, const field_value &value); +/* alias for set_field_value */ + virtual bool sf(const char *f, const field_value &v) { return set_field_value(f,v); } + + + +/* Return field name by it index */ +// virtual char *field_name(int f_index) { return field_by_index(f_index)->get_field_name(); }; + +/* Getting value of field for current record */ + virtual const field_value & get_field_value(const char *f_name); +/* Alias to get_field_value */ + const field_value fv(const char *f) { return get_field_value(f); } + +/* Getting value of field for current record */ + virtual const field_value & get_field_value(int index); //const char *f_name); + +/* Alias to get_field_value */ + const field_value & fv(int index) + { + return get_field_value(index); + } + +/* ------------ for transaction ------------------- */ + void set_autocommit(bool v) { autocommit = v; } + bool get_autocommit() { return autocommit; } + +/* ----------------- for debug -------------------- */ + Fields *get_fields_object() {return fields_object;}; + Fields *get_edit_object() {return edit_object;}; + + private: + void set_ds_state(dsStates new_state) {ds_state = new_state;}; + public: +/* return ds_state value */ + dsStates get_state() {return ds_state;}; + +/*add a new value to select_sql*/ + void set_select_sql(const char *sel_sql); + void set_select_sql(const string &select_sql); +/*add a new value to update_sql*/ + void add_update_sql(const char *upd_sql); + void add_update_sql(const string &upd_sql); +/*add a new value to insert_sql*/ + void add_insert_sql(const char *ins_sql); + void add_insert_sql(const string &ins_sql); + /*add a new value to delete_sql*/ + void add_delete_sql(const char *del_sql); + void add_delete_sql(const string &del_sql); + +/*clear update_sql*/ + void clear_update_sql(); +/*clear insert_sql*/ + void clear_insert_sql(); +/*clear delete_sql*/ + void clear_delete_sql(); + +/*get value of select_sql*/ + const char *get_select_sql(); + +}; + +#endif diff --git a/gb.db.sqlite2/src/gb.db.sqlite2.component b/gb.db.sqlite2/src/gb.db.sqlite2.component new file mode 100644 index 00000000..5f9bdca4 --- /dev/null +++ b/gb.db.sqlite2/src/gb.db.sqlite2.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.db.sqlite2 +Author=Nigel Gerrard,Benoît Minisini +State=Deprecated +Require=gb.db + diff --git a/gb.db.sqlite2/src/main.cpp b/gb.db.sqlite2/src/main.cpp new file mode 100644 index 00000000..cc21bc48 --- /dev/null +++ b/gb.db.sqlite2/src/main.cpp @@ -0,0 +1,2528 @@ +/*************************************************************************** + + main.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include +#include /* For basename function */ +#include /* For passwd file functions */ +#include /* For group file functions */ +#include +#include +#include +#include +#include + +#include "sqlitedataset.h" + +#include "main.h" + +extern "C" { +GB_INTERFACE GB EXPORT; +DB_INTERFACE DB EXPORT; +} //end extern "C" + +static char _buffer[32]; + +static int _print_query = FALSE; + + +/***************************************************************************** + + The driver interface + +*****************************************************************************/ + +DECLARE_DRIVER(_driver, "sqlite2"); + + +/* Internal function to convert a database type into a Gambas type */ + +static GB_TYPE conv_type(int type) +{ + switch(type) + { + case ft_Boolean: + return GB_T_BOOLEAN; + case ft_Short: + case ft_UShort: + case ft_Long: + case ft_ULong: + return GB_T_INTEGER; + + case ft_Float: + case ft_Double: + return GB_T_FLOAT; + + case ft_LongDouble: + return GB_T_LONG; + + case ft_Date: + return GB_T_DATE; + + case ft_String: + case ft_WideString: + case ft_Char: + case ft_WChar: + return GB_T_STRING; + + default: + return GB_T_STRING; + + } +} + + +/* Internal function to convert a database value into a Gambas variant value */ + +static void conv_data(const char *data, GB_VARIANT_VALUE *val, fType type) +{ + GB_VALUE conv; + GB_DATE_SERIAL date; + double sec; + + switch (type) + { + case ft_Boolean: + + val->type = GB_T_BOOLEAN; + if (data[0] == 't' || data[0] == 'T') + val->value._boolean = -1; + else + val->value._boolean = atoi(data) ? -1 : 0; + break; + + case ft_Short: + case ft_UShort: + case ft_Long: + case ft_ULong: + + GB.NumberFromString(GB_NB_READ_INTEGER, data, strlen(data), &conv); + + val->type = GB_T_INTEGER; + val->value._integer = conv._integer.value; + + break; + + case ft_LongDouble: + + GB.NumberFromString(GB_NB_READ_LONG, data, strlen(data), &conv); + + val->type = GB_T_LONG; + val->value._long = conv._long.value; + + break; + + case ft_Float: + case ft_Double: + + GB.NumberFromString(GB_NB_READ_FLOAT, data, strlen(data), &conv); + + val->type = GB_T_FLOAT; + val->value._float = conv._float.value; + + break; + + case ft_Date: + + memset(&date, 0, sizeof(date)); + + switch (strlen(data)) + { + case 14: + sscanf(data, "%4d%2d%2d%2d%2d%lf", &date.year, &date.month, + &date.day, &date.hour, &date.min, &sec); + date.sec = (short) sec; + date.msec = (short) ((sec - date.sec) * 1000 + 0.5); + break; + case 12: + sscanf(data, "%2d%2d%2d%2d%2d%lf", &date.year, &date.month, + &date.day, &date.hour, &date.min, &sec); + date.sec = (short) sec; + date.msec = (short) ((sec - date.sec) * 1000 + 0.5); + break; + case 10: + if (sscanf(data, "%4d-%2d-%2d", &date.year, &date.month, + &date.day) != 3) + { + if (sscanf(data, "%4d/%2d/%2d", &date.year, &date.month, + &date.day) != 3) + { + if (sscanf(data, "%4d:%2d:%lf", &date.hour, &date.min, + &sec) == 3) + { + date.sec = (short) sec; + date.msec = (short) ((sec - date.sec) * 1000 + 0.5); + } + else + { + sscanf(data, "%2d%2d%2d%2d%2d", &date.year, + &date.month, &date.day, &date.hour, &date.min); + } + } + } + + break; + case 8: + if (sscanf(data, "%4d%2d%2d", &date.year, &date.month, + &date.day) != 3) + { + sscanf(data, "%2d/%2d/%2d", &date.year, &date.month, + &date.day); + } + break; + case 6: + sscanf(data, "%2d%2d%2d", &date.year, &date.month, &date.day); + break; + case 4: + sscanf(data, "%2d%2d", &date.year, &date.month); + break; + case 2: + sscanf(data, "%2d", &date.year); + break; + default: + sscanf(data, "%4d-%2d-%2d %2d:%2d:%lf", &date.year, + &date.month, &date.day, &date.hour, &date.min, &sec); + date.sec = (short) sec; + date.msec = (short) ((sec - date.sec) * 1000 + 0.5); + } + if (date.year < 100) + date.year += 1900; + + GB.MakeDate(&date, (GB_DATE *) & conv); + + val->type = GB_T_DATE; + val->value._date.date = conv._date.value.date; + val->value._date.time = conv._date.value.time; + + break; + + case ft_String: + case ft_WideString: + case ft_Char: + case ft_WChar: + default: + + val->type = GB_T_CSTRING; + val->value._string = (char *)data; + + break; + } + +} + +/* Internal function to substitute the table name into a query */ + +static char *query_param[3]; + +static void query_get_param(int index, char **str, int *len, char quote) +{ + if (index > 3) + return; + + index--; + *str = query_param[index]; + *len = strlen(*str); + + if (quote == '\'') + { + *str = DB.QuoteString(*str, *len, quote); + *len = GB.StringLength(*str); + } +} + +/* Internal function to run a query */ + +static int do_query(DB_DATABASE *db, const char *error, Dataset **pres, + const char *qtemp, int nsubst, ...) +{ + SqliteDatabase *conn = (SqliteDatabase *)db->handle; + va_list args; + int i; + const char *query; + Dataset *res = conn->CreateDataset(); + int ret; + + if (nsubst) + { + va_start(args, nsubst); + if (nsubst > 3) + nsubst = 3; + for (i = 0; i < nsubst; i++) + query_param[i] = va_arg(args, char *); + + query = DB.SubstString(qtemp, 0, query_get_param); + } + else + query = qtemp; + + if (_print_query) + { + _print_query = FALSE; + } + + if (DB.IsDebug()) + fprintf(stderr, "sqlite2: %p: %s\n", conn, query); + + if (strncasecmp("select",query,6) == 0){ + + if(res->query(query)){ + ret = FALSE; + if (pres){ + *pres = res; + } + } + else { + ret = TRUE; + GB.Error(error, conn->getErrorMsg()); + } + } + else { + if(res->exec(query)){ + ret = FALSE; + if (pres){ + *pres = res; + } + } + else { + ret = TRUE; + GB.Error(error, conn->getErrorMsg()); + } + } + + if (!pres) + res->close(); + + if (ret) + db->error = conn->lastError(); + else + db->error = 0; + + return ret; +} + +/* Internal function to check whether a file is a sqlite database file */ +static bool IsDatabaseFile (const char *filename) +{ + /* SQLite databases start with the string: + * ** This file contains an SQLite 2.1 database ** + * */ + FILE* fp; + bool res; + char magic_text[48]; + + fp = fopen(filename, "r"); + if (!fp) + return false; + + res = fread(magic_text, 1, 47, fp) == 47; + fclose(fp); + + if (!res) + return false; + + magic_text[47] = '\0'; + + if (strcmp(magic_text, "** This file contains an SQLite 2.1 database **")) + return false; + + return true; +} + +/* Internal function to locate database and return full qualified */ +/* path. */ +static char *FindDatabase (const char *name, const char *hostName) +{ + char *dbhome = NULL; + char *fullpath = NULL; + + /* Does Name includes fullpath */ + if (*name == '/') + { + if (IsDatabaseFile(name)) + fullpath = GB.NewZeroString(name); + + return fullpath; + } + + /* Hostname contains home area */ + fullpath = GB.NewZeroString(hostName); + fullpath = GB.AddChar(fullpath, '/'); + fullpath = GB.AddString(fullpath, name, 0); + if (IsDatabaseFile(fullpath)) + return fullpath; + + GB.FreeString(&fullpath); + + /* Check the GAMBAS_SQLITE_DBHOME setting */ + dbhome = getenv("GAMBAS_SQLITE_DBHOME"); + if (dbhome) + { + fullpath = GB.NewZeroString(dbhome); + fullpath = GB.AddChar(fullpath, '/'); + fullpath = GB.AddString(fullpath, name, 0); + + if (IsDatabaseFile(fullpath)) + return fullpath; + } + + #if 0 + /* Now check for database in current working directory */ + if (getcwd(cwd, MAX_PATH) == NULL){ + GB.Error("Unable to get databases: &1", "Can't find current directory"); + return NULL; + } + #endif + + fullpath = GB.NewZeroString(GB.TempDir()); + fullpath = GB.AddString(fullpath, "/sqlite/", 0); + fullpath = GB.AddString(fullpath, name, 0); + + if (IsDatabaseFile(fullpath)) + return fullpath; + + GB.FreeString(&fullpath); + return NULL; +} + + +/* Internal function to return database home directory */ +/* - GAMBAS_SQLITE_HOMEDB if set or temporary directory /tmp/gambas.%pid%/ */ + +static char *GetDatabaseHome() +{ + char *env = NULL; + char *dbhome = NULL; + + GB.Alloc(POINTER(&dbhome), MAX_PATH); + + /* Check for Environment variable */ + + env = getenv("GAMBAS_SQLITE_DBHOME"); + + /* if not set then set to current working directory */ + if (env == NULL){ + /* + if (getcwd(dbhome, MAX_PATH) == NULL){ + GB.Error("Unable to get databases: &1", "Can't find current directory"); + GB.Free((void **)&dbhome); + return NULL; + } + */ + + sprintf(dbhome, "%s/sqlite", GB.TempDir()); + } + else { + strcpy(dbhome, env); + } + + return dbhome; +} + + +// Internal function to walk a directory and list files +// Used by database_list + +static int WalkDirectory(const char *dir, char ***databases) +{ + DIR *dp; + struct dirent *entry; + struct stat statbuf; + char cwd[MAX_PATH]; + + if ((dp = opendir(dir)) == NULL) + return -1; + + if (getcwd(cwd, MAX_PATH) == NULL) + { + fprintf(stderr, "gb.db.sqlite2: warning: getcwd: %s\n", strerror(errno)); + return -1; + } + + if (chdir(dir)) + { + fprintf(stderr, "gb.db.sqlite2: warning: chdir: %s\n", strerror(errno)); + return -1; + } + + while ((entry = readdir(dp)) != NULL) + { + stat(entry->d_name, &statbuf); + + if (S_ISREG(statbuf.st_mode)) + { + if (IsDatabaseFile(entry->d_name)) + *(char **)GB.Add(databases) = GB.NewZeroString(entry->d_name); + } + } + + // BM: you must call closedir() + closedir(dp); + + if (chdir(cwd)) + fprintf(stderr, "gb.db.sqlite2: warning: chdir: %s\n", strerror(errno)); + + return GB.Count(databases); +} + +/* Internal function to check database version number */ +int db_version() +{ + //Check db version + int dbversion =0; + unsigned int verMain, verMajor, verMinor; + sscanf(sqlite_version,"%2u.%2u.%2u", &verMain, &verMajor, &verMinor); + dbversion = ((verMain * 10000) + (verMajor * 100) + verMinor); + return dbversion; +} + +/***************************************************************************** + + get_quote() + + Returns the character used for quoting object names. + +*****************************************************************************/ + +static const char *get_quote(void) +{ + return QUOTE_STRING; +} + +/***************************************************************************** + + open_database() + + Connect to a database. + + points at a structure describing each connection parameter. + + In Sqlite, there is no such thing as a host. If this is set then check + to see whether this is actually a path to a home area. NG 01/04/04 + + This function must return a database handle, or NULL if the connection + has failed. + +*****************************************************************************/ + +static int open_database(DB_DESC *desc, DB_DATABASE *db) +{ + SqliteDatabase *conn = new SqliteDatabase(); + char *name = NULL; + char *db_fullpath = NULL; + bool memory; + + /* connect by default to memory database */ + + if (desc->name) + { + name = GB.NewZeroString(desc->name); + memory = false; + } + else + { + name = GB.NewZeroString(":memory:"); + memory = true; + } + + if (desc->host) + conn->setHostName(desc->host); + + if (memory) + { + conn->setDatabase(name); + } + else if ((db_fullpath = FindDatabase(name, conn->getHostName())) != NULL) + { + conn->setDatabase(db_fullpath); + } + else + { + GB.Error("Unable to locate database: &1", name); + GB.FreeString(&name); + delete conn; + return TRUE; + } + + GB.FreeString(&name); + GB.FreeString(&db_fullpath); + + if ( conn->connect() != DB_CONNECTION_OK) + { + GB.Error("Cannot open database: &1", conn->getErrorMsg()); + conn->disconnect(); + delete conn; + return TRUE; + } + + /* Character set cannot be set for sqlite. A sqlite re-compile + * is required. */ + db->charset = GB.NewZeroString(strcmp(sqlite_encoding, "iso8859") == 0 ? "ISO-8859-1" : "UTF-8"); + + /* set dbversion */ + db->version = db_version(); + + /* flags */ + db->flags.no_table_type = TRUE; + db->flags.no_serial = TRUE; + db->flags.no_blob = TRUE; + db->flags.no_nest = TRUE; + db->flags.no_collation = TRUE; + + db->db_name_char = "."; + + db->handle = conn; + return FALSE; +} + + +/***************************************************************************** + + close_database() + + Terminates the database connection. + + contains the database handle. + +*****************************************************************************/ + +static void close_database(DB_DATABASE *db) +{ + SqliteDatabase *conn = (SqliteDatabase *)db->handle; + + if (conn) + { + conn->disconnect(); + delete conn; + } +} + + +/***************************************************************************** + + get_collations() + + Return the available collations as a Gambas string array. + +*****************************************************************************/ + +static GB_ARRAY get_collations(DB_DATABASE *db) +{ + return NULL; +} + + + +/***************************************************************************** + + format_value() + + This function transforms a gambas value into a string value that can + be inserted into a SQL query. + + points to the value. + is a callback called to insert the string into the query. + + This function must return TRUE if it translates the value, and FALSE if + it does not. + + If the value is not translated, then a default translation is used. + +*****************************************************************************/ + +static int format_value(GB_VALUE * arg, DB_FORMAT_CALLBACK add) +{ + char *s; + int i, l; + GB_DATE_SERIAL *date; + + switch (arg->type) + { + case GB_T_BOOLEAN: +/*Note this is likely to go to a tinyint */ + if (VALUE((GB_BOOLEAN *) arg)) + add("'1'", 3); + else + add("'0'", 3); + return TRUE; + + case GB_T_STRING: + case GB_T_CSTRING: + + s = VALUE((GB_STRING *)arg).addr + VALUE((GB_STRING *)arg).start; + l = VALUE((GB_STRING *)arg).len; + + add("'", 1); + + for (i = 0; i < l; i++, s++) + { + add(s, 1); + if (*s == '\'') + add(s, 1); + } + + add("'", 1); + + return TRUE; + + case GB_T_DATE: + + date = GB.SplitDate((GB_DATE *) arg); + + l = sprintf(_buffer, "'%04d-%02d-%02d %02d:%02d:%02d", + date->year, date->month, date->day, + date->hour, date->min, date->sec); + + add(_buffer, l); + + if (date->msec) + { + l = sprintf(_buffer, ".%03d", date->msec); + add(_buffer, l); + } + + add("'", 1); + + return TRUE; + + default: + return FALSE; + } +} + + +/***************************************************************************** + + format_blob() + + This function transforms a blob value into a string value that can + be inserted into a SQL query. + + points to the DB_BLOB structure. + is a callback called to insert the string into the query. + +*****************************************************************************/ + +static void format_blob(DB_BLOB *blob, DB_FORMAT_CALLBACK add) +{ +} + + +/***************************************************************************** + + exec_query() + + Send a query to the server and gets the result. + + is the database handle, as returned by open_database() + is the query string. + will receive the result handle of the query. + is an error message used when the query failed. + + can be NULL, when we don't care getting the result. + +*****************************************************************************/ + +static int exec_query(DB_DATABASE *db, const char *query, DB_RESULT *result, const char *err) +{ + return do_query(db, err, (Dataset **)result, query, 0); +} + + +/***************************************************************************** + + get_last_insert_id() + + Return the value of the last serial field used in an INSERT statement + + is the database handle, as returned by open_database() + +*****************************************************************************/ + +static int64_t get_last_insert_id(DB_DATABASE *db) +{ + GB.Error("Unsupported feature"); + return -1; +} + + +/***************************************************************************** + + query_init() + + Initialize an info structure from a query result. + + is the handle of the query result. + points to the info structure. + will receive the number of records returned by the query. + + This function must initialize the info->nfield field with the number of + field in the query result. + +*****************************************************************************/ + +static void query_init(DB_RESULT result, DB_INFO *info, int *count) +{ + Dataset *res = (Dataset *)result; + + if(res) + { + *count = res->num_rows(); + info->nfield = res->fieldCount(); + } + else { + *count = 0; + info->nfield = 0; + } +} + + +/***************************************************************************** + + query_release() + + Free the info structure filled by query_init() and the result handle. + + is the handle of the query result. + points to the info structure. + +*****************************************************************************/ + +static void query_release(DB_RESULT result, DB_INFO *info) +{ + ((Dataset *)result)->close(); +} + + +/***************************************************************************** + + query_fill() + + Fill a result buffer with the value of each field of a record. + + is the database handle, as returned by open_database() + is the handle of the result. + is the index of the record in the result. + points to an array having one element for each field in the + result. + is a boolean telling if we want the next row. + + This function must return DB_OK, DB_ERROR or DB_NO_DATA + + This function must use GB.StoreVariant() to store the value in the + buffer. + +*****************************************************************************/ + +#if 0 +static int query_fill(DB_DATABASE *db, DB_RESULT result, int pos, GB_VARIANT_VALUE *buffer, int next) +{ + Dataset *res = (Dataset *)result; + int i; + char *data; + GB_VARIANT value; + + if (!next) + res->seek(pos);/* move to record */ + else + res->next(); + + for ( i=0; i < res->fieldCount(); i++) + { + GB.NewZeroString( &data, res->fv(res->fieldName(i)).get_asString().data()); + + value.type = GB_T_VARIANT; + value.value._object.type = GB_T_NULL; + + //if (field->type != FIELD_TYPE_NULL) + if (data){ + conv_data(data, &value.value, (fType) res->fieldType(i)); + } + + GB.FreeString(&data); + GB.StoreVariant(&value, &buffer[i]); + } + + return FALSE; +} +#endif + +static int query_fill(DB_DATABASE *db, DB_RESULT result, int pos, GB_VARIANT_VALUE * buffer, int next) +{ + Dataset *res = (Dataset *) result; + int i; + const char *data; + GB_VARIANT value; + + if (!next) + res->seek(pos); + else + res->next(); + + for (i = 0; i < res->fieldCount(); i++) + { + if (res->fv(i).get_isNull()) + data = NULL; + else + data = res->fv(i).get_asString().data(); + + //fprintf(stderr, "query_fill: %d.%d %s\n", pos, i, data); + + value.type = GB_T_VARIANT; + value.value.type = GB_T_NULL; + + //if (field->type != FIELD_TYPE_NULL) + if (data) + conv_data(data, &value.value, (fType) res->fieldType(i)); + + //GB.FreeString(&data); + GB.StoreVariant(&value, &buffer[i]); + } + + return DB_OK; +} + + +/***************************************************************************** + + blob_read() + + Returns the value of a BLOB field. + + is the handle of the result. + is the index of the record in the result. + points at a DB_BLOB structure that will receive a pointer to the + data and its length. + + NOTE: this function is always called after query_fill() with the same + value of . + +*****************************************************************************/ + +static void blob_read(DB_RESULT result, int pos, int field, DB_BLOB *blob) +{ +} + + +/***************************************************************************** + + field_name() + + Return the name of a field in a result from its index. + + is the result handle. + is the field index. + +*****************************************************************************/ + +static char *field_name(DB_RESULT result, int field) +{ + return (char *)(((Dataset *)result)->fieldName(field)); +} + + +/***************************************************************************** + + field_index() + + Return the index of a field in a result from its name. + + is the result handle. + is the field name. + can be ignored by this driver. + +*****************************************************************************/ + +static int field_index(DB_RESULT result, const char *name, DB_DATABASE *db) +{ + char *fld; + + fld = strchr((char *)name, (int)FLD_SEP); + if (fld){ //Includes table identity + fld[0] = '.'; + } + return (((Dataset *)result)->fieldIndex(name)); +} + + +/***************************************************************************** + + field_type() + + Return the Gambas type of a field in a result from its index. + + is the result handle. + is the field index. + +*****************************************************************************/ + +static GB_TYPE field_type(DB_RESULT result, int field) +{ + return conv_type(((Dataset *)result)->fieldType(field)); +} + + +/***************************************************************************** + + field_length() + + Return the length of a field in a result from its index. + + is the result handle. + is the field index. + +*****************************************************************************/ + +static int field_length(DB_RESULT result, int field) +{ + int len; + len = ((Dataset *)result)->fieldSize(field); + GB_TYPE type = conv_type(((Dataset *)result)->fieldType(field)); + + if (type != GB_T_STRING) + return 0; + else + return len; +} + + +/***************************************************************************** + + begin_transaction() + + Begin a transaction. + + is the database handle. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + + In mysql commit/rollback can only be used with transaction safe tables (BDB, + or InnoDB tables) + + ISAM, MyISAM and HEAP tables will commit straight away. The transaction + methods are therefore ignored. + +*****************************************************************************/ + +static int begin_transaction(DB_DATABASE *db) +{ + return do_query(db, "Unable to begin transaction: &1", NULL, "BEGIN", 0); +} + + +/***************************************************************************** + + commit_transaction() + + Commit a transaction. + + is the database handle. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int commit_transaction(DB_DATABASE *db) +{ + return do_query(db, "Unable to commit transaction: &1", NULL, "COMMIT", 0); +} + + +/***************************************************************************** + + rollback_transaction() + + Rolllback a transaction. + + is the database handle. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int rollback_transaction(DB_DATABASE *db) +{ + return do_query(db, "Unable to rollback transaction: &1", NULL, "ROLLBACK", 0); +} + +/***************************************************************************** + + table_init() + + Initialize an info structure from table fields. + + is the database handle. +
    is the table name. + points at the info structure. + + This function must initialize the following info fields: + - info->nfield must contain the number of fields in the table. + - info->fields is a char*[] pointing at the name of each field. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int field_info(DB_DATABASE *db, const char *table, const char *field, DB_FIELD *info); + +static int table_init(DB_DATABASE *db, const char *table, DB_INFO *info) +{ + const char *qfield = "PRAGMA table_info('&1')"; + + Dataset *res; + int i, n; + DB_FIELD *f; + const char *field; + + /* Nom de la table */ + + info->table = GB.NewZeroString(table); + + /* Liste des champs */ + + if (do_query(db, "Unable to get table fields: &1", &res, qfield, 1, table)) + return TRUE; + + result_set* r = (result_set*) res->getExecRes(); + + info->nfield = n = r->records.size(); + if (n == 0){ + res->close(); + return TRUE; + } + + GB.Alloc(POINTER(&info->field), sizeof(DB_FIELD) * n); + + + for (i = 0; i < n; i++) + { + f = &info->field[i]; + field = r->records[i][1].get_asString().data(); + + if (field_info(db, table, field, f)) + { + res->close(); + return TRUE; + } + + f->name = GB.NewZeroString(field); + /*f->length = 0; + f->type = conv_type(GetFieldType((char *) r->records[i][2].get_asString().data(), (unsigned int *) &f->length));*/ + } + + res->close(); + + return FALSE; +} + +/***************************************************************************** + + table_index() + + Initialize an info structure from table primary index. + + is the database handle. +
    is the table name. + points at the info structure. + + This function must initialize the following info fields: + - info->nindex must contain the number of fields in the primary index. + - info->index is a int[] giving the index of each index field in + info->fields. + + This function must be called after table_init(). + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_index(DB_DATABASE *db, const char *table, DB_INFO *info) +{ + const char *qindex1 = "PRAGMA index_list('&1')"; + const char *qindex2 = "PRAGMA index_info('&1')"; + + Dataset *res; + char *sql = NULL; + int n = 0; + + /* Index primaire */ + + if (do_query(db, "Unable to get primary index: &1", &res, qindex1, 1, table)) + return TRUE; + + result_set* r = (result_set*) res->getExecRes(); + if (( n = r->records.size()) <= 0){ + /* no indexes returned */ + /* sqlite will use a PRIMARY INDEX on ROWID */ + /* BM: without a true primary index, Edit() does not work */ + GB.Error("Table '&1' has no primary index", table); + res->close(); + return TRUE; + } + + for (int i = 0; i < n; i++) + { + if (strstr(r->records[i][1].get_asString().data(), "autoindex") != NULL){ + sql = GB.NewZeroString(r->records[i][1].get_asString().data()); + res->close(); + + if (do_query(db, "Unable to get information on primary index: &1", &res, qindex2, 1, sql)){ + res->close(); + GB.FreeString(&sql); + return TRUE; + } + GB.FreeString(&sql); + + r = (result_set*) res->getExecRes(); + info->nindex = r->records.size(); + GB.Alloc(POINTER(&info->index), sizeof(int) * info->nindex); + + for (i = 0; i < info->nindex; i++){ + info->index[i] = r->records[i][1].get_asInteger(); + } + break; + } + } + + res->close(); + + return FALSE; +} + + +/***************************************************************************** + + table_release() + + Free the info structure filled by table_init() and/or table_index() + + is the database handle. + points at the info structure. + +*****************************************************************************/ + +static void table_release(DB_DATABASE *db, DB_INFO *info) +{ + /* All is done outside the driver */ +} + +/***************************************************************************** + + table_exist() + + Returns if a table exists + + is the database handle. +
    is the table name. + + This function returns TRUE if the table exists, and FALSE if not. + +*****************************************************************************/ + +static int table_exist(DB_DATABASE *db, const char *table) +{ + + const char *query = "select tbl_name from " + "( select tbl_name from sqlite_master where type = 'table' union " + "select tbl_name from sqlite_temp_master where type = 'table' ) " + "where tbl_name = '&1'"; + + if ( strcmp(table,"sqlite_master") == 0 || strcmp(table,"sqlite_temp_master") == 0){ + return TRUE; + } + + Dataset *res; + int exist; + + if (do_query(db, "Unable to check table: &1", &res, query, 1, table)) + return FALSE; + + exist = res->num_rows(); + res->close(); + return exist; +} + +/***************************************************************************** + + table_list() + + Returns an array containing the name of each table in the database + + is the database handle. + points to a variable that will receive the char* array. + + This function returns the number of tables, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int table_list(DB_DATABASE *db, char ***tables) +{ + const char *query = + "select tbl_name from " + "( select tbl_name from sqlite_master where type = 'table' union " + " select tbl_name from sqlite_temp_master where type = 'table')"; + + Dataset *res; + int rows; + int i = 0; + + if (do_query(db, "Unable to get tables: &1", &res, query, 0)) + return -1; + + rows = res->num_rows(); + GB.NewArray(tables, sizeof(char *), rows + 2);//sqlite_master and sqlite_temp_master need to be + //added to the list + + while ( !res->eof()){ + (*tables)[i] = GB.NewZeroString(res->fv("tbl_name").get_asString().data()); + res->next(); + i++; + } + + + res->close(); + + (*tables)[i] = GB.NewZeroString("sqlite_master"); + (*tables)[++i] = GB.NewZeroString("sqlite_temp_master"); + return rows; +} + +/***************************************************************************** + + table_primary_key() + + Returns a string representing the primary key of a table. + + is the database handle. +
    is the table name. + points to a string that will receive the primary key. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_primary_key(DB_DATABASE *db, const char *table, char ***primary) +{ + const char *qindex1 = "PRAGMA index_list('&1')"; + const char *qindex2 = "PRAGMA index_info('&1')"; + + Dataset *res; + int i, n; + char *sql; + + if (do_query(db, "Unable to get primary key: &1", &res, qindex1, 1, table)) + return TRUE; + + GB.NewArray(primary, sizeof(char *), 0); + + result_set* r = (result_set*) res->getExecRes(); + //if ((n = r->records.size()) < 1){ //No Indexes found + // GB.NewString((char **)GB.Add(primary), "ROWID", 0); + // res->close(); + // return FALSE; + //} + + n = r->records.size(); + for (i = 0; i < n; i++) + { + if (strstr(r->records[i][1].get_asString().data(), "autoindex")){ + sql = GB.NewZeroString(r->records[i][1].get_asString().data()); + res->close(); + + if (do_query(db, "Unable to get primary key: &1", &res, qindex2, 1, sql)){ + res->close(); + GB.FreeString(&sql); + return TRUE; + } + GB.FreeString(&sql); + + r = (result_set*) res->getExecRes(); + if ((n = r->records.size()) < 1 ){ + // No information returned for key + res->close(); + return TRUE; + } + + for (i = 0; i < n; i++) + *(char **)GB.Add(primary) = GB.NewZeroString(r->records[i][2].get_asString().data()); + + break; + } + } + + res->close(); +/* + if (GB.Count(*primary) == 0){ + GB.NewString((char **)GB.Add(primary), "ROWID", 0); + } +*/ + return FALSE; +} + +/***************************************************************************** + + table_is_system() + + Returns if a table is a system table. + + is the database handle. +
    is the table name. + + This function returns TRUE if the table is a system table, and FALSE if + not. + + Note: There are only two tables in Sqlite used by the system. + sqlite_master and sqlite_temp_master + +*****************************************************************************/ + +static int table_is_system(DB_DATABASE *db, const char *table) +{ + if (strcmp(table,"sqlite_master") == 0 || + strcmp(table, "sqlite_temp_master") == 0) + return TRUE; + else + return FALSE; +} + +/***************************************************************************** + + table_type() + Not Valid in Sqlite + + is the database handle. +
    is the table name. +*****************************************************************************/ +static char *table_type(DB_DATABASE *db, const char *table, const char *type) +{ + if (type) + GB.Error("SQLite does not have any table types"); + return NULL; +} + +/***************************************************************************** + + table_delete() + + Deletes a table. + + is the database handle. +
    is the table name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_delete(DB_DATABASE *db, const char *table) +{ + return + do_query(db, "Unable to delete table: &1", NULL, + "drop table '&1'", 1, table); +} + +/***************************************************************************** + + table_create() + + Creates a table. + + is the database handle. +
    is the table name. + points to a linked list of field descriptions. + is the primary key. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_create(DB_DATABASE *db, const char *table, DB_FIELD *fields, char **primary, const char *not_used) +{ + DB_FIELD *fp; + int comma; + const char *type; + int i; + + DB.Query.Init(); + + DB.Query.Add("CREATE TABLE "); + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(table); + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(" ( "); + + comma = FALSE; + for (fp = fields; fp; fp = fp->next) + { + if (comma) + DB.Query.Add(", "); + else + comma = TRUE; + + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(fp->name); + DB.Query.Add(QUOTE_STRING); + + switch (fp->type) + { + case GB_T_BOOLEAN: type = "BOOL"; break; + case GB_T_INTEGER: type = "INT4"; break; + case GB_T_LONG: type = "BIGINT"; break; + case GB_T_FLOAT: type = "FLOAT8"; break; + case GB_T_DATE: type = "DATETIME"; break; + case GB_T_STRING: + + if (fp->length <= 0) + type = "TEXT"; + else + { + sprintf(_buffer, "VARCHAR(%d)", fp->length); + type = _buffer; + } + + break; + + default: type = "TEXT"; break; + } + + DB.Query.Add(" "); + DB.Query.Add(type); + + if (fp->def.type != GB_T_NULL) + { + DB.Query.Add(" NOT NULL DEFAULT "); + DB.FormatVariant(&_driver, &fp->def, DB.Query.AddLength); + } + else if (DB.StringArray.Find(primary, fp->name) >= 0) + { + DB.Query.Add(" NOT NULL "); + } + } + + if (primary) + { + DB.Query.Add(", PRIMARY KEY ("); + + for (i = 0; i < GB.Count(primary); i++) + { + if (i > 0) + DB.Query.Add(","); + + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(primary[i]); + DB.Query.Add(QUOTE_STRING); + } + + DB.Query.Add(")"); + } + + DB.Query.Add(" )"); + + return do_query(db, "Cannot create table: &1", NULL, DB.Query.Get(), 0); +} + +/***************************************************************************** + + field_exist() + + Returns if a field exists in a given table + + is the database handle. +
    is the table name. + is the field name. + + This function returns TRUE if the field exists, and FALSE if not. + +*****************************************************************************/ + +static int field_exist(DB_DATABASE *db, const char *table, const char *field) +{ + const char *query = "PRAGMA table_info('&1')"; + + int i, n; + Dataset *res; + int exist = 0; + + if (do_query(db, "Unable to find field: &1.&2", &res, query, 2, table, field)){ + return FALSE; + } + + result_set* r = (result_set*) res->getExecRes(); + + n = r->records.size(); + + for (i = 0; i < n; i++){ + if (strcmp(field, r->records[i][1].get_asString().data()) == 0) + exist++; + } + + res->close(); + return exist; +} + +/***************************************************************************** + + field_list() + + Returns an array containing the name of each field in a given table + + is the database handle. +
    is the table name. + points to a variable that will receive the char* array. + + This function returns the number of fields, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int field_list(DB_DATABASE *db, const char *table, char ***fields) +{ + const char *query = "PRAGMA table_info('&1')"; + + int i, n; + Dataset *res; + + if (do_query(db, "Unable to get fields: &1", &res, query, 1, table)){ + return -1; + } + + result_set* r = (result_set*) res->getExecRes(); + + n = r->records.size(); + + if (fields) /* (BM) see the function commentary */ + { + GB.NewArray(fields, sizeof(char *), n); + + for (i = 0; i < n; i++){ + (*fields)[i] = GB.NewZeroString(r->records[i][1].get_asString().data()); + } + } + + res->close(); + return n; +} + + +/***************************************************************************** + + field_info() + + Get field description + + is the database handle. +
    is the table name. + is the field name. + points to a structure filled by the function. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int field_info(DB_DATABASE *db, const char *table, const char *field, DB_FIELD *info) +{ + const char *query = "PRAGMA table_info('&1')"; + + Dataset *res; + GB_VARIANT def; + char *val; + char *_fieldName = NULL; + char *_fieldType = NULL; + char *_defaultValue = NULL; + bool _fieldNotNull = FALSE; + int i, n; + fType type; + + if (do_query(db, "Unable to get fields: &1", &res, query,1, table)) + { + return TRUE; + } + + result_set *r = (result_set*) res->getExecRes(); + if ((n = r->records.size()) == 0) + { + GB.Error("Unable to find field &1.&2", table, field); + return TRUE; + } + + for ( i =0; i < n; i++){ + _fieldName = (char *)r->records[i][1].get_asString().data(); + + if (strcmp(_fieldName, field) == 0){ + _fieldType = (char *)r->records[i][2].get_asString().data(); + _fieldNotNull = (char *)r->records[i][3].get_asBool(); + _defaultValue = (char *)r->records[i][4].get_asString().data(); + break; + } + } + + if (i >= n) + { + GB.Error("Unable to find field &1.&2", table, field); + return TRUE; + } + + info->name = NULL; + + type = GetFieldType(_fieldType, (unsigned int *) &info->length); + info->type = conv_type(type); + + info->def.type = GB_T_NULL; + + if ( _fieldNotNull) + { + def.type = GB_T_VARIANT; + def.value.type = GB_T_NULL; + + val = _defaultValue; + + if (val && *val) + { + conv_data(val, &def.value, type); + GB.StoreVariant(&def, &info->def); + } + } + + info->collation = NULL; + + res->close(); + return FALSE; +} + +/***************************************************************************** + + index_exist() + + Returns if an index exists in a given table + + is the database handle. +
    is the table name. + is the index name. + + This function returns TRUE if the index exists, and FALSE if not. + +*****************************************************************************/ + +static int index_exist(DB_DATABASE *db, const char *table, const char *index) +{ + const char *query = "select tbl_name from " + "( select tbl_name from sqlite_master where type = 'index' and " + " name = '&2' union " + "select tbl_name from sqlite_temp_master where type = 'index' and " + " name = '&2' ) " + "where tbl_name = '&1'"; + + Dataset *res; + int exist; + + if (do_query(db, "Unable to check table: &1", &res, query, 2, table, index)) + return FALSE; + + exist = res->num_rows(); + res->close(); + return exist; +} + +/***************************************************************************** + + index_list() + + Returns an array containing the name of each index in a given table + + is the database handle. +
    is the table name. + points to a variable that will receive the char* array. + + This function returns the number of indexes, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int index_list(DB_DATABASE *db, const char *table, char ***indexes) +{ + + const char *query = + "select name from " + "( select name from sqlite_master where type = 'index' and tbl_name = '&1' " + " union select name from sqlite_temp_master where type = 'index' and " + " tbl_name = '&1')"; + + Dataset *res; + int no_indexes, i = 0; + + if (do_query(db, "Unable to get tables: &1", &res, query, 1, table)) + return -1; + + no_indexes = res->num_rows(); + GB.NewArray(indexes, sizeof(char *), no_indexes); + + while ( !res->eof()){ + //(res->fv("tbl_name").get_asString().data()); + (*indexes)[i] = GB.NewZeroString(res->fv(res->fieldName(0)).get_asString().data()); + res->next(); + i++; + } + + res->close(); + + return no_indexes; +} + +/***************************************************************************** + + index_info() + + Get index description + + is the database handle. +
    is the table name. + is the index name. + points to a structure filled by the function. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int index_info(DB_DATABASE *db, const char *table, const char *index, DB_INDEX *info) +{ + /* sqlite indexes are unique to the database, and not to the table */ + + const char *qindex1 = "PRAGMA index_list('&1')"; + const char *qindex2 = "PRAGMA index_info('&1')"; + + Dataset *res; + int i, j, n; + + if (do_query(db, "Unable to get index info for table: &1", &res, qindex1, 1, table)) + return TRUE; + + result_set* r = (result_set*) res->getExecRes(); + if (( n = r->records.size()) == 0){ + /* no indexes found */ + res->close(); + GB.Error("Unable to find index &1.&2", table, index); + return TRUE; + } + + for (i = 0, j = 0; i < n; i++) + { /* Find the required index */ + if ( strcmp( index, r->records[i][1].get_asString().data()) == 0){ + j++; + break; + } + } + + if ( j == 0) + { + GB.Error("Unable to find index &1.&2", table, index); + return TRUE; + } + + info->name = NULL; + info->unique = strncmp("1",r->records[i][2].get_asString().data(),1) == 0 ? TRUE : FALSE; + info->primary = strstr(r->records[i][1].get_asString().data(), "autoindex") + != NULL ? TRUE : FALSE; + + DB.Query.Init(); + + if (do_query(db, "Unable to get index info for : &1", &res, qindex2, 1, index)){ + res->close(); + return TRUE; + } + + r = (result_set*) res->getExecRes(); + n = r->records.size(); + i = 0; + /* (BM) row can be null if we are seeking the last index */ + while ( i < n ) + { + if (i > 0) + DB.Query.Add(","); + + /* Get fields in key */ + DB.Query.Add(r->records[i][2].get_asString().data()); + i++; + } + + res->close(); + info->fields = DB.Query.GetNew(); + + return FALSE; +} + +/***************************************************************************** + + index_delete() + + Deletes an index. + + is the database handle. +
    is the table name. + is the index name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int index_delete(DB_DATABASE *db, const char *table, const char *index) +{ + return + do_query(db, "Unable to delete index: &1", NULL, + "drop index '&1'", 1, index); +} + +/***************************************************************************** + + index_create() + + Creates an index. + + is the database handle. +
    is the table name. + is the index name. + points to a structure describing the index. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int index_create(DB_DATABASE *db, const char *table, const char *index, DB_INDEX *info) +{ + DB.Query.Init(); + + DB.Query.Add("CREATE "); + if (info->unique) + DB.Query.Add("UNIQUE "); + DB.Query.Add("INDEX "); + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(index); + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(" ON "); + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(table); + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(" ( "); + DB.Query.Add(info->fields); + DB.Query.Add(" )"); + + return do_query(db, "Cannot create index: &1", NULL, DB.Query.Get(), 0); +} + +/***************************************************************************** +* +* database_exist() +* +* Returns if a database exists +* +* is any database handle. +* is the database name. +* +* This function returns TRUE if the database exists, and FALSE if not. +* SQLite: Databases are just files, so we need to ceck to see whether +* the file exists and is a sqlite file. +******************************************************************************/ +static int database_exist(DB_DATABASE *db, const char *name) +{ + SqliteDatabase *conn = (SqliteDatabase *)db->handle; + char *fullpath = NULL; + + if (strcmp(name,":memory:") == 0) + return TRUE; //Database is loaded in memory only + + if((fullpath = FindDatabase(name, conn->getHostName())) != NULL){ + GB.FreeString(&fullpath); + return TRUE; + } + + GB.FreeString(&fullpath); + return FALSE; +} + +/***************************************************************************** +* +* database_list() +* +* Returns an array containing the name of each database +* +* is any database handle. +* points to a variable that will receive the char* array. +* +* This function returns the number of databases, or -1 if the command has +* failed. +* +* Be careful: can be NULL, so that just the count is returned. +* +* Sqlite databases are files. Using we will only list +* files within a designated directory. Propose that all +* areas are walked through. +******************************************************************************/ + +static int database_list(DB_DATABASE *db, char ***databases) +{ + char *dbhome; + + SqliteDatabase *conn = (SqliteDatabase *)db->handle; + GB.NewArray(databases, sizeof(char *), 0); + + /* Hostname contains home area */ + dbhome = (char *)conn->getHostName(); + if (dbhome && *dbhome) + { + WalkDirectory( dbhome, databases ); + } + else + { + /* Checks GAMBAS_SQLITE_DBHOME if set, or Current Working Directory */ + /* Might have to come back and seperate */ + dbhome = GetDatabaseHome(); + if (dbhome){ + //GB.Error("Unable to get databases: &1", "Can't find current directory"); + WalkDirectory( dbhome, databases ); + GB.Free(POINTER(&dbhome)); + } + } + + return GB.Count(databases); +} + +/***************************************************************************** +* +* database_is_system() +* +* Returns if a database is a system database. +* +* is any database handle. +* is the database name. +* +* This function returns TRUE if the database is a system database, and +* FALSE if not. +* +* NOTE: Sqlite doesn't have such a thing. +* +******************************************************************************/ + +static int database_is_system(DB_DATABASE *db, const char *name) +{ + return FALSE; +} + +/***************************************************************************** +* +* database_delete() +* +* Deletes a database. +* +* is the database handle. +* is the database name. +* +* This function returns TRUE if the command has failed, and FALSE if +* everything was OK. +* +******************************************************************************/ + +static int database_delete(DB_DATABASE *db, const char *name) +{ + char *fullpath = NULL; + SqliteDatabase *conn = (SqliteDatabase *)db->handle; + + if((fullpath = FindDatabase(name, conn->getHostName())) == NULL){ + GB.FreeString(&fullpath); + GB.Error("Cannot Find database: &1", name); + return TRUE; + } + + if( remove(fullpath) != 0){ + GB.Error("Unable to delete database &1", fullpath); + GB.FreeString(&fullpath); + return TRUE; + } + + GB.FreeString(&fullpath); + return FALSE; +} + +/***************************************************************************** +* +* database_create() +* +* Creates a database. +* +* is the database handle. +* is the database name. +* +* This function returns TRUE if the command has failed, and FALSE if +* everything was OK. +* +* SQLite automatically creates a database on connect if the file +* does not exist. +******************************************************************************/ + +static int database_create(DB_DATABASE *db, const char *name) +{ + SqliteDatabase *conn = (SqliteDatabase *)db->handle; + SqliteDatabase conn2; + char *fullpath = NULL; + //char *homepath = NULL; + //DIR *dp; + char *dir; + const char *host; + + /* Does Name include fullpath */ + /* BM: do not use basename(), it can modify its argument */ + if (name && name[0] == '/') + { + fullpath = GB.NewZeroString(name); + goto _CREATE_DATABASE; + } + + /* Hostname contains home area */ + //if ((dp = opendir(conn->getHostName()))) + /* BM: Why use opendir(), and why forget to call closedir() ? */ + host = conn->getHostName(); + if (host && host[0]) + { + fullpath = GB.NewZeroString(host); + } + else + { + dir = GetDatabaseHome(); + mkdir(dir, S_IRWXU); + fullpath = GB.NewZeroString(dir); + GB.Free(POINTER(&dir)); + } + + if (fullpath[strlen(fullpath) - 1] != '/') + fullpath = GB.AddChar(fullpath, '/'); + + fullpath = GB.AddString(fullpath, name, 0); + +_CREATE_DATABASE: + + conn2.setDatabase(fullpath); + + GB.FreeString(&fullpath); + if ( conn2.connect() != DB_CONNECTION_OK){ + GB.Error("Cannot create database: &1", conn2.getErrorMsg()); + conn2.disconnect(); + return TRUE; + } + + //Create and remove a table to initialize database + db->handle = &conn2; + if (!do_query(db, "Unable to initialise database", NULL, "CREATE TABLE GAMBAS (FIELD1 TEXT)", 0)) + do_query(db, NULL, NULL, "DROP TABLE GAMBAS",0); + + conn2.disconnect(); + db->handle = conn; + + return FALSE; +} + + +/***************************************************************************** +* +* user_exist() +* +* Returns if a user exists. +* +* is any database handle. +* is the user name. +* +* This function returns TRUE if the user exists, and FALSE if not. +* Sqlite does not have different users. Access is controlled by +* access rightd on the file. +* We can check that the user exists on the machine and has access to +* database file! +* [Currently only checks against /etc/passwd. +* Does not check against /etc/shadow or pam. +* +******************************************************************************/ + +static int user_exist(DB_DATABASE *db, const char *name) +{ + struct stat dbbuf; + struct passwd *fileowner, *user; // /etc/passwd structure + struct group *Group; // /etc/group structure + char **Member; + const char *Databasefile; + bool in_memory; + + SqliteDatabase *conn = (SqliteDatabase *)db->handle; + + if (( Databasefile = conn->getDatabase()) == NULL){ + GB.Error("User_exist:&1", "Unable to get databasename"); + return FALSE; + } + + in_memory = strcmp(Databasefile, ":memory:") == 0; + + /* Is username in passwd file */ + if ((user = getpwnam(name)) == NULL){ + return FALSE; + } + + if (in_memory) + return (user->pw_uid == getuid()); + + if (stat(Databasefile, &dbbuf) != 0) + { + GB.Error("User_exist: Unable to get status of &1", Databasefile); + return FALSE; + } + + /* Now what are the database file permissions */ + /* The order of tests is important */ + + if (( fileowner = getpwuid(dbbuf.st_uid)) != NULL){ + if ( fileowner->pw_uid == user->pw_uid ){ + /* User is owner */ + if (( dbbuf.st_mode & S_IRUSR ) || (dbbuf.st_mode & S_IWUSR)){ + return TRUE; + } + else { + return FALSE; + } + } + if ( fileowner->pw_gid == user->pw_gid ){ + if (( dbbuf.st_mode & S_IRGRP ) || (dbbuf.st_mode & S_IWGRP)){ + /* User has access to the file via primary group access.*/ + return TRUE; + } + else { + return FALSE; + } + } + + } + + /* Check whether user is in the same group */ + + Group = getgrgid(dbbuf.st_gid); + Member = Group->gr_mem; + while (Member && *Member){ + if (strcmp(*Member, name) == 0){ + //User is a member of the group + if (( dbbuf.st_mode & S_IRGRP ) || (dbbuf.st_mode & S_IWGRP)){ + return TRUE; + } + else { + return FALSE; + } + } + Member++; + } + + if (( dbbuf.st_mode & S_IROTH ) || (dbbuf.st_mode & S_IWOTH)){ + /* Any user can access this file */ + return TRUE; + } + + return FALSE; + +} + +/***************************************************************************** +* +* user_list() +* +* Returns an array containing the name of each user. +* +* is the database handle. +* points to a variable that will receive the char* array. +* +* This function returns the number of users, or -1 if the command has +* failed. +* +* Be careful: can be NULL, so that just the count is returned. +* Sqlite does not have users. +* +******************************************************************************/ + + +static int user_list(DB_DATABASE *db, char ***users) +{ + //Should we use GB.HashTable.New etc. to ensure + //duplicates are not reported back, then transfer + //the valid elements to the Array. + //Need to check order of rights. e.g. User overides + //group and other, group overides other. + //That is if the user is a member of a group that + //is given no rights, but other has all rights, + //they should not be able to access + char *Databasefile; + struct stat buf; + struct passwd *user; // /etc/passwd structure + struct group *Group; // /etc/group structure + char **Member; + int Count = 0; + bool in_memory; + + SqliteDatabase *conn = (SqliteDatabase *)db->handle; + + if (( Databasefile = (char *)conn->getDatabase()) == NULL){ + GB.Error("Unable to get databasename"); + return -1; + } + + in_memory = strcmp(Databasefile, ":memory:") == 0; + + if (in_memory) + { + buf.st_mode = S_IWUSR | S_IRUSR; + buf.st_uid = getuid(); + } + else if (stat(Databasefile, &buf) != 0) + { + GB.Error("Unable to get status of &1", Databasefile); + return -1; + } + + if (users) + GB.NewArray(users, sizeof(char *), 0); + + if (!in_memory) + { + + /* If file has other access then any user could use it */ + if (( buf.st_mode & S_IROTH ) || (buf.st_mode & S_IWOTH)){ + /* Any user can access this file */ + + while (( user = getpwent()) != NULL ){ + if (users){ + *(char **)GB.Add(users) = GB.NewZeroString(user->pw_name); + } + else { + Count++; + } + } + + if (users){ + return GB.Count(users); + } + else { + return Count; + } + } + + /* If group access then add all users in the group */ + if (( buf.st_mode & S_IRGRP ) || (buf.st_mode & S_IWGRP)){ + + Group = getgrgid(buf.st_gid); + Member = Group->gr_mem; + while (Member && *Member){ + if (users){ + *(char **)GB.Add(users) = GB.NewZeroString(*Member); + } + else { + Count++; + } + Member++; + } + } + + } + + /* Don't forget the owner if that has not already been added */ + if (( buf.st_mode & S_IRUSR ) || (buf.st_mode & S_IWUSR)){ + if (( user = getpwuid(buf.st_uid)) != NULL){ + if (users){ + *(char **)GB.Add(users) = GB.NewZeroString(user->pw_name); + } + else { + Count++; + } + } + } + + if (users) + return GB.Count(users); + else + return Count; +} + +/***************************************************************************** +* +* user_info() +* +* Get user description +* +* is the database handle. +* is the user name. +* points to a structure filled by the function. +* +* This function returns TRUE if the command has failed, and FALSE if +* everything was OK. +* +* Sqlite privileges are just file privileges. We will return Admin +* rights where privilege allows Write. There is no password. +* +******************************************************************************/ + +static int user_info(DB_DATABASE *db, const char *name, DB_USER *info ) +{ + char *Databasefile; + //struct stat buf; + struct passwd *user; // /etc/passwd structure + bool in_memory; + //struct group *Group; // /etc/group structure + //char **Member; + //int Count; + + SqliteDatabase *conn = (SqliteDatabase *)db->handle; + + /* Is username in passwd file */ + if ((user = getpwnam(name)) == NULL){ + GB.Error("User_info: Invalid user &1", name); + return TRUE; + } + + if (( Databasefile = (char *)conn->getDatabase()) == NULL){ + GB.Error("User_info: &1", "Unable to get databasename"); + return TRUE; + } + + in_memory = strcmp(Databasefile, ":memory:") == 0; + + if (in_memory) + info->admin = true; + else + info->admin = access(Databasefile, W_OK); + + /* If file has other access then any user could use it */ + //Need to look at order of checks + info->name = NULL; + +// if (row[3]) //password does not exist for sqlite +// GB.NewString(&info->password, row[3], 0); //password is encrypted in mysql + + return FALSE; +} + +/***************************************************************************** + + user_delete() + + Deletes a user. + + is any database handle. + is the user name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + + Sqlite users are operated by the O/S +*****************************************************************************/ + +static int user_delete(DB_DATABASE *db, const char *name) +{ + GB.Error("SQLite users do not exist."); + return TRUE; +} + +/***************************************************************************** +* +* user_create() +* +* Creates a user. +* +* is the database handle. +* is the user name. +* points to a structure describing the user. +* +* This function returns TRUE if the command has failed, and FALSE if +* everything was OK. +* +* +* Sqlite: No user create +******************************************************************************/ + +static int user_create(DB_DATABASE *db, const char *name, DB_USER *info) +{ + GB.Error("SQLite users do not exist."); + return TRUE; +} + +/***************************************************************************** +* +* user_set_password() +* +* Change the user password. +* +* is the database handle. +* is the user name. +* is the new password +* +* This function returns TRUE if the command has failed, and FALSE if +* everything was OK. +* +* Sqlite : No user passwords. +******************************************************************************/ + +static int user_set_password(DB_DATABASE *db, const char *name, const char *password) +{ + GB.Error("SQLite users do not exist."); + return TRUE; +} + + +/***************************************************************************** + + The component entry and exit functions. + +*****************************************************************************/ + +extern "C" { +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.db", DB_INTERFACE_VERSION, &DB); + DB.Register(&_driver); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +} //extern "C" diff --git a/gb.db.sqlite2/src/main.h b/gb.db.sqlite2/src/main.h new file mode 100644 index 00000000..564a97bb --- /dev/null +++ b/gb.db.sqlite2/src/main.h @@ -0,0 +1,44 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +extern "C" { +#include "gambas.h" +#include "gb_common.h" +#include "../gb.db.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern DB_INTERFACE DB; +#endif +} + +#define QUOTE_STRING "'" + +#define MAX_PATH 132 /* MAX LENGTH OF FILENAME PATH */ + +#include "gb.db.proto.h" + +#endif /* __MAIN_H */ diff --git a/gb.db.sqlite2/src/qry_dat.cpp b/gb.db.sqlite2/src/qry_dat.cpp new file mode 100644 index 00000000..e7672894 --- /dev/null +++ b/gb.db.sqlite2/src/qry_dat.cpp @@ -0,0 +1,469 @@ +/*************************************************************************** + + qry_dat.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/********************************************************************** + * Copyright (c) 2002, Leo Seib, Hannover + * + * Project: C++ Dynamic Library + * Module: FieldValue class realisation file + * Author: Leo Seib E-Mail: lev@almaty.pointstrike.net + * Begin: 5/04/2002 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **********************************************************************/ + +#include "qry_dat.h" + +extern "C" { +#include "gambas.h" +extern GB_INTERFACE GB; +} + + + +//Constructors +field_value::field_value() +{ + str_value = ""; + len = 0; + field_type = ft_String; + is_null = true; +}; + + +//empty destructor +field_value::~field_value() +{ +} + + +string field_value::get_asString() const +{ + return str_value; +}; + +bool field_value::get_asBool() const +{ + return (str_value != "" && str_value != "0"); +}; + +int field_value::get_asInteger() const +{ + return atoi(str_value.data()); +}; + + + #if 0 + char field_value::get_asChar() const + { + switch (field_type) + { + case ft_String: + { + return str_value[0]; + } + case ft_Boolean: + { + char c; + + if (bool_value) + return c = 'T'; + else + return c = 'F'; + } + case ft_Char: + { + return char_value; + } + case ft_Short: + { + char t[ft_Short_Length]; + + sprintf(t, "%i", short_value); + return t[0]; + } + case ft_UShort: + { + char t[ft_Short_Length]; + + sprintf(t, "%i", ushort_value); + return t[0]; + } + case ft_Long: + { + char t[ft_Long_Length]; + + sprintf(t, "%ld", long_value); + return t[0]; + } + case ft_ULong: + { + char t[ft_Long_Length]; + + sprintf(t, "%lu", ulong_value); + return t[0]; + } + case ft_Float: + case ft_Double: + { + char t[ft_Double_Length]; + + sprintf(t, "%f", double_value); + return t[0]; + } + default: + { + return str_value[0]; + } + } + }; + + + short field_value::get_asShort() const + { + switch (field_type) + { + case ft_String: + { + return (short) atoi(str_value.c_str()); + } + case ft_Boolean: + { + return (short) bool_value; + } + case ft_Char: + { + return (short) char_value; + } + case ft_Short: + { + return short_value; + } + case ft_UShort: + { + return (short) ushort_value; + } + case ft_Long: + { + return (short) long_value; + } + case ft_ULong: + { + return (short) ulong_value; + } + case ft_Float: + case ft_Double: + { + return (short) double_value; + } + default: + { + return (short) atoi(str_value.c_str()); + } + } + }; + + + unsigned short field_value::get_asUShort() const + { + switch (field_type) + { + case ft_String: + { + return (unsigned short) atoi(str_value.c_str()); + } + case ft_Boolean: + { + return (unsigned short) bool_value; + } + case ft_Char: + { + return (unsigned short) char_value; + } + case ft_Short: + { + return (unsigned short) short_value; + } + case ft_UShort: + { + return ushort_value; + } + case ft_Long: + { + return (unsigned short) long_value; + } + case ft_ULong: + { + return (unsigned short) ulong_value; + } + case ft_Float: + case ft_Double: + { + return (unsigned short) double_value; + } + default: + { + return (unsigned short) atoi(str_value.c_str()); + } + } + }; + + long field_value::get_asLong() const + { + switch (field_type) + { + case ft_String: + { + return (long) atoi(str_value.c_str()); + } + case ft_Boolean: + { + return (long) bool_value; + } + case ft_Char: + { + return (long) char_value; + } + case ft_Short: + { + return (long) short_value; + } + case ft_UShort: + { + return (long) ushort_value; + } + case ft_Long: + { + return long_value; + } + case ft_ULong: + { + return (long) ulong_value; + } + case ft_Float: + case ft_Double: + { + return (long) double_value; + } + default: + { + return (long) atoi(str_value.c_str()); + } + } + }; + + int field_value::get_asInteger() const + { + return (int) get_asLong(); + } + + unsigned long field_value::get_asULong() const + { + switch (field_type) + { + case ft_String: + { + return (unsigned long) atoi(str_value.c_str()); + } + case ft_Boolean: + { + return (unsigned long) bool_value; + } + case ft_Char: + { + return (unsigned long) char_value; + } + case ft_Short: + { + return (unsigned long) short_value; + } + case ft_UShort: + { + return (unsigned long) ushort_value; + } + case ft_Long: + { + return (unsigned long) long_value; + } + case ft_ULong: + { + return ulong_value; + } + case ft_Float: + case ft_Double: + { + return (unsigned long) double_value; + } + default: + { + return (unsigned long) atoi(str_value.c_str()); + } + } + }; + + + + double field_value::get_asDouble() const + { + switch (field_type) + { + case ft_String: + { + return atof(str_value.c_str()); + } + case ft_Boolean: + { + return (double) bool_value; + } + case ft_Char: + { + return (double) char_value; + } + case ft_Short: + { + return (double) short_value; + } + case ft_UShort: + { + return (double) ushort_value; + } + case ft_Long: + { + return (double) long_value; + } + case ft_ULong: + { + return (double) ulong_value; + } + case ft_Float: + case ft_Double: + { + return (double) double_value; + } + default: + { + return atof(str_value.c_str()); + } + } + }; +#endif + +field_value & field_value::operator=(const field_value & fv) +{ + if (this == &fv) + return *this; + + if (fv.get_isNull()) + set_isNull(fv.get_fType()); + else + set_asString(fv.get_asString(), fv.get_field_type()); + + return *this; +}; + + +//Set functions +void field_value::set_asString(const char *s, fType type) +{ + str_value = s; + field_type = type; + is_null = s == NULL || *s == 0; +}; + +void field_value::set_asString(const string & s, fType type) +{ + str_value = s; + field_type = type; + is_null = s.length() == 0; +}; + + +string field_value::gft() +{ + string tmp; + + switch (field_type) + { + case ft_String: + { + tmp.assign("string"); + return tmp; + } + case ft_Boolean: + { + tmp.assign("bool"); + return tmp; + } + case ft_Char: + { + tmp.assign("char"); + return tmp; + } + case ft_Short: + { + tmp.assign("short"); + return tmp; + } + case ft_Long: + { + tmp.assign("long"); + return tmp; + } + case ft_Float: + { + tmp.assign("float"); + return tmp; + } + case ft_Double: + { + tmp.assign("double"); + return tmp; + } + case ft_Date: + { + tmp.assign("date"); + return tmp; + } + default: + break; + } + tmp.assign("string"); + return tmp; +} diff --git a/gb.db.sqlite2/src/qry_dat.h b/gb.db.sqlite2/src/qry_dat.h new file mode 100644 index 00000000..c195a6aa --- /dev/null +++ b/gb.db.sqlite2/src/qry_dat.h @@ -0,0 +1,302 @@ +/*************************************************************************** + + qry_dat.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/********************************************************************** + * Copyright (c) 2002, Leo Seib, Hannover + * + * Project:Dataset C++ Dynamic Library + * Module: FieldValue class and result sets classes header file + * Author: Leo Seib E-Mail: lev@almaty.pointstrike.net + * Begin: 5/04/2002 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **********************************************************************/ + +using namespace std; + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sqlite.h" + +enum fType + { + ft_String, + ft_Boolean, + ft_Char, + ft_WChar, + ft_WideString, + ft_Short, + ft_UShort, + ft_Long, + ft_ULong, + ft_Float, + ft_Double, + ft_LongDouble, + ft_Date, + ft_Object, + }; + +/* Size of Strings for fType */ +#define ft_Boolean_Length 5 //TRUE or FALSE +#define ft_Short_Length 10 +#define ft_LongDouble_Length 32 +#define ft_Long_Length 12 +#define ft_Float_Length 16 +#define ft_Date_Length 19 +#define ft_Double_Length 32 + +class field_value + { + private: + fType field_type; + string str_value; + bool is_null; + int len; + + public: + field_value(); + ~field_value(); + + int get_len() const { return len; } + + fType get_fType() const + { + return field_type; + } + + fType get_field_type() const + { + return field_type; + } + + string gft(); + + bool get_isNull() const + { + return is_null; + } + + string get_asString() const; + bool get_asBool() const; + int get_asInteger() const; + #if 0 + char get_asChar() const; + short get_asShort() const; + unsigned short get_asUShort() const; + long get_asLong() const; + unsigned long get_asULong() const; + double get_asDouble() const; + #endif + + #if 0 + field_value & operator=(const char *s) + { + set_asString(s); + return *this; + } + field_value & operator=(const string & s) + { + set_asString(s); + return *this; + } + #endif + + #if 0 + field_value & operator=(const bool b) + { + set_asBool(b); + return *this; + } + field_value & operator=(const short s) + { + set_asShort(s); + return *this; + } + field_value & operator=(const unsigned short us) + { + set_asUShort(us); + return *this; + } + field_value & operator=(const long l) + { + set_asLong(l); + return *this; + } + field_value & operator=(const unsigned long l) + { + set_asULong(l); + return *this; + } + field_value & operator=(const int i) + { + set_asLong(i); + return *this; + } + field_value & operator=(const double d) + { + set_asDouble(d); + return *this; + } + #endif + + field_value & operator=(const field_value & fv); + + //class ostream; + #if 0 + friend ostream & operator<<(ostream & os, const field_value & fv) + { + switch (fv.get_fType()) + { + case ft_String: + case ft_WideString: + case ft_WChar: + case ft_Object: + { + return os << fv.get_asString(); + break; + } + case ft_Date: + { + return os << fv.get_asString(); + break; + } + case ft_Boolean: + { + return os << fv.get_asBool(); + break; + } + case ft_Char: + { + return os << fv.get_asChar(); + break; + } + case ft_Short: + { + return os << fv.get_asShort(); + break; + } + case ft_UShort: + { + return os << fv.get_asUShort(); + break; + } + case ft_Long: + { + return os << fv.get_asLong(); + break; + } + case ft_ULong: + { + return os << fv.get_asULong(); + break; + } + case ft_Float: + case ft_LongDouble: + case ft_Double: + { + return os << fv.get_asDouble(); + break; + } + } + } + #endif + + void set_isNull(fType f) + { + is_null = true; + field_type = f; + str_value = ""; + } + void set_asString(const char *s, fType type); + void set_asString(const string & s, fType type); + /*void set_asBool(const bool b); + void set_asChar(const char c); + void set_asShort(const short s); + void set_asUShort(const unsigned short us); + void set_asInteger(const int i); + void set_asLong(const long l); + void set_asULong(const unsigned long l); + void set_asDouble(const double d); + void set_asDate(const char *s); //NG + void set_asDate(const string & s); //NG*/ + }; + +struct field_prop + { + string name, display_name; + fType type; + string field_table; //? + bool read_only; + unsigned int field_len; + unsigned int field_flags; + unsigned int notnull; + int idx; + }; + +struct field + { + field_prop props; + field_value val; + }; + + +typedef map < int, field > Fields; +typedef map < int, field_value > sql_record; +typedef map < int, field_prop > record_prop; +typedef map < int, sql_record > query_data; +typedef field_value variant; + +//typedef Fields::iterator fld_itor; +typedef sql_record::iterator rec_itor; +typedef record_prop::iterator recprop_itor; +typedef query_data::iterator qry_itor; + +struct result_set + { + sqlite *conn; //NG + record_prop record_header; + query_data records; + }; diff --git a/gb.db.sqlite2/src/sqlitedataset.cpp b/gb.db.sqlite2/src/sqlitedataset.cpp new file mode 100644 index 00000000..8630df54 --- /dev/null +++ b/gb.db.sqlite2/src/sqlitedataset.cpp @@ -0,0 +1,783 @@ +/*************************************************************************** + + sqlitedataset.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/********************************************************************** + * Copyright (c) 2002, Leo Seib, Hannover + * + * Project:SQLiteDataset C++ Dynamic Library + * Module: SQLiteDataset class realisation file + * Author: Leo Seib E-Mail: lev@almaty.pointstrike.net + * Begin: 5/04/2002 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **********************************************************************/ + + +#include +#include +#include +#include +#include +#include + +#include "sqlitedataset.h" +#include + +#include "gambas.h" + + +/**************************************************************/ + +//************* Callback function *************************** + +int callback(void* res_ptr,int ncol, char** reslt,char** cols){ + + /* NG: Type definition */ +// typedef vector Tables; + Tables tables; + Tables::iterator it; + + char *item; + char *table; + + result_set* r = (result_set*)res_ptr;//dynamic_cast(res_ptr); + int sz = r->records.size(); + + //if (reslt == NULL ) cout << "EMPTY!!!\n"; + if (!r->record_header.size()){ /*IF*/ + for (int i=0; i < ncol; i++) { + item = strchr(cols[i], (int)'.'); + if (!item){ /* Field does not include table info */ + item = cols[i]; + r->record_header[i].name = item; //NG + table = NULL; + r->record_header[i].field_table = ""; + r->record_header[i].type = ft_String;//default type to string + } + else { + //table = strndup(cols[i], strchr(cols[i], (int)'.') - cols[i]); + table = GB.NewString(cols[i], strchr(cols[i], (int)'.') - cols[i]); +// table = strdup(reslt[0]); + r->record_header[i].name = item + 1; + r->record_header[i].field_table = table; + r->record_header[i].type = ft_String;//default type to string + } + + if (!table) { + /* Field does not contain table info, + * so let's default to string. This + * has probably happened because aliases + * are being used */ + } + else { + /* Check Table Name and add to list */ + bool TableRegistered = false; + for ( it = tables.begin(); it != tables.end(); it++ ){ + if (strcmp((*it).data(), table) == 0) + TableRegistered = true; + } + if ( TableRegistered == false ){ + tables.push_back(table); + } + } + GB.FreeString(&table); //from strdup + } + + SetFieldType( r, tables); // Set all the field types + + for (int i=0; i < ncol; i++) { + /* Should table name be included in field name */ + if (tables.size() > 1){ + r->record_header[i].name = cols[i]; + } + +/* if (tables.size() < 1){ + r->record_header[i].type = ft_String;//Where type cannot be found + //default to string + }*/ + } + }/* IF close */ + + if (reslt != NULL) + { + for (int i = 0; i < ncol; i++) + { + //fprintf(stderr, "callback: [%d] %s: %s\n", i, cols[i], reslt[i]); + if (reslt[i] == NULL) + { + r->records[sz][i].set_isNull(r->record_header[i].type); + } + else + { + r->records[sz][i].set_asString(reslt[i], r->record_header[i].type); + } + } + } + //printf("Fsz: [%i]\n",r->record_header.size()); + //printf("Recs: [%i]\n",r->records.size()); + //cout << " val |" <(db)->getHandle(); + } + else return NULL; +} + +void SqliteDataset::make_query(StringList &_sql) { + string query; + + try { + + if (autocommit) db->start_transaction(); + + + if (db == NULL) GB.Error("No Database Connection"); + + //close(); + + + for (list::iterator i =_sql.begin(); i!=_sql.end(); i++) { + query = *i; + char* err=NULL; + Dataset::parse_sql(query); + //cout << "Executing: "<setErr(sqlite_exec(this->handle(),query.c_str(),NULL,NULL,&err))!=SQLITE_OK) { + GB.Error(db->getErrorMsg()); + } + } // end of for + + + if (db->in_transaction() && autocommit) db->commit_transaction(); + + active = true; + ds_state = dsSelect; + refresh(); + + } // end of try + catch(...) { + if (db->in_transaction()) db->rollback_transaction(); + } + +} + + +void SqliteDataset::make_insert() { + make_query(insert_sql); + last(); +} + + +void SqliteDataset::make_edit() { + make_query(update_sql); +} + + +void SqliteDataset::make_deletion() { + make_query(delete_sql); +} + + +void SqliteDataset::fill_fields() { + //cout <<"rr "<size() == 0) // Filling columns name + for (uint i = 0; i < result.record_header.size(); i++) { + (*fields_object)[i].props = result.record_header[i]; + //(*edit_object)[i].props = result.record_header[i]; + } + + //Filling result + if (result.records.size() != 0) { + for (uint i = 0; i < result.records[frecno].size(); i++){ + (*fields_object)[i].val = result.records[frecno][i]; + //(*edit_object)[i].val = result.records[frecno][i]; + } + } + else + { + field_value tmp; + for (uint i = 0; i < result.record_header.size(); i++) + (*fields_object)[i].val = tmp; + } + +} + + +//------------- public functions implementation -----------------// + +// BM: should retry if error = SQLITE_SCHEMA + +int SqliteDataset::exec(const string &sql) +{ + int res; + int retry; + + + if (!handle()) + GB.Error("No Database Connection"); + + exec_res.record_header.clear(); + exec_res.records.clear(); + exec_res.conn = handle();//NG + //if ((strncmp("select",sql.c_str(),6) == 0) || (strncmp("SELECT",sql.c_str(),6) == 0)) + + for (retry = 1; retry <= 2; retry++) + { + res = sqlite_exec(handle(), sql.c_str(), &callback, &exec_res, &errmsg); + if (res != SQLITE_SCHEMA) + break; + } + + db->setErr(res); + //if (res != SQLITE_OK) + // GB.Error(db->getErrorMsg()); + + return (res == SQLITE_OK); +} + +int SqliteDataset::exec() { + return exec(sql); +} + +const void* SqliteDataset::getExecRes() { + return &exec_res; +} + + +bool SqliteDataset::query(const char *query) { + + int res; + int retry; + + + //try{ + if (db == NULL) GB.Error("Database is not Defined"); + if(dynamic_cast(db)->getHandle() == NULL) GB.Error("No Database Connection"); + if ((strncasecmp("select",query,6) != 0) /*&& + (strncasecmp("PRAGMA table",query,12) !=0) && + (strncasecmp("PRAGMA index",query,12) !=0)*/ + ) GB.Error("MUST be select SQL or PRAGMA table or index!"); + + //close(); + + //cout << "Curr size "<setErr(res); + + if (res == SQLITE_OK) + { + active = true; + ds_state = dsSelect; + //cout << "Curr size2 "<first(); + //cout << "Curr size3 "<clear(); + fields_object->clear(); + ds_state = dsInactive; + active = false; + delete this; +} + + +void SqliteDataset::cancel() { + if ((ds_state == dsInsert) || (ds_state==dsEdit)) + { + if (result.record_header.size()) ds_state = dsSelect; + else ds_state = dsInactive; + } +} + + +int SqliteDataset::num_rows() { + return result.records.size(); +} + + +bool SqliteDataset::eof() { + return feof; +} + + +bool SqliteDataset::bof() { + return fbof; +} + + +void SqliteDataset::first() { + Dataset::first(); + this->fill_fields(); + //cout << "In first "<< fields_object->size()<<"\n"; +} + +void SqliteDataset::last() { + Dataset::last(); + fill_fields(); +} + +void SqliteDataset::prev(void) { + Dataset::prev(); + fill_fields(); +} + +void SqliteDataset::next(void) { + Dataset::next(); + if (!eof()) + fill_fields(); +} + + +//bool SqliteDataset::seek(int pos=0) { +bool SqliteDataset::seek(int pos) { + if (ds_state == dsSelect) { + Dataset::seek(pos); + fill_fields(); + return true; + } + return false; +} + + + +long SqliteDataset::nextid(const char *seq_name) { + if (handle()) return db->nextid(seq_name); + else return DB_UNEXPECTED_RESULT; +} + +/* Helper function */ +void SetFieldType( result_set *r, Tables tables){ + + Tables::iterator it; + + sqlite_vm *vm; + const char *tail; + const char **vals; + const char **names; + int ncol; + unsigned int len; + +// result_set res; + char sqlcmd[512]; + + for ( it = tables.begin(); it != tables.end(); it++ ){ + sprintf(sqlcmd,"PRAGMA table_info('%s')", (*it).data()); + if ( sqlite_compile(r->conn,sqlcmd,&tail,&vm,NULL) != SQLITE_OK) { + return; + } + while ( sqlite_step(vm, &ncol, &vals, &names) == SQLITE_ROW ){ + /* Type is in 2 */ + /* field name is 1 */ + /* not null indicator is 3 */ + /* dflt value is 4 */ + + for (uint e=0; erecord_header.size(); e++){ + + if ( r->record_header[e].name == vals[1] && + r->record_header[e].field_table == (*it).data()) { + + + r->record_header[e].type = GetFieldType((char *)vals[2], &len); + r->record_header[e].field_len = len; + /* Is not null */ + r->record_header[e].notnull = vals[3][0]; + } + } /* For end */ + } /* while end */ + + sqlite_finalize(vm, NULL); + } +} + +/* Return fType and length from String field*/ +fType GetFieldType(const char *Type, unsigned int *length ) +{ + char *upper; + char *_left, *_right; + fType rType; /* For return */ + unsigned int rTypeLen; + int i; + + upper = GB.NewZeroString(Type); + for (i = 0; i < GB.StringLength(upper); i++) + upper[i] = toupper(upper[i]); + + Type = upper; + if (!Type) Type = ""; + + if (strstr(Type, "BLOB") + || strstr(Type, "CHAR(") /* note the opening bracket */ + || strstr(Type, "CLOB") + || strstr(Type, "TEXT") /* also catches TINYTEXT */ + || strstr(Type, "VARCHAR") + || strstr(Type, "ENUM") + || strstr(Type, "SET") + || strstr(Type, "YEAR")) { /* MySQL 2 or 4 digit year (string) */ + rType = ft_String; + } + else if (strstr(Type, "CHAR") /* this is a 1-byte value */ + || strstr(Type, "TINYINT") + || strstr(Type, "INT1") + || strstr(Type, "BOOL")) { + rType = ft_Boolean; + /* Length is for when value is used as a string */ + rTypeLen = ft_Boolean_Length; + } + else if (strstr(Type, "SMALLINT") + || strstr(Type, "INT2")) { + rType = ft_Short; + /* Length is for when value is used as a string */ + rTypeLen = ft_Short_Length; + } + else if (strstr(Type, "MEDIUMINT")) { + rType = ft_Short; + /* Length is for when value is used as a string */ + rTypeLen = ft_Short_Length; + } + else if (strstr(Type, "BIGINT") + || strstr(Type, "INT8")) { + rType = ft_LongDouble; + /* Length is for when value is used as a string */ + rTypeLen = ft_LongDouble_Length; + } + else if (strstr(Type, "INTEGER") + || strstr(Type, "INT") + || strstr(Type, "INT4")) { + rType = ft_Long; + /* Length is for when value is used as a string */ + rTypeLen = ft_Long_Length; + } + else if (strstr(Type, "DECIMAL") || + strstr(Type, "NUMERIC")) { + rType = ft_Float; + /* Length is for when value is used as a string */ + rTypeLen = ft_Float_Length; + } + else if (strstr(Type, "TIMESTAMP") + || strstr(Type, "DATETIME")) { + rType = ft_Date; + rTypeLen = ft_Date_Length; + } + else if (strstr(Type, "DATE")) { + rType = ft_Date; + /* Length is for when value is used as a string */ + rTypeLen = ft_Date_Length; + } + else if (strstr(Type, "TIME")) { + rType = ft_Date; + /* Length is for when value is used as a string */ + rTypeLen = ft_Date_Length; + } + else if (strstr(Type, "DOUBLE") || strstr(Type, "FLOAT8")) + { + rType = ft_Double; + /* Length is for when value is used as a string */ + rTypeLen = ft_Double_Length; + } + else if (strstr(Type, "REAL") /* this is PostgreSQL "real", not + MySQL "real" which is a + synonym of "double" */ + || strstr(Type, "FLOAT") + || strstr(Type, "FLOAT4")) { + rType = ft_Float; + /* Length is for when value is used as a string */ + rTypeLen = ft_Float_Length; + } + else { + rType = ft_String; + /* most reasonable default */ + } + + if ( rType == ft_String){ + /* if a length has been defined it will be between ()*/ + _right = (char *)rindex(Type, ')'); + _left = (char *)index(Type, '('); + if (_right){ + _right = NULL; + rTypeLen = atoi(_left + 1); + } + else { + /* set a default length */ + rTypeLen = DEFAULT_STRING_LENGTH; + } + } + + if ( length != NULL ) + *length = rTypeLen; + + GB.FreeString(&upper); + + return rType; +} + diff --git a/gb.db.sqlite2/src/sqlitedataset.h b/gb.db.sqlite2/src/sqlitedataset.h new file mode 100644 index 00000000..0cade96c --- /dev/null +++ b/gb.db.sqlite2/src/sqlitedataset.h @@ -0,0 +1,208 @@ +/*************************************************************************** + + sqlitedataset.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/********************************************************************** + * Copyright (c) 2002, Leo Seib, Hannover + * + * Project:SQLiteDataset C++ Dynamic Library + * Module: SQLiteDataset class header file + * Author: Leo Seib E-Mail: lev@almaty.pointstrike.net + * Begin: 5/04/2002 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **********************************************************************/ + +#ifndef _SQLITEDATASET_H +#define _SQLITEDATASET_H + +#include +#include "dataset.h" +#include "sqlite.h" + +extern "C" { +#include "gambas.h" +#include "gb_common.h" +#include "../gb.db.h" + +extern GB_INTERFACE GB; +extern DB_INTERFACE DB; +} + + +/* NG */ +using namespace std; +#include +#include + +typedef vector Tables; + +//#define DEFAULT_STRING_LENGTH 65535 +#define DEFAULT_STRING_LENGTH 0 +/* */ + +/* Return Field Type from Database */ +void SetFieldType(result_set *r, Tables tables); +fType GetFieldType(const char *Type, unsigned int *len); + +/***************** Class SqliteDatabase definition ****************** + + class 'SqliteDatabase' connects with Sqlite-server + +******************************************************************/ + +class SqliteDatabase: public Database { +protected: +/* connect descriptor */ + sqlite *conn; + bool _in_transaction; + int last_err; + +public: +/* default constructor */ + SqliteDatabase(); +/* destructor */ + virtual ~SqliteDatabase(); + + Dataset *CreateDataset() const; + +/* func. returns connection handle with SQLite-server */ + sqlite *getHandle() { return conn; } +/* func. returns current status about SQLite-server connection */ + virtual int status(); + virtual int setErr(int err_code); +/* func. returns error message if error occurs */ + virtual const char *getErrorMsg(); + +/* func. connects to database-server */ + virtual int connect(); +/* func. disconnects from database-server */ + virtual void disconnect(); +/* func. creates new database */ + virtual int create(); +/* func. deletes database */ + virtual int drop(); + + virtual long nextid(const char* seq_name); + +/* virtual methods for transaction */ + + virtual void start_transaction(); + virtual void commit_transaction(); + virtual void rollback_transaction(); + + bool in_transaction() {return _in_transaction;} + + int lastError() { return last_err; } +}; + + + +/***************** Class SqliteDataset definition ******************* + + class 'SqliteDataset' does a query to SQLite-server + +******************************************************************/ + +class SqliteDataset : public Dataset { +protected: +/* query results*/ + result_set result; + result_set exec_res; + + char* errmsg; + + sqlite* handle(); + +/* Makes direct queries to database */ + virtual void make_query(StringList &_sql); +/* Makes direct inserts into database */ + virtual void make_insert(); +/* Edit SQL */ + virtual void make_edit(); +/* Delete SQL */ + virtual void make_deletion(); + + //static int sqlite_callback(void* res_ptr,int ncol, char** reslt, char** cols); + +/* This function works only with MySQL database + Filling the fields information from select statement */ + virtual void fill_fields(); +/* Changing field values during dataset navigation */ + +public: +/* constructor */ + SqliteDataset(); + SqliteDataset(SqliteDatabase *newDb); + +/* destructor */ + virtual ~SqliteDataset(); + +/* opens a query & then sets a query results */ + virtual void open(); + virtual void open(const string &sql); +/* func. executes a query without results to return */ + virtual int exec (); + virtual int exec (const string &sql); + virtual const void* getExecRes(); +/* as open, but with our query exept Sql */ + virtual bool query(const char *query); + virtual bool query(const string &query); +/* func. closes a query */ + virtual void close(void); +/* Cancel changes, made in insert or edit states of dataset */ + virtual void cancel(); +/* sequence numbers */ + virtual long nextid(const char *seq_name); +/* sequence numbers */ + virtual int num_rows(); + + virtual bool bof(); + virtual bool eof(); + virtual void first(); + virtual void last(); + virtual void prev(); + virtual void next(); +/* Go to record No (starting with 0) */ + virtual bool seek(int pos=0); + + +}; + +#endif diff --git a/gb.db.sqlite2/src/stringhelper.cpp b/gb.db.sqlite2/src/stringhelper.cpp new file mode 100644 index 00000000..dfcb1323 --- /dev/null +++ b/gb.db.sqlite2/src/stringhelper.cpp @@ -0,0 +1,95 @@ +/*************************************************************************** + + stringhelper.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/********************************************************************** + * Copyright (c) 2002, Leo Seib, Hannover + * + * Project:SQLiteDataset C++ Dynamic Library + * Module: StringHelper class realisation file + * Author: Leo Seib E-Mail: lev@almaty.pointstrike.net + * Begin: 5/04/2002 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **********************************************************************/ + +#include "stringhelper.h" + +str_helper::str_helper() {work_str = "";} + +str_helper::str_helper(const char *s){work_str = s;} + +string str_helper::get_str() {return work_str;} + +void str_helper::set_str(const char *s) {work_str = s; }; + +string str_helper::before(string s, bool &is_found) { + is_found = false; + int pos = work_str.find(s); + int idx = pos+s.length(); + is_found = pos >= 0; + if (idx == (int) work_str.length()) + return work_str.substr(0,pos); + else + if ((pos >= 0) && (!( (work_str[idx]>48 && work_str[idx]<58) || (work_str[idx]>64 && work_str[idx]<91) || (work_str[idx]>97 && work_str[idx]<123) || work_str[idx]==95 ))) + return work_str.substr(0,pos); + else //return (string)NULL; + { string tmp(""); + return tmp; + } +} + +string str_helper::after(string s) {return work_str.substr(work_str.find(s)+s.length(),work_str.length());} + +string str_helper::replace(string pattern, string by_what) { + bool is_found; + string bf = before(pattern,is_found); + string aft(""); + string result(""); + while (is_found) { + aft = after(pattern); + result = bf + by_what + aft; + work_str.assign(result); + bf = before(pattern,is_found); + } + if (result.length()==0) result = work_str.c_str(); + return result; +} + + diff --git a/gb.db.sqlite2/src/stringhelper.h b/gb.db.sqlite2/src/stringhelper.h new file mode 100644 index 00000000..720e5a48 --- /dev/null +++ b/gb.db.sqlite2/src/stringhelper.h @@ -0,0 +1,72 @@ +/*************************************************************************** + + stringhelper.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/********************************************************************** + * Copyright (c) 2002, Leo Seib, Hannover + * + * Project:SQLiteDataset C++ Dynamic Library + * Module: StringHelper class header file + * Author: Leo Seib E-Mail: lev@almaty.pointstrike.net + * Begin: 5/04/2002 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **********************************************************************/ + +using namespace std; + +#include + +class str_helper { + private: + string work_str; + public: + str_helper(); + str_helper (const char *s); + ~str_helper () {}; + + string get_str(); + void set_str(const char *s); + + string before(string s, bool &is_found); + + string after(string s); + + string replace(string pattern, string by_what); +}; diff --git a/gb.db.sqlite3/AUTHORS b/gb.db.sqlite3/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.db.sqlite3/COPYING b/gb.db.sqlite3/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.db.sqlite3/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.db.sqlite3/ChangeLog b/gb.db.sqlite3/ChangeLog new file mode 100644 index 00000000..2cc6c90f --- /dev/null +++ b/gb.db.sqlite3/ChangeLog @@ -0,0 +1,55 @@ +02/03/04 - Added sqlite system tables sqlite_master and sqlite_temp_master to +the table list etc. +02/03/04 - Changed default string size to 0, which is unlimited. +22/03/04 - Cleared up some deallocations. +22/03/04 - Allowed for accessing same field name within multiple table + query. e.g. select fred.id, sid.id from fred, sid +01/04/04 - Enabled Field aliasing for exec. +03/04/04 - The host variable within the database class can be used as the + database home, that is, set it to the path where the database files + are located. +03/04/04 - The location of the database files is order by: + a) If fullpath given then use this. + b) If Host variable set, then look at the path set in the variable. + c) If environment variable GAMBAS_SQLITE_DBHOME is set then use. + d) Current working directory. +04/06/04 - Allow memory only database. e.g. :memory: as the database name. + +* 17 Jul 2004 - BM + +- By default, an in-memory database is opened if no database name is specified. +- An in_memory database returns as user-list only the current user, with admin rights. +- The databases are not searched by default in the current working directory anymore. + Instead, they are searched in the /tmp/gambas.%pid%/sqlite/ directory. + +* 29 Aug 2004 - BM + +- Many clean-ups in database_create(). The function FullPath() is not necessary + anymore, and has been removed. +- Fix table_index() so that it returns error if no true primary key is found + in the table. + +* 01 Sep 2004 - NG + +- Checks that a full path points at a real SQLite database in FindDatabase(). + +* 20 Nov 2004 - DC + +- I've replaced all float values for double values in the sqlite driver. + +* 22 Nov 2004 - NG + +- Spawned sqlite3 driver from sqlite driver + +* 17 Nov 2005 - NG + +- Enable BIGINT support for GB_T_LONG + +* 02 Jan 2006 - BM + +- Support for Blob and Serial fields +- Some cleanup in the sqlite dataset class. + +* 08 Jan 2005 - BM + +- Detection of sqlite2 database, and fallback to the sqlite2 driver if needed. diff --git a/gb.db.sqlite3/INSTALL b/gb.db.sqlite3/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.db.sqlite3/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.db.sqlite3/Makefile.am b/gb.db.sqlite3/Makefile.am new file mode 100644 index 00000000..a1dca82a --- /dev/null +++ b/gb.db.sqlite3/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @SQLITE3_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.db.sqlite3/NEWS b/gb.db.sqlite3/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.db.sqlite3/README b/gb.db.sqlite3/README new file mode 100644 index 00000000..c38e1392 --- /dev/null +++ b/gb.db.sqlite3/README @@ -0,0 +1,60 @@ +NG 29/12/2005 - For sqlite3 PRAGMA full_column_names is only useful + when PRAGMA short_column_names is off. Included so + that this will work from 3.2.1 onwards. + +(Daniel Campos ) - All float values has been replace +by double values, as some extra wrong decimals were added in conversions from sql data +types to Gambas data types. + +Uses the STL and requires the sqliteclient library. Currently tested with +SQLite 2.8.13. from www.sqlite.org + +sqlite is typeless...although in the create you can specify type details (these are +just comments!). These types have been defined to map to gambas, those not defined +will default to string. + +Database/Gambas Type : ft_String/ GB_T_STRING +DDL Value : BLOB, CHAR(, CLOB, TEXT, VARCHAR, ENUM, SET, YEAR, Non specified + +Database/Gambas Type : ft_Boolean/ GB_T_BOOLEAN +DDL Value : CHAR, TINYINT, INT1, BOOL + +Database Type/Gambas : ft_Short/ GB_T_INTEGER +DDL Value : SMALLINT, INT2, MEDIUMINT + +Database/Gambas Type : ft_LongDouble/ GB_T_FLOAT +DDL Value : BIGINT, INT8 + +Database/Gambas Type : ft_Long/ GB_T_INTEGER +DDL Value : INTEGER, INT, INT4 + +Database/Gambas Type : ft_Float/GB_T_FLOAT +DDL Value : DECIMAL, NUMERIC, REAL, FLOAT, FLOAT8, FLOAT4 + +Database/Gambas Type : ft_Date/GB_T_DATE +DDL Value : TIMESTAMP, DATETIME, DATE, TIME + +Database/Gambas Type : ft_Double/GB_T_FLOAT +DDL Value : DOUBLE + +Thanks go to Leo Seib for his SQLiteDataset library ( sqlitedataset.sourceforge.net) +which has been the base for my hacking. + +The order for locating database files is: +1) If database is fullpath qualified +2) If Host is specified as a valid path +3) If set, GAMBAS_SQLITE_DBHOME variable +4) Current working directory. + +The driver will look for databases in the current working directory unless the environment +variable GAMBAS_SQLITE_DBHOME is set. + +There is no concept of users within sqlite. Access is controlled by file permissions +on the database file. + +Creates database /tmp/sqlite.db by default. For the gambas-database-manager this will +allow functionality. + +Sqlite is not case sensative on table or field names. The implementation for Gambas is. + +Database :memory: will load only in memory diff --git a/gb.db.sqlite3/acinclude.m4 b/gb.db.sqlite3/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.db.sqlite3/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.db.sqlite3/component.am b/gb.db.sqlite3/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.db.sqlite3/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.db.sqlite3/configure.ac b/gb.db.sqlite3/configure.ac new file mode 100644 index 00000000..a32573ab --- /dev/null +++ b/gb.db.sqlite3/configure.ac @@ -0,0 +1,19 @@ +dnl ---- configure.ac for gb.db.sqlite + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-db-sqlite3, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.db.sqlite3) +AC_PROG_LIBTOOL + +GB_COMPONENT_PKG_CONFIG( + sqlite3, SQLITE3, gb.db.sqlite3, [src], + 'sqlite3 >= 3.0') + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.db.sqlite3/gambas.h b/gb.db.sqlite3/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.db.sqlite3/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.db.sqlite3/gb.db.h b/gb.db.sqlite3/gb.db.h new file mode 120000 index 00000000..563bd00c --- /dev/null +++ b/gb.db.sqlite3/gb.db.h @@ -0,0 +1 @@ +../main/lib/db/gb.db.h \ No newline at end of file diff --git a/gb.db.sqlite3/gb.db.proto.h b/gb.db.sqlite3/gb.db.proto.h new file mode 120000 index 00000000..0a8e6920 --- /dev/null +++ b/gb.db.sqlite3/gb.db.proto.h @@ -0,0 +1 @@ +../main/lib/db/gb.db.proto.h \ No newline at end of file diff --git a/gb.db.sqlite3/gb_common.h b/gb.db.sqlite3/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.db.sqlite3/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.db.sqlite3/m4 b/gb.db.sqlite3/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.db.sqlite3/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.db.sqlite3/reconf b/gb.db.sqlite3/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.db.sqlite3/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.db.sqlite3/src/Makefile.am b/gb.db.sqlite3/src/Makefile.am new file mode 100755 index 00000000..c2af50e0 --- /dev/null +++ b/gb.db.sqlite3/src/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.db.sqlite3 +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.db.sqlite3.la + +gb_db_sqlite3_la_LIBADD = @SQLITE3_LIB@ +gb_db_sqlite3_la_LDFLAGS = -module @LD_FLAGS@ @SQLITE3_LDFLAGS@ +gb_db_sqlite3_la_CFLAGS = $(AM_CFLAGS) +gb_db_sqlite3_la_CPPFLAGS = @SQLITE3_INC@ + +gb_db_sqlite3_la_SOURCES = \ + main.h main.c \ + helper.h helper.c \ + gb_buffer.h gb_buffer.c diff --git a/gb.db.sqlite3/src/README b/gb.db.sqlite3/src/README new file mode 100644 index 00000000..b1589b20 --- /dev/null +++ b/gb.db.sqlite3/src/README @@ -0,0 +1,56 @@ +(Daniel Campos ) - All float values has been replace +by double values, as some extra wrong decimals were added in conversions from sql data +types to Gambas data types. + +Uses the STL and requires the sqliteclient library. Currently tested with +SQLite 2.8.13. from www.sqlite.org + +sqlite is typeless...although in the create you can specify type details (these are +just comments!). These types have been defined to map to gambas, those not defined +will default to string. + +Database/Gambas Type : ft_String/ GB_T_STRING +DDL Value : BLOB, CHAR(, CLOB, TEXT, VARCHAR, ENUM, SET, YEAR, Non specified + +Database/Gambas Type : ft_Boolean/ GB_T_BOOLEAN +DDL Value : CHAR, TINYINT, INT1, BOOL + +Database Type/Gambas : ft_Short/ GB_T_INTEGER +DDL Value : SMALLINT, INT2, MEDIUMINT + +Database/Gambas Type : ft_LongDouble/ GB_T_FLOAT +DDL Value : BIGINT, INT8 + +Database/Gambas Type : ft_Long/ GB_T_INTEGER +DDL Value : INTEGER, INT, INT4 + +Database/Gambas Type : ft_Float/GB_T_FLOAT +DDL Value : DECIMAL, NUMERIC, REAL, FLOAT, FLOAT8, FLOAT4 + +Database/Gambas Type : ft_Date/GB_T_DATE +DDL Value : TIMESTAMP, DATETIME, DATE, TIME + +Database/Gambas Type : ft_Double/GB_T_FLOAT +DDL Value : DOUBLE + +Thanks go to Leo Seib for his SQLiteDataset library ( sqlitedataset.sourceforge.net) +which has been the base for my hacking. + +The order for locating database files is: +1) If database is fullpath qualified +2) If Host is specified as a valid path +3) If set, GAMBAS_SQLITE_DBHOME variable +4) Current working directory. + +The driver will look for databases in the current working directory unless the environment +variable GAMBAS_SQLITE_DBHOME is set. + +There is no concept of users within sqlite. Access is controlled by file permissions +on the database file. + +Creates database /tmp/sqlite.db by default. For the gambas-database-manager this will +allow functionality. + +Sqlite is not case sensative on table or field names. The implementation for Gambas is. + +Database :memory: will load only in memory diff --git a/gb.db.sqlite3/src/gb.db.sqlite3.component b/gb.db.sqlite3/src/gb.db.sqlite3.component new file mode 100644 index 00000000..a6209de7 --- /dev/null +++ b/gb.db.sqlite3/src/gb.db.sqlite3.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.db.sqlite3 +Author=Nigel Gerrard,Benoît Minisini +State=0 +Require=gb.db + diff --git a/gb.db.sqlite3/src/gb_buffer.c b/gb.db.sqlite3/src/gb_buffer.c new file mode 100644 index 00000000..d18fdc9b --- /dev/null +++ b/gb.db.sqlite3/src/gb_buffer.c @@ -0,0 +1,104 @@ +/*************************************************************************** + + gb_buffer.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __BUFFER_C + +#include "main.h" +#include "gb_buffer.h" + +#define ALLOC(a, b) GB.Alloc(POINTER(a), b) +#define FREE(a) GB.Free(POINTER(a)) +#define REALLOC(a, b) GB.Realloc(POINTER(a), b) + +void BUFFER_create(void *p_data) +{ + BUFFER *buf; + + ALLOC(&buf, sizeof(BUFFER)); + buf->max = 0; + buf->length = 0; + + *((void **)p_data) = BUFFER_TO_DATA(buf); +} + + +void BUFFER_delete(void *p_data) +{ + void **data = (void **)p_data; + BUFFER *buf = DATA_TO_BUFFER(*data); + + FREE(&buf); + *data = NULL; +} + + +bool BUFFER_need(void *p_data, size_t size) +{ + void **data = (void **)p_data; + BUFFER *buffer = DATA_TO_BUFFER(*data); + + buffer->length += size; + //fprintf(stderr, "BUFFER_need: %ld (%ld / %ld)\n", size, buffer->length, buffer->max); + + if (buffer->length > buffer->max) + { + while (buffer->length >= buffer->max) + buffer->max += BUFFER_INC; + + REALLOC(&buffer, sizeof(char) * buffer->max + sizeof(BUFFER)); + *data = BUFFER_TO_DATA(buffer); + } + + return FALSE; +} + + +offset_t BUFFER_add(void *p_data, const void *string, int len) +{ + void **data = (void **)p_data; + BUFFER *buffer = DATA_TO_BUFFER(*data); + size_t pos; + + if (len < 0) + len = strlen((const char *)string); + + pos = buffer->length; + BUFFER_need(p_data, len); + + memcpy(*data + pos, string, len); + + //fprintf(stderr, ">> BUFFER_add\n"); + + return pos; +} + +void BUFFER_add_char(void *p_data, char c) +{ + void **data = (void **)p_data; + BUFFER *buffer = DATA_TO_BUFFER(*data); + size_t pos; + + pos = buffer->length; + BUFFER_need(p_data, 1); + *(char *)(*data + pos) = c; +} diff --git a/gb.db.sqlite3/src/gb_buffer.h b/gb.db.sqlite3/src/gb_buffer.h new file mode 100644 index 00000000..6d6af6d0 --- /dev/null +++ b/gb.db.sqlite3/src/gb_buffer.h @@ -0,0 +1,47 @@ +/*************************************************************************** + + gb_buffer.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_BUFFER_H +#define __GB_BUFFER_H + +typedef + struct { + size_t length; + size_t max; + } + BUFFER; + +#define BUFFER_INC 256 + +void BUFFER_create(void *p_data); +void BUFFER_delete(void *p_data); +offset_t BUFFER_add(void *p_data, const void *string, int len); +bool BUFFER_need(void *p_data, size_t size); +void BUFFER_add_char(void *p_data, char c); + +#define DATA_TO_BUFFER(_data) ((BUFFER *)(_data) - 1) +#define BUFFER_TO_DATA(_buffer) ((char *)((BUFFER *)(_buffer) + 1)) + +#define BUFFER_length(_data) (DATA_TO_BUFFER(_data)->length) + +#endif diff --git a/gb.db.sqlite3/src/helper.c b/gb.db.sqlite3/src/helper.c new file mode 100644 index 00000000..408edd79 --- /dev/null +++ b/gb.db.sqlite3/src/helper.c @@ -0,0 +1,523 @@ +/*************************************************************************** + + helper.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __HELPER_C + +#include "helper.h" + +#include "gb_buffer.h" + +//-------------------------------------------------------------------------- + +static int _last_error = SQLITE_OK; + +SQLITE_DATABASE *sqlite_open_database(const char *path, const char *host) +{ + SQLITE_DATABASE *db; + sqlite3 *handle; + + if (!path) + path = ":memory:"; + + _last_error = sqlite3_open(path, &handle); + + if (_last_error == SQLITE_OK) + { + GB.Alloc(POINTER(&db), sizeof(SQLITE_DATABASE)); + db->handle = handle; + db->path = GB.NewZeroString(path); + db->host = GB.NewZeroString(host); + db->error = SQLITE_OK; + return db; + } + else + return NULL; +} + +void sqlite_close_database(SQLITE_DATABASE *db) +{ + sqlite3_close(db->handle); + GB.FreeString(&db->path); + GB.FreeString(&db->host); + GB.Free(POINTER(&db)); +} + +const char *sqlite_get_error_message(SQLITE_DATABASE *db) +{ + const char *error; + int err; + + err = db ? db->error : _last_error; + + switch (err) + { + case SQLITE_OK: + error = "Successful result"; + break; + case SQLITE_ERROR: + error = sqlite3_errmsg(db->handle); + break; + case SQLITE_INTERNAL: + error = "Internal logic error - Report this error on the mailing-list at sqlite.org"; + break; + case SQLITE_PERM: + error = "Access permission denied"; + break; + case SQLITE_ABORT: + error = "Callback routine requested an abort"; + break; + case SQLITE_BUSY: + error = "The database file is locked"; + break; + case SQLITE_LOCKED: + error = "A table in the database is locked"; + break; + case SQLITE_NOMEM: + error = "Out of memory"; + break; + case SQLITE_READONLY: + error = "Attempt to write a readonly database"; + break; + case SQLITE_INTERRUPT: + error = "Operation terminated by sqlite_interrupt()"; + break; + case SQLITE_IOERR: + error = "Some kind of disk I/O error occurred"; + break; + case SQLITE_CORRUPT: + error = "The database disk image is malformed"; + break; + case SQLITE_NOTFOUND: + error = "(Internal Only) Table or record not found"; + break; + case SQLITE_FULL: + error = "Insertion failed because database is full"; + break; + case SQLITE_CANTOPEN: + error = "Unable to open the database file"; + break; + case SQLITE_PROTOCOL: + error = "Database lock protocol error"; + break; + case SQLITE_EMPTY: + error = "(Internal Only) Database table is empty"; + break; + case SQLITE_SCHEMA: + error = "The database schema changed"; + break; + case SQLITE_TOOBIG: + error = "Too much data for one row of a table"; + break; + case SQLITE_CONSTRAINT: + error = "Abort due to constraint violation"; + break; + case SQLITE_MISMATCH: + error = "Data type mismatch"; + break; + default: + error = "Undefined SQLite error"; + } + + return error; +} + +//-------------------------------------------------------------------------- + +GB_TYPE sqlite_get_type(const char *type, int *length) +{ + int i; + char *upper; + GB_TYPE gtype; + char *left, *right; + + if (length) + *length = 0; + + if (!type || !*type) + return GB_T_STRING; + + upper = GB.NewZeroString(type); + for (i = 0; i < GB.StringLength(upper); i++) + upper[i] = toupper(upper[i]); + type = upper; + + if (strstr(type, "CHAR(") /* note the opening bracket */ + || strstr(type, "CLOB") || strstr(type, "TEXT") /* also catches TINYTEXT */ + || strstr(type, "VARCHAR") || strstr(type, "VARYING CHAR") + || strstr(type, "ENUM") || strstr(type, "SET") || strstr(type, "YEAR")) + { /* MySQL 2 or 4 digit year (string) */ + gtype = GB_T_STRING; + } + else if (strstr(type, "CHAR") /* this is a 1-byte value */ + || strstr(type, "TINYINT") + || strstr(type, "INT1") || strstr(type, "BOOL")) + { + gtype = GB_T_BOOLEAN; + } + else if (strstr(type, "SMALLINT") || strstr(type, "INT2")) + { + gtype = GB_T_INTEGER; + } + else if (strstr(type, "MEDIUMINT")) + { + gtype = GB_T_INTEGER; + } + else if (strstr(type, "BIGINT") || strstr(type, "INT8")) + { + gtype = GB_T_LONG; + } + else if (strstr(type, "INTEGER") + || strstr(type, "INT") || strstr(type, "INT4")) + { + gtype = GB_T_INTEGER; + } + else if (strstr(type, "DECIMAL") || strstr(type, "NUMERIC")) + { + gtype = GB_T_FLOAT; + } + else if (strstr(type, "TIMESTAMP") || strstr(type, "DATETIME")) + { + gtype = GB_T_DATE; + } + else if (strstr(type, "DATE")) + { + gtype = GB_T_DATE; + } + else if (strstr(type, "TIME")) + { + gtype = GB_T_DATE; + } + else if (strstr(type, "DOUBLE") || strstr(type, "FLOAT8")) + { + gtype = GB_T_FLOAT; + } + else if (strstr(type, "REAL") /* this is PostgreSQL "real", not + MySQL "real" which is a + synonym of "double" */ + || strstr(type, "FLOAT") + || strstr(type, "FLOAT4")) + { + gtype = GB_T_FLOAT; + } + else if (strstr(type, "BLOB")) // BM + { + gtype = DB_T_BLOB;; + } + else + gtype = GB_T_STRING; + + if (gtype == GB_T_STRING && type && length != NULL) + { + /* if a length has been defined it will be between () */ + right = (char *)rindex(type, ')'); + left = (char *)index(type, '('); + if (left && right) + { + *right = 0; + *length = atoi(left + 1); + } + } + + GB.FreeString(&upper); + return gtype; +} + +//-------------------------------------------------------------------------- + +static int my_sqlite3_exec( + sqlite3 *db, /* The database on which the SQL executes */ + const char *zSql, /* The SQL to be executed */ + SQLITE_RESULT *result, + bool need_types +) +{ + int rc = SQLITE_OK; + const char *zLeftover; + sqlite3_stmt *pStmt = 0; + int ncol; + int nRetry = 0; + int nCallback; + int i; + const char *decltype; + int n_unknown = 0; + int type; + + //fprintf(stderr, "my_sqlite3_exec: %s\n", zSql); + + if( zSql==0 ) return SQLITE_OK; + while( (rc==SQLITE_OK || (rc==SQLITE_SCHEMA && (++nRetry)<2)) && zSql[0] ) + { + pStmt = 0; + //fprintf(stderr, "my_sqlite3_exec: sqlite3_prepare_v2 ?\n"); + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover); + if( rc!=SQLITE_OK ){ + //fprintf(stderr, "my_sqlite3_exec: sqlite3_prepare_v2 failed: %s\n", sqlite3_errmsg(db)); + if( pStmt ) sqlite3_finalize(pStmt); + continue; + } + if( !pStmt ){ + /* this happens for a comment or white-space */ + zSql = zLeftover; + continue; + } + + nCallback = 0; + + result->ncol = ncol = sqlite3_column_count(pStmt); + + if (ncol > 0) + { + GB.AllocZero(POINTER(&result->names), ncol * sizeof(const char *)); + if (need_types) + { + GB.Alloc(POINTER(&result->types), ncol * sizeof(int)); + GB.Alloc(POINTER(&result->lengths), ncol * sizeof(int)); + } + } + + GB.NewArray(POINTER(&result->values), sizeof(int), 0); + + for(;;) + { + rc = sqlite3_step(pStmt); + + /* Invoke the callback function if required */ + if((SQLITE_ROW==rc || + //(SQLITE_DONE==rc && !nCallback && db->flags&SQLITE_NullCallback)) ){ + (SQLITE_DONE==rc && !nCallback && 1)) ) + { + if (0 == nCallback) + { + for (i = 0; i < ncol; i++) + result->names[i] = GB.NewZeroString(sqlite3_column_name(pStmt, i)); + + if (need_types) + { + for (i = 0; i < ncol; i++) + { + decltype = sqlite3_column_decltype(pStmt, i); + if (!decltype) + { + result->types[i] = 0; + result->lengths[i] = 0; + n_unknown++; + } + else + { + result->types[i] = (int)sqlite_get_type(decltype, &result->lengths[i]); + } + } + } + + nCallback++; + } + + if (rc == SQLITE_ROW) + { + int *addr = GB.Insert(&result->values, -1, ncol * 2); + char *value; + int len; + + result->nrow++; + + for (i = 0; i < ncol; i++) + { + type = sqlite3_column_type(pStmt, i); + + if (n_unknown && result->types[i] == 0) + { + switch(type) + { + case SQLITE_INTEGER: result->types[i] = GB_T_LONG; break; + case SQLITE_FLOAT: result->types[i] = GB_T_FLOAT; break; + case SQLITE_BLOB: result->types[i] = (int)DB_T_BLOB; break; + default: result->types[i] = GB_T_STRING; + } + + n_unknown--; + } + + if (type == SQLITE_BLOB) + value = (char *)sqlite3_column_blob(pStmt, i); + else + value = (char *)sqlite3_column_text(pStmt, i); + + len = sqlite3_column_bytes(pStmt, i); + + if (len == 0) + addr[i * 2] = BUFFER_length(result->buffer) - 1; + else + { + addr[i * 2] = BUFFER_add(&result->buffer, value, len + 1); + result->buffer[BUFFER_length(result->buffer) - 1] = 0; + } + + addr[i * 2 + 1] = len; + + } + } + } + + if (rc != SQLITE_ROW) + { + rc = sqlite3_finalize(pStmt); + pStmt = 0; + + if (rc != SQLITE_SCHEMA) + { + nRetry = 0; + zSql = zLeftover; + while( isspace((unsigned char)zSql[0]) ) zSql++; + } + + break; + } + } + } + + if (pStmt) + sqlite3_finalize(pStmt); + + return rc; +} + + +SQLITE_RESULT *sqlite_query_exec(SQLITE_DATABASE *db, const char *query, bool need_types) +{ + SQLITE_RESULT *result; + int retry; + int res; + + GB.AllocZero(POINTER(&result), sizeof(SQLITE_RESULT)); + BUFFER_create(&result->buffer); + + for (retry = 1; retry <= 2; retry++) + { + res = my_sqlite3_exec(db->handle, query, result, need_types); + if (res != SQLITE_SCHEMA) + break; + } + + if (res != SQLITE_OK) + { + db->error = res; + sqlite_query_free(result); + return NULL; + } + else + return result; +} + +void sqlite_query_free(SQLITE_RESULT *result) +{ + int i; + + for (i = 0; i < result->ncol; i++) + GB.FreeString(&result->names[i]); + + GB.Free(POINTER(&result->names)); + + GB.Free(POINTER(&result->types)); + + GB.Free(POINTER(&result->lengths)); + + GB.FreeArray(&result->values); + + BUFFER_delete(&result->buffer); + + GB.Free(POINTER(&result)); +} + +void sqlite_query_get(SQLITE_RESULT *result, int pos, int col, char **value, int *length) +{ + int i; + + if (pos < 0 || pos >= result->nrow || col < 0 || col >= result->ncol) + { + *value = NULL; + if (length) + *length = 0; + return; + } + + i = pos * result->ncol * 2 + col * 2; + *value = result->buffer + result->values[i]; + if (length) + *length = result->values[i + 1]; +} + +char *sqlite_query_get_string(SQLITE_RESULT *result, int pos, int col) +{ + char *value; + sqlite_query_get(result, pos, col, &value, NULL); + return value ? value : ""; +} + +int sqlite_query_get_int(SQLITE_RESULT *result, int pos, int col) +{ + char *value; + + sqlite_query_get(result, pos, col, &value, NULL); + if (!value) + return 0; + else + return atoi(value); +} + + +int sqlite_query_find_field(SQLITE_RESULT *result, const char *name) +{ + int i; + char *field; + char *p; + + /*fprintf(stderr, "sqlite_query_find_field: %s\n", name); + + for (i = 0; i < result->ncol; i++) + fprintf(stderr, "'%s' ", result->names[i]); + fprintf(stderr, "\n");*/ + + if (strchr(name, '.')) + { + for (i = 0; i < result->ncol; i++) + { + if (strcmp(result->names[i], name) == 0) + return i; + } + } + else + { + for (i = 0; i < result->ncol; i++) + { + field = result->names[i]; + p = strchr(field, '.'); + if (p) + field = p + 1; + + if (strcmp(field, name) == 0) + return i; + } + } + + return -1; +} diff --git a/gb.db.sqlite3/src/helper.h b/gb.db.sqlite3/src/helper.h new file mode 100644 index 00000000..d44cdbc8 --- /dev/null +++ b/gb.db.sqlite3/src/helper.h @@ -0,0 +1,68 @@ +/*************************************************************************** + + helper.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __HELPER_H +#define __HELPER_H + +#include "main.h" +#include "gb_buffer.h" + +typedef + struct { + sqlite3 *handle; + char *path; + char *host; + int error; + } + SQLITE_DATABASE; + +SQLITE_DATABASE *sqlite_open_database(const char *name, const char *host); +void sqlite_close_database(SQLITE_DATABASE *db); +const char *sqlite_get_error_message(SQLITE_DATABASE *db); + +typedef + struct { + SQLITE_DATABASE *db; + int nrow; + int ncol; + char **names; + int *types; + int *lengths; + char *buffer; + int *values; + } + SQLITE_RESULT; + +GB_TYPE sqlite_get_type(const char *type, int *length); + +SQLITE_RESULT *sqlite_query_exec(SQLITE_DATABASE *db, const char *query, bool need_types); +void sqlite_query_free(SQLITE_RESULT *result); +int sqlite_query_find_field(SQLITE_RESULT *result, const char *name); + +void sqlite_query_get(SQLITE_RESULT *result, int pos, int col, char **value, int *length); +char *sqlite_query_get_string(SQLITE_RESULT *result, int pos, int col); +int sqlite_query_get_int(SQLITE_RESULT *result, int pos, int col); + + +#endif /* __HELPER_H */ + diff --git a/gb.db.sqlite3/src/main.c b/gb.db.sqlite3/src/main.c new file mode 100644 index 00000000..7686ea96 --- /dev/null +++ b/gb.db.sqlite3/src/main.c @@ -0,0 +1,2534 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include +#include /* For basename function */ +#include /* For passwd file functions */ +#include /* For group file functions */ +#include +#include +#include +#include +#include + +#include "gb.db.proto.h" +#include "helper.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; +DB_INTERFACE DB EXPORT; + +static DB_DRIVER _driver; + +static char _buffer[32]; + +static int _print_query = FALSE; +static bool _need_field_type = FALSE; +static char *_table_schema = NULL; + + +//-------------------------------------------------------------------------- + +/* Internal function to convert a database value into a Gambas variant value */ + +static void conv_data(const char *data, GB_VARIANT_VALUE * val, int type) +{ + GB_VALUE conv; + GB_DATE_SERIAL date; + double sec; + + switch (type) + { + case GB_T_BOOLEAN: + + val->type = GB_T_BOOLEAN; + if (data[0] == 't' || data[0] == 'T') + val->value._boolean = -1; + else + val->value._boolean = atoi(data) ? -1 : 0; + break; + + case GB_T_INTEGER: + + GB.NumberFromString(GB_NB_READ_INTEGER, data, strlen(data), &conv); + + val->type = GB_T_INTEGER; + val->value._integer = conv._integer.value; + + break; + + case GB_T_FLOAT: + + GB.NumberFromString(GB_NB_READ_FLOAT, data, strlen(data), &conv); + + val->type = GB_T_FLOAT; + val->value._float = conv._float.value; + + break; + + case GB_T_LONG: + + GB.NumberFromString(GB_NB_READ_LONG, data, strlen(data), &conv); + + val->type = GB_T_LONG; + val->value._long = conv._long.value; + + break; + + case GB_T_DATE: + + memset(&date, 0, sizeof(date)); + + switch (strlen(data)) + { + case 14: + sscanf(data, "%4d%2d%2d%2d%2d%lf", &date.year, &date.month, + &date.day, &date.hour, &date.min, &sec); + date.sec = (short) sec; + date.msec = (short) ((sec - date.sec) * 1000 + 0.5); + break; + case 12: + sscanf(data, "%2d%2d%2d%2d%2d%lf", &date.year, &date.month, + &date.day, &date.hour, &date.min, &sec); + date.sec = (short) sec; + date.msec = (short) ((sec - date.sec) * 1000 + 0.5); + break; + case 10: + if (sscanf(data, "%4d-%2d-%2d", &date.year, &date.month, + &date.day) != 3) + { + if (sscanf(data, "%4d/%2d/%2d", &date.year, &date.month, + &date.day) != 3) + { + if (sscanf(data, "%4d:%2d:%lf", &date.hour, &date.min, + &sec) == 3) + { + date.sec = (short) sec; + date.msec = (short) ((sec - date.sec) * 1000 + 0.5); + } + else + { + sscanf(data, "%2d%2d%2d%2d%2d", &date.year, + &date.month, &date.day, &date.hour, &date.min); + } + } + } + + break; + case 8: + if (sscanf(data, "%4d%2d%2d", &date.year, &date.month, + &date.day) != 3) + { + sscanf(data, "%2d/%2d/%2d", &date.year, &date.month, + &date.day); + } + break; + case 6: + sscanf(data, "%2d%2d%2d", &date.year, &date.month, &date.day); + break; + case 4: + sscanf(data, "%2d%2d", &date.year, &date.month); + break; + case 2: + sscanf(data, "%2d", &date.year); + break; + default: + sscanf(data, "%4d-%2d-%2d %2d:%2d:%lf", &date.year, + &date.month, &date.day, &date.hour, &date.min, &sec); + date.sec = (short) sec; + date.msec = (short) ((sec - date.sec) * 1000 + 0.5); + } + if (date.year < 100) + date.year += 1900; + + GB.MakeDate(&date, (GB_DATE *) & conv); + + val->type = GB_T_DATE; + val->value._date.date = conv._date.value.date; + val->value._date.time = conv._date.value.time; + + break; + + case (int)DB_T_BLOB: + // Blob fields are read by blob_read() + val->type = GB_T_NULL; + break; + + default: + + val->type = GB_T_CSTRING; + val->value._string = (char *)data; + } +} + +/* internal function to quote a value stored as a blob */ + +static void quote_blob(const char *data, int len, DB_FORMAT_CALLBACK add) +{ + int i; + unsigned char c; + char buffer[2]; + static const char *hexa_digit = "0123456789ABCDEF"; + + if (len == 0) + { + (*add) ("NULL", 4); + return; + } + + (*add) ("X'", 2); + for (i = 0; i < len; i++) + { + c = (unsigned char) data[i]; + buffer[0] = hexa_digit[c >> 4]; + buffer[1] = hexa_digit[c & 15]; + + (*add) (buffer, 2); + } + (*add) ("'", 1); +} + + +/* Internal function to substitute the table name into a query */ + +static char *query_param[3]; + +static void query_get_param(int index, char **str, int *len, char quote) +{ + if (index > 3) + return; + + index--; + *str = query_param[index]; + *len = strlen(*str); + + if (quote == '\'') + { + *str = DB.QuoteString(*str, *len, quote); + *len = GB.StringLength(*str); + } +} + +/* Internal function to run a query */ + +static int do_query(DB_DATABASE *db, const char *error, SQLITE_RESULT **pres, const char *qtemp, int nsubst, ...) +{ + SQLITE_DATABASE *conn = (SQLITE_DATABASE *)db->handle; + SQLITE_RESULT *res; + va_list args; + int i; + const char *query; + int err; + int retry = 0; + int max_retry; + + if (nsubst) + { + va_start(args, nsubst); + if (nsubst > 3) + nsubst = 3; + for (i = 0; i < nsubst; i++) + query_param[i] = va_arg(args, char *); + + query = DB.SubstString(qtemp, 0, query_get_param); + } + else + query = qtemp; + + if (_print_query) + { + _print_query = FALSE; + } + + if (DB.IsDebug()) + fprintf(stderr, "gb.db.sqlite3: %p: %s\n", conn, query); + + if (db->timeout > 0) + max_retry = db->timeout * 5; + else if (db->timeout == 0) + max_retry = 600; // 120 s max + else + max_retry = 0; + + for(;;) + { + err = 0; + + res = sqlite_query_exec(conn, query, _need_field_type); + + if (res) + { + if (pres) + *pres = res; + else + sqlite_query_free(res); + break; + } + + err = conn->error; + + if (err != SQLITE_BUSY || retry >= max_retry) + { + GB.Error(error, sqlite_get_error_message(conn)); + break; + } + + retry++; + usleep(200000); + } + + db->error = err; + _need_field_type = FALSE; + return err != 0; +} + +/* Internal function to check whether a file is a sqlite database file */ + +static bool is_sqlite2_database(const char *filename) +{ + /* SQLite databases start with the string: + * ** This file contains an SQLite 2.1 database ** + * */ + FILE* fp; + bool res; + char magic_text[48]; + + fp = fopen(filename, "r"); + if (!fp) + return FALSE; + + res = fread(magic_text, 1, 47, fp) == 47; + fclose(fp); + + if (!res) + return FALSE; + + magic_text[47] = '\0'; + + if (strcmp(magic_text, "** This file contains an SQLite 2.1 database **")) + return FALSE; + + return TRUE; +} + +static bool is_sqlite3_database(const char *filename) +{ + FILE *fp; + bool res; + char magic_text[16]; + + fp = fopen(filename, "r"); + if (!fp) + return FALSE; + + res = fread(magic_text, 1, 15, fp) == 15; + fclose(fp); + + if (!res) + return FALSE; + + magic_text[15] = '\0'; + + if (strcmp(magic_text, "SQLite format 3")) + return FALSE; + + return TRUE; +} + +static bool is_database_file(const char *filename) +{ + return is_sqlite3_database(filename) || is_sqlite2_database(filename); +} + + +/* Internal function to locate database and return full qualified */ +/* path. */ +static char *find_database(const char *name, const char *hostName) +{ + char *dbhome = NULL; + char *fullpath = NULL; + + /* Does Name includes fullpath */ + if (*name == '/') + { + if (is_database_file(name)) + fullpath = GB.NewZeroString(name); + + return fullpath; + } + + /* Hostname contains home area */ + fullpath = GB.NewZeroString(hostName); + fullpath = GB.AddChar(fullpath, '/'); + fullpath = GB.AddString(fullpath, name, 0); + if (is_database_file(fullpath)) + { + return fullpath; + } + GB.FreeString(&fullpath); + + /* Check the GAMBAS_SQLITE_DBHOME setting */ + dbhome = getenv("GAMBAS_SQLITE_DBHOME"); + + if (dbhome != NULL) + { + fullpath = GB.NewZeroString(dbhome); + fullpath = GB.AddChar(fullpath, '/'); + fullpath = GB.AddString(fullpath, name, 0); + + if (is_database_file(fullpath)) + return fullpath; + + GB.FreeString(&fullpath); + } + + fullpath = GB.NewZeroString(GB.TempDir()); + fullpath = GB.AddString(fullpath, "/sqlite/", 0); + fullpath = GB.AddString(fullpath, name, 0); + + if (is_database_file(fullpath)) + return fullpath; + + GB.FreeString(&fullpath); + return NULL; +} + +/* Internal function to return database home directory */ +/* - GAMBAS_SQLITE_HOMEDB if set or temporary directory /tmp/gambas.%pid%/ */ + +static char *get_database_home() +{ + char *env = NULL; + char *dbhome = NULL; + + GB.Alloc(POINTER(&dbhome), PATH_MAX); + + /* Check for Environment variable */ + + env = getenv("GAMBAS_SQLITE_DBHOME"); + + /* if not set then set to current working directory */ + if (env == NULL) + { + /* + if (getcwd(dbhome, PATH_MAX) == NULL){ + GB.Error("Unable to get databases: &1", "Can't find current directory"); + GB.Free((void **)&dbhome); + return NULL; + } + */ + + sprintf(dbhome, "%s/sqlite", GB.TempDir()); + } + else + { + strcpy(dbhome, env); + } + + return dbhome; +} + + +// Internal function to walk a directory and list files +// Used by database_list + +static int walk_directory(const char *dir, char ***databases) +{ + DIR *dp; + struct dirent *entry; + struct stat statbuf; + char cwd[PATH_MAX]; + + if ((dp = opendir(dir)) == NULL) + return -1; + + if (getcwd(cwd, PATH_MAX) == NULL) + { + fprintf(stderr, "gb.db.sqlite3: warning: getcwd: %s\n", strerror(errno)); + return -1; + } + + if (chdir(dir)) + { + fprintf(stderr, "gb.db.sqlite3: warning: chdir: %s\n", strerror(errno)); + return -1; + } + + while ((entry = readdir(dp)) != NULL) + { + stat(entry->d_name, &statbuf); + + if (S_ISREG(statbuf.st_mode)) + { + if (is_database_file(entry->d_name)) + *(char **)GB.Add(databases) = GB.NewZeroString(entry->d_name); + } + } + + // BM: you must call closedir() + closedir(dp); + + if (chdir(cwd)) + fprintf(stderr, "gb.db.sqlite3: warning: chdir: %s\n", strerror(errno)); + + return GB.Count(databases); +} + + +/* Internal function to check database version number */ +static int db_version() +{ + //Check db version + int dbversion = 0; + unsigned int verMain, verMajor, verMinor; + + sscanf(sqlite3_libversion(), "%2u.%2u.%2u", &verMain, &verMajor, &verMinor); + dbversion = ((verMain * 10000) + (verMajor * 100) + verMinor); + return dbversion; +} + +/* Get the schema of a table */ +static char *get_table_schema(DB_DATABASE *db, const char *table) +{ + char *schema = NULL; + SQLITE_RESULT *res; + + if (!do_query(db, "Unable to get table schema: &1", &res, "select sql from sqlite_master where type = 'table' and tbl_name = '&1'", 1, table)) + { + schema = GB.NewZeroString(sqlite_query_get_string(res, 0, 0)); + sqlite_query_free(res); + } + + return schema; +} + +/***************************************************************************** + + get_quote() + + Returns the character used for quoting object names. + +*****************************************************************************/ + +static const char *get_quote(void) +{ + return QUOTE_STRING; +} + +/***************************************************************************** + + open_database() + + Connect to a database. + + points at a structure describing each connection parameter. + + In Sqlite, there is no such thing as a host. If this is set then check + to see whether this is actually a path to a home area. NG 01/04/04 + + This function must return a database handle, or NULL if the connection + has failed. + +*****************************************************************************/ + +static int open_database(DB_DESC *desc, DB_DATABASE *db) +{ + SQLITE_DATABASE *conn; + char *path; + char *host; + + host = desc->host; + if (!host) + host = ""; + + if (desc->name) + { + path = find_database(desc->name, host); + if (!path) + { + GB.Error("Unable to locate database `&1` in `&2`", desc->name, host); + return TRUE; + } + } + else + path = NULL; + + if (path) + { + if (is_sqlite2_database(path)) + { + DB.TryAnother("sqlite2"); + GB.FreeString(&path); + return TRUE; + } + } + + conn = sqlite_open_database(path, host); + GB.FreeString(&path); + + if (!conn) + { + GB.Error("Cannot open database: &1", sqlite_get_error_message(NULL)); + return TRUE; + } + + db->handle = conn; + /* set dbversion */ + db->version = db_version(); + + if (do_query(db, "Unable to initialize connection: &1", NULL, "PRAGMA empty_result_callbacks = ON", 0)) + goto CANNOT_OPEN; + + if (db->version < 30803) + { + /* NG 29/12/2005 - 3.2.1 introduced a problem with columns names + * which is resolved by setting short columns off first */ + + if (do_query(db, "Unable to initialize connection: &1", NULL, "PRAGMA short_column_names = OFF", 0)) + goto CANNOT_OPEN; + } + + if (do_query(db, "Unable to initialize connection: &1", NULL, "PRAGMA full_column_names = ON", 0)) + goto CANNOT_OPEN; + + /* Character set cannot be set for sqlite. A sqlite re-compile + * is required. */ + db->charset = GB.NewZeroString("UTF-8"); + + /* flags */ + db->flags.no_table_type = TRUE; + db->flags.no_nest = TRUE; + + db->db_name_char = "."; + + return FALSE; + +CANNOT_OPEN: + + sqlite_close_database(conn); + return TRUE; +} + + +/***************************************************************************** + + close_database() + + Terminates the database connection. + + contains the database handle. + +*****************************************************************************/ + +static void close_database(DB_DATABASE * db) +{ + sqlite_close_database((SQLITE_DATABASE *)db->handle); +} + + +/***************************************************************************** + + get_collations() + + Return the available collations as a Gambas string array. + +*****************************************************************************/ + +static GB_ARRAY get_collations(DB_DATABASE *db) +{ + static const char *const collations[] = { "BINARY", "NOCASE", "RTRIM" }; + GB_ARRAY array; + int i; + + GB.Array.New(&array, GB_T_STRING, 3); + for (i = 0; i < 3; i++) + *((char **)GB.Array.Get(array, i)) = GB.NewZeroString(collations[i]); + + return array; +} + + +/***************************************************************************** + + format_value() + + This function transforms a gambas value into a string value that can + be inserted into a SQL query. + + points to the value. + is a callback called to insert the string into the query. + + This function must return TRUE if it translates the value, and FALSE if + it does not. + + If the value is not translated, then a default translation is used. + +*****************************************************************************/ + +static int format_value(GB_VALUE * arg, DB_FORMAT_CALLBACK add) +{ + char *s; + int i, l; + GB_DATE_SERIAL *date; + + switch (arg->type) + { + case GB_T_BOOLEAN: +/*Note this is likely to go to a tinyint */ + if (VALUE((GB_BOOLEAN *) arg)) + add("'1'", 3); + else + add("'0'", 3); + return TRUE; + + case GB_T_STRING: + case GB_T_CSTRING: + + s = VALUE((GB_STRING *)arg).addr + VALUE((GB_STRING *)arg).start; + l = VALUE((GB_STRING *)arg).len; + + add("'", 1); + + for (i = 0; i < l; i++, s++) + { + add(s, 1); + if (*s == '\'') + add(s, 1); + } + + add("'", 1); + + return TRUE; + + case GB_T_DATE: + + date = GB.SplitDate((GB_DATE *) arg); + + l = sprintf(_buffer, "'%04d-%02d-%02d %02d:%02d:%02d", + date->year, date->month, date->day, + date->hour, date->min, date->sec); + + add(_buffer, l); + + if (date->msec) + { + l = sprintf(_buffer, ".%03d", date->msec); + add(_buffer, l); + } + + add("'", 1); + + return TRUE; + + default: + return FALSE; + } +} + + +/***************************************************************************** + + format_blob() + + This function transforms a blob value into a string value that can + be inserted into a SQL query. + + points to the DB_BLOB structure. + is a callback called to insert the string into the query. + +*****************************************************************************/ + +static void format_blob(DB_BLOB * blob, DB_FORMAT_CALLBACK add) +{ + quote_blob(blob->data, blob->length, add); +} + + +/***************************************************************************** + + exec_query() + + Send a query to the server and gets the result. + + is the database handle, as returned by open_database() + is the query string. + will receive the result handle of the query. + is an error message used when the query failed. + + can be NULL, when we don't care getting the result. + +*****************************************************************************/ + +static int exec_query(DB_DATABASE *db, const char *query, DB_RESULT *result, const char *err) +{ + _need_field_type = TRUE; + return do_query(db, err, (SQLITE_RESULT **)result, query, 0); +} + + +/***************************************************************************** + + get_last_insert_id() + + Return the value of the last serial field used in an INSERT statement + + is the database handle, as returned by open_database() + +*****************************************************************************/ + +static int64_t get_last_insert_id(DB_DATABASE *db) +{ + SQLITE_RESULT *res; + + if (do_query(db, "Unable to retrieve last insert id", &res, "select last_insert_rowid();", 0)) + return -1; + + return atoll(sqlite_query_get_string(res, 0, 0)); +} + + +/***************************************************************************** + + query_init() + + Initialize an info structure from a query result. + + is the handle of the query result. + points to the info structure. + will receive the number of records returned by the query. + + This function must initialize the info->nfield field with the number of + field in the query result. + +*****************************************************************************/ + +static void query_init(DB_RESULT result, DB_INFO * info, int *count) +{ + SQLITE_RESULT *res = (SQLITE_RESULT *)result; + + if (res) + { + *count = res->nrow; + info->nfield = res->ncol; + } + else + { + *count = 0; + info->nfield = 0; + } +} + + +/***************************************************************************** + + query_release() + + Free the info structure filled by query_init() and the result handle. + + is the handle of the query result. + points to the info structure. + +*****************************************************************************/ + +static void query_release(DB_RESULT result, DB_INFO * info) +{ + sqlite_query_free((SQLITE_RESULT *)result); +} + + +/***************************************************************************** + + query_fill() + + Fill a result buffer with the value of each field of a record. + + is the database handle, as returned by open_database() + is the handle of the result. + is the index of the record in the result. + points to an array having one element for each field in the + result. + is a boolean telling if we want the next row. + + This function must return DB_OK, DB_ERROR or DB_NO_DATA + + This function must use GB.StoreVariant() to store the value in the + buffer. + +*****************************************************************************/ + +static int query_fill(DB_DATABASE *db, DB_RESULT result, int pos, GB_VARIANT_VALUE * buffer, int next) +{ + SQLITE_RESULT *res = (SQLITE_RESULT *)result; + int i; + char *data; + int len; + GB_VARIANT value; + int type; + + for (i = 0; i < res->ncol; i++) + { + type = res->types[i]; + + if (type == DB_T_BLOB) + data = NULL; + else + { + sqlite_query_get(res, pos, i, &data, &len); + if (len == 0) + data = NULL; + } + + value.type = GB_T_VARIANT; + value.value.type = GB_T_NULL; + + if (data) + conv_data(data, &value.value, type); + + //GB.FreeString(&data); + GB.StoreVariant(&value, &buffer[i]); + } + + return DB_OK; +} + + +/***************************************************************************** + + blob_read() + + Returns the value of a BLOB field. + + is the handle of the result. + is the index of the record in the result. + points at a DB_BLOB structure that will receive a pointer to the + data and its length. + +*****************************************************************************/ + +static void blob_read(DB_RESULT result, int pos, int field, DB_BLOB * blob) +{ + sqlite_query_get((SQLITE_RESULT *)result, pos, field, &blob->data, &blob->length); + blob->constant = TRUE; +} + + +/***************************************************************************** + + field_name() + + Return the name of a field in a result from its index. + + is the result handle. + is the field index. + +*****************************************************************************/ + +static char *field_name(DB_RESULT result, int field) +{ + return (char *)((SQLITE_RESULT *)result)->names[field]; +} + + +/***************************************************************************** + + field_index() + + Return the index of a field in a result from its name. + + is the result handle. + is the field name. + can be ignored by this driver. + +*****************************************************************************/ + +static int field_index(DB_RESULT result, const char *name, DB_DATABASE *db) +{ + return sqlite_query_find_field((SQLITE_RESULT *)result, name); +} + + +/***************************************************************************** + + field_type() + + Return the Gambas type of a field in a result from its index. + + is the result handle. + is the field index. + +*****************************************************************************/ + +static GB_TYPE field_type(DB_RESULT result, int field) +{ + return (GB_TYPE)((SQLITE_RESULT *)result)->types[field]; +} + + +/***************************************************************************** + + field_length() + + Return the length of a field in a result from its index. + + is the result handle. + is the field index. + +*****************************************************************************/ + +static int field_length(DB_RESULT result, int field) +{ + return ((SQLITE_RESULT *)result)->lengths[field]; +} + + +/***************************************************************************** + + begin_transaction() + + Begin a transaction. + + is the database handle. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + + In mysql commit/rollback can only be used with transaction safe tables (BDB, + or InnoDB tables) + + ISAM, MyISAM and HEAP tables will commit straight away. The transaction + methods are therefore ignored. + +*****************************************************************************/ + +static int begin_transaction(DB_DATABASE * db) +{ + return do_query(db, "Unable to begin transaction: &1", NULL, "BEGIN", 0); +} + + +/***************************************************************************** + + commit_transaction() + + Commit a transaction. + + is the database handle. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int commit_transaction(DB_DATABASE * db) +{ + return do_query(db, "Unable to commit transaction: &1", NULL, "COMMIT", 0); +} + + +/***************************************************************************** + + rollback_transaction() + + Rolllback a transaction. + + is the database handle. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int rollback_transaction(DB_DATABASE * db) +{ + return do_query(db, "Unable to rollback transaction: &1", NULL, "ROLLBACK", 0); +} + +/***************************************************************************** + + table_init() + + Initialize an info structure from table fields. + + is the database handle. +
    is the table name. + points at the info structure. + + This function must initialize the following info fields: + - info->nfield must contain the number of fields in the table. + - info->fields is a char*[] pointing at the name of each field. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int field_info(DB_DATABASE * db, const char *table, const char *field, DB_FIELD * info); + +static int table_init(DB_DATABASE * db, const char *table, DB_INFO * info) +{ + const char *qfield = "PRAGMA table_info('&1')"; + + SQLITE_RESULT *res; + int i, n; + DB_FIELD *f; + char *field; + + /* Nom de la table */ + + info->table = GB.NewZeroString(table); + + /* Liste des champs */ + + if (do_query(db, "Unable to get table fields: &1", &res, qfield, 1, table)) + return TRUE; + + info->nfield = n = res->nrow; + if (n == 0) + { + sqlite_query_free(res); + return TRUE; + } + + GB.Alloc(POINTER(&info->field), sizeof(DB_FIELD) * n); + + for (i = 0; i < n; i++) + { + sqlite_query_get(res, i, 1, &field, NULL); + f = &info->field[i]; + + if (field_info(db, table, field, f)) + { + sqlite_query_free(res); + return TRUE; + } + + f->name = GB.NewZeroString(field); + } + + sqlite_query_free(res); + return FALSE; +} + + +/***************************************************************************** + + table_index() + + Initialize an info structure from table primary index. + + is the database handle. +
    is the table name. + points at the info structure. + + This function must initialize the following info fields: + - info->nindex must contain the number of fields in the primary index. + - info->index is a int[] giving the index of each index field in + info->fields. + + This function must be called after table_init(). + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_index(DB_DATABASE * db, const char *table, DB_INFO * info) +{ + const char *qindex1 = "PRAGMA index_list('&1')"; + const char *qindex2 = "PRAGMA index_info('&1')"; + + SQLITE_RESULT *res; + int n = 0; + int i; + char *sql; + char *data; + + /* Index primaire */ + + info->nindex = 0; + + if (do_query(db, "Unable to get primary index: &1", &res, qindex1, 1, table)) + return TRUE; + + n = res->nrow; + + for (i = 0; i < n; i++) + { + data = sqlite_query_get_string(res, i, 2); + if (*data != '1') + continue; + + data = sqlite_query_get_string(res, i, 1); + if (strstr(data, "autoindex") == NULL) + continue; + + sql = GB.NewZeroString(data); + + sqlite_query_free(res); + + if (do_query(db, "Unable to get information on primary index: &1", &res, qindex2, 1, sql)) + { + GB.FreeString(&sql); + return TRUE; + } + GB.FreeString(&sql); + + info->nindex = res->nrow; + GB.Alloc(POINTER(&info->index), sizeof(int) * info->nindex); + + for (i = 0; i < info->nindex; i++) + info->index[i] = sqlite_query_get_int(res, i, 1); + break; + } + + sqlite_query_free(res); + + if (info->nindex == 0) + { + // [BM] If there is no primary key, we suppose that the first field of INTEGER datatype is the primary key. + // Because we use INTEGER only when creating the AUTOINCREMENT field. + + if (do_query(db, "Unable to get primary index: &1", &res, "PRAGMA table_info('&1')", 1, table)) + return TRUE; + + info->nindex = 1; + GB.Alloc(POINTER(&info->index), sizeof(int)); + + for (i = 0; i < res->nrow; i++) + { + if (strcasecmp(sqlite_query_get_string(res, i, 2), "INTEGER") == 0) + { + info->index[0] = i; + break; + } + } + + sqlite_query_free(res); + + if (i >= res->nrow) + { + GB.Free(POINTER(&info->index)); + return TRUE; + } + else + { + return FALSE; + } + } + + return FALSE; +} + + +/***************************************************************************** + + table_release() + + Free the info structure filled by table_init() and/or table_index() + + is the database handle. + points at the info structure. + +*****************************************************************************/ + +static void table_release(DB_DATABASE * db, DB_INFO * info) +{ + /* All is done outside the driver */ +} + +/***************************************************************************** + + table_exist() + + Returns if a table exists + + is the database handle. +
    is the table name. + + This function returns TRUE if the table exists, and FALSE if not. + +*****************************************************************************/ + +static int table_exist(DB_DATABASE * db, const char *table) +{ + const char *query = "select tbl_name from " + "( select tbl_name from sqlite_master where type = 'table' union " + "select tbl_name from sqlite_temp_master where type = 'table' ) " + "where tbl_name = '&1'"; + + if (strcmp(table, "sqlite_master") == 0 + || strcmp(table, "sqlite_temp_master") == 0) + { + return TRUE; + } + + SQLITE_RESULT *res; + int exist; + + if (do_query(db, "Unable to check table: &1", &res, query, 1, table)) + return FALSE; + + exist = res->nrow > 0; + sqlite_query_free(res); + return exist; +} + +/***************************************************************************** + + table_list() + + Returns an array containing the name of each table in the database + + is the database handle. + points to a variable that will receive the char* array. + + This function returns the number of tables, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int table_list(DB_DATABASE * db, char ***tables) +{ + const char *query = + "select tbl_name from " + "( select tbl_name from sqlite_master where type = 'table' union " + " select tbl_name from sqlite_temp_master where type = 'table')"; + + SQLITE_RESULT *res; + int nrow; + char *data; + int len; + int i; + + if (do_query(db, "Unable to get tables: &1", &res, query, 0)) + return -1; + + nrow = res->nrow; + + // sqlite_master and sqlite_temp_master need to be added to the list + GB.NewArray(tables, sizeof(char *), nrow + 2); + + for (i = 0; i < nrow; i++) + { + sqlite_query_get(res, i, 0, &data, &len); + (*tables)[i] = GB.NewString(data, len); + } + + sqlite_query_free(res); + + (*tables)[nrow] = GB.NewZeroString("sqlite_master"); + (*tables)[nrow + 1] = GB.NewZeroString("sqlite_temp_master"); + return nrow + 2; +} + +/***************************************************************************** + + table_primary_key() + + Returns a string representing the primary key of a table. + + is the database handle. +
    is the table name. + points to a string that will receive the primary key. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +#if 0 +static int old_table_primary_key(DB_DATABASE * db, const char *table, char ***primary) +{ + const char *qindex1 = "PRAGMA index_list('&1')"; + const char *qindex2 = "PRAGMA index_info('&1')"; + + Dataset *res; + int i, n; + char *sql; + result_set *r; + + if (do_query + (db, "Unable to get primary key: &1", &res, qindex1, 1, table)) + return TRUE; + + GB.NewArray(primary, sizeof(char *), 0); + + r = (result_set *) res->getResult(); + n = r->records.size(); + + for (i = 0; i < n; i++) + { + if (strstr(r->records[i][1].get_asString().data(), "autoindex")) + { + if (strncmp("1", r->records[i][2].get_asString().data(), 1) == 0) + { + sql = GB.NewZeroString(r->records[i][1].get_asString().data()); + res->close(); + + if (do_query(db, "Unable to get primary key: &1", &res, qindex2, 1, sql)) + { + res->close(); + GB.FreeString(&sql); + return TRUE; + } + GB.FreeString(&sql); + + r = (result_set *) res->getResult(); + if ((n = r->records.size()) < 1) + { + // No information returned for key + res->close(); + return TRUE; + } + + for (i = 0; i < n; i++) + { + *(char **)GB.Add(primary) = GB.NewZeroString(r->records[i][2].get_asString().data()); + } + break; + } + } + } + + res->close(); + + // [BM] If there is no primary key, we suppose that the first field of INTEGER datatype is the primary key. + // Because we use INTEGER only when creating the AUTOINCREMENT field. + + if (GB.Count(*primary) == 0) + { + if (do_query + (db, "Unable to get primary key: &1", &res, + "PRAGMA table_info('&1')", 1, table)) + return TRUE; + + r = (result_set *) res->getResult(); + + for (i = 0; i < (int) r->records.size(); i++) + { + if (strcasecmp(r->records[i][5].get_asString().data(), "0") != 0) + { + *(char **)GB.Add(primary) = GB.NewZeroString(r->records[i][1].get_asString().data()); + break; + } + } + } + + return FALSE; +} +#endif + +static int table_primary_key(DB_DATABASE * db, const char *table, char ***primary) +{ + SQLITE_RESULT *res; + int i, j, n; + char *data; + int len; + + if (do_query(db, "Unable to get primary key: &1", &res, "PRAGMA table_info('&1')", 1, table)) + return TRUE; + + n = 0; + for (i = 0; i < res->nrow; i++) + { + j = sqlite_query_get_int(res, i, 5); + if (j > n) + n = j; + } + + GB.NewArray(primary, sizeof(char *), n); + + for (i = 0; i < res->nrow; i++) + { + j = sqlite_query_get_int(res, i, 5); + if (j > 0) + { + sqlite_query_get(res, i, 1, &data, &len); + (*primary)[j - 1] = GB.NewString(data, len); + } + } + + sqlite_query_free(res); + return FALSE; +} + +/***************************************************************************** + + table_is_system() + + Returns if a table is a system table. + + is the database handle. +
    is the table name. + + This function returns TRUE if the table is a system table, and FALSE if + not. + + Note: According to the documentation, all tables beginning without + "sqlite_" are reserved. + +*****************************************************************************/ + +static int table_is_system(DB_DATABASE * db, const char *table) +{ + return strncmp(table, "sqlite_", 7) == 0; +} + +/***************************************************************************** + + table_type() + + Not Valid in Sqlite + + is the database handle. +
    is the table name. + +*****************************************************************************/ + +static char *table_type(DB_DATABASE * db, const char *table, const char *type) +{ + if (type) + GB.Error("SQLite does not have any table types"); + return NULL; +} + +/***************************************************************************** + + table_delete() + + Deletes a table. + + is the database handle. +
    is the table name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_delete(DB_DATABASE * db, const char *table) +{ + return do_query(db, "Unable to delete table: &1", NULL, "drop table '&1'", 1, table); +} + +/***************************************************************************** + + table_create() + + Creates a table. + + is the database handle. +
    is the table name. + points to a linked list of field descriptions. + is the primary key. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int table_create(DB_DATABASE * db, const char *table, DB_FIELD * fields, char **primary, const char *not_used) +{ + DB_FIELD *fp; + int comma; + const char *type; + int i; + bool no_pkey = FALSE; + + DB.Query.Init(); + + DB.Query.Add("CREATE TABLE "); + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(table); + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(" ( "); + + comma = FALSE; + for (fp = fields; fp; fp = fp->next) + { + if (comma) + DB.Query.Add(", "); + else + comma = TRUE; + + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(fp->name); + DB.Query.Add(QUOTE_STRING); + + if (fp->type == DB_T_SERIAL) + { + DB.Query.Add(" INTEGER PRIMARY KEY AUTOINCREMENT"); + no_pkey = TRUE; + } + else if (fp->type == DB_T_BLOB) + { + DB.Query.Add(" BLOB "); + } + else + { + switch (fp->type) + { + case GB_T_BOOLEAN: + type = "BOOL"; + break; + case GB_T_INTEGER: + type = "INT4"; + break; + case GB_T_LONG: + type = "BIGINT"; + break; + case GB_T_FLOAT: + type = "FLOAT8"; + break; + case GB_T_DATE: + type = "DATETIME"; + break; + case GB_T_STRING: + + if (fp->length <= 0) + type = "TEXT"; + else + { + sprintf(_buffer, "VARCHAR(%d)", fp->length); + type = _buffer; + } + + break; + + default: + type = "TEXT"; + break; + } + + DB.Query.Add(" "); + DB.Query.Add(type); + + if (fp->collation && *fp->collation) + { + DB.Query.Add(" COLLATE "); + DB.Query.Add(fp->collation); + } + + if (fp->def.type != GB_T_NULL) + { + DB.Query.Add(" NOT NULL DEFAULT "); + DB.FormatVariant(&_driver, &fp->def, DB.Query.AddLength); + } + else if (DB.StringArray.Find(primary, fp->name) >= 0) + { + DB.Query.Add(" NOT NULL "); + } + } + } + + if (primary && !no_pkey) + { + DB.Query.Add(", PRIMARY KEY ("); + + for (i = 0; i < GB.Count(primary); i++) + { + if (i > 0) + DB.Query.Add(","); + + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(primary[i]); + DB.Query.Add(QUOTE_STRING); + } + + DB.Query.Add(")"); + } + + DB.Query.Add(" )"); + + return do_query(db, "Cannot create table: &1", NULL, DB.Query.Get(), 0); +} + +/***************************************************************************** + + field_exist() + + Returns if a field exists in a given table + + is the database handle. +
    is the table name. + is the field name. + + This function returns TRUE if the field exists, and FALSE if not. + +*****************************************************************************/ + +static int field_exist(DB_DATABASE * db, const char *table, const char *field) +{ + const char *query = "PRAGMA table_info('&1')"; + + int i; + SQLITE_RESULT *res; + int exist = 0; + + if (do_query(db, "Unable to find field: &1.&2", &res, query, 2, table, field)) + return FALSE; + + for (i = 0; i < res->nrow; i++) + { + + if (strcmp(field, sqlite_query_get_string(res, i, 1)) == 0) + { + exist++; + break; + } + } + + sqlite_query_free(res); + return exist; +} + +/***************************************************************************** + + field_list() + + Returns an array containing the name of each field in a given table + + is the database handle. +
    is the table name. + points to a variable that will receive the char* array. + + This function returns the number of fields, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int field_list(DB_DATABASE * db, const char *table, char ***fields) +{ + const char *query = "PRAGMA table_info('&1')"; + + int i, n; + SQLITE_RESULT *res; + + if (do_query(db, "Unable to get fields: &1", &res, query, 1, table)) + return -1; + + n = res->nrow; + + if (fields) /* (BM) see the function commentary */ + { + GB.NewArray(fields, sizeof(char *), n); + + for (i = 0; i < n; i++) + (*fields)[i] = GB.NewZeroString(sqlite_query_get_string(res, i, 1)); + } + + sqlite_query_free(res); + return n; +} + + +/***************************************************************************** + + field_info() + + Get field description + + is the database handle. +
    is the table name. + is the field name. + points to a structure filled by the function. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int field_info(DB_DATABASE *db, const char *table, const char *field, DB_FIELD *info) +{ + const char *query = "PRAGMA table_info('&1')"; + + SQLITE_RESULT *res; + GB_VARIANT def; + char *val; + char *_fieldName = NULL; + char *_fieldType = NULL; + char *_defaultValue = NULL; + bool _fieldNotNull = FALSE; + int i, n; + bool autoinc; + char *schema; + int type; + + if (do_query(db, "Unable to get fields: &1", &res, query, 1, table)) + return TRUE; + + n = res->nrow; + + //fprintf(stderr, "field_info: %s.%s\n", table, field); + + for (i = 0; i < n; i++) + { + _fieldName = sqlite_query_get_string(res, i, 1); + + if (strcmp(_fieldName, field) == 0) + { + _fieldType = sqlite_query_get_string(res, i, 2); + _fieldNotNull = sqlite_query_get_int(res, i, 3) != 0; + _defaultValue = sqlite_query_get_string(res, i, 4); + break; + } + } + + if (i >= n) + { + GB.Error("Unable to find field &1.&2", table, field); + sqlite_query_free(res); + return TRUE; + } + + //fprintf(stderr, "field_info: type = %s not_null = %s default = %s\n", _fieldType, _fieldNotNull, _defaultValue); + + info->name = NULL; + + // This API is not always defined! + //(sqlite3_table_column_metadata(db_handle, NULL, table, field, NULL, NULL, NULL, NULL, &autoinc) != SQLITE_OK) + + /*{ + // [BM] We use INTEGER only when creating the AUTOINCREMENT field. + fprintf(stderr, "_fieldType = %s\n", _fieldType); + autoinc = strstr(_fieldType, "INTEGER") && strstr(_fieldType, "AUTOINCREMENT"); + }*/ + + autoinc = FALSE; + info->collation = NULL; + + if (_table_schema) + schema = _table_schema; + else + schema = get_table_schema(db, table); + + if (schema) + { + char *p, *p2; + char *field_desc; + int len; + + p = strchr(schema, '('); + if (p) + { + while (*p != ')') + { + p++; + p2 = strchr(p, ','); + if (!p2) + p2 = p + strlen(p) - 1; + + while (p < p2 && *p == ' ') + p++; + + if (*p == '\'' || *p == '"') + p++; + + len = strlen(_fieldName); + if ((p2 - p) < len || strncasecmp(p, _fieldName, len)) + { + p = p2; + continue; + } + + p += len; + if (*p == '\'') + p++; + + len = p2 - p; + if (len <= 0) + break; + + field_desc = GB.NewString(p, len); + + if (strstr(_fieldType, "INTEGER")) + { + if (strstr(field_desc, "AUTOINCREMENT")) + autoinc = TRUE; + } + + p = strstr(field_desc, "COLLATE"); + if (p) + { + p += 7; + while (*p == ' ') + p++; + + p2 = strchr(p, ' '); + if (!p2) + p2 = field_desc + len; + info->collation = GB.NewString(p, p2 - p); + } + + GB.FreeString(&field_desc); + break; + } + + } + } + + if (!_table_schema) + GB.FreeString(&schema); + + type = sqlite_get_type(_fieldType, &info->length); + + if (autoinc) + info->type = DB_T_SERIAL; + else + info->type = type; + + //fprintf(stderr, "field_info: type = %d length = %d\n", info->type, info->length); + + info->def.type = GB_T_NULL; + + if (_fieldNotNull) + { + def.type = GB_T_VARIANT; + def.value.type = GB_T_NULL; + + val = DB.UnquoteString(_defaultValue, strlen(_defaultValue), '\''); + + if (val && *val) + { + conv_data(val, &def.value, type); + GB.StoreVariant(&def, &info->def); + } + } + + sqlite_query_free(res); + return FALSE; +} + +/***************************************************************************** + + index_exist() + + Returns if an index exists in a given table + + is the database handle. +
    is the table name. + is the index name. + + This function returns TRUE if the index exists, and FALSE if not. + +*****************************************************************************/ + +static int index_exist(DB_DATABASE * db, const char *table, const char *index) +{ + const char *query = "select tbl_name from " + "( select tbl_name from sqlite_master where type = 'index' and " + " name = '&2' union " + "select tbl_name from sqlite_temp_master where type = 'index' and " + " name = '&2' ) " "where tbl_name = '&1'"; + + SQLITE_RESULT *res; + int exist; + + if (do_query(db, "Unable to check table: &1", &res, query, 2, table, index)) + return FALSE; + + exist = res->nrow > 0; + sqlite_query_free(res); + return exist; +} + +/***************************************************************************** + + index_list() + + Returns an array containing the name of each index in a given table + + is the database handle. +
    is the table name. + points to a variable that will receive the char* array. + + This function returns the number of indexes, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + +*****************************************************************************/ + +static int index_list(DB_DATABASE * db, const char *table, char ***indexes) +{ + const char *query = + "select name from " + "( select name from sqlite_master where type = 'index' and tbl_name = '&1' " + " union select name from sqlite_temp_master where type = 'index' and " + " tbl_name = '&1')"; + + SQLITE_RESULT *res; + int nindex, i; + + if (do_query(db, "Unable to get tables: &1", &res, query, 1, table)) + return -1; + + nindex = res->nrow; + GB.NewArray(indexes, sizeof(char *), nindex); + + for (i = 0; i < nindex; i++) + (*indexes)[i] = GB.NewZeroString(sqlite_query_get_string(res, i, 0)); + + sqlite_query_free(res); + return nindex; +} + +/***************************************************************************** + + index_info() + + Get index description + + is the database handle. +
    is the table name. + is the index name. + points to a structure filled by the function. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int index_info(DB_DATABASE * db, const char *table, const char *index, DB_INDEX * info) +{ + /* sqlite indexes are unique to the database, and not to the table */ + + const char *qindex1 = "PRAGMA index_list('&1')"; + const char *qindex2 = "PRAGMA index_info('&1')"; + + SQLITE_RESULT *res; + int i, j, n; + + if (do_query(db, "Unable to get index info for table: &1", &res, qindex1, 1, table)) + return TRUE; + + if ((n = res->nrow) == 0) + { + /* no indexes found */ + sqlite_query_free(res); + GB.Error("Unable to find index &1.&2", table, index); + return TRUE; + } + + for (i = 0, j = 0; i < n; i++) + { /* Find the required index */ + if (strcmp(index, sqlite_query_get_string(res, i, 1)) == 0) + { + j++; + break; + } + } + + if (j == 0) + { + GB.Error("Unable to find index &1.&2", table, index); + sqlite_query_free(res); + return TRUE; + } + + info->name = NULL; + info->unique = sqlite_query_get_int(res, i, 2) != 0; + info->primary = strstr(sqlite_query_get_string(res, i, 1), "autoindex") != NULL; + + sqlite_query_free(res); + + DB.Query.Init(); + + if (do_query(db, "Unable to get index info for : &1", &res, qindex2, 1, index)) + return TRUE; + + n = res->nrow; + i = 0; + /* (BM) row can be null if we are seeking the last index */ + while (i < n) + { + if (i > 0) + DB.Query.Add(","); + + /* Get fields in key */ + DB.Query.Add(sqlite_query_get_string(res, i, 2)); + i++; + } + + sqlite_query_free(res); + info->fields = DB.Query.GetNew(); + + return FALSE; +} + +/***************************************************************************** + + index_delete() + + Deletes an index. + + is the database handle. +
    is the table name. + is the index name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int index_delete(DB_DATABASE * db, const char *table, const char *index) +{ + return do_query(db, "Unable to delete index: &1", NULL, "drop index &1", 1, index); +} + +/***************************************************************************** + + index_create() + + Creates an index. + + is the database handle. +
    is the table name. + is the index name. + points to a structure describing the index. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + +*****************************************************************************/ + +static int index_create(DB_DATABASE * db, const char *table, const char *index, DB_INDEX *info) +{ + DB.Query.Init(); + + DB.Query.Add("CREATE "); + if (info->unique) + DB.Query.Add("UNIQUE "); + DB.Query.Add("INDEX "); + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(index); + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(" ON "); + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(table); + DB.Query.Add(QUOTE_STRING); + DB.Query.Add(" ( "); + DB.Query.Add(info->fields); + DB.Query.Add(" )"); + + return do_query(db, "Cannot create index: &1", NULL, DB.Query.Get(), 0); +} + +/***************************************************************************** + + database_exist() + + Returns if a database exists + + is any database handle. + is the database name. + + This function returns TRUE if the database exists, and FALSE if not. + SQLite: Databases are just files, so we need to ceck to see whether + the file exists and is a sqlite file. + +******************************************************************************/ + +static int database_exist(DB_DATABASE *db, const char *name) +{ + SQLITE_DATABASE *conn = (SQLITE_DATABASE *)db->handle; + char *fullpath; + bool exist; + + if (!name || !*name || strcmp(name, ":memory:") == 0) + return TRUE; //Database is loaded in memory only + + fullpath = find_database(name, conn->host); + exist = fullpath != NULL; + GB.FreeString(&fullpath); + return exist; +} + +/***************************************************************************** + + database_list() + + Returns an array containing the name of each database + + is any database handle. + points to a variable that will receive the char* array. + + This function returns the number of databases, or -1 if the command has + failed. + + Be careful: can be NULL, so that just the count is returned. + + Sqlite databases are files. Using we will only list + files within a designated directory. Propose that all + areas are walked through. + + ******************************************************************************/ + +static int database_list(DB_DATABASE *db, char ***databases) +{ + SQLITE_DATABASE *conn = (SQLITE_DATABASE *)db->handle; + const char *dbhome; + + GB.NewArray(databases, sizeof(char *), 0); + + /* Hostname contains home area */ + if (conn->host && *conn->host) + { + walk_directory(conn->host, databases); + } + else + { + dbhome = get_database_home(); + if (dbhome) + { + walk_directory(dbhome, databases); + GB.Free(POINTER(&dbhome)); + } + } + + return GB.Count(databases); +} + +/***************************************************************************** + * + * database_is_system() + * + * Returns if a database is a system database. + * + * is any database handle. + * is the database name. + * + * This function returns TRUE if the database is a system database, and + * FALSE if not. + * + * Note: Sqlite doesn't have such a thing. + ******************************************************************************/ + +static int database_is_system(DB_DATABASE * db, const char *name) +{ + return FALSE; +} + +/***************************************************************************** + * + * database_delete() + * + * Deletes a database. + * + * is the database handle. + * is the database name. + * + * This function returns TRUE if the command has failed, and FALSE if + * everything was OK. + * + ******************************************************************************/ + +static int database_delete(DB_DATABASE * db, const char *name) +{ + SQLITE_DATABASE *conn = (SQLITE_DATABASE *)db->handle; + char *fullpath = NULL; + char *other; + bool err; + + fullpath = find_database(name, conn->host); + + if (!fullpath) + { + GB.Error("Cannot find database: &1", name); + err = TRUE; + } + else if (unlink(fullpath) != 0) + { + GB.Error("Unable to delete database &1", fullpath); + err = TRUE; + } + else + { + other = GB.NewString(fullpath, GB.StringLength(fullpath)); + other = GB.AddString(other, "-shm", 4); + unlink(other); + GB.FreeString(&other); + + other = GB.NewString(fullpath, GB.StringLength(fullpath)); + other = GB.AddString(other, "-wal", 4); + unlink(other); + GB.FreeString(&other); + + err = FALSE; + } + + GB.FreeString(&fullpath); + return err; +} + +/***************************************************************************** + * + * database_create() + * + * Creates a database. + * + * is the database handle. + * is the database name. + * + * This function returns TRUE if the command has failed, and FALSE if + * everything was OK. + * + * SQLite automatically creates a database on connect if the file + * does not exist. + ******************************************************************************/ + +static int database_create(DB_DATABASE *db, const char *name) +{ + SQLITE_DATABASE *conn, *save; + char *fullpath = NULL; + const char *host = NULL; + char *dir; + + save = (SQLITE_DATABASE *)db->handle; + + /* Does name include fullpath? */ + if (name && name[0] == '/') + { + fullpath = GB.NewZeroString(name); + goto _CREATE_DATABASE; + } + + /* Hostname contains home area */ + host = save->host; + if (host && host[0]) + { + fullpath = GB.NewZeroString(host); + } + else + { + dir = get_database_home(); + mkdir(dir, S_IRWXU); + fullpath = GB.NewZeroString(dir); + GB.Free(POINTER(&dir)); + } + + if (fullpath[strlen(fullpath) - 1] != '/') + fullpath = GB.AddChar(fullpath, '/'); + + fullpath = GB.AddString(fullpath, name, 0); + +_CREATE_DATABASE: + + if (DB.IsDebug()) + fprintf(stderr, "sqlite3: create database: %s\n", fullpath); + + conn = sqlite_open_database(fullpath, host); + GB.FreeString(&fullpath); + + if (!conn) + { + GB.Error("Cannot create database: &1", sqlite_get_error_message(NULL)); + return TRUE; + } + + //Create and remove a table to initialize database + db->handle = conn; + if (!do_query(db, "Unable to initialise database", NULL, "CREATE TABLE GAMBAS (FIELD1 TEXT)", 0)) + do_query(db, NULL, NULL, "DROP TABLE GAMBAS", 0); + + sqlite_close_database(conn); + db->handle = save; + + return FALSE; +} + + +/***************************************************************************** + * + * user_exist() + * + * Returns if a user exists. + * + * is any database handle. + * is the user name. + * + * This function returns TRUE if the user exists, and FALSE if not. + * Sqlite does not have different users. Access is controlled by + * access rightd on the file. + * We can check that the user exists on the machine and has access to + * database file! + * [Currently only checks against /etc/passwd. + * Does not check against /etc/shadow or pam. + * + ******************************************************************************/ + +static int user_exist(DB_DATABASE *db, const char *name) +{ + return FALSE; +} + +/***************************************************************************** + * + * user_list() + * + * Returns an array containing the name of each user. + * + * is the database handle. + * points to a variable that will receive the char* array. + * + * This function returns the number of users, or -1 if the command has + * failed. + * + * Be careful: can be NULL, so that just the count is returned. + * Sqlite does not have users. + * + ******************************************************************************/ + + +static int user_list(DB_DATABASE * db, char ***users) +{ + if (users) + GB.NewArray(users, sizeof(char *), 0); + return 0; +} + +/***************************************************************************** + * + * user_info() + * + * Get user description + * + * is the database handle. + * is the user name. + * points to a structure filled by the function. + * + * This function returns TRUE if the command has failed, and FALSE if + * everything was OK. + * + * Sqlite privileges are just file privileges. We will return Admin + * rights where privilege allows Write. There is no password. + ******************************************************************************/ + +static int user_info(DB_DATABASE * db, const char *name, DB_USER * info) +{ + GB.Error("Invalid user &1", name); + return TRUE; +} + +/***************************************************************************** + + user_delete() + + Deletes a user. + + is any database handle. + is the user name. + + This function returns TRUE if the command has failed, and FALSE if + everything was OK. + + Sqlite users are operated by the O/S + +*****************************************************************************/ + +static int user_delete(DB_DATABASE * db, const char *name) +{ + GB.Error("Invalid user: &1", name); + return TRUE; +} + +/***************************************************************************** + * + * user_create() + * + * Creates a user. + * + * is the database handle. + * is the user name. + * points to a structure describing the user. + * + * This function returns TRUE if the command has failed, and FALSE if + * everything was OK. + * + * + * Sqlite: No user create + ******************************************************************************/ + +static int user_create(DB_DATABASE * db, const char *name, DB_USER * info) +{ + GB.Error("SQLite has no users"); + return TRUE; +} + +/***************************************************************************** + * + * user_set_password() + * + * Change the user password. + * + * is the database handle. + * is the user name. + * is the new password + * + * This function returns TRUE if the command has failed, and FALSE if + * everything was OK. + * + * Sqlite : No user passwords. + ******************************************************************************/ + +static int user_set_password(DB_DATABASE * db, const char *name, const char *password) +{ + GB.Error("SQLite has no users"); + return TRUE; +} + + +/***************************************************************************** + + The driver interface + +*****************************************************************************/ + +DECLARE_DRIVER(_driver, "sqlite3"); + +/***************************************************************************** + + The component entry and exit functions. + +*****************************************************************************/ + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.db", DB_INTERFACE_VERSION, &DB); + DB.Register(&_driver); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.db.sqlite3/src/main.h b/gb.db.sqlite3/src/main.h new file mode 100644 index 00000000..dcb91bb3 --- /dev/null +++ b/gb.db.sqlite3/src/main.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "../gb.db.h" +#include "sqlite3.h" + +#ifndef __MAIN_C + extern GB_INTERFACE GB; + extern DB_INTERFACE DB; +#endif + +#define QUOTE_STRING "\"" + +#endif /* __MAIN_H */ diff --git a/gb.dbus/AUTHORS b/gb.dbus/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.dbus/COPYING b/gb.dbus/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.dbus/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.dbus/ChangeLog b/gb.dbus/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.dbus/INSTALL b/gb.dbus/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.dbus/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.dbus/Makefile.am b/gb.dbus/Makefile.am new file mode 100644 index 00000000..cf7e823a --- /dev/null +++ b/gb.dbus/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @DBUS_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.dbus/NEWS b/gb.dbus/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.dbus/README b/gb.dbus/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.dbus/acinclude.m4 b/gb.dbus/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.dbus/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.dbus/component.am b/gb.dbus/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.dbus/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.dbus/configure.ac b/gb.dbus/configure.ac new file mode 100644 index 00000000..bc9333b2 --- /dev/null +++ b/gb.dbus/configure.ac @@ -0,0 +1,15 @@ +dnl ---- configure.ac for gb.dbus + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-dbus, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.dbus) +AC_PROG_LIBTOOL + +GB_COMPONENT_PKG_CONFIG( + dbus, DBUS, gb.dbus, [src], + dbus-1) +AC_OUTPUT( Makefile src/Makefile ) + +GB_PRINT_MESSAGES diff --git a/gb.dbus/gambas.h b/gb.dbus/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.dbus/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.dbus/gb_common.h b/gb.dbus/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.dbus/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.dbus/m4 b/gb.dbus/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.dbus/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.dbus/reconf b/gb.dbus/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.dbus/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.dbus/src/Makefile.am b/gb.dbus/src/Makefile.am new file mode 100644 index 00000000..861988c5 --- /dev/null +++ b/gb.dbus/src/Makefile.am @@ -0,0 +1,17 @@ +COMPONENT = gb.dbus +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.dbus.la + +gb_dbus_la_LIBADD = @DBUS_LIB@ +gb_dbus_la_LDFLAGS = -module @LD_FLAGS@ @DBUS_LDFLAGS@ +gb_dbus_la_CPPFLAGS = @DBUS_INC@ + +gb_dbus_la_SOURCES = \ + main.c main.h \ + helper.c helper.h \ + dbus_print_message.c dbus_print_message.h \ + c_dbus.c c_dbus.h \ + c_dbusvariant.c c_dbusvariant.h \ + c_dbusconnection.c c_dbusconnection.h \ + c_dbusobserver.c c_dbusobserver.h diff --git a/gb.dbus/src/c_dbus.c b/gb.dbus/src/c_dbus.c new file mode 100644 index 00000000..c797742a --- /dev/null +++ b/gb.dbus/src/c_dbus.c @@ -0,0 +1,74 @@ +/*************************************************************************** + + c_dbus.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_DBUS_C + +#include "helper.h" +#include "c_dbusconnection.h" +#include "c_dbus.h" + +BEGIN_PROPERTY(DBus_System) + + GB.ReturnObject(CDBUSCONNECTION_get(DBUS_BUS_SYSTEM)); + +END_PROPERTY + +BEGIN_PROPERTY(DBus_Session) + + GB.ReturnObject(CDBUSCONNECTION_get(DBUS_BUS_SESSION)); + +END_PROPERTY + +BEGIN_PROPERTY(DBus_Debug) + + if (READ_PROPERTY) + GB.ReturnBoolean(DBUS_Debug); + else + DBUS_Debug = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_METHOD(DBus_SplitSignature, GB_STRING sign) + + GB.ReturnObject(DBUS_split_signature(GB.ToZeroString(ARG(sign)))); + +END_METHOD + +GB_DESC CDBusDesc[] = +{ + GB_DECLARE_STATIC("_DBus"), + + GB_STATIC_PROPERTY_READ("System", "DBusConnection", DBus_System), + GB_STATIC_PROPERTY_READ("Session", "DBusConnection", DBus_Session), + GB_STATIC_PROPERTY("Debug", "b", DBus_Debug), + + GB_CONSTANT("Method", "i", DBUS_MESSAGE_TYPE_METHOD_CALL), + GB_CONSTANT("Reply", "i", DBUS_MESSAGE_TYPE_METHOD_RETURN), + GB_CONSTANT("Signal", "i", DBUS_MESSAGE_TYPE_SIGNAL), + GB_CONSTANT("Error", "i", DBUS_MESSAGE_TYPE_ERROR), + + GB_STATIC_METHOD("_SplitSignature", "String[]", DBus_SplitSignature, "(Signature)s"), + + GB_END_DECLARE +}; + diff --git a/gb.dbus/src/c_dbus.h b/gb.dbus/src/c_dbus.h new file mode 100644 index 00000000..860d41dc --- /dev/null +++ b/gb.dbus/src/c_dbus.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + c_dbus.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_DBUS_H +#define __C_DBUS_H + +#include "main.h" + +#ifndef __C_DBUS_C + +extern GB_DESC CDBusDesc[]; + +#endif + +#endif /* __CDBUS_H */ diff --git a/gb.dbus/src/c_dbusconnection.c b/gb.dbus/src/c_dbusconnection.c new file mode 100644 index 00000000..1da35d64 --- /dev/null +++ b/gb.dbus/src/c_dbusconnection.c @@ -0,0 +1,286 @@ +/*************************************************************************** + + c_dbusconnection.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_DBUSCONNECTION_C + +#include "helper.h" +#include "c_dbusconnection.h" + +static CDBUSCONNECTION *_system = NULL; +static CDBUSCONNECTION *_session = NULL; + +static DBusConnection *get_bus(DBusBusType type) +{ + DBusConnection *conn; + + conn = dbus_bus_get(type, NULL); + if (!conn) + GB.Error("Cannot connect to the &1 bus", type == DBUS_BUS_SYSTEM ? "system" : "session"); + else + dbus_connection_set_exit_on_disconnect(conn, FALSE); + + return conn; +} + +CDBUSCONNECTION *CDBUSCONNECTION_get(DBusBusType type) +{ + DBusConnection *conn; + + if (type == DBUS_BUS_SYSTEM) + { + if (!_system) + { + conn = get_bus(type); + if (conn) + { + _system = GB.New(GB.FindClass("DBusConnection"), NULL, NULL); + GB.Ref(_system); + _system->connection = conn; + } + } + return _system; + } + else if (type == DBUS_BUS_SESSION) + { + if (!_session) + { + conn = get_bus(type); + if (conn) + { + _session = GB.New(GB.FindClass("DBusConnection"), NULL, NULL); + GB.Ref(_session); + _session->connection = conn; + } + } + return _session; + } + else + return NULL; +} + +BEGIN_METHOD_VOID(DBusConnection_exit) + + GB.Unref(POINTER(&_session)); + GB.Unref(POINTER(&_system)); + +END_METHOD + +BEGIN_METHOD_VOID(DBusConnection_free) + + GB.StoreVariant(NULL, &THIS->tag); + dbus_connection_unref(THIS->connection); + +END_METHOD + +BEGIN_METHOD(DBusConnection_Introspect, GB_STRING application; GB_STRING object) + + char *application = GB.ToZeroString(ARG(application)); + char *object; + + if (!MISSING(object)) + object = GB.ToZeroString(ARG(object)); + else + object = "/"; + + GB.ReturnNewZeroString(DBUS_introspect(THIS->connection, application, object)); + +END_METHOD + +BEGIN_METHOD(DBusConnection_CallMethod, GB_STRING application; GB_STRING object; GB_STRING interface; GB_STRING method; + GB_STRING input_signature; GB_STRING output_signature; GB_OBJECT arguments) + + char *application = GB.ToZeroString(ARG(application)); + char *object = GB.ToZeroString(ARG(object)); + char *interface = GB.ToZeroString(ARG(interface)); + char *method = GB.ToZeroString(ARG(method)); + char *input_signature = GB.ToZeroString(ARG(input_signature)); + char *output_signature = GB.ToZeroString(ARG(output_signature)); + + if (DBUS_validate_path(object, LENGTH(object))) + { + GB.Error("Invalid object path"); + return; + } + + if (!*interface) + interface = NULL; + else if (DBUS_validate_interface(interface, LENGTH(interface))) + { + GB.Error("Invalid interface name"); + return; + } + + if (DBUS_validate_method(method, LENGTH(method))) + { + GB.Error("Invalid method name"); + return; + } + + if (DBUS_call_method(THIS->connection, application, object, interface, method, input_signature, output_signature, VARG(arguments))) + return; + + GB.ReturnConvVariant(); + +END_METHOD + +BEGIN_METHOD(DBusConnection_SendSignal, GB_STRING object; GB_STRING interface; GB_STRING signal; GB_STRING input_signature; GB_OBJECT arguments) + + char *object = GB.ToZeroString(ARG(object)); + char *interface = GB.ToZeroString(ARG(interface)); + char *signal = GB.ToZeroString(ARG(signal)); + char *input_signature = GB.ToZeroString(ARG(input_signature)); + + if (DBUS_validate_path(object, LENGTH(object))) + { + GB.Error("Invalid object path"); + return; + } + + if (!*interface) + interface = NULL; + else if (DBUS_validate_interface(interface, LENGTH(interface))) + { + GB.Error("Invalid interface name"); + return; + } + + if (DBUS_validate_method(signal, LENGTH(signal))) + { + GB.Error("Invalid signal name"); + return; + } + + if (DBUS_send_signal(THIS->connection, object, interface, signal, input_signature, VARG(arguments))) + return; + +END_METHOD + +BEGIN_PROPERTY(DBusConnection_Applications) + + DBUS_call_method(THIS->connection, "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames", "", "as", NULL); + +END_PROPERTY + + +BEGIN_METHOD(DBusConnection_RequestName, GB_STRING name; GB_BOOLEAN unique) + + bool ret = DBUS_register(THIS->connection, GB.ToZeroString(ARG(name)), VARGOPT(unique, FALSE)); + + GB.ReturnBoolean(ret); + +END_METHOD + + +BEGIN_METHOD(DBusConnection_ReleaseName, GB_STRING name) + + bool ret = DBUS_unregister(THIS->connection, GB.ToZeroString(ARG(name))); + + GB.ReturnBoolean(ret); + +END_METHOD + + +BEGIN_PROPERTY(DBusConnection_Name) + + GB.ReturnNewZeroString(dbus_bus_get_unique_name(THIS->connection)); + +END_PROPERTY + +BEGIN_METHOD(DBusConnection_Register, GB_OBJECT object; GB_STRING path; GB_OBJECT interfaces) + + GB_FUNCTION func; + void *object = VARG(object); + + if (GB.CheckObject(object)) + return; + + if (GB.GetFunction(&func, object, "_Register", NULL, NULL)) + { + GB.Error("Cannot find _Register method"); + return; + } + + if (MISSING(interfaces)) + { + GB.Push(2, GB_T_OBJECT, THIS, GB_T_STRING, STRING(path), LENGTH(path)); + GB.Call(&func, 2, TRUE); + } + else + { + GB.Push(3, GB_T_OBJECT, THIS, GB_T_STRING, STRING(path), LENGTH(path), GB_T_OBJECT, VARG(interfaces)); + GB.Call(&func, 3, TRUE); + } + +END_METHOD + +BEGIN_METHOD(DBusConnection_Unregister, GB_OBJECT object) + + GB_FUNCTION func; + void *object = VARG(object); + + if (GB.CheckObject(object)) + return; + + if (GB.GetFunction(&func, object, "_Unregister", NULL, NULL)) + { + GB.Error("Cannot find _Unregister method"); + return; + } + + GB.Push(1, GB_T_OBJECT, THIS); + GB.Call(&func, 1, TRUE); + +END_METHOD + +BEGIN_PROPERTY(DBusConnection_Tag) + + if (READ_PROPERTY) + GB.ReturnVariant(&THIS->tag); + else + GB.StoreVariant(PROP(GB_VARIANT), POINTER(&(THIS->tag))); + +END_METHOD + + +GB_DESC CDBusConnectionDesc[] = +{ + GB_DECLARE("DBusConnection", sizeof(CDBUSCONNECTION)), GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("_exit", NULL, DBusConnection_exit, NULL), + GB_METHOD("_free", NULL, DBusConnection_free, NULL), + GB_METHOD("_Introspect", "s", DBusConnection_Introspect, "(Application)s[(Object)s]"), + GB_METHOD("_CallMethod", "v", DBusConnection_CallMethod, "(Application)s(Object)s(Interface)s(Method)s(InputSignature)s(OutputSignature)s(Arguments)Array;"), + GB_METHOD("_SendSignal", "v", DBusConnection_SendSignal, "(Object)s(Interface)s(Signal)s(InputSignature)s(Arguments)Array;"), + //GB_METHOD("_AddMatch", "b", DBusConnection_AddMatch, "(Type)s[(Object)s(Member)s(Interface)s(Destination)s]"), + //GB_METHOD("_RemoveMatch", "b", DBusConnection_RemoveMatch, "(Type)s[(Object)s(Member)s(Interface)s(Destination)s]"), + GB_PROPERTY_READ("Applications", "String[]", DBusConnection_Applications), + GB_METHOD("_RequestName", "b", DBusConnection_RequestName, "(Name)s[(Unique)b]"), + GB_METHOD("_ReleaseName", "b", DBusConnection_ReleaseName, "(Name)s"), + GB_PROPERTY_READ("_Name", "s", DBusConnection_Name), + GB_METHOD("Register", NULL, DBusConnection_Register, "(Object)DBusObject;(Path)s[(Interface)String[];]"), + GB_METHOD("Unregister", NULL, DBusConnection_Unregister, "(Object)DBusObject;"), + GB_PROPERTY("Tag", "v", DBusConnection_Tag), + + GB_END_DECLARE +}; + diff --git a/gb.dbus/src/c_dbusconnection.h b/gb.dbus/src/c_dbusconnection.h new file mode 100644 index 00000000..91b652e1 --- /dev/null +++ b/gb.dbus/src/c_dbusconnection.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + c_dbusconnection.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_DBUSCONNECTION_H +#define __C_DBUSCONNECTION_H + +#include "main.h" + +#ifndef __C_DBUSCONNECTION_C + +extern GB_DESC CDBusConnectionDesc[]; + +#else + +#define THIS ((CDBUSCONNECTION *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + DBusConnection *connection; + GB_VARIANT_VALUE tag; + } + CDBUSCONNECTION; + +CDBUSCONNECTION *CDBUSCONNECTION_get(DBusBusType type); + +#endif /* __CDBUS_H */ diff --git a/gb.dbus/src/c_dbusobserver.c b/gb.dbus/src/c_dbusobserver.c new file mode 100644 index 00000000..8345710c --- /dev/null +++ b/gb.dbus/src/c_dbusobserver.c @@ -0,0 +1,322 @@ +/*************************************************************************** + + c_dbusobserver.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_DBUSOBSERVER_C + +#include "helper.h" +#include "c_dbusconnection.h" +#include "c_dbusobserver.h" + +DECLARE_EVENT(EVENT_MESSAGE); + +CDBUSOBSERVER *DBUS_observers = NULL; + +void DBUS_raise_observer(CDBUSOBSERVER *_object) +{ + GB.Raise(THIS, EVENT_MESSAGE, 0); +} + +static char *add_rule(char *match, const char *name, const char *rule) +{ + if (!rule) + return match; + + if (rule[0] == '*' && rule[1] == 0) + return match; + + if (match) + match = GB.AddChar(match, ','); + + match = GB.AddString(match, name, 0); + match = GB.AddString(match, "='", 2); + match = GB.AddString(match, rule, 0); + match = GB.AddChar(match, '\''); + return match; +} + +static void set_filter(char **property, const char *str, int len) +{ + if (!str) + return; + + if (len < 0) + len = strlen(str); + + if (len == 0) // || (len == 1 && *str == '*')) + return; + + *property = GB.NewString(str, len); +} + +static void update_match(CDBUSOBSERVER *_object, bool noerr) +{ + //static char *type[] = { "method_call", "method_return", "signal", "error" }; + char *match = NULL; + DBusError error; + + switch(THIS->type) + { + case DBUS_MESSAGE_TYPE_METHOD_CALL: + match = add_rule(match, "type", "method_call"); + break; + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + match = add_rule(match, "type", "method_return"); + break; + case DBUS_MESSAGE_TYPE_SIGNAL: + match = add_rule(match, "type", "signal"); + break; + case DBUS_MESSAGE_TYPE_ERROR: + match = add_rule(match, "type", "error"); + break; + default: + GB.Error("Invalid message type"); + return; + } + + match = add_rule(match, "path", THIS->object); + match = add_rule(match, "member", THIS->member); + match = add_rule(match, "interface", THIS->interface); + + if (THIS->destination && *(THIS->destination)) + match = add_rule(match, "destination", THIS->destination); + else + match = add_rule(match, "destination", dbus_bus_get_unique_name(THIS->connection)); + + dbus_error_init(&error); + + DBUS_watch(THIS->connection, THIS->enabled); + + if (THIS->enabled) + { + if (DBUS_Debug) + fprintf(stderr, "gb.dbus: add match: %s\n", match); + dbus_bus_add_match(THIS->connection, match, &error); + if (dbus_error_is_set(&error)) + { + if (!noerr) + GB.Error("Cannot enable observer"); + dbus_error_free(&error); + THIS->enabled = FALSE; + DBUS_watch(THIS->connection, FALSE); + } + } + else + { + if (DBUS_Debug) + fprintf(stderr, "gb.dbus: remove match: %s\n", match); + dbus_bus_remove_match(THIS->connection, match, &error); + if (dbus_error_is_set(&error)) + { + if (!noerr) + GB.Error("Cannot disable observer"); + dbus_error_free(&error); + THIS->enabled = TRUE; + DBUS_watch(THIS->connection, TRUE); + } + } + + dbus_connection_flush(THIS->connection); + + GB.FreeString(&match); +} + +BEGIN_METHOD(DBusObserver_new, GB_OBJECT connection; GB_INTEGER type; GB_STRING object; GB_STRING member; GB_STRING interface; GB_STRING destination) + + CDBUSCONNECTION *connection = VARG(connection); + + if (GB.CheckObject(connection)) + return; + + THIS->connection = connection->connection; + + THIS->type = VARG(type); + if (!MISSING(object)) set_filter(&THIS->object, STRING(object), LENGTH(object)); + if (!MISSING(member)) set_filter(&THIS->member, STRING(member), LENGTH(member)); + if (!MISSING(interface)) set_filter(&THIS->interface, STRING(interface), LENGTH(interface)); + if (!MISSING(destination)) set_filter(&THIS->destination, STRING(destination), LENGTH(destination)); + + THIS->next = DBUS_observers; + + if (DBUS_observers) + DBUS_observers->prev = THIS; + + DBUS_observers = THIS; + + THIS->enabled = TRUE; + update_match(THIS, FALSE); + +END_METHOD + +BEGIN_METHOD_VOID(DBusObserver_free) + + if (THIS->enabled) + { + THIS->enabled = FALSE; + update_match(THIS, TRUE); + } + + if (THIS == DBUS_observers) + DBUS_observers = THIS->next; + + if (THIS->prev) + THIS->prev->next = THIS->next; + + if (THIS->next) + THIS->next->prev = THIS->prev; + + GB.FreeString(&THIS->object); + GB.FreeString(&THIS->member); + GB.FreeString(&THIS->interface); + GB.FreeString(&THIS->destination); + +END_METHOD + +BEGIN_PROPERTY(DBusObserver_Enabled) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->enabled); + else + { + bool e = VPROP(GB_BOOLEAN); + if (e == THIS->enabled) + return; + THIS->enabled = e; + update_match(THIS, FALSE); + } + +END_PROPERTY + + +BEGIN_PROPERTY(DBusObserver_Message) + + if (THIS->message) + RETURN_SELF(); + else + GB.ReturnNull(); + +END_PROPERTY + + +BEGIN_PROPERTY(DBusObserverMessage_Type) + + GB.ReturnInteger(dbus_message_get_type(THIS->message)); + +END_PROPERTY + +BEGIN_PROPERTY(DBusObserverMessage_Serial) + + GB.ReturnInteger(dbus_message_get_serial(THIS->message)); + +END_PROPERTY + +BEGIN_PROPERTY(DBusObserverMessage_Sender) + + GB.ReturnConstZeroString(dbus_message_get_sender(THIS->message)); + +END_PROPERTY + +BEGIN_PROPERTY(DBusObserverMessage_Destination) + + GB.ReturnConstZeroString(dbus_message_get_destination(THIS->message)); + +END_PROPERTY + +BEGIN_PROPERTY(DBusObserverMessage_Object) + + GB.ReturnConstZeroString(dbus_message_get_path(THIS->message)); + +END_PROPERTY + +BEGIN_PROPERTY(DBusObserverMessage_Interface) + + GB.ReturnConstZeroString(dbus_message_get_interface(THIS->message)); + +END_PROPERTY + +BEGIN_PROPERTY(DBusObserverMessage_Member) + + GB.ReturnConstZeroString(dbus_message_get_member(THIS->message)); + +END_PROPERTY + +BEGIN_PROPERTY(DBusObserverMessage_Arguments) + + DBUS_retrieve_message_arguments(THIS->message); + +END_PROPERTY + +BEGIN_METHOD(DBusObserver_Reply, GB_STRING signature; GB_OBJECT args) + + if (THIS->message) + { + if (!DBUS_reply(THIS->connection, THIS->message, MISSING(signature) ? NULL : GB.ToZeroString(ARG(signature)), VARGOPT(args, NULL))) + THIS->reply = TRUE; + } + +END_METHOD + +BEGIN_METHOD(DBusObserver_Error, GB_STRING type; GB_STRING error) + + if (THIS->message) + { + if (!DBUS_error(THIS->connection, THIS->message, MISSING(error) ? NULL : GB.ToZeroString(ARG(error)), MISSING(type) ? NULL : GB.ToZeroString(ARG(type)))) + THIS->reply = TRUE; + } + +END_METHOD + +GB_DESC CDBusObserverMessageDesc[] = +{ + GB_DECLARE(".DBusObserver.Message", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Type", "i", DBusObserverMessage_Type), + GB_PROPERTY_READ("Serial", "i", DBusObserverMessage_Serial), + GB_PROPERTY_READ("Sender", "s", DBusObserverMessage_Sender), + GB_PROPERTY_READ("Destination", "s", DBusObserverMessage_Destination), + GB_PROPERTY_READ("Object", "s", DBusObserverMessage_Object), + GB_PROPERTY_READ("Interface", "s", DBusObserverMessage_Interface), + GB_PROPERTY_READ("Member", "s", DBusObserverMessage_Member), + GB_PROPERTY_READ("Arguments", "Variant[]", DBusObserverMessage_Arguments), + + GB_END_DECLARE +}; + + +GB_DESC CDBusObserverDesc[] = +{ + GB_DECLARE("DBusObserver", sizeof(CDBUSOBSERVER)), + + GB_METHOD("_new", NULL, DBusObserver_new, "(Connection)DBusConnection;(Type)i[(Object)s(Member)s(Interface)s(Destination)s]"), + GB_METHOD("_free", NULL, DBusObserver_free, NULL), + + //GB_PROPERTY("Tag", "v", DBusObserver_Tag), + GB_PROPERTY("Enabled", "b", DBusObserver_Enabled), + GB_PROPERTY_READ("Message", ".DBusObserver.Message", DBusObserver_Message), + GB_METHOD("Reply", NULL, DBusObserver_Reply, "[(Signature)s(Arguments)Array;]"), + GB_METHOD("Error", NULL, DBusObserver_Error, "[(Error)s(Type)s]"), + + GB_EVENT("Message", NULL, NULL, &EVENT_MESSAGE), + + GB_END_DECLARE +}; + diff --git a/gb.dbus/src/c_dbusobserver.h b/gb.dbus/src/c_dbusobserver.h new file mode 100644 index 00000000..d33ad82e --- /dev/null +++ b/gb.dbus/src/c_dbusobserver.h @@ -0,0 +1,63 @@ +/*************************************************************************** + + c_dbusobserver.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_DBUSOBSERVER_H +#define __C_DBUSOBSERVER_H + +#include "main.h" + +typedef + struct CDBUSOBSERVER + { + GB_BASE ob; + struct CDBUSOBSERVER *prev; + struct CDBUSOBSERVER *next; + DBusConnection *connection; + int type; + char *object; + char *member; + char *interface; + char *destination; + DBusMessage *message; + //GB_VARIANT tag; + unsigned enabled : 1; + unsigned reply : 1; + } + CDBUSOBSERVER; + +#ifndef __C_DBUSOBSERVER_C + +extern GB_DESC CDBusObserverDesc[]; +extern GB_DESC CDBusObserverMessageDesc[]; + +extern CDBUSOBSERVER *DBUS_observers; + +#else + +#define THIS ((CDBUSOBSERVER *)_object) + +#endif + +void DBUS_raise_observer(CDBUSOBSERVER *_object); + +#endif /* __CDBUS_H */ diff --git a/gb.dbus/src/c_dbusvariant.c b/gb.dbus/src/c_dbusvariant.c new file mode 100644 index 00000000..e3362c61 --- /dev/null +++ b/gb.dbus/src/c_dbusvariant.c @@ -0,0 +1,72 @@ +/*************************************************************************** + + c_dbusvariant.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_DBUSVARIANT_C + +#include "helper.h" +#include "c_dbusvariant.h" + +BEGIN_METHOD_VOID(DBusVariant_free) + + GB.StoreVariant(NULL, (void *)&THIS->value); + //GB.StoreString(NULL, &THIS->signature); + //fprintf(stderr, "DBusVariant: free: %p\n", THIS); + +END_METHOD + +BEGIN_PROPERTY(DBusVariant_Value) + + if (READ_PROPERTY) + GB.ReturnVariant(&THIS->value); + else + GB.StoreVariant(PROP(GB_VARIANT), (void *)&THIS->value); + +END_PROPERTY + +/*BEGIN_PROPERTY(DBusVariant_Signature) + + GB.ReturnString(THIS->signature); + +END_PROPERTY*/ + +GB_DESC CDBusVariantDesc[] = +{ + GB_DECLARE("DBusVariant", sizeof(CDBUSVARIANT)), + + GB_METHOD("_free", NULL, DBusVariant_free, NULL), + GB_PROPERTY("Value", "v", DBusVariant_Value), + GB_CONSTANT("Signature", "s", "v"), + + GB_END_DECLARE +}; + + +char *CDBUSVARIANT_get_signature(CDBUSVARIANT *_object) +{ + GB_VALUE *val = GB.GetProperty((void *)GB.GetClass(THIS), "Signature"); + + if (!val || (val->type != GB_T_STRING && val->type != GB_T_CSTRING)) + return "v"; + + return val->_string.value.addr; +} diff --git a/gb.dbus/src/c_dbusvariant.h b/gb.dbus/src/c_dbusvariant.h new file mode 100644 index 00000000..69606042 --- /dev/null +++ b/gb.dbus/src/c_dbusvariant.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + c_dbusvariant.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_DBUSVARIANT_H +#define __C_DBUSVARIANT_H + +#include "main.h" + +#ifndef __C_DBUSVARIANT_C + +extern GB_DESC CDBusVariantDesc[]; + +#else + +#define THIS ((CDBUSVARIANT *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + GB_VARIANT_VALUE value; + } + CDBUSVARIANT; + +char *CDBUSVARIANT_get_signature(CDBUSVARIANT *_object); + +#endif /* __CDBUS_H */ diff --git a/gb.dbus/src/dbus_print_message.c b/gb.dbus/src/dbus_print_message.c new file mode 100644 index 00000000..31564c10 --- /dev/null +++ b/gb.dbus/src/dbus_print_message.c @@ -0,0 +1,435 @@ +/*************************************************************************** + + dbus_print_message.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-print-message.h Utility function to print out a message + * + * Copyright (C) 2003 Philip Blundell + * Copyright (C) 2003 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA + * + */ +#include "dbus_print_message.h" + +#include +#include "config.h" + +static const char* +type_to_name (int message_type) +{ + switch (message_type) + { + case DBUS_MESSAGE_TYPE_SIGNAL: + return "signal"; + case DBUS_MESSAGE_TYPE_METHOD_CALL: + return "method call"; + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + return "method return"; + case DBUS_MESSAGE_TYPE_ERROR: + return "error"; + default: + return "(unknown message type)"; + } +} + +#define INDENT 3 + +static void +indent (int depth) +{ + while (depth-- > 0) + fprintf(stderr, " "); /* INDENT spaces. */ +} + +static void +print_hex (unsigned char *bytes, unsigned int len, int depth) +{ + int i, columns; + + fprintf(stderr, "array of bytes [\n"); + + indent (depth + 1); + + /* Each byte takes 3 cells (two hexits, and a space), except the last one. */ + columns = (80 - ((depth + 1) * INDENT)) / 3; + + if (columns < 8) + columns = 8; + + i = 0; + + while (i < len) + { + fprintf(stderr, "%02x", bytes[i]); + i++; + + if (i != len) + { + if (i % columns == 0) + { + fprintf(stderr, "\n"); + indent (depth + 1); + } + else + { + fprintf(stderr, " "); + } + } + } + + fprintf(stderr, "\n"); + indent (depth); + fprintf(stderr, "]\n"); +} + +#define DEFAULT_SIZE 100 + +static void +print_ay (DBusMessageIter *iter, int depth) +{ + /* Not using DBusString because it's not public API. It's 2009, and I'm + * manually growing a string chunk by chunk. + */ + unsigned char *bytes = malloc (DEFAULT_SIZE + 1); + unsigned char *new_bytes; + unsigned int len = 0; + unsigned int max = DEFAULT_SIZE; + dbus_bool_t all_ascii = TRUE; + int current_type; + + while ((current_type = dbus_message_iter_get_arg_type (iter)) + != DBUS_TYPE_INVALID) + { + unsigned char val; + + dbus_message_iter_get_basic (iter, &val); + bytes[len] = val; + len++; + + if (val < 32 || val > 126) + all_ascii = FALSE; + + if (len == max) + { + max *= 2; + new_bytes = realloc (bytes, max + 1); + if (!new_bytes) + break; + bytes = new_bytes; + } + + dbus_message_iter_next (iter); + } + + if (all_ascii) + { + bytes[len] = '\0'; + fprintf(stderr, "array of bytes \"%s\"\n", bytes); + } + else + { + print_hex (bytes, len, depth); + } + + free (bytes); +} + +static void +print_iter (DBusMessageIter *iter, dbus_bool_t literal, int depth) +{ + do + { + int type = dbus_message_iter_get_arg_type (iter); + + if (type == DBUS_TYPE_INVALID) + break; + + indent(depth); + + switch (type) + { + case DBUS_TYPE_STRING: + { + char *val; + dbus_message_iter_get_basic (iter, &val); + if (!literal) + fprintf(stderr, "string \""); + fprintf(stderr, "%s", val); + if (!literal) + fprintf(stderr, "\"\n"); + break; + } + + case DBUS_TYPE_SIGNATURE: + { + char *val; + dbus_message_iter_get_basic (iter, &val); + if (!literal) + fprintf(stderr, "signature \""); + fprintf(stderr, "%s", val); + if (!literal) + fprintf(stderr, "\"\n"); + break; + } + + case DBUS_TYPE_OBJECT_PATH: + { + char *val; + dbus_message_iter_get_basic (iter, &val); + if (!literal) + fprintf(stderr, "object path \""); + fprintf(stderr, "%s", val); + if (!literal) + fprintf(stderr, "\"\n"); + break; + } + + case DBUS_TYPE_INT16: + { + dbus_int16_t val; + dbus_message_iter_get_basic (iter, &val); + fprintf(stderr, "int16 %d\n", val); + break; + } + + case DBUS_TYPE_UINT16: + { + dbus_uint16_t val; + dbus_message_iter_get_basic (iter, &val); + fprintf(stderr, "uint16 %u\n", val); + break; + } + + case DBUS_TYPE_INT32: + { + dbus_int32_t val; + dbus_message_iter_get_basic (iter, &val); + fprintf(stderr, "int32 %d\n", val); + break; + } + + case DBUS_TYPE_UINT32: + { + dbus_uint32_t val; + dbus_message_iter_get_basic (iter, &val); + fprintf(stderr, "uint32 %u\n", val); + break; + } + + case DBUS_TYPE_INT64: + { + dbus_int64_t val; + dbus_message_iter_get_basic (iter, &val); +#ifdef DBUS_INT64_PRINTF_MODIFIER + fprintf(stderr, "int64 %" DBUS_INT64_PRINTF_MODIFIER "d\n", val); +#else + fprintf(stderr, "int64 (omitted)\n"); +#endif + break; + } + + case DBUS_TYPE_UINT64: + { + dbus_uint64_t val; + dbus_message_iter_get_basic (iter, &val); +#ifdef DBUS_INT64_PRINTF_MODIFIER + fprintf(stderr, "uint64 %" DBUS_INT64_PRINTF_MODIFIER "u\n", val); +#else + fprintf(stderr, "uint64 (omitted)\n"); +#endif + break; + } + + case DBUS_TYPE_DOUBLE: + { + double val; + dbus_message_iter_get_basic (iter, &val); + fprintf(stderr, "double %g\n", val); + break; + } + + case DBUS_TYPE_BYTE: + { + unsigned char val; + dbus_message_iter_get_basic (iter, &val); + fprintf(stderr, "byte %d\n", val); + break; + } + + case DBUS_TYPE_BOOLEAN: + { + dbus_bool_t val; + dbus_message_iter_get_basic (iter, &val); + fprintf(stderr, "boolean %s\n", val ? "true" : "false"); + break; + } + + case DBUS_TYPE_VARIANT: + { + DBusMessageIter subiter; + + dbus_message_iter_recurse (iter, &subiter); + + fprintf(stderr, "variant "); + print_iter (&subiter, literal, depth+1); + break; + } + case DBUS_TYPE_ARRAY: + { + int current_type; + DBusMessageIter subiter; + + dbus_message_iter_recurse (iter, &subiter); + + current_type = dbus_message_iter_get_arg_type (&subiter); + + if (current_type == DBUS_TYPE_BYTE) + { + print_ay (&subiter, depth); + break; + } + + fprintf(stderr, "array [\n"); + while (current_type != DBUS_TYPE_INVALID) + { + print_iter (&subiter, literal, depth+1); + + dbus_message_iter_next (&subiter); + current_type = dbus_message_iter_get_arg_type (&subiter); + + if (current_type != DBUS_TYPE_INVALID) + fprintf(stderr, ","); + } + indent(depth); + fprintf(stderr, "]\n"); + break; + } + case DBUS_TYPE_DICT_ENTRY: + { + DBusMessageIter subiter; + + dbus_message_iter_recurse (iter, &subiter); + + fprintf(stderr, "dict entry(\n"); + print_iter (&subiter, literal, depth+1); + dbus_message_iter_next (&subiter); + print_iter (&subiter, literal, depth+1); + indent(depth); + fprintf(stderr, ")\n"); + break; + } + + case DBUS_TYPE_STRUCT: + { + int current_type; + DBusMessageIter subiter; + + dbus_message_iter_recurse (iter, &subiter); + + fprintf(stderr, "struct {\n"); + while ((current_type = dbus_message_iter_get_arg_type (&subiter)) != DBUS_TYPE_INVALID) + { + print_iter (&subiter, literal, depth+1); + dbus_message_iter_next (&subiter); + if (dbus_message_iter_get_arg_type (&subiter) != DBUS_TYPE_INVALID) + fprintf(stderr, ","); + } + indent(depth); + fprintf(stderr, "}\n"); + break; + } + + default: + fprintf(stderr, " (too dumb to decipher arg type '%c')\n", type); + break; + } + } while (dbus_message_iter_next (iter)); +} + +void +print_message (DBusMessage *message, dbus_bool_t literal) +{ + DBusMessageIter iter; + const char *sender; + const char *destination; + int message_type; + + message_type = dbus_message_get_type (message); + sender = dbus_message_get_sender (message); + destination = dbus_message_get_destination (message); + + if (!literal) + { + fprintf(stderr, "%s sender=%s -> dest=%s", + type_to_name (message_type), + sender ? sender : "(null sender)", + destination ? destination : "(null destination)"); + + switch (message_type) + { + case DBUS_MESSAGE_TYPE_METHOD_CALL: + case DBUS_MESSAGE_TYPE_SIGNAL: + fprintf(stderr, " serial=%u path=%s; interface=%s; member=%s\n", + dbus_message_get_serial (message), + dbus_message_get_path (message), + dbus_message_get_interface (message), + dbus_message_get_member (message)); + break; + + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + fprintf(stderr, " reply_serial=%u\n", + dbus_message_get_reply_serial (message)); + break; + + case DBUS_MESSAGE_TYPE_ERROR: + fprintf(stderr, " error_name=%s reply_serial=%u\n", + dbus_message_get_error_name (message), + dbus_message_get_reply_serial (message)); + break; + + default: + fprintf(stderr, "\n"); + break; + } + } + + dbus_message_iter_init (message, &iter); + print_iter (&iter, literal, 1); + fflush (stdout); + +} + diff --git a/gb.dbus/src/dbus_print_message.h b/gb.dbus/src/dbus_print_message.h new file mode 100644 index 00000000..66df93f7 --- /dev/null +++ b/gb.dbus/src/dbus_print_message.h @@ -0,0 +1,55 @@ +/*************************************************************************** + + dbus_print_message.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-print-message.h Utility function to print out a message + * + * Copyright (C) 2003 Philip Blundell + * Copyright (C) 2003 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA + * + */ +#ifndef DBUS_PRINT_MESSAGE_H +#define DBUS_PRINT_MESSAGE_H + +#include +#include +#include + +void print_message (DBusMessage *message, dbus_bool_t literal); + +#endif /* DBUS_PRINT_MESSAGE_H */ diff --git a/gb.dbus/src/gb.dbus.component b/gb.dbus/src/gb.dbus.component new file mode 100644 index 00000000..020f90da --- /dev/null +++ b/gb.dbus/src/gb.dbus.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.dbus +Author=Benoît Minisini +State=NotFinished diff --git a/gb.dbus/src/gb.dbus/.directory b/gb.dbus/src/gb.dbus/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/gb.dbus/src/gb.dbus/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/gb.dbus/src/gb.dbus/.icon.png b/gb.dbus/src/gb.dbus/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a9254f949f86671b58a744a6a93623576acc5217 GIT binary patch literal 10569 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 "." Then + sCar = "." + Else + sCar = "" + Endif + Endif + sRes &= sCar + Next + + If Right(sRes) = "." Then sRes = Left(sRes, -1) + + Return sRes + +End + + +Static Public Sub _RegisterApplication(hConnection As DBusConnection) As String + + Dim sName As String + + If IsNull(hConnection.Tag) Then + sName = Name_Read() + If hConnection._RequestName(sName, True) Then + If $bUnique Then + Error.Raise("Unique application is already registered") + Else + Error.Raise("Application is already registered") + Endif + Endif + hConnection.Tag = sName + Endif + Inc $iRegisterCount + Return hConnection.Tag + +End + +Static Public Sub _UnregisterApplication(hConnection As DBusConnection) + + Dec $iRegisterCount + If $iRegisterCount > 0 Then Return + If Not hConnection.Tag Then Return + hConnection._ReleaseName(hConnection.Tag) + hConnection.Tag = "" + +End + +Static Private Function Name_Read() As String + + Dim sName As String + + If $sAppName Then Return $sAppName + + sName = "org.gambas." & _Normalize(Application.Name, True, "-_") + If Not $bUnique Then sName &= "-" & CStr(Application.Handle) + Return sName + +End + +Static Private Sub Name_Write(Value As String) + + $sAppName = _Normalize(Value, True, "-_") + +End + +Static Public Sub Register((Object) As DBusObject, Path As String, Optional Interfaces As String[]) + + Object._Register(DBus.Session, Path, Interfaces) + +End + +Static Public Sub Unregister((Object) As DBusObject) + + Object._Unregister(DBus.Session) + +End + +Static Public Sub IsRegistered((Object) As DBusObject) As Boolean + + Return {Object}._Path + +End + + +Static Public Sub Raise((Object) As DBusObject, Signal As String, Optional Arguments As Variant[]) + + Object._Raise(DBus.Session, Signal, Arguments) + +End + +Static Private Function Null_Read() As Object + + Return _DBusNull + +End + +Static Public Sub _HasSystemTray() As Boolean + + Try Return DBus["org.kde.StatusNotifierWatcher"]["/StatusNotifierWatcher", "org.kde.StatusNotifierWatcher"].IsStatusNotifierHostRegistered + +End + +Static Private Function Unique_Read() As Boolean + + Return $bUnique + +End + +Static Private Sub Unique_Write(Value As Boolean) + + $bUnique = Value + +End diff --git a/gb.dbus/src/gb.dbus/.src/DBusApplication.class b/gb.dbus/src/gb.dbus/.src/DBusApplication.class new file mode 100644 index 00000000..99efe9de --- /dev/null +++ b/gb.dbus/src/gb.dbus/.src/DBusApplication.class @@ -0,0 +1,135 @@ +' Gambas class file + +Export + +Private $hConn As DBusConnection +Private $sName As String +Private $cCache As New Collection +Private $sKey As String +Private $iIndex As Integer +Private $iRequest As Integer +'Private $hObject As DBusProxy + +Property Read Connection As DBusConnection +Property Read Name As String +Property Read Index As Integer + +Public Sub _new(Connection As DBusConnection, ApplicationName As String) + + $hConn = Connection + $sName = ApplicationName + If $hConn = _DBus.System Then + $sKey = "system://" & $sName + Else + $sKey = "session://" & $sName + Endif + + DBus._ApplicationCache[$sKey] = Me + $iIndex = DBus._ApplicationCache.Count + DBus._ApplicationCache[$iIndex] = Me + +End + +Public Sub _get(ObjectPath As String, Optional Interface As String) As DBusProxy + + Dim hObject As DBusProxy + Dim sKey As String + + sKey = ObjectPath + If Interface Then sKey &= "#" & Interface + + hObject = $cCache[sKey] + If Not hObject Then + hObject = New DBusProxy(Me, ObjectPath, Interface) + $cCache[sKey] = hObject + Endif + Return hObject + +End + +Private Function Connection_Read() As DBusConnection + + Return $hConn + +End + +Public Sub _Introspect(sObjectPath As String) As String + + Try Return $hConn._Introspect($sName, sObjectPath) + +End + +Private Function Name_Read() As String + + Return $sName + +End + +Private Function Index_Read() As Integer + + Return $iIndex + +End + +' Public Sub _unknown(...) As Variant +' +' Dim aArg As Variant[] +' 'Dim iInd As Integer +' Dim hObject As DBusProxy = $hObject +' +' If Param.Count Then +' ' aArg = New Variant[Param.Count] +' ' For iInd = 0 To Param.Max +' ' aArg[iInd] = Param[iInd] +' ' Next +' aArg = Param.All +' Endif +' +' $hObject = Null +' Return hObject._Invoke(Param.Name, aArg) +' +' End +' +' Public Sub _property() As Boolean +' +' $hObject = _get(Replace($sName, "/", ".")) +' Return $hObject._property() +' +' End + + +Private Sub Request() + + Inc $iRequest + If $iRequest = 1 Then DBus.Session._RequestName($sName, True) + +End + +Private Sub Release() + + Dec $iRequest + If $iRequest > 0 Then Return + DBus.Session._ReleaseName($sName) + +End + +Public Sub Register((Object) As DBusObject, Path As String, Optional Interfaces As String[]) + + Request() + Object._Register($hConn, $sName &/ Path, Interfaces) + +End + +Public Sub Unregister((Object) As DBusObject) + + Object._Unregister($hConn) + Release() + +End + +Public Sub Raise((Object) As DBusObject, Signal As String, Optional Arguments As Variant[]) + + ' Apparently you can't define the sender of the signal! + Object._Raise($hConn, Signal, Arguments) + +End diff --git a/gb.dbus/src/gb.dbus/.src/DBusObject.class b/gb.dbus/src/gb.dbus/.src/DBusObject.class new file mode 100644 index 00000000..ead0cc57 --- /dev/null +++ b/gb.dbus/src/gb.dbus/.src/DBusObject.class @@ -0,0 +1,702 @@ +' Gambas class file + +Export + +Static Public _ObjectCache As New Collection + +Static Private $cType As Collection = [ + "b": "b", + "c": "y", + "h": "n", + "i": "i", + "l": "x", + "p": "x", + "g": "d", + "f": "d", + "d": "d", + "s": "s", + "v": "v", + "DBusObject": "o", + "Collection": "a{sv}", + "Boolean[]": "ab", + "Byte[]": "ay", + "Short[]": "an", + "Integer[]": "ai", + "Long[]": "ax", + "Pointer[]": "ax", + "Single[]": "ad", + "Float[]": "ad", + "Date[]": "ad", + "String[]": "as", + "Variant[]": "av"] + +Static Private $sName As String + +Property Read _Path As String + +Private $sFullPath As String +Private $sPath As String +Private $sInterface As String +Private $aInterface As String[] +Private $aChildren As String[] +Private $hObserver As DBusObserver +Private $iRegister As Integer +Private $bRegisterParent As Boolean + +Static Private Sub IsDBusValues(sType As String) As Boolean + + Dim hClass As Class + + If sType = "DBusValues" Then Return True + + Try hClass = Classes[sType] + If Not hClass Then Return + If Not hClass.Parent Then Return + + Return IsDBusValues(hClass.Parent.Name) + +End + +Static Private Sub IsDBusVariant(sType As String) As Boolean + + Dim hClass As Class + + If sType = "DBusVariant" Then Return True + + Try hClass = Classes[sType] + If Not hClass Then Return + If Not hClass.Parent Then Return + + Return IsDBusVariant(hClass.Parent.Name) + +End + + +Static Private Sub ConvType(sType As String) As String + + Dim sSign As String + Dim hClass As Class + + sSign = $cType[sType] + + If Not sSign And If IsDBusVariant(sType) Then + hClass = Classes[sType] + sSign = hClass["Signature"].Value + Endif + + Return sSign + +End + +Public Sub _AddChild(sChild As String) + + If Not $aChildren Then $aChildren = New String[] + $aChildren.Add(sChild) + +End + +Public Sub _RemoveChild(sChild As String) + + Dim iPos As Integer + + If Not $aChildren Then Return + iPos = $aChildren.Find(sChild) + If iPos >= 0 Then + $aChildren.Remove(iPos) + If $aChildren.Count = 0 Then $aChildren = Null + Endif + +End + + +Public Sub _Register(hConnection As DBusConnection, sFullPath As String, Optional aInterface As String[]) + + Dim hParent As DBusObject + Dim sParent As String + Dim iPos As Integer + Dim sService As String + Dim sPath As String + + If $sFullPath Then + If $sFullPath <> sFullPath Then Error.Raise("Object already registered") + Inc $iRegister + Return + Endif + + Inc $iRegister + + If Not $sName Then + $sName = DBus._RegisterApplication(hConnection) + If Not DBus.Unique Then $sName = Left($sName, - Len(CStr(Application.Id)) - 1) + Endif + + iPos = InStr(sFullPath, "/") + If iPos = 0 Then Error.Raise("Bad object path") + sService = Left(sFullPath, iPos - 1) + sPath = Mid$(sFullPath, iPos) + + If _ObjectCache.Exist(sFullPath) Then Error.Raise("Path already used") + + $sFullPath = sFullPath + $sPath = sPath + 'Debug Me;; $sFullPath; " | "; $sPath + + $sInterface = DBus._Normalize($sName & "." & Object.Class(Me).Name) + If aInterface Then $aInterface = aInterface.Copy() + + _ObjectCache[sFullPath] = Me + + If sPath <> "/" Then + + sParent = File.Dir(sPath) + hParent = _ObjectCache[sService &/ sParent] + If Not hParent Then + $bRegisterParent = True + hParent = New DBusObject + hParent._Register(hConnection, sService &/ sParent) + Endif + + hParent._AddChild(File.Name(sPath)) + + Endif + + $hObserver = New DBusObserver(hConnection, DBus.Method, sPath, "", "", sService) As "DBusObserver" + +End + +Public Sub _Unregister(hConnection As DBusConnection) + + Dim sChild As String + Dim hChild As DBusObject + Dim sParent As String + Dim hParent As DBusObject + Dim sService As String + Dim iPos As Integer + Dim sPath As String + + Dec $iRegister + If $iRegister > 0 Then Return + + iPos = InStr($sFullPath, "/") + sService = Left($sFullPath, iPos - 1) + sPath = Mid$($sFullPath, iPos) + + If $bRegisterParent Then + + $bRegisterParent = False + sParent = File.Dir(sPath) + hParent = _ObjectCache[sService &/ sParent] + If hParent Then + hParent._Unregister(hConnection) + Return + Endif + + Endif + + If $aChildren Then + For Each sChild In $aChildren.Copy() + hChild = _ObjectCache[$sFullPath &/ sChild] + hChild._Unregister(hConnection) + Next + $aChildren = Null + Endif + + hParent = _ObjectCache[File.Dir($sFullPath)] + If hParent Then hParent._RemoveChild(File.Name($sFullPath)) + + $hObserver = Null + _ObjectCache[$sFullPath] = Null + $sFullPath = "" + $sPath = "" + + If _ObjectCache.Count = 0 Then + DBus._UnregisterApplication(hConnection) + $sName = "" + Endif + +End + +Static Private Sub ToDBusSignature(sSign As String) As String[] + + Dim aResult As New String[] + Dim sOne As String + Dim iPos As Integer + Dim sClass As String + + If Not sSign Then + If DBus.Debug Then Error "gb.dbus: void signature" + Return aResult + Endif + + While sSign + + If IsLCase(Left(sSign)) Then + + sOne = ConvType(Left(sSign)) + sSign = Mid$(sSign, 2) + + Else + + iPos = InStr(sSign, ";") + If iPos Then + sClass = Left(sSign, iPos - 1) + sSign = Mid$(sSign, iPos + 1) + Else + sClass = sSign + sSign = "" + Endif + + sOne = ConvType(sClass) + + Endif + + If Not sOne Then + If DBus.Debug Then Error "gb.dbus: cannot convert signature for "; sClass + Return + Endif + + aResult.Add(sOne) + + Wend + + Return aResult + +End + + + + +Public Sub _Introspect() As String + + Dim sRes As String + Dim sChild As String + Dim sSymbol As String + Dim hClass As Class + Dim bHasProperty As Boolean + Dim bHasMethod As Boolean + Dim sType As String + Dim sArg As String + Dim iInd As Integer + Dim cDescInterface As New Collection + Dim sDesc As String + Dim sInterface As String + Dim sPrefix As String + Dim aSign As String[] + Dim sKind As String + Dim sSign As String + Dim bReadOnly As Boolean + Dim bMulti As Boolean + Dim aType As String[] + Dim I As Integer + + sRes &= "\n" + sRes &= "\n" + + sRes &= " \n" + sRes &= " \n" + sRes &= " \n" + sRes &= " \n" + sRes &= " \n" + + + hClass = Object.Class(Me) + + For Each sSymbol In hClass.Symbols + With hClass[sSymbol] + If Left(sSymbol) = "_" Then Continue + If .Static Then Continue + If .Kind = Class.Property Then + bHasProperty = True + Else If .Kind = Class.Method Then + bHasMethod = True + Endif + End With + Next + + sRes &= " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + + If bHasProperty Then + sRes &= " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + Endif + + For Each sSymbol In hClass.Symbols + + With hClass[sSymbol] + + If .Kind = Class.Event Then + sSymbol = Mid$(sSymbol, 2) + Else + If .Static Then Continue + Endif + + sPrefix = "" + If $aInterface Then + For Each sInterface In $aInterface + sPrefix = LCase(Replace(sInterface, ".", "_") & "_") + If LCase(sSymbol) Begins sPrefix And RInStr(sSymbol, "_") <= Len(sPrefix) Then Break + sPrefix = "" + Next + Endif + + sSymbol = Mid$(sSymbol, Len(sPrefix) + 1) + + If InStr(sSymbol, "_") Then Continue + + sDesc = "" + sType = "" + sKind = .Kind + sSign = .Signature + bReadOnly = .ReadOnly + + If .Type Then + sType = .Type + bMulti = IsDBusValues(sType) + sType = ConvType(sType) + If Not sType Then Continue + Endif + + End With + + Select Case sKind + + Case Class.Method, Class.Event + + aSign = ToDBusSignature(sSign) + If Not aSign Then Continue + + sArg = "" + + For iInd = 0 To aSign.Max + sArg &= " \n" + Next + + If sKind = Class.Event Then + + sDesc &= " \n" + If sArg Then sDesc &= sArg + sDesc &= " \n" + + Else + + sDesc &= " \n" + + If sArg Then sDesc &= sArg + + If sType Then + If bMulti Then + aType = DBus._SplitSignature(sType) + For I = 0 To aType.Max + sDesc &= " \n" + Next + Else + sDesc &= " \n" + Endif + Endif + + sDesc &= " \n" + + Endif + + Case Class.Property + + sDesc &= " \n" + + End Select + + If sPrefix Then + cDescInterface[sInterface] &= sDesc + Else + cDescInterface[$sInterface] &= sDesc + Endif + + Next + + For Each sDesc In cDescInterface + sRes &= " \n" & sDesc & " \n" + Next + + If $aChildren Then + For Each sChild In $aChildren.Sort() + sRes &= " \n" + Next + Endif + + sRes &= "\n" + + Return sRes + +End + +Private Sub GetProperty() + + Dim sInterface As String + Dim sProperty As String + + With $hObserver.Message + + sInterface = .Arguments[0] + sProperty = .Arguments[1] + + If $aInterface And If sInterface <> $sInterface Then + sProperty = Replace(sInterface, ".", "_") & "_" & sProperty + Else + If InStr(sProperty, "_") Then Return + Endif + + $hObserver.Reply("v", [Object.GetProperty(Me, sProperty)]) + + End With + +Catch + + If DBus.Debug Then Error "gb.dbus: org.freedesktop.Properties.Get: error: "; Error.Text + $hObserver.Error(Error.Text) + +End + +Private Sub GetAllProperties() + + Dim cVal As New Collection + Dim sWantInterface As String + Dim hClass As Class + Dim sSymbol As String + Dim sInterface As String + Dim sPrefix As String + Dim sProp As String + Dim vVal As Variant + Dim hObs As DBusObserver + Dim bComma As Boolean + + hObs = $hObserver + + With hObs.Message + + sWantInterface = .Arguments[0] + + hClass = Object.Class(Me) + + 'Debug "[";; System.Backtrace.Join(" ") + + For Each sSymbol In hClass.Symbols + + With hClass[sSymbol] + + If .Kind <> Class.Property Then Continue + If .Static Then Continue + + sPrefix = "" + If $aInterface Then + For Each sInterface In $aInterface + sPrefix = LCase(Replace(sInterface, ".", "_") & "_") + If LCase(sSymbol) Begins sPrefix And RInStr(sSymbol, "_") <= Len(sPrefix) Then Break + sPrefix = "" + Next + Endif + + sProp = Mid$(sSymbol, Len(sPrefix) + 1) + If InStr(sProp, "_") Then Continue + + If Not sPrefix Then sInterface = $sInterface + + If sInterface <> sWantInterface Then Continue + + 'Debug sSymbol; " @ "; sInterface + + vVal = Object.GetProperty(Me, sSymbol) + If IsNull(vVal) Then + If TypeOf(vVal) = gb.String Then + cVal[sProp] = DBus.Null + 'Debug sProp; ": "; "DBus.Null" + Endif + Else + cVal[sProp] = vVal + 'If TypeOf(vVal) = gb.Object Then Debug sProp; ": "; vVal + Endif + + End With + Next + + 'Debug "]" + + If DBus.Debug Then + Error "gb.dbus: GetAll("; Quote(sWantInterface); "): ["; + For Each vVal In cVal + If bComma Then + Error ", "; + Else + bComma = True + Endif + Error Quote(cVal.Key); ": "; vVal; + Next + Error "]" + Endif + + hObs.Reply("a{sv}", [cVal]) + + End With + +Catch + + If DBus.Debug Then Error "gb.dbus: error: org.freedesktop.Properties.GetAll: "; Error.Text;; Error.Backtrace.Join(" ") + hObs.Error(Error.Text) + +End + +Private Sub SetProperty() + + Dim sInterface As String + Dim sProperty As String + Dim vValue As Variant + + With $hObserver.Message + + sInterface = .Arguments[0] + sProperty = .Arguments[1] + vValue = .Arguments[2] + + If $aInterface And If sInterface <> $sInterface Then + sProperty = Replace(sInterface, ".", "_") & "_" & sProperty + Else + If InStr(sProperty, "_") Then Return + Endif + + Object.SetProperty(Me, sProperty, vValue) + + End With + +Catch + + If DBus.Debug Then Error "gb.dbus: org.freedesktop.Properties.Set: error: "; Error.Text + $hObserver.Error(Error.Text) + +End + + +Public Sub DBusObserver_Message() + + Dim vVal As Variant + Dim sMethod As String + Dim sInterface As String + Dim sType As String + Dim hObs As DBusObserver + + hObs = $hObserver + + sMethod = hObs.Message.Member + sInterface = hObs.Message.Interface + + If DBus.Debug Then Error "gb.dbus: call: ["; hObs.Message.Destination; "]";; sInterface;; sMethod + + If InStr(sMethod, "_") Then Return + + If sInterface = "org.freedesktop.DBus.Introspectable" Or If Not sInterface Then + If sMethod = "Introspect" Then + hObs.Reply("s", [_Introspect()]) + Return + Endif + Endif + + If sInterface = "org.freedesktop.DBus.Properties" Or If Not sInterface Then + If sMethod = "Get" Then + GetProperty() + Return + Else If sMethod = "Set" Then + SetProperty() + hObs.Reply() + Return + Else If sMethod = "GetAll" Then + GetAllProperties() + Return + Endif + Endif + + If $aInterface And If sInterface <> $sInterface Then + sMethod = Replace(sInterface, ".", "_") & "_" & sMethod + Endif + + sType = Object.Class(Me)[sMethod].Type + + If sType Then + vVal = Object.Call(Me, sMethod, hObs.Message.Arguments) + If TypeOf(vVal) = gb.Object And If vVal Is DBusValues Then + sType = vVal.Signature + hObs.Reply(sType, vVal.Value) + Else If TypeOf(vVal) = gb.Object And If vVal Is DBusVariant Then + sType = vVal.Signature + hObs.Reply(sType, [vVal.Value]) + Else + sType = ConvType(sType) + If Not sType Then sType = "v" + hObs.Reply(sType, [vVal]) + Endif + Else + Object.Call(Me, sMethod, hObs.Message.Arguments) + hObs.Reply() + Endif + +Catch + + If DBus.Debug Then Error "gb.dbus: "; sInterface; "."; sMethod; ": error: "; Error.Text;; Error.Backtrace.Join(" ") + hObs.Error(Error.Text) + +End + + +Public Sub _Raise(hConnection As DBusConnection, sSignal As String, aArg As Object) + + Dim hClass As Class + Dim aSign As String[] + Dim iPos As Integer + Dim sInterface As String + Dim sSymbol As String + + If Not $sFullPath Then Error.Raise("DBus object is not registered") + + hClass = Object.Class(Me) + sSymbol = ":" & Replace(sSignal, ".", "_") + If Not hClass.Exist(sSymbol) Then Error.Raise("Unknown signal") + + With hClass[sSymbol] + aSign = ToDBusSignature(.Signature) + If Not aSign Then Error.Raise("Unknown signal") + End With + + iPos = RInStr(sSignal, ".") + If iPos Then + sInterface = Left(sSignal, iPos - 1) + sSignal = Mid$(sSignal, iPos + 1) + Else + sInterface = $sInterface + Endif + + iPos = InStr($sFullPath, "/") + hConnection._SendSignal(Mid$($sFullPath, iPos), sInterface, sSignal, aSign.Join(""), aArg) + +End + + +Private Function _Path_Read() As String + + Return $sPath + +End diff --git a/gb.dbus/src/gb.dbus/.src/DBusProxy.class b/gb.dbus/src/gb.dbus/.src/DBusProxy.class new file mode 100644 index 00000000..37cb7237 --- /dev/null +++ b/gb.dbus/src/gb.dbus/.src/DBusProxy.class @@ -0,0 +1,309 @@ +' Gambas class file + +Export + +Property Read Children As String[] +Property Read _Introspection As String + +Private $hAppIndex As Integer +Private $sObjectPath As String +Private $sInterface As String +Private $sIntrospection As String +Private $cSignature As New Collection + +Public Sub _new((Application) As DBusApplication, ObjectPath As String, Optional Interface As String) + + Dim iPos, iPos2 As Integer + + $hAppIndex = Application.Index + $sObjectPath = ObjectPath + $sInterface = Interface + + $sIntrospection = Application._Introspect(ObjectPath) + If Not $sIntrospection Then Error.Raise("Unknown object") + + If Interface Then + iPos = InStr($sIntrospection, "") + If iPos = 0 Then Error.Raise("Unknown interface") + iPos2 = InStr($sIntrospection, "", iPos) + If iPos2 = 0 Then Error.Raise("Introspection error") + $sIntrospection = Mid$($sIntrospection, iPos, iPos2 - iPos) + Endif + + 'Debug $sIntrospection + +End + +Private Sub GetSignature(sSymbol As String) As String + + Dim sFind As String + Dim iPos, iStart, iEnd As Integer + Dim sSignIn, sSignOut As String + Dim sDir, sType As String + Dim sAccess As String + Dim iPos2 As Integer + + sSignIn = $cSignature[sSymbol] + If sSignIn Then Return sSignIn + + iPos = InStr($sIntrospection, " name=\"" & sSymbol & "\"", 1, gb.IgnoreCase) + If iPos = 0 Then Error.Raise("Unknown symbol") + + iStart = RInStr($sIntrospection, "<", iPos) + If iStart = 0 Then Error.Raise("Malformed introspection") + iEnd = InStr($sIntrospection, ">", iStart) + If iEnd = 0 Then Error.Raise("Malformed introspection") + + If Mid$($sIntrospection, iStart) Begins " "/>" Then + + iEnd = InStr($sIntrospection, "", iStart) + + Do + iPos = InStr($sIntrospection, " iEnd Then Break + iPos2 = InStr($sIntrospection, ">", iPos + 1) + If iPos2 = 0 Or If iPos2 > iEnd Then Break + + sDir = "in" + Try sDir = Scan(Mid$($sIntrospection, iPos, iPos2 - iPos), "*direction=\"*\"*")[1] + Try sType = Scan(Mid$($sIntrospection, iPos, iPos2 - iPos), "*type=\"*\"*")[1] + If Error Then Break + + If sDir = "in" Then + sSignIn &= sType + Else + sSignOut &= sType + Endif + + iPos = iPos2 + + Loop + + Endif + + sType = "M:" & sSignIn & ":" & sSignOut + + Else + + Error.Raise("Unknown symbol") + + Endif + + $cSignature[sSymbol] = sType + Return sType + + sFind = "", iPos + 1) + Try sType = Scan(Mid$($sIntrospection, iPos, iPos2 - iPos), "*type=\"*\"*")[1] + If Error Then Error.Raise("Unknown property") + + Try sAccess = Scan(Mid$($sIntrospection, iPos, iPos2 - iPos), "*access=\"*\"*")[1] + If Error Then Error.Raise("Malformed property") + + If sAccess = "read" Then + sType &= ":r" + Else If sAccess = "write" Then + sType &= ":w" + Else If sAccess = "readwrite" Then + sType &= ":rw" + Else + Error.Raise("Malformed property") + Endif + + sType = "P:" & sType + $cSignature[sSymbol] = sType + Return sType + +METHOD: + + sFind = " "/>" Then + + iEnd = InStr($sIntrospection, "", iPos) + + Do + iPos = InStr($sIntrospection, " iEnd Then Break + iPos2 = InStr($sIntrospection, ">", iPos + 1) + If iPos2 = 0 Or If iPos2 > iEnd Then Break + + sDir = "in" + Try sDir = Scan(Mid$($sIntrospection, iPos, iPos2 - iPos), "*direction=\"*\"*")[1] + Try sType = Scan(Mid$($sIntrospection, iPos, iPos2 - iPos), "*type=\"*\"*")[1] + If Error Then Break + + If sDir = "in" Then + sSignIn &= sType + Else + sSignOut &= sType + Endif + + iPos = iPos2 + + Loop + + Endif + + sSignIn = "M:" & sSignIn & ":" & sSignOut + $cSignature[sSymbol] = sSignIn + Return sSignIn + +End + +Public Sub _Invoke(sName As String, aArg As Variant[]) As Variant + + Dim aSign As String[] + Dim hApp As DBusApplication + Dim vVal As Variant + Dim sSign As String + Dim bProperty As Boolean + Dim hVariant As DBusVariant + + hApp = DBus._ApplicationCache[$hAppIndex] + + sSign = GetSignature(sName) + bProperty = Left(sSign, 2) = "P:" + sSign = Mid$(sSign, 3) + + aSign = Split(sSign, ":") + + If bProperty Then 'Error.Raise("Unknown property") + + If IsNull(aArg) Or If aArg.Count = 0 Then + 'Debug "read property " & sName + If InStr(aSign[1], "r") = 0 Then Error.Raise("Write-only property") + aArg = New Variant[2] + aArg[0] = $sInterface + aArg[1] = sName + Return hApp.Connection._CallMethod(hApp.Name, $sObjectPath, "org.freedesktop.DBus.Properties", "Get", "ss", "v", aArg) + Else + 'Debug "write property " & sName + If InStr(aSign[1], "w") = 0 Then Error.Raise("Read-only property") + vVal = aArg[0] + aArg = New Variant[3] + aArg[0] = $sInterface + aArg[1] = sName + hVariant = New DBusVariant + hVariant.Value = vVal + aArg[2] = hVariant + hApp.Connection._CallMethod(hApp.Name, $sObjectPath, "org.freedesktop.DBus.Properties", "Set", "ssv", "", aArg) + Endif + + Else + + 'Print GetSignature(Param.Name) + Return hApp.Connection._CallMethod(hApp.Name, $sObjectPath, $sInterface, sName, aSign[0], aSign[1], aArg) + + Endif + +End + +Public Sub _unknown(...) As Variant + + Dim aArg As Variant[] + + If Param.Count Then aArg = Param.All + Return _Invoke(Param.Name, aArg) + +End + +Public Sub _property() As Boolean + + 'Return True + 'Debug Param.Name;; GetSignature(Param.Name) + Return Left(GetSignature(Param.Name)) = "P" + +End + + +' We do not use the cached introspection, because new objects may have appeared. +' But new interfaces or new methods are unlikely. + +Private Function Children_Read() As String[] + + Dim sIntr As String + Dim aChildren As New String[] + Dim iPos, iPos2, iLevel As Integer + Dim sNode, sChild As String + Dim hApp As DBusApplication + + hApp = DBus._ApplicationCache[$hAppIndex] + sIntr = hApp._Introspect($sObjectPath) + + Do + iPos2 = iPos + iPos = InStr(sIntr, "", iPos2 + 1) + If iPos2 = 0 Then iPos2 = Len(sIntr) + 1 + + If iPos > Len(sIntr) And If iPos2 > Len(sIntr) Then Return aChildren + + If iPos < iPos2 Then + + Inc iLevel + If iLevel <> 2 Then Continue + + iPos2 = InStr(sIntr, ">", iPos + 1) + If iPos2 = 0 Then Continue + + sNode = Mid$(sIntr, iPos, iPos2 - iPos + 1) + Try sChild = Scan(sNode, "*name=\"*\"*")[1] + If sChild Then aChildren.Add(sChild) + + If Right(sNode, 2) = "/>" Then Dec iLevel + + Else + + iPos = iPos2 + Dec iLevel + + Endif + + Loop + +End + +Private Function _Introspection_Read() As String + + Return $sIntrospection + +End diff --git a/gb.dbus/src/gb.dbus/.src/DBusSignal.class b/gb.dbus/src/gb.dbus/.src/DBusSignal.class new file mode 100644 index 00000000..5fa4024e --- /dev/null +++ b/gb.dbus/src/gb.dbus/.src/DBusSignal.class @@ -0,0 +1,33 @@ +' Gambas class file + +Export + +Event Signal(Signal As String, Arguments As Variant[]) + +Property Enabled As Boolean + +Private $hObserver As DBusObserver + +Public Sub _new(Connection As DBusConnection, Interface As String, Optional Every As Boolean) + + $hObserver = New DBusObserver(Connection, DBus.Signal, "", "", Interface, If(Every, "*", "")) As "DBusObserver" + +End + +Public Sub DBusObserver_Message() + + Raise Signal($hObserver.Message.Member, $hObserver.Message.Arguments) + +End + +Private Function Enabled_Read() As Boolean + + Return $hObserver.Enabled + +End + +Private Sub Enabled_Write(Value As Boolean) + + $hObserver.Enabled = Value + +End diff --git a/gb.dbus/src/gb.dbus/.src/DBusValues.class b/gb.dbus/src/gb.dbus/.src/DBusValues.class new file mode 100644 index 00000000..783545dd --- /dev/null +++ b/gb.dbus/src/gb.dbus/.src/DBusValues.class @@ -0,0 +1,4 @@ +' Gambas class file + +Export +Inherits DBusVariant \ No newline at end of file diff --git a/gb.dbus/src/gb.dbus/.src/MMain.module b/gb.dbus/src/gb.dbus/.src/MMain.module new file mode 100644 index 00000000..c8163abd --- /dev/null +++ b/gb.dbus/src/gb.dbus/.src/MMain.module @@ -0,0 +1,154 @@ +' Gambas module file + +Public Sub Main2() + + Dim aVal As Variant[] + Dim hSignal As DBusSignal + + 'DBus["org.kde.kmail"]["/kmail/kmail_mainwindow_1"].geometry = [0, 24, 1024, 768] '.Get("", "rect") + 'aRect = DBus["org.kde.kmail"]["/kmail/kmail_mainwindow_1", "com.trolltech.Qt.QWidget"].geometry + 'Get("com.trolltech.Qt.QWidget", "rect") + + 'DBus.Session._Introspect("org.kde.kmail", "/kmail/kmail_mainwindow_1") + ' aRect = DBus.Session.CallMethod("org.kde.kmail", "/kmail/kmail_mainwindow_1", "", "Get", "ss", "(iiii)", ["com.trolltech.Qt.QWidget", "rect"]) + ' For iInd = 0 To aRect.Max + ' Error aRect[iInd];; + ' Next + ' Error + + 'Print DBus["org.kde.kmail"]["/kmail/kmail_mainwindow_1"].geometry '"turlutu" + 'Debug aRect + 'Print DBus["system://org.freedesktop.Hal"]["/org/freedesktop/Hal/Manager", "org.freedesktop.Hal.Manager"].GetAllDevices().Join("\n") + 'Print DBus["org.kde.krunner"]["/"].Children.Join("\n") + 'Print DBus["org.kde.krunner"]["/"]._Introspection + + 'Print DBus.Session._Name + 'Dim hTest As CTest = CTest + + ' + ' Debug DBus.Session.Applications.Join("\n") + ' + DBus.Debug = True + DBus.Session.Register(CTest, "/Test", ["org.mpris.MediaPlayer2", "org.mpris.MediaPlayer2.Player"]) + + ' Wait 60 + + 'DBus.Name = "org.mpris.MediaPlayer2.xt7" + 'DBus.Session.Register(CTest2, "/org/mpris/MediaPlayer2", ["org.mpris.MediaPlayer2", "org.mrpris.MediaPlayer2.Player"]) + + 'Wait 10 + ' + + 'aVal = DBus["org.freedesktop.Notifications"]["/org/freedesktop/Notifications"].GetServerInformation() + 'Print aVal[0];; aVal[1];; aVal[2] + 'Print DBus["system://org.freedesktop.UDisks"]["/"].Children + + 'hSignal = New DBusSignal(DBus.Session, "org.freedesktop.DBus") As "MySignal" + ' hSignal = New DBusSignal(DBus.Session, "", True) As "MySignal" + 'Wait 30 + + 'DBus.Session.Unregister(CTest) + + ' Dim o As Object + ' Dim s As String + 'Dim cInts As Collection = ["image_path": "/usr/share/icons/gnome/48x48/actions/add.png"] + + 'DBus["org.freedesktop.Notifications"]["/org/freedesktop/Notifications"].Notify("MyApp", 0, "", "PoissonTChat", "Fabien: Chu la ! ici", ["test"], cInts, 1000) + + 'Print "BUG 1" + 'Print "Try DBus[\"system://org.freedesktop.UDisks\"][\"/org/freedesktop/UDisks\"].Uninhibit(\"fake_cookie\")" + 's = "AuxArmes!" + ' Error "---------------------------" + ' Try Print DBus["system://org.freedesktop.UDisks"]["/org/freedesktop/UDisks"].Uninhibit("AuxArmes!") + ' Error "---------------------------" + ' Print Error.Text + 'Try DBus["system://org.freedesktop.UDisks"]["/org/freedesktop/UDisks"].Uninhibit("fake_cookie") + 'Return + + ' If Error Then + ' Print "NO BUG. Error message:";; Error.Text + ' Else + ' Print "BUG! No error message" + ' End If + ' + ' Print "BUG 2" + ' Print "Try DBus[\"system://org.freedesktop.UDisks\"][\"/org/freedesktop/UDisks\"].FindDeviceByDeviceFile(\"/fake_device\")" + ' Try o = DBus["system://org.freedesktop.UDisks"]["/org/freedesktop/UDisks"].FindDeviceByDeviceFile("/fake_device") + ' If Error Then + ' If Error.Text = "Unable to call method" Then + ' Print "BUG! No detailed error message: ";; Error.Text + ' Else + ' Print "NO BUG. Error message is detailed:";; Error.Text + ' End If + ' Else + ' Print "No error. Strange" + ' End If + +End + +Public Sub MySignal_Signal(Method As String, Arguments As Variant[]) + + Debug Method + +End + +' Public Sub Main() +' +' Dim vVal As Variant +' +' Print DBus._HasSystemTray() +' +' 'Print DBus.Null +' 'Print DBus.Null +' +' 'DBus.Session.Register(hObject, "/StatusNotifierItem", ["org.freedesktop.StatusNotifierItem"]) +' 'DBus.Session._RequestName("org.freedesktop.StatusNotifierItem-" & CStr(Application.Handle) & "-1", True) +' +' ' Wait 5 +' ' +' ' DBus.Session.Raise(hObject, "org.freedesktop.StatusNotifierItem.IconChanged") +' ' DBus.Session.Raise(hObject, "org.freedesktop.StatusNotifierItem.NewStatus", ["Chuck!"]) +' ' +' ' Wait 30 +' ' +' ' DBus.Session.Unregister(hObject) +' +' End + +' Public Sub Main() +' +' DBus["org.kde.StatusNotifierItem-3527-1"]["/StatusNotifierItem", "org.kde.StatusNotifierItem"].ContextMenu(0, 0) +' +' End + +Public Sub Main3() + + DBus.Debug = True + DBus.Name = "org.mpris.MediaPlayer2.xt7" + DBus.Session.Register(mpris, "/org/mpris/MediaPlayer2", ["org.mpris.MediaPlayer2", "org.mpris.MediaPlayer2.Player", "org.mpris.MediaPlayer2.TrackList", "org.mpris.MediaPlayer2.Playlists", "org.freedesktop.Dbus.Properties", "org.freedesktop.Dbus.Introspectable"]) + +End + +Public Sub Main1() + + Dim hDBusProxy As DBusProxy + + hDBusProxy = DBus["org.gambas.test3"]["/export1"] + hDBusProxy.Trigger1 = 44 + Print hDBusProxy.Trigger1 + +End + +Public Sub Main() + + Dim hDBusApplication As DBusApplication + Dim hDBusProxy As DBusProxy + Dim iState As Integer + + hDBusApplication = New DBusApplication(DBus.System, "org.freedesktop.NetworkManager") + hDBusProxy = New DBusProxy(hDBusApplication, "/org/freedesktop/NetworkManager", "org.freedesktop.NetworkManager") + Try Print hDBusProxy.Version + Print hDBusProxy.state() + Print hDBusProxy.State() 'Should be invalid + +End diff --git a/gb.dbus/src/gb.dbus/.src/MyObject.class b/gb.dbus/src/gb.dbus/.src/MyObject.class new file mode 100644 index 00000000..aaf7107e --- /dev/null +++ b/gb.dbus/src/gb.dbus/.src/MyObject.class @@ -0,0 +1,32 @@ +' Gambas class file + +Inherits DBusObject + +Event org_freedesktop_StatusNotifierItem_IconChanged +Event org_freedesktop_StatusNotifierItem_NewStatus(sStatus As String) + +Property Read org_freedesktop_StatusNotifierItem_Category As String +Property Read org_freedesktop_StatusNotifierItem_IconPixmap As MyValue + +Public Sub org_freedesktop_StatusNotifierItem_Activate(X As Integer, Y As Integer) + + + +End + + +Private Function org_freedesktop_StatusNotifierItem_Category_Read() As String + + + +End + +Private Function org_freedesktop_StatusNotifierItem_IconPixmap_Read() As MyValue + + + +End + +'Private Function org_freedesktop_StatusNotifierItem_IconPixmap_Read() As Image +' +'End diff --git a/gb.dbus/src/gb.dbus/.src/MyValue.class b/gb.dbus/src/gb.dbus/.src/MyValue.class new file mode 100644 index 00000000..5cb8b409 --- /dev/null +++ b/gb.dbus/src/gb.dbus/.src/MyValue.class @@ -0,0 +1,5 @@ +' Gambas class file + +Inherits DBusVariant + +Public Const Signature As String = "a(iiay)" \ No newline at end of file diff --git a/gb.dbus/src/gb.dbus/.src/_DBusNull.class b/gb.dbus/src/gb.dbus/.src/_DBusNull.class new file mode 100644 index 00000000..8a161ea0 --- /dev/null +++ b/gb.dbus/src/gb.dbus/.src/_DBusNull.class @@ -0,0 +1,5 @@ +' Gambas class file + +Export +Create Static + diff --git a/gb.dbus/src/gb.dbus/.src/mpris.class b/gb.dbus/src/gb.dbus/.src/mpris.class new file mode 100644 index 00000000..32ea45fa --- /dev/null +++ b/gb.dbus/src/gb.dbus/.src/mpris.class @@ -0,0 +1,205 @@ +' Gambas class file + +' + +Inherits DBusObject + +Create Static + + +''org.freedesktop.Dbus.Introspectable------------------------------------------------------------------------------------------------------------- +' +' Public Function org_freedesktop_Dbus_Introspectable_Introspect() As String +' Return "NULL" +' End +' +' ''org.freedesktop.Dbus.Properties----------------------------------------------------------------------------------------------------------------- +' Public Function org_freedesktop_Dbus_Properties_Get(Myinterface As String, MyProperty As String) As String +' Return "NULL" +' End +' Public Function org_freedesktop_Dbus_Properties_Set(Myinterface As String, MyProperty As String) As String +' Return "NULL" +' End +' Public Function org_freedesktop_Dbus_Properties_GetAll(Myinterface As String, MyProperty As String) As String[] +' Return "NULL" +' End + + +''ORG.MPRIS.MEDIAPLAYER2----------------------------------------------------------------------------------------------------------------- +Property Read org_mpris_MediaPlayer2_CanQuit As Boolean +Property Read org_mpris_MediaPlayer2_CanRaise As Boolean +Property Read org_mpris_MediaPlayer2_HasTrackList As Boolean +Property Read org_mpris_MediaPlayer2_Identity As String +Property Read org_mpris_MediaPlayer2_DesktopEntry As String 'opzionale +Property Read org_mpris_MediaPlayer2_SupportedUriSchemes As String[] +Property Read org_mpris_MediaPlayer2_SupportedMimeTypes As String[] + +Function org_mpris_MediaPlayer2_CanQuit_Read() As Boolean + Return True +End + +Function org_mpris_MediaPlayer2_CanRaise_Read() As Boolean + Return True +End + +Function org_mpris_MediaPlayer2_HasTrackList_Read() As Boolean + Return True +End + +Function org_mpris_MediaPlayer2_Identity_Read() As String + Return "xt7-player" +End + +Function org_mpris_MediaPlayer2_DesktopEntry_Read() As String + Return "xt7-player" +End + +Function org_mpris_MediaPlayer2_SupportedUriSchemes_Read() As String[] + Return ["file", "http"] +End + +Function org_mpris_MediaPlayer2_SupportedMimeTypes_Read() As String[] + Return ["application/ogg", "application/x-ogg", "application/x-ogm-audio", "audio/aac", "audio/mp4", "audio/mpeg", "audio/mpegurl", "audio/ogg", "audio/vnd.rn-realaudio", "audio/vorbis", "audio/x-flac", "audio/x-mp3", "audio/x-mpeg", "audio/x-mpegurl", "audio/x-ms-wma", "audio/x-musepack", "audio/x-oggflac", "audio/x-pn-realaudio", "audio/x-scpls", "audio/x-speex", "audio/x-vorbis", "audio/x-vorbis+ogg", "audio/x-wav", "video/x-ms-asf", "x-content/audio-player"] +End + +Public Function org_mpris_MediaPlayer2_Raise() + 'FMain.raise +End + + Public Function org_mpris_MediaPlayer2_Quit() + 'we cannot just quit the application, because it would unregister + 'dbus before we exit this kifunction, leading to a runtime error + 'so use a timer to program a delayed quit. + 'FMain.TimerQuit.start + End + +''ORG.MPRIS.MEDIAPLAYER2.PLAYER----------------------------------------------------------------------------------------------------------------- +Property Read org_mpris_MediaPlayer2_Player_PlaybackStatus As String +Property org_mpris_MediaPlayer2_Player_LoopStatus As String +Property org_mpris_MediaPlayer2_Player_Rate As Float +Property org_mpris_MediaPlayer2_Player_Shuffle As Boolean +Property Read org_mpris_MediaPlayer2_Player_Metadata As String[] 'controllare bene il tipo di dato +Property org_mpris_MediaPlayer2_Player_Volume As Float +Property Read org_mpris_MediaPlayer2_Player_Position As Long +Property Read org_mpris_MediaPlayer2_Player_MinimumRate As Float +Property Read org_mpris_MediaPlayer2_Player_MaximumRate As Float +Property Read org_mpris_MediaPlayer2_Player_CanGoNext As Boolean +Property Read org_mpris_MediaPlayer2_Player_CanGoPrevious As Boolean +Property Read org_mpris_MediaPlayer2_Player_CanPlay As Boolean +Property Read org_mpris_MediaPlayer2_Player_CanPause As Boolean +Property Read org_mpris_MediaPlayer2_Player_CanSeek As Boolean +Property Read org_mpris_MediaPlayer2_Player_CanControl As Boolean + + +Function org_mpris_MediaPlayer2_Player_PlayBackStatus_Read() As String +End + +Function org_mpris_MediaPlayer2_Player_LoopStatus_Read() As String + 'If FMain.TogglePls.value Then + Return "Playlist" + 'Else + 'Return "None" + 'Endif +End + +Function org_mpris_MediaPlayer2_Player_LoopStatus_Write(p As String) + 'FMain.TogglePls.value = (p = "Playlist") +End + +Function org_mpris_MediaPlayer2_Player_Rate_Read() As Float +End + +Function org_mpris_MediaPlayer2_Player_Rate_Write(p As Float) +End + +Function org_mpris_MediaPlayer2_Player_Shuffle_Read() As Boolean + 'Return FMain.ShufflePls.value +End + +Function org_mpris_MediaPlayer2_Player_Shuffle_Write(p As Boolean) + 'FMain.ShufflePls.value = p +End + +Function org_mpris_MediaPlayer2_Player_Metadata_Read() As String[] +End + +Function org_mpris_MediaPlayer2_Player_Volume_Read() As Float + 'Return (FMain.SaveVolumeSlider.value * (100 Div FMain.SaveVolumeSlider.MaxValue)) +End + +Function org_mpris_MediaPlayer2_Player_Volume_Write(p As Float) + ' If p <= 0 Then + ' FMain.volume_set("0") + ' Else + ' FMain.volume_set(CStr(p * 100)) + ' Endif +End + +Function org_mpris_MediaPlayer2_Player_Position_Read() As Long +End + +Function org_mpris_MediaPlayer2_Player_Position_Write() As Long +End + +Function org_mpris_MediaPlayer2_Player_MinimumRate_Read() As Float +End + +Function org_mpris_MediaPlayer2_Player_MaximumRate_Read() As Float +End + +Function org_mpris_MediaPlayer2_Player_CanGoNext_Read() As Boolean +End + +Function org_mpris_MediaPlayer2_Player_CanGoPrevious_Read() As Boolean +End + +Function org_mpris_MediaPlayer2_Player_CanPlay_Read() As Boolean +End + +Function org_mpris_MediaPlayer2_Player_CanPause_Read() As Boolean +End + +Function org_mpris_MediaPlayer2_Player_CanSeek_Read() As Boolean +End + +Function org_mpris_MediaPlayer2_Player_CanControl_Read() As Boolean +End + + + +Public Function org_mpris_MediaPlayer2_Player_Next() + 'FMain.SelectNext(False, FMain.GetActivePlayQueue()) +End + +Public Function org_mpris_MediaPlayer2_Player_Previous() + 'FMain.ButtonPrev_click() +End + +Public Function org_mpris_MediaPlayer2_Player_Pause() + +End + +Public Function org_mpris_MediaPlayer2_Player_PlayPause() +' FMain.ButtonPlay_click() +End + +Public Function org_mpris_MediaPlayer2_Player_Stop() + 'FMain.buttonstop_click() +End + +Public Function org_mpris_MediaPlayer2_Player_Play() + 'If (FMain.mplayer.ProcessRunningOvr()) And (Not FMain.mplayer.paused()) Then Return + 'FMain.ButtonPlay_click() +End + +Public Function org_mpris_MediaPlayer2_Player_Seek(p As Long) + +End + +Public Function org_mpris_MediaPlayer2_Player_SetPosition(o As Long, p As Long) + +End + +Public Function org_mpris_MediaPlayer2_Player_OpenUri(p As String) + 'FMain.Play(p) +End diff --git a/gb.dbus/src/helper.c b/gb.dbus/src/helper.c new file mode 100644 index 00000000..6a80e644 --- /dev/null +++ b/gb.dbus/src/helper.c @@ -0,0 +1,1557 @@ +/*************************************************************************** + + helper.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __HELPER_C + +#include "c_dbusvariant.h" +#include "c_dbusobserver.h" +#include "dbus_print_message.h" +#include "helper.h" + +//#define DEBUG_ARG + +typedef + void (*RETRIEVE_CALLBACK)(GB_TYPE type, void *data, void *param); + +typedef + struct { + GB_COLLECTION col; + char *key; + } + COLLECTION_ADD; + +bool DBUS_Debug = FALSE; + +static void handle_message(int fd, int type, DBusConnection *connection) +{ + //fprintf(stdout, "handle_message\n"); + do + { + dbus_connection_read_write_dispatch(connection, -1); + } + while (dbus_connection_get_dispatch_status(connection) == DBUS_DISPATCH_DATA_REMAINS); +} + +static void check_message_now(DBusConnection *connection) +{ + if (dbus_connection_get_dispatch_status(connection) == DBUS_DISPATCH_DATA_REMAINS) + handle_message(-1, 0, connection); +} + +static void check_message(DBusConnection *connection) +{ + GB.Post((GB_CALLBACK)check_message_now, (intptr_t)connection); +} + + +/*************************************************************************** + +Method signatures have the following syntax: + +... + +where is the DBus signature of the i-th input argument. All +arguments are in order. + +The signature of one argument can be a simple type: + +y BYTE Byte +b BOOLEAN Boolean +n INT16 Short +q UINT16 Short +i INT32 Integer +u UINT32 Integer +x INT64 Long +t UINT64 Long +d DOUBLE Float +s STRING String +o OBJECT_PATH String +g SIGNATURE String +v VARIANT Variant + +Or a compound type (T is any datatype): + +aT ARRAY Array +(TT...) STRUCT Variant[] +{TT} DICT_ENTRY Collection + +***************************************************************************/ + +typedef + struct { + const char *name; + const char *dbus; + } + CONV_TYPE; + +CONV_TYPE _conv_type[] = +{ + { "DBusObject", "o" }, + { "Collection", "a{sv}" }, + { "Boolean[]", "ab" }, + { "Byte[]", "ay" }, + { "Short[]", "an" }, + { "Integer[]", "ai" }, + { "Long[]", "ax" }, + { "Single[]", "ad" }, + { "Float[]", "ad" }, + { "Date[]", "ad" }, + { "Pointer[]", "ax" }, + { "String[]", "as" }, + { "Variant[]", "av" }, + { "DBusObject[]", "ao" }, + { NULL } +}; + + +static const char *to_dbus_type(GB_VALUE *arg) +{ + CONV_TYPE *p; + GB_TYPE type, atype; + int a; + const char *dtype; + char *result; + + switch(arg->type) + { + case GB_T_BOOLEAN: return DBUS_TYPE_BOOLEAN_AS_STRING; + case GB_T_BYTE: return DBUS_TYPE_BYTE_AS_STRING; + case GB_T_SHORT: return DBUS_TYPE_INT16_AS_STRING; + case GB_T_INTEGER: return DBUS_TYPE_INT32_AS_STRING; + case GB_T_LONG: return DBUS_TYPE_INT64_AS_STRING; + case GB_T_POINTER: return DBUS_TYPE_INT64_AS_STRING; + case GB_T_SINGLE: return DBUS_TYPE_DOUBLE_AS_STRING; + case GB_T_FLOAT: return DBUS_TYPE_DOUBLE_AS_STRING; + case GB_T_STRING: return DBUS_TYPE_STRING_AS_STRING; + default: break; + } + + if (arg->type >= GB_T_OBJECT) + { + //fprintf(stderr, "%s\n", GB.GetClassName((void *)arg->type)); + + for (p = _conv_type; p->name; p++) + { + if (GB.FindClass(p->name) == arg->type) + return p->dbus; + } + + if (GB.Is(arg->_object.value, GB.FindClass("Array"))) + { + type = arg->type; + a = 0; + + for(;;) + { + atype = GB.GetArrayType(type); + if (atype <= GB_T_OBJECT) + break; + type = atype; + a++; + } + + dtype = "v"; + for (p = _conv_type; p->name; p++) + { + if (GB.FindClass(p->name) == type) + { + dtype = p->dbus; + break; + } + } + + result = NULL; + while (a--) + result = GB.AddChar(result, 'a'); + result = GB.AddString(result, dtype, strlen(dtype)); + GB.FreeStringLater(result); + + return result; + } + + if (GB.Is(arg->_object.value, CLASS_DBusVariant)) + return "v"; + + if (GB.Is(arg->_object.value, CLASS_DBusNull)) + return "s"; + + return NULL; + } + + return "v"; +} + +static char *array_from_dbus_type(const char *signature) +{ + static char type[DBUS_MAXIMUM_SIGNATURE_LENGTH + 1]; + DBusSignatureIter siter; + + dbus_signature_iter_init(&siter, signature); + + switch (dbus_signature_iter_get_current_type(&siter)) + { + case DBUS_TYPE_BYTE: return "Byte[]"; + case DBUS_TYPE_BOOLEAN: return "Boolean[]"; + case DBUS_TYPE_INT16: case DBUS_TYPE_UINT16: return "Short[]"; + case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: return "Integer[]"; + case DBUS_TYPE_INT64: case DBUS_TYPE_UINT64: return "Long[]"; + case DBUS_TYPE_DOUBLE: return "Float[]"; + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: return "String[]"; + case DBUS_TYPE_VARIANT: return "Variant[]"; + + case DBUS_TYPE_DICT_ENTRY: + if (signature[1] == 's') + return "Collection"; + else + return NULL; + + + case DBUS_TYPE_ARRAY: + { + DBusSignatureIter siter_contents; + char *type_contents; + char *sign_contents; + + dbus_signature_iter_recurse(&siter, &siter_contents); + sign_contents = dbus_signature_iter_get_signature(&siter_contents); + type_contents = array_from_dbus_type(sign_contents); + dbus_free(sign_contents); + if (!type_contents) + return NULL; + + if (type_contents != type) + strcpy(type, type_contents); + GB.GetArrayClass(GB.FindClass(type)); + strcat(type, "[]"); + return type; + } + + default: + return "Variant[]"; + } +} + +static GB_TYPE from_dbus_type(const char *signature) +{ + DBusSignatureIter siter; + + dbus_signature_iter_init(&siter, signature); + + switch (dbus_signature_iter_get_current_type(&siter)) + { + case DBUS_TYPE_BYTE: return GB_T_BYTE; + case DBUS_TYPE_BOOLEAN: return GB_T_BOOLEAN; + case DBUS_TYPE_INT16: case DBUS_TYPE_UINT16: return GB_T_SHORT; + case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: return GB_T_INTEGER; + case DBUS_TYPE_INT64: case DBUS_TYPE_UINT64: return GB_T_LONG; + case DBUS_TYPE_DOUBLE: return GB_T_FLOAT; + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: return GB_T_STRING; + + case DBUS_TYPE_ARRAY: + { + DBusSignatureIter siter_contents; + char *type; + char *sign_contents; + + dbus_signature_iter_recurse(&siter, &siter_contents); + sign_contents = dbus_signature_iter_get_signature(&siter_contents); + type = array_from_dbus_type(sign_contents); + dbus_free(sign_contents); + if (type) + return GB.FindClass(type); + else + return GB_T_VARIANT; + } + + case DBUS_TYPE_STRUCT: + { + DBusSignatureIter siter_contents; + char *atype; + GB_TYPE type, type2; + char *sign_contents; + + dbus_signature_iter_recurse(&siter, &siter_contents); + sign_contents = dbus_signature_iter_get_signature(&siter_contents); + atype = array_from_dbus_type(sign_contents); + dbus_free(sign_contents); + if (atype) + type = GB.FindClass(atype); + else + return GB.FindClass("Variant[]"); + + while (dbus_signature_iter_next(&siter_contents)) + { + sign_contents = dbus_signature_iter_get_signature(&siter_contents); + atype = array_from_dbus_type(sign_contents); + dbus_free(sign_contents); + if (atype) + type2 = GB.FindClass(atype); + else + return GB.FindClass("Variant[]"); + + if (type != type2) + return GB.FindClass("Variant[]"); + } + + return type; + } + + case DBUS_TYPE_VARIANT: + default: return GB_T_VARIANT; + } +} + +static bool append_arg(DBusMessageIter *iter, const char *signature, GB_VALUE *arg) +{ + DBusSignatureIter siter; + int type; + GB_TYPE gtype; + char *sign; + GB_VALUE rarg; + GB_VALUE targ; + + if (arg->type == GB_T_VARIANT) + GB.Conv(arg, arg->_variant.value.type); + + dbus_signature_iter_init(&siter, signature); + type = dbus_signature_iter_get_current_type(&siter); + + sign = dbus_signature_iter_get_signature(&siter); + gtype = from_dbus_type(sign); + dbus_free(sign); + + if (gtype == GB_T_NULL) + { + GB.Error("Unsupported datatype for signature '&1'", sign); + goto __UNSUPPORTED; + } + else if (gtype != GB_T_VARIANT) + { + // The contents of arg must not be modified, because it may have to be freed later by the caller + + if (arg->type >= GB_T_OBJECT) + { + if (GB.Is(arg->_object.value, CLASS_DBusNull)) + { + targ.type = GB_T_NULL; + GB.ReleaseValue(arg); + arg = &targ; + } + else if (GB.Is(arg->_object.value, CLASS_DBusObject) && type == DBUS_TYPE_OBJECT_PATH) + { + GB_VALUE *val = GB.GetProperty(arg->_object.value, "_Path"); + + if (val && (val->type == GB_T_STRING || val->type != GB_T_CSTRING)) + { + targ = *val; + targ.type = GB_T_CSTRING; + // The value returned by GB.GetProperty() is not referenced, so it must not be freed. + GB.BorrowValue(&targ); + GB.ReleaseValue(arg); + arg = &targ; + } + } + else if (GB.Is(arg->_object.value, CLASS_DBusVariant)) + { + CDBUSVARIANT *dbusvariant = (CDBUSVARIANT *)arg->_object.value; + + targ.type = GB_T_VARIANT; + targ._variant.value = dbusvariant->value; + GB.BorrowValue(&targ); + GB.ReleaseValue(arg); + arg = &targ; + } + } + + if (GB.Conv(arg, gtype)) + { + //BREAKPOINT(); + GB.ReleaseValue(arg); + GB.Error("Type mismatch"); + return TRUE; + } + } + + switch(type) + { + case DBUS_TYPE_BYTE: + { + unsigned char val; + val = arg->_integer.value; + dbus_message_iter_append_basic(iter, type, &val); +#ifdef DEBUG_ARG + fprintf(stderr, "%d ", (int)val); +#endif + break; + } + + case DBUS_TYPE_BOOLEAN: + { + dbus_bool_t val; + val = arg->_boolean.value ? 1 : 0; + dbus_message_iter_append_basic(iter, type, &val); +#ifdef DEBUG_ARG + fprintf(stderr, "%s ", val ? "true" : "false"); +#endif + break; + } + + case DBUS_TYPE_INT16: + case DBUS_TYPE_UINT16: + { + dbus_int16_t val; + val = arg->_integer.value; + dbus_message_iter_append_basic(iter, type, &val); +#ifdef DEBUG_ARG + fprintf(stderr, "%d ", (int)val); +#endif + break; + } + + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT32: + { + dbus_int32_t val; + val = arg->_integer.value; + dbus_message_iter_append_basic(iter, type, &val); +#ifdef DEBUG_ARG + fprintf(stderr, "%d ", val); +#endif + break; + } + + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + { + dbus_int64_t val; + val = arg->_long.value; + dbus_message_iter_append_basic(iter, type, &val); +#ifdef DEBUG_ARG + fprintf(stderr, "%" PRId64 " ", val); +#endif + break; + } + + case DBUS_TYPE_DOUBLE: + { + double val; + val = arg->_float.value; + dbus_message_iter_append_basic(iter, type, &val); +#ifdef DEBUG_ARG + fprintf(stderr, "%g ", val); +#endif + break; + } + + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: + { + const char *str; + if (arg->type == GB_T_NULL) + str = ""; + else + str = GB.ToZeroString((GB_STRING *)arg); + + if (type == DBUS_TYPE_OBJECT_PATH && DBUS_validate_path(str, -1)) + { + GB.Error("Invalid object path: &1", str); + goto __ERROR; + } + + dbus_message_iter_append_basic(iter, type, &str); +#ifdef DEBUG_ARG + fprintf(stderr, "\"%s\" ", str); +#endif + break; + } + + case DBUS_TYPE_ARRAY: + { + DBusMessageIter citer; + DBusMessageIter dict_entry_iter; + int i; + GB_VALUE value; + const char *contents_signature = &signature[1]; + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, contents_signature, &citer); + + if (contents_signature[0] == '{' && contents_signature[1] == 's') + { + GB_COLLECTION col = (GB_COLLECTION)(arg->_object.value); + char *key; + int len; + +#ifdef DEBUG_ARG + fprintf(stderr, "{ "); +#endif + if (col) + { + GB_COLLECTION_ITER iter; + + GB.Collection.Enum(col, &iter, NULL, NULL, NULL); + for(;;) + { + if (GB.Collection.Enum(col, &iter, (GB_VARIANT *)&value, &key, &len)) + break; + + key = GB.TempString(key, len); + dbus_message_iter_open_container(&citer, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter); + dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key); +#ifdef DEBUG_ARG + fprintf(stderr, "%s: ", key); +#endif + + GB.BorrowValue(&value); + if (append_arg(&dict_entry_iter, &contents_signature[2], &value)) + goto __ERROR; + + dbus_message_iter_close_container(&citer, &dict_entry_iter); + } + } +#ifdef DEBUG_ARG + fprintf(stderr, "} "); +#endif + } + else + { + GB_ARRAY array = (GB_ARRAY)(arg->_object.value); + +#ifdef DEBUG_ARG + fprintf(stderr, "[ "); +#endif + if (array) + { + for (i = 0; i < GB.Array.Count(array); i++) + { + value.type = GB.Array.Type(array); + GB.ReadValue(&value, GB.Array.Get(array, i), value.type); + GB.BorrowValue(&value); + if (append_arg(&citer, contents_signature, &value)) + goto __ERROR; + } + } + } + + dbus_message_iter_close_container(iter, &citer); +#ifdef DEBUG_ARG + fprintf(stderr, "] "); +#endif + break; + } + + case DBUS_TYPE_STRUCT: + { + GB_ARRAY array; + DBusMessageIter citer; + DBusSignatureIter siter_contents; + int i; + GB_VALUE value; + + array = (GB_ARRAY)(arg->_object.value); + + dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &citer); +#ifdef DEBUG_ARG + fprintf(stderr, "( "); +#endif + dbus_signature_iter_recurse(&siter, &siter_contents); + + if (array) + { + for (i = 0; i < GB.Array.Count(array); i++) + { + value.type = GB.Array.Type(array); + GB.ReadValue(&value, GB.Array.Get(array, i), value.type); + GB.BorrowValue(&value); + sign = dbus_signature_iter_get_signature(&siter_contents); + if (append_arg(&citer, sign, &value)) + goto __ERROR_SIGN; + dbus_free(sign); + dbus_signature_iter_next(&siter_contents); + } + } + + dbus_message_iter_close_container(iter, &citer); +#ifdef DEBUG_ARG + fprintf(stderr, ") "); +#endif + break; + } + + case DBUS_TYPE_VARIANT: + { + DBusMessageIter citer; + const char *contents_signature; + +#ifdef DEBUG_ARG + fprintf(stderr, "< "); +#endif + if (arg->type >= GB_T_OBJECT && GB.Is(arg->_object.value, CLASS_DBusVariant)) + { + CDBUSVARIANT *dbusvariant = (CDBUSVARIANT *)arg->_object.value; + + rarg.type = GB_T_VARIANT; + rarg._variant.value = dbusvariant->value; + + contents_signature = CDBUSVARIANT_get_signature(dbusvariant); + //fprintf(stderr, "#1: %s\n", contents_signature); + GB.BorrowValue(&rarg); + dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, contents_signature, &citer); + if (append_arg(&citer, contents_signature, &rarg)) + goto __ERROR; + dbus_message_iter_close_container(iter, &citer); + } + else + { + contents_signature = to_dbus_type(arg); + //fprintf(stderr, "#3: %s\n", contents_signature); + if (!contents_signature) + { + GB.Error("Unsupported datatype in variant value"); + goto __UNSUPPORTED; + } + + dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, contents_signature, &citer); + GB.BorrowValue(arg); + if (append_arg(&citer, contents_signature, arg)) + goto __ERROR; + dbus_message_iter_close_container(iter, &citer); + } +#ifdef DEBUG_ARG + fprintf(stderr, "> "); +#endif + + break; + } + + default: + GB.Error("Unsupported signature: &1", sign); + goto __UNSUPPORTED; + } + + GB.ReleaseValue(arg); + return FALSE; + +__UNSUPPORTED: + //GB.Error("Unsupported datatype"); + goto __ERROR; + +__ERROR_SIGN: + dbus_free(sign); + +__ERROR: + GB.ReleaseValue(arg); + return TRUE; +} + +static void return_value_cb(GB_TYPE type, void *value, void *param) +{ + if (type == GB_T_STRING) + type = GB_T_CSTRING; + + GB.ReturnPtr(type, value); +} + +static void add_value_cb(GB_TYPE type, void *value, void *param) +{ + GB_ARRAY array = (GB_ARRAY)param; + GB_VALUE val; + + if (type == GB_T_STRING) + type = GB_T_CSTRING; + + GB.ReadValue(&val, value, type); + GB.BorrowValue(&val); + GB.Conv(&val, GB.Array.Type(array)); + GB.Store(GB.Array.Type(array), &val, GB.Array.Add(array)); + GB.ReleaseValue(&val); +} + +static void add_collection_cb(GB_TYPE type, void *value, void *param) +{ + COLLECTION_ADD *add = (COLLECTION_ADD *)param; + GB_VALUE val; + + if (type == GB_T_STRING) + type = GB_T_CSTRING; + + GB.ReadValue(&val, value, type); + GB.BorrowValue(&val); + GB.Conv(&val, GB_T_VARIANT); + //GB.Store(GB.Array.Type(array), &val, GB.Array.Add(array)); + GB.Collection.Set(add->col, add->key, strlen(add->key), (GB_VARIANT *)&val); + GB.ReleaseValue(&val); +} + +static bool retrieve_arg(DBusMessageIter *iter, RETRIEVE_CALLBACK cb, void *param) +{ + char *signature = dbus_message_iter_get_signature(iter); + GB_TYPE gtype = from_dbus_type(signature); + int type = dbus_message_iter_get_arg_type(iter); + +#ifdef DEBUG_ARG + fprintf(stderr, "retrieve_arg: signature = '%s'", signature); + if (gtype > GB_T_OBJECT) fprintf(stderr, " / %s", GB.GetClassName((void *)gtype)); + fprintf(stderr, "\n"); +#endif + + dbus_free(signature); + + if (dbus_type_is_basic(type)) + { +#ifdef DEBUG_ARG + fprintf(stderr, "-> basic type\n"); +#endif + char val[16]; + dbus_message_iter_get_basic(iter, (void *)val); + (*cb)(gtype, val, param); +#ifdef DEBUG_ARG + fprintf(stderr, "<- basic type\n"); +#endif + return FALSE; + } + + switch(type) + { + case DBUS_TYPE_VARIANT: + { + DBusMessageIter iter_contents; + + #ifdef DEBUG_ARG + fprintf(stderr, "-> variant\n"); + #endif + dbus_message_iter_recurse(iter, &iter_contents); + if (retrieve_arg(&iter_contents, cb, param)) + return TRUE; + #ifdef DEBUG_ARG + fprintf(stderr, "<- variant\n"); + #endif + return FALSE; + } + + case DBUS_TYPE_ARRAY: + case DBUS_TYPE_STRUCT: + { + char *signature_contents; + DBusMessageIter iter_contents; + DBusMessageIter dict_entry_contents; + + dbus_message_iter_recurse(iter, &iter_contents); + + signature_contents = dbus_message_iter_get_signature(&iter_contents); + + if (signature_contents[0] == '{' && signature_contents[1] == 's') + { + GB_COLLECTION col; + COLLECTION_ADD add; + + #ifdef DEBUG_ARG + fprintf(stderr, "-> collection\n"); + #endif + + GB.Collection.New(POINTER(&col), FALSE); + + add.col = col; + + for(;;) + { + if (dbus_message_iter_get_arg_type(&iter_contents) == DBUS_TYPE_INVALID) + break; + + dbus_message_iter_recurse(&iter_contents, &dict_entry_contents); + + // key + + dbus_message_iter_get_basic(&dict_entry_contents, &add.key); + dbus_message_iter_next(&dict_entry_contents); + + // value + + if (dbus_message_iter_get_arg_type(&dict_entry_contents) != DBUS_TYPE_INVALID) + { + if (retrieve_arg(&dict_entry_contents, add_collection_cb, &add)) + return TRUE; + } + + dbus_message_iter_next(&iter_contents); + } + + (*cb)(gtype, &col, param); + + #ifdef DEBUG_ARG + fprintf(stderr, "<- collection\n"); + #endif + + return FALSE; + } + else + { + GB_ARRAY array = GB.New(gtype, NULL, NULL); + + #ifdef DEBUG_ARG + fprintf(stderr, (type == DBUS_TYPE_ARRAY) ? "-> array" : "-> struct"); + fprintf(stderr, " '%s' / %s\n", signature_contents, GB.GetClassName((void *)array)); + #endif + + dbus_free(signature_contents); + + while (dbus_message_iter_get_arg_type(&iter_contents) != DBUS_TYPE_INVALID) + { + if (retrieve_arg(&iter_contents, add_value_cb, array)) + return TRUE; + dbus_message_iter_next(&iter_contents); + } + + (*cb)(gtype, &array, param); + + #ifdef DEBUG_ARG + fprintf(stderr, (type == DBUS_TYPE_ARRAY) ? "<- array\n" : "<- struct\n"); + #endif + + return FALSE; + } + } + } + + GB.Error("Unsupported DBus datatype"); + return TRUE; +} + +static bool define_arguments(DBusMessage *message, const char *signature, GB_ARRAY arguments) +{ + int nparam; + int n; + GB_TYPE type; + GB_VALUE value; + DBusMessageIter iter; + DBusSignatureIter siter; + char *sign; + char *arg_sign; + bool err = FALSE; + + if (arguments) + { + nparam = GB.Array.Count(arguments); + type = GB.Array.Type(arguments); + } + else + { + nparam = 0; + type = GB_T_NULL; + } + +#ifdef DEBUG_ARG + fprintf(stderr, "define_arguments: %s %d %ld\n", signature, nparam, type); +#endif + + if (!signature || !*signature) + { + if (nparam > 0) + { + GB.Error("Too many arguments"); + return TRUE; + } + else + return FALSE; + } + + sign = GB.NewZeroString(signature); + + dbus_message_iter_init_append(message, &iter); + + dbus_signature_iter_init(&siter, sign); + + for (n = 0; n < nparam; n++) + { + value.type = type; + GB.ReadValue(&value, GB.Array.Get(arguments, n), type); + GB.BorrowValue(&value); + + arg_sign = dbus_signature_iter_get_signature(&siter); +#ifdef DEBUG_ARG + fprintf(stderr, "[%d] %s: ", n, arg_sign); +#endif + err = append_arg(&iter, arg_sign, &value); +#ifdef DEBUG_ARG + fprintf(stderr, "\n"); +#endif + dbus_free(arg_sign); + + if (err) + goto __RETURN; + + if (!dbus_signature_iter_next(&siter)) + { + if (n < (nparam - 1)) + { + GB.Error("Too many arguments"); + err = TRUE; + } + goto __RETURN; + } + } + + GB.Error("Not enough arguments"); + err = TRUE; + +__RETURN: + + GB.FreeString(&sign); + return err; +} + +bool DBUS_call_method(DBusConnection *connection, const char *application, const char *path, const char *interface, const char *method, + const char *signature_in, const char *signature_out, GB_ARRAY arguments) +{ + DBusMessage *message; + //int n; + DBusMessageIter iter; + DBusMessage *reply; + DBusError error; + //DBusSignatureIter siter; + bool ret; + //GB_TYPE type; + //int nparam; + + message = dbus_message_new_method_call(application, path, interface, method); + if (!message) + { + GB.Error("Couldn't allocate D-Bus message"); + return TRUE; + } + + ret = TRUE; + + dbus_message_set_auto_start(message, TRUE); + + if (define_arguments(message, signature_in, arguments)) + goto __RETURN; + + // Do not use asynchronous call, otherwise error message is lost. + + /*if (!signature_out || !*signature_out) + { + dbus_connection_send(connection, message, NULL); + dbus_connection_flush(connection); + reply = NULL; + ret = FALSE; + }*/ + + dbus_error_init(&error); + reply = dbus_connection_send_with_reply_and_block(connection, message, -1, &error); + check_message(connection); + + if (dbus_error_is_set(&error)) + { + GB.Error("&1: &2", error.name, error.message); + if (reply) + dbus_message_unref(reply); + dbus_error_free(&error); + goto __RETURN; + } + + if (!reply) + goto __RETURN; + + dbus_message_iter_init(reply, &iter); + + if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_INVALID) + { + GB.ReturnNull(); + ret = FALSE; + } + else if (!dbus_message_iter_has_next(&iter)) + { + ret = retrieve_arg(&iter, return_value_cb, NULL); + } + else + { + GB_ARRAY result; + GB.Array.New(POINTER(&result), GB_T_VARIANT, 0); + + do + { + if (retrieve_arg(&iter, add_value_cb, result)) + { + GB.Unref(&result); + goto __RETURN; + } + } + while (dbus_message_iter_next(&iter)); + + GB.ReturnObject(result); + ret = FALSE; + } + + dbus_message_unref(reply); + +__RETURN: + + dbus_message_unref(message); + + return ret; +} + + +bool DBUS_send_signal(DBusConnection *connection, const char *path, const char *interface, const char *signal, const char *signature_in, GB_ARRAY arguments) +{ + DBusMessage *message; + bool ret; + + message = dbus_message_new_signal(path, interface, signal); + if (!message) + { + GB.Error("Couldn't allocate D-Bus message"); + return TRUE; + } + + ret = TRUE; + + //dbus_message_set_auto_start(message, TRUE); + + if (define_arguments(message, signature_in, arguments)) + goto __RETURN; + + //dbus_error_init(&error); + dbus_connection_send(connection, message, NULL); + //dbus_connection_flush(connection); + check_message(connection); + ret = FALSE; + +__RETURN: + + dbus_message_unref(message); + + return ret; +} + + +bool DBUS_retrieve_message_arguments(DBusMessage *message) +{ + DBusMessageIter iter; + + dbus_message_iter_init(message, &iter); + + if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_INVALID) + { + GB.ReturnNull(); + return FALSE; + } + else + { + GB_ARRAY result; + GB.Array.New(POINTER(&result), GB_T_VARIANT, 0); + + do + { + if (retrieve_arg(&iter, add_value_cb, result)) + { + GB.Unref(&result); + return TRUE; + } + } + while(dbus_message_iter_next(&iter)); + + GB.ReturnObject(result); + return FALSE; + } +} + +bool DBUS_reply(DBusConnection *connection, DBusMessage *message, const char *signature, GB_ARRAY arguments) +{ + bool ret = TRUE; + DBusMessage *reply; + //DBusError error; + dbus_uint32_t serial = 0; + + reply = dbus_message_new_method_return(message); + + if (signature && *signature && arguments) + { + if (define_arguments(reply, signature, arguments)) + goto __RETURN; + } + + if (!dbus_connection_send(connection, reply, &serial)) + { + GB.Error("Cannot send reply"); + goto __RETURN; + } + + dbus_connection_flush(connection); + + check_message(connection); + + ret = FALSE; + +__RETURN: + + dbus_message_unref(reply); + return ret; +} + +bool DBUS_error(DBusConnection *connection, DBusMessage *message, const char *type, const char *error) +{ + bool ret = TRUE; + DBusMessage *reply; + dbus_uint32_t serial = 0; + char *full_type; + + if (!error) error = ""; + + if (!type) + full_type = DBUS_ERROR_FAILED; + else + { + full_type = GB.NewZeroString("org.freedesktop.org.DBus.Error."); + full_type = GB.AddString(full_type, type, 0); + } + + reply = dbus_message_new_error(message, full_type, error); + + if (!dbus_connection_send(connection, reply, &serial)) + { + GB.Error("Cannot send error"); + goto __RETURN; + } + + dbus_connection_flush(connection); + + check_message(connection); + + ret = FALSE; + +__RETURN: + + dbus_message_unref(reply); + return ret; +} + +/*static const char *type_to_name (int message_type) +{ + switch (message_type) + { + case DBUS_MESSAGE_TYPE_SIGNAL: + return "signal"; + case DBUS_MESSAGE_TYPE_METHOD_CALL: + return "method call"; + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + return "method return"; + case DBUS_MESSAGE_TYPE_ERROR: + return "error"; + default: + return "(unknown message type)"; + } +}*/ + +char *DBUS_introspect(DBusConnection *connection, const char *application, const char *path) +{ + DBusMessage *message; + DBusMessage *reply; + DBusError error; + DBusMessageIter iter; + int type; + char *signature = NULL; + + message = dbus_message_new_method_call(application, path, "org.freedesktop.DBus.Introspectable", "Introspect"); + if (!message) + { + GB.Error("Couldn't allocate D-Bus message"); + return NULL; + } + + dbus_message_set_auto_start(message, TRUE); + + dbus_error_init(&error); + reply = dbus_connection_send_with_reply_and_block(connection, message, -1, &error); + check_message(connection); + + if (dbus_error_is_set(&error)) + { + GB.Error("&1: &2", error.name, error.message); + dbus_error_free(&error); + goto __RETURN; + } + + if (!reply) + goto __RETURN; + + dbus_message_iter_init(reply, &iter); + type = dbus_message_iter_get_arg_type(&iter); + if (type == DBUS_TYPE_STRING) + dbus_message_iter_get_basic(&iter, &signature); + + dbus_message_unref (reply); + +__RETURN: + + dbus_message_unref(message); + return signature; +} + +static bool check_filter(char *rule, const char *value) +{ + if (!rule || !*rule || (rule[0] == '*' && rule[1] == 0)) + return FALSE; + + return (strcasecmp(rule, value) != 0); +} + +static DBusHandlerResult filter_func(DBusConnection *connection, DBusMessage *message, void *user_data) +{ + CDBUSOBSERVER *obs; + bool found = FALSE; + + for (obs = DBUS_observers; obs; obs = obs->next) + { + if (obs->type != dbus_message_get_type(message)) + continue; + + // Beware: "" means "only me" in DBusObserver, but everything there! + if (check_filter(obs->destination, dbus_message_get_destination(message))) + continue; + if (check_filter(obs->object, dbus_message_get_path(message))) + continue; + if (check_filter(obs->member, dbus_message_get_member(message))) + continue; + if (check_filter(obs->interface, dbus_message_get_interface(message))) + continue; + + //print_message(message, FALSE); + + found = TRUE; + obs->message = message; + obs->reply = FALSE; + DBUS_raise_observer(obs); + obs->message = NULL; + if (obs->reply) + return DBUS_HANDLER_RESULT_HANDLED; + } + + if (!found && DBUS_Debug) + { + fprintf(stderr, "gb.dbus: warning: unhandled message: "); + print_message(message, FALSE); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static bool get_socket(DBusConnection *connection, int *socket) +{ + if (!dbus_connection_get_socket(connection, socket)) + { + GB.Error("Unable to get DBus connection socket"); + return TRUE; + } + else + return FALSE; +} + +bool DBUS_watch(DBusConnection *connection, bool on) +{ + intptr_t count; + int socket; + static dbus_int32_t slot = -1; + + if (!dbus_connection_allocate_data_slot(&slot)) + { + GB.Error("Unable to allocate DBusConnection data slot"); + return TRUE; + } + + if (get_socket(connection, &socket)) + return TRUE; + + count = (intptr_t)dbus_connection_get_data(connection, slot); + + if (on) + { + if (count == 0) + { + count++; + if (!dbus_connection_set_data(connection, slot, (void *)count, NULL)) + { + GB.Error("Unable to increment watch count"); + return TRUE; + } + + if (!dbus_connection_add_filter(connection, filter_func, NULL, NULL)) + { + GB.Error("Unable to watch the DBus connection"); + return TRUE; + } + + if (DBUS_Debug) + fprintf(stderr, "gb.dbus: start watching connection\n"); + + GB.Watch(socket, GB_WATCH_READ, (void *)handle_message, (intptr_t)connection); + check_message(connection); + } + } + else + { + count--; + if (!dbus_connection_set_data(connection, slot, (void *)count, NULL)) + { + GB.Error("Unable to decrement watch count"); + return TRUE; + } + + if (count == 0) + { + if (DBUS_Debug) + fprintf(stderr, "gb.dbus: stop watching connection\n"); + GB.Watch(socket, GB_WATCH_NONE, (void *)handle_message, (intptr_t)connection); + } + } + + return FALSE; +} + +bool DBUS_register(DBusConnection *connection, const char *name, bool unique) +{ + DBusError error; + int ret; + + dbus_error_init(&error); + + //fprintf(stderr, "DBUS_register: %s / unique = %d\n", name, unique); + + ret = dbus_bus_request_name(connection, name, unique ? DBUS_NAME_FLAG_DO_NOT_QUEUE : 0, &error); + + if (dbus_error_is_set(&error)) + { + GB.Error("Unable to register application name: &1", error.message); + dbus_error_free(&error); + return TRUE; + } + + if (unique && ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) + return TRUE; + + return DBUS_watch(connection, TRUE); +} + +bool DBUS_unregister(DBusConnection *connection, const char *name) +{ + DBusError error; + + dbus_error_init(&error); + + dbus_bus_release_name(connection, name, &error); + + if (dbus_error_is_set(&error)) + { + GB.Error("Unable to unregister application name: &1", error.message); + dbus_error_free(&error); + return TRUE; + } + + return DBUS_watch(connection, FALSE); +} + + +/*************************************************************************** + +Validation routines taken directly from the D-Bus source code. + +***************************************************************************/ + + +/** +* Determine wether the given character is valid as the first character +* in a name. +*/ +#define VALID_INITIAL_NAME_CHARACTER(c) \ + ( ((c) >= 'A' && (c) <= 'Z') || \ + ((c) >= 'a' && (c) <= 'z') || \ + ((c) == '_') ) + +/** +* Determine wether the given character is valid as a second or later +* character in a name +*/ +#define VALID_NAME_CHARACTER(c) \ + ( ((c) >= '0' && (c) <= '9') || \ + ((c) >= 'A' && (c) <= 'Z') || \ + ((c) >= 'a' && (c) <= 'z') || \ + ((c) == '_') ) + +bool DBUS_validate_path(const char *path, int len) +{ + const unsigned char *s; + const unsigned char *end; + const unsigned char *last_slash; + + if (len <= 0) + len = strlen(path); + + s = (const unsigned char *)path; + end = s + (uint)len; + + if (*s != '/') + return TRUE; + last_slash = s; + ++s; + + while (s != end) + { + if (*s == '/') + { + if ((s - last_slash) < 2) + return TRUE; /* no empty path components allowed */ + + last_slash = s; + } + else + { + if (!VALID_NAME_CHARACTER (*s)) + return TRUE; + } + + ++s; + } + + if ((end - last_slash) < 2 && len > 1) + return TRUE; /* trailing slash not allowed unless the string is "/" */ + + return FALSE; +} + +bool DBUS_validate_interface (const char *interface, int len) +{ + const unsigned char *s; + const unsigned char *end; + const unsigned char *last_dot; + + if (!interface) + return FALSE; + + if (len <= 0) + len = strlen(interface); + + if (len > DBUS_MAXIMUM_NAME_LENGTH || len == 0) + return TRUE; + + last_dot = NULL; + s = (const unsigned char *)interface; + end = s + (uint)len; + + /* check special cases of first char so it doesn't have to be done + * in the loop. Note we know len > 0 + */ + if (*s == '.') /* disallow starting with a . */ + return TRUE; + else if (!VALID_INITIAL_NAME_CHARACTER (*s)) + return TRUE; + else + ++s; + + while (s != end) + { + if (*s == '.') + { + if ((s + 1) == end) + return TRUE; + else if (!VALID_INITIAL_NAME_CHARACTER (*(s + 1))) + return TRUE; + last_dot = s; + ++s; /* we just validated the next char, so skip two */ + } + else if (!VALID_NAME_CHARACTER (*s)) + { + return TRUE; + } + + ++s; + } + + if (last_dot == NULL) + return TRUE; + + return FALSE; +} + +bool DBUS_validate_method(const char *method, int len) +{ + const unsigned char *s; + const unsigned char *end; + + if (len <= 0) + len = strlen(method); + + if (len > DBUS_MAXIMUM_NAME_LENGTH || len == 0) + return TRUE; + + s = (const unsigned char *)method; + end = s + (uint)len; + + /* check special cases of first char so it doesn't have to be done + * in the loop. Note we know len > 0 + */ + + if (!VALID_INITIAL_NAME_CHARACTER (*s)) + return TRUE; + else + ++s; + + while (s != end) + { + if (!VALID_NAME_CHARACTER (*s)) + { + return TRUE; + } + + ++s; + } + + return FALSE; +} + +GB_ARRAY DBUS_split_signature(const char *signature) +{ + GB_ARRAY result; + DBusSignatureIter siter; + char *sign; + + GB.Array.New(POINTER(&result), GB_T_STRING, 0); + + dbus_signature_iter_init(&siter, signature); + + for(;;) + { + sign = dbus_signature_iter_get_signature(&siter); + *(char **)GB.Array.Add(result) = GB.NewZeroString(sign); + dbus_free(sign); + + if (!dbus_signature_iter_next(&siter)) + break; + } + + return result; +} diff --git a/gb.dbus/src/helper.h b/gb.dbus/src/helper.h new file mode 100644 index 00000000..d190b3ac --- /dev/null +++ b/gb.dbus/src/helper.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + helper.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __HELPER_H +#define __HELPER_H + +#include "main.h" + +#ifndef __HELPER_C +extern bool DBUS_Debug; +#endif + +bool DBUS_call_method(DBusConnection *connection, const char *application, const char *path, const char *interface, const char *method, + const char *signature_in, const char *signature_out, GB_ARRAY arguments); + +bool DBUS_send_signal(DBusConnection *connection, const char *path, const char *interface, const char *signal, const char *signature_in, GB_ARRAY arguments); + +char *DBUS_introspect(DBusConnection *connection, const char *dest, const char *path); + +bool DBUS_register(DBusConnection *connection, const char *name, bool unique); +bool DBUS_unregister(DBusConnection *connection, const char *name); +bool DBUS_watch(DBusConnection *connection, bool on); + +bool DBUS_validate_path(const char *path, int len); +bool DBUS_validate_interface (const char *interface, int len); +bool DBUS_validate_method(const char *method, int len); + +bool DBUS_retrieve_message_arguments(DBusMessage *message); +bool DBUS_reply(DBusConnection *connection, DBusMessage *message, const char *signature, GB_ARRAY arguments); + +const char *DBUS_to_dbus_type(GB_TYPE type); + +bool DBUS_error(DBusConnection *connection, DBusMessage *message, const char *type, const char *error); + +GB_ARRAY DBUS_split_signature(const char *signature); + +#endif diff --git a/gb.dbus/src/main.c b/gb.dbus/src/main.c new file mode 100644 index 00000000..a98b4a2c --- /dev/null +++ b/gb.dbus/src/main.c @@ -0,0 +1,60 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "c_dbusvariant.h" +#include "c_dbusconnection.h" +#include "c_dbusobserver.h" +#include "c_dbus.h" + +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_CLASS CLASS_DBusVariant; +GB_CLASS CLASS_DBusNull; +GB_CLASS CLASS_DBusObject; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CDBusVariantDesc, + CDBusObserverMessageDesc, + CDBusObserverDesc, + CDBusConnectionDesc, + CDBusDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + CLASS_DBusVariant = GB.FindClass("DBusVariant"); + CLASS_DBusNull = GB.FindClass("_DBusNull"); + CLASS_DBusObject = GB.FindClass("DBusObject"); + return 0; +} + +void EXPORT GB_EXIT() +{ +} + diff --git a/gb.dbus/src/main.h b/gb.dbus/src/main.h new file mode 100644 index 00000000..6a88c100 --- /dev/null +++ b/gb.dbus/src/main.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" + +#include + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern GB_CLASS CLASS_DBusObject; +extern GB_CLASS CLASS_DBusVariant; +extern GB_CLASS CLASS_DBusNull; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.desktop.gnome.keyring/AUTHORS b/gb.desktop.gnome.keyring/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.desktop.gnome.keyring/COPYING b/gb.desktop.gnome.keyring/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.desktop.gnome.keyring/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.desktop.gnome.keyring/ChangeLog b/gb.desktop.gnome.keyring/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.desktop.gnome.keyring/INSTALL b/gb.desktop.gnome.keyring/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.desktop.gnome.keyring/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.desktop.gnome.keyring/Makefile.am b/gb.desktop.gnome.keyring/Makefile.am new file mode 100644 index 00000000..6ebcb700 --- /dev/null +++ b/gb.desktop.gnome.keyring/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @DESKTOP_GNOME_KEYRING_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.desktop.gnome.keyring/NEWS b/gb.desktop.gnome.keyring/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.desktop.gnome.keyring/README b/gb.desktop.gnome.keyring/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.desktop.gnome.keyring/acinclude.m4 b/gb.desktop.gnome.keyring/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.desktop.gnome.keyring/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.desktop.gnome.keyring/component.am b/gb.desktop.gnome.keyring/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.desktop.gnome.keyring/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.desktop.gnome.keyring/configure.ac b/gb.desktop.gnome.keyring/configure.ac new file mode 100644 index 00000000..ab0ed91f --- /dev/null +++ b/gb.desktop.gnome.keyring/configure.ac @@ -0,0 +1,18 @@ +dnl ---- configure.ac for gb.desktop.gnome.keyring + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-desktop-gnome-keyring, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.desktop.gnome.keyring) +AC_PROG_LIBTOOL + +GB_COMPONENT_PKG_CONFIG( + gb_desktop_gnome_keyring, + DESKTOP_GNOME_KEYRING, + gb.desktop.gnome.keyring, + [src], + gnome-keyring-1) + +AC_OUTPUT( Makefile src/Makefile ) +GB_PRINT_MESSAGES diff --git a/gb.desktop.gnome.keyring/gambas.h b/gb.desktop.gnome.keyring/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.desktop.gnome.keyring/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.desktop.gnome.keyring/gb_common.h b/gb.desktop.gnome.keyring/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.desktop.gnome.keyring/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.desktop.gnome.keyring/m4 b/gb.desktop.gnome.keyring/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.desktop.gnome.keyring/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.desktop.gnome.keyring/reconf b/gb.desktop.gnome.keyring/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.desktop.gnome.keyring/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.desktop.gnome.keyring/src/Makefile.am b/gb.desktop.gnome.keyring/src/Makefile.am new file mode 100644 index 00000000..37f244f8 --- /dev/null +++ b/gb.desktop.gnome.keyring/src/Makefile.am @@ -0,0 +1,11 @@ +COMPONENT = gb.desktop.gnome.keyring +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.desktop.gnome.keyring.la + +gb_desktop_gnome_keyring_la_LIBADD = @DESKTOP_GNOME_KEYRING_LIB@ +gb_desktop_gnome_keyring_la_LDFLAGS = -module @LD_FLAGS@ @DESKTOP_GNOME_KEYRING_LDFLAGS@ +gb_desktop_gnome_keyring_la_CPPFLAGS = @DESKTOP_GNOME_KEYRING_INC@ + +gb_desktop_gnome_keyring_la_SOURCES = main.h main.c keyring.c keyring.h + diff --git a/gb.desktop.gnome.keyring/src/gb.desktop.gnome.keyring.component b/gb.desktop.gnome.keyring/src/gb.desktop.gnome.keyring.component new file mode 100644 index 00000000..c4e3f89c --- /dev/null +++ b/gb.desktop.gnome.keyring/src/gb.desktop.gnome.keyring.component @@ -0,0 +1,3 @@ +[Component] +State=Deprecated +Requires=gb.desktop diff --git a/gb.desktop.gnome.keyring/src/keyring.c b/gb.desktop.gnome.keyring/src/keyring.c new file mode 100644 index 00000000..b576c21f --- /dev/null +++ b/gb.desktop.gnome.keyring/src/keyring.c @@ -0,0 +1,161 @@ +/*************************************************************************** + + keyring.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __KEYRING_C + +#include +#include + +#include +#include "gnome-keyring.h" +#include "keyring.h" + +BEGIN_METHOD_VOID(KeyRing_init) + + g_set_prgname(GB.Application.Name()); + g_set_application_name(GB.Application.Title()); + +END_METHOD + +static char *get_password(const char *name) +{ + GnomeKeyringAttributeList * attributes; + GnomeKeyringResult result; + GList *found_list; + GList *i; + GnomeKeyringFound *found; + char * password = NULL; + + attributes = g_array_new(FALSE, FALSE, sizeof (GnomeKeyringAttribute)); + gnome_keyring_attribute_list_append_string(attributes, "name", name); + gnome_keyring_attribute_list_append_string(attributes, "magic", GB.Application.Name()); + + result = gnome_keyring_find_items_sync(GNOME_KEYRING_ITEM_GENERIC_SECRET, attributes, &found_list); + gnome_keyring_attribute_list_free(attributes); + + if (result == GNOME_KEYRING_RESULT_NO_MATCH) + return NULL; + + if (result != GNOME_KEYRING_RESULT_OK) + { + GB.Error("Unable to retrieve password: &1", gnome_keyring_result_to_message(result)); + return NULL; + } + + for (i = found_list; i != NULL; i = i->next) + { + found = i->data; + password = g_strdup(found->secret); + break; + } + + gnome_keyring_found_list_free(found_list); + + return password; +} + + +static bool set_password(const char *name, const char *password) +{ + GnomeKeyringAttributeList *attributes; + GnomeKeyringResult result; + guint item_id; + GList *found_list; + GList *i; + + attributes = g_array_new(FALSE, FALSE, sizeof (GnomeKeyringAttribute)); + gnome_keyring_attribute_list_append_string(attributes, "name", name); + gnome_keyring_attribute_list_append_string(attributes, "magic", GB.Application.Name()); + + if (password && *password) + { + result = gnome_keyring_item_create_sync(NULL, + GNOME_KEYRING_ITEM_GENERIC_SECRET, + name, + attributes, + password, + TRUE, + &item_id); + + gnome_keyring_attribute_list_free(attributes); + + if (result == GNOME_KEYRING_RESULT_OK) + return FALSE; + + GB.Error("Unable to store password: &1", gnome_keyring_result_to_message(result)); + return TRUE; + } + else + { + result = gnome_keyring_find_items_sync(GNOME_KEYRING_ITEM_GENERIC_SECRET, attributes, &found_list); + gnome_keyring_attribute_list_free(attributes); + + if (result == GNOME_KEYRING_RESULT_NO_MATCH) + return FALSE; + + if (result == GNOME_KEYRING_RESULT_OK) + { + for (i = found_list; i != NULL; i = i->next) + gnome_keyring_item_delete_sync(NULL, ((GnomeKeyringFound *)(i->data))->item_id); + + gnome_keyring_found_list_free(found_list); + return FALSE; + } + + GB.Error("Unable to remove password: &1", gnome_keyring_result_to_message(result)); + return TRUE; + } +} + +BEGIN_METHOD(KeyRing_get_password, GB_STRING key) + + char *password; + + password = get_password(GB.ToZeroString(ARG(key))); + + if (password) + { + GB.ReturnNewZeroString(password); + g_free(password); + } + +END_METHOD + +BEGIN_METHOD(KeyRing_set_password, GB_STRING key; GB_STRING password) + + if (!set_password(GB.ToZeroString(ARG(key)), GB.ToZeroString(ARG(password)))) + return; + +END_METHOD + +GB_DESC CKeyringDesc[] = +{ + GB_DECLARE("_Keyring", 0), + + GB_STATIC_METHOD("_init", NULL, KeyRing_init, NULL), + + GB_STATIC_METHOD("GetPassword", "s", KeyRing_get_password, "(Key)s"), + GB_STATIC_METHOD("SetPassword", NULL, KeyRing_set_password, "(Key)s(Password)s"), + + GB_END_DECLARE +}; diff --git a/gb.desktop.gnome.keyring/src/keyring.h b/gb.desktop.gnome.keyring/src/keyring.h new file mode 100644 index 00000000..bb4d4bba --- /dev/null +++ b/gb.desktop.gnome.keyring/src/keyring.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + keyring.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __KEYRING_H +#define __KEYRING_H + +#include "gambas.h" +#include "main.h" + +#ifndef __KEYRING_C +extern GB_DESC CKeyringDesc[]; +#endif + +#endif diff --git a/gb.desktop.gnome.keyring/src/main.c b/gb.desktop.gnome.keyring/src/main.c new file mode 100644 index 00000000..bff1e89b --- /dev/null +++ b/gb.desktop.gnome.keyring/src/main.c @@ -0,0 +1,49 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "keyring.h" +#include "main.h" + + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CKeyringDesc, + NULL +}; + + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} + + + diff --git a/gb.desktop.gnome.keyring/src/main.h b/gb.desktop.gnome.keyring/src/main.h new file mode 100644 index 00000000..50ba4e7a --- /dev/null +++ b/gb.desktop.gnome.keyring/src/main.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/gb.desktop.x11/AUTHORS b/gb.desktop.x11/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.desktop.x11/COPYING b/gb.desktop.x11/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.desktop.x11/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.desktop.x11/ChangeLog b/gb.desktop.x11/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.desktop.x11/INSTALL b/gb.desktop.x11/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.desktop.x11/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.desktop.x11/Makefile.am b/gb.desktop.x11/Makefile.am new file mode 100644 index 00000000..52805698 --- /dev/null +++ b/gb.desktop.x11/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @DESKTOP_X11_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.desktop.x11/NEWS b/gb.desktop.x11/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.desktop.x11/README b/gb.desktop.x11/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.desktop.x11/acinclude.m4 b/gb.desktop.x11/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.desktop.x11/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.desktop.x11/component.am b/gb.desktop.x11/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.desktop.x11/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.desktop.x11/configure.ac b/gb.desktop.x11/configure.ac new file mode 100644 index 00000000..b616663e --- /dev/null +++ b/gb.desktop.x11/configure.ac @@ -0,0 +1,23 @@ +dnl ---- configure.ac for gb.desktop.x11 + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-desktop-x11, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) +GB_INIT(gb.desktop.x11) + +AC_PROG_LIBTOOL +GB_CHECK_XWINDOW +AC_CHECK_LIB(Xtst, XTestQueryExtension, [X_LIBS="$X_LIBS -lXtst"], [touch DISABLED], [$X_LIBS]) + +GB_COMPONENT( + desktop_x11, + DESKTOP_X11, + gb.desktop.x11, + [src], + [], + [], + [$C_LIB $THREAD_LIB $X_LIBS], + [$THREAD_INC]) + +AC_OUTPUT( Makefile src/Makefile ) +GB_PRINT_MESSAGES diff --git a/gb.desktop.x11/gambas.h b/gb.desktop.x11/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.desktop.x11/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.desktop.x11/gb.image.h b/gb.desktop.x11/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.desktop.x11/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.desktop.x11/gb_common.h b/gb.desktop.x11/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.desktop.x11/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.desktop.x11/gb_list.h b/gb.desktop.x11/gb_list.h new file mode 120000 index 00000000..cfd35489 --- /dev/null +++ b/gb.desktop.x11/gb_list.h @@ -0,0 +1 @@ +../main/share/gb_list.h \ No newline at end of file diff --git a/gb.desktop.x11/gb_list_temp.h b/gb.desktop.x11/gb_list_temp.h new file mode 120000 index 00000000..0bd26c39 --- /dev/null +++ b/gb.desktop.x11/gb_list_temp.h @@ -0,0 +1 @@ +../main/share/gb_list_temp.h \ No newline at end of file diff --git a/gb.desktop.x11/m4 b/gb.desktop.x11/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.desktop.x11/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.desktop.x11/reconf b/gb.desktop.x11/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.desktop.x11/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.desktop.x11/src/Makefile.am b/gb.desktop.x11/src/Makefile.am new file mode 100644 index 00000000..851ef9a6 --- /dev/null +++ b/gb.desktop.x11/src/Makefile.am @@ -0,0 +1,38 @@ +COMPONENT = gb.desktop.x11 +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.desktop.x11.la + +gb_desktop_x11_la_LIBADD = @DESKTOP_X11_LIB@ +gb_desktop_x11_la_LDFLAGS = -module @LD_FLAGS@ @DESKTOP_X11_LDFLAGS@ +gb_desktop_x11_la_CPPFLAGS = @DESKTOP_X11_INC@ + +gb_desktop_x11_la_SOURCES = \ + main.c main.h \ + x11.c x11.h \ + gb_list.c \ + c_x11.c c_x11.h \ + c_x11systray.h c_x11systray.c \ + systray/systray.h \ + systray/systray.c \ + systray/icons.c \ + systray/icons.h \ + systray/xembed.c \ + systray/xembed.h \ + systray/embed.c \ + systray/embed.h \ + systray/settings.c \ + systray/settings.h \ + systray/xutils.c \ + systray/xutils.h \ + systray/tray.c \ + systray/tray.h \ + systray/debug.h \ + systray/list.h \ + systray/wmh.c \ + systray/wmh.h \ + systray/common.h \ + systray/debug.c \ + systray/kde_tray.c \ + systray/kde_tray.h + \ No newline at end of file diff --git a/gb.desktop.x11/src/c_x11.c b/gb.desktop.x11/src/c_x11.c new file mode 100644 index 00000000..63736247 --- /dev/null +++ b/gb.desktop.x11/src/c_x11.c @@ -0,0 +1,759 @@ +/*************************************************************************** + + c_x11.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_X11_C + +#include "x11.h" +#include "c_x11.h" + +//#define DEBUG_ICON 1 + +#if 0 +typedef struct PROPERTY_FORMAT +{ + const char *name; + Atom atom; + const char *format; +}; + +static PROPERTY_FORMAT _property_format[] = { + {"ARC" XA_ARC, "16iiccii" }, + {"ATOM", XA_ATOM, "32a" }, + {"BITMAP", XA_BITMAP, "32x" }, + {"CARDINAL", XA_CARDINAL, "0c" }, + {"COLORMAP", XA_COLORMAP, "32x" }, + {"CURSOR", XA_CURSOR, "32x" }, + {"DRAWABLE", XA_DRAWABLE, "32x" }, + {"FONT", XA_FONT, "32x" }, + {"INTEGER", XA_INTEGER, "0i" }, + {"PIXMAP", XA_PIXMAP, "32x" }, + {"POINT", XA_POINT, "16ii" }, + {"RECTANGLE", XA_RECTANGLE, "16iicc" }, + {"RGB_COLOR_MAP", XA_RGB_COLOR_MAP, "32xcccccccxx" }, + {"STRING", XA_STRING, "8s" }, + {"WINDOW", XA_WINDOW, "32x" }, + {"VISUALID", XA_VISUALID, "32x" }, + {"WM_COLORMAP_WINDOWS", 0, "32x" }, + {"WM_COMMAND", XA_WM_COMMAND, "8s" }, + {"WM_HINTS", XA_WM_HINTS, "32mbcxxiixx" }, + {"WM_ICON_NAME", XA_WM_ICON_NAME, "8t" }, + {"WM_ICON_SIZE", XA_WM_ICON_SIZE, "32cccccc" }, + {"WM_NAME", XA_WM_NAME, "8t" }, + {"WM_PROTOCOLS", 0, "32a" }, + {"WM_SIZE_HINTS", XA_WM_SIZE_HINTS, "32mii" }, + {"WM_STATE", 0, "32cx" } +}; +#endif + +DECLARE_EVENT(EVENT_Change); +DECLARE_EVENT(EVENT_Window); + +/*BEGIN_METHOD_VOID(CDESKTOP_init) + + X11_init(); + +END_METHOD*/ + +BEGIN_METHOD(X11_FindWindow, GB_STRING title; GB_STRING klass; GB_STRING role) + + Window *windows; + Window win; + int count; + int i; + char *title = MISSING(title) ? NULL : STRING(title); + int ltitle = MISSING(title) ? 0 : LENGTH(title); + char *klass = MISSING(klass) ? NULL : STRING(klass); + int lklass = MISSING(klass) ? 0 : LENGTH(klass); + char *role = MISSING(role) ? NULL : STRING(role); + int lrole = MISSING(role) ? 0 : LENGTH(role); + char *prop; + int lprop; + GB_ARRAY result; + + if (X11_init()) + return; + + GB.Array.New(&result, GB_T_INTEGER, 0); + + X11_find_windows(&windows, &count); + + for (i = 0; i < count; i++) + { + win = windows[i]; + + if (ltitle) + { + X11_get_window_title(win, &prop, &lprop); + if (!GB.MatchString(title, ltitle, prop, lprop)) + continue; + } + + if (lklass) + { + X11_get_window_class(win, &prop, &lprop); + //qDebug("class = %.*s", lprop, prop); + if (!GB.MatchString(klass, lklass, prop, lprop)) + continue; + } + + if (lrole) + { + X11_get_window_role(win, &prop, &lprop); + //qDebug("role = %.*s", lprop, prop); + if (!GB.MatchString(role, lrole, prop, lprop)) + continue; + } + + *((int *)GB.Array.Add(result)) = (int)win; + } + + GB.ReturnObject(result); + +END_METHOD + + +BEGIN_METHOD(X11_SendKey, GB_STRING key; GB_BOOLEAN press) + + char *error; + + if (X11_init()) + return; + + error = X11_send_key(GB.ToZeroString(ARG(key)), VARG(press)); + if (error) GB.Error(error); + +END_METHOD + + +BEGIN_PROPERTY(X11_RootWindow) + + if (X11_init()) + return; + + GB.ReturnInteger(X11_root); + +END_PROPERTY + + +BEGIN_PROPERTY(X11_Time) + + intptr_t time; + + GB.Component.GetInfo("TIME", POINTER(&time)); + + GB.ReturnInteger((int)time); + +END_PROPERTY + + +BEGIN_METHOD(X11_GetWindowProperty, GB_INTEGER window; GB_STRING name) + + char *value; + Atom type; + Atom prop; + int format; + GB_ARRAY array; + int i, count; + char *name; + Window window; + GB_VARIANT_VALUE ret; + + if (X11_init()) + return; + + prop = X11_intern_atom(GB.ToZeroString(ARG(name)), FALSE); + if (prop == None) + { + GB.ReturnVariant(NULL); + return; + } + + window = VARG(window); + + value = X11_get_property(window, prop, &type, &format, &count); + if (!value) + { + GB.ReturnVariant(NULL); + return; + } + + if (type == XA_ATOM) + { + GB.Array.New(&array, GB_T_STRING, count); + for (i = 0; i < count; i++) + { + name = XGetAtomName(X11_display, *((Atom *)value + i)); + *(char **)GB.Array.Get(array, i) = GB.NewZeroString(name); + XFree(name); + } + + ret.type = GB_T_OBJECT; + ret.value._object = array; + GB.ReturnVariant(&ret); + } + else if (type == X11_UTF8_STRING && format == 8) + { + count = GB.StringLength(value); + GB.Array.New(&array, GB_T_STRING, 0); + while (count > 0) + { + for (i = 0; i < count; i++) + { + if (!value[i]) + break; + } + if (i) + *(char **)GB.Array.Add(array) = GB.NewString(value, i); + i++; + value += i; + count -= i; + } + ret.type = GB_T_OBJECT; + ret.value._object = array; + GB.ReturnVariant(&ret); + } + else + { + switch(format) + { + case 16: + count = GB.StringLength(value) / sizeof(int); + GB.Array.New(&array, GB_T_SHORT, count); + /*for (i = 0; i < count; i++) + *((short *)GB.Array.Get(array, i)) = *((short *)value + i);*/ + memcpy(GB.Array.Get(array, 0), value, sizeof(short) * count); + + ret.type = GB_T_OBJECT; + ret.value._object = array; + GB.ReturnVariant(&ret); + + break; + + case 32: // A "long", not necessarily 32 bits!! + count = GB.StringLength(value) / sizeof(long); + GB.Array.New(&array, GB_T_INTEGER, count); + for (i = 0; i < count; i++) + *((int *)GB.Array.Get(array, i)) = *((long *)value + i); + + ret.type = GB_T_OBJECT; + ret.value._object = array; + GB.ReturnVariant(&ret); + + break; + + default: + + ret.type = GB_T_STRING; + ret.value._string = value; + GB.ReturnVariant(&ret); + } + } + +END_METHOD + +BEGIN_METHOD(X11_SetWindowProperty, GB_INTEGER window; GB_STRING name; GB_STRING type; GB_VARIANT value) + + Atom type; + Atom prop; + int format; + int count; + void *data; + void *object; + Window window; + void *buffer = NULL; + #if OS_64BITS + long padded_value; + long *padded_data; + int i; + #endif + + if (X11_init()) + return; + + prop = X11_intern_atom(GB.ToZeroString(ARG(name)), TRUE); + type = X11_intern_atom(GB.ToZeroString(ARG(type)), TRUE); + count = 0; + format = 0; + + window = VARG(window); + + switch(VARG(value).type) + { + case GB_T_STRING: + case GB_T_CSTRING: + format = 8; + data = VARG(value).value._string; + count = GB.StringLength(data); + break; + + case GB_T_BOOLEAN: + case GB_T_BYTE: + case GB_T_SHORT: + case GB_T_INTEGER: + format = 32; + #if OS_64BITS + padded_value = VARG(value).value._integer; + data = &padded_value; + #else + data = &VARG(value).value._integer; + #endif + count = 1; + break; + + default: + if (VARG(value).type >= GB_T_OBJECT) + { + object = VARG(value).value._object; + if (GB.Is(object, GB.FindClass("Array"))) + { + data = GB.Array.Get((GB_ARRAY)object, 0); + count = GB.Array.Count((GB_ARRAY)object); + + switch((int)GB.Array.Type(object)) + { + case GB_T_BYTE: + format = 8; + break; + + case GB_T_SHORT: + format = 16; + break; + + case GB_T_INTEGER: + format = 32; + #if OS_64BITS + padded_data = (long *)alloca(sizeof(long) * count); + for (i = 0; i < count; i++) + padded_data[i] = *((int *)data + i); + data = padded_data; + #endif + break; + + case GB_T_STRING: + if (type == X11_UTF8_STRING) + { + int i, len; + char *p; + char **d = (char **)data; + + len = 0; + for (i = 0; i < count; i++) + len += GB.StringLength(d[i]) + 1; + + GB.Alloc(&buffer, len); + + format = 8; + data = buffer; + count = len; + + p = buffer; + for (i = 0; i < count; i++) + { + len = GB.StringLength(d[i]); + memcpy(p, d[i], len + 1); + p += len + 1; + } + + break; + } + + default: + goto __ERROR; + } + } + } + else + goto __ERROR; + + break; + } + + if (format) + X11_set_property(window, prop, type, format, data, count); + + if (buffer) + GB.Free(&buffer); + + return; + +__ERROR: + + GB.Error("Datatype not supported"); + +END_METHOD + +BEGIN_METHOD(X11_InternAtom, GB_STRING atom; GB_BOOLEAN create) + + if (X11_init()) + return; + + GB.ReturnInteger(X11_intern_atom(GB.ToZeroString(ARG(atom)), VARGOPT(create, FALSE))); + +END_METHOD + +BEGIN_METHOD(X11_GetAtomName, GB_INTEGER atom) + + char *name; + + if (X11_init()) + return; + + name = XGetAtomName(X11_display, VARG(atom)); + GB.ReturnNewZeroString(name); + XFree(name); + +END_METHOD + +BEGIN_METHOD(X11_SendClientMessage, GB_STRING message; GB_OBJECT data; GB_INTEGER window) + + GB_ARRAY array; + void *data = NULL; + int count = 0; + int format = 0; + #if OS_64BITS + long *padded_data; + int i; + #endif + + if (X11_init()) + return; + + if (!MISSING(data) && VARG(data)) + { + array = VARG(data); + data = GB.Array.Get(array, 0); + count = GB.Array.Count(array); + + switch((int)GB.Array.Type(array)) + { + case GB_T_BYTE: + format = 8; + break; + + case GB_T_SHORT: + format = 16; + break; + + case GB_T_INTEGER: + format = 32; + #if OS_64BITS + padded_data = (long *)alloca(sizeof(long) * count); + for (i = 0; i < count; i++) + padded_data[i] = *((int *)data + i); + data = padded_data; + #endif + break; + + /*case GB_T_STRING: + format = 32; + // TODO: convert strings to Atom + break;*/ + + default: + fprintf(stderr, "gb.desktop: unsupported array datatype for 'Data' argument"); + return; + } + } + + X11_send_client_message(X11_root, VARGOPT(window, X11_root), + X11_intern_atom(GB.ToZeroString(ARG(message)), TRUE), + data, format, count); + +END_METHOD + +BEGIN_PROPERTY(X11_EventFilter) + + if (X11_init()) + return; + + if (READ_PROPERTY) + GB.ReturnBoolean(X11_event_filter_enabled); + else + X11_enable_event_filter(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD(X11_WatchWindow, GB_INTEGER window; GB_BOOLEAN watch) + + XWindowAttributes attr; + int mask = PropertyChangeMask | StructureNotifyMask; + + if (X11_init()) + return; + + XGetWindowAttributes(X11_display, VARG(window), &attr); + + if (VPROP(GB_BOOLEAN)) + XSelectInput(X11_display, (Window)VARG(window), attr.your_event_mask | mask); + else + XSelectInput(X11_display, (Window)VARG(window), attr.your_event_mask & ~mask); + +END_METHOD + +BEGIN_METHOD(X11_GetWindowGeometry, GB_INTEGER window) + + GB_ARRAY array; + int *data; + + if (X11_init()) + return; + + GB.Array.New(&array, GB_T_INTEGER, 4); + data = (int *)GB.Array.Get(array, 0); + + X11_get_window_geometry(VARG(window), &data[0], &data[1], &data[2], &data[3]); + //XGetGeometry(X11_display, VARG(window), &root, &data[0], &data[1], (uint *)&data[2], (uint *)&data[3], &ignore, &ignore); + GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(X11_MakeIcon, GB_OBJECT data; GB_INTEGER width; GB_INTEGER height) + + GB_ARRAY array; + int *data; + int count; + int size; + int width = VARGOPT(width, -1); + int height = VARGOPT(height, width); + int w, h; + + array = VARG(data); + if (GB.CheckObject(array)) + return; + + data = (int *)GB.Array.Get(array, 0); + count = GB.Array.Count(array); + + if (width < 0) + { + for(;;) + { + if (count < 2) + break; + w = data[0]; + h = data[1]; + if (!w || !h) + break; + #if DEBUG_ICON + fprintf(stderr, "%d [%d %d]\n", count, w, h); + #endif + if (w > width) + { + width = w; + height = h; + } + size = w * h + 2; + data += size; + count -= size; + } + + data = (int *)GB.Array.Get(array, 0); + count = GB.Array.Count(array); + } + + for(;;) + { + if (count < 2) + { + GB.ReturnNull(); + return; + } + w = data[0]; + h = data[1]; + if (w == width && h == height) + break; + if (!w || !h) + GB.ReturnNull(); + size = w * h + 2; + data += size; + count -= size; + } + + GB.ReturnObject(IMAGE.Create(w, h, GB_IMAGE_BGRA, (unsigned char *)&data[2])); + +END_METHOD + +BEGIN_METHOD(X11_MinimizeWindow, GB_INTEGER window; GB_BOOLEAN minimized) + + if (X11_init()) + return; + + if (VARG(minimized)) + { + long state = IconicState; + + X11_send_client_message(X11_root, VARG(window), + X11_intern_atom("WM_CHANGE_STATE", TRUE), + (char *)&state, 32, 1); + } + else + { + XMapWindow(X11_display, VARG(window)); + } + +END_METHOD + + +BEGIN_METHOD_VOID(X11_Flush) + + if (X11_init()) + return; + + XFlush(X11_display); + +END_METHOD + + +BEGIN_METHOD_VOID(X11_Sync) + + if (X11_init()) + return; + + XSync(X11_display, FALSE); + +END_METHOD + + +BEGIN_METHOD(X11_MoveWindow, GB_INTEGER window; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + if (MISSING(w) || MISSING(h)) + XMoveWindow(X11_display, VARG(window), VARG(x), VARG(y)); + else + XMoveResizeWindow(X11_display, VARG(window), VARG(x), VARG(y), VARG(w), VARG(h)); + +END_METHOD + +BEGIN_METHOD(X11_ResizeWindow, GB_INTEGER window; GB_INTEGER w; GB_INTEGER h) + + XResizeWindow(X11_display, VARG(window), VARG(w), VARG(h)); + +END_METHOD + + + +GB_DESC X11Desc[] = +{ + GB_DECLARE("X11", 0), GB_VIRTUAL_CLASS(), + + //GB_STATIC_METHOD("Init", NULL, CDESKTOP_init, NULL), + + GB_CONSTANT("None", "i", None), + GB_CONSTANT("CurrentTime", "i", CurrentTime), + + GB_STATIC_METHOD("FindWindow", "Integer[]", X11_FindWindow, "[(Title)s(Application)s(Role)s]"), + GB_STATIC_METHOD("SendKey", NULL, X11_SendKey, "(Key)s(Press)b"), + GB_STATIC_PROPERTY_READ("RootWindow", "i", X11_RootWindow), + GB_STATIC_PROPERTY_READ("Time", "i", X11_Time), + GB_STATIC_METHOD("GetWindowProperty", "v", X11_GetWindowProperty, "(Window)i(Property)s"), + GB_STATIC_METHOD("SetWindowProperty", NULL, X11_SetWindowProperty, "(Window)i(Property)s(Type)s(Value)v"), + GB_STATIC_METHOD("InternAtom", "i", X11_InternAtom, "(Atom)s[(Create)b]"), + GB_STATIC_METHOD("GetAtomName", "s", X11_GetAtomName, "(Atom)i"), + GB_STATIC_METHOD("SendClientMessageToRootWindow", NULL, X11_SendClientMessage, "(Message)s[(Data)Array;(Window)i]"), + GB_STATIC_PROPERTY("EventFilter", "b", X11_EventFilter), + GB_STATIC_METHOD("WatchWindow", NULL, X11_WatchWindow, "(Window)i(Watch)b"), + GB_STATIC_METHOD("GetWindowGeometry", "Integer[]", X11_GetWindowGeometry, "(Window)i"), + GB_STATIC_METHOD("MakeIcon", "Image", X11_MakeIcon, "(Data)Array;[(Width)i(Height)h]"), + GB_STATIC_METHOD("MinimizeWindow", NULL, X11_MinimizeWindow, "(Window)i(Minimized)b"), + GB_STATIC_METHOD("Sync", NULL, X11_Sync, NULL), + GB_STATIC_METHOD("Flush", NULL, X11_Flush, NULL), + GB_STATIC_METHOD("MoveWindow", NULL, X11_MoveWindow, "(Window)i(X)i(Y)i[(Width)i(Height)i]"), + GB_STATIC_METHOD("ResizeWindow", NULL, X11_ResizeWindow, "(Window)i(Width)i(Height)i"), + + GB_END_DECLARE +}; + +//---- X11Watcher --------------------------------------------------------- + +static CX11WATCHER *_watcher_list = NULL; + +void WATCHER_event_filter(XEvent *e) +{ + CX11WATCHER *watcher; + + if (!_watcher_list) + return; + + if (e->type == PropertyNotify) + { + LIST_for_each(watcher, _watcher_list) + { + if (watcher->window && e->xany.window != watcher->window) + continue; + if (watcher->property && e->xproperty.atom != watcher->property) + continue; + + GB.Raise(watcher, EVENT_Change, 2, GB_T_INTEGER, e->xany.window, GB_T_INTEGER, e->xproperty.atom); + } + } + else if (e->type == ConfigureNotify) + { + LIST_for_each(watcher, _watcher_list) + { + if (watcher->window && e->xany.window != watcher->window) + continue; + + GB.Raise(watcher, EVENT_Window, + 5, GB_T_INTEGER, e->xany.window, + GB_T_INTEGER, e->xconfigure.x, + GB_T_INTEGER, e->xconfigure.y, + GB_T_INTEGER, e->xconfigure.width, + GB_T_INTEGER, e->xconfigure.height); + } + } +} + +BEGIN_METHOD(X11Watcher_new, GB_INTEGER window; GB_STRING property) + + if (X11_init()) + return; + + WATCHER->window = VARGOPT(window, 0); + WATCHER->property = MISSING(property) ? 0 : X11_intern_atom(GB.ToZeroString(ARG(property)), FALSE); + + if (!_watcher_list) + X11_enable_event_filter(TRUE); + + LIST_insert(&_watcher_list, WATCHER, &WATCHER->list); + +END_METHOD + +BEGIN_METHOD_VOID(X11Watcher_free) + + LIST_remove(&_watcher_list, WATCHER, &WATCHER->list); + if (!_watcher_list) + X11_enable_event_filter(FALSE); + +END_METHOD + +GB_DESC X11WatcherDesc[] = +{ + GB_DECLARE("X11Watcher", sizeof(CX11WATCHER)), + + GB_METHOD("_new", NULL, X11Watcher_new, "[(Window)i(Property)s]"), + GB_METHOD("_free", NULL, X11Watcher_free, NULL), + + GB_EVENT("Property", NULL, "(Window)i(Property)i", &EVENT_Change), + GB_EVENT("Configure", NULL, "(Window)i(X)i(Y)i(Width)i(Height)i", &EVENT_Window), + + GB_END_DECLARE +}; diff --git a/gb.desktop.x11/src/c_x11.h b/gb.desktop.x11/src/c_x11.h new file mode 100644 index 00000000..292f9098 --- /dev/null +++ b/gb.desktop.x11/src/c_x11.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + c_x11.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_X11_H +#define __C_X11_H + +#include "main.h" +#include "gb_list.h" + +#ifndef __C_X11_C +extern GB_DESC X11Desc[]; +extern GB_DESC X11WatcherDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + Atom property; + Window window; + LIST list; + } + CX11WATCHER; + +#define WATCHER ((CX11WATCHER *)_object) + +void WATCHER_event_filter(XEvent *); + +#endif diff --git a/gb.desktop.x11/src/c_x11systray.c b/gb.desktop.x11/src/c_x11systray.c new file mode 100644 index 00000000..dbb4ac51 --- /dev/null +++ b/gb.desktop.x11/src/c_x11systray.c @@ -0,0 +1,187 @@ +/*************************************************************************** + + c_x11systray.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_X11SYSTRAY_C + +#include "x11.h" +#include "systray/systray.h" +#include "c_x11systray.h" + +//--------------------------------------------------------------------------- + +static GB_FUNCTION _arrange_func; + +void SYSTRAY_raise_arrange(void) +{ + static bool init = FALSE; + + if (!init) + { + GB_CLASS startup = GB.Application.StartupClass(); + GB.GetFunction(&_arrange_func, (void *)startup, "X11Systray_Arrange", "", ""); + init = TRUE; + } + + GB.Call(&_arrange_func, 0, TRUE); +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(X11Systray_Show, GB_INTEGER window) + + X11_init(); + SYSTRAY_init(X11_display, VARG(window)); + +END_METHOD + +BEGIN_PROPERTY(X11Systray_Count) + + GB.ReturnInteger(SYSTRAY_get_count()); + +END_PROPERTY + +BEGIN_METHOD(X11Systray_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= SYSTRAY_get_count()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(SYSTRAY_get(index)); + +END_METHOD + +BEGIN_METHOD_VOID(X11Systray_Refresh) + + SYSTRAY_refresh(); + +END_METHOD + +//--------------------------------------------------------------------------- + +#define THIS_ICON ((CX11SYSTRAYICON *)_object) + +BEGIN_PROPERTY(X11SystrayIcon_X) + + GB.ReturnInteger(THIS_ICON->x); + +END_PROPERTY + +BEGIN_PROPERTY(X11SystrayIcon_Y) + + GB.ReturnInteger(THIS_ICON->y); + +END_PROPERTY + +BEGIN_PROPERTY(X11SystrayIcon_Width) + + GB.ReturnInteger(THIS_ICON->w); + +END_PROPERTY + +BEGIN_PROPERTY(X11SystrayIcon_Height) + + GB.ReturnInteger(THIS_ICON->h); + +END_PROPERTY + +BEGIN_PROPERTY(X11SystrayIcon_IconWidth) + + GB.ReturnInteger(THIS_ICON->iw); + +END_PROPERTY + +BEGIN_PROPERTY(X11SystrayIcon_IconHeight) + + GB.ReturnInteger(THIS_ICON->ih); + +END_PROPERTY + +BEGIN_METHOD(X11SystrayIcon_Move, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + THIS_ICON->x = VARG(x); + THIS_ICON->y = VARG(y), + THIS_ICON->is_updated = TRUE; + + if (!MISSING(w) && !MISSING(h)) + { + THIS_ICON->w = VARG(w); + THIS_ICON->h = VARG(h), + THIS_ICON->is_resized = TRUE; + } + +END_METHOD + +BEGIN_METHOD(X11SystrayIcon_Resize, GB_INTEGER w; GB_INTEGER h) + + THIS_ICON->w = VARG(w); + THIS_ICON->h = VARG(h), + THIS_ICON->is_updated = TRUE; + THIS_ICON->is_resized = TRUE; + +END_METHOD + +BEGIN_PROPERTY(X11SystrayIcon_Handle) + + GB.ReturnInteger((int)THIS_ICON->wid); + +END_PROPERTY + + +GB_DESC X11SystrayIconDesc[] = +{ + GB_DECLARE("X11SystrayIcon", sizeof(CX11SYSTRAYICON)), + GB_NOT_CREATABLE(), + + GB_PROPERTY_READ("X", "i", X11SystrayIcon_X), + GB_PROPERTY_READ("Y", "i", X11SystrayIcon_Y), + GB_PROPERTY_READ("W", "i", X11SystrayIcon_Width), + GB_PROPERTY_READ("Width", "i", X11SystrayIcon_Width), + GB_PROPERTY_READ("H", "i", X11SystrayIcon_Height), + GB_PROPERTY_READ("Height", "i", X11SystrayIcon_Height), + GB_PROPERTY_READ("IconW", "i", X11SystrayIcon_IconWidth), + GB_PROPERTY_READ("IconWidth", "i", X11SystrayIcon_IconWidth), + GB_PROPERTY_READ("IconH", "i", X11SystrayIcon_IconHeight), + GB_PROPERTY_READ("IconHeight", "i", X11SystrayIcon_IconHeight), + GB_PROPERTY_READ("Handle", "i", X11SystrayIcon_Handle), + + GB_METHOD("Move", NULL, X11SystrayIcon_Move, "(X)i(Y)i[(Width)i(Height)i]"), + GB_METHOD("Resize", NULL, X11SystrayIcon_Resize, "(Width)i(Height)i"), + + GB_END_DECLARE +}; + +GB_DESC X11SystrayDesc[] = +{ + GB_DECLARE_VIRTUAL("X11Systray"), + + GB_STATIC_METHOD("Show", NULL, X11Systray_Show, "(Window)i"), + GB_STATIC_PROPERTY_READ("Count", "i", X11Systray_Count), + GB_STATIC_METHOD("_get", "X11SystrayIcon", X11Systray_get, "(Index)i"), + GB_STATIC_METHOD("Refresh", NULL, X11Systray_Refresh, NULL), + + GB_END_DECLARE +}; diff --git a/gb.desktop.x11/src/c_x11systray.h b/gb.desktop.x11/src/c_x11systray.h new file mode 100644 index 00000000..f9d0ad26 --- /dev/null +++ b/gb.desktop.x11/src/c_x11systray.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + c_x11systray.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_X11SYSTRAY_H +#define __C_X11SYSTRAY_H + +#include "main.h" +#include "systray/systray.h" +#include "systray/icons.h" + +#ifndef __C_X11SYSTRAY_C +extern GB_DESC X11SystrayIconDesc[]; +extern GB_DESC X11SystrayDesc[]; +#endif + +void SYSTRAY_raise_arrange(void); + +#endif diff --git a/gb.desktop.x11/src/gb.desktop.x11.component b/gb.desktop.x11/src/gb.desktop.x11.component new file mode 100644 index 00000000..6ce47c38 --- /dev/null +++ b/gb.desktop.x11/src/gb.desktop.x11.component @@ -0,0 +1,4 @@ +[Component] +Author=Benoît Minisini +State=1 +Requires=gb.image diff --git a/gb.desktop.x11/src/gb_list.c b/gb.desktop.x11/src/gb_list.c new file mode 100644 index 00000000..34557b46 --- /dev/null +++ b/gb.desktop.x11/src/gb_list.c @@ -0,0 +1,24 @@ +/*************************************************************************** + + gb_list.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_list_temp.h" diff --git a/gb.desktop.x11/src/main.c b/gb.desktop.x11/src/main.c new file mode 100644 index 00000000..dce4ec35 --- /dev/null +++ b/gb.desktop.x11/src/main.c @@ -0,0 +1,53 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "x11.h" +#include "c_x11.h" +#include "c_x11systray.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + X11Desc, + X11WatcherDesc, + X11SystrayIconDesc, + X11SystrayDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + // Must be not be unloaded, because libxtst registers some exit procedure called at XCloseDisplay() + return -1; +} + +void EXPORT GB_EXIT(void) +{ + X11_exit(); +} diff --git a/gb.desktop.x11/src/main.h b/gb.desktop.x11/src/main.h new file mode 100644 index 00000000..6d667fb5 --- /dev/null +++ b/gb.desktop.x11/src/main.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb.image.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.desktop.x11/src/systray/common.h b/gb.desktop.x11/src/systray/common.h new file mode 100644 index 00000000..554ba363 --- /dev/null +++ b/gb.desktop.x11/src/systray/common.h @@ -0,0 +1,82 @@ +/* ------------------------------- +* vim:tabstop=4:shiftwidth=4 +* common.h +* Mon, 01 May 2006 01:45:08 +0700 +* ------------------------------- +* Common declarations +* -------------------------------*/ + +#ifndef _COMMON_H_ +#define _COMMON_H_ + +#include "debug.h" + +#define PROGNAME PACKAGE + +/* Default icon size */ +#define FALLBACK_ICON_SIZE 24 +/* Minimal icon size */ +#define MIN_ICON_SIZE 16 +/* Default KDE icon size */ +#define KDE_ICON_SIZE 22 +/* Number of time icon is allowed to change its size after which + * stalonetray gives up */ +#define ICON_SIZE_RESETS_THRESHOLD 2 + +/* Meaningful names for return values */ +#define SUCCESS 1 +#define FAILURE 0 + +/* Simple macro to return status and log it if necessary */ +#define RETURN_STATUS(rc) do { \ + LOG_TRACE(("status = %s\n", ((rc) == SUCCESS ? "SUCCESS" : "FAILURE"))); \ + return rc; \ +} while(0) + +/* Meaningful names for return values of icon mass-operations */ +#define MATCH 1 +#define NO_MATCH 0 + +/* Simple macro to return mass-op status and log it if necessary */ +#define RETURN_MATCH(rc) do { \ + LOG_TRACE(("status = %s\n", rc == MATCH : "MATCH" : "NO_MATCH")); \ + return rc; \ +} while(0) + +/* Meaningful names for return values of icon mass-operations */ +#define MATCH 1 +/* Portable macro for function name */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + #define __FUNC__ __func__ +#elif defined(__GNUC__) && __GNUC__ >= 3 + #define __FUNC__ __FUNCTION__ +#else + #define __FUNC__ "unknown" +#endif + +/* DIE */ +#define DIEDIE() exit(-1) +/* Print a message and... DIE */ +#define DIE(message) do { LOG_ERROR(message); DIEDIE(); } while(0) +/* Log OOM condition */ +#define LOG_ERR_IE(message) do { \ + LOG_ERROR(("Internal error, please report to maintaner (see AUTHORS)\n")); \ + LOG_ERROR(message); \ +} while(0); +/* DIE on internal error */ +#define DIE_IE(message) do { LOG_ERR_IE(message); DIEDIE(); } while(0) +/* Log OOM condition */ +#define LOG_ERR_OOM(message) do { \ + LOG_ERROR(("Out of memory\n")); \ + LOG_ERROR(message); \ +} while(0); +/* DIE on OOM error */ +#define DIE_OOM(message) do { LOG_ERR_OOM(message); DIEDIE(); } while(0) + +/*** WARNING: feed following macros only with side-effects-free expressions ***/ +/* Get a value within target interval */ +#define cutoff(tgt,min,max) (tgt) < (min) ? (min) : ((tgt) > (max) ? max : tgt) +/* Update value to fit into target interval */ +#define val_range(tgt,min,max) (tgt) = cutoff(tgt,min,max) + +#endif diff --git a/gb.desktop.x11/src/systray/debug.c b/gb.desktop.x11/src/systray/debug.c new file mode 100644 index 00000000..f50326b4 --- /dev/null +++ b/gb.desktop.x11/src/systray/debug.c @@ -0,0 +1,150 @@ +/* ------------------------------- +* vim:tabstop=4:shiftwidth=4 +* debug.c +* Mon, 01 May 2006 01:44:42 +0700 +* ------------------------------- +* Debugging code/utilities +* -------------------------------*/ + +#include "systray.h" +#include "debug.h" +#include "xutils.h" + +#include +#include +#include +#include + +#ifdef DBG_PRINT_PID +#include +#include +#endif + +int debug_output_disabled = 0; + +/* Disables all output from debugging macros */ +void debug_disable_output() +{ + debug_output_disabled = 1; +} + +/* Print the message to STDERR (varadic macros is not used in the sake of portability) */ +void print_message_to_stderr(const char *fmt,...) +{ + static char msg[PATH_MAX]; + va_list va; + va_start(va, fmt); + vsnprintf(msg, PATH_MAX, fmt, va); + va_end(va); + fputs(msg, stderr); +} + +#ifdef DEBUG + +int trace_mode = False; + +void print_trace_header(const char *funcname, const char *fname, const int line) +{ +#ifdef TRACE_PID + static pid_t pid = 0; +#endif +#ifdef TRACE_TIMESTAMP + { + static char timestr[PATH_MAX+1]; + time_t curtime = time(NULL); + struct tm * loctime = localtime(&curtime); + strftime(timestr, PATH_MAX, "%b %e %H:%M:%S", loctime); + fprintf(stderr, "%s ", timestr); + } +#endif +#ifdef TRACE_WM_NAME + fprintf(stderr, "%s ", settings.wnd_name); +#endif +#ifdef TRACE_PID + if (!pid) pid = getpid(); + fprintf(stderr, "(%d) ", pid); +#endif +#ifdef TRACE_DPY + if (tray_data.dpy != NULL) + fprintf(stderr, "(%s) ", DisplayString(tray_data.dpy)); + else + fprintf(stderr, "(dpy) "); +#endif +#ifdef TRACE_VERBOSE_LOCATION + fprintf(stderr, "(%s:%4d) ", fname, line); +#endif + fprintf(stderr, "%s(): ", funcname); +} +#endif + + +/* Print the summary of icon data */ +int print_icon_data(struct TrayIcon *ti) +{ +#ifdef DEBUG + XWindowAttributes xwa; +#endif + LOG_INFO (("wid = 0x%x\n", ti->wid)); + LOG_TRACE((" self = %p\n", ti)); + LOG_TRACE((" prev = %p\n", ti->prev)); + LOG_TRACE((" next = %p\n", ti->next)); + LOG_TRACE((" invalid = %d\n", ti->is_invalid)); + LOG_TRACE((" layed_out = %d\n", ti->is_layed_out)); + LOG_TRACE((" update_pos = %d\n", ti->is_updated)); + LOG_INFO ((" name = %s\n", x11_get_window_name(tray_data.dpy, ti->wid, ""))); + LOG_INFO ((" visible = %d\n", ti->is_visible)); + LOG_INFO ((" position (grid) = %dx%d+%d+%d\n", + ti->l.grd_rect.w, ti->l.grd_rect.h, + ti->l.grd_rect.x, ti->l.grd_rect.y)); + LOG_INFO ((" position (pixels) = %dx%d+%d+%d\n", + ti->l.icn_rect.w, ti->l.icn_rect.h, + ti->l.icn_rect.x, ti->l.icn_rect.y)); + LOG_INFO ((" wnd_sz = %dx%d\n", ti->l.wnd_sz.x, ti->l.wnd_sz.y)); + LOG_TRACE((" cmode = %d\n", ti->cmode)); + LOG_INFO ((" xembed = %d\n", ti->is_xembed_supported)); + if (ti->is_xembed_supported) { + LOG_TRACE((" xembed_accepts_focus = %d\n", ti->is_xembed_accepts_focus)); + LOG_TRACE((" xembed_last_timestamp = %d\n", ti->xembed_last_timestamp)); + LOG_TRACE((" xembed_last_msgid = %d\n", ti->xembed_last_msgid)); + } + LOG_INFO((" embedded = %d\n", ti->is_embedded)); +#ifdef DEBUG + if (ti->is_embedded) { + LOG_TRACE((" mid-parent = 0x%x\n", ti->mid_parent)); + if (!XGetWindowAttributes(tray_data.dpy, ti->mid_parent, &xwa)) { + LOG_TRACE((" ERR: XGetWindowAttributes() on mid-parent window faied\n")); + } else { + LOG_TRACE((" mid-parent wid = 0x%x\n", ti->mid_parent)); + LOG_TRACE((" mid-parent state = %s\n", xwa.map_state == IsUnmapped ? "Unmapped" : (xwa.map_state == IsUnviewable ? "Unviewable" : "Viewable"))); + LOG_TRACE((" mid-parent geometry = %dx%d+%d+%d\n", xwa.width, xwa.height, xwa.x, xwa.y)); + } + } + if (settings.log_level > LOG_LEVEL_INFO) { + Window real_parent, root, *children = NULL; + unsigned int nchildren = 0; + int rc; + rc = XQueryTree(tray_data.dpy, ti->wid, &root, &real_parent, &children, &nchildren); + if (rc && children != NULL && nchildren > 0) XFree(children); + if (!rc) { + LOG_TRACE((" ERR: XQueryTree() failed\n")); + } else { + LOG_TRACE((" parent wid from XQueryTree() = 0x%x\n", real_parent)); + } + } + if (!XGetWindowAttributes(tray_data.dpy, ti->wid, &xwa)) { + LOG_TRACE((" ERR: XGetWindowAttributes() on icon window failed\n")); + } else { + LOG_TRACE((" geometry from XGetWindowAttributes() = %dx%d+%d+%d\n", xwa.width, xwa.height, xwa.x, xwa.y)); + LOG_TRACE((" mapstate from XGetWindowAttributes() = %s\n", xwa.map_state == IsUnmapped ? "Unmapped" : (xwa.map_state == IsUnviewable ? "Unviewable" : "Viewable"))); + } +#endif + /* This resets x11 error state (which might have left from X11 calls above) */ + x11_ok(); + return NO_MATCH; +} + +void dump_icon_list() +{ + icon_list_forall(&print_icon_data); +} + diff --git a/gb.desktop.x11/src/systray/debug.h b/gb.desktop.x11/src/systray/debug.h new file mode 100644 index 00000000..5f3f21fc --- /dev/null +++ b/gb.desktop.x11/src/systray/debug.h @@ -0,0 +1,85 @@ +/* ------------------------------- +* vim:tabstop=4:shiftwidth=4 +* debug.h +* Tue, 24 Aug 2004 12:05:38 +0700 +* ------------------------------- +* Debugging code/utilities +* -------------------------------*/ + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#include +#include + +#include "systray.h" +#include "settings.h" +#include "tray.h" +#include "common.h" + +#include +#include + +#define LOG_LEVEL_ERR 0 +#define LOG_LEVEL_INFO 1 +#define LOG_LEVEL_TRACE 2 + +extern int debug_output_disabled; + +/* Disables all output from debugging macros */ +void debug_disable_output(); + +/* Print the message to STDERR (in the sake of portability, we do not use varadic macros) */ +void print_message_to_stderr(const char *fmt,...); + +#ifdef DEBUG +/* The following defines control what is printed in each logged line */ +/* Print window name */ +#define TRACE_WM_NAME +/* Print pid */ +#undef TRACE_PID +/* Print name of display */ +#undef TRACE_DPY +/* Print timestamp */ +#define TRACE_TIMESTAMP +/* Print name of a function, file and line which outputs the message */ +/* Othewise, only function name is printed */ +#undef TRACE_VERBOSE_LOCATION +/* Print the debug header as specified by defines below */ +void print_trace_header(const char *funcname, const char *fname, const int line); +#define PRINT_TRACE_HEADER() do { \ + print_trace_header(__FUNC__, __FILE__, __LINE__); \ +} while(0) +/* Print the debug message of specified level */ +#define LOG_TRACE(message) do { \ + if (!debug_output_disabled && settings.log_level >= LOG_LEVEL_TRACE) { \ + PRINT_TRACE_HEADER(); \ + print_message_to_stderr message; \ + }; \ +} while (0) +#else +#define PRINT_TRACE_HEADER() do {} while(0) +#define LOG_TRACE(message) do {} while(0) +#endif + +#define LOG_ERROR(message) do { \ + if (!debug_output_disabled) { \ + if (settings.log_level >= LOG_LEVEL_TRACE) PRINT_TRACE_HEADER(); \ + if (settings.log_level >= LOG_LEVEL_ERR) print_message_to_stderr message; \ + } \ +} while(0) + +#define LOG_INFO(message) do { \ + if (!debug_output_disabled) { \ + if (settings.log_level >= LOG_LEVEL_TRACE) PRINT_TRACE_HEADER(); \ + if (settings.log_level >= LOG_LEVEL_INFO) print_message_to_stderr message; \ + } \ +} while(0) + +/* Print the summary of icon data */ +int print_icon_data(struct TrayIcon *ti); +/* Print icon list contents */ +void dump_icon_list(); + +#endif + diff --git a/gb.desktop.x11/src/systray/embed.c b/gb.desktop.x11/src/systray/embed.c new file mode 100644 index 00000000..f52afa3c --- /dev/null +++ b/gb.desktop.x11/src/systray/embed.c @@ -0,0 +1,357 @@ +/* ------------------------------- +* vim:tabstop=4:shiftwidth=4 +* embed.c +* Fri, 03 Sep 2004 20:38:55 +0700 +* ------------------------------- +* embedding cycle implementation +* -------------------------------*/ + +#include "systray.h" + +#include +#include + +#include +#include + +#ifdef DELAY_EMBEDDING_CONFIRMATION +#include +#endif + +#include "embed.h" + +#include "common.h" +#include "tray.h" +#include "debug.h" +#include "icons.h" +#include "kde_tray.h" +#include "settings.h" + +#include "xembed.h" +#include "xutils.h" + +#define CALC_INNER_POS(x_, y_, ti_) do { \ + x_ = (ti_->l.icn_rect.w - ti_->l.wnd_sz.x) / 2; \ + y_ = (ti_->l.icn_rect.h - ti_->l.wnd_sz.y) / 2; \ +} while (0); + +#ifdef DELAY_EMBEDDING_CONFIRMATION +void *send_delayed_confirmation(void *dummy) +{ + struct TrayIcon* ti = (struct TrayIcon *) dummy; + Display *dpy; + + if ((dpy = XOpenDisplay(settings.display_str)) != NULL) { + LOG_TRACE(("will now sleep for %d seconds\n", settings.confirmation_delay)); + sleep(settings.confirmation_delay); + LOG_TRACE(("sending embedding confirmation\n")); + x11_send_client_msg32(dpy, + tray_data.tray, + tray_data.tray, + tray_data.xa_tray_opcode, + 0, + STALONE_TRAY_DOCK_CONFIRMED, + ti->wid, 0, 0); + XSync(dpy, False); + XClose(dpy); + } else { + DIE_IE(("failed to initialize display\n")); + } + pthread_exit(NULL); +} +#endif + +int embedder_embed(struct TrayIcon *ti) +{ + int rc; + + //XSetWindowAttributes xswa; + + /* If the icon is being embedded as hidden, + * we just start listening for property changes + * to track _XEMBED mapped state */ + if (!ti->is_visible) { + XSelectInput(tray_data.dpy, ti->wid, PropertyChangeMask); + return x11_ok(); + } + + /* 0. Start listening for events on icon window */ + XSelectInput(tray_data.dpy, ti->wid, StructureNotifyMask | PropertyChangeMask); + if (!x11_ok()) RETURN_STATUS(FAILURE); + + /* 1. Calculate position of mid-parent window */ + //CALC_INNER_POS(x, y, ti); + //LOG_TRACE(("position of icon 0x%x inside the tray: (%d, %d)\n", ti->wid, x, y)); + + /* 2. Create mid-parent window */ + + /*ti->mid_parent = XCreateSimpleWindow(tray_data.dpy, tray_data.tray, + ti->l.icn_rect.x + x, ti->l.icn_rect.y + y, + ti->l.wnd_sz.x, ti->l.wnd_sz.y, 0, 0, 0);*/ + + /* 2.5. Setup mid-parent window properties */ + //xswa.win_gravity = settings.bit_gravity; + //XChangeWindowAttributes(tray_data.dpy, ti->mid_parent, CWWinGravity, &xswa); + + //XSetWindowBackgroundPixmap(tray_data.dpy, ti->mid_parent, ParentRelative); + + //if (!x11_ok() || ti->mid_parent == None) RETURN_STATUS(FAILURE); + if (!x11_ok()) + RETURN_STATUS(FAILURE); + + //LOG_TRACE(("created mid-parent window 0x%x\n", ti->mid_parent)); + /* 3. Embed window into mid-parent */ + switch (ti->cmode) { + case CM_KDE: + case CM_FDO: + XReparentWindow(tray_data.dpy, ti->wid, tray_data.tray, 0, 0); + XMapRaised(tray_data.dpy, ti->wid); + break; + default: + break; + } + + XSetWindowBackgroundPixmap(tray_data.dpy, ti->wid, ParentRelative); + + /* 4. Show mid-parent */ + //XMapWindow(tray_data.dpy, ti->mid_parent); + /* mid-parent must be lowered so that it does not osbcure + * scollbar windows */ + //XLowerWindow(tray_data.dpy, ti->mid_parent); + if (!x11_ok()) RETURN_STATUS(FAILURE); +#ifndef DELAY_EMBEDDING_CONFIRMATION + /* 5. Send message confirming embedding */ + rc = x11_send_client_msg32(tray_data.dpy, + tray_data.tray, + tray_data.tray, + tray_data.xa_tray_opcode, + 0, + STALONE_TRAY_DOCK_CONFIRMED, + ti->wid, 0, 0); + RETURN_STATUS(rc != 0); +#else + /* This is here for debugging purposes */ + { + pthread_t delayed_thread; + pthread_create(&delayed_thread, NULL, send_delayed_confirmation, (void *) ti); + LOG_TRACE(("sent delayed confirmation\n")); + RETURN_STATUS(SUCCESS); + } +#endif +} + +int embedder_unembed(struct TrayIcon *ti) +{ + if (!ti->is_embedded) return SUCCESS; + switch (ti->cmode) { + case CM_KDE: + case CM_FDO: + /* Unembed icon as described in system tray protocol */ + if (ti->is_embedded && !ti->is_destroyed) { + XSelectInput(tray_data.dpy, ti->wid, NoEventMask); + XUnmapWindow(tray_data.dpy, ti->wid); + XReparentWindow(tray_data.dpy, ti->wid, DefaultRootWindow(tray_data.dpy), + ti->l.icn_rect.x, ti->l.icn_rect.y); + XMapRaised(tray_data.dpy, ti->wid); + if (!x11_ok()) LOG_ERROR(("failed to move icon 0x%x out of the tray\n")); + } + /* Destroy mid-parent */ + /*if (ti->mid_parent != None) { + XDestroyWindow(tray_data.dpy, ti->mid_parent); + if (!x11_ok()) LOG_ERROR(("failed to destroy icon mid-parent 0x%x\n", ti->mid_parent)); + }*/ + break; + default: + LOG_ERR_IE(("Error: the compatibility mode %d is not supported (should not happen)\n", ti->cmode)); + return FAILURE; + } + LOG_TRACE(("done unembedding 0x%x\n", ti->wid)); + RETURN_STATUS(x11_ok() == 0); /* This resets error status for the generations to come (XXX) */ +} + +int embedder_hide(struct TrayIcon *ti) +{ + //XUnmapWindow(tray_data.dpy, ti->mid_parent); + XUnmapWindow(tray_data.dpy, ti->wid); + /* We do not wany any StructureNotify events for icon window anymore */ + XSelectInput(tray_data.dpy, ti->wid, PropertyChangeMask); + if (!x11_ok()) { + ti->is_invalid = True; + return FAILURE; + } else { + ti->is_size_set = False; + ti->num_size_resets = 0; + ti->is_visible = False; + return SUCCESS; + } +} + +static void move_icon(struct TrayIcon *ti) +{ + //XMoveResizeWindow(tray_data.dpy, ti->mid_parent, ti->x, ti->y, ti->w, ti->h); + //XMoveWindow(tray_data.dpy, ti->wid, 0, 0); + XMoveResizeWindow(tray_data.dpy, ti->wid, ti->x, ti->y, ti->w, ti->h); +} + +int embedder_show(struct TrayIcon *ti) +{ + //unsigned int x, y; + /* If the window has never been embedded, + * perform real embedding */ + //if (ti->mid_parent == None) { + if (!ti->is_embedded) { + ti->is_visible = True; + return embedder_embed(ti); + } +#if 0 + /* 0. calculate new position for mid-parent */ + CALC_INNER_POS(x, y, ti); + /* 1. move mid-parent to new location */ + XMoveResizeWindow(tray_data.dpy, ti->mid_parent, + ti->l.icn_rect.x + x, ti->l.icn_rect.y + y, + ti->l.wnd_sz.x, ti->l.wnd_sz.y); + /* 2. adjust icon position inside mid-parent */ + XMoveWindow(tray_data.dpy, ti->wid, 0, 0); +#endif + move_icon(ti); + /* 3. map icon ? */ + XMapRaised(tray_data.dpy, ti->wid); + /* 4. map mid-parent */ + //XMapWindow(tray_data.dpy, ti->mid_parent); + XSelectInput(tray_data.dpy, ti->wid, StructureNotifyMask | PropertyChangeMask); + if (!x11_ok()) { + ti->is_invalid = True; + return FAILURE; + } else { + ti->is_visible = True; + return SUCCESS; + } +} + +static int update_forced = False; + +static int embedder_update_window_position(struct TrayIcon *ti) +{ + //int x, y; + /* Ignore hidden icons */ + if (!ti->is_visible) + return NO_MATCH; + /* Update only those icons that do want it (everyone if update was forced) */ + if (!update_forced && !ti->is_updated && !ti->is_resized && ti->is_embedded) + return NO_MATCH; + LOG_TRACE(("Updating position of icon 0x%x\n", ti->wid)); + /* Recalculate icon position */ + //CALC_INNER_POS(x, y, ti); + /* Reset the flags */ + ti->is_resized = False; + ti->is_updated = False; +#if 0 + /* Move mid-parent window */ + XMoveResizeWindow(tray_data.dpy, ti->mid_parent, + ti->l.icn_rect.x + x, ti->l.icn_rect.y + y, + ti->l.wnd_sz.x, ti->l.wnd_sz.y); + /* Sanitize icon position inside mid-parent */ + XMoveWindow(tray_data.dpy, ti->wid, 0, 0); +#endif + move_icon(ti); + /* Refresh the icon */ + embedder_refresh(ti); + if (!x11_ok()) { + LOG_TRACE(("failed to update position of icon 0x%x\n", ti->wid)); + ti->is_invalid = True; + } + return NO_MATCH; +} + +int embedder_update_positions(int forced) +{ + SYSTRAY_raise_arrange(); + /* I wish C had closures =( */ + update_forced = forced; + icon_list_forall(&embedder_update_window_position); + return SUCCESS; +} + +int embedder_refresh(struct TrayIcon *ti) +{ + if (!ti->is_visible) return NO_MATCH; + //XClearWindow(tray_data.dpy, ti->mid_parent); + x11_refresh_window(tray_data.dpy, ti->wid, ti->l.wnd_sz.x, ti->l.wnd_sz.y, True); + /* Check if the icon has survived all these manipulations */ + if (!x11_ok()) { + LOG_TRACE(("could not refresh 0x%x\n", ti->wid)); + ti->is_invalid = True; + } + return NO_MATCH; +} + +/* This function defines initial icon size or + * is used to reset size of the icon window */ +int embedder_reset_size(struct TrayIcon *ti) +{ + int rc = FAILURE; + int iw, ih; + /* Do not reset size for non-KDE icons with size set if icon_resizes + * are handled */ + if (ti->is_size_set && (ti->cmode != CM_KDE) && !(settings.kludge_flags & KLUDGE_FORCE_ICONS_SIZE)) + return SUCCESS; + /* Increase counter of size resets for given icon. If this number + * exeeds the threshold, do nothing. This should work around the icons + * that react badly to size changes */ + if (ti->is_size_set) ti->num_size_resets++; + if (ti->num_size_resets > ICON_SIZE_RESETS_THRESHOLD) return SUCCESS; + + iw = ih = settings.icon_size; + + if (ti->cmode == CM_KDE) + { + if (iw > KDE_ICON_SIZE) + iw = KDE_ICON_SIZE; + ih = iw; + } + else + { + /* If icon hints are to be respected, retrive the data */ + if (settings.kludge_flags & KLUDGE_USE_ICONS_HINTS) + { + rc = x11_get_window_min_size(tray_data.dpy, ti->wid, &iw, &ih); + if (rc == SUCCESS) + { + if (iw < 4 || ih < 4) + iw = ih = settings.icon_size; + + //fprintf(stderr, "embedder_reset_size: hint size: %ld %dx%d\n", ti->wid, iw, ih); + } + } +#if 0 + /* If this has failed, or icon hinst are not respected, or minimal size hints + * are too small, fall back to default values */ + if (!rc || + !(settings.kludge_flags & KLUDGE_USE_ICONS_HINTS) || + (settings.kludge_flags & KLUDGE_FORCE_ICONS_SIZE) || + (icon_sz.x < settings.icon_size && icon_sz.y < settings.icon_size)) + { + icon_sz.x = settings.icon_size; + icon_sz.y = settings.icon_size; + } +#endif + } + LOG_TRACE(("proposed icon size: %dx%d\n", iw, ih)); + //fprintf(stderr, "proposed icon size: %dx%d\n", iw, ih); + if (x11_set_window_size(tray_data.dpy, ti->wid, iw, ih)) + { + ti->l.wnd_sz.x = iw; + ti->l.wnd_sz.y = ih; + ti->iw = iw; + ti->ih = ih; + ti->w = iw; + ti->h = ih; + ti->is_size_set = True; + return SUCCESS; + } + else + { + ti->is_invalid = True; + return FAILURE; + } +} diff --git a/gb.desktop.x11/src/systray/embed.h b/gb.desktop.x11/src/systray/embed.h new file mode 100644 index 00000000..71d81333 --- /dev/null +++ b/gb.desktop.x11/src/systray/embed.h @@ -0,0 +1,43 @@ +/* ------------------------------- +* vim:tabstop=4:shiftwidth=4 +* embed.h +* Fri, 03 Sep 2004 20:38:55 +0700 +* ------------------------------- +* embedding cycle implementation +* -------------------------------*/ + +#ifndef _EMBED_H_ + +#include "icons.h" + +/* Constants for compatibility modes */ +/* KDE */ +#define CM_KDE 1 +/* Generic, freedesktop.org */ +#define CM_FDO 2 + +/* Embed an icon */ +int embedder_embed(struct TrayIcon *ti); + +/* Unembed an icon */ +int embedder_unembed(struct TrayIcon *ti); + +/* If (forced) + * recalculate and update positions of all icons; + * else + * recalculate and update positions of all icons that have requested an update; */ +int embedder_update_positions(int force); + +/* Show the icon */ +int embedder_show(struct TrayIcon *ti); + +/* Hide the icon */ +int embedder_hide(struct TrayIcon *ti); + +/* Refresh icon and its parent */ +int embedder_refresh(struct TrayIcon *ti); + +/* (Re)set icon size according to the policy */ +int embedder_reset_size(struct TrayIcon *ti); + +#endif diff --git a/gb.desktop.x11/src/systray/icons.c b/gb.desktop.x11/src/systray/icons.c new file mode 100644 index 00000000..7100206c --- /dev/null +++ b/gb.desktop.x11/src/systray/icons.c @@ -0,0 +1,242 @@ +/* ------------------------------- +* vim:tabstop=4:shiftwidth=4 +* icons.c +* Tue, 24 Aug 2004 12:05:38 +0700 +* ------------------------------- +* Manipulations with reparented +* windows --- tray icons +* -------------------------------*/ + +#include +#include + +#include "icons.h" + +#include "common.h" +#include "debug.h" +//#include "layout.h" +#include "list.h" +#include "tray.h" + +#ifdef DEBUG +#include "xutils.h" +#endif + +#include "assert.h" + +struct TrayIcon *icons_head = NULL; + +struct TrayIcon *icon_list_new(Window wid, int cmode) +{ + struct TrayIcon *new_icon; + /* Do not allocate second structure for the same window */ + if (icon_list_find(wid) != NULL) + return NULL; + + new_icon = GB.New(GB.FindClass("X11SystrayIcon"), NULL, NULL); + GB.Ref(new_icon); + + /*if ((new_icon = malloc(sizeof(struct TrayIcon))) == NULL) { + LOG_ERR_OOM(("Could not allocate memory for new icon\n")); + return NULL; + }*/ + + new_icon->wid = wid; + new_icon->l.wnd_sz.x = 0; + new_icon->l.wnd_sz.y = 0; + //new_icon->mid_parent = None; + new_icon->cmode = cmode; + new_icon->is_embedded = False; + new_icon->is_layed_out = False; + new_icon->is_updated = False; + new_icon->is_resized = True; + new_icon->is_visible = False; + new_icon->is_invalid = False; + new_icon->is_xembed_supported = False; + new_icon->is_size_set = False; + new_icon->num_size_resets = 0; + LIST_ADD_ITEM(icons_head, new_icon); + return new_icon; +} + +int icon_list_free(struct TrayIcon *ti) +{ + if (ti != NULL) { + LIST_DEL_ITEM(icons_head, ti); + ti->is_invalid = TRUE; + GB.Unref(POINTER(&ti)); + } + return SUCCESS; +} + +struct TrayIcon *icon_list_next(struct TrayIcon *ti) +{ + return (ti != NULL && ti->next != NULL) ? ti->next : icons_head; +} + +struct TrayIcon *icon_list_prev(struct TrayIcon *ti) +{ + struct TrayIcon *tmp; + if (ti != NULL && ti->prev != NULL) + return ti->prev; + else { + tmp = icons_head; + for (; tmp->next != NULL; tmp = tmp->next); + return tmp; + } +} + +static struct TrayIcon *backup_head = NULL; + +int icon_list_backup() +{ + struct TrayIcon *tmp, *cur, *cur2; + /* Refuse to perform second backup in a row */ + if (backup_head != NULL) { + DIE_IE(("Only one backup of icon list at a time is supported\n")); + } + /* For each icon in the list we allocate new temporary structure and add it + * to the end of temporary list backup_head */ + for (cur = icons_head, cur2 = NULL; cur != NULL; cur = cur->next, cur2 = tmp) { + tmp = (struct TrayIcon *) malloc(sizeof(struct TrayIcon)); + if (tmp == NULL) { + LOG_ERR_OOM(("Could not allocate backup list")); + icon_list_backup_purge(); + return FAILURE; + } + memcpy(tmp, cur, sizeof(struct TrayIcon)); + LIST_INSERT_AFTER(backup_head, cur2, tmp); + cur2 = tmp; + } + return SUCCESS; +} + +int icon_list_restore() +{ + struct TrayIcon *cur_b, *cur_i, *prev_sv, *next_sv; + LOG_TRACE(("restoring the icon list from the backup\n")); + /* Restore the list by copying raw data from + * backup list. This assumes that sequences have the + * same length. */ + for (cur_i = icons_head, cur_b = backup_head; + cur_i != NULL && cur_b != NULL; + cur_i = cur_i->next, cur_b = cur_b->next) + { + prev_sv = cur_i->prev; next_sv = cur_i->next; + memcpy(cur_i, cur_b, sizeof(struct TrayIcon)); + cur_i->prev = prev_sv; cur_i->next = next_sv; + } + /* Some consistency checking: ensures that + * both lists had the same length */ + assert(cur_i == NULL && cur_b == NULL); + /* Clean backup list */ + LIST_CLEAN(backup_head, cur_b); + backup_head = NULL; + return SUCCESS; +} + +int icon_list_backup_purge() +{ + struct TrayIcon *tmp; + LOG_TRACE(("purging the backed up icon list\n")); + /* Clean backup list */ + LIST_CLEAN(backup_head, tmp); + backup_head = NULL; + return SUCCESS; +} + +struct TrayIcon *icon_list_find(Window wid) +{ + /* Traverse the whole list */ + struct TrayIcon *tmp; + for (tmp = icons_head; tmp != NULL; tmp = tmp->next) + if (tmp->wid == wid) + return tmp; + return NULL; +} + +struct TrayIcon *icon_list_find_ex(Window wid) +{ + /* Traverse the whole list */ + struct TrayIcon *tmp; + for (tmp = icons_head; tmp != NULL; tmp = tmp->next) + if (tmp->wid == wid) // || tmp->mid_parent == wid) + return tmp; + return NULL; +} + +void icon_list_clean(IconCallbackFunc cbk) +{ + while (icons_head) + { + (*cbk)(icons_head); + icon_list_free(icons_head); + } +} + +/* TODO: is it necessary always to sort the full list? */ +void icon_list_sort(IconCmpFunc cmp) +{ + struct TrayIcon *new_head = NULL, *cur, *tmp; + while (icons_head != NULL) { + /* Find the least element and move it to temporary list */ + cur = icons_head; + for (tmp = icons_head; tmp != NULL; tmp = tmp->next) + if (cmp(tmp, cur) > 0) + cur = tmp; + LIST_DEL_ITEM(icons_head, cur); + LIST_ADD_ITEM(new_head, cur); + } + icons_head = new_head; +} + +struct TrayIcon *icon_list_forall(IconCallbackFunc cbk) +{ + return icon_list_forall_from(icons_head, cbk); +} + +struct TrayIcon *icon_list_forall_from(struct TrayIcon *tgt, IconCallbackFunc cbk) +{ + /* Traverse the list starting from tgt*/ + struct TrayIcon *tmp; + for (tmp = tgt != NULL ? tgt : icons_head; tmp != NULL; tmp = tmp->next) + if (cbk(tmp) == MATCH) { + return tmp; + } + return NULL; +} + +int icon_get_count(void) +{ + struct TrayIcon *icon; + int n = 0; + + for (icon = icons_head; icon; icon = icon->next) + { + if (icon->is_visible && icon->w > 0 && icon->h > 0) + n++; + } + + return n; +} + +struct TrayIcon *icon_get(int i) +{ + struct TrayIcon *icon = NULL; + + if (i >= 0 && i < icon_get_count()) + { + i = icon_get_count() - i - 1; + for (icon = icons_head; icon; icon = icon->next) + { + if (icon->is_visible && icon->w > 0 && icon->h > 0) + { + if (i == 0) + break; + i--; + } + } + } + + return icon; +} diff --git a/gb.desktop.x11/src/systray/icons.h b/gb.desktop.x11/src/systray/icons.h new file mode 100644 index 00000000..91818c57 --- /dev/null +++ b/gb.desktop.x11/src/systray/icons.h @@ -0,0 +1,117 @@ +/* ------------------------------- + * vim:tabstop=4:shiftwidth=4 + * icons.h + * Tue, 24 Aug 2004 12:05:38 +0700 + * ------------------------------- + * Manipulations with the list of + * tray icons + * -------------------------------*/ + +#ifndef _ICONS_H_ +#define _ICONS_H_ + +#include "systray.h" +#include +#include + +/* Simple point & rect data structures */ +struct Point { int x, y; }; +struct Rect { int x, y, w, h; }; + +/* Tray icon layout data structure */ +struct Layout { + struct Rect grd_rect; /* The rect in the grid */ + struct Rect icn_rect; /* Real position inside the tray */ + struct Point wnd_sz; /* Size of the window of the icon */ +}; + +/* Tray icon data structure */ +struct TrayIcon { + GB_BASE ob; + struct TrayIcon *next; + struct TrayIcon *prev; + Window wid; /* Window ID */ + //Window mid_parent; /* Mid-parent ID */ + int x, y, w, h; + int iw, ih; + int cmode; /* Compatibility mode: CM_FDO/CM_KDE (see embed.h) */ + int num_size_resets; /* How many times size was reset */ + unsigned long xembed_data[2];/* XEMBED data */ + long xembed_last_timestamp; /* The timestamp of last processed xembed message */ + long xembed_last_msgid; /* ID of the last processed xembed message */ + struct Layout l; /* Layout info */ + unsigned is_embedded : 1; /* Flag: is the icon succesfully embedded ? */ + unsigned is_invalid : 1; /* Flag: is the icon invalid ? */ + unsigned is_visible : 1; /* Flag: is the icon hidden ? */ + unsigned is_resized : 1; /* Flag: the icon has recently resized itself */ + unsigned is_layed_out : 1; /* Flag: the icon is succesfully layed out */ + unsigned is_updated : 1; /* Flag: the position of the icon needs to be updated */ + unsigned is_xembed_supported : 1; /* Flag: does the icon support xembed */ + unsigned is_size_set : 1; /* Flag: has the size for the icon been set */ + unsigned is_xembed_accepts_focus : 1;/* Flag: does the icon want focus */ + unsigned is_destroyed : 1; /* If a DestroyNotify has been received */ + unsigned invalid : 1; +}; + +/* Typedef for comparison function */ +typedef int (*IconCmpFunc) (struct TrayIcon *, struct TrayIcon *); + +/* Typedef for callback function */ +typedef int (*IconCallbackFunc) (struct TrayIcon *); + +/* Add the new icon to the list */ +struct TrayIcon *icon_list_new(Window w, int cmode); + +/* Delete the icon from the list */ +int icon_list_free(struct TrayIcon *ti); + +/* Return the next/previous icon in the list after the icon specified by ti */ +struct TrayIcon *icon_list_next(struct TrayIcon *ti); +struct TrayIcon *icon_list_prev(struct TrayIcon *ti); + +/************************************************* + * BIG FAT WARNING: backup/restore routines will + * memleak/fail if the number of icons in the + * list has changed between backup/restore calls. + * (in return, it does not invalidate pointers :P) + *************************************************/ + +/* Back up the list */ +int icon_list_backup(); + +/* Restore the list from the backup */ +int icon_list_restore(); + +/* Free the back-up list */ +int icon_list_backup_purge(); + +/* Apply a callback specified by cbk to all icons. + * List is traversed in a natural order. Function stops + * and returns current_icon if cbk(current_icon) == MATCH */ +struct TrayIcon *icon_list_forall(IconCallbackFunc cbk); +/* For readability sake, we sometimes use this function */ +#define icon_list_advanced_find icon_list_forall + +/* Same as above, but start traversal from the icon specified by tgt */ +struct TrayIcon *icon_list_forall_from(struct TrayIcon *tgt, IconCallbackFunc cbk); + +/* Clear the whole list, calling cbk for each icon */ +void icon_list_clean(IconCallbackFunc cbk); + +/* Sort the list using comparison function specified by cmp. + * Memo for writing comparison functions: + * if a < b => cmp(a,b) < 0 + * if a = b => cmp(a,b) = 0 + * if a > b => cmp(a,b) > 0 */ +void icon_list_sort(IconCmpFunc cmp); + +/* Find the icon with wid == w */ +struct TrayIcon *icon_list_find(Window w); + +/* Find the icon with wid == w or parent wid == w */ +struct TrayIcon *icon_list_find_ex(Window w); + +int icon_get_count(void); +struct TrayIcon *icon_get(int i); + +#endif diff --git a/gb.desktop.x11/src/systray/kde_tray.c b/gb.desktop.x11/src/systray/kde_tray.c new file mode 100644 index 00000000..25c6b785 --- /dev/null +++ b/gb.desktop.x11/src/systray/kde_tray.c @@ -0,0 +1,161 @@ +/* ------------------------------- +* vim:tabstop=4:shiftwidth=4 +* kde_tray.c +* Sun, 19 Sep 2004 12:31:10 +0700 +* ------------------------------- +* kde tray related routines +* -------------------------------*/ + +#include +#include +#include + +#include "debug.h" +#include "xutils.h" +#include "kde_tray.h" + +/* This list holds "old" KDE icons, e.g. the icons that are (likely) to be + * already embedded into some system tray and, therefore, are to be ignored. The + * list is empty initially */ +Window *old_kde_icons = NULL; +unsigned long n_old_kde_icons = -1; + +int kde_tray_update_fallback_mode(Display *dpy) +{ + /* Get the contents of KDE_NET_SYSTEM_TRAY_WINDOWS root window property. + * All windows that are listed there are considered to be "old" KDE icons, + * i.e. icons that are to be ignored on the tray startup. + * If the property does not exist, fall back to old mode */ + if (tray_data.xa_kde_net_system_tray_windows == None || + !x11_get_root_winlist_prop(dpy, tray_data.xa_kde_net_system_tray_windows, + (unsigned char **) &old_kde_icons, &n_old_kde_icons)) + { + LOG_INFO(("WM does not support KDE_NET_SYSTEM_TRAY_WINDOWS, will use legacy scheme\n")); + x11_extend_root_event_mask(tray_data.dpy, SubstructureNotifyMask); + tray_data.kde_tray_old_mode = 1; + } else { + tray_data.kde_tray_old_mode = 0; + } + return tray_data.kde_tray_old_mode; +} + +void kde_tray_init(Display *dpy) +{ + unsigned long n_client_windows, i; + Window *client_windows; + Atom xa_net_client_list; + if (!kde_tray_update_fallback_mode(dpy)) return; + /* n_old_kde_icons == -1 iff this is the first time this function is called + * with fallback mode disabled */ + if (n_old_kde_icons != -1) return; + /* 1. If theres no previous tray selection owner, try to embed all available + * KDE icons and, therefore, leave the list of old KDE icons empty */ + if (tray_data.old_selection_owner == None) { + n_old_kde_icons = 0; + return; + } + /* 2.Next, we are going to remove some entries from old_kde_icons list */ + /* 2.a. First, we remove all icons that are listed in _NET_CLIENT_LIST property, + * since this means that they are not embedded in any kind of tray */ + xa_net_client_list = XInternAtom(dpy, "_NET_CLIENT_LIST", True); + if (x11_get_root_winlist_prop(dpy, xa_net_client_list, + (unsigned char **) &client_windows, &n_client_windows)) + { + for (i = 0; i < n_client_windows; i++) + kde_tray_old_icons_remove(client_windows[i]); + } + /* 2.b. Second, we remove all windows that have root window as their parent, + * since this also means that they are not embedded in any kind of tray */ + for (i = 0; i < n_old_kde_icons; i++) { + Window root, parent, *children; + unsigned int nchildren; + int rc; + nchildren = 0; children = NULL; + if ((rc = XQueryTree(dpy, old_kde_icons[i], &root, &parent, &children, &nchildren))) { + if (root == parent) old_kde_icons[i] = None; + if (nchildren > 0) XFree(children); + } + if (!x11_ok() || !rc) old_kde_icons[i] = None; + } +#ifdef DEBUG + /* Some diagnostic output */ + for (i = 0; i < n_old_kde_icons; i++) + if (old_kde_icons[i] != None) + LOG_TRACE(("0x%x is marked as an old KDE icon\n", old_kde_icons[i])); +#endif +} + +int kde_tray_update_old_icons(Display *dpy) +{ + int i, rc; + XWindowAttributes xwa; + /* Remove dead entries from old kde icons list. + * We use XGetWindowAttributes to see if the + * window is still alive */ + for (i = 0; i < n_old_kde_icons; i++) { + rc = XGetWindowAttributes(dpy, old_kde_icons[i], &xwa); + if (!x11_ok() || !rc) + old_kde_icons[i] = None; + } + return SUCCESS; +} + +int kde_tray_is_old_icon(Window w) +{ + int i; + for (i = 0; i < n_old_kde_icons; i++) + if (old_kde_icons[i] == w) + return True; + return False; +} + +void kde_tray_old_icons_remove(Window w) +{ + int i; + for (i = 0; i < n_old_kde_icons; i++) + if (old_kde_icons[i] == w) { + LOG_TRACE(("0x%x unmarked as an old kde icon\n", w)); + old_kde_icons[i] = None; + } +} + +int kde_tray_check_for_icon(Display *dpy, Window w) +{ + Atom actual_type; + int actual_format; + unsigned long nitems, bytes_after; + static Atom xa_kde_net_wm_system_tray_window_for = None; + unsigned char *data = NULL; + /* Check if the window has a property named _KDE_NET_WM_SYSTEM_TRAY_WINDOW FOR */ + if (xa_kde_net_wm_system_tray_window_for == None) + xa_kde_net_wm_system_tray_window_for = XInternAtom(dpy, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", True); + /* If this atom does not exist, we have nothing to check for */ + if (xa_kde_net_wm_system_tray_window_for == None) return False; + /* TODO: use x11_ call */ + XGetWindowProperty(dpy, w, xa_kde_net_wm_system_tray_window_for, 0L, 1L, + False, XA_WINDOW, &actual_type, &actual_format, &nitems, &bytes_after, + &data); + XFree(data); + if (x11_ok() && actual_type == XA_WINDOW && nitems == 1) + return SUCCESS; + else + return FAILURE; +} + +Window kde_tray_find_icon(Display *dpy, Window w) +{ + Window root, parent, *children = NULL; + unsigned int nchildren; + int i; + Window r = None; + if (kde_tray_check_for_icon(dpy, w)) return w; + XQueryTree(dpy, w, &root, &parent, &children, &nchildren); + if (!x11_ok()) goto bailout; + for (i = 0; i < nchildren; i++) + if ((r = kde_tray_find_icon(dpy, children[i])) != None) + goto bailout; +bailout: + if (children != NULL && nchildren > 0) XFree(children); + return r; +} + diff --git a/gb.desktop.x11/src/systray/kde_tray.h b/gb.desktop.x11/src/systray/kde_tray.h new file mode 100644 index 00000000..d3982257 --- /dev/null +++ b/gb.desktop.x11/src/systray/kde_tray.h @@ -0,0 +1,34 @@ +/* ------------------------------- +* vim:tabstop=4:shiftwidth=4 +* kde_tray.h +* Sun, 19 Sep 2004 12:28:59 +0700 +* ------------------------------- +* KDE tray related routines +* -------------------------------*/ + +#ifndef _KDE_TRAY_H_ +#define _KDE_TRAY_H_ + +/* Init support for KDE tray icons. */ +void kde_tray_init(Display *dpy); + +/* Check if WM supports KDE tray icons */ +int kde_tray_update_fallback_mode(Display *dpy); + +/* Update the list of "old" KDE icons. Icon is considered "old" + * if it was present before the tray was started. */ +int kde_tray_update_old_icons(Display *dpy); + +/* Check if the window w is an "old" KDE tray icon */ +int kde_tray_is_old_icon(Window w); + +/* Remove the window w from the list of "old" KDE tray icons */ +void kde_tray_old_icons_remove(Window w); + +/* Check if the window w is a KDE tray icon */ +int kde_tray_check_for_icon(Display *dpy, Window w); + +/* Find KDE tray icon in subwindows of w */ +Window kde_tray_find_icon(Display *dpy, Window w); + +#endif diff --git a/gb.desktop.x11/src/systray/list.h b/gb.desktop.x11/src/systray/list.h new file mode 100644 index 00000000..a5f70b28 --- /dev/null +++ b/gb.desktop.x11/src/systray/list.h @@ -0,0 +1,76 @@ +/* ------------------------------- + * vim:tabstop=4:shiftwidth=4 + * list.h + * Fri, 10 Sep 2004 23:34:48 +0700 + * ------------------------------- + * Simple double linked list + * -------------------------------*/ + +#ifndef _LIST_H_ +#define _LIST_H_ + +/* Implement basic functions for doubly-linked list. + * Each structure to hold an element of the list must have two + * fields pointer fields: prev and next. */ + +/* Add item i_ to the head of the list h_ (pointer to the head) */ +#define LIST_ADD_ITEM(h_, i_) do { \ + (i_)->prev = NULL; \ + if ((h_) != NULL) { \ + (i_)->next = (h_); \ + (h_)->prev = (i_); \ + } else { \ + (i_)->next = NULL; \ + } \ + (h_) = (i_); \ +} while(0) + +/* Insert item i_ after item t_ to the list h_ (pointer to the head) */ +#define LIST_INSERT_AFTER(h_,t_,i_) do { \ + (i_)->prev = (t_); \ + if ((t_) != NULL) { \ + (i_)->next = (t_)->next; \ + (t_)->next = (i_); \ + } else { \ + if ((h_) != NULL) { \ + (i_)->next = (h_); \ + (h_)->prev = (i_); \ + } else { \ + (i_)->next = NULL; \ + } \ + (h_) = (i_); \ + } \ +} while(0) + +/* Delete item i_ from the list h_ (pointer to the head) */ +#define LIST_DEL_ITEM(h_, i_) do { \ + if (i_->prev != NULL) \ + i_->prev->next = i_->next; \ + if (i_->next != NULL) \ + i_->next->prev = i_->prev; \ + if (i_ == h_) \ + h_ = i_->next; \ +} while(0) + +/* Clean the list h_ (pointer to the head); + * tmp_ is a temporary variable */ +#define LIST_CLEAN(h_, tmp_) do { \ + for (tmp_ = h_, h_ = (h_ != NULL) ? h_->next : NULL; tmp_ != NULL; \ + tmp_ = h_, h_ = h_ != NULL ? h_->next : NULL) { \ + free(tmp_); \ + } \ + h_ = NULL; \ +} while(0) + +/* Clean the list h_ (pointer to the head) calling cbk_ for every + * element of the list; tmp_ is a temporary variable */ +/* Do not call free(), deletion must be done by the callback */ +#define LIST_CLEAN_CBK(h_, tmp_, cbk_) do { \ + for (tmp_ = h_, h_ = (h_ != NULL) ? h_->next : NULL; tmp_ != NULL; \ + tmp_ = h_, h_ = h_ != NULL ? h_->next : NULL) { \ + cbk_(tmp_); \ + } \ + h_ = NULL; \ +} while(0) + +#endif diff --git a/gb.desktop.x11/src/systray/settings.c b/gb.desktop.x11/src/systray/settings.c new file mode 100644 index 00000000..defa5f95 --- /dev/null +++ b/gb.desktop.x11/src/systray/settings.c @@ -0,0 +1,949 @@ +/* ------------------------------- + * vim:tabstop=4:shiftwidth=4 + * settings.c + * Sun, 12 Sep 2004 18:55:53 +0700 + * ------------------------------- + * settings parser\container + * -------------------------------*/ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "systray.h" + +#include "common.h" +#include "settings.h" +#include "debug.h" +//#include "layout.h" +#include "list.h" +#include "tray.h" + +#include "xutils.h" +#include "wmh.h" + +/* Here we keep our filthy settings */ +struct Settings settings; +/* Initialize data */ +void init_default_settings() +{ + settings.bg_color_str = "gray"; + settings.tint_color_str = "white"; + //settings.scrollbars_highlight_color_str = "white"; + settings.display_str = NULL; +#ifdef DEBUG + settings.log_level = LOG_LEVEL_ERR; +#endif + settings.geometry_str = NULL; + settings.max_geometry_str = "0x0"; + settings.icon_size = FALLBACK_ICON_SIZE; + settings.slot_size = -1; + settings.deco_flags = DECO_NONE; + settings.max_tray_dims.x = 0; + settings.max_tray_dims.y = 0; + settings.parent_bg = 0; + settings.shrink_back_mode = 1; + settings.sticky = 1; + settings.skip_taskbar = 1; + settings.transparent = 0; + settings.vertical = 0; + //settings.grow_gravity = GRAV_N | GRAV_W; + //settings.icon_gravity = GRAV_N | GRAV_W; + settings.wnd_type = _NET_WM_WINDOW_TYPE_DOCK; + settings.wnd_layer = NULL; + settings.wnd_name = PROGNAME; + settings.xsync = 0; + settings.need_help = 0; + settings.config_fname = NULL; + settings.full_pmt_search = 1; + settings.min_space_policy = 0; + settings.pixmap_bg = 0; + settings.bg_pmap_path = NULL; + settings.tint_level = 0; + settings.fuzzy_edges = 0; + settings.dockapp_mode = DOCKAPP_NONE; + //settings.scrollbars_size = -1; + //settings.scrollbars_mode = SB_MODE_NONE; + //settings.scrollbars_inc = -1; + settings.wm_strut_mode = WM_STRUT_AUTO; + settings.kludge_flags = KLUDGE_USE_ICONS_HINTS; + settings.remote_click_name = NULL; + settings.remote_click_btn = REMOTE_CLICK_BTN_DEFAULT; + settings.remote_click_cnt = REMOTE_CLICK_CNT_DEFAULT; + settings.remote_click_pos.x = REMOTE_CLICK_POS_DEFAULT; + settings.remote_click_pos.y = REMOTE_CLICK_POS_DEFAULT; +#ifdef DELAY_EMBEDDING_CONFIRMATION + settings.confirmation_delay = 3; +#endif +} + +#if 0 +/* ******* general parsing utils ********* */ + +#define PARSING_ERROR(msg,str) if (!silent) LOG_ERROR(("Parsing error: " msg ", \"%s\" found\n", str)); + +/* Parse highlight color */ +int parse_scrollbars_highlight_color(char *str, char ***tgt, int silent) +{ + if (!strcasecmp(str, "disable")) + **tgt = NULL; + else { + **tgt = strdup(str); + if (**tgt == NULL) DIE_OOM(("Could not copy value from parameter\n")); + } + return SUCCESS; +} + +/* Parse log level */ +int parse_log_level(char *str, int **tgt, int silent) +{ + if (!strcmp(str, "err")) + **tgt = LOG_LEVEL_ERR; + else if (!strcmp(str, "info")) + **tgt = LOG_LEVEL_INFO; +#ifdef DEBUG + else if (!strcmp(str, "trace")) + **tgt = LOG_LEVEL_TRACE; +#endif + else { + PARSING_ERROR("err, info, or trace expected", str); + return FAILURE; + } + return SUCCESS; +} + +/* Parse dockapp mode */ +int parse_dockapp_mode(char *str, int **tgt, int silent) +{ + if (!strcmp(str, "none")) + **tgt = DOCKAPP_NONE; + else if (!strcmp(str, "simple")) + **tgt = DOCKAPP_SIMPLE; + else if (!strcmp(str, "wmaker")) + **tgt = DOCKAPP_WMAKER; + else { + PARSING_ERROR("none, simple, or wmaker expected", str); + return FAILURE; + } + return SUCCESS; +} + +/* Parse gravity string ORing resulting value + * with current value of tgt */ +int parse_gravity(char *str, int **tgt, int silent) +{ + int i, r = 0, s; + if (str == NULL) goto fail; + s = strlen(str); + if (s > 2) goto fail; + for (i = 0; i < s; i++) + switch (tolower(str[i])) { + case 'n': r |= GRAV_N; break; + case 's': r |= GRAV_S; break; + case 'w': r |= GRAV_W; break; + case 'e': r |= GRAV_E; break; + default: goto fail; + } + if ((r & GRAV_N && r & GRAV_S) || (r & GRAV_E && r & GRAV_W)) + goto fail; + **tgt = r; + return SUCCESS; +fail: + PARSING_ERROR("gravity expected", str); + return FAILURE; +} + +/* Parse integer string storing resulting value in tgt */ +int parse_int(char *str, int **tgt, int silent) +{ + int r; + char *tail; + r = strtol(str, &tail, 0); + if (*tail == 0) { + **tgt = r; + return SUCCESS; + } else { + PARSING_ERROR("integer expected", str); + return FAILURE; + } +} + +/* Parse kludges mode */ +int parse_kludges(char *str, int **tgt, int silent) +{ + char *curtok = str, *rest = str; + do { + if ((rest = strchr(rest, ',')) != NULL) *(rest++) = 0; + if (!strcasecmp(curtok, "fix_window_pos")) + **tgt = KLUDGE_FIX_WND_POS; +/* else if (!strcasecmp(curtok, "fix_window_size"))*/ +/* **tgt = KLUDGE_FIX_WND_SIZE;*/ + else if (!strcasecmp(curtok, "force_icons_size")) + **tgt = KLUDGE_FORCE_ICONS_SIZE; + else if (!strcasecmp(curtok, "use_icons_hints")) + **tgt = KLUDGE_USE_ICONS_HINTS; + else { + PARSING_ERROR("kludge flag expected", curtok); + return FAILURE; + } + curtok = rest; + } while (rest != NULL); + return SUCCESS; +} + +/* Parse strut mode */ +int parse_strut_mode(char *str, int **tgt, int silent) +{ + if (!strcasecmp(str, "auto")) + **tgt = WM_STRUT_AUTO; + else if (!strcasecmp(str, "top")) + **tgt = WM_STRUT_TOP; + else if (!strcasecmp(str, "bottom")) + **tgt = WM_STRUT_BOT; + else if (!strcasecmp(str, "left")) + **tgt = WM_STRUT_LFT; + else if (!strcasecmp(str, "right")) + **tgt = WM_STRUT_RHT; + else if (!strcasecmp(str, "none")) + **tgt = WM_STRUT_NONE; + else { + PARSING_ERROR("one of top, bottom, left, right, or auto expected", str); + return FAILURE; + } + return SUCCESS; +} + +/* Parse boolean string storing result in tgt*/ +int parse_bool(char *str, int **tgt, int silent) +{ + if (!strcasecmp(str, "yes") || !strcasecmp(str, "on") || !strcasecmp(str, "true") || !strcasecmp(str, "1")) + **tgt = True; + else if (!strcasecmp(str, "no") || !strcasecmp(str, "off") || !strcasecmp(str, "false") || !strcasecmp(str, "0")) + **tgt = False; + else { + PARSING_ERROR("boolean expected", str); + return FAILURE; + } + return SUCCESS; +} + +/* Backwards version of the boolean parser */ +int parse_bool_rev(char *str, int **tgt, int silent) +{ + if (parse_bool(str, tgt, silent)) { + **tgt = !**tgt; + return SUCCESS; + } + return FAILURE; +} + +/* Parse window layer string storing result in tgt */ +int parse_wnd_layer(char *str, char ***tgt, int silent) +{ + if (!strcasecmp(str, "top")) + **tgt = _NET_WM_STATE_ABOVE; + else if (!strcasecmp(str, "bottom")) + **tgt = _NET_WM_STATE_BELOW; + else if (!strcasecmp(str, "normal")) + **tgt = NULL; + else { + PARSING_ERROR("window layer expected", str); + return FAILURE; + } + return SUCCESS; +} + +/* Parse window type string storing result in tgt */ +int parse_wnd_type(char *str, char ***tgt, int silent) +{ + if (!strcasecmp(str, "dock")) + **tgt = _NET_WM_WINDOW_TYPE_DOCK; + else if (!strcasecmp(str, "toolbar")) + **tgt = _NET_WM_WINDOW_TYPE_TOOLBAR; + else if (!strcasecmp(str, "utility")) + **tgt = _NET_WM_WINDOW_TYPE_UTILITY; + else if (!strcasecmp(str, "normal")) + **tgt = _NET_WM_WINDOW_TYPE_NORMAL; + else if (!strcasecmp(str, "desktop")) + **tgt = _NET_WM_WINDOW_TYPE_DESKTOP; + else { + PARSING_ERROR("window type expected", str); + return FAILURE; + } + return SUCCESS; +} + +/* Just copy string from arg to *tgt */ +int parse_copystr(char *str, char ***tgt, int silent) +{ + /* Valgrind note: this memory will never + * be freed before stalonetray's exit. */ + **tgt = strdup(str); + if (**tgt == NULL) DIE_OOM(("Could not copy value from parameter\n")); + return SUCCESS; +} + +/* Parses window decoration specification */ +int parse_deco(char *str, int **tgt, int silent) +{ + if (!strcasecmp(str, "none")) + **tgt = DECO_NONE; + else if (!strcasecmp(str, "all")) + **tgt = DECO_ALL; + else if (!strcasecmp(str, "border")) + **tgt = DECO_BORDER; + else if (!strcasecmp(str, "title")) + **tgt = DECO_TITLE; + else { + PARSING_ERROR("decoration specification expected", str); + return FAILURE; + } + return SUCCESS; +} + +/* Parses window decoration specification */ +int parse_sb_mode(char *str, int **tgt, int silent) +{ + if (!strcasecmp(str, "none")) + **tgt = 0; + else if (!strcasecmp(str, "vertical")) + **tgt = SB_MODE_VERT; + else if (!strcasecmp(str, "horizontal")) + **tgt = SB_MODE_HORZ; + else if (!strcasecmp(str, "all")) + **tgt = SB_MODE_HORZ | SB_MODE_VERT; + else { + PARSING_ERROR("scrollbars specification expected", str); + return FAILURE; + } + return SUCCESS; +} + +#if 0 +/* Parses remote op specification */ +int parse_remote(char *str, void **tgt, int silent) +{ +#define NEXT_TOK(str, rest) do { \ + (str) = (rest); \ + if ((str) != NULL) { \ + (rest) = strchr((str), ','); \ + if ((rest) != NULL) *((rest)++)=0; \ + } \ +} while(0) +#define PARSE_INT(tgt, str, tail, def, msg) do { \ + if (str == NULL || *(str) == '\0') { \ + (tgt) = def; \ + } else { \ + (tgt) = strtol((str), &(tail), 0); \ + if (*(tail) != '\0') { \ + PARSING_ERROR(msg, (str)); \ + return FAILURE; \ + } \ + } \ +} while(0) + /* Handy names for parameters */ + int *flag = (int *) tgt[0]; + char **name = (char **) tgt[1]; + int *btn = (int *) tgt[2]; + struct Point *pos = (struct Point *) tgt[3]; + /* Local variables */ + char *rest = str, *tail; + if (str == NULL || strlen(str) == 0) return FAILURE; + *flag = 1; + NEXT_TOK(str, rest); + *name = strdup(str); + NEXT_TOK(str, rest); + PARSE_INT(*btn, str, tail, INT_MIN, "remote click: button number expected"); + NEXT_TOK(str, rest); + PARSE_INT(pos->x, str, tail, INT_MIN, "remote click: x coordinate expected"); + NEXT_TOK(str, rest); + PARSE_INT(pos->y, str, tail, INT_MIN, "remote click: y coordinate expected"); + return SUCCESS; +#undef NEXT_TOK +#undef PARSE_INT +} +#endif + +int parse_remote_click_type(char *str, int **tgt, int silent) +{ + if (!strcasecmp(str, "single")) + **tgt = 1; + else if (!strcasecmp(str, "double")) + **tgt = 2; + else { + PARSING_ERROR("click type can be single or double", str); + return FAILURE; + } + return SUCCESS; +} + +int parse_pos(char *str, void **tgt, int silent) +{ + struct Point *pos = (struct Point *) tgt[0]; + unsigned int dummy; + XParseGeometry(str, &pos->x, &pos->y, &dummy, &dummy); + return SUCCESS; +} + +/************ CLI **************/ + +#define MAX_TARGETS 10 + +/* parameter parser function */ +typedef int (*param_parser_t) (char *str, void *tgt[MAX_TARGETS], int silent); + +struct Param { + char *short_name; /* Short command line parameter name */ + char *long_name; /* Long command line parameter name */ + char *rc_name; /* Parameter name for rc file */ + void *target[MAX_TARGETS];/* Pointers to the values that are set by this parameter */ + param_parser_t parser; /* Pointer to parsing function */ + int pass; /* 0th pass parameters are parsed before rc file, + 1st pass parameters are parsed after it */ + int takes_arg; /* Wheather this parameter takes an argument */ + int optional_arg; /* Wheather the argument is optional */ + char *default_arg_val; /* Default value of the argument if none is given */ + /* char *desc; */ /* TODO: Description */ +}; + +struct Param params[] = { + {"-display", NULL, "display", {&settings.display_str}, (param_parser_t) &parse_copystr, 1, 1, 0, NULL}, + {NULL, "--log-level", "log_level", {&settings.log_level}, (param_parser_t) &parse_log_level, 1, 1, 0, NULL}, + {"-bg", "--background", "background", {&settings.bg_color_str}, (param_parser_t) &parse_copystr, 1, 1, 0, NULL}, + {"-c", "--config", NULL, {&settings.config_fname}, (param_parser_t) &parse_copystr, 0, 1, 0, NULL}, +#ifdef DELAY_EMBEDDING_CONFIRMATION + {NULL, "--confirmation-delay", "confirmation_delay", {&settings.confirmation_delay}, (param_parser_t) &parse_int, 1, 1, 0, NULL}, +#endif + {"-d", "--decorations", "decorations", {&settings.deco_flags}, (param_parser_t) &parse_deco, 1, 1, 1, "all"}, + {NULL, "--dockapp-mode", "dockapp_mode", {&settings.dockapp_mode}, (param_parser_t) &parse_dockapp_mode, 1, 1, 1, "simple"}, + {"-f", "--fuzzy-edges", "fuzzy_edges", {&settings.fuzzy_edges}, (param_parser_t) &parse_int, 1, 1, 1, "2"}, + {"-geometry", "--geometry", "geometry", {&settings.geometry_str}, (param_parser_t) &parse_copystr, 1, 1, 0, NULL}, + {NULL, "--grow-gravity", "grow_gravity", {&settings.grow_gravity}, (param_parser_t) &parse_gravity, 1, 1, 0, NULL}, + {NULL, "--icon-gravity", "icon_gravity", {&settings.icon_gravity}, (param_parser_t) &parse_gravity, 1, 1, 0, NULL}, + { "-i", "--icon-size", "icon_size", {&settings.icon_size}, (param_parser_t) &parse_int, 1, 1, 0, NULL}, + {"-h", "--help", NULL, {&settings.need_help}, (param_parser_t) &parse_bool, 0, 0, 0, "true" }, + {NULL, "--kludges", "kludges", {&settings.kludge_flags}, (param_parser_t) &parse_kludges, 1, 1, 0, NULL}, + {NULL, "--max-geometry", "max_geometry", {&settings.max_geometry_str}, (param_parser_t) &parse_copystr, 1, 1, 0, NULL}, + {NULL, "--no-shrink", "no_shrink", {&settings.shrink_back_mode}, (param_parser_t) &parse_bool_rev, 1, 1, 1, "true"}, + {"-p", "--parent-bg", "parent_bg", {&settings.parent_bg}, (param_parser_t) &parse_bool, 1, 1, 1, "true"}, +#ifdef XPM_SUPPORTED + {NULL, "--pixmap-bg", "pixmap_bg", {&settings.bg_pmap_path}, (param_parser_t) &parse_copystr, 1, 1, 0, NULL}, +#endif + {"-r", "--remote-click-icon", NULL, {&settings.remote_click_name}, (param_parser_t) &parse_copystr, 1, 1, 0, NULL}, + {NULL, "--remote-click-button", NULL, {&settings.remote_click_btn}, (param_parser_t) &parse_int, 1, 1, 0, NULL}, + {NULL, "--remote-click-position", NULL, {&settings.remote_click_pos}, (param_parser_t) &parse_pos, 1, 1, 0, NULL}, + {NULL, "--remote-click-type", NULL, {&settings.remote_click_cnt}, (param_parser_t) &parse_remote_click_type, 1, 1, 0, NULL}, + {NULL, "--scrollbars", "scrollbars", {&settings.scrollbars_mode}, (param_parser_t) &parse_sb_mode, 1, 1, 0, NULL}, + {NULL, "--scrollbars-highlight", "scrollbars_highlight", {&settings.scrollbars_highlight_color_str}, (param_parser_t) &parse_scrollbars_highlight_color, 1, 1, 0, NULL}, + {NULL, "--scrollbars-step", "scrollbars_step", {&settings.scrollbars_inc}, (param_parser_t) &parse_int, 1, 1, 0, NULL}, + {NULL, "--scrollbars-size", "scrollbars_size", {&settings.scrollbars_size}, (param_parser_t) &parse_int, 1, 1, 0, NULL}, + {NULL, "--skip-taskbar", "skip_taskbar", {&settings.skip_taskbar}, (param_parser_t) &parse_bool, 1, 1, 1, "true"}, + {"-s", "--slot-size", "slot_size", {&settings.slot_size}, (param_parser_t) &parse_int, 1, 1, 0, NULL}, + {NULL, "--sticky", "sticky", {&settings.sticky}, (param_parser_t) &parse_bool, 1, 1, 1, "true"}, + {NULL, "--tint-color", "tint_color", {&settings.tint_color_str}, (param_parser_t) &parse_copystr, 1, 1, 0, NULL}, + {NULL, "--tint-level", "tint_level", {&settings.tint_level}, (param_parser_t) &parse_int, 1, 1, 0, NULL}, + {"-t", "--transparent", "transparent", {&settings.transparent}, (param_parser_t) &parse_bool, 1, 1, 1, "true"}, + {"-v", "--vertical", "vertical", {&settings.vertical}, (param_parser_t) &parse_bool, 1, 1, 1, "true"}, + {NULL, "--window-layer", "window_layer", {&settings.wnd_layer}, (param_parser_t) &parse_wnd_layer, 1, 1, 0, NULL}, + {NULL, "--window-name", "window_name", {&settings.wnd_name}, (param_parser_t) &parse_copystr, 1, 1, 0, NULL}, + {NULL, "--window-strut", "window_strut", {&settings.wm_strut_mode}, (param_parser_t) &parse_strut_mode, 1, 1, 0, NULL}, + {NULL, "--window-type", "window_type", {&settings.wnd_type}, (param_parser_t) &parse_wnd_type, 1, 1, 0, NULL}, + {NULL, "--xsync", "xsync", {&settings.xsync}, (param_parser_t) &parse_bool, 1, 1, 1, "true"}, + {NULL, NULL, NULL, {NULL}} +}; +#endif + +#if 0 +void usage(char *progname) +{ + printf( "\nstalonetray "VERSION" [ " FEATURE_LIST " ]\n"); + printf( "\nUsage: %s [options...]\n", progname); + printf( "\n" + "For short options argument can be specified as -o value or -ovalue.\n" + "For long options argument can be specified as --option value or\n" + "--option=value. All flag-options have expilicit optional boolean \n" + "argument, which can be true (1, yes) or false (0, no).\n" + "\n" + "Possible options are:\n" + " -display use X display \n" + " -bg, --background select background color (default: #777777)\n" + " -c, --config read configuration from \n" + " (instead of default $HOME/.stalonetrayrc)\n" + " -d, --decorations set what part of window decorations are\n" + " visible; deco can be: none (default),\n" + " title, border, all\n" + " --dockapp-mode [] enable dockapp mode; mode can be none (default),\n" + " simple (default if no mode specified), or wmaker\n" + " -f, --fuzzy-edges [] set edges fuzziness level from\n" + " 0 (disabled) to 3 (maximum); works with\n" + " tinting and/or pixmap background;\n" + " if not specified, level defaults to 2\n" + " [-]-geometry set initial tray`s geometry (width and height\n" + " are defined in icon slots; offsets are defined\n" + " in pixels)\n" + " --grow-gravity set tray`s grow gravity,\n" + " either to N, S, W, E, NW, NE, SW, or SE\n" + " --icon-gravity icon positioning gravity (NW, NE, SW, SE)\n" + " -i, --icon-size set basic icon size to ; default is 24\n" + " -h, --help show this message\n" +#ifdef DEBUG + " --log-level set the level of output to either err\n" + " (default), info, or trace\n" +#else + " --log-level set the level of output to either err\n" + " (default), or info\n" +#endif + " --kludges enable specific kludges to work around\n" + " non-conforming WMs and/or stalonetray bugs;\n" + " argument is a comma-separated list of:\n" + " - fix_window_pos (fix window position),\n" + " - force_icons_size (ignore icon resizes),\n" + " - use_icons_hints (use icon size hints)\n" + " --max-geometry set tray maximal width and height; 0 indicates\n" + " no limit in respective direction\n" + " --no-shrink do not shrink window back after icon removal\n" + " -p, --parent-bg use parent for background\n" + " --pixmap-bg use pixmap for tray`s window background\n" + " -r, --remote-click-icon remote control (assumes an instance of\n" + " stalonetray is already an active tray on this\n" + " screen); sends click to icon which window's \n" + " name is \n" + " --remote-click-button defines mouse button for --remote-click-icon\n" + " --remote-click-position x defines position for --remote-click-icon\n" + " --remote-click-type defines click type for --remote-click-icon;\n" + " type can be either single, or double\n" + " --scrollbars set scrollbar mode either to all, horizontal,\n" + " vertical, or none\n" + " --scrollbars-highlight set scrollbar highlighting mode which can\n" + " be either color spec (default color is red)\n" + " or disable\n" + " --scrollbars-step set scrollbar step to n pixels\n" + " --scrollbars-size set scrollbar size to n pixels\n" + " --slot-size set icon slot size to n\n" + " --skip-taskbar hide tray`s window from the taskbar\n" + " --sticky make tray`s window sticky across multiple\n" + " desktops/pages\n" + " -t, --transparent enable root transparency\n" + " --tint-color tint tray background with color (not used\n" + " with plain color background)\n" + " --tint-level set tinting level from 0 to 255\n" + " -v, --vertical use vertical layout of icons (horizontal\n" + " layout is used by default)\n" + " --window-layer set tray`s window EWMH layer\n" + " either to bottom, normal, or top\n" + " --window-strut set window strut mode to either auto,\n" + " left, right, top, or bottom\n" + " --window-type set tray`s window EWMH type to either\n" + " normal, dock, toolbar, utility, desktop\n" + " --xsync operate on X server synchronously (SLOW)\n" + "\n" + ); +} + +/* Parse command line parameters */ +int parse_cmdline(int argc, char **argv, int pass) +{ + struct Param *p, *match; + char *arg, *progname = argv[0]; + while (--argc > 0) { + argv++; + match = NULL; + for (p = params; p->parser != NULL; p++) { + if (p->takes_arg) { + if (p->short_name != NULL && strstr(*argv, p->short_name) == *argv) { + if ((*argv)[strlen(p->short_name)] != '\0') /* accept arguments in the form -a5 */ + arg = *argv + strlen(p->short_name); + else if (argc > 1 && argv[1][0] != '-') { /* accept arguments in the form -a 5, + do not accept values starting with '-' */ + arg = *(++argv); + argc--; + } else if(!p->optional_arg) { /* argument is missing */ + LOG_ERROR(("%s expects an argument\n", p->short_name)); + break; + } else /* argument is optional, use default value */ + arg = p->default_arg_val; + } else if (p->long_name != NULL && strstr(*argv, p->long_name) == *argv) { + if ((*argv)[strlen(p->long_name)] == '=') /* accept arguments in the form --abcd=5 */ + arg = *argv + strlen(p->long_name) + 1; + else if ((*argv)[strlen(p->long_name)] == '\0') { /* accept arguments in the from ---abcd 5 */ + if (argc > 1 && argv[1][0] != '-' ) { /* arguments cannot start with the dash */ + arg = *(++argv); + argc--; + } else if (!p->optional_arg) { /*argument is missing */ + LOG_ERROR(("%s expects an argument\n", p->long_name)); + break; + } else /* argument is optional, use default value */ + arg = p->default_arg_val; + } else continue; /* just in case when there can be both --abc and --abcd */ + } else continue; + match = p; + break; + } else if (strcmp(*argv, p->short_name) == 0 || strcmp(*argv, p->long_name) == 0) { + match = p; + arg = p->default_arg_val; + break; + } + } +#define USAGE_AND_DIE() do { usage(progname); DIE(("Could not parse command line\n")); } while (0) + if (match == NULL) USAGE_AND_DIE(); + if (match->pass != pass) continue; + if (arg == NULL) DIE_IE(("Argument cannot be NULL!\n")); + LOG_TRACE(("cmdline: pass %d, param \"%s\", arg \"%s\"\n", pass, match->long_name != NULL ? match->long_name : match->short_name, arg)); + if (!match->parser(arg, match->target, match->optional_arg)) { + if (match->optional_arg) { + argc++; argv--; + assert(arg != match->default_arg_val); + match->parser(match->default_arg_val, match->target, False); + } else + USAGE_AND_DIE(); + } + } + if (settings.need_help) { + usage(progname); + exit(0); + } + return SUCCESS; +} +#endif + +/************ .stalonetrayrc ************/ + +/************************************************************************************** + * ::= [] [( )* ] [] + * ::= ""| + * ::= # + **************************************************************************************/ + +/* Does exactly what its name says */ +#define SKIP_SPACES(p) { for (; *p != 0 && isspace((int) *p); p++); } +/* Break the line in argc, argv pair */ +int get_args(char *line, int *argc, char ***argv) +{ + int q_flag = 0; + char *arg_start, *q_pos; + *argc = 0; + *argv = NULL; + /* 1. Strip leading spaces */ + SKIP_SPACES(line); + if (0 == *line) { /* meaningless line */ + return SUCCESS; + } + arg_start = line; + /* 2. Strip comments */ + for (; 0 != *line; line++) { + q_flag = ('"' == *line) ? !q_flag : q_flag; + if ('#' == *line && !q_flag) { + *line = 0; + break; + } + } + if (q_flag) { /* disbalance of quotes */ + LOG_ERROR(("Disbalance of quotes\n")); + return FAILURE; + } + if (arg_start == line) { /* meaningless line */ + return SUCCESS; + } + line--; + /* 3. Strip trailing spaces */ + for (; line != arg_start && isspace((int) *line); line--); + if (arg_start == line) { /* meaningless line */ + return FAILURE; + } + *(line + 1) = 0; /* this _is_ really ok since isspace(0) != 0 */ + line = arg_start; + /* 4. Extract arguments */ + do { + (*argc)++; + /* Add space to store one more argument */ + if (NULL == (*argv = realloc(*argv, *argc * sizeof(char *)))) + DIE_OOM(("Could not allocate memory to parse parameters\n")); + if (*arg_start == '"') { /* 4.1. argument is quoted: find matching quote */ + arg_start++; + (*argv)[*argc - 1] = arg_start; + if (NULL == (q_pos = strchr(arg_start, '"'))) { + free(*argv); + DIE_IE(("Quotes balance calculation failed\n")); + return FAILURE; + } + arg_start = q_pos; + } else { /* 4.2. whitespace-separated argument: find fist whitespace */ + (*argv)[*argc - 1] = arg_start; + for (; 0 != *arg_start && !isspace((int) *arg_start); arg_start++); + } + if (*arg_start != 0) { + *arg_start = 0; + arg_start++; + SKIP_SPACES(arg_start); + } + } while(*arg_start != 0); + return SUCCESS; +} + +#if 0 +#define READ_BUF_SZ 512 +/* Parses rc file (path is either taken from settings.config_fname + * or ~/.stalonetrayrc is used) */ +void parse_rc() +{ + char *home_dir; + static char config_fname[PATH_MAX]; + FILE *cfg; + + char buf[READ_BUF_SZ + 1]; + int lnum = 0; + + int argc; + char **argv, *arg; + + struct Param *p, *match; + + /* 1. Setup file name */ + if (settings.config_fname == NULL) { + if ((home_dir = getenv("HOME")) == NULL) { + LOG_ERROR(("You have no $HOME. I'm sorry for you.\n")); + return; + } + snprintf(config_fname, PATH_MAX-1, "%s/%s", home_dir, STALONETRAY_RC); + settings.config_fname = config_fname; + } + + LOG_INFO(("using config file \"%s\"\n", settings.config_fname)); + + /* 2. Open file */ + cfg = fopen(settings.config_fname, "r"); + if (cfg == NULL) { + LOG_ERROR(("could not open %s (%s)\n", settings.config_fname, strerror(errno))); + return; + } + + /* 3. Read the file line by line */ + buf[READ_BUF_SZ] = 0; + while (!feof(cfg)) { + lnum++; + if (fgets(buf, READ_BUF_SZ, cfg) == NULL) { + if (ferror(cfg)) LOG_ERROR(("read error (%s)\n", strerror(errno))); + break; + } + + if (!get_args(buf, &argc, &argv)) { + DIE(("Configuration file parse error at %s:%d: could not parse line\n", settings.config_fname, lnum)); + } + if (!argc) continue; /* This is empty/comment-only line */ + + match = NULL; + for (p = params; p->parser != NULL; p++) { + if (p->rc_name != NULL && strcmp(argv[0], p->rc_name) == 0) { + if (argc - 1 > (p->takes_arg ? 1 : 0) || (!p->optional_arg && argc - 1 < 1)) + DIE(("Configuration file parse error at %s:%d:" + "invalid number of args for \"%s\" (%s required)\n", + settings.config_fname, + lnum, + argv[0], + p->optional_arg ? "0/1" : "1" )); + match = p; + arg = (!p->takes_arg || (p->optional_arg && argc == 1)) ? p->default_arg_val : argv[1]; + break; + } + } + if (!match) { + DIE(("Configuration file parse error at %s:%d: unrecognized rc file keyword \"%s\".\n", settings.config_fname, lnum, argv[0])); + } + assert(arg != NULL); + LOG_TRACE(("rc: param \"%s\", arg \"%s\"\n", match->rc_name, arg)); + if (!match->parser(arg, match->target, False)) { + DIE(("Configuration file parse error at %s:%d: could not parse argument for \"%s\".\n", settings.config_fname, lnum, argv[0])); + } + free(argv); + } +} +#endif + +#if 0 +/* Interpret all settings that need an open display or other settings */ +void interpret_settings() +{ + static int gravity_matrix[11] = { + ForgetGravity, + EastGravity, + WestGravity, + ForgetGravity, + SouthGravity, + SouthEastGravity, + SouthWestGravity, + ForgetGravity, + NorthGravity, + NorthEastGravity, + NorthWestGravity + }; + int geom_flags; + //int rc; + int dummy; + XWindowAttributes root_wa; + /* Sanitize icon size */ + val_range(settings.icon_size, MIN_ICON_SIZE, INT_MAX); + if (settings.slot_size < settings.icon_size) settings.slot_size = settings.icon_size; + /* Sanitize scrollbar settings */ + if (settings.scrollbars_mode != SB_MODE_NONE) { + val_range(settings.scrollbars_inc, settings.slot_size / 2, INT_MAX); + if (settings.scrollbars_size < 0) settings.scrollbars_size = settings.slot_size / 4; + } + /* Sanitize all gravity strings */ + settings.icon_gravity |= ((settings.icon_gravity & GRAV_V) ? 0 : GRAV_N); + settings.icon_gravity |= ((settings.icon_gravity & GRAV_H) ? 0 : GRAV_W); + settings.win_gravity = gravity_matrix[settings.grow_gravity]; + settings.bit_gravity = gravity_matrix[settings.icon_gravity]; + /* Parse all background-related settings */ +#ifdef XPM_SUPPORTED + settings.pixmap_bg = (settings.bg_pmap_path != NULL); +#endif + if (settings.pixmap_bg) { + settings.parent_bg = False; + settings.transparent = False; + } + if (settings.transparent) + settings.parent_bg = False; + /* Parse color-related settings */ + if (!x11_parse_color(tray_data.dpy, settings.bg_color_str, &settings.bg_color)) + DIE(("Could not parse background color \"%s\"\n", settings.bg_color_str)); + if (settings.scrollbars_highlight_color_str != NULL) + { + if (!x11_parse_color(tray_data.dpy, settings.scrollbars_highlight_color_str, &settings.scrollbars_highlight_color)) + { + DIE(("Could not parse scrollbars highlight color \"%s\"\n", settings.bg_color_str)); + } + } + /* Sanitize tint level value */ + val_range(settings.tint_level, 0, 255); + if (settings.tint_level) { + /* Parse tint color */ + if (!x11_parse_color(tray_data.dpy, settings.tint_color_str, &settings.tint_color)) + DIE(("Could not parse tint color \"%s\"\n", settings.tint_color_str)); + } + /* Sanitize edges fuzziness */ + val_range(settings.fuzzy_edges, 0, 3); + /* Get dimensions of root window */ + if (!XGetWindowAttributes(tray_data.dpy, DefaultRootWindow(tray_data.dpy), &root_wa)) + DIE(("Could not get root window dimensions.\n")); + tray_data.root_wnd.x = 0; + tray_data.root_wnd.y = 0; + tray_data.root_wnd.width = root_wa.width; + tray_data.root_wnd.height = root_wa.height; + /* Parse geometry */ +# define DEFAULT_GEOMETRY "1x1+0+0" + tray_data.xsh.flags = PResizeInc | PBaseSize; + tray_data.xsh.x = 0; + tray_data.xsh.y = 0; + tray_data.xsh.width_inc = settings.slot_size; + tray_data.xsh.height_inc = settings.slot_size; + tray_data.xsh.base_width = 0; + tray_data.xsh.base_height = 0; + //tray_calc_window_size(0, 0, &tray_data.xsh.base_width, &tray_data.xsh.base_height); + geom_flags = XWMGeometry(tray_data.dpy, DefaultScreen(tray_data.dpy), + settings.geometry_str, DEFAULT_GEOMETRY, 0, + &tray_data.xsh, &tray_data.xsh.x, &tray_data.xsh.y, + &tray_data.xsh.width, &tray_data.xsh.height, &tray_data.xsh.win_gravity); + tray_data.xsh.win_gravity = settings.win_gravity; + tray_data.xsh.min_width = tray_data.xsh.width; + tray_data.xsh.min_height = tray_data.xsh.height; + tray_data.xsh.max_width = tray_data.xsh.width; + tray_data.xsh.min_height = tray_data.xsh.height; + tray_data.xsh.flags = PResizeInc | PBaseSize | PMinSize | PMaxSize | PWinGravity; + //tray_calc_tray_area_size(tray_data.xsh.width, tray_data.xsh.height, &settings.orig_tray_dims.x, &settings.orig_tray_dims.y); + /* Dockapp mode */ + if (settings.dockapp_mode == DOCKAPP_WMAKER) + tray_data.xsh.flags |= USPosition; + else { + if (geom_flags & (XValue | YValue)) tray_data.xsh.flags |= USPosition; else tray_data.xsh.flags |= PPosition; + if (geom_flags & (WidthValue | HeightValue)) tray_data.xsh.flags |= USSize; else tray_data.xsh.flags |= PSize; + } + LOG_TRACE(("final geometry: %dx%d at (%d,%d)\n", + tray_data.xsh.width, tray_data.xsh.height, + tray_data.xsh.x, tray_data.xsh.y)); + if ((geom_flags & XNegative) && (geom_flags & YNegative)) + settings.geom_gravity = SouthEastGravity; + else if (geom_flags & YNegative) + settings.geom_gravity = SouthWestGravity; + else if (geom_flags & XNegative) + settings.geom_gravity = NorthEastGravity; + else + settings.geom_gravity = NorthWestGravity; + /* Set tray maximal width/height */ + geom_flags = XParseGeometry(settings.max_geometry_str, + &dummy, &dummy, + (unsigned int *) &settings.max_tray_dims.x, + (unsigned int *) &settings.max_tray_dims.y); + LOG_TRACE(("max geometry from max_geometry_str: %dx%d\n", + settings.max_tray_dims.x, + settings.max_tray_dims.y)); + if (!settings.max_tray_dims.x) + settings.max_tray_dims.x = root_wa.width; + else { + settings.max_tray_dims.x *= settings.slot_size; + val_range(settings.max_tray_dims.x, settings.orig_tray_dims.x, INT_MAX); + } + if (!settings.max_tray_dims.y) + settings.max_tray_dims.y = root_wa.height; + else { + settings.max_tray_dims.y *= settings.slot_size; + val_range(settings.max_tray_dims.y, settings.orig_tray_dims.y, INT_MAX); + } + LOG_TRACE(("max geometry after normalization: %dx%d\n", + settings.max_tray_dims.x, + settings.max_tray_dims.y)); + /* XXX: this assumes certain degree of symmetry and in some point + * in the future this may not be the case... */ + //tray_calc_window_size(0, 0, &tray_data.scrollbars_data.scroll_base.x, &tray_data.scrollbars_data.scroll_base.y); + tray_data.scrollbars_data.scroll_base.x /= 2; + tray_data.scrollbars_data.scroll_base.y /= 2; +} +#endif + +/************** "main" ***********/ +int read_settings(int argc, char **argv) +{ + init_default_settings(); + /* Parse 0th pass command line args */ + //parse_cmdline(argc, argv, 0); + /* Parse configuration files */ + //parse_rc(); + /* Parse 1st pass command line args */ + //parse_cmdline(argc, argv, 1); + /* Display some settings */ + LOG_TRACE(("bg_color_str = \"%s\"\n", settings.bg_color_str)); + LOG_TRACE(("config_fname = \"%s\"\n", settings.config_fname)); + LOG_TRACE(("deco_flags = 0x%x\n", settings.deco_flags)); + LOG_TRACE(("display_str = \"%s\"\n", settings.display_str)); + LOG_TRACE(("dockapp_mode = %d\n", settings.dockapp_mode)); + LOG_TRACE(("full_pmt_search = %d\n", settings.full_pmt_search)); + LOG_TRACE(("geometry_str = \"%s\"\n", settings.geometry_str)); + //LOG_TRACE(("grow_gravity = 0x%x\n", settings.grow_gravity)); + //LOG_TRACE(("icon_gravity = 0x%x\n", settings.icon_gravity)); + LOG_TRACE(("icon_size = %d\n", settings.icon_size)); + LOG_TRACE(("log_level = %d\n", settings.log_level)); + LOG_TRACE(("max_tray_dims.x = %d\n", settings.max_tray_dims.x)); + LOG_TRACE(("max_tray_dims.y = %d\n", settings.max_tray_dims.y)); + LOG_TRACE(("min_space_policy = %d\n", settings.min_space_policy)); + LOG_TRACE(("need_help = %d\n", settings.need_help)); + LOG_TRACE(("parent_bg = %d\n", settings.parent_bg)); + //LOG_TRACE(("scrollbars_highlight_color_str = \"%s\"\n", settings.scrollbars_highlight_color_str)); + //LOG_TRACE(("scrollbars_highlight_color.pixel = %ld\n", settings.scrollbars_highlight_color.pixel)); + //LOG_TRACE(("scrollbars_inc = %d\n", settings.scrollbars_inc)); + //LOG_TRACE(("scrollbars_mode = %d\n", settings.scrollbars_mode)); + //LOG_TRACE(("scrollbars_size = %d\n", settings.scrollbars_size)); + LOG_TRACE(("shrink_back_mode = %d\n", settings.shrink_back_mode)); + LOG_TRACE(("slot_size = %d\n", settings.slot_size)); + LOG_TRACE(("vertical = %d\n", settings.vertical)); + LOG_TRACE(("xsync = %d\n", settings.xsync)); + return SUCCESS; +} + diff --git a/gb.desktop.x11/src/systray/settings.h b/gb.desktop.x11/src/systray/settings.h new file mode 100644 index 00000000..6ba554a6 --- /dev/null +++ b/gb.desktop.x11/src/systray/settings.h @@ -0,0 +1,94 @@ +/* ------------------------------- +* vim:tabstop=4:shiftwidth=4 +* settings.h +* Sun, 12 Sep 2004 18:06:08 +0700 +* ------------------------------- +* settings parser\container +* -------------------------------*/ + +#ifndef _SETTINGS_H_ +#define _SETTINGS_H_ + +#include +#include + +#include "systray.h" +//#include "layout.h" + +/* Default name of configuration file */ +#define STALONETRAY_RC ".stalonetrayrc" + +struct Settings { + /* Flags */ + int parent_bg; /* Enable pseudo-transparency using parents' background*/ + int deco_flags; /* Decoration flags */ + int transparent; /* Enable root-transparency */ + int skip_taskbar; /* Remove tray from wm`s taskbar */ + int sticky; /* Make tray sticky across desktops/pages */ + int xsync; /* Operate on X server syncronously */ + int pixmap_bg; /* Is pixmap used for background */ + int min_space_policy; /* Use placement that cause minimal grow */ + int full_pmt_search; /* Use non-first-match search algorithm */ + int vertical; /* Use vertical icon layout */ + int shrink_back_mode; /* Keep tray's window size minimal */ + int dockapp_mode; /* Activate dockapp mode */ + int kludge_flags; /* What kludges to activate */ + + int need_help; /* Print usage and exit */ + + /* Strings */ + char *display_str; /* Name of the display */ + char *bg_color_str; /* Background color name */ + char *scrollbars_highlight_color_str; /* Name of color to highlight scrollbars with. NULL means highlighting is disabled */ + char *geometry_str; /* Geometry spec */ + char *max_geometry_str; /* Geometry spec */ + char *config_fname; /* Path to the configuration file */ + char *wnd_type; /* Window type */ + char *wnd_layer; /* Window layer */ + char *wnd_name; /* Window name (WM_NAME) */ + char *bg_pmap_path; /* Background pixmap path */ + char *tint_color_str; /* Color used for tinting */ + char *remote_click_name; /* Icon name to execute remote click on */ + + /* Values */ + int icon_size; /* Icon size */ + int slot_size; /* Grid slot size */ + //int grow_gravity; /* Icon gravity (interpretation of icon_gravity_str) */ + //int icon_gravity; /* Grow gravity (interpretation of grow_gravity_str) */ + //int win_gravity; /* Tray window gravity (computed using grow gravity) */ + //int bit_gravity; /* Tray window bit gravity (computed using icon_gravity) */ + int geom_gravity; /* Tray window gravity when mapping the window (computed using geometry_str) */ + int fuzzy_edges; /* Level of edges fuzziness (0 = disabled) */ + int tint_level; /* Tinting level (0 = disabled) */ + //int scrollbars_mode; /* SB_MODE_NONE | SB_MODE_VERT | SB_MODE_HORZ */ + //int scrollbars_size; /* Size of scrollbar windows in pixels */ + //int scrollbars_inc; /* Step of scrollbar */ + int wm_strut_mode; /* WM strut mode */ + struct Point max_tray_dims;/* Maximal tray dimensions */ + struct Point max_layout_dims; /* Maximal layout dimensions */ + struct Point orig_tray_dims; /* Original tray dimensions */ + struct Point remote_click_pos; /* Remote click position */ + int remote_click_btn; /* Remote click button */ + int remote_click_cnt; /* Remote click count */ + + XColor tint_color; /* Color used for tinting */ + +#ifdef DELAY_EMBEDDING_CONFIRMATION + int confirmation_delay; +#endif + + XColor bg_color; /* Tray background color */ + //XColor scrollbars_highlight_color; /* Color to highlight scrollbars with */ + int log_level; /* Debug level */ +}; + +extern struct Settings settings; + +/* Read settings from cmd line and configuration file */ +int read_settings(int argc, char **argv); +/* Interpret all settings that either need an +* open display or are interpreted from other +* settings */ +//void interpret_settings(); + +#endif diff --git a/gb.desktop.x11/src/systray/systray.c b/gb.desktop.x11/src/systray/systray.c new file mode 100644 index 00000000..45fec292 --- /dev/null +++ b/gb.desktop.x11/src/systray/systray.c @@ -0,0 +1,943 @@ +/* ------------------------------- +* vim:tabstop=4:shiftwidth=4 +* main.c +* Tue, 24 Aug 2004 12:19:48 +0700 +* ------------------------------- +* main is main +* -------------------------------*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "systray.h" + +#ifdef DEBUG +#if defined(HAVE_BACKTRACE) + #include +#elif defined(HAVE_PRINTSTACK) + #include +#endif +#endif + +#include "common.h" +#include "debug.h" +#include "icons.h" +//#include "layout.h" +#include "embed.h" +#include "xembed.h" +#include "xutils.h" +#include "wmh.h" + +#ifndef NO_NATIVE_KDE +#include "kde_tray.h" +#endif + +#include "settings.h" +//#include "scrollbars.h" +#include "tray.h" + +struct TrayData tray_data; +static int tray_status_requested = 0; +#ifdef ENABLE_GRACEFUL_EXIT_HACK +static Display *async_dpy; +#endif + +static bool _refresh = FALSE; +static bool _refresh_forced = FALSE; + +static void refresh_icons() +{ + embedder_update_positions(_refresh_forced); + tray_update_window_props(); + _refresh = FALSE; + _refresh_forced = FALSE; +} + +static void refresh_icons_later(bool forced) +{ + _refresh_forced |= forced; + + if (_refresh) + return; + + _refresh = TRUE; + GB.Post((void (*)())refresh_icons, (intptr_t)0); +} + +void my_usleep(useconds_t usec) +{ + struct timeval timeout; + fd_set rfds; + FD_ZERO(&rfds); + timeout.tv_sec = 0; + timeout.tv_usec = usec; + select(1, &rfds, NULL, NULL, &timeout); +} + +/**************************** + * Signal handlers, cleanup + ***************************/ +void request_tray_status_on_signal(int sig) +{ + tray_status_requested = 1; +} + +#ifdef ENABLE_GRACEFUL_EXIT_HACK +void exit_on_signal(int sig) +{ + if (sig == SIGPIPE) { + debug_disable_output(); + } else { + psignal(sig, ""); + /* This is UGLY and is, probably, to be submitted to + * Daily WTF, but it is the only way I found not to + * use usleep in main event loop. */ + LOG_TRACE(("Sending fake WM_DELETE_WINDOW message\n")); + } + x11_send_client_msg32(async_dpy, + tray_data.tray, + tray_data.tray, + tray_data.xa_wm_protocols, + tray_data.xa_wm_delete_window, 0, 0, 0, 0); + XSync(async_dpy, False); +} +#endif + +void cleanup() +{ + static int clean = 0; + static int cleanup_in_progress = 0; + if (!clean && cleanup_in_progress) { + LOG_ERROR(("forced to die\n")); + abort(); + } + if (clean) return; + cleanup_in_progress = 1; + if (x11_connection_status()) { + LOG_TRACE(("being nice to the icons\n")); + /* Clean the list unembedding icons one by one */ + icon_list_clean(&embedder_unembed); + /* Give away the selection */ + if (tray_data.is_active) + XSetSelectionOwner(tray_data.dpy, tray_data.xa_tray_selection, None, CurrentTime); + /* Sync in order to wait until all icons finish their reparenting + * process */ + XSync(tray_data.dpy, False); + //XCloseDisplay(tray_data.dpy); + tray_data.dpy = NULL; + } + cleanup_in_progress = 0; + clean = 1; +} + +/************************************** + * Helper functions + **************************************/ + +/* Print tray status */ +void dump_tray_status() +{ + //int grid_w, grid_h; + tray_status_requested = 0; + //layout_get_size(&grid_w, &grid_h); + LOG_INFO(("----------- tray status -----------\n")); + LOG_INFO(("active: %s\n", tray_data.is_active ? "yes" : "no")); + LOG_INFO(("geometry: %dx%d+%d+%d\n", + tray_data.xsh.width, tray_data.xsh.height, + tray_data.xsh.x, tray_data.xsh.y)); + if (tray_data.xembed_data.current) + LOG_INFO(("XEMBED focus: 0x%x\n", tray_data.xembed_data.current->wid)); + else + LOG_INFO(("XEMBED focus: none\n")); + LOG_INFO(("currently managed icons: %d\n", icon_get_count())); + //icon_list_forall(&print_icon_data); + LOG_INFO(("-----------------------------------\n")); +} + +/************************************** + * (Un)embedding cycle implementation + **************************************/ + +/* Add icon to the tray */ +void add_icon(Window w, int cmode) +{ + struct TrayIcon *ti; + /* Aviod adding duplicates */ + if ((ti = icon_list_find(w)) != NULL) { + LOG_TRACE(("ignoring second request to embed 0x%x" + "(requested cmode=%d, current cmode=%d\n", + w, cmode, ti->cmode)); + return; + } + /* Dear Edsger W. Dijkstra, I see you behind my back =( */ + if ((ti = icon_list_new(w, cmode)) == NULL) goto failed0; + LOG_TRACE(("starting embedding for icon 0x%x, cmode=%d\n", w, cmode)); + x11_dump_win_info(tray_data.dpy, w); + /* Start embedding cycle */ + if (!xembed_check_support(ti)) goto failed1; + if (ti->is_xembed_supported) + ti->is_visible = xembed_get_mapped_state(ti); + else + ti->is_visible = True; + if (ti->is_visible) { + if (!embedder_reset_size(ti)) goto failed1; + //if (!layout_add(ti)) goto failed1; + } + if (!xembed_embed(ti)) goto failed1; + if (!embedder_embed(ti)) goto failed2; + + refresh_icons_later(FALSE); + + /* Report success */ + LOG_INFO(("added icon %s (wid 0x%x) as %s\n", + x11_get_window_name(tray_data.dpy, ti->wid, ""), + ti->wid, + ti->is_visible ? "visible" : "hidden")); + goto ok; +failed2: + //layout_remove(ti); +failed1: + icon_list_free(ti); +failed0: + LOG_INFO(("failed to add icon %s (wid 0x%x)\n", + x11_get_window_name(tray_data.dpy, ti->wid, ""), + ti->wid)); +ok: + if (settings.log_level >= LOG_LEVEL_TRACE) dump_tray_status(); + return; +} + +/* Remove icon from the tray */ +void remove_icon(Window w) +{ + struct TrayIcon *ti; + //char *name; + /* Ignore false alarms */ + if ((ti = icon_list_find(w)) == NULL) return; + dump_tray_status(); + embedder_unembed(ti); + xembed_unembed(ti); + //layout_remove(ti); + icon_list_free(ti); + LOG_INFO(("removed icon %s (wid 0x%x)\n", x11_get_window_name(tray_data.dpy, ti->wid, ""), w)); + + refresh_icons_later(FALSE); + + dump_tray_status(); +} + +/* Remove a destroyed icon from the tray */ +void destroy_icon(Window w) +{ + struct TrayIcon *ti; + //char *name; + /* Ignore false alarms */ + if ((ti = icon_list_find(w)) == NULL) return; + dump_tray_status(); + ti->is_destroyed = True; + embedder_unembed(ti); + xembed_unembed(ti); + //layout_remove(ti); + icon_list_free(ti); + LOG_INFO(("destroy icon (wid 0x%x)\n", w)); + + refresh_icons_later(FALSE); + + dump_tray_status(); +} + +/* Track icon visibility state changes */ +void icon_track_visibility_changes(Window w) +{ + struct TrayIcon *ti; + int mapped; + /* Ignore false alarms */ + if ((ti = icon_list_find(w)) == NULL || !ti->is_xembed_supported) return; + mapped = xembed_get_mapped_state(ti); + LOG_TRACE(("xembed_is_mapped(0x%x) = %u\n", w, mapped)); + LOG_TRACE(("is_visible = %u\n", ti->is_visible)); +#ifdef DEBUG + x11_dump_win_info(tray_data.dpy, ti->wid); +#endif + /* Nothing has changed */ + if (mapped == ti->is_visible) return; + ti->is_visible = mapped; + LOG_INFO(("%s icon 0x%x\n", mapped ? "showing" : "hiding", w)); + if (mapped) { /* Icon has become mapped and is listed as hidden. Show this icon. */ + embedder_reset_size(ti); + /*if (!layout_add(ti)) { + xembed_set_mapped_state(ti, False); + return; + }*/ + embedder_show(ti); + } else { /* Icon has become unmapped and is listed as visible. Hide this icon. */ + //layout_remove(ti); + embedder_hide(ti); + } + + refresh_icons_later(FALSE); +} + +/* helper to identify invalid icons */ +int find_invalid_icons(struct TrayIcon *ti) +{ + return ti->is_invalid; +} + +#ifndef NO_NATIVE_KDE +/* Find newly available KDE icons and add them as necessary */ +/* TODO: move to kde_tray.c */ +void kde_icons_update() +{ + unsigned long list_len, i; + Window *kde_tray_icons; + if (tray_data.kde_tray_old_mode || + !x11_get_root_winlist_prop(tray_data.dpy, tray_data.xa_kde_net_system_tray_windows, + (unsigned char **) &kde_tray_icons, &list_len)) + { + return; + } + for (i = 0; i < list_len; i++) + /* If the icon is not None and is non old, try to add it + * (if the icon is already there, nothing is gonna happen). */ + if (kde_tray_icons[i] != None && !kde_tray_is_old_icon(kde_tray_icons[i])) + { + LOG_TRACE(("found (possibly unembedded) KDE icon %s (wid 0x%x)\n", + x11_get_window_name(tray_data.dpy, kde_tray_icons[i], ""), + kde_tray_icons[i])); + add_icon(kde_tray_icons[i], CM_KDE); + } + XFree(kde_tray_icons); +} +#endif + +#define PT_MASK_SB (1L << 0) +#define PT_MASK_ALL PT_MASK_SB + +/* Perform several periodic tasks */ +void perform_periodic_tasks(int mask) +{ + struct TrayIcon *ti; + /* 1. Remove all invalid icons */ + while ((ti = icon_list_forall(&find_invalid_icons)) != NULL) { + LOG_TRACE(("icon 0x%x is invalid; removing\n", ti->wid)); + remove_icon(ti->wid); + } + /* 2. Print tray status if asked to */ + if (tray_status_requested) dump_tray_status(); + /* 3. KLUDGE to fix window size on (buggy?) WMs */ + if (settings.kludge_flags & KLUDGE_FIX_WND_SIZE) { + /* KLUDGE TODO: resolve */ + XWindowAttributes xwa; + XGetWindowAttributes(tray_data.dpy, tray_data.tray, &xwa); + if (!tray_data.is_reparented && + (xwa.width != tray_data.xsh.width || xwa.height != tray_data.xsh.height)) + { + LOG_TRACE(("KLUDGE: fixing tray window size (current: %dx%d, required: %dx%d)\n", + xwa.width, xwa.height, + tray_data.xsh.width, tray_data.xsh.height)); + tray_update_window_props(); + } + } + /* 4. run scrollbars periodic tasks */ + //if (mask & PT_MASK_SB) scrollbars_periodic_tasks(); +} + +/********************** + * Event handlers + **********************/ + +void expose(XExposeEvent ev) +{ + if (ev.window == tray_data.tray && settings.parent_bg && ev.count == 0) + tray_refresh_window(False); +} + +void visibility_notify(XVisibilityEvent ev) +{ +} + +void property_notify(XPropertyEvent ev) +{ +#define TRACE_PROPS +#if defined(DEBUG) && defined(TRACE_PROPS) + char *atom_name; + atom_name = XGetAtomName(tray_data.dpy, ev.atom); + LOG_TRACE(("atom = %s\n", atom_name)); + XFree(atom_name); +#endif + /* React on wallpaper change */ + if (ev.atom == tray_data.xa_xrootpmap_id || ev.atom == tray_data.xa_xsetroot_id) { + if (settings.transparent) tray_update_bg(True); + if (settings.parent_bg || settings.transparent || settings.fuzzy_edges) + tray_refresh_window(True); + } +#ifndef NO_NATIVE_KDE + /* React on change of list of KDE icons */ + if (ev.atom == tray_data.xa_kde_net_system_tray_windows) { + if (tray_data.is_active) + kde_icons_update(); + else + LOG_TRACE(("not updating KDE icons list: tray is not active\n")); + kde_tray_update_old_icons(tray_data.dpy); + } +#endif + /* React on WM (re)starts */ + if (ev.atom == XInternAtom(tray_data.dpy, _NET_SUPPORTING_WM_CHECK, False)) { +#ifdef DEBUG + ewmh_list_supported_atoms(tray_data.dpy); +#endif + tray_set_wm_hints(); +#ifndef NO_NATIVE_KDE + kde_tray_update_fallback_mode(tray_data.dpy); +#endif + } + /* React on _XEMBED_INFO changes of embedded icons + * (currently used to track icon visibility status) */ + if (ev.atom == tray_data.xembed_data.xa_xembed_info) { + icon_track_visibility_changes(ev.window); + } + if (ev.atom == tray_data.xa_net_client_list) { + Window *windows; + unsigned long nwindows, rc, i; + rc = x11_get_root_winlist_prop(tray_data.dpy, + tray_data.xa_net_client_list, + (unsigned char **) &windows, + &nwindows); + if (x11_ok() && rc) { + tray_data.is_reparented = True; + for(i = 0; i < nwindows; i++) + if (windows[i] == tray_data.tray) { + tray_data.is_reparented = False; + break; + } + } + if (nwindows) XFree(windows); + LOG_TRACE(("tray was %sreparented\n", tray_data.is_reparented ? "" : "not ")); + } +} + +void reparent_notify(XReparentEvent ev) +{ + struct TrayIcon *ti; + ti = icon_list_find(ev.window); + if (ti == NULL) return; + /* Reparenting out of the tray is one of non-destructive + * ways to end XEMBED protocol (see spec) */ + //if (ti->is_embedded && ti->mid_parent != ev.parent) { + if (ti->is_embedded && tray_data.tray != ev.parent) { + LOG_TRACE(("will now unembed 0x%x\n", ti->wid)); +#ifdef DEBUG + print_icon_data(ti); + x11_dump_win_info(tray_data.dpy, ev.parent); +#endif + remove_icon(ev.window); + } +} + +void client_message(XClientMessageEvent ev) +{ + int cmode = CM_FDO; + struct TrayIcon *ti; +#ifdef DEBUG + /* Print neat message(s) about this event to aid debugging */ + char *msg_type_name; + msg_type_name = XGetAtomName(tray_data.dpy, ev.message_type); + if (msg_type_name != NULL) { + LOG_TRACE(("message \"%s\"\n", msg_type_name)); + XFree(msg_type_name); + } + if (ev.message_type == tray_data.xa_wm_protocols) { + msg_type_name = XGetAtomName(tray_data.dpy, ev.data.l[0]); + if (msg_type_name != NULL) { + LOG_TRACE(("WM_PROTOCOLS message type: %s\n", msg_type_name)); + XFree(msg_type_name); + } + } +#endif + /* Graceful exit */ + if (ev.message_type == tray_data.xa_wm_protocols && + ev.data.l[0] == tray_data.xa_wm_delete_window && + ev.window == tray_data.tray) + { + LOG_TRACE(("got WM_DELETE message, will now exit\n")); + //exit(0); // atexit will call cleanup() + cleanup(); + return; + } + /* Handle _NET_SYSTEM_TRAY_* messages */ + if (ev.message_type == tray_data.xa_tray_opcode && tray_data.is_active) { + LOG_TRACE(("this is the _NET_SYSTEM_TRAY_OPCODE(%lu) message\n", ev.data.l[1])); + switch (ev.data.l[1]) { + /* This is the starting point of NET SYSTEM TRAY protocol */ + case SYSTEM_TRAY_REQUEST_DOCK: + LOG_TRACE(("dockin' requested by window 0x%x, serving in a moment\n", ev.data.l[2])); +#ifndef NO_NATIVE_KDE + if (kde_tray_check_for_icon(tray_data.dpy, ev.data.l[2])) cmode = CM_KDE; + if (kde_tray_is_old_icon(ev.data.l[2])) kde_tray_old_icons_remove(ev.data.l[2]); +#endif + add_icon(ev.data.l[2], cmode); + break; + /* We ignore these messages, since we do not show + * any baloons anyways */ + case SYSTEM_TRAY_BEGIN_MESSAGE: + case SYSTEM_TRAY_CANCEL_MESSAGE: + break; + /* Below are special cases added by this implementation */ + /* STALONETRAY_TRAY_DOCK_CONFIRMED is sent by stalonetray + * to itself. (see embed.c) */ + case STALONE_TRAY_DOCK_CONFIRMED: + ti = icon_list_find(ev.data.l[2]); + if (ti != NULL && !ti->is_embedded) { + ti->is_embedded = True; + LOG_TRACE(("embedding confirmed for icon 0x%x\n", ti->wid)); +#ifdef DEBUG + dump_tray_status(); +#endif + } + tray_update_window_props(); + break; + /* Dump tray status on request */ + case STALONE_TRAY_STATUS_REQUESTED: + dump_tray_status(); + break; + /* Find icon and scroll to it if necessary */ + case STALONE_TRAY_REMOTE_CONTROL: + ti = icon_list_find(ev.window); + if (ti == NULL) break; + //scrollbars_scroll_to(ti); +#if 0 + /* Quick hack */ + { + Window icon = ev.window; + int rc; + int x = ev.data.l[3], y = ev.data.l[4], depth = 0, idummy, i; + int btn = ev.data.l[2]; + Window win, root; + unsigned int udummy, w, h; + XGetGeometry(tray_data.dpy, icon, &root, + &idummy, &idummy, + &w, &h, &udummy, &udummy); + LOG_TRACE(("wid=0x%x w=%d h=%d\n", icon, w, h)); + x = (x == REMOTE_CLICK_POS_DEFAULT) ? w / 2 : x; + y = (y == REMOTE_CLICK_POS_DEFAULT) ? h / 2 : y; + /* 3.2. Find subwindow to execute click on */ + win = x11_find_subwindow_at(tray_data.dpy, icon, &x, &y, depth); + /* 3.3. Send mouse click(s) to target */ + LOG_TRACE(("wid=0x%x btn=%d x=%d y=%d\n", + win, btn, x, y)); +#define SEND_BTN_EVENT(press, time) do { \ + x11_send_button(tray_data.dpy, /* dispslay */ \ + press, /* event type */ \ + win, /* target window */ \ + root, /* root window */ \ + time, /* time */ \ + btn, /* button */ \ + Button1Mask << (btn - 1), /* state mask */ \ + x, /* x coord (relative) */ \ + y); /* y coord (relative) */ \ + } while (0) + for (i = 0; i < ev.data.l[0]; i++) { + SEND_BTN_EVENT(1, x11_get_server_timestamp(tray_data.dpy, tray_data.tray)); + my_usleep(250); + SEND_BTN_EVENT(0, x11_get_server_timestamp(tray_data.dpy, tray_data.tray)); + } +#undef SEND_BTN_EVENT + } +#endif + break; + default: + break; + } + } +#ifdef DEBUG + if (ev.message_type == tray_data.xa_tray_opcode && !tray_data.is_active) + LOG_TRACE(("ignoring _NET_SYSTEM_TRAY_OPCODE(%lu) message because tray is not active\n", tray_data.is_active)); +#endif +} + +void destroy_notify(XDestroyWindowEvent ev) +{ + if (!tray_data.is_active && ev.window == tray_data.old_selection_owner) { + /* Old tray selection owner was destroyed. Take over selection ownership. */ + tray_acquire_selection(); + } else if (ev.window != tray_data.tray) { + /* Try to remove icon from the tray */ + destroy_icon(ev.window); +#ifndef NO_NATIVE_KDE + } else if (kde_tray_is_old_icon(ev.window)) { + /* Since X Server may reuse window ids, remove ev.window + * from the list of old KDE icons */ + kde_tray_old_icons_remove(ev.window); +#endif + } +} + +void configure_notify(XConfigureEvent ev) +{ + struct TrayIcon *ti; + struct Point sz; + XWindowAttributes xwa; + if (ev.window == tray_data.tray) { + /* Tray window was resized */ + /* TODO: distinguish between synthetic and real configure notifies */ + /* TODO: catch rejected configure requests */ + /* XXX: Geometry stuff is a mess. Geometry + * is specified in slots, but almost always is + * stored in pixels... */ + LOG_TRACE(("tray window geometry from event: %ux%u+%d+%d\n", ev.width, ev.height, ev.x, ev.y)); + /* Sometimes, configure notifies come too late, so we fetch real geometry ourselves */ + XGetWindowAttributes(tray_data.dpy, tray_data.tray, &xwa); + x11_get_window_abs_coords(tray_data.dpy, tray_data.tray, &tray_data.xsh.x, &tray_data.xsh.y); + LOG_TRACE(("tray window geometry from X11 calls: %dx%d+%d+%d\n", xwa.width, xwa.height, tray_data.xsh.x, tray_data.xsh.y)); + tray_data.xsh.width = xwa.width; + tray_data.xsh.height = xwa.height; + /* Update icons positions */ + /* XXX: internal API is bad. example below */ + //icon_list_forall(&layout_translate_to_window); + + //embedder_update_positions(True); + refresh_icons_later(TRUE); + + /* Adjust window background if necessary */ + tray_update_bg(False); + tray_refresh_window(True); + tray_update_window_strut(); + //scrollbars_update(); + } else if ((ti = icon_list_find(ev.window)) != NULL) { /* Some icon has resized its window */ + /* KDE icons are not allowed to change their size. Reset icon size. */ + if (ti->cmode == CM_KDE || settings.kludge_flags & KLUDGE_FORCE_ICONS_SIZE) { + embedder_reset_size(ti); + return; + } + if (settings.kludge_flags & KLUDGE_FORCE_ICONS_SIZE) return; + /* Get new window size */ + if (!x11_get_window_size(tray_data.dpy, ti->wid, &sz.x, &sz.y)) { + embedder_unembed(ti); + return; + } + LOG_TRACE(("icon 0x%x was resized, new size: %ux%u, old size: %ux%u\n", ev.window, + sz.x, sz.y, ti->l.wnd_sz.x, ti->l.wnd_sz.y)); + /* Check if the size has really changed */ + if (sz.x == ti->l.wnd_sz.x && sz.y == ti->l.wnd_sz.y) return; + ti->l.wnd_sz = sz; + ti->is_resized = True; + /* Do the job */ + //layout_handle_icon_resize(ti); + embedder_refresh(ti); +#ifdef DEBUG + print_icon_data(ti); +#endif + refresh_icons_later(FALSE); +#ifdef DEBUG + dump_tray_status(); +#endif + } +} + +void selection_clear(XSelectionClearEvent ev) +{ + /* Is it _NET_SYSTEM_TRAY selection? */ + if (ev.selection == tray_data.xa_tray_selection) { + /* Is it us who has lost the selection */ + if (ev.window == tray_data.tray) { + LOG_INFO(("another tray detected; deactivating\n")); + tray_data.is_active = False; + tray_data.old_selection_owner = XGetSelectionOwner(tray_data.dpy, tray_data.xa_tray_selection); + if (!x11_ok()) { + LOG_INFO(("could not find proper new tray; reactivating\n")); + tray_acquire_selection(); + }; + LOG_TRACE(("new selection owner is 0x%x\n", tray_data.old_selection_owner)); + XSelectInput(tray_data.dpy, tray_data.old_selection_owner, StructureNotifyMask); + return; + } else if (!tray_data.is_active) { + /* Someone else has lost selection and tray is not active --- take over the selection */ + LOG_INFO(("another tray exited; reactivating\n")); + tray_acquire_selection(); + } else { + /* Just in case */ + LOG_TRACE(("WEIRD: tray is active and someone else has lost tray selection\n")); + } + } +} + +void map_notify(XMapEvent ev) +{ +#ifndef NO_NATIVE_KDE + /* Legacy scheme to handle KDE icons */ + if (tray_data.kde_tray_old_mode) { + struct TrayIcon *ti = icon_list_find_ex(ev.window); + if (ti == NULL) { + Window w = kde_tray_find_icon(tray_data.dpy, ev.window); + if (w != None) { + LOG_TRACE(("Legacy scheme for KDE icons: detected KDE icon 0x%x. Adding.\n", w)); + add_icon(w, CM_KDE); + /* TODO: remove some properties to trick ion3 so that it no longer thinks that w is a toplevel. + * Candidates for removal: + * - WM_STATE */ + } + } + } +#endif +} + +void unmap_notify(XUnmapEvent ev) +{ + struct TrayIcon *ti; + ti = icon_list_find(ev.window); + if (ti != NULL && !ti->is_invalid) { + /* KLUDGE! sometimes icons occasionally + * unmap their windows, but do _not_ destroy + * them. We map those windows back */ + /* XXX: not root caused */ + LOG_TRACE(("Unmap icons KLUDGE executed for 0x%x\n", ti->wid)); + XMapRaised(tray_data.dpy, ti->wid); + if (!x11_ok()) ti->is_invalid = True; + } +} + +/*********************************************************/ +/* main() for usual operation */ +static void tray_main(int argc, char **argv, Window window) +{ + X11_enable_event_filter(TRUE); + /* Interpret those settings that need an open display */ + //interpret_settings(); +#ifdef DEBUG + ewmh_list_supported_atoms(tray_data.dpy); +#endif + /* Create and show tray window */ + tray_create_window(argc, argv, window); + tray_acquire_selection(); + //tray_show_window(); +#ifndef NO_NATIVE_KDE + kde_tray_init(tray_data.dpy); +#endif + xembed_init(); +#ifndef NO_NATIVE_KDE + kde_icons_update(); +#endif +} + +void SYSTRAY_event_filter(XEvent *e) +{ + XEvent ev; + + if (!tray_data.dpy) + return; + + ev = *e; + + xembed_handle_event(ev); + //scrollbars_handle_event(ev); + switch (ev.type) { + case VisibilityNotify: + LOG_TRACE(("VisibilityNotify (0x%x, state=%d)\n", ev.xvisibility.window, ev.xvisibility.state)); + visibility_notify(ev.xvisibility); + break; + case Expose: + LOG_TRACE(("Expose (0x%x)\n", ev.xexpose.window)); + expose(ev.xexpose); + break; + case PropertyNotify: + LOG_TRACE(("PropertyNotify(0x%x)\n", ev.xproperty.window)); + property_notify(ev.xproperty); + break; + case DestroyNotify: + LOG_TRACE(("DestroyNotify(0x%x)\n", ev.xdestroywindow.window)); + destroy_notify(ev.xdestroywindow); + break; + case ClientMessage: + LOG_TRACE(("ClientMessage(from 0x%x?)\n", ev.xclient.window)); + client_message(ev.xclient); + break; + case ConfigureNotify: + LOG_TRACE(("ConfigureNotify(0x%x)\n", ev.xconfigure.window)); + configure_notify(ev.xconfigure); + break; + case MapNotify: + LOG_TRACE(("MapNotify(0x%x)\n", ev.xmap.window)); + map_notify(ev.xmap); + break; + case ReparentNotify: + LOG_TRACE(("ReparentNotify(0x%x to 0x%x)\n", ev.xreparent.window, ev.xreparent.parent)); + reparent_notify(ev.xreparent); + break; + case SelectionClear: + LOG_TRACE(("SelectionClear (0x%x has lost selection)\n", ev.xselectionclear.window)); + selection_clear(ev.xselectionclear); + break; + case SelectionNotify: + LOG_TRACE(("SelectionNotify\n")); + break; + case SelectionRequest: + LOG_TRACE(("SelectionRequest (from 0x%x to 0x%x)\n", ev.xselectionrequest.requestor, ev.xselectionrequest.owner)); + break; + case UnmapNotify: + LOG_TRACE(("UnmapNotify(0x%x)\n", ev.xunmap.window)); + unmap_notify(ev.xunmap); + break; + default: + #if defined(DEBUG) && defined(TRACE_EVENTS) + LOG_TRACE(("Unhandled event: %s, serial: %d, window: 0x%x\n", x11_event_names[ev.type], ev.xany.serial, ev.xany.window)); + #endif + break; + } + + // TODO: perform_periodic_tasks(PT_MASK_ALL); +#if 0 + if (tray_data.terminated) goto bailout; + /* Perform all periodic tasks but for scrollbars */ + perform_periodic_tasks(PT_MASK_ALL & (~PT_MASK_SB)); + } + perform_periodic_tasks(PT_MASK_ALL); + my_usleep(500000L); + } +bailout: + LOG_TRACE(("Clean exit\n")); + return 0; +#endif +} + +#if 0 +/* main() for controlling stalonetray remotely */ +int remote_main(int argc, char **argv) +{ + Window tray, icon = None; + int rc; + int x, y, depth = 0, idummy, i; + Window win, root; + unsigned int udummy, w, h; + tray_init_selection_atoms(); + tray_create_phony_window(); + LOG_TRACE(("name=\"%s\" btn=%d cnt=%d x=%d y=%d\n", + settings.remote_click_name, + settings.remote_click_btn, + settings.remote_click_cnt, + settings.remote_click_pos.x, + settings.remote_click_pos.y)); + tray = XGetSelectionOwner(tray_data.dpy, tray_data.xa_tray_selection); + if (tray == None) return 255; + /* 1. find window matching requested name */ + icon = x11_find_subwindow_by_name(tray_data.dpy, tray, settings.remote_click_name); + if (icon == None) return 127; + /* 2. form a message to tray requesting it to show the icon */ + rc = x11_send_client_msg32(tray_data.dpy, /* display */ + tray, /* destination */ + icon, /* window */ + tray_data.xa_tray_opcode, /* atom */ + settings.remote_click_cnt, /* data0 */ + STALONE_TRAY_REMOTE_CONTROL, /* data1 */ + settings.remote_click_btn, /* data2 */ + settings.remote_click_pos.x, /* data3 */ + settings.remote_click_pos.y /* data4 */ + ); + if (!rc) return 63; + /* 3. Execute the click */ + /* 3.1. Sort out click position */ + XGetGeometry(tray_data.dpy, icon, &root, + &idummy, &idummy, + &w, &h, &udummy, &udummy); + LOG_TRACE(("wid=0x%x w=%d h=%d\n", icon, w, h)); + x = (settings.remote_click_pos.x == REMOTE_CLICK_POS_DEFAULT) ? w / 2 : settings.remote_click_pos.x; + y = (settings.remote_click_pos.y == REMOTE_CLICK_POS_DEFAULT) ? h / 2 : settings.remote_click_pos.y; + /* 3.2. Find subwindow to execute click on */ + win = x11_find_subwindow_at(tray_data.dpy, icon, &x, &y, depth); + /* 3.3. Send mouse click(s) to target */ + LOG_TRACE(("wid=0x%x btn=%d x=%d y=%d\n", + win, settings.remote_click_btn, x, y)); +#define SEND_BTN_EVENT(press, time) do { \ + x11_send_button(tray_data.dpy, /* dispslay */ \ + press, /* event type */ \ + win, /* target window */ \ + root, /* root window */ \ + time, /* time */ \ + settings.remote_click_btn, /* button */ \ + Button1Mask << (settings.remote_click_btn - 1), /* state mask */ \ + x, /* x coord (relative) */ \ + y); /* y coord (relative) */ \ +} while (0) + for (i = 0; i < settings.remote_click_cnt; i++) { + SEND_BTN_EVENT(1, x11_get_server_timestamp(tray_data.dpy, tray_data.tray)); + my_usleep(250); + SEND_BTN_EVENT(0, x11_get_server_timestamp(tray_data.dpy, tray_data.tray)); + } +#undef SEND_BTN_EVENT + return 0; +} +#endif + +/* main() */ +void SYSTRAY_init(Display *display, Window window) +{ + /* Read settings */ + tray_init(); + + read_settings(0, NULL); + //settings.parent_bg = TRUE; + +#ifdef DEBUG + settings.log_level = LOG_LEVEL_TRACE; +#endif + /* Register cleanup and signal handlers */ + //atexit(cleanup); + //signal(SIGUSR1, &request_tray_status_on_signal); +#ifdef ENABLE_GRACEFUL_EXIT_HACK + signal(SIGINT, &exit_on_signal); + signal(SIGTERM, &exit_on_signal); + signal(SIGPIPE, &exit_on_signal); +#endif + /* Open display */ + /*if ((tray_data.dpy = XOpenDisplay(settings.display_str)) == NULL) + DIE(("could not open display\n")); + else + LOG_TRACE(("Opened dpy %p\n", tray_data.dpy));*/ + tray_data.dpy = display; + +#ifdef ENABLE_GRACEFUL_EXIT_HACK + if ((async_dpy = XOpenDisplay(settings.display_str)) == NULL) + DIE(("could not open display\n")); + else + LOG_TRACE(("Opened async dpy %p\n", async_dpy)); +#endif + if (settings.xsync) + XSynchronize(tray_data.dpy, True); + x11_trap_errors(); + /* Execute proper main() function */ + //if (settings.remote_click_name != NULL) + // return remote_main(argc, argv); + //else + tray_main(0, NULL, window); +} + +void SYSTRAY_exit() +{ + cleanup(); +} + +int SYSTRAY_get_count() +{ + return icon_get_count(); +} + +CX11SYSTRAYICON *SYSTRAY_get(int i) +{ + return icon_get(i); +} + +void SYSTRAY_refresh(void) +{ + refresh_icons_later(TRUE); +} diff --git a/gb.desktop.x11/src/systray/systray.h b/gb.desktop.x11/src/systray/systray.h new file mode 100644 index 00000000..fb3b1280 --- /dev/null +++ b/gb.desktop.x11/src/systray/systray.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + systray.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SYSTRAY_H +#define __SYSTRAY_H + +#include "config.h" +#include "../x11.h" +#include "../c_x11systray.h" +#include "../main.h" + +typedef + struct TrayIcon + CX11SYSTRAYICON; + +//#define DEBUG + +void SYSTRAY_init(Display *display, Window window); +void SYSTRAY_exit(); +void SYSTRAY_event_filter(XEvent *ev); +int SYSTRAY_get_count(); +CX11SYSTRAYICON *SYSTRAY_get(int i); +void SYSTRAY_refresh(void); + +#endif diff --git a/gb.desktop.x11/src/systray/tray.c b/gb.desktop.x11/src/systray/tray.c new file mode 100644 index 00000000..465c1322 --- /dev/null +++ b/gb.desktop.x11/src/systray/tray.c @@ -0,0 +1,732 @@ +/* ************************************ + * vim:tabstop=4:shiftwidth=4:cindent:fen:fdm=syntax + * tray.c + * Tue, 07 Mar 2006 10:36:10 +0600 + * ************************************ + * tray functions + * ************************************/ + +#include "systray.h" + +#include +#include +#include +#include + +#ifdef XPM_SUPPORTED +#include +#endif + +#include + +#include "common.h" +#include "tray.h" +#include "settings.h" +#include "xutils.h" +#include "wmh.h" +#include "embed.h" +#include "icons.h" + +#ifndef NO_NATIVE_KDE +#include "kde_tray.h" +#endif + +#include "debug.h" + +void tray_init() +{ + tray_data.tray = None; + tray_data.hint_win = None; + tray_data.dpy = NULL; + tray_data.terminated = False; + tray_data.bg_pmap = None; + tray_data.xa_xrootpmap_id = None; + tray_data.xa_xsetroot_id = None; + tray_data.kde_tray_old_mode = 0; + //scrollbars_init(); +} + +#ifdef XPM_SUPPORTED +int tray_init_pixmap_bg() +{ + XpmAttributes xpma; + Pixmap mask = None; + int rc; + xpma.valuemask = 0; + rc = XpmReadFileToPixmap( + tray_data.dpy, + tray_data.tray, + settings.bg_pmap_path, + &tray_data.bg_pmap, + &mask, + &xpma); + if (rc != XpmSuccess) { + DIE(("Could not read background pixmap from %s.\n", settings.bg_pmap_path)); + return FAILURE; + } + /* Ignore the mask */ + if (mask != None) XFreePixmap(tray_data.dpy, mask); + tray_data.bg_pmap_dims.x = xpma.height; + tray_data.bg_pmap_dims.y = xpma.width; + LOG_TRACE(("created background pixmap (%dx%d)\n", xpma.width, xpma.height)); + return SUCCESS; +} +#endif + +/* Mostly from FVWM:fvwm/colorset.c:172 */ +Pixmap tray_get_root_pixmap(Atom prop) +{ + Atom type; + int format; + unsigned long length, after; + unsigned char *reteval = NULL; + int ret; + Pixmap pix = None; + Window root_window = XRootWindow(tray_data.dpy, DefaultScreen(tray_data.dpy)); + ret = XGetWindowProperty(tray_data.dpy, + root_window, + prop, + 0L, + 1L, + False, + XA_PIXMAP, + &type, + &format, + &length, + &after, + &reteval); + if (x11_ok() && ret == Success && type == XA_PIXMAP && format == 32 && length == 1 && after == 0) + pix = (Pixmap)(*(long *)reteval); + if (reteval) XFree(reteval); + return pix; +} + +/* Originally from FVWM:fvwm/colorset.c:195 */ +int tray_update_root_bg_pmap(Pixmap *pmap, int *width, int *height) +{ + unsigned int w = 0, h = 0; + int rc = 0; + XID dummy; + Pixmap pix = None; + /* Retrive root image pixmap */ + /* Try _XROOTPMAP_ID first */ + if (tray_data.xa_xrootpmap_id != None) + pix = tray_get_root_pixmap(tray_data.xa_xrootpmap_id); + /* Else try _XSETROOT_ID */ + if (!pix && tray_data.xa_xsetroot_id != None) + pix = tray_get_root_pixmap(tray_data.xa_xsetroot_id); + /* Get root pixmap size */ + if (pix) + rc = XGetGeometry(tray_data.dpy, + pix, + &dummy, + (int *)&dummy, + (int *)&dummy, + &w, + &h, + (unsigned int *)&dummy, + (unsigned int *)&dummy); + if (!x11_ok() || !pix || !rc) { + LOG_TRACE(("could not update root background pixmap\n")); + return FAILURE; + } + *pmap = pix; + if (width != NULL) *width = w; + if (height != NULL) *height = h; + LOG_TRACE(("got new root pixmap: 0x%lx (%ix%i)\n", pix, w, h)); + return SUCCESS; +} + +/* XXX: most of Pixmaps are not really needed. Use XImages instead. + * GCs, XImages and Pixmaps stay allocated during cleanup, valgrind + * complains. Who cares? + * TODO: move pixmaps/GCs into tray_data and free them during the exit. */ +int tray_update_bg(int update_pixmap) +{ + static Pixmap root_pmap = None; + static Pixmap bg_pmap = None; + static Pixmap final_pmap = None; + static GC root_gc = None; + static GC bg_gc = None; + static GC final_gc = None; + static int old_width = -1, old_height = -1; + struct Rect clr; /* clipping rectangle */ + //struct Rect clr_loc; /* clipping rectangle in local coords */ + XImage *bg_img; + //int bg_pmap_updated = False; +#define make_gc(pmap,gc) do { XGCValues gcv; \ + if (gc != None) XFreeGC(tray_data.dpy, gc); \ + gcv.graphics_exposures = False; \ + gc = XCreateGC(tray_data.dpy, pmap, GCGraphicsExposures, &gcv); \ + } while(0) +#define make_pmap(pmap) do { \ + if (pmap != None) XFreePixmap(tray_data.dpy, pmap); \ + pmap = XCreatePixmap(tray_data.dpy, tray_data.tray, \ + tray_data.xsh.width, tray_data.xsh.height, \ + DefaultDepth(tray_data.dpy, DefaultScreen(tray_data.dpy))); \ + } while(0) +#define recreate_pixmap(pmap,gc) do { \ + make_pmap(pmap); \ + make_gc(pmap, gc); \ + } while(0) + /* No need to update background if it is a color one */ + if (!settings.transparent && !settings.pixmap_bg) return SUCCESS; + /* Calculate clipping rect */ + clr.x = cutoff(tray_data.xsh.x, 0, tray_data.root_wnd.width); + clr.y = cutoff(tray_data.xsh.y, 0, tray_data.root_wnd.height); + clr.w = cutoff(tray_data.xsh.x + tray_data.xsh.width, 0, tray_data.root_wnd.width) - clr.x; + clr.h = cutoff(tray_data.xsh.y + tray_data.xsh.height, 0, tray_data.root_wnd.height) - clr.y; + /* There's no need to update background if tray is not visible */ + /* TODO: visibility is better to be tracked using some events */ + if (clr.w == 0 || clr.h == 0) return SUCCESS; + /* Calculate local clipping rect */ + /*clr_loc.x = clr.x - tray_data.xsh.x; + clr_loc.y = clr.y - tray_data.xsh.y; + clr_loc.w = clr.w; + clr_loc.h = clr.h;*/ + if (old_width != tray_data.xsh.width || old_height != tray_data.xsh.height || final_pmap == None) + recreate_pixmap(final_pmap, final_gc); + /* Update root pixmap if asked and necessary */ + if ((root_pmap == None || update_pixmap) && + (settings.transparent || settings.fuzzy_edges)) + { + if (!tray_update_root_bg_pmap(&root_pmap, NULL, NULL)) { + /* More gracefull solution */ + LOG_TRACE(("still waiting for root pixmap\n")); + return SUCCESS; + } else + make_gc(root_pmap, root_gc); + } + /* We don't have to update background if only window position has + * changed unless we depend on root pixmap */ + if (tray_data.xsh.width == old_width && tray_data.xsh.width == old_height && + !settings.transparent && !settings.fuzzy_edges) + { + return SUCCESS; + } + /* If pixmap bg is used, bg_pmap holds tinted and tiled background pixmap, + * so there's no need to update it unless window size has changed */ + if (settings.pixmap_bg && (bg_pmap == None || + old_width != tray_data.xsh.width || old_height != tray_data.xsh.height)) + { + int i, j; + recreate_pixmap(bg_pmap, bg_gc); + for (i = 0; i < tray_data.xsh.width / tray_data.bg_pmap_dims.x + 1; i++) + for (j = 0; j < tray_data.xsh.height / tray_data.bg_pmap_dims.y + 1; j++) { + XCopyArea(tray_data.dpy, tray_data.bg_pmap, bg_pmap, bg_gc, + 0, 0, tray_data.bg_pmap_dims.x, tray_data.bg_pmap_dims.y, + i * tray_data.bg_pmap_dims.x, j * tray_data.bg_pmap_dims.y); + } + //bg_pmap_updated = True; + } else + /* If root transparency is enabled, it is neccessary to copy portion of + * root pixmap under the window (root_pmap) to bg_pmap */ + /* XXX: must correctly work around situations when bg pixmap is smaller than root window (but how?) */ + if (settings.transparent) { + recreate_pixmap(bg_pmap, bg_gc); + XCopyArea(tray_data.dpy, root_pmap, bg_pmap, bg_gc, + tray_data.xsh.x, tray_data.xsh.y, + tray_data.xsh.width, tray_data.xsh.height, + 0, 0); + } + /* Create an XImage from bg_pmap */ + bg_img = XGetImage(tray_data.dpy, bg_pmap, + 0, 0, tray_data.xsh.width, tray_data.xsh.height, + XAllPlanes(), ZPixmap); + if (bg_img == NULL) return FAILURE; +#if 0 + /* Tint the image if necessary. If bg_pmap was not updated, tinting + * is not needed, since it has been already done */ + if (settings.tint_level && ((settings.pixmap_bg && bg_pmap_updated) || settings.transparent)) { + image_tint(bg_img, &settings.tint_color, settings.tint_level); + XPutImage(tray_data.dpy, bg_pmap, bg_gc, bg_img, + 0, 0, + 0, 0, tray_data.xsh.width, tray_data.xsh.height); + bg_pmap_updated = False; + } +#endif +#if 0 + /* Apply fuzzy edges */ + /* XXX: THIS IS UGLY */ + if (settings.fuzzy_edges) { + static CARD8 *alpha_mask = NULL; + /* Portion of root pixmap under the tray */ + XImage *root_img = XGetImage(tray_data.dpy, root_pmap, + clr.x, clr.y, clr.w, clr.h, + XAllPlanes(), ZPixmap); + /* Proxy structure to work on */ + XImage *tmp_img = NULL; + static Pixmap tmp_pmap = None; + static GC tmp_gc = None; + if (root_img == NULL) { + LOG_ERROR(("Failed to get image of root pixmap under tray window\n")); + LOG_TRACE(("Clipping rectangle: %dx%d+%d+%d\n", clr.w, clr.h, clr.x, clr.y)); + return x11_ok(); + } + /* Alpha mask needs to be updated only on size changes */ + if (old_width != tray_data.xsh.width || old_height != tray_data.xsh.height) { + recreate_pixmap(tmp_pmap, tmp_gc); + if (alpha_mask != NULL) free(alpha_mask); + alpha_mask = image_create_alpha_mask(settings.fuzzy_edges, tray_data.xsh.width, tray_data.xsh.height); + } + XPutImage(tray_data.dpy, tmp_pmap, tmp_gc, root_img, + clr_loc.x, clr_loc.y, + 0, 0, clr_loc.w, clr_loc.h); + tmp_img = XGetImage(tray_data.dpy, tmp_pmap, + 0, 0, tray_data.xsh.width, tray_data.xsh.height, + XAllPlanes(), ZPixmap); + if (alpha_mask != NULL && tmp_img != NULL) image_compose(bg_img, tmp_img, alpha_mask); + XDestroyImage(root_img); + if (tmp_img != NULL) XDestroyImage(tmp_img); + } +#endif + XPutImage(tray_data.dpy, final_pmap, final_gc, bg_img, + 0, 0, + 0, 0, tray_data.xsh.width, tray_data.xsh.height); + XSetWindowBackgroundPixmap(tray_data.dpy, tray_data.tray, final_pmap); + XDestroyImage(bg_img); + old_width = tray_data.xsh.width; + old_height = tray_data.xsh.height; + RETURN_STATUS(x11_ok()); +} + +void tray_refresh_window(int exposures) +{ + LOG_TRACE(("refreshing tray window\n")); + icon_list_forall(&embedder_refresh); + x11_refresh_window(tray_data.dpy, tray_data.tray, tray_data.xsh.width, tray_data.xsh.height, exposures); + //scrollbars_refresh(exposures); +} + +#if 0 +int tray_calc_window_size(int base_width, int base_height, int *wnd_width, int *wnd_height) +{ + *wnd_width = base_width; + *wnd_height = base_height; + if (settings.scrollbars_mode & SB_MODE_HORZ) *wnd_width += settings.scrollbars_size * 2; + if (settings.scrollbars_mode & SB_MODE_VERT) *wnd_height += settings.scrollbars_size * 2; + return SUCCESS; +} + +int tray_calc_tray_area_size(int wnd_width, int wnd_height, int *base_width, int *base_height) +{ + *base_width = wnd_width; + *base_height = wnd_height; + if (settings.scrollbars_mode & SB_MODE_HORZ) *base_width -= settings.scrollbars_size * 2; + if (settings.scrollbars_mode & SB_MODE_VERT) *base_height -= settings.scrollbars_size * 2; + return SUCCESS; +} +#endif + +int tray_update_window_strut() +{ +#if 0 + /* Set window strut */ + if (settings.wm_strut_mode != WM_STRUT_NONE) { + int strut_mode; + if (settings.wm_strut_mode == WM_STRUT_AUTO) { + /* Do autodetection: if some edge of tray is adjacent to one + * of screen edges, we could set window strut to that */ + int h_strut_mode, v_strut_mode; + int p_strut_mode, s_strut_mode; + h_strut_mode = (tray_data.xsh.x == 0 ? WM_STRUT_LFT : + (tray_data.xsh.x + tray_data.xsh.width == tray_data.root_wnd.width ? WM_STRUT_RHT : + WM_STRUT_NONE)); + v_strut_mode = (tray_data.xsh.y == 0 ? WM_STRUT_TOP : + (tray_data.xsh.x + tray_data.xsh.height == tray_data.root_wnd.height ? WM_STRUT_BOT : + WM_STRUT_NONE)); + /* If tray is vertical, horizontal strut mode has higher priority, + * else vertical strut mode has higher priority */ + if (settings.vertical) { + p_strut_mode = h_strut_mode; + s_strut_mode = v_strut_mode; + } else { + p_strut_mode = v_strut_mode; + s_strut_mode = h_strut_mode; + } + if (p_strut_mode != WM_STRUT_NONE) + strut_mode = p_strut_mode; + else + strut_mode = s_strut_mode; + } else + strut_mode = settings.wm_strut_mode; + LOG_TRACE(("computed final strut mode: %d\n", strut_mode)); + /* Update respective window hint */ + if (strut_mode != WM_STRUT_NONE) { + wm_strut_t wm_strut; + int base_idx; + memset(wm_strut, 0, sizeof(wm_strut)); + LOG_TRACE(("current tray geometry: %dx%d+%d+%d\n", + tray_data.xsh.width, tray_data.xsh.height, + tray_data.xsh.x, tray_data.xsh.y)); + if (strut_mode == WM_STRUT_TOP || strut_mode == WM_STRUT_BOT) { + if (strut_mode == WM_STRUT_TOP) { + base_idx = WM_STRUT_IDX_TOP; + wm_strut[WM_STRUT_IDX_TOP] = tray_data.xsh.y + tray_data.xsh.height; + } else { + base_idx = WM_STRUT_IDX_BOT; + wm_strut[WM_STRUT_IDX_BOT] = tray_data.root_wnd.height - tray_data.xsh.y; + } + wm_strut[base_idx + WM_STRUT_IDX_START_OFFSET] = tray_data.xsh.x; + wm_strut[base_idx + WM_STRUT_IDX_END_OFFSET] = tray_data.xsh.x + tray_data.xsh.width; + } else { + if (strut_mode == WM_STRUT_LFT) { + base_idx = WM_STRUT_IDX_LFT; + wm_strut[WM_STRUT_IDX_LFT] = tray_data.xsh.x + tray_data.xsh.width; + } else { + base_idx = WM_STRUT_IDX_RHT; + wm_strut[WM_STRUT_IDX_RHT] = tray_data.root_wnd.width - tray_data.xsh.x; + } + wm_strut[base_idx + WM_STRUT_IDX_START_OFFSET] = tray_data.xsh.y; + wm_strut[base_idx + WM_STRUT_IDX_END_OFFSET] = tray_data.xsh.y + tray_data.xsh.height; + } + { int i; + for (i = 0; i < _NET_WM_STRUT_PARTIAL_SZ; i++) + LOG_TRACE(("computed hints [%d] = %d\n", i, wm_strut[i])); + } + ewmh_set_window_strut(tray_data.dpy, tray_data.tray, wm_strut); + } + } +#endif + return SUCCESS; +} + +int tray_update_window_props() +{ +#if 0 + XSizeHints xsh; + int cur_base_width, cur_base_height; + int new_width, new_height; + int layout_width, layout_height; + /* Phase 1: calculate new tray window dimensions. + * Algorithm summary: + * new_dims = + * if (layout_dims > max_dims) max_dims; + * else if ((shrink_back && layout_dims > orig_dims) || + * (layout_dims > current_dims)) layout_dims; + * else if (shrink_back) orig_dims; + * else current_dims; + */ + x11_get_window_size(tray_data.dpy, tray_data.tray, + &tray_data.xsh.width, &tray_data.xsh.height); + layout_get_size(&layout_width, &layout_height); + LOG_TRACE(("layout geometry: %dx%d\n", layout_width, layout_height)); + tray_calc_tray_area_size(tray_data.xsh.width, tray_data.xsh.height, + &cur_base_width, &cur_base_height); + LOG_TRACE(("base tray geometry: %dx%d\n", cur_base_width, cur_base_height)); + LOG_TRACE(("orig tray geometry: %dx%d\n", settings.orig_tray_dims.x, settings.orig_tray_dims.y)); + LOG_TRACE(("max tray geometry: %dx%d\n", settings.max_tray_dims.x, settings.max_tray_dims.y)); +#define CALC_DIM(tgt,cur,layout,max,orig) \ + if (layout > max) tgt = max; \ + else if ((settings.shrink_back_mode && layout > orig) || layout > cur) tgt = layout; \ + else if (settings.shrink_back_mode) tgt = orig; \ + else tgt = cur; + CALC_DIM(new_width, cur_base_width, layout_width, + settings.max_tray_dims.x, settings.orig_tray_dims.x); + CALC_DIM(new_height, cur_base_height, layout_height, + settings.max_tray_dims.y, settings.orig_tray_dims.y); + LOG_TRACE(("new base tray geometry: %dx%d\n", new_width, new_height)); + tray_calc_window_size(new_width, new_height, &new_width, &new_height); + LOG_TRACE(("new tray geometry: %dx%d\n", new_width, new_height)); +#if 1 + /* Not sure if this is really necessary */ + xsh.x = tray_data.xsh.x; + xsh.y = tray_data.xsh.y; + xsh.width = new_width; + xsh.height = new_height; +#endif + xsh.min_width = new_width; + xsh.min_height = new_height; + xsh.max_width = new_width; + xsh.max_height = new_height; + xsh.width_inc = settings.slot_size; + xsh.height_inc = settings.slot_size; + tray_calc_window_size(0, 0, &xsh.base_width, &xsh.base_height); + xsh.win_gravity = NorthWestGravity; + xsh.flags = tray_data.xsh.flags; + XSetWMNormalHints(tray_data.dpy, tray_data.tray, &xsh); + /* Phase 2: set new window size + * This check helps to avod extra (erroneous) moves of the window when + * geometry changes are not updated yet, but tray_update_window_props() was + * called once again */ + if (new_width != tray_data.xsh.width || new_height != tray_data.xsh.height) { + /* Apparently, not every WM (hello, WindowMaker!) handles gravity the + * way I have expected (i.e. using it to calculate reference point as + * described in ICCM/WM specs). Perhaps, I was dreaming. So, prior to + * resizing trays window, it is necessary to recalculate window + * absolute position and shift it according to grow gravity settings */ + x11_get_window_abs_coords(tray_data.dpy, tray_data.tray, + &tray_data.xsh.x, &tray_data.xsh.y); + LOG_TRACE(("old tray window geometry: %dx%d+%d+%d\n", new_width, new_height, + tray_data.xsh.x, tray_data.xsh.y)); + if (settings.grow_gravity & GRAV_E) + tray_data.xsh.x -= new_width - tray_data.xsh.width; + else if (!(settings.grow_gravity & GRAV_H)) + tray_data.xsh.x -= (new_width - tray_data.xsh.width) / 2; + if (settings.grow_gravity & GRAV_S) + tray_data.xsh.y -= new_height - tray_data.xsh.height; + else if (!(settings.grow_gravity & GRAV_V)) + tray_data.xsh.y -= (new_height - tray_data.xsh.height) / 2; + tray_data.xsh.width = new_width; + tray_data.xsh.height = new_height; + LOG_TRACE(("new tray window geometry: %dx%d+%d+%d\n", new_width, new_height, + tray_data.xsh.x, tray_data.xsh.y)); + XResizeWindow(tray_data.dpy, tray_data.tray, new_width, new_height); + XMoveWindow(tray_data.dpy, tray_data.tray, tray_data.xsh.x, tray_data.xsh.y); + if (!x11_ok()) { + LOG_TRACE(("could not update tray window geometry\n")); + return FAILURE; + } + } else { + /* XXX: Why do we need this again? */ + XResizeWindow(tray_data.dpy, tray_data.tray, + tray_data.xsh.width, tray_data.xsh.height); + } + tray_update_window_strut(); + scrollbars_update(); +#endif + return SUCCESS; +} + +void tray_create_window(int argc, char **argv, Window window) +{ + //XTextProperty wm_name; + //XSetWindowAttributes xswa; + //XClassHint xch; + //XWMHints xwmh; + Atom net_system_tray_orientation; + Atom net_system_tray_visual; + Atom orient; + Atom protocols_atoms[2]; + VisualID visual; + + /* Create some atoms */ + tray_data.xa_wm_delete_window = + XInternAtom(tray_data.dpy, "WM_DELETE_WINDOW", False); + tray_data.xa_wm_take_focus = + XInternAtom(tray_data.dpy, "WM_TAKE_FOCUS", False); + tray_data.xa_wm_protocols = + XInternAtom(tray_data.dpy, "WM_PROTOCOLS", False); + tray_data.xa_kde_net_system_tray_windows = + XInternAtom(tray_data.dpy, "_KDE_NET_SYSTEM_TRAY_WINDOWS", False); + tray_data.xa_net_client_list = + XInternAtom(tray_data.dpy, "_NET_CLIENT_LIST", False); +#if 0 + /* Create tray window */ + tray_data.tray = XCreateSimpleWindow( + tray_data.dpy, + DefaultRootWindow(tray_data.dpy), + tray_data.xsh.x, tray_data.xsh.y, + tray_data.xsh.width, tray_data.xsh.height, + 0, + settings.bg_color.pixel, + settings.bg_color.pixel); + LOG_TRACE(("created tray window: 0x%x\n", tray_data.tray)); + if (settings.dockapp_mode == DOCKAPP_WMAKER) { + tray_data.hint_win = XCreateSimpleWindow( + tray_data.dpy, + DefaultRootWindow(tray_data.dpy), + 0, 0, + 1, 1, + 0, + settings.bg_color.pixel, + settings.bg_color.pixel); + LOG_TRACE(("created hint_win window: 0x%x\n", tray_data.hint_win)); + } + /* Set tray window properties */ + xswa.bit_gravity = settings.bit_gravity; + xswa.win_gravity = settings.win_gravity; + xswa.backing_store = settings.parent_bg ? NotUseful : WhenMapped; + XChangeWindowAttributes(tray_data.dpy, + tray_data.tray, + CWBitGravity | CWWinGravity | CWBackingStore, + &xswa); + { + /* XXX: use XStoreName ?*/ + int numtries = 0; + /* First, try user-supplied value */ + while (1) { + if (XmbTextListToTextProperty(tray_data.dpy, &settings.wnd_name, 1, XTextStyle, &wm_name) != Success) + /* Retry with default value */ + settings.wnd_name = PROGNAME; + else + break; + if (numtries++ > 1) DIE(("Invalid window name \"%s\"\n", settings.wnd_name)); + } + } + XSetWMName(tray_data.dpy, tray_data.tray, &wm_name); + XFree(wm_name.value); + /* Setup class hints */ + xch.res_class = PROGNAME; + xch.res_name = PROGNAME; + /* Setup window manager hints */ + xwmh.flags = StateHint | InputHint; + xwmh.input = False; + xwmh.initial_state = settings.dockapp_mode != DOCKAPP_NONE ? WithdrawnState: NormalState; + /* Apply hints */ + XSetWMHints(tray_data.dpy, tray_data.tray, &xwmh); + XSetClassHint(tray_data.dpy, tray_data.tray, &xch); + XSetWMNormalHints(tray_data.dpy, tray_data.tray, &tray_data.xsh); + XSetCommand(tray_data.dpy, tray_data.tray, argv, argc); + /* Set properties of hint window if WindowMaker dockapp mode enabled */ + if (settings.dockapp_mode == DOCKAPP_WMAKER) { + xwmh.flags |= IconWindowHint | IconPositionHint | WindowGroupHint; + xwmh.initial_state = WithdrawnState; + xwmh.icon_x = tray_data.xsh.x; + xwmh.icon_y = tray_data.xsh.y; + xwmh.icon_window = tray_data.tray; + xwmh.window_group = tray_data.hint_win; + XSetClassHint(tray_data.dpy, tray_data.hint_win, &xch); + XSetWMHints(tray_data.dpy, tray_data.hint_win, &xwmh); + } +#endif + + tray_data.tray = window; + //X11_get_window_geometry(window, &tray_data.xsh.x, &tray_data.xsh.y, &tray_data.xsh.width, &tray_data.xsh.height); + + // TODO + /* v0.2 tray protocol support */ + orient = + settings.vertical ? _NET_SYSTEM_TRAY_ORIENTATION_HORZ : _NET_SYSTEM_TRAY_ORIENTATION_VERT; + net_system_tray_orientation = XInternAtom(tray_data.dpy, TRAY_ORIENTATION_ATOM, False); + XChangeProperty(tray_data.dpy, tray_data.tray, + net_system_tray_orientation, net_system_tray_orientation, 32, + PropModeReplace, + (unsigned char *) &orient, 1); + + XWindowAttributes attr; + XGetWindowAttributes(tray_data.dpy, tray_data.tray, &attr); + + net_system_tray_visual = XInternAtom(tray_data.dpy, "_NET_SYSTEM_TRAY_VISUAL", False); + visual = XVisualIDFromVisual(attr.visual); + + XChangeProperty(tray_data.dpy, tray_data.tray, + net_system_tray_visual, XA_VISUALID, 32, PropModeReplace, + (unsigned char *)&visual, 1); + + /* Ask X server / WM to report certain events */ + protocols_atoms[0] = tray_data.xa_wm_delete_window; + protocols_atoms[1] = tray_data.xa_wm_take_focus; + XSetWMProtocols(tray_data.dpy, tray_data.tray, protocols_atoms, 2); + XSelectInput(tray_data.dpy, tray_data.tray, + StructureNotifyMask | FocusChangeMask | PropertyChangeMask | ExposureMask ); + x11_extend_root_event_mask(tray_data.dpy, PropertyChangeMask); + //scrollbars_create(); + /* Set tray window background if necessary */ +#if 0 +#ifdef XPM_SUPPORTED + if (settings.pixmap_bg) tray_init_pixmap_bg(); +#endif + if (settings.parent_bg) + XSetWindowBackgroundPixmap(tray_data.dpy, tray_data.tray, ParentRelative); + else if (settings.transparent || settings.fuzzy_edges) { + tray_data.xa_xrootpmap_id = XInternAtom(tray_data.dpy, "_XROOTPMAP_ID", False); + tray_data.xa_xsetroot_id = XInternAtom(tray_data.dpy, "_XSETROOT_ID", False); + } + tray_update_bg(True); +#endif +} + +#if 0 +void tray_create_phony_window() +{ + /* Create fake tray window */ + tray_data.tray = XCreateSimpleWindow( + tray_data.dpy, + DefaultRootWindow(tray_data.dpy), + 0, 0, 1, 1, + 0, + 0, 0); + /* Select for PropertyNotify so that x11_get_server_timestamp() works */ + XSelectInput(tray_data.dpy, tray_data.tray, PropertyChangeMask); +} +#endif + +int tray_set_wm_hints() +{ +#if 0 + int mwm_decor = 0; + if (settings.deco_flags & DECO_TITLE) + mwm_decor |= MWM_DECOR_TITLE | MWM_DECOR_MENU; + if (settings.deco_flags & DECO_BORDER) + mwm_decor |= MWM_DECOR_RESIZEH | MWM_DECOR_BORDER; + mwm_set_hints(tray_data.dpy, tray_data.tray, mwm_decor, MWM_FUNC_ALL); +#endif + if (settings.sticky) { + ewmh_add_window_state(tray_data.dpy, tray_data.tray, _NET_WM_STATE_STICKY); + ewmh_set_window_atom32(tray_data.dpy, tray_data.tray, _NET_WM_DESKTOP, 0xFFFFFFFF); + } + if (settings.skip_taskbar) + ewmh_add_window_state(tray_data.dpy, tray_data.tray, _NET_WM_STATE_SKIP_TASKBAR); + if (settings.wnd_layer != NULL) + ewmh_add_window_state(tray_data.dpy, tray_data.tray, settings.wnd_layer); + if (strcmp(settings.wnd_type, _NET_WM_WINDOW_TYPE_NORMAL) != 0) + ewmh_add_window_type(tray_data.dpy, tray_data.tray, settings.wnd_type); + /* Alwas add NORMAL window type for WM that do not support (some) special types */ + ewmh_add_window_type(tray_data.dpy, tray_data.tray, _NET_WM_WINDOW_TYPE_NORMAL); + return SUCCESS; +} + +void tray_init_selection_atoms() +{ + static char *tray_sel_atom_name = NULL; + /* Obtain selection atom name basing on current screen number */ + if (tray_sel_atom_name == NULL) { + tray_sel_atom_name = (char *)malloc(strlen(TRAY_SEL_ATOM) + 10); + if (tray_sel_atom_name == NULL) DIE_OOM(("could not allocate memory for selection atom name\n")); + snprintf(tray_sel_atom_name, + strlen(TRAY_SEL_ATOM) + 10, + "%s%u", + TRAY_SEL_ATOM, + DefaultScreen(tray_data.dpy)); + } + LOG_TRACE(("tray_sel_atom_name=%s\n", tray_sel_atom_name)); + /* Initialize atom values */ + tray_data.xa_tray_selection = XInternAtom(tray_data.dpy, tray_sel_atom_name, False); + tray_data.xa_tray_opcode = XInternAtom(tray_data.dpy, "_NET_SYSTEM_TRAY_OPCODE", False); + tray_data.xa_tray_data = XInternAtom(tray_data.dpy, "_NET_SYSTEM_TRAY_MESSAGE_DATA", False); +} + +void tray_acquire_selection() +{ + Time timestamp = x11_get_server_timestamp(tray_data.dpy, tray_data.tray); + tray_init_selection_atoms(); + /* Save old selection owner */ + tray_data.old_selection_owner = XGetSelectionOwner(tray_data.dpy, tray_data.xa_tray_selection); + LOG_TRACE(("old selection owner: 0x%x\n", tray_data.old_selection_owner)); + /* Acquire selection */ + XSetSelectionOwner(tray_data.dpy, tray_data.xa_tray_selection, + tray_data.tray, timestamp); + /* Check if we have really got the selection */ + if (XGetSelectionOwner(tray_data.dpy, tray_data.xa_tray_selection) != tray_data.tray) { + DIE(("could not set selection owner.\nMay be another (greedy) tray running?\n")); + } else { + tray_data.is_active = True; + LOG_TRACE(("ok, got _NET_SYSTEM_TRAY selection\n")); + } + /* Send the message notifying about new MANAGER */ + x11_send_client_msg32(tray_data.dpy, DefaultRootWindow(tray_data.dpy), + DefaultRootWindow(tray_data.dpy), + XInternAtom(tray_data.dpy, "MANAGER", False), + timestamp, + tray_data.xa_tray_selection, tray_data.tray, 0, 0); +} + +#if 0 +void tray_show_window() +{ + tray_set_wm_hints(); + tray_update_window_props(); + XMapRaised(tray_data.dpy, tray_data.tray); + if (settings.dockapp_mode == DOCKAPP_NONE) + XMoveWindow(tray_data.dpy, tray_data.tray, tray_data.xsh.x, tray_data.xsh.y); + if (settings.dockapp_mode == DOCKAPP_WMAKER) + XMapWindow(tray_data.dpy, tray_data.hint_win); + /* XXX: I do not why, but for some WM it is + * required to set hints / window properties + * after and before window creation */ + /* TODO: check if this is really necessary */ + tray_set_wm_hints(); + tray_update_window_props(); +} +#endif diff --git a/gb.desktop.x11/src/systray/tray.h b/gb.desktop.x11/src/systray/tray.h new file mode 100644 index 00000000..0772a49f --- /dev/null +++ b/gb.desktop.x11/src/systray/tray.h @@ -0,0 +1,141 @@ +/* ------------------------------- + * vim:tabstop=4:shiftwidth=4 + * tray.h + * Wed, 29 Sep 2004 23:10:02 +0700 + * ------------------------------- + * Common tray routines + * -------------------------------*/ + +#ifndef _TRAY_H_ +#define _TRAY_H_ + +#include +#include +#include + +#include + +#include "systray.h" +#include "common.h" +#include "icons.h" +//#include "scrollbars.h" +#include "xembed.h" + +/* Tray opcode messages from System Tray Protocol Specification + * http:freedesktop.org/Standards/systemtray-spec/systemtray-spec-0.2.html */ +#define SYSTEM_TRAY_REQUEST_DOCK 0 +/* These two are unused */ +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 +/* Custom message: remote control */ +#define STALONE_TRAY_REMOTE_CONTROL 0xFFFD +/* Custom message: request for status */ +#define STALONE_TRAY_STATUS_REQUESTED 0xFFFE +/* Custom message: confirmation of embedding */ +#define STALONE_TRAY_DOCK_CONFIRMED 0xFFFF +/* Name of tray selection atom */ +#define TRAY_SEL_ATOM "_NET_SYSTEM_TRAY_S" +/* Name of tray orientation atom*/ +#define TRAY_ORIENTATION_ATOM "_NET_SYSTEM_TRAY_ORIENTATION" +/* Name of tray orientation atom*/ +#define STALONETRAY_REMOTE_ATOM "STALONETRAY_REMOTE" +/* Values of tray orientation property */ +#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 +#define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1 + +/* Window decoration flags */ +#define DECO_BORDER (1 << 0) +#define DECO_TITLE (1 << 1) +#define DECO_NONE 0 +#define DECO_ALL (DECO_BORDER | DECO_TITLE) + +/* WM struts flags */ +#define WM_STRUT_NONE 0 +#define WM_STRUT_LFT 1 +#define WM_STRUT_RHT 2 +#define WM_STRUT_TOP 3 +#define WM_STRUT_BOT 4 +#define WM_STRUT_AUTO 5 + +/* Dockapp modes */ +#define DOCKAPP_NONE 0 +#define DOCKAPP_SIMPLE 1 +#define DOCKAPP_WMAKER 2 + +/* Kludge flags */ +#define KLUDGE_FIX_WND_SIZE (1L << 1) +#define KLUDGE_FIX_WND_POS (1L << 2) +#define KLUDGE_USE_ICONS_HINTS (1L << 3) +#define KLUDGE_FORCE_ICONS_SIZE (1L << 3) + +/* Remote click constants */ +#define REMOTE_CLICK_BTN_DEFAULT Button1 +#define REMOTE_CLICK_POS_DEFAULT INT_MAX +#define REMOTE_CLICK_CNT_DEFAULT 1 + +/* Structure to hold all tray data */ +struct TrayData { + /* General */ + Window tray; /* ID of tray window */ + Window hint_win; /* ID of icon window */ + Display *dpy; /* Display pointer */ + XSizeHints xsh; /* Size & position of the tray window */ + XSizeHints root_wnd; /* Size & position :) of the root window */ + Window old_selection_owner; /* Old owner of tray selection */ + int terminated; /* Exit flag */ + int is_active; /* Is the tray active? */ + int is_reparented; /* Was the tray reparented in smth like FvwmButtons ? */ + int kde_tray_old_mode; /* Use legacy scheme to handle KDE icons via MapNotify */ + + /* Atoms */ + Atom xa_tray_selection; /* Atom: _NET_SYSTEM_TRAY_SELECTION_S */ + Atom xa_tray_opcode; /* Atom: _NET_SYSTEM_TRAY_MESSAGE_OPCODE */ + Atom xa_tray_data; /* Atom: _NET_SYSTEM_TRAY_MESSAGE_DATA */ + Atom xa_wm_protocols; /* Atom: WM_PROTOCOLS */ + Atom xa_wm_delete_window; /* Atom: WM_DELETE_WINDOW */ + Atom xa_wm_take_focus; /* Atom: WM_TAKE_FOCUS */ + Atom xa_kde_net_system_tray_windows; /* Atom: _KDE_NET_SYSTEM_TRAY_WINDOWS */ + Atom xa_net_client_list; /* Atom: _NET_CLIENT_LIST */ + + /* Background pixmap */ + Atom xa_xrootpmap_id; /* Atom: _XROOTPMAP_ID */ + Atom xa_xsetroot_id; /* Atom: _XSETROOT_ID */ + Pixmap bg_pmap; /* Pixmap for tray background */ + struct Point bg_pmap_dims; /* Background pixmap dimensions */ + + /* XEMBED data */ + struct XEMBEDData xembed_data; /* XEMBED data */ + + /* Scrollbar data */ + //struct ScrollbarsData scrollbars_data; +}; +extern struct TrayData tray_data; + +/* Initialize all tray data structures */ +void tray_init(); +/* Create tray window */ +void tray_create_window(int argc, char **argv, Window window); +/* Create phony tray window so that certain x11_ calls work */ +void tray_create_phony_window(); +/* Initialize tray selection atoms */ +void tray_init_selection_atoms(); +/* Acquire tray selection */ +void tray_acquire_selection(); +/* Show tray window */ +void tray_show_window(); +/* Refresh tray window */ +void tray_refresh_window(int exposures); +/* Update tray background (and pixmap, if update_pixmap is true) */ +int tray_update_bg(int update_pixmap); +/* Calculate tray window size given the size of icon area in pixels. */ +//int tray_calc_window_size(int base_width, int base_height, int *new_width, int *new_height); +/* Calculate size of icon area given the tray window size in pixels. */ +//int tray_calc_tray_area_size(int wnd_width, int wnd_height, int *base_width, int *base_height); +/* Update window struts (if enabled) */ +int tray_update_window_strut(); +/* Update tray window size and hints */ +int tray_update_window_props(); +/* Set tray window WM hints */ +int tray_set_wm_hints(); + +#endif diff --git a/gb.desktop.x11/src/systray/wmh.c b/gb.desktop.x11/src/systray/wmh.c new file mode 100644 index 00000000..46699618 --- /dev/null +++ b/gb.desktop.x11/src/systray/wmh.c @@ -0,0 +1,232 @@ +/* ------------------------------- + * vim:tabstop=4:shiftwidth=4 + * ewmh.c + * Thu, 30 Mar 2006 23:15:37 +0700 + * ------------------------------- + * EWMH/MWM support + * ------------------------------- */ +#include +#include + +#include "wmh.h" +#include "common.h" +#include "debug.h" +#include "xutils.h" + +/* Structure for Motif WM hints */ +typedef struct { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long inputMode; + unsigned long status; +} PropMotifWmHints; +/* Bit flags for fields of MWM hints data structure */ +#define MWM_HINTS_FUNCTIONS (1L << 0) +#define MWM_HINTS_DECORATIONS (1L << 1) +#define MWM_HINTS_INPUT_MODE (1L << 2) +#define MWM_HINTS_STATUS (1L << 3) +/* Number of CARD32 entries in MWM hints data structure */ +#define PROP_MOTIF_WM_HINTS_ELEMENTS 5 + +/* Check if WM that supports EWMH hints is present on given display */ +int ewmh_wm_present(Display *dpy) +{ + Window *check_win, *check_win_self_ref; + unsigned long cw_len = 0, cwsr_len = 0; + int rc = False; + /* see _NET_SUPPORTING_WM_CHECK in the WM spec. */ + rc = x11_get_root_winlist_prop(dpy, + XInternAtom(dpy, _NET_SUPPORTING_WM_CHECK, False), + (unsigned char **) &check_win, + &cw_len); + if (x11_ok() && rc && cw_len == 1) { + LOG_TRACE(("_NET_SUPPORTING_WM_CHECK (root) = 0x%x\n", check_win[0])); + x11_get_window_prop32(dpy, + check_win[0], + XInternAtom(dpy, _NET_SUPPORTING_WM_CHECK, False), + XA_WINDOW, + (unsigned char **) &check_win_self_ref, + &cwsr_len); + rc = (x11_ok() && rc && cwsr_len == 1 && check_win[0] == check_win_self_ref[0]); + LOG_TRACE(("_NET_SUPPORTING_WM_CHECK (self reference) = 0x%x\n", check_win[0])); + } + if (cw_len != 0) XFree(check_win); + if (cwsr_len != 0) XFree(check_win_self_ref); + LOG_TRACE(("EWMH WM %sdetected\n", rc ? "" : "not ")); + return rc; +} + +/* Add EWMH window state for the given window */ +int ewmh_add_window_state(Display *dpy, Window wnd, char *state) +{ + Atom prop; + Atom atom; + XWindowAttributes xwa; + int rc; + prop = XInternAtom(dpy, "_NET_WM_STATE", False); + atom = XInternAtom(dpy, state, False); + LOG_TRACE(("adding state %s to window 0x%x\n", state, atom)); + /* Ping the window and get its state */ + rc = XGetWindowAttributes(dpy, wnd, &xwa); + if (!x11_ok() || !rc ) return FAILURE; + + if (xwa.map_state != IsUnmapped && ewmh_wm_present(dpy)) { + /* For mapped windows, ask WM (if it is here) to add the window state */ + rc = x11_send_client_msg32(dpy, xwa.root, wnd, prop, + _NET_WM_STATE_ADD, atom, 0, 0, 0); + } else { + /* Else, alter the window state atom value ourselves */ + rc = XChangeProperty(dpy, wnd, prop, XA_ATOM, 32, PropModeAppend, (unsigned char *)&atom, 1); + rc = x11_ok() && rc; + } + return rc; +} + +/* Add EWMH window type for the given window */ +int ewmh_add_window_type(Display *dpy, Window wnd, char *type) +{ + Atom prop; + Atom atom; + prop = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + atom = XInternAtom(dpy, type, False); + LOG_TRACE(("adding type %s to window 0x%x\n", type, atom)); + /* Update property value (append) */ + XChangeProperty(dpy, wnd, prop, XA_ATOM, 32, PropModeAppend, (unsigned char *)&atom, 1); + return x11_ok(); +} + +/* Set data for _NET_WM_STRUT{,_PARTIAL} hints */ +int ewmh_set_window_strut(Display *dpy, Window wnd, wm_strut_t wm_strut) +{ + Atom prop_strut; + Atom prop_strut_partial; + prop_strut = XInternAtom(dpy, _NET_WM_STRUT, False); + prop_strut_partial = XInternAtom(dpy, _NET_WM_STRUT_PARTIAL, False); + XChangeProperty(dpy, wnd, prop_strut, XA_CARDINAL, 32, PropModeReplace, + (unsigned char *)wm_strut, _NET_WM_STRUT_SZ); + XChangeProperty(dpy, wnd, prop_strut_partial, XA_CARDINAL, 32, PropModeReplace, + (unsigned char *)wm_strut, _NET_WM_STRUT_PARTIAL_SZ); + return x11_ok(); +} + +/* Set CARD32 value of EWMH atom for a given window */ +int ewmh_set_window_atom32(Display *dpy, Window wnd, char *prop_name, CARD32 value) +{ + Atom prop; + Atom atom; + XWindowAttributes xwa; + int rc; + prop = XInternAtom(dpy, prop_name, False); + LOG_TRACE(("0x%x: setting atom %s to 0x%x\n", wnd, prop_name, value)); + /* Ping the window and get its state */ + rc = XGetWindowAttributes(dpy, wnd, &xwa); + if (!x11_ok() || !rc ) return FAILURE; + if (xwa.map_state != IsUnmapped && ewmh_wm_present(dpy)) { + /* For mapped windows, ask WM (if it is here) to add the window state */ + return x11_send_client_msg32(dpy, DefaultRootWindow(dpy), wnd, prop, + value, 2 /* source indication */, 0, 0, 0); + } else { + /* Else, alter the window state atom value ourselves */ + XChangeProperty(dpy, wnd, prop, XA_ATOM, 32, PropModeAppend, (unsigned char *)&atom, 1); + return x11_ok(); + } +} + +#if 0 +/* Set MWM hints */ +int mwm_set_hints(Display *dpy, Window wnd, unsigned long decorations, unsigned long functions) +{ + PropMotifWmHints *prop = NULL, new_prop; + int act_fmt, rc; + unsigned long nitems, bytes_after; + static Atom atom = None, act_type; + /* Check if WM supports Motif WM hints */ + if (atom == None) atom = XInternAtom(dpy, "_MOTIF_WM_HINTS", False); + if (atom == None) return FAILURE; + /* Get current hints */ + rc = XGetWindowProperty(dpy, wnd, atom, 0, 5, False, atom, + &act_type, &act_fmt, &nitems, &bytes_after, + (unsigned char**) &prop); + if ((act_type == None && act_fmt == 0 && bytes_after == 0) || nitems == 0) { + /* Hints are either not set or have some other type. + * Reset all values. */ + memset(&new_prop, 0, sizeof(PropMotifWmHints)); + if (prop != NULL) XFree(prop); + prop = &new_prop; + } else if (prop != NULL) { + /* Copy value */ + new_prop = *prop; + XFree(prop); + prop = &new_prop; + } else { + /* Something is broken */ + x11_ok(); /* Reset x11 error status */ + return FAILURE; + } + /* Update value */ + prop->flags |= MWM_HINTS_DECORATIONS | MWM_HINTS_FUNCTIONS; + prop->decorations = decorations; + prop->functions = functions; + XChangeProperty(dpy, wnd, atom, atom, 32, PropModeReplace, + (unsigned char*) prop, PROP_MOTIF_WM_HINTS_ELEMENTS); + return x11_ok(); +} +#endif + +#ifdef DEBUG +/* Dumps EWMH atoms supported by WM */ +int ewmh_list_supported_atoms(Display *dpy) +{ + Atom *atom_list; + unsigned long atom_list_len, i; + char *atom_name; + if (ewmh_wm_present(dpy)) { + if (x11_get_window_prop32(dpy, + DefaultRootWindow(dpy), + XInternAtom(dpy, _NET_SUPPORTED, False), + XA_ATOM, + (unsigned char **) &atom_list, + &atom_list_len)) + if (atom_list_len) { + for (i = 0; i < atom_list_len; i++) { + atom_name = XGetAtomName(dpy, atom_list[i]); + if (atom_name != NULL) { + LOG_TRACE(("_NET_SUPPORTED[%d]: %s (0x%x)\n", i, atom_name, atom_list[i])); + } else + LOG_TRACE(("_NET_SUPPORTED[%d]: bogus value (0x%x)\n", i, atom_list[i])); + XFree(atom_name); + x11_ok(); + } + free(atom_list); + return SUCCESS; + } + } + return FAILURE; +} + +/* List EWMH states that are set for the given window */ +int ewmh_dump_window_states(Display *dpy, Window wnd) +{ + Atom prop, *data; + unsigned long prop_len; + int j; + char *tmp; + /* Check if WM supports _NET_WM_STATE */ + prop = XInternAtom(tray_data.dpy, "_NET_WM_STATE", True); + if (prop == None) return FAILURE; + /* Retrive the list of states */ + if (x11_get_window_prop32(dpy, wnd, prop, XA_ATOM, (unsigned char**) &data, &prop_len)) { + for (j = 0; j < prop_len; j++) { + tmp = XGetAtomName(tray_data.dpy, data[j]); + if (x11_ok() && tmp != NULL) { + LOG_TRACE(("0x%x:_NET_WM_STATE[%d] = %s\n", wnd, j, tmp)); + XFree(tmp); + } + } + return SUCCESS; + } + return FAILURE; +} +#endif + diff --git a/gb.desktop.x11/src/systray/wmh.h b/gb.desktop.x11/src/systray/wmh.h new file mode 100644 index 00000000..05b000be --- /dev/null +++ b/gb.desktop.x11/src/systray/wmh.h @@ -0,0 +1,102 @@ +/* ------------------------------- + * vim:tabstop=4:shiftwidth=4 + * ewmh.h + * Thu, 30 Mar 2006 23:12:43 +0700 + * ------------------------------- + * EWMH/MWM support + * ------------------------------- */ + +#ifndef _EWMH_H_ +#define _EWMH_H_ + +#include "systray.h" + +#include +#include + +/* Defines for Motif WM functions */ +#define MWM_FUNC_ALL (1L << 0) +#define MWM_FUNC_RESIZE (1L << 1) +#define MWM_FUNC_MOVE (1L << 2) +#define MWM_FUNC_MINIMIZE (1L << 3) +#define MWM_FUNC_MAXIMIZE (1L << 4) +#define MWM_FUNC_CLOSE (1L << 5) +/* Defines for Motif WM decorations */ +#define MWM_DECOR_ALL (1L << 0) +#define MWM_DECOR_BORDER (1L << 1) +#define MWM_DECOR_RESIZEH (1L << 2) +#define MWM_DECOR_TITLE (1L << 3) +#define MWM_DECOR_MENU (1L << 4) +#define MWM_DECOR_MINIMIZE (1L << 5) +#define MWM_DECOR_MAXIMIZE (1L << 6) + +/* EWMH atoms */ +#define _NET_SUPPORTED "_NET_SUPPORTED" +#define _NET_SUPPORTING_WM_CHECK "_NET_SUPPORTING_WM_CHECK" +#define _NET_WM_DESKTOP "_NET_WM_DESKTOP" +#define _NET_ACTIVE_WINDOW "_NET_ACTIVE_WINDOW" +#define _NET_CLIENT_LIST "_NET_CLIENT_LIST" +#define _NET_WM_PING "_NET_WM_PING" +#define _NET_WM_STRUT_PARTIAL "_NET_WM_STRUT_PARTIAL" +#define _NET_WM_STRUT "_NET_WM_STRUT" + +/* Defines for EWMH window types */ +#define _NET_WM_WINDOW_TYPE_DESKTOP "_NET_WM_WINDOW_TYPE_DESKTOP" +#define _NET_WM_WINDOW_TYPE_DOCK "_NET_WM_WINDOW_TYPE_DOCK" +#define _NET_WM_WINDOW_TYPE_TOOLBAR "_NET_WM_WINDOW_TYPE_TOOLBAR" +#define _NET_WM_WINDOW_TYPE_MENU "_NET_WM_WINDOW_TYPE_MENU" +#define _NET_WM_WINDOW_TYPE_UTILITY "_NET_WM_WINDOW_TYPE_UTILITY" +#define _NET_WM_WINDOW_TYPE_SPLASH "_NET_WM_WINDOW_TYPE_SPLASH" +#define _NET_WM_WINDOW_TYPE_DIALOG "_NET_WM_WINDOW_TYPE_DIALOG" +#define _NET_WM_WINDOW_TYPE_NORMAL "_NET_WM_WINDOW_TYPE_NORMAL" + +/* Defined for EWMH window states */ +#define _NET_WM_STATE_MODAL "_NET_WM_STATE_MODAL" +#define _NET_WM_STATE_STICKY "_NET_WM_STATE_STICKY" +#define _NET_WM_STATE_MAXIMIZED_VERT "_NET_WM_STATE_MAXIMIZED_VERT" +#define _NET_WM_STATE_MAXIMIZED_HORZ "_NET_WM_STATE_MAXIMIZED_HORZ" +#define _NET_WM_STATE_SHADED "_NET_WM_STATE_SHADED" +#define _NET_WM_STATE_SKIP_TASKBAR "_NET_WM_STATE_SKIP_TASKBAR" +#define _NET_WM_STATE_SKIP_PAGER "_NET_WM_STATE_SKIP_PAGER" +#define _NET_WM_STATE_HIDDEN "_NET_WM_STATE_HIDDEN" +#define _NET_WM_STATE_FULLSCREEN "_NET_WM_STATE_FULLSCREEN" +#define _NET_WM_STATE_ABOVE "_NET_WM_STATE_ABOVE" +#define _NET_WM_STATE_BELOW "_NET_WM_STATE_BELOW" +#define _NET_WM_STATE_DEMANDS_ATTENTION "_NET_WM_STATE_DEMANDS_ATTENTION" + +/* Flags for window state manipulations */ +#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ +#define _NET_WM_STATE_ADD 1 /* add/set property */ +#define _NET_WM_STATE_TOGGLE 2 /* toggle property */ + +#define _NET_WM_STRUT_PARTIAL_SZ 12 +#define _NET_WM_STRUT_SZ 4 +typedef unsigned long wm_strut_t[_NET_WM_STRUT_PARTIAL_SZ]; +#define WM_STRUT_IDX_LFT 0 +#define WM_STRUT_IDX_RHT 1 +#define WM_STRUT_IDX_TOP 2 +#define WM_STRUT_IDX_BOT 3 +#define WM_STRUT_IDX_START_OFFSET 4 +#define WM_STRUT_IDX_END_OFFSET 8 + +/* Check if WM that supports EWMH hints is present on given display */ +int ewmh_wm_present(Display *dpy); +/* Add window type for the window wnd */ +int ewmh_add_window_state(Display *dpy, Window wnd, char *state); +/* Add window type for the window wnd */ +int ewmh_add_window_type(Display *dpy, Window wnd, char *type); +/* Set data for _NET_WM_STRUT_PARTIAL hint */ +int ewmh_set_window_strut(Display *dpy, Window wnd, wm_strut_t wm_strut); +/* Set CARD32 value of EWMH atom for a given window */ +int ewmh_set_window_atom32(Display *dpy, Window wnd, char *prop_name, CARD32 value); +/* Set Motif WM hints for window wnd; read MWM spec for more info */ +int mwm_set_hints(Display *dpy, Window wnd, unsigned long decorations, unsigned long functions); + +#ifdef DEBUG +/* Dumps EWMH atoms supported by WM */ +int ewmh_list_supported_atoms(Display *dpy); +/* Dump all EWMH states that have been set for the window wnd */ +int ewmh_dump_window_states(Display *dpy, Window wnd); +#endif + +#endif diff --git a/gb.desktop.x11/src/systray/xembed.c b/gb.desktop.x11/src/systray/xembed.c new file mode 100644 index 00000000..0d6d1fda --- /dev/null +++ b/gb.desktop.x11/src/systray/xembed.c @@ -0,0 +1,537 @@ +/* ------------------------------- +* vim:tabstop=4:shiftwidth=4 +* xembed.c +* Tue, 24 Aug 2004 12:05:38 +0700 +* ------------------------------- +* XEMBED protocol implementation +* -------------------------------*/ + +/* Currently broken: + * - XEMBED_{ACTIVATE,REGISTER,UNREGISTER}_ACCELERATOR + * - XEMBED_REQUEST_FOCUS */ + +#include +#include +#include +#include + +#include "common.h" +#include "debug.h" +#include "xembed.h" +#include "xutils.h" +#include "wmh.h" + +#include "list.h" + +/* Internal return codes */ +#define XEMBED_RESULT_OK 0 +#define XEMBED_RESULT_UNSUPPORTED 1 +#define XEMBED_RESULT_X11ERROR 2 + +/* XEMBED messages */ +#define XEMBED_EMBEDDED_NOTIFY 0 +#define XEMBED_WINDOW_ACTIVATE 1 +#define XEMBED_WINDOW_DEACTIVATE 2 +#define XEMBED_REQUEST_FOCUS 3 +#define XEMBED_FOCUS_IN 4 +#define XEMBED_FOCUS_OUT 5 +#define XEMBED_FOCUS_NEXT 6 +#define XEMBED_FOCUS_PREV 7 +/* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */ +#define XEMBED_MODALITY_ON 10 +#define XEMBED_MODALITY_OFF 11 +#define XEMBED_REGISTER_ACCELERATOR 12 +#define XEMBED_UNREGISTER_ACCELERATOR 13 +#define XEMBED_ACTIVATE_ACCELERATOR 14 + +/* Details for XEMBED_FOCUS_IN */ +#define XEMBED_FOCUS_CURRENT 0 +#define XEMBED_FOCUS_FIRST 1 +#define XEMBED_FOCUS_LAST 2 + +/* Modifiers field for XEMBED_REGISTER_ACCELERATOR */ +#define XEMBED_MODIFIER_SHIFT (1 << 0) +#define XEMBED_MODIFIER_CONTROL (1 << 1) +#define XEMBED_MODIFIER_ALT (1 << 2) +#define XEMBED_MODIFIER_SUPER (1 << 3) +#define XEMBED_MODIFIER_HYPER (1 << 4) + +/* Flags for XEMBED_ACTIVATE_ACCELERATOR */ +#define XEMBED_ACCELERATOR_OVERLOADED (1 << 0) + +/* Directions for focusing */ +#define XEMBED_DIRECTION_DEFAULT 0 +#define XEMBED_DIRECTION_UP_DOWN 1 +#define XEMBED_DIRECTION_LEFT_RIGHT 2 + +/* Flags for _XEMBED_INFO */ +#define XEMBED_MAPPED (1 << 0) + +/* Structure to hold XEMBED accelerator data */ +struct XEMBEDAccel { + struct XEMBEDAccel *next, *prev; + int overloaded; /* Is this accelerator overloaded? */ + long id; /* Accelerator Id */ + long symb; /* Symbol */ + long mods; /* Modifiers */ +}; + +/* Shortcuts for sending XEMBED messages */ +#define xembed_send_msg(dpy, dst, timestamp, msg, detail, data1, data2) \ + x11_send_client_msg32(dpy, dst, dst, tray_data.xembed_data.xa_xembed, timestamp, msg, detail, data1, data2) + +#define xembed_send_embedded_notify(dpy, src, dst, timestamp) \ + xembed_send_msg(dpy, dst, timestamp, XEMBED_EMBEDDED_NOTIFY, 0, src, 0) + +#define xembed_send_window_activate(dpy, dst, timestamp) \ + xembed_send_msg(dpy, dst, timestamp, XEMBED_WINDOW_ACTIVATE, 0, 0, 0) + +#define xembed_send_window_deactivate(dpy, dst, timestamp) \ + xembed_send_msg(dpy, dst, timestamp, XEMBED_WINDOW_DEACTIVATE, 0, 0, 0) + +#define xembed_send_focus_in(dpy, dst, focus, timestamp) \ + xembed_send_msg(dpy, dst, timestamp, XEMBED_FOCUS_IN, focus, 0, 0) + +#define xembed_send_focus_out(dpy, dst, timestamp) \ + xembed_send_msg(dpy, dst, timestamp, XEMBED_FOCUS_OUT, 0, 0, 0) + +#define xembed_send_activate_accelerator(dpy, dst, timestamp, id, overloaded) \ + xembed_send_msg(dpy, dst, timestamp, XEMBED_ACTIVATE_ACCELERATOR, id, overloaded, 0) + +/* Retrieve XEMBED data for the given icon */ +int xembed_retrieve_data(struct TrayIcon *ti); +/* Post icon XEMBED data to window property */ +int xembed_post_data(struct TrayIcon *ti); +/* Returns the next icon in tab chain */ +struct TrayIcon *xembed_next(); +/* Returns the previous icon in tab chain */ +struct TrayIcon *xembed_prev(); +/* XEMBED event handler */ +int xembed_process_kbd_event(XKeyEvent xkey); +/* Register new XEMBED accelerator */ +void xembed_add_accel(long id, long symb, long mods); +/* Delete previously registered XEMBED accelerator */ +void xembed_del_accel(long id); +/* Activate previously registered XEMBED accelerator */ +void xembed_act_accel(struct XEMBEDAccel *accel); +/* Switch XEMBED focus to the specified icon */ +void xembed_switch_focus_to(struct TrayIcon *tgt, long focus); +/* Broadcast the focus change to all icons */ +void xembed_track_focus_change(int activate); +/* Process XEMBED message */ +void xembed_message(XClientMessageEvent ev); +/* Tries to request focus from WM */ +void xembed_request_focus_from_wm(); + +void xembed_init() +{ + /* 1. Initialize data structures */ + tray_data.xembed_data.window_has_focus = False; + tray_data.xembed_data.focus_requested = False; + tray_data.xembed_data.current = NULL; + tray_data.xembed_data.accels = NULL; + tray_data.xembed_data.timestamp = CurrentTime; + tray_data.xembed_data.xa_xembed = XInternAtom(tray_data.dpy, "_XEMBED", False); + tray_data.xembed_data.xa_xembed_info = XInternAtom(tray_data.dpy, "_XEMBED_INFO", False); + /* 2. Create focus proxy (see XEMBED spec) */ + tray_data.xembed_data.focus_proxy = + XCreateSimpleWindow(tray_data.dpy, tray_data.tray, -1, -1, 1, 1, 0, 0, 0); + XSelectInput(tray_data.dpy, tray_data.xembed_data.focus_proxy, FocusChangeMask | KeyPressMask | KeyReleaseMask); + XMapRaised(tray_data.dpy, tray_data.xembed_data.focus_proxy); + if (!x11_ok()) DIE(("could not create focus proxy\n")); + LOG_TRACE(("created focus proxy, wid=0x%x\n", tray_data.xembed_data.focus_proxy)); +} + +void xembed_handle_event(XEvent ev) +{ + switch (ev.type) { + case FocusOut: + /* Broadcast that the focus has left tray window */ + LOG_TRACE(("FocusOut 0x%x\n", ev.xfocus.window)); + if (ev.xfocus.window == tray_data.xembed_data.focus_proxy) + xembed_track_focus_change(False); + break; + case ClientMessage: + /* Handle XEMBED-related messages */ + if (ev.xclient.message_type == tray_data.xembed_data.xa_xembed) { + xembed_message(ev.xclient); + } else if (ev.xclient.message_type == tray_data.xa_tray_opcode) { + /* we peek at _NET_SYSTEM_TRAY_OPCODE messages + * to obtain proper timestamp for embedding */ + tray_data.xembed_data.timestamp = ev.xclient.data.l[0]; + if (tray_data.xembed_data.timestamp == CurrentTime) + tray_data.xembed_data.timestamp = x11_get_server_timestamp(tray_data.dpy, tray_data.tray); + } else if (ev.xclient.message_type == tray_data.xa_wm_protocols && + ev.xclient.data.l[0] == tray_data.xa_wm_take_focus && + tray_data.xembed_data.focus_requested) + { + XSetInputFocus(tray_data.dpy, tray_data.xembed_data.focus_proxy, RevertToParent, ev.xclient.data.l[1]); + if (!x11_ok()) DIE_IE(("Could not set focus to XEMBED focus proxy\n")); + LOG_TRACE(("focus set to focus proxy\n")); + xembed_track_focus_change(True); + tray_data.xembed_data.focus_requested = False; + } + break; + case KeyRelease: + case KeyPress: + /* Propagate key events to currently focused icon */ + tray_data.xembed_data.timestamp = ev.xkey.time; + if (ev.type == KeyRelease && xembed_process_kbd_event(ev.xkey)) + break; + if (tray_data.xembed_data.current != NULL) { + int rc; + LOG_TRACE(("current icon accepts_focus: %d\n", tray_data.xembed_data.current->is_xembed_accepts_focus)); + rc = XSendEvent(tray_data.dpy, tray_data.xembed_data.current->wid, False, NoEventMask, &ev); + if (!x11_ok() || rc == 0) { + tray_data.xembed_data.current->is_invalid = True; + return; + } + LOG_TRACE(("sent key event to 0x%x\n", tray_data.xembed_data.current->wid)); + } + break; + } +} + +int xembed_check_support(struct TrayIcon *ti) +{ + int rc = xembed_retrieve_data(ti); + ti->is_xembed_supported = (rc == XEMBED_RESULT_OK); + return rc != XEMBED_RESULT_X11ERROR; +} + +int xembed_get_mapped_state(struct TrayIcon *ti) +{ + /* It is OK to retrieve data each time this function + * is called, since there is some overhead only during + * initialization, when xembed_retrieve_data is called 2 + * times in a row(). */ + int rc = xembed_retrieve_data(ti); + if (ti->is_xembed_supported && rc == XEMBED_RESULT_OK) + return ((ti->xembed_data[1] & XEMBED_MAPPED) != 0); + else { + ti->is_xembed_supported = False; + ti->is_invalid = (rc == XEMBED_RESULT_X11ERROR); + return False; + } +} + +int xembed_set_mapped_state(struct TrayIcon *ti, int state) +{ + if (!ti->is_xembed_supported) return FAILURE; + if (state) + ti->xembed_data[1] |= XEMBED_MAPPED; + else + ti->xembed_data[1] &= ~XEMBED_MAPPED; + return xembed_post_data(ti); +} + +int xembed_embed(struct TrayIcon *ti) +{ + /* if XEMBED is not supported, do nothing */ + if (!ti->is_xembed_supported) return SUCCESS; + /* By default, consider that all icons accept focus */ + ti->is_xembed_accepts_focus = True; + /* Send notification */ + if (!xembed_send_embedded_notify(tray_data.dpy, tray_data.tray, ti->wid, tray_data.xembed_data.timestamp)) + return FAILURE; + ti->xembed_last_timestamp = tray_data.xembed_data.timestamp; + ti->xembed_last_msgid = XEMBED_EMBEDDED_NOTIFY; + if (tray_data.xembed_data.current == NULL) { + /* No icon has focus. Set focus to this one */ + if (!xembed_send_focus_in(tray_data.dpy, ti->wid, XEMBED_FOCUS_FIRST, tray_data.xembed_data.timestamp)) + return FAILURE; + tray_data.xembed_data.current = ti; + } + /* Send activation message if tray window has focus */ + if (tray_data.xembed_data.window_has_focus) + return xembed_send_window_activate(tray_data.dpy, ti->wid, tray_data.xembed_data.timestamp); + return SUCCESS; +} + +int xembed_unembed(struct TrayIcon *ti) +{ + struct TrayIcon *tmp; + tray_data.xembed_data.timestamp = x11_get_server_timestamp(tray_data.dpy, tray_data.tray); + if (ti == tray_data.xembed_data.current) { + /* Currently focused icon is being unembedded, + * move focus to the next icon. */ + tmp = xembed_next(); + if (tmp == ti || tmp->is_xembed_accepts_focus == False) { + xembed_switch_focus_to(NULL, 0); + } else { + xembed_switch_focus_to(tmp, XEMBED_FOCUS_FIRST); + } + } + return SUCCESS; +} + +/*********** implementation level ***************/ +void xembed_switch_focus_to(struct TrayIcon *tgt, long focus) +{ + /* 1. Send "focus out" message to the currently focused icon */ + if (tray_data.xembed_data.current != NULL) { + LOG_TRACE(("XEMBED focus was removed from icon 0x%x (pointer %p)\n", tray_data.xembed_data.current->wid, tray_data.xembed_data.current)); + xembed_send_focus_out(tray_data.dpy, tray_data.xembed_data.current->wid, tray_data.xembed_data.timestamp); + } + /* 2. Send "focus in" message to the icon to be focused */ + if (tgt != NULL) { + xembed_send_focus_in(tray_data.dpy, tgt->wid, focus, tray_data.xembed_data.timestamp); + LOG_TRACE(("XEMBED focus was set to icon 0x%x (pointer %p)\n", tgt->wid, tgt)); + } else { + LOG_TRACE(("XEMBED focus was unset\n")); + } + tray_data.xembed_data.current = tgt; +} + +static int activate = 0; + +int broadcast_activate_msg(struct TrayIcon *ti) +{ + if (activate) + xembed_send_window_activate(tray_data.dpy, ti->wid, tray_data.xembed_data.timestamp); + else + xembed_send_window_deactivate(tray_data.dpy, ti->wid, tray_data.xembed_data.timestamp); + return NO_MATCH; +} + +void xembed_track_focus_change(int in) +{ + if (tray_data.xembed_data.window_has_focus == in) return; + tray_data.xembed_data.window_has_focus = in; + activate = in; + icon_list_forall(&broadcast_activate_msg); + LOG_TRACE(("XEMBED focus is %s\n", in ? "ON" : "OFF")); +} + +void xembed_message(XClientMessageEvent ev) +{ + long msgid; + LOG_TRACE(("this is an _XEMBED message, window: 0x%x, timestamp: %u, opcode: %u, \ndetail: 0x%x, data1 = 0x%x, data2 = 0x%x\n", + ev.window, ev.data.l[0], ev.data.l[1], ev.data.l[2], ev.data.l[3], ev.data.l[4])); +#ifdef DEBUG + if (tray_data.xembed_data.current != NULL) + LOG_TRACE(("XEMBED focus is in window 0x%x (pointer %p)\n", tray_data.xembed_data.current->wid, tray_data.xembed_data.current)); + else + LOG_TRACE(("XEMBED focus is unset\n")); +#endif + if (ev.window != tray_data.tray) { + LOG_TRACE(("inoring _XEMBED message to some other window\n")); + return; + } + /* Update timestamp if necessary */ + if (ev.data.l[0] == CurrentTime) + ev.data.l[0] = x11_get_server_timestamp(tray_data.dpy, tray_data.tray); + tray_data.xembed_data.timestamp = ev.data.l[0]; + msgid = ev.data.l[1]; + LOG_TRACE(("_XEMBED message %u\n", msgid)); + switch (msgid) { + case XEMBED_REQUEST_FOCUS: + xembed_request_focus_from_wm(); + break; + case XEMBED_FOCUS_NEXT: + case XEMBED_FOCUS_PREV: + if (tray_data.xembed_data.current != NULL) { + struct TrayIcon *old_focus, *new_focus; + old_focus = tray_data.xembed_data.current; + new_focus = (msgid == XEMBED_FOCUS_NEXT) ? + xembed_next(): + xembed_prev(); + if (new_focus->is_xembed_accepts_focus) { + /* If the last message for the new focus target was focus_{next,prev} and + * it has the same timestamp as the current message, it is likely that + * the corresponding icon does not want to be focused at all. So mark it + * as not accepting focus. */ + if (new_focus->xembed_last_timestamp == tray_data.xembed_data.timestamp && + (new_focus->xembed_last_msgid == XEMBED_FOCUS_NEXT || + new_focus->xembed_last_msgid == XEMBED_FOCUS_PREV)) + { + new_focus->is_xembed_accepts_focus = False; + new_focus = False; + } + old_focus->xembed_last_timestamp = tray_data.xembed_data.timestamp; + old_focus->xembed_last_msgid = msgid; + } else + new_focus = NULL; + xembed_switch_focus_to(new_focus, (msgid == XEMBED_FOCUS_NEXT) ? + XEMBED_FOCUS_FIRST : + XEMBED_FOCUS_LAST); + } + break; + case XEMBED_REGISTER_ACCELERATOR: + xembed_add_accel(ev.data.l[2], ev.data.l[3], ev.data.l[4]); + break; + case XEMBED_UNREGISTER_ACCELERATOR: + xembed_del_accel(ev.data.l[2]); + break; + default: + LOG_TRACE(("Unhandled _XEMBED message, id = %d\n", ev.data.l[1])); + break; + } +} + +void xembed_add_accel(long id, long symb, long mods) +{ + struct XEMBEDAccel *xaccel, *tmp; + xaccel = (struct XEMBEDAccel *) malloc(sizeof(struct XEMBEDAccel)); + if (xaccel == NULL) { + LOG_ERR_OOM(("Could not register new XEMBED accelerator\n")); + return; + } + xaccel->id = id; + xaccel->symb = symb; + xaccel->mods = mods; + xaccel->overloaded = 0; + /* Check if there are already registered accelerators that are overloaded + * by this one */ + for (tmp = tray_data.xembed_data.accels; tmp != NULL; tmp = tmp->next) + if (tmp->symb == symb && tmp->mods == mods) { + xaccel->overloaded++; + tmp->overloaded++; + } + LIST_ADD_ITEM(tray_data.xembed_data.accels, xaccel); + LOG_TRACE(("added new XEMBED accelerator: id=0x%x, sym=0x%x, mods=0x%x, overloaded=%d\n", + id, symb, mods, xaccel->overloaded)); +} + +void xembed_del_accel(long id) +{ + struct XEMBEDAccel *tmp, *tgt = NULL; + for (tmp = tray_data.xembed_data.accels; tmp != NULL; tmp = tmp->next) + if (tmp->id == id) { + tgt = tmp; + return; + } + if (tgt == NULL) { + LOG_TRACE(("refusing to remove unregistered XEMBED accelerator\n")); + return; + } + /* Update overloaded status of the remaining accelerators */ + for (tmp = tray_data.xembed_data.accels; tmp != NULL; tmp = tmp->next) + if (tmp->symb == tgt->symb && tmp->mods == tgt->mods) + tmp->overloaded--; + LIST_DEL_ITEM(tray_data.xembed_data.accels, tgt); + LOG_TRACE(("removed XEMBED accelator id=0x%x", tgt->id)); + free(tgt); +} + +static struct XEMBEDAccel *cur_accel; + +int xembed_act_accel_helper(struct TrayIcon *ti) +{ + xembed_send_activate_accelerator(tray_data.dpy, + ti->wid, + tray_data.xembed_data.timestamp, + cur_accel->id, cur_accel->overloaded ? 1 : 0); + return NO_MATCH; +} + +void xembed_act_accel(struct XEMBEDAccel *accel) +{ + LOG_TRACE(("activating XEMBED accelerator: id=0x%x (symb=0x%x, mods=0x%x)\n", accel->id, accel->symb, accel->mods)); + cur_accel = accel; + icon_list_forall(&xembed_act_accel_helper); +} + +int xembed_process_kbd_event(XKeyEvent xkey) +{ + struct XEMBEDAccel *tmp; + int hits = 0; + KeySym keysym; + static char buf[20]; + XLookupString(&xkey, buf, 20, &keysym, NULL); + LOG_TRACE(("Key event (type=%d) with keycode=0x%x, symb=0x%x, state=0x%x\n", xkey.type, xkey.keycode, keysym, xkey.state)); + for (tmp = tray_data.xembed_data.accels; tmp != NULL; tmp = tmp->next) + if (tmp->symb == keysym && tmp->mods == xkey.state) { + xembed_act_accel(tmp); + hits = 1; + } + return hits; +} + +struct TrayIcon *xembed_next() +{ + struct TrayIcon *tmp, *blocker; + tmp = tray_data.xembed_data.current != NULL ? tray_data.xembed_data.current : NULL; + blocker = tmp != NULL ? tmp : icon_list_next(NULL); + do { + tmp = icon_list_next(tmp); + } while ((!tmp->is_xembed_supported || !tmp->is_xembed_accepts_focus) && tmp != blocker); + return tmp; +} + +struct TrayIcon *xembed_prev() +{ + struct TrayIcon *tmp, *blocker; + tmp = tray_data.xembed_data.current != NULL ? tray_data.xembed_data.current : NULL; + blocker = tmp != NULL ? tmp : icon_list_prev(tmp); + do { + tmp = icon_list_prev(tmp); + } while ((!tmp->is_xembed_supported || !tmp->is_xembed_accepts_focus) && tmp != blocker); + return tmp; +} + +int xembed_retrieve_data(struct TrayIcon *ti) +{ + Atom act_type; + int act_fmt; + unsigned long nitems, bytesafter, *data; + unsigned char *tmpdata; + int rc; + /* NOTE: x11_get_win_prop32 is not used since we need to distinguish between + * X11 errors and absence of the property */ + x11_ok(); + + rc = XGetWindowProperty(tray_data.dpy, + ti->wid, + tray_data.xembed_data.xa_xembed_info, + 0, + 2, + False, + tray_data.xembed_data.xa_xembed_info, + &act_type, + &act_fmt, + &nitems, + &bytesafter, + &tmpdata); + if (rc != Success) + return XEMBED_RESULT_X11ERROR; + rc = (act_type == tray_data.xembed_data.xa_xembed_info && nitems == 2); + if (rc) { + data = (unsigned long*) tmpdata; + ti->xembed_data[0] = data[0]; + ti->xembed_data[1] = data[1]; + } + if (nitems && tmpdata != NULL) XFree(tmpdata); + return rc ? XEMBED_RESULT_OK : XEMBED_RESULT_UNSUPPORTED; +} + +int xembed_post_data(struct TrayIcon *ti) +{ + if (!ti->is_xembed_supported) return XEMBED_RESULT_UNSUPPORTED; + XChangeProperty(tray_data.dpy, + ti->wid, + tray_data.xembed_data.xa_xembed_info, + tray_data.xembed_data.xa_xembed_info, + 32, + PropModeReplace, + (unsigned char *) ti->xembed_data, + 2); + return x11_ok() ? XEMBED_RESULT_OK : XEMBED_RESULT_X11ERROR; +} + +void xembed_request_focus_from_wm() +{ + if (!tray_data.is_reparented) { + x11_send_client_msg32(tray_data.dpy, + DefaultRootWindow(tray_data.dpy), + tray_data.tray, + XInternAtom(tray_data.dpy, "_NET_ACTIVE_WINDOW", True), + 1, /* Request is from application */ + x11_get_server_timestamp(tray_data.dpy, tray_data.tray), /* Timestamp */ + 0, /* None window is focused current (?) */ + 0, /* Unused */ + 0);/* Unused */ + tray_data.xembed_data.focus_requested = True; + } +} + diff --git a/gb.desktop.x11/src/systray/xembed.h b/gb.desktop.x11/src/systray/xembed.h new file mode 100644 index 00000000..1faf2f1b --- /dev/null +++ b/gb.desktop.x11/src/systray/xembed.h @@ -0,0 +1,49 @@ +/* ------------------------------- +* vim:tabstop=4:shiftwidth=4 +* icons.h +* Tue, 24 Aug 2004 12:05:38 +0700 +* ------------------------------- +* XEMBED protocol implementation +* -------------------------------*/ + +#ifndef _XEMBED_H_ +#define _XEMBED_H_ + +#include + +#include "icons.h" + +/* Data structure for all XEMBED-related data for the tray */ +struct XEMBEDData { + struct TrayIcon *current; /* Pointer to the currently focused icon */ + struct XEMBEDAccel *accels; /* List of registered XEMBED accelerators */ + int window_has_focus; /* Flag: does tray's window have focus */ + int focus_requested; /* Flag: if there is not completed focus request */ + Window focus_proxy; /* Window ID of XEMBED focus proxy */ + long timestamp; /* Timestamp of current processed message */ + Atom xa_xembed_info; /* Atom: XEMBED_INFO */ + Atom xa_xembed; /* Atom: XEMBED */ +}; + +/* Initialize XEMBED data structures */ +void xembed_init(); + +/* Event handling routine for XEMBED support */ +void xembed_handle_event(XEvent ev); + +/* Check if icon ti supports XEMBED */ +int xembed_check_support(struct TrayIcon *ti); + +/* Send XEMBED embedding acknowledgement to icon ti */ +int xembed_embed(struct TrayIcon *ti); + +/* Same as above for unembedding */ +int xembed_unembed(struct TrayIcon *ti); + +/* Get XEMBED mapped state from XEMBED info */ +int xembed_get_mapped_state(struct TrayIcon *ti); + +/* Set XEMBED mapped state in XEMBED info */ +int xembed_set_mapped_state(struct TrayIcon *ti, int state); + +#endif diff --git a/gb.desktop.x11/src/systray/xutils.c b/gb.desktop.x11/src/systray/xutils.c new file mode 100644 index 00000000..615b9f8e --- /dev/null +++ b/gb.desktop.x11/src/systray/xutils.c @@ -0,0 +1,442 @@ +/* ************************************ + * vim:tabstop=4:shiftwidth=4 + * xutils.c + * Sun, 05 Mar 2006 17:56:56 +0600 + * ************************************ + * misc X11 utilities + * ************************************/ + +#include "systray.h" + +#include +#include +#include +#include + +#include +#include + +#include "xutils.h" + +#include "xembed.h" +#include "debug.h" +#include "common.h" + +static int trapped_x11_error_code = 0; +static int (*old_x11_error_handler) (Display *, XErrorEvent *) = NULL; +static int current_x11_connection_status = 1; +static int (*old_x11_io_error_handler) (Display *) = NULL; + +int x11_io_error_handler(Display *dpy) +{ + current_x11_connection_status = 0; + old_x11_io_error_handler(dpy); + DIE(("Connection to X11 server lost. Dying.\n")); +} + +int x11_connection_status() +{ + return current_x11_connection_status; +} + +int x11_error_handler(Display *dpy, XErrorEvent *err) +{ +#ifdef DEBUG + static char msg[PATH_MAX], req_num_str[32], req_str[PATH_MAX]; + trapped_x11_error_code = err->error_code; + XGetErrorText(dpy, trapped_x11_error_code, msg, sizeof(msg)-1); + snprintf(req_num_str, 32, "%d", err->request_code); + XGetErrorDatabaseText(dpy, "XRequest", req_num_str, "Unknown", req_str, PATH_MAX); + LOG_ERROR(("X11 error: %s (request: %s, resource 0x%x)\n", msg, req_str, err->request_code, err->minor_code, err->resourceid)); +#else + trapped_x11_error_code = err->error_code; +#endif + return 0; +} + +int x11_ok_helper(const char *file, const int line, const char *func) +{ + if (trapped_x11_error_code) { +#ifdef DEBUG + LOG_ERROR(("X11 error %d detected at %s:%d:%s\n", + trapped_x11_error_code, + file, + line, + func)); +#endif + trapped_x11_error_code = 0; + return FAILURE; + } else + return SUCCESS; +} + +int x11_current_error() +{ + return trapped_x11_error_code; +} + +void x11_trap_errors() +{ + old_x11_io_error_handler = XSetIOErrorHandler(x11_io_error_handler); + old_x11_error_handler = XSetErrorHandler(x11_error_handler); + trapped_x11_error_code = 0; +} + +int x11_untrap_errors() +{ + XSetErrorHandler(old_x11_error_handler); + return trapped_x11_error_code; +} + +static Window timestamp_wnd; +static Atom timestamp_atom = None; + +Bool x11_wait_for_timestamp(Display *dpy, XEvent *xevent, XPointer data) +{ + return ((xevent->type == PropertyNotify && + xevent->xproperty.window == *((Window *)data) && + xevent->xproperty.atom == timestamp_atom) || + (xevent->type == DestroyNotify && + xevent->xdestroywindow.window == *((Window *)data))); +} + +Time x11_get_server_timestamp(Display *dpy, Window wnd) +{ + unsigned char c = 's'; + XEvent xevent; + + if (timestamp_atom == None) + timestamp_atom = XInternAtom(dpy, "STALONETRAY_TIMESTAMP", False); + + if (GB.Component.IsLoaded("gb.qt5")) + return CurrentTime; + + x11_ok(); /* Just reset the status (XXX) */ + /* Trigger PropertyNotify event which has a timestamp field */ + XChangeProperty(dpy, wnd, timestamp_atom, timestamp_atom, 8, PropModeReplace, &c, 1); + if (!x11_ok()) return CurrentTime; + + /* Wait for the event + * XXX: this may block... */ + timestamp_wnd = wnd; + + XIfEvent(dpy, &xevent, x11_wait_for_timestamp, (XPointer)×tamp_wnd); + + return x11_ok() ? xevent.xproperty.time : CurrentTime; +} + +int x11_get_window_prop32(Display *dpy, Window dst, Atom atom, Atom type, unsigned char **data, unsigned long *len) +{ + Atom act_type; + int act_fmt, rc; + unsigned long bytes_after, prop_len, buf_len; + unsigned char *buf = NULL; + + *data = NULL; *len = 0; + /* Get the property size */ + rc = XGetWindowProperty(dpy, dst, atom, + 0L, 1L, False, type, &act_type, &act_fmt, + &buf_len, &bytes_after, &buf); + + /* The requested property does not exist */ + if (!x11_ok() || rc != Success || act_type != type || act_fmt != 32) return FAILURE; + + if (buf != NULL) XFree(buf); + + /* Now go get the property */ + prop_len = bytes_after / 4 + 1; + XGetWindowProperty(dpy, dst, atom, + 0L, prop_len, False, type, &act_type, &act_fmt, + &buf_len, &bytes_after, &buf); + + if (x11_ok()) { + *len = buf_len; + *data = buf; + return SUCCESS; + } else { + return FAILURE; + } +} + +int x11_send_client_msg32(Display *dpy, Window dst, Window wnd, Atom type, long data0, long data1, long data2, long data3, long data4) +{ + XEvent ev; + Status rc; + ev.xclient.type = ClientMessage; + ev.xclient.serial = 0; + ev.xclient.send_event = True; + ev.xclient.message_type = type; + ev.xclient.window = wnd; + ev.xclient.format = 32; + ev.xclient.data.l[0] = data0; + ev.xclient.data.l[1] = data1; + ev.xclient.data.l[2] = data2; + ev.xclient.data.l[3] = data3; + ev.xclient.data.l[4] = data4; + /* XXX: Replace 0xFFFFFF for better portability? */ + /* XXX: This should actually read NoEventMask... + * seems like extra parameter is necessary */ + rc = XSendEvent(dpy, dst, False, 0xFFFFFF, &ev); + return x11_ok() && rc != 0; +} + +int x11_send_visibility(Display *dpy, Window dst, long state) +{ + XEvent xe; + int rc; + xe.type = VisibilityNotify; + xe.xvisibility.window = dst; + xe.xvisibility.state = state; + rc = XSendEvent(tray_data.dpy, dst, True, NoEventMask, &xe); + return x11_ok() && rc != 0; +} + +int x11_send_button(Display *dpy, + int press, Window dst, Window root, Time time, + unsigned int button, unsigned int state, + int x, int y) +{ + XEvent ev; + int rx, ry, rc, idummy; + unsigned int udummy; + Window dst_root; + + if (!x11_get_window_abs_coords(dpy, dst, &rx, &ry)) return FAILURE; + XGetGeometry(dpy, dst, &dst_root, + &idummy, &idummy, + &udummy, &udummy, &udummy, &udummy); + if (!x11_ok()) return FAILURE; + + ev.type = press ? ButtonPress : ButtonRelease; + ev.xbutton.display = dpy; + ev.xbutton.window = dst; + ev.xbutton.subwindow = None; + ev.xbutton.root = root; + ev.xbutton.time = time; + ev.xbutton.x = x; + ev.xbutton.y = y; + ev.xbutton.x_root = rx + x; + ev.xbutton.y_root = ry + y; + ev.xbutton.button = button; + ev.xbutton.state = state; + ev.xbutton.same_screen = (root == dst_root); + + rc = XSendEvent(dpy, dst, True, + SubstructureNotifyMask | (press ? ButtonPressMask : ButtonReleaseMask), + &ev); + return rc && x11_ok(); +} + +int x11_send_expose(Display *dpy, Window dst, int x, int y, int width, int height) +{ + XEvent xe; + int rc; + xe.type = Expose; + xe.xexpose.window = dst; + xe.xexpose.x = x; + xe.xexpose.y = y; + xe.xexpose.width = width; + xe.xexpose.height = height; + xe.xexpose.count = 0; + rc = XSendEvent(tray_data.dpy, dst, True, NoEventMask, &xe); + return x11_ok() && rc != 0; +} + +int x11_refresh_window(Display *dpy, Window dst, int width, int height, int exposures) +{ + x11_send_visibility(tray_data.dpy, dst, VisibilityFullyObscured); + x11_send_visibility(tray_data.dpy, dst, VisibilityUnobscured); + XClearArea(tray_data.dpy, dst, 0, 0, width, height, exposures); + return x11_ok(); +} + +int x11_set_window_size(Display *dpy, Window w, int x, int y) +{ + XSizeHints xsh; + xsh.flags = PSize; + xsh.width = x; + xsh.height = y; + XSetWMNormalHints(dpy, w, &xsh); + XResizeWindow(dpy, w, x, y); + if (!x11_ok()) return FAILURE; + return SUCCESS; +} + +int x11_get_window_size(Display *dpy, Window w, int *x, int *y) +{ + XWindowAttributes xwa; + XGetWindowAttributes(dpy, w, &xwa); + if (!x11_ok()) return FAILURE; + *x = xwa.width; + *y = xwa.height; + return SUCCESS; +} + +int x11_get_window_min_size(Display *dpy, Window w, int *x, int *y) +{ + XSizeHints xsh; + long flags = 0; + int rc = FAILURE; + if (XGetWMNormalHints(dpy, w, &xsh, &flags)) { + flags = xsh.flags & flags; + if (flags & PMinSize) { + *x = xsh.min_width; + *y = xsh.min_height; + rc = SUCCESS; + } + } + return x11_ok() && rc; +} + +int x11_get_window_abs_coords(Display *dpy, Window dst, int *x, int *y) +{ + XWindowAttributes xwa; + Window child; +#if 0 + x11_ok(); /* XXX: this should go away, shouldn't it? */ +#endif + XGetWindowAttributes(dpy, dst, &xwa); + XTranslateCoordinates(dpy, dst, xwa.root, 0, 0, x, y, &child); + return x11_ok(); +} + +char *x11_get_window_name(Display *dpy, Window dst, char *def) +{ + static char *name; + if (name != NULL) XFree(name); + if (!XFetchName(dpy, dst, &name)) name = NULL; + return (name != NULL ? name : def); +} + +Window x11_find_subwindow_by_name(Display *dpy, Window tgt, char *name) +{ + char *tgt_name = NULL; + Window ret = None, *children, dummy; + int i; + unsigned int nchildren; + if (XFetchName(dpy, tgt, &tgt_name)) { + LOG_TRACE(("tgt_name=\"%s\", name=\"%s\"\n", tgt_name, name)); + if (!strcmp(tgt_name, name)) ret = tgt; + } + if (!x11_ok()) return None; + if (tgt_name != NULL) XFree(tgt_name); + if (ret != None) return ret; + XQueryTree(dpy, tgt, &dummy, &dummy, &children, &nchildren); + if (!x11_ok()) return None; + for (i = 0; i < nchildren; i++) { + ret = x11_find_subwindow_by_name(dpy, children[i], name); + if (ret != None) break; + } + if (children != NULL) XFree(children); + return ret; +} + +/* x and y are relative to top at input, and relative to found window on return */ +Window x11_find_subwindow_at(Display *dpy, Window top, int *x, int *y, int depth) +{ + int d, c, bx = 0, by = 0; + Window dummy, *children; + unsigned int nchildren; + Window cur = top, old = None; + for (d = 1; d != depth && cur != old && old != None; d++) + { + old = cur; + /* Query children of current window and traverse them starting + * from the top in stacking order (i.e. from the end of the list + * returned by XQueryTree) */ + XQueryTree(dpy, cur, &dummy, &dummy, &children, &nchildren); + LOG_TRACE(("cur=0x%x nchildren=%d\n", cur, nchildren)); + if (!x11_ok()) goto fail; + /* Exit the loop if window has no children */ + if (nchildren == 0) break; + /* Loop over children starting from topmost */ + for (c = nchildren-1; c >=0; c--) { + XWindowAttributes xwa; + XGetWindowAttributes(dpy, children[c], &xwa); + if (!x11_ok()) goto fail; + /* Check if this child contains the (x,y) point */ + if (xwa.x + bx <= *x && *x < xwa.x + xwa.width + bx && + xwa.y + by <= *y && *y < xwa.y + xwa.height + by) + { + cur = children[c]; + bx += xwa.x; by += xwa.y; + break; + } + } + if (children != NULL) XFree(children); + } + *x -= bx; + *y -= by; + return cur; +fail: + if (children != NULL) XFree(children); + return None; +} + +void x11_extend_root_event_mask(Display *dpy, long mask) +{ + static long old_mask = 0; + old_mask |= mask; + XSelectInput(dpy, DefaultRootWindow(dpy), old_mask); +} + +int x11_parse_color(Display *dpy, char *str, XColor *color) +{ + int rc; + rc = XParseColor(dpy, XDefaultColormap(dpy, DefaultScreen(dpy)), str, color); + if (rc) XAllocColor(dpy, XDefaultColormap(dpy, DefaultScreen(dpy)), color); + return x11_ok() && rc; +} + +#ifdef DEBUG +const char *x11_event_names[LASTEvent] = { + "unknown0", + "unknown1", + "KeyPress", + "KeyRelease", + "ButtonPress", + "ButtonRelease", + "MotionNotify", + "EnterNotify", + "LeaveNotify", + "FocusIn", + "FocusOut", + "KeymapNotify", + "Expose", + "GraphicsExpose", + "NoExpose", + "VisibilityNotify", + "CreateNotify", + "DestroyNotify", + "UnmapNotify", + "MapNotify", + "MapRequest", + "ReparentNotify", + "ConfigureNotify", + "ConfigureRequest", + "GravityNotify", + "ResizeRequest", + "CirculateNotify", + "CirculateRequest", + "PropertyNotify", + "SelectionClear", + "SelectionRequest", + "SelectionNotify", + "ColormapNotify", + "ClientMessage", + "MappingNotify" +}; + +void x11_dump_win_info(Display *dpy, Window wid) +{ +#if defined(DEBUG) && defined(ENABLE_DUMP_WIN_INFO) + if (settings.log_level >= LOG_LEVEL_TRACE) { + char cmd[PATH_MAX]; + snprintf(cmd, PATH_MAX, "xwininfo -tree -size -bits -stats -id 0x%x 1>&2\n", (unsigned int) wid); + system(cmd); + snprintf(cmd, PATH_MAX, "xprop -id 0x%x 1>&2\n", (unsigned int) wid); + system(cmd); + } +#endif +} +#endif diff --git a/gb.desktop.x11/src/systray/xutils.h b/gb.desktop.x11/src/systray/xutils.h new file mode 100644 index 00000000..a517fbc5 --- /dev/null +++ b/gb.desktop.x11/src/systray/xutils.h @@ -0,0 +1,116 @@ +/* ************************************ + * vim:tabstop=4:shiftwidth=4 + * xutils.h + * Sun, 05 Mar 2006 17:16:44 +0600 + * ************************************ + * misc X11 utilities + * ************************************/ + +#ifndef _XUTILS_H_ +#define _XUTILS_H_ + +#include +#include + +#include "common.h" +#include "icons.h" + +/* Returns 1 if connection is active, 0 otherwise */ +int x11_connection_status(); + +/* Return current server timestamp */ +Time x11_get_server_timestamp(Display *dpy, Window wnd); + +/* Convinient way to send a client message event */ +int x11_send_client_msg32(Display *dpy, Window dst, Window wnd, + Atom type, long data0, long data1, + long data2, long data3, long data4); + +/* Same for visibility event */ +int x11_send_visibility(Display *dpy, Window dst, long state); + +/* Same for expose event */ +int x11_send_expose(Display *dpy, Window dst, int x, int y, int width, int height); + +/* Same for button event */ +int x11_send_button(Display *dpy, + int press, Window dst, Window root, Time time, + unsigned int button, unsigned int state, + int x, int y); + +/* Refresh window */ +int x11_refresh_window(Display *dpy, Window dst, int width, int height, int exposures); + +/* Set window size updating its size hints */ +int x11_set_window_size(Display *dpy, Window w, int x, int y); + +/* Get window size (uses XGetWindowAttributes) */ +int x11_get_window_size(Display *dpy, Window w, int *x, int *y); + +/* Get window minimal size hints if they are available */ +int x11_get_window_min_size(Display *dpy, Window w, int *x, int *y); + +/* Retrive 32-bit property from the target window */ +int x11_get_window_prop32(Display *dpy, Window dst, Atom atom, Atom type, unsigned char **data, unsigned long *len); + +/* Retrive window-list property from the specified window */ +#define x11_get_winlist_prop(dpy, dst, atom, data, len) x11_get_window_prop32(dpy, dst, atom, XA_WINDOW, data, len) + +/* Shortcut for the root window case */ +#define x11_get_root_winlist_prop(dpy, atom, data, len) x11_get_winlist_prop(dpy, DefaultRootWindow(dpy), atom, data, len) + +/* Returns window absolute position (relative to the root window) */ +int x11_get_window_abs_coords(Display *dpy, Window dst, int *x, int *y); + +/* Get window name. NOT THREAD SAFE. Returns pointer to static buffer */ +char *x11_get_window_name(Display *dpy, Window dst, char *def); + +/* Find subwindow by name */ +Window x11_find_subwindow_by_name(Display *dpy, Window tgt, char *name); + +/* Find subwindow by at coords */ +Window x11_find_subwindow_at(Display *dpy, Window top, int *x, int *y, int depth); + +/* Extends event mask of the root window */ +void x11_extend_root_event_mask(Display *dpy, long mask); + +/* Parse text representation of a color */ +int x11_parse_color(Display *dpy, char *str, XColor *color); + +/* Checks if any X11 errors have occured so far. */ +/* ACHTUNG!!! after any sequence X operations any of which + * that might have failed, you _must_ call x11_ok(), since + * it resets the internal error flag. If you dont, it will show + * up as a error elsewhere. JFYI, always check for x11_ok() first, + * since + * if (!rc || x11_ok()) { fail; } + * is likely to leave error condition on: x11_ok() wont be called + * if rc != 0. */ +#define x11_ok() x11_ok_helper(__FILE__, __LINE__, __FUNC__) +int x11_ok_helper(const char * file, const int line, const char *func); + +/* WARNING: following functions do not support nested calls */ + +/* Installs custom X11 error handler */ +void x11_trap_errors(); + +/* Removes custom X11 error handler */ +int x11_untrap_errors(); + +#ifdef DEBUG + +/* Array that maps event_number -> event_name */ +const extern char *x11_event_names[LASTEvent]; + +/* Dumps window info. Does nothing unless ENABLE_DUMP_WIN_INFO is defined, + * launches xwininfo and xwinprop otherwise */ +void x11_dump_win_info(Display *dpy, Window w); + +#else + +/* Dummy delcaration */ +#define x11_dump_win_info(dpy,w) do {} while (0); +#endif + +#endif + diff --git a/gb.desktop.x11/src/x11.c b/gb.desktop.x11/src/x11.c new file mode 100644 index 00000000..45d7de9c --- /dev/null +++ b/gb.desktop.x11/src/x11.c @@ -0,0 +1,835 @@ +/*************************************************************************** + + x11.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __X11_C + +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "x11.h" +#include "c_x11.h" +#include "systray/systray.h" + +#define MAX_WINDOW_PROP 16 + +Atom X11_atom_net_wm_state; +Atom X11_atom_net_wm_state_above; +Atom X11_atom_net_wm_state_below; +Atom X11_atom_net_wm_state_stays_on_top; +Atom X11_atom_net_wm_state_skip_taskbar; +Atom X11_atom_net_wm_window_type; +Atom X11_atom_net_wm_window_type_normal; +Atom X11_atom_net_wm_window_type_utility; +Atom X11_atom_net_wm_desktop; +Atom X11_atom_net_wm_user_time; +Atom X11_atom_net_current_desktop; +Atom X11_atom_net_workarea = None; +Atom X11_atom_motif_wm_hints = None; +Atom X11_atom_net_system_tray = None; + +Atom X11_UTF8_STRING; + +bool X11_ready = FALSE; + +Display *X11_display = NULL; +#define _display X11_display +Window X11_root = 0; +#define _root X11_root +bool X11_event_filter_enabled = FALSE; + +static bool _atom_init = FALSE; + +static bool _has_test_extension = FALSE; +static bool _init_keycode = FALSE; + +static int _min_keycode, _max_keycode, _keysyms_per_keycode; +static KeySym *_keycode_map = NULL; + +static XModifierKeymap *_modifier_map = NULL; +static KeyCode *_shift_keycode = NULL; +static KeyCode *_alt_gr_keycode = NULL; + +typedef + struct { + int count; + Atom atoms[MAX_WINDOW_PROP]; + bool changed; + } + X11_PROPERTY; + +static X11_PROPERTY _window_prop; +static X11_PROPERTY _window_save[2]; + +static char *_property_value = NULL; + +static GB_FUNCTION _x11_property_notify_func; +static GB_FUNCTION _x11_configure_notify_func; + +static void init_atoms() +{ + if (_atom_init) + return; + + //fprintf(stderr, "init_atom: display = %p\n", _display); + X11_atom_net_current_desktop = XInternAtom(_display, "_NET_CURRENT_DESKTOP", True); + //fprintf(stderr, "init_atom: #1\n"); + X11_atom_net_wm_state = XInternAtom(_display, "_NET_WM_STATE", True); + X11_atom_net_wm_state_above = XInternAtom(_display, "_NET_WM_STATE_ABOVE", True); + X11_atom_net_wm_state_below = XInternAtom(_display, "_NET_WM_STATE_BELOW", True); + X11_atom_net_wm_state_stays_on_top = XInternAtom(_display, "_NET_WM_STATE_STAYS_ON_TOP", True); + X11_atom_net_wm_state_skip_taskbar = XInternAtom(_display, "_NET_WM_STATE_SKIP_TASKBAR", True); + + X11_atom_net_wm_desktop = XInternAtom(_display, "_NET_WM_DESKTOP", True); + X11_atom_net_wm_window_type = XInternAtom(_display, "_NET_WM_WINDOW_TYPE", True); + X11_atom_net_wm_window_type_normal = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_NORMAL", True); + X11_atom_net_wm_window_type_utility = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_UTILITY", True); + X11_atom_net_wm_user_time = XInternAtom(_display, "_NET_WM_USER_TIME", True); + + X11_UTF8_STRING = XInternAtom(X11_display, "UTF8_STRING", True); + + _atom_init = TRUE; +} + +#define PROPERTY_START_READ 1024 +#define PROPERTY_NEXT_READ 1024 + +#if 0 + // On 64 bits OS, format 32 are actually long, i.e. int padded to 64 bits! + #if OS_64BITS + if (format == 32) + { + int *p = (int *)_property_value; + for (i = 0; i < count; i++) + p[i] = *((long *)data + i); + } + else + #endif + { + memcpy(_property_value, (char *)data, count * size); + } +#endif + +static size_t sizeof_format(int format) +{ + return format == 32 ? sizeof(long) : (format == 16 ? sizeof(short) : sizeof(char)); +} + +char *X11_get_property(Window wid, Atom prop, Atom *type, int *format, int *pcount) +{ + uchar *data; + unsigned long count; + unsigned long after; + unsigned long offset; + int size, offset_size; + + *pcount = 0; + + if (XGetWindowProperty(_display, wid, prop, 0, PROPERTY_START_READ / sizeof(int32_t), + False, AnyPropertyType, type, format, + &count, &after, &data) != Success) + return NULL; + + *pcount += count; + + size = sizeof_format(*format); + offset_size = *format == 32 ? sizeof(int32_t) : ( *format == 16 ? sizeof(short) : 1 ); + + //fprintf(stderr, "X11_get_property: format = %d size = %d count = %ld after = %ld\n", *format, size, count, after); + + GB.FreeString(&_property_value); + _property_value = GB.NewString((char *)data, count * size); + XFree(data); + + offset = count * offset_size / sizeof(int32_t); + + while (after) + { + //fprintf(stderr, "X11_get_property: offset = %ld read = %ld\n", offset, Min(after, PROPERTY_NEXT_READ) / sizeof(int32_t)); + + if (XGetWindowProperty(_display, wid, prop, offset, Min(after, PROPERTY_NEXT_READ) / sizeof(int32_t), + False, AnyPropertyType, type, format, + &count, &after, &data) != Success) + return NULL; + + *pcount += count; + offset += count * offset_size / sizeof(int32_t); + + //fprintf(stderr, "X11_get_property: format = %d size = %d count = %ld after = %ld next offset = %ld\n", *format, size, count, after, offset); + + _property_value = GB.AddString(_property_value, (char *)data, count * size); + XFree(data); + } + + return _property_value; +} + +#if 0 +static void get_property(Window wid, Atom prop, int maxlength, unsigned char **data, unsigned long *count) +{ + Atom type; + int format; + unsigned long after; + + XGetWindowProperty(_display, wid, prop, 0, maxlength / 4, + False, AnyPropertyType, &type, &format, + count, &after, data); +} +#endif + +static char *get_property(Window wid, Atom prop, int *count) +{ + Atom type; + int format; + + return X11_get_property(wid, prop, &type, &format, count); +} + +Atom X11_get_property_type(Window wid, Atom prop, int *format) +{ + uchar *data = NULL; + unsigned long count; + unsigned long after; + Atom type; + + if (XGetWindowProperty(X11_display, wid, prop, 0, PROPERTY_START_READ / sizeof(int32_t), + False, AnyPropertyType, &type, format, + &count, &after, &data) != Success) + return None; + + XFree(data); + return type; +} + +#if 0 + #if OS_64BITS + long padded_data[count]; + int i; + + if (format == 32) + { + for (i = 0; i < count; i++) + padded_data[i] = ((int *)data)[i]; + } + data = padded_data; + #endif +#endif + +void X11_set_property(Window wid, Atom prop, Atom type, int format, void *data, int count) +{ + XChangeProperty(X11_display, wid, prop, type, format, PropModeReplace, (uchar *)data, count); +} + +Atom X11_intern_atom(const char *name, bool create) +{ + int val = atoi(name); + + if (val) + return (Atom)val; + else + return XInternAtom(X11_display, name, !create); +} + +static void load_window_state(Window win, Atom prop) +{ + int length; + char *data; + + _window_prop.count = 0; + _window_prop.changed = FALSE; + + data = get_property(win, prop, &length); + + if (length > MAX_WINDOW_PROP) + length = MAX_WINDOW_PROP; + + _window_prop.count = length; + if (data) + memcpy(_window_prop.atoms, data, length * sizeof(Atom)); +} + +static void save_window_state(Window win, Atom prop) +{ + if (_window_prop.changed) + { + //fprintf(stderr, "XChangeProperty: %ld %ld\n", (long)win, (long)prop); + XChangeProperty(_display, win, prop, XA_ATOM, 32, PropModeReplace, (unsigned char *)_window_prop.atoms, _window_prop.count); + } +} + +static bool has_window_state(Atom prop) +{ + int i; + + for (i = 0; i < _window_prop.count; i++) + { + if (_window_prop.atoms[i] == prop) + return TRUE; + } + + return FALSE; +} + +static void set_window_state(Atom prop) +{ + if (has_window_state(prop)) + return; + + if (_window_prop.count == MAX_WINDOW_PROP) + { + fprintf(stderr, "X11: set_window_state: Too many properties in window\n"); + return; + } + + _window_prop.atoms[_window_prop.count++] = prop; + _window_prop.changed = TRUE; +} + +static void clear_window_state(Atom prop) +{ + int i; + + for (i = 0; i < _window_prop.count; i++) + { + if (_window_prop.atoms[i] == prop) + { + _window_prop.count--; + + for (; i < _window_prop.count; i++) + _window_prop.atoms[i] = _window_prop.atoms[i + 1]; + + _window_prop.changed = TRUE; + return; + } + } +} + + +bool X11_do_init() +{ + int event_base, error_base, major_version, minor_version; + + if (X11_ready) + return FALSE; + + GB.Component.GetInfo("DISPLAY", POINTER(&_display)); + + _root = DefaultRootWindow(_display); + + X11_ready = _display != NULL; + + if (!X11_ready) + { + fprintf(stderr, "WARNING: X11_init() has failed\n"); + return TRUE; + } + + init_atoms(); + + _has_test_extension = XTestQueryExtension(_display, &event_base, &error_base, &major_version, &minor_version); + + return FALSE; +} + +void X11_exit() +{ + if (_keycode_map) + XFree(_keycode_map); + if (_modifier_map) + XFreeModifiermap(_modifier_map); + if (_property_value) + GB.FreeString(&_property_value); +} + +void X11_send_client_message(Window dest, Window window, Atom message, char *data, int format, int count) +{ + XEvent e; + int mask = (SubstructureRedirectMask | SubstructureNotifyMask); + + //fprintf(stderr, "X11_send_client_message: dest = %ld window = %ld message = %ld format = %d count = %d\n", dest, window, message, format, count); + + e.xclient.type = ClientMessage; + e.xclient.message_type = message; + e.xclient.display = X11_display; + e.xclient.window = window; + e.xclient.format = format; + + memset(&e.xclient.data.l[0], 0, sizeof(long) * 5); + if (data) + { + count *= sizeof_format(format); + if (count > (sizeof(long) * 5)) + count = sizeof(long) * 5; + memcpy(&e.xclient.data.l[0], data, count); + //fprintf(stderr, "%ld %ld %ld %ld %ld\n", e.xclient.data.l[0], e.xclient.data.l[1], e.xclient.data.l[2], e.xclient.data.l[3], e.xclient.data.l[4]); + } + + XSendEvent(X11_display, dest, False, mask, &e); + XFlush(X11_display); +} + +void X11_window_change_property(Window window, bool visible, Atom property, bool set) +{ + XEvent e; + long mask = (SubstructureRedirectMask | SubstructureNotifyMask); + + if (visible) + { + e.xclient.type = ClientMessage; + e.xclient.message_type = X11_atom_net_wm_state; + e.xclient.display = _display; + e.xclient.window = window; + e.xclient.format = 32; + e.xclient.data.l[0] = set ? 1 : 0; + e.xclient.data.l[1] = property; + e.xclient.data.l[2] = 0; + e.xclient.data.l[3] = 0; + e.xclient.data.l[4] = 0; + + XSendEvent(_display, _root, False, mask, &e); + XFlush(X11_display); + } + else + { + load_window_state(window, X11_atom_net_wm_state); + + if (set) + set_window_state(property); + else + clear_window_state(property); + + save_window_state(window, X11_atom_net_wm_state); + } +} + + +bool X11_window_has_property(Window window, Atom property) +{ + load_window_state(window, X11_atom_net_wm_state); + return has_window_state(property); +} + +void X11_sync(void) +{ + XSync(_display, False); +} + +void X11_window_save_properties(Window window) +{ + load_window_state(window, X11_atom_net_wm_state); + _window_save[0] = _window_prop; + //load_window_state(window, X11_atom_net_wm_window_type); + //_window_save[1] = _window_prop; +} + +void X11_window_restore_properties(Window window) +{ + _window_prop = _window_save[0]; + save_window_state(window, X11_atom_net_wm_state); + //_window_prop = _window_save[1]; + //save_window_state(window, X11_atom_net_wm_window_type); +} + +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +#define OPCODE "_NET_SYSTEM_TRAY_OPCODE" + +void X11_window_dock(Window window) +{ + Window xmanager=None; + XClientMessageEvent ev; + Atom OpCodeAtom; + Screen *xscreen; + char buf[256]; + Atom selection_atom; + + buf[0]=0; + + xscreen = DefaultScreenOfDisplay(_display); + sprintf(buf,"_NET_SYSTEM_TRAY_S%d",XScreenNumberOfScreen(xscreen)); + selection_atom = XInternAtom(_display, buf, 0); + + XGrabServer(_display); + + xmanager = XGetSelectionOwner(_display, selection_atom); + if (xmanager != None) + XSelectInput(_display, xmanager, StructureNotifyMask); + + XUngrabServer(_display); + XFlush(_display); + + /*********************************************** + Dock Tray Icon + ************************************************/ + + OpCodeAtom = XInternAtom(_display, OPCODE, 0); + + ev.type = ClientMessage; + ev.window = xmanager; + ev.message_type = OpCodeAtom; + ev.format = 32; + ev.data.l[0] = 0; + ev.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK; + ev.data.l[2] = window; + ev.data.l[3] = 0; + ev.data.l[4] = 0; + + XSendEvent(_display, xmanager, 0, NoEventMask, (XEvent *)&ev); + XSync(_display, 0); +} + +void X11_window_startup(Window window, int x, int y, int w, int h) +{ + XSizeHints s; + + s.flags = USPosition | PPosition | USSize | PSize; + + s.x = x; + s.y = y; + s.width = w; + s.height = h; + + XSetWMNormalHints(_display, window, &s); +} + +void X11_find_windows(Window **window_list, int *count) +{ + static Atom _net_client_list = 0; + + if (!_net_client_list) + _net_client_list = XInternAtom(_display, "_NET_CLIENT_LIST", True); + + *window_list = (Window *)get_property(_root, _net_client_list, count); +} + +void X11_get_window_title(Window window, char **result, int *length) +{ + static Atom _net_wm_name = 0; + + if (!_net_wm_name) + _net_wm_name = XInternAtom(_display, "_NET_WM_NAME", True); + + *result = get_property(window, _net_wm_name, length); +} + +void X11_get_window_class(Window window, char **result, int *length) +{ + *result = get_property(window, XA_WM_CLASS, length); +} + +void X11_get_window_role(Window window, char **result, int *length) +{ + static Atom wm_window_role = (Atom)0; + + if (!wm_window_role) + wm_window_role = XInternAtom(_display, "WM_WINDOW_ROLE", True); + + *result = get_property(window, wm_window_role, length); +} + + +// Makes a tool window + +void X11_set_window_tool(Window window, int tool, Window parent) +{ + load_window_state(window, X11_atom_net_wm_window_type); + + if (tool) + { + set_window_state(X11_atom_net_wm_window_type_utility); + clear_window_state(X11_atom_net_wm_window_type_normal); + if (parent) + XSetTransientForHint(_display, window, parent); + } + else + { + clear_window_state(X11_atom_net_wm_window_type_utility); + set_window_state(X11_atom_net_wm_window_type_normal); + } + + save_window_state(window, X11_atom_net_wm_window_type); +} + +int X11_get_window_tool(Window window) +{ + load_window_state(window, X11_atom_net_wm_window_type); + return has_window_state(X11_atom_net_wm_window_type_utility); +} + + +// Set window desktop + +void X11_window_set_desktop(Window window, bool visible, int desktop) +{ + XEvent e; + long mask = (SubstructureRedirectMask | SubstructureNotifyMask); + + if (visible) + { + e.xclient.type = ClientMessage; + e.xclient.message_type = X11_atom_net_wm_desktop; + e.xclient.display = _display; + e.xclient.window = window; + e.xclient.format = 32; + e.xclient.data.l[0] = desktop; + e.xclient.data.l[1] = 1; + e.xclient.data.l[2] = 0; + e.xclient.data.l[3] = 0; + e.xclient.data.l[4] = 0; + + XSendEvent(_display, _root, False, mask, &e); + XFlush(_display); + } + else + { + XChangeProperty(_display, window, X11_atom_net_wm_desktop, XA_CARDINAL, 32, PropModeReplace, + (unsigned char *)&desktop, 1); + XFlush(_display); + } +} + + +int X11_window_get_desktop(Window window) +{ + int length; + char *data = NULL; + int desktop = 0; + + data = get_property(window, X11_atom_net_wm_desktop, &length); + if (data) + desktop = *((int *)data); + + return desktop; +} + +int X11_get_current_desktop() +{ + int length; + char *data; + int desktop = 0; + + data = get_property(_root, X11_atom_net_current_desktop, &length); + if (data) + desktop = *((int *)data); + + return desktop; +} + +static void init_keycode() +{ + int i, j; //, k; + KeyCode *pm, *p; + + if (_init_keycode) + return; + + XDisplayKeycodes(_display, &_min_keycode, &_max_keycode); + + _keycode_map = XGetKeyboardMapping(_display, _min_keycode, _max_keycode - _min_keycode + 1, &_keysyms_per_keycode); + _modifier_map = XGetModifierMapping(_display); + + p = _modifier_map->modifiermap; + for (i = 0; i < 8; i++) + { + pm = p; + for (j = 0; j < _modifier_map->max_keypermod; j++) + { + //for (k = 0; k < 3; k++) + switch (XkbKeycodeToKeysym(_display, *p, 0, 0)) + { + case XK_Shift_L: _shift_keycode = pm; break; + case XK_Mode_switch: _alt_gr_keycode = pm; break; + } + p++; + } + } + + //fprintf(stderr, "SHIFT: %d ALTGR: %d\n", _shift_keycode[0], _alt_gr_keycode[0]); + + _init_keycode = TRUE; +} + +static void send_modifiers(KeyCode *codes, bool press) +{ + int i; + + for (i = 0; i < _modifier_map->max_keypermod; i++) + { + if (codes[i]) + XTestFakeKeyEvent(_display, codes[i], press, CurrentTime); + } +} + +static void handle_modifier(KeyCode code, KeySym keysym, bool press) +{ + KeySym *sym; + int i; + + sym = &_keycode_map[(code - _min_keycode) * _keysyms_per_keycode]; + + for (i = 0; i < _keysyms_per_keycode; i++) + { + if (keysym == sym[i]) + { + //fprintf(stderr, "0x%04X: %d\n", keysym, i); + break; + } + } + + switch(i) + { + case 1: + send_modifiers(_shift_keycode, press); + break; + case 2: + send_modifiers(_alt_gr_keycode, press); + break; + case 3: + send_modifiers(_shift_keycode, press); + send_modifiers(_alt_gr_keycode, press); + break; + //default: + // fprintf(stderr, "0x%04X: ?\n", keysym); + } +} + +char *X11_send_key(char *key, bool press) +{ + KeySym keysym; + KeyCode code; + + if (!_has_test_extension) + return "No XTEST extension"; + + if (!_init_keycode) + init_keycode(); + + //fprintf(stderr, "X11_send_key: '%s' %d\n", key, XStringToKeysym(key)); + + if (strlen(key) == 1) + { + if (*key == '\n') + keysym = XK_Return; + else if (*key == '\t') + keysym = XK_Tab; + else if (*key >= ' ' || *key < 0) + keysym = (int)*key & 0xFF; + else + keysym = NoSymbol; + } + else + keysym = XStringToKeysym(key); + + if (keysym == NoSymbol) + return "Unknown key"; + + code = XKeysymToKeycode(_display, keysym); + if (code) + { + if (press) + handle_modifier(code, keysym, TRUE); + + XTestFakeKeyEvent(_display, code, press, CurrentTime); + + if (press) + handle_modifier(code, keysym, FALSE); + } + + usleep(1000); + + return NULL; +} + +void X11_enable_event_filter(bool enable) +{ + static int count = 0; + + void (*set_event_filter)(void *) = NULL; + + if (enable) + count++; + else + count--; + + GB.Component.GetInfo("SET_EVENT_FILTER", POINTER(&set_event_filter)); + if (set_event_filter) + (*set_event_filter)(count ? X11_event_filter : NULL); +} + +void X11_event_filter(XEvent *e) +{ + static bool init = FALSE; + + if (!init) + { + GB_CLASS startup = GB.Application.StartupClass(); + GB.GetFunction(&_x11_property_notify_func, (void *)startup, "X11_PropertyNotify", "ii", ""); + GB.GetFunction(&_x11_configure_notify_func, (void *)startup, "X11_ConfigureNotify", "iiiii", ""); + init = TRUE; + } + + if (e->type == PropertyNotify && GB_FUNCTION_IS_VALID(&_x11_property_notify_func)) + { + GB.Push(2, GB_T_INTEGER, e->xany.window, GB_T_INTEGER, e->xproperty.atom); + GB.Call(&_x11_property_notify_func, 2, TRUE); + } + else if (e->type == ConfigureNotify && GB_FUNCTION_IS_VALID(&_x11_configure_notify_func)) + { + GB.Push(5, GB_T_INTEGER, e->xany.window, + GB_T_INTEGER, e->xconfigure.x, + GB_T_INTEGER, e->xconfigure.y, + GB_T_INTEGER, e->xconfigure.width, + GB_T_INTEGER, e->xconfigure.height); + + GB.Call(&_x11_configure_notify_func, 5, TRUE); + } + + WATCHER_event_filter(e); + SYSTRAY_event_filter(e); +} + +void X11_get_window_geometry(Window win, int *wx, int *wy, int *ww, int *wh) +{ + Window p; + int transx, transy; + XWindowAttributes wattr; + + *wx = *wy = *ww = *wh = 0; + + if (!XTranslateCoordinates(_display, win, _root, 0, 0, &transx, &transy, &p)) + return; + + if (!XGetWindowAttributes(_display, win, &wattr)) + return; + + *wx = transx - wattr.border_width; + *wy = transy - wattr.border_width; + *ww = wattr.width + wattr.border_width * 2; + *wh = wattr.height + wattr.border_width * 2; +} diff --git a/gb.desktop.x11/src/x11.h b/gb.desktop.x11/src/x11.h new file mode 100644 index 00000000..1070ad02 --- /dev/null +++ b/gb.desktop.x11/src/x11.h @@ -0,0 +1,116 @@ +/*************************************************************************** + + x11.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __X11_H +#define __X11_H + +#include +#include +#include +#include +#include +#include + +#ifdef QT_VERSION +#undef FocusIn +#undef FocusOut +#undef KeyPress +#undef KeyRelease +#endif + +#include "gambas.h" +#include "gb_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __X11_C +EXTERN Atom X11_atom_net_wm_state; +EXTERN Atom X11_atom_net_wm_state_above; +EXTERN Atom X11_atom_net_wm_state_below; +EXTERN Atom X11_atom_net_wm_state_stays_on_top; +EXTERN Atom X11_atom_net_wm_state_skip_taskbar; +EXTERN Atom X11_atom_net_wm_window_type; +EXTERN Atom X11_atom_net_wm_desktop; + +EXTERN Window X11_root; +EXTERN Display *X11_display; +EXTERN bool X11_ready; +EXTERN bool X11_event_filter_enabled; + +EXTERN Atom X11_UTF8_STRING; +#endif + +typedef + struct { + char *title; + char *klass; + char *role; + } + X11_WINDOW_INFO; + +bool X11_do_init(); +#define X11_init() (!X11_ready && X11_do_init()) +void X11_exit(); +void X11_sync(void); + +#define X11_get_screen_count() ScreenCount(X11_display) +#define X11_get_root_window(_screen) RootWindow(X11_display, (_screen)) + +/* Functions to deal with the _NET_WM_STATE and _NET_WM_TYPE property */ +void X11_window_change_property(Window window, bool visible, Atom property, bool set); +bool X11_window_has_property(Window window, Atom property); +void X11_window_save_properties(Window window); +void X11_window_restore_properties(Window window); +/* Function to dock a window in the system tray */ +void X11_window_dock(Window window); +/* Function to define startup position hints for a window being shown */ +void X11_window_startup(Window window, int x, int y, int w, int h); +/* Functions to search for a specific top-level window */ +void X11_find_windows(Window **window_list, int *count); +void X11_get_window_title(Window window, char **result, int *length); +void X11_get_window_class(Window window, char **result, int *length); +void X11_get_window_role(Window window, char **result, int *length); +void X11_get_window_geometry(Window win, int *wx, int *wy, int *ww, int *wh); +/* Function to make a tool window */ +void X11_set_window_tool(Window window, int tool, Window parent); +int X11_get_window_tool(Window window); +void X11_window_set_desktop(Window window, bool visible, int desktop); +int X11_window_get_desktop(Window window); +int X11_get_current_desktop(); +char *X11_send_key(char *key, bool press); + +Atom X11_intern_atom(const char *name, bool create); +char *X11_get_property(Window wid, Atom prop, Atom *type, int *format, int *count); +Atom X11_get_property_type(Window wid, Atom prop, int *format); +void X11_set_property(Window wid, Atom prop, Atom type, int format, void *data, int count); +void X11_send_client_message(Window dest, Window window, Atom message, char *data, int format, int count); +void X11_event_filter(XEvent *e); +void X11_enable_event_filter(bool enable); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gb.gmp/AUTHORS b/gb.gmp/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.gmp/COPYING b/gb.gmp/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.gmp/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.gmp/ChangeLog b/gb.gmp/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.gmp/INSTALL b/gb.gmp/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.gmp/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.gmp/Makefile.am b/gb.gmp/Makefile.am new file mode 100644 index 00000000..28e8c41a --- /dev/null +++ b/gb.gmp/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @GMP_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.gmp/NEWS b/gb.gmp/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.gmp/README b/gb.gmp/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.gmp/acinclude.m4 b/gb.gmp/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.gmp/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.gmp/component.am b/gb.gmp/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.gmp/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.gmp/configure.ac b/gb.gmp/configure.ac new file mode 100644 index 00000000..56d924e4 --- /dev/null +++ b/gb.gmp/configure.ac @@ -0,0 +1,20 @@ +dnl ---- configure.ac for gb.gmp + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-gmp, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.gmp) +AC_PROG_LIBTOOL + +GB_COMPONENT( + gmp, + GMP, + gb.gmp, + [src], + [GB_FIND(gmp.h, $prefix /usr/local /usr /opt/local, include)], + [GB_FIND(libgmp.$SHLIBEXT, $prefix /usr/local /usr /opt/local, lib)], + [$C_LIB -lgmp], + [ ]) +AC_OUTPUT( Makefile src/Makefile ) +GB_PRINT_MESSAGES diff --git a/gb.gmp/gambas.h b/gb.gmp/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.gmp/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.gmp/gb_common.h b/gb.gmp/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.gmp/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.gmp/m4 b/gb.gmp/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.gmp/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.gmp/reconf b/gb.gmp/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.gmp/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.gmp/src/Makefile.am b/gb.gmp/src/Makefile.am new file mode 100644 index 00000000..a25326a6 --- /dev/null +++ b/gb.gmp/src/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.gmp +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gmp.la + +gb_gmp_la_LIBADD = @GMP_LIB@ +gb_gmp_la_LDFLAGS = -module @LD_FLAGS@ @GMP_LDFLAGS@ +gb_gmp_la_CPPFLAGS = @GMP_INC@ + +gb_gmp_la_SOURCES = \ + main.c main.h \ + c_bigint.c c_bigint.h \ + c_rational.c c_rational.h diff --git a/gb.gmp/src/c_bigint.c b/gb.gmp/src/c_bigint.c new file mode 100644 index 00000000..e1733ac5 --- /dev/null +++ b/gb.gmp/src/c_bigint.c @@ -0,0 +1,682 @@ +/*************************************************************************** + + c_bigint.c + + gb.gmp component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_BIGINT_C + +#include "main.h" +#include "c_bigint.h" + +#define THIS ((CBIGINT *)_object) +#define NUMBER (THIS->n) + +//---- BigInt number creation ---------------------------------------------- + +CBIGINT *BIGINT_create(mpz_t number) +{ + CBIGINT *c; + + c = (CBIGINT *)GB.New(CLASS_BigInt, NULL, NULL); + mpz_set(c->n, number); + mpz_clear(number); + + return c; +} + +#define BIGINT_make(_a, _b, _op) \ +({ \ + if ((_a)->ob.ref <= 1) \ + _op((_a)->n, (_a)->n, (_b)->n); \ + else \ + { \ + mpz_t n; \ + mpz_init(n); \ + _op(n, (_a)->n, (_b)->n); \ + _a = BIGINT_create(n); \ + } \ + _a; \ +}) + +#define BIGINT_make_unary(_a, _op) \ +({ \ + if ((_a)->ob.ref <= 1) \ + _op((_a)->n, (_a)->n); \ + else \ + { \ + mpz_t n; \ + mpz_init(n); \ + _op(n, (_a)->n); \ + _a = BIGINT_create(n); \ + } \ + _a; \ +}) + +#define BIGINT_make_int(__a, _f, _op) \ +({ \ + CBIGINT *_a = (__a); \ + if (_a->ob.ref <= 1) \ + _op(_a->n, _a->n, (ulong)(_f)); \ + else \ + { \ + mpz_t n; \ + mpz_init(n); \ + _op(n, _a->n, (ulong)(_f)); \ + _a = BIGINT_create(n); \ + } \ + _a; \ +}) + +#define BIGINT_make_int_invert(_a, _f, _op) \ +({ \ + if ((_a)->ob.ref <= 1) \ + _op((_a)->n, (ulong)(_f), (_a)->n); \ + else \ + { \ + mpz_t n; \ + mpz_init(n); \ + _op(n, (ulong)(_f), (_a)->n); \ + _a = BIGINT_create(n); \ + } \ + _a; \ +}) + +#define BIGINT_make_bit(__a, _n, _op) \ +({ \ + CBIGINT *_a = (__a); \ + if (_a->ob.ref <= 1) \ + _op(_a->n, _a->n, (_n)); \ + else \ + { \ + mpz_t n; \ + mpz_init(n); \ + _op(n, _a->n, (_n)); \ + _a = BIGINT_create(n); \ + } \ + _a; \ +}) + +//---- Arithmetic operators ------------------------------------------------- + +static CBIGINT *_addf(CBIGINT *a, double f, bool invert) +{ + if (f < 0) + return BIGINT_make_int(a, (-f), mpz_sub_ui); + else + return BIGINT_make_int(a, f, mpz_add_ui); +} + +static CBIGINT *_add(CBIGINT *a, CBIGINT *b, bool invert) +{ + return BIGINT_make(a, b, mpz_add); +} + +static CBIGINT *_subf(CBIGINT *a, double f, bool invert) +{ + if (invert) + { + if (f < 0) + return BIGINT_make_int(a, (-f), mpz_add_ui); + else + return BIGINT_make_int_invert(a, f, mpz_ui_sub); + } + else + { + if (f < 0) + return BIGINT_make_int(a, (-f), mpz_add_ui); + else + return BIGINT_make_int(a, f, mpz_sub_ui); + } +} + +static CBIGINT *_sub(CBIGINT *a, CBIGINT *b, bool invert) +{ + return BIGINT_make(a, b, mpz_sub); +} + +static CBIGINT *_mulf(CBIGINT *a, double f, bool invert) +{ + return BIGINT_make_int(a, f, mpz_mul_si); +} + +static CBIGINT *_mul(CBIGINT *a, CBIGINT *b, bool invert) +{ + return BIGINT_make(a, b, mpz_mul); +} + +static CBIGINT *_div(CBIGINT *a, CBIGINT *b, bool invert) +{ + if (mpz_cmp_si(b->n, 0) == 0) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + else + return BIGINT_make(a, b, mpz_tdiv_q); +} + +static CBIGINT *_divf(CBIGINT *a, double f, bool invert) +{ + if (invert) + { + CBIGINT *b; + mpz_t n; + + mpz_init_set_d(n, f); + b = BIGINT_create(n); + + return _div(b, a, FALSE); + } + else + { + if (f > 0) + { + return BIGINT_make_int(a, f, mpz_tdiv_q_ui); + } + else if (f < 0) + { + a = BIGINT_make_int(a, (-f), mpz_tdiv_q_ui); + mpz_neg(a->n, a->n); + return a; + } + else + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + } +} + +static int _equal(CBIGINT *a, CBIGINT *b, bool invert) +{ + return mpz_cmp(a->n, b->n) == 0; +} + +static int _equalf(CBIGINT *a, double f, bool invert) +{ + return mpz_cmp_d(a->n, f) == 0; +} + +static int _comp(CBIGINT *a, CBIGINT *b, bool invert) +{ + return mpz_cmp(a->n, b->n); +} + +static int _compf(CBIGINT *a, double f, bool invert) +{ + return mpz_cmp_d(a->n, f); +} + +static CBIGINT *_neg(CBIGINT *a) +{ + return BIGINT_make_unary(a, mpz_neg); +} + +static CBIGINT *_abs(CBIGINT *a) +{ + return BIGINT_make_unary(a, mpz_abs); +} + +static int _sgn(CBIGINT *a) +{ + return mpz_sgn(a->n); +} + +static CBIGINT *_pow(CBIGINT *a, CBIGINT *b, bool invert) +{ + if (!mpz_fits_slong_p(b->n)) + { + GB.Error(GB_ERR_OVERFLOW); + return NULL; + } + + return BIGINT_make_int(a, mpz_get_si(b->n), mpz_pow_ui); +} + +static CBIGINT *_powf(CBIGINT *a, double f, bool invert) +{ + if (invert) + { + mpz_t b; + + if (!mpz_fits_slong_p(a->n)) + return NULL; + + mpz_init_set_si(b, (long)f); + mpz_pow_ui(b, b, mpz_get_si(a->n)); + + return BIGINT_create(b); + } + else + return BIGINT_make_int(a, f, mpz_pow_ui); +} + + +static GB_OPERATOR_DESC _operator = +{ + .equal = (void *)_equal, + .equalf = (void *)_equalf, + .comp = (void *)_comp, + .compf = (void *)_compf, + .add = (void *)_add, + .addf = (void *)_addf, + .sub = (void *)_sub, + .subf = (void *)_subf, + .mul = (void *)_mul, + .mulf = (void *)_mulf, + .div = (void *)_div, + .divf = (void *)_divf, + .pow = (void *)_pow, + .powf = (void *)_powf, + .abs = (void *)_abs, + .neg = (void *)_neg, + .sgn = (void *)_sgn +}; + +//---- Conversions ---------------------------------------------------------- + +char *BIGINT_to_string(mpz_t n, int base) +{ + char *str; + int len; + + //if (mpz_cmp_si(n, 0) == 0) + // return GB.NewZeroString("0"); + + len = mpz_sizeinbase (n, base); + if (mpz_sgn(n) < 0) + len++; + + str = GB.NewString(NULL, len); + mpz_get_str(str, -base, n); + + if (str[len - 1] == 0) + str = GB.ExtendString(str, len - 1); + + return str; +} + +CBIGINT *BIGINT_from_string(char *str, int base) +{ + mpz_t n; + + if (mpz_init_set_str(n, str, base)) + return NULL; + else + return BIGINT_create(n); +} + +static bool _convert(CBIGINT *a, GB_TYPE type, GB_VALUE *conv) +{ + if (a) + { + switch (type) + { + case GB_T_FLOAT: + conv->_float.value = mpz_get_d(a->n); + return FALSE; + + case GB_T_SINGLE: + conv->_single.value = mpz_get_d(a->n); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + conv->_integer.value = (int)mpz_get_si(a->n); + return FALSE; + + case GB_T_LONG: + conv->_long.value = (int64_t)mpz_get_si(a->n); + return FALSE; + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = BIGINT_to_string(a->n, 10); //, type == GB_T_CSTRING); + conv->_string.value.start = 0; + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); + return FALSE; + + default: + return TRUE; + } + } + else + { + mpz_t n; + + switch(type) + { + case GB_T_FLOAT: + mpz_init_set_d(n, conv->_float.value); + conv->_object.value = BIGINT_create(n); + return FALSE; + + case GB_T_SINGLE: + mpz_init_set_d(n, conv->_single.value); + conv->_object.value = BIGINT_create(n); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + mpz_init_set_si(n, (long)conv->_integer.value); + conv->_object.value = BIGINT_create(n); + return FALSE; + + case GB_T_LONG: + mpz_init_set_si(n, (long)conv->_long.value); + conv->_object.value = BIGINT_create(n); + return FALSE; + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_object.value = BIGINT_from_string(GB.ToZeroString(&conv->_string), 10); + return conv->_object.value == NULL; + + default: + return TRUE; + } + } +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(BigInt_new) + + mpz_init_set_si(THIS->n, 0); + +END_METHOD + +BEGIN_METHOD_VOID(BigInt_free) + + mpz_clear(THIS->n); + +END_METHOD + +BEGIN_METHOD(BigInt_compare, GB_OBJECT other) + + CBIGINT *other = VARG(other); + + if (GB.CheckObject(other)) + return; + + GB.ReturnInteger(mpz_cmp(NUMBER, other->n)); + +END_METHOD + +BEGIN_METHOD(BigInt_ToString, GB_INTEGER base) + + char *str; + int base = VARGOPT(base, 10); + + if (base < 2 || base > 36) + { + GB.Error("Base must be between 2 and 36"); + return; + } + + str = BIGINT_to_string(NUMBER, base); + GB.FreeStringLater(str); + GB.ReturnString(str); + +END_METHOD + +BEGIN_METHOD(BigInt_Shl, GB_INTEGER bits) + + GB.ReturnObject(BIGINT_make_int(THIS, VARG(bits), mpz_mul_2exp)); + +END_METHOD + +BEGIN_METHOD(BigInt_PowM, GB_OBJECT exp; GB_OBJECT mod) + + mpz_t n; + CBIGINT *exp = VARG(exp); + CBIGINT *mod = VARG(mod); + + if (GB.CheckObject(exp) || GB.CheckObject(mod)) + return; + + mpz_init(n); + mpz_powm(n, NUMBER, exp->n, mod->n); + GB.ReturnObject(BIGINT_create(n)); + +END_METHOD + +BEGIN_METHOD(BigInt_InvM, GB_OBJECT mod) + + mpz_t n; + CBIGINT *mod = VARG(mod); + + if (GB.CheckObject(mod)) + return; + + mpz_init(n); + mpz_invert(n, NUMBER, mod->n); + GB.ReturnObject(BIGINT_create(n)); + +END_METHOD + +BEGIN_METHOD(BigInt_Fact, GB_INTEGER n) + + mpz_t fact; + int n = VARG(n); + + if (n < 0) + { + GB.Error(GB_ERR_ARG); + return; + } + + mpz_init(fact); + mpz_fac_ui(fact, n); + GB.ReturnObject(BIGINT_create(fact)); + +END_METHOD + +BEGIN_METHOD(BigInt_Fibonacci, GB_INTEGER n) + + mpz_t r; + int n = VARG(n); + + if (n < 0) + { + GB.Error(GB_ERR_ARG); + return; + } + + mpz_init(r); + mpz_fib_ui(r, n); + GB.ReturnObject(BIGINT_create(r)); + +END_METHOD + +BEGIN_METHOD(BigInt_GCD, GB_OBJECT a; GB_OBJECT b) + + mpz_t n; + CBIGINT *a = VARG(a); + CBIGINT *b = VARG(b); + + if (GB.CheckObject(a) || GB.CheckObject(b)) + return; + + mpz_init(n); + mpz_gcd(n, a->n, b->n); + GB.ReturnObject(BIGINT_create(n)); + +END_METHOD + +BEGIN_METHOD(BigInt_LCM, GB_OBJECT a; GB_OBJECT b) + + mpz_t n; + CBIGINT *a = VARG(a); + CBIGINT *b = VARG(b); + + if (GB.CheckObject(a) || GB.CheckObject(b)) + return; + + mpz_init(n); + mpz_lcm(n, a->n, b->n); + GB.ReturnObject(BIGINT_create(n)); + +END_METHOD + +BEGIN_METHOD(BigInt_FromString, GB_STRING str; GB_INTEGER base) + + CBIGINT *n; + int base = VARGOPT(base, 10); + + if (base < 2 || base > 36) + { + GB.Error("Base must be between 2 and 36"); + return; + } + + n = BIGINT_from_string(GB.ToZeroString(ARG(str)), base); + if (!n) + { + GB.Error(GB_ERR_TYPE); + return; + } + + GB.ReturnObject(n); + +END_METHOD + +BEGIN_PROPERTY(BigInt_Odd) + + GB.ReturnBoolean(mpz_odd_p(NUMBER)); + +END_PROPERTY + +BEGIN_PROPERTY(BigInt_Even) + + GB.ReturnBoolean(mpz_even_p(NUMBER)); + +END_PROPERTY + +BEGIN_METHOD(BigInt_And, GB_OBJECT a; GB_OBJECT b) + + CBIGINT *a = VARG(a); + CBIGINT *b = VARG(b); + + if (GB.CheckObject(a) || GB.CheckObject(b)) + return; + + GB.ReturnObject(BIGINT_make(a, b, mpz_and)); + +END_METHOD + +BEGIN_METHOD(BigInt_Or, GB_OBJECT a; GB_OBJECT b) + + CBIGINT *a = VARG(a); + CBIGINT *b = VARG(b); + + if (GB.CheckObject(a) || GB.CheckObject(b)) + return; + + GB.ReturnObject(BIGINT_make(a, b, mpz_ior)); + +END_METHOD + +BEGIN_METHOD(BigInt_Xor, GB_OBJECT a; GB_OBJECT b) + + CBIGINT *a = VARG(a); + CBIGINT *b = VARG(b); + + if (GB.CheckObject(a) || GB.CheckObject(b)) + return; + + GB.ReturnObject(BIGINT_make(a, b, mpz_xor)); + +END_METHOD + +BEGIN_METHOD(BigInt_Not, GB_OBJECT a) + + CBIGINT *a = VARG(a); + + if (GB.CheckObject(a)) + return; + + GB.ReturnObject(BIGINT_make_unary(a, mpz_com)); + +END_METHOD + +#define IMPLEMENT_BIT(_name, _func) \ +BEGIN_METHOD(BigInt_##_name, GB_INTEGER bit) \ + \ + _func(NUMBER, VARG(bit)); \ + RETURN_SELF(); \ + \ +END_METHOD + +IMPLEMENT_BIT(BSet, mpz_setbit) +IMPLEMENT_BIT(BClr, mpz_clrbit) +IMPLEMENT_BIT(BChg, mpz_combit) + +BEGIN_METHOD(BigInt_BTst, GB_INTEGER bit) + + GB.ReturnBoolean(mpz_tstbit(NUMBER, VARG(bit))); + +END_METHOD + +//--------------------------------------------------------------------------- + + +GB_DESC BigIntDesc[] = +{ + GB_DECLARE("BigInt", sizeof(CBIGINT)), + + GB_METHOD("_new", NULL, BigInt_new, NULL), + GB_METHOD("_free", NULL, BigInt_free, NULL), + GB_METHOD("_compare", "i", BigInt_compare, "(Other)BigInt;"), + + GB_STATIC_METHOD("Fact", "BigInt", BigInt_Fact, "(N)i"), + GB_STATIC_METHOD("Fibonacci", "BigInt", BigInt_Fibonacci, "(N)i"), + GB_STATIC_METHOD("GCD", "BigInt", BigInt_GCD, "(A)BigInt;(B)BigInt;"), + GB_STATIC_METHOD("LCM", "BigInt", BigInt_LCM, "(A)BigInt;(B)BigInt;"), + GB_STATIC_METHOD("FromString", "BigInt", BigInt_FromString, "(String)s[(Base)i]"), + + GB_STATIC_METHOD("And", "BigInt", BigInt_And, "(A)BigInt;(B)BigInt;"), + GB_STATIC_METHOD("Or", "BigInt", BigInt_Or, "(A)BigInt;(B)BigInt;"), + GB_STATIC_METHOD("Xor", "BigInt", BigInt_Xor, "(A)BigInt;(B)BigInt;"), + GB_STATIC_METHOD("Not", "BigInt", BigInt_Not, "(A)BigInt;"), + + GB_METHOD("BSet", "BigInt", BigInt_BSet, "(Bit)i"), + GB_METHOD("BClr", "BigInt", BigInt_BClr, "(Bit)i"), + GB_METHOD("BTst", "b", BigInt_BTst, "(Bit)i"), + GB_METHOD("BChg", "BigInt", BigInt_BChg, "(Bit)i"), + + GB_PROPERTY_READ("Odd", "b", BigInt_Odd), + GB_PROPERTY_READ("Even", "b", BigInt_Even), + + GB_METHOD("ToString", "s", BigInt_ToString, "[(Base)i]"), + GB_METHOD("Shl", "BigInt", BigInt_Shl, "(Bits)i"), + GB_METHOD("PowM", "BigInt", BigInt_PowM, "(Exp)BigInt;(Mod)BigInt;"), + GB_METHOD("InvM", "BigInt", BigInt_InvM, "(Mod)BigInt;"), + + GB_INTERFACE("_operator", &_operator), + GB_INTERFACE("_convert", &_convert), + + GB_END_DECLARE +}; diff --git a/gb.gmp/src/c_bigint.h b/gb.gmp/src/c_bigint.h new file mode 100644 index 00000000..fea855ff --- /dev/null +++ b/gb.gmp/src/c_bigint.h @@ -0,0 +1,44 @@ +/*************************************************************************** + + c_bigint.h + + gb.gmp component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_BIGINT_H +#define __C_BIGINT_H + +#include + +#ifndef __C_BIGINT_C +extern GB_DESC BigIntDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + mpz_t n; + } + CBIGINT; + +CBIGINT *BIGINT_create(mpz_t number); + +#endif /* __C_BIGINT_H */ diff --git a/gb.gmp/src/c_rational.c b/gb.gmp/src/c_rational.c new file mode 100644 index 00000000..4f146c60 --- /dev/null +++ b/gb.gmp/src/c_rational.c @@ -0,0 +1,730 @@ +/*************************************************************************** + + c_rational.c + + gb.gmp component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_RATIONAL_C + +#include "main.h" +#include "c_bigint.h" +#include "c_rational.h" + +#define THIS ((CRATIONAL *)_object) +#define NUMBER (THIS->n) + +//---- Utility functions & macros -------------------------------------------- + +static CRATIONAL _tmp; + +static void from_double(mpq_t n, double f, int level) +{ + double fa; + int nfa; + mpq_t ni, nn; + bool neg; + + //fprintf(stderr, "from_double: %.14g\n", f); + + if (level >= 10) + goto __DEFAULT; + + fa = fabs(f); + if (fa >= 1E8 || fa <= 1E-8) + goto __DEFAULT; + + neg = (f < 0); + + nfa = (int)fa; + if (nfa >= 1) + fa -= nfa; + + //fprintf(stderr, "fa = %.14g %.14g\n", fa, (fa*1E8) - (int)(fa*1E8)); + + if (nfa && fa < 1E-8) + { + mpq_set_si(n, 0, 1); + } + else if (((fa*1E8) - (int)(fa*1E8)) < 1E-8) + { + mpq_set_si(n, (int)(fa*1E8), 100000000); + } + else + { + mpq_init(ni); + from_double(ni, 1 / fa, level + 1); + mpq_inv(n, ni); + mpq_clear(ni); + } + + mpq_init(nn); + mpq_set_si(nn, nfa, 1); + mpq_add(n, n, nn); + mpq_clear(nn); + + if (neg) + mpq_neg(n, n); + + mpq_canonicalize(n); + + return; + +__DEFAULT: + + mpq_set_d(n, f); +} + +static void my_mpq_set_d(mpq_t n, double f) +{ + from_double(n, f, 0); +} + +#define mpq_init_set_d(_n, _f) (mpq_init(_n), my_mpq_set_d(_n, _f)) +#define mpq_init_set_si(_n, _i) (mpq_init(_n), mpq_set_si(_n, _i, 1)) + +//---- Rational number creation ---------------------------------------------- + +CRATIONAL *RATIONAL_create(mpq_t number) +{ + CRATIONAL *c; + + c = (CRATIONAL *)GB.New(CLASS_Rational, NULL, NULL); + mpq_set(c->n, number); + //mpq_canonicalize(c->n); + mpq_clear(number); + + return c; +} + +#define RATIONAL_make(__a, _b, _op) \ +({ \ + CRATIONAL *_a = __a; \ + if ((_a)->ob.ref <= 1) \ + _op((_a)->n, (_a)->n, (_b)->n); \ + else \ + { \ + mpq_t n; \ + mpq_init(n); \ + _op(n, (_a)->n, (_b)->n); \ + _a = RATIONAL_create(n); \ + } \ + _a; \ +}) + +#define RATIONAL_make_unary(_a, _op) \ +({ \ + if ((_a)->ob.ref <= 1) \ + _op((_a)->n, (_a)->n); \ + else \ + { \ + mpq_t n; \ + mpq_init(n); \ + _op(n, (_a)->n); \ + _a = RATIONAL_create(n); \ + } \ + _a; \ +}) + +#define RATIONAL_make_int(__a, _f, _op) \ +({ \ + CRATIONAL *_a = (__a); \ + if (_a->ob.ref <= 1) \ + _op(_a->n, _a->n, (ulong)(_f)); \ + else \ + { \ + mpq_t n; \ + mpq_init(n); \ + _op(n, _a->n, (ulong)(_f)); \ + _a = RATIONAL_create(n); \ + } \ + _a; \ +}) + +#define RATIONAL_make_int_invert(_a, _f, _op) \ +({ \ + if ((_a)->ob.ref <= 1) \ + _op((_a)->n, (ulong)(_f), (_a)->n); \ + else \ + { \ + mpq_t n; \ + mpq_init(n); \ + _op(n, (ulong)(_f), (_a)->n); \ + _a = RATIONAL_create(n); \ + } \ + _a; \ +}) + +#define RATIONAL_make_bit(__a, _n, _op) \ +({ \ + CRATIONAL *_a = (__a); \ + if (_a->ob.ref <= 1) \ + _op(_a->n, _a->n, (_n)); \ + else \ + { \ + mpq_t n; \ + mpq_init(n); \ + _op(n, _a->n, (_n)); \ + _a = RATIONAL_create(n); \ + } \ + _a; \ +}) + +//---- Arithmetic operators ------------------------------------------------- + +static CRATIONAL *_add(CRATIONAL *a, CRATIONAL *b, bool invert) +{ + return RATIONAL_make(a, b, mpq_add); +} + +static CRATIONAL *_addf(CRATIONAL *a, double f, bool invert) +{ + my_mpq_set_d(_tmp.n, f); + return RATIONAL_make(a, &_tmp, mpq_add); +} + +static CRATIONAL *_addo(CRATIONAL *a, void *o, bool invert) +{ + if (GB.Is(o, CLASS_BigInt)) + { + mpq_set_z(_tmp.n, ((CBIGINT *)o)->n); + return RATIONAL_make(a, &_tmp, mpq_add); + } + else + return NULL; +} + +static CRATIONAL *_sub(CRATIONAL *a, CRATIONAL *b, bool invert) +{ + return RATIONAL_make(a, b, mpq_sub); +} + +static CRATIONAL *_subf(CRATIONAL *a, double f, bool invert) +{ + my_mpq_set_d(_tmp.n, f); + + if (invert) + return RATIONAL_make(&_tmp, a, mpq_sub); + else + return RATIONAL_make(a, &_tmp, mpq_sub); +} + +static CRATIONAL *_subo(CRATIONAL *a, void *o, bool invert) +{ + if (GB.Is(o, CLASS_BigInt)) + { + mpq_set_z(_tmp.n, ((CBIGINT *)o)->n); + if (invert) + return RATIONAL_make(&_tmp, a, mpq_sub); + else + return RATIONAL_make(a, &_tmp, mpq_sub); + } + else + return NULL; +} + +static CRATIONAL *_mul(CRATIONAL *a, CRATIONAL *b, bool invert) +{ + return RATIONAL_make(a, b, mpq_mul); +} + +static CRATIONAL *_mulf(CRATIONAL *a, double f, bool invert) +{ + my_mpq_set_d(_tmp.n, f); + return RATIONAL_make(a, &_tmp, mpq_mul); +} + +static CRATIONAL *_mulo(CRATIONAL *a, void *o, bool invert) +{ + if (GB.Is(o, CLASS_BigInt)) + { + mpq_set_z(_tmp.n, ((CBIGINT *)o)->n); + return RATIONAL_make(a, &_tmp, mpq_mul); + } + else + return NULL; +} + +static CRATIONAL *_div(CRATIONAL *a, CRATIONAL *b, bool invert) +{ + if (mpq_cmp_si(b->n, 0, 1) == 0) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + else + return RATIONAL_make(a, b, mpq_div); +} + +static CRATIONAL *_divf(CRATIONAL *a, double f, bool invert) +{ + my_mpq_set_d(_tmp.n, f); + if (invert) + return _div(&_tmp, a, FALSE); + else + return _div(a, &_tmp, FALSE); +} + +static CRATIONAL *_divo(CRATIONAL *a, void *o, bool invert) +{ + if (GB.Is(o, CLASS_BigInt)) + { + mpq_set_z(_tmp.n, ((CBIGINT *)o)->n); + if (invert) + return _div(&_tmp, a, FALSE); + else + return _div(a, &_tmp, FALSE); + } + else + return NULL; +} + +static int _equal(CRATIONAL *a, CRATIONAL *b, bool invert) +{ + return mpq_equal(a->n, b->n); +} + +static int _equalf(CRATIONAL *a, double f, bool invert) +{ + my_mpq_set_d(_tmp.n, f); + return mpq_equal(a->n, _tmp.n); +} + +static int _equalo(CRATIONAL *a, void *o, bool invert) +{ + if (GB.Is(o, CLASS_BigInt)) + { + mpq_set_z(_tmp.n, ((CBIGINT *)o)->n); + return mpq_equal(a->n, _tmp.n); + } + else + return -1; +} + +static int _comp(CRATIONAL *a, CRATIONAL *b, bool invert) +{ + return mpq_cmp(a->n, b->n); +} + +static int _compf(CRATIONAL *a, double f, bool invert) +{ + my_mpq_set_d(_tmp.n, f); + return mpq_cmp(a->n, _tmp.n); +} + +static int _compo(CRATIONAL *a, void *o, bool invert) +{ + if (GB.Is(o, CLASS_BigInt)) + { + mpq_set_z(_tmp.n, ((CBIGINT *)o)->n); + return mpq_cmp(a->n, _tmp.n); + } + else + return -2; +} + +static CRATIONAL *_neg(CRATIONAL *a) +{ + return RATIONAL_make_unary(a, mpq_neg); +} + +static CRATIONAL *_abs(CRATIONAL *a) +{ + return RATIONAL_make_unary(a, mpq_abs); +} + +static int _sgn(CRATIONAL *a) +{ + return mpq_sgn(a->n); +} + +static CRATIONAL *_powf(CRATIONAL *a, double f, bool invert) +{ + ulong p; + mpz_t num, den; + mpq_t n; + + if (invert || (double)(int)f != f) + return NULL; + + if (f < 0) + { + f = (-f); + invert = TRUE; + } + + p = (ulong)f; + + mpz_init(num); + mpz_pow_ui(num, mpq_numref(a->n), p); + + mpz_init(den); + mpz_pow_ui(den, mpq_denref(a->n), p); + + mpq_init(n); + if (invert) + mpz_swap(num, den); + + if (mpz_cmp_si(den, 0) == 0) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + + mpq_set_num(n, num); + mpq_set_den(n, den); + + mpz_clear(num); + mpz_clear(den); + + mpq_canonicalize(n); + + return RATIONAL_create(n); +} + +static CRATIONAL *_powo(CRATIONAL *a, void *o, bool invert) +{ + CBIGINT *b; + + if (invert || !GB.Is(o, CLASS_BigInt)) + return NULL; + + b = (CBIGINT *)o; + + if (!mpz_fits_slong_p(b->n)) + { + GB.Error(GB_ERR_OVERFLOW); + return NULL; + } + + return _powf(a, (double)mpz_get_si(b->n), invert); +} + +static GB_OPERATOR_DESC _operator = +{ + .equal = (void *)_equal, + .equalf = (void *)_equalf, + .equalo = (void *)_equalo, + .comp = (void *)_comp, + .compf = (void *)_compf, + .compo = (void *)_compo, + .add = (void *)_add, + .addf = (void *)_addf, + .addo = (void *)_addo, + .sub = (void *)_sub, + .subf = (void *)_subf, + .subo = (void *)_subo, + .mul = (void *)_mul, + .mulf = (void *)_mulf, + .mulo = (void *)_mulo, + .div = (void *)_div, + .divf = (void *)_divf, + .divo = (void *)_divo, + .powo = (void *)_powo, + .powf = (void *)_powf, + .abs = (void *)_abs, + .neg = (void *)_neg, + .sgn = (void *)_sgn +}; + +//---- Conversions ---------------------------------------------------------- + +char *RATIONAL_to_string(mpq_t n, int base) +{ + char *str; + int len; + + len = mpz_sizeinbase (mpq_numref(n), base) + mpz_sizeinbase(mpq_denref(n), base) + 2; + if (mpq_sgn(n) < 0) + len++; + + str = GB.NewString(NULL, len); + memset(str, 0, len); + mpq_get_str(str, -base, n); + + while (len > 0 && str[len - 1] == 0) + { + str = GB.ExtendString(str, len - 1); + len--; + } + + return str; +} + +CRATIONAL *RATIONAL_from_string(char *str, int base) +{ + mpq_t n; + + mpq_init(n); + if (mpq_set_str(n, str, base)) + { + mpq_clear(n); + return NULL; + } + else + { + mpq_canonicalize(n); + return RATIONAL_create(n); + } +} + +static bool _convert(CRATIONAL *a, GB_TYPE type, GB_VALUE *conv) +{ + if (a) + { + switch (type) + { + case GB_T_FLOAT: + conv->_float.value = mpq_get_d(a->n); + return FALSE; + + case GB_T_SINGLE: + conv->_single.value = mpq_get_d(a->n); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + conv->_integer.value = (int)mpq_get_d(a->n); + return FALSE; + + case GB_T_LONG: + conv->_long.value = (int64_t)mpq_get_d(a->n); + return FALSE; + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = RATIONAL_to_string(a->n, 10); //, type == GB_T_CSTRING); + conv->_string.value.start = 0; + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); + return FALSE; + + default: + + if (type == CLASS_BigInt) + { + mpz_t n; + + mpz_init(n); + mpz_tdiv_q(n, mpq_numref(a->n), mpq_denref(a->n)); + + conv->_object.value = BIGINT_create(n); + + return FALSE; + } + + return TRUE; + } + } + else + { + mpq_t n; + + switch(type) + { + case GB_T_FLOAT: + mpq_init_set_d(n, conv->_float.value); + conv->_object.value = RATIONAL_create(n); + return FALSE; + + case GB_T_SINGLE: + mpq_init_set_d(n, conv->_single.value); + conv->_object.value = RATIONAL_create(n); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + mpq_init_set_si(n, (long)conv->_integer.value); + conv->_object.value = RATIONAL_create(n); + return FALSE; + + case GB_T_LONG: + mpq_init_set_si(n, (long)conv->_long.value); + conv->_object.value = RATIONAL_create(n); + return FALSE; + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_object.value = RATIONAL_from_string(GB.ToZeroString(&conv->_string), 10); + return conv->_object.value == NULL; + + default: + + if (type == CLASS_BigInt) + { + mpq_init(n); + mpq_set_z(n, ((CBIGINT *)conv->_object.value)->n); + conv->_object.value = RATIONAL_create(n); + return FALSE; + } + + return TRUE; + } + } +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Rational_init) + + _tmp.ob.ref = 2; + mpq_init(_tmp.n); + +END_METHOD + +BEGIN_METHOD_VOID(Rational_exit) + + mpq_clear(_tmp.n); + +END_METHOD + +BEGIN_METHOD_VOID(Rational_new) + + mpq_init(THIS->n); + +END_METHOD + +BEGIN_METHOD_VOID(Rational_free) + + mpq_clear(THIS->n); + +END_METHOD + +BEGIN_METHOD(Rational_compare, GB_OBJECT other) + + CRATIONAL *other = VARG(other); + + if (GB.CheckObject(other)) + return; + + GB.ReturnInteger(mpq_cmp(NUMBER, other->n)); + +END_METHOD + +BEGIN_METHOD(Rational_ToString, GB_INTEGER base) + + char *str; + int base = VARGOPT(base, 10); + + if (base < 2 || base > 36) + { + GB.Error("Base must be between 2 and 36"); + return; + } + + str = RATIONAL_to_string(NUMBER, base); + GB.FreeStringLater(str); + GB.ReturnString(str); + +END_METHOD + +BEGIN_METHOD(Rational_FromString, GB_STRING str; GB_INTEGER base) + + CRATIONAL *n; + int base = VARGOPT(base, 10); + + if (base < 2 || base > 36) + { + GB.Error("Base must be between 2 and 36"); + return; + } + + n = RATIONAL_from_string(GB.ToZeroString(ARG(str)), base); + if (!n) + { + GB.Error(GB_ERR_TYPE); + return; + } + + GB.ReturnObject(n); + +END_METHOD + +BEGIN_PROPERTY(Rational_Num) + + if (READ_PROPERTY) + { + mpz_t n; + mpz_init(n); + mpq_get_num(n, NUMBER); + GB.ReturnObject(BIGINT_create(n)); + } + else + { + CBIGINT *num = VPROP(GB_OBJECT); + + if (GB.CheckObject(num)) + return; + + mpq_set_num(NUMBER, num->n); + mpq_canonicalize(NUMBER); + } + +END_PROPERTY + +BEGIN_PROPERTY(Rational_Den) + + if (READ_PROPERTY) + { + mpz_t n; + mpz_init(n); + mpq_get_den(n, NUMBER); + GB.ReturnObject(BIGINT_create(n)); + } + else + { + CBIGINT *den = VPROP(GB_OBJECT); + + if (GB.CheckObject(den)) + return; + + mpq_set_den(NUMBER, den->n); + mpq_canonicalize(NUMBER); + } + +END_PROPERTY + +//--------------------------------------------------------------------------- + + +GB_DESC RationalDesc[] = +{ + GB_DECLARE("Rational", sizeof(CRATIONAL)), + + GB_STATIC_METHOD("_init", NULL, Rational_init, NULL), + GB_STATIC_METHOD("_exit", NULL, Rational_exit, NULL), + GB_METHOD("_new", NULL, Rational_new, NULL), + GB_METHOD("_free", NULL, Rational_free, NULL), + GB_METHOD("_compare", "i", Rational_compare, "(Other)Rational;"), + + GB_PROPERTY("Num", "BigInt", Rational_Num), + GB_PROPERTY("Den", "BigInt", Rational_Den), + + GB_STATIC_METHOD("FromString", "Rational", Rational_FromString, "(String)s[(Base)i]"), + + GB_METHOD("ToString", "s", Rational_ToString, "[(Base)i]"), + + GB_INTERFACE("_operator", &_operator), + GB_INTERFACE("_convert", &_convert), + + GB_END_DECLARE +}; diff --git a/gb.gmp/src/c_rational.h b/gb.gmp/src/c_rational.h new file mode 100644 index 00000000..3daac581 --- /dev/null +++ b/gb.gmp/src/c_rational.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + c_rational.h + + gb.gmp component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_RATIONAL_H +#define __C_RATIONAL_H + +#include + +#ifndef __C_RATIONAL_C +extern GB_DESC RationalDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + mpq_t n; + } + CRATIONAL; + +#endif /* __C_RATIONAL_H */ diff --git a/gb.gmp/src/gb.gmp.component b/gb.gmp/src/gb.gmp.component new file mode 100644 index 00000000..2ed93b42 --- /dev/null +++ b/gb.gmp/src/gb.gmp.component @@ -0,0 +1,3 @@ +[Component] +Author=Benoît Minisini +State=Stable diff --git a/gb.gmp/src/main.c b/gb.gmp/src/main.c new file mode 100644 index 00000000..41a7b073 --- /dev/null +++ b/gb.gmp/src/main.c @@ -0,0 +1,84 @@ +/*************************************************************************** + + main.c + + gb.gmp component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +#include "c_bigint.h" +#include "c_rational.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + BigIntDesc, + RationalDesc, + NULL // Must have a null entry for the end of the structure +}; + +GB_CLASS CLASS_BigInt; +GB_CLASS CLASS_Rational; + +/*static void error_handler(const char *reason, const char *file, int line, int gsl_errno) +{ + //fprintf(stderr, "gb.gsl: error: %s: %s\n", gsl_strerror(gsl_errno), reason); + GB.Error("&1: &2", gsl_strerror(gsl_errno), reason); +}*/ + +static void *my_malloc(size_t n) +{ + void *p; + GB.Alloc(&p, n); + return p; +} + +static void my_free(void *p, size_t n) +{ + GB.Free(&p); +} + +static void *my_realloc(void *p, size_t old, size_t n) +{ + GB.Realloc(&p, n); + return p; +} + +int EXPORT GB_INIT(void) +{ + CLASS_BigInt = GB.FindClass("BigInt"); + CLASS_Rational = GB.FindClass("Rational"); + + mp_set_memory_functions(my_malloc, my_realloc, my_free); + + //gsl_set_error_handler(error_handler); + + return 0; +} + +void EXPORT GB_EXIT() +{ + +} diff --git a/gb.gmp/src/main.h b/gb.gmp/src/main.h new file mode 100644 index 00000000..306e1547 --- /dev/null +++ b/gb.gmp/src/main.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + main.h + + gb.gmp component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern GB_CLASS CLASS_BigInt; +extern GB_CLASS CLASS_Rational; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.gsl/AUTHORS b/gb.gsl/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.gsl/COPYING b/gb.gsl/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.gsl/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.gsl/ChangeLog b/gb.gsl/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.gsl/INSTALL b/gb.gsl/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.gsl/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.gsl/Makefile.am b/gb.gsl/Makefile.am new file mode 100644 index 00000000..67e464d3 --- /dev/null +++ b/gb.gsl/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @GSL_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.gsl/NEWS b/gb.gsl/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.gsl/README b/gb.gsl/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.gsl/Test/test/.directory b/gb.gsl/Test/test/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/gb.gsl/Test/test/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/gb.gsl/Test/test/.icon.png b/gb.gsl/Test/test/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7c7a1e742729196ac371b2e78c72463876bbacd1 GIT binary patch literal 3682 zcmV-o4xRCdP)eNXR6cb4w#8w4;wVnU1^Hc=viD5K6e8FfYm!ODNLrPKS8^qzj{e)~OV{^-54n6;baTXm}5eRp~H`?}7DZ0+{^Jdsv~c z1{M@0@WlOvOe;v>t;6tMHAJJZvj(o8mCokQVzSq6K`F&|?kV7V_e|znKi$vfmSgAj zTc0WbMgOo9YaFbF>|_r=ytjZgOVZg^2d}&ffza@=p@`vRSmI63WyOLto_c61$2%h| z|KSm8YI{L?t}Xymzr6!%Au-O)Kd#Ql!G*9A!4L z@x&bms*bdC>~Irv^J97T@p)M7<-4!Ifo3oU;$z$(6j%pPXd}T92n#BC?!^Hx7NU;5 za00-Be4F%1gV^%pjh(BW83SR~qkFN&k{<8kvAeRleQ`3Mbijs#;E(nj8dOWuDfU!0 z5i1>j`n4(CSdzlC+u@Ch(Z{*H9)KW`WH zi9PrO*H#_q=#_BbJq?6MelJ!$z4zI~SbZ-D*ANvYQv}ym6#$4hwtINXMtuJ0Ch=cy zHTTRdh*Kl0ys^Yh^vzxgNkQL;&+KdHbM(ixf!^}^@TmTPONna|K*X`Wk=w>NR!1D+ z_i8oDun-q^;j2Y*%miR4V*FZ38BkKjo$J7BMQZG}lmV@j@++mr?3n0Vy+Rrj*-9C| zmi-YhYKIh;F9R4SN`3W5^wb|>!K!a#oG4OF&^(MBO+ZQ+&@QQhdQ1d}%LKp}hh1ea zVyBldt>h+J>nrJKIn2D9zdAAg?PvflX@Xj58BlTzfXmPaFvel;u8oM2*+i=L(<4*p z%}6B1t58B+^7Su9c1-K8v=h);D8D+b(qDQvxVg51Q>pQYXc*gAM?-;OdhvDGkIRut z5cd1fF>y#ao-juH)I#~SQZhI;05kjN;yF-l?;zcVtt|@nH2Tw^6&|1lWQls~n z3v2@Xz7AggoG!;wF*_3s{G%ff}s?CBw1NEXa4X2F`Zly^l@*1?2>{bYq8 z)X_{~VkX|$_;Cd=)~6H%q*f{*rC?MMXD`-?(%soiOIs~92evUQwVA0aR*(q9WoFXc z+Q!rWy`D%+Jdvn_3G`yZ!J+!+goD_|8nRRK(Ass*-@5E&j8CmpKr3l|N(uqP$^UF^ zKuc9QK34?4332#%6L-zXz+~j{QbjZC4jkv#&p$`SAKyk4%;bgL4b=7okz%;%n=}zv_7r0Y?A^ZM)ZE{8QOpymG4rJ;G@T#Mqf-iX+ko0Y8HDt zgBT0XzOad}-0^v0mOV(t(MDSKHWD(RW8-o9P9c;g)Y?E!FiP>Fn@|%l;v5OUT7=TF zO}mtBS5l755WQXl>!<(sjrQ8g7n^bZZeVOuLpww@PiK6bw!R3NzyVc5U0l zeYZ|!PtZfBb?Ub!vqs-J^Ksru%6M zhskO`&dNK#Ga<%@0Wbu>Sfq^VR&70^RSCpkim(?{RDBvB@*>@ z;`H_qb;VG2w1Jt^U3lFt`uu%#wKVcSDbx7nfie~e*p!$~o7GIJuVU4Gk4~t004c># zqzhw3#3Nb-!1}GhmSF%^N1{$fG#sL<{U9?I7O`<_8I3)DV$yTaaS3!+yi3xoCHOkq z`BH(0oxyl&%ibY#dnD zr3eTqy!`;c5rR<2k2lheF-3GHPiEYQ)NDLl9C#2AG z_!C^-SP~}B!ILtHZ_U3Im*@NzDvXDe6hk2(ApisK1(YNpBwheBPz3~0PBhoh?v5eQ z>ms3e5m8?kzN4RDO_a>s0^)kRIpuQGbqeC#ipkzET0v5J4)dSYp$2sR{UqnUqT?9HuE0MoGb`#5giyJ2+lbLGIK=6YYB#fE;e%E%=83Xi0z( z1eAefWltOc zDMpS6gD@~hqz6WXO7=GtgP#+Ul9`iT$V)H&7QJUPDe37LA$aqhO*n~Z$i=HzymB?1 zY{goEwRUJi)vXnKnmTaJ`V4&?&3ymSf2Fc!7xgv!7`*;?7b)zJH!Vg)fL6k9h9W?S zk>lpc(`QktC1uU^Ow)cotg0pLrc#VMmYSM+gg2g^hGTg8dWoApAFLe!A#f>)!lAOR zm4mPTg(+EC_}lA<7{knx1>+t8N(wPJ`y~h3fc^-O1cxbs8dLI%N+_MP2zSIwZc;Kb z#*2xBNouR-7O&!t+wNgeVgjK9JF&(f2Il>RauMEN#=PuYmbk%lq6HCA96q?4aG&qA z-?`F7is9(iazqx8N(KhUf{sL-X`U`QEt6%-OUXM_!5~rW-Z}Vu#XfeSvq4DIfXN+uiB5&YFy_7Pm2H{hO2-}5*S>b zCk^UcgS9C$=8#nWA;FA1Y(gqkLUQPZ^?d#I`zV>SnB4qA$}2x&>xXX=TQ~<#S{A;9 z6b@NSbzL3BNB8pC()-5M_@IQeL&-=pDhuEjX6R{1agNu+ZnuZKSKh_*zu$ms)(u2` zC&_Soh(y8&AxKP4WBIb%xo+W7YHAKqzGnvw?X853Atx<^f?G=w6ZZO940)5%`e7h% zy?xX?aw;qy1qg(qsZqzA-%{(VOj^m1*Q2`t65RG@Yi7vx8o*f77lv5fnp#QaC7>CimP1?5CQst zBfy^r0RVV_Twpqo4Ws~GuCl+?okNEHwAT$B2aW+vKxo7m5H(=hLw^KF&a#^PDwG|7 z5FRK2Aj%ougI_C#&j-lmGo1gxW<1HM%%7IbYu}$rdrz3Be%HWXDmw8u?WW`Xr-}Bp zUNJX!JoGaTR{7BGc(gkfw>O!RLRh|hFSQoP)K^W-z0ds$X11)9nbY6S-xH2?8> zGn>jf=xT4JtNb~92VT2k#s@VyFQ2;BHnck#PkbgOa*~V`ix3dw)@L;jmoelgt=T|q z))b^x1ez;2@y-*N(5WkKeo#yA--uC3xZPd|Rx!6I%%t4OJn>JZ=REJStygP(`y1nC z_Dsr5kH@1!I8Km_&wrD(uWTnh{nGzjea-9t0Zzk&3RC?negFUf07*qoM6N<$g0~_7 A2mk;8 literal 0 HcmV?d00001 diff --git a/gb.gsl/Test/test/.project b/gb.gsl/Test/test/.project new file mode 100644 index 00000000..fa45a305 --- /dev/null +++ b/gb.gsl/Test/test/.project @@ -0,0 +1,9 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.0.90 +Title=test +Startup=MMain +Version=3.6.90 +Component=gb.gsl +Component=gb.option +Component=gb.xml +TabSize=2 diff --git a/gb.gsl/Test/test/.src/MMain.module b/gb.gsl/Test/test/.src/MMain.module new file mode 100644 index 00000000..0a0df525 --- /dev/null +++ b/gb.gsl/Test/test/.src/MMain.module @@ -0,0 +1,247 @@ +' Gambas module file + +Private ts As New TestSuite + +'------------------------------------------------------------------ +'Creates a new test object and adds it to the test suite +'------------------------------------------------------------------ +Public Function AddTest(name As String, result As Variant, expected As Variant, Optional note As String) As Test + + Dim t As New Test + + ts.AddTest(t) + t.Run(name, result, expected, note) + +End + + +'----------------------------------------------------------------- +'Test All GSL Class Methods +'----------------------------------------------------------------- +Public Sub testGslMethods() + Dim result As Variant + + AddTest("GSL.Acosh(45)", GSL.Acosh(45), ACosh(45)) + AddTest("GSL.Asinh(45)", GSL.Asinh(45), ASinh(45)) + AddTest("GSL.Atanh(0.45)", GSL.Atanh(0.45), ATanh(0.45)) + AddTest("GSL.Expm1(45)", GSL.Expm1(45), Expm(45)) + AddTest("GSL.Fcmpb(-0.3, 0.2, 0.1)", GSL.Fcmpb(-0.3, 0.2, 0.1), GSL.Fcmpb(-0.3, 0.2, 0.1)) + AddTest("GSL.Fcmpi(-0.3, 0.2, 0.1)", GSL.Fcmpi(1.3, 2.2, -1.1), 1) + result = GSL.Frexp(0.45) + AddTest("GSL.Frexp(0.45)", result, result) + AddTest("GSL.Hypot(10.27, 7.45)", GSL.Hypot(10.27, 7.45), GSL.Hypot(10.27, 7.45)) + AddTest("GSL.Hypot3(10.27, 7.45, 6.78125)", GSL.Hypot3(10.27, 7.45, 6.78125), GSL.Hypot3(10.27, 7.45, 6.78125)) + AddTest("GSL.IntPow(2.0, 8)", GSL.IntPow(2.0, 8), 256.00) + AddTest("GSL.IntPow2(8)", GSL.IntPow2(8), GSL.IntPow2(8)) + AddTest("GSL.IntPow3(8)", GSL.IntPow3(8), GSL.IntPow3(8)) + AddTest("GSL.IntPow4(8)", GSL.IntPow4(8), GSL.IntPow4(8)) + AddTest("GSL.IntPow5(8)", GSL.IntPow5(8), GSL.IntPow5(8)) + AddTest("GSL.IntPow6(8)", GSL.IntPow6(8), GSL.IntPow6(8)) + AddTest("GSL.IntPow7(8)", GSL.IntPow7(8), GSL.IntPow7(8)) + AddTest("GSL.IntPow8(8)", GSL.IntPow8(8), GSL.IntPow8(8)) + AddTest("GSL.IntPow9(8)", GSL.IntPow9(8), GSL.IntPow9(8)) + AddTest("GSL.IsEven(2)", GSL.IsEven(2), True) + AddTest("GSL.IsFinite(25)", GSL.IsFinite(25), True) + AddTest("GSL.IsInf(0)", GSL.IsInf(0), False) + AddTest("GSL.IsNan(0)", GSL.IsNan(0), False) + AddTest("GSL.IsOdd(3)", GSL.IsOdd(3), True) + AddTest("GSL.IsPos(0)", GSL.IsPos(0), True) + AddTest("GSL.Ldexp(17.63, 3)", GSL.Ldexp(17.63, 3), 141.04) + AddTest("GSL.Log1p(12.36)", GSL.Log1p(12.36), GSL.Log1p(12.36)) + AddTest("GSL.MaxFloat(2.0123, 2.01234)", GSL.MaxFloat(2.0123, 2.01234), 2.01234) + AddTest("GSL.MaxInt(1,3)", GSL.MaxInt(1, 3), 3) + AddTest("GSL.MinFLoat(0.001, 0.0011)", GSL.MinFLoat(0.001, 0.0011), 0.001) + AddTest("GSL.MinInt(1,3)", GSL.MinInt(1, 3), 1) + + End + + +'---------------------------------------------------------------- +'Test GSL Class constance +'---------------------------------------------------------------- +Public Sub testGslConstance() + ' Constance + AddTest("GSL.M_1_PI", GSL.M_1_PI, 0.31830988618379067153776752675) + AddTest("GSL.M_2_PI", GSL.M_2_PI, 0.63661977236758134307553505349) + AddTest("GSL.M_2_SQRTPI", GSL.M_2_SQRTPI, 1.12837916709551257389615890312) + AddTest("GSL.M_E", GSL.M_E, 2.71828182845904523536028747135) + AddTest("GSL.M_EULER", GSL.M_EULER, 0.57721566490153286060651209008) + AddTest("GSL.M_LN2", GSL.M_LN2, 0.69314718055994530941723212146) + AddTest("GSL.M_LN10", GSL.M_LN10, 2.30258509299404568401799145468) + AddTest("GSL.M_LNPI", GSL.M_LNPI, 1.14472988584940017414342735135) + AddTest("GSL.M_LOG10E", GSL.M_LOG10E, 0.43429448190325182765112891892) + AddTest("GSL.M_LOGE", GSL.M_LOGE, 1.44269504088896340735992468100) + AddTest("GSL.M_PI", GSL.M_PI, 3.14159265358979323846264338328) + AddTest("GSL.M_PI_2", GSL.M_PI_2, 1.57079632679489661923132169164) + AddTest("GSL.M_PI_4", GSL.M_PI_4, 0.78539816339744830961566084582) + AddTest("GSL.M_SQRT1_2", GSL.M_SQRT1_2, 0.70710678118654752440084436210) + AddTest("GSL.M_SQRT2", GSL.M_SQRT2, 1.41421356237309504880168872421) + AddTest("GSL.M_SQRT3", GSL.M_SQRT3, 1.73205080756887729352744634151) + AddTest("GSL.M_SQRTPI", GSL.M_SQRTPI, 1.77245385090551602729816748334) + +End + + + +'------------------------------------------------------------------ +'Test Complex Class Methods +'------------------------------------------------------------------ +Public Sub AddComplexTest(name As String, result As Variant, expected As Variant, Optional note As String) + + Dim t As New TestComplex + + ts.AddTest(t) + t.Run(name, result, expected, note) + +End + + +'------------------------------------------------------------------- +'Test Complex Class Methods +'------------------------------------------------------------------- +Public Sub testComplexMethods() + Dim c As New Complex + Dim z1 As New Complex + Dim z2 As New Complex + Dim z3 As Complex + + z1.Set(1, 1) + z2.Set(2, 2) + + AddComplexTest("z1.Abs()", z1.Abs(), z1.Abs(), ("z1 = " & z1.ToString())) + AddComplexTest("z1.Abs2()", z1.Abs2(), z1.Abs2(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Add(z2)", z1.Add(z2), z1.Add(z2), "z1 = " & z1.ToString()) + AddComplexTest("z1.AddImag(1)", z1.AddImag(1), z1.AddImag(1), "z1 = " & z1.ToString()) + AddComplexTest("z1.AddReal(1)", z1.AddReal(1), z1.AddReal(1), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arccos()", z1.Arccos(), z1.Arccos(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arccosh()", z1.Arccosh(), z1.Arccosh(), "z1 = " & z1.ToString()) + AddComplexTest("z1.ArccoshReal(5.45)", z1.ArccoshReal(5.45), z1.ArccoshReal(5.45), "z1 = " & z1.ToString()) + AddComplexTest("z1.ArccosReal(3.25)", z1.ArccosReal(3.25), z1.ArccosReal(3.25), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arccot()", z1.Arccot(), z1.Arccot(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arccoth()", z1.Arccoth(), z1.Arccoth(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arccsc()", z1.Arccsc(), z1.Arccsc(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arccsch()", z1.Arccsch(), z1.Arccsch(), "z1 = " & z1.ToString()) + AddComplexTest("z1.ArccscReal(3.45)", z1.ArccscReal(3.45), z1.ArccscReal(3.45), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arcsec()", z1.Arcsec(), z1.Arcsec(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arcsech()", z1.Arcsech(), z1.Arcsech(), "z1 = " & z1.ToString()) + AddComplexTest("z1.ArcsecReal(3.45)", z1.ArcsecReal(3.45), z1.ArcsecReal(3.45), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arcsin()", z1.Arcsin(), z1.Arcsin(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arcsinh()", z1.Arcsinh(), z1.Arcsinh(), "z1 = " & z1.ToString()) + AddComplexTest("z1.ArcsinReal(3.45)", z1.ArcsinReal(3.45), z1.ArcsinReal(3.45), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arctan()", z1.Arctan(), z1.Arctan(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arctanh()", z1.Arctanh(), z1.Arctanh(), "z1 = " & z1.ToString()) + AddComplexTest("z1.ArctanhReal(3.45)", z1.ArctanhReal(3.45), z1.ArctanhReal(3.45), "z1 = " & z1.ToString()) + AddComplexTest("z1.Arg()", z1.Arg(), z1.Arg(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Copy()", z1.Copy(), z1.Copy(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Cos()", z1.Cos(), z1.Cos(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Cosh()", z1.Cosh(), z1.Cosh(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Cot()", z1.Cot(), z1.Cot(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Coth()", z1.Coth(), z1.Coth(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Csc()", z1.Csc(), z1.Csc(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Csch()", z1.Csch(), z1.Csch(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Csch()", z1.Csch(), z1.Csch(), "z1 = [" & z1.ToString() & "] z2 = [" & z2.ToString() & "]") + AddComplexTest("z1.DivImag(3.45)", z1.DivImag(3.45), z1.DivImag(3.45), "z1 = " & z1.ToString()) + AddComplexTest("z1.DivReal(3.45)", z1.DivReal(3.45), z1.DivReal(3.45), "z1 = " & z1.ToString()) + AddComplexTest("z1.Exp()", z1.Exp(), z1.Exp(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Log()", z1.Log(), z1.Log(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Log10()", z1.Log10(), z1.Log10(), "z1 = " & z1.ToString()) + AddComplexTest("z1.LogAbs()", z1.LogAbs(), z1.LogAbs(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Logb(z2)", z1.Logb(z2), z1.Logb(z2), "z1 = [" & z1.ToString() & "] z2 = [" & z2.ToString() & "]") + AddComplexTest("z1.Mul(z2)", z1.Mul(z2), z1.Mul(z2), "z1 = [" & z1.ToString() & "] z2 = [" & z2.ToString() & "]") + AddComplexTest("z1.MulImag(3.45)", z1.MulImag(3.45), z1.MulImag(3.45), "z1 = " & z1.ToString()) + AddComplexTest("z1.MulReal(3.45)", z1.MulReal(3.45), z1.MulReal(3.45), "z1 = " & z1.ToString()) + z1.Polar(5, 2) + AddComplexTest("z1.Polar(5,2)", z1.Copy(), z1.Copy(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Pow(z2)", z1.Pow(z2), z1.Pow(z2), "z1 = [" & z1.ToString() & "] z2 = [" & z2.ToString() & "]") + AddComplexTest("z1.PowReal(2) ", z1.PowReal(2), z1.PowReal(2), "z1 = [" & z1.ToString() & "] z2 = [" & z2.ToString() & "]") + z1.Rect(1, 1) + AddComplexTest("z1.Rect(1,1)", z1.Copy(), z1.Copy(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Sec()", z1.Sec(), z1.Sec(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Sech()", z1.Sech(), z1.Sech(), "z1 = " & z1.ToString()) + z1.Set(2, 2) + AddComplexTest("z1.Set(2,2)", z1.Copy(), z1.Copy(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Sin()", z1.Sin(), z1.Sin(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Sinh()", z1.Sinh(), z1.Sinh(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Sqrt()", z1.Sqrt(), z1.Sqrt(), "z1 = " & z1.ToString()) + AddComplexTest("z1.SqrtReal(3.45)", z1.SqrtReal(3.45), z1.SqrtReal(3.45), "z1 = " & z1.ToString()) + AddComplexTest("z1.Sub(z2)", z1.Sub(z2), z1.Sub(z2), "z1 = [" & z1.ToString() & "] z2 = [" & z2.ToString() & "]") + AddComplexTest("z1.SubImag(1.321)", z1.SubImag(1.321), z1.SubImag(1.321), "z1 = " & z1.ToString()) + AddComplexTest("z1.SubReal(2.125)", z1.SubReal(2.125), z1.SubReal(2.125), "z1 = " & z1.ToString()) + AddComplexTest("z1.Tan()", z1.Tan(), z1.Tan(), "z1 = " & z1.ToString()) + AddComplexTest("z1.Tanh()", z1.Tanh(), z1.Tanh(), "z1 = " & z1.ToString()) + +End + + +'------------------------------------------------------------------- +' Add Polynomial Tests +'------------------------------------------------------------------- +Public Sub AddPolynomialTest(name As String, result As Variant, expected As Variant, Optional note As String) + + Dim t As New TestPolynomial + + ts.AddTest(t) + t.Run(name, result, expected, note) + +End + + +'------------------------------------------------------------------- +' Polynomial Tests +'------------------------------------------------------------------- +Public Sub testPolynomialMethods() + Dim c As New Complex + Dim p1 As New Polynomial + Dim p2 As New Polynomial + Dim p3 As Polynomial + Dim f1 As Float + Dim f2 As Float + + p1.AddCoef(0.565) + p1.AddCoef(1.738) + p1.AddCoef(3.1465) + + AddPolynomialTest("p1.AddCoef(0.565)", p1.AddCoef(0.565), 1, p1.ToString()) + AddPolynomialTest("p1.AddCoef(1.738)", p1.AddCoef(1.738), 2, p1.ToString()) + AddPolynomialTest("p1.AddCoef(3.1465)", p1.AddCoef(3.1465), 3, p1.ToString()) + + +End Sub + + + + + + +'------------------------------------------------------------------- +'Run all tests +'------------------------------------------------------------------- +Public Sub Main() + + ' Setup test suite + ts.Name = "GSL Component" + ts.HeaderChar = "=" + ts.HeaderWidth = 40 + ts.TestHeaderChar = "-" + ts.TestHeaderWidth = 40 + ts.Note = "This test suite was developed for use\n" + "with GSL Component development. However,\n" + "it may be useful in other projects." + ts.ShowTestNotes = True + + 'Run our GSL Class Tests + testGslMethods() + testGslConstance + + 'Run Complex Class Test + testComplexMethods() + + 'Run Polynomial Class Test + testPolynomialMethods() + + + ' Finnish by showing test results + ts.ShowTests() +End + + diff --git a/gb.gsl/Test/test/.src/Test.class b/gb.gsl/Test/test/.src/Test.class new file mode 100644 index 00000000..65440363 --- /dev/null +++ b/gb.gsl/Test/test/.src/Test.class @@ -0,0 +1,199 @@ +' Gambas class file + +' ================================================================== +' @Class: TestSuite +' @Author: R Morgan +' @Date: 03/01/2012 +' @Ver: 0.01 +' @Desc: A framework for running unit and regression tests. +' ================================================================== +Public Name As String ' Name of test, usually the function or method name. +Public msgError As String ' Error Message if any +Public hasError As Boolean = False ' True is we find an error +Public Expected As Variant ' Expected Value +Public Result As Variant ' Result value +Public ExpType As String ' Expected Datatype +Public ResType As String ' Result Datatype +Public Note As String ' Note on test + +'------------------------------------------------------------------- +'@Sub: AddError +'@Desc: This method simply adds the passed error string to the +'error message array. +'@Ver:0.01 +'@First: 03/01/2012 +'@Returns: Void +'@Param msg - A string containing the error message +'------------------------------------------------------------------- +Public Sub AddError(msg As String) + + Me.msgError = msg + Me.hasError = True + +End + + +'------------------------------------------------------------------- +'@Func; TypeError +'@Desc: This method creates an error message for a type error +'@Ver:0.01 +'@First: 03/01/2012 +'@Returns: A string containing the type error message. +'@Param msg - A string containing the type as a string. +'------------------------------------------------------------------- +Public Function TypeError(gotType As String, expectedType As String) As String + + Dim msg As String + + msg = "Type error :<<< Expected type: " & expectedType & " Got type: " & gotType & " >>>" + + Return msg + +End + + +'------------------------------------------------------------------- +'@Desc: This method simply adds the passed error string to the +'error message array. +'@Ver:0.01 +'@First: 03/01/2012 +'@Returns: Void +'@Param: msg - A string containing the error message +'------------------------------------------------------------------- +Public Function ValueError(gotValue As Variant, expectedValue As Variant) As String + + Dim msg As String + + msg = "Value error: <<< Expected: " & Str(expectedValue) & " Got: " & Str(gotValue) & " >>>" + Return msg + +End + + +'------------------------------------------------------------------- +'@Desc: This method simply adds the passed error string to the +'error message array. +'@Ver:0.01 +'@First: 03/17/2012 +'@Returns: Void +'@Params: msg - A string contianing the error message. +'------------------------------------------------------------------- +Public Function LengthError(result As Integer, expected As Integer) As String + Dim msg As String + + msg = "Length error: <<< Expected a length of " & Str(expected) & " Got: " & Str(expected) & " >>>" + Return msg + +End + + +'------------------------------------------------------------------- +'@Desc: This method simply adds the passed error string to the +'error message array. +'@Ver:0.01 +'@First: 03/01/2012 +'@Returns: Void +'@Param msg - A string containing the error message +'------------------------------------------------------------------- +Public Function getTypeString(p As Variant) As String + + Select Case TypeOf(p) + Case gb.NULL + Return "NULL" + Case gb.Boolean + Return "Boolean" + Case gb.Byte + Return "Byte" + Case gb.Class + Return "Class" + Case gb.Date + Return "Date" + Case gb.Float + Return "Float" + Case gb.Integer + Return "Integer" + Case gb.Long + Return "Long" + Case gb.Object + Return "Object" + Case gb.Pointer + Return "Pointer" + Case gb.Short + Return "Short" + Case gb.Single + Return "Single" + Case gb.String + Return "String" + Case gb.Variant + Return "Variant" + Default + Return "Unknown" + End Select + +End + + +'------------------------------------------------------------------- +'Compare two value for equality +'------------------------------------------------------------------- +Public Function IsEqual(result As Variant, expected As Variant) As Boolean + Dim err As Boolean = False + Dim i As Integer = 0 + + If TypeOf(result) <> TypeOf(expected) Then + AddError(TypeError(Me.ResType, Me.ExpType)) + Me.hasError = True + Else + If IsObject(result) And result Is Array And expected Is Array Then + If result.len <> expected.len Then + Me.hasError(LengthError(result.len, expected.len)) + Else + For i = 0 To result.len - 1 + If result[i] <> expected[i] Then + Me.hasError = True + Endif + Next + Endif + + Else + If result <> expected Then + AddError(ValueError(Me.Result, Me.Expected)) + Me.hasError = True + Endif + Endif + Endif + + Return Me.hasError + +End + + + +'------------------------------------------------------------------- +'@Desc: This method tests the given values for equality in both +' type and value. +'@Ver:0.01 +'@First: 03/01/2012 +'@Returns: +'@Param: func - A string containing the function that was tested. +'@Param: result - A variant value containing the actual result of +' the test. +'@Param: expected - A variant value containing the expected result + 'value for the test. +'------------------------------------------------------------------- +Public Sub Run(func As String, result As Variant, expected As Variant, Optional note As String) As Boolean + + Dim err As Boolean = False + + Me.Name = func + Me.Note = note + + Me.Expected = expected + Me.ExpType = getTypeString(expected) + + Me.Result = result + Me.ResType = Me.getTypeString(result) + + Return IsEqual(result, expected) + +End diff --git a/gb.gsl/Test/test/.src/TestComplex.class b/gb.gsl/Test/test/.src/TestComplex.class new file mode 100644 index 00000000..64cb208f --- /dev/null +++ b/gb.gsl/Test/test/.src/TestComplex.class @@ -0,0 +1,94 @@ +' Gambas class file + +Inherits Test + +Public Function getComplexTypeString(p As Variant) As String + Dim result As String + + result = Me.getTypeString(p) + + If result = "Unknown" Then + If result Is Complex Then + result = "Complex" + Endif + Endif + + Return result + +End + + +Public Sub IsComplexEqual(result As Variant, expected As Variant) As Boolean + + If result Is Complex Or expected Is Complex Then + 'We have objects that need special processing + If Me.IsEqual(result, expected) Then + Me.Result = result.ToString() + Me.ResType = "Complext" + Me.Expected = expected.ToString() + Me.ExpType = "Complex" + Else + Me.AddError(Me.ValueError(result.ToString, expected.ToString)) + Me.hasError = True + Endif + Else + Me.hasError = Me.IsEqual(result, expected) + Endif + + Return Me.hasError + +End + + +Public Sub Run(func As String, result As Variant, expected As Variant, Optional note As String) As Boolean + Dim err As Boolean = False + + Me.Name = func + Me.Note = note + + Me.Expected = expected + Me.ExpType = Me.getTypeString(expected) + + + Me.Result = result + Me.ResType = Me.getTypeString(result) + + If TypeOf(result) <> gb.Object And TypeOf(expected) <> gb.Object Then + ' Not an object so run parent code + If TypeOf(result) <> TypeOf(expected) Then + Me.AddError(Me.TypeError(Me.ResType, Me.ExpType)) + err = True + Else + If result <> expected Then + Me.AddError(Me.ValueError(Me.Result, Me.Expected)) + err = True + Endif + Endif + Else + 'We have objects that need special processing + If TypeOf(result) <> TypeOf(expected) Then + Me.AddError(Me.TypeError(Me.ResType, Me.ExpType)) + err = True + Else + If result Is Complex And expected Is Complex Then + If Me.IsEqual(result, expected) Then + Me.Result = result.ToString() + Me.ResType = "Complex Object" + Me.Expected = expected.ToString() + Me.ExpType = "Complex Object" + Else + Me.AddError(Me.ValueError(result.ToString, expected.ToString)) + err = True + Endif + Endif + Endif + + Endif + + Return Me.hasError + + + +End + + diff --git a/gb.gsl/Test/test/.src/TestSuite.class b/gb.gsl/Test/test/.src/TestSuite.class new file mode 100644 index 00000000..99fcb0d1 --- /dev/null +++ b/gb.gsl/Test/test/.src/TestSuite.class @@ -0,0 +1,141 @@ +' Gambas class file + +' ================================================================== +' @Class: TestSuite +' @Author: R Morgan +' @Date: 03/01/2012 +' @Ver: 0.01 +' @Desc: A framework for running unit and regression tests. +' ================================================================== + +Public numErrors As Integer +Public msgErrors As New String[] +Private numTests As Integer +Public Tests As New Test[] +Public hasErrors As Boolean +Public HeaderChar As String = "=" +Public HeaderWidth As Integer = 40 +Public TestHeaderChar As String = "-" +Public TestHeaderWidth As Integer = 40 +Public Name As String + +Public Note As String +Public NoteHeaderChar As String = "*" +Public NoteHeaderWidth As Integer = 40 +Public ShowTestNotes As Boolean = False + + +Public Sub AddTest(t As Test) + + Tests.Add(t) + numTests += 1 + +End + + + +Public Procedure ErrorCount() As Integer + Dim i As Integer + Dim t As Test + + For i = 0 To Tests.Length - 1 + t = Tests[i] + If t.hasError Then + Me.numErrors += 1 + Me.hasErrors = True + Endif + Next + + Return Me.numErrors +End + + + + +Public Sub ShowHeader() + Dim Header As String + + Me.ErrorCount() + + Header = String$(Me.HeaderWidth, Me.HeaderChar) + Header &= "\n " & Name & " test\n" + Header &= " Date: " & Date() & "\n" + Header &= " Time: " & Time() & "\n" + + If Me.hasErrors Then + Header &= " Failure:" & Me.numErrors & " errors occurred.\n" + Else + Header &= " Success: All tests passed.\n" + Endif + + Header &= " There are " & numTests & " test in this run.\n" + Header &= String$(Me.HeaderWidth, Me.HeaderChar) + Header &= "\n\n" + + Print Header + +End + +Public Sub ShowNotes() + Dim Header As String + + If Len(Me.Note) > 0 Then + Header = String$(Me.NoteHeaderWidth, Me.NoteHeaderChar) + Header &= "\n" + Header &= Me.Note & "\n" + Header &= String$(Me.NoteHeaderWidth, Me.NoteHeaderChar) + Header &= "\n\n" + + Print Header + + Endif + +End + + + +Public Sub ShowTest(idx As Integer) + Dim t As New Test + Dim header As String + Dim cnt As Integer = idx + 1 + t = Tests[idx] + + header = String$(Me.TestHeaderWidth, Me.TestHeaderChar) + header &= "\n #" & cnt & " " & t.Name & "\n" + + If t.hasError Then + header &= " Status: <<<<< Failure >>>>> \n" + header &= " Error: " & t.msgError & "\n" + Else + header &= " Status: Passed \n" + Endif + + header &= " Expected result: " & Str(t.Expected) & "\n of type: " & t.ExpType & "\n" + header &= " Recieved result: " & Str(t.Result) & "\n of type: " & t.ResType & "\n" + + If ShowTestNotes And Len(t.Note) <> 0 Then + header &= " Notes: " & t.Note & "\n" + Endif + header &= String$(Me.TestHeaderWidth, Me.TestHeaderChar) + + Print header + +End + + +Public Sub ShowTests() + Dim t As Test + Dim i As Integer + + Me.ShowHeader() + Me.ShowNotes() + + i = 0 + For i = 0 To Tests.Length - 1 + ShowTest(i) + Next + +End + + + diff --git a/gb.gsl/acinclude.m4 b/gb.gsl/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.gsl/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.gsl/component.am b/gb.gsl/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.gsl/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.gsl/configure.ac b/gb.gsl/configure.ac new file mode 100644 index 00000000..3a6c7af9 --- /dev/null +++ b/gb.gsl/configure.ac @@ -0,0 +1,18 @@ +dnl ---- configure.ac for gb.gsl + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-gsl, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.gsl) +AC_PROG_LIBTOOL + +GB_COMPONENT_SEARCH( + gsl, GSL, gb.gsl, [src], + gsl, + [GB_FIND(gsl/gsl_cblas.h gsl/gsl_math.h, /usr /usr/local `gsl-config --prefix`, include include/gsl)], + [GB_FIND(libgslcblas.$SHLIBEXT libgsl.$SHLIBEXT, /usr/lib /usr/local /usr/local/lib `gsl-config --prefix`, lib)], + [$X_LIBS -llibgsl -llibgslcblas]) + +AC_OUTPUT( Makefile src/Makefile ) +GB_PRINT_MESSAGES diff --git a/gb.gsl/gambas.h b/gb.gsl/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.gsl/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.gsl/gb_common.h b/gb.gsl/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.gsl/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.gsl/m4 b/gb.gsl/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.gsl/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.gsl/reconf b/gb.gsl/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.gsl/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.gsl/src/Makefile.am b/gb.gsl/src/Makefile.am new file mode 100644 index 00000000..eb2dcb83 --- /dev/null +++ b/gb.gsl/src/Makefile.am @@ -0,0 +1,16 @@ +COMPONENT = gb.gsl +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gsl.la + +gb_gsl_la_LIBADD = @GSL_LIB@ +gb_gsl_la_LDFLAGS = -module @LD_FLAGS@ @GSL_LDFLAGS@ +gb_gsl_la_CPPFLAGS = @GSL_INC@ + +gb_gsl_la_SOURCES = \ + main.c main.h \ + c_gsl.c c_gsl.h \ + c_complex.c c_complex.h \ + c_vector.c c_vector.h \ + c_matrix.c c_matrix.h \ + c_polynomial.c c_polynomial.h diff --git a/gb.gsl/src/c_complex.c b/gb.gsl/src/c_complex.c new file mode 100644 index 00000000..dedb858b --- /dev/null +++ b/gb.gsl/src/c_complex.c @@ -0,0 +1,689 @@ +/*************************************************************************** + + c_complex.c + + gb.gsl component + + (c) 2012 Randall Morgan + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_COMPLEX_C + +#include "c_complex.h" +#include "c_gsl.h" +#include + +#define THIS ((CCOMPLEX *)_object) + +gsl_complex COMPLEX_zero = {{ 0.0, 0.0 }}; +gsl_complex COMPLEX_one = {{ 1.0, 0.0 }}; + +//---- Complex number creation ---------------------------------------------- + +CCOMPLEX *COMPLEX_create(gsl_complex number) +{ + CCOMPLEX *c; + + c = (CCOMPLEX *)GB.New(CLASS_Complex, NULL, NULL); + c->number = number; + + return c; +} + +#define COMPLEX_make(_a, _number) (((_a)->ob.ref <= 1) ? ((_a)->number = (_number), (_a)) : COMPLEX_create(_number)) + +CCOMPLEX *COMPLEX_push_complex(double value) +{ + return COMPLEX_create(gsl_complex_rect(0, value)); +} + +//---- Utility functions ---------------------------------------------------- + +int COMPLEX_get_value(GB_VALUE *value, COMPLEX_VALUE *v) +{ + GB.Conv(value, value->_variant.value.type); + + if (value->type >= GB_T_OBJECT && GB.Is(value->_object.value, CLASS_Complex)) + { + CCOMPLEX *c = (CCOMPLEX *)(value->_object.value); + if (GB.CheckObject(c)) + return CGV_ERR; + v->z = c->number; + if (GSL_IMAG(v->z) == 0.0) + return CGV_FLOAT; + else + return CGV_COMPLEX; + } + else + { + if (GB.Conv(value, GB_T_FLOAT)) + return CGV_ERR; + + v->z.dat[0] = value->_float.value; + v->z.dat[1] = 0.0; + return CGV_FLOAT; + } +} + +//---- Arithmetic operators ------------------------------------------------- + +static CCOMPLEX *_addf(CCOMPLEX *a, double f, bool invert) +{ + return COMPLEX_make(a, gsl_complex_add_real(a->number, f)); +} + +static CCOMPLEX *_add(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + return COMPLEX_make(a, gsl_complex_add(a->number, b->number)); +} + +static CCOMPLEX *_subf(CCOMPLEX *a, double f, bool invert) +{ + if (invert) + return COMPLEX_make(a, gsl_complex_add_real(gsl_complex_negative(a->number), f)); + else + return COMPLEX_make(a, gsl_complex_sub_real(a->number, f)); +} + +static CCOMPLEX *_sub(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + return COMPLEX_make(a, gsl_complex_sub(a->number, b->number)); +} + +static CCOMPLEX *_mulf(CCOMPLEX *a, double f, bool invert) +{ + return COMPLEX_make(a, gsl_complex_mul_real(a->number, f)); +} + +static CCOMPLEX *_mul(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + return COMPLEX_make(a, gsl_complex_mul(a->number, b->number)); +} + +static CCOMPLEX *_divf(CCOMPLEX *a, double f, bool invert) +{ + if (invert) + { + gsl_complex c = gsl_complex_inverse(a->number); + + if (isfinite(GSL_REAL(c)) && isfinite(GSL_IMAG(c))) + return COMPLEX_make(a, gsl_complex_mul_real(c, f)); + else + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + } + else + { + gsl_complex c = gsl_complex_div_real(a->number, f); + + if (isfinite(GSL_REAL(c)) && isfinite(GSL_IMAG(c))) + return COMPLEX_make(a, c); + else + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + } +} + +static CCOMPLEX *_div(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + gsl_complex c = gsl_complex_div(a->number, b->number); + + if (isfinite(GSL_REAL(c)) && isfinite(GSL_IMAG(c))) + return COMPLEX_make(a, c); + else + { + GB.Error(GB_ERR_ZERO); + return NULL; + } +} + +static int _equal(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + return GSL_REAL(a->number) == GSL_REAL(b->number) && GSL_IMAG(a->number) == GSL_IMAG(b->number); +} + +static int _equalf(CCOMPLEX *a, double f, bool invert) +{ + return GSL_REAL(a->number) == f && GSL_IMAG(a->number) == 0.0; +} + +static CCOMPLEX *_neg(CCOMPLEX *a) +{ + return COMPLEX_create(gsl_complex_negative(a->number)); +} + +static double _fabs(CCOMPLEX *a) +{ + return gsl_complex_abs(a->number); +} + +/*static CCOMPLEX *_powi(CCOMPLEX *a, int i) +{ + CCOMPLEX *r; + bool inv; + + inv = i < 0; + i = abs(i); + + if (i == 2) + r = _mul(a, a); + else if (i == 3) + { + r = COMPLEX_create(RE(a), IM(a)); + r = _mul(r, a); + r = _mul(r, a); + } + else if (i == 4) + { + a = _mul(a, a); + r = _mul(a, a); + } + else + r = COMPLEX_make(a, RE(a), IM(a)); + + if (inv) + return _idivf(r, 1); + else + return r; +}*/ + +static CCOMPLEX *_pow(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + return COMPLEX_make(a, gsl_complex_pow(a->number, b->number)); +} + +static CCOMPLEX *_powf(CCOMPLEX *a, double f, bool invert) +{ + return COMPLEX_make(a, gsl_complex_pow_real(a->number, f)); +} + + +static GB_OPERATOR_DESC _operator = +{ + .equal = (void *)_equal, + .equalf = (void *)_equalf, + .add = (void *)_add, + .addf = (void *)_addf, + .sub = (void *)_sub, + .subf = (void *)_subf, + .mul = (void *)_mul, + .mulf = (void *)_mulf, + .div = (void *)_div, + .divf = (void *)_divf, + .pow = (void *)_pow, + .powf = (void *)_powf, + .fabs = (void *)_fabs, + .neg = (void *)_neg +}; + +//---- Conversions ---------------------------------------------------------- + +char *COMPLEX_to_string(gsl_complex number, bool local) +{ + char buffer[64]; + char *p; + char *str; + int len; + double real, imag; + + real = number.dat[0]; + imag = number.dat[1]; + + if (real == 0.0 && imag == 0.0) + return GB.NewString("0", 1); + + p = buffer; + + if (real != 0.0) + { + GB.NumberToString(local, real, NULL, &str, &len); + strncpy(p, str, len); + p += len; + } + + if (imag != 0.0) + { + if (imag < 0.0) + { + *p++ = '-'; + imag = (-imag); + } + else if (p != buffer) + *p++ = '+'; + + if (imag != 1.0 || !local) + { + GB.NumberToString(local, imag, NULL, &str, &len); + strncpy(p, str, len); + p += len; + } + *p++ = 'i'; + } + + return GB.NewString(buffer, p - buffer); +} + +static bool _convert(CCOMPLEX *a, GB_TYPE type, GB_VALUE *conv) +{ + if (a) + { + switch (type) + { + case GB_T_FLOAT: + if (GSL_IMAG(a->number)) + return TRUE; + conv->_float.value = GSL_REAL(a->number); + return FALSE; + + case GB_T_SINGLE: + if (GSL_IMAG(a->number)) + return TRUE; + conv->_single.value = GSL_REAL(a->number); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + if (GSL_IMAG(a->number)) + return TRUE; + conv->_integer.value = GSL_REAL(a->number); + return FALSE; + + case GB_T_LONG: + if (GSL_IMAG(a->number)) + return TRUE; + conv->_long.value = GSL_REAL(a->number); + return FALSE; + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = COMPLEX_to_string(a->number, type == GB_T_CSTRING); + conv->_string.value.start = 0; + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); + return FALSE; + + default: + return TRUE; + } + } + else + { + switch(type) + { + case GB_T_FLOAT: + conv->_object.value = COMPLEX_create(gsl_complex_rect(conv->_float.value, 0)); + return FALSE; + + case GB_T_SINGLE: + conv->_object.value = COMPLEX_create(gsl_complex_rect(conv->_single.value, 0)); + return FALSE; + + case GB_T_LONG: + conv->_object.value = COMPLEX_create(gsl_complex_rect((double)conv->_long.value, 0)); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + conv->_object.value = COMPLEX_create(gsl_complex_rect(conv->_integer.value, 0)); + return FALSE; + + default: + return TRUE; + } + } +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Complex_new, GB_FLOAT real; GB_FLOAT imag) + + THIS->number.dat[0] = VARGOPT(real, 0.0); + THIS->number.dat[1] = VARGOPT(imag, 0.0); + +END_METHOD + + +BEGIN_METHOD(Complex_call, GB_FLOAT real; GB_FLOAT imag) + + GB.ReturnObject(COMPLEX_create(gsl_complex_rect(VARG(real), VARG(imag)))); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_Copy) + + GB.ReturnObject(COMPLEX_create(THIS->number)); + +END_METHOD + + +BEGIN_METHOD(Complex_ToString, GB_BOOLEAN local) + + GB.ReturnString(GB.FreeStringLater(COMPLEX_to_string(THIS->number, VARGOPT(local, FALSE)))); + +END_METHOD + + +BEGIN_METHOD(Complex_Polar, GB_FLOAT real; GB_FLOAT imag) + + GB.ReturnObject(COMPLEX_create(gsl_complex_polar(VARGOPT(real, 0.0), VARGOPT(imag, 0.0)))); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_Arg) + + GB.ReturnFloat(gsl_complex_arg(THIS->number)); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_Abs) + + GB.ReturnFloat(gsl_complex_abs(THIS->number)); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_Abs2) + + GB.ReturnFloat(gsl_complex_abs2(THIS->number)); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_LogAbs) + + GB.ReturnFloat(gsl_complex_logabs(THIS->number)); + +END_METHOD + + + +/****************************** + Property Methods +******************************/ + +BEGIN_PROPERTY(Complex_Real) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->number.dat[0]); + else + THIS->number.dat[0] = (VPROP(GB_FLOAT)); + +END_PROPERTY + + +BEGIN_PROPERTY(Complex_Imagined) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->number.dat[1]); + else + THIS->number.dat[1] = (VPROP(GB_FLOAT)); + +END_PROPERTY + + +BEGIN_PROPERTY(Complex_Handle) + + GB.ReturnPointer(&THIS->number); + +END_PROPERTY + + +/************************************************** + Complex arithmetic operators +**************************************************/ + +#define IMPLEMENT_OP(_name, _func) \ +BEGIN_METHOD(Complex_##_name, GB_OBJECT x) \ + \ + CCOMPLEX *x = VARG(x); \ + \ + if (GB.CheckObject(x)) \ + return; \ + \ + GB.ReturnObject(COMPLEX_create(_func(THIS->number, x->number))); \ + \ +END_METHOD + +IMPLEMENT_OP(Add, gsl_complex_add) +IMPLEMENT_OP(Sub, gsl_complex_sub) +IMPLEMENT_OP(Mul, gsl_complex_mul) +IMPLEMENT_OP(Div, gsl_complex_div) + + +/********************************************* + Operations On Real +*********************************************/ + +#define IMPLEMENT_OP_REAL(_name, _func) \ +BEGIN_METHOD(Complex_##_name, GB_FLOAT x) \ + \ + GB.ReturnObject(COMPLEX_create(_func(THIS->number, VARG(x)))); \ + \ +END_METHOD + +IMPLEMENT_OP_REAL(AddReal, gsl_complex_add_real) +IMPLEMENT_OP_REAL(SubReal, gsl_complex_sub_real) +IMPLEMENT_OP_REAL(MulReal, gsl_complex_mul_real) +IMPLEMENT_OP_REAL(DivReal, gsl_complex_div_real) + + +/********************************************* + Operations On Imaginary +*********************************************/ + +IMPLEMENT_OP_REAL(AddImag, gsl_complex_add_imag) +IMPLEMENT_OP_REAL(SubImag, gsl_complex_sub_imag) +IMPLEMENT_OP_REAL(MulImag, gsl_complex_mul_imag) +IMPLEMENT_OP_REAL(DivImag, gsl_complex_div_imag) + +#define IMPLEMENT_FUNC(_name, _func) \ +BEGIN_METHOD_VOID(Complex_##_name) \ + \ + GB.ReturnObject(COMPLEX_create(_func(THIS->number))); \ + \ +END_METHOD + +IMPLEMENT_FUNC(Conjugate, gsl_complex_conjugate) +IMPLEMENT_FUNC(Inverse, gsl_complex_inverse) +IMPLEMENT_FUNC(Negative, gsl_complex_negative) + +/************************************************** + Elementary Complex Functions +**************************************************/ + +IMPLEMENT_FUNC(Sqrt, gsl_complex_sqrt) + +#define IMPLEMENT_FUNC_REAL(_name, _func) \ +BEGIN_METHOD(Complex_##_name, GB_FLOAT x) \ + \ + GB.ReturnObject(COMPLEX_create(_func(VARG(x)))); \ + \ +END_METHOD + +IMPLEMENT_FUNC_REAL(SqrtReal, gsl_complex_sqrt_real) + +IMPLEMENT_OP(Pow, gsl_complex_pow) +IMPLEMENT_OP_REAL(PowReal, gsl_complex_pow_real) + +IMPLEMENT_FUNC(Exp, gsl_complex_exp) +IMPLEMENT_FUNC(Log, gsl_complex_log) +IMPLEMENT_FUNC(Log10, gsl_complex_log10) +IMPLEMENT_OP(LogB, gsl_complex_log_b) + +/************************************************** + Complex Trigonometric Functions +**************************************************/ + +IMPLEMENT_FUNC(Sin, gsl_complex_sin) +IMPLEMENT_FUNC(Cos, gsl_complex_cos) +IMPLEMENT_FUNC(Tan, gsl_complex_tan) +IMPLEMENT_FUNC(Sec, gsl_complex_sec) +IMPLEMENT_FUNC(Csc, gsl_complex_csc) +IMPLEMENT_FUNC(Cot, gsl_complex_cot) + +/************************************************** + Inverse Complex Trigonometric Functions +**************************************************/ + +IMPLEMENT_FUNC(Arcsin, gsl_complex_arcsin) +IMPLEMENT_FUNC_REAL(ArcsinReal, gsl_complex_arcsin_real) +IMPLEMENT_FUNC(Arccos, gsl_complex_arccos) +IMPLEMENT_FUNC_REAL(ArccosReal, gsl_complex_arccos_real) +IMPLEMENT_FUNC(Arctan, gsl_complex_arctan) +IMPLEMENT_FUNC(Arcsec, gsl_complex_arcsec) +IMPLEMENT_FUNC_REAL(ArcsecReal, gsl_complex_arcsec_real) +IMPLEMENT_FUNC(Arccsc, gsl_complex_arccsc) +IMPLEMENT_FUNC_REAL(ArccscReal, gsl_complex_arccsc_real) +IMPLEMENT_FUNC(Arccot, gsl_complex_arccot) + +/************************************************** + Complex Hyperbolic Functions +**************************************************/ + +IMPLEMENT_FUNC(Sinh, gsl_complex_sinh) +IMPLEMENT_FUNC(Cosh, gsl_complex_cosh) +IMPLEMENT_FUNC(Tanh, gsl_complex_tanh) +IMPLEMENT_FUNC(Sech, gsl_complex_sech) +IMPLEMENT_FUNC(Csch, gsl_complex_csch) +IMPLEMENT_FUNC(Coth, gsl_complex_coth) + +/************************************************** + Inverse Complex Hyperbolic Functions +**************************************************/ + +IMPLEMENT_FUNC(Arcsinh, gsl_complex_arcsinh) +IMPLEMENT_FUNC(Arccosh, gsl_complex_arccosh) +IMPLEMENT_FUNC_REAL(ArccoshReal, gsl_complex_arccosh_real) +IMPLEMENT_FUNC(Arctanh, gsl_complex_arctanh) +IMPLEMENT_FUNC_REAL(ArctanhReal, gsl_complex_arctanh_real) +IMPLEMENT_FUNC(Arcsech, gsl_complex_arcsech) +IMPLEMENT_FUNC(Arccsch, gsl_complex_arccsch) +IMPLEMENT_FUNC(Arccoth, gsl_complex_arccoth) + +/************************************************** + Describe Class properties and methods to Gambas +**************************************************/ + +GB_DESC ComplexDesc[] = +{ + GB_DECLARE("Complex", sizeof(CCOMPLEX)), + + // Utility Methods + GB_METHOD("_new", NULL, Complex_new, "[(Real)f(Imag)f]"), + GB_STATIC_METHOD("_call", "Complex", Complex_call, "[(Real)f(Imag)f]"), + GB_STATIC_METHOD("Polar", "Complex", Complex_Polar, "[(Abs)f(Arg)f]"), + + GB_METHOD("Copy", "Complex", Complex_Copy, NULL), + GB_METHOD("ToString", "s", Complex_ToString, "[(Local)b]"), + + GB_METHOD("Conj", "Complex", Complex_Conjugate, NULL), + //GB_METHOD("Neg", "Complex", Complex_Negative, NULL), + GB_METHOD("Inv", "Complex", Complex_Inverse, NULL), + //GB_METHOD("Set", NULL, Complex_Set, "[(Real)f(Imag)f]"), + + // Properties + GB_PROPERTY("Real", "f", Complex_Real), + GB_PROPERTY("Imag", "f", Complex_Imagined), + GB_PROPERTY("Handle", "p", Complex_Handle), + + GB_METHOD("Abs", "f", Complex_Abs, NULL), + GB_METHOD("Abs2", "f", Complex_Abs2, NULL), + GB_METHOD("LogAbs", "f", Complex_LogAbs, NULL), + GB_METHOD("Arg", "f", Complex_Arg, NULL), + + /* Operations on gsl_complex */ + // Elementary Math Functions + //GB_METHOD("Add", "Complex", Complex_Add, "(X)Complex"), + //GB_METHOD("Sub", "Complex", Complex_Sub, "(X)Complex"), + //GB_METHOD("Mul", "Complex", Complex_Mul, "(X)Complex"), + //GB_METHOD("Div", "Complex", Complex_Div, "(X)Complex"), + + // Operations On Real + //GB_METHOD("AddReal", "Complex", Complex_AddReal, "(X)f"), + //GB_METHOD("SubReal", "Complex", Complex_SubReal, "(X)f"), + //GB_METHOD("MulReal", "Complex", Complex_MulReal, "(X)f"), + //GB_METHOD("DivReal", "Complex", Complex_DivReal, "(X)f"), + + // Operations On Imaginary + //GB_METHOD("AddImag", "Complex", Complex_AddImag, "(X)f"), + //GB_METHOD("SubImag", "Complex", Complex_SubImag, "(X)f"), + //GB_METHOD("MulImag", "Complex", Complex_MulImag, "(X)f"), + //GB_METHOD("DivImag", "Complex", Complex_DivImag, "(X)f"), + + // Elementary Complex Functions + GB_METHOD("Sqrt", "Complex", Complex_Sqrt, NULL), + GB_STATIC_METHOD("SqrtReal", "Complex", Complex_SqrtReal, "(X)f"), + //GB_METHOD("Pow", "Complex", Complex_Pow, "(X)Complex"), + //GB_METHOD("PowReal", "Complex", Complex_PowReal, "(X)f"), + GB_METHOD("Exp", "Complex", Complex_Exp, NULL), + GB_METHOD("Log", "Complex", Complex_Log, NULL), + GB_METHOD("Log10", "Complex", Complex_Log10, NULL), + GB_METHOD("LogB", "Complex", Complex_LogB, "(X)Complex"), + + // Complex Trigonometric Functions + GB_METHOD("Sin", "Complex", Complex_Sin, NULL), + GB_METHOD("Cos", "Complex", Complex_Cos, NULL), + GB_METHOD("Tan", "Complex", Complex_Tan, NULL), + GB_METHOD("Sec", "Complex", Complex_Sec, NULL), + GB_METHOD("Csc", "Complex", Complex_Csc, NULL), + GB_METHOD("Cot", "Complex", Complex_Cot, NULL), + + // Inverse Complex Trigonometric Functions + GB_METHOD("ASin", "Complex", Complex_Arcsin, NULL), + GB_STATIC_METHOD("ASinReal", "Complex", Complex_ArcsinReal, "(X)f"), + GB_METHOD("ACos", "Complex", Complex_Arccos, NULL), + GB_STATIC_METHOD("ACosReal", "Complex", Complex_ArccosReal, "(X)f"), + GB_METHOD("ATan", "Complex", Complex_Arctan, NULL), + GB_METHOD("ASec", "Complex", Complex_Arcsec, NULL), + GB_STATIC_METHOD("ASecReal", "Complex", Complex_ArcsecReal, "(X)f"), + GB_METHOD("ACsc", "Complex", Complex_Arccsc, NULL), + GB_STATIC_METHOD("ACscReal", "Complex", Complex_ArccscReal, "(X)f"), + GB_METHOD("ACot", "Complex", Complex_Arccot, NULL), + + // Complex Hyperbolic Functions + GB_METHOD("Sinh", "Complex", Complex_Sinh, NULL), + GB_METHOD("Cosh", "Complex", Complex_Cosh, NULL), + GB_METHOD("Tanh", "Complex", Complex_Tanh, NULL), + GB_METHOD("Sech", "Complex", Complex_Sech, NULL), + GB_METHOD("Csch", "Complex", Complex_Csch, NULL), + GB_METHOD("Coth", "Complex", Complex_Coth, NULL), + + // Inverse Complex Hyperbolic Functions + GB_METHOD("ASinh", "Complex", Complex_Arcsinh, NULL), + GB_METHOD("ACosh", "Complex", Complex_Arccosh, NULL), + GB_STATIC_METHOD("ACoshReal", "Complex", Complex_ArccoshReal, "(X)f"), + GB_METHOD("ATanh", "Complex", Complex_Arctanh, NULL), + GB_STATIC_METHOD("ATanhReal", "Complex", Complex_ArctanhReal, "(X)f"), + GB_METHOD("ASech", "Complex", Complex_Arcsech, NULL), + GB_METHOD("ACsch", "Complex", Complex_Arccsch, NULL), + GB_METHOD("ACoth", "Complex", Complex_Arccoth, NULL), + + GB_INTERFACE("_operator", &_operator), + GB_INTERFACE("_convert", &_convert), + + GB_END_DECLARE +}; diff --git a/gb.gsl/src/c_complex.h b/gb.gsl/src/c_complex.h new file mode 100644 index 00000000..6b6db0cb --- /dev/null +++ b/gb.gsl/src/c_complex.h @@ -0,0 +1,70 @@ +/*************************************************************************** + + c_complex.h + + gb.gsl component + + (c) 2012 Randall Morgan + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_COMPLEX_H +#define __C_COMPLEX_H + +#include "main.h" + +#ifndef _C_COMPLEX_C +extern GB_DESC ComplexDesc[]; +extern gsl_complex COMPLEX_zero; +extern gsl_complex COMPLEX_one; +//extern GB_DESC ComplexArrayDesc[]; +#endif + +typedef + struct + { + GB_BASE ob; + gsl_complex number; + } + CCOMPLEX; + +typedef + union + { + gsl_complex z; + double x; + } + COMPLEX_VALUE; + +enum +{ + CGV_ERR, + CGV_FLOAT, + CGV_COMPLEX +}; + +CCOMPLEX *COMPLEX_create(gsl_complex number); +CCOMPLEX *COMPLEX_push_complex(double value); +char *COMPLEX_to_string(gsl_complex number, bool local); + +#define COMPLEX_get(_c) ((_c) ? (_c)->number : COMPLEX_zero) + +int COMPLEX_get_value(GB_VALUE *value, COMPLEX_VALUE *v); +int COMPLEX_comp(gsl_complex a, gsl_complex b); + +#endif /* __C_COMPLEX_H */ diff --git a/gb.gsl/src/c_gsl.c b/gb.gsl/src/c_gsl.c new file mode 100644 index 00000000..7c979c49 --- /dev/null +++ b/gb.gsl/src/c_gsl.c @@ -0,0 +1,315 @@ +/*************************************************************************** + + c_gsl.c + + gb.gsl component + + (c) 2012 Randall Morgan + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_GSL_C + +#include "c_gsl.h" +#include +#include + + +/*-------------------------------- + Number testing functions +--------------------------------*/ + +BEGIN_METHOD(GSL_ISNAN, GB_FLOAT x;) + // This function returns 1 if x is not-a-number. + // Call GSL Function int gsl_isnan(const double x) + int c; + + c = gsl_isnan(VARG(x)); + + GB.ReturnBoolean(c); + +END_METHOD + + +BEGIN_METHOD(GSL_ISINF, GB_FLOAT x;) + // This function returns +1 if x is positive infinity, + // -1 if x is negative infinity and 0 otherwise. + // Call GSL Function int gsl_isinf(const double x) + int c; + + c= gsl_isinf(VARG(x)); + + GB.ReturnInteger(c); + +END_METHOD + + +BEGIN_METHOD(GSL_ISFINITE, GB_FLOAT x;) + // This function returns 1 if x is a real number, + // and -1 if it is infinite or not-a-number. + // Call GSL Function int gsl_isfinite(const double x) + int c; + + c = gsl_finite(VARG(x)); + + GB.ReturnBoolean(c); + +END_METHOD + + +BEGIN_METHOD(GSL_FCMP, GB_FLOAT x; GB_FLOAT y; GB_FLOAT e;) + // Function: int gsl_fcmp (double x, double y, double epsilon) + // This function determines whether x and y are approximately + // equal to a relative accuracy epsilon. + // The relative accuracy is measured using an interval of size 2 \delta, + // where \delta = 2^k \epsilon and k is the maximum base-2 exponent of x + // and y as computed by the function frexp. + // If x and y lie within this interval, they are considered approximately + // equal and the function returns 0. Otherwise if x < y, the function returns + // -1, or if x > y, the function returns +1. + // Note that x and y are compared to relative accuracy, so this function is + // not suitable for testing whether a value is approximately zero. + + GB.ReturnInteger(gsl_fcmp (VARG(x), VARG(y), VARG(e))); + +END_METHOD + + + +/*----------------------------------------------- +Elementary Functions +-----------------------------------------------*/ + +BEGIN_METHOD(GSL_LOG1P, GB_FLOAT x;) + // This function computes the value of \log(1+x) + // in a way that is accurate for small x. + // Call GSL Function int gsl_isnan(const double x) + GB.ReturnFloat(gsl_log1p (VARG(x))); + +END_METHOD + + +BEGIN_METHOD(GSL_EXPM1, GB_FLOAT x;) + // This function computes the value of \exp(x)-1 + // in a way that is accurate for small x. + GB.ReturnFloat(gsl_expm1 (VARG(x))); + +END_METHOD + + +BEGIN_METHOD(GSL_HYPOT, GB_FLOAT x; GB_FLOAT y;) + // This function computes the value of + // \sqrt{x^2 + y^2} in a way that avoids overflow. + // Call GSL function double gsl_hypot (const double x, const double y) + GB.ReturnFloat(gsl_hypot(VARG(x), VARG(y))); + +END_METHOD + + +BEGIN_METHOD(GSL_HYPOT3, GB_FLOAT x; GB_FLOAT y; GB_FLOAT z;) + // This function computes the value of \sqrt{x^2 + y^2 + z^2} + // in a way that avoids overflow. + // Call GSL function double gsl_hypot3 (const double x, const double y, const double z) + + GB.ReturnFloat(gsl_hypot3(VARG(x), VARG(y), VARG(z))); + +END_METHOD + + +BEGIN_METHOD(GSL_ACOSH, GB_FLOAT x;) + // This function computes the value of \arccosh(x). + // It provides an alternative to the standard math function acosh(x). + GB.ReturnFloat(gsl_acosh(VARG(x))); + +END_METHOD + +BEGIN_METHOD(GSL_ASINH, GB_FLOAT x;) + // Function: double gsl_asinh (const double x) + // This function computes the value of arcsinh(x). + // It provides an alternative to the standard math function asinh(x). + GB.ReturnFloat(gsl_asinh(VARG(x))); +END_METHOD + +BEGIN_METHOD(GSL_ATANH, GB_FLOAT x;) + // Function: double gsl_atanh (const double x) + // This function computes the value of \arctanh(x). + // It provides an alternative to the standard math function atanh(x). + GB.ReturnFloat(gsl_atanh(VARG(x))); +END_METHOD + +BEGIN_METHOD(GSL_LDEXP, GB_FLOAT x; GB_INTEGER e;) + // Function: double gsl_ldexp (double x, int e) + // This function computes the value of x * 2^e. + // It provides an alternative to the standard math function ldexp(x,e). + GB.ReturnFloat(gsl_ldexp(VARG(x), VARG(e))); +END_METHOD + + +BEGIN_METHOD(GSL_FREXP, GB_FLOAT x;) + // Function: double gsl_frexp (double x, int * e) + // This function splits the number x into its normalized + // fraction f and exponent e, such that x = f * 2^e and + // 0.5 <= f < 1. The function returns f and stores the + // exponent in e. If x is zero, both f and e are set to + // zero. This function provides an alternative to the + // standard math function frexp(x, e). + int b; + double r; + GB_ARRAY arr; + + b = 0.0; + r = gsl_frexp(VARG(x), &b); + //printf("r: %f \te: %i\n", r, b); + + GB.Array.New(&arr, GB_T_FLOAT, 2); + *((double *)GB.Array.Get(arr, 0)) = r; + *((double *)GB.Array.Get(arr, 1)) = b; + GB.ReturnObject(arr); + +END_METHOD + +// BM they are useless, as they are already implemented that way in the interpreter. + +#if 0 +/*----------------------------------------------- +Small Integer Power Functions +-----------------------------------------------*/ +BEGIN_METHOD(GSL_INTPOW, GB_FLOAT x; GB_INTEGER i;) + // A common complaint about the standard C library is its lack + // of a function for calculating (small) integer powers. GSL + // provides some simple functions to fill this gap. For reasons + // of efficiency, these functions do not check for overflow or + // underflow conditions. + GB.ReturnFloat(gsl_pow_int(VARG(x), VARG(i))); + +END_METHOD + + +BEGIN_METHOD(GSL_INTPOW2, GB_FLOAT x;) + // Return x^2 using a small int safe method + // call gsl native function double gsl_pow_2(double x) + GB.ReturnFloat(gsl_pow_2(VARG(x))); +END_METHOD + + +BEGIN_METHOD(GSL_INTPOW3, GB_FLOAT x;) + // Return x^3 using a small int safe method + // call gsl native function double gsl_pow_3(double x) + GB.ReturnFloat(gsl_pow_3(VARG(x))); +END_METHOD + + +BEGIN_METHOD(GSL_INTPOW4, GB_FLOAT x;) + // Return x^4 using a small int safe method + // call gsl native function double gsl_pow_3(double x) + GB.ReturnFloat(gsl_pow_4(VARG(x))); +END_METHOD + +BEGIN_METHOD(GSL_INTPOW5, GB_FLOAT x;) + // Return x^5 using a small int safe method + // call gsl native function double gsl_pow_3(double x) + GB.ReturnFloat(gsl_pow_5(VARG(x))); +END_METHOD + + +BEGIN_METHOD(GSL_INTPOW6, GB_FLOAT x;) + // Return x^6 using a small int safe method + // call gsl native function double gsl_pow_3(double x) + GB.ReturnFloat(gsl_pow_6(VARG(x))); +END_METHOD + + +BEGIN_METHOD(GSL_INTPOW7, GB_FLOAT x;) + // Return x^7 using a small int safe method + // call gsl native function double gsl_pow_3(double x) + GB.ReturnFloat(gsl_pow_7(VARG(x))); +END_METHOD + + +BEGIN_METHOD(GSL_INTPOW8, GB_FLOAT x;) + // Return x^8 using a small int safe method + // call gsl native function double gsl_pow_3(double x) + GB.ReturnFloat(gsl_pow_8(VARG(x))); +END_METHOD + + +BEGIN_METHOD(GSL_INTPOW9, GB_FLOAT x;) + // Return x^9 using a small int safe method + // call gsl native function double gsl_pow_3(double x) + GB.ReturnFloat(gsl_pow_9(VARG(x))); +END_METHOD +#endif + +/************************************************** + Describe Class properties and methods to Gambas +**************************************************/ +GB_DESC CGslDesc[] = +{ + GB_DECLARE("GSL",0), GB_NOT_CREATABLE(), + + // Number testing functions + GB_STATIC_METHOD("IsNan", "b", GSL_ISNAN, "(X)f"), + GB_STATIC_METHOD("IsInf", "i", GSL_ISINF, "(X)f"), + GB_STATIC_METHOD("IsFinite", "b", GSL_ISFINITE, "(X)f"), + GB_STATIC_METHOD("Fcmp", "i", GSL_FCMP, "(X)f(Y)f(E)f"), + + // Elementary Functions + GB_STATIC_METHOD("Log1p", "f", GSL_LOG1P, "(X)f"), + GB_STATIC_METHOD("Expm1", "f", GSL_EXPM1, "(X)f"), + GB_STATIC_METHOD("Hypot", "f", GSL_HYPOT, "(X)f(Y)f"), + GB_STATIC_METHOD("Hypot3", "f", GSL_HYPOT3, "(X)f(Y)f(Z)f"), + GB_STATIC_METHOD("Acosh", "f", GSL_ACOSH, "(X)f"), + GB_STATIC_METHOD("Asinh", "f", GSL_ASINH, "(X)f"), + GB_STATIC_METHOD("Atanh", "f", GSL_ATANH, "(X)f"), + GB_STATIC_METHOD("Ldexp", "f", GSL_LDEXP, "(X)f(E)i"), + GB_STATIC_METHOD("Frexp", "Float[]", GSL_FREXP, "(X)f"), + + // Return x^y using a small int safe method + /*GB_STATIC_METHOD("IntPow", "f", GSL_INTPOW, "(X)f(I)i"), + GB_STATIC_METHOD("IntPow2", "f", GSL_INTPOW2, "(X)f"), + GB_STATIC_METHOD("IntPow3", "f", GSL_INTPOW3, "(X)f"), + GB_STATIC_METHOD("IntPow4", "f", GSL_INTPOW4, "(X)f"), + GB_STATIC_METHOD("IntPow5", "f", GSL_INTPOW5, "(X)f"), + GB_STATIC_METHOD("IntPow6", "f", GSL_INTPOW6, "(X)f"), + GB_STATIC_METHOD("IntPow7", "f", GSL_INTPOW7, "(X)f"), + GB_STATIC_METHOD("IntPow8", "f", GSL_INTPOW8, "(X)f"), + GB_STATIC_METHOD("IntPow9", "f", GSL_INTPOW9, "(X)f"),*/ + + // Class Constants + GB_FLOAT_CONSTANT("E", M_E), + GB_FLOAT_CONSTANT("LOG2E", M_LOG2E), + GB_FLOAT_CONSTANT("LOG10E", M_LOG10E), + GB_FLOAT_CONSTANT("SQRT2", M_SQRT2), + GB_FLOAT_CONSTANT("SQRT1_2", M_SQRT1_2), + GB_FLOAT_CONSTANT("SQRT3", M_SQRT3), + GB_FLOAT_CONSTANT("PI", M_PI), + GB_FLOAT_CONSTANT("PI_2", M_PI_2), + GB_FLOAT_CONSTANT("PI_4", M_PI_4), + GB_FLOAT_CONSTANT("SQRTPI", M_SQRTPI), + GB_FLOAT_CONSTANT("INV2_SQRTPI", M_2_SQRTPI), + GB_FLOAT_CONSTANT("INV_PI", M_1_PI), + GB_FLOAT_CONSTANT("INV2_PI", M_2_PI), + GB_FLOAT_CONSTANT("LN10", M_LN10), + GB_FLOAT_CONSTANT("LN2", M_LN2), + GB_FLOAT_CONSTANT("LNPI", M_LNPI), + GB_FLOAT_CONSTANT("EULER", M_EULER), + + GB_END_DECLARE +}; + + diff --git a/gb.gsl/src/c_gsl.h b/gb.gsl/src/c_gsl.h new file mode 100644 index 00000000..45bee674 --- /dev/null +++ b/gb.gsl/src/c_gsl.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + c_gsl.h + + gb.gsl component + + (c) 2012 Randall Morgan + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_GSL_H +#define __C_GSL_H + +#include "gambas.h" + +extern GB_INTERFACE GB EXPORT; + +#ifndef __C_GSL_C +extern GB_DESC CGslDesc[]; +#endif + +#endif /* __C_GSL_H */ diff --git a/gb.gsl/src/c_matrix.c b/gb.gsl/src/c_matrix.c new file mode 100644 index 00000000..37a6bf3b --- /dev/null +++ b/gb.gsl/src/c_matrix.c @@ -0,0 +1,1300 @@ +/*************************************************************************** + + c_vector.c + + gb.gsl component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_MATRIX_C + +#include "c_complex.h" +#include "c_vector.h" +#include "c_matrix.h" + +#define THIS ((CMATRIX *)_object) +#define MAT(_m) ((gsl_matrix *)(_m)->matrix) +#define CMAT(_m) ((gsl_matrix_complex *)(_m)->matrix) +#define HEIGHT(_m) ((int)(MAT(_m)->size1)) +#define WIDTH(_m) ((int)(MAT(_m)->size2)) +#define COMPLEX(_m) ((_m)->complex) + +//---- Matrix creation ------------------------------------------------------ + +static CMATRIX *MATRIX_create(int width, int height, bool complex, bool init) +{ + CMATRIX *m = GB.Create(CLASS_Matrix, NULL,NULL); + + if (complex) + m->matrix = init ? gsl_matrix_complex_calloc(height, width) : gsl_matrix_complex_alloc(height, width); + else + m->matrix = init ? gsl_matrix_calloc(height, width) : gsl_matrix_alloc(height, width); + + m->complex = complex; + return m; +} + +static CMATRIX *MATRIX_identity(int width, int height, bool complex) +{ + CMATRIX *m = MATRIX_create(width, height, complex, FALSE); + + if (complex) + gsl_matrix_complex_set_identity(CMAT(m)); + else + gsl_matrix_set_identity(MAT(m)); + + return m; +} + +static CMATRIX *MATRIX_create_from(void *matrix, bool complex) +{ + CMATRIX *m = GB.Create(CLASS_Matrix, NULL,NULL); + + m->matrix = matrix; + m->complex = complex; + return m; +} + +static CMATRIX *MATRIX_copy(CMATRIX *_object) +{ + CMATRIX *copy = MATRIX_create(WIDTH(THIS), HEIGHT(THIS), COMPLEX(THIS), FALSE); + if (COMPLEX(THIS)) + gsl_matrix_complex_memcpy(CMAT(copy), CMAT(THIS)); + else + gsl_matrix_memcpy(MAT(copy), MAT(THIS)); + + return copy; +} + +#define MATRIX_make(_ma) (((_ma)->ob.ref <= 1) ? (_ma) : MATRIX_copy(_ma)) + +/*static CMATRIX *MATRIX_convert_to_complex(CMATRIX *_object) +{ + CMATRIX *m = MATRIX_create(WIDTH(THIS), HEIGHT(THIS), TRUE, FALSE); + int i, j; + + for (i = 0; i < HEIGHT(THIS); i++) + for (j = 0; j < WIDTH(THIS); j++) + gsl_matrix_complex_set(CMAT(m), i, j, gsl_complex_rect(gsl_matrix_get(MAT(THIS), i, j), 0)); + + return m; +}*/ + +static void MATRIX_ensure_complex(CMATRIX *_object) +{ + gsl_matrix_complex *v; + int w = WIDTH(THIS); + int h = HEIGHT(THIS); + int i, j; + + if (COMPLEX(THIS)) + return; + + v = gsl_matrix_complex_alloc(h, w); + for (i = 0; i < h; i++) + for (j = 0; j < w; j++) + gsl_matrix_complex_set(v, i, j, gsl_complex_rect(gsl_matrix_get(MAT(THIS), i, j), 0)); + + gsl_matrix_free(MAT(THIS)); + THIS->matrix = v; + THIS->complex = TRUE; +} + + +/*static bool MATRIX_ensure_not_complex(CMATRIX *_object) +{ + gsl_matrix *m; + int w = WIDTH(THIS); + int h = HEIGHT(THIS); + int i, j; + gsl_complex c; + + if (!COMPLEX(THIS)) + return FALSE; + + for (i = 0; i < h; i++) + for (j = 0; j < w; j++) + { + c = gsl_matrix_complex_get(CMAT(THIS), i, j); + if (GSL_IMAG(c) != 0.0) + return TRUE; + } + + m = gsl_matrix_alloc(h, w); + + for (i = 0; i < h; i++) + for (j = 0; j < w; j++) + gsl_matrix_set(m, i, j, GSL_REAL(gsl_matrix_complex_get(CMAT(THIS), i, j))); + + gsl_matrix_complex_free(CMAT(THIS)); + THIS->matrix = m; + THIS->complex = FALSE; + return FALSE; +}*/ + +static void matrix_negative(void *m, bool complex) +{ + uint i; + gsl_matrix *mm = (gsl_matrix *)m; + double *d = mm->data; + uint n = (uint)(mm->size1 * mm->size2); + + if (complex) + n *= 2; + + for (i = 0; i < n; i++) + d[i] = -d[i]; +} + +static bool matrix_determinant(CMATRIX *m, COMPLEX_VALUE *det) +{ + int sign = 0; + int size = WIDTH(m); + + if (size != HEIGHT(m)) + return TRUE; + + gsl_permutation *p = gsl_permutation_calloc(size); + + if (COMPLEX(m)) + { + gsl_matrix_complex *tmp = gsl_matrix_complex_alloc(size, size); + gsl_matrix_complex_memcpy(tmp, CMAT(m)); + gsl_linalg_complex_LU_decomp(tmp, p, &sign); + det->z = gsl_linalg_complex_LU_det(tmp, sign); + gsl_matrix_complex_free(tmp); + } + else + { + gsl_matrix *tmp = gsl_matrix_alloc(size, size); + gsl_matrix_memcpy(tmp, MAT(m)); + gsl_linalg_LU_decomp(tmp, p, &sign); + det->x = gsl_linalg_LU_det(tmp, sign); + det->z.dat[1] = 0; + gsl_matrix_free(tmp); + } + + gsl_permutation_free(p); + return FALSE; +} + +static void *matrix_invert(void *m, bool complex) +{ + int sign = 0; + int size = ((gsl_matrix *)m)->size1; + void *result; + + if (size != ((gsl_matrix *)m)->size2) + return NULL; + + gsl_permutation *p = gsl_permutation_calloc(size); + + if (!complex) + { + gsl_matrix *tmp = gsl_matrix_alloc(size, size); + result = gsl_matrix_alloc(size, size); + gsl_matrix_memcpy(tmp, (gsl_matrix *)m); + gsl_linalg_LU_decomp(tmp, p, &sign); + if (gsl_linalg_LU_invert(tmp, p, (gsl_matrix *)result) != GSL_SUCCESS) + { + gsl_matrix_free(result); + return NULL; + } + gsl_matrix_free(tmp); + } + else + { + gsl_matrix_complex *tmp = gsl_matrix_complex_alloc(size, size); + result = gsl_matrix_complex_alloc(size, size); + gsl_matrix_complex_memcpy(tmp, (gsl_matrix_complex *)m); + gsl_linalg_complex_LU_decomp(tmp, p, &sign); + if (gsl_linalg_complex_LU_invert(tmp, p, (gsl_matrix_complex *)result) != GSL_SUCCESS) + { + gsl_matrix_complex_free(result); + return NULL; + } + gsl_matrix_complex_free(tmp); + } + + gsl_permutation_free(p); + return result; +} + +static void matrix_add_identity(gsl_matrix *m, double f) +{ + gsl_matrix *id = gsl_matrix_alloc(m->size1, m->size2); + gsl_matrix_set_identity(id); + gsl_matrix_scale(id, f); + gsl_matrix_add(m, id); + gsl_matrix_free(id); +} + +static void matrix_complex_add_identity(gsl_matrix_complex *m, gsl_complex c) +{ + gsl_matrix_complex *id = gsl_matrix_complex_alloc(m->size1, m->size2); + gsl_matrix_complex_set_identity(id); + gsl_matrix_complex_scale(id, c); + gsl_matrix_complex_add(m, id); + gsl_matrix_complex_free(id); +} + + + +//---- Arithmetic operators ------------------------------------------------- + +#define IMPLEMENT_OP(_name) \ +static CMATRIX *_name(CMATRIX *a, CMATRIX *b, bool invert) \ +{ \ + CMATRIX *m; \ + \ + if (COMPLEX(a) || COMPLEX(b)) \ + { \ + MATRIX_ensure_complex(a); \ + MATRIX_ensure_complex(b); \ + m = MAKE_MATRIX(a); \ + CFUNC(CMAT(m), CMAT(a), CMAT(b)); \ + } \ + else \ + { \ + m = MAKE_MATRIX(a); \ + FUNC(MAT(m), MAT(a), MAT(b)); \ + } \ + \ + return m; \ +} + +#define IMPLEMENT_OP_FLOAT(_name) \ +static CMATRIX *_name(CMATRIX *a, double f, bool invert) \ +{ \ + CMATRIX *m = MAKE_MATRIX(a); \ + \ + if (COMPLEX(a)) \ + { \ + CFUNC(CMAT(m), CMAT(a), f); \ + } \ + else \ + { \ + FUNC(MAT(m), MAT(a), f); \ + } \ + \ + return m; \ +} + +#define IMPLEMENT_OP_OTHER(_name) \ +static CMATRIX *_name(CMATRIX *a, void *b, bool invert) \ +{ \ + CMATRIX *m = MAKE_MATRIX(a); \ + \ + if (GB.Is(b, CLASS_Complex)) \ + { \ + MATRIX_ensure_complex(m); \ + CFUNC(CMAT(m), CMAT(a), ((CCOMPLEX *)b)->number); \ + return m; \ + } \ + else \ + return NULL; \ +} + +#define MAKE_MATRIX(_a) MATRIX_make(_a) + +#define FUNC(_m, _a, _b) gsl_matrix_add(_m, _b) +#define CFUNC(_m, _a, _b) gsl_matrix_complex_add(_m, _b) +IMPLEMENT_OP(_add) +#undef FUNC +#undef CFUNC + +#define FUNC(_m, _a, _f) matrix_add_identity(_m, _f) +#define CFUNC(_m, _a, _f) matrix_complex_add_identity(_m, gsl_complex_rect(_f, 0)) +IMPLEMENT_OP_FLOAT(_addf) +#undef FUNC +#undef CFUNC + +#define CFUNC(_m, _a, _c) matrix_complex_add_identity(_m, _c) +IMPLEMENT_OP_OTHER(_addo) +#undef CFUNC + +#define FUNC(_m, _a, _b) gsl_matrix_sub(_m, _b) +#define CFUNC(_m, _a, _b) gsl_matrix_complex_sub(_m, _b) +IMPLEMENT_OP(_sub) +#undef FUNC +#undef CFUNC + +#define FUNC(_m, _a, _f) \ + if (invert) \ + { \ + matrix_negative(_m, FALSE); \ + matrix_add_identity(_m, _f); \ + } \ + else \ + matrix_add_identity(_m, -(_f)); + +#define CFUNC(_m, _a, _f) \ + if (invert) \ + { \ + matrix_negative(_m, TRUE); \ + matrix_complex_add_identity(_m, gsl_complex_rect(_f, 0)); \ + } \ + else \ + matrix_complex_add_identity(_m, gsl_complex_rect(-(_f), 0)); + +IMPLEMENT_OP_FLOAT(_subf) +#undef FUNC +#undef CFUNC + +#define CFUNC(_m, _a, _c) \ + if (invert) \ + matrix_negative(_m, TRUE); \ + else \ + gsl_complex_negative(_c); \ + matrix_complex_add_identity(_m, _c); + +IMPLEMENT_OP_OTHER(_subo) +#undef CFUNC + +#define FUNC(_m, _a, _f) gsl_matrix_scale(_m, _f) +#define CFUNC(_m, _a, _f) gsl_matrix_complex_scale(_m, gsl_complex_rect(_f, 0)) +IMPLEMENT_OP_FLOAT(_mulf) +#undef FUNC +#undef CFUNC + +#define CFUNC(_m, _a, _c) gsl_matrix_complex_scale(_m, _c) +IMPLEMENT_OP_OTHER(_mulo) +#undef CFUNC + +#undef MAKE_MATRIX +#define MAKE_MATRIX(_a) MATRIX_copy(_a) + +#define FUNC(_m, _a, _b) gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, _a, _b, 0.0, _m); +#define CFUNC(_m, _a, _b) gsl_blas_zgemm(CblasNoTrans, CblasNoTrans, COMPLEX_one, _a, _b, COMPLEX_zero, _m); +IMPLEMENT_OP(_mul) +#undef FUNC +#undef CFUNC + +#define FUNC(_m, _a, _b) \ +{ \ + gsl_matrix *inv = matrix_invert(_b, FALSE); \ + if (!inv) \ + return NULL; \ + gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, _a, inv, 0.0, _m); \ + gsl_matrix_free(inv); \ +} + +#define CFUNC(_m, _a, _b) \ +{ \ + gsl_matrix_complex *inv = matrix_invert(_b, TRUE); \ + if (!inv) \ + { \ + GB.Error(GB_ERR_ZERO); \ + return NULL; \ + } \ + gsl_blas_zgemm(CblasNoTrans, CblasNoTrans, COMPLEX_one, _a, inv, COMPLEX_zero, _m); \ + gsl_matrix_complex_free(inv); \ +} + +IMPLEMENT_OP(_div) +#undef FUNC +#undef CFUNC + +static CMATRIX *_divf(CMATRIX *a, double f, bool invert) +{ + bool complex = COMPLEX(a); + CMATRIX *m; + + if (invert) + { + void *inv = matrix_invert(MAT(a), complex); + + if (!inv) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + + m = MATRIX_create_from(inv, complex); + } + else + { + if (f == 0.0) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + + f = 1 / f; + m = MATRIX_make(a); + } + + if (complex) + gsl_matrix_complex_scale(CMAT(m), gsl_complex_rect(f, 0)); + else + gsl_matrix_scale(MAT(m), f); + + return m; +} + +static CMATRIX *_divo(CMATRIX *a, void *b, bool invert) +{ + bool complex = COMPLEX(a); + CMATRIX *m; + gsl_complex c; + + if (!GB.Is(b, CLASS_Complex)) + return NULL; + + c = ((CCOMPLEX *)b)->number; + + if (invert) + { + void *inv = matrix_invert(MAT(a), complex); + + if (!inv) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + + m = MATRIX_create_from(inv, complex); + } + else + { + if (GSL_REAL(c) == 0 && GSL_IMAG(c) == 0) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + + c = gsl_complex_inverse(c); + m = MATRIX_make(a); + } + + MATRIX_ensure_complex(m); + gsl_matrix_complex_scale(CMAT(m), c); + + return m; +} + +static int _equal(CMATRIX *a, CMATRIX *b) +{ + if (WIDTH(a) != WIDTH(b) || HEIGHT(a) != HEIGHT(b)) + return FALSE; + + if (COMPLEX(a) || COMPLEX(b)) + { + MATRIX_ensure_complex(a); + MATRIX_ensure_complex(b); + return gsl_matrix_complex_equal(CMAT(a), CMAT(b)); + } + else + return gsl_matrix_equal(MAT(a), MAT(b)); +} + +static int _equalf(CMATRIX *a, double f) +{ + bool result; + + if (COMPLEX(a)) + { + if (f == 0.0) + return gsl_matrix_complex_isnull(CMAT(a)); + + gsl_matrix_complex *m = gsl_matrix_complex_alloc(WIDTH(a), HEIGHT(a)); + gsl_matrix_complex_set_identity(m); + gsl_matrix_complex_scale(m, gsl_complex_rect(f, 0)); + result = gsl_matrix_complex_equal(CMAT(a), m); + gsl_matrix_complex_free(m); + } + else + { + if (f == 0.0) + return gsl_matrix_isnull(MAT(a)); + + gsl_matrix *m = gsl_matrix_alloc(WIDTH(a), HEIGHT(a)); + gsl_matrix_set_identity(m); + gsl_matrix_scale(m, f); + result = gsl_matrix_equal(MAT(a), m); + gsl_matrix_free(m); + } + + return result; +} + +static int _equalo(CMATRIX *a, void *b) +{ + bool result; + CCOMPLEX *c; + + if (!GB.Is(b, CLASS_Complex)) + return -1; + + c = (CCOMPLEX *)b; + + if (GSL_IMAG(c->number) == 0.0) + return _equalf(a, GSL_REAL(c->number)); + + if (!COMPLEX(a)) + return FALSE; + + gsl_matrix_complex *m = gsl_matrix_complex_alloc(WIDTH(a), HEIGHT(a)); + gsl_matrix_complex_set_identity(m); + gsl_matrix_complex_scale(m, c->number); + result = gsl_matrix_complex_equal(CMAT(a), m); + gsl_matrix_complex_free(m); + return result; +} + +static CMATRIX *_neg(CMATRIX *a) +{ + CMATRIX *m = MATRIX_make(a); + matrix_negative(m->matrix, m->complex); + return m; +} + +static CMATRIX *_powi(CMATRIX *m, int n) +{ + if (n == 1) + return m; + + CMATRIX *m2 = _mul(m, m, FALSE); + CMATRIX *r; + + if ((n & 1) == 0) + { + n /= 2; + if (n > 1) + r = _powi(m2, n); + else + r = m2; + } + else + { + n /= 2; + if (n > 1) + r = _powi(m2, n); + else + r = m2; + + m2 = _mul(r, m, FALSE); + GB.Unref(POINTER(&r)); + r = m2; + } + + GB.Unref(POINTER(&m)); + return r; +} + +static CMATRIX *_powf(CMATRIX *a, double f, bool invert) +{ + if (invert || f != (double)(int)f) + return NULL; + + CMATRIX *m; + int n = (int)f; + + if (n == 0) + { + m = MATRIX_make(a); + if (COMPLEX(m)) + gsl_matrix_complex_set_identity(CMAT(m)); + else + gsl_matrix_set_identity(MAT(m)); + } + else if (n == 1) + { + m = a; + } + else if (n > 1) + { + m = _powi(MATRIX_copy(a), n); + } + else if (n < 0) + { + void *inv = matrix_invert(a->matrix, COMPLEX(a)); + if (inv == NULL) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + + m = _powi(MATRIX_create_from(inv, COMPLEX(a)), (-n)); + } + + return m; +} + +static GB_OPERATOR_DESC _operator = +{ + .equal = (void *)_equal, + .equalf = (void *)_equalf, + .equalo = (void *)_equalo, + .add = (void *)_add, + .addf = (void *)_addf, + .addo = (void *)_addo, + .sub = (void *)_sub, + .subf = (void *)_subf, + .subo = (void *)_subo, + .mul = (void *)_mul, + .mulf = (void *)_mulf, + .mulo = (void *)_mulo, + .div = (void *)_div, + .divf = (void *)_divf, + .divo = (void *)_divo, + .powf = (void *)_powf, + .neg = (void *)_neg +}; + +//---- Conversions ---------------------------------------------------------- + +static char *_to_string(CMATRIX *_object, bool local) +{ + char *result = NULL; + int i, j; + int w = WIDTH(THIS); + int h = HEIGHT(THIS); + char *str; + int len; + + result = GB.AddChar(result, '['); + + for (i = 0; i < h; i++) + { + if (i) + { + if (!local) + result = GB.AddChar(result, ','); + } + + result = GB.AddChar(result, '['); + + for (j = 0; j < w; j++) + { + if (j) + result = GB.AddChar(result, local ? ' ' : ','); + + if (!COMPLEX(THIS)) + { + GB.NumberToString(local, gsl_matrix_get(MAT(THIS), i, j), NULL, &str, &len); + result = GB.AddString(result, str, len); + } + else + { + str = COMPLEX_to_string(gsl_matrix_complex_get(CMAT(THIS), i, j), local); + result = GB.AddString(result, str, GB.StringLength(str)); + GB.FreeString(&str); + } + } + + result = GB.AddChar(result, ']'); + } + + result = GB.AddChar(result, ']'); + + return result; +} + +static bool _convert(CMATRIX *_object, GB_TYPE type, GB_VALUE *conv) +{ + if (THIS) + { + if (!COMPLEX(THIS)) + { + switch (type) + { + /*case GB_T_FLOAT: + conv->_float.value = gsl_blas_dnrm2(MAT(THIS)); + return FALSE; + + case GB_T_SINGLE: + conv->_single.value = gsl_blas_dnrm2(MAT(THIS)); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + conv->_integer.value = gsl_blas_dnrm2(MAT(THIS)); + return FALSE; + + case GB_T_LONG: + conv->_long.value = gsl_blas_dnrm2(MAT(THIS)); + return FALSE;*/ + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = _to_string(THIS, type == GB_T_CSTRING); + conv->_string.value.start = 0; + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); + return FALSE; + + default: + return TRUE; + } + } + else + { + switch (type) + { + /*case GB_T_FLOAT: + conv->_float.value = gsl_blas_dznrm2(CMAT(THIS)); + return FALSE; + + case GB_T_SINGLE: + conv->_single.value = gsl_blas_dznrm2(CMAT(THIS)); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + conv->_integer.value = gsl_blas_dznrm2(CMAT(THIS)); + return FALSE; + + case GB_T_LONG: + conv->_long.value = gsl_blas_dznrm2(CMAT(THIS)); + return FALSE;*/ + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = _to_string(THIS, type == GB_T_CSTRING); + conv->_string.value.start = 0; + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); + return FALSE; + + default: + return TRUE; + } + } + } + else if (type >= GB_T_OBJECT) + { + /*if (type == CLASS_Complex) + { + CCOMPLEX *c = (CCOMPLEX *)conv->_object.value; + CMATRIX *m = MATRIX_create(2, 2, FALSE, FALSE); + + gsl_matrix_set(MAT(m), 0, 0, GSL_REAL(c->number)); + gsl_matrix_set(MAT(m), 1, 1, GSL_REAL(c->number)); + gsl_matrix_set(MAT(m), 0, 1, -GSL_IMAG(c->number)); + gsl_matrix_set(MAT(m), 1, 0, GSL_IMAG(c->number)); + + conv->_object.value = m; + return FALSE; + } + else*/ if (GB.Is(conv->_object.value, CLASS_Array)) + { + GB_ARRAY array = (GB_ARRAY)conv->_object.value; + GB_ARRAY array2; + int height = GB.Array.Count(array); + int width = 0; + GB_TYPE atype = GB.Array.Type(array); + GB_TYPE atype2; + int i, j, w; + GB_VALUE temp; + void *data; + CMATRIX *m; + CCOMPLEX *c; + bool complex; + + if (atype >= GB_T_OBJECT) + { + complex = FALSE; + + for (i = 0; i < height; i++) + { + data = GB.Array.Get(array, i); + array2 = *((void **)data); + if (!array2 || !GB.Is(array2, CLASS_Array)) + return TRUE; + + w = GB.Array.Count(array2); + if (w > width) + width = w; + + atype2 = GB.Array.Type(array2); + + if (atype2 == GB_T_VARIANT || atype2 == CLASS_Complex) + complex = TRUE; + else if (!(atype2 > GB_T_BOOLEAN && atype2 <= GB_T_FLOAT)) + return TRUE; + } + + //fprintf(stderr, "create: %d %d %d\n", width, height, complex); + m = MATRIX_create(width, height, complex, TRUE); + + for (i = 0; i < height; i++) + { + array2 = *((void **)GB.Array.Get(array, i)); + atype2 = GB.Array.Type(array2); + w = GB.Array.Count(array2); + + if (atype2 > GB_T_BOOLEAN && atype2 <= GB_T_FLOAT) + { + for (j = 0; j < w; j++) + { + data = GB.Array.Get(array2, j); + GB.ReadValue(&temp, data, atype2); + GB.Conv(&temp, GB_T_FLOAT); + if (complex) + gsl_matrix_complex_set(CMAT(m), i, j, gsl_complex_rect(temp._float.value, 0)); + else + gsl_matrix_set(MAT(m), i, j, temp._float.value); + } + } + else if (atype2 == GB_T_VARIANT) + { + for (j = 0; j < w; j++) + { + GB.ReadValue(&temp, GB.Array.Get(array2, j), atype2); + GB.BorrowValue(&temp); + GB.Conv(&temp, CLASS_Complex); + c = temp._object.value; + if (c) + gsl_matrix_complex_set(CMAT(m), i, j, c->number); + else + gsl_matrix_complex_set(CMAT(m), i, j, COMPLEX_zero); + GB.ReleaseValue(&temp); + } + } + else if (atype2 == CLASS_Complex) + { + for (j = 0; j < w; j++) + { + c = *((CCOMPLEX **)GB.Array.Get(array2, j)); + if (c) + gsl_matrix_complex_set(CMAT(m), i, j, c->number); + else + gsl_matrix_complex_set(CMAT(m), i, j, COMPLEX_zero); + } + } + } + + conv->_object.value = m; + return FALSE; + } + } + } + /*else if (type > GB_T_BOOLEAN && type <= GB_T_FLOAT) + { + CMATRIX *m = MATRIX_create(2, 2, FALSE, TRUE); + double value; + + if (type == GB_T_FLOAT) + value = conv->_float.value; + else if (type == GB_T_SINGLE) + value = conv->_single.value; + else + value = conv->_integer.value; + + gsl_matrix_set(MAT(m), 0, 0, value); + gsl_matrix_set(MAT(m), 1, 1, value); + + conv->_object.value = m; + return FALSE; + }*/ + + return TRUE; +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Matrix_new, GB_INTEGER height; GB_INTEGER width; GB_BOOLEAN complex) + + bool complex = VARGOPT(complex, FALSE); + int h = VARGOPT(height, 2); + int w = VARGOPT(width, 2); + + if (h < 1) h = 1; + if (w < 1) w = 1; + + THIS->complex = complex; + + if (!complex) + THIS->matrix = gsl_matrix_calloc(h, w); + else + THIS->matrix = gsl_matrix_complex_calloc(h, w); + +END_METHOD + + +BEGIN_METHOD_VOID(Matrix_free) + + if (!COMPLEX(THIS)) + gsl_matrix_free(MAT(THIS)); + else + gsl_matrix_complex_free(CMAT(THIS)); + +END_METHOD + + +BEGIN_PROPERTY(Matrix_Width) + + GB.ReturnInteger(WIDTH(THIS)); + +END_PROPERTY + + +BEGIN_PROPERTY(Matrix_Height) + + GB.ReturnInteger(HEIGHT(THIS)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Matrix_Copy) + + GB.ReturnObject(MATRIX_copy(THIS)); + +END_METHOD + + +BEGIN_METHOD(Matrix_get, GB_INTEGER i; GB_INTEGER j) + + int w = WIDTH(THIS), h = HEIGHT(THIS); + int i = VARG(i), j = VARG(j); + + if (i < 0 || i >= h || j < 0 || j >= w) + { + GB.Error(GB_ERR_BOUND); + return; + } + + if (!COMPLEX(THIS)) + GB.ReturnFloat(gsl_matrix_get(MAT(THIS), i, j)); + else + GB.ReturnObject(COMPLEX_create(gsl_matrix_complex_get(CMAT(THIS), i, j))); + + GB.ReturnConvVariant(); + +END_METHOD + + +BEGIN_METHOD(Matrix_put, GB_VARIANT value; GB_INTEGER i; GB_INTEGER j) + + int w = WIDTH(THIS), h = HEIGHT(THIS); + int i = VARG(i), j = VARG(j); + GB_VALUE *value = (GB_VALUE *)ARG(value); + int type; + COMPLEX_VALUE cv; + + if (i < 0 || i >= h || j < 0 || j >= w) + { + GB.Error(GB_ERR_BOUND); + return; + } + + type = COMPLEX_get_value(value, &cv); + + if (type == CGV_ERR) + return; + + if (type == CGV_COMPLEX) + { + MATRIX_ensure_complex(THIS); + gsl_matrix_complex_set(CMAT(THIS), i, j, cv.z); + } + else + { + if (COMPLEX(THIS)) + gsl_matrix_complex_set(CMAT(THIS), i, j, cv.z); + else + gsl_matrix_set(MAT(THIS), i, j, cv.x); + } + +END_METHOD + + +BEGIN_PROPERTY(Matrix_Handle) + + GB.ReturnPointer(THIS->matrix); + +END_PROPERTY + + +BEGIN_METHOD(Matrix_Identity, GB_INTEGER width; GB_INTEGER height; GB_BOOLEAN complex) + + GB.ReturnObject(MATRIX_identity(VARGOPT(width, 2), VARGOPT(height, 2), VARGOPT(complex, FALSE))); + +END_METHOD + + +BEGIN_METHOD(Matrix_Row, GB_INTEGER row) + + int row = VARG(row); + + if (row < 0 || row >= HEIGHT(THIS)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + bool complex = COMPLEX(THIS); + CVECTOR *v = VECTOR_create(WIDTH(THIS), complex, FALSE); + + if (complex) + gsl_matrix_complex_get_row(CVEC(v), CMAT(THIS), row); + else + gsl_matrix_get_row(VEC(v), MAT(THIS), row); + + GB.ReturnObject(v); + +END_METHOD + +BEGIN_METHOD(Matrix_Column, GB_INTEGER column) + + int column = VARG(column); + + if (column < 0 || column >= WIDTH(THIS)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + bool complex = COMPLEX(THIS); + CVECTOR *v = VECTOR_create(HEIGHT(THIS), complex, FALSE); + + if (complex) + gsl_matrix_complex_get_col(CVEC(v), CMAT(THIS), column); + else + gsl_matrix_get_col(VEC(v), MAT(THIS), column); + + GB.ReturnObject(v); + +END_METHOD + +BEGIN_METHOD(Matrix_SetRow, GB_INTEGER row; GB_OBJECT vector) + + int row = VARG(row); + + if (row < 0 || row >= HEIGHT(THIS)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + CVECTOR *v = (CVECTOR *)VARG(vector); + + if (GB.CheckObject(v)) + return; + + if (SIZE(v) != WIDTH(THIS)) + { + GB.Error("Vector size does not match matrix width"); + return; + } + + bool complex = COMPLEX(THIS); + + if (complex) + { + VECTOR_ensure_complex(v); + gsl_matrix_complex_set_row(CMAT(THIS), row, CVEC(v)); + } + else + { + if (VECTOR_ensure_not_complex(v)) + { + GB.Error(GB_ERR_TYPE, "Float", "Complex"); + return; + } + gsl_matrix_set_row(MAT(THIS), row, VEC(v)); + } + +END_METHOD + +BEGIN_METHOD(Matrix_SetColumn, GB_INTEGER column; GB_OBJECT vector) + + int column = VARG(column); + + if (column < 0 || column >= WIDTH(THIS)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + CVECTOR *v = (CVECTOR *)VARG(vector); + + if (GB.CheckObject(v)) + return; + + if (SIZE(v) != HEIGHT(THIS)) + { + GB.Error("Vector size does not match matrix height"); + return; + } + + bool complex = COMPLEX(THIS); + + if (complex) + { + VECTOR_ensure_complex(v); + gsl_matrix_complex_set_col(CMAT(THIS), column, CVEC(v)); + } + else + { + if (VECTOR_ensure_not_complex(v)) + { + GB.Error(GB_ERR_TYPE, "Float", "Complex"); + return; + } + gsl_matrix_set_col(MAT(THIS), column, VEC(v)); + } + +END_METHOD + +BEGIN_METHOD_VOID(Matrix_Determinant) + + COMPLEX_VALUE cv; + + if (matrix_determinant(THIS, &cv)) + { + GB.Error("Matrix is not square"); + return; + } + + if (COMPLEX(THIS)) + GB.ReturnObject(COMPLEX_create(cv.z)); + else + GB.ReturnFloat(cv.x); + + GB.ReturnConvVariant(); + +END_METHOD + + +BEGIN_METHOD(Matrix_call, GB_OBJECT vector) + + CVECTOR *v = VARG(vector); + CVECTOR *result; + + if (GB.CheckObject(v)) + return; + + if (COMPLEX(THIS) || v->complex) + { + MATRIX_ensure_complex(THIS); + VECTOR_ensure_complex(v); + result = VECTOR_create(SIZE(v), TRUE, FALSE); + gsl_blas_zgemv(CblasNoTrans, COMPLEX_one, CMAT(THIS), CVEC(v), COMPLEX_zero, CVEC(result)); + GB.ReturnObject(result); + } + else + { + result = VECTOR_create(SIZE(v), FALSE, FALSE); + gsl_blas_dgemv(CblasNoTrans, 1.0, MAT(THIS), VEC(v), 0.0, VEC(result)); + GB.ReturnObject(result); + } + +END_METHOD + + +BEGIN_METHOD_VOID(Matrix_Transpose) + + if (!COMPLEX(THIS)) + { + gsl_matrix *m = gsl_matrix_alloc(WIDTH(THIS), HEIGHT(THIS)); + gsl_matrix_transpose_memcpy(m, MAT(THIS)); + GB.ReturnObject(MATRIX_create_from(m, FALSE)); + } + else + { + gsl_matrix_complex *m = gsl_matrix_complex_alloc(WIDTH(THIS), HEIGHT(THIS)); + gsl_matrix_complex_transpose_memcpy(m, CMAT(THIS)); + GB.ReturnObject(MATRIX_create_from(m, TRUE)); + } + +END_METHOD + + +BEGIN_METHOD_VOID(Matrix_Conjugate) + + CMATRIX *m = MATRIX_copy(THIS); + + if (COMPLEX(THIS)) + { + int i, j; + + for (i = 0; i < HEIGHT(m); i++) + for (j = 0; j < WIDTH(m); j++) + gsl_matrix_complex_set(CMAT(m), i, j, gsl_complex_conjugate(gsl_matrix_complex_get(CMAT(m), i, j))); + } + + GB.ReturnObject(m); + +END_METHOD + + +BEGIN_METHOD_VOID(Matrix_Invert) + + void *m = matrix_invert(THIS->matrix, COMPLEX(THIS)); + + if (!m) + GB.ReturnNull(); + else + GB.ReturnObject(MATRIX_create_from(m, COMPLEX(THIS))); + +END_METHOD + + +BEGIN_METHOD(Matrix_ToString, GB_BOOLEAN local) + + GB.ReturnString(GB.FreeStringLater(_to_string(THIS, VARGOPT(local, FALSE)))); + +END_METHOD + + +//--------------------------------------------------------------------------- + +GB_DESC MatrixDesc[] = +{ + GB_DECLARE("Matrix", sizeof(CMATRIX)), + + GB_METHOD("_new", NULL, Matrix_new, "[(Width)i(Height)i(Complex)b]"), + GB_METHOD("_free", NULL, Matrix_free, NULL), + //GB_STATIC_METHOD("_call", "Vector", Matrix_call, "(Value)f."), + GB_METHOD("Copy", "Matrix", Matrix_Copy, NULL), + GB_METHOD("ToString", "s", Matrix_ToString, "[(Local)b]"), + + GB_STATIC_METHOD("Identity", "Matrix", Matrix_Identity, "[(Width)i(Height)i(Complex)b]"), + + GB_PROPERTY_READ("Width", "i", Matrix_Width), + GB_PROPERTY_READ("Height", "i", Matrix_Height), + GB_PROPERTY_READ("Handle", "p", Matrix_Handle), + + GB_METHOD("Det", "v", Matrix_Determinant, NULL), + + GB_METHOD("_get", "v", Matrix_get, "(I)i(J)i"), + GB_METHOD("_put", NULL, Matrix_put, "(Value)v(I)i(J)i"), + + GB_METHOD("_call", "Vector", Matrix_call, "(Vector)Vector"), + + //GB_METHOD("Equal", "b", Matrix_Equal, "(Matrix)Matrix;"), + + GB_METHOD("Row", "Vector", Matrix_Row, "(Row)i"), + GB_METHOD("Column", "Vector", Matrix_Column, "(Column)i"), + GB_METHOD("SetRow", NULL, Matrix_SetRow, "(Row)i(Vector)Vector;"), + GB_METHOD("SetColumn", NULL, Matrix_SetColumn, "(Column)i(Vector)Vector;"), + + //GB_METHOD("Scale", "Matrix", Matrix_Scale, "(Value)v"), + GB_METHOD("Trans", "Matrix", Matrix_Transpose, NULL), + GB_METHOD("Conj", "Matrix", Matrix_Conjugate, NULL), + GB_METHOD("Inv", "Matrix", Matrix_Invert, NULL), + + GB_INTERFACE("_convert", &_convert), + GB_INTERFACE("_operator", &_operator), + + GB_END_DECLARE +}; diff --git a/gb.gsl/src/c_matrix.h b/gb.gsl/src/c_matrix.h new file mode 100644 index 00000000..ded6edee --- /dev/null +++ b/gb.gsl/src/c_matrix.h @@ -0,0 +1,44 @@ +/*************************************************************************** + + c_matrix.h + + gb.gsl component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_MATRIX_H +#define __C_MATRIX_H + +#include "main.h" + +#ifndef __C_MATRIX_C +extern GB_DESC MatrixDesc[]; +#endif + +typedef + struct + { + GB_BASE ob; + void *matrix; + bool complex; + } + CMATRIX; + +#endif /* __C_MATRIX_H */ diff --git a/gb.gsl/src/c_newtonpolynomial.c b/gb.gsl/src/c_newtonpolynomial.c new file mode 100644 index 00000000..5c48d0f7 --- /dev/null +++ b/gb.gsl/src/c_newtonpolynomial.c @@ -0,0 +1,53 @@ +/*************************************************************************** + + c_newtonpolynomial.c + + gb.gsl component + + (c) 2012 Randall Morgan + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ +/*========================================================================= + === NOTE THIS IS SIMPLE LEARNING CODE === + AND NOTHING USEFUL EXISTS HERE YET +==========================================================================*/ + +#define __C_GSL_NEWTONPOLYNOMIAL_C + +#include "c_newtonpolynomial.h" + + +#define THIS ((CNEWTONPOLYNOMIAL *)_object) + +static CNEWTONPOLYNOMIAL *create_newtonplynomial() +{ + return (CNEWTONPOLYNOMIAL *)GB.New(GB.FindClass("NetonPolynomial"), NULL, NULL); +} + + + +/************************************************** + Describe Class properties and methods to Gambas +**************************************************/ +GB_DESC CNetwonPolynomial[] = +{ + GB_DECLARE("NewtonPolynomial", sizeof(CNEWTONPOLYNOMIAL)), + + GB_END_DECLARE +}; + diff --git a/gb.gsl/src/c_newtonpolynomial.h b/gb.gsl/src/c_newtonpolynomial.h new file mode 100644 index 00000000..cfc4055f --- /dev/null +++ b/gb.gsl/src/c_newtonpolynomial.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + c_newtonpolynomial.h + + gb.gsl component + + (c) 2012 Randall Morgan + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_GSL_NEWTONPOLYNOMIAL_H +#define __C_GSL_NEWTONPOLYNOMIAL_H + +#include "gambas.h" +#include +#include + + +GB_INTERFACE GB EXPORT; + +extern GB_DESC CNewtonPolynomialDesc[]; + +typedef + struct { + GB_BASE ob; + double *dd; + double *xa; + int len; + } + CNEWTONPOLYNOMIAL; + + +#endif /* __C_GSL_NEWTONPOLYNOMIAL_H */ diff --git a/gb.gsl/src/c_polynomial.c b/gb.gsl/src/c_polynomial.c new file mode 100644 index 00000000..cf64908d --- /dev/null +++ b/gb.gsl/src/c_polynomial.c @@ -0,0 +1,914 @@ +/*************************************************************************** + + c_polynomial.c + + gb.gsl component + + (c) 2012 Randall Morgan + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_POLYNOMIAL_C + +#include "c_complex.h" +#include "c_vector.h" +#include "c_polynomial.h" + +#define THIS ((CPOLYNOMIAL *)_object) +#define DATA(_p) ((double *)(_p)->data) +#define CDATA(_p) ((gsl_complex *)(_p)->data) +#define COUNT(_p) ((_p)->size) +#define COMPLEX(_p) ((_p)->complex) + +//---- Utility methods ------------------------------------------------------ + +static CPOLYNOMIAL *POLYNOMIAL_create(int size, bool complex) +{ + CPOLYNOMIAL *p = (CPOLYNOMIAL *)GB.Create(CLASS_Polynomial, NULL, NULL); + + GB.NewArray(POINTER(&p->data), complex ? sizeof(gsl_complex) : sizeof(double), size); + + p->size = size; + p->complex = complex; + + return p; +} + +static CPOLYNOMIAL *POLYNOMIAL_copy(CPOLYNOMIAL *_object) +{ + CPOLYNOMIAL *p = POLYNOMIAL_create(COUNT(THIS), COMPLEX(THIS)); + memcpy(p->data, THIS->data, THIS->size * (COMPLEX(THIS) ? sizeof(gsl_complex) : sizeof(double))); + return p; +} + +#define POLYNOMIAL_make(_a) (((_a)->ob.ref <= 1) ? (_a) : POLYNOMIAL_copy(_a)) + +static int get_degree(CPOLYNOMIAL *_object) +{ + int i; + + if (COMPLEX(THIS)) + { + gsl_complex *d = CDATA(THIS); + + for (i = COUNT(THIS) - 1; i >= 0; i--) + { + if (GSL_REAL(d[i]) != 0.0 || GSL_IMAG(d[i]) != 0.0) + return i; + } + } + else + { + double *d = DATA(THIS); + + for (i = COUNT(THIS) - 1; i >= 0; i--) + { + if (d[i] != 0.0) + return i; + } + } + + return 0; +} + +static void ensure_size(CPOLYNOMIAL *_object, int size) +{ + if (size > COUNT(THIS)) + { + GB.Insert(POINTER(&THIS->data), -1, size - COUNT(THIS)); + THIS->size = size; + } +} + +static CPOLYNOMIAL *POLYNOMIAL_make_size(CPOLYNOMIAL *a, int min_size) +{ + if (a->size >= min_size) + return POLYNOMIAL_make(a); + + a = POLYNOMIAL_copy(a); + ensure_size(a, min_size); + return a; +} + +static void ensure_complex(CPOLYNOMIAL *_object) +{ + gsl_complex *d; + int size, i; + + if (COMPLEX(THIS)) + return; + + if (THIS->data) + { + size = COUNT(THIS); + GB.NewArray(POINTER(&d), sizeof(gsl_complex), size); + for (i = 0; i < size; i++) + d[i].dat[0] = DATA(THIS)[i]; + GB.FreeArray(POINTER(&THIS->data)); + THIS->data = d; + } + + THIS->complex = TRUE; +} + +static bool ensure_not_complex(CPOLYNOMIAL *_object) +{ + gsl_complex *cd; + double *d; + int size, i; + + if (!COMPLEX(THIS)) + return FALSE; + + if (THIS->data) + { + size = COUNT(THIS); + cd = CDATA(THIS); + + for (i = 0; i < size; i++) + { + if (GSL_IMAG(cd[i]) != 0.0) + return TRUE; + } + + GB.NewArray(POINTER(&d), sizeof(double), size); + + for (i = 0; i < size; i++) + d[i] = GSL_REAL(cd[i]); + + GB.FreeArray(POINTER(&THIS->data)); + THIS->data = d; + } + + THIS->complex = FALSE; + return FALSE; +} + +static void poly_negative(CPOLYNOMIAL *_object) +{ + int i; + + if (COMPLEX(THIS)) + { + for (i = 0; i < COUNT(THIS); i++) + DATA(THIS)[i] = (- DATA(THIS)[i]); + } + else + { + for (i = 0; i < COUNT(THIS); i++) + CDATA(THIS)[i] = gsl_complex_negative(CDATA(THIS)[i]); + } +} + +//---- Arithmetic operators ------------------------------------------------- + +static CPOLYNOMIAL *_addf(CPOLYNOMIAL *a, double f, bool invert) +{ + CPOLYNOMIAL *p = POLYNOMIAL_make(a); + + DATA(p)[0] += f; + return p; +} + +static CPOLYNOMIAL *_add(CPOLYNOMIAL *a, CPOLYNOMIAL *b, bool invert) +{ + int da = get_degree(a); + int db = get_degree(b); + int d = Max(da, db); + int dm = Min(d, db); + int i; + + CPOLYNOMIAL *p = POLYNOMIAL_make_size(a, d + 1); + + if (COMPLEX(a) || COMPLEX(b)) + { + ensure_complex(p); + ensure_complex(b); + + for (i = 0; i <= dm; i++) + CDATA(p)[i] = gsl_complex_add(CDATA(p)[i], CDATA(b)[i]); + } + else + { + for (i = 0; i <= dm; i++) + DATA(p)[i] += DATA(b)[i]; + } + + return p; +} + +static CPOLYNOMIAL *op_array(CPOLYNOMIAL *a, void *b, bool invert, CPOLYNOMIAL *(*func)(CPOLYNOMIAL *, CPOLYNOMIAL *, bool)) +{ + GB_VALUE conv; + bool err; + CPOLYNOMIAL *p; + + conv._object.type = (GB_TYPE)GB.GetClass(b); + conv._object.value = b; + + GB.Ref(b); + err = GB.Conv(&conv, CLASS_Polynomial); + + if (!err) + { + if (invert) + { + GB.Ref(conv._object.value); + p = (*func)(conv._object.value, a, FALSE); + GB.Unref(&conv._object.value); + } + else + { + p = (*func)(a, conv._object.value, FALSE); + } + + GB.Unref(&conv._object.value); + + return p; + } + else + return NULL; +} + +static CPOLYNOMIAL *_addo(CPOLYNOMIAL *a, void *b, bool invert) +{ + CPOLYNOMIAL *p; + if (GB.Is(b, CLASS_Complex)) + { + p = POLYNOMIAL_make(a); + + ensure_complex(p); + CDATA(p)[0] = gsl_complex_add(CDATA(p)[0], ((CCOMPLEX *)b)->number); + return p; + } + else if (GB.Is(b, CLASS_Array)) + { + return op_array(a, b, invert, _add); + } + + return NULL; +} + +static CPOLYNOMIAL *_subf(CPOLYNOMIAL *a, double f, bool invert) +{ + CPOLYNOMIAL *p = POLYNOMIAL_make(a); + + if (invert) + poly_negative(p); + else + f = -f; + + DATA(p)[0] += f; + return p; +} + +static CPOLYNOMIAL *_sub(CPOLYNOMIAL *a, CPOLYNOMIAL *b, bool invert) +{ + int da = get_degree(a); + int db = get_degree(b); + int d = Max(da, db); + int dm = Min(d, db); + int i; + + CPOLYNOMIAL *p = POLYNOMIAL_make_size(a, d + 1); + + if (COMPLEX(a) || COMPLEX(b)) + { + ensure_complex(p); + ensure_complex(b); + + for (i = 0; i <= dm; i++) + CDATA(p)[i] = gsl_complex_sub(CDATA(p)[i], CDATA(b)[i]); + } + else + { + for (i = 0; i <= dm; i++) + DATA(p)[i] -= DATA(b)[i]; + } + + return p; +} + +static CPOLYNOMIAL *_subo(CPOLYNOMIAL *a, void *b, bool invert) +{ + CPOLYNOMIAL *p; + + if (GB.Is(b, CLASS_Complex)) + { + p = POLYNOMIAL_make(a); + + if (invert) + { + poly_negative(p); + ensure_complex(p); + CDATA(p)[0] = gsl_complex_add(CDATA(p)[0], ((CCOMPLEX *)b)->number); + } + else + { + ensure_complex(p); + CDATA(p)[0] = gsl_complex_sub(CDATA(p)[0], ((CCOMPLEX *)b)->number); + } + + return p; + } + else if (GB.Is(b, CLASS_Array)) + { + return op_array(a, b, invert, _sub); + } + + return NULL; +} + +static CPOLYNOMIAL *_neg(CPOLYNOMIAL *a) +{ + CPOLYNOMIAL *p = POLYNOMIAL_make(a); + poly_negative(p); + return p; +} + +static int _equal(CPOLYNOMIAL *a, CPOLYNOMIAL *b, bool invert) +{ + int da = get_degree(a); + int db = get_degree(b); + int i; + + if (da != db) + return FALSE; + + if (COMPLEX(a) || COMPLEX(b)) + { + ensure_complex(a); + ensure_complex(b); + + for (i = 0; i <= da; i++) + if (GSL_REAL(CDATA(a)[i]) != GSL_REAL(CDATA(b)[i]) || GSL_IMAG(CDATA(a)[i]) != GSL_IMAG(CDATA(b)[i])) + return FALSE; + } + else + { + for (i = 0; i <= da; i++) + if (DATA(a)[i] != DATA(b)[i]) + return FALSE; + } + + return TRUE; +} + + +static GB_OPERATOR_DESC _operator = +{ + add: (void *)_add, + addf: (void *)_addf, + addo: (void *)_addo, + sub: (void *)_sub, + subf: (void *)_subf, + subo: (void *)_subo, + /*mul: (void *)_mul, + mulf: (void *)_mulf, + div: (void *)_div, + divf: (void *)_divf, + idivf: (void *)_idivf,*/ + equal: (void *)_equal, + /*equalf: (void *)_equalf, + abs: (void *)_abs,*/ + neg: (void *)_neg +}; + +//---- Conversions ---------------------------------------------------------- + +char *POLYNOMIAL_to_string(CPOLYNOMIAL *p, bool local) +{ + int i; + int size = COUNT(p); + char *result = NULL; + char *str; + int len; + char buffer[16]; + double re, im; + bool add = FALSE; + bool complex = COMPLEX(p); + gsl_complex c; + bool par; + + i = size; + while (i > 0) + { + i--; + + if (complex) + { + c = CDATA(p)[i]; + re = GSL_REAL(c); + im = GSL_IMAG(c); + } + else + { + re = DATA(p)[i]; + im = 0.0; + } + + if (re == 0.0 && im == 0.0) + continue; + + par = i > 0 && re != 0.0 && im != 0.0; + + if (!add) + add = TRUE; + else if (re > 0.0 || (re == 0.0 && im > 0.0) || par) + result = GB.AddChar(result, '+'); + + //l = GB.StringLength(result); + + if (par) + result = GB.AddChar(result, '('); + + if (i == 0 || re != 1.0 || im != 0.0) + { + if (re != 0.0) + { + if (re == -1.0 && i > 0) + result = GB.AddChar(result, '-'); + else + { + GB.NumberToString(local, re, NULL, &str, &len); + result = GB.AddString(result, str, len); + } + } + if (im != 0.0) + { + if (re != 0.0 && im > 0.0) + result = GB.AddChar(result, '+'); + + if (local && im == -1.0) + result = GB.AddChar(result, '-'); + else if (!local || im != 1.0) + { + GB.NumberToString(local, im, NULL, &str, &len); + result = GB.AddString(result, str, len); + } + result = GB.AddChar(result, 'i'); + } + } + + if (par) + result = GB.AddChar(result, ')'); + + if (i > 0) + { + if (!local && ((im == 0 && re != 0 && re != 1 && re != -1) || (im != 0))) + result = GB.AddChar(result, '*'); + result = GB.AddChar(result, 'x'); + if (i > 1) + { + result = GB.AddChar(result, '^'); + len = sprintf(buffer, "%d", i); + result = GB.AddString(result, buffer, len); + } + } + } + + if (!result) + result = GB.NewString("0", 1); + + return result; +} + +bool POLYNOMIAL_convert(CPOLYNOMIAL *a, GB_TYPE type, GB_VALUE *conv) +{ + if (a) + { + switch (type) + { + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = POLYNOMIAL_to_string(a, type == GB_T_CSTRING); + conv->_string.value.start = 0; + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); + return FALSE; + + default: + return TRUE; + } + } + else + { + double coef; + + switch(type) + { + case GB_T_FLOAT: coef = conv->_float.value; break; + case GB_T_SINGLE: coef = conv->_single.value; break; + case GB_T_INTEGER: case GB_T_SHORT: case GB_T_BYTE: coef = conv->_integer.value; + return FALSE; + + default: + if (type >= GB_T_OBJECT) + { + if (GB.Is(conv->_object.value, CLASS_Array)) + { + CPOLYNOMIAL *p; + CCOMPLEX *c; + GB_ARRAY array = (GB_ARRAY)conv->_object.value; + int size = GB.Array.Count(array); + int i; + GB_VALUE temp; + void *data; + GB_TYPE atype = GB.Array.Type(array); + + if (atype > GB_T_BOOLEAN && atype <= GB_T_FLOAT) + { + p = POLYNOMIAL_create(size, FALSE); + + for (i = 0; i < size; i++) + { + data = GB.Array.Get(array, i); + GB.ReadValue(&temp, data, atype); + GB.Conv(&temp, GB_T_FLOAT); + DATA(p)[i] = temp._float.value; + } + + conv->_object.value = p; + return FALSE; + } + else if (atype == GB_T_VARIANT) + { + p = POLYNOMIAL_create(size, TRUE); + + for (i = 0; i < size; i++) + { + GB.ReadValue(&temp, GB.Array.Get(array, i), atype); + GB.BorrowValue(&temp); + GB.Conv(&temp, CLASS_Complex); + c = temp._object.value; + CDATA(p)[i] = c->number; + GB.ReleaseValue(&temp); + } + + conv->_object.value = p; + return FALSE; + } + else if (atype == CLASS_Complex) + { + p = POLYNOMIAL_create(size, TRUE); + + for (i = 0; i < size; i++) + { + c = *(CCOMPLEX **)GB.Array.Get(array, i); + if (c) + CDATA(p)[i] = c->number; + else + CDATA(p)[i] = COMPLEX_zero; + } + + conv->_object.value = p; + return FALSE; + } + } + } + + return TRUE; + } + + a = POLYNOMIAL_create(1, FALSE); + *DATA(a) = coef; + conv->_object.value = a; + return FALSE; + } +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Polynomial_new, GB_INTEGER size; GB_BOOLEAN complex) + + bool complex = VARGOPT(complex, FALSE); + int size = VARGOPT(size, 0); + + GB.NewArray(POINTER(&THIS->data), complex ? sizeof(gsl_complex) : sizeof(double), size); + + THIS->size = size; + THIS->complex = complex; + +END_METHOD + + +BEGIN_METHOD_VOID(Polynomial_free) + + GB.FreeArray(POINTER(&THIS->data)); + +END_METHOD + + +BEGIN_PROPERTY(Polynomial_Count) + + GB.ReturnInteger(COUNT(THIS)); + +END_PROPERTY + + +BEGIN_PROPERTY(Polynomial_Degree) + + GB.ReturnInteger(get_degree(THIS)); + +END_PROPERTY + + +BEGIN_METHOD(Polynomial_ToString, GB_BOOLEAN local) + + GB.ReturnString(GB.FreeStringLater(POLYNOMIAL_to_string(THIS, VARGOPT(local, FALSE)))); + +END_METHOD + + +BEGIN_METHOD(Polynomial_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= COUNT(THIS)) + { + if (COMPLEX(THIS)) + GB.ReturnObject(COMPLEX_create(COMPLEX_zero)); + else + GB.ReturnFloat(0.0); + } + else + { + if (COMPLEX(THIS)) + GB.ReturnObject(COMPLEX_create(CDATA(THIS)[index])); + else + GB.ReturnFloat(DATA(THIS)[index]); + } + + GB.ReturnConvVariant(); + +END_METHOD + + +BEGIN_METHOD(Polynomial_put, GB_VARIANT value; GB_INTEGER index) + + int index = VARG(index); + GB_VALUE *value = (GB_VALUE *)ARG(value); + int type; + COMPLEX_VALUE cv; + + if (index < 0 || index > 65535) + { + GB.Error(GB_ERR_ARG); + return; + } + + type = COMPLEX_get_value(value, &cv); + + if (type == CGV_ERR) + return; + + ensure_size(THIS, index + 1); + + if (type == CGV_COMPLEX) + { + ensure_complex(THIS); + CDATA(THIS)[index] = cv.z; + } + else + { + if (COMPLEX(THIS)) + CDATA(THIS)[index] = cv.z; + else + DATA(THIS)[index] = cv.x; + } + +END_METHOD + + +BEGIN_METHOD(Polynomial_Eval, GB_VARIANT value) + + GB_VALUE *value = (GB_VALUE *)ARG(value); + int type; + COMPLEX_VALUE cv; + + type = COMPLEX_get_value(value, &cv); + if (type == CGV_ERR) + return; + + if (COMPLEX(THIS)) + { + GB.ReturnObject(COMPLEX_create(gsl_complex_poly_complex_eval(CDATA(THIS), COUNT(THIS), cv.z))); + } + else + { + if (type == CGV_COMPLEX) + GB.ReturnObject(COMPLEX_create(gsl_poly_complex_eval(DATA(THIS), COUNT(THIS), cv.z))); + else + GB.ReturnFloat(gsl_poly_eval(DATA(THIS), COUNT(THIS), cv.x)); + } + +END_METHOD + + +BEGIN_METHOD(Polynomial_Solve, GB_BOOLEAN want_croot) + + bool want_croot = VARGOPT(want_croot, FALSE); + bool complex = COMPLEX(THIS); + + double *data = DATA(THIS); + gsl_complex *cdata = CDATA(THIS); + + int nr = 0, i, dg, ret; + + GB_ARRAY result; + + double r[3]; + gsl_complex cr[3]; + + double *z = NULL; + gsl_poly_complex_workspace *work; + + double *root; + CCOMPLEX **croot; + + dg = get_degree(THIS) + 1; + + if (dg > 2) + { + if (complex && ensure_not_complex(THIS)) + { + GB.Error("Cannot solve polynomial with complex coefficients"); + return; + } + + data = DATA(THIS); + } + + switch(dg) + { + case 1: + GB.ReturnNull(); + return; + + case 2: + nr = 1; + if (complex) + { + cr[0] = gsl_complex_div(gsl_complex_negative(cdata[0]), cdata[1]); + if (!want_croot) + { + if (GSL_IMAG(cr[0]) == 0.0) + r[0] = GSL_REAL(cr[0]); + else + nr = 0; + } + } + else + { + r[0] = -data[0] / data[1]; + if (want_croot) + cr[0] = gsl_complex_rect(r[0], 0); + } + + break; + + case 3: + if (want_croot) + nr = gsl_poly_complex_solve_quadratic(data[2], data[1], data[0], &cr[0], &cr[1]); + else + nr = gsl_poly_solve_quadratic(data[2], data[1], data[0], &r[0], &r[1]); + + break; + + case 4: + if (data[3] == 1.0) + { + if (want_croot) + nr = gsl_poly_complex_solve_cubic(data[2], data[1], data[0], &cr[0], &cr[1], &cr[2]); + else + nr = gsl_poly_solve_cubic(data[2], data[1], data[0], &r[0], &r[1], &r[2]); + break; + } + + default: + work = gsl_poly_complex_workspace_alloc(dg); + GB.Alloc(POINTER(&z), sizeof(double) * (dg - 1) * 2); + + ret = gsl_poly_complex_solve(data, dg, work, z); + + gsl_poly_complex_workspace_free(work); + + if (ret != GSL_SUCCESS) + { + GB.Free(POINTER(&z)); + return; + } + + if (!want_croot) + { + nr = 0; + for (i = 0; i < (dg - 1); i++) + { + if (z[i * 2 + 1] == 0.0) + nr++; + } + } + else + nr = dg - 1; + } + + if (!want_croot) + { + GB.Array.New(&result, GB_T_FLOAT, nr); + + if (nr > 0) + root = (double *)GB.Array.Get(result, 0); + + if (!z) + { + if (nr >= 1) root[0] = r[0]; + if (nr >= 2) root[1] = r[1]; + if (nr >= 3) root[2] = r[2]; + } + else + { + if (nr > 0) + { + nr = 0; + + for (i = 0; i < (dg - 1); i++) + { + if (z[i * 2 + 1] == 0.0) + root[nr++] = z[i * 2]; + } + } + + GB.Free(POINTER(&z)); + } + } + else + { + GB.Array.New(&result, CLASS_Complex, nr); + + if (nr > 0) + croot = (CCOMPLEX **)GB.Array.Get(result, 0); + else + croot = NULL; + + if (!z) + { + if (nr >= 1) croot[0] = COMPLEX_create(cr[0]); + if (nr >= 2) croot[1] = COMPLEX_create(cr[1]); + if (nr >= 3) croot[2] = COMPLEX_create(cr[2]); + } + else + { + for (i = 0; i < nr; i++) + croot[i] = COMPLEX_create(gsl_complex_rect(z[i * 2], z[i * 2 + 1])); + + GB.Free(POINTER(&z)); + } + + for (i = 0; i < nr; i++) + GB.Ref(croot[i]); + } + + GB.ReturnObject(result); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC PolynomialDesc[] = +{ + GB_DECLARE("Polynomial", sizeof(CPOLYNOMIAL)), + + GB_METHOD("_new", NULL, Polynomial_new, "[(Size)i(Complex)b]"), + GB_METHOD("_free", NULL, Polynomial_free, NULL), + + GB_METHOD("ToString", "s", Polynomial_ToString, "[(Local)b]"), + + GB_PROPERTY_READ("Degree", "i", Polynomial_Degree), + GB_PROPERTY_READ("Count", "i", Polynomial_Count), + + GB_METHOD("_get", "v", Polynomial_get, "(Index)i"), + GB_METHOD("_put", NULL, Polynomial_put, "(Value)v(Index)i"), + + GB_METHOD("_call", "v", Polynomial_Eval, "(X)v"), + GB_METHOD("Eval", "v", Polynomial_Eval, "(X)v"), + GB_METHOD("Solve", "Array", Polynomial_Solve, "[(Complex)b]"), + + GB_INTERFACE("_operator", &_operator), + GB_INTERFACE("_convert", &POLYNOMIAL_convert), + + GB_END_DECLARE +}; diff --git a/gb.gsl/src/c_polynomial.h b/gb.gsl/src/c_polynomial.h new file mode 100644 index 00000000..e2105bdb --- /dev/null +++ b/gb.gsl/src/c_polynomial.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + c_polynomial.h + + gb.gsl component + + (c) 2012 Randall Morgan + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_POLYNOMIAL_H +#define __C_POLYNOMIAL_H + +#include "main.h" +#include +#include + +extern GB_INTERFACE GB EXPORT; + +extern GB_DESC PolynomialDesc[]; + +typedef + struct { + GB_BASE ob; + int size; + void *data; + bool complex; + } + CPOLYNOMIAL; + +bool POLYNOMIAL_convert(CPOLYNOMIAL *a, GB_TYPE type, GB_VALUE *conv); + +#endif /* __C_POLYNOMIAL_H */ diff --git a/gb.gsl/src/c_vector.c b/gb.gsl/src/c_vector.c new file mode 100644 index 00000000..cd4ee3f5 --- /dev/null +++ b/gb.gsl/src/c_vector.c @@ -0,0 +1,817 @@ +/*************************************************************************** + + c_vector.c + + gb.gsl component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_VECTOR_C + +#include "c_complex.h" +#include "c_polynomial.h" +#include "c_vector.h" + +#define THIS ((CVECTOR *)_object) +#define COMPLEX(_v) ((_v)->complex) + +//---- Utility functions ---------------------------------------------- + +/*int gsl_vector_has_zero(gsl_vector *a) +{ + int i; + int size = (int)a->size; + + for (i = 0; i < size; i++) + { + if (gsl_vector_get(a, i) == 0.0) + return TRUE; + } + + return FALSE; +} + +int gsl_vector_complex_has_zero(gsl_vector *a) +{ + int i; + int size = (int)a->size; + gsl_complex *p: + + for (i = 0; i < size; i++) + { + p = gsl_vector_complex_ptr((gsl_vector_complext *)a, i); + if (p->dat[0] == 0.0 && p->dat[1] == 0.0) + return TRUE; + } + + return FALSE; +} + +void gsl_vector_inverse(gsl_vector *a) +{ + int i; + int size = (int)a->size; + double *p; + + for (i = 0; i < size; i++) + { + p = gsl_vector_ptr(a, i); + *p = 1.0 / *p; + } +} + +void gsl_vector_complex_inverse(gsl_vector *a) +{ + int i; + int size = (int)a->size; + gsl_complex *p; + + for (i = 0; i < size; i++) + { + p = gsl_vector_complex_ptr((gsl_vector_complex *)a, i); + *p = gsl_complex_inverse(*p); + } +} + +void gsl_vector_negative(gsl_vector *a) +{ + int i; + int size = (int)a->size; + double *p; + + for (i = 0; i < size; i++) + { + p = gsl_vector_ptr(a, i); + *p = (- *p); + } +} + +void gsl_vector_complex_negative(gsl_vector *a) +{ + int i; + int size = (int)a->size; + gsl_complex *p; + + for (i = 0; i < size; i++) + { + p = gsl_vector_complex_ptr((gsl_vector_complext *)a, i); + *p = gsl_complex_negative(*p); + } +}*/ + +//---- Vector creation ------------------------------------------------------ + +//static bool _do_not_init = FALSE; + +CVECTOR *VECTOR_create(int size, bool complex, bool init) +{ + CVECTOR *v = (CVECTOR *)GB.Create(CLASS_Vector, NULL, NULL); + + v->complex = complex; + + if (!complex) + v->vector = init ? gsl_vector_calloc(size) : gsl_vector_alloc(size); + else + v->vector = init ? gsl_vector_complex_calloc(size) : gsl_vector_complex_alloc(size); + + //GB.Push(2, GB_T_INTEGER, size, GB_T_BOOLEAN, complex); + //return (CVECTOR *)GB.New(CLASS_Vector, NULL, (void *)(intptr_t)2); + + return v; +} + +/*CVECTOR *VECTOR_create_from(void *vector, bool complex) +{ + int size = ((gsl_vector *)vector)->size; + CVECTOR *v = VECTOR_create(size, complex, FALSE); + + if (complex) + gsl_vector_complex_free(CVEC(v)); + else + gsl_vector_free(VEC(v)); + + v->vector = vector; + return v; +}*/ + +static CVECTOR *VECTOR_copy(CVECTOR *_object) +{ + CVECTOR *copy = VECTOR_create(SIZE(THIS), COMPLEX(THIS), FALSE); + if (!COMPLEX(THIS)) + gsl_vector_memcpy(VEC(copy), VEC(THIS)); + else + gsl_vector_complex_memcpy(CVEC(copy), CVEC(THIS)); + + return copy; +} + +#define VECTOR_make(_a) (((_a)->ob.ref <= 1) ? (_a) : VECTOR_copy(_a)) + +static CVECTOR *VECTOR_convert_to_complex(CVECTOR *_object) +{ + CVECTOR *v = VECTOR_create(SIZE(THIS), TRUE, FALSE); + int i; + + for (i = 0; i < SIZE(THIS); i++) + gsl_vector_complex_set((gsl_vector_complex *)v->vector, i, gsl_complex_rect(gsl_vector_get(VEC(THIS), i), 0)); + + return v; +} + +void VECTOR_ensure_complex(CVECTOR *_object) +{ + gsl_vector_complex *v; + int size = SIZE(THIS); + int i; + + if (COMPLEX(THIS)) + return; + + v = gsl_vector_complex_alloc(size); + for (i = 0; i < size; i++) + gsl_vector_complex_set(v, i, gsl_complex_rect(gsl_vector_get(VEC(THIS), i), 0)); + + gsl_vector_free(VEC(THIS)); + THIS->vector = v; + THIS->complex = TRUE; +} + + +bool VECTOR_ensure_not_complex(CVECTOR *_object) +{ + gsl_vector *v; + int size = SIZE(THIS); + int i; + gsl_complex c; + + if (!COMPLEX(THIS)) + return FALSE; + + for (i = 0; i < size; i++) + { + c = gsl_vector_complex_get(CVEC(THIS), i); + if (GSL_IMAG(c) != 0.0) + return TRUE; + } + + v = gsl_vector_alloc(size); + + for (i = 0; i < size; i++) + gsl_vector_set(v, i, GSL_REAL(gsl_vector_complex_get(CVEC(THIS), i))); + + gsl_vector_complex_free(CVEC(THIS)); + THIS->vector = v; + THIS->complex = FALSE; + return FALSE; +} + +//---- Arithmetic operators ------------------------------------------------- + +static CVECTOR *_add(CVECTOR *a, CVECTOR *b, bool invert) +{ + CVECTOR *v = VECTOR_make(a); + + if (COMPLEX(v) || COMPLEX(b)) + { + VECTOR_ensure_complex(v); + VECTOR_ensure_complex(b); + gsl_vector_complex_add(CVEC(v), CVEC(b)); + } + else + gsl_vector_add(VEC(v), VEC(b)); + + return v; +} + +static CVECTOR *_sub(CVECTOR *a, CVECTOR *b, bool invert) +{ + CVECTOR *v = VECTOR_make(a); + + if (COMPLEX(v) || COMPLEX(b)) + { + VECTOR_ensure_complex(v); + VECTOR_ensure_complex(b); + gsl_vector_complex_sub(CVEC(v), CVEC(b)); + } + else + gsl_vector_sub(VEC(v), VEC(b)); + + return v; +} + +static CVECTOR *_mulf(CVECTOR *a, double f, bool invert) +{ + CVECTOR *v = VECTOR_make(a); + + if (COMPLEX(v)) + gsl_vector_complex_scale(CVEC(v), gsl_complex_rect(f, 0)); + else + gsl_vector_scale(VEC(v), f); + + return v; +} + +static CVECTOR *_mulo(CVECTOR *a, void *b, bool invert) +{ + CVECTOR *v = VECTOR_make(a); + + if (!GB.Is(b, CLASS_Complex)) + return NULL; + + VECTOR_ensure_complex(v); + gsl_vector_complex_scale(CVEC(v), ((CCOMPLEX *)b)->number); + + return v; +} + +static CVECTOR *_divf(CVECTOR *a, double f, bool invert) +{ + if (invert) + return NULL; + + if (f == 0.0) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + + return _mulf(a, 1 / f, FALSE); +} + +static CVECTOR *_divo(CVECTOR *a, void *b, bool invert) +{ + if (!GB.Is(b, CLASS_Complex)) + return NULL; + + CCOMPLEX *c = (CCOMPLEX *)b; + + if (invert) + return NULL; + + if (GSL_REAL(c->number) == 0 && GSL_IMAG(c->number) == 0) + { + GB.Error(GB_ERR_ZERO); + return NULL; + } + + CVECTOR *v = VECTOR_make(a); + + VECTOR_ensure_complex(v); + gsl_vector_complex_scale(CVEC(v), gsl_complex_inverse(c->number)); + + return v; +} + +static int _equal(CVECTOR *a, CVECTOR *b, bool invert) +{ + if (COMPLEX(a) || COMPLEX(b)) + { + VECTOR_ensure_complex(a); + VECTOR_ensure_complex(b); + return gsl_vector_complex_equal(CVEC(a), CVEC(b)); + } + else + return gsl_vector_equal(VEC(a), VEC(b)); +} + +static CVECTOR *_neg(CVECTOR *a) +{ + return _mulf(a, -1, FALSE); +} + +static GB_OPERATOR_DESC _operator = +{ + .equal = (void *)_equal, + .add = (void *)_add, + .sub = (void *)_sub, + .mulf = (void *)_mulf, + .mulo = (void *)_mulo, + .divf = (void *)_divf, + .divo = (void *)_divo, + .neg = (void *)_neg +}; + +//---- Conversions ---------------------------------------------------------- + +static char *_to_string(CVECTOR *_object, bool local) +{ + char *result = NULL; + int i; + int size = SIZE(THIS); + char *str; + int len; + + result = GB.AddChar(result, '['); + + for (i = 0; i < size; i++) + { + if (i) + result = GB.AddChar(result, local ? ' ' : ','); + + if (!COMPLEX(THIS)) + { + GB.NumberToString(local, gsl_vector_get(VEC(THIS), i), NULL, &str, &len); + result = GB.AddString(result, str, len); + } + else + { + str = COMPLEX_to_string(gsl_vector_complex_get(CVEC(THIS), i), local); + result = GB.AddString(result, str, GB.StringLength(str)); + GB.FreeString(&str); + } + } + + result = GB.AddChar(result, ']'); + + return result; +} + +static bool _convert(CVECTOR *_object, GB_TYPE type, GB_VALUE *conv) +{ + if (THIS) + { + if (!COMPLEX(THIS)) + { + switch (type) + { + case GB_T_FLOAT: + conv->_float.value = gsl_blas_dnrm2(VEC(THIS)); + return FALSE; + + case GB_T_SINGLE: + conv->_single.value = gsl_blas_dnrm2(VEC(THIS)); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + conv->_integer.value = gsl_blas_dnrm2(VEC(THIS)); + return FALSE; + + case GB_T_LONG: + conv->_long.value = gsl_blas_dnrm2(VEC(THIS)); + return FALSE; + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = _to_string(THIS, type == GB_T_CSTRING); + conv->_string.value.start = 0; + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); + return FALSE; + + default: + break; + } + } + else + { + switch (type) + { + case GB_T_FLOAT: + conv->_float.value = gsl_blas_dznrm2(CVEC(THIS)); + return FALSE; + + case GB_T_SINGLE: + conv->_single.value = gsl_blas_dznrm2(CVEC(THIS)); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + conv->_integer.value = gsl_blas_dznrm2(CVEC(THIS)); + return FALSE; + + case GB_T_LONG: + conv->_long.value = gsl_blas_dznrm2(CVEC(THIS)); + return FALSE; + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = _to_string(THIS, type == GB_T_CSTRING); + conv->_string.value.start = 0; + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); + return FALSE; + + default: + break; + } + } + + // Vector ---> Float[] + if ((type == GB.FindClass("Float[]") || type == CLASS_Polynomial) && !COMPLEX(THIS)) + { + GB_ARRAY a; + int i; + double *data; + + GB.Array.New(&a, GB_T_FLOAT, SIZE(THIS)); + data = (double *)GB.Array.Get(a, 0); + for(i = 0; i < SIZE(THIS); i++) + data[i] = gsl_vector_get(VEC(THIS), i); + + conv->_object.value = a; + if (type != CLASS_Polynomial) + return FALSE; + } + // Vector ---> Complex[] + else if (type == GB.FindClass("Complex[]") || type == CLASS_Polynomial) + { + GB_ARRAY a; + int i; + void **data; + CCOMPLEX *c; + + GB.Array.New(&a, CLASS_Complex, SIZE(THIS)); + data = (void **)GB.Array.Get(a, 0); + for(i = 0; i < SIZE(THIS); i++) + { + c = COMPLEX_create(COMPLEX(THIS) ? gsl_vector_complex_get(CVEC(THIS), i) : gsl_complex_rect(gsl_vector_get(VEC(THIS), i), 0)); + data[i] = c; + GB.Ref(c); + } + + conv->_object.value = a; + if (type != CLASS_Polynomial) + return FALSE; + } + else + return TRUE; + + // Vector ---> Polynomial + if (type == CLASS_Polynomial) + { + void *unref = conv->_object.value; + GB.Ref(unref); // Will be unref by the next GB.Conv() + POLYNOMIAL_convert(FALSE, type, conv); + GB.Unref(&unref); // Will be unref by the next GB.Conv() + //GB.Conv(conv, type); + //GB.UnrefKeep(&conv->_object.value, FALSE); // Will be ref again after the current GB.Conv() + return FALSE; + } + + } + else if (type >= GB_T_OBJECT) + { + if (GB.Is(conv->_object.value, CLASS_Array)) + { + GB_ARRAY array = (GB_ARRAY)conv->_object.value; + int size = GB.Array.Count(array); + CVECTOR *v; + int i; + GB_VALUE temp; + void *data; + GB_TYPE atype = GB.Array.Type(array); + + // Float[] Integer[] ... ---> Vector + if (atype > GB_T_BOOLEAN && atype <= GB_T_FLOAT) + { + v = VECTOR_create(size, FALSE, FALSE); + + for (i = 0; i < size; i++) + { + data = GB.Array.Get(array, i); + GB.ReadValue(&temp, data, atype); + GB.Conv(&temp, GB_T_FLOAT); + gsl_vector_set(VEC(v), i, temp._float.value); + } + + conv->_object.value = v; + return FALSE; + } + // Variant[] ---> Vector + else if (atype == GB_T_VARIANT) + { + CCOMPLEX *c; + v = VECTOR_create(size, TRUE, FALSE); + + for (i = 0; i < size; i++) + { + GB.ReadValue(&temp, GB.Array.Get(array, i), atype); + GB.BorrowValue(&temp); + GB.Conv(&temp, CLASS_Complex); + c = temp._object.value; + if (c) + gsl_vector_complex_set(CVEC(v), i, c->number); + else + gsl_vector_complex_set(CVEC(v), i, COMPLEX_zero); + GB.ReleaseValue(&temp); + } + + conv->_object.value = v; + return FALSE; + } + // Complex[] ---> Vector + else if (atype == CLASS_Complex) + { + CCOMPLEX *c; + v = VECTOR_create(size, TRUE, FALSE); + + for (i = 0; i < size; i++) + { + c = *((CCOMPLEX **)GB.Array.Get(array, i)); + if (c) + gsl_vector_complex_set(CVEC(v), i, c->number); + else + gsl_vector_complex_set(CVEC(v), i, COMPLEX_zero); + } + + conv->_object.value = v; + return FALSE; + } + } + // Float Integer... ---> Vector + else if (type > GB_T_BOOLEAN && type <= GB_T_FLOAT) + { + CVECTOR *v = VECTOR_create(1, FALSE, FALSE); + if (type == GB_T_FLOAT) + gsl_vector_set(VEC(v), 0, conv->_float.value); + else if (type == GB_T_SINGLE) + gsl_vector_set(VEC(v), 0, conv->_single.value); + else + gsl_vector_set(VEC(v), 0, conv->_integer.value); + conv->_object.value = v; + return FALSE; + } + // Complex ---> Vector + else if (type == CLASS_Complex) + { + CCOMPLEX *c = (CCOMPLEX *)conv->_object.value; + CVECTOR *v = VECTOR_create(1, TRUE, FALSE); + gsl_vector_complex_set(CVEC(v), 0, c->number); + conv->_object.value = v; + return FALSE; + } + } + + return TRUE; +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Vector_new, GB_INTEGER size; GB_BOOLEAN complex) + + bool complex = VARGOPT(complex, FALSE); + int size = VARGOPT(size, 1); + + if (size < 1) size = 1; + + THIS->complex = complex; + + if (!complex) + THIS->vector = gsl_vector_calloc(size); + else + THIS->vector = gsl_vector_complex_calloc(size); + +END_METHOD + + +BEGIN_METHOD_VOID(Vector_free) + + if (!COMPLEX(THIS)) + gsl_vector_free(VEC(THIS)); + else + gsl_vector_complex_free(CVEC(THIS)); + +END_METHOD + + +BEGIN_PROPERTY(Vector_Count) + + GB.ReturnInteger(SIZE(THIS)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Vector_Copy) + + GB.ReturnObject(VECTOR_copy(THIS)); + +END_METHOD + + +BEGIN_METHOD(Vector_get, GB_INTEGER index) + + int size = SIZE(THIS); + int index = VARG(index); + + if (index < 0 || index >= size) + { + GB.Error(GB_ERR_BOUND); + return; + } + + if (!COMPLEX(THIS)) + GB.ReturnFloat(gsl_vector_get(VEC(THIS), index)); + else + GB.ReturnObject(COMPLEX_create(gsl_vector_complex_get(CVEC(THIS), index))); + + GB.ReturnConvVariant(); + +END_METHOD + + +BEGIN_METHOD(Vector_put, GB_VARIANT value; GB_INTEGER index) + + int index = VARG(index); + int size = SIZE(THIS); + GB_VALUE *value = (GB_VALUE *)ARG(value); + int type; + COMPLEX_VALUE cv; + + if (index < 0 || index > size) + { + GB.Error(GB_ERR_BOUND); + return; + } + + type = COMPLEX_get_value(value, &cv); + + if (type == CGV_ERR) + return; + + if (type == CGV_COMPLEX) + { + VECTOR_ensure_complex(THIS); + gsl_vector_complex_set(CVEC(THIS), index, cv.z); + } + else + { + if (COMPLEX(THIS)) + gsl_vector_complex_set(CVEC(THIS), index, cv.z); + else + gsl_vector_set(VEC(THIS), index, cv.x); + } + +END_METHOD + + +static void do_dot(CVECTOR *_object, CVECTOR *v, bool conj) +{ + bool ca, cb; + + if (GB.CheckObject(v)) + return; + + ca = !COMPLEX(THIS); + cb = !COMPLEX(v); + + if (ca && cb) + { + double result; + gsl_blas_ddot(VEC(THIS), v->vector, &result); + GB.ReturnFloat(result); + } + else + { + CVECTOR *a, *b; + gsl_complex result; + + if (ca) + a = VECTOR_convert_to_complex(THIS); + else + a = THIS; + + if (cb) + b = VECTOR_convert_to_complex(v); + else + b = v; + + if (conj) + gsl_blas_zdotc(CVEC(a), CVEC(b), &result); + else + gsl_blas_zdotu(CVEC(a), CVEC(b), &result); + + GB.ReturnObject(COMPLEX_create(result)); + + if (ca) GB.Unref(POINTER(&a)); + if (cb) GB.Unref(POINTER(&b)); + } + + GB.ReturnConvVariant(); +} + + +BEGIN_METHOD(Vector_Dot, GB_OBJECT vector) + + do_dot(THIS, VARG(vector), FALSE); + +END_METHOD + + +BEGIN_METHOD(Vector_ConjDot, GB_OBJECT vector) + + do_dot(THIS, VARG(vector), TRUE); + +END_METHOD + + +BEGIN_METHOD_VOID(Vector_Norm) + + if (!COMPLEX(THIS)) + GB.ReturnFloat(gsl_blas_dnrm2(VEC(THIS))); + else + GB.ReturnFloat(gsl_blas_dznrm2(CVEC(THIS))); + +END_METHOD + + +BEGIN_PROPERTY(Vector_Handle) + + GB.ReturnPointer(THIS->vector); + +END_PROPERTY + + +BEGIN_METHOD(Vector_ToString, GB_BOOLEAN local) + + GB.ReturnString(GB.FreeStringLater(_to_string(THIS, VARGOPT(local, FALSE)))); + +END_METHOD + + +//--------------------------------------------------------------------- + +GB_DESC VectorDesc[] = +{ + GB_DECLARE("Vector", sizeof(CVECTOR)), + + GB_METHOD("_new", NULL, Vector_new, "[(Size)i(Complex)b]"), + GB_METHOD("_free", NULL, Vector_free, NULL), + //GB_STATIC_METHOD("_call", "Vector", Vector_call, "(Value)f."), + GB_METHOD("Copy", "Vector", Vector_Copy, NULL), + GB_METHOD("ToString", "s", Vector_ToString, "[(Local)b]"), + + GB_PROPERTY_READ("Count", "i", Vector_Count), + GB_PROPERTY_READ("Handle", "p", Vector_Handle), + + GB_METHOD("_get", "v", Vector_get, "(Index)i"), + GB_METHOD("_put", NULL, Vector_put, "(Value)v(Index)i"), + + //GB_METHOD("Scale", "Vector", Vector_Scale, "(Value)v"), + GB_METHOD("Dot", "v", Vector_Dot, "(Vector)Vector"), + GB_METHOD("ConjDot", "v", Vector_ConjDot, "(Vector)Vector"), + GB_METHOD("Norm", "f", Vector_Norm, NULL), + //GB_METHOD("Equal", "b", Vector_Equal, "(Vector)Vector;"), + + GB_INTERFACE("_convert", &_convert), + GB_INTERFACE("_operator", &_operator), + + GB_END_DECLARE +}; diff --git a/gb.gsl/src/c_vector.h b/gb.gsl/src/c_vector.h new file mode 100644 index 00000000..5b36b8c1 --- /dev/null +++ b/gb.gsl/src/c_vector.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + c_vector.h + + gb.gsl component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_VECTOR_H +#define __C_VECTOR_H + +#include "main.h" + +#ifndef __C_VECTOR_C +extern GB_DESC VectorDesc[]; +#endif + +typedef + struct + { + GB_BASE ob; + void *vector; + bool complex; + } + CVECTOR; + +#define VEC(_v) ((gsl_vector *)(_v)->vector) +#define CVEC(_v) ((gsl_vector_complex *)(_v)->vector) +#define SIZE(_v) ((int)(VEC(_v)->size)) + +CVECTOR *VECTOR_create(int size, bool complex, bool init); +void VECTOR_ensure_complex(CVECTOR *_object); +bool VECTOR_ensure_not_complex(CVECTOR *_object); + +#endif /* __C_VECTOR_H */ diff --git a/gb.gsl/src/gb.gsl.component b/gb.gsl/src/gb.gsl.component new file mode 100644 index 00000000..1d60d9da --- /dev/null +++ b/gb.gsl/src/gb.gsl.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.gsl +Author=Randall Morgan,Benoît Minisini +State=NotFinished +Implements=Complex diff --git a/gb.gsl/src/main.c b/gb.gsl/src/main.c new file mode 100644 index 00000000..9fb9fdba --- /dev/null +++ b/gb.gsl/src/main.c @@ -0,0 +1,88 @@ +/*************************************************************************** + + main.c + + gb.gsl component + + (c) 2012 Randall Morgan + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +#include "c_gsl.h" +#include "c_complex.h" +#include "c_vector.h" +#include "c_matrix.h" +#include "c_polynomial.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CGslDesc, /* The Elementary math functions */ + ComplexDesc, + VectorDesc, + PolynomialDesc, + MatrixDesc, + /* Other classes go here as completed */ + NULL // Must have a null entry for the end of the structure +}; + +GB_CLASS CLASS_Array; +GB_CLASS CLASS_Complex; +GB_CLASS CLASS_Matrix; +GB_CLASS CLASS_Vector; +GB_CLASS CLASS_Polynomial; + +static void error_handler(const char *reason, const char *file, int line, int gsl_errno) +{ + //fprintf(stderr, "gb.gsl: error: %s: %s\n", gsl_strerror(gsl_errno), reason); + GB.Error("&1: &2", gsl_strerror(gsl_errno), reason); +} + +int EXPORT GB_INIT(void) +{ + CLASS_Array = GB.FindClass("Array"); + CLASS_Complex = GB.FindClass("Complex"); + CLASS_Vector = GB.FindClass("Vector"); + CLASS_Matrix = GB.FindClass("Matrix"); + CLASS_Polynomial = GB.FindClass("Polynomial"); + + gsl_set_error_handler(error_handler); + + return 0; +} + +void EXPORT GB_EXIT() +{ + +} + +int EXPORT GB_INFO(const char *key, void **value) +{ + if (!strcasecmp(key, "PUSH_COMPLEX")) + { + *value = (void *)COMPLEX_push_complex; + return TRUE; + } + else + return FALSE; +} diff --git a/gb.gsl/src/main.h b/gb.gsl/src/main.h new file mode 100644 index 00000000..484c9ce2 --- /dev/null +++ b/gb.gsl/src/main.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + main.h + + gb.gsl component + + (c) 2012 Randall Morgan + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern GB_CLASS CLASS_Array; +extern GB_CLASS CLASS_Complex; +extern GB_CLASS CLASS_Vector; +extern GB_CLASS CLASS_Matrix; +extern GB_CLASS CLASS_Polynomial; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.gtk/AUTHORS b/gb.gtk/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.gtk/COPYING b/gb.gtk/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.gtk/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.gtk/ChangeLog b/gb.gtk/ChangeLog new file mode 100644 index 00000000..7de33bdf --- /dev/null +++ b/gb.gtk/ChangeLog @@ -0,0 +1,118 @@ +* FIXED: A serious bug making the program crash when deleting controls. +* FIXED: Some "Arrangement" bugs in some containers. +* NEW: "Menu" Enabled property implemented. +* FIXED: "Button" enabled/disabled aspect with some themes. +* FIXED: "PictureBox" can have focus now. +* NEW: Added an "Scroll" event to "ScrollView" container to detect user scroll changes. + + +* NEW: "Lang","Timer", and "Error" hooks implemented. +* NEW: "Label.Autoresize" property implemented. +* FIXED: "ScrollBar" is aligned at top or left of the control when there's extra space, as gb.qt does. +* FIXED: Some "Container" arrangement problems. +* FIXED: "HSplit" and "VSplit" arrangement fixed. +* NEW: "Separator" widget added. A simple widget which shows an horizontal or vertical separator line. +* FIXED: "TextLabel" horizontal alignment was wrong. +* FIXED: "Stock" icons take the current theme in account. +* FIXED: "ToolButton" click event is working now. +* NEW: "Button" and "ToggleButton" have a new boolean property "Vertical". If set, the + picture is show in the Top side instead of the Left side of the button. +* NEW: "TextLabel" and "Message" are able handle some HTML tags: ,,,,, + ,,,,

    ...

    ,

    ,
    , and are able to ignore unrecognized + tags and parse not XML formatted strings. +* NEW: "Label" and "TextLabel" controls have a "Transparent" property now. +* FIXED: "Label" and "TextLabel" controls redesigned. +* FIXED: Segmentation fault in the "Stock" class. +* FIXED: "Window" size is working in transitions from border resizable to fixed. +* FIXED: "Picture" cache redesigned. +* FIXED: "ComboBox" background and foreground colors. + +050808 - Alpha + +* NEW: "UserContainer" class implemented. +* FIXED: Some problems in menu representation. +* NEW: "Stock" class implemented. + +050804 - Alpha + +* NEW: Added "HSplit" and "VSplit" containers. +* FIXED: Picture cache now is implemented using a hash table (faster). +* FIXED: some speed problems in DrawingArea. + +050731 - Alpha + +* NEW: ScrollView.EnsureVisible implemented. +* NEW: "UserControl" class implemented. +* NEW: TrayIcon.Tag, W and H properties. +* NEW: Font.Scalable and Font.Grade properties implemented. +* NEW: System colors added to "Color" class. +* NEW: TextArea.Scrollbar property added. +* NEW: Control.Reparent method added to reparent widgets (but not top-level windows) +* FIXED: Mouse, Focus, and Key events handling redesigned: 28 bytes less per widget. +* NEW: Application.Font implemented. + +050730 - Alpha + +* FIXED: Controls now inherit parent font. +* FIXED: Tooltips management improved: best performance and less memory wasted. +* NEW: Application.Tooltip: Enabled and Font properties implemented +* NEW: MouseWheel event, Mouse.Orientation and Mouse.Delta properties implemented. +* NEW: Fonts.Count and Fonts._next implemented. +* FIXED: Containers only arrange visible children. +* FIXED: Segmentation fault on some containers when arrangement is not "None". +* FIXED: Some Window.Picture representation problems. +* NEW: "ListView" Find implemented. +* NEW: "Application.ActiveWindow" implemented. +* NEW: "TrayIcon": ScreenX, ScreenY, Width and Height properties added. +* NEW: "TrayIcon" adapted to the gb.qt new interface. +* NEW: "Mouse.ScreenX" and "Mouse.ScreenY" implemented. +* NEW: "Desktop.Scale", "Control.MoveScaled" and "Control.ResizeScaled" implemented. + +050725 - Alpha + +* FIXED: Restored compatibility with GTK+-2.4 +* FIXED: "ComboBox" Length and Text properties are working now. +* FIXED: "TextBox" Length property is working now. +* NEW: "ColumnView" Click and Activate events implemented. + +050724 - Alpha + +* NEW: "Picture.Copy" implemented. +* FIXED: Pictures now display correctly PNG transparences. +* FIXED: "Picture" class redesigned for better performance and less memory usage. +* NEW: "Image" _get, _put, copy and replace methods implemented. +* FIXED: 4 bytes less wasted per "PictureBox". +* FIXED: 4 bytes less wasted per "ComboBox". +* FIXED: Internal code reorganization and cleaning. + +050722 - Alpha + +* FIXED: "Show" calls to "ShowModal" if current Window is modal. +* NEW: 8 bytes less wasted per container. +* FIXED: "Frame" children position fixed. +* FIXED: "TabStrip" can display accelerators in labels. BackGround and ForeGround properties + fixed. Some less memory wasted per TabStrip widget. +* NEW: "Window" Maximized, Minimized and FullScreen properties are working. State removed. +* FIXED: "Dialog" class now implements Dialog.paths and OpenFile(Multi) to be compatible with + the gb.qt component. +* FIXED: "Window" now has two related properties "Mask" and "Picture" in order to be + compatible with the gb.qt implementation. Masking works correctly now. Using a Picture + with Mask=FALSE just uses the Picture as background for the Window. +* FIXED: "Plugger" control now is called "Embedder" in order to be compatible with + the gb.qt implementation. Methods, Events and Properties names changed too. +* NEW: "Application.Embedder" added to be compatible with gb.qt implementation. + + +050702 - Alpha + +* NEW: ScrollView properties and methods fully implemented. +* FIXED: Button font handling improved: more compatibility with gb.qt +* FIXED: "fg" and "bg" widgets pointers removed from gControl (8 bytes less used per + widget) +* NEW: Image.Save, Picture.Save, Picture.Flush, Picture._put, Picture._get methods + implemented. +* FIXED: ListView.BackGround now works correctly. +* NEW: Mouse.Move method implemented. +* NEW: Picture.Clear method implemented. +* NEW: Window and Form can have a parent now. +* NEW: ScrollBar and Slider properties and methods are fully implemented now. \ No newline at end of file diff --git a/gb.gtk/INSTALL b/gb.gtk/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.gtk/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.gtk/Makefile.am b/gb.gtk/Makefile.am new file mode 100644 index 00000000..65461878 --- /dev/null +++ b/gb.gtk/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @GTK_DIR@ +EXTRA_DIST = reconf spec share gb*.h gambas.h diff --git a/gb.gtk/NEWS b/gb.gtk/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.gtk/README b/gb.gtk/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.gtk/TODO b/gb.gtk/TODO new file mode 100644 index 00000000..8866cc08 --- /dev/null +++ b/gb.gtk/TODO @@ -0,0 +1,16 @@ +TODO: + +* HTML support in the TextEdit control. + +* The Settings properties of some controls. + +* The MovieBox source code was not reviewed. + +* The Control.Design property. + +* Solve underline and strikethrough problems. + +* TextEdit text size does not work. + +* Events ignored during a WAIT. + diff --git a/gb.gtk/acinclude.m4 b/gb.gtk/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.gtk/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.gtk/component.am b/gb.gtk/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.gtk/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.gtk/configure.ac b/gb.gtk/configure.ac new file mode 100644 index 00000000..b4d97036 --- /dev/null +++ b/gb.gtk/configure.ac @@ -0,0 +1,28 @@ +dnl ---- configure.ac for gb.gtk component + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-gtk, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.gtk) +AC_PROG_LIBTOOL + +GB_CHECK_XWINDOW() + +GB_COMPONENT_PKG_CONFIG( + gtk, GTK, gb.gtk, [src], + 'gtk+-2.0 >= 2.16' 'librsvg-2.0 >= 2.14.3' 'cairo >= 1.6.0' 'cairo-ft >= 1.6.0' 'gtk+-unix-print-2.0 >= 2.10' sm ice x11 +) + +GB_COMPONENT_PKG_CONFIG( + gtkopengl, GTKOPENGL, gb.gtk.opengl, [opengl], + gtkglext-1.0 gl x11 +) + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +src/opengl/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.gtk/gambas.h b/gb.gtk/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.gtk/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.gtk/gb.draw.h b/gb.gtk/gb.draw.h new file mode 120000 index 00000000..82ba0778 --- /dev/null +++ b/gb.gtk/gb.draw.h @@ -0,0 +1 @@ +../main/lib/draw/gb.draw.h \ No newline at end of file diff --git a/gb.gtk/gb.geom.h b/gb.gtk/gb.geom.h new file mode 120000 index 00000000..be9b8748 --- /dev/null +++ b/gb.gtk/gb.geom.h @@ -0,0 +1 @@ +../main/lib/draw/gb.geom.h \ No newline at end of file diff --git a/gb.gtk/gb.gl.h b/gb.gtk/gb.gl.h new file mode 120000 index 00000000..ff28a726 --- /dev/null +++ b/gb.gtk/gb.gl.h @@ -0,0 +1 @@ +../gb.opengl/src/gb.gl.h \ No newline at end of file diff --git a/gb.gtk/gb.image.h b/gb.gtk/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.gtk/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.gtk/gb.paint.h b/gb.gtk/gb.paint.h new file mode 120000 index 00000000..a516fe05 --- /dev/null +++ b/gb.gtk/gb.paint.h @@ -0,0 +1 @@ +../main/lib/draw/gb.paint.h \ No newline at end of file diff --git a/gb.gtk/gb_common.h b/gb.gtk/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.gtk/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.gtk/m4 b/gb.gtk/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.gtk/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.gtk/reconf b/gb.gtk/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.gtk/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.gtk/share b/gb.gtk/share new file mode 120000 index 00000000..638490a4 --- /dev/null +++ b/gb.gtk/share @@ -0,0 +1 @@ +../gb.qt4/share \ No newline at end of file diff --git a/gb.gtk/src/CButton.cpp b/gb.gtk/src/CButton.cpp new file mode 100644 index 00000000..e6c47d28 --- /dev/null +++ b/gb.gtk/src/CButton.cpp @@ -0,0 +1,309 @@ +/*************************************************************************** + + CButton.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CBUTTON_CPP + +#include "CButton.h" +#include "CContainer.h" +#include "CPicture.h" + +DECLARE_EVENT(EVENT_Click); + +void gb_raise_button_Click(gControl *sender) +{ + CWIDGET *ob = GetObject(sender); + + if (!ob) return; + GB.Ref(ob); + GB.Raise((void*)ob,EVENT_Click,0); + CACTION_raise(ob); + GB.Unref(POINTER(&ob)); +} + +/*************************************************************** + +CONSTRUCTORS + +****************************************************************/ + +BEGIN_METHOD(CBUTTON_new, GB_OBJECT parent) + + InitControl(new gButton(CONTAINER(VARG(parent)), gButton::Button), (CWIDGET*)THIS); + BUTTON->onClick=gb_raise_button_Click; + +END_METHOD + + +BEGIN_METHOD(CTOGGLEBUTTON_new, GB_OBJECT parent) + + InitControl(new gButton(CONTAINER(VARG(parent)), gButton::Toggle), (CWIDGET*)THIS); + BUTTON->onClick=gb_raise_button_Click; + +END_METHOD + +BEGIN_METHOD(CCHECKBOX_new, GB_OBJECT parent) + + InitControl(new gButton(CONTAINER(VARG(parent)), gButton::Check), (CWIDGET*)THIS); + BUTTON->onClick = gb_raise_button_Click; + +END_METHOD + +BEGIN_METHOD(CRADIOBUTTON_new, GB_OBJECT parent) + + InitControl(new gButton(CONTAINER(VARG(parent)), gButton::Radio), (CWIDGET*)THIS); + BUTTON->onClick=gb_raise_button_Click; + +END_METHOD + +BEGIN_METHOD(CTOOLBUTTON_new, GB_OBJECT parent) + + InitControl(new gButton(CONTAINER(VARG(parent)), gButton::Tool), (CWIDGET*)THIS); + BUTTON->onClick=gb_raise_button_Click; + +END_METHOD + + +BEGIN_PROPERTY(CBUTTON_text) + + if (READ_PROPERTY) { GB.ReturnNewZeroString(BUTTON->text()); return; } + BUTTON->setText((const char*)GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(CBUTTON_picture) + + if (READ_PROPERTY) + { + gPicture *pic = BUTTON->picture(); + GB.ReturnObject(pic ? pic->getTagValue() : 0); + } + else + { + CPICTURE *pic = (CPICTURE *)VPROP(GB_OBJECT); + BUTTON->setPicture(pic ? pic->picture : 0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CBUTTON_border) + + if (READ_PROPERTY) { GB.ReturnBoolean(BUTTON->getBorder()); return; } + BUTTON->setBorder(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CBUTTON_value) + + if (READ_PROPERTY) { GB.ReturnBoolean(BUTTON->value()); return; } + BUTTON->setValue(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CBUTTON_default) + + if (READ_PROPERTY) { GB.ReturnBoolean(BUTTON->isDefault()); return; } + BUTTON->setDefault(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CBUTTON_cancel) + + if (READ_PROPERTY) { GB.ReturnBoolean(BUTTON->isCancel()); return; } + BUTTON->setCancel(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTOOLBUTTON_toggle) + + if (READ_PROPERTY) + GB.ReturnBoolean(BUTTON->isToggle()); + else + BUTTON->setToggle(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CCHECKBOX_tristate) + + if (READ_PROPERTY) + GB.ReturnBoolean(BUTTON->isTristate()); + else + BUTTON->setTristate(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CCHECKBOX_value) + + if (READ_PROPERTY) + { + if (BUTTON->isTristate() && BUTTON->inconsistent()) + GB.ReturnInteger(1); + else + GB.ReturnInteger(BUTTON->value() ? -1 : 0); + } + else + { + if (BUTTON->isTristate() && VPROP(GB_INTEGER) == 1) + BUTTON->setInconsistent(true); + else + { + BUTTON->setInconsistent(false); + BUTTON->setValue(VPROP(GB_INTEGER)); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(CBUTTON_radio) + + if (READ_PROPERTY) + GB.ReturnBoolean(BUTTON->isRadio()); + else + BUTTON->setRadio(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CBUTTON_autoresize) + + if (READ_PROPERTY) + GB.ReturnBoolean(BUTTON->isAutoResize()); + else + BUTTON->setAutoResize(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +GB_DESC CButtonDesc[] = +{ + GB_DECLARE("Button", sizeof(CBUTTON)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CBUTTON_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", CBUTTON_text), + GB_PROPERTY("Caption", "s", CBUTTON_text), + GB_PROPERTY("Picture", "Picture", CBUTTON_picture), + + GB_PROPERTY("Border", "b", CBUTTON_border), + GB_PROPERTY("Default", "b", CBUTTON_default), + GB_PROPERTY("Cancel", "b", CBUTTON_cancel), + GB_PROPERTY("Value", "b", CBUTTON_value), + GB_PROPERTY("AutoResize", "b", CBUTTON_autoresize), + + GB_EVENT("Click", 0, 0, &EVENT_Click), + + BUTTON_DESCRIPTION, + + GB_END_DECLARE +}; + +GB_DESC CToggleButtonDesc[] = +{ + GB_DECLARE("ToggleButton", sizeof(CBUTTON)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CTOGGLEBUTTON_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", CBUTTON_text), + GB_PROPERTY("Caption", "s", CBUTTON_text), + GB_PROPERTY("Picture", "Picture", CBUTTON_picture), + GB_PROPERTY("Value", "b", CBUTTON_value), + GB_PROPERTY("Border", "b", CBUTTON_border), + GB_PROPERTY("Radio", "b", CBUTTON_radio), + GB_PROPERTY("AutoResize", "b", CBUTTON_autoresize), + + GB_EVENT("Click", 0, 0, &EVENT_Click), + + TOGGLEBUTTON_DESCRIPTION, + + GB_END_DECLARE +}; + +GB_DESC CRadioButtonDesc[] = +{ + GB_DECLARE("RadioButton", sizeof(CBUTTON)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CRADIOBUTTON_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", CBUTTON_text), + GB_PROPERTY("Caption", "s", CBUTTON_text), + GB_PROPERTY("Value", "b", CBUTTON_value), + GB_PROPERTY("AutoResize", "b", CBUTTON_autoresize), + + GB_EVENT("Click", 0, 0, &EVENT_Click), + + RADIOBUTTON_DESCRIPTION, + + GB_END_DECLARE +}; + +GB_DESC CCheckBoxDesc[] = +{ + GB_DECLARE("CheckBox", sizeof(CBUTTON)), GB_INHERITS("Control"), + + GB_CONSTANT("False", "i", 0), + GB_CONSTANT("True", "i", -1), + GB_CONSTANT("None", "i", 1), + + GB_METHOD("_new", 0, CCHECKBOX_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", CBUTTON_text), + GB_PROPERTY("Caption", "s", CBUTTON_text), + GB_PROPERTY("Tristate", "b", CCHECKBOX_tristate), + GB_PROPERTY("AutoResize", "b", CBUTTON_autoresize), + + GB_PROPERTY("Value", "i", CCHECKBOX_value), + + GB_EVENT("Click", 0, 0, &EVENT_Click), + + CHECKBOX_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CToolButtonDesc[] = +{ + GB_DECLARE("ToolButton", sizeof(CBUTTON)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CTOOLBUTTON_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", CBUTTON_text), + GB_PROPERTY("Caption", "s", CBUTTON_text), + GB_PROPERTY("Picture", "Picture", CBUTTON_picture), + GB_PROPERTY("Value", "b", CBUTTON_value), + GB_PROPERTY("Toggle", "b", CTOOLBUTTON_toggle), + GB_PROPERTY("Border", "b", CBUTTON_border), + GB_PROPERTY("Radio", "b", CBUTTON_radio), + GB_PROPERTY("AutoResize", "b", CBUTTON_autoresize), + + GB_EVENT("Click", 0, 0, &EVENT_Click), + + TOOLBUTTON_DESCRIPTION, + + GB_END_DECLARE +}; + + + diff --git a/gb.gtk/src/CButton.h b/gb.gtk/src/CButton.h new file mode 100644 index 00000000..3a50a8e1 --- /dev/null +++ b/gb.gtk/src/CButton.h @@ -0,0 +1,53 @@ +/*************************************************************************** + + CButton.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CBUTTON_H +#define __CBUTTON_H + +#include "main.h" +#include "gbutton.h" +#include "CWidget.h" +#include "CPicture.h" + + +#ifndef __CBUTTON_CPP +extern GB_DESC CButtonDesc[]; +extern GB_DESC CToggleButtonDesc[]; +extern GB_DESC CCheckBoxDesc[]; +extern GB_DESC CRadioButtonDesc[]; +extern GB_DESC CToolButtonDesc[]; +#else + +#define THIS ((CBUTTON *)_object) +#define BUTTON ((gButton*)THIS->ob.widget) + +#endif + +typedef + struct + { + CWIDGET ob; + } + CBUTTON; + +#endif diff --git a/gb.gtk/src/CClipboard.cpp b/gb.gtk/src/CClipboard.cpp new file mode 100644 index 00000000..f2facd51 --- /dev/null +++ b/gb.gtk/src/CClipboard.cpp @@ -0,0 +1,589 @@ +/*************************************************************************** + + CClipboard.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCLIPBOARD_CPP + +#include "gclipboard.h" + +#include "CWidget.h" +#include "CClipboard.h" +#include "CImage.h" +#include "CPicture.h" + +/*************************************************************************** + + Clipboard + +***************************************************************************/ + +static CIMAGE *_clipboard_image = NULL; + +BEGIN_METHOD_VOID(Clipboard_Clear) + + gClipboard::clear(); + GB.StoreObject(NULL, POINTER(&_clipboard_image)); + +END_METHOD + +static char *get_format(int i = 0, bool charset = false, bool drag = false) +{ + char *format = drag ? gDrag::getFormat(i) : gClipboard::getFormat(i); + char *p; + + if (format && !charset) + { + p = index(format, ';'); + if (p) + { + format = gt_free_later(g_strndup(format, p - format)); + } + } + + return format; +} + +static void get_formats(GB_ARRAY array, bool drag = false) +{ + int i, j; + char *fmt; + + for (i = 0;; i++) + { + fmt = get_format(i, true, drag); + if (!fmt) + break; + + if (*fmt < 'a' || *fmt > 'z') + continue; + + for (j = 0; j < GB.Array.Count(array); j++) + { + if (strcasecmp(fmt, *((char **)GB.Array.Get(array, j))) == 0) + break; + } + + if (j < GB.Array.Count(array)) + continue; + + *((char **)GB.Array.Add(array)) = GB.NewZeroString(fmt); + } +} + +static bool exist_format(char *format, bool drag = false) +{ + int i; + char *fmt; + bool ret = false; + + for (i = 0;; i++) + { + fmt = get_format(i, true, drag); + if (!fmt) + break; + + if (*fmt < 'a' || *fmt > 'z') + continue; + + if (!strcasecmp(format, fmt)) + { + ret = true; + break; + } + } + + return ret; +} + +BEGIN_PROPERTY(Clipboard_Format) + + GB.ReturnNewZeroString(get_format()); + +END_PROPERTY + +BEGIN_PROPERTY(Clipboard_Formats) + + GB_ARRAY array; + + GB.Array.New(&array, GB_T_STRING, 0); + get_formats(array); + GB.ReturnObject(array); + +END_PROPERTY + +BEGIN_PROPERTY(CCLIPBOARD_type) + + GB.ReturnInteger(gClipboard::getType()); + +END_PROPERTY + +BEGIN_METHOD(Clipboard_Copy, GB_VARIANT data; GB_STRING format) + + char *format; + + if (VARG(data).type == GB_T_STRING) + { + if (MISSING(format)) + format = NULL; + else + { + format = GB.ToZeroString(ARG(format)); + if (strlen(format) < 6 || strncmp(format, "text/", 5) ) + goto _BAD_FORMAT; + } + + gClipboard::setText(VARG(data).value._string, -1, format); + return; + } + + if (VARG(data).type >= GB_T_OBJECT && GB.Is(VARG(data).value._object, GB.FindClass("Image"))) + { + CIMAGE *img; + + if (!MISSING(format)) + goto _BAD_FORMAT; + + img = (CIMAGE *)VARG(data).value._object; + GB.Unref(POINTER(&_clipboard_image)); + GB.Ref(img); + _clipboard_image = img; + gClipboard::setImage(CIMAGE_get(img)); + return; + } + +_BAD_FORMAT: + + GB.Error("Bad clipboard format"); + +END_METHOD + + +BEGIN_METHOD(Clipboard_Paste, GB_STRING format) + + CIMAGE *img; + char *format = NULL; + char *text; + int len; + int type; + + type = gClipboard::getType(); + + if (!MISSING(format)) + { + format = GB.ToZeroString(ARG(format)); + if (!exist_format(format)) + { + GB.ReturnVariant(NULL); + return; + } + if (strncasecmp(format, "text/", 5) == 0) + type = gClipboard::Text; + } + + switch(type) + { + case gClipboard::Text: + text = gClipboard::getText(&len, format); + if (text) + GB.ReturnNewString(text, len); + else + GB.ReturnNull(); + break; + + case gClipboard::Image: + img = CIMAGE_create(gClipboard::getImage()); + GB.ReturnObject((void *)img); + break; + + case gClipboard::Nothing: + default: + GB.ReturnNull(); + } + + GB.ReturnConvVariant(); + +END_METHOD + +BEGIN_PROPERTY(Clipboard_Current) + + if (READ_PROPERTY) + GB.ReturnInteger(gClipboard::getCurrent()); + else + gClipboard::setCurrent(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(Clipboard_HasChanged) + + GB.ReturnBoolean(gClipboard::hasChanged()); + +END_PROPERTY + +GB_DESC CClipboardDesc[] = +{ + GB_DECLARE_STATIC("Clipboard"), + + GB_CONSTANT("None", "i", 0), + GB_CONSTANT("Text", "i", 1), + GB_CONSTANT("Image", "i", 2), + + GB_CONSTANT("Default", "i", 0), + GB_CONSTANT("Selection", "i", 1), + + GB_STATIC_METHOD("_exit", 0, Clipboard_Clear, 0), + GB_STATIC_METHOD("Clear", 0, Clipboard_Clear, 0), + + GB_STATIC_PROPERTY("Current", "i", Clipboard_Current), + + GB_STATIC_PROPERTY_READ("Format", "s", Clipboard_Format), + GB_STATIC_PROPERTY_READ("Formats", "String[]", Clipboard_Formats), + GB_STATIC_PROPERTY_READ("Type", "i", CCLIPBOARD_type), + GB_STATIC_PROPERTY_READ("HasChanged", "b", Clipboard_HasChanged), + + GB_STATIC_METHOD("Copy", 0, Clipboard_Copy, "(Data)v[(Format)s]"), + GB_STATIC_METHOD("Paste", "v", Clipboard_Paste, "[(Format)s]"), + + GB_END_DECLARE +}; + + +/*************************************************************************** + + Drag + +***************************************************************************/ + +void *CDRAG_drag(CWIDGET *source, GB_VARIANT_VALUE *data, char *format) +{ + gControl *dest; + + if (GB.CheckObject(source)) + return NULL; + + if (gDrag::isActive()) + { + GB.Error("Undergoing drag"); + return NULL; + } + + if (data->type == GB_T_STRING) + { + if (format) + { + if (strlen(format) < 5) + goto _BAD_FORMAT; + if (strncasecmp(format, "text/", 5)) + goto _BAD_FORMAT; + } + + dest = gDrag::dragText(source->widget, data->value._string, format); + } + else if (data->type >= GB_T_OBJECT && GB.Is(data->value._object, GB.FindClass("Image"))) + { + if (format && *format) + goto _BAD_FORMAT; + + dest = gDrag::dragImage(source->widget, CIMAGE_get((CIMAGE *)data->value._object)); + } + else + goto _BAD_FORMAT; + + //hide_frame(NULL); + //GB.Post((GB_POST_FUNC)post_exit_drag, 0); + + return GetObject(dest); + +_BAD_FORMAT: + + GB.Error("Bad drag format"); + return NULL; +} + +BEGIN_METHOD(Drag_call, GB_OBJECT source; GB_VARIANT data; GB_STRING format) + + GB.ReturnObject(CDRAG_drag((CWIDGET *)VARG(source), &VARG(data), MISSING(format) ? NULL : GB.ToZeroString(ARG(format)))); + +END_METHOD + + +BEGIN_PROPERTY(Drag_Icon) + + if (READ_PROPERTY) + { + gPicture *pic = gDrag::getIcon(); + GB.ReturnObject(pic ? pic->getTagValue() : 0); + } + else + { + CPICTURE *pic = (CPICTURE *)VPROP(GB_OBJECT); + gDrag::setIcon(pic ? pic->picture : 0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_IconX) + + int x, y; + + gDrag::getIconPos(&x, &y); + + if (READ_PROPERTY) + GB.ReturnInteger(x); + else + gDrag::setIconPos(VPROP(GB_INTEGER), y); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_IconY) + + int x, y; + + gDrag::getIconPos(&x, &y); + + if (READ_PROPERTY) + GB.ReturnInteger(y); + else + gDrag::setIconPos(x, VPROP(GB_INTEGER)); + +END_PROPERTY + + +#define CHECK_VALID() \ + if (!gDrag::isEnabled()) \ + { \ + GB.Error("No drag data"); \ + return; \ + } + + +BEGIN_PROPERTY(Drag_Type) + + CHECK_VALID(); + GB.ReturnInteger(gDrag::getType()); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_Format) + + CHECK_VALID(); + + GB.ReturnNewZeroString(get_format(0, false, true)); + +END_PROPERTY + +BEGIN_PROPERTY(Drag_Formats) + + GB_ARRAY array; + + CHECK_VALID(); + + GB.Array.New(&array, GB_T_STRING, 0); + get_formats(array, true); + GB.ReturnObject(array); + +END_PROPERTY + + + +static void paste_drag(char *format) +{ + CIMAGE *image; + char *text; + int len; + + //if (format) + // g_debug("format: %s drag: %s\n", format, gDrag::getFormat()); + + //fprintf(stderr, "paste_drag: %s\n", format); + + if (format) + { + if (!exist_format(format, true)) + { + GB.ReturnVariant(NULL); + return; + } + } + + switch(gDrag::getType()) + { + case gDrag::Text: + text = gDrag::getText(&len, format); + if (text) + GB.ReturnNewString(text, len); + else + GB.ReturnNull(); + break; + + case gDrag::Image: + image = CIMAGE_create(gDrag::getImage()->copy()); + GB.ReturnObject((void *)image); + break; + + default: + GB.ReturnNull(); + } + + GB.ReturnConvVariant(); +} + +BEGIN_PROPERTY(Drag_Data) + + CHECK_VALID(); + + if (!gDrag::isActive()) + { + GB.ReturnNull(); + return; + } + + paste_drag(NULL); + +END_PROPERTY + + +BEGIN_METHOD(Drag_Paste, GB_STRING format) + + CHECK_VALID(); + + if (!gDrag::isActive()) + { + GB.ReturnNull(); + return; + } + + paste_drag(MISSING(format) ? NULL : GB.ToZeroString(ARG(format))); + +END_METHOD + + +BEGIN_PROPERTY(Drag_Action) + + CHECK_VALID(); + GB.ReturnInteger(gDrag::getAction()); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_Source) + + CHECK_VALID(); + GB.ReturnObject(GetObject(gDrag::getSource())); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_X) + + CHECK_VALID(); + GB.ReturnInteger(gDrag::getDropX()); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_Y) + + CHECK_VALID(); + GB.ReturnInteger(gDrag::getDropY()); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Drag_exit) + + gDrag::exit(); + +END_METHOD + + +BEGIN_PROPERTY(Drag_Pending) + + GB.ReturnBoolean(gDrag::isActive()); + +END_PROPERTY + + +BEGIN_METHOD(Drag_Show, GB_OBJECT control; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + if (GB.CheckObject(VARG(control))) + return; + + /*if (!gDrag::isActive()) + { + GB.Error("No undergoing drag"); + return; + }*/ + + if (MISSING(x) || MISSING(y) || MISSING(w) || MISSING(h)) + gDrag::show(((CWIDGET *)VARG(control))->widget); + else + gDrag::show(((CWIDGET *)VARG(control))->widget, VARG(x), VARG(y), VARG(w), VARG(h)); + +END_METHOD + + +BEGIN_METHOD_VOID(Drag_Hide) + + gDrag::hide(); + +END_METHOD + + +GB_DESC CDragDesc[] = +{ + GB_DECLARE("Drag", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", gDrag::Nothing), + GB_CONSTANT("Text", "i", gDrag::Text), + GB_CONSTANT("Image", "i", gDrag::Image), + + GB_CONSTANT("Copy", "i", 0), + GB_CONSTANT("Link", "i", 1), + GB_CONSTANT("Move", "i", 2), + + GB_STATIC_PROPERTY("Icon", "Picture", Drag_Icon), + GB_STATIC_PROPERTY("IconX", "i", Drag_IconX), + GB_STATIC_PROPERTY("IconY", "i", Drag_IconY), + + GB_STATIC_PROPERTY_READ("Data", "v", Drag_Data), + GB_STATIC_PROPERTY_READ("Format", "s", Drag_Format), + GB_STATIC_PROPERTY_READ("Formats", "String[]", Drag_Formats), + GB_STATIC_PROPERTY_READ("Type", "i", Drag_Type), + GB_STATIC_PROPERTY_READ("Action", "i", Drag_Action), + GB_STATIC_PROPERTY_READ("Source", "Control", Drag_Source), + GB_STATIC_PROPERTY_READ("X", "i", Drag_X), + GB_STATIC_PROPERTY_READ("Y", "i", Drag_Y), + GB_STATIC_PROPERTY_READ("Pending", "b", Drag_Pending), + + GB_STATIC_METHOD("_call", "Control", Drag_call, "(Source)Control;(Data)v[(Format)s]"), + GB_STATIC_METHOD("_exit", 0, Drag_exit, 0), + GB_STATIC_METHOD("Show", 0, Drag_Show, "(Control)Control;[(X)i(Y)i(Width)i(Height)i]"), + GB_STATIC_METHOD("Hide", 0, Drag_Hide, 0), + GB_STATIC_METHOD("Paste", "v", Drag_Paste, "[(Format)s]"), + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/CClipboard.h b/gb.gtk/src/CClipboard.h new file mode 100644 index 00000000..17e69f99 --- /dev/null +++ b/gb.gtk/src/CClipboard.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + CClipboard.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCLIPBOARD_H +#define __CCLIPBOARD_H + +#include "main.h" + +#ifndef __CCLIPBOARD_CPP +extern GB_DESC CClipboardDesc[]; +extern GB_DESC CDragDesc[]; +#endif + +void *CDRAG_drag(CWIDGET *source, GB_VARIANT_VALUE *data, char *format); + +#endif diff --git a/gb.gtk/src/CColor.cpp b/gb.gtk/src/CColor.cpp new file mode 100644 index 00000000..46874a03 --- /dev/null +++ b/gb.gtk/src/CColor.cpp @@ -0,0 +1,139 @@ +/*************************************************************************** + + CColor.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCOLOR_CPP + +#include + +#include "CColor.h" +#include "gdesktop.h" +#include "gcolor.h" + +BEGIN_PROPERTY(Color_Background) + + GB.ReturnInteger(gDesktop::bgColor()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_Foreground) + + GB.ReturnInteger(gDesktop::fgColor()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_TextBackground) + + GB.ReturnInteger(gDesktop::textbgColor()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_TextForeground) + + GB.ReturnInteger(gDesktop::textfgColor()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_SelectedBackground) + + GB.ReturnInteger(gDesktop::selbgColor()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_LightBackground) + + GB.ReturnInteger(gDesktop::lightbgColor()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_SelectedForeground) + + GB.ReturnInteger(gDesktop::selfgColor()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_ButtonBackground) + + GB.ReturnInteger(gDesktop::buttonbgColor()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_ButtonForeground) + + GB.ReturnInteger(gDesktop::buttonfgColor()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_LightForeground) + + GB.ReturnInteger(gDesktop::lightfgColor()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_TooltipBackground) + + GB.ReturnInteger(gDesktop::tooltipBackground()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_TooltipForeground) + + GB.ReturnInteger(gDesktop::tooltipForeground()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_LinkForeground) + + GB.ReturnInteger(gDesktop::linkForeground()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_VisitedForeground) + + GB.ReturnInteger(gDesktop::visitedForeground()); + +END_PROPERTY + +GB_DESC CColorDesc[] = +{ + GB_DECLARE_STATIC("Color"), + + GB_STATIC_PROPERTY_READ("Background", "i", Color_Background), + GB_STATIC_PROPERTY_READ("SelectedBackground", "i", Color_SelectedBackground), + GB_STATIC_PROPERTY_READ("LightBackground", "i", Color_LightBackground), + GB_STATIC_PROPERTY_READ("TextBackground", "i", Color_TextBackground), + GB_STATIC_PROPERTY_READ("ButtonBackground", "i", Color_ButtonBackground), + GB_STATIC_PROPERTY_READ("TooltipBackground", "i", Color_TooltipBackground), + + GB_STATIC_PROPERTY_READ("Foreground", "i", Color_Foreground), + GB_STATIC_PROPERTY_READ("SelectedForeground", "i", Color_SelectedForeground), + GB_STATIC_PROPERTY_READ("LightForeground", "i", Color_LightForeground), + GB_STATIC_PROPERTY_READ("TextForeground", "i", Color_TextForeground), + GB_STATIC_PROPERTY_READ("ButtonForeground", "i", Color_ButtonForeground), + GB_STATIC_PROPERTY_READ("TooltipForeground", "i", Color_TooltipForeground), + GB_STATIC_PROPERTY_READ("LinkForeground", "i", Color_LinkForeground), + GB_STATIC_PROPERTY_READ("VisitedForeground", "i", Color_VisitedForeground), + + GB_END_DECLARE +}; + + diff --git a/gb.gtk/src/CColor.h b/gb.gtk/src/CColor.h new file mode 100644 index 00000000..f33e6de2 --- /dev/null +++ b/gb.gtk/src/CColor.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + CColor.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCOLOR_H +#define __CCOLOR_H + +#include "main.h" + +#ifndef __CCOLOR_CPP +extern GB_DESC CColorDesc[]; +#endif + +#endif diff --git a/gb.gtk/src/CConst.cpp b/gb.gtk/src/CConst.cpp new file mode 100644 index 00000000..41820d82 --- /dev/null +++ b/gb.gtk/src/CConst.cpp @@ -0,0 +1,133 @@ +/*************************************************************************** + + CConst.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCONST_CPP + + +#include "gambas.h" +#include "main.h" +#include "widgets.h" +#include "CConst.h" + + +#define IMPLEMENT_ALIGN(_method, _code) \ +BEGIN_METHOD(_method, GB_INTEGER align) \ + int a = VARG(align); \ + GB.ReturnBoolean(_code); \ +END_METHOD + +IMPLEMENT_ALIGN(Align_IsTop, ALIGN_IS_TOP(a)) +IMPLEMENT_ALIGN(Align_IsBottom, ALIGN_IS_BOTTOM(a)) +IMPLEMENT_ALIGN(Align_IsMiddle, ALIGN_IS_MIDDLE(a)) +IMPLEMENT_ALIGN(Align_IsLeft, ALIGN_IS_LEFT(a)) +IMPLEMENT_ALIGN(Align_IsRight, ALIGN_IS_RIGHT(a)) +IMPLEMENT_ALIGN(Align_IsCenter, ALIGN_IS_CENTER(a)) + +GB_DESC CAlignDesc[] = +{ + GB_DECLARE("Align", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("Normal", "i", ALIGN_NORMAL), + GB_CONSTANT("Left", "i", ALIGN_LEFT), + GB_CONSTANT("Right", "i", ALIGN_RIGHT), + GB_CONSTANT("Center", "i", ALIGN_CENTER), + + GB_CONSTANT("TopNormal", "i", ALIGN_TOP_NORMAL), + GB_CONSTANT("TopLeft", "i", ALIGN_TOP_LEFT), + GB_CONSTANT("TopRight", "i", ALIGN_TOP_RIGHT), + GB_CONSTANT("Top", "i", ALIGN_TOP), + + GB_CONSTANT("BottomNormal", "i", ALIGN_BOTTOM_NORMAL), + GB_CONSTANT("BottomLeft", "i", ALIGN_BOTTOM_LEFT), + GB_CONSTANT("BottomRight", "i", ALIGN_BOTTOM_RIGHT), + GB_CONSTANT("Bottom", "i", ALIGN_BOTTOM), + + GB_CONSTANT("Justify", "i", ALIGN_JUSTIFY), + + GB_STATIC_METHOD("IsTop", "b", Align_IsTop, "(Alignment)i"), + GB_STATIC_METHOD("IsBottom", "b", Align_IsBottom, "(Alignment)i"), + GB_STATIC_METHOD("IsMiddle", "b", Align_IsMiddle, "(Alignment)i"), + GB_STATIC_METHOD("IsLeft", "b", Align_IsLeft, "(Alignment)i"), + GB_STATIC_METHOD("IsCenter", "b", Align_IsCenter, "(Alignment)i"), + GB_STATIC_METHOD("IsRight", "b", Align_IsRight, "(Alignment)i"), + + GB_END_DECLARE +}; + + +GB_DESC CArrangeDesc[] = +{ + GB_DECLARE("Arrange", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", ARRANGE_NONE), + GB_CONSTANT("Horizontal", "i", ARRANGE_HORIZONTAL), + GB_CONSTANT("Vertical", "i", ARRANGE_VERTICAL), + GB_CONSTANT("LeftRight", "i", ARRANGE_ROW), + GB_CONSTANT("TopBottom", "i", ARRANGE_COLUMN), + GB_CONSTANT("Row", "i", ARRANGE_ROW), + GB_CONSTANT("Column", "i", ARRANGE_COLUMN), + GB_CONSTANT("Fill", "i", ARRANGE_FILL), + + GB_END_DECLARE +}; + + +GB_DESC CBorderDesc[] = +{ + GB_DECLARE("Border", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", BORDER_NONE), + GB_CONSTANT("Plain", "i", BORDER_PLAIN), + GB_CONSTANT("Sunken", "i", BORDER_SUNKEN), + GB_CONSTANT("Raised", "i", BORDER_RAISED), + GB_CONSTANT("Etched", "i", BORDER_ETCHED), + + GB_END_DECLARE +}; + + +GB_DESC CScrollDesc[] = +{ + GB_DECLARE("Scroll", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", SCROLL_NONE), + GB_CONSTANT("Horizontal", "i", SCROLL_HORIZONTAL), + GB_CONSTANT("Vertical", "i", SCROLL_VERTICAL), + GB_CONSTANT("Both", "i", SCROLL_BOTH), + + GB_END_DECLARE +}; + + +GB_DESC CSelectDesc[] = +{ + GB_DECLARE("Select", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", SELECT_NONE), + GB_CONSTANT("Single", "i", SELECT_SINGLE), + GB_CONSTANT("Multiple", "i", SELECT_MULTIPLE), + + GB_END_DECLARE +}; + + diff --git a/gb.gtk/src/CConst.h b/gb.gtk/src/CConst.h new file mode 100644 index 00000000..77280e1f --- /dev/null +++ b/gb.gtk/src/CConst.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + CConst.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCONST_H +#define __CCONST_H + +#include "gambas.h" + +#ifndef __CCONST_CPP +extern GB_DESC CAlignDesc[]; +extern GB_DESC CArrangeDesc[]; +extern GB_DESC CBorderDesc[]; +extern GB_DESC CScrollDesc[]; +extern GB_DESC CLineDesc[]; +extern GB_DESC CFillDesc[]; +extern GB_DESC CSelectDesc[]; +#endif + + + + +#endif diff --git a/gb.gtk/src/CContainer.cpp b/gb.gtk/src/CContainer.cpp new file mode 100644 index 00000000..d6df3c1c --- /dev/null +++ b/gb.gtk/src/CContainer.cpp @@ -0,0 +1,616 @@ +/*************************************************************************** + + CContainer.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCONTAINER_CPP + +#include "gambas.h" +#include "CContainer.h" +#include "gframe.h" +#include "gmainwindow.h" + +/*************************************************************************** + + Container + +***************************************************************************/ + +DECLARE_EVENT(EVENT_BeforeArrange); +DECLARE_EVENT(EVENT_Arrange); +DECLARE_EVENT(EVENT_Insert); + + +void CCONTAINER_cb_before_arrange(gContainer *sender) +{ + GB.Raise(sender->hFree, EVENT_BeforeArrange, 0); +} + +void CCONTAINER_cb_arrange(gContainer *sender) +{ + GB.Raise(sender->hFree, EVENT_Arrange, 0); +} + + +void CCONTAINER_raise_insert(CCONTAINER *_object, CWIDGET *child) +{ + GB.Raise(THIS, EVENT_Insert, 1, GB_T_OBJECT, child); +} + + +static void get_client_area(gContainer *cont, int *x, int *y, int *w, int *h) +{ + gContainer *proxy = cont->proxyContainer(); + + if (x) *x = proxy->clientX(); + if (y) *y = proxy->clientY(); + if (w) *w = proxy->clientWidth(); + if (h) *h = proxy->clientHeight(); + + if (x || y) + { + while (proxy && proxy != cont) + { + if (x) *x += proxy->x(); + if (y) *y += proxy->y(); + proxy = proxy->parent(); + } + } +} + + +BEGIN_PROPERTY(Container_X) + + int x; + get_client_area(WIDGET, &x, NULL, NULL, NULL); + GB.ReturnInteger(x); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Y) + + int y; + get_client_area(WIDGET, NULL, &y, NULL, NULL); + GB.ReturnInteger(y); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_ClientWidth) + + int w; + get_client_area(WIDGET, NULL, NULL, &w, NULL); + GB.ReturnInteger(w); + + +END_PROPERTY + + +BEGIN_PROPERTY(Container_ClientHeight) + + int h; + get_client_area(WIDGET, NULL, NULL, NULL, &h); + GB.ReturnInteger(h); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Arrangement) + + if (READ_PROPERTY) { GB.ReturnInteger(WIDGET->arrange()); return; } + WIDGET->setArrange(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_AutoResize) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->autoResize()); + else + WIDGET->setAutoResize(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Padding) + + if (READ_PROPERTY) { GB.ReturnInteger(WIDGET->padding()); return; } + WIDGET->setPadding(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Spacing) + + if (READ_PROPERTY) { GB.ReturnBoolean(WIDGET->spacing()); return; } + WIDGET->setSpacing(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Margin) + + if (READ_PROPERTY) { GB.ReturnBoolean(WIDGET->margin()); return; } + WIDGET->setMargin(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Indent) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->indent()); + else + WIDGET->setMargin(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Invert) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->invert()); + else + WIDGET->setInvert(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_METHOD(Container_Find, GB_INTEGER x; GB_INTEGER y) + + gControl *child = WIDGET->find(VARG(x), VARG(y)); + + if (child) + GB.ReturnObject(child->hFree); + else + GB.ReturnNull(); + +END_METHOD + + +BEGIN_METHOD(Container_unknown, GB_VALUE x; GB_VALUE y) + + char *name = GB.GetUnknown(); + int nparam = GB.NParam(); + + if (strcasecmp(name, "Find")) + { + GB.Error(GB_ERR_NSYMBOL, GB.GetClassName(NULL), name); + return; + } + + if (nparam < 2) + { + GB.Error("Not enough argument"); + return; + } + else if (nparam > 2) + { + GB.Error("Too many argument"); + return; + } + + GB.Deprecated(GTK_NAME, "Container.Find", "Container.FindChild"); + + if (GB.Conv(ARG(x), GB_T_INTEGER) || GB.Conv(ARG(y), GB_T_INTEGER)) + return; + + Container_Find(_object, _param); + + GB.ReturnConvVariant(); + +END_METHOD + + +//--------------------------------------------------------------------------- + + +BEGIN_PROPERTY(Container_Children) + + CCONTAINERCHILDREN *children = (CCONTAINERCHILDREN *)GB.New(CLASS_ContainerChildren, NULL, NULL); + gContainer *cont = WIDGET->proxyContainer(); + gControl *child; + int i; + + children->container = THIS; + GB.Ref(THIS); + + GB.NewArray(POINTER(&children->children), sizeof(void *), 0); + + for (i = 0; i < cont->childCount(); i++) + { + child = cont->child(i); + if (!child->hFree || child->isDestroyed()) + continue; + GB.Ref(child->hFree); + *(void **)GB.Add(&children->children) = child->hFree; + } + + GB.ReturnObject(children); + +END_PROPERTY + + +BEGIN_METHOD_VOID(ContainerChildren_free) + + int i; + CWIDGET **array = THIS_CHILDREN->children; + + for (i = 0; i < GB.Count(array); i++) + GB.Unref(POINTER(&array[i])); + + GB.FreeArray(&THIS_CHILDREN->children); + GB.Unref(POINTER(&THIS_CHILDREN->container)); + +END_METHOD + + +BEGIN_PROPERTY(ContainerChildren_Count) + + GB.ReturnInteger(GB.Count(THIS_CHILDREN->children)); + +END_PROPERTY + + +BEGIN_PROPERTY(ContainerChildren_Max) + + GB.ReturnInteger(GB.Count(THIS_CHILDREN->children) - 1); + +END_PROPERTY + + +BEGIN_METHOD(ContainerChildren_get, GB_INTEGER index) + + CWIDGET **array = THIS_CHILDREN->children; + int index = VARG(index); + + if (index < 0 || index >= GB.Count(array)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(array[index]); + +END_METHOD + + +BEGIN_METHOD_VOID(ContainerChildren_next) + + CWIDGET **array = THIS_CHILDREN->children; + int index; + + index = ENUM(int); + + if (index >= GB.Count(array)) + GB.StopEnum(); + else + { + ENUM(int) = index + 1; + GB.ReturnObject(array[index]); + } + +END_METHOD + + +BEGIN_METHOD_VOID(ContainerChildren_Clear) + + ((gContainer *)(THIS_CHILDREN->container->ob.widget))->clear(); + +END_METHOD + + +//--------------------------------------------------------------------------- + + +GB_DESC ContainerChildrenDesc[] = +{ + GB_DECLARE("ContainerChildren", sizeof(CCONTAINER)), GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, ContainerChildren_free, NULL), + GB_METHOD("_next", "Control", ContainerChildren_next, NULL), + GB_METHOD("_get", "Control", ContainerChildren_get, "(Index)i"), + GB_PROPERTY_READ("Count", "i", ContainerChildren_Count), + GB_PROPERTY_READ("Max", "i", ContainerChildren_Max), + GB_METHOD("Clear", NULL, ContainerChildren_Clear, NULL), + + GB_END_DECLARE +}; + + +GB_DESC ContainerDesc[] = +{ + GB_DECLARE("Container", sizeof(CCONTAINER)), GB_INHERITS("Control"), + + GB_NOT_CREATABLE(), + + GB_PROPERTY_READ("Children", "ContainerChildren", Container_Children), + + GB_PROPERTY_READ("ClientX", "i", Container_X), + GB_PROPERTY_READ("ClientY", "i", Container_Y), + GB_PROPERTY_READ("ClientW", "i", Container_ClientWidth), + GB_PROPERTY_READ("ClientWidth", "i", Container_ClientWidth), + GB_PROPERTY_READ("ClientH", "i", Container_ClientHeight), + GB_PROPERTY_READ("ClientHeight", "i", Container_ClientHeight), + + GB_METHOD("FindChild", "Control", Container_Find, "(X)i(Y)i"), + GB_METHOD("_unknown", "v", Container_unknown, "."), + + CONTAINER_DESCRIPTION, + + GB_EVENT("BeforeArrange", NULL, NULL, &EVENT_BeforeArrange), + GB_EVENT("Arrange", NULL, NULL, &EVENT_Arrange), + GB_EVENT("NewChild", NULL, "(Child)Control", &EVENT_Insert), + + GB_END_DECLARE +}; + +/**************************************************************************** + + UserControl & UserContainer + +****************************************************************************/ + +BEGIN_METHOD(UserControl_new, GB_OBJECT parent) + + InitControl(new gPanel(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + + PANEL->setArrange(ARRANGE_FILL); + PANEL->setUser(true); + + THIS_UC->container = THIS; + +END_METHOD + + +BEGIN_PROPERTY(UserControl_Container) + + CCONTAINER *ct; + gControl *test; + int count; + int bucle; + bool ok = false; + + if (READ_PROPERTY) + { + GB.ReturnObject(THIS_UC->container); + return; + } + + ct = (CCONTAINER*)VPROP(GB_OBJECT); + + if (!ct) + { + if (THIS_CONT != THIS) + WIDGET_CONT->setProxyContainerFor(NULL); + THIS_UC->container = THIS; + WIDGET->setProxyContainer(NULL); + WIDGET->setProxy(NULL); + return; + } + + if (GB.CheckObject(ct)) + return; + + count = PANEL->childCount(); + + for (bucle = 0; bucle < count; bucle++) + { + test = PANEL->child(bucle); + do + { + if (test->parent() == WIDGET) + { + ok = true; + break; + } + test = test->parent(); + } while (test); + } + + if (!ok) + { + GB.Error("Container must be a child control"); + return; + } + + gColor bg = WIDGET_CONT->background(); + gColor fg = WIDGET_CONT->foreground(); + + if (THIS_CONT != THIS) + WIDGET_CONT->setProxyContainerFor(NULL); + + THIS_UC->container = (CCONTAINER *)GetObject(((gContainer *)ct->ob.widget)->proxyContainer()); + + WIDGET->setProxyContainer(WIDGET_CONT->proxyContainer()); + WIDGET->setProxy(WIDGET_CONT); + + WIDGET_CONT->setProxyContainerFor(WIDGET); + WIDGET_CONT->setBackground(bg); + WIDGET_CONT->setForeground(fg); + WIDGET_CONT->performArrange(); + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Container) + + if (READ_PROPERTY) + UserControl_Container(_object, _param); + else + { + UserControl_Container(_object, _param); + + WIDGET_CONT->setFullArrangement(&THIS_UC->save); + //qDebug("(%s %p): arrangement = %08X", GB.GetClassName(THIS), THIS, after->arrangement); + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Arrangement) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET_CONT->arrange()); + else + { + WIDGET_CONT->setArrange(VPROP(GB_INTEGER)); + THIS_UC->save = WIDGET_CONT->fullArrangement(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_AutoResize) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET_CONT->autoResize()); + else + { + WIDGET_CONT->setAutoResize(VPROP(GB_BOOLEAN)); + THIS_UC->save = WIDGET_CONT->fullArrangement(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Padding) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET_CONT->padding()); + else + { + WIDGET_CONT->setPadding(VPROP(GB_INTEGER)); + THIS_UC->save = WIDGET_CONT->fullArrangement(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Spacing) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET_CONT->spacing()); + else + { + WIDGET_CONT->setSpacing(VPROP(GB_BOOLEAN)); + THIS_UC->save = WIDGET_CONT->fullArrangement(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Margin) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET_CONT->margin()); + else + { + WIDGET_CONT->setMargin(VPROP(GB_BOOLEAN)); + THIS_UC->save = WIDGET_CONT->fullArrangement(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Indent) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET_CONT->indent()); + else + { + WIDGET_CONT->setIndent(VPROP(GB_INTEGER)); + THIS_UC->save = WIDGET_CONT->fullArrangement(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Invert) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET_CONT->invert()); + else + { + WIDGET_CONT->setInvert(VPROP(GB_BOOLEAN)); + THIS_UC->save = WIDGET_CONT->fullArrangement(); + } + +END_PROPERTY + +BEGIN_PROPERTY(UserContainer_Focus) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->canFocus()); + else + WIDGET->setCanFocus(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +//--------------------------------------------------------------------------- + +GB_DESC UserControlDesc[] = +{ + GB_DECLARE("UserControl", sizeof(CUSERCONTROL)), GB_INHERITS("Container"), + GB_NOT_CREATABLE(), + + GB_METHOD("_new", NULL, UserControl_new, "(Parent)Container;"), + GB_PROPERTY("_Container", "Container", UserControl_Container), + GB_PROPERTY("_AutoResize", "b", Container_AutoResize), + GB_PROPERTY("_Arrangement", "i", Container_Arrangement), + GB_PROPERTY("_Padding", "i", Container_Padding), + GB_PROPERTY("_Spacing", "b", Container_Spacing), + GB_PROPERTY("_Margin", "b", Container_Margin), + GB_PROPERTY("_Indent", "b", Container_Indent), + GB_PROPERTY("_Invert", "b", Container_Invert), + + USERCONTROL_DESCRIPTION, + + GB_END_DECLARE +}; + +GB_DESC UserContainerDesc[] = +{ + GB_DECLARE("UserContainer", sizeof(CUSERCONTROL)), GB_INHERITS("Container"), + GB_NOT_CREATABLE(), + + GB_METHOD("_new", NULL, UserControl_new, "(Parent)Container;"), + + GB_PROPERTY("_Container", "Container", UserContainer_Container), + GB_PROPERTY("_Arrangement", "i", Container_Arrangement), + + GB_PROPERTY("Arrangement", "i", UserContainer_Arrangement), + GB_PROPERTY("AutoResize", "b", UserContainer_AutoResize), + GB_PROPERTY("Padding", "i", UserContainer_Padding), + GB_PROPERTY("Spacing", "b", UserContainer_Spacing), + GB_PROPERTY("Margin", "b", UserContainer_Margin), + GB_PROPERTY("Indent", "b", UserContainer_Indent), + GB_PROPERTY("Invert", "b", UserContainer_Invert), + + //GB_PROPERTY("Focus", "b", UserContainer_Focus), + + USERCONTAINER_DESCRIPTION, + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/CContainer.h b/gb.gtk/src/CContainer.h new file mode 100644 index 00000000..ac28f0ef --- /dev/null +++ b/gb.gtk/src/CContainer.h @@ -0,0 +1,92 @@ +/*************************************************************************** + + CContainer.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCONTAINER_H +#define __CCONTAINER_H + + +#include "main.h" +#include "gambas.h" +#include "widgets.h" +#include "CWidget.h" + +#ifndef __CCONTAINER_CPP + +extern GB_DESC ContainerChildrenDesc[]; +extern GB_DESC ContainerDesc[]; +extern GB_DESC UserControlDesc[]; +extern GB_DESC UserContainerDesc[]; + +#else + +#define THIS ((CCONTAINER *)_object) +#define THIS_CHILDREN ((CCONTAINERCHILDREN *)_object) +#define THIS_UC ((CUSERCONTROL *)_object) +#define WIDGET ((gContainer*)THIS->ob.widget) +#define PANEL ((gPanel *)(THIS->ob.widget)) + +#define THIS_CONT (THIS_UC->container) +#define WIDGET_CONT ((gContainer *)THIS_UC->container->ob.widget) + +#endif + + +typedef + struct + { + CWIDGET ob; + } + CCONTAINER; + +typedef + struct + { + GB_BASE ob; + CCONTAINER *container; + CWIDGET **children; + } + CCONTAINERCHILDREN; + +typedef + struct + { + CWIDGET widget; + CCONTAINER *container; + gContainerArrangement save; + } + CUSERCONTROL; + + +DECLARE_PROPERTY(Container_Arrangement); +DECLARE_PROPERTY(Container_AutoResize); +DECLARE_PROPERTY(Container_Padding); +DECLARE_PROPERTY(Container_Spacing); +DECLARE_PROPERTY(Container_Margin); +DECLARE_PROPERTY(Container_Indent); +DECLARE_PROPERTY(Container_Invert); + +void CCONTAINER_cb_arrange(gContainer *sender); +void CCONTAINER_cb_before_arrange(gContainer *sender); +void CCONTAINER_raise_insert(CCONTAINER *_object, CWIDGET *child); + +#endif diff --git a/gb.gtk/src/CDialog.cpp b/gb.gtk/src/CDialog.cpp new file mode 100644 index 00000000..4086f21a --- /dev/null +++ b/gb.gtk/src/CDialog.cpp @@ -0,0 +1,294 @@ +/*************************************************************************** + + CDialog.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDIALOG_CPP + +#include "CDialog.h" +#include "CFont.h" + +static GB_ARRAY dialog_filter = NULL; + + +BEGIN_METHOD_VOID(CDIALOG_exit) + + GB.StoreObject(NULL, POINTER(&dialog_filter)); + +END_METHOD + + +BEGIN_PROPERTY(Dialog_Title) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(gDialog::title()); + else + gDialog::setTitle(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_Filter) + + char **filters; + char *filter; + int i; + + if (READ_PROPERTY) + GB.ReturnObject(dialog_filter); + else + { + GB.StoreObject(PROP(GB_OBJECT), POINTER(&dialog_filter)); + GB.NewArray(&filters, sizeof(char *), 0); + if (dialog_filter) + { + for (i = 0; i < (GB.Array.Count(dialog_filter) - 1); i += 2) + { + filter = *((char **)GB.Array.Get(dialog_filter, i)); + if (filter && !strcmp(filter, "*")) + continue; + *((char **)GB.Add(&filters)) = filter; + filter = *((char **)GB.Array.Get(dialog_filter, i + 1)); + *((char **)GB.Add(&filters)) = filter; + } + } + + *((char **)GB.Add(&filters)) = (char *)"*"; + *((char **)GB.Add(&filters)) = GB.Translate("All Files"); + + gDialog::setFilter(filters, GB.Count(filters)); + GB.FreeArray(&filters); + } + +END_PROPERTY + +#if 0 +BEGIN_PROPERTY(Dialog_Filter) + + GB_ARRAY Array=NULL; + char **buf=NULL; + char *ctmp; + long count=0; + long bucle; + long tmp=0; + + if (READ_PROPERTY) + { + buf=gDialog::filter(&count); + if (buf) + { + GB.Array.New(&Array,GB_T_STRING,count); + for (bucle=0;buclecopy()); + else + ft = NULL; + + GB.ReturnObject(ft); + return; + } + + ft=(CFONT*)VPROP(GB_OBJECT); + if (ft && ft->font) + gDialog::setFont(ft->font); + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_Color) + + if (READ_PROPERTY) { GB.ReturnInteger( gDialog::color() ); return; } + gDialog::setColor(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_METHOD(Dialog_OpenFile,GB_BOOLEAN Multi;) + + bool Multi=false; + + if (!MISSING(Multi)) Multi=VARG(Multi); + + GB.ReturnBoolean( gDialog::openFile(Multi) ); + +END_METHOD + + +BEGIN_METHOD_VOID(Dialog_SaveFile) + + GB.ReturnBoolean( gDialog::saveFile() ); + +END_METHOD + + +BEGIN_METHOD_VOID(Dialog_SelectDirectory) + + GB.ReturnBoolean( gDialog::selectFolder() ); + +END_METHOD + + +BEGIN_METHOD_VOID(Dialog_SelectColor) + + GB.ReturnBoolean ( gDialog::selectColor() ); + +END_METHOD + + +BEGIN_METHOD_VOID(Dialog_SelectFont) + + GB.ReturnBoolean ( gDialog::selectFont() ); + +END_METHOD + + +GB_DESC CDialogDesc[] = +{ + GB_DECLARE("Dialog", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_exit", 0, CDIALOG_exit, 0), + + GB_STATIC_METHOD("OpenFile", "b", Dialog_OpenFile, "[(Multi)b]"), + GB_STATIC_METHOD("SaveFile", "b", Dialog_SaveFile, 0), + GB_STATIC_METHOD("SelectDirectory", "b", Dialog_SelectDirectory, 0), + GB_STATIC_METHOD("SelectColor", "b", Dialog_SelectColor, 0), + GB_STATIC_METHOD("SelectFont", "b", Dialog_SelectFont, 0), + + GB_STATIC_PROPERTY_READ("Paths", "String[]", Dialog_Paths), + + GB_STATIC_PROPERTY("Title", "s", Dialog_Title), + GB_STATIC_PROPERTY("Path", "s", Dialog_Path), + GB_STATIC_PROPERTY("Filter", "String[]", Dialog_Filter), + GB_STATIC_PROPERTY("Color", "i", Dialog_Color), + GB_STATIC_PROPERTY("Font", "Font", Dialog_Font), + GB_STATIC_PROPERTY("ShowHidden", "b", Dialog_ShowHidden), + + GB_END_DECLARE +}; + + diff --git a/gb.gtk/src/CDialog.h b/gb.gtk/src/CDialog.h new file mode 100644 index 00000000..b7272bc1 --- /dev/null +++ b/gb.gtk/src/CDialog.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + CDialog.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDIALOG_H +#define __CDIALOG_H + +#include "main.h" +#include "gdialog.h" + +#ifndef __CDIALOG_CPP +extern GB_DESC CDialogDesc[]; +#endif + +#endif diff --git a/gb.gtk/src/CDraw.cpp b/gb.gtk/src/CDraw.cpp new file mode 100644 index 00000000..51a79eb2 --- /dev/null +++ b/gb.gtk/src/CDraw.cpp @@ -0,0 +1,39 @@ +/*************************************************************************** + + CDraw.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDRAW_CPP + +#include "CWindow.h" +#include "CDrawingArea.h" +#include "CPicture.h" +#include "CImage.h" +#include "CFont.h" +#include "CDraw.h" +#include "gdesktop.h" + +DRAW_INTERFACE DRAW EXPORT; + +void DRAW_init() +{ + GB.GetInterface("gb.draw", DRAW_INTERFACE_VERSION, &DRAW); +} diff --git a/gb.gtk/src/CDraw.h b/gb.gtk/src/CDraw.h new file mode 100644 index 00000000..5aad01ec --- /dev/null +++ b/gb.gtk/src/CDraw.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + CDraw.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDRAW_H +#define __CDRAW_H + +#include "main.h" +#include "gb.draw.h" + +#ifndef __CDRAW_C + +extern DRAW_INTERFACE DRAW; + +#endif + +void DRAW_init(); + +#endif + diff --git a/gb.gtk/src/CDrawingArea.cpp b/gb.gtk/src/CDrawingArea.cpp new file mode 100644 index 00000000..dc422bc0 --- /dev/null +++ b/gb.gtk/src/CDrawingArea.cpp @@ -0,0 +1,258 @@ +/*************************************************************************** + + CDrawingArea.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDRAWINGAREA_CPP + +#include + +#include "main.h" +#include "gambas.h" +#include "widgets.h" +#include "CDraw.h" +#include "cpaint_impl.h" +#include "CDrawingArea.h" +#include "CWidget.h" +#include "CContainer.h" + +DECLARE_EVENT(EVENT_Draw); +DECLARE_EVENT(EVENT_Font); +DECLARE_EVENT(EVENT_Change); + + +/*************************************************************************** + + DrawingArea + +***************************************************************************/ + +void CDRAWINGAREA_send_change_event(void) +{ + GList *iter = g_list_first(gControl::controlList()); + gControl *control; + + while (iter) + { + control = (gControl *)iter->data; + if (control->getClass() == Type_gDrawingArea) + GB.Raise(control->hFree, EVENT_Change, 0); + iter = g_list_next(iter); + } +} + +static void cleanup_drawing(intptr_t _unused) +{ + PAINT_end(); +} + +#ifdef GTK3 +static void cb_expose(gDrawingArea *sender, cairo_t *cr) +{ + CWIDGET *_object = GetObject(sender); + GB_RAISE_HANDLER handler; + cairo_t *save; + + if (GB.CanRaise(THIS, EVENT_Draw)) + { + handler.callback = cleanup_drawing; + handler.data = (intptr_t)0; + + GB.RaiseBegin(&handler); + + save = THIS->context; + THIS->context = cr; + PAINT_begin(THIS); + + GB.Raise(THIS, EVENT_Draw, 0); + + PAINT_end(); + THIS->context = save; + + GB.RaiseEnd(&handler); + } +} +#else +static void cb_expose(gDrawingArea *sender, GdkRegion *region, int dx, int dy) +{ + CWIDGET *_object = GetObject(sender); + GB_RAISE_HANDLER handler; + + if (GB.CanRaise(THIS, EVENT_Draw)) + { + handler.callback = cleanup_drawing; + handler.data = (intptr_t)THIS; + + GB.RaiseBegin(&handler); + + PAINT_begin(THIS); + gdk_region_offset(region, -dx, -dy); + PAINT_clip_region(region); + gdk_region_offset(region, dx, dy); + + GB.Raise(THIS, EVENT_Draw, 0); + + PAINT_end(); + + GB.RaiseEnd(&handler); + } +} +#endif + +static void cb_font_change(gDrawingArea *sender) +{ + CWIDGET *_object = GetObject(sender); + GB.Raise(THIS, EVENT_Font, 0); +} + + +BEGIN_METHOD(CDRAWINGAREA_new, GB_OBJECT parent) + + InitControl(new gDrawingArea(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + WIDGET->onExpose = cb_expose; + WIDGET->onFontChange = cb_font_change; + +END_METHOD + +BEGIN_PROPERTY(DrawingArea_Border) + + if (READ_PROPERTY) { GB.ReturnInteger(WIDGET->getBorder()); return; } + WIDGET->setBorder(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(DrawingArea_Cached) + + if (READ_PROPERTY) { GB.ReturnBoolean(WIDGET->cached()); return; } + WIDGET->setCached(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(DrawingArea_Focus) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->canFocus()); + else + WIDGET->setCanFocus(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD_VOID(DrawingArea_Clear) + + if (DRAW.Paint.IsPainted(THIS)) + { + GB.Error("DrawingArea is being painted"); + return; + } + + WIDGET->clear(); + +END_METHOD + +BEGIN_PROPERTY(DrawingArea_Painted) + + static bool deprecated = false; + + if (!deprecated) + { + deprecated = true; + GB.Deprecated(GTK_NAME, "DrawingArea.Painted", NULL); + } + + if (READ_PROPERTY) + GB.ReturnBoolean(true); + +END_PROPERTY + +BEGIN_PROPERTY(DrawingArea_NoBackground) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->hasNoBackground()); + else + WIDGET->setNoBackground(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD(DrawingArea_Refresh, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + int x, y, w, h; + + if (MISSING(x) && MISSING(y) && MISSING(w) && MISSING(h)) + WIDGET->refresh(); + else + { + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + w = VARGOPT(w, WIDGET->width()); + h = VARGOPT(h, WIDGET->height()); + + WIDGET->refresh(x,y,w,h); + } + +END_METHOD + +BEGIN_PROPERTY(DrawingArea_Tablet) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->useTablet()); + else + WIDGET->setUseTablet(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +GB_DESC CDrawingAreaDesc[] = +{ + GB_DECLARE("DrawingArea", sizeof(CDRAWINGAREA)), GB_INHERITS("Container"), + + GB_METHOD("_new", 0, CDRAWINGAREA_new, "(Parent)Container;"), + + GB_PROPERTY("Arrangement", "i", Container_Arrangement), + GB_PROPERTY("AutoResize", "b", Container_AutoResize), + GB_PROPERTY("Padding", "i", Container_Padding), + GB_PROPERTY("Spacing", "b", Container_Spacing), + GB_PROPERTY("Margin", "b", Container_Margin), + GB_PROPERTY("Indent", "b", Container_Indent), + GB_PROPERTY("Invert", "b", Container_Invert), + + GB_PROPERTY("Cached", "b", DrawingArea_Cached), + GB_PROPERTY("Border", "i", DrawingArea_Border), + GB_PROPERTY("Focus","b",DrawingArea_Focus), + GB_PROPERTY("Painted", "b", DrawingArea_Painted), + GB_PROPERTY("NoBackground", "b", DrawingArea_NoBackground), + + GB_PROPERTY("Tablet", "b", DrawingArea_Tablet), + + GB_METHOD("Clear", NULL, DrawingArea_Clear, NULL), + GB_METHOD("Refresh", NULL, DrawingArea_Refresh, "[(X)i(Y)i(Width)i(Height)i]"), + + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + GB_EVENT("Font", NULL, NULL, &EVENT_Font), + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + + GB_INTERFACE("Paint", &PAINT_Interface), + + DRAWINGAREA_DESCRIPTION, + + GB_END_DECLARE +}; + + diff --git a/gb.gtk/src/CDrawingArea.h b/gb.gtk/src/CDrawingArea.h new file mode 100644 index 00000000..920c1129 --- /dev/null +++ b/gb.gtk/src/CDrawingArea.h @@ -0,0 +1,53 @@ +/*************************************************************************** + + CDrawingArea.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDRAWINGAREA_H +#define __CDRAWINGAREA_H + +#include "main.h" +#include "CWidget.h" +#include "gdrawingarea.h" + +#ifndef __CDRAWINGAREA_CPP +extern GB_DESC CDrawingAreaDesc[]; +#else + +#define THIS ((CDRAWINGAREA *)_object) +#define WIDGET ((gDrawingArea*)THIS->ob.widget) + +#endif + +typedef + struct + { + CWIDGET ob; +#ifdef GTK3 + cairo_t *context; +#endif + bool merge; + } + CDRAWINGAREA; + +void CDRAWINGAREA_send_change_event(void); + +#endif diff --git a/gb.gtk/src/CFont.cpp b/gb.gtk/src/CFont.cpp new file mode 100644 index 00000000..2a29f738 --- /dev/null +++ b/gb.gtk/src/CFont.cpp @@ -0,0 +1,421 @@ +/*************************************************************************** + + CFont.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CFONT_CPP + +#include +#include "CFont.h" +#include "gdesktop.h" +#include "ggambastag.h" + +#include "gb.form.font.h" + +CFONT *CFONT_create(gFont *font, FONT_FUNC func, void *object) +{ + CFONT *fnt; + + if (font && font->getTag()) + return (CFONT *)font->getTagValue(); + + fnt = (CFONT *)GB.New(GB.FindClass("Font"), 0, 0); + + if (font) + { + fnt->font->unref(); + fnt->font = font; + //font->ref(); + font->setTag(new gGambasTag((void *)fnt)); + } + + fnt->func = func; + fnt->object = object; + if (object) + GB.Ref(object); + + return fnt; +} + +BEGIN_METHOD(Font_new, GB_STRING font) + + if (!MISSING(font)) + FONT = new gFont(GB.ToZeroString(ARG(font))); + else + FONT = gDesktop::font()->copy(); + +END_METHOD + + +BEGIN_METHOD_VOID(Font_free) + + GB.Unref(POINTER(&THIS->object)); + gFont::assign(&THIS->font); + +END_METHOD + +static void CFONT_manage(int prop, CFONT *_object, void *_param) +{ + gFont *f = FONT; + double size; + + if (!f) + { + THIS->font = ((CWIDGET *)THIS->object)->widget->font()->copy(); + f = FONT; + } + + if (READ_PROPERTY) + { + switch(prop) + { + case CFONT::Name: GB.ReturnNewZeroString(f->name()); break; + case CFONT::Size: GB.ReturnFloat(f->size()); break; + case CFONT::Grade: GB.ReturnInteger(f->grade()); break; + case CFONT::Bold: GB.ReturnBoolean(f->bold()); break; + case CFONT::Italic: GB.ReturnBoolean(f->italic()); break; + case CFONT::Underline: GB.ReturnBoolean(f->underline()); break; + case CFONT::Strikeout: GB.ReturnBoolean(f->strikeout()); break; + } + } + else + { + switch (prop) + { + case CFONT::Name: f->setName(GB.ToZeroString(PROP(GB_STRING))); break; + case CFONT::Size: + size = VPROP(GB_FLOAT); + if (size <= 0) + { + GB.Error("Bad font size"); + return; + } + f->setSize(VPROP(GB_FLOAT)); + break; + case CFONT::Grade: f->setGrade(VPROP(GB_INTEGER)); break; + case CFONT::Bold: f->setBold(VPROP(GB_BOOLEAN)); break; + case CFONT::Italic: f->setItalic(VPROP(GB_BOOLEAN)); break; + case CFONT::Underline: f->setUnderline(VPROP(GB_BOOLEAN)); break; + case CFONT::Strikeout: f->setStrikeout(VPROP(GB_BOOLEAN)); break; + } + + if (THIS->func) + (*(THIS->func))(f, THIS->object); + else if (THIS->object) + { + // THIS->control->widget->setFont(*f); - Not needed anymore + // TODO Make a Gambas API to call SetProperty faster + + //fprintf(stderr, "applying font to (%s %p)\n", GB.GetClassName(THIS->object), THIS->object); + + GB_FUNCTION func; + + GB.GetFunction(&func, (void *)GB.FindClass("Object"), "SetProperty", NULL, NULL); + GB.Push(3, + GB_T_OBJECT, THIS->object, + GB_T_STRING, "Font", 4, + GB_T_OBJECT, THIS + ); + GB.Call(&func, 3, TRUE); + } + + THIS->modified = TRUE; + } +} + + +BEGIN_PROPERTY(Font_Name) + + CFONT_manage(CFONT::Name, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Size) + + CFONT_manage(CFONT::Size, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Grade) + + CFONT_manage(CFONT::Grade, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Bold) + + CFONT_manage(CFONT::Bold, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Italic) + + CFONT_manage(CFONT::Italic, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Underline) + + CFONT_manage(CFONT::Underline, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Strikeout) + + CFONT_manage(CFONT::Strikeout, OBJECT(CFONT), _param); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Font_ToString) + + GB.ReturnNewZeroString(FONT->toString()); + +END_METHOD + + +BEGIN_METHOD(Font_get, GB_STRING str) + + CFONT *font; + gFont *fnt; + + fnt = new gFont(GB.ToZeroString(ARG(str))); + font = CFONT_create(fnt); + //gFont::assign(&fnt); + + GB.ReturnObject(font); + +END_METHOD + +BEGIN_METHOD_VOID(Font_Copy) + + GB.ReturnObject(CFONT_create(FONT->copy())); + +END_METHOD + + +BEGIN_PROPERTY(Font_Ascent) + + GB.ReturnInteger(FONT->ascent()); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Descent) + + GB.ReturnInteger(FONT->descent()); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Height) + + GB.ReturnInteger(FONT->height()); + +END_PROPERTY + + +BEGIN_METHOD(Font_TextWidth, GB_STRING text) + + GB.ReturnInteger(FONT->width(STRING(text), LENGTH(text))); + +END_METHOD + + +BEGIN_METHOD(Font_TextHeight, GB_STRING text) + + GB.ReturnInteger(FONT->height(STRING(text), LENGTH(text))); + +END_METHOD + + +BEGIN_METHOD(Font_RichTextWidth, GB_STRING text) + + float w; + + FONT->richTextSize(STRING(text), LENGTH(text), -1, &w, NULL); + GB.ReturnInteger(ceil(w)); + +END_METHOD + + +BEGIN_METHOD(Font_RichTextHeight, GB_STRING text; GB_INTEGER width) + + float h; + + FONT->richTextSize(STRING(text), LENGTH(text), VARGOPT(width, -1), NULL, &h); + GB.ReturnInteger(ceil(h)); + +END_METHOD + + +BEGIN_PROPERTY(Font_Fixed) + + GB.ReturnBoolean(FONT->fixed()); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Fonts_next) + + int *pos; + + pos = (int *)GB.GetEnum(); + + if (pos[0] >= gFont::count()) + { + GB.StopEnum (); + return; + } + + GB.ReturnNewZeroString(gFont::familyItem(pos[0]++)); + +END_METHOD + + +BEGIN_METHOD(Fonts_Exist, GB_STRING family) + + int i; + char *family = GB.ToZeroString(ARG(family)); + + for (i = 0; i < gFont::count(); i++) + { + if (strcmp(gFont::familyItem(i), family) == 0) + { + GB.ReturnBoolean(TRUE); + return; + } + } + + GB.ReturnBoolean(FALSE); + +END_METHOD + + +BEGIN_PROPERTY(Fonts_Count) + + GB.ReturnInteger(gFont::count()); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Styles) + + GB_ARRAY array; + + GB.Array.New(&array, GB_T_STRING, 0); + *(char **)GB.Array.Add(array) = GB.NewZeroString("Regular"); + *(char **)GB.Array.Add(array) = GB.NewZeroString("Italic"); + *(char **)GB.Array.Add(array) = GB.NewZeroString("Bold"); + *(char **)GB.Array.Add(array) = GB.NewZeroString("Bold Italic"); + + GB.ReturnObject(array); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Scalable) + + GB.ReturnBoolean(FONT->scalable()); + +END_PROPERTY + +#if 0 +BEGIN_PROPERTY(Font_Underline) + + if (READ_PROPERTY) { GB.ReturnBoolean(FONT->underline()); return; } + FONT->setUnderline(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Strikeout) + + if (READ_PROPERTY) { GB.ReturnBoolean(FONT->strikeOut()); return; } + FONT->setStrikeOut(VPROP(GB_BOOLEAN)); + +END_PROPERTY +#endif + +BEGIN_PROPERTY(Font_Modified) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->modified); + else + THIS->modified = VPROP(GB_BOOLEAN); + +END_PROPERTY + + +//------------------------------------------------------------------------- + +GB_DESC CFontsDesc[] = +{ + GB_DECLARE_STATIC("Fonts"), + + GB_STATIC_METHOD("_next", "s", Fonts_next, NULL), + GB_STATIC_METHOD("Exist", "b", Fonts_Exist, "(Family)s"), + GB_STATIC_PROPERTY_READ("Count", "i", Fonts_Count), + + GB_END_DECLARE +}; + +GB_DESC CFontDesc[] = +{ + GB_DECLARE("Font", sizeof(CFONT)), + + GB_METHOD("_new", 0, Font_new, "[(Font)s]"), + GB_METHOD("_free", 0, Font_free, 0), + GB_METHOD("Copy", "Font", Font_Copy, NULL), + + GB_PROPERTY("Name", "s", Font_Name), + GB_PROPERTY("Size", "f", Font_Size), + GB_PROPERTY("Bold", "b", Font_Bold), + GB_PROPERTY("Italic", "b", Font_Italic), + GB_PROPERTY("Underline", "b", Font_Underline), + GB_PROPERTY("Strikeout", "b", Font_Strikeout), + GB_PROPERTY("Grade", "i", Font_Grade), + GB_PROPERTY("Modified", "b", Font_Modified), + + GB_METHOD("ToString", "s", Font_ToString, 0), + + GB_METHOD("TextWidth", "i", Font_TextWidth, "(Text)s"), + GB_METHOD("TextHeight", "i", Font_TextHeight, "(Text)s"), + + GB_METHOD("RichTextWidth", "i", Font_RichTextWidth, "(Text)s"), + GB_METHOD("RichTextHeight", "i", Font_RichTextHeight, "(Text)s[(Width)i]"), + + GB_STATIC_METHOD("_get", "Font", Font_get, "(Font)s"), + #if 0 + GB_STATIC_PROPERTY("Resolution", "i", CFONT_resolution), + #endif + GB_PROPERTY_READ("Ascent", "i", Font_Ascent), + GB_PROPERTY_READ("Descent", "i", Font_Descent), + GB_PROPERTY_READ("Height", "i", Font_Height), + GB_PROPERTY_READ("H", "i", Font_Height), + + GB_PROPERTY_READ("Fixed", "b", Font_Fixed), + GB_PROPERTY_READ("Scalable", "b", Font_Scalable), + GB_PROPERTY_READ("Styles", "String[]", Font_Styles), + + GB_END_DECLARE +}; + + diff --git a/gb.gtk/src/CFont.h b/gb.gtk/src/CFont.h new file mode 100644 index 00000000..34996da0 --- /dev/null +++ b/gb.gtk/src/CFont.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + CFont.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CFONT_H +#define __CFONT_H + +#include "main.h" +#include "gfont.h" + +#ifndef __CFONT_CPP +extern GB_DESC CFontDesc[]; +extern GB_DESC CFontsDesc[]; + +#else + +#define THIS ((CFONT *)_object) +#define FONT (THIS->font) + +#endif + +typedef + void (*FONT_FUNC)(gFont *, void *); + +typedef struct + { + GB_BASE ob; + gFont *font; + FONT_FUNC func; + void *object; + unsigned modified : 1; + enum { Name, Size, Grade, Bold, Italic, Underline, Strikeout }; + } + CFONT; + +CFONT *CFONT_create(gFont *font, FONT_FUNC func = 0, void *object = 0); + +#endif diff --git a/gb.gtk/src/CFrame.cpp b/gb.gtk/src/CFrame.cpp new file mode 100644 index 00000000..43eb7456 --- /dev/null +++ b/gb.gtk/src/CFrame.cpp @@ -0,0 +1,211 @@ +/*************************************************************************** + + CFrame.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CFRAME_CPP + +#include "CFrame.h" + + +BEGIN_METHOD(CFRAME_new, GB_OBJECT parent) + + InitControl(new gFrame(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + +END_METHOD + + +BEGIN_METHOD(CPANEL_new, GB_OBJECT parent) + + InitControl(new gPanel(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + +END_METHOD + + +BEGIN_METHOD(CHBOX_new, GB_OBJECT parent) + + InitControl(new gPanel(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + PANEL->setArrange(ARRANGE_HORIZONTAL); + //WIDGET->setAutoSize(true); + +END_METHOD + + +BEGIN_METHOD(CVBOX_new, GB_OBJECT parent) + + InitControl(new gPanel(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + PANEL->setArrange(ARRANGE_VERTICAL); + //WIDGET->setAutoSize(true); + +END_METHOD + + +BEGIN_METHOD(CHPANEL_new, GB_OBJECT parent) + + InitControl(new gPanel(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + PANEL->setArrange(ARRANGE_LEFT_RIGHT); + //WIDGET->setAutoSize(true); + +END_METHOD + + +BEGIN_METHOD(CVPANEL_new, GB_OBJECT parent) + + InitControl(new gPanel(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + PANEL->setArrange(ARRANGE_TOP_BOTTOM); + //WIDGET->setAutoSize(true); + +END_METHOD + +BEGIN_PROPERTY(CPANEL_border) + + if (READ_PROPERTY) { GB.ReturnInteger(PANEL->getBorder()); return; } + PANEL->setBorder(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CFRAME_text) + + if (READ_PROPERTY) { GB.ReturnNewZeroString( FRAME->text()); return; } + FRAME->setText(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +GB_DESC CFrameDesc[] = +{ + GB_DECLARE("Frame", sizeof(CFRAME)), GB_INHERITS("Container"), + + GB_METHOD("_new", 0, CFRAME_new, "(Parent)Container;"), + + GB_PROPERTY("Caption", "s", CFRAME_text), + GB_PROPERTY("Text", "s", CFRAME_text), + GB_PROPERTY("Title", "s", CFRAME_text), + GB_PROPERTY("Arrangement", "i", Container_Arrangement), + GB_PROPERTY("AutoResize", "b", Container_AutoResize), + GB_PROPERTY("Padding", "i", Container_Padding), + GB_PROPERTY("Spacing", "b", Container_Spacing), + GB_PROPERTY("Margin", "b", Container_Margin), + GB_PROPERTY("Indent", "b", Container_Indent), + GB_PROPERTY("Invert", "b", Container_Invert), + + FRAME_DESCRIPTION, + + GB_END_DECLARE +}; + +GB_DESC CPanelDesc[] = +{ + GB_DECLARE("Panel", sizeof(CFRAME)), GB_INHERITS("Container"), + + GB_METHOD("_new", 0, CPANEL_new, "(Parent)Container;"), + + GB_PROPERTY("Border", "i", CPANEL_border), + GB_PROPERTY("Arrangement", "i", Container_Arrangement), + GB_PROPERTY("AutoResize", "b", Container_AutoResize), + GB_PROPERTY("Padding", "i", Container_Padding), + GB_PROPERTY("Spacing", "b", Container_Spacing), + GB_PROPERTY("Margin", "b", Container_Margin), + GB_PROPERTY("Indent", "b", Container_Indent), + GB_PROPERTY("Invert", "b", Container_Invert), + + PANEL_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CHBoxDesc[] = +{ + GB_DECLARE("HBox", sizeof(CFRAME)), GB_INHERITS("Container"), + + GB_METHOD("_new", 0, CHBOX_new, "(Parent)Container;"), + + GB_PROPERTY("Spacing", "b", Container_Spacing), + GB_PROPERTY("Margin", "b", Container_Margin), + GB_PROPERTY("Padding", "i", Container_Padding), + GB_PROPERTY("AutoResize", "b", Container_AutoResize), + GB_PROPERTY("Indent", "b", Container_Indent), + GB_PROPERTY("Invert", "b", Container_Invert), + + HBOX_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CVBoxDesc[] = +{ + GB_DECLARE("VBox", sizeof(CFRAME)), GB_INHERITS("Container"), + + GB_METHOD("_new", 0, CVBOX_new, "(Parent)Container;"), + + GB_PROPERTY("Spacing", "b", Container_Spacing), + GB_PROPERTY("Margin", "b", Container_Margin), + GB_PROPERTY("Padding", "i", Container_Padding), + GB_PROPERTY("AutoResize", "b", Container_AutoResize), + GB_PROPERTY("Indent", "b", Container_Indent), + GB_PROPERTY("Invert", "b", Container_Invert), + + VBOX_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CHPanelDesc[] = +{ + GB_DECLARE("HPanel", sizeof(CFRAME)), GB_INHERITS("Container"), + + GB_METHOD("_new", 0, CHPANEL_new, "(Parent)Container;"), + + GB_PROPERTY("Spacing", "b", Container_Spacing), + GB_PROPERTY("Margin", "b", Container_Margin), + GB_PROPERTY("Padding", "i", Container_Padding), + GB_PROPERTY("AutoResize", "b", Container_AutoResize), + GB_PROPERTY("Indent", "b", Container_Indent), + GB_PROPERTY("Invert", "b", Container_Invert), + + HPANEL_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CVPanelDesc[] = +{ + GB_DECLARE("VPanel", sizeof(CFRAME)), GB_INHERITS("Container"), + + GB_METHOD("_new", 0, CVPANEL_new, "(Parent)Container;"), + + GB_PROPERTY("Spacing", "b", Container_Spacing), + GB_PROPERTY("Margin", "b", Container_Margin), + GB_PROPERTY("Padding", "i", Container_Padding), + GB_PROPERTY("AutoResize", "b", Container_AutoResize), + GB_PROPERTY("Indent", "b", Container_Indent), + GB_PROPERTY("Invert", "b", Container_Invert), + + VPANEL_DESCRIPTION, + + GB_END_DECLARE +}; + + + diff --git a/gb.gtk/src/CFrame.h b/gb.gtk/src/CFrame.h new file mode 100644 index 00000000..9792abae --- /dev/null +++ b/gb.gtk/src/CFrame.h @@ -0,0 +1,55 @@ +/*************************************************************************** + + CFrame.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CFRAME_H +#define __CFRAME_H + +#include "main.h" +#include "gframe.h" +#include "CWidget.h" +#include "CContainer.h" + +#ifndef __CFRAME_CPP +extern GB_DESC CFrameDesc[]; +extern GB_DESC CPanelDesc[]; +extern GB_DESC CHBoxDesc[]; +extern GB_DESC CVBoxDesc[]; +extern GB_DESC CHPanelDesc[]; +extern GB_DESC CVPanelDesc[]; +#else + +#define THIS ((CFRAME *)_object) + +#define FRAME ((gFrame *)(THIS->widget.widget)) +#define PANEL ((gPanel *)(THIS->widget.widget)) + +#endif + +typedef + struct + { + CWIDGET widget; + } + CFRAME; + +#endif diff --git a/gb.gtk/src/CImage.cpp b/gb.gtk/src/CImage.cpp new file mode 100644 index 00000000..7de49ac5 --- /dev/null +++ b/gb.gtk/src/CImage.cpp @@ -0,0 +1,285 @@ +/*************************************************************************** + + CImage.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CIMAGE_CPP + +#include "main.h" +#include "gambas.h" +#include "widgets.h" +#include "ggambastag.h" +#include "CDraw.h" +#include "cpaint_impl.h" +#include "CScreen.h" +#include "CPicture.h" +#include "CImage.h" + +static void free_image(GB_IMG *img, void *image) +{ + ((gPicture *)image)->unref(); +} + +static void *temp_image(GB_IMG *img) +{ + gPicture *image; + + if (!img->data) + image = new gPicture(); + else + image = gPicture::fromData((const char *)img->data, img->width, img->height); + + image->setTag(new gGambasTag((void *)img)); + + return image; +} + +static GB_IMG_OWNER _image_owner = { + "gb.gtk", + GB_IMAGE_RGBA, + free_image, + free_image, + temp_image, + NULL + }; + +gPicture *CIMAGE_get(CIMAGE *_object) +{ + return (gPicture *)IMAGE.Check(&THIS->img, &_image_owner); +} + +#define check_image CIMAGE_get + +static void take_image(CIMAGE *_object, gPicture *image) +{ + IMAGE.Take(&THIS->img, &_image_owner, image, image->width(), image->height(), image->data()); + + if (!image->getTag()) + image->setTag(new gGambasTag(THIS)); +} + +CIMAGE *CIMAGE_create(gPicture *image) +{ + CIMAGE *img; + static GB_CLASS class_id = 0; + + if (!class_id) + class_id = GB.FindClass("Image"); + + img = (CIMAGE *)GB.New(class_id, NULL, NULL); + + if (image) + take_image(img, image); + else + take_image(img, new gPicture()); + + return img; +} + +/*CIMAGE *CIMAGE_create(gPicture *picture) +{ + CIMAGE *pic; + GB.New((void **)POINTER(&pic), GB.FindClass("Image"), 0, 0); + if (picture) + { + pic->picture->unref(); + pic->picture = picture; + picture->setTag(new gGambasTag((void *)pic)); + } + return pic; +}*/ + +void* GTK_GetImage(GdkPixbuf *buf) +{ + CIMAGE *pic = CIMAGE_create(new gPicture(buf)); + g_object_ref(buf); + return (void*)pic; +} + +/******************************************************************************* + + Image + +*******************************************************************************/ + +#if 0 +BEGIN_METHOD(CIMAGE_new, GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN trans) + + int w = VARGOPT(w, 0); + int h = VARGOPT(h, 0); + bool trans = VARGOPT(trans, false); + + IMAGE = new gPicture(gPicture::MEMORY, w, h, trans); + PICTURE->setTag(new gGambasTag((void *)THIS)); + +END_METHOD + + +BEGIN_METHOD_VOID(CIMAGE_free) + + PICTURE->unref(); + +END_METHOD +#endif + +BEGIN_PROPERTY(Image_Picture) + + check_image(THIS); + + //GB.ReturnObject(THIS); + + CPICTURE *pic = CPICTURE_create(PICTURE->copy()); + GB.ReturnObject(pic); + +END_PROPERTY + + +BEGIN_METHOD(Image_Load, GB_STRING path) + + CIMAGE *image; + char *addr; + int len; + + if (!GB.LoadFile(STRING(path), LENGTH(path), &addr, &len)) + { + //fprintf(stderr, "Image_Load: %.*s\n", LENGTH(path), STRING(path)); + gPicture *pic = gPicture::fromMemory(addr, len); + GB.ReleaseFile(addr, len); + + if (pic) + { + image = CIMAGE_create(pic); + pic->getPixbuf(); + GB.ReturnObject(image); + return; + } + } + + GB.Error("Unable to load image"); + +END_METHOD + + +BEGIN_METHOD(Image_FromString, GB_STRING data) + + CIMAGE *image; + + gPicture *pic = gPicture::fromMemory(STRING(data), LENGTH(data)); + + if (pic) + { + image = CIMAGE_create(pic); + pic->getPixbuf(); + GB.ReturnObject(image); + return; + } + + GB.Error("Unable to load image"); + +END_METHOD + + +BEGIN_METHOD(Image_Save, GB_STRING path; GB_INTEGER quality) + + check_image(THIS); + + switch (PICTURE->save(GB.FileName(STRING(path), LENGTH(path)), VARGOPT(quality, -1))) + { + case 0: break; + case -1: GB.Error("Unknown format"); break; + case -2: GB.Error("Unable to save picture"); break; + } + +END_METHOD + + +BEGIN_METHOD(Image_Stretch, GB_INTEGER width; GB_INTEGER height) + + CIMAGE *img; + + check_image(THIS); + img = CIMAGE_create(PICTURE->stretch(VARG(width), VARG(height), true)); + GB.ReturnObject((void*)img); + +END_METHOD + + +BEGIN_METHOD(Image_Rotate, GB_FLOAT angle) + + CIMAGE *img; + + check_image(THIS); + img = CIMAGE_create(PICTURE->rotate(VARG(angle))); + GB.ReturnObject((void*)img); + +END_METHOD + + +BEGIN_METHOD(Image_PaintImage, GB_OBJECT img; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER sx; GB_INTEGER sy; GB_INTEGER sw; GB_INTEGER sh) + + int x, y, w, h, sx, sy, sw, sh; + CIMAGE *image = (CIMAGE *)VARG(img); + gPicture *src; + + if (GB.CheckObject(image)) + return; + + src = check_image(image); + check_image(THIS); + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + w = VARGOPT(w, -1); + h = VARGOPT(h, -1); + + sx = VARGOPT(sx, 0); + sy = VARGOPT(sy, 0); + sw = VARGOPT(sw, -1); + sh = VARGOPT(sh, -1); + + //DRAW_NORMALIZE(x, y, w, h, sx, sy, sw, sh, pic->width(), pic->height()); + + PICTURE->draw(src, x, y, w, h, sx, sy, sw, sh); + +END_METHOD + + +GB_DESC CImageDesc[] = +{ + GB_DECLARE("Image", sizeof(CIMAGE)), + + GB_STATIC_METHOD("Load", "Image", Image_Load, "(Path)s"), + GB_STATIC_METHOD("FromString", "Image", Image_FromString, "(Data)s"), + GB_METHOD("Save", 0, Image_Save, "(Path)s[(Quality)i]"), + + GB_METHOD("Stretch", "Image", Image_Stretch, "(Width)i(Height)i"), + GB_METHOD("Rotate", "Image", Image_Rotate, "(Angle)f"), + + GB_METHOD("PaintImage", 0, Image_PaintImage, "(Image)Image;(X)i(Y)i[(Width)i(Height)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + + GB_PROPERTY_READ("Picture", "Picture", Image_Picture), + + GB_INTERFACE("Paint", &PAINT_Interface), + GB_INTERFACE("PaintMatrix", &PAINT_MATRIX_Interface), + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/CImage.h b/gb.gtk/src/CImage.h new file mode 100644 index 00000000..ff019ced --- /dev/null +++ b/gb.gtk/src/CImage.h @@ -0,0 +1,55 @@ +/*************************************************************************** + + CImage.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CIMAGE_H +#define __CIMAGE_H + +#include "gambas.h" +#include "gb.image.h" +#include "widgets.h" + +typedef + struct + { + GB_IMG img; + } + CIMAGE; + +#ifndef __CIMAGE_CPP + +extern GB_DESC CImageDesc[]; + +#else + +#define THIS ((CIMAGE *)_object) +#define PICTURE ((gPicture *)THIS->img.temp_handle) +#define GET_PICTURE(_image) ((gPicture *)(_image->img.temp_handle)) + +#endif + +void *GTK_GetImage(GdkPixbuf *buf); + +CIMAGE *CIMAGE_create(gPicture *picture); +gPicture *CIMAGE_get(CIMAGE *); + +#endif diff --git a/gb.gtk/src/CKey.cpp b/gb.gtk/src/CKey.cpp new file mode 100644 index 00000000..3a51a2cb --- /dev/null +++ b/gb.gtk/src/CKey.cpp @@ -0,0 +1,184 @@ +/*************************************************************************** + + CKey.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CKEY_CPP + +#include "CKey.h" +#include +#include "gkey.h" + + +BEGIN_METHOD(Key_get, GB_STRING key) + + char *key = GB.ToZeroString(ARG(key)); + + if (!GB.GetProperty((void *)GB.FindClass("Key"), key)) + { + GB.Error(NULL); + GB.ReturnInteger(gKey::fromString(GB.ToZeroString(ARG(key)))); + } + +END_METHOD + +#define CHECK_VALID() \ + if (gKey::valid() == 0) \ + { \ + GB.Error("No keyboard event data"); \ + return; \ + } + +BEGIN_PROPERTY(Key_Text) + + CHECK_VALID(); + GB.ReturnNewZeroString(gKey::text()); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Code) + + CHECK_VALID(); + GB.ReturnInteger(gKey::code()); + +END_PROPERTY + +BEGIN_PROPERTY(Key_State) + + CHECK_VALID(); + GB.ReturnInteger(gKey::state()); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Shift) + + CHECK_VALID(); + GB.ReturnBoolean(gKey::shift()); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Control) + + CHECK_VALID(); + GB.ReturnBoolean(gKey::control()); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Alt) + + CHECK_VALID(); + GB.ReturnBoolean(gKey::alt()); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Meta) + + CHECK_VALID(); + GB.ReturnBoolean(gKey::meta()); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Normal) + + CHECK_VALID(); + GB.ReturnBoolean(gKey::normal()); + +END_PROPERTY + + + +GB_DESC CKeyDesc[] = +{ + GB_DECLARE("Key", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_get", "i", Key_get, "(Key)s"), + + GB_CONSTANT("Esc", "i", GDK_Escape), + GB_CONSTANT("Escape", "i", GDK_Escape), + GB_CONSTANT("Tab", "i", GDK_Tab), + GB_CONSTANT("BackTab", "i", GDK_ISO_Left_Tab), + GB_CONSTANT("BackSpace", "i", GDK_BackSpace), + GB_CONSTANT("Return", "i", GDK_Return), + GB_CONSTANT("Enter", "i", GDK_KP_Enter), + GB_CONSTANT("Ins", "i", GDK_Insert), + GB_CONSTANT("Del", "i", GDK_Delete), + GB_CONSTANT("Insert", "i", GDK_Insert), + GB_CONSTANT("Delete", "i", GDK_Delete), + GB_CONSTANT("Pause", "i", GDK_Pause), + GB_CONSTANT("Print", "i", GDK_Print), + GB_CONSTANT("SysReq", "i", GDK_Sys_Req), + GB_CONSTANT("Home", "i", GDK_Home), + GB_CONSTANT("End", "i", GDK_End), + GB_CONSTANT("Left", "i", GDK_Left), + GB_CONSTANT("Up", "i", GDK_Up), + GB_CONSTANT("Right", "i", GDK_Right), + GB_CONSTANT("Down", "i", GDK_Down), + GB_CONSTANT("PgUp", "i", GDK_Page_Up), + GB_CONSTANT("PgDown", "i", GDK_Page_Down), + GB_CONSTANT("PageUp", "i", GDK_Page_Up), + GB_CONSTANT("PageDown", "i", GDK_Page_Down), + GB_CONSTANT("ShiftKey", "i", GDK_Shift_L), + GB_CONSTANT("ControlKey", "i", GDK_Control_L), + GB_CONSTANT("MetaKey", "i", GDK_Meta_L), + GB_CONSTANT("AltKey", "i", GDK_Alt_L), + GB_CONSTANT("CapsLock", "i", GDK_Caps_Lock), + GB_CONSTANT("NumLock", "i", GDK_Num_Lock), + GB_CONSTANT("ScrollLock", "i", GDK_Scroll_Lock), + GB_CONSTANT("F1", "i", GDK_F1), + GB_CONSTANT("F2", "i", GDK_F2), + GB_CONSTANT("F3", "i", GDK_F3), + GB_CONSTANT("F4", "i", GDK_F4), + GB_CONSTANT("F5", "i", GDK_F5), + GB_CONSTANT("F6", "i", GDK_F6), + GB_CONSTANT("F7", "i", GDK_F7), + GB_CONSTANT("F8", "i", GDK_F8), + GB_CONSTANT("F9", "i", GDK_F9), + GB_CONSTANT("F10", "i", GDK_F10), + GB_CONSTANT("F11", "i", GDK_F11), + GB_CONSTANT("F12", "i", GDK_F12), + GB_CONSTANT("F13", "i", GDK_F13), + GB_CONSTANT("F14", "i", GDK_F14), + GB_CONSTANT("F15", "i", GDK_F15), + GB_CONSTANT("F16", "i", GDK_F16), + GB_CONSTANT("F17", "i", GDK_F17), + GB_CONSTANT("F18", "i", GDK_F18), + GB_CONSTANT("F19", "i", GDK_F19), + GB_CONSTANT("F20", "i", GDK_F20), + GB_CONSTANT("F21", "i", GDK_F21), + GB_CONSTANT("F22", "i", GDK_F22), + GB_CONSTANT("F23", "i", GDK_F23), + GB_CONSTANT("F24", "i", GDK_F24), + GB_CONSTANT("Menu", "i", GDK_Menu), + GB_CONSTANT("Help", "i", GDK_Help), + GB_CONSTANT("Space", "i", GDK_space), + + GB_STATIC_PROPERTY_READ("Text", "s", Key_Text), + GB_STATIC_PROPERTY_READ("Code", "i", Key_Code), + GB_STATIC_PROPERTY_READ("State", "i", Key_State), + GB_STATIC_PROPERTY_READ("Shift", "b", Key_Shift), + GB_STATIC_PROPERTY_READ("Control", "b", Key_Control), + GB_STATIC_PROPERTY_READ("Alt", "b", Key_Alt), + GB_STATIC_PROPERTY_READ("Meta", "b", Key_Meta), + GB_STATIC_PROPERTY_READ("Normal", "b", Key_Normal), + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/CKey.h b/gb.gtk/src/CKey.h new file mode 100644 index 00000000..289c01aa --- /dev/null +++ b/gb.gtk/src/CKey.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + CKey.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CKEY_H +#define __CKEY_H + +#include "main.h" + +#ifndef __CKEY_CPP +extern GB_DESC CKeyDesc[]; +#endif + + +#endif diff --git a/gb.gtk/src/CLabel.cpp b/gb.gtk/src/CLabel.cpp new file mode 100644 index 00000000..17dbf16c --- /dev/null +++ b/gb.gtk/src/CLabel.cpp @@ -0,0 +1,166 @@ +/*************************************************************************** + + CLabel.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CLABEL_CPP + +#include + +#include "gambas.h" +#include "widgets.h" +#include "CLabel.h" +#include "CContainer.h" + + +BEGIN_METHOD(CLABEL_new, GB_OBJECT parent) + + InitControl(new gLabel(CONTAINER(VARG(parent))),(CWIDGET*)THIS); + +END_METHOD + +BEGIN_METHOD(CTEXTLABEL_new, GB_OBJECT parent) + + InitControl(new gLabel(CONTAINER(VARG(parent))),(CWIDGET*)THIS); + + WIDGET->setWrap(true); + WIDGET->enableMarkup(true); + WIDGET->setAlignment(ALIGN_TOP_NORMAL); + +END_METHOD + +BEGIN_PROPERTY(CLABEL_auto_resize) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->autoResize()); + else + WIDGET->setAutoResize(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CLABEL_text) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(WIDGET->text()); + else + WIDGET->setText(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +BEGIN_PROPERTY(CLABEL_border) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->getFrameBorder()); + else + WIDGET->setFrameBorder(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CLABEL_alignment) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->alignment()); + else + WIDGET->setAlignment(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(CLABEL_transparent) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isTransparent()); + else + WIDGET->setTransparent(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CLABEL_padding) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->padding()); + else + WIDGET->setPadding(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_METHOD_VOID(CLABEL_adjust) + + WIDGET->adjust(); + +END_METHOD + +BEGIN_PROPERTY(Label_Wrap) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->wrap()); + else + WIDGET->setWrap(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + + +GB_DESC CLabelDesc[] = +{ + GB_DECLARE("Label", sizeof(CLABEL)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CLABEL_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", CLABEL_text), + GB_PROPERTY("Caption", "s", CLABEL_text), + GB_PROPERTY("Alignment", "i", CLABEL_alignment), + GB_PROPERTY("Border", "i", CLABEL_border), + GB_PROPERTY("AutoResize", "b", CLABEL_auto_resize), + GB_PROPERTY("Transparent","b",CLABEL_transparent), + GB_PROPERTY("Padding", "i", CLABEL_padding), + GB_METHOD("Adjust", 0, CLABEL_adjust, 0), + + LABEL_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CTextLabelDesc[] = +{ + GB_DECLARE("TextLabel", sizeof(CLABEL)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CTEXTLABEL_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", CLABEL_text), + GB_PROPERTY("Caption", "s", CLABEL_text), + GB_PROPERTY("Alignment", "i", CLABEL_alignment), + GB_PROPERTY("Border", "i", CLABEL_border), + GB_PROPERTY("AutoResize", "b", CLABEL_auto_resize), + GB_PROPERTY("Transparent","b",CLABEL_transparent), + GB_PROPERTY("Wrap","b",Label_Wrap), + GB_PROPERTY("Padding", "i", CLABEL_padding), + GB_METHOD("Adjust", 0, CLABEL_adjust, 0), + + TEXTLABEL_DESCRIPTION, + + GB_END_DECLARE +}; + + + diff --git a/gb.gtk/src/CLabel.h b/gb.gtk/src/CLabel.h new file mode 100644 index 00000000..7d78cad6 --- /dev/null +++ b/gb.gtk/src/CLabel.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + CLabel.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CLABEL_H +#define __CLABEL_H + +#include "main.h" +#include "CWidget.h" +#include "glabel.h" + +#ifndef __CLABEL_CPP +extern GB_DESC CLabelDesc[]; +extern GB_DESC CTextLabelDesc[]; + +#else + +#define THIS ((CLABEL *)_object) +#define WIDGET ((gLabel*)THIS->ob.widget) + +#endif + +typedef + struct + { + CWIDGET ob; + } + CLABEL; + +#endif diff --git a/gb.gtk/src/CMenu.cpp b/gb.gtk/src/CMenu.cpp new file mode 100644 index 00000000..18ff57fe --- /dev/null +++ b/gb.gtk/src/CMenu.cpp @@ -0,0 +1,600 @@ +/*************************************************************************** + + CMenu.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CMENU_CPP + +#include "main.h" +#include "gambas.h" +#include "CWidget.h" +#include "CWindow.h" +#include "CPicture.h" +#include "CMenu.h" + + +DECLARE_EVENT(EVENT_Click); +DECLARE_EVENT(EVENT_Show); +DECLARE_EVENT(EVENT_Hide); + +static CMENU *_popup_menu_clicked = NULL; +static GB_FUNCTION _init_shortcut_func; + +#define HANDLE_PROXY(_ob) \ + while (((CMENU *)(_ob))->proxy) \ + _ob = (__typeof__ _ob)(((CMENU *)(_ob))->proxy); + +static void register_proxy(void *_object, CMENU *proxy) +{ + CMENU *check = proxy; + + while (check) + { + if (check == THIS) + { + GB.Error("Circular proxy chain"); + return; + } + + check = (CMENU *)check->proxy; + } + + //if (THIS_EXT && THIS_EXT->proxy && EXT(THIS_EXT->proxy)) + // EXT(THIS_EXT->proxy)->proxy_for = NULL; + + + GB.Unref(POINTER(&THIS->proxy)); + + if (!MENU) + return; + + if (proxy) + { + GB.Ref(proxy); + THIS->proxy = proxy; + MENU->setProxy((gMenu *)proxy->widget); + } + else + MENU->setProxy(NULL); + +} + +static void send_click_event(void *_object) +{ + GB.Raise(THIS, EVENT_Click, 0); + CACTION_raise(THIS); + GB.Unref(POINTER(&_object)); +} + +static int CMENU_check(void *_object) +{ + return (MENU == NULL); +} + +#ifdef GTK3 + +static void delete_menu(gMenu *menu) +{ + delete menu; +} + +#else + +static void delete_later(gMenu *menu) +{ + delete menu; +} + +static void delete_menu(gMenu *menu) +{ + void *_object = menu->hFree; + + if (!MENU) + return; + + menu->willBeDeletedLater(); + THIS->widget = NULL; + + GB.Post((GB_CALLBACK)delete_later, (intptr_t)menu); +} + +#endif + +static void cb_finish(gMenu *sender) +{ + CMENU *_object = (CMENU*)sender->hFree; + if (_object) + { + CACTION_register(THIS, THIS->action, NULL); + GB.FreeString(&THIS->action); + THIS->widget = NULL; + GB.StoreVariant(NULL, POINTER(&THIS->tag)); + GB.Unref(POINTER(&_object)); + } +} + +static void cb_click(gMenu *sender) +{ + void *_object = sender->hFree; + + GB.Ref(THIS); + + if (gMenu::insidePopup()) + { + GB.Unref(POINTER(&_popup_menu_clicked)); + _popup_menu_clicked = THIS; + } + else + send_click_event(THIS); +} + +static void cb_show(gMenu *sender) +{ + static bool init = FALSE; + + void *_object = sender->hFree; + + GB.Ref(THIS); + + GB.Raise(THIS, EVENT_Show, 0); + + if (!THIS->init_shortcut) + { + if (!init) + { + GB.GetFunction(&_init_shortcut_func, (void *)GB.FindClass("_Gui"), "_DefineShortcut", NULL, NULL); + init = TRUE; + } + + THIS->init_shortcut = TRUE; + GB.Push(1, GB_T_OBJECT, THIS); + GB.Call(&_init_shortcut_func, 1, FALSE); + } + + GB.Unref(POINTER(&_object)); +} + +static void cb_hide(gMenu *sender) +{ + void *_object = sender->hFree; + GB.Raise(THIS, EVENT_Hide, 0); +} + +void CMENU_check_popup_click(void) +{ + if (_popup_menu_clicked) + { + CMENU *menu = _popup_menu_clicked; + _popup_menu_clicked = NULL; + send_click_event(menu); + } +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(Menu_new, GB_OBJECT parent; GB_BOOLEAN hidden) + + void *parent = VARG(parent); + bool hidden; + char *name; + + hidden = VARGOPT(hidden, false); + + if (GB.Is(parent, CLASS_Window)) + { + if (!((CWINDOW*)parent)->ob.widget) + { + GB.Error("Invalid window"); + return; + } + + THIS->widget = new gMenu((gMainWindow*)((CWINDOW*)parent)->ob.widget, hidden); + goto __OK; + } + + if (GB.Is(parent, CLASS_Menu)) + { + if ( !((CMENU*)parent)->widget ) + { + GB.Error("Invalid menu"); + return; + } + + THIS->widget = new gMenu((gMenu*)((CMENU*)parent)->widget, hidden); + MENU->onClick = cb_click; + goto __OK; + } + + GB.Error("Type mismatch. The parent control of a Menu must be a Window or another Menu."); + return; + +__OK: + + MENU->hFree = (void*)THIS; + MENU->onFinish = cb_finish; + MENU->onShow = cb_show; + MENU->onHide = cb_hide; + + name = GB.GetLastEventName(); + if (!name) + name = GB.GetClassName((void *)THIS); + + MENU->setName(name); + + GB.Ref((void*)THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(Menu_free) + + GB.FreeString(&THIS->save_text); + register_proxy(THIS, NULL); + if (MENU) MENU->destroy(); + +END_METHOD + + +BEGIN_PROPERTY(Menu_Text) + + if (READ_PROPERTY) + { + if (THIS->save_text) + GB.ReturnString(THIS->save_text); + else + GB.ReturnNewZeroString(MENU->text()); + return; + } + else + { + MENU->setText(GB.ToZeroString(PROP(GB_STRING))); + + if (!MENU->topLevel()) + ((CMENU *)GetObject((gMenu *)MENU->parent()))->init_shortcut = FALSE; + + GB.FreeString(&THIS->save_text); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Picture) + + if (READ_PROPERTY) + { + gPicture *pic = MENU->picture(); + GB.ReturnObject(pic ? pic->getTagValue() : 0); + } + else + { + CPICTURE *pic = (CPICTURE *)VPROP(GB_OBJECT); + MENU->setPicture(pic ? pic->picture : 0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Enabled) + + if (READ_PROPERTY) { GB.ReturnBoolean(MENU->enabled()); return; } + MENU->setEnabled(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Checked) + + if (READ_PROPERTY) + GB.ReturnBoolean(MENU->checked()); + else + MENU->setChecked(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Value) + + if (MENU->toggle() || MENU->radio()) + { + Menu_Checked(_object, _param); + return; + } + + if (READ_PROPERTY) + { + GB.ReturnBoolean(0); + } + else if (!MENU->topLevel()) + { + GB.Ref(THIS); + send_click_event(THIS); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Shortcut) + + if (READ_PROPERTY) + { + GB.ReturnNewZeroString(MENU->shortcut()); + return; + } + + MENU->setShortcut(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Visible) + + if (READ_PROPERTY) { GB.ReturnBoolean(MENU->isVisible()); return; } + MENU->setVisible(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD_VOID(Menu_Show) + + MENU->setVisible(true); + +END_METHOD + +BEGIN_METHOD_VOID(Menu_Hide) + + MENU->setVisible(false); + +END_METHOD + + +BEGIN_METHOD_VOID(Menu_Delete) + + delete_menu(MENU); + +END_METHOD + + +BEGIN_PROPERTY(MenuChildren_Count) + + GB.ReturnInteger(MENU->childCount()); + +END_PROPERTY + + +BEGIN_METHOD_VOID(MenuChildren_next) + + CMENU *Mn; + gMenu *mn; + int *ct; + + ct=(int*)GB.GetEnum(); + + if ( ct[0]>=MENU->childCount() ) { GB.StopEnum(); return; } + mn=MENU->childMenu(ct[0]); + Mn=(CMENU*)mn->hFree; + ct[0]++; + GB.ReturnObject(Mn); + +END_PROPERTY + + +BEGIN_METHOD(MenuChildren_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= MENU->childCount()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(MENU->childMenu(index)->hFree); + +END_METHOD + +BEGIN_METHOD_VOID(MenuChildren_Clear) + + while (MENU->childCount()) + delete_menu(MENU->childMenu(0)); + + THIS->init_shortcut = FALSE; + +END_PROPERTY + + +BEGIN_METHOD(Menu_Popup, GB_INTEGER x; GB_INTEGER y) + + HANDLE_PROXY(_object); + + if (!MISSING(x) && !MISSING(y)) + MENU->popup(VARG(x), VARG(y)); + else + MENU->popup(); + + CMENU_check_popup_click(); + +END_METHOD + +BEGIN_METHOD_VOID(Menu_Close) + + HANDLE_PROXY(_object); + MENU->close(); + +END_METHOD + + +BEGIN_PROPERTY(Menu_Tag) + + if (READ_PROPERTY) + GB.ReturnVariant(&THIS->tag); + else + GB.StoreVariant(PROP(GB_VARIANT), (void *)&THIS->tag); + +END_METHOD + +BEGIN_PROPERTY(Menu_Toggle) + + if (READ_PROPERTY) + GB.ReturnBoolean(MENU->toggle()); + else + MENU->setToggle(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Radio) + + if (READ_PROPERTY) + GB.ReturnBoolean(MENU->radio()); + else + MENU->setRadio(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Window) + + GB.ReturnObject(GetObject(MENU->window())); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Name) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(MENU->name()); + else + MENU->setName(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Action) + + if (READ_PROPERTY) + GB.ReturnString(THIS->action); + else + { + CACTION_register(THIS, THIS->action, GB.ToZeroString(PROP(GB_STRING))); + GB.StoreString(PROP(GB_STRING), &THIS->action); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_SaveText) + + if (READ_PROPERTY) + GB.ReturnString(THIS->save_text); + else + GB.StoreString(PROP(GB_STRING), &THIS->save_text); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Proxy) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->proxy); + else + { + CMENU *menu = (CMENU *)VPROP(GB_OBJECT); + + if (menu && GB.CheckObject(menu)) + return; + + register_proxy(THIS, menu); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Closed) + + HANDLE_PROXY(_object); + GB.ReturnBoolean(MENU->isClosed()); + +END_PROPERTY + + + +//--------------------------------------------------------------------------- + +GB_DESC CMenuChildrenDesc[] = +{ + GB_DECLARE(".Menu.Children", sizeof(CMENU)), GB_VIRTUAL_CLASS(), + + GB_METHOD("_next", "Menu", MenuChildren_next, 0), + GB_METHOD("_get", "Menu", MenuChildren_get, "(Index)i"), + GB_METHOD("Clear", 0, MenuChildren_Clear, 0), + GB_PROPERTY_READ("Count", "i", MenuChildren_Count), + + GB_END_DECLARE +}; + + +GB_DESC CMenuDesc[] = +{ + GB_DECLARE("Menu", sizeof(CMENU)), + GB_HOOK_CHECK(CMENU_check), + + //GB_STATIC_METHOD("_init", 0, CMENU_init, 0), + GB_METHOD("_new", 0, Menu_new, "(Parent)o[(Hidden)b]"), + GB_METHOD("_free", 0, Menu_free, 0), + + + GB_PROPERTY("Name", "s", Menu_Name), + GB_PROPERTY("Caption", "s", Menu_Text), + GB_PROPERTY("Text", "s", Menu_Text), + GB_PROPERTY("_Text", "s", Menu_SaveText), + GB_PROPERTY("Enabled", "b", Menu_Enabled), + GB_PROPERTY("Checked", "b", Menu_Checked), + GB_PROPERTY("Tag", "v", Menu_Tag), + GB_PROPERTY("Picture", "Picture", Menu_Picture), + GB_PROPERTY("Shortcut", "s", Menu_Shortcut), + GB_PROPERTY("Visible", "b", Menu_Visible), + GB_PROPERTY("Toggle", "b", Menu_Toggle), + GB_PROPERTY("Radio", "b", Menu_Radio), + GB_PROPERTY("Value", "b", Menu_Value), + //GB_PROPERTY("TearOff", "b", CMENU_tear_off), + GB_PROPERTY("Action", "s", Menu_Action), + GB_PROPERTY_READ("Window", "Window", Menu_Window), + GB_PROPERTY("Proxy", "Menu", Menu_Proxy), + + GB_PROPERTY_SELF("Children", ".Menu.Children"), + + MENU_DESCRIPTION, + + GB_METHOD("Popup", 0, Menu_Popup, "[(X)i(Y)i]"), + GB_METHOD("Close", NULL, Menu_Close, NULL), + GB_METHOD("Delete", 0, Menu_Delete, 0), + GB_METHOD("Show", 0, Menu_Show, 0), + GB_METHOD("Hide", 0, Menu_Hide, 0), + + GB_PROPERTY_READ("Closed", "b", Menu_Closed), + + GB_EVENT("Click", 0, 0, &EVENT_Click), + GB_EVENT("Show", 0, 0, &EVENT_Show), + GB_EVENT("Hide", 0, 0, &EVENT_Hide), + + GB_END_DECLARE +}; + + diff --git a/gb.gtk/src/CMenu.h b/gb.gtk/src/CMenu.h new file mode 100644 index 00000000..b004399a --- /dev/null +++ b/gb.gtk/src/CMenu.h @@ -0,0 +1,56 @@ +/*************************************************************************** + + CMenu.h + + (c) 2004-2005 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CMENU_H +#define __CMENU_H + +#include "main.h" +#include "gmenu.h" +#include "CPicture.h" + +#ifndef __CMENU_CPP +extern GB_DESC CMenuDesc[]; +extern GB_DESC CMenuChildrenDesc[]; +#else + +#define THIS ((CMENU*)_object) +#define MENU ((gMenu*)THIS->widget) + +#endif + +typedef + struct + { + GB_BASE ob; + gMenu *widget; + GB_VARIANT_VALUE tag; + char *action; + char *save_text; + void *proxy; + unsigned init_shortcut : 1; + } + CMENU; + +void CMENU_check_popup_click(void); + +#endif diff --git a/gb.gtk/src/CMessage.cpp b/gb.gtk/src/CMessage.cpp new file mode 100644 index 00000000..a8f6e2c8 --- /dev/null +++ b/gb.gtk/src/CMessage.cpp @@ -0,0 +1,136 @@ +/*************************************************************************** + + CMessage.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CMESSAGE_CPP + +#include "CMessage.h" + +static int _global_lock = 0; + +typedef + struct { + GB_STRING msg; + GB_STRING btn1; + GB_STRING btn2; + GB_STRING btn3; + } + MSG_PARAM; + +static void show_message_box(int type, MSG_PARAM *_p) +{ + char *msg = GB.ToZeroString(ARG(msg)); + char *btn1, *btn2, *btn3; + int ret; + char *title; + + btn1 = MISSING(btn1) ? NULL : GB.ToZeroString(ARG(btn1)); + btn2 = (type == 0 || MISSING(btn2)) ? NULL : GB.ToZeroString(ARG(btn2)); + btn3 = (type == 0 || MISSING(btn3)) ? NULL : GB.ToZeroString(ARG(btn3)); + + if (_global_lock) + { + GB.Error("Message box already displayed"); + return; + } + + _global_lock++; + + title = gMessage::title(); + if (!title) + title = GB.Application.Title(); + + switch (type) + { + case 0: ret = gMessage::showInfo(msg, btn1); break; + case 1: ret = gMessage::showWarning(msg, btn1, btn2, btn3); break; + case 2: ret = gMessage::showQuestion(msg, btn1, btn2, btn3); break; + case 3: ret = gMessage::showError(msg, btn1, btn2, btn3); break; + case 4: ret = gMessage::showDelete(msg, btn1, btn2, btn3); break; + default: ret = 0; + } + + gMessage::setTitle(NULL); + + GB.ReturnInteger(ret); + + _global_lock--; +} + +BEGIN_METHOD(CMESSAGE_info, GB_STRING msg; GB_STRING btn) + + show_message_box(0, (MSG_PARAM *)_p); + +END_METHOD + + +BEGIN_METHOD(CMESSAGE_warning, GB_STRING msg; GB_STRING btn1; GB_STRING btn2; GB_STRING btn3) + + show_message_box(1, (MSG_PARAM *)_p); + +END_METHOD + + +BEGIN_METHOD(CMESSAGE_question, GB_STRING msg; GB_STRING btn1; GB_STRING btn2; GB_STRING btn3) + + show_message_box(2, (MSG_PARAM *)_p); + +END_METHOD + + +BEGIN_METHOD(CMESSAGE_error, GB_STRING msg; GB_STRING btn1; GB_STRING btn2; GB_STRING btn3) + + show_message_box(3, (MSG_PARAM *)_p); + +END_METHOD + + +BEGIN_METHOD(CMESSAGE_delete, GB_STRING msg; GB_STRING btn1; GB_STRING btn2; GB_STRING btn3) + + show_message_box(4, (MSG_PARAM *)_p); + +END_METHOD + + +BEGIN_PROPERTY(CMESSAGE_title) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(gMessage::title()); + else + gMessage::setTitle(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +GB_DESC CMessageDesc[] = +{ + GB_DECLARE("Message", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_call", "i", CMESSAGE_info, "(Message)s[(Button)s]"), + GB_STATIC_METHOD("Info", "i", CMESSAGE_info, "(Message)s[(Button)s]"), + GB_STATIC_METHOD("Warning", "i", CMESSAGE_warning, "(Message)s[(Button1)s(Button2)s(Button3)s]"), + GB_STATIC_METHOD("Question", "i", CMESSAGE_question, "(Message)s[(Button1)s(Button2)s(Button3)s]"), + GB_STATIC_METHOD("Error", "i", CMESSAGE_error, "(Message)s[(Button1)s(Button2)s(Button3)s]"), + GB_STATIC_METHOD("Delete", "i", CMESSAGE_delete, "(Message)s[(Button1)s(Button2)s(Button3)s]"), + GB_STATIC_PROPERTY("Title", "s", CMESSAGE_title), + + GB_END_DECLARE +}; diff --git a/gb.gtk/src/CMessage.h b/gb.gtk/src/CMessage.h new file mode 100644 index 00000000..2c5a898a --- /dev/null +++ b/gb.gtk/src/CMessage.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + CMessage.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CMESSAGE_H +#define __CMESSAGE_H + +#include "main.h" +#include "gmessage.h" + +#ifndef __CMESSAGE_CPP +extern GB_DESC CMessageDesc[]; +#endif +#endif diff --git a/gb.gtk/src/CMouse.cpp b/gb.gtk/src/CMouse.cpp new file mode 100644 index 00000000..e7bfd70c --- /dev/null +++ b/gb.gtk/src/CMouse.cpp @@ -0,0 +1,432 @@ +/*************************************************************************** + + CMouse.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CMOUSE_CPP + +#include "CMouse.h" + +//------------------------------------------------------------------------- + +BEGIN_METHOD(CCURSOR_new, GB_OBJECT picture; GB_INTEGER x; GB_INTEGER y) + + CPICTURE *Pic=(CPICTURE*)VARG(picture); + gPicture *pic=NULL; + long X=0,Y=0; + + if (!MISSING(x)) X=VARG(x); + if (!MISSING(y)) Y=VARG(y); + if (Pic) pic=Pic->picture; + + THIS->cur=new gCursor(pic,X,Y); + +END_METHOD + + +BEGIN_METHOD_VOID(CCURSOR_delete) + + delete THIS->cur; + +END_METHOD + + +BEGIN_PROPERTY(CCURSOR_x) + + GB.ReturnInteger(THIS->cur->left()); + +END_PROPERTY + + +BEGIN_PROPERTY(CCURSOR_y) + + GB.ReturnInteger(THIS->cur->top()); + +END_PROPERTY + + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Mouse_ScreenX) + + GB.ReturnInteger(gMouse::screenX()); + +END_PROPERTY + + +BEGIN_PROPERTY(Mouse_ScreenY) + + GB.ReturnInteger(gMouse::screenY()); + +END_PROPERTY + + +BEGIN_METHOD(Mouse_Move, GB_INTEGER x; GB_INTEGER y) + + gMouse::move(VARG(x),VARG(y)); + +END_PROPERTY + +#define CHECK_VALID() \ + if (!gMouse::isValid()) \ + { \ + GB.Error("No mouse event data"); \ + return; \ + } + +BEGIN_PROPERTY(Mouse_X) + + CHECK_VALID(); + GB.ReturnInteger(gMouse::x()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Y) + + CHECK_VALID(); + GB.ReturnInteger(gMouse::y()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_StartX) + + CHECK_VALID(); + GB.ReturnInteger(gMouse::startX()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_StartY) + + CHECK_VALID(); + GB.ReturnInteger(gMouse::startY()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Button) + + CHECK_VALID(); + GB.ReturnInteger(gMouse::button()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_State) + + CHECK_VALID(); + + int state = gMouse::state(); + int result = 0; + + if (state & GDK_BUTTON1_MASK) + result |= MOUSE_LEFT; + if (state & GDK_BUTTON2_MASK) + result |= MOUSE_MIDDLE; + if (state & GDK_BUTTON3_MASK) + result |= MOUSE_RIGHT; + if (state & GDK_SHIFT_MASK) + result |= MOUSE_SHIFT; + if (state & GDK_CONTROL_MASK) + result |= MOUSE_CTRL; + if (state & GDK_MOD1_MASK) + result |= MOUSE_ALT; + if (state & GDK_MOD2_MASK) + result |= MOUSE_META; + + GB.ReturnInteger(result); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Left) + + CHECK_VALID(); + GB.ReturnBoolean(gMouse::left()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Right) + + CHECK_VALID(); + GB.ReturnBoolean(gMouse::right()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Middle) + + CHECK_VALID(); + GB.ReturnBoolean(gMouse::middle()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Shift) + + CHECK_VALID(); + GB.ReturnBoolean(gMouse::shift()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Control) + + CHECK_VALID(); + GB.ReturnBoolean(gMouse::control()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Alt) + + CHECK_VALID(); + GB.ReturnBoolean(gMouse::alt()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Meta) + + CHECK_VALID(); + GB.ReturnBoolean(gMouse::meta()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Normal) + + CHECK_VALID(); + GB.ReturnBoolean(gMouse::normal()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Delta) + + CHECK_VALID(); + GB.ReturnInteger(gMouse::delta()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Orientation) + + CHECK_VALID(); + GB.ReturnInteger(gMouse::orientation()); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Forward) + + CHECK_VALID(); + GB.ReturnBoolean(gMouse::delta() > 0); + +END_PROPERTY + +BEGIN_METHOD(Mouse_Inside, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + gControl *widget; + int x, y, xw, yw; + + if (GB.CheckObject(control)) + return; + + widget = control->widget; + + if (!widget->isVisible()) + { + GB.ReturnBoolean(false); + return; + } + + gMouse::getScreenPos(&x, &y); + widget->getScreenPos(&xw, &yw); + x -= xw; + y -= yw; + GB.ReturnBoolean(x >= 0 && x < widget->width() && y >= 0 && y < widget->height()); + +END_METHOD + +BEGIN_METHOD(Mouse_Translate, GB_INTEGER dx; GB_INTEGER dy) + + CHECK_VALID(); + gMouse::translate(VARG(dx), VARG(dy)); + +END_METHOD + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Pointer_ScreenX) + + CHECK_VALID(); + GB.ReturnFloat(gMouse::getPointerScreenX()); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_ScreenY) + + CHECK_VALID(); + GB.ReturnFloat(gMouse::getPointerScreenY()); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_X) + + CHECK_VALID(); + GB.ReturnFloat(gMouse::getPointerX()); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_Y) + + CHECK_VALID(); + GB.ReturnFloat(gMouse::getPointerY()); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_XTilt) + + CHECK_VALID(); + GB.ReturnFloat(gMouse::getAxis(GDK_AXIS_XTILT)); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_YTilt) + + CHECK_VALID(); + GB.ReturnFloat(gMouse::getAxis(GDK_AXIS_YTILT)); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_Pressure) + + CHECK_VALID(); + GB.ReturnFloat(gMouse::getAxis(GDK_AXIS_PRESSURE)); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_Rotation) + + CHECK_VALID(); + GB.ReturnFloat(gMouse::getAxis(GDK_AXIS_WHEEL)); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_Type) + + CHECK_VALID(); + GB.ReturnInteger(gMouse::getType()); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC CCursorDesc[] = +{ + GB_DECLARE("Cursor", sizeof(CCURSOR)), + + GB_METHOD("_new", 0, CCURSOR_new, "(Picture)Picture;[(X)i(Y)i]"), + GB_METHOD("_free", 0, CCURSOR_delete, NULL), + + GB_PROPERTY_READ("X", "i", CCURSOR_x), + GB_PROPERTY_READ("Y", "i", CCURSOR_y), + + GB_END_DECLARE +}; + + +GB_DESC CMouseDesc[] = +{ + GB_DECLARE_VIRTUAL("Mouse"), + + GB_STATIC_PROPERTY_READ("ScreenX", "i", Mouse_ScreenX), + GB_STATIC_PROPERTY_READ("ScreenY", "i", Mouse_ScreenY), + GB_STATIC_METHOD("Move", 0, Mouse_Move, "(X)i(Y)i"), + GB_STATIC_METHOD("Inside", "b", Mouse_Inside, "(Control)Control"), + + GB_CONSTANT("Default", "i", CURSOR_DEFAULT), + GB_CONSTANT("Custom", "i", CURSOR_CUSTOM), + GB_CONSTANT("Blank", "i", GDK_BLANK_CURSOR), + GB_CONSTANT("Arrow", "i", GDK_LEFT_PTR), + GB_CONSTANT("Cross", "i", GDK_CROSSHAIR), + GB_CONSTANT("Wait", "i", GDK_WATCH), + GB_CONSTANT("Text", "i", GDK_XTERM), + GB_CONSTANT("SizeAll", "i", GDK_FLEUR), + GB_CONSTANT("SizeH", "i", GDK_SB_H_DOUBLE_ARROW), + GB_CONSTANT("SizeV", "i", GDK_SB_V_DOUBLE_ARROW), + GB_CONSTANT("SizeN", "i", GDK_TOP_SIDE), + GB_CONSTANT("SizeS", "i", GDK_BOTTOM_SIDE), + GB_CONSTANT("SizeW", "i", GDK_LEFT_SIDE), + GB_CONSTANT("SizeE", "i", GDK_RIGHT_SIDE), + //GB_CONSTANT("SizeNW", "i", GDK_LAST_CURSOR+1), //FDiag + GB_CONSTANT("SizeNW", "i", GDK_TOP_LEFT_CORNER), //FDiag + //GB_CONSTANT("SizeSE", "i", GDK_LAST_CURSOR+1), + GB_CONSTANT("SizeSE", "i", GDK_BOTTOM_RIGHT_CORNER), + //GB_CONSTANT("SizeNE", "i", GDK_LAST_CURSOR+2), //BDiag + GB_CONSTANT("SizeNE", "i", GDK_TOP_RIGHT_CORNER), //BDiag + //GB_CONSTANT("SizeSW", "i", GDK_LAST_CURSOR+2), + GB_CONSTANT("SizeSW", "i", GDK_BOTTOM_LEFT_CORNER), + GB_CONSTANT("SizeNWSE", "i", GDK_LAST_CURSOR+1), + GB_CONSTANT("SizeNESW", "i", GDK_LAST_CURSOR+2), + //GB_CONSTANT("SplitH", "i", GDK_LAST_CURSOR+3), // SplitH + //GB_CONSTANT("SplitV", "i", GDK_LAST_CURSOR+4), // SplitV + GB_CONSTANT("SplitH", "i", GDK_SB_H_DOUBLE_ARROW), // SplitH + GB_CONSTANT("SplitV", "i", GDK_SB_V_DOUBLE_ARROW), // SplitV + GB_CONSTANT("Pointing", "i", GDK_HAND2), + + GB_CONSTANT("Horizontal", "i", 0), + GB_CONSTANT("Vertical", "i", 1), + + GB_STATIC_PROPERTY_READ("X", "i", Mouse_X), + GB_STATIC_PROPERTY_READ("Y", "i", Mouse_Y), + GB_STATIC_PROPERTY_READ("StartX", "i", Mouse_StartX), + GB_STATIC_PROPERTY_READ("StartY", "i", Mouse_StartY), + GB_STATIC_PROPERTY_READ("Left", "b", Mouse_Left), + GB_STATIC_PROPERTY_READ("Right", "b", Mouse_Right), + GB_STATIC_PROPERTY_READ("Middle", "b", Mouse_Middle), + GB_STATIC_PROPERTY_READ("Button", "i", Mouse_Button), + GB_STATIC_PROPERTY_READ("State", "i", Mouse_State), + GB_STATIC_PROPERTY_READ("Shift", "b", Mouse_Shift), + GB_STATIC_PROPERTY_READ("Control", "b", Mouse_Control), + GB_STATIC_PROPERTY_READ("Alt", "b", Mouse_Alt), + GB_STATIC_PROPERTY_READ("Meta", "b", Mouse_Meta), + GB_STATIC_PROPERTY_READ("Normal", "b", Mouse_Normal), + GB_STATIC_PROPERTY_READ("Orientation", "i", Mouse_Orientation), + GB_STATIC_PROPERTY_READ("Delta", "f", Mouse_Delta), + GB_STATIC_PROPERTY_READ("Forward", "b", Mouse_Forward), + + GB_STATIC_METHOD("Translate", NULL, Mouse_Translate, "(DX)i(DY)i"), + + GB_END_DECLARE +}; + +GB_DESC CPointerDesc[] = +{ + GB_DECLARE_VIRTUAL("Pointer"), + + GB_CONSTANT("Mouse", "i", POINTER_MOUSE), + GB_CONSTANT("Pen", "i", POINTER_PEN), + GB_CONSTANT("Eraser", "i", POINTER_ERASER), + GB_CONSTANT("Cursor", "i", POINTER_CURSOR), + + GB_STATIC_PROPERTY_READ("Type", "i", Pointer_Type), + GB_STATIC_PROPERTY_READ("X", "f", Pointer_X), + GB_STATIC_PROPERTY_READ("Y", "f", Pointer_Y), + GB_STATIC_PROPERTY_READ("ScreenX", "f", Pointer_ScreenX), + GB_STATIC_PROPERTY_READ("ScreenY", "f", Pointer_ScreenY), + GB_STATIC_PROPERTY_READ("XTilt", "f", Pointer_XTilt), + GB_STATIC_PROPERTY_READ("YTilt", "f", Pointer_YTilt), + GB_STATIC_PROPERTY_READ("Pressure", "f", Pointer_Pressure), + GB_STATIC_PROPERTY_READ("Rotation", "f", Pointer_Rotation), + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/CMouse.h b/gb.gtk/src/CMouse.h new file mode 100644 index 00000000..305392ca --- /dev/null +++ b/gb.gtk/src/CMouse.h @@ -0,0 +1,54 @@ +/*************************************************************************** + + CMouse.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CMOUSE_H +#define __CMOUSE_H + +#include "main.h" +#include "CPicture.h" +#include "gmouse.h" + +#ifndef __CMOUSE_CPP + +extern GB_DESC CMouseDesc[]; +extern GB_DESC CCursorDesc[]; +extern GB_DESC CPointerDesc[]; + +#else + +#define THIS ((CCURSOR *)_object) + +#endif + +typedef + struct + { + GB_BASE ob; + gCursor *cur; + } + CCURSOR; + +#define MOUSE_CONSTANTS \ + "" +#endif diff --git a/gb.gtk/src/CMovieBox.cpp b/gb.gtk/src/CMovieBox.cpp new file mode 100644 index 00000000..97df3147 --- /dev/null +++ b/gb.gtk/src/CMovieBox.cpp @@ -0,0 +1,135 @@ +/*************************************************************************** + + CMovieBox.cpp + + (c) 2004-2006 - Daniel Campos Fernández + (c) 2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CMOVIEBOX_CPP + + +#include "main.h" +#include "gambas.h" +#include "widgets.h" + +#include "CMovieBox.h" +#include "CPicture.h" +#include "CContainer.h" + +#include + + +BEGIN_METHOD(CMOVIEBOX_new, GB_OBJECT parent) + + InitControl(new gMovieBox(CONTAINER(VARG(parent))),(CWIDGET*)MTHIS); + +END_METHOD + +BEGIN_METHOD_VOID(CMOVIEBOX_free) + + if (MTHIS->path) GB.Free(POINTER(&MTHIS->path)); + +END_METHOD + + +BEGIN_PROPERTY(CMOVIEBOX_border) + + if (READ_PROPERTY) { GB.ReturnInteger(MBOX->getBorder()); return; } + MBOX->setBorder(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(CMOVIEBOX_path) + + char *addr; + char *name; + int len; + + if (READ_PROPERTY) + { + GB.ReturnNewZeroString(MTHIS->path); + return; + } + + name=GB.ToZeroString(PROP(GB_STRING)); + if (GB.LoadFile (name,strlen(name),&addr,&len)) + { + GB.Error("File or directory does not exist"); + return; + } + + if (MTHIS->path) { GB.Free(POINTER(&MTHIS->path)); MTHIS->path=NULL; } + GB.Alloc(POINTER(&MTHIS->path),strlen(name)+1); + strcpy(MTHIS->path,name); + MBOX->loadMovie(addr,len); + GB.ReleaseFile(addr,len); + +END_PROPERTY + + +BEGIN_PROPERTY(CMOVIEBOX_playing) + + if (READ_PROPERTY) { GB.ReturnBoolean(MBOX->playing()); return; } + MBOX->setPlaying(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CMOVIEBOX_rewind) + + if (MBOX->playing()) + { + MBOX->setPlaying(false); + MBOX->setPlaying(true); + } + +END_METHOD + +BEGIN_PROPERTY(MovieBox_Alignment) + + if (READ_PROPERTY) + GB.ReturnInteger(MBOX->alignment()); + else + MBOX->setAlignment(VPROP(GB_INTEGER)); + +END_PROPERTY + + +GB_DESC CMovieBoxDesc[] = +{ + GB_DECLARE("MovieBox", sizeof(CMOVIEBOX)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CMOVIEBOX_new, "(Parent)Container;"), + GB_METHOD("_free", 0, CMOVIEBOX_free,0), + GB_METHOD("Rewind", 0, CMOVIEBOX_rewind, 0), + + GB_PROPERTY("Path", "s", CMOVIEBOX_path), + GB_PROPERTY("Playing", "b", CMOVIEBOX_playing), + GB_PROPERTY("Border", "i", CMOVIEBOX_border), + GB_PROPERTY("Alignment", "i", MovieBox_Alignment), + + MOVIEBOX_DESCRIPTION, + + GB_END_DECLARE +}; + + + diff --git a/gb.gtk/src/CMovieBox.h b/gb.gtk/src/CMovieBox.h new file mode 100644 index 00000000..fd8690b2 --- /dev/null +++ b/gb.gtk/src/CMovieBox.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + CMovieBox.h + + (c) 2004-2006 - Daniel Campos Fernández + (c) 2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CMOVIEBOX_H +#define __CMOVIEBOX_H + +#include "main.h" +#include "CWidget.h" +#include "CPicture.h" +#include "gmoviebox.h" + +#ifndef __CMOVIEBOX_CPP +extern GB_DESC CMovieBoxDesc[]; +#else + +#define MTHIS ((CMOVIEBOX *)_object) +#define MBOX ((gMovieBox*)MTHIS->ob.widget) + +#endif + +typedef + struct + { + CWIDGET ob; + char *path; + } + CMOVIEBOX; + +#endif diff --git a/gb.gtk/src/CPicture.cpp b/gb.gtk/src/CPicture.cpp new file mode 100644 index 00000000..d9a7958f --- /dev/null +++ b/gb.gtk/src/CPicture.cpp @@ -0,0 +1,276 @@ +/*************************************************************************** + + CPicture.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPICTURE_CPP + +#include "main.h" +#include "gambas.h" +#include "widgets.h" +#include "ggambastag.h" +#include "CDraw.h" +#include "cpaint_impl.h" +#include "CScreen.h" +#include "CImage.h" +#include "CPicture.h" + +CPICTURE *CPICTURE_create(gPicture *picture) +{ + CPICTURE *pic; + + pic = (CPICTURE *)GB.New(GB.FindClass("Picture"), 0, 0); + if (picture) + { + pic->picture->unref(); + pic->picture = picture; + picture->setTag(new gGambasTag((void *)pic)); + } + return pic; +} + +void* GTK_GetPicture(GdkPixbuf *buf) +{ + CPICTURE *pic = CPICTURE_create(new gPicture(buf)); + g_object_ref(buf); + return (void*)pic; +} + + +bool CPICTURE_load_image(gPicture **p, const char *path, int lenp) +{ + char *addr; + int len; + + *p = NULL; + + if (GB.LoadFile(path, lenp, &addr, &len)) + { + GB.Error(NULL); + return FALSE; + } + + *p = gPicture::fromMemory(addr, len); + GB.ReleaseFile(addr, len); + return *p != NULL; +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Picture_new, GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN trans) + + int w = VARGOPT(w, 0); + int h = VARGOPT(h, 0); + bool trans = VARGOPT(trans, false); + +#ifdef GTK3 + PICTURE = new gPicture(gPicture::SURFACE, w, h, trans); +#else + PICTURE = new gPicture(gPicture::PIXMAP, w, h, trans); +#endif + PICTURE->setTag(new gGambasTag(THIS)); + +END_METHOD + + +BEGIN_METHOD_VOID(Picture_free) + + if (PICTURE) PICTURE->unref(); + +END_METHOD + + +BEGIN_METHOD(Picture_Resize, GB_INTEGER width; GB_INTEGER height) + + PICTURE->resize(VARG(width),VARG(height)); + +END_METHOD + + +BEGIN_PROPERTY(Picture_Width) + + GB.ReturnInteger(PICTURE->width()); + +END_PROPERTY + + +BEGIN_PROPERTY(Picture_Height) + + GB.ReturnInteger(PICTURE->height()); + +END_PROPERTY + + +BEGIN_PROPERTY(Picture_Depth) + + GB.ReturnInteger(PICTURE->depth()); + +END_PROPERTY + + +BEGIN_METHOD(Picture_Load, GB_STRING path) + + CPICTURE *picture; + char *addr; + int len; + + if (!GB.LoadFile(STRING(path), LENGTH(path), &addr, &len)) + { + gPicture *pic = gPicture::fromMemory(addr, len); + GB.ReleaseFile(addr, len); + + if (pic) + { + picture = CPICTURE_create(pic); + GB.ReturnObject(picture); + return; + } + } + + GB.Error("Unable to load picture"); + +END_METHOD + +BEGIN_METHOD(Picture_FromString, GB_STRING data) + + CPICTURE *picture; + + gPicture *pic = gPicture::fromMemory(STRING(data), LENGTH(data)); + + if (pic) + { + picture = CPICTURE_create(pic); + GB.ReturnObject(picture); + return; + } + + GB.Error("Unable to load picture"); + +END_METHOD + +/* +BEGIN_METHOD(CPICTURE_fromMemory,GB_STRING data;) + + CPICTURE *pic=NULL; + + if (!LENGTH(data)) return; + + GB.New((void **)&pic, GB.FindClass("Picture"), 0, 0); + pic->picture->fromMemory(STRING(data),LENGTH(data)); + GB.ReturnObject(pic); + + +END_METHOD +*/ + +BEGIN_METHOD(Picture_Save, GB_STRING path; GB_INTEGER quality) + + switch (PICTURE->save(GB.FileName(STRING(path), LENGTH(path)), VARGOPT(quality, -1))) + { + case 0: break; + case -1: GB.Error("Unknown format"); break; + case -2: GB.Error("Unable to save picture"); break; + } + +END_METHOD + + +BEGIN_METHOD_VOID(Picture_Clear) + + PICTURE->clear(); + +END_METHOD + + +BEGIN_METHOD(Picture_Fill, GB_INTEGER col) + + PICTURE->fill(VARG(col)); + +END_METHOD + + +BEGIN_METHOD(Picture_Copy, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + CPICTURE *pic=NULL; + int x=0; + int y=0; + int w=PICTURE->width(); + int h=PICTURE->height(); + + if (!MISSING(x)) x=VARG(x); + if (!MISSING(y)) y=VARG(y); + if (!MISSING(w)) w=VARG(w); + if (!MISSING(h)) h=VARG(h); + + pic = CPICTURE_create(PICTURE->copy(x, y, w, h)); + GB.ReturnObject(pic); + +END_METHOD + + +BEGIN_PROPERTY(Picture_Image) + + CIMAGE *img = CIMAGE_create(PICTURE->copy()); + //CIMAGE_get(img)->getPixbuf(); + GB.ReturnObject((void*)img); + +END_PROPERTY + +BEGIN_PROPERTY(Picture_Transparent) + + if (READ_PROPERTY) { GB.ReturnBoolean(PICTURE->isTransparent()); return; } + PICTURE->setTransparent(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +GB_DESC CPictureDesc[] = +{ + GB_DECLARE("Picture", sizeof(CPICTURE)), + + //GB_STATIC_METHOD("_exit", NULL, CPICTURE_flush, NULL), + + GB_METHOD("_new", NULL, Picture_new, "[(Width)i(Height)i(Transparent)b]"), + GB_METHOD("_free", NULL, Picture_free, NULL), + + GB_PROPERTY_READ("Width", "i", Picture_Width), + GB_PROPERTY_READ("Height", "i", Picture_Height), + GB_PROPERTY_READ("W", "i", Picture_Width), + GB_PROPERTY_READ("H", "i", Picture_Height), + GB_PROPERTY_READ("Depth", "i", Picture_Depth), + GB_PROPERTY("Transparent", "b", Picture_Transparent), + + GB_STATIC_METHOD("Load", "Picture", Picture_Load, "(Path)s"), + GB_STATIC_METHOD("FromString", "Picture", Picture_FromString, "(Data)s"), + GB_METHOD("Save", NULL, Picture_Save, "(Path)s[(Quality)i]"), + GB_METHOD("Resize", NULL, Picture_Resize, "(Width)i(Height)i"), + + GB_METHOD("Clear", NULL, Picture_Clear, NULL), + GB_METHOD("Fill", NULL, Picture_Fill, "(Color)i"), + + GB_METHOD("Copy", "Picture", Picture_Copy, "[(X)i(Y)i(Width)i(Height)i]"), + GB_PROPERTY_READ("Image", "Image", Picture_Image), + + GB_INTERFACE("Paint", &PAINT_Interface), + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/CPicture.h b/gb.gtk/src/CPicture.h new file mode 100644 index 00000000..ada01c0c --- /dev/null +++ b/gb.gtk/src/CPicture.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + CPicture.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPICTURE_H +#define __CPICTURE_H + +#include "gambas.h" +#include "widgets.h" + +typedef + struct + { + GB_BASE ob; + gPicture *picture; + } + CPICTURE; + +#ifndef __CPICTURE_CPP + +extern GB_DESC CPictureDesc[]; + +#else + +#define MAX_KEY 255 + +#define STOCK_PREFIX "icon:/" +#define STOCK_PREFIX_LEN 6 + +#define THIS OBJECT(CPICTURE) +#define PICTURE (((CPICTURE*)(_object))->picture) + +#endif + +void *GTK_GetPicture(GdkPixbuf *buf); +CPICTURE *CPICTURE_create(gPicture *picture); + +#endif diff --git a/gb.gtk/src/CScreen.cpp b/gb.gtk/src/CScreen.cpp new file mode 100644 index 00000000..53d0e367 --- /dev/null +++ b/gb.gtk/src/CScreen.cpp @@ -0,0 +1,480 @@ +/*************************************************************************** + + CScreen.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSCREEN_CPP + +#include "CWindow.h" +#include "CPicture.h" +#include "CFont.h" +#include "CDrawingArea.h" +#include "CScreen.h" + +#include "gtrayicon.h" +#include "gapplication.h" +#include "gmainwindow.h" +#include "cpaint_impl.h" +#include "desktop.h" + +extern int CWINDOW_Embedder; +extern bool CWINDOW_Embedded; + +extern int MAIN_scale; + +char *CAPPLICATION_Theme = 0; +GB_ARRAY CAPPLICATION_Restart = NULL; + +static int _busy = 0; + +#define MAX_SCREEN 16 +static CSCREEN *_screens[MAX_SCREEN] = { NULL }; + +static bool _animations = FALSE; +static bool _shadows = FALSE; + +static CSCREEN *get_screen(int num) +{ + if (num < 0 || num >= MAX_SCREEN || num >= gDesktop::count()) + return NULL; + + if (!_screens[num]) + { + _screens[num] = (CSCREEN *)GB.New(GB.FindClass("Screen"), NULL, 0); + _screens[num]->index = num; + GB.Ref(_screens[num]); + } + + return _screens[num]; +} + +static void free_screens(void) +{ + int i; + + for (i = 0; i < MAX_SCREEN; i++) + { + if (_screens[i]) + GB.Unref(POINTER(&_screens[i])); + } +} + +static GdkRectangle *geometry(int num) +{ + static GdkRectangle rect; + gDesktop::geometry(num, &rect); + return ▭ +} + +static GdkRectangle *available_geometry(int num) +{ + static GdkRectangle rect; + gDesktop::availableGeometry(num, &rect); + return ▭ +} + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Desktop_X) + + GB.ReturnInteger(available_geometry(0)->x); + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Y) + + GB.ReturnInteger(available_geometry(0)->y); + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Width) + + GB.ReturnInteger(available_geometry(0)->width); + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Height) + + GB.ReturnInteger(available_geometry(0)->height); + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Resolution) + + GB.ReturnInteger(gDesktop::resolution()); + +END_PROPERTY + +BEGIN_METHOD(Desktop_Screenshot, GB_INTEGER x; GB_INTEGER y; GB_INTEGER width; GB_INTEGER height) + + CPICTURE *pic; + gPicture *buf = gDesktop::screenshot(VARGOPT(x,0), VARGOPT(y, 0), VARGOPT(width, 0), VARGOPT(height, 0)); + + pic = (CPICTURE *)GB.New(GB.FindClass("Picture"), 0, 0); + if (pic->picture) pic->picture->unref(); + pic->picture = buf; + GB.ReturnObject(pic); + +END_METHOD + +BEGIN_PROPERTY(Desktop_HasSystemTray) + + #ifdef NO_X_WINDOW + GB.Return(FALSE); + #else + GB.ReturnBoolean(gTrayIcon::hasSystemTray()); + #endif + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Type) + + GB.ReturnConstZeroString(DESKTOP_get_type()); + +END_PROPERTY + +//------------------------------------------------------------------------- + +static void set_font(gFont *font, void *object = 0) +{ + gDesktop::setFont(font); + MAIN_scale = gDesktop::scale(); +} + +BEGIN_PROPERTY(Application_Font) + + if (READ_PROPERTY) + GB.ReturnObject(CFONT_create(gDesktop::font()->copy(), set_font)); + else if (VPROP(GB_OBJECT)) + set_font(((CFONT*)VPROP(GB_OBJECT))->font); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_ActiveWindow) + + GB.ReturnObject(CWINDOW_Active); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_ActiveControl) + + GB.ReturnObject(GetObject(gApplication::activeControl())); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_PreviousControl) + + GB.ReturnObject(GetObject(gApplication::previousControl())); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Busy) + + int busy; + + if (READ_PROPERTY) + GB.ReturnInteger(_busy); + else + { + busy = VPROP(GB_INTEGER); + + if (_busy == 0 && busy != 0) + gApplication::setBusy(true); + else if (_busy > 0 && busy == 0) + gApplication::setBusy(false); + + _busy = busy; + if (MAIN_debug_busy) + fprintf(stderr, "%s: Application.Busy = %d\n", GB.Debug.GetCurrentPosition(), busy); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Desktop_Scale) + + GB.ReturnInteger(MAIN_scale); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_ShowTooltips) + + if (READ_PROPERTY) + GB.ReturnBoolean(gApplication::areTooltipsEnabled()); + else + gApplication::enableTooltips(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Animations) + + if (READ_PROPERTY) + GB.ReturnBoolean(_animations); + else if (_animations != VPROP(GB_BOOLEAN)) + { + _animations = VPROP(GB_BOOLEAN); + CDRAWINGAREA_send_change_event(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Shadows) + + if (READ_PROPERTY) + GB.ReturnBoolean(_shadows); + else if (_shadows != VPROP(GB_BOOLEAN)) + { + _shadows = VPROP(GB_BOOLEAN); + CDRAWINGAREA_send_change_event(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_MainWindow) + + if (READ_PROPERTY) + GB.ReturnObject(CWINDOW_Main); + else + { + CWINDOW_Main = (CWINDOW *)VPROP(GB_OBJECT); + gApplication::setMainWindow(CWINDOW_Main ? (gMainWindow *)CWINDOW_Main->ob.widget : NULL); + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(Application_exit) + + GB.FreeString(&CAPPLICATION_Theme); + free_screens(); + +END_METHOD + + +BEGIN_PROPERTY(Application_Embedder) + + if (READ_PROPERTY) + GB.ReturnInteger(CWINDOW_Embedder); + else + { + if (CWINDOW_Embedded) + { + GB.Error("Application is already embedded"); + return; + } + + CWINDOW_Embedder = VPROP(GB_INTEGER); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Theme) + + if (READ_PROPERTY) { GB.ReturnString(CAPPLICATION_Theme); return; } + GB.StoreString(PROP(GB_STRING), &CAPPLICATION_Theme); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Restart) + + if (READ_PROPERTY) + GB.ReturnObject(CAPPLICATION_Restart); + else + GB.StoreObject(PROP(GB_OBJECT), POINTER(&CAPPLICATION_Restart)); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_DblClickTime) + + GB.ReturnInteger(gApplication::dblClickTime()); + +END_PROPERTY + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Screens_Count) + + GB.ReturnInteger(gDesktop::count()); + +END_PROPERTY + + +BEGIN_METHOD(Screens_get, GB_INTEGER screen) + + GB.ReturnObject(get_screen(VARG(screen))); + +END_METHOD + + +BEGIN_METHOD_VOID(Screens_next) + + int *index = (int *)GB.GetEnum(); + + if (*index >= gDesktop::count()) + GB.StopEnum(); + else + { + GB.ReturnObject(get_screen(*index)); + (*index)++; + } + +END_METHOD + + +BEGIN_PROPERTY(Screen_X) + + GB.ReturnInteger(geometry(SCREEN->index)->x); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Y) + + GB.ReturnInteger(geometry(SCREEN->index)->y); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Width) + + GB.ReturnInteger(geometry(SCREEN->index)->width); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Height) + + GB.ReturnInteger(geometry(SCREEN->index)->height); + +END_PROPERTY + + +BEGIN_PROPERTY(Screen_AvailableX) + + GB.ReturnInteger(available_geometry(SCREEN->index)->x); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_AvailableY) + + GB.ReturnInteger(available_geometry(SCREEN->index)->y); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_AvailableWidth) + + GB.ReturnInteger(available_geometry(SCREEN->index)->width); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_AvailableHeight) + + GB.ReturnInteger(available_geometry(SCREEN->index)->height); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC ScreenDesc[] = +{ + GB_DECLARE("Screen", sizeof(CSCREEN)), GB_NOT_CREATABLE(), GB_AUTO_CREATABLE(), + + GB_PROPERTY_READ("X", "i", Screen_X), + GB_PROPERTY_READ("Y", "i", Screen_Y), + GB_PROPERTY_READ("W", "i", Screen_Width), + GB_PROPERTY_READ("H", "i", Screen_Height), + GB_PROPERTY_READ("Width", "i", Screen_Width), + GB_PROPERTY_READ("Height", "i", Screen_Height), + + GB_PROPERTY_READ("AvailableX", "i", Screen_AvailableX), + GB_PROPERTY_READ("AvailableY", "i", Screen_AvailableY), + GB_PROPERTY_READ("AvailableWidth", "i", Screen_AvailableWidth), + GB_PROPERTY_READ("AvailableHeight", "i", Screen_AvailableHeight), + + GB_END_DECLARE +}; + +GB_DESC ScreensDesc[] = +{ + GB_DECLARE("Screens", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY_READ("Count", "i", Screens_Count), + GB_STATIC_METHOD("_get", "Screen", Screens_get, "(Screen)i"), + GB_STATIC_METHOD("_next", "Screen", Screens_next, NULL), + + GB_END_DECLARE +}; + +GB_DESC DesktopDesc[] = +{ + GB_DECLARE("Desktop", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY_READ("X", "i", Desktop_X), + GB_STATIC_PROPERTY_READ("Y", "i", Desktop_Y), + GB_STATIC_PROPERTY_READ("W", "i", Desktop_Width), + GB_STATIC_PROPERTY_READ("H", "i", Desktop_Height), + GB_STATIC_PROPERTY_READ("Width", "i", Desktop_Width), + GB_STATIC_PROPERTY_READ("Height", "i", Desktop_Height), + + GB_CONSTANT("Charset", "s", "UTF-8"), + GB_STATIC_PROPERTY_READ("Resolution", "i", Desktop_Resolution), + GB_STATIC_PROPERTY_READ("Scale","i",Desktop_Scale), + GB_STATIC_PROPERTY_READ("HasSystemTray", "b", Desktop_HasSystemTray), + + GB_STATIC_METHOD("Screenshot", "Picture", Desktop_Screenshot, "[(X)i(Y)i(Width)i(Height)i]"), + + GB_STATIC_PROPERTY_READ("Type", "s", Desktop_Type), + + GB_END_DECLARE +}; + +GB_DESC ApplicationDesc[] = +{ + GB_DECLARE("Application", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_exit", NULL, Application_exit, 0), + + GB_STATIC_PROPERTY("Font", "Font", Application_Font), + GB_STATIC_PROPERTY_READ("ActiveControl", "Control", Application_ActiveControl), + GB_STATIC_PROPERTY_READ("PreviousControl", "Control", Application_PreviousControl), + GB_STATIC_PROPERTY_READ("ActiveWindow", "Window", Application_ActiveWindow), + GB_STATIC_PROPERTY("MainWindow", "Window", Application_MainWindow), + GB_STATIC_PROPERTY("Busy", "i", Application_Busy), + GB_STATIC_PROPERTY("ShowTooltips", "b", Application_ShowTooltips), + GB_STATIC_PROPERTY("Animations", "b", Application_Animations), + GB_STATIC_PROPERTY("Shadows", "b", Application_Shadows), + GB_STATIC_PROPERTY("Embedder", "i", Application_Embedder), + GB_STATIC_PROPERTY("Theme", "s", Application_Theme), + GB_STATIC_PROPERTY("Restart", "String[]", Application_Restart), + GB_STATIC_PROPERTY_READ("DblClickTime", "i", Application_DblClickTime), + + GB_END_DECLARE +}; + + diff --git a/gb.gtk/src/CScreen.h b/gb.gtk/src/CScreen.h new file mode 100644 index 00000000..0b835b1f --- /dev/null +++ b/gb.gtk/src/CScreen.h @@ -0,0 +1,54 @@ +/*************************************************************************** + + CScreen.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSCREEN_H +#define __CSCREEN_H + +#include "main.h" +#include "gdesktop.h" +#include "gapplication.h" + +#ifndef __CSCREEN_CPP +extern GB_DESC ScreenDesc[]; +extern GB_DESC ScreensDesc[]; +extern GB_DESC DesktopDesc[]; +extern GB_DESC ApplicationDesc[]; + +extern char *CAPPLICATION_Theme; +extern GB_ARRAY CAPPLICATION_Restart; + +#else + +#define SCREEN ((CSCREEN *)_object) + +#endif + +typedef + struct + { + GB_BASE ob; + int index; + } + CSCREEN; + +#endif diff --git a/gb.gtk/src/CSeparator.cpp b/gb.gtk/src/CSeparator.cpp new file mode 100644 index 00000000..3b792a5d --- /dev/null +++ b/gb.gtk/src/CSeparator.cpp @@ -0,0 +1,51 @@ +/*************************************************************************** + + CSeparator.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSEPARATOR_CPP + +#include "CSeparator.h" +#include "CContainer.h" + + +BEGIN_METHOD(CSEPARATOR_new, GB_OBJECT parent) + + InitControl(new gSeparator(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + +END_METHOD + + + +GB_DESC CSeparatorDesc[] = +{ + GB_DECLARE("Separator", sizeof(CSEPARATOR)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CSEPARATOR_new, "(Parent)Container;"), + + SEPARATOR_DESCRIPTION, + + GB_END_DECLARE +}; + + + + diff --git a/gb.gtk/src/CSeparator.h b/gb.gtk/src/CSeparator.h new file mode 100644 index 00000000..9d171c16 --- /dev/null +++ b/gb.gtk/src/CSeparator.h @@ -0,0 +1,47 @@ +/*************************************************************************** + + CSeparator.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSEPARATOR_H +#define __CSEPARATOR_H + +#include "main.h" +#include "CWidget.h" +#include "gseparator.h" + +#ifndef __CSEPARATOR_CPP +extern GB_DESC CSeparatorDesc[]; +#else + +#define THIS ((CSEPARATOR *)_object) +#define SEPARATOR ((gSeparator*)THIS->ob.widget) + +#endif + +typedef + struct + { + CWIDGET ob; + } + CSEPARATOR; + +#endif diff --git a/gb.gtk/src/CSlider.cpp b/gb.gtk/src/CSlider.cpp new file mode 100644 index 00000000..1f816a2b --- /dev/null +++ b/gb.gtk/src/CSlider.cpp @@ -0,0 +1,161 @@ +/*************************************************************************** + + CSlider.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSLIDER_CPP + +#include "main.h" +#include "gambas.h" +#include "widgets.h" + +#include "CSlider.h" +#include "CContainer.h" +#include "CWidget.h" + +DECLARE_EVENT(EVENT_Change); + +void gb_raise_slider_Click(gSlider *sender) +{ + CWIDGET *_ob=GetObject(sender); + + if (!_ob) return; + GB.Raise((void*)_ob,EVENT_Change,0); +} + + +BEGIN_METHOD(CSLIDER_new, GB_OBJECT parent) + + InitControl(new gSlider(CONTAINER(VARG(parent))),(CWIDGET*)THIS); + + SLIDER->onChange=gb_raise_slider_Click; + +END_METHOD + +BEGIN_METHOD(CSCROLLBAR_new, GB_OBJECT parent) + + InitControl(new gScrollBar(CONTAINER(VARG(parent))),(CWIDGET*)THIS); + + SBAR->onChange=gb_raise_slider_Click; + +END_METHOD + + +BEGIN_PROPERTY(CSLIDER_tracking) + + if (READ_PROPERTY) { GB.ReturnBoolean(SLIDER->tracking()); return; } + SLIDER->setTracking(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CSLIDER_value) + + if (READ_PROPERTY) { GB.ReturnInteger(SLIDER->value()); return; } + SLIDER->setValue(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(CSLIDER_minval) + + if (READ_PROPERTY) { GB.ReturnInteger(SLIDER->min()); return; } + SLIDER->setMin(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CSLIDER_maxval) + + if (READ_PROPERTY) { GB.ReturnInteger(SLIDER->max()); return; } + SLIDER->setMax(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CSLIDER_linestep) + + if (READ_PROPERTY) { GB.ReturnInteger(SLIDER->step()); return; } + SLIDER->setStep(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CSLIDER_pagestep) + + if (READ_PROPERTY) { GB.ReturnInteger(SLIDER->pageStep()); return; } + SLIDER->setPageStep(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CSLIDER_mark) + + if (READ_PROPERTY){ GB.ReturnBoolean(SLIDER->mark()); return; } + SLIDER->setMark(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Slider_DefaultSize) + + GB.ReturnInteger(SLIDER->getDefaultSize()); + +END_PROPERTY + +GB_DESC CSliderDesc[] = +{ + GB_DECLARE("Slider", sizeof(CSLIDER)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CSLIDER_new, "(Parent)Container;"), + + GB_PROPERTY("Tracking", "b", CSLIDER_tracking), + GB_PROPERTY("Value", "i", CSLIDER_value), + GB_PROPERTY("Mark", "b", CSLIDER_mark), + GB_PROPERTY("MinValue", "i", CSLIDER_minval), + GB_PROPERTY("MaxValue", "i", CSLIDER_maxval), + GB_PROPERTY("Step", "i", CSLIDER_linestep), + GB_PROPERTY("PageStep", "i", CSLIDER_pagestep), + + GB_EVENT("Change", 0, 0, &EVENT_Change), + + SLIDER_DESCRIPTION, + + GB_END_DECLARE +}; + +GB_DESC CScrollBarDesc[] = +{ + GB_DECLARE("ScrollBar", sizeof(CSCROLLBAR)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CSCROLLBAR_new, "(Parent)Container;"), + + GB_PROPERTY("Tracking", "b", CSLIDER_tracking), + GB_PROPERTY("Value", "i", CSLIDER_value), + GB_PROPERTY("MinValue", "i", CSLIDER_minval), + GB_PROPERTY("MaxValue", "i", CSLIDER_maxval), + GB_PROPERTY("Step", "i", CSLIDER_linestep), + GB_PROPERTY("PageStep", "i", CSLIDER_pagestep), + GB_PROPERTY("DefaultSize", "i", Slider_DefaultSize), + + GB_EVENT("Change", 0, 0, &EVENT_Change), + + SCROLLBAR_DESCRIPTION, + + GB_END_DECLARE +}; + + diff --git a/gb.gtk/src/CSlider.h b/gb.gtk/src/CSlider.h new file mode 100644 index 00000000..bfcd05d4 --- /dev/null +++ b/gb.gtk/src/CSlider.h @@ -0,0 +1,59 @@ +/*************************************************************************** + + CSlider.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSLIDER_H +#define __CSLIDER_H + +#include "main.h" +#include "gslider.h" +#include "gscrollbar.h" + +#ifndef __CSLIDER_CPP + +extern GB_DESC CSliderDesc[]; +extern GB_DESC CScrollBarDesc[]; + +#else + +#define THIS ((CSLIDER *)_object) +#define SLIDER ((gSlider*)THIS->ob.widget) +#define SBAR ((gScrollBar *)THIS->ob.widget) + +#endif + +typedef + struct + { + CWIDGET ob; + } + CSLIDER; + +typedef + struct + { + CWIDGET ob; + } + CSCROLLBAR; + + +#endif diff --git a/gb.gtk/src/CStock.cpp b/gb.gtk/src/CStock.cpp new file mode 100644 index 00000000..cc1c63d3 --- /dev/null +++ b/gb.gtk/src/CStock.cpp @@ -0,0 +1,52 @@ +/*************************************************************************** + + CStock.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSTOCK_CPP + +#include "main.h" +#include "widgets.h" +#include "CPicture.h" +#include "CStock.h" + +/******************************************************************************* + + class Stock + +*******************************************************************************/ + +BEGIN_METHOD_VOID(CSTOCK_get) + + GB.Deprecated(GTK_NAME, "Stock class", NULL); + GB.ReturnNull(); + +END_METHOD + + +GB_DESC CStockDesc[] = +{ + GB_DECLARE("Stock", 0), GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("_get", "Picture", CSTOCK_get, "(Key)s[(Default)s]"), + + GB_END_DECLARE +}; diff --git a/gb.gtk/src/CStock.h b/gb.gtk/src/CStock.h new file mode 100644 index 00000000..b390a27a --- /dev/null +++ b/gb.gtk/src/CStock.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + CStock.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSTOCK_H +#define __CSTOCK_H + +#include "main.h" + +#ifndef __CSTOCK_CPP + +extern GB_DESC CStockDesc[]; + +#endif + +#endif diff --git a/gb.gtk/src/CStyle.cpp b/gb.gtk/src/CStyle.cpp new file mode 100644 index 00000000..fe9f7914 --- /dev/null +++ b/gb.gtk/src/CStyle.cpp @@ -0,0 +1,894 @@ +/*************************************************************************** + + CStyle.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSTYLE_CPP + +#include "CDrawingArea.h" +#include "CPicture.h" +#include "CStyle.h" +#include "cpaint_impl.h" + +#ifdef GTK3 + +static cairo_t *_cr = NULL; +static GtkWidget *_button = NULL; +static GtkWidget *_check_button = NULL; +static GtkWidget *_entry = NULL; +static GtkWidget *_radio_button = NULL; +static GtkStyleProvider *_css = NULL; + +#else + +static GdkDrawable *_dr = NULL; +static int _dr_x = 0; +static int _dr_y = 0; +static GtkWidget *_widget = NULL; + +#endif + +static STYLE_T *_stl = NULL; + +#ifdef GTK3 +static GtkStyleContext *get_style(GType type = G_TYPE_NONE) +{ + GtkWidget *widget = NULL; + + if (type == GTK_TYPE_BUTTON) + { + if (!_button) _button = gtk_button_new(); + widget = _button; + } + else if (type == GTK_TYPE_CHECK_BUTTON) + { + if (!_check_button) _check_button = gtk_check_button_new(); + widget = _check_button; + } + else if (type == GTK_TYPE_ENTRY) + { + if (!_entry) + { + _entry = gtk_entry_new(); + gtk_widget_set_name(_entry, "se"); + } + widget = _entry; + } + else if (type == GTK_TYPE_RADIO_BUTTON) + { + if (!_radio_button) _radio_button = gtk_radio_button_new(NULL); + widget = _radio_button; + } + + if (!_css) + _css = GTK_STYLE_PROVIDER(gtk_css_provider_new()); + + return widget ? gtk_widget_get_style_context(widget) : NULL; +} +#else +static GtkStyle *attach_style(GtkStyle *style) +{ + if (_widget) + return gtk_style_attach(style, gtk_widget_get_window(_widget)); + else + return gtk_style_attach(style, (GdkWindow*)_dr); +} + +static GtkStyle *get_style(GType type = G_TYPE_NONE) +{ + if (type == G_TYPE_NONE && _widget) + { + _stl = gtk_style_copy(gtk_widget_get_style(_widget)); + //_stl = gtk_style_attach(_stl, gtk_widget_get_window(_widget)); + } + else + { + if (type != G_TYPE_NONE) + _stl = gtk_style_copy(gt_get_style(type)); + else + _stl = gtk_style_copy(gtk_widget_get_default_style()); + + //_stl = gtk_style_attach(_stl, (GdkWindow*)_dr); + } + + _stl = attach_style(_stl); + + return _stl; +} +#endif + +#ifdef GTK3 +static bool begin_draw(int *x, int *y) +{ + void *device = PAINT_get_current_device(); + if (!device) + return TRUE; + + _cr = PAINT_get_current_context(); + + if (GB.Is(device, CLASS_DrawingArea)) + { + gDrawingArea *wid = (gDrawingArea *)((CDRAWINGAREA *)device)->ob.widget; + + if (!(wid->cached() || wid->inDrawEvent())) + { + GB.Error("Cannot draw outside of 'Draw' event handler"); + return TRUE; + } + + //_widget = wid->widget; + } + else + { + //_widget = NULL; + } + + return FALSE; +} +#else +static bool begin_draw(int *x, int *y) +{ + void *device = PAINT_get_current_device(); + if (!device) + return TRUE; + + cairo_t *context = PAINT_get_current_context(); + cairo_surface_flush(cairo_get_target(context)); + + if (GB.Is(device, CLASS_DrawingArea)) + { + gDrawingArea *wid = (gDrawingArea *)((CDRAWINGAREA *)device)->ob.widget; + + if (wid->cached() || wid->inDrawEvent()) + { + if (wid->cached()) + { + wid->resizeCache(); + _dr = wid->buffer; + } + else + { + _dr = wid->widget->window; + GtkAllocation *a = &wid->widget->allocation; + _dr_x = a->x; + _dr_y = a->y; + *x += _dr_x; + *y += _dr_y; + } + } + else + { + GB.Error("Cannot draw outside of 'Draw' event handler"); + return TRUE; + } + + _widget = wid->widget; + } + else if (GB.Is(device, CLASS_Picture)) + { + gPicture *pic = ((CPICTURE *)device)->picture; + if (pic->isVoid()) + { + GB.Error("Bad picture"); + return TRUE; + } + _dr = pic->getPixmap(); + //pic->invalidate(); + _widget = NULL; + } + else + { + GB.Error("Device not supported"); + } + + return FALSE; +} +#endif + +static void end_draw() +{ +#ifdef GTK3 + _cr = NULL; + if (_stl) + { + gtk_style_context_restore(_stl); + _stl = NULL; + } +#else + _dr = NULL; + if (_stl) + { + gtk_style_detach(_stl); + g_object_unref(G_OBJECT(_stl)); + _stl = NULL; + } + _widget = NULL; +#endif + +#ifndef GTK3 + cairo_t *context = PAINT_get_current_context(); + cairo_surface_mark_dirty(cairo_get_target(context)); +#endif +} + +#ifdef GTK3 + +static STATE_T get_state(int state) +{ + int gstate = STATE_NORMAL; + + if (state & GB_DRAW_STATE_DISABLED) + gstate |= STATE_INSENSITIVE; + if (state & GB_DRAW_STATE_ACTIVE) + gstate |= STATE_ACTIVE; + if (state & GB_DRAW_STATE_HOVER) + gstate |= STATE_PRELIGHT; + if (state & GB_DRAW_STATE_FOCUS) + gstate |= STATE_FOCUSED; + + return (STATE_T)gstate; +} + +static void set_state(GtkStyleContext *style, int state) +{ + gtk_style_context_set_state(style, get_state(state)); +} + +#else + +static STATE_T get_state(int state) +{ + if (state & GB_DRAW_STATE_DISABLED) + return STATE_INSENSITIVE; + if (state & GB_DRAW_STATE_FOCUS) + return STATE_ACTIVE; + if (state & GB_DRAW_STATE_HOVER) + return STATE_PRELIGHT; + if (state & GB_DRAW_STATE_ACTIVE) + return STATE_ACTIVE; + + return STATE_NORMAL; +} + +#endif + +#ifndef GTK3 +static GdkRectangle *get_area() +{ + static GdkRectangle area; + + if (PAINT_get_clip(&area.x, &area.y, &area.width, &area.height)) + return NULL; + else + { + area.x += _dr_x; + area.y += _dr_y; + //fprintf(stderr, "clip: %d %d %d %d\n", area.x, area.y, area.width, area.height); + return &area; + } +} +#endif + +#ifdef GTK3 +static void paint_focus(STYLE_T *style, int x, int y, int w, int h) +{ + gtk_render_focus(style, _cr, x, y, w, h); +} +#else +static void paint_focus(STYLE_T *style, int x, int y, int w, int h, STATE_T state, const char *kind) +{ + gtk_paint_focus(style, _dr, state, get_area(), _widget, kind, x, y, w, h); +} +#endif + +static void style_arrow(int x, int y, int w, int h, int type, int state) +{ + GtkArrowType arrow; + STYLE_T *style = get_style(GTK_TYPE_BUTTON); + + switch (type) + { + case ALIGN_NORMAL: arrow = GB.System.IsRightToLeft() ? GTK_ARROW_LEFT : GTK_ARROW_RIGHT; break; + case ALIGN_LEFT: arrow = GTK_ARROW_LEFT; break; + case ALIGN_RIGHT: arrow = GTK_ARROW_RIGHT; break; + case ALIGN_TOP: arrow = GTK_ARROW_UP; break; + case ALIGN_BOTTOM: arrow = GTK_ARROW_DOWN; break; + default: + return; + } + +#ifdef GTK3 + double angle; + + switch(arrow) + { + case GTK_ARROW_LEFT: angle = M_PI * 1.5; break; + case GTK_ARROW_RIGHT: angle = M_PI / 2; break; + case GTK_ARROW_UP: angle = 0; break; + case GTK_ARROW_DOWN: angle = M_PI; break; + default: return; + } + + if (w > h) + { + x += (w - h) / 2; + w = h; + } + else if (h > w) + { + y += (h - w) / 2; + } + + set_state(style, state); + gtk_render_arrow(style, _cr, angle, x, y, w); +#else + gtk_paint_arrow(style, _dr, get_state(state), + GTK_SHADOW_NONE, get_area(), _widget, NULL, + arrow, TRUE, x, y, w, h); +#endif +} + +static void style_check(int x, int y, int w, int h, int value, int state) +{ + STYLE_T *style = get_style(GTK_TYPE_CHECK_BUTTON); + + if (value) + state |= GB_DRAW_STATE_ACTIVE; + +#ifdef GTK3 + set_state(style, state); + gtk_render_check(style, _cr, x, y, w, h); + if (state & GB_DRAW_STATE_FOCUS) + paint_focus(style, x, y, w, h); +#else + GtkShadowType shadow; + GtkStateType st = get_state(state); + + if (value) + state |= GB_DRAW_STATE_ACTIVE; + + //_dr->offset(&x, &y); + + switch (value) + { + case -1: shadow = GTK_SHADOW_IN; break; + case 1: shadow = GTK_SHADOW_ETCHED_IN; break; + default: shadow = GTK_SHADOW_OUT; break; + } + + gtk_paint_check(style, _dr, + st, shadow, get_area(), NULL, "checkbutton", + x, y, w, h); + if (state & GB_DRAW_STATE_FOCUS) + paint_focus(style, x, y, w, h, st, "checkbutton"); +#endif + +} + +static void style_option(int x, int y, int w, int h, int value, int state) +{ + STYLE_T *style = get_style(GTK_TYPE_RADIO_BUTTON); + + if (value) + state |= GB_DRAW_STATE_ACTIVE; + +#ifdef GTK3 + set_state(style, state); + gtk_render_option(style, _cr, x, y, w, h); + if (state & GB_DRAW_STATE_FOCUS) + paint_focus(style, x, y, w, h); +#else + GtkShadowType shadow; + GtkStateType st = get_state(state | (value ? GB_DRAW_STATE_ACTIVE : 0)); + + shadow = value ? GTK_SHADOW_IN : GTK_SHADOW_OUT; + + gtk_paint_option(style, _dr, + st, shadow, get_area(), NULL, "radiobutton", + x, y, w, h); + if (state & GB_DRAW_STATE_FOCUS) + paint_focus(style, x, y, w, h, st, "radiobutton"); +#endif + +} + +static void style_separator(int x, int y, int w, int h, int vertical, int state) +{ + STYLE_T *style = get_style(); + + if (vertical) + { +#ifdef GTK3 + set_state(style, state); + gtk_render_line(style, _cr, x + (w / 2), y, x + (w / 2), y + h - 1); +#else + gtk_paint_vline(style, _dr, + get_state(state), get_area(), NULL, NULL, + y, y + h - 1, x + (w / 2)); +#endif + } + else + { +#ifdef GTK3 + set_state(style, state); + gtk_render_line(style, _cr, x, y + (h / 2), x + w - 1, y + (h / 2)); +#else + gtk_paint_hline(style, _dr, + get_state(state), get_area(), NULL, NULL, + x, x + w - 1, y + (h / 2)); +#endif + } +} + +static void style_button(int x, int y, int w, int h, int value, int state, int flat) +{ + STYLE_T *style = get_style(GTK_TYPE_BUTTON); + + if (value) + state |= GB_DRAW_STATE_ACTIVE; + +#ifndef GTK3 + int xf, yf, wf, hf; + GtkBorder *default_border, *default_outside_border, *inner_border; + int focus_width, focus_pad, df; + gboolean interior_focus; + + gtk_style_get(style, GTK_TYPE_BUTTON, + "default-border", &default_border, + "default-outside-border", &default_outside_border, + "inner-border", &inner_border, + "focus-line-width", &focus_width, + "focus-padding", &focus_pad, + "interior-focus", &interior_focus, + (char *)NULL); + + /*if (default_outside_border) + { + x += default_outside_border->left; + y += default_outside_border->top; + w -= default_outside_border->left + default_outside_border->right; + h -= default_outside_border->top + default_outside_border->bottom; + }*/ + + if (default_border) + { + x += default_border->left; + y += default_border->top; + w -= default_border->left + default_border->right; + h -= default_border->top + default_border->bottom; + } + + if (inner_border) gtk_border_free(inner_border); + if (default_outside_border) gtk_border_free(default_outside_border); + if (default_border) gtk_border_free(default_border); + + xf = x; + yf = y; + wf = w; + hf = h; + + if (interior_focus) + { + df = focus_pad + style->xthickness; + xf += df; + wf -= df * 2; + df = focus_pad + style->ythickness; + yf += df; + hf -= df * 2; + } + else if (state & GB_DRAW_STATE_FOCUS) + { + df = focus_pad + focus_width; + + x += df; + w -= df * 2; + y += df; + h -= df * 2; + } +#endif + + if (flat && (state & GB_DRAW_STATE_HOVER) == 0) + { + /*gtk_paint_flat_box(style, _dr, + st, value ? GTK_SHADOW_IN : GTK_SHADOW_OUT, + get_area(d), _widget, "button", + x, y, w, h); + if (_dr->mask()) + gtk_paint_flat_box(style, _dr->mask(), + st, value ? GTK_SHADOW_IN : GTK_SHADOW_OUT, + get_area(d), _widget, "button", + x, y, w, h);*/ + } + else + { +#ifdef GTK3 + set_state(style, state); + gtk_render_background(style, _cr, x, y, w, h); + gtk_render_frame(style, _cr, x, y, w, h); + if (state & GB_DRAW_STATE_FOCUS) + paint_focus(style, x, y, w, h); +#else + GtkStateType st = get_state(state); + gtk_paint_box(style, _dr, + st, value ? GTK_SHADOW_IN : GTK_SHADOW_OUT, + get_area(), _widget, "button", + x, y, w, h); + if (state & GB_DRAW_STATE_FOCUS) + paint_focus(style, xf, yf, wf, hf, st, "button"); +#endif + } + +} + +static void style_panel(int x, int y, int w, int h, int border, int state) +{ + STYLE_T *style = get_style(); + +#ifdef GTK3 + gColor col = 0; + + if (border == BORDER_PLAIN) + { + col = IMAGE.MergeColor(gDesktop::bgColor(), gDesktop::fgColor(), 0.5); + col = IMAGE.LighterColor(col); + } + + gt_draw_border(_cr, style, get_state(state), border, col, x, y, w, h); + +#else + GtkShadowType shadow; + GtkStateType st = get_state(state); + + switch (border) + { + case BORDER_SUNKEN: shadow = GTK_SHADOW_IN; break; + case BORDER_RAISED: shadow = GTK_SHADOW_OUT; break; + case BORDER_ETCHED: shadow = GTK_SHADOW_ETCHED_IN; break; + default: shadow = GTK_SHADOW_NONE; + } + + gtk_paint_shadow(style, _dr, st, shadow, get_area(), NULL, NULL, x, y, w, h); + + if (border == BORDER_PLAIN) + { + GdkGC *gc; + GdkGCValues values; + uint col; + + col = IMAGE.MergeColor(gDesktop::bgColor(), gDesktop::fgColor(), 0.5); + col = IMAGE.LighterColor(col); + + fill_gdk_color(&values.foreground, col, gdk_drawable_get_colormap(_dr)); + gc = gtk_gc_get(gdk_drawable_get_depth(_dr), gdk_drawable_get_colormap(_dr), &values, GDK_GC_FOREGROUND); + gdk_draw_rectangle(_dr, gc, FALSE, x, y, w - 1, h - 1); + gtk_gc_release(gc); + } + + if (state & GB_DRAW_STATE_FOCUS) + paint_focus(style, x, y, w, h, st, "button"); +#endif + +} + +static void style_handle(int x, int y, int w, int h, int vertical, int state) +{ + STYLE_T *style = get_style(); + +#ifdef GTK3 + set_state(style, state); + gtk_render_handle(style, _cr, x, y, w, h); +#else + gtk_paint_handle(style, _dr, get_state(state), + GTK_SHADOW_NONE, get_area(), NULL, NULL, + x, y, w, h, + (!vertical) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL); +#endif +} + +static void style_box(int x, int y, int w, int h, int state, GB_COLOR color) +{ + STYLE_T *style = get_style(GTK_TYPE_ENTRY); + + if (gApplication::fix_oxygen) + { + x -= 3; + w += 6; + } + +#ifdef GTK3 + + set_state(style, state); + + if (color != GB_COLOR_DEFAULT) + { + char *css = NULL; + char buffer[256]; + + g_stradd(&css, "#se:not(:selected) { background-color:"); + gt_to_css_color(buffer, color); + g_stradd(&css, buffer); + g_stradd(&css, "; background-image:none; }\n"); + gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(_css), css, -1, NULL); + gtk_style_context_add_provider(style, _css, GTK_STYLE_PROVIDER_PRIORITY_USER); + +#if GTK_CHECK_VERSION(3, 12, 0) +#else + gtk_style_context_invalidate(style); +#endif + gtk_render_background(style, _cr, x, y, w, h); + gtk_style_context_remove_provider(style, GTK_STYLE_PROVIDER(_css)); +#if GTK_CHECK_VERSION(3, 12, 0) +#else + gtk_style_context_invalidate(style); +#endif + } + else + gtk_render_background(style, _cr, x, y, w, h); + + gtk_render_frame(style, _cr, x, y, w, h); + + if (color != GB_COLOR_DEFAULT) + gtk_style_context_remove_provider(style, _css); + +#else + + if (gApplication::fix_breeze) + state &= ~GB_DRAW_STATE_HOVER; + + GtkStateType st = get_state(state); + + if (color == GB_COLOR_DEFAULT) + { + //fprintf(stderr, "paint_box: default color\n"); + gtk_paint_box (style, _dr, st, GTK_SHADOW_NONE, get_area(), _widget, "entry", x, y, w, h); + gtk_paint_shadow(style, _dr, st, GTK_SHADOW_NONE, get_area(), NULL, "entry", x, y, w, h); + /*color = gDesktop::bgColor(); + if (_widget) + { + gControl *control = gt_get_control(_widget); + if (control) + color = control->realBackground(); + }*/ + } + else + { + GtkStyle *style2 = gtk_style_copy(style); + for (int i = 0; i < 5; i++) + { + fill_gdk_color(&style2->bg[i], color); + fill_gdk_color(&style2->base[i], color); + } + style2 = attach_style(style2); + + gtk_paint_box (style2, _dr, st, + GTK_SHADOW_IN, get_area(), _widget, "entry", x, y, w, h); + + g_object_unref(G_OBJECT(style2)); + } + + if (state & GB_DRAW_STATE_FOCUS) + paint_focus(style, x, y, w, h, st, "entry"); + +#endif +} + +BEGIN_PROPERTY(Style_ScrollbarSize) + + GB.ReturnInteger(gApplication::getScrollbarSize()); + +END_PROPERTY + +BEGIN_PROPERTY(Style_ScrollbarSpacing) + + GB.ReturnInteger(gApplication::getScrollbarSpacing()); + +END_PROPERTY + +BEGIN_PROPERTY(Style_FrameWidth) + + GB.ReturnInteger(gApplication::getFrameWidth()); + +END_PROPERTY + +BEGIN_PROPERTY(Style_BoxFrameWidth) + + int w, h; + gApplication::getBoxFrame(&w, &h); + GB.ReturnInteger(w); + +END_PROPERTY + +BEGIN_PROPERTY(Style_BoxFrameHeight) + + int w, h; + gApplication::getBoxFrame(&w, &h); + GB.ReturnInteger(h); + +END_PROPERTY + +BEGIN_PROPERTY(Style_Name) + + GB.ReturnNewZeroString(gApplication::getStyleName()); + +END_PROPERTY + +#define BEGIN_DRAW() \ + int x, y, w, h; \ +\ + x = VARG(x); \ + y = VARG(y); \ + w = VARG(w); \ + h = VARG(h); \ +\ + if (w < 1 || h < 1) \ + return; \ + \ + if (begin_draw(&x, &y)) \ + return; + +#define END_DRAW() end_draw() + +BEGIN_METHOD(Style_PaintArrow, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER type; GB_INTEGER state) + + BEGIN_DRAW(); + style_arrow(x, y, w, h, VARG(type), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + END_DRAW(); + +END_METHOD + +BEGIN_METHOD(Style_PaintCheck, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER value; GB_INTEGER state) + + BEGIN_DRAW(); + style_check(x, y, w, h, VARG(value), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + END_DRAW(); + +END_METHOD + +BEGIN_METHOD(Style_PaintOption, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN value; GB_INTEGER state) + + BEGIN_DRAW(); + style_option(x, y, w, h, VARG(value), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + END_DRAW(); + +END_METHOD + +BEGIN_METHOD(Style_PaintSeparator, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN vertical; GB_INTEGER state) + + BEGIN_DRAW(); + style_separator(x, y, w, h, VARGOPT(vertical, FALSE), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + END_DRAW(); + +END_METHOD + +BEGIN_METHOD(Style_PaintButton, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN value; GB_INTEGER state; GB_BOOLEAN flat) + + BEGIN_DRAW(); + style_button(x, y, w, h, VARG(value), VARGOPT(state, GB_DRAW_STATE_NORMAL), VARGOPT(flat, FALSE)); + END_DRAW(); + +END_METHOD + +BEGIN_METHOD(Style_PaintPanel, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER border; GB_INTEGER state) + + BEGIN_DRAW(); + style_panel(x, y, w, h, VARG(border), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + END_DRAW(); + +END_METHOD + +BEGIN_METHOD(Style_PaintHandle, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN vertical; GB_INTEGER state) + + BEGIN_DRAW(); + style_handle(x, y, w, h, VARGOPT(vertical, FALSE), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + END_DRAW(); + +END_METHOD + +BEGIN_METHOD(Style_PaintBox, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER state; GB_INTEGER color) + + BEGIN_DRAW(); + style_box(x, y, w, h, VARGOPT(state, GB_DRAW_STATE_NORMAL), VARGOPT(color, GB_COLOR_DEFAULT)); + END_DRAW(); + +END_METHOD + +BEGIN_METHOD(Style_StateOf, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + gControl *widget; + int state; + bool design; + + if (GB.CheckObject(control)) + return; + + widget = control->widget; + state = GB_DRAW_STATE_NORMAL; + design = widget->design(); + + if (!widget->isEnabled()) + state |= GB_DRAW_STATE_DISABLED; + if (widget->hasVisibleFocus() && !design) + state |= GB_DRAW_STATE_FOCUS; + if (widget->hovered() && !design) + state |= GB_DRAW_STATE_HOVER; + + GB.ReturnInteger(state); + +END_METHOD + +BEGIN_METHOD(Style_BackgroundOf, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + + if (GB.CheckObject(control)) + return; + + GB.ReturnInteger(control->widget->realBackground(true)); + +END_METHOD + +BEGIN_METHOD(Style_ForegroundOf, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + + if (GB.CheckObject(control)) + return; + + GB.ReturnInteger(control->widget->realForeground(true)); + +END_METHOD + + +GB_DESC StyleDesc[] = +{ + GB_DECLARE("Style", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY_READ("ScrollbarSize", "i", Style_ScrollbarSize), + GB_STATIC_PROPERTY_READ("ScrollbarSpacing", "i", Style_ScrollbarSpacing), + GB_STATIC_PROPERTY_READ("FrameWidth", "i", Style_FrameWidth), + GB_STATIC_PROPERTY_READ("TextBoxFrameWidth", "i", Style_BoxFrameWidth), + GB_STATIC_PROPERTY_READ("BoxFrameWidth", "i", Style_BoxFrameWidth), + GB_STATIC_PROPERTY_READ("BoxFrameHeight", "i", Style_BoxFrameHeight), + GB_STATIC_PROPERTY_READ("Name", "s", Style_Name), + + GB_STATIC_METHOD("PaintArrow", NULL, Style_PaintArrow, "(X)i(Y)i(Width)i(Height)i(Type)i[(Flag)i]"), + GB_STATIC_METHOD("PaintCheck", NULL, Style_PaintCheck, "(X)i(Y)i(Width)i(Height)i(Value)i[(Flag)i]"), + GB_STATIC_METHOD("PaintOption", NULL, Style_PaintOption, "(X)i(Y)i(Width)i(Height)i(Value)b[(Flag)i]"), + GB_STATIC_METHOD("PaintSeparator", NULL, Style_PaintSeparator, "(X)i(Y)i(Width)i(Height)i[(Vertical)b(Flag)i]"), + GB_STATIC_METHOD("PaintButton", NULL, Style_PaintButton, "(X)i(Y)i(Width)i(Height)i(Value)b[(Flag)i(Flat)b]"), + GB_STATIC_METHOD("PaintPanel", NULL, Style_PaintPanel, "(X)i(Y)i(Width)i(Height)i(Border)i[(Flag)i]"), + GB_STATIC_METHOD("PaintHandle", NULL, Style_PaintHandle, "(X)i(Y)i(Width)i(Height)i[(Vertical)b(Flag)i]"), + GB_STATIC_METHOD("PaintBox", NULL, Style_PaintBox, "(X)i(Y)i(Width)i(Height)i[(Flag)i(Color)i]"), + + GB_CONSTANT("Normal", "i", GB_DRAW_STATE_NORMAL), + GB_CONSTANT("Disabled", "i", GB_DRAW_STATE_DISABLED), + GB_CONSTANT("HasFocus", "i", GB_DRAW_STATE_FOCUS), + GB_CONSTANT("Hovered", "i", GB_DRAW_STATE_HOVER), + GB_CONSTANT("Active", "i", GB_DRAW_STATE_ACTIVE), + + GB_STATIC_METHOD("StateOf", "i", Style_StateOf, "(Control)Control;"), + GB_STATIC_METHOD("BackgroundOf", "i", Style_BackgroundOf, "(Control)Control;"), + GB_STATIC_METHOD("ForegroundOf", "i", Style_ForegroundOf, "(Control)Control;"), + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/CStyle.h b/gb.gtk/src/CStyle.h new file mode 100644 index 00000000..2af538c2 --- /dev/null +++ b/gb.gtk/src/CStyle.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + CStyle.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSTYLE_H +#define __CSTYLE_H + +#include "main.h" +#include "gdesktop.h" +#include "gapplication.h" + +#ifndef __CSTYLE_CPP +extern GB_DESC StyleDesc[]; +#endif + +#endif diff --git a/gb.gtk/src/CTabStrip.cpp b/gb.gtk/src/CTabStrip.cpp new file mode 100644 index 00000000..adffc321 --- /dev/null +++ b/gb.gtk/src/CTabStrip.cpp @@ -0,0 +1,386 @@ +/*************************************************************************** + + CTabStrip.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTABSTRIP_CPP + +#include "CPicture.h" +#include "CContainer.h" +#include "CFont.h" +#include "CTabStrip.h" + +DECLARE_EVENT(EVENT_Click); +DECLARE_EVENT(EVENT_Close); + +static void gb_tabstrip_raise_click(CTABSTRIP *_object) +{ + GB.Raise(THIS, EVENT_Click, 0); + GB.Unref(POINTER(&_object)); +} + +static void gb_tabstrip_post_click(gTabStrip *sender) +{ + CWIDGET *_object = GetObject(sender); + + GB.Ref(THIS); + GB.Post((GB_CALLBACK)gb_tabstrip_raise_click, (long)THIS); +} + +static void handle_close(gTabStrip *sender, int index) +{ + CWIDGET *_object = GetObject(sender); + GB.Raise(THIS, EVENT_Close, 1, GB_T_INTEGER, index); +} + +/*************************************************************************** + + TabStrip + +***************************************************************************/ + +BEGIN_METHOD(CTABSTRIP_new, GB_OBJECT parent) + + InitControl(new gTabStrip(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + TABSTRIP->onClick = gb_tabstrip_post_click; + TABSTRIP->onClose = handle_close; + gb_tabstrip_post_click(TABSTRIP); + +END_METHOD + + +BEGIN_METHOD_VOID(TabStrip_free) + + GB.Unref(POINTER(&THIS->textFont)); + +END_METHOD + + +BEGIN_PROPERTY(CTABSTRIP_tabs) + + if (READ_PROPERTY) { GB.ReturnInteger(TABSTRIP->count()); return; } + + if (VPROP(GB_INTEGER) < 1 || VPROP(GB_INTEGER) > 255) + { + GB.Error("Bad argument"); + return; + } + + if (TABSTRIP->setCount(VPROP(GB_INTEGER))) + GB.Error("Tab is not empty"); + +END_PROPERTY + + + +BEGIN_PROPERTY(CTABSTRIP_index) + + if (READ_PROPERTY) { GB.ReturnInteger(TABSTRIP->index()); return; } + if ( (VPROP(GB_INTEGER)<0) || (VPROP(GB_INTEGER)>=TABSTRIP->count()) ) + { + GB.Error("Bad index"); + return; + } + TABSTRIP->setIndex(VPROP(GB_INTEGER)); + + +END_PROPERTY + + +BEGIN_PROPERTY(CTABSTRIP_current) + + THIS->index = TABSTRIP->index(); + RETURN_SELF(); + +END_PROPERTY + + +BEGIN_METHOD(CTABSTRIP_get, GB_INTEGER index) + + if ( (VARG(index)<0) || (VARG(index)>=TABSTRIP->count()) ) + { + GB.Error("Bad index"); + return; + } + + THIS->index=VARG(index); + RETURN_SELF(); + +END_METHOD + + +BEGIN_PROPERTY(CTABSTRIP_orientation) + + if (READ_PROPERTY) + switch (TABSTRIP->orientation()) + { + case GTK_POS_TOP: GB.ReturnInteger(ALIGN_TOP); break; + case GTK_POS_BOTTOM: GB.ReturnInteger(ALIGN_BOTTOM); break; + case GTK_POS_LEFT: GB.ReturnInteger(ALIGN_LEFT); break; + case GTK_POS_RIGHT: GB.ReturnInteger(ALIGN_RIGHT); break; + default: GB.ReturnInteger(ALIGN_NORMAL); break; + } + + else + switch (VPROP(GB_INTEGER)) + { + case ALIGN_TOP: TABSTRIP->setOrientation(GTK_POS_TOP); break; + case ALIGN_BOTTOM: TABSTRIP->setOrientation(GTK_POS_BOTTOM); break; + case ALIGN_LEFT: TABSTRIP->setOrientation(GTK_POS_LEFT); break; + case ALIGN_RIGHT: TABSTRIP->setOrientation(GTK_POS_RIGHT); break; + + } + +END_PROPERTY + + + +/*************************************************************************** + + .Tab + +***************************************************************************/ + +BEGIN_PROPERTY(CTAB_text) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(TABSTRIP->tabText(THIS->index)); + else + TABSTRIP->setTabText(THIS->index, GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_TextFont) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->textFont); + else + { + GB.StoreObject(PROP(GB_OBJECT), POINTER(&THIS->textFont)); + CFONT *font = (CFONT *)THIS->textFont; + if (font) + TABSTRIP->setTextFont(font->font); + else + TABSTRIP->setTextFont(0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CTAB_picture) + + if (READ_PROPERTY) + { + gPicture *pic = TABSTRIP->tabPicture(THIS->index);; + GB.ReturnObject(pic ? pic->getTagValue() : 0); + } + else + { + CPICTURE *pic = (CPICTURE *)VPROP(GB_OBJECT); + TABSTRIP->setTabPicture(THIS->index, pic ? pic->picture : 0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CTAB_enabled) + + if (READ_PROPERTY) + GB.ReturnBoolean(TABSTRIP->tabEnabled(THIS->index)); + else + TABSTRIP->setTabEnabled(THIS->index,VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CTAB_next) + + int *ct = (int *)GB.GetEnum(); + + if (*ct >= TABSTRIP->tabCount(THIS->index)) + { + GB.StopEnum(); + return; + } + + GB.ReturnObject(GetObject(TABSTRIP->tabChild(THIS->index, *ct))); + (*ct)++; + +END_METHOD + +BEGIN_METHOD(CTAB_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= TABSTRIP->tabCount(THIS->index)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(GetObject(TABSTRIP->tabChild(THIS->index, index))); + +END_METHOD + +BEGIN_PROPERTY(CTAB_count) + + GB.ReturnInteger(TABSTRIP->tabCount(THIS->index)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTABSTRIP_text) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(TABSTRIP->tabText(TABSTRIP->index())); + else + TABSTRIP->setTabText(TABSTRIP->index(), GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(CTABSTRIP_picture) + + int index = TABSTRIP->index(); + + if (READ_PROPERTY) + { + gPicture *pic = TABSTRIP->tabPicture(index); + GB.ReturnObject(pic ? pic->getTagValue() : 0); + } + else + { + CPICTURE *pic = (CPICTURE *)VPROP(GB_OBJECT); + TABSTRIP->setTabPicture(index, pic ? pic->picture : 0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_Closable) + + if (READ_PROPERTY) + GB.ReturnBoolean(TABSTRIP->isClosable()); + else + TABSTRIP->setClosable(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CTAB_delete) + + if (TABSTRIP->removeTab(THIS->index)) + GB.Error("Tab is not empty"); + +END_METHOD + +BEGIN_PROPERTY(CTAB_visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(TABSTRIP->tabVisible(THIS->index)); + else + TABSTRIP->setTabVisible(THIS->index, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD(TabStrip_FindIndex, GB_OBJECT child) + + CWIDGET *child = (CWIDGET *)VARG(child); + + if (GB.CheckObject(child)) + return; + + GB.ReturnInteger(TABSTRIP->findIndex(child->widget)); + +END_METHOD + + +/*************************************************************************** + + Descriptions + +***************************************************************************/ + +GB_DESC CTabStripContainerChildrenDesc[] = +{ + GB_DECLARE_VIRTUAL(".TabStripContainer.Children"), + + GB_METHOD("_next", "Control", CTAB_next, NULL), + GB_PROPERTY_READ("Count", "i", CTAB_count), + GB_METHOD("_get", "Control", CTAB_next, "(Index)i"), + + GB_END_DECLARE +}; + + +GB_DESC CTabStripContainerDesc[] = +{ + GB_DECLARE_VIRTUAL(".TabStripContainer"), + + GB_PROPERTY("Text", "s", CTAB_text), + GB_PROPERTY("Picture", "Picture", CTAB_picture), + GB_PROPERTY("Caption", "s", CTAB_text), + GB_PROPERTY("Enabled", "b", CTAB_enabled), + GB_PROPERTY("Visible", "b", CTAB_visible), + GB_PROPERTY_SELF("Children", ".TabStripContainer.Children"), + GB_METHOD("Delete", 0, CTAB_delete, 0), + + GB_END_DECLARE +}; + + +GB_DESC CTabStripDesc[] = +{ + GB_DECLARE("TabStrip", sizeof(CTABSTRIP)), GB_INHERITS("Container"), + + GB_METHOD("_new", NULL, CTABSTRIP_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, TabStrip_free, NULL), + + GB_PROPERTY("Count", "i", CTABSTRIP_tabs), + GB_PROPERTY("Text", "s", CTABSTRIP_text), + GB_PROPERTY("TextFont", "Font", TabStrip_TextFont), + GB_PROPERTY("Picture", "Picture", CTABSTRIP_picture), + GB_PROPERTY("Closable", "b", TabStrip_Closable), + GB_PROPERTY("Caption", "s", CTABSTRIP_text), + GB_PROPERTY_READ("Current", ".TabStripContainer", CTABSTRIP_current), + GB_PROPERTY("Index", "i", CTABSTRIP_index), + GB_PROPERTY("Orientation", "i", CTABSTRIP_orientation), + + GB_PROPERTY("Arrangement", "i", Container_Arrangement), + GB_PROPERTY("AutoResize", "b", Container_AutoResize), + GB_PROPERTY("Padding", "i", Container_Padding), + GB_PROPERTY("Spacing", "b", Container_Spacing), + GB_PROPERTY("Margin", "b", Container_Margin), + GB_PROPERTY("Indent", "b", Container_Indent), + GB_PROPERTY("Invert", "b", Container_Invert), + + GB_METHOD("_get", ".TabStripContainer", CTABSTRIP_get, "(Index)i"), + GB_METHOD("FindIndex", "i", TabStrip_FindIndex, "(Child)Control;"), + + GB_EVENT("Click", NULL, NULL, &EVENT_Click), + GB_EVENT("Close", NULL, "(Index)i", &EVENT_Close), + + TABSTRIP_DESCRIPTION, + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/CTabStrip.h b/gb.gtk/src/CTabStrip.h new file mode 100644 index 00000000..b052631c --- /dev/null +++ b/gb.gtk/src/CTabStrip.h @@ -0,0 +1,53 @@ +/*************************************************************************** + + CTabStrip.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTABSTRIP_H +#define __CTABSTRIP_H + +#include "main.h" +#include "gtabstrip.h" +#include "CWidget.h" +#include "CContainer.h" + +#ifndef __CTABSTRIP_CPP +extern GB_DESC CTabStripDesc[]; +extern GB_DESC CTabStripContainerDesc[]; +extern GB_DESC CTabStripContainerChildrenDesc[]; +#else + + +#define THIS ((CTABSTRIP*)_object) +#define TABSTRIP ((gTabStrip*)THIS->ob.widget) + +#endif + +typedef + struct + { + CWIDGET ob; + int index; + void *textFont; + } + CTABSTRIP; + +#endif diff --git a/gb.gtk/src/CTextArea.cpp b/gb.gtk/src/CTextArea.cpp new file mode 100644 index 00000000..b2d2b899 --- /dev/null +++ b/gb.gtk/src/CTextArea.cpp @@ -0,0 +1,501 @@ +/*************************************************************************** + + CTextArea.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTEXTAREA_CPP + +#include "gambas.h" +#include "main.h" +#include "widgets.h" + +#include "CTextArea.h" +#include "CWidget.h" +#include "CContainer.h" + +#include + +DECLARE_EVENT(EVENT_Change); +DECLARE_EVENT(EVENT_Cursor); +//DECLARE_EVENT(EVENT_Link); //TODO + +static void cb_change(gTextArea *sender) +{ + CWIDGET *_object = GetObject((gControl*)sender); + GB.Raise(THIS, EVENT_Change, 0); +} + +static void cb_cursor(gTextArea *sender) +{ + CWIDGET *_object = GetObject((gControl*)sender); + GB.Raise(THIS, EVENT_Cursor, 0); +} + +BEGIN_METHOD(CTEXTAREA_new, GB_OBJECT parent) + + InitControl(new gTextArea(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + WIDGET->onChange = cb_change; + WIDGET->onCursor = cb_cursor; + +END_METHOD + + +BEGIN_PROPERTY(CTEXTAREA_text) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(TEXTAREA->text()); + else + TEXTAREA->setText(PSTRING(), PLENGTH()); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_length) + + GB.ReturnInteger(TEXTAREA->length()); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_read_only) + + if (READ_PROPERTY) + GB.ReturnBoolean(TEXTAREA->readOnly()); + else + TEXTAREA->setReadOnly(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_wrap) + + if (READ_PROPERTY) + GB.ReturnBoolean(TEXTAREA->wrap()); + else + TEXTAREA->setWrap(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_line) + + if (READ_PROPERTY) + GB.ReturnInteger(TEXTAREA->line()); + else + TEXTAREA->setLine(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_column) + + if (READ_PROPERTY) + GB.ReturnInteger(TEXTAREA->column()); + else + TEXTAREA->setColumn(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_pos) + + if (READ_PROPERTY) + GB.ReturnInteger(TEXTAREA->position()); + else + TEXTAREA->setPosition(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CTEXTAREA_clear) + + TEXTAREA->clear(); + +END_METHOD + + +BEGIN_METHOD(CTEXTAREA_insert, GB_STRING text) + + TEXTAREA->insert(GB.ToZeroString(ARG(text))); + +END_METHOD + +BEGIN_PROPERTY(CTEXTAREA_border) + + if (READ_PROPERTY) + GB.ReturnBoolean(TEXTAREA->hasBorder()); + else + TEXTAREA->setBorder(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + + +/*************************************************************************** + + .TextArea.Selection + +***************************************************************************/ + +BEGIN_PROPERTY(CTEXTAREA_sel_text) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(TEXTAREA->selText()); + else + TEXTAREA->setSelText(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_sel_length) + + GB.ReturnInteger(TEXTAREA->selEnd()-TEXTAREA->selStart()); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_sel_start) + + GB.ReturnInteger(TEXTAREA->selStart()); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CTEXTAREA_sel_clear) + + TEXTAREA->selDelete(); + +END_METHOD + + +BEGIN_METHOD(CTEXTAREA_sel_select, GB_INTEGER start; GB_INTEGER length) + + int start = VARGOPT(start, 0); + int length = VARGOPT(length, TEXTAREA->length()); + + TEXTAREA->selSelect(start, length); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_sel_all) + + TEXTAREA->selectAll(); + +END_METHOD + + +BEGIN_METHOD(CTEXTAREA_to_pos, GB_INTEGER line; GB_INTEGER col) + + GB.ReturnInteger(TEXTAREA->toPosition(VARG(line),VARG(col))); + +END_METHOD + + +BEGIN_METHOD(CTEXTAREA_to_line, GB_INTEGER pos) + + GB.ReturnInteger(TEXTAREA->toLine(VARG(pos))); + +END_METHOD + + +BEGIN_METHOD(CTEXTAREA_to_col, GB_INTEGER pos) + + GB.ReturnInteger(TEXTAREA->toColumn(VARG(pos))); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_copy) + + TEXTAREA->copy(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_cut) + + TEXTAREA->cut(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_paste) + + TEXTAREA->paste(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_ensure_visible) + + TEXTAREA->ensureVisible(); + +END_METHOD + + +BEGIN_PROPERTY(CTEXTAREA_scrollbar) + + if (READ_PROPERTY) + GB.ReturnInteger(TEXTAREA->scrollBar()); + else + TEXTAREA->setScrollBar(VPROP(GB_INTEGER)); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_undo) + + TEXTAREA->undo(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_redo) + + TEXTAREA->redo(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_selected) + + GB.ReturnBoolean(TEXTAREA->isSelected()); + +END_METHOD + + +BEGIN_PROPERTY(TextArea_Alignment) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->alignment()); + else + WIDGET->setAlignment(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_METHOD(TextArea_CursorAt, GB_INTEGER pos) + + int x, y; + WIDGET->getCursorPos(&x, &y, VARGOPT(pos, -1)); + GB.ReturnObject(GEOM.CreatePoint(x, y)); + +END_PROPERTY + +GB_DESC CTextAreaSelectionDesc[] = +{ + GB_DECLARE(".TextArea.Selection", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Text", "s", CTEXTAREA_sel_text), + GB_PROPERTY_READ("Length", "i", CTEXTAREA_sel_length), + GB_PROPERTY_READ("Start", "i", CTEXTAREA_sel_start), + GB_PROPERTY_READ("Pos", "i", CTEXTAREA_sel_start), + + GB_METHOD("Hide", 0, CTEXTAREA_sel_clear, 0), + + GB_END_DECLARE +}; + +GB_DESC CTextAreaDesc[] = +{ + GB_DECLARE("TextArea", sizeof(CTEXTAREA)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CTEXTAREA_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", CTEXTAREA_text), + GB_PROPERTY_READ("Length", "i", CTEXTAREA_length), + GB_PROPERTY("ReadOnly", "b", CTEXTAREA_read_only), + + GB_PROPERTY("ScrollBar", "i", CTEXTAREA_scrollbar), + GB_PROPERTY("Wrap", "b", CTEXTAREA_wrap), + GB_PROPERTY("Border", "b", CTEXTAREA_border), + GB_PROPERTY("Alignment", "i", TextArea_Alignment), + + GB_PROPERTY("Line", "i", CTEXTAREA_line), + GB_PROPERTY("Column", "i", CTEXTAREA_column), + GB_PROPERTY("Pos", "i", CTEXTAREA_pos), + + GB_PROPERTY_SELF("Selection", ".TextArea.Selection"), + GB_METHOD("Select", NULL, CTEXTAREA_sel_select, "[(Start)i(Length)i]"), + GB_METHOD("SelectAll", NULL, CTEXTAREA_sel_all, NULL), + GB_METHOD("Unselect", NULL, CTEXTAREA_sel_clear, NULL), + GB_PROPERTY_READ("Selected", "b", CTEXTAREA_selected), + + GB_METHOD("Clear", NULL, CTEXTAREA_clear, NULL), + GB_METHOD("Insert", NULL, CTEXTAREA_insert, "(Text)s"), + + GB_METHOD("Copy", NULL, CTEXTAREA_copy, NULL), + GB_METHOD("Cut", NULL, CTEXTAREA_cut, NULL), + GB_METHOD("Paste", NULL, CTEXTAREA_paste, NULL), + GB_METHOD("Undo", NULL, CTEXTAREA_undo, NULL), + GB_METHOD("Redo", NULL, CTEXTAREA_redo, NULL), + + GB_METHOD("ToPos", "i", CTEXTAREA_to_pos, "(Line)i(Column)i"), + GB_METHOD("ToLine", "i", CTEXTAREA_to_line, "(Pos)i"), + GB_METHOD("ToColumn", "i", CTEXTAREA_to_col, "(Pos)i"), + + GB_METHOD("EnsureVisible", NULL, CTEXTAREA_ensure_visible, NULL), + + GB_METHOD("CursorAt", "Point", TextArea_CursorAt, "[(Pos)i]"), + + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + GB_EVENT("Cursor", NULL, NULL, &EVENT_Cursor), + + TEXTAREA_DESCRIPTION, + + GB_END_DECLARE +}; + +#if 0 +/** TextEdit ***************************************************************/ + +BEGIN_METHOD(CTEXTEDIT_new, GB_OBJECT parent) + + InitControl(new gTextArea(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + WIDGET->setWrap(true); + +END_METHOD + +BEGIN_PROPERTY(CTEXTEDIT_scroll_x) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->scrollX()); + else + WIDGET->setScrollX(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_scroll_y) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->scrollY()); + else + WIDGET->setScrollY(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_text_width) + + GB.ReturnInteger(WIDGET->textWidth()); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_text_height) + + GB.ReturnInteger(WIDGET->textHeight()); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_format_alignment) + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_format_font) + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_format_color) + +END_PROPERTY + + +GB_DESC CTextEditFormatDesc[] = +{ + GB_DECLARE(".TextEditFormat", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Alignment", "i", CTEXTEDIT_format_alignment), + //GB_PROPERTY("Position", "i", CTEXTEDIT_format_position), + GB_PROPERTY("Font", "Font", CTEXTEDIT_format_font), + GB_PROPERTY("Color", "i", CTEXTEDIT_format_color), + + GB_END_DECLARE +}; + +GB_DESC CTextEditSelectionDesc[] = +{ + GB_DECLARE(".TextEditSelection", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Text", "s", CTEXTAREA_sel_text), + GB_PROPERTY_READ("Length", "i", CTEXTAREA_sel_length), + GB_PROPERTY_READ("Start", "i", CTEXTAREA_sel_start), + GB_METHOD("Hide", NULL, CTEXTAREA_sel_clear, NULL), + + GB_END_DECLARE +}; + + +GB_DESC CTextEditDesc[] = +{ + GB_DECLARE("TextEdit", sizeof(CTEXTAREA)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CTEXTEDIT_new, "(Parent)Container;"), + + GB_PROPERTY("ReadOnly", "b", CTEXTAREA_read_only), + + GB_METHOD("Clear", NULL, CTEXTAREA_clear, NULL), + + GB_PROPERTY("Text", "s", CTEXTAREA_text), + GB_METHOD("Insert", NULL, CTEXTAREA_insert, "(Text)s"), + + GB_PROPERTY("Paragraph", "i", CTEXTAREA_line), + GB_PROPERTY("Index", "i", CTEXTAREA_column), + GB_PROPERTY("Pos", "i", CTEXTAREA_pos), + + GB_METHOD("ToPos", "i", CTEXTAREA_to_pos, "(Paragraph)i(Index)i"), + GB_METHOD("ToParagraph", "i", CTEXTAREA_to_line, "(Pos)i"), + GB_METHOD("ToIndex", "i", CTEXTAREA_to_col, "(Pos)i"), + + GB_METHOD("EnsureVisible", NULL, CTEXTAREA_ensure_visible, NULL), + + GB_PROPERTY_SELF("Selection", ".TextEditSelection"), + GB_METHOD("Select", NULL, CTEXTAREA_sel_select, "[(Start)i(Length)i]"), + GB_METHOD("SelectAll", NULL, CTEXTAREA_sel_all, NULL), + GB_METHOD("Unselect", NULL, CTEXTAREA_sel_clear, NULL), + GB_PROPERTY_READ("Selected", "b", CTEXTAREA_selected), + + GB_METHOD("Copy", NULL, CTEXTAREA_copy, NULL), + GB_METHOD("Cut", NULL, CTEXTAREA_cut, NULL), + GB_METHOD("Paste", NULL, CTEXTAREA_paste, NULL), + GB_METHOD("Undo", NULL, CTEXTAREA_undo, NULL), + GB_METHOD("Redo", NULL, CTEXTAREA_redo, NULL), + + GB_PROPERTY("Border", "b", CTEXTAREA_border), + GB_PROPERTY("ScrollBar", "i", CTEXTAREA_scrollbar), + + GB_PROPERTY("ScrollX", "i", CTEXTEDIT_scroll_x), + GB_PROPERTY("ScrollY", "i", CTEXTEDIT_scroll_y), + + GB_PROPERTY("TextWidth", "i", CTEXTEDIT_text_width), + GB_PROPERTY("TextHeight", "i", CTEXTEDIT_text_height), + + GB_PROPERTY_SELF("Format", ".TextEditFormat"), + + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + GB_EVENT("Cursor", NULL, NULL, &EVENT_Cursor), + GB_EVENT("Link", NULL, "(Path)s", &EVENT_Link), + + TEXTEDIT_DESCRIPTION, + + GB_END_DECLARE +}; + +#endif diff --git a/gb.gtk/src/CTextArea.h b/gb.gtk/src/CTextArea.h new file mode 100644 index 00000000..f33e9dc0 --- /dev/null +++ b/gb.gtk/src/CTextArea.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + CTextArea.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTEXTAREA_H +#define __CTEXTAREA_H + +#include "main.h" +#include "gtextarea.h" + + +#ifndef __CTEXTAREA_CPP +extern GB_DESC CTextAreaDesc[]; +extern GB_DESC CTextAreaSelectionDesc[]; +/*extern GB_DESC CTextEditDesc[]; +extern GB_DESC CTextEditSelectionDesc[]; +extern GB_DESC CTextEditFormatDesc[];*/ +#else + +#define THIS ((CTEXTAREA *)_object) +#define TEXTAREA ((gTextArea *)THIS->ob.widget) +#define WIDGET ((gTextArea *)THIS->ob.widget) + +#endif + +typedef + struct + { + CWIDGET ob; + } + CTEXTAREA; + +#endif diff --git a/gb.gtk/src/CTextBox.cpp b/gb.gtk/src/CTextBox.cpp new file mode 100644 index 00000000..0e530f60 --- /dev/null +++ b/gb.gtk/src/CTextBox.cpp @@ -0,0 +1,539 @@ +/*************************************************************************** + + CTextBox.cpp + + (c) 2004-2005 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTEXTBOX_CPP + +#include +#include +#include "main.h" +#include "gambas.h" +#include "widgets.h" +#include "CTextBox.h" +#include "CWidget.h" +#include "CContainer.h" + + +DECLARE_EVENT(EVENT_Change); +DECLARE_EVENT(EVENT_Activate); +DECLARE_EVENT(EVENT_Click); + +/*static void txt_post_change(void *_object) +{ + GB.Raise(THIS, EVENT_Change, 0); + GB.Unref(POINTER(&_object)); +}*/ + +static void txt_raise_change(gTextBox *sender) +{ + CWIDGET *_object = GetObject((gControl*)sender); + GB.Raise(THIS, EVENT_Change, 0); +} + +static void txt_raise_activate(gTextBox *sender) +{ + CWIDGET *_object = GetObject((gControl*)sender); + GB.Raise(THIS, EVENT_Activate, 0); +} + +/*************************************************************************** + + TextBox + +***************************************************************************/ + +#define CHECK_COMBOBOX() \ + if (!TEXTBOX->hasEntry()) \ + { \ + GB.Error("ComboBox is read-only"); \ + return; \ + } + +BEGIN_METHOD(CTEXTBOX_new, GB_OBJECT parent) + + InitControl(new gTextBox(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + TEXTBOX->onChange = txt_raise_change; + TEXTBOX->onActivate = txt_raise_activate; + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTBOX_clear) + + TEXTBOX->clear(); + +END_METHOD + + +BEGIN_METHOD(CTEXTBOX_insert, GB_STRING text) + + CHECK_COMBOBOX(); + TEXTBOX->insert(STRING(text),LENGTH(text)); + +END_METHOD + + +BEGIN_PROPERTY(CTEXTBOX_text) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(TEXTBOX->text()); + else + TEXTBOX->setText(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTBOX_length) + + GB.ReturnInteger(TEXTBOX->length()); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTBOX_alignment) + + if (READ_PROPERTY) { GB.ReturnInteger(TEXTBOX->alignment()); return; } + TEXTBOX->setAlignment(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTBOX_pos) + + CHECK_COMBOBOX(); + if (READ_PROPERTY) { GB.ReturnInteger(TEXTBOX->position()); return; } + TEXTBOX->setPosition(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTBOX_read_only) + + if (READ_PROPERTY) { GB.ReturnBoolean(TEXTBOX->isReadOnly()); return; } + TEXTBOX->setReadOnly(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTBOX_border) + + if (READ_PROPERTY) { GB.ReturnBoolean(TEXTBOX->hasBorder()); return; } + TEXTBOX->setBorder(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTBOX_password) + + CHECK_COMBOBOX(); + if (READ_PROPERTY) { GB.ReturnBoolean(TEXTBOX->password()); return; } + TEXTBOX->setPassword(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTBOX_max_length) + + CHECK_COMBOBOX(); + if (READ_PROPERTY) { GB.ReturnInteger(TEXTBOX->maxLength()); return; } + TEXTBOX->setMaxLength(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CTEXTBOX_selected) + + CHECK_COMBOBOX(); + GB.ReturnBoolean(TEXTBOX->isSelected()); + +END_METHOD + + +BEGIN_METHOD(TextBox_CursorAt, GB_INTEGER pos) + + int x, y; + + CHECK_COMBOBOX(); + TEXTBOX->getCursorPos(&x, &y, VARGOPT(pos, -1)); + GB.ReturnObject(GEOM.CreatePoint(x, y)); + +END_PROPERTY + +/*************************************************************************** + + .TextBox.Selection + +***************************************************************************/ + +BEGIN_PROPERTY(CTEXTBOX_sel_text) + + char *buf; + + CHECK_COMBOBOX(); + + if (READ_PROPERTY) + { + buf=TEXTBOX->selText(); + GB.ReturnNewZeroString(buf); + g_free(buf); + return; + } + + buf=GB.ToZeroString(PROP(GB_STRING)); + TEXTBOX->setSelText(buf,strlen(buf)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTBOX_sel_length) + + CHECK_COMBOBOX(); + GB.ReturnInteger(TEXTBOX->selLength()); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTBOX_sel_start) + + CHECK_COMBOBOX(); + GB.ReturnInteger(TEXTBOX->selStart()); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CTEXTBOX_sel_clear) + + CHECK_COMBOBOX(); + TEXTBOX->selClear(); + +END_METHOD + +BEGIN_METHOD_VOID(CTEXTBOX_sel_all) + + CHECK_COMBOBOX(); + TEXTBOX->selectAll(); + +END_METHOD + +BEGIN_METHOD(CTEXTBOX_sel_select, GB_INTEGER start; GB_INTEGER length) + + CHECK_COMBOBOX(); + TEXTBOX->select(VARG(start),VARG(length)); + +END_METHOD + + + +/*************************************************************************** + + ComboBox + +***************************************************************************/ + +#undef THIS +#define THIS ((CCOMBOBOX *)_object) + +static void cmb_raise_click(gComboBox *sender) +{ + CWIDGET *_object = GetObject((gControl*)sender); + if (THIS->click) + return; + THIS->click = true; + GB.Raise(THIS, EVENT_Click, 0); + THIS->click = false; +} + + +BEGIN_METHOD(CCOMBOBOX_new, GB_OBJECT parent) + + InitControl(new gComboBox(CONTAINER(VARG(parent))), (CWIDGET*)THIS); + + COMBOBOX->onClick = cmb_raise_click; + COMBOBOX->onChange = txt_raise_change; + COMBOBOX->onActivate = txt_raise_activate; + +END_METHOD + + +BEGIN_PROPERTY(CCOMBOBOX_text) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(COMBOBOX->text()); + else + COMBOBOX->setText(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CCOMBOBOX_popup) + + COMBOBOX->popup(); + +END_METHOD + + +BEGIN_METHOD(CCOMBOBOX_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= COMBOBOX->count()) + { + GB.Error("Bad index"); + return; + } + + THIS->index = index; + RETURN_SELF(); + +END_METHOD + + +BEGIN_PROPERTY(CCOMBOBOX_item_text) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(COMBOBOX->itemText(THIS->index)); + else + COMBOBOX->setItemText(THIS->index,GB.ToZeroString(PROP(GB_STRING))); + + +END_PROPERTY + + +BEGIN_METHOD(CCOMBOBOX_add, GB_STRING item; GB_INTEGER pos) + + int Pos; + char *Item=GB.ToZeroString(ARG(item)); + + if (MISSING(pos)) Pos=COMBOBOX->count(); + else Pos=VARG(pos); + + COMBOBOX->add(Item,Pos); + +END_METHOD + + +BEGIN_METHOD(CCOMBOBOX_remove, GB_INTEGER pos) + + COMBOBOX->remove(VARG(pos)); + +END_METHOD + + +BEGIN_PROPERTY(CCOMBOBOX_sorted) + + if (READ_PROPERTY) { GB.ReturnBoolean(COMBOBOX->isSorted()); return; } + COMBOBOX->setSorted(VPROP(GB_BOOLEAN)); + +END_METHOD + + +BEGIN_PROPERTY(CCOMBOBOX_count) + + GB.ReturnInteger(COMBOBOX->count()); + +END_PROPERTY + + +BEGIN_PROPERTY(CCOMBOBOX_index) + + if (READ_PROPERTY) { GB.ReturnInteger(COMBOBOX->index()); return; } + COMBOBOX->setIndex(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(CCOMBOBOX_current) + + if (!COMBOBOX->count()) { GB.ReturnNull(); return; } + THIS->index = COMBOBOX->index(); + RETURN_SELF(); + +END_PROPERTY + +BEGIN_METHOD(CCOMBOBOX_find, GB_STRING item) + + GB.ReturnInteger(COMBOBOX->find(GB.ToZeroString(ARG(item)))); + +END_METHOD + + +BEGIN_PROPERTY(CCOMBOBOX_list) + + GB_ARRAY array; + int i; + + if (READ_PROPERTY) + { + GB.Array.New(&array, GB_T_STRING, COMBOBOX->count()); + for (i = 0; i < COMBOBOX->count(); i++) + { + *((char **)GB.Array.Get(array, i)) = GB.NewZeroString(COMBOBOX->itemText(i)); + } + + GB.ReturnObject(array); + } + else + { + char *text = GB.NewZeroString(COMBOBOX->text()); + + array = VPROP(GB_OBJECT); + COMBOBOX->lock(); + COMBOBOX->clear(); + if (array) + { + for (i = 0; i < GB.Array.Count(array); i++) + COMBOBOX->add(*((char **)GB.Array.Get(array, i))); + } + COMBOBOX->setText(text); + COMBOBOX->unlock(); + + GB.FreeString(&text); + + if (COMBOBOX->isReadOnly()) + { + if (COMBOBOX->index() < 0 && COMBOBOX->count() > 0) + COMBOBOX->setIndex(0); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(ComboBox_Border) + + if (READ_PROPERTY) + GB.ReturnBoolean(COMBOBOX->hasBorder()); + else + COMBOBOX->setBorder(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +/*************************************************************************** + + Descriptions + +***************************************************************************/ + +GB_DESC CTextBoxSelectionDesc[] = +{ + GB_DECLARE(".TextBox.Selection", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Text", "s", CTEXTBOX_sel_text), + GB_PROPERTY_READ("Length", "i", CTEXTBOX_sel_length), + GB_PROPERTY_READ("Start", "i", CTEXTBOX_sel_start), + GB_PROPERTY_READ("Pos", "i", CTEXTBOX_sel_start), + + GB_METHOD("Hide", 0, CTEXTBOX_sel_clear, 0), + + GB_END_DECLARE +}; + +GB_DESC CTextBoxDesc[] = +{ + GB_DECLARE("TextBox", sizeof(CTEXTBOX)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CTEXTBOX_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", CTEXTBOX_text), + GB_PROPERTY("Alignment", "i", CTEXTBOX_alignment), + GB_PROPERTY_READ("Length", "i", CTEXTBOX_length), + GB_PROPERTY("Pos", "i", CTEXTBOX_pos), + GB_PROPERTY("ReadOnly", "b", CTEXTBOX_read_only), + GB_PROPERTY("Border", "b", CTEXTBOX_border), + GB_PROPERTY("Password", "b", CTEXTBOX_password), + GB_PROPERTY("MaxLength", "i", CTEXTBOX_max_length), + + GB_PROPERTY_SELF("Selection", ".TextBox.Selection"), + GB_METHOD("Select", 0, CTEXTBOX_sel_select, "[(Start)i(Length)i]"), + GB_METHOD("SelectAll", 0, CTEXTBOX_sel_all, 0), + GB_METHOD("Unselect", 0, CTEXTBOX_sel_clear, 0), + GB_PROPERTY_READ("Selected", "b", CTEXTBOX_selected), + + GB_METHOD("Clear", 0, CTEXTBOX_clear, 0), + GB_METHOD("Insert", 0, CTEXTBOX_insert, "(Text)s"), + + GB_METHOD("CursorAt", "Point", TextBox_CursorAt, "[(Pos)i]"), + + GB_EVENT("Change", 0, 0, &EVENT_Change), + GB_EVENT("Activate", 0, 0, &EVENT_Activate), + + TEXTBOX_DESCRIPTION, + + GB_END_DECLARE +}; + + + +GB_DESC CComboBoxItemDesc[] = +{ + GB_DECLARE(".ComboBox.Item", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Text", "s", CCOMBOBOX_item_text), + + GB_END_DECLARE +}; + + +GB_DESC CComboBoxDesc[] = +{ + GB_DECLARE("ComboBox", sizeof(CCOMBOBOX)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CCOMBOBOX_new, "(Parent)Container;"), + GB_METHOD("_get", ".ComboBox.Item", CCOMBOBOX_get, "(Index)i"), + GB_METHOD("Popup", 0, CCOMBOBOX_popup, 0), + GB_METHOD("Clear", 0, CTEXTBOX_clear, 0), + GB_METHOD("Insert", 0, CTEXTBOX_insert, "(Text)s"), + GB_METHOD("Add", 0, CCOMBOBOX_add, "(Item)s[(Index)i]"), + GB_METHOD("Remove", 0, CCOMBOBOX_remove, "(Index)i"), + GB_METHOD("Find", "i", CCOMBOBOX_find, "(Item)s"), + + GB_PROPERTY("Text", "s", CCOMBOBOX_text), + GB_PROPERTY_READ("Length", "i", CTEXTBOX_length), + GB_PROPERTY("Pos", "i", CTEXTBOX_pos), + GB_PROPERTY("ReadOnly", "b", CTEXTBOX_read_only), + GB_PROPERTY("Password", "b", CTEXTBOX_password), + GB_PROPERTY("MaxLength", "i", CTEXTBOX_max_length), + GB_PROPERTY("Border", "b", ComboBox_Border), + + GB_PROPERTY_SELF("Selection", ".TextBox.Selection"), + GB_METHOD("Select", 0, CTEXTBOX_sel_select, "[(Start)i(Length)i]"), + GB_METHOD("SelectAll", 0, CTEXTBOX_sel_all, 0), + GB_METHOD("Unselect", 0, CTEXTBOX_sel_clear, 0), + GB_PROPERTY_READ("Selected", "b", CTEXTBOX_selected), + + GB_PROPERTY("Sorted", "b", CCOMBOBOX_sorted), + GB_PROPERTY("List", "String[]", CCOMBOBOX_list), + GB_PROPERTY_READ("Count", "i", CCOMBOBOX_count), + GB_PROPERTY_READ("Current", ".ComboBox.Item", CCOMBOBOX_current), + GB_PROPERTY("Index", "i", CCOMBOBOX_index), + + GB_METHOD("CursorAt", "Point", TextBox_CursorAt, "[(Pos)i]"), + + GB_EVENT("Change", 0, 0, &EVENT_Change), + GB_EVENT("Activate", 0, 0, &EVENT_Activate), + GB_EVENT("Click", 0, 0, &EVENT_Click), + + COMBOBOX_DESCRIPTION, + + GB_END_DECLARE +}; + + diff --git a/gb.gtk/src/CTextBox.h b/gb.gtk/src/CTextBox.h new file mode 100644 index 00000000..21ed370c --- /dev/null +++ b/gb.gtk/src/CTextBox.h @@ -0,0 +1,62 @@ +/*************************************************************************** + + CTextBox.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTEXTBOX_H +#define __CTEXTBOX_H + +#include "main.h" +#include "gtextbox.h" +#include "gcombobox.h" + +#ifndef __CTEXTBOX_CPP + +extern GB_DESC CTextBoxSelectionDesc[]; +extern GB_DESC CTextBoxDesc[]; +extern GB_DESC CComboBoxDesc[]; +extern GB_DESC CComboBoxItemDesc[]; + +#else + +#define THIS ((CTEXTBOX *)_object) +#define TEXTBOX ((gTextBox *)THIS->ob.widget) +#define COMBOBOX ((gComboBox *)THIS->ob.widget) + +#endif + +typedef + struct + { + CWIDGET ob; + } + CTEXTBOX; + +typedef + struct + { + CWIDGET ob; + int index; + bool click; + } + CCOMBOBOX; + +#endif diff --git a/gb.gtk/src/CTrayIcon.cpp b/gb.gtk/src/CTrayIcon.cpp new file mode 100644 index 00000000..5be312f5 --- /dev/null +++ b/gb.gtk/src/CTrayIcon.cpp @@ -0,0 +1,336 @@ +/*************************************************************************** + + CTrayIcon.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTRAYICON_CPP + +#include + +#include "gambas.h" +#include "widgets.h" +#include "CTrayIcon.h" +#include "CPicture.h" +#include "CContainer.h" +#include "CMenu.h" +#include "gmouse.h" + +DECLARE_EVENT(EVENT_Click); +DECLARE_EVENT(EVENT_MiddleClick); +DECLARE_EVENT(EVENT_Scroll); + +static void cb_destroy(gTrayIcon *sender) +{ + CTRAYICON *_object = (CTRAYICON*)sender->hFree; + THIS->base.widget = NULL; + GB.Unref(POINTER(&_object)); +} + +static void cb_click(gTrayIcon *sender, int button) +{ + if (button == 1) + GB.Raise(sender->hFree, EVENT_Click, 0); + else if (button == 2) + GB.Raise(sender->hFree, EVENT_MiddleClick, 0); +} + +static void cb_menu(gTrayIcon *sender) +{ + CTRAYICON *_object = (CTRAYICON *)sender->hFree; + + if (THIS->popup) + { + void *parent = GB.Parent(THIS); + if (parent && !CWIDGET_check(parent) && GB.Is(parent, CLASS_Control)) + { + gMainWindow *window = ((CWIDGET *)parent)->widget->window(); + gMenu *menu = gMenu::findFromName(window, THIS->popup); + if (menu) + { + menu->popup(); + CMENU_check_popup_click(); + } + return; + } + } + + //GB.Raise(sender->hFree, EVENT_Menu, 0); +} + +static void cb_scroll(gTrayIcon *sender) +{ + GB.Raise(sender->hFree, EVENT_Scroll, 2, GB_T_FLOAT, (float)gMouse::delta(), GB_T_INTEGER, gMouse::orientation()); +} + +static int CTRAYICON_check(void *_object) +{ + return TRAYICON == NULL; +} + +BEGIN_METHOD_VOID(TrayIcon_new) + + THIS->base.widget = new gTrayIcon(); + TRAYICON->hFree = (void*)THIS; + + THIS->base.tag.type = GB_T_NULL; + + TRAYICON->onClick = cb_click; + TRAYICON->onMenu = cb_menu; + TRAYICON->onDestroy = cb_destroy; + TRAYICON->onScroll = cb_scroll; + + GB.Ref(THIS); + //Add_Tray(_object); + +END_METHOD + +static void destroy_tray_icon(CTRAYICON *_object) +{ + if (TRAYICON) + { + delete TRAYICON; + THIS->base.widget = NULL; + MAIN_check_quit(); + } +} + +BEGIN_METHOD_VOID(TrayIcon_free) + + GB.StoreObject(NULL, POINTER(&THIS->picture)); + GB.StoreVariant(NULL, &THIS->base.tag); + GB.FreeString(&THIS->popup); + + destroy_tray_icon(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(TrayIcon_Delete) + + destroy_tray_icon(THIS); + +END_METHOD + + +BEGIN_PROPERTY(TrayIcon_Picture) + + if (READ_PROPERTY) + { + GB.ReturnObject(THIS->picture); + return; + } + + GB.StoreObject(PROP(GB_OBJECT), POINTER(&THIS->picture)); + if (THIS->picture) + TRAYICON->setPicture(THIS->picture->picture); + else + TRAYICON->setPicture(0); + +END_PROPERTY + +BEGIN_PROPERTY(TrayIcon_Text) + + if (READ_PROPERTY) + { + GB.ReturnNewZeroString(TRAYICON->toolTip()); + return; + } + + TRAYICON->setToolTip(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_METHOD_VOID(TrayIcon_Show) + + TRAYICON->show(); + +END_METHOD + +BEGIN_METHOD_VOID(TrayIcon_Hide) + + TRAYICON->hide(); + MAIN_check_quit(); + +END_METHOD + +BEGIN_PROPERTY(TrayIcon_Visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(TRAYICON->isVisible()); + else + { + TRAYICON->setVisible(VPROP(GB_BOOLEAN)); + if (!VPROP(GB_BOOLEAN)) + MAIN_check_quit(); + } + +END_PROPERTY + +BEGIN_PROPERTY(TrayIcon_Tag) + + if (READ_PROPERTY) + GB.ReturnVariant(&THIS->base.tag); + else + GB.StoreVariant(PROP(GB_VARIANT), (void *)&THIS->base.tag); + +END_METHOD + +BEGIN_PROPERTY(TrayIcons_Count) + + GB.ReturnInteger(gTrayIcon::count()); + +END_PROPERTY + +BEGIN_METHOD(TrayIcons_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= gTrayIcon::count()) + { + GB.Error("Bad index"); + return; + } + + GB.ReturnObject(gTrayIcon::get(index)->hFree); + +END_METHOD + +BEGIN_METHOD_VOID(TrayIcons_next) + + int *vl; + + vl = (int *)GB.GetEnum(); + if (*vl >= gTrayIcon::count()) + { + GB.StopEnum(); + } + else + { + GB.ReturnObject (gTrayIcon::get(*vl)->hFree); + (*vl)++; + } + +END_METHOD + +BEGIN_PROPERTY(TrayIcon_PopupMenu) + + if (READ_PROPERTY) + GB.ReturnString(THIS->popup); + else + GB.StoreString(PROP(GB_STRING), &(THIS->popup)); + +END_PROPERTY + +BEGIN_METHOD_VOID(TrayIcon_unknown) + + static char prop[32]; + char *name = GB.GetUnknown(); + + if (strcasecmp(name, "ScreenX") == 0 || strcasecmp(name, "ScreenY") == 0) + { + sprintf(prop, "TrayIcon.%s", name); + GB.Deprecated(GTK_NAME, prop, NULL); + + if (READ_PROPERTY) + { + GB.ReturnInteger(0); + GB.ReturnConvVariant(); + return; + } + else + GB.Error(GB_ERR_NWRITE, GB.GetClassName(NULL), name); + } + else if (strcasecmp(name, "W") == 0 || strcasecmp(name, "Width") == 0 || strcasecmp(name, "H") == 0 || strcasecmp(name, "Height") == 0) + { + sprintf(prop, "TrayIcon.%s", name); + GB.Deprecated(GTK_NAME, prop, NULL); + + if (READ_PROPERTY) + { + GB.ReturnInteger(24); + GB.ReturnConvVariant(); + return; + } + else + GB.Error(GB_ERR_NWRITE, GB.GetClassName(NULL), name); + } + else + { + GB.Error(GB_ERR_NSYMBOL, GB.GetClassName(NULL), name); + } + +END_METHOD + +BEGIN_METHOD_VOID(TrayIcons_DeleteAll) + + gTrayIcon::exit(); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC TrayIconsDesc[] = +{ + GB_DECLARE("TrayIcons", 0), GB_NOT_CREATABLE(), + + GB_STATIC_PROPERTY_READ("Count", "i", TrayIcons_Count), + GB_STATIC_METHOD("_get","TrayIcon", TrayIcons_get,"(Index)i"), + GB_STATIC_METHOD("_next", "TrayIcon", TrayIcons_next, NULL), + GB_STATIC_METHOD("DeleteAll", NULL, TrayIcons_DeleteAll, NULL), + + GB_END_DECLARE +}; + +GB_DESC TrayIconDesc[] = +{ + GB_DECLARE("TrayIcon", sizeof(CTRAYICON)), + GB_HOOK_CHECK(CTRAYICON_check), + + GB_CONSTANT("Horizontal", "i", 0), + GB_CONSTANT("Vertical", "i", 1), + + GB_METHOD("_new", NULL, TrayIcon_new, NULL), + GB_METHOD("_free", NULL, TrayIcon_free, NULL), + + GB_METHOD("Show", NULL, TrayIcon_Show, NULL), + GB_METHOD("Hide", NULL, TrayIcon_Hide, NULL), + GB_METHOD("Delete", NULL, TrayIcon_Hide, NULL), + + GB_PROPERTY("Picture", "Picture", TrayIcon_Picture), + GB_PROPERTY("Icon", "Picture", TrayIcon_Picture), + GB_PROPERTY("Visible", "b", TrayIcon_Visible), + + GB_PROPERTY("Text", "s", TrayIcon_Text), + GB_PROPERTY("PopupMenu", "s", TrayIcon_PopupMenu), + GB_PROPERTY("Tooltip", "s", TrayIcon_Text), + GB_PROPERTY("Tag", "v", TrayIcon_Tag), + + GB_EVENT("Click", NULL, NULL, &EVENT_Click), + GB_EVENT("MiddleClick", NULL, NULL, &EVENT_MiddleClick), + GB_EVENT("Scroll", NULL, "(Delta)f(Orientation)i", &EVENT_Scroll), + + GB_METHOD("_unknown", "v", TrayIcon_unknown, "."), + + TRAYICON_DESCRIPTION, + + GB_END_DECLARE +}; diff --git a/gb.gtk/src/CTrayIcon.h b/gb.gtk/src/CTrayIcon.h new file mode 100644 index 00000000..9ca31f94 --- /dev/null +++ b/gb.gtk/src/CTrayIcon.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + CTrayIcon.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTRAYICON_H +#define __CTRAYICON_H + +#include "main.h" +#include "gtrayicon.h" +#include "CPicture.h" + +#ifndef __CTRAYICON_CPP + +extern GB_DESC TrayIconDesc[]; +extern GB_DESC TrayIconsDesc[]; + +#else + +#define THIS ((CTRAYICON *)_object) +#define TRAYICON ((gTrayIcon *)(THIS->base.widget)) + +#endif + +typedef + struct + { + GTK_CONTROL base; + CPICTURE *picture; + char *popup; + } + CTRAYICON; + +#endif diff --git a/gb.gtk/src/CWatcher.cpp b/gb.gtk/src/CWatcher.cpp new file mode 100644 index 00000000..723749f7 --- /dev/null +++ b/gb.gtk/src/CWatcher.cpp @@ -0,0 +1,141 @@ +/*************************************************************************** + + CWatcher.cpp + + (c) 2004-2005 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWATCHER_CPP + +#include +#include "main.h" +#include "CWatcher.h" + +DECLARE_EVENT(EVENT_Move); +DECLARE_EVENT(EVENT_Resize); +DECLARE_EVENT(EVENT_Show); +DECLARE_EVENT(EVENT_Hide); +//DECLARE_EVENT(EVENT_Remove); + +static void raise_show(GtkWidget *widget, CWATCHER *_object) +{ + GB.Raise(THIS, EVENT_Show, 0); +} + +static void raise_hide(GtkWidget *widget, CWATCHER *_object) +{ + GB.Raise(THIS, EVENT_Hide, 0); +} + +static void raise_configure(GtkWidget *widget, GdkEventConfigure *e, CWATCHER *_object) +{ + GB.Ref(_object); + + if (THIS->x != e->x || THIS->y != e->y) + { + THIS->x = e->x; + THIS->y = e->y; + GB.Raise(THIS, EVENT_Move, 0); + } + + if (THIS->w != e->width || THIS->h != e->height) + { + THIS->w = e->width; + THIS->h = e->height; + GB.Raise(THIS, EVENT_Resize, 0); + } + + GB.Unref(POINTER(&_object)); +} + +static void cb_destroy(GtkWidget *widget, CWATCHER *_object) +{ + GB.Unref(POINTER(&THIS->wid)); + THIS->wid = 0; +} + + +/** Watcher class *********************************************************/ + +BEGIN_METHOD(CWATCHER_new, GB_OBJECT control) + + GtkWidget *wid; + gControl *control; + + THIS->wid = (CWIDGET*)VARG(control); + + if (GB.CheckObject(THIS->wid)) + return; + + //fprintf(stderr, "Watcher %p: Ref %p (%s %p)\n", _object, THIS->wid, GB.GetClassName(THIS->wid), THIS->wid); + GB.Ref((void*)THIS->wid); + + control = THIS->wid->widget; + + THIS->x = control->left() - 1; + THIS->y = control->top() - 1; + THIS->w = control->width() - 1; + THIS->h = control->height() - 1; + + wid = THIS->wid->widget->border; + g_signal_connect(G_OBJECT(wid), "map", G_CALLBACK(raise_show), _object); + g_signal_connect(G_OBJECT(wid), "unmap", G_CALLBACK(raise_hide), _object); + g_signal_connect(G_OBJECT(wid), "configure-event", G_CALLBACK(raise_configure), _object); + g_signal_connect(G_OBJECT(wid), "destroy", G_CALLBACK(cb_destroy), _object); + +END_METHOD + +BEGIN_METHOD_VOID(CWATCHER_free) + + //fprintf(stderr, "Watcher %p: UnRef %p %p ?\n", THIS, THIS->wid, THIS->wid ? THIS->wid->widget : 0); + + if (THIS->wid) + { + if (THIS->wid->widget) + g_signal_handlers_disconnect_matched(G_OBJECT(THIS->wid->widget->border), G_SIGNAL_MATCH_DATA, 0, (GQuark)0, NULL, NULL, _object); + GB.Unref(POINTER(&THIS->wid)); + } + +END_METHOD + +BEGIN_PROPERTY(CWATCHER_control) + + GB.ReturnObject((void*)THIS->wid); + +END_PROPERTY + +GB_DESC CWatcherDesc[] = +{ + GB_DECLARE("Watcher", sizeof(CWATCHER)), + + GB_METHOD("_new", 0, CWATCHER_new, "(Control)Control;"), + GB_METHOD("_free", 0, CWATCHER_free, 0), + + GB_PROPERTY("Control", "Control", CWATCHER_control), + + GB_EVENT("Move", 0, 0, &EVENT_Move), + GB_EVENT("Resize", 0, 0, &EVENT_Resize), + GB_EVENT("Show", 0, 0, &EVENT_Show), + GB_EVENT("Hide", 0, 0, &EVENT_Hide), + + GB_CONSTANT("_DefaultEvent", "s", "Resize"), + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/CWatcher.h b/gb.gtk/src/CWatcher.h new file mode 100644 index 00000000..aba5924c --- /dev/null +++ b/gb.gtk/src/CWatcher.h @@ -0,0 +1,44 @@ +/*************************************************************************** + + CWatcher.h + + (c) 2004-2005 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWATCHER_H +#define __CWATCHER_H + +#include "gambas.h" +#include "CWidget.h" + +typedef + struct + { + GB_BASE ob; + CWIDGET *wid; + int x, y, w, h; + } + CWATCHER; + +#ifndef __CWATCHER_CPP +extern GB_DESC CWatcherDesc[]; +#else +#define THIS ((CWATCHER*)_object) +#endif +#endif diff --git a/gb.gtk/src/CWidget.cpp b/gb.gtk/src/CWidget.cpp new file mode 100644 index 00000000..0e41b5ae --- /dev/null +++ b/gb.gtk/src/CWidget.cpp @@ -0,0 +1,1055 @@ +/*************************************************************************** + + CWidget.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWIDGET_CPP + +#include "widgets.h" +#include "gapplication.h" + +#include "CWidget.h" +#include "CWindow.h" +#include "CMenu.h" +#include "CFont.h" +#include "CMouse.h" +#include "CPicture.h" +#include "CContainer.h" +#include "CClipboard.h" +#include "CFrame.h" + +extern int MAIN_scale; + +DECLARE_EVENT(EVENT_Enter); +DECLARE_EVENT(EVENT_GotFocus); +DECLARE_EVENT(EVENT_LostFocus); +DECLARE_EVENT(EVENT_KeyPress); +DECLARE_EVENT(EVENT_KeyRelease); +DECLARE_EVENT(EVENT_Leave); +DECLARE_EVENT(EVENT_MouseDown); +DECLARE_EVENT(EVENT_MouseMove); +DECLARE_EVENT(EVENT_MouseDrag); +DECLARE_EVENT(EVENT_MouseUp); +DECLARE_EVENT(EVENT_MouseWheel); +DECLARE_EVENT(EVENT_DblClick); +DECLARE_EVENT(EVENT_Menu); +DECLARE_EVENT(EVENT_Drag); +DECLARE_EVENT(EVENT_DragMove); +DECLARE_EVENT(EVENT_Drop); +DECLARE_EVENT(EVENT_DragLeave); + +//Plug +DECLARE_EVENT(EVENT_Plugged); +DECLARE_EVENT(EVENT_Unplugged); +DECLARE_EVENT(EVENT_PlugError); + +//static void *CLASS_Image = NULL; +static GB_CLASS CLASS_UserContainer = 0; +static GB_CLASS CLASS_UserControl = 0; + +/** Action *****************************************************************/ + +static bool has_action(void *control) +{ + if (GB.Is(control, GB.FindClass("Menu"))) + { + gMenu *menu = ((CMENU *)(control))->widget; + return menu ? menu->action() : false; + } + else + { + gControl *ctrl = ((CWIDGET *)(control))->widget; + return ctrl ? ctrl->action() : false; + } +} + +static void set_action(void *control, bool v) +{ + if (GB.Is(control, GB.FindClass("Menu"))) + { + gMenu *menu = ((CMENU *)(control))->widget; + if (menu) + menu->setAction(v); + } + else + { + gControl *ctrl = ((CWIDGET *)(control))->widget; + if (ctrl) + ctrl->setAction(v); + } +} + +#define HAS_ACTION(_control) has_action(_control) +#define SET_ACTION(_control, _flag) set_action(_control, _flag) + +#include "gb.form.action.h" + + +/** Control ****************************************************************/ + +void gb_plug_raise_plugged(gControl *sender) +{ + CWIDGET *_ob=GetObject(sender); + + if (!_ob) return; + GB.Raise((void*)_ob,EVENT_Plugged,0); +} + +void gb_plug_raise_unplugged(gControl *sender) +{ + CWIDGET *_ob=GetObject(sender); + + if (!_ob) return; + GB.Raise((void*)_ob,EVENT_Unplugged,0); +} + +void gb_plug_raise_error(gControl *sender) +{ + CWIDGET *_ob=GetObject(sender); + + if (!_ob) return; + GB.Raise((void*)_ob,EVENT_PlugError,0); +} + +// static void raise_menu(void *_object) +// { +// GB.Raise(THIS, EVENT_Menu, 0); +// GB.Unref(POINTER(&_object)); +// } + +static int to_gambas_event(int type) +{ + switch (type) + { + case gEvent_MousePress: return EVENT_MouseDown; + case gEvent_MouseRelease: return EVENT_MouseUp; + case gEvent_MouseMove: return EVENT_MouseMove; + case gEvent_MouseDrag: return EVENT_MouseDrag; + case gEvent_MouseWheel: return EVENT_MouseWheel; + case gEvent_MouseMenu: return EVENT_Menu; + case gEvent_MouseDblClick: return EVENT_DblClick; + case gEvent_KeyPress: return EVENT_KeyPress; + case gEvent_KeyRelease: return EVENT_KeyRelease; + case gEvent_FocusIn: return EVENT_GotFocus; + case gEvent_FocusOut: return EVENT_LostFocus; + case gEvent_Enter: return EVENT_Enter; + case gEvent_Leave: return EVENT_Leave; + case gEvent_DragMove: return EVENT_DragMove; + case gEvent_Drop: return EVENT_Drop; + default: fprintf(stderr, "warning: to_gambas_event: unhandled event: %d\n", type); return -1; + } +} + +static bool gb_can_raise(gControl *sender, int type) +{ + CWIDGET *ob = GetObject(sender); + if (!ob) + return false; + + type = to_gambas_event(type); + if (type < 0) + return false; + + return GB.CanRaise(ob, type); +} + +bool gb_raise_MouseEvent(gControl *sender, int type) +{ + CWIDGET *ob = GetObject(sender); + bool ret = false; + + if (!ob) return false; // possible, for MouseDrag for example + + switch(type) + { + case gEvent_MouseDrag: + ret = GB.Raise(ob, EVENT_MouseDrag, 0); + break; + + case gEvent_MouseMenu: + + for(;;) + { + if (GB.CanRaise(ob, EVENT_Menu)) + { + int old = gMenu::popupCount(); + if (GB.Raise(ob, EVENT_Menu, 0) || old != gMenu::popupCount()) + return true; + } + + if (ob->popup) + { + gMainWindow *window = sender->window(); + gMenu *menu = gMenu::findFromName(window, ob->popup); + if (menu) + { + menu->popup(); + CMENU_check_popup_click(); + } + return true; + } + + if (sender->isTopLevel()) + break; + + sender = sender->parent(); + ob = GetObject(sender); + } + + break; + + default: + ret = GB.Raise(ob, to_gambas_event(type), 0); + + } + + return ret; +} + +bool gb_raise_KeyEvent(gControl *sender, int type) +{ + return GB.Raise(GetObject(sender), to_gambas_event(type), 0); +} + +void gb_raise_EnterLeave(gControl *sender, int type) +{ + GB.Raise(GetObject(sender), to_gambas_event(type), 0); +} + +void gb_raise_FocusEvent(gControl *sender, int type) +{ + GB.Raise(GetObject(sender), to_gambas_event(type), 0); +} + +bool gb_raise_Drag(gControl *sender) +{ + CWIDGET *_object = GetObject(sender); + + if (!THIS) + return true; + + if (!GB.CanRaise(THIS, EVENT_Drag)) + { + if (GB.CanRaise(THIS, EVENT_DragMove) || GB.CanRaise(THIS, EVENT_Drop)) + return false; + else + return true; + } + + return GB.Raise(THIS, EVENT_Drag, 0); +} + +void gb_raise_DragLeave(gControl *sender) +{ + CWIDGET *_object = GetObject(sender); + + GB.Raise(THIS, EVENT_DragLeave, 0); +} + +bool gb_raise_DragMove(gControl *sender) +{ + CWIDGET *_object = GetObject(sender); + + if (!THIS) + return true; + + if (!GB.CanRaise(THIS, EVENT_DragMove)) + return !GB.CanRaise(THIS, EVENT_Drag); + + return GB.Raise(THIS, EVENT_DragMove, 0); +} + +bool gb_raise_Drop(gControl *sender) +{ + CWIDGET *_object = GetObject(sender); + + if (!THIS) + return false; + + if (!GB.CanRaise(THIS, EVENT_Drop)) + return false; + + GB.Raise(THIS, EVENT_Drop, 0); + return true; +} + +void DeleteControl(gControl *control) +{ + CWIDGET *widget = (CWIDGET*)control->hFree; + + if (!widget) + return; + + GB.Detach(widget); + + GB.StoreVariant(NULL, POINTER(&widget->tag)); + GB.StoreObject(NULL, POINTER(&widget->cursor)); + + CACTION_register(widget, widget->action, NULL); + GB.FreeString(&widget->action); + + if (control->isTopLevel()) + CWINDOW_check_main_window((CWINDOW*)widget); + + GB.Unref(POINTER(&widget->font)); + GB.FreeString(&widget->popup); + + widget->font = NULL; + widget->widget = NULL; + GB.Unref(POINTER(&widget)); + + control->hFree = NULL; +} + + +void InitControl(gControl *control, CWIDGET *widget) +{ + static int n = 0; + char *name; + + GB.Ref((void*)widget); + + widget->widget = control; + control->hFree=(void*)widget; + //fprintf(stderr, "InitControl: %p %p\n", control, widget); + + name = GB.GetLastEventName(); + if (!name) + { + char buffer[16]; + n++; + sprintf(buffer, "#%d", n); + control->setName(buffer); + } + else + control->setName(name); + + control->onFinish = DeleteControl; + control->onMouseEvent = gb_raise_MouseEvent; + control->onKeyEvent = gb_raise_KeyEvent; + control->onFocusEvent = gb_raise_FocusEvent; + control->onDrag = gb_raise_Drag; + control->onDragLeave = gb_raise_DragLeave; + control->onDragMove = gb_raise_DragMove; + control->onDrop = gb_raise_Drop; + control->onEnterLeave = gb_raise_EnterLeave; + control->canRaise = gb_can_raise; + + if (control->isContainer()) + { + ((gContainer *)control)->onBeforeArrange = CCONTAINER_cb_before_arrange; + ((gContainer *)control)->onArrange = CCONTAINER_cb_arrange; + } + + if (control->parent()) + CCONTAINER_raise_insert((CCONTAINER *)control->parent()->hFree, widget); +} + +CWIDGET *GetContainer(CWIDGET *control) +{ + if (!control) return NULL; + + if (!CLASS_UserContainer) CLASS_UserContainer=GB.FindClass("UserContainer"); + if (!CLASS_UserControl) CLASS_UserControl=GB.FindClass("UserControl"); + + if ( GB.Is (control,CLASS_UserContainer) || GB.Is (control,CLASS_UserControl) ) + return (CWIDGET*)((CUSERCONTROL*)control)->container; + + return control; +} + +int CWIDGET_get_handle(void *_object) +{ + return CONTROL->handle(); +} + +int CWIDGET_check(void *_object) +{ + return (!CONTROL || CONTROL->isDestroyed()); +} + +/************************************************************************************* + +Embedder + +**************************************************************************************/ + +BEGIN_METHOD(CPLUGIN_new, GB_OBJECT parent) + + InitControl(new gPlugin(CONTAINER(VARG(parent))), (CWIDGET*)_object); + PLUGIN->onPlug = gb_plug_raise_plugged; + PLUGIN->onUnplug = gb_plug_raise_unplugged; + PLUGIN->onError = gb_plug_raise_error; + +END_METHOD + +BEGIN_PROPERTY(CPLUGIN_client) + + GB.ReturnInteger(PLUGIN->client()); + +END_PROPERTY + +BEGIN_METHOD(CPLUGIN_embed, GB_INTEGER id) + + PLUGIN->plug(VARG(id)); + +END_METHOD + +BEGIN_METHOD_VOID(CPLUGIN_discard) + + PLUGIN->discard(); + +END_METHOD + + +/************************************************************************************** + +Control + +**************************************************************************************/ + +BEGIN_PROPERTY(CWIDGET_x) + + if (READ_PROPERTY) { GB.ReturnInteger(CONTROL->left()); return; } + CONTROL->setLeft(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_screen_x) + + GB.ReturnInteger(CONTROL->screenX()); + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_y) + + if (READ_PROPERTY) { GB.ReturnInteger(CONTROL->top()); return; } + CONTROL->setTop(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_screen_y) + + GB.ReturnInteger(CONTROL->screenY()); + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_w) + + if (READ_PROPERTY) { GB.ReturnInteger(CONTROL->width()); return; } + CONTROL->setWidth(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_h) + + if (READ_PROPERTY) { GB.ReturnInteger(CONTROL->height()); return; } + CONTROL->setHeight(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(CCONTROL_font) + + if (!THIS->font) + { + THIS->font = CFONT_create(new gFont(), 0, THIS); + GB.Ref(THIS->font); + } + + if (READ_PROPERTY) + { + CONTROL->actualFontTo(((CFONT *)THIS->font)->font); + GB.ReturnObject(THIS->font); + } + else + { + CFONT *font = (CFONT *)VPROP(GB_OBJECT); + if (font) + CONTROL->setFont(font->font->copy()); + else + CONTROL->setFont(NULL); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_design) + + if (READ_PROPERTY) { GB.ReturnBoolean(CONTROL->design()); return; } + CONTROL->setDesign(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(CONTROL->isVisible()); + else + CONTROL->setVisible(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_enabled) + + if (READ_PROPERTY) + GB.ReturnBoolean(CONTROL->isEnabled()); + else + CONTROL->setEnabled(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_HasFocus) + + GB.ReturnBoolean(CONTROL->hasFocus()); + +END_PROPERTY + +BEGIN_PROPERTY(Control_Hovered) + + GB.ReturnBoolean(CONTROL->hovered()); + +END_PROPERTY + +BEGIN_PROPERTY(CWIDGET_expand) + + if (READ_PROPERTY) { GB.ReturnBoolean(CONTROL->expand()); return; } + CONTROL->setExpand(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_ignore) + + if (READ_PROPERTY) { GB.ReturnBoolean(CONTROL->ignore()); return; } + CONTROL->setIgnore(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_METHOD(CWIDGET_move, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + if (MISSING(w) && MISSING(h)) + CONTROL->move(VARG(x), VARG(y)); + else + CONTROL->moveResize(VARG(x), VARG(y), VARGOPT(w, CONTROL->width()), VARGOPT(h, CONTROL->height())); + +END_METHOD + + +BEGIN_METHOD(CWIDGET_resize, GB_INTEGER w; GB_INTEGER h) + + CONTROL->resize(VARG(w),VARG(h)); + +END_METHOD + +BEGIN_METHOD(CWIDGET_moveScaled, GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h) + + int x, y, w, h; + + x = (int)(VARG(x) * MAIN_scale + 0.5); + y = (int)(VARG(y) * MAIN_scale + 0.5); + w = (MISSING(w) ? -1 : (int)(VARG(w) * MAIN_scale + 0.5)); + h = (MISSING(h) ? -1 : (int)(VARG(h) * MAIN_scale + 0.5)); + + if (w == 0) w = 1; + if (h == 0) h = 1; + + CONTROL->move(x, y); + if (w > 0 && h > 0) + CONTROL->resize(w, h); + +END_METHOD + + +BEGIN_METHOD(CWIDGET_resizeScaled, GB_FLOAT w; GB_FLOAT h) + + int w, h; + + w = (int)(VARG(w) * MAIN_scale + 0.5); + h = (int)(VARG(h) * MAIN_scale + 0.5); + + if (w == 0) w = 1; + if (h == 0) h = 1; + + CONTROL->resize(w, h); + +END_METHOD + + +BEGIN_METHOD_VOID(CWIDGET_delete) + + if (CONTROL) + CONTROL->destroy(); + +END_METHOD + + +BEGIN_METHOD_VOID(CWIDGET_show) + + CONTROL->show(); + +END_METHOD + + +BEGIN_METHOD_VOID(CWIDGET_hide) + + CONTROL->hide(); + +END_METHOD + +BEGIN_METHOD(CWIDGET_reparent, GB_OBJECT parent; GB_INTEGER x; GB_INTEGER y) + + CCONTAINER *parent = (CCONTAINER*)VARG(parent); + int x, y; + + if (parent || !GB.Is(THIS, CLASS_Window)) + { + if (GB.CheckObject(parent)) + return; + } + + x = CONTROL->x(); + y = CONTROL->y(); + + if (!MISSING(x) && !MISSING(y)) + { + x = VARG(x); + y = VARG(y); + } + + CONTROL->reparent(parent ? CONTAINER(parent) : NULL, x, y); + +END_METHOD + + +BEGIN_METHOD_VOID(CWIDGET_raise) + + CONTROL->raise(); + +END_METHOD + + +BEGIN_METHOD_VOID(CWIDGET_lower) + + CONTROL->lower(); + +END_METHOD + + +BEGIN_PROPERTY(CWIDGET_next) + + if (READ_PROPERTY) + GB.ReturnObject(GetObject(CONTROL->next())); + else + { + CWIDGET *next = (CWIDGET *)VPROP(GB_OBJECT); + CONTROL->setNext(next ? next->widget : NULL); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_previous) + + if (READ_PROPERTY) + GB.ReturnObject(GetObject(CONTROL->previous())); + else + { + CWIDGET *previous = (CWIDGET *)VPROP(GB_OBJECT); + CONTROL->setPrevious(previous ? previous->widget : NULL); + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(CWIDGET_refresh) //, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + CONTROL->refresh(); + +END_METHOD + + +BEGIN_METHOD_VOID(CWIDGET_set_focus) + + CONTROL->setFocus(); + +END_METHOD + + +BEGIN_PROPERTY(CWIDGET_tag) + + if (READ_PROPERTY) + GB.ReturnVariant(&THIS->tag); + else + GB.StoreVariant(PROP(GB_VARIANT), (void *)&THIS->tag); + +END_METHOD + + +BEGIN_PROPERTY(CWIDGET_mouse) + + if (READ_PROPERTY) + GB.ReturnInteger(CONTROL->mouse()); + else + CONTROL->setMouse(VPROP(GB_INTEGER)); + +END_METHOD + + +BEGIN_PROPERTY(Control_Cursor) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->cursor); + else + { + GB.StoreObject(PROP(GB_OBJECT), &THIS->cursor); + CCURSOR *c = (CCURSOR *)THIS->cursor; + CONTROL->setCursor(c ? c->cur : NULL); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_background) + + if (CONTROL->proxy()) + { + if (READ_PROPERTY) + GB.GetProperty(GetObject(CONTROL->proxy()), "Background"); + else + { + GB_VALUE value; + value.type = GB_T_INTEGER; + value._integer.value = VPROP(GB_INTEGER); + GB.SetProperty(GetObject(CONTROL->proxy()), "Background", &value); + } + + return; + } + + if (READ_PROPERTY) + GB.ReturnInteger(CONTROL->background()); + else + CONTROL->setBackground(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_foreground) + + if (CONTROL->proxy()) + { + if (READ_PROPERTY) + GB.GetProperty(GetObject(CONTROL->proxy()), "Foreground"); + else + { + GB_VALUE value; + value.type = GB_T_INTEGER; + value._integer.value = VPROP(GB_INTEGER); + GB.SetProperty(GetObject(CONTROL->proxy()), "Foreground", &value); + } + + return; + } + + if (READ_PROPERTY) + GB.ReturnInteger(CONTROL->foreground()); + else + CONTROL->setForeground(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Parent) + + gContainer *parent = CONTROL->parent(); + + if (parent) + { + while (parent->proxyContainerFor()) + parent = parent->proxyContainerFor(); + } + + GB.ReturnObject(GetObject(parent)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control__Parent) + + GB.ReturnObject(GetObject(CONTROL->parent())); + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_window) + + GB.ReturnObject(GetObject(CONTROL->window())); + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_id) + + GB.ReturnInteger(CONTROL->handle()); + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_tooltip) + + if (READ_PROPERTY) + { + GB.ReturnNewZeroString(CONTROL->toolTip()); + return; + } + + CONTROL->setToolTip(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +/*BEGIN_METHOD_VOID(CWIDGET_screenshot) + + CPICTURE *img; + + GB.New(POINTER(&img), GB.FindClass("Picture"), 0, 0); + if (img->picture) delete img->picture; + img->picture=CONTROL->screenshot(); + GB.ReturnObject((void*)img); + +END_METHOD*/ + +BEGIN_METHOD_VOID(CCONTROL_grab) + + CONTROL->grab(); + +END_METHOD + + +BEGIN_METHOD(CWIDGET_drag, GB_VARIANT data; GB_STRING format) + + GB.ReturnObject(CDRAG_drag(THIS, &VARG(data), MISSING(format) ? NULL : GB.ToZeroString(ARG(format)))); + +END_METHOD + + +BEGIN_PROPERTY(CWIDGET_drop) + + if (READ_PROPERTY) + GB.ReturnBoolean(CONTROL->acceptDrops()); + else + CONTROL->setAcceptDrops(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CCONTROL_tracking) + + if (READ_PROPERTY) + GB.ReturnBoolean(CONTROL->isTracking()); + else + CONTROL->setTracking(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CCONTROL_name) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(CONTROL->name()); + else + CONTROL->setName(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(CCONTROL_action) + + if (READ_PROPERTY) + GB.ReturnString(THIS->action); + else + { + CACTION_register(THIS, THIS->action, GB.ToZeroString(PROP(GB_STRING))); + GB.StoreString(PROP(GB_STRING), &THIS->action); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Proxy) + + if (READ_PROPERTY) + GB.ReturnObject(GetObject(CONTROL->proxy())); + else + { + CWIDGET *proxy = (CWIDGET *)VPROP(GB_OBJECT); + if (CONTROL->setProxy(proxy ? proxy->widget : NULL)) + GB.Error("Circular proxy chain"); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_PopupMenu) + + if (READ_PROPERTY) + GB.ReturnString(THIS->popup); + else + GB.StoreString(PROP(GB_STRING), &(THIS->popup)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_NoTabFocus) + + if (READ_PROPERTY) + GB.ReturnBoolean(CONTROL->isNoTabFocus()); + else + CONTROL->setNoTabFocus(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +GB_DESC CWidgetDesc[] = +{ + GB_DECLARE("Control", sizeof(CWIDGET)), + + GB_NOT_CREATABLE(), + GB_HOOK_CHECK(CWIDGET_check), + + //GB_METHOD("_free", 0, CWIDGET_delete, 0), + + GB_METHOD("Move", NULL, CWIDGET_move, "(X)i(Y)i[(Width)i(Height)i]"), + GB_METHOD("Resize", NULL, CWIDGET_resize, "(Width)i(Height)i"), + GB_METHOD("MoveScaled", NULL, CWIDGET_moveScaled, "(X)f(Y)f[(Width)f(Height)f]"), + GB_METHOD("ResizeScaled", NULL, CWIDGET_resizeScaled, "(Width)f(Height)f"), + GB_METHOD("Delete", NULL, CWIDGET_delete, NULL), + GB_METHOD("Show", NULL, CWIDGET_show, NULL), + GB_METHOD("Hide", NULL, CWIDGET_hide, NULL), + GB_METHOD("Reparent", NULL, CWIDGET_reparent, "(Parent)Container;[(X)i(Y)i]"), + + GB_METHOD("Raise", NULL, CWIDGET_raise, NULL), + GB_METHOD("Lower", NULL, CWIDGET_lower, NULL), + + GB_PROPERTY("Next", "Control", CWIDGET_next), + GB_PROPERTY("Previous", "Control", CWIDGET_previous), + + GB_METHOD("SetFocus", NULL, CWIDGET_set_focus, NULL), + GB_METHOD("Refresh", NULL, CWIDGET_refresh, NULL), + //GB_METHOD("Screenshot", "Picture", CWIDGET_screenshot, 0), + GB_METHOD("Grab", NULL, CCONTROL_grab, NULL), + GB_METHOD("Drag", "Control", CWIDGET_drag, "(Data)v[(Format)s]"), + + GB_PROPERTY("X", "i", CWIDGET_x), + GB_PROPERTY("Y", "i", CWIDGET_y), + GB_PROPERTY_READ("ScreenX", "i", CWIDGET_screen_x), + GB_PROPERTY_READ("ScreenY", "i", CWIDGET_screen_y), + GB_PROPERTY("W", "i", CWIDGET_w), + GB_PROPERTY("H", "i", CWIDGET_h), + GB_PROPERTY("Left", "i", CWIDGET_x), + GB_PROPERTY("Top", "i", CWIDGET_y), + GB_PROPERTY("Width", "i", CWIDGET_w), + GB_PROPERTY("Height", "i", CWIDGET_h), + + GB_PROPERTY("Visible", "b", CWIDGET_visible), + GB_PROPERTY("Enabled", "b", CWIDGET_enabled), + GB_PROPERTY_READ("HasFocus", "b", Control_HasFocus), + GB_PROPERTY_READ("Hovered", "b", Control_Hovered), + + GB_PROPERTY("Expand", "b", CWIDGET_expand), + GB_PROPERTY("Ignore", "b", CWIDGET_ignore), + + GB_PROPERTY("Font", "Font", CCONTROL_font), + GB_PROPERTY("Background", "i", CWIDGET_background), + GB_PROPERTY("Foreground", "i", CWIDGET_foreground), + + GB_PROPERTY("Design", "b", CWIDGET_design), + GB_PROPERTY("Name", "s", CCONTROL_name), + GB_PROPERTY("Tag", "v", CWIDGET_tag), + GB_PROPERTY("Tracking", "b", CCONTROL_tracking), + GB_PROPERTY("Mouse", "i", CWIDGET_mouse), + GB_PROPERTY("Cursor", "Cursor", Control_Cursor), + GB_PROPERTY("ToolTip", "s", CWIDGET_tooltip), + GB_PROPERTY("Drop", "b", CWIDGET_drop), + GB_PROPERTY("Action", "s", CCONTROL_action), + GB_PROPERTY("PopupMenu", "s", Control_PopupMenu), + GB_PROPERTY("Proxy", "Control", Control_Proxy), + GB_PROPERTY("NoTabFocus", "b", Control_NoTabFocus), + + GB_PROPERTY_READ("Parent", "Container", Control_Parent), + GB_PROPERTY_READ("_Parent", "Container", Control__Parent), + GB_PROPERTY_READ("Window", "Window", CWIDGET_window), + GB_PROPERTY_READ("Id", "i", CWIDGET_id), + GB_PROPERTY_READ("Handle", "i", CWIDGET_id), + + GB_EVENT("Enter", NULL, NULL, &EVENT_Enter), + GB_EVENT("GotFocus", NULL, NULL, &EVENT_GotFocus), + GB_EVENT("LostFocus", NULL, NULL, &EVENT_LostFocus), + GB_EVENT("KeyPress", NULL, NULL, &EVENT_KeyPress), + GB_EVENT("KeyRelease", NULL, NULL, &EVENT_KeyRelease), + GB_EVENT("Leave", NULL, NULL, &EVENT_Leave), + GB_EVENT("MouseDown", NULL, NULL, &EVENT_MouseDown), + GB_EVENT("MouseMove", NULL, NULL, &EVENT_MouseMove), + GB_EVENT("MouseDrag", NULL, NULL, &EVENT_MouseDrag), + GB_EVENT("MouseUp", NULL, NULL, &EVENT_MouseUp), + GB_EVENT("MouseWheel", NULL, NULL, &EVENT_MouseWheel), + GB_EVENT("DblClick", NULL, NULL, &EVENT_DblClick), + GB_EVENT("Menu", NULL, NULL, &EVENT_Menu), + GB_EVENT("Drag", NULL, NULL, &EVENT_Drag), + GB_EVENT("DragMove", NULL, NULL, &EVENT_DragMove), + GB_EVENT("Drop", NULL, NULL, &EVENT_Drop), + GB_EVENT("DragLeave", NULL, NULL, &EVENT_DragLeave), + + CONTROL_DESCRIPTION, + + GB_END_DECLARE +}; + +GB_DESC CPluginDesc[] = +{ + GB_DECLARE("Embedder", sizeof(CPLUGIN)), GB_INHERITS("Control"), + + GB_METHOD("_new", 0, CPLUGIN_new, "(Parent)Container;"), + GB_METHOD("Embed", 0, CPLUGIN_embed, "(Client)i"), + GB_METHOD("Discard", 0, CPLUGIN_discard, 0), + + GB_PROPERTY_READ("Client", "i", CPLUGIN_client), + //GB_PROPERTY("Border", "i", CPLUGIN_border), + + GB_EVENT("Embed", NULL, NULL, &EVENT_Plugged), + GB_EVENT("Close", NULL, NULL, &EVENT_Unplugged), + GB_EVENT("Error", NULL, NULL, &EVENT_PlugError), /* TODO */ + + EMBEDDER_DESCRIPTION, + + GB_END_DECLARE +}; + + diff --git a/gb.gtk/src/CWidget.h b/gb.gtk/src/CWidget.h new file mode 100644 index 00000000..c512c066 --- /dev/null +++ b/gb.gtk/src/CWidget.h @@ -0,0 +1,88 @@ +/*************************************************************************** + + CWidget.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWIDGET_H +#define __CWIDGET_H + +#include "main.h" +#include "gcontrol.h" +#include "gplugin.h" + +#ifndef __CWIDGET_CPP + +extern GB_DESC CWidgetDesc[]; +extern GB_DESC CPluginDesc[]; + +#else + +#define THIS ((CWIDGET *)_object) +#define CONTROL (THIS->widget) +#define PLUGIN (((CPLUGIN*)_object)->widget) +#define CPLUGIN_PROPERTIES "*" + +#endif + +typedef + struct + { + GB_BASE ob; + gControl *widget; + GB_VARIANT_VALUE tag; + void *font; + void *cursor; + char *popup; + char *action; + } + CWIDGET; + +typedef + struct + { + GB_BASE ob; + gPlugin *widget; + GB_VARIANT_VALUE tag; + void *font; + char *popup; + } + CPLUGIN; + + +void InitControl(gControl *control, CWIDGET *widget); +void DeleteControl(gControl *control); +CWIDGET *GetContainer(CWIDGET *control); +#define GetObject(_control) ((CWIDGET *)((_control) ? (_control)->hFree : NULL)) + +#define CWIDGET_PROPERTIES CCONTROL_PROPERTIES + +#define CONTAINER(_parent) ((gContainer *)GetContainer((CWIDGET *)_parent)->widget) + +DECLARE_PROPERTY(CCONTROL_action); + +int CWIDGET_check(void *_object); +int CWIDGET_get_handle(void *_object); + +void CACTION_register(void *control, const char *old, const char *key); +void CACTION_raise(void *control); + +#endif + diff --git a/gb.gtk/src/CWindow.cpp b/gb.gtk/src/CWindow.cpp new file mode 100644 index 00000000..8cfb985b --- /dev/null +++ b/gb.gtk/src/CWindow.cpp @@ -0,0 +1,950 @@ +/*************************************************************************** + + CWindow.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWINDOW_CPP + +#include "x11.h" +#include "main.h" +#include "CWindow.h" +#include "CWidget.h" +#include "CContainer.h" +#include "CPicture.h" +#include "CMenu.h" +#include "CDraw.h" +#include "gapplication.h" + +int CWINDOW_Embedder = 0; +bool CWINDOW_Embedded = false; + +CWINDOW *CWINDOW_Active = NULL; +CWINDOW *CWINDOW_Main = NULL; +static int MODAL_windows = 0; + +DECLARE_EVENT(EVENT_Open); +DECLARE_EVENT(EVENT_Show); +DECLARE_EVENT(EVENT_Hide); +DECLARE_EVENT(EVENT_Close); +DECLARE_EVENT(EVENT_Activate); +DECLARE_EVENT(EVENT_Deactivate); +DECLARE_EVENT(EVENT_Move); +DECLARE_EVENT(EVENT_Resize); +DECLARE_EVENT(EVENT_Title); +DECLARE_EVENT(EVENT_Icon); +DECLARE_EVENT(EVENT_Font); + +void CWINDOW_check_main_window(CWINDOW *win) +{ + if (CWINDOW_Main == win) + { + //fprintf(stderr, "CWINDOW_Main = NULL\n"); + CWINDOW_Main = NULL; + } +} + + +static void cb_open(gMainWindow *sender) +{ + CWIDGET *_object = GetObject(sender); + GB.Raise(THIS, EVENT_Open, 0); +} + +static void cb_font_change(gMainWindow *sender) +{ + CWIDGET *_object = GetObject(sender); + GB.Raise(THIS, EVENT_Font, 0); +} + +/*static void gb_post_window_Open(gMainWindow *sender) +{ + GB.Post((GB_POST_FUNC)cb_open, (long)sender); +}*/ + +static void cb_show(gMainWindow *sender) +{ + CWIDGET *_object = GetObject(sender); + + GB.Ref(THIS); + GB.Raise(THIS, EVENT_Show, 0); + if (!sender->spontaneous()) + CACTION_raise(THIS); + GB.Unref(POINTER(&_object)); +} + +/*static void gb_post_window_Show(gMainWindow *sender) +{ + GB.Post( (void (*)())cb_show,(long)sender); +}*/ + +static void cb_hide(gMainWindow *sender) +{ + CWIDGET *_object = GetObject(sender); + + GB.Ref(THIS); + GB.Raise(THIS, EVENT_Hide, 0); + if (!sender->spontaneous()) + CACTION_raise(THIS); + GB.Unref(POINTER(&_object)); +} + +static void cb_move(gMainWindow *sender) +{ + CWIDGET *_object = GetObject(sender); + GB.Raise(THIS, EVENT_Move, 0); +} + +static void cb_resize(gMainWindow *sender) +{ + CWIDGET *_object = GetObject(sender); + GB.Raise(THIS, EVENT_Resize, 0); +} + +static bool close_window(CWINDOW *_object, int ret = 0) +{ + THIS->ret = ret; + + return WINDOW->close(); +} + +void CWINDOW_delete_all() +{ + int i; + gMainWindow *win; + CWINDOW *window; + + for(i = 0;; i++) + { + win = gMainWindow::get(i); + if (!win) + break; + + window = (CWINDOW *)GetObject(win); + if (window == CWINDOW_Main) + continue; + + win->destroy(); + } +} + +bool CWINDOW_must_quit() +{ + int i; + gMainWindow *win; + + for(i = 0; i < gMainWindow::count(); i++) + { + win = gMainWindow::get(i); + if (win->isTopLevel() && win->isOpened()) + return false; + } + + return true; +} + +static bool gb_raise_window_Close(gMainWindow *sender) +{ + CWINDOW *_object = (CWINDOW*)GetObject(sender); + + if (!THIS) + return false; + + if (GB.Raise(THIS, EVENT_Close, 0)) + return true; + + if (CWINDOW_Main && sender == CWINDOW_Main->ob.widget) + { + if (gMainWindow::closeAll()) + return true; + if (!sender->isPersistent()) + { + CWINDOW_delete_all(); + CWINDOW_Main = NULL; + } + } + + if (THIS->embed) + { + CWINDOW_Embedder = 0; + CWINDOW_Embedded = false; + } + + MAIN_check_quit(); + + return false; +} + +static void activate_window(gMainWindow *window) +{ + CWINDOW *active; + + if (window) + { + for(;;) + { + active = (CWINDOW *)GetObject(window); + if (window->isTopLevel()) + break; + if (GB.CanRaise(active, EVENT_Activate)) + break; + window = window->parent()->window(); + } + } + else + active = NULL; + + if (active == CWINDOW_Active) + return; + + if (CWINDOW_Active) + { + GB.Raise(CWINDOW_Active, EVENT_Deactivate, 0); + CWINDOW_Active = 0; + } + + if (active) + { + GB.Raise(active, EVENT_Activate, 0); + } + + CWINDOW_Active = active; +} + +static void cb_activate(gMainWindow *sender) +{ + activate_window(sender); +} + +static void cb_deactivate(gMainWindow *sender) +{ + activate_window(NULL); +} + +/*static void cb_state(gMainWindow *sender) +{ + CWINDOW *_object = (CWINDOW*)GetObject(sender); + GB.Raise(THIS, EVENT_State, 0); +}*/ + + +/*************************************************************************** + + Window + +***************************************************************************/ + +BEGIN_METHOD(CWINDOW_new, GB_OBJECT parent;) + + GB_CLASS CLASS_container; + CWIDGET *parent = NULL; + int plug = 0; + + if (!gApplication::isInit()) + { + GB.Error("GUI is not initialized"); + return; + } + + if (!MISSING(parent) && VARG(parent)) + { + CLASS_container = GB.FindClass("Container"); + if (GB.Conv((GB_VALUE *)(void *)ARG(parent), (GB_TYPE)CLASS_container)) + return; + + parent=(CWIDGET*)VARG(parent); + parent=GetContainer ((CWIDGET*)parent); + } + + if ( CWINDOW_Embedder && (!CWINDOW_Embedded) && (!parent) ) + { + plug=CWINDOW_Embedder; + THIS->embed=true; + } + + if (!parent) + THIS->ob.widget = new gMainWindow(plug); + else + THIS->ob.widget = new gMainWindow((gContainer *)parent->widget); + + InitControl(THIS->ob.widget, (CWIDGET*)THIS); + + WINDOW->onOpen = cb_open; + WINDOW->onShow = cb_show; + WINDOW->onHide = cb_hide; + WINDOW->onMove = cb_move; + WINDOW->onResize = cb_resize; + WINDOW->onClose = gb_raise_window_Close; + WINDOW->onActivate = cb_activate; + WINDOW->onDeactivate = cb_deactivate; + WINDOW->onFontChange = cb_font_change; + +END_METHOD + +BEGIN_METHOD_VOID(CWINDOW_free) + +END_METHOD + + +BEGIN_METHOD_VOID(CWINDOW_next) + + int *vl; + + vl = (int *)GB.GetEnum(); + + if (gMainWindow::count() <= *vl) + GB.StopEnum(); + else + { + GB.ReturnObject(gMainWindow::get(*vl)->hFree); + (*vl)++; + } + +END_METHOD + + +BEGIN_PROPERTY(CWINDOW_count) + + GB.ReturnInteger(gMainWindow::count()); + +END_PROPERTY + + +BEGIN_METHOD(CWINDOW_get_from_id, GB_INTEGER index) + + gMainWindow *ob = gMainWindow::get(VARG(index)); + + GB.ReturnObject(ob ? ob->hFree : NULL); + +END_METHOD + + +BEGIN_METHOD(CWINDOW_close, GB_INTEGER ret) + + GB.ReturnBoolean(close_window(THIS, VARGOPT(ret, 0))); + +END_METHOD + + +BEGIN_METHOD_VOID(CWINDOW_raise) + + WINDOW->raise(); + +END_METHOD + +static bool check_closed(CWINDOW *_object, bool modal) +{ + if (WINDOW->isOpened()) + { + if (modal || WINDOW->isModal()) + { + GB.Error("Window is already opened"); + return TRUE; + } + } + + return FALSE; +} + +BEGIN_METHOD_VOID(CWINDOW_show_modal) + + if (check_closed(THIS, TRUE)) + return; + + THIS->ret = 0; + MODAL_windows++; + WINDOW->showModal(); + MODAL_windows--; + GB.ReturnInteger(THIS->ret); + +END_METHOD + + +BEGIN_METHOD(Window_ShowPopup, GB_INTEGER x; GB_INTEGER y) + + if (check_closed(THIS, TRUE)) + return; + + THIS->ret = 0; + MODAL_windows++; + if (!MISSING(x) && !MISSING(y)) + WINDOW->showPopup(VARG(x), VARG(y)); + else + WINDOW->showPopup(); + MODAL_windows--; + GB.ReturnInteger(THIS->ret); + +END_METHOD + + +BEGIN_METHOD_VOID(CWINDOW_show) + + if (check_closed(THIS, FALSE)) + return; + + WINDOW->showActivate(); + +END_METHOD + + +BEGIN_PROPERTY(CWINDOW_modal) + + GB.ReturnBoolean(WINDOW->isModal()); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_top_level) + + GB.ReturnBoolean(WINDOW->isTopLevel()); + +END_PROPERTY + +// + + +BEGIN_PROPERTY(CWINDOW_persistent) + + if (READ_PROPERTY) + GB.ReturnBoolean (WINDOW->isPersistent()); + else + WINDOW->setPersistent(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CWINDOW_text) + + if (READ_PROPERTY) { GB.ReturnNewZeroString(WINDOW->text()); return; } + WINDOW->setText((const char*)GB.ToZeroString(PROP(GB_STRING))); + GB.Raise(THIS, EVENT_Title, 0); + +END_PROPERTY + + +BEGIN_PROPERTY(CWINDOW_border) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->hasBorder()); + else + WINDOW->setBorder(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CWINDOW_resizable) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->isResizable()); + else + WINDOW->setResizable(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +#if 0 +BEGIN_PROPERTY(CWINDOW_type) + + if (READ_PROPERTY) + GB.ReturnInteger(WINDOW->getType()); + else + { + fprintf(stderr, "gb.gtk: warning: Window.Type is deprecated\n"); + WINDOW->setType(VPROP(GB_INTEGER)); + } + +END_PROPERTY +#endif + +BEGIN_PROPERTY(Window_Utility) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->isUtility()); + else + WINDOW->setUtility(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CWINDOW_icon) + + if (READ_PROPERTY) + { + gPicture *pic = WINDOW->icon(); + GB.ReturnObject(pic ? pic->getTagValue() : 0); + } + else + { + CPICTURE *pic = (CPICTURE *)VPROP(GB_OBJECT); + WINDOW->setIcon(pic ? pic->picture : 0); + GB.Raise(THIS, EVENT_Icon, 0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CWINDOW_top_only) + + if (READ_PROPERTY) { GB.ReturnBoolean(WINDOW->topOnly()); return; } + WINDOW->setTopOnly(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CWINDOW_skip_taskbar) + + if (READ_PROPERTY) { GB.ReturnBoolean(WINDOW->skipTaskBar()); return; } + WINDOW->setSkipTaskBar(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CWINDOW_mask) + + if (READ_PROPERTY){ GB.ReturnBoolean(WINDOW->mask()); return; } + WINDOW->setMask(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_picture) + + if (READ_PROPERTY) + { + gPicture *pic = WINDOW->picture(); + GB.ReturnObject(pic ? pic->getTagValue() : 0); + } + else + { + CPICTURE *pic = (CPICTURE *)VPROP(GB_OBJECT); + WINDOW->setPicture(pic ? pic->picture : 0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CWINDOW_minimized) + + if (READ_PROPERTY) { GB.ReturnBoolean ( WINDOW->minimized() ); return; } + WINDOW->setMinimized(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_maximized) + + if (READ_PROPERTY) { GB.ReturnBoolean ( WINDOW->maximized() ); return; } + WINDOW->setMaximized(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_fullscreen) + + if (READ_PROPERTY) { GB.ReturnBoolean ( WINDOW->fullscreen() ); return; } + WINDOW->setFullscreen(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD_VOID(CWINDOW_center) + + WINDOW->center(); + +END_METHOD + + +BEGIN_PROPERTY(CWINDOW_menu_count) + + GB.ReturnInteger(WINDOW->menuCount()); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_stacking) + + if (READ_PROPERTY) + GB.ReturnInteger(WINDOW->getStacking()); + else + WINDOW->setStacking(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_sticky) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->getSticky()); + else + WINDOW->setSticky(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD_VOID(CWINDOW_menu_next) + + gMenu *mn; + int *ct; + + ct = (int *)GB.GetEnum(); + + if (*ct >= gMenu::winChildCount(WINDOW)) + { + GB.StopEnum(); + return; + } + + mn = gMenu::winChildMenu(WINDOW, *ct); + (*ct)++; + + GB.ReturnObject(mn->hFree); + +END_PROPERTY + + +BEGIN_METHOD(CWINDOW_menu_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= gMenu::winChildCount(WINDOW)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(gMenu::winChildMenu(WINDOW, index)->hFree); + +END_METHOD + +BEGIN_PROPERTY(CWINDOW_control_count) + + GB.ReturnInteger(WINDOW->controlCount()); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CWINDOW_control_next) + + int index; + gControl *control; + + index = ENUM(int); + + control = WINDOW->getControl(index); + + if (!control) + { + GB.StopEnum(); + return; + } + + ENUM(int) = index + 1; + GB.ReturnObject(GetObject(control)); + +END_PROPERTY + + +BEGIN_METHOD(CWINDOW_get, GB_STRING name) + + gControl *control = WINDOW->getControl(GB.ToZeroString(ARG(name))); + + if (!control) + GB.ReturnNull(); + else + GB.ReturnObject(GetObject(control)); + +END_METHOD + +BEGIN_PROPERTY(CWINDOW_closed) + + GB.ReturnBoolean(WINDOW->isClosed()); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_menu_visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->isMenuBarVisible()); + else + WINDOW->setMenuBarVisible(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD_VOID(CWINDOW_menu_show) + + WINDOW->setMenuBarVisible(true); + +END_METHOD + +BEGIN_METHOD_VOID(CWINDOW_menu_hide) + + WINDOW->setMenuBarVisible(false); + +END_METHOD + +BEGIN_PROPERTY(Window_Opacity) + + if (READ_PROPERTY) + GB.ReturnInteger(WINDOW->opacity() * 100); + else + { + double opacity = VPROP(GB_INTEGER) / 100.0; + + if (opacity < 0.0) + opacity = 0.0; + else if (opacity > 1.0) + opacity = 1.0; + + WINDOW->setOpacity(opacity); + } + +END_PROPERTY + +BEGIN_PROPERTY(Window_Screen) + + GB.ReturnInteger(WINDOW->screen()); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Transparent) + + bool trans = WINDOW->isTransparent(); + + if (READ_PROPERTY) + GB.ReturnBoolean(trans); + else + { + bool new_trans = VPROP(GB_BOOLEAN); + if (trans == new_trans) + return; + + if (!new_trans) + { + GB.Error("Transparent property cannot be reset"); + return; + } + + WINDOW->setTransparent(true); + } + +END_PROPERTY + +BEGIN_PROPERTY(Window_TakeFocus) + + if (READ_PROPERTY) + GB.ReturnBoolean(!WINDOW->isNoTakeFocus()); + else + WINDOW->setNoTakeFocus(!VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD_VOID(Window_Activate) + + WINDOW->activate(); + +END_METHOD + +//------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(CFORM_new) + + if (!GB.Parent(_object)) + GB.Attach(_object, _object, "Form"); + + WINDOW->setName(GB.GetClassName((void *)THIS)); + +END_METHOD + + +BEGIN_METHOD_VOID(CFORM_main) + + CWINDOW *form; + + form = (CWINDOW *)GB.AutoCreate(GB.GetClass(NULL), 0); + if (!((gMainWindow *)form->ob.widget)->isHidden()) + CWINDOW_show(form, NULL); + +END_METHOD + + +BEGIN_METHOD(CFORM_load, GB_OBJECT parent) + + if (!MISSING(parent)) + GB.Push(1, GB_T_OBJECT, VARG(parent)); + + GB.AutoCreate(GB.GetClass(NULL), MISSING(parent) ? 0 : 1); + +END_METHOD + +/*************************************************************************** + + Declarations + +***************************************************************************/ + +GB_DESC CWindowMenusDesc[] = +{ + GB_DECLARE(".Window.Menus", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_next", "Menu", CWINDOW_menu_next, 0), + GB_METHOD("_get", "Menu", CWINDOW_menu_get, "(Index)i"), + GB_PROPERTY_READ("Count", "i", CWINDOW_menu_count), + GB_METHOD("Show", NULL, CWINDOW_menu_show, NULL), + GB_METHOD("Hide", NULL, CWINDOW_menu_hide, NULL), + GB_PROPERTY("Visible", "b", CWINDOW_menu_visible), + + GB_END_DECLARE +}; + +GB_DESC CWindowControlsDesc[] = +{ + GB_DECLARE(".Window.Controls", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_next", "Control", CWINDOW_control_next, 0), + GB_METHOD("_get", "Control", CWINDOW_get, "(Name)s"), + GB_PROPERTY_READ("Count", "i", CWINDOW_control_count), + + GB_END_DECLARE +}; + +#if 0 +GB_DESC CWindowTypeDesc[] = +{ + GB_DECLARE("WindowType", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("Normal", "i", _NET_WM_WINDOW_TYPE_NORMAL), + GB_CONSTANT("Dock", "i", _NET_WM_WINDOW_TYPE_DOCK), + GB_CONSTANT("Toolbar", "i", _NET_WM_WINDOW_TYPE_TOOLBAR), + GB_CONSTANT("Menu", "i", _NET_WM_WINDOW_TYPE_MENU), + GB_CONSTANT("Utility", "i", _NET_WM_WINDOW_TYPE_UTILITY), + GB_CONSTANT("Splash", "i", _NET_WM_WINDOW_TYPE_SPLASH), + GB_CONSTANT("Dialog", "i", _NET_WM_WINDOW_TYPE_DIALOG), + GB_CONSTANT("DropDownMenu", "i", _NET_WM_WINDOW_TYPE_DROPDOWN_MENU), + GB_CONSTANT("PopupMenu", "i", _NET_WM_WINDOW_TYPE_POPUP_MENU), + GB_CONSTANT("Tooltip", "i", _NET_WM_WINDOW_TYPE_TOOLTIP), + GB_CONSTANT("Notification", "i", _NET_WM_WINDOW_TYPE_NOTIFICATION), + GB_CONSTANT("Combo", "i", _NET_WM_WINDOW_TYPE_COMBO), + GB_CONSTANT("DragAndDrop", "i", _NET_WM_WINDOW_TYPE_DND), + GB_CONSTANT("Desktop", "i", _NET_WM_WINDOW_TYPE_DESKTOP), + + GB_END_DECLARE +}; +#endif + +GB_DESC CWindowDesc[] = +{ + GB_DECLARE("Window", sizeof(CWINDOW)), GB_INHERITS("Container"), + + GB_CONSTANT("Normal", "i", 0), + GB_CONSTANT("Above", "i", 1), + GB_CONSTANT("Below", "i", 2), + + GB_METHOD("_new", NULL, CWINDOW_new, "[(Parent)Control;]"), + GB_METHOD("_free", NULL, CWINDOW_free, NULL), + GB_METHOD("_get", "Control", CWINDOW_get, "(Name)s"), + + GB_METHOD("Close", "b", CWINDOW_close, "[(Return)i]"), + GB_METHOD("Raise", NULL, CWINDOW_raise, NULL), + GB_METHOD("Show", NULL, CWINDOW_show, NULL), + //GB_METHOD("Hide", NULL, CWINDOW_hide, NULL), + //GB_PROPERTY("Visible", "b", CWINDOW_visible), + GB_METHOD("ShowModal", "i", CWINDOW_show_modal, NULL), + GB_METHOD("ShowDialog", "i", CWINDOW_show_modal, NULL), + GB_METHOD("ShowPopup", "i", Window_ShowPopup, "[(X)i(Y)i]"), + GB_METHOD("Center", NULL, CWINDOW_center, NULL), + GB_METHOD("Activate", NULL, Window_Activate, NULL), + + GB_PROPERTY_READ("Modal", "b", CWINDOW_modal), + GB_PROPERTY_READ("TopLevel", "b", CWINDOW_top_level), + GB_PROPERTY_READ("Closed", "b", CWINDOW_closed), + + GB_PROPERTY("Stacking","i",CWINDOW_stacking), + GB_PROPERTY("Sticky","b",CWINDOW_sticky), + GB_PROPERTY("Persistent", "b", CWINDOW_persistent), + GB_PROPERTY("Text", "s", CWINDOW_text), + GB_PROPERTY("Title", "s", CWINDOW_text), + GB_PROPERTY("Caption", "s", CWINDOW_text), + GB_PROPERTY("Icon", "Picture", CWINDOW_icon), + + GB_PROPERTY("Minimized", "b", CWINDOW_minimized), + GB_PROPERTY("Maximized", "b", CWINDOW_maximized), + GB_PROPERTY("FullScreen", "b", CWINDOW_fullscreen), + + GB_PROPERTY("TopOnly", "b", CWINDOW_top_only), + GB_PROPERTY("SkipTaskbar", "b", CWINDOW_skip_taskbar), + GB_PROPERTY("Opacity", "i", Window_Opacity), + GB_PROPERTY("Transparent", "b", Window_Transparent), + GB_PROPERTY("TakeFocus", "b", Window_TakeFocus), + + GB_PROPERTY("Arrangement", "i", Container_Arrangement), + GB_PROPERTY("AutoResize", "b", Container_AutoResize), + GB_PROPERTY("Padding", "i", Container_Padding), + GB_PROPERTY("Spacing", "b", Container_Spacing), + GB_PROPERTY("Margin", "b", Container_Margin), + GB_PROPERTY("Indent", "b", Container_Indent), + GB_PROPERTY("Invert", "b", Container_Invert), + + //GB_PROPERTY("Type", "i", CWINDOW_type), + GB_PROPERTY("Utility", "b", Window_Utility), + GB_PROPERTY("Border", "b", CWINDOW_border), + GB_PROPERTY("Resizable", "b", CWINDOW_resizable), + + GB_PROPERTY("Mask","b",CWINDOW_mask), + GB_PROPERTY("Picture", "Picture", CWINDOW_picture), + + GB_PROPERTY_READ("Screen", "i", Window_Screen), + + GB_PROPERTY_SELF("Menus", ".Window.Menus"), + GB_PROPERTY_SELF("Controls", ".Window.Controls"), + + WINDOW_DESCRIPTION, + + GB_EVENT("Close", NULL, NULL, &EVENT_Close), + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + GB_EVENT("Activate", NULL, NULL, &EVENT_Activate), + GB_EVENT("Deactivate", NULL, NULL, &EVENT_Deactivate), + GB_EVENT("Move", NULL, NULL, &EVENT_Move), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + GB_EVENT("Show", NULL, NULL, &EVENT_Show), + GB_EVENT("Hide", NULL, NULL, &EVENT_Hide), + GB_EVENT("Title", NULL, NULL, &EVENT_Title), + GB_EVENT("Icon", NULL, NULL, &EVENT_Icon), + GB_EVENT("Font", NULL, NULL, &EVENT_Font), + + GB_END_DECLARE +}; + + +GB_DESC CWindowsDesc[] = +{ + GB_DECLARE("Windows", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_next", "Window", CWINDOW_next, 0), + GB_STATIC_METHOD("_get", "Window", CWINDOW_get_from_id, "(Id)i"), + GB_STATIC_PROPERTY_READ("Count", "i", CWINDOW_count), + + GB_END_DECLARE +}; + + +GB_DESC CFormDesc[] = +{ + GB_DECLARE("Form", sizeof(CFORM)), GB_INHERITS("Window"), + GB_AUTO_CREATABLE(), + + GB_STATIC_METHOD("Main", 0, CFORM_main, 0), + GB_STATIC_METHOD("Load", 0, CFORM_load, "[(Parent)Control;]"), + GB_METHOD("_new", 0, CFORM_new, 0), + + FORM_DESCRIPTION, + + GB_END_DECLARE +}; diff --git a/gb.gtk/src/CWindow.h b/gb.gtk/src/CWindow.h new file mode 100644 index 00000000..76305e96 --- /dev/null +++ b/gb.gtk/src/CWindow.h @@ -0,0 +1,69 @@ +/*************************************************************************** + + CWindow.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWINDOW_H +#define __CWINDOW_H + +#include "main.h" +#include "gmainwindow.h" +#include "CWidget.h" +#include "CPicture.h" + +typedef + struct + { + CWIDGET ob; + int ret; + unsigned embed : 1; + } + CWINDOW; + +typedef + CWINDOW CFORM; + +#ifndef __CWINDOW_CPP + +extern GB_DESC CWindowMenusDesc[]; +extern GB_DESC CWindowControlsDesc[]; +extern GB_DESC CWindowDesc[]; +//extern GB_DESC CWindowTypeDesc[]; +extern GB_DESC CWindowsDesc[]; +extern GB_DESC CFormDesc[]; + +extern CWINDOW *CWINDOW_Active; +extern CWINDOW *CWINDOW_Main; + +#else + +#define THIS ((CWINDOW *)_object) +#define WINDOW ((gMainWindow*)THIS->ob.widget) + +#endif + +void CWINDOW_check_main_window(CWINDOW *win); +bool CWINDOW_must_quit(); +void CWINDOW_close_all(); +void CWINDOW_delete_all(); + +#endif + diff --git a/gb.gtk/src/Makefile.am b/gb.gtk/src/Makefile.am new file mode 100644 index 00000000..5ab3dedc --- /dev/null +++ b/gb.gtk/src/Makefile.am @@ -0,0 +1,101 @@ +COMPONENT = gb.gtk +include $(top_srcdir)/component.am + +SUBDIRS = . @GTKOPENGL_DIR@ + +gblib_LTLIBRARIES = gb.gtk.la + +gb_gtk_la_LIBADD = @THREAD_LIB@ @GTK_LIB@ +gb_gtk_la_LDFLAGS = -module @LD_FLAGS@ @GTK_LDFLAGS@ +gb_gtk_la_CPPFLAGS = @THREAD_INC@ @GTK_INC@ -I$(top_srcdir)/share + +gb_gtk_la_SOURCES = \ + gtag.h ggambastag.h gshare.h \ + gb.gtk.h widgets.h font-parser.h font-parser.cpp \ + gtools.cpp gcolor.h \ + gfont.cpp gpicture.cpp \ + gdesktop.cpp gmessage.cpp \ + gcontrol.cpp gcontainer.cpp gseparator.cpp \ + gbutton.cpp gslider.cpp glabel.cpp gsignals.cpp \ + gmoviebox.cpp gcombobox.cpp gtextbox.cpp gtextarea.cpp \ + gframe.cpp gtabstrip.cpp \ + gmenu.cpp gtrayicon.cpp gmainwindow.cpp \ + gtree.h gtree.cpp \ + watcher.h watcher.cpp \ + CStock.cpp CStock.h \ + CConst.h CConst.cpp CColor.h CColor.cpp \ + CFont.h CFont.cpp \ + CKey.h CKey.cpp \ + CScreen.h CScreen.cpp \ + CStyle.h CStyle.cpp \ + CMessage.h CMessage.cpp CDialog.h CDialog.cpp \ + CDraw.h CDraw.cpp \ + cpaint_impl.h cpaint_impl.cpp \ + CImage.h CImage.cpp \ + CPicture.h CPicture.cpp \ + CClipboard.h CClipboard.cpp \ + CMouse.h CMouse.cpp \ + CWatcher.h CWatcher.cpp \ + CWidget.h CWidget.cpp CContainer.h CContainer.cpp \ + CSeparator.h CSeparator.cpp \ + CDrawingArea.h CDrawingArea.cpp \ + CLabel.h CLabel.cpp \ + CSlider.h CSlider.cpp \ + CButton.h CButton.cpp \ + CMovieBox.h CMovieBox.cpp \ + CTextBox.h CTextBox.cpp \ + CTextArea.h CTextArea.cpp \ + CFrame.h CFrame.cpp \ + CTabStrip.h CTabStrip.cpp \ + CMenu.h CMenu.cpp CTrayIcon.h CTrayIcon.cpp CWindow.h CWindow.cpp \ + cprinter.h cprinter.cpp \ + csvgimage.h csvgimage.cpp \ + main.h main.cpp \ + gkey.h gkey.cpp \ + gcursor.h gcursor.cpp \ + gmouse.h gmouse.cpp \ + gdesktop.h \ + gpicture.h \ + gfont.h \ + gmessage.h \ + gdialog.h \ + gcontrol.h \ + gseparator.h \ + gtrayicon.h \ + gplugin.h \ + glabel.h \ + gbutton.h \ + gmoviebox.h \ + gcombobox.h \ + gtextbox.h \ + gtextarea.h \ + gslider.h \ + gscrollbar.h \ + gcontainer.h \ + gdrawingarea.h gdrawingarea.cpp \ + gtabstrip.h \ + gframe.h \ + gmenu.h \ + gmainwindow.h \ + gapplication.h gapplication.cpp \ + gclipboard.h \ + gdrag.h gdrag.cpp \ + gtools.h kentities.h \ + gprinter.h gprinter.cpp \ + gglarea.h gglarea.cpp \ + x11.h x11.c \ + desktop.h desktop.c \ + sm/bonobo-macros.h \ + sm/gnome-macros.h \ + sm/gnome-uidefs.h \ + sm/libgnomeui.h \ + sm/libgnomeuiP.h \ + sm/gnome-client.h \ + sm/gnome-client.c \ + sm/gnome-ice.h \ + sm/gnome-ice.c \ + sm/gnome-marshal.h \ + sm/gnome-marshal.c \ + sm/gnometypebuiltins.h \ + sm/gnometypebuiltins.c \ + sm/sm.h diff --git a/gb.gtk/src/cpaint_impl.cpp b/gb.gtk/src/cpaint_impl.cpp new file mode 100644 index 00000000..38cd2bdf --- /dev/null +++ b/gb.gtk/src/cpaint_impl.cpp @@ -0,0 +1,1668 @@ +/*************************************************************************** + + cpaint_impl.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPAINT_IMPL_CPP + +#include +#include + +#include "gambas.h" +#include "gb_common.h" +#include "widgets.h" +#include "gdesktop.h" + +#include "CWindow.h" +#include "CDrawingArea.h" +#include "CPicture.h" +#include "CImage.h" +#include "cprinter.h" +#include "csvgimage.h" +#include "CFont.h" +#include "CDraw.h" +#include "cpaint_impl.h" + +/**** Cairo image management *********************************************/ + +static void free_image(GB_IMG *img, void *image) +{ + cairo_surface_destroy((cairo_surface_t *)image); +} + +static void *temp_image(GB_IMG *img) +{ + cairo_surface_t *image; + + if (!img->data) + image = NULL; // TODO: use a static small image surface + else + image = cairo_image_surface_create_for_data(img->data, CAIRO_FORMAT_ARGB32, img->width, img->height, + cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, img->width)); + return image; +} + +static GB_IMG_OWNER _image_owner = { + "gb.gtk.cairo", + GB_IMAGE_BGRP, + free_image, + free_image, + temp_image + }; + +static cairo_surface_t *check_image(void *img) +{ + // TODO: format is endian-dependent + return (cairo_surface_t *)IMAGE.Check((GB_IMG *)img, &_image_owner); +} + +static GB_COLOR get_color(GB_PAINT *d, GB_COLOR col) +{ + if (col == GB_COLOR_DEFAULT) + { + if (GB.Is(d->device, CLASS_DrawingArea)) + col = (((CWIDGET *)d->device)->widget)->realBackground(true); + else + col = 0xFFFFFF; + } + + return col; +} + + +/**** Paint implementation ***********************************************/ + +typedef + struct { + cairo_t *context; + GtkPrintContext *print_context; + CFONT *font; + CFONT **font_stack; + PangoLayout *layout; + float ascent; + cairo_matrix_t init; + double dx; + double dy; + double bx; + double by; + bool invert; + } + GB_PAINT_EXTRA; + +#define EXTRA(d) ((GB_PAINT_EXTRA *)d->extra) +#define CONTEXT(d) EXTRA(d)->context +//#define DX(d) EXTRA(d)->dx +//#define DY(d) EXTRA(d)->dy +#define DX(d) 0 +#define DY(d) 0 + +static bool init_painting(GB_PAINT *d, cairo_surface_t *target, double w, double h, int rx, int ry) +{ + GB_PAINT_EXTRA *dx = EXTRA(d); + + d->area.width = w; + d->area.height = h; + d->resolutionX = rx; //device->physicalDpiX(); + d->resolutionY = ry; //device->physicalDpiY(); + + /*if (device->paintingActive()) + { + GB.Error("Device already being painted"); + return TRUE; + }*/ + + if (target) + { + dx->context = cairo_create(target); + cairo_surface_destroy(target); + } + + cairo_set_line_width(CONTEXT(d), 1.0); + /*cairo_set_line_join(CONTEXT(d), CAIRO_LINE_JOIN_MITER); + cairo_set_miter_limit(CONTEXT(d), 10.0); + cairo_set_line_cap(CONTEXT(d), CAIRO_LINE_CAP_BUTT);*/ + + dx->font = NULL; + dx->font_stack = NULL; + + cairo_get_matrix(CONTEXT(d), &EXTRA(d)->init); + + return FALSE; +} + +#if 0 +static void _gtk_print_context_rotate_according_to_orientation (GtkPrintContext *context, cairo_t *cr) +{ + cairo_matrix_t matrix; + gdouble width, height; + GtkPageSetup *page = gtk_print_context_get_page_setup(context); + + /*width = gtk_paper_size_get_width (paper_size, GTK_UNIT_INCH); + width = width * context->surface_dpi_x / context->pixels_per_unit_x; + height = gtk_paper_size_get_height (paper_size, GTK_UNIT_INCH); + height = height * context->surface_dpi_y / context->pixels_per_unit_y;*/ + + width = gtk_print_context_get_width(context); + height = gtk_print_context_get_height(context); + + switch (gtk_page_setup_get_orientation (page)) + { + default: + case GTK_PAGE_ORIENTATION_PORTRAIT: + break; + case GTK_PAGE_ORIENTATION_LANDSCAPE: + fprintf(stderr, "rotate landscape\n"); + cairo_translate (cr, 0, height); + cairo_matrix_init (&matrix, + 0, -1, + 1, 0, + 0, 0); + cairo_transform (cr, &matrix); + break; + case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT: + cairo_translate (cr, width, height); + cairo_matrix_init (&matrix, + -1, 0, + 0, -1, + 0, 0); + cairo_transform (cr, &matrix); + break; + case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE: + cairo_translate (cr, width, 0); + cairo_matrix_init (&matrix, + 0, 1, + -1, 0, + 0, 0); + cairo_transform (cr, &matrix); + break; + } +} +#endif + +static int Begin(GB_PAINT *d) +{ + void *device = d->device; + cairo_surface_t *target = NULL; + double w, h; + int rx = 96, ry = 96; + + EXTRA(d)->print_context = NULL; + EXTRA(d)->dx = EXTRA(d)->dy = 0; + + if (GB.Is(device, CLASS_Picture)) + { + gPicture *picture = ((CPICTURE *)device)->picture; + + if (picture->isVoid()) + { + GB.Error("Bad picture"); + return TRUE; + } + + w = picture->width(); + h = picture->height(); + +#ifdef GTK3 + target = picture->getSurface(); + cairo_surface_reference(target); +#else + GdkDrawable *pixmap = (GdkDrawable *)picture->getPixmap(); + + target = + cairo_xlib_surface_create(gdk_x11_drawable_get_xdisplay(pixmap), gdk_x11_drawable_get_xid(pixmap), + gdk_x11_visual_get_xvisual(gdk_drawable_get_visual(pixmap)), w, h); +#endif + } + else if (GB.Is(device, CLASS_Image)) + { + target = check_image(device); + if (!target) + { + GB.Error("Bad image"); + return TRUE; + } + + cairo_surface_reference(target); + w = ((GB_IMG *)device)->width; + h = ((GB_IMG *)device)->height; + } + else if (GB.Is(device, CLASS_DrawingArea)) + { + gDrawingArea *wid = (gDrawingArea *)((CWIDGET *)device)->widget; + double dx = 0, dy = 0; + + w = wid->width(); + h = wid->height(); + +#ifdef GTK3 + if (wid->cached()) + { + EXTRA(d)->context = cairo_create(wid->buffer); + } + else + { + if (!wid->inDrawEvent()) + { + GB.Error("Cannot paint outside of Draw event handler"); + return TRUE; + } + + EXTRA(d)->context = ((CDRAWINGAREA *)device)->context; + cairo_reference(CONTEXT(d)); + + /*GtkAllocation a; + gtk_widget_get_allocation(wid->border, &a); + dx = a.x; + dy = a.y;*/ + + } + + rx = gDesktop::resolution(); //device->physicalDpiX(); + ry = gDesktop::resolution(); //device->physicalDpiY(); + +#else + GdkDrawable *dr; + + if (wid->cached()) + { + wid->resizeCache(); // Why is it needed? + dr = wid->buffer; + } + else + { + if (!wid->inDrawEvent()) + { + GB.Error("Cannot paint outside of Draw event handler"); + return TRUE; + } + + GtkAllocation *a = &wid->widget->allocation; + dx = a->x; + dy = a->y; + dr = gtk_widget_get_window(wid->widget); + } + + rx = gDesktop::resolution(); //device->physicalDpiX(); + ry = gDesktop::resolution(); //device->physicalDpiY(); + + EXTRA(d)->context = gdk_cairo_create(dr); +#endif + + EXTRA(d)->dx = dx; + EXTRA(d)->dy = dy; + + cairo_translate(CONTEXT(d), dx, dy); + } + else if (GB.Is(device, CLASS_Printer)) + { + GtkPrintContext *context = ((CPRINTER *)device)->context; + + if (!context) + { + GB.Error("Printer is not printing"); + return TRUE; + } + + EXTRA(d)->print_context = context; + EXTRA(d)->context = gtk_print_context_get_cairo_context(context); + + //_gtk_print_context_rotate_according_to_orientation(context, CONTEXT(d)); + + /*fprintf(stderr, "Paint.Begin: orientation = %d w = %g h = %g\n", gtk_page_setup_get_orientation(gtk_print_context_get_page_setup(context)), + gtk_page_setup_get_paper_width(gtk_print_context_get_page_setup(context), GTK_UNIT_MM), + gtk_page_setup_get_paper_height(gtk_print_context_get_page_setup(context), GTK_UNIT_MM) + );*/ + + cairo_reference(CONTEXT(d)); + + cairo_surface_set_fallback_resolution(cairo_get_target(CONTEXT(d)), 1200, 1200); + + w = gtk_print_context_get_width(context); + h = gtk_print_context_get_height(context); + //fprintf(stderr, "Paint.Begin: w = %g h = %g\n", w, h); + + rx = (int)gtk_print_context_get_dpi_x(context); + ry = (int)gtk_print_context_get_dpi_y(context); + + //cairo_get_matrix(CONTEXT(d), &EXTRA(d)->init); + /*cairo_identity_matrix(CONTEXT(d)); + cairo_get_matrix(CONTEXT(d), &t); + fprintf(stderr, "matrix: [%g %g %g]\n [%g %g %g]\n", t.xx, t.xy, t.x0, t.yx, t.yy, t.y0);*/ + } + else if (GB.Is(device, CLASS_SvgImage)) + { + CSVGIMAGE *svgimage = ((CSVGIMAGE *)device); + target = SVGIMAGE_begin(svgimage); + if (!target) + return TRUE; + + cairo_surface_reference(target); + w = svgimage->width; + h = svgimage->height; + rx = ry = 72; + } + else + return TRUE; + + return init_painting(d, target, w, h, rx, ry); +} + +static void End(GB_PAINT *d) +{ + void *device = d->device; + GB_PAINT_EXTRA *dx = EXTRA(d); + + if (dx->layout) + g_object_unref(dx->layout); + + if (dx->font_stack) + GB.FreeArray(POINTER(&dx->font_stack)); + + GB.Unref(POINTER(&dx->font)); + + if (GB.Is(device, CLASS_Picture)) + { + gPicture *picture = ((CPICTURE *)device)->picture; + picture->invalidate(); + } + else if (GB.Is(device, CLASS_DrawingArea)) + { + gDrawingArea *wid = (gDrawingArea *)((CWIDGET *)device)->widget; + if (wid && wid->cached()) + wid->setCache(); + } + else if (GB.Is(device, CLASS_SvgImage)) + { + CSVGIMAGE *svgimage = ((CSVGIMAGE *)device); + SVGIMAGE_end(svgimage); + } + + cairo_destroy(dx->context); +} + +static void Save(GB_PAINT *d) +{ + GB_PAINT_EXTRA *dx = EXTRA(d); + CFONT **pfont; + + cairo_save(dx->context); + + if (!dx->font_stack) + GB.NewArray(POINTER(&dx->font_stack), sizeof(void *), 0); + + pfont = (CFONT **)GB.Add(POINTER(&dx->font_stack)); + *pfont = dx->font; + if (*pfont) + GB.Ref(*pfont); +} + +static void Restore(GB_PAINT *d) +{ + GB_PAINT_EXTRA *dx = EXTRA(d); + + cairo_restore(dx->context); + + if (dx->font_stack && GB.Count(dx->font_stack) > 0) + { + CFONT *font = dx->font_stack[GB.Count(dx->font_stack) - 1]; + GB.Unref(POINTER(&dx->font)); + dx->font = font; + GB.Remove(POINTER(&dx->font_stack), GB.Count(dx->font_stack) - 1, 1); + } +} + +static void Antialias(GB_PAINT *d, int set, int *antialias) +{ + if (set) + cairo_set_antialias(CONTEXT(d), *antialias ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE); + else + *antialias = (cairo_get_antialias(CONTEXT(d)) == CAIRO_ANTIALIAS_NONE) ? 0 : 1; +} + +static void _Font(GB_PAINT *d, int set, GB_FONT *font); + +static void update_layout(GB_PAINT *d) +{ + CFONT *font; + GB_PAINT_EXTRA *dx = EXTRA(d); + + if (dx->layout) + { + _Font(d, FALSE, (GB_FONT *)&font); + gt_add_layout_from_font(dx->layout, font->font, d->resolutionY); + dx->ascent = font->font->ascentF(); + + pango_cairo_context_set_font_options(pango_layout_get_context(dx->layout), gdk_screen_get_font_options (gdk_screen_get_default())); + + /*cairo_font_options_t *options = cairo_font_options_create(); //cairo_font_options_copy(pango_cairo_context_get_font_options(pango_layout_get_context(dx->layout))); + cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY); + cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_MEDIUM); + cairo_font_options_set_hint_metrics(options, CAIRO_HINT_METRICS_ON); + cairo_font_options_set_subpixel_order(options, CAIRO_SUBPIXEL_ORDER_RGB); + pango_cairo_context_set_font_options(pango_layout_get_context(dx->layout), options); + cairo_font_options_destroy(options);*/ + + pango_layout_context_changed(dx->layout); + + } +} + +static void apply_font(gFont *font, void *object = 0) +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + update_layout(d); +} + + +// Font is used by X11! +static void _Font(GB_PAINT *d, int set, GB_FONT *font) +{ + if (set) + { + GB.Unref(POINTER(&EXTRA(d)->font)); + if (*font) + { + EXTRA(d)->font = CFONT_create(((CFONT *)(*font))->font->copy(), apply_font); + GB.Ref(EXTRA(d)->font); + } + else + EXTRA(d)->font = NULL; + + update_layout(d); + } + else + { + if (!EXTRA(d)->font) + { + if (GB.Is(d->device, CLASS_DrawingArea)) + { + gDrawingArea *wid = (gDrawingArea *)((CWIDGET *)d->device)->widget; + EXTRA(d)->font = CFONT_create(wid->font()->copy(), apply_font); + } + else + { + EXTRA(d)->font = CFONT_create(new gFont(), apply_font); + } + + GB.Ref(EXTRA(d)->font); + } + + *font = (GB_FONT)EXTRA(d)->font; + } +} + + +static void Background(GB_PAINT *d, int set, GB_COLOR *color) +{ + if (set) + { + int r, g, b, a; + int col = get_color(d, *color); + GB_COLOR_SPLIT(col, r, g, b, a); + cairo_set_source_rgba(CONTEXT(d), r / 255.0, g / 255.0, b / 255.0, a / 255.0); + } + else + { + double r, g, b, a; + if (cairo_pattern_get_rgba(cairo_get_source(CONTEXT(d)), &r, &g, &b, &a) != CAIRO_STATUS_SUCCESS) + *color = 0; + else + *color = GB_COLOR_MAKE((int)(r * 255.0), (int)(g * 255.0), (int)(b * 255.0), (int)(a * 255.0)); + } +} + + +static void Invert(GB_PAINT *d, int set, int *invert) +{ + #if CAIRO_MAJOR >= 2 || (CAIRO_MAJOR == 1 && CAIRO_MINOR >= 10) + if (set) + cairo_set_operator(CONTEXT(d), *invert ? CAIRO_OPERATOR_DIFFERENCE : CAIRO_OPERATOR_OVER); + else + *invert = cairo_get_operator(CONTEXT(d)) == CAIRO_OPERATOR_DIFFERENCE; + #else + if (set) + EXTRA(d)->invert = *invert; + else + *invert = EXTRA(d)->invert; + #endif +} + + +static void Clip(GB_PAINT *d, int preserve) +{ + if (preserve) + cairo_clip_preserve(CONTEXT(d)); + else + cairo_clip(CONTEXT(d)); +} + +static void ResetClip(GB_PAINT *d) +{ + cairo_reset_clip(CONTEXT(d)); +} + +static void ClipExtents(GB_PAINT *d, GB_EXTENTS *ext) +{ + double x1, y1, x2, y2; + cairo_clip_extents(CONTEXT(d), &x1, &y1, &x2, &y2); + + ext->x1 = (float)x1 - DX(d); + ext->y1 = (float)y1 - DY(d); + ext->x2 = (float)x2; + ext->y2 = (float)y2; +} + +static void Fill(GB_PAINT *d, int preserve) +{ + if (preserve) + cairo_fill_preserve(CONTEXT(d)); + else + cairo_fill(CONTEXT(d)); +} + +static void Stroke(GB_PAINT *d, int preserve) +{ + if (preserve) + cairo_stroke_preserve(CONTEXT(d)); + else + cairo_stroke(CONTEXT(d)); +} + +static void PathExtents(GB_PAINT *d, GB_EXTENTS *ext) +{ + double x1, y1, x2, y2; + cairo_path_extents(CONTEXT(d), &x1, &y1, &x2, &y2); + + ext->x1 = (float)x1 - DX(d); + ext->y1 = (float)y1 - DY(d); + ext->x2 = (float)x2; + ext->y2 = (float)y2; +} + +static int PathContains(GB_PAINT *d, float x, float y) +{ + return cairo_in_fill(CONTEXT(d), (double)x + DX(d), (double)y + DY(d)); +} + +static void PathOutline(GB_PAINT *d, GB_PAINT_OUTLINE_CB cb) +{ + cairo_path_t *path; + cairo_path_data_t *data; + int i; + + path = cairo_copy_path_flat(CONTEXT(d)); + + for (i = 0; i < path->num_data; i += path->data[i].header.length) + { + data = &path->data[i]; + switch (data->header.type) + { + case CAIRO_PATH_MOVE_TO: + (*cb)(GB_PAINT_PATH_MOVE, data[1].point.x, data[1].point.y); + break; + + case CAIRO_PATH_LINE_TO: + (*cb)(GB_PAINT_PATH_LINE, data[1].point.x, data[1].point.y); + break; + + case CAIRO_PATH_CURVE_TO: + fprintf(stderr, "gb.gtk: warning: CAIRO_PATH_CURVE_TO not supported\n"); + break; + + case CAIRO_PATH_CLOSE_PATH: + fprintf(stderr, "gb.gtk: warning: CAIRO_PATH_CLOSE_PATH not supported\n"); + break; + } + } + + cairo_path_destroy(path); +} + +static void Dash(GB_PAINT *d, int set, float **dashes, int *count) +{ + int i; + double lw; + + lw = cairo_get_line_width(CONTEXT(d)); + if (lw == 0) lw = 1; + + if (set) + { + if (!*count) + cairo_set_dash(CONTEXT(d), NULL, 0, 0.0); + else + { + double dd[*count]; + + for (i = 0; i < *count; i++) + dd[i] = (*dashes)[i] * lw; + + cairo_set_dash(CONTEXT(d), dd, *count, 0.0); + } + } + else + { + *count = cairo_get_dash_count(CONTEXT(d)); + + if (*count) + { + double dd[*count]; + cairo_get_dash(CONTEXT(d), dd, NULL); + + GB.Alloc(POINTER(dashes), sizeof(float) * *count); + for (int i = 0; i < *count; i++) + (*dashes)[i] = (float)dd[i] / lw; + } + else + { + *dashes = NULL; + } + } +} + +static void DashOffset(GB_PAINT *d, int set, float *offset) +{ + double lw; + + lw = cairo_get_line_width(CONTEXT(d)); + if (lw == 0) lw = 1; + + if (set) + { + int count = cairo_get_dash_count(CONTEXT(d)); + double dashes[count]; + cairo_get_dash(CONTEXT(d), dashes, NULL); + cairo_set_dash(CONTEXT(d), dashes, count, (double)*offset * lw); + } + else + { + double v; + cairo_get_dash(CONTEXT(d), NULL, &v); + *offset = (float)v / lw; + } +} + + +static void FillRule(GB_PAINT *d, int set, int *value) +{ + if (set) + { + cairo_fill_rule_t v; + + switch (*value) + { + case GB_PAINT_FILL_RULE_EVEN_ODD: v = CAIRO_FILL_RULE_EVEN_ODD; break; + case GB_PAINT_FILL_RULE_WINDING: default: v = CAIRO_FILL_RULE_WINDING; + } + + cairo_set_fill_rule(CONTEXT(d), v); + } + else + { + switch (cairo_get_fill_rule(CONTEXT(d))) + { + case CAIRO_FILL_RULE_EVEN_ODD: *value = GB_PAINT_FILL_RULE_EVEN_ODD; break; + case CAIRO_FILL_RULE_WINDING: default: *value = GB_PAINT_FILL_RULE_WINDING; + } + } +} + + +static void FillStyle(GB_PAINT *d, int set, int *style) +{ + /*if (set) + { + EXTRA(d)->fillRule = *value; + } + else + *value = EXTRA(d)->fillRule;*/ +} + + +static void LineCap(GB_PAINT *d, int set, int *value) +{ + if (set) + { + cairo_line_cap_t v; + + switch (*value) + { + case GB_PAINT_LINE_CAP_ROUND: v = CAIRO_LINE_CAP_ROUND; break; + case GB_PAINT_LINE_CAP_SQUARE: v = CAIRO_LINE_CAP_SQUARE; break; + case GB_PAINT_LINE_CAP_BUTT: default: v = CAIRO_LINE_CAP_BUTT; + } + + cairo_set_line_cap(CONTEXT(d), v); + } + else + { + switch (cairo_get_line_cap(CONTEXT(d))) + { + case CAIRO_LINE_CAP_ROUND: *value = GB_PAINT_LINE_CAP_ROUND; break; + case CAIRO_LINE_CAP_SQUARE: *value = GB_PAINT_LINE_CAP_SQUARE; break; + case CAIRO_LINE_CAP_BUTT: default: *value = GB_PAINT_LINE_CAP_BUTT; + } + } +} + +static void LineJoin(GB_PAINT *d, int set, int *value) +{ + if (set) + { + cairo_line_join_t v; + + switch (*value) + { + case GB_PAINT_LINE_JOIN_ROUND: v = CAIRO_LINE_JOIN_ROUND; break; + case GB_PAINT_LINE_JOIN_BEVEL: v = CAIRO_LINE_JOIN_BEVEL; break; + case GB_PAINT_LINE_JOIN_MITER: default: v = CAIRO_LINE_JOIN_MITER; + } + + cairo_set_line_join(CONTEXT(d), v); + } + else + { + switch (cairo_get_line_join(CONTEXT(d))) + { + case CAIRO_LINE_JOIN_ROUND: *value = GB_PAINT_LINE_JOIN_ROUND; break; + case CAIRO_LINE_JOIN_BEVEL: *value = GB_PAINT_LINE_JOIN_BEVEL; break; + case CAIRO_LINE_JOIN_MITER: default: *value = GB_PAINT_LINE_JOIN_MITER; + } + } +} + +static void LineWidth(GB_PAINT *d, int set, float *value) +{ + if (set) + { + float *dashes; + int count; + float offset; + + Dash(d, FALSE, &dashes, &count); + DashOffset(d, FALSE, &offset); + + cairo_set_line_width(CONTEXT(d), (double)*value); + + Dash(d, TRUE, &dashes, &count); + DashOffset(d, TRUE, &offset); + GB.Free(POINTER(&dashes)); + } + else + *value = (float)cairo_get_line_width(CONTEXT(d)); +} + +static void MiterLimit(GB_PAINT *d, int set, float *value) +{ + if (set) + cairo_set_miter_limit(CONTEXT(d), (double)*value); + else + *value = (float)cairo_get_miter_limit(CONTEXT(d)); +} + + +static void Operator(GB_PAINT *d, int set, int *value) +{ + if (set) + { + cairo_operator_t v; + + switch (*value) + { + case GB_PAINT_OPERATOR_CLEAR: v = CAIRO_OPERATOR_CLEAR; break; + case GB_PAINT_OPERATOR_SOURCE: v = CAIRO_OPERATOR_SOURCE; break; + case GB_PAINT_OPERATOR_IN: v = CAIRO_OPERATOR_IN; break; + case GB_PAINT_OPERATOR_OUT: v = CAIRO_OPERATOR_OUT; break; + case GB_PAINT_OPERATOR_ATOP: v = CAIRO_OPERATOR_ATOP; break; + case GB_PAINT_OPERATOR_DEST: v = CAIRO_OPERATOR_DEST; break; + case GB_PAINT_OPERATOR_DEST_OVER: v = CAIRO_OPERATOR_DEST_OVER; break; + case GB_PAINT_OPERATOR_DEST_IN: v = CAIRO_OPERATOR_DEST_IN; break; + case GB_PAINT_OPERATOR_DEST_OUT: v = CAIRO_OPERATOR_DEST_OUT; break; + case GB_PAINT_OPERATOR_DEST_ATOP: v = CAIRO_OPERATOR_DEST_ATOP; break; + case GB_PAINT_OPERATOR_XOR: v = CAIRO_OPERATOR_XOR; break; + case GB_PAINT_OPERATOR_ADD: v = CAIRO_OPERATOR_ADD; break; + case GB_PAINT_OPERATOR_SATURATE: v = CAIRO_OPERATOR_SATURATE; break; + case GB_PAINT_OPERATOR_OVER: default: v = CAIRO_OPERATOR_OVER; break; + } + + cairo_set_operator(CONTEXT(d), v); + } + else + { + switch (cairo_get_operator(CONTEXT(d))) + { + case CAIRO_OPERATOR_CLEAR: *value = GB_PAINT_OPERATOR_CLEAR; break; + case CAIRO_OPERATOR_SOURCE: *value = GB_PAINT_OPERATOR_SOURCE; break; + case CAIRO_OPERATOR_IN: *value = GB_PAINT_OPERATOR_IN; break; + case CAIRO_OPERATOR_OUT: *value = GB_PAINT_OPERATOR_OUT; break; + case CAIRO_OPERATOR_ATOP: *value = GB_PAINT_OPERATOR_ATOP; break; + case CAIRO_OPERATOR_DEST: *value = GB_PAINT_OPERATOR_DEST; break; + case CAIRO_OPERATOR_DEST_OVER: *value = GB_PAINT_OPERATOR_DEST_OVER; break; + case CAIRO_OPERATOR_DEST_IN: *value = GB_PAINT_OPERATOR_DEST_IN; break; + case CAIRO_OPERATOR_DEST_OUT: *value = GB_PAINT_OPERATOR_DEST_OUT; break; + case CAIRO_OPERATOR_DEST_ATOP: *value = GB_PAINT_OPERATOR_DEST_ATOP; break; + case CAIRO_OPERATOR_XOR: *value = GB_PAINT_OPERATOR_XOR; break; + case CAIRO_OPERATOR_ADD: *value = GB_PAINT_OPERATOR_ADD; break; + case CAIRO_OPERATOR_SATURATE: *value = GB_PAINT_OPERATOR_SATURATE; break; + case CAIRO_OPERATOR_OVER: default: *value = GB_PAINT_OPERATOR_OVER; + } + } +} + + +static void NewPath(GB_PAINT *d) +{ + cairo_new_path(CONTEXT(d)); +} + +static void ClosePath(GB_PAINT *d) +{ + cairo_close_path(CONTEXT(d)); +} + + +static void Arc(GB_PAINT *d, float xc, float yc, float radius, float angle, float length, bool pie) +{ + xc += DX(d); + yc += DY(d); + + cairo_new_sub_path(CONTEXT(d)); + + if (pie) + cairo_move_to(CONTEXT(d), 0, 0); + + if (length < 0.0) + cairo_arc_negative(CONTEXT(d), xc, yc, radius, angle, angle + length); + else + cairo_arc(CONTEXT(d), xc, yc, radius, angle, angle + length); + + if (pie) + cairo_close_path(CONTEXT(d)); +} + +static void Ellipse(GB_PAINT *d, float x, float y, float width, float height, float angle, float length, bool pie) +{ + x += DX(d); + y += DY(d); + + cairo_new_sub_path(CONTEXT(d)); + + cairo_save(CONTEXT(d)); + + cairo_translate(CONTEXT(d), x + width / 2, y + height / 2); + cairo_scale(CONTEXT(d), width / 2, height / 2); + + if (pie) + cairo_move_to(CONTEXT(d), 0, 0); + + if (length < 0.0) + cairo_arc_negative(CONTEXT(d), 0, 0, 1, angle, angle + length); + else + cairo_arc(CONTEXT(d), 0, 0, 1, angle, angle + length); + + if (pie) + cairo_close_path(CONTEXT(d)); + + cairo_restore(CONTEXT(d)); +} + +static void Rectangle(GB_PAINT *d, float x, float y, float width, float height) +{ + x += DX(d); + y += DY(d); + cairo_rectangle(CONTEXT(d), x, y, width, height); +} + +static void ClipRect(GB_PAINT *d, int x, int y, int w, int h) +{ + ResetClip(d); + Rectangle(d, x, y, w, h); + Clip(d, FALSE); +} + +static void GetCurrentPoint(GB_PAINT *d, float *x, float *y) +{ + double cx, cy; + + cairo_get_current_point(CONTEXT(d), &cx, &cy); + *x = (float)cx - DX(d); + *y = (float)cy - DY(d); +} + +static void MoveTo(GB_PAINT *d, float x, float y) +{ + cairo_move_to(CONTEXT(d), x + DX(d), y + DY(d)); +} + +static void LineTo(GB_PAINT *d, float x, float y) +{ + cairo_line_to(CONTEXT(d), x + DX(d), y + DY(d)); +} + +static void CurveTo(GB_PAINT *d, float x1, float y1, float x2, float y2, float x3, float y3) +{ + cairo_curve_to(CONTEXT(d), x1 + DX(d), y1 + DY(d), x2 + DX(d), y2 + DY(d), x3 + DX(d), y3 + DY(d)); +} + +static PangoLayout *create_pango_layout(GB_PAINT *d) +{ + GB_PAINT_EXTRA *dx = EXTRA(d); + + /*if (dx->print_context) + return gtk_print_context_create_pango_layout(dx->print_context); + else*/ + + if (!dx->layout) + { + dx->layout = pango_cairo_create_layout(dx->context); + + update_layout(d); + } + + return dx->layout; +} + +static void draw_text(GB_PAINT *d, bool rich, const char *text, int len, float w, float h, int align, bool draw) +{ + char *html = NULL; + PangoLayout *layout; + float tw, th, offx, offy; + + layout = create_pango_layout(d); + + if (rich) + { + html = gt_html_to_pango_string(text, len, false); + pango_layout_set_text(layout, "", 0); + pango_layout_set_markup(layout, html, -1); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); + if (w > 0) + pango_layout_set_width(layout, (int)(w * PANGO_SCALE)); + } + else + { + pango_layout_set_markup(layout, "", 0); + pango_layout_set_text(layout, text, len); + pango_layout_set_width(layout, -1); + } + + if (align == GB_DRAW_ALIGN_DEFAULT) + align = ALIGN_TOP_NORMAL; + + if (w > 0 || h > 0) + { + gt_layout_alignment(layout, w, h, &tw, &th, align, &offx, &offy); + if (rich) + offx = 0; + } + else + { + offx = 0; + offy = -(EXTRA(d)->ascent); + } + + cairo_rel_move_to(CONTEXT(d), offx, offy); + if (draw) + pango_cairo_show_layout(CONTEXT(d), layout); + else + pango_cairo_layout_path(CONTEXT(d), layout); + + if (html) g_free(html); +} + +static void Text(GB_PAINT *d, const char *text, int len, float w, float h, int align, bool draw) +{ + draw_text(d, FALSE, text, len, w, h, align, draw); +} + +static void RichText(GB_PAINT *d, const char *text, int len, float w, float h, int align, bool draw) +{ + draw_text(d, TRUE, text, len, w, h, align, draw); +} + + +static void get_text_extents(GB_PAINT *d, bool rich, const char *text, int len, GB_EXTENTS *ext, float width) +{ + char *html = NULL; + PangoLayout *layout; + CFONT *font; + PangoRectangle rect; + float x, y; + + layout = create_pango_layout(d); + + if (rich) + { + html = gt_html_to_pango_string(text, len, false); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); + pango_layout_set_markup(layout, html, -1); + } + else + pango_layout_set_text(layout, text, len); + + _Font(d, FALSE, (GB_FONT *)&font); + gt_add_layout_from_font(layout, font->font, d->resolutionY); + + if (width > 0) + pango_layout_set_width(layout, width * PANGO_SCALE); + + pango_layout_get_extents(layout, &rect, NULL); + + GetCurrentPoint(d, &x, &y); + + ext->x1 = (float)rect.x / PANGO_SCALE + x; + ext->y1 = (float)rect.y / PANGO_SCALE + y - EXTRA(d)->ascent; + ext->x2 = ext->x1 + (float)rect.width / PANGO_SCALE; + ext->y2 = ext->y1 + (float)rect.height / PANGO_SCALE; + + if (html) g_free(html); +} + +static void TextExtents(GB_PAINT *d, const char *text, int len, GB_EXTENTS *ext) +{ + get_text_extents(d, FALSE, text, len, ext, -1); +} + +static void RichTextExtents(GB_PAINT *d, const char *text, int len, GB_EXTENTS *ext, float width) +{ + get_text_extents(d, TRUE, text, len, ext, width); +} + +static void TextSize(GB_PAINT *d, const char *text, int len, float *w, float *h) +{ + float scale; + CFONT *font; + _Font(d, FALSE, (GB_FONT *)&font); + + scale = (float)d->resolutionY / gDesktop::resolution(); + + font->font->textSize(text, len, w, h); + + *w *= scale; + *h *= scale; +} + +static void RichTextSize(GB_PAINT *d, const char *text, int len, float sw, float *w, float *h) +{ + float scale; + CFONT *font; + _Font(d, FALSE, (GB_FONT *)&font); + + scale = (float)d->resolutionY / gDesktop::resolution(); + + if (sw > 0) + sw /= scale; + + font->font->richTextSize(text, len, sw, w, h); + *w *= scale; + *h *= scale; +} + + +static void Matrix(GB_PAINT *d, int set, GB_TRANSFORM matrix) +{ + cairo_matrix_t *t = (cairo_matrix_t *)matrix; + + if (set) + { + if (t) + cairo_set_matrix(CONTEXT(d), t); + else + { + //if (EXTRA(d)->print_context) + cairo_set_matrix(CONTEXT(d), &EXTRA(d)->init); + /*else + { + cairo_ + cairo_identity_matrix(CONTEXT(d)); + cairo_translate(CONTEXT(d), EXTRA(d)->dx, EXTRA(d)->dy); + }*/ + } + } + else + cairo_get_matrix(CONTEXT(d), t); +} + + +static void SetBrush(GB_PAINT *d, GB_BRUSH brush) +{ + cairo_set_source(CONTEXT(d), (cairo_pattern_t *)brush); +} + +static void BrushOrigin(GB_PAINT *d, int set, float *x, float *y) +{ + if (set) + { + cairo_pattern_t *brush; + cairo_matrix_t matrix; + + brush = cairo_get_source(CONTEXT(d)); + cairo_pattern_get_matrix(brush, &matrix); + cairo_matrix_translate(&matrix, EXTRA(d)->bx, EXTRA(d)->by); + cairo_matrix_translate(&matrix, (- *x), (- *y)); + cairo_pattern_set_matrix(brush, &matrix); + + EXTRA(d)->bx = *x; + EXTRA(d)->by = *y; + } + else + { + *x = EXTRA(d)->bx; + *y = EXTRA(d)->by; + } +} + +static void BrushFree(GB_BRUSH brush) +{ + // Should I release the surface associated with an image brush? Apparently no. + cairo_pattern_destroy((cairo_pattern_t *)brush); +} + +static void BrushColor(GB_BRUSH *brush, GB_COLOR color) +{ + int r, g, b, a; + GB_COLOR_SPLIT(color, r, g, b, a); + *brush = (GB_BRUSH)cairo_pattern_create_rgba(r / 255.0, g / 255.0, b / 255.0, a / 255.0); +} + +static void BrushImage(GB_BRUSH *brush, GB_IMAGE image) +{ + gPicture *picture = CIMAGE_get((CIMAGE *)image); + cairo_surface_t *surface; + cairo_pattern_t *pattern; + + surface = gt_cairo_create_surface_from_pixbuf(picture->getPixbuf()); + + pattern = cairo_pattern_create_for_surface(surface); + cairo_surface_destroy(surface); + + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + + *brush = (GB_BRUSH)pattern; +} + +static void handle_color_stop(cairo_pattern_t *pattern, int nstop, const double *positions, const GB_COLOR *colors) +{ + int i, r, g, b, a; + + for (i = 0; i < nstop; i++) + { + GB_COLOR_SPLIT(colors[i], r, g, b, a); + cairo_pattern_add_color_stop_rgba(pattern, positions[i], r / 255.0, g / 255.0, b / 255.0, a / 255.0); + } +} + +static void set_pattern_extend(cairo_pattern_t *pattern, int extend) +{ + cairo_extend_t cext; + + switch (extend) + { + case GB_PAINT_EXTEND_REPEAT: cext = CAIRO_EXTEND_REPEAT; break; + case GB_PAINT_EXTEND_REFLECT: cext = CAIRO_EXTEND_REFLECT; break; + case GB_PAINT_EXTEND_PAD: default: cext = CAIRO_EXTEND_PAD; + } + + cairo_pattern_set_extend(pattern, cext); +} + +static void BrushLinearGradient(GB_BRUSH *brush, float x0, float y0, float x1, float y1, int nstop, double *positions, GB_COLOR *colors, int extend) +{ + cairo_pattern_t *pattern; + + pattern = cairo_pattern_create_linear(x0, y0, x1, y1); + + handle_color_stop(pattern, nstop, positions, colors); + set_pattern_extend(pattern, extend); + + *brush = (GB_BRUSH)pattern; +} + +static void BrushRadialGradient(GB_BRUSH *brush, float cx, float cy, float r, float fx, float fy, int nstop, double *positions, GB_COLOR *colors, int extend) +{ + cairo_pattern_t *pattern; + + // I know that from librsvg sources + pattern = cairo_pattern_create_radial(fx, fy, 0.0, cx, cy, r); + + handle_color_stop(pattern, nstop, positions, colors); + set_pattern_extend(pattern, extend); + + *brush = (GB_BRUSH)pattern; +} + +// Matrix must be inverted, so that it behaves the same way as in Qt4 + +static void BrushMatrix(GB_BRUSH brush, int set, GB_TRANSFORM matrix) +{ + cairo_matrix_t *t = (cairo_matrix_t *)matrix; + cairo_pattern_t *pattern = (cairo_pattern_t *)brush; + cairo_matrix_t actual; + + if (set) + { + if (t) + { + actual = *t; + cairo_matrix_invert(&actual); + } + else + cairo_matrix_init_identity(&actual); + + cairo_pattern_set_matrix(pattern, &actual); + } + else + { + cairo_pattern_get_matrix(pattern, t); + cairo_matrix_invert(t); + } +} + +static void TransformCreate(GB_TRANSFORM *matrix) +{ + GB.Alloc(POINTER(matrix), sizeof(cairo_matrix_t)); + cairo_matrix_init_identity((cairo_matrix_t *)*matrix); +} + +static void TransformCopy(GB_TRANSFORM *matrix, GB_TRANSFORM copy) +{ + GB.Alloc(POINTER(matrix), sizeof(cairo_matrix_t)); + *(cairo_matrix_t *)*matrix = *(cairo_matrix_t *)copy; +} + +static void TransformDelete(GB_TRANSFORM *matrix) +{ + GB.Free(POINTER(matrix)); +} + +static void TransformInit(GB_TRANSFORM matrix, float xx, float yx, float xy, float yy, float x0, float y0) +{ + cairo_matrix_init((cairo_matrix_t *)matrix, xx, yx, xy, yy, x0, y0); +} + +static void TransformTranslate(GB_TRANSFORM matrix, float tx, float ty) +{ + cairo_matrix_translate((cairo_matrix_t *)matrix, tx, ty); +} + +static void TransformScale(GB_TRANSFORM matrix, float sx, float sy) +{ + cairo_matrix_scale((cairo_matrix_t *)matrix, sx, sy); +} + +static void TransformRotate(GB_TRANSFORM matrix, float angle) +{ + cairo_matrix_rotate((cairo_matrix_t *)matrix, -angle); +} + +static int TransformInvert(GB_TRANSFORM matrix) +{ + cairo_status_t status = cairo_matrix_invert((cairo_matrix_t *)matrix); + return status != CAIRO_STATUS_SUCCESS; +} + +static void TransformMultiply(GB_TRANSFORM matrix, GB_TRANSFORM matrix2) +{ + cairo_matrix_multiply((cairo_matrix_t *)matrix, (cairo_matrix_t *)matrix, (cairo_matrix_t *)matrix2); +} + +static void TransformMap(GB_TRANSFORM matrix, double *x, double *y) +{ + cairo_matrix_transform_point((cairo_matrix_t *)matrix, x, y); +} + +static void my_cairo_fill(cairo_t *cr) +{ + if (cairo_get_operator(cr) == CAIRO_OPERATOR_OVER) + cairo_fill(cr); + else + { + cairo_save(cr); + cairo_clip(cr); + cairo_paint(cr); + cairo_restore(cr); + } +} + +static void DrawImage(GB_PAINT *d, GB_IMAGE image, float x, float y, float w, float h, float opacity, GB_RECT *source) +{ + cairo_t *cr = CONTEXT(d); + cairo_surface_t *surface; + cairo_pattern_t *pattern = NULL; + cairo_pattern_t *save; + cairo_matrix_t matrix; + + save = cairo_get_source(cr); + cairo_pattern_reference(save); + cairo_save(cr); + + surface = check_image(image); //picture->getSurface(); + + pattern = cairo_pattern_create_for_surface(surface); + + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + + if (source && w >= source->w && h >= source->h && w == (int)w && h == (int)h && ((int)w % source->w) == 0 && ((int)h % source->h) == 0) + cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST); + + cairo_matrix_init_identity(&matrix); + cairo_matrix_translate(&matrix, x, y); + if (source) + { + cairo_matrix_scale(&matrix, w / source->w, h / source->h); + cairo_matrix_translate(&matrix, -source->x, -source->y); + } + else if (w > 0 && h > 0) + cairo_matrix_scale(&matrix, w / cairo_image_surface_get_width(surface), h / cairo_image_surface_get_height(surface)); + + cairo_matrix_invert(&matrix); + cairo_pattern_set_matrix(pattern, &matrix); + cairo_set_source(cr, pattern); + + cairo_rectangle(cr, x, y, w, h); + + if (opacity == 1.0) + { + my_cairo_fill(cr); + } + else + { + cairo_clip(cr); + cairo_paint_with_alpha(cr, opacity); + } + + cairo_restore(cr); + cairo_set_source(cr, save); + cairo_pattern_destroy(save); + + cairo_pattern_destroy(pattern); +} + +#ifdef GTK3 +static void DrawPicture(GB_PAINT *d, GB_PICTURE picture, float x, float y, float w, float h, GB_RECT *source) +{ + cairo_t *cr = CONTEXT(d); + gPicture *pic = ((CPICTURE *)picture)->picture; + cairo_surface_t *surface; + cairo_pattern_t *pattern = NULL; + cairo_pattern_t *save; + cairo_matrix_t matrix; + + cairo_save(cr); + + save = cairo_get_source(cr); + cairo_pattern_reference(save); + + surface = pic->getSurface(); + + pattern = cairo_pattern_create_for_surface(surface); + + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + + if (source && w >= source->w && h >= source->h && w == (int)w && h == (int)h && ((int)w % source->w) == 0 && ((int)h % source->h) == 0) + cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST); + + cairo_matrix_init_identity(&matrix); + cairo_matrix_translate(&matrix, x, y); + if (source) + { + cairo_matrix_scale(&matrix, w / source->w, h / source->h); + cairo_matrix_translate(&matrix, -source->x, -source->y); + } + else if (w > 0 && h > 0) + cairo_matrix_scale(&matrix, w / cairo_image_surface_get_width(surface), h / cairo_image_surface_get_height(surface)); + + cairo_matrix_invert(&matrix); + cairo_pattern_set_matrix(pattern, &matrix); + cairo_set_source(cr, pattern); + + cairo_rectangle(cr, x, y, w, h); + my_cairo_fill(cr); + + cairo_set_source(cr, save); + cairo_pattern_destroy(save); + + cairo_restore(cr); + + cairo_pattern_destroy(pattern); +} +#else +static void DrawPicture(GB_PAINT *d, GB_PICTURE picture, float x, float y, float w, float h, GB_RECT *source) +{ + cairo_pattern_t *pattern, *save; + cairo_matrix_t matrix; + gPicture *pic = ((CPICTURE *)picture)->picture; + + if (pic->type() != gPicture::PIXMAP || source) + { + gt_cairo_draw_pixbuf(CONTEXT(d), pic->getPixbuf(), x, y, w, h, 1.0, source); + return; + } + + x += DX(d); + y += DY(d); + + cairo_save(CONTEXT(d)); + save = cairo_get_source(CONTEXT(d)); + cairo_pattern_reference(save); + + gdk_cairo_set_source_pixmap(CONTEXT(d), pic->getPixmap(), 0, 0); + + pattern = cairo_get_source(CONTEXT(d)); + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + + /*if (source && w >= source->w && h >= source->h && w == (int)w && h == (int)h && ((int)w % source->w) == 0 && ((int)h % source->h) == 0) + cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);*/ + + //gdk_cairo_set_source_pixbuf(CONTEXT(d), picture->getPixbuf(), x, y); + + cairo_matrix_init_identity(&matrix); + cairo_matrix_translate(&matrix, x, y); + cairo_matrix_scale(&matrix, w / pic->width(), h / pic->height()); + cairo_matrix_invert(&matrix); + cairo_pattern_set_matrix(pattern, &matrix); + + cairo_rectangle(CONTEXT(d), x, y, w, h); + my_cairo_fill(CONTEXT(d)); + + cairo_set_source(CONTEXT(d), save); + cairo_pattern_destroy(save); + + cairo_restore(CONTEXT(d)); +} +#endif + +static void GetPictureInfo(GB_PAINT *d, GB_PICTURE picture, GB_PICTURE_INFO *info) +{ + gPicture *pic = ((CPICTURE *)picture)->picture; + + info->width = pic->width(); + info->height = pic->height(); +} + +static void FillRect(GB_PAINT *d, float x, float y, float w, float h, GB_COLOR color) +{ + cairo_pattern_t *save; + + x += DX(d); + y += DY(d); + + save = cairo_get_source(CONTEXT(d)); + cairo_pattern_reference(save); + + Background(d, TRUE, &color); + cairo_rectangle(CONTEXT(d), x, y, w, h); + my_cairo_fill(CONTEXT(d)); + + cairo_set_source(CONTEXT(d), save); + cairo_pattern_destroy(save); +} + + +GB_PAINT_DESC PAINT_Interface = +{ + sizeof(GB_PAINT_EXTRA), + Begin, + End, + Save, + Restore, + Antialias, + _Font, + Background, + Invert, + Clip, + ResetClip, + ClipExtents, + ClipRect, + Fill, + Stroke, + PathExtents, + PathContains, + PathOutline, + Dash, + DashOffset, + FillRule, + FillStyle, + LineCap, + LineJoin, + LineWidth, + MiterLimit, + Operator, + NewPath, + ClosePath, + Arc, + Ellipse, + Rectangle, + GetCurrentPoint, + MoveTo, + LineTo, + CurveTo, + Text, + TextExtents, + TextSize, + RichText, + RichTextExtents, + RichTextSize, + Matrix, + SetBrush, + BrushOrigin, + DrawImage, + DrawPicture, + GetPictureInfo, + FillRect, + { + BrushFree, + BrushColor, + BrushImage, + BrushLinearGradient, + BrushRadialGradient, + BrushMatrix, + } +}; + +GB_PAINT_MATRIX_DESC PAINT_MATRIX_Interface = +{ + TransformCreate, + TransformCopy, + TransformDelete, + TransformInit, + TransformTranslate, + TransformScale, + TransformRotate, + TransformInvert, + TransformMultiply, + TransformMap +}; + +void PAINT_begin(void *device) +{ + DRAW.Paint.Begin(device); +} + +void PAINT_end() +{ + DRAW.Paint.End(); +} + +void PAINT_clip(int x, int y, int w, int h) +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + if (d) + { + cairo_rectangle(CONTEXT(d), (double)x, (double)y, (double)w, (double)h); + cairo_clip(CONTEXT(d)); + } +} + +#ifdef GTK3 +#else +void PAINT_clip_region(GdkRegion *region) +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + if (d) + { + gdk_cairo_region(CONTEXT(d), region); + cairo_clip(CONTEXT(d)); + } +} +#endif + +cairo_t *PAINT_get_current_context() +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + if (d) + return CONTEXT(d); + + GB.Error("No current device"); + return NULL; +} + +void *PAINT_get_current_device() +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + if (d) + return d->device; + + GB.Error("No current device"); + return NULL; +} + +bool PAINT_get_clip(int *x, int *y, int *w, int *h) +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + GB_EXTENTS ext; + + ClipExtents(d, &ext); + + *x = ceilf(ext.x1); + *y = ceilf(ext.y1); + *w = floorf(ext.x2) - *x; + *h = floorf(ext.y2) - *y; + + return *w <= 0 || *h <= 0; +} + +void PAINT_apply_offset(int *x, int *y) +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + *x += EXTRA(d)->dx; + *y += EXTRA(d)->dy; +} diff --git a/gb.gtk/src/cpaint_impl.h b/gb.gtk/src/cpaint_impl.h new file mode 100644 index 00000000..7340fae2 --- /dev/null +++ b/gb.gtk/src/cpaint_impl.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + cpaint_impl.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPAINT_IMPL_H +#define __CPAINT_IMPL_H + +#include "gambas.h" +#include "gb.paint.h" +#include + +#ifndef __CPAINT_IMPL_C + +extern GB_PAINT_DESC PAINT_Interface; +extern GB_PAINT_MATRIX_DESC PAINT_MATRIX_Interface; + +#endif + +void PAINT_begin(void *device); +void PAINT_end(); +void PAINT_clip(int x, int y, int w, int h); +#ifndef GTK3 +void PAINT_clip_region(GdkRegion *region); +#endif +cairo_t *PAINT_get_current_context(); +void *PAINT_get_current_device(); +bool PAINT_get_clip(int *x, int *y, int *w, int *h); +void PAINT_apply_offset(int *x, int *y); + +#endif diff --git a/gb.gtk/src/cprinter.cpp b/gb.gtk/src/cprinter.cpp new file mode 100644 index 00000000..e34d986e --- /dev/null +++ b/gb.gtk/src/cprinter.cpp @@ -0,0 +1,378 @@ +/*************************************************************************** + + cprinter.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPRINTER_CPP + +#include "main.h" +#include "gambas.h" +#include "widgets.h" +#include "cpaint_impl.h" +#include "gb.form.print.h" +#include "cprinter.h" + +DECLARE_EVENT(EVENT_Begin); +DECLARE_EVENT(EVENT_End); +DECLARE_EVENT(EVENT_Paginate); +DECLARE_EVENT(EVENT_Draw); + +static void cb_begin(gPrinter *printer, GtkPrintContext *context) +{ + void *_object = printer->tag; + THIS->current = 0; + THIS->context = context; + PAINT_begin(THIS); + GB.Raise(THIS, EVENT_Begin, 0); + PAINT_end(); +} + +static void cb_end(gPrinter *printer) +{ + void *_object = printer->tag; + THIS->current = 0; + GB.Raise(THIS, EVENT_End, 0); +} + +static void cb_paginate(gPrinter *printer) +{ + void *_object = printer->tag; + + if (GB.CanRaise(THIS, EVENT_Paginate)) + GB.Raise(THIS, EVENT_Paginate, 0); + else + printer->setPageCount(printer->pageCount()); +} + +static void cb_draw(gPrinter *printer, GtkPrintContext *context, int page) +{ + void *_object = printer->tag; + THIS->current = page + 1; + THIS->context = context; + PAINT_begin(THIS); + GB.Raise(THIS, EVENT_Draw, 0); + PAINT_end(); +} + +BEGIN_METHOD_VOID(Printer_new) + + THIS->printer = new gPrinter(); + PRINTER->tag = THIS; + PRINTER->onBegin = cb_begin; + PRINTER->onEnd = cb_end; + PRINTER->onDraw = cb_draw; + PRINTER->onPaginate = cb_paginate; + +END_METHOD + +BEGIN_METHOD_VOID(Printer_free) + + delete THIS->printer; + +END_METHOD + +BEGIN_METHOD_VOID(Printer_Configure) + + GB.ReturnBoolean(PRINTER->configure()); + +END_METHOD + +BEGIN_METHOD_VOID(Printer_Print) + + GB.ReturnBoolean(PRINTER->print()); + +END_METHOD + +BEGIN_METHOD_VOID(Printer_Cancel) + + PRINTER->cancel(); + +END_METHOD + +BEGIN_PROPERTY(Printer_Count) + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->pageCount()); + else + PRINTER->setPageCount(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Current) + + GB.ReturnInteger(THIS->current); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Name) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(PRINTER->name()); + else + PRINTER->setName(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Orientation) + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->orientation()); + else + PRINTER->setOrientation(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Paper) + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->paperModel()); + else + PRINTER->setPaperModel(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_PaperWidth) + + double w, h; + + PRINTER->getPaperSize(&w, &h); + + if (READ_PROPERTY) + GB.ReturnFloat(w); + else + PRINTER->setPaperSize(VPROP(GB_FLOAT), h); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_PaperHeight) + + double w, h; + + PRINTER->getPaperSize(&w, &h); + + if (READ_PROPERTY) + GB.ReturnFloat(h); + else + PRINTER->setPaperSize(w, VPROP(GB_FLOAT)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_CollateCopies) + + if (READ_PROPERTY) + GB.ReturnBoolean(PRINTER->collateCopies()); + else + PRINTER->setCollateCopies(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_ReverseOrder) + + if (READ_PROPERTY) + GB.ReturnBoolean(PRINTER->reverserOrder()); + else + PRINTER->setReverseOrder(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Duplex) + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->duplex()); + else + PRINTER->setDuplex(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_GrayScale) + + if (READ_PROPERTY) + GB.ReturnBoolean(!PRINTER->useColor()); + else + PRINTER->setUseColor(!VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_NumCopies) + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->numCopies()); + else + PRINTER->setNumCopies(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Resolution) + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->resolution()); + else + PRINTER->setResolution(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_FirstPage) + + int from, to; + + PRINTER->getPrintPages(&from, &to); + + if (READ_PROPERTY) + GB.ReturnInteger(from + 1); + else + PRINTER->setPrintPages(VPROP(GB_INTEGER) - 1, to); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_LastPage) + + int from, to; + + PRINTER->getPrintPages(&from, &to); + + if (READ_PROPERTY) + GB.ReturnInteger(to + 1); + else + PRINTER->setPrintPages(from, VPROP(GB_INTEGER) - 1); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_FullPage) + + if (READ_PROPERTY) + GB.ReturnBoolean(PRINTER->useFullPage()); + else + PRINTER->setUseFullPage(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_OutputFile) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(PRINTER->outputFileName()); + else + PRINTER->setOutputFileName(GB.FileName(PSTRING(), PLENGTH())); + +END_PROPERTY + +static bool find_default_printer(const char *name, bool def) +{ + if (def) + { + GB.ReturnNewZeroString(name); + return true; + } + else + return false; +} + +BEGIN_PROPERTY(Printer_Default) + + GB.ReturnNull(); + gPrinter::enumeratePrinters(find_default_printer); + +END_PROPERTY + +static GB_ARRAY _list = NULL; + +static bool add_printer(const char *name, bool def) +{ + *((char **)GB.Array.Add(_list)) = GB.NewZeroString(name); + return FALSE; +} + +BEGIN_PROPERTY(Printer_List) + + GB_ARRAY array; + + GB.Array.New(&array, GB_T_STRING, 0); + _list = array; + gPrinter::enumeratePrinters(add_printer); + _list = NULL; + GB.ReturnObject(array); + +END_PROPERTY + + + +GB_DESC PrinterDesc[] = +{ + GB_DECLARE("Printer", sizeof(CPRINTER)), + + GB_STATIC_PROPERTY_READ("Default", "s", Printer_Default), + GB_STATIC_PROPERTY_READ("List", "String[]", Printer_List), + + GB_CONSTANT("Portrait", "i", GB_PRINT_PORTRAIT), + GB_CONSTANT("Landscape", "i", GB_PRINT_LANDSCAPE), + //GB_CONSTANT("ReversePortrait", "i", GB_PRINT_REVERSE_PORTRAIT), + //GB_CONSTANT("ReverseLandscape", "i", GB_PRINT_REVERSE_LANDSCAPE), + + GB_CONSTANT("Custom", "i", GB_PRINT_CUSTOM), + GB_CONSTANT("A3", "i", GB_PRINT_A3), + GB_CONSTANT("A4", "i", GB_PRINT_A4), + GB_CONSTANT("A5", "i", GB_PRINT_A5), + GB_CONSTANT("B5", "i", GB_PRINT_B5), + GB_CONSTANT("Letter", "i", GB_PRINT_LETTER), + GB_CONSTANT("Executive", "i", GB_PRINT_EXECUTIVE), + GB_CONSTANT("Legal", "i", GB_PRINT_LEGAL), + + GB_CONSTANT("Simplex", "i", GB_PRINT_SIMPLEX), + GB_CONSTANT("Horizontal", "i", GB_PRINT_DUPLEX_HORIZONTAL), + GB_CONSTANT("Vertical", "i", GB_PRINT_DUPLEX_VERTICAL), + + GB_METHOD("_new", NULL, Printer_new, NULL), + GB_METHOD("_free", NULL, Printer_free, NULL), + + GB_METHOD("Configure", "b", Printer_Configure, NULL), + GB_METHOD("Print", "b", Printer_Print, NULL), + GB_METHOD("Cancel", NULL, Printer_Cancel, NULL), + + GB_PROPERTY("Count", "i", Printer_Count), + GB_PROPERTY_READ("Page", "i", Printer_Current), + + GB_PROPERTY("Name", "s", Printer_Name), + GB_PROPERTY("Orientation", "i", Printer_Orientation), + GB_PROPERTY("Paper", "i", Printer_Paper), + GB_PROPERTY("PaperWidth", "f", Printer_PaperWidth), + GB_PROPERTY("PaperHeight", "f", Printer_PaperHeight), + GB_PROPERTY("CollateCopies", "b", Printer_CollateCopies), + GB_PROPERTY("ReverseOrder", "b", Printer_ReverseOrder), + GB_PROPERTY("Duplex", "i", Printer_Duplex), + GB_PROPERTY("GrayScale", "b", Printer_GrayScale), + GB_PROPERTY("NumCopies", "i", Printer_NumCopies), + GB_PROPERTY("Resolution", "i", Printer_Resolution), + GB_PROPERTY("FirstPage", "i", Printer_FirstPage), + GB_PROPERTY("LastPage", "i", Printer_LastPage), + GB_PROPERTY("FullPage", "b", Printer_FullPage), + GB_PROPERTY("OutputFile", "s", Printer_OutputFile), + + GB_EVENT("Begin", NULL, NULL, &EVENT_Begin), + GB_EVENT("End", NULL, NULL, &EVENT_End), + GB_EVENT("Paginate", NULL, NULL, &EVENT_Paginate), + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + + GB_INTERFACE("Paint", &PAINT_Interface), + + PRINTER_DESCRIPTION, + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/cprinter.h b/gb.gtk/src/cprinter.h new file mode 100644 index 00000000..9f331954 --- /dev/null +++ b/gb.gtk/src/cprinter.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + cprinter.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPRINTER_H +#define __CPRINTER_H + +#include "gambas.h" +#include "widgets.h" +#include "gprinter.h" + +typedef + struct + { + GB_BASE ob; + gPrinter *printer; + int current; + GtkPrintContext *context; + } + CPRINTER; + +#ifndef __CPRINTER_CPP + +extern GB_DESC PrinterDesc[]; + +#else + +#define THIS ((CPRINTER *)_object) +#define PRINTER THIS->printer + +#endif + +#endif diff --git a/gb.gtk/src/csvgimage.cpp b/gb.gtk/src/csvgimage.cpp new file mode 100644 index 00000000..68ff79b8 --- /dev/null +++ b/gb.gtk/src/csvgimage.cpp @@ -0,0 +1,266 @@ +/*************************************************************************** + + csvgimage.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSVGIMAGE_CPP + +#include +#include + +#include "main.h" +#include "gambas.h" +#include "widgets.h" +#include "cpaint_impl.h" +#include "csvgimage.h" + +#define MM_TO_PT(_mm) ((_mm) * 72 / 25.4) +#define PT_TO_MM(_pt) ((_pt) / 72 * 25.4) + +static void release(CSVGIMAGE *_object) +{ + if (HANDLE) + { + g_object_unref(G_OBJECT(HANDLE)); + HANDLE = NULL; + } + + if (SURFACE) + { + cairo_surface_destroy(SURFACE); + THIS->surface = NULL; + unlink(THIS->file); + GB.FreeString(&THIS->file); + } + + THIS->width = THIS->height = 0; +} + +cairo_surface_t *SVGIMAGE_begin(CSVGIMAGE *_object) +{ + if (!SURFACE) + { + if (THIS->width <= 0 || THIS->height <= 0) + { + GB.Error("SvgImage size is not defined"); + return NULL; + } + + THIS->file = GB.NewZeroString(GB.TempFile(NULL)); + SURFACE = cairo_svg_surface_create(THIS->file, THIS->width, THIS->height); + + if (HANDLE) + { + cairo_t *context = cairo_create(SURFACE); + rsvg_handle_render_cairo(HANDLE, context); + cairo_destroy(context); + } + } + + return SURFACE; +} + +void SVGIMAGE_end(CSVGIMAGE *_object) +{ +} + +BEGIN_METHOD(SvgImage_new, GB_FLOAT width; GB_FLOAT height) + + THIS->width = VARGOPT(width, 0); + THIS->height = VARGOPT(height, 0); + +END_METHOD + +BEGIN_METHOD_VOID(SvgImage_free) + + release(THIS); + +END_METHOD + +BEGIN_PROPERTY(SvgImage_Width) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->width); + else + THIS->width = VPROP(GB_FLOAT); + +END_PROPERTY + +BEGIN_PROPERTY(SvgImage_Height) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->height); + else + THIS->height = VPROP(GB_FLOAT); + +END_PROPERTY + +BEGIN_METHOD(SvgImage_Resize, GB_FLOAT width; GB_FLOAT height) + + THIS->width = VARG(width); + THIS->height = VARG(height); + +END_METHOD + +static const char *load_file(CSVGIMAGE *_object, const char *path, int len_path) +{ + RsvgHandle *handle = NULL; + RsvgDimensionData dim; + char *addr; + int len; + const char *err = NULL; + + if (GB.LoadFile(path, len_path, &addr, &len)) + return "Unable to load SVG file"; + + handle = rsvg_handle_new_from_data((const guint8 *)addr, len / sizeof(guint8), NULL); + if (!handle) + { + err = "Unable to load SVG file: invalid format"; + goto __RETURN; + } + + rsvg_handle_set_dpi(handle, 72); + + release(THIS); + THIS->handle = handle; + rsvg_handle_get_dimensions(handle, &dim); + THIS->width = dim.width; + THIS->height = dim.height; + + handle = NULL; + +__RETURN: + + if (handle) + g_object_unref(G_OBJECT(handle)); + + GB.ReleaseFile(addr, len); + return err; +} + +BEGIN_METHOD(SvgImage_Load, GB_STRING path) + + CSVGIMAGE *svgimage; + const char *err; + + svgimage = (CSVGIMAGE *)GB.New(CLASS_SvgImage, NULL, NULL); + + if ((err = load_file(svgimage, STRING(path), LENGTH(path)))) + { + GB.Unref(POINTER(&svgimage)); + GB.Error(err); + return; + } + + GB.ReturnObject(svgimage); + +END_METHOD + +BEGIN_METHOD(SvgImage_Paint, GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h) + + cairo_t *context = PAINT_get_current_context(); + const char *err; + cairo_matrix_t matrix; + double tx, ty, sx, sy; + RsvgDimensionData dim; + + if (!context) + return; + + if (THIS->file) + { + cairo_surface_finish(SURFACE); + if ((err = load_file(THIS, THIS->file, GB.StringLength(THIS->file)))) + { + GB.Error(err); + return; + } + } + + if (!HANDLE) + return; + + if (THIS->width <= 0 || THIS->height <= 0) + return; + + rsvg_handle_get_dimensions(HANDLE, &dim); + sx = VARGOPT(w, THIS->width) / dim.width; + sy = VARGOPT(h, THIS->height) / dim.height; + + cairo_get_matrix(context, &matrix); + cairo_scale(context, sx, sy); + cairo_get_current_point(context, &tx, &ty); + cairo_translate(context, VARGOPT(x, tx), VARGOPT(y, ty)); + rsvg_handle_render_cairo(HANDLE, context); + cairo_set_matrix(context, &matrix); + +END_METHOD + +BEGIN_METHOD(SvgImage_Save, GB_STRING file) + + if (!THIS->file) + { + if (!SVGIMAGE_begin(THIS)) + { + GB.Error("Void image"); + return; + } + } + + cairo_surface_finish(SURFACE); + if (GB.CopyFile(THIS->file, GB.FileName(STRING(file), LENGTH(file)))) + return; + + load_file(THIS, THIS->file, GB.StringLength(THIS->file)); + +END_METHOD + +BEGIN_METHOD_VOID(SvgImage_Clear) + + release(THIS); + +END_METHOD + +GB_DESC SvgImageDesc[] = +{ + GB_DECLARE("SvgImage", sizeof(CSVGIMAGE)), + + GB_METHOD("_new", NULL, SvgImage_new, "[(Width)f(Height)f]"), + GB_METHOD("_free", NULL, SvgImage_free, NULL), + + GB_PROPERTY("Width", "f", SvgImage_Width), + GB_PROPERTY("W", "f", SvgImage_Width), + GB_PROPERTY("Height", "f", SvgImage_Height), + GB_PROPERTY("H", "f", SvgImage_Height), + GB_METHOD("Resize", NULL, SvgImage_Resize, "(Width)f(Height)f"), + + GB_STATIC_METHOD("Load", "SvgImage", SvgImage_Load, "(Path)s"), + GB_METHOD("Save", NULL, SvgImage_Save, "(Path)s"), + GB_METHOD("Paint", NULL, SvgImage_Paint, "[(X)f(Y)f(Width)f(Height)f]"), + + GB_METHOD("Clear", NULL, SvgImage_Clear, NULL), + + GB_INTERFACE("Paint", &PAINT_Interface), + + GB_END_DECLARE +}; + diff --git a/gb.gtk/src/csvgimage.h b/gb.gtk/src/csvgimage.h new file mode 100644 index 00000000..c0a5168f --- /dev/null +++ b/gb.gtk/src/csvgimage.h @@ -0,0 +1,62 @@ +/*************************************************************************** + + csvgimage.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSVGIMAGE_H +#define __CSVGIMAGE_H + +#include "gambas.h" +#include "widgets.h" +#include +#include +#ifndef RSVG_CAIRO_H +#include +#endif + +typedef + struct + { + GB_BASE ob; + cairo_surface_t *surface; + RsvgHandle *handle; + char *file; + double width; + double height; + } + CSVGIMAGE; + +#ifndef __CSVGIMAGE_CPP + +extern GB_DESC SvgImageDesc[]; + +#else + +#define THIS OBJECT(CSVGIMAGE) +#define SURFACE THIS->surface +#define HANDLE THIS->handle + +#endif + +cairo_surface_t *SVGIMAGE_begin(CSVGIMAGE *_object); +void SVGIMAGE_end(CSVGIMAGE *_object); + +#endif diff --git a/gb.gtk/src/desktop.c b/gb.gtk/src/desktop.c new file mode 120000 index 00000000..d7d642ed --- /dev/null +++ b/gb.gtk/src/desktop.c @@ -0,0 +1 @@ +../../gb.qt4/src/desktop.c \ No newline at end of file diff --git a/gb.gtk/src/desktop.h b/gb.gtk/src/desktop.h new file mode 120000 index 00000000..f260187e --- /dev/null +++ b/gb.gtk/src/desktop.h @@ -0,0 +1 @@ +../../gb.qt4/src/desktop.h \ No newline at end of file diff --git a/gb.gtk/src/font-parser.cpp b/gb.gtk/src/font-parser.cpp new file mode 100644 index 00000000..dc978655 --- /dev/null +++ b/gb.gtk/src/font-parser.cpp @@ -0,0 +1,175 @@ +/*************************************************************************** + + font-parser.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include +#include +#include + +static char *gb_font_pointers[8]; +static char *gb_font_name; +static int gb_font_strikeout; +static int gb_font_underline; +static int gb_font_italic; +static int gb_font_bold; +static int gb_font_size; +static int gb_font_relative; + +int gb_fontparser_italic() +{ + return gb_font_italic; +} + +int gb_fontparser_bold() +{ + return gb_font_bold; +} + +int gb_fontparser_underline() +{ + return gb_font_underline; +} + +int gb_fontparser_strikeout() +{ + return gb_font_strikeout; +} + +int gb_fontparser_relative() +{ + return gb_font_relative; +} + +int gb_fontparser_size() +{ + return gb_font_size; +} + +char* gb_fontparser_name() +{ + return gb_font_name; +} + +void gb_font_trim() +{ + int bc,lenptr; + char *ptr; + + for (bc=0;bc<8;bc++) + { + if (gb_font_pointers[bc]==NULL) return; + ptr=gb_font_pointers[bc]; + while (ptr[0]==' ') + { + if (ptr[0]==0) break; + if (ptr[0]==' ') gb_font_pointers[bc]++; + ptr++; + } + lenptr=strlen(gb_font_pointers[bc])-1; + ptr=gb_font_pointers[bc]; + while (lenptr>=0) + { + if (ptr[lenptr]==' ') { ptr[lenptr]=0; } else { lenptr=0; } + lenptr--; + } + } + + +} + +int gb_font_is_size(char *str) +{ + long bc,max; + int rel=false; + int vl=0; + int fact=1; + + if (!str) return true; + + max=strlen(str); + for(bc=0;bc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +int gb_fontparser_italic(); +int gb_fontparser_bold(); +int gb_fontparser_underline(); +int gb_fontparser_strikeout(); +int gb_fontparser_relative(); +int gb_fontparser_size(); +char* gb_fontparser_name(); +void gb_fontparser_parse(char *str); + diff --git a/gb.gtk/src/gapplication.cpp b/gb.gtk/src/gapplication.cpp new file mode 100644 index 00000000..fa75aff3 --- /dev/null +++ b/gb.gtk/src/gapplication.cpp @@ -0,0 +1,1552 @@ +/*************************************************************************** + + gapplication.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include +#include +#include + +#include "widgets.h" +#include "x11.h" +#include "gapplication.h" +#include "gtrayicon.h" +#include "gdesktop.h" +#include "gkey.h" +#include "gmenu.h" +#include "gmessage.h" +#include "gdialog.h" +#include "gclipboard.h" +#include "gmouse.h" +#include "gprinter.h" +#include "sm/sm.h" +#include "gmainwindow.h" + +//#define DEBUG_ENTER_LEAVE 1 + +static bool _debug_keypress = false; + +bool gApplication::fix_breeze = false; +bool gApplication::fix_oxygen = false; + + +/************************************************************************** + + Global event handler + +**************************************************************************/ + +static bool _focus_change = false; + +static GtkWindowGroup *get_window_group(GtkWidget *widget) +{ + GtkWidget *toplevel = NULL; + + if (widget) + toplevel = gtk_widget_get_toplevel(widget); + + if (GTK_IS_WINDOW(toplevel)) + return gtk_window_get_group(GTK_WINDOW(toplevel)); + else + return gtk_window_get_group(NULL); +} + +/*static gboolean close_dialog(GtkButton *button) +{ + gtk_button_clicked(button); + return FALSE; +}*/ + +/*static bool raise_key_event_to_parent_window(gControl *control, int type) +{ + gMainWindow *win; + + while (control->parent()) + { + win = control->parent()->window(); + if (win->onKeyEvent && win->canRaise(win, type)) + { + //fprintf(stderr, "onKeyEvent: %d %p %s\n", type, win, win->name()); + if (win->onKeyEvent(win, type)) + return true; + } + + control = win; + } + + return false; +}*/ + +/* +static bool check_crossing_event(GdkEvent *event) +{ + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "check_crossing_event: %d / %d\n", event->crossing.detail, event->crossing.mode); + #endif + + return true; + + if ((event->crossing.mode == GDK_CROSSING_NORMAL || event->crossing.mode == GDK_CROSSING_STATE_CHANGED)) + // || event->crossing.mode == GDK_CROSSING_UNGRAB || event->crossing.mode == GDK_CROSSING_GTK_UNGRAB)) + return true; + else + { + fprintf(stderr, "ignore\n"); + return false; + } +}*/ + +static gControl *find_child(gControl *control, int rx, int ry, gControl *button_grab = NULL) +{ + gContainer *cont; + gControl *child; + int x, y; + int cx, cy, cw, ch; + + if (gApplication::_control_grab) + return gApplication::_control_grab; + + /*grab = gtk_grab_get_current(); + if (grab) + { + child = gt_get_control(grab); + if (child) + return child; + }*/ + + if (button_grab) + return button_grab; + + //fprintf(stderr, "find_child: %s\n", control->name()); + + control = control->topLevel(); + + while (control->isContainer()) + { + control->getScreenPos(&x, &y); + cont = (gContainer *)control; + + cx = cont->clientX(); + cy = cont->clientY(); + cw = cont->clientWidth(); + ch = cont->clientHeight(); + + //fprintf(stderr, "client area of %s: %d %d %d %d\n", control->name(), cx, cy, cw, ch); + + x = rx - x; + y = ry - y; + if (x < cx || y < cy || x >= (cx + cw) || y >= (cy + ch)) + { + //fprintf(stderr, "outside of client area of %s\n", control->name()); + return NULL; + } + + child = cont->find(x, y); + if (!child) + break; + + control = child; + } + + //fprintf(stderr, "-> %s\n", control->name()); + + return control; +} + +void gApplication::checkHoveredControl(gControl *control) +{ + if (gApplication::_enter != control) + { + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "checkHoveredControl: %s\n", control->name()); + #endif + + gControl *leave = gApplication::_enter; + + while (leave && leave != control && !leave->isAncestorOf(control)) + { + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "checkHoveredControl: leave: %s\n", leave->name()); + #endif + leave->emitLeaveEvent(); + leave = leave->parent(); + } + + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "checkHoveredControl: _enter <- %s\n", control ? control->name() : "ø"); + #endif + + if (control) + { + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "checkHoveredControl: enter: %s\n", control->name()); + #endif + control->emitEnterEvent(); + } + } +} + +static void gambas_handle_event(GdkEvent *event) +{ + GtkWidget *widget; + GtkWidget *current_grab; + GtkWidget *grab; + GtkWindowGroup *group; +#ifdef GTK3 + GdkDevice *device; +#endif + gControl *control, *save_control; + gControl *button_grab; + int x, y, xs, ys, xc, yc; + bool cancel; + int type; + bool handle_event = false; + + if (gApplication::_fix_printer_dialog) + { + widget = gtk_get_event_widget(event); + if (widget) + { + //fprintf(stderr, "type: %s\n", G_OBJECT_TYPE_NAME(widget)); + if (!strcmp(G_OBJECT_TYPE_NAME(gtk_widget_get_toplevel(widget)), "GtkPrintUnixDialog")) + { + if (event->type == GDK_WINDOW_STATE) + { + //fprintf(stderr, "event: GDK_WINDOW_STATE!\n"); + widget = gtk_window_get_default_widget(GTK_WINDOW(gtk_widget_get_toplevel(widget))); + if (widget && GTK_IS_BUTTON(widget)) + { + GtkPrintUnixDialog *dialog = GTK_PRINT_UNIX_DIALOG(gtk_widget_get_toplevel(widget)); + gPrinter::fixPrintDialog(dialog); + gApplication::_fix_printer_dialog = false; + //fprintf(stderr, "gtk_button_clicked: %s\n", gtk_button_get_label(GTK_BUTTON(widget))); + if (gApplication::_close_next_window) + gtk_button_clicked(GTK_BUTTON(widget)); + gApplication::_close_next_window = false; + //return; + //g_timeout_add(0, (GSourceFunc)close_dialog, GTK_BUTTON(widget)); + goto __HANDLE_EVENT; + } + //fprintf(stderr, "event: MAP! <<< end\n"); + } + } + } + } + + /*if (event->type == GDK_GRAB_BROKEN) + { + if (gApplication::_in_popup) + fprintf(stderr, "**** GDK_GRAB_BROKEN inside popup: %s %swindow = %p grab_window = %p popup_window = %p\n", event->grab_broken.keyboard ? "keyboard" : "pointer", + event->grab_broken.implicit ? "implicit " : "", event->grab_broken.window, event->grab_broken.grab_window, gApplication::_popup_grab_window); + }*/ + + if (!((event->type >= GDK_MOTION_NOTIFY && event->type <= GDK_FOCUS_CHANGE) || event->type == GDK_SCROLL)) + goto __HANDLE_EVENT; + + widget = gtk_get_event_widget(event); + if (!widget) + goto __HANDLE_EVENT; + + if (_debug_keypress && (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)) + { + fprintf(stderr, "[%p] %s: keyval = %d state = %08X (%08X) is_modifier = %d hardware = %d send_event = %d for %p\n", event, event->type == GDK_KEY_PRESS ? "GDK_KEY_PRESS" : "GDK_KEY_RELEASE", + event->key.keyval, event->key.state, event->key.state & ~GDK_MODIFIER_MASK, event->key.is_modifier, event->key.hardware_keycode, event->key.send_event, widget); + } + + /*if ((event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)) + { + if (event->key.state & ~GDK_MODIFIER_MASK) // == 0) + { + if (_debug_keypress) + fprintf(stderr, "ignore key event\n"); + goto __HANDLE_EVENT; + } + }*/ + +#ifdef GTK3 + device = gdk_event_get_device (event); + group = get_window_group(widget); + current_grab = gtk_window_group_get_current_device_grab(group, device); + if (!current_grab) + current_grab = gtk_window_group_get_current_grab(group); //gtk_grab_get_current(); +#else + group = get_window_group(widget); + current_grab = gtk_window_group_get_current_grab(group); //gtk_grab_get_current(); +#endif + + button_grab = gApplication::_button_grab; + + if (gApplication::_control_grab) + { + control = gApplication::_control_grab; + widget = control->border; + //fprintf(stderr, "[1] _control_grab: %s -> widget = %p\n", control->name(), widget); + goto __FOUND_WIDGET; + } + + if (gMenu::currentPopup()) + { + grab = GTK_WIDGET(gMenu::currentPopup()->child); + //fprintf(stderr, "[2] popup menu: grab = %p\n", grab); + if (get_window_group(grab) != get_window_group(widget) && (event->type == GDK_ENTER_NOTIFY || event->type == GDK_LEAVE_NOTIFY)) + goto __RETURN; + } + else + { + grab = current_grab; //gtk_window_group_get_current_grab(get_window_group(widget)); + //fprintf(stderr, "[3] popup: grab = %p / %p / %p\n", gApplication::_popup_grab, grab, gtk_grab_get_current()); + if (!grab) + grab = gApplication::_popup_grab; + //fprintf(stderr, "[4] grab = %p\n", grab); + //fprintf(stderr, "search grab for widget %p -> group = %p -> grab = %p WINDOW = %d\n", widget, get_window_group(widget), grab, GTK_IS_WINDOW(grab)); + //if (grab && grab != widget && !GTK_IS_WINDOW(grab)) + // goto __HANDLE_EVENT; + + //if (!grab && gApplication::_popup_grab) + // grab = gApplication::_popup_grab; + } + //gdk_window_get_user_data(gApplication::_popup_grab_window, (gpointer *)&grab); + + if (grab) + { + control = gt_get_control(grab); + //fprintf(stderr, "grab = %p -> %p %s\n", grab, control, control ? control->name() : ""); + + if (!control) + goto __HANDLE_EVENT; + } + + if (event->type == GDK_FOCUS_CHANGE) + { + control = NULL; + //if (GTK_IS_WINDOW(widget)) + control = gt_get_control(widget); + //fprintf(stderr, "GDK_FOCUS_CHANGE: widget = %p %d : %s %d\n", widget, GTK_IS_WINDOW(widget), control ? control->name() : NULL, event->focus_change.in); + + //if (GTK_IS_WINDOW(widget)) + { + control = gt_get_control(widget); + if (control) + gApplication::setActiveControl(control, event->focus_change.in); + else if (event->focus_change.in) + { + //fprintf(stderr, "GDK_FOCUS_CHANGE: setActiveWindow(NULL)\n"); + gMainWindow::setActiveWindow(NULL); + } + } + + if (event->focus_change.in && grab && widget != grab && !gtk_widget_is_ancestor(widget, grab)) + { + //fprintf(stderr, "Check popup grab\n"); + gApplication::grabPopup(); + // Must continue, otherwise things are broken by some styles + //return; + } + + goto __HANDLE_EVENT; + } + + if (grab && widget != grab && !gtk_widget_is_ancestor(widget, grab)) + { + //fprintf(stderr, "-> widget = grab\n"); + widget = grab; + } + + //fprintf(stderr, "grab = %p widget = %p %d\n", grab, widget, grab && !gtk_widget_is_ancestor(widget, grab)); + + while (widget) + { + control = gt_get_control(widget); + if (control || grab) + break; + widget = gtk_widget_get_parent(widget); + } + + /*if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE || event->type == GDK_MOTION_NOTIFY) + { + fprintf(stderr, "[%s] widget = %p grab = %p _popup_grab = %p _button_grab = %p\n", + event->type == GDK_BUTTON_PRESS ? "down" : event->type == GDK_BUTTON_RELEASE ? "up" : "move", + widget, grab, gApplication::_popup_grab, gApplication::_button_grab); + //fprintf(stderr, "widget = %p (%p) grab = %p (%p)\n", widget, widget ? g_object_get_data(G_OBJECT(widget), "gambas-control") : 0, + // grab, grab ? g_object_get_data(G_OBJECT(grab), "gambas-control") : 0); + }*/ + + /*if (event->type == GDK_BUTTON_PRESS || event->type == GDK_KEY_PRESS) + { + fprintf(stderr, "[GDK_BUTTON_PRESS] widget = %p control = %p grab = %p _popup_grab = %p _button_grab = %p\n", + widget, control, grab, gApplication::_popup_grab, gApplication::_button_grab); + }*/ + + if (!widget || !control) + goto __HANDLE_EVENT; + +__FOUND_WIDGET: + + //fprintf(stderr, "control = %p %s\n", control, control->name()); + + /*switch ((int)event->type) + { + case GDK_ENTER_NOTIFY: + fprintf(stderr, "ENTER: %p %s\n", control, control ? control->name() : 0); + break; + + case GDK_LEAVE_NOTIFY: + fprintf(stderr, "LEAVE: %p %s\n", control, control ? control->name() : 0); + break; + }*/ + + //group = get_window_group(widget); + //if (group != gApplication::currentGroup()) + // goto __HANDLE_EVENT; + + cancel = false; + + gApplication::updateLastEvent(event); + + switch ((int)event->type) + { + case GDK_ENTER_NOTIFY: + + control = find_child(control, (int)event->crossing.x_root, (int)event->crossing.y_root); + if (!control) + goto __HANDLE_EVENT; + +#if DEBUG_ENTER_LEAVE + fprintf(stderr, "GDK_ENTER_NOTIFY: %s (%s) %d %d %p %p\n", control->name(), gApplication::_enter ? gApplication::_enter->name() : "ø", (int)event->crossing.x_root, (int)event->crossing.y_root, event->crossing.window, event->crossing.subwindow); +#endif + + if (button_grab) + { + gApplication::_enter_after_button_grab = control; + break; + } + + if (gApplication::_leave) + { + if (gApplication::_leave == control || gApplication::_leave->isAncestorOf(control)) + gApplication::_leave = NULL; + } + + gApplication::checkHoveredControl(control); + + /* + if (gApplication::_leave == control) + { + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "enter ignored: %s\n", control->name()); + #endif + gApplication::_leave = NULL; + } + else if (gApplication::_enter != control) + { + if (check_crossing_event(event)) + { + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "enter: %s\n", control->name()); + #endif + gApplication::checkHoveredControl(control); + } + }*/ + + break; + + case GDK_LEAVE_NOTIFY: + +#if DEBUG_ENTER_LEAVE + fprintf(stderr, "GDK_LEAVE_NOTIFY: %s %p %p\n", control->name(), event->crossing.window, event->crossing.subwindow); +#endif + + if (button_grab) + break; + + //control = find_child(control, (int)event->button.x_root, (int)event->button.y_root); + + gApplication::_leave = control; + /* + if (gdk_events_pending() && gApplication::_leave == NULL) + { + if (check_crossing_event(event)) + { + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "leave later: %s\n", control->name()); + #endif + gApplication::_leave = control; + } + } + else if (gApplication::_leave != control) + { + if (check_crossing_event(event)) + { + if (gApplication::_leave == control) + gApplication::_leave = NULL; + + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "leave: %s\n", control->name()); + #endif + control->emitLeaveEvent(); + } + } + */ + + //if (widget != control->border && widget != control->widget) + // goto __RETURN; + + break; + + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + { + /*if (event->type == GDK_BUTTON_PRESS) + fprintf(stderr, "GDK_BUTTON_PRESS: %p / %p\n", control, button_grab); + else if (event->type == GDK_BUTTON_RELEASE) + fprintf(stderr, "GDK_BUTTON_RELEASE: %p / %p\n", control, button_grab);*/ + + switch ((int)event->type) + { + case GDK_BUTTON_PRESS: type = gEvent_MousePress; break; + case GDK_2BUTTON_PRESS: type = gEvent_MouseDblClick; break; + default: type = gEvent_MouseRelease; break; + } + + save_control = find_child(control, (int)event->button.x_root, (int)event->button.y_root, button_grab); + + if (!save_control) + { + if (type == gEvent_MousePress && control->isTopLevel()) + { + gMainWindow *win = ((gMainWindow *)control); + if (win->isPopup()) + win->close(); + } + + goto __HANDLE_EVENT; + } + + control = save_control; + + bool menu = false; + + if (event->type != GDK_BUTTON_RELEASE) + { + if (control->canFocus() && !control->hasFocus()) + control->setFocus(); + if (!control->_no_auto_grab) + gApplication::setButtonGrab(control); + } + + + __BUTTON_TRY_PROXY: + + if (!control->design() && !control->isEnabled()) + goto __HANDLE_EVENT; + + cancel = false; + + if (control->onMouseEvent) + { + if (control->canRaise(control, type)) + { + control->getScreenPos(&xc, &yc); + xs = (int)event->button.x_root; + ys = (int)event->button.y_root; + x = xs - xc; + y = ys - yc; + + gMouse::validate(); + gMouse::setEvent(event); + //gMouse::setValid(1,(int)event->x,(int)event->y,event->button,event->state,data->screenX(),data->screenY()); + gMouse::setMouse(x, y, xs, ys, event->button.button, event->button.state); + switch ((int)event->type) + { + case GDK_BUTTON_PRESS: + gMouse::setStart(x, y); + cancel = control->onMouseEvent(control, gEvent_MousePress); + break; + + case GDK_2BUTTON_PRESS: + cancel = control->onMouseEvent(control, gEvent_MouseDblClick); + break; + + case GDK_BUTTON_RELEASE: + cancel = control->onMouseEvent(control, gEvent_MouseRelease); + break; + } + + gMouse::invalidate(); + } + } + + /*if (type == gEvent_MousePress && control->isTopLevel()) + { + gMainWindow *win = ((gMainWindow *)control); + if (win->isPopup()) + { + control->getScreenPos(&xc, &yc); + xs = (int)event->button.x_root; + ys = (int)event->button.y_root; + x = xs - xc; + y = ys - yc; + + if (x < 0 || y < 0 || x >= win->width() || y >= win->height()) + win->close(); + } + } + else*/ if (type == gEvent_MouseRelease && control->_grab) + { + gApplication::exitLoop(control); + } + +#if GTK_CHECK_VERSION(3, 4, 0) + if (gdk_event_triggers_context_menu(event)) +#else + if (event->button.button == 3 && event->type == GDK_BUTTON_PRESS) +#endif + menu = true; + + if (!cancel) + { + if (control->_proxy_for) + { + control = control->_proxy_for; + //fprintf(stderr, "PRESS: try %s\n", control->name()); + goto __BUTTON_TRY_PROXY; + } + } + + if (menu) + { + control = save_control; + while (control) + { + if (control->onMouseEvent(control, gEvent_MouseMenu)) + { + cancel = true; + break; + } + control = control->_proxy_for; + } + } + + if (cancel) + { + gMouse::resetTranslate(); + goto __RETURN; + } + + if (widget != save_control->border && widget != save_control->widget) + { + //fprintf(stderr, "widget = %p, control = %p %p %s\n", widget, save_control->border, save_control->widget, save_control->name()); + gMouse::resetTranslate(); + goto __RETURN; + } + + break; + } + + case GDK_MOTION_NOTIFY: + + gdk_event_request_motions(&event->motion); + + save_control = control = find_child(control, (int)event->motion.x_root, (int)event->motion.y_root, button_grab); + if (!control) + goto __HANDLE_EVENT; + + //fprintf(stderr, "GDK_MOTION_NOTIFY: (%p %s) grab = %p\n", control, control->name(), button_grab); + + gApplication::checkHoveredControl(control); + + __MOTION_TRY_PROXY: + + if (!control->design() && !control->isEnabled()) + goto __HANDLE_EVENT; + + if (control->onMouseEvent && (control->canRaise(control, gEvent_MouseMove) || control->canRaise(control, gEvent_MouseDrag)) + && (control->isTracking() || (event->motion.state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)))) + { + control->getScreenPos(&xc, &yc); + xs = (int)event->motion.x_root; + ys = (int)event->motion.y_root; + x = xs - xc; + y = ys - yc; + + gMouse::validate(); + gMouse::setEvent(event); + gMouse::setMouse(x, y, xs, ys, 0, event->motion.state); + + //fprintf(stderr, "pressure = %g\n", gMouse::getAxis(GDK_AXIS_PRESSURE)); + + cancel = control->onMouseEvent(control, gEvent_MouseMove); + + //if (data->acceptDrops() && gDrag::checkThreshold(data, gMouse::x(), gMouse::y(), gMouse::startX(), gMouse::startY())) + if (!cancel && (event->motion.state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) + //&& (abs(gMouse::x() - gMouse::y()) + abs(gMouse::startX() - gMouse::startY())) > 8) + && gDrag::checkThreshold(control, gMouse::x(), gMouse::y(), gMouse::startX(), gMouse::startY())) + { + //fprintf(stderr, "gEvent_MouseDrag: event = %p\n", gApplication::lastEvent()); + cancel = control->onMouseEvent(control, gEvent_MouseDrag); + } + gMouse::invalidate(); + + if (cancel) + goto __RETURN; + } + + if (control->_proxy_for) + { + control = control->_proxy_for; + //fprintf(stderr, "MOVE: try %s\n", control->name()); + goto __MOTION_TRY_PROXY; + } + + gMouse::resetTranslate(); + //if (widget != save_control->border && widget != save_control->widget) + // goto __RETURN; + + break; + + case GDK_SCROLL: + + save_control = control = find_child(control, (int)event->scroll.x_root, (int)event->scroll.y_root); + if (!control) + goto __HANDLE_EVENT; + + __SCROLL_TRY_PROXY: + + if (!control->design() && !control->isEnabled()) + goto __HANDLE_EVENT; + + if (control->onMouseEvent && control->canRaise(control, gEvent_MouseWheel)) + { + int dir, dt, ort; + + control->getScreenPos(&xc, &yc); + xs = (int)event->scroll.x_root; + ys = (int)event->scroll.y_root; + x = xs - xc; + y = ys - yc; + + dir = event->scroll.direction; + +#ifdef GTK3 + if (dir == GDK_SCROLL_SMOOTH) + { + /*gdouble dx = 0, dy = 0; + gdk_event_get_scroll_deltas((GdkEvent *)event, &dx, &dy); + if (fabs(dy) > fabs(dx)) + dir = (dy < 0) ? GDK_SCROLL_UP : GDK_SCROLL_DOWN; + else + dir = (dx < 0) ? GDK_SCROLL_LEFT : GDK_SCROLL_RIGHT;*/ + goto __HANDLE_EVENT; + } +#endif + + switch (dir) + { + case GDK_SCROLL_DOWN: dt = -1; ort = 1; break; + case GDK_SCROLL_LEFT: dt = -1; ort = 0; break; + case GDK_SCROLL_RIGHT: dt = 1; ort = 0; break; + case GDK_SCROLL_UP: default: dt = 1; ort = 1; break; + } + + gMouse::validate(); + gMouse::setEvent(event); + gMouse::setMouse(x, y, xs, ys, 0, event->scroll.state); + gMouse::setWheel(dt, ort); + cancel = control->onMouseEvent(control, gEvent_MouseWheel); + gMouse::invalidate(); + } + + if (cancel) + { + gMouse::resetTranslate(); + goto __RETURN; + } + + if (control->_proxy_for) + { + control = control->_proxy_for; + goto __SCROLL_TRY_PROXY; + } + + if (!control->_use_wheel) + { + control = control->parent(); + if (control) + goto __SCROLL_TRY_PROXY; + } + + if (widget != save_control->border && widget != save_control->widget) + { + gMouse::resetTranslate(); + goto __RETURN; + } + + break; + + case GDK_KEY_PRESS: + + if (event->key.keyval) + gKey::_last_key_press = event->key.keyval; + goto __HANDLE_EVENT; + + case GDK_KEY_RELEASE: + + if (event->key.keyval) + gKey::_last_key_release = event->key.keyval; + goto __HANDLE_EVENT; + } + +__HANDLE_EVENT: + + handle_event = true; + +__RETURN: + + if (event->type == GDK_BUTTON_RELEASE && gApplication::_button_grab) + { + if (gApplication::_enter_after_button_grab) + { + gApplication::checkHoveredControl(gApplication::_enter_after_button_grab); + gApplication::_enter_after_button_grab = NULL; + } + gApplication::setButtonGrab(NULL); + } + + if (handle_event) + gtk_main_do_event(event); + + if (!gdk_events_pending()) // && event->type != GDK_ENTER_NOTIFY && event->type != GDK_LEAVE_NOTIFY) + { + if (gApplication::_leave) + { + //if () // || (gApplication::_leave != control && check_crossing_event(event))) + #if DEBUG_ENTER_LEAVE + fprintf(stderr, "post leave: %s\n", gApplication::_leave->name()); + #endif + + if (gApplication::_enter == gApplication::_leave) + gApplication::_enter = NULL; + + gApplication::_leave->emitLeaveEvent(); + + gApplication::_leave = NULL; + } + } + +} + + + +/************************************************************************** + +gApplication + +**************************************************************************/ + +int appEvents; + +bool gApplication::_init = false; +bool gApplication::_busy = false; +char *gApplication::_title = NULL; +int gApplication::_in_popup = 0; +GtkWidget *gApplication::_popup_grab = NULL; +int gApplication::_loopLevel = 0; +void *gApplication::_loop_owner = 0; +GtkWindowGroup *gApplication::_group = NULL; +gControl *gApplication::_enter = NULL; +gControl *gApplication::_leave = NULL; +gControl *gApplication::_ignore_until_next_enter = NULL; +gControl *gApplication::_button_grab = NULL; +gControl *gApplication::_enter_after_button_grab = NULL; +gControl *gApplication::_control_grab = NULL; +gControl *gApplication::_active_control = NULL; +gControl *gApplication::_previous_control = NULL; +gControl *gApplication::_old_active_control = NULL; +bool (*gApplication::onKeyEvent)(int) = NULL; +guint32 gApplication::_event_time = 0; +bool gApplication::_close_next_window = false; +bool gApplication::_fix_printer_dialog = false; +gMainWindow *gApplication::_main_window = NULL; +void (*gApplication::onEnterEventLoop)(); +void (*gApplication::onLeaveEventLoop)(); +bool gApplication::_must_quit = false; +GdkEvent *gApplication::_event = NULL; + +void gApplication::grabPopup() +{ + //fprintf(stderr, "grabPopup: %p\n", _popup_grab); + + if (!_popup_grab) + return; + + gt_grab(_popup_grab, TRUE, GDK_CURRENT_TIME); +} + +void gApplication::ungrabPopup() +{ + //fprintf(stderr, "ungrabPopup: %p\n", _popup_grab); + //gtk_grab_remove(_popup_grab); + + _popup_grab = NULL; + gt_ungrab(); +} + +bool gApplication::areTooltipsEnabled() +{ + gboolean enabled; + GtkSettings *settings; + + settings = gtk_settings_get_default(); + + g_object_get (settings, "gtk-enable-tooltips", &enabled, (char *)NULL); + + return enabled; +} + +void gApplication::enableTooltips(bool vl) +{ + GtkSettings *settings; + gboolean enabled = vl; + settings = gtk_settings_get_default(); + g_object_set (settings, "gtk-enable-tooltips", enabled, (char *)NULL); +} + +void gApplication::suspendEvents(bool vl) +{ + if (!vl) appEvents=3; //all + else appEvents=1; //user +} + +void gApplication::enableEvents() +{ + appEvents=0; +} + +bool gApplication::userEvents() +{ + if (appEvents) return false; + return true; +} + +bool gApplication::allEvents() +{ + if (appEvents & 2) return false; + return true; +} + +static void do_nothing() +{ +} + +/*#ifdef GTK3 +static void (*_old_scrollbar_button_press)(); +static void (*_old_scrollbar_button_release)(); + +static gint scrollbar_button_press(GtkWidget *widget, GdkEventButton *event) +{ + gint ret = ((gint (*)(GtkWidget *, GdkEventButton *))_old_scrollbar_button_press)(widget, event); + if (ret) + gtk_grab_add(widget); + return ret; +} + +static gint scrollbar_button_release(GtkWidget *widget, GdkEventButton *event) +{ + gint ret = ((gint (*)(GtkWidget *, GdkEventButton *))_old_scrollbar_button_release)(widget, event); + if (ret) + gtk_grab_remove(widget); + return ret; +} + +#endif*/ + +static gboolean master_client_save_yourself(GnomeClient *client, gint phase, GnomeSaveStyle save_style, gboolean is_shutting_down, GnomeInteractStyle interact_style, gboolean fast, gpointer user_data) +{ + if (gApplication::mainWindow()) + { + //fprintf(stderr, "master_client_save_yourself: %d\n", X11_window_get_desktop((Window)(gApplication::mainWindow()->handle()))); + session_manager_set_desktop(X11_window_get_desktop((Window)(gApplication::mainWindow()->handle()))); + } + return true; +} + +static void master_client_die(GnomeClient *client, gpointer user_data) +{ + if (gApplication::mainWindow()) + gApplication::mainWindow()->close(); + else + gMainWindow::closeAll(); + + gApplication::quit(); + MAIN_check_quit(); +} + +void gApplication::init(int *argc, char ***argv) +{ + appEvents=0; + + gtk_init(argc, argv); + session_manager_init(argc, argv); + g_signal_connect(gnome_master_client(), "save-yourself", G_CALLBACK(master_client_save_yourself), NULL); + g_signal_connect(gnome_master_client(), "die", G_CALLBACK(master_client_die), NULL); + + gdk_event_handler_set((GdkEventFunc)gambas_handle_event, NULL, NULL); + + gKey::init(); + + onEnterEventLoop = do_nothing; + onLeaveEventLoop = do_nothing; + + _group = gtk_window_group_new(); + + _loop_owner = 0; + + char *env = getenv("GB_GTK_DEBUG_KEYPRESS"); + if (env && strcmp(env, "0")) + _debug_keypress = true; + + fix_breeze = strcasecmp(getStyleName(), "breeze") == 0 || strcasecmp(getStyleName(), "breeze dark") == 0 ; + fix_oxygen = strcasecmp(getStyleName(), "oxygen-gtk") == 0; + + gApplication::_init = true; +} + +void gApplication::exit() +{ + session_manager_exit(); + + if (_title) + g_free(_title); + + gKey::exit(); + gTrayIcon::exit(); + gDesktop::exit(); + gMessage::exit(); + gDialog::exit(); + gFont::exit(); + gt_exit(); +} + +int gApplication::controlCount() +{ + GList *iter; + int ct=1; + + if (!gControl::controlList()) return 0; + + iter=g_list_first(gControl::controlList()); + while (iter->next) + { + ct++; + iter=iter->next; + } + + return ct; +} + +gControl* gApplication::controlItem(GtkWidget *wid) +{ + gControl *control; + + while (wid) + { + control = gt_get_control(wid); + if (control) + return control; + wid = gtk_widget_get_parent(wid); + } + + return NULL; +} + +gControl* gApplication::controlItem(int index) +{ + GList *iter; + + if (!gControl::controlList()) return NULL; + iter=g_list_nth(gControl::controlList(),index); + if (!iter) return NULL; + return (gControl*)iter->data; +} + +void gApplication::setBusy(bool b) +{ + GList *iter; + gControl *control; + + if (b == _busy) + return; + + _busy = b; + + iter = g_list_first(gControl::controlList()); + + while (iter) + { + control = (gControl *)iter->data; + + if (control->mustUpdateCursor()) + control->setMouse(control->mouse()); + + iter = g_list_next(iter); + } + + gdk_display_flush(gdk_display_get_default()); +} + +static bool _dirty = false; + +static gboolean update_geometry(void *data) +{ + GList *iter; + gControl *control; + + if (gContainer::_arrangement_level) + return true; + + _dirty = false; + //g_debug(">>>> update_geometry"); + iter = g_list_first(gControl::controlList()); + while (iter) + { + control = (gControl *)iter->data; + control->updateGeometry(); + iter = iter->next; + } + //g_debug("<<<<"); + + return false; +} + +void gApplication::setDirty() +{ + if (_dirty) + return; + + _dirty = true; + g_timeout_add(0, (GSourceFunc)update_geometry, NULL); +} + +void gApplication::setDefaultTitle(const char *title) +{ + if (_title) + g_free(_title); + _title = g_strdup(title); +} + +GtkWindowGroup *gApplication::enterGroup() +{ + gControl *control = _enter; + GtkWindowGroup *oldGroup = _group; + _group = gtk_window_group_new(); + + _enter = _leave = NULL; + + while (control) + { + control->emit(SIGNAL(control->onEnterLeave), gEvent_Leave); + control = control->parent(); + } + + return oldGroup; +} + +void gApplication::exitGroup(GtkWindowGroup *oldGroup) +{ + g_object_unref(_group); + _group = oldGroup; +} + +void gApplication::enterLoop(void *owner, bool showIt, GtkWindow *modal) +{ + void *old_owner = _loop_owner; + int l = _loopLevel; + //GtkWindowGroup *oldGroup; + + if (showIt) ((gControl *)owner)->show(); + + //oldGroup = enterGroup(); + + _loopLevel++; + _loop_owner = owner; + + (*onEnterEventLoop)(); + do + { + MAIN_do_iteration(false); + } + while (_loopLevel > l); + (*onLeaveEventLoop)(); + + _loop_owner = old_owner; + + //exitGroup(oldGroup); +} + +void gApplication::enterPopup(gMainWindow *owner) +{ + void *old_owner; + int l; + //GtkWindowGroup *oldGroup; + GtkWindow *window = GTK_WINDOW(owner->border); + GtkWidget *old_popup_grab; + + _in_popup++; + + // Remove possible current button grab + gApplication::setButtonGrab(NULL); +// + //oldGroup = enterGroup(); + + gtk_window_set_modal(window, true); + gdk_window_set_override_redirect(gtk_widget_get_window(owner->border), true); + + owner->show(); + + if (!owner->isDestroyed()) + { + old_popup_grab = _popup_grab; + _popup_grab = owner->border; + + if (_in_popup == 1) + gApplication::grabPopup(); + + l = _loopLevel; + old_owner = _loop_owner; + + _loopLevel++; + _loop_owner = owner; + + (*onEnterEventLoop)(); + do + { + MAIN_do_iteration(false); + } + while (_loopLevel > l); + (*onLeaveEventLoop)(); + + gApplication::ungrabPopup(); + _popup_grab = old_popup_grab; + + _loop_owner = old_owner; + + if (owner->border) + { + gdk_window_set_override_redirect(gtk_widget_get_window(owner->border), false); + gtk_window_set_modal(window, false); + } + //exitGroup(oldGroup); + } + else + gControl::cleanRemovedControls(); + + _in_popup--; +} + +void gApplication::exitLoop(void *owner) +{ + if (!hasLoop(owner)) + return; + + if (_loopLevel > 0) + _loopLevel--; +} + +GtkWindowGroup *gApplication::currentGroup() +{ + if (_group) + return _group; + else + return gtk_window_get_group(NULL); +} + +void gApplication::updateLastEvent(GdkEvent *e) +{ + _event = e; + _event_time = gdk_event_get_time(e); +} + +void gApplication::updateLastEventTime() +{ + _event_time = gtk_get_current_event_time(); +} + +static void post_focus_change(void *) +{ + gControl *current, *control, *next; + + if (!_focus_change) + return; + + //fprintf(stderr, "post_focus_change\n"); + + for(;;) + { + current = gApplication::activeControl(); + //fprintf(stderr, "activeControl = %s\n", current ? current->name() : NULL); + if (current == gApplication::_old_active_control) + break; + + control = gApplication::_old_active_control; + while (control) + { + next = control->_proxy_for; + if (control->onFocusEvent) + control->onFocusEvent(control, gEvent_FocusOut); + control = next; + } + + current = gApplication::activeControl(); + if (current == gApplication::_old_active_control) + break; + + gApplication::_old_active_control = current; + //fprintf(stderr, "_old_active_control = %s\n", current ? current->name() : NULL); + gMainWindow::setActiveWindow(current); + + control = gApplication::activeControl(); + while (control) + { + next = control->_proxy_for; + if (control->onFocusEvent) + control->onFocusEvent(control, gEvent_FocusIn); + control = next; + } + } + + _focus_change = FALSE; +} + +void gApplication::handleFocusNow() +{ + post_focus_change(NULL); +} + +static void handle_focus_change() +{ + if (_focus_change) + return; + + _focus_change = TRUE; + GB.Post((void (*)())post_focus_change, (intptr_t)NULL); +} + +void gApplication::setActiveControl(gControl *control, bool on) +{ + if (on == (_active_control == control)) + return; + + //fprintf(stderr, "setActiveControl: %s %d\n", control->name(), on); + + if (_active_control) + _previous_control = _active_control; + + _active_control = on ? control : NULL; + gKey::setActiveControl(_active_control); + handle_focus_change(); +} + +int gApplication::getScrollbarSize() +{ + //GtkStyle* st; + gint trough_border; + gint slider_width; + + //st = gtk_rc_get_style_by_paths(gtk_settings_get_default(), NULL, "OsBar", G_TYPE_NONE); + + if (g_type_from_name("OsBar")) + { + char *env = getenv("LIBOVERLAY_SCROLLBAR"); + if (!env || *env != '0') + return 1; + } + + gt_get_style_property(GTK_TYPE_SCROLLBAR, "slider-width", &slider_width); + gt_get_style_property(GTK_TYPE_SCROLLBAR, "trough-border", &trough_border); + + return (trough_border) * 2 + slider_width; +} + +int gApplication::getScrollbarSpacing() +{ + gint v; + + gt_get_style_property(GTK_TYPE_SCROLLED_WINDOW, "scrollbar-spacing", &v); + + return v; +} + +int gApplication::getInnerWidth() +{ + if (fix_oxygen) + return 1; + else + return 0; +} + +int gApplication::getFrameWidth() +{ + int w; +#ifdef GTK3 + int h; + + getBoxFrame(&w, &h); + w = h; + +#else + GtkStyle *style; + gint focus_width; + gboolean interior_focus; + //int inner; + + style = gt_get_style(GTK_TYPE_ENTRY); + + gt_get_style_property(GTK_TYPE_ENTRY, "focus-line-width", &focus_width); + gt_get_style_property(GTK_TYPE_ENTRY, "interior-focus", &interior_focus); + + w = MIN(style->xthickness, style->ythickness); + + if (!interior_focus) + w += focus_width; + + w += getInnerWidth(); +#endif + + return w; +} + +void gApplication::getBoxFrame(int *pw, int *ph) +{ + int w, h; + +#ifdef GTK3 + + GtkStyleContext *context = gt_get_style(GTK_TYPE_ENTRY); + GtkBorder border; + GtkBorder padding; + int radius; + + gtk_style_context_get_padding(context, STATE_FOCUSED, &padding); + //fprintf(stderr, "padding: %d %d %d %d\n", padding.top, padding.right, padding.bottom, padding.left); + gtk_style_context_get_border(context, STATE_FOCUSED, &border); + //fprintf(stderr, "border: %d %d %d %d\n", border.top, border.right, border.bottom, border.left); + + gtk_style_context_get(context, STATE_FOCUSED, GTK_STYLE_PROPERTY_BORDER_RADIUS, &radius, NULL); + //fprintf(stderr, "border-radius: %d\n", radius); + radius /= 2; + + w = MAX(border.left + padding.left, border.right + padding.right); + w = MAX(w, radius); + + h = MAX(border.top + padding.top, border.bottom + padding.bottom);//, MAX(padding.top, padding.bottom)); + h = MAX(h, radius); + + w = MAX(2, w); + h = MAX(2, h); + +#else + + GtkStyle *style; + gint focus_width; + gboolean interior_focus; + int inner; + + style = gt_get_style(GTK_TYPE_ENTRY); + + gt_get_style_property(GTK_TYPE_ENTRY, "focus-line-width", &focus_width); + gt_get_style_property(GTK_TYPE_ENTRY, "interior-focus", &interior_focus); + + w = style->xthickness; + h = style->ythickness; + + if (!interior_focus) + { + w += focus_width; + h += focus_width; + } + + inner = getInnerWidth(); + w += inner; + h += inner; + +#endif + + *pw = w; + *ph = h; +} + +char *gApplication::getStyleName() +{ + static char *_theme = NULL; + + if (!_theme) + { + GtkSettings *settings = gtk_settings_get_default(); + g_object_get(settings, "gtk-theme-name", &_theme, (char *)NULL); + } + + return _theme; +} + +static GdkFilterReturn x11_event_filter(GdkXEvent *xevent, GdkEvent *event, gpointer data) +{ + ((X11_EVENT_FILTER)data)((XEvent *)xevent); + return GDK_FILTER_CONTINUE; +} + +void gApplication::setEventFilter(X11_EVENT_FILTER filter) +{ + static X11_EVENT_FILTER save_filter = NULL; + static GdkEventMask save_mask = (GdkEventMask)0; + + if (save_filter) + { + gdk_window_remove_filter(NULL, (GdkFilterFunc)x11_event_filter, (gpointer)save_filter); + gdk_window_set_events(gdk_get_default_root_window(), save_mask); + } + + if (filter) + { + save_mask = gdk_window_get_events(gdk_get_default_root_window()); + gdk_window_set_events(gdk_get_default_root_window(), (GdkEventMask)(save_mask | GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK)); + gdk_window_add_filter(NULL, (GdkFilterFunc)x11_event_filter, (gpointer)filter); + } + + save_filter = filter; +} + +void gApplication::setMainWindow(gMainWindow *win) +{ + _main_window = win; +} + +void gApplication::quit() +{ + _must_quit = true; +} + +int gApplication::dblClickTime() +{ + gint value; + g_object_get (gtk_settings_get_default(), "gtk-double-click-time", &value, (char *)NULL); + return value; +} diff --git a/gb.gtk/src/gapplication.h b/gb.gtk/src/gapplication.h new file mode 100644 index 00000000..ee5ca3a5 --- /dev/null +++ b/gb.gtk/src/gapplication.h @@ -0,0 +1,135 @@ +/*************************************************************************** + + gapplication.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GAPPLICATION_H +#define __GAPPLICATION_H + +typedef + void (*X11_EVENT_FILTER)(XEvent *); + +class gControl; +class gMainWindow; + +class gApplication +{ +public: + static void init(int *argc, char ***argv); + static bool isInit() { return _init; } + static void quit(); + static void exit(); + static bool mustQuit() { return _must_quit; } + + static int controlCount(); + + static gControl* controlItem(int index); + static gControl* controlItem(GtkWidget *wid); + + static void setBusy(bool b); + static bool isBusy() { return _busy; } + + static gControl* activeControl() { return _active_control; } + static gControl* previousControl() { return _previous_control; } + static void setActiveControl(gControl *control, bool on); + static void handleFocusNow(); + + static void suspendEvents(bool vl); + static void enableEvents(); + static bool userEvents(); + static bool allEvents(); + + static void enableTooltips(bool vl); + static bool areTooltipsEnabled(); + + static int dblClickTime(); + + static void setDefaultTitle(const char *title); + static char *defaultTitle() { return _title; } + + static void setDirty(); + static int loopLevel() { return _loopLevel; } + static void enterLoop(void *owner, bool showIt = false, GtkWindow *modal = NULL); + static void enterPopup(gMainWindow *owner); + static void exitLoop(void *owner); + static bool hasLoop(void *owner) { return _loop_owner == owner; } + static GtkWindowGroup *enterGroup(); + static void exitGroup(GtkWindowGroup *oldGroup); + static guint32 lastEventTime() { return _event_time; } + static GdkEvent *lastEvent() { return _event; } + static void updateLastEvent(GdkEvent *e); + static void updateLastEventTime(); + + static bool (*onKeyEvent)(int type); + + static int getScrollbarSize(); + static int getScrollbarSpacing(); + static int getFrameWidth(); + static int getInnerWidth(); + static void getBoxFrame(int *w, int *h); + static char *getStyleName(); + + static void grabPopup(); + static void ungrabPopup(); + + static void setMainWindow(gMainWindow *win); + static gMainWindow *mainWindow() { return _main_window; } + + static void checkHoveredControl(gControl *control); + + static void setEventFilter(X11_EVENT_FILTER filter); + + static void setButtonGrab(gControl *grab) { _button_grab = grab; } + + static bool fix_breeze; + static bool fix_oxygen; + + //"Private" + static bool _init; + static bool _busy; + static bool _must_quit; + static char *_title; + static int _loopLevel; + static int _in_popup; + static GtkWidget *_popup_grab; + static void *_loop_owner; + static GtkWindowGroup *_group; + static GtkWindowGroup *currentGroup(); + //static void dispatchEnterLeave(gControl *enter); + static gControl *_enter; + static gControl *_leave; + static gControl *_ignore_until_next_enter; + static gControl *_active_control; + static gControl *_previous_control; + static gControl *_old_active_control; + static gControl *_button_grab; + static gControl *_enter_after_button_grab; + static gControl *_control_grab; + static guint32 _event_time; + static GdkEvent *_event; + static gMainWindow *_main_window; + static bool _close_next_window; + static bool _fix_printer_dialog; + static void (*onEnterEventLoop)(); + static void (*onLeaveEventLoop)(); +}; + +#endif diff --git a/gb.gtk/src/gb.gtk.component b/gb.gtk/src/gb.gtk.component new file mode 100644 index 00000000..0349774f --- /dev/null +++ b/gb.gtk/src/gb.gtk.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.gtk +Author=Daniel Campos Fernández,Benoît Minisini +Implements=Form,EventLoop,ImageIO +Requires=gb.image +Type=Form diff --git a/gb.gtk/src/gb.gtk.h b/gb.gtk/src/gb.gtk.h new file mode 100644 index 00000000..e7a1988d --- /dev/null +++ b/gb.gtk/src/gb.gtk.h @@ -0,0 +1,64 @@ +/*************************************************************************** + + gb.gtk.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef GB_GTK_H +#define GB_GTK_H + +#include "gambas.h" +#include +#include + +#ifndef GDK_WINDOWING_X11 +#define NO_X_WINDOW 1 +#endif + +#define GTK_INTERFACE_VERSION 1 + +#ifdef GTK3 +#define GTK_NAME "gb.gtk3" +#else +#define GTK_NAME "gb.gtk" +#endif + +typedef + struct + { + intptr_t version; + GtkWidget *(*CreateGLArea)(void *control, void *parent, void (*init)(GtkWidget *)); + void *_null; + } + GTK_INTERFACE; + +typedef + struct { + GB_BASE ob; + void *widget; + GB_VARIANT_VALUE tag; + void *font; + void *cursor; + char *popup; + char *action; + } + GTK_CONTROL; + +#endif diff --git a/gb.gtk/src/gbutton.cpp b/gb.gtk/src/gbutton.cpp new file mode 100644 index 00000000..b653de3c --- /dev/null +++ b/gb.gtk/src/gbutton.cpp @@ -0,0 +1,803 @@ +/*************************************************************************** + + gbutton.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gapplication.h" +#include "gmainwindow.h" +#include "gbutton.h" + +#include + +static void cb_click(GtkButton *object, gButton *data) +{ + if (data->disable) + { + data->disable=false; + return; + } + + if (!gApplication::userEvents()) return; + + data->unsetOtherRadioButtons(); + + if (data->type == gButton::Tool) + { + if (!data->isToggle()) + { + data->disable = true; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->widget), false); + } + } + + data->emit(SIGNAL(data->onClick)); +} + +static void cb_click_radio(GtkButton *object,gControl *data) +{ + if (!gApplication::userEvents()) return; + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(object))) + if (((gButton*)data)->onClick) ((gButton*)data)->onClick((gControl*)data); + return; +} + +static void cb_click_check(GtkButton *object, gButton *data) +{ + if (data->isTristate() && !data->locked()) + { + data->lock(); + if (data->inconsistent()) + { + data->setInconsistent(false); + data->setValue(false); + } + else if (!data->value()) + data->setInconsistent(true); + data->unlock(); + } + + data->emit(SIGNAL(data->onClick)); +} + +#ifdef GTK3 +static gboolean button_draw(GtkWidget *wid, cairo_t *cr, gButton *data) +{ + GdkPixbuf *img; + GdkRectangle rpix={0,0,0,0}; + GdkRectangle rect; + GtkCellRendererState state; + gint py, px; + bool rtl, bcenter=false; + gint dx, dy; + GtkStateFlags f; + + rtl = gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL; + + rect.x = rect.y = 0; + rect.width = data->width(); + rect.height = data->height(); + + px = rect.width; + + if (gtk_widget_get_state_flags(data->widget) & GTK_STATE_FLAG_ACTIVE) + { + gtk_widget_style_get (wid, + "child-displacement-x", &dx, + "child-displacement-y", &dy, + (void *)NULL); + rect.x += dx; + rect.y += dy; + } + + //g_debug("button_expose: %d %d %d %d", e->area.x, e->area.y, e->area.width, e->area.height); + //g_debug("rect: %d %d %d %d", rect.x, rect.y, rect.width, rect.height); + + if (data->rendpix) + { + if (gtk_widget_get_state_flags(data->widget) & GTK_STATE_FLAG_INSENSITIVE) + { + if (!data->rendinc) + data->rendinc = gt_pixbuf_create_disabled(data->rendpix); + img = data->rendinc; + } + else + img = data->rendpix; + + rpix.width = gdk_pixbuf_get_width(img); + rpix.height = gdk_pixbuf_get_height(img); + + py = (rect.height - rpix.height)/2; + + bcenter = !(data->text()) || !(*data->text()); + + if (bcenter) + { + //fprintf(stderr, "draw pixbuf: %d %d\n", rect.x + (px-rpix.width)/2, rect.y + py); + //gdk_draw_pixbuf(GDK_DRAWABLE(win),gc,img,0,0,rect.x + (px-rpix.width)/2, rect.y + py, + // -1,-1,GDK_RGB_DITHER_MAX,0,0); + + gt_cairo_draw_pixbuf(cr, img, rect.x + (px - rpix.width) / 2, rect.y + py, -1, -1, 1.0, NULL); + return false; + } + + if (rtl) + gt_cairo_draw_pixbuf(cr, img, rect.x + rect.width - 6, rect.y + py, -1, -1, 1.0, NULL); + else + gt_cairo_draw_pixbuf(cr, img, rect.x + 6, rect.y + py, -1, -1, 1.0, NULL); + + rect.width -= rpix.width; + rect.x += rpix.width; + } + + gt_set_cell_renderer_text_from_font((GtkCellRendererText *)data->rendtxt, data->font()); + g_object_set(G_OBJECT(data->rendtxt), "sensitive", true, (void *)NULL); + + + f = gtk_widget_get_state_flags(data->widget); + + if (f & GTK_STATE_INSENSITIVE) + { + state = GTK_CELL_RENDERER_INSENSITIVE; + g_object_set(G_OBJECT(data->rendtxt), "sensitive", false, (void *)NULL); + } + /*else if (f & GTK_STATE_SELECTED) + { + state = GTK_CELL_RENDERER_SELECTED; + }*/ + else + state = (GtkCellRendererState)0; + + if (rect.width >= 1 && rect.height >= 1 && data->bufText && *data->bufText) + { + gtk_cell_renderer_set_fixed_size(data->rendtxt, rect.width, rect.height); + gtk_cell_renderer_render(data->rendtxt, cr, wid, &rect, &rect, state); + } + + return FALSE; +} +#else +static gboolean button_expose(GtkWidget *wid,GdkEventExpose *e,gButton *data) +{ + cairo_t *cr; + GdkPixbuf *img; + GdkRectangle rpix={0,0,0,0}; + GdkRectangle rect; + GtkCellRendererState state; + gint py,px; + bool rtl,bcenter=false; + gint dx, dy; + GdkWindow *win; + + + rtl = gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL; + + rect = wid->allocation; + win = wid->window; + + px = rect.width; + + if (GTK_WIDGET_STATE(data->widget) == GTK_STATE_ACTIVE) + { + gtk_widget_style_get (wid, + "child-displacement-x", &dx, + "child-displacement-y", &dy, + (void *)NULL); + rect.x += dx; + rect.y += dy; + } + + //g_debug("button_expose: %d %d %d %d", e->area.x, e->area.y, e->area.width, e->area.height); + //g_debug("rect: %d %d %d %d", rect.x, rect.y, rect.width, rect.height); + + if (data->rendpix) + { + if (GTK_WIDGET_STATE(data->widget)==GTK_STATE_INSENSITIVE) + { + if (!data->rendinc) + data->rendinc = gt_pixbuf_create_disabled(data->rendpix); + img = data->rendinc; + } + else + img = data->rendpix; + + rpix.width = gdk_pixbuf_get_width(img); + rpix.height = gdk_pixbuf_get_height(img); + + py = (rect.height - rpix.height)/2; + + cr = gdk_cairo_create(win); + gdk_cairo_region(cr, e->region); + cairo_clip(cr); + + bcenter = !(data->text()) || !(*data->text()); + + if (bcenter) + { + //fprintf(stderr, "draw pixbuf: %d %d\n", rect.x + (px-rpix.width)/2, rect.y + py); + //gdk_draw_pixbuf(GDK_DRAWABLE(win),gc,img,0,0,rect.x + (px-rpix.width)/2, rect.y + py, + // -1,-1,GDK_RGB_DITHER_MAX,0,0); + + gt_cairo_draw_pixbuf(cr, img, rect.x + (px - rpix.width) / 2, rect.y + py, -1, -1, 1.0, NULL); + + cairo_destroy(cr); + return false; + } + + if (rtl) + gt_cairo_draw_pixbuf(cr, img, rect.x + rect.width - 6, rect.y + py, -1, -1, 1.0, NULL); + else + gt_cairo_draw_pixbuf(cr, img, rect.x + 6, rect.y + py, -1, -1, 1.0, NULL); + + cairo_destroy(cr); + + rect.width -= rpix.width; + rect.x += rpix.width; + } + + gt_set_cell_renderer_text_from_font((GtkCellRendererText *)data->rendtxt, data->font()); + g_object_set(G_OBJECT(data->rendtxt), "sensitive", true, (void *)NULL); + + switch (GTK_WIDGET_STATE(data->widget)) + { + //case GTK_STATE_NORMAL: + //case GTK_STATE_ACTIVE: state=GTK_CELL_RENDERER_PRELIT; break; + //case GTK_STATE_PRELIGHT: state=GTK_CELL_RENDERER_PRELIT; break; + case GTK_STATE_SELECTED: + state = GTK_CELL_RENDERER_SELECTED; + break; + + case GTK_STATE_INSENSITIVE: + state = GTK_CELL_RENDERER_INSENSITIVE; + g_object_set(G_OBJECT(data->rendtxt), "sensitive", false, (void *)NULL); + break; + + default: + state = (GtkCellRendererState)0; + break; + } + + + /*rect.width-=12; + rect.x+=6; + if (rtl) + { + rect.width=px-rect.x-6; + rect.x=6; + }*/ + + if (rect.width >= 1 && rect.height >= 1) + { + gtk_cell_renderer_set_fixed_size(data->rendtxt, rect.width, rect.height); + gtk_cell_renderer_render(data->rendtxt, win, wid, &rect, &rect, &e->area, state); + } + + return FALSE; +} +#endif + +gButton::gButton(gContainer *par, Type typ) : gControl(par) +{ + gContainer *ct; + + g_typ = Type_gButton; + + disable = false; + _toggle = false; + _radio = false; + _animated = false; + _stretch = true; + _tristate = false; + _autoresize = false; + bufText = NULL; + rendtxt = NULL; + rendpix = NULL; + rendinc = NULL; + _label = NULL; + pic = NULL; + shortcut = 0; + + switch(typ) + { + case Check: + border = gtk_check_button_new(); + break; + + case Radio: + ct = parent(); + if (!ct->radiogroup) + { + ct->radiogroup = gtk_radio_button_new(NULL); + g_object_ref(ct->radiogroup); + border = gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(ct->radiogroup)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(border),true); + } + else + { + border = gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(ct->radiogroup)); + } + break; + + case Toggle: + _no_background = true; + rendtxt = gtk_cell_renderer_text_new(); + border = gtk_toggle_button_new(); + break; + + case Tool: + _no_background = true; + rendtxt = gtk_cell_renderer_text_new(); + border = gtk_toggle_button_new(); + gtk_button_set_focus_on_click(GTK_BUTTON(border), false); + break; + + default: + _no_background = true; + border = gtk_button_new(); + rendtxt = gtk_cell_renderer_text_new(); + typ = Button; + break; + } + + widget = border; + + type = typ; + + if (rendtxt) + { + g_object_set(G_OBJECT(rendtxt),"xalign",0.5,(void *)NULL); + g_object_set(G_OBJECT(rendtxt),"yalign",0.5,(void *)NULL); + + ON_DRAW(widget, this, button_expose, button_draw); + } + + realize(); + + gtk_widget_add_events(widget, GDK_POINTER_MOTION_MASK); + onClick = NULL; + + if (type == Radio) + g_signal_connect(G_OBJECT(widget),"clicked",G_CALLBACK(cb_click_radio),(gpointer)this); + else if (type == Check) + g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK(cb_click_check), (gpointer)this); + else + { + g_signal_connect(G_OBJECT(widget),"clicked",G_CALLBACK(cb_click),(gpointer)this); + setColorButton(); + } + + setText(NULL); + + if (type == Tool) + setBorder(false); +} + +gButton::~gButton() +{ + setDefault(false); + setCancel(false); + setPicture(0); + g_free(bufText); +} + +void gButton::setInconsistent(bool vl) +{ + if (type != Check) return; + + gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON(widget),vl); +} + +bool gButton::inconsistent() +{ + gboolean vl=false; + + if (type != Check) return false; + g_object_get (G_OBJECT(widget),"inconsistent",&vl,(void *)NULL); + return vl; +} + +const char* gButton::text() +{ + //if (type == Tool) return this->toolTip(); + return bufText; +} + +void gButton::setText(const char *st) +{ + GtkAccelGroup *accel; + char *buf; + + accel = window()->accel; + + if (bufText) + { + if (shortcut) + gtk_widget_remove_accelerator(widget, accel, (guint)shortcut, GDK_MOD1_MASK); + g_free(bufText); + } + + bufText = st ? g_strdup(st) : NULL; + + if (rendtxt) + { + if (bufText && *bufText) + { + shortcut = (int)gMnemonic_correctMarkup(bufText, &buf); + + if (shortcut) + gtk_widget_add_accelerator(widget, "clicked", accel, (guint)shortcut, GDK_MOD1_MASK, (GtkAccelFlags)0); + + if (rendtxt) + g_object_set(G_OBJECT(rendtxt), "markup", buf, (void *)NULL); + + g_free(buf); + } + else + { + g_object_set(G_OBJECT(rendtxt), "markup", "", (void *)NULL); + } + + refresh(); + } + else + { + if (bufText && *bufText) + { + gMnemonic_correctText((char*)st, &buf); + gtk_button_set_use_underline(GTK_BUTTON(widget), TRUE); + gtk_button_set_label(GTK_BUTTON(widget), buf); + g_free(buf); + } + else + gtk_button_set_label(GTK_BUTTON(widget), ""); + + _label = gtk_bin_get_child(GTK_BIN(widget)); + set_gdk_fg_color(_label, foreground()); + } + + updateFont(); +} + + +gPicture* gButton::picture() +{ + if ( (type == Check) || (type == Radio) ) + return NULL; + + return pic; +} + +void gButton::setPicture(gPicture *npic) +{ + GdkPixbuf *new_rendpix = NULL; + + if ((type == Check) || (type == Radio)) return; + + gPicture::assign(&pic, npic); + + if (pic) + { + new_rendpix = pic->getPixbuf(); + if (new_rendpix) + g_object_ref(new_rendpix); + } + + if (rendpix) { g_object_unref(G_OBJECT(rendpix)); rendpix = NULL; } + if (rendinc) { g_object_unref(G_OBJECT(rendinc)); rendinc = NULL; } + + rendpix = new_rendpix; + + updateSize(); + refresh(); +} + +bool gButton::getBorder() +{ + switch(gtk_button_get_relief(GTK_BUTTON(widget))) + { + case GTK_RELIEF_NORMAL: + case GTK_RELIEF_HALF: + return true; + default: + return false; + } +} + +void gButton::setBorder(bool vl) +{ + gtk_button_set_relief (GTK_BUTTON(widget), vl ? GTK_RELIEF_NORMAL : GTK_RELIEF_NONE); +} + +bool gButton::isDefault() +{ + gMainWindow *win = window(); + return win ? win->_default == this : false; +} + +void gButton::setDefault(bool vl) +{ + gMainWindow *win = window(); + + if (type != Button || !win) + return; + + if (vl) + { + win->_default = this; +#if GTK_CHECK_VERSION(2, 18, 0) + gtk_widget_set_can_default(widget, true); +#else + GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_DEFAULT); +#endif + //gtk_widget_grab_default (widget); + } + else + { +#if GTK_CHECK_VERSION(2, 18, 0) + gtk_widget_set_can_default(widget, false); +#else + GTK_WIDGET_UNSET_FLAGS(widget, GTK_CAN_DEFAULT); +#endif + if (win->_default == this) + win->_default = NULL; + } +} + +bool gButton::isCancel() +{ + gMainWindow *win = window(); + return win ? win->_cancel == this : false; +} + +void gButton::setCancel(bool vl) +{ + gMainWindow *win = window(); + + if (type != Button || !win) + return; + + if (vl) + win->_cancel = this; + else if (win->_cancel == this) + win->_cancel = NULL; +} + +bool gButton::value() +{ + if (type == Button) + return false; + else + return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)); +} + +void gButton::setValue(bool vl) +{ + if (type == Button) + { + if (vl) gtk_button_clicked(GTK_BUTTON(widget)); + } + else + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(widget),vl); +} + +void gButton::setToggle(bool vl) +{ + if (type != Tool) return; + _toggle = vl; +} + +bool gButton::isToggle() +{ + return type == Toggle || type == Check || type == Radio || _toggle; +} + +void gButton::animateClick(bool on) +{ + if (type != Button) return; + + if (!on && !_animated) + { +#ifdef GTK3 + gtk_widget_set_state_flags(widget, GTK_STATE_FLAG_ACTIVE, FALSE); +#else + gtk_widget_set_state(widget, GTK_STATE_ACTIVE); +#endif + refresh(); + _animated = true; + } + else if (on && _animated) + { + _animated = false; +#ifdef GTK3 + gtk_widget_set_state_flags(widget, GTK_STATE_FLAG_NORMAL, FALSE); +#else + gtk_widget_set_state(widget, GTK_STATE_NORMAL); +#endif + gtk_button_clicked(GTK_BUTTON(widget)); + } +} + +int gButton::minimumHeight() +{ + int mh = 0; + + if (bufText && *bufText) + { + if (type == Button || type == Toggle || type == Tool) + mh = font()->height() + 8; + else + mh = font()->height() + 2; + } + + if (pic && (pic->height() > mh)) + mh = pic->height(); + + return mh; +} + +void gButton::setRadio(bool vl) +{ + _radio = vl; + if (value()) + unsetOtherRadioButtons(); +} + +bool gButton::isRadio() +{ + return type == Radio || _radio; +} + +void gButton::unsetOtherRadioButtons() +{ + gContainer *pr = parent(); + gControl *child; + gButton *button; + int i; + + if (type == Radio || type == Button || !isRadio() || !isToggle()) + return; + + for (i = 0; i < pr->childCount(); i++) + { + child = pr->child(i); + if (child->getClass() != getClass()) + continue; + + button = (gButton *)child; + + if (button == this) + { + if (!value()) + { + button->disable = true; + button->setValue(true); + } + } + else if (button->type == type && button->isRadio() && button->isToggle() && button->value()) + { + button->disable = true; + button->setValue(false); + } + } +} + +bool gButton::hasShortcut() +{ + return isDefault() || isCancel() || shortcut; +} + + +void gButton::setStretch(bool vl) +{ + _stretch = vl; +} + +void gButton::setRealForeground(gColor color) +{ + gControl::setRealForeground(color); + +#ifndef GTK3 + if (_label) + set_gdk_fg_color(_label, color); +#endif + + if (rendtxt) + { + if (color == COLOR_DEFAULT) + { + g_object_set(G_OBJECT(rendtxt), + "foreground-set", FALSE, + (void *)NULL); + } + else + { + GdkColor col; + fill_gdk_color(&col, color); + g_object_set(G_OBJECT(rendtxt), + "foreground-set", TRUE, + "foreground-gdk", &col, + (void *)NULL); + } + } +} + +void gButton::setTristate(bool vl) +{ + _tristate = vl; + if (!_tristate) + setInconsistent(false); +} + +void gButton::setAutoResize(bool vl) +{ + _autoresize = vl; + updateSize(); +} + +void gButton::updateSize() +{ + int mw, mh; + + if (!_autoresize) + return; + + mh = minimumHeight(); + mw = 0; + + if (bufText && *bufText) + { + gint m; + + if (type == Check || type == Radio) + { +#ifdef GTK3 + int indicator_size, indicator_spacing, focus_width, focus_pad; + gtk_widget_style_get(widget, + "indicator-size", &indicator_size, + "indicator-spacing", &indicator_spacing, + "focus-line-width", &focus_width, + "focus-padding", &focus_pad, + (char *)NULL); + m = (indicator_size + indicator_spacing * 2 + 2 * (focus_width + focus_pad)) + indicator_spacing + font()->width(bufText, strlen(bufText)); +#else + GtkRequisition req; + g_signal_emit_by_name(border, "size-request", &req); + m = req.width; +#endif + } + else + m = font()->width(bufText, strlen(bufText)) + 16; + + mw += m; + } + + if (pic) + { + if (mw) mw += 8; + mw += pic->width(); + } + + if (mh < height()) + mh = height(); + + resize(mw, mh); +} diff --git a/gb.gtk/src/gbutton.h b/gb.gtk/src/gbutton.h new file mode 100644 index 00000000..fbf43c27 --- /dev/null +++ b/gb.gtk/src/gbutton.h @@ -0,0 +1,100 @@ +/*************************************************************************** + + gbutton.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBUTTON_H +#define __GBUTTON_H + +class gButton : public gControl +{ +public: + + enum Type + { + Button, Toggle, Check, Radio, Tool + }; + + gButton(gContainer *parent, Type type); + ~gButton(); + + bool getBorder(); + bool isCancel(); + bool isDefault(); + const char* text(); + gPicture* picture(); + bool value(); + bool isToggle(); + bool isRadio(); + //bool isEnabled() const; + bool inconsistent(); + bool isStretch() { return _stretch; } + bool isTristate() const { return _tristate; } + bool isAutoResize() const { return _autoresize; } + + //void setEnabled(bool vl); + void setBorder(bool vl); + void setCancel(bool vl); + void setDefault(bool vl); + void setText(const char *st); + void setPicture(gPicture *pic); + void setValue(bool vl); + void setToggle(bool vl); + void setRadio(bool vl); + void setInconsistent(bool vl); + void setStretch(bool vl); + void setTristate(bool vl); + void setAutoResize(bool vl); + + virtual void setRealForeground(gColor color); + //virtual void setRealBackground(gColor color); + +//"Method" + void animateClick(bool on); + +//"Signals" + void (*onClick)(gControl *sender); + +//"Private" + int type; + char *bufText; + GtkWidget *_label; + GtkCellRenderer *rendtxt; + GdkPixbuf *rendpix,*rendinc; + gPicture *pic; + int shortcut; + unsigned disable : 1; + unsigned _toggle : 1; + unsigned _animated : 1; + unsigned _radio : 1; + unsigned _stretch : 1; + unsigned _tristate : 1; + unsigned _autoresize : 1; + + bool hasShortcut(); + void unsetOtherRadioButtons(); + virtual int minimumHeight(); + virtual void updateSize(); + + static bool isButton(gControl *control) { return control->getClass() == Type_gButton && ((gButton *)control)->type == Button; } +}; + +#endif diff --git a/gb.gtk/src/gclipboard.h b/gb.gtk/src/gclipboard.h new file mode 100644 index 00000000..b403603d --- /dev/null +++ b/gb.gtk/src/gclipboard.h @@ -0,0 +1,55 @@ +/*************************************************************************** + + gclipboard.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GCLIPBOARD_H +#define __GCLIPBOARD_H + +class gPicture; + +class gClipboard +{ +public: + enum + { + Nothing = 0, + Text = 1, + Image = 2 + }; + enum { + Clipboard = 0, + Selection = 1 + }; + + static void setCurrent(int clipboard); + static int getCurrent(); + static void clear(); + static char *getFormat(int n = 0); + static int getType(); + static void setText(char *text, int len, char *format = 0); + static char *getText(int *len, const char *format); + static void setImage(gPicture *image); + static gPicture *getImage(); + static bool hasChanged(); +}; + +#endif diff --git a/gb.gtk/src/gcolor.h b/gb.gtk/src/gcolor.h new file mode 100644 index 00000000..b7adcf42 --- /dev/null +++ b/gb.gtk/src/gcolor.h @@ -0,0 +1,32 @@ +/*************************************************************************** + + gcolor.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GCOLOR_H +#define __GCOLOR_H + +#include "gb.image.h" + +typedef + uint gColor; + +#endif diff --git a/gb.gtk/src/gcombobox.cpp b/gb.gtk/src/gcombobox.cpp new file mode 100644 index 00000000..63ef97e2 --- /dev/null +++ b/gb.gtk/src/gcombobox.cpp @@ -0,0 +1,645 @@ +/*************************************************************************** + + gcombobox.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gmainwindow.h" +#include "gdesktop.h" +#include "gcombobox.h" + +/************************************************************************** + + gComboBox + +**************************************************************************/ + +static void cb_click(GtkComboBox *widget,gComboBox *data) +{ + if (data->locked()) + return; + + if (!data->isReadOnly() && data->count()) + { + int index = data->index(); + if (index >= 0) + { + const char *text = index >= 0 ? data->itemText(index) : NULL; + if (!text) text = ""; + data->lock(); + gtk_entry_set_text(GTK_ENTRY(data->entry), text); + data->setIndex(index); + data->unlock(); + data->emit(SIGNAL(data->onChange)); + } + } + + if (data->index() >= 0) + data->emit(SIGNAL(data->onClick)); +} + +/*static int pathToIndex(GtkTreePath *path) +{ + gint *indices; + + if (!path) + return -1; + + indices = gtk_tree_path_get_indices(path); + return indices[0]; +}*/ + +static GtkTreePath *indexToPath(int index) +{ + char buffer[16]; + sprintf(buffer, "%d", index); + return gtk_tree_path_new_from_string(buffer); +} + +static void combo_cell_text(GtkComboBox *combo, GtkCellRenderer *cell, GtkTreeModel *md, GtkTreeIter *iter, gTree *tr) +{ + gTreeRow *row = NULL; + gTreeCell *data; + const char *buf = ""; + char *key; + + key = tr->iterToKey(iter); + if (key) + row = (gTreeRow*)g_hash_table_lookup(tr->datakey, (gpointer)key); + + if (row) + { + data = row->get(0); + if (data) + { + if (data->text()) + buf = data->text(); + } + } + + g_object_set(G_OBJECT(cell), + "text", buf, + (void *)NULL); +} + +static GtkWidget *_find_button; + +static void cb_find_button(GtkWidget *widget, gpointer data) +{ + if (GTK_IS_TOGGLE_BUTTON(widget)) + _find_button = widget; + else if (GTK_IS_CONTAINER(widget)) + gtk_container_forall(GTK_CONTAINER(widget), cb_find_button, NULL); +} + +static GtkWidget *find_button(GtkWidget *combo) +{ + _find_button = NULL; + gtk_container_forall(GTK_CONTAINER(combo), cb_find_button, NULL); + return _find_button; +} + +char *gComboBox::indexToKey(int index) +{ + char *key; + GtkTreePath *path = indexToPath(index); + key = find(path); + gtk_tree_path_free(path); + return key; +} + +void gComboBox::create(bool readOnly) +{ + bool first = !border; + char *save = NULL; + GB_COLOR bg, fg; + GList *cells; + + //fprintf(stderr, "create: %d hasFocus = %d\n", readOnly, focus ); + + lock(); + + if (first) + { +#if GTK3 + border = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); //gtk_event_box_new(); +#else + border = gtk_alignment_new(0, 0, 1, 1); //gtk_event_box_new(); +#endif + } + else + { + save = g_strdup(text()); + bg = background(); + fg = foreground(); + } + + if (widget) + { + if (cell) g_object_unref(cell); + cell = NULL; + gtk_widget_destroy(widget); + _button = NULL; + } + + if (readOnly) + { + widget = gtk_combo_box_new_with_model(GTK_TREE_MODEL(tree->store)); + entry = NULL; + + cell = gtk_cell_renderer_text_new (); + g_object_ref_sink(cell); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), cell, true); + + //gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(widget), cell, "text", 0, (void *)NULL); + g_object_set(cell, "ypad", 0, (void *)NULL); + + gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(widget), cell, (GtkCellLayoutDataFunc)combo_cell_text, (gpointer)tree, NULL); + +#ifdef GTK3 + gtk_widget_set_hexpand(widget, TRUE); +#endif + } + else + { + +#if GTK_CHECK_VERSION(2, 24, 0) + widget = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(tree->store)); +#else + widget = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(tree->store), 0); +#endif + entry = gtk_bin_get_child(GTK_BIN(widget)); + +#ifdef GTK3 + gtk_widget_set_hexpand(entry, TRUE); +#endif + + g_signal_handler_disconnect(widget, g_signal_handler_find(widget, G_SIGNAL_MATCH_ID, g_signal_lookup("changed", G_OBJECT_TYPE(widget)), 0, 0, 0, 0)); + + cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(widget)); + cell = (GtkCellRenderer *)cells->data; + g_list_free(cells); + g_object_ref(cell); + //gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(widget), cell, "text", 0, (void *)NULL); + //g_object_set(cell, "ypad", 0, (void *)NULL); + gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(widget), cell, (GtkCellLayoutDataFunc)combo_cell_text, (gpointer)tree, NULL); + } + +#ifdef GTK3 + gtk_combo_box_set_popup_fixed_width(GTK_COMBO_BOX(widget), true); +#endif + + if (first) + { + realize(false); + } + else + { + gtk_container_add(GTK_CONTAINER(border), widget); + gtk_widget_show(widget); + widgetSignals(); + } + + g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(cb_click), (gpointer)this); + + if (entry) + { + initEntry(); + setColorBase(); + //g_signal_connect(G_OBJECT(entry), "key-press-event", G_CALLBACK(gcb_keypress), (gpointer)this); + //g_signal_connect(G_OBJECT(entry), "key-release-event", G_CALLBACK(gcb_keyrelease), (gpointer)this); + g_signal_connect(G_OBJECT(entry), "focus-in-event", G_CALLBACK(gcb_focus_in), (gpointer)this); + g_signal_connect(G_OBJECT(entry), "focus-out-event", G_CALLBACK(gcb_focus_out), (gpointer)this); + } + else + { + _has_input_method = FALSE; + setColorButton(); + } + + updateFocusHandler(); + + if (!first) + { + setBackground(bg); + setForeground(fg); + updateFont(); + } + + setText(save); + g_free(save); + + unlock(); +} + +gComboBox::gComboBox(gContainer *parent) : gTextBox(parent, true) +{ + onChange = NULL; + onClick = NULL; + onActivate = NULL; + + _no_background = TRUE; + _last_key = 0; + _model_dirty = false; + _model_dirty_timeout = 0; + sort = false; + border = widget = NULL; + entry = NULL; + _button = NULL; + cell = NULL; + _use_wheel = true; + + g_typ = Type_gComboBox; + + tree = new gTree(); + tree->addColumn(); + //tree->addColumn(); + //tree->setHeaders(false); + + create(false); +} + +gComboBox::~gComboBox() +{ + if (_model_dirty_timeout) + g_source_remove(_model_dirty_timeout); + + gtk_combo_box_popdown(GTK_COMBO_BOX(widget)); + + if (cell) g_object_unref(cell); + + delete tree; +} + +void gComboBox::popup() +{ + gtk_combo_box_popup(GTK_COMBO_BOX(widget)); +} + +#ifdef GTK3 +GtkWidget *gComboBox::getStyleSheetWidget() +{ + return entry ? entry : border; +} + +#else +void gComboBox::setRealBackground(gColor color) +{ + gControl::setRealBackground(color); + if (entry) + set_gdk_base_color(entry, color); +} +#endif + +void gComboBox::setRealForeground(gColor color) +{ + gControl::setRealForeground(color); + if (entry) + set_gdk_text_color(entry, color); +} + +int gComboBox::count() +{ + return tree->rowCount(); +} + +int gComboBox::index() +{ + updateModel(); + return gtk_combo_box_get_active (GTK_COMBO_BOX(widget)); +} + +char* gComboBox::itemText(int ind) +{ + gTreeRow *row; + gTreeCell *cell; + + if (ind < 0) + return NULL; + + updateModel(); + + char *key = indexToKey(ind); + + if (!key) return NULL; + row = tree->getRow(key); + if (!row) return NULL; + cell = row->get(0); + if (!cell) return NULL; + return cell->text(); +} + +int gComboBox::length() +{ + gchar *buf; + + if (!entry) + { + buf = itemText(index()); + if (!buf) + return 0; + else + return g_utf8_strlen(buf, -1); + } + else + return gTextBox::length(); +} + +bool gComboBox::isSorted() +{ + return tree->isSorted(); +} + +char* gComboBox::text() +{ + if (entry) + return gTextBox::text(); + else + return itemText(index()); +} + +void gComboBox::setIndex(int vl) +{ + //fprintf(stderr, "setIndex: %d\n", vl); + + if (vl < 0) + vl = -1; + else if (vl >= count()) + return; + + if (vl == index()) + { + emit(SIGNAL(onClick)); + return; + } + + updateModel(); + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), vl); + + if (entry) + gTextBox::setText(itemText(vl)); +} + +void gComboBox::checkIndex() +{ + if (index() < 0) + { + lock(); + setIndex(0); + unlock(); + } +} + +void gComboBox::setItemText(int ind, const char *text) +{ + gTreeRow *row; + gTreeCell *cell; + + char *key = indexToKey(ind); + + if (!key) return; + row = tree->getRow(key); + if (!row) return; + cell = row->get(0); + if (!cell) return; + + cell->setText(text); + + updateSort(); +} + +void gComboBox::setReadOnly(bool vl) +{ + if (isReadOnly() == vl) + return; + create(!isReadOnly()); +} + +void gComboBox::setSorted(bool vl) +{ + tree->setSorted(vl); +} + +void gComboBox::setText(const char *vl) +{ + int index = find(vl); + + if (!entry || index >= 0) + setIndex(index); + else if (entry) + gTextBox::setText(vl); +} + +static gboolean combo_set_model_and_sort(gComboBox *combo) +{ + //fprintf(stderr, "combo_set_model_and_sort\n"); + gtk_combo_box_set_model(GTK_COMBO_BOX(combo->widget), GTK_TREE_MODEL(combo->tree->store)); + + if (combo->isSorted()) + combo->tree->sort(); + + combo->_model_dirty = false; + combo->_model_dirty_timeout = 0; + + if (combo->isReadOnly()) + combo->checkIndex(); + + return FALSE; +} + +void gComboBox::updateModel() +{ + if (_model_dirty) + { + g_source_remove(_model_dirty_timeout); + combo_set_model_and_sort(this); + } +} + +void gComboBox::updateSort() +{ + if (_model_dirty) + return; + + _model_dirty = true; + _model_dirty_timeout = g_timeout_add(0, (GSourceFunc)combo_set_model_and_sort, this); + + gtk_combo_box_set_model(GTK_COMBO_BOX(widget), NULL); +} + +void gComboBox::add(const char *text, int pos) +{ + //GtkTreeModel *model; + gTreeRow *row; + gTreeCell *cell; + + char key[16]; + char *after; + + _last_key++; + sprintf(key, "%d", _last_key); + + if (pos < 0 || pos > count()) + after = NULL; + else + after = indexToKey(pos); + + //g_signal_lookup("rowGTK_TYPE_COMBO_BOX); + + row = tree->addRow(key, after, true); + if (row) + { + cell = row->get(0); + if (cell) + { + cell->setText(text); + updateSort(); + } + } + + //gtk_combo_box_set_model(GTK_COMBO_BOX(widget), model); +} + +void gComboBox::clear() +{ + lock(); + tree->clear(); + unlock(); +} + +int gComboBox::find(const char *text) +{ + int i; + const char *it; + + if (!text) + text = ""; + + for (i = 0; i < count(); i++) + { + it = itemText(i); + if (!it) + it = ""; + if (!strcmp(it, text)) + return i; + } + + return -1; +} + +void gComboBox::remove(int pos) +{ + updateModel(); + tree->removeRow(indexToKey(pos)); + updateSort(); +} + +void gComboBox::resize(int w, int h) +{ + gControl::resize(w,h); +} + +void gComboBox::updateFont() +{ + gControl::updateFont(); + if (cell) + g_object_set(G_OBJECT(cell), "font-desc", font()->desc(), (void *)NULL); +#ifndef GTK3 + else + gtk_widget_modify_font(entry, font()->desc()); +#endif +} + +void gComboBox::setFocus() +{ + gControl::setFocus(); + if (entry && window()->isVisible()) + gtk_widget_grab_focus(entry); +} + +int gComboBox::minimumHeight() +{ + int h; +#ifdef GTK3 + gtk_widget_get_preferred_height(widget, &h, NULL); +#else + GtkRequisition req; + + gtk_widget_size_request(widget, &req); + h = req.height; +#endif + + if (entry) + return h - 4; + else + return h; +} + + +bool gComboBox::isReadOnly() +{ + return entry == NULL; +} + + +static gboolean button_focus_in(GtkWidget *widget, GdkEventFocus *event, gComboBox *control) +{ + if (control->isReadOnly()) + return gcb_focus_in(widget, event, control); + + control->setFocus(); + return false; +} + +static gboolean button_focus_out(GtkWidget *widget, GdkEventFocus *event, gComboBox *control) +{ + if (control->isReadOnly()) + return gcb_focus_out(widget, event, control); + + return false; +} + +void gComboBox::updateFocusHandler() +{ + GtkWidget *button = find_button(widget); + + if (button == _button) + return; + + _button = button; + //g_signal_connect(G_OBJECT(button), "key-press-event", G_CALLBACK(gcb_keypress), (gpointer)this); + //g_signal_connect(G_OBJECT(button), "key-release-event", G_CALLBACK(gcb_keyrelease), (gpointer)this); + g_signal_connect(G_OBJECT(button), "focus-in-event", G_CALLBACK(button_focus_in), (gpointer)this); + g_signal_connect(G_OBJECT(button), "focus-out-event", G_CALLBACK(button_focus_out), (gpointer)this); +} + +bool gComboBox::hasBorder() const +{ + gboolean v; + + g_object_get(G_OBJECT(widget), "has-frame", &v, (void *)NULL); + return v; +} + +void gComboBox::setBorder(bool v) +{ + g_object_set(G_OBJECT(widget), "has-frame", v, (void *)NULL); +} diff --git a/gb.gtk/src/gcombobox.h b/gb.gtk/src/gcombobox.h new file mode 100644 index 00000000..7e81ebd3 --- /dev/null +++ b/gb.gtk/src/gcombobox.h @@ -0,0 +1,94 @@ +/*************************************************************************** + + gcombobox.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GCOMBOBOX_H +#define __GCOMBOBOX_H + +#include "gtextbox.h" +#include "gtree.h" + +class gComboBox : public gTextBox +{ +public: + gComboBox(gContainer *parent); + ~gComboBox(); + + int count(); + int index(); + char* itemText(int ind); + virtual int length(); + //char** list(); + virtual bool isReadOnly(); + bool isSorted(); + virtual char *text(); + + void setIndex(int vl); + void setItemText(int ind, const char *txt); + //void setList(char **vl); + virtual void setReadOnly(bool vl); + void setSorted(bool vl); + virtual void setText(const char *vl); + + bool hasBorder() const; + void setBorder(bool v); + +//"Methods" + void popup(); + void add(const char *vl, int pos = -1); + virtual void clear(); + int find(const char *ptr); + void remove(int pos); + + virtual void resize(int w, int h); +#ifdef GTK3 + virtual GtkWidget *getStyleSheetWidget(); +#else + virtual void setRealBackground(gColor vl); +#endif + virtual void setRealForeground(gColor vl); + virtual void setFocus(); + +//"Signals" + void (*onClick)(gComboBox *sender); + +//"Private" + bool sort; + GtkCellRenderer *cell; + virtual int minimumHeight(); + gTree *tree; + bool _model_dirty; + int _last_key; + GtkWidget *_button; + int _model_dirty_timeout; + + virtual void updateFont(); + void updateModel(); + void updateSort(); + char *indexToKey(int index); + char* find(GtkTreePath *path) { return tree->pathToKey(path, false); } + void checkIndex(); + void updateFocusHandler(); + void create(bool readOnly); +}; + +#endif diff --git a/gb.gtk/src/gcontainer.cpp b/gb.gtk/src/gcontainer.cpp new file mode 100644 index 00000000..6426b552 --- /dev/null +++ b/gb.gtk/src/gcontainer.cpp @@ -0,0 +1,842 @@ +/*************************************************************************** + + gcontainer.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include +#include "widgets.h" + +#include +#include + +#include "gapplication.h" +#include "gdesktop.h" +#include "gmainwindow.h" +#include "gcontainer.h" + +static gControl* get_next_child_widget (gContainer *gtk_control, int *gtk_list, int gtk_count) +{ + gControl *ctrl; + + while ( (*gtk_list) < gtk_count ) + { + ctrl = gtk_control->child(*gtk_list); + //fprintf(stderr, "get_next_child_widget: %d: %p: %s\n", *gtk_list, ctrl, ctrl->name()); + (*gtk_list)++; + + if (!ctrl->border || !ctrl->widget || !ctrl->isVisible()) + continue; + + return ctrl; + } + + //fprintf(stderr, "get_next_child_widget: ==> NULL\n"); + return NULL; +} + +static void cb_arrange(gContainer *sender) +{ + if (sender->onArrange) + (*(sender->onArrange))(sender); +} + +static void cb_before_arrange(gContainer *sender) +{ + if (sender->onBeforeArrange) + (*(sender->onBeforeArrange))(sender); +} + +static void resize_container(gControl *cont, int w, int h) +{ + if (w >= 0 && h >= 0) + cont->resize(w, h); +} + + +#define WIDGET_TYPE gControl* +#define CONTAINER_TYPE gContainer* +#define ARRANGEMENT_TYPE gContainerArrangement* +#define IS_RIGHT_TO_LEFT() gDesktop::rightToLeft() +#define GET_WIDGET(_object) _object +#define GET_CONTAINER(_object) _object +#define GET_ARRANGEMENT(_object) (((gContainer*)_object)->getArrangement()) +#define IS_EXPAND(_object) (((gControl*)_object)->expand()) +#define IS_IGNORE(_object) (((gControl*)_object)->ignore()) +#define IS_DESIGN(_object) (((gControl*)_object)->design()) +#define IS_WIDGET_VISIBLE(_widget) (((gControl*)_widget)->isVisible()) + +#define CAN_ARRANGE(_object) (IS_WIDGET_VISIBLE(GET_WIDGET(_object)) || (((gControl *)_object)->isTopLevel() && ((gMainWindow *)_object)->opened)) + +// BM: ClientX() & ClientY() are relative to the border. +// We need X & Y relative to the container widget. + +#define GET_WIDGET_CONTENTS(_widget, _x, _y, _w, _h) _x=((gContainer*)_widget)->containerX(); \ + _y=((gContainer*)_widget)->containerY(); \ + _w=((gContainer*)_widget)->containerWidth(); \ + _h=((gContainer*)_widget)->containerHeight() + +#define GET_WIDGET_X(_widget) (((gControl*)_widget)->left()) +#define GET_WIDGET_Y(_widget) (((gControl*)_widget)->top()) +#define GET_WIDGET_W(_widget) (((gControl*)_widget)->width()) +#define GET_WIDGET_H(_widget) (((gControl*)_widget)->height()) +#define MOVE_WIDGET(_object, _widget, _x, _y) (((gControl*)_widget)->move( _x, _y)) +#define RESIZE_WIDGET(_object, _widget, _w, _h) (((gControl*)_widget)->resize( _w, _h)) +#define MOVE_RESIZE_WIDGET(_object, _widget, _x, _y, _w, _h) (((gControl*)_widget)->moveResize( _x, _y, _w, _h)) +#define RESIZE_CONTAINER(_widget, _cont, _w, _h) resize_container((gControl *)(_cont), _w, _h) + +#define INIT_CHECK_CHILDREN_LIST(_widget) \ + gContainer *gtk_control=(gContainer*)_widget; \ + int gtk_list=0; \ + int gtk_count = gtk_control->childCount(); + +#define HAS_CHILDREN() (gtk_count != 0) + +#define RESET_CHILDREN_LIST() gtk_list=0; + +#define GET_NEXT_CHILD_WIDGET() get_next_child_widget (gtk_control,>k_list,gtk_count) + +#define GET_OBJECT_FROM_WIDGET(_widget) ((void*)_widget) + +#define GET_OBJECT_NAME(_object) (((gControl *)_object)->name()) + +#define RAISE_ARRANGE_EVENT(_object) cb_arrange((gContainer *)_object); +#define RAISE_BEFORE_ARRANGE_EVENT(_object) cb_before_arrange((gContainer *)_object); + +#define DESKTOP_SCALE gDesktop::scale() + +#define FUNCTION_NAME arrangeContainer + +#include "gb.form.arrangement.h" + +int gContainer::_arrangement_level = 0; + +void gContainer::performArrange() +{ + if (_no_arrangement) + { + _did_arrangement = true; + return; + } + + if (!gApplication::allEvents()) return; + + _did_arrangement = false; + + arrangeContainer((void*)this); +} + + +static int _max_w, _max_h; +static int _gms_x, _gms_y, _gms_w, _gms_h; + +static void gms_move_widget(gControl *wid, int x, int y) +{ + int w = x + wid->width(); + int h = y + wid->height(); + + if (w > _max_w) _max_w = w; + if (h > _max_h) _max_h = h; +} + +static void gms_move_resize_widget(gControl *wid, int x, int y, int w, int h) +{ + w += x; + h += y; + + if (w > _max_w) _max_w = w; + if (h > _max_h) _max_h = h; +} + +#undef MOVE_WIDGET +#define MOVE_WIDGET(_object, _widget, _x, _y) gms_move_widget(_widget, _x, _y) +#undef RESIZE_WIDGET +#define RESIZE_WIDGET(_object, _widget, _w, _h) (0) +#undef MOVE_RESIZE_WIDGET +#define MOVE_RESIZE_WIDGET(_object, _widget, _x, _y, _w, _h) gms_move_resize_widget(_widget, _x, _y, _w, _h) +#undef RAISE_BEFORE_ARRANGE_EVENT +#define RAISE_BEFORE_ARRANGE_EVENT(_object) (0) +#undef RAISE_ARRANGE_EVENT +#define RAISE_ARRANGE_EVENT(_object) (0) +#undef FUNCTION_NAME +#define FUNCTION_NAME get_max_size +#undef RESIZE_CONTAINER +#define RESIZE_CONTAINER(_object, _cont, _w, _h) (0) +#undef GET_WIDGET_CONTENTS +#define GET_WIDGET_CONTENTS(_widget, _x, _y, _w, _h) \ + _x = _gms_x; \ + _y = _gms_y; \ + _w = _gms_w; \ + _h = _gms_h; + +//#undef IS_WIDGET_VISIBLE +//#define IS_WIDGET_VISIBLE(_cont) (1) +#define GET_MAX_SIZE + +#include "gb.form.arrangement.h" + +void gContainer::getMaxSize(int xc, int yc, int wc, int hc, int *w, int *h) +{ + int add; + gContainerArrangement *arr = getArrangement(); + bool locked; + + locked = arr->locked; + arr->locked = false; + + _max_w = 0; + _max_h = 0; + _gms_x = xc; + _gms_y = yc; + _gms_w = wc; + _gms_h = hc; + get_max_size((void *)this); + + if (arr->margin) + add = arr->padding ? arr->padding : gDesktop::scale(); + else if (!arr->spacing) + add = arr->padding; + else + add = 0; + + *w = _max_w + add; + *h = _max_h + add; + + arr->locked = locked; +} + + +void gContainer::initialize() +{ + _children = g_ptr_array_new(); + + radiogroup = NULL; + onArrange = NULL; + onBeforeArrange = NULL; + _proxyContainer = NULL; + _proxyContainerFor = NULL; + _client_x = -1; + _client_y = -1; + _client_w = 0; + _client_h = 0; + _no_arrangement = 0; + //onInsert = NULL; + + arrangement.mode = 0; + arrangement.spacing = 0; + arrangement.padding = 0; + arrangement.autoresize = false; + arrangement.locked = false; + arrangement.user = false; + arrangement.margin = false; + arrangement.indent = 0; + arrangement.invert = false; +} + +gContainer::gContainer() +{ + //g_print("gContainer() %d\n",this); + initialize(); +} + +gContainer::gContainer(gContainer *parent) : gControl(parent) +{ + //g_print("gContainer(parent) %d par: %d\n",this,parent); + initialize(); +} + +gContainer::~gContainer() +{ + int i; + + for (i = 0; i < childCount(); i++) + child(i)->removeParent(); + + g_ptr_array_unref(_children); + + if (radiogroup) { g_object_unref(G_OBJECT(radiogroup)); radiogroup=NULL; } +} + +int gContainer::childCount() const +{ + return _children->len; +} + +gControl* gContainer::child(int index) const +{ + if (index < 0 || index >= (int)_children->len) + return NULL; + else + return (gControl *)g_ptr_array_index(_children, index); +} + +int gContainer::childIndex(gControl *ch) const +{ + int i; + + for (i = 0; i < childCount(); i++) + { + if (child(i) == ch) + return i; + } + + return -1; +} + +void gContainer::setArrange(int vl) +{ + switch(vl) + { + case ARRANGE_NONE: + case ARRANGE_HORIZONTAL: + case ARRANGE_VERTICAL: + case ARRANGE_LEFT_RIGHT: + case ARRANGE_TOP_BOTTOM: + case ARRANGE_FILL: + if (vl != arrangement.mode) + { + arrangement.mode = vl; + updateScrollBar(); + performArrange(); + } + default: + break; + } +} + +void gContainer::setPadding(int vl) +{ + if (vl >= 0 && vl <= 255 && vl != arrangement.padding) + { + arrangement.padding = vl; + performArrange(); + } +} + +void gContainer::setSpacing(bool vl) +{ + if (vl != arrangement.spacing) + { + arrangement.spacing = vl; + performArrange(); + } +} + +void gContainer::setMargin(bool vl) +{ + if (vl != arrangement.margin) + { + arrangement.margin = vl; + performArrange(); + } +} + +void gContainer::setIndent(int vl) +{ + if (vl < 0) + vl = 1; + if (vl >= 0 && vl <= 7 && vl != arrangement.indent) + { + arrangement.indent = vl; + performArrange(); + } +} + +void gContainer::setAutoResize(bool vl) +{ + if (vl != arrangement.autoresize) + { + arrangement.autoresize = vl; + performArrange(); + } +} + +void gContainer::setUser(bool vl) +{ + if (vl != arrangement.user) + { + arrangement.user = vl; + performArrange(); + } +} + +void gContainer::setInvert(bool vl) +{ + if (vl != arrangement.invert) + { + arrangement.invert = vl; + performArrange(); + } +} + +int gContainer::clientX() +{ + gint xc, yc; + GtkWidget *cont = getContainer(); + + if (_client_x >= 0) + return _client_x; + + + if (!_scroll && gtk_widget_get_window(cont) && gtk_widget_get_window(border)) + { + gtk_widget_translate_coordinates(cont, border, 0, 0, &xc, &yc); + xc += containerX(); + } + else + xc = getFrameWidth(); + + return xc; +} + +int gContainer::containerX() +{ + GtkWidget *cont = getContainer(); + + if (cont == widget && widget == frame) + return getFrameWidth(); + else + return 0; +} + +int gContainer::clientY() +{ + gint xc, yc; + GtkWidget *cont = getContainer(); + + if (_client_y >= 0) + return _client_y; + + if (!_scroll && gtk_widget_get_window(cont) && gtk_widget_get_window(border)) + { + gtk_widget_translate_coordinates(cont, border, 0, 0, &xc, &yc); + yc += containerY(); + } + else + yc = getFrameWidth(); + + return yc; +} + +#if 0 +int gContainer::clientY() +{ + gint xc, yc; + GtkWidget *cont = getContainer(); + + if (!cont->window || !border->window || cont == widget) + return getFrameWidth(); + +// if (width() != border->allocation.width || height() != border->allocation.height) +// { +// updateGeometry(); +// GtkAllocation a = { x(), y(), width(), height() }; +// gtk_widget_size_allocate(widget, &a); +// } + + gtk_widget_translate_coordinates(cont, border, 0, 0, &xc, &yc); + if (cont == widget) + yc += getFrameWidth(); + + return yc; // + getFrameWidth(); +} +#endif + +int gContainer::containerY() +{ + GtkWidget *cont = getContainer(); + + if (cont == widget && widget == frame) + return getFrameWidth(); + else + return 0; +} + +int gContainer::clientWidth() +{ + GtkWidget *cont = getContainer(); + + if (_client_w > 0) + return _client_w; + + if (cont != widget && gtk_widget_get_window(cont)) + { + GtkAllocation a; + + gtk_widget_get_allocation(widget, &a); + + if ((width() != a.width || height() != a.height) + && a.width > 0 && a.height > 0) + { + //g_debug("clientWidth: %s: %d", name(), width()); + a.x = x(); a.y = y(); a.width = width(); a.height = height(); + gt_disable_warnings(true); + gtk_widget_size_allocate(widget, &a); + gt_disable_warnings(false); + } + //g_debug("ClientWidth: %s -> %d", this->name(), cont->allocation.width); + + gtk_widget_get_allocation(cont, &a); + + if (a.width > 0) + return a.width; + } + + if (_scroll) + return (int)gtk_adjustment_get_page_size(gtk_scrolled_window_get_hadjustment(_scroll)); + + return width() - getFrameWidth() * 2; +} + +int gContainer::containerWidth() +{ + return clientWidth(); +} + +int gContainer::clientHeight() +{ + GtkWidget *cont = getContainer(); + + if (_client_h > 0) + return _client_h; + + if (cont != widget && gtk_widget_get_window(cont)) + { + GtkAllocation a; + + gtk_widget_get_allocation(widget, &a); + + if ((width() != a.width || height() != a.height) + && a.width > 0 && a.height > 0) + { + //g_debug("clientHeight: %s: %d", name(), height()); + a.x = x(); a.y = y(); a.width = width(); a.height = height(); + //gt_disable_warnings(true); + gtk_widget_size_allocate(widget, &a); + //gt_disable_warnings(false); + //gtk_container_resize_children(GTK_CONTAINER(widget)); + } + //g_debug("ClientHeight: %s -> %d", this->name(), cont->allocation.height); + gtk_widget_get_allocation(cont, &a); + + if (a.height > 0) + return a.height; + } + + if (_scroll) + return (int)gtk_adjustment_get_page_size(gtk_scrolled_window_get_vadjustment(_scroll)); + + return height() - getFrameWidth() * 2; +} + +int gContainer::containerHeight() +{ + return clientHeight(); +} + +void gContainer::insert(gControl *child, bool realize) +{ + //fprintf(stderr, "insert %s into %s\n", child->name(), name()); + + if (!gtk_widget_get_parent(child->border)) + { + //gtk_layout_put(GTK_LAYOUT(getContainer()), child->border, 0, 0); + gtk_container_add(GTK_CONTAINER(getContainer()), child->border); + } + child->bufX = child->bufY = 0; + + g_ptr_array_add(_children, child); + + if (realize) + child->visible = true; + + //g_debug("gContainer::insert: visible = %d", isReallyVisible()); + performArrange(); + //fprintf(stderr, "--> %d %d %d %d\n", child->x(), child->y(), child->width(), child->height()); + updateFocusChain(); + + if (realize) + { + gtk_widget_realize(child->border); + gtk_widget_show_all(child->border); + } + +#ifndef GTK3 + if (hasBackground() && !child->_bg_set) child->setBackground(); + if (hasForeground() && !child->_fg_set) child->setForeground(); +#endif + child->updateFont(); +} + +void gContainer::remove(gControl *child) +{ + g_ptr_array_remove(_children, child); + updateFocusChain(); +} + + +gControl *gContainer::find(int x, int y) +{ + int i; + gControl *ch; + + //fprintf(stderr, "gContainer::find: %s (C %d %d %d %d) (S %d %d): %d %d\n", name(), clientX(), clientY(), clientWidth(), clientHeight(), scrollX(), scrollY(), x, y); + + x -= clientX(); + y -= clientY(); + + if (gApplication::_button_grab != this) + { + if (x < 0 || y < 0 || x >= clientWidth() || y >= clientHeight()) + return NULL; + } + + if (_scroll) + { + x += scrollX(); + y += scrollY(); + } + + for (i = childCount() - 1; i >= 0; i--) + { + ch = child(i); + //fprintf(stderr, "test: %s %d: %d %d %d %d / %d %d\n", ch->name(), ch->isVisible(), ch->x(), ch->y(), ch->width(), ch->height(), x, y); + if (ch->isVisible() && x >= ch->left() && y >= ch->top() && x < (ch->left() + ch->width()) && y < (ch->top() + ch->height())) + { + //fprintf(stderr, "--> %s\n", ch->name()); + return ch; + } + } + + //fprintf(stderr, "--> NULL\n"); + return NULL; +} + + +#ifndef GTK3 +void gContainer::setBackground(gColor color) +{ + int i; + gControl *ch; + + gControl::setBackground(color); + + for (i = 0; i < childCount(); i++) + { + ch = gContainer::child(i); + if (!ch->_bg_set) + ch->setBackground(); + } +} +#endif + +#ifdef GTK3 +void gContainer::updateColor() +{ + int i; + + for (i = 0; i < childCount(); i++) + gContainer::child(i)->updateColor(); +} +#endif + +void gContainer::setForeground(gColor color) +{ + int i; + gControl *ch; + + gControl::setForeground(color); + + for (i = 0; i < childCount(); i++) + { + ch = gContainer::child(i); + if (!ch->_fg_set) + ch->setForeground(); + } +} + +GtkWidget *gContainer::getContainer() +{ + return widget; +} + +gControl *gContainer::findFirstFocus() +{ + int i; + gControl *ch; + + for (i = 0; i < childCount(); i++) + { + ch = child(i); + if (ch->isContainer()) + { + ch = ((gContainer *)ch)->findFirstFocus(); + if (ch) + return ch; + } + else + { + if (gtk_widget_get_can_focus(ch->widget) && !((ch->getClass() == Type_gButton) && ((gButton *)ch)->hasShortcut())) + return ch; + } + } + + return NULL; +} + +void gContainer::resize(int w, int h) +{ + if (w == bufW && h == bufH) + return; + + _client_w = 0; + _client_h = 0; + + gControl::resize(w, h); + performArrange(); +} + +void gContainer::setVisible(bool vl) +{ + bool arr; + + if (vl == isVisible()) + return; + + arr = vl && !isVisible(); + + gControl::setVisible(vl); + + if (arr) + performArrange(); +} + +void gContainer::updateFocusChain() +{ + GList *chain = NULL; + int i; + gControl *ch; + + for (i = 0; i < childCount(); i++) + { + ch = child(i); + if (ch->isNoTabFocus()) + continue; + chain = g_list_prepend(chain, ch->border); + } + + chain = g_list_reverse(chain); + + gtk_container_set_focus_chain(GTK_CONTAINER(widget), chain); + + g_list_free(chain); +} + +void gContainer::updateFont() +{ + int i; + + gControl::updateFont(); + + for (i = 0; i < childCount(); i++) + child(i)->updateFont(); +} + +void gContainer::moveChild(gControl *child, int x, int y) +{ + GtkWidget *cont = gtk_widget_get_parent(child->border); //getContainer(); + + if (GTK_IS_LAYOUT(cont)) + gtk_layout_move(GTK_LAYOUT(cont), child->border, x, y); + else + gtk_fixed_move(GTK_FIXED(cont), child->border, x, y); +} + +bool gContainer::hasBackground() const +{ + return _bg_set || (parent() && parent()->hasBackground()); +} + +bool gContainer::hasForeground() const +{ + return _fg_set || (parent() && parent()->hasForeground()); +} + +void gContainer::setFullArrangement(gContainerArrangement *arr) +{ + bool locked = arrangement.locked; + + arrangement = *arr; + arrangement.locked = locked; + performArrange(); +} + +void gContainer::disableArrangement() +{ + if (_no_arrangement == 0) + _did_arrangement = false; + + _no_arrangement++; +} + +void gContainer::enableArrangement() +{ + _no_arrangement--; + if (_no_arrangement == 0 && _did_arrangement) + performArrange(); +} + +void gContainer::hideHiddenChildren() +{ + int i; + gControl *child; + + for (i = 0;; i++) + { + child = gContainer::child(i); + if (!child) + break; + if (!child->isVisible()) + gtk_widget_hide(child->border); + else if (child->isContainer()) + ((gContainer *)child)->hideHiddenChildren(); + } +} + +void gContainer::reparent(gContainer *newpr, int x, int y) +{ + gControl::reparent(newpr, x, y); + hideHiddenChildren(); +} + + +void gContainer::clear() +{ + gContainer *cont = proxyContainer(); + gControl *ch; + + for(;;) + { + ch = cont->child(0); + if (!ch) + break; + ch->destroy(); + } +} diff --git a/gb.gtk/src/gcontainer.h b/gb.gtk/src/gcontainer.h new file mode 100644 index 00000000..aaf0fecd --- /dev/null +++ b/gb.gtk/src/gcontainer.h @@ -0,0 +1,147 @@ +/*************************************************************************** + + gcontainer.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GCONTAINER_H +#define __GCONTAINER_H + +#include "gcontrol.h" + +struct gContainerArrangement +{ + unsigned mode : 4; + unsigned user : 1; + unsigned locked : 1; + unsigned margin : 1; + unsigned spacing : 1; + unsigned padding : 8; + unsigned indent : 4; + unsigned dirty : 1; + unsigned autoresize : 1; + unsigned invert : 1; + unsigned _reserved: 9; +}; + +class gContainer : public gControl +{ +public: + gContainer(); + gContainer(gContainer *parent); + ~gContainer(); + + int arrange() const { return arrangement.mode; } + bool autoResize() const { return arrangement.autoresize; } + bool isUser() const { return arrangement.user; } + int padding() const { return arrangement.padding; } + bool spacing() const { return arrangement.spacing; } + bool margin() const { return arrangement.margin; } + int indent() const { return arrangement.indent; } + bool invert() const { return arrangement.invert; } + + virtual int clientWidth(); + virtual int clientHeight(); + virtual int clientX(); + virtual int clientY(); + virtual int containerX(); + virtual int containerY(); + virtual int containerWidth(); + virtual int containerHeight(); + + void setArrange(int vl); + void setUser(bool vl); + void setAutoResize(bool vl); + void setPadding(int vl); + void setSpacing(bool vl); + void setMargin(bool vl); + void setIndent(int vl); + void setInvert(bool vl); + + virtual int childCount() const; + virtual gControl *child(int index) const; + + int childIndex(gControl *ch) const; + + void clear(); + + virtual gControl *find(int x, int y); + + gContainerArrangement *getArrangement() { return &arrangement; } + gContainerArrangement fullArrangement() { return arrangement; } + void setFullArrangement(gContainerArrangement *arr); + + virtual void performArrange(); + void getMaxSize(int xc, int yc, int wc, int hc, int *w, int *h); + +#ifndef GTK3 + virtual void setBackground(gColor color = COLOR_DEFAULT); +#else + virtual void updateColor(); +#endif + virtual void setForeground(gColor color = COLOR_DEFAULT); + virtual void updateFont(); + + bool hasBackground() const; + bool hasForeground() const; + + virtual void resize(int w, int h); + + virtual void setVisible(bool vl); + + gContainer *proxyContainer() { return _proxyContainer ? _proxyContainer : this; } + void setProxyContainer(gContainer *proxy) { if (_proxyContainer != this) _proxyContainer = proxy; else _proxyContainer = NULL; } + gContainer *proxyContainerFor() { return _proxyContainerFor; } + void setProxyContainerFor(gContainer *proxy) { if (proxy != this) _proxyContainerFor = proxy; else _proxyContainerFor = NULL; } + + void disableArrangement(); + void enableArrangement(); + +//"Signals" + void (*onArrange)(gContainer *sender); + void (*onBeforeArrange)(gContainer *sender); + //void (*onInsert)(gContainer *sender, gControl *child); + +//"Private" + GtkWidget *radiogroup; + GPtrArray *_children; + int _client_x, _client_y, _client_w, _client_h; + + virtual void insert(gControl *child, bool realize = false); + virtual void remove(gControl *child); + virtual void moveChild(gControl *child, int x, int y); + virtual void reparent(gContainer *newpr, int x, int y); + void hideHiddenChildren(); + virtual GtkWidget *getContainer(); + gControl *findFirstFocus(); + void updateFocusChain(); + + static int _arrangement_level; + +private: + void initialize(); + gContainerArrangement arrangement; + gContainer *_proxyContainer; + gContainer *_proxyContainerFor; + unsigned _did_arrangement : 1; + unsigned _no_arrangement : 7; +}; + +#endif diff --git a/gb.gtk/src/gcontrol.cpp b/gb.gtk/src/gcontrol.cpp new file mode 100644 index 00000000..ce670915 --- /dev/null +++ b/gb.gtk/src/gcontrol.cpp @@ -0,0 +1,2794 @@ +/*************************************************************************** + + gcontrol.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include + +#ifndef GAMBAS_DIRECTFB +#ifdef GDK_WINDOWING_X11 +#include +#include +#include +#include +#include +#include +#endif +#endif + +#include "widgets.h" +#include "gapplication.h" +#include "gbutton.h" +#include "gdrawingarea.h" +#include "gmainwindow.h" +#include "gmoviebox.h" +#include "gplugin.h" +#include "gscrollbar.h" +#include "gslider.h" +#include "gdesktop.h" +#include "gdrag.h" +#include "gmouse.h" +#include "gcontrol.h" + +static GList *controls = NULL; +static GList *controls_destroyed = NULL; + +#ifdef GTK3 + +typedef + struct { + void (*get_preferred_height)(GtkWidget *widget, gint *minimum_height, gint *natural_height); + void (*get_preferred_width_for_height)(GtkWidget *widget, gint height, gint *minimum_width, gint *natural_width); + void (*get_preferred_width)(GtkWidget *widget, gint *minimum_width, gint *natural_width); + void (*get_preferred_height_for_width)(GtkWidget *widget, gint width, gint *minimum_height, gint *natural_height); +#if GTK_CHECK_VERSION(3, 10, 0) + void (*get_preferred_height_and_baseline_for_width)(GtkWidget *widget, gint width, gint *minimum, gint *natural, gint *minimum_baseline, gint *natural_baseline); + void (*size_allocate)(GtkWidget *widget, GtkAllocation *allocation); +#endif + } + PATCH_FUNCS; + +#endif + +#if 0 +static const char *_cursor_fdiag[] = +{ +"16 16 4 1", +"# c None", +"a c #000000", +"b c #c0c0c0", +". c #ffffff", +"..........######", +".aaaaaaaa.######", +".a....ba.#######", +".a...ba.########", +".a....ab.#######", +".a.b...ab.######", +".abaa...ab.###..", +".aa.ba...ab.#.a.", +".a.#.ba...ab.aa.", +"..###.ba...aaba.", +"######.ba...b.a.", +"#######.ba....a.", +"########.ab...a.", +"#######.ab....a.", +"######.aaaaaaaa.", +"######.........." +}; + +static const char *_cursor_bdiag[] = +{ +"16 16 4 1", +". c None", +"a c #000000", +"b c #c0c0c0", +"# c #ffffff", +"......##########", +"......#aaaaaaaa#", +".......#ab####a#", +"........#ab###a#", +".......#ba####a#", +"......#ba###b#a#", +"##...#ba###aaba#", +"#a#.#ba###ab#aa#", +"#aa#ba###ab#.#a#", +"#abaa###ab#...##", +"#a#b###ab#......", +"#a####ab#.......", +"#a###ba#........", +"#a####ba#.......", +"#aaaaaaaa#......", +"##########......" +}; +#endif + +// Geometry optimization hack - Sometimes fails, so it is disabled... +#define GEOMETRY_OPTIMIZATION 0 + + +#ifdef GTK3 +static gboolean cb_frame_draw(GtkWidget *wid, cairo_t *cr, gControl *control) +{ + control->drawBorder(cr); + return false; +} +#else +static gboolean cb_frame_expose(GtkWidget *wid, GdkEventExpose *e, gControl *control) +{ + control->drawBorder(e); + return false; +} +#endif + +#ifdef GTK3 +static gboolean cb_background_draw(GtkWidget *wid, cairo_t *cr, gControl *control) +{ + control->drawBackground(cr); + return false; +} +#else +static gboolean cb_background_expose(GtkWidget *wid, GdkEventExpose *e, gControl *control) +{ + control->drawBackground(e); + return false; +} +#endif + + +/**************************************************************************** + +gPlugin + +****************************************************************************/ + +gboolean gPlugin_OnUnplug(GtkSocket *socket,gPlugin *data) +{ + if (data->onUnplug) data->onUnplug(data); + return true; +} + +void gPlugin_OnPlug(GtkSocket *socket,gPlugin *data) +{ + if (data->onPlug) data->onPlug(data); +} + + +gPlugin::gPlugin(gContainer *parent) : gControl(parent) +{ + g_typ=Type_gPlugin; + + border = gtk_socket_new(); + widget = border; + realize(false); + + onPlug = NULL; + onUnplug = NULL; + + g_signal_connect(G_OBJECT(widget), "plug-removed", G_CALLBACK(gPlugin_OnUnplug), (gpointer)this); + g_signal_connect(G_OBJECT(widget), "plug-added", G_CALLBACK(gPlugin_OnPlug), (gpointer)this); + + ON_DRAW_BEFORE(border, this, cb_background_expose, cb_background_draw); + + setCanFocus(true); +} + +int gPlugin::client() +{ + //GdkNativeWindow win = gtk_socket_get_id(GTK_SOCKET(widget)); + //return (long)win; + GdkWindow *win = gtk_socket_get_plug_window(GTK_SOCKET(widget)); + if (!win) + return 0; + else + return (int)GDK_WINDOW_XID(win); +} + +void gPlugin::plug(int id) +{ + void (*func)(gControl *); + int i; + Display *d = gdk_x11_display_get_xdisplay(gdk_display_get_default()); + + func = onPlug; + onPlug = NULL; + + for (i = 1; i >= 0; i--) + { + if (i == 0) + onPlug = func; + + gtk_socket_add_id(GTK_SOCKET(widget), (Window)id); + } + + if (client()) + XAddToSaveSet(d, client()); + else + emit(SIGNAL(onError)); +} + +void gPlugin::discard() +{ + #ifdef GAMBAS_DIRECTFB + stub("DIRECTFB/gPlugin:discard()"); + #else + #ifdef GDK_WINDOWING_X11 + + Display *d = gdk_x11_display_get_xdisplay(gdk_display_get_default()); + + if (!client()) return; + + XRemoveFromSaveSet(d, client()); + XReparentWindow(d, client(), GDK_ROOT_WINDOW(), 0, 0); + + #else + stub("no-X11/gPlugin:discard()"); + #endif + #endif + +} + + +/***************************************************************** + +CREATION AND DESTRUCTION + +******************************************************************/ + +void gControl::cleanRemovedControls() +{ + GList *iter; + gControl *control; + + if (!controls_destroyed) return; + + for(;;) + { + iter = g_list_first(controls_destroyed); + if (!iter) + break; + control = (gControl *)iter->data; + gtk_widget_destroy(control->border); + } + + controls_destroyed = NULL; +} + +static bool always_can_raise(gControl *sender, int type) +{ + return true; +} + +void gControl::initAll(gContainer *parent) +{ + bufW = 0; + bufH = 0; + bufX = 0; + bufY = 0; + curs = NULL; + _font = NULL; + _resolved_font = NULL; + g_typ = 0; + dsg = false; + expa = false; + igno = false; + _accept_drops = false; + _drag_enter = false; + _drag_get_data = false; + frame_border = 0; + frame_padding = 0; + _bg_set = false; + _fg_set = false; + have_cursor = false; + use_base = false; + _mouse = CURSOR_DEFAULT; + pr = parent; + _name = NULL; + visible = false; + _locked = 0; + _destroyed = false; + _no_delete = false; + _action = false; + _dirty_pos = _dirty_size = false; + _tracking = false; + _has_input_method = false; + _no_default_mouse_event = false; + _proxy = _proxy_for = NULL; + _no_tab_focus = false; + _inside = false; + _no_auto_grab = false; + _no_background = false; + _use_wheel = false; + _scrollbar = SCROLL_NONE; + _input_method = NULL; + + onFinish = NULL; + onFocusEvent = NULL; + onKeyEvent = NULL; + onMouseEvent = NULL; + onDrag = NULL; + onDragMove = NULL; + onDrop = NULL; + onDragLeave = NULL; + onEnterLeave = NULL; + canRaise = always_can_raise; + + frame = border = widget = NULL; + _scroll = NULL; + hFree = NULL; + _grab = false; + + _fg = _bg = COLOR_DEFAULT; +#ifdef GTK3 + _css = NULL; + _fg_name = _bg_name = NULL; +#endif + + controls = g_list_append(controls,this); +} + +gControl::gControl() +{ + initAll(NULL); +} + +gControl::gControl(gContainer *parent) +{ + initAll(parent); +} + +gControl::~gControl() +{ + gMainWindow *win = window(); + + emit(SIGNAL(onFinish)); + + /*if (pr) + pr->remove(this);*/ + + if (win && win->focus == this) + win->focus = NULL; + + if (_proxy) + _proxy->_proxy_for = NULL; + if (_proxy_for) + _proxy_for->_proxy = NULL; + + if (gDrag::getSource() == this) + gDrag::cancel(); + + if (curs) + { + delete curs; + curs=NULL; + } + + if (_font) + { + gFont::assign(&_font); + gFont::assign(&_resolved_font); + } + +#ifdef GTK3 + if (_css) + g_object_unref(_css); +#endif + + //fprintf(stderr, "~gControl: %s\n", name()); + + setName(NULL); + + controls = g_list_remove(controls, this); + controls_destroyed = g_list_remove(controls_destroyed, this); + + #define CLEAN_POINTER(_p) if (_p == this) _p = NULL + + CLEAN_POINTER(gApplication::_enter); + CLEAN_POINTER(gApplication::_leave); + CLEAN_POINTER(gApplication::_active_control); + CLEAN_POINTER(gApplication::_previous_control); + CLEAN_POINTER(gApplication::_old_active_control); + CLEAN_POINTER(gApplication::_button_grab); + CLEAN_POINTER(gApplication::_enter_after_button_grab); + CLEAN_POINTER(gApplication::_control_grab); + CLEAN_POINTER(gApplication::_ignore_until_next_enter); +} + +void gControl::destroy() +{ + if (_destroyed) + return; + + hide(); + + //fprintf(stderr, "added to destroy list: %p\n", this); + controls_destroyed = g_list_prepend(controls_destroyed, (gpointer)this); + if (pr) + pr->remove(this); + _destroyed = true; +} + + +bool gControl::isEnabled() const +{ + return gtk_widget_is_sensitive(border); +} + +bool gControl::isReallyVisible() +{ + if (!isTopLevel() && !topLevel()->isReallyVisible()) + return false; + +#if GTK_CHECK_VERSION(2, 20, 0) + return gtk_widget_get_mapped(border); +#else + return GTK_WIDGET_MAPPED(border); +#endif +} + +void gControl::setEnabled(bool vl) +{ + gtk_widget_set_sensitive(border, vl); +} + +void gControl::setVisible(bool vl) +{ + if (vl == visible) + return; + + visible = vl; + + if (vl) + { + if (bufW <= 0 || bufH <= 0) + return; + + gtk_widget_show(border); + _dirty_size = true; + updateGeometry(); + } + else + { + if (parent() && hasFocus()) + gtk_widget_child_focus(GTK_WIDGET(gtk_widget_get_toplevel(border)), GTK_DIR_TAB_FORWARD); + if (gtk_widget_has_grab(border)) + gtk_grab_remove(border); + gtk_widget_hide(border); + } + + if (pr) pr->performArrange(); +} + +/***************************************************************** + +POSITION AND SIZE + +******************************************************************/ + +bool gControl::getScreenPos(int *x, int *y) +{ + if (!gtk_widget_get_window(border)) + { + *x = *y = 0; // widget is not realized + return true; + } + + gdk_window_get_origin(gtk_widget_get_window(border), x, y); + + //fprintf(stderr, "getScreenPos: %s: %d %d: %d\n", name(), *x, *y, gtk_widget_get_has_window(border)); + + #if GTK_CHECK_VERSION(2, 18, 0) + if (!gtk_widget_get_has_window(border)) + { + GtkAllocation a; + gtk_widget_get_allocation(border, &a); + *x += a.x; + *y += a.y; + } + #endif + + //fprintf(stderr, "getScreenPos: --> %d %d\n", *x, *y); + return false; +} + +int gControl::screenX() +{ + int x,y; + getScreenPos(&x, &y); + return x; +} + +int gControl::screenY() +{ + int x,y; + getScreenPos(&x, &y); + return y; +} + +void gControl::setLeft(int l) +{ + move(l,top()); +} + +void gControl::setTop(int t) +{ + move(left(),t); +} + +void gControl::setWidth(int w) +{ + resize(w,height()); +} + +void gControl::setHeight(int h) +{ + resize(width(),h); +} + +static void send_configure (gControl *control) +{ + GtkWidget *widget; + GdkEvent *event; + + widget = control->border; + +#if GTK_CHECK_VERSION(2, 20, 0) + if (!gtk_widget_get_realized(widget)) + return; +#else + if (!GTK_WIDGET_REALIZED(widget)) + return; +#endif + +// if (control->isWindow()) +// g_debug("send configure to window: %s", control->name()); + + event = gdk_event_new (GDK_CONFIGURE); + + event->configure.window = NULL; //(GdkWindow *)g_object_ref(widget->window); + event->configure.send_event = TRUE; + event->configure.x = control->x(); + event->configure.y = control->y(); + event->configure.width = control->width(); + event->configure.height = control->height(); + + gtk_widget_event (widget, event); + gdk_event_free (event); +} + +void gControl::move(int x, int y) +{ + //GtkLayout *fx; + + if (x == bufX && y == bufY) + return; + + bufX = x; + bufY = y; + + //g_debug("move: %p: %d %d", this, x, y); + _dirty_pos = true; + if (pr) + { + // TODO: check the following optimization to see if it can be enabled again + //if (gtk_widget_get_parent(border) == pr->getContainer()) + pr->performArrange(); + } + + #if GEOMETRY_OPTIMIZATION + gApplication::setDirty(); + #else + updateGeometry(); + #endif + + send_configure(this); // needed for Watcher and Form Move events +} + +void gControl::resize(int w, int h) +{ + if (w < minimumWidth()) + w = minimumWidth(); + + if (h < minimumHeight()) + h = minimumHeight(); + + if (bufW == w && bufH == h) + return; + + if (w < 1 || h < 1) + { + bufW = w; + bufH = h; + + if (visible) + gtk_widget_hide(border); + } + else + { + bufW = w; + bufH = h; + + if (frame && widget != border) + { + int fw = getFrameWidth() * 2; + if (w < fw || h < fw) + gtk_widget_hide(widget); + else + gtk_widget_show(widget); + } + + if (visible) + gtk_widget_show(border); + + //g_debug("resize: %p %s: %d %d", this, name(), w, h); + _dirty_size = true; + + #if GEOMETRY_OPTIMIZATION + gApplication::setDirty(); + #else + updateGeometry(); + #endif + } + + if (pr) + pr->performArrange(); + + send_configure(this); // needed for Watcher and Form Resize events +} + +void gControl::moveResize(int x, int y, int w, int h) +{ + if (pr) + pr->disableArrangement(); + + resize(w, h); + move(x, y); + + if (pr) + pr->enableArrangement(); +} + +void gControl::updateGeometry() +{ +// if (_dirty_pos) +// { +// g_debug("move: %p -> %d %d", this, x(), y()); +// _dirty_pos = false; +// GtkLayout *fx = GTK_LAYOUT(gtk_widget_get_parent(border)); +// gtk_layout_move(fx, border, x(), y()); +// } +// +// if (_dirty_size) +// { +// GtkAllocation a = { x(), y(), width(), height() }; +// g_debug("resize: %p -> %d %d", this, width(), height()); +// _dirty_size = false; +// //gtk_widget_set_size_request(border, width(), height()); +// gtk_widget_size_allocate(border, +// } + if (_dirty_pos || _dirty_size) + { + //g_debug("move-resize: %s: %d %d %d %d", this->name(), x(), y(), width(), height()); + if (_dirty_pos) + { + if (pr) + pr->moveChild(this, x(), y()); + + _dirty_pos = false; + } + if (_dirty_size && visible) + { + gtk_widget_set_size_request(border, width(), height()); + _dirty_size = false; + } + } +} + +/***************************************************************** + +APPEARANCE + +******************************************************************/ + + +void gControl::setExpand (bool vl) +{ + if (vl == expa) + return; + + expa=vl; + + if (pr) pr->performArrange(); +} + + +void gControl::setIgnore (bool vl) +{ + if (vl == igno) + return; + + igno=vl; + if (pr) pr->performArrange(); +} + +char* gControl::toolTip() +{ + char *text = gtk_widget_get_tooltip_text(border); + gt_free_later(text); + return text; +} + +void gControl::setToolTip(char* vl) +{ + gtk_widget_set_tooltip_text(border, vl ? vl : ""); +} + +gFont* gControl::font() +{ + if (_resolved_font) + { + //fprintf(stderr, "%s: font -> _resolved_font\n", name()); + return _resolved_font; + } + else if (pr) + { + //fprintf(stderr, "%s: font -> parent\n", name()); + return pr->font(); + } + else + { + //fprintf(stderr, "%s: font -> desktop\n", name()); + return gDesktop::font(); + } +} + +void gControl::actualFontTo(gFont *ft) +{ + font()->copyTo(ft); +} + +void gControl::resolveFont() +{ + gFont *font; + + if (_font) + { + font = new gFont(); + font->mergeFrom(_font); + if (pr) + font->mergeFrom(pr->font()); + else + font->mergeFrom(gDesktop::font()); + + gFont::set(&_resolved_font, font); + } + else + gFont::assign(&_resolved_font); +} + +void gControl::setFont(gFont *ft) +{ + //fprintf(stderr, "setFont: %s: %s\n", name(), ft->toFullString()); + if (ft) + gFont::assign(&_font, ft); + else if (_font) + gFont::assign(&_font); + + gFont::assign(&_resolved_font); + + updateFont(); + + resize(); + + //fprintf(stderr, "--> %s: _font = %s\n", name(), _font ? _font->toFullString() : NULL); +} + +#ifdef GTK3 + +void gControl::updateFont() +{ + resolveFont(); + updateStyleSheet(); + updateSize(); +} + +#else + +static void cb_update_font(GtkWidget *widget, gpointer data) +{ + PangoFontDescription *desc = (PangoFontDescription *)data; + gtk_widget_modify_font(widget, desc); +} + +void gControl::updateFont() +{ + resolveFont(); + gtk_widget_modify_font(widget, font()->desc()); + if (!isContainer() && GTK_IS_CONTAINER(widget)) + gtk_container_forall(GTK_CONTAINER(widget), (GtkCallback)cb_update_font, (gpointer)font()->desc()); + refresh(); + updateSize(); +} + +#endif + +void gControl::updateSize() +{ +} + +int gControl::mouse() +{ + if (_proxy) + return _proxy->mouse(); + else + return _mouse; +} + +gCursor* gControl::cursor() +{ + if (_proxy) + return _proxy->cursor(); + + if (!curs) return NULL; + return new gCursor(curs); +} + +void gControl::setCursor(gCursor *vl) +{ + if (_proxy) + { + _proxy->setCursor(vl); + return; + } + + if (curs) { delete curs; curs=NULL;} + if (!vl) + { + setMouse(CURSOR_DEFAULT); + return; + } + curs=new gCursor(vl); + setMouse(CURSOR_CUSTOM); +} + +void gControl::updateCursor(GdkCursor *cursor) +{ + if (GDK_IS_WINDOW(gtk_widget_get_window(border)) && _inside) + { + //fprintf(stderr, "updateCursor: %s %p\n", name(), cursor); + if (!cursor && parent() && gtk_widget_get_window(parent()->border) == gtk_widget_get_window(border)) + parent()->updateCursor(parent()->getGdkCursor()); + else + gdk_window_set_cursor(gtk_widget_get_window(border), cursor); + } +} + +GdkCursor *gControl::getGdkCursor() +{ + const char *name; + GdkCursor *cr = NULL; + int m = _mouse; + + if (gApplication::isBusy()) + m = GDK_WATCH; + + if (m == CURSOR_CUSTOM) + { + if (curs && curs->cur) + return curs->cur; + } + + if (m != CURSOR_DEFAULT) + { + switch(m) + { + case GDK_BLANK_CURSOR: name = "none"; break; + case GDK_LEFT_PTR: name = "default"; break; + case GDK_CROSSHAIR: name = "crosshair"; break; + case GDK_WATCH: name = "wait"; break; + case GDK_XTERM: name = "text"; break; + case GDK_FLEUR: name = "move"; break; + case GDK_SB_H_DOUBLE_ARROW: name = "ew-resize"; break; + case GDK_SB_V_DOUBLE_ARROW: name = "ns-resize"; break; + case GDK_TOP_SIDE: name = "n-resize"; break; + case GDK_BOTTOM_SIDE: name = "s-resize"; break; + case GDK_LEFT_SIDE: name = "w-resize"; break; + case GDK_RIGHT_SIDE: name = "e-resize"; break; + case GDK_TOP_LEFT_CORNER: name = "nw-resize"; break; + case GDK_BOTTOM_RIGHT_CORNER: name = "se-resize"; break; + case GDK_TOP_RIGHT_CORNER: name = "ne-resize"; break; + case GDK_BOTTOM_LEFT_CORNER: name = "sw-resize"; break; + case GDK_LAST_CURSOR+1: name = "nwse-resize"; break; + case GDK_LAST_CURSOR+2: name = "nesw-resize"; break; + case GDK_HAND2: name = "pointer"; break; + default: name = "default"; + } + + cr = gdk_cursor_new_from_name(gdk_display_get_default(), name); + if (!cr) + cr = gdk_cursor_new_for_display(gdk_display_get_default(), (GdkCursorType)m); + + /* + if (m < GDK_LAST_CURSOR) + { + cr = gdk_cursor_new_for_display(gdk_display_get_default(), (GdkCursorType)m); + } + else + { + if (m == (GDK_LAST_CURSOR+1)) //FDiag + { + pix = gdk_pixbuf_new_from_xpm_data(_cursor_fdiag); + cr = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), pix, 8, 8); + g_object_unref(pix); + } + else if (m == (GDK_LAST_CURSOR+2)) //BDiag + { + pix = gdk_pixbuf_new_from_xpm_data(_cursor_bdiag); + cr = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), pix, 8, 8); + g_object_unref(pix); + } + }*/ + } + + return cr; +} + +void gControl::setMouse(int m) +{ + if (_proxy) + { + _proxy->setMouse(m); + return; + } + + if (m == CURSOR_CUSTOM) + { + if (!curs || !curs->cur) + m = CURSOR_DEFAULT; + } + + _mouse = m; + + updateCursor(getGdkCursor()); +} + + + +/***************************************************************** + +HANDLES + +******************************************************************/ + +bool gControl::isWindow() const +{ + return g_typ == Type_gMainWindow; +} + +gMainWindow* gControl::window() +{ + if (isWindow()) + return (gMainWindow *)this; + + if (!pr) + return NULL; + else + return pr->window(); +} + +gMainWindow* gControl::topLevel() +{ + gControl *child = this; + + while (!child->isTopLevel()) + child = child->parent(); + + return (gMainWindow *)child; +} + +int gControl::handle() +{ + #ifndef GAMBAS_DIRECTFB + #ifdef GDK_WINDOWING_X11 + if (!gtk_widget_get_window(border)) + return 0; + else + return GDK_WINDOW_XID(gtk_widget_get_window(border)); + #else + stub("no-X11/gControl::handle()"); + return 0; + #endif + #else + stub("DIRECTFB/gControl::handle()"); + return 0; + #endif + +} + +/***************************************************************** + +MISC + +******************************************************************/ + +void gControl::refresh() +{ + //refresh(0, 0, 0, 0); + gtk_widget_queue_draw(border); + if (frame != border && GTK_IS_WIDGET(frame)) + gtk_widget_queue_draw(frame); + if (widget != frame && GTK_IS_WIDGET(widget)) + gtk_widget_queue_draw(widget); + + afterRefresh(); +} + +void gControl::refresh(int x, int y, int w, int h) +{ + GdkRectangle r; + GtkAllocation a; + + gtk_widget_get_allocation(border, &a); + + if (x < 0 || y < 0 || w <= 0 || h <= 0) + { + x = y = 0; + w = width(); + h = height(); + } + r.x = a.x + x; + r.y = a.y + y; + r.width = w; + r.height = h; + + gdk_window_invalidate_rect(gtk_widget_get_window(border), &r, TRUE); + + afterRefresh(); +} + +void gControl::afterRefresh() +{ +} + +bool gControl::design() +{ + return dsg; +} + +void gControl::setDesign(bool vl) +{ + dsg=vl; +} + +void gControl::setFocus() +{ + if (_proxy) + { + _proxy->setFocus(); + return; + } + + gMainWindow *win = window(); + + if (!win) + return; + + if (win->isVisible()) + { + //if (isVisible() && bufW > 0 && bufH > 0) + //fprintf(stderr, "setFocus now %s\n", name()); + gtk_widget_grab_focus(widget); + } + else + { + //fprintf(stderr, "setFocus later %s\n", name()); + win->focus = this; + } +} + +bool gControl::hasFocus() const +{ + if (_proxy) + return _proxy->hasFocus(); + else +#if GTK_CHECK_VERSION(2, 18, 0) + return (border && gtk_widget_has_focus(border)) || (widget && gtk_widget_has_focus(widget)) || gApplication::activeControl() == this; +#else + return (border && GTK_WIDGET_HAS_FOCUS(border)) || (widget && GTK_WIDGET_HAS_FOCUS(widget)) || gApplication::activeControl() == this; +#endif +} + +#if GTK_CHECK_VERSION(3, 2, 0) +bool gControl::hasVisibleFocus() const +{ + if (_proxy) + return _proxy->hasVisibleFocus(); + else + return (border && gtk_widget_has_visible_focus(border)) || (widget && gtk_widget_has_visible_focus(widget)); +} +#endif + +gControl* gControl::next() +{ + int index; + + if (!pr) + return NULL; + + index = pr->childIndex(this); + if (index < 0 || index >= pr->childCount()) + return NULL; + else + return pr->child(index + 1); +} + +gControl* gControl::previous() +{ + int index; + + if (!pr) + return NULL; + + index = pr->childIndex(this); + if (index <= 0) + return NULL; + else + return pr->child(index - 1); +} + + +void gControl::lower() +{ + gpointer *p; + GList *chd; + GtkWidget *child; + gControl *br; + int x,y; + GtkContainer *parent; + + if (!pr) return; + + /*if (gtk_widget_get_has_window(border)) + { + gdk_window_lower(gtk_widget_get_window(border)); + if (gtk_widget_get_window(widget)) + gdk_window_lower(gtk_widget_get_window(widget)); + } + else*/ + { + //fprintf(stderr, "gb.gtk: warning: gControl::lower(): no window\n"); + + if (!(chd=gtk_container_get_children(GTK_CONTAINER(pr->getContainer())))) return; + + chd = g_list_first(chd); + + while(chd) + { + child = (GtkWidget*)chd->data; + + br = gt_get_control(child); + + if (br && br != this) + { + x = br->x(); + y = br->y(); + parent = GTK_CONTAINER(gtk_widget_get_parent(br->border)); + g_object_ref(G_OBJECT(br->border)); + gtk_container_remove(parent, br->border); + gtk_container_add(parent, br->border); + + if (GTK_IS_LAYOUT(parent)) + gtk_layout_move(GTK_LAYOUT(parent), br->border, x, y); + else + gtk_fixed_move(GTK_FIXED(parent), br->border, x, y); + + g_object_unref(G_OBJECT(br->border)); + } + + chd = g_list_next(chd); + } + } + + g_ptr_array_remove(pr->_children, this); + + g_ptr_array_add(pr->_children, NULL); + p = pr->_children->pdata; + g_memmove(&p[1], &p[0], (pr->_children->len - 1) * sizeof(gpointer)); + p[0] = this; + + //pr->ch_list = g_list_remove(pr->ch_list, this); + //pr->ch_list = g_list_prepend(pr->ch_list, this); + pr->updateFocusChain(); + pr->performArrange(); +} + +void gControl::raise() +{ + int x, y; + GtkContainer *parent; + + if (!pr) return; + + x = left(); + y = top(); + parent = GTK_CONTAINER(gtk_widget_get_parent(border)); + g_object_ref(G_OBJECT(border)); + gtk_container_remove(parent, border); + gtk_container_add(parent, border); + + //pr->moveChild(this, x, y); + if (GTK_IS_LAYOUT(parent)) + gtk_layout_move(GTK_LAYOUT(parent), border, x, y); + else + gtk_fixed_move(GTK_FIXED(parent), border, x, y); + + g_object_unref(G_OBJECT(border)); + + g_ptr_array_remove(pr->_children, this); + g_ptr_array_add(pr->_children, this); + + pr->updateFocusChain(); + pr->performArrange(); +} + +void gControl::setNext(gControl *ctrl) +{ + #ifdef GAMBAS_DIRECTFB + stub("DIRECTFB/gControl::setNext()"); + #else + Window stack[2]; + GPtrArray *ch; + uint i; + + if (!ctrl) + { + raise(); + return; + } + + if (ctrl == this || isTopLevel() || ctrl->parent() != parent()) + return; + + if (gtk_widget_get_has_window(ctrl->border) && gtk_widget_get_has_window(border)) + { + stack[0] = GDK_WINDOW_XID(gtk_widget_get_window(ctrl->border)); + stack[1] = GDK_WINDOW_XID(gtk_widget_get_window(border)); + + XRestackWindows(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(border)), stack, 2 ); + } + + ch = pr->_children; + g_ptr_array_remove(ch, this); + g_ptr_array_add(ch, NULL); + + for (i = 0; i < ch->len; i++) + { + if (g_ptr_array_index(ch, i) == ctrl) + { + g_memmove(&ch->pdata[i + 1], &ch->pdata[i], (ch->len - i - 1) * sizeof(gpointer)); + ch->pdata[i] = this; + break; + } + } + + pr->updateFocusChain(); + pr->performArrange(); + #endif +} + +void gControl::setPrevious(gControl *ctrl) +{ + if (!ctrl) + lower(); + else + setNext(ctrl->next()); +} + +/********************************************************************* + +Drag & Drop + +**********************************************************************/ + +void gControl::setAcceptDrops(bool vl) +{ + //GtkWidget *w; + //GtkTargetEntry entry[7]; + + /* BM: ?? + if (!pr) w=frame; + else w=widget; + */ + + if (vl == _accept_drops) + return; + + _accept_drops = vl; + + if (vl) + { + gtk_drag_dest_set(border, (GtkDestDefaults)0, NULL, 0, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK)); + //if (widget != border) + // gtk_drag_dest_set(widget, (GtkDestDefaults)0, NULL, 0, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK)); + } + else + { + gtk_drag_dest_unset(border); + //if (widget != border) + // gtk_drag_dest_unset(widget); + } +} + +/********************************************************************* + +Internal + +**********************************************************************/ + +void gControl::connectParent() +{ + if (pr) + { + //gtk_widget_set_redraw_on_allocate(border, false); + + pr->insert(this, true); + } + + // BM: Widget has been created, so we can set its cursor if application is busy + if (gApplication::isBusy() && mustUpdateCursor()) + setMouse(mouse()); +} + +GList* gControl::controlList() +{ + return controls; +} + +gColor gControl::getFrameColor() +{ + return gDesktop::lightfgColor(); +} + +#ifdef GTK3 +void gControl::drawBorder(cairo_t *cr) +{ + gt_draw_border(cr, gtk_widget_get_style_context(widget), GTK_STATE_FLAG_NORMAL, getFrameBorder(), getFrameColor(), 0, 0, width(), height(), use_base); +} +#else +void gControl::drawBorder(GdkEventExpose *e) +{ + GdkWindow *win; + GtkShadowType shadow; + gint x, y, w, h; + cairo_t *cr; + GtkWidget *wid; + GtkAllocation a; + + if (getFrameBorder() == BORDER_NONE) + return; + + x = 0; + y = 0; + w = width(); + h = height(); + + if (frame) + wid = frame; + else + wid = widget; + + if (GTK_IS_LAYOUT(wid)) + win = gtk_layout_get_bin_window(GTK_LAYOUT(wid)); + else + win = gtk_widget_get_window(wid); + + gtk_widget_get_allocation(wid, &a); + x = a.x; + y = a.y; + + if (w < 1 || h < 1) + return; + + switch (getFrameBorder()) + { + case BORDER_PLAIN: + + cr = gdk_cairo_create(win); + gt_cairo_draw_rect(cr, x, y, w, h, getFrameColor()); + cairo_destroy(cr); + return; + + case BORDER_SUNKEN: shadow = GTK_SHADOW_IN; break; + case BORDER_RAISED: shadow = GTK_SHADOW_OUT; break; + case BORDER_ETCHED: shadow = GTK_SHADOW_ETCHED_IN; break; + + default: + return; + } + + GdkRectangle clip; + gdk_region_get_clipbox(e->region, &clip); + GtkStyle *st = gtk_widget_get_style(widget); + if (use_base) + gtk_paint_box(st, win, GTK_STATE_NORMAL, shadow, &clip, widget, "entry", x, y, w, h); + else + gtk_paint_shadow(st, win, GTK_STATE_NORMAL, shadow, &clip, widget, NULL, x, y, w, h); +} +#endif + +/*static void cb_size_allocate(GtkWidget *wid, GtkAllocation *a, gContainer *container) +{ + if (!container->isTopLevel()) + container->performArrange(); +}*/ + + +/* + The different cases managed by gControl::realize() + + border frame widget + 0 0 W + 0 F W + B 0 W + B F W +*/ + +static void add_container(GtkWidget *parent, GtkWidget *child) +{ + GtkWidget *ch; + + for(;;) + { + if (!GTK_IS_BIN(parent)) + break; + + ch = gtk_bin_get_child(GTK_BIN(parent)); + if (!ch) + break; + + parent = ch; + } + + gtk_container_add(GTK_CONTAINER(parent), child); +} + +void gControl::registerControl() +{ + gt_register_control(border, this); +} + +#ifdef GTK3 +/*static gboolean cb_clip_children(GtkWidget *wid, GdkEventExpose *e, gContainer *d) +{ + cairo_region_t *me; + GtkAllocation a; + + gtk_widget_get_allocation(wid, &a); + me = cairo_region_create_rectangle((cairo_rectangle_int_t *)&a); + + cairo_region_intersect(e->region, me); + + cairo_region_destroy(me); + + if (cairo_region_is_empty(e->region)) + return TRUE; + + return FALSE; +}*/ +#else +static gboolean cb_clip_children(GtkWidget *wid, GdkEventExpose *e, gContainer *d) +{ + GdkRegion *me; + GtkAllocation a; + + gtk_widget_get_allocation(wid, &a); + me = gdk_region_rectangle((GdkRectangle *)&a); + + gdk_region_intersect(e->region, me); + + gdk_region_destroy(me); + + if (gdk_region_empty(e->region)) + return TRUE; + + return FALSE; +} +#endif + +#if 0 +static gboolean cb_clip_by_parent(GtkWidget *wid, GdkEventExpose *e, gControl *d) +{ + GdkRegion *preg; + GdkRectangle prect = { 0, 0, d->parent()->width() - d->x(), d->parent()->height() - d->y() }; + + fprintf(stderr, "area = %d %d %d %d prect = %d %d %d %d\n", + e->area.x, e->area.y, e->area.width, e->area.height, + prect.x, prect.y, prect.width, prect.height); + + preg = gdk_region_rectangle(&prect); + + gdk_region_intersect(e->region, preg); + + gdk_region_destroy(preg); + + if (gdk_region_empty(e->region)) + return TRUE; + + gdk_region_get_clipbox(e->region, &prect); + e->area = prect; + fprintf(stderr, "--> %d %d %d %d\n", prect.x, prect.y, prect.width, prect.height); + + return FALSE; +} +#endif + +#ifdef GTK3 + +//fprintf(stderr, "get_preferred_width [%p %s] %p\n", klass, G_OBJECT_TYPE_NAME(widget), klass->_gtk_reserved2); +//fprintf(stderr, "get_preferred_height [%p %s] %p\n", klass, G_OBJECT_TYPE_NAME(widget), klass->_gtk_reserved3); + +//#define must_patch(_widget) (gt_get_control(_widget) != NULL) + +static bool must_patch(GtkWidget *widget) +{ + GtkWidget *parent; + gControl *parent_control; + + if (GTK_IS_ENTRY(widget)) + return true; + + if (gt_get_control(widget)) + return true; + + parent = gtk_widget_get_parent(widget); + if (!parent) + return false; + + if (GTK_IS_SCROLLED_WINDOW(parent)) + { + parent = gtk_widget_get_parent(parent); + if (!parent) + return false; + } + + parent_control = gt_get_control(parent); + if (!parent_control) + return false; + + return (parent_control->widget == widget || (GtkWidget *)parent_control->_scroll == widget); +} + +//fprintf(stderr, #type "_get_preferred_height: %d %d\n", minimum_size ? *minimum_size : -1, natural_size ? *natural_size : -1); +//fprintf(stderr, #type "_get_preferred_width: %d %d\n", minimum_size ? *minimum_size : -1, natural_size ? *natural_size : -1); + +#define OLD_FUNC ((PATCH_FUNCS *)(klass->_gtk_reserved6)) + +#define PATCH_DECLARE_COMMON(_type, _name) \ +static void _name##get_preferred_width(GtkWidget *widget, gint *minimum_size, gint *natural_size) \ +{ \ + GtkWidgetClass *klass = (GtkWidgetClass*)g_type_class_peek(_type); \ + (*OLD_FUNC->get_preferred_width)(widget, minimum_size, natural_size); \ + if (minimum_size && must_patch(widget)) \ + *minimum_size = 0; \ +} \ +static void _name##get_preferred_height(GtkWidget *widget, gint *minimum_size, gint *natural_size) \ +{ \ + GtkWidgetClass *klass = (GtkWidgetClass *)g_type_class_peek(_type); \ + (*OLD_FUNC->get_preferred_height)(widget, minimum_size, natural_size); \ + if (minimum_size && must_patch(widget)) \ + *minimum_size = 0; \ +} \ +static void _name##get_preferred_height_for_width(GtkWidget *widget, gint width, gint *minimum_size, gint *natural_size) \ +{ \ + if (minimum_size && must_patch(widget)) \ + { \ + *minimum_size = 0; \ + *natural_size = 0; \ + return; \ + } \ + GtkWidgetClass *klass = (GtkWidgetClass *)g_type_class_peek(_type); \ + (*OLD_FUNC->get_preferred_height_for_width)(widget, width, minimum_size, natural_size); \ +} \ +static void _name##get_preferred_width_for_height(GtkWidget *widget, gint height, gint *minimum_size, gint *natural_size) \ +{ \ + if (minimum_size && must_patch(widget)) \ + { \ + *minimum_size = 0; \ + *natural_size = 0; \ + return; \ + } \ + GtkWidgetClass *klass = (GtkWidgetClass *)g_type_class_peek(_type); \ + (*OLD_FUNC->get_preferred_height_for_width)(widget, height, minimum_size, natural_size); \ +} + +#if GTK_CHECK_VERSION(3, 14, 0) + +#define PATCH_DECLARE_SIZE(_type, _name) \ +static void _name##size_allocate(GtkWidget *widget, GtkAllocation *allocation) \ +{ \ + GtkWidgetClass *klass = (GtkWidgetClass *)g_type_class_peek(_type); \ + (*OLD_FUNC->size_allocate)(widget, allocation); \ + gtk_widget_set_clip(widget, allocation); \ +} + +#elif GTK_CHECK_VERSION(3, 10, 0) + +#define PATCH_DECLARE_SIZE(_type, _name) \ +static void _name##size_allocate(GtkWidget *widget, GtkAllocation *allocation) \ +{ \ + GtkWidgetClass *klass = (GtkWidgetClass *)g_type_class_peek(_type); \ + (*OLD_FUNC->size_allocate)(widget, allocation); \ +} + +#else + +#define PATCH_DECLARE_SIZE(_type, _name) + +#endif + +#define PATCH_DECLARE(type) PATCH_DECLARE_COMMON(type, type##_) PATCH_DECLARE_SIZE(type, type##_) + +#define PATCH_DECLARE_BASELINE(type) \ +static void type##_get_preferred_height_and_baseline_for_width(GtkWidget *widget, gint width, gint *minimum, gint *natural, gint *minimum_baseline, gint *natural_baseline) \ +{ \ + if (minimum && minimum_baseline && must_patch(widget)) \ + { \ + GtkWidgetClass *klass = (GtkWidgetClass *)g_type_class_peek(type); \ + if (OLD_FUNC->get_preferred_height_and_baseline_for_width) \ + (*OLD_FUNC->get_preferred_height_and_baseline_for_width)(widget, width, minimum, natural, minimum_baseline, natural_baseline); \ + else \ + { \ + *minimum_baseline = 0; \ + *natural_baseline = 0; \ + } \ + *minimum = 0; \ + *natural = 0; \ + return; \ + } \ + GtkWidgetClass *klass = (GtkWidgetClass *)g_type_class_peek(type); \ + (*OLD_FUNC->get_preferred_height_and_baseline_for_width)(widget, width, minimum, natural, minimum_baseline, natural_baseline); \ +} + +//fprintf(stderr, "patching [%p %s] (%p %p)\n", klass, G_OBJECT_TYPE_NAME(widget), klass->get_preferred_width, klass->get_preferred_height); +// fprintf(stderr, "PATCH_CLASS: %s\n", G_OBJECT_TYPE_NAME(widget)); + +#if GTK_CHECK_VERSION(3,10,0) + +#define PATCH_CLASS(widget, type) \ +if (G_OBJECT_TYPE(widget) == type) \ +{ \ + GtkWidgetClass *klass = (GtkWidgetClass *)GTK_WIDGET_GET_CLASS(widget); \ + if (klass->get_preferred_width != type##_get_preferred_width) \ + { \ + PATCH_FUNCS *funcs = g_new0(PATCH_FUNCS, 1); \ + funcs->get_preferred_width = klass->get_preferred_width; \ + funcs->get_preferred_height = klass->get_preferred_height; \ + funcs->get_preferred_height_for_width = klass->get_preferred_height_for_width; \ + funcs->get_preferred_width_for_height = klass->get_preferred_width_for_height; \ + funcs->size_allocate = klass->size_allocate; \ + klass->_gtk_reserved6 = (void(*)())funcs; \ + klass->get_preferred_width = type##_get_preferred_width; \ + klass->get_preferred_height = type##_get_preferred_height; \ + klass->get_preferred_height_for_width = type##_get_preferred_height_for_width; \ + klass->get_preferred_width_for_height = type##_get_preferred_width_for_height; \ + klass->size_allocate = type##_size_allocate; \ + } \ +} + +#define PATCH_CLASS_BASELINE(widget, type) \ +if (G_OBJECT_TYPE(widget) == type) \ +{ \ + GtkWidgetClass *klass = (GtkWidgetClass *)GTK_WIDGET_GET_CLASS(widget); \ + if (klass->get_preferred_width != type##_get_preferred_width) \ + { \ + PATCH_FUNCS *funcs = g_new0(PATCH_FUNCS, 1); \ + funcs->get_preferred_width = klass->get_preferred_width; \ + funcs->get_preferred_height = klass->get_preferred_height; \ + funcs->get_preferred_height_for_width = klass->get_preferred_height_for_width; \ + funcs->get_preferred_width_for_height = klass->get_preferred_width_for_height; \ + funcs->size_allocate = klass->size_allocate; \ + funcs->get_preferred_height_and_baseline_for_width = klass->get_preferred_height_and_baseline_for_width; \ + klass->_gtk_reserved6 = (void(*)())funcs; \ + klass->get_preferred_width = type##_get_preferred_width; \ + klass->get_preferred_height = type##_get_preferred_height; \ + klass->get_preferred_height_for_width = type##_get_preferred_height_for_width; \ + klass->get_preferred_width_for_height = type##_get_preferred_width_for_height; \ + klass->size_allocate = type##_size_allocate; \ + klass->get_preferred_height_and_baseline_for_width = type##_get_preferred_height_and_baseline_for_width; \ + } \ +} + +#else + +#define PATCH_CLASS(widget, type) \ +if (G_OBJECT_TYPE(widget) == type) \ +{ \ + GtkWidgetClass *klass = (GtkWidgetClass *)GTK_WIDGET_GET_CLASS(widget); \ + if (klass->get_preferred_width != type##_get_preferred_width) \ + { \ + PATCH_FUNCS *funcs = g_new0(PATCH_FUNCS, 1); \ + funcs->get_preferred_width = klass->get_preferred_width; \ + funcs->get_preferred_height = klass->get_preferred_height; \ + funcs->get_preferred_height_for_width = klass->get_preferred_height_for_width; \ + funcs->get_preferred_width_for_height = klass->get_preferred_width_for_height; \ + klass->_gtk_reserved6 = (void(*)())funcs; \ + klass->get_preferred_width = type##_get_preferred_width; \ + klass->get_preferred_height = type##_get_preferred_height; \ + klass->get_preferred_height_for_width = type##_get_preferred_height_for_width; \ + klass->get_preferred_width_for_height = type##_get_preferred_width_for_height; \ + } \ +} + +#define PATCH_CLASS_BASELINE PATCH_CLASS + +#endif + +PATCH_DECLARE(GTK_TYPE_WINDOW) +PATCH_DECLARE(GTK_TYPE_ENTRY) +PATCH_DECLARE(GTK_TYPE_COMBO_BOX) +PATCH_DECLARE(GTK_TYPE_SPIN_BUTTON) +PATCH_DECLARE(GTK_TYPE_BUTTON) +PATCH_DECLARE(GTK_TYPE_FIXED) +PATCH_DECLARE(GTK_TYPE_EVENT_BOX) +//PATCH_DECLARE(GTK_TYPE_ALIGNMENT) +PATCH_DECLARE(GTK_TYPE_BOX) +PATCH_DECLARE(GTK_TYPE_TOGGLE_BUTTON) +PATCH_DECLARE(GTK_TYPE_SCROLLED_WINDOW) +PATCH_DECLARE(GTK_TYPE_CHECK_BUTTON) +PATCH_DECLARE(GTK_TYPE_RADIO_BUTTON) +PATCH_DECLARE(GTK_TYPE_NOTEBOOK) +PATCH_DECLARE(GTK_TYPE_SOCKET) +PATCH_DECLARE(GTK_TYPE_TEXT_VIEW) +PATCH_DECLARE(GTK_TYPE_SCROLLBAR) +PATCH_DECLARE(GTK_TYPE_SCALE) + +#if GTK_CHECK_VERSION(3,10,0) +PATCH_DECLARE_BASELINE(GTK_TYPE_ENTRY) +PATCH_DECLARE_BASELINE(GTK_TYPE_COMBO_BOX) +PATCH_DECLARE_BASELINE(GTK_TYPE_SPIN_BUTTON) +PATCH_DECLARE_BASELINE(GTK_TYPE_BUTTON) +#endif + +/*int gt_get_preferred_width(GtkWidget *widget) +{ + int m, n; + GtkWidgetClass *klass = (GtkWidgetClass*)g_type_class_peek(G_OBJECT_TYPE(widget)); + if (klass->_gtk_reserved6) + (*(void (*)(GtkWidget *, gint *, gint *))klass->_gtk_reserved6)(widget, &m, &n); + return m; +}*/ + +#endif + +void gControl::realize(bool make_frame) +{ + if (!_scroll) + { + if (!make_frame) + { + frame = widget; + } + else if (!frame) + { +#if GTK3 + border = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_hexpand(widget, TRUE); +#else + border = gtk_alignment_new(0, 0, 1, 1); +#endif + gtk_widget_set_redraw_on_allocate(frame, TRUE); + } + + if (!border) + border = widget; + + //printf("border = %p / frame = %p / widget =%p\n", border, frame, widget); + + if (border != frame) + { + //printf("frame -> border\n"); + add_container(border, frame); + } + if (frame != widget && border != widget) + { + //printf("widget -> frame\n"); + add_container(frame, widget); + } + + if (!make_frame) + frame = 0; + } + + //fprintf(stderr, "realize: %p %p\n", border, widget); + +#ifdef GTK3 + + PATCH_CLASS(border, GTK_TYPE_WINDOW) + else PATCH_CLASS_BASELINE(border, GTK_TYPE_ENTRY) + else PATCH_CLASS_BASELINE(border, GTK_TYPE_SPIN_BUTTON) + else PATCH_CLASS_BASELINE(border, GTK_TYPE_BUTTON) + else PATCH_CLASS(border, GTK_TYPE_FIXED) + else PATCH_CLASS(border, GTK_TYPE_EVENT_BOX) + //else PATCH_CLASS(border, GTK_TYPE_ALIGNMENT) + else PATCH_CLASS(border, GTK_TYPE_BOX) + else PATCH_CLASS(border, GTK_TYPE_TOGGLE_BUTTON) + else PATCH_CLASS(border, GTK_TYPE_SCROLLED_WINDOW) + else PATCH_CLASS(border, GTK_TYPE_CHECK_BUTTON) + else PATCH_CLASS(border, GTK_TYPE_RADIO_BUTTON) + else PATCH_CLASS(border, GTK_TYPE_NOTEBOOK) + else PATCH_CLASS(border, GTK_TYPE_SOCKET) + else PATCH_CLASS(border, GTK_TYPE_TEXT_VIEW) + else PATCH_CLASS(border, GTK_TYPE_SCROLLBAR) + else PATCH_CLASS(border, GTK_TYPE_SCALE) + else + { + fprintf(stderr, "gb.gtk3: warning: class %s was not patched\n", G_OBJECT_TYPE_NAME(border)); + } + + PATCH_CLASS_BASELINE(widget, GTK_TYPE_COMBO_BOX) + else PATCH_CLASS(widget, GTK_TYPE_TEXT_VIEW) + +#endif + + connectParent(); + initSignals(); + +//#ifndef GTK3 + if (!_no_background && !gtk_widget_get_has_window(border)) + //g_signal_connect(G_OBJECT(border), "expose-event", G_CALLBACK(cb_draw_background), (gpointer)this); + ON_DRAW_BEFORE(border, this, cb_background_expose, cb_background_draw); + + if (frame) + ON_DRAW_BEFORE(frame, this, cb_frame_expose, cb_frame_draw); +//#endif + + /*else if (!isTopLevel()) + { + fprintf(stderr, "clip by parent\n"); + g_signal_connect(G_OBJECT(border), "expose-event", G_CALLBACK(cb_clip_by_parent), (gpointer)this); + }*/ + +#ifndef GTK3 + if (isContainer() && !gtk_widget_get_has_window(widget)) + g_signal_connect(G_OBJECT(widget), "expose-event", G_CALLBACK(cb_clip_children), (gpointer)this); +#endif + + //if (isContainer() && widget != border) + // g_signal_connect(G_OBJECT(widget), "size-allocate", G_CALLBACK(cb_size_allocate), (gpointer)this); + + gtk_widget_add_events(widget, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK + | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK + | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); + + if (widget != border && (GTK_IS_WINDOW(border) || (GTK_IS_EVENT_BOX(border) && !gtk_event_box_get_visible_window(GTK_EVENT_BOX(border))))) + { + gtk_widget_add_events(border, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK + | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK + | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); + } + + registerControl(); + updateFont(); +} + +void gControl::realizeScrolledWindow(GtkWidget *wid, bool doNotRealize) +{ + _scroll = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL)); + +#ifdef GTK3 + PATCH_CLASS(_scroll, GTK_TYPE_SCROLLED_WINDOW) + PATCH_CLASS(wid, GTK_TYPE_TEXT_VIEW) +#endif + +#ifdef GTK3 + border = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_hexpand(wid, TRUE); +#else + border = gtk_alignment_new(0, 0, 1, 1); +#endif + gtk_widget_set_redraw_on_allocate(border, TRUE); + widget = wid; + frame = border; + _no_auto_grab = true; + + //gtk_container_add(GTK_CONTAINER(border), GTK_WIDGET(_scroll)); + gtk_scrolled_window_set_policy(_scroll, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(_scroll, GTK_SHADOW_NONE); + gtk_container_add(GTK_CONTAINER(border), GTK_WIDGET(_scroll)); + gtk_container_add(GTK_CONTAINER(_scroll), widget); + + if (!doNotRealize) + realize(false); + else + registerControl(); + + updateFont(); +} + +void gControl::updateBorder() +{ + int pad; + + if (!frame) + return; + +#if GTK3 + if (!GTK_IS_BOX(frame)) +#else + if (!GTK_IS_ALIGNMENT(frame)) +#endif + { + refresh(); + return; + } + + switch (frame_border) + { + case BORDER_NONE: pad = 0; break; + case BORDER_PLAIN: pad = 1; break; + default: pad = gApplication::getFrameWidth(); break; + } + + if ((int)frame_padding > pad) + pad = frame_padding; + +#if GTK3 + g_object_set(widget, "margin", pad, NULL); +#else + gtk_alignment_set_padding(GTK_ALIGNMENT(frame), pad, pad, pad, pad); + refresh(); +#endif + //gtk_widget_queue_draw(frame); +} + +int gControl::getFrameWidth() +{ + guint p; + + if (frame) + { +#if GTK3 + if (GTK_IS_BOX(frame)) + { + g_object_get(widget, "margin", &p, NULL); + return p; + } +#else + if (GTK_IS_ALIGNMENT(frame)) + { + gtk_alignment_get_padding(GTK_ALIGNMENT(frame), &p, NULL, NULL, NULL); + return p; + } +#endif + } + + /*if (_scroll) + { + if (gtk_scrolled_window_get_shadow_type(_scroll) == GTK_SHADOW_NONE) + return 0; + else + return gApplication::getFrameWidth(); + }*/ + + switch (frame_border) + { + case BORDER_NONE: p = 0; break; + case BORDER_PLAIN: p = 1; break; + default: p = gApplication::getFrameWidth(); break; + } + return p; +} + +void gControl::setFrameBorder(int border) +{ + if (border < BORDER_NONE || border > BORDER_ETCHED) + return; + + frame_border = border; + updateBorder(); +} + +bool gControl::hasBorder() const +{ + return getFrameBorder() != BORDER_NONE; +} + +void gControl::setBorder(bool vl) +{ + setFrameBorder(vl ? BORDER_SUNKEN : BORDER_NONE); + _has_border = vl; +} + + +void gControl::setFramePadding(int padding) +{ + if (padding < 0) + padding = 0; + frame_padding = padding; + updateBorder(); +} + + +void gControl::setName(char *name) +{ + if (_name) g_free(_name); + _name = NULL; + if (name) _name = g_strdup(name); +} + +#ifdef GTK3 + +GtkWidget *gControl::getStyleSheetWidget() +{ + return border; +} + +void gControl::updateStyleSheet() +{ + static int count = 0; + + GtkWidget *wid; + GtkStyleContext *context; + char *css = NULL; + const char *name; + char buffer[128]; + int s; + + wid = getStyleSheetWidget(); + context = gtk_widget_get_style_context(wid); + + if (_bg == COLOR_DEFAULT && _fg == COLOR_DEFAULT && !_font) + { + if (_css) + gtk_style_context_remove_provider(context, _css); + } + else + { + if (!_css) + { + count++; + sprintf(buffer, "g%d", count); + gtk_widget_set_name(wid, buffer); + + _css = GTK_STYLE_PROVIDER(gtk_css_provider_new()); + } + else + gtk_style_context_remove_provider(context, _css); + + name = gtk_widget_get_name(wid); + sprintf(buffer, "#%s {\ntransition:none;\n", name); + g_stradd(&css, buffer); + + if (_bg != COLOR_DEFAULT) + { + g_stradd(&css, "background-color:"); + gt_to_css_color(buffer, _bg); + g_stradd(&css, buffer); + g_stradd(&css, ";\nbackground-image:none;\n"); + } + + if (_fg != COLOR_DEFAULT) + { + g_stradd(&css, "color:"); + gt_to_css_color(buffer, _fg); + g_stradd(&css, buffer); + g_stradd(&css, ";\n"); + } + + if (_font) + { + if (_font->_name_set) + { + g_stradd(&css, "font-family:\""); + g_stradd(&css, _font->name()); + g_stradd(&css, "\";\n"); + } + + if (_font->_size_set) + { + g_stradd(&css, "font-size:"); + s = (int)(_font->size() * 10 + 0.5); + sprintf(buffer, "%dpt;\n", s / 10); //, s % 10); + g_stradd(&css, buffer); + } + + if (_font->_bold_set) + { + g_stradd(&css, "font-weight:"); + g_stradd(&css, _font->bold() ? "bold" : "normal"); + g_stradd(&css, ";\n"); + } + + if (_font->_italic_set) + { + g_stradd(&css, "font-style:"); + g_stradd(&css, _font->italic() ? "italic" : "normal"); + g_stradd(&css, ";\n"); + } + + if (_font->_underline_set || _font->_strikeout_set) + { + g_stradd(&css, "text-decoration-line:"); + if (_font->strikeout()) + g_stradd(&css, "line-through"); + else if (_font->underline()) + g_stradd(&css, "underline"); + else + g_stradd(&css, "none"); + g_stradd(&css, ";\n"); + } + } + + g_stradd(&css, "}\n"); + + //fprintf(stderr, "---- %s\n%s", _name, css); + + gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(_css), css, -1, NULL); + gtk_style_context_add_provider(context, _css, GTK_STYLE_PROVIDER_PRIORITY_USER + 10); + } +} + +gColor gControl::realBackground(bool no_default) +{ + if (_bg != COLOR_DEFAULT) + return _bg; + else if (pr) + return pr->realBackground(no_default); + else + return no_default ? gDesktop::bgColor() : COLOR_DEFAULT; +} + +gColor gControl::background() +{ + return _bg; +} + +void gControl::setRealBackground(gColor color) +{ +} + +void gControl::setBackground(gColor color) +{ + _bg = color; + updateStyleSheet(); + //gt_widget_set_color(border, FALSE, _bg, _bg_name, &_bg_default); + updateColor(); +} + +gColor gControl::realForeground(bool no_default) +{ + if (_fg != COLOR_DEFAULT) + return _fg; + else if (pr) + return pr->realForeground(no_default); + else + return no_default ? gDesktop::fgColor() : COLOR_DEFAULT; +} + +gColor gControl::foreground() +{ + return _fg; +} + +void gControl::setRealForeground(gColor color) +{ +} + +void gControl::setForeground(gColor color) +{ + _fg = color; + updateStyleSheet(); + //gt_widget_set_color(border, TRUE, _fg, _fg_name, &_fg_default); + updateColor(); +} + +#else + +gColor gControl::realBackground(bool no_default) +{ + if (_bg_set) + return use_base ? get_gdk_base_color(widget, isEnabled()) : get_gdk_bg_color(widget, isEnabled()); + else if (pr) + return pr->realBackground(no_default); + else + return no_default ? gDesktop::bgColor() : COLOR_DEFAULT; +} + +gColor gControl::background() +{ + return _bg; +} + +static void set_background(GtkWidget *widget, gColor color, bool use_base) +{ + if (use_base) + set_gdk_base_color(widget, color); + else + set_gdk_bg_color(widget, color); +} + +void gControl::setRealBackground(gColor color) +{ + set_background(border, color, use_base); + if (border != frame && GTK_IS_WIDGET(frame)) + set_background(frame, color, use_base); + if (frame != widget) + set_background(widget, color, use_base); +} + +void gControl::setBackground(gColor color) +{ + _bg = color; + _bg_set = color != COLOR_DEFAULT; + + if (!_bg_set) + { + if (pr && !use_base) + color = pr->realBackground(); + } + + setRealBackground(color); +} + +gColor gControl::realForeground(bool no_default) +{ + if (_fg_set) + return use_base ? get_gdk_text_color(widget, isEnabled()) : get_gdk_fg_color(widget, isEnabled()); + else if (pr) + return pr->realForeground(no_default); + else + return no_default ? gDesktop::fgColor() : COLOR_DEFAULT; +} + +gColor gControl::foreground() +{ + return _fg; +} + +static void set_foreground(GtkWidget *widget, gColor color, bool use_base) +{ + if (use_base) + set_gdk_text_color(widget, color); + else + set_gdk_fg_color(widget, color); +} + +void gControl::setRealForeground(gColor color) +{ + set_foreground(widget, color, use_base); +} + +void gControl::setForeground(gColor color) +{ + _fg = color; + _fg_set = color != COLOR_DEFAULT; + + if (!_fg_set) + { + if (pr) + color = pr->realForeground(); + } + + setRealForeground(color); +} + +#endif + +void gControl::emit(void *signal) +{ + if (!signal || locked()) + return; + (*((void (*)(gControl *))signal))(this); +} + +void gControl::emit(void *signal, intptr_t arg) +{ + if (!signal || locked()) + return; + (*((void (*)(gControl *, intptr_t))signal))(this, arg); +} + +void gControl::reparent(gContainer *newpr, int x, int y) +{ + gContainer *oldpr; + bool was_visible = isVisible(); + + // newpr can be equal to pr: for example, to move a control for one + // tab to another tab of the same TabStrip! + + if (!newpr || !newpr->getContainer()) + return; + + if (pr == newpr && pr->getContainer() == newpr->getContainer()) + return; + + if (was_visible) hide(); + //gtk_widget_unrealize(border); + + oldpr = pr; + pr = newpr; + + if (oldpr == newpr) + { + gt_widget_reparent(border, newpr->getContainer()); + oldpr->performArrange(); + } + else + { + if (oldpr) + { + gt_widget_reparent(border, newpr->getContainer()); + oldpr->remove(this); + oldpr->performArrange(); + } + + newpr->insert(this); + } + + //gtk_widget_realize(border); + move(x, y); + if (was_visible) + { + //fprintf(stderr, "was_visible\n"); + show(); + } +} + +int gControl::scrollX() +{ + if (!_scroll) + return 0; + + return (int)gtk_adjustment_get_value(gtk_scrolled_window_get_hadjustment(_scroll)); +} + +int gControl::scrollY() +{ + if (!_scroll) + return 0; + + return (int)gtk_adjustment_get_value(gtk_scrolled_window_get_vadjustment(_scroll)); +} + +void gControl::setScrollX(int vl) +{ + GtkAdjustment* adj; + int max; + + if (!_scroll) + return; + + adj = gtk_scrolled_window_get_hadjustment(_scroll); + + max = (int)(gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj)); + + if (vl < 0) + vl = 0; + else if (vl > max) + vl = max; + + gtk_adjustment_set_value(adj, (gdouble)vl); +} + +void gControl::setScrollY(int vl) +{ + GtkAdjustment* adj; + int max; + + if (!_scroll) + return; + + adj = gtk_scrolled_window_get_vadjustment(_scroll); + + max = (int)(gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj)); + + if (vl < 0) + vl = 0; + else if (vl > max) + vl = max; + + gtk_adjustment_set_value(adj, (gdouble)vl); +} + +void gControl::scroll(int x, int y) +{ + setScrollX(x); + setScrollY(y); +} + +/*int gControl::scrollWidth() +{ + return widget->requisition.width; +} + +int gControl::scrollHeight() +{ + return widget->requisition.height; +}*/ + +void gControl::setScrollBar(int vl) +{ + if (!_scroll) + return; + + _scrollbar = vl & 3; + updateScrollBar(); +} + +void gControl::updateScrollBar() +{ + if (!_scroll) + return; + + switch(_scrollbar) + { + case SCROLL_NONE: + gtk_scrolled_window_set_policy(_scroll, GTK_POLICY_NEVER, GTK_POLICY_NEVER); + break; + case SCROLL_HORIZONTAL: + gtk_scrolled_window_set_policy(_scroll, GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); + break; + case SCROLL_VERTICAL: + gtk_scrolled_window_set_policy(_scroll, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + break; + case SCROLL_BOTH: + gtk_scrolled_window_set_policy(_scroll, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + break; + } +} + +int gControl::minimumHeight() const +{ + return 0; +} + +int gControl::minimumWidth() const +{ + return 0; +} + +bool gControl::isTracking() const +{ + if (_proxy) + return _proxy->isTracking(); + else + return _tracking; +} + +void gControl::setTracking(bool v) +{ + if (_proxy) + _proxy->setTracking(v); + else + _tracking = v; + /* + GtkWidget *wid; + + if (GTK_IS_EVENT_BOX(border)) + wid = border; + else + wid = widget; + + if (v != _tracking) + { + uint event_mask = gtk_widget_get_events(wid); + _tracking = v; + if (v) + { + _old_tracking = event_mask & GDK_POINTER_MOTION_MASK; + event_mask |= GDK_POINTER_MOTION_MASK; + } + else + { + event_mask &= ~GDK_POINTER_MOTION_MASK; + } + + if (!_old_tracking) + { + gtk_widget_unrealize(wid); + gtk_widget_set_events(wid, event_mask); + gtk_widget_realize(wid); + } + } + */ +} + +bool gControl::grab() +{ + gControl *old_control_grab; + bool save_tracking; + + if (_grab) + return false; + + if (gt_grab(border, FALSE, gApplication::lastEventTime())) + return true; + + _grab = true; + save_tracking = _tracking; + _tracking = true; + + old_control_grab = gApplication::_control_grab; + gApplication::_control_grab = this; + + gApplication::enterLoop(this); + + gApplication::_control_grab = old_control_grab; + + gt_ungrab(); + + _tracking = save_tracking; + _grab = false; + return false; +} + +bool gControl::hovered() +{ + //int x, y, xm, ym; + + if (!isVisible()) + return false; + else + return _inside; + + /*getScreenPos(&x, &y); + gMouse::getScreenPos(&xm, &ym); + + return (xm >= x && ym >= y && xm < (x + width()) && ym < (y + height()));*/ +} + +bool gControl::setProxy(gControl *proxy) +{ + gControl *check = proxy; + + while (check) + { + if (check == this) + return true; + + check = check->_proxy; + } + + if (_proxy) + _proxy->_proxy_for = NULL; + + _proxy = proxy; + + if (_proxy) + _proxy->_proxy_for = this; + + return false; +} + +void gControl::setNoTabFocus(bool v) +{ + if (_no_tab_focus == v) + return; + + _no_tab_focus = v; + if (pr) + pr->updateFocusChain(); +} + +void gControl::emitEnterEvent(bool no_leave) +{ + gContainer *cont; + + //fprintf(stderr, "start enter %s\n", name()); + + if (parent()) + parent()->emitEnterEvent(true); + + if (!no_leave && isContainer()) + { + cont = (gContainer *)this; + int i; + + for (i = 0; i < cont->childCount(); i++) + cont->child(i)->emitLeaveEvent(); + } + + gApplication::_enter = this; + + if (gApplication::_leave) + { + if (gApplication::_leave == this || gApplication::_leave->isAncestorOf(this)) + gApplication::_leave = NULL; + } + + if (_inside) + return; + _inside = true; + + //fprintf(stderr, "end enter %s\n", name()); + + setMouse(mouse()); + + if (gApplication::_ignore_until_next_enter) + { + //fprintf(stderr, "ignore next enter for %s\n", name()); + if (gApplication::_ignore_until_next_enter == this) + gApplication::_ignore_until_next_enter = NULL; + return; + } + + //fprintf(stderr, "RAISE ENTER: %s\n", name()); + emit(SIGNAL(onEnterLeave), gEvent_Enter); +} + +void gControl::emitLeaveEvent() +{ + if (gApplication::_enter == this) + gApplication::_enter = NULL; + + if (!_inside) + return; + + //fprintf(stderr, "start leave %s\n", name()); + + if (isContainer()) + { + gContainer *cont = (gContainer *)this; + int i; + + for (i = 0; i < cont->childCount(); i++) + cont->child(i)->emitLeaveEvent(); + } + + _inside = false; + + //fprintf(stderr, "end leave %s\n", name()); + + if (parent()) parent()->setMouse(parent()->mouse()); + + if (gApplication::_ignore_until_next_enter) + { + //fprintf(stderr, "ignore next leave for %s\n", name()); + return; + } + + //fprintf(stderr, "RAISE LEAVE: %s\n", name()); + emit(SIGNAL(onEnterLeave), gEvent_Leave); +} + +bool gControl::isAncestorOf(gControl *child) +{ + if (!isContainer()) + return false; + + for(;;) + { + child = child->parent(); + if (!child) + return false; + else if (child == this) + return true; + } +} + +#ifdef GTK3 +void gControl::drawBackground(cairo_t *cr) +{ + if (background() == COLOR_DEFAULT) + return; + + //fprintf(stderr, "gControl::drawBackground\n"); + + gt_cairo_set_source_color(cr, background()); + cairo_rectangle(cr, 0, 0, width(), height()); + cairo_fill(cr); +} +#else +void gControl::drawBackground(GdkEventExpose *e) +{ + GtkAllocation a; + + if (background() == COLOR_DEFAULT) + return; + + cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(border)); + + gdk_cairo_region(cr, e->region); + cairo_clip(cr); + gt_cairo_set_source_color(cr, background()); + + gtk_widget_get_allocation(border, &a); + cairo_rectangle(cr, a.x, a.y, width(), height()); + cairo_fill(cr); + + cairo_destroy(cr); +} +#endif + +bool gControl::canFocus() const +{ +#if GTK_CHECK_VERSION(2, 18, 0) + return gtk_widget_get_can_focus(widget); +#else + return GTK_WIDGET_CAN_FOCUS(widget); +#endif +} + +void gControl::setCanFocus(bool vl) +{ + if (vl == canFocus()) + return; + + gtk_widget_set_can_focus(widget, vl); + + /*_has_input_method = vl; + + if (_input_method && !vl) + { + g_object_unref(_input_method); + _input_method = NULL; + } + else if (!_input_method && vl) + { + _input_method = gtk_im_multicontext_new(); + }*/ + + if (pr) + pr->updateFocusChain(); +} + +#ifdef GTK3 +void gControl::updateColor() +{ +} + +void gControl::setColorNames(const char *bg_names[], const char *fg_names[]) +{ + _bg_name_list = bg_names; + _fg_name_list = fg_names; + + if (!bg_names) + { + _bg_name = NULL; + _fg_name = NULL; + use_base = FALSE; + return; + } + + gt_style_lookup_color(gtk_widget_get_style_context(widget), bg_names, &_bg_name, &_bg_default); + gt_style_lookup_color(gtk_widget_get_style_context(widget), fg_names, &_fg_name, &_fg_default); +} + +void gControl::setColorBase() +{ + static const char *bg_names[] = { "base_color", "theme_base_color", NULL }; + static const char *fg_names[] = { "text_color", "theme_text_color", NULL }; + setColorNames(bg_names, fg_names); + use_base = TRUE; +} + +void gControl::setColorButton() +{ + const char *bg_names[] = { "button_bg_color", "theme_button_bg_color", "theme_bg_color", NULL }; + const char *fg_names[] = { "button_fg_color", "theme_button_fg_color", "theme_fg_color", NULL }; + setColorNames(bg_names, fg_names); + use_base = FALSE; +} +#endif + +GtkIMContext *gControl::getInputMethod() +{ + return _input_method; +} diff --git a/gb.gtk/src/gcontrol.h b/gb.gtk/src/gcontrol.h new file mode 100644 index 00000000..7c7999ff --- /dev/null +++ b/gb.gtk/src/gcontrol.h @@ -0,0 +1,320 @@ +/*************************************************************************** + + gcontrol.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GCONTROL_H +#define __GCONTROL_H + +#include "gcolor.h" +#include "gdrag.h" + +class gContainer; +class gMainWindow; + +class gControl +{ +public: + gControl(); + gControl(gContainer *parent); + virtual ~gControl(); + + void *hFree; + +// "Properties" + int getClass() const { return g_typ; } + + bool isContainer() const { return (g_typ & 0x100) != 0; } + bool isWindow() const; + bool isTopLevel() const { return pr == NULL; } + bool isDestroyed() const { return _destroyed; } + + gMainWindow *window(); + gMainWindow *topLevel(); + + gContainer *parent() const { return pr; } + bool isAncestorOf(gControl *child); + gCursor* cursor(); + bool design(); + virtual bool isEnabled() const; + bool expand() const { return expa; } + bool ignore() const { return igno; } + bool hovered(); + virtual int handle(); + + int left() const { return bufX; } + int x() const { return left(); } + int top() const { return bufY; } + int y() const { return top(); } + int width() const { return bufW; } + int height() const { return bufH; } + + int mouse(); + gControl *next(); + gControl *previous(); + int screenX(); + int screenY(); + virtual bool getScreenPos(int *x, int *y); + char *toolTip(); + bool isVisible() const { return visible; } + bool isReallyVisible(); + bool acceptDrops() { return _accept_drops; } + char *name() { return _name; } + void setName(char *name); + bool action() { return _action; } + void setAction(bool v) { _action = v; } + + void setCursor(gCursor *vl); + void setAcceptDrops(bool vl); + void setDesign(bool vl); + virtual void setEnabled(bool vl); + void setExpand (bool vl); + void setIgnore (bool vl); + virtual void setHeight(int h); + void setLeft(int l); + void setMouse(int m); + virtual void updateCursor(GdkCursor *cursor); + void setToolTip(char *vl); + void setTop(int t); + virtual void setVisible(bool v); + virtual void setWidth(int w); + void setPrevious(gControl *prev); + void setNext(gControl *next); + + bool isTracking() const; + void setTracking(bool vl); + + bool isNoTabFocus() const { return _no_tab_focus; } + void setNoTabFocus(bool v); + + gColor background(); + gColor foreground(); + virtual void setBackground(gColor color = COLOR_DEFAULT); + virtual void setForeground(gColor color = COLOR_DEFAULT); + gColor realBackground(bool no_default = false); + gColor realForeground(bool no_default = false); + virtual void setRealBackground(gColor color); + virtual void setRealForeground(gColor color); + + virtual gFont *font(); + void actualFontTo(gFont *ft); + virtual void setFont(gFont *ft); + bool ownFont() { return _font != NULL; } + virtual void updateFont(); + virtual void updateSize(); +#ifdef GTK3 + virtual GtkWidget *getStyleSheetWidget(); + void updateStyleSheet(); + virtual void updateColor(); + void setColorNames(const char *bg_names[], const char *fg_names[]); + void setColorBase(); + void setColorButton(); +#else + void setColorNames(const char **, const char **) {} + void setColorBase() { use_base = TRUE; } + void setColorButton() { use_base = FALSE; } +#endif + + bool canFocus() const; + void setCanFocus(bool vl); + + gControl *proxy() const { return _proxy; } + bool setProxy(gControl *proxy); + + int scrollX(); + int scrollY(); + void scroll(int x, int y); + void setScrollX(int vl); + void setScrollY(int vl); + //virtual int scrollWidth(); + //virtual int scrollHeight(); + int scrollBar() const { return _scrollbar; } + void setScrollBar(int vl); + virtual void updateScrollBar(); + +// "Methods" + void dragText(char *txt, char *format = NULL) { gDrag::dragText(this, txt, format); } + void dragImage(gPicture *pic) { gDrag::dragImage(this, pic); } + + virtual void reparent(gContainer *newpr, int x, int y); + void hide() { setVisible(false); } + void lower(); + void raise(); + virtual void move(int x, int y); + virtual void resize(int w, int h); + virtual void moveResize(int x, int y, int w, int h); + virtual void setFocus(); + bool hasFocus() const; +#if GTK_CHECK_VERSION(3, 2, 0) + bool hasVisibleFocus() const; +#else + bool hasVisibleFocus() const { return hasFocus(); } +#endif + void resize() { resize(width(), height()); } + void show() { setVisible(true); } + void refresh(); + void refresh(int x, int y, int w, int h); + virtual void afterRefresh(); + bool grab(); + void destroy(); + void destroyNow() { destroy(); cleanRemovedControls(); } + + void lock() { _locked++; } + void unlock() { _locked--; } + bool locked() { return _locked; } + + void getGeometry(GdkRectangle *rect) const { rect->x = bufX; rect->y = bufY; rect->width = bufW; rect->height = bufH; } + void setGeometry(GdkRectangle *rect) { moveResize(rect->x, rect->y, rect->width, rect->height); } + + void emit(void *signal); + void emit(void *signal, intptr_t arg); + void emit(void *signal, char *arg) { emit(signal, (intptr_t)arg); } + +// "Signals" + bool (*canRaise)(gControl *sender, int type); + void (*onFinish)(gControl *sender); // Special + void (*onFocusEvent)(gControl *sender, int type); + bool (*onKeyEvent)(gControl *sender, int type); + bool (*onMouseEvent)(gControl *sender, int type); + void (*onEnterLeave)(gControl *sender, int type); + bool (*onDrag)(gControl *sender); + bool (*onDragMove)(gControl *sender); + bool (*onDrop)(gControl *sender); + void (*onDragLeave)(gControl *sender); + //void (*onMove)(gControl *sender); + //void (*onResize)(gControl *sender); + +// "Private" + gint bufW,bufH,bufX,bufY; + gCursor *curs; + gFont *_font; + gFont *_resolved_font; + GtkWidget *widget; + GtkWidget *border; + GtkWidget *frame; + GtkScrolledWindow *_scroll; + short g_typ; + short _mouse; + gControl *_proxy, *_proxy_for; + gColor _bg, _fg; +#ifdef GTK3 + GtkStyleProvider *_css; + const char *_bg_name; + const char **_bg_name_list; + GdkRGBA _bg_default; + const char *_fg_name; + const char **_fg_name_list; + GdkRGBA _fg_default; +#endif + + unsigned dsg : 1; + unsigned expa : 1; + unsigned igno : 1; + unsigned _action : 1; // *reserved* + unsigned _accept_drops : 1; // If the control accepts drops + unsigned _drag_get_data : 1; // If we got information on the dragged data + unsigned _drag_enter : 1; // If we have entered the control for drag & drop + unsigned _tracking : 1; // If we are tracking mouse move even if no mouse button is pressed + + unsigned _old_tracking : 1; // real value when Tracking is false + unsigned _bg_set : 1; // Have a private background + unsigned _fg_set : 1; // Have a private foreground + unsigned have_cursor : 1; // If gApplication::setBusy() must update the cursor + unsigned use_base : 1; // Use base and text color for foreground and background + unsigned visible : 1; // A control can be hidden if its width or height is zero + unsigned _destroyed : 1; // If the control has already been added to the destroy list + + unsigned _locked : 4; // For locking events + unsigned frame_border : 4; + unsigned frame_padding : 8; + + unsigned _scrollbar : 2; + unsigned _dirty_pos : 1; // If the position of the widget has changed + unsigned _dirty_size : 1; // If the size of the widget has changed + unsigned _no_delete : 1; // Do not delete on destroy signal + unsigned _has_input_method : 1; // Has its own input method management + unsigned _no_default_mouse_event : 1; // No default mouse events + unsigned _grab : 1; // control is currently grabbing mouse and keyboard + unsigned _has_border : 1; // if the control has a border + unsigned _no_tab_focus : 1; // Don't put inside focus chain + unsigned _inside : 1; // if we got an enter event, but not a leave event yet. + unsigned _no_auto_grab : 1; // do not automatically grab widget on button press event + unsigned _no_background : 1; // Don't draw the background automatically + unsigned _use_wheel : 1; // Do not propagate the mouse wheel event + + void removeParent() { pr = NULL; } + void initSignals(); + void borderSignals(); + void widgetSignals(); + void connectParent(); + void setParent(gContainer *parent) { pr = parent; } + void initAll(gContainer *pr); + void realize(bool make_frame = false); + void realizeScrolledWindow(GtkWidget *wid, bool doNotRealize = false); + void registerControl(); + void updateGeometry(); + bool mustUpdateCursor() { return mouse() != -1 || have_cursor; } + + bool hasInputMethod() { return _has_input_method; } + virtual GtkIMContext *getInputMethod(); + + GdkCursor *getGdkCursor(); + virtual void updateBorder(); + int getFrameBorder() const { return frame_border; } + void setFrameBorder(int border); + void setBorder(bool b); + bool hasBorder() const; + int getFramePadding() const { return frame_padding; } + void setFramePadding(int padding); + virtual int getFrameWidth(); + virtual gColor getFrameColor(); +#ifdef GTK3 + void drawBorder(cairo_t *cr); + void drawBackground(cairo_t *cr); +#else + void drawBorder(GdkEventExpose *e); + void drawBackground(GdkEventExpose *e); +#endif + + virtual int minimumHeight() const; + virtual int minimumWidth() const; + void resolveFont(); + + void emitEnterEvent(bool no_leave = false); + void emitLeaveEvent(); + +/* static gControl* dragWidget(); + static void setDragWidget(gControl *ct); + static char *dragTextBuffer(); + static GdkPixbuf *dragPictureBuffer(); + static void freeDragBuffer();*/ + static GList* controlList(); + static void cleanRemovedControls(); + +private: + gContainer *pr; + char *_name; + GtkIMContext *_input_method; +}; + +#define SIGNAL(_signal) ((void *)_signal) + +#endif diff --git a/gb.gtk/src/gcursor.cpp b/gb.gtk/src/gcursor.cpp new file mode 100644 index 00000000..e7ae3f9c --- /dev/null +++ b/gb.gtk/src/gcursor.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** + + gcursor.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gb.form.font.h" + +#ifndef GAMBAS_DIRECTFB +#ifdef GDK_WINDOWING_X11 +#include +#include +#include +#include "x11.h" +#endif +#endif + +#include "gcursor.h" + +gCursor::gCursor(gPicture *pic, int px, int py) +{ + static bool check = false; + GdkDisplay *dp = gdk_display_get_default(); + + if (!check) + { + if (!gdk_display_supports_cursor_color(dp) || !gdk_display_supports_cursor_alpha(dp)) + fprintf(stderr, "gb.gtk: warning: RGBA cursors are not supported\n"); + check = true; + } + + x = px; + y = py; + cur = NULL; + if (!pic || pic->isVoid()) + return; + + if (pic->width() <= x) + x = pic->width() - 1; + if (pic->height() <= y) + y = pic->height() - 1; + + cur=gdk_cursor_new_from_pixbuf(dp, pic->getPixbuf(), x, y); +} + +gCursor::gCursor(gCursor *cursor) +{ + cur = NULL; + if (!cursor) return; + if (!cursor->cur) return; + + cur = cursor->cur; + x = cursor->x; + y = cursor->y; + if (cur) +#ifdef GTK3 + g_object_ref(cur); +#else + gdk_cursor_ref(cur); +#endif +} + +gCursor::~gCursor() +{ + if (cur) +#ifdef GTK3 + g_object_unref(cur); +#else + gdk_cursor_unref(cur); +#endif +} diff --git a/gb.gtk/src/gcursor.h b/gb.gtk/src/gcursor.h new file mode 100644 index 00000000..27451aa3 --- /dev/null +++ b/gb.gtk/src/gcursor.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + gcursor.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GCURSOR_H +#define __GCURSOR_H + +#define CURSOR_DEFAULT GDK_X_CURSOR +#define CURSOR_CUSTOM GDK_CURSOR_IS_PIXMAP + +class gPicture; + +class gCursor +{ +public: + gCursor(gPicture *pic,int x,int y); + gCursor(gCursor *cursor); + ~gCursor(); + +//"Properties" + int left() const { return x; } + int top() const { return y; } + +//"Private" + GdkCursor *cur; + int x; + int y; +}; + +#endif diff --git a/gb.gtk/src/gdesktop.cpp b/gb.gtk/src/gdesktop.cpp new file mode 100644 index 00000000..c15b3eb9 --- /dev/null +++ b/gb.gtk/src/gdesktop.cpp @@ -0,0 +1,402 @@ +/*************************************************************************** + + gdesktop.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gb.form.font.h" + +#ifndef GAMBAS_DIRECTFB +#ifdef GDK_WINDOWING_X11 +#include +#include +#include +#include "x11.h" +#endif +#endif + +#include "gapplication.h" +#include "gmainwindow.h" +#include "gdesktop.h" + +/*********************************************************************** + +Desktop + +************************************************************************/ + +gFont *gDesktop::_desktop_font = NULL; +int gDesktop::_desktop_scale = 0; + +bool gDesktop::rightToLeft() +{ + return MAIN_rtl; //gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL; +} + +void gDesktop::init() +{ + _desktop_font = new gFont(); + _desktop_font->setAll(true); + _desktop_scale = 0; +} + +void gDesktop::exit() +{ + gFont::assign(&_desktop_font); +} + +gFont* gDesktop::font() +{ + if (!_desktop_font) gDesktop::init(); + return _desktop_font; +} + +void gDesktop::setFont(gFont *ft) +{ + GList *iter; + gControl *control; + + gFont::set(&_desktop_font, ft->copy()); + _desktop_scale = 0; + + iter = g_list_first(gControl::controlList()); + + while (iter) + { + control = (gControl *)iter->data; + control->updateFont(); + iter = g_list_next(iter); + } +} + +/*gControl* gDesktop::activeControl() +{ + gControl *test, *curr=NULL; + GList *iter=gControl::controlList(); + + if (!iter) return NULL; + + iter=g_list_first(iter); + while (iter) + { + test=(gControl*)iter->data; + if (test->hasFocus()) + { + curr=test; + break; + } + iter=iter->next; + } + + return curr; + +}*/ + +gMainWindow* gDesktop::activeWindow() +{ + return gMainWindow::_active ? gMainWindow::_active->topLevel() : NULL; +} + +#ifdef GTK3 +static gColor get_color(GType type, gColor default_color, GtkStateFlags state, bool fg, bool text) +{ + static const char *bg_names[] = { "bg_color", "theme_bg_color", NULL }; + static const char *fg_names[] = { "fg_color", "theme_fg_color", NULL }; + static const char *base_bg_names[] = { "base_color", "theme_base_color", NULL }; + static const char *base_fg_names[] = { "text_color", "theme_text_color", NULL }; + static const char *sel_bg_names[] = { "selected_bg_color", "theme_selected_bg_color", NULL }; + static const char *sel_fg_names[] = { "selected_fg_color", "theme_selected_fg_color", NULL }; + static const char *tt_bg_names[] = { "tooltip_bg_color", "theme_tooltip_bg_color", NULL }; + static const char *tt_fg_names[] = { "tooltip_fg_color", "link_fg_color", "theme_tooltip_fg_color", NULL }; + static const char *link_fg_names[] = { "link_color", "theme_link_color", NULL }; + static const char *visited_fg_names[] = { "visited_link_color", "theme_visited_link_color", NULL }; + + GtkStyleContext *st = gt_get_style(type); + GdkRGBA rgba; + + if (type == GTK_TYPE_WINDOW || type == GTK_TYPE_BUTTON) + { + if (!gt_style_lookup_color(st, fg ? fg_names : bg_names, NULL, &rgba)) + goto __OK; + } + else if (type == GTK_TYPE_ENTRY) + { + if (state == GTK_STATE_FLAG_SELECTED) + { + if (!gt_style_lookup_color(st, fg ? sel_fg_names : sel_bg_names, NULL, &rgba)) + goto __OK; + } + else + { + if (!gt_style_lookup_color(st, fg ? base_fg_names : base_bg_names, NULL, &rgba)) + goto __OK; + } + } + else if (type == GTK_TYPE_TOOLTIP) + { + if (!gt_style_lookup_color(st, fg ? tt_fg_names : tt_bg_names, NULL, &rgba)) + goto __OK; + } + else if (type == GTK_TYPE_LINK_BUTTON) + { + if (!gt_style_lookup_color(st, fg ? link_fg_names : visited_fg_names, NULL, &rgba)) + goto __OK; + } + + if (!st) + return default_color; + + if (fg) + gtk_style_context_get_color(st, state, &rgba); + else + gtk_style_context_get_background_color(st, state, &rgba); + +__OK: + + if (rgba.alpha < 0.05) + rgba.alpha = 1; + + return gt_to_color(&rgba); +} +#else +static gColor get_color(GType type, gColor default_color, GtkStateType state, bool fg, bool text) +{ + GtkStyle *st = gt_get_style(type); + GdkColor *color; + + if (!st) + return default_color; + + if (text) + { + if (fg) + color = &st->text[state]; + else + color = &st->base[state]; + } + else + { + if (fg) + color = &st->fg[state]; + else + color = &st->bg[state]; + } + return get_gdk_color(color); +} +#endif + +gColor gDesktop::buttonfgColor() +{ + return get_color(GTK_TYPE_BUTTON, 0, STATE_NORMAL, true, false); +} + +gColor gDesktop::buttonbgColor() +{ + return get_color(GTK_TYPE_BUTTON, 0xC0C0C0, STATE_NORMAL, false, false); +} + +gColor gDesktop::fgColor() +{ + return get_color(GTK_TYPE_WINDOW, 0, STATE_NORMAL, true, false); +} + +gColor gDesktop::bgColor() +{ + return get_color(GTK_TYPE_WINDOW, 0xC0C0C0, STATE_NORMAL, false, false); +} + +gColor gDesktop::textfgColor() +{ + return get_color(GTK_TYPE_ENTRY, 0, STATE_NORMAL, true, true); +} + +gColor gDesktop::textbgColor() +{ + return get_color(GTK_TYPE_ENTRY, 0xFFFFFF, STATE_NORMAL, false, true); +} + +gColor gDesktop::selfgColor() +{ + return get_color(GTK_TYPE_ENTRY, 0xFFFFFF, STATE_SELECTED, true, true); +} + +gColor gDesktop::selbgColor() +{ + return get_color(GTK_TYPE_ENTRY, 0x0000FF, STATE_SELECTED, false, true); +} + +static int get_luminance(gColor col) +{ + int r, g, b; + gt_color_to_rgb(col, &r, &g, &b); + return (int)(0.299 * r + 0.587 * g + 0.114 * b); +} + +gColor gDesktop::tooltipForeground() +{ + int r, g, b; + int h, s, v; + gColor fg = get_color(GTK_TYPE_TOOLTIP, 0, STATE_NORMAL, true, false); + gColor bg = gDesktop::tooltipBackground(); + int lfg = get_luminance(fg); + int lbg = get_luminance(bg); + + if (abs(lfg - lbg) > 64) + return fg; + + gt_color_to_rgb(fg, &r, &g, &b); + gt_rgb_to_hsv(r, g, b, &h, &s, &v); + v = 255 - v; + gt_hsv_to_rgb(h, s, v, &r, &g, &b); + return gt_rgb_to_color(r, g, b); +} + +gColor gDesktop::tooltipBackground() +{ + return get_color(GTK_TYPE_TOOLTIP, 0xFFFFDF, STATE_NORMAL, false, false); +} + +gColor gDesktop::linkForeground() +{ + return get_color(GTK_TYPE_LINK_BUTTON, 0x0000FF, STATE_NORMAL, true, false); +} + +gColor gDesktop::visitedForeground() +{ +#ifdef GTK3 + return get_color(GTK_TYPE_LINK_BUTTON, 0x0080FF, STATE_NORMAL, false, false); +#else + int r, g ,b, h, s, v; + gt_color_to_rgb(linkForeground(), &r, &g, &b); + gt_rgb_to_hsv(r, g, b, &h, &s, &v); + h -= 30; + if (h < 0) + h += 360; + gt_hsv_to_rgb(h, s, v, &r, &g, &b); + return gt_rgb_to_color(r, g, b); +#endif +} + +gColor gDesktop::lightbgColor() +{ + uint col = IMAGE.MergeColor(gDesktop::selbgColor(), gDesktop::selfgColor(), 0.2); + return col; +} + +gColor gDesktop::lightfgColor() +{ + uint col = IMAGE.MergeColor(gDesktop::bgColor(), gDesktop::fgColor(), 0.2); + return col; +} + +int gDesktop::height() +{ + return gdk_screen_get_height(gdk_screen_get_default ()); +} + +int gDesktop::width() +{ + return gdk_screen_get_width(gdk_screen_get_default ()); +} + +int gDesktop::resolution() +{ + gdouble res = gdk_screen_get_resolution(gdk_screen_get_default()); + if (res == -1) + res = 96; + return res; +} + +int gDesktop::scale() +{ + if (!_desktop_scale) + { + gFont *ft = font(); + _desktop_scale = GET_DESKTOP_SCALE(ft->size(), resolution()); + } + +/* PangoLanguage *lng=NULL; + PangoContext* ct=gdk_pango_context_get(); + GtkStyle *sty=gtk_widget_get_default_style(); + PangoFontDescription *ft=sty->font_desc; + PangoFontMetrics* fm; + int val; + + if (!_desktop_scale) + { + if (getenv("LANG")) + lng = pango_language_from_string(getenv("LANG")); + + fm = pango_context_get_metrics (ct,ft,lng); + + val = 1 + (pango_font_metrics_get_ascent(fm) + pango_font_metrics_get_descent(fm)) / PANGO_SCALE; + val = GET_DESKTOP_SCALE(val); + pango_font_metrics_unref(fm); + g_object_unref(G_OBJECT(ct)); + + if (!val) val = 1; + _desktop_scale = val; + }*/ + + return _desktop_scale; +} + +gPicture* gDesktop::screenshot(int x, int y, int w, int h) +{ + return gt_grab_window(gdk_get_default_root_window(), x, y, w, h); +} + +int gDesktop::count() +{ +#ifdef GTK3 + return gdk_screen_get_n_monitors(gdk_screen_get_default()); +#else + return gdk_display_get_n_screens(gdk_display_get_default()); +#endif +} + +void gDesktop::geometry(int screen, GdkRectangle *rect) +{ + rect->x = rect->y = rect->width = rect->height = 0; + if (screen < 0 || screen >= count()) + return; + +#ifdef GTK3 + gdk_screen_get_monitor_geometry(gdk_screen_get_default(), screen, rect); +#else + rect->width = gdk_screen_get_width(gdk_display_get_screen(gdk_display_get_default(), screen)); + rect->height = gdk_screen_get_height(gdk_display_get_screen(gdk_display_get_default(), screen)); +#endif +} + +void gDesktop::availableGeometry(int screen, GdkRectangle *rect) +{ + rect->x = rect->y = rect->width = rect->height = 0; + if (screen < 0 || screen >= count()) + return; + +#ifdef GTK3 + gdk_screen_get_monitor_workarea(gdk_screen_get_default(), screen, rect); +#else + if (X11_get_available_geometry(screen, &rect->x, &rect->y, &rect->width, &rect->height)) + geometry(screen, rect); +#endif +} diff --git a/gb.gtk/src/gdesktop.h b/gb.gtk/src/gdesktop.h new file mode 100644 index 00000000..c4676cae --- /dev/null +++ b/gb.gtk/src/gdesktop.h @@ -0,0 +1,78 @@ +/*************************************************************************** + + gdesktop.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GDESKTOP_H +#define __GDESKTOP_H + +class gPicture; +class gMainWindow; +class gControl; +class gFont; + +class gDesktop +{ +public: + + static void init(); + static void exit(); + + static gColor buttonfgColor(); + static gColor buttonbgColor(); + static gColor fgColor(); + static gColor bgColor(); + static gColor textfgColor(); + static gColor textbgColor(); + static gColor selfgColor(); + static gColor selbgColor(); + static gColor lightbgColor(); + static gColor lightfgColor(); + static gColor tooltipForeground(); + static gColor tooltipBackground(); + static gColor linkForeground(); + static gColor visitedForeground(); + + static gFont* font(); + static void setFont(gFont *vl); + static int height(); + static int width(); + static int resolution(); + static int scale(); + static gPicture* screenshot(int x = 0, int y = 0, int w = 0, int h = 0); + static gMainWindow* activeWindow(); + + static bool rightToLeft(); + + static int count(); + static void geometry(int screen, GdkRectangle *rect); + static void availableGeometry(int screen, GdkRectangle *rect); + + static void geometry(GdkRectangle *rect) { geometry(0, rect); } + static void availableGeometry(GdkRectangle *rect) { availableGeometry(0, rect); } + +private: + + static int _desktop_scale; + static gFont *_desktop_font; +}; + +#endif diff --git a/gb.gtk/src/gdialog.h b/gb.gtk/src/gdialog.h new file mode 100644 index 00000000..be41b169 --- /dev/null +++ b/gb.gtk/src/gdialog.h @@ -0,0 +1,59 @@ +/*************************************************************************** + + gdialog.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GDIALOG_H +#define __GDIALOG_H + +#include "gcolor.h" + +class gDialog +{ +public: + static void exit(); + + static gColor color(); + static char** filter(int *nfilter); + static char** paths(); + static char* path(); + static char* title(); + static gFont* font(); + static bool showHidden(); + + static void setColor(gColor col); + static void setFilter(char **filter, int nfilter); + static void setPath(char *vl); + static void setTitle(char *title); + static void setFont(gFont *ft); + static void setShowHidden(bool v); + + static bool selectColor(); + static bool selectFolder(); + static bool selectFont(); + static bool openFile(bool multi=false); + static bool saveFile(); + +private: + static GPtrArray *_filter; +}; + +#endif diff --git a/gb.gtk/src/gdrag.cpp b/gb.gtk/src/gdrag.cpp new file mode 100644 index 00000000..5270826e --- /dev/null +++ b/gb.gtk/src/gdrag.cpp @@ -0,0 +1,897 @@ +/*************************************************************************** + + gdrag.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef GAMBAS_DIRECTFB +#ifdef GDK_WINDOWING_X11 +#include +#include +#include +#endif +#endif + +#include "widgets.h" +#include "gapplication.h" +#include "gmainwindow.h" +#include "gmouse.h" +#include "gclipboard.h" +#include "gdrag.h" + +//#define DEBUG_ME 1 + +#ifdef GTK3 +struct _GtkTargetList +{ + GList *list; + guint ref_count; +}; + +#if GTK_CHECK_VERSION(3, 12, 0) +#else +struct _GtkTargetPair +{ + GdkAtom target; + guint flags; + guint info; +}; +#endif + +typedef + struct _GtkTargetPair GtkTargetPair; +#endif + +static int _current_clipboard = gClipboard::Clipboard; +static GtkClipboard *_clipboard = NULL; +static GtkClipboard *_selection = NULL; +static bool _clipboard_has_changed[2] = { FALSE }; + +static char *convert_format(char *fmt) +{ + if (!strcmp(fmt, "STRING")) + return (char *)"text/plain"; + if (!strcmp(fmt, "UTF8_STRING")) + return (char *)"text/plain;charset=utf-8"; + return fmt; +} + +#if GTK_CHECK_VERSION(2, 14, 0) +#else +static gint gtk_selection_data_get_length(const GtkSelectionData *sel) +{ + return sel->length; +} + +static const guchar *gtk_selection_data_get_data(const GtkSelectionData *sel) +{ + return sel->data; +} +#endif + +#if GTK_CHECK_VERSION(2, 22, 0) +#else +static GList *gdk_drag_context_list_targets(GdkDragContext *context) +{ + return context->targets; +} +#endif + +/*********************************************************************** + + Clipboard + +************************************************************************/ + +static void cb_change(GtkClipboard *clipboard, GdkEvent *, gpointer) +{ + _clipboard_has_changed[clipboard == _selection ? gClipboard::Clipboard : gClipboard::Selection] = TRUE; +} + + +static GtkClipboard *get_clipboard() +{ + if (_current_clipboard == gClipboard::Selection) + { + if (!_selection) + { + _selection = gtk_clipboard_get(GDK_SELECTION_PRIMARY); + g_signal_connect(G_OBJECT(_selection), "owner-change", G_CALLBACK(cb_change), (gpointer)gClipboard::Clipboard); + } + return _selection; + } + else + { + if (!_clipboard) + { + _clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + g_signal_connect(G_OBJECT(_clipboard), "owner-change", G_CALLBACK(cb_change), (gpointer)gClipboard::Selection); + } + return _clipboard; + } +} + +bool gClipboard::hasChanged() +{ + return _clipboard_has_changed[_current_clipboard]; +} + +void gClipboard::setCurrent(int clipboard) +{ + _current_clipboard = clipboard; +} + +int gClipboard::getCurrent() +{ + return _current_clipboard; +} + +void gClipboard::clear() +{ + gtk_clipboard_clear(get_clipboard()); +} + +char *gClipboard::getFormat(int n) +{ + int i; + gint n_tg; + GdkAtom *targets; + char *fmt, *cfmt; + + if (!gtk_clipboard_wait_for_targets(get_clipboard(), &targets, &n_tg)) + return NULL; + + for (i = 0; i < n_tg; i++) + { + fmt = gdk_atom_name(targets[i]); + cfmt = convert_format(fmt); + if (!islower(cfmt[0])) + { + g_free(fmt); + continue; + } + if (n == 0) + { + gt_free_later(fmt); + return cfmt; + } + n--; + } + + return NULL; +} + +static void cb_clear_text(GtkClipboard *clipboard, gpointer text) +{ + if (text) g_free(text); +} + +static void cb_get_text(GtkClipboard *clipboard, GtkSelectionData *selection, guint info, gpointer text) +{ + gtk_selection_data_set(selection, gtk_selection_data_get_target(selection), 8, (guchar *)text, strlen((char *)text)); +} + +int gClipboard::getType() +{ + if (gtk_clipboard_wait_is_image_available(get_clipboard())) return Image; + if (gtk_clipboard_wait_is_text_available(get_clipboard())) return Text; + return Nothing; +} + +void gClipboard::setImage(gPicture *image) +{ + gtk_clipboard_set_image(get_clipboard(), image->getPixbuf()); +} + +gPicture * gClipboard::getImage() +{ + _clipboard_has_changed[_current_clipboard] = FALSE; + return new gPicture(gtk_clipboard_wait_for_image(get_clipboard())); +} + +static void +gt_clipboard_set_text (GtkClipboard *clipboard, char *format, const gchar *text, gint len) +{ + GtkTargetList *list; + GList *l; + GtkTargetEntry *targets; + gint n_targets, i; + + list = gtk_target_list_new (NULL, 0); + if (format) + gtk_target_list_add (list, gdk_atom_intern(format, false), 0, 0); + gtk_target_list_add_text_targets (list, 0); + + n_targets = g_list_length(list->list); + targets = g_new0 (GtkTargetEntry, n_targets); + for (l = list->list, i = 0; l; l = l->next, i++) + { + GtkTargetPair *pair = (GtkTargetPair *)l->data; + targets[i].target = gdk_atom_name (pair->target); + } + + if (len < 0) + len = strlen (text); + + gtk_clipboard_set_with_data (clipboard, + targets, n_targets, + cb_get_text, cb_clear_text, + g_strndup (text, len)); + gtk_clipboard_set_can_store (clipboard, NULL, 0); + + for (i = 0; i < n_targets; i++) + g_free (targets[i].target); + g_free (targets); + gtk_target_list_unref (list); +} + +void gClipboard::setText(char *text, int len, char *format) +{ + if (!text) + return; + + gt_clipboard_set_text(get_clipboard(), format, text, len); +} + +char *gClipboard::getText(int *len, const char *format) +{ + GdkAtom target = GDK_NONE; + gint n_tg; + GdkAtom *targets; + char *fmt; + int i; + GtkSelectionData *data; + char *text; + + *len = 0; + + if (!gtk_clipboard_wait_for_targets(get_clipboard(), &targets, &n_tg) || n_tg <= 0) + return NULL; + + for (i = 0; i < n_tg; i++) + { + target = targets[i]; + fmt = convert_format(gt_free_later(gdk_atom_name(target))); + if (!islower(fmt[0])) + continue; + if (!format && !strncasecmp(fmt, "text/", 5)) + break; + if (format && !strcasecmp(fmt, format)) + break; + } + + if (i >= n_tg) + return NULL; + + if (!gtk_clipboard_wait_is_target_available(get_clipboard(), target)) + return NULL; + + data = gtk_clipboard_wait_for_contents(get_clipboard(), target); + *len = gtk_selection_data_get_length(data); + text = (char *)g_malloc(*len); + memcpy(text, gtk_selection_data_get_data(data), *len); + + gtk_selection_data_free(data); + + _clipboard_has_changed[_current_clipboard] = FALSE; + + return gt_free_later(text); +} + +/*********************************************************************** + + Drag & Drop + +************************************************************************/ + +bool gDrag::_active = false; +gPicture *gDrag::_icon = NULL; +int gDrag::_icon_x = 0; +int gDrag::_icon_y = 0; +gControl *gDrag::_source = NULL; +gControl *gDrag::_destination = NULL; +gControl *gDrag::_dest = NULL; +int gDrag::_action = 0; +int gDrag::_type = 0; +gPicture *gDrag::_picture = NULL; +char *gDrag::_text = NULL; +int gDrag::_text_len = 0; +char *gDrag::_format = NULL; +int gDrag::_enabled = 0; +int gDrag::_x = -1; +int gDrag::_y = -1; +GdkDragContext *gDrag::_context = NULL; +guint32 gDrag::_time = 0; +bool gDrag::_local = false; +volatile bool gDrag::_got_data = false; +volatile bool gDrag::_end = false; + +void gDrag::setIcon(gPicture *vl) +{ + gPicture::assign(&_icon, vl); +} + +void gDrag::cancel() +{ + #if DEBUG_ME + fprintf(stderr, "gDrag::cancel\n"); + #endif + + hide(); + setIcon(NULL); + setDropText(NULL); + setDropImage(NULL); + g_free(_format); + _format = NULL; + _source = NULL; + _destination = NULL; + _dest = NULL; + _type = Nothing; + _x = _y = -1; + _time = 0; + _got_data = false; + _local = false; + _active = false; + +#if DEBUG_ME + fprintf(stderr, "_active -> false\n"); +#endif +} + +void gDrag::exit() +{ + cancel(); +} + +gControl *gDrag::drag(gControl *source, GtkTargetList *list) +{ + GdkDragContext *ct; + gControl *dest; + int button; + + #if DEBUG_ME + fprintf(stderr, "gDrag::drag: source = %p list = %p event = %p\n", source, list, gApplication::lastEvent()); + #endif + + button = gMouse::left() ? 1 : gMouse::middle() ? 2 : gMouse::right() ? 3 : 0; + +#if GTK_CHECK_VERSION(3, 10, 0) + ct = gtk_drag_begin_with_coordinates(source->border, list, GDK_ACTION_COPY, button, gApplication::lastEvent(), -1, -1); +#else + ct = gtk_drag_begin(source->border, list, GDK_ACTION_COPY, button, gApplication::lastEvent()); +#endif + if (!ct) + return NULL; + + _local = true; + _active = true; + +#if DEBUG_ME + fprintf(stderr, "gDrag::drag: begin\n"); +#endif + + if (_icon) + { + GdkPixbuf *icon = _icon->getIconPixbuf(); + gtk_drag_set_icon_pixbuf(ct, icon, _icon_x, _icon_y); + if (icon != _icon->getPixbuf()) + g_object_unref(G_OBJECT(icon)); + } + + _end = false; + while (!_end) + MAIN_do_iteration(true); + + gtk_target_list_unref(list); + + dest = _destination; + cancel(); + +#if DEBUG_ME + fprintf(stderr, "gDrag::drag: end\n"); +#endif + + return dest; +} + +gControl *gDrag::dragText(gControl *source, char *text, char *format) +{ + GtkTargetList *list; + + //cancel(); + + #if DEBUG_ME + fprintf(stderr, "gDrag::dragText: %s\n", text); + #endif + + setDropText(text); + + list = gtk_target_list_new (NULL, 0); + if (format) + gtk_target_list_add(list, gdk_atom_intern(format, false), 0, 0); + gtk_target_list_add_text_targets(list, 0); + + //gtk_target_list_add (list,gdk_atom_intern("UTF8_STRING",false),0,0); + //gtk_target_list_add (list,gdk_atom_intern("COMPOUND_TEXT",false),0,0); + //gtk_target_list_add (list,gdk_atom_intern("TEXT",false),0,0); + //gtk_target_list_add (list,GDK_TARGET_STRING,0,0); + //gtk_target_list_add (list,gdk_atom_intern("text/plain;charset=utf-8",false),0,0); + //gtk_target_list_add (list,gdk_atom_intern("text/plain",false),0,0); + + setDropInfo(Text, format); + + return drag(source, list); +} + +gControl *gDrag::dragImage(gControl *source, gPicture *image) +{ + GtkTargetList *list; + + setDropImage(image); + + list = gtk_target_list_new (NULL,0); + + gtk_target_list_add(list, gdk_atom_intern("image/png", false), 0, 0); + gtk_target_list_add(list, gdk_atom_intern("image/jpg", false), 0, 0); + gtk_target_list_add(list, gdk_atom_intern("image/jpeg", false), 0, 0); + gtk_target_list_add(list, gdk_atom_intern("image/gif", false), 0, 0); + + setDropInfo(Image, NULL); + + return drag(source, list); +} + +void gDrag::setDropInfo(int type, char *format) +{ + _type = type; + g_free(_format); + _format = g_strdup(format); +} + + +void gDrag::setDropData(int action, int x, int y, gControl *source, gControl *dest) +{ + #if DEBUG_ME + fprintf(stderr, "gDrag::setDropData: action = %d x = %d y = %d source = %p\n", action, x, y, source); + #endif + + _x = x; + _y = y; + _action = action; + _source = source; + _destination = dest; + _active = true; +} + +void gDrag::setDropText(char *text, int len) +{ + #if DEBUG_ME + fprintf(stderr, "gDrag::setDropText: text = '%s' %d\n", text, len); + #endif + + g_free(_text); + if (text) + { + if (len < 0) len = strlen(text); + _text_len = len; + _text = (char *)g_malloc(len); + memcpy(_text, text, len); + } + else + { + _text = NULL; + _text_len = 0; + } +} + +void gDrag::setDropImage(gPicture* image) +{ + //g_debug("gDrag::setDropImage: image = %p\n", image); + gPicture::assign(&_picture, image); +} + +void gDrag::setDropImage(char *buf, int len) +{ + GdkPixbufLoader *ld; + GdkPixbuf *pixbuf = NULL; + + //g_debug("gDrag::setDropImage: buf = %p len = %d\n", buf, len); + + if (buf && len > 0) + { + ld = gdk_pixbuf_loader_new (); + if (gdk_pixbuf_loader_write(ld, (const guchar*)buf, len, NULL)) + { + gdk_pixbuf_loader_close (ld, NULL); + pixbuf = gdk_pixbuf_loader_get_pixbuf(ld); + } + g_object_unref(G_OBJECT(ld)); + } + + if (pixbuf) + setDropImage(new gPicture(pixbuf)); + else + setDropImage(NULL); +} + +bool gDrag::checkThreshold(gControl *control, int x, int y, int sx, int sy) +{ + if (_active) + return false; + else + return gtk_drag_check_threshold(control->border, sx, sy, x, y); +} + +GdkDragContext *gDrag::enable(GdkDragContext *context, gControl *control, guint32 time) +{ + GdkDragContext *old = _context; + + #if DEBUG_ME + fprintf(stderr, "gDrag::enable\n"); + #endif + + _enabled++; + _context = context; + _time = time; + _dest = control; + return old; +} + +GdkDragContext *gDrag::disable(GdkDragContext *context) +{ + GdkDragContext *old = _context; + + #if DEBUG_ME + fprintf(stderr, "gDrag::disable\n"); + #endif + + _context = context; + _enabled--; + return old; +} + +//static GtkWidget *_frame_container = 0; +static GdkWindow *_frame[4] = { 0 }; +static bool _frame_visible = false; +static gControl *_frame_control = 0; + +static void hide_frame(gControl *control) +{ + int i; + + if (!_frame_visible) + return; + + if (control && control != _frame_control) + return; + + for (i = 0; i < 4; i++) + gdk_window_destroy(_frame[i]); + + _frame_visible = false; +} + +static void move_frame_border(GdkWindow *window, int x, int y, int w, int h) +{ + gdk_window_move_resize(window, x, y, w, h); +} + +static void show_frame(gControl *control, int x, int y, int w, int h) +{ + int i; + GdkWindowAttr attr = { 0 }; + GdkWindow *window; + GdkWindow *parent; + GtkAllocation a; + + if (w < 0) w = control->width() - control->getFrameWidth() * 2; + if (h < 0) h = control->height() - control->getFrameWidth() * 2; + + if (w < 2 || h < 2) + return; + + //g_debug("show %p %d %d %d %d", control->border->window, x, y, w, h); + + if (control != _frame_control) + hide_frame(NULL); + + // Don't know why I should do that... + if (control->_scroll) + { + parent = gtk_widget_get_window(control->widget); + } + else + { + parent = gtk_widget_get_window(control->border); + gtk_widget_get_allocation(control->border, &a); + x += a.x; + y += a.y; + } + + + if (!_frame_visible) + { +#ifdef GTK3 + GdkRGBA rgba; + gt_from_color(0, &rgba); +#else + GdkColor color; + fill_gdk_color(&color, 0); +#endif + + attr.wclass = GDK_INPUT_OUTPUT; + attr.window_type = GDK_WINDOW_CHILD; + + for (i = 0; i < 4; i++) + { + window = gdk_window_new(parent, &attr, 0); +#ifdef GTK3 + gdk_window_set_background_rgba(window, &rgba); +#else + gdk_window_set_background(window, &color); +#endif + _frame[i] = window; + } + } + + //x -= 2; + //y -= 2; + //w += 4; + //h += 4; + move_frame_border(_frame[0], x, y, w, 2); + move_frame_border(_frame[1], x, y, 2, h); + move_frame_border(_frame[2], x + w - 2, y, 2, h); + move_frame_border(_frame[3], x, y + h - 2, w, 2); + + for (i = 0; i < 4; i++) + gdk_window_show(_frame[i]); + + _frame_control = control; + _frame_visible = true; +} + +// static gboolean +// cb_drag_highlight_expose (GtkWidget *widget, +// GdkEventExpose *event, +// gpointer data) +// { +// gint x, y, width, height; +// +// if (GTK_WIDGET_DRAWABLE (widget)) +// { +// cairo_t *cr; +// +// gDrag::getHighlight(&x, &y, &width, &height); +// +// if (GTK_WIDGET_NO_WINDOW (widget)) +// { +// x += widget->allocation.x; +// y += widget->allocation.y; +// } +// +// gtk_paint_shadow (widget->style, widget->window, +// GTK_STATE_NORMAL, GTK_SHADOW_OUT, +// NULL, widget, "dnd", +// x, y, width, height); +// +// cr = gdk_cairo_create (widget->window); +// cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */ +// cairo_set_line_width (cr, 1.0); +// cairo_rectangle (cr, +// x + 0.5, y + 0.5, +// width - 1, height - 1); +// cairo_stroke (cr); +// cairo_destroy (cr); +// } +// +// return FALSE; +// } + +void gDrag::show(gControl *control, int x, int y, int w, int h) +{ + show_frame(control, x, y, w, h); +} + +void gDrag::hide(gControl *control) +{ + hide_frame(control); +} + + +char *gDrag::getFormat(int n) +{ + GList *tg; + gchar *format, *cformat; + + //if (gDrag::getType()) // local DnD + // return; + + //g_debug("set_from_context: non local\n"); + + if (_format) + return n == 0 ? _format : NULL; + + if (!_context) + return NULL; + + tg = g_list_first(gdk_drag_context_list_targets(_context)); + + while (tg) + { + format = gdk_atom_name((GdkAtom)tg->data); + cformat = convert_format(format); + + if (islower(cformat[0])) + { + if (n <= 0) + { + gt_free_later(format); + return cformat;; + } + n--; + } + + g_free(format); + tg = g_list_next(tg); + } + + return NULL; +} + +int gDrag::getType() +{ + int i; + char *format; + + if (_type) + return _type; + + for (i = 0;; i++) + { + format = getFormat(i); + if (!format) + return Nothing; + if (strlen(format) >= 5 && !strncasecmp(format, "text/", 5)) + return Text; + if (strlen(format) >= 6 &&! strncasecmp(format, "image/", 6)) + return Image; + } +} + +static void cb_drag_data_received(GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *sel, guint info, guint time, gControl *data) +{ +#if DEBUG_ME + fprintf(stderr, "cb_drag_data_received\n"); +#endif + + if (gDrag::getType() == gDrag::Text) + { + if (gtk_selection_data_get_length(sel) != -1) + gDrag::setDropText((char*)gtk_selection_data_get_data(sel), gtk_selection_data_get_length(sel)); + else + gDrag::setDropText(NULL); + } + + if (gDrag::getType() == gDrag::Image) + { + //fprintf(stderr, "Image\n"); + if (gtk_selection_data_get_length(sel) != -1) + gDrag::setDropImage((char*)gtk_selection_data_get_data(sel), gtk_selection_data_get_length(sel)); + else + gDrag::setDropImage(NULL); + } + + gDrag::_got_data = true; +} + + +bool gDrag::getData(const char *prefix) +{ + GList *tg; + gchar *format = NULL; + char *cfmt; + gulong id; + static bool norec = false; + gControl *dest; + +#if DEBUG_ME + fprintf(stderr, "getData: norec = %d _local = %d\n", norec, _local); +#endif + + if (norec || _local) // local DnD + return false; + + tg = g_list_first(gdk_drag_context_list_targets(_context)); + + while (tg) + { + g_free(format); + format = gdk_atom_name((GdkAtom)tg->data); + cfmt = convert_format(format); + //fprintf(stderr, "getData: prefix = %s format = '%s'\n", prefix, format); + + if (strlen(cfmt) >= strlen(prefix) && !strncasecmp(cfmt, prefix, strlen(prefix))) + { + g_free(format); + + dest = _dest; + + id = g_signal_connect(dest->border, "drag-data-received", G_CALLBACK(cb_drag_data_received), (gpointer)dest); + //fprintf(stderr, "gDrag::getData: g_signal_connect -> %p %ld\n", dest->border, id); + + _got_data = false; + + norec = true; + + gtk_drag_get_data (_dest->border, _context, (GdkAtom)tg->data, _time); + + while (!_got_data) + MAIN_do_iteration(true); + + norec = false; + + //fprintf(stderr, "gDrag::getData: g_signal_disconnect -> %p %ld\n", dest->border, id); + g_signal_handler_disconnect(dest->border, id); + + return false; + } + + tg = g_list_next(tg); + } + + g_free(format); + return true; +} + +char *gDrag::getText(int *len, const char *format, bool fromOutside) +{ + //setDropText(NULL); + + if (!format) + format = "text/"; + + if (!fromOutside && getData(format)) + { + *len = 0; + return NULL; + } + else + { + *len = _text_len; + return _text; + } +} + +gPicture *gDrag::getImage(bool fromOutside) +{ + if (_picture) + return _picture; + + if (!fromOutside && getData("image/")) + return NULL; + + return _picture; +} + +void gDrag::end() +{ + _end = true; + _active = false; +} diff --git a/gb.gtk/src/gdrag.h b/gb.gtk/src/gdrag.h new file mode 100644 index 00000000..351af4f6 --- /dev/null +++ b/gb.gtk/src/gdrag.h @@ -0,0 +1,116 @@ +/*************************************************************************** + + gdrag.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GDRAG_H +#define __GDRAG_H + +class gPicture; +class gControl; + +class gDrag +{ +public: + enum + { + Nothing = 0, + Text = 1, + Image = 2 + }; + enum { + Copy = 0, + Move = 1, + Link = 2 + }; + + static void exit(); + + static bool isActive() { return _active; } + static bool isEnabled() { return _enabled; } + + static void setIcon(gPicture *vl); + static gPicture *getIcon() { return _icon; } + static void getIconPos(int *x, int *y) { *x = _icon_x; *y = _icon_y; } + static void setIconPos(int x, int y) { _icon_x = x; _icon_y = y; } + + static gControl *dragText(gControl *source, char *text, char *format = 0); + static gControl *dragImage(gControl *source, gPicture *image); + static void end(); + static void cancel(); + + static gControl *getSource() { return _source; } + static gControl *getDestination() { return _destination; } + static int getAction() { return _action; } + + static int getType(); + static char *getFormat(int n = 0); + static char *getText(int *len, const char *format, bool fromOutside = false); + static gPicture *getImage(bool fromOutside = false); + + static int getDropX() { return _x; } + static int getDropY() { return _y; } + + static void show(gControl *control, int x = 0, int y = 0, int w = -1, int h = -1); + static void hide(gControl *control = NULL); + + static bool checkThreshold(gControl *control, int x, int y, int sx, int sy); + + // "Private" + static void setDropInfo(int type, char *format); + static void setDropData(int action, int x, int y, gControl *source, gControl *dest); + static void setDropText(char *text, int len = -1); + static void setDropImage(gPicture *image); + static void setDropImage(char *buf, int len); + + static GdkDragContext *enable(GdkDragContext *context, gControl *control, guint32 time); + static GdkDragContext *disable(GdkDragContext *context); + static bool getData(const char *prefix); + + static volatile bool _got_data; + +private: + + static gControl *drag(gControl *source, GtkTargetList *list); + + static bool _active; + static gPicture *_icon; + static int _icon_x; + static int _icon_y; + static gControl *_source; + static gControl *_destination; + static int _action; + static int _type; + static gPicture *_picture; + static char *_text; + static int _text_len; + static char *_format; + static int _enabled; + static int _x; + static int _y; + static GdkDragContext *_context; + static gControl *_dest; + static guint32 _time; + static bool _local; + static volatile bool _end; +}; + +#endif diff --git a/gb.gtk/src/gdrawingarea.cpp b/gb.gtk/src/gdrawingarea.cpp new file mode 100644 index 00000000..0cd6bac9 --- /dev/null +++ b/gb.gtk/src/gdrawingarea.cpp @@ -0,0 +1,468 @@ +/*************************************************************************** + + gdrawingarea.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gmouse.h" +#include "gdrawingarea.h" + +#ifdef GTK3 +#define UNREF_BUFFER() (cairo_surface_destroy(buffer), buffer = NULL) +#else +#define UNREF_BUFFER() (g_object_unref(G_OBJECT(buffer)), buffer = NULL) +#endif + + +/**************************************************************************************** + +gDrawingArea Widget + +*****************************************************************************************/ + +int gDrawingArea::_in_any_draw_event = 0; + +#ifdef GTK3 +static gboolean cb_draw(GtkWidget *wid, cairo_t *cr, gDrawingArea *data) +{ + /*cairo_rectangle_list_t *list; + cairo_rectangle_t *r; + int i;*/ + + if (data->cached()) + { + cairo_set_source_surface(cr, data->buffer, 0, 0); + cairo_paint(cr); + data->drawBorder(cr); + } + else + { + //data->drawBackground(); + + if (data->onExpose) + { + gDrawingArea::_in_any_draw_event++; + data->_in_draw_event = true; + data->onExpose(data, cr); + gDrawingArea::_in_any_draw_event--; + data->_in_draw_event = false; + /* + list = cairo_copy_clip_rectangle_list(cr); + + fprintf(stderr, "%d %d\n", list->status, list->num_rectangles); + if (list->status != CAIRO_STATUS_SUCCESS) + { + data->onExpose(data, cr); + } + else + { + for (i = 0; i < list->num_rectangles; i++) + { + r = &list->rectangles[i]; + cairo_save(cr); + cairo_rectangle(cr, r->x, r->y, r->width, r->height); + cairo_clip(cr); + data->onExpose(data, cr); + cairo_restore(cr); + } + } + + cairo_rectangle_list_destroy(list); + */ + } + data->drawBorder(cr); + } + + return false; +} +#else +static gboolean cb_expose(GtkWidget *wid, GdkEventExpose *e, gDrawingArea *data) +{ + if (data->cached()) + { + data->drawBorder(e); + } + else + { + //data->drawBackground(); + + if (data->onExpose) + { + gDrawingArea::_in_any_draw_event++; + data->_in_draw_event = true; + + /*GdkRectangle *rect; + int i, n; + gdk_region_get_rectangles(e->region, &rect, &n); + + for (i = 0; i < n; i++) + fprintf(stderr, "[%d] %d %d %d %d\n", i, rect[i].x, rect[i].y, rect[i].width, rect[i].height); + + g_free(rect);*/ + + data->onExpose(data, e->region, wid->allocation.x, wid->allocation.y); + gDrawingArea::_in_any_draw_event--; + data->_in_draw_event = false; + } + data->drawBorder(e); + } + + return false; +} +#endif + +static void cb_size(GtkWidget *wid, GtkAllocation *a, gDrawingArea *data) +{ + data->updateCache(); +} + +/*static gboolean cb_button_press(GtkWidget *wid, GdkEventButton *event, gDrawingArea *data) +{ + if (data->canFocus()) + data->setFocus(); + + return false; +}*/ + +void gDrawingArea::create(void) +{ + int i; + GtkWidget *ch; + bool doReparent = false; + bool was_visible = isVisible(); + GdkRectangle rect; + int bg, fg; + + if (border) + { + getGeometry(&rect); + bg = background(); + fg = foreground(); + parent()->remove(this); + + for (i = 0; i < childCount(); i++) + { + ch = child(i)->border; + g_object_ref(G_OBJECT(ch)); + gtk_container_remove(GTK_CONTAINER(widget), ch); + } + + _no_delete = true; + gtk_widget_destroy(border); + _no_delete = false; + doReparent = true; + } + + if (_cached || _use_tablet) + { + border = gtk_event_box_new(); + widget = gtk_fixed_new(); + box = widget; + gtk_widget_set_app_paintable(border, TRUE); + gtk_widget_set_app_paintable(box, TRUE); + } + else + { + border = widget = gtk_fixed_new(); + box = NULL; + } + + realize(false); + + g_signal_connect(G_OBJECT(border), "size-allocate", G_CALLBACK(cb_size), (gpointer)this); + ON_DRAW_BEFORE(border, this, cb_expose, cb_draw); + + updateUseTablet(); + + if (doReparent) + { + if (box) + gtk_widget_realize(box); + + setBackground(bg); + setForeground(fg); + setFont(font()); + bufX = bufY = bufW = bufH = -1; + setGeometry(&rect); + + for (i = 0; i < childCount(); i++) + { + ch = child(i)->border; + gtk_container_add(GTK_CONTAINER(widget), ch); + moveChild(child(i), child(i)->x(), child(i)->y()); + g_object_unref(G_OBJECT(ch)); + } + + if (was_visible) + show(); + else + hide(); + } +} + +gDrawingArea::gDrawingArea(gContainer *parent) : gContainer(parent) +{ + _cached = false; + buffer = NULL; + box = NULL; + _old_bg_id = 0; + _resize_cache = false; + _no_background = false; + _use_tablet = false; + + onExpose = NULL; + onFontChange = NULL; + + g_typ = Type_gDrawingArea; + + create(); +} + +gDrawingArea::~gDrawingArea() +{ + if (buffer) + UNREF_BUFFER(); +} + +void gDrawingArea::resize(int w, int h) +{ + // TODO Do not resize cache if the DrawingArea is being painted + gContainer::resize(w,h); + //updateCache(); +} + +void gDrawingArea::updateEventMask() +{ + /* + static int event_mask; + XWindowAttributes attr; + + gtk_widget_realize(border); + + if (!enabled()) + { + XGetWindowAttributes(gdk_display, GDK_WINDOW_XID(border->window), &attr); + event_mask = attr.your_event_mask; + XSelectInput(gdk_display, GDK_WINDOW_XID(border->window), ExposureMask); + } + else + { + XSelectInput(gdk_display, GDK_WINDOW_XID(border->window), event_mask); + } + */ +} + +void gDrawingArea::setEnabled(bool vl) +{ + gContainer::setEnabled(vl); + updateEventMask(); +} + +void gDrawingArea::setCached(bool vl) +{ + if (vl == _cached) return; + + _cached = vl; + + if (!_cached) + { + UNREF_BUFFER(); + set_gdk_bg_color(border, background()); + } + + create(); + resizeCache(); +} + +void gDrawingArea::resizeCache() +{ + int bw, bh; + int w, h; +#ifdef GTK3 + cairo_surface_t *buf; +#else + GdkPixmap *buf; +#endif + GdkWindow *win; + cairo_t *cr; + + if (!_cached) + return; + + win = gtk_widget_get_window(GTK_WIDGET(box)); + if (!win) + return; + + w = width(); + h = height(); + + if (buffer) + { +#ifdef GTK3 + bw = cairo_image_surface_get_width(buffer); + bh = cairo_image_surface_get_height(buffer); +#else + gdk_drawable_get_size(buffer, &bw, &bh); +#endif + } + else + bw = bh = 0; + + if (bw != w || bh != h) + { +#ifdef GTK3 + buf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); + cr = cairo_create(buf); +#else + buf = gdk_pixmap_new(win, w, h, -1); + cr = gdk_cairo_create(buf); +#endif + + if (w > bw || h > bh || !buffer) + { + gt_cairo_set_source_color(cr, realBackground(true)); + cairo_rectangle(cr, 0, 0, w, h); + cairo_fill(cr); + } + + if (buffer) + { + if (bw > w) bw = w; + if (bh > h) bh = h; + //gdk_draw_drawable(buf, gc2, buffer, 0, 0, 0, 0, bw, bh); +#ifdef GTK3 + cairo_set_source_surface(cr, buffer, 0, 0); +#else + gdk_cairo_set_source_pixmap(cr, buffer, 0, 0); +#endif + cairo_rectangle(cr, 0, 0, bw, bh); + cairo_fill(cr); + + UNREF_BUFFER(); + } + + buffer = buf; + cairo_destroy(cr); + } + + //drawBorder(buffer); + refreshCache(); +} + +void gDrawingArea::setCache() +{ + if (!_cached) + return; + +#ifdef GTK3 +#else + gdk_window_set_back_pixmap(gtk_widget_get_window(box), buffer, FALSE); +#endif + refreshCache(); +} + +static gboolean resize_cache(gDrawingArea *data) +{ + //fprintf(stderr, "resize_cache\n"); + data->resizeCache(); + data->setCache(); + data->_resize_cache = false; + return false; +} + +void gDrawingArea::updateCache() +{ + if (!_cached) + return; + + if (!_resize_cache) + { + _resize_cache = true; + g_timeout_add(10, (GSourceFunc)resize_cache, (gpointer)this); + } +} + +void gDrawingArea::clear() +{ + if (_cached && buffer) + { + UNREF_BUFFER(); + resizeCache(); + setCache(); + } +} + +void gDrawingArea::refreshCache() +{ + gtk_widget_queue_draw(box); + //if (box && box->window) + // gdk_window_clear(box->window); +} + +void gDrawingArea::setNoBackground(bool vl) +{ + /* + GdkWindow *win; + + gtk_widget_realize(widget); + win = widget->window; + + if (vl) + gdk_window_set_back_pixmap(win, NULL, FALSE); + else + setBackground(background()); + */ + if (vl != _no_background) + { + _no_background = vl; + create(); + } +} + +void gDrawingArea::setRealBackground(gColor color) +{ + gControl::setRealBackground(color); + clear(); +} + +void gDrawingArea::updateUseTablet() +{ + if (_use_tablet) + gMouse::initDevices(); +#ifndef GTK3 + gtk_widget_set_extension_events(border, _use_tablet ? GDK_EXTENSION_EVENTS_CURSOR : GDK_EXTENSION_EVENTS_NONE); +#endif + //fprintf(stderr, "gtk_widget_set_extension_events: %s %p: %d\n", name(), border, _use_tablet); +} + +void gDrawingArea::setUseTablet(bool vl) +{ + if (vl == _use_tablet) + return; + _use_tablet = vl; + create(); +} + +void gDrawingArea::updateFont() +{ + gContainer::updateFont(); + emit(SIGNAL(onFontChange)); +} diff --git a/gb.gtk/src/gdrawingarea.h b/gb.gtk/src/gdrawingarea.h new file mode 100644 index 00000000..6c8b3d43 --- /dev/null +++ b/gb.gtk/src/gdrawingarea.h @@ -0,0 +1,86 @@ +/*************************************************************************** + + gdrawingarea.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GDRAWINGAREA_H +#define __GDRAWINGAREA_H + +class gDrawingArea : public gContainer +{ +public: + gDrawingArea(gContainer *parent); + ~gDrawingArea(); + + int getBorder() const { return getFrameBorder(); } + bool cached() const { return _cached; } + bool hasNoBackground() const { return _no_background; } + bool useTablet() const { return _use_tablet; } + + void setBorder(int vl) { setFrameBorder(vl); } + void setCached(bool vl); + void setNoBackground(bool vl); + void setUseTablet(bool vl); + + bool inDrawEvent() const { return _in_draw_event; } + static bool inAnyDrawEvent() { return _in_any_draw_event; } + +//"Methods" + void clear(); + virtual void resize(int w, int h); + virtual void setEnabled(bool vl); + virtual void setRealBackground(gColor color); + virtual void updateFont(); + +//"Events" +#ifdef GTK3 + void (*onExpose)(gDrawingArea *sender, cairo_t *cr); +#else + void (*onExpose)(gDrawingArea *sender, GdkRegion *region, int dx, int dy); +#endif + void (*onFontChange)(gDrawingArea *sender); + +//"Private" + void create(); + void updateCache(); + void resizeCache(); + void refreshCache(); + void updateEventMask(); + void setCache(); + void updateUseTablet(); + +#ifdef GTK3 + cairo_surface_t *buffer; +#else + GdkPixmap *buffer; +#endif + GtkWidget *box; + uint _event_mask; + uint _old_bg_id; + unsigned _cached : 1; + unsigned _resize_cache : 1; + unsigned _in_draw_event : 1; + unsigned _no_background : 1; + unsigned _use_tablet : 1; + static int _in_any_draw_event; +}; + +#endif diff --git a/gb.gtk/src/gfont.cpp b/gb.gtk/src/gfont.cpp new file mode 100644 index 00000000..3acba9e9 --- /dev/null +++ b/gb.gtk/src/gfont.cpp @@ -0,0 +1,724 @@ +/*************************************************************************** + + gfont.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "font-parser.h" +#include "gdesktop.h" +#include "gtools.h" +#include "gb.form.font.h" + +#include + +static void set_font_from_string(gFont *font, const char *str) +{ + gchar **tokens, **p; + gchar *copy, *elt; + int grade; + double size; + + /*font->setBold(false); + font->setItalic(false); + font->setUnderline(false); + font->setStrikeOut(false); + font->setName(gDesktop::font()->name()); + font->setSize(gDesktop::font()->size());*/ + + if (!str || !*str) + return; + + tokens = g_strsplit(str, ",", 0); + + p = tokens; + for(p = tokens; *p; p++) + { + copy = g_strdup(*p); + elt = g_strstrip(copy); + + if (!strcasecmp(elt, "bold")) + font->setBold(true); + else if (!strcasecmp(elt, "italic")) + font->setItalic(true); + else if (!strcasecmp(elt, "underline")) + font->setUnderline(true); + else if (!strcasecmp(elt, "strikeout")) + font->setStrikeout(true); + else if (elt[0] == '+' || elt[0] == '-' || elt[0] == '0') + { + grade = atoi(elt); + if (grade || elt[0] == '0') + font->setGrade(grade); + } + else + { + size = atof(elt); + if (isdigit(*elt) && size != 0.0) + font->setSize(size); + else + { + font->setBold(false); + font->setItalic(false); + font->setUnderline(false); + font->setStrikeout(false); + font->setName(elt); + } + } + + g_free(copy); + } + + g_strfreev(tokens); +} + + +/******************************************************************************** + +gFont + +*********************************************************************************/ +static int FONT_n_families; +GList *FONT_families = NULL; + +static int _nfont = 0; + +void gFont::init() +{ + PangoFontFamily **_families; + PangoContext *ct; + char *buf1,*buf2; + int bucle; + + ct=gdk_pango_context_get(); + pango_context_list_families(ct, &_families, &FONT_n_families); + + for (bucle=0;bucledata); + iter=iter->next; + } + } + + if (FONT_families) g_list_free(FONT_families); + + //if (_nfont) + // fprintf(stderr, "WARNING: %d gFont objects were not freed.\n", _nfont); +} + +int gFont::count() +{ + if (!FONT_families) gFont::init(); + return FONT_n_families; +} + +const char *gFont::familyItem(int pos) +{ + if (!FONT_families) gFont::init(); + if ( (pos<0) || (pos>=FONT_n_families) ) return NULL; + + return (const char*)g_list_nth (FONT_families,pos)->data; +} + +#if 0 +void gFont::updateWidget() +{ + if (!wid) return; + + PangoFontDescription *desc=pango_context_get_font_description(ct); + gtk_widget_modify_font(wid,desc); + + if (G_OBJECT_TYPE(wid)==GTK_TYPE_LABEL) + { + PangoAttrList *pal=pango_attr_list_new(); + if (strike) + { + PangoAttribute *pa=pango_attr_strikethrough_new(true); + pa->start_index = 0; + pa->end_index = g_utf8_strlen(gtk_label_get_text(GTK_LABEL(wid)), -1); + pango_attr_list_insert(pal, pa); + } + if (uline) + { + PangoAttribute *pa=pango_attr_underline_new(PANGO_UNDERLINE_SINGLE); + pa->start_index = 0; + pa->end_index = g_utf8_strlen(gtk_label_get_text(GTK_LABEL(wid)), -1); + pango_attr_list_insert(pal, pa); + } + gtk_label_set_attributes(GTK_LABEL(wid), pal); + pango_attr_list_unref(pal); + } + +} +#endif + +void gFont::reset() +{ + strike = false; + uline = false; + + _bold_set = false; + _italic_set = false; + _name_set = false; + _size_set = false; + _strikeout_set = false; + _underline_set = false; +} + +void gFont::setAll(bool v) +{ + _bold_set = v; + _italic_set = v; + _name_set = v; + _size_set = v; + _strikeout_set = v; + _underline_set = v; +} + +void gFont::setAllFrom(gFont *font) +{ + if (!font) + setAll(false); + else + { + _bold_set = font->_bold_set; + _italic_set = font->_italic_set; + _name_set = font->_name_set; + _size_set = font->_size_set; + _strikeout_set = font->_strikeout_set; + _underline_set = font->_underline_set; + } +} + +void gFont::realize() +{ + ct = NULL; + _height = 0; + + reset(); + + _nfont++; +} + +void gFont::initFlags() +{ + gFont *comp = new gFont(); + + _bold_set = comp->bold() != bold(); + _italic_set = comp->italic() != italic(); + _name_set = strcmp(comp->name(), name()) != 0; + _size_set = comp->size() != size(); + _strikeout_set = comp->strikeout() != strikeout(); + _underline_set = comp->underline() != underline(); +} + +void gFont::create() +{ +#ifdef GTK3 + char *font; + g_object_get(gtk_settings_get_default(), "gtk-font-name", &font, (char *)NULL); + realize(); + ct = gdk_pango_context_get(); + pango_context_set_font_description(ct, pango_font_description_from_string(font)); + g_free(font); +#else + GtkStyle *sty = gtk_widget_get_default_style(); + realize(); + ct = gdk_pango_context_get(); + pango_context_set_font_description(ct, sty->font_desc); +#endif +} + +gFont::gFont() : gShare() +{ + create(); +} + +gFont::gFont(GtkWidget *wid) : gShare() +{ + PangoAttrList *lst; + PangoAttrIterator* iter; + + realize(); + ct = gtk_widget_create_pango_context(wid); + g_object_ref(ct); + + if (G_OBJECT_TYPE(wid)==GTK_TYPE_LABEL) + { + lst=gtk_label_get_attributes(GTK_LABEL(wid)); + if (lst) + { + iter=pango_attr_list_get_iterator(lst); + if (pango_attr_iterator_get(iter,PANGO_ATTR_STRIKETHROUGH)) strike=true; + if (pango_attr_iterator_get(iter,PANGO_ATTR_UNDERLINE)) uline=true; + pango_attr_iterator_destroy(iter); + } + } + + initFlags(); +} + +gFont::gFont(PangoFontDescription *fd) : gShare() +{ + realize(); + ct = gdk_pango_context_get(); + pango_context_set_font_description(ct, fd); + initFlags(); +} + +gFont::gFont(const char *name) : gShare() +{ + create(); + set_font_from_string(this, name); +} + +void gFont::copyTo(gFont *dst) +{ + dst->reset(); + if (_name_set) dst->setName(name()); + if (_size_set) dst->setSize(size()); + if (_bold_set) dst->setBold(bold()); + if (_italic_set) dst->setItalic(italic()); + if (_underline_set) dst->setUnderline(underline()); + if (_strikeout_set) dst->setStrikeout(strikeout()); +} + +void gFont::mergeFrom(gFont *src) +{ + if (!_name_set && src->_name_set) setName(src->name()); + if (!_size_set && src->_size_set) setSize(src->size()); + if (!_bold_set && src->_bold_set) setBold(src->bold()); + if (!_italic_set && src->_italic_set) setItalic(src->italic()); + if (!_underline_set && src->_underline_set) setUnderline(src->underline()); + if (!_strikeout_set && src->_strikeout_set) setStrikeout(src->strikeout()); +} + +gFont *gFont::copy() +{ + gFont *f = new gFont(); + copyTo(f); + return f; +} + +gFont::~gFont() +{ + g_object_unref(ct); + _nfont--; +} + + +int gFont::ascent() +{ + PangoFontDescription *desc=pango_context_get_font_description(ct); + PangoFontMetrics *metric=pango_context_get_metrics(ct,desc,NULL); + + //fprintf(stderr, "ascent: %d\n", pango_font_metrics_get_ascent(metric)); + return gt_pango_to_pixel(pango_font_metrics_get_ascent(metric)); +} + +float gFont::ascentF() +{ + PangoFontDescription *desc=pango_context_get_font_description(ct); + PangoFontMetrics *metric=pango_context_get_metrics(ct,desc,NULL); + + return (float)pango_font_metrics_get_ascent(metric) / PANGO_SCALE; +} + +int gFont::descent() +{ + PangoFontDescription *desc=pango_context_get_font_description(ct); + PangoFontMetrics *metric=pango_context_get_metrics(ct,desc,NULL); + + //fprintf(stderr, "descent: %d\n", pango_font_metrics_get_descent(metric)); + return gt_pango_to_pixel(pango_font_metrics_get_descent(metric)); +} + +bool gFont::bold() +{ + PangoFontDescription *desc=pango_context_get_font_description(ct); + PangoWeight w; + + w=pango_font_description_get_weight(desc); + if (w>PANGO_WEIGHT_NORMAL) return true; + return false; +} + +void gFont::setBold(bool vl) +{ + PangoFontDescription *desc=pango_context_get_font_description(ct); + + if (vl) + pango_font_description_set_weight(desc,PANGO_WEIGHT_BOLD); + else + pango_font_description_set_weight(desc,PANGO_WEIGHT_NORMAL); + + _bold_set = true; +} + +bool gFont::italic() +{ + PangoFontDescription *desc=pango_context_get_font_description(ct); + + return pango_font_description_get_style(desc) !=PANGO_STYLE_NORMAL; +} + +void gFont::setItalic(bool vl) +{ + PangoFontDescription *desc=pango_context_get_font_description(ct); + + if (vl) + pango_font_description_set_style(desc,PANGO_STYLE_ITALIC); + else + pango_font_description_set_style(desc,PANGO_STYLE_NORMAL); + + _italic_set = true; +} + +char* gFont::name() +{ + PangoFontDescription *desc=pango_context_get_font_description(ct); + + return (char *)pango_font_description_get_family (desc); +} + +void gFont::setName(char *nm) +{ + PangoFontDescription *desc=pango_context_get_font_description(ct); + + pango_font_description_set_family (desc,nm); + + _name_set = true; + _height = 0; +} + +double gFont::size() +{ + double size; + + PangoFontDescription *desc=pango_context_get_font_description(ct); + + size=pango_font_description_get_size(desc); + return size / (double)PANGO_SCALE; +} + +int gFont::grade() +{ + double desktop = gDesktop::font()->size(); + return SIZE_TO_GRADE(size(), desktop); +} + +void gFont::setSize(double sz) +{ + PangoFontDescription *desc=pango_context_get_font_description(ct); + + pango_font_description_set_size(desc, (int)(sz * PANGO_SCALE + 0.5)); + + _size_set = true; + _height = 0; +} + +void gFont::setGrade(int grade) +{ + double desktop = gDesktop::font()->size(); + + if (grade < FONT_GRADE_MIN) + grade = FONT_GRADE_MIN; + else if (grade > FONT_GRADE_MAX) + grade = FONT_GRADE_MAX; + + setSize(GRADE_TO_SIZE(grade, desktop)); +} + +const char *gFont::toString() +{ + GString *desc = g_string_new(name()); + char *ret; + int s; + + s = (int)(size() * 10 + 0.5); + + g_string_append_printf(desc, ",%d", s / 10); + if (s % 10) + g_string_append_printf(desc, ".%d", s % 10); + if (bold()) + g_string_append(desc, ",Bold"); + if (italic()) + g_string_append(desc, ",Italic"); + if (underline()) + g_string_append(desc, ",Underline"); + if (strikeout()) + g_string_append(desc, ",Strikeout"); + + ret = g_string_free(desc, false); + gt_free_later(ret); + + return ret; +} + +const char *gFont::toFullString() +{ + GString *desc = g_string_new(""); + char *ret; + + g_string_append_printf(desc, "[ "); + + if (_name_set) + g_string_append_printf(desc, "%s ", name()); + + if (_size_set) + g_string_append_printf(desc, "%g ", (double)((int)(size() * 10 + 0.5)) / 10); + + if (_bold_set) + g_string_append_printf(desc, "%s ", bold() ? "Bold" : "NotBold"); + if (_italic_set) + g_string_append_printf(desc, "%s ", italic() ? "Italic" : "NotItalic"); + if (_underline_set) + g_string_append_printf(desc, "%s ", underline() ? "Underline" : "NotUnderline"); + if (_strikeout_set) + g_string_append_printf(desc, "%s ", strikeout() ? "Strikeout" : "NotStrikeout"); + + g_string_append_printf(desc, "]"); + + ret = g_string_free(desc, false); + gt_free_later(ret); + return ret; +} + +void gFont::textSize(const char *text, int len, float *w, float *h) +{ + PangoLayout *ly; + int tw = 0, th = 0; + + if (text && len) + { + ly = pango_layout_new(ct); + pango_layout_set_text(ly, text, len); + pango_layout_get_size(ly, &tw, &th); + } + + if (w) *w = (float)tw / PANGO_SCALE; + if (h) *h = (float)th / PANGO_SCALE; +} + +int gFont::width(const char *text, int len) +{ + float fw; + textSize(text, len, &fw, NULL); + return gt_pango_to_pixel(fw * PANGO_SCALE); +} + +int gFont::height(const char *text, int len) +{ + float fh; + textSize(text, len, NULL, &fh); + return gt_pango_to_pixel(fh * PANGO_SCALE); +} + +int gFont::height() +{ + if (!_height) + _height = height(" ", 1); + return _height; +} + +bool gFont::scalable() +{ + bool ret=false; + + PangoFontDescription *desc=pango_context_get_font_description(ct); + //PangoFontDescription *tmp; + const char* name=pango_font_description_get_family(desc); + PangoFontFamily **families; + PangoFontFace **faces; + int *sizes; + int n_families; + int n_faces; + int n_sizes; + //int b2; + const char *buf; + + if (!name) return false; + + pango_context_list_families(ct,&families,&n_families); + + if (!families) return false; + + for (int bucle=0;bucle 0) + pango_layout_set_width(ly, sw * PANGO_SCALE); + pango_layout_get_size(ly, &tw, &th); + g_free(html); + } + + if (w) *w = (float)tw / PANGO_SCALE; + if (h) *h = (float)th / PANGO_SCALE; +} diff --git a/gb.gtk/src/gfont.h b/gb.gtk/src/gfont.h new file mode 100644 index 00000000..e8af324e --- /dev/null +++ b/gb.gtk/src/gfont.h @@ -0,0 +1,107 @@ +/*************************************************************************** + + gfont.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GFONT_H +#define __GFONT_H + +#include "gshare.h" + +class gFont : public gShare +{ +public: + gFont(); + gFont(const char *name); + virtual ~gFont(); + + static void assign(gFont **dst, gFont *src = 0) { gShare::assign((gShare **)dst, src); } + static void set(gFont **dst, gFont *src = 0) { gShare::assign((gShare **)dst, src); src->unref(); } + + static void init(); + static void exit(); + static int count(); + static const char *familyItem(int pos); + + gFont *copy(); + void copyTo(gFont *dst); + void mergeFrom(gFont *src); + int ascent(); + float ascentF(); + int descent(); + bool fixed(); + bool scalable(); + char **styles(); + + bool bold(); + bool italic(); + char* name(); + int resolution(); + double size(); + bool strikeout(); + bool underline(); + int grade(); + + void setBold(bool vl); + void setItalic(bool vl); + void setName(char *nm); + void setResolution(int vl); + void setSize(double sz); + void setGrade(int grade); + void setStrikeout(bool vl); + void setUnderline(bool vl); + + const char *toString(); + const char *toFullString(); + int width(const char *text, int len = -1); + int height(const char *text, int len = -1); + int height(); + void textSize(const char *text, int len, float *w, float *h); + void richTextSize(const char *txt, int len, float sw, float *w, float *h); + +//"Private" + gFont(GtkWidget *wg); + gFont(PangoFontDescription *fd); + PangoContext* ct; + PangoFontDescription *desc() { return pango_context_get_font_description(ct); } + bool isAllSet(); + void setAll(bool v); + void setAllFrom(gFont *font); + void reset(); + + unsigned _bold_set : 1; + unsigned _italic_set : 1; + unsigned _name_set : 1; + unsigned _size_set : 1; + unsigned _strikeout_set : 1; + unsigned _underline_set : 1; + +private: + + bool uline; + bool strike; + void create(); + void realize(); + void initFlags(); + int _height; +}; + +#endif diff --git a/gb.gtk/src/gframe.cpp b/gb.gtk/src/gframe.cpp new file mode 100644 index 00000000..02ff1e9b --- /dev/null +++ b/gb.gtk/src/gframe.cpp @@ -0,0 +1,255 @@ +/*************************************************************************** + + gframe.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gapplication.h" +#include "gframe.h" + +/**************************************************************************** + +Panel + +****************************************************************************/ + +void gPanel::create(void) +{ + int i; + GtkWidget *ch, *box; + bool doReparent = false; + bool was_visible = isVisible(); + GdkRectangle rect; + int bg, fg; + gControl *nextSibling; + + if (border) + { + getGeometry(&rect); + bg = background(); + fg = foreground(); + nextSibling = next(); + parent()->remove(this); + + for (i = 0; i < childCount(); i++) + { + ch = child(i)->border; + g_object_ref(G_OBJECT(ch)); + gtk_container_remove(GTK_CONTAINER(widget), ch); + } + + _no_delete = true; + gtk_widget_destroy(border); + _no_delete = false; + doReparent = true; + } + + if (_bg_set) + { + border = gtk_event_box_new(); + widget = gtk_fixed_new(); + box = widget; + //gtk_widget_set_app_paintable(border, TRUE); + //gtk_widget_set_app_paintable(box, TRUE); + } + else + { + border = widget = gtk_fixed_new(); + box = NULL; + } + + frame = widget; + realize(true); + + //g_signal_connect(G_OBJECT(border), "size-allocate", G_CALLBACK(cb_size), (gpointer)this); + //g_signal_connect(G_OBJECT(border), "expose-event", G_CALLBACK(cb_expose), (gpointer)this); + + if (doReparent) + { + if (box) + gtk_widget_realize(box); + + setNext(nextSibling); + setBackground(bg); + setForeground(fg); + updateFont(); + bufX = bufY = bufW = bufH = -1; + setGeometry(&rect); + + for (i = 0; i < childCount(); i++) + { + ch = child(i)->border; + gtk_container_add(GTK_CONTAINER(widget), ch); + moveChild(child(i), child(i)->x(), child(i)->y()); + g_object_unref(G_OBJECT(ch)); + } + + if (was_visible) + show(); + else + hide(); + + //gApplication::checkHoveredControl(this); + + if (_inside) + { + _inside = false; + if (gApplication::_enter == this) + gApplication::_enter = NULL; + gApplication::_ignore_until_next_enter = this; + } + } +} + +gPanel::gPanel(gContainer *parent) : gContainer(parent) +{ + g_typ = Type_gPanel; + border = NULL; + create(); +} + +void gPanel::setBackground(gColor color) +{ + bool set = _bg_set; + + gContainer::setBackground(color); + + if (set != _bg_set) + create(); +} + +/**************************************************************************** + +Frame + +****************************************************************************/ + +gFrame::gFrame(gContainer *parent) : gContainer(parent) +{ + g_typ=Type_gFrame; + + border = widget = gtk_fixed_new(); + + fr = gtk_frame_new(NULL); + //label = gtk_frame_get_label_widget(GTK_FRAME(fr)); + gtk_container_set_border_width(GTK_CONTAINER(fr),0); + gtk_frame_set_shadow_type(GTK_FRAME(fr), GTK_SHADOW_ETCHED_IN); + gtk_container_add(GTK_CONTAINER(widget), fr); + + realize(false); + + //g_signal_connect(G_OBJECT(border), "size-allocate", G_CALLBACK(cb_frame_resize), (gpointer)this); +} + +const char *gFrame::text() +{ + const char *label = gtk_frame_get_label(GTK_FRAME(fr)); + if (!label) + label = ""; + return label; +} + +void gFrame::setText(const char *vl) +{ + if (!vl) + vl = ""; + + gtk_frame_set_label(GTK_FRAME(fr), vl); + gtk_frame_set_label_align(GTK_FRAME(fr), 0.5, 0.0); +} + +void gFrame::updateFont() +{ + GtkWidget *label = gtk_frame_get_label_widget(GTK_FRAME(fr)); + gContainer::updateFont(); + if (label) + { +#ifdef GTK3 +#else + gtk_widget_modify_font(label, font()->desc()); +#endif + } + performArrange(); +} + +#ifdef GTK3 +GtkWidget *gFrame::getStyleSheetWidget() +{ + return fr; +} +#else +void gFrame::setRealForeground(gColor color) +{ + gControl::setRealForeground(color); + //if (label) set_gdk_fg_color(label, color); +} +#endif + +int gFrame::containerX() +{ + return gApplication::getFrameWidth(); +} + +int gFrame::clientX() +{ + return 0; +} + +int gFrame::clientWidth() +{ + return width(); // - gApplication::getFrameWidth() * 2; +} + +int gFrame::containerWidth() +{ + return clientWidth() - gApplication::getFrameWidth() * 2; +} + +int gFrame::containerY() +{ + int y = gApplication::getFrameWidth(); + + if (*text()) + y = font()->height() * 3 / 2; + + return y; +} + +int gFrame::clientY() +{ + return 0; +} + +int gFrame::clientHeight() +{ + return height(); // - clientY() - gApplication::getFrameWidth(); +} + +int gFrame::containerHeight() +{ + return clientHeight() - containerY() - gApplication::getFrameWidth(); +} + +void gFrame::resize(int w, int h) +{ + gContainer::resize(w, h); + gtk_widget_set_size_request(fr, w, h); +} diff --git a/gb.gtk/src/gframe.h b/gb.gtk/src/gframe.h new file mode 100644 index 00000000..c7fa5175 --- /dev/null +++ b/gb.gtk/src/gframe.h @@ -0,0 +1,74 @@ +/*************************************************************************** + + gframe.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GFRAME_H +#define __GFRAME_H + +#include "gcontainer.h" +#include "gcolor.h" + +class gPanel : public gContainer +{ +public: + gPanel(gContainer *parent); + + int getBorder() { return getFrameBorder(); } + void setBorder(int vl) { setFrameBorder(vl); } + virtual void setBackground(gColor color = COLOR_DEFAULT); + +private: + + void create(); +}; + +class gFrame : public gContainer +{ +public: + gFrame(gContainer *parent); + + const char *text(); + void setText(const char *vl); + + virtual int clientX(); + virtual int clientY(); + virtual int clientWidth(); + virtual int clientHeight(); + virtual int containerX(); + virtual int containerY(); + virtual int containerWidth(); + virtual int containerHeight(); + + virtual void updateFont(); +#ifdef GTK3 + virtual GtkWidget *getStyleSheetWidget(); +#else + virtual void setRealForeground(gColor color); +#endif + + virtual void resize(int w, int h); + +//"Private" + GtkWidget *fr; +}; + +#endif diff --git a/gb.gtk/src/ggambastag.h b/gb.gtk/src/ggambastag.h new file mode 100644 index 00000000..7b6d7f07 --- /dev/null +++ b/gb.gtk/src/ggambastag.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + ggambastag.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GGAMBASTAG_H +#define __GGAMBASTAG_H + +#include "gtag.h" + +class gGambasTag: public gTag +{ +public: + gGambasTag(void *p) : gTag(p) { } + virtual ~gGambasTag() {} + virtual void ref(void *v) { GB.Ref(v); } + virtual void unref(void *v) { GB.Unref((void **)&v); } +}; + +#endif diff --git a/gb.gtk/src/gglarea.cpp b/gb.gtk/src/gglarea.cpp new file mode 100644 index 00000000..3fd799e8 --- /dev/null +++ b/gb.gtk/src/gglarea.cpp @@ -0,0 +1,38 @@ +/*************************************************************************** + + gglarea.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GGLAREA_C + +#include "widgets.h" +#include "gglarea.h" + +gGLArea::gGLArea(gContainer *parent, void (*init)(GtkWidget *)) : gControl(parent) +{ + g_typ = Type_gGLArea; + + border = widget = gtk_event_box_new(); + gtk_widget_set_can_focus(widget, TRUE); + (*init)(widget); + realize(false); +} + diff --git a/gb.gtk/src/gglarea.h b/gb.gtk/src/gglarea.h new file mode 100644 index 00000000..65e4f7b5 --- /dev/null +++ b/gb.gtk/src/gglarea.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + gglarea.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GGLAREA_H +#define __GGLAREA_H + +class gGLArea : public gControl +{ +public: + gGLArea(gContainer *parent, void (*init)(GtkWidget *)); +}; + +#endif diff --git a/gb.gtk/src/gkey.cpp b/gb.gtk/src/gkey.cpp new file mode 100644 index 00000000..1eb36082 --- /dev/null +++ b/gb.gtk/src/gkey.cpp @@ -0,0 +1,566 @@ +/*************************************************************************** + + gkey.cpp + + (c) 2004-2006 - Daniel Campos Fernández + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GKEY_CPP + +#include +#include +#include + +#include "widgets.h" +#include "gapplication.h" +#include "gtrayicon.h" +#include "gdesktop.h" +#include "gmainwindow.h" +#include "gkey.h" + +//#define DEBUG_IM 1 + +/************************************************************************* + +gKey + +**************************************************************************/ + +bool gKey::_valid = false; +bool gKey::_canceled = false; +GdkEventKey gKey::_event; +int gKey::_last_key_press = 0; +int gKey::_last_key_release = 0; + +static GtkIMContext *_im_context = NULL; +static char *_im_default_slave = NULL; +static bool _im_has_input_method = FALSE; +static gControl *_im_control = NULL; +static bool _im_no_commit = false; +static GdkWindow *_im_window = NULL; +static bool _im_is_xim = FALSE; +static bool _im_ignore_event = FALSE; +static bool _im_got_commit = FALSE; + +//#define MAX_CODE 16 +//static uint _key_code[MAX_CODE] = { 0 }; + + +//char *_im_text = NULL; + +const char *gKey::text() +{ + if (!_valid) + return 0; + else + return _event.string; +} + +int gKey::code() +{ + if (!_valid) + return 0; + + int code = _event.keyval; + + if (code >= GDK_a && code <= GDK_z) + code += GDK_A - GDK_a; + else if (code == GDK_Alt_R) + code = GDK_Alt_L; + else if (code == GDK_Control_R) + code = GDK_Control_L; + else if (code == GDK_Meta_R) + code = GDK_Meta_L; + else if (code == GDK_Shift_R) + code = GDK_Shift_L; + else + { + int unicode = gdk_keyval_to_unicode(code); + if (unicode >= 32 && unicode < 127) + code = unicode; + } + + return code; +} + +int gKey::state() +{ + if (!_valid) + return 0; + else + return _event.state; +} + +bool gKey::alt() +{ + return state() & GDK_MOD1_MASK; // || _event.keyval == GDK_Alt_L || _event.keyval == GDK_Alt_R; +} + +bool gKey::control() +{ + return state() & GDK_CONTROL_MASK; // || _event.keyval == GDK_Control_L || _event.keyval == GDK_Control_R; +} + +bool gKey::meta() +{ + return state() & GDK_META_MASK; // || _event.keyval == GDK_Meta_L || _event.keyval == GDK_Meta_R; +} + +bool gKey::normal() +{ + return (state() & (GDK_MOD1_MASK | GDK_CONTROL_MASK | GDK_META_MASK | GDK_SHIFT_MASK)) == 0; +} + +bool gKey::shift() +{ + return state() & GDK_SHIFT_MASK; // || _event.keyval == GDK_Shift_L || _event.keyval == GDK_Shift_R; +} + +int gKey::fromString(char *str) +{ + char *lstr; + int key; + + if (!str || !*str) + return 0; + + lstr = g_ascii_strup(str, -1); + key = gdk_keyval_from_name(lstr); + g_free(lstr); + if (key) return key; + + lstr = g_ascii_strdown(str, -1); + key = gdk_keyval_from_name(lstr); + g_free(lstr); + if (key) return key; + + key = gdk_keyval_from_name(str); + if (key) return key; + + if (!str[1] && isascii(str[0])) + return str[0]; + else + return 0; +} + +void gKey::disable() +{ + if (!_valid) + return; + + _valid = false; + _event.keyval = 0; + _event.state = 0; + //g_free(_event.string); +} + +bool gKey::enable(gControl *control, GdkEventKey *event) +{ + bool f = false; + + if (_valid) + disable(); + + _valid = true; + _canceled = false; + + if (event) + { + _im_no_commit = false; + + _event = *event; + _event.window = _im_window; + + if (gKey::mustIgnoreEvent(event)) + return true; + + if (control == _im_control) + { + #if DEBUG_IM + fprintf(stderr, "gKey::enable: [%p] flag = %d event->string = %d\n", event, (event->state & (1 << 25)) != 0, *event->string); + #endif + +#if 0 + if (_im_slave_is_xim) + { + /*if (_im_xim_abort == 2) + { + f = true; + _im_xim_abort = 0; + } + else*/ + { + GdkEventKey save = *event; + if (save.string) + save.string = g_strdup(save.string); + + f = gtk_im_context_filter_keypress(_im_context, event); + *event = save; + _im_xim_abort++; + } + } + else + f = gtk_im_context_filter_keypress(_im_context, event); +#endif + + if (!_im_has_input_method) + f = gtk_im_context_filter_keypress(_im_context, event); + + #if DEBUG_IM + fprintf(stderr, "gKey::enable: [%p] filter -> %d\n", event, f); + #endif + } + } + + return f || _canceled; +} + +bool gKey::mustIgnoreEvent(GdkEventKey *event) +{ + if (!_im_has_input_method) + return false; + else + return (event->type == GDK_KEY_PRESS) && ((uchar)*event->string >= 32 || event->keyval == 0); +} + +void gcb_im_commit(GtkIMContext *context, const char *str, gpointer pointer) +{ + bool disable = false; + + // Not called from a key press event! + if (!_im_control) + return; + + #if DEBUG_IM + fprintf(stderr, "cb_im_commit: \"%s\" _im_no_commit = %d gKey::valid = %d\n", str, _im_no_commit, gKey::valid()); + #endif + + if (!gKey::valid()) + { + gKey::enable(_im_control, NULL); + gKey::_event.keyval = gKey::_last_key_press; + disable = true; + } + + gKey::_canceled = gKey::raiseEvent(gEvent_KeyPress, _im_control, str); +#if DEBUG_IM + fprintf(stderr, "cb_im_commit: canceled = %d\n", gKey::_canceled); +#endif + + if (disable) + gKey::disable(); + + _im_no_commit = true; +} + +static gboolean hook_commit(GSignalInvocationHint *ihint, guint n_param_values, const GValue *param_values, gpointer data) +{ + _im_got_commit = TRUE; + return true; +} + +void gKey::init() +{ + GdkWindowAttr attr; + + attr.event_mask = GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK; + attr.width = attr.height = 10; + attr.wclass = GDK_INPUT_OUTPUT; + attr.window_type = GDK_WINDOW_TOPLEVEL; + + _im_window = gdk_window_new(NULL, &attr, 0); + + _im_context = gtk_im_multicontext_new(); + gtk_im_context_set_client_window (_im_context, _im_window); + + _im_default_slave = g_strdup(gtk_im_multicontext_get_context_id(GTK_IM_MULTICONTEXT(_im_context))); + + g_signal_connect(_im_context, "commit", G_CALLBACK(gcb_im_commit), NULL); + + g_signal_add_emission_hook(g_signal_lookup("commit", GTK_TYPE_IM_CONTEXT), (GQuark)0, hook_commit, (gpointer)0, NULL); +} + +void gKey::exit() +{ + disable(); + g_free(_im_default_slave); + g_object_unref(_im_context); +} + +void gKey::setActiveControl(gControl *control) +{ + const char *slave; + GtkIMContext *context; + + if (_im_control) + { +#if DEBUG_IM + fprintf(stderr, "gtk_im_context_focus_out\n"); +#endif + if (!_im_has_input_method) + { + gtk_im_context_reset(_im_context); + gtk_im_context_set_client_window (_im_context, 0); + gtk_im_context_reset(_im_context); + gtk_im_context_focus_out(_im_context); + gtk_im_context_reset(_im_context); + } + + _im_control = NULL; + } + + if (control) + { + _im_control = control; + + if (!control->hasInputMethod()) + { + _im_has_input_method = FALSE; + gtk_im_context_reset(_im_context); + gtk_im_context_set_client_window (_im_context, gtk_widget_get_window(control->widget)); + gtk_im_context_reset(_im_context); + gtk_im_context_focus_in(_im_context); + gtk_im_context_reset(_im_context); + //slave = gtk_im_multicontext_get_context_id(GTK_IM_MULTICONTEXT(_im_context)); + _im_is_xim = FALSE; + } + else + { + _im_has_input_method = TRUE; + context = control->getInputMethod(); + if (GTK_IS_IM_MULTICONTEXT(context)) + { + slave = gtk_im_multicontext_get_context_id(GTK_IM_MULTICONTEXT(context)); + _im_is_xim = slave && strcmp(slave, "xim") == 0; + } + else + { + _im_is_xim = FALSE; + } + } + + _im_ignore_event = FALSE; + +#if DEBUG_IM + fprintf(stderr,"\n------------------------\n"); + fprintf(stderr, "gtk_im_context_focus_in: _im_has_input_method = %d\n", _im_has_input_method); +#endif + } +} + +static bool raise_key_event_to_parent_window(gControl *control, int type) +{ + gMainWindow *win; + + while (control->parent()) + { + win = control->parent()->window(); + if (win->onKeyEvent && win->canRaise(win, type)) + { + //fprintf(stderr, "onKeyEvent: %d %p %s\n", type, win, win->name()); + if (win->onKeyEvent(win, type)) + return true; + } + + control = win; + } + + return false; +} + +#if 0 +static bool can_raise(GdkEventKey *event) +{ + int i; + + if (event->type == GDK_KEY_PRESS) + { + for (i = 0; i < MAX_CODE; i++) + { + if (event->keyval == _key_code[i]) + return false; + } + for (i = 0; i < MAX_CODE; i++) + { + if (!_key_code[i]) + { + //fprintf(stderr, "store key %d\n", event->keyval); + _key_code[i] = event->keyval; + break; + } + } + return true; + } + else + { + for (i = 0; i < MAX_CODE; i++) + { + if (event->keyval == _key_code[i]) + { + //fprintf(stderr, "remove key %d\n", event->keyval); + _key_code[i] = 0; + return true; + } + } + return false; + } +} +#endif + +bool gKey::raiseEvent(int type, gControl *control, const char *text) +{ + bool parent_got_it = false; + bool cancel = false; + +#if DEBUG_IM + fprintf(stderr, "gKey::raiseEvent to %p %s\n", control, control->name()); +#endif + + if (text) + _event.string = (gchar *)text; + + //if (!can_raise(&_event)) + // return false; + +__KEY_TRY_PROXY: + + if (!parent_got_it) + { + parent_got_it = true; + + if (gApplication::onKeyEvent) + cancel = gApplication::onKeyEvent(type); + + if (!cancel) + cancel = raise_key_event_to_parent_window(control, type); + } + + if (!cancel && control->onKeyEvent && control->canRaise(control, type)) + { + //fprintf(stderr, "gEvent_KeyPress on %p %s\n", control, control->name()); + //fprintf(stderr, "onKeyEvent: %p %d %p %s\n", event, type, control, control->name()); + cancel = control->onKeyEvent(control, type); + } + + if (cancel) + return true; + + if (control->_proxy_for) + { + control = control->_proxy_for; + goto __KEY_TRY_PROXY; + } + + return false; +} + +static bool check_button(gControl *w) +{ + return w && w->isVisible() && w->isEnabled(); +} + +gboolean gcb_key_event(GtkWidget *widget, GdkEvent *event, gControl *control) +{ + gMainWindow *win; + int type; + bool cancel; + +#if DEBUG_IM + fprintf(stderr, "gcb_key_event %s for %p %s\n", event->type == GDK_KEY_PRESS ? "GDK_KEY_PRESS" : "GDK_KEY_RELEASE", control, control->name()); +#endif + + /*if (!control->_grab && gApplication::activeControl()) + control = gApplication::activeControl();*/ + if (!control || control != gApplication::activeControl()) + return false; + +#if DEBUG_IM + fprintf(stderr, "handle it\n"); +#endif + + //if (event->type == GDK_KEY_PRESS) + // fprintf(stderr, "GDK_KEY_PRESS: control = %p %s %p %08X\n", control, control ? control->name() : "", event, event->key.state); + + if (_im_is_xim) + { + _im_ignore_event = !_im_ignore_event; + if (_im_ignore_event) + return false; + } + + type = (event->type == GDK_KEY_PRESS) ? gEvent_KeyPress : gEvent_KeyRelease; + + if (gKey::enable(control, &event->key)) + { + gKey::disable(); + return gKey::canceled() || !_im_has_input_method; + } + + if (gKey::mustIgnoreEvent(&event->key)) + { + gKey::disable(); + return true; + } + + cancel = gKey::raiseEvent(type, control, NULL); + gKey::disable(); + + if (cancel) + return true; + + win = control->window(); + + if (event->key.keyval == GDK_Escape) + { + if (control->_grab) + { + gApplication::exitLoop(control); + return true; + } + + if (check_button(win->_cancel)) + { + win->_cancel->setFocus(); + win->_cancel->animateClick(type == gEvent_KeyRelease); + return true; + } + } + else if (event->key.keyval == GDK_Return || event->key.keyval == GDK_KP_Enter) + { + if (check_button(win->_default)) + { + win->_default->setFocus(); + win->_default->animateClick(type == gEvent_KeyRelease); + return true; + } + } + + if (control->_grab) + return true; + + return false; +} + +bool gKey::gotCommit() +{ + bool ret = _im_got_commit; + _im_got_commit = FALSE; + return ret; +} diff --git a/gb.gtk/src/gkey.h b/gb.gtk/src/gkey.h new file mode 100644 index 00000000..865f2e7b --- /dev/null +++ b/gb.gtk/src/gkey.h @@ -0,0 +1,70 @@ +/*************************************************************************** + + gkey.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GKEY_H +#define __GKEY_H + +class gKey +{ +public: + static bool valid() { return _valid; } + static const char *text(); + static int code(); + static int state(); + + static bool alt(); + static bool control(); + static bool meta(); + static bool normal(); + static bool shift(); + + static int fromString(char* str); + +//"Private" + static void disable(); + static bool enable(gControl *control, GdkEventKey *e); + static bool canceled() { return _canceled; } + static void init(); + static void exit(); + + static void setActiveControl(gControl *control); + + static bool raiseEvent(int type, gControl *control, const char *text); + + static bool mustIgnoreEvent(GdkEventKey *e); + + static bool gotCommit(); + + static bool _canceled; + static GdkEventKey _event; + static int _last_key_press; + static int _last_key_release; + +private: + static bool _valid; +}; + +void gcb_im_commit(GtkIMContext *context, const char *str, gpointer pointer); +gboolean gcb_key_event(GtkWidget *widget, GdkEvent *event, gControl *data); + +#endif diff --git a/gb.gtk/src/glabel.cpp b/gb.gtk/src/glabel.cpp new file mode 100644 index 00000000..18303a50 --- /dev/null +++ b/gb.gtk/src/glabel.cpp @@ -0,0 +1,381 @@ +/*************************************************************************** + + glabel.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "glabel.h" + +#ifdef GTK3 +static gboolean cb_draw(GtkWidget *draw, cairo_t *cr, gLabel *d) +{ + GdkRGBA rgba; + int vw, vh, lw, lh; + int fw = d->getFramePadding() + d->getFrameWidth(); + int xa = d->lay_x; + + //d->drawBackground(cr); + d->drawBorder(cr); + + gt_from_color(d->realForeground(true), &rgba); + gdk_cairo_set_source_rgba(cr, &rgba); + + if (xa == 3) + { + if (gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL) + xa = 2; + else + xa = 0; + } + + switch (xa) + { + case 0: pango_layout_set_alignment(d->layout, PANGO_ALIGN_LEFT); break; + case 1: pango_layout_set_alignment(d->layout, PANGO_ALIGN_CENTER); break; + case 2: pango_layout_set_alignment(d->layout, PANGO_ALIGN_RIGHT); break; + } + + vw = d->width(); + vh = d->height(); + + pango_layout_get_pixel_size(d->layout, &lw, &lh); + + if (!d->markup || !d->wrap()) + { + switch (xa) + { + case 0: vw = fw; break; + case 1: vw = (vw - lw) / 2; break; + case 2: vw = vw - lw - fw; break; + } + } + else + vw = fw; + + switch (d->lay_y) + { + case 0: vh = fw; break; + case 1: vh = (vh - lh) / 2; break; + case 2: vh = vh - lh - fw; break; + } + + if (vh < 0) vh = 0; + + //vw += draw->allocation.x; + //vh += draw->allocation.y; + + cairo_move_to(cr, vw, vh); + pango_cairo_show_layout(cr, d->layout); + + return false; +} +#else +static gboolean cb_expose(GtkWidget *draw, GdkEventExpose *e, gLabel *d) +{ + GtkStyle *style = gtk_widget_get_style(draw); + cairo_t *cr; + int vw, vh, lw, lh; + int fw = d->getFramePadding() + d->getFrameWidth(); + int xa = d->lay_x; + + cr = gdk_cairo_create(draw->window); + gdk_cairo_region(cr, e->region); + cairo_clip(cr); + + if (style) + gdk_cairo_set_source_color(cr, &style->fg[GTK_STATE_NORMAL]); + + if (xa == 3) + { + if (gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL) + xa = 2; + else + xa = 0; + } + + vw = d->width(); + vh = d->height(); + + pango_layout_set_alignment(d->layout, PANGO_ALIGN_LEFT); + pango_layout_get_pixel_size(d->layout, &lw, &lh); + + switch (xa) + { + case 0: pango_layout_set_alignment(d->layout, PANGO_ALIGN_LEFT); break; + case 1: pango_layout_set_alignment(d->layout, PANGO_ALIGN_CENTER); break; + case 2: pango_layout_set_alignment(d->layout, PANGO_ALIGN_RIGHT); break; + } + + if (!d->markup || !d->wrap()) + { + switch (xa) + { + case 0: vw = fw; break; + case 1: vw = (vw - lw) / 2; break; + case 2: vw = vw - lw - fw; break; + } + } + else + vw = fw; + + switch (d->lay_y) + { + case 0: vh = fw; break; + case 1: vh = (vh - lh) / 2; break; + case 2: vh = vh - lh - fw; break; + } + + if (vh < 0) vh = 0; + + vw += draw->allocation.x; + vh += draw->allocation.y; + + cairo_move_to(cr, vw, vh); + pango_cairo_show_layout(cr, d->layout); + cairo_destroy(cr); + + d->drawBorder(e); + + return false; +} +#endif + +gLabel::gLabel(gContainer *parent) : gControl(parent) +{ + textdata = NULL; + g_typ = Type_gLabel; + markup = false; + _autoresize = false; + _mask_dirty = false; + _transparent = false; + _locked = false; + _wrap = false; + align = -1; + + border = widget = gtk_fixed_new(); + layout = gtk_widget_create_pango_layout(border, ""); + + realize(false); + + ON_DRAW(widget, this, cb_expose, cb_draw); + + setAlignment(ALIGN_NORMAL); + setText(""); +} + +gLabel::~gLabel() +{ + if (textdata) g_free(textdata); + g_object_unref(G_OBJECT(layout)); +} + +void gLabel::updateLayout() +{ + char *bpango; + + if (!textdata) + pango_layout_set_text(layout,"",-1); + else + { + if (markup) + { + bpango = gt_html_to_pango_string(textdata, -1, false); + if (!bpango) + pango_layout_set_text(layout,"",-1); + else + { + pango_layout_set_markup(layout,bpango,-1); + g_free(bpango); + } + } + else + pango_layout_set_text(layout,textdata,-1); + } + + gt_add_layout_from_font(layout, font()); +} + +void gLabel::updateSize(bool adjust, bool noresize_width) +{ + gint w, h; + int fw; + + updateLayout(); + + if (_locked || !textdata || !*textdata) + return; + + fw = getFrameWidth() + getFramePadding(); + + if (markup && _wrap) + { + w = width() - fw * 2; + if (w < 0) + return; + w *= PANGO_SCALE; + } + else + w = -1; + + pango_layout_set_width(layout, w); + + pango_layout_get_pixel_size(layout, &w, &h); + + if (!adjust && _wrap) + w = width(); + else + w += fw * 2; + + h += fw * 2; + + if ((!_autoresize && !adjust) || (noresize_width && w != width())) + return; + + if ((align == ALIGN_CENTER || align == ALIGN_LEFT || align == ALIGN_NORMAL || align == ALIGN_RIGHT) && h < height()) + h = height(); + + _locked = true; + resize(w, h); + _locked = false; +} + +void gLabel::adjust() +{ + updateSize(true); +} + +void gLabel::setAutoResize(bool vl) +{ + _autoresize = vl; + updateSize(); +} + +void gLabel::afterRefresh() +{ + if (!isVisible()) + return; + + _mask_dirty = _transparent; +} + +void gLabel::setTransparent(bool vl) +{ + if (_transparent == vl) + return; + + _transparent = vl; + //gtk_widget_shape_combine_mask(border, NULL, 0, 0); + //refresh(); +} + +char *gLabel::text() +{ + return textdata; +} + +void gLabel::setText(const char *vl) +{ + //bool no_text_before = !textdata || !*textdata; + + g_free(textdata); + + if (vl) + textdata = g_strdup(vl); + else + textdata = 0; + + updateSize(); + refresh(); +} + +void gLabel::enableMarkup(bool vl) +{ + if (markup != vl) + { + markup = vl; + updateSize(); + refresh(); + } +} + + +int gLabel::alignment() +{ + return align; +} + +void gLabel::setAlignment(int al) +{ + if (align == al) + return; + + switch (al) + { + case ALIGN_BOTTOM: lay_y=2; lay_x=1; break; + case ALIGN_BOTTOM_LEFT: lay_y=2; lay_x=0; break; + case ALIGN_BOTTOM_NORMAL: lay_y=2; lay_x=3; break; + case ALIGN_BOTTOM_RIGHT: lay_y=2; lay_x=2; break; + case ALIGN_CENTER: lay_y=1; lay_x=1; break; + case ALIGN_LEFT: lay_y=1; lay_x=0; break; + case ALIGN_NORMAL: lay_y=1; lay_x=3; break; + case ALIGN_RIGHT: lay_y=1; lay_x=2; break; + case ALIGN_TOP: lay_y=0; lay_x=1; break; + case ALIGN_TOP_LEFT: lay_y=0; lay_x=0; break; + case ALIGN_TOP_NORMAL: lay_y=0; lay_x=3; break; + case ALIGN_TOP_RIGHT: lay_y=0; lay_x=2; break; + default: return; + } + + align = al; + refresh(); +} + + +void gLabel::resize(int w, int h) +{ + bool update = markup && width() != w; + gControl::resize(w, h); + if (update) + updateSize(false); +} + +void gLabel::setWrap(bool v) +{ + _wrap = v; + updateSize(true); +} + +/*gColor gLabel::getFrameColor() +{ + return realForeground(); +}*/ + +void gLabel::updateSize() +{ + updateSize(false); +} + + +void gLabel::updateBorder() +{ + gControl::updateBorder(); + updateSize(false); +} \ No newline at end of file diff --git a/gb.gtk/src/glabel.h b/gb.gtk/src/glabel.h new file mode 100644 index 00000000..32c79f70 --- /dev/null +++ b/gb.gtk/src/glabel.h @@ -0,0 +1,74 @@ +/*************************************************************************** + + glabel.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GSIMPLELABEL_H +#define __GSIMPLELABEL_H + +#include "gcontrol.h" + +class gLabel : public gControl +{ +public: + gLabel(gContainer *parent); + ~gLabel(); + + int alignment(); + int getBorder() const { return getFrameBorder(); } + char* text(); + bool isTransparent() const { return _transparent; } + bool autoResize() const { return _autoresize; } + int padding() const { return getFramePadding(); } + bool wrap() const { return _wrap; } + + void setAlignment(int al); + void setBorder(int vl) { setFrameBorder(vl); } + void setText(const char *st); + void setTransparent(bool vl); + void setAutoResize(bool vl); + void setPadding(int vl) { setFramePadding(vl); } + void setWrap(bool vl); + +//"Methods" + void enableMarkup(bool vl); + void adjust(); + virtual void resize(int w, int h); + virtual void afterRefresh(); + +//"Private" + //virtual gColor getFrameColor(); + virtual void updateSize(); + virtual void updateBorder(); + void updateSize(bool adjust, bool noresize = false); + void updateLayout(); + PangoLayout *layout; + int align,lay_x,lay_y; + unsigned markup : 1; + unsigned _autoresize : 1; + unsigned _transparent : 1; + unsigned _mask_dirty : 1; + unsigned _locked : 1; + unsigned _wrap : 1; + char *textdata; +}; + +#endif diff --git a/gb.gtk/src/gmainwindow.cpp b/gb.gtk/src/gmainwindow.cpp new file mode 100644 index 00000000..28a49ecb --- /dev/null +++ b/gb.gtk/src/gmainwindow.cpp @@ -0,0 +1,1732 @@ +/*************************************************************************** + + gmainwindow.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include +#include + +#include "widgets.h" + +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "x11.h" +#include "sm/sm.h" + +#include "gapplication.h" +#include "gdesktop.h" +#include "gkey.h" +#include "gmenu.h" +#include "gmessage.h" +#include "gdialog.h" +#include "gmouse.h" +#include "gmainwindow.h" + +static gboolean cb_frame(GtkWidget *widget,GdkEventWindowState *event,gMainWindow *data) +{ + data->performArrange(); + data->emit(SIGNAL(data->onState)); + return false; +} + +static gboolean cb_show(GtkWidget *widget, gMainWindow *data) +{ + data->emitOpen(); + + if (data->opened) + { + data->setGeometryHints(); + + //data->performArrange(); + data->emitResize(); + data->emit(SIGNAL(data->onShow)); + data->_not_spontaneous = false; + } + return false; +} + +static gboolean cb_map(GtkWidget *widget, GdkEvent *event, gMainWindow *data) +{ + data->_unmap = false; + return cb_show(widget, data); +} + +static gboolean cb_hide(GtkWidget *widget, gMainWindow *data) +{ + if (!data->_unmap) + { + data->emit(SIGNAL(data->onHide)); + data->_not_spontaneous = false; + } + + return false; + //if (data == gDesktop::activeWindow()) + // gMainWindow::setActiveWindow(NULL); +} + +static gboolean cb_unmap(GtkWidget *widget, GdkEvent *event, gMainWindow *data) +{ + bool ret = cb_hide(widget, data); + data->_unmap = true; + return ret; +} + +static gboolean cb_close(GtkWidget *widget,GdkEvent *event, gMainWindow *data) +{ + if (!gMainWindow::_current || data == gMainWindow::_current) + data->doClose(); + + return true; +} + +static gboolean cb_configure(GtkWidget *widget, GdkEventConfigure *event, gMainWindow *data) +{ + gint x, y; + + if (data->opened) + { + if (data->isTopLevel()) + { + gtk_window_get_position(GTK_WINDOW(data->border), &x, &y); + } + else + { + x = event->x; + y = event->y; + } + + //fprintf(stderr, "cb_configure: %s: (%d %d %d %d) -> (%d %d %d %d) window = %p resized = %d send_event = %d\n", data->name(), data->bufX, data->bufY, data->bufW, data->bufH, x, y, event->width, event->height, event->window, data->_resized, event->send_event); + + if (x != data->bufX || y != data->bufY) + { + data->bufX = x; + data->bufY = y; + if (data->onMove) data->onMove(data); + } + + if ((event->width != data->bufW) || (event->height != data->bufH) || (data->_resized) || !event->window) + { + //BREAKPOINT(); + data->_resized = false; + data->bufW = event->width; + data->bufH = event->height; + data->emitResize(); + } + } + + return false; +} + +#ifdef GTK3 +static gboolean cb_draw(GtkWidget *wid, cairo_t *cr, gMainWindow *data) +{ + if (data->isTransparent()) + { + if (data->background() == COLOR_DEFAULT) + gt_cairo_set_source_color(cr, 0xFF000000); + else + gt_cairo_set_source_color(cr, data->background()); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + } + + if (data->_picture) + { + cairo_pattern_t *pattern; + + pattern = cairo_pattern_create_for_surface(data->_picture->getSurface()); + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + + cairo_set_source(cr, pattern); + cairo_paint(cr); + + cairo_pattern_destroy(pattern); + } + + return false; +} +#else +static gboolean cb_expose(GtkWidget *wid, GdkEventExpose *e, gMainWindow *data) +{ + bool draw_bg = data->isTransparent(); + bool draw_pic = data->_picture; + + if (!draw_bg && !draw_pic) + return false; + + cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(wid)); + + if (draw_bg) + { + if (data->background() == COLOR_DEFAULT) + gt_cairo_set_source_color(cr, 0xFF000000); + else + gt_cairo_set_source_color(cr, data->background()); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + } + + if (draw_pic) + { + cairo_pattern_t *pattern; + + gdk_cairo_region(cr, e->region); + cairo_clip(cr); + + pattern = cairo_pattern_create_for_surface(data->_picture->getSurface()); + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + + cairo_set_source(cr, pattern); + cairo_paint(cr); + + cairo_pattern_destroy(pattern); + } + + cairo_destroy(cr); + return false; +} +#endif + + + +GList *gMainWindow::windows = NULL; +gMainWindow *gMainWindow::_active = NULL; +gMainWindow *gMainWindow::_current = NULL; + +void gMainWindow::initialize() +{ + //fprintf(stderr, "new window: %p in %p\n", this, parent()); + + stack = 0; + _type = 0; + accel = NULL; + _default = NULL; + _cancel = NULL; + menuBar = NULL; + layout = NULL; + _icon = NULL; + _picture = NULL; + focus = 0; + _title = NULL; + _current = NULL; + _style = NULL; + _resize_last_w = _resize_last_h = -1; + _min_w = _min_h = 0; + + opened = false; + sticky = false; + persistent = false; + _mask = false; + _masked = false; + _resized = false; + top_only = false; + _closing = false; + _not_spontaneous = false; + _skip_taskbar = false; + _xembed = false; + _activate = false; + _hidden = false; + _hideMenuBar = false; + _showMenuBar = true; + _popup = false; + _maximized = _minimized = _fullscreen = false; + _transparent = false; + _utility = false; + _no_take_focus = false; + _moved = false; + _resizable = true; + _unmap = false; + + onOpen = NULL; + onShow = NULL; + onHide = NULL; + onMove = NULL; + onResize = NULL; + onActivate = NULL; + onDeactivate = NULL; + onState = NULL; + onFontChange = NULL; + + accel = gtk_accel_group_new(); +} + +void gMainWindow::initWindow() +{ + //resize(200,150); + + if (!isTopLevel()) + { + g_signal_connect(G_OBJECT(border), "configure-event", G_CALLBACK(cb_configure), (gpointer)this); + g_signal_connect_after(G_OBJECT(border), "map", G_CALLBACK(cb_show), (gpointer)this); + g_signal_connect(G_OBJECT(border),"unmap", G_CALLBACK(cb_hide),(gpointer)this); + //g_signal_connect_after(G_OBJECT(border), "size-allocate", G_CALLBACK(cb_configure), (gpointer)this); + ON_DRAW_BEFORE(widget, this, cb_expose, cb_draw); + gtk_widget_add_events(border, GDK_STRUCTURE_MASK); + } + else + { + //g_signal_connect(G_OBJECT(border),"size-request",G_CALLBACK(cb_realize),(gpointer)this); + //g_signal_connect(G_OBJECT(border), "show", G_CALLBACK(cb_show),(gpointer)this); + g_signal_connect(G_OBJECT(border), "hide", G_CALLBACK(cb_hide),(gpointer)this); + g_signal_connect(G_OBJECT(border), "map-event", G_CALLBACK(cb_map),(gpointer)this); + g_signal_connect(G_OBJECT(border), "unmap-event", G_CALLBACK(cb_unmap),(gpointer)this); + g_signal_connect(G_OBJECT(border), "configure-event", G_CALLBACK(cb_configure),(gpointer)this); + g_signal_connect(G_OBJECT(border), "delete-event", G_CALLBACK(cb_close),(gpointer)this); + g_signal_connect(G_OBJECT(border), "window-state-event", G_CALLBACK(cb_frame),(gpointer)this); + + gtk_widget_add_events(widget,GDK_BUTTON_MOTION_MASK); + ON_DRAW_BEFORE(border, this, cb_expose, cb_draw); + } + + gtk_window_add_accel_group(GTK_WINDOW(topLevel()->border), accel); + + have_cursor = true; //parent() == 0 && !_xembed; +} + +#if 0 //def GTK3 + +static void (*old_fixed_get_preferred_width)(GtkWidget *, gint *, gint *); +static void (*old_fixed_get_preferred_height)(GtkWidget *, gint *, gint *); + +static void gtk_fixed_get_preferred_width(GtkWidget *widget, gint *minimum_size, gint *natural_size) +{ + (*old_fixed_get_preferred_width)(widget, minimum_size, natural_size); + *minimum_size = 0; +} + +static void gtk_fixed_get_preferred_height(GtkWidget *widget, gint *minimum_size, gint *natural_size) +{ + (*old_fixed_get_preferred_height)(widget, minimum_size, natural_size); + *minimum_size = 0; +} + +#endif + +gMainWindow::gMainWindow(int plug) : gContainer(NULL) +{ + initialize(); + g_typ = Type_gMainWindow; + + windows = g_list_append(windows, (gpointer)this); + + _xembed = plug != 0; + + if (_xembed) + border = gtk_plug_new(plug); + else + border = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + + widget = gtk_fixed_new(); //gtk_layout_new(0,0); + +#if 0 //def GTK3 + static bool patch = FALSE; + + if (!patch) + { + GtkWidgetClass *klass; + + klass = (GtkWidgetClass *)GTK_FIXED_GET_CLASS(widget); + old_fixed_get_preferred_width = klass->get_preferred_width; + klass->get_preferred_width = gtk_fixed_get_preferred_width; + old_fixed_get_preferred_height = klass->get_preferred_height; + klass->get_preferred_height = gtk_fixed_get_preferred_height; + /*klass = (GtkWidgetClass *)GTK_FIXED_GET_CLASS(border); + old_window_get_preferred_width = klass->get_preferred_width; + klass->get_preferred_width = gtk_window_get_preferred_width; + old_window_get_preferred_height = klass->get_preferred_height; + klass->get_preferred_height = gtk_window_get_preferred_height;*/ + + patch = true; + } +#endif + + realize(false); + initWindow(); + + gtk_widget_realize(border); + gtk_widget_show(widget); + gtk_widget_set_size_request(border, 1, 1); + + setCanFocus(false); +} + +gMainWindow::gMainWindow(gContainer *par) : gContainer(par) +{ + initialize(); + g_typ = Type_gMainWindow; + +#ifdef GTK3 + //border = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + border = gtk_fixed_new(); +#else + border = gtk_alignment_new(0, 0, 1, 1); +#endif + widget = gtk_fixed_new(); + + realize(false); + initWindow(); + + setCanFocus(false); +} + +gMainWindow::~gMainWindow() +{ + //fprintf(stderr, "delete window %p %s\n", this, name()); + + gApplication::handleFocusNow(); + + if (opened) + { + emit(SIGNAL(onClose)); + opened = false; + if (GTK_IS_WINDOW(border) && isModal()) + gApplication::exitLoop(this); + } + + gPicture::assign(&_picture); + gPicture::assign(&_icon); + if (_title) g_free(_title); + g_object_unref(accel); + if (_style) g_object_unref(_style); + + if (_active == this) + _active = NULL; + + if (gApplication::mainWindow() == this) + gApplication::setMainWindow(NULL); + + windows = g_list_remove(windows, (gpointer)this); +} + +bool gMainWindow::getSticky() +{ + return sticky; +} + +int gMainWindow::getStacking() +{ + return stack; +} + +void gMainWindow::setSticky(bool vl) +{ + sticky=vl; + if (!isTopLevel()) return; + + if (vl) gtk_window_stick(GTK_WINDOW(border)); + else gtk_window_unstick(GTK_WINDOW(border)); +} + +void gMainWindow::setStacking(int vl) +{ + stack=vl; + if (!isTopLevel()) return; + + switch (vl) + { + case 0: + gtk_window_set_keep_below(GTK_WINDOW(border),FALSE); + gtk_window_set_keep_above(GTK_WINDOW(border),FALSE); + break; + case 1: + gtk_window_set_keep_below(GTK_WINDOW(border),FALSE); + gtk_window_set_keep_above(GTK_WINDOW(border),TRUE); + break; + case 2: + gtk_window_set_keep_above(GTK_WINDOW(border),FALSE); + gtk_window_set_keep_below(GTK_WINDOW(border),TRUE); + break; + } +} + +void gMainWindow::setRealBackground(gColor color) +{ + if (!_picture) + { + gControl::setRealBackground(color); + gMenu::updateColor(this); + } +} + +void gMainWindow::setRealForeground(gColor color) +{ + gControl::setRealForeground(color); + gMenu::updateColor(this); +} + +void gMainWindow::move(int x, int y) +{ + //gint ox, oy; + + if (isTopLevel()) + { + if (!_moved && (x || y)) + _moved = true; + + if (x == bufX && y == bufY) + return; + + /*#ifdef GDK_WINDOWING_X11 + gdk_window_get_origin(gtk_widget_get_window(border), &ox, &oy); + ox = x + ox - bufX; + oy = y + oy - bufY; + bufX = x; + bufY = y; + if (bufW > 0 && bufH > 0) + { + if (!X11_send_move_resize_event(GDK_WINDOW_XID(gtk_widget_get_window(border)), ox, oy, width(), height())) + return; + } + #else*/ + bufX = x; + bufY = y; + //#endif + + gtk_window_move(GTK_WINDOW(border), x, y); + } + else + { + gContainer::move(x,y); + } +} + + +void gMainWindow::resize(int w, int h) +{ + if (w == bufW && h == bufH) + return; + + _resized = true; + + if (isTopLevel()) + { + //fprintf(stderr, "gMainWindow::resize: %d %d %s\n", w, h, name()); + //gdk_window_enable_synchronized_configure (border->window); + + bufW = w < 0 ? 0 : w; + bufH = h < 0 ? 0 : h; + + if (w < 1 || h < 1) + { + if (visible) + gtk_widget_hide(border); + } + else + { + if (isResizable()) + gtk_window_resize(GTK_WINDOW(border), w, h); + else + { + gtk_widget_set_size_request(border, w, h); + } + + if (visible) + gtk_widget_show(border); + } + } + else + { + //fprintf(stderr, "resize %p -> (%d %d) (%d %d)\n", this, bufW, bufH, w, h); + gContainer::resize(w, h); + } +} + +void gMainWindow::moveResize(int x, int y, int w, int h) +{ + //if (isTopLevel()) + // gdk_window_move_resize(border->window, x, y, w, h); + //else + gContainer::moveResize(x, y, w, h); +} + +void gMainWindow::emitOpen() +{ + //fprintf(stderr, "emit Open: %p (%d %d) %d resizable = %d fullscreen = %d\n", this, width(), height(), opened, isResizable(), fullscreen()); + + if (!opened) + { + opened = true; + //_no_resize_event = true; // If the event loop is run during emitOpen(), some spurious configure events are received. + + if (!_min_w && !_min_h) + { + _min_w = width(); + _min_h = height(); + } + + + gtk_widget_realize(border); + + performArrange(); + emit(SIGNAL(onOpen)); + if (opened) + { + //fprintf(stderr, "emit Move & Resize: %p\n", this); + emit(SIGNAL(onMove)); + emitResize(); + } + } + + //_no_resize_event = false; +} + +void gMainWindow::present() +{ + if (_no_take_focus) + gtk_widget_show(GTK_WIDGET(border)); + else + gtk_window_present(GTK_WINDOW(border)); +} + +void gMainWindow::afterShow() +{ + if (_activate) + { + present(); + _activate = false; + } +} + +void gMainWindow::setVisible(bool vl) +{ + if (!vl) + _hidden = true; + + if (vl == isVisible()) + return; + + if (vl) + { + bool arr = !isVisible(); + + emitOpen(); + if (!opened) + return; + + _not_spontaneous = !visible; + visible = true; + _hidden = false; + + setTransparent(_transparent); // must not call gtk_window_present! + + if (isTopLevel()) + { + /*if (!_xembed) + { + fprintf(stderr, "gtk_window_group_add_window: %p -> %p\n", border, gApplication::currentGroup()); + gtk_window_group_add_window(gApplication::currentGroup(), GTK_WINDOW(border)); + fprintf(stderr, "-> %p\n", gtk_window_get_group(GTK_WINDOW(border))); + }*/ + + // Thanks for Ubuntu's GTK+ patching :-( + #ifndef GTK3 + //gtk_window_set_has_resize_grip(GTK_WINDOW(border), false); + if (g_object_class_find_property(G_OBJECT_GET_CLASS(border), "has-resize-grip")) + g_object_set(G_OBJECT(border), "has-resize-grip", false, (char *)NULL); + #endif + + gtk_window_move(GTK_WINDOW(border), bufX, bufY); + + if (isPopup()) + { + gtk_widget_show_now(border); + gtk_widget_grab_focus(border); + } + else + { + present(); + } + + if (!_title || !*_title) + gtk_window_set_title(GTK_WINDOW(border), gApplication::defaultTitle()); + + if (isUtility()) + { + gMainWindow *parent = _current; + + if (!parent && gApplication::mainWindow() && gApplication::mainWindow() != this) + parent = gApplication::mainWindow(); + + if (parent) + gtk_window_set_transient_for(GTK_WINDOW(border), GTK_WINDOW(parent->border)); + + if (!_no_take_focus) + present(); + } + + if (gApplication::mainWindow() == this) + { + int desktop = session_manager_get_desktop(); + if (desktop >= 0) + { + //fprintf(stderr, "X11_window_set_desktop: %d (%d)\n", desktop, true); + X11_window_set_desktop((Window)handle(), true, desktop); + session_manager_set_desktop(-1); + } + } + } + else + { + gtk_widget_show(border); + parent()->performArrange(); + } + + drawMask(); + + if (focus) + { + //fprintf(stderr, "focus = %s\n", focus->name()); + focus->setFocus(); + focus = 0; + } + + if (skipTaskBar()) + _activate = true; + + if (arr) + performArrange(); + } + else + { + if (this == _active) + focus = gApplication::activeControl(); + + _not_spontaneous = visible; + gContainer::setVisible(false); + + if (_popup) + gApplication::exitLoop(this); + + if (gApplication::_button_grab && !gApplication::_button_grab->isReallyVisible()) + gApplication::setButtonGrab(NULL); + } +} + + +void gMainWindow::setMinimized(bool vl) +{ + if (!isTopLevel()) return; + + _minimized = vl; + if (vl) gtk_window_iconify(GTK_WINDOW(border)); + else gtk_window_deiconify(GTK_WINDOW(border)); +} + +void gMainWindow::setMaximized(bool vl) +{ + if (!isTopLevel()) + return; + + _maximized = vl; + + if (vl) + gtk_window_maximize(GTK_WINDOW(border)); + else + gtk_window_unmaximize(GTK_WINDOW(border)); +} + +void gMainWindow::setFullscreen(bool vl) +{ + if (!isTopLevel()) + return; + + _fullscreen = vl; + + if (vl) + { + gtk_window_fullscreen(GTK_WINDOW(border)); + if (isVisible()) + present(); + } + else + gtk_window_unfullscreen(GTK_WINDOW(border)); +} + +void gMainWindow::center() +{ + GdkRectangle rect; + int x, y; + + if (!isTopLevel()) return; + + gDesktop::availableGeometry(screen(), &rect); + + x = rect.x + (rect.width - width()) / 2; + y = rect.y + (rect.height - height()) / 2; + + move(x, y); +} + +bool gMainWindow::isModal() const +{ + if (!isTopLevel()) return false; + + return gtk_window_get_modal(GTK_WINDOW(border)); +} + +void gMainWindow::showModal() +{ + gMainWindow *save; + gMainWindow *parent; + + if (!isTopLevel()) return; + if (isModal()) return; + + //show(); + + gtk_window_set_modal(GTK_WINDOW(border), true); + center(); + //show(); + gtk_grab_add(border); + + parent = _current; + if (!parent) + { + parent = gApplication::mainWindow(); + if (!parent) + parent = _active; + } + + if (parent) + gtk_window_set_transient_for(GTK_WINDOW(border), GTK_WINDOW(parent->topLevel()->border)); + + save = _current; + _current = this; + + gApplication::enterLoop(this, true); + + _current = save; + + gtk_grab_remove(border); + gtk_window_set_modal(GTK_WINDOW(border), false); + + if (!persistent) + destroyNow(); + else + hide(); +} + +void gMainWindow::showPopup(int x, int y) +{ + gMainWindow *save; + bool has_border; + int oldx, oldy; + //int type; + + if (!isTopLevel()) return; + if (isModal()) return; + + //gtk_widget_unrealize(border); + //((GtkWindow *)border)->type = GTK_WINDOW_POPUP; + //gtk_widget_realize(border); + + oldx = left(); + oldy = top(); + + has_border = gtk_window_get_decorated(GTK_WINDOW(border)); + //type = getType(); + + //setType(_NET_WM_WINDOW_TYPE_COMBO); + gtk_window_set_decorated(GTK_WINDOW(border), false); + //gtk_window_set_type_hint(GTK_WINDOW(border), GDK_WINDOW_TYPE_HINT_POPUP_MENU); + + move(x, y); + gtk_window_resize(GTK_WINDOW(border), bufW, bufH); + + //reparent(NULL, x, y, GTK_WINDOW_POPUP); + + _popup = true; + save = _current; + _current = this; + + gApplication::enterPopup(this); + + _current = save; + _popup = false; + + if (!persistent) + { + destroyNow(); + } + else + { + hide(); + + //gdk_window_set_override_redirect(gtk_widget_get_window(GTK_WINDOW(border)), false); + gtk_window_set_decorated(GTK_WINDOW(border), has_border); + //setType(type); + //gtk_window_set_type_hint(GTK_WINDOW(border), type); + + move(oldx, oldy); + } +} + +void gMainWindow::showActivate() +{ + bool v = isTopLevel() && isVisible() && !_no_take_focus; + + if (!_moved) + center(); + show(); + if (v) + present(); +} + +void gMainWindow::activate() +{ + if (isTopLevel() && isVisible()) + present(); +} + +void gMainWindow::showPopup() +{ + int x, y; + gMouse::getScreenPos(&x, &y); + showPopup(x, y); +} + +void gMainWindow::raise() +{ + if (!isTopLevel()) { gControl::raise(); return; } + present(); +} + +const char* gMainWindow::text() +{ + return _title; +} + +bool gMainWindow::skipTaskBar() +{ + if (!isTopLevel()) + return false; + else + return _skip_taskbar; +} + + +void gMainWindow::setText(const char *txt) +{ + if (_title) g_free(_title); + _title = g_strdup(txt); + + if (isTopLevel()) + gtk_window_set_title(GTK_WINDOW(border), txt); +} + +bool gMainWindow::hasBorder() +{ + if (isTopLevel()) + return gtk_window_get_decorated(GTK_WINDOW(border)); + else + return false; +} + +bool gMainWindow::isResizable() +{ + if (isTopLevel()) + return _resizable; + else + return false; +} + +void gMainWindow::setBorder(bool b) +{ + if (!isTopLevel()) + return; + + gtk_window_set_decorated(GTK_WINDOW(border), b); + /*#ifdef GDK_WINDOWING_X11 + XSetWindowAttributes attr; + attr.override_redirect = !b; + XChangeWindowAttributes(GDK_WINDOW_XDISPLAY(border->window), GDK_WINDOW_XID(border->window), CWOverrideRedirect, &attr); + #endif*/ +} + +void gMainWindow::setResizable(bool b) +{ + if (!isTopLevel()) + return; + + if (b == isResizable()) + return; + + _resizable = b; + setGeometryHints(); +} + + +void gMainWindow::setSkipTaskBar(bool b) +{ + _skip_taskbar = b; + if (!isTopLevel()) return; + gtk_window_set_skip_taskbar_hint(GTK_WINDOW(border), b); +} + + +/*gPicture* gMainWindow::icon() +{ + GdkPixbuf *buf; + gPicture *pic; + + if (!isTopLevel()) return NULL; + + buf=gtk_window_get_icon(GTK_WINDOW(border)); + if (!buf) return NULL; + + pic=gPicture::fromPixbuf(buf); + + return pic; +}*/ + +void gMainWindow::setIcon(gPicture *pic) +{ + gPicture::assign(&_icon, pic); + + if (!isTopLevel()) return; + gtk_window_set_icon(GTK_WINDOW(border), pic ? pic->getPixbuf() : NULL); +} + +bool gMainWindow::topOnly() +{ + if (!isTopLevel()) return false; + + return top_only; +} + +void gMainWindow::setTopOnly(bool vl) +{ + if (!isTopLevel()) return; + + gtk_window_set_keep_above (GTK_WINDOW(border),vl); + top_only=vl; +} + + +void gMainWindow::setMask(bool vl) +{ + if (_mask == vl) + return; + + _mask = vl; + drawMask(); +} + +void gMainWindow::setPicture(gPicture *pic) +{ + gPicture::assign(&_picture, pic); + drawMask(); +} + +void gMainWindow::remap() +{ + if (!isVisible()) + return; + + gtk_widget_unmap(border); + gtk_widget_map(border); + + if (_skip_taskbar) { setSkipTaskBar(false); setSkipTaskBar(true); } + if (top_only) { setTopOnly(false); setTopOnly(true); } + if (sticky) { setSticky(false); setSticky(true); } + if (stack) { setStacking(0); setStacking(stack); } + X11_set_window_type(handle(), _type); +} + +void gMainWindow::drawMask() +{ + bool do_remap = false; + + if (!isVisible()) + return; + +#ifdef GTK3 + + cairo_region_t *mask; + + if (_mask && _picture) + mask = gdk_cairo_region_create_from_surface(_picture->getSurface()); + else + mask = NULL; + + gdk_window_shape_combine_region(gtk_widget_get_window(border), mask, 0, 0); + if (mask) + cairo_region_destroy(mask); + + refresh(); + +#else + + GdkBitmap *mask = (_mask && _picture) ? _picture->getMask() : NULL; + do_remap = !mask && _masked; + + gdk_window_shape_combine_mask(border->window, mask, 0, 0); + +#endif + + if (_picture) + { + gtk_widget_set_app_paintable(border, TRUE); + gtk_widget_realize(border); + gtk_widget_realize(widget); + for (int i = 0; i < controlCount(); i++) + getControl(i)->refresh(); + } + else if (!_transparent) + { + gtk_widget_set_app_paintable(border, FALSE); + setRealBackground(background()); + } + + _masked = mask != NULL; + + if (do_remap) + remap(); + else + { + if (!_skip_taskbar) + { + setSkipTaskBar(true); + setSkipTaskBar(false); + } + } +} + +int gMainWindow::menuCount() +{ + if (!menuBar) return 0; + return gMenu::winChildCount(this); +} + +void gMainWindow::setPersistent(bool vl) +{ + persistent = vl; +} + +bool gMainWindow::doClose() +{ + if (_closing) + return false; + + if (opened) + { + if (isModal() && !gApplication::hasLoop(this)) + return true; + + _closing = true; + if (onClose) + { + if (!onClose(this)) + opened = false; + } + else + opened = false; + _closing = false; + + if (!opened && isModal()) + gApplication::exitLoop(this); + } + + if (!opened) // && !modal()) + { + if (_active == this) + setActiveWindow(NULL); + + if (!isModal()) + { + if (persistent) + hide(); + else + destroy(); + } + return false; + } + else + return opened; +} + + +bool gMainWindow::close() +{ + return doClose(); +} + +static void hide_hidden_children(gContainer *cont) +{ + int i; + gControl *child; + + for (i = 0;; i++) + { + child = cont->child(i); + if (!child) + break; + if (!child->isVisible()) + gtk_widget_hide(child->border); + else if (child->isContainer()) + hide_hidden_children((gContainer *)child); + } +} + +void gMainWindow::reparent(gContainer *newpr, int x, int y) +{ + GtkWidget *new_border; + int w, h; + gColor fg, bg; + + if (_xembed) + return; + + bg = background(); + fg = foreground(); + + if (isTopLevel() && newpr) + { + gtk_window_remove_accel_group(GTK_WINDOW(topLevel()->border), accel); + + new_border = gtk_event_box_new(); + gt_widget_reparent(widget, new_border); + embedMenuBar(new_border); + _no_delete = true; + gtk_widget_destroy(border); + _no_delete = false; + + border = new_border; + registerControl(); + setCanFocus(false); + + setParent(newpr); + connectParent(); + borderSignals(); + initWindow(); + + setBackground(bg); + setForeground(fg); + setFont(font()); + + checkMenuBar(); + + bufX = bufY = 0; + move(x, y); + + gtk_widget_set_size_request(border, width(), height()); + + // Hidden children are incorrectly shown. Fix that! + hideHiddenChildren(); + } + else if ((!isTopLevel() && !newpr) + || (isTopLevel() && isPopup())) + //|| (isTopLevel() && (isPopup() ^ (type == GTK_WINDOW_POPUP)))) + { + gtk_window_remove_accel_group(GTK_WINDOW(topLevel()->border), accel); + // TODO: test that + new_border = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gt_widget_reparent(widget, new_border); + embedMenuBar(new_border); + _no_delete = true; + gtk_widget_destroy(border); + _no_delete = false; + + border = new_border; + registerControl(); + setCanFocus(true); + + if (parent()) + { + parent()->remove(this); + parent()->arrange(); + setParent(NULL); + } + initWindow(); + borderSignals(); + setBackground(bg); + setForeground(fg); + setFont(font()); + + move(x, y); + w = width(); + h = height(); + bufW = bufH = -1; + gtk_widget_set_size_request(border, 1, 1); + resize(w, h); + + hideHiddenChildren(); + _popup = false; //type == GTK_WINDOW_POPUP; + } + else + { + gContainer::reparent(newpr, x, y); + } +} + + +int gMainWindow::controlCount() +{ + GList *list = gControl::controlList(); + gControl *ctrl; + int n = 0; + + while (list) + { + ctrl = (gControl *)list->data; + if (ctrl->window() == this && !ctrl->isDestroyed()) + n++; + list = g_list_next(list); + } + + return n; +} + +gControl *gMainWindow::getControl(char *name) +{ + GList *list = gControl::controlList(); + gControl *ctrl; + + while (list) + { + ctrl = (gControl *)list->data; + if (ctrl->window() == this && !strcasecmp(ctrl->name(), name) && !ctrl->isDestroyed()) + return ctrl; + list = g_list_next(list); + } + + return NULL; +} + +gControl *gMainWindow::getControl(int index) +{ + GList *list = gControl::controlList(); + gControl *ctrl; + int i = 0; + + while (list) + { + ctrl = (gControl *)list->data; + if (ctrl->window() == this && !ctrl->isDestroyed()) + { + if (i == index) + return ctrl; + i++; + } + list = g_list_next(list); + } + + return NULL; +} + + +int gMainWindow::clientX() +{ + return 0; +} + +int gMainWindow::containerX() +{ + return 0; +} + +int gMainWindow::clientY() +{ + if (isMenuBarVisible()) + return menuBarHeight(); + else + return 0; +} + +int gMainWindow::containerY() +{ + return 0; +} + + +int gMainWindow::clientWidth() +{ + return width(); +} + + +int gMainWindow::menuBarHeight() +{ + int h = 0; + + if (menuBar) + { + //gtk_widget_show(GTK_WIDGET(menuBar)); + //fprintf(stderr, "menuBarHeight: gtk_widget_get_visible: %d\n", gtk_widget_get_visible(GTK_WIDGET(menuBar))); +#ifdef GTK3 + gtk_widget_get_preferred_height(GTK_WIDGET(menuBar), NULL, &h); +#else + GtkRequisition req = { 0, 0 }; + gtk_widget_size_request(GTK_WIDGET(menuBar), &req); + h = req.height; +#endif + //fprintf(stderr, "menuBarHeight: %d\n", h); + } + + return h; +} + +int gMainWindow::clientHeight() +{ + if (isMenuBarVisible()) + return height() - menuBarHeight(); + else + return height(); +} + +void gMainWindow::setActiveWindow(gControl *control) +{ + gMainWindow *window = control ? control->window() : NULL; + gMainWindow *old = _active; + + if (window == _active) + return; + + _active = window; + + //fprintf(stderr, "setActiveWindow: %p %s\n", _active, _active ? _active->name() : ""); + + if (old) + old->emit(SIGNAL(old->onDeactivate)); + + if (window) + window->emit(SIGNAL(window->onActivate)); +} + +#ifdef GDK_WINDOWING_X11 +bool gMainWindow::isUtility() const +{ + return _utility; +} + +void gMainWindow::setUtility(bool v) +{ + bool remap = false; + + if (!isTopLevel()) + return; + + // TODO: works only if the window is not mapped! + + _utility = v; +#if GTK_CHECK_VERSION(2, 20, 0) + if (gtk_widget_get_mapped(border)) +#else + if (GTK_WIDGET_MAPPED(border)) +#endif + { + remap = true; + gtk_widget_unmap(border); + } + + gtk_window_set_type_hint(GTK_WINDOW(border), v ? GDK_WINDOW_TYPE_HINT_UTILITY : GDK_WINDOW_TYPE_HINT_NORMAL); + + if (remap) + gtk_widget_map(border); +} +#else +bool gMainWindow::isUtility() +{ + return _utility; +} + +void gMainWindow::setUtility(bool v) +{ + _utility = v; +} +#endif + +void gMainWindow::configure() +{ + int h; + + if (bufW < 1 || bufH < 1) + return; + + h = menuBarHeight(); + + //fprintf(stderr, "configure: %s: %d %d - %d %d\n", name(), isMenuBarVisible(), h, width(), height()); + + if (isMenuBarVisible()) + { + gtk_fixed_move(layout, GTK_WIDGET(menuBar), 0, 0); + if (h > 1) + gtk_widget_set_size_request(GTK_WIDGET(menuBar), width(), h); + gtk_fixed_move(layout, widget, 0, h); + gtk_widget_set_size_request(widget, width(), Max(0, height() - h)); + } + else + { + if (layout) + { + if (menuBar) + gtk_fixed_move(layout, GTK_WIDGET(menuBar), 0, -h); + gtk_fixed_move(layout, widget, 0, 0); + } + gtk_widget_set_size_request(widget, width(), height()); + } +} + +void gMainWindow::setMenuBarVisible(bool v) +{ + _showMenuBar = v; + + if (!menuBar) + return; + + configure(); + performArrange(); +} + +bool gMainWindow::isMenuBarVisible() +{ + //fprintf(stderr, "isMenuBarVisible: %d\n", !!(menuBar && !_hideMenuBar && _showMenuBar)); + return menuBar && !_hideMenuBar && _showMenuBar; //|| (menuBar && GTK_WIDGET_MAPPED(GTK_WIDGET(menuBar))); +} + +void gMainWindow::updateFont() +{ + gContainer::updateFont(); + gMenu::updateFont(this); + emit(SIGNAL(onFontChange)); +} + +void gMainWindow::checkMenuBar() +{ + int i; + gMenu *menu; + + //fprintf(stderr, "gMainWindow::checkMenuBar\n"); + + if (menuBar) + { + _hideMenuBar = true; + for (i = 0;; i++) + { + menu = gMenu::winChildMenu(this, i); + if (!menu) + break; + if (menu->isVisible() && !menu->isSeparator()) + { + _hideMenuBar = false; + break; + } + } + } + + configure(); + performArrange(); +} + +void gMainWindow::embedMenuBar(GtkWidget *border) +{ + if (menuBar) + { + // layout is automatically destroyed ? + layout = GTK_FIXED(gtk_fixed_new()); + + g_object_ref(G_OBJECT(menuBar)); + + if (gtk_widget_get_parent(GTK_WIDGET(menuBar))) + gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(GTK_WIDGET(menuBar))), GTK_WIDGET(menuBar)); + + gtk_fixed_put(layout, GTK_WIDGET(menuBar), 0, 0); + + g_object_unref(G_OBJECT(menuBar)); + + gt_widget_reparent(widget, GTK_WIDGET(layout)); + gtk_container_add(GTK_CONTAINER(border), GTK_WIDGET(layout)); + + gtk_widget_show(GTK_WIDGET(menuBar)); + gtk_widget_show(GTK_WIDGET(layout)); + gtk_widget_show(GTK_WIDGET(widget)); + + gMenu::updateFont(this); + gMenu::updateColor(this); + + checkMenuBar(); + } +} + +/*bool gMainWindow::getScreenPos(int *x, int *y) +{ + return gContainer::getScreenPos(x, y); +}*/ + +double gMainWindow::opacity() +{ + if (isTopLevel()) +#if GTK_CHECK_VERSION(3, 8, 0) + return gtk_widget_get_opacity(border); +#else + return gtk_window_get_opacity(GTK_WINDOW(border)); +#endif + else + return 1.0; +} + +void gMainWindow::setOpacity(double v) +{ + if (isTopLevel()) +#if GTK_CHECK_VERSION(3, 8, 0) + gtk_widget_set_opacity(border, v); +#else + gtk_window_set_opacity(GTK_WINDOW(border), v); +#endif +} + +int gMainWindow::screen() +{ + gMainWindow *tl = topLevel(); + return gdk_screen_get_number(gtk_window_get_screen(GTK_WINDOW(tl->border))); +} + +void gMainWindow::emitResize() +{ + if (bufW == _resize_last_w && bufH == _resize_last_h) + return; + + _resize_last_w = bufW; + _resize_last_h = bufH; + configure(); + performArrange(); + emit(SIGNAL(onResize)); +} + +void gMainWindow::setGeometryHints() +{ + GdkGeometry geometry; + + if (isTopLevel()) + { + if (isResizable()) + { + if (isModal()) + { + geometry.min_width = _min_w; + geometry.min_height = _min_h; + } + else + { + geometry.min_width = 0; + geometry.min_height = 0; + } + + geometry.max_width = 32767; + geometry.max_height = 32767; + } + else + { + geometry.min_width = width(); + geometry.min_height = height(); + geometry.max_width = width(); + geometry.max_height = height(); + } + + gtk_window_set_geometry_hints(GTK_WINDOW(border), NULL, &geometry, (GdkWindowHints)(GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE | GDK_HINT_POS)); + //gdk_window_set_geometry_hints(gtk_widget_get_window(border), &geometry, (GdkWindowHints)(GDK_HINT_MIN_SIZE | GDK_HINT_POS)); + } +} + +void gMainWindow::setBackground(gColor vl) +{ + _bg = vl; + if (!_transparent) + gControl::setBackground(vl); +} + +void gMainWindow::setTransparent(bool vl) +{ + if (!vl) + return; + + _transparent = TRUE; + + if (!isVisible()) + return; + +#ifdef GTK3 + GdkScreen *screen = NULL; + GdkVisual *visual = NULL; + + screen = gtk_widget_get_screen(border); + visual = gdk_screen_get_rgba_visual(screen); + if (visual == NULL) + return; +#else + GdkScreen *screen; + GdkColormap *colormap; + + screen = gtk_widget_get_screen(border); + colormap = gdk_screen_get_rgba_colormap(screen); + if (colormap == NULL) + return; +#endif + + gtk_widget_unrealize(border); + + gtk_widget_set_app_paintable(border, TRUE); + +#ifdef GTK3 + gtk_widget_set_visual(border, visual); +#else + gtk_widget_set_colormap(border, colormap); +#endif + + gtk_widget_realize(border); + + int w = width(); + int h = height(); + + bufW = w - 1; + resize(w, h); + + //gtk_window_present(GTK_WINDOW(border)); +} + +bool gMainWindow::closeAll() +{ + int i; + gMainWindow *win; + + for(i = 0; i < count(); i++) + { + win = get(i); + if (!win) + break; + if (win == gApplication::mainWindow()) + continue; + if (win->close()) + return true; + } + + return false; +} + +void gMainWindow::setNoTakeFocus(bool v) +{ + _no_take_focus = v; + if (isTopLevel()) + gtk_window_set_focus_on_map(GTK_WINDOW(border), !_no_take_focus); +} diff --git a/gb.gtk/src/gmainwindow.h b/gb.gtk/src/gmainwindow.h new file mode 100644 index 00000000..bc1c2be9 --- /dev/null +++ b/gb.gtk/src/gmainwindow.h @@ -0,0 +1,206 @@ +/*************************************************************************** + + gmainwindow.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GMAINWINDOW_H +#define __GMAINWINDOW_H + +#include "gbutton.h" + +class gMainWindow : public gContainer +{ +public: + gMainWindow(int plug = 0); + gMainWindow(gContainer *parent); + ~gMainWindow(); + +//"Properties" + bool hasBorder(); + bool isResizable(); + bool isUtility() const; + gPicture *icon() { return _icon; } + gPicture *picture() { return _picture; } + bool mask() { return _mask; } + int menuCount(); + bool isModal() const; + const char* text(); + bool topOnly(); + bool skipTaskBar(); + bool minimized() const { return _minimized; } + bool maximized() const { return _maximized; } + bool fullscreen() const { return _fullscreen; } + bool getSticky(); + int getStacking(); + bool isPersistent() const { return persistent; } + bool isOpened() const { return opened; } + bool isClosed() const { return !opened; } + bool isHidden() const { return _hidden; } + bool isPopup() const { return _popup; } + bool isTransparent() const { return _transparent; } + bool isNoTakeFocus() const { return _no_take_focus; } + int screen(); + + int controlCount(); + gControl *getControl(char *name); + gControl *getControl(int i); + + void setBorder(bool b); + void setResizable(bool b); + void setUtility(bool v); + void setIcon(gPicture *pic); + void setMask(bool vl); + void setPicture(gPicture *pic); + void setText(const char *txt); + void setTopOnly(bool vl); + void setSkipTaskBar(bool b); + void setMinimized(bool vl); + void setMaximized(bool vl); + void setFullscreen(bool vl); + void setSticky(bool vl); + void setStacking(int vl); + void setPersistent(bool vl); + void setTransparent(bool vl); + void setNoTakeFocus(bool vl); + + virtual void setVisible(bool vl); + virtual void setBackground(gColor vl); + virtual void setRealBackground(gColor vl); + virtual void setRealForeground(gColor vl); + + virtual int clientWidth(); + virtual int clientHeight(); + virtual int clientX(); + virtual int clientY(); + virtual int containerX(); + virtual int containerY(); + + //virtual bool getScreenPos(int *x, int *y); + + bool spontaneous() { return !_not_spontaneous; } + + void setMenuBarVisible(bool v); + bool isMenuBarVisible(); + + double opacity(); + void setOpacity(double v); + +//"Methods" + void center(); + void showActivate(); + void showModal(); + void showPopup(); + void showPopup(int x, int y); + void activate(); + void raise(); + virtual void move(int x, int y); + virtual void resize(int w, int h); + virtual void moveResize(int x, int y, int w, int h); + bool close(); + virtual void reparent(gContainer *newpr, int x, int y); + +//"Signals" + void (*onOpen)(gMainWindow *sender); + void (*onShow)(gMainWindow *sender); + void (*onHide)(gMainWindow *sender); + void (*onMove)(gMainWindow *sender); + void (*onResize)(gMainWindow *sender); + bool (*onClose)(gMainWindow *sender); + void (*onActivate)(gMainWindow *sender); + void (*onDeactivate)(gMainWindow *sender); + void (*onState)(gMainWindow *sender); + void (*onFontChange)(gMainWindow *sender); + +//"Static" + static GList *windows; + static int count() { return g_list_length(windows); } + static gMainWindow *get(int index) { return (gMainWindow *)g_list_nth_data(windows, index); } + static gMainWindow *_active; + static void setActiveWindow(gControl *control); + static gMainWindow *_current; + static bool closeAll(); + +//"Private" + void initialize(); + void drawMask(); + void initWindow(); + void emitOpen(); + void remap(); + bool doClose(); + void afterShow(); + void checkMenuBar(); + int menuBarHeight(); + void configure(); + void embedMenuBar(GtkWidget *border); + void emitResize(); + void setGeometryHints(); + virtual void updateFont(); + void present(); + + GtkWindowGroup *group; + GtkAccelGroup *accel; + GtkMenuBar *menuBar; + GtkFixed *layout; + int stack; + int _type; + gPicture *_icon; + gPicture *_picture; + char *_title; + GtkStyle *_style; + + gControl *focus; + gButton *_default; + gButton *_cancel; + + int _resize_last_w; + int _resize_last_h; + + int _min_w; + int _min_h; + + unsigned _mask : 1; + unsigned top_only : 1; + unsigned _resized : 1; + unsigned persistent : 1; + unsigned sticky : 1; + unsigned opened : 1; + unsigned _closing : 1; + unsigned _not_spontaneous : 1; + unsigned _skip_taskbar : 1; + unsigned _masked : 1; + unsigned _xembed : 1; + unsigned _activate : 1; + unsigned _hidden : 1; + unsigned _hideMenuBar : 1; + unsigned _showMenuBar : 1; + unsigned _popup : 1; + unsigned _maximized : 1; + unsigned _minimized : 1; + unsigned _fullscreen : 1; + unsigned _utility : 1; + unsigned _transparent : 1; + unsigned _no_take_focus : 1; + unsigned _moved : 1; + unsigned _resizable : 1; + unsigned _unmap : 1; +}; + +#endif diff --git a/gb.gtk/src/gmenu.cpp b/gb.gtk/src/gmenu.cpp new file mode 100644 index 00000000..57941f2c --- /dev/null +++ b/gb.gtk/src/gmenu.cpp @@ -0,0 +1,1169 @@ +/*************************************************************************** + + gmenu.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gmainwindow.h" +#include "gapplication.h" +#include "gdesktop.h" +#include "gmenu.h" + +typedef + struct { + int x; + int y; + } + MenuPosition; + +gMenu *gMenu::_current_popup = NULL; +int gMenu::_in_popup = 0; +int gMenu::_popup_count = 0; + +static GList *menus = NULL; + +static gint my_menu_shell_enter_notify(GtkWidget *widget, GdkEventCrossing *event) +{ + GtkWidgetClass *klass = GTK_WIDGET_GET_CLASS(widget); + GtkWidget *menu_item; + gMenu *menu; + + if (event->mode == GDK_CROSSING_GTK_GRAB || + event->mode == GDK_CROSSING_GTK_UNGRAB || + event->mode == GDK_CROSSING_STATE_CHANGED) + goto __PREVIOUS; + + menu_item = gtk_get_event_widget((GdkEvent*) event); + if (!menu_item) + goto __PREVIOUS; + + menu = (gMenu *)g_object_get_data(G_OBJECT(menu_item), "gambas-menu"); + if (menu) + menu->ensureChildMenu(); + +__PREVIOUS: + + return ((gint (*)(GtkWidget *, GdkEventCrossing *))klass->_gtk_reserved6)(widget, event); +} + +static void patch_classes(void) +{ + GtkWidgetClass *klass; + + klass = (GtkWidgetClass *)g_type_class_peek(GTK_TYPE_MENU_SHELL); + if (klass->enter_notify_event != my_menu_shell_enter_notify) + { + //fprintf(stderr, "patch_class: %p\n", klass->enter_notify_event); + klass->_gtk_reserved6 = (void (*)())klass->enter_notify_event; + klass->enter_notify_event = my_menu_shell_enter_notify; + } + + klass = (GtkWidgetClass *)g_type_class_peek(GTK_TYPE_MENU_BAR); + if (klass->enter_notify_event != my_menu_shell_enter_notify) + { + //fprintf(stderr, "patch_class: %p\n", klass->enter_notify_event); + klass->_gtk_reserved6 = (void (*)())klass->enter_notify_event; + klass->enter_notify_event = my_menu_shell_enter_notify; + } +} + + +static void mnu_destroy (GtkWidget *object, gMenu *data) +{ + if (data->stop_signal) + data->stop_signal = false; + else + data->destroy(); +} + +static void mnu_activate(GtkMenuItem *menuitem, gMenu *data) +{ + if (data->child) + return; + + if (data->radio()) + data->setRadio(); + else if (data->toggle()) + data->setChecked(!data->checked()); + + if (data->onClick) + { + //fprintf(stderr, "mnu_activate: %s\n", data->name()); + data->onClick(data); + } +} + +static gboolean cb_map(GtkWidget *menu, gMenu *data) +{ + data->_opened = true; + data->hideSeparators(); + if (data->onShow) (*data->onShow)(data); + return false; +} + +static gboolean cb_unmap(GtkWidget *menu, gMenu *data) +{ + data->_opened = false; + if (data->onHide) (*data->onHide)(data); + return false; +} + +static int get_menu_pos(GtkWidget *menu) +{ + GList *children, *iter; + int pos; + + if (!gtk_widget_get_parent(menu)) + { + //g_debug("get_menu_pos: no parent for menu %p", menu); + return -1; + } + + children = gtk_container_get_children(GTK_CONTAINER(gtk_widget_get_parent(menu))); + iter = g_list_first(children); + + for(pos = 0;; pos++) + { + if (iter->data == (gpointer)menu) + break; + iter = g_list_next(iter); + } + + g_list_free(children); + + return pos; +} + +#ifdef GTK3 +static gboolean cb_check_draw(GtkWidget *wid, cairo_t *cr, gMenu *menu) +{ + static GtkWidget *check_menu_item = NULL; + static GtkWidget *radio_menu_item = NULL; + + int x, y, w, h; + gint indicator_size = 0; + GtkStateFlags state; + GtkWidget *item; + GtkStyleContext *style; + + GtkAllocation a; + gtk_widget_get_allocation(wid, &a); + x = 0; //a.x; + y = 0; //a.y; + w = a.width; + h = a.height; + + if (menu->radio()) + { + if (!radio_menu_item) + radio_menu_item = gtk_radio_menu_item_new(NULL); + item = radio_menu_item; + style = gtk_widget_get_style_context(item); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_RADIO); + } + else + { + if (!check_menu_item) + check_menu_item = gtk_check_menu_item_new(); + item = check_menu_item; + style = gtk_widget_get_style_context(item); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_CHECK); + } + + gtk_widget_style_get(item, "indicator-size", &indicator_size, (char *)NULL); + indicator_size = MAX(16, indicator_size); + + x += (w - indicator_size) / 2; + y += (h - indicator_size) / 2; + w = indicator_size; + h = indicator_size; + + state = gtk_widget_get_state_flags(wid); + + if (menu->checked()) + state = (GtkStateFlags)((int)state | GTK_STATE_FLAG_ACTIVE +#if GTK_CHECK_VERSION(3, 14, 0) + | GTK_STATE_FLAG_CHECKED +#endif + ); + + gtk_widget_set_state_flags(item, state, true); + + style = gtk_widget_get_style_context(wid); + gtk_style_context_save(style); + gtk_style_context_set_state(style, state); + + if (menu->radio()) + { + gtk_style_context_add_class(style, GTK_STYLE_CLASS_RADIO); + //gtk_render_background(style, cr, x, y, w, h); + gtk_render_option(style, cr, x, y, indicator_size, indicator_size); + } + else + { + gtk_style_context_add_class(style, GTK_STYLE_CLASS_CHECK); + //gtk_render_background(style, cr, x, y, w, h); + gtk_render_check(style, cr, x, y, indicator_size, indicator_size); + } + + gtk_style_context_restore(style); + return false; +} +#else +static gboolean cb_check_expose(GtkWidget *wid, GdkEventExpose *e, gMenu *menu) +{ + static GtkWidget *check_menu_item = NULL; + static GtkWidget *radio_menu_item = NULL; + GtkWidget *item; + + int x, y, w, h; + gint indicator_size; + GtkShadowType shadow; + + x = wid->allocation.x; + y = wid->allocation.y; + w = wid->allocation.width; + h = wid->allocation.height; + + if (menu->radio()) + { + if (!radio_menu_item) + radio_menu_item = gtk_radio_menu_item_new(NULL); + item = radio_menu_item; + } + else + { + if (!check_menu_item) + check_menu_item = gtk_check_menu_item_new(); + item = check_menu_item; + } + + gtk_widget_style_get(item, "indicator-size", &indicator_size, (char *)NULL); + indicator_size = MAX(16, indicator_size); + + x += (w - indicator_size) / 2; + y += (h - indicator_size) / 2; + w = indicator_size; + h = indicator_size; + + gtk_widget_set_state(item, (GtkStateType)GTK_WIDGET_STATE(wid)); + + shadow = menu->checked() ? GTK_SHADOW_IN : GTK_SHADOW_OUT; + + if (menu->radio()) + { + gtk_paint_option(wid->style, wid->window, + (GtkStateType)GTK_WIDGET_STATE(wid), shadow, + &e->area, radio_menu_item, "radiobutton", + x, y, w, h); + } + else + { + + gtk_paint_check(wid->style, wid->window, + (GtkStateType)GTK_WIDGET_STATE(wid), shadow, + &e->area, check_menu_item, "check", + x, y, w, h); + } + + return false; +} +#endif + +void gMenu::update() +{ + GtkMenuShell *shell = NULL; + gint pos; + int size; + + if (_no_update) + return; + + //g_debug("%p: START UPDATE (menu = %p child = %p parent = %p)", this, menu); + + if (_style != _oldstyle) + { + if (child) + { + g_object_ref(G_OBJECT(child)); + if (_style == MENU) + gtk_menu_item_set_submenu(menu, NULL); + } + + if (menu) + { + pos = get_menu_pos(GTK_WIDGET(menu)); + //shell = (GtkMenuShell*)GTK_WIDGET(menu)->parent; + if (_style != NOTHING) + stop_signal = true; + gtk_widget_destroy(GTK_WIDGET(menu)); + //g_debug("%p: delete old menu/separator", this); + } + else + { + pos = -1; + //shell = NULL; + } + + if (_style != NOTHING) + { + if (_style == SEPARATOR) + { + menu = (GtkMenuItem *)gtk_separator_menu_item_new(); +#ifdef GTK3 +#else + GtkRequisition req; + gtk_widget_size_request(GTK_WIDGET(menu), &req); + if (req.height > 5) + gtk_widget_set_size_request(GTK_WIDGET(menu), -1, 5); +#endif + //g_debug("%p: create new separator %p", this, menu); + } + else if (_style == MENU) + { + menu = (GtkMenuItem *)gtk_image_menu_item_new(); + //g_debug("%p: create new menu %p", this, menu); + + hbox = gtk_hbox_new(false, gDesktop::scale()); + //set_gdk_bg_color(hbox, 0xFF0000); + gtk_container_add(GTK_CONTAINER(menu), GTK_WIDGET(hbox)); + + label = gtk_label_new_with_mnemonic(""); + //gtk_label_set_justify(GTK_LABEL(lbl),GTK_JUSTIFY_LEFT); + + if (!top_level) + { + image = gtk_image_new(); + g_object_ref(image); + + aclbl = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(aclbl), 0, 0.5); + gtk_size_group_add_widget(((gMenu*)pr)->sizeGroup, aclbl); + + check = gtk_image_new(); + g_object_ref(check); + size = MAX(18, window()->font()->height()); + gtk_widget_set_size_request(check, size, size); + ON_DRAW(check, this, cb_check_expose, cb_check_draw); + //g_signal_connect_after(G_OBJECT(check), "expose-event", G_CALLBACK(cb_check_expose), (gpointer)this); + + //gtk_box_pack_start(GTK_BOX(hbox), image, false, false, 0); + gtk_box_pack_start(GTK_BOX(hbox), label, false, false, 0); + gtk_box_pack_end(GTK_BOX(hbox), aclbl, false, false, 0); + } + else + { + aclbl = NULL; + gtk_box_pack_start(GTK_BOX(hbox), label, false, false, 0); + } + + if (child) + { + gtk_menu_item_set_submenu(menu, GTK_WIDGET(child)); + g_object_unref(G_OBJECT(child)); + } + + + //set_gdk_fg_color(label, get_gdk_fg_color(GTK_WIDGET(shell))); + //set_gdk_bg_color(label, get_gdk_bg_color(GTK_WIDGET(shell))); + } + + gtk_widget_show_all(GTK_WIDGET(menu)); + + if (top_level) + { + gMainWindow *win = (gMainWindow *)pr; + + //set_gdk_fg_color(GTK_WIDGET(menu), win->foreground()); + //set_gdk_bg_color(GTK_WIDGET(menu), win->background()); + + //gtk_menu_shell_append(GTK_MENU_SHELL(win->menuBar), GTK_WIDGET(menu)); + shell = GTK_MENU_SHELL(win->menuBar); + } + else + { + gMenu *parent = (gMenu *)pr; + + if (!parent->child) + { + parent->child = (GtkMenu*)gtk_menu_new(); + + //g_debug("%p: creates a new child menu container in parent %p", this, parent->child); + + g_signal_connect(G_OBJECT(parent->child), "map", G_CALLBACK(cb_map), (gpointer)parent); + g_signal_connect(G_OBJECT(parent->child), "unmap", G_CALLBACK(cb_unmap), (gpointer)parent); + gtk_widget_show_all(GTK_WIDGET(parent->child)); + if (parent->style() == MENU) + gtk_menu_item_set_submenu(parent->menu, GTK_WIDGET(parent->child)); + + parent->setColor(); + + //gtk_menu_shell_append(GTK_MENU_SHELL(parent->child), GTK_WIDGET(menu)); + //g_debug("%p: add to new parent %p", this, parent->child); + } + shell = GTK_MENU_SHELL(parent->child); + } + + if (shell) + { + patch_classes(); + + if (pos < 0) + { + gtk_menu_shell_append(shell, GTK_WIDGET(menu)); + //g_debug("%p: append to parent %p", this, shell); + } + else + { + gtk_menu_shell_insert(shell, GTK_WIDGET(menu), pos); + //g_debug("%p: insert into parent %p", this, shell); + } + } + + g_signal_connect(G_OBJECT(menu), "destroy", G_CALLBACK(mnu_destroy), (gpointer)this); + g_signal_connect(G_OBJECT(menu), "activate", G_CALLBACK(mnu_activate), (gpointer)this); + + g_object_set_data(G_OBJECT(menu), "gambas-menu", this); + } + + _oldstyle = _style; + updateVisible(); + } + + if (_style == MENU) + { + { + char *buf; + gMnemonic_correctText(_text, &buf); + gtk_label_set_text_with_mnemonic(GTK_LABEL(label), buf); + g_free(buf); + } + + if (!top_level) + { + char *buf; + + if (_shortcut) + { + buf = g_strconcat("\t", _shortcut ," ",(void *)NULL); + gtk_label_set_text(GTK_LABEL(aclbl), buf); + g_free(buf); + } + else + gtk_label_set_text(GTK_LABEL(aclbl), "\t"); + + if (_checked || _radio || _toggle) + { + gtk_image_menu_item_set_image((GtkImageMenuItem *)menu, check); + gtk_widget_show(check); + gtk_widget_hide(image); + } + else + { + gtk_image_set_from_pixbuf(GTK_IMAGE(image), _picture ? _picture->getPixbuf() : NULL); + gtk_image_menu_item_set_image((GtkImageMenuItem *)menu, image); + gtk_widget_show(image); + gtk_widget_hide(check); + } + } + + setColor(); + setFont(); + } + + //g_debug("%p: END UPDATE", this); +} + +void gMenu::initialize() +{ + //fprintf(stderr, "gMenu::gMenu: %p (%p)\n", this, pr); + + stop_signal = false; + + onFinish = NULL; + onClick = NULL; + onShow = NULL; + onHide = NULL; + + hFree = NULL; + child = NULL; + image = NULL; + label = NULL; + aclbl = NULL; + check = NULL; + menu = NULL; + top_level=false; + + _text = NULL; + _shortcut = NULL; + _checked = false; + _picture = NULL; + _name = NULL; + _toggle = false; + _radio = false; + + _style = NOTHING; + _oldstyle = NOTHING; + + _no_update = false; + _destroyed = false; + _delete_later = false; + _action = false; + _visible = false; + _opened = false; + + _proxy = NULL; + + sizeGroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + menus = g_list_append (menus,(gpointer)this); +} + + +static gboolean cb_menubar_changed(GtkWidget *widget, gMainWindow *data) +{ + data->configure(); + return false; +} + +gMenu::gMenu(gMainWindow *par, bool hidden) +{ + pr = (gpointer)par; + initialize(); + top_level=true; + + accel = par->accel; + g_object_ref(accel); + + if (!par->menuBar) + { + par->menuBar = (GtkMenuBar*)gtk_menu_bar_new(); + g_signal_connect_after(G_OBJECT(par->menuBar), "map", G_CALLBACK(cb_menubar_changed), (gpointer)par); + g_signal_connect(G_OBJECT(par->menuBar),"unmap", G_CALLBACK(cb_menubar_changed), (gpointer)par); + par->embedMenuBar(par->border); + } + + setText(NULL); + setVisible(!hidden); +} + +gMenu::gMenu(gMenu *par, bool hidden) +{ + pr = (gpointer)par; + + initialize(); + //sizeGroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + if (!par) return; + if (!par->menu) return; + + accel = par->accel; + g_object_ref(accel); + + setText(NULL); + setVisible(!hidden); +} + +gMenu::~gMenu() +{ + GList *item; + gMenu *mn; + + if (_destroyed) + return; + + //fprintf(stderr, "gMenu::~gMenu: %p (%p) '%s'\n", this, pr, _text); + + _destroyed = true; + + setProxy(NULL); + + // Remove references to me + +#define CLEAR_POINTER(_field) if ((_field) == (void *)this) _field = NULL + + item = g_list_first(menus); + while (item) + { + mn = (gMenu*)item->data; + CLEAR_POINTER(mn->pr); + CLEAR_POINTER(mn->_proxy); + item = g_list_next(item); + } + + menus = g_list_remove(menus, (gpointer)this); + + _no_update = true; + setText(NULL); + setShortcut(NULL); + setPicture(NULL); + + //if (_style != NOTHING) + { + if (aclbl && (!top_level) && pr) + gtk_size_group_remove_widget(((gMenu*)pr)->sizeGroup, aclbl); + + if (sizeGroup) + g_object_unref(G_OBJECT(sizeGroup)); + + if (accel) + g_object_unref(accel); + } + + _style = NOTHING; + + if (child) + gtk_widget_destroy(GTK_WIDGET(child)); + + stop_signal = true; + + if (image) + gtk_widget_destroy(GTK_WIDGET(image)); + + if (check) + gtk_widget_destroy(GTK_WIDGET(check)); + + if (menu) + gtk_widget_destroy(GTK_WIDGET(menu)); + + if (_current_popup == this) + _current_popup = NULL; + + if (onFinish) onFinish(this); +} + +bool gMenu::enabled() +{ + return gtk_widget_is_sensitive(GTK_WIDGET(menu)); +} + +void gMenu::setEnabled(bool vl) +{ + gtk_widget_set_sensitive(GTK_WIDGET(menu),vl); +} + + +void gMenu::setText(const char *text) +{ + g_free(_text); + if (text) + _text = g_strdup(text); + else + _text = NULL; + + if (!_text || !*_text) + _style = SEPARATOR; + else + _style = MENU; + + update(); +} + +bool gMenu::isVisible() +{ + if (!menu) return false; + return _visible; +} + +void gMenu::updateVisible() +{ + bool vl = _visible; + + if (top_level && _style != MENU) + vl = false; + + //fprintf(stderr, "gMenu::updateVisible: %s '%s' %d\n", name(), text(), vl); + + gtk_widget_set_visible(GTK_WIDGET(menu), vl); + //g_object_set(G_OBJECT(menu),"visible",vl,(void *)NULL); + + if (top_level && pr) + ((gMainWindow *)pr)->checkMenuBar(); +} + +void gMenu::setVisible(bool vl) +{ + if (!menu) return; + if (vl == _visible) return; + + _visible = vl; + updateVisible(); +} + +void gMenu::setPicture(gPicture *pic) +{ + //fprintf(stderr, "gMenu::setPicture: %p\n", pic); + gPicture::assign(&_picture, pic); + update(); +} + +void gMenu::setChecked(bool vl) +{ + _checked = vl; + update(); +} + +void gMenu::setToggle(bool vl) +{ + _toggle = vl; + update(); +} + +void gMenu::setRadio(bool vl) +{ + _radio = vl; + update(); +} + +int gMenu::childCount() +{ + GList *item; + gMenu *mn; + int ct=0; + + if (!menus) return 0; + + item=g_list_first(menus); + while (item) + { + mn=(gMenu*)item->data; + if (mn->pr == (void*)this && !mn->_delete_later) + ct++; + item=g_list_next(item); + } + + return ct; +} + +gMenu* gMenu::childMenu(int pos) +{ + GList *item; + gMenu *mn; + int ct=0; + + if (!menus) return NULL; + + item=g_list_first(menus); + while (item) + { + mn=(gMenu*)item->data; + if (mn->pr == (void*)this && !mn->_delete_later) + { + if (ct==pos) return mn; + ct++; + } + item=g_list_next(item); + } + + return NULL; +} + +void gMenu::destroy() +{ + if (!_destroyed && !_delete_later) + delete this; +} + +static void position_menu(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, MenuPosition *pos) +{ + *x = pos->x; + *y = pos->y; + *push_in = false; + delete pos; +} + +void gMenu::doPopup(bool move, int x, int y) +{ + gMenu *save_current_popup; + MenuPosition *pos = NULL; + + if (!child) + return; + + if (move) + { + pos = new MenuPosition; + pos->x = x; + pos->y = y; + } + + save_current_popup = _current_popup; + _current_popup = this; + + _in_popup++; + _popup_count++; + + gtk_menu_popup(child, NULL, NULL, move ? (GtkMenuPositionFunc)position_menu : NULL, (gpointer)pos, 0, gApplication::lastEventTime()); + +#if GTK_CHECK_VERSION(2, 20, 0) + while (_current_popup && child && gtk_widget_get_mapped(GTK_WIDGET(child))) +#else + while (_current_popup && child && GTK_WIDGET_MAPPED(child)) +#endif + MAIN_do_iteration(false); + + _current_popup = save_current_popup; + + _in_popup--; + + // flush the event loop so that the main window is reactivated before the click menu event is raised + + while (gtk_events_pending()) + MAIN_do_iteration(false); +} + +void gMenu::popup(int x, int y) +{ + doPopup(true, x, y); +} + +void gMenu::popup() +{ + doPopup(false); +} + +void gMenu::close() +{ + if (!child) + return; + + gtk_menu_popdown(child); +} + +int gMenu::winChildCount(gMainWindow *par) +{ + GList *item; + gMenu *mn; + int ct=0; + + if (!menus) return 0; + + item=g_list_first(menus); + while (item) + { + mn=(gMenu*)item->data; + if (mn->pr == (void*)par) ct++; + item=g_list_next(item); + } + + return ct; +} + +gMenu* gMenu::winChildMenu(gMainWindow *par,int pos) +{ + GList *item; + gMenu *mn; + int ct=0; + + if (!menus) return NULL; + + item=g_list_first(menus); + while (item) + { + mn=(gMenu*)item->data; + if (mn->pr == (void*)par) + { + if (ct==pos) return mn; + ct++; + } + item=g_list_next(item); + } + + return NULL; +} + +gMenu *gMenu::findFromName(gMainWindow *win, const char *name) +{ + int i; + int count; + gMenu *menu; + + for(;;) + { + count = winChildCount(win); + for (i = 0; i < count; i++) + { + menu = winChildMenu(win, i); + if (!strcasecmp(menu->name(), name)) + return menu; + } + + if (!win->parent()) + break; + win = win->parent()->window(); + if (!win) + break; + } + + return NULL; +} + +void gMenu::setShortcut(char *shortcut) +{ + guint key; + GdkModifierType mods; + + if (_shortcut) + { + gt_shortcut_parse(_shortcut, &key, &mods); + if (key) + gtk_widget_remove_accelerator(GTK_WIDGET(menu), accel, key, mods); + g_free(_shortcut); + _shortcut = NULL; + } + + if (shortcut) + { + _shortcut = g_strdup(shortcut); + gt_shortcut_parse(_shortcut, &key, &mods); + if (key) + gtk_widget_add_accelerator(GTK_WIDGET(menu),"activate",accel,key,mods,(GtkAccelFlags)0); + } + + update(); +} + +gMainWindow *gMenu::window() +{ + if (!pr) + return NULL; + + if (top_level) + return (gMainWindow *)pr; + + return ((gMenu *)pr)->window(); +} + +void gMenu::setName(char *name) +{ + if (_name) + { + g_free(_name); + _name = NULL; + } + + if (name) + _name = g_strdup(name); +} + + +void gMenu::hideSeparators() +{ + gMenu *ch; + gMenu *last_ch; + bool is_sep; + bool last_sep; + GList *item; + + if (!child) + return; + + last_sep = true; + last_ch = 0; + + item = g_list_first(menus); + while (item) + { + ch = (gMenu*)item->data; + if (ch->pr == this) + { + is_sep = ch->style() == SEPARATOR; + if (is_sep) + { + if (last_sep) + { + ch->hide(); + } + else + { + ch->show(); + last_sep = true; + last_ch = ch; + } + } + else + { + if (ch->isVisible()) + { + ch->ensureChildMenu(); + last_sep = false; + } + } + } + + item = g_list_next(item); + } + + if (last_sep && last_ch) + last_ch->hide(); +} + +void gMenu::setFont() +{ + gMainWindow *win = window(); +#ifdef GTK3 + if (label) gtk_widget_override_font(GTK_WIDGET(label), win->font()->desc()); + if (aclbl) gtk_widget_override_font(GTK_WIDGET(aclbl), win->font()->desc()); +#else + if (label) gtk_widget_modify_font(GTK_WIDGET(label), win->font()->desc()); + if (aclbl) gtk_widget_modify_font(GTK_WIDGET(aclbl), win->font()->desc()); +#endif +} + +void gMenu::setColor() +{ + gMainWindow *win = window(); + + if (pr == win) + { + if (label) set_gdk_fg_color(GTK_WIDGET(label), win->foreground()); + } + //if (aclbl) set_gdk_fg_color(GTK_WIDGET(aclbl), win->foreground()); +} + +void gMenu::updateColor(gMainWindow *win) +{ + //GList *item; + //gMenu *mn; + + if (!win->menuBar) + return; + + set_gdk_bg_color(GTK_WIDGET(win->menuBar), win->background()); + set_gdk_fg_color(GTK_WIDGET(win->menuBar), win->foreground()); + + /*if (!menus) + return; + + item = g_list_first(menus); + while (item) + { + mn = (gMenu*)item->data; + //if (mn->pr == (void*)win) + mn->setColor(); + item = g_list_next(item); + }*/ +} + +void gMenu::updateFont(gMainWindow *win) +{ + GList *item; + gMenu *mn; + + if (win->menuBar) + { + //fprintf(stderr, "set menu bar font\n"); +#ifdef GTK3 + gtk_widget_override_font(GTK_WIDGET(win->menuBar), win->ownFont() ? win->font()->desc() : NULL); +#else + gtk_widget_modify_font(GTK_WIDGET(win->menuBar), win->ownFont() ? win->font()->desc() : NULL); +#endif + } + + if (!menus) + return; + + item = g_list_first(menus); + while (item) + { + mn = (gMenu*)item->data; + if (mn->pr == (void*)win) + mn->setFont(); + item=g_list_next(item); + } +} + +void gMenu::setRadio() +{ + gMenu *child; + GList *item; + GList *start = NULL; + + if (top_level) + return; + + item = g_list_first(menus); + while (item) + { + child = (gMenu*)item->data; + if (child->parent() == pr && !child->_delete_later) + { + if (child->radio()) + { + if (!start) + start = item; + if (child == this) + break; + } + else + start = NULL; + } + + item = g_list_next(item); + } + + item = start; + while (item) + { + child = (gMenu*)item->data; + if (child->parent() == pr && !child->_delete_later) + { + if (!child->radio()) + break; + + child->setChecked(child == this); + } + + item = g_list_next(item); + } +} + +void gMenu::setProxy(gMenu *menu) +{ + _proxy = menu; +} + +GtkMenu *gMenu::getSubMenu() +{ + if (_proxy) + return _proxy->getSubMenu(); + else + return child; +} + +void gMenu::ensureChildMenu() +{ + GtkWidget *child = GTK_WIDGET(getSubMenu()); + + if (child) + { + // TODO: create parent menu? + if (gtk_menu_item_get_submenu(menu) != child) + { + //fprintf(stderr, "ensureChildMenu: %s\n", name()); + g_object_ref(child); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(gtk_menu_get_attach_widget(GTK_MENU(child))), NULL); + gtk_menu_item_set_submenu(menu, child); + g_object_unref(child); + } + } +} + diff --git a/gb.gtk/src/gmenu.h b/gb.gtk/src/gmenu.h new file mode 100644 index 00000000..40290491 --- /dev/null +++ b/gb.gtk/src/gmenu.h @@ -0,0 +1,150 @@ +/*************************************************************************** + + gmenu.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GMENU_H +#define __GMENU_H + +class gMainWindow; +class gPicture; + +class gMenu +{ +public: + gMenu(gMainWindow *par,bool hidden); + gMenu(gMenu *par,bool hidden); + ~gMenu(); + + void *hFree; + + static int winChildCount(gMainWindow *win); + static gMenu* winChildMenu(gMainWindow *par,int pos); + static void updateFont(gMainWindow *win); + static void updateColor(gMainWindow *win); + static gMenu *findFromName(gMainWindow *win, const char *name); + + static int popupCount() { return _popup_count; } + +//"Properties" + bool checked() const { return _checked; } + bool toggle() const { return _toggle; } + bool radio() const { return _radio; } + bool enabled(); + gMenu* childMenu(int pos); + int childCount(); + char* shortcut() const { return _shortcut; } + char* text() const { return _text; } + bool isVisible(); + gPicture* picture() const { return _picture; } + gMainWindow* window(); + char *name() const { return _name; } + bool topLevel() const { return top_level; } + bool isSeparator() const { return _style == SEPARATOR; } + void *parent() const { return pr; } + bool isClosed() const { return !_opened; } + + void setChecked(bool vl); + void setToggle(bool vl); + void setRadio(bool vl); + void setEnabled(bool vl); + void setShortcut(char *txt); + void setText(const char *vl); + void setVisible(bool vl); + void show() { setVisible(true); } + void hide() { setVisible(false); } + void setPicture(gPicture *pic); + void setName(char *name); + bool action() const { return _action; } + void setAction(bool v) { _action = v; } + void setFont(); + void setColor(); + + void setProxy(gMenu *menu); + gMenu *proxy() const { return _proxy; } + +//"Methods" + void popup(); + void popup(int x, int y); + void close(); + void destroy(); + static bool insidePopup() { return _in_popup > 0; } + static gMenu *currentPopup() { return _current_popup; } + +// "Signals" + void (*onFinish)(gMenu *sender); // Special + void (*onClick)(gMenu *sender); + void (*onShow)(gMenu *sender); + void (*onHide)(gMenu *sender); + +//"Private" + enum gMenuStyle { NOTHING, SEPARATOR, MENU }; + + void *pr; + bool stop_signal; + GtkAccelGroup *accel; + GtkMenu *child; + GtkMenuItem *menu; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *aclbl; + GtkWidget *image; + GtkWidget *check; + GtkSizeGroup *sizeGroup; + gMenu *_proxy; + unsigned _opened : 1; + + void initialize(); + gMenuStyle style() const { return _style; } + void hideSeparators(); + void willBeDeletedLater() { _delete_later = TRUE; } + void setRadio(); + GtkMenu *getSubMenu(); + void ensureChildMenu(); + +private: + + gMenuStyle _style, _oldstyle; + char *_name; + gPicture *_picture; + char *_shortcut; + char *_text; + + unsigned _checked : 1; + unsigned _toggle : 1; + unsigned _radio : 1; + unsigned _no_update : 1; + unsigned _destroyed : 1; + unsigned _delete_later : 1; + unsigned top_level : 1; + unsigned _action : 1; + unsigned _visible : 1; + + static gMenu *_current_popup; + static int _in_popup; + static int _popup_count; + + void doPopup(bool move, int x = 0, int y = 0); + void update(); + void updateVisible(); +}; + +#endif diff --git a/gb.gtk/src/gmessage.cpp b/gb.gtk/src/gmessage.cpp new file mode 100644 index 00000000..8699f9f8 --- /dev/null +++ b/gb.gtk/src/gmessage.cpp @@ -0,0 +1,762 @@ +/*************************************************************************** + + gmessage.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include +#include +#include "widgets.h" +#include "gdialog.h" +#include "gdesktop.h" +#include "gmainwindow.h" +#include "gapplication.h" +#include "gmessage.h" + +static char *MESSAGE_title=NULL; + +#define MESSAGE_ok ((char *)"OK") + +static gColor DIALOG_color=0; +static char *DIALOG_path=NULL; +static char **DIALOG_paths=NULL; +static char *DIALOG_title=NULL; +static gFont *DIALOG_font=NULL; +static bool _dialog_show_hidden = false; + +static gboolean cb_show(GtkWidget *widget, gpointer data) +{ + GdkGeometry geometry; + geometry.min_width = gDesktop::scale() * 32; + geometry.min_height = gDesktop::scale() * 6; + + gtk_window_set_geometry_hints(GTK_WINDOW(widget), widget, &geometry, (GdkWindowHints)(GDK_HINT_MIN_SIZE | GDK_HINT_POS)); + return false; +} + + +static int run_dialog(GtkDialog *window) +{ + gMainWindow *active; + GtkWindowGroup *oldGroup; + int ret; + bool busy; + + active = gDesktop::activeWindow(); + if (active) + gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(active->border)); + + //gApplication::setActiveControl(gApplication::activeControl(), false); + //GB.CheckPost(); + + busy = gApplication::isBusy(); + gApplication::setBusy(false); + + gtk_window_present(GTK_WINDOW(window)); + oldGroup = gApplication::enterGroup(); + gApplication::_loopLevel++; + (*gApplication::onEnterEventLoop)(); + ret = gtk_dialog_run(window); + (*gApplication::onLeaveEventLoop)(); + gApplication::_loopLevel--; + gApplication::exitGroup(oldGroup); + + gApplication::setBusy(busy); + + return ret; +} + + +/****************************************************************************** + +gMessage + +*******************************************************************************/ + +typedef struct + { + char *bt1; + char *bt2; + char *bt3; + } +dlg_btn; + +static dlg_btn bt; + +guint custom_dialog(const gchar *icon,GtkButtonsType btn,char *sg) +{ + GtkWidget *msg, *hrz, *label, *img; + gint resp; + char *buf=NULL; + char *title; + + if (bt.bt1) { gMnemonic_correctText(bt.bt1, &buf); bt.bt1 = buf; } + if (bt.bt2) { gMnemonic_correctText(bt.bt2, &buf); bt.bt2 = buf; } + if (bt.bt3) { gMnemonic_correctText(bt.bt3, &buf); bt.bt3 = buf; } + + title = gMessage::title(); + +#ifdef GTK3 + msg = gtk_dialog_new_with_buttons(title, NULL, + GTK_DIALOG_MODAL, + bt.bt1, 1, bt.bt2, 2, bt.bt3, 3, (char *)NULL); + img = gtk_image_new_from_icon_name(icon,GTK_ICON_SIZE_DIALOG); +#else + msg = gtk_dialog_new_with_buttons(title, NULL, + (GtkDialogFlags)(GTK_DIALOG_MODAL+GTK_DIALOG_NO_SEPARATOR), + bt.bt1, 1, bt.bt2, 2, bt.bt3, 3, (char *)NULL); + img = gtk_image_new_from_stock(icon,GTK_ICON_SIZE_DIALOG); +#endif + + label = gtk_label_new(""); + + if (sg) + buf = gt_html_to_pango_string(sg, -1, true); + + if (buf) + { + gtk_label_set_markup(GTK_LABEL(label),buf); + g_free(buf); + } + + hrz = gtk_hbox_new(FALSE, gDesktop::scale()); + gtk_container_set_border_width(GTK_CONTAINER(hrz), gDesktop::scale() * 2); + + gtk_container_add (GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(msg))),hrz); + + gtk_container_add (GTK_CONTAINER(hrz),img); + gtk_box_set_child_packing(GTK_BOX(hrz), img, false, false, 0, GTK_PACK_START); + + gtk_container_add (GTK_CONTAINER(hrz),label); + //gtk_box_set_child_packing(GTK_BOX(hrz), label, true, false, 0, GTK_PACK_END); + + gtk_widget_show_all(hrz); + + gtk_widget_realize(msg); + + gdk_window_set_type_hint(gtk_widget_get_window(msg), GDK_WINDOW_TYPE_HINT_UTILITY); + gtk_window_set_position(GTK_WINDOW(msg), GTK_WIN_POS_CENTER_ALWAYS); + + g_signal_connect(G_OBJECT(msg), "show", G_CALLBACK(cb_show), (gpointer)NULL); + + resp = run_dialog(GTK_DIALOG(msg)); + gtk_widget_destroy(msg); + + if (resp<0) + { + resp=1; + if (bt.bt2) resp=2; + if (bt.bt3) resp=3; + } + + if (bt.bt1) g_free(bt.bt1); + if (bt.bt2) g_free(bt.bt2); + if (bt.bt3) g_free(bt.bt3); + + return resp; +} + +int gMessage::showDelete(char *msg,char *btn1,char *btn2,char *btn3) +{ + bt.bt1=MESSAGE_ok; + bt.bt2=NULL; + bt.bt3=NULL; + if (btn1) bt.bt1=btn1; + if (btn2) bt.bt2=btn2; + if (btn3) bt.bt3=btn3; + return custom_dialog( +#ifdef GTK3 + "user-trash", +#else + GTK_STOCK_DELETE, +#endif + GTK_BUTTONS_OK,msg); +} + +int gMessage::showError(char *msg,char *btn1,char *btn2,char *btn3) +{ + bt.bt1=MESSAGE_ok; + bt.bt2=NULL; + bt.bt3=NULL; + if (btn1) bt.bt1=btn1; + if (btn2) bt.bt2=btn2; + if (btn3) bt.bt3=btn3; + return custom_dialog( +#ifdef GTK3 + "dialog-error", +#else + GTK_STOCK_DIALOG_ERROR, +#endif + GTK_BUTTONS_OK,msg); +} + +int gMessage::showInfo(char *msg,char *btn1) +{ + bt.bt1=MESSAGE_ok; + bt.bt2=NULL; + bt.bt3=NULL; + if (btn1) bt.bt1=btn1; + return custom_dialog( +#ifdef GTK3 + "dialog-information", +#else + GTK_STOCK_DIALOG_INFO, +#endif + GTK_BUTTONS_OK,msg); +} + +int gMessage::showQuestion(char *msg,char *btn1,char *btn2,char *btn3) +{ + bt.bt1=MESSAGE_ok; + bt.bt2=NULL; + bt.bt3=NULL; + if (btn1) bt.bt1=btn1; + if (btn2) bt.bt2=btn2; + if (btn3) bt.bt3=btn3; + return custom_dialog( +#ifdef GTK3 + "dialog-question", +#else + GTK_STOCK_DIALOG_QUESTION, +#endif + GTK_BUTTONS_OK,msg); +} + +int gMessage::showWarning(char *msg,char *btn1,char *btn2,char *btn3) +{ + bt.bt1=MESSAGE_ok; + bt.bt2=NULL; + bt.bt3=NULL; + if (btn1) bt.bt1=btn1; + if (btn2) bt.bt2=btn2; + if (btn3) bt.bt3=btn3; + return custom_dialog( +#ifdef GTK3 + "dialog-warning", +#else + GTK_STOCK_DIALOG_WARNING, +#endif + GTK_BUTTONS_OK,msg); +} + +char *gMessage::title() +{ + return MESSAGE_title; +} + +void gMessage::setTitle(char *title) +{ + if (MESSAGE_title) + { + g_free(MESSAGE_title); + MESSAGE_title=NULL; + } + + if (title && *title) + MESSAGE_title = g_strdup(title); +} + +void gMessage::exit() +{ + gMessage::setTitle(NULL); +} + +/****************************************************************************** + +gDialog + +*******************************************************************************/ + +GPtrArray *gDialog::_filter = NULL; + +static void free_path(void) +{ + if (DIALOG_path) + { + g_free(DIALOG_path); + DIALOG_path = NULL; + } + + if (DIALOG_paths) + { + int i = 0; + + while (DIALOG_paths[i]) + { + g_free(DIALOG_paths[i]); + i++; + } + g_free(DIALOG_paths); + DIALOG_paths=NULL; + } +} + +static void set_filters(GtkFileChooser* ch) +{ + char **filters; + int nfilters; + int i, p; + GString *name; + char *filter; + char **patterns; + GtkFileFilter *ft; + GSList *lft; + + filters = gDialog::filter(&nfilters); + if (!nfilters) + return; + + nfilters--; + + for (i = 0; i < nfilters; i += 2) + { + filter = filters[i]; + + ft = gtk_file_filter_new(); + + name = g_string_new(filters[i + 1]); + g_string_append_printf(name, " (%s)", filter); + gtk_file_filter_set_name(ft, name->str); + g_string_free(name, true); + + patterns = g_strsplit(filter, ";", 0); + for (p = 0; patterns[p]; p++) + gtk_file_filter_add_pattern(ft, patterns[p]); + + g_strfreev(patterns); + + gtk_file_chooser_add_filter(ch, ft); + } + + lft = gtk_file_chooser_list_filters(ch); + if (lft) + { + gtk_file_chooser_set_filter(ch, (GtkFileFilter *)lft->data); + g_slist_free(lft); + } +} + +static bool run_file_dialog(GtkFileChooserDialog *msg) +{ + GSList *names,*iter; + char *buf; + long b=0; + + set_filters((GtkFileChooser*)msg); + + if (run_dialog(GTK_DIALOG(msg)) != GTK_RESPONSE_OK) + { + gtk_widget_destroy(GTK_WIDGET(msg)); + gDialog::setTitle(NULL); + return true; + } + + free_path(); + + names=gtk_file_chooser_get_filenames((GtkFileChooser*)msg); + + if (names) + { + buf=(char*)names->data; + if (buf) { + DIALOG_path=(char*)g_malloc( sizeof(char)*(strlen(buf)+1) ); + strcpy(DIALOG_path,buf); + } + + b=0; + DIALOG_paths=(char**)g_malloc(sizeof(char*)*(g_slist_length(names)+1) ); + DIALOG_paths[g_slist_length(names)]=NULL; + iter=names; + while(iter) + { + buf=(char*)iter->data; + DIALOG_paths[b]=(char*)g_malloc( sizeof(char)*(strlen(buf)+1) ); + strcpy(DIALOG_paths[b++],buf); + iter=iter->next; + } + + g_slist_free(names); + } + + gtk_widget_destroy(GTK_WIDGET(msg)); + gDialog::setTitle(NULL); + return false; +} + +void gDialog::exit() +{ + free_path(); + + gDialog::setFilter(NULL, 0); + gFont::assign(&DIALOG_font); +} + +gFont* gDialog::font() +{ + return DIALOG_font; +} + +void gDialog::setFont(gFont *ft) +{ + gFont::set(&DIALOG_font, ft->copy()); +} + +gColor gDialog::color() +{ + return DIALOG_color; +} + +void gDialog::setColor(gColor col) +{ + DIALOG_color=col; +} + +char* gDialog::title() +{ + return DIALOG_title; +} + +void gDialog::setTitle(char *vl) +{ + if (DIALOG_title) + { + g_free(DIALOG_title); + DIALOG_title=NULL; + } + + if (vl && *vl) + DIALOG_title = g_strdup(vl); +} + +char *gDialog::path() +{ + return DIALOG_path; +} + +char **gDialog::paths() +{ + return DIALOG_paths; +} + +void gDialog::setPath(char *vl) +{ + if (DIALOG_path) + { + g_free(DIALOG_path); + DIALOG_path=NULL; + } + + if (!vl) return; + + DIALOG_path=(char*)g_malloc( sizeof(char)*(strlen(vl)+1) ); + strcpy(DIALOG_path,vl); +} + +bool gDialog::showHidden() +{ + return _dialog_show_hidden; +} + +void gDialog::setShowHidden(bool v) +{ + _dialog_show_hidden = v; +} + +char** gDialog::filter(int *nfilter) +{ + if (!_filter) + { + *nfilter = 0; + return NULL; + } + + *nfilter = _filter->len; + return (char **)(_filter->pdata); +} + +void gDialog::setFilter(char** filter, int nfilter) +{ + int i; + + if (_filter) + { + for (i = 0; i < (int)_filter->len; i++) + g_free(g_ptr_array_index(_filter, i)); + + g_ptr_array_free(_filter, true); + _filter = NULL; + } + + if (!filter) + return; + + _filter = g_ptr_array_new(); + for (i = 0; i < nfilter; i++) + g_ptr_array_add(_filter, (gpointer)g_strdup(filter[i])); +} + +bool gDialog::openFile(bool multi) +{ + GtkFileChooserDialog *msg; + + msg = (GtkFileChooserDialog*)gtk_file_chooser_dialog_new( + DIALOG_title ? DIALOG_title : GB.Translate("Open file"), + NULL, + GTK_FILE_CHOOSER_ACTION_OPEN, +#ifdef GTK3 + GB.Translate("Cancel"), GTK_RESPONSE_CANCEL, GB.Translate("Open"), GTK_RESPONSE_OK, +#else + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, +#endif + (void *)NULL); + + gtk_file_chooser_set_local_only((GtkFileChooser*)msg, true); + gtk_file_chooser_set_select_multiple((GtkFileChooser*)msg, multi); + gtk_widget_show(GTK_WIDGET(msg)); + gtk_file_chooser_unselect_all((GtkFileChooser*)msg); + + if (DIALOG_path) + { + if (g_file_test(DIALOG_path, G_FILE_TEST_IS_DIR)) + gtk_file_chooser_set_current_folder((GtkFileChooser*)msg, DIALOG_path); + else + gtk_file_chooser_select_filename((GtkFileChooser*)msg, DIALOG_path); + } + + gtk_file_chooser_set_show_hidden((GtkFileChooser*)msg, _dialog_show_hidden); + + return run_file_dialog(msg); +} + +bool gDialog::saveFile() +{ + GtkFileChooserDialog *msg; + + msg = (GtkFileChooserDialog*)gtk_file_chooser_dialog_new( + DIALOG_title ? DIALOG_title : GB.Translate("Save file"), + NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, +#ifdef GTK3 + GB.Translate("Cancel"), GTK_RESPONSE_CANCEL, GB.Translate("Save"), GTK_RESPONSE_OK, +#else + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_OK, +#endif + (void *)NULL); + + gtk_file_chooser_set_do_overwrite_confirmation((GtkFileChooser*)msg, true); + gtk_file_chooser_set_local_only((GtkFileChooser*)msg, true); + gtk_file_chooser_set_select_multiple((GtkFileChooser*)msg, false); + gtk_widget_show(GTK_WIDGET(msg)); + gtk_file_chooser_unselect_all((GtkFileChooser*)msg); + + if (DIALOG_path) + { + if (*DIALOG_path && DIALOG_path[strlen(DIALOG_path) - 1] == '/' && g_file_test(DIALOG_path, G_FILE_TEST_IS_DIR)) + gtk_file_chooser_set_current_folder((GtkFileChooser*)msg, DIALOG_path); + else + gtk_file_chooser_select_filename((GtkFileChooser*)msg, DIALOG_path); + } + + gtk_file_chooser_set_show_hidden((GtkFileChooser*)msg, _dialog_show_hidden); + + return run_file_dialog(msg); +} + +bool gDialog::selectFolder() +{ + GtkFileChooserDialog *msg; + + msg = (GtkFileChooserDialog*)gtk_file_chooser_dialog_new( + DIALOG_title ? DIALOG_title : GB.Translate("Select directory"), + NULL, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, +#ifdef GTK3 + GB.Translate("Cancel"), GTK_RESPONSE_CANCEL, GB.Translate("Open"), GTK_RESPONSE_OK, +#else + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, +#endif + (void *)NULL); + + gtk_file_chooser_set_local_only((GtkFileChooser*)msg, true); + gtk_file_chooser_set_select_multiple((GtkFileChooser*)msg, false); + gtk_widget_show(GTK_WIDGET(msg)); + gtk_file_chooser_unselect_all((GtkFileChooser*)msg); + if (DIALOG_path) + { + if (g_file_test(DIALOG_path, G_FILE_TEST_IS_DIR)) + gtk_file_chooser_set_current_folder((GtkFileChooser*)msg, DIALOG_path); + else + gtk_file_chooser_select_filename((GtkFileChooser*)msg, DIALOG_path); + } + + gtk_file_chooser_set_show_hidden((GtkFileChooser*)msg, _dialog_show_hidden); + + return run_file_dialog(msg); +} + +#ifdef GTK3 + +GType type1, type2; + +bool gDialog::selectFont() +{ + GtkFontChooserDialog *dialog; + PangoFontDescription *desc; + gFont *font; + + type1 = pango_font_family_get_type(); + type2 = pango_font_face_get_type(); + + dialog = (GtkFontChooserDialog *)gtk_font_chooser_dialog_new(DIALOG_title, NULL); + + if (DIALOG_font) + gtk_font_chooser_set_font_desc(GTK_FONT_CHOOSER(dialog), pango_context_get_font_description(DIALOG_font->ct)); + + if (run_dialog(GTK_DIALOG(dialog)) != GTK_RESPONSE_OK) + { + gtk_widget_destroy(GTK_WIDGET(dialog)); + gDialog::setTitle(NULL); + return true; + } + + desc = gtk_font_chooser_get_font_desc(GTK_FONT_CHOOSER(dialog)); + + gtk_widget_destroy(GTK_WIDGET(dialog)); + gDialog::setTitle(NULL); + + font = new gFont(desc); + setFont(font); + gFont::assign(&font); + + pango_font_description_free(desc); + + //printf("-> %s/%s/%s/%d\n", DIALOG_font->name(), DIALOG_font->bold() ? "BOLD" : "", DIALOG_font->italic() ? "ITALIC" : "", DIALOG_font->size()); + + return false; +} +#else +bool gDialog::selectFont() +{ + GtkFontSelectionDialog *msg; + PangoFontDescription *desc; + char *buf; + gFont *font; + + if (DIALOG_title) + msg=(GtkFontSelectionDialog*)gtk_font_selection_dialog_new (DIALOG_title); + else + msg=(GtkFontSelectionDialog*)gtk_font_selection_dialog_new ("Select Font"); + + + if (DIALOG_font) + { + desc=pango_context_get_font_description(DIALOG_font->ct); + buf=pango_font_description_to_string(desc); + gtk_font_selection_dialog_set_font_name(msg,buf); + g_free(buf); + } + + if (run_dialog(GTK_DIALOG(msg)) != GTK_RESPONSE_OK) + { + gtk_widget_destroy(GTK_WIDGET(msg)); + gDialog::setTitle(NULL); + return true; + } + + buf = gtk_font_selection_dialog_get_font_name(msg); + desc = pango_font_description_from_string(buf); + g_free(buf); + + gtk_widget_destroy(GTK_WIDGET(msg)); + gDialog::setTitle(NULL); + + font = new gFont(desc); + setFont(font); + gFont::assign(&font); + + pango_font_description_free(desc); + + //printf("-> %s/%s/%s/%d\n", DIALOG_font->name(), DIALOG_font->bold() ? "BOLD" : "", DIALOG_font->italic() ? "ITALIC" : "", DIALOG_font->size()); + + return false; +} +#endif + +#ifdef GTK3 +bool gDialog::selectColor() +{ + GtkColorChooserDialog *dialog; + GdkRGBA color; + + gt_color_to_frgba(DIALOG_color, &color.red, &color.green, &color.blue, &color.alpha); + + dialog = (GtkColorChooserDialog *)gtk_color_chooser_dialog_new(DIALOG_title, NULL); + + gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color); + + gtk_window_present(GTK_WINDOW(dialog)); + + if (run_dialog(GTK_DIALOG(dialog)) != GTK_RESPONSE_OK) + { + gtk_widget_destroy(GTK_WIDGET(dialog)); + gDialog::setTitle(NULL); + return true; + } + + gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color); + DIALOG_color = gt_frgba_to_color(color.red, color.green, color.blue, color.alpha); + + gtk_widget_destroy(GTK_WIDGET(dialog)); + gDialog::setTitle(NULL); + return false; +} +#else +bool gDialog::selectColor() +{ + GtkColorSelectionDialog *msg; + GdkColor gcol; + + fill_gdk_color(&gcol, DIALOG_color); + + if (DIALOG_title) + msg=(GtkColorSelectionDialog*)gtk_color_selection_dialog_new (DIALOG_title); + else + msg=(GtkColorSelectionDialog*)gtk_color_selection_dialog_new(GB.Translate("Select Color")); + + gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(msg)), &gcol); + + gtk_window_present(GTK_WINDOW(msg)); + if (run_dialog(GTK_DIALOG(msg)) != GTK_RESPONSE_OK) + { + gtk_widget_destroy(GTK_WIDGET(msg)); + gDialog::setTitle(NULL); + return true; + } + + gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(msg)), &gcol); + + DIALOG_color = get_gdk_color(&gcol); + + gtk_widget_destroy(GTK_WIDGET(msg)); + gDialog::setTitle(NULL); + return false; +} +#endif + diff --git a/gb.gtk/src/gmessage.h b/gb.gtk/src/gmessage.h new file mode 100644 index 00000000..6408523a --- /dev/null +++ b/gb.gtk/src/gmessage.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + gmessage.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GMESSAGE_H +#define __GMESSAGE_H + +class gMessage +{ +public: + static int showDelete(char *msg,char *btn1,char *btn2,char *btn3); + static int showError(char *msg,char *btn1,char *btn2,char *btn3); + static int showInfo(char *msg,char *btn1); + static int showQuestion(char *msg,char *btn1,char *btn2,char *btn3); + static int showWarning(char *msg,char *btn1,char *btn2,char *btn3); + + static void setTitle(char *title); + static char *title(); + + static void exit(); +}; + +#endif diff --git a/gb.gtk/src/gmouse.cpp b/gb.gtk/src/gmouse.cpp new file mode 100644 index 00000000..5bf70d9c --- /dev/null +++ b/gb.gtk/src/gmouse.cpp @@ -0,0 +1,338 @@ +/*************************************************************************** + + gmouse.cpp + + (c) 2004-2006 - Daniel Campos Fernández + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GMOUSE_CPP + +#include "widgets.h" +#include "gmouse.h" + +int gMouse::_isValid = 0; +int gMouse::_x; +int gMouse::_y; +int gMouse::_screen_x; +int gMouse::_screen_y; +int gMouse::_button; +int gMouse::_state; +int gMouse::_delta; +int gMouse::_orientation; +int gMouse::_start_x; +int gMouse::_start_y; +int gMouse::_dx = 0; +int gMouse::_dy = 0; +GdkEvent *gMouse::_event = 0; + +#ifdef GTK3 +static GdkDevice *get_pointer() +{ + return gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default())); +} +#endif + +void gMouse::move(int x, int y) +{ + GdkDisplay* dpy = gdk_display_get_default(); +#ifdef GTK3 + gdk_device_warp(get_pointer(), gdk_display_get_default_screen(dpy), x, y); +#else + gdk_display_warp_pointer(dpy, gdk_display_get_default_screen(dpy), x, y); +#endif +} + +int gMouse::button() +{ + return _isValid ? _button : 0; +} + +int gMouse::state() +{ + return _isValid ? _state : 0; +} + +bool gMouse::left() +{ + return _isValid ? (_state & GDK_BUTTON1_MASK || _button == 1) : false; +} + +bool gMouse::right() +{ + return _isValid ? (_state & GDK_BUTTON3_MASK || _button == 3) : false; +} + +bool gMouse::middle() +{ + return _isValid ? (_state & GDK_BUTTON2_MASK || _button == 2) : false; +} + +bool gMouse::shift() +{ + return _isValid ? (_state & GDK_SHIFT_MASK) : false; +} + +bool gMouse::control() +{ + return _isValid ? (_state & GDK_CONTROL_MASK) : false; +} + +bool gMouse::alt() +{ + return _isValid ? (_state & GDK_MOD1_MASK) : false; +} + +bool gMouse::meta() +{ + return _isValid ? (_state & GDK_MOD2_MASK) : false; +} + +bool gMouse::normal() +{ + return _isValid ? (_state & 0xFF) : false; +} + +int gMouse::x() +{ + return _isValid ? _x + _dx : -1; +} + +int gMouse::y() +{ + return _isValid ? _y + _dy : -1; +} + +void gMouse::getScreenPos(int *x, int *y) +{ + if (_isValid) + { + *x = _screen_x; + *y = _screen_y; + } + else + { +#ifdef GTK3 + gdk_device_get_position(get_pointer(), NULL, x, y); +#else + gdk_display_get_pointer(gdk_display_get_default(), NULL, x, y, NULL); +#endif + } +} + +int gMouse::screenX() +{ + gint x; + +#ifdef GTK3 + gdk_device_get_position(get_pointer(), NULL, &x, NULL); +#else + gdk_display_get_pointer(gdk_display_get_default(), NULL, &x, NULL, NULL); +#endif + + return x; +} + +int gMouse::screenY() +{ + gint y; + +#ifdef GTK3 + gdk_device_get_position(get_pointer(), NULL, NULL, &y); +#else + gdk_display_get_pointer(gdk_display_get_default(), NULL, NULL, &y, NULL); +#endif + + return y; +} + +int gMouse::delta() +{ + return _isValid ? _delta : -1; +} + +int gMouse::orientation() +{ + return _isValid ? _orientation : -1; +} + +//"Private" + +void gMouse::setWheel(int dt,int orn) +{ + _delta = dt; + _orientation = orn; +} + +void gMouse::setStart(int sx, int sy) +{ + _start_x = sx; + _start_y = sy; +} + +void gMouse::setMouse(int x, int y, int sx, int sy, int button, int state) +{ + _delta = 0; + _orientation = 0; + + _x = x; + _y = y; + _state = state; + _button = button; + _screen_x = sx; + _screen_y = sy; + + /*switch(button) + { + case 1: _button = 1; break; + case 2: _button = 4; break; + case 3: _button = 2; break; + default: + _button = 0; + if (_state & GDK_BUTTON1_MASK) _button += 1; + if (_state & GDK_BUTTON2_MASK) _button += 4; + if (_state & GDK_BUTTON3_MASK) _button += 2; + }*/ +} + +static GdkDevice *get_event_device(GdkEvent *event) +{ + switch(event->type) + { + case GDK_BUTTON_PRESS: case GDK_2BUTTON_PRESS: case GDK_3BUTTON_PRESS: case GDK_BUTTON_RELEASE: + return ((GdkEventButton *)event)->device; + + case GDK_SCROLL: + return ((GdkEventScroll *)event)->device; + + case GDK_MOTION_NOTIFY: + return ((GdkEventMotion *)event)->device; + + case GDK_PROXIMITY_IN: case GDK_PROXIMITY_OUT: + return ((GdkEventProximity *)event)->device; + + default: + return NULL; + } +} + +void gMouse::setEvent(GdkEvent *event) +{ + _event = gdk_event_copy(event); + //fprintf(stderr, "device = %p\n", get_event_device(event)); +} + +double gMouse::getAxis(GdkAxisUse axis) +{ + double value; + + if (gdk_event_get_axis(_event, axis, &value)) + return value; + else + return 0.0; +} + + +int gMouse::getType() +{ + GdkDevice *device = get_event_device(_event); + + if (!device) + return POINTER_MOUSE; + + switch(gdk_device_get_source(device)) + { + case GDK_SOURCE_PEN: return POINTER_PEN; + case GDK_SOURCE_ERASER: return POINTER_ERASER; + case GDK_SOURCE_CURSOR: return POINTER_CURSOR; + default: return POINTER_MOUSE; + } +} + +void gMouse::initDevices() +{ +#ifndef GTK3 + static bool done = false; + + GList *devices; + GdkDevice *device; + + if (done) + return; + + //fprintf(stderr, "initDevices\n"); + + devices = gdk_devices_list(); + + while (devices) + { + device = (GdkDevice *)devices->data; + + if (gdk_device_get_source(device) != GDK_SOURCE_MOUSE) + { + //fprintf(stderr, "enable device '%s'\n", gdk_device_get_name(device)); + gdk_device_set_mode(device, GDK_MODE_SCREEN); + } + + devices = devices->next; + } + + done = true; +#endif +} + +double gMouse::getPointerX() +{ + return ((GdkEventMotion *)_event)->x + _dx; +} + +double gMouse::getPointerY() +{ + return ((GdkEventMotion *)_event)->y + _dy; +} + +double gMouse::getPointerScreenX() +{ + return getAxis(GDK_AXIS_X); +} + +double gMouse::getPointerScreenY() +{ + return getAxis(GDK_AXIS_Y); +} + +void gMouse::invalidate() +{ + _isValid--; + + if (_isValid == 0) + { + if (_event) + { + gdk_event_free(_event); + _event = 0; + } + } +} + +void gMouse::translate(int dx, int dy) +{ + _dx = dx; + _dy = dy; +} diff --git a/gb.gtk/src/gmouse.h b/gb.gtk/src/gmouse.h new file mode 100644 index 00000000..080db9be --- /dev/null +++ b/gb.gtk/src/gmouse.h @@ -0,0 +1,92 @@ +/*************************************************************************** + + gmouse.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GMOUSE_H +#define __GMOUSE_H + +class gMouse +{ +public: + +//"Properties" + static int button(); + static int state(); + static bool left(); + static bool right(); + static bool middle(); + static bool shift(); + static bool control(); + static bool alt(); + static bool meta(); + static bool normal(); + static int x(); + static int y(); + static int screenX(); + static int screenY(); + static void getScreenPos(int *x, int *y); + static int delta(); + static int orientation(); + static bool isValid() { return _isValid; } + static int startX() { return _start_x + _dx; } + static int startY() { return _start_y + _dy; } + + static double getAxis(GdkAxisUse axis); + static int getType(); + static double getPointerX(); + static double getPointerY(); + static double getPointerScreenX(); + static double getPointerScreenY(); + + static void initDevices(); + +//"Methods" + static void move(int x, int y); + static void translate(int dx, int dy); + static void resetTranslate() { translate(0, 0); } + +//"Private" + static void setWheel(int dt, int orn); + static void setStart(int sx, int sy); + static void setMouse(int x, int y, int sx, int sy, int button, int state); + static void setEvent(GdkEvent *event); + static void validate() { _isValid++; } + static void invalidate(); + +private: + static int _isValid; + static int _x; + static int _y; + static int _screen_x; + static int _screen_y; + static int _button; + static int _state; + static int _delta; + static int _orientation; + static int _start_x; + static int _start_y; + static int _dx; + static int _dy; + static GdkEvent *_event; +}; + +#endif diff --git a/gb.gtk/src/gmoviebox.cpp b/gb.gtk/src/gmoviebox.cpp new file mode 100644 index 00000000..c549240c --- /dev/null +++ b/gb.gtk/src/gmoviebox.cpp @@ -0,0 +1,163 @@ +/*************************************************************************** + + gmovie.cpp + + (c) 2004-2006 Daniel Campos Fernández + (c) 2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include +#include "widgets.h" +#include "gmoviebox.h" + +/**************************************************************************************** + +gMovieBox + +*****************************************************************************************/ + +gMovieBox::gMovieBox(gContainer *parent) : gControl(parent) +{ + g_typ=Type_gMovieBox; + + timeout=0; + pl=false; + animation=NULL; + + border = gtk_alignment_new(0,0,1,1); + widget = gtk_image_new(); + realize(true); + + setAlignment(ALIGN_TOP_LEFT); +} + +gMovieBox::~gMovieBox() +{ + if (playing()) + setPlaying(false); + if (animation) + g_object_unref(G_OBJECT(animation)); +} + + +/*int gMovieBox::getBorder() +{ + return Frame_getBorder(GTK_FRAME(widget->parent)); +}*/ + +bool gMovieBox::playing() +{ + return pl; +} + +/*void gMovieBox::setBorder(int vl) +{ + Frame_setBorder(GTK_FRAME(widget->parent),vl); +}*/ + +gboolean gMovieBox_move(gMovieBox *data) +{ + GTimeVal tim; + GdkPixbuf *buf; + + + g_get_current_time(&tim); + if (!gdk_pixbuf_animation_iter_advance(data->iter,&tim)) return true; + + buf=gdk_pixbuf_animation_iter_get_pixbuf(data->iter); + gtk_image_set_from_pixbuf(GTK_IMAGE(data->widget),buf); + + return true; +} + +void gMovieBox::setPlaying(bool vl) +{ + GTimeVal tim; + GdkPixbuf *buf; + int interval; + + if (vl) + { + if (!pl) + { + if (!animation) return; + g_get_current_time(&tim); + iter=gdk_pixbuf_animation_get_iter(animation,&tim); + buf=gdk_pixbuf_animation_iter_get_pixbuf(iter); + gtk_image_set_from_pixbuf(GTK_IMAGE(widget),buf); + interval=gdk_pixbuf_animation_iter_get_delay_time(iter); + if (interval>0) { + timeout=g_timeout_add(interval,(GSourceFunc)gMovieBox_move,this); + pl=true; + } + } + return; + } + + if (pl) + { + g_source_remove(timeout); + pl=false; + } +} + +bool gMovieBox::loadMovie(char *buf, int len) +{ + GdkPixbufLoader* loader; + bool bplay; + + bplay=playing(); + setPlaying(false); + + loader=gdk_pixbuf_loader_new(); + if (!gdk_pixbuf_loader_write(loader,(guchar*)buf,(gsize)len,NULL)){ + g_object_unref(G_OBJECT(loader)); + setPlaying(bplay); + return false; + } + + gdk_pixbuf_loader_close(loader,NULL); + + if (animation) g_object_unref(G_OBJECT(animation)); + animation=gdk_pixbuf_loader_get_animation(loader); + g_object_ref(G_OBJECT(animation)); + g_object_unref(G_OBJECT(loader)); + setPlaying(bplay); + return true; +} + +int gMovieBox::alignment() +{ + gfloat x, y; + + gtk_misc_get_alignment(GTK_MISC(widget), &x, &y); + return gt_to_alignment(x, y); +} + +void gMovieBox::setAlignment(int al) +{ + gtk_misc_set_alignment(GTK_MISC(widget), gt_from_alignment(al, false), gt_from_alignment(al, true)); +} + +gColor gMovieBox::getFrameColor() +{ + return realForeground(); +} + + diff --git a/gb.gtk/src/gmoviebox.h b/gb.gtk/src/gmoviebox.h new file mode 100644 index 00000000..ecddcb46 --- /dev/null +++ b/gb.gtk/src/gmoviebox.h @@ -0,0 +1,53 @@ +/*************************************************************************** + + gmoviebox.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GMOVIEBOX_H +#define __GMOVIEBOX_H + +class gMovieBox : public gControl +{ +public: + gMovieBox(gContainer *parent); + ~gMovieBox(); + +//"Properties" + int getBorder() { return getFrameBorder(); } + bool playing(); + int alignment(); + + void setBorder(int vl) { setFrameBorder(vl); } + void setPlaying(bool vl); + void setAlignment(int vl); + +//"Methods" + bool loadMovie(char *buf, int len); + +//"Private" + virtual gColor getFrameColor(); + bool pl; + guint timeout; + GdkPixbufAnimation *animation; + GdkPixbufAnimationIter *iter; +}; + +#endif diff --git a/gb.gtk/src/gpicture.cpp b/gb.gtk/src/gpicture.cpp new file mode 100644 index 00000000..2b54678e --- /dev/null +++ b/gb.gtk/src/gpicture.cpp @@ -0,0 +1,1230 @@ +/*************************************************************************** + + gpicture.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include +#include + +#include "gpicture.h" + + +/***************************************************************** + +gPicture + +******************************************************************/ + +#define LOAD_INC 65536L + +static bool pixbufFromMemory(GdkPixbuf **ppixbuf, char *addr, unsigned int len, bool *trans) +{ + GdkPixbufLoader *loader; + GdkPixbuf *pixbuf; + GError *error = NULL; + gsize size; + + *ppixbuf = 0; + + loader = gdk_pixbuf_loader_new(); + + while (len > 0) + { + size = len > LOAD_INC ? LOAD_INC : len; + if (!gdk_pixbuf_loader_write(loader,(guchar*)addr,size, &error)) + { + //g_debug("ERROR: %s", error->message); + goto __ERROR; + } + addr += size; + len -= size; + } + + if (!gdk_pixbuf_loader_close(loader, &error)) + { + //g_debug("ERROR: %s", error->message); + goto __ERROR; + } + + pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); + g_object_ref(pixbuf); + + if (gdk_pixbuf_get_n_channels(pixbuf) == 3) + { + // Rowstride breaks gb.image (it is rounded up so that a line is always a four bytes multiple). + GdkPixbuf *aimg; + aimg = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0); + g_object_unref(pixbuf); + pixbuf = aimg; + *trans = false; + } + else + *trans = true; + + g_object_unref(G_OBJECT(loader)); + + *ppixbuf = pixbuf; + return true; + +__ERROR: + g_object_unref(G_OBJECT(loader)); + return false; +} + + +void gPicture::initialize() +{ +#ifndef GTK3 + pixmap = NULL; + mask = NULL; +#endif + pixbuf = NULL; + surface = NULL; + _transparent = false; + _type = VOID; + _width = 0; + _height = 0; +} + +gPicture::gPicture() : gShare() +{ + //fprintf(stderr, "gPicture(): %p\n", this); + initialize(); +} + +#ifdef GTK3 +static cairo_surface_t *create_surface(int w, int h) +{ + return cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); +} +#else +static GdkPixmap *create_pixmap(int w, int h) +{ + GdkScreen *scr; + gint depth; + GdkPixmap *pixmap; + + scr = gdk_screen_get_default(); + depth = (gdk_screen_get_system_visual(scr))->depth; + + pixmap = gdk_pixmap_new(NULL, w, h, depth); + gdk_drawable_set_colormap(GDK_DRAWABLE(pixmap), gdk_colormap_get_system()); + return pixmap; +} +#endif + +#ifndef GTK3 +void gPicture::createMask(bool opaque) +{ + GdkGC *gc; + GdkGCValues gc_values; + + if (mask || !_transparent) + return; + + mask = gdk_pixmap_new(NULL, _width, _height, 1); + + gc_values.foreground.pixel = opaque ? 1 : 0; + gc = gdk_gc_new_with_values(mask, &gc_values, GDK_GC_FOREGROUND); + + gdk_gc_set_fill(gc, GDK_SOLID); + gdk_draw_rectangle(mask, gc, TRUE, 0, 0, _width, _height); + + g_object_unref(gc); +} +#endif + +gPicture::gPicture(gPictureType type, int w, int h, bool trans) : gShare() +{ + //fprintf(stderr, "gPicture(): %p: %d %d %d %d\n", this, type, w, h, trans); + initialize(); + + _transparent = trans; + + if (!type) + return; + + if (w <= 0 || h <= 0) return; + + _type = type; + _width = w; + _height = h; + +#ifdef GTK3 + if (_type == SURFACE) + { + surface = create_surface(w, h); + } +#else + if (_type == PIXMAP) + { + pixmap = create_pixmap(w, h); + createMask(false); + } + else +#endif + if (_type == PIXBUF) + { + pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, w, h); + } +} + + +// The gPicture takes the GdkPixbuf object. Do not unreference it after. + +gPicture::gPicture(GdkPixbuf *image, bool trans) : gShare() +{ + //fprintf(stderr, "gPicture(image): %p: %p %d\n", this, image, trans); + initialize(); + if (!image) + return; + + _type = PIXBUF; + _width = gdk_pixbuf_get_width(image); + _height = gdk_pixbuf_get_height(image); + _transparent = trans; + pixbuf = image; + + if (gdk_pixbuf_get_n_channels(pixbuf) == 3) + { + GdkPixbuf *aimg; + aimg = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0); + g_object_unref(G_OBJECT(pixbuf)); + //g_object_ref(G_OBJECT(aimg)); + pixbuf = aimg; + _transparent = false; + } + +} + +#ifdef GTK3 + +gPicture::gPicture(cairo_surface_t *surf) : gShare() +{ + initialize(); + if (!surf) + return; + _type = SURFACE; + surface = surf; + _width = cairo_image_surface_get_width(surf); + _height = cairo_image_surface_get_height(surf); +} + +#else + +// The gPicture takes the GdkPixmap object. Do not unreference it after. + +gPicture::gPicture(GdkPixmap *pix) : gShare() +{ + initialize(); + if (!pix) + return; + + _type = PIXMAP; + gdk_drawable_get_size((GdkDrawable *)pix, &_width, &_height); + pixmap = pix; +} +#endif + +gPicture::~gPicture() +{ + //fprintf(stderr, "~gPicture: %p\n", this); + clear(); +} + +void gPicture::invalidate() +{ +#ifndef GTK3 + if (pixmap && _type != PIXMAP) + { + g_object_unref(G_OBJECT(pixmap)); + pixmap = NULL; + if (mask) + { + g_object_unref(mask); + mask = NULL; + } + } +#endif + + if (pixbuf && _type != PIXBUF) + { + g_object_unref(G_OBJECT(pixbuf)); + pixbuf = NULL; + } + + if (surface && _type != SURFACE) + { + cairo_surface_destroy(surface); + surface = NULL; + } +} + +GdkPixbuf *gPicture::getPixbuf() +{ + if (_type == VOID) + return NULL; + + if (pixbuf) + return pixbuf; + +#ifndef GTK3 + if (_type == PIXMAP) + { + pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, width(), height()); + gdk_pixbuf_get_from_drawable(pixbuf, pixmap, NULL, 0, 0, 0, 0, width(), height()); + + if (mask) + { + uchar *s, *d; + int i; + GdkPixbuf *alpha; + + alpha = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, width(), height()); + gdk_pixbuf_get_from_drawable(alpha, mask, NULL, 0, 0, 0, 0, width(), height()); + s = gdk_pixbuf_get_pixels(alpha); + d = gdk_pixbuf_get_pixels(pixbuf) + 3; + //fprintf(stderr, "mask -> pixbuf\n\n"); + for (i = 0; i < (_width * _height); i++) + { + //fprintf(stderr, "%08X%c", *((uint *)s), (1 + (i % _width)) == _width ? '\n' : ' '); + d[0] = s[0]; + d += 4; + s += 4; + } + g_object_unref(alpha); + } + } + else +#endif + if (_type == SURFACE) + { +#ifdef GTK3 + pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, _width, _height); +#else + fprintf(stderr, "gb.gtk: warning: cairo surface to pixbuf conversion not implemented.\n"); + return NULL; +#endif + } + + _type = PIXBUF; + return pixbuf; +} + +#ifdef GTK3 +cairo_surface_t *gPicture::getSurface() +{ + if (_type == VOID) + return NULL; + + if (_type != SURFACE) + { + getPixbuf(); + surface = gt_cairo_create_surface_from_pixbuf(pixbuf); + } + + _type = SURFACE; + return surface; +} +#else +cairo_surface_t *gPicture::getSurface() +{ + if (_type == VOID) + return NULL; + + if (surface) + return surface; + + getPixbuf(); + surface = gt_cairo_create_surface_from_pixbuf(pixbuf); + + return surface; +} +#endif + +#ifndef GTK3 +GdkPixmap *gPicture::getPixmap() +{ + if (_type == VOID) + return NULL; + + if (_type != PIXMAP) + { + if (_type != PIXBUF) + getPixbuf(); + + if (pixmap) + g_object_unref(G_OBJECT(pixmap)); + if (mask) + g_object_unref(G_OBJECT(mask)); + + gt_pixbuf_render_pixmap_and_mask(pixbuf, &pixmap, &mask, 128); + } + + _type = PIXMAP; + return pixmap; +} + +GdkBitmap *gPicture::getMask() +{ + getPixmap(); + return mask; +} +#endif + + +gPicture *gPicture::fromMemory(char *addr, unsigned int len) +{ + GdkPixbuf *pixbuf; + bool trans; + + if (!pixbufFromMemory(&pixbuf, addr, len, &trans)) + return 0; + else + { + gPicture *pic = new gPicture(pixbuf); + return pic; + } +} + +gPicture *gPicture::fromData(const char *data, int width, int height) +{ + GdkPixbuf *pixbuf; + + if (width <= 0 || height <= 0) + return new gPicture(); + else + { + pixbuf = gdk_pixbuf_new_from_data((const guchar *)data, GDK_COLORSPACE_RGB, TRUE, 8, width, height, width * sizeof(int), NULL, NULL); + return new gPicture(pixbuf); + } +} + +int gPicture::depth() +{ + int depth = 0; + +#ifndef GTK3 + if (pixmap) + depth = gdk_drawable_get_depth(GDK_DRAWABLE(pixmap)); + else +#endif + if (pixbuf || surface) + depth = 32; + + return depth; +} + +void gPicture::setTransparent(bool vl) +{ + if (vl == _transparent) + return; + + _transparent = vl; + +#ifndef GTK3 + if (_type == PIXMAP) + { + if (_transparent) + createMask(true); + else + { + if (mask) + { + g_object_unref(G_OBJECT(mask)); + mask = 0; + } + } + } +#endif +} + +void gPicture::fill(gColor col) +{ +#ifndef GTK3 + if (_type == PIXMAP) + { + gt_pixmap_fill(pixmap, col, NULL); + } + else +#endif + if (_type == PIXBUF) + { + int r, g, b, a; + union + { + char c[4]; + uint value; + } + color; + + gt_color_to_rgba(col, &r, &g, &b, &a); + + color.c[0] = a ^ 0xFF; + color.c[1] = b; + color.c[2] = g; + color.c[3] = r; + + gdk_pixbuf_fill(pixbuf, color.value); + } +#ifdef GTK3 + else if (_type == SURFACE) + { + cairo_t *cr = cairo_create(surface); + gt_cairo_set_source_color(cr, col); + cairo_paint(cr); + cairo_destroy(cr); + } +#endif + + invalidate(); +} + + +// returns -> 0, OK / -1, Bad format / -2 invalid path + +int gPicture::save(const char *path, int quality) +{ + bool ok=false; + int b; + char *type; + const char *buf=NULL; + GSList *formats = gdk_pixbuf_get_formats(); + GSList *iter=formats; + GdkPixbuf *image = getPixbuf(); + char arg[16]; + + for (b=strlen(path)-1;b>=0;b--) + if (path[b]=='.') { buf=path+b+1; break; } + + if (!buf) return -1; + + while (iter && (!ok) ) + { + if (gdk_pixbuf_format_is_writable ((GdkPixbufFormat*)iter->data)) + { + type=gdk_pixbuf_format_get_name((GdkPixbufFormat*)iter->data); + if (!strcasecmp(type,buf)) + { + ok=true; + break; + } + else + g_free(type); + } + iter=iter->next; + } + + if (!ok) + { + g_slist_free(formats); + if (!strcasecmp("jpg", buf)) + type = (char *)"jpeg"; + else + return -1; + } + + if (quality >= 0) + { + sprintf(arg, "%d", quality); + b = gdk_pixbuf_save(image, path, type, NULL, "quality", arg, (void *)NULL); + } + else + b = gdk_pixbuf_save(image, path, type, NULL, (void *)NULL); + + + if (ok) { + g_free(type); + g_slist_free(formats); + } + + if (!b) return -2; + return 0; +} + + +/*********************************************************************** +The following function tries to load an icon from predefined or "stock" +items. It accepts the format: StockSize/IconName, where StockSize can be: + +"Menu", "SmallToolBar","LargeToolBar","Button","Dnd","Dialog" + +And IconName can be: + +"Add","Apply","Bold","Cancel", +"CDRom","Clear","Close","ColorPicker", +"Convert","Copy","Cut","Delete", +"DialogAuthentication""DialogError","DialogInfo","DialogQuestion", +"DialogWarning","Dnd","DndMultiple", "Execute", +"Find","FindAndReplace","Floppy","GotoBottom", +"GotoFirst", "GotoLast","GotoTop","GoBack", +"GoDown","GoForward","GoUp","HardDisk" +"Help","Home","Indent","Index", +"Italic","JumpTo","JustifyCenter", "JustifyFill", +"JustifyLeft","JustifyRight","MissingImage","Network", +"New","No","Ok","Open", +"Paste","Preferences","Print","PrintPreview", +"Properties","Quit","Redo","Refresh", +"Remove","RevertToSaved","Save","SaveAs", +"SelectColor","SelectFont","SortAscending","SortDescending", +"SpellCheck","Stop","StrikeThrough","Undelete", +"Underline","Undo","Unindent","Yes", +"Zoom100","ZoomFit","ZoomIn","ZoomOut" +*************************************************************************/ + + + +/*********************************************************************** +The following function tries to load an icon from predefined system +paths +***********************************************************************/ +gPicture* gPicture::fromNamedIcon(const char *name, int len) +{ + GtkIconTheme* theme; + GdkPixbuf *buf; + gPicture *pic = NULL; + int r_type=32; + char *c_name, *r_name; + + if (len < 0) len = strlen(name); + + c_name = g_strndup(name, len); + r_name = strchr(c_name, '/'); + + if (!r_name) + r_name = c_name; + else + { + r_name[0] = 0; r_name++; + if (!strcasecmp(c_name,"menu")) r_type=8; + else if (!strcasecmp(c_name,"smalltoolbar")) r_type=16; + else if (!strcasecmp(c_name,"largetoolbar")) r_type=32; + else if (!strcasecmp(c_name,"button")) r_type=16; + else if (!strcasecmp(c_name,"dnd")) r_type=32; + else if (!strcasecmp(c_name,"dialog")) r_type=48; + else { r_name--; r_name[0]='/'; g_free(c_name); return NULL; } + } + + + theme=gtk_icon_theme_get_default(); + buf=gtk_icon_theme_load_icon(theme,r_name,r_type,GTK_ICON_LOOKUP_USE_BUILTIN,NULL); + g_free(c_name); + if (!buf) return NULL; + + pic = gPicture::fromPixbuf(buf); + g_object_unref(buf); + + return pic; +} + +void gPicture::clear() +{ + //fprintf(stderr, "gPicture::clear: %p (%d %d) pixmap = %p pixbuf = %p\n", this, _width, _height, pixmap, pixbuf); + + _width = 0; + _height = 0; + _type = VOID; + +#ifndef GTK3 + if (pixmap) + g_object_unref(G_OBJECT(pixmap)); + if (mask) + g_object_unref(G_OBJECT(mask)); + + pixmap = NULL; + mask = NULL; +#endif + + if (pixbuf) + g_object_unref(G_OBJECT(pixbuf)); + if (surface) + cairo_surface_destroy(surface); + + pixbuf = NULL; + surface = NULL; +} + +void gPicture::resize(int w, int h) +{ + if (_width <= 0 || _height <= 0) + { + clear(); + return; + } + +#ifndef GTK3 + if (_type == PIXMAP) + { + GdkPixmap *buf; + GdkBitmap *save; + GdkGC *gc; + + buf = create_pixmap(w, h); + + gc=gdk_gc_new(buf); + gdk_draw_drawable(buf, gc, pixmap, 0, 0, 0, 0, w, h); + g_object_unref(gc); + + g_object_unref(G_OBJECT(pixmap)); + pixmap = buf; + + if (_transparent) + { + save = mask; + + mask = gdk_pixmap_new(NULL, w, h, 1); + + gc=gdk_gc_new(mask); + gdk_draw_drawable(mask, gc, save, 0, 0, 0, 0, w, h); + g_object_unref(gc); + + g_object_unref(save); + } + } + else +#endif + if (_type == PIXBUF) + { + GdkPixbuf *buf; + + if (w > width() || h > height()) + { + buf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, w, h); + if (w > width()) w = width(); + if (h > height()) h = height(); + gdk_pixbuf_copy_area(pixbuf, 0, 0, w, h, buf, 0, 0); + } + else + { + buf = gdk_pixbuf_new_subpixbuf(pixbuf, 0, 0, w, h); + } + + g_object_unref(G_OBJECT(pixbuf)); + pixbuf = buf; + } +#ifdef GTK3 + else if (_type == SURFACE) + { + cairo_surface_t *buf = create_surface(w, h); + cairo_t *cr = cairo_create(buf); + cairo_set_source_surface(cr, surface, 0, 0); + cairo_paint(cr); + cairo_destroy(cr); + } +#endif + + _width = w; + _height = h; + + invalidate(); +} + + +gPicture *gPicture::copy(int x, int y, int w, int h) +{ + gPicture *ret = NULL; + + if (_type == VOID || w <= 0 || h <= 0) + return new gPicture(); + +#ifndef GTK3 + if (_type == PIXMAP) + { + GdkGC *gc; + + ret = new gPicture(_type, w, h, _transparent); + + gc=gdk_gc_new(ret->pixmap); + gdk_draw_drawable(ret->pixmap, gc, pixmap, x, y, 0, 0, w, h); + g_object_unref(gc); + + if (ret->mask) + { + gc=gdk_gc_new(ret->mask); + gdk_draw_drawable(ret->mask, gc, mask, x, y, 0, 0, w, h); + g_object_unref(gc); + } + } + else +#endif + if (_type == PIXBUF) + { + GdkPixbuf *buf; + if (x == 0 && y == 0 && w == width() && h == height()) + buf = gdk_pixbuf_copy(pixbuf); + else + { + buf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, w, h); + gdk_pixbuf_copy_area(pixbuf, x, y, w, h, buf, 0, 0); + } + + ret = new gPicture(buf, _transparent); + } +#ifdef GTK3 + else if (_type == SURFACE) + { + cairo_surface_t *buf = create_surface(w, h); + cairo_t *cr = cairo_create(buf); + cairo_set_source_surface(cr, surface, x, y); + cairo_rectangle(cr, 0, 0, w, h); + cairo_fill(cr); + cairo_destroy(cr); + + ret = new gPicture(buf); + } +#endif + + return ret; +} + +gPicture *gPicture::copy() +{ + return copy(0, 0, width(), height()); +} + +void gPicture::putPixel(int x, int y, gColor col) +{ + guchar *p; + unsigned int nchannels; + unsigned int rowstride; + GdkPixbuf *image; + + if ( (x<0) || (x>width()) ) return; + if ( (y<0) || (y>height()) ) return; + + image = getPixbuf(); + + nchannels=gdk_pixbuf_get_n_channels(image); + rowstride=gdk_pixbuf_get_rowstride(image); + + p = gdk_pixbuf_get_pixels(image) + (x * nchannels) + (y * rowstride); + + /*if (nchannels>0) p[0]=( (col>>16) & 0xFF); + if (nchannels>1) p[1]=( (col>>8) & 0xFF ); + if (nchannels>2) p[2]=( col & 0xFF ); + //if (nchannels>3) p[3]=(col>>24);*/ + p[0]=((col>>16) & 0xFF); + p[1]=((col>>8) & 0xFF); + p[2]=(col & 0xFF); + if (nchannels>3) p[3]=255 - (col >> 24); + + invalidate(); +} + +gColor gPicture::getPixel(int x, int y) +{ + guchar *p; + unsigned int nchannels; + unsigned int rowstride; + gColor ret=0; + GdkPixbuf *image; + + if ( (x<0) || (x>width()) ) return 0; + if ( (y<0) || (y>height()) ) return 0; + + image = getPixbuf(); + + nchannels=gdk_pixbuf_get_n_channels(image); + rowstride=gdk_pixbuf_get_rowstride(image); + + p = gdk_pixbuf_get_pixels(image) + (x * nchannels) + (y * rowstride); + + if (nchannels>3) ret += (((gColor)255-p[3]) << 24); + if (nchannels>0) ret += (((gColor)p[0]) << 16); + if (nchannels>1) ret += (((gColor)p[1]) << 8); + if (nchannels>2) ret += ((gColor)p[2]); + + + return ret; +} + +unsigned char *gPicture::data() +{ + GdkPixbuf *pixbuf = getPixbuf(); + if (!pixbuf) + return NULL; + else + return gdk_pixbuf_get_pixels(pixbuf); +} + +// void gPicture::replace(gColor src, gColor dst, bool noteq) +// { +// if (_type == VOID) +// return; +// +// gt_pixbuf_replace_color(getPixbuf(), src, dst, noteq); +// invalidate(); +// } + + +gPicture* gPicture::flip(bool mirror) +{ + gPicture *ret; + guint32 *src, *dst; + int w, h; + register guint32 *s, *d; + register int x, y; + int rowstride; + + getPixbuf(); + ret = copy(); + + if (!isVoid()) + { + src = (guint32 *)data(); + dst = (guint32 *)ret->data(); + w = width(); + h = height(); + + rowstride = gdk_pixbuf_get_rowstride(getPixbuf()) / sizeof(guint32); + + if (!mirror) + { + for (y = 0; y < h; y++) + { + s = src + y * rowstride; + d = dst + y * rowstride + w; + for (x = 0; x < w; x++) + *--d = *s++; + } + } + else + { + s = src; + d = dst + h * rowstride; + for (y = 0; y < h; y++) + { + d -= rowstride; + memcpy(d, s, w * sizeof(guint32)); + s += rowstride; + } + } + } + + return ret; +} + +/* + This algorithm is inspired from the QT one +*/ + +static void rotate_image(double mat11, double mat12, double mat21, double mat22, double matdx, double matdy, + uchar *dptr, int dbpl, int dWidth, int dHeight, + uchar *sptr, int sbpl, int sWidth, int sHeight + ) +{ + int m11 = int(mat11 * 65536.0 + 1.0); + int m12 = int(mat12 * 65536.0 + 1.0); + int m21 = int(mat21 * 65536.0 + 1.0); + int m22 = int(mat22 * 65536.0 + 1.0); + + uint trigx; + uint trigy; + uint maxws = sWidth << 16; + uint maxhs = sHeight << 16; + + int m21ydx = int(matdx * 65536.0 + 1.0); + int m22ydy = int(matdy * 65536.0 + 1.0); + + for (int y = 0; y < dHeight; y++ ) + { + trigx = m21ydx; + trigy = m22ydy; + uchar *maxp = dptr + dbpl; + while (dptr < maxp) + { + if (trigx < maxws && trigy < maxhs) + *((uint*)dptr) = *((uint *)(sptr + sbpl * (trigy >> 16) + ((trigx >> 16) << 2))); + trigx += m11; + trigy += m12; + dptr += 4; + } + m21ydx += m21; + m22ydy += m22; + } +} + +gPicture* gPicture::rotate(double angle) +{ + double cosa = cos(-angle); + double sina = sin(-angle); + + if (angle == 0.0 || (cosa == 1.0 && sina == 0.0) || (width() <= 1 && height() <= 1)) + return copy(); + + double dx, dy, cx, cy; + int nw, nh; + double minx, miny, maxx, maxy; + int i, px[3], py[3]; + + nw = 0; + nh = 0; + minx = miny = maxx = maxy = 0; + + px[0] = (int)(width() * cosa + height() * (-sina) + 0.5); + py[0] = (int)(width() * sina + height() * cosa + 0.5); + + px[1] = (int)(width() * cosa + 0.5); + py[1] = (int)(width() * sina + 0.5); + + px[2] = (int)(height() * (-sina) + 0.5); + py[2] = (int)(height() * cosa + 0.5); + + for (i = 0; i < 3; i++) + { + if (px[i] > maxx) maxx = px[i]; + if (px[i] < minx) minx = px[i]; + if (py[i] > maxy) maxy = py[i]; + if (py[i] < miny) miny = py[i]; + } + + nw = (int)(maxx - minx + 0.5); + nh = (int)(maxy - miny + 0.5); + + cx = nw / 2.0 * cosa + nh / 2.0 * sina; + cy = nw / 2.0 * -sina + nh / 2.0 * cosa; + + dx = width() / 2.0 - cx; + dy = height() / 2.0 - cy; + + GdkPixbuf *src = getPixbuf(); + gPicture *npic = new gPicture(PIXBUF, nw, nh, isTransparent()); + npic->fill(0); + GdkPixbuf *dst = npic->getPixbuf(); + + rotate_image(cosa, -sina, sina, cosa, dx, dy, + gdk_pixbuf_get_pixels(dst), nw * 4, nw, nh, + gdk_pixbuf_get_pixels(src), width() * 4, width(), height()); + + return npic; +} + +gPicture *gPicture::stretch(int w, int h, bool smooth) +{ + gPicture *ret; + GdkPixbuf *image; + int ws, hs; + + if (w <= 0 && h <= 0) + return new gPicture(); + + if (w < 0) + w = width() * h / height(); + else if (h < 0) + h = height() * w / width(); + + if (w <= 0 || h <= 0) + return new gPicture(); + + ret = copy(); + if (ret->isVoid()) + return ret; + + image = ret->getPixbuf(); + + if (smooth) + { + ws = w; + hs = h; + if (ws < (width() / 4)) + ws = w * 4; + if (hs < (height() / 4)) + hs = h * 4; + if (ws != w || hs != h) + { + ret->pixbuf = gdk_pixbuf_scale_simple(image, ws, hs, GDK_INTERP_NEAREST); + g_object_unref(G_OBJECT(image)); + image = ret->pixbuf; + } + ret->pixbuf = gdk_pixbuf_scale_simple(image, w, h, GDK_INTERP_BILINEAR); + } + else + ret->pixbuf = gdk_pixbuf_scale_simple(image, w, h, GDK_INTERP_NEAREST); + + g_object_unref(G_OBJECT(image)); + + ret->_width = w; + ret->_height = h; + + ret->invalidate(); + + return ret; +} + + +void gPicture::draw(gPicture *pic, int x, int y, int w, int h, int sx, int sy, int sw, int sh) +{ + if (isVoid() || pic->isVoid()) + return; + + GT_NORMALIZE(x, y, w, h, sx, sy, sw, sh, pic->width(), pic->height()); + + if (x >= width() || y >= height()) + return; + + +#ifndef GTK3 + if (_type == PIXMAP) + { + GdkPixmap *dst = getPixmap(); + + if (pic->type() == gPicture::PIXMAP && !pic->isTransparent() && w == sw && h == sh) + { + GdkGC *gc = gdk_gc_new(GDK_DRAWABLE(dst)); + GdkPixmap *src = pic->getPixmap(); + gdk_draw_drawable(GDK_DRAWABLE(dst), gc, src, sx, sy, x, y, sw, sh); + g_object_unref(G_OBJECT(gc)); + } + else + { + bool del = false; + + if (w != sw || h != sh) + { + gPicture *pic2; + pic2 = pic->copy(sx, sy, sw, sh); + pic = pic2->stretch(w, h, true); + delete pic2; + del = true; + sx = 0; sy = 0; sw = w; sh = h; + } + + gdk_draw_pixbuf(GDK_DRAWABLE(dst), NULL, pic->getPixbuf(), sx, sy, x, y, sw, sh, GDK_RGB_DITHER_MAX, 0, 0); + + if (del) + delete pic; + } + } + else +#endif + if (_type == PIXBUF) + { + GdkPixbuf *dst = getPixbuf(); + GdkPixbuf *src = pic->getPixbuf(); + double scale_x, scale_y, offset_x, offset_y; + + scale_x = (double)w / sw; + scale_y = (double)h / sh; + offset_x = x - sx * scale_x; + offset_y = y - sy * scale_y; + + if (x < 0) + x = 0; + + if (y < 0) + y = 0; + + if ((x + w) > width()) + w = width() - x; + + if ((y + h) > height()) + h = height() - y; + + gdk_pixbuf_composite(src, dst, x, y, w, h, offset_x, offset_y, scale_x, scale_y, GDK_INTERP_BILINEAR, 255); + } + + invalidate(); +} + + +/***************************************************************** + +gPictureCache + +******************************************************************/ + +GHashTable *gPictureCache::cache = 0; + +static void destroy_key(char *key) +{ + g_free(key); +} + +static void destroy_value(gPicture *pic) +{ + //fprintf(stderr, "gPictureCache: destroy_value %p\n", pixmap); + pic->unref(); +} + +void gPictureCache::init() +{ + cache = g_hash_table_new_full((GHashFunc)g_str_hash, (GEqualFunc)g_str_equal, (GDestroyNotify)destroy_key, (GDestroyNotify)destroy_value); +} + +void gPictureCache::exit() +{ + g_hash_table_destroy(cache); +} + +void gPictureCache::put(const char *key, gPicture *pic) +{ + if (!key || !*key) return; + + //fprintf(stderr, "gPictureCache: put %p\n", pixmap); + pic->ref(); + g_hash_table_replace(cache, (gpointer)g_strdup(key), (gpointer)pic); +} + +gPicture *gPictureCache::get(const char *key) +{ + if (!key || !*key) return 0; + + return (gPicture *)g_hash_table_lookup(cache, (gconstpointer)key); +} + +void gPictureCache::flush() +{ + exit(); + init(); +} + +void gPicture::makeGray() +{ + if (_type == VOID) + return; + + gt_pixbuf_make_gray(getPixbuf()); + invalidate(); +} + +void gPicture::makeTransparent(gColor color) +{ + if (_type == VOID) + return; + + gt_pixbuf_make_alpha(getPixbuf(), color); + invalidate(); +} + +GdkPixbuf *gPicture::getIconPixbuf() +{ + GdkPixbuf *icon = getPixbuf(); + + if ((_width & 7) || (_height & 7)) + { + icon = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, (_width + 7) & ~7, (_height + 7) & ~7); + gdk_pixbuf_fill(icon, 0); + gdk_pixbuf_copy_area(getPixbuf(), 0, 0, _width, _height, icon, 0, 0); + } + + return icon; +} + diff --git a/gb.gtk/src/gpicture.h b/gb.gtk/src/gpicture.h new file mode 100644 index 00000000..5e782a2b --- /dev/null +++ b/gb.gtk/src/gpicture.h @@ -0,0 +1,134 @@ +/*************************************************************************** + + gpicture.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GPICTURE_H +#define __GPICTURE_H + +#include "widgets.h" +#include "gshare.h" +#include "gcolor.h" +#include "gtag.h" + +class alphaCache; + +class gPicture : public gShare +{ +public: + + enum gPictureType + { + VOID, + PIXBUF, +#ifndef GTK3 + PIXMAP, +#endif + SURFACE + }; + + gPicture(); + gPicture(gPictureType type, int w, int h, bool trans); + ~gPicture(); + gPicture *copy(); + + bool isVoid() { return _type == VOID; } + + static void assign(gPicture **dst, gPicture *src = 0) { gShare::assign((gShare **)dst, src); } + + gPictureType type() const { return _type; } + int width() const { return _width; } + int height() const { return _height; } + int depth(); + bool isTransparent() const { return _transparent; } + unsigned char *data(); + + void setTransparent(bool vl); + + void clear(); + void resize(int width,int height); + int save(const char *path, int quality = -1); + void fill(gColor col); + gPicture *copy(int x, int y, int w, int h); + + gPicture *flip(bool mirror = false); + gPicture *mirror() { return flip(true); } + gPicture *rotate(double ang); + gPicture *stretch(int w, int h, bool smooth); + + gColor getPixel(int x, int y); + void putPixel(int x, int y, gColor col); + void draw(gPicture *src, int x, int y, int w = -1, int h = -1, int sx = 0, int sy = 0, int sw = -1, int sh = -1); + void makeGray(); + void makeTransparent(gColor color); + + static gPicture *fromNamedIcon(const char *name, int len = -1); + static gPicture *fromMemory(char *addr, unsigned int len); + static gPicture *fromData(const char *data, int width, int height); + +//"Private" +#ifndef GTK3 + GdkPixmap *pixmap; + GdkBitmap *mask; +#endif + GdkPixbuf *pixbuf; + cairo_surface_t *surface; + + gPictureType _type; + bool _transparent; + int _width; + int _height; + + gPicture(GdkPixbuf *image, bool trans = true); +#ifdef GTK3 + gPicture(cairo_surface_t *surf); +#else + gPicture(GdkPixmap *pix); +#endif + void initialize(); + GdkPixbuf *getPixbuf(); +#ifndef GTK3 + GdkPixmap *getPixmap(); + GdkBitmap *getMask(); +#endif + GdkPixbuf *getIconPixbuf(); + cairo_surface_t *getSurface(); + + static gPicture* fromPixbuf(GdkPixbuf *buf) { return new gPicture(buf); } + +// "Private" + void invalidate(); + void createMask(bool white); +}; + +class gPictureCache +{ +public: + static void put(const char *key, gPicture *img); + static gPicture *get(const char *key); + static void flush(); + static void init(); + static void exit(); +private: + static GHashTable *cache; +}; + +#endif diff --git a/gb.gtk/src/gplugin.h b/gb.gtk/src/gplugin.h new file mode 100644 index 00000000..284fb377 --- /dev/null +++ b/gb.gtk/src/gplugin.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + gplugin.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GPLUGIN_H +#define __GPLUGIN_H + +class gPlugin : public gControl +{ +public: + gPlugin(gContainer *parent); + void plug(int id); + void discard(); +//"Properties" + int client(); + + int getBorder() { return getFrameBorder(); } + void setBorder(int vl) { setFrameBorder(vl); } + +//"Signals" + void (*onPlug)(gControl *sender); + void (*onUnplug)(gControl *sender); + void (*onError)(gControl *sender); +}; + +#endif diff --git a/gb.gtk/src/gprinter.cpp b/gb.gtk/src/gprinter.cpp new file mode 100644 index 00000000..985ba5aa --- /dev/null +++ b/gb.gtk/src/gprinter.cpp @@ -0,0 +1,735 @@ +/*************************************************************************** + + gprinter.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GPRINTER_CPP + +#include + +#include "widgets.h" +#include "gdesktop.h" +#include "gmainwindow.h" +#include "gapplication.h" +#include "gb.form.print.h" +#include "gprinter.h" + +//#define DEBUG_ME 1 + +static void cb_begin_cancel(GtkPrintOperation *operation, GtkPrintContext *context, gPrinter *printer) +{ + if (printer->_preview) + { + if (printer->onBegin) + (*printer->onBegin)(printer, context); + return; + } + + #if DEBUG_ME + fprintf(stderr, "cb_begin_cancel: %d\n", cairo_surface_get_type(cairo_get_target(gtk_print_context_get_cairo_context(context)))); + #endif + printer->storeSettings(); + printer->cancel(); + printer->_configure_ok = true; +} + +static void cb_begin(GtkPrintOperation *operation, GtkPrintContext *context, gPrinter *printer) +{ + #if DEBUG_ME + fprintf(stderr, "cb_begin\n"); + #endif + printer->defineSettings(); + //gtk_print_settings_to_file(gtk_print_operation_get_print_settings(operation), "/home/benoit/settings-begin-before.txt", NULL); + if (printer->onBegin) + (*printer->onBegin)(printer, context); + //gtk_print_settings_to_file(gtk_print_operation_get_print_settings(operation), "/home/benoit/settings-begin-after.txt", NULL); +} + +static void cb_end(GtkPrintOperation *operation, GtkPrintContext *context, gPrinter *printer) +{ + #if DEBUG_ME + fprintf(stderr, "cb_end: %d\n", printer->_preview); + #endif + if (printer->_preview && printer->onEnd) + (*printer->onEnd)(printer); +} + +static gboolean cb_paginate(GtkPrintOperation *operation, GtkPrintContext *context, gPrinter *printer) +{ + #if DEBUG_ME + fprintf(stderr, "cb_paginate\n"); + #endif + if (printer->onPaginate) + { + (*printer->onPaginate)(printer); + return printer->isPageCountSet(); + } + else + return TRUE; +} + +static void cb_draw(GtkPrintOperation *operation, GtkPrintContext *context, int page, gPrinter *printer) +{ + #if DEBUG_ME + fprintf(stderr, "cb_draw\n"); + #endif + if (printer->onDraw) + (*printer->onDraw)(printer, context, page); +} + +static gboolean cb_preview(GtkPrintOperation *operation, GtkPrintOperationPreview *preview,GtkPrintContext *context, GtkWindow *parent, gPrinter *printer) +{ + #if DEBUG_ME + fprintf(stderr, "cb_preview\n"); + #endif + printer->_preview = true; + return FALSE; +} + + +static gboolean find_default_printer(GtkPrinter *gtk_printer, gPrinter *printer) +{ + if (!printer->name()) + printer->setName(gtk_printer_get_name(gtk_printer)); + + if (gtk_printer_is_default(gtk_printer)) + { + #if DEBUG_ME + fprintf(stderr, "find_default_printer: %s\n", gtk_printer_get_name(gtk_printer)); + #endif + + printer->setName(gtk_printer_get_name(gtk_printer)); + return TRUE; + } + + return FALSE; +} + +static gboolean find_file_printer(GtkPrinter *gtk_printer, gPrinter *printer) +{ + if (!strcmp(G_OBJECT_TYPE_NAME(gtk_printer_get_backend(gtk_printer)), "GtkPrintBackendFile")) + { + #if DEBUG_ME + fprintf(stderr, "find_file_printer: %s\n", gtk_printer_get_name(gtk_printer)); + #endif + + printer->setName(gtk_printer_get_name(gtk_printer)); + return TRUE; + } + + return FALSE; +} + +#ifdef GTK3 +extern "C" void gtk_printer_option_widget_get_type(void); +#endif + +gPrinter *gPrinter::_current = NULL; + +gPrinter::gPrinter() +{ + _operation = NULL; + _settings = gtk_print_settings_new(); + _page = gtk_page_setup_new(); + _page_count = 1; + _page_count_set = false; + +#ifdef GTK3 + gtk_printer_option_widget_get_type(); +#endif + gtk_enumerate_printers((GtkPrinterFunc)find_default_printer, this, NULL, TRUE); + + setPaperModel(GB_PRINT_A4); + setUseFullPage(false); + + onBegin = NULL; + onEnd = NULL; + onDraw = NULL; + onPaginate = NULL; +} + +gPrinter::~gPrinter() +{ + g_object_unref(G_OBJECT(_settings)); + g_object_unref(G_OBJECT(_page)); +} + +void gPrinter::storeSettings() +{ + if (!_operation) + return; + + g_object_unref(G_OBJECT(_settings)); + _settings = gtk_print_settings_copy(gtk_print_operation_get_print_settings(_operation)); + #if DEBUG_ME + gtk_print_settings_to_file(_settings, "/home/benoit/settings.txt", NULL); + gtk_page_setup_to_file(_page, "/home/benoit/page.txt", NULL); + #endif +} + +void gPrinter::defineSettings() +{ + if (!_operation) + return; + + gtk_print_operation_set_print_settings(_operation, _settings); + gtk_print_operation_set_default_page_setup(_operation, _page); +} + +bool gPrinter::run(bool configure) +{ + GtkPrintOperation *operation; + GtkPrintOperationResult res; + GtkPrintOperationAction action; + gMainWindow *active; + GError *error; + const char *file; + + #if DEBUG_ME + fprintf(stderr, "gPrinter::run: %d\n", configure); + fprintf(stderr, "orientation = %d\n", orientation()); + #endif + + operation = gtk_print_operation_new(); + _operation = operation; + + gtk_print_operation_set_embed_page_setup(operation, true); + gtk_print_operation_set_n_pages(operation, _page_count); + gtk_print_operation_set_use_full_page(operation, _use_full_page); + gtk_print_operation_set_print_settings(operation, _settings); + gtk_print_operation_set_default_page_setup(_operation, _page); + + if (configure) + { + _preview = false; + _configure_ok = false; + g_signal_connect(operation, "begin_print", G_CALLBACK(cb_begin_cancel), this); + g_signal_connect(operation, "preview", G_CALLBACK(cb_preview), this); + g_signal_connect(operation, "end_print", G_CALLBACK(cb_end), this); + g_signal_connect(operation, "paginate", G_CALLBACK(cb_paginate), this); + g_signal_connect(operation, "draw_page", G_CALLBACK(cb_draw), this); + } + else + { + _preview = true; + g_signal_connect(operation, "begin_print", G_CALLBACK(cb_begin), this); + g_signal_connect(operation, "end_print", G_CALLBACK(cb_end), this); + g_signal_connect(operation, "paginate", G_CALLBACK(cb_paginate), this); + g_signal_connect(operation, "draw_page", G_CALLBACK(cb_draw), this); + } + + active = gDesktop::activeWindow(); + + if (isVirtual()) + { + _current = this; + gApplication::_fix_printer_dialog = true; + } + else + gApplication::_fix_printer_dialog = false; + + action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG; + if (!configure) + { + file = outputFileName(); + if (file) + { + unlink(file); + setOutputFileName(outputFileName()); + defineSettings(); + } + // GTK+ bug: GTK_PRINT_OPERATION_ACTION_PRINT does not work with virtual printers. + if (isVirtual()) + { + gApplication::_close_next_window = true; + } + else + action = GTK_PRINT_OPERATION_ACTION_PRINT; + } + + //gtk_print_settings_to_file(gtk_print_operation_get_print_settings(operation), "/home/benoit/settings-before.txt", NULL); + res = gtk_print_operation_run(operation, action, active ? GTK_WINDOW(active->border) : NULL, &error); + _current = NULL; + //gtk_print_settings_to_file(gtk_print_operation_get_print_settings(operation), "/home/benoit/settings-after.txt", NULL); + + #if DEBUG_ME + fprintf(stderr, "_preview = %d\n", _preview); + #endif + + if (_preview) + { + _preview = false; + res = GTK_PRINT_OPERATION_RESULT_CANCEL; + } + else if (_configure_ok) + res = GTK_PRINT_OPERATION_RESULT_APPLY; + + if (res == GTK_PRINT_OPERATION_RESULT_ERROR) + { + #if DEBUG_ME + fprintf(stderr, "error: %s\n", error->message); + #endif + g_error_free(error); + } + else if (res == GTK_PRINT_OPERATION_RESULT_CANCEL) + { + #if DEBUG_ME + fprintf(stderr, "cancel\n"); + #endif + } + else + { + #if DEBUG_ME + fprintf(stderr, "ok\n"); + #endif + } + + if (!configure) + _page_count_set = false; + + if (configure && res == GTK_PRINT_OPERATION_RESULT_APPLY) + { + g_object_unref(G_OBJECT(_page)); + _page = gtk_page_setup_copy(gtk_print_operation_get_default_page_setup(operation)); + } + + g_object_unref(G_OBJECT(operation)); + _operation = NULL; + + #if DEBUG_ME + fprintf(stderr, "orientation => %d\n", orientation()); + #endif + + return res != GTK_PRINT_OPERATION_RESULT_APPLY; +} + +void gPrinter::cancel() +{ + if (!_operation) + return; + + #if DEBUG_ME + fprintf(stderr, "gPrinter::cancel\n"); + #endif + gtk_print_operation_cancel(_operation); +} + +void gPrinter::setPageCount(int v) +{ + if (v < 1 || v > 32767) + return; + + _page_count = v; + _page_count_set = true; + if (_operation) + gtk_print_operation_set_n_pages(_operation, _page_count); +} + +int gPrinter::orientation() const +{ + switch (gtk_page_setup_get_orientation(_page)) + //switch (gtk_print_settings_get_orientation(_settings)) + { + case GTK_PAGE_ORIENTATION_LANDSCAPE: return GB_PRINT_LANDSCAPE; + case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT: return GB_PRINT_PORTRAIT; + case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE: return GB_PRINT_LANDSCAPE; + case GTK_PAGE_ORIENTATION_PORTRAIT: default: return GB_PRINT_PORTRAIT; + } +} + +void gPrinter::setOrientation(int v) +{ + GtkPageOrientation orient; + + switch(v) + { + case GB_PRINT_LANDSCAPE: orient = GTK_PAGE_ORIENTATION_LANDSCAPE; break; + //case GB_PRINT_REVERSE_PORTRAIT: orient = GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT; break; + //case GB_PRINT_REVERSE_LANDSCAPE: orient = GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE; break; + case GB_PRINT_PORTRAIT: default: orient = GTK_PAGE_ORIENTATION_PORTRAIT; break; + } + + gtk_print_settings_set_orientation(_settings, orient); + gtk_page_setup_set_orientation(_page, orient); +} + +GtkPaperSize *gPrinter::getPaperSize() +{ + const char *name; + + switch(_paper_size) + { + case GB_PRINT_A3: name = GTK_PAPER_NAME_A3; break; + case GB_PRINT_A4: name = GTK_PAPER_NAME_A4; break; + case GB_PRINT_A5: name = GTK_PAPER_NAME_A5; break; + case GB_PRINT_B5: name = GTK_PAPER_NAME_B5; break; + case GB_PRINT_LETTER: name = GTK_PAPER_NAME_LETTER; break; + case GB_PRINT_EXECUTIVE: name = GTK_PAPER_NAME_EXECUTIVE; break; + case GB_PRINT_LEGAL: name = GTK_PAPER_NAME_LEGAL; break; + default: name = GTK_PAPER_NAME_A4; _paper_size = GB_PRINT_A4; + } + + return gtk_paper_size_new(name); +} + +void gPrinter::setPaperModel(int v) +{ + GtkPaperSize *paper; + + _paper_size = v; + paper = getPaperSize(); + gtk_print_settings_set_paper_size(_settings, paper); + gtk_page_setup_set_paper_size(_page, paper); + gtk_paper_size_free(paper); +} + +void gPrinter::getPaperSize(double *width, double *height) +{ + GtkPaperSize *paper = gtk_page_setup_get_paper_size(_page); + + *width = gtk_paper_size_get_width(paper, GTK_UNIT_MM); + *height = gtk_paper_size_get_height(paper, GTK_UNIT_MM); + + if (orientation() == GB_PRINT_LANDSCAPE) + { + double swap = *width; + *width = *height; + *height = swap; + } + +#if 0 + if (_paper_size == GB_PRINT_CUSTOM) + { + *width = gtk_print_settings_get_paper_width(_settings, GTK_UNIT_MM); + *height = gtk_print_settings_get_paper_height(_settings, GTK_UNIT_MM); + //*width = gtk_page_setup_get_paper_width(_page, GTK_UNIT_MM); + //*height = gtk_page_setup_get_paper_height(_page, GTK_UNIT_MM); + + // orientation is taken into account + } + else + { + GtkPaperSize *paper = getPaperSize(); + *width = gtk_paper_size_get_width(paper, GTK_UNIT_MM); + *height = gtk_paper_size_get_height(paper, GTK_UNIT_MM); + gtk_paper_size_free(paper); + + } +#endif +} + +void gPrinter::setPaperSize(double width, double height) +{ + GtkPaperSize *paper; + + _paper_size = GB_PRINT_CUSTOM; + + if (orientation() == GB_PRINT_LANDSCAPE) + { + double swap = width; + width = height; + height = swap; + } + + paper = gtk_paper_size_new_custom("Custom", "Custom", width, height, GTK_UNIT_MM); + gtk_page_setup_set_paper_size(_page, paper); + gtk_print_settings_set_paper_size(_settings, paper); + gtk_paper_size_free(paper); + + /*if (orientation() == GB_PRINT_LANDSCAPE) + { + double swap = width; + width = height; + height = swap; + }*/ + + //gtk_print_settings_set_paper_width(_settings, width, GTK_UNIT_MM); + //gtk_print_settings_set_paper_height(_settings, height, GTK_UNIT_MM); +} + +bool gPrinter::collateCopies() const +{ + return gtk_print_settings_get_collate(_settings); +} + +void gPrinter::setCollateCopies(bool v) +{ + gtk_print_settings_set_collate(_settings, v); +} + +bool gPrinter::reverserOrder() const +{ + return gtk_print_settings_get_reverse(_settings); +} + +void gPrinter::setReverseOrder(bool v) +{ + gtk_print_settings_set_reverse(_settings, v); +} + +int gPrinter::duplex() const +{ + switch (gtk_print_settings_get_duplex(_settings)) + { + case GTK_PRINT_DUPLEX_SIMPLEX: return GB_PRINT_SIMPLEX; + case GTK_PRINT_DUPLEX_HORIZONTAL: return GB_PRINT_DUPLEX_HORIZONTAL; + case GTK_PRINT_DUPLEX_VERTICAL: return GB_PRINT_DUPLEX_VERTICAL; + default: return GB_PRINT_SIMPLEX; + } +} + +void gPrinter::setDuplex(int v) +{ + GtkPrintDuplex duplex; + + switch(v) + { + case GB_PRINT_SIMPLEX: duplex = GTK_PRINT_DUPLEX_SIMPLEX; break; + case GB_PRINT_DUPLEX_HORIZONTAL: duplex = GTK_PRINT_DUPLEX_HORIZONTAL; break; + case GB_PRINT_DUPLEX_VERTICAL: duplex = GTK_PRINT_DUPLEX_VERTICAL; break; + default: duplex = GTK_PRINT_DUPLEX_SIMPLEX; break; + } + + gtk_print_settings_set_duplex(_settings, duplex); +} + +bool gPrinter::useColor() const +{ + return gtk_print_settings_get_use_color(_settings); +} + +void gPrinter::setUseColor(bool v) +{ + gtk_print_settings_set_use_color(_settings, v); +} + +int gPrinter::numCopies() const +{ + return gtk_print_settings_get_n_copies(_settings); +} + +void gPrinter::setNumCopies(int v) +{ + gtk_print_settings_set_n_copies(_settings, v); +} + +int gPrinter::resolution() const +{ + return gtk_print_settings_get_resolution(_settings); +} + +void gPrinter::setResolution(int v) +{ + gtk_print_settings_set_resolution(_settings, v); +} + +void gPrinter::getPrintPages(int *from, int *to) const +{ + GtkPageRange *range; + int nrange; + + if (gtk_print_settings_get_print_pages(_settings) == GTK_PRINT_PAGES_ALL) + { + *from = *to = -1; + return; + } + + range = gtk_print_settings_get_page_ranges(_settings, &nrange); + + if (nrange <= 0) + *from = *to = -1; + else + { + *from = range->start; + *to = range->end; + g_free(range); + } +} + +void gPrinter::setPrintPages(int from, int to) +{ + GtkPageRange range = { from, to }; + + gtk_print_settings_set_page_ranges(_settings, &range, 1); + if (from < 0) + gtk_print_settings_set_print_pages(_settings, GTK_PRINT_PAGES_ALL); + else + gtk_print_settings_set_print_pages(_settings, GTK_PRINT_PAGES_RANGES); +} + +void gPrinter::setUseFullPage(bool v) +{ + _use_full_page = v; + if (_operation) + gtk_print_operation_set_use_full_page(_operation, v); +} + +const char *gPrinter::name() const +{ + return gtk_print_settings_get_printer(_settings); +} + +void gPrinter::setName(const char *name) +{ + gtk_print_settings_set_printer(_settings, name); +} + +static char *unescape_uri(const char *uri) +{ + char *path; + + if (!uri) + return NULL; + + if (strncmp(uri, "file://", 7)) + return NULL; + + path = g_uri_unescape_string(&uri[7], "/"); + gt_free_later(path); + + return path; +} + +const char *gPrinter::outputFileName() const +{ + //fprintf(stderr, "outputFileName: %s\n", gtk_print_settings_get(_settings, GTK_PRINT_SETTINGS_OUTPUT_URI)); + return unescape_uri(gtk_print_settings_get(_settings, GTK_PRINT_SETTINGS_OUTPUT_URI)); +} + +void gPrinter::setOutputFileName(const char *file) +{ + char *escaped; + char *uri = NULL; //[strlen(file) + 7]; + //const char *format; + + escaped = g_uri_escape_string(file, "/", true); + g_stradd(&uri, "file://"); + g_stradd(&uri, escaped); + g_free(escaped); + + /*if (g_str_has_suffix(uri, ".ps")) + format = "ps"; + else if (g_str_has_suffix(uri, ".pdf")) + format = "pdf"; + else if (g_str_has_suffix(uri, ".svg")) + format = "svg"; + else + format = NULL;*/ + + gtk_enumerate_printers((GtkPrinterFunc)find_file_printer, this, NULL, TRUE); + + gtk_print_settings_set(_settings, GTK_PRINT_SETTINGS_OUTPUT_URI, uri); + g_free(uri); + + // It does not work!!! + //if (format) + // gtk_print_settings_set(_settings, GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT, format); +} + + +static bool _is_virtual; + +static gboolean find_printer(GtkPrinter *gtk_printer, gPrinter *printer) +{ + if (strcmp(printer->name(), gtk_printer_get_name(gtk_printer))) + return false; + + _is_virtual = gtk_printer_is_virtual(gtk_printer); + return true; +} + +bool gPrinter::isVirtual() +{ + _is_virtual = false; + gtk_enumerate_printers((GtkPrinterFunc)find_printer, this, NULL, TRUE); + return _is_virtual; +} + +static int _dump_tree_radio_button; +static int _dump_tree_entry; + +static void dump_tree(GtkWidget *wid, GtkPrintUnixDialog *dialog) +{ + //fprintf(stderr, "dump_tree: %s\n", G_OBJECT_TYPE_NAME(wid)); + if (GTK_IS_RADIO_BUTTON(wid)) + { + //fprintf(stderr, "dump_tree: radio button: %s\n", gtk_button_get_label(GTK_BUTTON(wid))); + _dump_tree_radio_button--; + if (_dump_tree_radio_button == 0) + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(wid), TRUE); + } + } + else if (GTK_IS_ENTRY(wid)) + { + //fprintf(stderr, "dump_tree: entry: %s\n", gtk_entry_get_text(GTK_ENTRY(wid))); + _dump_tree_entry--; + if (_dump_tree_entry == 0) + { + char *path; + char *name; + + path = unescape_uri(gtk_print_settings_get(gPrinter::_current->_settings, GTK_PRINT_SETTINGS_OUTPUT_URI)); + //fprintf(stderr, "dump_tree: path = %s\n", path); + if (path) + { + name = g_path_get_basename(path); + gtk_entry_set_text(GTK_ENTRY(wid), name); + g_free(name); + } + } + } + else if (GTK_IS_CONTAINER(wid)) + gtk_container_foreach(GTK_CONTAINER(wid), (GtkCallback)dump_tree, (gpointer)dialog); +} + +void gPrinter::fixPrintDialog(GtkPrintUnixDialog *dialog) +{ + const char *output = gtk_print_settings_get(gPrinter::_current->_settings, GTK_PRINT_SETTINGS_OUTPUT_URI); + + _dump_tree_entry = 1; + _dump_tree_radio_button = 0; + + if (output) + { + if (g_str_has_suffix(output, ".pdf")) + _dump_tree_radio_button = 0; + if (g_str_has_suffix(output, ".ps")) + _dump_tree_radio_button = 2; + else if (g_str_has_suffix(output, ".svg")) + _dump_tree_radio_button = 3; + } + + dump_tree(GTK_WIDGET(dialog), dialog); +} + +static gboolean find_all_printers(GtkPrinter *gtk_printer, bool (*callback)(const char *, bool)) +{ + if (strcmp(G_OBJECT_TYPE_NAME(gtk_printer_get_backend(gtk_printer)), "GtkPrintBackendFile")) + return (*callback)(gtk_printer_get_name(gtk_printer), gtk_printer_is_default(gtk_printer)); + + return FALSE; +} + +void gPrinter::enumeratePrinters(bool (*callback)(const char *, bool)) +{ + gtk_enumerate_printers((GtkPrinterFunc)find_all_printers, (gpointer)callback, NULL, TRUE); +} diff --git a/gb.gtk/src/gprinter.h b/gb.gtk/src/gprinter.h new file mode 100644 index 00000000..ca3a9a5c --- /dev/null +++ b/gb.gtk/src/gprinter.h @@ -0,0 +1,116 @@ +/*************************************************************************** + + gprinter.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GPRINTER_H +#define __GPRINTER_H + +#include + +class gPrinter +{ +public: + gPrinter(); + virtual ~gPrinter(); + void *tag; + + bool configure() { return run(true); } + bool print() { return run(false); } + void cancel(); + + void setPageCount(int v); + int pageCount() const { return _page_count; } + bool isPageCountSet() const { return _page_count_set; } + + int orientation() const; + void setOrientation(int v); + + int paperModel() const { return _paper_size; } + void setPaperModel(int v); + + void getPaperSize(double *width, double *height); + void setPaperSize(double width, double height); + + bool collateCopies() const; + void setCollateCopies(bool v); + + bool reverserOrder() const; + void setReverseOrder(bool v); + + int duplex() const; + void setDuplex(int v); + + bool useColor() const; + void setUseColor(bool v); + + int numCopies() const; + void setNumCopies(int v); + + int resolution() const; + void setResolution(int v); + + void getPrintPages(int *from, int *to) const; + void setPrintPages(int from, int to); + + bool useFullPage() const { return _use_full_page; } + void setUseFullPage(bool v); + + const char *name() const; + void setName(const char *name); + + const char *outputFileName() const; + void setOutputFileName(const char *file); + +// Signals + + void (*onBegin)(gPrinter *me, GtkPrintContext *context); + void (*onEnd)(gPrinter *me); + void (*onDraw)(gPrinter *me, GtkPrintContext *context, int page); + void (*onPaginate)(gPrinter *me); + + void defineSettings(); + void storeSettings(); + + bool _preview; + bool _configure_ok; + GtkPrinter *_printer; + GtkPrintSettings *_settings; + + static void fixPrintDialog(GtkPrintUnixDialog *dialog); + static gPrinter *_current; + + static void enumeratePrinters(bool (*callback)(const char *name, bool)); + +private: + bool run(bool configure); + bool isVirtual(); + GtkPaperSize *getPaperSize(); + + GtkPrintOperation *_operation; + GtkPageSetup *_page; + int _page_count; + bool _page_count_set; + int _paper_size; + bool _use_full_page; +}; + +#endif diff --git a/gb.gtk/src/gscrollbar.h b/gb.gtk/src/gscrollbar.h new file mode 100644 index 00000000..1d5cbc87 --- /dev/null +++ b/gb.gtk/src/gscrollbar.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + gscrollbar.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GSCROLLBAR_H +#define __GSCROLLBAR_H + +#include "gslider.h" + +class gScrollBar : public gSlider +{ +public: + gScrollBar(gContainer *parent); + virtual void resize(int w, int h); +}; + +#endif diff --git a/gb.gtk/src/gseparator.cpp b/gb.gtk/src/gseparator.cpp new file mode 100644 index 00000000..baa53506 --- /dev/null +++ b/gb.gtk/src/gseparator.cpp @@ -0,0 +1,103 @@ +/*************************************************************************** + + gseparator.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gdesktop.h" +#include "gseparator.h" + +#ifdef GTK3 +static gboolean cb_draw(GtkWidget *wid, cairo_t *cr, gSeparator *data) +{ + gint x, y, w, h; + gColor color; + + x = y = 0; + w = data->width(); + h = data->height(); + + if (w == 1 || h == 1) + { + color = data->foreground(); + if (color == COLOR_DEFAULT) + color = gDesktop::lightfgColor(); + + gt_cairo_set_source_color(cr, color); + + cairo_rectangle(cr, x, y, w, h); + cairo_fill(cr); + } + else if (w>=h) + gtk_render_line(gtk_widget_get_style_context(wid), cr, x, y + (h / 2), x + w - 1, y + (h / 2)); + else + gtk_render_line(gtk_widget_get_style_context(wid), cr, x + (w / 2), y, x + (w / 2), y + h - 1); + + return false; +} +#else +static gboolean cb_expose(GtkWidget *wid, GdkEventExpose *e, gSeparator *data) +{ + gint x, y, w, h; + gColor color; + + x = wid->allocation.x; + y = wid->allocation.y; + w = data->width(); + h = data->height(); + + if (w == 1 || h == 1) + { + cairo_t *cr; + + cr = gdk_cairo_create(wid->window); + + color = data->foreground(); + if (color == COLOR_DEFAULT) + color = gDesktop::lightfgColor(); + + gt_cairo_set_source_color(cr, color); + + cairo_rectangle(cr, e->area.x, e->area.y, e->area.width, e->area.height); + cairo_fill(cr); + cairo_destroy(cr); + } + else if (w>=h) + gtk_paint_hline(wid->style, wid->window, GTK_STATE_NORMAL, &e->area, wid, NULL, x, x + w, y + h / 2); + else + gtk_paint_vline(wid->style, wid->window, GTK_STATE_NORMAL, &e->area, wid, NULL, y, y + h, x + w / 2); + + return false; +} +#endif + +gSeparator::gSeparator(gContainer *parent) : gControl(parent) +{ + g_typ=Type_gSeparator; + + border = widget = gtk_fixed_new(); + + realize(false); + + ON_DRAW(widget, this, cb_expose, cb_draw); +} + + diff --git a/gb.gtk/src/gseparator.h b/gb.gtk/src/gseparator.h new file mode 100644 index 00000000..05c7dc78 --- /dev/null +++ b/gb.gtk/src/gseparator.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + gseparator.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GSEPARATOR_H +#define __GSEPARATOR_H + +class gSeparator : public gControl +{ +public: + gSeparator(gContainer *parent); +}; + +#endif diff --git a/gb.gtk/src/gshare.h b/gb.gtk/src/gshare.h new file mode 100644 index 00000000..05573d20 --- /dev/null +++ b/gb.gtk/src/gshare.h @@ -0,0 +1,92 @@ +/*************************************************************************** + + gshare.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GSHARE_H +#define __GSHARE_H + +#include "gtag.h" + +//#define DEBUG_SHARE 1 + +#ifdef DEBUG_SHARE + +class gShare +{ +public: + gShare() { nref = 1; tag = NULL; fprintf(stderr, "gShare: %p (1)\n", this); } + virtual ~gShare() { fprintf(stderr, "~gShare: %p\n", this); if (tag) while (nref > 1) nref--, tag->unref(); } + + void ref() { nref++; if (tag) tag->ref(); fprintf(stderr, "gShare::ref: %p (%d)\n", this, nref); } + void unref() { nref--; fprintf(stderr, "gShare::unref: %p (%d)\n", this, nref); if (nref <= 0) delete this; else if (tag) tag->unref(); } + int refCount() { return nref; } + + void setTag(gTag *t) { tag = t; for (int i = 0; i < (nref - 1); i++) tag->ref(); } + gTag *getTag() { return tag; } + void *getTagValue() { return tag->get(); } + +protected: + static void assign(gShare **dst, gShare *src = 0) + { + if (src) src->ref(); + if (*dst) (*dst)->unref(); + *dst = src; + } + +private: + int nref; + gTag *tag; +}; + +#else + +class gShare +{ +public: + gShare() { nref = 1; tag = NULL; } + virtual ~gShare() { if (tag) { while (nref > 1) nref--, tag->unref(); delete tag; } } + + void ref() { nref++; if (tag) tag->ref(); } + void unref() { nref--; if (nref <= 0) delete this; else if (tag) tag->unref(); } + int refCount() { return nref; } + + void setTag(gTag *t) { tag = t; for (int i = 0; i < (nref - 1); i++) tag->ref(); } + gTag *getTag() { return tag; } + void *getTagValue() { return tag->get(); } + +protected: + static void assign(gShare **dst, gShare *src = 0) + { + if (src) src->ref(); + if (*dst) (*dst)->unref(); + *dst = src; + } + +private: + int nref; + gTag *tag; +}; + +#endif + +#endif + diff --git a/gb.gtk/src/gsignals.cpp b/gb.gtk/src/gsignals.cpp new file mode 100644 index 00000000..c05134b0 --- /dev/null +++ b/gb.gtk/src/gsignals.cpp @@ -0,0 +1,355 @@ +/*************************************************************************** + + gsignals.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include + +#include "gapplication.h" +#include "gdrawingarea.h" +#include "gkey.h" +#include "gmouse.h" +#include "gmainwindow.h" +#include "gdrag.h" +#include "gdesktop.h" + +//#define DEBUG_DND 1 + +static void sg_destroy (GtkWidget *object,gControl *data) +{ + if (data->_no_delete) + return; + + //if (!data->_destroyed) + delete data; +} + +static gboolean sg_menu(GtkWidget *widget, gControl *data) +{ + if (!gApplication::userEvents()) return false; + if (data->onMouseEvent) + return data->onMouseEvent(data, gEvent_MouseMenu); + else + return false; +} + +gboolean gcb_focus_in(GtkWidget *widget,GdkEventFocus *event,gControl *data) +{ + if (!gApplication::allEvents()) return false; + + //fprintf(stderr, "gcb_focus_in: %s\n", data->name()); + + gApplication::setActiveControl(data, true); + + return false; +} + +gboolean gcb_focus_out(GtkWidget *widget,GdkEventFocus *event,gControl *data) +{ + if (!gApplication::allEvents()) return false; + + //fprintf(stderr, "gcb_focus_out: %s\n", data->name()); + + gApplication::setActiveControl(data, false); + + return false; +} + + + +/**************************************************** + Drag +*****************************************************/ + +static void sg_drag_data_get(GtkWidget *widget, GdkDragContext *context, GtkSelectionData *dt, guint i, guint time, gControl *data) +{ + char *text; + int len; + gPicture *pic; + //g_debug("sg_drag_data_get\n"); + + context = gDrag::enable(context, data, time); + + text = gDrag::getText(&len, NULL, true); + if (text) + { + gtk_selection_data_set_text(dt, text, len); + return; + } + + pic = gDrag::getImage(true); + if (pic) + { + gtk_selection_data_set_pixbuf(dt, pic->getPixbuf()); + } + + gDrag::disable(context); +} + +static void sg_drag_end(GtkWidget *widget,GdkDragContext *ct,gControl *data) +{ + #if DEBUG_DND + fprintf(stderr, "sg_drag_end: %s\n", data->name()); + #endif + + gDrag::end(); +} + + +/**************************************************** + Drop +*****************************************************/ + +// BM: What for? +//static guint32 _drag_time = 0; + +static gboolean sg_drag_motion(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gControl *data) +{ + bool retval = true; + int action; + gControl *source; + + if (!gApplication::allEvents()) return true; + + #if DEBUG_DND + fprintf(stderr, "sg_drag_motion: %s\n", data->name()); + #endif + /*if (_drag_time != context->start_time) + { + g_debug("sg_drag_motion: cancel!\n"); + gDrag::cancel(); + data->_drop_0 = true; + data->_drop_1 = false; + data->_drop_2 = false; + }*/ + +#if GTK_CHECK_VERSION(2, 22, 0) + switch (gdk_drag_context_get_suggested_action(context)) +#else + switch (context->suggested_action) +#endif + { + case GDK_ACTION_MOVE: + action = gDrag::Move; + break; + case GDK_ACTION_LINK: + action = gDrag::Link; + break; + case GDK_ACTION_COPY: + default: + action = gDrag::Copy; + } + + source = gApplication::controlItem(gtk_drag_get_source_widget(context)); + gDrag::setDropData(action, x, y, source, NULL); + + context = gDrag::enable(context, data, time); + + if (!data->_drag_enter) + { + //fprintf(stderr, "sg_drag_motion: onDrag: %p\n", widget); + + x = 0; + y = 0; + + if (data->onDrag) + retval = !data->onDrag(data); + data->_drag_enter = true; + } + + if (retval) + { + //fprintf(stderr, "sg_drag_motion: onDragMove: %p\n", widget); + gControl *control = data; + + //while (control->_proxy) + // control = control->_proxy; + + while (control) + { + #if DEBUG_DND + fprintf(stderr, "drag move %s\n", control->name()); + #endif + if (control->canRaise(control, gEvent_DragMove)) + { + if (control->onDragMove) + { + retval = !control->onDragMove(control); + if (!retval) + break; + } + } + control = control->_proxy; + } + } + + context = gDrag::disable(context); + + if (retval) + { + //fprintf(stderr, "sg_drag_motion: accept\n"); +#if GTK_CHECK_VERSION(2, 22, 0) + gdk_drag_status(context, gdk_drag_context_get_suggested_action(context), time); +#else + gdk_drag_status(context, context->suggested_action, time); +#endif + return true; + } + + //fprintf(stderr, "sg_drag_motion: cancel\n"); + gDrag::hide(data); + return false; +} + + +void sg_drag_leave(GtkWidget *widget, GdkDragContext *context, guint time, gControl *data) +{ + #if DEBUG_DND + fprintf(stderr, "sg_drag_leave: %s\n", data->name()); + #endif + + data->_drag_enter = false; + gDrag::hide(data); + + gControl *control = data; + + //while (control->_proxy) + // control = control->_proxy; + + while (control) + { + control->emit(SIGNAL(control->onDragLeave)); + control = control->_proxy; + } +} + + +gboolean sg_drag_drop(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gControl *data) +{ + gControl *source; + + #if DEBUG_DND + fprintf(stderr, "sg_drag_drop: %s\n", data->name()); + #endif + + // sg_drag_leave() is automatically called when a drop occurs + //sg_drag_leave(widget, context, time, data); + + if (!data->canRaise(data, gEvent_Drop)) + { + gtk_drag_finish(context, false, false, time); + return false; + } + + source = gApplication::controlItem(gtk_drag_get_source_widget(context)); + + gDrag::setDropData(gDrag::getAction(), x, y, source, data); + + context = gDrag::enable(context, data, time); + data->_drag_get_data = true; + + if (data->onDrop) + data->onDrop(data); + + context = gDrag::disable(context); + + //fprintf(stderr, "cancel = %d\n", cancel); + + gtk_drag_finish(context, true, false, time); + + //data->_drag_enter = false; + data->_drag_get_data = false; + + return true; +} + +// void sg_size(GtkWidget *widget,GtkRequisition *req, gContainer *data) +// { +// if (data->parent()) data->parent()->performArrange(); +// if (data->isContainer()) data->performArrange(); +// } + +static void cb_show(GtkWidget *widget, gContainer *data) +{ + data->performArrange(); +} + +void gControl::borderSignals() +{ + g_signal_connect(G_OBJECT(border),"destroy",G_CALLBACK(sg_destroy),(gpointer)this); + //g_signal_connect(G_OBJECT(border),"drag-data-received",G_CALLBACK(sg_drag_data_received),(gpointer)this); + g_signal_connect(G_OBJECT(border),"drag-motion",G_CALLBACK(sg_drag_motion),(gpointer)this); + g_signal_connect(G_OBJECT(border),"drag-leave",G_CALLBACK(sg_drag_leave),(gpointer)this); + g_signal_connect(G_OBJECT(border),"drag-drop",G_CALLBACK(sg_drag_drop),(gpointer)this); + g_signal_connect(G_OBJECT(border),"drag-data-get",G_CALLBACK(sg_drag_data_get),(gpointer)this); + g_signal_connect(G_OBJECT(border),"drag-end",G_CALLBACK(sg_drag_end),(gpointer)this); + //g_signal_connect(G_OBJECT(border),"enter-notify-event",G_CALLBACK(sg_enter),(gpointer)this); + //g_signal_connect(G_OBJECT(border),"leave-notify-event",G_CALLBACK(sg_enter),(gpointer)this); + + //g_signal_connect_after(G_OBJECT(border),"size-allocate",G_CALLBACK(sg_size),(gpointer)this); + + if (isContainer()) + g_signal_connect(G_OBJECT(border), "show", G_CALLBACK(cb_show), (gpointer)this); + + if (border != widget && !_scroll) + { + /*if (!_no_default_mouse_event) + { + g_signal_connect(G_OBJECT(border),"button-release-event",G_CALLBACK(gcb_button_release),(gpointer)this); + g_signal_connect(G_OBJECT(border),"button-press-event",G_CALLBACK(gcb_button_press),(gpointer)this); + }*/ + g_signal_connect(G_OBJECT(border),"popup-menu",G_CALLBACK(sg_menu),(gpointer)this); + //g_signal_connect_after(G_OBJECT(border),"motion-notify-event",G_CALLBACK(sg_motion),(gpointer)this); + //g_signal_connect(G_OBJECT(border),"scroll-event",G_CALLBACK(sg_scroll),(gpointer)this); + } +} + +void gControl::widgetSignals() +{ + if (!(border != widget && !_scroll)) + { + //g_signal_connect(G_OBJECT(widget),"scroll-event",G_CALLBACK(sg_scroll),(gpointer)this); + /*if (!_no_default_mouse_event) + { + g_signal_connect(G_OBJECT(widget),"button-release-event",G_CALLBACK(gcb_button_release),(gpointer)this); + g_signal_connect(G_OBJECT(widget),"button-press-event",G_CALLBACK(gcb_button_press),(gpointer)this); + }*/ + //g_signal_connect(G_OBJECT(widget),"motion-notify-event",G_CALLBACK(sg_motion),(gpointer)this); + g_signal_connect(G_OBJECT(widget),"popup-menu",G_CALLBACK(sg_menu),(gpointer)this); + } + + g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(gcb_key_event), (gpointer)this); + g_signal_connect(G_OBJECT(widget), "key-release-event", G_CALLBACK(gcb_key_event), (gpointer)this); + g_signal_connect(G_OBJECT(widget), "focus-in-event", G_CALLBACK(gcb_focus_in),(gpointer)this); + g_signal_connect(G_OBJECT(widget), "focus-out-event", G_CALLBACK(gcb_focus_out),(gpointer)this); + //g_signal_connect(G_OBJECT(widget),"event",G_CALLBACK(sg_event),(gpointer)this); + if (widget != border) + { + g_signal_connect(G_OBJECT(widget), "drag-end", G_CALLBACK(sg_drag_end), (gpointer)this); + } +} + +void gControl::initSignals() +{ + borderSignals(); + widgetSignals(); +} diff --git a/gb.gtk/src/gslider.cpp b/gb.gtk/src/gslider.cpp new file mode 100644 index 00000000..a744a540 --- /dev/null +++ b/gb.gtk/src/gslider.cpp @@ -0,0 +1,332 @@ +/*************************************************************************** + + gslider.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gdesktop.h" +#include "gscrollbar.h" +#include "gslider.h" + +static void cb_change(GtkAdjustment *adj, gSlider *data) +{ + int new_value = gtk_adjustment_get_value(adj); + + if (data->_value == new_value) + return; + + data->_value = new_value; + if (data->onChange) + data->onChange(data); +} + +void gSlider::update() +{ + GtkAdjustment* adj = gtk_range_get_adjustment(GTK_RANGE(widget)); + int value = _value; + + if (value < _min) + value = _min; + else if (value > _max) + value = _max; + + if (g_typ == Type_gSlider) + { +#ifndef GTK3 + if (_min == _max) + _max = _min + 1; +#endif + gtk_range_set_range(GTK_RANGE(widget), (gdouble)_min, (gdouble)_max); + gtk_range_set_increments(GTK_RANGE(widget), (gdouble)_step, (gdouble)_page_step); + } + else + { + gtk_range_set_range(GTK_RANGE(widget), (gdouble)_min, (gdouble)_max + _page_step); + gtk_range_set_increments(GTK_RANGE(widget), (gdouble)_step, (gdouble)_page_step); + gtk_adjustment_set_page_size(adj, _page_step); + } + gtk_range_set_value(GTK_RANGE(widget), value); +#ifndef GTK3 + gtk_range_set_update_policy(GTK_RANGE(widget), _tracking ? GTK_UPDATE_CONTINUOUS : GTK_UPDATE_DISCONTINUOUS); +#endif + + checkInverted(); +} + +void gSlider::init() +{ + GtkAdjustment* adj = gtk_range_get_adjustment(GTK_RANGE(widget)); + + _use_wheel = true; + onChange = NULL; + + g_signal_connect(adj, "value-changed", G_CALLBACK(cb_change), (gpointer)this); + //g_signal_connect(adj, "changed", G_CALLBACK(cb_change), (gpointer)this); +} + +gSlider::gSlider(gContainer *par, bool scrollbar) : gControl(par) +{ + g_typ = Type_gSlider; + + _mark = false; + _step = 1; + _page_step = 10; + _value = 0; + _min = 0; + _max = 100; + _tracking = true; + +/*#ifdef GTK3 + border = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); +#else + border = gtk_alignment_new(0, 0, 1, 1); +#endif*/ + + if (scrollbar) + return; + +#ifdef GTK3 + widget = gtk_scale_new(GTK_ORIENTATION_VERTICAL, NULL); + //gtk_widget_set_hexpand(widget, TRUE); +#else + widget = gtk_vscale_new(NULL); +#endif + gtk_scale_set_draw_value(GTK_SCALE(widget), false); + + init(); + update(); + realize(false); + //g_signal_connect_after(G_OBJECT(border),"expose-event",G_CALLBACK(slider_Expose),(gpointer)this); +} + +gScrollBar::gScrollBar(gContainer *par) : gSlider(par, true) +{ + g_typ = Type_gScrollBar; +#ifdef GTK3 + widget = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, NULL); +#else + widget = gtk_hscrollbar_new(NULL); +#endif + + init(); + update(); + realize(false); + +#ifndef GTK3 + gtk_range_set_update_policy(GTK_RANGE(widget),GTK_UPDATE_CONTINUOUS); +#endif +} + +bool gSlider::mark() +{ + return _mark; +} + +void gSlider::updateMark() +{ + int i; + int step; + + if (!_mark) + return; + + gtk_scale_clear_marks(GTK_SCALE(widget)); + + step = _page_step; + while (step < ((_max - _min) / 20)) + step *= 2; + + for (i = _min; i <= _max; i += step) + gtk_scale_add_mark(GTK_SCALE(widget), i, isVertical() ? GTK_POS_TOP : GTK_POS_RIGHT, NULL); +} + +void gSlider::setMark(bool vl) +{ + + if (vl == _mark) return; + + _mark = vl; + gtk_scale_clear_marks(GTK_SCALE(widget)); + updateMark(); +} + +int gSlider::step() +{ + return _step; +} + +int gSlider::pageStep() +{ + return _page_step; +} + +void gSlider::setStep(int vl) +{ + if (vl < 1) vl = 1; + if (vl == _step) return; + + _step = vl; + update(); + if (_mark) gtk_widget_queue_draw(widget); +} + +void gSlider::setPageStep(int vl) +{ + if (vl < 1) vl = 1; + if (vl == _page_step) return; + + _page_step = vl; + update(); + updateMark(); +} + +int gSlider::max() +{ + return _max; +} + +int gSlider::min() +{ + return _min; +} + +int gSlider::value() +{ + return _value; +} + +void gSlider::setMax(int vl) +{ + _max = vl; + if (_min > _max) + _min = _max; + update(); + updateMark(); +} + +void gSlider::setMin(int vl) +{ + _min = vl; + if (_min > _max) + _max = _min; + update(); + updateMark(); +} + +bool gSlider::tracking() +{ + return _tracking; +} + +void gSlider::setTracking(bool vl) +{ + _tracking = vl; + update(); +} + +void gSlider::setValue(int vl) +{ + if (vl < _min) + vl = _min; + else if (vl > _max) + vl = _max; + + if (_value == vl) + return; + + _value = vl; + update(); + + emit(SIGNAL(onChange)); +} + +#if 0 +void gSlider::orientation(int w,int h) +{ + //GtkAdjustment *adj; + //GtkOrientation orient; + + gtk_orientable_set_orientation(GTK_ORIENTABLE(widget), (w < h) ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL); + + /*if (orient != gtk_orientable_get_orientation(GTK_ORIENTABLE(widget))) + { + adj = gtk_range_get_adjustment(GTK_RANGE(widget)); + g_object_ref(adj); + + gtk_widget_destroy(widget); + + widget = gtk_scale_new( + if (type == GTK_TYPE_VSCALE) + widget = gtk_vscale_new(adj); + else + widget = gtk_hscale_new(adj); + + gtk_container_add(GTK_CONTAINER(border), widget); + + gtk_scale_set_draw_value(GTK_SCALE(widget), false); + gtk_widget_show(widget); + widgetSignals(); + g_signal_connect(G_OBJECT(widget), "value-changed", G_CALLBACK(cb_change), (gpointer)this); + + g_object_unref(adj); + + init(); + }*/ +} +#endif + +void gSlider::resize(int w, int h) +{ + gControl::resize(w, h); + gtk_orientable_set_orientation(GTK_ORIENTABLE(widget), (w < h) ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL); +} + +void gScrollBar::resize(int w, int h) +{ + gControl::resize(w, h); + gtk_orientable_set_orientation(GTK_ORIENTABLE(widget), (w < h) ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL); +} + +int gSlider::getDefaultSize() +{ + GtkRequisition req; + +#ifdef GTK3 + gtk_widget_get_preferred_size(widget, &req, NULL); +#else + gtk_widget_size_request(widget, &req); +#endif + + if (width() < height()) + return req.width; + else + return req.height; +} + +bool gSlider::isVertical() const +{ + return gtk_orientable_get_orientation(GTK_ORIENTABLE(widget)) == GTK_ORIENTATION_VERTICAL; +} + +void gSlider::checkInverted() +{ + gtk_range_set_inverted(GTK_RANGE(widget), !isVertical() && gDesktop::rightToLeft()); +} diff --git a/gb.gtk/src/gslider.h b/gb.gtk/src/gslider.h new file mode 100644 index 00000000..29ca5346 --- /dev/null +++ b/gb.gtk/src/gslider.h @@ -0,0 +1,75 @@ +/*************************************************************************** + + gslider.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GSLIDER_H +#define __GSLIDER_H + +class gSlider : public gControl +{ +public: + gSlider(gContainer *parent, bool scrollbar = false); + +//"Properties" + //int foreground(); + //int background(); + int max(); + int min(); + bool tracking(); + int value(); + bool mark(); + int step(); + int pageStep(); + + //void setForeground(int vl); + //void setBackground(int vl); + void setMax(int vl); + void setMin(int vl); + void setTracking(bool vl); + void setValue(int vl); + void setMark(bool vl); + void setStep(int vl); + void setPageStep(int vl); + + int getDefaultSize(); + bool isVertical() const; + + virtual void resize(int w, int h); + +//"Signals" + void (*onChange)(gSlider *sender); + +//"Private" + void updateMark(); + void init(); + void update(); + void checkInverted(); + + bool _mark; + int _step; + int _page_step; + int _value; + int _min, _max; + bool _tracking; +}; + +#endif diff --git a/gb.gtk/src/gtabstrip.cpp b/gb.gtk/src/gtabstrip.cpp new file mode 100644 index 00000000..3042c16d --- /dev/null +++ b/gb.gtk/src/gtabstrip.cpp @@ -0,0 +1,856 @@ +/*************************************************************************** + + gtabstrip.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gapplication.h" +#include "gmouse.h" +#include "gdesktop.h" +#include "gtabstrip.h" + +static const struct { + guint width; + guint height; + guint bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */ + guint8 pixel_data[16 * 16 * 4 + 1]; +} _close_button = { + 16, 16, 4, + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0_a]\206`d_\344bd`x\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0__]v`d_\344ac_\204\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0bd`\335\261\264\254\377}\200z\356`b^\200\0\0\0\0\0\0" + "\0\0`b\\}z}w\355\260\263\253\377ce`\331\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0_a]v}\200z\356\272\275\265\377\201\203}\356`b^\200`b\\}z}w" + "\355\261\264\254\377|~y\355ad_n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0ac]~\203\205\200\356\300\304\274\377\206\211\203\356\200\202" + "}\355\261\264\254\377|~y\356`d^x\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0bd`\177\212\215\210\356\301\304\274\377\271" + "\274\264\377\201\204}\356ac_y\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0bd`}\213\215\210\355\303\306\276\377" + "\272\276\266\377\177\201|\355`c\\w\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0bd^}\204\206\201\355\307\313\303\377\212" + "\215\206\355\203\205\200\355\261\264\254\377z|v\355`c\\w\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0aa]v\200\202~\355\302\306\276\377" + "\205\207\202\356bd^\200`b^}{}w\355\261\264\254\377z|v\355`c`o\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0dfa\342\271\274\264\377\203\205\200\355" + "`d^\200\0\0\0\0\0\0\0\0`b\\}|~y\354\261\264\254\377bd^\340\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0ab_\231cfa\341`b^z\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0^`\\wcfa\335`b^\222\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0", +}; + + + +cairo_surface_t *gTabStrip::_button_normal = NULL; +cairo_surface_t *gTabStrip::_button_disabled = NULL; + +static void cb_click(GtkNotebook *nb, GtkWidget *pg, guint pnum, gTabStrip *data) +{ + data->updateFont(); + data->performArrange(); + data->emit(SIGNAL(data->onClick)); +} + +static void cb_size_allocate(GtkWidget *wid, GtkAllocation *alloc, gTabStrip *data) +{ + if (wid == data->getContainer() && (alloc->width != data->_client_w || alloc->height != data->_client_h)) + { + int tx, ty, px, py; + + if (data->getScreenPos(&tx, &ty)) + return; + + gdk_window_get_origin(gtk_widget_get_window(wid), &px, &py); + //fprintf(stderr, "alloc: tab = %d %d page = %d %d alloc = %d %d\n", tx, ty, px, py, alloc->x, alloc->y); + + data->_client_x = px - tx + alloc->x; + data->_client_y = py - ty + alloc->y; + data->_client_w = alloc->width; + data->_client_h = alloc->height; + //fprintf(stderr, "alloc: %s: %d %d %d %d\n", data->name(), data->_client_x, data->_client_y, alloc->width, alloc->height); + data->performArrange(); + } +} + +#ifdef GTK3 +static gboolean cb_button_draw(GtkWidget *wid, cairo_t *cr, gTabStrip *data) +#else +static gboolean cb_button_expose(GtkWidget *wid, GdkEventExpose *e, gTabStrip *data) +#endif +{ +#ifndef GTK3 + cairo_t *cr; +#endif + cairo_surface_t *img; + GdkRectangle rpix = {0,0,0,0}; + GdkRectangle rect = {0,0}; + int py, px; + int dx, dy; + +#ifndef GTK3 + GTK_BUTTON(wid)->relief = GTK_RELIEF_NORMAL; +#endif + + gtk_widget_get_allocation(wid, &rect); +#ifdef GTK3 + rect.x = rect.y = 0; +#endif + px = rect.width; + +#ifdef GTK3 + if (gtk_widget_get_state_flags(data->widget) & GTK_STATE_FLAG_ACTIVE) +#else + if (GTK_WIDGET_STATE(data->widget) == GTK_STATE_ACTIVE) +#endif + { + gtk_widget_style_get (wid, + "child-displacement-x", &dx, + "child-displacement-y", &dy, + (void *)NULL); + rect.x += dx; + rect.y += dy; + } + +#ifdef GTK3 + if (gtk_widget_get_state_flags(data->widget) & GTK_STATE_FLAG_INSENSITIVE) +#else + if (GTK_WIDGET_STATE(data->widget) == GTK_STATE_INSENSITIVE) +#endif + img = data->_button_disabled; + else + img = data->_button_normal; + + rpix.width = cairo_image_surface_get_width(img); + rpix.height = cairo_image_surface_get_height(img); + + py = (rect.height - rpix.height) / 2; + + rect.x += (px - rpix.width) / 2; + rect.y += py; + +#ifndef GTK3 + cr = gdk_cairo_create(wid->window); +#endif + + cairo_set_source_surface(cr, img, rect.x, rect.y); + cairo_paint(cr); + +#ifndef GTK3 + cairo_destroy(cr); +#endif + + return false; +} + +static void cb_button_clicked(GtkWidget *wid, gTabStrip *data) +{ + if (data->onClose) + (*data->onClose)(data, data->getRealIndex((GtkWidget *)g_object_get_data(G_OBJECT(wid), "gambas-tab-page"))); +} + +#ifdef GTK3 +static void cb_scroll(GtkWidget *wid, GdkEvent *event, gTabStrip *data) +{ + int dir = event->scroll.direction; + int page; + + if (dir == GDK_SCROLL_SMOOTH) + return; + + page = gtk_notebook_get_current_page(GTK_NOTEBOOK(data->widget)); + + switch (dir) + { + case GDK_SCROLL_UP: + case GDK_SCROLL_LEFT: + page--; + if (page >= 0) + gtk_notebook_set_current_page(GTK_NOTEBOOK(data->widget), page); + break; + case GDK_SCROLL_RIGHT: + case GDK_SCROLL_DOWN: default: + page++; + if (page < gtk_notebook_get_n_pages(GTK_NOTEBOOK(data->widget))) + gtk_notebook_set_current_page(GTK_NOTEBOOK(data->widget), page); + break; + } +} +#endif + +/**************************************************************************** + + gTabStripPage + +****************************************************************************/ + +class gTabStripPage +{ +public: + gTabStripPage(gTabStrip *tab); + ~gTabStripPage(); + + char *text() const; + void setText(char *text); + gPicture *picture() const { return _picture; } + void setPicture(gPicture *picture); + bool isVisible() const { return _visible; } + void setVisible(bool v); + bool enabled() const; + void setEnabled(bool v); + /*int count() const; + gControl *child(int n) const;*/ + void updateColors(); + void updateFont(); + void updateButton(); + + GtkWidget *fix; + GtkWidget *widget; + GtkWidget *label; + GtkWidget *image; + GtkWidget *hbox; + GtkWidget *_button; + + gPicture *_picture; + gTabStrip *parent; + bool _visible; + int index; +}; + +gTabStripPage::gTabStripPage(gTabStrip *tab) +{ + char text[16]; + + parent = tab; + + widget = gtk_fixed_new(); + + //fix = gtk_event_box_new(); + + hbox = gtk_hbox_new(false, 4); + fix = hbox; + //gtk_box_set_spacing(GTK_BOX(hbox), 4); + //gtk_container_add(GTK_CONTAINER(fix), hbox); + + image = gtk_image_new(); + //gtk_container_add(GTK_CONTAINER(hbox), image); + gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0); + + label = gtk_label_new_with_mnemonic(""); + //gtk_container_add(GTK_CONTAINER(hbox), label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + //gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END); + + updateColors(); + updateFont(); + + g_signal_connect_after(G_OBJECT(widget), "size-allocate", G_CALLBACK(cb_size_allocate), (gpointer)parent); + + g_object_ref(widget); + g_object_ref(fix); + + _visible = false; + _picture = NULL; + + if (parent->count()) + index = parent->get(parent->count() - 1)->index + 1; + else + index = 0; + + gtk_widget_hide(image); + + _button = NULL; + updateButton(); + + sprintf(text, "Tab %d", index); + setText(text); + + setVisible(true); +} + +gTabStripPage::~gTabStripPage() +{ + setVisible(false); + gPicture::assign(&_picture); + g_object_unref(fix); + g_object_unref(widget); +} + +void gTabStripPage::updateColors() +{ +#ifdef GTK3 + gt_widget_set_color(widget, FALSE, parent->realBackground()); +#else + set_gdk_bg_color(widget, parent->realBackground()); + set_gdk_fg_color(label, parent->realForeground()); +#endif +} + +void gTabStripPage::updateFont() +{ + PangoFontDescription *desc = NULL; + gFont *fnt; + + fnt = parent->textFont(); + if (!fnt) + fnt = parent->font(); + + if (fnt) + desc = fnt->desc(); + +#ifdef GTK3 + gtk_widget_override_font(widget, desc); + gtk_widget_override_font(label, desc); +#else + gtk_widget_modify_font(widget, desc); + gtk_widget_modify_font(label, desc); +#endif +} + +void gTabStripPage::setText(char *text) +{ + char *buf; + + gMnemonic_correctText(text, &buf); + gtk_label_set_text_with_mnemonic(GTK_LABEL(label), buf); + g_free(buf); +} + +char *gTabStripPage::text() const +{ + char *buf; + + gMnemonic_returnText((char*)gtk_label_get_text(GTK_LABEL(label)), &buf); + gt_free_later(buf); + return buf; +} + +void gTabStripPage::setPicture(gPicture *picture) +{ + GdkPixbuf *buf; + + gPicture::assign(&_picture, picture); + + buf = _picture ? _picture->getPixbuf() : NULL; + + if (!buf) + gtk_widget_hide(image); + else + { + gtk_image_set_from_pixbuf(GTK_IMAGE(image), buf); + gtk_widget_show(image); + } +} + +bool gTabStripPage::enabled() const +{ +#if GTK_CHECK_VERSION(2, 18, 0) + return gtk_widget_get_sensitive(hbox); +#else + return GTK_WIDGET_SENSITIVE(hbox); +#endif +} + +void gTabStripPage::setEnabled(bool v) +{ + gtk_widget_set_sensitive(label, v); + gtk_widget_set_sensitive(image, v); + gtk_widget_set_sensitive(widget, v); +} + +void gTabStripPage::setVisible(bool v) +{ + int ind, n; + gTabStripPage *page; + + if (_visible == v) + return; + + _visible = v; + + if (v) + { + n = 0; + for (ind = 0; ind < parent->count(); ind++) + { + page = parent->get(ind); + if (index <= page->index) + break; + if (page->isVisible()) + n++; + } + gtk_notebook_insert_page(GTK_NOTEBOOK(parent->widget), widget, fix, n); + //gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(parent->widget), widget, TRUE); + gtk_widget_realize(widget); + gtk_widget_realize(fix); + gtk_widget_show_all(widget); + gtk_widget_show_all(fix); + //gtk_container_resize_children(GTK_CONTAINER(gtk_widget_get_parent(widget))); + //gtk_container_resize_children(GTK_CONTAINER(gtk_widget_get_parent(gtk_widget_get_parent(widget)))); + } + else + { + ind = gtk_notebook_page_num(GTK_NOTEBOOK(parent->widget), widget); + gtk_notebook_remove_page(GTK_NOTEBOOK(parent->widget), ind); + } +} + +#ifndef GTK3 +static gboolean cb_button_fix(GtkWidget *wid, GdkEvent *e, gTabStripPage *data) +{ + GTK_BUTTON(wid)->relief = GTK_RELIEF_NONE; + return false; +} +#endif + +void gTabStripPage::updateButton() +{ + bool v = parent->isClosable(); + + if (v && !_button) + { + _button = gtk_button_new(); + gtk_button_set_focus_on_click(GTK_BUTTON(_button), false); +#ifdef GTK3 + gtk_button_set_relief(GTK_BUTTON(_button), GTK_RELIEF_NONE); +#else + g_signal_connect(G_OBJECT(_button), "expose-event", G_CALLBACK(cb_button_fix), (gpointer)this); +#endif + //g_signal_connect_after(G_OBJECT(_button), "expose-event", G_CALLBACK(cb_button_expose), (gpointer)parent); + ON_DRAW(_button, parent, cb_button_expose, cb_button_draw); + g_signal_connect(G_OBJECT(_button), "clicked", G_CALLBACK(cb_button_clicked), (gpointer)parent); + g_object_set_data(G_OBJECT(_button), "gambas-tab-page", (void *)widget); + + gtk_widget_show(_button); + + gtk_box_pack_start(GTK_BOX(hbox), _button, FALSE, FALSE, 0); + } + else if (!v && _button) + { + gtk_widget_destroy(_button); + _button = NULL; + } + + if (_button) + { + gtk_widget_set_size_request(_button, 20, 20); + } +} + + +/**************************************************************************** + + gTabStrip + +****************************************************************************/ + +gTabStrip::gTabStrip(gContainer *parent) : gContainer(parent) +{ + g_typ = Type_gTabStrip; + _pages = g_ptr_array_new(); + _textFont = NULL; + _closable = false; + + onClick = NULL; + onClose = NULL; + + //border = gtk_event_box_new(); + //gtk_event_box_set_visible_window(GTK_EVENT_BOX(border), false); + + border = widget = gtk_notebook_new(); + gtk_notebook_set_scrollable(GTK_NOTEBOOK(widget), TRUE); + gtk_drag_dest_unset(widget); + + realize(); + + gtk_widget_add_events(border, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK + | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK + | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_SCROLL_MASK); + + setCount(1); + + g_signal_connect_after(G_OBJECT(widget), "switch-page", G_CALLBACK(cb_click), (gpointer)this); +#ifdef GTK3 + g_signal_connect(G_OBJECT(widget), "scroll-event", G_CALLBACK(cb_scroll), (gpointer)this); +#endif +} + +gTabStrip::~gTabStrip() +{ + // Do not use setCount() or removeTab(), because they cannot remove non-empty tabs + lock(); + while (count()) + destroyTab(count() - 1); + unlock(); + gFont::assign(&_textFont); + setClosable(false); + g_ptr_array_free(_pages, TRUE); +} + + +int gTabStrip::getRealIndex(GtkWidget *page) const +{ + int i; + + for (i = 0; i < count(); i++) + { + if (get(i)->widget == page) + return i; + } + + return -1; +} + +int gTabStrip::index() const +{ + GtkWidget *page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(widget), gtk_notebook_get_current_page(GTK_NOTEBOOK(widget))); + return getRealIndex(page); +} + +void gTabStrip::setIndex(int vl) +{ + GtkWidget *page; + + if ( (vl<0) || (vl>=count()) || !get(vl)->isVisible() ) return; + + page = get(vl)->widget; + gtk_notebook_set_current_page(GTK_NOTEBOOK(widget), getRealIndex(page)); + //widget = get(vl)->widget; +} + +int gTabStrip::orientation() const +{ + return gtk_notebook_get_tab_pos(GTK_NOTEBOOK(widget)); +} + +void gTabStrip::destroyTab(int ind) +{ + delete (gTabStripPage *)g_ptr_array_index(_pages, ind); + g_ptr_array_remove_index(_pages, ind); +} + +bool gTabStrip::removeTab(int ind) +{ + gTabStripPage *page = get(ind); + + if (!page || tabCount(ind)) + return true; + + destroyTab(ind); + return false; +} + +bool gTabStrip::setCount(int vl) +{ + int i, ind; + + if (vl == count()) + return false; + + ind = index(); + + if (vl > count()) + { + lock(); + while (vl > count()) + g_ptr_array_add(_pages, (gpointer)new gTabStripPage(this)); + setIndex(count() - 1); + unlock(); + } + + if (vl < count()) + { + //GList *chd,*iter,*ok=NULL; + /*chd=gtk_container_get_children(GTK_CONTAINER(border)); + iter=chd; + while (iter) + { + if (gtk_notebook_page_num(GTK_NOTEBOOK(border),GTK_WIDGET(iter->data))>=vl) + { + ok=gtk_container_get_children(GTK_CONTAINER(iter->data)); + if (ok) break; + } + iter=g_list_next(iter); + } + g_list_free(chd); + if (ok) return -1;*/ + + for (i = vl; i < count(); i++) + { + if (tabCount(i)) + return true; + } + + lock(); + while (vl < count()) + removeTab(count() - 1); + unlock(); + } + + if (ind != index()) + emit(SIGNAL(onClick)); + + return false; +} + +void gTabStrip::setOrientation(int vl) +{ + gtk_notebook_set_tab_pos(GTK_NOTEBOOK(widget),GtkPositionType(vl)); +} + +gPicture* gTabStrip::tabPicture(int ind) const +{ + if ( (ind<0) || (ind>=count()) ) + return NULL; + else + return get(ind)->picture(); +} + +void gTabStrip::setTabPicture(int ind,gPicture *pic) +{ + if ( (ind<0) || (ind>=count()) ) return; + get(ind)->setPicture(pic); +} + +bool gTabStrip::tabEnabled(int ind) const +{ + if ( (ind<0) || (ind>=count()) ) + return FALSE; + else + return get(ind)->enabled(); +} + +void gTabStrip::setTabEnabled(int ind, bool vl) +{ + if ( (ind<0) || (ind>=count()) ) return; + get(ind)->setEnabled(vl); +} + +bool gTabStrip::tabVisible(int ind) const +{ + if ( (ind<0) || (ind>=count()) ) + return FALSE; + else + return get(ind)->isVisible(); +} + +void gTabStrip::setTabVisible(int ind, bool vl) +{ + if ( (ind<0) || (ind>=count()) ) return; + get(ind)->setVisible(vl); +} + +char* gTabStrip::tabText(int ind) const +{ + if ( (ind<0) || (ind>=count()) ) + return NULL; + else + return get(ind)->text(); +} + +void gTabStrip::setTabText(int ind, char *txt) +{ + if ( (ind<0) || (ind>=count()) ) return; + get(ind)->setText(txt); +} + +int gTabStrip::tabCount(int ind) const +{ + int i; + gControl *ch; + int ct = 0; + + if ((ind < 0) || (ind >= count())) + return 0; + + for (i = 0; i < gContainer::childCount(); i++) + { + ch = gContainer::child(i); + if (gtk_widget_get_parent(ch->border) == get(ind)->widget) + ct++; + } + + return ct; +} + +gControl *gTabStrip::tabChild(int ind, int n) const +{ + int i; + gControl *ch; + int ct = 0; + + if ((ind < 0) || (ind >= count())) + return NULL; + + for (i = 0; i < gContainer::childCount(); i++) + { + ch = gContainer::child(i); + if (gtk_widget_get_parent(ch->border) == get(ind)->widget) + { + if (ct == n) + return ch; + ct++; + } + } + + return NULL; +} + +int gTabStrip::childCount() const +{ + return tabCount(index()); +} + +gControl *gTabStrip::child(int ind) const +{ + return tabChild(index(), ind); +} + +GtkWidget *gTabStrip::getContainer() +{ + gTabStripPage *page = get(index()); + + if (page) + return page->widget; + else + return NULL; +} + +#ifdef GTK3 +void gTabStrip::updateColor() +{ + //fprintf(stderr, "%s: updateColors\n", name()); + gt_widget_set_color(border, false, realBackground()); + gt_widget_set_color(widget, false, realBackground()); + + for (int i = 0; i < count(); i++) + get(i)->updateColors(); +} +#else +void gTabStrip::setRealBackground(gColor color) +{ + gControl::setRealBackground(color); + + for (int i = 0; i < count(); i++) + get(i)->updateColors(); +} +#endif + +void gTabStrip::setRealForeground(gColor color) +{ + gControl::setRealForeground(color); + + for (int i = 0; i < count(); i++) + get(i)->updateColors(); +} + +gTabStripPage *gTabStrip::get(int ind) const +{ + if (ind < 0 || ind >= count()) + return NULL; + else + return (gTabStripPage *)g_ptr_array_index(_pages, ind); +} + +void gTabStrip::updateFont() +{ + int i; + + gContainer::updateFont(); + + for (i = 0; i < count(); i++) + get(i)->updateFont(); +} + +gFont *gTabStrip::textFont() +{ + return _textFont; +} + +void gTabStrip::setTextFont(gFont *font) +{ + gFont::assign(&_textFont, font); + updateFont(); +} + +void gTabStrip::setClosable(bool v) +{ + int i; + + if (v == _closable) + return; + + _closable = v; + + if (v) + { + if (!_button_normal) + { + GdkPixbuf *normal, *disabled; + + normal = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "window-close", 16, GTK_ICON_LOOKUP_USE_BUILTIN, NULL); + if (!normal) + { + normal = gdk_pixbuf_new_from_data(_close_button.pixel_data, GDK_COLORSPACE_RGB, TRUE, 8, + _close_button.width, _close_button.height, + _close_button.width * sizeof(int), NULL, NULL); + } + + disabled = gt_pixbuf_create_disabled(normal); + + _button_normal = gt_cairo_create_surface_from_pixbuf(normal); + _button_disabled = gt_cairo_create_surface_from_pixbuf(disabled); + + g_object_unref(normal); + g_object_unref(disabled); + + } + } + + for (i = 0; i < count(); i++) + get(i)->updateButton(); + +} + +int gTabStrip::findIndex(gControl *child) const +{ + int i; + GtkWidget *page; + + page = gtk_widget_get_parent(child->border); + + for (i = 0; i < count(); i++) + { + if (get(i)->widget == page) + return i; + } + + return -1; +} diff --git a/gb.gtk/src/gtabstrip.h b/gb.gtk/src/gtabstrip.h new file mode 100644 index 00000000..dcebd42a --- /dev/null +++ b/gb.gtk/src/gtabstrip.h @@ -0,0 +1,93 @@ +/*************************************************************************** + + gtabstrip.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GTABSTRIP_H +#define __GTABSTRIP_H + +class gTabStripPage; + +class gTabStrip : public gContainer +{ + friend class gTabStripPage; + +public: + gTabStrip(gContainer *parent); + ~gTabStrip(); + +//"Properties" + int count() const { return _pages->len; } + int index() const; + int orientation() const; + void setOrientation(int vl); + bool tabEnabled(int ind) const; + gPicture* tabPicture(int ind) const; + bool tabVisible(int ind) const; + char *tabText(int ind) const; + int tabCount(int ind) const; + gControl *tabChild(int ind, int n) const; + int findIndex(gControl *child) const; + + bool setCount(int vl); + void setIndex(int vl); + void setTabPicture(int ind, gPicture *pic); + void setTabEnabled(int ind, bool vl); + void setTabText(int ind, char *txt); + void setTabVisible(int ind, bool vl); + bool removeTab(int ind); + void setClosable(bool v); + bool isClosable() const { return _closable; } + + virtual int childCount() const; + virtual gControl *child(int index) const; + +#ifndef GTK3 + virtual void setRealBackground(gColor color); +#else + virtual void updateColor(); +#endif + virtual void setRealForeground(gColor color); + virtual void updateFont(); + + gFont *textFont(); + void setTextFont(gFont *ft); + + //"Events" + void (*onClick)(gTabStrip *sender); + void (*onClose)(gTabStrip *sender, int index); + +//"Private" + virtual GtkWidget *getContainer(); + int getRealIndex(GtkWidget *page) const; + + static cairo_surface_t *_button_normal; + static cairo_surface_t *_button_disabled; + +private: + bool _closable; + GPtrArray *_pages; + gFont *_textFont; + gTabStripPage *get(int ind) const; + void destroyTab(int ind); +}; + +#endif diff --git a/gb.gtk/src/gtag.h b/gb.gtk/src/gtag.h new file mode 100644 index 00000000..b576b0b7 --- /dev/null +++ b/gb.gtk/src/gtag.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + gtag.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GTAG_H +#define __GTAG_H + +class gTag +{ +public: + gTag() {} + gTag(void *v) { value = v; } + virtual ~gTag() {} + virtual void ref(void *v) {}; + virtual void unref(void *v) {}; + void *get() { return value; } + void ref() { ref(value); } + void unref() { unref(value); } +private: + void *value; +}; + +#endif diff --git a/gb.gtk/src/gtextarea.cpp b/gb.gtk/src/gtextarea.cpp new file mode 100644 index 00000000..0e1a2d90 --- /dev/null +++ b/gb.gtk/src/gtextarea.cpp @@ -0,0 +1,1081 @@ +/*************************************************************************** + + gtextarea.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gapplication.h" +#include "gclipboard.h" +#include "gdesktop.h" +#include "gkey.h" +#include "gtextarea.h" + +#ifdef GTK3 + +struct _GtkTextViewPrivate +{ + void *layout; + GtkTextBuffer *buffer; + + guint blink_time; /* time in msec the cursor has blinked since last user event */ + guint im_spot_idle; + gchar *im_module; + GdkDevice *grab_device; + GdkDevice *dnd_device; + + gulong selection_drag_handler; + void *text_handle; + GtkWidget *selection_bubble; + guint selection_bubble_timeout_id; + + GtkWidget *magnifier_popover; + GtkWidget *magnifier; + + void *text_window; + void *left_window; + void *right_window; + void *top_window; + void *bottom_window; + + GtkAdjustment *hadjustment; + GtkAdjustment *vadjustment; + + gint xoffset; /* Offsets between widget coordinates and buffer coordinates */ + gint yoffset; + gint width; /* Width and height of the buffer */ + gint height; + + /* This is used to monitor the overall size request + * and decide whether we need to queue resizes when + * the buffer content changes. + * + * FIXME: This could be done in a simpler way by + * consulting the above width/height of the buffer + some + * padding values, however all of this request code needs + * to be changed to use GtkWidget Iface and deserves + * more attention. + */ + GtkRequisition cached_size_request; + + /* The virtual cursor position is normally the same as the + * actual (strong) cursor position, except in two circumstances: + * + * a) When the cursor is moved vertically with the keyboard + * b) When the text view is scrolled with the keyboard + * + * In case a), virtual_cursor_x is preserved, but not virtual_cursor_y + * In case b), both virtual_cursor_x and virtual_cursor_y are preserved. + */ + gint virtual_cursor_x; /* -1 means use actual cursor position */ + gint virtual_cursor_y; /* -1 means use actual cursor position */ + + GtkTextMark *first_para_mark; /* Mark at the beginning of the first onscreen paragraph */ + gint first_para_pixels; /* Offset of top of screen in the first onscreen paragraph */ + + guint blink_timeout; + guint scroll_timeout; + + guint first_validate_idle; /* Idle to revalidate onscreen portion, runs before resize */ + guint incremental_validate_idle; /* Idle to revalidate offscreen portions, runs after redraw */ + + gint pending_place_cursor_button; + + GtkTextMark *dnd_mark; + + GtkIMContext *im_context; + GtkWidget *popup_menu; + + gint drag_start_x; + gint drag_start_y; + + GSList *children; + + void *pending_scroll; + + void *pixel_cache; + + /* Default style settings */ + gint pixels_above_lines; + gint pixels_below_lines; + gint pixels_inside_wrap; + GtkWrapMode wrap_mode; + GtkJustification justify; + gint left_margin; + gint right_margin; + gint indent; + PangoTabArray *tabs; + guint editable : 1; + + guint overwrite_mode : 1; + guint cursor_visible : 1; + + /* if we have reset the IM since the last character entered */ + guint need_im_reset : 1; + + guint accepts_tab : 1; + + guint width_changed : 1; + + /* debug flag - means that we've validated onscreen since the + * last "invalidate" signal from the layout + */ + guint onscreen_validated : 1; + + guint mouse_cursor_obscured : 1; + + guint scroll_after_paste : 1; + + /* GtkScrollablePolicy needs to be checked when + * driving the scrollable adjustment values */ + guint hscroll_policy : 1; + guint vscroll_policy : 1; + guint cursor_handle_dragged : 1; + guint selection_handle_dragged : 1; + guint populate_all : 1; + + guint in_scroll : 1; +}; + +#else + +// Private structure took from GTK+ 2.10 code. Used for setting the text area cursor. +// **** May not work on future GTK+ versions **** + +struct PrivateGtkTextWindow +{ + GtkTextWindowType type; + GtkWidget *widget; + GdkWindow *window; + GdkWindow *bin_window; + GtkRequisition requisition; + GdkRectangle allocation; +}; + +// Private exported functions to get the size of the text +// **** May not work on future GTK+ versions **** + +extern "C" { +void gtk_text_layout_set_screen_width(struct _GtkTextLayout *layout, gint width); +void gtk_text_layout_get_size(struct _GtkTextLayout *layout, gint *width, gint *height); +void gtk_text_layout_invalidate (struct _GtkTextLayout *layout, const GtkTextIter *start, const GtkTextIter *end); +void gtk_text_layout_validate (struct _GtkTextLayout *layout, gint max_pixels); +} +#endif + +#if !GLIB_CHECK_VERSION(2, 32, 0) +/** + * g_signal_handlers_disconnect_by_data: + * @instance: The instance to remove handlers from + * @data: the closure data of the handlers' closures + * + * Disconnects all handlers on an instance that match @data. + * + * Returns: The number of handlers that matched. + * + * Since: 2.32 + */ +#define g_signal_handlers_disconnect_by_data(instance, data) \ + g_signal_handlers_disconnect_matched ((instance), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, (data)) +#endif + +// Undo/Redo actions + +enum { ACTION_VOID, ACTION_INSERT, ACTION_DELETE }; + +class gTextAreaAction +{ +public: + gTextAreaAction *prev; + gTextAreaAction *next; + GString *text; + int length; + int start; + int end; + unsigned mergeable : 1; + unsigned delete_key_used : 1; + unsigned type : 2; + + gTextAreaAction(); + ~gTextAreaAction(); + static gTextAreaAction *insertAction(GtkTextBuffer *buffer, char *text, int length, GtkTextIter *where); + static gTextAreaAction *deleteAction(GtkTextBuffer *buffer, GtkTextIter *start, GtkTextIter *end); + bool canBeMerged(gTextAreaAction *prev); + void addText(char *text, int length); +}; + +gTextAreaAction::gTextAreaAction() +{ + prev = next = NULL; + text = NULL; + length = start = end = 0; + mergeable = delete_key_used = false; + type = ACTION_VOID; +} + +gTextAreaAction::~gTextAreaAction() +{ + if (text) + g_string_free(text, TRUE); +} + +gTextAreaAction *gTextAreaAction::insertAction(GtkTextBuffer *buffer, char *text, int len, GtkTextIter *where) +{ + gTextAreaAction *action = new gTextAreaAction; + + action->type = ACTION_INSERT; + action->start = gtk_text_iter_get_offset(where); + action->text = g_string_new_len(text, len); + action->length = g_utf8_strlen(text, len); + + action->mergeable = (len == 1) && *text != '\r' && *text != '\n' && *text != ' '; + + return action; +} + +gTextAreaAction *gTextAreaAction::deleteAction(GtkTextBuffer *buffer, GtkTextIter *start, GtkTextIter *end) +{ + GtkTextIter insert_iter; + int insert_offset; + GtkTextMark *mark; + gTextAreaAction *action = new gTextAreaAction; + char *text; + + action->type = ACTION_DELETE; + text = gtk_text_buffer_get_text(buffer, start, end, FALSE); + action->text = g_string_new(text); + action->length = g_utf8_strlen(action->text->str, action->text->len); + g_free(text); + action->start = gtk_text_iter_get_offset(start); + action->end = gtk_text_iter_get_offset(end); + + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &insert_iter, mark); + insert_offset = gtk_text_iter_get_offset(&insert_iter); + + action->delete_key_used = insert_offset < action->start; + + action->mergeable = (action->length == 1) && *(action->text->str) != '\r' && *(action->text->str) != '\n' && *(action->text->str) != ' '; + + return action; +} + +bool gTextAreaAction::canBeMerged(gTextAreaAction *prev) +{ + if (!prev || prev->type != type) + return false; + + if (!mergeable || !prev->mergeable) + return false; + + if (type == ACTION_INSERT) + { + if (start != (prev->start + prev->length)) + return false; + if (isspace(*text->str) != isspace(*prev->text->str)) + return false; + } + else if (type == ACTION_DELETE) + { + if (prev->delete_key_used != delete_key_used) + return false; + if (prev->start != start && prev->start != end) + return false; + if (isspace(*text->str) != isspace(*prev->text->str)) + return false; + } + else + return false; + + return true; +} + +void gTextAreaAction::addText(char *add, int len) +{ + g_string_append_len(text, add, len); + length += g_utf8_strlen(add, len); +} + + +// Callbacks + +/*static gboolean cb_motion_notify_event(GtkWidget *widget, GdkEventMotion *event, gTextArea *data) +{ + // Workaround GtkTextView internal cursor changes + if (gApplication::isBusy()) + data->setMouse(data->mouse()); + return FALSE; +}*/ + +static void cb_changed(GtkTextBuffer *buf, gTextArea *data) +{ + data->emit(SIGNAL(data->onChange)); +} + +static void cb_mark_set(GtkTextBuffer *buf, GtkTextIter *location, GtkTextMark *mark, gTextArea *data) +{ + //if (mark == gtk_text_buffer_get_insert(buf)) + data->emitCursor(); +} + +static void cb_insert_text(GtkTextBuffer *buf, GtkTextIter *location, gchar *text, gint len, gTextArea *ctrl) +{ + gTextAreaAction *action, *prev; + + if (gKey::gotCommit()) + { + gcb_im_commit(NULL, text, NULL); + if (gKey::canceled()) + { + g_signal_stop_emission_by_name(G_OBJECT(buf), "insert-text"); + return; + } + } + + if (!ctrl->_undo_in_progress) + ctrl->clearRedoStack(); + + if (ctrl->_not_undoable_action) + return; + + action = gTextAreaAction::insertAction(buf, text, len, location); + + prev = ctrl->_undo_stack; + if (action->canBeMerged(prev)) + { + prev->addText(action->text->str, action->length); + //fprintf(stderr, "insert merge: %d '%.*s'\n", prev->start, prev->text->len, prev->text->str); + delete action; + } + else + { + //fprintf(stderr, "insert: %d '%.*s'\n", action->start, action->text->len, action->text->str); + action->next = ctrl->_undo_stack; + if (ctrl->_undo_stack) + ctrl->_undo_stack->prev = action; + ctrl->_undo_stack = action; + } +} + +static void cb_delete_range(GtkTextBuffer *buf, GtkTextIter *start, GtkTextIter *end, gTextArea *ctrl) +{ + gTextAreaAction *action, *prev; + + if (!ctrl->_undo_in_progress) + ctrl->clearRedoStack(); + + if (ctrl->_not_undoable_action) + return; + + action = gTextAreaAction::deleteAction(buf, start, end); + + prev = ctrl->_undo_stack; + if (action->canBeMerged(prev)) + { + if (prev->start == action->start) + { + prev->addText(action->text->str, action->length); + prev->end += action->end - action->start; + } + else + { + GString *text = prev->text; + prev->text = action->text; + action->text = NULL; + prev->addText(text->str, text->len); + g_string_free(text, TRUE); + prev->start = action->start; + } + //fprintf(stderr, "delete merge: %d %d '%.*s'\n", prev->start, prev->end, prev->text->len, prev->text->str); + delete action; + } + else + { + //fprintf(stderr, "delete: %d %d '%.*s'\n", action->start, action->end, action->text->len, action->text->str); + action->next = ctrl->_undo_stack; + if (ctrl->_undo_stack) + ctrl->_undo_stack->prev = action; + ctrl->_undo_stack = action; + } +} + +static gboolean cb_keypress(GtkWidget *widget, GdkEvent *event, gTextArea *ctrl) +{ + if (event->key.state & GDK_CONTROL_MASK) + { + int key = gdk_keyval_to_unicode(gdk_keyval_to_upper(event->key.keyval)); + + if (!ctrl->readOnly()) + { + if (key == 'Z') + { + ctrl->undo(); + return true; + } + else if (key == 'Y') + { + ctrl->redo(); + return true; + } + else if (key == 'X') + { + ctrl->cut(); + ctrl->ensureVisible(); + return true; + } + else if (key == 'V') + { + ctrl->paste(); + ctrl->ensureVisible(); + return true; + } + } + + if (key == 'A') + { + ctrl->selectAll(); + return true; + } + else if (key == 'C') + { + ctrl->copy(); + return true; + } + } + + return false; +} + +// TextArea + +gTextArea::gTextArea(gContainer *parent) : gControl(parent) +{ + g_typ = Type_gTextArea; + _align_normal = false; + _last_pos = -1; + + have_cursor = true; + _undo_stack = NULL; + _redo_stack = NULL; + _not_undoable_action = 0; + _undo_in_progress = false; + _has_input_method = true; + _use_wheel = true; + + onChange = 0; + onCursor = 0; + + textview = gtk_text_view_new(); + realizeScrolledWindow(textview); + + setColorBase(); + + //g_signal_connect_after(G_OBJECT(textview), "motion-notify-event", G_CALLBACK(cb_motion_notify_event), (gpointer)this); + g_signal_connect(G_OBJECT(textview), "key-press-event", G_CALLBACK(cb_keypress), (gpointer)this); + + _buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)); + g_signal_connect_after(G_OBJECT(_buffer), "changed", G_CALLBACK(cb_changed), (gpointer)this); + g_signal_connect_after(G_OBJECT(_buffer), "mark-set", G_CALLBACK(cb_mark_set), (gpointer)this); + g_signal_connect(G_OBJECT(_buffer), "insert-text", G_CALLBACK(cb_insert_text), (gpointer)this); + g_signal_connect(G_OBJECT(_buffer), "delete-range", G_CALLBACK(cb_delete_range), (gpointer)this); + + /*gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 2); + gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 2);*/ + + setBorder(true); + setWrap(false); +} + +void gTextArea::clearUndoStack() +{ + gTextAreaAction *action; + + while (_undo_stack) + { + action = _undo_stack; + _undo_stack = _undo_stack->next; + delete action; + } +} + +void gTextArea::clearRedoStack() +{ + gTextAreaAction *action; + + while (_redo_stack) + { + action = _redo_stack; + _redo_stack = _redo_stack->next; + delete action; + } +} + +gTextArea::~gTextArea() +{ + g_signal_handlers_disconnect_by_data(gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)), this); + clearRedoStack(); + clearUndoStack(); +} + +char *gTextArea::text() +{ + GtkTextIter start; + GtkTextIter end; + char *text; + + gtk_text_buffer_get_bounds(_buffer, &start, &end); + + text = gtk_text_buffer_get_text(_buffer, &start, &end, true); + gt_free_later(text); + return text; +} + +void gTextArea::setText(const char *txt, int len) +{ + if (!txt) + { + txt = ""; + len = 0; + } + + _last_pos = -1; + begin(); + gtk_text_buffer_set_text(_buffer, (const gchar *)txt, len); + end(); +} + +bool gTextArea::readOnly() +{ + return !gtk_text_view_get_editable(GTK_TEXT_VIEW(textview)); +} + +void gTextArea::setReadOnly(bool vl) +{ + gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), !vl); + gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(textview), !vl); +} + +GtkTextIter *gTextArea::getIterAt(int pos) +{ + static GtkTextIter iter; + + if (pos < 0) + gtk_text_buffer_get_iter_at_mark(_buffer, &iter, gtk_text_buffer_get_insert(_buffer)); + else + gtk_text_buffer_get_iter_at_offset(_buffer, &iter, pos); + + return &iter; +} + +int gTextArea::line() +{ + return gtk_text_iter_get_line(getIterAt()); +} + +void gTextArea::setLine(int vl) +{ + int col = column(); + GtkTextIter *iter = getIterAt(); + + if (vl < 0) + { + setPosition(0); + return; + } + else if (vl >= gtk_text_buffer_get_line_count(_buffer)) + { + setPosition(length()); + return; + } + + gtk_text_iter_set_line(iter, vl); + if (gtk_text_iter_get_chars_in_line(iter) <= col) + col = gtk_text_iter_get_chars_in_line(iter) - 1; + gtk_text_iter_set_line_offset(iter, col); + gtk_text_buffer_place_cursor(_buffer, iter); + ensureVisible(); +} + +int gTextArea::column() +{ + return gtk_text_iter_get_line_offset(getIterAt()); +} + +void gTextArea::setColumn(int vl) +{ + GtkTextIter *iter = getIterAt(); + + if (vl < 0) + { + vl = gtk_text_iter_get_chars_in_line(iter)-1; + } + else + { + if (gtk_text_iter_get_chars_in_line (iter) <= vl) + vl = gtk_text_iter_get_chars_in_line(iter) - 1; + } + + gtk_text_iter_set_line_offset(iter,vl); + gtk_text_buffer_place_cursor(_buffer, iter); + ensureVisible(); +} + +int gTextArea::position() +{ + return gtk_text_iter_get_offset(getIterAt()); +} + +void gTextArea::setPosition(int vl) +{ + GtkTextIter *iter = getIterAt(); + + gtk_text_iter_set_offset(iter, vl); + gtk_text_buffer_place_cursor(_buffer, iter); + ensureVisible(); +} + +int gTextArea::length() +{ + GtkTextIter iter; + + gtk_text_buffer_get_end_iter(_buffer, &iter); + return gtk_text_iter_get_offset(&iter); +} + +bool gTextArea::wrap() +{ + return (gtk_text_view_get_wrap_mode(GTK_TEXT_VIEW(textview)) != GTK_WRAP_NONE); +} + +void gTextArea::setWrap(bool vl) +{ + if (vl) + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview), GTK_WRAP_WORD_CHAR); + else + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview), GTK_WRAP_NONE); +} + +/********************************************************************************** + +gTextArea methods + +***********************************************************************************/ + +void gTextArea::copy() +{ + gtk_text_buffer_copy_clipboard(_buffer, gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); +} + +void gTextArea::cut() +{ + gtk_text_buffer_cut_clipboard(_buffer, gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), true); +} + +void gTextArea::ensureVisible() +{ + gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(textview), gtk_text_buffer_get_insert(_buffer)); +} + +void gTextArea::paste() +{ + char *txt; + int len; + + if (gClipboard::getType() != gClipboard::Text) + return; + + txt = gClipboard::getText(&len, "text/plain"); + if (txt) + gtk_text_buffer_insert_at_cursor(_buffer, (const gchar *)txt, len); +} + +void gTextArea::insert(const char *txt) +{ + if (!txt || !*txt) + return; + + gtk_text_buffer_insert_at_cursor(_buffer, (const gchar *)txt, -1); +} + +int gTextArea::toLine(int pos) +{ + if (pos < 0) + pos=0; + else if (pos > length()) + pos = length(); + + return gtk_text_iter_get_line(getIterAt(pos)); +} + +int gTextArea::toColumn(int pos) +{ + if (pos < 0) + pos=0; + else if (pos > length()) + pos = length(); + + return gtk_text_iter_get_line_offset(getIterAt(pos)); +} + +int gTextArea::toPosition(int line, int col) +{ + GtkTextIter iter; + int lm, cm; + + if (line < 0) line = 0; + if (col < 0) col = 0; + + lm = gtk_text_buffer_get_line_count(_buffer) - 1; + if (line >= lm) + line = lm; + + gtk_text_buffer_get_start_iter(_buffer, &iter); + gtk_text_iter_set_line(&iter, line); + + cm = gtk_text_iter_get_chars_in_line(&iter); + if (line < lm) + cm--; + + if (col > cm) + col = cm; + + gtk_text_iter_set_line_offset(&iter, col); + + return gtk_text_iter_get_offset(&iter); +} + + +/********************************************************************************** + +gTextArea selection + +***********************************************************************************/ + +bool gTextArea::isSelected() +{ + return gtk_text_buffer_get_selection_bounds(_buffer, NULL, NULL); + //return gtk_text_buffer_get_has_selection(buf); // Only since 2.10 +} + +int gTextArea::selStart() +{ + GtkTextIter start, end; + + gtk_text_buffer_get_selection_bounds(_buffer, &start, &end); + return gtk_text_iter_get_offset(&start); +} + +int gTextArea::selEnd() +{ + GtkTextIter start, end; + + gtk_text_buffer_get_selection_bounds(_buffer, &start, &end); + return gtk_text_iter_get_offset(&end); +} + +char *gTextArea::selText() +{ + GtkTextIter start, end; + char *text; + + gtk_text_buffer_get_selection_bounds(_buffer, &start, &end); + text = gtk_text_buffer_get_text(_buffer, &start, &end, true); + gt_free_later(text); + return text; +} + +void gTextArea::setSelText(const char *vl) +{ + GtkTextIter start, end; + + if (!vl) + vl = ""; + + if (gtk_text_buffer_get_selection_bounds(_buffer, &start, &end)) + gtk_text_buffer_delete(_buffer, &start, &end); + + gtk_text_buffer_insert(_buffer, &start, vl, -1); +} + +void gTextArea::selDelete() +{ + GtkTextIter start, end; + + if (gtk_text_buffer_get_selection_bounds(_buffer, &start, &end)) + { + gtk_text_iter_set_offset(&end, gtk_text_iter_get_offset(&start)); + gtk_text_buffer_select_range(_buffer, &start, &end); + } +} + +void gTextArea::selSelect(int pos, int length) +{ + GtkTextIter start, end; + + gtk_text_buffer_get_end_iter(_buffer, &start); + + if (gtk_text_iter_get_offset(&start) < pos) + pos = gtk_text_iter_get_offset(&start); + + if (pos < 0) + { + length -= pos; + pos = 0; + } + + if ((pos + length) < 0) + length = (-pos); + + gtk_text_buffer_get_selection_bounds(_buffer, &start, &end); + gtk_text_iter_set_offset(&start, pos); + gtk_text_iter_set_offset(&end, pos + length); + gtk_text_buffer_select_range(_buffer, &start, &end); +} + +void gTextArea::updateCursor(GdkCursor *cursor) +{ + GdkWindow *win; + +#ifdef GTK3 + win = gtk_text_view_get_window(GTK_TEXT_VIEW(textview), GTK_TEXT_WINDOW_TEXT); +#else + win = ((PrivateGtkTextWindow *)GTK_TEXT_VIEW(textview)->text_window)->bin_window; +#endif + + gControl::updateCursor(cursor); + + if (!win) + return; + + if (cursor) + gdk_window_set_cursor(win, cursor); + else + { + cursor = gdk_cursor_new_for_display(gtk_widget_get_display(textview), GDK_XTERM); + gdk_window_set_cursor(win, cursor); +#ifdef GTK3 + g_object_unref(cursor); +#else + gdk_cursor_unref(cursor); +#endif + } +} + +/*void gTextArea::waitForLayout(int *tw, int *th) +{ + GtkTextIter start; + GtkTextIter end; + gint w, h; + + gtk_text_layout_set_screen_width(GTK_TEXT_VIEW(textview)->layout, width()); + + gtk_text_buffer_get_bounds (_buffer, &start, &end); + gtk_text_layout_invalidate (GTK_TEXT_VIEW(textview)->layout, &start, &end); + gtk_text_layout_validate(GTK_TEXT_VIEW(textview)->layout, 0x7FFFFFFF); + gtk_text_layout_get_size(GTK_TEXT_VIEW(textview)->layout, &w, &h); + + *tw = w; + *th = h; +} + +int gTextArea::textWidth() +{ + int w, h; + waitForLayout(&w, &h); + return w; +} + +int gTextArea::textHeight() +{ + int w, h; + waitForLayout(&w, &h); + return h; +}*/ + +int gTextArea::alignment() const +{ + if (_align_normal) + return ALIGN_NORMAL; + + switch(gtk_text_view_get_justification(GTK_TEXT_VIEW(textview))) + { + case GTK_JUSTIFY_RIGHT: return ALIGN_RIGHT; + case GTK_JUSTIFY_CENTER: return ALIGN_CENTER; + case GTK_JUSTIFY_LEFT: default: return ALIGN_LEFT; + } +} + +void gTextArea::setAlignment(int vl) +{ + GtkJustification align; + + _align_normal = false; + + switch(vl & 0xF) + { + case ALIGN_LEFT: align = GTK_JUSTIFY_LEFT; break; + case ALIGN_RIGHT: align = GTK_JUSTIFY_RIGHT; break; + case ALIGN_CENTER: align = GTK_JUSTIFY_CENTER; break; + case ALIGN_NORMAL: default: align = gDesktop::rightToLeft() ? GTK_JUSTIFY_RIGHT: GTK_JUSTIFY_LEFT; _align_normal = true; break; + } + + gtk_text_view_set_justification(GTK_TEXT_VIEW(textview), align); +} + +void gTextArea::undo() +{ + gTextAreaAction *action; + GtkTextIter start, stop; + + if (!_undo_stack) + return; + + begin(); + _undo_in_progress = true; + + action = _undo_stack; + _undo_stack = _undo_stack->next; + + action->prev = NULL; + action->next = _redo_stack; + if (_redo_stack) + _redo_stack->prev = action; + _redo_stack = action; + + if (action->type == ACTION_INSERT) + { + gtk_text_buffer_get_iter_at_offset(_buffer, &start, action->start); + gtk_text_buffer_get_iter_at_offset(_buffer, &stop, action->start + action->length); + gtk_text_buffer_delete(_buffer, &start, &stop); + gtk_text_buffer_place_cursor(_buffer, &start); + } + else if (action->type == ACTION_DELETE) + { + gtk_text_buffer_get_iter_at_offset(_buffer, &start, action->start); + gtk_text_buffer_insert(_buffer, &start, action->text->str, action->text->len); + gtk_text_buffer_get_iter_at_offset(_buffer, &stop, action->end); + if (action->delete_key_used) + gtk_text_buffer_place_cursor(_buffer, &start); + else + gtk_text_buffer_place_cursor(_buffer, &stop); + } + + end(); + ensureVisible(); + _undo_in_progress = false; +} + +void gTextArea::redo() +{ + gTextAreaAction *action; + GtkTextIter start, stop; + + if (!_redo_stack) + return; + + begin(); + _undo_in_progress = true; + + action = _redo_stack; + _redo_stack = _redo_stack->next; + + action->prev = NULL; + action->next = _undo_stack; + if (_undo_stack) + _undo_stack->prev = action; + _undo_stack = action; + + if (action->type == ACTION_INSERT) + { + gtk_text_buffer_get_iter_at_offset(_buffer, &start, action->start); + gtk_text_buffer_insert(_buffer, &start, action->text->str, action->text->len); + gtk_text_buffer_get_iter_at_offset(_buffer, &start, action->start + action->length); + gtk_text_buffer_place_cursor(_buffer, &start); + } + else if (action->type == ACTION_DELETE) + { + gtk_text_buffer_get_iter_at_offset(_buffer, &start, action->start); + gtk_text_buffer_get_iter_at_offset(_buffer, &stop, action->end); + gtk_text_buffer_delete(_buffer, &start, &stop); + gtk_text_buffer_place_cursor(_buffer, &start); + } + + end(); + ensureVisible(); + _undo_in_progress = false; +} + +void gTextArea::clear() +{ + begin(); + setText(""); + clearUndoStack(); + clearRedoStack(); + end(); +} + +GtkIMContext *gTextArea::getInputMethod() +{ +#ifdef GTK3 + return GTK_TEXT_VIEW(widget)->priv->im_context; +#else + return GTK_TEXT_VIEW(widget)->im_context; +#endif +} + +#ifdef GTK3 +GtkWidget *gTextArea::getStyleSheetWidget() +{ + return textview; +} + +int gTextArea::minimumWidth() const +{ + return gDesktop::scale() * 4; +} + +int gTextArea::minimumHeight() const +{ + return gDesktop::scale() * 8; +} +#endif + +void gTextArea::getCursorPos(int *x, int *y, int pos) +{ + GdkRectangle rect; + int f = getFrameWidth(); + GtkTextIter *iter = getIterAt(pos); + + gtk_text_view_get_iter_location(GTK_TEXT_VIEW(widget), iter, &rect); + gtk_text_view_buffer_to_window_coords(GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_WIDGET, rect.x, rect.y + rect.height, x, y); + *x += f; + *y += f; +} + +void gTextArea::emitCursor() +{ + int pos = position(); + + if (pos == _last_pos) + return; + + _last_pos = pos; + emit(SIGNAL(onCursor)); +} diff --git a/gb.gtk/src/gtextarea.h b/gb.gtk/src/gtextarea.h new file mode 100644 index 00000000..0c54eeea --- /dev/null +++ b/gb.gtk/src/gtextarea.h @@ -0,0 +1,122 @@ +/*************************************************************************** + + gtextarea.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GTEXTAREA_H +#define __GTEXTAREA_H + +class gTextAreaAction; + +class gTextArea : public gControl +{ +public: + gTextArea(gContainer *parent); + ~gTextArea(); + +//"Properties" + int column(); + int length(); + int line(); + int position(); + bool readOnly(); + char* text(); + bool wrap(); + bool isSelected(); + + void setColumn(int vl); + void setLine(int vl); + void setPosition(int vl); + void setReadOnly(bool vl); + void setText(const char *txt, int len = -1); + void setWrap(bool vl); + + //int textWidth(); + //int textHeight(); + + int alignment() const; + void setAlignment(int vl); + +//"Methods" + void copy(); + void cut(); + void ensureVisible(); + void paste(); + void insert(const char *txt); + int toLine(int pos); + int toColumn(int pos); + int toPosition(int line, int col); + +//"Selection properties" + int selStart(); + int selEnd(); + char* selText(); + + void setSelText(const char *vl); + +//"Selection methods" + void selDelete(); + void selSelect(int pos, int length); + void selectAll() { selSelect(0, length()); } + + bool canUndo() const { return _undo_stack != 0; } + bool canRedo() const { return _redo_stack != 0; } + void begin() { _not_undoable_action++; } + void end() { _not_undoable_action--; } + void undo(); + void redo(); + void clear(); + + void getCursorPos(int *x, int *y, int pos); + + void emitCursor(); + +//"Signals" + void (*onChange)(gTextArea *sender); + void (*onCursor)(gTextArea *sender); + +//"Private" + virtual void updateCursor(GdkCursor *cursor); +#ifdef GTK3 + virtual GtkWidget *getStyleSheetWidget(); + virtual int minimumWidth() const; + virtual int minimumHeight() const; +#endif + virtual GtkIMContext *getInputMethod(); + //void waitForLayout(int *tw, int *th); + void clearUndoStack(); + void clearRedoStack(); + + gTextAreaAction *_undo_stack; + gTextAreaAction *_redo_stack; + int _not_undoable_action; + bool _undo_in_progress; + +private: + GtkWidget *textview; + GtkTextBuffer *_buffer; + bool _align_normal; + int _last_pos; + + GtkTextIter *getIterAt(int pos = -1); +}; + +#endif diff --git a/gb.gtk/src/gtextbox.cpp b/gb.gtk/src/gtextbox.cpp new file mode 100644 index 00000000..5810259b --- /dev/null +++ b/gb.gtk/src/gtextbox.cpp @@ -0,0 +1,517 @@ +/*************************************************************************** + + gtextbox.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gapplication.h" +#include "gkey.h" +#include "gtextbox.h" + +#ifdef GTK3 + +#define MAX_ICONS 2 + +#if GTK_CHECK_VERSION(3,14,0) + +struct _GtkEntryPrivate +{ + void *icons[MAX_ICONS]; + + GtkEntryBuffer *buffer; + GtkIMContext *im_context; + GtkWidget *popup_menu; + + GdkWindow *text_area; +}; + +#else + +struct _GtkEntryPrivate +{ + void *icons[MAX_ICONS]; + + GtkEntryBuffer *buffer; + GtkIMContext *im_context; + GtkWidget *popup_menu; + + GdkDevice *device; + + GdkWindow *text_area; +}; + +#endif + +#endif + +#ifdef GTK3 +GtkCssProvider *gTextBox::_style_provider = NULL; +#endif + +static gboolean raise_change(gTextBox *data) +{ + if (data->_changed) + { + data->emit(SIGNAL(data->onChange)); + data->_changed = false; + } + return FALSE; +} + +static void cb_before_insert(GtkEditable *editable, gchar *new_text, gint new_text_length, gint *position, gTextBox *data) +{ + if (gKey::gotCommit()) + { + gcb_im_commit(NULL, new_text, NULL); + if (gKey::canceled()) + g_signal_stop_emission_by_name (G_OBJECT(editable), "insert-text"); + *position = gtk_editable_get_position(editable); + } +} + +static void cb_change_insert(GtkEditable *editable, gchar *new_text, gint new_text_length, gint *position, gTextBox *data) +{ + data->_changed = false; + gtk_editable_set_position(editable, *position); + data->emit(SIGNAL(data->onChange)); + *position = gtk_editable_get_position(editable); +} + +static void cb_change_delete(GtkEditable *editable, gint start_pos, gint end_pos, gTextBox *data) +{ + if (!data->_changed) + { + data->_changed = true; + g_timeout_add(0, (GSourceFunc)raise_change, data); + } +} + +static void cb_activate(GtkEntry *editable,gTextBox *data) +{ + data->emit(SIGNAL(data->onActivate)); +} + + +gTextBox::gTextBox(gContainer *parent, bool combo) : gControl(parent) +{ +#ifdef GTK3 + if (!_style_provider) + { + const char *css; + + _style_provider = gtk_css_provider_new(); + + /*if (strcmp(gApplication::getStyleName(), "Clearlooks-Phenix") == 0) + css = ".entry { border-width: 0; padding: 0; border-radius: 0; margin: 0; border-style: none; box-shadow: none; background-image: none; }"; + else*/ + css = "* { border: none; border-radius: 0; margin: 0; padding: 0; box-shadow: none; }"; + + gtk_css_provider_load_from_data(_style_provider, css, -1, NULL); + } + + g_object_ref(_style_provider); +#endif + + if (!combo) + { + g_typ=Type_gTextBox; + + have_cursor = true; + _no_background = TRUE; + + entry = widget = gtk_entry_new(); + realize(); + setColorBase(); + initEntry(); + } + + _changed = false; + _border = true; + + onChange = NULL; + onActivate = NULL; +} + +gTextBox::~gTextBox() +{ +#ifdef GTK3 + g_object_unref(_style_provider); +#endif +} + +/*static void cb_im_commit(GtkIMContext *context, const char *str, gpointer pointer) +{ + fprintf(stderr, "cb_im_commit: %s\n", str); +}*/ + +void gTextBox::initEntry() +{ + _has_input_method = entry != NULL; + + if (!entry) + return; + + // This imitates the QT behaviour, where change signal is raised after the cursor has been moved. + g_signal_connect(G_OBJECT(entry), "insert-text", G_CALLBACK(cb_before_insert), (gpointer)this); + g_signal_connect_after(G_OBJECT(entry), "insert-text", G_CALLBACK(cb_change_insert), (gpointer)this); + g_signal_connect_after(G_OBJECT(entry), "delete-text", G_CALLBACK(cb_change_delete), (gpointer)this); + g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(cb_activate), (gpointer)this); + //g_signal_connect(getInputMethod(), "commit", G_CALLBACK(cb_im_commit), (gpointer)this); +} + +char* gTextBox::text() +{ + return (char*)gtk_entry_get_text(GTK_ENTRY(entry)); +} + +void gTextBox::setText(const char *vl) +{ + if (!vl) vl = ""; + + if (!entry || !strcmp(vl, text())) + return; + + lock(); + gtk_entry_set_text(GTK_ENTRY(entry), vl); + gtk_editable_set_position(GTK_EDITABLE(entry), -1); + unlock(); + emit(SIGNAL(onChange)); +} + +bool gTextBox::password() +{ + if (entry) + return !gtk_entry_get_visibility(GTK_ENTRY(entry)); + else + return false; +} + +void gTextBox::setPassword(bool vl) +{ + if (!entry) + return; + + gtk_entry_set_visibility(GTK_ENTRY(entry),!vl); + if (vl) + gtk_entry_set_invisible_char(GTK_ENTRY(entry), (gunichar)0x25CF); +} + +bool gTextBox::isReadOnly() +{ + return !gtk_editable_get_editable(GTK_EDITABLE(entry)); +} + +void gTextBox::setReadOnly(bool vl) +{ + gtk_editable_set_editable(GTK_EDITABLE(entry),!vl); +} + +int gTextBox::position() +{ + if (entry) + return gtk_editable_get_position(GTK_EDITABLE(entry)); + else + return 0; +} + +void gTextBox::setPosition(int pos) +{ + int len; + + if (!entry) + return; + + len = length(); + + if (pos < 0) + pos = 0; + else if (pos > len) + pos = -1; + + gtk_editable_set_position(GTK_EDITABLE(entry), pos); +} + +bool gTextBox::hasBorder() +{ + if (entry) + return _border; //gtk_entry_get_has_frame(GTK_ENTRY(entry)); + else + return true; +} + +void gTextBox::setBorder(bool vl) +{ + if (!entry) + return; + + if (vl == hasBorder()) + return; + + _border = vl; + + gtk_entry_set_has_frame(GTK_ENTRY(entry), vl); +#ifdef GTK3 + GtkStyleContext *style = gtk_widget_get_style_context(entry); + if (vl) + gtk_style_context_remove_provider(style, GTK_STYLE_PROVIDER(_style_provider)); + else + gtk_style_context_add_provider(style, GTK_STYLE_PROVIDER(_style_provider), GTK_STYLE_PROVIDER_PRIORITY_USER); + + //gtk_style_context_invalidate(style); +#endif + + /*if (vl) + gtk_entry_set_inner_border(GTK_ENTRY(entry), NULL); + else + { + GtkBorder *border = gtk_border_new(); + border->left = border->right = 2; + border->top = 1; + gtk_entry_set_inner_border(GTK_ENTRY(entry), border); + gtk_border_free(border); + }*/ +} + +void gTextBox::insert(char *txt, int len) +{ + if (!entry || !len || !txt) return; + + lock(); + gtk_editable_delete_selection(GTK_EDITABLE(entry)); + unlock(); + int pos = position(); + gtk_editable_insert_text(GTK_EDITABLE(entry), txt, len, &pos); +} + +int gTextBox::length() +{ + const gchar *buf; + + if (!entry) + return 0; + + buf=gtk_entry_get_text(GTK_ENTRY(entry)); + if (!buf) return 0; + + return g_utf8_strlen(buf, -1); +} + +int gTextBox::maxLength() +{ + if (entry) + return gtk_entry_get_max_length(GTK_ENTRY(entry)); + else + return 0; +} + +void gTextBox::setMaxLength(int vl) +{ + if (!entry) + return; + if (vl<0) vl=0; + if (vl>65536) vl=0; + gtk_entry_set_max_length(GTK_ENTRY(entry), vl); + +} + +bool gTextBox::isSelected() +{ + if (entry) + return gtk_editable_get_selection_bounds(GTK_EDITABLE(entry), NULL, NULL); + else + return false; +} + +int gTextBox::selStart() +{ + int start; + + if (!entry) + return -1; + + gtk_editable_get_selection_bounds(GTK_EDITABLE(entry),&start,NULL); + return start; +} + +int gTextBox::selLength() +{ + int start,end; + + if (!entry) + return 0; + + gtk_editable_get_selection_bounds(GTK_EDITABLE(entry),&start,&end); + return end - start; +} + +char* gTextBox::selText() +{ + int start,end; + + if (!entry) + return NULL; + gtk_editable_get_selection_bounds(GTK_EDITABLE(entry),&start,&end); + return gtk_editable_get_chars(GTK_EDITABLE(entry),start,end); + +} + +void gTextBox::setSelText(char *txt,int len) +{ + int start,end; + + if (!entry) + return; + + gtk_editable_get_selection_bounds(GTK_EDITABLE(entry),&start,&end); + gtk_editable_delete_text(GTK_EDITABLE(entry),start,end); + gtk_editable_insert_text(GTK_EDITABLE(entry),txt,len,&start); + +} + +void gTextBox::selClear() +{ + int start; + + if (!entry) + return; + + gtk_editable_get_selection_bounds(GTK_EDITABLE(entry),&start,NULL); + gtk_editable_select_region(GTK_EDITABLE(entry),start,start); +} + +void gTextBox::selectAll() +{ + if (entry) + gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1); +} + +void gTextBox::select(int start,int len) +{ + if (!entry) + return; + if ( (len<=0) || (start<0) ) { selClear(); return; } + gtk_editable_select_region(GTK_EDITABLE(entry),start,start+len); +} + + +int gTextBox::alignment() +{ + if (entry) + return gt_to_alignment(gtk_entry_get_alignment(GTK_ENTRY(entry))); + else + return ALIGN_NORMAL; +} + +void gTextBox::setAlignment(int al) +{ + if (!entry) + return; + gtk_entry_set_alignment(GTK_ENTRY(entry), gt_from_alignment(al)); +} + +void gTextBox::updateCursor(GdkCursor *cursor) +{ + GdkWindow *win; + + gControl::updateCursor(cursor); + if (!entry) + return; + +#ifdef GTK3 + win = GTK_ENTRY(entry)->priv->text_area; +#else + win = GTK_ENTRY(entry)->text_area; +#endif + + if (!win) + return; + + if (cursor) + gdk_window_set_cursor(win, cursor); + else + { + cursor = gdk_cursor_new_for_display(gtk_widget_get_display(widget), GDK_XTERM); + gdk_window_set_cursor(win, cursor); +#ifdef GTK3 + g_object_unref(cursor); +#else + gdk_cursor_unref(cursor); +#endif + } +} + +void gTextBox::clear() +{ + setText(""); +} + + +int gTextBox::minimumHeight() +{ + /*GtkRequisition req; + + gtk_widget_size_request(widget, &req); + return req.height - 4;*/ + return font()->height() + hasBorder() ? 4 : 2; +} + +GtkIMContext *gTextBox::getInputMethod() +{ +#ifdef GTK3 + return entry ? GTK_ENTRY(entry)->priv->im_context : NULL; +#else + return entry ? GTK_ENTRY(entry)->im_context : NULL; +#endif +} + + +void gTextBox::getCursorPos(int *x, int *y, int pos) +{ + int px, py; + PangoLayout *layout; + PangoRectangle rect; + + layout = gtk_entry_get_layout(GTK_ENTRY(entry)); + pos = gtk_entry_text_index_to_layout_index(GTK_ENTRY(entry), pos < 0 ? position() : pos); + pango_layout_get_cursor_pos(layout, pos, &rect, NULL); + + gtk_entry_get_layout_offsets(GTK_ENTRY(entry), &px, &py); + +#ifdef GTK3 + GdkRectangle r; + gtk_entry_get_text_area(GTK_ENTRY(entry), &r); + py = r.y; +#endif + + *x = px + PANGO_PIXELS(rect.x); + *y = py + PANGO_PIXELS(rect.y + rect.height); +} + +void gTextBox::setFocus() +{ + bool r = isReadOnly(); + + if (!r) + setReadOnly(true); + gControl::setFocus(); + if (!r) + setReadOnly(false); +} diff --git a/gb.gtk/src/gtextbox.h b/gb.gtk/src/gtextbox.h new file mode 100644 index 00000000..7a362ebf --- /dev/null +++ b/gb.gtk/src/gtextbox.h @@ -0,0 +1,86 @@ +/*************************************************************************** + + gtextbox.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GTEXTBOX_H +#define __GTEXTBOX_H + +class gTextBox : public gControl +{ +public: + gTextBox(gContainer *parent, bool combo = false); + ~gTextBox(); + +//"Properties" + int alignment(); + bool hasBorder(); + virtual int length(); + int maxLength(); + bool password(); + int position(); + virtual char *text(); + virtual bool isReadOnly(); + int selLength(); + int selStart(); + char* selText(); + bool isSelected(); + + void setAlignment(int vl); + void setBorder(bool vl); + void setMaxLength(int len); + void setPassword(bool vl); + void setPosition(int pos); + virtual void setReadOnly(bool vl); + virtual void setText(const char *vl); + void setSelText(char *txt, int len); + +//"Methods" + virtual void clear(); + virtual void setFocus(); + void insert(char* txt,int len); + void selClear(); + void select(int start,int len); + void selectAll(); + bool hasEntry() const { return entry != 0; } + + void getCursorPos(int *x, int *y, int pos); + +//"Signals" + void (*onChange)(gTextBox *sender); + void (*onActivate)(gTextBox *sender); + +//"Private" + virtual void updateCursor(GdkCursor *cursor); + void initEntry(); + GtkWidget *entry; + virtual int minimumHeight(); + virtual GtkIMContext *getInputMethod(); + + unsigned _changed : 1; + unsigned _border : 1; + +#ifdef GTK3 + static GtkCssProvider *_style_provider; +#endif +}; + +#endif diff --git a/gb.gtk/src/gtools.cpp b/gb.gtk/src/gtools.cpp new file mode 100644 index 00000000..2fab0709 --- /dev/null +++ b/gb.gtk/src/gtools.cpp @@ -0,0 +1,2331 @@ +/*************************************************************************** + + gtools.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" +#include "gcolor.h" +#include "gdesktop.h" +#include "gtools.h" +#include "gpicture.h" +#include "gapplication.h" + +// HTML character entities +#include "kentities.h" + +void stub(const char *function) +{ + printf("gb.gtk: warning: %s not yet implemented\n", function); +} + +/******************************************************************* + +Conversion between GDK and long type colors + +********************************************************************/ + +#define SCALE(i) ((int)(i * 255.0 / 65535.0 + 0.5)) +#define UNSCALE(d) ((int)(d / 255.0 * 65535.0 + 0.5)) + +#ifdef GTK3 +static GtkStateFlags _color_style_bg[] = { GTK_STATE_FLAG_NORMAL, GTK_STATE_FLAG_INSENSITIVE, (GtkStateFlags)-1 }; +static GtkStateFlags _color_style_fg[] = { GTK_STATE_FLAG_NORMAL, GTK_STATE_FLAG_ACTIVE, GTK_STATE_FLAG_PRELIGHT, (GtkStateFlags)-1 }; +#else +static GtkStateType _color_style_bg[] = { GTK_STATE_INSENSITIVE, GTK_STATE_ACTIVE, GTK_STATE_PRELIGHT, GTK_STATE_NORMAL }; +static GtkStateType _color_style_fg[] = { GTK_STATE_ACTIVE, GTK_STATE_PRELIGHT, GTK_STATE_NORMAL }; +#endif + +#ifdef GTK3 +void fill_gdk_color(GdkColor *gcol, gColor color) +{ + int r, g, b; + + gt_color_to_rgb(color, &r, &g, &b); + + gcol->red = UNSCALE(r); + gcol->green = UNSCALE(g); + gcol->blue = UNSCALE(b); +} +#else +void fill_gdk_color(GdkColor *gcol, gColor color, GdkColormap *cmap) +{ + int r, g, b; + + if (!cmap) + cmap = gdk_colormap_get_system(); + + gt_color_to_rgb(color, &r, &g, &b); + + gcol->red = UNSCALE(r); + gcol->green = UNSCALE(g); + gcol->blue = UNSCALE(b); + + gdk_colormap_alloc_color(cmap, gcol, TRUE, TRUE); +} +#endif + +gColor get_gdk_color(GdkColor *gcol) +{ + return gt_rgb_to_color(SCALE(gcol->red), SCALE(gcol->green), SCALE(gcol->blue)); +} + +#ifdef GTK3 + +static void set_color(GtkWidget *wid, gColor color, void (*func)(GtkWidget *, GtkStateFlags, const GdkRGBA *), bool fg) +{ + GdkRGBA gcol; + GdkRGBA *pcol; + int i; + //GtkStyleContext *style; + GtkStateFlags st; + + if (color == COLOR_DEFAULT) + { + pcol = NULL; + } + else + { + gt_from_color(color, &gcol); + pcol = &gcol; + + /*style = gtk_widget_get_style_context(wid); + if (fg) + gtk_style_context_get_color(style, GTK_STATE_FLAG_SELECTED, &scol); + else + gtk_style_context_get_background_color(style, GTK_STATE_FLAG_SELECTED, &scol);*/ + } + + for (i = 0;; i++) + { + st = fg ? _color_style_fg[i] : _color_style_bg[i]; + if (st < 0) + break; + (*func)(wid, st, pcol); + } + + /*if (color != COLOR_DEFAULT) + (*func)(wid, GTK_STATE_FLAG_SELECTED, &scol);*/ +} + +#else + +static void set_color(GtkWidget *wid, gColor color, void (*func)(GtkWidget *, GtkStateType, const GdkColor *), bool fg) +{ + GdkColor gcol; + GdkColor *pcol; + int i; + GtkStateType st; + + if (color == COLOR_DEFAULT) + { + pcol = NULL; + } + else + { + fill_gdk_color(&gcol, color); + pcol = &gcol; + } + + for (i = 0;; i++) + { + st = fg ? _color_style_fg[i] : _color_style_bg[i]; + (*func)(wid, st, pcol); + if (st == GTK_STATE_NORMAL) + break; + } +} + +#endif + +#ifndef GTK3 +gColor get_gdk_fg_color(GtkWidget *wid, bool enabled) +{ + GtkStyle* st; + + st=gtk_widget_get_style(wid); + return get_gdk_color(&st->fg[enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE]); +} +#endif + +void set_gdk_fg_color(GtkWidget *wid, gColor color) +{ +#ifdef GTK3 + set_color(wid, color, gtk_widget_override_color, true); +#else + set_color(wid, color, gtk_widget_modify_fg, true); +#endif +} + +#ifndef GTK3 +gColor get_gdk_bg_color(GtkWidget *wid, bool enabled) +{ + GtkStyle* st; + + st=gtk_widget_get_style(wid); + return get_gdk_color(&st->bg[enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE]); +} +#endif + +void set_gdk_bg_color(GtkWidget *wid,gColor color) +{ +#ifdef GTK3 + set_color(wid, color, gtk_widget_override_background_color, false); +#else + set_color(wid, color, gtk_widget_modify_bg, false); +#endif +} + +#ifndef GTK3 +gColor get_gdk_text_color(GtkWidget *wid, bool enabled) +{ + GtkStyle* st; + + st=gtk_widget_get_style(wid); + return get_gdk_color(&st->text[enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE]); +} +#endif + +void set_gdk_text_color(GtkWidget *wid,gColor color) +{ +#ifdef GTK3 + set_color(wid, color, gtk_widget_override_color, true); +#else + set_color(wid, color, gtk_widget_modify_text, true); +#endif +} + +#ifndef GTK3 +gColor get_gdk_base_color(GtkWidget *wid, bool enabled) +{ + GtkStyle* st; + + st=gtk_widget_get_style(wid); + return get_gdk_color(&st->base[enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE]); +} +#endif + +void set_gdk_base_color(GtkWidget *wid,gColor color) +{ +#ifdef GTK3 + set_color(wid, color, gtk_widget_override_background_color, false); +#else + set_color(wid, color, gtk_widget_modify_base, false); +#endif +} + +void gt_color_to_rgb(gColor color, int *r, int *g, int *b) +{ + *b = color & 0xFF; + *g = (color >> 8) & 0xFF; + *r = (color >> 16) & 0xFF; +} + +gColor gt_rgb_to_color(int r, int g, int b) +{ + return (gColor)(b | (g << 8) | (r << 16)); +} + +void gt_color_to_rgba(gColor color, int *r, int *g, int *b, int *a) +{ + *b = color & 0xFF; + *g = (color >> 8) & 0xFF; + *r = (color >> 16) & 0xFF; + *a = 255 - ((color >> 24) & 0xFF); +} + +gColor gt_rgba_to_color(int r, int g, int b, int a) +{ + return (gColor)(b | (g << 8) | (r << 16) | ((255 - a) << 24)); +} + +void gt_color_to_frgba(gColor color, double *r, double *g, double *b, double *a) +{ + *b = (color & 0xFF) / 255.0; + *g = ((color >> 8) & 0xFF) / 255.0; + *r = ((color >> 16) & 0xFF) / 255.0; + *a = 1 - ((color >> 24) & 0xFF) / 255.0; +} + +#ifdef GTK3 + +void gt_from_color(gColor color, GdkRGBA *rgba) +{ + gt_color_to_frgba(color, &rgba->red, &rgba->green, &rgba->blue, &rgba->alpha); +} + +gColor gt_to_color(GdkRGBA *rgba) +{ + return gt_frgba_to_color(rgba->red, rgba->green, rgba->blue, rgba->alpha); +} + +void gt_to_css_color(char *css, gColor color) +{ + int r, g, b, a; + char buffer[8]; + + gt_color_to_rgba(color, &r, &g, &b, &a); + if (a == 255) + sprintf(css, "#%02X%02X%02X", r, g, b); + else + { + sprintf(buffer, "%.3f", a / 255.0); + sprintf(css, "rgba(%d,%d,%d,0.%s)", r, g, b, &buffer[2]); + } +} + +#endif + +gColor gt_frgba_to_color(double r, double g, double b, double a) +{ + return gt_rgba_to_color((int)(r * 255.0), (int)(g * 255.0), (int)(b * 255.0), (int)(a * 255.0)); +} + +void gt_rgb_to_hsv(int r, int g, int b, int *H, int *S,int *V ) +{ + float R = (float)r, G = (float)g, B = (float)b; + float v, x, f; + int i; + + R/=255; + G/=255; + B/=255; + + x=R; + if (Gv) v=G; + if (B>v) v=B; + + + if(v == x) { + *H=-1; + *S=0; + *V = (int)(v * 255); + return; + } + + f = (R == x) ? G - B : ((G == x) ? B - R : R - G); + i = (R == x) ? 3 : ((G == x) ? 5 : 1); + *H=(int)((i - f /(v - x))*60); + *S=(int)(((v - x)/v)*255); + *V=(int)(v*255); +} + +void gt_hsv_to_rgb(int h, int s, int v, int *R, int *G, int *B) +{ + double H,S,V; + double var_h,var_i,var_1,var_2,var_3,tmp_r,tmp_g,tmp_b; + + if (h < 0) + h = 360 - ((-h) % 360); + else + h = h % 360; + + H=((double)h)/360; + S=((double)s)/255; + V=((double)v)/255; + + if ( S == 0 ) + { + *R = (int)(V * 255); + *G = (int)(V * 255); + *B = (int)(V * 255); + } + else + { + var_h = H * 6; + var_i = (int)var_h; + var_1 = V * ( 1 - S ); + var_2 = V * ( 1 - S * ( var_h - var_i ) ); + var_3 = V * ( 1 - S * ( 1 - ( var_h - var_i ) ) ); + + switch ((int)var_i) + { + case 0: + tmp_r = V; + tmp_g = var_3; + tmp_b = var_1; + break; + + case 1: + tmp_r = var_2; + tmp_g = V; + tmp_b = var_1; + break; + + case 2: + tmp_r = var_1; + tmp_g = V; + tmp_b = var_3; + break; + + case 3: + tmp_r = var_1; + tmp_g = var_2; + tmp_b = V; + break; + + case 4: + tmp_r = var_3; + tmp_g = var_1; + tmp_b = V; + break; + + default: + tmp_r = V; + tmp_g = var_1; + tmp_b = var_2; + break; + } + + *R = (int)(tmp_r * 255); + *G = (int)(tmp_g * 255); + *B = (int)(tmp_b * 255); + + } +} + + +#if 0 +/******************************************************************* + + Border style in gWidgets that use a Frame to show it + + *******************************************************************/ +int Frame_getBorder(GtkFrame *fr) +{ + switch (gtk_frame_get_shadow_type(fr)) + { + case GTK_SHADOW_NONE: return BORDER_NONE; + case GTK_SHADOW_ETCHED_OUT: return BORDER_PLAIN; + case GTK_SHADOW_IN: return BORDER_SUNKEN; + case GTK_SHADOW_OUT: return BORDER_RAISED; + case GTK_SHADOW_ETCHED_IN: return BORDER_ETCHED; + } + + return BORDER_NONE; +} + +void Frame_setBorder(GtkFrame *fr,int vl) +{ + switch(vl) + { + case BORDER_NONE: + gtk_frame_set_shadow_type(fr,GTK_SHADOW_NONE); + break; + case BORDER_PLAIN: + gtk_frame_set_shadow_type(fr,GTK_SHADOW_ETCHED_OUT); + break; + case BORDER_SUNKEN: + gtk_frame_set_shadow_type(fr,GTK_SHADOW_IN); + break; + case BORDER_RAISED: + gtk_frame_set_shadow_type(fr,GTK_SHADOW_OUT); + break; + case BORDER_ETCHED: + gtk_frame_set_shadow_type(fr,GTK_SHADOW_ETCHED_IN); + break; + default: return; + } +} +#endif + +/************************************************************* + + Takes a snapshot of a GdkWindow + + *************************************************************/ + +gPicture *gt_grab_window(GdkWindow *win, int x, int y, int w, int h) +{ + int ow, oh; + int ww, hh; + int dx, dy; + GdkPixbuf *buf = NULL; + gPicture *pic; + +#ifdef GTK3 + gdk_window_get_geometry(win, NULL, NULL, &ww, &hh); +#else + gdk_window_get_geometry(win, 0, 0, &ww, &hh, 0); +#endif + + if (w <= 0 || h <= 0) + { + w = ww; + h = hh; + } + + dx = dy = 0; + ow = w; + oh = h; + + if (x < 0) + { + w += x; + dx = -x; + x = 0; + } + + if (y < 0) + { + h += y; + dy = -y; + y = 0; + } + + if ((x + w) > ww) + w = ww - x; + + if ((y + h) > hh) + h = hh - y; + + if (w > 0 && h > 0) + { +#ifdef GTK3 + buf = gdk_pixbuf_get_from_window(win, x, y, w, h); +#else + buf = gdk_pixbuf_get_from_drawable(NULL, win, NULL, x, y, 0, 0, w, h); +#endif + } + + if (w == ow && h == oh) + return new gPicture(buf); + + pic = new gPicture(gPicture::PIXBUF, ow, oh, false); + pic->fill(0); + + if (w <= 0 || h <= 0) + return pic; + + gdk_pixbuf_copy_area(buf, 0, 0, w, h, pic->getPixbuf(), dx, dy); + return pic; +} + +/************************************************************* + + GtkLabel with accelerators + + *************************************************************/ + +void gMnemonic_correctText(char *st,char **buf) +{ + int bucle,b2; + + if (!st || !*st) + { + *buf = g_strdup(""); + return; + } + + int len = strlen(st); + int len_in = len; + + for (bucle=0;bucle') + { + len+=3; + } + } + + *buf=(char*)g_malloc(sizeof(char)*(len+1)); + (*buf[0])=0; + b2=0; + + for (bucle=0;bucle') + { + (*buf)[b2++]='&'; + (*buf)[b2++]='g'; + (*buf)[b2++]='t'; + (*buf)[b2++]=';'; + } + else + { + (*buf)[b2++]=st[bucle]; + } + (*buf)[b2]=0; + } + + return retval; +} + +void gMnemonic_returnText(char *st,char **buf) +{ + int bucle,b2; + + if (!st || !*st) + { + *buf = g_strdup(""); + return; + } + + int len = strlen(st); + int len_in = len; + + for (bucle=0;bucle> 1) + *g) >> 2; // (r + b + g) / 3 + + r += 4; + g += 4; + b += 4; + } + + return dimg; +} + +void gt_shortcut_parse(char *shortcut, guint *key, GdkModifierType *mods) +{ + gchar **cads; + gchar *res; + int bucle; + + res = NULL; + + if (!shortcut || !*shortcut) + { + *key = 0; + return; + } + + cads = g_strsplit(shortcut, "+", 0); + + bucle = 0; + while (cads[bucle]) + { + g_strstrip(cads[bucle]); + bucle++; + } + + bucle = 0; + while (cads[bucle]) + { + if (!strcasecmp(cads[bucle],"ctrl")) + g_stradd(&res, ""); + else if (!strcasecmp(cads[bucle],"shift")) + g_stradd(&res, ""); + else if (!strcasecmp(cads[bucle],"alt")) + g_stradd(&res, ""); + else + g_stradd(&res, cads[bucle]); + + bucle++; + } + + g_strfreev(cads); + gtk_accelerator_parse(res, key, mods); + + if (res) + g_free(res); +} + +#define MAX_FREE_LATER 16 + +static char *_free_later_ptr[MAX_FREE_LATER] = { 0 }; +static int _free_later_index = 0; + +char *gt_free_later(char *ptr) +{ + if (_free_later_ptr[_free_later_index]) + g_free(_free_later_ptr[_free_later_index]); + + _free_later_ptr[_free_later_index] = ptr; + + _free_later_index++; + + if (_free_later_index >= MAX_FREE_LATER) + _free_later_index = 0; + + return ptr; +} + +static void free_all_later() +{ + int i; + char *p; + + for (i = 0; i < MAX_FREE_LATER; i++) + { + p = _free_later_ptr[i]; + if (p) + { + g_free(p); + _free_later_ptr[i] = NULL; + } + } +} + +void gt_exit() +{ + free_all_later(); +} + +static const char *html_entity(char c) +{ + static char same[2]; + + if (c == '<') + return "<"; + else if (c == '>') + return ">"; + else if (c == '&') + return "&"; + + same[0] = c; + same[1] = 0; + return (const char *)same; +} + +static void add_space(GString *str) +{ + char c; + + if (str->len == 0) + return; + + c = str->str[str->len - 1]; + if (c != ' ' && c != '\n') + g_string_append_c(str, ' '); +} + +char *gt_html_to_pango_string(const char *html, int len_html, bool newline_are_break) +{ + static const char *title[] = + { + "h1", "size=\"xx-large\"", + "h2", "size=\"x-large\"", + "h3", "size=\"large\"", + "h4", "size=\"medium\"", + "h5", "size=\"small\"", + "h6", "size=\"x-small\"", + NULL, NULL + }; + + static const char *accept[] = { "b", "big", "i", "s", "sub", "sup", "small", "tt", "u", NULL }; + + static const char *size_name[] = { "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large" }; + static const int size_stack_len = 16; + + GString *pango = g_string_new(""); + const char *p, *p_end, *p_markup; + char c; + char *token, *markup, **attr; + const char *pp; + gsize len; + bool start_token = false; + bool end_token = false; + const char **pt; + int size = 3; // medium + char size_stack[size_stack_len]; + int size_stack_ptr = 0; + bool newline = true; + bool inside_par = false; + + p_end = &html[len_html < 0 ? strlen(html) : len_html]; + p_markup = NULL; + + for (p = html;; p++) + { + c = *p; + if (!c) + break; + + if (c == '<') + { + if (p[1] == '/') + { + p_markup = &p[2]; + start_token = false; + end_token = true; + } + else + { + p_markup = &p[1]; + start_token = true; + end_token = false; + } + continue; + } + + if (c == '>') + { + if (!p_markup) + { + g_string_append(pango, ">"); + newline = false; + continue; + } + + len = p - p_markup; + + if (p[-1] == '/') + { + if (len > 0) + len--; + end_token = true; + } + + if (len <= 0) + { + g_string_append(pango, "<"); + if (end_token) g_string_append(pango, "/"); + g_string_append(pango, ">"); + p_markup = NULL; + continue; + } + + markup = g_strndup(p_markup, len); + attr = g_strsplit(markup, " ", -1); + token = attr[0]; + + for (pt = title; *pt; pt += 2) + { + if (!strcasecmp(*pt, token)) + { + if (start_token && !end_token) + g_string_append_printf(pango, "", pt[1]); + else if (end_token && !start_token) + { + g_string_append_printf(pango, "\n"); + g_string_append(pango, "\n"); + newline = true; + } + goto __FOUND_TOKEN; + } + } + + for (pt = accept; *pt; pt++) + { + if (!strcasecmp(*pt, token)) + { + if (start_token != end_token) + g_string_append_printf(pango, "<%s%s>", end_token ? "/" : "", *pt); + goto __FOUND_TOKEN; + } + } + + if (!strcasecmp(token, "br") || !strcasecmp(token, "hr")) + { + if (start_token) + { + g_string_append(pango, "\n"); + newline = true; + } + goto __FOUND_TOKEN; + } + + if (!strcasecmp(token, "p")) + { + if ((end_token || inside_par) && p[1]) + { + g_string_append(pango, "\n\n"); + newline = true; + } + inside_par = start_token; + goto __FOUND_TOKEN; + } + + if (!strcasecmp(token, "font")) + { + if (end_token && !start_token) + { + if (size_stack_ptr > 0) + { + size_stack_ptr--; + if (size_stack_ptr < size_stack_len) + size = size_stack[size_stack_ptr]; + } + g_string_append(pango, ""); + } + else if (start_token && !end_token) + { + if (size_stack_ptr < size_stack_len) + size_stack[size_stack_ptr] = size; + size_stack_ptr++; + + g_string_append(pango, " 6) + size = 6; + + g_string_append(pango, "\""); + g_string_append(pango, size_name[size]); + g_string_append(pango, "\""); + } + } + g_string_append(pango, ">"); + } + goto __FOUND_TOKEN; + } + + if (!strcasecmp(token, "a") || !strncasecmp(token, "a href", 6)) + { + if (start_token && !end_token) + g_string_append(pango, ""); + else if (end_token && !start_token) + g_string_append(pango, ""); + goto __FOUND_TOKEN; + } + + g_string_append(pango, "<"); + if (end_token) g_string_append(pango, "/"); + while (p_markup < p) + { + g_string_append(pango, html_entity(*p_markup)); + p_markup++; + } + g_string_append(pango, ">"); + newline = false; + + __FOUND_TOKEN: + + g_free(token); + p_markup = NULL; + continue; + } + + if (c == '\n' && !newline_are_break) + { + if (!newline) + add_space(pango); + continue; + } + + if (c == '&') + { + const char *entity_start = ++p; + int entity_len; + int entity_unicode; + char *endptr; + +// if ((p_end - p) >= 6 && !strncasecmp(p, " ", 6)) +// { +// g_string_append(pango, " "); +// p += 5; +// continue; +// } + while (p < p_end && *p != ';' && ((uchar)*p) > ' ') + p++; + + entity_len = p - entity_start; + if (*p != ';') + entity_len = 0; + + if (entity_len > 1) + { + if ((entity_len == 3 && !strncasecmp(entity_start, "amp", 3)) + || (entity_len == 2 && (!strncasecmp(entity_start, "lt", 2) || !strncasecmp(entity_start, "gt", 2)))) + { + g_string_append_len(pango, entity_start - 1, entity_len + 2); + continue; + } + else + { + entity_unicode = -1; + endptr = NULL; + errno = 0; + + if (*entity_start == '#') + { + if (entity_start[1] == 'x') + { + entity_unicode = strtol(&entity_start[2], &endptr, 16); + if (entity_unicode <= 0) + entity_unicode = -1; + } + else if (isdigit(entity_start[1])) + { + entity_unicode = strtol(&entity_start[1], &endptr, 10); + if (entity_unicode <= 0) + entity_unicode = -1; + } + } + else + { + const struct entity *e = kde_findEntity(entity_start, entity_len); + if (e) + entity_unicode = e->code; + } + + if (entity_unicode >= 0) + { + g_string_append_unichar(pango, entity_unicode); + continue; + } + } + } + + g_string_append(pango, "&"); + p = entity_start - 1; + newline = false; + continue; + } + + if (!p_markup) + { + if (c == ' ') + { + if (!newline) + add_space(pango); + continue; + } + + g_string_append(pango, html_entity(*p)); + newline = false; + } + } + + // Sometimes the first markup is not taken into account. + // This is a workaround for this bug: + g_string_prepend_unichar(pango, 0xFEFF); + + p = g_string_free(pango, false); + //fprintf(stderr, "pango: '%s'\n", p); + return (char *)p; +} + +int gt_to_alignment(double halign, double valign) +{ + int align = 0; + + if (halign == 0.0) + align += 0x1; + else if (halign == 0.5) + align += 0x2; + else if (halign == 1.0) + align += 0x3; + + if (valign == 0.0) + align += 0x10; + else if (valign == 1.0) + align += 0x20; + + return align; +} + +double gt_from_alignment(int align, bool vertical) +{ + if (vertical) + { + switch(align & 0xF0) + { + case 0x10: return 0.0; + case 0x20: return 1.0; + default: return 0.5; + } + } + else + { + switch (align & 0x0F) + { + case 1: return 0.0; + case 2: return 1.0; + case 3: return 0.5; + default: return gDesktop::rightToLeft() ? 1.0 : 0.0; + } + } +} + + +void gt_ensure_visible(GtEnsureVisible *arg, int x, int y, int w, int h) +{ + w = (w + 1) / 2; + h = (h + 1) / 2; + x += w; + y += h; + + int pw = arg->clientWidth; + int ph = arg->clientHeight; + + int cx = -arg->scrollX; + int cy = -arg->scrollY; + int cw = arg->scrollWidth; + int ch = arg->scrollHeight; + + if ( pw < w*2 ) + w=pw/2; + if ( ph < h*2 ) + h=ph/2; + + if ( cw <= pw ) { + w=0; + cx=0; + } + if ( ch <= ph ) { + h=0; + cy=0; + } + + if ( x < -cx+w ) + cx = -x+w; + else if ( x >= -cx+pw-w ) + cx = -x+pw-w; + + if ( y < -cy+h ) + cy = -y+h; + else if ( y >= -cy+ph-h ) + cy = -y+ph-h; + + if ( cx > 0 ) + cx=0; + else if ( cx < pw-cw && cw>pw ) + cx=pw-cw; + + if ( cy > 0 ) + cy=0; + else if ( cy < ph-ch && ch>ph ) + cy=ph-ch; + + arg->scrollX = -cx; + arg->scrollY = -cy; +} + +#ifdef GTK3 +#else +void gt_pixmap_fill(GdkPixmap *pix, gColor col, GdkGC *gc) +{ + GdkColor color; + int w, h; + bool free_gc = false; + + fill_gdk_color(&color, col); + + #if GTK_CHECK_VERSION(2, 24, 0) + gdk_pixmap_get_size(pix, &w, &h); + #else + gdk_drawable_get_size(GDK_DRAWABLE(pix), &w, &h); + #endif + + if (!gc) + { + gc = gdk_gc_new(pix); + free_gc = true; + } + + gdk_gc_set_foreground(gc, &color); + gdk_gc_set_background(gc, &color); + gdk_draw_rectangle(pix, gc, true, 0, 0, w, h); + + if (free_gc) + g_object_unref(gc); +} + +void gt_pixbuf_render_pixmap_and_mask(GdkPixbuf *pixbuf, GdkPixmap **pixmap_return, GdkBitmap **mask_return, int alpha_threshold) +{ + GdkColormap *colormap = gdk_rgb_get_colormap(); + GdkScreen *screen; + + screen = gdk_colormap_get_screen (colormap); + + if (pixmap_return) + { + GdkGC *gc; + *pixmap_return = gdk_pixmap_new (gdk_screen_get_root_window (screen), + gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf), + gdk_colormap_get_visual (colormap)->depth); + + gdk_drawable_set_colormap (GDK_DRAWABLE (*pixmap_return), colormap); + + gc = gdk_gc_new(*pixmap_return); + + gt_pixmap_fill(*pixmap_return, 0, gc); + + gdk_draw_pixbuf (*pixmap_return, gc, pixbuf, + 0, 0, 0, 0, + gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf), + GDK_RGB_DITHER_NORMAL, + 0, 0); + + g_object_unref(gc); + } + + if (mask_return) + { + if (gdk_pixbuf_get_has_alpha (pixbuf)) + { + *mask_return = gdk_pixmap_new (gdk_screen_get_root_window (screen), + gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf), 1); + + gdk_pixbuf_render_threshold_alpha (pixbuf, *mask_return, + 0, 0, 0, 0, + gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf), + alpha_threshold); + } + else + *mask_return = NULL; + } +} +#endif + +#if 0 +void gt_pixbuf_replace_color(GdkPixbuf *pixbuf, gColor src, gColor dst, bool noteq) +{ + guint32 *p; + int i, n; + + p = (guint32 *)gdk_pixbuf_get_pixels(pixbuf); + n = gdk_pixbuf_get_width(pixbuf) * gdk_pixbuf_get_height(pixbuf); + + src ^= 0xFF000000; + dst ^= 0xFF000000; + + if (noteq) + { + for (i = 0; i < n; i++, p ++) + { + if (*p != src) + *p = dst; + } + } + else + { + for (i = 0; i < n; i++, p ++) + { + if (*p == src) + *p = dst; + } + } +} +#endif + +// Comes from the GIMP + +typedef + struct { + float r; + float b; + float g; + float a; + } + RGB; + +static void color_to_alpha(RGB *src, const RGB *color) +{ + RGB alpha; + + alpha.a = src->a; + + if (color->r < 0.0001) + alpha.r = src->r; + else if (src->r > color->r) + alpha.r = (src->r - color->r) / (1.0 - color->r); + else if (src->r < color->r) + alpha.r = (color->r - src->r) / color->r; + else alpha.r = 0.0; + + if (color->g < 0.0001) + alpha.g = src->g; + else if (src->g > color->g) + alpha.g = (src->g - color->g) / (1.0 - color->g); + else if (src->g < color->g) + alpha.g = (color->g - src->g) / (color->g); + else alpha.g = 0.0; + + if (color->b < 0.0001) + alpha.b = src->b; + else if (src->b > color->b) + alpha.b = (src->b - color->b) / (1.0 - color->b); + else if (src->b < color->b) + alpha.b = (color->b - src->b) / (color->b); + else alpha.b = 0.0; + + if (alpha.r > alpha.g) + { + if (alpha.r > alpha.b) + { + src->a = alpha.r; + } + else + { + src->a = alpha.b; + } + } + else if (alpha.g > alpha.b) + { + src->a = alpha.g; + } + else + { + src->a = alpha.b; + } + + if (src->a < 0.0001) + return; + + src->r = (src->r - color->r) / src->a + color->r; + src->g = (src->g - color->g) / src->a + color->g; + src->b = (src->b - color->b) / src->a + color->b; + + src->a *= alpha.a; +} + +void gt_pixbuf_make_alpha(GdkPixbuf *pixbuf, gColor color) +{ + guchar *p; + int i, n; + RGB rgb_color, rgb_src; + int r, g, b; + + p = gdk_pixbuf_get_pixels(pixbuf); + n = gdk_pixbuf_get_width(pixbuf) * gdk_pixbuf_get_height(pixbuf); + + gt_color_to_rgb(color, &r, &g, &b); + + rgb_color.b = b / 255.0; + rgb_color.g = g / 255.0; + rgb_color.r = r / 255.0; + rgb_color.a = 1.0; + + for (i = 0; i < n; i++, p += 4) + { + rgb_src.r = p[0] / 255.0; + rgb_src.g = p[1] / 255.0; + rgb_src.b = p[2] / 255.0; + rgb_src.a = p[3] / 255.0; + + color_to_alpha(&rgb_src, &rgb_color); + + p[0] = rgb_src.r * 255.0 + 0.5; + p[1] = rgb_src.g * 255.0 + 0.5; + p[2] = rgb_src.b * 255.0 + 0.5; + //p[3] = 0xFF ^ (uchar)(rgb_src.a * 255.0 + 0.5); + p[3] = rgb_src.a * 255.0 + 0.5; + } +} + +void gt_pixbuf_make_gray(GdkPixbuf *pixbuf) +{ + guchar *p; + int i, n; + + p = gdk_pixbuf_get_pixels(pixbuf); + n = gdk_pixbuf_get_width(pixbuf) * gdk_pixbuf_get_height(pixbuf); + + for (i = 0; i < n; i++, p += 4) + p[0] = p[1] = p[2] = (p[0] * 11 + p[1] * 16 + p[2] * 5) / 32; +} + +static void disabled_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer data) +{ + // Print only debugging messages + if (log_level & G_LOG_LEVEL_DEBUG) + g_log_default_handler(log_domain, log_level, message, data); +} + +static GLogFunc old_handler = NULL; + +void gt_disable_warnings(bool disable) +{ + //fprintf(stderr, "disable warnings\n"); + if (disable) + old_handler = g_log_set_default_handler(disabled_handler, NULL); + else + g_log_set_default_handler(old_handler, NULL); + //fprintf(stderr, "enable warnings\n"); +} + +void gt_set_cell_renderer_text_from_font(GtkCellRendererText *cell, gFont *font) +{ + g_object_set(G_OBJECT(cell), + "font-desc", font->desc(), + "underline", font->underline() ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE, + "strikethrough", font->strikeout(), + (void *)NULL); +} + +static void set_layout_from_font(PangoLayout *layout, gFont *font, bool add, int dpi) +{ + PangoFontDescription *desc; + PangoAttrList *attrs; + PangoAttribute *attr; + bool copy = false; + + desc = pango_context_get_font_description(font->ct); + + if (dpi && dpi != gDesktop::resolution()) + { + int size = pango_font_description_get_size(desc); + desc = pango_font_description_copy(desc); + copy = true; + pango_font_description_set_size(desc, size * dpi / gDesktop::resolution()); + } + + pango_layout_set_font_description(layout, desc); + + if (add) + { + attrs = pango_layout_get_attributes(layout); + if (!attrs) + { + attrs = pango_attr_list_new(); + add = false; + } + } + else + attrs = pango_attr_list_new(); + + if (font->underline()) + { + attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE); + pango_attr_list_insert(attrs, attr); + } + + if (font->strikeout()) + { + attr = pango_attr_strikethrough_new(true); + pango_attr_list_insert(attrs, attr); + } + + pango_layout_set_attributes(layout, attrs); + + if (!add) + pango_attr_list_unref(attrs); + + pango_layout_context_changed(layout); + + if (copy) + pango_font_description_free(desc); +} + +void gt_set_layout_from_font(PangoLayout *layout, gFont *font, int dpi) +{ + set_layout_from_font(layout, font, false, dpi); +} + +void gt_add_layout_from_font(PangoLayout *layout, gFont *font, int dpi) +{ + set_layout_from_font(layout, font, true, dpi); +} + +void gt_layout_alignment(PangoLayout *layout, float w, float h, float *tw, float *th, int align, float *offX, float *offY) +{ + int ptw, pth; + pango_layout_get_size(layout, &ptw, &pth); + *tw = (float)ptw / PANGO_SCALE; + *th = (float)pth / PANGO_SCALE; + + if (w < 0) w = *tw; + if (h < 0) h = *th; + + switch (align) + { + case ALIGN_BOTTOM_NORMAL: + align = gDesktop::rightToLeft() ? ALIGN_BOTTOM_RIGHT : ALIGN_BOTTOM_LEFT; + break; + + case ALIGN_NORMAL: + align = gDesktop::rightToLeft() ? ALIGN_RIGHT : ALIGN_LEFT; + break; + + case ALIGN_TOP_NORMAL: + align = gDesktop::rightToLeft() ? ALIGN_TOP_RIGHT : ALIGN_TOP_LEFT; + break; + } + + switch (align) + { + case ALIGN_BOTTOM: + pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); + *offX = (w - *tw) / 2; + *offY = h - *th; + break; + + case ALIGN_BOTTOM_LEFT: + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); + *offX = 0; + *offY = h - *th; + break; + + case ALIGN_BOTTOM_RIGHT: + pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT); + *offX = w - *tw; + *offY = h - *th; + break; + + case ALIGN_CENTER: + pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); + *offX = (w - *tw) / 2; + *offY = (h - *th) / 2; + break; + + case ALIGN_LEFT: + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); + *offX = 0; + *offY = (h - *th) / 2; + break; + + case ALIGN_RIGHT: + pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT); + *offX = w - *tw; + *offY = (h - *th) / 2; + break; + + case ALIGN_TOP: + pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); + *offX = (w - *tw) / 2; + *offY = 0; + break; + + case ALIGN_TOP_LEFT: + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); + *offX = 0; + *offY = 0; + break; + + case ALIGN_TOP_RIGHT: + pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT); + *offX = w - *tw; + *offY = 0; + break; + } +} + +#if GTK_CHECK_VERSION(2, 18, 0) +#else +void +gtk_widget_set_can_focus (GtkWidget *widget, + gboolean can_focus) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + if (can_focus != GTK_WIDGET_CAN_FOCUS (widget)) + { + if (can_focus) + GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS); + else + GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_FOCUS); + + gtk_widget_queue_resize (widget); + g_object_notify (G_OBJECT (widget), "can-focus"); + } +} + +void gtk_widget_get_allocation(GtkWidget *widget, GtkAllocation *allocation) +{ + *allocation = widget->allocation; +} +#endif + +static void add_again(GtkWidget *widget, GtkWidget *ignore) +{ + int x, y; + GtkContainer *parent; + gControl *control; + gContainer *parent_control; + + if (widget == ignore) + return; + + parent = GTK_CONTAINER(gtk_widget_get_parent(ignore)); + control = gt_get_control(widget); + parent_control = (gContainer *)gt_get_control(parent); + + if (control && parent_control) + { + x = control->x(); + y = control->y(); + } + + g_object_ref(G_OBJECT(widget)); + gtk_container_remove(parent, widget); + gtk_container_add(parent, widget); + g_object_unref(G_OBJECT(widget)); + + if (control && parent_control) + parent_control->moveChild(control, x, y); +} + +void gt_lower_widget(GtkWidget *widget) +{ + GtkContainer *parent; + + parent = GTK_CONTAINER(gtk_widget_get_parent(widget)); + if (parent) + gtk_container_foreach(parent, (GtkCallback)add_again, widget); +} + +#if GTK_CHECK_VERSION(2, 22, 0) +#else +int gdk_device_get_source(GdkDevice *device) +{ + return device->source; +} + +//GtkWidget *_gtk_window_group_get_current_grab(GtkWindowGroup *window_group); + +GtkWidget *gtk_window_group_get_current_grab(GtkWindowGroup *window_group) +{ + if (window_group->grabs) + return GTK_WIDGET(window_group->grabs->data); + return NULL; +} +#endif + +void gt_cairo_set_source_color(cairo_t *cr, GB_COLOR color) +{ + int r, g, b, a; + + GB_COLOR_SPLIT(color, r, g, b, a); + cairo_set_source_rgba(cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0); +} + +void gt_cairo_draw_rect(cairo_t *cr, int x, int y, int w, int h, GB_COLOR color) +{ + gt_cairo_set_source_color(cr, color); + + cairo_rectangle(cr, x, y, w, 1); + cairo_fill(cr); + if (h <= 1) + return; + cairo_rectangle(cr, x, y + h - 1, w, 1); + cairo_fill(cr); + if (h <= 2) + return; + cairo_rectangle(cr, x, y + 1, 1, h - 2); + cairo_fill(cr); + cairo_rectangle(cr, x + w - 1, y + 1, 1, h - 2); + cairo_fill(cr); +} + +// Function partially taken from the GTK+ source code. +cairo_surface_t *gt_cairo_create_surface_from_pixbuf(const GdkPixbuf *pixbuf) +{ + gint width = gdk_pixbuf_get_width (pixbuf); + gint height = gdk_pixbuf_get_height (pixbuf); + guchar *gdk_pixels = gdk_pixbuf_get_pixels (pixbuf); + int gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf); + int n_channels = gdk_pixbuf_get_n_channels (pixbuf); + int cairo_stride; + guchar *cairo_pixels; + cairo_format_t format; + cairo_surface_t *surface; + static const cairo_user_data_key_t key = { 0 }; + int j; + + //fprintf(stderr, "gt_cairo_create_surface_from_pixbuf: %d %d / %d\n", width, height, n_channels); + + if (n_channels == 3) + format = CAIRO_FORMAT_RGB24; + else + format = CAIRO_FORMAT_ARGB32; + + cairo_stride = cairo_format_stride_for_width (format, width); + cairo_pixels = (uchar *)g_malloc (height * cairo_stride); + surface = cairo_image_surface_create_for_data ((unsigned char *)cairo_pixels, + format, + width, height, cairo_stride); + + cairo_surface_set_user_data (surface, &key, + cairo_pixels, (cairo_destroy_func_t)g_free); + + for (j = height; j; j--) + { + guchar *p = gdk_pixels; + guchar *q = cairo_pixels; + + if (n_channels == 3) + { + guchar *end = p + 3 * width; + + while (p < end) + { + #if G_BYTE_ORDER == G_LITTLE_ENDIAN + q[0] = p[2]; + q[1] = p[1]; + q[2] = p[0]; + #else + q[1] = p[0]; + q[2] = p[1]; + q[3] = p[2]; + #endif + p += 3; + q += 4; + } + } + else + { + guchar *end = p + 4 * width; + guint t1,t2,t3; + + #define MULT(d,c,a,t) G_STMT_START { t = c * a + 0x7f; d = ((t >> 8) + t) >> 8; } G_STMT_END + + while (p < end) + { + #if G_BYTE_ORDER == G_LITTLE_ENDIAN + MULT(q[0], p[2], p[3], t1); + MULT(q[1], p[1], p[3], t2); + MULT(q[2], p[0], p[3], t3); + q[3] = p[3]; + #else + q[0] = p[3]; + MULT(q[1], p[0], p[3], t1); + MULT(q[2], p[1], p[3], t2); + MULT(q[3], p[2], p[3], t3); + #endif + + p += 4; + q += 4; + } + + #undef MULT + } + + gdk_pixels += gdk_rowstride; + cairo_pixels += cairo_stride; + } + + return surface; +} + +void gt_cairo_draw_pixbuf(cairo_t *cr, GdkPixbuf *pixbuf, float x, float y, float w, float h, float opacity, GB_RECT *source) +{ + cairo_surface_t *surface; + cairo_pattern_t *pattern = NULL, *save; + cairo_matrix_t matrix; + + cairo_save(cr); + + save = cairo_get_source(cr); + cairo_pattern_reference(save); + + if (source) + pixbuf = gdk_pixbuf_new_subpixbuf(pixbuf, source->x, source->y, source->w, source->h); + + if (w < 0 || h < 0) + { + //fprintf(stderr, "gdk_cairo_set_source_pixbuf: %d %d\n", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf)); + gdk_cairo_set_source_pixbuf(cr, pixbuf, x, y); + cairo_rectangle(cr, x, y, gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf)); + } + else + { + surface = gt_cairo_create_surface_from_pixbuf(pixbuf); + pattern = cairo_pattern_create_for_surface(surface); + cairo_surface_destroy(surface); + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + + if (source && w >= source->w && h >= source->h && w == (int)w && h == (int)h && ((int)w % source->w) == 0 && ((int)h % source->h) == 0) + cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST); + + //gdk_cairo_set_source_pixbuf(cr, picture->getPixbuf(), x, y); + + cairo_matrix_init_identity(&matrix); + cairo_matrix_translate(&matrix, x, y); + cairo_matrix_scale(&matrix, w / gdk_pixbuf_get_width(pixbuf), h / gdk_pixbuf_get_height(pixbuf)); + cairo_matrix_invert(&matrix); + cairo_pattern_set_matrix(pattern, &matrix); + cairo_set_source(cr, pattern); + + cairo_rectangle(cr, x, y, w, h); + } + + if (opacity == 1.0) + { + cairo_fill(cr); + } + else + { + cairo_clip(cr); + cairo_paint_with_alpha(cr, opacity); + } + + cairo_set_source(cr, save); + cairo_pattern_destroy(save); + + cairo_restore(cr); + + if (pattern) + cairo_pattern_destroy(pattern); + + if (source) + g_object_unref(pixbuf); +} + + +// Style management + +#define NUM_STYLES 12 + +#ifdef GTK3 +static int _style_context_loaded = 0; +static GtkStyleContext *_style_context[NUM_STYLES]; +#else +static int _style_loaded = 0; +static GtkStyle *_style[NUM_STYLES]; +#endif + +static int type_to_index(GType type) +{ + if (type == GTK_TYPE_ENTRY) + return 1; + else if (type == GTK_TYPE_LAYOUT) + return 2; + else if (type == GTK_TYPE_TOOLTIP) + return 3; + else if (type == GTK_TYPE_SCROLLBAR) + return 4; + else if (type == GTK_TYPE_SCROLLED_WINDOW) + return 5; + else if (type == GTK_TYPE_CHECK_BUTTON) + return 6; + else if (type == GTK_TYPE_RADIO_BUTTON) + return 7; + else if (type == GTK_TYPE_FRAME) + return 8; + else if (type == GTK_TYPE_LABEL) + return 9; + else if (type == GTK_TYPE_BUTTON) + return 10; + else if (type == GTK_TYPE_WINDOW) + return 11; + else + return 0; +} + +#ifdef GTK3 + +const char *gt_get_style_class(GType type) +{ + static const char *_class[] = { + GTK_STYLE_CLASS_DEFAULT, GTK_STYLE_CLASS_ENTRY, GTK_STYLE_CLASS_BACKGROUND, GTK_STYLE_CLASS_TOOLTIP, + GTK_STYLE_CLASS_SCROLLBAR, GTK_STYLE_CLASS_DEFAULT, GTK_STYLE_CLASS_CHECK, GTK_STYLE_CLASS_RADIO, + GTK_STYLE_CLASS_FRAME, GTK_STYLE_CLASS_BACKGROUND, GTK_STYLE_CLASS_BUTTON, GTK_STYLE_CLASS_DEFAULT + }; + + int index = type_to_index(type); + if (index < 0) + return NULL; + else + return _class[index]; +} + +GtkStyleContext *gt_get_style(GType type) +{ + int index = type_to_index(type); + if (index < 0) + return NULL; + + if ((_style_context_loaded & (1 << index)) == 0) + { + GtkStyleContext *style = gtk_style_context_new(); + GtkWidgetPath *path = gtk_widget_path_new(); + const char *klass = gt_get_style_class(type); + + if (klass) + gtk_style_context_add_class(style, klass); + + gtk_widget_path_append_type(path, type); +#if GTK_CHECK_VERSION(3, 20, 0) + gtk_widget_path_iter_set_object_name(path, -1, klass); +#endif + gtk_style_context_set_path(style, path); + //gtk_widget_path_unref(path); + + _style_context[index] = style; + _style_context_loaded |= (1 << index); + } + + return _style_context[index]; +} + +#else + +static GtkStyle *get_style(const char *name, GType type) +{ + GtkSettings *set = gtk_settings_get_default(); + GtkStyle* st = gtk_rc_get_style_by_paths(set, NULL, name, type); + if (!st) st = gtk_widget_get_default_style(); + return st; +} + +static GtkStyle *get_widget_style(const char *name) +{ + GtkSettings *set = gtk_settings_get_default(); + GtkStyle* st = gtk_rc_get_style_by_paths(set, name, NULL, G_TYPE_NONE); + if (!st) st = gtk_widget_get_default_style(); + return st; +} + +GtkStyle *gt_get_style(GType type) +{ + int index = type_to_index(type); + if (index < 0) + return NULL; + + if ((_style_loaded & (1 << index)) == 0) + { + if (type == GTK_TYPE_TOOLTIP) + _style[index] = get_widget_style("gtk-tooltip"); + else + _style[index] = get_style(g_type_name(type), type); + + _style_loaded |= (1 << index); + } + + return _style[index]; +} + +#endif + +void gt_get_style_property(GType type, const char *name, void *pvalue) +{ +#ifdef GTK3 + gtk_style_context_get_style(gt_get_style(type), name, pvalue, NULL); +#else + gtk_style_get(gt_get_style(type), type, name, pvalue, (char *)NULL); +#endif +} + + +#ifdef GTK3 + +// Draw a styled border + +void gt_draw_border(cairo_t *cr, GtkStyleContext *st, GtkStateFlags state, int border, gColor color, int x, int y, int w, int h, bool bg) +{ + if (border == BORDER_NONE) + return; + + if (w < 2 || h < 2) + return; + + if (border == BORDER_PLAIN) + { + gt_cairo_draw_rect(cr, x, y, w, h, color); + return; + } + + if (strcmp(gApplication::getStyleName(), "oxygen-gtk") == 0) + { + x -= 3; + w += 6; + } + + gboolean interior_focus; + + if (border == BORDER_RAISED) + { + st = gt_get_style(GTK_TYPE_BUTTON); + gtk_style_context_set_state(st, state); + //gtk_style_context_add_class(st, GTK_STYLE_CLASS_BUTTON); + gtk_render_frame(st, cr, x, y, w, h); + } + else if (border == BORDER_SUNKEN) + { + st = gt_get_style(GTK_TYPE_ENTRY); + gtk_style_context_set_state(st, state); + //gtk_style_context_add_class(st, GTK_STYLE_CLASS_ENTRY); + if (bg) + gtk_render_background(st, cr, x, y, w, h); + gtk_render_frame(st, cr, x, y, w, h); + } + else if (border == BORDER_ETCHED) + { + st = gt_get_style(GTK_TYPE_FRAME); + gtk_style_context_set_state(st, state); + //gtk_style_context_add_class(st, GTK_STYLE_CLASS_FRAME); + gtk_render_frame(st, cr, x, y, w, h); + } + + if (state & GTK_STATE_FLAG_FOCUSED) + { + gtk_style_context_get_style (st, "interior-focus", &interior_focus, NULL); + if (!interior_focus) + gtk_render_focus(st, cr, x, y, w, h); + } +} +#endif + +#ifdef GTK3 + +void gt_widget_set_color(GtkWidget *widget, bool fg, gColor color, const char *name, const GdkRGBA *def_color) +{ + if (color == COLOR_DEFAULT) + { + if (name) + gtk_widget_override_symbolic_color(widget, name, def_color); + + if (fg) + gtk_widget_override_color(widget, GTK_STATE_FLAG_NORMAL, NULL); + else + gtk_widget_override_background_color(widget, GTK_STATE_FLAG_NORMAL, NULL); + } + else + { + GdkRGBA rgba; + gt_from_color(color, &rgba); + + if (name) + gtk_widget_override_symbolic_color(widget, name, &rgba); + + if (fg) + gtk_widget_override_color(widget, GTK_STATE_FLAG_NORMAL, &rgba); + else + gtk_widget_override_background_color(widget, GTK_STATE_FLAG_NORMAL, &rgba); + } +} + +bool gt_style_lookup_color(GtkStyleContext *style, const char **names, const char **pname, GdkRGBA *rgba) +{ + const char **p = names; + const char *name; + + while((name = *p++)) + { + if (gtk_style_context_lookup_color(style, name, rgba)) + break; + } + + if (name) + { + if (pname) + *pname = name; + return FALSE; + } + else + return TRUE; +} + +#endif + +bool gt_grab(GtkWidget *widget, bool owner_event, guint32 time) +{ + GdkWindow *win = gtk_widget_get_window(widget); + int ret; + +#ifdef GTK3 + + GdkDevice *pointer = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default())); + GdkDevice *keyboard = gdk_device_get_associated_device(pointer); + + ret = gdk_device_grab(pointer, win, GDK_OWNERSHIP_APPLICATION, owner_event, + (GdkEventMask)(GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK), + gdk_window_get_cursor(win), + time); + + if (ret == GDK_GRAB_SUCCESS) + { + ret = gdk_device_grab(keyboard, win, GDK_OWNERSHIP_APPLICATION, owner_event, + (GdkEventMask)(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK), + NULL, time); + + if (ret == GDK_GRAB_SUCCESS) + return FALSE; + + gdk_device_ungrab(pointer, GDK_CURRENT_TIME); + } + +#else + + ret = gdk_pointer_grab(win, owner_event, + (GdkEventMask)(GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK), + NULL, +#if GTK_CHECK_VERSION(2, 18, 0) + gdk_window_get_cursor(win), +#else + NULL, +#endif + time); + + if (ret == GDK_GRAB_SUCCESS) + { + ret = gdk_keyboard_grab(win, owner_event, time); + + if (ret == GDK_GRAB_SUCCESS) + return FALSE; + + gdk_pointer_ungrab(GDK_CURRENT_TIME); + } + +#endif + + fprintf(stderr, "gb.gtk: warning: grab failed: %d\n", ret); + return TRUE; +} + +void gt_ungrab(void) +{ +#ifdef GTK3 + GdkDevice *pointer = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default())); + GdkDevice *keyboard = gdk_device_get_associated_device(pointer); + + gdk_device_ungrab(pointer, GDK_CURRENT_TIME); + gdk_device_ungrab(keyboard, GDK_CURRENT_TIME); +#else + gdk_pointer_ungrab(GDK_CURRENT_TIME); + gdk_keyboard_ungrab(GDK_CURRENT_TIME); +#endif +} + +#ifdef GTK3 +void gt_widget_reparent(GtkWidget *widget, GtkWidget *new_parent) +{ + GtkWidget *parent = gtk_widget_get_parent(widget); + + if (parent == new_parent) + return; + + g_object_ref(widget); + gtk_container_remove(GTK_CONTAINER(parent), widget); + gtk_container_add(GTK_CONTAINER(new_parent), widget); + g_object_unref(widget); +} +#endif diff --git a/gb.gtk/src/gtools.h b/gb.gtk/src/gtools.h new file mode 100644 index 00000000..d20b81c8 --- /dev/null +++ b/gb.gtk/src/gtools.h @@ -0,0 +1,220 @@ +/*************************************************************************** + + gtools.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GTOOLS_H +#define __GTOOLS_H + +#include "widgets.h" +#include "gpicture.h" +#include "gcontrol.h" +#include "gb.paint.h" + +void stub(const char *function); + +void gt_exit(); + +void g_stradd(gchar **res, const gchar *s); + +// Frees a string later + +char *gt_free_later(char *ptr); + +// Parse a shortcut string + +void gt_shortcut_parse(char *shortcut, guint *key, GdkModifierType *mods); + +// Converts HTML to Pango format + +char* gt_html_to_pango_string(const char *html, int len_html, bool newline); + +// Converts to/from GTK+ alignment + +int gt_to_alignment(double halign, double valign = 0.5); +double gt_from_alignment(int align, bool vertical = false); + +// Global signal handlers + +gboolean gcb_focus_in(GtkWidget *widget, GdkEventFocus *event, gControl *data); +gboolean gcb_focus_out(GtkWidget *widget, GdkEventFocus *event, gControl *data); + +// Where to scroll to ensure that a specific area is visible + +typedef + struct { + int clientWidth; + int clientHeight; + int scrollX; + int scrollY; + int scrollWidth; + int scrollHeight; + } + GtEnsureVisible; + +void gt_ensure_visible(GtEnsureVisible *arg, int x, int y, int w, int h); + +#define GT_NORMALIZE(x, y, w, h, sx, sy, sw, sh, width, height) \ + if (w < 0) w = width; \ + if (h < 0) h = height; \ + if (sw < 0) sw = width; \ + if (sh < 0) sh = height; \ + if (sx >= (width) || sy >= (height) || sw <= 0 || sh <= 0) \ + return; \ + if (sx < 0) x -= sx, sx = 0; \ + if (sy < 0) y -= sy, sy = 0; \ + if (sw > ((width) - sx)) \ + sw = ((width) - sx); \ + if (sh > ((height) - sy)) \ + sh = ((height) - sy); + +#ifndef GTK3 +void gt_pixmap_fill(GdkPixmap *pix, gColor col, GdkGC *gc); +#endif + +// Creates a disabled version of a pixbuf + +GdkPixbuf *gt_pixbuf_create_disabled(GdkPixbuf *img); +#ifndef GTK3 +void gt_pixbuf_render_pixmap_and_mask(GdkPixbuf *pixbuf, GdkPixmap **pixmap_return, GdkBitmap **mask_return, int alpha_threshold); +#endif +void gt_pixbuf_make_alpha(GdkPixbuf *pixbuf, gColor color); +void gt_pixbuf_make_gray(GdkPixbuf *pixbuf); + +// Enable/disable warning messages + +void gt_disable_warnings(bool disable); + +// Initialize a GtkCellRendererText from a font + +void gt_set_cell_renderer_text_from_font(GtkCellRendererText *cell, gFont *font); + +// Initialize a PangoLayout from a font + +void gt_set_layout_from_font(PangoLayout *layout, gFont *font, int dpi = 0); +void gt_add_layout_from_font(PangoLayout *layout, gFont *font, int dpi = 0); + +#define gt_pango_to_pixel(_pango) (((_pango) + (PANGO_SCALE / 2)) / PANGO_SCALE) + +// Grab a window + +gPicture *gt_grab_window(GdkWindow *win, int x = 0, int y = 0, int w = 0, int h = 0); + +// Compute the alignment of a PangoLayout + +void gt_layout_alignment(PangoLayout *layout, float w, float h, float *tw, float *th, int align, float *offX, float *offY); + +#if GTK_CHECK_VERSION(2, 18, 0) +#else +void gtk_widget_set_can_focus(GtkWidget *widget, gboolean can_focus); +void gtk_widget_get_allocation(GtkWidget *widget, GtkAllocation *allocation); +#endif + +void gt_lower_widget(GtkWidget *widget); + +#if GTK_CHECK_VERSION(2, 22, 0) +#else +int gdk_device_get_source(GdkDevice *device); +GtkWidget *gtk_window_group_get_current_grab(GtkWindowGroup *window_group); +#endif + +// Cairo support + +cairo_surface_t *gt_cairo_create_surface_from_pixbuf(const GdkPixbuf *pixbuf); + +void gt_cairo_set_source_color(cairo_t *cr, GB_COLOR color); +void gt_cairo_draw_rect(cairo_t *cr, int x, int y, int w, int h, GB_COLOR color); +void gt_cairo_draw_pixbuf(cairo_t *cr, GdkPixbuf *pixbuf, float x, float y, float w, float h, float opacity, GB_RECT *source); + +// Color functions + +gColor get_gdk_color(GdkColor *gcol); +#ifdef GTK3 +void fill_gdk_color(GdkColor *gcol, gColor color); +#else +void fill_gdk_color(GdkColor *gcol, gColor color, GdkColormap *cmap = NULL); +#endif +gColor get_gdk_text_color(GtkWidget *wid, bool enabled); +void set_gdk_text_color(GtkWidget *wid,gColor color); +gColor get_gdk_base_color(GtkWidget *wid, bool enabled); +void set_gdk_base_color(GtkWidget *wid,gColor color); +gColor get_gdk_fg_color(GtkWidget *wid, bool enabled); +void set_gdk_fg_color(GtkWidget *wid,gColor color); +gColor get_gdk_bg_color(GtkWidget *wid, bool enabled); +void set_gdk_bg_color(GtkWidget *wid,gColor color); + +void gt_color_to_rgb(gColor color, int *r, int *g, int *b); +gColor gt_rgb_to_color(int r, int g, int b); +void gt_color_to_rgba(gColor color, int *r, int *g, int *b, int *a); +gColor gt_rgba_to_color(int r, int g, int b, int a); +void gt_rgb_to_hsv(int r, int g, int b, int *h, int *s, int *v); +void gt_hsv_to_rgb(int h, int s, int v, int *r, int *g, int *b); +void gt_color_to_frgba(gColor color, double *r, double *g, double *b, double *a); +gColor gt_frgba_to_color(double r, double g, double b, double a); + +#ifdef GTK3 + +void gt_from_color(gColor color, GdkRGBA *rgba); +gColor gt_to_color(GdkRGBA *rgba); +void gt_to_css_color(char *css, gColor color); + +void gt_widget_set_color(GtkWidget *widget, bool fg, gColor color, const char *name = NULL, const GdkRGBA *def_color = NULL); +bool gt_style_lookup_color(GtkStyleContext *style, const char **names, const char **pname, GdkRGBA *rgba); + +#endif + +// Draw a control border + +#ifdef GTK3 +void gt_draw_border(cairo_t *cr, GtkStyleContext *st, GtkStateFlags state, int border, gColor color, int x, int y, int w, int h, bool bg = false); +#endif + +// Style management + +#ifdef GTK3 +GtkStyleContext *gt_get_style(GType type); +#else +GtkStyle *gt_get_style(GType type); +#endif + +void gt_get_style_property(GType type, const char *name, void *pvalue); + +void gMnemonic_correctText(char *st,char **buf); +guint gMnemonic_correctMarkup(char *st,char **buf); +void gMnemonic_returnText(char *st,char **buf); + +/*#ifdef GTK3 +int gt_get_preferred_width(GtkWidget *widget); +#endif*/ + +#define gt_get_control(_widget) ((gControl *)g_object_get_data(G_OBJECT(_widget), "gambas-control")) +#define gt_register_control(_widget, _control) g_object_set_data(G_OBJECT(_widget), "gambas-control", _control) + +bool gt_grab(GtkWidget *widget, bool owner_event, guint32 time); +void gt_ungrab(); + +#ifdef GTK3 +void gt_widget_reparent(GtkWidget *widget, GtkWidget *new_parent); +#else +#define gt_widget_reparent gtk_widget_reparent +#endif + +#endif diff --git a/gb.gtk/src/gtrayicon.cpp b/gb.gtk/src/gtrayicon.cpp new file mode 100644 index 00000000..a06aa925 --- /dev/null +++ b/gb.gtk/src/gtrayicon.cpp @@ -0,0 +1,289 @@ +/*************************************************************************** + + gtrayicon.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "widgets.h" + +#include +#include "x11.h" +#include "gapplication.h" +#include "gmouse.h" +#include "gtrayicon.h" + +#include "gb.form.trayicon.h" + +int gTrayIcon::_visible_count = 0; + +/************************************************************************* + +gTrayIcon + +**************************************************************************/ + +static gboolean cb_button_press(GtkStatusIcon *plug, GdkEventButton *event, gTrayIcon *data) +{ + if (gApplication::loopLevel() > data->loopLevel()) return false; + + gApplication::updateLastEventTime(); + + if (data->onClick) + { + gMouse::validate(); + gMouse::setMouse((int)event->x, (int)event->y, (int)event->x_root, (int)event->y_root, event->button, event->state); + if (event->type == GDK_BUTTON_PRESS) + data->onClick(data, event->button); + /*else if (event->type == GDK_2BUTTON_PRESS) + data->onDoubleClick(data);*/ + gMouse::invalidate(); + } + + /*if (event->button == 3) + if (data->onMenu) + data->onMenu(data);*/ + + return false; +} + +static gboolean cb_menu(GtkStatusIcon *plug, guint button, guint activate_time, gTrayIcon *data) +{ + if (gApplication::loopLevel() > data->loopLevel()) return false; + + gApplication::updateLastEventTime(); + + if (data->onMenu) + data->onMenu(data); + + return false; +} + +static gboolean cb_scroll(GtkStatusIcon *plug, GdkEventScroll *event, gTrayIcon *data) +{ + GdkScrollDirection dir; + int dt = 0; + int ort = 0; + + if (gApplication::loopLevel() > data->loopLevel()) return false; + + gApplication::updateLastEventTime(); + + if (data->onScroll) + { + dir = event->direction; + +#ifdef GTK3 + if (dir == GDK_SCROLL_SMOOTH) + return false; + /*{ + gdouble dx = 0, dy = 0; + gdk_event_get_scroll_deltas((GdkEvent *)event, &dx, &dy); + if (fabs(dy) > fabs(dx)) + dir = (dy < 0) ? GDK_SCROLL_UP : GDK_SCROLL_DOWN; + else + dir = (dx < 0) ? GDK_SCROLL_LEFT : GDK_SCROLL_RIGHT; + }*/ +#endif + + switch (dir) + { + case GDK_SCROLL_UP: dt=1; ort=1; break; + case GDK_SCROLL_DOWN: dt=-1; ort=1; break; + case GDK_SCROLL_LEFT: dt=-1; ort=0; break; + case GDK_SCROLL_RIGHT: default: dt=1; ort=0; break; + } + + gMouse::validate(); + gMouse::setMouse((int)event->x, (int)event->y, (int)event->x_root, (int)event->y_root, 0, event->state); + gMouse::setWheel(dt, ort); + data->onScroll(data); + gMouse::invalidate(); + } + + return false; +} + + +GList *gTrayIcon::trayicons = NULL; +gPicture *gTrayIcon::_default_icon = NULL; + +gTrayIcon::gTrayIcon() +{ + plug = NULL; + buftext = NULL; + _icon = NULL; + _loopLevel = 0; + + onClick = NULL; + onScroll = NULL; + onMenu = NULL; + + trayicons = g_list_append(trayicons, (gpointer)this); +} + +gTrayIcon::~gTrayIcon() +{ + setVisible(false); + + gPicture::assign(&_icon); + + if (buftext) + { + g_free(buftext); + buftext = NULL; + } + + trayicons = g_list_remove(trayicons, (gpointer)this); + + if (!trayicons && _default_icon) + { + delete _default_icon; + _default_icon = NULL; + } + + if (onDestroy) (*onDestroy)(this); +} + +gPicture *gTrayIcon::defaultIcon() +{ + if (!_default_icon) + { + GdkPixbuf *img = gdk_pixbuf_new_from_data(_default_trayicon_data, GDK_COLORSPACE_RGB, TRUE, 8, + DEFAULT_TRAYICON_WIDTH, DEFAULT_TRAYICON_HEIGHT, + DEFAULT_TRAYICON_WIDTH * sizeof(int), NULL, NULL); + _default_icon = new gPicture(img); + } + + return _default_icon; +} + +void gTrayIcon::updatePicture() +{ + GdkPixbuf *pixbuf; + + if (!plug) + return; + + if (_icon) + pixbuf = _icon->getPixbuf(); + else + pixbuf = defaultIcon()->getPixbuf(); + + gtk_status_icon_set_from_pixbuf(plug, pixbuf); + + _iconw = gdk_pixbuf_get_width(pixbuf); + _iconh = gdk_pixbuf_get_height(pixbuf); +} + +void gTrayIcon::setPicture(gPicture *picture) +{ + gPicture::assign(&_icon, picture); + updatePicture(); +} + +char* gTrayIcon::toolTip() +{ + return buftext; +} + +void gTrayIcon::updateTooltip() +{ + if (!plug) + return; + + gtk_status_icon_set_tooltip_text(plug, buftext); +} + +void gTrayIcon::setToolTip(char* vl) +{ + if (buftext) + g_free(buftext); + + buftext = vl && *vl ? g_strdup(vl) : NULL; + updateTooltip(); +} + +bool gTrayIcon::isVisible() +{ + return (bool)plug; +} + +static void hide_icon(GtkStatusIcon *plug) +{ + gtk_status_icon_set_visible(plug, FALSE); + g_object_unref(plug); +} + +void gTrayIcon::setVisible(bool vl) +{ + if (vl) + { + if (!plug) + { + _loopLevel = gApplication::loopLevel() + 1; + + plug = gtk_status_icon_new(); + + updatePicture(); + updateTooltip(); + + #ifdef GDK_WINDOWING_X11 + // Needed, otherwise the icon does not appear in Gnome or XFCE notification area! + XSizeHints hints; + hints.flags = PMinSize; + hints.min_width = _iconw; + hints.min_height = _iconh; + XSetWMNormalHints(gdk_x11_display_get_xdisplay(gdk_display_get_default()), gtk_status_icon_get_x11_window_id(plug), &hints); + #endif + + gtk_status_icon_set_visible(plug, TRUE); + + g_signal_connect(G_OBJECT(plug), "button-press-event", G_CALLBACK(cb_button_press), (gpointer)this); + g_signal_connect(G_OBJECT(plug), "popup-menu", G_CALLBACK(cb_menu), (gpointer)this); + g_signal_connect(G_OBJECT(plug), "scroll-event", G_CALLBACK(cb_scroll), (gpointer)this); + + _visible_count++; + + usleep(10000); // BUG: Embedding too fast sometimes fails with GTK+ + } + } + else + { + if (plug) + { + GB.Post((void (*)())hide_icon, (intptr_t)plug); + plug = NULL; + _visible_count--; + } + } +} + +void gTrayIcon::exit() +{ + gTrayIcon *icon; + + while((icon = get(0))) + delete icon; +} + +bool gTrayIcon::hasSystemTray() +{ + return X11_get_system_tray() != 0; +} diff --git a/gb.gtk/src/gtrayicon.h b/gb.gtk/src/gtrayicon.h new file mode 100644 index 00000000..c371c1c6 --- /dev/null +++ b/gb.gtk/src/gtrayicon.h @@ -0,0 +1,84 @@ +/*************************************************************************** + + gtrayicon.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GTRAYICON_H +#define __GTRAYICON_H + +class gTrayIcon +{ +public: +//"Properties" + gTrayIcon(); + ~gTrayIcon(); + + void *hFree; + + char *key(); + gPicture* picture() { return _icon; } + void setPicture(gPicture *pic); + char* toolTip(); + void setToolTip(char *txt); + bool isVisible(); + void setVisible(bool vl); + +//"Methods" + void destroy(); + void show() { setVisible(true); } + void hide() { setVisible(false); } + int loopLevel() { return _loopLevel; } + +//"Events" + void (*onClick)(gTrayIcon *sender, int button); + void (*onScroll)(gTrayIcon *sender); + void (*onMenu)(gTrayIcon *sender); + void (*onDestroy)(gTrayIcon *sender); + +//"Static" + + static int count() { return g_list_length(trayicons); } + static int visibleCount() { return _visible_count; } + static gTrayIcon *get(int index) { return (gTrayIcon *)g_list_nth_data(trayicons, index); } + static void exit(); + static gPicture *defaultIcon(); + static bool hasSystemTray(); + +//"Private" + GtkStatusIcon *plug; + gPicture *_icon; + int _iconw, _iconh; + char *buftext; + bool onHide; + int _loopLevel; + gPicture *getIcon() { return _icon ? _icon : defaultIcon(); } + void updateTooltip(); + void updatePicture(); + + static GList *trayicons; + static gPicture *_default_icon; + +private: + + static int _visible_count; +}; + +#endif diff --git a/gb.gtk/src/gtree.cpp b/gb.gtk/src/gtree.cpp new file mode 100644 index 00000000..f34b08f5 --- /dev/null +++ b/gb.gtk/src/gtree.cpp @@ -0,0 +1,1291 @@ +/*************************************************************************** + + gtree.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include +#include +#include + +#include "gpicture.h" +#include "gtree.h" + +#if GTK_CHECK_VERSION(2, 12, 0) + +#define HIDDEN_COL 0 + +#else + +#define HIDDEN_COL 1 + +#endif + +static GtkTreeViewColumn* gt_tree_view_find_column(GtkTreeView *tree, int ind) +{ + GtkTreeViewColumn *col=NULL; + GList *cols; + GList *iter; + + if (!tree) return NULL; + cols=gtk_tree_view_get_columns(GTK_TREE_VIEW(tree)); + if (!cols) return NULL; + iter=g_list_nth(cols,(guint)ind + HIDDEN_COL); + if (iter) { + col=(GtkTreeViewColumn*)iter->data; + } + g_list_free(cols); + + return col; +} + +/* +static int gt_tree_view_find_index(GtkTreeView *tree, GtkTreeViewColumn *column) +{ + int index; + GList *cols; + GList *iter; + + if (!tree) return -1; + cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(tree)); + if (!cols) return -1; + + iter = cols; + index = 0; + if (HIDDEN_COL) + iter = g_list_next(iter); + + while (iter) + { + if (iter->data == (gpointer)column) + break; + iter = g_list_next(iter); + index++; + } + + g_list_free(cols); + + if (!iter) + index = -1; + + return index; +} +*/ + +/************************************************************ + +gTreeCell + +*************************************************************/ + +gTreeCell::gTreeCell() +{ + _text = NULL; + _picture = NULL; +} + +gTreeCell::~gTreeCell() +{ + setText(NULL); + setPicture(NULL); +} + +void gTreeCell::setText(const char *vl) +{ + if (_text) + g_free(_text); + + _text = vl ? g_strdup(vl) : NULL; +} + +void gTreeCell::setPicture(gPicture *vl) +{ + gPicture::assign(&_picture, vl); +} + +/************************************************************ + +gTreeRow + +*************************************************************/ + +gTreeRow::gTreeRow(gTree *tr, char *key, GtkTreeIter *iter) +{ + int count; + + data = NULL; + dataiter = iter; + tree = tr; + _key = key; + //_expanded = false; + //_editable = tr->isEditable(); + + count = tree->columnCount(); + + while (count > 0) + { + count--; + data = g_list_prepend(data, (gpointer)new gTreeCell()); + } + + if (data) + data = g_list_reverse(data); + + //fprintf(stderr, "new key: (%p) %s\n", _key, _key); +} + +gTreeRow::~gTreeRow() +{ + GList *iter=NULL; + + if (tree->onRemove) + (*tree->onRemove)(tree, _key); + + if (dataiter) gtk_tree_iter_free(dataiter); + if (data) iter=g_list_first(data); + + while (iter) + { + delete (gTreeCell*)iter->data; + iter=g_list_next(iter); + } + + if (data) g_list_free(data); + + //fprintf(stderr, "free key: (%p) %s\n", _key, _key); + g_free(_key); +} + +void gTreeRow::add() +{ + data = g_list_append(data, (gpointer)new gTreeCell()); +} + +void gTreeRow::remove() +{ + GList *iter=NULL; + gTreeCell *cell; + + if (!data) return; + + iter = g_list_last(data); + cell = (gTreeCell *)iter->data; + data = g_list_remove(data, cell); + delete cell; +} + +/*int gTreeRow::children() +{ + GtkTreeIter iter; + int ct=1; + + if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(tree->store),&iter,dataiter)) return 0; + + while ( gtk_tree_model_iter_next(GTK_TREE_MODEL(tree->store),&iter) ) ct++; + return ct; +}*/ + +void gTreeRow::update() +{ + GtkTreePath *path; + + path=gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store),dataiter); + if (path) + { + gtk_tree_model_row_changed (GTK_TREE_MODEL(tree->store),path,dataiter); + gtk_tree_path_free(path); + } +} + +/*char* gTreeRow::parentKey() +{ + GtkTreeIter it; + char *key; + + if (!gtk_tree_model_iter_parent(GTK_TREE_MODEL(parent->store),&it,dataiter)) return NULL; + gtk_tree_model_get(GTK_TREE_MODEL(parent->store),&it,0,&key,-1); + return key; +}*/ + +/* +void gTreeRow::setExpanded(bool ex) +{ + GtkTreePath *path; + //char *current; + + //fprintf(stderr, "setExpanded: %s = %d\n", _key, ex); + + if (!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(tree->store),dataiter)) + { + _expanded = ex; + return; + } + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store),dataiter); + if (!path) + return; + + if (ex) + gtk_tree_view_expand_row(GTK_TREE_VIEW(tree->widget), path, false); + else + gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree->widget), path); + + gtk_tree_path_free(path); +} + +void gTreeRow::setExpanded() +{ + tree->view->lock(); + //_expanded = !_expanded; + setExpanded(_expanded); + tree->view->unlock(); +} + +bool gTreeRow::isExpanded() +{ +// GtkTreePath *path; +// bool real = false; +// +// path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store),dataiter); +// if (path) +// { +// real = gtk_tree_view_row_expanded(GTK_TREE_VIEW(tree->widget),path); +// gtk_tree_path_free(path); +// } + + //fprintf(stderr, "isExpanded: %s: %d (%d)\n", _key, _expanded, real); + + return _expanded; +} +*/ + +#if 0 +void gTreeRow::updateExpanded(bool ex) +{ + //GtkTreePath *path; + + /*path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store),dataiter); + if (!path) + return; + + _expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(tree->widget),path); + gtk_tree_path_free(path);*/ + + _expanded = ex; + + //fprintf(stderr, "updateExpanded: %s = %d\n", _key, _expanded); +} +#endif + +gTreeCell* gTreeRow::get(int ind) +{ + GList *iter; + + if (!data) + return NULL; + + iter = g_list_nth(data, ind); + if (!iter) + return NULL; + + return (gTreeCell*)iter->data; +} + +void gTreeRow::ensureVisible() +{ + GtkTreePath *path; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store),dataiter); + if (path) + { + gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(tree->widget), path, NULL, false, 0.0, 0.0); + gtk_tree_path_free(path); + } +} + +#if 0 +char *gTreeRow::next() +{ + GtkTreePath* path; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store), dataiter); + if (!path) + return NULL; + + gtk_tree_path_next(path); + return tree->pathToKey(path); +} + +char *gTreeRow::prev() +{ + GtkTreePath* path; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store), dataiter); + if (!path) + return NULL; + + gtk_tree_path_prev(path); + return tree->pathToKey(path); +} + +char *gTreeRow::child() +{ + GtkTreePath* path; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store), dataiter); + if (!path) + return NULL; + + gtk_tree_path_down(path); + return tree->pathToKey(path); +} + +char *gTreeRow::last() +{ + GtkTreePath* path; + GtkTreeIter iter, save_iter; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store), dataiter); + if (!path) + return NULL; + + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(tree->store), &iter, path)) + return NULL; + + gtk_tree_path_free(path); + + //count = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(tree->store), NULL); + //if (!count) return NULL; + + //while (--count) + for(;;) + { + save_iter = iter; + if (!gtk_tree_model_iter_next(GTK_TREE_MODEL(tree->store), &iter)) + { + iter = save_iter; + break; + } + } + + return tree->iterToKey(&iter); +} + +char *gTreeRow::parent() +{ + GtkTreeIter *iter = gtk_tree_iter_copy(dataiter); + char *key; + //GtkTreePath* path; + + if (!gtk_tree_model_iter_parent(GTK_TREE_MODEL(tree->store), iter, dataiter)) + key = NULL; + else + key = tree->iterToKey(iter); + + gtk_tree_iter_free(iter); + return key; + + /*path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store), dataiter); + if (!path) + return NULL; + + if (!gtk_tree_path_up(path)) + return NULL; + + return tree->pathToKey(path);*/ +} + +char *gTreeRow::above() +{ + char *key, *key2; + + key = prev(); + if (!key) + return parent(); + + for(;;) + { + key2 = (*tree)[key]->child(); + if (!key2) + break; + + key = key2; + + for(;;) + { + key2 = (*tree)[key]->next(); + if (!key2) + break; + key = key2; + } + } + + return key; +} + +char *gTreeRow::below() +{ + char *key, *key2; + + key = child(); + if (key) + return key; + + key = next(); + if (key) + return key; + + key = parent(); + + for(;;) + { + if (!key) + return NULL; + + key2 = (*tree)[key]->next(); + if (key2) + return key2; + + key = (*tree)[key]->parent(); + } +} + + +void gTreeRow::rect(int *x, int *y, int *w, int *h) +{ + GtkTreePath* path; + GdkRectangle rect; + GtkTreeViewColumn *column; + gint depth; + gint size; + gint margin; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store), dataiter); + if (!path) + return; + + column = gt_tree_view_find_column(GTK_TREE_VIEW(tree->widget), tree->columnCount() - 1); + gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tree->widget), path, column, &rect); + depth = gtk_tree_path_get_depth(path); + gtk_tree_path_free(path); + + gtk_widget_style_get (tree->widget, + "expander-size", &size, + "vertical-separator", &margin, + (void *)NULL); + + size += 4; // Constant that is hard-coded into GTK+ source code + + if (!tree->hasExpanders()) + depth--; + + *x = depth * size; + *w = rect.x + rect.width - *x; + *h = rect.height + margin; + *y = rect.y; +} + +void gTreeRow::moveFirst() +{ + gtk_tree_store_move_after(tree->store, dataiter, NULL); +} + +void gTreeRow::moveAfter(char *key) +{ + gTreeRow *row; + + if (!key || !*key) + { + moveFirst(); + return; + } + + row = tree->getRow(key); + if (!row) + return; + if (strcmp(row->parent(), parent())) + return; + gtk_tree_store_move_after(tree->store, dataiter, row->dataiter); +} + +void gTreeRow::moveLast() +{ + gtk_tree_store_move_before(tree->store, dataiter, NULL); +} + +void gTreeRow::moveBefore(char *key) +{ + gTreeRow *row; + + if (!key || !*key) + { + moveLast(); + return; + } + + row = tree->getRow(key); + if (!row) + return; + if (strcmp(row->parent(), parent())) + return; + gtk_tree_store_move_before(tree->store, dataiter, row->dataiter); +} + +void gTreeRow::startRename() +{ + GtkTreePath* path; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store), dataiter); + if (!path) + return; + + //tree->view->setFocus(); + gtk_tree_view_set_cursor(GTK_TREE_VIEW(tree->widget), path, gt_tree_view_find_column(GTK_TREE_VIEW(tree->widget), 0), true); + gtk_tree_path_free(path); +} +#endif + +/************************************************************ + +gTree + +*************************************************************/ + +static gboolean gTree_equal(char *a,char *b) +{ + return !strcasecmp(a,b); +} + +#if 0 +static void cb_tree_edited(GtkCellRendererText *renderer, gchar *spath, gchar *new_text, gTree *tree) +{ + gTreeView *view = tree->view; + + if (!tree->_edited_row) + return; + + view->setItemText(tree->_edited_row, new_text); + view->emit(SIGNAL(view->onRename), tree->_edited_row); + tree->_edited_row = NULL; +} + +static void cb_tree_started(GtkCellRenderer *renderer, GtkCellEditable *editable, gchar *spath, gTree *tree) +{ + GtkTreePath *path = gtk_tree_path_new_from_string(spath); + if (path) + tree->_edited_row = tree->pathToKey(path); + else + tree->_edited_row = NULL; +} + +static void cb_tree_canceled(GtkCellRendererText *renderer, gTree *tree) +{ + gTreeView *view = tree->view; + view->emit(SIGNAL(view->onCancel), tree->_edited_row); + tree->_edited_row = NULL; +} + +static void cb_column_clicked(GtkTreeViewColumn *col, gTree *tree) +{ + int index = gt_tree_view_find_index(GTK_TREE_VIEW(tree->widget), col); + + if (index == tree->getSortColumn()) + tree->setSortAscending(!tree->isSortAscending()); + else + tree->setSortColumn(index); +} +#endif + +static gint tree_compare(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gTree *tree) +{ + bool def = true; + int comp; + char *ka = tree->iterToKey(a); + char *kb = tree->iterToKey(b); + const char *ta, *tb; + + //fprintf(stderr, "ka = '%s' kb = '%s'\n", ka, kb); + + //if (tree->view && tree->view->onCompare) + // def = tree->view->onCompare(tree->view, ka, kb, &comp); + + if (def) + { + ta = tree->getRow(ka)->get(tree->getSortColumn())->text(); + if (!ta) ta = ""; + tb = tree->getRow(kb)->get(tree->getSortColumn())->text(); + if (!tb) tb = ""; + + //fprintf(stderr, "ta = '%s' tb = '%s'\n", ta, tb); + + comp = g_utf8_collate(ta, tb); + } + + if (!tree->isSortAscending()) + comp = (-comp); + + return comp; +} + + +void gTree::showExpanders() +{ + #if GTK_CHECK_VERSION(2, 12, 0) + gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(widget), true); + #else + gtk_tree_view_set_expander_column(GTK_TREE_VIEW(widget), gt_tree_view_find_column(GTK_TREE_VIEW(widget), 0)); + #endif + _expander = true; +} + +gTree::gTree() +{ + onRemove = NULL; + + datakey = g_hash_table_new((GHashFunc)g_str_hash,(GEqualFunc)gTree_equal); + + store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER); + widget = NULL; + + //_editable = false; + _resizable = false; + _auto_resize = false; + //_edited_row = NULL; + _sorted = false; + _ascending = true; + _sort_column = 0; + _init_sort = false; + _sort_dirty = false; + _expander = false; + _no_click = 0; + +#if 0 + if (view) + { + #if GTK_CHECK_VERSION(2, 12, 0) + gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(widget), false); + #else + GtkTreeViewColumn *column = gtk_tree_view_column_new(); + //gtk_tree_view_column_pack_start(column,rgraph,false); + //gtk_tree_view_column_pack_start(column,rtext,true); + //gtk_tree_view_column_set_cell_data_func(column,rgraph,(GtkTreeCellDataFunc)tree_cell_graph,(gpointer)this,NULL); + //gtk_tree_view_column_set_cell_data_func(column,rtext,(GtkTreeCellDataFunc)tree_cell_text,(gpointer)this,NULL); + gtk_tree_view_column_set_visible(column, false); + gtk_tree_view_append_column(GTK_TREE_VIEW(widget), column); + gtk_tree_view_set_expander_column(GTK_TREE_VIEW(widget), column); + #endif + + rgraph = gtk_cell_renderer_pixbuf_new(); + g_object_ref_sink(rgraph); + rtext = gtk_cell_renderer_text_new(); + g_object_ref_sink(rtext); + + g_signal_connect(G_OBJECT(rtext), "edited", G_CALLBACK(cb_tree_edited), (gpointer)this); + g_signal_connect(G_OBJECT(rtext), "editing-started", G_CALLBACK(cb_tree_started), (gpointer)this); + g_signal_connect(G_OBJECT(rtext), "editing-canceled", G_CALLBACK(cb_tree_canceled), (gpointer)this); + //addColumn(); + setAutoResize(true); + } +#endif +} + +gTree::~gTree() +{ + clear(); + g_hash_table_destroy(datakey); + /*if (view) + { + g_object_unref(rgraph); + g_object_unref(rtext); + }*/ +} + +void gTree::clear() +{ + char *key; + + while ((key = firstRow())) + removeRow(key); +} + +/* +void gTree::clear(char *parent) +{ + gTreeRow *row; + char *child; + + row = (*this)[parent]; + if (!row) + return; + + lock(); + while((child = row->child())) + removeRow(child); + unlock(); +} +*/ + +/* +int gTree::visibleWidth() +{ + GdkRectangle rect; + gint w,h; + + gtk_tree_view_get_visible_rect (GTK_TREE_VIEW(widget),&rect); + gtk_tree_view_convert_bin_window_to_widget_coords(GTK_TREE_VIEW(widget),rect.width,rect.height,&w,&h); + return w; +} + +int gTree::visibleHeight() +{ + GdkRectangle rect; + gint w,h; + + gtk_tree_view_get_visible_rect (GTK_TREE_VIEW(widget),&rect); + gtk_tree_view_convert_bin_window_to_widget_coords(GTK_TREE_VIEW(widget),rect.width,rect.height,&w,&h); + return h; +} +*/ + +char *gTree::iterToKey(GtkTreeIter *iter) +{ + char *key; + gtk_tree_model_get(GTK_TREE_MODEL(store), iter, 1, &key, -1); + return key; +} + +char *gTree::pathToKey(GtkTreePath *path, bool free) +{ + GtkTreeIter iter; + char *key; + + if (!path) return NULL; + + if (gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path)) + key = iterToKey(&iter); + else + key = NULL; + + if (free) gtk_tree_path_free(path); + + return key; +} + +char* gTree::cursor() +{ + GtkTreePath *path; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(widget), &path, NULL); + return pathToKey(path); +} + +void gTree::setCursor(char *vl) +{ + GtkTreePath *path; + gTreeRow *row = getRow(vl); + + if (!row) return; + path = gtk_tree_model_get_path(GTK_TREE_MODEL(store),row->dataiter); + if (path) + { + gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget),path,NULL,false); + gtk_tree_path_free(path); + } +} + +char *gTree::firstRow() +{ + GtkTreeIter iter; + + if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL(store), &iter)) + return NULL; + + return iterToKey(&iter); +} + +#if 0 +char *gTree::lastRow() +{ + GtkTreeIter iter; + gint count; + + if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) + return NULL; + + count = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL); + if (!count) return NULL; + + if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter, NULL, count - 1)) + return NULL; + + //while (--count) + // gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter); + + return iterToKey(&iter); +} +#endif + +bool gTree::rowExists(char *key) +{ + return (bool)(key && *key && g_hash_table_lookup(datakey,(gconstpointer)key)); +} + +bool gTree::rowSelected(char *key) +{ + GtkTreeSelection *sel; + gTreeRow *row=(gTreeRow*)g_hash_table_lookup(datakey,(gconstpointer)key); + if (!row) return false; + + sel=gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); + if (!sel) return false; + return gtk_tree_selection_iter_is_selected(sel,row->dataiter); + +} + +void gTree::setRowSelected(char *key,bool vl) +{ + GtkTreeSelection *sel; + gTreeRow *row = (gTreeRow*)g_hash_table_lookup(datakey, (gconstpointer)key); + if (!row) return; + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); + if (!sel) return; + + _no_click++; + if (vl) + gtk_tree_selection_select_iter(sel,row->dataiter); + else + gtk_tree_selection_unselect_iter(sel,row->dataiter); + _no_click--; +} + +/* +bool gTree::isRowEditable(char *key) +{ + gTreeRow *row = getRow(key); + if (!row) + return false; + else + return row->isEditable(); +} + +void gTree::setRowEditable(char *key, bool vl) +{ + gTreeRow *row = getRow(key); + if (!row) + return; + + row->setEditable(vl); +} +*/ + +int gTree::rowCount() +{ + return g_hash_table_size(datakey); +} + + +bool gTree::removeRow(char *key) +{ + gTreeRow *row; + + if (!key || !*key) return false; + + row=(gTreeRow*)g_hash_table_lookup(datakey, (gconstpointer)key); + if (!row) return false; + + //fprintf(stderr, "gTree::removeRow: '%s'\n", key); + + //while((child = row->child())) + // removeRow(child); + + //fprintf(stderr, "gTree::removeRow: '%s' removed\n", key); + + g_hash_table_remove (datakey, (gconstpointer)key); + gtk_tree_store_remove(store, row->dataiter); + delete row; + + return true; +} + +gTreeRow* gTree::getRow(char *key) const +{ + if (!key) + return NULL; + else + return (gTreeRow*)g_hash_table_lookup (datakey,(gconstpointer)key); +} + +gTreeRow* gTree::addRow(char *key, char *after, bool before) +{ + GtkTreeIter iter; + GtkTreeIter *piter; + gTreeRow *row,*aft = NULL; + char *buf; + + if (!key) + return NULL; + + if (g_hash_table_lookup (datakey,(gconstpointer)key)) return NULL; + + if (after) + { + aft=(gTreeRow*)g_hash_table_lookup (datakey,(gconstpointer)after); + if (!aft) return NULL; + } + + piter = NULL; + + //fprintf(stderr, "[0]: %d %d\n", columnResizable(0), gtk_tree_view_column_get_sizing(gt_tree_view_find_column(GTK_TREE_VIEW(widget), 0))); + + if (aft) + { + if (before) + gtk_tree_store_insert_before(store, &iter, piter, aft->dataiter); + else + gtk_tree_store_insert_after(store, &iter, piter, aft->dataiter); + } + else + gtk_tree_store_append (store, &iter, piter); + + buf = g_strdup(key); // Will be freed by ~gTreeRow() + + row = new gTreeRow(this, buf, gtk_tree_iter_copy(&iter)); + + g_hash_table_insert(datakey, (gpointer)buf, (gpointer)row); + gtk_tree_store_set(store, &iter, 1, buf, -1); + + //fprintf(stderr, "[0]: -> %d %d\n", columnResizable(0), gtk_tree_view_column_get_sizing(gt_tree_view_find_column(GTK_TREE_VIEW(widget), 0))); + + return row; +} + +#if 0 +static void tree_cell_text(GtkTreeViewColumn *col,GtkCellRenderer *cell,GtkTreeModel *md,GtkTreeIter *iter,gTree *Tr) +{ + gTreeRow *row = NULL; + gTreeCell *data; + const char *buf = ""; + char *key; + int index = -1; + double align; + + key = Tr->iterToKey(iter); + if (key) + row= (gTreeRow*)g_hash_table_lookup(Tr->datakey, (gpointer)key); + + if (row) + { + index = gt_tree_view_find_index(GTK_TREE_VIEW(Tr->widget), col); + data = row->get(index); + if (data) + { + if (data->text()) + buf = data->text(); + } + } + + align = gtk_tree_view_column_get_alignment(col); + + g_object_set(G_OBJECT(cell), + "text", buf, + "editable", false, + "xalign", align, + (void *)NULL); +} + +static void tree_cell_graph(GtkTreeViewColumn *col,GtkCellRenderer *cell,GtkTreeModel *md,GtkTreeIter *iter,gTree *Tr) +{ + gTreeRow *row=NULL; + gTreeCell *data; + GdkPixbuf *buf=NULL; + char *key; + + key = Tr->iterToKey(iter); + if (key) + row=(gTreeRow*)g_hash_table_lookup(Tr->datakey,(gpointer)key); + + if (row) + { + data = row->get(gt_tree_view_find_index(GTK_TREE_VIEW(Tr->widget), col)); + if (data) + { + buf = data->picture() ? data->picture()->getPixbuf() : NULL; + } + } + + g_object_set(G_OBJECT(cell),"pixbuf",buf,(void *)NULL); + +} +#endif + +static void gTree_addColumn(char *key,gTreeRow *value,gpointer data) +{ + value->add(); +} + +void gTree::addColumn() +{ + g_hash_table_foreach(datakey,(GHFunc)gTree_addColumn,NULL); +} + +static void gTree_removeColumn(char *key,gTreeRow *value,gpointer data) +{ + value->remove(); +} + +void gTree::removeColumn() +{ + int vl; + + if (!(vl = columnCount()) ) + return; + g_hash_table_foreach(datakey,(GHFunc)gTree_removeColumn,NULL); +} + +int gTree::columnCount() +{ + return 1; +} + +#if 0 +char* gTree::columnName(int ind) +{ + GtkTreeViewColumn *col=gt_tree_view_find_column(GTK_TREE_VIEW(widget),ind); + + if (!col) return NULL; + return (char *)gtk_tree_view_column_get_title(col); +} + +void gTree::setColumnName(int ind,char *vl) +{ + GtkTreeViewColumn *col=gt_tree_view_find_column(GTK_TREE_VIEW(widget),ind); + + if (!col) return; + gtk_tree_view_column_set_title(col,(const gchar*)vl); +} + +bool gTree::columnVisible(int ind) +{ + GtkTreeViewColumn *col=gt_tree_view_find_column(GTK_TREE_VIEW(widget),ind); + + if (!col) return false; + return gtk_tree_view_column_get_visible(col); + +} + +void gTree::setColumnVisible(int ind,bool vl) +{ + GtkTreeViewColumn *col=gt_tree_view_find_column(GTK_TREE_VIEW(widget),ind); + + if (!col) return; + gtk_tree_view_column_set_visible(col,vl); +} + +bool gTree::columnResizable(int ind) +{ + GtkTreeViewColumn *col=gt_tree_view_find_column(GTK_TREE_VIEW(widget),ind); + + if (!col) return false; + return gtk_tree_view_column_get_resizable(col); +} + +void gTree::setColumnResizable(int ind, bool vl) +{ + GtkTreeViewColumn *col=gt_tree_view_find_column(GTK_TREE_VIEW(widget),ind); + if (!col) return; + gtk_tree_view_column_set_resizable(col,vl); +} + +int gTree::columnAlignment(int ind) +{ + GtkTreeViewColumn *col = gt_tree_view_find_column(GTK_TREE_VIEW(widget), ind); + + if (!col) + return ALIGN_LEFT; + else + return gt_to_alignment(gtk_tree_view_column_get_alignment(col)); +} + +void gTree::setColumnAlignment(int ind, int align) +{ + GtkTreeViewColumn *col = gt_tree_view_find_column(GTK_TREE_VIEW(widget), ind); + + if (!col) + return; + + gtk_tree_view_column_set_alignment(col, gt_from_alignment(align)); +} + +int gTree::columnWidth(int ind) +{ + GtkTreeViewColumn *col = gt_tree_view_find_column(GTK_TREE_VIEW(widget), ind); + int w; + + if (!col) + return 0; + + w = gtk_tree_view_column_get_width(col); + if (w == 0) + w = gtk_tree_view_column_get_fixed_width(col); + + return w; +} + +void gTree::setColumnWidth(int ind, int w) +{ + GtkTreeViewColumn *col = gt_tree_view_find_column(GTK_TREE_VIEW(widget), ind); + + if (!col) + return; + + if (w > 0) + { + gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width(col, w); + } + else + gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + + setColumnResizable(ind, isResizable()); +} +#endif + +#if 0 +bool gTree::headers() +{ + if (!widget) return false; + return gtk_tree_view_get_headers_visible(GTK_TREE_VIEW(widget)); +} + +void gTree::setHeaders(bool vl) +{ + if (!widget) return; + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(widget),vl); +} + +void gTree::setResizable(bool vl) +{ + int i; + + for (i = 0; i < columnCount(); i++) + gtk_tree_view_column_set_resizable(gt_tree_view_find_column(GTK_TREE_VIEW(widget), i), vl); + + _resizable = vl; +} +#endif + +void gTree::setAutoResize(bool vl) +{ + int i; + + for (i = 0; i < columnCount(); i++) + gtk_tree_view_column_set_sizing(gt_tree_view_find_column(GTK_TREE_VIEW(widget), i), vl ? GTK_TREE_VIEW_COLUMN_AUTOSIZE : GTK_TREE_VIEW_COLUMN_FIXED); + + _auto_resize = vl; +} + +void gTree::selectAll() +{ + GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); + + if (!sel) + return; + + _no_click++; + gtk_tree_selection_select_all(sel); + _no_click--; +} + +void gTree::unselectAll() +{ + GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); + if (!sel) + return; + + _no_click++; + gtk_tree_selection_unselect_all(sel); + _no_click--; +} + +void gTree::setSorted(bool v) +{ + if (v == _sorted) + return; + + _sorted = v; + _sort_column = v ? 0 : -1; + if (!_sorted) + { + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, GTK_SORT_ASCENDING); + gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), NULL, NULL, NULL); + } + updateSort(); +} + +void gTree::setSortColumn(int v) +{ + if (_sort_column < 0) + setSorted(false); + else + { + _sort_column = v; + _ascending = true; + updateSort(); + } +} + +void gTree::setSortAscending(bool v) +{ + _ascending = v; + updateSort(); +} + +void gTree::sort() +{ + if (!_sorted) + return; + + // BM: force the store to be sorted + gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), (GtkTreeIterCompareFunc)tree_compare, this, NULL); + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING); + _sort_dirty = false; +} + +void gTree::updateSort() +{ + sortLater(); +} + +static gboolean tree_sort_later(gTree *tree) +{ + tree->sort(); + return FALSE; +} + +void gTree::sortLater() +{ + if (!isSorted() || _sort_dirty) + return; + + _sort_dirty = true; + g_timeout_add(0, (GSourceFunc)tree_sort_later, this); +} + diff --git a/gb.gtk/src/gtree.h b/gb.gtk/src/gtree.h new file mode 100644 index 00000000..56fb715a --- /dev/null +++ b/gb.gtk/src/gtree.h @@ -0,0 +1,190 @@ +/*************************************************************************** + + gtree.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GTREE_H +#define __GTREE_H + +#include + +class gTree; + +class gTreeCell +{ +public: + gTreeCell(); + ~gTreeCell(); + + char *text() { return _text; } + gPicture *picture() { return _picture; } + + void setText(const char *vl); + void setPicture(gPicture *vl); + +private: + char *_text; + gPicture *_picture; +}; + +class gTreeRow +{ +public: + GList *data; + GtkTreeIter *dataiter; + gTree *tree; + + gTreeRow(gTree *tr, char *key, GtkTreeIter *iter); + ~gTreeRow(); + + void add(); + void remove(); + void update(); + //int children(); + char *key() { return _key; } + + //void setExpanded(); + //void setExpanded(bool ex); + //bool isExpanded(); + void ensureVisible(); + + gTreeCell* get(int ind); + + /*char *next(); + char *prev(); + char *child(); + char *last(); + char *parent(); + char *above(); + char *below(); + + void moveFirst(); + void moveLast(); + void moveAfter(char *key); + void moveBefore(char *key); + + void rect(int *x, int *y, int *w, int *h); + void updateExpanded(bool ex);*/ + + //bool isEditable() { return _editable; } + //void setEditable(bool vl) { _editable = vl; } + + //void startRename(); + +private: + char *_key; + char *_parent; + //bool _expanded; + //bool _editable; +}; + +class gTree +{ +public: + GtkWidget *widget; + GtkTreeStore *store; + GtkCellRenderer *rtext; + GtkCellRenderer *rgraph; + GHashTable *datakey; + void (*onRemove)(gTree *tree, char *key); + char *_edited_row; + unsigned _editable : 1; + unsigned _resizable : 1; + unsigned _auto_resize : 1; + unsigned _expander : 1; + unsigned _sorted : 1; + unsigned _ascending : 1; + unsigned _init_sort : 1; + unsigned _sort_dirty : 1; + int _no_click; + int _sort_column; + + gTree(); + ~gTree(); + + //General + //int visibleWidth(); + //int visibleHeight(); + char* cursor(); + void setCursor(char *vl); + void selectAll(); + void unselectAll(); + //bool isEditable() { return _editable; } + //void setEditable(bool vl) { _editable = vl; } + //bool isRenaming() const { return _edited_row != NULL; } + + // Rows + gTreeRow* addRow(char *key, char *after = NULL, bool before = false); + gTreeRow* getRow(char *key) const; + gTreeRow* operator[](char *key) const { return getRow(key); } + + bool removeRow(char *key); + int rowCount(); + bool rowExists(char *key); + bool rowSelected(char *key); + void setRowSelected(char *key,bool vl); + //bool isRowEditable(char *key); + //void setRowEditable(char *key, bool vl); + char *firstRow(); + //char *lastRow(); + + void clear(); + //void clear(char *parent); + + // Columns + //bool headers(); + //void setHeaders(bool vl); + /*bool isResizable() { return _resizable; } + void setResizable(bool vl);*/ + bool isAutoResize() { return _auto_resize; } + void setAutoResize(bool vl); + void addColumn(); + void removeColumn(); + int columnCount(); + /*char *columnName(int ind); + void setColumnName(int ind,char *vl); + bool columnVisible(int ind); + void setColumnVisible(int ind, bool vl); + bool columnResizable(int ind); + void setColumnResizable(int ind, bool vl); + int columnAlignment(int ind); + void setColumnAlignment(int ind, int a); + int columnWidth(int col); + void setColumnWidth(int col, int w);*/ + + bool isSorted() { return _sorted; } + void setSorted(bool v); + int getSortColumn() { return _sort_column; } + void setSortColumn(int v); + bool isSortAscending() { return _ascending; } + void setSortAscending(bool v); + + char *pathToKey(GtkTreePath *path, bool free = true); + char *iterToKey(GtkTreeIter *iter); + + void showExpanders(); + bool hasExpanders() { return _expander; } + void sortLater(); + void sort(); + void updateSort(); +}; + +#endif diff --git a/gb.gtk/src/kentities.h b/gb.gtk/src/kentities.h new file mode 100644 index 00000000..89604936 --- /dev/null +++ b/gb.gtk/src/kentities.h @@ -0,0 +1,860 @@ +/*************************************************************************** + + kentities.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* ANSI-C code produced by gperf version 3.0.1 */ +/* Command-line: gperf -a -L ANSI-C -E -C -c -o -t -k '*' -Nkde_findEntity -D -Hhash_Entity -Wwordlist_Entity -s 2 kentities.gperf */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to ." +#endif + +#line 1 "kentities.gperf" + +/* This file is part of the KDE libraries + + Copyright (C) 1999 Lars Knoll (knoll@mpi-hd.mpg.de) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + ---------------------------------------------------------------------------- + + kentities.gperf: input file to generate a hash table for entities + kentities.c: DO NOT EDIT! generated by the command + "gperf -a -L "ANSI-C" -C -G -c -o -t -k '*' -Nkde_findEntity -D -s 2 khtmlentities.gperf > entities.c" + from kentities.gperf + + $Id: kentities.c 465272 2005-09-29 09:47:40Z mueller $ +*/ +#line 31 "kentities.gperf" +struct entity { + const char *name; + int code; +}; +/* maximum key range = 924, duplicates = 0 */ + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +hash_Entity (register const char *str, register unsigned int len) +{ + static const unsigned short asso_values[] = + { + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 0, + 145, 120, 0, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 125, 315, 35, 60, 325, + 5, 5, 0, 130, 932, 10, 0, 20, 110, 120, + 100, 0, 45, 40, 10, 135, 0, 932, 0, 15, + 50, 932, 932, 932, 932, 932, 932, 0, 15, 70, + 0, 20, 200, 250, 295, 10, 285, 5, 140, 90, + 15, 5, 65, 195, 5, 100, 25, 25, 130, 0, + 75, 190, 30, 35, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 932, 932 + }; + register int hval = len; + + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[7]]; + /*FALLTHROUGH*/ + case 7: + hval += asso_values[(unsigned char)str[6]]; + /*FALLTHROUGH*/ + case 6: + hval += asso_values[(unsigned char)str[5]]; + /*FALLTHROUGH*/ + case 5: + hval += asso_values[(unsigned char)str[4]]; + /*FALLTHROUGH*/ + case 4: + hval += asso_values[(unsigned char)str[3]]; + /*FALLTHROUGH*/ + case 3: + hval += asso_values[(unsigned char)str[2]]; + /*FALLTHROUGH*/ + case 2: + hval += asso_values[(unsigned char)str[1]+1]; + /*FALLTHROUGH*/ + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval; +} + +#ifdef __GNUC__ +__inline +#endif +const struct entity * +kde_findEntity (register const char *str, register unsigned int len) +{ + enum + { + TOTAL_KEYWORDS = 280, + MIN_WORD_LENGTH = 2, + MAX_WORD_LENGTH = 8, + MIN_HASH_VALUE = 8, + MAX_HASH_VALUE = 931 + }; + + static const struct entity wordlist_Entity[] = + { +#line 116 "kentities.gperf" + {"and", 0x22a5}, +#line 264 "kentities.gperf" + {"rho", 0x03c1}, +#line 142 "kentities.gperf" + {"darr", 0x2193}, +#line 143 "kentities.gperf" + {"dcaron", 0x10f}, +#line 257 "kentities.gperf" + {"rarr", 0x2192}, +#line 258 "kentities.gperf" + {"rcaron", 0x0159}, +#line 95 "kentities.gperf" + {"Tcaron", 0x0164}, +#line 185 "kentities.gperf" + {"int", 0x222b}, +#line 214 "kentities.gperf" + {"ncaron", 0x0148}, +#line 161 "kentities.gperf" + {"eta", 0x03b7}, +#line 150 "kentities.gperf" + {"ecaron", 0x011b}, +#line 94 "kentities.gperf" + {"Tau", 0x03a4}, +#line 299 "kentities.gperf" + {"uarr", 0x2191}, +#line 288 "kentities.gperf" + {"tcaron", 0x0165}, +#line 48 "kentities.gperf" + {"Chi", 0x03a7}, +#line 312 "kentities.gperf" + {"zcaron", 0x017e}, +#line 90 "kentities.gperf" + {"Rho", 0x03a1}, +#line 188 "kentities.gperf" + {"isin", 0x2208}, +#line 293 "kentities.gperf" + {"thorn", 0x00fe}, +#line 46 "kentities.gperf" + {"Ccaron", 0x010c}, +#line 287 "kentities.gperf" + {"tau", 0x03c4}, +#line 96 "kentities.gperf" + {"Theta", 0x0398}, +#line 91 "kentities.gperf" + {"Scaron", 0x0160}, +#line 110 "kentities.gperf" + {"acute", 0x00b4}, +#line 89 "kentities.gperf" + {"Rcaron", 0x0158}, +#line 106 "kentities.gperf" + {"Zcaron", 0x017d}, +#line 115 "kentities.gperf" + {"amp", 38}, +#line 220 "kentities.gperf" + {"nsub", 0x2284}, +#line 290 "kentities.gperf" + {"theta", 0x03b8}, +#line 289 "kentities.gperf" + {"there4", 0x2234}, +#line 243 "kentities.gperf" + {"phi", 0x03c6}, +#line 238 "kentities.gperf" + {"para", 0x00b6}, +#line 109 "kentities.gperf" + {"acirc", 0x00e2}, +#line 50 "kentities.gperf" + {"Dcaron", 0x010e}, +#line 132 "kentities.gperf" + {"chi", 0x03c7}, +#line 224 "kentities.gperf" + {"ocirc", 0x00f4}, +#line 180 "kentities.gperf" + {"icirc", 0x00ee}, +#line 128 "kentities.gperf" + {"ccaron", 0x010d}, +#line 251 "kentities.gperf" + {"psi", 0x03c8}, +#line 186 "kentities.gperf" + {"iota", 0x03b9}, +#line 254 "kentities.gperf" + {"radic", 0x221a}, +#line 231 "kentities.gperf" + {"or", 0x22a6}, +#line 218 "kentities.gperf" + {"not", 0x00ac}, +#line 152 "kentities.gperf" + {"ecirc", 0x00ea}, +#line 239 "kentities.gperf" + {"part", 0x2202}, +#line 300 "kentities.gperf" + {"ucirc", 0x00fb}, +#line 84 "kentities.gperf" + {"Phi", 0x03a6}, +#line 69 "kentities.gperf" + {"Lambda", 0x039b}, +#line 269 "kentities.gperf" + {"scaron", 0x0161}, +#line 229 "kentities.gperf" + {"omicron", 0x03bf}, +#line 88 "kentities.gperf" + {"QUOT", 34}, +#line 219 "kentities.gperf" + {"notin", 0x2209}, +#line 70 "kentities.gperf" + {"LT", 60}, +#line 87 "kentities.gperf" + {"Psi", 0x03a8}, +#line 72 "kentities.gperf" + {"Ncaron", 0x0147}, +#line 62 "kentities.gperf" + {"GT", 62}, +#line 227 "kentities.gperf" + {"oline", 0x203e}, +#line 222 "kentities.gperf" + {"nu", 0x03bd}, +#line 296 "kentities.gperf" + {"trade", 0x2122}, +#line 71 "kentities.gperf" + {"Mu", 0x039c}, +#line 127 "kentities.gperf" + {"cap", 0x2229}, +#line 270 "kentities.gperf" + {"sdot", 0x22c5}, +#line 190 "kentities.gperf" + {"kappa", 0x03ba}, +#line 68 "kentities.gperf" + {"Kappa", 0x039a}, +#line 108 "kentities.gperf" + {"aacute", 0x00e1}, +#line 164 "kentities.gperf" + {"euro", 0x20ac}, +#line 223 "kentities.gperf" + {"oacute", 0x00f3}, +#line 205 "kentities.gperf" + {"lt", 60}, +#line 195 "kentities.gperf" + {"larr", 0x2190}, +#line 179 "kentities.gperf" + {"iacute", 0x00ed}, +#line 249 "kentities.gperf" + {"prod", 0x220f}, +#line 247 "kentities.gperf" + {"pound", 0x00a3}, +#line 104 "kentities.gperf" + {"Yacute", 0x00dd}, +#line 259 "kentities.gperf" + {"rceil", 0x2309}, +#line 149 "kentities.gperf" + {"eacute", 0x00e9}, +#line 302 "kentities.gperf" + {"uml", 0x00a8}, +#line 206 "kentities.gperf" + {"macr", 0x00af}, +#line 137 "kentities.gperf" + {"crarr", 0x21b5}, +#line 298 "kentities.gperf" + {"uacute", 0x00fa}, +#line 265 "kentities.gperf" + {"rlm", 0x200f}, +#line 212 "kentities.gperf" + {"nabla", 0x2207}, +#line 187 "kentities.gperf" + {"iquest", 0x00bf}, +#line 158 "kentities.gperf" + {"ensp", 0x2002}, +#line 160 "kentities.gperf" + {"equiv", 0x2261}, +#line 233 "kentities.gperf" + {"ordm", 0x00ba}, +#line 121 "kentities.gperf" + {"atilde", 0x00e3}, +#line 156 "kentities.gperf" + {"emsp", 0x2003}, +#line 61 "kentities.gperf" + {"Gamma", 0x0393}, +#line 235 "kentities.gperf" + {"otilde", 0x00f5}, +#line 148 "kentities.gperf" + {"dol", 0x0024}, +#line 77 "kentities.gperf" + {"Ocirc", 0x00d4}, +#line 47 "kentities.gperf" + {"Ccedil", 0x00c7}, +#line 38 "kentities.gperf" + {"Acirc", 0x00c2}, +#line 221 "kentities.gperf" + {"ntilde", 0x00f1}, +#line 216 "kentities.gperf" + {"ne", 0x2260}, +#line 64 "kentities.gperf" + {"Icirc", 0x00ce}, +#line 211 "kentities.gperf" + {"mu", 0x03bc}, +#line 66 "kentities.gperf" + {"Iota", 0x0399}, +#line 98 "kentities.gperf" + {"Ucirc", 0x00db}, +#line 292 "kentities.gperf" + {"thinsp", 0x2009}, +#line 201 "kentities.gperf" + {"loz", 0x25ca}, +#line 250 "kentities.gperf" + {"prop", 0x221d}, +#line 74 "kentities.gperf" + {"Nu", 0x039d}, +#line 124 "kentities.gperf" + {"beta", 0x03b2}, +#line 184 "kentities.gperf" + {"infin", 0x221e}, +#line 129 "kentities.gperf" + {"ccedil", 0x00e7}, +#line 80 "kentities.gperf" + {"Omicron", 0x039f}, +#line 277 "kentities.gperf" + {"sub", 0x2282}, +#line 256 "kentities.gperf" + {"raquo", 0x00bb}, +#line 139 "kentities.gperf" + {"curren", 0x00a4}, +#line 213 "kentities.gperf" + {"nbsp", 0x00a0}, +#line 260 "kentities.gperf" + {"rdquo", 0x201d}, +#line 236 "kentities.gperf" + {"otimes", 0x2297}, +#line 117 "kentities.gperf" + {"ang", 0x2220}, +#line 313 "kentities.gperf" + {"zeta", 0x03b6}, +#line 267 "kentities.gperf" + {"rsquo", 0x2019}, +#line 266 "kentities.gperf" + {"rsaquo", 0x203a}, +#line 123 "kentities.gperf" + {"bdquo", 0x201e}, +#line 192 "kentities.gperf" + {"lambda", 0x03bb}, +#line 138 "kentities.gperf" + {"cup", 0x222a}, +#line 278 "kentities.gperf" + {"sube", 0x2286}, +#line 125 "kentities.gperf" + {"brvbar", 0x00a6}, +#line 174 "kentities.gperf" + {"gt", 62}, +#line 107 "kentities.gperf" + {"Zeta", 0x0396}, +#line 76 "kentities.gperf" + {"Oacute", 0x00d3}, +#line 37 "kentities.gperf" + {"Aacute", 0x00c1}, +#line 103 "kentities.gperf" + {"Xi", 0x039e}, +#line 255 "kentities.gperf" + {"rang", 0x232a}, +#line 248 "kentities.gperf" + {"prime", 0x2032}, +#line 63 "kentities.gperf" + {"Iacute", 0x00cd}, +#line 228 "kentities.gperf" + {"omega", 0x03c9}, +#line 97 "kentities.gperf" + {"Uacute", 0x00da}, +#line 284 "kentities.gperf" + {"sup", 0x2283}, +#line 280 "kentities.gperf" + {"sup1", 0x00b9}, +#line 183 "kentities.gperf" + {"image", 0x2111}, +#line 217 "kentities.gperf" + {"ni", 0x220b}, +#line 272 "kentities.gperf" + {"shy", 0x00ad}, +#line 118 "kentities.gperf" + {"apos", 0x0027}, +#line 134 "kentities.gperf" + {"clubs", 0x2663}, +#line 307 "kentities.gperf" + {"weierp", 0x2118}, +#line 232 "kentities.gperf" + {"ordf", 0x00aa}, +#line 73 "kentities.gperf" + {"Ntilde", 0x00d1}, +#line 131 "kentities.gperf" + {"cent", 0x00a2}, +#line 196 "kentities.gperf" + {"lceil", 0x2308}, +#line 285 "kentities.gperf" + {"supe", 0x2287}, +#line 155 "kentities.gperf" + {"empty", 0x2205}, +#line 82 "kentities.gperf" + {"Otilde", 0x00d5}, +#line 279 "kentities.gperf" + {"sum", 0x2211}, +#line 176 "kentities.gperf" + {"harr", 0x2194}, +#line 86 "kentities.gperf" + {"Prime", 0x2033}, +#line 43 "kentities.gperf" + {"Atilde", 0x00c3}, +#line 140 "kentities.gperf" + {"dArr", 0x21d3}, +#line 202 "kentities.gperf" + {"lrm", 0x200e}, +#line 253 "kentities.gperf" + {"rArr", 0x21d2}, +#line 151 "kentities.gperf" + {"eague", 0x00e9}, +#line 200 "kentities.gperf" + {"lowast", 0x2217}, +#line 41 "kentities.gperf" + {"AMP", 38}, +#line 242 "kentities.gperf" + {"perp", 0x22a5}, +#line 198 "kentities.gperf" + {"le", 0x2264}, +#line 162 "kentities.gperf" + {"eth", 0x00f0}, +#line 261 "kentities.gperf" + {"real", 0x211c}, +#line 165 "kentities.gperf" + {"exist", 0x2203}, +#line 309 "kentities.gperf" + {"yacute", 0x00fd}, +#line 244 "kentities.gperf" + {"pi", 0x03c0}, +#line 59 "kentities.gperf" + {"Eta", 0x0397}, +#line 297 "kentities.gperf" + {"uArr", 0x21d1}, +#line 54 "kentities.gperf" + {"Ecaron", 0x011a}, +#line 252 "kentities.gperf" + {"quot", 34}, +#line 308 "kentities.gperf" + {"xi", 0x03be}, +#line 122 "kentities.gperf" + {"auml", 0x00e4}, +#line 237 "kentities.gperf" + {"ouml", 0x00f6}, +#line 145 "kentities.gperf" + {"delta", 0x03b4}, +#line 189 "kentities.gperf" + {"iuml", 0x00ef}, +#line 120 "kentities.gperf" + {"asymp", 0x2248}, +#line 169 "kentities.gperf" + {"frac14", 0x00bc}, +#line 105 "kentities.gperf" + {"Yuml", 0x0178}, +#line 119 "kentities.gperf" + {"aring", 0x00e5}, +#line 163 "kentities.gperf" + {"euml", 0x00eb}, +#line 194 "kentities.gperf" + {"laquo", 0x00ab}, +#line 240 "kentities.gperf" + {"percnt", 0x0025}, +#line 85 "kentities.gperf" + {"Pi", 0x03a0}, +#line 306 "kentities.gperf" + {"uuml", 0x00fc}, +#line 197 "kentities.gperf" + {"ldquo", 0x201c}, +#line 246 "kentities.gperf" + {"plusmn", 0x00b1}, +#line 314 "kentities.gperf" + {"zwj", 0x200d}, +#line 136 "kentities.gperf" + {"copy", 0x00a9}, +#line 204 "kentities.gperf" + {"lsquo", 0x2018}, +#line 203 "kentities.gperf" + {"lsaquo", 0x2039}, +#line 271 "kentities.gperf" + {"sect", 0x00a7}, +#line 268 "kentities.gperf" + {"sbquo", 0x201a}, +#line 135 "kentities.gperf" + {"cong", 0x2245}, +#line 305 "kentities.gperf" + {"uring", 0x016f}, +#line 310 "kentities.gperf" + {"yen", 0x00a5}, +#line 315 "kentities.gperf" + {"zwnj", 0x200c}, +#line 79 "kentities.gperf" + {"Omega", 0x03a9}, +#line 209 "kentities.gperf" + {"middot", 0x00b7}, +#line 166 "kentities.gperf" + {"fnof", 0x0192}, +#line 55 "kentities.gperf" + {"Ecirc", 0x00ca}, +#line 263 "kentities.gperf" + {"rfloor", 0x230b}, +#line 283 "kentities.gperf" + {"sup3", 0x00b3}, +#line 93 "kentities.gperf" + {"THORN", 0x00de}, +#line 276 "kentities.gperf" + {"spades", 0x2660}, +#line 193 "kentities.gperf" + {"lang", 0x2329}, +#line 130 "kentities.gperf" + {"cedil", 0x00b8}, +#line 157 "kentities.gperf" + {"endash", 0x2013}, +#line 126 "kentities.gperf" + {"bull", 0x2022}, +#line 51 "kentities.gperf" + {"Delta", 0x0394}, +#line 133 "kentities.gperf" + {"circ", 0x02c6}, +#line 215 "kentities.gperf" + {"ndash", 0x2013}, +#line 154 "kentities.gperf" + {"emdash", 0x2014}, +#line 281 "kentities.gperf" + {"supl", 0x00b9}, +#line 282 "kentities.gperf" + {"sup2", 0x00b2}, +#line 172 "kentities.gperf" + {"gamma", 0x03b3}, +#line 147 "kentities.gperf" + {"divide", 0x00f7}, +#line 173 "kentities.gperf" + {"ge", 0x2265}, +#line 144 "kentities.gperf" + {"deg", 0x00b0}, +#line 114 "kentities.gperf" + {"alpha", 0x03b1}, +#line 112 "kentities.gperf" + {"agrave", 0x00e0}, +#line 262 "kentities.gperf" + {"reg", 0x00ae}, +#line 208 "kentities.gperf" + {"micro", 0x00b5}, +#line 226 "kentities.gperf" + {"ograve", 0x00f2}, +#line 52 "kentities.gperf" + {"ETH", 0x00d0}, +#line 182 "kentities.gperf" + {"igrave", 0x00ec}, +#line 291 "kentities.gperf" + {"thetasym", 0x03d1}, +#line 191 "kentities.gperf" + {"lArr", 0x21d0}, +#line 230 "kentities.gperf" + {"oplus", 0x2295}, +#line 294 "kentities.gperf" + {"tilde", 0x02dc}, +#line 153 "kentities.gperf" + {"egrave", 0x00e8}, +#line 275 "kentities.gperf" + {"sim", 0x223c}, +#line 146 "kentities.gperf" + {"diams", 0x2666}, +#line 301 "kentities.gperf" + {"ugrave", 0x00f9}, +#line 245 "kentities.gperf" + {"piv", 0x03d6}, +#line 83 "kentities.gperf" + {"Ouml", 0x00d6}, +#line 53 "kentities.gperf" + {"Eacute", 0x00c9}, +#line 44 "kentities.gperf" + {"Auml", 0x00c4}, +#line 159 "kentities.gperf" + {"epsilon", 0x03b5}, +#line 67 "kentities.gperf" + {"Iuml", 0x00cf}, +#line 170 "kentities.gperf" + {"frac34", 0x00be}, +#line 304 "kentities.gperf" + {"upsilon", 0x03c5}, +#line 102 "kentities.gperf" + {"Uuml", 0x00dc}, +#line 181 "kentities.gperf" + {"iexcl", 0x00a1}, +#line 42 "kentities.gperf" + {"Aring", 0x00c5}, +#line 207 "kentities.gperf" + {"mdash", 0x2014}, +#line 101 "kentities.gperf" + {"Uring", 0x016e}, +#line 241 "kentities.gperf" + {"permil", 0x2030}, +#line 210 "kentities.gperf" + {"minus", 0x2212}, +#line 168 "kentities.gperf" + {"frac12", 0x00bd}, +#line 295 "kentities.gperf" + {"times", 0x00d7}, +#line 75 "kentities.gperf" + {"OElig", 0x0152}, +#line 36 "kentities.gperf" + {"AElig", 0x00c6}, +#line 286 "kentities.gperf" + {"szlig", 0x00df}, +#line 45 "kentities.gperf" + {"Beta", 0x0392}, +#line 171 "kentities.gperf" + {"frasl", 0x2044}, +#line 141 "kentities.gperf" + {"dagger", 0x2020}, +#line 199 "kentities.gperf" + {"lfloor", 0x230a}, +#line 311 "kentities.gperf" + {"yuml", 0x00ff}, +#line 167 "kentities.gperf" + {"forall", 0x2200}, +#line 234 "kentities.gperf" + {"oslash", 0x00f8}, +#line 78 "kentities.gperf" + {"Ograve", 0x00d2}, +#line 40 "kentities.gperf" + {"Alpha", 0x0391}, +#line 39 "kentities.gperf" + {"Agrave", 0x00c0}, +#line 65 "kentities.gperf" + {"Igrave", 0x00cc}, +#line 99 "kentities.gperf" + {"Ugrave", 0x00d9}, +#line 111 "kentities.gperf" + {"aelig", 0x00e6}, +#line 49 "kentities.gperf" + {"Dagger", 0x2021}, +#line 100 "kentities.gperf" + {"Upsilon", 0x03a5}, +#line 225 "kentities.gperf" + {"oelig", 0x0153}, +#line 175 "kentities.gperf" + {"hArr", 0x21d4}, +#line 303 "kentities.gperf" + {"upsih", 0x03d2}, +#line 177 "kentities.gperf" + {"hearts", 0x2665}, +#line 57 "kentities.gperf" + {"Eague", 0x00c9}, +#line 92 "kentities.gperf" + {"Sigma", 0x03a3}, +#line 81 "kentities.gperf" + {"Oslash", 0x00d8}, +#line 60 "kentities.gperf" + {"Euml", 0x00cb}, +#line 113 "kentities.gperf" + {"alefsym", 0x2135}, +#line 273 "kentities.gperf" + {"sigma", 0x03c3}, +#line 56 "kentities.gperf" + {"Egrave", 0x00c8}, +#line 58 "kentities.gperf" + {"Epsilon", 0x0395}, +#line 178 "kentities.gperf" + {"hellip", 0x2026}, +#line 274 "kentities.gperf" + {"sigmaf", 0x03c2} + }; + + static const short lookup[] = + { + -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, 2, + -1, 3, -1, -1, 4, -1, 5, -1, -1, -1, + -1, 6, -1, 7, -1, -1, 8, -1, 9, -1, + -1, 10, -1, 11, 12, -1, 13, -1, 14, -1, + -1, 15, -1, 16, 17, 18, 19, -1, 20, -1, + 21, 22, -1, -1, -1, 23, 24, -1, -1, -1, + -1, 25, -1, 26, 27, 28, 29, -1, 30, 31, + 32, 33, -1, 34, -1, 35, -1, -1, -1, -1, + 36, 37, -1, 38, 39, 40, -1, 41, 42, -1, + 43, -1, -1, -1, 44, 45, -1, -1, -1, -1, + -1, -1, -1, 46, -1, -1, 47, -1, -1, -1, + -1, 48, 49, -1, 50, 51, -1, 52, 53, -1, + -1, 54, 55, -1, -1, 56, -1, 57, -1, -1, + 58, -1, 59, 60, 61, 62, -1, -1, -1, -1, + 63, 64, -1, -1, 65, -1, 66, 67, -1, 68, + -1, 69, -1, -1, 70, 71, 72, -1, -1, -1, + 73, 74, -1, 75, 76, 77, 78, -1, 79, -1, + 80, 81, -1, -1, 82, 83, -1, -1, -1, 84, + -1, 85, -1, -1, 86, 87, 88, -1, 89, -1, + 90, 91, -1, -1, -1, 92, 93, 94, -1, -1, + 95, -1, 96, -1, 97, 98, -1, -1, -1, -1, + -1, 99, -1, -1, -1, -1, -1, -1, 100, 101, + -1, -1, 102, -1, 103, 104, 105, 106, 107, -1, + 108, 109, -1, -1, 110, 111, 112, -1, 113, 114, + 115, 116, -1, -1, -1, 117, 118, -1, 119, 120, + -1, 121, -1, -1, -1, -1, -1, 122, -1, 123, + -1, 124, -1, -1, -1, -1, 125, 126, -1, 127, + 128, 129, -1, -1, -1, 130, 131, -1, 132, 133, + 134, -1, 135, 136, 137, 138, 139, -1, -1, 140, + -1, 141, -1, -1, 142, 143, -1, -1, -1, 144, + 145, 146, -1, 147, 148, 149, 150, -1, -1, 151, + -1, -1, -1, 152, 153, 154, 155, -1, 156, 157, + -1, -1, 158, 159, -1, -1, -1, -1, -1, 160, + 161, 162, 163, 164, 165, -1, 166, -1, -1, 167, + -1, -1, 168, -1, 169, -1, -1, -1, -1, 170, + 171, -1, -1, -1, 172, 173, 174, -1, -1, 175, + 176, -1, -1, -1, 177, 178, 179, 180, -1, 181, + 182, 183, -1, 184, 185, 186, 187, -1, -1, 188, + 189, -1, -1, -1, 190, 191, -1, -1, 192, 193, + 194, 195, -1, -1, 196, 197, 198, -1, -1, 199, + 200, 201, -1, -1, 202, 203, 204, -1, -1, 205, + 206, -1, -1, -1, 207, 208, 209, -1, -1, 210, + -1, -1, -1, -1, 211, -1, -1, -1, -1, -1, + 212, 213, 214, 215, -1, 216, 217, -1, 218, -1, + 219, 220, -1, 221, -1, -1, 222, -1, 223, 224, + 225, -1, -1, -1, -1, 226, 227, -1, 228, -1, + 229, 230, -1, 231, 232, -1, 233, -1, -1, 234, + -1, -1, 235, -1, 236, -1, 237, 238, -1, 239, + 240, -1, -1, -1, -1, 241, -1, -1, -1, -1, + 242, -1, -1, -1, -1, 243, 244, -1, -1, -1, + 245, 246, -1, -1, -1, 247, -1, -1, -1, -1, + 248, -1, -1, -1, -1, 249, -1, -1, -1, -1, + 250, -1, -1, -1, 251, 252, 253, -1, -1, -1, + -1, 254, -1, -1, 255, -1, 256, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 257, -1, -1, -1, -1, 258, -1, -1, -1, + 259, 260, -1, -1, -1, -1, 261, -1, -1, -1, + -1, 262, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 263, 264, 265, -1, -1, + 266, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 267, -1, -1, -1, -1, -1, + 268, 269, -1, -1, -1, -1, -1, -1, -1, -1, + 270, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 271, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 272, -1, -1, 273, + -1, -1, -1, -1, -1, -1, -1, 274, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 275, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 276, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 277, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 278, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 279 + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = hash_Entity (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + { + register int index = lookup[key]; + + if (index >= 0) + { + register const char *s = wordlist_Entity[index].name; + + if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0') + return &wordlist_Entity[index]; + } + } + } + return 0; +} +#line 316 "kentities.gperf" + + diff --git a/gb.gtk/src/main.cpp b/gb.gtk/src/main.cpp new file mode 100644 index 00000000..5550e127 --- /dev/null +++ b/gb.gtk/src/main.cpp @@ -0,0 +1,595 @@ +/*************************************************************************** + + main.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include + +#include "main.h" +#include "gb.image.h" +#include "gb.gtk.h" +#include "watcher.h" +#include "gglarea.h" +#include "gkey.h" + +#include "x11.h" +#include "desktop.h" +#include "CScreen.h" +#include "CStyle.h" +#include "CDraw.h" +#include "CConst.h" +#include "CColor.h" +#include "CFont.h" +#include "CKey.h" +#include "CPicture.h" +#include "CMovieBox.h" +#include "CImage.h" +#include "CClipboard.h" +#include "CMouse.h" +#include "CMessage.h" +#include "CDialog.h" +#include "CWatcher.h" +#include "CWidget.h" +#include "CDrawingArea.h" +#include "CContainer.h" +#include "CFrame.h" +#include "CMenu.h" +#include "CWindow.h" +#include "CLabel.h" +#include "CButton.h" +#include "CTextBox.h" +#include "CTextArea.h" +#include "CSlider.h" +#include "CTabStrip.h" +#include "CTrayIcon.h" +#include "CStock.h" +#include "CSeparator.h" +#include "cprinter.h" +#include "csvgimage.h" + +#include +#include + +GB_CLASS CLASS_Control; +GB_CLASS CLASS_ContainerChildren; +GB_CLASS CLASS_Picture; +GB_CLASS CLASS_Image; +GB_CLASS CLASS_DrawingArea; +GB_CLASS CLASS_Menu; +GB_CLASS CLASS_Window; +GB_CLASS CLASS_Printer; +GB_CLASS CLASS_SvgImage; + +static void my_lang(char *lang,int rtl1); +static void my_error(int code,char *error,char *where); +static void my_quit (void); +static void my_main(int *argc, char ***argv); +static void my_timer(GB_TIMER *timer,bool on); +static void my_wait(int duration); +static void my_post(void); +static int my_loop(); +static void my_watch(int fd, int type, void *callback, intptr_t param); + +static GtkWidget *GTK_CreateGLArea(void *_object, void *parent, void (*init)(GtkWidget *)); + +static bool _post_check = false; +static bool _must_check_quit = false; + +static bool _application_keypress = false; +static GB_FUNCTION _application_keypress_func; + +static void *_old_hook_main; + +int MAIN_scale = 0; +bool MAIN_debug_busy = false; +bool MAIN_rtl = false; + +extern "C" +{ + +const GB_INTERFACE *GB_PTR EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; +GEOM_INTERFACE GEOM EXPORT; + +static void declare_tray_icon() +{ + GB.Component.Declare(TrayIconsDesc); + GB.Component.Declare(TrayIconDesc); +} + +GB_DESC *GB_CLASSES[] EXPORT = +{ + ScreenDesc, + ScreensDesc, + DesktopDesc, + ApplicationDesc, + StyleDesc, + CSelectDesc, + CAlignDesc, + CArrangeDesc, + CBorderDesc, + CScrollDesc, + CColorDesc, + CFontsDesc, + CFontDesc, + CKeyDesc, + CImageDesc, + CPictureDesc, + CClipboardDesc, + CDragDesc, + CCursorDesc, + CMouseDesc, + CPointerDesc, + CMessageDesc, + CDialogDesc, + CWatcherDesc, + CWidgetDesc, + ContainerChildrenDesc, + ContainerDesc, + CDrawingAreaDesc, + CFrameDesc, + UserControlDesc, + UserContainerDesc, + CPanelDesc, + CHBoxDesc, + CVBoxDesc, + CHPanelDesc, + CVPanelDesc, + CMenuDesc, + CMenuChildrenDesc, + CWindowMenusDesc, + CWindowControlsDesc, + CWindowDesc, + CWindowsDesc, + CFormDesc, + CLabelDesc, + CTextLabelDesc, + CSliderDesc, + CScrollBarDesc, + CButtonDesc, + CToggleButtonDesc, + CCheckBoxDesc, + CRadioButtonDesc, + CToolButtonDesc, + CMovieBoxDesc, + CTextBoxSelectionDesc, + CTextBoxDesc, + CTextAreaDesc, + CTextAreaSelectionDesc, + CComboBoxDesc, + CComboBoxItemDesc, + CTabStripDesc, + CTabStripContainerDesc, + CTabStripContainerChildrenDesc, + CPluginDesc, + CSeparatorDesc, + CStockDesc, + PrinterDesc, + SvgImageDesc, + NULL +}; + +void *GB_GTK_1[] EXPORT = +{ + (void *)GTK_INTERFACE_VERSION, + (void *)GTK_CreateGLArea, + NULL +}; + +const char *GB_INCLUDE EXPORT = "gb.draw,gb.gui.base"; + +int EXPORT GB_INIT(void) +{ + char *env; + + env = getenv("GB_GUI_BUSY"); + if (env && atoi(env)) + MAIN_debug_busy = true; + + GB.Hook(GB_HOOK_QUIT, (void *)my_quit); + _old_hook_main = GB.Hook(GB_HOOK_MAIN, (void *)my_main); + GB.Hook(GB_HOOK_WAIT, (void *)my_wait); + GB.Hook(GB_HOOK_LOOP, (void *)my_loop); + GB.Hook(GB_HOOK_TIMER,(void *)my_timer); + GB.Hook(GB_HOOK_WATCH,(void *)my_watch); + GB.Hook(GB_HOOK_POST,(void*)my_post); + GB.Hook(GB_HOOK_ERROR,(void*)my_error); + GB.Hook(GB_HOOK_LANG,(void*)my_lang); + + GB.Component.Load("gb.draw"); + GB.Component.Load("gb.image"); + GB.Component.Load("gb.gui.base"); + + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + GB.GetInterface("gb.geom", GEOM_INTERFACE_VERSION, &GEOM); + + IMAGE.SetDefaultFormat(GB_IMAGE_RGBA); + DRAW_init(); + + CWatcher::init(); + + CLASS_Control = GB.FindClass("Control"); + CLASS_ContainerChildren = GB.FindClass("ContainerChildren"); + //CLASS_UserControl = GB.FindClass("UserControl"); + //CLASS_UserContainer = GB.FindClass("UserContainer"); + CLASS_Window = GB.FindClass("Window"); + CLASS_Menu = GB.FindClass("Menu"); + CLASS_Picture = GB.FindClass("Picture"); + //CLASS_Drawing = GB.FindClass("Drawing"); + CLASS_DrawingArea = GB.FindClass("DrawingArea"); + CLASS_Printer = GB.FindClass("Printer"); + CLASS_Image = GB.FindClass("Image"); + CLASS_SvgImage = GB.FindClass("SvgImage"); + +#if !defined(GLIB_VERSION_2_36) + g_type_init(); +#endif /* !defined(GLIB_VERSION_2_36) */ + + my_lang(GB.System.Language(), GB.System.IsRightToLeft()); + + return -1; +} + +void EXPORT GB_EXIT() +{ + CWatcher::exit(); +} + +int EXPORT GB_INFO(const char *key, void **value) +{ + if (!strcasecmp(key, "DISPLAY")) + { + *value = (void *)gdk_x11_display_get_xdisplay(gdk_display_get_default()); + return TRUE; + } + else if (!strcasecmp(key, "ROOT_WINDOW")) + { + *value = (void *)gdk_x11_get_default_root_xwindow(); + return TRUE; + } + else if (!strcasecmp(key, "GET_HANDLE")) + { + *value = (void *)CWIDGET_get_handle; + return TRUE; + } + else if (!strcasecmp(key, "SET_EVENT_FILTER")) + { + *value = (void *)gApplication::setEventFilter; + return TRUE; + } + else if (!strcasecmp(key, "TIME")) + { + *value = (void *)(intptr_t)gtk_get_current_event_time(); //gdk_x11_display_get_user_time(gdk_display_get_default()); + return TRUE; + } + else if (!strcasecmp(key, "DECLARE_TRAYICON")) + { + *value = (void *)declare_tray_icon; + return TRUE; + } + else + return FALSE; +} + +static void activate_main_window(intptr_t) +{ + if (gMainWindow::_active) + gtk_window_present(GTK_WINDOW(gMainWindow::_active->topLevel()->border)); +} + +void EXPORT GB_SIGNAL(int signal, void *param) +{ + static GtkWidget *save_popup_grab = NULL; + + switch(signal) + { + case GB_SIGNAL_DEBUG_BREAK: + if (gApplication::_popup_grab) + { + save_popup_grab = gApplication::_popup_grab; + gApplication::ungrabPopup(); + } + break; + + case GB_SIGNAL_DEBUG_FORWARD: + //while (qApp->activePopupWidget()) + // delete qApp->activePopupWidget(); + if (gdk_display_get_default()) + gdk_display_sync(gdk_display_get_default()); + break; + + case GB_SIGNAL_DEBUG_CONTINUE: + GB.Post((GB_CALLBACK)activate_main_window, 0); + if (save_popup_grab) + { + gApplication::_popup_grab = save_popup_grab; + save_popup_grab = NULL; + gApplication::grabPopup(); + } + break; + } +} + +} // extern "C" + +void my_quit (void) +{ + GB_FUNCTION func; + + while (gtk_events_pending()) + gtk_main_iteration(); + + if (GB.ExistClass("TrayIcons")) + { + if (!GB.GetFunction(&func, (void *)GB.FindClass("TrayIcons"), "DeleteAll", NULL, NULL)) + GB.Call(&func, 0, FALSE); + } + + if (!GB.GetFunction(&func, (void *)GB.FindClass("_Gui"), "_Quit", NULL, NULL)) + GB.Call(&func, 0, FALSE); + + CWINDOW_delete_all(); + gControl::cleanRemovedControls(); + + CWatcher::Clear(); + gApplication::exit(); + + #ifdef GDK_WINDOWING_X11 + X11_exit(); + #endif +} + +static bool global_key_event_handler(int type) +{ + GB.Call(&_application_keypress_func, 0, FALSE); + return GB.Stopped(); +} + +static void my_main(int *argc, char ***argv) +{ + static bool init = false; + char *env; + + if (init) + return; + + env = getenv("GB_X11_INIT_THREADS"); + if (env && atoi(env)) + XInitThreads(); + + gApplication::init(argc, argv); + gApplication::setDefaultTitle(GB.Application.Title()); + gDesktop::init(); + + gApplication::onEnterEventLoop = GB.Debug.EnterEventLoop; + gApplication::onLeaveEventLoop = GB.Debug.LeaveEventLoop; + + MAIN_scale = gDesktop::scale(); + #ifdef GDK_WINDOWING_X11 + X11_init(gdk_x11_display_get_xdisplay(gdk_display_get_default()), gdk_x11_get_default_root_xwindow()); + #endif + + if (GB.GetFunction(&_application_keypress_func, (void *)GB.Application.StartupClass(), "Application_KeyPress", "", "") == 0) + { + _application_keypress = true; + gApplication::onKeyEvent = global_key_event_handler; + } + + init = true; + + CALL_HOOK_MAIN(_old_hook_main, argc, argv); +} + +/*static void raise_timer(GB_TIMER *timer) +{ + GB.RaiseTimer(timer); + GB.Unref(POINTER(&timer)); +}*/ + +typedef + struct { + int source; + GTimer *timer; + int timeout; + } + MyTimerId; + +gboolean my_timer_function(GB_TIMER *timer) +{ + if (timer->id) + { + GB.RaiseTimer(timer); + + if (timer->id) + { + MyTimerId *id = (MyTimerId *)timer->id; + GTimer *t = id->timer; + int elapsed = (int)(g_timer_elapsed(t, NULL) * 1000) - id->timeout; + int next = timer->delay - elapsed; + if (next < 10) + next = 10; + id->timeout = next; + g_timer_start(t); + id->source = g_timeout_add(next, (GSourceFunc)my_timer_function,(gpointer)timer); + //timer->id = (intptr_t)g_timeout_add(next, (GSourceFunc)my_timer_function,(gpointer)timer); + //fprintf(stderr, "elapsed = %d delay = %d next = %d\n", elapsed, timer->delay, next); + } + } + + return false; +} + +static void my_timer(GB_TIMER *timer,bool on) +{ + if (timer->id) + { + MyTimerId *id = (MyTimerId *)timer->id; + g_source_remove(id->source); + g_timer_destroy(id->timer); + g_free(id); + timer->id = 0; + } + + if (on) + { + MyTimerId *id = g_new(MyTimerId, 1); + id->timer = g_timer_new(); + id->timeout = timer->delay; + id->source = (intptr_t)g_timeout_add(timer->delay,(GSourceFunc)my_timer_function,(gpointer)timer); + timer->id = (intptr_t)id; + return; + } +} + +static void my_post(void) +{ + _post_check = true; +} + +void MAIN_check_quit() +{ + _must_check_quit = true; +} + +static int my_loop() +{ + gControl::cleanRemovedControls(); + _must_check_quit = true; + + for(;;) + { + if (_must_check_quit) + { + if (gApplication::mustQuit()) + break; + if (CWINDOW_must_quit() && CWatcher::count() == 0 && gTrayIcon::visibleCount() == 0) + break; + _must_check_quit = false; + } + MAIN_do_iteration(false); + } + + my_quit(); + + return 0; +} + +static void my_wait(int duration) +{ + if (gDrawingArea::inAnyDrawEvent()) + { + GB.Error("Wait is forbidden during a repaint event"); + return; + } + + if (duration > 0 && gKey::valid()) + { +#ifdef GTK3 + fprintf(stderr, "gb.gtk3: warning: calling the event loop during a keyboard event handler is ignored\n"); +#else + fprintf(stderr, "gb.gtk: warning: calling the event loop during a keyboard event handler is ignored\n"); +#endif + return; + } + + MAIN_do_iteration(true, true); +} + +static void my_watch(int fd, int type, void *callback, intptr_t param) +{ + CWatcher::Add(fd,type,callback,param); +} + +static void my_error(int code,char *error,char *where) +{ + char *showstr; + char scode[10]; + + sprintf(scode,"%d",code); + + showstr=g_strconcat("This application has raised an unexpected
    error and must abort.

    [", scode, "] ", error, ".\n", where, (void *)NULL); + + gMessage::setTitle(GB.Application.Title()); + gMessage::showError(showstr,NULL,NULL,NULL); + + g_free(showstr); +} + +static void my_lang(char *lang, int rtl) +{ + int i, n; + gControl *iter; + + MAIN_rtl = rtl; + + if (rtl) + gtk_widget_set_default_direction(GTK_TEXT_DIR_RTL); + else + gtk_widget_set_default_direction(GTK_TEXT_DIR_LTR); + + n = gApplication::controlCount(); + for (i = 0; i < n; i++) + { + iter = gApplication::controlItem(i); + if (iter->isVisible() && iter->isContainer()) + ((gContainer*)iter)->performArrange(); + } +} + +void MAIN_do_iteration_just_events() +{ + if (gtk_events_pending()) + gtk_main_iteration_do(false); +} + +void MAIN_do_iteration(bool do_not_block, bool do_not_sleep) +{ + gApplication::_loopLevel++; + + if (do_not_block) + { + if (gtk_events_pending ()) + gtk_main_iteration_do (false); + } + else + gtk_main_iteration_do(true); + + gApplication::_loopLevel--; + + if (_post_check) + { + _post_check = false; + GB.CheckPost(); + } + + gControl::cleanRemovedControls(); +} + +static GtkWidget *GTK_CreateGLArea(void *_object, void *parent, void (*init)(GtkWidget *)) +{ + gControl *ctrl = new gGLArea(CONTAINER(parent), init); + InitControl(ctrl, (CWIDGET*)_object); + //WIDGET->onExpose = Darea_Expose; + return ctrl->widget; +} + diff --git a/gb.gtk/src/main.h b/gb.gtk/src/main.h new file mode 100644 index 00000000..06985afc --- /dev/null +++ b/gb.gtk/src/main.h @@ -0,0 +1,60 @@ +/*************************************************************************** + + main.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb.image.h" +#include "gb.geom.h" +#include "gb.gtk.h" +#include "widgets.h" +#include "CWidget.h" + +#ifndef __MAIN_C +extern const GB_INTERFACE *GB_PTR; +extern IMAGE_INTERFACE IMAGE; +extern GEOM_INTERFACE GEOM; + +extern GB_CLASS CLASS_Control; +extern GB_CLASS CLASS_ContainerChildren; +extern GB_CLASS CLASS_Picture; +extern GB_CLASS CLASS_Image; +extern GB_CLASS CLASS_DrawingArea; +extern GB_CLASS CLASS_Menu; +extern GB_CLASS CLASS_Window; +extern GB_CLASS CLASS_Printer; +extern GB_CLASS CLASS_SvgImage; + +extern bool MAIN_debug_busy; +extern bool MAIN_rtl; +#endif + +#define GB (*GB_PTR) + +void MAIN_do_iteration(bool do_not_block, bool do_not_sleep = false); +void MAIN_do_iteration_just_events(); +void MAIN_check_quit(); + +#endif + diff --git a/gb.gtk/src/opengl/Makefile.am b/gb.gtk/src/opengl/Makefile.am new file mode 100644 index 00000000..48468dae --- /dev/null +++ b/gb.gtk/src/opengl/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.gtk.opengl +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gtk.opengl.la + +gb_gtk_opengl_la_LIBADD = @GTK_LIB@ @GTKOPENGL_LIB@ +gb_gtk_opengl_la_LDFLAGS = -module @LD_FLAGS@ @GTK_LDFLAGS@ @GTKOPENGL_LDFLAGS@ +gb_gtk_opengl_la_CPPFLAGS = @GTK_INC@ @GTKOPENGL_INC@ + +gb_gtk_opengl_la_SOURCES = \ + main.h main.c \ + c_glarea.h c_glarea.c + diff --git a/gb.gtk/src/opengl/c_glarea.c b/gb.gtk/src/opengl/c_glarea.c new file mode 100644 index 00000000..21cae6b3 --- /dev/null +++ b/gb.gtk/src/opengl/c_glarea.c @@ -0,0 +1,185 @@ +/*************************************************************************** + + c_glarea.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_GLAREA_C + +#include +#include "c_glarea.h" + +//-- GLArea ----------------------------------------------------------------- + +DECLARE_EVENT(EVENT_Open); +DECLARE_EVENT(EVENT_Draw); +DECLARE_EVENT(EVENT_Resize); + +static void cb_init_ext(GtkWidget *widget) +{ + GdkGLConfig *config; // la config qui va aller avec + + static const gint attrList[] = { // les paramètres de la config + GDK_GL_DOUBLEBUFFER, + GDK_GL_RGBA, + GDK_GL_RED_SIZE, 1, + GDK_GL_GREEN_SIZE, 1, + GDK_GL_BLUE_SIZE, 1, + GDK_GL_ALPHA_SIZE, 1, + GDK_GL_DEPTH_SIZE, 1, + GDK_GL_ATTRIB_LIST_NONE }; + + config = gdk_gl_config_new(attrList); + + /* ajout du support Opengl */ + gtk_widget_set_gl_capability(widget, config, NULL, TRUE, GDK_GL_RGBA_TYPE); +} + +static void init_control(void *_object) +{ + GdkGLContext *context; + GdkGLDrawable *surface; + GtkWidget *widget = THIS->widget; + + if (THIS->init) + return; + + //fprintf(stderr, "init_control: %p\n", THIS); + + /* recuperation du contexte et de la surface de notre widget */ + context = gtk_widget_get_gl_context(widget); + surface = gtk_widget_get_gl_drawable(widget); + + /* activation du contexte */ + if (gdk_gl_drawable_gl_begin(surface, context)) + { + GL.Init(); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + GB.Raise(THIS, EVENT_Open, 0); + GB.Raise(THIS, EVENT_Resize, 0); + gdk_gl_drawable_gl_end(surface); // désactivation du contexte + } + + THIS->init = TRUE; + + return; +} + +static gboolean cb_reshape_ext(GtkWidget *widget, GdkEventConfigure *ev, void *_object) +{ + /* recuperation du contexte et de la surface de notre widget */ + GdkGLContext *context; + GdkGLDrawable *surface; + + if (!THIS->init) + return TRUE; + + //fprintf(stderr, "cb_reshape_ext: %p\n", THIS); + + if (!gtk_widget_is_gl_capable(widget)) + { + fprintf(stderr, "not capable!\n"); + return TRUE; + } + + context = gtk_widget_get_gl_context(widget); + surface = gtk_widget_get_gl_drawable(widget); + + /* activation du contexte */ + if(gdk_gl_drawable_gl_begin(surface, context)) + { + //reshape(ev−>height,ev−>width); // redimensionnement Opengl + GB.Raise(THIS, EVENT_Resize, 0); + gdk_gl_drawable_gl_end(surface); // désactivation du contexte + } + + return TRUE; +} + +static gboolean cb_draw_ext(GtkWidget *widget, GdkEventExpose *e, void *_object) +{ + /* recuperation du contexte et de la surface de notre widget */ + GdkGLContext *context; + GdkGLDrawable *surface; + + //fprintf(stderr, "cb_draw_ext: %p\n", THIS); + + if (!gtk_widget_is_gl_capable(widget)) + { + fprintf(stderr, "not capable!\n"); + return TRUE; + } + + context = gtk_widget_get_gl_context(widget); + surface = gtk_widget_get_gl_drawable(widget); + + /* activation du contexte */ + if(gdk_gl_drawable_gl_begin(surface, context)) + { + //draw(); // dessin Opengl + init_control(THIS); + GB.Raise(THIS, EVENT_Draw, 0); + gdk_gl_drawable_swap_buffers(surface); // permutation tampons + gdk_gl_drawable_gl_end(surface); // désactivation du contexte + } + + return TRUE; +} + + +BEGIN_METHOD(GLArea_new, GB_OBJECT parent) + + THIS->widget = GTK.CreateGLArea(THIS, VARG(parent), cb_init_ext); + + if (!gtk_widget_is_gl_capable(THIS->widget)) + { + GB.Error("Unable to set OpenGL capability"); + return; + } + + g_signal_connect(G_OBJECT(THIS->widget), "configure-event", G_CALLBACK(cb_reshape_ext), (gpointer)THIS); + g_signal_connect(G_OBJECT(THIS->widget), "expose-event", G_CALLBACK(cb_draw_ext), (gpointer)THIS); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC GLAreaDesc[] = +{ + GB_DECLARE("GLArea", sizeof(CGLAREA)), GB_INHERITS("Control"), + + //GB_STATIC_METHOD("_exit", NULL, GLArea_exit, NULL), + + GB_METHOD("_new", NULL, GLArea_new, "(Parent)Container;"), + //GB_METHOD("_free", NULL, GLArea_free, NULL), + //GB_METHOD("Update", NULL, GLArea_update, NULL), + //GB_METHOD("Refresh", NULL, GLArea_Refresh, NULL), + //GB_METHOD("Select", NULL, CGLAREA_select, NULL), + //GB_METHOD("Text", NULL, CGLAREA_text, "(Text)s(X)i(Y)i"), + + GB_CONSTANT("_Group", "s", "Special"), + + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + + GB_END_DECLARE +}; diff --git a/gb.gtk/src/opengl/c_glarea.h b/gb.gtk/src/opengl/c_glarea.h new file mode 100644 index 00000000..21fd6934 --- /dev/null +++ b/gb.gtk/src/opengl/c_glarea.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + c_glarea.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_GLAREA_H +#define __C_GLAREA_H + +#include "main.h" + +typedef + struct { + GTK_CONTROL control; + GtkWidget *widget; + bool init; + } + CGLAREA; + +#ifndef __C_GLAREA_C +extern GB_DESC GLAreaDesc[]; +#else + +#define THIS ((CGLAREA *)_object) +#define WIDGET ((GLarea *)((GTK_CONTROL *)_object)->widget) + +//#define CGLAREA_PROPERTIES QT_WIDGET_PROPERTIES + +#endif /* __CGLAREA_CPP */ + +#endif diff --git a/gb.gtk/src/opengl/gb.gtk.opengl.component b/gb.gtk/src/opengl/gb.gtk.opengl.component new file mode 100644 index 00000000..fdb78303 --- /dev/null +++ b/gb.gtk/src/opengl/gb.gtk.opengl.component @@ -0,0 +1,6 @@ +[Component] +Author=Benoît Minisini +Require=gb.gtk,gb.opengl +Type=Form +Implement=OpenGLViewer +State=Stable diff --git a/gb.gtk/src/opengl/main.c b/gb.gtk/src/opengl/main.c new file mode 100644 index 00000000..abf8fde9 --- /dev/null +++ b/gb.gtk/src/opengl/main.c @@ -0,0 +1,61 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +#include "c_glarea.h" + +GB_INTERFACE GB EXPORT; +GTK_INTERFACE GTK; +GL_INTERFACE GL; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + GLAreaDesc, + NULL +}; + +static void *_old_hook_main; + +static void hook_main(int *argc, char ***argv) +{ + gtk_init(argc, argv); + gtk_gl_init(argc, argv); + + CALL_HOOK_MAIN(_old_hook_main, argc, argv); +} + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.gtk", GTK_INTERFACE_VERSION, >K); + GB.GetInterface("gb.opengl", GL_INTERFACE_VERSION, &GL); + + _old_hook_main = GB.Hook(GB_HOOK_MAIN, (void *)hook_main); + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.gtk/src/opengl/main.h b/gb.gtk/src/opengl/main.h new file mode 100644 index 00000000..e95eb144 --- /dev/null +++ b/gb.gtk/src/opengl/main.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "../gb.gtk.h" +#include "gb.gl.h" +#include +#include + +#ifndef __MAIN_CPP +extern GB_INTERFACE GB; +extern GTK_INTERFACE GTK; +extern GL_INTERFACE GL; +#endif + +#endif diff --git a/gb.gtk/src/sm/bonobo-macros.h b/gb.gtk/src/sm/bonobo-macros.h new file mode 100644 index 00000000..fdf49568 --- /dev/null +++ b/gb.gtk/src/sm/bonobo-macros.h @@ -0,0 +1,100 @@ +/** + * Useful macros. + * + * Author: + * Darin Adler + * + * Copyright 2001 Ben Tea Spoons, Inc. + */ +#ifndef _BONOBO_MACROS_H_ +#define _BONOBO_MACROS_H_ + +#include + +G_BEGIN_DECLS + +/* Macros for defining classes. Ideas taken from Nautilus and GOB. */ + +/* Define the boilerplate type stuff to reduce typos and code size. Defines + * the get_type method and the parent_class static variable. */ + +#define BONOBO_BOILERPLATE(type, type_as_function, corba_type, \ + parent_type, parent_type_macro, \ + register_type_macro) \ +static void type_as_function ## _class_init (type ## Class *klass); \ +static void type_as_function ## _instance_init (type *object); \ +static parent_type ## Class *parent_class = NULL; \ +static void \ +type_as_function ## _class_init_trampoline (gpointer klass, \ + gpointer data) \ +{ \ + parent_class = (parent_type ## Class *)g_type_class_ref ( \ + parent_type_macro); \ + type_as_function ## _class_init ((type ## Class *)klass); \ +} \ +GType \ +type_as_function ## _get_type (void) \ +{ \ + static GType object_type = 0; \ + if (object_type == 0) { \ + static const GTypeInfo object_info = { \ + sizeof (type ## Class), \ + NULL, /* base_init */ \ + NULL, /* base_finalize */ \ + type_as_function ## _class_init_trampoline, \ + NULL, /* class_finalize */ \ + NULL, /* class_data */ \ + sizeof (type), \ + 0, /* n_preallocs */ \ + (GInstanceInitFunc) type_as_function ## _instance_init \ + }; \ + object_type = register_type_macro \ + (type, type_as_function, corba_type, \ + parent_type, parent_type_macro); \ + } \ + return object_type; \ +} + +/* Just call the parent handler. This assumes that there is a variable + * named parent_class that points to the (duh!) parent class. Note that + * this macro is not to be used with things that return something, use + * the _WITH_DEFAULT version for that */ +#define BONOBO_CALL_PARENT(parent_class_cast, name, args) \ + ((parent_class_cast(parent_class)->name != NULL) ? \ + parent_class_cast(parent_class)->name args : (void)0) + +/* Same as above, but in case there is no implementation, it evaluates + * to def_return */ +#define BONOBO_CALL_PARENT_WITH_DEFAULT(parent_class_cast, \ + name, args, def_return) \ + ((parent_class_cast(parent_class)->name != NULL) ? \ + parent_class_cast(parent_class)->name args : def_return) + + +#define BONOBO_CLASS_BOILERPLATE(type, type_as_function, \ + parent_type, parent_type_macro) \ + BONOBO_BOILERPLATE(type, type_as_function, type, \ + parent_type, parent_type_macro, \ + BONOBO_REGISTER_TYPE) +#define BONOBO_REGISTER_TYPE(type, type_as_function, corba_type, \ + parent_type, parent_type_macro) \ + bonobo_type_unique (parent_type_macro, NULL, NULL, 0, \ + &object_info, #type) + +#define BONOBO_CLASS_BOILERPLATE_FULL(type, type_as_function, \ + corba_type, \ + parent_type, parent_type_macro) \ + BONOBO_BOILERPLATE(type, type_as_function, corba_type, \ + parent_type, parent_type_macro, \ + BONOBO_REGISTER_TYPE_FULL) +#define BONOBO_REGISTER_TYPE_FULL(type, type_as_function, corba_type, \ + parent_type, parent_type_macro) \ + bonobo_type_unique (parent_type_macro, \ + POA_##corba_type##__init, \ + POA_##corba_type##__fini, \ + G_STRUCT_OFFSET (type##Class, epv), \ + &object_info, #type) + +G_END_DECLS + +#endif /* _BONOBO_MACROS_H_ */ diff --git a/gb.gtk/src/sm/gnome-client.c b/gb.gtk/src/sm/gnome-client.c new file mode 100644 index 00000000..cb9838cb --- /dev/null +++ b/gb.gtk/src/sm/gnome-client.c @@ -0,0 +1,3083 @@ +/* gnome-client.c - GNOME session management client support + * + * Copyright (C) 1998, 1999 Carsten Schaar + * All rights reserved. + * + * Author: Carsten Schaar + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Cambridge, MA 02139, USA. + */ +/* + @NOTATION@ +*/ + +#define _XOPEN_SOURCE 500 + +#include +#include +#include +#include +#include +#include + +/* Must be before all other gnome includes!! */ +//#include + +#include "gnome-client.h" +#include "gnome-uidefs.h" +#include "gnome-ice.h" +#include "gnome-marshal.h" + +#include "libgnomeuiP.h" + +#ifdef HAVE_LIBSM +#include +#endif /* HAVE_LIBSM */ + +//#define DEBUG_ME + +static int _sm_desktop = -1; + +static GtkWidget *client_grab_widget = NULL; + +enum { + SAVE_YOURSELF, + DIE, + SAVE_COMPLETE, + SHUTDOWN_CANCELLED, + CONNECT, + DISCONNECT, + LAST_SIGNAL +}; + +//static void gnome_real_client_destroy (GtkObject *object); +static void gnome_real_client_finalize (GObject *object); +static void gnome_real_client_save_complete (GnomeClient *client); +static void gnome_real_client_shutdown_cancelled (GnomeClient *client); +static void gnome_real_client_connect (GnomeClient *client, + gboolean restarted); +static void gnome_real_client_disconnect (GnomeClient *client); +static void master_client_connect (GnomeClient *client, + gboolean restarted, + gpointer client_data); +static void master_client_disconnect (GnomeClient *client, + gpointer client_data); + + +static void client_unset_config_prefix (GnomeClient *client); + +static gchar** array_init_from_arg (gint argc, + gchar *argv[]); + +static gint client_signals[LAST_SIGNAL] = { 0 }; + +static const char *sm_client_id_arg_name G_GNUC_UNUSED = "-session"; +static const char *sm_client_desktop_arg_name G_GNUC_UNUSED = "-session-desktop"; +static const char *sm_config_prefix_arg_name G_GNUC_UNUSED = "--sm-config-prefix"; +#ifdef HAVE_GTK_MULTIHEAD +static const char *sm_screen G_GNUC_UNUSED = "--screen"; +#endif + +//G_GNUC_INTERNAL extern void _gnome_ui_gettext_init (gboolean bind_codeset); + +/* The master client. */ +static GnomeClient *master_client= NULL; + +static gboolean master_client_restored= FALSE; + +// BM: device grab management + +static gboolean gt_pointer_is_grabbed() +{ +#ifdef GTK3 + GdkDevice *pointer = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default())); + return gdk_display_device_is_grabbed(gdk_display_get_default(), pointer); +#else + return gdk_pointer_is_grabbed(); +#endif +} + +static void gt_ungrab(void) +{ +#ifdef GTK3 + GdkDevice *pointer = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default())); + GdkDevice *keyboard = gdk_device_get_associated_device(pointer); + + gdk_device_ungrab(pointer, GDK_CURRENT_TIME); + gdk_device_ungrab(keyboard, GDK_CURRENT_TIME); +#else + gdk_pointer_ungrab(GDK_CURRENT_TIME); + gdk_keyboard_ungrab(GDK_CURRENT_TIME); +#endif +} + +void gnome_type_init() +{ + (void) gnome_interact_style_get_type (); + (void) gnome_dialog_type_get_type (); + (void) gnome_save_style_get_type (); + (void) gnome_restart_style_get_type (); + (void) gnome_client_state_get_type (); + (void) gnome_client_flags_get_type (); +} + +/*****************************************************************************/ +/* Managing the interaction keys. */ + +/* The following function handle the interaction keys. Each time an + application requests interaction an interaction key is created. If + the session manager decides, that a client may interact now, the + application is given the according interaction key. No other + interaction may take place, until the application returns the + application key. */ + +#ifdef HAVE_LIBSM + +typedef struct _InteractionKey InteractionKey; + + +struct _InteractionKey +{ + gint tag; + GnomeClient *client; + GnomeDialogType dialog_type; + gboolean in_use; + gboolean interp; + GnomeInteractFunction function; + gpointer data; + GDestroyNotify destroy; +}; + + +/* List of all existing interaction keys. */ +static GList *interact_functions = NULL; + + +static InteractionKey * +interaction_key_new (GnomeClient *client, + GnomeDialogType dialog_type, + gboolean interp, + GnomeInteractFunction function, + gpointer data, + GDestroyNotify destroy) +{ + static gint tag= 1; + + InteractionKey *key= g_new (InteractionKey, 1); + + if (key) + { + key->tag = tag++; + key->client = client; + key->dialog_type= dialog_type; + key->in_use = FALSE; + key->interp = interp; + key->function = function; + key->data = data; + key->destroy = destroy; + + interact_functions= g_list_append (interact_functions, key); + } + + return key; +} + + +static void +interaction_key_destroy (InteractionKey *key) +{ + interact_functions= g_list_remove (interact_functions, key); + + if (key->destroy) + (key->destroy) (key->data); + + g_free (key); +} + + +static void +interaction_key_destroy_if_possible (InteractionKey *key) +{ + if (key->in_use) + key->client = NULL; + else + interaction_key_destroy (key); +} + + +static InteractionKey * +interaction_key_find_by_tag (gint tag) +{ + InteractionKey *key; + GList *tmp= interact_functions; + + while (tmp) + { + key= (InteractionKey *) tmp->data; + + if (key->tag == tag) + return key; + + tmp= tmp->next; + } + + return NULL; +} + + +static void +interaction_key_use (InteractionKey *key) +{ + key->in_use= TRUE; + + if (!key->interp) + key->function (key->client, key->tag, key->dialog_type, key->data); +} + +#endif /* HAVE_LIBSM */ + + +/*****************************************************************************/ +/* Helper functions, that set session management properties. */ + +/* The following functions are used to set and unset session + management properties of a special type. */ + +#ifdef HAVE_LIBSM + + +static void +client_set_value (GnomeClient *client, + gchar *name, + char *type, + int num_vals, + SmPropValue *vals) +{ + SmProp *proplist[1]; + SmProp prop; + + prop.name = name; + prop.type = type; + prop.num_vals = num_vals; + prop.vals = vals; + + proplist[0]= ∝ + SmcSetProperties ((SmcConn) client->smc_conn, 1, proplist); +} + + +static void +client_set_string (GnomeClient *client, gchar *name, gchar *value) +{ + SmPropValue val; + + g_return_if_fail (name); + + if (!GNOME_CLIENT_CONNECTED (client) || (value == NULL)) + return; + + val.length = strlen (value)+1; + val.value = value; + + client_set_value (client, name, SmARRAY8, 1, &val); +} + + +static void +client_set_gchar (GnomeClient *client, gchar *name, gchar value) +{ + SmPropValue val; + + g_return_if_fail (name); + + if (!GNOME_CLIENT_CONNECTED (client)) + return; + + val.length = 1; + val.value = &value; + + client_set_value (client, name, SmCARD8, 1, &val); +} + + +static void +client_set_ghash0 (gchar *key, gchar *value, SmPropValue **vals) +{ + (*vals)->length= strlen (key); + (*vals)->value = key; + (*vals)++; + + (*vals)->length= strlen (value); + (*vals)->value = value; + (*vals)++; +} + +static void +client_set_ghash (GnomeClient *client, gchar *name, GHashTable *table) +{ + gint argc; + SmPropValue *vals; + SmPropValue *tmp; + + g_return_if_fail (name); + g_return_if_fail (table); + + if (!GNOME_CLIENT_CONNECTED (client)) + return; + + /* multiply by 2 because for each element + * we have a key and a value + */ + argc = 2 * g_hash_table_size (table); + + if (argc == 0) + return; + + /* Now initialize the 'vals' array. */ + vals = g_new (SmPropValue, argc); + tmp = vals; + + g_hash_table_foreach (table, (GHFunc) client_set_ghash0, &tmp); + + client_set_value (client, name, SmLISTofARRAY8, argc, vals); + + g_free (vals); + +} + + +static void +client_set_array (GnomeClient *client, gchar *name, gchar *array[]) +{ + gint argc; + gchar **ptr; + gint i; + + SmPropValue *vals; + + g_return_if_fail (name); + + if (!GNOME_CLIENT_CONNECTED (client) || (array == NULL)) + return; + + /* We count the number of elements in our array. */ + for (ptr = array, argc = 0; *ptr ; ptr++, argc++) /* LOOP */; + + /* Now initialize the 'vals' array. */ + vals = g_new (SmPropValue, argc); + for (ptr = array, i = 0 ; i < argc ; ptr++, i++) + { + vals[i].length = strlen (*ptr); + vals[i].value = *ptr; + } + + client_set_value (client, name, SmLISTofARRAY8, argc, vals); + + g_free (vals); +} + +static void +client_set_clone_command (GnomeClient *client) +{ + GList *list; + gint argc; + gchar **ptr; + gint i = 0; + + SmPropValue *vals; + + if (!GNOME_CLIENT_CONNECTED (client)) + return; + + ptr=client->clone_command ? client->clone_command : client->restart_command; + + if (!ptr) + return; + + for (argc = 0; *ptr ; ptr++) argc++; + + /* Add space for static arguments and config prefix. */ + argc += g_list_length (client->static_args) + 2; + + vals = g_new (SmPropValue, argc); + + ptr=client->clone_command ? client->clone_command : client->restart_command; + + vals[i].length = strlen (*ptr); + vals[i++].value = *ptr++; + + if (client->config_prefix) + { + vals[i].length = strlen (sm_config_prefix_arg_name); + vals[i++].value = (char *) sm_config_prefix_arg_name; + vals[i].length = strlen (client->config_prefix); + vals[i++].value = client->config_prefix; + } + + for (list = client->static_args; list; list= g_list_next (list)) + { + vals[i].length= strlen ((gchar *)list->data); + vals[i++].value = (gchar *)list->data; + } + + while (*ptr) + { + vals[i].length = strlen (*ptr); + vals[i++].value = *ptr++; + } + + client_set_value (client, SmCloneCommand, SmLISTofARRAY8, i, vals); + + g_free (vals); +} + +static void +client_set_restart_command (GnomeClient *client) +{ + GList *list; + gint argc; + gchar **ptr; + gint i = 0; + gchar *tmp; + +#ifdef HAVE_GTK_MULTIHEAD + gint disp; +#endif + + SmPropValue *vals; + + if (!GNOME_CLIENT_CONNECTED (client) || (client->restart_command == NULL)) + return; + +#ifdef DEBUG_ME + fprintf(stderr, "client_set_restart_command: desktop = %d\n", _sm_desktop); +#endif + + for (ptr = client->restart_command, argc = 0; *ptr ; ptr++) argc++; + + /* Add space for static arguments, desktop and client id. */ + argc += g_list_length (client->static_args) + 2; + if (_sm_desktop >= 0) + argc += 2; + +#ifdef HAVE_GTK_MULTIHEAD + /* add two more for --screen */ + argc += 2; +#endif + + vals = g_new (SmPropValue, argc); + + ptr = client->restart_command; + + vals[i].length = strlen (*ptr); + vals[i++].value = *ptr++; + + for (list = client->static_args; list; list = g_list_next (list)) + { + vals[i].length= strlen ((gchar *)list->data); + vals[i++].value = (gchar *)list->data; + } + + while (*ptr) + { + vals[i].length = strlen (*ptr); + vals[i++].value = *ptr++; + } + + /*if (client->config_prefix) + { + vals[i].length = strlen (sm_config_prefix_arg_name); + vals[i++].value = (char *) sm_config_prefix_arg_name; + vals[i].length = strlen (client->config_prefix); + vals[i++].value = client->config_prefix; + }*/ + + vals[i].length = strlen (sm_client_id_arg_name); + vals[i++].value = (char *) sm_client_id_arg_name; + vals[i].length = strlen (client->client_id); + vals[i++].value = client->client_id; + + if (_sm_desktop >= 0) + { + tmp = g_strdup_printf ("%d", _sm_desktop); + vals[i].length = strlen(sm_client_desktop_arg_name); + vals[i++].value = (char *) sm_client_desktop_arg_name; + vals[i].length = strlen(tmp); + vals[i++].value = tmp; + } + +#ifdef HAVE_GTK_MULTIHEAD + /* set the screen number */ + disp = gdk_screen_get_number (gdk_screen_get_default ()); + tmp = g_strdup_printf ("%d", disp); + vals[i].length = strlen (sm_screen); + vals[i++].value = (char *)sm_screen; + vals[i].length = strlen (tmp); + vals[i++].value = tmp; +#endif + +#ifdef DEBUG_ME + { + int j; + + fprintf(stderr, "client_set_restart_command: "); + for (j = 0; j < i; j++) + fprintf(stderr, "%s ", (char *)vals[j].value); + fputc('\n', stderr); + } +#endif + + client_set_value (client, SmRestartCommand, SmLISTofARRAY8, i, vals); + + g_free (vals); + +#ifdef HAVE_GTK_MULTIHEAD + g_free (tmp); +#endif +} + +static void +client_unset (GnomeClient *client, gchar *name) +{ + g_return_if_fail (name != NULL); + + if (GNOME_CLIENT_CONNECTED (client)) + SmcDeleteProperties ((SmcConn) client->smc_conn, 1, &name); +} + +#endif /* HAVE_LIBSM */ + +static void +client_set_state (GnomeClient *client, GnomeClientState state) +{ + client->state= state; +} + + +/*****************************************************************************/ +/* Callback functions */ + +/* The following functions are callback functions (and helper + functions). They are registered in 'gnome_client_connect', + 'gnome_interaction_key_return' and + 'gnome_client_request_interaction_internal', + 'client_save_yourself_callback'. */ + +#ifdef HAVE_LIBSM + + +static void +client_save_phase_2_callback (SmcConn smc_conn, SmPointer client_data); + +static void +client_save_yourself_possibly_done (GnomeClient *client) +{ +#ifdef DEBUG_ME + fprintf(stderr, "%s\n", __func__); +#endif + + if (client->interaction_keys) + return; + + if ((client->state == GNOME_CLIENT_SAVING_PHASE_1) && + client->save_phase_2_requested) + { + Status status; + + status= SmcRequestSaveYourselfPhase2 ((SmcConn) client->smc_conn, + client_save_phase_2_callback, + (SmPointer) client); + + if (status) + client_set_state (client, GNOME_CLIENT_WAITING_FOR_PHASE_2); + } + + if ((client->state == GNOME_CLIENT_SAVING_PHASE_1) || + (client->state == GNOME_CLIENT_SAVING_PHASE_2)) + { + SmcSaveYourselfDone ((SmcConn) client->smc_conn, + client->save_successfull); + + if (client->shutdown) + client_set_state (client, GNOME_CLIENT_FROZEN); + else + client_set_state (client, GNOME_CLIENT_IDLE); + } +} + + +static void +client_save_phase_2_callback (SmcConn smc_conn, SmPointer client_data) +{ + GnomeClient *client= (GnomeClient*) client_data; + gboolean ret; + +#ifdef DEBUG_ME + fprintf(stderr, "%s\n", __func__); +#endif + + client_set_state (client, GNOME_CLIENT_SAVING_PHASE_2); + + g_signal_emit (client, client_signals[SAVE_YOURSELF], 0, + 2, + client->save_style, + client->shutdown, + client->interact_style, + client->fast, + &ret); + + client_save_yourself_possibly_done (client); +} + +static gint +end_wait (gpointer data) +{ + gboolean *waiting = (gboolean*)data; + *waiting = FALSE; + return 0; +} + +static void +client_save_yourself_callback (SmcConn smc_conn, + SmPointer client_data, + int save_style, + gboolean shutdown, + int interact_style, + gboolean fast) +{ + GnomeClient *client= (GnomeClient*) client_data; + //gchar *name, *prefix; + //int fd, len; + gboolean ret; + +#ifdef DEBUG_ME + fprintf(stderr, "%s\n", __func__); +#endif + + if (!client_grab_widget) + { + GDK_THREADS_ENTER(); + client_grab_widget = gtk_widget_new (GTK_TYPE_INVISIBLE, NULL); + GDK_THREADS_LEAVE(); + } + + /* The first SaveYourself after registering for the first time + * is a special case (SM specs 7.2). + * + * This SaveYourself seems to be included in the protocol to + * ask the client to specify its initial SmProperties since + * there is little point saving a copy of the initial state. + * + * A bug in xsm means that it does not send us a SaveComplete + * in response to this initial SaveYourself. Therefore, we + * must not set a grab because it would never be released. + * Indeed, even telling the app that this SaveYourself has + * arrived is hazardous as the app may take its own steps + * to freeze its WM state while waiting for the SaveComplete. + * + * Fortunately, we have already set the SmProperties during + * gnome_client_connect so there is little lost in simply + * returning immediately. + * + * Apps which really want to save their initial states can + * do so safely using gnome_client_save_yourself_request. */ + + if (client->state == GNOME_CLIENT_REGISTERING) + { + client_set_state (client, GNOME_CLIENT_IDLE); + + /* Double check that this is a section 7.2 SaveYourself: */ + + if (save_style == SmSaveLocal && + interact_style == SmInteractStyleNone && + !shutdown && !fast) + { + /* The protocol requires this even if xsm ignores it. */ + SmcSaveYourselfDone ((SmcConn) client->smc_conn, TRUE); + return; + } + } + + switch (save_style) + { + case SmSaveGlobal: + client->save_style= GNOME_SAVE_GLOBAL; + break; + + case SmSaveLocal: + client->save_style= GNOME_SAVE_LOCAL; + break; + + case SmSaveBoth: + default: + client->save_style= GNOME_SAVE_BOTH; + break; + } + client->shutdown= shutdown; + switch (interact_style) + { + case SmInteractStyleErrors: + client->interact_style= GNOME_INTERACT_ERRORS; + break; + + case SmInteractStyleAny: + client->interact_style= GNOME_INTERACT_ANY; + break; + + case SmInteractStyleNone: + default: + client->interact_style= GNOME_INTERACT_NONE; + break; + } + client->fast = fast; + + client->save_phase_2_requested= FALSE; + client->save_successfull = TRUE; + client->save_yourself_emitted = FALSE; + + client_set_state (client, GNOME_CLIENT_SAVING_PHASE_1); + + GDK_THREADS_ENTER(); + + if (gt_pointer_is_grabbed()) + { + gboolean waiting = TRUE; + gint id = g_timeout_add (4000, end_wait, &waiting); + + while (gt_pointer_is_grabbed() && waiting) + gtk_main_iteration(); + g_source_remove (id); + } + + /* Check that we did not receive a shutdown cancelled while waiting + * for the grab to be released. The grab should prevent it but ... */ + if (client->state != GNOME_CLIENT_SAVING_PHASE_1) + { + GDK_THREADS_LEAVE(); + return; + } + + gt_ungrab(); + gtk_grab_add (client_grab_widget); + + GDK_THREADS_LEAVE(); + +#if 0 + name = g_strdup (gnome_client_get_global_config_prefix(client)); + name[strlen (name) - 1] = '\0'; + + prefix = g_strconcat (name, "-XXXXX", "/", NULL); + g_free (name); + len = strlen (prefix); + + name = gnome_config_get_real_path (prefix); + g_free (prefix); + + name [strlen (name) - 1] = 'X'; + fd = mkstemp (name); + + if (fd != -1) + { + unlink (name); + close (fd); + g_free (client->config_prefix); + client->config_prefix = g_strconcat (name+strlen(name) - len, "/", NULL); + + if (client == master_client) + { + /* The config prefix has been changed, so it cannot be used + anymore to restore a saved session. */ + master_client_restored= FALSE; + } + } + g_free (name); +#endif + + g_signal_emit (client, client_signals[SAVE_YOURSELF], 0, + 1, + client->save_style, + shutdown, + client->interact_style, + fast, + &ret); + + client_set_clone_command (client); + client_set_restart_command (client); + +#ifdef BREAK_KDE_SESSION_MANAGER + /* The KDE session manager actually cares about the `success' + field of the SaveYourselfDone message (unlike gnome-session, which + totally ignores it. Hence the code below has the effect of making + KDE unable to shutdown when any GNOME apps are running that haven't + connected to the "save_yourself" signal. */ + + if (!client->save_yourself_emitted) + client->save_successfull= FALSE; +#endif + + client_save_yourself_possibly_done (client); +} + + +static void +client_die_callback (SmcConn smc_conn, SmPointer client_data) +{ + GnomeClient *client= (GnomeClient*) client_data; + +#ifdef DEBUG_ME + fprintf(stderr, "%s\n", __func__); +#endif + + if (client_grab_widget) + { + GDK_THREADS_ENTER(); + gtk_grab_remove (client_grab_widget); + GDK_THREADS_LEAVE(); + } + + g_signal_emit (client, client_signals[DIE], 0); +} + + +static void +client_save_complete_callback (SmcConn smc_conn, SmPointer client_data) +{ + GnomeClient *client = (GnomeClient*) client_data; + +#ifdef DEBUG_ME + fprintf(stderr, "%s\n", __func__); +#endif + + if (client_grab_widget) + { + GDK_THREADS_ENTER(); + gtk_grab_remove (client_grab_widget); + GDK_THREADS_LEAVE(); + } + + g_signal_emit (client, client_signals[SAVE_COMPLETE], 0); +} + + +static void +client_shutdown_cancelled_callback (SmcConn smc_conn, SmPointer client_data) +{ + GnomeClient *client= (GnomeClient*) client_data; + +#ifdef DEBUG_ME + fprintf(stderr, "%s\n", __func__); +#endif + + if (client_grab_widget) + { + GDK_THREADS_ENTER(); + gtk_grab_remove (client_grab_widget); + GDK_THREADS_LEAVE(); + } + + g_signal_emit (client, client_signals[SHUTDOWN_CANCELLED], 0); +} + + +static void +client_interact_callback (SmcConn smc_conn, SmPointer client_data) +{ + GnomeClient *client= (GnomeClient *) client_data; + +#ifdef DEBUG_ME + fprintf(stderr, "%s\n", __func__); +#endif + + if (client->interaction_keys) + { + GSList *tmp= client->interaction_keys; + InteractionKey *key= (InteractionKey *) tmp->data; + + client->interaction_keys= g_slist_remove (tmp, tmp->data); + + interaction_key_use (key); + } + else + { + /* This branch should never be executed. But if it is executed, + we just finish interacting. */ + SmcInteractDone ((SmcConn) client->smc_conn, FALSE); + } +} + + +#endif /* HAVE_LIBSM */ + + +/*****************************************************************************/ +/* Managing the master client */ + +#if 0 +/* The following environment variables will be set on the master + client, if they are defined the programs environment. The array + must end with a NULL entry. + For now we have no entries. You might think that saving DISPLAY, + or HOME, or something like that would be right. It isn't. We + definitely want to inherit these values from the user's (possibly + changing) environment. */ +static char* master_environment[]= +{ + NULL +}; +#endif + +/********* gnome_client module */ + +#if 0 +/* Forward declaration for our module functions. */ +static void client_parse_func (poptContext ctx, + enum poptCallbackReason reason, + const struct poptOption *opt, + const char *arg, void *data); + +static gboolean gnome_client_goption_sm_config_prefix (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error); + +static gboolean gnome_client_goption_sm_disable (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error); + +static void gnome_client_pre_args_parse(GnomeProgram *app, GnomeModuleInfo *mod_info); +static void gnome_client_post_args_parse(GnomeProgram *app, GnomeModuleInfo *mod_info); +#endif + +enum { ARG_SM_CLIENT_ID=1, ARG_SM_CONFIG_PREFIX, ARG_SM_DISABLE }; + +typedef struct { + guint sm_connect_id; +} GnomeProgramClass_gnome_client; + +typedef struct { + gboolean sm_connect; +} GnomeProgramPrivate_gnome_client; + +//static gchar *gnome_client_goption_sm_client_id = NULL; + +//static GQuark quark_gnome_program_private_gnome_client = 0; +//static GQuark quark_gnome_program_class_gnome_client = 0; + +#if 0 +static void +gnome_client_module_get_property (GObject *object, guint param_id, GValue *value, + GParamSpec *pspec) +{ + GnomeProgramClass_gnome_client *cdata; + GnomeProgramPrivate_gnome_client *priv; + GnomeProgram *program; + + g_return_if_fail (object != NULL); + g_return_if_fail (GNOME_IS_PROGRAM (object)); + + program = GNOME_PROGRAM (object); + + cdata = g_type_get_qdata (G_OBJECT_TYPE (program), + quark_gnome_program_class_gnome_client); + priv = g_object_get_qdata (G_OBJECT (program), + quark_gnome_program_private_gnome_client); + + if (param_id == cdata->sm_connect_id) + g_value_set_boolean (value, priv->sm_connect); + else + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec); +} + +static void +gnome_client_module_set_property (GObject *object, guint param_id, + const GValue *value, GParamSpec *pspec) +{ + GnomeProgramClass_gnome_client *cdata; + GnomeProgramPrivate_gnome_client *priv; + GnomeProgram *program; + + g_return_if_fail (object != NULL); + g_return_if_fail (GNOME_IS_PROGRAM (object)); + + program = GNOME_PROGRAM (object); + + cdata = g_type_get_qdata (G_OBJECT_TYPE (program), + quark_gnome_program_class_gnome_client); + priv = g_object_get_qdata (G_OBJECT (program), + quark_gnome_program_private_gnome_client); + + if (param_id == cdata->sm_connect_id) { + priv->sm_connect = g_value_get_boolean (value); + } else { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + } +} + +static void +gnome_client_module_init_pass (const GnomeModuleInfo *mod_info) +{ + if (!quark_gnome_program_private_gnome_client) + quark_gnome_program_private_gnome_client = g_quark_from_static_string + ("gnome-program-private:gnome_client"); + + if (!quark_gnome_program_class_gnome_client) + quark_gnome_program_class_gnome_client = g_quark_from_static_string + ("gnome-program-class:gnome_client"); +} + +static void +gnome_client_module_class_init (GnomeProgramClass *klass, const GnomeModuleInfo *mod_info) +{ + GnomeProgramClass_gnome_client *cdata = g_new0 (GnomeProgramClass_gnome_client, 1); + + g_type_set_qdata (G_OBJECT_CLASS_TYPE (klass), quark_gnome_program_class_gnome_client, cdata); + + cdata->sm_connect_id = gnome_program_install_property ( + klass, + gnome_client_module_get_property, + gnome_client_module_set_property, + g_param_spec_boolean (GNOME_CLIENT_PARAM_SM_CONNECT, NULL, NULL, + sm_connect_default, + (G_PARAM_READABLE | G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT))); +} + +static void +gnome_client_module_instance_init (GnomeProgram *program, GnomeModuleInfo *mod_info) +{ + GnomeProgramPrivate_gnome_client *priv = g_new0 (GnomeProgramPrivate_gnome_client, 1); + + g_object_set_qdata_full (G_OBJECT (program), quark_gnome_program_private_gnome_client, priv, g_free); +} + +static GOptionGroup * +gnome_client_module_get_goption_group (void) +{ + const GOptionEntry session_goptions[] = { + { "sm-client-id", '\0', 0, G_OPTION_ARG_STRING, + &gnome_client_goption_sm_client_id, + N_("Specify session management ID"), N_("ID")}, + { "sm-config-prefix", '\0', 0, G_OPTION_ARG_CALLBACK, + gnome_client_goption_sm_config_prefix, + N_("Specify prefix of saved configuration"), N_("PREFIX")}, + { "sm-disable", '\0', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, + gnome_client_goption_sm_disable, + N_("Disable connection to session manager"), NULL}, + { NULL } + }; + GOptionGroup *option_group; + + //_gnome_ui_gettext_init (TRUE); + + option_group = g_option_group_new ("gnome-session", + N_("Session management:"), + N_("Show session management options"), + NULL, NULL); + //g_option_group_set_translation_domain (option_group, GETTEXT_PACKAGE); + g_option_group_add_entries(option_group, session_goptions); + + return option_group; +} + +const GnomeModuleInfo * +gnome_client_module_info_get (void) +{ + /* Command-line arguments understood by this module. */ + static const struct poptOption options[] = { + //{ NULL, '\0', POPT_ARG_INTL_DOMAIN, GETTEXT_PACKAGE, 0, NULL, NULL }, + + { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_PRE | POPT_CBFLAG_POST, + client_parse_func, 0, NULL, NULL }, + + { "sm-client-id", '\0', POPT_ARG_STRING, NULL, ARG_SM_CLIENT_ID, + N_("Specify session management ID"), N_("ID") }, + + { "sm-config-prefix", '\0', POPT_ARG_STRING, NULL, ARG_SM_CONFIG_PREFIX, + N_("Specify prefix of saved configuration"), N_("PREFIX") }, + + { "sm-disable", '\0', POPT_ARG_NONE, NULL, ARG_SM_DISABLE, + N_("Disable connection to session manager"), NULL }, + + { NULL, '\0', 0, NULL, 0 } + }; + + static GnomeModuleInfo module_info = { + "gnome-client", VERSION, N_("Session management"), + NULL, gnome_client_module_instance_init, + gnome_client_pre_args_parse, gnome_client_post_args_parse, + (struct poptOption *)options, + gnome_client_module_init_pass, gnome_client_module_class_init, + NULL, NULL + }; + + module_info.get_goption_group_func = gnome_client_module_get_goption_group; + + /*if (module_info.requirements == NULL) { + static GnomeModuleRequirement req[3]; + + //_gnome_ui_gettext_init (FALSE); + + req[0].required_version = "1.3.7"; + req[0].module_info = gnome_gtk_module_info_get (); + + req[1].required_version = "1.102.0"; + req[1].module_info = libgnome_module_info_get (); + + req[2].required_version = NULL; + req[2].module_info = NULL; + + module_info.requirements = req; + }*/ + + return &module_info; +} + +/* Parse command-line arguments we recognize. */ +static void +client_parse_func (poptContext ctx, + enum poptCallbackReason reason, + const struct poptOption *opt, + const char *arg, void *data) +{ + switch (reason) { + case POPT_CALLBACK_REASON_OPTION: + switch (opt->val) { + case ARG_SM_CLIENT_ID: + gnome_client_set_id (master_client, arg); + break; + case ARG_SM_DISABLE: + g_object_set (G_OBJECT (gnome_program_get()), + GNOME_CLIENT_PARAM_SM_CONNECT, FALSE, NULL); + break; + case ARG_SM_CONFIG_PREFIX: + g_free(master_client->config_prefix); + master_client->config_prefix= g_strdup (arg); + master_client_restored = TRUE; + break; + } + break; + default: + break; + } +} + +static gboolean +gnome_client_goption_sm_disable (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + g_object_set (G_OBJECT (gnome_program_get()), + GNOME_CLIENT_PARAM_SM_CONNECT, FALSE, NULL); + + return TRUE; +} + +static gboolean +gnome_client_goption_sm_config_prefix (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + g_free (master_client->config_prefix); + master_client->config_prefix = g_strdup (value); + master_client_restored = TRUE; + + return TRUE; +} +#endif + +int session_manager_get_desktop() +{ + return _sm_desktop; +} + +void session_manager_set_desktop(int desktop) +{ +#ifdef DEBUG_ME + fprintf(stderr, "%s: %d\n", __func__, desktop); +#endif + _sm_desktop = desktop; +} + +void session_manager_init(int *argc, char ***argv) +{ + char *cwd; + + gnome_type_init(); + + /* Create the master client. */ + master_client = gnome_client_new_without_connection (); + /* Connect the master client's default signals. */ + g_signal_connect (master_client, "connect", G_CALLBACK (master_client_connect), NULL); + g_signal_connect (master_client, "disconnect", G_CALLBACK (master_client_disconnect), NULL); + + /* Initialise ICE */ + gnome_ice_init (); + + cwd = g_get_current_dir(); + if (cwd != NULL) + { + gnome_client_set_current_directory (master_client, cwd); + g_free (cwd); + } + + /* needed on non-glibc systems: */ + gnome_client_set_program(master_client, (*argv)[0]); + /* Argument parsing is starting. We set the restart and the + clone command to a default value, so other functions can use + the master client while parsing the command line. */ + gnome_client_set_restart_command(master_client, 1, &master_client->program); + + if (*argc > 2) + { + if (strcmp((*argv)[*argc - 2], "-session-desktop") == 0) + { + char *endptr; + int desktop; + + desktop = strtol((*argv)[*argc - 1], &endptr, 10); + if (!*endptr) + session_manager_set_desktop(desktop); + *argc -= 2; + } + } + + if (*argc > 2) + { + if (strcmp((*argv)[*argc - 2], "-session") == 0) + { + gnome_client_set_id(master_client, (*argv)[*argc - 1]); + *argc -= 2; + } + } + + gnome_client_connect(master_client); +} + +void session_manager_exit() +{ + gnome_client_disconnect(master_client); +} + +#if 0 +static void +gnome_client_post_args_parse(GnomeProgram *app, GnomeModuleInfo *mod_info) +{ + gboolean do_connect = TRUE; + + g_object_get (G_OBJECT (app), GNOME_CLIENT_PARAM_SM_CONNECT, &do_connect, NULL); + + if (gnome_client_goption_sm_client_id != NULL) + { + gnome_client_set_id (master_client, gnome_client_goption_sm_client_id); + g_free (gnome_client_goption_sm_client_id); + gnome_client_goption_sm_client_id = NULL; + } + else + { + const gchar *desktop_autostart_id; + + desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID"); + + if (desktop_autostart_id != NULL) + gnome_client_set_id (master_client, desktop_autostart_id); + } + + /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to + * use the same client id. */ + g_unsetenv ("DESKTOP_AUTOSTART_ID"); + + /* We're done, so we can connect to the session manager now. */ + if (do_connect) + gnome_client_connect (master_client); +} +#endif + +#ifndef GNOME_DISABLE_DEPRECATED_SOURCE +/** + * gnome_client_disable_master_connection + * + * Description: Don't connect the master client to the session manager. Usually + * invoked by users when they pass the --sm-disable argument to a Gnome application. + **/ + +void +gnome_client_disable_master_connection (void) +{ +} +#endif /* GNOME_DISABLE_DEPRECATED_SOURCE */ + +#if 0 +static void _gdk_windowing_display_set_sm_client_id (GdkDisplay *display, const gchar *sm_client_id) +{ + if (gdk_display_is_closed(display) + return; + + if (sm_client_id && strcmp (sm_client_id, "")) + { + XChangeProperty (gdk_x11_display_get_xdisplay(display), display_x11->leader_window, + gdk_x11_get_xatom_by_name_for_display (display, "SM_CLIENT_ID"), + XA_STRING, 8, PropModeReplace, (guchar *)sm_client_id, + strlen (sm_client_id)); + } + else + XDeleteProperty (gdk_x11_display_get_xdisplay(display), display_x11->leader_window, + gdk_x11_get_xatom_by_name_for_display (display, "SM_CLIENT_ID")); +} + +static void gdk_set_sm_client_id (const gchar* sm_client_id) +{ + GSList *displays, *tmp_list; + + g_free (gdk_sm_client_id); + gdk_sm_client_id = g_strdup (sm_client_id); + + displays = gdk_display_manager_list_displays (gdk_display_manager_get ()); + for (tmp_list = displays; tmp_list; tmp_list = tmp_list->next) + _gdk_windowing_display_set_sm_client_id (tmp_list->data, sm_client_id); + + g_slist_free (displays); +} +#endif + +static void +master_client_connect (GnomeClient *client, + gboolean restarted, + gpointer client_data) +{ + gdk_x11_set_sm_client_id(gnome_client_get_id (client)); +} + +static void +master_client_disconnect (GnomeClient *client, + gpointer client_data) +{ + if(client_grab_widget && gtk_grab_get_current() == client_grab_widget) + gtk_grab_remove(client_grab_widget); + + gdk_x11_set_sm_client_id(NULL); +} + +/** + * gnome_master_client + * + * Description: + * Get the master session management client. This master client gets a client + * id, that may be specified by the '--sm-client-id' command line option. A + * master client will be generated by gnome_program_init(). If possible the + * master client will contact the session manager after command-line parsing is + * finished (unless gnome_client_disable_master_connection() was called). The + * master client will also set the SM_CLIENT_ID property on the client leader + * window of your application. + * + * Additionally, the master client gets some static arguments set automatically + * (see gnome_client_add_static_arg() for static arguments): + * gnome_program_init() passes all the command line options which are + * recognised by gtk as static arguments to the master client. + * + * Returns: Pointer to the master client + **/ + +GnomeClient* +gnome_master_client (void) +{ + return master_client; +} + + +/*****************************************************************************/ +/* GTK-class managing functions */ + +GNOME_CLASS_BOILERPLATE (GnomeClient, gnome_client, GObject, G_TYPE_OBJECT) + +static void +gnome_client_class_init (GnomeClientClass *klass) +{ + //GObjectClass *object_class = (GObjectClass*) klass; + GObjectClass *gobject_class = (GObjectClass*) klass; + + client_signals[SAVE_YOURSELF] = + g_signal_new ("save_yourself", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GnomeClientClass, save_yourself), + NULL, NULL, + _gnome_marshal_BOOLEAN__INT_ENUM_BOOLEAN_ENUM_BOOLEAN, + G_TYPE_BOOLEAN, 5, + G_TYPE_INT, + GNOME_TYPE_SAVE_STYLE, + G_TYPE_BOOLEAN, + GNOME_TYPE_INTERACT_STYLE, + G_TYPE_BOOLEAN); + client_signals[DIE] = + g_signal_new ("die", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GnomeClientClass, die), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + client_signals[SAVE_COMPLETE] = + g_signal_new ("save_complete", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GnomeClientClass, save_complete), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + client_signals[SHUTDOWN_CANCELLED] = + g_signal_new ("shutdown_cancelled", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GnomeClientClass, shutdown_cancelled), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + client_signals[CONNECT] = + g_signal_new ("connect", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GnomeClientClass, connect), + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, + G_TYPE_BOOLEAN); + client_signals[DISCONNECT] = + g_signal_new ("disconnect", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GnomeClientClass, disconnect), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + //object_class->destroy = gnome_real_client_destroy; + gobject_class->finalize = gnome_real_client_finalize; + + klass->save_yourself = NULL; + klass->die = gnome_client_disconnect; + klass->save_complete = gnome_real_client_save_complete; + klass->shutdown_cancelled = gnome_real_client_shutdown_cancelled; + klass->connect = gnome_real_client_connect; + klass->disconnect = gnome_real_client_disconnect; +} + +static void +gnome_client_instance_init (GnomeClient *client) +{ + client->smc_conn = NULL; + client->client_id = NULL; + client->previous_id = NULL; + client->input_id = 0; + client->config_prefix = NULL; + client->global_config_prefix= NULL; + + client->static_args = NULL; + + /* Preset some default values. */ + client->clone_command = NULL; + client->current_directory = NULL; + client->discard_command = NULL; + client->environment = g_hash_table_new (g_str_hash, g_str_equal); + + client->process_id = getpid (); + + client->program = NULL; + client->resign_command = NULL; + client->restart_command = NULL; + + client->restart_style = -1; + + client->shutdown_command = NULL; + + client->user_id= g_strdup (g_get_user_name ()); + + client->state = GNOME_CLIENT_DISCONNECTED; + client->interaction_keys = NULL; +} + +static gboolean +environment_entry_remove (gchar *key, gchar *value, gpointer data) +{ + g_free (key); + g_free (value); + + return TRUE; +} + +#if 0 +static void +gnome_real_client_destroy (GtkObject *object) +{ + GnomeClient *client; + + /* remember, destroy can be run multiple times! */ + + g_return_if_fail (object != NULL); + g_return_if_fail (GNOME_IS_CLIENT (object)); + + client = GNOME_CLIENT (object); + + if (GNOME_CLIENT_CONNECTED (client)) + gnome_client_disconnect (client); + + GNOME_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object)); +} +#endif + +static void +gnome_real_client_finalize (GObject *object) +{ + GnomeClient *client; + + g_return_if_fail (object != NULL); + g_return_if_fail (GNOME_IS_CLIENT (object)); + + client = GNOME_CLIENT (object); + + g_free (client->client_id); + client->client_id = NULL; + g_free (client->previous_id); + client->previous_id = NULL; + g_free (client->config_prefix); + client->config_prefix = NULL; + g_free (client->global_config_prefix); + client->global_config_prefix = NULL; + + if (client->static_args != NULL) { + g_list_foreach (client->static_args, (GFunc)g_free, NULL); + g_list_free (client->static_args); + client->static_args = NULL; + } + + g_strfreev (client->clone_command); + client->clone_command = NULL; + g_free (client->current_directory); + client->current_directory = NULL; + g_strfreev (client->discard_command); + client->discard_command = NULL; + + if(client->environment != NULL) { + g_hash_table_foreach_remove (client->environment, + (GHRFunc)environment_entry_remove, NULL); + g_hash_table_destroy (client->environment); + client->environment = NULL; + } + + g_free (client->program); + client->program = NULL; + g_strfreev (client->resign_command); + client->resign_command = NULL; + g_strfreev (client->restart_command); + client->restart_command = NULL; + g_strfreev (client->shutdown_command); + client->shutdown_command = NULL; + g_free (client->user_id); + client->user_id = NULL; + + GNOME_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + +/*****************************************************************************/ +/* 'gnome_client' public member functions */ + + +/** + * gnome_client_new + * + * Description: Allocates memory for a new GNOME session management client + * object. After allocating, the client tries to connect to a session manager. + * You probably want to use gnome_master_client() instead. + * + * Returns: Pointer to a newly allocated GNOME session management client object. + **/ + +GnomeClient * +gnome_client_new (void) +{ + GnomeClient *client; + + client= gnome_client_new_without_connection (); + + gnome_client_connect (client); + + return client; +} + + +/** + * gnome_client_new_without_connection + * + * Description: Allocates memory for a new GNOME session management client + * object. You probably want to use gnome_master_client() instead. + * + * Returns: Pointer to a newly allocated GNOME session management client object. + **/ + +GnomeClient * +gnome_client_new_without_connection (void) +{ + GnomeClient *client; + + client = g_object_new (GNOME_TYPE_CLIENT, NULL); + + /* Preset the CloneCommand, RestartCommand and Program properties. + FIXME: having a default would be cool, but it is probably hard to + get this to interact correctly with the distributed command-line + parsing. */ + client->clone_command = NULL; + client->restart_command = NULL; + + /* Non-glibc systems do not get this set on the master_client until + client_parse_func but this is not a problem. + The SM specs require explictly require that this is the value: */ + client->program = g_strdup (g_get_prgname ()); + + return client; +} + + +/** + * gnome_client_flush + * @client: A #GnomeClient instance. + * + * Description: + * This will force the underlying connection to the session manager to be + * flushed. This is useful if you have some pending changes that you want to + * make sure get committed. + **/ + +void +gnome_client_flush (GnomeClient *client) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + +#ifdef HAVE_LIBSM + if (GNOME_CLIENT_CONNECTED (client)) { + IceConn conn = SmcGetIceConnection ((SmcConn) client->smc_conn); + IceFlush (conn); + } +#endif +} + +/*****************************************************************************/ + +#define ERROR_STRING_LENGTH 256 + + +/** + * gnome_client_connect + * @client: A #GnomeClient instance. + * + * Description: Causes the client to connect to the session manager. + * Usually happens automatically; no need to call this function. + **/ + +void +gnome_client_connect (GnomeClient *client) +{ +#ifdef HAVE_LIBSM + SmcCallbacks callbacks; + gchar *client_id; +#endif /* HAVE_LIBSM */ + + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + +#ifdef HAVE_LIBSM + if (GNOME_CLIENT_CONNECTED (client)) + return; + + callbacks.save_yourself.callback = client_save_yourself_callback; + callbacks.die.callback = client_die_callback; + callbacks.save_complete.callback = client_save_complete_callback; + callbacks.shutdown_cancelled.callback = client_shutdown_cancelled_callback; + + callbacks.save_yourself.client_data = + callbacks.die.client_data = + callbacks.save_complete.client_data = + callbacks.shutdown_cancelled.client_data = (SmPointer) client; + + + if (g_getenv ("SESSION_MANAGER")) + { + gchar error_string_ret[ERROR_STRING_LENGTH] = ""; + + client->smc_conn= (gpointer) + SmcOpenConnection (NULL, client, + SmProtoMajor, SmProtoMinor, + SmcSaveYourselfProcMask | SmcDieProcMask | + SmcSaveCompleteProcMask | + SmcShutdownCancelledProcMask, + &callbacks, + client->client_id, &client_id, + ERROR_STRING_LENGTH, error_string_ret); + + if (error_string_ret[0]) + g_warning ("While connecting to session manager:\n%s.", + error_string_ret); + +#ifdef DEBUG_ME + fprintf(stderr, "gnome_client_connect: id = %s\n", client_id); +#endif + } + + if (GNOME_CLIENT_CONNECTED (client)) + { + gint restarted= FALSE; + + g_free (client->previous_id); + client->previous_id= client->client_id; + client->client_id= client_id; + + restarted= (client->previous_id && + !strcmp (client->previous_id, client_id)); + + client_set_state (client, (restarted ? + GNOME_CLIENT_IDLE : + GNOME_CLIENT_REGISTERING)); + + /* Let all the world know, that we have a connection to a + session manager. */ + g_signal_emit (client, client_signals[CONNECT], 0, restarted); + } +#endif /* HAVE_LIBSM */ +} + + + +/** + * gnome_client_disconnect + * @client: A #GnomeClient instance. + * + * Description: Disconnect the client from the session manager. + **/ + +void +gnome_client_disconnect (GnomeClient *client) +{ +#ifdef DEBUG_ME + fprintf(stderr, "gnome_client_disconnect\n"); +#endif + + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + if (GNOME_CLIENT_CONNECTED (client)) + { + gnome_client_flush (client); + g_signal_emit (client, client_signals[DISCONNECT], 0); + } +} + +/*****************************************************************************/ + +/** + * gnome_client_get_flags: + * @client: Pointer to GNOME session client object. + * + * Description: Determine the client's status with the session manager., + * + * Returns: Various #GnomeClientFlag flags which have been or'd together. + **/ + +GnomeClientFlags +gnome_client_get_flags (GnomeClient *client) +{ + GnomeClientFlags flags= 0; + + g_return_val_if_fail (client != NULL, 0); + g_return_val_if_fail (GNOME_IS_CLIENT (client), 0); + + /* To not break binary compability with existing code, the + GnomeClient struct has not been changed. So the flags have to be + calculated every time. */ + + if (GNOME_CLIENT_CONNECTED (client)) + { + flags |= GNOME_CLIENT_IS_CONNECTED; + + if (client->previous_id && + !strcmp (client->previous_id, client->client_id)) + flags |= GNOME_CLIENT_RESTARTED; + + if (master_client_restored && (client == master_client)) + flags |= GNOME_CLIENT_RESTORED; + } + + return flags; +} + + +/*****************************************************************************/ + +/** + * gnome_client_set_clone_command + * @client: Pointer to GNOME session client object. + * @argc: Number of strings in the @argv vector. + * @argv: Argument strings, suitable for use with execv(). + * + * Description: Set a command the session manager can use to create a new + * instance of the application. + **/ + +void +gnome_client_set_clone_command (GnomeClient *client, + gint argc, gchar *argv[]) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + /* Whenever clone_command == NULL then restart_command is used instead */ + g_strfreev (client->clone_command); + client->clone_command = array_init_from_arg (argc, argv); + +#ifdef HAVE_LIBSM + client_set_clone_command (client); +#endif /* HAVE_LIBSM */ +} + +/** + * gnome_client_set_current_directory + * @client: Pointer to GNOME session client object. + * @dir: Directory path. + * + * Description: Set the directory to be in when running shutdown, discard, + * restart, etc. commands. + **/ + +void +gnome_client_set_current_directory (GnomeClient *client, + const gchar *dir) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + g_free (client->current_directory); + + if (dir) + { + client->current_directory= g_strdup (dir); +#ifdef HAVE_LIBSM + client_set_string (client, SmCurrentDirectory, + client->current_directory); +#endif /* HAVE_LIBSM */ + } + else + { + client->current_directory= NULL; +#ifdef HAVE_LIBSM + client_unset (client, SmCurrentDirectory); +#endif /* HAVE_LIBSM */ + } +} + + +/* See doc/reference/tmpl/gnome-client.sgml for documentation. */ + +void +gnome_client_set_discard_command (GnomeClient *client, + gint argc, gchar *argv[]) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + if (argv == NULL) + { + g_return_if_fail (argc == 0); + + g_strfreev (client->discard_command); + client->discard_command= NULL; +#ifdef HAVE_LIBSM + client_unset (client, SmDiscardCommand); +#endif /* HAVE_LIBSM */ + } + else + { + g_strfreev (client->discard_command); + client->discard_command = array_init_from_arg (argc, argv); + +#ifdef HAVE_LIBSM + client_set_array (client, SmDiscardCommand, + client->discard_command); +#endif /* HAVE_LIBSM */ + } +} + + + +/** + * gnome_client_set_environment + * @client: Pointer to GNOME session client object. + * @name: Name of the environment variable + * @value: Value of the environment variable + * + * Description: Set an environment variable to be placed in the + * client's environment prior to running restart, shutdown, discard, etc. commands. + **/ + +void +gnome_client_set_environment (GnomeClient *client, + const gchar *name, + const gchar *value) +{ + gchar *old_name; + gchar *old_value; + + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + g_return_if_fail (name != NULL); + + if (g_hash_table_lookup_extended (client->environment, + name, + (gpointer *) &old_name, + (gpointer *) &old_value)) + { + if (value) + { + g_hash_table_insert (client->environment, old_name, + g_strdup (value)); + g_free (old_value); + } + else + { + g_hash_table_remove (client->environment, name); + g_free (old_name); + g_free (old_value); + } + } + else if (value) + { + g_hash_table_insert (client->environment, + g_strdup (name), + g_strdup (value)); + } + +#ifdef HAVE_LIBSM + client_set_ghash (client, SmEnvironment, client->environment); +#endif /* HAVE_LIBSM */ +} + + + +/** + * gnome_client_set_process_id + * @client: Pointer to GNOME session client object. + * @pid: PID to set as the client's PID. + * + * Description: The client should tell the session manager the result of + * getpid(). However, GNOME does this automatically; so you do not need this + * function. + **/ + +void +gnome_client_set_process_id (GnomeClient *client, pid_t pid) +{ + gchar str_pid[32]; + + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + client->process_id= pid; + + g_snprintf (str_pid, sizeof(str_pid), "%d", client->process_id); + +#ifdef HAVE_LIBSM + client_set_string (client, SmProcessID, str_pid); +#endif /* HAVE_LIBSM */ +} + + + +/** + * gnome_client_set_program + * @client: Pointer to GNOME session client object. + * @program: Name of the program. + * + * Description: Used to tell the session manager the name of your program. Set + * automatically; this function isn't needed. + **/ + +void +gnome_client_set_program (GnomeClient *client, + const gchar *program) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + /* The Program property is required, so you must not unset it. */ + g_return_if_fail (program != NULL); + + g_free (client->program); + + client->program= g_strdup (program); + + client_unset_config_prefix (client); + +#ifdef HAVE_LIBSM + client_set_string (client, SmProgram, client->program); +#endif /* HAVE_LIBSM */ +} + + + +/** + * gnome_client_set_resign_command + * @client: Pointer to GNOME session client object. + * @argc: Number of strings in @argv. + * @argv: execv()-style command to undo the effects of the client. + * + * Description: Some clients can be "undone," removing their effects and + * deleting any saved state. For example, xmodmap could register a resign + * command to undo the keymap changes it saved. + * + * Used by clients that use the #GNOME_RESTART_ANYWAY restart style to to undo + * their effects (these clients usually perform initialisation functions and + * leave effects behind after they die). The resign command combines the + * effects of a shutdown command and a discard command. It is executed when the + * user decides that the client should cease to be restarted. + **/ + +void +gnome_client_set_resign_command (GnomeClient *client, + gint argc, gchar *argv[]) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + if (argv == NULL) + { + g_return_if_fail (argc == 0); + + g_strfreev (client->resign_command); + client->resign_command= NULL; + +#ifdef HAVE_LIBSM + client_unset (client, SmResignCommand); +#endif /* HAVE_LIBSM */ + } + else + { + g_strfreev (client->resign_command); + client->resign_command = array_init_from_arg (argc, argv); + +#ifdef HAVE_LIBSM + client_set_array (client, SmResignCommand, client->resign_command); +#endif /* HAVE_LIBSM */ + } +} + + + +/** + * gnome_client_set_restart_command + * @client: Pointer to GNOME session client object. + * @argc: Number of strings in argv. + * @argv: Argument vector to an execv() to restart the client. + * + * Description: When clients crash or the user logs out and back in, they are + * restarted. This command should perform the restart. Executing the restart + * command on the local host should reproduce the state of the client at the + * time of the session save as closely as possible. Saving config info under + * the gnome_client_get_config_prefix() is generally useful. + **/ + +void +gnome_client_set_restart_command (GnomeClient *client, + gint argc, gchar *argv[]) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + /* The RestartCommand property is required, so you must not unset + it. */ + g_return_if_fail (argc != 0); + g_return_if_fail (argv != NULL); + + g_strfreev (client->restart_command); + client->restart_command = array_init_from_arg (argc, argv); + +#ifdef HAVE_LIBSM + client_set_restart_command (client); +#endif /* HAVE_LIBSM */ +} + +/** + * gnome_client_set_priority + * @client: Pointer to GNOME session client object. + * @priority: Position of client in session start up ordering. + * + * Description: + * + * The gnome-session manager restarts clients in order of their + * priorities in a similar way to the start up ordering in SysV. + * This function allows the app to suggest a position in this + * ordering. The value should be between 0 and 99. A default + * value of 50 is assigned to apps that do not provide a value. + * The user may assign a different priority. + **/ + +void +gnome_client_set_priority (GnomeClient *client, guint priority) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + +#ifdef HAVE_LIBSM + if (priority > 99) + priority = 99; + + client_set_gchar (client, "_GSM_Priority", (gchar) priority); +#endif +} + +/** + * gnome_client_set_restart_style + * @client: Pointer to GNOME session client object. + * @style: When to restart the client. + * + * Tells the session manager how the client should be restarted in future + * session. The options are given by the #GnomeRestartStyle enum. + **/ + +void +gnome_client_set_restart_style (GnomeClient *client, + GnomeRestartStyle style) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + switch (style) + { + case GNOME_RESTART_IF_RUNNING: +#ifdef HAVE_LIBSM + client_set_gchar (client, SmRestartStyleHint, SmRestartIfRunning); + break; +#endif /* HAVE_LIBSM */ + + case GNOME_RESTART_ANYWAY: +#ifdef HAVE_LIBSM + client_set_gchar (client, SmRestartStyleHint, SmRestartAnyway); + break; +#endif /* HAVE_LIBSM */ + + case GNOME_RESTART_IMMEDIATELY: +#ifdef HAVE_LIBSM + client_set_gchar (client, SmRestartStyleHint, SmRestartImmediately); + break; +#endif /* HAVE_LIBSM */ + + case GNOME_RESTART_NEVER: +#ifdef HAVE_LIBSM + client_set_gchar (client, SmRestartStyleHint, SmRestartNever); +#endif /* HAVE_LIBSM */ + break; + + default: + g_assert_not_reached (); + return; + } + + client->restart_style= style; +} + + + +/** + * gnome_client_set_shutdown_command + * @client: Pointer to GNOME session client object. + * @argc: Number of strings in argv. + * @argv: Command to shutdown the client if the client isn't running. + * + * Description: GNOME_RESTART_ANYWAY clients can set this command to run + * when the user logs out but the client is no longer running. + * + * Used by clients that use the GNOME_RESTART_ANYWAY restart style to + * to undo their effects (these clients usually perform initialisation + * functions and leave effects behind after they die). The shutdown + * command simply undoes the effects of the client. It is executed + * during a normal logout. + **/ + +void +gnome_client_set_shutdown_command (GnomeClient *client, + gint argc, gchar *argv[]) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + if (argv == NULL) + { + g_return_if_fail (argc == 0); + + g_strfreev (client->shutdown_command); + client->shutdown_command = NULL; +#ifdef HAVE_LIBSM + client_unset (client, SmShutdownCommand); +#endif /* HAVE_LIBSM */ + } + else + { + g_strfreev (client->shutdown_command); + client->shutdown_command = array_init_from_arg (argc, argv); + +#ifdef HAVE_LIBSM + client_set_array (client, SmShutdownCommand, + client->shutdown_command); +#endif /* HAVE_LIBSM */ + } +} + + + +/** + * gnome_client_set_user_id + * @client: Pointer to GNOME session client object. + * @id: Username. + * + * Description: Tell the session manager the user's login name. GNOME + * does this automatically; no need to call the function. + **/ + +void +gnome_client_set_user_id (GnomeClient *client, + const gchar *id) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + /* The UserID property is required, so you must not unset it. */ + g_return_if_fail (id != NULL); + + g_free (client->user_id); + + client->user_id= g_strdup (id); +#ifdef HAVE_LIBSM + client_set_string (client, SmUserID, client->user_id); +#endif /* HAVE_LIBSM */ +} + + + +/** + * gnome_client_add_static_arg + * @client: Pointer to GNOME session client object. + * @...: %NULL-terminated list of arguments to add to the restart command. + * + * Description: You can add arguments to your restart command's argv with this + * function. This function provides an alternative way of adding new arguments + * to the restart command. The arguments are placed before the arguments + * specified by gnome_client_set_restart_command() and after the arguments + * recognised by GTK+ that are specified by the user on the original command + * line. + **/ + +void +gnome_client_add_static_arg (GnomeClient *client, ...) +{ + va_list args; + gchar *str; + + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + va_start (args, client); + str= va_arg (args, gchar*); + while (str) + { + client->static_args= g_list_append (client->static_args, g_strdup(str)); + str= va_arg (args, gchar*); + } + va_end (args); +} + +/*****************************************************************************/ + + +/** + * gnome_client_set_id + * @client: A #GnomeClient instance. + * @id: Session management ID. + * + * Description: Set the client's session management ID; must be done + * before connecting to the session manager. There is usually no reason to call + * this function. + **/ + +void +gnome_client_set_id (GnomeClient *client, const gchar *id) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + g_return_if_fail (!GNOME_CLIENT_CONNECTED (client)); + + g_return_if_fail (id != NULL); + + g_free (client->client_id); + client->client_id = g_strdup (id); + +#ifdef DEBUG_ME + fprintf(stderr, "gnome_client_set_id: %s\n", id); +#endif +} + + + +/** + * gnome_client_get_id + * @client: A #GnomeClient instance. + * + * Description: Returns session management ID + * + * Returns: Session management ID for this client; %NULL if not connected to a + * session manager. + **/ + +const gchar * +gnome_client_get_id (GnomeClient *client) +{ + g_return_val_if_fail (client != NULL, NULL); + g_return_val_if_fail (GNOME_IS_CLIENT (client), NULL); + + return client->client_id; +} + +/** + * gnome_client_get_previous_id + * @client: A #GnomeClient instance. + * + * Description: Get the session management ID from the previous session. + * + * Returns: Pointer to the session management ID the client had in the last + * session, or %NULL if it was not in a previous session. + **/ + +const gchar * +gnome_client_get_previous_id (GnomeClient *client) +{ + g_return_val_if_fail (client != NULL, NULL); + g_return_val_if_fail (GNOME_IS_CLIENT (client), NULL); + + return client->previous_id; +} + +/** + * gnome_client_get_desktop_id: + * @client: A #GnomeClient instance. + * + * Get the client ID of the desktop's current instance, i.e. if + * you consider the desktop as a whole as a session managed app, this + * returns its session ID using a GNOME extension to session + * management. May return %NULL for apps not running under a recent + * version of gnome-session; apps should handle that case. + * + * Returns: Session ID of GNOME desktop instance, or %NULL if none. + **/ +const gchar* +gnome_client_get_desktop_id (GnomeClient *client) +{ + g_return_val_if_fail (GNOME_IS_CLIENT (client), NULL); + + return g_getenv ("GNOME_DESKTOP_SESSION_ID"); +} + +/** + * gnome_client_get_config_prefix + * @client: Pointer to GNOME session client object. + * + * Description: Get the config prefix for a client. This config prefix + * provides a suitable place to store any details about the state of + * the client which can not be described using the app's command line + * arguments (as set in the restart command). You may push the + * returned value using gnome_config_push_prefix() and read or write + * any values you require. + * + * Returns: Config prefix. The returned string belongs to libgnomeui library + * and should NOT be freed by the caller. + **/ + +const gchar * +gnome_client_get_config_prefix (GnomeClient *client) +{ + g_return_val_if_fail (client == NULL || GNOME_IS_CLIENT (client), NULL); + + if (!client) + client = master_client; + + if (!client) + return gnome_client_get_global_config_prefix (client); + + if (!client->config_prefix) + client->config_prefix = (char *)gnome_client_get_global_config_prefix (client); + + return client->config_prefix; +} + +static gchar* config_prefix = NULL; + +/** + * gnome_client_set_global_config_prefix + * @client: Pointer to GNOME session client object. + * @prefix: Prefix for saving the global configuration. + * + * Description: Set the value used for the global config prefix. The config + * prefixes returned by gnome_client_get_config_prefix() are formed by + * extending this prefix with an unique identifier. + * + * The global config prefix defaults to a name based on the name of + * the executable. This function allows you to set it to a different + * value. It should be called BEFORE retrieving the config prefix for + * the first time. Later calls will be ignored. + * + * For example, setting a global config prefix of "/app.d/session/" + * would ensure that all your session save files or directories would + * be gathered together into the app.d directory. + **/ + +void +gnome_client_set_global_config_prefix (GnomeClient *client, const gchar* prefix) +{ + if (client == NULL) + { + config_prefix= g_strdup (prefix); + return; + } + + g_return_if_fail (GNOME_IS_CLIENT (client)); + + client->global_config_prefix = g_strdup (prefix); +} + +/** + * gnome_client_get_global_config_prefix + * @client: Pointer to GNOME session client object. + * + * Description: Get the config prefix that will be returned by + * gnome_client_get_config_prefix() for clients which have NOT been restarted + * or cloned (i.e. for clients started by the user without `--sm-' options). + * This config prefix may be used to write the user's preferred config for + * these "new" clients. + * + * You could also use this prefix as a place to store and retrieve config + * details that you wish to apply to ALL instances of the app. However, this + * practice limits the users freedom to configure each instance in a different + * way so it should be used with caution. + * + * Returns: The config prefix as a newly allocated string. + **/ + +const gchar * +gnome_client_get_global_config_prefix (GnomeClient *client) +{ + if (client == NULL) + { + if (!config_prefix) { + const char *prgname; + const char *name; + + prgname = g_get_prgname (); + + g_assert (prgname != NULL); + + name= strrchr (prgname, '/'); + name= name ? (name+1) : prgname; + + config_prefix= g_strconcat ("/", name, "/", NULL); + } + + return config_prefix; + } + + g_return_val_if_fail (GNOME_IS_CLIENT (client), NULL); + + if (!client->global_config_prefix) + { + char *name; + name= strrchr (client->program, '/'); + + name= name ? (name+1) : client->program; + + client->global_config_prefix= g_strconcat ("/", name, "/", NULL); + } + + return client->global_config_prefix; +} + +static void +client_unset_config_prefix (GnomeClient *client) +{ + g_free (client->config_prefix); + client->config_prefix= NULL; + g_free (client->global_config_prefix); + client->global_config_prefix= NULL; +} + + +/*****************************************************************************/ + +static void +gnome_real_client_save_complete (GnomeClient *client) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + client_set_state (client, GNOME_CLIENT_IDLE); +} + +/* The following function is executed, after receiving a SHUTDOWN + * CANCELLED message from the session manager. It is executed before + * the function connected to the 'shutdown_cancelled' signal are + * called */ + +static void +gnome_real_client_shutdown_cancelled (GnomeClient *client) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + +#ifdef HAVE_LIBSM + if ((client->state == GNOME_CLIENT_SAVING_PHASE_1) || + (client->state == GNOME_CLIENT_WAITING_FOR_PHASE_2) || + (client->state == GNOME_CLIENT_SAVING_PHASE_2)) + SmcSaveYourselfDone ((SmcConn) client->smc_conn, False); + + client_set_state (client, GNOME_CLIENT_IDLE); + + /* Free all interation keys but the one in use. */ + while (client->interaction_keys) + { + GSList *tmp= client->interaction_keys; + + interaction_key_destroy_if_possible ((InteractionKey *) tmp->data); + + client->interaction_keys= g_slist_remove (tmp, tmp->data); + } +#endif /* HAVE_LIBSM */ +} + +static void +gnome_real_client_connect (GnomeClient *client, + gboolean restarted) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + +#ifdef HAVE_LIBSM + /* We now set all non empty properties. */ + client_set_string (client, SmCurrentDirectory, client->current_directory); + client_set_array (client, SmDiscardCommand, client->discard_command); + client_set_ghash (client, SmEnvironment, client->environment); + { + gchar str_pid[32]; + + g_snprintf (str_pid, sizeof(str_pid), "%d", client->process_id); + client_set_string (client, SmProcessID, str_pid); + } + client_set_string (client, SmProgram, client->program); + client_set_array (client, SmResignCommand, client->resign_command); + + client_set_clone_command (client); + client_set_restart_command (client); + + switch (client->restart_style) + { + case GNOME_RESTART_IF_RUNNING: + client_set_gchar (client, SmRestartStyleHint, SmRestartIfRunning); + break; + + case GNOME_RESTART_ANYWAY: + client_set_gchar (client, SmRestartStyleHint, SmRestartAnyway); + break; + + case GNOME_RESTART_IMMEDIATELY: + client_set_gchar (client, SmRestartStyleHint, SmRestartImmediately); + break; + + case GNOME_RESTART_NEVER: + client_set_gchar (client, SmRestartStyleHint, SmRestartNever); + break; + + default: + break; + } + + client_set_array (client, SmShutdownCommand, client->shutdown_command); + client_set_string (client, SmUserID, client->user_id); +#endif /* HAVE_LIBSM */ +} + + +static void +gnome_real_client_disconnect (GnomeClient *client) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + +#ifdef HAVE_LIBSM + if (GNOME_CLIENT_CONNECTED (client)) + { + SmcCloseConnection ((SmcConn) client->smc_conn, 0, NULL); + client->smc_conn = NULL; + } + + client_set_state (client, GNOME_CLIENT_DISCONNECTED); + + /* Free all interation keys but the one in use. */ + while (client->interaction_keys) + { + GSList *tmp= client->interaction_keys; + + interaction_key_destroy_if_possible ((InteractionKey *) tmp->data); + + client->interaction_keys= g_slist_remove (tmp, tmp->data); + } + +#endif /* HAVE_LIBSM */ +} + +/*****************************************************************************/ + +static void +gnome_client_request_interaction_internal (GnomeClient *client, + GnomeDialogType dialog_type, + gboolean interp, + GnomeInteractFunction function, + gpointer data, + GDestroyNotify destroy) +{ +#ifdef HAVE_LIBSM + Status status; + InteractionKey *key; + int _dialog_type; +#endif + + /* Convert GnomeDialogType into XSM library values */ + switch (dialog_type) + { + case GNOME_DIALOG_ERROR: +#ifdef HAVE_LIBSM + _dialog_type= SmDialogError; +#endif + break; + + case GNOME_DIALOG_NORMAL: +#ifdef HAVE_LIBSM + _dialog_type= SmDialogError; +#endif + break; + + default: + g_assert_not_reached (); + return; + } + +#ifdef HAVE_LIBSM + + key= interaction_key_new (client, dialog_type, interp, + function, data, destroy); + + g_return_if_fail (key); + + status= SmcInteractRequest ((SmcConn) client->smc_conn, _dialog_type, + client_interact_callback, (SmPointer) client); + + if (status) + { + client->interaction_keys = g_slist_append (client->interaction_keys, + key); + } + else + { + interaction_key_destroy (key); + } +#endif /* HAVE_LIBSM */ +} + +static void +gnome_client_save_dialog_show (GnomeClient *client, gint key, + GnomeDialogType type, gpointer data) +{ + GtkDialog* dialog = GTK_DIALOG (data); + gboolean shutdown_cancelled; + + if (client->shutdown) + gtk_dialog_add_button (dialog, _("Cancel Logout"), GTK_RESPONSE_CANCEL); + gtk_widget_show_all (GTK_WIDGET (dialog)); + /* These are SYSTEM modal dialogs so map them above everything else */ +#if 0 + /* FIXME: hmmm, _NET doesn't include a way to do this, I'm not sure + * if window type == DOCK is a correct way and the old win_hints + * layer stuff is no more */ + gnome_win_hints_set_layer (GTK_WIDGET (dialog), WIN_LAYER_ABOVE_DOCK); +#endif + shutdown_cancelled = (gtk_dialog_run (dialog) == GTK_RESPONSE_CANCEL); + gnome_interaction_key_return (key, shutdown_cancelled); +} + +/** + * gnome_client_save_any_dialog + * @client: Pointer to #GnomeClient object. + * @dialog: Pointer to GNOME dialog widget (a #GtkDialog widget). + * + * Description: + * May be called during a "save_youself" handler to request that a + * (modal) dialog is presented to the user. The session manager decides + * when the dialog is shown, but it will not be shown it unless the + * session manager is sending an interaction style of #GNOME_INTERACT_ANY. + * A "Cancel Logout" button will be added during a shutdown. + **/ +void +gnome_client_save_any_dialog (GnomeClient *client, GtkDialog *dialog) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (dialog != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + g_return_if_fail (GTK_IS_DIALOG (dialog)); + + if (client->interact_style == GNOME_INTERACT_ANY) + gnome_client_request_interaction (client, + GNOME_DIALOG_NORMAL, + gnome_client_save_dialog_show, + (gpointer) dialog); +} + +/** + * gnome_client_save_error_dialog + * @client: Pointer to #GnomeClient object. + * @dialog: Pointer to GNOME dialog widget (a #GtkDialog widget). + * + * Description: + * May be called during a "save_youself" handler when an error has occurred + * during the save. The session manager decides when the dialog is shown, but + * it will not be shown it unless the session manager is sending an interaction + * style of #GNOME_INTERACT_ANY. A "Cancel Logout" button will be added + * during a shutdown. + **/ + +void +gnome_client_save_error_dialog (GnomeClient *client, GtkDialog *dialog) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (dialog != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + g_return_if_fail (GTK_IS_DIALOG (dialog)); + + if (client->interact_style != GNOME_INTERACT_NONE) + gnome_client_request_interaction (client, + GNOME_DIALOG_ERROR, + gnome_client_save_dialog_show, + (gpointer) dialog); +} + +/** + * gnome_client_request_interaction + * @client: A #GnomeClient object. + * @dialog_type: The type of dialog to create. + * @function: Callback to invoke to perform the interaction. + * @data: Callback data. + * + * Description: Use the following functions, if you want to interact with the + * user during a "save_yourself" handler without being restricted to using the + * dialog based commands gnome_client_save_any_dialog() or + * gnome_client_save_error_dialog(). Note, however, that overriding the session + * manager specified preference in this way (by using arbitrary dialog boxes) + * is not very nice. + * + * If and when the session manager decides that it's the app's turn to interact + * then 'func' will be called with the specified arguments and a unique + * 'GnomeInteractionKey'. The session manager will block other + * clients from interacting until this key is returned with + * gnome_interaction_key_return(). + **/ + +void +gnome_client_request_interaction (GnomeClient *client, + GnomeDialogType dialog_type, + GnomeInteractFunction function, + gpointer data) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + g_return_if_fail ((client->state == GNOME_CLIENT_SAVING_PHASE_1) || + (client->state == GNOME_CLIENT_SAVING_PHASE_2)); + + g_return_if_fail ((client->interact_style != GNOME_INTERACT_NONE) && + ((client->interact_style == GNOME_INTERACT_ANY) || + (dialog_type == GNOME_DIALOG_ERROR))); + + gnome_client_request_interaction_internal (client, dialog_type, + FALSE, function, data, NULL); +} + + +/** + * gnome_client_request_interaction_interp + * @client: Pointer to GNOME session client object. + * @dialog_type: Type of dialog to show. + * @function: Callback to perform the interaction (a #GnomeInteractFunction). + * @data: Callback data. + * @destroy: Function to destroy callback data. + * + * Description: Similar to gnome_client_request_interaction(), but used when + * you need to destroy the callback data after the interaction. + **/ + +#if 0 +void +gnome_client_request_interaction_interp (GnomeClient *client, + GnomeDialogType dialog_type, + GtkCallbackMarshal function, + gpointer data, + GDestroyNotify destroy) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + g_return_if_fail ((client->state == GNOME_CLIENT_SAVING_PHASE_1) || + (client->state == GNOME_CLIENT_SAVING_PHASE_2)); + + g_return_if_fail ((client->interact_style != GNOME_INTERACT_NONE) && + ((client->interact_style == GNOME_INTERACT_ANY) || + (dialog_type == GNOME_DIALOG_ERROR))); + + gnome_client_request_interaction_internal (client, dialog_type, + TRUE, + (GnomeInteractFunction)function, + data, destroy); +} +#endif + +/*****************************************************************************/ + + +/** + * gnome_client_request_phase_2 + * @client: A #GnomeClient object. + * + * Description: Request the session managaer to emit the "save_yourself" + * signal for a second time after all the clients in the session have ceased + * interacting with the user and entered an idle state. This might be useful if + * your app manages other apps and requires that they are in an idle state + * before saving its final data. + **/ + +void +gnome_client_request_phase_2 (GnomeClient *client) +{ + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + /* Check if we are in save phase one */ + g_return_if_fail (client->state == GNOME_CLIENT_SAVING_PHASE_1); + + client->save_phase_2_requested= TRUE; +} + + +/** + * gnome_client_request_save + * @client: Pointer to GNOME session client object. + * @save_style: Save style to request. + * @shutdown: Whether to log out of the session. + * @interact_style: Whether to allow user interaction. + * @fast: Minimize activity to save as soon as possible. + * @global: Request that all other apps in the session also save their state. + * + * Description: Request the session manager to save the session in + * some way. The arguments correspond with the arguments passed to the + * "save_yourself" signal handler. + * + * The save_style indicates whether the save should affect data + * accessible to other users (#GNOME_SAVE_GLOBAL) or only the state + * visible to the current user (#GNOME_SAVE_LOCAL) or both. Setting + * shutdown to %TRUE will initiate a logout. The interact_style + * specifies which kinds of interaction will be available. Setting + * fast to %TRUE will limit the save to setting the session manager + * properties plus any essential data. Setting the value of global to + * %TRUE will request that all the other apps in the session do a save + * as well. A global save is mandatory when doing a shutdown. + * + **/ + +void +gnome_client_request_save (GnomeClient *client, + GnomeSaveStyle save_style, + gboolean shutdown, + GnomeInteractStyle interact_style, + gboolean fast, + gboolean global) +{ +#ifdef HAVE_LIBSM + int _save_style; + int _interact_style; +#endif + + g_return_if_fail (client != NULL); + g_return_if_fail (GNOME_IS_CLIENT (client)); + + switch (save_style) + { + case GNOME_SAVE_GLOBAL: +#ifdef HAVE_LIBSM + _save_style= SmSaveGlobal; + break; +#endif + + case GNOME_SAVE_LOCAL: +#ifdef HAVE_LIBSM + _save_style= SmSaveLocal; + break; +#endif + + case GNOME_SAVE_BOTH: +#ifdef HAVE_LIBSM + _save_style= SmSaveBoth; +#endif + break; + + default: + g_assert_not_reached (); + return; + } + + switch (interact_style) + { + case GNOME_INTERACT_NONE: +#ifdef HAVE_LIBSM + _interact_style= SmInteractStyleNone; + break; +#endif + + case GNOME_INTERACT_ERRORS: +#ifdef HAVE_LIBSM + _interact_style= SmInteractStyleErrors; + break; +#endif + + case GNOME_INTERACT_ANY: +#ifdef HAVE_LIBSM + _interact_style= SmInteractStyleAny; +#endif + break; + + default: + g_assert_not_reached (); + return; + } + + if (GNOME_CLIENT_CONNECTED (client)) + { +#ifdef HAVE_LIBSM +#if 0 + if (shutdown) + { + gnome_triggers_do("Session shutdown", NULL, "gnome", "logout", NULL); + } +#endif + + SmcRequestSaveYourself ((SmcConn) client->smc_conn, _save_style, + shutdown, _interact_style, + fast, global); +#endif + } + else + { + gboolean ret; + g_signal_emit (client, client_signals[SAVE_YOURSELF], 0, + 1, save_style, shutdown, interact_style, fast, &ret); + if (shutdown) + g_signal_emit (client, client_signals[DIE], 0); + } +} + +/*****************************************************************************/ +/* 'InteractionKey' stuff */ + + +/** + * gnome_interaction_key_return + * @key: Key passed to interaction callback + * @cancel_shutdown: If %TRUE, cancel the shutdown + * + * Description: Used in interaction callback to tell the session manager + * you are done interacting. + **/ + +void +gnome_interaction_key_return (gint tag, + gboolean cancel_shutdown) +{ +#ifdef HAVE_LIBSM + InteractionKey *key; + GnomeClient *client; + + key= interaction_key_find_by_tag (tag); + g_return_if_fail (key); + + client= key->client; + + interaction_key_destroy (key); + + /* The case that 'client != NULL' should only occur, if the + connection to the session manager was closed, while we where + interacting or we received a SHUTDOWN_CANCELLED while + interacting. */ + if (client == NULL) + return; + + client->interaction_keys= g_slist_remove (client->interaction_keys, key); + + if (cancel_shutdown && !client->shutdown) + cancel_shutdown= FALSE; + + SmcInteractDone ((SmcConn) client->smc_conn, cancel_shutdown); + + client_save_yourself_possibly_done (client); +#endif /* HAVE_LIBSM */ +} + +/*****************************************************************************/ +/* array helping functions - these function should be replaced by g_lists */ + +static gchar ** +array_init_from_arg (gint argc, gchar *argv[]) +{ + gchar **array; + int i; + + if (argv == NULL) + { + g_return_val_if_fail (argc == 0, NULL); + + return NULL; + } + else + { + /* Now initialize the array. */ + array = g_new (gchar *, argc + 1); + + for(i = 0; i < argc; i++) + array[i] = g_strdup(argv[i]); + + array[i] = NULL; + } + + return array; +} diff --git a/gb.gtk/src/sm/gnome-client.h b/gb.gtk/src/sm/gnome-client.h new file mode 100644 index 00000000..48f00003 --- /dev/null +++ b/gb.gtk/src/sm/gnome-client.h @@ -0,0 +1,487 @@ +/* gnome-client.h - GNOME session management client support + * + * Copyright (C) 1998 Carsten Schaar + * All rights reserved + * + * Author: Carsten Schaar + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Cambridge, MA 02139, USA. + */ +/* + @NOTATION@ +*/ + +#ifndef GNOME_CLIENT_H +#define GNOME_CLIENT_H + +#include +#include + +#define HAVE_LIBSM +#define N_(x) x +#define _(x) x +//#define GNOME_DISABLE_DEPRECATED +//#define GTK_DISABLE_DEPRECATED + +#include +#include +#include +#include + +#ifdef GTK3 +#undef GDK_THREADS_ENTER +#define GDK_THREADS_ENTER() +#undef GDK_THREADS_LEAVE +#define GDK_THREADS_LEAVE() +#endif + +//#include "gnome-program.h" + +G_BEGIN_DECLS + +#define GNOME_TYPE_CLIENT (gnome_client_get_type ()) +#define GNOME_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CLIENT, GnomeClient)) +#define GNOME_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CLIENT, GnomeClientClass)) +#define GNOME_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CLIENT)) +#define GNOME_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (((klass), GNOME_TYPE_CLIENT))) +#define GNOME_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CLIENT, GnomeClientClass)) + +#define GNOME_CLIENT_CONNECTED(obj) (GNOME_CLIENT (obj)->smc_conn) + +typedef struct _GnomeClient GnomeClient; +typedef struct _GnomeClientClass GnomeClientClass; + + +typedef enum +{ + GNOME_INTERACT_NONE, + GNOME_INTERACT_ERRORS, + GNOME_INTERACT_ANY +} GnomeInteractStyle; + +typedef enum +{ + GNOME_DIALOG_ERROR, + GNOME_DIALOG_NORMAL +} GnomeDialogType; + +typedef enum +{ + /* update structure when adding an enum */ + GNOME_SAVE_GLOBAL, + GNOME_SAVE_LOCAL, + GNOME_SAVE_BOTH +} GnomeSaveStyle; + +typedef enum +{ + /* update structure when adding an enum */ + GNOME_RESTART_IF_RUNNING, + GNOME_RESTART_ANYWAY, + GNOME_RESTART_IMMEDIATELY, + GNOME_RESTART_NEVER +} GnomeRestartStyle; + +typedef enum +{ + /* update structure when adding an enum */ + GNOME_CLIENT_IDLE, + GNOME_CLIENT_SAVING_PHASE_1, + GNOME_CLIENT_WAITING_FOR_PHASE_2, + GNOME_CLIENT_SAVING_PHASE_2, + GNOME_CLIENT_FROZEN, + GNOME_CLIENT_DISCONNECTED, + GNOME_CLIENT_REGISTERING +} GnomeClientState; + +typedef enum +{ + GNOME_CLIENT_IS_CONNECTED= 1 << 0, + GNOME_CLIENT_RESTARTED = 1 << 1, + GNOME_CLIENT_RESTORED = 1 << 2 +} GnomeClientFlags; + + +typedef void (*GnomeInteractFunction) (GnomeClient *client, + gint key, + GnomeDialogType dialog_type, + gpointer data); + +struct _GnomeClient +{ + GObject object; + + /* general information about the connection to the session manager */ + gpointer smc_conn; + + /* client id of this client */ + gchar *client_id; + + /* Previous client id of this client. */ + gchar *previous_id; + + /* Prefix for per save configuration files. */ + gchar *config_prefix; + + /* Prefix for app configuration files. */ + gchar *global_config_prefix; + + /* Static command line options. */ + GList *static_args; + + /* The following properties are predefined in the X session + management protocol. The entries marked with a 'x' are required + by the session management protocol. The entries marked with a + 's' are set automatically when creating a new gnome client. */ + gchar **clone_command; /*[xs]*/ + gchar *current_directory; /*[ ]*/ + gchar **discard_command; /*[ ]*/ + GHashTable *environment; /*[ ]*/ + pid_t process_id; /*[ s]*/ + gchar *program; /*[xs]*/ + gchar **resign_command; /*[ ]*/ + gchar **restart_command; /*[xs]*/ + GnomeRestartStyle restart_style; /*[ ]*/ + gchar **shutdown_command; /*[ ]*/ + gchar *user_id; /*[xs]*/ + + GSList *interaction_keys; + + + gint input_id; + + /* values sent with the last SaveYourself message */ + guint save_style : 2; /* GnomeRestartStyle */ + guint interact_style : 2; /* GnomeInteractStyle */ + + /* other internal state information */ + guint state : 3; /* GnomeClientState */ + + guint shutdown : 1; + guint fast : 1; + guint save_phase_2_requested : 1; + guint save_successfull : 1; + guint save_yourself_emitted : 1; + + gpointer reserved; /* Reserved for private struct */ +}; + + +struct _GnomeClientClass +{ + GObjectClass parent_class; + + gboolean (* save_yourself) (GnomeClient *client, + gint phase, + GnomeSaveStyle save_style, + gboolean shutdown, + GnomeInteractStyle interact_style, + gboolean fast); + void (* die) (GnomeClient *client); + void (* save_complete) (GnomeClient *client); + void (* shutdown_cancelled) (GnomeClient *client); + + void (* connect) (GnomeClient *client, + gboolean restarted); + void (* disconnect) (GnomeClient *client); + + /* Padding for possible expansion */ + gpointer padding1; + gpointer padding2; +}; + +//#define GNOME_CLIENT_MODULE gnome_client_module_info_get() +//const GnomeModuleInfo *gnome_client_module_info_get (void) G_GNUC_CONST; + +#define GNOME_CLIENT_PARAM_SM_CONNECT "sm-connect" + +GType gnome_client_get_type (void) G_GNUC_CONST; + +/* Get the master session management client. This master client gets + a client id, that may be specified by the '--sm-client-id' command + line option. A master client will be generated by 'gnome-init'. + If possible the master client will contact the session manager + after command-line parsing is finished (unless + 'gnome_client_disable_master_connection' was called). The master + client will also set the SM_CLIENT_ID property on the client leader + window of your application. + + Additionally, the master client gets some static arguments set + automatically (see 'gnome_client_add_static_arg' for static + arguments): 'gnome_init' passes all the command line options which + are recognised by gtk as static arguments to the master client. */ +GnomeClient *gnome_master_client (void); + +/* Get the config prefix for a client. This config prefix provides a + suitable place to store any details about the state of the client + which can not be described using the app's command line arguments (as + set in the restart command). You may push the returned value using + 'gnome_config_push_prefix' and read or write any values you require. */ +const gchar* gnome_client_get_config_prefix (GnomeClient *client); + +/* Get the config prefix that will be returned by the previous function + for clients which have NOT been restarted or cloned (i.e. for clients + started by the user without `--sm-' options). This config prefix may be + used to write the user's preferred config for these "new" clients. + + You could also use this prefix as a place to store and retrieve config + details that you wish to apply to ALL instances of the app. However, + this practice limits the users freedom to configure each instance in + a different way so it should be used with caution. */ +const gchar* gnome_client_get_global_config_prefix (GnomeClient *client); + +/* Set the value used for the global config prefix. The config prefixes + returned by gnome_client_get_config_prefix are formed by extending + this prefix with an unique identifier. + + The global config prefix defaults to a name based on the name of + the executable. This function allows you to set it to a different + value. It should be called BEFORE retrieving the config prefix for + the first time. Later calls will be ignored. + + For example, setting a global config prefix of "/app.d/session/" + would ensure that all your session save files or directories would + be gathered together into the app.d directory. */ +void gnome_client_set_global_config_prefix (GnomeClient *client, + const gchar* prefix); + +/* Returns some flags, that give additional information about this + client. Right now, the following flags are supported: + + - GNOME_CLIENT_IS_CONNECTED: The client is connected to a session + manager (It's the same information like using + GNOME_CLIENT_CONNECTED). + + - GNOME_CLIENT_RESTARTED: The client has been restarted, i. e. it + has been running with the same client id before. + + - GNOME_CLIENT_RESTORED: This flag is only used for the master + client. It indicates, that there may be a configuraion file from + which the clients state should be restored (using the + gnome_client_get_config_prefix call). */ + +GnomeClientFlags gnome_client_get_flags (GnomeClient *client); + +/* The following functions are used to set or unset the session + management properties that are used by the session manager to determine + how to handle the app. If you want to unset an array property, you + have to specify a NULL argv, if you want to unset a string property + you have to specify NULL as parameter. + + The `--sm-' options are automatically added as the first arguments + to the restart and clone commands and you should not try to set them. */ + +/* The session manager usually only restarts clients which were running + when the session was last saved. You can set the restart style to make + the manager restart the client: + - at the start of every session (GNOME_RESTART_ANYWAY) or + - whenever the client dies (GNOME_RESTART_IMMEDIATELY) or + - never (GNOME_RESTART_NEVER). */ +void gnome_client_set_restart_style (GnomeClient *client, + GnomeRestartStyle style); + +/* The gnome-session manager includes an extension to the protocol which + allows the order in which clients are started up to be organised into + a number of run levels. This function may be used to inform the + gnome-session manager of where this client should appear in this + run level ordering. The priority runs from 0 (started first) to 99 + (started last) and defaults to 50. Users may override the value + that is suggested to gnome-session by calling this function. */ +void gnome_client_set_priority (GnomeClient *client, + guint priority); + +/* Executing the restart command on the local host should reproduce + the state of the client at the time of the session save as closely + as possible. Saving config info under the gnome_client_get_config_prefix + is generally useful. */ +void gnome_client_set_restart_command (GnomeClient *client, + gint argc, gchar *argv[]); + +/* This function provides an alternative way of adding new arguments + to the restart command. The arguments are placed before the arguments + specified by 'gnome_client_set_restart_command' and after the arguments + recognised by gtk specified by the user on the original command line. */ +void gnome_client_add_static_arg (GnomeClient *client, ...); + +/* Executing the discard command on the local host should delete the + information saved as part of the session save that was in progress + when the discard command was set. For example: + + gchar *prefix = gnome_client_get_config_prefix (client); + gchar *argv[] = { "rm", "-r", NULL }; + argv[2] = gnome_config_get_real_path (prefix); + gnome_client_set_discard_command (client, 3, argv); */ +void gnome_client_set_discard_command (GnomeClient *client, + gint argc, gchar *argv[]); + +/* These two commands are used by clients that use the GNOME_RESTART_ANYWAY + restart style to to undo their effects (these clients usually perform + initialisation functions and leave effects behind after they die). + The shutdown command simply undoes the effects of the client. It is + executed during a normal logout. The resign command combines the effects + of a shutdown command and a discard command. It is executed when the user + decides that the client should cease to be restarted. */ +void gnome_client_set_resign_command (GnomeClient *client, + gint argc, gchar *argv[]); +void gnome_client_set_shutdown_command (GnomeClient *client, + gint argc, gchar *argv[]); + +/* All the preceeding session manager commands are executed in the directory + and environment set up by these two commands: */ +void gnome_client_set_current_directory (GnomeClient *client, + const gchar *dir); +void gnome_client_set_environment (GnomeClient *client, + const gchar *name, + const gchar *value); + +/* These four values are set automatically to the values required by the + session manager and you should not need to change them. The clone + command is directly copied from the restart command. */ +void gnome_client_set_clone_command (GnomeClient *client, + gint argc, gchar *argv[]); +void gnome_client_set_process_id (GnomeClient *client, + pid_t pid); +void gnome_client_set_program (GnomeClient *client, + const gchar *program); +void gnome_client_set_user_id (GnomeClient *client, + const gchar *id); + +/* The following function may be called during a "save_youself" handler + to request that a (modal) dialog is presented to the user. The session + manager decides when the dialog is shown and it will not be shown + unless the interact_style == GNOME_INTERACT_ANY. A "Cancel Logout" + button will be added during a shutdown. */ +void gnome_client_save_any_dialog (GnomeClient *client, + GtkDialog *dialog); + +/* The following function may be called during a "save_youself" handler + when an error has occurred during the save. The session manager decides + when the dialog is shown and it will not be shown when the interact_style + == GNOME_INTERACT_NONE. A "Cancel Logout" button will be added + during a shutdown. */ +void gnome_client_save_error_dialog (GnomeClient *client, + GtkDialog *dialog); + +/* Request the session manager to emit the "save_yourself" signal for + a second time after all the clients in the session have ceased + interacting with the user and entered an idle state. This might be + useful if your app manages other apps and requires that they are in + an idle state before saving its final data. */ +void gnome_client_request_phase_2 (GnomeClient *client); + +/* Request the session manager to save the session in some way. The + arguments correspond with the arguments passed to the "save_yourself" + signal handler. + + The save_style indicates whether the save should affect data accessible + to other users (GNOME_SAVE_GLOBAL) or only the state visible to + the current user (GNOME_SAVE_LOCAL) or both. Setting shutdown to + TRUE will initiate a logout. The interact_style specifies which kinds + of interaction will be available. Setting fast to TRUE will limit the + save to setting the session manager properties plus any essential data. + Setting the value of global to TRUE will request that all the other + apps in the session do a save as well. A global save is mandatory when + doing a shutdown. */ +void gnome_client_request_save (GnomeClient *client, + GnomeSaveStyle save_style, + gboolean shutdown, + GnomeInteractStyle interact_style, + gboolean fast, + gboolean global); + +/* Flush the underlying connection to the session manager. This is + useful if you have some pending changes that you want to make sure + get committed. */ +void gnome_client_flush (GnomeClient *client); + +#ifndef GNOME_DISABLE_DEPRECATED +/* Note: Use the GNOME_CLIENT_PARAM_SM_CONNECT property + * of GnomeProgram */ +/* Normally the master client is connected to the session manager + automatically, when calling 'gnome_init'. One can disable this + automatic connect by calling this function. Using this function + should definitely be an exception. */ +void gnome_client_disable_master_connection (void); +#endif /* GNOME_DISABLE_DEPRECATED */ + +/* Create a new session management client and try to connect to a + session manager. This is useful if you are acting as a proxy for + other executables that do not communicate with session manager. */ +GnomeClient *gnome_client_new (void); + +/* Create a new session management client. */ +GnomeClient *gnome_client_new_without_connection (void); + +/* Try to connect to a session manager. If the client was created + with a valid session management client id, we will try to connect + to the manager with this old id. If the connection was successful, + the "connect" signal will be emitted, after some default properties + have been sent to the session manager. */ +void gnome_client_connect (GnomeClient *client); + +/* Disconnect from the session manager. After disconnecting, the + "disconnect" signal will be emitted. */ +void gnome_client_disconnect (GnomeClient *client); + +/* Set the client id. This is only possible, if the client is not + connected to a session manager. */ +void gnome_client_set_id (GnomeClient *client, + const gchar *id); + +/* Get the client id of a session management client object. If this + object has never been connected to a session manager and a client + id hasn't been set, this function returns 'NULL'. */ +const gchar* gnome_client_get_id (GnomeClient *client); + +/* Get the client id from the last session. If this client was not + recreated from a previous session, returns NULL. The session + manager tries to maintain the same id from one session to another. */ +const gchar* gnome_client_get_previous_id (GnomeClient *client); + +/* Get the client ID of the desktop's current instance, i.e. if you + * consider the desktop as a whole as a session managed app, this + * returns its session ID (GNOME extension to SM) + */ +const gchar* gnome_client_get_desktop_id (GnomeClient *client); + +/* Use the following functions, if you want to interact with the user + during a "save_yourself" handler without being restricted to using + the dialog based commands gnome_client_save_[any/error]_dialog. + If and when the session manager decides that it's the app's turn to + interact then 'func' will be called with the specified arguments and + a unique 'GnomeInteractionKey'. The session manager will block other + clients from interacting until this key is returned with + 'gnome_interaction_key_return'. */ +void gnome_client_request_interaction (GnomeClient *client, + GnomeDialogType dialog_type, + GnomeInteractFunction function, + gpointer data); + +#if 0 +void gnome_client_request_interaction_interp (GnomeClient *client, + GnomeDialogType dialog_type, + GtkCallbackMarshal function, + gpointer data, + GDestroyNotify destroy); +#endif + +/* 'gnome_interaction_key_return' is used to tell gnome, that you are + finished with interaction */ +void gnome_interaction_key_return (gint key, + gboolean cancel_shutdown); + +G_END_DECLS + +#endif /* GNOME_CLIENT_H */ diff --git a/gb.gtk/src/sm/gnome-ice.c b/gb.gtk/src/sm/gnome-ice.c new file mode 100644 index 00000000..c82e9bc2 --- /dev/null +++ b/gb.gtk/src/sm/gnome-ice.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation + * All rights reserved. + * + * This file is part of the Gnome Library. + * + * The Gnome Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * The Gnome Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Gnome Library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + @NOTATION@ + */ + +/* gnome-ice.c - Interface between ICE and Gtk. + Written by Tom Tromey . */ + +#include "sm.h" + +#ifdef HAVE_LIBSM +#include +#endif /* HAVE_LIBSM */ + +#include +#include + +#include +#include "gnome-ice.h" + +#ifdef HAVE_LIBSM + +static void gnome_ice_io_error_handler (IceConn connection); + +static void new_ice_connection (IceConn connection, IcePointer client_data, + Bool opening, IcePointer *watch_data); + +/* This is called when data is available on an ICE connection. */ +static gboolean +process_ice_messages (GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + IceConn connection = (IceConn) data; + IceProcessMessagesStatus status; + + status = IceProcessMessages (connection, NULL, NULL); + + if (status == IceProcessMessagesIOError) + { + IcePointer context = IceGetConnectionContext (connection); + + if (context && G_IS_OBJECT (context)) + { + guint disconnect_id = g_signal_lookup ("disconnect", G_OBJECT_TYPE (context)); + + if (disconnect_id > 0) + g_signal_emit (context, disconnect_id, 0); + } + else + { + IceSetShutdownNegotiation (connection, False); + IceCloseConnection (connection); + } + } + + return TRUE; +} + +/* This is called when a new ICE connection is made. It arranges for + the ICE connection to be handled via the event loop. */ +static void +new_ice_connection (IceConn connection, IcePointer client_data, Bool opening, + IcePointer *watch_data) +{ + guint input_id; + + if (opening) + { + GIOChannel *channel; + /* Make sure we don't pass on these file descriptors to any + exec'ed children */ + fcntl(IceConnectionNumber(connection),F_SETFD, + fcntl(IceConnectionNumber(connection),F_GETFD,0) | FD_CLOEXEC); + + channel = g_io_channel_unix_new (IceConnectionNumber (connection)); + input_id = g_io_add_watch (channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI, + process_ice_messages, + connection); + g_io_channel_unref (channel); + + *watch_data = (IcePointer) GUINT_TO_POINTER (input_id); + } + else + { + input_id = GPOINTER_TO_UINT ((gpointer) *watch_data); + + g_source_remove (input_id); + } +} + +static IceIOErrorHandler gnome_ice_installed_handler; + +/* We call any handler installed before (or after) gnome_ice_init but + avoid calling the default libICE handler which does an exit() */ +static void +gnome_ice_io_error_handler (IceConn connection) +{ + if (gnome_ice_installed_handler) + (*gnome_ice_installed_handler) (connection); +} + +#endif /* HAVE_LIBSM */ + +/** + * gnome_ice_init: + * + * Initialises the ICE(Inter-Client Exchange). Used to make some initial calls into + * the ICE library in order to accept ICE connection . + */ +void +gnome_ice_init (void) +{ +#ifdef HAVE_LIBSM + static gboolean ice_init = FALSE; + + if (! ice_init) + { + IceIOErrorHandler default_handler; + + gnome_ice_installed_handler = IceSetIOErrorHandler (NULL); + default_handler = IceSetIOErrorHandler (gnome_ice_io_error_handler); + + if (gnome_ice_installed_handler == default_handler) + gnome_ice_installed_handler = NULL; + + IceAddConnectionWatch (new_ice_connection, NULL); + + ice_init = TRUE; + } +#endif /* HAVE_LIBSM */ +} diff --git a/gb.gtk/src/sm/gnome-ice.h b/gb.gtk/src/sm/gnome-ice.h new file mode 100644 index 00000000..5b8677f4 --- /dev/null +++ b/gb.gtk/src/sm/gnome-ice.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation + * All rights reserved. + * + * This file is part of the Gnome Library. + * + * The Gnome Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * The Gnome Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Gnome Library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + @NOTATION@ + */ + +/* gnome-ice.h - Interface between ICE and Gtk. + Written by Tom Tromey . */ + +#ifndef GNOME_ICE_H +#define GNOME_ICE_H + + + +G_BEGIN_DECLS + +/* This function should be called before any ICE functions are used. + It will arrange for ICE connections to be read and dispatched via + the Gtk event loop. This function can be called any number of + times without harm. */ +void gnome_ice_init (void); + +G_END_DECLS + +#endif /* GNOME_ICE_H */ diff --git a/gb.gtk/src/sm/gnome-macros.h b/gb.gtk/src/sm/gnome-macros.h new file mode 100644 index 00000000..7b521864 --- /dev/null +++ b/gb.gtk/src/sm/gnome-macros.h @@ -0,0 +1,64 @@ +/* gnome-macros.h + * Macros for making GObject objects to avoid typos and reduce code size + * Copyright (C) 2000 Eazel, Inc. + * + * Authors: George Lebl + * + * All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +/* + @NOTATION@ +*/ + +#ifndef GNOME_MACROS_H +#define GNOME_MACROS_H + +#ifndef GNOME_DISABLE_DEPRECATED + +#include "bonobo-macros.h" + +/* Macros for defining classes. Ideas taken from Nautilus and GOB. */ + +/* Define the boilerplate type stuff to reduce typos and code size. Defines + * the get_type method and the parent_class static variable. */ +#define GNOME_CLASS_BOILERPLATE(type, type_as_function, \ + parent_type, parent_type_macro) \ + BONOBO_BOILERPLATE(type, type_as_function, type, \ + parent_type, parent_type_macro, \ + GNOME_REGISTER_TYPE) +#define GNOME_REGISTER_TYPE(type, type_as_function, corba_type, \ + parent_type, parent_type_macro) \ + g_type_register_static (parent_type_macro, #type, &object_info, 0) + +/* Just call the parent handler. This assumes that there is a variable + * named parent_class that points to the (duh!) parent class. Note that + * this macro is not to be used with things that return something, use + * the _WITH_DEFAULT version for that */ +#define GNOME_CALL_PARENT(parent_class_cast, name, args) \ + BONOBO_CALL_PARENT (parent_class_cast, name, args) + +/* Same as above, but in case there is no implementation, it evaluates + * to def_return */ +#define GNOME_CALL_PARENT_WITH_DEFAULT(parent_class_cast, \ + name, args, def_return) \ + BONOBO_CALL_PARENT_WITH_DEFAULT ( \ + parent_class_cast, name, args, def_return) + +#endif /* !GNOME_DISABLE_DEPRECATED */ + +#endif /* GNOME_MACROS_H */ diff --git a/gb.gtk/src/sm/gnome-marshal.c b/gb.gtk/src/sm/gnome-marshal.c new file mode 100644 index 00000000..859a0648 --- /dev/null +++ b/gb.gtk/src/sm/gnome-marshal.c @@ -0,0 +1,369 @@ + +#include + + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_schar (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#define g_marshal_value_peek_variant(v) g_value_get_variant (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +/* BOOLEAN:INT,ENUM,BOOLEAN,ENUM,BOOLEAN (./gnome-marshal.list:1) */ +void +_gnome_marshal_BOOLEAN__INT_ENUM_BOOLEAN_ENUM_BOOLEAN (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__INT_ENUM_BOOLEAN_ENUM_BOOLEAN) (gpointer data1, + gint arg_1, + gint arg_2, + gboolean arg_3, + gint arg_4, + gboolean arg_5, + gpointer data2); + register GMarshalFunc_BOOLEAN__INT_ENUM_BOOLEAN_ENUM_BOOLEAN callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 6); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__INT_ENUM_BOOLEAN_ENUM_BOOLEAN) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_enum (param_values + 2), + g_marshal_value_peek_boolean (param_values + 3), + g_marshal_value_peek_enum (param_values + 4), + g_marshal_value_peek_boolean (param_values + 5), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOLEAN:INT,STRING (./gnome-marshal.list:2) */ +void +_gnome_marshal_BOOLEAN__INT_STRING (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__INT_STRING) (gpointer data1, + gint arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_BOOLEAN__INT_STRING callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__INT_STRING) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_string (param_values + 2), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOLEAN:OBJECT (./gnome-marshal.list:3) */ +void +_gnome_marshal_BOOLEAN__OBJECT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__OBJECT) (gpointer data1, + gpointer arg_1, + gpointer data2); + register GMarshalFunc_BOOLEAN__OBJECT callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 2); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__OBJECT) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_object (param_values + 1), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOLEAN:VOID (./gnome-marshal.list:4) */ +void +_gnome_marshal_BOOLEAN__VOID (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__VOID) (gpointer data1, + gpointer data2); + register GMarshalFunc_BOOLEAN__VOID callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 1); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__VOID) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* INT:VOID (./gnome-marshal.list:5) */ +void +_gnome_marshal_INT__VOID (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gint (*GMarshalFunc_INT__VOID) (gpointer data1, + gpointer data2); + register GMarshalFunc_INT__VOID callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gint v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 1); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_INT__VOID) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + data2); + + g_value_set_int (return_value, v_return); +} + +/* VOID:ENUM,BOOLEAN (./gnome-marshal.list:6) */ +void +_gnome_marshal_VOID__ENUM_BOOLEAN (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__ENUM_BOOLEAN) (gpointer data1, + gint arg_1, + gboolean arg_2, + gpointer data2); + register GMarshalFunc_VOID__ENUM_BOOLEAN callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__ENUM_BOOLEAN) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_enum (param_values + 1), + g_marshal_value_peek_boolean (param_values + 2), + data2); +} + +/* VOID:INT,BOXED (./gnome-marshal.list:7) */ +void +_gnome_marshal_VOID__INT_BOXED (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__INT_BOXED) (gpointer data1, + gint arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__INT_BOXED callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__INT_BOXED) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_boxed (param_values + 2), + data2); +} + +/* VOID:OBJECT (./gnome-marshal.list:8) */ + +/* VOID:UINT,UINT,UINT,UINT (./gnome-marshal.list:9) */ +void +_gnome_marshal_VOID__UINT_UINT_UINT_UINT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__UINT_UINT_UINT_UINT) (gpointer data1, + guint arg_1, + guint arg_2, + guint arg_3, + guint arg_4, + gpointer data2); + register GMarshalFunc_VOID__UINT_UINT_UINT_UINT callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 5); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__UINT_UINT_UINT_UINT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_uint (param_values + 1), + g_marshal_value_peek_uint (param_values + 2), + g_marshal_value_peek_uint (param_values + 3), + g_marshal_value_peek_uint (param_values + 4), + data2); +} + diff --git a/gb.gtk/src/sm/gnome-marshal.h b/gb.gtk/src/sm/gnome-marshal.h new file mode 100644 index 00000000..95c7a997 --- /dev/null +++ b/gb.gtk/src/sm/gnome-marshal.h @@ -0,0 +1,79 @@ + +#ifndef ___gnome_marshal_MARSHAL_H__ +#define ___gnome_marshal_MARSHAL_H__ + +#include + +G_BEGIN_DECLS + +/* BOOLEAN:INT,ENUM,BOOLEAN,ENUM,BOOLEAN (./gnome-marshal.list:1) */ +extern void _gnome_marshal_BOOLEAN__INT_ENUM_BOOLEAN_ENUM_BOOLEAN (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* BOOLEAN:INT,STRING (./gnome-marshal.list:2) */ +extern void _gnome_marshal_BOOLEAN__INT_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* BOOLEAN:OBJECT (./gnome-marshal.list:3) */ +extern void _gnome_marshal_BOOLEAN__OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* BOOLEAN:VOID (./gnome-marshal.list:4) */ +extern void _gnome_marshal_BOOLEAN__VOID (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* INT:VOID (./gnome-marshal.list:5) */ +extern void _gnome_marshal_INT__VOID (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:ENUM,BOOLEAN (./gnome-marshal.list:6) */ +extern void _gnome_marshal_VOID__ENUM_BOOLEAN (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:INT,BOXED (./gnome-marshal.list:7) */ +extern void _gnome_marshal_VOID__INT_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:OBJECT (./gnome-marshal.list:8) */ +#define _gnome_marshal_VOID__OBJECT g_cclosure_marshal_VOID__OBJECT + +/* VOID:UINT,UINT,UINT,UINT (./gnome-marshal.list:9) */ +extern void _gnome_marshal_VOID__UINT_UINT_UINT_UINT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +G_END_DECLS + +#endif /* ___gnome_marshal_MARSHAL_H__ */ + diff --git a/gb.gtk/src/sm/gnome-uidefs.h b/gb.gtk/src/sm/gnome-uidefs.h new file mode 100644 index 00000000..dbf0e828 --- /dev/null +++ b/gb.gtk/src/sm/gnome-uidefs.h @@ -0,0 +1,122 @@ +/* gnome-uidefs.h: + * Copyright (C) 1998 Havoc Pennington + * All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Cambridge, MA 02139, USA. + */ +/* + @NOTATION@ +*/ + +#ifndef GNOME_UIDEFS_H +#define GNOME_UIDEFS_H + +/* This file defines standard sizes, spacings, and whatever + else seems standardizable via simple defines. */ + +/* All-purpose padding. If you always use these instead of making up + some arbitrary padding number that looks good on your screen, + people can change the "spaciousness" of the GUI globally. */ +#define GNOME_PAD 8 +#define GNOME_PAD_SMALL 4 +#define GNOME_PAD_BIG 12 + +/* Deprecated with GnomeDialog, GTK has a much nicer way of + * doing this */ +#ifndef GNOME_DISABLE_DEPRECATED + +/* These are the button numbers on a yes-no or ok-cancel GnomeDialog, + and in the gnome-app-util callbacks. Make the program more + readable, is all. */ +#define GNOME_YES 0 +#define GNOME_NO 1 +#define GNOME_OK 0 +#define GNOME_CANCEL 1 + +#endif + +#include + +/* These are keybindings, in GnomeUIInfo format. USE THEM OR DIE! + Add to the list as well.. +*/ + +#define GNOME_KEY_NAME_QUIT 'q' +#define GNOME_KEY_MOD_QUIT (GDK_CONTROL_MASK) + +#ifndef GNOME_DISABLE_DEPRECATED + +#define GNOME_KEY_NAME_EXIT GNOME_KEY_NAME_QUIT +#define GNOME_KEY_MOD_EXIT GNOME_KEY_MOD_QUIT + +#endif + +#define GNOME_KEY_NAME_CLOSE 'w' +#define GNOME_KEY_MOD_CLOSE (GDK_CONTROL_MASK) + +#define GNOME_KEY_NAME_CUT 'x' +#define GNOME_KEY_MOD_CUT (GDK_CONTROL_MASK) +#define GNOME_KEY_NAME_COPY 'c' +#define GNOME_KEY_MOD_COPY (GDK_CONTROL_MASK) +#define GNOME_KEY_NAME_PASTE 'v' +#define GNOME_KEY_MOD_PASTE (GDK_CONTROL_MASK) +#define GNOME_KEY_NAME_SELECT_ALL 'a' +#define GNOME_KEY_MOD_SELECT_ALL (GDK_CONTROL_MASK) +#define GNOME_KEY_NAME_CLEAR 0 +#define GNOME_KEY_MOD_CLEAR (0) + +#define GNOME_KEY_NAME_UNDO 'z' +#define GNOME_KEY_MOD_UNDO (GDK_CONTROL_MASK) +#define GNOME_KEY_NAME_REDO 'z' +#define GNOME_KEY_MOD_REDO (GDK_CONTROL_MASK | GDK_SHIFT_MASK) + +#define GNOME_KEY_NAME_SAVE 's' +#define GNOME_KEY_MOD_SAVE (GDK_CONTROL_MASK) +#define GNOME_KEY_NAME_OPEN 'o' +#define GNOME_KEY_MOD_OPEN (GDK_CONTROL_MASK) +#define GNOME_KEY_NAME_SAVE_AS 's' +#define GNOME_KEY_MOD_SAVE_AS (GDK_CONTROL_MASK | GDK_SHIFT_MASK) +#define GNOME_KEY_NAME_NEW 'n' +#define GNOME_KEY_MOD_NEW (GDK_CONTROL_MASK) + +#define GNOME_KEY_NAME_PRINT 'p' +#define GNOME_KEY_MOD_PRINT (GDK_CONTROL_MASK) + +#define GNOME_KEY_NAME_PRINT_SETUP 0 +#define GNOME_KEY_MOD_PRINT_SETUP (0) + +#define GNOME_KEY_NAME_FIND 'f' +#define GNOME_KEY_MOD_FIND (GDK_CONTROL_MASK) +#define GNOME_KEY_NAME_FIND_AGAIN 'g' +#define GNOME_KEY_MOD_FIND_AGAIN (GDK_CONTROL_MASK) +#define GNOME_KEY_NAME_REPLACE 'r' +#define GNOME_KEY_MOD_REPLACE (GDK_CONTROL_MASK) + +#define GNOME_KEY_NAME_NEW_WINDOW 0 +#define GNOME_KEY_MOD_NEW_WINDOW (0) +#define GNOME_KEY_NAME_CLOSE_WINDOW 0 +#define GNOME_KEY_MOD_CLOSE_WINDOW (0) + +#define GNOME_KEY_NAME_REDO_MOVE 'z' +#define GNOME_KEY_MOD_REDO_MOVE (GDK_CONTROL_MASK | GDK_SHIFT_MASK) +#define GNOME_KEY_NAME_UNDO_MOVE 'z' +#define GNOME_KEY_MOD_UNDO_MOVE (GDK_CONTROL_MASK) + +#define GNOME_KEY_NAME_PAUSE_GAME GDK_Pause +#define GNOME_KEY_MOD_PAUSE_GAME (0) +#define GNOME_KEY_NAME_NEW_GAME 'n' +#define GNOME_KEY_MOD_NEW_GAME (GDK_CONTROL_MASK) + +#endif diff --git a/gb.gtk/src/sm/gnometypebuiltins.c b/gb.gtk/src/sm/gnometypebuiltins.c new file mode 100644 index 00000000..2727c751 --- /dev/null +++ b/gb.gtk/src/sm/gnometypebuiltins.c @@ -0,0 +1,459 @@ + + + +//#include "libgnomeui.h" + +#include "gnome-client.h" +#include "gnometypebuiltins.h" + +#if 0 +/* enumerations from "gnome-app-helper.h" */ +GType +gnome_ui_info_type_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_ui_info_type_values[] = { + { GNOME_APP_UI_ENDOFINFO, "GNOME_APP_UI_ENDOFINFO", "endofinfo" }, + { GNOME_APP_UI_ITEM, "GNOME_APP_UI_ITEM", "item" }, + { GNOME_APP_UI_TOGGLEITEM, "GNOME_APP_UI_TOGGLEITEM", "toggleitem" }, + { GNOME_APP_UI_RADIOITEMS, "GNOME_APP_UI_RADIOITEMS", "radioitems" }, + { GNOME_APP_UI_SUBTREE, "GNOME_APP_UI_SUBTREE", "subtree" }, + { GNOME_APP_UI_SEPARATOR, "GNOME_APP_UI_SEPARATOR", "separator" }, + { GNOME_APP_UI_HELP, "GNOME_APP_UI_HELP", "help" }, + { GNOME_APP_UI_BUILDER_DATA, "GNOME_APP_UI_BUILDER_DATA", "builder-data" }, + { GNOME_APP_UI_ITEM_CONFIGURABLE, "GNOME_APP_UI_ITEM_CONFIGURABLE", "item-configurable" }, + { GNOME_APP_UI_SUBTREE_STOCK, "GNOME_APP_UI_SUBTREE_STOCK", "subtree-stock" }, + { GNOME_APP_UI_INCLUDE, "GNOME_APP_UI_INCLUDE", "include" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeUIInfoType", _gnome_ui_info_type_values); + } + + return type; +} + +GType +gnome_ui_info_configurable_types_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_ui_info_configurable_types_values[] = { + { GNOME_APP_CONFIGURABLE_ITEM_NEW, "GNOME_APP_CONFIGURABLE_ITEM_NEW", "new" }, + { GNOME_APP_CONFIGURABLE_ITEM_OPEN, "GNOME_APP_CONFIGURABLE_ITEM_OPEN", "open" }, + { GNOME_APP_CONFIGURABLE_ITEM_SAVE, "GNOME_APP_CONFIGURABLE_ITEM_SAVE", "save" }, + { GNOME_APP_CONFIGURABLE_ITEM_SAVE_AS, "GNOME_APP_CONFIGURABLE_ITEM_SAVE_AS", "save-as" }, + { GNOME_APP_CONFIGURABLE_ITEM_REVERT, "GNOME_APP_CONFIGURABLE_ITEM_REVERT", "revert" }, + { GNOME_APP_CONFIGURABLE_ITEM_PRINT, "GNOME_APP_CONFIGURABLE_ITEM_PRINT", "print" }, + { GNOME_APP_CONFIGURABLE_ITEM_PRINT_SETUP, "GNOME_APP_CONFIGURABLE_ITEM_PRINT_SETUP", "print-setup" }, + { GNOME_APP_CONFIGURABLE_ITEM_CLOSE, "GNOME_APP_CONFIGURABLE_ITEM_CLOSE", "close" }, + { GNOME_APP_CONFIGURABLE_ITEM_QUIT, "GNOME_APP_CONFIGURABLE_ITEM_QUIT", "quit" }, + { GNOME_APP_CONFIGURABLE_ITEM_CUT, "GNOME_APP_CONFIGURABLE_ITEM_CUT", "cut" }, + { GNOME_APP_CONFIGURABLE_ITEM_COPY, "GNOME_APP_CONFIGURABLE_ITEM_COPY", "copy" }, + { GNOME_APP_CONFIGURABLE_ITEM_PASTE, "GNOME_APP_CONFIGURABLE_ITEM_PASTE", "paste" }, + { GNOME_APP_CONFIGURABLE_ITEM_CLEAR, "GNOME_APP_CONFIGURABLE_ITEM_CLEAR", "clear" }, + { GNOME_APP_CONFIGURABLE_ITEM_UNDO, "GNOME_APP_CONFIGURABLE_ITEM_UNDO", "undo" }, + { GNOME_APP_CONFIGURABLE_ITEM_REDO, "GNOME_APP_CONFIGURABLE_ITEM_REDO", "redo" }, + { GNOME_APP_CONFIGURABLE_ITEM_FIND, "GNOME_APP_CONFIGURABLE_ITEM_FIND", "find" }, + { GNOME_APP_CONFIGURABLE_ITEM_FIND_AGAIN, "GNOME_APP_CONFIGURABLE_ITEM_FIND_AGAIN", "find-again" }, + { GNOME_APP_CONFIGURABLE_ITEM_REPLACE, "GNOME_APP_CONFIGURABLE_ITEM_REPLACE", "replace" }, + { GNOME_APP_CONFIGURABLE_ITEM_PROPERTIES, "GNOME_APP_CONFIGURABLE_ITEM_PROPERTIES", "properties" }, + { GNOME_APP_CONFIGURABLE_ITEM_PREFERENCES, "GNOME_APP_CONFIGURABLE_ITEM_PREFERENCES", "preferences" }, + { GNOME_APP_CONFIGURABLE_ITEM_ABOUT, "GNOME_APP_CONFIGURABLE_ITEM_ABOUT", "about" }, + { GNOME_APP_CONFIGURABLE_ITEM_SELECT_ALL, "GNOME_APP_CONFIGURABLE_ITEM_SELECT_ALL", "select-all" }, + { GNOME_APP_CONFIGURABLE_ITEM_NEW_WINDOW, "GNOME_APP_CONFIGURABLE_ITEM_NEW_WINDOW", "new-window" }, + { GNOME_APP_CONFIGURABLE_ITEM_CLOSE_WINDOW, "GNOME_APP_CONFIGURABLE_ITEM_CLOSE_WINDOW", "close-window" }, + { GNOME_APP_CONFIGURABLE_ITEM_NEW_GAME, "GNOME_APP_CONFIGURABLE_ITEM_NEW_GAME", "new-game" }, + { GNOME_APP_CONFIGURABLE_ITEM_PAUSE_GAME, "GNOME_APP_CONFIGURABLE_ITEM_PAUSE_GAME", "pause-game" }, + { GNOME_APP_CONFIGURABLE_ITEM_RESTART_GAME, "GNOME_APP_CONFIGURABLE_ITEM_RESTART_GAME", "restart-game" }, + { GNOME_APP_CONFIGURABLE_ITEM_UNDO_MOVE, "GNOME_APP_CONFIGURABLE_ITEM_UNDO_MOVE", "undo-move" }, + { GNOME_APP_CONFIGURABLE_ITEM_REDO_MOVE, "GNOME_APP_CONFIGURABLE_ITEM_REDO_MOVE", "redo-move" }, + { GNOME_APP_CONFIGURABLE_ITEM_HINT, "GNOME_APP_CONFIGURABLE_ITEM_HINT", "hint" }, + { GNOME_APP_CONFIGURABLE_ITEM_SCORES, "GNOME_APP_CONFIGURABLE_ITEM_SCORES", "scores" }, + { GNOME_APP_CONFIGURABLE_ITEM_END_GAME, "GNOME_APP_CONFIGURABLE_ITEM_END_GAME", "end-game" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeUIInfoConfigurableTypes", _gnome_ui_info_configurable_types_values); + } + + return type; +} + +GType +gnome_ui_pixmap_type_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_ui_pixmap_type_values[] = { + { GNOME_APP_PIXMAP_NONE, "GNOME_APP_PIXMAP_NONE", "none" }, + { GNOME_APP_PIXMAP_STOCK, "GNOME_APP_PIXMAP_STOCK", "stock" }, + { GNOME_APP_PIXMAP_DATA, "GNOME_APP_PIXMAP_DATA", "data" }, + { GNOME_APP_PIXMAP_FILENAME, "GNOME_APP_PIXMAP_FILENAME", "filename" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeUIPixmapType", _gnome_ui_pixmap_type_values); + } + + return type; +} +#endif + +/* enumerations from "gnome-client.h" */ +GType +gnome_interact_style_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_interact_style_values[] = { + { GNOME_INTERACT_NONE, "GNOME_INTERACT_NONE", "none" }, + { GNOME_INTERACT_ERRORS, "GNOME_INTERACT_ERRORS", "errors" }, + { GNOME_INTERACT_ANY, "GNOME_INTERACT_ANY", "any" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeInteractStyle", _gnome_interact_style_values); + } + + return type; +} + +GType +gnome_dialog_type_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_dialog_type_values[] = { + { GNOME_DIALOG_ERROR, "GNOME_DIALOG_ERROR", "error" }, + { GNOME_DIALOG_NORMAL, "GNOME_DIALOG_NORMAL", "normal" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeDialogType", _gnome_dialog_type_values); + } + + return type; +} + +GType +gnome_save_style_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_save_style_values[] = { + { GNOME_SAVE_GLOBAL, "GNOME_SAVE_GLOBAL", "global" }, + { GNOME_SAVE_LOCAL, "GNOME_SAVE_LOCAL", "local" }, + { GNOME_SAVE_BOTH, "GNOME_SAVE_BOTH", "both" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeSaveStyle", _gnome_save_style_values); + } + + return type; +} + +GType +gnome_restart_style_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_restart_style_values[] = { + { GNOME_RESTART_IF_RUNNING, "GNOME_RESTART_IF_RUNNING", "if-running" }, + { GNOME_RESTART_ANYWAY, "GNOME_RESTART_ANYWAY", "anyway" }, + { GNOME_RESTART_IMMEDIATELY, "GNOME_RESTART_IMMEDIATELY", "immediately" }, + { GNOME_RESTART_NEVER, "GNOME_RESTART_NEVER", "never" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeRestartStyle", _gnome_restart_style_values); + } + + return type; +} + +GType +gnome_client_state_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_client_state_values[] = { + { GNOME_CLIENT_IDLE, "GNOME_CLIENT_IDLE", "idle" }, + { GNOME_CLIENT_SAVING_PHASE_1, "GNOME_CLIENT_SAVING_PHASE_1", "saving-phase-1" }, + { GNOME_CLIENT_WAITING_FOR_PHASE_2, "GNOME_CLIENT_WAITING_FOR_PHASE_2", "waiting-for-phase-2" }, + { GNOME_CLIENT_SAVING_PHASE_2, "GNOME_CLIENT_SAVING_PHASE_2", "saving-phase-2" }, + { GNOME_CLIENT_FROZEN, "GNOME_CLIENT_FROZEN", "frozen" }, + { GNOME_CLIENT_DISCONNECTED, "GNOME_CLIENT_DISCONNECTED", "disconnected" }, + { GNOME_CLIENT_REGISTERING, "GNOME_CLIENT_REGISTERING", "registering" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeClientState", _gnome_client_state_values); + } + + return type; +} + +GType +gnome_client_flags_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GFlagsValue _gnome_client_flags_values[] = { + { GNOME_CLIENT_IS_CONNECTED, "GNOME_CLIENT_IS_CONNECTED", "is-connected" }, + { GNOME_CLIENT_RESTARTED, "GNOME_CLIENT_RESTARTED", "restarted" }, + { GNOME_CLIENT_RESTORED, "GNOME_CLIENT_RESTORED", "restored" }, + { 0, NULL, NULL } + }; + + type = g_flags_register_static ("GnomeClientFlags", _gnome_client_flags_values); + } + + return type; +} + +#if 0 +/* enumerations from "gnome-dateedit.h" */ +GType +gnome_date_edit_flags_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GFlagsValue _gnome_date_edit_flags_values[] = { + { GNOME_DATE_EDIT_SHOW_TIME, "GNOME_DATE_EDIT_SHOW_TIME", "show-time" }, + { GNOME_DATE_EDIT_24_HR, "GNOME_DATE_EDIT_24_HR", "24-hr" }, + { GNOME_DATE_EDIT_WEEK_STARTS_ON_MONDAY, "GNOME_DATE_EDIT_WEEK_STARTS_ON_MONDAY", "week-starts-on-monday" }, + { GNOME_DATE_EDIT_DISPLAY_SECONDS, "GNOME_DATE_EDIT_DISPLAY_SECONDS", "display-seconds" }, + { 0, NULL, NULL } + }; + + type = g_flags_register_static ("GnomeDateEditFlags", _gnome_date_edit_flags_values); + } + + return type; +} + + +/* enumerations from "gnome-druid-page-edge.h" */ +GType +gnome_edge_position_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_edge_position_values[] = { + { GNOME_EDGE_START, "GNOME_EDGE_START", "start" }, + { GNOME_EDGE_FINISH, "GNOME_EDGE_FINISH", "finish" }, + { GNOME_EDGE_OTHER, "GNOME_EDGE_OTHER", "other" }, + { GNOME_EDGE_LAST, "GNOME_EDGE_LAST", "last" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeEdgePosition", _gnome_edge_position_values); + } + + return type; +} + + +/* enumerations from "gnome-font-picker.h" */ +GType +gnome_font_picker_mode_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_font_picker_mode_values[] = { + { GNOME_FONT_PICKER_MODE_PIXMAP, "GNOME_FONT_PICKER_MODE_PIXMAP", "pixmap" }, + { GNOME_FONT_PICKER_MODE_FONT_INFO, "GNOME_FONT_PICKER_MODE_FONT_INFO", "font-info" }, + { GNOME_FONT_PICKER_MODE_USER_WIDGET, "GNOME_FONT_PICKER_MODE_USER_WIDGET", "user-widget" }, + { GNOME_FONT_PICKER_MODE_UNKNOWN, "GNOME_FONT_PICKER_MODE_UNKNOWN", "unknown" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeFontPickerMode", _gnome_font_picker_mode_values); + } + + return type; +} + + +/* enumerations from "gnome-icon-list.h" */ +GType +gnome_icon_list_mode_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_icon_list_mode_values[] = { + { GNOME_ICON_LIST_ICONS, "GNOME_ICON_LIST_ICONS", "icons" }, + { GNOME_ICON_LIST_TEXT_BELOW, "GNOME_ICON_LIST_TEXT_BELOW", "text-below" }, + { GNOME_ICON_LIST_TEXT_RIGHT, "GNOME_ICON_LIST_TEXT_RIGHT", "text-right" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeIconListMode", _gnome_icon_list_mode_values); + } + + return type; +} + + +/* enumerations from "gnome-icon-lookup.h" */ +GType +gnome_icon_lookup_flags_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GFlagsValue _gnome_icon_lookup_flags_values[] = { + { GNOME_ICON_LOOKUP_FLAGS_NONE, "GNOME_ICON_LOOKUP_FLAGS_NONE", "none" }, + { GNOME_ICON_LOOKUP_FLAGS_EMBEDDING_TEXT, "GNOME_ICON_LOOKUP_FLAGS_EMBEDDING_TEXT", "embedding-text" }, + { GNOME_ICON_LOOKUP_FLAGS_SHOW_SMALL_IMAGES_AS_THEMSELVES, "GNOME_ICON_LOOKUP_FLAGS_SHOW_SMALL_IMAGES_AS_THEMSELVES", "show-small-images-as-themselves" }, + { GNOME_ICON_LOOKUP_FLAGS_ALLOW_SVG_AS_THEMSELVES, "GNOME_ICON_LOOKUP_FLAGS_ALLOW_SVG_AS_THEMSELVES", "allow-svg-as-themselves" }, + { 0, NULL, NULL } + }; + + type = g_flags_register_static ("GnomeIconLookupFlags", _gnome_icon_lookup_flags_values); + } + + return type; +} + +GType +gnome_icon_lookup_result_flags_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GFlagsValue _gnome_icon_lookup_result_flags_values[] = { + { GNOME_ICON_LOOKUP_RESULT_FLAGS_NONE, "GNOME_ICON_LOOKUP_RESULT_FLAGS_NONE", "none" }, + { GNOME_ICON_LOOKUP_RESULT_FLAGS_THUMBNAIL, "GNOME_ICON_LOOKUP_RESULT_FLAGS_THUMBNAIL", "thumbnail" }, + { 0, NULL, NULL } + }; + + type = g_flags_register_static ("GnomeIconLookupResultFlags", _gnome_icon_lookup_result_flags_values); + } + + return type; +} + + +/* enumerations from "gnome-mdi.h" */ +GType +gnome_mdi_mode_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_mdi_mode_values[] = { + { GNOME_MDI_NOTEBOOK, "GNOME_MDI_NOTEBOOK", "notebook" }, + { GNOME_MDI_TOPLEVEL, "GNOME_MDI_TOPLEVEL", "toplevel" }, + { GNOME_MDI_MODAL, "GNOME_MDI_MODAL", "modal" }, + { GNOME_MDI_DEFAULT_MODE, "GNOME_MDI_DEFAULT_MODE", "default-mode" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeMDIMode", _gnome_mdi_mode_values); + } + + return type; +} + + +/* enumerations from "gnome-password-dialog.h" */ +GType +gnome_password_dialog_remember_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_password_dialog_remember_values[] = { + { GNOME_PASSWORD_DIALOG_REMEMBER_NOTHING, "GNOME_PASSWORD_DIALOG_REMEMBER_NOTHING", "nothing" }, + { GNOME_PASSWORD_DIALOG_REMEMBER_SESSION, "GNOME_PASSWORD_DIALOG_REMEMBER_SESSION", "session" }, + { GNOME_PASSWORD_DIALOG_REMEMBER_FOREVER, "GNOME_PASSWORD_DIALOG_REMEMBER_FOREVER", "forever" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomePasswordDialogRemember", _gnome_password_dialog_remember_values); + } + + return type; +} + + +/* enumerations from "gnome-thumbnail.h" */ +GType +gnome_thumbnail_size_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_thumbnail_size_values[] = { + { GNOME_THUMBNAIL_SIZE_NORMAL, "GNOME_THUMBNAIL_SIZE_NORMAL", "normal" }, + { GNOME_THUMBNAIL_SIZE_LARGE, "GNOME_THUMBNAIL_SIZE_LARGE", "large" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomeThumbnailSize", _gnome_thumbnail_size_values); + } + + return type; +} + + +/* enumerations from "gnome-types.h" */ +GType +gnome_preferences_type_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GEnumValue _gnome_preferences_type_values[] = { + { GNOME_PREFERENCES_NEVER, "GNOME_PREFERENCES_NEVER", "never" }, + { GNOME_PREFERENCES_USER, "GNOME_PREFERENCES_USER", "user" }, + { GNOME_PREFERENCES_ALWAYS, "GNOME_PREFERENCES_ALWAYS", "always" }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("GnomePreferencesType", _gnome_preferences_type_values); + } + + return type; +} +#endif + + + + diff --git a/gb.gtk/src/sm/gnometypebuiltins.h b/gb.gtk/src/sm/gnometypebuiltins.h new file mode 100644 index 00000000..09eb506c --- /dev/null +++ b/gb.gtk/src/sm/gnometypebuiltins.h @@ -0,0 +1,76 @@ + + + +#ifndef __GNOMETYPEBUILTINS_H__ +#define __GNOMETYPEBUILTINS_H__ 1 + +#include + +G_BEGIN_DECLS + + +/* --- gnome-app-helper.h --- */ +#define GNOME_TYPE_UI_INFO_TYPE gnome_ui_info_type_get_type() +GType gnome_ui_info_type_get_type (void); +#define GNOME_TYPE_UI_INFO_CONFIGURABLE_TYPES gnome_ui_info_configurable_types_get_type() +GType gnome_ui_info_configurable_types_get_type (void); +#define GNOME_TYPE_UI_PIXMAP_TYPE gnome_ui_pixmap_type_get_type() +GType gnome_ui_pixmap_type_get_type (void); + +/* --- gnome-client.h --- */ +#define GNOME_TYPE_INTERACT_STYLE gnome_interact_style_get_type() +GType gnome_interact_style_get_type (void); +#define GNOME_TYPE_DIALOG_TYPE gnome_dialog_type_get_type() +GType gnome_dialog_type_get_type (void); +#define GNOME_TYPE_SAVE_STYLE gnome_save_style_get_type() +GType gnome_save_style_get_type (void); +#define GNOME_TYPE_RESTART_STYLE gnome_restart_style_get_type() +GType gnome_restart_style_get_type (void); +#define GNOME_TYPE_CLIENT_STATE gnome_client_state_get_type() +GType gnome_client_state_get_type (void); +#define GNOME_TYPE_CLIENT_FLAGS gnome_client_flags_get_type() +GType gnome_client_flags_get_type (void); + +/* --- gnome-dateedit.h --- */ +#define GNOME_TYPE_DATE_EDIT_FLAGS gnome_date_edit_flags_get_type() +GType gnome_date_edit_flags_get_type (void); + +/* --- gnome-druid-page-edge.h --- */ +#define GNOME_TYPE_EDGE_POSITION gnome_edge_position_get_type() +GType gnome_edge_position_get_type (void); + +/* --- gnome-font-picker.h --- */ +#define GNOME_TYPE_FONT_PICKER_MODE gnome_font_picker_mode_get_type() +GType gnome_font_picker_mode_get_type (void); + +/* --- gnome-icon-list.h --- */ +#define GNOME_TYPE_ICON_LIST_MODE gnome_icon_list_mode_get_type() +GType gnome_icon_list_mode_get_type (void); + +/* --- gnome-icon-lookup.h --- */ +#define GNOME_TYPE_ICON_LOOKUP_FLAGS gnome_icon_lookup_flags_get_type() +GType gnome_icon_lookup_flags_get_type (void); +#define GNOME_TYPE_ICON_LOOKUP_RESULT_FLAGS gnome_icon_lookup_result_flags_get_type() +GType gnome_icon_lookup_result_flags_get_type (void); + +/* --- gnome-mdi.h --- */ +#define GNOME_TYPE_MDI_MODE gnome_mdi_mode_get_type() +GType gnome_mdi_mode_get_type (void); + +/* --- gnome-password-dialog.h --- */ +#define GNOME_TYPE_PASSWORD_DIALOG_REMEMBER gnome_password_dialog_remember_get_type() +GType gnome_password_dialog_remember_get_type (void); + +/* --- gnome-thumbnail.h --- */ +#define GNOME_TYPE_THUMBNAIL_SIZE gnome_thumbnail_size_get_type() +GType gnome_thumbnail_size_get_type (void); + +/* --- gnome-types.h --- */ +#define GNOME_TYPE_PREFERENCES_TYPE gnome_preferences_type_get_type() +GType gnome_preferences_type_get_type (void); +G_END_DECLS + +#endif /* __GNOMETYPEBUILTINS_H__ */ + + + diff --git a/gb.gtk/src/sm/libgnomeui.h b/gb.gtk/src/sm/libgnomeui.h new file mode 100644 index 00000000..0dcd851f --- /dev/null +++ b/gb.gtk/src/sm/libgnomeui.h @@ -0,0 +1,88 @@ +/* -*- Mode: C; c-set-style: linux; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation + * All rights reserved. + * + * This file is part of the Gnome Library. + * + * The Gnome Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * The Gnome Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Gnome Library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + @NOTATION@ + */ + +#ifndef LIBGNOMEUI_H +#define LIBGNOMEUI_H + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/gb.gtk/src/sm/libgnomeuiP.h b/gb.gtk/src/sm/libgnomeuiP.h new file mode 100644 index 00000000..fb234868 --- /dev/null +++ b/gb.gtk/src/sm/libgnomeuiP.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; c-set-style: linux; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation + * All rights reserved. + * + * This file is part of the Gnome Library. + * + * The Gnome Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * The Gnome Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Gnome Library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + @NOTATION@ + */ + +#ifndef LIBGNOMEUIP_H +#define LIBGNOMEUIP_H + +#include + + +//#include +#include "gnome-macros.h" +#include "gnometypebuiltins.h" +//#include "gnome-gconf-ui.h" + +G_BEGIN_DECLS + +void gnome_type_init(void); + +#ifdef G_OS_WIN32 +const char *_gnome_ui_get_localedir (void); +const char *_gnome_ui_get_datadir (void); + +#undef GNOMEUILOCALEDIR +#define GNOMEUILOCALEDIR _gnome_ui_get_localedir() +#undef LIBGNOMEUI_DATADIR +#define LIBGNOMEUI_DATADIR _gnome_ui_get_datadir() + +#endif + +G_END_DECLS + +#endif /* LIBGNOMEUIP_H */ + diff --git a/gb.gtk/src/sm/sm.h b/gb.gtk/src/sm/sm.h new file mode 100644 index 00000000..657a7499 --- /dev/null +++ b/gb.gtk/src/sm/sm.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + sm.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SM_H +#define __SM_H + +#include "gnome-client.h" + +G_BEGIN_DECLS + +void session_manager_init(int *argc, char ***argv); +void session_manager_exit(void); +int session_manager_get_desktop(void); +void session_manager_set_desktop(int desktop); + +G_END_DECLS + +#endif diff --git a/gb.gtk/src/watcher.cpp b/gb.gtk/src/watcher.cpp new file mode 100644 index 00000000..88f8ccfa --- /dev/null +++ b/gb.gtk/src/watcher.cpp @@ -0,0 +1,150 @@ +/*************************************************************************** + + watcher.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include +#include +#include +#include "main.h" +#include "gambas.h" +#include "watcher.h" + +static WATCH **watch = NULL; + +static gboolean watch_adaptor(GIOChannel *source, GIOCondition condition, gpointer param) +{ + WATCH *data = (WATCH *)param; + + if (!data) return true; + + switch (condition) + { + case G_IO_IN: + (*data->callback_read)(data->fd, GB_WATCH_READ, data->param_read); break; + case G_IO_OUT: + (*data->callback_write)(data->fd, GB_WATCH_WRITE, data->param_write); break; + default: break; + } + + return true; +} + +void CWatcher::init() +{ + GB.NewArray(POINTER(&watch), sizeof(WATCH *), 0); +} + +void CWatcher::exit() +{ + Clear(); + GB.FreeArray(POINTER(&watch)); +} + +void CWatcher::Clear() +{ + while (count()) + { + CWatcher::Add(watch[0]->fd, GB_WATCH_NONE, NULL, 0); + } +} + +void CWatcher::Remove(int fd) +{ + CWatcher::Add(fd,GB_WATCH_NONE,NULL,0); +} + +void CWatcher::Add(int fd, int type, void *callback, intptr_t param) +{ + WATCH *data = NULL; + WATCH **pwatch; + int i; + + for (i = 0; i < count(); i++) + { + if (watch[i]->fd == fd) + { + data = watch[i]; + break; + } + } + + if (!data) + { + if (type == GB_WATCH_NONE || !callback) + return; + + pwatch = (WATCH **)GB.Add(&watch); + GB.Alloc(POINTER(pwatch), sizeof(WATCH)); + data = *pwatch; + data->fd = fd; + data->channel_read = data->channel_write = 0; + data->callback_read = data->callback_write = 0; + } + + if (data->callback_read && (type == GB_WATCH_NONE || type == GB_WATCH_READ)) + { + g_source_remove(data->id_read); + g_io_channel_unref(data->channel_read); + data->callback_read = 0; + data->channel_read = 0; + } + + if (data->callback_write && (type == GB_WATCH_NONE || type == GB_WATCH_WRITE)) + { + g_source_remove(data->id_write); + g_io_channel_unref(data->channel_write); + data->callback_write = 0; + data->channel_write = 0; + } + + if (callback) + { + if (type == GB_WATCH_READ) + { + data->callback_read = (WATCH_CALLBACK)callback; + data->param_read = param; + data->channel_read = g_io_channel_unix_new(fd); + g_io_channel_set_encoding(data->channel_read, NULL, NULL); + data->id_read = g_io_add_watch_full(data->channel_read, G_PRIORITY_LOW, G_IO_IN, watch_adaptor, (void*)data, NULL); + } + else if (type == GB_WATCH_WRITE) + { + data->callback_write = (WATCH_CALLBACK)callback; + data->param_write = param; + data->channel_write = g_io_channel_unix_new(fd); + g_io_channel_set_encoding(data->channel_write, NULL, NULL); + data->id_write = g_io_add_watch_full(data->channel_write, G_PRIORITY_LOW, G_IO_OUT, watch_adaptor, (void*)data, NULL); + } + } + + if (!data->callback_read && !data->callback_write) + { + GB.Free(POINTER(&data)); + GB.Remove(&watch, i, 1); + MAIN_check_quit(); + } +} + +int CWatcher::count() +{ + return GB.Count(watch); +} diff --git a/gb.gtk/src/watcher.h b/gb.gtk/src/watcher.h new file mode 100644 index 00000000..8eb08f1e --- /dev/null +++ b/gb.gtk/src/watcher.h @@ -0,0 +1,58 @@ +/*************************************************************************** + + watcher.h + + (c) 2004-2007 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __WATCHER_H +#define __WATCHER_H + +#include "gambas.h" +#include + +typedef + GB_WATCH_CALLBACK WATCH_CALLBACK; + +typedef + struct { + int fd; + GIOChannel *channel_read; + guint id_read; + WATCH_CALLBACK callback_read; + intptr_t param_read; + GIOChannel *channel_write; + guint id_write; + WATCH_CALLBACK callback_write; + intptr_t param_write; + } + WATCH; + +class CWatcher +{ +public: + static void init(); + static void exit(); + static void Add(int fd, int type, void *callback, intptr_t param); + static void Clear(); + static void Remove(int fd); + static int count(); +}; + +#endif diff --git a/gb.gtk/src/widgets.h b/gb.gtk/src/widgets.h new file mode 100644 index 00000000..68a5e41a --- /dev/null +++ b/gb.gtk/src/widgets.h @@ -0,0 +1,164 @@ +/*************************************************************************** + + widgets.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __WIDGETS_H +#define __WIDGETS_H + +#include +#include +#ifndef GAMBAS_DIRECTFB +#ifdef GDK_WINDOWING_X11 +#include +#endif +#endif +#include + +#ifdef GTK3 + +#ifdef GDK_WINDOWING_X11 +#include +#endif +#include + +#endif + +#include +#include +#include +#include +#include +#include + +#include "gb_common.h" + +#include "gb.form.properties.h" +#include "gb.form.const.h" + +#include "gpicture.h" +#include "gcursor.h" +#include "gfont.h" +#include "gcontrol.h" +#include "gcontainer.h" +#include "gtools.h" + +#include "main.h" +//#define GTK_DEBUG_SIGNALS +//#define GTK_DEBUG_OBJECTS + +enum +{ + Type_gLabel = 0x0001, + Type_gTextLabel = 0x0002, + Type_gButton = 0x0003, + Type_gPictureBox = 0x0007, + Type_gProgressBar = 0x000A, + Type_gSlider = 0x000E, + Type_gScrollBar = 0x000F, + Type_gPlugin = 0x0013, + Type_gMovieBox = 0x0014, + Type_gSpinBox = 0x0016, + Type_gSeparator = 0x0018, + Type_gFrame = 0x0105, + Type_gMainWindow = 0x0106, + Type_gPanel = 0x0108, + Type_gDrawingArea = 0x0109, + Type_gTabStrip = 0x0111, + Type_gSplitter = 0x0117, + Type_gScrollView = 0x0115, + Type_gTextBox = 0x1004, + Type_gTextArea = 0x100B, + Type_gComboBox = 0x100C, + Type_gListBox = 0x100D, + Type_gListView = 0x1010, + Type_gColumnView = 0x1012, + Type_gTreeView = 0x1014, + Type_gGLArea = 0x0018, +}; + +enum +{ + gEvent_MousePress, + gEvent_MouseRelease, + gEvent_MouseMove, + gEvent_MouseDrag, + gEvent_MouseWheel, + gEvent_MouseDblClick, + gEvent_MouseMenu, + gEvent_KeyPress, + gEvent_KeyRelease, + gEvent_FocusIn, + gEvent_FocusOut, + gEvent_Enter, + gEvent_Leave, + gEvent_DragMove, + gEvent_Drop +}; + +typedef + unsigned char uchar; + +#ifdef GTK3 + #define ON_DRAW_BEFORE(_widget, _this, _gtk, _gtk3) g_signal_connect(G_OBJECT(_widget), "draw", G_CALLBACK(_gtk3), (gpointer)_this) + #define ON_DRAW(_widget, _this, _gtk, _gtk3) g_signal_connect_after(G_OBJECT(_widget), "draw", G_CALLBACK(_gtk3), (gpointer)_this) +#else + #define ON_DRAW_BEFORE(_widget, _this, _gtk, _gtk3) g_signal_connect(G_OBJECT(_widget), "expose-event", G_CALLBACK(_gtk), (gpointer)_this) + #define ON_DRAW(_widget, _this, _gtk, _gtk3) g_signal_connect_after(G_OBJECT(_widget), "expose-event", G_CALLBACK(_gtk), (gpointer)_this) +#endif + +#ifdef GTK3 + +#define STATE_T GtkStateFlags +#define STYLE_T GtkStyleContext + +#define STATE_NORMAL GTK_STATE_FLAG_NORMAL +#define STATE_ACTIVE GTK_STATE_FLAG_ACTIVE +#define STATE_INSENSITIVE GTK_STATE_FLAG_INSENSITIVE +#define STATE_PRELIGHT GTK_STATE_FLAG_PRELIGHT +#define STATE_SELECTED GTK_STATE_FLAG_SELECTED +#define STATE_FOCUSED GTK_STATE_FLAG_FOCUSED + +#define gtk_hbox_new(_homogeneous, _spacing) gtk_box_new(GTK_ORIENTATION_HORIZONTAL, _spacing) +#define gtk_vbox_new(_homogeneous, _spacing) gtk_box_new(GTK_ORIENTATION_VERTICAL, _spacing) + +#define gtk_hscale_new(_adj) gtk_scale_new(GTK_ORIENTATION_HORIZONTAL, _adj) +#define gtk_vscale_new(_adj) gtk_scale_new(GTK_ORIENTATION_VERTICAL, _adj) + +#define gtk_hscrollbar_new(_adj) gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, _adj) +#define gtk_vscrollbar_new(_adj) gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, _adj) + +#define gtk_modify_font gtk_override_font + +#else + +#define STATE_T GtkStateType +#define STYLE_T GtkStyle + +#define STATE_NORMAL GTK_STATE_NORMAL +#define STATE_ACTIVE GTK_STATE_ACTIVE +#define STATE_INSENSITIVE GTK_STATE_INSENSITIVE +#define STATE_PRELIGHT GTK_STATE_PRELIGHT +#define STATE_SELECTED GTK_STATE_SELECTED + +#endif + +#endif diff --git a/gb.gtk/src/x11.c b/gb.gtk/src/x11.c new file mode 120000 index 00000000..944e55f3 --- /dev/null +++ b/gb.gtk/src/x11.c @@ -0,0 +1 @@ +../../gb.qt4/src/x11.c \ No newline at end of file diff --git a/gb.gtk/src/x11.h b/gb.gtk/src/x11.h new file mode 120000 index 00000000..479f5274 --- /dev/null +++ b/gb.gtk/src/x11.h @@ -0,0 +1 @@ +../../gb.qt4/src/x11.h \ No newline at end of file diff --git a/gb.gtk3/AUTHORS b/gb.gtk3/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.gtk3/COPYING b/gb.gtk3/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.gtk3/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.gtk3/ChangeLog b/gb.gtk3/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.gtk3/INSTALL b/gb.gtk3/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.gtk3/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.gtk3/Makefile.am b/gb.gtk3/Makefile.am new file mode 100644 index 00000000..533d6473 --- /dev/null +++ b/gb.gtk3/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @GTK3_DIR@ +EXTRA_DIST = reconf spec share gb*.h gambas.h diff --git a/gb.gtk3/NEWS b/gb.gtk3/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.gtk3/README b/gb.gtk3/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.gtk3/TODO b/gb.gtk3/TODO new file mode 100644 index 00000000..e69de29b diff --git a/gb.gtk3/acinclude.m4 b/gb.gtk3/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.gtk3/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.gtk3/component.am b/gb.gtk3/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.gtk3/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.gtk3/configure.ac b/gb.gtk3/configure.ac new file mode 100644 index 00000000..eee72d4b --- /dev/null +++ b/gb.gtk3/configure.ac @@ -0,0 +1,22 @@ +dnl ---- configure.ac for gb.gtk3 component + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-gtk3, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.gtk3) +AC_PROG_LIBTOOL + +GB_CHECK_XWINDOW() + +GB_COMPONENT_PKG_CONFIG( + gtk3, GTK3, gb.gtk3, [src], + 'gtk+-3.0 >= 3.4' 'librsvg-2.0 >= 2.14.3' 'cairo >= 1.6.0' 'cairo-ft >= 1.6.0' 'gtk+-unix-print-3.0 >= 3.4' sm ice x11 +) + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.gtk3/gambas.h b/gb.gtk3/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.gtk3/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.gtk3/gb.draw.h b/gb.gtk3/gb.draw.h new file mode 120000 index 00000000..82ba0778 --- /dev/null +++ b/gb.gtk3/gb.draw.h @@ -0,0 +1 @@ +../main/lib/draw/gb.draw.h \ No newline at end of file diff --git a/gb.gtk3/gb.geom.h b/gb.gtk3/gb.geom.h new file mode 120000 index 00000000..abc5659e --- /dev/null +++ b/gb.gtk3/gb.geom.h @@ -0,0 +1 @@ +../main/lib/geom/gb.geom.h \ No newline at end of file diff --git a/gb.gtk3/gb.gl.h b/gb.gtk3/gb.gl.h new file mode 120000 index 00000000..ff28a726 --- /dev/null +++ b/gb.gtk3/gb.gl.h @@ -0,0 +1 @@ +../gb.opengl/src/gb.gl.h \ No newline at end of file diff --git a/gb.gtk3/gb.image.h b/gb.gtk3/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.gtk3/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.gtk3/gb.paint.h b/gb.gtk3/gb.paint.h new file mode 120000 index 00000000..a516fe05 --- /dev/null +++ b/gb.gtk3/gb.paint.h @@ -0,0 +1 @@ +../main/lib/draw/gb.paint.h \ No newline at end of file diff --git a/gb.gtk3/gb_common.h b/gb.gtk3/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.gtk3/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.gtk3/m4 b/gb.gtk3/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.gtk3/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.gtk3/reconf b/gb.gtk3/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.gtk3/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.gtk3/share b/gb.gtk3/share new file mode 120000 index 00000000..638490a4 --- /dev/null +++ b/gb.gtk3/share @@ -0,0 +1 @@ +../gb.qt4/share \ No newline at end of file diff --git a/gb.gtk3/src/CButton.cpp b/gb.gtk3/src/CButton.cpp new file mode 120000 index 00000000..b206d256 --- /dev/null +++ b/gb.gtk3/src/CButton.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CButton.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CButton.h b/gb.gtk3/src/CButton.h new file mode 120000 index 00000000..6a9cc03e --- /dev/null +++ b/gb.gtk3/src/CButton.h @@ -0,0 +1 @@ +../../gb.gtk/src/CButton.h \ No newline at end of file diff --git a/gb.gtk3/src/CClipboard.cpp b/gb.gtk3/src/CClipboard.cpp new file mode 120000 index 00000000..6d673158 --- /dev/null +++ b/gb.gtk3/src/CClipboard.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CClipboard.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CClipboard.h b/gb.gtk3/src/CClipboard.h new file mode 120000 index 00000000..f2d94a27 --- /dev/null +++ b/gb.gtk3/src/CClipboard.h @@ -0,0 +1 @@ +../../gb.gtk/src/CClipboard.h \ No newline at end of file diff --git a/gb.gtk3/src/CColor.cpp b/gb.gtk3/src/CColor.cpp new file mode 120000 index 00000000..efb6578f --- /dev/null +++ b/gb.gtk3/src/CColor.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CColor.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CColor.h b/gb.gtk3/src/CColor.h new file mode 120000 index 00000000..6eabe04b --- /dev/null +++ b/gb.gtk3/src/CColor.h @@ -0,0 +1 @@ +../../gb.gtk/src/CColor.h \ No newline at end of file diff --git a/gb.gtk3/src/CConst.cpp b/gb.gtk3/src/CConst.cpp new file mode 120000 index 00000000..2f9cf1cd --- /dev/null +++ b/gb.gtk3/src/CConst.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CConst.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CConst.h b/gb.gtk3/src/CConst.h new file mode 120000 index 00000000..797d0bec --- /dev/null +++ b/gb.gtk3/src/CConst.h @@ -0,0 +1 @@ +../../gb.gtk/src/CConst.h \ No newline at end of file diff --git a/gb.gtk3/src/CContainer.cpp b/gb.gtk3/src/CContainer.cpp new file mode 120000 index 00000000..ee69dfdd --- /dev/null +++ b/gb.gtk3/src/CContainer.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CContainer.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CContainer.h b/gb.gtk3/src/CContainer.h new file mode 120000 index 00000000..db213757 --- /dev/null +++ b/gb.gtk3/src/CContainer.h @@ -0,0 +1 @@ +../../gb.gtk/src/CContainer.h \ No newline at end of file diff --git a/gb.gtk3/src/CDialog.cpp b/gb.gtk3/src/CDialog.cpp new file mode 120000 index 00000000..14d97758 --- /dev/null +++ b/gb.gtk3/src/CDialog.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CDialog.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CDialog.h b/gb.gtk3/src/CDialog.h new file mode 120000 index 00000000..f63dd4ba --- /dev/null +++ b/gb.gtk3/src/CDialog.h @@ -0,0 +1 @@ +../../gb.gtk/src/CDialog.h \ No newline at end of file diff --git a/gb.gtk3/src/CDraw.cpp b/gb.gtk3/src/CDraw.cpp new file mode 120000 index 00000000..e31bbfb6 --- /dev/null +++ b/gb.gtk3/src/CDraw.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CDraw.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CDraw.h b/gb.gtk3/src/CDraw.h new file mode 120000 index 00000000..b3dade9d --- /dev/null +++ b/gb.gtk3/src/CDraw.h @@ -0,0 +1 @@ +../../gb.gtk/src/CDraw.h \ No newline at end of file diff --git a/gb.gtk3/src/CDrawingArea.cpp b/gb.gtk3/src/CDrawingArea.cpp new file mode 120000 index 00000000..b354fe6f --- /dev/null +++ b/gb.gtk3/src/CDrawingArea.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CDrawingArea.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CDrawingArea.h b/gb.gtk3/src/CDrawingArea.h new file mode 120000 index 00000000..04c3d7b4 --- /dev/null +++ b/gb.gtk3/src/CDrawingArea.h @@ -0,0 +1 @@ +../../gb.gtk/src/CDrawingArea.h \ No newline at end of file diff --git a/gb.gtk3/src/CFont.cpp b/gb.gtk3/src/CFont.cpp new file mode 120000 index 00000000..36deecbc --- /dev/null +++ b/gb.gtk3/src/CFont.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CFont.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CFont.h b/gb.gtk3/src/CFont.h new file mode 120000 index 00000000..bb6359de --- /dev/null +++ b/gb.gtk3/src/CFont.h @@ -0,0 +1 @@ +../../gb.gtk/src/CFont.h \ No newline at end of file diff --git a/gb.gtk3/src/CFrame.cpp b/gb.gtk3/src/CFrame.cpp new file mode 120000 index 00000000..e58739ae --- /dev/null +++ b/gb.gtk3/src/CFrame.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CFrame.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CFrame.h b/gb.gtk3/src/CFrame.h new file mode 120000 index 00000000..5962aff7 --- /dev/null +++ b/gb.gtk3/src/CFrame.h @@ -0,0 +1 @@ +../../gb.gtk/src/CFrame.h \ No newline at end of file diff --git a/gb.gtk3/src/CImage.cpp b/gb.gtk3/src/CImage.cpp new file mode 120000 index 00000000..7ba515a4 --- /dev/null +++ b/gb.gtk3/src/CImage.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CImage.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CImage.h b/gb.gtk3/src/CImage.h new file mode 120000 index 00000000..890fb998 --- /dev/null +++ b/gb.gtk3/src/CImage.h @@ -0,0 +1 @@ +../../gb.gtk/src/CImage.h \ No newline at end of file diff --git a/gb.gtk3/src/CKey.cpp b/gb.gtk3/src/CKey.cpp new file mode 120000 index 00000000..abbe6451 --- /dev/null +++ b/gb.gtk3/src/CKey.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CKey.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CKey.h b/gb.gtk3/src/CKey.h new file mode 120000 index 00000000..492bec84 --- /dev/null +++ b/gb.gtk3/src/CKey.h @@ -0,0 +1 @@ +../../gb.gtk/src/CKey.h \ No newline at end of file diff --git a/gb.gtk3/src/CLabel.cpp b/gb.gtk3/src/CLabel.cpp new file mode 120000 index 00000000..f83802f2 --- /dev/null +++ b/gb.gtk3/src/CLabel.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CLabel.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CLabel.h b/gb.gtk3/src/CLabel.h new file mode 120000 index 00000000..6cfcec71 --- /dev/null +++ b/gb.gtk3/src/CLabel.h @@ -0,0 +1 @@ +../../gb.gtk/src/CLabel.h \ No newline at end of file diff --git a/gb.gtk3/src/CMenu.cpp b/gb.gtk3/src/CMenu.cpp new file mode 120000 index 00000000..0c697bb8 --- /dev/null +++ b/gb.gtk3/src/CMenu.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CMenu.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CMenu.h b/gb.gtk3/src/CMenu.h new file mode 120000 index 00000000..4e66e412 --- /dev/null +++ b/gb.gtk3/src/CMenu.h @@ -0,0 +1 @@ +../../gb.gtk/src/CMenu.h \ No newline at end of file diff --git a/gb.gtk3/src/CMessage.cpp b/gb.gtk3/src/CMessage.cpp new file mode 120000 index 00000000..a22d526a --- /dev/null +++ b/gb.gtk3/src/CMessage.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CMessage.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CMessage.h b/gb.gtk3/src/CMessage.h new file mode 120000 index 00000000..5bf94ca7 --- /dev/null +++ b/gb.gtk3/src/CMessage.h @@ -0,0 +1 @@ +../../gb.gtk/src/CMessage.h \ No newline at end of file diff --git a/gb.gtk3/src/CMouse.cpp b/gb.gtk3/src/CMouse.cpp new file mode 120000 index 00000000..6ac84fa9 --- /dev/null +++ b/gb.gtk3/src/CMouse.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CMouse.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CMouse.h b/gb.gtk3/src/CMouse.h new file mode 120000 index 00000000..131b313c --- /dev/null +++ b/gb.gtk3/src/CMouse.h @@ -0,0 +1 @@ +../../gb.gtk/src/CMouse.h \ No newline at end of file diff --git a/gb.gtk3/src/CMovieBox.cpp b/gb.gtk3/src/CMovieBox.cpp new file mode 120000 index 00000000..72a394d3 --- /dev/null +++ b/gb.gtk3/src/CMovieBox.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CMovieBox.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CMovieBox.h b/gb.gtk3/src/CMovieBox.h new file mode 120000 index 00000000..e1c2b139 --- /dev/null +++ b/gb.gtk3/src/CMovieBox.h @@ -0,0 +1 @@ +../../gb.gtk/src/CMovieBox.h \ No newline at end of file diff --git a/gb.gtk3/src/CPicture.cpp b/gb.gtk3/src/CPicture.cpp new file mode 120000 index 00000000..39a951ac --- /dev/null +++ b/gb.gtk3/src/CPicture.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CPicture.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CPicture.h b/gb.gtk3/src/CPicture.h new file mode 120000 index 00000000..ff13a6c2 --- /dev/null +++ b/gb.gtk3/src/CPicture.h @@ -0,0 +1 @@ +../../gb.gtk/src/CPicture.h \ No newline at end of file diff --git a/gb.gtk3/src/CScreen.cpp b/gb.gtk3/src/CScreen.cpp new file mode 120000 index 00000000..1efce7fd --- /dev/null +++ b/gb.gtk3/src/CScreen.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CScreen.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CScreen.h b/gb.gtk3/src/CScreen.h new file mode 120000 index 00000000..914350a8 --- /dev/null +++ b/gb.gtk3/src/CScreen.h @@ -0,0 +1 @@ +../../gb.gtk/src/CScreen.h \ No newline at end of file diff --git a/gb.gtk3/src/CSeparator.cpp b/gb.gtk3/src/CSeparator.cpp new file mode 120000 index 00000000..72ffe56a --- /dev/null +++ b/gb.gtk3/src/CSeparator.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CSeparator.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CSeparator.h b/gb.gtk3/src/CSeparator.h new file mode 120000 index 00000000..b77da00b --- /dev/null +++ b/gb.gtk3/src/CSeparator.h @@ -0,0 +1 @@ +../../gb.gtk/src/CSeparator.h \ No newline at end of file diff --git a/gb.gtk3/src/CSlider.cpp b/gb.gtk3/src/CSlider.cpp new file mode 120000 index 00000000..4e35d1d7 --- /dev/null +++ b/gb.gtk3/src/CSlider.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CSlider.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CSlider.h b/gb.gtk3/src/CSlider.h new file mode 120000 index 00000000..2e44081e --- /dev/null +++ b/gb.gtk3/src/CSlider.h @@ -0,0 +1 @@ +../../gb.gtk/src/CSlider.h \ No newline at end of file diff --git a/gb.gtk3/src/CStock.cpp b/gb.gtk3/src/CStock.cpp new file mode 120000 index 00000000..ef971f2e --- /dev/null +++ b/gb.gtk3/src/CStock.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CStock.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CStock.h b/gb.gtk3/src/CStock.h new file mode 120000 index 00000000..4b288f66 --- /dev/null +++ b/gb.gtk3/src/CStock.h @@ -0,0 +1 @@ +../../gb.gtk/src/CStock.h \ No newline at end of file diff --git a/gb.gtk3/src/CStyle.cpp b/gb.gtk3/src/CStyle.cpp new file mode 120000 index 00000000..7066f397 --- /dev/null +++ b/gb.gtk3/src/CStyle.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CStyle.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CStyle.h b/gb.gtk3/src/CStyle.h new file mode 120000 index 00000000..9b3c5c1b --- /dev/null +++ b/gb.gtk3/src/CStyle.h @@ -0,0 +1 @@ +../../gb.gtk/src/CStyle.h \ No newline at end of file diff --git a/gb.gtk3/src/CTabStrip.cpp b/gb.gtk3/src/CTabStrip.cpp new file mode 120000 index 00000000..c841c6d4 --- /dev/null +++ b/gb.gtk3/src/CTabStrip.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CTabStrip.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CTabStrip.h b/gb.gtk3/src/CTabStrip.h new file mode 120000 index 00000000..309e53e3 --- /dev/null +++ b/gb.gtk3/src/CTabStrip.h @@ -0,0 +1 @@ +../../gb.gtk/src/CTabStrip.h \ No newline at end of file diff --git a/gb.gtk3/src/CTextArea.cpp b/gb.gtk3/src/CTextArea.cpp new file mode 120000 index 00000000..c0c19b03 --- /dev/null +++ b/gb.gtk3/src/CTextArea.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CTextArea.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CTextArea.h b/gb.gtk3/src/CTextArea.h new file mode 120000 index 00000000..6fa7b022 --- /dev/null +++ b/gb.gtk3/src/CTextArea.h @@ -0,0 +1 @@ +../../gb.gtk/src/CTextArea.h \ No newline at end of file diff --git a/gb.gtk3/src/CTextBox.cpp b/gb.gtk3/src/CTextBox.cpp new file mode 120000 index 00000000..a0243b8c --- /dev/null +++ b/gb.gtk3/src/CTextBox.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CTextBox.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CTextBox.h b/gb.gtk3/src/CTextBox.h new file mode 120000 index 00000000..7f2661ed --- /dev/null +++ b/gb.gtk3/src/CTextBox.h @@ -0,0 +1 @@ +../../gb.gtk/src/CTextBox.h \ No newline at end of file diff --git a/gb.gtk3/src/CTrayIcon.cpp b/gb.gtk3/src/CTrayIcon.cpp new file mode 120000 index 00000000..1a52f108 --- /dev/null +++ b/gb.gtk3/src/CTrayIcon.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CTrayIcon.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CTrayIcon.h b/gb.gtk3/src/CTrayIcon.h new file mode 120000 index 00000000..a09627d4 --- /dev/null +++ b/gb.gtk3/src/CTrayIcon.h @@ -0,0 +1 @@ +../../gb.gtk/src/CTrayIcon.h \ No newline at end of file diff --git a/gb.gtk3/src/CWatcher.cpp b/gb.gtk3/src/CWatcher.cpp new file mode 120000 index 00000000..3b2d66c0 --- /dev/null +++ b/gb.gtk3/src/CWatcher.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CWatcher.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CWatcher.h b/gb.gtk3/src/CWatcher.h new file mode 120000 index 00000000..677919ef --- /dev/null +++ b/gb.gtk3/src/CWatcher.h @@ -0,0 +1 @@ +../../gb.gtk/src/CWatcher.h \ No newline at end of file diff --git a/gb.gtk3/src/CWidget.cpp b/gb.gtk3/src/CWidget.cpp new file mode 120000 index 00000000..5c61624d --- /dev/null +++ b/gb.gtk3/src/CWidget.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CWidget.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CWidget.h b/gb.gtk3/src/CWidget.h new file mode 120000 index 00000000..94f1cfa6 --- /dev/null +++ b/gb.gtk3/src/CWidget.h @@ -0,0 +1 @@ +../../gb.gtk/src/CWidget.h \ No newline at end of file diff --git a/gb.gtk3/src/CWindow.cpp b/gb.gtk3/src/CWindow.cpp new file mode 120000 index 00000000..a7a11bbd --- /dev/null +++ b/gb.gtk3/src/CWindow.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/CWindow.cpp \ No newline at end of file diff --git a/gb.gtk3/src/CWindow.h b/gb.gtk3/src/CWindow.h new file mode 120000 index 00000000..b47fa6cb --- /dev/null +++ b/gb.gtk3/src/CWindow.h @@ -0,0 +1 @@ +../../gb.gtk/src/CWindow.h \ No newline at end of file diff --git a/gb.gtk3/src/Makefile.am b/gb.gtk3/src/Makefile.am new file mode 100644 index 00000000..39457f4a --- /dev/null +++ b/gb.gtk3/src/Makefile.am @@ -0,0 +1,101 @@ +COMPONENT = gb.gtk3 +include $(top_srcdir)/component.am + +##SUBDIRS = . @GTKOPENGL_DIR@ + +gblib_LTLIBRARIES = gb.gtk3.la + +gb_gtk3_la_LIBADD = @GTK3_LIB@ +gb_gtk3_la_LDFLAGS = -module @LD_FLAGS@ @GTK3_LDFLAGS@ +gb_gtk3_la_CPPFLAGS = @GTK3_INC@ -I$(top_srcdir)/share -DGTK3 -DGTK_DISABLE_SINGLE_INCLUDES -DGSEAL_ENABLE + +gb_gtk3_la_SOURCES = \ + gtag.h ggambastag.h gshare.h \ + gb.gtk.h widgets.h font-parser.h font-parser.cpp \ + gtools.cpp gcolor.h \ + gfont.cpp gpicture.cpp \ + gdesktop.cpp gmessage.cpp \ + gcontrol.cpp gcontainer.cpp gseparator.cpp \ + gbutton.cpp gslider.cpp glabel.cpp gsignals.cpp \ + gmoviebox.cpp gcombobox.cpp gtextbox.cpp gtextarea.cpp \ + gframe.cpp gtabstrip.cpp \ + gmenu.cpp gtrayicon.cpp gmainwindow.cpp \ + gtree.h gtree.cpp \ + watcher.h watcher.cpp \ + CStock.cpp CStock.h \ + CConst.h CConst.cpp CColor.h CColor.cpp \ + CFont.h CFont.cpp \ + CKey.h CKey.cpp \ + CScreen.h CScreen.cpp \ + CStyle.h CStyle.cpp \ + CMessage.h CMessage.cpp CDialog.h CDialog.cpp \ + CDraw.h CDraw.cpp \ + cpaint_impl.h cpaint_impl.cpp \ + CImage.h CImage.cpp \ + CPicture.h CPicture.cpp \ + CClipboard.h CClipboard.cpp \ + CMouse.h CMouse.cpp \ + CWatcher.h CWatcher.cpp \ + CWidget.h CWidget.cpp CContainer.h CContainer.cpp \ + CSeparator.h CSeparator.cpp \ + CDrawingArea.h CDrawingArea.cpp \ + CLabel.h CLabel.cpp \ + CSlider.h CSlider.cpp \ + CButton.h CButton.cpp \ + CMovieBox.h CMovieBox.cpp \ + CTextBox.h CTextBox.cpp \ + CTextArea.h CTextArea.cpp \ + CFrame.h CFrame.cpp \ + CTabStrip.h CTabStrip.cpp \ + CMenu.h CMenu.cpp CTrayIcon.h CTrayIcon.cpp CWindow.h CWindow.cpp \ + cprinter.h cprinter.cpp \ + csvgimage.h csvgimage.cpp \ + main.h main.cpp \ + gkey.h gkey.cpp \ + gcursor.h gcursor.cpp \ + gmouse.h gmouse.cpp \ + gdesktop.h \ + gpicture.h \ + gfont.h \ + gmessage.h \ + gdialog.h \ + gcontrol.h \ + gseparator.h \ + gtrayicon.h \ + gplugin.h \ + glabel.h \ + gbutton.h \ + gmoviebox.h \ + gcombobox.h \ + gtextbox.h \ + gtextarea.h \ + gslider.h \ + gscrollbar.h \ + gcontainer.h \ + gdrawingarea.h gdrawingarea.cpp \ + gtabstrip.h \ + gframe.h \ + gmenu.h \ + gmainwindow.h \ + gapplication.h gapplication.cpp \ + gclipboard.h \ + gdrag.h gdrag.cpp \ + gtools.h kentities.h \ + gprinter.h gprinter.cpp \ + gglarea.h gglarea.cpp \ + x11.h x11.c \ + desktop.h desktop.c \ + sm/bonobo-macros.h \ + sm/gnome-macros.h \ + sm/gnome-uidefs.h \ + sm/libgnomeui.h \ + sm/libgnomeuiP.h \ + sm/gnome-client.h \ + sm/gnome-client.c \ + sm/gnome-ice.h \ + sm/gnome-ice.c \ + sm/gnome-marshal.h \ + sm/gnome-marshal.c \ + sm/gnometypebuiltins.h \ + sm/gnometypebuiltins.c \ + sm/sm.h diff --git a/gb.gtk3/src/cpaint_impl.cpp b/gb.gtk3/src/cpaint_impl.cpp new file mode 120000 index 00000000..3a30576c --- /dev/null +++ b/gb.gtk3/src/cpaint_impl.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/cpaint_impl.cpp \ No newline at end of file diff --git a/gb.gtk3/src/cpaint_impl.h b/gb.gtk3/src/cpaint_impl.h new file mode 120000 index 00000000..7bce6656 --- /dev/null +++ b/gb.gtk3/src/cpaint_impl.h @@ -0,0 +1 @@ +../../gb.gtk/src/cpaint_impl.h \ No newline at end of file diff --git a/gb.gtk3/src/cprinter.cpp b/gb.gtk3/src/cprinter.cpp new file mode 120000 index 00000000..a0b7e15e --- /dev/null +++ b/gb.gtk3/src/cprinter.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/cprinter.cpp \ No newline at end of file diff --git a/gb.gtk3/src/cprinter.h b/gb.gtk3/src/cprinter.h new file mode 120000 index 00000000..bf4ec774 --- /dev/null +++ b/gb.gtk3/src/cprinter.h @@ -0,0 +1 @@ +../../gb.gtk/src/cprinter.h \ No newline at end of file diff --git a/gb.gtk3/src/csvgimage.cpp b/gb.gtk3/src/csvgimage.cpp new file mode 120000 index 00000000..ff4c044c --- /dev/null +++ b/gb.gtk3/src/csvgimage.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/csvgimage.cpp \ No newline at end of file diff --git a/gb.gtk3/src/csvgimage.h b/gb.gtk3/src/csvgimage.h new file mode 120000 index 00000000..f4e94823 --- /dev/null +++ b/gb.gtk3/src/csvgimage.h @@ -0,0 +1 @@ +../../gb.gtk/src/csvgimage.h \ No newline at end of file diff --git a/gb.gtk3/src/desktop.c b/gb.gtk3/src/desktop.c new file mode 120000 index 00000000..d990cd1d --- /dev/null +++ b/gb.gtk3/src/desktop.c @@ -0,0 +1 @@ +../../gb.gtk/src/desktop.c \ No newline at end of file diff --git a/gb.gtk3/src/desktop.h b/gb.gtk3/src/desktop.h new file mode 120000 index 00000000..3274ce80 --- /dev/null +++ b/gb.gtk3/src/desktop.h @@ -0,0 +1 @@ +../../gb.gtk/src/desktop.h \ No newline at end of file diff --git a/gb.gtk3/src/font-parser.cpp b/gb.gtk3/src/font-parser.cpp new file mode 120000 index 00000000..8cb01289 --- /dev/null +++ b/gb.gtk3/src/font-parser.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/font-parser.cpp \ No newline at end of file diff --git a/gb.gtk3/src/font-parser.h b/gb.gtk3/src/font-parser.h new file mode 120000 index 00000000..716f4340 --- /dev/null +++ b/gb.gtk3/src/font-parser.h @@ -0,0 +1 @@ +../../gb.gtk/src/font-parser.h \ No newline at end of file diff --git a/gb.gtk3/src/gapplication.cpp b/gb.gtk3/src/gapplication.cpp new file mode 120000 index 00000000..da5ab25a --- /dev/null +++ b/gb.gtk3/src/gapplication.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gapplication.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gapplication.h b/gb.gtk3/src/gapplication.h new file mode 120000 index 00000000..cd414365 --- /dev/null +++ b/gb.gtk3/src/gapplication.h @@ -0,0 +1 @@ +../../gb.gtk/src/gapplication.h \ No newline at end of file diff --git a/gb.gtk3/src/gb.gtk.h b/gb.gtk3/src/gb.gtk.h new file mode 120000 index 00000000..d19b9b97 --- /dev/null +++ b/gb.gtk3/src/gb.gtk.h @@ -0,0 +1 @@ +../../gb.gtk/src/gb.gtk.h \ No newline at end of file diff --git a/gb.gtk3/src/gb.gtk3.component b/gb.gtk3/src/gb.gtk3.component new file mode 100644 index 00000000..02ef83b5 --- /dev/null +++ b/gb.gtk3/src/gb.gtk3.component @@ -0,0 +1,7 @@ +[Component] +Key=gb.gtk3 +Author=Benoît Minisini +Implements=Form,EventLoop,ImageIO +Requires=gb.image +Type=Form +State=NotFinished diff --git a/gb.gtk3/src/gbutton.cpp b/gb.gtk3/src/gbutton.cpp new file mode 120000 index 00000000..732700e5 --- /dev/null +++ b/gb.gtk3/src/gbutton.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gbutton.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gbutton.h b/gb.gtk3/src/gbutton.h new file mode 120000 index 00000000..0693b18f --- /dev/null +++ b/gb.gtk3/src/gbutton.h @@ -0,0 +1 @@ +../../gb.gtk/src/gbutton.h \ No newline at end of file diff --git a/gb.gtk3/src/gclipboard.h b/gb.gtk3/src/gclipboard.h new file mode 120000 index 00000000..f4034bb6 --- /dev/null +++ b/gb.gtk3/src/gclipboard.h @@ -0,0 +1 @@ +../../gb.gtk/src/gclipboard.h \ No newline at end of file diff --git a/gb.gtk3/src/gcolor.h b/gb.gtk3/src/gcolor.h new file mode 120000 index 00000000..93b7741c --- /dev/null +++ b/gb.gtk3/src/gcolor.h @@ -0,0 +1 @@ +../../gb.gtk/src/gcolor.h \ No newline at end of file diff --git a/gb.gtk3/src/gcombobox.cpp b/gb.gtk3/src/gcombobox.cpp new file mode 120000 index 00000000..508ff90d --- /dev/null +++ b/gb.gtk3/src/gcombobox.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gcombobox.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gcombobox.h b/gb.gtk3/src/gcombobox.h new file mode 120000 index 00000000..bdb85087 --- /dev/null +++ b/gb.gtk3/src/gcombobox.h @@ -0,0 +1 @@ +../../gb.gtk/src/gcombobox.h \ No newline at end of file diff --git a/gb.gtk3/src/gcontainer.cpp b/gb.gtk3/src/gcontainer.cpp new file mode 120000 index 00000000..2dc004fd --- /dev/null +++ b/gb.gtk3/src/gcontainer.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gcontainer.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gcontainer.h b/gb.gtk3/src/gcontainer.h new file mode 120000 index 00000000..b6a4be6f --- /dev/null +++ b/gb.gtk3/src/gcontainer.h @@ -0,0 +1 @@ +../../gb.gtk/src/gcontainer.h \ No newline at end of file diff --git a/gb.gtk3/src/gcontrol.cpp b/gb.gtk3/src/gcontrol.cpp new file mode 120000 index 00000000..978b4ee6 --- /dev/null +++ b/gb.gtk3/src/gcontrol.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gcontrol.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gcontrol.h b/gb.gtk3/src/gcontrol.h new file mode 120000 index 00000000..20d8af74 --- /dev/null +++ b/gb.gtk3/src/gcontrol.h @@ -0,0 +1 @@ +../../gb.gtk/src/gcontrol.h \ No newline at end of file diff --git a/gb.gtk3/src/gcursor.cpp b/gb.gtk3/src/gcursor.cpp new file mode 120000 index 00000000..113d89bb --- /dev/null +++ b/gb.gtk3/src/gcursor.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gcursor.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gcursor.h b/gb.gtk3/src/gcursor.h new file mode 120000 index 00000000..66be2fe3 --- /dev/null +++ b/gb.gtk3/src/gcursor.h @@ -0,0 +1 @@ +../../gb.gtk/src/gcursor.h \ No newline at end of file diff --git a/gb.gtk3/src/gdesktop.cpp b/gb.gtk3/src/gdesktop.cpp new file mode 120000 index 00000000..a41bce43 --- /dev/null +++ b/gb.gtk3/src/gdesktop.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gdesktop.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gdesktop.h b/gb.gtk3/src/gdesktop.h new file mode 120000 index 00000000..f8410ca3 --- /dev/null +++ b/gb.gtk3/src/gdesktop.h @@ -0,0 +1 @@ +../../gb.gtk/src/gdesktop.h \ No newline at end of file diff --git a/gb.gtk3/src/gdialog.h b/gb.gtk3/src/gdialog.h new file mode 120000 index 00000000..a91068eb --- /dev/null +++ b/gb.gtk3/src/gdialog.h @@ -0,0 +1 @@ +../../gb.gtk/src/gdialog.h \ No newline at end of file diff --git a/gb.gtk3/src/gdrag.cpp b/gb.gtk3/src/gdrag.cpp new file mode 120000 index 00000000..a120b2b3 --- /dev/null +++ b/gb.gtk3/src/gdrag.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gdrag.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gdrag.h b/gb.gtk3/src/gdrag.h new file mode 120000 index 00000000..c1eee5d7 --- /dev/null +++ b/gb.gtk3/src/gdrag.h @@ -0,0 +1 @@ +../../gb.gtk/src/gdrag.h \ No newline at end of file diff --git a/gb.gtk3/src/gdrawingarea.cpp b/gb.gtk3/src/gdrawingarea.cpp new file mode 120000 index 00000000..742990cd --- /dev/null +++ b/gb.gtk3/src/gdrawingarea.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gdrawingarea.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gdrawingarea.h b/gb.gtk3/src/gdrawingarea.h new file mode 120000 index 00000000..f3eb6d39 --- /dev/null +++ b/gb.gtk3/src/gdrawingarea.h @@ -0,0 +1 @@ +../../gb.gtk/src/gdrawingarea.h \ No newline at end of file diff --git a/gb.gtk3/src/gfont.cpp b/gb.gtk3/src/gfont.cpp new file mode 120000 index 00000000..3c8c7ad1 --- /dev/null +++ b/gb.gtk3/src/gfont.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gfont.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gfont.h b/gb.gtk3/src/gfont.h new file mode 120000 index 00000000..0dccf9d3 --- /dev/null +++ b/gb.gtk3/src/gfont.h @@ -0,0 +1 @@ +../../gb.gtk/src/gfont.h \ No newline at end of file diff --git a/gb.gtk3/src/gframe.cpp b/gb.gtk3/src/gframe.cpp new file mode 120000 index 00000000..1cd67251 --- /dev/null +++ b/gb.gtk3/src/gframe.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gframe.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gframe.h b/gb.gtk3/src/gframe.h new file mode 120000 index 00000000..41628ee0 --- /dev/null +++ b/gb.gtk3/src/gframe.h @@ -0,0 +1 @@ +../../gb.gtk/src/gframe.h \ No newline at end of file diff --git a/gb.gtk3/src/ggambastag.h b/gb.gtk3/src/ggambastag.h new file mode 120000 index 00000000..7fb73da3 --- /dev/null +++ b/gb.gtk3/src/ggambastag.h @@ -0,0 +1 @@ +../../gb.gtk/src/ggambastag.h \ No newline at end of file diff --git a/gb.gtk3/src/gglarea.cpp b/gb.gtk3/src/gglarea.cpp new file mode 120000 index 00000000..d8935b87 --- /dev/null +++ b/gb.gtk3/src/gglarea.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gglarea.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gglarea.h b/gb.gtk3/src/gglarea.h new file mode 120000 index 00000000..84b0fa59 --- /dev/null +++ b/gb.gtk3/src/gglarea.h @@ -0,0 +1 @@ +../../gb.gtk/src/gglarea.h \ No newline at end of file diff --git a/gb.gtk3/src/gkey.cpp b/gb.gtk3/src/gkey.cpp new file mode 120000 index 00000000..dd925606 --- /dev/null +++ b/gb.gtk3/src/gkey.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gkey.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gkey.h b/gb.gtk3/src/gkey.h new file mode 120000 index 00000000..714b68eb --- /dev/null +++ b/gb.gtk3/src/gkey.h @@ -0,0 +1 @@ +../../gb.gtk/src/gkey.h \ No newline at end of file diff --git a/gb.gtk3/src/glabel.cpp b/gb.gtk3/src/glabel.cpp new file mode 120000 index 00000000..499ffbf9 --- /dev/null +++ b/gb.gtk3/src/glabel.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/glabel.cpp \ No newline at end of file diff --git a/gb.gtk3/src/glabel.h b/gb.gtk3/src/glabel.h new file mode 120000 index 00000000..ffd077fd --- /dev/null +++ b/gb.gtk3/src/glabel.h @@ -0,0 +1 @@ +../../gb.gtk/src/glabel.h \ No newline at end of file diff --git a/gb.gtk3/src/gmainwindow.cpp b/gb.gtk3/src/gmainwindow.cpp new file mode 120000 index 00000000..65dab4d5 --- /dev/null +++ b/gb.gtk3/src/gmainwindow.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gmainwindow.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gmainwindow.h b/gb.gtk3/src/gmainwindow.h new file mode 120000 index 00000000..1b869bdf --- /dev/null +++ b/gb.gtk3/src/gmainwindow.h @@ -0,0 +1 @@ +../../gb.gtk/src/gmainwindow.h \ No newline at end of file diff --git a/gb.gtk3/src/gmenu.cpp b/gb.gtk3/src/gmenu.cpp new file mode 120000 index 00000000..c9162903 --- /dev/null +++ b/gb.gtk3/src/gmenu.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gmenu.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gmenu.h b/gb.gtk3/src/gmenu.h new file mode 120000 index 00000000..2f3122a7 --- /dev/null +++ b/gb.gtk3/src/gmenu.h @@ -0,0 +1 @@ +../../gb.gtk/src/gmenu.h \ No newline at end of file diff --git a/gb.gtk3/src/gmessage.cpp b/gb.gtk3/src/gmessage.cpp new file mode 120000 index 00000000..eb130c39 --- /dev/null +++ b/gb.gtk3/src/gmessage.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gmessage.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gmessage.h b/gb.gtk3/src/gmessage.h new file mode 120000 index 00000000..552349bb --- /dev/null +++ b/gb.gtk3/src/gmessage.h @@ -0,0 +1 @@ +../../gb.gtk/src/gmessage.h \ No newline at end of file diff --git a/gb.gtk3/src/gmouse.cpp b/gb.gtk3/src/gmouse.cpp new file mode 120000 index 00000000..36c37b97 --- /dev/null +++ b/gb.gtk3/src/gmouse.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gmouse.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gmouse.h b/gb.gtk3/src/gmouse.h new file mode 120000 index 00000000..e7335241 --- /dev/null +++ b/gb.gtk3/src/gmouse.h @@ -0,0 +1 @@ +../../gb.gtk/src/gmouse.h \ No newline at end of file diff --git a/gb.gtk3/src/gmoviebox.cpp b/gb.gtk3/src/gmoviebox.cpp new file mode 120000 index 00000000..1512be26 --- /dev/null +++ b/gb.gtk3/src/gmoviebox.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gmoviebox.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gmoviebox.h b/gb.gtk3/src/gmoviebox.h new file mode 120000 index 00000000..34b9e079 --- /dev/null +++ b/gb.gtk3/src/gmoviebox.h @@ -0,0 +1 @@ +../../gb.gtk/src/gmoviebox.h \ No newline at end of file diff --git a/gb.gtk3/src/gpicture.cpp b/gb.gtk3/src/gpicture.cpp new file mode 120000 index 00000000..720e5134 --- /dev/null +++ b/gb.gtk3/src/gpicture.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gpicture.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gpicture.h b/gb.gtk3/src/gpicture.h new file mode 120000 index 00000000..97ddb1a0 --- /dev/null +++ b/gb.gtk3/src/gpicture.h @@ -0,0 +1 @@ +../../gb.gtk/src/gpicture.h \ No newline at end of file diff --git a/gb.gtk3/src/gplugin.h b/gb.gtk3/src/gplugin.h new file mode 120000 index 00000000..55f85bca --- /dev/null +++ b/gb.gtk3/src/gplugin.h @@ -0,0 +1 @@ +../../gb.gtk/src/gplugin.h \ No newline at end of file diff --git a/gb.gtk3/src/gprinter.cpp b/gb.gtk3/src/gprinter.cpp new file mode 120000 index 00000000..2fd4ba7d --- /dev/null +++ b/gb.gtk3/src/gprinter.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gprinter.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gprinter.h b/gb.gtk3/src/gprinter.h new file mode 120000 index 00000000..b44ee706 --- /dev/null +++ b/gb.gtk3/src/gprinter.h @@ -0,0 +1 @@ +../../gb.gtk/src/gprinter.h \ No newline at end of file diff --git a/gb.gtk3/src/gscrollbar.h b/gb.gtk3/src/gscrollbar.h new file mode 120000 index 00000000..bc71add4 --- /dev/null +++ b/gb.gtk3/src/gscrollbar.h @@ -0,0 +1 @@ +../../gb.gtk/src/gscrollbar.h \ No newline at end of file diff --git a/gb.gtk3/src/gseparator.cpp b/gb.gtk3/src/gseparator.cpp new file mode 120000 index 00000000..6cc95e37 --- /dev/null +++ b/gb.gtk3/src/gseparator.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gseparator.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gseparator.h b/gb.gtk3/src/gseparator.h new file mode 120000 index 00000000..256cd16d --- /dev/null +++ b/gb.gtk3/src/gseparator.h @@ -0,0 +1 @@ +../../gb.gtk/src/gseparator.h \ No newline at end of file diff --git a/gb.gtk3/src/gshare.h b/gb.gtk3/src/gshare.h new file mode 120000 index 00000000..d4cfea20 --- /dev/null +++ b/gb.gtk3/src/gshare.h @@ -0,0 +1 @@ +../../gb.gtk/src/gshare.h \ No newline at end of file diff --git a/gb.gtk3/src/gsignals.cpp b/gb.gtk3/src/gsignals.cpp new file mode 120000 index 00000000..65ebd3db --- /dev/null +++ b/gb.gtk3/src/gsignals.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gsignals.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gslider.cpp b/gb.gtk3/src/gslider.cpp new file mode 120000 index 00000000..2c102be2 --- /dev/null +++ b/gb.gtk3/src/gslider.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gslider.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gslider.h b/gb.gtk3/src/gslider.h new file mode 120000 index 00000000..9edb0927 --- /dev/null +++ b/gb.gtk3/src/gslider.h @@ -0,0 +1 @@ +../../gb.gtk/src/gslider.h \ No newline at end of file diff --git a/gb.gtk3/src/gtabstrip.cpp b/gb.gtk3/src/gtabstrip.cpp new file mode 120000 index 00000000..44502f74 --- /dev/null +++ b/gb.gtk3/src/gtabstrip.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gtabstrip.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gtabstrip.h b/gb.gtk3/src/gtabstrip.h new file mode 120000 index 00000000..1955f5fb --- /dev/null +++ b/gb.gtk3/src/gtabstrip.h @@ -0,0 +1 @@ +../../gb.gtk/src/gtabstrip.h \ No newline at end of file diff --git a/gb.gtk3/src/gtag.h b/gb.gtk3/src/gtag.h new file mode 120000 index 00000000..1c95e72b --- /dev/null +++ b/gb.gtk3/src/gtag.h @@ -0,0 +1 @@ +../../gb.gtk/src/gtag.h \ No newline at end of file diff --git a/gb.gtk3/src/gtextarea.cpp b/gb.gtk3/src/gtextarea.cpp new file mode 120000 index 00000000..ebf404d6 --- /dev/null +++ b/gb.gtk3/src/gtextarea.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gtextarea.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gtextarea.h b/gb.gtk3/src/gtextarea.h new file mode 120000 index 00000000..c9375c10 --- /dev/null +++ b/gb.gtk3/src/gtextarea.h @@ -0,0 +1 @@ +../../gb.gtk/src/gtextarea.h \ No newline at end of file diff --git a/gb.gtk3/src/gtextbox.cpp b/gb.gtk3/src/gtextbox.cpp new file mode 120000 index 00000000..445b644e --- /dev/null +++ b/gb.gtk3/src/gtextbox.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gtextbox.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gtextbox.h b/gb.gtk3/src/gtextbox.h new file mode 120000 index 00000000..8fac46eb --- /dev/null +++ b/gb.gtk3/src/gtextbox.h @@ -0,0 +1 @@ +../../gb.gtk/src/gtextbox.h \ No newline at end of file diff --git a/gb.gtk3/src/gtools.cpp b/gb.gtk3/src/gtools.cpp new file mode 120000 index 00000000..2ecdae0a --- /dev/null +++ b/gb.gtk3/src/gtools.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gtools.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gtools.h b/gb.gtk3/src/gtools.h new file mode 120000 index 00000000..2edf4162 --- /dev/null +++ b/gb.gtk3/src/gtools.h @@ -0,0 +1 @@ +../../gb.gtk/src/gtools.h \ No newline at end of file diff --git a/gb.gtk3/src/gtrayicon.cpp b/gb.gtk3/src/gtrayicon.cpp new file mode 120000 index 00000000..2190b99a --- /dev/null +++ b/gb.gtk3/src/gtrayicon.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gtrayicon.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gtrayicon.h b/gb.gtk3/src/gtrayicon.h new file mode 120000 index 00000000..00dab936 --- /dev/null +++ b/gb.gtk3/src/gtrayicon.h @@ -0,0 +1 @@ +../../gb.gtk/src/gtrayicon.h \ No newline at end of file diff --git a/gb.gtk3/src/gtree.cpp b/gb.gtk3/src/gtree.cpp new file mode 120000 index 00000000..52418b62 --- /dev/null +++ b/gb.gtk3/src/gtree.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/gtree.cpp \ No newline at end of file diff --git a/gb.gtk3/src/gtree.h b/gb.gtk3/src/gtree.h new file mode 120000 index 00000000..d30e5966 --- /dev/null +++ b/gb.gtk3/src/gtree.h @@ -0,0 +1 @@ +../../gb.gtk/src/gtree.h \ No newline at end of file diff --git a/gb.gtk3/src/kentities.h b/gb.gtk3/src/kentities.h new file mode 120000 index 00000000..8e1ac6de --- /dev/null +++ b/gb.gtk3/src/kentities.h @@ -0,0 +1 @@ +../../gb.gtk/src/kentities.h \ No newline at end of file diff --git a/gb.gtk3/src/main.cpp b/gb.gtk3/src/main.cpp new file mode 120000 index 00000000..f3fa5ce6 --- /dev/null +++ b/gb.gtk3/src/main.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/main.cpp \ No newline at end of file diff --git a/gb.gtk3/src/main.h b/gb.gtk3/src/main.h new file mode 120000 index 00000000..93ccdcc4 --- /dev/null +++ b/gb.gtk3/src/main.h @@ -0,0 +1 @@ +../../gb.gtk/src/main.h \ No newline at end of file diff --git a/gb.gtk3/src/sm/bonobo-macros.h b/gb.gtk3/src/sm/bonobo-macros.h new file mode 120000 index 00000000..b5082af4 --- /dev/null +++ b/gb.gtk3/src/sm/bonobo-macros.h @@ -0,0 +1 @@ +../../../gb.gtk/src/sm/bonobo-macros.h \ No newline at end of file diff --git a/gb.gtk3/src/sm/gnome-client.c b/gb.gtk3/src/sm/gnome-client.c new file mode 120000 index 00000000..004d3034 --- /dev/null +++ b/gb.gtk3/src/sm/gnome-client.c @@ -0,0 +1 @@ +../../../gb.gtk/src/sm/gnome-client.c \ No newline at end of file diff --git a/gb.gtk3/src/sm/gnome-client.h b/gb.gtk3/src/sm/gnome-client.h new file mode 120000 index 00000000..1ebc5561 --- /dev/null +++ b/gb.gtk3/src/sm/gnome-client.h @@ -0,0 +1 @@ +../../../gb.gtk/src/sm/gnome-client.h \ No newline at end of file diff --git a/gb.gtk3/src/sm/gnome-ice.c b/gb.gtk3/src/sm/gnome-ice.c new file mode 120000 index 00000000..3ce044e0 --- /dev/null +++ b/gb.gtk3/src/sm/gnome-ice.c @@ -0,0 +1 @@ +../../../gb.gtk/src/sm/gnome-ice.c \ No newline at end of file diff --git a/gb.gtk3/src/sm/gnome-ice.h b/gb.gtk3/src/sm/gnome-ice.h new file mode 120000 index 00000000..288bdb25 --- /dev/null +++ b/gb.gtk3/src/sm/gnome-ice.h @@ -0,0 +1 @@ +../../../gb.gtk/src/sm/gnome-ice.h \ No newline at end of file diff --git a/gb.gtk3/src/sm/gnome-macros.h b/gb.gtk3/src/sm/gnome-macros.h new file mode 120000 index 00000000..8e96af97 --- /dev/null +++ b/gb.gtk3/src/sm/gnome-macros.h @@ -0,0 +1 @@ +../../../gb.gtk/src/sm/gnome-macros.h \ No newline at end of file diff --git a/gb.gtk3/src/sm/gnome-marshal.c b/gb.gtk3/src/sm/gnome-marshal.c new file mode 120000 index 00000000..3513d5d8 --- /dev/null +++ b/gb.gtk3/src/sm/gnome-marshal.c @@ -0,0 +1 @@ +../../../gb.gtk/src/sm/gnome-marshal.c \ No newline at end of file diff --git a/gb.gtk3/src/sm/gnome-marshal.h b/gb.gtk3/src/sm/gnome-marshal.h new file mode 120000 index 00000000..42bc80f9 --- /dev/null +++ b/gb.gtk3/src/sm/gnome-marshal.h @@ -0,0 +1 @@ +../../../gb.gtk/src/sm/gnome-marshal.h \ No newline at end of file diff --git a/gb.gtk3/src/sm/gnome-uidefs.h b/gb.gtk3/src/sm/gnome-uidefs.h new file mode 120000 index 00000000..69c5f5da --- /dev/null +++ b/gb.gtk3/src/sm/gnome-uidefs.h @@ -0,0 +1 @@ +../../../gb.gtk/src/sm/gnome-uidefs.h \ No newline at end of file diff --git a/gb.gtk3/src/sm/gnometypebuiltins.c b/gb.gtk3/src/sm/gnometypebuiltins.c new file mode 120000 index 00000000..37b58fa6 --- /dev/null +++ b/gb.gtk3/src/sm/gnometypebuiltins.c @@ -0,0 +1 @@ +../../../gb.gtk/src/sm/gnometypebuiltins.c \ No newline at end of file diff --git a/gb.gtk3/src/sm/gnometypebuiltins.h b/gb.gtk3/src/sm/gnometypebuiltins.h new file mode 120000 index 00000000..ed1590c4 --- /dev/null +++ b/gb.gtk3/src/sm/gnometypebuiltins.h @@ -0,0 +1 @@ +../../../gb.gtk/src/sm/gnometypebuiltins.h \ No newline at end of file diff --git a/gb.gtk3/src/sm/libgnomeui.h b/gb.gtk3/src/sm/libgnomeui.h new file mode 120000 index 00000000..5772736c --- /dev/null +++ b/gb.gtk3/src/sm/libgnomeui.h @@ -0,0 +1 @@ +../../../gb.gtk/src/sm/libgnomeui.h \ No newline at end of file diff --git a/gb.gtk3/src/sm/libgnomeuiP.h b/gb.gtk3/src/sm/libgnomeuiP.h new file mode 120000 index 00000000..33a15fc4 --- /dev/null +++ b/gb.gtk3/src/sm/libgnomeuiP.h @@ -0,0 +1 @@ +../../../gb.gtk/src/sm/libgnomeuiP.h \ No newline at end of file diff --git a/gb.gtk3/src/sm/sm.h b/gb.gtk3/src/sm/sm.h new file mode 120000 index 00000000..6df12b14 --- /dev/null +++ b/gb.gtk3/src/sm/sm.h @@ -0,0 +1 @@ +../../../gb.gtk/src/sm/sm.h \ No newline at end of file diff --git a/gb.gtk3/src/watcher.cpp b/gb.gtk3/src/watcher.cpp new file mode 120000 index 00000000..c2bd449e --- /dev/null +++ b/gb.gtk3/src/watcher.cpp @@ -0,0 +1 @@ +../../gb.gtk/src/watcher.cpp \ No newline at end of file diff --git a/gb.gtk3/src/watcher.h b/gb.gtk3/src/watcher.h new file mode 120000 index 00000000..02c01ecb --- /dev/null +++ b/gb.gtk3/src/watcher.h @@ -0,0 +1 @@ +../../gb.gtk/src/watcher.h \ No newline at end of file diff --git a/gb.gtk3/src/widgets.h b/gb.gtk3/src/widgets.h new file mode 120000 index 00000000..f7ef9769 --- /dev/null +++ b/gb.gtk3/src/widgets.h @@ -0,0 +1 @@ +../../gb.gtk/src/widgets.h \ No newline at end of file diff --git a/gb.gtk3/src/x11.c b/gb.gtk3/src/x11.c new file mode 120000 index 00000000..d5dea0cb --- /dev/null +++ b/gb.gtk3/src/x11.c @@ -0,0 +1 @@ +../../gb.gtk/src/x11.c \ No newline at end of file diff --git a/gb.gtk3/src/x11.h b/gb.gtk3/src/x11.h new file mode 120000 index 00000000..50be7e7d --- /dev/null +++ b/gb.gtk3/src/x11.h @@ -0,0 +1 @@ +../../gb.gtk/src/x11.h \ No newline at end of file diff --git a/gb.httpd/AUTHORS b/gb.httpd/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.httpd/COPYING b/gb.httpd/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.httpd/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.httpd/ChangeLog b/gb.httpd/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.httpd/INSTALL b/gb.httpd/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.httpd/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.httpd/Makefile.am b/gb.httpd/Makefile.am new file mode 100644 index 00000000..4cac8ec7 --- /dev/null +++ b/gb.httpd/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @HTTPD_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.httpd/NEWS b/gb.httpd/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.httpd/README b/gb.httpd/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.httpd/acinclude.m4 b/gb.httpd/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.httpd/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.httpd/component.am b/gb.httpd/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.httpd/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.httpd/configure.ac b/gb.httpd/configure.ac new file mode 100644 index 00000000..5fb0fa50 --- /dev/null +++ b/gb.httpd/configure.ac @@ -0,0 +1,150 @@ +dnl ---- configure.ac for gb.httpd + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-httpd, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.httpd) +AC_PROG_LIBTOOL + +V_CCOPT="-O" +if test "$GCC" = yes ; then + AC_MSG_CHECKING(gcc version) + AC_CACHE_VAL(ac_cv_lbl_gcc_vers, + ac_cv_lbl_gcc_vers=`$CC -dumpversion 2>&1 | \ + sed -e 's/\..*//'`) + AC_MSG_RESULT($ac_cv_lbl_gcc_vers) + if test "$ac_cv_lbl_gcc_vers" -gt 1 ; then + V_CCOPT="-O2" + fi +fi +if test -f .devel ; then + V_CCOPT="-g $V_CCOPT -Wall -Wmissing-prototypes -Wstrict-prototypes" +fi + +dnl +dnl maybe this should be a loop +dnl +AC_MSG_CHECKING(how to link static binaries) +AC_CACHE_VAL(ac_cv_lbl_static_flag, + ac_cv_lbl_static_flag=unknown + echo 'main() {}' > conftest.c + if test "$GCC" != yes ; then + trial_flag="-Bstatic" + test=`$CC $trial_flag -o conftest conftest.c 2>&1` + if test -z "$test" ; then + ac_cv_lbl_static_flag="$trial_flag" + fi + rm -f conftest + fi + if test "$ac_cv_lbl_static_flag" = unknown ; then + trial_flag="-static" + test=`$CC $trial_flag -o conftest conftest.c 2>&1` + if test -z "$test" ; then + ac_cv_lbl_static_flag="$trial_flag" + fi + rm -f conftest + fi + rm conftest.c) +AC_MSG_RESULT($ac_cv_lbl_static_flag) +if test "$ac_cv_lbl_static_flag" != unknown ; then + V_STATICFLAG="$ac_cv_lbl_static_flag" +fi + +AC_MSG_CHECKING(for __progname) +AC_CACHE_VAL(ac_cv_extern__progname, + AC_TRY_LINK([], + [extern char *__progname; + puts(__progname)], + ac_cv_extern__progname=yes, + ac_cv_extern__progname=no)) +if test $ac_cv_extern__progname = yes ; then + AC_DEFINE([HAVE__PROGNAME], [], [have __progname]) + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi + +AC_CHECK_HEADERS(fcntl.h grp.h memory.h paths.h poll.h sys/poll.h sys/devpoll.h sys/event.h osreldate.h) +AC_HEADER_TIME +AC_HEADER_DIRENT + +dnl d="/usr/local/v6/lib" +dnl AC_MSG_CHECKING(for $d) +dnl if test -d $d; then +dnl AC_MSG_RESULT(yes (Adding -L$d to LDFLAGS)) +dnl LDFLAGS="$LDFLAGS -L$d" +dnl else +dnl AC_MSG_RESULT(no) +dnl fi + +dnl +dnl Most operating systems have gethostbyname() in the default searched +dnl libraries (i.e. libc): +dnl +V_NETLIBS="" +AC_CHECK_FUNC(gethostbyname, , + # Some OSes (eg. Solaris) place it in libnsl: + GB_AC_LBL_CHECK_LIB(nsl, gethostbyname, + V_NETLIBS="-lnsl $V_NETLIBS", + # Some strange OSes (SINIX) have it in libsocket: + GB_AC_LBL_CHECK_LIB(socket, gethostbyname, + V_NETLIBS="-lsocket $V_NETLIBS", + # Unfortunately libsocket sometimes depends on libnsl. + # AC_CHECK_LIB's API is essentially broken so the + # following ugliness is necessary: + GB_AC_LBL_CHECK_LIB(socket, gethostbyname, + V_NETLIBS="-lsocket -lnsl $V_NETLIBS", + AC_CHECK_LIB(resolv, gethostbyname, + V_NETLIBS="-lresolv $V_NETLIBS"), + -lnsl)))) +AC_CHECK_FUNC(socket, , + AC_CHECK_LIB(socket, socket, + V_NETLIBS="-lsocket $V_NETLIBS", + GB_AC_LBL_CHECK_LIB(socket, socket, + V_NETLIBS="-lsocket -lnsl $V_NETLIBS", , -lnsl))) + +AC_CHECK_LIB(inet6, main) + +AC_CHECK_FUNC(crypt, , AC_CHECK_LIB(crypt, crypt)) +AC_CHECK_FUNC(hstrerror, , + AC_CHECK_LIB(resolv, hstrerror, V_NETLIBS="-lresolv $V_NETLIBS")) + +AC_REPLACE_FUNCS(strerror) +AC_CHECK_FUNCS(waitpid vsnprintf daemon setsid setlogin getaddrinfo getnameinfo gai_strerror kqueue atoll) +AC_FUNC_MMAP + +case "$target_os" in +solaris*) + dnl Solaris's select() is a bad wrapper routine. + AC_CHECK_FUNCS(poll) + ;; +*) + AC_CHECK_FUNCS(select poll) + ;; +esac + +GB_AC_ACME_TM_GMTOFF +GB_AC_ACME_INT64T +GB_AC_ACME_SOCKLENT + +AC_PROG_MAKE_SET +AC_PROG_INSTALL + +AC_SUBST(DEFS) +AC_SUBST(V_CCOPT) +AC_SUBST(V_STATICFLAG) +AC_SUBST(V_NETLIBS) + +GB_COMPONENT( + httpd, + HTTPD, + gb.httpd, + [src], + [], + [], + [], + []) + +AC_OUTPUT( Makefile src/Makefile ) +GB_PRINT_MESSAGES diff --git a/gb.httpd/gambas.h b/gb.httpd/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.httpd/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.httpd/gb_common.h b/gb.httpd/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.httpd/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.httpd/m4 b/gb.httpd/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.httpd/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.httpd/reconf b/gb.httpd/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.httpd/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.httpd/src/Makefile.am b/gb.httpd/src/Makefile.am new file mode 100644 index 00000000..baa21f56 --- /dev/null +++ b/gb.httpd/src/Makefile.am @@ -0,0 +1,20 @@ +COMPONENT = gb.httpd +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.httpd.la + +gb_httpd_la_LIBADD = @HTTPD_LIB@ +gb_httpd_la_LDFLAGS = -module @LD_FLAGS@ @HTTPD_LDFLAGS@ +gb_httpd_la_CPPFLAGS = @HTTPD_INC@ + +gb_httpd_la_SOURCES = main.c main.h \ + fdwatch.h fdwatch.c \ + libhttpd.h libhttpd.c \ + match.h match.c \ + mime_encodings.h \ + mime_types.h \ + tdate_parse.h tdate_parse.c \ + thttpd.h thttpd.c \ + timers.h timers.c \ + version.h + diff --git a/gb.httpd/src/fdwatch.c b/gb.httpd/src/fdwatch.c new file mode 100644 index 00000000..f8f4a2fd --- /dev/null +++ b/gb.httpd/src/fdwatch.c @@ -0,0 +1,840 @@ +/* fdwatch.c - fd watcher routines, either select() or poll() +** +** Copyright (c) 1999,2000 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifdef HAVE_POLL_H +#include +#else /* HAVE_POLL_H */ +#ifdef HAVE_SYS_POLL_H +#include +#endif /* HAVE_SYS_POLL_H */ +#endif /* HAVE_POLL_H */ + +#ifdef HAVE_SYS_DEVPOLL_H +#include +#ifndef HAVE_DEVPOLL +#define HAVE_DEVPOLL +#endif /* !HAVE_DEVPOLL */ +#endif /* HAVE_SYS_DEVPOLL_H */ + +#ifdef HAVE_SYS_EVENT_H +#include +#endif /* HAVE_SYS_EVENT_H */ + +#include "fdwatch.h" + +#ifdef HAVE_SELECT +#ifndef FD_SET +#define NFDBITS 32 +#define FD_SETSIZE 32 +#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) +#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) +#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) +#define FD_ZERO(p) bzero((char*)(p), sizeof(*(p))) +#endif /* !FD_SET */ +#endif /* HAVE_SELECT */ + +static int nfiles; +static long nwatches; +static int *fd_rw; +static void **fd_data; +static int nreturned, next_ridx; + +#ifdef HAVE_KQUEUE + +#define WHICH "kevent" +#define INIT( nfiles ) kqueue_init( nfiles ) +#define ADD_FD( fd, rw ) kqueue_add_fd( fd, rw ) +#define DEL_FD( fd ) kqueue_del_fd( fd ) +#define WATCH( timeout_msecs ) kqueue_watch( timeout_msecs ) +#define CHECK_FD( fd ) kqueue_check_fd( fd ) +#define GET_FD( ridx ) kqueue_get_fd( ridx ) + +static int kqueue_init(int nfiles); +static void kqueue_add_fd(int fd, int rw); +static void kqueue_del_fd(int fd); +static int kqueue_watch(long timeout_msecs); +static int kqueue_check_fd(int fd); +static int kqueue_get_fd(int ridx); + +#else /* HAVE_KQUEUE */ +#ifdef HAVE_DEVPOLL + +#define WHICH "devpoll" +#define INIT( nfiles ) devpoll_init( nfiles ) +#define ADD_FD( fd, rw ) devpoll_add_fd( fd, rw ) +#define DEL_FD( fd ) devpoll_del_fd( fd ) +#define WATCH( timeout_msecs ) devpoll_watch( timeout_msecs ) +#define CHECK_FD( fd ) devpoll_check_fd( fd ) +#define GET_FD( ridx ) devpoll_get_fd( ridx ) + +static int devpoll_init(int nfiles); +static void devpoll_add_fd(int fd, int rw); +static void devpoll_del_fd(int fd); +static int devpoll_watch(long timeout_msecs); +static int devpoll_check_fd(int fd); +static int devpoll_get_fd(int ridx); + +#else /* HAVE_DEVPOLL */ +#ifdef HAVE_POLL + +#define WHICH "poll" +#define INIT( nfiles ) poll_init( nfiles ) +#define ADD_FD( fd, rw ) poll_add_fd( fd, rw ) +#define DEL_FD( fd ) poll_del_fd( fd ) +#define WATCH( timeout_msecs ) poll_watch( timeout_msecs ) +#define CHECK_FD( fd ) poll_check_fd( fd ) +#define GET_FD( ridx ) poll_get_fd( ridx ) + +static int poll_init(int nfiles); +static void poll_add_fd(int fd, int rw); +static void poll_del_fd(int fd); +static int poll_watch(long timeout_msecs); +static int poll_check_fd(int fd); +static int poll_get_fd(int ridx); + +#else /* HAVE_POLL */ +#ifdef HAVE_SELECT + +#define WHICH "select" +#define INIT( nfiles ) select_init( nfiles ) +#define ADD_FD( fd, rw ) select_add_fd( fd, rw ) +#define DEL_FD( fd ) select_del_fd( fd ) +#define WATCH( timeout_msecs ) select_watch( timeout_msecs ) +#define CHECK_FD( fd ) select_check_fd( fd ) +#define GET_FD( ridx ) select_get_fd( ridx ) + +static int select_init(int nfiles); +static void select_add_fd(int fd, int rw); +static void select_del_fd(int fd); +static int select_watch(long timeout_msecs); +static int select_check_fd(int fd); +static int select_get_fd(int ridx); + +#endif /* HAVE_SELECT */ +#endif /* HAVE_POLL */ +#endif /* HAVE_DEVPOLL */ +#endif /* HAVE_KQUEUE */ + + +/* Routines. */ + +/* Figure out how many file descriptors the system allows, and +** initialize the fdwatch data structures. Returns -1 on failure. +*/ +int fdwatch_get_nfiles(void) +{ + int i; +#ifdef RLIMIT_NOFILE + struct rlimit rl; +#endif /* RLIMIT_NOFILE */ + + /* Figure out how many fd's we can have. */ + nfiles = getdtablesize(); +#ifdef RLIMIT_NOFILE + /* If we have getrlimit(), use that, and attempt to raise the limit. */ + if (getrlimit(RLIMIT_NOFILE, &rl) == 0) + { + nfiles = rl.rlim_cur; + if (rl.rlim_max == RLIM_INFINITY) + rl.rlim_cur = 8192; /* arbitrary */ + else if (rl.rlim_max > rl.rlim_cur) + rl.rlim_cur = rl.rlim_max; + if (setrlimit(RLIMIT_NOFILE, &rl) == 0) + nfiles = rl.rlim_cur; + } +#endif /* RLIMIT_NOFILE */ + +#if defined(HAVE_SELECT) && ! ( defined(HAVE_POLL) || defined(HAVE_DEVPOLL) || defined(HAVE_KQUEUE) ) + /* If we use select(), then we must limit ourselves to FD_SETSIZE. */ + nfiles = MIN(nfiles, FD_SETSIZE); +#endif /* HAVE_SELECT && ! ( HAVE_POLL || HAVE_DEVPOLL || HAVE_KQUEUE ) */ + + /* Initialize the fdwatch data structures. */ + nwatches = 0; + fd_rw = (int *) malloc(sizeof(int) * nfiles); + fd_data = (void **) malloc(sizeof(void *) * nfiles); + if (fd_rw == (int *) 0 || fd_data == (void **) 0) + return -1; + for (i = 0; i < nfiles; ++i) + fd_rw[i] = -1; + if (INIT(nfiles) == -1) + return -1; + + return nfiles; +} + + +/* Add a descriptor to the watch list. rw is either FDW_READ or FDW_WRITE. */ +void fdwatch_add_fd(int fd, void *client_data, int rw) +{ + if (fd < 0 || fd >= nfiles || fd_rw[fd] != -1) + { + syslog(LOG_ERR, "bad fd (%d) passed to fdwatch_add_fd!", fd); + return; + } + ADD_FD(fd, rw); + fd_rw[fd] = rw; + fd_data[fd] = client_data; +} + + +/* Remove a descriptor from the watch list. */ +void fdwatch_del_fd(int fd) +{ + if (fd < 0 || fd >= nfiles || fd_rw[fd] == -1) + { + syslog(LOG_ERR, "bad fd (%d) passed to fdwatch_del_fd!", fd); + return; + } + DEL_FD(fd); + fd_rw[fd] = -1; + fd_data[fd] = (void *) 0; +} + +/* Do the watch. Return value is the number of descriptors that are ready, +** or 0 if the timeout expired, or -1 on errors. A timeout of INFTIM means +** wait indefinitely. +*/ +int fdwatch(long timeout_msecs) +{ + ++nwatches; + nreturned = WATCH(timeout_msecs); + next_ridx = 0; + return nreturned; +} + + +/* Check if a descriptor was ready. */ +int fdwatch_check_fd(int fd) +{ + if (fd < 0 || fd >= nfiles || fd_rw[fd] == -1) + { + syslog(LOG_ERR, "bad fd (%d) passed to fdwatch_check_fd!", fd); + return 0; + } + return CHECK_FD(fd); +} + + +void *fdwatch_get_next_client_data(void) +{ + int fd; + + if (next_ridx >= nreturned) + return (void *) -1; + fd = GET_FD(next_ridx++); + if (fd < 0 || fd >= nfiles) + return (void *) 0; + return fd_data[fd]; +} + + +/* Generate debugging statistics syslog message. */ +void fdwatch_logstats(long secs) +{ + if (secs > 0) + syslog(LOG_INFO, " fdwatch - %ld %ss (%g/sec)", + nwatches, WHICH, (float) nwatches / secs); + nwatches = 0; +} + + +#ifdef HAVE_KQUEUE + +static int maxkqevents; +static struct kevent *kqevents; +static int nkqevents; +static struct kevent *kqrevents; +static int *kqrfdidx; +static int kq; + + +static int kqueue_init(int nfiles) +{ + kq = kqueue(); + if (kq == -1) + return -1; + maxkqevents = nfiles * 2; + kqevents = (struct kevent *) malloc(sizeof(struct kevent) * maxkqevents); + kqrevents = (struct kevent *) malloc(sizeof(struct kevent) * nfiles); + kqrfdidx = (int *) malloc(sizeof(int) * nfiles); + if (kqevents == (struct kevent *) 0 || kqrevents == (struct kevent *) 0 || + kqrfdidx == (int *) 0) + return -1; + (void) memset(kqevents, 0, sizeof(struct kevent) * maxkqevents); + (void) memset(kqrfdidx, 0, sizeof(int) * nfiles); + return 0; +} + + +static void kqueue_add_fd(int fd, int rw) +{ + if (nkqevents >= maxkqevents) + { + syslog(LOG_ERR, "too many kqevents in kqueue_add_fd!"); + return; + } + kqevents[nkqevents].ident = fd; + kqevents[nkqevents].flags = EV_ADD; + switch (rw) + { + case FDW_READ: + kqevents[nkqevents].filter = EVFILT_READ; + break; + case FDW_WRITE: + kqevents[nkqevents].filter = EVFILT_WRITE; + break; + default: + break; + } + ++nkqevents; +} + + +static void kqueue_del_fd(int fd) +{ + if (nkqevents >= maxkqevents) + { + syslog(LOG_ERR, "too many kqevents in kqueue_del_fd!"); + return; + } + kqevents[nkqevents].ident = fd; + kqevents[nkqevents].flags = EV_DELETE; + switch (fd_rw[fd]) + { + case FDW_READ: + kqevents[nkqevents].filter = EVFILT_READ; + break; + case FDW_WRITE: + kqevents[nkqevents].filter = EVFILT_WRITE; + break; + } + ++nkqevents; +} + + +static int kqueue_watch(long timeout_msecs) +{ + int i, r; + + if (timeout_msecs == INFTIM) + r = + kevent(kq, kqevents, nkqevents, kqrevents, nfiles, + (struct timespec *) 0); + else + { + struct timespec ts; + ts.tv_sec = timeout_msecs / 1000L; + ts.tv_nsec = (timeout_msecs % 1000L) * 1000000L; + r = kevent(kq, kqevents, nkqevents, kqrevents, nfiles, &ts); + } + nkqevents = 0; + if (r == -1) + return -1; + + for (i = 0; i < r; ++i) + kqrfdidx[kqrevents[i].ident] = i; + + return r; +} + + +static int kqueue_check_fd(int fd) +{ + int ridx = kqrfdidx[fd]; + + if (ridx < 0 || ridx >= nfiles) + { + syslog(LOG_ERR, "bad ridx (%d) in kqueue_check_fd!", ridx); + return 0; + } + if (ridx >= nreturned) + return 0; + if (kqrevents[ridx].ident != fd) + return 0; + if (kqrevents[ridx].flags & EV_ERROR) + return 0; + switch (fd_rw[fd]) + { + case FDW_READ: + return kqrevents[ridx].filter == EVFILT_READ; + case FDW_WRITE: + return kqrevents[ridx].filter == EVFILT_WRITE; + default: + return 0; + } +} + + +static int kqueue_get_fd(int ridx) +{ + if (ridx < 0 || ridx >= nfiles) + { + syslog(LOG_ERR, "bad ridx (%d) in kqueue_get_fd!", ridx); + return -1; + } + return kqrevents[ridx].ident; +} + +#else /* HAVE_KQUEUE */ + + +#ifdef HAVE_DEVPOLL + +static int maxdpevents; +static struct pollfd *dpevents; +static int ndpevents; +static struct pollfd *dprevents; +static int *dp_rfdidx; +static int dp; + + +static int devpoll_init(int nfiles) +{ + dp = open("/dev/poll", O_RDWR); + if (dp == -1) + return -1; + (void) fcntl(dp, F_SETFD, 1); + maxdpevents = nfiles * 2; + dpevents = (struct pollfd *) malloc(sizeof(struct pollfd) * maxdpevents); + dprevents = (struct pollfd *) malloc(sizeof(struct pollfd) * nfiles); + dp_rfdidx = (int *) malloc(sizeof(int) * nfiles); + if (dpevents == (struct pollfd *) 0 || dprevents == (struct pollfd *) 0 || + dp_rfdidx == (int *) 0) + return -1; + (void) memset(dp_rfdidx, 0, sizeof(int) * nfiles); + return 0; +} + + +static void devpoll_add_fd(int fd, int rw) +{ + if (ndpevents >= maxdpevents) + { + syslog(LOG_ERR, "too many fds in devpoll_add_fd!"); + return; + } + dpevents[ndpevents].fd = fd; + switch (rw) + { + case FDW_READ: + dpevents[ndpevents].events = POLLIN; + break; + case FDW_WRITE: + dpevents[ndpevents].events = POLLOUT; + break; + default: + break; + } + ++ndpevents; +} + + +static void devpoll_del_fd(int fd) +{ + if (ndpevents >= maxdpevents) + { + syslog(LOG_ERR, "too many fds in devpoll_del_fd!"); + return; + } + dpevents[ndpevents].fd = fd; + dpevents[ndpevents].events = POLLREMOVE; + ++ndpevents; +} + + +static int devpoll_watch(long timeout_msecs) +{ + int i, r; + struct dvpoll dvp; + + r = sizeof(struct pollfd) * ndpevents; + if (r > 0 && write(dp, dpevents, r) != r) + return -1; + + ndpevents = 0; + dvp.dp_fds = dprevents; + dvp.dp_nfds = nfiles; + dvp.dp_timeout = (int) timeout_msecs; + + r = ioctl(dp, DP_POLL, &dvp); + if (r == -1) + return -1; + + for (i = 0; i < r; ++i) + dp_rfdidx[dprevents[i].fd] = i; + + return r; +} + + +static int devpoll_check_fd(int fd) +{ + int ridx = dp_rfdidx[fd]; + + if (ridx < 0 || ridx >= nfiles) + { + syslog(LOG_ERR, "bad ridx (%d) in devpoll_check_fd!", ridx); + return 0; + } + if (ridx >= nreturned) + return 0; + if (dprevents[ridx].fd != fd) + return 0; + if (dprevents[ridx].revents & POLLERR) + return 0; + switch (fd_rw[fd]) + { + case FDW_READ: + return dprevents[ridx].revents & (POLLIN | POLLHUP | POLLNVAL); + case FDW_WRITE: + return dprevents[ridx].revents & (POLLOUT | POLLHUP | POLLNVAL); + default: + return 0; + } +} + + +static int devpoll_get_fd(int ridx) +{ + if (ridx < 0 || ridx >= nfiles) + { + syslog(LOG_ERR, "bad ridx (%d) in devpoll_get_fd!", ridx); + return -1; + } + return dprevents[ridx].fd; +} + + +#else /* HAVE_DEVPOLL */ + + +#ifdef HAVE_POLL + +static struct pollfd *pollfds; +static int npoll_fds; +static int *poll_fdidx; +static int *poll_rfdidx; + + +static int poll_init(int nfiles) +{ + int i; + + pollfds = (struct pollfd *) malloc(sizeof(struct pollfd) * nfiles); + poll_fdidx = (int *) malloc(sizeof(int) * nfiles); + poll_rfdidx = (int *) malloc(sizeof(int) * nfiles); + if (pollfds == (struct pollfd *) 0 || poll_fdidx == (int *) 0 || + poll_rfdidx == (int *) 0) + return -1; + for (i = 0; i < nfiles; ++i) + pollfds[i].fd = poll_fdidx[i] = -1; + return 0; +} + + +static void poll_add_fd(int fd, int rw) +{ + if (npoll_fds >= nfiles) + { + syslog(LOG_ERR, "too many fds in poll_add_fd!"); + return; + } + pollfds[npoll_fds].fd = fd; + switch (rw) + { + case FDW_READ: + pollfds[npoll_fds].events = POLLIN; + break; + case FDW_WRITE: + pollfds[npoll_fds].events = POLLOUT; + break; + default: + break; + } + poll_fdidx[fd] = npoll_fds; + ++npoll_fds; +} + + +static void poll_del_fd(int fd) +{ + int idx = poll_fdidx[fd]; + + if (idx < 0 || idx >= nfiles) + { + syslog(LOG_ERR, "bad idx (%d) in poll_del_fd!", idx); + return; + } + --npoll_fds; + pollfds[idx] = pollfds[npoll_fds]; + poll_fdidx[pollfds[idx].fd] = idx; + pollfds[npoll_fds].fd = -1; + poll_fdidx[fd] = -1; +} + + +static int poll_watch(long timeout_msecs) +{ + int r, ridx, i; + + r = poll(pollfds, npoll_fds, (int) timeout_msecs); + if (r <= 0) + return r; + + ridx = 0; + for (i = 0; i < npoll_fds; ++i) + if (pollfds[i].revents & + (POLLIN | POLLOUT | POLLERR | POLLHUP | POLLNVAL)) + { + poll_rfdidx[ridx++] = pollfds[i].fd; + if (ridx == r) + break; + } + + return ridx; /* should be equal to r */ +} + + +static int poll_check_fd(int fd) +{ + int fdidx = poll_fdidx[fd]; + + if (fdidx < 0 || fdidx >= nfiles) + { + syslog(LOG_ERR, "bad fdidx (%d) in poll_check_fd!", fdidx); + return 0; + } + if (pollfds[fdidx].revents & POLLERR) + return 0; + switch (fd_rw[fd]) + { + case FDW_READ: + return pollfds[fdidx].revents & (POLLIN | POLLHUP | POLLNVAL); + case FDW_WRITE: + return pollfds[fdidx].revents & (POLLOUT | POLLHUP | POLLNVAL); + default: + return 0; + } +} + + +static int poll_get_fd(int ridx) +{ + if (ridx < 0 || ridx >= nfiles) + { + syslog(LOG_ERR, "bad ridx (%d) in poll_get_fd!", ridx); + return -1; + } + return poll_rfdidx[ridx]; +} + +#else /* HAVE_POLL */ + + +#ifdef HAVE_SELECT + +static fd_set master_rfdset; +static fd_set master_wfdset; +static fd_set working_rfdset; +static fd_set working_wfdset; +static int *select_fds; +static int *select_fdidx; +static int *select_rfdidx; +static int nselect_fds; +static int maxfd; +static int maxfd_changed; + + +static int select_init(int nfiles) +{ + int i; + + FD_ZERO(&master_rfdset); + FD_ZERO(&master_wfdset); + select_fds = (int *) malloc(sizeof(int) * nfiles); + select_fdidx = (int *) malloc(sizeof(int) * nfiles); + select_rfdidx = (int *) malloc(sizeof(int) * nfiles); + if (select_fds == (int *) 0 || select_fdidx == (int *) 0 || + select_rfdidx == (int *) 0) + return -1; + nselect_fds = 0; + maxfd = -1; + maxfd_changed = 0; + for (i = 0; i < nfiles; ++i) + select_fds[i] = select_fdidx[i] = -1; + return 0; +} + + +static void select_add_fd(int fd, int rw) +{ + if (nselect_fds >= nfiles) + { + syslog(LOG_ERR, "too many fds in select_add_fd!"); + return; + } + select_fds[nselect_fds] = fd; + switch (rw) + { + case FDW_READ: + FD_SET(fd, &master_rfdset); + break; + case FDW_WRITE: + FD_SET(fd, &master_wfdset); + break; + default: + break; + } + if (fd > maxfd) + maxfd = fd; + select_fdidx[fd] = nselect_fds; + ++nselect_fds; +} + + +static void select_del_fd(int fd) +{ + int idx = select_fdidx[fd]; + + if (idx < 0 || idx >= nfiles) + { + syslog(LOG_ERR, "bad idx (%d) in select_del_fd!", idx); + return; + } + + --nselect_fds; + select_fds[idx] = select_fds[nselect_fds]; + select_fdidx[select_fds[idx]] = idx; + select_fds[nselect_fds] = -1; + select_fdidx[fd] = -1; + + FD_CLR(fd, &master_rfdset); + FD_CLR(fd, &master_wfdset); + + if (fd >= maxfd) + maxfd_changed = 1; +} + + +static int select_get_maxfd(void) +{ + if (maxfd_changed) + { + int i; + maxfd = -1; + for (i = 0; i < nselect_fds; ++i) + if (select_fds[i] > maxfd) + maxfd = select_fds[i]; + maxfd_changed = 0; + } + return maxfd; +} + + +static int select_watch(long timeout_msecs) +{ + int mfd; + int r, idx, ridx; + + working_rfdset = master_rfdset; + working_wfdset = master_wfdset; + mfd = select_get_maxfd(); + if (timeout_msecs == INFTIM) + r = select(mfd + 1, &working_rfdset, &working_wfdset, (fd_set *) 0, + (struct timeval *) 0); + else + { + struct timeval timeout; + timeout.tv_sec = timeout_msecs / 1000L; + timeout.tv_usec = (timeout_msecs % 1000L) * 1000L; + r = + select(mfd + 1, &working_rfdset, &working_wfdset, (fd_set *) 0, + &timeout); + } + if (r <= 0) + return r; + + ridx = 0; + for (idx = 0; idx < nselect_fds; ++idx) + if (select_check_fd(select_fds[idx])) + { + select_rfdidx[ridx++] = select_fds[idx]; + if (ridx == r) + break; + } + + return ridx; /* should be equal to r */ +} + + +static int select_check_fd(int fd) +{ + switch (fd_rw[fd]) + { + case FDW_READ: + return FD_ISSET(fd, &working_rfdset); + case FDW_WRITE: + return FD_ISSET(fd, &working_wfdset); + default: + return 0; + } +} + + +static int select_get_fd(int ridx) +{ + if (ridx < 0 || ridx >= nfiles) + { + syslog(LOG_ERR, "bad ridx (%d) in select_get_fd!", ridx); + return -1; + } + return select_rfdidx[ridx]; +} + +#endif /* HAVE_SELECT */ + +#endif /* HAVE_POLL */ + +#endif /* HAVE_DEVPOLL */ + +#endif /* HAVE_KQUEUE */ diff --git a/gb.httpd/src/fdwatch.h b/gb.httpd/src/fdwatch.h new file mode 100644 index 00000000..2ed52713 --- /dev/null +++ b/gb.httpd/src/fdwatch.h @@ -0,0 +1,85 @@ +/* fdwatch.h - header file for fdwatch package +** +** This package abstracts the use of the select()/poll()/kqueue() +** system calls. The basic function of these calls is to watch a set +** of file descriptors for activity. select() originated in the BSD world, +** while poll() came from SysV land, and their interfaces are somewhat +** different. fdwatch lets you write your code to a single interface, +** with the portability differences hidden inside the package. +** +** Usage is fairly simple. Call fdwatch_get_nfiles() to initialize +** the package and find out how many fine descriptors are available. +** Then each time through your main loop, call fdwatch_clear(), then +** fdwatch_add_fd() for each of the descriptors you want to watch, +** then call fdwatch() to actually perform the watch. After it returns +** you can check which descriptors are ready via fdwatch_check_fd(). +** +** If your descriptor set hasn't changed from the last time through +** the loop, you can skip calling fdwatch_clear() and fdwatch_add_fd() +** to save a little CPU time. +** +** +** Copyright (c) 1999 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _FDWATCH_H_ +#define _FDWATCH_H_ + +#define FDW_READ 0 +#define FDW_WRITE 1 + +#ifndef INFTIM +#define INFTIM -1 +#endif /* INFTIM */ + +/* Figure out how many file descriptors the system allows, and +** initialize the fdwatch data structures. Returns -1 on failure. +*/ +extern int fdwatch_get_nfiles(void); + +/* Add a descriptor to the watch list. rw is either FDW_READ or FDW_WRITE. */ +extern void fdwatch_add_fd(int fd, void *client_data, int rw); + +/* Delete a descriptor from the watch list. */ +extern void fdwatch_del_fd(int fd); + +/* Do the watch. Return value is the number of descriptors that are ready, +** or 0 if the timeout expired, or -1 on errors. A timeout of INFTIM means +** wait indefinitely. +*/ +extern int fdwatch(long timeout_msecs); + +/* Check if a descriptor was ready. */ +extern int fdwatch_check_fd(int fd); + +/* Get the client data for the next returned event. Returns -1 when there +** are no more events. +*/ +extern void *fdwatch_get_next_client_data(void); + +/* Generate debugging statistics syslog message. */ +extern void fdwatch_logstats(long secs); + +#endif /* _FDWATCH_H_ */ diff --git a/gb.httpd/src/gb.httpd.component b/gb.httpd/src/gb.httpd.component new file mode 100644 index 00000000..2ed93b42 --- /dev/null +++ b/gb.httpd/src/gb.httpd.component @@ -0,0 +1,3 @@ +[Component] +Author=Benoît Minisini +State=Stable diff --git a/gb.httpd/src/libhttpd.c b/gb.httpd/src/libhttpd.c new file mode 100644 index 00000000..4ecec5a0 --- /dev/null +++ b/gb.httpd/src/libhttpd.c @@ -0,0 +1,4376 @@ +/* libhttpd.c - HTTP protocol library +** +** Copyright (c) 1995,1998,1999,2000,2001 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#include "main.h" +#include "thttpd.h" +#include "version.h" + +#ifdef SHOW_SERVER_VERSION +#define EXPOSED_SERVER_SOFTWARE SERVER_SOFTWARE +#else /* SHOW_SERVER_VERSION */ +#define EXPOSED_SERVER_SOFTWARE "gb.httpd" +#endif /* SHOW_SERVER_VERSION */ + +#include +#include +#include + +#include +#include +#include +#include +#ifdef HAVE_MEMORY_H +#include +#endif /* HAVE_MEMORY_H */ +#include +#include +#include +#include +#include +//#include +#include +#include + +#ifdef HAVE_OSRELDATE_H +#include +#endif /* HAVE_OSRELDATE_H */ + +#ifdef HAVE_DIRENT_H +#include +#define NAMLEN(dirent) strlen((dirent)->d_name) +#else +#define dirent direct +#define NAMLEN(dirent) (dirent)->d_namlen +#ifdef HAVE_SYS_NDIR_H +#include +#endif +#ifdef HAVE_SYS_DIR_H +#include +#endif +#ifdef HAVE_NDIR_H +#include +#endif +#endif + +extern char *crypt(const char *key, const char *setting); + +#include "libhttpd.h" +#include "timers.h" +#include "match.h" +#include "tdate_parse.h" + +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif + +#ifndef HAVE_INT64T +typedef long long int64_t; +#endif + +#ifndef HAVE_SOCKLENT +typedef int socklen_t; +#endif + +#ifdef __CYGWIN__ +#define timezone _timezone +#endif + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +extern char **environ; + +/* Forwards. */ +static void check_options(void); +static void free_httpd_server(httpd_server * hs); +static int initialize_listen_socket(httpd_sockaddr * saP); +static void add_response(httpd_conn * hc, char *str); +static void send_mime(httpd_conn * hc, int status, char *title, + char *encodings, char *extraheads, char *type, + off_t length, time_t mod); +static void send_response(httpd_conn * hc, int status, char *title, + char *extraheads, char *form, char *arg); +static void send_response_tail(httpd_conn * hc); +static void defang(char *str, char *dfstr, int dfsize); +#ifdef ERR_DIR +static int send_err_file(httpd_conn * hc, int status, char *title, + char *extraheads, char *filename); +#endif /* ERR_DIR */ +#ifdef AUTH_FILE +static void send_authenticate(httpd_conn * hc, char *realm); +static int b64_decode(const char *str, unsigned char *space, int size); +//static int auth_check (httpd_conn * hc, char *dirname); +//static int auth_check2 (httpd_conn * hc, char *dirname); +#endif /* AUTH_FILE */ +//static void send_dirredirect (httpd_conn * hc); +static int hexit(char c); +static void strdecode(char *to, char *from); +#ifdef GENERATE_INDEXES +static void strencode(char *to, int tosize, char *from); +#endif /* GENERATE_INDEXES */ +#ifdef TILDE_MAP_1 +static int tilde_map_1(httpd_conn * hc); +#endif /* TILDE_MAP_1 */ +#ifdef TILDE_MAP_2 +static int tilde_map_2(httpd_conn * hc); +#endif /* TILDE_MAP_2 */ +static int vhost_map(httpd_conn * hc); +//static char *expand_symlinks (char *path, char **restP, int no_symlink_check, int tildemapped); +static char *bufgets(httpd_conn * hc); +static void de_dotdot(char *file); +static void init_mime(void); +static void figure_mime (httpd_conn * hc); +#ifdef CGI_TIMELIMIT +static void cgi_kill2(ClientData client_data, struct timeval *nowP); +static void cgi_kill(ClientData client_data, struct timeval *nowP); +#endif /* CGI_TIMELIMIT */ +#ifdef GENERATE_INDEXES +static int ls(httpd_conn * hc); +#endif /* GENERATE_INDEXES */ +static char *build_env(char *fmt, char *arg); +#ifdef SERVER_NAME_LIST +static char *hostname_map(char *hostname); +#endif /* SERVER_NAME_LIST */ +static char **make_envp(httpd_conn * hc); +static char **make_argp(httpd_conn * hc); +static void cgi_interpose_input(httpd_conn * hc, int wfd); +static void post_post_garbage_hack(httpd_conn * hc); +static void cgi_interpose_output(httpd_conn * hc, int rfd); +static void cgi_child(httpd_conn * hc); +static int cgi(httpd_conn * hc); +static int really_start_request(httpd_conn * hc, struct timeval *nowP); +static void make_log_entry(httpd_conn * hc, struct timeval *nowP); +static int check_referer(httpd_conn * hc); +static int really_check_referer(httpd_conn * hc); +static int sockaddr_check(httpd_sockaddr * saP); +static size_t sockaddr_len(httpd_sockaddr * saP); +static int my_snprintf(char *str, size_t size, const char *format, ...); +#ifndef HAVE_ATOLL +static long long atoll(const char *str); +#endif /* HAVE_ATOLL */ + + +/* This global keeps track of whether we are in the main process or a +** sub-process. The reason is that httpd_write_response() can get called +** in either context; when it is called from the main process it must use +** non-blocking I/O to avoid stalling the server, but when it is called +** from a sub-process it wants to use blocking I/O so that the whole +** response definitely gets written. So, it checks this variable. A bit +** of a hack but it seems to do the right thing. +*/ +static int sub_process = 0; + + +static void check_options(void) +{ +#if defined(TILDE_MAP_1) && defined(TILDE_MAP_2) + syslog(LOG_CRIT, "both TILDE_MAP_1 and TILDE_MAP_2 are defined"); + exit(1); +#endif /* both */ +} + + +static void free_httpd_server(httpd_server * hs) +{ + if (hs->binding_hostname != (char *) 0) + free((void *) hs->binding_hostname); + if (hs->cwd != (char *) 0) + free((void *) hs->cwd); + if (hs->cgi_pattern != (char *) 0) + free((void *) hs->cgi_pattern); + if (hs->charset != (char *) 0) + free((void *) hs->charset); + if (hs->p3p != (char *) 0) + free((void *) hs->p3p); + if (hs->url_pattern != (char *) 0) + free((void *) hs->url_pattern); + if (hs->local_pattern != (char *) 0) + free((void *) hs->local_pattern); + free((void *) hs); +} + + +httpd_server *httpd_initialize(char *hostname, httpd_sockaddr * sa4P, + httpd_sockaddr * sa6P, unsigned short port, + char *cgi_pattern, int cgi_limit, + int cgi_timelimit, char *charset, char *p3p, + int max_age, char *cwd, int no_log, + FILE * logfp, int no_symlink_check, int vhost, + int global_passwd, char *url_pattern, + char *local_pattern, int no_empty_referers) +{ + httpd_server *hs; + static char ghnbuf[256]; + char *cp; + + check_options(); + + hs = NEW(httpd_server, 1); + if (hs == (httpd_server *) 0) + { + syslog(LOG_CRIT, "out of memory allocating an httpd_server"); + return (httpd_server *) 0; + } + + if (hostname != (char *) 0) + { + hs->binding_hostname = strdup(hostname); + if (hs->binding_hostname == (char *) 0) + { + syslog(LOG_CRIT, "out of memory copying hostname"); + return (httpd_server *) 0; + } + hs->server_hostname = hs->binding_hostname; + } + else + { + hs->binding_hostname = (char *) 0; + hs->server_hostname = (char *) 0; + if (gethostname(ghnbuf, sizeof(ghnbuf)) < 0) + ghnbuf[0] = '\0'; +#ifdef SERVER_NAME_LIST + if (ghnbuf[0] != '\0') + hs->server_hostname = hostname_map(ghnbuf); +#endif /* SERVER_NAME_LIST */ + if (hs->server_hostname == (char *) 0) + { +#ifdef SERVER_NAME + hs->server_hostname = SERVER_NAME; +#else /* SERVER_NAME */ + if (ghnbuf[0] != '\0') + hs->server_hostname = ghnbuf; +#endif /* SERVER_NAME */ + } + } + + hs->port = port; + if (cgi_pattern == (char *) 0) + hs->cgi_pattern = (char *) 0; + else + { + /* Nuke any leading slashes. */ + if (cgi_pattern[0] == '/') + ++cgi_pattern; + hs->cgi_pattern = strdup(cgi_pattern); + if (hs->cgi_pattern == (char *) 0) + { + syslog(LOG_CRIT, "out of memory copying cgi_pattern"); + return (httpd_server *) 0; + } + /* Nuke any leading slashes in the cgi pattern. */ + while ((cp = strstr(hs->cgi_pattern, "|/")) != (char *) 0) + (void) strcpy(cp + 1, cp + 2); + } + hs->cgi_limit = cgi_limit; + hs->cgi_timelimit = cgi_timelimit; + hs->cgi_count = 0; + hs->charset = strdup(charset); + hs->p3p = strdup(p3p); + hs->max_age = max_age; + hs->cwd = strdup(cwd); + if (hs->cwd == (char *) 0) + { + syslog(LOG_CRIT, "out of memory copying cwd"); + return (httpd_server *) 0; + } + if (url_pattern == (char *) 0) + hs->url_pattern = (char *) 0; + else + { + hs->url_pattern = strdup(url_pattern); + if (hs->url_pattern == (char *) 0) + { + syslog(LOG_CRIT, "out of memory copying url_pattern"); + return (httpd_server *) 0; + } + } + if (local_pattern == (char *) 0) + hs->local_pattern = (char *) 0; + else + { + hs->local_pattern = strdup(local_pattern); + if (hs->local_pattern == (char *) 0) + { + syslog(LOG_CRIT, "out of memory copying local_pattern"); + return (httpd_server *) 0; + } + } + hs->no_log = no_log; + hs->logfp = (FILE *) 0; + httpd_set_logfp(hs, logfp); + hs->no_symlink_check = no_symlink_check; + hs->vhost = vhost; + hs->global_passwd = global_passwd; + hs->no_empty_referers = no_empty_referers; + + /* Initialize listen sockets. Try v6 first because of a Linux peculiarity; + ** like some other systems, it has magical v6 sockets that also listen for + ** v4, but in Linux if you bind a v4 socket first then the v6 bind fails. + */ + if (sa6P == (httpd_sockaddr *) 0) + hs->listen6_fd = -1; + else + hs->listen6_fd = initialize_listen_socket(sa6P); + if (sa4P == (httpd_sockaddr *) 0) + hs->listen4_fd = -1; + else + hs->listen4_fd = initialize_listen_socket(sa4P); + /* If we didn't get any valid sockets, fail. */ + if (hs->listen4_fd == -1 && hs->listen6_fd == -1) + { + free_httpd_server(hs); + return (httpd_server *) 0; + } + + init_mime(); + + /* Done initializing. */ + if (hs->binding_hostname == (char *) 0) + syslog(LOG_NOTICE, "starting on port %d", (int) hs->port); + else + syslog(LOG_NOTICE, "starting on %.80s, port %d", httpd_ntoa(hs->listen4_fd != -1 ? sa4P : sa6P), (int) hs->port); + + return hs; +} + + +static int initialize_listen_socket(httpd_sockaddr * saP) +{ + int listen_fd; + int on, flags; + + /* Check sockaddr. */ + if (!sockaddr_check(saP)) + { + syslog(LOG_CRIT, "unknown sockaddr family on listen socket"); + return -1; + } + + /* Create socket. */ + listen_fd = socket(saP->sa.sa_family, SOCK_STREAM, 0); + if (listen_fd < 0) + { + syslog(LOG_CRIT, "socket %.80s - %m", httpd_ntoa(saP)); + return -1; + } + (void) fcntl(listen_fd, F_SETFD, 1); + + /* Allow reuse of local addresses. */ + on = 1; + if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, + sizeof(on)) < 0) + syslog(LOG_CRIT, "setsockopt SO_REUSEADDR - %m"); + + /* Bind to it. */ + if (bind(listen_fd, &saP->sa, sockaddr_len(saP)) < 0) + { + syslog(LOG_CRIT, "bind %.80s - %m", httpd_ntoa(saP)); + (void) close(listen_fd); + return -1; + } + + /* Set the listen file descriptor to no-delay / non-blocking mode. */ + flags = fcntl(listen_fd, F_GETFL, 0); + if (flags == -1) + { + syslog(LOG_CRIT, "fcntl F_GETFL - %m"); + (void) close(listen_fd); + return -1; + } + if (fcntl(listen_fd, F_SETFL, flags | O_NDELAY) < 0) + { + syslog(LOG_CRIT, "fcntl O_NDELAY - %m"); + (void) close(listen_fd); + return -1; + } + + /* Start a listen going. */ + if (listen(listen_fd, LISTEN_BACKLOG) < 0) + { + syslog(LOG_CRIT, "listen - %m"); + (void) close(listen_fd); + return -1; + } + + /* Use accept filtering, if available. */ +#ifdef SO_ACCEPTFILTER + { +#if ( __FreeBSD_version >= 411000 ) +#define ACCEPT_FILTER_NAME "httpready" +#else +#define ACCEPT_FILTER_NAME "dataready" +#endif + struct accept_filter_arg af; + (void) bzero(&af, sizeof(af)); + (void) strcpy(af.af_name, ACCEPT_FILTER_NAME); + (void) setsockopt(listen_fd, SOL_SOCKET, SO_ACCEPTFILTER, (char *) &af, + sizeof(af)); + } +#endif /* SO_ACCEPTFILTER */ + + return listen_fd; +} + + +void httpd_set_logfp(httpd_server * hs, FILE * logfp) +{ + if (hs->logfp != (FILE *) 0) + (void) fclose(hs->logfp); + hs->logfp = logfp; +} + + +void httpd_terminate(httpd_server * hs) +{ + httpd_unlisten(hs); + if (hs->logfp != (FILE *) 0) + (void) fclose(hs->logfp); + free_httpd_server(hs); +} + + +void httpd_unlisten(httpd_server * hs) +{ + if (hs->listen4_fd != -1) + { + (void) close(hs->listen4_fd); + hs->listen4_fd = -1; + } + if (hs->listen6_fd != -1) + { + (void) close(hs->listen6_fd); + hs->listen6_fd = -1; + } +} + + +/* Conditional macro to allow two alternate forms for use in the built-in +** error pages. If EXPLICIT_ERROR_PAGES is defined, the second and more +** explicit error form is used; otherwise, the first and more generic +** form is used. +*/ +#ifdef EXPLICIT_ERROR_PAGES +#define ERROR_FORM(a,b) b +#else /* EXPLICIT_ERROR_PAGES */ +#define ERROR_FORM(a,b) a +#endif /* EXPLICIT_ERROR_PAGES */ + + +static char *ok200title = "OK"; +static char *ok206title = "Partial Content"; + +static char *err302title = "Found"; +//static char *err302form = "The actual URL is '%.80s'.\n"; + +static char *err304title = "Not Modified"; + +char *httpd_err400title = "Bad Request"; +char *httpd_err400form = + "Your request has bad syntax or is inherently impossible to satisfy.\n"; + +#ifdef AUTH_FILE +static char *err401title = "Unauthorized"; +static char *err401form = "Authorization required for the URL '%.80s'.\n"; +#endif /* AUTH_FILE */ + +static char *err403title = "Forbidden"; +#ifndef EXPLICIT_ERROR_PAGES +static char *err403form = + "You do not have permission to get URL '%.80s' from this server.\n"; +#endif /* !EXPLICIT_ERROR_PAGES */ + +static char *err404title = "Not Found"; +static char *err404form = "The requested URL '%.80s' was not found on this server.\n"; + +char *httpd_err408title = "Request Timeout"; +char *httpd_err408form = + "No request appeared within a reasonable time period.\n"; + +static char *err500title = "Internal Error"; +static char *err500form = + "There was an unusual problem serving the requested URL '%.80s'.\n"; + +static char *err501title = "Not Implemented"; +static char *err501form = + "The requested method '%.80s' is not implemented by this server.\n"; + +char *httpd_err503title = "Service Temporarily Overloaded"; +char *httpd_err503form = + "The requested URL '%.80s' is temporarily overloaded. Please try again later.\n"; + + +/* Append a string to the buffer waiting to be sent as response. */ +static void add_response(httpd_conn * hc, char *str) +{ + size_t len; + + len = strlen(str); + httpd_realloc_str(&hc->response, &hc->maxresponse, hc->responselen + len); + (void) memmove(&(hc->response[hc->responselen]), str, len); + hc->responselen += len; +} + +/* Send the buffered response. */ +void httpd_write_response(httpd_conn * hc) +{ + /* If we are in a sub-process, turn off no-delay mode. */ + if (sub_process) + httpd_clear_ndelay(hc->conn_fd); + /* Send the response, if necessary. */ + if (hc->responselen > 0) + { + (void) httpd_write_fully(hc->conn_fd, hc->response, hc->responselen); + hc->responselen = 0; + } +} + + +/* Set no-delay / non-blocking mode on a socket. */ +void httpd_set_ndelay(int fd) +{ + int flags, newflags; + + flags = fcntl(fd, F_GETFL, 0); + if (flags != -1) + { + newflags = flags | (int) O_NDELAY; + if (newflags != flags) + (void) fcntl(fd, F_SETFL, newflags); + } +} + + +/* Clear no-delay / non-blocking mode on a socket. */ +void httpd_clear_ndelay(int fd) +{ + int flags, newflags; + + flags = fcntl(fd, F_GETFL, 0); + if (flags != -1) + { + newflags = flags & ~(int) O_NDELAY; + if (newflags != flags) + (void) fcntl(fd, F_SETFL, newflags); + } +} + + +static void +send_mime(httpd_conn * hc, int status, char *title, char *encodings, + char *extraheads, char *type, off_t length, time_t mod) +{ + time_t now, expires; + const char *rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT"; + char nowbuf[100]; + char modbuf[100]; + char expbuf[100]; + char fixed_type[500]; + char buf[1000]; + int partial_content; + int s100; + + hc->status = status; + hc->bytes_to_send = length; + if (hc->mime_flag) + { + if (status == 200 && hc->got_range && + (hc->last_byte_index >= hc->first_byte_index) && + ((hc->last_byte_index != length - 1) || + (hc->first_byte_index != 0)) && + (hc->range_if == (time_t) - 1 || hc->range_if == hc->sb.mtime)) + { + partial_content = 1; + hc->status = status = 206; + title = ok206title; + } + else + { + partial_content = 0; + hc->got_range = 0; + } + + now = time((time_t *) 0); + if (mod == (time_t) 0) + mod = now; + (void) strftime(nowbuf, sizeof(nowbuf), rfc1123fmt, gmtime(&now)); + (void) strftime(modbuf, sizeof(modbuf), rfc1123fmt, gmtime(&mod)); + (void) my_snprintf(fixed_type, sizeof(fixed_type), type, hc->hs->charset); + (void) my_snprintf(buf, sizeof(buf), + "%.20s %d %s\015\012Server: %s\015\012Content-Type: %s\015\012Date: %s\015\012Last-Modified: %s\015\012Accept-Ranges: bytes\015\012Connection: close\015\012", + hc->protocol, status, title, EXPOSED_SERVER_SOFTWARE, + fixed_type, nowbuf, modbuf); + add_response(hc, buf); + s100 = status / 100; + if (s100 != 2 && s100 != 3) + { + (void) my_snprintf(buf, sizeof(buf), + "Cache-Control: no-cache,no-store\015\012"); + add_response(hc, buf); + } + if (encodings[0] != '\0') + { + (void) my_snprintf(buf, sizeof(buf), + "Content-Encoding: %s\015\012", encodings); + add_response(hc, buf); + } + if (partial_content) + { + (void) my_snprintf(buf, sizeof(buf), + "Content-Range: bytes %lld-%lld/%lld\015\012Content-Length: %lld\015\012", + (int64_t) hc->first_byte_index, + (int64_t) hc->last_byte_index, (int64_t) length, + (int64_t) (hc->last_byte_index - + hc->first_byte_index + 1)); + add_response(hc, buf); + } + else if (length >= 0) + { + (void) my_snprintf(buf, sizeof(buf), + "Content-Length: %lld\015\012", (int64_t) length); + add_response(hc, buf); + } + if (hc->hs->p3p[0] != '\0') + { + (void) my_snprintf(buf, sizeof(buf), "P3P: %s\015\012", hc->hs->p3p); + add_response(hc, buf); + } + if (hc->hs->max_age >= 0) + { + expires = now + hc->hs->max_age; + (void) strftime(expbuf, sizeof(expbuf), rfc1123fmt, gmtime(&expires)); + (void) my_snprintf(buf, sizeof(buf), + "Cache-Control: max-age=%d\015\012Expires: %s\015\012", + hc->hs->max_age, expbuf); + add_response(hc, buf); + } + if (extraheads[0] != '\0') + add_response(hc, extraheads); + add_response(hc, "\015\012"); + } +} + + +static int str_alloc_count = 0; +static size_t str_alloc_size = 0; + +void httpd_realloc_str(char **strP, size_t * maxsizeP, size_t size) +{ + if (*maxsizeP == 0) + { + *maxsizeP = MAX(200, size + 100); + *strP = NEW(char, *maxsizeP + 1); + ++str_alloc_count; + str_alloc_size += *maxsizeP; + } + else if (size > *maxsizeP) + { + str_alloc_size -= *maxsizeP; + *maxsizeP = MAX(*maxsizeP * 2, size * 5 / 4); + *strP = RENEW(*strP, char, *maxsizeP + 1); + str_alloc_size += *maxsizeP; + } + else + return; + if (*strP == (char *) 0) + { + syslog(LOG_ERR, "out of memory reallocating a string to %d bytes", + *maxsizeP); + exit(1); + } +} + + +static void +send_response(httpd_conn * hc, int status, char *title, char *extraheads, + char *form, char *arg) +{ + char defanged_arg[1000], buf[2000]; + + send_mime(hc, status, title, "", extraheads, "text/html; charset=%s", + (off_t) - 1, (time_t) 0); + (void) my_snprintf(buf, sizeof(buf), "\ +\n\ +%d %s\n\ +\n\ +

    %d %s

    \n", status, title, status, title); + add_response(hc, buf); + defang(arg, defanged_arg, sizeof(defanged_arg)); + (void) my_snprintf(buf, sizeof(buf), form, defanged_arg); + add_response(hc, buf); + if (match("**MSIE**", hc->useragent)) + { + int n; + add_response(hc, "\n"); + } + send_response_tail(hc); +} + + +static void send_response_tail(httpd_conn * hc) +{ +#if 0 + char buf[1000]; + + (void) my_snprintf(buf, sizeof(buf), "\ +
    \n\ +
    %s
    \n\ +\n\ +\n", SERVER_ADDRESS, EXPOSED_SERVER_SOFTWARE); + add_response(hc, buf); +#endif +} + + +static void defang(char *str, char *dfstr, int dfsize) +{ + char *cp1; + char *cp2; + + for (cp1 = str, cp2 = dfstr; + *cp1 != '\0' && cp2 - dfstr < dfsize - 5; ++cp1, ++cp2) + { + switch (*cp1) + { + case '<': + *cp2++ = '&'; + *cp2++ = 'l'; + *cp2++ = 't'; + *cp2 = ';'; + break; + case '>': + *cp2++ = '&'; + *cp2++ = 'g'; + *cp2++ = 't'; + *cp2 = ';'; + break; + default: + *cp2 = *cp1; + break; + } + } + *cp2 = '\0'; +} + + +void +httpd_send_err(httpd_conn * hc, int status, char *title, char *extraheads, + char *form, char *arg) +{ +#ifdef ERR_DIR + + char filename[1000]; + + /* Try virtual host error page. */ + if (hc->hs->vhost && hc->hostdir[0] != '\0') + { + (void) my_snprintf(filename, sizeof(filename), + "%s/%s/err%d.html", hc->hostdir, ERR_DIR, status); + if (send_err_file(hc, status, title, extraheads, filename)) + return; + } + + /* Try server-wide error page. */ + (void) my_snprintf(filename, sizeof(filename), + "%s/err%d.html", ERR_DIR, status); + if (send_err_file(hc, status, title, extraheads, filename)) + return; + + /* Fall back on built-in error page. */ + send_response(hc, status, title, extraheads, form, arg); + +#else /* ERR_DIR */ + + send_response(hc, status, title, extraheads, form, arg); + +#endif /* ERR_DIR */ +} + + +#ifdef ERR_DIR +static int +send_err_file(httpd_conn * hc, int status, char *title, char *extraheads, + char *filename) +{ + FILE *fp; + char buf[1000]; + size_t r; + + fp = fopen(filename, "r"); + if (fp == (FILE *) 0) + return 0; + send_mime(hc, status, title, "", extraheads, "text/html; charset=%s", + (off_t) - 1, (time_t) 0); + for (;;) + { + r = fread(buf, 1, sizeof(buf) - 1, fp); + if (r == 0) + break; + buf[r] = '\0'; + add_response(hc, buf); + } + (void) fclose(fp); + +#ifdef ERR_APPEND_SERVER_INFO + send_response_tail(hc); +#endif /* ERR_APPEND_SERVER_INFO */ + + return 1; +} +#endif /* ERR_DIR */ + + +#ifdef AUTH_FILE + +static void send_authenticate(httpd_conn * hc, char *realm) +{ + static char *header; + static size_t maxheader = 0; + static char headstr[] = "WWW-Authenticate: Basic realm=\""; + + httpd_realloc_str(&header, &maxheader, sizeof(headstr) + strlen(realm) + 3); + (void) my_snprintf(header, maxheader, "%s%s\"\015\012", headstr, realm); + httpd_send_err(hc, 401, err401title, header, err401form, hc->encodedurl); + /* If the request was a POST then there might still be data to be read, + ** so we need to do a lingering close. + */ + if (hc->method == METHOD_POST) + hc->should_linger = 1; +} + + +/* Base-64 decoding. This represents binary data as printable ASCII +** characters. Three 8-bit binary bytes are turned into four 6-bit +** values, like so: +** +** [11111111] [22222222] [33333333] +** +** [111111] [112222] [222233] [333333] +** +** Then the 6-bit values are represented using the characters "A-Za-z0-9+/". +*/ + +static int b64_decode_table[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00-0F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10-1F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 20-2F */ + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 30-3F */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 40-4F */ + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 50-5F */ + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 60-6F */ + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 70-7F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80-8F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90-9F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A0-AF */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B0-BF */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C0-CF */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D0-DF */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E0-EF */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* F0-FF */ +}; + +/* Do base-64 decoding on a string. Ignore any non-base64 bytes. +** Return the actual number of bytes generated. The decoded size will +** be at most 3/4 the size of the encoded, and may be smaller if there +** are padding characters (blanks, newlines). +*/ +static int b64_decode(const char *str, unsigned char *space, int size) +{ + const char *cp; + int space_idx, phase; + int d, prev_d = 0; + unsigned char c; + + space_idx = 0; + phase = 0; + for (cp = str; *cp != '\0'; ++cp) + { + d = b64_decode_table[(int) *cp]; + if (d != -1) + { + switch (phase) + { + case 0: + ++phase; + break; + case 1: + c = ((prev_d << 2) | ((d & 0x30) >> 4)); + if (space_idx < size) + space[space_idx++] = c; + ++phase; + break; + case 2: + c = (((prev_d & 0xf) << 4) | ((d & 0x3c) >> 2)); + if (space_idx < size) + space[space_idx++] = c; + ++phase; + break; + case 3: + c = (((prev_d & 0x03) << 6) | d); + if (space_idx < size) + space[space_idx++] = c; + phase = 0; + break; + } + prev_d = d; + } + } + return space_idx; +} + +/* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */ +static int auth_check(httpd_conn * hc, char *dirname) +{ + if (hc->hs->global_passwd) + { + char *topdir; + if (hc->hs->vhost && hc->hostdir[0] != '\0') + topdir = hc->hostdir; + else + topdir = "."; + switch (auth_check2(hc, topdir)) + { + case -1: + return -1; + case 1: + return 1; + } + } + return auth_check2(hc, dirname); +} + +/* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */ +static int auth_check2(httpd_conn * hc, char *dirname) +{ + static char *authpath; + static size_t maxauthpath = 0; + struct stat sb; + char authinfo[500]; + char *authpass; + char *colon; + int l; + FILE *fp; + char line[500]; + char *cryp; + static char *prevauthpath; + static size_t maxprevauthpath = 0; + static time_t prevmtime; + static char *prevuser; + static size_t maxprevuser = 0; + static char *prevcryp; + static size_t maxprevcryp = 0; + + /* Construct auth filename. */ + httpd_realloc_str(&authpath, &maxauthpath, + strlen(dirname) + 1 + sizeof(AUTH_FILE)); + (void) my_snprintf(authpath, maxauthpath, "%s/%s", dirname, AUTH_FILE); + + /* Does this directory have an auth file? */ + if (stat(authpath, &sb) < 0) + /* Nope, let the request go through. */ + return 0; + + /* Does this request contain basic authorization info? */ + if (hc->authorization[0] == '\0' || + strncmp(hc->authorization, "Basic ", 6) != 0) + { + /* Nope, return a 401 Unauthorized. */ + send_authenticate(hc, dirname); + return -1; + } + + /* Decode it. */ + l = b64_decode(&(hc->authorization[6]), (unsigned char *) authinfo, + sizeof(authinfo) - 1); + authinfo[l] = '\0'; + /* Split into user and password. */ + authpass = strchr(authinfo, ':'); + if (authpass == (char *) 0) + { + /* No colon? Bogus auth info. */ + send_authenticate(hc, dirname); + return -1; + } + *authpass++ = '\0'; + /* If there are more fields, cut them off. */ + colon = strchr(authpass, ':'); + if (colon != (char *) 0) + *colon = '\0'; + + /* See if we have a cached entry and can use it. */ + if (maxprevauthpath != 0 && + strcmp(authpath, prevauthpath) == 0 && + sb.st_mtime == prevmtime && strcmp(authinfo, prevuser) == 0) + { + /* Yes. Check against the cached encrypted password. */ + if (strcmp(crypt(authpass, prevcryp), prevcryp) == 0) + { + /* Ok! */ + httpd_realloc_str(&hc->remoteuser, &hc->maxremoteuser, + strlen(authinfo)); + (void) strcpy(hc->remoteuser, authinfo); + return 1; + } + else + { + /* No. */ + send_authenticate(hc, dirname); + return -1; + } + } + + /* Open the password file. */ + fp = fopen(authpath, "r"); + if (fp == (FILE *) 0) + { + /* The file exists but we can't open it? Disallow access. */ + syslog(LOG_ERR, "%.80s auth file %.80s could not be opened - %m", + httpd_ntoa(&hc->client_addr), authpath); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' is protected by an authentication file, but the authentication file cannot be opened.\n"), + hc->encodedurl); + return -1; + } + + /* Read it. */ + while (fgets(line, sizeof(line), fp) != (char *) 0) + { + /* Nuke newline. */ + l = strlen(line); + if (line[l - 1] == '\n') + line[l - 1] = '\0'; + /* Split into user and encrypted password. */ + cryp = strchr(line, ':'); + if (cryp == (char *) 0) + continue; + *cryp++ = '\0'; + /* Is this the right user? */ + if (strcmp(line, authinfo) == 0) + { + /* Yes. */ + (void) fclose(fp); + /* So is the password right? */ + if (strcmp(crypt(authpass, cryp), cryp) == 0) + { + /* Ok! */ + httpd_realloc_str(&hc->remoteuser, &hc->maxremoteuser, strlen(line)); + (void) strcpy(hc->remoteuser, line); + /* And cache this user's info for next time. */ + httpd_realloc_str(&prevauthpath, &maxprevauthpath, strlen(authpath)); + (void) strcpy(prevauthpath, authpath); + prevmtime = sb.st_mtime; + httpd_realloc_str(&prevuser, &maxprevuser, strlen(authinfo)); + (void) strcpy(prevuser, authinfo); + httpd_realloc_str(&prevcryp, &maxprevcryp, strlen(cryp)); + (void) strcpy(prevcryp, cryp); + return 1; + } + else + { + /* No. */ + send_authenticate(hc, dirname); + return -1; + } + } + } + + /* Didn't find that user. Access denied. */ + (void) fclose(fp); + send_authenticate(hc, dirname); + return -1; +} + +#endif /* AUTH_FILE */ + +#if 0 +static void send_dirredirect(httpd_conn * hc) +{ + static char *location; + static char *header; + static size_t maxlocation = 0, maxheader = 0; + static char headstr[] = "Location: "; + + if (hc->query[0] != '\0') + { + char *cp = strchr(hc->encodedurl, '?'); + if (cp != (char *) 0) /* should always find it */ + *cp = '\0'; + httpd_realloc_str(&location, &maxlocation, + strlen(hc->encodedurl) + 2 + strlen(hc->query)); + (void) my_snprintf(location, maxlocation, + "%s/?%s", hc->encodedurl, hc->query); + } + else + { + httpd_realloc_str(&location, &maxlocation, strlen(hc->encodedurl) + 1); + (void) my_snprintf(location, maxlocation, "%s/", hc->encodedurl); + } + httpd_realloc_str(&header, &maxheader, sizeof(headstr) + strlen(location)); + (void) my_snprintf(header, maxheader, "%s%s\015\012", headstr, location); + send_response(hc, 302, err302title, header, err302form, location); +} +#endif + +char *httpd_method_str(int method) +{ + switch (method) + { + case METHOD_GET: + return "GET"; + case METHOD_HEAD: + return "HEAD"; + case METHOD_POST: + return "POST"; + default: + return "UNKNOWN"; + } +} + + +static int hexit(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return 0; /* shouldn't happen, we're guarded by isxdigit() */ +} + + +/* Copies and decodes a string. It's ok for from and to to be the +** same string. +*/ +static void strdecode(char *to, char *from) +{ + for (; *from != '\0'; ++to, ++from) + { + if (from[0] == '%' && isxdigit(from[1]) && isxdigit(from[2])) + { + *to = hexit(from[1]) * 16 + hexit(from[2]); + from += 2; + } + else + *to = *from; + } + *to = '\0'; +} + + +#ifdef GENERATE_INDEXES +/* Copies and encodes a string. */ +static void strencode(char *to, int tosize, char *from) +{ + int tolen; + + for (tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from) + { + if (isalnum(*from) || strchr("/_.-~", *from) != (char *) 0) + { + *to = *from; + ++to; + ++tolen; + } + else + { + (void) sprintf(to, "%%%02x", (int) *from & 0xff); + to += 3; + tolen += 3; + } + } + *to = '\0'; +} +#endif /* GENERATE_INDEXES */ + + +#ifdef TILDE_MAP_1 +/* Map a ~username/whatever URL into /username. */ +static int tilde_map_1(httpd_conn * hc) +{ + static char *temp; + static size_t maxtemp = 0; + int len; + static char *prefix = TILDE_MAP_1; + + len = strlen(hc->expnfilename) - 1; + httpd_realloc_str(&temp, &maxtemp, len); + (void) strcpy(temp, &hc->expnfilename[1]); + httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, + strlen(prefix) + 1 + len); + (void) strcpy(hc->expnfilename, prefix); + if (prefix[0] != '\0') + (void) strcat(hc->expnfilename, "/"); + (void) strcat(hc->expnfilename, temp); + return 1; +} +#endif /* TILDE_MAP_1 */ + +#ifdef TILDE_MAP_2 +/* Map a ~username/whatever URL into /. */ +static int tilde_map_2(httpd_conn * hc) +{ + static char *temp; + static size_t maxtemp = 0; + static char *postfix = TILDE_MAP_2; + char *cp; + struct passwd *pw; + char *alt; + char *rest; + + /* Get the username. */ + httpd_realloc_str(&temp, &maxtemp, strlen(hc->expnfilename) - 1); + (void) strcpy(temp, &hc->expnfilename[1]); + cp = strchr(temp, '/'); + if (cp != (char *) 0) + *cp++ = '\0'; + else + cp = ""; + + /* Get the passwd entry. */ + pw = getpwnam(temp); + if (pw == (struct passwd *) 0) + return 0; + + /* Set up altdir. */ + httpd_realloc_str(&hc->altdir, &hc->maxaltdir, + strlen(pw->pw_dir) + 1 + strlen(postfix)); + (void) strcpy(hc->altdir, pw->pw_dir); + if (postfix[0] != '\0') + { + (void) strcat(hc->altdir, "/"); + (void) strcat(hc->altdir, postfix); + } + alt = expand_symlinks(hc->altdir, &rest, 0, 1); + if (rest[0] != '\0') + return 0; + httpd_realloc_str(&hc->altdir, &hc->maxaltdir, strlen(alt)); + (void) strcpy(hc->altdir, alt); + + /* And the filename becomes altdir plus the post-~ part of the original. */ + httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, + strlen(hc->altdir) + 1 + strlen(cp)); + (void) my_snprintf(hc->expnfilename, hc->maxexpnfilename, + "%s/%s", hc->altdir, cp); + + /* For this type of tilde mapping, we want to defeat vhost mapping. */ + hc->tildemapped = 1; + + return 1; +} +#endif /* TILDE_MAP_2 */ + + +/* Virtual host mapping. */ +static int vhost_map(httpd_conn * hc) +{ + httpd_sockaddr sa; + socklen_t sz; + static char *tempfilename; + static size_t maxtempfilename = 0; + char *cp1; + int len; +#ifdef VHOST_DIRLEVELS + int i; + char *cp2; +#endif /* VHOST_DIRLEVELS */ + + /* Figure out the virtual hostname. */ + if (hc->reqhost[0] != '\0') + hc->hostname = hc->reqhost; + else if (hc->hdrhost[0] != '\0') + hc->hostname = hc->hdrhost; + else + { + sz = sizeof(sa); + if (getsockname(hc->conn_fd, &sa.sa, &sz) < 0) + { + syslog(LOG_ERR, "getsockname - %m"); + return 0; + } + hc->hostname = httpd_ntoa(&sa); + } + /* Pound it to lower case. */ + for (cp1 = hc->hostname; *cp1 != '\0'; ++cp1) + if (isupper(*cp1)) + *cp1 = tolower(*cp1); + + if (hc->tildemapped) + return 1; + + /* Figure out the host directory. */ +#ifdef VHOST_DIRLEVELS + httpd_realloc_str(&hc->hostdir, &hc->maxhostdir, + strlen(hc->hostname) + 2 * VHOST_DIRLEVELS); + if (strncmp(hc->hostname, "www.", 4) == 0) + cp1 = &hc->hostname[4]; + else + cp1 = hc->hostname; + for (cp2 = hc->hostdir, i = 0; i < VHOST_DIRLEVELS; ++i) + { + /* Skip dots in the hostname. If we don't, then we get vhost + ** directories in higher level of filestructure if dot gets + ** involved into path construction. It's `while' used here instead + ** of `if' for it's possible to have a hostname formed with two + ** dots at the end of it. + */ + while (*cp1 == '.') + ++cp1; + /* Copy a character from the hostname, or '_' if we ran out. */ + if (*cp1 != '\0') + *cp2++ = *cp1++; + else + *cp2++ = '_'; + /* Copy a slash. */ + *cp2++ = '/'; + } + (void) strcpy(cp2, hc->hostname); +#else /* VHOST_DIRLEVELS */ + httpd_realloc_str(&hc->hostdir, &hc->maxhostdir, strlen(hc->hostname)); + (void) strcpy(hc->hostdir, hc->hostname); +#endif /* VHOST_DIRLEVELS */ + + /* Prepend hostdir to the filename. */ + len = strlen(hc->expnfilename); + httpd_realloc_str(&tempfilename, &maxtempfilename, len); + (void) strcpy(tempfilename, hc->expnfilename); + httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, + strlen(hc->hostdir) + 1 + len); + (void) strcpy(hc->expnfilename, hc->hostdir); + (void) strcat(hc->expnfilename, "/"); + (void) strcat(hc->expnfilename, tempfilename); + return 1; +} + + +#if 0 +/* Expands all symlinks in the given filename, eliding ..'s and leading /'s. +** Returns the expanded path (pointer to static string), or (char*) 0 on +** errors. Also returns, in the string pointed to by restP, any trailing +** parts of the path that don't exist. +** +** This is a fairly nice little routine. It handles any size filenames +** without excessive mallocs. +*/ +static char *expand_symlinks(char *path, char **restP, int no_symlink_check, + int tildemapped) +{ + static char *checked; + static char *rest; + char link[5000]; + static size_t maxchecked = 0, maxrest = 0; + size_t checkedlen, restlen, linklen, prevcheckedlen, prevrestlen; + int nlinks, i; + char *r; + char *cp1; + char *cp2; + + if (no_symlink_check) + { + /* If we are chrooted, we can actually skip the symlink-expansion, + ** since it's impossible to get out of the tree. However, we still + ** need to do the pathinfo check, and the existing symlink expansion + ** code is a pretty reasonable way to do this. So, what we do is + ** a single stat() of the whole filename - if it exists, then we + ** return it as is with nothing in restP. If it doesn't exist, we + ** fall through to the existing code. + ** + ** One side-effect of this is that users can't symlink to central + ** approved CGIs any more. The workaround is to use the central + ** URL for the CGI instead of a local symlinked one. + */ + struct stat sb; + if (stat(path, &sb) != -1) + { + checkedlen = strlen(path); + httpd_realloc_str(&checked, &maxchecked, checkedlen); + (void) strcpy(checked, path); + /* Trim trailing slashes. */ + while (checked[checkedlen - 1] == '/') + { + checked[checkedlen - 1] = '\0'; + --checkedlen; + } + httpd_realloc_str(&rest, &maxrest, 0); + rest[0] = '\0'; + *restP = rest; + return checked; + } + } + + /* Start out with nothing in checked and the whole filename in rest. */ + httpd_realloc_str(&checked, &maxchecked, 1); + checked[0] = '\0'; + checkedlen = 0; + restlen = strlen(path); + httpd_realloc_str(&rest, &maxrest, restlen); + (void) strcpy(rest, path); + if (rest[restlen - 1] == '/') + rest[--restlen] = '\0'; /* trim trailing slash */ + if (!tildemapped) + /* Remove any leading slashes. */ + while (rest[0] == '/') + { + (void) strcpy(rest, &(rest[1])); + --restlen; + } + r = rest; + nlinks = 0; + + /* While there are still components to check... */ + while (restlen > 0) + { + /* Save current checkedlen in case we get a symlink. Save current + ** restlen in case we get a non-existant component. + */ + prevcheckedlen = checkedlen; + prevrestlen = restlen; + + /* Grab one component from r and transfer it to checked. */ + cp1 = strchr(r, '/'); + if (cp1 != (char *) 0) + { + i = cp1 - r; + if (i == 0) + { + /* Special case for absolute paths. */ + httpd_realloc_str(&checked, &maxchecked, checkedlen + 1); + (void) strncpy(&checked[checkedlen], r, 1); + checkedlen += 1; + } + else if (strncmp(r, "..", MAX(i, 2)) == 0) + { + /* Ignore ..'s that go above the start of the path. */ + if (checkedlen != 0) + { + cp2 = strrchr(checked, '/'); + if (cp2 == (char *) 0) + checkedlen = 0; + else if (cp2 == checked) + checkedlen = 1; + else + checkedlen = cp2 - checked; + } + } + else + { + httpd_realloc_str(&checked, &maxchecked, checkedlen + 1 + i); + if (checkedlen > 0 && checked[checkedlen - 1] != '/') + checked[checkedlen++] = '/'; + (void) strncpy(&checked[checkedlen], r, i); + checkedlen += i; + } + checked[checkedlen] = '\0'; + r += i + 1; + restlen -= i + 1; + } + else + { + /* No slashes remaining, r is all one component. */ + if (strcmp(r, "..") == 0) + { + /* Ignore ..'s that go above the start of the path. */ + if (checkedlen != 0) + { + cp2 = strrchr(checked, '/'); + if (cp2 == (char *) 0) + checkedlen = 0; + else if (cp2 == checked) + checkedlen = 1; + else + checkedlen = cp2 - checked; + checked[checkedlen] = '\0'; + } + } + else + { + httpd_realloc_str(&checked, &maxchecked, checkedlen + 1 + restlen); + if (checkedlen > 0 && checked[checkedlen - 1] != '/') + checked[checkedlen++] = '/'; + (void) strcpy(&checked[checkedlen], r); + checkedlen += restlen; + } + r += restlen; + restlen = 0; + } + + /* Try reading the current filename as a symlink */ + if (checked[0] == '\0') + continue; + linklen = readlink(checked, link, sizeof(link) - 1); + if (linklen == -1) + { + if (errno == EINVAL) + continue; /* not a symlink */ + if (errno == EACCES || errno == ENOENT || errno == ENOTDIR) + { + /* That last component was bogus. Restore and return. */ + *restP = r - (prevrestlen - restlen); + if (prevcheckedlen == 0) + (void) strcpy(checked, "."); + else + checked[prevcheckedlen] = '\0'; + return checked; + } + syslog(LOG_ERR, "readlink %.80s - %m", checked); + return (char *) 0; + } + ++nlinks; + if (nlinks > MAX_LINKS) + { + syslog(LOG_ERR, "too many symlinks in %.80s", path); + return (char *) 0; + } + link[linklen] = '\0'; + if (link[linklen - 1] == '/') + link[--linklen] = '\0'; /* trim trailing slash */ + + /* Insert the link contents in front of the rest of the filename. */ + if (restlen != 0) + { + (void) strcpy(rest, r); + httpd_realloc_str(&rest, &maxrest, restlen + linklen + 1); + for (i = restlen; i >= 0; --i) + rest[i + linklen + 1] = rest[i]; + (void) strcpy(rest, link); + rest[linklen] = '/'; + restlen += linklen + 1; + r = rest; + } + else + { + /* There's nothing left in the filename, so the link contents + ** becomes the rest. + */ + httpd_realloc_str(&rest, &maxrest, linklen); + (void) strcpy(rest, link); + restlen = linklen; + r = rest; + } + + if (rest[0] == '/') + { + /* There must have been an absolute symlink - zero out checked. */ + checked[0] = '\0'; + checkedlen = 0; + } + else + { + /* Re-check this component. */ + checkedlen = prevcheckedlen; + checked[checkedlen] = '\0'; + } + } + + /* Ok. */ + *restP = r; + if (checked[0] == '\0') + (void) strcpy(checked, "."); + return checked; +} +#endif + +int httpd_get_conn(httpd_server * hs, int listen_fd, httpd_conn * hc) +{ + httpd_sockaddr sa; + socklen_t sz; + + if (!hc->initialized) + { + hc->read_size = 0; + httpd_realloc_str(&hc->read_buf, &hc->read_size, 500); + hc->maxdecodedurl = + hc->maxorigfilename = hc->maxexpnfilename = hc->maxencodings = + hc->maxpathinfo = hc->maxquery = hc->maxaccept = + hc->maxaccepte = hc->maxreqhost = hc->maxhostdir = + hc->maxremoteuser = hc->maxresponse = 0; +#ifdef TILDE_MAP_2 + hc->maxaltdir = 0; +#endif /* TILDE_MAP_2 */ + httpd_realloc_str(&hc->decodedurl, &hc->maxdecodedurl, 1); + httpd_realloc_str(&hc->origfilename, &hc->maxorigfilename, 1); + httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, 0); + httpd_realloc_str(&hc->encodings, &hc->maxencodings, 0); + httpd_realloc_str(&hc->pathinfo, &hc->maxpathinfo, 0); + httpd_realloc_str(&hc->query, &hc->maxquery, 0); + httpd_realloc_str(&hc->accept, &hc->maxaccept, 0); + httpd_realloc_str(&hc->accepte, &hc->maxaccepte, 0); + httpd_realloc_str(&hc->reqhost, &hc->maxreqhost, 0); + httpd_realloc_str(&hc->hostdir, &hc->maxhostdir, 0); + httpd_realloc_str(&hc->remoteuser, &hc->maxremoteuser, 0); + httpd_realloc_str(&hc->response, &hc->maxresponse, 0); +#ifdef TILDE_MAP_2 + httpd_realloc_str(&hc->altdir, &hc->maxaltdir, 0); +#endif /* TILDE_MAP_2 */ + hc->initialized = 1; + } + + /* Accept the new connection. */ + sz = sizeof(sa); + hc->conn_fd = accept(listen_fd, &sa.sa, &sz); + if (hc->conn_fd < 0) + { + if (errno == EWOULDBLOCK) + return GC_NO_MORE; + syslog(LOG_ERR, "accept - %m"); + return GC_FAIL; + } + if (!sockaddr_check(&sa)) + { + syslog(LOG_ERR, "unknown sockaddr family"); + close(hc->conn_fd); + hc->conn_fd = -1; + return GC_FAIL; + } + (void) fcntl(hc->conn_fd, F_SETFD, 1); + hc->hs = hs; + (void) memset(&hc->client_addr, 0, sizeof(hc->client_addr)); + (void) memmove(&hc->client_addr, &sa, sockaddr_len(&sa)); + hc->read_idx = 0; + hc->checked_idx = 0; + hc->checked_state = CHST_FIRSTWORD; + hc->method = METHOD_UNKNOWN; + hc->status = 0; + hc->bytes_to_send = 0; + hc->bytes_sent = 0; + hc->encodedurl = ""; + hc->decodedurl[0] = '\0'; + hc->protocol = "UNKNOWN"; + hc->origfilename[0] = '\0'; + hc->expnfilename[0] = '\0'; + hc->encodings[0] = '\0'; + hc->pathinfo[0] = '\0'; + hc->query[0] = '\0'; + hc->referer = ""; + hc->useragent = ""; + hc->accept[0] = '\0'; + hc->accepte[0] = '\0'; + hc->acceptl = ""; + hc->cookie = ""; + hc->contenttype = ""; +#ifdef X_CGI_HEADER + hc->xcgi = ""; +#endif + hc->reqhost[0] = '\0'; + hc->hdrhost = ""; + hc->hostdir[0] = '\0'; + hc->authorization = ""; + hc->remoteuser[0] = '\0'; + hc->response[0] = '\0'; +#ifdef TILDE_MAP_2 + hc->altdir[0] = '\0'; +#endif /* TILDE_MAP_2 */ + hc->responselen = 0; + hc->if_modified_since = (time_t) - 1; + hc->range_if = (time_t) - 1; + hc->contentlength = -1; + hc->type = ""; + hc->hostname = (char *) 0; + hc->mime_flag = 1; + hc->one_one = 0; + hc->got_range = 0; + hc->tildemapped = 0; + hc->first_byte_index = 0; + hc->last_byte_index = -1; + hc->keep_alive = 0; + hc->should_linger = 0; + hc->file_address = (char *) 0; + return GC_OK; +} + + +/* Checks hc->read_buf to see whether a complete request has been read so far; +** either the first line has two words (an HTTP/0.9 request), or the first +** line has three words and there's a blank line present. +** +** hc->read_idx is how much has been read in; hc->checked_idx is how much we +** have checked so far; and hc->checked_state is the current state of the +** finite state machine. +*/ +int httpd_got_request(httpd_conn * hc) +{ + char c; + + for (; hc->checked_idx < hc->read_idx; ++hc->checked_idx) + { + c = hc->read_buf[hc->checked_idx]; + switch (hc->checked_state) + { + case CHST_FIRSTWORD: + switch (c) + { + case ' ': + case '\t': + hc->checked_state = CHST_FIRSTWS; + break; + case '\012': + case '\015': + hc->checked_state = CHST_BOGUS; + return GR_BAD_REQUEST; + } + break; + case CHST_FIRSTWS: + switch (c) + { + case ' ': + case '\t': + break; + case '\012': + case '\015': + hc->checked_state = CHST_BOGUS; + return GR_BAD_REQUEST; + default: + hc->checked_state = CHST_SECONDWORD; + break; + } + break; + case CHST_SECONDWORD: + switch (c) + { + case ' ': + case '\t': + hc->checked_state = CHST_SECONDWS; + break; + case '\012': + case '\015': + /* The first line has only two words - an HTTP/0.9 request. */ + return GR_GOT_REQUEST; + } + break; + case CHST_SECONDWS: + switch (c) + { + case ' ': + case '\t': + break; + case '\012': + case '\015': + hc->checked_state = CHST_BOGUS; + return GR_BAD_REQUEST; + default: + hc->checked_state = CHST_THIRDWORD; + break; + } + break; + case CHST_THIRDWORD: + switch (c) + { + case ' ': + case '\t': + hc->checked_state = CHST_THIRDWS; + break; + case '\012': + hc->checked_state = CHST_LF; + break; + case '\015': + hc->checked_state = CHST_CR; + break; + } + break; + case CHST_THIRDWS: + switch (c) + { + case ' ': + case '\t': + break; + case '\012': + hc->checked_state = CHST_LF; + break; + case '\015': + hc->checked_state = CHST_CR; + break; + default: + hc->checked_state = CHST_BOGUS; + return GR_BAD_REQUEST; + } + break; + case CHST_LINE: + switch (c) + { + case '\012': + hc->checked_state = CHST_LF; + break; + case '\015': + hc->checked_state = CHST_CR; + break; + } + break; + case CHST_LF: + switch (c) + { + case '\012': + /* Two newlines in a row - a blank line - end of request. */ + return GR_GOT_REQUEST; + case '\015': + hc->checked_state = CHST_CR; + break; + default: + hc->checked_state = CHST_LINE; + break; + } + break; + case CHST_CR: + switch (c) + { + case '\012': + hc->checked_state = CHST_CRLF; + break; + case '\015': + /* Two returns in a row - end of request. */ + return GR_GOT_REQUEST; + default: + hc->checked_state = CHST_LINE; + break; + } + break; + case CHST_CRLF: + switch (c) + { + case '\012': + /* Two newlines in a row - end of request. */ + return GR_GOT_REQUEST; + case '\015': + hc->checked_state = CHST_CRLFCR; + break; + default: + hc->checked_state = CHST_LINE; + break; + } + break; + case CHST_CRLFCR: + switch (c) + { + case '\012': + case '\015': + /* Two CRLFs or two CRs in a row - end of request. */ + return GR_GOT_REQUEST; + default: + hc->checked_state = CHST_LINE; + break; + } + break; + case CHST_BOGUS: + return GR_BAD_REQUEST; + } + } + return GR_NO_REQUEST; +} + + +int httpd_parse_request(httpd_conn * hc) +{ + char *buf; + char *method_str; + char *url; + char *protocol; + char *reqhost; + char *eol; + char *cp; + char *pi; + + hc->checked_idx = 0; /* reset */ + method_str = bufgets(hc); + url = strpbrk(method_str, " \t\012\015"); + if (url == (char *) 0) + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + return -1; + } + *url++ = '\0'; + url += strspn(url, " \t\012\015"); + protocol = strpbrk(url, " \t\012\015"); + if (protocol == (char *) 0) + { + protocol = "HTTP/0.9"; + hc->mime_flag = 0; + } + else + { + *protocol++ = '\0'; + protocol += strspn(protocol, " \t\012\015"); + if (*protocol != '\0') + { + eol = strpbrk(protocol, " \t\012\015"); + if (eol != (char *) 0) + *eol = '\0'; + if (strcasecmp(protocol, "HTTP/1.0") != 0) + hc->one_one = 1; + } + } + hc->protocol = protocol; + + /* Check for HTTP/1.1 absolute URL. */ + if (strncasecmp(url, "http://", 7) == 0) + { + if (!hc->one_one) + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + return -1; + } + reqhost = url + 7; + url = strchr(reqhost, '/'); + if (url == (char *) 0) + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + return -1; + } + *url = '\0'; + if (strchr(reqhost, '/') != (char *) 0 || reqhost[0] == '.') + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + return -1; + } + httpd_realloc_str(&hc->reqhost, &hc->maxreqhost, strlen(reqhost)); + (void) strcpy(hc->reqhost, reqhost); + *url = '/'; + } + + if (*url != '/') + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + return -1; + } + + if (strcasecmp(method_str, httpd_method_str(METHOD_GET)) == 0) + hc->method = METHOD_GET; + else if (strcasecmp(method_str, httpd_method_str(METHOD_HEAD)) == 0) + hc->method = METHOD_HEAD; + else if (strcasecmp(method_str, httpd_method_str(METHOD_POST)) == 0) + hc->method = METHOD_POST; + else + { + httpd_send_err(hc, 501, err501title, "", err501form, method_str); + return -1; + } + + hc->encodedurl = url; + httpd_realloc_str(&hc->decodedurl, &hc->maxdecodedurl, + strlen(hc->encodedurl)); + strdecode(hc->decodedurl, hc->encodedurl); + + httpd_realloc_str(&hc->origfilename, &hc->maxorigfilename, + strlen(hc->decodedurl)); + + pi = &hc->decodedurl[1]; + while (*pi == '/') + pi++; + + (void) strcpy(hc->origfilename, pi); + + /* Special case for top-level URL. */ + //if ( hc->origfilename[0] == '\0' ) + // (void) strcpy( hc->origfilename, "." ); + + /* Extract query string from encoded URL. */ + cp = strchr(hc->encodedurl, '?'); + if (cp != (char *) 0) + { + ++cp; + httpd_realloc_str(&hc->query, &hc->maxquery, strlen(cp)); + (void) strcpy(hc->query, cp); + /* Remove query from (decoded) origfilename. */ + cp = strchr(hc->origfilename, '?'); + if (cp != (char *) 0) + *cp = '\0'; + } + + de_dotdot(hc->origfilename); + + if (hc->origfilename[0] == '/' || + (hc->origfilename[0] == '.' && hc->origfilename[1] == '.' && + (hc->origfilename[2] == '\0' || hc->origfilename[2] == '/'))) + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + return -1; + } + + if (hc->mime_flag) + { + /* Read the MIME headers. */ + while ((buf = bufgets(hc)) != (char *) 0) + { + if (buf[0] == '\0') + break; + if (strncasecmp(buf, "Referer:", 8) == 0) + { + cp = &buf[8]; + cp += strspn(cp, " \t"); + hc->referer = cp; + } + else if (strncasecmp(buf, "User-Agent:", 11) == 0) + { + cp = &buf[11]; + cp += strspn(cp, " \t"); + hc->useragent = cp; + } + else if (strncasecmp(buf, "Host:", 5) == 0) + { + cp = &buf[5]; + cp += strspn(cp, " \t"); + hc->hdrhost = cp; + cp = strchr(hc->hdrhost, ':'); + if (cp != (char *) 0) + *cp = '\0'; + if (strchr(hc->hdrhost, '/') != (char *) 0 || hc->hdrhost[0] == '.') + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, + ""); + return -1; + } + } + else if (strncasecmp(buf, "Accept:", 7) == 0) + { + cp = &buf[7]; + cp += strspn(cp, " \t"); + if (hc->accept[0] != '\0') + { + if (strlen(hc->accept) > 5000) + { + syslog(LOG_ERR, "%.80s way too much Accept: data", + httpd_ntoa(&hc->client_addr)); + continue; + } + httpd_realloc_str(&hc->accept, &hc->maxaccept, + strlen(hc->accept) + 2 + strlen(cp)); + (void) strcat(hc->accept, ", "); + } + else + httpd_realloc_str(&hc->accept, &hc->maxaccept, strlen(cp)); + + (void) strcat(hc->accept, cp); + } + else if (strncasecmp(buf, "Accept-Encoding:", 16) == 0) + { + cp = &buf[16]; + cp += strspn(cp, " \t"); + if (hc->accepte[0] != '\0') + { + if (strlen(hc->accepte) > 5000) + { + syslog(LOG_ERR, "%.80s way too much Accept-Encoding: data", + httpd_ntoa(&hc->client_addr)); + continue; + } + httpd_realloc_str(&hc->accepte, &hc->maxaccepte, + strlen(hc->accepte) + 2 + strlen(cp)); + (void) strcat(hc->accepte, ", "); + } + else + httpd_realloc_str(&hc->accepte, &hc->maxaccepte, strlen(cp)); + + (void) strcpy(hc->accepte, cp); + } + else if (strncasecmp(buf, "Accept-Language:", 16) == 0) + { + cp = &buf[16]; + cp += strspn(cp, " \t"); + hc->acceptl = cp; + } + else if (strncasecmp(buf, "If-Modified-Since:", 18) == 0) + { + cp = &buf[18]; + hc->if_modified_since = tdate_parse(cp); + if (hc->if_modified_since == (time_t) - 1) + syslog(LOG_DEBUG, "unparsable time: %.80s", cp); + } + else if (strncasecmp(buf, "Cookie:", 7) == 0) + { + cp = &buf[7]; + cp += strspn(cp, " \t"); + hc->cookie = cp; + } + else if (strncasecmp(buf, "Range:", 6) == 0) + { + /* Only support %d- and %d-%d, not %d-%d,%d-%d or -%d. */ + if (strchr(buf, ',') == (char *) 0) + { + char *cp_dash; + cp = strpbrk(buf, "="); + if (cp != (char *) 0) + { + cp_dash = strchr(cp + 1, '-'); + if (cp_dash != (char *) 0 && cp_dash != cp + 1) + { + *cp_dash = '\0'; + hc->got_range = 1; + hc->first_byte_index = atoll(cp + 1); + if (hc->first_byte_index < 0) + hc->first_byte_index = 0; + if (isdigit((int) cp_dash[1])) + { + hc->last_byte_index = atoll(cp_dash + 1); + if (hc->last_byte_index < 0) + hc->last_byte_index = -1; + } + } + } + } + } + else if (strncasecmp(buf, "Range-If:", 9) == 0 || + strncasecmp(buf, "If-Range:", 9) == 0) + { + cp = &buf[9]; + hc->range_if = tdate_parse(cp); + if (hc->range_if == (time_t) - 1) + syslog(LOG_DEBUG, "unparsable time: %.80s", cp); + } + else if (strncasecmp(buf, "Content-Type:", 13) == 0) + { + cp = &buf[13]; + cp += strspn(cp, " \t"); + hc->contenttype = cp; + } + else if (strncasecmp(buf, "Content-Length:", 15) == 0) + { + cp = &buf[15]; + hc->contentlength = atol(cp); + } + else if (strncasecmp(buf, "Authorization:", 14) == 0) + { + cp = &buf[14]; + cp += strspn(cp, " \t"); + hc->authorization = cp; + } + else if (strncasecmp(buf, "Connection:", 11) == 0) + { + cp = &buf[11]; + cp += strspn(cp, " \t"); + if (strcasecmp(cp, "keep-alive") == 0) + hc->keep_alive = 1; + } +#ifdef X_CGI_HEADER + else if (strncasecmp(buf, "X-Cgi:", 6) == 0) + { + cp = &buf[6]; + cp += strspn(cp, " \t"); + hc->xcgi = cp; + } +#endif +#ifdef LOG_UNKNOWN_HEADERS + else if (strncasecmp(buf, "Accept-Charset:", 15) == 0 || + strncasecmp(buf, "Accept-Language:", 16) == 0 || + strncasecmp(buf, "Agent:", 6) == 0 || + strncasecmp(buf, "Cache-Control:", 14) == 0 || + strncasecmp(buf, "Cache-Info:", 11) == 0 || + strncasecmp(buf, "Charge-To:", 10) == 0 || + strncasecmp(buf, "Client-IP:", 10) == 0 || + strncasecmp(buf, "Date:", 5) == 0 || + strncasecmp(buf, "Extension:", 10) == 0 || + strncasecmp(buf, "Forwarded:", 10) == 0 || + strncasecmp(buf, "From:", 5) == 0 || + strncasecmp(buf, "HTTP-Version:", 13) == 0 || + strncasecmp(buf, "Max-Forwards:", 13) == 0 || + strncasecmp(buf, "Message-Id:", 11) == 0 || + strncasecmp(buf, "MIME-Version:", 13) == 0 || + strncasecmp(buf, "Negotiate:", 10) == 0 || + strncasecmp(buf, "Pragma:", 7) == 0 || + strncasecmp(buf, "Proxy-Agent:", 12) == 0 || + strncasecmp(buf, "Proxy-Connection:", 17) == 0 || + strncasecmp(buf, "Security-Scheme:", 16) == 0 || + strncasecmp(buf, "Session-Id:", 11) == 0 || + strncasecmp(buf, "UA-Color:", 9) == 0 || + strncasecmp(buf, "UA-CPU:", 7) == 0 || + strncasecmp(buf, "UA-Disp:", 8) == 0 || + strncasecmp(buf, "UA-OS:", 6) == 0 || + strncasecmp(buf, "UA-Pixels:", 10) == 0 || + strncasecmp(buf, "User:", 5) == 0 || + strncasecmp(buf, "Via:", 4) == 0 || + strncasecmp(buf, "X-", 2) == 0) + ; /* ignore */ + else + syslog(LOG_DEBUG, "unknown request header: %.80s", buf); +#endif /* LOG_UNKNOWN_HEADERS */ + } + } + + if (hc->one_one) + { + /* Check that HTTP/1.1 requests specify a host, as required. */ + if (hc->reqhost[0] == '\0' && hc->hdrhost[0] == '\0') + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + return -1; + } + + /* If the client wants to do keep-alives, it might also be doing + ** pipelining. There's no way for us to tell. Since we don't + ** implement keep-alives yet, if we close such a connection there + ** might be unread pipelined requests waiting. So, we have to + ** do a lingering close. + */ + if (hc->keep_alive) + hc->should_linger = 1; + } + + /* Ok, the request has been parsed. Now we resolve stuff that + ** may require the entire request. + */ + + /* Copy original filename to expanded filename. */ + httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, + strlen(hc->origfilename)); + (void) strcpy(hc->expnfilename, hc->origfilename); + + /* Tilde mapping. */ + if (hc->expnfilename[0] == '~') + { +#ifdef TILDE_MAP_1 + if (!tilde_map_1(hc)) + { + httpd_send_err(hc, 404, err404title, "", err404form, hc->encodedurl); + return -1; + } +#endif /* TILDE_MAP_1 */ +#ifdef TILDE_MAP_2 + if (!tilde_map_2(hc)) + { + httpd_send_err(hc, 404, err404title, "", err404form, hc->encodedurl); + return -1; + } +#endif /* TILDE_MAP_2 */ + } + + /* Virtual host mapping. */ + if (hc->hs->vhost) + if (!vhost_map(hc)) + { + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + return -1; + } + +#if 0 + /* Expand all symbolic links in the filename. This also gives us + ** any trailing non-existing components, for pathinfo. + */ + cp = + expand_symlinks(hc->expnfilename, &pi, hc->hs->no_symlink_check, + hc->tildemapped); + + //fprintf(stderr, "expnfilename: %s -> cp = %s pi = %s\n", hc->expnfilename, cp, pi); + + if (cp == (char *) 0) + { + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + return -1; + } + httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, strlen(cp)); + (void) strcpy(hc->expnfilename, cp); + httpd_realloc_str(&hc->pathinfo, &hc->maxpathinfo, strlen(pi)); + (void) strcpy(hc->pathinfo, pi); + + /* Remove pathinfo stuff from the original filename too. */ + if (hc->pathinfo[0] != '\0') + { + int i; + i = strlen(hc->origfilename) - strlen(hc->pathinfo); + if (i > 0 && strcmp(&hc->origfilename[i], hc->pathinfo) == 0) + hc->origfilename[i - 1] = '\0'; + } + + /* If the expanded filename is an absolute path, check that it's still + ** within the current directory or the alternate directory. + */ + if (hc->expnfilename[0] == '/') + { + if (strncmp(hc->expnfilename, hc->hs->cwd, strlen(hc->hs->cwd)) == 0) + { + /* Elide the current directory. */ + (void) strcpy(hc->expnfilename, &hc->expnfilename[strlen(hc->hs->cwd)]); + } +#ifdef TILDE_MAP_2 + else if (hc->altdir[0] != '\0' && + (strncmp(hc->expnfilename, hc->altdir, + strlen(hc->altdir)) == 0 && + (hc->expnfilename[strlen(hc->altdir)] == '\0' || + hc->expnfilename[strlen(hc->altdir)] == '/'))) + { + } +#endif /* TILDE_MAP_2 */ + else + { + syslog(LOG_NOTICE, "%.80s URL \"%.80s\" goes outside the web tree", + httpd_ntoa(&hc->client_addr), hc->encodedurl); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' resolves to a file outside the permitted web server directory tree.\n"), + hc->encodedurl); + return -1; + } + } +#endif + + return 0; +} + + +static char *bufgets(httpd_conn * hc) +{ + int i; + char c; + + for (i = hc->checked_idx; hc->checked_idx < hc->read_idx; ++hc->checked_idx) + { + c = hc->read_buf[hc->checked_idx]; + if (c == '\012' || c == '\015') + { + hc->read_buf[hc->checked_idx] = '\0'; + ++hc->checked_idx; + if (c == '\015' && hc->checked_idx < hc->read_idx && + hc->read_buf[hc->checked_idx] == '\012') + { + hc->read_buf[hc->checked_idx] = '\0'; + ++hc->checked_idx; + } + return &(hc->read_buf[i]); + } + } + return (char *) 0; +} + + +static void de_dotdot(char *file) +{ + char *cp; + char *cp2; + int l; + + /* Collapse any multiple / sequences. */ + while ((cp = strstr(file, "//")) != (char *) 0) + { + for (cp2 = cp + 2; *cp2 == '/'; ++cp2) + continue; + (void) strcpy(cp + 1, cp2); + } + + /* Remove leading ./ and any /./ sequences. */ + while (strncmp(file, "./", 2) == 0) + (void) strcpy(file, file + 2); + while ((cp = strstr(file, "/./")) != (char *) 0) + (void) strcpy(cp, cp + 2); + + /* Alternate between removing leading ../ and removing xxx/../ */ + for (;;) + { + while (strncmp(file, "../", 3) == 0) + (void) strcpy(file, file + 3); + cp = strstr(file, "/../"); + if (cp == (char *) 0) + break; + for (cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2) + continue; + (void) strcpy(cp2 + 1, cp + 4); + } + + /* Also elide any xxx/.. at the end. */ + while ((l = strlen(file)) > 3 && strcmp((cp = file + l - 3), "/..") == 0) + { + for (cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2) + continue; + if (cp2 < file) + break; + *cp2 = '\0'; + } +} + + +void httpd_close_conn(httpd_conn * hc, struct timeval *nowP) +{ + make_log_entry(hc, nowP); + + if (hc->file_address != (char *) 0) + { + GB.ReleaseFile(hc->file_address, hc->file_len); + //mmc_unmap(hc->file_address, &(hc->sb), nowP); + hc->file_address = (char *) 0; + } + if (hc->conn_fd >= 0) + { + (void) close(hc->conn_fd); + hc->conn_fd = -1; + } +} + +void httpd_destroy_conn(httpd_conn * hc) +{ + if (hc->initialized) + { + free((void *) hc->read_buf); + free((void *) hc->decodedurl); + free((void *) hc->origfilename); + free((void *) hc->expnfilename); + free((void *) hc->encodings); + free((void *) hc->pathinfo); + free((void *) hc->query); + free((void *) hc->accept); + free((void *) hc->accepte); + free((void *) hc->reqhost); + free((void *) hc->hostdir); + free((void *) hc->remoteuser); + free((void *) hc->response); +#ifdef TILDE_MAP_2 + free((void *) hc->altdir); +#endif /* TILDE_MAP_2 */ + hc->initialized = 0; + } +} + + +struct mime_entry +{ + char *ext; + size_t ext_len; + char *val; + size_t val_len; +}; +static struct mime_entry enc_tab[] = { +#include "mime_encodings.h" +}; + +static const int n_enc_tab = sizeof(enc_tab) / sizeof(*enc_tab); +static struct mime_entry typ_tab[] = { +#include "mime_types.h" +}; + +static const int n_typ_tab = sizeof(typ_tab) / sizeof(*typ_tab); + + +/* qsort comparison routine - declared old-style on purpose, for portability. */ +static int ext_compare(a, b) + struct mime_entry *a; + struct mime_entry *b; +{ + return strcmp(a->ext, b->ext); +} + + +static void init_mime(void) +{ + int i; + + /* Sort the tables so we can do binary search. */ + qsort(enc_tab, n_enc_tab, sizeof(*enc_tab), ext_compare); + qsort(typ_tab, n_typ_tab, sizeof(*typ_tab), ext_compare); + + /* Fill in the lengths. */ + for (i = 0; i < n_enc_tab; ++i) + { + enc_tab[i].ext_len = strlen(enc_tab[i].ext); + enc_tab[i].val_len = strlen(enc_tab[i].val); + } + for (i = 0; i < n_typ_tab; ++i) + { + typ_tab[i].ext_len = strlen(typ_tab[i].ext); + typ_tab[i].val_len = strlen(typ_tab[i].val); + } + +} + +/* Figure out MIME encodings and type based on the filename. Multiple +** encodings are separated by commas, and are listed in the order in +** which they were applied to the file. +*/ +static void figure_mime(httpd_conn * hc) +{ + char *prev_dot; + char *dot; + char *ext; + int me_indexes[100], n_me_indexes; + size_t ext_len, encodings_len; + int i, top, bot, mid; + int r; + char *default_type = "text/plain; charset=%s"; + + /* Peel off encoding extensions until there aren't any more. */ + n_me_indexes = 0; + for (prev_dot = &hc->expnfilename[strlen(hc->expnfilename)];; + prev_dot = dot) + { + for (dot = prev_dot - 1; dot >= hc->expnfilename && *dot != '.'; --dot) + ; + if (dot < hc->expnfilename) + { + /* No dot found. No more encoding extensions, and no type + ** extension either. + */ + hc->type = default_type; + goto done; + } + ext = dot + 1; + ext_len = prev_dot - ext; + /* Search the encodings table. Linear search is fine here, there + ** are only a few entries. + */ + for (i = 0; i < n_enc_tab; ++i) + { + if (ext_len == enc_tab[i].ext_len + && strncasecmp(ext, enc_tab[i].ext, ext_len) == 0) + { + if (n_me_indexes < sizeof(me_indexes) / sizeof(*me_indexes)) + { + me_indexes[n_me_indexes] = i; + ++n_me_indexes; + } + goto next; + } + } + /* No encoding extension found. Break and look for a type extension. */ + break; + + next:; + } + + /* Binary search for a matching type extension. */ + top = n_typ_tab - 1; + bot = 0; + while (top >= bot) + { + mid = (top + bot) / 2; + r = strncasecmp(ext, typ_tab[mid].ext, ext_len); + if (r < 0) + top = mid - 1; + else if (r > 0) + bot = mid + 1; + else if (ext_len < typ_tab[mid].ext_len) + top = mid - 1; + else if (ext_len > typ_tab[mid].ext_len) + bot = mid + 1; + else + { + hc->type = typ_tab[mid].val; + goto done; + } + } + hc->type = default_type; + +done: + + /* The last thing we do is actually generate the mime-encoding header. */ + hc->encodings[0] = '\0'; + encodings_len = 0; + for (i = n_me_indexes - 1; i >= 0; --i) + { + httpd_realloc_str(&hc->encodings, &hc->maxencodings, + encodings_len + enc_tab[me_indexes[i]].val_len + 1); + if (hc->encodings[0] != '\0') + { + (void) strcpy(&hc->encodings[encodings_len], ","); + ++encodings_len; + } + (void) strcpy(&hc->encodings[encodings_len], enc_tab[me_indexes[i]].val); + encodings_len += enc_tab[me_indexes[i]].val_len; + } + +} + +#ifdef CGI_TIMELIMIT +static void cgi_kill2(ClientData client_data, struct timeval *nowP) +{ + pid_t pid; + + pid = (pid_t) client_data.i; + if (kill(pid, SIGKILL) == 0) + syslog(LOG_ERR, "hard-killed CGI process %d", pid); +} + +static void cgi_kill(ClientData client_data, struct timeval *nowP) +{ + pid_t pid; + + pid = (pid_t) client_data.i; + if (kill(pid, SIGINT) == 0) + { + syslog(LOG_ERR, "killed CGI process %d", pid); + /* In case this isn't enough, schedule an uncatchable kill. */ + if (tmr_create(nowP, cgi_kill2, client_data, 5 * 1000L, 0) == (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(cgi_kill2) failed"); + exit(1); + } + } +} +#endif /* CGI_TIMELIMIT */ + + +#ifdef GENERATE_INDEXES + +/* qsort comparison routine - declared old-style on purpose, for portability. */ +static int name_compare(a, b) + char **a; + char **b; +{ + return strcmp(*a, *b); +} + +#if 0 +static int ls(httpd_conn * hc) +{ + DIR *dirp; + struct dirent *de; + int namlen; + static int maxnames = 0; + int nnames; + static char *names; + static char **nameptrs; + static char *name; + static size_t maxname = 0; + static char *rname; + static size_t maxrname = 0; + static char *encrname; + static size_t maxencrname = 0; + FILE *fp; + int i, r; + struct stat sb; + struct stat lsb; + char modestr[20]; + char *linkprefix; + char link[MAXPATHLEN + 1]; + int linklen; + char *fileclass; + time_t now; + char *timestr; + ClientData client_data; + + dirp = opendir(hc->expnfilename); + if (dirp == (DIR *) 0) + { + syslog(LOG_ERR, "opendir %.80s - %m", hc->expnfilename); + httpd_send_err(hc, 404, err404title, "", err404form, hc->encodedurl); + return -1; + } + + if (hc->method == METHOD_HEAD) + { + closedir(dirp); + send_mime(hc, 200, ok200title, "", "", "text/html; charset=%s", + (off_t) - 1, hc->sb.mtime); + } + else if (hc->method == METHOD_GET) + { + if (hc->hs->cgi_limit != 0 && hc->hs->cgi_count >= hc->hs->cgi_limit) + { + closedir(dirp); + httpd_send_err(hc, 503, httpd_err503title, "", httpd_err503form, + hc->encodedurl); + return -1; + } + ++hc->hs->cgi_count; + r = fork(); + if (r < 0) + { + syslog(LOG_ERR, "fork - %m"); + closedir(dirp); + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + return -1; + } + if (r == 0) + { + /* Child process. */ + sub_process = 1; + httpd_unlisten(hc->hs); + send_mime(hc, 200, ok200title, "", "", "text/html; charset=%s", + (off_t) - 1, hc->sb.mtime); + httpd_write_response(hc); + +#ifdef CGI_NICE + /* Set priority. */ + (void) nice(CGI_NICE); +#endif /* CGI_NICE */ + + /* Open a stdio stream so that we can use fprintf, which is more + ** efficient than a bunch of separate write()s. We don't have + ** to worry about double closes or file descriptor leaks cause + ** we're in a subprocess. + */ + fp = fdopen(hc->conn_fd, "w"); + if (fp == (FILE *) 0) + { + syslog(LOG_ERR, "fdopen - %m"); + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + httpd_write_response(hc); + closedir(dirp); + exit(1); + } + + (void) fprintf(fp, "\ +\n\ +Index of %.80s\n\ +\n\ +

    Index of %.80s

    \n\ +
    \n\
    +mode  links  bytes  last-changed  name\n\
    +
    ", hc->encodedurl, hc->encodedurl); + + /* Read in names. */ + nnames = 0; + while ((de = readdir(dirp)) != 0) /* dirent or direct */ + { + if (nnames >= maxnames) + { + if (maxnames == 0) + { + maxnames = 100; + names = NEW(char, maxnames * (MAXPATHLEN + 1)); + nameptrs = NEW(char *, maxnames); + } + else + { + maxnames *= 2; + names = RENEW(names, char, maxnames * (MAXPATHLEN + 1)); + nameptrs = RENEW(nameptrs, char *, maxnames); + } + if (names == (char *) 0 || nameptrs == (char **) 0) + { + syslog(LOG_ERR, "out of memory reallocating directory names"); + exit(1); + } + for (i = 0; i < maxnames; ++i) + nameptrs[i] = &names[i * (MAXPATHLEN + 1)]; + } + namlen = NAMLEN(de); + (void) strncpy(nameptrs[nnames], de->d_name, namlen); + nameptrs[nnames][namlen] = '\0'; + ++nnames; + } + closedir(dirp); + + /* Sort the names. */ + qsort(nameptrs, nnames, sizeof(*nameptrs), name_compare); + + /* Generate output. */ + for (i = 0; i < nnames; ++i) + { + httpd_realloc_str(&name, &maxname, + strlen(hc->expnfilename) + 1 + strlen(nameptrs[i])); + httpd_realloc_str(&rname, &maxrname, + strlen(hc->origfilename) + 1 + strlen(nameptrs[i])); + if (hc->expnfilename[0] == '\0' || strcmp(hc->expnfilename, ".") == 0) + { + (void) strcpy(name, nameptrs[i]); + (void) strcpy(rname, nameptrs[i]); + } + else + { + (void) my_snprintf(name, maxname, + "%s/%s", hc->expnfilename, nameptrs[i]); + if (strcmp(hc->origfilename, ".") == 0) + (void) my_snprintf(rname, maxrname, "%s", nameptrs[i]); + else + (void) my_snprintf(rname, maxrname, + "%s%s", hc->origfilename, nameptrs[i]); + } + httpd_realloc_str(&encrname, &maxencrname, 3 * strlen(rname) + 1); + strencode(encrname, maxencrname, rname); + + if (stat(name, &sb) < 0 || lstat(name, &lsb) < 0) + continue; + + linkprefix = ""; + link[0] = '\0'; + /* Break down mode word. First the file type. */ + switch (lsb.st_mode & S_IFMT) + { + case S_IFIFO: + modestr[0] = 'p'; + break; + case S_IFCHR: + modestr[0] = 'c'; + break; + case S_IFDIR: + modestr[0] = 'd'; + break; + case S_IFBLK: + modestr[0] = 'b'; + break; + case S_IFREG: + modestr[0] = '-'; + break; + case S_IFSOCK: + modestr[0] = 's'; + break; + case S_IFLNK: + modestr[0] = 'l'; + linklen = readlink(name, link, sizeof(link) - 1); + if (linklen != -1) + { + link[linklen] = '\0'; + linkprefix = " -> "; + } + break; + default: + modestr[0] = '?'; + break; + } + /* Now the world permissions. Owner and group permissions + ** are not of interest to web clients. + */ + modestr[1] = (lsb.st_mode & S_IROTH) ? 'r' : '-'; + modestr[2] = (lsb.st_mode & S_IWOTH) ? 'w' : '-'; + modestr[3] = (lsb.st_mode & S_IXOTH) ? 'x' : '-'; + modestr[4] = '\0'; + + /* We also leave out the owner and group name, they are + ** also not of interest to web clients. Plus if we're + ** running under chroot(), they would require a copy + ** of /etc/passwd and /etc/group, which we want to avoid. + */ + + /* Get time string. */ + now = time((time_t *) 0); + timestr = ctime(&lsb.st_mtime); + timestr[0] = timestr[4]; + timestr[1] = timestr[5]; + timestr[2] = timestr[6]; + timestr[3] = ' '; + timestr[4] = timestr[8]; + timestr[5] = timestr[9]; + timestr[6] = ' '; + if (now - lsb.st_mtime > 60 * 60 * 24 * 182) /* 1/2 year */ + { + timestr[7] = ' '; + timestr[8] = timestr[20]; + timestr[9] = timestr[21]; + timestr[10] = timestr[22]; + timestr[11] = timestr[23]; + } + else + { + timestr[7] = timestr[11]; + timestr[8] = timestr[12]; + timestr[9] = ':'; + timestr[10] = timestr[14]; + timestr[11] = timestr[15]; + } + timestr[12] = '\0'; + + /* The ls -F file class. */ + switch (sb.st_mode & S_IFMT) + { + case S_IFDIR: + fileclass = "/"; + break; + case S_IFSOCK: + fileclass = "="; + break; + case S_IFLNK: + fileclass = "@"; + break; + default: + fileclass = (sb.st_mode & S_IXOTH) ? "*" : ""; + break; + } + + /* And print. */ + (void) fprintf(fp, + "%s %3ld %10ld %s %.80s%s%s%s\n", + modestr, (long) lsb.st_nlink, (int64_t) lsb.st_size, + timestr, encrname, S_ISDIR(sb.st_mode) ? "/" : "", + nameptrs[i], linkprefix, link, fileclass); + } + + (void) fprintf(fp, "
    \n\n"); + (void) fclose(fp); + exit(0); + } + + /* Parent process. */ + closedir(dirp); + syslog(LOG_INFO, "spawned indexing process %d for directory '%.200s'", r, + hc->expnfilename); +#ifdef CGI_TIMELIMIT + /* Schedule a kill for the child process, in case it runs too long */ + if (hc->hs->cgi_timelimit) + { + client_data.i = r; + if (tmr_create + ((struct timeval *) 0, cgi_kill, client_data, + hc->hs->cgi_timelimit * 1000L, 0) == (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(cgi_kill ls) failed"); + exit(1); + } + } +#endif /* CGI_TIMELIMIT */ + hc->status = 200; + hc->bytes_sent = CGI_BYTECOUNT; + hc->should_linger = 0; + } + else + { + closedir(dirp); + httpd_send_err(hc, 501, err501title, "", err501form, + httpd_method_str(hc->method)); + return -1; + } + + return 0; +} +#endif + +#endif /* GENERATE_INDEXES */ + + +static char *build_env(char *fmt, char *arg) +{ + char *cp; + size_t size; + static char *buf; + static size_t maxbuf = 0; + + size = strlen(fmt) + strlen(arg); + if (size > maxbuf) + httpd_realloc_str(&buf, &maxbuf, size); + + (void) my_snprintf(buf, maxbuf, fmt, arg); + cp = strdup(buf); + if (cp == (char *) 0) + { + syslog(LOG_ERR, "out of memory copying environment variable"); + exit(1); + } + //fprintf(stderr, "build_env: %s\n", cp); + return cp; +} + + +#ifdef SERVER_NAME_LIST +static char *hostname_map(char *hostname) +{ + int len, n; + static char *list[] = { SERVER_NAME_LIST }; + + len = strlen(hostname); + for (n = sizeof(list) / sizeof(*list) - 1; n >= 0; --n) + if (strncasecmp(hostname, list[n], len) == 0) + if (list[n][len] == '/') /* check in case of a substring match */ + return &list[n][len + 1]; + return (char *) 0; +} +#endif /* SERVER_NAME_LIST */ + + +/* Set up environment variables. Be real careful here to avoid +** letting malicious clients overrun a buffer. We don't have +** to worry about freeing stuff since we're a sub-process. +*/ +static char **make_envp(httpd_conn * hc) +{ + static char *envp[50]; + int envn; + char *cp; + char buf[256]; + + envn = 0; + //envp[envn++] = build_env("PATH=%s", CGI_PATH); +#ifdef CGI_LD_LIBRARY_PATH + envp[envn++] = build_env("LD_LIBRARY_PATH=%s", CGI_LD_LIBRARY_PATH); +#endif /* CGI_LD_LIBRARY_PATH */ + envp[envn++] = build_env("SERVER_SOFTWARE=%s", SERVER_SOFTWARE); + /* If vhosting, use that server-name here. */ + if (hc->hs->vhost && hc->hostname != (char *) 0) + cp = hc->hostname; + else + cp = hc->hs->server_hostname; + if (cp != (char *) 0) + envp[envn++] = build_env("SERVER_NAME=%s", cp); + envp[envn++] = "GATEWAY_INTERFACE=CGI/1.1"; + envp[envn++] = build_env("SERVER_PROTOCOL=%s", hc->protocol); + (void) my_snprintf(buf, sizeof(buf), "%d", (int) hc->hs->port); + envp[envn++] = build_env("SERVER_PORT=%s", buf); + envp[envn++] = build_env("REQUEST_METHOD=%s", httpd_method_str(hc->method)); + + /*if ( hc->pathinfo[0] != '\0' ) + { + char* cp2; + size_t l; + envp[envn++] = build_env( "PATH_INFO=/%s", hc->pathinfo ); + l = strlen( hc->hs->cwd ) + strlen( hc->pathinfo ) + 1; + cp2 = NEW( char, l ); + if ( cp2 != (char*) 0 ) + { + (void) my_snprintf( cp2, l, "%s%s", hc->hs->cwd, hc->pathinfo ); + envp[envn++] = build_env( "PATH_TRANSLATED=%s", cp2 ); + } + } */ + + envp[envn++] = "SCRIPT_NAME=/"; + + envp[envn++] = build_env("PATH_INFO=/%s", hc->expnfilename); + //envp[envn++] = build_env( "SCRIPT_NAME=%s", ""); + + if (hc->query[0] != '\0') + envp[envn++] = build_env("QUERY_STRING=%s", hc->query); + envp[envn++] = build_env("REMOTE_ADDR=%s", httpd_ntoa(&hc->client_addr)); + if (hc->referer[0] != '\0') + envp[envn++] = build_env("HTTP_REFERER=%s", hc->referer); + if (hc->useragent[0] != '\0') + envp[envn++] = build_env("HTTP_USER_AGENT=%s", hc->useragent); + if (hc->accept[0] != '\0') + envp[envn++] = build_env("HTTP_ACCEPT=%s", hc->accept); + if (hc->accepte[0] != '\0') + envp[envn++] = build_env("HTTP_ACCEPT_ENCODING=%s", hc->accepte); + if (hc->acceptl[0] != '\0') + envp[envn++] = build_env("HTTP_ACCEPT_LANGUAGE=%s", hc->acceptl); + if (hc->cookie[0] != '\0') + envp[envn++] = build_env("HTTP_COOKIE=%s", hc->cookie); + if (hc->contenttype[0] != '\0') + envp[envn++] = build_env("CONTENT_TYPE=%s", hc->contenttype); + if (hc->hdrhost[0] != '\0') + envp[envn++] = build_env("HTTP_HOST=%s", hc->hdrhost); + + if (hc->contentlength != -1) + { + (void) my_snprintf(buf, sizeof(buf), "%lu", + (unsigned long) hc->contentlength); + envp[envn++] = build_env("CONTENT_LENGTH=%s", buf); + } + if (hc->remoteuser[0] != '\0') + envp[envn++] = build_env("REMOTE_USER=%s", hc->remoteuser); + if (hc->authorization[0] != '\0') + envp[envn++] = build_env("AUTH_TYPE=%s", "Basic"); + + /* We only support Basic auth at the moment. */ + if (getenv("TZ") != (char *) 0) + envp[envn++] = build_env("TZ=%s", getenv("TZ")); + + //fprintf(stderr, "make_envp #2.4: %d %p\n", getpid(), hc->hs->cgi_pattern); + //envp[envn++] = build_env( "CGI_PATTERN=%s", hc->hs->cgi_pattern ); + +#ifdef X_CGI_HEADER + //if (hc->xcgi[0]) + envp[envn++] = build_env("X_CGI=%s", hc->xcgi); + if (hc->if_modified_since != (time_t) - 1) + { + my_snprintf(buf, sizeof(buf), "%lld", (int64_t) (hc->if_modified_since)); + envp[envn++] = build_env("HTTP_IF_MODIFIED_SINCE=%s", buf); + } +#endif + + envp[envn] = (char *) 0; + return envp; +} + + +/* Set up argument vector. Again, we don't have to worry about freeing stuff +** since we're a sub-process. This gets done after make_envp() because we +** scribble on hc->query. +*/ +static char **make_argp(httpd_conn * hc) +{ + char **argp; + int argn; + char *cp1; + char *cp2; + + /* By allocating an arg slot for every character in the query, plus + ** one for the filename and one for the NULL, we are guaranteed to + ** have enough. We could actually use strlen/2. + */ + argp = NEW(char *, strlen(hc->query) + 2); + if (argp == (char **) 0) + return (char **) 0; + + argp[0] = strrchr(hc->expnfilename, '/'); + if (argp[0] != (char *) 0) + ++argp[0]; + else + argp[0] = hc->expnfilename; + + argn = 1; + /* According to the CGI spec at http://hoohoo.ncsa.uiuc.edu/cgi/cl.html, + ** "The server should search the query information for a non-encoded = + ** character to determine if the command line is to be used, if it finds + ** one, the command line is not to be used." + */ + if (strchr(hc->query, '=') == (char *) 0) + { + for (cp1 = cp2 = hc->query; *cp2 != '\0'; ++cp2) + { + if (*cp2 == '+') + { + *cp2 = '\0'; + strdecode(cp1, cp1); + argp[argn++] = cp1; + cp1 = cp2 + 1; + } + } + if (cp2 != cp1) + { + strdecode(cp1, cp1); + argp[argn++] = cp1; + } + } + + argp[argn] = (char *) 0; + return argp; +} + + +/* This routine is used only for POST requests. It reads the data +** from the request and sends it to the child process. The only reason +** we need to do it this way instead of just letting the child read +** directly is that we have already read part of the data into our +** buffer. +*/ +static void cgi_interpose_input(httpd_conn * hc, int wfd) +{ + size_t c; + ssize_t r; + char buf[1024]; + + c = hc->read_idx - hc->checked_idx; + if (c > 0) + { + if (httpd_write_fully(wfd, &(hc->read_buf[hc->checked_idx]), c) != c) + return; + } + while (c < hc->contentlength) + { + r = read(hc->conn_fd, buf, MIN(sizeof(buf), hc->contentlength - c)); + if (r < 0 && (errno == EINTR || errno == EAGAIN)) + { + sleep(1); + continue; + } + if (r <= 0) + return; + if (httpd_write_fully(wfd, buf, r) != r) + return; + c += r; + } + post_post_garbage_hack(hc); +} + + +/* Special hack to deal with broken browsers that send a LF or CRLF +** after POST data, causing TCP resets - we just read and discard up +** to 2 bytes. Unfortunately this doesn't fix the problem for CGIs +** which avoid the interposer process due to their POST data being +** short. Creating an interposer process for all POST CGIs is +** unacceptably expensive. The eventual fix will come when interposing +** gets integrated into the main loop as a tasklet instead of a process. +*/ +static void post_post_garbage_hack(httpd_conn * hc) +{ + char buf[2]; + + /* If we are in a sub-process, turn on no-delay mode in case we + ** previously cleared it. + */ + if (sub_process) + httpd_set_ndelay(hc->conn_fd); + /* And read up to 2 bytes. */ + (void) read(hc->conn_fd, buf, sizeof(buf)); +} + + +/* This routine is used for parsed-header CGIs. The idea here is that the +** CGI can return special headers such as "Status:" and "Location:" which +** change the return status of the response. Since the return status has to +** be the very first line written out, we have to accumulate all the headers +** and check for the special ones before writing the status. Then we write +** out the saved headers and proceed to echo the rest of the response. +*/ +static void cgi_interpose_output(httpd_conn * hc, int rfd) +{ + int r; + char buf[1024]; + size_t headers_size, headers_len; + char *headers; + char *br; + int status; + char *title; + char *cp; + + /* Make sure the connection is in blocking mode. It should already + ** be blocking, but we might as well be sure. + */ + httpd_clear_ndelay(hc->conn_fd); + + /* Slurp in all headers. */ + headers_size = 0; + httpd_realloc_str(&headers, &headers_size, 500); + headers_len = 0; + for (;;) + { + r = read(rfd, buf, sizeof(buf)); + if (r < 0 && (errno == EINTR || errno == EAGAIN)) + { + sleep(1); + continue; + } + if (r <= 0) + { + br = &(headers[headers_len]); + break; + } + httpd_realloc_str(&headers, &headers_size, headers_len + r); + (void) memmove(&(headers[headers_len]), buf, r); + headers_len += r; + headers[headers_len] = '\0'; + if ((br = strstr(headers, "\015\012\015\012")) != (char *) 0 || + (br = strstr(headers, "\012\012")) != (char *) 0) + break; + } + + /* If there were no headers, bail. */ + if (headers[0] == '\0') + return; + + /* Figure out the status. Look for a Status: or Location: header; + ** else if there's an HTTP header line, get it from there; else + ** default to 200. + */ + status = 200; + if (strncmp(headers, "HTTP/", 5) == 0) + { + cp = headers; + cp += strcspn(cp, " \t"); + status = atoi(cp); + } + if ((cp = strstr(headers, "Status:")) != (char *) 0 && + cp < br && (cp == headers || *(cp - 1) == '\012')) + { + cp += 7; + cp += strspn(cp, " \t"); + status = atoi(cp); + } + if ((cp = strstr(headers, "Location:")) != (char *) 0 && + cp < br && (cp == headers || *(cp - 1) == '\012')) + status = 302; + + /* Write the status line. */ + switch (status) + { + case 200: + title = ok200title; + break; + case 302: + title = err302title; + break; + case 304: + title = err304title; + break; + case 400: + title = httpd_err400title; + break; +#ifdef AUTH_FILE + case 401: + title = err401title; + break; +#endif /* AUTH_FILE */ + case 403: + title = err403title; + break; + case 404: + title = err404title; + break; + case 408: + title = httpd_err408title; + break; + case 500: + title = err500title; + break; + case 501: + title = err501title; + break; + case 503: + title = httpd_err503title; + break; + default: + title = "Something"; + break; + } + (void) my_snprintf(buf, sizeof(buf), "HTTP/1.0 %d %s\015\012", status, + title); + (void) httpd_write_fully(hc->conn_fd, buf, strlen(buf)); + + /* Write the saved headers. */ + (void) httpd_write_fully(hc->conn_fd, headers, headers_len); + + /* Echo the rest of the output. */ + for (;;) + { + r = read(rfd, buf, sizeof(buf)); + if (r < 0 && (errno == EINTR || errno == EAGAIN)) + { + sleep(1); + continue; + } + if (r <= 0) + break; + if (httpd_write_fully(hc->conn_fd, buf, r) != r) + break; + } + shutdown(hc->conn_fd, SHUT_WR); +} + + +/* CGI child process. */ +static void cgi_child(httpd_conn * hc) +{ + int r; + char **argp; + char **envp; + //char* binary; + //char* directory; + + /* Unset close-on-exec flag for this socket. This actually shouldn't + ** be necessary, according to POSIX a dup()'d file descriptor does + ** *not* inherit the close-on-exec flag, its flag is always clear. + ** However, Linux messes this up and does copy the flag to the + ** dup()'d descriptor, so we have to clear it. This could be + ** ifdeffed for Linux only. + */ + (void) fcntl(hc->conn_fd, F_SETFD, 0); + + /* Close the syslog descriptor so that the CGI program can't + ** mess with it. All other open descriptors should be either + ** the listen socket(s), sockets from accept(), or the file-logging + ** fd, and all of those are set to close-on-exec, so we don't + ** have to close anything else. + */ + closelog(); + + /* If the socket happens to be using one of the stdin/stdout/stderr + ** descriptors, move it to another descriptor so that the dup2 calls + ** below don't screw things up. We arbitrarily pick fd 3 - if there + ** was already something on it, we clobber it, but that doesn't matter + ** since at this point the only fd of interest is the connection. + ** All others will be closed on exec. + */ + if (hc->conn_fd == STDIN_FILENO || hc->conn_fd == STDOUT_FILENO + || hc->conn_fd == STDERR_FILENO) + { + int newfd = dup2(hc->conn_fd, STDERR_FILENO + 1); + if (newfd >= 0) + hc->conn_fd = newfd; + /* If the dup2 fails, shrug. We'll just take our chances. + ** Shouldn't happen though. + */ + } + + /* Make the environment vector. */ + envp = make_envp(hc); + + /* Make the argument vector. */ + argp = make_argp(hc); + + /* Set up stdin. For POSTs we may have to set up a pipe from an + ** interposer process, depending on if we've read some of the data + ** into our buffer. + */ + if (hc->method == METHOD_POST && hc->read_idx > hc->checked_idx) + { + int p[2]; + + if (pipe(p) < 0) + { + syslog(LOG_ERR, "pipe - %m"); + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + httpd_write_response(hc); + exit(1); + } + + r = fork(); + + if (r < 0) + { + syslog(LOG_ERR, "fork - %m"); + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + httpd_write_response(hc); + exit(1); + } + + if (r == 0) + { + /* Interposer process. */ + sub_process = 1; + (void) close(p[0]); + cgi_interpose_input(hc, p[1]); + exit(0); + } + + /* Need to schedule a kill for process r; but in the main process! */ + (void) close(p[1]); + if (p[0] != STDIN_FILENO) + { + (void) dup2(p[0], STDIN_FILENO); + (void) close(p[0]); + } + } + else + { + /* Otherwise, the request socket is stdin. */ + if (hc->conn_fd != STDIN_FILENO) + (void) dup2(hc->conn_fd, STDIN_FILENO); + } + + /* Set up stdout/stderr. If we're doing CGI header parsing, + ** we need an output interposer too. + */ + if (strncmp(argp[0], "nph-", 4) != 0 && hc->mime_flag) + { + int p[2]; + + if (pipe(p) < 0) + { + syslog(LOG_ERR, "pipe - %m"); + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + httpd_write_response(hc); + exit(1); + } + r = fork(); + if (r < 0) + { + syslog(LOG_ERR, "fork - %m"); + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + httpd_write_response(hc); + exit(1); + } + if (r == 0) + { + /* Interposer process. */ + sub_process = 1; + (void) close(p[1]); + cgi_interpose_output(hc, p[0]); + exit(0); + } + /* Need to schedule a kill for process r; but in the main process! */ + (void) close(p[0]); + if (p[1] != STDOUT_FILENO) + (void) dup2(p[1], STDOUT_FILENO); + // BM: let stderr go to the IDE + //if (p[1] != STDERR_FILENO) + // (void) dup2(p[1], STDERR_FILENO); + if (p[1] != STDOUT_FILENO && p[1] != STDERR_FILENO) + (void) close(p[1]); + } + else + { + /* Otherwise, the request socket is stdout/stderr. */ + if (hc->conn_fd != STDOUT_FILENO) + (void) dup2(hc->conn_fd, STDOUT_FILENO); + // BM: let stderr go to the IDE + // if (hc->conn_fd != STDERR_FILENO) + // (void) dup2(hc->conn_fd, STDERR_FILENO); + } + + /* At this point we would like to set close-on-exec again for hc->conn_fd + ** (see previous comments on Linux's broken behavior re: close-on-exec + ** and dup.) Unfortunately there seems to be another Linux problem, or + ** perhaps a different aspect of the same problem - if we do this + ** close-on-exec in Linux, the socket stays open but stderr gets + ** closed - the last fd duped from the socket. What a mess. So we'll + ** just leave the socket as is, which under other OSs means an extra + ** file descriptor gets passed to the child process. Since the child + ** probably already has that file open via stdin stdout and/or stderr, + ** this is not a problem. + */ + /* (void) fcntl( hc->conn_fd, F_SETFD, 1 ); */ + +#ifdef CGI_NICE + /* Set priority. */ + (void)nice(CGI_NICE); +#endif /* CGI_NICE */ + +#if 0 + /* Split the program into directory and binary, so we can chdir() + ** to the program's own directory. This isn't in the CGI 1.1 + ** spec, but it's what other HTTP servers do. + */ + directory = strdup(hc->expnfilename); + if (directory == (char *) 0) + binary = hc->expnfilename; /* ignore errors */ + else + { + binary = strrchr(directory, '/'); + if (binary == (char *) 0) + binary = hc->expnfilename; + else + { + *binary++ = '\0'; + (void) chdir(directory); /* ignore errors */ + } + } +#endif + + // Reset signal handlers + + (void) signal(SIGTERM, SIG_DFL); + (void) signal(SIGINT, SIG_DFL); + (void) signal(SIGCHLD, SIG_DFL); + (void) signal(SIGPIPE, SIG_DFL); + (void) signal(SIGHUP, SIG_DFL); + (void) signal(SIGUSR1, SIG_DFL); + (void) signal(SIGUSR2, SIG_DFL); + (void) signal(SIGALRM, SIG_DFL); + +#if 0 + /* Run the program. */ + (void) execve(binary, argp, envp); + + /* Something went wrong. */ + syslog(LOG_ERR, "execve %.80s - %m", hc->expnfilename); + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + httpd_write_response(hc); + exit(1); +#endif + + environ = envp; + //fprintf(stderr, "%s\n", hc->encodedurl); + run_cgi(); + +} + +static int cgi_start(httpd_conn *hc) +{ + int r; + ClientData client_data; + + ++hc->hs->cgi_count; + httpd_clear_ndelay(hc->conn_fd); + r = fork(); + if (r < 0) + { + syslog(LOG_ERR, "fork - %m"); + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + return -1; + } + + if (r == 0) + { + /* Child process. */ + sub_process = 1; + httpd_unlisten(hc->hs); + cgi_child(hc); + } + else + { + /* Parent process. */ + syslog(LOG_INFO, "spawned CGI process %d for path '%.200s'", r, hc->expnfilename); +#ifdef CGI_TIMELIMIT + /* Schedule a kill for the child process, in case it runs too long */ + if (hc->hs->cgi_timelimit) + { + client_data.i = r; + if (tmr_create + ((struct timeval *) 0, cgi_kill, client_data, + hc->hs->cgi_timelimit * 1000L, 0) == (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(cgi_kill child) failed"); + exit(1); + } + } +#endif /* CGI_TIMELIMIT */ + hc->status = 200; + hc->bytes_sent = CGI_BYTECOUNT; + hc->should_linger = 0; + } + + return 0; +} + +int httpd_check_paused(httpd_conn *hc) +{ + //syslog(LOG_INFO, "[%p] try again cgi script '%.200s'", hc, hc->expnfilename); + + if (hc->hs->cgi_count >= 1) + { + //syslog(LOG_INFO, "[%p] postponed cgi script '%.200s'", hc, hc->expnfilename); + return 1; + } + + return cgi_start(hc); +} + +static int cgi(httpd_conn * hc) +{ + if (hc->method == METHOD_GET || hc->method == METHOD_POST) + { + if (hc->hs->cgi_limit != 0 && hc->hs->cgi_count >= hc->hs->cgi_limit) + { + if (hc->hs->debug) + { + //syslog(LOG_INFO, "postponed cgi script '%.200s'", hc->expnfilename); + return 1; + } + else + { + httpd_send_err(hc, 503, httpd_err503title, "", httpd_err503form, + hc->encodedurl); + return -1; + } + } + + cgi_start(hc); + } + else + { + httpd_send_err(hc, 501, err501title, "", err501form, httpd_method_str(hc->method)); + return -1; + } + + return 0; +} + + +static int really_start_request(httpd_conn * hc, struct timeval *nowP) +{ + //static char *indexname; + //static size_t maxindexname = 0; + //static const char *index_names[] = { INDEX_NAMES }; + //int i; +#ifdef AUTH_FILE + //static char *dirname; + //static size_t maxdirname = 0; +#endif /* AUTH_FILE */ + //size_t expnlen, indxlen; + //char *cp; + //char *pi; + char *public_file = NULL; + int public_file_len = 0; + + if (hc->method != METHOD_GET && hc->method != METHOD_HEAD && + hc->method != METHOD_POST) + { + httpd_send_err(hc, 501, err501title, "", err501form, httpd_method_str(hc->method)); + return -1; + } + + //-------------------------------------------------------------- + + if (strlen(hc->expnfilename) == 0) + { + public_file = NULL; + } + else + { + public_file_len = strlen(hc->expnfilename) + strlen(PUBLIC_PREFIX); + public_file = alloca(public_file_len + 1); + strcpy(public_file, PUBLIC_PREFIX); + strcpy(&public_file[strlen(PUBLIC_PREFIX)], hc->expnfilename); + } + + /* Stat the file. */ + //if (stat(hc->expnfilename, &hc->sb) < 0) + /*{ + httpd_send_err(hc, 404, err404title, "", err404form, hc->encodedurl); + //httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + return -1; + }*/ + +#if 0 + /* Is it world-readable or world-executable? We check explicitly instead + ** of just trying to open it, so that no one ever gets surprised by + ** a file that's not set world-readable and yet somehow is + ** readable by the HTTP server and therefore the *whole* world. + */ + if (!(hc->sb.st_mode & (S_IROTH | S_IXOTH))) + { + syslog(LOG_INFO, + "%.80s URL \"%.80s\" resolves to a non world-readable file", + httpd_ntoa(&hc->client_addr), hc->encodedurl); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' resolves to a file that is not world-readable.\n"), + hc->encodedurl); + return -1; + } +#endif + + if (public_file && !GB.StatFile(public_file, &hc->sb, FALSE)) + { + /* Is it a directory? */ + if (hc->sb.type == GB_STAT_DIRECTORY) + { + httpd_send_err(hc, 404, err404title, "", err404form, hc->encodedurl); + return -1; + } + + /* Fill in last_byte_index, if necessary. */ + if (hc->got_range && + (hc->last_byte_index == -1 || hc->last_byte_index >= hc->sb.size)) + hc->last_byte_index = hc->sb.size - 1; + + figure_mime(hc); + + if (hc->method == METHOD_HEAD) + { + send_mime(hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.size, hc->sb.mtime); + } + else if (hc->if_modified_since != (time_t) - 1 && + hc->if_modified_since >= hc->sb.mtime) + { + send_mime(hc, 304, err304title, hc->encodings, "", hc->type, (off_t) - 1, hc->sb.mtime); + } + else + { + //hc->file_address = mmc_map(hc->expnfilename, &(hc->sb), nowP); + //if (hc->file_address == (char *) 0) + + //syslog(LOG_DEBUG, "%.*s", public_file_len, public_file); + + if (GB.LoadFile(public_file, public_file_len, &hc->file_address, &hc->file_len)) + { + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + return -1; + } + send_mime(hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.size, hc->sb.mtime); + } + + return 0; + } + +#if 0 + /* If there's pathinfo, it's just a non-existent file. */ + /*if (hc->pathinfo[0] != '\0') + { + httpd_send_err(hc, 404, err404title, "", err404form, hc->encodedurl); + return -1; + }*/ + + /* Special handling for directory URLs that don't end in a slash. + ** We send back an explicit redirect with the slash, because + ** otherwise many clients can't build relative URLs properly. + */ + if (strcmp(hc->origfilename, "") != 0 && + strcmp(hc->origfilename, ".") != 0 && + hc->origfilename[strlen(hc->origfilename) - 1] != '/') + { + send_dirredirect(hc); + return -1; + } +#endif + +#if 0 + /* Check for an index file. */ + for (i = 0; i < sizeof(index_names) / sizeof(char *); ++i) + { + httpd_realloc_str(&indexname, &maxindexname, + expnlen + 1 + strlen(index_names[i])); + (void) strcpy(indexname, hc->expnfilename); + indxlen = strlen(indexname); + if (indxlen == 0 || indexname[indxlen - 1] != '/') + (void) strcat(indexname, "/"); + if (strcmp(indexname, "./") == 0) + indexname[0] = '\0'; + (void) strcat(indexname, index_names[i]); + if (stat(indexname, &hc->sb) >= 0) + goto got_one; + } + + /* Nope, no index file, so it's an actual directory request. */ +#ifdef GENERATE_INDEXES + /* Directories must be readable for indexing. */ + if (!(hc->sb.st_mode & S_IROTH)) + { + syslog(LOG_INFO, + "%.80s URL \"%.80s\" tried to index a directory with indexing disabled", + httpd_ntoa(&hc->client_addr), hc->encodedurl); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' resolves to a directory that has indexing disabled.\n"), + hc->encodedurl); + return -1; + } +#ifdef AUTH_FILE + /* Check authorization for this directory. */ + if (auth_check(hc, hc->expnfilename) == -1) + return -1; +#endif /* AUTH_FILE */ + /* Referer check. */ + if (!check_referer(hc)) + return -1; + /* Ok, generate an index. */ + return ls(hc); +#else /* GENERATE_INDEXES */ + syslog(LOG_INFO, "%.80s URL \"%.80s\" tried to index a directory", + httpd_ntoa(&hc->client_addr), hc->encodedurl); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' is a directory, and directory indexing is disabled on this server.\n"), + hc->encodedurl); + return -1; +#endif /* GENERATE_INDEXES */ + + got_one:; + /* Got an index file. Expand symlinks again. More pathinfo means + ** something went wrong. + */ + cp = + expand_symlinks(indexname, &pi, hc->hs->no_symlink_check, + hc->tildemapped); + if (cp == (char *) 0 || pi[0] != '\0') + { + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + return -1; + } + expnlen = strlen(cp); + httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, expnlen); + (void) strcpy(hc->expnfilename, cp); + + /* Now, is the index version world-readable or world-executable? */ + if (!(hc->sb.st_mode & (S_IROTH | S_IXOTH))) + { + syslog(LOG_INFO, + "%.80s URL \"%.80s\" resolves to a non-world-readable index file", + httpd_ntoa(&hc->client_addr), hc->encodedurl); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' resolves to an index file that is not world-readable.\n"), + hc->encodedurl); + return -1; + } + } + +#ifdef AUTH_FILE + /* Check authorization for this directory. */ + httpd_realloc_str(&dirname, &maxdirname, expnlen); + (void) strcpy(dirname, hc->expnfilename); + cp = strrchr(dirname, '/'); + if (cp == (char *) 0) + (void) strcpy(dirname, "."); + else + *cp = '\0'; + if (auth_check(hc, dirname) == -1) + return -1; + + /* Check if the filename is the AUTH_FILE itself - that's verboten. */ + if (expnlen == sizeof(AUTH_FILE) - 1) + { + if (strcmp(hc->expnfilename, AUTH_FILE) == 0) + { + syslog(LOG_NOTICE, + "%.80s URL \"%.80s\" tried to retrieve an auth file", + httpd_ntoa(&hc->client_addr), hc->encodedurl); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n"), + hc->encodedurl); + return -1; + } + } + else if (expnlen >= sizeof(AUTH_FILE) && + strcmp(&(hc->expnfilename[expnlen - sizeof(AUTH_FILE) + 1]), + AUTH_FILE) == 0 + && hc->expnfilename[expnlen - sizeof(AUTH_FILE)] == '/') + { + syslog(LOG_NOTICE, + "%.80s URL \"%.80s\" tried to retrieve an auth file", + httpd_ntoa(&hc->client_addr), hc->encodedurl); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n"), + hc->encodedurl); + return -1; + } +#endif /* AUTH_FILE */ +#endif + + //-------------------------------------------------------------- + + /* Referer check. */ + if (!check_referer(hc)) + return -1; + + /* Is it world-executable and in the CGI area? */ + //if ( hc->hs->cgi_pattern != (char*) 0 && ( hc->sb.st_mode & S_IXOTH ) && match( hc->hs->cgi_pattern, hc->expnfilename ) ) + return cgi(hc); + +#if 0 + /* Is it world-executable and in the CGI area? */ + if (hc->hs->cgi_pattern != (char *) 0 && + (hc->sb.st_mode & S_IXOTH) && + match(hc->hs->cgi_pattern, hc->expnfilename)) + return cgi(hc); + + /* It's not CGI. If it's executable or there's pathinfo, someone's + ** trying to either serve or run a non-CGI file as CGI. Either case + ** is prohibited. + */ + if (hc->sb.st_mode & S_IXOTH) + { + syslog(LOG_NOTICE, "%.80s URL \"%.80s\" is executable but isn't CGI", + httpd_ntoa(&hc->client_addr), hc->encodedurl); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' resolves to a file which is marked executable but is not a CGI file; retrieving it is forbidden.\n"), + hc->encodedurl); + return -1; + } + if (hc->pathinfo[0] != '\0') + { + syslog(LOG_INFO, "%.80s URL \"%.80s\" has pathinfo but isn't CGI", + httpd_ntoa(&hc->client_addr), hc->encodedurl); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "The requested URL '%.80s' resolves to a file plus CGI-style pathinfo, but the file is not a valid CGI file.\n"), + hc->encodedurl); + return -1; + } + + /* Fill in last_byte_index, if necessary. */ + if (hc->got_range && + (hc->last_byte_index == -1 || hc->last_byte_index >= hc->sb.size)) + hc->last_byte_index = hc->sb.size - 1; + + figure_mime(hc); + + if (hc->method == METHOD_HEAD) + { + send_mime(hc, 200, ok200title, hc->encodings, "", hc->type, + hc->sb.size, hc->sb.mtime); + } + else if (hc->if_modified_since != (time_t) - 1 && + hc->if_modified_since >= hc->sb.mtime) + { + send_mime(hc, 304, err304title, hc->encodings, "", hc->type, (off_t) - 1, + hc->sb.mtime); + } + else + { + hc->file_address = mmc_map(hc->expnfilename, &(hc->sb), nowP); + if (hc->file_address == (char *) 0) + { + httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl); + return -1; + } + send_mime(hc, 200, ok200title, hc->encodings, "", hc->type, + hc->sb.size, hc->sb.mtime); + } + + return 0; +#endif +} + + +int httpd_start_request(httpd_conn * hc, struct timeval *nowP) +{ + int r; + + /* Really start the request. */ + r = really_start_request(hc, nowP); + + /* And return the status. */ + return r; +} + + +static void make_log_entry(httpd_conn * hc, struct timeval *nowP) +{ + char *ru; + char url[305]; + char bytes[40]; + + if (hc->hs->no_log) + return; + + /* This is straight CERN Combined Log Format - the only tweak + ** being that if we're using syslog() we leave out the date, because + ** syslogd puts it in. The included syslogtocern script turns the + ** results into true CERN format. + */ + + /* Format remote user. */ + if (hc->remoteuser[0] != '\0') + ru = hc->remoteuser; + else + ru = "-"; + /* If we're vhosting, prepend the hostname to the url. This is + ** a little weird, perhaps writing separate log files for + ** each vhost would make more sense. + */ + if (hc->hs->vhost && !hc->tildemapped) + (void) my_snprintf(url, sizeof(url), + "/%.100s%.200s", + hc->hostname == + (char *) 0 ? hc->hs->server_hostname : hc->hostname, + hc->encodedurl); + else + (void) my_snprintf(url, sizeof(url), "%.200s", hc->encodedurl); + /* Format the bytes. */ + if (hc->bytes_sent >= 0) + (void) my_snprintf(bytes, sizeof(bytes), "%lld", + (int64_t) hc->bytes_sent); + else + (void) strcpy(bytes, "-"); + + /* Logfile or syslog? */ + if (hc->hs->logfp != (FILE *) 0) + { + time_t now; + struct tm *t; + const char *cernfmt_nozone = "%d/%b/%Y:%H:%M:%S"; + char date_nozone[100]; + int zone; + char sign; + char date[100]; + + /* Get the current time, if necessary. */ + if (nowP != (struct timeval *) 0) + now = nowP->tv_sec; + else + now = time((time_t *) 0); + /* Format the time, forcing a numeric timezone (some log analyzers + ** are stoooopid about this). + */ + t = localtime(&now); + (void) strftime(date_nozone, sizeof(date_nozone), cernfmt_nozone, t); +#ifdef HAVE_TM_GMTOFF + zone = t->tm_gmtoff / 60L; +#else + zone = -timezone / 60L; + /* Probably have to add something about daylight time here. */ +#endif + if (zone >= 0) + sign = '+'; + else + { + sign = '-'; + zone = -zone; + } + zone = (zone / 60) * 100 + zone % 60; + (void) my_snprintf(date, sizeof(date), + "%s %c%04d", date_nozone, sign, zone); + /* And write the log entry. */ + (void) fprintf(hc->hs->logfp, + "%.80s - %.80s [%s] \"%.80s %.300s %.80s\" %d %s \"%.200s\" \"%.200s\"\n", + httpd_ntoa(&hc->client_addr), ru, date, + httpd_method_str(hc->method), url, hc->protocol, + hc->status, bytes, hc->referer, hc->useragent); +#ifdef FLUSH_LOG_EVERY_TIME + (void) fflush(hc->hs->logfp); +#endif + } + else + syslog(LOG_INFO, + "%.80s - %.80s \"%.80s %.200s %.80s\" %d %s \"%.200s\" \"%.200s\"", + httpd_ntoa(&hc->client_addr), ru, + httpd_method_str(hc->method), url, hc->protocol, + hc->status, bytes, hc->referer, hc->useragent); +} + + +/* Returns 1 if ok to serve the url, 0 if not. */ +static int check_referer(httpd_conn * hc) +{ + int r; + char *cp; + + /* Are we doing referer checking at all? */ + if (hc->hs->url_pattern == (char *) 0) + return 1; + + r = really_check_referer(hc); + + if (!r) + { + if (hc->hs->vhost && hc->hostname != (char *) 0) + cp = hc->hostname; + else + cp = hc->hs->server_hostname; + if (cp == (char *) 0) + cp = ""; + syslog(LOG_INFO, "%.80s non-local referer \"%.80s%.80s\" \"%.80s\"", + httpd_ntoa(&hc->client_addr), cp, hc->encodedurl, hc->referer); + httpd_send_err(hc, 403, err403title, "", + ERROR_FORM(err403form, + "You must supply a local referer to get URL '%.80s' from this server.\n"), + hc->encodedurl); + } + return r; +} + + +/* Returns 1 if ok to serve the url, 0 if not. */ +static int really_check_referer(httpd_conn * hc) +{ + httpd_server *hs; + char *cp1; + char *cp2; + char *cp3; + static char *refhost = (char *) 0; + static size_t refhost_size = 0; + char *lp; + + hs = hc->hs; + + /* Check for an empty referer. */ + if (hc->referer == (char *) 0 || hc->referer[0] == '\0' || + (cp1 = strstr(hc->referer, "//")) == (char *) 0) + { + /* Disallow if we require a referer and the url matches. */ + if (hs->no_empty_referers && match(hs->url_pattern, hc->origfilename)) + return 0; + /* Otherwise ok. */ + return 1; + } + + /* Extract referer host. */ + cp1 += 2; + for (cp2 = cp1; *cp2 != '/' && *cp2 != ':' && *cp2 != '\0'; ++cp2) + continue; + httpd_realloc_str(&refhost, &refhost_size, cp2 - cp1); + for (cp3 = refhost; cp1 < cp2; ++cp1, ++cp3) + if (isupper(*cp1)) + *cp3 = tolower(*cp1); + else + *cp3 = *cp1; + *cp3 = '\0'; + + /* Local pattern? */ + if (hs->local_pattern != (char *) 0) + lp = hs->local_pattern; + else + { + /* No local pattern. What's our hostname? */ + if (!hs->vhost) + { + /* Not vhosting, use the server name. */ + lp = hs->server_hostname; + if (lp == (char *) 0) + /* Couldn't figure out local hostname - give up. */ + return 1; + } + else + { + /* We are vhosting, use the hostname on this connection. */ + lp = hc->hostname; + if (lp == (char *) 0) + /* Oops, no hostname. Maybe it's an old browser that + ** doesn't send a Host: header. We could figure out + ** the default hostname for this IP address, but it's + ** not worth it for the few requests like this. + */ + return 1; + } + } + + /* If the referer host doesn't match the local host pattern, and + ** the filename does match the url pattern, it's an illegal reference. + */ + if (!match(lp, refhost) && match(hs->url_pattern, hc->origfilename)) + return 0; + /* Otherwise ok. */ + return 1; +} + + +char *httpd_ntoa(httpd_sockaddr * saP) +{ +#ifdef USE_IPV6 + static char str[200]; + + if (getnameinfo + (&saP->sa, sockaddr_len(saP), str, sizeof(str), 0, 0, + NI_NUMERICHOST) != 0) + { + str[0] = '?'; + str[1] = '\0'; + } + else if (IN6_IS_ADDR_V4MAPPED(&saP->sa_in6.sin6_addr) + && strncmp(str, "::ffff:", 7) == 0) + /* Elide IPv6ish prefix for IPv4 addresses. */ + (void) strcpy(str, &str[7]); + + return str; + +#else /* USE_IPV6 */ + + return inet_ntoa(saP->sa_in.sin_addr); + +#endif /* USE_IPV6 */ +} + + +static int sockaddr_check(httpd_sockaddr * saP) +{ + switch (saP->sa.sa_family) + { + case AF_INET: + return 1; +#ifdef USE_IPV6 + case AF_INET6: + return 1; +#endif /* USE_IPV6 */ + default: + return 0; + } +} + + +static size_t sockaddr_len(httpd_sockaddr * saP) +{ + switch (saP->sa.sa_family) + { + case AF_INET: + return sizeof(struct sockaddr_in); +#ifdef USE_IPV6 + case AF_INET6: + return sizeof(struct sockaddr_in6); +#endif /* USE_IPV6 */ + default: + return 0; /* shouldn't happen */ + } +} + + +/* Some systems don't have snprintf(), so we make our own that uses +** either vsnprintf() or vsprintf(). If your system doesn't have +** vsnprintf(), it is probably vulnerable to buffer overruns. +** Upgrade! +*/ +static int my_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list ap; + int r; + + va_start(ap, format); +#ifdef HAVE_VSNPRINTF + r = vsnprintf(str, size, format, ap); +#else /* HAVE_VSNPRINTF */ + r = vsprintf(str, format, ap); +#endif /* HAVE_VSNPRINTF */ + va_end(ap); + return r; +} + + +#ifndef HAVE_ATOLL +static long long atoll(const char *str) +{ + long long value; + long long sign; + + while (isspace(*str)) + ++str; + switch (*str) + { + case '-': + sign = -1; + ++str; + break; + case '+': + sign = 1; + ++str; + break; + default: + sign = 1; + break; + } + value = 0; + while (isdigit(*str)) + { + value = value * 10 + (*str - '0'); + ++str; + } + return sign * value; +} +#endif /* HAVE_ATOLL */ + + +/* Read the requested buffer completely, accounting for interruptions. */ +int httpd_read_fully(int fd, void *buf, size_t nbytes) +{ + int nread; + + nread = 0; + while (nread < nbytes) + { + int r; + + r = read(fd, (char *) buf + nread, nbytes - nread); + if (r < 0 && (errno == EINTR || errno == EAGAIN)) + { + sleep(1); + continue; + } + if (r < 0) + return r; + if (r == 0) + break; + nread += r; + } + + return nread; +} + + +/* Write the requested buffer completely, accounting for interruptions. */ +int httpd_write_fully(int fd, const void *buf, size_t nbytes) +{ + int nwritten; + + nwritten = 0; + while (nwritten < nbytes) + { + int r; + + r = write(fd, (char *) buf + nwritten, nbytes - nwritten); + if (r < 0 && (errno == EINTR || errno == EAGAIN)) + { + sleep(1); + continue; + } + if (r < 0) + return r; + if (r == 0) + break; + nwritten += r; + } + + return nwritten; +} + + +/* Generate debugging statistics syslog message. */ +void httpd_logstats(long secs) +{ + if (str_alloc_count > 0) + syslog(LOG_INFO, + " libhttpd - %d strings allocated, %lu bytes (%g bytes/str)", + str_alloc_count, (unsigned long) str_alloc_size, + (float) str_alloc_size / str_alloc_count); +} diff --git a/gb.httpd/src/libhttpd.h b/gb.httpd/src/libhttpd.h new file mode 100644 index 00000000..90e06bb1 --- /dev/null +++ b/gb.httpd/src/libhttpd.h @@ -0,0 +1,298 @@ +/* libhttpd.h - defines for libhttpd +** +** Copyright (c) 1995,1998,1999,2000,2001 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _LIBHTTPD_H_ +#define _LIBHTTPD_H_ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(AF_INET6) && defined(IN6_IS_ADDR_V4MAPPED) +#define USE_IPV6 +#endif + + +/* A few convenient defines. */ + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#define NEW(t,n) ((t*) malloc( sizeof(t) * (n) )) +#define RENEW(o,t,n) ((t*) realloc( (void*) o, sizeof(t) * (n) )) + + +/* The httpd structs. */ + +/* A multi-family sockaddr. */ +typedef union +{ + struct sockaddr sa; + struct sockaddr_in sa_in; +#ifdef USE_IPV6 + struct sockaddr_in6 sa_in6; + struct sockaddr_storage sa_stor; +#endif /* USE_IPV6 */ +} httpd_sockaddr; + +/* A server. */ +typedef struct +{ + char *binding_hostname; + char *server_hostname; + unsigned short port; + char *cgi_pattern; + int cgi_limit, cgi_timelimit, cgi_count; + char *charset; + char *p3p; + int max_age; + char *cwd; + int listen4_fd, listen6_fd; + int no_log; + FILE *logfp; + int no_symlink_check; + int vhost; + int global_passwd; + char *url_pattern; + char *local_pattern; + int no_empty_referers; + unsigned debug : 1; +} httpd_server; + +/* A connection. */ +typedef struct +{ + int initialized; + httpd_server *hs; + httpd_sockaddr client_addr; + char *read_buf; + size_t read_size, read_idx, checked_idx; + int checked_state; + int method; + int status; + off_t bytes_to_send; + off_t bytes_sent; + char *encodedurl; + char *decodedurl; + char *protocol; + char *origfilename; + char *expnfilename; + char *encodings; + char *pathinfo; + char *query; + char *referer; + char *useragent; + char *accept; + char *accepte; + char *acceptl; + char *cookie; + char *contenttype; +#ifdef X_CGI_HEADER + char *xcgi; +#endif + char *reqhost; + char *hdrhost; + char *hostdir; + char *authorization; + char *remoteuser; + char *response; + size_t maxdecodedurl, maxorigfilename, maxexpnfilename, maxencodings, + maxpathinfo, maxquery, maxaccept, maxaccepte, maxreqhost, maxhostdir, + maxremoteuser, maxresponse; +#ifdef TILDE_MAP_2 + char *altdir; + size_t maxaltdir; +#endif /* TILDE_MAP_2 */ + size_t responselen; + time_t if_modified_since, range_if; + size_t contentlength; + char *type; /* not malloc()ed */ + char *hostname; /* not malloc()ed */ + int mime_flag; + int one_one; /* HTTP/1.1 or better */ + int got_range; + int tildemapped; /* this connection got tilde-mapped */ + off_t first_byte_index, last_byte_index; + int keep_alive; + int should_linger; + GB_FILE_STAT sb; + int conn_fd; + char *file_address; + int file_len; +} httpd_conn; + +/* Methods. */ +#define METHOD_UNKNOWN 0 +#define METHOD_GET 1 +#define METHOD_HEAD 2 +#define METHOD_POST 3 + +/* States for checked_state. */ +#define CHST_FIRSTWORD 0 +#define CHST_FIRSTWS 1 +#define CHST_SECONDWORD 2 +#define CHST_SECONDWS 3 +#define CHST_THIRDWORD 4 +#define CHST_THIRDWS 5 +#define CHST_LINE 6 +#define CHST_LF 7 +#define CHST_CR 8 +#define CHST_CRLF 9 +#define CHST_CRLFCR 10 +#define CHST_BOGUS 11 + + +/* Initializes. Does the socket(), bind(), and listen(). Returns an +** httpd_server* which includes a socket fd that you can select() on. +** Return (httpd_server*) 0 on error. +*/ +extern httpd_server *httpd_initialize(char *hostname, httpd_sockaddr * sa4P, + httpd_sockaddr * sa6P, + unsigned short port, char *cgi_pattern, + int cgi_limit, int cgi_timelimit, + char *charset, char *p3p, int max_age, + char *cwd, int no_log, FILE * logfp, + int no_symlink_check, int vhost, + int global_passwd, char *url_pattern, + char *local_pattern, + int no_empty_referers); + +/* Change the log file. */ +extern void httpd_set_logfp(httpd_server * hs, FILE * logfp); + +/* Call to unlisten/close socket(s) listening for new connections. */ +extern void httpd_unlisten(httpd_server * hs); + +/* Call to shut down. */ +extern void httpd_terminate(httpd_server * hs); + + +/* When a listen fd is ready to read, call this. It does the accept() and +** returns an httpd_conn* which includes the fd to read the request from and +** write the response to. Returns an indication of whether the accept() +** failed, succeeded, or if there were no more connections to accept. +** +** In order to minimize malloc()s, the caller passes in the httpd_conn. +** The caller is also responsible for setting initialized to zero before the +** first call using each different httpd_conn. +*/ +extern int httpd_get_conn(httpd_server * hs, int listen_fd, httpd_conn * hc); +#define GC_FAIL 0 +#define GC_OK 1 +#define GC_NO_MORE 2 + +/* Checks whether the data in hc->read_buf constitutes a complete request +** yet. The caller reads data into hc->read_buf[hc->read_idx] and advances +** hc->read_idx. This routine checks what has been read so far, using +** hc->checked_idx and hc->checked_state to keep track, and returns an +** indication of whether there is no complete request yet, there is a +** complete request, or there won't be a valid request due to a syntax error. +*/ +extern int httpd_got_request(httpd_conn * hc); +#define GR_NO_REQUEST 0 +#define GR_GOT_REQUEST 1 +#define GR_BAD_REQUEST 2 + +/* Parses the request in hc->read_buf. Fills in lots of fields in hc, +** like the URL and the various headers. +** +** Returns -1 on error. +*/ +extern int httpd_parse_request(httpd_conn * hc); + +/* Starts sending data back to the client. In some cases (directories, +** CGI programs), finishes sending by itself - in those cases, hc->file_fd +** is <0. If there is more data to be sent, then hc->file_fd is a file +** descriptor for the file to send. If you don't have a current timeval +** handy just pass in 0. +** +** Returns -1 on error. +*/ +extern int httpd_start_request(httpd_conn * hc, struct timeval *nowP); + +/* Actually sends any buffered response text. */ +extern void httpd_write_response(httpd_conn * hc); + +/* Call this to close down a connection and free the data. A fine point, +** if you fork() with a connection open you should still call this in the +** parent process - the connection will stay open in the child. +** If you don't have a current timeval handy just pass in 0. +*/ +extern void httpd_close_conn(httpd_conn * hc, struct timeval *nowP); + +/* Call this to de-initialize a connection struct and *really* free the +** mallocced strings. +*/ +extern void httpd_destroy_conn(httpd_conn * hc); + + +/* Send an error message back to the client. */ +extern void httpd_send_err(httpd_conn * hc, int status, char *title, + char *extraheads, char *form, char *arg); + +/* Some error messages. */ +extern char *httpd_err400title; +extern char *httpd_err400form; +extern char *httpd_err408title; +extern char *httpd_err408form; +extern char *httpd_err503title; +extern char *httpd_err503form; + +/* Generate a string representation of a method number. */ +extern char *httpd_method_str(int method); + +/* Reallocate a string. */ +extern void httpd_realloc_str(char **strP, size_t * maxsizeP, size_t size); + +/* Format a network socket to a string representation. */ +extern char *httpd_ntoa(httpd_sockaddr * saP); + +/* Set NDELAY mode on a socket. */ +extern void httpd_set_ndelay(int fd); + +/* Clear NDELAY mode on a socket. */ +extern void httpd_clear_ndelay(int fd); + +/* Read the requested buffer completely, accounting for interruptions. */ +extern int httpd_read_fully(int fd, void *buf, size_t nbytes); + +/* Write the requested buffer completely, accounting for interruptions. */ +extern int httpd_write_fully(int fd, const void *buf, size_t nbytes); + +/* Generate debugging statistics syslog message. */ +extern void httpd_logstats(long secs); + +extern int httpd_check_paused(httpd_conn *hc); + +#endif /* _LIBHTTPD_H_ */ diff --git a/gb.httpd/src/main.c b/gb.httpd/src/main.c new file mode 100644 index 00000000..394adc58 --- /dev/null +++ b/gb.httpd/src/main.c @@ -0,0 +1,90 @@ +/*************************************************************************** + + main.c + + gb.httpd component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gb_common.h" + +#include +#include + +#include "main.h" + +const GB_INTERFACE *GB_PTR EXPORT; + +int thttpd_main(int argc, char **argv, bool debug); + +static jmp_buf _setjmp_env; +static bool _debug = FALSE; + +void syslog(int priority, const char *format, ...) +{ + va_list args; + + if (!_debug && priority != LOG_CRIT) + return; + + va_start(args, format); + + fprintf(stderr, "gb.httpd: "); + vfprintf(stderr, format, args); + putc('\n', stderr); + + va_end(args); +} + +void run_cgi(void) +{ + longjmp(_setjmp_env, 1); +} + +void EXPORT GB_MAIN(int argc, char **argv) +{ + char *env; + + if (setjmp(_setjmp_env) == 0) + { + GB.System.SetLanguage("C"); + + env = getenv("GB_HTTPD_DEBUG"); + if (env && env[0] && strcmp(env, "0") != 0) + _debug = TRUE; + + thttpd_main(argc, argv, GB.System.Debug()); + } + else + { + GB.System.HasForked(); + } +} + +int EXPORT GB_INIT() +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.httpd/src/main.h b/gb.httpd/src/main.h new file mode 100644 index 00000000..3fdb5469 --- /dev/null +++ b/gb.httpd/src/main.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + main.h + + gb.httpd component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" + +#ifndef __MAIN_C +extern const GB_INTERFACE *GB_PTR; +#endif + +#define GB (*GB_PTR) + +#define LOG_EMERG 0 /* system is unusable */ +#define LOG_ALERT 1 /* action must be taken immediately */ +#define LOG_CRIT 2 /* critical conditions */ +#define LOG_ERR 3 /* error conditions */ +#define LOG_WARNING 4 /* warning conditions */ +#define LOG_NOTICE 5 /* normal but significant condition */ +#define LOG_INFO 6 /* informational */ +#define LOG_DEBUG 7 /* debug-level messages */ + +void syslog(int priority, const char *format, ...); +#define closelog() + +void run_cgi(); + +#endif /* __MAIN_H */ diff --git a/gb.httpd/src/match.c b/gb.httpd/src/match.c new file mode 100644 index 00000000..f3ebbaf7 --- /dev/null +++ b/gb.httpd/src/match.c @@ -0,0 +1,86 @@ +/* match.c - simple shell-style filename matcher +** +** Only does ? * and **, and multiple patterns separated by |. Returns 1 or 0. +** +** (c) 1995,2000 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + + +#include + +#include "match.h" + +static int match_one(const char *pattern, int patternlen, const char *string); + +int match(const char *pattern, const char *string) +{ + const char *or; + + for (;;) + { + or = strchr(pattern, '|'); + if (or == (char *) 0) + return match_one(pattern, strlen(pattern), string); + if (match_one(pattern, or - pattern, string)) + return 1; + pattern = or + 1; + } +} + + +static int match_one(const char *pattern, int patternlen, const char *string) +{ + const char *p; + + for (p = pattern; p - pattern < patternlen; ++p, ++string) + { + if (*p == '?' && *string != '\0') + continue; + if (*p == '*') + { + int i, pl; + ++p; + if (*p == '*') + { + /* Double-wildcard matches anything. */ + ++p; + i = strlen(string); + } + else + /* Single-wildcard matches anything but slash. */ + i = strcspn(string, "/"); + pl = patternlen - (p - pattern); + for (; i >= 0; --i) + if (match_one(p, pl, &(string[i]))) + return 1; + return 0; + } + if (*p != *string) + return 0; + } + if (*string == '\0') + return 1; + return 0; +} diff --git a/gb.httpd/src/match.h b/gb.httpd/src/match.h new file mode 100644 index 00000000..0de85eee --- /dev/null +++ b/gb.httpd/src/match.h @@ -0,0 +1,36 @@ +/* match.h - simple shell-style filename patcher +** +** (c) 1995 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _MATCH_H_ +#define _MATCH_H_ + +/* Simple shell-style filename pattern matcher. Only does ? * and **, and +** multiple patterns separated by |. Returns 1 or 0. +*/ +extern int match(const char *pattern, const char *string); + +#endif /* _MATCH_H_ */ diff --git a/gb.httpd/src/mime_encodings.h b/gb.httpd/src/mime_encodings.h new file mode 100644 index 00000000..b1130e7e --- /dev/null +++ b/gb.httpd/src/mime_encodings.h @@ -0,0 +1,8 @@ +{ +"Z", 0, "compress", 0}, + +{ +"gz", 0, "gzip", 0}, + +{ +"uu", 0, "x-uuencode", 0}, diff --git a/gb.httpd/src/mime_types.h b/gb.httpd/src/mime_types.h new file mode 100644 index 00000000..e049fc4c --- /dev/null +++ b/gb.httpd/src/mime_types.h @@ -0,0 +1,569 @@ +{ +"a", 0, "application/octet-stream", 0}, + +{ +"aab", 0, "application/x-authorware-bin", 0}, + +{ +"aam", 0, "application/x-authorware-map", 0}, + +{ +"aas", 0, "application/x-authorware-seg", 0}, + +{ +"ai", 0, "application/postscript", 0}, + +{ +"aif", 0, "audio/x-aiff", 0}, + +{ +"aifc", 0, "audio/x-aiff", 0}, + +{ +"aiff", 0, "audio/x-aiff", 0}, + +{ +"asc", 0, "text/plain", 0}, + +{ +"asf", 0, "video/x-ms-asf", 0}, + +{ +"asx", 0, "video/x-ms-asf", 0}, + +{ +"au", 0, "audio/basic", 0}, + +{ +"avi", 0, "video/x-msvideo", 0}, + +{ +"bcpio", 0, "application/x-bcpio", 0}, + +{ +"bin", 0, "application/octet-stream", 0}, + +{ +"bmp", 0, "image/bmp", 0}, + +{ +"cdf", 0, "application/x-netcdf", 0}, + +{ +"class", 0, "application/x-java-vm", 0}, + +{ +"cpio", 0, "application/x-cpio", 0}, + +{ +"cpt", 0, "application/mac-compactpro", 0}, + +{ +"crl", 0, "application/x-pkcs7-crl", 0}, + +{ +"crt", 0, "application/x-x509-ca-cert", 0}, + +{ +"csh", 0, "application/x-csh", 0}, + +{ +"css", 0, "text/css", 0}, + +{ +"dcr", 0, "application/x-director", 0}, + +{ +"dir", 0, "application/x-director", 0}, + +{ +"djv", 0, "image/vnd.djvu", 0}, + +{ +"djvu", 0, "image/vnd.djvu", 0}, + +{ +"dll", 0, "application/octet-stream", 0}, + +{ +"dms", 0, "application/octet-stream", 0}, + +{ +"doc", 0, "application/msword", 0}, + +{ +"dtd", 0, "text/xml", 0}, + +{ +"dump", 0, "application/octet-stream", 0}, + +{ +"dvi", 0, "application/x-dvi", 0}, + +{ +"dxr", 0, "application/x-director", 0}, + +{ +"eps", 0, "application/postscript", 0}, + +{ +"etx", 0, "text/x-setext", 0}, + +{ +"exe", 0, "application/octet-stream", 0}, + +{ +"ez", 0, "application/andrew-inset", 0}, + +{ +"fgd", 0, "application/x-director", 0}, + +{ +"fh", 0, "image/x-freehand", 0}, + +{ +"fh4", 0, "image/x-freehand", 0}, + +{ +"fh5", 0, "image/x-freehand", 0}, + +{ +"fh7", 0, "image/x-freehand", 0}, + +{ +"fhc", 0, "image/x-freehand", 0}, + +{ +"gif", 0, "image/gif", 0}, + +{ +"gtar", 0, "application/x-gtar", 0}, + +{ +"hdf", 0, "application/x-hdf", 0}, + +{ +"hqx", 0, "application/mac-binhex40", 0}, + +{ +"htm", 0, "text/html; charset=%s", 0}, + +{ +"html", 0, "text/html; charset=%s", 0}, + +{ +"ice", 0, "x-conference/x-cooltalk", 0}, + +{ +"ief", 0, "image/ief", 0}, + +{ +"iges", 0, "model/iges", 0}, + +{ +"igs", 0, "model/iges", 0}, + +{ +"iv", 0, "application/x-inventor", 0}, + +{ +"jar", 0, "application/x-java-archive", 0}, + +{ +"jfif", 0, "image/jpeg", 0}, + +{ +"jpe", 0, "image/jpeg", 0}, + +{ +"jpeg", 0, "image/jpeg", 0}, + +{ +"jpg", 0, "image/jpeg", 0}, + +{ +"js", 0, "application/x-javascript", 0}, + +{ +"kar", 0, "audio/midi", 0}, + +{ +"latex", 0, "application/x-latex", 0}, + +{ +"lha", 0, "application/octet-stream", 0}, + +{ +"lzh", 0, "application/octet-stream", 0}, + +{ +"m3u", 0, "audio/x-mpegurl", 0}, + +{ +"man", 0, "application/x-troff-man", 0}, + +{ +"mathml", 0, "application/mathml+xml", 0}, + +{ +"me", 0, "application/x-troff-me", 0}, + +{ +"mesh", 0, "model/mesh", 0}, + +{ +"mid", 0, "audio/midi", 0}, + +{ +"midi", 0, "audio/midi", 0}, + +{ +"mif", 0, "application/vnd.mif", 0}, + +{ +"mime", 0, "message/rfc822", 0}, + +{ +"mml", 0, "application/mathml+xml", 0}, + +{ +"mov", 0, "video/quicktime", 0}, + +{ +"movie", 0, "video/x-sgi-movie", 0}, + +{ +"mp2", 0, "audio/mpeg", 0}, + +{ +"mp3", 0, "audio/mpeg", 0}, + +{ +"mp4", 0, "video/mp4", 0}, + +{ +"mpe", 0, "video/mpeg", 0}, + +{ +"mpeg", 0, "video/mpeg", 0}, + +{ +"mpg", 0, "video/mpeg", 0}, + +{ +"mpga", 0, "audio/mpeg", 0}, + +{ +"ms", 0, "application/x-troff-ms", 0}, + +{ +"msh", 0, "model/mesh", 0}, + +{ +"mv", 0, "video/x-sgi-movie", 0}, + +{ +"mxu", 0, "video/vnd.mpegurl", 0}, + +{ +"nc", 0, "application/x-netcdf", 0}, + +{ +"o", 0, "application/octet-stream", 0}, + +{ +"oda", 0, "application/oda", 0}, + +{ +"ogg", 0, "application/x-ogg", 0}, + +{ +"pac", 0, "application/x-ns-proxy-autoconfig", 0}, + +{ +"pbm", 0, "image/x-portable-bitmap", 0}, + +{ +"pdb", 0, "chemical/x-pdb", 0}, + +{ +"pdf", 0, "application/pdf", 0}, + +{ +"pgm", 0, "image/x-portable-graymap", 0}, + +{ +"pgn", 0, "application/x-chess-pgn", 0}, + +{ +"png", 0, "image/png", 0}, + +{ +"pnm", 0, "image/x-portable-anymap", 0}, + +{ +"ppm", 0, "image/x-portable-pixmap", 0}, + +{ +"ppt", 0, "application/vnd.ms-powerpoint", 0}, + +{ +"ps", 0, "application/postscript", 0}, + +{ +"qt", 0, "video/quicktime", 0}, + +{ +"ra", 0, "audio/x-realaudio", 0}, + +{ +"ram", 0, "audio/x-pn-realaudio", 0}, + +{ +"ras", 0, "image/x-cmu-raster", 0}, + +{ +"rdf", 0, "application/rdf+xml", 0}, + +{ +"rgb", 0, "image/x-rgb", 0}, + +{ +"rm", 0, "audio/x-pn-realaudio", 0}, + +{ +"roff", 0, "application/x-troff", 0}, + +{ +"rpm", 0, "audio/x-pn-realaudio-plugin", 0}, + +{ +"rss", 0, "application/rss+xml", 0}, + +{ +"rtf", 0, "text/rtf", 0}, + +{ +"rtx", 0, "text/richtext", 0}, + +{ +"sgm", 0, "text/sgml", 0}, + +{ +"sgml", 0, "text/sgml", 0}, + +{ +"sh", 0, "application/x-sh", 0}, + +{ +"shar", 0, "application/x-shar", 0}, + +{ +"silo", 0, "model/mesh", 0}, + +{ +"sit", 0, "application/x-stuffit", 0}, + +{ +"skd", 0, "application/x-koan", 0}, + +{ +"skm", 0, "application/x-koan", 0}, + +{ +"skp", 0, "application/x-koan", 0}, + +{ +"skt", 0, "application/x-koan", 0}, + +{ +"smi", 0, "application/smil", 0}, + +{ +"smil", 0, "application/smil", 0}, + +{ +"snd", 0, "audio/basic", 0}, + +{ +"so", 0, "application/octet-stream", 0}, + +{ +"spl", 0, "application/x-futuresplash", 0}, + +{ +"src", 0, "application/x-wais-source", 0}, + +{ +"stc", 0, "application/vnd.sun.xml.calc.template", 0}, + +{ +"std", 0, "application/vnd.sun.xml.draw.template", 0}, + +{ +"sti", 0, "application/vnd.sun.xml.impress.template", 0}, + +{ +"stw", 0, "application/vnd.sun.xml.writer.template", 0}, + +{ +"sv4cpio", 0, "application/x-sv4cpio", 0}, + +{ +"sv4crc", 0, "application/x-sv4crc", 0}, + +{ +"svg", 0, "image/svg+xml", 0}, + +{ +"svgz", 0, "image/svg+xml", 0}, + +{ +"swf", 0, "application/x-shockwave-flash", 0}, + +{ +"sxc", 0, "application/vnd.sun.xml.calc", 0}, + +{ +"sxd", 0, "application/vnd.sun.xml.draw", 0}, + +{ +"sxg", 0, "application/vnd.sun.xml.writer.global", 0}, + +{ +"sxi", 0, "application/vnd.sun.xml.impress", 0}, + +{ +"sxm", 0, "application/vnd.sun.xml.math", 0}, + +{ +"sxw", 0, "application/vnd.sun.xml.writer", 0}, + +{ +"t", 0, "application/x-troff", 0}, + +{ +"tar", 0, "application/x-tar", 0}, + +{ +"tcl", 0, "application/x-tcl", 0}, + +{ +"tex", 0, "application/x-tex", 0}, + +{ +"texi", 0, "application/x-texinfo", 0}, + +{ +"texinfo", 0, "application/x-texinfo", 0}, + +{ +"tif", 0, "image/tiff", 0}, + +{ +"tiff", 0, "image/tiff", 0}, + +{ +"tr", 0, "application/x-troff", 0}, + +{ +"tsp", 0, "application/dsptype", 0}, + +{ +"tsv", 0, "text/tab-separated-values", 0}, + +{ +"txt", 0, "text/plain; charset=%s", 0}, + +{ +"ustar", 0, "application/x-ustar", 0}, + +{ +"vcd", 0, "application/x-cdlink", 0}, + +{ +"vrml", 0, "model/vrml", 0}, + +{ +"vx", 0, "video/x-rad-screenplay", 0}, + +{ +"wav", 0, "audio/x-wav", 0}, + +{ +"wax", 0, "audio/x-ms-wax", 0}, + +{ +"wbmp", 0, "image/vnd.wap.wbmp", 0}, + +{ +"wbxml", 0, "application/vnd.wap.wbxml", 0}, + +{ +"wm", 0, "video/x-ms-wm", 0}, + +{ +"wma", 0, "audio/x-ms-wma", 0}, + +{ +"wmd", 0, "application/x-ms-wmd", 0}, + +{ +"wml", 0, "text/vnd.wap.wml", 0}, + +{ +"wmlc", 0, "application/vnd.wap.wmlc", 0}, + +{ +"wmls", 0, "text/vnd.wap.wmlscript", 0}, + +{ +"wmlsc", 0, "application/vnd.wap.wmlscriptc", 0}, + +{ +"wmv", 0, "video/x-ms-wmv", 0}, + +{ +"wmx", 0, "video/x-ms-wmx", 0}, + +{ +"wmz", 0, "application/x-ms-wmz", 0}, + +{ +"wrl", 0, "model/vrml", 0}, + +{ +"wsrc", 0, "application/x-wais-source", 0}, + +{ +"wvx", 0, "video/x-ms-wvx", 0}, + +{ +"xbm", 0, "image/x-xbitmap", 0}, + +{ +"xht", 0, "application/xhtml+xml", 0}, + +{ +"xhtml", 0, "application/xhtml+xml", 0}, + +{ +"xls", 0, "application/vnd.ms-excel", 0}, + +{ +"xml", 0, "text/xml", 0}, + +{ +"xpm", 0, "image/x-xpixmap", 0}, + +{ +"xsl", 0, "text/xml", 0}, + +{ +"xwd", 0, "image/x-xwindowdump", 0}, + +{ +"xyz", 0, "chemical/x-xyz", 0}, + +{ +"zip", 0, "application/zip", 0}, diff --git a/gb.httpd/src/strerror.c b/gb.httpd/src/strerror.c new file mode 100644 index 00000000..0299e332 --- /dev/null +++ b/gb.httpd/src/strerror.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strerror.c 5.1 (Berkeley) 4/9/89"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include + +char *strerror(errnum) + int errnum; +{ + extern int sys_nerr; + extern char *sys_errlist[]; + static char ebuf[20]; + + if ((unsigned int) errnum < sys_nerr) + return (sys_errlist[errnum]); + (void) sprintf(ebuf, "Unknown error: %d", errnum); + return (ebuf); +} diff --git a/gb.httpd/src/tdate_parse.c b/gb.httpd/src/tdate_parse.c new file mode 100644 index 00000000..dcc91e19 --- /dev/null +++ b/gb.httpd/src/tdate_parse.c @@ -0,0 +1,312 @@ +/* tdate_parse - parse string dates into internal form, stripped-down version +** +** (c) 1995 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +/* This is a stripped-down version of date_parse.c, available at +** http://www.acme.com/software/date_parse/ +*/ + +#include + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include +#include +#include +#include + +#include "tdate_parse.h" + + +struct strlong +{ + char *s; + long l; +}; + + +static void pound_case(char *str) +{ + for (; *str != '\0'; ++str) + { + if (isupper((int) *str)) + *str = tolower((int) *str); + } +} + +static int strlong_compare(v1, v2) + char *v1; + char *v2; +{ + return strcmp(((struct strlong *) v1)->s, ((struct strlong *) v2)->s); +} + + +static int strlong_search(char *str, struct strlong *tab, int n, long *lP) +{ + int i, h, l, r; + + l = 0; + h = n - 1; + for (;;) + { + i = (h + l) / 2; + r = strcmp(str, tab[i].s); + if (r < 0) + h = i - 1; + else if (r > 0) + l = i + 1; + else + { + *lP = tab[i].l; + return 1; + } + if (h < l) + return 0; + } +} + + +static int scan_wday(char *str_wday, long *tm_wdayP) +{ + static struct strlong wday_tab[] = { + {"sun", 0}, {"sunday", 0}, + {"mon", 1}, {"monday", 1}, + {"tue", 2}, {"tuesday", 2}, + {"wed", 3}, {"wednesday", 3}, + {"thu", 4}, {"thursday", 4}, + {"fri", 5}, {"friday", 5}, + {"sat", 6}, {"saturday", 6}, + }; + static int sorted = 0; + + if (!sorted) + { + (void) qsort(wday_tab, sizeof(wday_tab) / sizeof(struct strlong), + sizeof(struct strlong), strlong_compare); + sorted = 1; + } + pound_case(str_wday); + return strlong_search(str_wday, wday_tab, + sizeof(wday_tab) / sizeof(struct strlong), tm_wdayP); +} + + +static int scan_mon(char *str_mon, long *tm_monP) +{ + static struct strlong mon_tab[] = { + {"jan", 0}, {"january", 0}, + {"feb", 1}, {"february", 1}, + {"mar", 2}, {"march", 2}, + {"apr", 3}, {"april", 3}, + {"may", 4}, + {"jun", 5}, {"june", 5}, + {"jul", 6}, {"july", 6}, + {"aug", 7}, {"august", 7}, + {"sep", 8}, {"september", 8}, + {"oct", 9}, {"october", 9}, + {"nov", 10}, {"november", 10}, + {"dec", 11}, {"december", 11}, + }; + static int sorted = 0; + + if (!sorted) + { + (void) qsort(mon_tab, sizeof(mon_tab) / sizeof(struct strlong), + sizeof(struct strlong), strlong_compare); + sorted = 1; + } + pound_case(str_mon); + return strlong_search(str_mon, mon_tab, + sizeof(mon_tab) / sizeof(struct strlong), tm_monP); +} + + +static int is_leap(int year) +{ + return year % 400 ? (year % 100 ? (year % 4 ? 0 : 1) : 0) : 1; +} + + +/* Basically the same as mktime(). */ +static time_t tm_to_time(struct tm *tmP) +{ + time_t t; + static int monthtab[12] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 + }; + + /* Years since epoch, converted to days. */ + t = (tmP->tm_year - 70) * 365; + /* Leap days for previous years. */ + t += (tmP->tm_year - 69) / 4; + /* Days for the beginning of this month. */ + t += monthtab[tmP->tm_mon]; + /* Leap day for this year. */ + if (tmP->tm_mon >= 2 && is_leap(tmP->tm_year + 1900)) + ++t; + /* Days since the beginning of this month. */ + t += tmP->tm_mday - 1; /* 1-based field */ + /* Hours, minutes, and seconds. */ + t = t * 24 + tmP->tm_hour; + t = t * 60 + tmP->tm_min; + t = t * 60 + tmP->tm_sec; + + return t; +} + + +time_t tdate_parse(char *str) +{ + struct tm tm; + char *cp; + char str_mon[500], str_wday[500]; + int tm_sec, tm_min, tm_hour, tm_mday, tm_year; + long tm_mon, tm_wday; + time_t t; + + /* Initialize. */ + (void) memset((char *) &tm, 0, sizeof(struct tm)); + + /* Skip initial whitespace ourselves - sscanf is clumsy at this. */ + for (cp = str; *cp == ' ' || *cp == '\t'; ++cp) + continue; + + /* And do the sscanfs. WARNING: you can add more formats here, + ** but be careful! You can easily screw up the parsing of existing + ** formats when you add new ones. The order is important. + */ + + /* DD-mth-YY HH:MM:SS GMT */ + if (sscanf(cp, "%d-%400[a-zA-Z]-%d %d:%d:%d GMT", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec) == 6 && scan_mon(str_mon, &tm_mon)) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + + /* DD mth YY HH:MM:SS GMT */ + else if (sscanf(cp, "%d %400[a-zA-Z] %d %d:%d:%d GMT", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec) == 6 && scan_mon(str_mon, &tm_mon)) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + + /* HH:MM:SS GMT DD-mth-YY */ + else if (sscanf(cp, "%d:%d:%d GMT %d-%400[a-zA-Z]-%d", + &tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon, + &tm_year) == 6 && scan_mon(str_mon, &tm_mon)) + { + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + } + + /* HH:MM:SS GMT DD mth YY */ + else if (sscanf(cp, "%d:%d:%d GMT %d %400[a-zA-Z] %d", + &tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon, + &tm_year) == 6 && scan_mon(str_mon, &tm_mon)) + { + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + } + + /* wdy, DD-mth-YY HH:MM:SS GMT */ + else if (sscanf(cp, "%400[a-zA-Z], %d-%400[a-zA-Z]-%d %d:%d:%d GMT", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec) == 7 && + scan_wday(str_wday, &tm_wday) && scan_mon(str_mon, &tm_mon)) + { + tm.tm_wday = tm_wday; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + + /* wdy, DD mth YY HH:MM:SS GMT */ + else if (sscanf(cp, "%400[a-zA-Z], %d %400[a-zA-Z] %d %d:%d:%d GMT", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec) == 7 && + scan_wday(str_wday, &tm_wday) && scan_mon(str_mon, &tm_mon)) + { + tm.tm_wday = tm_wday; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + + /* wdy mth DD HH:MM:SS GMT YY */ + else if (sscanf(cp, "%400[a-zA-Z] %400[a-zA-Z] %d %d:%d:%d GMT %d", + str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, &tm_sec, + &tm_year) == 7 && + scan_wday(str_wday, &tm_wday) && scan_mon(str_mon, &tm_mon)) + { + tm.tm_wday = tm_wday; + tm.tm_mon = tm_mon; + tm.tm_mday = tm_mday; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + tm.tm_year = tm_year; + } + else + return (time_t) - 1; + + if (tm.tm_year > 1900) + tm.tm_year -= 1900; + else if (tm.tm_year < 70) + tm.tm_year += 100; + + t = tm_to_time(&tm); + + return t; +} diff --git a/gb.httpd/src/tdate_parse.h b/gb.httpd/src/tdate_parse.h new file mode 100644 index 00000000..1a908163 --- /dev/null +++ b/gb.httpd/src/tdate_parse.h @@ -0,0 +1,33 @@ +/* tdate_parse.h - parse string dates into internal form, stripped-down version +** +** (c) 1995 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _TDATE_PARSE_H_ +#define _TDATE_PARSE_H_ + +extern time_t tdate_parse(char *str); + +#endif /* _TDATE_PARSE_H_ */ diff --git a/gb.httpd/src/thttpd.c b/gb.httpd/src/thttpd.c new file mode 100644 index 00000000..ac80813f --- /dev/null +++ b/gb.httpd/src/thttpd.c @@ -0,0 +1,2277 @@ +/* thttpd.c - tiny/turbo/throttling HTTP server +** +** (c) 1995,1998,1999,2000,2001 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#include "main.h" +#include "thttpd.h" +#include "version.h" + +#include +#include +#include +#include +#include +#include + +#include +#ifdef HAVE_FCNTL_H +#include +#endif +#include +#ifdef HAVE_GRP_H +#include +#endif +#include +#include +#include +#include +//#include +#ifdef TIME_WITH_SYS_TIME +#include +#endif +#include + +#include "fdwatch.h" +#include "libhttpd.h" +//#include "mmc.h" +#include "timers.h" +#include "match.h" + +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif + +#ifndef HAVE_INT64T +typedef long long int64_t; +#endif + + +static char *argv0; +static bool _debug; +//static int debug; +static unsigned short port; +static char *dir; +static char *data_dir; +static int do_chroot, no_log, no_symlink_check, do_vhost, do_global_passwd; +static char *cgi_pattern; +static int cgi_limit; +static int cgi_timelimit; +static char *url_pattern; +static int no_empty_referers; +static char *local_pattern; +static char *logfile; +static char *throttlefile; +static char *hostname; +static char *pidfile; +static char *user; +static char *charset; +static char *p3p; +static int max_age; + + +typedef struct +{ + char *pattern; + long max_limit, min_limit; + long rate; + off_t bytes_since_avg; + int num_sending; +} throttletab; +static throttletab *throttles; +static int numthrottles, maxthrottles; + +#define THROTTLE_NOLIMIT -1 + + +typedef struct +{ + int conn_state; + int next_free_connect; + httpd_conn *hc; + int tnums[MAXTHROTTLENUMS]; /* throttle indexes */ + int numtnums; + long max_limit, min_limit; + time_t started_at, active_at; + Timer *wakeup_timer; + Timer *linger_timer; + long wouldblock_delay; + off_t bytes; + off_t end_byte_index; + off_t next_byte_index; +} connecttab; +static connecttab *connects; +static int num_connects, max_connects, first_free_connect; +static int httpd_conn_count; + +/* The connection states. */ +#define CNST_FREE 0 +#define CNST_READING 1 +#define CNST_SENDING 2 +#define CNST_PAUSING 3 +#define CNST_LINGERING 4 + + +static httpd_server *hs = (httpd_server *) 0; +int terminate = 0; +time_t start_time, stats_time; +long stats_connections; +off_t stats_bytes; +int stats_simultaneous; + +static volatile int got_hup, got_usr1, watchdog_flag; + + +/* Forwards. */ +static void parse_args(int argc, char **argv); +/*static void usage (void); +static void read_config (char *filename); +static void value_required (char *name, char *value); +static void no_value_required (char *name, char *value);*/ +static char *e_strdup(char *oldstr); +static void lookup_hostname(httpd_sockaddr * sa4P, size_t sa4_len, + int *gotv4P, httpd_sockaddr * sa6P, + size_t sa6_len, int *gotv6P); +static void read_throttlefile(char *throttlefile); +static void shut_down(void); +static int handle_newconnect(struct timeval *tvP, int listen_fd); +static void handle_read(connecttab * c, struct timeval *tvP); +static void handle_send(connecttab * c, struct timeval *tvP); +static void handle_linger(connecttab * c, struct timeval *tvP); +static int check_throttles(connecttab * c); +static void clear_throttles(connecttab * c, struct timeval *tvP); +static void update_throttles(ClientData client_data, struct timeval *nowP); +static void finish_connection(connecttab * c, struct timeval *tvP); +static void clear_connection(connecttab * c, struct timeval *tvP); +static void really_clear_connection(connecttab * c, struct timeval *tvP); +static void idle(ClientData client_data, struct timeval *nowP); +static void wakeup_connection(ClientData client_data, struct timeval *nowP); +static void linger_clear_connection(ClientData client_data, + struct timeval *nowP); +static void occasional(ClientData client_data, struct timeval *nowP); +#ifdef STATS_TIME +static void show_stats(ClientData client_data, struct timeval *nowP); +#endif /* STATS_TIME */ +static void logstats(struct timeval *nowP); +static void thttpd_logstats(long secs); + + +/* SIGTERM and SIGINT say to exit immediately. */ +static void handle_term(int sig) +{ + /* Don't need to set up the handler again, since it's a one-shot. */ + + shut_down(); + syslog(LOG_NOTICE, "exiting due to signal %d", sig); + closelog(); + exit(1); +} + + +/* SIGCHLD - a chile process exitted, so we need to reap the zombie */ +static void handle_chld(int sig) +{ + const int oerrno = errno; + pid_t pid; + int status; + +#ifndef HAVE_SIGSET + /* Set up handler again. */ + (void) signal(SIGCHLD, handle_chld); +#endif /* ! HAVE_SIGSET */ + + /* Reap defunct children until there aren't any more. */ + for (;;) + { +#ifdef HAVE_WAITPID + pid = waitpid((pid_t) - 1, &status, WNOHANG); +#else /* HAVE_WAITPID */ + pid = wait3(&status, WNOHANG, (struct rusage *) 0); +#endif /* HAVE_WAITPID */ + if ((int) pid == 0) /* none left */ + break; + if ((int) pid < 0) + { + if (errno == EINTR || errno == EAGAIN) + continue; + /* ECHILD shouldn't happen with the WNOHANG option, + ** but with some kernels it does anyway. Ignore it. + */ + if (errno != ECHILD) + syslog(LOG_ERR, "child wait - %m"); + break; + } + /* Decrement the CGI count. Note that this is not accurate, since + ** each CGI can involve two or even three child processes. + ** Decrementing for each child means that when there is heavy CGI + ** activity, the count will be lower than it should be, and therefore + ** more CGIs will be allowed than should be. + */ + if (hs != (httpd_server *) 0) + { + --hs->cgi_count; + if (hs->cgi_count < 0) + hs->cgi_count = 0; + } + } + + /* Restore previous errno. */ + errno = oerrno; +} + + +/* SIGHUP says to re-open the log file. */ +static void handle_hup(int sig) +{ + const int oerrno = errno; + +#ifndef HAVE_SIGSET + /* Set up handler again. */ + (void) signal(SIGHUP, handle_hup); +#endif /* ! HAVE_SIGSET */ + + /* Just set a flag that we got the signal. */ + got_hup = 1; + + /* Restore previous errno. */ + errno = oerrno; +} + + +/* SIGUSR1 says to exit as soon as all current connections are done. */ +static void handle_usr1(int sig) +{ + /* Don't need to set up the handler again, since it's a one-shot. */ + + if (!_debug && num_connects == 0) + { + /* If there are no active connections we want to exit immediately + ** here. Not only is it faster, but without any connections the + ** main loop won't wake up until the next new connection. + */ + shut_down(); + syslog(LOG_NOTICE, "exiting"); + closelog(); + exit(0); + } + + /* Otherwise, just set a flag that we got the signal. */ + got_usr1 = 1; + + /* Don't need to restore old errno, since we didn't do any syscalls. */ +} + + +/* SIGUSR2 says to generate the stats syslogs immediately. */ +static void handle_usr2(int sig) +{ + const int oerrno = errno; + +#ifndef HAVE_SIGSET + /* Set up handler again. */ + (void) signal(SIGUSR2, handle_usr2); +#endif /* ! HAVE_SIGSET */ + + logstats((struct timeval *) 0); + + /* Restore previous errno. */ + errno = oerrno; +} + + +/* SIGALRM is used as a watchdog. */ +static void handle_alrm(int sig) +{ + const int oerrno = errno; + + /* If nothing has been happening */ + if (!watchdog_flag) + { + /* Try changing dirs to someplace we can write. */ + (void) chdir("/tmp"); + /* Dump core. */ + abort(); + } + watchdog_flag = 0; + +#ifndef HAVE_SIGSET + /* Set up handler again. */ + (void) signal(SIGALRM, handle_alrm); +#endif /* ! HAVE_SIGSET */ + /* Set up alarm again. */ + (void) alarm(OCCASIONAL_TIME * 3); + + /* Restore previous errno. */ + errno = oerrno; +} + + +static void re_open_logfile(void) +{ + FILE *logfp; + + if (no_log || hs == (httpd_server *) 0) + return; + + /* Re-open the log file. */ + if (logfile != (char *) 0 && strcmp(logfile, "-") != 0) + { + syslog(LOG_NOTICE, "re-opening logfile"); + logfp = fopen(logfile, "a"); + if (logfp == (FILE *) 0) + { + syslog(LOG_CRIT, "re-opening %.80s - %m", logfile); + return; + } + (void) fcntl(fileno(logfp), F_SETFD, 1); + httpd_set_logfp(hs, logfp); + } +} + + +int thttpd_main(int argc, char **argv, bool debug) +{ + char *cp; + struct passwd *pwd; + uid_t uid = 32767; + gid_t gid = 32767; + char cwd[MAXPATHLEN + 1]; + FILE *logfp; + int num_ready; + int cnum; + connecttab *c; + httpd_conn *hc; + httpd_sockaddr sa4; + httpd_sockaddr sa6; + int gotv4, gotv6; + struct timeval tv; + + argv0 = argv[0]; + _debug = debug; + + cp = strrchr(argv0, '/'); + if (cp != (char *) 0) + ++cp; + else + cp = argv0; + + //openlog( cp, LOG_NDELAY|LOG_PID, LOG_FACILITY ); + + /* Handle command-line arguments. */ + parse_args(argc, argv); + //debug = 1; + do_chroot = 0; + if (_debug) + { + cgi_limit = 1; + cgi_timelimit = 0; + } + + /* Read zone info now, in case we chroot(). */ + tzset(); + + /* Look up hostname now, in case we chroot(). */ + lookup_hostname(&sa4, sizeof(sa4), &gotv4, &sa6, sizeof(sa6), &gotv6); + if (!(gotv4 || gotv6)) + { + syslog(LOG_ERR, "can't find any valid address"); + (void) fprintf(stderr, "%s: can't find any valid address\n", argv0); + exit(1); + } + + /* Throttle file. */ + numthrottles = 0; + maxthrottles = 0; + throttles = (throttletab *) 0; + if (throttlefile != (char *) 0) + read_throttlefile(throttlefile); + + /* If we're root and we're going to become another user, get the uid/gid + ** now. + */ + if (getuid() == 0) + { + pwd = getpwnam(user); + if (pwd == (struct passwd *) 0) + { + syslog(LOG_CRIT, "unknown user - '%.80s'", user); + (void) fprintf(stderr, "%s: unknown user - '%s'\n", argv0, user); + exit(1); + } + uid = pwd->pw_uid; + gid = pwd->pw_gid; + } + + /* Log file. */ +#if 0 + if (logfile != (char *) 0) + { + if (strcmp(logfile, "/dev/null") == 0) + { + no_log = 1; + logfp = (FILE *) 0; + } + else if (strcmp(logfile, "-") == 0) + logfp = stdout; + else + { + logfp = fopen(logfile, "a"); + if (logfp == (FILE *) 0) + { + syslog(LOG_CRIT, "%.80s - %m", logfile); + perror(logfile); + exit(1); + } + if (logfile[0] != '/') + { + syslog(LOG_WARNING, + "logfile is not an absolute path, you may not be able to re-open it"); + (void) fprintf(stderr, + "%s: logfile is not an absolute path, you may not be able to re-open it\n", + argv0); + } + (void) fcntl(fileno(logfp), F_SETFD, 1); + if (getuid() == 0) + { + /* If we are root then we chown the log file to the user we'll + ** be switching to. + */ + if (fchown(fileno(logfp), uid, gid) < 0) + { + syslog(LOG_WARNING, "fchown logfile - %m"); + perror("fchown logfile"); + } + } + } + } + else +#endif + logfp = (FILE *) 0; + +#if 0 + /* Switch directories if requested. */ + if (dir != (char *) 0) + { + if (chdir(dir) < 0) + { + syslog(LOG_CRIT, "chdir - %m"); + perror("chdir"); + exit(1); + } + } +#ifdef USE_USER_DIR + else if (getuid() == 0) + { + /* No explicit directory was specified, we're root, and the + ** USE_USER_DIR option is set - switch to the specified user's + ** home dir. + */ + if (chdir(pwd->pw_dir) < 0) + { + syslog(LOG_CRIT, "chdir - %m"); + perror("chdir"); + exit(1); + } + } +#endif /* USE_USER_DIR */ +#endif + + /* Get current directory. */ + (void) getcwd(cwd, sizeof(cwd) - 1); + if (cwd[strlen(cwd) - 1] != '/') + (void) strcat(cwd, "/"); + + if (0) //!debug) + { + /* We're not going to use stdin stdout or stderr from here on, so close + ** them to save file descriptors. + */ + (void) fclose(stdin); + if (logfp != stdout) + (void) fclose(stdout); + (void) fclose(stderr); + + /* Daemonize - make ourselves a subprocess. */ +#ifdef HAVE_DAEMON + if (daemon(1, 1) < 0) + { + syslog(LOG_CRIT, "daemon - %m"); + exit(1); + } +#else /* HAVE_DAEMON */ + switch (fork()) + { + case 0: + break; + case -1: + syslog(LOG_CRIT, "fork - %m"); + exit(1); + default: + exit(0); + } +#ifdef HAVE_SETSID + (void) setsid(); +#endif /* HAVE_SETSID */ +#endif /* HAVE_DAEMON */ + } + else + { + /* Even if we don't daemonize, we still want to disown our parent + ** process. + */ +#ifdef HAVE_SETSID + (void) setsid(); +#endif /* HAVE_SETSID */ + } + +#if 0 + if (pidfile != (char *) 0) + { + /* Write the PID file. */ + FILE *pidfp = fopen(pidfile, "w"); + if (pidfp == (FILE *) 0) + { + syslog(LOG_CRIT, "%.80s - %m", pidfile); + exit(1); + } + (void) fprintf(pidfp, "%d\n", (int) getpid()); + (void) fclose(pidfp); + } +#endif + + /* Initialize the fdwatch package. Have to do this before chroot, + ** if /dev/poll is used. + */ + max_connects = fdwatch_get_nfiles(); + if (max_connects < 0) + { + syslog(LOG_CRIT, "fdwatch initialization failure"); + exit(1); + } + max_connects -= SPARE_FDS; + + /* Chroot if requested. */ + if (0) //do_chroot) + { + if (chroot(cwd) < 0) + { + syslog(LOG_CRIT, "chroot - %m"); + perror("chroot"); + exit(1); + } + /* If we're logging and the logfile's pathname begins with the + ** chroot tree's pathname, then elide the chroot pathname so + ** that the logfile pathname still works from inside the chroot + ** tree. + */ + if (logfile != (char *) 0 && strcmp(logfile, "-") != 0) + { + if (strncmp(logfile, cwd, strlen(cwd)) == 0) + { + (void) strcpy(logfile, &logfile[strlen(cwd) - 1]); + /* (We already guaranteed that cwd ends with a slash, so leaving + ** that slash in logfile makes it an absolute pathname within + ** the chroot tree.) + */ + } + else + { + syslog(LOG_WARNING, + "logfile is not within the chroot tree, you will not be able to re-open it"); + (void) fprintf(stderr, + "%s: logfile is not within the chroot tree, you will not be able to re-open it\n", + argv0); + } + } + (void) strcpy(cwd, "/"); + /* Always chdir to / after a chroot. */ + if (chdir(cwd) < 0) + { + syslog(LOG_CRIT, "chroot chdir - %m"); + perror("chroot chdir"); + exit(1); + } + } + + /* Switch directories again if requested. */ +#if 0 + if (data_dir != (char *) 0) + { + if (chdir(data_dir) < 0) + { + syslog(LOG_CRIT, "data_dir chdir - %m"); + perror("data_dir chdir"); + exit(1); + } + } +#endif + + /* Set up to catch signals. */ +#ifdef HAVE_SIGSET + (void) sigset(SIGTERM, handle_term); + (void) sigset(SIGINT, handle_term); + (void) sigset(SIGCHLD, handle_chld); + (void) sigset(SIGPIPE, SIG_IGN); /* get EPIPE instead */ + (void) sigset(SIGHUP, handle_hup); + (void) sigset(SIGUSR1, handle_usr1); + (void) sigset(SIGUSR2, handle_usr2); + (void) sigset(SIGALRM, handle_alrm); +#else /* HAVE_SIGSET */ + (void) signal(SIGTERM, handle_term); + (void) signal(SIGINT, handle_term); + (void) signal(SIGCHLD, handle_chld); + (void) signal(SIGPIPE, SIG_IGN); /* get EPIPE instead */ + (void) signal(SIGHUP, handle_hup); + (void) signal(SIGUSR1, handle_usr1); + (void) signal(SIGUSR2, handle_usr2); + (void) signal(SIGALRM, handle_alrm); +#endif /* HAVE_SIGSET */ + got_hup = 0; + got_usr1 = 0; + watchdog_flag = 0; + (void) alarm(OCCASIONAL_TIME * 3); + + /* Initialize the timer package. */ + tmr_init(); + + /* Initialize the HTTP layer. Got to do this before giving up root, + ** so that we can bind to a privileged port. + */ + hs = httpd_initialize(hostname, + gotv4 ? &sa4 : (httpd_sockaddr *) 0, + gotv6 ? &sa6 : (httpd_sockaddr *) 0, port, + cgi_pattern, cgi_limit, cgi_timelimit, charset, p3p, + max_age, cwd, no_log, logfp, no_symlink_check, + do_vhost, do_global_passwd, url_pattern, + local_pattern, no_empty_referers); + if (hs == (httpd_server *) 0) + exit(1); + + hs->debug = _debug; + + /* Set up the occasional timer. */ + if (tmr_create + ((struct timeval *) 0, occasional, JunkClientData, + OCCASIONAL_TIME * 1000L, 1) == (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(occasional) failed"); + exit(1); + } + /* Set up the idle timer. */ + if (tmr_create((struct timeval *) 0, idle, JunkClientData, 5 * 1000L, 1) == + (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(idle) failed"); + exit(1); + } + if (numthrottles > 0) + { + /* Set up the throttles timer. */ + if (tmr_create + ((struct timeval *) 0, update_throttles, JunkClientData, + THROTTLE_TIME * 1000L, 1) == (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(update_throttles) failed"); + exit(1); + } + } +#ifdef STATS_TIME + /* Set up the stats timer. */ + if (tmr_create + ((struct timeval *) 0, show_stats, JunkClientData, STATS_TIME * 1000L, + 1) == (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(show_stats) failed"); + exit(1); + } +#endif /* STATS_TIME */ + start_time = stats_time = time((time_t *) 0); + stats_connections = 0; + stats_bytes = 0; + stats_simultaneous = 0; + + /* If we're root, try to become someone else. */ + if (getuid() == 0) + { + /* Set aux groups to null. */ + if (setgroups(0, (const gid_t *) 0) < 0) + { + syslog(LOG_CRIT, "setgroups - %m"); + exit(1); + } + /* Set primary group. */ + if (setgid(gid) < 0) + { + syslog(LOG_CRIT, "setgid - %m"); + exit(1); + } + /* Try setting aux groups correctly - not critical if this fails. */ + if (initgroups(user, gid) < 0) + syslog(LOG_WARNING, "initgroups - %m"); +#ifdef HAVE_SETLOGIN + /* Set login name. */ + (void) setlogin(user); +#endif /* HAVE_SETLOGIN */ + /* Set uid. */ + if (setuid(uid) < 0) + { + syslog(LOG_CRIT, "setuid - %m"); + exit(1); + } + /* Check for unnecessary security exposure. */ + if (!do_chroot) + syslog(LOG_WARNING, + "started as root without requesting chroot(), warning only"); + } + + /* Initialize our connections table. */ + connects = NEW(connecttab, max_connects); + if (connects == (connecttab *) 0) + { + syslog(LOG_CRIT, "out of memory allocating a connecttab"); + exit(1); + } + for (cnum = 0; cnum < max_connects; ++cnum) + { + connects[cnum].conn_state = CNST_FREE; + connects[cnum].next_free_connect = cnum + 1; + connects[cnum].hc = (httpd_conn *) 0; + } + connects[max_connects - 1].next_free_connect = -1; /* end of link list */ + first_free_connect = 0; + num_connects = 0; + httpd_conn_count = 0; + + if (hs != (httpd_server *) 0) + { + if (hs->listen4_fd != -1) + fdwatch_add_fd(hs->listen4_fd, (void *) 0, FDW_READ); + if (hs->listen6_fd != -1) + fdwatch_add_fd(hs->listen6_fd, (void *) 0, FDW_READ); + } + + /* Main loop. */ + (void) gettimeofday(&tv, (struct timezone *) 0); + while ((!terminate) || num_connects > 0) + { + /* Do we need to re-open the log file? */ + if (got_hup) + { + re_open_logfile(); + got_hup = 0; + } + + /* Do the fd watch. */ + num_ready = fdwatch(tmr_mstimeout(&tv)); + if (num_ready < 0) + { + if (errno == EINTR || errno == EAGAIN) + continue; /* try again */ + syslog(LOG_ERR, "fdwatch - %m"); + exit(1); + } + (void) gettimeofday(&tv, (struct timezone *) 0); + + if (num_ready == 0) + { + /* No fd's are ready - run the timers. */ + tmr_run(&tv); + continue; + } + + /* Is it a new connection? */ + if (hs != (httpd_server *) 0 && hs->listen6_fd != -1 && + fdwatch_check_fd(hs->listen6_fd)) + { + if (handle_newconnect(&tv, hs->listen6_fd)) + /* Go around the loop and do another fdwatch, rather than + ** dropping through and processing existing connections. + ** New connections always get priority. + */ + continue; + } + if (hs != (httpd_server *) 0 && hs->listen4_fd != -1 && + fdwatch_check_fd(hs->listen4_fd)) + { + if (handle_newconnect(&tv, hs->listen4_fd)) + /* Go around the loop and do another fdwatch, rather than + ** dropping through and processing existing connections. + ** New connections always get priority. + */ + continue; + } + + /* Find the connections that need servicing. */ + while ((c = + (connecttab *) fdwatch_get_next_client_data()) != + (connecttab *) - 1) + { + if (c == (connecttab *) 0) + continue; + hc = c->hc; + if (!fdwatch_check_fd(hc->conn_fd)) + /* Something went wrong. */ + clear_connection(c, &tv); + else + switch (c->conn_state) + { + case CNST_READING: + handle_read(c, &tv); + break; + case CNST_SENDING: + handle_send(c, &tv); + break; + case CNST_LINGERING: + handle_linger(c, &tv); + break; + } + } + tmr_run(&tv); + + if (!_debug && got_usr1 && !terminate) + { + terminate = 1; + if (hs != (httpd_server *) 0) + { + if (hs->listen4_fd != -1) + fdwatch_del_fd(hs->listen4_fd); + if (hs->listen6_fd != -1) + fdwatch_del_fd(hs->listen6_fd); + httpd_unlisten(hs); + } + } + } + + /* The main loop terminated. */ + shut_down(); + syslog(LOG_NOTICE, "exiting"); + //closelog(); + exit(0); +} + + +static void parse_args(int argc, char **argv) +{ + char *env; + int val; + + //debug = 0; + port = DEFAULT_PORT; + dir = (char *) 0; + data_dir = (char *) 0; +#ifdef ALWAYS_CHROOT + do_chroot = 1; +#else /* ALWAYS_CHROOT */ + do_chroot = 0; +#endif /* ALWAYS_CHROOT */ + no_log = 0; + no_symlink_check = do_chroot; +#ifdef ALWAYS_VHOST + do_vhost = 1; +#else /* ALWAYS_VHOST */ + do_vhost = 0; +#endif /* ALWAYS_VHOST */ +#ifdef ALWAYS_GLOBAL_PASSWD + do_global_passwd = 1; +#else /* ALWAYS_GLOBAL_PASSWD */ + do_global_passwd = 0; +#endif /* ALWAYS_GLOBAL_PASSWD */ +#ifdef CGI_PATTERN + cgi_pattern = CGI_PATTERN; +#else /* CGI_PATTERN */ + cgi_pattern = (char *) 0; +#endif /* CGI_PATTERN */ +#ifdef CGI_LIMIT + cgi_limit = CGI_LIMIT; +#else /* CGI_LIMIT */ + cgi_limit = 0; +#endif /* CGI_LIMIT */ +#ifdef CGI_TIMELIMIT + cgi_timelimit = CGI_TIMELIMIT; +#else /* CGI_LIMIT */ + cgi_timelimit = 0; +#endif /* CGI_LIMIT */ + url_pattern = (char *) 0; + no_empty_referers = 0; + local_pattern = (char *) 0; + throttlefile = (char *) 0; + hostname = (char *) 0; + logfile = (char *) 0; + pidfile = (char *) 0; + user = DEFAULT_USER; + charset = DEFAULT_CHARSET; + p3p = ""; + max_age = -1; + + env = getenv("GB_HTTPD_PORT"); + if (env && *env) + { + port = (unsigned short) atoi(env); + if (port == 0) + port = 80; + } + + env = getenv("GB_HTTPD_TIMEOUT"); + if (env && *env) + { + val = atoi(env); + if (val == 0) + { + if (env[0] == '0' && env[1] == 0) + cgi_timelimit = 0; + } + else + cgi_timelimit = val; + } + +#if 0 + argn = 1; + while (argn < argc && argv[argn][0] == '-') + { + if (strcmp(argv[argn], "-V") == 0) + { + (void) printf("%s\n", SERVER_SOFTWARE); + exit(0); + } + else if (strcmp(argv[argn], "-C") == 0 && argn + 1 < argc) + { + ++argn; + read_config(argv[argn]); + } + else if (strcmp(argv[argn], "-p") == 0 && argn + 1 < argc) + { + ++argn; + port = (unsigned short) atoi(argv[argn]); + } + else if (strcmp(argv[argn], "-d") == 0 && argn + 1 < argc) + { + ++argn; + dir = argv[argn]; + } + else if (strcmp(argv[argn], "-r") == 0) + { + do_chroot = 1; + no_symlink_check = 1; + } + else if (strcmp(argv[argn], "-nor") == 0) + { + do_chroot = 0; + no_symlink_check = 0; + } + else if (strcmp(argv[argn], "-dd") == 0 && argn + 1 < argc) + { + ++argn; + data_dir = argv[argn]; + } + else if (strcmp(argv[argn], "-s") == 0) + no_symlink_check = 0; + else if (strcmp(argv[argn], "-nos") == 0) + no_symlink_check = 1; + else if (strcmp(argv[argn], "-u") == 0 && argn + 1 < argc) + { + ++argn; + user = argv[argn]; + } + else if (strcmp(argv[argn], "-c") == 0 && argn + 1 < argc) + { + ++argn; + cgi_pattern = argv[argn]; + } + else if (strcmp(argv[argn], "-t") == 0 && argn + 1 < argc) + { + ++argn; + throttlefile = argv[argn]; + } + else if (strcmp(argv[argn], "-h") == 0 && argn + 1 < argc) + { + ++argn; + hostname = argv[argn]; + } + else if (strcmp(argv[argn], "-l") == 0 && argn + 1 < argc) + { + ++argn; + logfile = argv[argn]; + } + else if (strcmp(argv[argn], "-v") == 0) + do_vhost = 1; + else if (strcmp(argv[argn], "-nov") == 0) + do_vhost = 0; + else if (strcmp(argv[argn], "-g") == 0) + do_global_passwd = 1; + else if (strcmp(argv[argn], "-nog") == 0) + do_global_passwd = 0; + else if (strcmp(argv[argn], "-i") == 0 && argn + 1 < argc) + { + ++argn; + pidfile = argv[argn]; + } + else if (strcmp(argv[argn], "-T") == 0 && argn + 1 < argc) + { + ++argn; + charset = argv[argn]; + } + else if (strcmp(argv[argn], "-P") == 0 && argn + 1 < argc) + { + ++argn; + p3p = argv[argn]; + } + else if (strcmp(argv[argn], "-M") == 0 && argn + 1 < argc) + { + ++argn; + max_age = atoi(argv[argn]); + } + else if (strcmp(argv[argn], "-D") == 0) + debug = 1; + else + usage(); + ++argn; + } + if (argn != argc) + usage(); +#endif +} + +#if 0 +static void usage(void) +{ + (void) fprintf(stderr, + "usage: %s [-C configfile] [-p port] [-d dir] [-r|-nor] [-dd data_dir] [-s|-nos] [-v|-nov] [-g|-nog] [-u user] [-c cgipat] [-t throttles] [-h host] [-l logfile] [-i pidfile] [-T charset] [-P P3P] [-M maxage] [-V] [-D]\n", + argv0); + exit(1); +} +#endif + +#if 0 +static void read_config(char *filename) +{ + FILE *fp; + char line[10000]; + char *cp; + char *cp2; + char *name; + char *value; + + fp = fopen(filename, "r"); + if (fp == (FILE *) 0) + { + perror(filename); + exit(1); + } + + while (fgets(line, sizeof(line), fp) != (char *) 0) + { + /* Trim comments. */ + if ((cp = strchr(line, '#')) != (char *) 0) + *cp = '\0'; + + /* Skip leading whitespace. */ + cp = line; + cp += strspn(cp, " \t\n\r"); + + /* Split line into words. */ + while (*cp != '\0') + { + /* Find next whitespace. */ + cp2 = cp + strcspn(cp, " \t\n\r"); + /* Insert EOS and advance next-word pointer. */ + while (*cp2 == ' ' || *cp2 == '\t' || *cp2 == '\n' || *cp2 == '\r') + *cp2++ = '\0'; + /* Split into name and value. */ + name = cp; + value = strchr(name, '='); + if (value != (char *) 0) + *value++ = '\0'; + /* Interpret. */ + if (strcasecmp(name, "debug") == 0) + { + no_value_required(name, value); + debug = 1; + } + else if (strcasecmp(name, "port") == 0) + { + value_required(name, value); + port = (unsigned short) atoi(value); + } + else if (strcasecmp(name, "dir") == 0) + { + value_required(name, value); + dir = e_strdup(value); + } + else if (strcasecmp(name, "chroot") == 0) + { + no_value_required(name, value); + do_chroot = 1; + no_symlink_check = 1; + } + else if (strcasecmp(name, "nochroot") == 0) + { + no_value_required(name, value); + do_chroot = 0; + no_symlink_check = 0; + } + else if (strcasecmp(name, "data_dir") == 0) + { + value_required(name, value); + data_dir = e_strdup(value); + } + else if (strcasecmp(name, "symlink") == 0) + { + no_value_required(name, value); + no_symlink_check = 0; + } + else if (strcasecmp(name, "nosymlink") == 0) + { + no_value_required(name, value); + no_symlink_check = 1; + } + else if (strcasecmp(name, "symlinks") == 0) + { + no_value_required(name, value); + no_symlink_check = 0; + } + else if (strcasecmp(name, "nosymlinks") == 0) + { + no_value_required(name, value); + no_symlink_check = 1; + } + else if (strcasecmp(name, "user") == 0) + { + value_required(name, value); + user = e_strdup(value); + } + else if (strcasecmp(name, "cgipat") == 0) + { + value_required(name, value); + cgi_pattern = e_strdup(value); + } + else if (strcasecmp(name, "cgilimit") == 0) + { + value_required(name, value); + cgi_limit = atoi(value); + } + else if (strcasecmp(name, "cgitimelimit") == 0) + { + value_required(name, value); + cgi_timelimit = atoi(value); + } + else if (strcasecmp(name, "urlpat") == 0) + { + value_required(name, value); + url_pattern = e_strdup(value); + } + else if (strcasecmp(name, "noemptyreferers") == 0) + { + no_value_required(name, value); + no_empty_referers = 1; + } + else if (strcasecmp(name, "localpat") == 0) + { + value_required(name, value); + local_pattern = e_strdup(value); + } + else if (strcasecmp(name, "throttles") == 0) + { + value_required(name, value); + throttlefile = e_strdup(value); + } + else if (strcasecmp(name, "host") == 0) + { + value_required(name, value); + hostname = e_strdup(value); + } + else if (strcasecmp(name, "logfile") == 0) + { + value_required(name, value); + logfile = e_strdup(value); + } + else if (strcasecmp(name, "vhost") == 0) + { + no_value_required(name, value); + do_vhost = 1; + } + else if (strcasecmp(name, "novhost") == 0) + { + no_value_required(name, value); + do_vhost = 0; + } + else if (strcasecmp(name, "globalpasswd") == 0) + { + no_value_required(name, value); + do_global_passwd = 1; + } + else if (strcasecmp(name, "noglobalpasswd") == 0) + { + no_value_required(name, value); + do_global_passwd = 0; + } + else if (strcasecmp(name, "pidfile") == 0) + { + value_required(name, value); + pidfile = e_strdup(value); + } + else if (strcasecmp(name, "charset") == 0) + { + value_required(name, value); + charset = e_strdup(value); + } + else if (strcasecmp(name, "p3p") == 0) + { + value_required(name, value); + p3p = e_strdup(value); + } + else if (strcasecmp(name, "max_age") == 0) + { + value_required(name, value); + max_age = atoi(value); + } + else + { + (void) fprintf(stderr, "%s: unknown config option '%s'\n", + argv0, name); + exit(1); + } + + /* Advance to next word. */ + cp = cp2; + cp += strspn(cp, " \t\n\r"); + } + } + + (void) fclose(fp); +} + +static void value_required(char *name, char *value) +{ + if (value == (char *) 0) + { + (void) fprintf(stderr, "%s: value required for %s option\n", argv0, name); + exit(1); + } +} + + +static void no_value_required(char *name, char *value) +{ + if (value != (char *) 0) + { + (void) fprintf(stderr, "%s: no value required for %s option\n", + argv0, name); + exit(1); + } +} +#endif + + +static char *e_strdup(char *oldstr) +{ + char *newstr; + + newstr = strdup(oldstr); + if (newstr == (char *) 0) + { + syslog(LOG_CRIT, "out of memory copying a string"); + (void) fprintf(stderr, "%s: out of memory copying a string\n", argv0); + exit(1); + } + return newstr; +} + + +static void +lookup_hostname(httpd_sockaddr * sa4P, size_t sa4_len, int *gotv4P, + httpd_sockaddr * sa6P, size_t sa6_len, int *gotv6P) +{ +#ifdef USE_IPV6 + + struct addrinfo hints; + char portstr[10]; + int gaierr; + struct addrinfo *ai; + struct addrinfo *ai2; + struct addrinfo *aiv6; + struct addrinfo *aiv4; + + (void) memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_flags = AI_PASSIVE; + hints.ai_socktype = SOCK_STREAM; + (void) snprintf(portstr, sizeof(portstr), "%d", (int) port); + if ((gaierr = getaddrinfo(hostname, portstr, &hints, &ai)) != 0) + { + syslog(LOG_CRIT, "getaddrinfo %.80s - %.80s", + hostname, gai_strerror(gaierr)); + (void) fprintf(stderr, "%s: getaddrinfo %s - %s\n", + argv0, hostname, gai_strerror(gaierr)); + exit(1); + } + + /* Find the first IPv6 and IPv4 entries. */ + aiv6 = (struct addrinfo *) 0; + aiv4 = (struct addrinfo *) 0; + for (ai2 = ai; ai2 != (struct addrinfo *) 0; ai2 = ai2->ai_next) + { + switch (ai2->ai_family) + { + case AF_INET6: + if (aiv6 == (struct addrinfo *) 0) + aiv6 = ai2; + break; + case AF_INET: + if (aiv4 == (struct addrinfo *) 0) + aiv4 = ai2; + break; + } + } + + if (aiv6 == (struct addrinfo *) 0) + *gotv6P = 0; + else + { + if (sa6_len < aiv6->ai_addrlen) + { + syslog(LOG_CRIT, "%.80s - sockaddr too small (%lu < %lu)", + hostname, (unsigned long) sa6_len, + (unsigned long) aiv6->ai_addrlen); + exit(1); + } + (void) memset(sa6P, 0, sa6_len); + (void) memmove(sa6P, aiv6->ai_addr, aiv6->ai_addrlen); + *gotv6P = 1; + } + + if (aiv4 == (struct addrinfo *) 0) + *gotv4P = 0; + else + { + if (sa4_len < aiv4->ai_addrlen) + { + syslog(LOG_CRIT, "%.80s - sockaddr too small (%lu < %lu)", + hostname, (unsigned long) sa4_len, + (unsigned long) aiv4->ai_addrlen); + exit(1); + } + (void) memset(sa4P, 0, sa4_len); + (void) memmove(sa4P, aiv4->ai_addr, aiv4->ai_addrlen); + *gotv4P = 1; + } + + freeaddrinfo(ai); + +#else /* USE_IPV6 */ + + struct hostent *he; + + *gotv6P = 0; + + (void) memset(sa4P, 0, sa4_len); + sa4P->sa.sa_family = AF_INET; + if (hostname == (char *) 0) + sa4P->sa_in.sin_addr.s_addr = htonl(INADDR_ANY); + else + { + sa4P->sa_in.sin_addr.s_addr = inet_addr(hostname); + if ((int) sa4P->sa_in.sin_addr.s_addr == -1) + { + he = gethostbyname(hostname); + if (he == (struct hostent *) 0) + { +#ifdef HAVE_HSTRERROR + syslog(LOG_CRIT, "gethostbyname %.80s - %.80s", + hostname, hstrerror(h_errno)); + (void) fprintf(stderr, "%s: gethostbyname %s - %s\n", + argv0, hostname, hstrerror(h_errno)); +#else /* HAVE_HSTRERROR */ + syslog(LOG_CRIT, "gethostbyname %.80s failed", hostname); + (void) fprintf(stderr, "%s: gethostbyname %s failed\n", argv0, + hostname); +#endif /* HAVE_HSTRERROR */ + exit(1); + } + if (he->h_addrtype != AF_INET) + { + syslog(LOG_CRIT, "%.80s - non-IP network address", hostname); + (void) fprintf(stderr, "%s: %s - non-IP network address\n", + argv0, hostname); + exit(1); + } + (void) memmove(&sa4P->sa_in.sin_addr.s_addr, he->h_addr, he->h_length); + } + } + sa4P->sa_in.sin_port = htons(port); + *gotv4P = 1; + +#endif /* USE_IPV6 */ +} + + +static void read_throttlefile(char *throttlefile) +{ + FILE *fp; + char buf[5000]; + char *cp; + int len; + char pattern[5000]; + long max_limit, min_limit; + struct timeval tv; + + fp = fopen(throttlefile, "r"); + if (fp == (FILE *) 0) + { + syslog(LOG_CRIT, "%.80s - %m", throttlefile); + perror(throttlefile); + exit(1); + } + + (void) gettimeofday(&tv, (struct timezone *) 0); + + while (fgets(buf, sizeof(buf), fp) != (char *) 0) + { + /* Nuke comments. */ + cp = strchr(buf, '#'); + if (cp != (char *) 0) + *cp = '\0'; + + /* Nuke trailing whitespace. */ + len = strlen(buf); + while (len > 0 && + (buf[len - 1] == ' ' || buf[len - 1] == '\t' || + buf[len - 1] == '\n' || buf[len - 1] == '\r')) + buf[--len] = '\0'; + + /* Ignore empty lines. */ + if (len == 0) + continue; + + /* Parse line. */ + if (sscanf + (buf, " %4900[^ \t] %ld-%ld", pattern, &min_limit, &max_limit) == 3) + { + } + else if (sscanf(buf, " %4900[^ \t] %ld", pattern, &max_limit) == 2) + min_limit = 0; + else + { + syslog(LOG_CRIT, "unparsable line in %.80s - %.80s", throttlefile, buf); + (void) fprintf(stderr, + "%s: unparsable line in %.80s - %.80s\n", + argv0, throttlefile, buf); + continue; + } + + /* Nuke any leading slashes in pattern. */ + if (pattern[0] == '/') + (void) strcpy(pattern, &pattern[1]); + while ((cp = strstr(pattern, "|/")) != (char *) 0) + (void) strcpy(cp + 1, cp + 2); + + /* Check for room in throttles. */ + if (numthrottles >= maxthrottles) + { + if (maxthrottles == 0) + { + maxthrottles = 100; /* arbitrary */ + throttles = NEW(throttletab, maxthrottles); + } + else + { + maxthrottles *= 2; + throttles = RENEW(throttles, throttletab, maxthrottles); + } + if (throttles == (throttletab *) 0) + { + syslog(LOG_CRIT, "out of memory allocating a throttletab"); + (void) fprintf(stderr, + "%s: out of memory allocating a throttletab\n", argv0); + exit(1); + } + } + + /* Add to table. */ + throttles[numthrottles].pattern = e_strdup(pattern); + throttles[numthrottles].max_limit = max_limit; + throttles[numthrottles].min_limit = min_limit; + throttles[numthrottles].rate = 0; + throttles[numthrottles].bytes_since_avg = 0; + throttles[numthrottles].num_sending = 0; + + ++numthrottles; + } + (void) fclose(fp); +} + + +static void shut_down(void) +{ + int cnum; + struct timeval tv; + + (void) gettimeofday(&tv, (struct timezone *) 0); + logstats(&tv); + for (cnum = 0; cnum < max_connects; ++cnum) + { + if (connects[cnum].conn_state != CNST_FREE) + httpd_close_conn(connects[cnum].hc, &tv); + if (connects[cnum].hc != (httpd_conn *) 0) + { + httpd_destroy_conn(connects[cnum].hc); + free((void *) connects[cnum].hc); + --httpd_conn_count; + connects[cnum].hc = (httpd_conn *) 0; + } + } + if (hs != (httpd_server *) 0) + { + httpd_server *ths = hs; + hs = (httpd_server *) 0; + if (ths->listen4_fd != -1) + fdwatch_del_fd(ths->listen4_fd); + if (ths->listen6_fd != -1) + fdwatch_del_fd(ths->listen6_fd); + httpd_terminate(ths); + } + //mmc_destroy(); + tmr_destroy(); + free((void *) connects); + if (throttles != (throttletab *) 0) + free((void *) throttles); +} + + +static int handle_newconnect(struct timeval *tvP, int listen_fd) +{ + connecttab *c; + //ClientData client_data; + + /* This loops until the accept() fails, trying to start new + ** connections as fast as possible so we don't overrun the + ** listen queue. + */ + for (;;) + { + /* Is there room in the connection table? */ + if (num_connects >= max_connects) + { + /* Out of connection slots. Run the timers, then the + ** existing connections, and maybe we'll free up a slot + ** by the time we get back here. + */ + syslog(LOG_WARNING, "too many connections!"); + tmr_run(tvP); + return 0; + } + /* Get the first free connection entry off the free list. */ + if (first_free_connect == -1 + || connects[first_free_connect].conn_state != CNST_FREE) + { + syslog(LOG_CRIT, "the connects free list is messed up"); + exit(1); + } + c = &connects[first_free_connect]; + /* Make the httpd_conn if necessary. */ + if (c->hc == (httpd_conn *) 0) + { + c->hc = NEW(httpd_conn, 1); + if (c->hc == (httpd_conn *) 0) + { + syslog(LOG_CRIT, "out of memory allocating an httpd_conn"); + exit(1); + } + c->hc->initialized = 0; + ++httpd_conn_count; + } + + /* Get the connection. */ + switch (httpd_get_conn(hs, listen_fd, c->hc)) + { + /* Some error happened. Run the timers, then the + ** existing connections. Maybe the error will clear. + */ + case GC_FAIL: + tmr_run(tvP); + return 0; + + /* No more connections to accept for now. */ + case GC_NO_MORE: + return 1; + } + c->conn_state = CNST_READING; + /* Pop it off the free list. */ + first_free_connect = c->next_free_connect; + c->next_free_connect = -1; + ++num_connects; + //client_data.p = c; + c->active_at = tvP->tv_sec; + c->wakeup_timer = (Timer *) 0; + c->linger_timer = (Timer *) 0; + c->next_byte_index = 0; + c->numtnums = 0; + + /* Set the connection file descriptor to no-delay mode. */ + httpd_set_ndelay(c->hc->conn_fd); + + fdwatch_add_fd(c->hc->conn_fd, c, FDW_READ); + + ++stats_connections; + if (num_connects > stats_simultaneous) + stats_simultaneous = num_connects; + } +} + +static void check_paused(ClientData client_data, struct timeval *tvP) +{ + connecttab *c = client_data.p; + int result; + + result = httpd_check_paused(c->hc); + + if (result == 1) + { + if (tmr_create((struct timeval *)0, check_paused, client_data, 100, 0) == NULL) + { + syslog(LOG_CRIT, "tmr_create(check_paused) failed"); + exit(1); + } + } + else if (result == 0) + { + c->conn_state = CNST_READING; + } + else + finish_connection(c, tvP); +} + +static void handle_read(connecttab * c, struct timeval *tvP) +{ + int sz; + int action; + httpd_conn *hc = c->hc; + ClientData client_data; + + /* Is there room in our buffer to read more bytes? */ + if (hc->read_idx >= hc->read_size) + { + if (hc->read_size > 5000) + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + finish_connection(c, tvP); + return; + } + httpd_realloc_str(&hc->read_buf, &hc->read_size, hc->read_size + 1000); + } + + /* Read some more bytes. */ + sz = read(hc->conn_fd, &(hc->read_buf[hc->read_idx]), + hc->read_size - hc->read_idx); + if (sz == 0) + { + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + finish_connection(c, tvP); + return; + } + if (sz < 0) + { + /* Ignore EINTR and EAGAIN. Also ignore EWOULDBLOCK. At first glance + ** you would think that connections returned by fdwatch as readable + ** should never give an EWOULDBLOCK; however, this apparently can + ** happen if a packet gets garbled. + */ + if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) + return; + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + finish_connection(c, tvP); + return; + } + hc->read_idx += sz; + c->active_at = tvP->tv_sec; + + /* Do we have a complete request yet? */ + switch (httpd_got_request(hc)) + { + case GR_NO_REQUEST: + return; + case GR_BAD_REQUEST: + httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); + finish_connection(c, tvP); + return; + } + + /* Yes. Try parsing and resolving it. */ + if (httpd_parse_request(hc) < 0) + { + finish_connection(c, tvP); + return; + } + + /* Check the throttle table */ + if (!check_throttles(c)) + { + httpd_send_err(hc, 503, httpd_err503title, "", httpd_err503form, + hc->encodedurl); + finish_connection(c, tvP); + return; + } + + /* Start the connection going. */ + action = httpd_start_request(hc, tvP); + //syslog(LOG_INFO, "[%p] action = %d", hc, action); + + if (action < 0) + { + /* Something went wrong. Close down the connection. */ + finish_connection(c, tvP); + return; + } + + if (action == 1) + { + //syslog(LOG_INFO, "[%p] pausing connection", hc); + c->conn_state = CNST_PAUSING; + client_data.p = c; + if (tmr_create((struct timeval *)0, check_paused, client_data, 100, 0) == NULL) + { + syslog(LOG_CRIT, "tmr_create(check_paused) failed"); + exit(1); + } + return; + } + + /* Fill in end_byte_index. */ + if (hc->got_range) + { + c->next_byte_index = hc->first_byte_index; + c->end_byte_index = hc->last_byte_index + 1; + } + else if (hc->bytes_to_send < 0) + c->end_byte_index = 0; + else + c->end_byte_index = hc->bytes_to_send; + + /* Check if it's already handled. */ + if (hc->file_address == (char *) 0) + { + /* No file address means someone else is handling it. */ + int tind; + for (tind = 0; tind < c->numtnums; ++tind) + throttles[c->tnums[tind]].bytes_since_avg += hc->bytes_sent; + c->next_byte_index = hc->bytes_sent; + finish_connection(c, tvP); + return; + } + if (c->next_byte_index >= c->end_byte_index) + { + /* There's nothing to send. */ + finish_connection(c, tvP); + return; + } + + /* Cool, we have a valid connection and a file to send to it. */ + c->conn_state = CNST_SENDING; + c->started_at = tvP->tv_sec; + c->wouldblock_delay = 0; + + fdwatch_del_fd(hc->conn_fd); + fdwatch_add_fd(hc->conn_fd, c, FDW_WRITE); +} + + +static void handle_send(connecttab * c, struct timeval *tvP) +{ + size_t max_bytes; + int sz, coast; + ClientData client_data; + time_t elapsed; + httpd_conn *hc = c->hc; + int tind; + + if (c->max_limit == THROTTLE_NOLIMIT) + max_bytes = 1000000000L; + else + max_bytes = c->max_limit / 4; /* send at most 1/4 seconds worth */ + + /* Do we need to write the headers first? */ + if (hc->responselen == 0) + { + /* No, just write the file. */ + sz = write(hc->conn_fd, &(hc->file_address[c->next_byte_index]), + MIN(c->end_byte_index - c->next_byte_index, max_bytes)); + } + else + { + /* Yes. We'll combine headers and file into a single writev(), + ** hoping that this generates a single packet. + */ + struct iovec iv[2]; + + iv[0].iov_base = hc->response; + iv[0].iov_len = hc->responselen; + iv[1].iov_base = &(hc->file_address[c->next_byte_index]); + iv[1].iov_len = MIN(c->end_byte_index - c->next_byte_index, max_bytes); + sz = writev(hc->conn_fd, iv, 2); + } + + if (sz < 0 && errno == EINTR) + return; + + if (sz == 0 || (sz < 0 && (errno == EWOULDBLOCK || errno == EAGAIN))) + { + /* This shouldn't happen, but some kernels, e.g. + ** SunOS 4.1.x, are broken and select() says that + ** O_NDELAY sockets are always writable even when + ** they're actually not. + ** + ** Current workaround is to block sending on this + ** socket for a brief adaptively-tuned period. + ** Fortunately we already have all the necessary + ** blocking code, for use with throttling. + */ + c->wouldblock_delay += MIN_WOULDBLOCK_DELAY; + c->conn_state = CNST_PAUSING; + fdwatch_del_fd(hc->conn_fd); + client_data.p = c; + if (c->wakeup_timer != (Timer *) 0) + syslog(LOG_ERR, "replacing non-null wakeup_timer!"); + c->wakeup_timer = + tmr_create(tvP, wakeup_connection, client_data, c->wouldblock_delay, 0); + if (c->wakeup_timer == (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(wakeup_connection) failed"); + exit(1); + } + return; + } + + if (sz < 0) + { + /* Something went wrong, close this connection. + ** + ** If it's just an EPIPE, don't bother logging, that + ** just means the client hung up on us. + ** + ** On some systems, write() occasionally gives an EINVAL. + ** Dunno why, something to do with the socket going + ** bad. Anyway, we don't log those either. + ** + ** And ECONNRESET isn't interesting either. + */ + if (errno != EPIPE && errno != EINVAL && errno != ECONNRESET) + syslog(LOG_ERR, "write - %m sending %.80s", hc->encodedurl); + clear_connection(c, tvP); + return; + } + + /* Ok, we wrote something. */ + c->active_at = tvP->tv_sec; + /* Was this a headers + file writev()? */ + if (hc->responselen > 0) + { + /* Yes; did we write only part of the headers? */ + if (sz < hc->responselen) + { + /* Yes; move the unwritten part to the front of the buffer. */ + int newlen = hc->responselen - sz; + (void) memmove(hc->response, &(hc->response[sz]), newlen); + hc->responselen = newlen; + sz = 0; + } + else + { + /* Nope, we wrote the full headers, so adjust accordingly. */ + sz -= hc->responselen; + hc->responselen = 0; + } + } + /* And update how much of the file we wrote. */ + c->next_byte_index += sz; + c->hc->bytes_sent += sz; + for (tind = 0; tind < c->numtnums; ++tind) + throttles[c->tnums[tind]].bytes_since_avg += sz; + + /* Are we done? */ + if (c->next_byte_index >= c->end_byte_index) + { + /* This connection is finished! */ + finish_connection(c, tvP); + return; + } + + /* Tune the (blockheaded) wouldblock delay. */ + if (c->wouldblock_delay > MIN_WOULDBLOCK_DELAY) + c->wouldblock_delay -= MIN_WOULDBLOCK_DELAY; + + /* If we're throttling, check if we're sending too fast. */ + if (c->max_limit != THROTTLE_NOLIMIT) + { + elapsed = tvP->tv_sec - c->started_at; + if (elapsed == 0) + elapsed = 1; /* count at least one second */ + if (c->hc->bytes_sent / elapsed > c->max_limit) + { + c->conn_state = CNST_PAUSING; + fdwatch_del_fd(hc->conn_fd); + /* How long should we wait to get back on schedule? If less + ** than a second (integer math rounding), use 1/2 second. + */ + coast = c->hc->bytes_sent / c->max_limit - elapsed; + client_data.p = c; + if (c->wakeup_timer != (Timer *) 0) + syslog(LOG_ERR, "replacing non-null wakeup_timer!"); + c->wakeup_timer = tmr_create(tvP, wakeup_connection, client_data, + coast > 0 ? (coast * 1000L) : 500L, 0); + if (c->wakeup_timer == (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(wakeup_connection) failed"); + exit(1); + } + } + } + /* (No check on min_limit here, that only controls connection startups.) */ +} + + +static void handle_linger(connecttab * c, struct timeval *tvP) +{ + char buf[4096]; + int r; + + /* In lingering-close mode we just read and ignore bytes. An error + ** or EOF ends things, otherwise we go until a timeout. + */ + r = read(c->hc->conn_fd, buf, sizeof(buf)); + if (r < 0 && (errno == EINTR || errno == EAGAIN)) + return; + if (r <= 0) + really_clear_connection(c, tvP); +} + + +static int check_throttles(connecttab * c) +{ + int tnum; + long l; + + c->numtnums = 0; + c->max_limit = c->min_limit = THROTTLE_NOLIMIT; + for (tnum = 0; tnum < numthrottles && c->numtnums < MAXTHROTTLENUMS; ++tnum) + if (match(throttles[tnum].pattern, c->hc->expnfilename)) + { + /* If we're way over the limit, don't even start. */ + if (throttles[tnum].rate > throttles[tnum].max_limit * 2) + return 0; + /* Also don't start if we're under the minimum. */ + if (throttles[tnum].rate < throttles[tnum].min_limit) + return 0; + if (throttles[tnum].num_sending < 0) + { + syslog(LOG_ERR, + "throttle sending count was negative - shouldn't happen!"); + throttles[tnum].num_sending = 0; + } + c->tnums[c->numtnums++] = tnum; + ++throttles[tnum].num_sending; + l = throttles[tnum].max_limit / throttles[tnum].num_sending; + if (c->max_limit == THROTTLE_NOLIMIT) + c->max_limit = l; + else + c->max_limit = MIN(c->max_limit, l); + l = throttles[tnum].min_limit; + if (c->min_limit == THROTTLE_NOLIMIT) + c->min_limit = l; + else + c->min_limit = MAX(c->min_limit, l); + } + return 1; +} + +static void clear_throttles(connecttab * c, struct timeval *tvP) +{ + int tind; + + for (tind = 0; tind < c->numtnums; ++tind) + --throttles[c->tnums[tind]].num_sending; +} + + +static void update_throttles(ClientData client_data, struct timeval *nowP) +{ + int tnum, tind; + int cnum; + connecttab *c; + long l; + + /* Update the average sending rate for each throttle. This is only used + ** when new connections start up. + */ + for (tnum = 0; tnum < numthrottles; ++tnum) + { + throttles[tnum].rate = + (2 * throttles[tnum].rate + + throttles[tnum].bytes_since_avg / THROTTLE_TIME) / 3; + throttles[tnum].bytes_since_avg = 0; + /* Log a warning message if necessary. */ + if (throttles[tnum].rate > throttles[tnum].max_limit + && throttles[tnum].num_sending != 0) + { + if (throttles[tnum].rate > throttles[tnum].max_limit * 2) + syslog(LOG_NOTICE, + "throttle #%d '%.80s' rate %ld greatly exceeding limit %ld; %d sending", + tnum, throttles[tnum].pattern, throttles[tnum].rate, + throttles[tnum].max_limit, throttles[tnum].num_sending); + else + syslog(LOG_INFO, + "throttle #%d '%.80s' rate %ld exceeding limit %ld; %d sending", + tnum, throttles[tnum].pattern, throttles[tnum].rate, + throttles[tnum].max_limit, throttles[tnum].num_sending); + } + if (throttles[tnum].rate < throttles[tnum].min_limit + && throttles[tnum].num_sending != 0) + { + syslog(LOG_NOTICE, + "throttle #%d '%.80s' rate %ld lower than minimum %ld; %d sending", + tnum, throttles[tnum].pattern, throttles[tnum].rate, + throttles[tnum].min_limit, throttles[tnum].num_sending); + } + } + + /* Now update the sending rate on all the currently-sending connections, + ** redistributing it evenly. + */ + for (cnum = 0; cnum < max_connects; ++cnum) + { + c = &connects[cnum]; + if (c->conn_state == CNST_SENDING || c->conn_state == CNST_PAUSING) + { + c->max_limit = THROTTLE_NOLIMIT; + for (tind = 0; tind < c->numtnums; ++tind) + { + tnum = c->tnums[tind]; + l = throttles[tnum].max_limit / throttles[tnum].num_sending; + if (c->max_limit == THROTTLE_NOLIMIT) + c->max_limit = l; + else + c->max_limit = MIN(c->max_limit, l); + } + } + } +} + + +static void finish_connection(connecttab * c, struct timeval *tvP) +{ + /* If we haven't actually sent the buffered response yet, do so now. */ + httpd_write_response(c->hc); + + /* And clear. */ + clear_connection(c, tvP); +} + + +static void clear_connection(connecttab * c, struct timeval *tvP) +{ + ClientData client_data; + + if (c->wakeup_timer != (Timer *) 0) + { + tmr_cancel(c->wakeup_timer); + c->wakeup_timer = 0; + } + + /* This is our version of Apache's lingering_close() routine, which is + ** their version of the often-broken SO_LINGER socket option. For why + ** this is necessary, see http://www.apache.org/docs/misc/fin_wait_2.html + ** What we do is delay the actual closing for a few seconds, while reading + ** any bytes that come over the connection. However, we don't want to do + ** this unless it's necessary, because it ties up a connection slot and + ** file descriptor which means our maximum connection-handling rate + ** is lower. So, elsewhere we set a flag when we detect the few + ** circumstances that make a lingering close necessary. If the flag + ** isn't set we do the real close now. + */ + if (c->conn_state == CNST_LINGERING) + { + /* If we were already lingering, shut down for real. */ + tmr_cancel(c->linger_timer); + c->linger_timer = (Timer *) 0; + c->hc->should_linger = 0; + } + if (c->hc->should_linger) + { + if (c->conn_state != CNST_PAUSING) + fdwatch_del_fd(c->hc->conn_fd); + c->conn_state = CNST_LINGERING; + shutdown(c->hc->conn_fd, SHUT_WR); + fdwatch_add_fd(c->hc->conn_fd, c, FDW_READ); + client_data.p = c; + if (c->linger_timer != (Timer *) 0) + syslog(LOG_ERR, "replacing non-null linger_timer!"); + c->linger_timer = + tmr_create(tvP, linger_clear_connection, client_data, LINGER_TIME, 0); + if (c->linger_timer == (Timer *) 0) + { + syslog(LOG_CRIT, "tmr_create(linger_clear_connection) failed"); + exit(1); + } + } + else + really_clear_connection(c, tvP); +} + + +static void really_clear_connection(connecttab * c, struct timeval *tvP) +{ + stats_bytes += c->hc->bytes_sent; + if (c->conn_state != CNST_PAUSING) + fdwatch_del_fd(c->hc->conn_fd); + httpd_close_conn(c->hc, tvP); + clear_throttles(c, tvP); + if (c->linger_timer != (Timer *) 0) + { + tmr_cancel(c->linger_timer); + c->linger_timer = 0; + } + c->conn_state = CNST_FREE; + c->next_free_connect = first_free_connect; + first_free_connect = c - connects; /* division by sizeof is implied */ + --num_connects; +} + + +static void idle(ClientData client_data, struct timeval *nowP) +{ + int cnum; + connecttab *c; + + for (cnum = 0; cnum < max_connects; ++cnum) + { + c = &connects[cnum]; + switch (c->conn_state) + { + case CNST_READING: + if (nowP->tv_sec - c->active_at >= IDLE_READ_TIMELIMIT) + { + syslog(LOG_INFO, + "%.80s connection timed out reading", + httpd_ntoa(&c->hc->client_addr)); + httpd_send_err(c->hc, 408, httpd_err408title, "", + httpd_err408form, ""); + finish_connection(c, nowP); + } + break; + case CNST_SENDING: + case CNST_PAUSING: + if (nowP->tv_sec - c->active_at >= IDLE_SEND_TIMELIMIT) + { + syslog(LOG_INFO, + "%.80s connection timed out sending", + httpd_ntoa(&c->hc->client_addr)); + clear_connection(c, nowP); + } + break; + } + } +} + + +static void wakeup_connection(ClientData client_data, struct timeval *nowP) +{ + connecttab *c; + + c = (connecttab *) client_data.p; + c->wakeup_timer = (Timer *) 0; + if (c->conn_state == CNST_PAUSING) + { + c->conn_state = CNST_SENDING; + fdwatch_add_fd(c->hc->conn_fd, c, FDW_WRITE); + } +} + +static void +linger_clear_connection(ClientData client_data, struct timeval *nowP) +{ + connecttab *c; + + c = (connecttab *) client_data.p; + c->linger_timer = (Timer *) 0; + really_clear_connection(c, nowP); +} + + +static void occasional(ClientData client_data, struct timeval *nowP) +{ + //mmc_cleanup(nowP); + tmr_cleanup(); + watchdog_flag = 1; /* let the watchdog know that we are alive */ +} + + +#ifdef STATS_TIME +static void show_stats(ClientData client_data, struct timeval *nowP) +{ + logstats(nowP); +} +#endif /* STATS_TIME */ + + +/* Generate debugging statistics syslog messages for all packages. */ +static void logstats(struct timeval *nowP) +{ + struct timeval tv; + time_t now; + long up_secs, stats_secs; + + if (nowP == (struct timeval *) 0) + { + (void) gettimeofday(&tv, (struct timezone *) 0); + nowP = &tv; + } + now = nowP->tv_sec; + up_secs = now - start_time; + stats_secs = now - stats_time; + if (stats_secs == 0) + stats_secs = 1; /* fudge */ + stats_time = now; + syslog(LOG_INFO, + "up %ld seconds, stats for %ld seconds:", up_secs, stats_secs); + + thttpd_logstats(stats_secs); + httpd_logstats(stats_secs); + //mmc_logstats(stats_secs); + fdwatch_logstats(stats_secs); + tmr_logstats(stats_secs); +} + + +/* Generate debugging statistics syslog message. */ +static void thttpd_logstats(long secs) +{ + if (secs > 0) + syslog(LOG_INFO, + " gb.httpd - %ld connections (%g/sec), %d max simultaneous, %lld bytes (%g/sec), %d httpd_conns allocated", + stats_connections, (float) stats_connections / secs, + stats_simultaneous, (int64_t) stats_bytes, + (float) stats_bytes / secs, httpd_conn_count); + stats_connections = 0; + stats_bytes = 0; + stats_simultaneous = 0; +} diff --git a/gb.httpd/src/thttpd.h b/gb.httpd/src/thttpd.h new file mode 100644 index 00000000..e85e7239 --- /dev/null +++ b/gb.httpd/src/thttpd.h @@ -0,0 +1,405 @@ +/* config.h - configuration defines for thttpd and libhttpd +** +** (c) 1995,1998,1999,2000,2001 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _THTTPD_H_ +#define _THTTPD_H_ + +#include "config.h" + +/* The following configuration settings are sorted in order of decreasing +** likelihood that you'd want to change them - most likely first, least +** likely last. +** +** In case you're not familiar with the convention, "#ifdef notdef" +** is a Berkeleyism used to indicate temporarily disabled code. +** The idea here is that you re-enable it by just moving it outside +** of the ifdef. +*/ + +/* CONFIGURE: CGI programs must match this pattern to get executed. It's +** a simple shell-style wildcard pattern, with * meaning any string not +** containing a slash, ** meaning any string at all, and ? meaning any +** single character; or multiple such patterns separated by |. The +** patterns get checked against the filename part of the incoming URL. +** +** Restricting CGI programs to a single directory lets the site administrator +** review them for security holes, and is strongly recommended. If there +** are individual users that you trust, you can enable their directories too. +** +** You can also specify a CGI pattern on the command line, with the -c flag. +** Such a pattern overrides this compiled-in default. +** +** If no CGI pattern is specified, neither here nor on the command line, +** then CGI programs cannot be run at all. If you want to disable CGI +** as a security measure that's how you do it, just don't define any +** pattern here and don't run with the -c flag. +*/ +#ifdef notdef +/* Some sample patterns. Allow programs only in one central directory: */ +#define CGI_PATTERN "/cgi-bin/*" +/* Allow programs in a central directory, or anywhere in a trusted +** user's tree: */ +#define CGI_PATTERN "/cgi-bin/*|/jef/**" +/* Allow any program ending with a .cgi: */ +#define CGI_PATTERN "**.cgi" +/* When virtual hosting, enable the central directory on every host: */ +#define CGI_PATTERN "/*/cgi-bin/*" +#endif + +/* CONFIGURE: How many seconds to allow CGI programs to run before killing +** them. This is in case someone writes a CGI program that goes into an +** infinite loop, or does a massive database lookup that would take hours, +** or whatever. If you don't want any limit, comment this out, but that's +** probably a really bad idea. +*/ +#define CGI_TIMELIMIT 600 + +/* CONFIGURE: Maximum number of simultaneous CGI programs allowed. +** If this many are already running, then attempts to run more will +** return an HTTP 503 error. If this is not defined then there's +** no limit (and you'd better have a lot of memory). This can also be +** set in the runtime config file. +*/ +#ifdef notdef +#define CGI_LIMIT 50 +#endif + +/* CONFIGURE: How many seconds to allow for reading the initial request +** on a new connection. +*/ +#define IDLE_READ_TIMELIMIT 60 + +/* CONFIGURE: How many seconds before an idle connection gets closed. +*/ +#define IDLE_SEND_TIMELIMIT 300 + +/* CONFIGURE: The syslog facility to use. Using this you can set up your +** syslog.conf so that all thttpd messages go into a separate file. Note +** that even if you use the -l command line flag to send logging to a +** file, errors still get sent via syslog. +*/ +#define LOG_FACILITY LOG_DAEMON + +/* CONFIGURE: Tilde mapping. Many URLs use ~username to indicate a +** user's home directory. thttpd provides two options for mapping +** this construct to an actual filename. +** +** 1) Map ~username to /username. This is the recommended choice. +** Each user gets a subdirectory in the main chrootable web tree, and +** the tilde construct points there. The prefix could be something +** like "users", or it could be empty. See also the makeweb program +** for letting users create their own web subdirectories. +** +** 2) Map ~username to /. The postfix would be +** the name of a subdirectory off of the user's actual home dir, something +** like "public_html". This is what Apache and other servers do. The problem +** is, you can't do this and chroot() at the same time, so it's inherently +** a security hole. This is strongly dis-recommended, but it's here because +** some people really want it. Use at your own risk. +** +** You can also leave both options undefined, and thttpd will not do +** anything special about tildes. Enabling both options is an error. +*/ +#ifdef notdef +#define TILDE_MAP_1 "users" +#define TILDE_MAP_2 "public_html" +#endif + +/* CONFIGURE: The file to use for authentication. If this is defined then +** thttpd checks for this file in the local directory before every fetch. +** If the file exists then authentication is done, otherwise the fetch +** proceeds as usual. +** +** If you undefine this then thttpd will not implement authentication +** at all and will not check for auth files, which saves a bit of CPU time. +*/ +//#define AUTH_FILE ".htpasswd" + +/* CONFIGURE: The default character set name to use with text MIME types. +** This gets substituted into the MIME types where they have a "%s". +** +** You can override this in the config file with the "charset" setting, +** or on the command like with the -T flag. +*/ +#define DEFAULT_CHARSET "utf-8" + + +/* Most people won't want to change anything below here. */ + +/* CONFIGURE: This controls the SERVER_NAME environment variable that gets +** passed to CGI programs. By default thttpd does a gethostname(), which +** gives the host's canonical name. If you want to always use some other name +** you can define it here. +** +** Alternately, if you want to run the same thttpd binary on multiple +** machines, and want to build in alternate names for some or all of +** them, you can define a list of canonical name to altername name +** mappings. thttpd seatches the list and when it finds a match on +** the canonical name, that alternate name gets used. If no match +** is found, the canonical name gets used. +** +** If both SERVER_NAME and SERVER_NAME_LIST are defined here, thttpd searches +** the list as above, and if no match is found then SERVER_NAME gets used. +** +** In any case, if thttpd is started with the -h flag, that name always +** gets used. +*/ +#ifdef notdef +#define SERVER_NAME "your.hostname.here" +#define SERVER_NAME_LIST \ + "canonical.name.here/alternate.name.here", \ + "canonical.name.two/alternate.name.two" +#endif + +/* CONFIGURE: Undefine this if you want thttpd to hide its specific version +** when returning into to browsers. Instead it'll just say "thttpd" with +** no version. +*/ +//#define SHOW_SERVER_VERSION + +/* CONFIGURE: Define this if you want to always chroot(), without having +** to give the -r command line flag. Some people like this as a security +** measure, to prevent inadvertant exposure by accidentally running without -r. +** You can still disable it at runtime with the -nor flag. +*/ +#ifdef notdef +#define ALWAYS_CHROOT +#endif + +/* CONFIGURE: Define this if you want to always do virtual hosting, without +** having to give the -v command line flag. You can still disable it at +** runtime with the -nov flag. +*/ +#ifdef notdef +#define ALWAYS_VHOST +#endif + +/* CONFIGURE: If you're using the vhost feature and you have a LOT of +** virtual hostnames (like, hundreds or thousands), you will want to +** enable this feature. It avoids a problem with most Unix filesystems, +** where if there are a whole lot of items in a directory then name lookup +** becomes very slow. This feature makes thttpd use subdirectories +** based on the first characters of each hostname. You can set it to use +** from one to three characters. If the hostname starts with "www.", that +** part is skipped over. Dots are also skipped over, and if the name isn't +** long enough then "_"s are used. Here are some examples of how hostnames +** would get turned into directory paths, for each different setting: +** 1: www.acme.com -> a/www.acme.com +** 1: foobar.acme.com -> f/foobar.acme.com +** 2: www.acme.com -> a/c/www.acme.com +** 2: foobar.acme.com -> f/o/foobar.acme.com +** 3: www.acme.com -> a/c/m/www.acme.com +** 3: foobar.acme.com -> f/o/o/foobar.acme.com +** 3: m.tv -> m/t/v/m.tv +** 4: m.tv -> m/t/v/_/m.tv +** Note that if you compile this setting in but then forget to set up +** the corresponding subdirectories, the only error indication you'll +** get is a "404 Not Found" when you try to visit a site. So be careful. +*/ +#ifdef notdef +#define VHOST_DIRLEVELS 1 +#define VHOST_DIRLEVELS 2 +#define VHOST_DIRLEVELS 3 +#endif + +/* CONFIGURE: Define this if you want to always use a global passwd file, +** without having to give the -P command line flag. You can still disable +** it at runtime with the -noP flag. +*/ +#ifdef notdef +#define ALWAYS_GLOBAL_PASSWD +#endif + +/* CONFIGURE: When started as root, the default username to switch to after +** initializing. If this user (or the one specified by the -u flag) does +** not exist, the program will refuse to run. +*/ +#define DEFAULT_USER "nobody" + +/* CONFIGURE: When started as root, the program can automatically chdir() +** to the home directory of the user specified by -u or DEFAULT_USER. +** An explicit -d still overrides this. +*/ +#ifdef notdef +#define USE_USER_DIR +#endif + +/* CONFIGURE: If this is defined, some of the built-in error pages will +** have more explicit information about exactly what the problem is. +** Some sysadmins don't like this, for security reasons. +*/ +#define EXPLICIT_ERROR_PAGES + +/* CONFIGURE: Subdirectory for custom error pages. The error filenames are +** $WEBDIR/$ERR_DIR/err%d.html - if virtual hosting is enabled then +** $WEBDIR/hostname/$ERR_DIR/err%d.html is searched first. This allows +** different custom error pages for each virtual hosting web server. If +** no custom page for a given error can be found, the built-in error page +** is generated. If ERR_DIR is not defined at all, only the built-in error +** pages will be generated. +*/ +#define ERR_DIR "errors" + +/* CONFIGURE: Define this if you want a standard HTML tail containing +** $SERVER_SOFTWARE and $SERVER_ADDRESS to be appended to the custom error +** pages. (It is always appended to the built-in error pages.) +*/ +#define ERR_APPEND_SERVER_INFO + +/* CONFIGURE: nice(2) value to use for CGI programs. If this is undefined, +** CGI programs run at normal priority. +*/ +//#define CGI_NICE 10 + +/* CONFIGURE: $PATH to use for CGI programs. +*/ +//#define CGI_PATH "/usr/local/bin:/usr/ucb:/bin:/usr/bin" + +/* CONFIGURE: If defined, $LD_LIBRARY_PATH to use for CGI programs. +*/ +#ifdef notdef +#define CGI_LD_LIBRARY_PATH "/usr/local/lib:/usr/lib" +#endif + +/* CONFIGURE: How often to run the occasional cleanup job. +*/ +#define OCCASIONAL_TIME 120 + +/* CONFIGURE: Seconds between stats syslogs. If this is undefined then +** no stats are accumulated and no stats syslogs are done. +*/ +//#define STATS_TIME 3600 + +/* CONFIGURE: The mmap cache tries to keep the total number of mapped +** files below this number, so you don't run out of kernel file descriptors. +** If you have reconfigured your kernel to have more descriptors, you can +** raise this and thttpd will keep more maps cached. However it's not +** a hard limit, thttpd will go over it if you really are accessing +** a whole lot of files. +*/ +#define DESIRED_MAX_MAPPED_FILES 1000 + +/* CONFIGURE: The mmap cache also tries to keep the total mapped bytes +** below this number, so you don't run out of address space. Again +** it's not a hard limit, thttpd will go over it if you really are +** accessing a bunch of large files. +*/ +#define DESIRED_MAX_MAPPED_BYTES 1000000000 + +/* CONFIGURE: Minimum and maximum intervals between child-process reaping, +** in seconds. +*/ +#define MIN_REAP_TIME 30 +#define MAX_REAP_TIME 900 + + +/* You almost certainly don't want to change anything below here. */ + +/* CONFIGURE: When throttling CGI programs, we don't know how many bytes +** they send back to the client because it would be inefficient to +** interpose a counter. CGI programs are much more expensive than +** regular files to serve, so we set an arbitrary and high byte count +** that gets applied to all CGI programs for throttling purposes. +*/ +#define CGI_BYTECOUNT 25000 + +/* CONFIGURE: The default port to listen on. 80 is the standard HTTP port. +*/ +#define DEFAULT_PORT 80 + +/* CONFIGURE: A list of index filenames to check. The files are searched +** for in this order. +*/ +#define INDEX_NAMES "index.html", "index.htm", "index.xhtml", "index.xht", "Default.htm", "index.cgi" + +/* CONFIGURE: If this is defined then thttpd will automatically generate +** index pages for directories that don't have an explicit index file. +** If you want to disable this behavior site-wide, perhaps for security +** reasons, just undefine this. Note that you can disable indexing of +** individual directories by merely doing a "chmod 711" on them - the +** standard Unix file permission to allow file access but disable "ls". +*/ +//#define GENERATE_INDEXES + +/* CONFIGURE: Whether to log unknown request headers. Most sites will not +** want to log them, which will save them a bit of CPU time. +*/ +#ifdef notdef +#define LOG_UNKNOWN_HEADERS +#endif + +/* CONFIGURE: Whether to fflush() the log file after each request. If +** this is turned off there's a slight savings in CPU cycles. +*/ +//#define FLUSH_LOG_EVERY_TIME + +/* CONFIGURE: Time between updates of the throttle table's rolling averages. */ +#define THROTTLE_TIME 2 + +/* CONFIGURE: The listen() backlog queue length. The 1024 doesn't actually +** get used, the kernel uses its maximum allowed value. This is a config +** parameter only in case there's some OS where asking for too high a queue +** length causes an error. Note that on many systems the maximum length is +** way too small - see http://www.acme.com/software/thttpd/notes.html +*/ +#define LISTEN_BACKLOG 1024 + +/* CONFIGURE: Maximum number of throttle patterns that any single URL can +** be included in. This has nothing to do with the number of throttle +** patterns that you can define, which is unlimited. +*/ +#define MAXTHROTTLENUMS 10 + +/* CONFIGURE: Number of file descriptors to reserve for uses other than +** connections. Currently this is 10, representing one for the listen fd, +** one for dup()ing at connection startup time, one for reading the file, +** one for syslog, and possibly one for the regular log file, which is +** five, plus a factor of two for who knows what. +*/ +#define SPARE_FDS 10 + +/* CONFIGURE: How many milliseconds to leave a connection open while doing a +** lingering close. +*/ +#define LINGER_TIME 500 + +/* CONFIGURE: Maximum number of symbolic links to follow before +** assuming there's a loop. +*/ +#define MAX_LINKS 32 + +/* CONFIGURE: You don't even want to know. +*/ +#define MIN_WOULDBLOCK_DELAY 100L + +/* CONFIGURE: Pass the X-Cgi header to the CGI script +*/ +#define X_CGI_HEADER + +#define PUBLIC_PREFIX ".public/" + +#endif /* _THTTPD_H_ */ diff --git a/gb.httpd/src/timers.c b/gb.httpd/src/timers.c new file mode 100644 index 00000000..e84a2fba --- /dev/null +++ b/gb.httpd/src/timers.c @@ -0,0 +1,334 @@ +/* timers.c - simple timer routines +** +** (c) 1995,1998,2000 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#include + +#include +#include +//#include + +#include "main.h" +#include "timers.h" + + +#define HASH_SIZE 67 +static Timer *timers[HASH_SIZE]; +static Timer *free_timers; +static int alloc_count, active_count, free_count; + +ClientData JunkClientData; + + + +static unsigned int hash(Timer * t) +{ + /* We can hash on the trigger time, even though it can change over + ** the life of a timer via either the periodic bit or the tmr_reset() + ** call. This is because both of those guys call l_resort(), which + ** recomputes the hash and moves the timer to the appropriate list. + */ + return ((unsigned int) t->time.tv_sec ^ + (unsigned int) t->time.tv_usec) % HASH_SIZE; +} + + +static void l_add(Timer * t) +{ + int h = t->hash; + register Timer *t2; + register Timer *t2prev; + + t2 = timers[h]; + if (t2 == (Timer *) 0) + { + /* The list is empty. */ + timers[h] = t; + t->prev = t->next = (Timer *) 0; + } + else + { + if (t->time.tv_sec < t2->time.tv_sec || + (t->time.tv_sec == t2->time.tv_sec && + t->time.tv_usec <= t2->time.tv_usec)) + { + /* The new timer goes at the head of the list. */ + timers[h] = t; + t->prev = (Timer *) 0; + t->next = t2; + t2->prev = t; + } + else + { + /* Walk the list to find the insertion point. */ + for (t2prev = t2, t2 = t2->next; t2 != (Timer *) 0; + t2prev = t2, t2 = t2->next) + { + if (t->time.tv_sec < t2->time.tv_sec || + (t->time.tv_sec == t2->time.tv_sec && + t->time.tv_usec <= t2->time.tv_usec)) + { + /* Found it. */ + t2prev->next = t; + t->prev = t2prev; + t->next = t2; + t2->prev = t; + return; + } + } + /* Oops, got to the end of the list. Add to tail. */ + t2prev->next = t; + t->prev = t2prev; + t->next = (Timer *) 0; + } + } +} + + +static void l_remove(Timer * t) +{ + int h = t->hash; + + if (t->prev == (Timer *) 0) + timers[h] = t->next; + else + t->prev->next = t->next; + if (t->next != (Timer *) 0) + t->next->prev = t->prev; +} + + +static void l_resort(Timer * t) +{ + /* Remove the timer from its old list. */ + l_remove(t); + /* Recompute the hash. */ + t->hash = hash(t); + /* And add it back in to its new list, sorted correctly. */ + l_add(t); +} + + +void tmr_init(void) +{ + int h; + + for (h = 0; h < HASH_SIZE; ++h) + timers[h] = (Timer *) 0; + free_timers = (Timer *) 0; + alloc_count = active_count = free_count = 0; +} + + +Timer *tmr_create(struct timeval *nowP, TimerProc * timer_proc, + ClientData client_data, long msecs, int periodic) +{ + Timer *t; + + if (free_timers != (Timer *) 0) + { + t = free_timers; + free_timers = t->next; + --free_count; + } + else + { + t = (Timer *) malloc(sizeof(Timer)); + if (t == (Timer *) 0) + return (Timer *) 0; + ++alloc_count; + } + + t->timer_proc = timer_proc; + t->client_data = client_data; + t->msecs = msecs; + t->periodic = periodic; + if (nowP != (struct timeval *) 0) + t->time = *nowP; + else + (void) gettimeofday(&t->time, (struct timezone *) 0); + t->time.tv_sec += msecs / 1000L; + t->time.tv_usec += (msecs % 1000L) * 1000L; + if (t->time.tv_usec >= 1000000L) + { + t->time.tv_sec += t->time.tv_usec / 1000000L; + t->time.tv_usec %= 1000000L; + } + t->hash = hash(t); + /* Add the new timer to the proper active list. */ + l_add(t); + ++active_count; + + return t; +} + + +struct timeval *tmr_timeout(struct timeval *nowP) +{ + long msecs; + static struct timeval timeout; + + msecs = tmr_mstimeout(nowP); + if (msecs == INFTIM) + return (struct timeval *) 0; + timeout.tv_sec = msecs / 1000L; + timeout.tv_usec = (msecs % 1000L) * 1000L; + return &timeout; +} + + +long tmr_mstimeout(struct timeval *nowP) +{ + int h; + int gotone; + long msecs, m; + register Timer *t; + + gotone = 0; + msecs = 0; /* make lint happy */ + /* Since the lists are sorted, we only need to look at the + ** first timer on each one. + */ + for (h = 0; h < HASH_SIZE; ++h) + { + t = timers[h]; + if (t != (Timer *) 0) + { + m = (t->time.tv_sec - nowP->tv_sec) * 1000L + + (t->time.tv_usec - nowP->tv_usec) / 1000L; + if (!gotone) + { + msecs = m; + gotone = 1; + } + else if (m < msecs) + msecs = m; + } + } + if (!gotone) + return INFTIM; + if (msecs <= 0) + msecs = 0; + return msecs; +} + + +void tmr_run(struct timeval *nowP) +{ + int h; + Timer *t; + Timer *next; + + for (h = 0; h < HASH_SIZE; ++h) + for (t = timers[h]; t != (Timer *) 0; t = next) + { + next = t->next; + /* Since the lists are sorted, as soon as we find a timer + ** that isn't ready yet, we can go on to the next list. + */ + if (t->time.tv_sec > nowP->tv_sec || + (t->time.tv_sec == nowP->tv_sec && t->time.tv_usec > nowP->tv_usec)) + break; + (t->timer_proc) (t->client_data, nowP); + if (t->periodic) + { + /* Reschedule. */ + t->time.tv_sec += t->msecs / 1000L; + t->time.tv_usec += (t->msecs % 1000L) * 1000L; + if (t->time.tv_usec >= 1000000L) + { + t->time.tv_sec += t->time.tv_usec / 1000000L; + t->time.tv_usec %= 1000000L; + } + l_resort(t); + } + else + tmr_cancel(t); + } +} + + +void tmr_reset(struct timeval *nowP, Timer * t) +{ + t->time = *nowP; + t->time.tv_sec += t->msecs / 1000L; + t->time.tv_usec += (t->msecs % 1000L) * 1000L; + if (t->time.tv_usec >= 1000000L) + { + t->time.tv_sec += t->time.tv_usec / 1000000L; + t->time.tv_usec %= 1000000L; + } + l_resort(t); +} + + +void tmr_cancel(Timer * t) +{ + /* Remove it from its active list. */ + l_remove(t); + --active_count; + /* And put it on the free list. */ + t->next = free_timers; + free_timers = t; + ++free_count; + t->prev = (Timer *) 0; +} + + +void tmr_cleanup(void) +{ + Timer *t; + + while (free_timers != (Timer *) 0) + { + t = free_timers; + free_timers = t->next; + --free_count; + free((void *) t); + --alloc_count; + } +} + + +void tmr_destroy(void) +{ + int h; + + for (h = 0; h < HASH_SIZE; ++h) + while (timers[h] != (Timer *) 0) + tmr_cancel(timers[h]); + tmr_cleanup(); +} + + +/* Generate debugging statistics syslog message. */ +void tmr_logstats(long secs) +{ + syslog(LOG_INFO, " timers - %d allocated, %d active, %d free", + alloc_count, active_count, free_count); + if (active_count + free_count != alloc_count) + syslog(LOG_ERR, "timer counts don't add up!"); +} diff --git a/gb.httpd/src/timers.h b/gb.httpd/src/timers.h new file mode 100644 index 00000000..abb2efb4 --- /dev/null +++ b/gb.httpd/src/timers.h @@ -0,0 +1,110 @@ +/* timers.h - header file for timers package +** +** (c) 1995,1998,1999,2000 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _TIMERS_H_ +#define _TIMERS_H_ + +#include + +#ifndef INFTIM +#define INFTIM -1 +#endif /* INFTIM */ + +/* ClientData is a random value that tags along with a timer. The client +** can use it for whatever, and it gets passed to the callback when the +** timer triggers. +*/ +typedef union +{ + void *p; + int i; + long l; +} ClientData; + +extern ClientData JunkClientData; /* for use when you don't care */ + +/* The TimerProc gets called when the timer expires. It gets passed +** the ClientData associated with the timer, and a timeval in case +** it wants to schedule another timer. +*/ +typedef void TimerProc(ClientData client_data, struct timeval *nowP); + +/* The Timer struct. */ +typedef struct TimerStruct +{ + TimerProc *timer_proc; + ClientData client_data; + long msecs; + int periodic; + struct timeval time; + struct TimerStruct *prev; + struct TimerStruct *next; + int hash; +} Timer; + +/* Initialize the timer package. */ +extern void tmr_init(void); + +/* Set up a timer, either periodic or one-shot. Returns (Timer*) 0 on errors. */ +extern Timer *tmr_create(struct timeval *nowP, TimerProc * timer_proc, + ClientData client_data, long msecs, int periodic); + +/* Returns a timeout indicating how long until the next timer triggers. You +** can just put the call to this routine right in your select(). Returns +** (struct timeval*) 0 if no timers are pending. +*/ +extern struct timeval *tmr_timeout(struct timeval *nowP); + +/* Returns a timeout in milliseconds indicating how long until the next timer +** triggers. You can just put the call to this routine right in your poll(). +** Returns INFTIM (-1) if no timers are pending. +*/ +extern long tmr_mstimeout(struct timeval *nowP); + +/* Run the list of timers. Your main program needs to call this every so often, +** or as indicated by tmr_timeout(). +*/ +extern void tmr_run(struct timeval *nowP); + +/* Reset the clock on a timer, to current time plus the original timeout. */ +extern void tmr_reset(struct timeval *nowP, Timer * timer); + +/* Deschedule a timer. Note that non-periodic timers are automatically +** descheduled when they run, so you don't have to call this on them. +*/ +extern void tmr_cancel(Timer * timer); + +/* Clean up the timers package, freeing any unused storage. */ +extern void tmr_cleanup(void); + +/* Cancel all timers and free storage, usually in preparation for exitting. */ +extern void tmr_destroy(void); + +/* Generate debugging statistics syslog message. */ +extern void tmr_logstats(long secs); + +#endif /* _TIMERS_H_ */ diff --git a/gb.httpd/src/version.h b/gb.httpd/src/version.h new file mode 100644 index 00000000..fe719326 --- /dev/null +++ b/gb.httpd/src/version.h @@ -0,0 +1,9 @@ +/* version.h - version defines for thttpd and libhttpd */ + +#ifndef _VERSION_H_ +#define _VERSION_H_ + +#define SERVER_SOFTWARE "gb.httpd " __DATE__ +#define SERVER_ADDRESS "http://gambas.sourceforge.net" + +#endif /* _VERSION_H_ */ diff --git a/gb.image.imlib/AUTHORS b/gb.image.imlib/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.image.imlib/COPYING b/gb.image.imlib/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.image.imlib/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.image.imlib/ChangeLog b/gb.image.imlib/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.image.imlib/INSTALL b/gb.image.imlib/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.image.imlib/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.image.imlib/Makefile.am b/gb.image.imlib/Makefile.am new file mode 100644 index 00000000..b8e6e696 --- /dev/null +++ b/gb.image.imlib/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @IMAGE_IMLIB_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.image.imlib/NEWS b/gb.image.imlib/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.image.imlib/README b/gb.image.imlib/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.image.imlib/acinclude.m4 b/gb.image.imlib/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.image.imlib/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.image.imlib/component.am b/gb.image.imlib/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.image.imlib/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.image.imlib/configure.ac b/gb.image.imlib/configure.ac new file mode 100644 index 00000000..8c1d0b73 --- /dev/null +++ b/gb.image.imlib/configure.ac @@ -0,0 +1,17 @@ +dnl ---- configure.ac for gb.image.imlib + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-image-imlib, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.image.imlib) +AC_PROG_LIBTOOL + +GB_COMPONENT_PKG_CONFIG( + image_imlib, IMAGE_IMLIB, gb.image.imlib, [src], + 'imlib2 >= 1.4.0' + ) + +AC_OUTPUT( Makefile src/Makefile ) + +GB_PRINT_MESSAGES diff --git a/gb.image.imlib/gambas.h b/gb.image.imlib/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.image.imlib/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.image.imlib/gb.draw.h b/gb.image.imlib/gb.draw.h new file mode 120000 index 00000000..82ba0778 --- /dev/null +++ b/gb.image.imlib/gb.draw.h @@ -0,0 +1 @@ +../main/lib/draw/gb.draw.h \ No newline at end of file diff --git a/gb.image.imlib/gb.image.h b/gb.image.imlib/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.image.imlib/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.image.imlib/gb_common.h b/gb.image.imlib/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.image.imlib/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.image.imlib/m4 b/gb.image.imlib/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.image.imlib/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.image.imlib/reconf b/gb.image.imlib/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.image.imlib/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.image.imlib/src/Makefile.am b/gb.image.imlib/src/Makefile.am new file mode 100644 index 00000000..9e9f3161 --- /dev/null +++ b/gb.image.imlib/src/Makefile.am @@ -0,0 +1,10 @@ +COMPONENT = gb.image.imlib +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.image.imlib.la + +gb_image_imlib_la_LIBADD = @IMAGE_IMLIB_LIB@ +gb_image_imlib_la_LDFLAGS = -module @LD_FLAGS@ @IMAGE_IMLIB_LDFLAGS@ +gb_image_imlib_la_CPPFLAGS = @IMAGE_IMLIB_INC@ + +gb_image_imlib_la_SOURCES = main.c main.h c_image.c c_image.h c_imlib.c c_imlib.h diff --git a/gb.image.imlib/src/c_image.c b/gb.image.imlib/src/c_image.c new file mode 100644 index 00000000..02221c5f --- /dev/null +++ b/gb.image.imlib/src/c_image.c @@ -0,0 +1,281 @@ +/*************************************************************************** + + c_image.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_IMAGE_C + +#include "gb.draw.h" +#include "c_image.h" + +static void free_image(GB_IMG *img, void *image) +{ + if (image) + { + imlib_context_set_image((Imlib_Image)image); + imlib_free_image(); + } +} + +static void *temp_image(GB_IMG *img) +{ + Imlib_Image image; + + if (!img->data) + image = NULL; + else + { + image = imlib_create_image_using_data(img->width, img->height, (DATA32 *)img->data); + } + + return image; +} + +static GB_IMG_OWNER _image_owner = { + "gb.image.imlib", + GB_IMAGE_BGRA, + free_image, + free_image, + temp_image, + NULL, + }; + +static Imlib_Image check_image(void *_object) +{ + Imlib_Image image = (Imlib_Image)IMAGE.Check(THIS, &_image_owner); + imlib_context_set_image(image); + imlib_image_set_has_alpha(TRUE); + return image; +} + +Imlib_Image IMAGE_check(void *_object) +{ + return check_image(_object); +} + +static void take_image(CIMAGE *_object, Imlib_Image image) +{ + imlib_context_set_image((Imlib_Image)image); + IMAGE.Take(THIS, &_image_owner, image, imlib_image_get_width(), imlib_image_get_height(), (void *)imlib_image_get_data()); +} + +CIMAGE *create_image(Imlib_Image image) +{ + CIMAGE *img; + + img = GB.New(GB.FindClass("Image"), NULL, NULL); + take_image(img, image); + return img; +} + +/***************************************************************************/ + +// Imlib2 cannot load images from memory + +#if 0 +static const char *get_error(Imlib_Load_Error error) +{ + switch(error) + { + case IMLIB_LOAD_ERROR_NONE: return NULL; + case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST: return "File does not exist"; + case IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY: return "File is a directory"; + case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ: return "Read permission denied"; + case IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT: return "Unknown file format"; + case IMLIB_LOAD_ERROR_PATH_TOO_LONG: return "Path too long"; + case IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT: return "Path component does not exist"; + case IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY: return "Path component is not a directory"; + case IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE: return "Path points oustide of address space"; + case IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS: return "Too many symbolic links"; + case IMLIB_LOAD_ERROR_OUT_OF_MEMORY: return "Out of memory"; + case IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS: return "Out of file descriptors"; + case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE: return "Write permission denied"; + case IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE: return "Out of disk space"; + default: return "Unknown error"; + }; +} + +BEGIN_METHOD(CIMAGE_load, GB_STRING path) + + Imlib_Image image; + Imlib_Load_Error error; + char *addr; + int len; + + if (GB.LoadFile(STRING(path), LENGTH(path), &addr, &len)) + { + GB.Error("Unable to load image"); + return; + } + + image = imlib_load_image_with_error_return(GB.ToZeroString(ARG(path)), &error); + + if (image) + GB.ReturnObject(create_image(image)); + else + GB.Error("Unable to load image: &1", get_error(error)); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_save, GB_STRING path; GB_INTEGER quality) + + Imlib_Load_Error error; + + check_image(THIS); + + imlib_context_set_image(THIS_IMAGE); + imlib_save_image_with_error_return(GB.ToZeroString(ARG(path)), &error); + + if (error != IMLIB_LOAD_ERROR_NONE) + GB.Error("Unable to save picture: &1", get_error(error)); + +END_METHOD +#endif + +BEGIN_METHOD(Image_Rotate, GB_FLOAT angle) + + Imlib_Image image; + + check_image(THIS); + imlib_context_set_anti_alias(TRUE); + image = imlib_create_rotated_image(VARG(angle)); + GB.ReturnObject(create_image(image)); + +END_METHOD + +BEGIN_METHOD(Image_Stretch, GB_INTEGER width; GB_INTEGER height) + + Imlib_Image image; + + check_image(THIS); + imlib_context_set_anti_alias(TRUE); + image = imlib_create_cropped_scaled_image(0, 0, imlib_image_get_width(), imlib_image_get_height(), VARG(width), VARG(height)); + GB.ReturnObject(create_image(image)); + +END_METHOD + +BEGIN_METHOD(Image_Blur, GB_INTEGER radius) + + Imlib_Image image; + + check_image(THIS); + + image = imlib_clone_image(); + imlib_context_set_image(image); + imlib_image_blur(VARGOPT(radius, 2)); + GB.ReturnObject(create_image(image)); + +END_METHOD + +BEGIN_METHOD_VOID(Image_Tile) + + Imlib_Image image; + + check_image(THIS); + + image = imlib_clone_image(); + imlib_context_set_image(image); + imlib_image_tile(); + GB.ReturnObject(create_image(image)); + +END_METHOD + +BEGIN_METHOD(Image_Sharpen, GB_INTEGER radius) + + Imlib_Image image; + + check_image(THIS); + + image = imlib_clone_image(); + imlib_context_set_image(image); + imlib_image_sharpen(VARGOPT(radius, 2)); + GB.ReturnObject(create_image(image)); + +END_METHOD + +BEGIN_METHOD(Image_PaintImage, GB_OBJECT img; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER sx; GB_INTEGER sy; GB_INTEGER sw; GB_INTEGER sh) + + int x, y, w, h, sx, sy, sw, sh, src_w, src_h; + CIMAGE *image = (CIMAGE *)VARG(img); + Imlib_Image src; + + if (GB.CheckObject(image)) + return; + + src = check_image(image); + src_w = imlib_image_get_width(); + src_h = imlib_image_get_height(); + + check_image(THIS); + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + w = VARGOPT(w, -1); + h = VARGOPT(h, -1); + + sx = VARGOPT(sx, 0); + sy = VARGOPT(sy, 0); + sw = VARGOPT(sw, -1); + sh = VARGOPT(sh, -1); + + DRAW_NORMALIZE(x, y, w, h, sx, sy, sw, sh, src_w, src_h); + + imlib_blend_image_onto_image(src, TRUE, sx, sy, sw, sh, x, y, w, h); + +END_METHOD + +BEGIN_METHOD(Image_Scroll, GB_INTEGER dx; GB_INTEGER dy; GB_INTEGER x; GB_INTEGER y; GB_INTEGER width; GB_INTEGER height) + + int x, y, width, height; + + check_image(THIS); + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + width = VARGOPT(width, imlib_image_get_width()); + height = VARGOPT(height, imlib_image_get_height()); + + imlib_image_scroll_rect(x, y, width, height, VARG(dx), VARG(dy)); + +END_METHOD + +GB_DESC ImageDesc[] = +{ + GB_DECLARE("Image", sizeof(CIMAGE)), + + //GB_STATIC_METHOD("Load", "Image", CIMAGE_load, "(Path)s"), + //GB_METHOD("Save", NULL, CIMAGE_save, "(Path)s[(Quality)i]"), + + GB_METHOD("Rotate", "Image", Image_Rotate, "(Angle)f"), + GB_METHOD("Stretch", "Image", Image_Stretch, "(Width)i(Height)i"), + + GB_METHOD("Blur", "Image", Image_Blur, "[(Radius)i]"), + GB_METHOD("Sharpen", "Image", Image_Sharpen, "[(Radius)i]"), + GB_METHOD("Tile", "Image", Image_Tile, NULL), + + GB_METHOD("PaintImage", NULL, Image_PaintImage, "(Image)Image;[(X)i(Y)i(Width)i(Height)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + GB_METHOD("Scroll", NULL, Image_Scroll, "(DX)i(DY)i[(X)i(Y)i(Width)i(Height)i]"), + //Gb_INTERFACE("Draw", &DRAW_Interface), + + GB_END_DECLARE +}; + diff --git a/gb.image.imlib/src/c_image.h b/gb.image.imlib/src/c_image.h new file mode 100644 index 00000000..003c5997 --- /dev/null +++ b/gb.image.imlib/src/c_image.h @@ -0,0 +1,47 @@ +/*************************************************************************** + + c_image.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_IMAGE_H +#define __C_IMAGE_H + +#include "main.h" + +typedef + struct + { + GB_IMG img; + } + CIMAGE; + +#ifndef __C_IMAGE_C +extern GB_DESC ImageDesc[]; +#else + +#define THIS ((GB_IMG *)_object) +#define THIS_IMAGE ((Imlib_Image)THIS->temp_handle) + +#endif + +Imlib_Image IMAGE_check(void *_object); + +#endif /* __CIMAGE_H */ diff --git a/gb.image.imlib/src/c_imlib.c b/gb.image.imlib/src/c_imlib.c new file mode 100644 index 00000000..401005b5 --- /dev/null +++ b/gb.image.imlib/src/c_imlib.c @@ -0,0 +1,129 @@ +/*************************************************************************** + + c_imlib.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_IMLIB_C + +#include "c_image.h" +#include "c_imlib.h" + +#if 0 +typedef + struct IMLIB_DRAW { + struct IMLIB_DRAW *previous; + CIMAGE *device; + Imlib_Image image; + } + IMLIB_DRAW; + +IMLIB_DRAW *_current = NULL; + +#define THIS _current +#define IMAGE (_current->image) + +static bool check_device() +{ + if (!_current) + { + GB.Error("No current device"); + return TRUE; + } + else + return FALSE; +} + +#define CHECK_DEVICE() \ + if (check_device()) \ + return; \ + imlib_context_set_image(THIS->image); + +/**** Cairo ****************************************************************/ + +BEGIN_METHOD(Imlib_Begin, GB_OBJECT device) + + void *device = VARG(device); + IMLIB_DRAW *draw; + + if (GB.CheckObject(device)) + return; + + GB.Alloc(POINTER(&draw), sizeof(IMLIB_DRAW)); + draw->previous = _current; + + if (GB.Is(device, GB.FindClass("Image"))) + { + draw->image = IMAGE_check(device); + } + else + { + GB.Free(POINTER(&draw)); + GB.Error("Bad device"); + return; + } + + draw->device = device; + GB.Ref(device); + + _current = draw; + +END_METHOD + +static void end_current() +{ + IMLIB_DRAW *draw = _current; + + if (!_current) + return; + + _current = draw->previous; + + GB.Unref(POINTER(&draw->device)); + GB.Free(POINTER(&draw)); +} + +BEGIN_METHOD_VOID(Imlib_End) + + if (check_device()) + return; + + end_current(); + +END_METHOD + +BEGIN_METHOD_VOID(Imlib_exit) + + while (_current) + end_current(); + +END_METHOD +#endif + +GB_DESC ImlibDesc[] = +{ + GB_DECLARE("Imlib", 0), GB_VIRTUAL_CLASS(), + + //GB_STATIC_PROPERTY("Image", "Image", Imlib_Image), + //GB_STATIC_PROPERTY("AntiAlias", "b", Imlib_AntiAlias), + + GB_END_DECLARE +}; + diff --git a/gb.image.imlib/src/c_imlib.h b/gb.image.imlib/src/c_imlib.h new file mode 100644 index 00000000..78a45272 --- /dev/null +++ b/gb.image.imlib/src/c_imlib.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + c_imlib.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_IMLIB_H +#define __C_IMLIB_H + +#include "main.h" + +#ifndef __C_IMLIB_C +extern GB_DESC ImlibDesc[]; +#endif + +#endif diff --git a/gb.image.imlib/src/gb.image.imlib.component b/gb.image.imlib/src/gb.image.imlib.component new file mode 100644 index 00000000..bb9d093c --- /dev/null +++ b/gb.image.imlib/src/gb.image.imlib.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.image.imlib +Author=Benoît Minisini +State=NotFinished +Requires=gb.image diff --git a/gb.image.imlib/src/main.c b/gb.image.imlib/src/main.c new file mode 100644 index 00000000..7967c9db --- /dev/null +++ b/gb.image.imlib/src/main.c @@ -0,0 +1,49 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" +#include "c_image.h" +#include "c_imlib.h" + +GB_INTERFACE GB EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + ImageDesc, + //ImlibDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + imlib_set_cache_size(0); + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.image.imlib/src/main.h b/gb.image.imlib/src/main.h new file mode 100644 index 00000000..39aa1b42 --- /dev/null +++ b/gb.image.imlib/src/main.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb.image.h" +#include "gb_common.h" + +#include + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.image.io/AUTHORS b/gb.image.io/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.image.io/COPYING b/gb.image.io/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.image.io/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.image.io/ChangeLog b/gb.image.io/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.image.io/INSTALL b/gb.image.io/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.image.io/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.image.io/Makefile.am b/gb.image.io/Makefile.am new file mode 100644 index 00000000..0f51dc58 --- /dev/null +++ b/gb.image.io/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @IMAGE_IO_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.image.io/NEWS b/gb.image.io/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.image.io/README b/gb.image.io/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.image.io/acinclude.m4 b/gb.image.io/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.image.io/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.image.io/component.am b/gb.image.io/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.image.io/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.image.io/configure.ac b/gb.image.io/configure.ac new file mode 100644 index 00000000..662d2c82 --- /dev/null +++ b/gb.image.io/configure.ac @@ -0,0 +1,17 @@ +dnl ---- configure.ac for gb.image.io + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-image-io, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.image.io) +AC_PROG_LIBTOOL + +GB_COMPONENT_PKG_CONFIG( + image_io, IMAGE_IO, gb.image.io, [src], + 'gdk-pixbuf-2.0 >= 2.4.13' + ) + +AC_OUTPUT( Makefile src/Makefile ) + +GB_PRINT_MESSAGES diff --git a/gb.image.io/gambas.h b/gb.image.io/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.image.io/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.image.io/gb.image.h b/gb.image.io/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.image.io/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.image.io/gb_common.h b/gb.image.io/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.image.io/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.image.io/m4 b/gb.image.io/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.image.io/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.image.io/reconf b/gb.image.io/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.image.io/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.image.io/src/Makefile.am b/gb.image.io/src/Makefile.am new file mode 100644 index 00000000..37b00bcf --- /dev/null +++ b/gb.image.io/src/Makefile.am @@ -0,0 +1,10 @@ +COMPONENT = gb.image.io +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.image.io.la + +gb_image_io_la_LIBADD = @IMAGE_IO_LIB@ +gb_image_io_la_LDFLAGS = -module @LD_FLAGS@ @IMAGE_IO_LDFLAGS@ +gb_image_io_la_CPPFLAGS = @IMAGE_IO_INC@ + +gb_image_io_la_SOURCES = main.c main.h c_image.c c_image.h diff --git a/gb.image.io/src/c_image.c b/gb.image.io/src/c_image.c new file mode 100644 index 00000000..1bb54088 --- /dev/null +++ b/gb.image.io/src/c_image.c @@ -0,0 +1,311 @@ +/*************************************************************************** + + c_image.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_IMAGE_C + +#include "c_image.h" + +#define LOAD_INC 65536L + +static void load_image(const char *addr, int len) +{ + GdkPixbuf *img = NULL; + const char *laddr; + int llen; + GdkPixbufLoader* loader = NULL; + GError *error = NULL; + int size; + int format; + GB_IMG *image; + + loader = gdk_pixbuf_loader_new(); + + laddr = addr; + llen = len; + while (llen > 0) + { + size = llen > LOAD_INC ? LOAD_INC : llen; + if (!gdk_pixbuf_loader_write(loader, (guchar*)laddr, (gsize)size, &error)) + { + fprintf(stderr, "gb.image.io: error: cannot load %d image bytes\n", size); + GB.Error(error->message); + goto __END; + } + laddr += size; + llen -= size; + } + + gdk_pixbuf_loader_close(loader, NULL); + + img = gdk_pixbuf_loader_get_pixbuf(loader); + if (!img) + { + GB.Error("Unable to load image"); + goto __END; + } + + g_object_ref(G_OBJECT(img)); + + // Rowstride breaks gb.image (it is rounded up so that a line is always a four bytes multiple). + if (gdk_pixbuf_get_n_channels(img) == 3) + { + GdkPixbuf *aimg; + aimg = gdk_pixbuf_add_alpha(img, FALSE, 0, 0, 0); + g_object_unref(G_OBJECT(img)); + img = aimg; + } + + //fprintf(stderr, "nchannels = %d size = %d x %d rowstride = %d\n", gdk_pixbuf_get_n_channels(img), gdk_pixbuf_get_width(img), gdk_pixbuf_get_height(img), gdk_pixbuf_get_rowstride(img)); + + switch (gdk_pixbuf_get_n_channels(img)) + { + case 3: format = GB_IMAGE_RGB; break; + case 4: format = GB_IMAGE_RGBA; break; + default: + g_object_unref(G_OBJECT(img)); + GB.Error("Unsupported number of channels"); + goto __END; + } + + image = IMAGE.Create(gdk_pixbuf_get_width(img), gdk_pixbuf_get_height(img), format, gdk_pixbuf_get_pixels(img)); + IMAGE.Convert(image, IMAGE.GetDefaultFormat()); + GB.ReturnObject(image); + + g_object_unref(G_OBJECT(img)); + +__END: + + if (loader) + g_object_unref(G_OBJECT(loader)); +} + + +BEGIN_METHOD(Image_Load, GB_STRING path) + + char *addr; + int len; + + if (GB.LoadFile(STRING(path), LENGTH(path), &addr, &len)) + { + GB.Error("Unable to load image"); + return; + } + + load_image(addr, len); + + GB.ReleaseFile(addr, len); + +END_METHOD + + +BEGIN_METHOD(Image_FromString, GB_STRING data) + + load_image(STRING(data), LENGTH(data)); + +END_METHOD + + +static const char *FILE_get_ext(const char *path) +{ + const char *p; + + p = rindex(path, '/'); + if (p) + path = &p[1]; + + p = rindex(path, '.'); + if (p == NULL) + return &path[strlen(path)]; + else + return p + 1; +} + +BEGIN_METHOD(Image_Save, GB_STRING path; GB_INTEGER quality) + + char *path = GB.FileName(STRING(path), LENGTH(path)); + const char *ext = NULL; + bool ok = FALSE; + int b; + char *format = NULL; + GSList *formats, *iter; + GdkPixbuf *image = NULL; + int quality = VARGOPT(quality, -1); + char arg[4]; + GError *error = NULL; + + ext = FILE_get_ext(path); + if (!ext || !*ext) + { + GB.Error("No extension specified"); + goto __END; + } + + SYNCHRONIZE_IMAGE(THIS); + IMAGE.Convert(THIS, GB_IMAGE_RGBA); + image = gdk_pixbuf_new_from_data((const guchar *)THIS->data, GDK_COLORSPACE_RGB, TRUE, 8, THIS->width, THIS->height, THIS->width * sizeof(uint), NULL, NULL); + + formats = gdk_pixbuf_get_formats(); + + iter = formats; + while (iter && (!ok) ) + { + if (gdk_pixbuf_format_is_writable((GdkPixbufFormat*)iter->data)) + { + format = gdk_pixbuf_format_get_name((GdkPixbufFormat*)iter->data); + if (!strcasecmp(format, ext)) + { + ok = TRUE; + break; + } + else + g_free(format); + } + iter = iter->next; + } + + g_slist_free(formats); + + if (!ok) + { + if (!strcasecmp("jpg", ext)) + format = (char *)"jpeg"; + } + + if (!format) + { + GB.Error("Unknown format"); + goto __END; + } + + if (quality >= 0) + { + if (strcmp(format, "jpeg") == 0) + { + if (quality > 100) + quality = 100; + sprintf(arg, "%d", quality); + b = gdk_pixbuf_save(image, path, format, &error, "quality", arg, (void *)NULL); + } + else if (strcmp(format, "png") == 0) + { + if (quality > 9) + quality = 9; + sprintf(arg, "%d", quality); + b = gdk_pixbuf_save(image, path, format, &error, "compression", arg, (void *)NULL); + } + else + b = gdk_pixbuf_save(image, path, format, &error, (void *)NULL); + } + else + b = gdk_pixbuf_save(image, path, format, &error, (void *)NULL); + + if (ok) + g_free(format); + + if (!b) + { + GB.Error(error->message); + goto __END; + } + +__END: + + if (image) + g_object_unref(G_OBJECT(image)); + +END_METHOD + + +static GdkPixbuf *stretch_pixbuf(GdkPixbuf *img, int w, int h) +{ + GdkPixbuf *image = NULL; + int wimg, himg; + int ws, hs; + + if (w <= 0 && h <= 0) + goto __RETURN; + + wimg = gdk_pixbuf_get_width(img); + himg = gdk_pixbuf_get_height(img); + + if (w < 0) + w = wimg * h / himg; + else if (h < 0) + h = himg * w / wimg; + + if (w <= 0 || h <= 0) + goto __RETURN; + + ws = w; + hs = h; + if (ws < (wimg / 4)) + ws = w * 4; + if (hs < (himg / 4)) + hs = h * 4; + if (ws != w || hs != h) + { + image = gdk_pixbuf_scale_simple(img, ws, hs, GDK_INTERP_NEAREST); + g_object_unref(G_OBJECT(img)); + img = image; + } + + image = gdk_pixbuf_scale_simple(img, w, h, GDK_INTERP_BILINEAR); + +__RETURN: + + g_object_unref(G_OBJECT(img)); + return image; +} + +BEGIN_METHOD(Image_Stretch, GB_INTEGER width; GB_INTEGER height) + + GdkPixbuf *img; + GB_IMG *image; + + SYNCHRONIZE_IMAGE(THIS); + IMAGE.Convert(THIS, GB_IMAGE_RGBA); + img = gdk_pixbuf_new_from_data((const guchar *)THIS->data, GDK_COLORSPACE_RGB, TRUE, 8, THIS->width, THIS->height, THIS->width * sizeof(uint), NULL, NULL); + + img = stretch_pixbuf(img, VARG(width), VARG(height)); + + image = IMAGE.Create(gdk_pixbuf_get_width(img), gdk_pixbuf_get_height(img), GB_IMAGE_RGBA, gdk_pixbuf_get_pixels(img)); + IMAGE.Convert(image, IMAGE.GetDefaultFormat()); + GB.ReturnObject(image); + + g_object_unref(G_OBJECT(img)); + +END_METHOD + + +GB_DESC CImageDesc[] = +{ + GB_DECLARE("Image", sizeof(CIMAGE)), + + GB_STATIC_METHOD("Load", "Image", Image_Load, "(Path)s"), + GB_STATIC_METHOD("FromString", "Image", Image_FromString, "(Data)s"), + GB_METHOD("Save", NULL, Image_Save, "(Path)s[(Quality)i]"), + GB_METHOD("Stretch", "Image", Image_Stretch, "(Width)i(Height)i"), + + GB_END_DECLARE +}; + diff --git a/gb.image.io/src/c_image.h b/gb.image.io/src/c_image.h new file mode 100644 index 00000000..bd24ce4c --- /dev/null +++ b/gb.image.io/src/c_image.h @@ -0,0 +1,44 @@ +/*************************************************************************** + + c_image.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_IMAGE_H +#define __C_IMAGE_H + +#include "main.h" + +typedef + struct + { + GB_IMG img; + } + CIMAGE; + +#ifndef __C_IMAGE_C +extern GB_DESC CImageDesc[]; +#else + +#define THIS ((GB_IMG *)_object) + +#endif + +#endif /* __CIMAGE_H */ diff --git a/gb.image.io/src/gb.image.io.component b/gb.image.io/src/gb.image.io.component new file mode 100644 index 00000000..470a2b88 --- /dev/null +++ b/gb.image.io/src/gb.image.io.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.image.io +Author=Benoît Minisini +State=0 +Implements=ImageIO +Requires=gb.image diff --git a/gb.image.io/src/main.c b/gb.image.io/src/main.c new file mode 100644 index 00000000..d3a1c1bc --- /dev/null +++ b/gb.image.io/src/main.c @@ -0,0 +1,51 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" +#include "c_image.h" + +GB_INTERFACE GB EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CImageDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + +#if !defined(GLIB_VERSION_2_36) + // Bug in the gdk-pixbuf documentation: the following is mandatory. + g_type_init(); +#endif /* !defined(GLIB_VERSION_2_36) */ + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.image.io/src/main.h b/gb.image.io/src/main.h new file mode 100644 index 00000000..87f98e92 --- /dev/null +++ b/gb.image.io/src/main.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb.image.h" +#include "gb_common.h" + +#include + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.jit.llvm/AUTHORS b/gb.jit.llvm/AUTHORS new file mode 100644 index 00000000..4fcce32d --- /dev/null +++ b/gb.jit.llvm/AUTHORS @@ -0,0 +1 @@ +Emil Lenngren diff --git a/gb.jit.llvm/COPYING b/gb.jit.llvm/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.jit.llvm/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.jit.llvm/ChangeLog b/gb.jit.llvm/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.jit.llvm/INSTALL b/gb.jit.llvm/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.jit.llvm/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.jit.llvm/Makefile.am b/gb.jit.llvm/Makefile.am new file mode 100644 index 00000000..ed93e642 --- /dev/null +++ b/gb.jit.llvm/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @JITLLVM_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.jit.llvm/NEWS b/gb.jit.llvm/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.jit.llvm/README b/gb.jit.llvm/README new file mode 100644 index 00000000..3122f86f --- /dev/null +++ b/gb.jit.llvm/README @@ -0,0 +1,17 @@ +You should use the latest version of LLVM. To check it out, run: + +svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm +cd llvm +mkdir build +cd build +../configure --prefix=/usr --enable-optimized --enable-jit --enable-shared +make -j4 +sudo make install + +You might need to edit the configure.ac file in order to set the llvm +location settings appropriate, followed by ./reconf. + +To enable the JIT for all functions in a Gambas class, add the word "Fast" +on a separate line at the top of the class file. The functions will then be +JIT compiled to native machine code, instead of letting the interpreter to +run it, if gb.jit is available. diff --git a/gb.jit.llvm/acinclude.m4 b/gb.jit.llvm/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.jit.llvm/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.jit.llvm/component.am b/gb.jit.llvm/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.jit.llvm/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.jit.llvm/configure.ac b/gb.jit.llvm/configure.ac new file mode 100644 index 00000000..8f3e7b8b --- /dev/null +++ b/gb.jit.llvm/configure.ac @@ -0,0 +1,53 @@ +dnl ---- configure.ac for gb.jit + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-jit-llvm, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.jit.llvm) +AC_PROG_LIBTOOL + +min_llvm_version=3.1 +max_llvm_version=3.5 +next_max_llvm_version=3.6 + +dnl llvm-config file can be forced with LLVM_CONFIG env var +if test "x$LLVM_CONFIG" = x; then + AC_PATH_PROG([LLVM_CONFIG], [llvm-config], [no]) +fi + +if test "x$LLVM_CONFIG" = xno; then + touch DISABLED DISABLED.gb.jit.llvm +else + AC_MSG_CHECKING([for LLVM, version between $min_llvm_version and $max_llvm_version]) + + LLVM_VERSION=`$LLVM_CONFIG --version` + + AX_COMPARE_VERSION($LLVM_VERSION, [ge], $min_llvm_version, [min_llvm_version_ok=y], [min_llvm_version_ok=n]) + AX_COMPARE_VERSION($LLVM_VERSION, [lt], $next_max_llvm_version, [max_llvm_version_ok=y], [max_llvm_version_ok=n]) + + if test "x$min_llvm_version_ok$max_llvm_version_ok" = xyy; then + AC_MSG_RESULT([yes ($LLVM_VERSION)]) + + if test "x$LLVM_LIBS" = x; then + LLVM_LIBS=-lLLVM-$LLVM_VERSION + fi + + else + AC_MSG_RESULT(no) + touch DISABLED DISABLED.gb.jit.llvm + fi +fi + +dnl [GB_FIND(libLLVM-$LLVM_VERSION.$SHLIBEXT, `$LLVM_CONFIG --prefix` /usr/lib/llvm* /usr/local /usr, lib)], + +GB_COMPONENT( + jitllvm, JITLLVM, gb.jit.llvm, [src], + [GB_FIND(llvm/ExecutionEngine/JIT.h llvm/Config/llvm-config.h llvm-c/Core.h, `$LLVM_CONFIG --prefix` /usr/lib/llvm* /usr/local /usr, include)], + [], + [$C_LIB `$LLVM_CONFIG --ldflags` $LLVM_LIBS], + [-I../../main/gbx -I../../main/share -D_DEBUG -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS]) + +AC_OUTPUT(Makefile src/Makefile) + +GB_PRINT_MESSAGES diff --git a/gb.jit.llvm/gambas.h b/gb.jit.llvm/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.jit.llvm/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.jit.llvm/gb_common.h b/gb.jit.llvm/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.jit.llvm/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.jit.llvm/m4 b/gb.jit.llvm/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.jit.llvm/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.jit.llvm/reconf b/gb.jit.llvm/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.jit.llvm/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.jit.llvm/src/Makefile.am b/gb.jit.llvm/src/Makefile.am new file mode 100644 index 00000000..dee551c1 --- /dev/null +++ b/gb.jit.llvm/src/Makefile.am @@ -0,0 +1,34 @@ +COMPONENT = gb.jit.llvm +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.jit.llvm.la +noinst_LTLIBRARIES = libjit.llvm.la + +libjit_llvm_la_LIBADD = +libjit_llvm_la_LDFLAGS = -module @LD_FLAGS@ +libjit_llvm_la_CPPFLAGS = @JITLLVM_INC@ +libjit_llvm_la_CXXFLAGS = $(AM_CFLAGS) -std=gnu++0x -fno-exceptions -fno-rtti + +gb_jit_llvm_la_LIBADD = libjit.la @JITLLVM_LIB@ +gb_jit_llvm_la_LDFLAGS = -module @LD_FLAGS@ @JITLLVM_LDFLAGS@ +gb_jit_llvm_la_CPPFLAGS = @JITLLVM_INC@ +gb_jit_llvm_la_CXXFLAGS = $(AM_CXXFLAGS) -std=gnu++0x -fno-exceptions + +libjit_llvm_la_SOURCES = \ + jit_gambas_pass.cpp \ + jit_gambas_pass.h + +gb_jit_llvm_la_SOURCES = \ + gb.jit.h \ + jit_api.cpp \ + jit_codegen_conv.h \ + jit_codegen.cpp \ + jit_compile.cpp \ + jit_conv.cpp \ + jit_expressions.cpp \ + jit.h \ + jit_read.cpp \ + jit_runtime.c \ + jit_runtime.h \ + main.cpp \ + main.h diff --git a/gb.jit.llvm/src/gb.jit.h b/gb.jit.llvm/src/gb.jit.h new file mode 100644 index 00000000..2fe62566 --- /dev/null +++ b/gb.jit.llvm/src/gb.jit.h @@ -0,0 +1,218 @@ +/*************************************************************************** + + gb.jit.h + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_JIT_H +#define __GB_JIT_H + +#include "gbx_extern.h" + +typedef + struct { + //void (*GetExec)(void); + + void (*F_EXEC_release)(TYPE type, VALUE *value); + void (*F_RELEASE_many)(VALUE* val, int n); + + void (*F_EXEC_push_unknown)(ushort code); + void (*F_EXEC_push_array)(ushort code); + + void (*F_EXEC_pop_unknown)(void); + void (*F_EXEC_pop_array)(ushort code); + + void *(*F_CENUM_create)(void *object); + void (*F_EXEC_enum_first)(PCODE code); + char (*F_EXEC_enum_next)(PCODE code); + + void (*F_EXEC_new)(void); + + void (*F_EXEC_enter_quick)(void); + void (*F_EXEC_enter)(void); + //void (*F_EXEC_jit_execute_function)(void); + void (*F_EXEC_native_quick)(void); + void (*F_EXEC_native)(void); + char (*F_EXEC_call_native)(void (*exec)(), void *object, TYPE type, VALUE *param); + void (*F_EXEC_function_real)(void); + void (*F_EXEC_leave_keep)(void); + void (*F_EXEC_leave_drop)(void); + void (*F_EXEC_function_loop)(void); + + char (*F_EXEC_special)(int special, CLASS *klass, void *object, int nparam, bool drop); + CLASS *(*F_EXEC_object_variant)(VALUE *val, OBJECT **pobject); + char (*F_EXEC_object_other)(VALUE *val, CLASS **pclass, OBJECT **pobject); + + EXTERN_FUNC_INFO (*F_EXTERN_get_function_info)(CLASS_EXTERN *ext); + void (*F_EXTERN_call)(void); + void *(*F_EXTERN_make_callback)(VALUE_FUNCTION* value); + + char *(*F_STRING_new)(const char *src, int len); + void (*F_STRING_new_temp_value)(VALUE *value, const char* src, int len); + void (*F_STRING_free_real)(char *ptr); + char *(*F_STRING_free_later)(char *ptr); + int (*F_STRING_compare)(const char *ptr1, int len1, const char *ptr2, int len2); + char (*F_STRING_equal_ignore_case_same)(const char *str1, const char *str2, int len); + int (*F_STRING_conv)(char **result, const char *str, int len, const char *src, const char *dst, char _throw); + + int (*F_OBJECT_comp_value)(VALUE *object_1, VALUE *object_2); + int (*F_COMPARE_object)(void *object_1, void *object_2); + char (*F_CLASS_inherits)(CLASS *klass, CLASS *parent); + void *(*F_OBJECT_create)(CLASS *klass, const char *name, void *parent, int nparam); + void (*F_CLASS_free)(void *object); + int (*F_SYMBOL_find)(void *symbol, ushort *sort, int n_symbol, size_t s_symbol, int flag, const char *name, int len, const char *prefix); + + CLASS *(*F_CARRAY_get_array_class)(CLASS *klass, CTYPE ctype); + void *(*F_CARRAY_get_data_multi)(void *_object, GB_INTEGER *arg, int nparam); + void *(*F_CARRAY_create_static)(CLASS *klass, void *ref, CLASS_ARRAY *desc, void *data); + void *(*F_CSTRUCT_create_static)(void *ref, CLASS *klass, char *addr); + + void (*F_REGEXP_scan)(void *array, const char *pattern, int len_pattern, const char *string, int len_string); + + void (*F_VALUE_convert)(VALUE *value, TYPE type); + void (*F_VALUE_to_string)(VALUE *value, char **addr, int *len); + void (*F_VALUE_convert_float)(VALUE *value); + void (*F_VALUE_convert_variant)(VALUE *value); + void (*F_VALUE_convert_string)(VALUE *value); + char (*F_VALUE_is_null)(VALUE *val); + void (*F_VALUE_undo_variant)(VALUE *value); + + char (*F_NUMBER_from_string)(int option, const char *str, int len, VALUE *value); + void (*F_NUMBER_int_to_string)(uint64_t nbr, int prec, int base, VALUE *value); + + char (*F_LOCAL_format_number)(double number, int fmt_type, const char *fmt, int len_fmt, char **str, int *len_str, char local); + + int (*F_DATE_to_string)(char *buffer, VALUE *value); + char (*F_DATE_from_string)(const char *str, int len, VALUE *val, char local); + int (*F_DATE_comp)(DATE *date1, DATE *date2); + void (*F_DATE_timer)(double *result, int from_start); + void (*F_DATE_now)(VALUE *val); + void (*F_DATE_add)(VALUE *date, int period, int val); + void (*F_DATE_diff)(VALUE *date1, VALUE *date2, int period); + + void (*F_randomize)(char set, uint seed); + double (*F_rnd)(void); + + void (*F_THROW)(int code, ...) NORETURN; + void (*F_ERROR_panic)(const char *error, ...) NORETURN; + void (*F_ERROR_propagate)(void) NORETURN; + void (*F_ERROR_reset)(ERROR_INFO *info); + void (*F_ERROR_set_last)(char bt); + void (*F_ERROR_lock)(void); + void (*F_ERROR_unlock)(void); + + const char *(*F_TYPE_get_name)(TYPE type); + + void (*F_SUBR_not)(ushort code); + void (*F_SUBR_and_)(ushort code); + void (*F_SUBR_cat)(ushort code); + void (*F_SUBR_file)(ushort code); + void (*F_SUBR_like)(ushort code); + void (*F_SUBR_string)(void); + void (*F_SUBR_upper)(ushort code); + void (*F_SUBR_instr)(ushort code); + void (*F_SUBR_subst)(ushort code); + void (*F_SUBR_replace)(ushort code); + void (*F_SUBR_split)(ushort code); + void (*F_SUBR_strcomp)(ushort code); + void (*F_SUBR_sconv)(ushort code); + void (*F_SUBR_abs)(ushort code); + void (*F_SUBR_int)(ushort code); + void (*F_SUBR_fix)(ushort code); + void (*F_SUBR_sgn)(ushort code); + void (*F_SUBR_min_max)(ushort code); + void (*F_SUBR_choose)(ushort code); + void (*F_SUBR_bit)(ushort code); + void (*F_SUBR_is_type)(ushort code); + void (*F_SUBR_hex_bin)(ushort code); + void (*F_SUBR_val)(ushort code); + void (*F_SUBR_format)(ushort code); + void (*F_SUBR_year)(ushort code); + void (*F_SUBR_week)(ushort code); + void (*F_SUBR_date)(ushort code); + void (*F_SUBR_time)(ushort code); + void (*F_SUBR_eval)(ushort code); + void (*F_SUBR_debug)(void); + void (*F_SUBR_wait)(ushort code); + void (*F_SUBR_open)(ushort code); + void (*F_SUBR_close)(ushort code); + void (*F_SUBR_input)(ushort code); + void (*F_SUBR_linput)(void); + void (*F_SUBR_print)(ushort code); + void (*F_SUBR_read)(ushort code); + void (*F_SUBR_write)(ushort code); + void (*F_SUBR_flush)(void); + void (*F_SUBR_lock)(void); + void (*F_SUBR_inp_out)(ushort code); + void (*F_SUBR_eof)(ushort code); + void (*F_SUBR_lof)(ushort code); + void (*F_SUBR_seek)(ushort code); + void (*F_SUBR_kill)(ushort code); + void (*F_SUBR_move)(ushort code); + void (*F_SUBR_exist)(ushort code); + void (*F_SUBR_access)(ushort code); + void (*F_SUBR_stat)(ushort code); + void (*F_SUBR_dfree)(void); + void (*F_SUBR_temp)(ushort code); + void (*F_SUBR_isdir)(ushort code); + void (*F_SUBR_dir)(ushort code); + void (*F_SUBR_rdir)(ushort code); + void (*F_SUBR_exec)(ushort code); + void (*F_SUBR_alloc)(ushort code); + void (*F_SUBR_free)(void); + void (*F_SUBR_realloc)(ushort code); + void (*F_SUBR_strptr)(ushort code); + void (*F_SUBR_collection)(ushort code); + void (*F_SUBR_tr)(ushort code); + void (*F_SUBR_quote)(ushort code); + void (*F_SUBR_unquote)(ushort code); + void (*F_SUBR_ptr)(ushort code); + + void (*F_CLASS_load_from_jit)(CLASS *klass); + void (*F_CLASS_run_inits)(CLASS *klass); + + const char *(*F_DEBUG_get_current_position)(void); + void (*F_DEBUG_Profile_Add)(void *cp, void *fp, void *pc); + + void (*F_EXEC_quit)(void); + + + //In GB: LOCAL_gettext, CLASS_auto_create, GB_Raise, GB_CollectionGet, GB_CollectionSet + + } + GB_JIT_INTERFACE; + +typedef + struct { + intptr_t version; + void (*Init)(GB_JIT_INTERFACE *jif, char **STACK_limit, STACK_CONTEXT *EXEC_current, VALUE **SP, VALUE *TEMP, + VALUE *RET, char *GAMBAS_StopEvent, char **EXEC_enum, EXEC_GLOBAL *EXEC, + const char **EXEC_unknown_name, char *__EXEC_profile, char *__EXEC_profile_instr, unsigned char *__EXEC_quit_value, + void **EVENT_Last, ERROR_CONTEXT **ERROR_current, ERROR_HANDLER **ERROR_handler, const char *STRING_char_string); + void (*CompileAndExecute)(void); + void (*LoadClass)(CLASS *klass); + } + JIT_INTERFACE; + +#define JIT_INTERFACE_VERSION 1 + +#endif /* __GB_JIT_H */ diff --git a/gb.jit.llvm/src/gb.jit.llvm.component b/gb.jit.llvm/src/gb.jit.llvm.component new file mode 100644 index 00000000..5d62b136 --- /dev/null +++ b/gb.jit.llvm/src/gb.jit.llvm.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.jit.llvm +Name=Gambas JIT Compiler based on LLVM +Author=Emil Lenngren +Hidden=True diff --git a/gb.jit.llvm/src/jit.h b/gb.jit.llvm/src/jit.h new file mode 100644 index 00000000..03dcd899 --- /dev/null +++ b/gb.jit.llvm/src/jit.h @@ -0,0 +1,1309 @@ +/*************************************************************************** + + jit.h + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __JIT_H +#define __JIT_H + +#include + +#if (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 3) + #include "llvm/IR/LLVMContext.h" + #include "llvm/IR/Module.h" + #include "llvm/IR/Constants.h" + #include "llvm/IR/DerivedTypes.h" + #include "llvm/IR/Intrinsics.h" + #include "llvm/IR/Instructions.h" +#else + #include "llvm/LLVMContext.h" + #include "llvm/Module.h" + #include "llvm/Constants.h" + #include "llvm/DerivedTypes.h" + #include "llvm/Intrinsics.h" + #include "llvm/Instructions.h" +#endif + +#if (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 5) + #include "llvm/IR/Verifier.h" + #include "llvm/IR/CFG.h" +#else + #include "llvm/Analysis/Verifier.h" + #include "llvm/Support/CFG.h" +#endif + +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/ExecutionEngine/Interpreter.h" +#include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/raw_ostream.h" + +#if (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 3) + #include "llvm/IR/IRBuilder.h" +#elif (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR ==2) + #include "llvm/IRBuilder.h" +#else + #include "llvm/Support/IRBuilder.h" +#endif + +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/PassManager.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" + +#include +#include + +extern "C" { +#include +#include + +#define class klass + +#include "gb_common.h" +//#include "gb_error.h" +#include "gbx_type.h" + +#include "gb_limit.h" +//#include "gbx_subr.h" +#include "gbx_stack.h" +//#include "gbx_exec.h" + +#include "gbx_local.h" +#include "gbx_event.h" + +#define throw _throw +#include "gbx_c_array.h" +#undef throw + +#undef class +} + +#include "main.h" + +struct Expression; + +void JIT_conv(Expression*& value, TYPE type, Expression* other = NULL); + +void JIT_read(void); +void JIT_codegen(void); + +void ref_stack(void); +void set_ctrl_type(TYPE type, int index, CLASS* second = NULL); +TYPE get_ctrl_type(int index); +CLASS* get_ctrl_class(int index); + +int special_ctrl_type(TYPE type); +bool is_ctrl_type_used(int type, int index); +int special_ctrl_type(TYPE type); + +struct Expression; +void register_new_expression(Expression*); +void free_all_expressions(void); + +void mark_address_taken(int addr); + +unsigned short* get_current_read_pos(void); + +extern int ngosubs; +extern uint64_t func_byref_mask; + +template +static bool isa(Expression* expr){ + return typeid(*expr) == typeid(T); +} + +template +static T* dyn_cast(Expression* expr){ + if (typeid(*expr) == typeid(T)) + return (T*)expr; + return NULL; +} + +struct Expression { + TYPE type; + bool on_stack; + bool stack_if_ref; + bool no_ref_variant; + + void ref_on_stack(){ + stack_if_ref = true; + if (type == T_STRING || type == T_OBJECT || type == T_VARIANT || TYPE_is_pure_object(type)) + on_stack = true; + } + void must_on_stack(){ + on_stack = true; + } + Expression(){ + type = T_VOID; + on_stack = false; + stack_if_ref = false; + no_ref_variant = false; + register_new_expression(this); + } + virtual void codegen(){ + puts(typeid(*this).name()); + assert(false && "Codegen not done yet for this type"); + } + virtual llvm::Value* codegen_get_value(){ + puts(typeid(*this).name()); + assert(false && "codegen_get_value not done yet for this type"); + } + virtual void codegen_on_stack(){ + puts(typeid(*this).name()); + assert(false && "codegen_on_stack not done yet for this type"); + } + virtual ~Expression(){} +}; + +struct ConvExpression : Expression { + Expression* expr; + ConvExpression(Expression* expr, TYPE type) : expr(expr) { + this->type = type; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +enum FunctionType { + PrivateFn, + PureObjectFn, + PureObjectStaticFn, + VirtualObjectFn, + VirtualObjectStaticFn, + StaticFn, + EventFn, + + ClassFn, + ObjectAsFn + //UnknownFn +}; + +struct FunctionExpression { + llvm::Value* effective_class; + CLASS* function_class; + Expression* function_object; + CLASS_DESC* function_desc; + const char* function_unknown; + char function_kind; + char function_defined; + short function_index; + char function_expr_type; //Contains enum FunctionType +}; + +struct PushCStringExpression : Expression { + char* addr; + int start; + int len; + PushCStringExpression(char* a, int s, int l) : addr(a), start(s), len(l) { + type = T_CSTRING; + } + + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +//PushQuick, PushInteger +struct PushIntegerExpression : Expression { + int bits; + int64_t i; + PushIntegerExpression(int bits, int64_t i) : bits(bits), i(i) { + switch(bits){ + case 1: type = T_BOOLEAN; return; + case 8: type = T_BYTE; return; + case 16: type = T_SHORT; return; + case 32: type = T_INTEGER; return; + case 64: type = T_LONG; return; + } + } + void codegen(); + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct AddQuickExpression : Expression { + Expression* expr; + int add; + AddQuickExpression(Expression* val, int add); + void codegen(){ + if (on_stack) codegen_on_stack(); + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ + codegen_get_value(); + } +}; + +struct PushFloatExpression : Expression { + double val; + PushFloatExpression(int bits, double val) : val(val) { + type = bits == 32 ? T_SINGLE : T_FLOAT; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushNullPointerExpression : Expression { + PushNullPointerExpression() { + type = T_POINTER; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushVoidDateExpression : Expression { + PushVoidDateExpression() { + type = T_DATE; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushNullExpression : Expression { + PushNullExpression() { + type = T_NULL; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushVoidExpression : Expression { + PushVoidExpression() { + type = T_VOID; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushLastExpression : Expression { + PushLastExpression() { + type = T_OBJECT; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushComplexExpression : Expression { + Expression* op; + PushComplexExpression(Expression* op) : op(op) { + JIT_conv(this->op, T_FLOAT); + //FIXME + abort(); + } +}; + +struct ReadVariableExpression : Expression { + char* addr; + CTYPE* ctype; + CLASS* klass; + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +static TYPE ctype_to_type(CTYPE* ctype, CLASS* klass = CP){ + TYPE type = ctype->id; + if (ctype->id == TC_ARRAY){ + CTYPE* ctype2 = &klass->load->array[ctype->value]->ctype; + type = (TYPE)(void*)JIF.F_CARRAY_get_array_class(klass, *ctype2); + } else if (ctype->id == T_OBJECT || ctype->id == 14){ + if (ctype->id != T_OBJECT || ctype->value >= 0) + type = (TYPE)(void*)klass->load->class_ref[ctype->value]; + } + return type; +} + +struct PushAutoCreateExpression : Expression { + CLASS* klass; + PushAutoCreateExpression(CLASS* klass) : klass(klass) { + type = (TYPE)(void*)klass; + } + + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushStaticExpression : ReadVariableExpression { + PushStaticExpression(int index); +}; + +struct PopStaticExpression : Expression { + Expression* val; + char* addr; + PopStaticExpression(Expression* val, int index); + void codegen(); +}; + +struct PushDynamicExpression : Expression { + int index; + int offset; + CTYPE* ctype; + PushDynamicExpression(int index); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PopDynamicExpression : Expression { + Expression* val; + int index; + int offset; + PopDynamicExpression(Expression* val, int index); + void codegen(); +}; + +struct PushFunctionExpression : Expression, FunctionExpression { + int index; + PushFunctionExpression(int index); + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct PushClassExpression : Expression { + CLASS* klass; + PushClassExpression(int index) { + type = T_CLASS; + klass = CP->load->class_ref[index]; + JIT_load_class(klass); + } + PushClassExpression(CLASS* klass) : klass(klass) { + type = T_CLASS; + } + void codegen_on_stack(); +}; + +struct PushLocalExpression : Expression { + int index; + PushLocalExpression(int index); + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct PushParamExpression : Expression { + int index; + PushParamExpression(int index) : index(index) { + type = FP->param[index + FP->n_param].type; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct PopLocalExpression : Expression { + Expression* val; + int index; + PopLocalExpression(Expression* val, int index); + void codegen(); +}; + +struct PopParamExpression : Expression { + Expression* val; + int index; + PopParamExpression(Expression* val, int index); + void codegen(); +}; + +struct PopCtrlExpression : Expression { + Expression* val; + int index; + PopCtrlExpression(Expression* val, int index); + void codegen(); +}; + +struct PopOptionalExpression : Expression { + Expression* val; + int index; + bool is_default; + PopOptionalExpression(Expression* val, int index); + void codegen(); +}; + +struct PushExternExpression : Expression { + CLASS* klass; + Expression* object_to_release; + int index; + bool must_variant_dispatch; + PushExternExpression(CLASS* klass, int index, Expression* object_to_release) + : klass(klass), object_to_release(object_to_release), index(index) { + type = T_FUNCTION; + must_variant_dispatch = false; //Might get changed later in CallExpression::CallExpression + } +}; + +struct PushEventExpression : Expression, FunctionExpression { + int index; + PushEventExpression(int ind, const char* unknown_name); +}; + +struct PushArrayExpression : Expression { + std::vector args; + unsigned short* pc; + bool can_quick; + PushArrayExpression(Expression** it, int nargs); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PopArrayExpression : Expression { + std::vector args; + Expression* val; + unsigned short* pc; + bool can_quick; + PopArrayExpression(Expression** it, int nargs, Expression* val); + void codegen(); +}; + +struct PushSuperExpression : Expression { + PushSuperExpression(){ + type = (TYPE)(void*)CP->parent; + if (type == 0) + THROW(E_PARENT); + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushMeExpression : Expression { + PushMeExpression() { + type = (TYPE)(void*)CP; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +//Base class, not to be used directly +struct PushPureObjectExpression : Expression { + Expression* obj; + int index; + PushPureObjectExpression(Expression* obj, int index) : obj(obj), index(index) {} + CLASS* klass(){ + return (CLASS*)(void*)obj->type; + } + CLASS_DESC* desc(){ + CLASS* c = (CLASS*)(void*)obj->type; + return c->table[index].desc; + } +}; + +struct PushPureObjectConstantExpression : PushPureObjectExpression { + PushPureObjectConstantExpression(Expression* obj, int index) + : PushPureObjectExpression(obj, index){ + type = desc()->constant.type; + ref_stack(); + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushPureObjectVariableExpression : PushPureObjectExpression { + PushPureObjectVariableExpression(Expression* obj, int index) + : PushPureObjectExpression(obj, index){ + type = ctype_to_type(&desc()->variable.ctype, desc()->variable.klass); + ref_stack(); + + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +/*struct PushPureObjectStaticVariableExpression : PushPureObjectExpression { + PushPureObjectStaticVariableExpression(Expression* obj, int index) + : PushPureObjectExpression(obj, index){ + type = ctype_to_type(&desc()->variable.ctype); + } +};*/ + +struct PushPureObjectPropertyExpression : PushPureObjectExpression { + PushPureObjectPropertyExpression(Expression* obj, int index) + : PushPureObjectExpression(obj, index){ + type = desc()->property.type; + ref_stack(); + obj->must_on_stack(); + } + llvm::Value* codegen_private(bool get_value); + llvm::Value* codegen_get_value(){ return codegen_private(true); } + void codegen_on_stack(){ codegen_private(false); } +}; + +//The object expression must return null, but must still be executed +//This should only be used with virtual classes +//For example: Application.Env.Count +struct PushPureObjectStaticPropertyExpression : PushPureObjectExpression { + const char* name; + PushPureObjectStaticPropertyExpression(Expression* obj, int index, const char* name) + : PushPureObjectExpression(obj, index), name(name) { + type = desc()->property.type; + ref_stack(); + obj->must_on_stack(); + } + llvm::Value* codegen_private(bool get_value); + llvm::Value* codegen_get_value(){ return codegen_private(true); } + void codegen_on_stack(){ codegen_private(false); } +}; + +struct PushPureObjectStructFieldExpression : PushPureObjectExpression { + PushPureObjectStructFieldExpression(Expression* obj, int index) + : PushPureObjectExpression(obj, index){ + type = ctype_to_type(&desc()->variable.ctype, desc()->variable.klass); + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushPureObjectUnknownExpression : Expression { + Expression* obj; + const char* name; + int name_id; + PushPureObjectUnknownExpression(Expression* obj, const char* name, int name_id) : obj(obj), name(name), name_id(name_id) { + type = T_VARIANT; + ref_stack(); + obj->must_on_stack(); + on_stack = true; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct PushPureObjectFunctionExpression : PushPureObjectExpression, FunctionExpression { + PushPureObjectFunctionExpression(Expression* obj, int index, const char* unknown_name); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushPureObjectStaticFunctionExpression : PushPureObjectExpression, FunctionExpression { + PushPureObjectStaticFunctionExpression(Expression* obj, int index, const char* unknown_name); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +//Virtual classes +struct PushVirtualPropertyExpression : PushPureObjectExpression { + PushVirtualPropertyExpression(Expression* obj, int index) + : PushPureObjectExpression(obj, index) { + type = desc()->property.type; + ref_stack(); + obj->must_on_stack(); + + /*if (TYPE_is_pure_object(type)) + while (((CLASS*)(void*)type)->override) + type = (TYPE)(((CLASS*)(void*)type)->override);*/ + } + llvm::Value* codegen_private(bool get_value); + llvm::Value* codegen_get_value(){ return codegen_private(true); } + void codegen_on_stack(){ codegen_private(false); } +}; + +struct PushVirtualFunctionExpression : PushPureObjectExpression, FunctionExpression { + PushVirtualFunctionExpression(Expression* obj, int index, const char* unknown_name); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushVirtualStaticFunctionExpression : PushPureObjectExpression, FunctionExpression { + PushVirtualStaticFunctionExpression(Expression* obj, int index, const char* unknown_name); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushUnknownExpression : Expression { + Expression* obj; + unsigned short* pc; + int name_id; + PushUnknownExpression(Expression* obj, int name_id, unsigned short* pc) : + obj(obj), pc(pc), name_id(name_id) { + type = T_VARIANT; + ref_stack(); + obj->must_on_stack(); + on_stack = true; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +//This actually reads an arbitrary memory address into a value +struct PushStaticVariableExpression : ReadVariableExpression { + PushStaticVariableExpression(CLASS_DESC_VARIABLE* var) { + this->type = var->type; + this->addr = (char*)var->klass->stat + var->offset; + this->ctype = &var->ctype; + this->klass = var->klass; + } +}; + +struct PushStaticPropertyExpression : Expression { + Expression* obj; + int index; + PushStaticPropertyExpression(Expression* obj, int index); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushStaticFunctionExpression : Expression, FunctionExpression { + Expression* obj; + int index; + PushStaticFunctionExpression(Expression* obj, int index, const char* unknown_name = NULL); + CLASS* klass(){ + return ((PushClassExpression*)obj)->klass; + //return (CLASS*)(void*)obj->type; + } + CLASS_DESC* desc(){ + CLASS_DESC* desc = klass()->table[index].desc; + return desc; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PushStaticUnknownExpression : Expression { + CLASS* klass; + const char* name; + int name_id; + PushStaticUnknownExpression(CLASS* klass, const char* name, int name_id) : klass(klass), name(name), name_id(name_id) { + type = T_VARIANT; + ref_stack(); + on_stack = true; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +//Base class, not to be used directly +struct PopPureObjectExpression : Expression { + Expression* obj; + Expression* val; + int index; + PopPureObjectExpression(Expression* obj, Expression* val, int index) + : obj(obj), val(val), index(index) {} + CLASS_DESC* desc(){ + CLASS* c = (CLASS*)(void*)obj->type; + return c->table[index].desc; + } +}; + +struct PopPureObjectVariableExpression : PopPureObjectExpression { + PopPureObjectVariableExpression(Expression* obj, Expression* val, int index) + : PopPureObjectExpression(obj, val, index) { + type = ctype_to_type(&desc()->variable.ctype, desc()->variable.klass); + JIT_conv(this->val, type); + } + void codegen(); +}; + +struct PopPureObjectStructFieldExpression : PopPureObjectExpression { + PopPureObjectStructFieldExpression(Expression* obj, Expression* val, int index) + : PopPureObjectExpression(obj, val, index) { + if (desc()->variable.ctype.id == TC_ARRAY || desc()->variable.ctype.id == TC_STRUCT) + THROW_ILLEGAL(); + type = ctype_to_type(&desc()->variable.ctype, desc()->variable.klass); + JIT_conv(this->val, type); + this->val->ref_on_stack(); + } + void codegen(); +}; + +/*struct PopPureObjectStaticVariableExpression : PopPureObjectExpression { + PopPureObjectStaticVariableExpression(Expression* obj, Expression* val, int index) + : PopPureObjectExpression(obj, val, index) { + type = ctype_to_type(&desc()->variable.ctype); + JIT_conv(val, type); + } +};*/ + +struct PopVirtualPropertyExpression : PopPureObjectExpression { + const char* name; + bool is_static; + PopVirtualPropertyExpression(Expression* obj, Expression* val, int index, const char* name, bool is_static); + void codegen(); +}; + +struct PopPureObjectPropertyExpression : PopPureObjectExpression { + PopPureObjectPropertyExpression(Expression* obj, Expression* val, int index); + void codegen(); +}; + +//Actually writes the contents of a Value to an arbitrary address +struct PopStaticVariableExpression : Expression { + char* addr; + Expression* val; + PopStaticVariableExpression(TYPE type, char* addr, Expression* val) + : addr(addr), val(val) { + this->type = type; + JIT_conv(this->val, type); + } + void codegen(); +}; + +struct PopStaticPropertyExpression : Expression { + CLASS* klass; + Expression* val; + int index; + PopStaticPropertyExpression(CLASS* klass, Expression* val, int index); + void codegen(); +}; + +struct PopUnknownPropertyUnknownExpression : Expression { + Expression* obj; + Expression* val; + const char* name; + PopUnknownPropertyUnknownExpression(Expression* obj, Expression* val, const char* name) + : obj(obj), val(val), name(name) { + //type = T_VARIANT; + ref_stack(); + val->must_on_stack(); + obj->must_on_stack(); + } + void codegen(); +}; + +struct PopUnknownExpression : Expression { + Expression* obj; + Expression* val; + int name_id; + unsigned short* pc; + PopUnknownExpression(Expression* obj, Expression* val, int name_id, unsigned short* pc) + : obj(obj), val(val), name_id(name_id), pc(pc) { + //type = T_VARIANT; + ref_stack(); + val->must_on_stack(); + obj->must_on_stack(); + } + void codegen(); +}; + + +/** +A Swap statement: +Push A +Push B +Pop A +Pop B +So we replace Push A, Push B, Pop A to a SwapExpression, +that is taken as input for Pop B +**/ +struct SwapExpression : Expression { + Expression* push_a_expr; + Expression* pop_a_expr; //Of course this includes Push B + SwapExpression(Expression* push_a, Expression* pop_a) : push_a_expr(push_a), pop_a_expr(pop_a) { + type = push_a_expr->type; + on_stack = push_a_expr->on_stack; + stack_if_ref = push_a_expr->stack_if_ref; + no_ref_variant = push_a_expr->no_ref_variant; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct CallExpression : Expression { + std::vector args; + std::vector byref_expressions; + Expression* func; + CLASS* klass; + CLASS_DESC* desc; + unsigned short* pc; + int index; + char kind; + bool variant_call; + bool can_quick; + CallExpression(Expression* function, int nargs, Expression** it, unsigned short* pc); + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct OnGotoExpression : Expression { + Expression* condition; + std::vector destinations; + int default_addr; + OnGotoExpression(Expression* condition, std::vector&& destinations, int default_addr) + : condition(condition), destinations(destinations), default_addr(default_addr) { + for(size_t i=0, e=destinations.size(); i!=e; i++) + mark_address_taken(destinations[i]); + mark_address_taken(default_addr); + JIT_conv(condition, T_INTEGER); + } + void codegen(); +}; + +struct JumpExpression : Expression { + int addr; + JumpExpression(int addr) : addr(addr) { + mark_address_taken(addr); + } + void codegen(); +}; + +struct GosubExpression : Expression { + int end_ctrl; + + //Normal use + int gosubaddr; + + //On Gosub use + Expression* condition; + std::vector destinations; + int default_addr; + + GosubExpression(int end_ctrl, int gosubaddr) : end_ctrl(end_ctrl), gosubaddr(gosubaddr) { + mark_address_taken(gosubaddr); + } + + GosubExpression(int end_ctrl, Expression* condition, std::vector&& destinations, int default_addr) + : end_ctrl(end_ctrl), condition(condition), destinations(destinations), default_addr(default_addr) { + for(size_t i=0, e=destinations.size(); i!=e; i++) + mark_address_taken(destinations[i]); + mark_address_taken(default_addr); + JIT_conv(condition, T_INTEGER); + } + void codegen(); +}; + +struct JumpIfExpression : Expression { + Expression* val; + int jump_addr; + int next_addr; + bool jump_if_true; + JumpIfExpression(Expression* value, int jump_addr, int next_addr, bool t) + : val(value), jump_addr(jump_addr), next_addr(next_addr), jump_if_true(t) { + mark_address_taken(jump_addr); + mark_address_taken(next_addr); + JIT_conv(val, T_BOOLEAN); + } + void codegen(); +}; + +struct JumpFirstExpression : Expression { + Expression *to, *step; + int ctrl_to, local_var; + int body_addr, done_addr; + JumpFirstExpression(int ctrl_to, Expression* to_expr, Expression* step_expr, int local_var, int body_addr, int done_addr); + void codegen(); +}; + +struct JumpNextExpression : Expression { + int ctrl_to, local_var; + int body_addr, done_addr; + JumpNextExpression(int ctrl_to, int local_var, int body_addr, int done_addr) + : ctrl_to(ctrl_to), local_var(local_var), body_addr(body_addr), done_addr(done_addr) {} + void codegen(); +}; + +struct JumpEnumFirstExpression : Expression { + Expression* obj; + llvm::Value* effective_class; + llvm::Value* ob; + int ctrl; + JumpEnumFirstExpression(int ctrl, Expression* obj) + : obj(obj), ctrl(ctrl) { + set_ctrl_type(obj->type, ctrl); + } + void codegen(); +}; + +//Workaround for return value for JumpEnumNext statement +//Also in use for ByRef +struct OnStackExpression : Expression { + OnStackExpression(){ + on_stack = true; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){} +}; + +struct JumpEnumNextExpression : Expression { + JumpEnumFirstExpression* jfirst; + OnStackExpression* retval; + int cont_addr; + int addr; + unsigned short* pc; + bool drop; + bool defined; + JumpEnumNextExpression(JumpEnumFirstExpression* jfirst, int cont_addr, int addr, unsigned short* pc, bool drop, OnStackExpression* retval); + void codegen(); +}; + +struct NopExpression : Expression { + char* buf; + bool test_stack; + + NopExpression(const char* str){ + int len = strlen(str); + buf = (char*)malloc(len+1); + memcpy(buf, str, len+1); + test_stack = true; + } + + NopExpression(bool test_stack) : test_stack(test_stack) { + buf = (char*)"..."; + } + + void codegen(); + ~NopExpression(){ + //free(buf); + } +}; + +struct ProfileLineExpression : Expression { + unsigned short* pc; + ProfileLineExpression(unsigned short* pc) : pc(pc) {} + void codegen(); +}; + +struct DropExpression : Expression { + Expression* expr; + DropExpression(Expression* expr) : expr(expr) {} + void codegen(); +}; + +//Dup is only used in: With something = something_else ... End With +struct DupedValueExpression : Expression { + llvm::Value* llvm_value; + DupedValueExpression(TYPE t){ + type = t; + llvm_value = NULL; + } + llvm::Value* codegen_get_value(){ return llvm_value; } +}; + +struct DupExpression : Expression { + Expression* expr; + DupedValueExpression* duped; + DupExpression(Expression*& expression) : expr(expression) { + expression = duped = new DupedValueExpression(type = expr->type); + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct NewExpression : Expression { + std::vector args; + unsigned short* pc; + bool event; + NewExpression(Expression** it, int nargs, bool event); + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct ReturnExpression : Expression { + Expression* retval; + unsigned short* pc; + int kind; + ReturnExpression(Expression* expr, int kind); + void codegen(); +}; + +struct QuitExpression : Expression { + Expression* quitval; + QuitExpression(Expression* quitval); + void codegen(); +}; + +struct StopEventExpression : Expression { + void codegen(); +}; + +struct TryExpression : Expression { + int addr1, addr2; + TryExpression(int addr1, int addr2) : addr1(addr1), addr2(addr2) { + mark_address_taken(addr1); + mark_address_taken(addr2); + } + void codegen(); +}; + +struct EndTryExpression : Expression { + void codegen(); +}; + +//Used as first statement in functions having a Catch/Finally +struct LargeTryExpression : Expression { + int addr2; + LargeTryExpression(){ + addr2 = (int)(EC - get_current_read_pos()); + mark_address_taken(0); + mark_address_taken(addr2); + } + void codegen(); +}; +//Inserted where EC points to +struct LargeCatchExpression : Expression { + void codegen(); +}; +//This is the Catch statement in the bytecode +struct CatchExpression : Expression { + void codegen(); +}; + +struct UnaryExpression : Expression { + Expression* expr; + UnaryExpression(Expression* expr) : expr(expr) {} +}; + +//Used by SubrExpression +struct CheckStringExpression : UnaryExpression { + CheckStringExpression(Expression* expr) : UnaryExpression(expr) { + type = T_STRING; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; +struct CheckIntegerExpression : UnaryExpression { + CheckIntegerExpression(Expression* expr) : UnaryExpression(expr) { + type = T_INTEGER; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; +struct CheckFloatExpression : UnaryExpression { + CheckFloatExpression(Expression* expr) : UnaryExpression(expr) { + type = T_FLOAT; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; +struct CheckPointerExpression : UnaryExpression { + CheckPointerExpression(Expression* expr) : UnaryExpression(expr) { + type = T_POINTER; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + + +struct SubrExpression : Expression { + std::vector args; + int digit; + int extra; + unsigned short* pc; + + TYPE type2; + + SubrExpression(int digit, Expression** it, int nargs, int extra); + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct BinOpExpression : Expression { + Expression *left, *right; + BinOpExpression(Expression** it) : left(it[0]), right(it[1]) {} + std::pair codegen_operands(); +}; + +struct EqExpression : BinOpExpression { + TYPE t; + EqExpression(Expression** it); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct NotExpression : UnaryExpression { + NotExpression(Expression* expr); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct LessExpression : BinOpExpression { + TYPE t; + LessExpression(Expression** it); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct NearExpression : BinOpExpression { + NearExpression(Expression** it) : BinOpExpression(it) { + type = T_BOOLEAN; + JIT_conv(left, T_STRING); + JIT_conv(right, T_STRING); + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct AddSubBaseExpression : BinOpExpression { + AddSubBaseExpression(Expression** it); +}; + +struct AddExpression : AddSubBaseExpression { + AddExpression(Expression** it) : AddSubBaseExpression(it) {} + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ + codegen_get_value(); + } +}; + +struct SubExpression : AddSubBaseExpression { + SubExpression(Expression** it) : AddSubBaseExpression(it) {} + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ + codegen_get_value(); + } +}; + +struct MulExpression : BinOpExpression { + MulExpression(Expression** it); + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ + codegen_get_value(); + } +}; + +struct DivExpression : BinOpExpression { + DivExpression(Expression** it) : BinOpExpression(it) { + type = T_FLOAT; + ref_stack(); + JIT_conv(left, T_FLOAT); + JIT_conv(right, T_FLOAT); + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ + codegen_get_value(); + } +}; + +struct QuoRemBaseExpression : BinOpExpression { + QuoRemBaseExpression(Expression** it); +}; + +struct QuoExpression : QuoRemBaseExpression { + QuoExpression(Expression** it) : QuoRemBaseExpression(it) {} + + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct RemExpression : QuoRemBaseExpression { + RemExpression(Expression** it) : QuoRemBaseExpression(it) {} + + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct PowExpression : BinOpExpression { + PowExpression(Expression** it) : BinOpExpression(it) { + JIT_conv(left, T_FLOAT); + if (TYPE_is_integer(right->type)){ + JIT_conv(right, T_INTEGER); + } else { + JIT_conv(right, T_FLOAT); + } + type = T_FLOAT; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct AndOrXorBaseExpression : BinOpExpression { + AndOrXorBaseExpression(Expression** it); +}; + +struct AndExpression : AndOrXorBaseExpression { + AndExpression(Expression** it) : AndOrXorBaseExpression(it) {} + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct OrExpression : AndOrXorBaseExpression { + OrExpression(Expression** it) : AndOrXorBaseExpression(it) {} + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct XorExpression : AndOrXorBaseExpression { + XorExpression(Expression** it) : AndOrXorBaseExpression(it) {} + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct IsExpression : BinOpExpression { + IsExpression(Expression** it) : BinOpExpression(it) { + JIT_conv(left, T_OBJECT); + type = T_BOOLEAN; + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ codegen_get_value(); } +}; + +struct NegExpression : UnaryExpression { + NegExpression(Expression* expr) : UnaryExpression(expr) { + type = expr->type; + if (type == T_VARIANT){ + ref_stack(); + no_ref_variant = true; + } else if (!TYPE_is_number(type)) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(){ + codegen_get_value(); + } +}; + +struct CatFileExpression : Expression { + std::vector args; + CatFileExpression(Expression** it, int nargs){ + args.resize(nargs); + for(int i=0; imust_on_stack(); + } + type = T_STRING; + on_stack = true; + } +}; + +struct CatExpression : CatFileExpression { + CatExpression(Expression** it, int nargs) + : CatFileExpression(it, nargs) {} + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct FileExpression : CatFileExpression { + FileExpression(Expression** it, int nargs) + : CatFileExpression(it, nargs) {} + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct LikeExpression : BinOpExpression { + int kind; + LikeExpression(Expression** it, int kind) + : BinOpExpression(it), kind(kind) { + type = T_BOOLEAN; + left->must_on_stack(); + right->must_on_stack(); + //The runtime converts to string, so ref_stack is kind of needed .. + ref_stack(); + } + llvm::Value* codegen_get_value(); + void codegen_on_stack(); +}; + +struct Statement { + int addr; + bool address_taken; + Expression* expr; +}; + +extern std::vector all_statements; + +void mark_address_taken(int addr); + +#endif /* __JIT_H */ diff --git a/gb.jit.llvm/src/jit_api.cpp b/gb.jit.llvm/src/jit_api.cpp new file mode 100644 index 00000000..0ba3c8d1 --- /dev/null +++ b/gb.jit.llvm/src/jit_api.cpp @@ -0,0 +1,79 @@ +/*************************************************************************** + + jit_api.c + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __JIT_API_C + +#include "main.h" + +extern "C" { + +GB_JIT_INTERFACE JIF; + +char **_STACK_limit; +STACK_CONTEXT *_EXEC_current; +VALUE **_SP; +VALUE *_TEMP; +VALUE *_RET; + +char *_GAMBAS_StopEvent; +char **_EXEC_enum; +EXEC_GLOBAL *_EXEC; +const char **_EXEC_unknown_name; +char *_EXEC_profile; +char *_EXEC_profile_instr; +unsigned char *_EXEC_quit_value; + +void **_EVENT_Last; + +ERROR_CONTEXT **_ERROR_current; +ERROR_HANDLER **_ERROR_handler; + +const char *_STRING_char_string; + +void JIT_init(GB_JIT_INTERFACE *jif, char **__STACK_limit, STACK_CONTEXT *__EXEC_current, VALUE **__SP, VALUE *__TEMP, + VALUE *__RET, char *__GAMBAS_StopEvent, char **__EXEC_enum, EXEC_GLOBAL *__EXEC, + const char **__EXEC_unknown_name, char *__EXEC_profile, char *__EXEC_profile_instr, unsigned char *__EXEC_quit_value, + void **__EVENT_Last, ERROR_CONTEXT **__ERROR_current, ERROR_HANDLER **__ERROR_handler, const char *__STRING_char_string) +{ + JIF = *jif; + _STACK_limit = __STACK_limit; + _EXEC_current = __EXEC_current; + _SP = __SP; + _TEMP = __TEMP; + _RET = __RET; + _GAMBAS_StopEvent = __GAMBAS_StopEvent; + _EXEC_enum = __EXEC_enum; + _EXEC = __EXEC; + _EXEC_unknown_name = __EXEC_unknown_name; + _EXEC_profile = __EXEC_profile; + _EXEC_profile_instr = __EXEC_profile_instr; + _EXEC_quit_value = __EXEC_quit_value; + _EVENT_Last = __EVENT_Last; + _ERROR_current = __ERROR_current; + _ERROR_handler = __ERROR_handler; + _STRING_char_string = __STRING_char_string; +} + +} diff --git a/gb.jit.llvm/src/jit_codegen.cpp b/gb.jit.llvm/src/jit_codegen.cpp new file mode 100644 index 00000000..96c11f98 --- /dev/null +++ b/gb.jit.llvm/src/jit_codegen.cpp @@ -0,0 +1,7001 @@ +/*************************************************************************** + + jit_codegen.c + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __JIT_CODEGEN_C + +#include +#include +#include +#include +#include + +#include "jit.h" + +#include "jit_runtime.h" +#include "jit_gambas_pass.h" + +#define class klass +extern "C" { +#include "gbx_struct.h" +/*#include "gbx_api.h" +#include "gbx_regexp.h" +#include "gbx_math.h" + +#define throw _throw +#include "gbx_compare.h" +#undef throw*/ +} +#undef class + +#ifdef OS_64BITS +#define TARGET_BITS 64 +#else +#define TARGET_BITS 32 +#endif + +#define GOSUB_ON_STACK + +#define llvmType(t) llvm::Type::t(llvm_context) +#define pointer_t(type) llvm::PointerType::get((type), 0) +#define charPP llvm::PointerType::get(llvmType(getInt8PtrTy), 0) +#define get_nullptr() llvm::ConstantPointerNull::get(llvmType(getInt8PtrTy)) +#define get_voidstring() get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)); + +#define LONG_TYPE (TARGET_BITS == 64 ? llvmType(getInt64Ty) : llvmType(getInt32Ty)) + +#ifdef __CYGWIN__ +#define __finite finite +#define __isnan __isnand +#define __isinf __isinfd +#endif + +const size_t TYPE_sizeof_memory_tab[16] = { 0, 1, 1, 2, 4, 8, 4, 8, 8, sizeof(void *), sizeof(void *), sizeof(void *), sizeof(VARIANT), 0, 0, 0 }; + +///DEBUG +void print_type(llvm::Value* v); + +static std::set mappings; +//static std::map variable_mappings; + +static llvm::LLVMContext llvm_context; + +static llvm::StructType* string_type; +static llvm::StructType* date_type; +static llvm::StructType* function_type; +static llvm::StructType* variant_type; +static llvm::StructType* object_type; +static llvm::StructType* OBJECT_type; +static llvm::StructType* value_type; +static llvm::StructType* value_types[17]; +static llvm::StructType* two_longs_type; +static llvm::StructType* gosub_stack_node_type; + +static llvm::Module* M; +static llvm::ExecutionEngine* EE = NULL; + +static llvm::Function* llvm_function; +static llvm::BasicBlock* entry_block; +static llvm::IRBuilder<>* builder; +static llvm::Value* temp_value; +static llvm::Value* temp_value2; +static llvm::Value* temp_voidptr; +static llvm::Value* temp_int; +static llvm::Value* temp_double; +static llvm::Value* temp_date; +static llvm::Value* temp_2longs; +static llvm::Value* temp_errcontext1; +static llvm::Value* temp_errcontext2; +static llvm::Value* temp_got_error; +static llvm::Value* temp_got_error2; +//static llvm::Value* temp_gosub_stack; +//static llvm::Value* temp_num_gosubs_on_stack; +static llvm::Value* gp; +static llvm::Value* gosub_return_point; + +static std::vector return_points; +static std::vector addr_blocks; //addr_blocks[i] contains the basic block at code pos i, if the address is taken +struct PendingBranch { + llvm::BasicBlock* insert_point; + llvm::Value* condition; //i1, NULL if unconditional branch + int true_addr; + int false_addr; //Not used if unconditional branch +}; +static std::vector pending_branches; +struct JumpTablePendingBranch { + llvm::BasicBlock* insert_point; + llvm::Value* condition; //int32 + std::vector* destinations; + int default_addr; +}; +static std::vector jump_table_pending_branches; +static std::vector gosub_continue_points; +static std::vector gosub_return_points; + +static std::vector current_ctrl_types; +static std::vector > ctrl_values; +static std::vector locals; +static std::vector params; +static llvm::Value* current_op; + +static bool in_try = false; +static bool has_tries = false; +static std::vector try_blocks; + +static unsigned short* statement_pc; + +static llvm::ConstantInt* getInteger(int bits, uint64_t value){ + return llvm::ConstantInt::get(llvm_context, llvm::APInt(bits, value, true)); +} + +template +static llvm::ConstantFP* getFloat(_FloatType_ value){ + return llvm::ConstantFP::get(llvm_context, llvm::APFloat(value)); +} + +static llvm::Type* TYPE_llvm(TYPE type){ + if (TYPE_is_object(type)) + return object_type; + + static llvm::Type* const arr[] = { + llvmType(getVoidTy), + llvmType(getInt1Ty), + llvmType(getInt8Ty), + llvmType(getInt16Ty), + llvmType(getInt32Ty), + llvmType(getInt64Ty), + llvmType(getFloatTy), + llvmType(getDoubleTy), + date_type, + string_type, + string_type, + llvmType(getInt8PtrTy), + variant_type, + NULL, + llvmType(getInt8PtrTy), + NULL + }; + return arr[type]; +} + +static llvm::Type* type_from_char(char c){ + switch (c){ + case 'v': return llvmType(getVoidTy); + case 'b': return llvmType(getInt1Ty); + case 'c': return llvmType(getInt8Ty); + case 'h': return llvmType(getInt16Ty); + case 'i': return llvmType(getInt32Ty); + case 'l': return llvmType(getInt64Ty); + case 'f': return llvmType(getFloatTy); + case 'd': return llvmType(getDoubleTy); + case 'p': return llvmType(getInt8PtrTy); + + case 'j': return LONG_TYPE; + } + return 0; //Error +} + +static std::vector string_to_type_vector(const char* args){ + std::vector llvm_args; + for(int i=0, e=strlen(args); i!=e; i++) + llvm_args.push_back(type_from_char(args[i])); + return llvm_args; +} + +static llvm::FunctionType* get_function_type(char ret, const char* args, bool vararg = false){ + std::vector llvm_args = string_to_type_vector(args); + return llvm::FunctionType::get(type_from_char(ret), llvm_args, vararg); +} + +static llvm::Type* get_value_type(TYPE type){ + if (TYPE_is_object(type)) return value_types[T_OBJECT]; + return value_types[type]; +} + +static llvm::BasicBlock* create_bb(const char* name){ + return llvm::BasicBlock::Create(llvm_context, name, llvm_function); +} + +extern "C" double __powidf2(double, int); + +static void llvm_init(){ + llvm::InitializeNativeTarget(); + + //string_type = llvm::StructType::create(llvm_context, string_to_type_vector("jpii"), "String"); + date_type = llvm::StructType::create(llvm_context, string_to_type_vector("ii"), "Date"); + function_type = llvm::StructType::create(llvm_context, string_to_type_vector("ppcch"), "Function"); + variant_type = llvm::StructType::create(llvm_context, string_to_type_vector("jl"), "Variant", true); + object_type = llvm::StructType::create(llvm_context, string_to_type_vector("pp"), "Object"); + OBJECT_type = llvm::StructType::create(llvm_context, string_to_type_vector("pj"), "OBJECT"); + value_type = llvm::StructType::create(llvm_context, string_to_type_vector("jjjj"), "Value"); + two_longs_type = llvm::StructType::create(llvm_context, string_to_type_vector("jj"), "TwoLongs"); + +#if TARGET_BITS == 64 + static const char* const arr[17] = {"jjjj", "ji", "ji", "ji", "ji", "jl", "jf", "jd", "jii", "jpii", NULL, "jp", "jjl", "jppcch", "jp", "j", "ppp"}; +#else + static const char* const arr[17] = {"jjjj", "ji", "ji", "ji", "ji", "jil", "jf", "jid", "jii", "jpii", NULL, "jp", "jjl", "jppcch", "jp", "j", "ppp"}; +#endif + static const char* const names[17] = {"Void", "Boolean", "Byte", "Short", "Integer", "Long", "Single", "Float", "Date", "String", NULL, "Pointer", "ValueVariant", "ValueFunction", "Class", "Null", "ValueObject"}; + for(int i=0; i<=16; i++) + if (arr[i]) + value_types[i] = llvm::StructType::create(llvm_context, string_to_type_vector(arr[i]), names[i]); + value_types[T_CSTRING] = value_types[T_STRING]; + + string_type = value_types[T_STRING]; + + { + std::vector llvm_args; + //llvm_args.push_back(llvmType(getInt8PtrTy)); //Stores the blockaddress. Is a ushort PC in gbx_stack.h but this should work due to alignment ;) + llvm_args.push_back(llvmType(getInt16Ty)); //Stores an id of the return point, 0 == none + llvm_args.push_back(pointer_t(value_type)); + gosub_stack_node_type = llvm::StructType::create(llvm_context, llvm_args, "GosubStackNode"); + } + + llvm::sys::DynamicLibrary::AddSymbol("__powidf2", (void*)__powidf2); +} + +void register_global_symbol(llvm::StringRef name, llvm::GlobalValue* value, void* address){ + if (mappings.insert(name).second) + EE->addGlobalMapping(value, address); +} + +static llvm::Value* get_global(void* var, llvm::Type* llvm_type = llvmType(getInt8PtrTy)){ + /*char buf[32]; + sprintf(buf, "v_%p", var); + std::string name = buf; + if (var == &SP) + name = "SP"; + else if (var == &BP) + name = "BP"; + else if (var == &CP) + name = "CP"; + auto it = variable_mappings.insert(std::make_pair(var, (llvm::GlobalVariable*)0)); + if (it.second){ + llvm::GlobalVariable* v = new llvm::GlobalVariable(*M, llvm_type, false, llvm::GlobalValue::ExternalLinkage, 0, name); + it.first->second = v; + EE->addGlobalMapping(v, var); + return v; + } else { + return it.first->second; + }*/ + return builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(var)), pointer_t(llvm_type)); +} + +#define get_global_function(n, ret, args) get_global_function_real(#n, (void*)n, ret, args) +#define get_global_function_jif(n, ret, args) get_global_function_real(#n, (void*)JIF.F_##n, ret, args) +#define get_global_function_vararg(n, ret, args) get_global_function_real(#n, (void*)n, ret, args, true) +#define get_global_function_jif_vararg(n, ret, args) get_global_function_real(#n, (void*)JIF.F_##n, ret, args, true) +static llvm::Value* get_global_function_real(const char* name, void* func, char ret, const char* args, bool vararg = false){ + llvm::FunctionType* ft = get_function_type(ret, args, vararg); + //llvm::sys::DynamicLibrary::AddSymbol + //return get_global(func, ft); + llvm::GlobalValue* val = (llvm::GlobalValue*)M->getOrInsertFunction(name, ft); + register_global_symbol(name, val, func); + return val; +} + +static llvm::Value* read_global(void* var, llvm::Type* pt = llvmType(getInt8PtrTy)){ + //llvm::Value* addr = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(var)), pointer_t(pt)); + llvm::Value* addr = get_global(var, pt); + llvm::Value* v = builder->CreateLoad(addr); + return v; +} + +static llvm::Value* create_gep(llvm::Value* addr, int bits1, int val1, int bits2, int val2){ + llvm::Value* arr[] = {getInteger(bits1, val1), getInteger(bits2, val2)}; + return builder->CreateGEP(addr, arr); +} + +static llvm::Value* get_element_addr(llvm::Value* addr, int index){ + return create_gep(addr, TARGET_BITS, 0, 32, index); +} + +static llvm::Value* load_element(llvm::Value* addr, int index){ + return builder->CreateLoad(get_element_addr(addr, index)); +} + +static void store_element(llvm::Value* addr, int index, llvm::Value* val){ + builder->CreateStore(val, get_element_addr(addr, index)); +} + +static llvm::Value* get_new_struct(llvm::StructType* st){ + return llvm::UndefValue::get(st); +} + +static llvm::Value* get_new_struct(llvm::StructType* st, llvm::Value* v1){ + static const unsigned int arr[1] = {0}; + return builder->CreateInsertValue(llvm::UndefValue::get(st), v1, arr); +} + +static llvm::Value* get_new_struct(llvm::StructType* st, llvm::Value* v1, llvm::Value* v2){ + static const unsigned int arr[2][1] = {{0}, {1}}; + llvm::Value* val = builder->CreateInsertValue(llvm::UndefValue::get(st), v1, arr[0]); + return builder->CreateInsertValue(val, v2, arr[1]); +} + +static llvm::Value* get_new_struct(llvm::StructType* st, llvm::Value* v1, llvm::Value* v2, llvm::Value* v3){ + static const unsigned int arr[3][1] = {{0}, {1}, {2}}; + llvm::Value* val = builder->CreateInsertValue(llvm::UndefValue::get(st), v1, arr[0]); + val = builder->CreateInsertValue(val, v2, arr[1]); + return builder->CreateInsertValue(val, v3, arr[2]); +} + +static llvm::Value* get_new_struct(llvm::StructType* st, llvm::Value* v1, llvm::Value* v2, llvm::Value* v3, llvm::Value* v4){ + static const unsigned int arr[5][1] = {{0}, {1}, {2}, {3}, {4}}; + llvm::Value* val = builder->CreateInsertValue(llvm::UndefValue::get(st), v1, arr[0]); + val = builder->CreateInsertValue(val, v2, arr[1]); + val = builder->CreateInsertValue(val, v3, arr[2]); + return builder->CreateInsertValue(val, v4, arr[3]); +} + +static llvm::Value* get_new_struct(llvm::StructType* st, llvm::Value* v1, llvm::Value* v2, llvm::Value* v3, llvm::Value* v4, llvm::Value* v5){ + static const unsigned int arr[5][1] = {{0}, {1}, {2}, {3}, {4}}; + llvm::Value* val = builder->CreateInsertValue(llvm::UndefValue::get(st), v1, arr[0]); + val = builder->CreateInsertValue(val, v2, arr[1]); + val = builder->CreateInsertValue(val, v3, arr[2]); + val = builder->CreateInsertValue(val, v4, arr[3]); + return builder->CreateInsertValue(val, v5, arr[4]); +} + +static llvm::Value* extract_value(llvm::Value* val, int index){ + unsigned int arr[] = {index}; + return builder->CreateExtractValue(val, arr); +} + +static llvm::Value* insert_value(llvm::Value* container, llvm::Value* value, int index){ + unsigned int arr[] = {index}; + return builder->CreateInsertValue(container, value, arr); +} + +static llvm::Value* read_value(llvm::Value* addr, TYPE type){ + addr = builder->CreateBitCast(addr, pointer_t(get_value_type(type))); + switch(type){ + case T_VOID: return NULL; + case T_BOOLEAN: + return builder->CreateICmpNE(load_element(addr, 1), getInteger(32, 0)); + case T_BYTE: + case T_SHORT: + return builder->CreateTrunc(load_element(addr, 1), TYPE_llvm(type)); + case T_LONG: + case T_FLOAT: +#if TARGET_BITS == 32 + return load_element(addr, 2); +#endif + case T_INTEGER: + case T_SINGLE: + case T_POINTER: + case T_CLASS: + return load_element(addr, 1); + case T_DATE: + return get_new_struct(date_type, load_element(addr, 1), load_element(addr, 2)); + case T_STRING: + case T_CSTRING: { + //return builder->CreateLoad(addr); + llvm::Value* t = load_element(addr, 0); + llvm::Value* res = get_new_struct(string_type, t, load_element(addr, 1), load_element(addr, 2), load_element(addr, 3)); + + /*llvm::BasicBlock *bb1 = create_bb("bb1"), *bb2 = create_bb("bb2"); + + builder->CreateCondBr(builder->CreateICmpEQ(t, getInteger(TARGET_BITS, 12)), bb1, bb2); + builder->SetInsertPoint(bb1); + builder->CreateCall(get_global_function(abort, 'v', "")); + builder->CreateUnreachable(); + builder->SetInsertPoint(bb2);*/ + return res; + //FIXME remove above - now outcommented! + + /*gen_if_noreturn(builder->CreateICmpEQ(t, getInteger(TARGET_BITS, 12)), [&](){ + builder->CreateCall(get_global_function(abort, 'v', "")); + });*/ + } + //case T_CSTRING: + // return get_new_struct(string_type, getInteger(1, true), load_element(addr, 1), load_element(addr, 2), load_element(addr, 3)); + case T_VARIANT: { + llvm::Value* not_null = get_new_struct(variant_type, load_element(addr, 1), load_element(addr, 2)); + llvm::Value* if_null = get_new_struct(variant_type, getInteger(TARGET_BITS, T_NULL)); + return builder->CreateSelect(builder->CreateICmpNE(load_element(addr, 0), getInteger(TARGET_BITS, T_NULL)), not_null, if_null); + } + case T_FUNCTION: + return get_new_struct(function_type, load_element(addr, 1), load_element(addr, 2), load_element(addr, 3), load_element(addr, 4), load_element(addr, 5)); + case T_NULL: + abort(); + default: { + //FIXME + //builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"), get_global((void*)"laddar %p\n", llvmType(getInt8Ty)), load_element(addr, 0)); + llvm::Value* is_null = load_element(builder->CreateBitCast(addr, pointer_t(value_types[T_NULL])), 0); + is_null = builder->CreateICmpEQ(is_null, getInteger(TARGET_BITS, T_NULL)); + //llvm::Value* nullstruct = insert_value(get_new_struct(object_type), get_nullptr(), 1); + llvm::Value* nullstruct = get_new_struct(object_type, get_global((void*)type, llvmType(getInt8Ty)), get_nullptr()); + return builder->CreateSelect(is_null, nullstruct, get_new_struct(object_type, load_element(addr, 0), load_element(addr, 1))); + } + } +} + +static void store_value(llvm::Value* addr, llvm::Value* val, TYPE type, bool store_type = true){ + addr = builder->CreateBitCast(addr, pointer_t(get_value_type(type))); + if (store_type && type != T_STRING) + store_element(addr, 0, TYPE_is_object(type) ? builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)type), llvmType(getInt8PtrTy)) : getInteger(TARGET_BITS, type)); + switch(type){ + case T_BOOLEAN: + case T_SHORT: + store_element(addr, 1, builder->CreateSExt(val, llvmType(getInt32Ty))); break; + case T_BYTE: + store_element(addr, 1, builder->CreateZExt(val, llvmType(getInt32Ty))); break; + case T_LONG: + case T_FLOAT: +#if TARGET_BITS == 32 + store_element(addr, 2, val); break; +#endif + case T_INTEGER: + case T_SINGLE: + case T_POINTER: + case T_CLASS: + store_element(addr, 1, val); break; + case T_DATE: + case T_VARIANT: + store_element(addr, 1, extract_value(val, 0)); + store_element(addr, 2, extract_value(val, 1)); + break; + case T_STRING: + case T_CSTRING: + //builder->CreateStore(val, addr); + store_element(addr, 0, extract_value(val, 0)); + store_element(addr, 1, extract_value(val, 1)); + store_element(addr, 2, extract_value(val, 2)); + store_element(addr, 3, extract_value(val, 3)); + /* store_element(addr, 0, builder->CreateSelect(extract_value(val, 0), getInteger(TARGET_BITS, T_CSTRING), getInteger(TARGET_BITS, T_STRING))); + store_element(addr, 1, extract_value(val, 1)); + store_element(addr, 2, extract_value(val, 2)); + store_element(addr, 3, extract_value(val, 3)); + break; + case T_CSTRING: + store_element(addr, 1, extract_value(val, 1)); + store_element(addr, 2, extract_value(val, 2)); + store_element(addr, 3, extract_value(val, 3));*/ + break; + case T_FUNCTION: + store_element(addr, 1, extract_value(val, 0)); + store_element(addr, 2, extract_value(val, 1)); + store_element(addr, 3, extract_value(val, 2)); + store_element(addr, 4, extract_value(val, 3)); + store_element(addr, 5, extract_value(val, 4)); + break; + case T_VOID: + case T_NULL: + break; + default: + store_element(addr, 0, extract_value(val, 0)); + store_element(addr, 1, extract_value(val, 1)); + break; + } +} + +/*static llvm::Value* array_read(llvm::Value* addr, TYPE type){ + switch(type){ + case T_VOID: + case T_CSTRING: + case T_NULL: + case T_CLASS: + case T_FUNCTION: + abort(); + case T_BOOLEAN: return builder->CreateTrunc(builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getInt8PtrTy))), llvmType(getInt1Ty)); + case T_BYTE: return builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getInt8PtrTy))); + case T_SHORT: return builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getInt16PtrTy))); + case T_INTEGER: return builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getInt32PtrTy))); + case T_LONG: return builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getInt64PtrTy))); + case T_SINGLE: return builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getFloatPtrTy))); + case T_FLOAT: return builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getDoublePtrTy))); + case T_DATE: return builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(date_type))); + case T_STRING: { + addr = builder->CreateBitCast(addr, charPP); + llvm::Value* len_addr = builder->CreateGEP(addr, getInteger(TARGET_BITS, -4)); + len_addr = builder->CreateBitCast(len_addr, llvmType(getInt32PtrTy)); + return get_new_struct(string_type, getInteger(TARGET_BITS, T_STRING), addr, getInteger(32, 0), builder->CreateLoad(len_addr)); + } + case T_POINTER: return builder->CreateLoad(builder->CreateBitCast(addr, charPP)); + case T_VARIANT: { + //FIXME can we skip the T_VOID -> T_NULL test? + return builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(variant_type))); + } + default: { + return get_new_struct(object_type, getInteger(TARGET_BITS, type), builder->CreateLoad(builder->CreateBitCast(addr, charPP))); + } + } +}*/ + +static void store_default(llvm::Value* addr, TYPE type){ + switch(type){ + case T_VOID: return; + case T_BOOLEAN: builder->CreateStore(getInteger(1, false), addr); return; + case T_BYTE: builder->CreateStore(getInteger(8, 0), addr); return; + case T_SHORT: builder->CreateStore(getInteger(16, 0), addr); return; + case T_INTEGER: builder->CreateStore(getInteger(32, 0), addr); return; + case T_LONG: builder->CreateStore(getInteger(64, 0), addr); return; + case T_SINGLE: builder->CreateStore(getFloat(0.0f), addr); return; + case T_FLOAT: builder->CreateStore(getFloat(0.0), addr); return; + case T_DATE: builder->CreateStore(get_new_struct(date_type, getInteger(32, 0), getInteger(32, 0)), addr); return; + case T_STRING: + case T_CSTRING: builder->CreateStore(get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)), addr); return; + case T_CLASS: //FIXME? + case T_POINTER: builder->CreateStore(get_nullptr(), addr); return; + case T_VARIANT: builder->CreateStore(get_new_struct(variant_type, getInteger(TARGET_BITS, T_NULL)/*, undef int64*/), addr); return; + case T_FUNCTION: + case T_NULL: abort(); + default: + builder->CreateStore(get_new_struct(object_type, get_global((void*)type, llvmType(getInt8Ty)), get_nullptr()), addr); return; + } +} + +static llvm::Value* get_default(TYPE type){ + switch(type){ + case T_VOID: return NULL; + case T_BOOLEAN: return getInteger(1, false); + case T_BYTE: return getInteger(8, 0); + case T_SHORT: return getInteger(16, 0); + case T_INTEGER: return getInteger(32, 0); + case T_LONG: return getInteger(64, 0); + case T_SINGLE: return getFloat(0.0f); + case T_FLOAT: return getFloat(0.0); + case T_DATE: return get_new_struct(date_type, getInteger(32, 0), getInteger(32, 0)); + case T_STRING: + case T_CSTRING: return get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)); + case T_CLASS: return NULL; + case T_POINTER: return get_nullptr(); + case T_VARIANT: return get_new_struct(variant_type, getInteger(TARGET_BITS, T_NULL)); + case T_FUNCTION: + case T_NULL: abort(); + default: + return get_new_struct(object_type, get_global((void*)type, llvmType(getInt8Ty)), get_nullptr()); + } +} + +template +static void gen_if(llvm::Value* cond, T func, const char* if_name="if.then", const char* cont_name="if.cont"){ + llvm::BasicBlock* then_block = create_bb(if_name); + + llvm::BasicBlock* from_block = builder->GetInsertBlock(); + + builder->SetInsertPoint(then_block); + func(); + llvm::BasicBlock* cont_block = create_bb(cont_name); + builder->CreateBr(cont_block); + + builder->SetInsertPoint(from_block); + builder->CreateCondBr(cond, then_block, cont_block); + + builder->SetInsertPoint(cont_block); +} + +template +static llvm::PHINode* gen_if_phi(llvm::Value* else_value, llvm::Value* cond, T func, const char* if_name="if.then", const char* cont_name="if.cont"){ + llvm::BasicBlock* then_block = create_bb(if_name); + + llvm::BasicBlock* from_block = builder->GetInsertBlock(); + + builder->SetInsertPoint(then_block); + llvm::Value* ret = func(); + llvm::BasicBlock* then_block2 = builder->GetInsertBlock(); + llvm::BasicBlock* cont_block = create_bb(cont_name); + builder->CreateBr(cont_block); + + builder->SetInsertPoint(from_block); + builder->CreateCondBr(cond, then_block, cont_block); + + builder->SetInsertPoint(cont_block); + + llvm::PHINode* phi = builder->CreatePHI(ret->getType(), 2); + phi->addIncoming(ret, then_block2); + phi->addIncoming(else_value, from_block); + return phi; +} + +template +static void gen_if_noreturn(llvm::Value* cond, T func, const char* if_name="if.then", const char* cont_name="if.cont"){ + llvm::BasicBlock* then_block = create_bb(if_name); + + llvm::BasicBlock* from_block = builder->GetInsertBlock(); + + builder->SetInsertPoint(then_block); + func(); + + builder->SetInsertPoint(from_block); + llvm::BasicBlock* cont_block = create_bb(cont_name); + builder->CreateCondBr(cond, then_block, cont_block); + + builder->SetInsertPoint(cont_block); +} + +template +static void gen_if_else(llvm::Value* cond, T1 then_func, T2 else_func, const char* if_name="if.then", const char* else_name="if.else", const char* cont_name="if.cont"){ + + llvm::BasicBlock* then_block = create_bb(if_name); + + llvm::BasicBlock* from_block = builder->GetInsertBlock(); + + builder->SetInsertPoint(then_block); + then_func(); + llvm::BasicBlock* then_block2 = builder->GetInsertBlock(); + + llvm::BasicBlock* else_block = create_bb(else_name); + builder->SetInsertPoint(else_block); + else_func(); + llvm::BasicBlock* else_block2 = builder->GetInsertBlock(); + + llvm::BasicBlock* cont_block = create_bb(cont_name); + builder->SetInsertPoint(from_block); + builder->CreateCondBr(cond, then_block, else_block); + builder->SetInsertPoint(then_block2); + builder->CreateBr(cont_block); + builder->SetInsertPoint(else_block2); + builder->CreateBr(cont_block); + builder->SetInsertPoint(cont_block); +} + +template +static llvm::PHINode* gen_if_else_phi(llvm::Value* cond, T1 then_func, T2 else_func, const char* if_name="if.then", const char* else_name="if.else", const char* cont_name="if.cont"){ + + llvm::BasicBlock* then_block = create_bb(if_name); + + llvm::BasicBlock* from_block = builder->GetInsertBlock(); + + builder->SetInsertPoint(then_block); + llvm::Value* ret1 = then_func(); + llvm::BasicBlock* then_block2 = builder->GetInsertBlock(); + + llvm::BasicBlock* else_block = create_bb(else_name); + builder->SetInsertPoint(else_block); + llvm::Value* ret2 = else_func(); + llvm::BasicBlock* else_block2 = builder->GetInsertBlock(); + + llvm::BasicBlock* cont_block = create_bb(cont_name); + builder->SetInsertPoint(from_block); + builder->CreateCondBr(cond, then_block, else_block); + builder->SetInsertPoint(then_block2); + builder->CreateBr(cont_block); + builder->SetInsertPoint(else_block2); + builder->CreateBr(cont_block); + builder->SetInsertPoint(cont_block); + + llvm::PHINode* phi = builder->CreatePHI(ret1->getType(), 2); + phi->addIncoming(ret1, then_block2); + phi->addIncoming(ret2, else_block2); + return phi; +} + +template +static void gen_while(llvm::Value* initial_condition, T1 body_func){ + llvm::BasicBlock* body_block = create_bb("while_block"); + + llvm::BasicBlock* from_block = builder->GetInsertBlock(); + + builder->SetInsertPoint(body_block); + llvm::Value* cont = body_func(); + llvm::BasicBlock* cont_block = create_bb("while_cont"); + builder->CreateCondBr(cont, body_block, cont_block); + + builder->SetInsertPoint(from_block); + builder->CreateCondBr(initial_condition, body_block, cont_block); + + builder->SetInsertPoint(cont_block); +} + +template +static llvm::Value* gen_and_if(llvm::Value* left, T1 right_func){ + llvm::BasicBlock* right_block = create_bb("and_if"); + + llvm::BasicBlock* from_block = builder->GetInsertBlock(); + + builder->SetInsertPoint(right_block); + llvm::Value* right = right_func(); + llvm::BasicBlock* cont_block = create_bb("and_if_cont"); + builder->CreateBr(cont_block); + + builder->SetInsertPoint(from_block); + builder->CreateCondBr(left, right_block, cont_block); + + builder->SetInsertPoint(cont_block); + llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2); + phi->addIncoming(getInteger(1, false), from_block); + phi->addIncoming(right, right_block); + return phi; +} + +static llvm::Value* get_cstring_from_addr(llvm::Value* addr){ + llvm::Value* str; + llvm::BasicBlock* B1 = builder->GetInsertBlock(); + llvm::BasicBlock* B2; + + gen_if(builder->CreateICmpNE(addr, get_nullptr()), [&](){ + llvm::Value* len = builder->CreateCall(get_global_function(strlen, 'j', "p"), addr); + if (TARGET_BITS == 64) + len = builder->CreateTrunc(len, llvmType(getInt32Ty)); + str = get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), addr, getInteger(32, 0), len); + B2 = builder->GetInsertBlock(); + }, "cstring_strlen", "cstring_null_or_done_strlen"); + + + llvm::PHINode* phi = builder->CreatePHI(string_type, 2); + phi->addIncoming(get_default(T_CSTRING), B1); + phi->addIncoming(str, B2); + return phi; +} + +static llvm::Value* to_target_int(llvm::Value* integer32){ + if (TARGET_BITS == 64) + return builder->CreateZExt(integer32, llvmType(getInt64Ty)); + return integer32; +} + +static void init_locals(){ + int n_local = FP->n_local; + int n_ctrl = FP->n_ctrl; + int n_param = FP->n_param; + + locals.resize(n_local + n_ctrl); + int i; + for(i=0; iCreateAlloca(TYPE_llvm(ctype_to_type(&FP->local[i].type))); + builder->CreateStore(get_default(ctype_to_type(&FP->local[i].type)), locals[i]); + //builder->CreateStore(read_value(builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, i*sizeof(VALUE))), ctype_to_type(&FP->local[i].type)), locals[i]); + } + /*for(i; i < n_local + n_ctrl; i++){ + TYPE type = get_ctrl_type(i); + if (type != T_VOID){ + locals[i] = builder->CreateAlloca(TYPE_llvm(type)); + store_default(locals[i], type); + } + }*/ + ctrl_values.resize(n_ctrl); + current_ctrl_types.resize(n_ctrl); + for(; i < n_local + n_ctrl; i++){ + set_ctrl_type(T_VOID, i); + locals[i] = NULL; + current_ctrl_types[i - n_local] = builder->CreateAlloca(llvmType(getInt32Ty)); + builder->CreateStore(getInteger(32, 0), current_ctrl_types[i - n_local]); + ctrl_values[i - n_local][1] = builder->CreateAlloca(string_type); + ctrl_values[i - n_local][2] = builder->CreateAlloca(object_type); + ctrl_values[i - n_local][3] = builder->CreateAlloca(variant_type); + } + + params.resize(n_param); + for(i=0; iCreateAlloca(TYPE_llvm(FP->param[i].type)); + builder->CreateStore(read_value(builder->CreateGEP(read_global(&PP), getInteger(TARGET_BITS, (i-n_param)*sizeof(VALUE))), FP->param[i].type), params[i]); + } +} + +static llvm::Value* release_ctrl(int i); + +static void finish_gosub_returns(){ + llvm::BasicBlock* bb = builder->GetInsertBlock(); + for(size_t i=0, e=gosub_return_points.size(); i!=e; i++){ + builder->SetInsertPoint(gosub_return_points[i]); + /*llvm::Value* index = builder->CreateSub(builder->CreateLoad(temp_num_gosubs_on_stack), getInteger(32, 1)); + builder->CreateStore(index, temp_num_gosubs_on_stack); + + llvm::Value* indices[2] = {getInteger(TARGET_BITS, 0), to_target_int(index)}; + llvm::Value* addr = builder->CreateGEP(temp_gosub_stack, indices);*/ + + //Cleanup current ctrl values + for(int i=0; in_ctrl; i++){ + llvm::Value* old_type = release_ctrl(i + FP->n_local); + builder->CreateStore(getInteger(32, 0), current_ctrl_types[i]); + gen_if(builder->CreateICmpNE(old_type, getInteger(32, 0)), [&](){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, (i + FP->n_local)*sizeof(VALUE))); + store_value(stack_addr, NULL, T_VOID); + }, "old_ctrl_needs_to_be_cleaned1"); + } + //And return + /*llvm::Value* addr = builder->CreateLoad(gosub_return_point); + llvm::IndirectBrInst* indbrinst = builder->CreateIndirectBr(addr, gosub_continue_points.size()); + + for(size_t j=0, e=gosub_continue_points.size(); j!=e; j++){ + indbrinst->addDestination(gosub_continue_points[j]); + }*/ + llvm::Value* return_id = builder->CreateLoad(gosub_return_point); + llvm::SwitchInst* switchinst = builder->CreateSwitch(return_id, gosub_continue_points[0], gosub_continue_points.size()-1); + + for(size_t j=1, e=gosub_continue_points.size(); j!=e; j++){ + switchinst->addCase(getInteger(16, j+1), gosub_continue_points[j]); + } + } + gosub_return_points.clear(); + builder->SetInsertPoint(bb); +} + +static void create_return(){ + llvm::BasicBlock* BB = create_bb("return"); + builder->CreateBr(BB); + + for(size_t i=0, e=return_points.size(); i!=e; i++){ + builder->SetInsertPoint(return_points[i]); + builder->CreateBr(BB); + } + return_points.clear(); + + builder->SetInsertPoint(BB); + + if (func_byref_mask){ + int nparam = FP->n_param; + for(int i=0; iparam[i].type; + if (type != T_STRING && !TYPE_is_object(type)){ + llvm::Value* v = builder->CreateLoad(params[i]); + llvm::Value* stack_addr = builder->CreateGEP(read_global(&PP), getInteger(TARGET_BITS, (i-nparam)*sizeof(VALUE))); + store_value(stack_addr, v, type); + } + } + } + + //static int counter = 0; + + //builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"), get_global((void*)"leave %d\n", llvmType(getInt8Ty)), getInteger(32, counter++)); + + builder->CreateCall(get_global_function_jif(EXEC_leave_keep, 'v', "")); + //builder->CreateCall(get_global_function_vararg(printf, 'v', "p"), get_global((void*)"leave done\n", llvmType(getInt8Ty))); + builder->CreateRetVoid(); +} +static llvm::Value* read_sp(); +static void codegen_statements(){ + addr_blocks.resize(all_statements.back()->addr + 1); + + for(size_t i=0, e=all_statements.size(); i!=e; i++){ + if (all_statements[i]->address_taken){ + llvm::BasicBlock* block = create_bb("block"); + builder->CreateBr(block); + builder->SetInsertPoint(block); + addr_blocks[all_statements[i]->addr] = block; + } + statement_pc = FP->code + all_statements[i]->addr; + /*builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)"before: %p\n", llvmType(getInt8Ty)), read_sp());*/ + all_statements[i]->expr->codegen(); + /*builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)"after: %p\n", llvmType(getInt8Ty)), read_sp());*/ + } +} + +static void insert_pending_branches(){ + for(size_t i=0, e=pending_branches.size(); i!=e; i++){ + PendingBranch& p = pending_branches[i]; + builder->SetInsertPoint(p.insert_point); + if (p.condition){ + builder->CreateCondBr(p.condition, addr_blocks[p.true_addr], addr_blocks[p.false_addr]); + } else { + builder->CreateBr(addr_blocks[p.true_addr]); + } + } + pending_branches.clear(); + for(size_t i=0, e=jump_table_pending_branches.size(); i!=e; i++){ + JumpTablePendingBranch& p = jump_table_pending_branches[i]; + builder->SetInsertPoint(p.insert_point); + llvm::SwitchInst* sw = builder->CreateSwitch(p.condition, addr_blocks[p.default_addr], p.destinations->size()); + for(size_t j=0, ee=p.destinations->size(); j!=ee; j++) + sw->addCase(getInteger(32, j), addr_blocks[(*p.destinations)[j]]); + } + jump_table_pending_branches.clear(); +} + +static void store_pc(unsigned short* pc){ + builder->CreateStore(getInteger(TARGET_BITS, (int64_t)(intptr_t)pc), get_global((void*)&PC, LONG_TYPE)); +} + +static void create_throw(int code){ + if (FP->debug) + store_pc(statement_pc); + builder->CreateCall(get_global_function_jif_vararg(THROW, 'v', "i"), getInteger(32, code)); + builder->CreateUnreachable(); +} +static void create_throw(int code, const char* a, const char* b){ + if (FP->debug) + store_pc(statement_pc); + builder->CreateCall3(get_global_function_jif_vararg(THROW, 'v', "i"), getInteger(32, code), get_global((void*)a, llvmType(getInt8Ty)), get_global((void*)b, llvmType(getInt8Ty))); + builder->CreateUnreachable(); +} +static void create_throw(llvm::Value* code){ + if (FP->debug) + store_pc(statement_pc); + builder->CreateCall(get_global_function_jif_vararg(THROW, 'v', "i"), code); + builder->CreateUnreachable(); +} + +static llvm::Value* get_value_on_top_addr(){ + //llvm::Value* sp = builder->CreateBitCast(read_global(&SP), pointer_t(value_type)); + llvm::Value* sp = read_global(&SP, pointer_t(value_type)); + return builder->CreateGEP(sp, getInteger(TARGET_BITS, -1)); +} + +static llvm::Value* get_top(TYPE type){ + return read_value(get_value_on_top_addr(), type); +} + +static llvm::Value* read_sp(){ + return read_global(&SP, pointer_t(value_type)); +} + +static void store_sp(llvm::Value* sp){ + llvm::Value* sp_addr = get_global(&SP, pointer_t(value_type)); + builder->CreateStore(sp, sp_addr); +} + +static void c_SP(int diff){ + if (diff == 0) return; + llvm::Value* sp_addr = get_global(&SP, pointer_t(value_type)); + llvm::Value* sp = builder->CreateLoad(sp_addr); + sp = builder->CreateGEP(sp, getInteger(TARGET_BITS, diff)); + builder->CreateStore(sp, sp_addr); +} + +static void push_value(llvm::Value* val, TYPE type){ + //c_SP(1); + //store_value(get_value_on_top_addr(), val, type); + llvm::Value* top = get_global(&SP, pointer_t(value_type)); + llvm::Value* addr = builder->CreateLoad(top); + store_value(addr, val, type); + builder->CreateStore(builder->CreateGEP(addr, getInteger(TARGET_BITS, 1)), top); +} + +static void set_top_value(llvm::Value* val, TYPE type, bool store_type = true){ + store_value(get_value_on_top_addr(), val, type, store_type); +} + +static llvm::Value* ret_top_stack(TYPE type, bool save){ + llvm::Value* val = read_value(get_value_on_top_addr(), type); + if (!save) + c_SP(-1); + return val; +} + +static void unref_string_no_nullcheck(llvm::Value* ptr){ + llvm::Value* str_ptr = builder->CreateBitCast(ptr, pointer_t(llvmType(getInt32Ty))); + llvm::Value* ref_addr = builder->CreateGEP(str_ptr, getInteger(TARGET_BITS, -2)); + llvm::Value* ref = builder->CreateLoad(ref_addr); + /*gen_if_noreturn(builder->CreateICmpSLE(ref, getInteger(32, 0)), [&](){ + builder->CreateCall(get_global_function(abort, 'v', "")); + builder->CreateUnreachable(); + }); + gen_if_noreturn(builder->CreateICmpUGE(ref, getInteger(32, 10000)), [&](){ + builder->CreateCall(get_global_function(abort, 'v', "")); + builder->CreateUnreachable(); + });*/ + ref = builder->CreateSub(ref, getInteger(32, 1)); + builder->CreateStore(ref, ref_addr); + llvm::Value* slt = builder->CreateICmpSLT(ref, getInteger(32, 1)); + if (llvm::Instruction* inst = llvm::dyn_cast(slt)){ + llvm::Value* arr[1] = {getInteger(32, 1)}; + inst->setMetadata("unref_slt", llvm::MDNode::get(llvm_context, arr)); + } + gen_if(slt, [&](){ + llvm::Value* free_func = get_global_function_jif(STRING_free_real, 'v', "p"); + builder->CreateCall(free_func, ptr); + }, "release_str", "release_done"); +} + +static void unref_string(llvm::Value* ptr){ + gen_if(builder->CreateICmpNE(ptr, get_nullptr()), [&](){ + unref_string_no_nullcheck(ptr); + }, "str_not_null", "unref_done"); +} + +static void unref_object_no_nullcheck(llvm::Value* ptr){ + /*static int counter = 0; + builder->CreateCall3(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)"unref #: %d, %p\n", llvmType(getInt8Ty)), getInteger(32, counter++), ptr);*/ + + llvm::Value* object_ptr = builder->CreateBitCast(ptr, pointer_t(OBJECT_type)); + llvm::Value* ref_addr = get_element_addr(object_ptr, 1); + llvm::Value* ref = builder->CreateLoad(ref_addr); + ref = builder->CreateSub(ref, getInteger(TARGET_BITS, 1)); + builder->CreateStore(ref, ref_addr); + /*builder->CreateCall3(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)"%p %ld -\n", llvmType(getInt8Ty)), ptr, ref);*/ + llvm::Value* slt = builder->CreateICmpSLT(ref, getInteger(TARGET_BITS, 1)); + if (llvm::Instruction* inst = llvm::dyn_cast(slt)){ + llvm::Value* arr[1] = {getInteger(32, 1)}; + inst->setMetadata("unref_slt", llvm::MDNode::get(llvm_context, arr)); + } + gen_if(slt, [&](){ + llvm::Value* free_func = get_global_function_jif(CLASS_free, 'v', "p"); + builder->CreateCall(free_func, ptr); + }, "release_obj", "release_done"); +} + +static void unref_object(llvm::Value* ptr){ + gen_if(builder->CreateICmpNE(ptr, get_nullptr()), [&](){ + unref_object_no_nullcheck(ptr); + }, "obj_not_null", "unref_done"); +} + +static void codegen_printf(const char* str1, int tal){ + /*builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)str1, llvmType(getInt8Ty)), getInteger(32, tal));*/ +} +static void codegen_printf(const char* str1){ + /*builder->CreateCall(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)str1, llvmType(getInt8Ty)));*/ +} +static void codegen_printf(const char* str1, llvm::Value* intval){ + /*builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)str1, llvmType(getInt8Ty)), intval);*/ +} + +static void borrow_string_no_nullcheck(llvm::Value* ptr){ + llvm::Value* str_ptr = builder->CreateBitCast(ptr, pointer_t(llvmType(getInt32Ty))); + llvm::Value* ref_addr = builder->CreateGEP(str_ptr, getInteger(TARGET_BITS, -2)); + llvm::Value* ref = builder->CreateLoad(ref_addr); + /*gen_if_noreturn(builder->CreateICmpSLE(ref, getInteger(32, 0)), [&](){ + builder->CreateCall(get_global_function(abort, 'v', "")); + builder->CreateUnreachable(); + }); + gen_if_noreturn(builder->CreateICmpUGT(ref, getInteger(32, 10000)), [&](){ + builder->CreateCall(get_global_function(abort, 'v', "")); + builder->CreateUnreachable(); + }); + + builder->CreateCall3(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)"%p %d\n", llvmType(getInt8Ty)), ptr, ref);*/ + + ref = builder->CreateAdd(ref, getInteger(32, 1)); + builder->CreateStore(ref, ref_addr); +} + +static void borrow_string(llvm::Value* ptr){ + gen_if(builder->CreateICmpNE(ptr, get_nullptr()), [&](){ + borrow_string_no_nullcheck(ptr); + }, "str_not_null", "string_borrow_done"); +} + +static void borrow_object_no_nullcheck(llvm::Value* ptr){ + llvm::Value* object_ptr = builder->CreateBitCast(ptr, pointer_t(OBJECT_type)); + llvm::Value* ref_addr = get_element_addr(object_ptr, 1); + llvm::Value* ref = builder->CreateLoad(ref_addr); + ref = builder->CreateAdd(ref, getInteger(TARGET_BITS, 1)); + builder->CreateStore(ref, ref_addr); + /*builder->CreateCall3(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)"%p %ld +\n", llvmType(getInt8Ty)), ptr, ref);*/ +} + +static void borrow_object(llvm::Value* ptr){ + gen_if(builder->CreateICmpNE(ptr, get_nullptr()), [&](){ + borrow_object_no_nullcheck(ptr); + }, "obj_not_null", "borrow_object_done"); +} + +static void borrow_variant(llvm::Value* val){ + builder->CreateCall2( + get_global_function(JR_borrow_variant, 'v', "jl"), + extract_value(val, 0), + extract_value(val, 1)); +} + +static void release(llvm::Value* val, TYPE type){ + if (TYPE_is_object(type)) + unref_object(extract_value(val, 1)); + else if (type == T_STRING){ + gen_if(builder->CreateICmpEQ(extract_value(val, 0), getInteger(TARGET_BITS, T_STRING)), [&](){ + unref_string(extract_value(val, 1)); + }, "release_T_STRING", "str_release_done"); + } else if (TYPE_is_variant(type)) + builder->CreateCall2( + get_global_function(JR_release_variant, 'v', "jl"), + extract_value(val, 0), + extract_value(val, 1)); +} + +static void borrow(llvm::Value* val, TYPE type){ + if (TYPE_is_object(type)) + borrow_object(extract_value(val, 1)); + else if (type == T_STRING){ + gen_if(builder->CreateICmpEQ(extract_value(val, 0), getInteger(TARGET_BITS, T_STRING)), [&](){ + borrow_string(extract_value(val, 1)); + }, "borrow_T_STRING", "str_borrow_done"); + } else if (TYPE_is_variant(type)) + borrow_variant(val); +} + +static void borrow_top(TYPE type){ + borrow(get_top(type), type); +} + +static void release_top(Expression* expr){ + if (TYPE_is_object(expr->type)){ + unref_object(extract_value(expr->codegen_get_value(), 1)); + } else if (expr->type == T_STRING){ + release(expr->codegen_get_value(), T_STRING); + } else if (TYPE_is_variant(expr->type)){ + expr->codegen(); + llvm::Value* free_variant_func = get_global_function_jif(EXEC_release, 'v', "jp"); + builder->CreateCall2(free_variant_func, getInteger(TARGET_BITS, T_VARIANT), get_value_on_top_addr()); + } else + expr->codegen(); + c_SP(-1); +} + +static void release_val(Expression* expr){ + if (TYPE_is_object(expr->type)){ + unref_object(extract_value(expr->codegen_get_value(), 1)); + } else if (TYPE_is_string(expr->type)){ + release(expr->codegen_get_value(), T_STRING); + } else if (TYPE_is_variant(expr->type)){ + expr->on_stack = true; + expr->codegen(); + llvm::Value* free_variant_func = get_global_function_jif(EXEC_release, 'v', "jp"); + builder->CreateCall2(free_variant_func, getInteger(TARGET_BITS, T_VARIANT), get_value_on_top_addr()); + c_SP(-1); + } else + expr->codegen(); +} + +///Convert a String to a pointer that can be put in an array or a Variant. +///If 'val' is a guaranteed to be T_CSTRING, set type to T_CSTRING, otherwise set it to T_STRING +static llvm::Value* string_for_array_or_variant(llvm::Value* val, TYPE type){ + llvm::Value* len = extract_value(val, 3); + return gen_if_phi(get_nullptr(), builder->CreateICmpNE(len, getInteger(32, 0)), [&](){ + llvm::Value* ptr = extract_value(val, 1); + llvm::Value* offset = extract_value(val, 2); + if (type == T_STRING){ + llvm::Value* is_t_string = builder->CreateICmpEQ(extract_value(val, 0), getInteger(TARGET_BITS, T_STRING)); + llvm::Value* test1 = gen_and_if(builder->CreateAnd( + is_t_string, + builder->CreateICmpEQ(offset, getInteger(32, 0))), [&](){ + llvm::Value* len_addr = builder->CreateGEP(ptr, getInteger(TARGET_BITS, -4)); + len_addr = builder->CreateBitCast(len_addr, llvmType(getInt32PtrTy)); + return builder->CreateICmpEQ(builder->CreateLoad(len_addr), len); + }); + return (llvm::Value*)gen_if_phi(ptr, builder->CreateXor(test1, getInteger(1, 1)), [&](){ + llvm::Value* newstr = builder->CreateCall2(get_global_function_jif(STRING_new, 'p', "pi"), builder->CreateGEP(ptr, to_target_int(offset)), len); + newstr = builder->CreateCall(get_global_function_jif(STRING_free_later, 'p', "p"), newstr); + gen_if(is_t_string, [&](){ + unref_string_no_nullcheck(ptr); + }); + borrow_string_no_nullcheck(newstr); + return newstr; + }); + } else { + llvm::Value* newstr = builder->CreateCall2(get_global_function_jif(STRING_new, 'p', "pi"), builder->CreateGEP(ptr, to_target_int(offset)), len); + newstr = builder->CreateCall(get_global_function_jif(STRING_free_later, 'p', "p"), newstr); + borrow_string_no_nullcheck(newstr); + return newstr; + } + }); +} + +///Transfers the ownership from val to the variable in memory +///The old data in memory is not released +static void variable_write(llvm::Value* addr, llvm::Value* val, TYPE type){ + if (type != T_BOOLEAN && type != T_STRING && type != T_CSTRING && !TYPE_is_object(type)) + addr = builder->CreateBitCast(addr, pointer_t(TYPE_llvm(type))); + /*if (type == T_BOOLEAN) + char* */ + switch(type){ + case T_VOID: + case T_NULL: + case T_CLASS: + case T_FUNCTION: + abort(); + case T_BOOLEAN: builder->CreateStore(builder->CreateSExt(val, llvmType(getInt8Ty)), addr); break; + case T_BYTE: + case T_SHORT: + case T_INTEGER: + case T_LONG: + case T_SINGLE: + case T_FLOAT: + case T_DATE: + case T_POINTER: + case T_VARIANT: + builder->CreateStore(val, addr); break; + case T_STRING: + case T_CSTRING: { + addr = builder->CreateBitCast(addr, charPP); + builder->CreateStore(string_for_array_or_variant(val, type), addr); + break; + } + default: { + builder->CreateStore(extract_value(val, 1), builder->CreateBitCast(addr, charPP)); + break; + } + } +} + +///Borrows the data +static llvm::Value* array_read(llvm::Value* addr, TYPE type){ + if (type != T_BOOLEAN && type != T_STRING && type != T_CSTRING && !TYPE_is_object(type)) + addr = builder->CreateBitCast(addr, pointer_t(TYPE_llvm(type))); + switch(type){ + case T_VOID: + case T_CSTRING: + case T_NULL: + case T_CLASS: + case T_FUNCTION: + abort(); + case T_BOOLEAN: return builder->CreateTrunc(builder->CreateLoad(addr), llvmType(getInt1Ty)); + case T_BYTE: + case T_SHORT: + case T_INTEGER: + case T_LONG: + case T_SINGLE: + case T_FLOAT: + case T_DATE: + case T_POINTER: + return builder->CreateLoad(addr); + case T_VARIANT: { + llvm::Value* ret = builder->CreateLoad(addr); + borrow_variant(ret); + return ret; + } + case T_STRING: { + llvm::Value* ptr = builder->CreateLoad(builder->CreateBitCast(addr, charPP)); + return gen_if_phi(get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)), + builder->CreateICmpNE(ptr, get_nullptr()), [&](){ + + llvm::Value* len_addr = builder->CreateGEP(ptr, getInteger(TARGET_BITS, -4)); + llvm::Value* len = builder->CreateLoad(builder->CreateBitCast(len_addr, llvmType(getInt32PtrTy))); + + borrow_string_no_nullcheck(ptr); + + return get_new_struct(string_type, getInteger(TARGET_BITS, T_STRING), ptr, getInteger(32, 0), len); + }); + } + default: { + llvm::Value* ret = builder->CreateLoad(builder->CreateBitCast(addr, charPP)); + borrow_object(ret); + return get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), ret); + } + } +} + +void DropExpression::codegen(){ + /*if (expr->on_stack){ + release_top(expr); + } else { + release_val(expr); + }*/ + if (CallExpression* ce = dyn_cast(expr)){ + if (ce->variant_call){ + ce->codegen_on_stack(); + llvm::Value* ret = get_value_on_top_addr(); + gen_if(builder->CreateICmpNE(load_element(ret, 0), getInteger(TARGET_BITS, 0)), [&](){ + release(ret_top_stack(T_VARIANT, true), T_VARIANT); + }); + c_SP(-1); + return; + } + } + release(expr->codegen_get_value(), expr->type); + if (expr->on_stack) + c_SP(-1); +} + +llvm::Value* PushCStringExpression::codegen_get_value(){ + llvm::Value* ret = get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), + builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(void*)addr), llvmType(getInt8PtrTy)), + getInteger(32, start), getInteger(32, len)); + if (on_stack) + push_value(ret, type); + return ret; +} + +void PushIntegerExpression::codegen(){ + if (on_stack) codegen_on_stack(); +} +llvm::Value* PushIntegerExpression::codegen_get_value(){ + llvm::Value* val = getInteger(bits, i); + if (on_stack) codegen_on_stack(); + return val; +} +void PushIntegerExpression::codegen_on_stack(){ + push_value(getInteger(bits, i), type); +} + +llvm::Value* PushFloatExpression::codegen_get_value(){ + llvm::Value* ret = type == T_SINGLE ? getFloat((float)val) : getFloat(val); + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* PushNullPointerExpression::codegen_get_value(){ + llvm::Value* ret = get_nullptr(); + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* PushVoidDateExpression::codegen_get_value(){ + llvm::Value* ret = get_new_struct(date_type, getInteger(32, 0), getInteger(32, 0)); + if (on_stack) + push_value(ret, T_DATE); + return ret; +} + +llvm::Value* PushNullExpression::codegen_get_value(){ + if (on_stack) + push_value(NULL, T_NULL); + return NULL; +} + +llvm::Value* PushVoidExpression::codegen_get_value(){ + if (on_stack) + push_value(NULL, T_VOID); + return NULL; +} + +llvm::Value* PushLastExpression::codegen_get_value(){ + llvm::Value* obj = read_global((void*)&EVENT_Last); + borrow_object(obj); + + llvm::Value* ret = get_new_struct(object_type, + builder->CreateIntToPtr(getInteger(TARGET_BITS, 16), llvmType(getInt8PtrTy)), obj); + + if (on_stack) + push_value(ret, T_OBJECT); + return ret; +} + +static llvm::Value* read_variable(TYPE type, char* addr){ + llvm::Value* ret; + if (type == T_BOOLEAN) + ret = builder->CreateTrunc(read_global(addr, llvmType(getInt8Ty)), llvmType(getInt1Ty)); + else if (type < T_STRING || type == T_POINTER) + ret = read_global(addr, TYPE_llvm(type)); + else if (type == T_STRING){ + llvm::Value* ad = read_global(addr); + ret = gen_if_phi(get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)), builder->CreateICmpNE(ad, get_nullptr()), [&](){ + borrow_string_no_nullcheck(ad); + + llvm::Value* len_addr = builder->CreateGEP(ad, getInteger(TARGET_BITS, -4)); + len_addr = builder->CreateBitCast(len_addr, llvmType(getInt32PtrTy)); + llvm::Value* len = builder->CreateLoad(len_addr); + + return get_new_struct(string_type, getInteger(TARGET_BITS, T_STRING), ad, getInteger(32, 0), len); + }); + } else if (type == T_CSTRING){ + llvm::Value* ad = read_global(addr); + ret = get_cstring_from_addr(ad); + } else if (TYPE_is_object(type)){ + llvm::Value* obj = read_global(addr); + ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), obj); + borrow_object(obj); + } else if (type == T_VARIANT){ //FIXME + ret = read_global(addr, variant_type); + ret = gen_if_else_phi(builder->CreateICmpEQ(extract_value(ret, 0), getInteger(TARGET_BITS, T_VOID)), [&](){ + return get_new_struct(variant_type, getInteger(TARGET_BITS, T_NULL)/*, extract_value(ret, 1)*/); + }, [&](){ + borrow_variant(ret); + return ret; + }, "Variant_T_VOID", "Variant_not_T_VOID", "Variant_T_VOID_done"); + } else abort(); + return ret; +} + +static llvm::Value* codegen_tc_array(CLASS* klass, llvm::Value* ref, int array_load_index, llvm::Value* start_addr, TYPE type){ + llvm::Value* obj = builder->CreateCall4(get_global_function_jif(CARRAY_create_static, 'p', "pppp"), + get_global((void*)klass, llvmType(getInt8Ty)), + ref, + get_global(klass->load->array[array_load_index], llvmType(getInt8Ty)), + start_addr); + + borrow_object_no_nullcheck(obj); + + return get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), obj); +} + +llvm::Value* ReadVariableExpression::codegen_get_value(){ + llvm::Value* ret; + if (ctype->id == TC_ARRAY){ + ret = codegen_tc_array(CP, get_global((void*)CP, llvmType(getInt8Ty)), ctype->value, get_global((void*)addr, llvmType(getInt8Ty)), type); + } else if (ctype->id == TC_STRUCT){ + ret = builder->CreateCall3(get_global_function_jif(CSTRUCT_create_static, 'p', "ppp"), + get_global((void*)klass, llvmType(getInt8Ty)), + builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), + get_global((void*)addr, llvmType(getInt8Ty))); + borrow_object_no_nullcheck(ret); + ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), ret); + } else { + ret = read_variable(type, addr); + } + if (on_stack) + push_value(ret, type); + return ret; +} + + +//FIXME this is almost same as array_read, only difference is in variant +static llvm::Value* read_variable(TYPE type, llvm::Value* addr){ + llvm::Value* ret; + if (type == T_BOOLEAN) + ret = builder->CreateTrunc(builder->CreateLoad(builder->CreateBitCast(addr, llvmType(getInt8PtrTy))), llvmType(getInt1Ty)); + else if (type < T_STRING || type == T_POINTER) + ret = builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(TYPE_llvm(type)))); + else if (type == T_STRING){ + llvm::Value* ad = builder->CreateLoad(builder->CreateBitCast(addr, charPP)); + ret = gen_if_phi(get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)), builder->CreateICmpNE(ad, get_nullptr()), [&](){ + borrow_string_no_nullcheck(ad); + + llvm::Value* len_addr = builder->CreateGEP(ad, getInteger(TARGET_BITS, -4)); + len_addr = builder->CreateBitCast(len_addr, llvmType(getInt32PtrTy)); + llvm::Value* len = builder->CreateLoad(len_addr); + + return get_new_struct(string_type, getInteger(TARGET_BITS, T_STRING), ad, getInteger(32, 0), len); + }); + } else if (type == T_CSTRING){ + llvm::Value* ad = builder->CreateLoad(builder->CreateBitCast(addr, charPP)); + ret = get_cstring_from_addr(ad); + } else if (TYPE_is_object(type)){ + llvm::Value* nobj = builder->CreateLoad(builder->CreateBitCast(addr, charPP)); + ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), nobj); + borrow_object(nobj); + } else if (type == T_VARIANT){ + ret = builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(variant_type))); + ret = gen_if_else_phi(builder->CreateICmpEQ(extract_value(ret, 0), getInteger(TARGET_BITS, T_VOID)), [&](){ + return get_new_struct(variant_type, getInteger(TARGET_BITS, T_NULL)/*, extract_value(ret, 1)*/); + }, [&](){ + borrow_variant(ret); + return ret; + }, "Variant_T_VOID", "Variant_not_T_VOID", "Variant_T_VOID_done"); + } else abort(); + return ret; + +} + +///Reading an instance variable +static llvm::Value* read_variable_offset(TYPE type, llvm::Value* obj, llvm::Value* offset){ + return read_variable(type, builder->CreateGEP(obj, offset)); +} + +static void release_variable(TYPE type, llvm::Value* addr){ + if (type == T_STRING){ + unref_string(builder->CreateLoad(builder->CreateBitCast(addr, charPP))); + } else if (TYPE_is_object(type)){ + unref_object(builder->CreateLoad(builder->CreateBitCast(addr, charPP))); + } else if (type == T_VARIANT){ + release(builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(variant_type))), T_VARIANT); + /*builder->CreateCall2(get_global_function(JR_release_variant, 'v', "jl"), + builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(LONG_TYPE))), + builder->CreateLoad(builder->CreateGEP(builder->CreateBitCast(addr, llvmType(getInt64PtrTy)), getInteger(TARGET_BITS, 1))) + );*/ + } +} + +static void write_variable_offset(TYPE type, llvm::Value* obj, int offset, llvm::Value* val){ + variable_write(builder->CreateGEP(obj, getInteger(TARGET_BITS, offset)), val, type); +} + +static void make_nullcheck(llvm::Value* addr){ + gen_if_noreturn(builder->CreateICmpEQ(addr, get_nullptr()), [&](){ + create_throw(E_NULL); + }, "is_null", "not_null"); +} + +template +static void make_nullcheck(llvm::Value* addr, T func_if_error){ + gen_if_noreturn(builder->CreateICmpEQ(addr, get_nullptr()), [&](){ + func_if_error(); + create_throw(E_NULL); + }, "is_null", "not_null"); +} + +static void make_double_nullcheck(llvm::Value* value){ + //FIXME: I think it is better to maybe make sure that an object on the stack actually + //is of type object (nullpointer or not), instead of T_NULL. + + llvm::Value* valtype = builder->CreatePtrToInt(extract_value(value, 0), LONG_TYPE); + + gen_if_noreturn(builder->CreateICmpEQ(valtype, getInteger(TARGET_BITS, T_NULL)), [&](){ + create_throw(E_NULL); + }); + + llvm::Value* object_ptr = extract_value(value, 1); + make_nullcheck(object_ptr); +} + +template +static void make_double_nullcheck(llvm::Value* value, T func_if_error){ + //FIXME: I think it is better to maybe make sure that an object on the stack actually + //is of type object (nullpointer or not), instead of T_NULL. + + llvm::Value* valtype = builder->CreatePtrToInt(extract_value(value, 0), LONG_TYPE); + + gen_if_noreturn(builder->CreateICmpEQ(valtype, getInteger(TARGET_BITS, T_NULL)), [&](){ + func_if_error(); + create_throw(E_NULL); + }); + + llvm::Value* object_ptr = extract_value(value, 1); + make_nullcheck(object_ptr, func_if_error); +} + +static llvm::Value* get_class_desc_entry(llvm::Value* object_ptr, int index){ + llvm::Value* class_ptr = load_element(builder->CreateBitCast(object_ptr, pointer_t(OBJECT_type)), 0); + llvm::Value* vtable_ptr = builder->CreateGEP(class_ptr, getInteger(TARGET_BITS, offsetof(CLASS, table))); + llvm::Value* vtable = builder->CreateLoad(builder->CreateBitCast(vtable_ptr, charPP)); + + llvm::Value* class_desc_ptr = builder->CreateGEP(vtable, getInteger(TARGET_BITS, index*sizeof(CLASS_DESC_SYMBOL)+offsetof(CLASS_DESC_SYMBOL, desc))); + llvm::Value* class_desc = builder->CreateLoad(builder->CreateBitCast(class_desc_ptr, charPP)); + return class_desc; +} + +static void create_check(CLASS* klass, llvm::Value* effective_class, llvm::Value* object){ + if (klass->must_check){ + //Assume klass->must_check only needs to be checked here, and not in the generated code + int offset_check = offsetof(CLASS, check)/sizeof(void*); //From CLASS structure in gbx_class.h + llvm::Value* check_func = builder->CreateLoad(builder->CreateGEP(builder->CreateBitCast(effective_class, charPP), getInteger(TARGET_BITS, offset_check))); + check_func = builder->CreateBitCast(check_func, pointer_t(get_function_type('i', "p"))); + gen_if_noreturn(builder->CreateICmpNE(builder->CreateCall(check_func, object), getInteger(32, 0)), [&](){ + + //static int counter = 0; + + //builder->CreateCall3(get_global_function_vararg(printf, 'v', "ppi"), + // get_global((void*)"Illegal: %p %d\n", llvmType(getInt8Ty)), object, getInteger(32, counter++)); + + create_throw(E_IOBJECT); + }, "illegal_object", "legal_object"); + } +} + +//Same body as PopStaticExpression ... +void PopStaticVariableExpression::codegen(){ + llvm::Value* value = val->codegen_get_value(); + llvm::Value* var_addr = get_global((void*)addr, llvmType(getInt8Ty)); + release_variable(type, var_addr); + variable_write(var_addr, value, type); + c_SP(-val->on_stack); +} + +void PushUnknownExpression::codegen_on_stack(){ + obj->codegen_on_stack(); + store_pc(pc); + builder->CreateCall(get_global_function_jif(EXEC_push_unknown, 'v', "h"), getInteger(16, 0)); +} + +llvm::Value* PushUnknownExpression::codegen_get_value(){ + codegen_on_stack(); + return ret_top_stack(T_VARIANT, true); +} + +llvm::Value* PushPureObjectVariableExpression::codegen_get_value(){ + if (isa(obj)){ + int offset = desc()->variable.offset; + llvm::Value* ret = read_variable_offset(type, current_op, getInteger(TARGET_BITS, offset)); + if (on_stack) + push_value(ret, type); + return ret; + } + llvm::Value* object = obj->codegen_get_value(); + make_double_nullcheck(object); + + llvm::Value* object_ptr = extract_value(object, 1); + + //We have a non-native object, so dispatch is not needed for effective class for checking + create_check(klass(), extract_value(object, 0), object_ptr); + + llvm::Value* desc_variable = get_class_desc_entry(object_ptr, index); + llvm::Value* offset = to_target_int(builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(desc_variable, getInteger(TARGET_BITS, offsetof(CLASS_DESC_VARIABLE, offset))), llvmType(getInt32PtrTy)))); + + llvm::Value* ret = read_variable_offset(type, object_ptr, offset); + + unref_object_no_nullcheck(object_ptr); + + c_SP(-obj->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type); + return ret; +} + +void PopPureObjectVariableExpression::codegen(){ + llvm::Value* value = val->codegen_get_value(); + llvm::Value* object = obj->codegen_get_value(); + make_double_nullcheck(object, [&](){ + release(value, val->type); + }); + + llvm::Value* object_ptr = extract_value(object, 1); + + //We have a non-native object, so dispatch is not needed for effective class for checking + create_check((CLASS*)(void*)obj->type, extract_value(object, 0), object_ptr); + + llvm::Value* desc_variable = get_class_desc_entry(object_ptr, index); + llvm::Value* offset = to_target_int(builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(desc_variable, getInteger(TARGET_BITS, offsetof(CLASS_DESC_VARIABLE, offset))), llvmType(getInt32PtrTy)))); + + llvm::Value* addr = builder->CreateGEP(object_ptr, offset); + + release_variable(type, addr); + variable_write(addr, value, type); + + unref_object_no_nullcheck(object_ptr); + + c_SP(-val->on_stack-obj->on_stack); +} + +llvm::Value* PushPureObjectConstantExpression::codegen_get_value(){ + llvm::Value* object = obj->codegen_get_value(); + if (obj->on_stack) + c_SP(-1); + + llvm::Value* object_ptr = extract_value(object, 1); + + make_nullcheck(object_ptr); + + llvm::Value* desc_const = get_class_desc_entry(object_ptr, index); + llvm::Value* ret; + + if (TYPE_is_string(type)){ + llvm::Value* translated = builder->CreateTrunc(builder->CreateLoad(builder->CreateGEP(desc_const, getInteger(TARGET_BITS, 2*sizeof(void*)+8))), llvmType(getInt1Ty)); + llvm::Value* straddr = builder->CreateGEP(desc_const, getInteger(TARGET_BITS, offsetof(CLASS_DESC_CONSTANT, value))); + straddr = builder->CreateLoad(builder->CreateBitCast(straddr, llvmType(getInt8PtrTy))); + straddr = gen_if_phi(straddr, translated, [&](){ + return builder->CreateCall(get_global_function(GB.Translate, 'p', "p"), straddr); + }); + llvm::Value* len = builder->CreateCall(get_global_function(strlen, 'j', "p"), straddr); + if (TARGET_BITS == 64) + len = builder->CreateTrunc(len, llvmType(getInt32Ty)); + ret = get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), straddr, getInteger(32, 0), len); + } else { + llvm::Type* t = TYPE_llvm(type); + llvm::Value* addr = builder->CreateGEP(desc_const, getInteger(TARGET_BITS, offsetof(CLASS_DESC_CONSTANT, value))); + ret = builder->CreateLoad(builder->CreateBitCast(addr, pointer_t(t))); + } + + unref_object_no_nullcheck(object_ptr); + + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* PushPureObjectPropertyExpression::codegen_private(bool get_value){ + obj->codegen_on_stack(); + + llvm::Value* object = ret_top_stack(obj->type, true); + + bool super = isa(obj); + + if (!super) + make_double_nullcheck(object); + + llvm::Value* object_ptr = extract_value(object, 1); + + llvm::Value* class_desc_property; + llvm::Value* is_native; + + if (super){ + is_native = getInteger(1, klass()->is_native); + } else { + class_desc_property = get_class_desc_entry(object_ptr, index); + if (!klass()->table[index].desc->property.native) + is_native = getInteger(1, false); //Assume no children can be native either + else + is_native = builder->CreateTrunc(builder->CreateLoad(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, 4*TARGET_BITS/8))), llvmType(getInt1Ty)); + } + + //FIXME dispatch: + create_check(klass(), extract_value(object, 0), object_ptr); + + llvm::Value* ret = gen_if_else_phi(is_native, [&](){ + llvm::Value* read = super ? get_global((void*)this->desc()->property.read, llvmType(getInt8Ty)) : builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, offsetof(CLASS_DESC_PROPERTY, read))), charPP)); + + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"), + read, + object_ptr, + getInteger(TARGET_BITS, type), + get_nullptr()); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){ + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + + llvm::Value* ret = read_value(get_global(&TEMP, value_type), type); + borrow(ret, type); + return ret; + }, [&](){ + llvm::Value* read = super ? (llvm::Value*)getInteger(32, (int)(intptr_t)this->desc()->property.read) : (llvm::Value*)builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, offsetof(CLASS_DESC_PROPERTY, read))), llvmType(getInt32PtrTy))); + llvm::Value* klass = super ? builder->CreateIntToPtr(getInteger(TARGET_BITS, obj->type), llvmType(getInt8PtrTy)) : builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, offsetof(CLASS_DESC_PROPERTY, klass))), charPP)); + + builder->CreateStore(klass, get_global((void*)&EXEC.klass)); + builder->CreateStore(object_ptr, get_global((void*)&EXEC.object)); + builder->CreateStore(getInteger(32, 0), get_global((void*)&EXEC.nparam, llvmType(getInt32Ty))); + builder->CreateStore(read, get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + + /*builder->CreateCall2(get_global_function_vararg(printf, 'v', "pi"), + get_global((void*)"%d\n", llvmType(getInt8Ty)), getInteger(32, 1));*/ + + builder->CreateCall(get_global_function_jif(EXEC_function_real, 'v', "")); + + llvm::Value* ret = read_value(get_global(RP, value_type), type); + builder->CreateStore(getInteger(TARGET_BITS, T_VOID), get_global(RP, LONG_TYPE)); + return ret; + }, "property_native", "property_non_native", "property_read_done"); + + unref_object_no_nullcheck(object_ptr); + + if (get_value && !on_stack){ + c_SP(-1); + return ret; + } else { + store_value(get_value_on_top_addr(), ret, type); + return ret; + } +} + +void PopPureObjectPropertyExpression::codegen(){ + llvm::Value* v = val->codegen_get_value(); + obj->codegen_on_stack(); + + llvm::Value* object = ret_top_stack(obj->type, true); + + bool super = isa(obj); + + if (!super) + make_double_nullcheck(object); + + llvm::Value* object_ptr = extract_value(object, 1); + + llvm::Value* class_desc_property; + llvm::Value* is_native; + + if (super){ + is_native = getInteger(1, ((CLASS*)(void*)obj->type)->is_native); + } else { + CLASS* klass = (CLASS*)(void*)obj->type; + class_desc_property = get_class_desc_entry(object_ptr, index); + if (!klass->table[index].desc->property.native) + is_native = getInteger(1, false); //Assume no children can be native either + else + is_native = builder->CreateTrunc(builder->CreateLoad(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, 4*TARGET_BITS/8))), llvmType(getInt1Ty)); + } + + //FIXME dispatch + create_check((CLASS*)(void*)obj->type, extract_value(object, 0), object_ptr); + + gen_if_else(is_native, [&](){ + llvm::Value* write = super ? get_global((void*)this->desc()->property.write, llvmType(getInt8Ty)) : builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, offsetof(CLASS_DESC_PROPERTY, write))), charPP)); + + llvm::Value* val_ptr = builder->CreateBitCast(builder->CreateGEP(read_sp(), getInteger(TARGET_BITS, -2)), llvmType(getInt8PtrTy)); + + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"), + write, + object_ptr, + getInteger(TARGET_BITS, 0), + val_ptr); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){ + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + release(v, val->type); + }, [&](){ + push_value(v, val->type); + llvm::Value* val_ptr = builder->CreateBitCast(builder->CreateGEP(read_sp(), getInteger(TARGET_BITS, -3)), pointer_t(LONG_TYPE)); + builder->CreateStore(getInteger(TARGET_BITS, T_VOID), val_ptr); + + llvm::Value* write = super ? (llvm::Value*)getInteger(32, (int)(intptr_t)this->desc()->property.write) : builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, offsetof(CLASS_DESC_PROPERTY, write))), llvmType(getInt32PtrTy))); + llvm::Value* klass = super ? builder->CreateIntToPtr(getInteger(TARGET_BITS, obj->type), llvmType(getInt8PtrTy)) : builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(class_desc_property, getInteger(TARGET_BITS, offsetof(CLASS_DESC_PROPERTY, klass))), charPP)); + + builder->CreateStore(klass, get_global((void*)&EXEC.klass)); + builder->CreateStore(object_ptr, get_global((void*)&EXEC.object)); + builder->CreateStore(getInteger(32, 1), get_global((void*)&EXEC.nparam, llvmType(getInt32Ty))); + builder->CreateStore(write, get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + + /*builder->CreateCall5(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)"%d %p %p %d\n", llvmType(getInt8Ty)), getInteger(32, 2), klass, class_desc_property, getInteger(32, index));*/ + + builder->CreateCall(get_global_function_jif(EXEC_function_real, 'v', "")); + //I think the release of RP is not needed, it should already be T_VOID... + }, "property_native", "property_non_native", "property_write_done"); + + unref_object_no_nullcheck(object_ptr); + c_SP(-2); +} + +void PopVirtualPropertyExpression::codegen(){ + llvm::Value* v = val->codegen_get_value(); + llvm::Value* object = obj->codegen_get_value(); + llvm::Value* object_ptr = extract_value(object, 1); + + if (is_static){ + //Only allowed on virtual classes + //Assume it returned a T_CLASS, or threw an error... + + gen_if_noreturn(builder->CreateICmpNE(load_element(get_value_on_top_addr(), 0), getInteger(TARGET_BITS, T_CLASS)), [&](){ + create_throw(E_STATIC, ((CLASS*)(void*)obj->type)->name, this->name); + }); + + object_ptr = get_nullptr(); + } else + create_check((CLASS*)(void*)obj->type, get_global((void*)obj->type, llvmType(getInt8Ty)), object_ptr); + + llvm::Value* write = get_global((void*)desc()->property.write, llvmType(getInt8Ty)); + llvm::Value* val_ptr = builder->CreateBitCast(builder->CreateGEP(read_sp(), getInteger(TARGET_BITS, -2)), llvmType(getInt8PtrTy)); + + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"), + write, + object_ptr, + getInteger(TARGET_BITS, 0), + val_ptr); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){ + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + + release(v, val->type); + if (!is_static) + unref_object_no_nullcheck(object_ptr); + c_SP(-2); +} + +llvm::Value* PushVirtualPropertyExpression::codegen_private(bool get_value){ + obj->codegen_on_stack(); + + llvm::Value* object = ret_top_stack(obj->type, true); + + //Might be T_NULL or T_CLASS or the virtual class: + llvm::Value* valtype = builder->CreatePtrToInt(extract_value(object, 0), LONG_TYPE); + + gen_if_noreturn(builder->CreateICmpEQ(valtype, getInteger(TARGET_BITS, T_NULL)), [&](){ + create_throw(E_NULL); + }); + + //T_CLASS could be resolved at compile time, since only gb.db returns a (virtual) object + //when invoking a static read property/static method... + + llvm::Value* object_ptr = builder->CreateSelect( + builder->CreateICmpEQ(valtype, getInteger(TARGET_BITS, T_CLASS)), + get_nullptr(), + extract_value(object, 1)); + + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"), + get_global((void*)desc()->property.read, llvmType(getInt8Ty)), //Assume no override by inheritance + object_ptr, + getInteger(TARGET_BITS, type), + get_nullptr()); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){ + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + + llvm::Value* ret = read_value(get_global(&TEMP, value_type), type); + borrow(ret, type); + + unref_object(object_ptr); + + if (get_value && !on_stack){ + c_SP(-1); + return ret; + } else { + store_value(get_value_on_top_addr(), ret, type); + return ret; + } +} + +llvm::Value* PushPureObjectStaticPropertyExpression::codegen_private(bool get_value){ + obj->codegen_on_stack(); + + //Assume it returned a T_CLASS, or threw an error... + + gen_if_noreturn(builder->CreateICmpNE(load_element(get_value_on_top_addr(), 0), getInteger(TARGET_BITS, T_CLASS)), [&](){ + create_throw(E_STATIC, this->klass()->name, this->name); + }); + + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"), + get_global((void*)desc()->property.read, llvmType(getInt8Ty)), //Assume no override by inheritance + get_nullptr(), + getInteger(TARGET_BITS, type), + get_nullptr()); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){ + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + + llvm::Value* ret = read_value(get_global(&TEMP, value_type), type); + borrow(ret, type); + + if (get_value && !on_stack){ + c_SP(-1); + return ret; + } else { + store_value(get_value_on_top_addr(), ret, type); + return ret; + } +} + +llvm::Value* PushStaticPropertyExpression::codegen_get_value(){ + CLASS* c = ((PushClassExpression*)obj)->klass; + CLASS_DESC* desc = c->table[index].desc; + + llvm::Value* ret; + + if (desc->property.native){ + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"), + get_global((void*)desc->property.read, llvmType(getInt8Ty)), + get_nullptr(), + getInteger(TARGET_BITS, type), + get_nullptr()); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){ + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + + ret = read_value(get_global(&TEMP, value_type), type); + borrow(ret, type); + } else { + builder->CreateStore(get_global((void*)desc->property.klass, llvmType(getInt8Ty)), get_global((void*)&EXEC.klass)); + builder->CreateStore(get_nullptr(), get_global((void*)&EXEC.object)); + builder->CreateStore(getInteger(32, 0), get_global((void*)&EXEC.nparam, llvmType(getInt32Ty))); + builder->CreateStore(getInteger(32, (int)(intptr_t)desc->property.read), get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + + /*builder->CreateCall2(get_global_function_vararg(printf, 'v', "pi"), + get_global((void*)"%d\n", llvmType(getInt8Ty)), getInteger(32, 3));*/ + + builder->CreateCall(get_global_function_jif(EXEC_function_real, 'v', "")); + + ret = read_value(get_global(RP, value_type), type); + builder->CreateStore(getInteger(TARGET_BITS, T_VOID), get_global(RP, LONG_TYPE)); + } + + if (on_stack) + push_value(ret, type); + return ret; +} + +void PopStaticPropertyExpression::codegen(){ + llvm::Value* value = val->codegen_get_value(); + + CLASS_DESC* desc = klass->table[index].desc; + + if (desc->property.native){ + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"), + get_global((void*)desc->property.write, llvmType(getInt8Ty)), + get_nullptr(), + getInteger(TARGET_BITS, type), + builder->CreateBitCast(get_value_on_top_addr(), llvmType(getInt8PtrTy))); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){ + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + + release(value, type); + c_SP(-val->on_stack); + } else { + builder->CreateStore(get_global((void*)desc->property.klass, llvmType(getInt8Ty)), get_global((void*)&EXEC.klass)); + builder->CreateStore(get_nullptr(), get_global((void*)&EXEC.object)); + builder->CreateStore(getInteger(32, 1), get_global((void*)&EXEC.nparam, llvmType(getInt32Ty))); + builder->CreateStore(getInteger(32, (int)(intptr_t)desc->property.write), get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + + /*builder->CreateCall2(get_global_function_vararg(printf, 'v', "pi"), + get_global((void*)"%d\n", llvmType(getInt8Ty)), getInteger(32, 4));*/ + + builder->CreateCall(get_global_function_jif(EXEC_function_real, 'v', "")); + } +} + +void PopUnknownPropertyUnknownExpression::codegen(){ + val->codegen_on_stack(); + + llvm::Value* effective_class; + llvm::Value* object; + + if (PushClassExpression* pce = dyn_cast(obj)){ + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)pce->klass), llvmType(getInt8PtrTy)); + object = get_nullptr(); + push_value(NULL, T_VOID); + } else { + llvm::Value* ob = obj->codegen_get_value(); + object = extract_value(ob, 1); + CLASS* k = (CLASS*)(void*)obj->type; + + if (isa(obj)){ + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy)); + } else if (!k->is_virtual){ + make_nullcheck(object); + effective_class = load_element(builder->CreateBitCast(object, pointer_t(OBJECT_type)), 0); + } else { + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy)); + } + + create_check(k, effective_class, object); + } + + builder->CreateCall3(get_global_function(JR_pop_unknown_property_unknown, 'v', "ppj"), + effective_class, object, getInteger(TARGET_BITS, (uint64_t)(void*)name)); +} + +void PopUnknownExpression::codegen(){ + val->codegen_on_stack(); + obj->codegen_on_stack(); + store_pc(pc); + builder->CreateCall(get_global_function_jif(EXEC_pop_unknown, 'v', "")); +} + +llvm::Value* PushDynamicExpression::codegen_get_value(){ + llvm::Value* ret; + if (ctype->id == TC_ARRAY){ + /*llvm::Value* obj = builder->CreateCall4(get_global_function_jif(CARRAY_create_static, 'p', "pppp"), + get_global((void*)CP, llvmType(getInt8Ty)), + current_op, + get_global(CP->load->array[ctype->value], llvmType(getInt8Ty)), + builder->CreateGEP(current_op, getInteger(TARGET_BITS, offset))); + + borrow_object_no_nullcheck(obj); + + ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), obj);*/ + ret = codegen_tc_array(CP, current_op, ctype->value, builder->CreateGEP(current_op, getInteger(TARGET_BITS, offset)), type); + } else if (ctype->id == TC_STRUCT){ + ret = builder->CreateCall3(get_global_function_jif(CSTRUCT_create_static, 'p', "ppp"), + get_global((void*)CP, llvmType(getInt8Ty)), + builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), + builder->CreateGEP(current_op, getInteger(TARGET_BITS, offset))); + borrow_object_no_nullcheck(ret); + return get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), ret); + } else { + ret = read_variable_offset(type, current_op, getInteger(TARGET_BITS, offset)); + } + if (on_stack) + push_value(ret, type); + return ret; +} + +void PopDynamicExpression::codegen(){ + llvm::Value* new_val = val->codegen_get_value(); + c_SP(-val->on_stack); + release_variable(type, builder->CreateGEP(current_op, getInteger(TARGET_BITS, offset))); + write_variable_offset(type, current_op, offset, new_val); +} + +//Same body as PopStaticVariableExpression ... +void PopStaticExpression::codegen(){ + llvm::Value* new_val = val->codegen_get_value(); + llvm::Value* ad = get_global((void*)addr, llvmType(getInt8Ty)); + release_variable(type, ad); + variable_write(ad, new_val, type); + c_SP(-val->on_stack); +} + +llvm::Value* PushFunctionExpression::codegen_get_value(){ + llvm::Value* func = insert_value(get_new_struct(function_type), current_op, 1); + borrow_object(current_op); + push_value(func, T_FUNCTION); + return func; +} +void PushFunctionExpression::codegen_on_stack(){ + //llvm::Value* func = get_new_struct(function_type, read_global((void*)&CP), current_op, getInteger(8, FUNCTION_PRIVATE), getInteger(8, true), getInteger(16, index)); + llvm::Value* func = insert_value(get_new_struct(function_type), current_op, 1); + borrow_object(current_op); + push_value(func, T_FUNCTION); +} + +void PushClassExpression::codegen_on_stack(){ + llvm::Value* sp = read_global((void*)&SP, pointer_t(value_types[T_CLASS])); + store_element(sp, 0, getInteger(TARGET_BITS, T_CLASS)); + store_element(sp, 1, get_global((void*)klass, llvmType(getInt8Ty))); + c_SP(1); +} + +llvm::Value* AddQuickExpression::codegen_get_value(){ + if (type == T_VARIANT){ + expr->codegen_on_stack(); + builder->CreateCall(get_global_function(JR_aq_variant, 'v', "i"), getInteger(32, add)); + return ret_top_stack(T_VARIANT, on_stack); + } + llvm::Value* orig = expr->codegen_get_value(); + if (expr->on_stack) c_SP(-1); + + llvm::Value* new_val; + switch(type){ + case T_BYTE: new_val = builder->CreateAdd(orig, getInteger(8, add)); break; + case T_SHORT: new_val = builder->CreateAdd(orig, getInteger(16, add)); break; + case T_INTEGER: new_val = builder->CreateAdd(orig, getInteger(32, add)); break; + case T_LONG: new_val = builder->CreateAdd(orig, getInteger(64, add)); break; + case T_SINGLE: new_val = builder->CreateFAdd(orig, getFloat((float)add)); break; + case T_FLOAT: new_val = builder->CreateFAdd(orig, getFloat((double)add)); break; + case T_POINTER: new_val = builder->CreateGEP(orig, getInteger(TARGET_BITS, add)); break; + default: __builtin_unreachable(); + } + if (on_stack) + push_value(new_val, type); + return new_val; +} + +llvm::Value* PushMeExpression::codegen_get_value(){ + //llvm::Value* ret = get_new_struct(object_type, read_global((void*)CP), current_op); + llvm::Value* ret = get_new_struct(object_type, get_global((void*)CP, llvmType(getInt8Ty)), current_op); + borrow_object_no_nullcheck(current_op); + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* PushSuperExpression::codegen_get_value(){ + llvm::Value* ret = get_new_struct(object_type, get_global((void*)type, llvmType(getInt8Ty)), current_op); + borrow_object_no_nullcheck(current_op); + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* PushLocalExpression::codegen_get_value(){ + llvm::Value* ret = builder->CreateLoad(locals[index]); + if (on_stack) + push_value(ret, type); + if (no_ref_variant == false) + borrow(ret, type); + return ret; +} +void PushLocalExpression::codegen_on_stack(){ + llvm::Value* ret = builder->CreateLoad(locals[index]); + push_value(ret, type); + + borrow(ret, type); //Is this better optimized? + //borrow_top(type); +} + +llvm::Value* PushParamExpression::codegen_get_value(){ + llvm::Value* ret = builder->CreateLoad(params[index + FP->n_param]); + if (on_stack) + push_value(ret, type); + borrow(ret, type); + return ret; +} +void PushParamExpression::codegen_on_stack(){ + llvm::Value* ret = builder->CreateLoad(params[index + FP->n_param]); + push_value(ret, type); + borrow(ret, type); + //borrow_top(type); +} + +void PopLocalExpression::codegen(){ + llvm::Value* v = val->codegen_get_value(); + if (val->on_stack) c_SP(-1); + llvm::Value* old_val = builder->CreateLoad(locals[index]); + release(old_val, type); + builder->CreateStore(v, locals[index]); + if (type == T_STRING || (type == T_VARIANT && !val->no_ref_variant) || TYPE_is_object(type)){ + if (TYPE_is_object(type)){ + //codegen_printf("%p\n", extract_value(v, 0)); + } + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type, false); + } else if (type == T_VARIANT && val->no_ref_variant){ + llvm::Value* vtype = extract_value(old_val, 0); + llvm::Value* is_str = builder->CreateICmpEQ(vtype, getInteger(TARGET_BITS, T_STRING)); + llvm::Value* is_obj = builder->CreateICmpUGE(vtype, getInteger(TARGET_BITS, T_OBJECT)); + gen_if(builder->CreateOr(is_str, is_obj), [&](){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, get_default(T_VARIANT), T_VARIANT, false); + }); + } +} + +void PopParamExpression::codegen(){ + llvm::Value* v = val->codegen_get_value(); + if (val->on_stack) c_SP(-1); + llvm::Value* old_val = builder->CreateLoad(params[index + FP->n_param]); + release(old_val, type); + builder->CreateStore(v, params[index + FP->n_param]); + if (type == T_STRING || (type == T_VARIANT && !val->no_ref_variant) || TYPE_is_object(type)){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&PP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type, false); + } else if (type == T_VARIANT && val->no_ref_variant){ + llvm::Value* vtype = extract_value(old_val, 0); + llvm::Value* is_str = builder->CreateICmpEQ(vtype, getInteger(TARGET_BITS, T_STRING)); + llvm::Value* is_obj = builder->CreateICmpUGE(vtype, getInteger(TARGET_BITS, T_OBJECT)); + gen_if(builder->CreateOr(is_str, is_obj), [&](){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&PP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, get_default(T_VARIANT), T_VARIANT, false); + }); + } +} + +static llvm::AllocaInst* create_alloca_in_entry(llvm::Type* t){ + llvm::BasicBlock* bb = builder->GetInsertBlock(); + builder->SetInsertPoint(entry_block, entry_block->begin()); + llvm::AllocaInst* ret = builder->CreateAlloca(t); + builder->SetInsertPoint(bb); + return ret; +} + +static llvm::AllocaInst* create_alloca_in_entry_init_default(llvm::Type* t, TYPE type){ + llvm::BasicBlock* bb = builder->GetInsertBlock(); + builder->SetInsertPoint(entry_block, entry_block->begin()); + llvm::AllocaInst* ret = builder->CreateAlloca(t); + store_default(ret, type); + builder->SetInsertPoint(bb); + return ret; +} + +static llvm::Value* release_ctrl(int index){ + llvm::Value* old_type = builder->CreateLoad(current_ctrl_types[index - FP->n_local]); + if (is_ctrl_type_used(1, index)){ + gen_if(builder->CreateICmpEQ(old_type, getInteger(32, 1)), [&](){ + release(builder->CreateLoad(ctrl_values[index - FP->n_local][1]), T_STRING); + }, "was_string_ctrl_before"); + } + if (is_ctrl_type_used(2, index)){ + gen_if(builder->CreateICmpEQ(old_type, getInteger(32, 2)), [&](){ + release(builder->CreateLoad(ctrl_values[index - FP->n_local][2]), T_OBJECT); + }, "was_object_ctrl_before"); + } + if (is_ctrl_type_used(3, index)){ + gen_if(builder->CreateICmpEQ(old_type, getInteger(32, 3)), [&](){ + release(builder->CreateLoad(ctrl_values[index - FP->n_local][3]), T_VARIANT); + }, "was_variant_ctrl_before"); + } + return old_type; +} + +static void set_ctrl(llvm::Value* v, TYPE type, int index){ + llvm::Value* old_type = release_ctrl(index); + + int type_next = special_ctrl_type(type); + builder->CreateStore(getInteger(32, type_next), current_ctrl_types[index - FP->n_local]); + set_ctrl_type(type, index); + + if (type_next == 0){ + if (locals[index] == NULL || locals[index]->getType() != TYPE_llvm(type)) + locals[index] = create_alloca_in_entry_init_default(TYPE_llvm(type), type); + } else { + locals[index] = ctrl_values[index - FP->n_local][type_next]; + } + + if (type == T_STRING || type == T_VARIANT || TYPE_is_object(type)){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type); + } else { + gen_if(builder->CreateICmpNE(old_type, getInteger(32, 0)), [&](){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type); + }, "old_ctrl_needs_to_be_cleaned2"); + } + + if (type != T_NULL) + builder->CreateStore(v, locals[index]); +#if 0 + TYPE old_ctrl_type = get_ctrl_type(index); + set_ctrl_type(type, index); + + if (old_ctrl_type != T_VOID && old_ctrl_type != T_NULL) + release(builder->CreateLoad(locals[index]), old_ctrl_type); + if (type == T_NULL) return; + if (old_ctrl_type != type){ + locals[index] = create_alloca_in_entry_init_default(TYPE_llvm(type), type); + } + builder->CreateStore(v, locals[index]); + if (old_ctrl_type == T_STRING || old_ctrl_type == T_VARIANT || TYPE_is_object(old_ctrl_type) || + type == T_STRING || type == T_VARIANT || TYPE_is_object(type)){ + + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type); + } +#endif + /*builder->CreateStore(v, locals[index]); + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type);*/ +} + +static void codegen_pop_ctrl(llvm::Value* v, Expression* val, int index){ + TYPE type = val->type; + + llvm::Value* old_type = release_ctrl(index); + + int type_next = special_ctrl_type(type); + builder->CreateStore(getInteger(32, type_next), current_ctrl_types[index - FP->n_local]); + set_ctrl_type(type, index); + + if (type_next == 0){ + if (locals[index] == NULL || locals[index]->getType() != TYPE_llvm(type)) + locals[index] = create_alloca_in_entry_init_default(TYPE_llvm(type), type); + } else { + locals[index] = ctrl_values[index - FP->n_local][type_next]; + } + + if (type == T_STRING || (type == T_VARIANT && !val->no_ref_variant) || TYPE_is_object(type)){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type); + } else { + gen_if(builder->CreateICmpNE(old_type, getInteger(32, 0)), [&](){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type); + }, "old_ctrl_needs_to_be_cleaned3"); + } + + if (val->on_stack) c_SP(-1); + + if (type != T_NULL) + builder->CreateStore(v, locals[index]); + + /* + // + TYPE type = val->type; + + TYPE old_ctrl_type = get_ctrl_type(index); + set_ctrl_type(type, index); + + if (val->on_stack) c_SP(-1); + if (old_ctrl_type != T_VOID && old_ctrl_type != T_NULL) + release(builder->CreateLoad(locals[index]), old_ctrl_type); + if (type == T_NULL) return; + if (old_ctrl_type != type){ + locals[index] = create_alloca_in_entry_init_default(TYPE_llvm(type), type); + } + builder->CreateStore(v, locals[index]); + if (old_ctrl_type == T_STRING || old_ctrl_type == T_VARIANT || TYPE_is_object(old_ctrl_type) || + type == T_STRING || (type == T_VARIANT && !val->no_ref_variant) || TYPE_is_object(type)){ + + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type); + } + */ +} + +static void codegen_pop_ctrl(Expression* val, int index){ + codegen_pop_ctrl(val->codegen_get_value(), val, index); + return; + + /*TYPE type = val->type; + + TYPE old_ctrl_type = get_ctrl_type(index); + set_ctrl_type(type, index); + + llvm::Value* v = val->codegen_get_value(); + if (val->on_stack) c_SP(-1); + if (old_ctrl_type != T_VOID && old_ctrl_type != T_NULL) + release(builder->CreateLoad(locals[index]), old_ctrl_type); + if (type == T_NULL) return; + if (old_ctrl_type != type){ + locals[index] = create_alloca_in_entry_init_default(TYPE_llvm(type), type); + } + builder->CreateStore(v, locals[index]); + if (old_ctrl_type == T_STRING || old_ctrl_type == T_VARIANT || TYPE_is_object(old_ctrl_type) || + type == T_STRING || (type == T_VARIANT && !val->no_ref_variant) || TYPE_is_object(type)){ + + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, index*sizeof(VALUE))); + store_value(stack_addr, v, type); + }*/ +} + +//Same code as PopLocalExpression::codegen +void PopCtrlExpression::codegen(){ + if (!isa(val)) + codegen_pop_ctrl(val, index); +} + +void PopOptionalExpression::codegen(){ + llvm::Value* addr = builder->CreateBitCast(builder->CreateGEP(read_global(&PP), getInteger(TARGET_BITS, index*sizeof(VALUE))), pointer_t(LONG_TYPE)); + llvm::Value* t = builder->CreateLoad(addr); + gen_if(builder->CreateICmpEQ(t, getInteger(TARGET_BITS, T_VOID)), [&](){ + if (is_default){ + store_default(params[FP->n_param + index], type); + } else { + llvm::Value* value = val->codegen_get_value(); + store_value(addr, value, type); + builder->CreateStore(value, params[FP->n_param + index]); + if (val->on_stack) c_SP(-1); + } + }, "not_passed", "passed_or_done"); +} + +llvm::Value* PushAutoCreateExpression::codegen_get_value(){ + llvm::Value* ret = builder->CreateCall2(get_global_function(GB.AutoCreate, 'p', "pi"), + get_global((void*)klass, llvmType(getInt8Ty)), getInteger(32, 0)); + + borrow_object_no_nullcheck(ret); + + ret = get_new_struct(object_type, + get_global((void*)klass, llvmType(getInt8Ty)), ret); + + if (on_stack) + push_value(ret, type); + return ret; +} + +void PushPureObjectUnknownExpression::codegen_on_stack(){ + llvm::Value* ob = obj->codegen_get_value(); + llvm::Value* object = extract_value(ob, 1); + CLASS* k = (CLASS*)(void*)obj->type; + llvm::Value* effective_class; + + if (isa(obj)){ + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy)); + } else if (!k->is_virtual){ + make_nullcheck(object); + effective_class = load_element(builder->CreateBitCast(object, pointer_t(OBJECT_type)), 0); + } else { + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy)); + } + + create_check(k, effective_class, object); + + /*llvm::Value* func = insert_value(get_new_struct(function_type), object, 1);*/ + + builder->CreateCall4(get_global_function(JR_push_unknown_property_unknown, 'v', "pipp"), + builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(void*)name), llvmType(getInt8PtrTy)), + getInteger(32, name_id), + effective_class, + object); +} + +llvm::Value* PushPureObjectUnknownExpression::codegen_get_value(){ + codegen_on_stack(); + return ret_top_stack(T_VARIANT, true); +} + +void PushStaticUnknownExpression::codegen_on_stack(){ + builder->CreateCall4(get_global_function(JR_push_unknown_property_unknown, 'v', "pipp"), + builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(void*)name), llvmType(getInt8PtrTy)), + getInteger(32, name_id), + builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(void*)klass), llvmType(getInt8PtrTy)), + get_nullptr()); +} + +llvm::Value* PushStaticUnknownExpression::codegen_get_value(){ + codegen_on_stack(); + return ret_top_stack(T_VARIANT, true); +} + +llvm::Value* PushPureObjectFunctionExpression::codegen_get_value(){ + llvm::Value* ob = obj->codegen_get_value(); + llvm::Value* object = extract_value(ob, 1); + CLASS* k = klass(); + CLASS_DESC* d = desc(); + + if (isa(obj)){ + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy)); + } else if (!k->is_virtual){ + make_nullcheck(object); + effective_class = load_element(builder->CreateBitCast(object, pointer_t(OBJECT_type)), 0); + } else { + //FIXME Doesn't PushVirtualFunctionExpression take care of this? + //CLASS_load(k); + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy)); + } + + create_check(k, effective_class, object); + + llvm::Value* func = insert_value(get_new_struct(function_type), object, 1); + + if (!d->method.native){ + //Assume native classes don't inherit non-native ones :) + } else { + int offset_table = TARGET_BITS == 64 ? 40/8 : 28/4; + int offset_desc_in_desc_symbol = TARGET_BITS == 64 ? 12 : 8; + int offset_native_flag = TARGET_BITS == 64 ? 35 : 19; + //table_addr = (char*)effective_class->table + llvm::Value* table_addr = builder->CreateLoad(builder->CreateGEP(builder->CreateBitCast(effective_class, pointer_t(llvmType(getInt8PtrTy))), getInteger(TARGET_BITS, offset_table))); + //desc_addr_addr = (char*)(table_addr + sizeof(CLASS_DESC_SYMBOL) * index + desc_offset) + llvm::Value* desc_addr_addr = builder->CreateGEP(table_addr, getInteger(TARGET_BITS, sizeof(CLASS_DESC_SYMBOL) * index + offset_desc_in_desc_symbol)); + //desc_addr = *(char**)desc_addr_addr + llvm::Value* desc_addr = builder->CreateLoad(builder->CreateBitCast(desc_addr_addr, pointer_t(llvmType(getInt8PtrTy)))); + //native_flag_addr = (char*)(desc_addr + offset_native_flag) + llvm::Value* native_flag_addr = builder->CreateGEP(desc_addr, getInteger(TARGET_BITS, offset_native_flag)); + //native_subr_flag = *native_flag_addr + llvm::Value* native_subr_flag = builder->CreateLoad(native_flag_addr); + //native_flag = (bool)(native_subr_flag & 1) + llvm::Value* native_flag = builder->CreateTrunc(native_subr_flag, llvmType(getInt1Ty)); + //subr_flag = (bool)(native_subr_flag & 2) + //llvm::Value* subr_flag = builder->CreateICmpNE(builder->CreateAnd(native_subr_flag, getInteger(8, 2)), getInteger(8, 0)); + + //llvm::Value* function_kind = builder->CreateSelect(native_flag, builder->CreateSelect(subr_flag, getInteger(8, FUNCTION_SUBR), getInteger(8, FUNCTION_NATIVE)), getInteger(8, FUNCTION_PUBLIC)); + llvm::Value* function_kind = builder->CreateSelect(native_flag, getInteger(8, FUNCTION_NATIVE), getInteger(8, FUNCTION_PUBLIC)); + ///Disable subr because it is only meant to be used static + func = insert_value(func, function_kind, 2); + } + if (obj->on_stack) + c_SP(-1); + if (on_stack) + push_value(func, T_FUNCTION); + return func; +} + +llvm::Value* PushVirtualFunctionExpression::codegen_get_value(){ + llvm::Value* ob = obj->codegen_get_value(); + llvm::Value* object = extract_value(ob, 1); + CLASS* k = klass(); + + effective_class = get_global((void*)k, llvmType(getInt8Ty)); + + create_check(k, effective_class, object); + + llvm::Value* func = insert_value(insert_value(get_new_struct(function_type), object, 1), getInteger(8, FUNCTION_NATIVE), 2); + if (obj->on_stack) + c_SP(-1); + if (on_stack) + push_value(func, T_FUNCTION); + return func; +} + +llvm::Value* PushVirtualStaticFunctionExpression::codegen_get_value(){ + llvm::Value* ob = obj->codegen_get_value(); + llvm::Value* object = extract_value(ob, 1); + CLASS* k = klass(); + + effective_class = get_global((void*)k, llvmType(getInt8Ty)); + + llvm::Value* func = insert_value(insert_value(get_new_struct(function_type), object, 1), getInteger(8, FUNCTION_NATIVE), 2); + if (obj->on_stack) + c_SP(-1); + if (on_stack) + push_value(func, T_FUNCTION); + return func; +} + +//Almost the same code as above, but setting object to null ... +llvm::Value* PushPureObjectStaticFunctionExpression::codegen_get_value(){ + llvm::Value* ob = obj->codegen_get_value(); + llvm::Value* object = extract_value(ob, 1); + CLASS* k = klass(); + CLASS_DESC* d = desc(); + + if (isa(obj)){ + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy)); + } else if (!k->is_virtual){ + make_nullcheck(object); + effective_class = load_element(builder->CreateBitCast(object, pointer_t(OBJECT_type)), 0); + } else { + //CLASS_load(k); + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)k), llvmType(getInt8PtrTy)); + } + + create_check(k, effective_class, object); + + unref_object(object); + object = get_nullptr(); + + llvm::Value* func = insert_value(get_new_struct(function_type), object, 1); + + if (!d->method.native){ + //Assume native classes don't inherit non-native ones :) + } else { + int offset_table = TARGET_BITS == 64 ? 40/8 : 28/4; + int offset_desc_in_desc_symbol = TARGET_BITS == 64 ? 12 : 8; + int offset_native_flag = TARGET_BITS == 64 ? 35 : 19; + //table_addr = (char*)effective_class->table + llvm::Value* table_addr = builder->CreateLoad(builder->CreateGEP(builder->CreateBitCast(effective_class, pointer_t(llvmType(getInt8PtrTy))), getInteger(TARGET_BITS, offset_table))); + //desc_addr_addr = (char*)(table_addr + sizeof(CLASS_DESC_SYMBOL) * index + desc_offset) + llvm::Value* desc_addr_addr = builder->CreateGEP(table_addr, getInteger(TARGET_BITS, sizeof(CLASS_DESC_SYMBOL) * index + offset_desc_in_desc_symbol)); + //desc_addr = *(char**)desc_addr_addr + llvm::Value* desc_addr = builder->CreateLoad(builder->CreateBitCast(desc_addr_addr, pointer_t(llvmType(getInt8PtrTy)))); + //native_flag_addr = (char*)(desc_addr + offset_native_flag) + llvm::Value* native_flag_addr = builder->CreateGEP(desc_addr, getInteger(TARGET_BITS, offset_native_flag)); + //native_subr_flag = *native_flag_addr + llvm::Value* native_subr_flag = builder->CreateLoad(native_flag_addr); + //native_flag = (bool)(native_subr_flag & 1) + llvm::Value* native_flag = builder->CreateTrunc(native_subr_flag, llvmType(getInt1Ty)); + //subr_flag = (bool)(native_subr_flag & 2) + //llvm::Value* subr_flag = builder->CreateICmpNE(builder->CreateAnd(native_subr_flag, getInteger(8, 2)), getInteger(8, 0)); + + //llvm::Value* function_kind = builder->CreateSelect(native_flag, builder->CreateSelect(subr_flag, getInteger(8, FUNCTION_SUBR), getInteger(8, FUNCTION_NATIVE)), getInteger(8, FUNCTION_PUBLIC)); + llvm::Value* function_kind = builder->CreateSelect(native_flag, getInteger(8, FUNCTION_NATIVE), getInteger(8, FUNCTION_PUBLIC)); + ///Disable subr, because I think it will never be used this way anyway, else it is a (big) performance hit. + func = insert_value(func, function_kind, 2); + } + if (obj->on_stack) + c_SP(-1); + if (on_stack) + push_value(func, T_FUNCTION); + return func; +} + +llvm::Value* PushStaticFunctionExpression::codegen_get_value(){ + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(intptr_t)(void*)klass()), llvmType(getInt8PtrTy)); + + llvm::Value* ret = get_new_struct(function_type, effective_class, get_nullptr()); + + if (on_stack) + push_value(ret, T_FUNCTION); + return ret; +} + +llvm::Value* PushPureObjectStructFieldExpression::codegen_get_value(){ + llvm::Value* struct_obj = extract_value(obj->codegen_get_value(), 1); + + make_nullcheck(struct_obj); + + llvm::Value* ref_addr = builder->CreateBitCast(builder->CreateGEP(struct_obj, getInteger(TARGET_BITS, offsetof(CSTRUCT, ref))), charPP); + + llvm::Value* element_addr = gen_if_else_phi(builder->CreateICmpNE(builder->CreateLoad(ref_addr), get_nullptr()), [&](){ + llvm::Value* addr_addr = builder->CreateBitCast(builder->CreateGEP(struct_obj, getInteger(TARGET_BITS, offsetof(CSTATICSTRUCT, addr))), charPP); + return builder->CreateGEP(builder->CreateLoad(addr_addr), getInteger(TARGET_BITS, this->desc()->variable.offset)); + }, [&](){ + return builder->CreateGEP(struct_obj, getInteger(TARGET_BITS, sizeof(CSTRUCT) + this->desc()->variable.offset)); + }); + + llvm::Value* ret; + + int ctype_id = desc()->variable.ctype.id; + if (ctype_id == TC_ARRAY){ + //Embedded array inside struct + ret = codegen_tc_array(desc()->variable.klass, struct_obj, desc()->variable.ctype.value, element_addr, type); + } else if (ctype_id == TC_STRUCT){ + //Embedded struct inside struct + ret = builder->CreateCall3(get_global_function_jif(CSTRUCT_create_static, 'p', "ppp"), + get_global((void*)desc()->variable.klass, llvmType(getInt8Ty)), + builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), + element_addr); + borrow_object_no_nullcheck(ret); + + ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), ret); + } else { + ret = read_variable(type, element_addr); + } + + unref_object_no_nullcheck(struct_obj); + + c_SP(-obj->on_stack); + if (on_stack) + push_value(ret, type); + return ret; +} + +void PopPureObjectStructFieldExpression::codegen(){ + llvm::Value* value = val->codegen_get_value(); + llvm::Value* struct_obj = extract_value(obj->codegen_get_value(), 1); + + make_nullcheck(struct_obj); + + llvm::Value* ref_addr = builder->CreateBitCast(builder->CreateGEP(struct_obj, getInteger(TARGET_BITS, offsetof(CSTRUCT, ref))), charPP); + + llvm::Value* element_addr = gen_if_else_phi(builder->CreateICmpNE(builder->CreateLoad(ref_addr), get_nullptr()), [&](){ + llvm::Value* addr_addr = builder->CreateBitCast(builder->CreateGEP(struct_obj, getInteger(TARGET_BITS, offsetof(CSTATICSTRUCT, addr))), charPP); + return builder->CreateGEP(builder->CreateLoad(addr_addr), getInteger(TARGET_BITS, this->desc()->variable.offset)); + }, [&](){ + return builder->CreateGEP(struct_obj, getInteger(TARGET_BITS, sizeof(CSTRUCT) + this->desc()->variable.offset)); + }); + + release_variable(type, element_addr); + variable_write(element_addr, value, type); + + unref_object_no_nullcheck(struct_obj); +} + +llvm::Value* SwapExpression::codegen_get_value(){ + llvm::Value* ret = push_a_expr->codegen_get_value(); + pop_a_expr->codegen(); + return ret; +} + +void SwapExpression::codegen_on_stack(){ + push_a_expr->codegen_on_stack(); + pop_a_expr->codegen(); +} + +static llvm::Type* const extern_types[] = { + llvmType(getVoidTy), + llvmType(getInt8Ty), + llvmType(getInt8Ty), + llvmType(getInt16Ty), + llvmType(getInt32Ty), + llvmType(getInt64Ty), + llvmType(getFloatTy), + llvmType(getDoubleTy), + NULL, + llvmType(getInt8PtrTy), + llvmType(getInt8PtrTy), + llvmType(getInt8PtrTy), + NULL, + NULL, + NULL, + llvmType(getInt8PtrTy), + llvmType(getInt8PtrTy) +}; + +llvm::Value* codegen_extern_manage_value(llvm::Value* val, TYPE type){ + if (type == T_BOOLEAN) + val = builder->CreateZExt(val, llvmType(getInt8Ty)); + + else if (TYPE_is_string(type)) + val = builder->CreateGEP(extract_value(val, 1), to_target_int(extract_value(val, 2))); + + else if (TYPE_is_object(type)){ + val = extract_value(val, 1); + val = gen_if_phi(get_nullptr(), builder->CreateICmpNE(val, get_nullptr()), [&](){ + CLASS* object_class = (CLASS*)(void*)type; + + llvm::Value* normal = builder->CreateGEP(val, getInteger(TARGET_BITS, sizeof(OBJECT))); + + llvm::Value* OBJ = builder->CreateBitCast(val, pointer_t(OBJECT_type)); + llvm::Value* klass = load_element(OBJ, 0); + + auto get_bit_from_class = [](llvm::Value* obj, int offset_byte, int offset_bit){ + return builder->CreateTrunc(builder->CreateLShr(builder->CreateLoad(builder->CreateGEP(obj, getInteger(TARGET_BITS, offset_byte))), getInteger(8, offset_bit)), llvmType(getInt1Ty)); + }; + + auto handle_class_object = [normal, &get_bit_from_class](llvm::Value* obj){ + const int offset_is_native = TARGET_BITS == 64 ? 34 : 22; + const int bit_index_is_native = 2; + + llvm::Value* is_native = get_bit_from_class(obj, offset_is_native, bit_index_is_native); + return gen_if_phi(normal, builder->CreateXor(is_native, getInteger(1, true)), [obj](){ + llvm::Value* stat = builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, offsetof(CLASS, stat))), charPP); + return builder->CreateLoad(stat); + }, "not_native"); + }; + + auto handle_struct_object = [](llvm::Value* obj){ + /*if (((CSTRUCT *)ob)->ref) + addr = (char *)((CSTATICSTRUCT *)ob)->addr; + else + addr = (char *)ob + sizeof(CSTRUCT);*/ + llvm::Value* ref_addr = builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, offsetof(CSTRUCT, ref))), charPP); + llvm::Value* ref_not_null = builder->CreateICmpNE(builder->CreateLoad(ref_addr), get_nullptr()); + + return gen_if_phi(builder->CreateGEP(obj, getInteger(TARGET_BITS, sizeof(CSTRUCT))), ref_not_null, [&](){ + llvm::Value* addr_addr = builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, offsetof(CSTATICSTRUCT, addr))), charPP); + return builder->CreateLoad(addr_addr); + }); + }; + + if (TYPE_is_pure_object(type) && object_class == (CLASS*)(void*)GB.FindClass("Class")){ + val = handle_class_object(val); + } else if (TYPE_is_pure_object(type) && CLASS_is_array(object_class)){ + val = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(val, getInteger(TARGET_BITS, offsetof(CARRAY, data))), charPP)); + } else if (TYPE_is_pure_object(type) && CLASS_is_struct(object_class)){ + val = handle_struct_object(val); + } else if (TYPE_is_pure_object(type)){ + val = normal; + } else { + val = gen_if_else_phi(builder->CreateICmpEQ(klass, builder->CreateIntToPtr(getInteger(TARGET_BITS, GB.FindClass("Class")), llvmType(getInt8PtrTy))), [&](){ + return handle_class_object(val); + }, [&](){ + const int offset_is_array = TARGET_BITS == 64 ? 34 : 22; + const int bit_index_is_array = 6; + + return gen_if_else_phi(get_bit_from_class(klass, offset_is_array, bit_index_is_array), [&](){ + return builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(val, getInteger(TARGET_BITS, offsetof(CARRAY, data))), charPP)); + }, [&](){ + const int offset_is_struct = TARGET_BITS == 64 ? 34 : 22; + const int bit_index_is_struct = 5; + + return gen_if_phi(normal, get_bit_from_class(klass, offset_is_struct, bit_index_is_struct), [&](){ + return handle_struct_object(val); + }, "extern_arg_is_struct"); + }, "extern_arg_is_array"); + }, "extern_arg_is_class"); + } + return val; + }, "OBJ_not_null_for_extern"); + } + return val; +} + +static llvm::Value* codegen_extern_manage_return_value(llvm::Value* ret, TYPE type){ + if (type == T_BOOLEAN) + ret = builder->CreateICmpNE(ret, getInteger(8, false)); + else if (TYPE_is_string(type)){ + ret = gen_if_phi(get_default(T_CSTRING), builder->CreateICmpNE(ret, get_nullptr()), [&](){ + return get_cstring_from_addr(ret); + }, "extern_return_not_nullstring"); + } else if (TYPE_is_object(type)){ + if (TYPE_is_pure_object(type)){ + CLASS* class_struct = (CLASS*)(void*)type; + if (CLASS_is_struct(class_struct)){ + ret = builder->CreateCall3(get_global_function_jif(CSTRUCT_create_static, 'p', "ppp"), + get_global((void*)-1, llvmType(getInt8Ty)), + get_global((void*)class_struct, llvmType(getInt8Ty)), + ret); + } + } + borrow_object(ret); + ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), ret); + } + return ret; +} + +static void func_extern_call_variant_vararg(void* return_value_addr, void* func_addr, int nargs, TYPE return_type); + +static llvm::Value* codegen_raise_event(std::vector& args, int index, bool on_stack){ + for(size_t i=0, e=args.size(); i!=e; i++){ + args[i]->codegen_on_stack(); + } + llvm::Value* event_stop = builder->CreateCall3(get_global_function_vararg(GB.Raise, 'c', "pii"), + current_op, getInteger(32, index), getInteger(32, -args.size())); + event_stop = builder->CreateTrunc(event_stop, llvmType(getInt1Ty)); + + if (on_stack) + push_value(event_stop, T_BOOLEAN); + return event_stop; +} + +llvm::Value* CallExpression::codegen_get_value(){ + if (func->type == T_CLASS && desc == NULL){ + //Cast + llvm::Value* ret = args[0]->codegen_get_value(); + if (!args[0]->on_stack && on_stack){ + push_value(ret, type); + } else if (args[0]->on_stack && !on_stack){ + c_SP(-1); + } + return ret; + } + if (FunctionExpression* fe = dynamic_cast(func)){ + if (fe->function_expr_type == EventFn){ + return codegen_raise_event(args, ((PushEventExpression*)func)->index, on_stack); + } + } + if (PushExternExpression* ee = dynamic_cast(func)){ + llvm::Value* call_addr; + + if (ee->object_to_release){ + llvm::Value* obj = ee->object_to_release->codegen_get_value(); + c_SP(-ee->object_to_release->on_stack); + call_addr = builder->CreateCall2(get_global_function(JR_extern_dispatch_object, 'p', "pi"), extract_value(obj, 1), getInteger(32, ee->index)); + } + + CLASS_EXTERN* ext; + if (ee->object_to_release) + ext = &ee->klass->load->ext[ee->klass->table[ee->index].desc->ext.exec]; //An extern method with the correct signature + else + ext = &ee->klass->load->ext[ee->index]; + EXTERN_FUNC_INFO func = JIF.F_EXTERN_get_function_info(ext); + + llvm::Value* ret = NULL; + + if (!ee->must_variant_dispatch){ + std::vector ft; + std::vector orig_args; + std::vector func_args; + ft.resize(ext->n_param); + orig_args.resize(args.size()); + func_args.resize(args.size()); + + for(size_t i=0; in_param) + ft[i] = extern_types[ext->param[i].type]; + + llvm::Value* val = args[i]->codegen_get_value(); + orig_args[i] = val; + + val = codegen_extern_manage_value(val, args[i]->type); + func_args[i] = val; + } + + llvm::FunctionType* function_type = llvm::FunctionType::get(extern_types[type > T_OBJECT ? T_OBJECT : type], ft, true); + + std::string function_name = ext->library; + function_name += '.'; + function_name += func.alias; + + llvm::Value* call_function; + if (!ee->object_to_release){ + llvm::GlobalValue* glob_val = (llvm::GlobalValue*)M->getOrInsertFunction(function_name, function_type); + register_global_symbol(function_name, glob_val, func.call); + call_function = glob_val; + } else { + call_function = builder->CreateBitCast(call_addr, pointer_t(function_type)); + } + + ret = builder->CreateCall(call_function, func_args); + + ret = codegen_extern_manage_return_value(ret, type); + + for(size_t arg=args.size(); arg --> 0; ){ + release(orig_args[arg], args[arg]->type); + if (args[arg]->on_stack) + c_SP(-1); + } + } else { + for(size_t i=0; icodegen_on_stack(); + } + + llvm::Value* return_value_addr = type == T_VOID ? get_nullptr() : (llvm::Value*)create_alloca_in_entry(TYPE_llvm(type)); + if (!ee->object_to_release) + call_addr = builder->CreateIntToPtr(getInteger(TARGET_BITS, (uint64_t)func.call), llvmType(getInt8PtrTy)); + llvm::Value* args_size = getInteger(32, args.size()); + llvm::Value* return_type = getInteger(TARGET_BITS, type); + + builder->CreateCall4(get_global_function(func_extern_call_variant_vararg, 'v', "ppij"), + builder->CreateBitCast(return_value_addr, llvmType(getInt8PtrTy)), + call_addr, + args_size, + return_type); + + if (type != T_VOID) + ret = builder->CreateLoad(return_value_addr); + } + + if (on_stack) + push_value(ret, type); + + return ret; + } + codegen_on_stack(); + return ret_top_stack(type, on_stack); +} +void CallExpression::codegen_on_stack(){ + FunctionExpression* fe = dynamic_cast(func); + if (fe && fe->function_kind == FUNCTION_SUBR){ + //String.Mid, String.Left etc + + for(size_t i=0, e=args.size(); i!=e; i++){ + args[i]->codegen_on_stack(); + } + + void* f = (void*)klass->table[index].desc->method.exec; + builder->CreateCall( + builder->CreateBitCast(get_global(f, llvmType(getInt8Ty)), pointer_t(get_function_type('v', "h"))), + getInteger(16, args.size())); + + return; + } + if (fe && fe->function_kind == FUNCTION_EVENT){ + codegen_raise_event(args, ((PushEventExpression*)func)->index, on_stack); + return; + } + if (isa(func)){ + codegen_get_value(); + return; + } + + llvm::Value *func_value, *object; + + FunctionExpression fe_temp; + + if (func->type == T_CLASS){ + if (desc == NULL){ + //Cast + llvm::Value* ret = args[0]->codegen_get_value(); + if (!args[0]->on_stack){ + push_value(ret, type); + } + return; + } + func_value = get_new_struct(function_type, get_global((void*)klass, llvmType(getInt8Ty)), get_nullptr()); + push_value(func_value, T_FUNCTION); + object = get_nullptr(); + + fe = &fe_temp; + + fe->function_class = klass; + fe->function_expr_type = ClassFn; + fe->function_kind = desc->method.native ? FUNCTION_NATIVE : FUNCTION_PUBLIC; + fe->effective_class = get_global((void*)klass, llvmType(getInt8Ty)); + fe->function_unknown = NULL; + } else if (fe != NULL){ + func_value = func->codegen_get_value(); + object = extract_value(func_value, 1); + } else { + //Variant call + func->codegen_on_stack(); + } + //llvm::Value* func_value_addr = builder->CreateBitCast(get_value_on_top_addr(), pointer_t(value_types[T_FUNCTION])); + //llvm::Value* object = load_element(func_value_addr, 2); + + for(size_t i=0, e=args.size(); i!=e; i++){ + args[i]->codegen_on_stack(); + } + + //FIXME might only be needed for non-quick calls: + builder->CreateStore(getInteger(TARGET_BITS, (int64_t)(intptr_t)pc), get_global((void*)&PC, LONG_TYPE)); + + if (fe != NULL){ + builder->CreateStore(getInteger(8, args.size()), get_global((void*)&EXEC.nparam, llvmType(getInt8Ty))); + if (fe->function_expr_type == PrivateFn){ + builder->CreateStore(getInteger(32, index), get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + builder->CreateStore(current_op, get_global((void*)&EXEC.object)); + builder->CreateStore(get_global((void*)klass, llvmType(getInt8Ty)), get_global((void*)&EXEC.klass)); + + if (can_quick) + builder->CreateCall(get_global_function_jif(EXEC_enter_quick, 'v', "")); + else + builder->CreateCall(get_global_function_jif(EXEC_enter, 'v', "")); + if (klass->load->func[index].fast) + builder->CreateCall(get_global_function(JR_EXEC_jit_execute_function, 'v', "")); + else + builder->CreateCall(get_global_function_jif(EXEC_function_loop, 'v', "")); + } /*else if (fe->function_expr_type == UnknownFn){ + *pc |= CODE_CALL_VARIANT; + } */else { + if (fe->function_unknown){ + builder->CreateStore(getInteger(TARGET_BITS, (int64_t)(intptr_t)fe->function_unknown), get_global((void*)&EXEC_unknown_name, LONG_TYPE)); + *pc |= CODE_CALL_VARIANT; + } + + if (fe->function_kind == FUNCTION_PUBLIC){ + if (can_quick) + builder->CreateCall3(get_global_function(JR_exec_enter_quick, 'v', "ppi"), fe->effective_class, object, getInteger(32, index)); + else + builder->CreateCall3(get_global_function(JR_exec_enter, 'v', "ppi"), fe->effective_class, object, getInteger(32, index)); + } else if (fe->function_kind == FUNCTION_NATIVE){ + builder->CreateStore(get_global((void*)&desc->method, llvmType(getInt8Ty)), get_global((void*)&EXEC.desc, llvmType(getInt8PtrTy))); + builder->CreateStore(object, get_global((void*)&EXEC.object)); + builder->CreateStore(fe->effective_class, get_global((void*)&EXEC.klass)); + + if (can_quick) + builder->CreateCall(get_global_function_jif(EXEC_native_quick, 'v', "")); + else { + builder->CreateStore(getInteger(8, true), get_global((void*)&EXEC.use_stack, llvmType(getInt8Ty))); + builder->CreateCall(get_global_function_jif(EXEC_native, 'v', "")); + } + } else if (fe->function_kind == -1){ + llvm::Value* kind = extract_value(func_value, 2); + gen_if_else(builder->CreateICmpEQ(kind, getInteger(8, FUNCTION_PUBLIC)), [&](){ + //Public + if (can_quick) + builder->CreateCall3(get_global_function(JR_exec_enter_quick, 'v', "ppi"), fe->effective_class, object, getInteger(32, index)); + else + builder->CreateCall3(get_global_function(JR_exec_enter, 'v', "ppi"), fe->effective_class, object, getInteger(32, index)); + //builder->CreateCall(get_global_function(EXEC_jit_execute_function, 'v', "")); + }, [&](){ + //Native + builder->CreateStore(get_global((void*)&desc->method, llvmType(getInt8Ty)), get_global((void*)&EXEC.desc)); + builder->CreateStore(object, get_global((void*)&EXEC.object)); + builder->CreateStore(fe->effective_class, get_global((void*)&EXEC.klass)); + + if (can_quick) + builder->CreateCall(get_global_function_jif(EXEC_native_quick, 'v', "")); + else { + builder->CreateStore(getInteger(8, true), get_global((void*)&EXEC.use_stack, llvmType(getInt8Ty))); + builder->CreateCall(get_global_function_jif(EXEC_native, 'v', "")); + } + }); + } else { + //FIXME hmm does this ever happen? + abort(); + } + } + } else { + //Variant call + builder->CreateCall(get_global_function(JR_call, 'v', "i"), getInteger(32, args.size())); + } + if (!byref_expressions.empty()){ + gen_if_noreturn(builder->CreateICmpEQ(read_global((void*)&PC, llvmType(getInt16PtrTy)), get_global((void*)pc, llvmType(getInt16Ty))), [&](){ + create_throw(E_BYREF); + }); + for(size_t i=0, e=byref_expressions.size(); i!=e; i++){ + byref_expressions[i]->codegen(); + } + } + + return; +#if 0 + if (desc != NULL || kind == FUNCTION_PRIVATE){ + if (kind != -1){ + switch(kind){ + case FUNCTION_NULL: abort(); + case FUNCTION_NATIVE: + builder->CreateStore(getInteger(8, true), get_global((void*)&EXEC.use_stack, llvmType(getInt8Ty))); + //builder->CreateStore(getInteger(8, true), get_global((void*)&EXEC.native, llvmType(getInt8Ty))); + builder->CreateStore(getInteger(32, index), get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + builder->CreateStore(get_global((void*)&desc->method, llvmType(getInt8Ty)), get_global((void*)&EXEC.desc, llvmType(getInt8PtrTy))); + builder->CreateStore(object, get_global((void*)&EXEC.object)); + builder->CreateStore(get_global((void*)klass, llvmType(getInt8Ty)), get_global((void*)&EXEC.klass)); + + + if (can_quick) + builder->CreateCall(get_global_function(EXEC_native_quick, 'v', "")); + else + builder->CreateCall(get_global_function(EXEC_native, 'v', "")); + break; + + case FUNCTION_PRIVATE: + //builder->CreateStore(getInteger(8, false), get_global((void*)&EXEC.native, llvmType(getInt8Ty))); + builder->CreateStore(getInteger(32, index), get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + builder->CreateStore(object, get_global((void*)&EXEC.object)); + builder->CreateStore(get_global((void*)klass, llvmType(getInt8Ty)), get_global((void*)&EXEC.klass)); + + if (can_quick) + builder->CreateCall(get_global_function(EXEC_enter_quick, 'v', "")); + else + builder->CreateCall(get_global_function(EXEC_enter, 'v', "")); + builder->CreateCall(get_global_function(EXEC_jit_execute_function, 'v', "")); + break; + + case FUNCTION_PUBLIC: { + //builder->CreateStore(getInteger(8, false), get_global((void*)&EXEC.native, llvmType(getInt8Ty))); + /*builder->CreateStore(getInteger(32, index), get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + builder->CreateStore(get_global((void*)&desc->method, llvmType(getInt8Ty)), get_global((void*)&EXEC.desc)); + builder->CreateStore(object, get_global((void*)&EXEC.object)); + builder->CreateStore(get_global((void*)klass, llvmType(getInt8Ty)), get_global((void*)&EXEC.klass));*/ + PushPureObjectExpression* ppoe = (PushPureObjectExpression*)func; + + if (can_quick) + builder->CreateCall3(get_global_function(JR_exec_enter_quick, 'v', "ppi"), ppoe->effective_class, object, getInteger(32, index)); + else + builder->CreateCall3(get_global_function(JR_exec_enter, 'v', "ppi"), ppoe->effective_class, object, getInteger(32, index)); + break; + } + case FUNCTION_EVENT: + builder->CreateCall3(get_global_function_vararg(GB_Raise, 'i', "pii"), + current_op, getInteger(32, index + (CP->parent ? CP->parent->n_event : 0)), getInteger(32, -args.size())); + break; + + case FUNCTION_UNKNOWN: + case FUNCTION_CALL: + case FUNCTION_EXTERN: + case FUNCTION_SUBR: + abort(); + } + } else { + llvm::Value* kind = extract_value(func_value, 2); + gen_if_else(builder->CreateICmpEQ(kind, getInteger(8, FUNCTION_SUBR)), [&](){ + //Subr + //FIXME + }, [&](){ + PushPureObjectExpression* ppoe = (PushPureObjectExpression*)func; + + + gen_if_else(builder->CreateICmpEQ(kind, getInteger(8, FUNCTION_PUBLIC)), [&](){ + //Public + if (can_quick) + builder->CreateCall3(get_global_function(JR_exec_enter_quick, 'v', "ppi"), ppoe->effective_class, object, getInteger(32, index)); + else + builder->CreateCall3(get_global_function(JR_exec_enter, 'v', "ppi"), ppoe->effective_class, object, getInteger(32, index)); + //builder->CreateCall(get_global_function(EXEC_jit_execute_function, 'v', "")); + }, [&](){ + //Native + builder->CreateStore(getInteger(32, index), get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + builder->CreateStore(get_global((void*)&desc->method, llvmType(getInt8Ty)), get_global((void*)&EXEC.desc)); + builder->CreateStore(object, get_global((void*)&EXEC.object)); + builder->CreateStore(ppoe->effective_class, get_global((void*)&EXEC.klass)); + + builder->CreateStore(getInteger(8, true), get_global((void*)&EXEC.use_stack, llvmType(getInt8Ty))); + if (can_quick) + builder->CreateCall(get_global_function(EXEC_native_quick, 'v', "")); + else + builder->CreateCall(get_global_function(EXEC_native, 'v', "")); + }); + }); + } + } else { + //FIXME call variant/object runtime + } +#endif +} + +llvm::Value* DupExpression::codegen_get_value(){ + if (on_stack || expr->on_stack) on_stack = expr->on_stack = true; + duped->llvm_value = expr->codegen_get_value(); + borrow(duped->llvm_value, type); + return duped->llvm_value; +} + +void DupExpression::codegen_on_stack(){ + on_stack = expr->on_stack = true; + expr->codegen_on_stack(); + duped->llvm_value = ret_top_stack(type, true); + borrow(duped->llvm_value, type); +} + +void OnGotoExpression::codegen(){ + JumpTablePendingBranch p; + p.condition = condition->codegen_get_value(); + if (condition->on_stack) + c_SP(-1); + p.insert_point = builder->GetInsertBlock(); //Must be after condition codegen! + p.destinations = &destinations; + p.default_addr = default_addr; + jump_table_pending_branches.push_back(p); + builder->SetInsertPoint(create_bb("dummy")); +} + +void JumpExpression::codegen(){ + PendingBranch p; + p.condition = NULL; + p.insert_point = builder->GetInsertBlock(); + p.true_addr = addr; + pending_branches.push_back(p); + builder->SetInsertPoint(create_bb("dummy")); +} + +void GosubExpression::codegen(){ + llvm::Value* cond = NULL; + if (!destinations.empty()){ + cond = condition->codegen_get_value(); + if (condition->on_stack) + c_SP(-1); + } + + gen_if_else(!cond ? getInteger(1, true) : builder->CreateICmpULT(cond, getInteger(32, destinations.size())), [&](){ + /*llvm::Value* index = builder->CreateLoad(temp_num_gosubs_on_stack); + llvm::Value* indices[2] = {getInteger(TARGET_BITS, 0), to_target_int(index)}; + builder->CreateStore(llvm::BlockAddress::get(contpoint), builder->CreateGEP(temp_gosub_stack, indices)); + + index = builder->CreateAdd(index, getInteger(32, 1)); + builder->CreateStore(index, temp_num_gosubs_on_stack);*/ + +#ifndef GOSUB_ON_STACK + llvm::Value* gosub_stack_node = builder->CreateBitCast(builder->CreateCall(get_global_function(GB.Add, 'p', "p"), get_global(&GP, llvmType(getInt8Ty))), pointer_t(gosub_stack_node_type)); + + /*builder->CreateStore(builder->CreateLoad(gosub_return_point), create_gep(gosub_stack_node, TARGET_BITS, 0, 32, 0)); + builder->CreateStore(llvm::BlockAddress::get(contpoint), gosub_return_point);*/ + unsigned int gosub_return_id = gosub_continue_points.size() + 1; + builder->CreateStore(builder->CreateLoad(gosub_return_point), create_gep(gosub_stack_node, TARGET_BITS, 0, 32, 0)); + builder->CreateStore(getInteger(16, gosub_return_id), gosub_return_point); +#else + llvm::Value* stack_addr = builder->CreateLoad(gp); + unsigned int gosub_return_id = gosub_continue_points.size() + 1; + store_value(stack_addr, builder->CreateLoad(gosub_return_point), T_SHORT); + builder->CreateStore(getInteger(16, gosub_return_id), gosub_return_point); + + int diff = 1 + end_ctrl - FP->n_local; + llvm::Value* new_gp = builder->CreateGEP(stack_addr, getInteger(TARGET_BITS, diff)); + gen_if_noreturn(builder->CreateICmpUGE( + builder->CreateGEP(stack_addr, getInteger(TARGET_BITS, 1 + FP->stack_usage - FP->n_local + 8)), + builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(void*)STACK_limit), pointer_t(value_type))), [&](){ + + create_throw(E_STACK); + }); + builder->CreateStore(new_gp, gp); + //c_SP(diff); + builder->CreateStore(new_gp, get_global(&SP, pointer_t(value_type))); +#endif + + + if (FP->n_ctrl){ +#ifndef GOSUB_ON_STACK + llvm::Value* gp_ctrl_addr = create_gep(gosub_stack_node, TARGET_BITS, 0, 32, 1); + + builder->CreateCall2(get_global_function(GB.Alloc, 'v', "pj"), builder->CreateBitCast(gp_ctrl_addr, llvmType(getInt8PtrTy)), getInteger(TARGET_BITS, sizeof(VALUE) * FP->n_ctrl)); + + llvm::Value* gp_ctrl = builder->CreateLoad(gp_ctrl_addr); +#else + llvm::Value* gp_ctrl = builder->CreateGEP(stack_addr, getInteger(TARGET_BITS, 1)); +#endif + + + for(int i=FP->n_local; in_local ? gp_ctrl : builder->CreateGEP(gp_ctrl, getInteger(TARGET_BITS, i - FP->n_local)), builder->CreateLoad(locals[i]), get_ctrl_type(i)); + int spec_type = special_ctrl_type(get_ctrl_type(i)); + if (spec_type != 0){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, i*sizeof(VALUE))); + store_value(stack_addr, NULL, T_VOID); + } + builder->CreateStore(getInteger(32, 0), current_ctrl_types[i - FP->n_local]); + } + for(int i=end_ctrl; in_local+FP->n_ctrl; i++){ + store_value(builder->CreateGEP(gp_ctrl, getInteger(TARGET_BITS, i - FP->n_local)), NULL, T_VOID); + + llvm::Value* old_type = release_ctrl(i); + builder->CreateStore(getInteger(32, 0), current_ctrl_types[i - FP->n_local]); + gen_if(builder->CreateICmpNE(old_type, getInteger(32, 0)), [&](){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, i*sizeof(VALUE))); + store_value(stack_addr, NULL, T_VOID); + }, "old_ctrl_needs_to_be_cleaned4"); + } + } + }, [&](){ + if (cond){ + PendingBranch p; + p.condition = NULL; + p.insert_point = builder->GetInsertBlock(); + p.true_addr = default_addr; + pending_branches.push_back(p); + builder->SetInsertPoint(create_bb("dummy")); + } + }, "gosub_should_run"); + + if (!cond){ + PendingBranch p; + p.condition = NULL; + p.insert_point = builder->GetInsertBlock(); + p.true_addr = gosubaddr; + pending_branches.push_back(p); + } else { + JumpTablePendingBranch p; + p.condition = cond; + p.insert_point = builder->GetInsertBlock(); + p.destinations = &destinations; + p.default_addr = destinations.back(); + jump_table_pending_branches.push_back(p); + } + + llvm::BasicBlock* contpoint = create_bb("gosub_continue_point"); + gosub_continue_points.push_back(contpoint); + + builder->SetInsertPoint(contpoint); + + //On return: +#ifndef GOSUB_ON_STACK + llvm::Value* gp = read_global(&GP, pointer_t(gosub_stack_node_type)); + llvm::Value* gp_array = builder->CreateBitCast(gp, llvmType(getInt32PtrTy)); + llvm::Value* count_addr = builder->CreateGEP(gp_array, getInteger(TARGET_BITS, -4)); //((int*)GP)[-4] == ARRAY_count(GP) + llvm::Value* index = builder->CreateAdd(builder->CreateLoad(count_addr), getInteger(32, -1)); + llvm::Value* gosub_stack_node = builder->CreateGEP(gp, to_target_int(index)); + builder->CreateStore(index, count_addr); + + llvm::Value* old_return_point = builder->CreateLoad(create_gep(gosub_stack_node, TARGET_BITS, 0, 32, 0)); + builder->CreateStore(old_return_point, gosub_return_point); +#else + + int diff = 1 + end_ctrl - FP->n_local; + llvm::Value* new_gp = builder->CreateGEP(builder->CreateLoad(gp), getInteger(TARGET_BITS, -diff)); + builder->CreateStore(new_gp, gp); + //c_SP(-diff) + builder->CreateStore(new_gp, get_global(&SP, pointer_t(value_type))); + + llvm::Value* old_return_point = read_value(new_gp, T_SHORT); + builder->CreateStore(old_return_point, gosub_return_point); +#endif + + + if (FP->n_ctrl){ +#ifndef GOSUB_ON_STACK + llvm::Value* gp_ctrl_addr = create_gep(gosub_stack_node, TARGET_BITS, 0, 32, 1); + llvm::Value* gp_ctrl = builder->CreateLoad(gp_ctrl_addr); +#else + llvm::Value* gp_ctrl = builder->CreateGEP(new_gp, getInteger(TARGET_BITS, 1)); +#endif + + for(int i=FP->n_local; in_local ? gp_ctrl : builder->CreateGEP(gp_ctrl, getInteger(TARGET_BITS, i - FP->n_local)), get_ctrl_type(i)); + builder->CreateStore(val, locals[i]); + builder->CreateStore(getInteger(32, special_ctrl_type(get_ctrl_type(i))), current_ctrl_types[i - FP->n_local]); + + if (special_ctrl_type(get_ctrl_type(i)) != 0){ + llvm::Value* stack_addr = builder->CreateGEP(read_global(&BP), getInteger(TARGET_BITS, i*sizeof(VALUE))); + store_value(stack_addr, val, get_ctrl_type(i)); + } + } + for(int i=end_ctrl; in_local+FP->n_ctrl; i++){ + builder->CreateStore(getInteger(32, 0), current_ctrl_types[i - FP->n_local]); + } + +#ifndef GOSUB_ON_STACK + builder->CreateCall(get_global_function(GB.Free, 'v', "p"), builder->CreateBitCast(gp_ctrl_addr, llvmType(getInt8PtrTy))); +#endif + } + +} + +void JumpIfExpression::codegen(){ + PendingBranch p; + p.condition = val->codegen_get_value(); + if (val->on_stack) + c_SP(-1); + p.insert_point = builder->GetInsertBlock(); //Must be after condition codegen! + p.true_addr = jump_if_true ? jump_addr : next_addr; + p.false_addr = jump_if_true ? next_addr : jump_addr; + pending_branches.push_back(p); + builder->SetInsertPoint(create_bb("dummy")); +} + +void JumpFirstExpression::codegen(){ + llvm::Value* to_val = to->codegen_get_value(); + if (to->on_stack) + c_SP(-1); + llvm::Value* step_val = step->codegen_get_value(); + if (step->on_stack) + c_SP(-1); + + //builder->CreateStore(to_val, locals[ctrl_to]); + //builder->CreateStore(step_val, locals[ctrl_to+1]); + set_ctrl(to_val, to->type, ctrl_to); + set_ctrl(step_val, step->type, ctrl_to+1); + llvm::Value* ival = builder->CreateLoad(locals[local_var]); + + //If step is 0, don't execute the loop + llvm::Value* condition; + if (step->type == T_SINGLE) + condition = builder->CreateFCmpUEQ(step_val, getFloat(0.0f)); + else if (step->type == T_FLOAT) + condition = builder->CreateFCmpUEQ(step_val, getFloat(0.0)); + else { + static const int bits[] = {0, 1, 8, 16, 32, 64}; + condition = builder->CreateICmpEQ(step_val, getInteger(bits[step->type], 0)); + } + gen_if_noreturn(condition, [&](){ + PendingBranch p; + p.insert_point = builder->GetInsertBlock(); + p.condition = NULL; + p.true_addr = done_addr; + pending_branches.push_back(p); + }, "step_is_zero", "step_not_zero"); + + TYPE to_type = to->type; + TYPE step_type = step->type; + + //Pre-test: + bool is_float = to_type >= T_SINGLE; + llvm::Value *is_positive, *cont_neg, *cont_pos; + if (is_float){ + is_positive = builder->CreateFCmpUGE(step_val, to_type == T_SINGLE ? getFloat(0.0f) : getFloat(0.0)); + cont_neg = builder->CreateFCmpUGE(ival, to_val); + cont_pos = builder->CreateFCmpULE(ival, to_val); + } else { + static const int bits[] = {0, 1, 8, 16, 32, 64}; + is_positive = builder->CreateICmpSGE(step_val, getInteger(bits[step_type], 0)); + if (to_type == T_BYTE){ + cont_neg = builder->CreateICmpUGE(ival, to_val); + cont_pos = builder->CreateICmpULE(ival, to_val); + } else { + cont_neg = builder->CreateICmpSGE(ival, to_val); + cont_pos = builder->CreateICmpSLE(ival, to_val); + } + } + llvm::Value* cont = builder->CreateSelect(is_positive, cont_pos, cont_neg); + + PendingBranch p; + p.insert_point = builder->GetInsertBlock(); + p.condition = cont; + p.true_addr = body_addr; + p.false_addr = done_addr; + pending_branches.push_back(p); + builder->SetInsertPoint(create_bb("dummy")); +} + +void JumpNextExpression::codegen(){ + llvm::Value* to_val = builder->CreateLoad(locals[ctrl_to]); + llvm::Value* step_val = builder->CreateLoad(locals[ctrl_to+1]); + llvm::Value* ival = builder->CreateLoad(locals[local_var]); + + TYPE to_type = get_ctrl_type(ctrl_to); + TYPE step_type = get_ctrl_type(ctrl_to+1); + + //Add step + if (step_type != to_type){ + if (to_type == T_BYTE) + ival = builder->CreateZExt(ival, llvmType(getInt32Ty)); + else + ival = builder->CreateSExt(ival, llvmType(getInt32Ty)); + ival = builder->CreateAdd(ival, step_val, "", false, true /*nsw*/); + ival = builder->CreateTrunc(ival, to_val->getType()); + builder->CreateStore(ival, locals[local_var]); + } else { + if (to_type == T_SINGLE || to_type == T_FLOAT) + ival = builder->CreateFAdd(ival, step_val); + else + ival = builder->CreateAdd(ival, step_val, "", false, true /*nsw*/); + builder->CreateStore(ival, locals[local_var]); + } + + //Test if continue + bool is_float = to_type >= T_SINGLE; + llvm::Value *is_positive, *cont_neg, *cont_pos; + if (is_float){ + is_positive = builder->CreateFCmpUGE(step_val, to_type == T_SINGLE ? getFloat(0.0f) : getFloat(0.0)); + cont_neg = builder->CreateFCmpUGE(ival, to_val); + cont_pos = builder->CreateFCmpULE(ival, to_val); + } else { + static const int bits[] = {0, 1, 8, 16, 32, 64}; + is_positive = builder->CreateICmpSGE(step_val, getInteger(bits[step_type], 0)); + if (to_type == T_BYTE){ + cont_neg = builder->CreateICmpUGE(ival, to_val); + cont_pos = builder->CreateICmpULE(ival, to_val); + } else { + cont_neg = builder->CreateICmpSGE(ival, to_val); + cont_pos = builder->CreateICmpSLE(ival, to_val); + } + } + llvm::Value* cont = builder->CreateSelect(is_positive, cont_pos, cont_neg); + + PendingBranch p; + p.insert_point = builder->GetInsertBlock(); + p.condition = cont; + p.true_addr = body_addr; + p.false_addr = done_addr; + pending_branches.push_back(p); + builder->SetInsertPoint(create_bb("dummy")); +} + +void JumpEnumFirstExpression::codegen(){ + //FIXME fix class enum + if (!TYPE_is_pure_object(obj->type)){ + codegen_pop_ctrl(obj, ctrl); + builder->CreateCall(get_global_function_jif(EXEC_enum_first, 'v', "h"), getInteger(16, ctrl)); + set_ctrl_type(T_OBJECT, ctrl+1); + } else { + llvm::Value* object = obj->codegen_get_value(); + ob = extract_value(object, 1); + + codegen_pop_ctrl(object, obj, ctrl); + + /* Instance variables: + llvm::Value* effective_class; + llvm::Value* obj; + */ + llvm::Value* enum_object; + + CLASS* klass = (CLASS*)(void*)obj->type; + if (klass->is_virtual){ + effective_class = builder->CreateIntToPtr(getInteger(TARGET_BITS, obj->type), llvmType(getInt8PtrTy)); + //Ok, so what we should pass to CENUM_create is the pointer to the class (in case of a virtual class is enumerated), or + //pointer to the object (in case of an instance of a virtual class is enumerated). + //As val._class.class is the same memory as val._object.object, we don't need to do anything special here. + enum_object = ob; + //However, when running EXEC_special with SPEC_FIRST, we need to pass null as object (if virtual class), so we replace ob with null + ob = builder->CreateSelect(builder->CreateICmpEQ(builder->CreatePtrToInt(extract_value(object, 0), llvmType(getInt32Ty)), getInteger(32, T_CLASS)), get_nullptr(), ob); + + //old: enum_object = builder->CreateSelect(builder->CreateICmpEQ(ob, get_nullptr()), effective_class, ob); + } else { + effective_class = extract_value(object, 0); + make_nullcheck(ob); + enum_object = ob; + } + + llvm::Value* cenum_obj = builder->CreateCall(get_global_function_jif(CENUM_create, 'p', "p"), enum_object); + + //unref_object(extract_value(builder->CreateLoad(locals[ctrl+1]), 1)); + borrow_object_no_nullcheck(cenum_obj); + + llvm::Value* cenum_object = get_new_struct(object_type, + get_global((void*)GB.FindClass("Enum"), llvmType(getInt8Ty)), cenum_obj); + + set_ctrl(cenum_object, T_OBJECT, ctrl+1); + + builder->CreateStore(read_global((void*)&EXEC_enum), temp_voidptr); + builder->CreateStore(cenum_obj, get_global((void*)&EXEC_enum)); + builder->CreateCall5(get_global_function_jif(EXEC_special, 'c', "ippic"), + getInteger(32, SPEC_FIRST), + effective_class, + ob, + getInteger(32, 0), + getInteger(8, true)); + builder->CreateStore(builder->CreateLoad(temp_voidptr), get_global((void*)&EXEC_enum)); + } +} + +llvm::Value* OnStackExpression::codegen_get_value(){ + return get_top(type); +} + +void JumpEnumNextExpression::codegen(){ + //FIXME class enum + llvm::Value* stop; + if (!TYPE_is_pure_object(jfirst->obj->type)){ + store_pc(pc); + stop = builder->CreateICmpNE(builder->CreateCall(get_global_function_jif(EXEC_enum_next, 'c', "h"), getInteger(16, drop)), getInteger(8, false)); + } else { + llvm::Value* cenum_obj = extract_value(builder->CreateLoad(locals[jfirst->ctrl+1]), 1); + int stop_offset = sizeof(GB_BASE) + sizeof(LIST) + 5*sizeof(void*); + stop = builder->CreateTrunc(builder->CreateLoad(builder->CreateGEP(cenum_obj, getInteger(TARGET_BITS, stop_offset))), llvmType(getInt1Ty)); + + stop = gen_if_phi(stop, builder->CreateXor(stop, getInteger(1, 1)), [&](){ + builder->CreateStore(read_global((void*)&EXEC_enum), temp_voidptr); + builder->CreateStore(cenum_obj, get_global((void*)&EXEC_enum)); + store_pc(pc); //Used by EXEC_native + llvm::Value* err = builder->CreateCall5(get_global_function_jif(EXEC_special, 'c', "ippic"), + getInteger(32, SPEC_NEXT), + jfirst->effective_class, + jfirst->ob, + getInteger(32, 0), + getInteger(8, false)); + builder->CreateStore(builder->CreateLoad(temp_voidptr), get_global((void*)&EXEC_enum)); + + gen_if_noreturn(builder->CreateICmpNE(err, getInteger(8, false)), [&](){ + create_throw(E_ENUM); + }); + + llvm::Value* stop_after = builder->CreateTrunc(builder->CreateLoad(builder->CreateGEP(cenum_obj, getInteger(TARGET_BITS, stop_offset))), llvmType(getInt1Ty)); + gen_if(builder->CreateOr(getInteger(1, drop), stop_after), [&](){ + //When enumeration stops, T_VOID can be returned instead of the given type + llvm::Value* t = get_top(type); + llvm::Value* tt = load_element(get_value_on_top_addr(), 0); + if (TYPE_is_object(type)) + tt = builder->CreatePtrToInt(tt, LONG_TYPE); + gen_if(builder->CreateICmpNE(tt, getInteger(TARGET_BITS, T_VOID)), [&](){ + release(t, type); + }); + c_SP(-1); + }); + return stop_after; + }, "enum_next_cont"); + } + + PendingBranch p; + p.condition = stop; + p.insert_point = builder->GetInsertBlock(); + p.true_addr = addr; + p.false_addr = cont_addr; + pending_branches.push_back(p); + builder->SetInsertPoint(create_bb("dummy")); +} + +void TryExpression::codegen(){ + in_try = true; + has_tries = true; + + try_blocks.push_back(builder->GetInsertBlock()); + + builder->CreateStore(read_sp(), get_global((void*)&EP, pointer_t(value_type))); + //We only want it to be non-null, so let's put in 1: + builder->CreateStore(get_global((void*)1, llvmType(getInt8Ty)), get_global((void*)&EC)); + + llvm::Value* jmpbuf = builder->CreateCall(get_global_function(JR_try, 'p', "p"), + create_gep(temp_errcontext1, TARGET_BITS, 0, TARGET_BITS, 0)); + + llvm::Function* f = llvm::cast(get_global_function(_setjmp, 'i', "p")); +#if LLVM_VERSION_MAJOR > 3 || (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR == 2) + f->addFnAttr(llvm::Attributes::ReturnsTwice); +#else + f->addFnAttr(llvm::Attribute::ReturnsTwice); +#endif + + llvm::Value* setjmp_return = builder->CreateCall(f, jmpbuf); + + builder->CreateStore(builder->CreateTrunc(setjmp_return, llvmType(getInt8Ty)), + create_gep(temp_errcontext1, TARGET_BITS, 0, TARGET_BITS, offsetof(ERROR_CONTEXT, ret))); + + llvm::Value* second_time = builder->CreateICmpNE(setjmp_return, getInteger(32, 0)); + + gen_if_else(second_time, [&](){ + builder->CreateCall(get_global_function(JR_try_unwind, 'v', "p"), builder->CreateBitCast(builder->CreateLoad(gp), llvmType(getInt8PtrTy))); + builder->CreateStore(getInteger(1, true), temp_got_error); + }, [&](){ + builder->CreateStore(getInteger(1, false), temp_got_error); + }, "Try_cleanup"); + + PendingBranch p; + p.condition = second_time; + p.insert_point = builder->GetInsertBlock(); + p.true_addr = addr2; + p.false_addr = addr1; + pending_branches.push_back(p); + builder->SetInsertPoint(create_bb("dummy")); +} + +void EndTryExpression::codegen(){ + in_try = false; + + llvm::Value* call = builder->CreateCall(get_global_function(JR_end_try, 'v', "p"), + create_gep(temp_errcontext1, TARGET_BITS, 0, TARGET_BITS, 0)); + + if (llvm::Instruction* inst = llvm::dyn_cast(call)){ + llvm::Value* arr[1] = {getInteger(32, 1)}; + inst->setMetadata("end_try", llvm::MDNode::get(llvm_context, arr)); + } + + builder->CreateStore(get_nullptr(), get_global((void*)&EP)); + //We only need to store NULL (no catch) or something whatever else inside EC (there is a catch) + llvm::Value* got_large_error = builder->CreateZExt(builder->CreateXor(builder->CreateLoad(temp_got_error2), getInteger(1, 1)), LONG_TYPE); + builder->CreateStore(got_large_error, get_global((void*)&EC, LONG_TYPE)); +} + +void LargeTryExpression::codegen(){ + has_tries = true; + + builder->CreateStore(getInteger(1, false), temp_got_error2); + + llvm::Value* jmpbuf = builder->CreateCall(get_global_function(JR_try, 'p', "p"), + create_gep(temp_errcontext2, TARGET_BITS, 0, TARGET_BITS, 0)); + + llvm::Function* f = llvm::cast(get_global_function(_setjmp, 'i', "p")); +#if LLVM_VERSION_MAJOR > 3 || (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR == 2) + f->addFnAttr(llvm::Attributes::ReturnsTwice); +#else + f->addFnAttr(llvm::Attribute::ReturnsTwice); +#endif + + llvm::Value* setjmp_return = builder->CreateCall(f, jmpbuf); + + builder->CreateStore(builder->CreateTrunc(setjmp_return, llvmType(getInt8Ty)), + create_gep(temp_errcontext2, TARGET_BITS, 0, TARGET_BITS, offsetof(ERROR_CONTEXT, ret))); + + llvm::Value* second_time = builder->CreateICmpNE(setjmp_return, getInteger(32, 0)); + + gen_if(second_time, [&](){ + builder->CreateCall(get_global_function(JR_try_unwind, 'v', "p"), builder->CreateBitCast(builder->CreateLoad(gp), llvmType(getInt8PtrTy))); + builder->CreateCall(get_global_function(JR_end_try, 'v', "p"), + create_gep(temp_errcontext2, TARGET_BITS, 0, TARGET_BITS, 0)); + builder->CreateStore(get_nullptr(), get_global((void*)&EC)); + builder->CreateStore(getInteger(1, true), temp_got_error2); + }, "Try_cleanup"); + + PendingBranch p; + p.condition = second_time; + p.insert_point = builder->GetInsertBlock(); + p.true_addr = addr2; + p.false_addr = 0; + pending_branches.push_back(p); + builder->SetInsertPoint(create_bb("dummy")); +} + +void LargeCatchExpression::codegen(){ + //Also in ReturnExpression if returning before this + builder->CreateCall(get_global_function(JR_end_try, 'v', "p"), + create_gep(temp_errcontext2, TARGET_BITS, 0, TARGET_BITS, 0)); + //Differs from original interpreter, error thrown in a Finally block is not caught. + builder->CreateStore(get_nullptr(), get_global((void*)&EC)); +} + +void CatchExpression::codegen(){ + llvm::Value* got_error = builder->CreateLoad(temp_got_error2); + + gen_if_noreturn(builder->CreateXor(got_error, getInteger(1, 1)), [&](){ + store_value(get_global((void*)RP), get_default(FP->type), FP->type); + return_points.push_back(builder->GetInsertBlock()); + }, "do_not_catch", "do_catch"); +} + +llvm::Value* NewExpression::codegen_get_value(){ + codegen_on_stack(); + return ret_top_stack(type, true); +} +void NewExpression::codegen_on_stack(){ + for(size_t i=0, e=args.size(); i!=e; i++){ + args[i]->codegen_on_stack(); + } + store_pc(pc); + builder->CreateCall(get_global_function_jif(EXEC_new, 'v', "")); +} + +std::pair BinOpExpression::codegen_operands(){ + llvm::Value* l = left->codegen_get_value(); + if (left->on_stack) + c_SP(-1); + llvm::Value* r = right->codegen_get_value(); + if (right->on_stack) + c_SP(-1); + return std::make_pair(l, r); +} + +llvm::Value* EqExpression::codegen_get_value(){ + llvm::Value* ret; + if ((t >= T_BOOLEAN && t <= T_LONG) || t == T_POINTER){ + auto op = codegen_operands(); + ret = builder->CreateICmpEQ(op.first, op.second); + } else if (t == T_DATE){ + auto op = codegen_operands(); + ret = builder->CreateAnd( + builder->CreateICmpEQ(extract_value(op.first, 0), extract_value(op.second, 0)), + builder->CreateICmpEQ(extract_value(op.first, 1), extract_value(op.second, 1)) + ); + } else if (t == T_NULL){ + //FIXME gör några unit tests på den här biten + if (left->type == T_NULL && right->type == T_NULL){ + ret = getInteger(1, true); + } else { + Expression* testexpr = left->type == T_NULL ? right : left; + llvm::Value* val = testexpr->codegen_get_value(); + switch(testexpr->type){ + case T_STRING: + case T_CSTRING: + ret = builder->CreateICmpEQ(extract_value(val, 3), getInteger(32, 0)); + if (testexpr->type == T_STRING) + release(val, T_STRING); + break; + case T_DATE: + ret = builder->CreateICmpEQ(builder->CreateOr(extract_value(val, 0), extract_value(val, 1)), getInteger(32, 0)); + break; + case T_POINTER: + ret = builder->CreateICmpEQ(val, get_nullptr()); + break; + case T_VARIANT: { + llvm::Value* vtype = extract_value(val, 0); + ret = gen_if_phi(getInteger(1, true), builder->CreateICmpNE(vtype, getInteger(TARGET_BITS, T_NULL)), [&](){ + if (TARGET_BITS == 64){ + llvm::Value* date_str_ptr_obj = builder->CreateOr( + builder->CreateAnd( + builder->CreateICmpUGE(vtype, getInteger(TARGET_BITS, T_DATE)), + builder->CreateICmpULE(vtype, getInteger(TARGET_BITS, T_POINTER)) + ), + builder->CreateICmpUGE(vtype, getInteger(TARGET_BITS, T_OBJECT)) + ); + llvm::Value* zero_data = builder->CreateICmpEQ(extract_value(val, 1), getInteger(64, 0)); + return builder->CreateSelect(date_str_ptr_obj, zero_data, getInteger(1, false)); + } else { + llvm::Value* vdata = extract_value(val, 1); + llvm::Value* ptr_is_null = builder->CreateICmpEQ(builder->CreateTrunc(vdata, llvmType(getInt32Ty)), getInteger(32, 0)); + return (llvm::Value*)gen_if_phi(ptr_is_null, builder->CreateICmpNE(vtype, getInteger(TARGET_BITS, T_STRING)), [&](){ + llvm::Value* zero_date = builder->CreateICmpEQ(vdata, getInteger(64, 0)); + return gen_if_phi(zero_date, builder->CreateICmpNE(vtype, getInteger(TARGET_BITS, T_DATE)), [&](){ + return builder->CreateSelect( + builder->CreateOr( + builder->CreateICmpEQ(vtype, getInteger(TARGET_BITS, T_POINTER)), + builder->CreateICmpUGE(vtype, getInteger(TARGET_BITS, T_OBJECT)) + ), + ptr_is_null, getInteger(1, false) + ); + }); + }); + } + }); + gen_if(builder->CreateXor(ret, getInteger(1, 1)), [&](){ + release(val, T_VARIANT); + }); + break; + } + default: + if (TYPE_is_object(testexpr->type)){ + llvm::Value* object = extract_value(val, 1); + ret = builder->CreateICmpEQ(object, get_nullptr()); + unref_object(object); + } else { + ret = getInteger(1, false); + } + break; + } + if (left->on_stack) + c_SP(-1); + if (right->on_stack) + c_SP(-1); + } + } else if (t == T_STRING || t == T_CSTRING){ + auto op = codegen_operands(); + llvm::Value* len1 = extract_value(op.first, 3); + llvm::Value* len2 = extract_value(op.second, 3); + + llvm::Value *ret1, *ret2; + llvm::BasicBlock *BB1, *BB2; + + gen_if_else(builder->CreateICmpNE(len1, len2), [&](){ + ret1 = getInteger(1, false); + BB1 = builder->GetInsertBlock(); + }, [&](){ + llvm::Value* addr1 = extract_value(op.first, 1); + llvm::Value* addr2 = extract_value(op.second, 1); + llvm::Value* offset1 = extract_value(op.first, 2); + llvm::Value* offset2 = extract_value(op.second, 2); + if (TARGET_BITS == 64){ + offset1 = builder->CreateZExt(offset1, llvmType(getInt64Ty)); + offset2 = builder->CreateZExt(offset2, llvmType(getInt64Ty)); + len1 = builder->CreateZExt(len1, llvmType(getInt64Ty)); + } + addr1 = builder->CreateGEP(addr1, offset1); + addr2 = builder->CreateGEP(addr2, offset2); + ret2 = builder->CreateICmpEQ(builder->CreateCall3(get_global_function(memcmp, 'i', "ppj"), addr1, addr2, len1), getInteger(32, 0)); + BB2 = builder->GetInsertBlock(); + }, "strcomp_not_same_length", "strcomp_same_length", "strcomp_done"); + + llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2); + phi->addIncoming(ret1, BB1); + phi->addIncoming(ret2, BB2); + ret = phi; + + release(op.first, left->type); + release(op.second, right->type); + } else if (t == T_SINGLE || t == T_FLOAT){ + auto op = codegen_operands(); + ret = builder->CreateFCmpUEQ(op.first, op.second); + } else if (t == T_OBJECT){ + llvm::Value* l = left->codegen_get_value(); + llvm::Value* r = right->codegen_get_value(); + llvm::Value* sp = read_sp();//builder->CreateBitCast(read_sp(), pointer_t(value_types[T_OBJECT])); + llvm::Value* laddr = builder->CreateGEP(sp, getInteger(TARGET_BITS, -2));//create_gep(sp, TARGET_BITS, -2, 32, 1); + llvm::Value* raddr = builder->CreateGEP(sp, getInteger(TARGET_BITS, -1));//create_gep(sp, TARGET_BITS, -1, 32, 1); + laddr = create_gep(builder->CreateBitCast(laddr, pointer_t(value_types[T_OBJECT])), TARGET_BITS, 0, 32, 1); + raddr = create_gep(builder->CreateBitCast(raddr, pointer_t(value_types[T_OBJECT])), TARGET_BITS, 0, 32, 1); + + llvm::Value* ret = builder->CreateCall2(get_global_function_jif(COMPARE_object, 'i', "pp"), + builder->CreateBitCast(laddr, llvmType(getInt8PtrTy)), + builder->CreateBitCast(raddr, llvmType(getInt8PtrTy))); + ret = builder->CreateICmpEQ(ret, getInteger(32, 0)); + + unref_object(extract_value(l, 1)); + unref_object(extract_value(r, 1)); + + c_SP(-2); + + if (on_stack) + push_value(ret, T_BOOLEAN); + return ret; + } else if (t == T_VARIANT){ + left->codegen_on_stack(); + right->codegen_on_stack(); + builder->CreateCall(get_global_function(JR_variant_equal, 'v', "")); + return ret_top_stack(T_BOOLEAN, on_stack); + } + if (on_stack) + push_value(ret, T_BOOLEAN); + return ret; +} + +static llvm::Value* is_empty_string(llvm::Value* str){ + //Null test of address should not be needed. Length check should be enough... + llvm::Value* len = extract_value(str, 3); + return builder->CreateICmpEQ(len, getInteger(32, 0)); +} + +llvm::Value* NotExpression::codegen_get_value(){ + if (expr->type == T_NULL){ + if (on_stack) + push_value(getInteger(1, true), T_BOOLEAN); + return getInteger(1, true); + } else if (TYPE_is_variant(expr->type)){ + expr->codegen_on_stack(); + builder->CreateCall(get_global_function_jif(SUBR_not, 'v', "h"), getInteger(16, 0/*T_VARIANT*/)); + return ret_top_stack(T_VARIANT, true); + } else { + llvm::Value* op = expr->codegen_get_value(); + if (expr->on_stack) + c_SP(-1); + llvm::Value* ret; + if (expr->type <= T_LONG){ + static const int bits[] = {0, 1, 8, 16, 32, 64}; + ret = builder->CreateXor(op, getInteger(bits[expr->type], -1)); + } else if (TYPE_is_string(expr->type)){ + ret = is_empty_string(op); + release(op, expr->type); + } else if (TYPE_is_object(expr->type)){ + ret = builder->CreateICmpEQ(extract_value(op, 1), get_nullptr()); + release(op, expr->type); + } + if (on_stack) + push_value(ret, T_BOOLEAN); + return ret; + } +} + +static llvm::Value* LessDate(llvm::Value* date_left, llvm::Value* date_right){ + llvm::Value* d1 = extract_value(date_left, 0); + llvm::Value* d2 = extract_value(date_right, 0); + llvm::Value* t1 = extract_value(date_left, 1); + llvm::Value* t2 = extract_value(date_right, 1); + + llvm::BasicBlock *BB1, *BB2, *BB3, *BB4; + llvm::Value* r3; + + gen_if_else(builder->CreateICmpSLT(d1, d2), [&](){ + BB1 = builder->GetInsertBlock(); //true + }, [&](){ + gen_if_else(builder->CreateICmpSGT(d1, d2), [&](){ + BB2 = builder->GetInsertBlock(); //false + }, [&](){ + r3 = builder->CreateICmpSLT(t1, t2); + BB3 = builder->GetInsertBlock(); //r3 + }); + llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2); + phi->addIncoming(getInteger(1, false), BB2); + phi->addIncoming(r3, BB3); + r3 = phi; + BB4 = builder->GetInsertBlock(); + }); + + llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2); + phi->addIncoming(getInteger(1, true), BB1); + phi->addIncoming(r3, BB4); + return phi; +} + +llvm::Value* LessExpression::codegen_get_value(){ + if (t == T_VARIANT){ + left->codegen_on_stack(); + right->codegen_on_stack(); + builder->CreateCall(get_global_function(JR_variant_compi_less_than, 'v', "")); + return ret_top_stack(T_BOOLEAN, true); + } else { + auto op = codegen_operands(); + llvm::Value* ret; + if (t <= T_LONG || t == T_POINTER){ + ret = builder->CreateICmpSLT(op.first, op.second); + } else if (t == T_DATE){ + ret = LessDate(op.first, op.second); + } else if (t == T_SINGLE || t == T_FLOAT){ + ret = builder->CreateFCmpULT(op.first, op.second); + } /*else if (TYPE_is_object(t)){ + //FIXME the interpreter does not allow this .. + abort(); + } */else if (TYPE_is_string(t)){ + llvm::Value* addr1 = extract_value(op.first, 1); + llvm::Value* addr2 = extract_value(op.second, 1); + llvm::Value* offset1 = extract_value(op.first, 2); + llvm::Value* offset2 = extract_value(op.second, 2); + llvm::Value* len1 = extract_value(op.first, 3); + llvm::Value* len2 = extract_value(op.second, 3); + if (TARGET_BITS == 64){ + offset1 = builder->CreateZExt(offset1, llvmType(getInt64Ty)); + offset2 = builder->CreateZExt(offset2, llvmType(getInt64Ty)); + } + addr1 = builder->CreateGEP(addr1, offset1); + addr2 = builder->CreateGEP(addr2, offset2); + ret = builder->CreateICmpEQ(builder->CreateCall4(get_global_function_jif(STRING_compare, 'i', "pipi"), addr1, len1, addr2, len2), getInteger(32, -1)); + release(op.first, left->type); + release(op.second, right->type); + } + if (on_stack) + push_value(ret, T_BOOLEAN); + + return ret; + } +} + +llvm::Value* NearExpression::codegen_get_value(){ + auto op = codegen_operands(); + llvm::Value* ret; + + llvm::Value* len1 = extract_value(op.first, 3); + llvm::Value* len2 = extract_value(op.second, 3); + + llvm::Value *ret1, *ret2; + llvm::BasicBlock *BB1, *BB2; + + gen_if_else(builder->CreateICmpNE(len1, len2), [&](){ + ret1 = getInteger(1, false); + BB1 = builder->GetInsertBlock(); + }, [&](){ + llvm::Value* addr1 = extract_value(op.first, 1); + llvm::Value* addr2 = extract_value(op.second, 1); + llvm::Value* offset1 = extract_value(op.first, 2); + llvm::Value* offset2 = extract_value(op.second, 2); + if (TARGET_BITS == 64){ + offset1 = builder->CreateZExt(offset1, llvmType(getInt64Ty)); + offset2 = builder->CreateZExt(offset2, llvmType(getInt64Ty)); + } + addr1 = builder->CreateGEP(addr1, offset1); + addr2 = builder->CreateGEP(addr2, offset2); + ret2 = builder->CreateICmpNE(builder->CreateCall3(get_global_function_jif(STRING_equal_ignore_case_same, 'c', "ppi"), addr1, addr2, len1), getInteger(8, 0)); + BB2 = builder->GetInsertBlock(); + }, "strcomp_not_same_length", "strcomp_same_length", "strcomp_done"); + + llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2); + phi->addIncoming(ret1, BB1); + phi->addIncoming(ret2, BB2); + ret = phi; + + release(op.first, left->type); + release(op.second, right->type); + + if (on_stack) + push_value(ret, T_BOOLEAN); + + return ret; +} + +llvm::Value* AddExpression::codegen_get_value(){ + if (type == T_VARIANT){ + left->codegen_on_stack(); + right->codegen_on_stack(); + builder->CreateCall(get_global_function(JR_add, 'v', "h"), getInteger(16, 0)); + return ret_top_stack(T_VARIANT, true); + } else { + auto op = codegen_operands(); + llvm::Value* ret; + + if (type == T_BOOLEAN){ + ret = builder->CreateOr(op.first, op.second); + } else if (type <= T_LONG || type == T_POINTER){ + ret = builder->CreateAdd(op.first, op.second); + } else /*if (type <= T_FLOAT)*/{ + ret = builder->CreateFAdd(op.first, op.second); + } + if (on_stack) + push_value(ret, type); + return ret; + } +} + +llvm::Value* SubExpression::codegen_get_value(){ + if (type == T_VARIANT){ + left->codegen_on_stack(); + right->codegen_on_stack(); + builder->CreateCall(get_global_function(JR_sub, 'v', "h"), getInteger(16, 0)); + return ret_top_stack(T_VARIANT, true); + } else { + auto op = codegen_operands(); + llvm::Value* ret; + + if (type == T_BOOLEAN){ + ret = builder->CreateXor(op.first, op.second); + } else if (type <= T_LONG || type == T_POINTER){ + ret = builder->CreateSub(op.first, op.second); + } else /*if (type <= T_FLOAT)*/{ + ret = builder->CreateFSub(op.first, op.second); + } + if (on_stack) + push_value(ret, type); + return ret; + } +} + +llvm::Value* MulExpression::codegen_get_value(){ + if (type == T_VARIANT){ + left->codegen_on_stack(); + right->codegen_on_stack(); + builder->CreateCall(get_global_function(JR_mul, 'v', "h"), getInteger(16, 0)); + return ret_top_stack(T_VARIANT, true); + } else { + auto op = codegen_operands(); + llvm::Value* ret; + + if (type == T_BOOLEAN){ + ret = builder->CreateAnd(op.first, op.second); + } else if (type <= T_LONG || type == T_POINTER){ + ret = builder->CreateMul(op.first, op.second); + } else /*if (type <= T_FLOAT)*/{ + ret = builder->CreateFMul(op.first, op.second); + } + if (on_stack) + push_value(ret, type); + return ret; + } +} + +llvm::Value* DivExpression::codegen_get_value(){ + auto op = codegen_operands(); + + gen_if_noreturn(builder->CreateFCmpUEQ(op.second, getFloat(0.0)), [&](){ + create_throw(E_ZERO); + }, "div_zero", "not_div_zero"); + + llvm::Value* ret = builder->CreateFDiv(op.first, op.second); + + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* QuoExpression::codegen_get_value(){ + auto op = codegen_operands(); + llvm::Value* ret; + if (type == T_BOOLEAN){ + gen_if_noreturn(builder->CreateICmpEQ(op.second, getInteger(1, false)), [&](){ + create_throw(E_ZERO); + }, "div_zero", "not_div_zero"); + ret = op.first; + } else { + static const int bits[] = {0, 1, 8, 16, 32, 64}; + gen_if_noreturn(builder->CreateICmpEQ(op.second, getInteger(bits[type], 0)), [&](){ + create_throw(E_ZERO); + }, "div_zero", "not_div_zero"); + if (type == T_BYTE) + ret = builder->CreateUDiv(op.first, op.second); + else + ret = builder->CreateSDiv(op.first, op.second); + } + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* RemExpression::codegen_get_value(){ + auto op = codegen_operands(); + llvm::Value* ret; + if (type == T_BOOLEAN){ + gen_if_noreturn(builder->CreateICmpEQ(op.second, getInteger(1, false)), [&](){ + create_throw(E_ZERO); + }, "div_zero", "not_div_zero"); + ret = op.first; + } else { + static const int bits[] = {0, 1, 8, 16, 32, 64}; + gen_if_noreturn(builder->CreateICmpEQ(op.second, getInteger(bits[type], 0)), [&](){ + create_throw(E_ZERO); + }, "div_zero", "not_div_zero"); + if (type == T_BYTE) + ret = builder->CreateURem(op.first, op.second); + else + ret = builder->CreateSRem(op.first, op.second); + } + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* PowExpression::codegen_get_value(){ + auto op = codegen_operands(); + llvm::Value* func; + if (right->type == T_INTEGER){ + llvm::Type* types[] = {llvmType(getDoubleTy)}; + func = llvm::Intrinsic::getDeclaration(M, llvm::Intrinsic::powi, types); + //func = get_global_function(__powidf2, 'd', "di"); + //func = M->getOrInsertFunction("llvm.powi.f64", get_function_type('d', "di")); + } else { + func = M->getOrInsertFunction("llvm.pow.f64", get_function_type('d', "dd")); + } + llvm::Value* ret = builder->CreateCall2(func, op.first, op.second); + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* AndExpression::codegen_get_value(){ + if (type == T_VARIANT){ + left->codegen_on_stack(); + right->codegen_on_stack(); + builder->CreateCall(get_global_function_jif(SUBR_and_, 'v', "h"), getInteger(16, C_AND)); + return ret_top_stack(T_VARIANT, on_stack); + } else { + auto op = codegen_operands(); + llvm::Value* ret = builder->CreateAnd(op.first, op.second); + if (on_stack) + push_value(ret, type); + return ret; + } +} + +llvm::Value* OrExpression::codegen_get_value(){ + if (type == T_VARIANT){ + left->codegen_on_stack(); + right->codegen_on_stack(); + builder->CreateCall(get_global_function_jif(SUBR_and_, 'v', "h"), getInteger(16, C_OR)); + return ret_top_stack(T_VARIANT, on_stack); + } else { + auto op = codegen_operands(); + llvm::Value* ret = builder->CreateOr(op.first, op.second); + if (on_stack) + push_value(ret, type); + return ret; + } +} + +llvm::Value* XorExpression::codegen_get_value(){ + if (type == T_VARIANT){ + left->codegen_on_stack(); + right->codegen_on_stack(); + builder->CreateCall(get_global_function_jif(SUBR_and_, 'v', "h"), getInteger(16, C_XOR)); + return ret_top_stack(T_VARIANT, on_stack); + } else { + auto op = codegen_operands(); + llvm::Value* ret = builder->CreateXor(op.first, op.second); + if (on_stack) + push_value(ret, type); + return ret; + } +} + +llvm::Value* IsExpression::codegen_get_value(){ + llvm::Value* obj = left->codegen_get_value(); + if (left->on_stack) + c_SP(-1); + + llvm::Value* addr = extract_value(obj, 1); + + llvm::BasicBlock *BB1, *BB2, *BB3, *BB4; + llvm::Value* ret; + + BB1 = builder->GetInsertBlock(); //false + gen_if(builder->CreateICmpNE(addr, get_nullptr()), [&](){ + llvm::Value* OBJ = builder->CreateBitCast(addr, pointer_t(OBJECT_type)); + llvm::Value* obj_klass = load_element(OBJ, 0); + + PushClassExpression* pce = dyn_cast(right); + assert(pce); + gen_if_else(builder->CreateICmpEQ(builder->CreateIntToPtr(getInteger(TARGET_BITS, (intptr_t)(void*)pce->klass), llvmType(getInt8PtrTy)), obj_klass), [&](){ + BB2 = builder->GetInsertBlock(); //true + }, [&](){ + ret = builder->CreateCall2(get_global_function_jif(CLASS_inherits, 'c', "pp"), obj_klass, builder->CreateIntToPtr(getInteger(TARGET_BITS, (intptr_t)(void*)pce->klass), llvmType(getInt8PtrTy))); + ret = builder->CreateICmpNE(ret, getInteger(8, false)); + BB3 = builder->GetInsertBlock(); + }); + llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2); + phi->addIncoming(getInteger(1, true), BB2); + phi->addIncoming(ret, BB3); + ret = phi; + + unref_object(addr); + + BB4 = builder->GetInsertBlock(); + }); + llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt1Ty), 2); + phi->addIncoming(getInteger(1, false), BB1); + phi->addIncoming(ret, BB4); + ret = phi; + + if (on_stack) + push_value(ret, type); + return ret; +} + +llvm::Value* NegExpression::codegen_get_value(){ + if (type == T_VARIANT){ + llvm::Value* val = expr->codegen_get_value(); + c_SP(-expr->on_stack); + llvm::Value* vtype = extract_value(val, 0); + llvm::Value* data = extract_value(val, 1); + + llvm::BasicBlock* bb[8] = { + create_bb("else"), + create_bb("bool"), + create_bb("byte"), + create_bb("short"), + create_bb("integer"), + create_bb("long"), + create_bb("single"), + create_bb("float") + }; + llvm::BasicBlock* done_block = create_bb("done_neg"); + + llvm::SwitchInst* si = builder->CreateSwitch(vtype, bb[0], 7); + for(int i=1; i<8; i++) + si->addCase(getInteger(TARGET_BITS, i), bb[i]); + + llvm::Value* res[8]; + + builder->SetInsertPoint(bb[1]); //bool + res[1] = data; + builder->CreateBr(done_block); + + builder->SetInsertPoint(bb[2]); //byte + res[2] = builder->CreateZExt( + builder->CreateSub(getInteger(8, 0), builder->CreateTrunc(data, llvmType(getInt8Ty))), + llvmType(getInt64Ty) + ); + builder->CreateBr(done_block); + + builder->SetInsertPoint(bb[3]); //short + res[3] = builder->CreateSExt( + builder->CreateSub(getInteger(16, 0), builder->CreateTrunc(data, llvmType(getInt16Ty))), + llvmType(getInt64Ty) + ); + builder->CreateBr(done_block); + + builder->SetInsertPoint(bb[4]); //integer + res[4] = builder->CreateSExt( + builder->CreateSub(getInteger(32, 0), builder->CreateTrunc(data, llvmType(getInt32Ty))), + llvmType(getInt64Ty) + ); + builder->CreateBr(done_block); + + builder->SetInsertPoint(bb[5]); //long + res[5] = builder->CreateSub(getInteger(64, 0), data); + builder->CreateBr(done_block); + + /* %1 = trunc i64 %data to i32 + %2 = bitcast i32 %1 to float + %3 = fsub float -0.000000e+00, %2 + %4 = bitcast float %3 to i32 + %5 = zext i32 %4 to i64 + ret i64 %5 */ + builder->SetInsertPoint(bb[6]); //single + res[6] = builder->CreateFSub(getFloat(0.0f), builder->CreateBitCast(builder->CreateTrunc(data, llvmType(getInt32Ty)), llvmType(getFloatTy))); + res[6] = builder->CreateZExt(builder->CreateBitCast(res[6], llvmType(getInt32Ty)), llvmType(getInt64Ty)); + builder->CreateBr(done_block); + + builder->SetInsertPoint(bb[7]); //float + res[7] = builder->CreateBitCast(builder->CreateFSub(getFloat(0.0), builder->CreateBitCast(data, llvmType(getDoubleTy))), llvmType(getInt64Ty)); + builder->CreateBr(done_block); + + builder->SetInsertPoint(bb[0]); //else + release(val, T_VARIANT); + create_throw(E_TYPE, "Number", "(unknown)"); + + builder->SetInsertPoint(done_block); + llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt64Ty), 7, "variant_neg_result"); + for(int i=1; i<8; i++){ + phi->addIncoming(res[i], bb[i]); + } + + llvm::Value* ret = get_new_struct(variant_type, vtype, phi); + + if (on_stack) + push_value(ret, type); + return ret; + } else { + llvm::Value* val = expr->codegen_get_value(); + if (expr->on_stack) + c_SP(-1); + llvm::Value* ret; + if (type == T_BOOLEAN){ + ret = val; + } else if (type <= T_LONG){ + static const int bits[] = {0, 1, 8, 16, 32, 64}; + ret = builder->CreateSub(getInteger(bits[type], 0), val); + } else { + ret = builder->CreateFSub(type == T_SINGLE ? getFloat(0.0f) : getFloat(0.0), val); + } + if (on_stack) + push_value(ret, type); + return ret; + } +} + +llvm::Value* CatExpression::codegen_get_value(){ + codegen_on_stack(); + return ret_top_stack(T_STRING, on_stack); +} +void CatExpression::codegen_on_stack(){ + for(size_t i=0, e=args.size(); i!=e; i++){ + args[i]->codegen_on_stack(); + } + builder->CreateCall(get_global_function_jif(SUBR_cat, 'v', "h"), getInteger(16, args.size())); +} + +llvm::Value* FileExpression::codegen_get_value(){ + codegen_on_stack(); + return ret_top_stack(T_STRING, on_stack); +} +void FileExpression::codegen_on_stack(){ + for(size_t i=0, e=args.size(); i!=e; i++){ + args[i]->codegen_on_stack(); + } + builder->CreateCall(get_global_function_jif(SUBR_file, 'v', "h"), getInteger(16, args.size())); +} + +llvm::Value* LikeExpression::codegen_get_value(){ + codegen_on_stack(); + return ret_top_stack(T_BOOLEAN, on_stack); +} +void LikeExpression::codegen_on_stack(){ + left->codegen_on_stack(); + right->codegen_on_stack(); + builder->CreateCall(get_global_function_jif(SUBR_like, 'v', "h"), getInteger(16, kind)); +} + +llvm::Value* CheckStringExpression::codegen_get_value(){ + llvm::Value* val = expr->codegen_get_value(); + llvm::Value* vtype = extract_value(val, 0); + llvm::Value* vdata = extract_value(val, 1); + + llvm::Value* nullstring = get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)); + + llvm::Value* ret = gen_if_phi(nullstring, builder->CreateICmpNE(vtype, getInteger(TARGET_BITS, T_NULL)), [&](){ + gen_if_noreturn(builder->CreateICmpNE(vtype, getInteger(TARGET_BITS, T_STRING)), [&](){ + //codegen_printf("okända typen: %ld\n", vtype); + create_throw(E_TYPE, JIF.F_TYPE_get_name(T_STRING), "(unknown)"); + }); + llvm::Value* strptr = builder->CreateIntToPtr(vdata, llvmType(getInt8PtrTy)); + llvm::Value* len = builder->CreateLoad(builder->CreateGEP(builder->CreateBitCast(strptr, llvmType(getInt32PtrTy)), getInteger(TARGET_BITS, -1))); + return get_new_struct(string_type, getInteger(TARGET_BITS, T_STRING), strptr, getInteger(32, 0), len); + }); + + c_SP(-expr->on_stack); + + if (on_stack) + push_value(ret, type); + return ret; +} +llvm::Value* CheckIntegerExpression::codegen_get_value(){ + llvm::Value* ret = expr->codegen_get_value(); + llvm::Value* vtype = extract_value(ret, 0); + llvm::Value* data = extract_value(ret, 1); + llvm::Value* not_ok = builder->CreateICmpUGT(vtype, getInteger(TARGET_BITS, T_INTEGER)); + + c_SP(-expr->on_stack); + + gen_if_noreturn(not_ok, [&](){ + release(ret, T_VARIANT); + create_throw(E_TYPE, JIF.F_TYPE_get_name(T_INTEGER), "(unknown)"); + }); + + ret = builder->CreateTrunc(data, llvmType(getInt32Ty)); + if (on_stack) + push_value(ret, T_INTEGER); + return ret; +} +llvm::Value* CheckFloatExpression::codegen_get_value(){ + llvm::Value* ret = expr->codegen_get_value(); + llvm::Value* vtype = extract_value(ret, 0); + llvm::Value* data = extract_value(ret, 1); + llvm::Value* not_ok = builder->CreateICmpUGT(vtype, getInteger(TARGET_BITS, T_FLOAT)); + + c_SP(-expr->on_stack); + + gen_if_noreturn(not_ok, [&](){ + release(ret, T_VARIANT); + create_throw(E_TYPE, JIF.F_TYPE_get_name(T_INTEGER), "(unknown)"); + }); + + llvm::Value* low32 = builder->CreateTrunc(data, llvmType(getInt32Ty)); + llvm::Value* float_from_int = builder->CreateSIToFP(low32, llvmType(getDoubleTy)); + + ret = gen_if_phi(float_from_int, builder->CreateICmpSGT(vtype, getInteger(TARGET_BITS, T_INTEGER)), [&](){ + llvm::Value* float_data = builder->CreateBitCast(data, llvmType(getDoubleTy)); + llvm::Value* single_data = builder->CreateFPExt(builder->CreateBitCast(low32, llvmType(getFloatTy)), llvmType(getDoubleTy)); + llvm::Value* long_data = builder->CreateSIToFP(data, llvmType(getDoubleTy)); + return builder->CreateSelect( + builder->CreateICmpEQ(vtype, getInteger(TARGET_BITS, T_FLOAT)), + float_data, + builder->CreateSelect(builder->CreateICmpEQ(vtype, getInteger(TARGET_BITS, T_SINGLE)), single_data, long_data)); + }); + + if (on_stack) + push_value(ret, T_FLOAT); + return ret; +} +llvm::Value* CheckPointerExpression::codegen_get_value(){ + llvm::Value* ret = expr->codegen_get_value(); + llvm::Value* vtype = extract_value(ret, 0); + llvm::Value* data = extract_value(ret, 1); + llvm::Value* not_ok = builder->CreateICmpNE(vtype, getInteger(TARGET_BITS, T_POINTER)); + + c_SP(-expr->on_stack); + + gen_if_noreturn(not_ok, [&](){ + release(ret, T_VARIANT); + create_throw(E_TYPE, JIF.F_TYPE_get_name(T_POINTER), "(unknown)"); + }); + + ret = builder->CreateIntToPtr(data, llvmType(getInt8PtrTy)); + if (on_stack) + push_value(ret, T_POINTER); + return ret; +} + +static llvm::Value* gen_max(llvm::Value* val1, llvm::Value* val2){ + return builder->CreateSelect(builder->CreateICmpSLT(val1, val2), val2, val1); +} + +static llvm::Value* gen_min(llvm::Value* val1, llvm::Value* val2){ + return builder->CreateSelect(builder->CreateICmpSLT(val1, val2), val1, val2); +} + +static llvm::Value* gen_minmax(llvm::Value* val, llvm::Value* lo, llvm::Value* hi){ + llvm::BasicBlock* BB1 = builder->GetInsertBlock(); + llvm::BasicBlock* BB2 = create_bb("minmax1"); + llvm::BasicBlock* BB3 = create_bb("minmax2"); + llvm::Value* cmp = builder->CreateICmpSLT(val, lo); + builder->CreateCondBr(cmp, BB3, BB2); + + builder->SetInsertPoint(BB2); + llvm::Value* cmp2 = builder->CreateICmpSGT(val, hi); + llvm::Value* r1 = builder->CreateSelect(cmp2, hi, val); + builder->CreateBr(BB3); + + builder->SetInsertPoint(BB3); + llvm::PHINode* phi = builder->CreatePHI(val->getType(), 2); + phi->addIncoming(lo, BB1); + phi->addIncoming(r1, BB2); + + return phi; +} + +static llvm::Value* get_string(llvm::Value* str){ + llvm::Value* ptr = extract_value(str, 1); + return builder->CreateGEP(ptr, to_target_int(extract_value(str, 2))); +} + +static std::pair get_string_len(llvm::Value* str){ + llvm::Value* ptr = extract_value(str, 1); + ptr = builder->CreateGEP(ptr, to_target_int(extract_value(str, 2))); + return std::pair(ptr, extract_value(str, 3)); +} + +static llvm::Value* codegen_spec_method(CLASS_DESC* desc, int index, bool dispatch, bool can_quick, bool object_on_stack, CLASS* klass, llvm::Value* effective_class, llvm::Value* obj, int nargs, bool noret){ + llvm::Value* is_native; + llvm::Value* desc_method; + llvm::Value* desc_exec; + llvm::Value* desc_class; + if (dispatch){ + int offset_table = TARGET_BITS == 64 ? 40/8 : 28/4; + int offset_desc_in_desc_symbol = TARGET_BITS == 64 ? 12 : 8; + int offset_native_flag = TARGET_BITS == 64 ? 35 : 19; + //table_addr = (char*)effective_class->table + llvm::Value* table_addr = builder->CreateLoad(builder->CreateGEP(builder->CreateBitCast(effective_class, pointer_t(llvmType(getInt8PtrTy))), getInteger(TARGET_BITS, offset_table))); + //desc_addr_addr = (char*)(table_addr + sizeof(CLASS_DESC_SYMBOL) * index + desc_offset) + llvm::Value* desc_addr_addr = builder->CreateGEP(table_addr, getInteger(TARGET_BITS, sizeof(CLASS_DESC_SYMBOL) * index + offset_desc_in_desc_symbol)); + //desc_addr = *(char**)desc_addr_addr + llvm::Value* desc_addr = builder->CreateLoad(builder->CreateBitCast(desc_addr_addr, pointer_t(llvmType(getInt8PtrTy)))); + //native_flag_addr = (char*)(desc_addr + offset_native_flag) + llvm::Value* native_flag_addr = builder->CreateGEP(desc_addr, getInteger(TARGET_BITS, offset_native_flag)); + //native_subr_flag = *native_flag_addr + llvm::Value* native_subr_flag = builder->CreateLoad(native_flag_addr); + //native_flag = (bool)(native_subr_flag & 1) + llvm::Value* native_flag = builder->CreateTrunc(native_subr_flag, llvmType(getInt1Ty)); + //subr_flag = (bool)(native_subr_flag & 2) + //llvm::Value* subr_flag = builder->CreateICmpNE(builder->CreateAnd(native_subr_flag, getInteger(8, 2)), getInteger(8, 0)); + + //llvm::Value* function_kind = builder->CreateSelect(native_flag, builder->CreateSelect(subr_flag, getInteger(8, FUNCTION_SUBR), getInteger(8, FUNCTION_NATIVE)), getInteger(8, FUNCTION_PUBLIC)); + ///Disable subr, because I think it will never be used this way anyway, else it is a (big) performance hit. + is_native = native_flag; + desc_method = desc_addr; + desc_exec = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(desc_addr, getInteger(TARGET_BITS, offsetof(CLASS_DESC_METHOD, exec))), charPP)); + desc_class = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(desc_addr, getInteger(TARGET_BITS, offsetof(CLASS_DESC_METHOD, klass))), charPP)); + + if (!desc->method.native){ + //Assume native classes don't inherit non-native ones :) + is_native = getInteger(1, false); + } + } else { + is_native = getInteger(1, desc->method.native); + desc_method = get_global((void*)&desc->method, llvmType(getInt8Ty)); + desc_exec = get_global((void*)desc->method.exec, llvmType(getInt8Ty)); + desc_class = get_global((void*)desc->method.klass, llvmType(getInt8Ty)); + } + + llvm::Value* dummy = getInteger(32, 0); + + llvm::Value* ret = gen_if_else_phi(is_native, [&](){ + if (can_quick){ + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(EXEC_call_native, 'c', "ppjp"), + desc_exec, + obj, + getInteger(TARGET_BITS, desc->method.type), + builder->CreateBitCast(builder->CreateGEP(read_sp(), getInteger(TARGET_BITS, -nargs)), llvmType(getInt8PtrTy))); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, false)), [&](){ + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + + llvm::Value* ret = dummy; + if (!noret){ + ret = read_value(get_global(&TEMP, value_type), desc->method.type); + builder->CreateStore(getInteger(TARGET_BITS, T_VOID), get_global(&TEMP, LONG_TYPE)); + borrow(ret, desc->method.type); + } + + if (nargs > 0){ + builder->CreateCall2(get_global_function_jif(RELEASE_many, 'v', "pi"), + builder->CreateBitCast(read_sp(), llvmType(getInt8PtrTy)), getInteger(32, nargs)); + c_SP(-nargs); + } + unref_object(obj); + if (object_on_stack) + c_SP(-1); + if (!noret) + push_value(ret, desc->method.type); + return ret; + } else { + builder->CreateStore(desc_class, get_global((void*)&EXEC.klass)); + builder->CreateStore(obj, get_global((void*)&EXEC.object)); + builder->CreateStore(getInteger(32, nargs), get_global((void*)&EXEC.nparam, llvmType(getInt32Ty))); + + builder->CreateStore(desc_method, get_global((void*)&EXEC.desc)); + builder->CreateStore(getInteger(8, false), get_global((void*)&EXEC.use_stack, llvmType(getInt8Ty))); + + builder->CreateCall(get_global_function_jif(EXEC_native, 'v' , "")); + + llvm::Value* ret = dummy; + if (!noret) + ret = read_value(get_value_on_top_addr(), desc->method.type); + + unref_object(obj); + if (object_on_stack){ + c_SP(-1); + if (!noret) + store_value(get_value_on_top_addr(), ret, desc->method.type); + } + if (noret) + c_SP(-1); + return ret; + } + }, [&](){ + builder->CreateStore(desc_class, get_global((void*)&EXEC.klass)); + builder->CreateStore(obj, get_global((void*)&EXEC.object)); + builder->CreateStore(getInteger(32, nargs), get_global((void*)&EXEC.nparam, llvmType(getInt32Ty))); + builder->CreateStore(builder->CreatePtrToInt(desc_exec, llvmType(getInt32Ty)), get_global((void*)&EXEC.index, llvmType(getInt32Ty))); + + /*builder->CreateCall2(get_global_function_vararg(printf, 'v', "pi"), + get_global((void*)"%d\n", llvmType(getInt8Ty)), getInteger(32, 5));*/ + + builder->CreateCall(get_global_function_jif(EXEC_function_real, 'v', "")); + + llvm::Value* ret = dummy; + if (!noret){ + ret = read_value(get_global(RP, value_type), desc->method.type); + builder->CreateStore(getInteger(TARGET_BITS, T_VOID), get_global(RP, LONG_TYPE)); + } + unref_object(obj); + if (object_on_stack) + c_SP(-1); + if (!noret) + push_value(ret, desc->method.type); + return ret; + }, "spec_native", "spec_non_native", "spec_done"); + return ret; +} + +struct InlineArrayGetAddrRet { + llvm::Value* element_addr; + CLASS* klass; + CLASS* struct_class; +}; +InlineArrayGetAddrRet inline_array_get_addr(std::vector& args, bool is_push_dynamic){ + CTYPE* ctype; + CLASS* klass; + llvm::Value* array_start; + if (is_push_dynamic){ + ctype = &CP->load->dyn[((PushDynamicExpression*)args[0])->index].type; + klass = CP; + array_start = builder->CreateGEP(current_op, getInteger(TARGET_BITS, ((PushDynamicExpression*)args[0])->offset)); + } else { + ctype = ((ReadVariableExpression*)args[0])->ctype; + klass = ((ReadVariableExpression*)args[0])->klass; + array_start = get_global((void*)((PushStaticExpression*)args[0])->addr, llvmType(getInt8Ty)); + } + + CLASS_ARRAY* ca = klass->load->array[ctype->value]; + int* dim = ca->dim; + int ndim = args.size()-1; + + int dims[ndim]; + memcpy(dims, dim, ndim*sizeof(int)); + dims[ndim-1] = -dims[ndim-1]; + + llvm::Value* index = getInteger(32, 0); + + for(int i=0; icodegen_get_value(); + gen_if_noreturn(builder->CreateICmpUGE(d, getInteger(32, dims[i])), [&](){ + create_throw(E_BOUND); + }); + if (i != 0) + index = builder->CreateMul(index, getInteger(32, dims[i])); + index = builder->CreateAdd(index, d); + + c_SP(-args[i+1]->on_stack); + } + + CLASS* struct_class = NULL; + int element_size; + if (ca->ctype.id == TC_STRUCT){ + //Array of structs + struct_class = klass->load->class_ref[ca->ctype.value]; + element_size = struct_class->size - sizeof(CSTRUCT); + } else { + element_size = TYPE_sizeof_memory(ctype_to_type(&ca->ctype, klass)); + } + + return { + builder->CreateGEP(array_start, to_target_int(builder->CreateMul(index, getInteger(32, element_size)))), + klass, + struct_class + }; +} + +llvm::Value* PushArrayExpression::codegen_get_value(){ + int nargs = args.size(); + + CLASS* klass; + bool is_push_class; + + bool is_push_dynamic = false; + + if ( ((is_push_dynamic = isa(args[0])) && ((PushDynamicExpression*)args[0])->ctype->id == TC_ARRAY) + || + (dynamic_cast(args[0]) && ((ReadVariableExpression*)args[0])->ctype->id == TC_ARRAY) ){ + auto ret = inline_array_get_addr(args, is_push_dynamic); + + llvm::Value* val; + + if (ret.struct_class){ + //Array of structs + llvm::Value* ref_obj; + if (is_push_dynamic) + ref_obj = current_op; + else + ref_obj = get_global((void*)((ReadVariableExpression*)args[0])->klass, llvmType(getInt8Ty)); + + val = builder->CreateCall3(get_global_function_jif(CSTRUCT_create_static, 'p', "ppp"), + ref_obj, + get_global((void*)ret.struct_class, llvmType(getInt8Ty)), + ret.element_addr); + + borrow_object_no_nullcheck(val); + + val = get_new_struct(object_type, get_global((void*)ret.struct_class, llvmType(getInt8Ty)), val); + } else { + val = array_read(ret.element_addr, type); + } + if (on_stack) + push_value(val, type); + return val; + } else if (TYPE_is_pure_object(args[0]->type)){ + klass = (CLASS*)(void*)args[0]->type; + + if (klass->quick_array == CQA_ARRAY){ + llvm::Value* obj = extract_value(args[0]->codegen_get_value(), 1); + llvm::Value* data; + if (args.size() == 2){ + llvm::Value* index = args[1]->codegen_get_value(); + + //We do not check that the dimensions are correct here ... :) + int element_size = TYPE_sizeof_memory(type); + int data_offset = offsetof(CARRAY, data); + int count_offset = offsetof(CARRAY, count); + + make_nullcheck(obj); + + c_SP(-args[0]->on_stack-args[1]->on_stack); + + llvm::Value* count = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, count_offset)), llvmType(getInt32PtrTy))); + data = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, data_offset)), charPP)); + + gen_if_noreturn(builder->CreateICmpUGE(index, count), [&](){ + unref_object_no_nullcheck(obj); + create_throw(E_BOUND); + }, "array_get_out_of_bounds"); + + data = builder->CreateGEP(data, to_target_int(builder->CreateMul(index, getInteger(32, element_size)))); + } else { + args[1]->codegen_on_stack(); + llvm::Value* ind_addr = get_value_on_top_addr(); + for(int i=2; icodegen_on_stack(); + + make_nullcheck(obj); + + c_SP(-args[0]->on_stack-(nargs-1)); + + data = builder->CreateCall3(get_global_function_jif(CARRAY_get_data_multi, 'p', "ppi"), + obj, builder->CreateBitCast(ind_addr, llvmType(getInt8PtrTy)), getInteger(32, nargs-1)); + + gen_if_noreturn(builder->CreateICmpEQ(data, get_nullptr()), [&](){ + unref_object_no_nullcheck(obj); + + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + } + data = array_read(data, type); + + unref_object_no_nullcheck(obj); + + if (on_stack) + push_value(data, type); + return data; + } else if (klass->quick_array == CQA_COLLECTION){ + llvm::Value* obj = extract_value(args[0]->codegen_get_value(), 1); + llvm::Value* key = args[1]->codegen_get_value(); + auto str = get_string_len(key); + + make_nullcheck(obj, [&](){ + if (!args[1]->on_stack) + release(key, T_STRING); + }); + + llvm::Value* sp = builder->CreateGEP(read_sp(), getInteger(TARGET_BITS, -args[0]->on_stack-args[1]->on_stack)); + llvm::Value* sp_int8 = builder->CreateBitCast(sp, llvmType(getInt8PtrTy)); + + /*llvm::Value* is_null =*/ builder->CreateCall4(get_global_function(GB.Collection.Get, 'c', "ppip"), + obj, str.first, str.second, sp_int8); + + //type is always T_VARIANT, not T_NULL + llvm::Value* ret = builder->CreateBitCast(sp, pointer_t(value_types[T_VARIANT])); + ret = get_new_struct(variant_type, load_element(ret, 1), load_element(ret, 2)); + + borrow_variant(ret); + + store_sp(builder->CreateGEP(sp, getInteger(TARGET_BITS, 1))); + + release(key, T_STRING); + unref_object_no_nullcheck(obj); + + return ret; + } + klass = (CLASS*)(void*)args[0]->type; + is_push_class = false; + goto _SPEC_GET_METHOD; + } else if (PushClassExpression* pce = dyn_cast(args[0])){ + //llvm::Value* obj = extract_value(args[0]->codegen_get_value(), 1); + klass = pce->klass; + is_push_class = true; + goto _SPEC_GET_METHOD; + } else { + for(size_t i=0, e=args.size(); i!=e; i++) + args[i]->codegen_on_stack(); + builder->CreateCall(get_global_function_jif(EXEC_push_array, 'v', "h"), getInteger(16, *pc)); + return ret_top_stack(T_VARIANT, on_stack); + } + + _SPEC_GET_METHOD: { + int index = klass->special[SPEC_GET]; + CLASS_DESC* desc = CLASS_get_desc(klass, index); + + llvm::Value* obj = NULL; + llvm::Value* effective_class = NULL; + bool dispatch; + + if (!is_push_class){ + llvm::Value* object = args[0]->codegen_get_value(); + obj = extract_value(object, 1); + + if (!klass->is_virtual && !isa(args[0])){ + make_nullcheck(obj); + + effective_class = load_element(builder->CreateBitCast(obj, pointer_t(OBJECT_type)), 0); + create_check(klass, effective_class, obj); + dispatch = true; + } else { + effective_class = get_global((void*)klass, llvmType(getInt8Ty)); + dispatch = false; + } + } else { + obj = get_nullptr(); + effective_class = get_global((void*)klass, llvmType(getInt8Ty)); + dispatch = false; + } + + for(size_t i=1; icodegen_on_stack(); + + return codegen_spec_method(desc, index, dispatch, can_quick, !is_push_class, klass, effective_class, obj, args.size()-1, false); + } +} + +void PopArrayExpression::codegen(){ + int nargs = args.size(); + + CLASS* klass; + bool is_push_class; + bool is_push_dynamic = false; + + if ( ((is_push_dynamic = isa(args[0])) && ((PushDynamicExpression*)args[0])->ctype->id == TC_ARRAY) + || + (dynamic_cast(args[0]) && ((ReadVariableExpression*)args[0])->ctype->id == TC_ARRAY) ){ + + llvm::Value* addr = inline_array_get_addr(args, is_push_dynamic).element_addr; + llvm::Value* new_val = val->codegen_get_value(); + + release_variable(type, addr); + variable_write(addr, new_val, type); + + c_SP(-val->on_stack); //FIXME what if destructor in release_variable throws an error? + + return; + } else if (TYPE_is_pure_object(args[0]->type)){ + klass = (CLASS*)(void*)args[0]->type; + + if (klass->quick_array == CQA_ARRAY){ + llvm::Value* new_val = val->codegen_get_value(); + + llvm::Value* obj = extract_value(args[0]->codegen_get_value(), 1); + llvm::Value* data; + if (args.size() == 2){ + llvm::Value* index = args[1]->codegen_get_value(); + + //We do not check that the dimensions are correct here ... :) + int element_size = TYPE_sizeof_memory(type); + int data_offset = offsetof(CARRAY, data); + int count_offset = offsetof(CARRAY, count); + + make_nullcheck(obj, [&](){ + release(new_val, val->type); + }); + + c_SP(-val->on_stack-args[0]->on_stack-args[1]->on_stack); //FIXME what if destructor in release_variable throws an error? + + llvm::Value* count = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, count_offset)), llvmType(getInt32PtrTy))); + data = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(obj, getInteger(TARGET_BITS, data_offset)), charPP)); + + gen_if_noreturn(builder->CreateICmpUGE(index, count), [&](){ + release(new_val, val->type); + unref_object_no_nullcheck(obj); + create_throw(E_BOUND); + }, "array_get_out_of_bounds"); + + data = builder->CreateGEP(data, to_target_int(builder->CreateMul(index, getInteger(32, element_size)))); + } else { + args[1]->codegen_on_stack(); + llvm::Value* ind_addr = get_value_on_top_addr(); + for(int i=2; icodegen_on_stack(); + + make_nullcheck(obj, [&](){ + if (!val->on_stack) + release(new_val, val->type); + }); + + c_SP(-val->on_stack-args[0]->on_stack-(nargs-1)); + + data = builder->CreateCall3(get_global_function_jif(CARRAY_get_data_multi, 'p', "ppi"), + obj, builder->CreateBitCast(ind_addr, llvmType(getInt8PtrTy)), getInteger(32, nargs-1)); + + gen_if_noreturn(builder->CreateICmpEQ(data, get_nullptr()), [&](){ + release(new_val, val->type); + unref_object_no_nullcheck(obj); + + builder->CreateCall(get_global_function_jif(ERROR_propagate, 'v', "")); + builder->CreateUnreachable(); + }); + } + release_variable(type, data); + variable_write(data, new_val, type); + + unref_object_no_nullcheck(obj); + + return; + } else if (klass->quick_array == CQA_COLLECTION){ + llvm::Value* value = val->codegen_get_value(); + llvm::Value* object = extract_value(args[0]->codegen_get_value(), 1); + llvm::Value* key = args[1]->codegen_get_value(); + + c_SP(-1-args[0]->on_stack-args[1]->on_stack); + + make_nullcheck(object, [&](){ + release(key, T_STRING); + }); + + gen_if_noreturn(builder->CreateICmpEQ(extract_value(key, 3), getInteger(32, 0)), [&](){ + release(key, T_STRING); + unref_object_no_nullcheck(object); + create_throw(E_VKEY); + }); + + auto str = get_string_len(key); + + builder->CreateCall4(get_global_function(GB.Collection.Set, 'c', "ppip"), + object, str.first, str.second, builder->CreateBitCast(read_sp(), llvmType(getInt8PtrTy))); + + release(value, T_VARIANT); + release(key, T_STRING); + unref_object_no_nullcheck(object); + return; + } + klass = (CLASS*)(void*)args[0]->type; + is_push_class = false; + goto _SPEC_PUT_METHOD; + } else if (PushClassExpression* pce = dyn_cast(args[0])){ + klass = pce->klass; + is_push_class = true; + goto _SPEC_PUT_METHOD; + } else { + val->codegen_on_stack(); + for(size_t i=0, e=args.size(); i!=e; i++) + args[i]->codegen_on_stack(); + builder->CreateCall(get_global_function_jif(EXEC_pop_array, 'v', "h"), getInteger(16, *pc)); + return; + } + + _SPEC_PUT_METHOD: { + int index = klass->special[SPEC_PUT]; + CLASS_DESC* desc = CLASS_get_desc(klass, index); + + llvm::Value* obj = NULL; + llvm::Value* effective_class = NULL; + bool dispatch; + + if (!is_push_class){ + llvm::Value* object = args[0]->codegen_get_value(); + obj = extract_value(object, 1); + + if (!klass->is_virtual && !isa(args[0])){ + make_nullcheck(obj); + + effective_class = load_element(builder->CreateBitCast(obj, pointer_t(OBJECT_type)), 0); + create_check(klass, effective_class, obj); + dispatch = true; + } else { + effective_class = get_global((void*)klass, llvmType(getInt8Ty)); + dispatch = false; + } + } else { + obj = get_nullptr(); + effective_class = get_global((void*)klass, llvmType(getInt8Ty)); + dispatch = false; + } + + val->codegen_on_stack(); + for(size_t i=1; icodegen_on_stack(); + + codegen_spec_method(desc, index, dispatch, can_quick, !is_push_class, klass, effective_class, obj, args.size(), true); + } +} + +#include "jit_codegen_conv.h" + +#define codegen_stack for(size_t i=0, e=args.size(); i!=e; i++){ args[i]->must_on_stack(); args[i]->codegen_on_stack(); } stack_diff -= args.size(); +#define codegen_value for(size_t i=0, e=args.size(); i!=e; i++){ param[i] = args[i]->codegen_get_value(); stack_diff -= args[i]->on_stack; } + +#define SUBR_CODE(func) on_stack = true; builder->CreateCall(get_global_function_jif(func, 'v', "h"), getInteger(16, (digit << 8) | extra)); break; +#define SUBR(func) on_stack = true; builder->CreateCall(get_global_function_jif(func, 'v', "")); break; + +static llvm::Value* create_phi(llvm::Value* v1, llvm::BasicBlock* BB1, llvm::Value* v2, llvm::BasicBlock* BB2){ + llvm::PHINode* phi = builder->CreatePHI(v1->getType(), 2); + phi->addIncoming(v1, BB1); + phi->addIncoming(v2, BB2); + return phi; +} + +llvm::Value* SubrExpression::codegen_get_value(){ + int nargs = args.size(); + llvm::Value* param[nargs]; + + int stack_diff = on_stack; + /*for(int i=0; ion_stack;*/ + + switch(digit){ + case 0x40: { //Left + codegen_value + llvm::Value* string_length = extract_value(param[0], 3); + llvm::Value* val = nargs == 1 ? getInteger(32, 1) : param[1]; + llvm::Value* backwards = builder->CreateAdd(val, string_length); + val = builder->CreateSelect(builder->CreateICmpSLT(val, getInteger(32, 0)), backwards, val); + val = gen_minmax(val, getInteger(32, 0), string_length); + param[0] = insert_value(param[0], val, 3); + c_SP(-(args[0]->on_stack + (nargs == 2 && args[1]->on_stack))); + if (on_stack) + push_value(param[0], args[0]->type); + return param[0]; + } + case 0x42: { //Right + codegen_value + llvm::Value* string_offset = extract_value(param[0], 2); + llvm::Value* string_length = extract_value(param[0], 3); + llvm::Value* val = nargs == 1 ? getInteger(32, 1) : param[1]; + llvm::Value* backwards = builder->CreateAdd(val, string_length); + val = builder->CreateSelect(builder->CreateICmpSLT(val, getInteger(32, 0)), backwards, val); + llvm::Value* len = gen_minmax(val, getInteger(32, 0), string_length); + val = builder->CreateSub(string_length, len); + val = builder->CreateAdd(string_offset, val); + param[0] = insert_value(insert_value(param[0], val, 2), len, 3); + c_SP(-(args[0]->on_stack + (nargs == 2 && args[1]->on_stack))); + if (on_stack) + push_value(param[0], args[0]->type); + return param[0]; + } + case 0x41: { //Mid + codegen_value + llvm::Value* start = builder->CreateSub(param[1], getInteger(32, 1)); + gen_if_noreturn(builder->CreateICmpSLT(start, getInteger(32, 0)), [&](){ + create_throw(E_ARG); + }); + llvm::Value* string_length = extract_value(param[0], 3); + + llvm::BasicBlock* BB1 = create_bb("Mid_return_empty_string"); + llvm::BasicBlock* BB2 = create_bb("cont1"); + llvm::BasicBlock* BB3 = create_bb("cont2"); + llvm::BasicBlock* BB4 = create_bb("cont3"); + + builder->CreateCondBr(builder->CreateICmpSGE(start, string_length), BB1, BB2); + builder->SetInsertPoint(BB2); + + llvm::Value* newlen = nargs == 2 ? string_length : param[2]; + + llvm::Value* string_length_minus_start = builder->CreateSub(string_length, start); + + llvm::Value* newlen_if_negative = gen_max(getInteger(32, 0), + builder->CreateAdd(string_length_minus_start, newlen)); + newlen = builder->CreateSelect(builder->CreateICmpSLT(newlen, getInteger(32, 0)), + newlen_if_negative, newlen); + + newlen = gen_minmax(newlen, getInteger(32, 0), string_length_minus_start); + + builder->CreateCondBr(builder->CreateICmpEQ(newlen, getInteger(32, 0)), BB1, BB3); + builder->SetInsertPoint(BB3); + + llvm::Value* ret1 = insert_value(insert_value(param[0], builder->CreateAdd(extract_value(param[0], 2), start), 2), newlen, 3); + + builder->CreateBr(BB4); + builder->SetInsertPoint(BB1); + release(param[0], args[0]->type); + llvm::BasicBlock* BB5 = builder->GetInsertBlock(); + llvm::Value* ret2 = get_voidstring(); + builder->CreateBr(BB4); + + builder->SetInsertPoint(BB4); + param[0] = create_phi(ret1, BB3, ret2, BB5); + c_SP(-(args[0]->on_stack + (nargs == 2 && args[1]->on_stack) + (nargs == 3 && args[2]->on_stack))); + if (on_stack) + push_value(param[0], T_STRING); + return param[0]; + } + case 0x43: { //Len + codegen_value + llvm::Value* string_length = extract_value(param[0], 3); + release(param[0], args[0]->type); + c_SP(-args[0]->on_stack + on_stack); + if (on_stack) + set_top_value(string_length, T_INTEGER); + return string_length; + } + case 0x44: { //Space + codegen_value + gen_if_noreturn(builder->CreateICmpSLT(param[0], getInteger(32, 0)), [&](){ + create_throw(E_ARG); + }); + llvm::Value* ret = gen_if_else_phi(builder->CreateICmpEQ(param[0], getInteger(32, 0)), [&](){ + return get_voidstring(); + }, [&](){ + builder->CreateCall3(get_global_function_jif(STRING_new_temp_value, 'v', "ppi"), + builder->CreateBitCast(temp_value, llvmType(getInt8PtrTy)), get_nullptr(), param[0]); + + llvm::Value* str = builder->CreateLoad(builder->CreateBitCast(temp_value, pointer_t(string_type))); + if (TARGET_BITS == 64) + param[0] = builder->CreateZExt(param[0], llvmType(getInt64Ty)); + + llvm::Value* ptr = extract_value(str, 1); + //llvm::Function* memset_func = llvm::Intrinsic::getDeclaration(M, llvm::Intrinsic::memset, string_to_type_vector("pj")); + const char* memset_name = TARGET_BITS == 64 ? "llvm.memset.p0i8.i64" : "llvm.memset.p0i8.i32"; + llvm::Function* memset_func = llvm::cast(M->getOrInsertFunction(memset_name, get_function_type('v', "pcjib", false))); + builder->CreateCall5(memset_func, ptr, getInteger(8, ' '), param[0], getInteger(32, 0), getInteger(1, false)); + borrow_string_no_nullcheck(ptr); + return str; + }); + c_SP(-args[0]->on_stack + on_stack); + if (on_stack) + set_top_value(ret, T_STRING); + return ret; + } + case 0x45: //String + codegen_stack + SUBR(SUBR_string) + + case 0x46: { //Trim + codegen_value + bool left = extra == 0 || extra == 1; + bool right = extra == 0 || extra == 2; + llvm::Value* string_ptr = extract_value(param[0], 1); + llvm::Value* string_offset = extract_value(param[0], 2); + llvm::Value* string_length = extract_value(param[0], 3); + + string_ptr = builder->CreateGEP(string_ptr, to_target_int(string_offset)); + + if (left){ + llvm::Value* enter_loop = gen_and_if( + builder->CreateICmpSGT(string_length, getInteger(32, 0)), + [&](){return builder->CreateICmpULE(builder->CreateLoad(string_ptr), getInteger(8, ' '));} + ); + llvm::BasicBlock *from_block = builder->GetInsertBlock(), *loop_block; + llvm::Value *ret_offset, *ret_length, *ret_ptr; + + gen_while(enter_loop, [&](){ + llvm::PHINode* phi_offset = builder->CreatePHI(llvmType(getInt32Ty), 2); + llvm::PHINode* phi_length = builder->CreatePHI(llvmType(getInt32Ty), 2); + llvm::PHINode* phi_ptr = builder->CreatePHI(llvmType(getInt8PtrTy), 2); + ret_offset = builder->CreateAdd(phi_offset, getInteger(32, 1)); + ret_length = builder->CreateSub(phi_length, getInteger(32, 1)); + ret_ptr = builder->CreateGEP(phi_ptr, getInteger(TARGET_BITS, 1)); + + llvm::Value* ret = gen_and_if( + builder->CreateICmpSGT(ret_length, getInteger(32, 0)), + [&](){return builder->CreateICmpULE(builder->CreateLoad(ret_ptr), getInteger(8, ' ')); } + ); + loop_block = builder->GetInsertBlock(); + phi_offset->addIncoming(string_offset, from_block); + phi_offset->addIncoming(ret_offset, loop_block); + phi_length->addIncoming(string_length, from_block); + phi_length->addIncoming(ret_length, loop_block); + phi_ptr->addIncoming(string_ptr, from_block); + phi_ptr->addIncoming(ret_ptr, loop_block); + return ret; + }); + string_ptr = create_phi(string_ptr, from_block, ret_ptr, loop_block); + string_offset = create_phi(string_offset, from_block, ret_offset, loop_block); + string_length = create_phi(string_length, from_block, ret_length, loop_block); + } + if (right){ + llvm::Value* enter_loop = gen_and_if( + builder->CreateICmpSGT(string_length, getInteger(32, 0)), + [&](){return builder->CreateICmpULE( + builder->CreateLoad( + builder->CreateGEP( + string_ptr, + to_target_int(builder->CreateSub(string_length, getInteger(32, 1))) + ) + ), getInteger(8, ' ') + );} + ); + llvm::BasicBlock *from_block = builder->GetInsertBlock(), *loop_block; + llvm::Value* ret_length; + gen_while(enter_loop, [&](){ + llvm::PHINode* phi_length = builder->CreatePHI(llvmType(getInt32Ty), 2); + ret_length = builder->CreateSub(phi_length, getInteger(32, 1)); + + llvm::Value* ret = gen_and_if( + builder->CreateICmpSGT(ret_length, getInteger(32, 0)), + [&](){return builder->CreateICmpULE( + builder->CreateLoad( + builder->CreateGEP( + string_ptr, + to_target_int(builder->CreateSub(ret_length, getInteger(32, 1))) + ) + ), getInteger(8, ' ') + );} + ); + loop_block = builder->GetInsertBlock(); + phi_length->addIncoming(string_length, from_block); + phi_length->addIncoming(ret_length, loop_block); + return ret; + }); + string_length = create_phi(string_length, from_block, ret_length, loop_block); + } + llvm::Value* ret = insert_value(insert_value(param[0], string_offset, 2), string_length, 3); + c_SP(-args[0]->on_stack + on_stack); + if (on_stack) + set_top_value(ret, args[0]->type); + return ret; + } + case 0x47: + case 0x48: { //UCase, LCase + codegen_stack + SUBR_CODE(SUBR_upper) + } + + case 0x49: { //Chr + codegen_value + gen_if_noreturn(builder->CreateICmpUGT(param[0], getInteger(32, 255)), [&](){ + create_throw(E_ARG); + }, "Chr_out_of_range", "Chr_ok"); + llvm::Value* arr = get_global((void*)STRING_char_string, llvmType(getInt8Ty)); + param[0] = builder->CreateMul(param[0], getInteger(32, 2)); + if (TARGET_BITS == 64) + param[0] = builder->CreateZExt(param[0], llvmType(getInt64Ty)); + llvm::Value* retaddr = builder->CreateGEP(arr, param[0]); + llvm::Value* ret = get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), retaddr, getInteger(32, 0), getInteger(32, 1)); + c_SP(-args[0]->on_stack + on_stack); + if (on_stack) + set_top_value(ret, T_CSTRING); + return ret; + } + + case 0x4A: { //Asc + codegen_value + llvm::Value* pos = nargs == 2 ? builder->CreateSub(param[1], getInteger(32, 1)) : getInteger(32, 0); + llvm::Value* strptr = extract_value(param[0], 1); + llvm::Value* ret = gen_if_else_phi(builder->CreateOr(builder->CreateICmpSLT(pos, getInteger(32, 0)), builder->CreateICmpSGE(pos, extract_value(param[0], 3))), [&](){ + return getInteger(32, 0); + }, [&](){ + pos = builder->CreateAdd(pos, extract_value(param[0], 2)); + llvm::Value* target_type_pos = TARGET_BITS == 64 ? builder->CreateZExt(pos, llvmType(getInt64Ty)) : pos; + llvm::Value* addr = builder->CreateGEP(strptr, target_type_pos); + return builder->CreateZExt(builder->CreateLoad(addr), llvmType(getInt32Ty)); + }, "empty_string", "not_empty_string", "Asc_done"); + release(param[0], args[0]->type); + c_SP(stack_diff); + if (on_stack) + set_top_value(ret, T_INTEGER); + return ret; + } + + case 0x4B: //InStr + case 0x4C: //RInStr + codegen_stack + SUBR_CODE(SUBR_instr) + + case 0x4D: //Subst + codegen_stack + SUBR_CODE(SUBR_subst) + + case 0x4E: //Replace + codegen_stack + SUBR_CODE(SUBR_replace) + + case 0x4F: //Split + codegen_stack + SUBR_CODE(SUBR_split) + + case 0x50: { //Scan + codegen_value + auto str = get_string_len(param[0]); + auto pat = get_string_len(param[1]); + + llvm::Value* arr = builder->CreateCall4(get_global_function_jif(OBJECT_create, 'p', "pppi"), get_global((void*)GB.FindClass("String[]"), llvmType(getInt8Ty)), get_nullptr(), get_nullptr(), getInteger(32, 0)); + + gen_if(builder->CreateAnd( + builder->CreateICmpNE(str.second, getInteger(32, 0)), + builder->CreateICmpNE(pat.second, getInteger(32, 0))), [&](){ + builder->CreateCall5(get_global_function_jif(REGEXP_scan, 'c', "ppipi"), arr, pat.first, pat.second, str.first, str.second); + }, "scan_ok_params", "scan_empty_params"); + + llvm::Value* ret = get_new_struct(object_type, get_global((void*)GB.FindClass("String[]"), llvmType(getInt8Ty)), arr); + borrow_object_no_nullcheck(arr); + release(param[0], args[0]->type); + release(param[1], args[1]->type); + + c_SP(-args[0]->on_stack-args[1]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, (TYPE)(void*)GB.FindClass("String[]")); + return ret; + } + + case 0x51: //Comp + codegen_stack + SUBR_CODE(SUBR_strcomp) + + case 0x52: { //Conv + codegen_value + auto str = get_string_len(param[0]); + llvm::Value* src = get_string(param[1]); + llvm::Value* dst = get_string(param[2]); + + llvm::Value* llvm_args[6] = {builder->CreateBitCast(temp_voidptr, llvmType(getInt8PtrTy)), str.first, str.second, src, dst, getInteger(8, false)}; + llvm::Value* conv_error = builder->CreateCall(get_global_function_jif(STRING_conv, 'i', "ppippc"), llvm_args); + release(param[0], args[0]->type); + release(param[1], args[1]->type); + release(param[2], args[2]->type); + c_SP(-args[0]->on_stack-args[1]->on_stack-args[2]->on_stack+on_stack); + + gen_if_noreturn(builder->CreateICmpNE(conv_error, getInteger(32, 0)), [&](){ + create_throw(conv_error); + }, "iconv_error", "iconv_ok"); + + llvm::Value* result = builder->CreateLoad(temp_voidptr); + + llvm::Value* ret = gen_if_else_phi(builder->CreateICmpEQ(result, get_nullptr()), [&](){ + return get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)); + }, [&](){ + borrow_string_no_nullcheck(result); //FIXME no nullcheck ok? + llvm::Value* len = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(result, getInteger(TARGET_BITS, -4)), llvmType(getInt32PtrTy))); + return get_new_struct(string_type, getInteger(TARGET_BITS, T_STRING), result, getInteger(32, 0), len); + }); + if (on_stack) + set_top_value(ret, T_STRING); + return ret; + } + + case 0x53: { //DConv, SConv + /*auto str = get_string_len(param[0]); + llvm::Value* src = builder->CreateIntToPtr(getInteger(TARGET_BITS, -2), llvmType(getInt8PtrTy)); + llvm::Value* dst = get_global(;*/ + codegen_stack + SUBR_CODE(SUBR_sconv) + } + + case 0x54: //Abs + if (type != T_VARIANT){ + codegen_value + llvm::Value* ret; + switch(type){ + #define ab(bits) ret = builder->CreateSelect(builder->CreateICmpSLT(param[0], getInteger(bits, 0)), builder->CreateSub(getInteger(bits, 0), param[0]), param[0]); break; + #define fab(zero) ret = builder->CreateSelect(builder->CreateFCmpULT(param[0], getFloat(zero)), builder->CreateFSub(getFloat(zero), param[0]), param[0]); break; + case T_BOOLEAN: + case T_BYTE: ret = param[0]; break; + case T_SHORT: ab(16) + case T_INTEGER: ab(32) + case T_LONG: ab(64) + case T_SINGLE: fab(0.0f) + case T_FLOAT: fab(0.0) + #undef ab + #undef fab + default: abort(); + } + c_SP(-args[0]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type, args[0]->on_stack ? false : true); + return ret; + } else { + //store_pc(pc); Should not be needed... + codegen_stack + SUBR_CODE(SUBR_abs) + } + + case 0x55: //Int + if (type != T_VARIANT){ + codegen_value + llvm::Value* ret = param[0]; + if (type == T_SINGLE) + ret = builder->CreateCall(get_global_function(floorf, 'f', "f"), param[0]); + else if (type == T_FLOAT) + ret = builder->CreateCall(get_global_function(floor, 'd', "d"), param[0]); + c_SP(-args[0]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type, args[0]->on_stack ? false : true); + return ret; + } else { + codegen_stack + SUBR_CODE(SUBR_int) + } + + case 0x56: //Fix + if (type != T_VARIANT){ + codegen_value + llvm::Value* ret = param[0]; + if (type >= T_SINGLE){ + llvm::Value* zero = type == T_SINGLE ? getFloat(0.0f) : getFloat(0.0); + llvm::Value* func1 = type == T_SINGLE ? get_global_function(floorf, 'f', "f") : get_global_function(floor, 'd', "d"); + llvm::Value* func2 = type == T_SINGLE ? get_global_function(fabsf, 'f', "f") : get_global_function(fabs, 'd', "d"); + ret = gen_if_else_phi(builder->CreateFCmpUGE(param[0], zero), [&](){ + return builder->CreateCall(func1, param[0]); + }, [&](){ + return builder->CreateFSub(zero, builder->CreateCall(func1, builder->CreateCall(func2, param[0]))); + }); + } + c_SP(-args[0]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type, args[0]->on_stack ? false : true); + return ret; + } else { + codegen_stack + SUBR_CODE(SUBR_fix) + } + + case 0x57: //Sgn + if (args[0]->type != T_VARIANT){ + llvm::Type* intTy = llvmType(getInt32Ty); + static const int bits[] = {0, 1, 8, 16, 32, 64}; + static llvm::Value* const zero[] = {NULL, getInteger(1, 0), getInteger(8, 0), getInteger(16, 0), getInteger(32, 0), getInteger(64, 0), getFloat(0.0f), getFloat(0.0)}; + codegen_value + llvm::Value* ret; + TYPE t = args[0]->type; + switch(t){ + case T_BOOLEAN: ret = builder->CreateSExt(param[0], intTy); break; + case T_BYTE: ret = builder->CreateZExt(builder->CreateICmpNE(param[0], zero[t]), intTy); break; + case T_SHORT: + case T_INTEGER: + case T_LONG: { + llvm::Value* lobit = builder->CreateAShr(param[0], bits[t]-1); + if (t < T_INTEGER) lobit = builder->CreateSExt(param[0], intTy); + else if (t > T_INTEGER) lobit = builder->CreateTrunc(param[0], intTy); + ret = builder->CreateSelect(builder->CreateICmpSGT(param[0], zero[t]), getInteger(32, 1), lobit); + break; + } + case T_SINGLE: + case T_FLOAT: { + ret = builder->CreateSelect(builder->CreateFCmpOGT(param[0], zero[t]), getInteger(32, 1), + builder->CreateSelect(builder->CreateFCmpOLT(param[0], zero[t]), getInteger(32, -1), zero[T_INTEGER])); + break; + } + default: __builtin_unreachable(); + } + c_SP(-args[0]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type, args[0]->on_stack ? false : true); + return ret; + } else { + codegen_stack + SUBR_CODE(SUBR_sgn) + } + + case 0x58: { //Math + codegen_value + llvm::Value* ret; + if (extra == 1){ + //frac + llvm::Value* x = builder->CreateCall(get_global_function(fabs, 'd', "d"), param[0]); + ret = builder->CreateFSub(x, builder->CreateCall(get_global_function(floor, 'd', "d"), x)); + } else if (extra == 11){ + //deg + ret = builder->CreateFDiv(builder->CreateFMul(param[0], getFloat(180.0)), getFloat(M_PI)); + } else if (extra == 12){ + //rad + ret = builder->CreateFDiv(builder->CreateFMul(param[0], getFloat(M_PI)), getFloat(180.0)); + } else { + #define f(name) get_global_function(name, 'd', "d") + llvm::Value* functions[28] = { + NULL, NULL, f(log), f(exp), f(sqrt), f(sin), f(cos), f(tan), f(atan), + f(asin), f(acos), NULL, NULL, f(log10), f(sinh), f(cosh), f(tanh), f(asinh), + f(acosh), f(atanh), f(exp2), f(exp10), f(log2), f(cbrt), f(expm1), f(log1p), + f(floor), f(ceil) + }; + #undef f + ret = builder->CreateCall(functions[extra], param[0]); + } + gen_if_noreturn(builder->CreateICmpEQ(builder->CreateCall(get_global_function(__finite, 'i', "d"), ret), getInteger(32, 0)), [&](){ + create_throw(E_MATH); + }); + c_SP(-args[0]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type, args[0]->on_stack ? false : true); + return ret; + } + + case 0x59: { //Pi + if (nargs == 0){ + if (on_stack) + push_value(getFloat(M_PI), T_FLOAT); + return getFloat(M_PI); + } else { + codegen_value + llvm::Value* ret = builder->CreateFMul(getFloat(M_PI), param[0]); + c_SP(-args[0]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, T_FLOAT, args[0]->on_stack ? false : true); + return ret; + } + } + + case 0x5A: { //Round + codegen_value + llvm::Value* val = nargs == 2 ? param[1] : getInteger(32, 0); + llvm::Value* power = builder->CreateCall2(M->getOrInsertFunction("llvm.powi.f64", get_function_type('d', "di")), getFloat(10.0), val); + + llvm::Value* ret = builder->CreateFMul( + builder->CreateCall(get_global_function(floor, 'd', "d"), + builder->CreateFAdd( + builder->CreateFDiv(param[0], power), + getFloat(0.5) + ) + ), power + ); + c_SP(-args[0]->on_stack-(nargs == 2 && args[1]->on_stack)+on_stack); + if (on_stack) + set_top_value(ret, T_FLOAT, args[0]->on_stack ? false : true); + return ret; + } + + case 0x5B: { //Randomize + codegen_value + llvm::Value* set = getInteger(8, nargs == 0 ? false : true); + llvm::Value* seed = nargs == 0 ? getInteger(32, 0) : param[0]; + builder->CreateCall2(get_global_function_jif(randomize, 'v', "ci"), set, seed); + c_SP(-(nargs && args[0]->on_stack)+on_stack); + if (on_stack) + set_top_value(NULL, T_VOID); + return NULL; + } + + case 0x5C: { //Rnd + codegen_value + llvm::Value *min, *max; + int stackdiff = on_stack; + if (nargs == 0){ + min = getFloat(0.0); + max = getFloat(1.0); + } else if (nargs == 1){ + min = getFloat(0.0); + max = param[0]; + stackdiff -= args[0]->on_stack; + } else if (nargs == 2){ + min = param[0]; + max = param[1]; + stackdiff -= args[0]->on_stack; + stackdiff -= args[1]->on_stack; + } + llvm::Value* r = builder->CreateCall(get_global_function_jif(rnd, 'd', "")); + llvm::Value* ret = builder->CreateFAdd(builder->CreateFMul(r, builder->CreateFSub(max, min)), min); + c_SP(stackdiff); + if (on_stack) + set_top_value(ret, T_FLOAT, (nargs >= 1 && args[0]->on_stack) ? false : true); + return ret; + } + + case 0x5D: //Min + case 0x5E: { //Max + if (type != T_VARIANT){ + bool is_max = digit == 0x5E; + codegen_value + llvm::Value* ret; + if (type == T_BOOLEAN || (type >= T_SHORT && type <= T_LONG)) + ret = (is_max ? gen_max : gen_min)(param[0], param[1]); + else if (type == T_BYTE) + ret = builder->CreateSelect(builder->CreateICmpULT(param[0], param[1]), is_max ? param[1] : param[0], is_max ? param[0] : param[1]); + else if (type == T_FLOAT) + ret = builder->CreateSelect(builder->CreateFCmpOLT(param[0], param[1]), is_max ? param[1] : param[0], is_max ? param[0] : param[1]); + else if (type == T_DATE) + ret = builder->CreateSelect(LessDate(param[0], param[1]), is_max ? param[0] : param[1], is_max ? param[1] : param[0]); + c_SP(-args[0]->on_stack-args[1]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type, args[0]->on_stack ? false : true); + return ret; + } else { + codegen_stack + extra = digit << 8; + SUBR_CODE(SUBR_min_max) + } + } + + case 0x5F: { //IIf + //FIXME if a destructor throws an error, this does not work ... + codegen_value + llvm::Value* ret; + if (args[1]->type == args[2]->type || (TYPE_is_string(args[1]->type) && TYPE_is_string(args[2]->type))){ + ret = builder->CreateSelect(param[0], param[1], param[2]); + gen_if_else(param[0], [&](){ + release(param[2], args[2]->type); + }, [&](){ + release(param[1], args[1]->type); + }, "IIf_release_false_argument", "IIf_release_true_argument", "IIf_release_done"); + + c_SP(-args[0]->on_stack-args[1]->on_stack-args[2]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type); + return ret; + } else { + //Convert to variant + + c_SP(-args[0]->on_stack-args[1]->on_stack-args[2]->on_stack); + + args[1]->on_stack = false; + args[2]->on_stack = false; + + ret = gen_if_else_phi(param[0], [&](){ + if (args[1]->type == type) + return param[1]; + + release(param[2], args[2]->type); + + return JIT_conv_to_variant(args[1], param[1], on_stack, NULL); + }, [&](){ + if (args[2]->type == type) + return param[2]; + + release(param[1], args[1]->type); + + return JIT_conv_to_variant(args[2], param[2], on_stack, NULL); + }, "IIf_then", "IIf_else"); + return ret; + } + break; + } + + case 0x60: { //Choose + llvm::Value* ret; + if (nargs < 4){ + codegen_value + ret = get_default(type); + } + if (nargs == 1){ + c_SP(-args[0]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type); + return ret; + } else if (nargs == 2){ + llvm::Value* cmp = builder->CreateICmpEQ(param[0], getInteger(32, 1)); + ret = builder->CreateSelect(cmp, param[1], ret); + c_SP(-args[0]->on_stack-args[1]->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type); + gen_if(builder->CreateXor(cmp, getInteger(1, true)), [&](){ + release(param[1], args[1]->type); + }, "release_1st_choose"); + return ret; + } else if (nargs == 3){ + llvm::Value* index = builder->CreateSub(param[0], getInteger(32, 1)); + + c_SP(-args[0]->on_stack-args[1]->on_stack-args[2]->on_stack); + + args[1]->on_stack = false; + args[2]->on_stack = false; + + bool stack_already_set_in_conv = false; + + ret = gen_if_else_phi(builder->CreateICmpULT(index, getInteger(32, 2)), [&](){ + llvm::Value* r; + if (args[1]->type == args[2]->type || (TYPE_is_string(args[1]->type) && TYPE_is_string(args[2]->type))){ + r = builder->CreateSelect(builder->CreateTrunc(index, llvmType(getInt1Ty)), param[2], param[1]); + c_SP(on_stack); + return r; + } else { + r = gen_if_else_phi(builder->CreateTrunc(index, llvmType(getInt1Ty)), [&](){ + return JIT_conv_to_variant(args[2], param[2], on_stack, NULL); + }, [&](){ + return JIT_conv_to_variant(args[1], param[1], on_stack, NULL); + }); + stack_already_set_in_conv = true; + return r; + } + }, [&](){ + c_SP(on_stack); + if (stack_already_set_in_conv && on_stack) + set_top_value(ret, type); + return ret; + }); + + if (!stack_already_set_in_conv && on_stack) + set_top_value(ret, type); + + gen_if(builder->CreateICmpNE(param[0], getInteger(32, 1)), [&](){ + release(param[1], args[1]->type); + }, "release_1st_choose"); + gen_if(builder->CreateICmpNE(param[0], getInteger(32, 2)), [&](){ + release(param[2], args[2]->type); + }, "release_2st_choose"); + + return ret; + } else { + codegen_stack + SUBR_CODE(SUBR_choose) + } + } + + case 0x61: { //Array + codegen_value + if (nargs > 0){ + push_value(getInteger(32, nargs), T_INTEGER); + } + llvm::Value* arr = builder->CreateCall4(get_global_function_jif(OBJECT_create, 'p', "pppi"), get_global((void*)type, llvmType(getInt8Ty)), get_nullptr(), get_nullptr(), getInteger(32, nargs > 0 ? 1 : 0)); + borrow_object_no_nullcheck(arr); + int element_size = TYPE_sizeof_memory(type2); + int data_offset = offsetof(CARRAY, data); + llvm::Value* data = builder->CreateLoad(builder->CreateBitCast(builder->CreateGEP(arr, getInteger(TARGET_BITS, data_offset)), charPP)); + for(int i=0; iCreateGEP(data, getInteger(TARGET_BITS, i*element_size)); + variable_write(addr, param[i], args[i]->type); + } + llvm::Value* ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, type), llvmType(getInt8PtrTy)), arr); + c_SP(stack_diff); + if (on_stack) + set_top_value(ret, type); + return ret; + } + + case 0x62: { //Math2 + codegen_value + llvm::Value* ret; + if (extra == 2){ + llvm::Value* temp = param[0]; + param[0] = param[1]; + param[1] = temp; + } + if (extra <= 2){ + //Atan2, Ang + ret = builder->CreateCall2(get_global_function(atan2, 'd', "dd"), param[0], param[1]); + } else { + ret = builder->CreateCall(get_global_function(sqrt, 'd', "d"), builder->CreateFAdd(builder->CreateFMul(param[0], param[0]), builder->CreateFMul(param[1], param[1]))); + } + gen_if_noreturn(builder->CreateICmpEQ(builder->CreateCall(get_global_function(__finite, 'i', "d"), ret), getInteger(32, 0)), [&](){ + create_throw(E_MATH); + }); + c_SP(stack_diff); + if (on_stack) + set_top_value(ret, T_FLOAT); + return ret; + } + + case 0x63: { //IsAscii, IsLetter, ... + codegen_value + llvm::Value* orig_ptr = extract_value(param[0], 1); + llvm::Value* ptr = builder->CreateGEP(orig_ptr, extract_value(param[0], 2)); + llvm::Value* endptr = builder->CreateGEP(ptr, to_target_int(extract_value(param[0], 3))); + + llvm::BasicBlock* from_block = builder->GetInsertBlock(); + llvm::BasicBlock* body_block = create_bb("IsChr..."); + llvm::BasicBlock* cont_block = create_bb("IsChr_done"); + builder->CreateCondBr(builder->CreateICmpNE(extract_value(param[0], 3), getInteger(32, 0)), body_block, cont_block); + + builder->SetInsertPoint(body_block); + llvm::PHINode* current_ptr = builder->CreatePHI(llvmType(getInt8PtrTy), 2); + llvm::Value* next = builder->CreateGEP(current_ptr, getInteger(TARGET_BITS, 1)); + current_ptr->addIncoming(ptr, from_block); + current_ptr->addIncoming(next, body_block); + llvm::Value* c = builder->CreateLoad(current_ptr); + llvm::Value* res; + switch(extra){ + case 1: //IsAscii: + res = builder->CreateICmpEQ(builder->CreateAnd(c, getInteger(8, ~0x7F)), getInteger(8, 0)); + break; + case 2: //IsLetter: + res = builder->CreateOr( + builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, 'a')), + builder->CreateICmpSLE(c, getInteger(8, 'z')) + ), builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, 'A')), + builder->CreateICmpSLE(c, getInteger(8, 'Z')) + ) + ); break; + case 3: //IsLower: + res = builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, 'a')), + builder->CreateICmpSLE(c, getInteger(8, 'z')) + ); break; + case 4: //IsUpper: + res = builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, 'A')), + builder->CreateICmpSLE(c, getInteger(8, 'Z')) + ); break; + case 5: //IsDigit: + res = builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, '0')), + builder->CreateICmpSLE(c, getInteger(8, '9')) + ); break; + case 6: //IsHexa: + res = builder->CreateOr( + builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, '0')), + builder->CreateICmpSLE(c, getInteger(8, '9')) + ), + builder->CreateOr( + builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, 'a')), + builder->CreateICmpSLE(c, getInteger(8, 'f')) + ), builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, 'A')), + builder->CreateICmpSLE(c, getInteger(8, 'F')) + ) + ) + ); break; + case 7: { //IsSpace: + const char* conds = " \n\r\t\f\v"; + res = builder->CreateICmpEQ(c, getInteger(8, conds[0])); + for(size_t i=1; iCreateOr(res, builder->CreateICmpEQ(c, getInteger(8, conds[i]))); + break; + } + case 8: //IsBlank: + res = builder->CreateOr(builder->CreateICmpEQ(c, getInteger(8, 32)), builder->CreateICmpEQ(c, getInteger(8, '\t'))); + break; + case 9: //IsPunct: + res = builder->CreateAnd( + builder->CreateICmpSGT(c, getInteger(8, 32)), + builder->CreateXor( + builder->CreateOr( + builder->CreateOr( + builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, 'a')), + builder->CreateICmpSLE(c, getInteger(8, 'z')) + ), builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, 'A')), + builder->CreateICmpSLE(c, getInteger(8, 'Z')) + ) + ), + builder->CreateAnd( + builder->CreateICmpSGE(c, getInteger(8, '0')), + builder->CreateICmpSLE(c, getInteger(8, '9')) + ) + ), + getInteger(1, 1) + ) + ); + break; + } + llvm::Value* still_inside_string = builder->CreateICmpNE(next, endptr); + builder->CreateCondBr(builder->CreateAnd(still_inside_string, res), body_block, cont_block); + + builder->SetInsertPoint(cont_block); + res = create_phi(getInteger(1, false), from_block, res, body_block); + release(param[0], args[0]->type); + c_SP(stack_diff); + if (on_stack) + set_top_value(res, T_BOOLEAN); + return res; + } + + case 0x64: //Bit manipulation + if (args[0]->type == T_VARIANT){ + codegen_stack + SUBR_CODE(SUBR_bit) + } else { + codegen_value + static const int bits[] = {0, 0, 8, 16, 32, 64}; + int nbits = bits[args[0]->type]; + gen_if_noreturn(builder->CreateICmpUGE(param[1], getInteger(32, nbits)), [&](){ + create_throw(E_ARG); + }, "bit_out_of_range"); + llvm::Value* bt = nbits == 32 ? param[1] : (nbits == 64 ? builder->CreateZExt(param[1], llvmType(getInt64Ty)) : builder->CreateTrunc(param[1], param[0]->getType())); + llvm::Value* res; + switch(extra){ + case 1: //BClr + res = builder->CreateAnd(param[0], builder->CreateXor(builder->CreateShl(getInteger(nbits, 1), bt), getInteger(nbits, -1))); + break; + case 2: //BSet + res = builder->CreateOr(param[0], builder->CreateShl(getInteger(nbits, 1), bt)); + break; + case 3: //BTst + res = builder->CreateICmpNE(builder->CreateAnd(param[0], builder->CreateShl(getInteger(nbits, 1), bt)), getInteger(nbits, 0)); + break; + case 4: //BChg: + res = builder->CreateXor(param[0], builder->CreateShl(getInteger(nbits, 1), bt)); + break; + case 5: //Asl: + res = builder->CreateShl(param[0], bt); + if (nbits != 8){ + llvm::Value* max_signed_int = getInteger(nbits, (1ULL << nbits)-1); //0x7F... + llvm::Value* min_signed_int = getInteger(nbits, 1ULL << nbits); //0x80... + res = builder->CreateOr( + builder->CreateAnd(res, max_signed_int), builder->CreateAnd(param[0], min_signed_int) + ); + } + break; + case 6: //Asr: + res = builder->CreateAShr(param[0], bt); + break; + case 7: //Rol: + res = builder->CreateOr( + builder->CreateShl(param[0], bt), builder->CreateLShr(param[0], builder->CreateSub(getInteger(nbits, nbits), bt)) + ); + break; + case 8: //Ror: + res = builder->CreateOr( + builder->CreateLShr(param[0], bt), builder->CreateShl(param[0], builder->CreateSub(getInteger(nbits, nbits), bt)) + ); + break; + case 9: //Lsl: + res = builder->CreateShl(param[0], bt); + break; + case 10: //Lsr: + res = builder->CreateLShr(param[0], bt); + break; + } + c_SP(stack_diff); + if (on_stack) + set_top_value(res, type, extra == 3 || !args[0]->on_stack); + return res; + } + + case 0x65: //IsBoolean ... + codegen_stack + SUBR_CODE(SUBR_is_type) + + case 0x66: { //TypeOf + codegen_value + llvm::Value* ret; + if (extra){ //Sizeof + ret = gen_if_phi(getInteger(32, TARGET_BITS/8), builder->CreateICmpULT(param[0], getInteger(32, 16)), [&](){ + llvm::Value* s = builder->CreateLoad(builder->CreateGEP(get_global((void*)TYPE_sizeof_memory_tab, LONG_TYPE), to_target_int(param[0]))); + if (TARGET_BITS == 64) + s = builder->CreateTrunc(s, llvmType(getInt32Ty)); + return s; + }, "SizeOf"); + c_SP(stack_diff); + if (on_stack) + set_top_value(ret, T_INTEGER, !args[0]->on_stack); + } else { //TypeOf + TYPE t = args[0]->type; + if (t == T_VARIANT){ + ret = extract_value(param[0], 0); + if (TARGET_BITS == 64) + ret = builder->CreateTrunc(ret, llvmType(getInt32Ty)); + llvm::BasicBlock *BB0 = builder->GetInsertBlock(), *BB1 = create_bb("TypeOf1"), *BB2 = create_bb("TypeOf2"), *BB3 = create_bb("TypeOf3"); + + builder->CreateCondBr(builder->CreateICmpEQ(ret, getInteger(32, T_CSTRING)), BB3, BB1); + builder->SetInsertPoint(BB1); + builder->CreateCondBr(builder->CreateICmpSGE(ret, getInteger(32, T_OBJECT)), BB2, BB3); + builder->SetInsertPoint(BB2); + builder->CreateBr(BB3); + builder->SetInsertPoint(BB3); + llvm::PHINode* phi = builder->CreatePHI(llvmType(getInt32Ty), 3); + phi->addIncoming(getInteger(32, T_OBJECT), BB2); + phi->addIncoming(ret, BB1); + phi->addIncoming(getInteger(32, T_STRING), BB0); + ret = phi; + } else if (t == T_CSTRING){ + ret = getInteger(32, T_STRING); + } else if (TYPE_is_object(t) && t != T_NULL){ + ret = getInteger(32, T_OBJECT); + } else { + ret = getInteger(32, t); + } + c_SP(stack_diff); + if (on_stack) + set_top_value(ret, T_INTEGER, args[0]->type != T_INTEGER || !args[0]->on_stack); + } + release(param[0], args[0]->type); + return ret; + } + + case 0x68: //Bin + case 0x69: //Hex + codegen_stack + SUBR_CODE(SUBR_hex_bin) + + case 0x6A: //Val + codegen_stack + SUBR_CODE(SUBR_val) + + case 0x6B: { //Str + args[0]->must_on_stack(); + codegen_value + llvm::Value* stack_top = get_value_on_top_addr(); + builder->CreateCall3(get_global_function_jif(VALUE_to_string, 'v', "ppp"), + builder->CreateBitCast(stack_top, llvmType(getInt8PtrTy)), + builder->CreateBitCast(temp_voidptr, llvmType(getInt8PtrTy)), + builder->CreateBitCast(temp_int, llvmType(getInt8PtrTy)) + ); + builder->CreateCall3(get_global_function_jif(STRING_new_temp_value, 'v', "ppi"), + builder->CreateBitCast(on_stack ? stack_top : temp_value, llvmType(getInt8PtrTy)), + builder->CreateLoad(temp_voidptr), + builder->CreateLoad(temp_int) + ); + llvm::Value* str = read_value(on_stack ? stack_top : temp_value, T_STRING); + release(param[0], args[0]->type); + borrow_string(extract_value(str, 1)); //Nullcheck needed, might be empty string = null pointer + if (!on_stack) + c_SP(-1); + return str; + } + + case 0x6C: //Format + codegen_stack + SUBR_CODE(SUBR_format) + + case 0x6D: { //Timer + builder->CreateCall2(get_global_function_jif(DATE_timer, 'v', "pi"), + builder->CreateBitCast(temp_double, llvmType(getInt8PtrTy)), + getInteger(32, 1)); + llvm::Value* ret = builder->CreateLoad(temp_double); + if (on_stack) + push_value(ret, T_FLOAT); + return ret; + } + + case 0x6E: { //Now + c_SP(on_stack); + llvm::Value* addr = on_stack ? get_value_on_top_addr() : temp_value; + builder->CreateCall(get_global_function_jif(DATE_now, 'v', "p"), + builder->CreateBitCast(addr, llvmType(getInt8PtrTy))); + return read_value(addr, T_DATE); + } + + case 0x6F: //Year + codegen_stack + SUBR_CODE(SUBR_year) + + case 0x70: //Week + codegen_stack + SUBR_CODE(SUBR_week) + + case 0x71: //Date + codegen_stack + SUBR_CODE(SUBR_date) + + case 0x72: //Time + codegen_stack + SUBR_CODE(SUBR_time) + + case 0x73: //DateAdd, DateDiff + if (extra == 0){ //DateAdd + if (on_stack) + args[0]->must_on_stack(); + codegen_value + c_SP(stack_diff); + llvm::Value* addr = args[0]->on_stack ? get_value_on_top_addr() : temp_value; + if (!args[0]>on_stack) + store_value(temp_value, param[0], T_DATE, false /*not needed*/); + builder->CreateCall3(get_global_function_jif(DATE_add, 'v', "pii"), + builder->CreateBitCast(addr, llvmType(getInt8PtrTy)), + param[1], param[2]); + return read_value(addr, T_DATE); + } else { //DateDiff + codegen_value + c_SP(stack_diff); + + llvm::Value* addr1 = args[0]->on_stack ? get_value_on_top_addr() : temp_value2; + llvm::Value* addr2 = args[1]->on_stack ? (args[0]->on_stack ? builder->CreateGEP(addr1, getInteger(TARGET_BITS, 1)) : get_value_on_top_addr()) : temp_value; + + if (!args[0]->on_stack) + store_value(temp_value2, param[0], T_DATE, false); + if (!args[1]->on_stack) + store_value(temp_value, param[1], T_DATE, false); + + llvm::Value* ret = builder->CreateCall3(get_global_function_jif(DATE_diff, 'i', "ppi"), + builder->CreateBitCast(addr2, llvmType(getInt8PtrTy)), + builder->CreateBitCast(addr1, llvmType(getInt8PtrTy)), + param[2]); + if (on_stack) + set_top_value(ret, T_INTEGER); + return ret; + } + + case 0x74: //Eval + codegen_stack + SUBR_CODE(SUBR_eval) + + case 0x75: { //Error + //llvm::Value* ret = builder->CreateICmpNE(read_global((void*)&EXEC_got_error, llvmType(getInt8Ty)), getInteger(8, 0)); + llvm::Value* ret = builder->CreateLoad(temp_got_error); + if (on_stack) + push_value(ret, T_BOOLEAN); + return ret; + } + + case 0x76: //Debug + store_pc(pc); + SUBR(SUBR_debug) + + case 0x77: //Wait + codegen_stack + SUBR_CODE(SUBR_wait) + + case 0x78: //Open + codegen_stack + SUBR_CODE(SUBR_open) + + case 0x79: //Close + codegen_stack + SUBR_CODE(SUBR_close) + + case 0x7A: //Input + codegen_stack + SUBR_CODE(SUBR_input) + + case 0x7B: //Line Input + codegen_stack + SUBR(SUBR_linput) + + case 0x7C: //Print + codegen_stack + SUBR_CODE(SUBR_print) + + case 0x7D: //Read + codegen_stack + SUBR_CODE(SUBR_read) + + case 0x7E: //Write + codegen_stack + SUBR_CODE(SUBR_write) + + case 0x7F: //Flush + codegen_stack + SUBR(SUBR_flush) + + case 0x80: //Lock + codegen_stack + SUBR(SUBR_lock) + + case 0x81: //Input From, Output To, Error To + codegen_stack + SUBR_CODE(SUBR_inp_out) + + case 0x82: //Eof + codegen_stack + SUBR_CODE(SUBR_eof) + + case 0x83: //Lof + codegen_stack + SUBR_CODE(SUBR_lof) + + case 0x84: //Seek + codegen_stack + SUBR_CODE(SUBR_seek) + + case 0x86: //Mkdir, deprecated -> Even() & Odd() + if (extra != 0){ + codegen_value + llvm::Value* val = builder->CreateTrunc(param[0], llvmType(getInt1Ty)); + if (extra == 1) //Even + val = builder->CreateXor(val, getInteger(1, 1)); + c_SP(stack_diff); + if (on_stack) + set_top_value(val, T_BOOLEAN); + return val; + } + //Else fallthrough: deprecated Kill(1) + + case 0x85: //Kill + case 0x87: //Rmdir, deprecated + if (extra == 0) extra = digit - 0x85; + codegen_stack + SUBR_CODE(SUBR_kill) + + case 0x8A: //Link, deprecated -> IsNan() & IsInf + if (extra != 0){ + codegen_value + if (extra == 1){ + //IsNan + llvm::Value* res = builder->CreateICmpNE(builder->CreateCall(get_global_function(__isnan, 'i', "d"), param[0]), getInteger(32, 0)); + c_SP(stack_diff); + if (on_stack) + set_top_value(res, T_BOOLEAN); + return res; + } else if (extra == 2){ + //IsInf + llvm::Value* res = builder->CreateCall(get_global_function(__isinf, 'i', "d"), param[0]); + c_SP(stack_diff); + if (on_stack) + set_top_value(res, T_INTEGER); + return res; + } + } + //Else fallthrough: deprecated Link + + case 0x88: //Move + case 0x89: //Copy, deprecated + if (extra == 0) extra = digit - 0x88; + codegen_stack + SUBR_CODE(SUBR_move) + + case 0x8B: //Exist + codegen_stack + SUBR_CODE(SUBR_exist) + + case 0x8C: //Access + codegen_stack + SUBR_CODE(SUBR_access) + + case 0x8D: //Stat + codegen_stack + SUBR_CODE(SUBR_stat) + + case 0x8E: //Dfree + codegen_stack + SUBR(SUBR_dfree) + + case 0x8F: //Temp + codegen_stack + SUBR_CODE(SUBR_temp) + + case 0x90: //IsDir + codegen_stack + SUBR_CODE(SUBR_isdir) + + case 0x91: //Dir + codegen_stack + SUBR_CODE(SUBR_dir) + + case 0x92: //RDir + codegen_stack + SUBR_CODE(SUBR_rdir) + + case 0x93: //Exec + codegen_stack + SUBR_CODE(SUBR_exec) + + case 0x94: //Alloc + codegen_stack + SUBR_CODE(SUBR_alloc) + + case 0x95: //Free + codegen_stack + SUBR(SUBR_free) + + case 0x96: //Realloc + codegen_stack + SUBR_CODE(SUBR_realloc) + + case 0x97: //StrPtr + codegen_stack + SUBR_CODE(SUBR_strptr) + + case 0x98: { //Sleep + codegen_value + store_element(temp_2longs, 0, builder->CreateFPToSI(param[0], LONG_TYPE)); + llvm::Value* fabs_ret = builder->CreateCall(get_global_function(fabs, 'd', "d"), param[0]); + llvm::Value* floor_ret = builder->CreateCall(get_global_function(floor, 'd', "d"), fabs_ret); + llvm::Value* nsec = builder->CreateFMul(builder->CreateFSub(fabs_ret, floor_ret), getFloat(1000000000.0)); + store_element(temp_2longs, 1, builder->CreateFPToSI(nsec, LONG_TYPE)); + + llvm::BasicBlock *BB1 = create_bb("nanosleep_loop"), *BB2 = create_bb("nanosleep_cont"); + + llvm::Value* ptr = builder->CreateBitCast(temp_2longs, llvmType(getInt8PtrTy)); + + builder->CreateBr(BB1); + builder->SetInsertPoint(BB1); + llvm::Value* res = builder->CreateCall2(get_global_function(nanosleep, 'i', "pp"), ptr, ptr); + builder->CreateCondBr(builder->CreateICmpSLT(res, getInteger(32, 0)), BB1, BB2); + + builder->SetInsertPoint(BB2); + + if (on_stack) + push_value(NULL, T_VOID); + return NULL; + } + + case 0x99: { //VarPtr + auto pie = (PushIntegerExpression*)args[0]; + + llvm::Value* ret; + + ushort op = pie->i; + int index = op & 0xFF; + if ((op & 0xFF00) == C_PUSH_LOCAL){ + CLASS_LOCAL* var = &FP->local[index]; + TYPE t = ctype_to_type(&var->type); + if (TYPE_is_string(t)){ + ret = builder->CreateGEP(load_element(locals[index], 1), to_target_int(load_element(locals[index], 2))); + } else { + ret = builder->CreateBitCast(locals[index], llvmType(getInt8PtrTy)); + } + } else if ((op & 0xF800) == C_PUSH_DYNAMIC) { + CLASS_VAR* var = &CP->load->dyn[op & 0x7FF]; + ret = builder->CreateGEP(current_op, getInteger(TARGET_BITS, var->pos)); + } else if ((op & 0xF800) == C_PUSH_STATIC) { + CLASS_VAR* var = &CP->load->stat[op & 0x7FF]; + ret = builder->CreateGEP(get_global((void*)CP->stat, llvmType(getInt8Ty)), getInteger(TARGET_BITS, var->pos)); + } + if (on_stack) + push_value(ret, T_POINTER); + return ret; + } + + case 0x9A: //Collection + codegen_stack + SUBR_CODE(SUBR_collection) + + case 0x9B: //Tr + codegen_stack + SUBR(SUBR_tr) + + case 0x9C: //Quote, Shell, Html + codegen_stack + SUBR_CODE(SUBR_quote) + + case 0x9D: //Unquote + codegen_stack + SUBR_CODE(SUBR_unquote) + + case 0x9E: //MkInteger, ... + codegen_value + if (extra <= T_BYTE){ + llvm::Value* offset = builder->CreateShl(builder->CreateZExt(param[0], LONG_TYPE), getInteger(TARGET_BITS, 1)); + + llvm::Value* ret = get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), + builder->CreateGEP(builder->CreateIntToPtr(getInteger(TARGET_BITS, (int64_t)(void*)&STRING_char_string[0]), llvmType(getInt8PtrTy)), offset), + getInteger(32, 0), getInteger(32, 1)); + + c_SP(stack_diff); + if (on_stack) + set_top_value(ret, T_CSTRING); + return ret; + } else { + static const int sizes[] = {0, 1, 1, 2, 4, 8, 4, 8, 8, 0, 0, TARGET_BITS/8}; + c_SP(stack_diff); + llvm::Value* stack_top = get_value_on_top_addr(); + builder->CreateCall3(get_global_function_jif(STRING_new_temp_value, 'v', "ppi"), + builder->CreateBitCast(stack_top, llvmType(getInt8PtrTy)), + get_nullptr(), + getInteger(32, sizes[extra])); + + llvm::Value* ret = read_value(stack_top, T_STRING); + llvm::Value* ptr = extract_value(ret, 1); + + borrow_string_no_nullcheck(ptr); + + builder->CreateStore(param[0], builder->CreateBitCast(ptr, pointer_t(param[0]->getType()))); + return ret; + } + break; + + case 0x9F: //Ptr + codegen_stack + SUBR_CODE(SUBR_ptr) + + default: assert(false && "Subr not implemented yet"); + } + return ret_top_stack(type, on_stack); +} +void SubrExpression::codegen_on_stack(){ + codegen_get_value(); +} + +void stack_corrupted_abort(){ + fprintf(stderr, "Stack became corrupted in a JIT function. Please make a bug report.\n"); + abort(); +} + +void NopExpression::codegen(){ + /*builder->CreateCall4(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)"Nu: %s %p %p\n", llvmType(getInt8Ty)), + get_global((void*)buf, llvmType(getInt8Ty)), + read_global((void*)&SP), + read_global((void*)&BP)); + builder->CreateCall4(get_global_function_vararg(printf, 'v', "p"), + get_global((void*)"Nu: %d %p %p\n", llvmType(getInt8Ty)), + getInteger(32, FP->n_local + FP->n_ctrl), + read_global((void*)&SP), + read_global((void*)&BP));*/ + + if (test_stack){ +#ifndef GOSUB_ON_STACK + llvm::Value* sp = read_global((void*)&SP); + llvm::Value* bp = read_global((void*)&BP); + bp = builder->CreateGEP(bp, getInteger(TARGET_BITS, sizeof(VALUE)*(FP->n_local+FP->n_ctrl))); +#else + llvm::Value* sp = builder->CreateBitCast(read_global((void*)&SP), pointer_t(value_type)); + llvm::Value* bp = builder->CreateLoad(gp); +#endif + gen_if_noreturn(builder->CreateICmpNE(bp, sp), [&](){ + builder->CreateCall(get_global_function(stack_corrupted_abort, 'v', "")); + builder->CreateUnreachable(); + }); + } +} + +void ProfileLineExpression::codegen(){ + gen_if(builder->CreateICmpNE(read_global((void*)&EXEC_profile_instr, llvmType(getInt8Ty)), getInteger(8, false)), [&](){ + builder->CreateCall3(get_global_function_jif(DEBUG_Profile_Add, 'v', "ppp"), + get_global((void*)CP, llvmType(getInt8Ty)), + get_global((void*)FP, llvmType(getInt8Ty)), + get_global((void*)pc, llvmType(getInt8Ty))); + }); +} + +void StopEventExpression::codegen(){ + builder->CreateStore(getInteger(8, true), get_global((void*)&GAMBAS_StopEvent, llvmType(getInt8Ty))); +} + +void ReturnExpression::codegen(){ + auto normal_return = [&](){ + llvm::Value* ret; + if (retval){ + ret = retval->codegen_get_value(); + if (retval->on_stack) + c_SP(-1); + } else { + ret = get_default(type); + } + store_value(get_global((void*)RP), ret, type); + if (in_try) + builder->CreateCall(get_global_function(JR_end_try, 'v', "p"), + create_gep(temp_errcontext1, TARGET_BITS, 0, TARGET_BITS, 0)); + if (EC != NULL){ + gen_if(builder->CreateXor(builder->CreateLoad(temp_got_error2), getInteger(1, 1)), [&](){ + llvm::Value* call = builder->CreateCall(get_global_function(JR_end_try, 'v', "p"), + create_gep(temp_errcontext2, TARGET_BITS, 0, TARGET_BITS, 0)); + + if (llvm::Instruction* inst = llvm::dyn_cast(call)){ + llvm::Value* arr[1] = {getInteger(32, 1)}; + inst->setMetadata("large_end_try", llvm::MDNode::get(llvm_context, arr)); + } + }, "return_in_large_try"); + } + return_points.push_back(builder->GetInsertBlock()); + }; + + if (ngosubs == 0 || kind > 0){ + normal_return(); + } else { + gen_if_noreturn(builder->CreateICmpEQ(builder->CreateLoad(gosub_return_point), getInteger(16, 0)), [&](){ + normal_return(); + }); + //Else return in Gosub + gosub_return_points.push_back(builder->GetInsertBlock()); + } + builder->SetInsertPoint(create_bb("dummy")); +} + +void QuitExpression::codegen(){ + if (quitval){ + llvm::Value* val = quitval->codegen_get_value(); + if (quitval->on_stack) + c_SP(-1); + + builder->CreateStore(val, get_global((void*)&EXEC_quit_value, llvmType(getInt8Ty))); + } + + builder->CreateCall(get_global_function_jif(EXEC_quit, 'v', "")); + builder->CreateUnreachable(); + builder->SetInsertPoint(create_bb("dummy")); +} + +static void debug_print_line(){ + for (int i = 1; i < 10; i++) + fputs("--------", stderr); + fputc('\n', stderr); +} + +static void run_optimizations(){ + bool changed = true; + while(changed){ + llvm::FunctionPassManager FPM(M); + llvm::PassManager MPM; + llvm::PassManagerBuilder PMB; + PMB.OptLevel = 2; + PMB.SizeLevel = 1; + PMB.populateFunctionPassManager(FPM); + PMB.populateModulePassManager(MPM); + + //FPM.add(createGambasPass()); + + FPM.doInitialization(); + FPM.run(*llvm_function); + FPM.doFinalization(); + MPM.run(*M); + + llvm::FunctionPass* pass = createGambasPass(); + changed = pass->runOnFunction(*llvm_function); + delete pass; + } +} + +template +static void do_search(llvm::BasicBlock* block, T func){ + std::queue q; + std::set vis; + + q.push(block); + vis.insert(block); + + while(!q.empty()){ + llvm::BasicBlock* BB = q.front(); q.pop(); + + for(llvm::BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ){ + if (func(I++)) + goto next_block_in_queue; + } + + for(llvm::succ_iterator SI = succ_begin(BB), E = succ_end(BB); SI != E; ++SI){ + if (vis.count(*SI) == 0){ + q.push(*SI); + vis.insert(*SI); + } + } + next_block_in_queue:; + } +} + +static void fix_setjmp(llvm::BasicBlock* catch_block, llvm::BasicBlock* try_block, const char* end_try_string){ + std::set vars; + std::set volatile_vars; + std::vector stores; + + do_search(try_block, [&](llvm::Value* value){ + if (llvm::StoreInst* si = llvm::dyn_cast(value)){ + if (llvm::AllocaInst* ai = llvm::dyn_cast(si->getPointerOperand())){ + vars.insert(ai); + stores.push_back(si); + } + } else if (llvm::CallInst* ci = llvm::dyn_cast(value)){ + if (ci->hasMetadata() && ci->getMetadata(end_try_string)) + return true; + } + return false; + }); + + do_search(catch_block, [&](llvm::Value* value){ + if (llvm::LoadInst* li = llvm::dyn_cast(value)){ + if (llvm::AllocaInst* ai = llvm::dyn_cast(li->getPointerOperand())){ + if (vars.count(ai) != 0){ + li->setVolatile(true); + volatile_vars.insert(ai); + } + } + } + return false; + }); + + for(size_t i=0, e=stores.size(); i!=e; i++){ + llvm::StoreInst* si = stores[i]; + llvm::AllocaInst* ai = llvm::dyn_cast(si->getPointerOperand()); + if (volatile_vars.count(ai) != 0){ + si->setVolatile(true); + } + } +} + +static void fix_setjmps(){ + if (!has_tries) + return; + + if (EC != NULL){ + //There is a Catch/Finally in the function, so a setjmp is somewhere in the entry block + //and the first branch is a conditional branch: true -> catch part, false -> try part + + llvm::BranchInst* br = llvm::dyn_cast(entry_block->getTerminator()); + assert(br && br->isConditional()); + + llvm::BasicBlock* catch_block = br->getSuccessor(0); + llvm::BasicBlock* try_block = br->getSuccessor(1); + + fix_setjmp(catch_block, try_block, "large_end_try"); + } + + for(size_t i=0, e=try_blocks.size(); i!=e; i++){ + llvm::BasicBlock* BB = try_blocks[i]; + + llvm::BranchInst* br = llvm::dyn_cast(BB->getTerminator()); + assert(br && br->isConditional()); + + llvm::BasicBlock* catch_block = br->getSuccessor(0); + llvm::BasicBlock* try_block = br->getSuccessor(1); + + fix_setjmp(catch_block, try_block, "end_try"); + } +} + + +struct DynamicAllocatedString { + char* data; + int len; + DynamicAllocatedString(const char* data, int len){ + this->data = new char[len]; + this->len = len; + memcpy(this->data, data, len); + } + ~DynamicAllocatedString(){ + delete[] data; + } +}; + +static std::vector extern_signature_strings; +static std::map extern_signatures; + +static void func_extern_call_variant_vararg(void* return_value_addr, void* func_addr, int nargs, TYPE return_type){ + std::map& signatures = extern_signatures; + + char signature_string[nargs+sizeof(TYPE)]; + + *(TYPE*)&signature_string[nargs] = return_type; + + for(int i=0; isetTargetTriple("x86_64-pc-linux-gnu"); + M->setDataLayout("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"); + } else { + M->setTargetTriple("i386-unknown-linux-gnu"); + M->setDataLayout("e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128"); + } + EE->addModule(M); + + static char buf[256] = "extern_func_caller_"; + int eob = strlen("extern_func_caller_"); + for(int i=0; i(M->getOrInsertFunction(buf, get_function_type('v', "pp", true))); + llvm::Function::arg_iterator arg_it = llvm_function->arg_begin(); + llvm::Value* func_addr_arg = arg_it++; + llvm::Value* return_value_addr_arg = arg_it; + + entry_block = create_bb("entry"); + builder = new llvm::IRBuilder<>(entry_block); + + std::vector ft; + std::vector orig_args; + std::vector func_args; + ft.resize(nargs); + orig_args.resize(nargs); + func_args.resize(nargs); + + llvm::Value* SP_base = builder->CreateGEP(read_sp(), getInteger(TARGET_BITS, -nargs)); + llvm::Value* SP_current = SP_base; + + for(int i=0; iCreateGEP(SP_current, getInteger(TARGET_BITS, 1)); + } + + //Call function + llvm::Type* function_type = llvm::FunctionType::get(extern_types[return_type > T_OBJECT ? T_OBJECT : return_type], ft, true); + llvm::Value* call_function = builder->CreateBitCast(func_addr_arg, pointer_t(function_type)); + + llvm::Value* ret = builder->CreateCall(call_function, func_args); + + //Manage return value + ret = codegen_extern_manage_return_value(ret, return_type); + if (return_type != T_VOID) + builder->CreateStore(ret, builder->CreateBitCast(return_value_addr_arg, pointer_t(TYPE_llvm(return_type)))); + + //Release arguments + for(int i=nargs; i --> 0; ){ + release(orig_args[i], signature_string[i]); + c_SP(-1); + } + + builder->CreateRetVoid(); + + //M->dump(); + + llvm::verifyModule(*M); + + llvm::FunctionPassManager FPM(M); + llvm::PassManager MPM; + llvm::PassManagerBuilder PMB; + PMB.OptLevel = 2; + PMB.SizeLevel = 1; + PMB.populateFunctionPassManager(FPM); + PMB.populateModulePassManager(MPM); + + FPM.doInitialization(); + FPM.run(*llvm_function); + FPM.doFinalization(); + MPM.run(*M); + + //Print out the code after optimization + if (MAIN_debug){ + debug_print_line(); + fprintf(stderr, "gb.jit: dumping vararg extern call function\n"); + debug_print_line(); + M->dump(); + debug_print_line(); + fputc('\n', stderr); + } + + void (*fn)(void*, void*) = (void(*)(void*, void*))EE->getPointerToFunction(llvm_function); + + delete builder; + + llvm_function->deleteBody(); + mappings.clear(); + + extern_signature_strings.emplace_back((char*)signature_string, nargs+sizeof(TYPE)); + signatures.insert(std::pair(llvm::StringRef(extern_signature_strings.back().data, nargs+sizeof(TYPE)), fn)); + (*fn)(func_addr, return_value_addr); + } else { + (*(it->second))(func_addr, return_value_addr); + } +} + +static void func_void(){ + RP->type = T_VOID; + JIF.F_EXEC_leave_keep(); +} +static void func_boolean(){ + RP->type = T_BOOLEAN; + RP->_boolean.value = 0; + JIF.F_EXEC_leave_keep(); +} +static void func_byte(){ + RP->type = T_BYTE; + RP->_byte.value = 0; + JIF.F_EXEC_leave_keep(); +} +static void func_short(){ + RP->type = T_SHORT; + RP->_short.value = 0; + JIF.F_EXEC_leave_keep(); +} +static void func_integer(){ + RP->type = T_INTEGER; + RP->_integer.value = 0; + JIF.F_EXEC_leave_keep(); +} +static void func_long(){ + RP->type = T_LONG; + RP->_long.value = 0; + JIF.F_EXEC_leave_keep(); +} +static void func_single(){ + RP->type = T_SINGLE; + RP->_single.value = 0.0f; + JIF.F_EXEC_leave_keep(); +} +static void func_float(){ + RP->type = T_FLOAT; + RP->_float.value = 0.0; + JIF.F_EXEC_leave_keep(); +} +static void func_date(){ + RP->type = T_DATE; + RP->_date.date = 0; + RP->_date.time = 0; + JIF.F_EXEC_leave_keep(); +} +static void func_string(){ + RP->type = T_CSTRING; + RP->_string.addr = NULL; + RP->_string.start = 0; + RP->_string.len = 0; + JIF.F_EXEC_leave_keep(); +} +static void func_pointer(){ + RP->type = T_POINTER; + RP->_pointer.value = NULL; + JIF.F_EXEC_leave_keep(); +} +static void func_variant(){ + RP->type = T_VARIANT; + RP->_variant.vtype = T_NULL; + JIF.F_EXEC_leave_keep(); +} +static void func_object(){ + RP->type = T_OBJECT; + RP->_object.object = NULL; + JIF.F_EXEC_leave_keep(); +} + +void JIT_codegen(){ + if (FP->type <= T_OBJECT && all_statements.size() == 1){ + if (auto re = dyn_cast(all_statements.front()->expr)){ + if (re->retval == NULL){ + //Standard empty function + + delete all_statements.front(); + all_statements.clear(); + + static void (* const funcs[17])(void) = {func_void, func_boolean, func_byte, func_short, func_integer, + func_long, func_single, func_float, func_date, func_string, func_string, + func_pointer, func_variant, NULL, NULL, NULL, func_object}; + CP->jit_functions[EXEC.index] = funcs[FP->type]; + + return; + } + } + } + + static bool inited = false; + if (!inited){ + llvm_init(); + inited = true; + } + + M = new llvm::Module("jit_mod", llvm_context); + if (TARGET_BITS == 64){ + M->setTargetTriple("x86_64-pc-linux-gnu"); + M->setDataLayout("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"); + } else { + M->setTargetTriple("i386-unknown-linux-gnu"); + M->setDataLayout("e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128"); + } + if (EE) + EE->addModule(M); + else + EE = llvm::EngineBuilder(M)/*.setOptLevel(llvm::CodeGenOpt::Aggressive)*/.create(); + + + static int counter = 0; + char buf[256]; + sprintf(buf, "func_%d_%s_%d", counter++, CP->name, EXEC.index); + llvm_function = llvm::cast(M->getOrInsertFunction(buf, get_function_type('v', ""))); + + entry_block = create_bb("entry"); + builder = new llvm::IRBuilder<>(entry_block); + + //builder->CreateCall(get_global_function((void*)&puts, 'v', "p"), get_global((void*)"hej", llvmType(getInt8Ty))); + //builder->CreateCall2(get_global_function_vararg(printf, 'v', "p"), get_global((void*)"enter %d\n", llvmType(getInt8Ty)), getInteger(32, counter-1)); + + current_op = OP == NULL ? get_nullptr() : read_global((void*)&OP); + temp_value = builder->CreateAlloca(value_type); + temp_value2 = builder->CreateAlloca(value_type); + temp_voidptr = builder->CreateAlloca(llvmType(getInt8PtrTy)); + temp_int = builder->CreateAlloca(llvmType(getInt32Ty)); + temp_double = builder->CreateAlloca(llvmType(getDoubleTy)); + temp_date = builder->CreateAlloca(date_type); + temp_2longs = builder->CreateAlloca(two_longs_type); + temp_errcontext1 = builder->CreateAlloca(llvm::ArrayType::get(llvmType(getInt8Ty), sizeof(ERROR_CONTEXT))); + temp_errcontext2 = builder->CreateAlloca(llvm::ArrayType::get(llvmType(getInt8Ty), sizeof(ERROR_CONTEXT))); + temp_got_error = builder->CreateAlloca(llvmType(getInt1Ty)); + temp_got_error2 = builder->CreateAlloca(llvmType(getInt1Ty)); + if (ngosubs != 0){ + //temp_gosub_stack = builder->CreateAlloca(llvm::ArrayType::get(llvmType(getInt8PtrTy), ngosubs + 100)); + /*temp_num_gosubs_on_stack = builder->CreateAlloca(llvmType(getInt32Ty)); + builder->CreateStore(getInteger(32, 0), temp_num_gosubs_on_stack);*/ + +#ifndef GOSUB_ON_STACK + builder->CreateCall3(get_global_function(GB.NewArray, 'v', "pii"), + get_global(&GP, llvmType(getInt8Ty)), getInteger(32, sizeof(STACK_GOSUB)), getInteger(32, 0)); +#endif + + /*gosub_return_point = builder->CreateAlloca(llvmType(getInt8PtrTy)); + builder->CreateStore(get_nullptr(), gosub_return_point);*/ + gosub_return_point = builder->CreateAlloca(llvmType(getInt16Ty)); + builder->CreateStore(getInteger(16, 0), gosub_return_point); + } + //For GoSubs and JR_try_unwind + gp = builder->CreateAlloca(pointer_t(value_type)); + builder->CreateStore(builder->CreateGEP(read_global((void*)&BP, pointer_t(value_type)), getInteger(TARGET_BITS, FP->n_local + FP->n_ctrl)), gp); + + has_tries = false; + try_blocks.clear(); + + init_locals(); + + codegen_statements(); + + finish_gosub_returns(); + create_return(); + + insert_pending_branches(); + //puts("Dump:"); + + //Print out the code before optimization + //M->dump(); + + llvm::verifyModule(*M); + + for(size_t i=0, e=all_statements.size(); i!=e; i++) + delete all_statements[i]; + all_statements.clear(); + + fix_setjmps(); + + run_optimizations(); + + //Print out the code after optimization + if (MAIN_debug){ + debug_print_line(); + fprintf(stderr, "gb.jit: dumping function %s.", CP->name); + if (FP->debug) + fprintf(stderr, "%s:\n", FP->debug->name); + else + fprintf(stderr, "%d:\n", EXEC.index); + debug_print_line(); + M->dump(); + debug_print_line(); + fputc('\n', stderr); + } + + void (*fn)(void) = (void(*)(void))EE->getPointerToFunction(llvm_function); + + delete builder; + + llvm_function->deleteBody(); + + CP->jit_functions[EXEC.index] = fn; + /*if (CP->jit_data == NULL){ + ALLOC_ZERO(&CP->jit_data, sizeof(llvm::ExecutionEngine*) * CP->load->n_func, "JIT_codegen"); + } + ((llvm::ExecutionEngine**)CP->jit_data)[EXEC.index] = EE;*/ + + mappings.clear(); + //variable_mappings.clear(); +} + + +static void JIT_cleanup(llvm::ExecutionEngine** EE, int n_func){ + /*for(int i=0; ifreeMachineCodeForFunction(EE[i]->FindFunctionNamed("func")); + delete EE[i]; + } + }*/ +} + +void JIT_end(){ + delete EE; + llvm::llvm_shutdown(); +} + +///DEBUG +void print_type(llvm::Value* v){ + v->getType()->print(llvm::outs()); + puts(""); +} +void print_expr_type(Expression* expr){ + puts(typeid(*expr).name()); +} diff --git a/gb.jit.llvm/src/jit_codegen_conv.h b/gb.jit.llvm/src/jit_codegen_conv.h new file mode 100644 index 00000000..09e30f6f --- /dev/null +++ b/gb.jit.llvm/src/jit_codegen_conv.h @@ -0,0 +1,786 @@ +/*************************************************************************** + + jit_codegen_conv.h + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __JIT_CODEGEN_CONV_H +#define __JIT_CODEGEN_CONV_H + +extern "C" { +#include "gbx_number.h" +#include "gb_common_buffer.h" +} + +#ifdef __CYGWIN__ +#define __finite finite +#endif + +llvm::Value* JIT_conv_to_variant(Expression* value, llvm::Value* val, bool on_stack, bool* no_ref_variant){ + llvm::Value* ret; + if (TYPE_is_string(value->type)){ + ret = string_for_array_or_variant(val, value->type); + ret = get_new_struct(variant_type, getInteger(TARGET_BITS, T_STRING), builder->CreatePtrToInt(ret, llvmType(getInt64Ty))); + } else { + if (value->type < T_OBJECT && no_ref_variant) + *no_ref_variant = true; + + llvm::Value* data; + llvm::Type* t64 = llvmType(getInt64Ty); + + if (value->type < T_OBJECT) + ret = get_new_struct(variant_type, getInteger(TARGET_BITS, value->type)); + else + ret = get_new_struct(variant_type, builder->CreatePtrToInt(extract_value(val, 0), LONG_TYPE)); + + switch(value->type){ + case T_BYTE: + data = builder->CreateZExt(val, t64); + break; + case T_BOOLEAN: + case T_SHORT: + case T_INTEGER: + data = builder->CreateSExt(val, t64); + break; + case T_LONG: + data = val; + break; + case T_SINGLE: + data = builder->CreateBitCast(val, llvmType(getInt32Ty)); + data = builder->CreateZExt(data, t64); + break; + case T_FLOAT: + data = builder->CreateBitCast(val, t64); + break; + case T_DATE: + data = builder->CreateShl(builder->CreateZExt(extract_value(val, 1), t64), getInteger(64, 32)); + data = builder->CreateOr(data, builder->CreateZExt(extract_value(val, 0), t64)); + break; + case T_POINTER: + data = builder->CreatePtrToInt(val, t64); + break; + case T_CLASS: + assert(dynamic_cast(value)); + data = getInteger(64, (uint64_t)(void*)((PushClassExpression*)value)->klass); + val = builder->CreateIntToPtr(data, llvmType(getInt8PtrTy)); + break; + case T_NULL: + break; + default: + data = builder->CreatePtrToInt(extract_value(val, 1), t64); + break; + } + if (value->type != T_NULL) + ret = insert_value(ret, data, 1); + + if (on_stack){ //FIXME is this code really good/correct? stack seems strange + c_SP(-value->on_stack+1); + llvm::Value* addr = builder->CreateBitCast(get_value_on_top_addr(), pointer_t(LONG_TYPE)); + + builder->CreateStore(getInteger(TARGET_BITS, T_VARIANT), addr); + + addr = builder->CreateGEP(addr, getInteger(TARGET_BITS, 1)); + if (value->type < T_OBJECT) + builder->CreateStore(getInteger(TARGET_BITS, value->type), addr); + else + builder->CreateStore(builder->CreatePtrToInt(extract_value(val, 0), LONG_TYPE), addr); + + if (value->type != T_NULL){ + addr = builder->CreateGEP(addr, getInteger(TARGET_BITS, 1)); + if (value->type == T_BYTE) + builder->CreateStore(builder->CreateZExt(val, llvmType(getInt32Ty)), builder->CreateBitCast(addr, llvmType(getInt32PtrTy))); + else if (value->type < T_INTEGER) + builder->CreateStore(builder->CreateSExt(val, llvmType(getInt32Ty)), builder->CreateBitCast(addr, llvmType(getInt32PtrTy))); + else if (value->type < T_OBJECT) + builder->CreateStore(val, builder->CreateBitCast(addr, pointer_t(TYPE_llvm(value->type)))); + else + builder->CreateStore(extract_value(val, 1), builder->CreateBitCast(addr, charPP)); + } + return ret; + } + } + c_SP(-value->on_stack+on_stack); + if (on_stack) + set_top_value(ret, T_VARIANT); + return ret; +} + +llvm::Value* ConvExpression::codegen_get_value() +{ + Expression* value = expr; + static const void *jump[16][16] = + { + /* ,-------> void b c h i l g f d cs s p v func class n */ + // | + /* void */ { &&__OK, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, }, + /* b */ { &&__N, &&__OK, &&__b2c, &&__b2h, &&__b2i, &&__b2l, &&__b2g, &&__b2f, &&__N, &&__b2s, &&__b2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* c */ { &&__N, NULL, &&__OK, &&__c2h, &&__c2i, &&__c2l, &&__c2g, &&__c2f, &&__c2d, &&__c2s, &&__c2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* h */ { &&__N, NULL, &&__h2c, &&__OK, &&__h2i, &&__h2l, &&__h2g, &&__h2f, &&__h2d, &&__h2s, &&__h2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* i */ { &&__N, NULL, &&__i2c, &&__i2h, &&__OK, &&__i2l, &&__i2g, &&__i2f, &&__i2d, &&__i2s, &&__i2s, &&__i2p, &&__2v, &&__N, &&__N, &&__N, }, + /* l */ { &&__N, NULL, &&__l2c, &&__l2h, &&__l2i, &&__OK, &&__l2g, &&__l2f, &&__l2d, &&__l2s, &&__l2s, &&__l2p, &&__2v, &&__N, &&__N, &&__N, }, + /* g */ { &&__N, NULL, &&__g2c, &&__g2h, &&__g2i, &&__g2l, &&__OK, &&__g2f, &&__g2d, &&__g2s, &&__g2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* f */ { &&__N, NULL, &&__f2c, &&__f2h, &&__f2i, &&__f2l, &&__f2g, &&__OK, &&__f2d, &&__f2s, &&__f2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* d */ { &&__N, &&__d2b, &&__d2c, &&__d2h, &&__d2i, &&__d2l, &&__d2g, &&__d2f, &&__OK, &&__d2s, &&__d2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* cs */ { &&__N, &&__s2b, &&__s2c, &&__s2h, &&__s2i, &&__s2l, &&__s2g, &&__s2f, &&__s2d, &&__OK, &&__OK, &&__N, &&__s2v, &&__N, &&__N, &&__N, }, + /* s */ { &&__N, &&__s2b, &&__s2c, &&__s2h, &&__s2i, &&__s2l, &&__s2g, &&__s2f, &&__s2d, &&__OK, &&__OK, &&__N, &&__s2v, &&__N, &&__N, &&__N, }, + /* p */ { &&__N, &&__N, &&__N, &&__N, &&__p2i, &&__p2l, &&__N, &&__N, &&__N, &&__N, &&__N, &&__OK, &&__2v, &&__N, &&__N, &&__N, }, + /* v */ { &&__N, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__OK, &&__N, &&__v2, &&__v2, }, + /* func */ { &&__N, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__F2p, &&__func, &&__OK, &&__N, &&__func, }, + /* class */ { &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__2v, &&__N, &&__OK, &&__N, }, + /* null */ { &&__N, NULL, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, NULL, NULL, NULL, &&__N, &&__2v, &&__N, &&__N, &&__OK, }, + }; + + llvm::Value* val = NULL; + llvm::Value* ret; + + if (value->type == (TYPE)-1) + goto __UNKNOWN; + else if ((type | value->type) >> 4) + goto __OBJECT; + else { + if (value->type != T_FUNCTION && value->type != T_CLASS) + val = value->codegen_get_value(); + goto *jump[value->type][type]; + } + + +__d2b: + ret = builder->CreateICmpNE(builder->CreateOr(extract_value(val, 0), extract_value(val, 1)), getInteger(32, 0)); + goto __DONE; + +__b2c: +__b2h: +__b2i: +__h2i: +__b2l: +__h2l: +__i2l: + + ret = builder->CreateSExt(val, TYPE_llvm(type)); + goto __DONE; + +__h2c: +__i2c: +__l2c: +__i2h: +__l2h: +__l2i: + + ret = builder->CreateTrunc(val, TYPE_llvm(type)); + goto __DONE; + +__g2c: +__f2c: + + ret = builder->CreateFPToUI(val, llvmType(getInt8Ty)); + goto __DONE; + +__c2h: +__c2i: +__c2l: + + ret = builder->CreateZExt(val, TYPE_llvm(type)); + goto __DONE; + +__g2h: +__f2h: +__g2i: +__f2i: +__g2l: +__f2l: + + ret = builder->CreateFPToSI(val, TYPE_llvm(type)); + goto __DONE; + +__p2i: +__p2l: + + ret = builder->CreatePtrToInt(val, TYPE_llvm(type)); + goto __DONE; + +__c2g: +__c2f: + + ret = builder->CreateUIToFP(val, TYPE_llvm(type)); + goto __DONE; + +__b2g: +__h2g: +__i2g: +__b2f: +__h2f: +__i2f: +__l2f: + + ret = builder->CreateSIToFP(val, TYPE_llvm(type)); + goto __DONE; + +__l2g: + + ret = builder->CreateSIToFP(val, TYPE_llvm(type)); + goto __test_overflow; + + +__f2g: + + ret = builder->CreateFPTrunc(val, llvmType(getFloatTy)); + goto __test_overflow; + +__test_overflow: +{ + llvm::Value* ret64 = type == T_SINGLE ? builder->CreateFPExt(ret, llvmType(getDoubleTy)) : ret; + llvm::Value* res = builder->CreateCall(get_global_function(__finite, 'i', "d"), ret64); + + gen_if_noreturn(builder->CreateICmpEQ(res, getInteger(32, 0)), [&](){ + create_throw(E_OVERFLOW); + }, "test_overflow"); + + goto __DONE; +} + +__g2f: + + ret = builder->CreateFPExt(val, llvmType(getDoubleTy)); + goto __DONE; + +__c2d: +__h2d: +__i2d: + + ret = val; + if (value->type != T_INTEGER){ + if (value->type == T_BYTE) + ret = builder->CreateZExt(ret, llvmType(getInt32Ty)); + else + ret = builder->CreateSExt(ret, llvmType(getInt32Ty)); + } + ret = gen_max(getInteger(32, 0), ret); + ret = get_new_struct(date_type, ret, getInteger(32, 0)); + goto __DONE; + +__l2d: + + ret = builder->CreateSelect( + builder->CreateICmpSLT(val, getInteger(64, 0)), + getInteger(32, 0), + builder->CreateSelect( + builder->CreateICmpSGT(val, getInteger(64, INT_MAX)), + getInteger(32, INT_MAX), + builder->CreateTrunc(val, llvmType(getInt32Ty)) + ) + ); + ret = get_new_struct(date_type, ret, getInteger(32, 0)); + goto __DONE; + +__g2d: +{ + llvm::Value* ival = builder->CreateCall(get_global_function(floorf, 'f', "f"), val); + llvm::Value* timepart = builder->CreateFPToSI(builder->CreateFAdd(builder->CreateFMul(builder->CreateFSub(val, ival), getFloat(86400000.0f)), getFloat(0.5f)), llvmType(getInt32Ty)); + ret = get_new_struct(date_type, builder->CreateFPToSI(ival, llvmType(getInt32Ty)), timepart); + goto __DONE; +} + +__f2d: +{ + llvm::Value* ival = builder->CreateCall(get_global_function(floor, 'd', "d"), val); + llvm::Value* timepart = builder->CreateFPToSI(builder->CreateFAdd(builder->CreateFMul(builder->CreateFSub(val, ival), getFloat(86400000.0)), getFloat(0.5)), llvmType(getInt32Ty)); + ret = get_new_struct(date_type, builder->CreateFPToSI(ival, llvmType(getInt32Ty)), timepart); + goto __DONE; +} + +__d2c: +__d2h: +__d2i: +__d2l: + + ret = extract_value(val, 0); + if (type < T_INTEGER) + ret = builder->CreateTrunc(ret, TYPE_llvm(type)); + else if (type == T_LONG) + ret = builder->CreateSExt(ret, llvmType(getInt64Ty)); + goto __DONE; + +__d2g: +{ + llvm::Value* datepart = builder->CreateSIToFP(extract_value(val, 0), llvmType(getFloatTy)); + llvm::Value* timepart = builder->CreateSIToFP(extract_value(val, 1), llvmType(getFloatTy)); + + ret = builder->CreateFAdd(datepart, builder->CreateFDiv(timepart, getFloat(86400000.0f))); + goto __DONE; +} + +__d2f: +{ + llvm::Value* datepart = builder->CreateSIToFP(extract_value(val, 0), llvmType(getDoubleTy)); + llvm::Value* timepart = builder->CreateSIToFP(extract_value(val, 1), llvmType(getDoubleTy)); + + ret = builder->CreateFAdd(datepart, builder->CreateFDiv(timepart, getFloat(86400000.0))); + goto __DONE; +} + +__b2s: + + ret = builder->CreateSelect(val, + get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_global((void*)"T", llvmType(getInt8Ty)), getInteger(32, 0), getInteger(32, 1)), + get_new_struct(string_type, getInteger(TARGET_BITS, T_CSTRING), get_nullptr(), getInteger(32, 0), getInteger(32, 0)) + ); + goto __DONE; + +__c2s: +__h2s: +__i2s: +__l2s: +{ + llvm::Value* num; + if (value->type == T_BYTE) + num = builder->CreateZExt(val, llvmType(getInt64Ty)); + else if (value->type == T_LONG) + num = val; + else + num = builder->CreateSExt(val, llvmType(getInt64Ty)); + + c_SP(-value->on_stack+on_stack); + + llvm::Value* addr = on_stack ? get_value_on_top_addr() : temp_value; + + builder->CreateCall4(get_global_function_jif(NUMBER_int_to_string, 'v', "liip"), + num, getInteger(32, 0), getInteger(32, 10), builder->CreateBitCast(addr, llvmType(getInt8PtrTy))); + + ret = read_value(addr, T_STRING); + borrow(ret, T_STRING); + return ret; +} + +__g2s: +__f2s: +{ + c_SP(-value->on_stack+on_stack); + + if (value->type == T_SINGLE) + val = builder->CreateFPExt(val, llvmType(getDoubleTy)); + + llvm::Value* args[] = { + val, getInteger(32, LF_GENERAL_NUMBER), get_nullptr(), getInteger(32, 0), + builder->CreateBitCast(temp_voidptr, llvmType(getInt8PtrTy)), + builder->CreateBitCast(temp_int, llvmType(getInt8PtrTy)), getInteger(8, 0) + }; + /*llvm::Value* got_error =*/ builder->CreateCall(get_global_function_jif(LOCAL_format_number, 'c', "dipippc"), args); + + llvm::Value* addr = on_stack ? get_value_on_top_addr() : temp_value; + + builder->CreateCall3(get_global_function_jif(STRING_new_temp_value, 'v', "ppi"), + builder->CreateBitCast(addr, llvmType(getInt8PtrTy)), + builder->CreateLoad(temp_voidptr), + builder->CreateLoad(temp_int) + ); + + ret = read_value(addr, T_STRING); + borrow(ret, T_STRING); + return ret; +} + +__d2s: +{ + llvm::Value* src_addr = value->on_stack ? get_value_on_top_addr() : temp_value; + if (!value->on_stack) + store_value(src_addr, val, T_DATE, false); + + c_SP(-value->on_stack+on_stack); + llvm::Value* addr = on_stack ? get_value_on_top_addr() : temp_value; + + static char buffer[128]; + + llvm::Value* len = builder->CreateCall2(get_global_function_jif(DATE_to_string, 'i', "pp"), + get_global((void*)buffer, llvmType(getInt8Ty)), + builder->CreateBitCast(src_addr, llvmType(getInt8PtrTy)) + ); + builder->CreateCall3(get_global_function_jif(STRING_new_temp_value, 'v', "ppi"), + builder->CreateBitCast(addr, llvmType(getInt8PtrTy)), + get_global((void*)buffer, llvmType(getInt8Ty)), + len + ); + + ret = read_value(addr, T_STRING); + borrow(ret, T_STRING); + return ret; +} + +__s2b: + + ret = builder->CreateICmpNE(extract_value(val, 3), getInteger(32, 0)); + release(val, value->type); + goto __DONE; + +__s2c: +__s2h: +__s2i: +__s2l: +{ + auto str = get_string_len(val); + + c_SP(-value->on_stack+on_stack); + llvm::Value* addr = (on_stack && (type == T_INTEGER || type == T_LONG)) ? get_value_on_top_addr() : temp_value; + + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(NUMBER_from_string, 'c', "ipip"), + getInteger(32, type == T_LONG ? NB_READ_LONG : NB_READ_INTEGER), str.first, str.second, + builder->CreateBitCast(addr, llvmType(getInt8PtrTy)) + ); + + release(val, value->type); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, 0)), [&](){ + create_throw(E_TYPE, JIF.F_TYPE_get_name(type), JIF.F_TYPE_get_name(value->type)); + }); + + llvm::Value* intval = read_value(addr, type == T_LONG ? T_LONG : T_INTEGER); + + if (type < T_INTEGER) + intval = builder->CreateTrunc(intval, TYPE_llvm(type)); + + if (on_stack && addr == temp_value) + set_top_value(intval, type); + + return intval; +} + +__s2g: +__s2f: +{ + auto str = get_string_len(val); + + c_SP(-value->on_stack+on_stack); + llvm::Value* addr = (on_stack && type == T_FLOAT) ? get_value_on_top_addr() : temp_value; + + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(NUMBER_from_string, 'c', "ipip"), + getInteger(32, NB_READ_FLOAT), str.first, str.second, + builder->CreateBitCast(addr, llvmType(getInt8PtrTy)) + ); + + release(val, value->type); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, 0)), [&](){ + create_throw(E_TYPE, JIF.F_TYPE_get_name(type), JIF.F_TYPE_get_name(value->type)); + }); + + llvm::Value* ret = read_value(addr, T_FLOAT); + + if (type == T_SINGLE) + ret = builder->CreateFPTrunc(ret, llvmType(getFloatTy)); + + if (on_stack && addr == temp_value) + set_top_value(ret, type); + + return ret; +} + +__s2d: +{ + auto str = get_string_len(val); + + c_SP(-value->on_stack+on_stack); + llvm::Value* addr = on_stack ? get_value_on_top_addr() : temp_value; + + llvm::Value* got_error = builder->CreateCall4(get_global_function_jif(DATE_from_string, 'c', "pipc"), + str.first, str.second, + builder->CreateBitCast(addr, llvmType(getInt8PtrTy)), + getInteger(8, false) + ); + + release(val, value->type); + + gen_if_noreturn(builder->CreateICmpNE(got_error, getInteger(8, 0)), [&](){ + create_throw(E_TYPE, JIF.F_TYPE_get_name(type), JIF.F_TYPE_get_name(value->type)); + }); + + return read_value(addr, T_DATE); +} + + /*addr = value->type == T_STRING ? value->_string.addr : NULL; + + if (DATE_from_string(value->_string.addr + value->_string.start, value->_string.len, value, FALSE)) + goto __N; + + STRING_unref(&addr); + return;*/ + +__UNKNOWN: + value->on_stack = true; + value->codegen_on_stack(); +__v2: + + builder->CreateCall2(get_global_function_jif(VALUE_convert, 'v', "pj"), + builder->CreateBitCast(get_value_on_top_addr(), llvmType(getInt8PtrTy)), + getInteger(TARGET_BITS, type)); + return ret_top_stack(type, on_stack); + + /*VALUE_undo_variant(value); + goto __CONV;*/ + +__s2v: +__2v: + return JIT_conv_to_variant(value, val, on_stack, &no_ref_variant); + + /* VALUE_put ne fonctionne pas avec T_STRING ! */ + /*if (value->type != T_NULL) + VALUE_put(value, &value->_variant.value, value->type); + + value->_variant.vtype = value->type; + value->type = T_VARIANT; + return;*/ + +__func: + + //if (unknown_function(value)) + // goto __CONV; + //else + goto __N; + +__i2p: +__l2p: + + ret = builder->CreateIntToPtr(val, llvmType(getInt8PtrTy)); + goto __DONE; + +__F2p: + + //FIXME + abort(); + /*value->_pointer.value = EXTERN_make_callback(&value->_function); + value->type = T_POINTER; + return;*/ + +__OBJECT: +{ + if (!TYPE_is_object(type)){ + val = value->codegen_get_value(); + if (type == T_BOOLEAN){ + llvm::Value* obj = extract_value(val, 1); + ret = builder->CreateICmpNE(obj, get_nullptr()); + unref_object(obj); + goto __DONE; + } + //type == T_VARIANT + goto __2v; + } + + if (!TYPE_is_object(value->type)){ + if (value->type == T_NULL){ + value->on_stack = false; + ret = get_new_struct(object_type, get_global((void*)type, llvmType(getInt8Ty)), get_nullptr()); + goto __DONE; + } + + if (value->type == T_VARIANT){ + //Variant to Object + val = value->codegen_get_value(); + goto __v2; + } + + if (value->type == T_CLASS){ + llvm::Value* object = get_global((void*)((PushClassExpression*)value)->klass, llvmType(getInt8Ty)); + + val = get_new_struct(object_type, + get_global((void*)GB.FindClass("Class"), llvmType(getInt8Ty)), + object); + + borrow_object_no_nullcheck(object); + + //To simplify code below + value->on_stack = false; + value->type = (TYPE)(void*)GB.FindClass("Class"); + } + } + + if (val == NULL) + val = value->codegen_get_value(); + + /*llvm::Value* klass = extract_value(val, 0);*/ + llvm::Value* object = extract_value(val, 1); + llvm::Value* to_class = get_global((void*)type, llvmType(getInt8Ty)); + + if (type == T_OBJECT){ + ret = get_new_struct(object_type, builder->CreateIntToPtr(getInteger(TARGET_BITS, T_OBJECT), llvmType(getInt8PtrTy)), object); + goto __DONE; + } + + c_SP(-value->on_stack); + + ret = gen_if_else_phi(builder->CreateICmpEQ(object, get_nullptr()), [&](){ + //OK + return get_new_struct(object_type, to_class, object); + }, [&](){ + if (value->type == T_OBJECT){ + //klass = load_element(builder->CreateBitCast(klass, pointer_t(OBJECT_TYPE)), 0); + + //return get_new_struct(object_type, to_class, builder->CreateCall3(get_global_function(JR_conv_from_T_OBJECT, 'p', "pp"), object, to_class)); + } + + /*//Check if klass is virtual + llvm::Value* class_flags = builder->CreateBitCast(builder->CreateGEP(klass, getInteger(TARGET_BITS, TARGET_BITS == 64 ? 32 : 20)), llvmType(getInt32Ty)); + llvm::Value* is_virtual = builder->CreateTrunc(builder->CreateLShr(class_flags, getInteger(32, 10)), llvmType(getInt1Ty)); + + gen_if_noreturn(is_virtual, [&](){ + create_throw(E_VIRTUAL); + }, "virtual_class");*/ + + if (value->type != T_OBJECT){ + if (JIF.F_CLASS_inherits((CLASS*)(void*)value->type, (CLASS*)(void*)type)){ + //This cast is always possible + return get_new_struct(object_type, to_class, object); + } + + /*llvm::Value* first_invalid_test = builder->CreateICmpEQ(builder->CreateCall2(get_global_function(CLASS_inherits, 'c', "pp"), klass, to_class), getInteger(8, false)); + + llvm::Value* invalid_cast = gen_and_if(builder->CreateICmpNE(klass, to_class), [&](){ + return builder->CreateICmpEQ(builder->CreateCall2(get_global_function(CLASS_inherits, 'c', "pp"), klass, to_class), getInteger(8, false)); + });*/ + } + return get_new_struct(object_type, to_class, builder->CreateCall2(get_global_function(JR_object_cast, 'p', "pp"), object, to_class)); + + }); + + if (on_stack) + push_value(ret, type); + return ret; +} +#if 0 + if (!TYPE_is_object(type)) + { + if (type == T_BOOLEAN) + { + test = (value->_object.object != NULL); + OBJECT_UNREF(value->_object.object, "VALUE_convert"); + value->_boolean.value = -test; + value->type = T_BOOLEAN; + return; + } + + if (type == T_VARIANT) + goto __2v; + + goto __N; + } + + if (!TYPE_is_object(value->type)) + { + if (value->type == T_NULL) + { + OBJECT_null(value, (CLASS *)type); /* marche aussi pour type = T_OBJECT */ + goto __TYPE; + } + + if (value->type == T_VARIANT) + goto __v2; + + if (value->type == T_FUNCTION) + goto __func; + + if (value->type == T_CLASS) + { + klass = value->_class.klass; + + if (CLASS_is_virtual(klass)) + THROW(E_VIRTUAL); + + CLASS_load(klass); + + if (klass->auto_create) + value->_object.object = CLASS_auto_create(klass, 0); + else + value->_object.object = klass; + + OBJECT_REF(value->_object.object, "VALUE_convert"); + value->type = T_OBJECT; + /* on continue... */ + } + else + goto __N; + } + + if (value->_object.object == NULL) + goto __TYPE; + + if (value->type == T_OBJECT) + { + /*if (value->_object.object == NULL) + goto __TYPE;*/ + + klass = OBJECT_class(value->_object.object); + /* on continue */ + } + else + klass = value->_object.klass; + + if (CLASS_is_virtual(klass)) + THROW(E_VIRTUAL); + + if (type == T_OBJECT) + goto __TYPE; +#endif + +/* + if ((klass == (CLASS *)type) || CLASS_inherits(klass, (CLASS *)type)) + goto __TYPE; + + if (value->type != T_OBJECT && value->_object.object) + { + klass = OBJECT_class(value->_object.object); + value->type = T_OBJECT; + goto __RETRY; + } + + if (klass->special[SPEC_CONVERT] != NO_SYMBOL) + { + void *conv = ((void *(*)())(CLASS_get_desc(klass, klass->special[SPEC_CONVERT])->constant.value._pointer))(value->_object.object, type); + if (conv) + { + OBJECT_REF(conv, "VALUE_conv"); + OBJECT_UNREF(value->_object.object, "VALUE_conv"); + value->_object.object = conv; + goto __TYPE; + } + } + + THROW(E_TYPE, TYPE_get_name(type), TYPE_get_name((TYPE)klass));*/ + +__DONE: + + c_SP(-value->on_stack+on_stack); + if (on_stack) + set_top_value(ret, type); + return ret; + +__OK: + + abort(); + return val; + +__N: + + THROW(E_TYPE, JIF.F_TYPE_get_name(type), JIF.F_TYPE_get_name(value->type)); + +__NR: + + THROW(E_NRETURN); +} + +#endif /* __JIT_CODEGEN_CONV_H */ diff --git a/gb.jit.llvm/src/jit_compile.cpp b/gb.jit.llvm/src/jit_compile.cpp new file mode 100644 index 00000000..4b88981f --- /dev/null +++ b/gb.jit.llvm/src/jit_compile.cpp @@ -0,0 +1,139 @@ +/*************************************************************************** + + jit_compile.c + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __JIT_COMPILE_C + +#include +#include +#include "jit.h" + +static std::vector all_expressions; //Save all expressions so they can be deleted +static std::vector > ctrl_types; //second contains a class if first is T_CLASS +static std::vector > used_ctrl_types; //0 = primitive type, 1 = string, 2 = object, 3 = variant +std::vector all_statements; +std::vector classes_to_load; +int ngosubs; +uint64_t func_byref_mask; + +void register_new_expression(Expression* expr){ + all_expressions.push_back(expr); +} + +void free_all_expressions(){ + for(size_t i=0, e=all_expressions.size(); i!=e; i++){ + //printf("%d %p\n", i, all_expressions[i]); + delete all_expressions[i]; + } + all_expressions.clear(); +} + +CLASS* get_ctrl_class(int index){ + return ctrl_types[index - FP->n_local].second; +} +TYPE get_ctrl_type(int index){ + return ctrl_types[index - FP->n_local].first; +} + +int special_ctrl_type(TYPE type){ + int t = 0; + if (TYPE_is_string(type)) + t = 1; + else if (TYPE_is_object(type)) + t = 2; + else if (TYPE_is_variant(type)) + t = 3; + return t; +} + +void set_ctrl_type(TYPE type, int index, CLASS* second){ + ctrl_types[index - FP->n_local] = std::make_pair(type, second); + used_ctrl_types[index - FP->n_local][special_ctrl_type(type)] = 1; +} + +bool is_ctrl_type_used(int type, int index){ + return used_ctrl_types[index - FP->n_local][type]; +} + +void JIT_load_class(CLASS* klass){ + if (klass->ready) + return; + + JIF.F_CLASS_load_from_jit(klass); + if (!klass->is_array) + classes_to_load.push_back(klass); +} + +static void print_line() +{ + int i; + + for (i = 1; i < 10; i++) + fputs("--------", stderr); + fputc('\n', stderr); +} + +void JIT_compile_and_execute(){ + if (MAIN_debug){ + print_line(); + fprintf(stderr, "gb.jit: beginning compiling %s.", CP->name); + if (FP->debug) + fprintf(stderr, "%s:\n", FP->debug->name); + else + fprintf(stderr, "%d:\n", EXEC.index); + print_line(); + fputc('\n', stderr); + } + + ctrl_types.resize(FP->n_ctrl); + used_ctrl_types.resize(FP->n_ctrl); + ngosubs = 0; + size_t load_classes_size = classes_to_load.size(); + TRY { + JIT_read(); + } CATCH { + classes_to_load.resize(load_classes_size); + JIF.F_ERROR_propagate(); + } END_TRY + JIT_codegen(); + free_all_expressions(); + /*for(size_t i=0, e=all_statements.size(); i!=e; i++) + delete all_statements[i]; + all_statements.clear();*/ + + void (*fn)(void) = CP->jit_functions[EXEC.index]; + + std::reverse(classes_to_load.begin() + load_classes_size, classes_to_load.end()); + + while(classes_to_load.size() > load_classes_size){ + CLASS* klass = classes_to_load.back(); classes_to_load.pop_back(); + + klass->loaded = true; + klass->ready = true; + + JIF.F_CLASS_run_inits(klass); + } + + (*fn)(); +} diff --git a/gb.jit.llvm/src/jit_conv.cpp b/gb.jit.llvm/src/jit_conv.cpp new file mode 100644 index 00000000..6acecd7a --- /dev/null +++ b/gb.jit.llvm/src/jit_conv.cpp @@ -0,0 +1,280 @@ +/*************************************************************************** + + jit_conv.c + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __JIT_CONV_C + +#include "jit.h" + +static const int type_to_bits[] = {0, 1, 8, 16, 32, 64, 32, 64}; + +void JIT_conv(Expression*& value, TYPE type, Expression* other){ + static const void *jump[16][16] = + { + /* ,-------> void b c h i l g f d cs s p v func class n */ + // | + /* void */ { &&__OK, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, }, + /* b */ { &&__N, &&__OK, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__N, &&__TYPE, &&__TYPE, &&__N, &&__TYPE, &&__N, &&__N, &&__N, }, + /* c */ { &&__N, &&__c2b, &&__OK, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__N, &&__TYPE, &&__N, &&__N, &&__N, }, + /* h */ { &&__N, &&__h2b, &&__TYPE, &&__OK, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__N, &&__TYPE, &&__N, &&__N, &&__N, }, + /* i */ { &&__N, &&__i2b, &&__TYPE, &&__TYPE, &&__OK, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__N, &&__N, &&__N, }, + /* l */ { &&__N, &&__l2b, &&__TYPE, &&__TYPE, &&__TYPE, &&__OK, &&__TYPE2, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__N, &&__N, &&__N, }, + /* g */ { &&__N, &&__g2b, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__OK, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__N, &&__TYPE, &&__N, &&__N, &&__N, }, + /* f */ { &&__N, &&__f2b, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE2, &&__OK, &&__TYPE, &&__TYPE, &&__TYPE, &&__N, &&__TYPE, &&__N, &&__N, &&__N, }, + /* d */ { &&__N, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__TYPE, &&__OK, &&__TYPE, &&__TYPE, &&__N, &&__TYPE, &&__N, &&__N, &&__N, }, + /* cs */ { &&__N, &&__TYPE, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__OK, &&__OK, &&__N, &&__TYPE, &&__N, &&__N, &&__N, }, + /* s */ { &&__N, &&__TYPE, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__TYPE2, &&__OK, &&__OK, &&__N, &&__TYPE, &&__N, &&__N, &&__N, }, + /* p */ { &&__N, &&__N, &&__N, &&__N, &&__TYPE, &&__TYPE, &&__N, &&__N, &&__N, &&__N, &&__N, &&__OK, &&__TYPE, &&__N, &&__N, &&__N, }, + /* v */ { &&__N, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__OK, &&__N, &&__v2, &&__v2, }, + /* func */ { &&__N, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__F2p, &&__func, &&__OK, &&__N, &&__func, }, + /* class */ { &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__TYPE, &&__N, &&__OK, &&__N, }, + /* null */ { &&__N, &&__n2b, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__n2d, &&__n2s, &&__n2s, &&__N, &&__TYPE, &&__N, &&__N, &&__OK, }, + }; + + if (type == T_CSTRING) + type = T_STRING; + + if (value->type == (TYPE)-1) //Unknown, so always do a VALUE_convert + goto __TYPE2; + else if ((type | value->type) >> 4) + goto __OBJECT; + else + goto *jump[value->type][type]; + +__c2b: +__h2b: +__i2b: +__l2b: { + + bool on_stack_save = value->on_stack; + bool ref_stack_save = value->stack_if_ref; + Expression* arr[] = {value, new PushIntegerExpression(type_to_bits[value->type], 0)}; + value = new NotExpression(new EqExpression(arr)); + value->on_stack = on_stack_save; + value->stack_if_ref = ref_stack_save; + return; +} + +__g2b: +__f2b: { + + bool on_stack_save = value->on_stack; + bool ref_stack_save = value->stack_if_ref; + Expression* arr[] = {value, new PushFloatExpression(type_to_bits[value->type], 0.0)}; + value = new NotExpression(new EqExpression(arr)); + value->on_stack = on_stack_save; + value->stack_if_ref = ref_stack_save; + return; +} + +__n2b: { + + bool on_stack_save = value->on_stack; + bool ref_stack_save = value->stack_if_ref; + //delete value; + value = new PushIntegerExpression(1, false); + value->on_stack = on_stack_save; + value->stack_if_ref = ref_stack_save; + return; +} + +__n2d: { + + bool on_stack_save = value->on_stack; + bool ref_stack_save = value->stack_if_ref; + //delete value; + value = new PushVoidDateExpression(); + value->on_stack = on_stack_save; + value->stack_if_ref = ref_stack_save; + return; +} + +__n2s: { + + bool on_stack_save = value->on_stack; + bool ref_stack_save = value->stack_if_ref; + //delete value; + value = new PushCStringExpression(NULL, 0, 0); + value->on_stack = on_stack_save; + value->stack_if_ref = ref_stack_save; + return; +} + +__F2p: { + if (PushFunctionExpression* pfe = dynamic_cast(value)){ + bool on_stack_save = value->on_stack; + VALUE_FUNCTION v; + v.type = T_FUNCTION; + v.klass = CP; + v.object = OP; + v.kind = FUNCTION_PRIVATE; + v.index = pfe->index; + v.defined = true; + if (OP) + ((OBJECT*)OP)->ref++; + value = new PushIntegerExpression(8*sizeof(void*), (int64_t)(void*)JIF.F_EXTERN_make_callback(&v)); + JIT_conv(value, T_POINTER); + value->on_stack = on_stack_save; + return; + } + assert(false && "Not implemented yet!"); +} + +__func: + + //if (unknown_function(value)) + // goto __CONV; + //else + goto __N; + +__v2: + + ref_stack(); + value->must_on_stack(); + value = new ConvExpression(value, type); + value->on_stack = true; + return; + +__OBJECT: + + if (TYPE_is_pure_object(type)) + JIT_load_class((CLASS*)(void*)type); + + if (TYPE_is_pure_object(value->type)) + JIT_load_class((CLASS*)(void*)value->type); + + if (!TYPE_is_object(type)) + { + if (type == T_BOOLEAN) + { + goto __TYPE; + } + + if (type == T_VARIANT) + goto __TYPE; + + goto __N; + } + + if (!TYPE_is_object(value->type)) + { + if (value->type == T_NULL) + { + goto __TYPE; + } + + if (value->type == T_VARIANT) + goto __TYPE2; + + if (value->type == T_FUNCTION) + goto __func; + + if (value->type == T_CLASS) + { + //Virtual, Auto create or 'Class' object + PushClassExpression* pce = dyn_cast(value); + assert(pce); + + CLASS* klass = pce->klass; + + if (CLASS_is_virtual(klass)) + THROW(E_VIRTUAL); + + if (klass->auto_create){ + Expression* tmp = new PushAutoCreateExpression(klass); + tmp->on_stack = value->on_stack; + tmp->stack_if_ref = value->stack_if_ref; + //delete value; + value = tmp; + } else { + //Get the 'Class' object from the object + if (type != T_OBJECT && type != GB.FindClass("Class")) + goto __TYPE2; //This only works if there is a SPEC_CONVERT + goto __TYPE; + } + } + else + goto __N; + } + + if (type == value->type) + return; + + if (value->type != T_OBJECT) + if (((CLASS*)(void*)value->type)->is_virtual) + THROW(E_VIRTUAL); + + /*if (value->type != T_OBJECT && type != T_OBJECT){ + if (!CLASS_inherits((CLASS*)(void*)value->type, (CLASS*)(void*)type) + && !CLASS_inherits((CLASS*)(void*)type, (CLASS*)(void*)value->type)){ + goto __N; + } + } Does not work for e.g. Integer[] -> Float[] ...*/ + + if (type == T_OBJECT) + goto __TYPE; + else + goto __TYPE2; + + + +__TYPE: { + + bool on_stack = value->on_stack; + bool stack_if_ref = value->stack_if_ref; + //value->on_stack = false; + //value->stack_if_ref = false; + value = new ConvExpression(value, type); + value->on_stack = on_stack; + value->stack_if_ref = stack_if_ref; + return; +} + +__TYPE2: { + + if (other) + other->must_on_stack(); + ref_stack(); + bool on_stack = value->on_stack; + bool stack_if_ref = value->stack_if_ref; + value->on_stack = true; + value = new ConvExpression(value, type); + value->on_stack = on_stack; + value->stack_if_ref = stack_if_ref; + + +} + +__OK: + + return; + +__N: + + THROW(E_TYPE, JIF.F_TYPE_get_name(type), JIF.F_TYPE_get_name(value->type)); + +__NR: + + THROW(E_NRETURN); +} diff --git a/gb.jit.llvm/src/jit_expressions.cpp b/gb.jit.llvm/src/jit_expressions.cpp new file mode 100644 index 00000000..2a97bbbf --- /dev/null +++ b/gb.jit.llvm/src/jit_expressions.cpp @@ -0,0 +1,1872 @@ +/*************************************************************************** + + jit_expressions.c + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __JIT_EXPRESSIONS_C + +#include "jit.h" +#include "jit_runtime.h" + +static void ref_variant(Expression* expr){ + if (expr->no_ref_variant) return; + ref_stack(); +} + +static void check_string(Expression*& expr){ + TYPE t = expr->type; + if (!TYPE_is_string(t) && t != T_NULL && t != T_VARIANT) + THROW(E_TYPE, JIF.F_TYPE_get_name(T_STRING), JIF.F_TYPE_get_name(t)); + if (TYPE_is_string(t)) + return; + if (t == T_NULL){ + //abort(); + //FIXME is this good? + assert(isa(expr)); + //delete expr; + expr = new PushCStringExpression(NULL, 0, 0); + return; + } + if (t == T_VARIANT){ + ref_variant(expr); + expr->must_on_stack(); + } + expr = new CheckStringExpression(expr); +} + +static void check_integer(Expression*& expr){ + TYPE t = expr->type; + if (!TYPE_is_integer(t) && t != T_VARIANT) + THROW(E_TYPE, JIF.F_TYPE_get_name(T_INTEGER), JIF.F_TYPE_get_name(t)); + if (t == T_VARIANT){ + ref_variant(expr); + expr->must_on_stack(); + expr = new CheckIntegerExpression(expr); + } +} + +static void check_float(Expression*& expr){ + TYPE t = expr->type; + if (!TYPE_is_number(t) && t != T_VARIANT) + THROW(E_TYPE, JIF.F_TYPE_get_name(T_FLOAT), JIF.F_TYPE_get_name(t)); + if (t == T_VARIANT){ + ref_variant(expr); + expr->must_on_stack(); + expr = new CheckFloatExpression(expr); + } else + JIT_conv(expr, T_FLOAT); +} + +static void check_pointer(Expression*& expr){ + TYPE t = expr->type; + if (t != T_POINTER && t != T_VARIANT) + THROW(E_TYPE, "Pointer", JIF.F_TYPE_get_name(t)); + if (t == T_VARIANT){ + ref_variant(expr); + expr->must_on_stack(); + expr = new CheckPointerExpression(expr); + } +} + +static TYPE check_good_type(TYPE t1, TYPE t2){ + if (t1 == T_CSTRING) t1 = T_STRING; + if (t2 == T_CSTRING) t2 = T_STRING; + + if (t1 == t2){ + + } else if (t1 == T_NULL){ + if (t2 <= T_FLOAT) + return T_VARIANT; + t1 = t2; + } else if (t1 <= T_FLOAT && t2 <= T_FLOAT){ + if (t2 > t1) + t1 = t2; + } else if (t2 == T_NULL){ + if (t1 <= T_FLOAT) + return T_VARIANT; + } else if (TYPE_is_object(t1) && TYPE_is_object(t2)){ + return T_OBJECT; + } else + return T_VARIANT; + + if (t1 == T_VOID) + THROW(E_NRETURN); + else if (t1 > T_VARIANT && t1 < T_OBJECT) + THROW(E_TYPE, "Standard type", JIF.F_TYPE_get_name(t1)); + + return t1; +} + +AddQuickExpression::AddQuickExpression(Expression* val, int add) : expr(val), add(add) { + no_ref_variant = true; + + if (TYPE_is_string(expr->type) || expr->type == T_DATE) + JIT_conv(expr, T_FLOAT); + + if (expr->type <= T_BOOLEAN || expr->type > T_VARIANT) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(expr->type)); + + type = expr->type; + + if (type == T_VARIANT) + expr->must_on_stack(); +} +PushStaticExpression::PushStaticExpression(int index) { + CLASS_VAR* var = &CP->load->stat[index]; + ctype = &var->type; + klass = CP; + type = ctype_to_type(ctype); + addr = (char*)CP->stat + var->pos; +} +PopStaticExpression::PopStaticExpression(Expression* val, int index) : val(val) { + CLASS_VAR* var = &CP->load->stat[index]; + CTYPE* ctype = &var->type; + if (ctype->id == TC_ARRAY || ctype->id == TC_STRUCT) + THROW_ILLEGAL(); + type = ctype_to_type(ctype); + addr = (char*)CP->stat + var->pos; + JIT_conv(this->val, type); +} +PushDynamicExpression::PushDynamicExpression(int index) : index(index) { + CLASS_VAR* var = &CP->load->dyn[index]; + ctype = &var->type; + type = ctype_to_type(ctype); + offset = var->pos; +} +PopDynamicExpression::PopDynamicExpression(Expression* val, int index) : val(val), index(index) { + CLASS_VAR* var = &CP->load->dyn[index]; + CTYPE* ctype = &var->type; + if (ctype->id == TC_ARRAY || ctype->id == TC_STRUCT) + THROW_ILLEGAL(); + type = ctype_to_type(ctype); + offset = var->pos; + JIT_conv(this->val, type); +} +PushFunctionExpression::PushFunctionExpression(int index) : index(index) { + type = T_FUNCTION; + + function_class = CP; + function_object = NULL; //Will be set later + function_kind = FUNCTION_PRIVATE; + function_defined = true; + function_unknown = NULL; + function_index = index; + function_expr_type = PrivateFn; +} +PushLocalExpression::PushLocalExpression(int index) : index(index) { + if (index >= FP->n_local){ + type = get_ctrl_type(index); + } else { + CLASS_LOCAL* var = &FP->local[index]; + CTYPE* ctype = &var->type; + type = ctype_to_type(ctype); + } +} +PopLocalExpression::PopLocalExpression(Expression* val, int index) : val(val), index(index) { + CLASS_LOCAL* var = &FP->local[index]; + CTYPE* ctype = &var->type; + type = ctype_to_type(ctype); + JIT_conv(this->val, type); +} +PopParamExpression::PopParamExpression(Expression* val, int index) : val(val), index(index) { + type = FP->param[index + FP->n_param].type; + JIT_conv(this->val, type); +} +PopCtrlExpression::PopCtrlExpression(Expression* val, int index) : val(val), index(index) { + CLASS* klass = NULL; + if (auto pce = dyn_cast(val)){ + klass = pce->klass; + } + set_ctrl_type(val->type, index, klass); + type = val->type; +} +PopOptionalExpression::PopOptionalExpression(Expression* val, int index) : val(val), index(index) { + type = FP->param[FP->n_param + index].type; + if (val->type == T_VOID){ + is_default = true; + } else { + is_default = false; + JIT_conv(this->val, type); + } +} +PushEventExpression::PushEventExpression(int ind, const char* unknown_name) : index(ind) { + //type = CP->load->event[index].type; + type = T_FUNCTION; + if (unknown_name){ + index = CLASS_find_symbol(CP, unknown_name); + if (index == NO_SYMBOL) + THROW(E_DYNAMIC, CP->name, unknown_name); + + CLASS_DESC* desc = CP->table[index].desc; + if (CLASS_DESC_get_type(desc) != CD_EVENT) + THROW(E_DYNAMIC, CP->name, unknown_name); + + index = desc->event.index; + } else if (CP->parent){ + index += CP->parent->n_event; + } + + function_kind = FUNCTION_EVENT; + function_expr_type = EventFn; +} +PushArrayExpression::PushArrayExpression(Expression** it, int nargs){ + args.resize(nargs); + for(int i=0; i(args[0])) && ((PushDynamicExpression*)args[0])->ctype->id == TC_ARRAY) + || + (dynamic_cast(args[0]) && ((ReadVariableExpression*)args[0])->ctype->id == TC_ARRAY) ){ + CTYPE* ctype; + CLASS* klass; + if (is_push_dynamic){ + ctype = &CP->load->dyn[((PushDynamicExpression*)args[0])->index].type; + klass = CP; + } else { + ctype = ((ReadVariableExpression*)args[0])->ctype; + klass = ((ReadVariableExpression*)args[0])->klass; + } + + CLASS_ARRAY* ca = klass->load->array[ctype->value]; + int* dim = ca->dim; + + int ndim = 1; + while(*dim++ > 0) + ndim++; + + if (nargs-1 != ndim) + THROW(E_NDIM); + + for(int i=0; ictype, klass); + } else if (TYPE_is_pure_object(args[0]->type)){ + klass = (CLASS*)(void*)args[0]->type; + + if (klass->quick_array == CQA_ARRAY){ + ref_stack(); + type = klass->array_type; + for(int i=1; i 2) + for(int i=1; imust_on_stack(); + } else if (klass->quick_array == CQA_COLLECTION){ + if (nargs > 2) + THROW(E_TMPARAM); + JIT_conv(args[1], T_STRING); + on_stack = true; + type = T_VARIANT; + } else { + klass = (CLASS*)(void*)args[0]->type; + is_push_class = false; + goto _SPEC_GET_METHOD; + } + } else if (PushClassExpression* pce = dyn_cast(args[0])){ + klass = pce->klass; + is_push_class = true; + goto _SPEC_GET_METHOD; + } else if (args[0]->type == T_OBJECT || args[0]->type == T_VARIANT){ + pc = get_current_read_pos(); + *pc |= 3 << 6; + type = T_VARIANT; + on_stack = true; + for(int i=0; imust_on_stack(); + } + } else { + THROW(E_NOBJECT); + } + + return; + + _SPEC_GET_METHOD: { + int index = klass->special[SPEC_GET]; + + if (index == NO_SYMBOL) + THROW(E_NARRAY, klass->name); + + ref_stack(); + + CLASS_DESC* desc = CLASS_get_desc(klass, index); + + type = desc->method.type; + + if (is_push_class){ + if (CLASS_DESC_get_type(desc) != CD_STATIC_METHOD){ + if (!klass->auto_create) + THROW(E_NOBJECT); + //delete args[0]; + args[0] = new PushAutoCreateExpression(klass); + } + } else { + if (CLASS_DESC_get_type(desc) == CD_STATIC_METHOD && !klass->is_virtual) + THROW(E_NARRAY); + } + + nargs--; + + if (nargs < desc->method.npmin) + THROW(E_NEPARAM); + if (nargs > desc->method.npmax && !desc->method.npvar) + THROW(E_TMPARAM); + + can_quick = !(desc->method.npmin < desc->method.npmax || nargs != desc->method.npmin || desc->method.npvar); + + if (can_quick){ + for(int i=0; imethod.signature[i]); + } + } + on_stack = true; + for(uint i=0; imust_on_stack(); + } +} +PopArrayExpression::PopArrayExpression(Expression** it, int nargs, Expression* val) : val(val) { + args.resize(nargs); + for(int i=0; i(args[0])) && ((PushDynamicExpression*)args[0])->ctype->id == TC_ARRAY) + || + (dynamic_cast(args[0]) && ((ReadVariableExpression*)args[0])->ctype->id == TC_ARRAY) ){ + CTYPE* ctype; + CLASS* klass; + if (is_push_dynamic){ + ctype = &CP->load->dyn[((PushDynamicExpression*)args[0])->index].type; + klass = CP; + } else { + ctype = ((ReadVariableExpression*)args[0])->ctype; + klass = ((ReadVariableExpression*)args[0])->klass; + } + + CLASS_ARRAY* ca = klass->load->array[ctype->value]; + int* dim = ca->dim; + + int ndim = 1; + while(*dim++ > 0) + ndim++; + + if (nargs-1 != ndim) + THROW(E_NDIM); + + for(int i=0; ictype.id == TC_STRUCT) + THROW_ILLEGAL(); + + type = ctype_to_type(&ca->ctype, klass); + + JIT_conv(this->val, type); + } else if (TYPE_is_pure_object(args[0]->type)){ + klass = (CLASS*)(void*)args[0]->type; + + if (klass->quick_array == CQA_ARRAY){ + type = klass->array_type; + JIT_conv(this->val, type); + ref_stack(); + for(int i=1; i 2) + for(int i=1; imust_on_stack(); + } else if (klass->quick_array == CQA_COLLECTION){ + if (nargs > 2) + THROW(E_TMPARAM); + JIT_conv(this->val, T_VARIANT); + JIT_conv(args[1], T_STRING); + this->val->must_on_stack(); + } else { + klass = (CLASS*)(void*)args[0]->type; + is_push_class = false; + goto _SPEC_PUT_METHOD; + } + } else if (PushClassExpression* pce = dyn_cast(args[0])){ + klass = pce->klass; + is_push_class = true; + goto _SPEC_PUT_METHOD; + } else if (args[0]->type == T_OBJECT || args[0]->type == T_VARIANT){ + pc = get_current_read_pos(); + *pc |= 3 << 6; + type = T_VARIANT; + on_stack = true; + this->val->must_on_stack(); + for(int i=0; imust_on_stack(); + } + } else { + THROW(E_NOBJECT); + } + + return; + + _SPEC_PUT_METHOD: { + int index = klass->special[SPEC_PUT]; + + if (index == NO_SYMBOL) + THROW(E_NARRAY, klass->name); + + ref_stack(); + + CLASS_DESC* desc = CLASS_get_desc(klass, index); + + type = desc->method.type; + + if (is_push_class){ + if (CLASS_DESC_get_type(desc) != CD_STATIC_METHOD){ + if (!klass->auto_create) + THROW(E_NOBJECT); + //delete args[0]; + args[0] = new PushAutoCreateExpression(klass); + } + } else { + if (CLASS_DESC_get_type(desc) == CD_STATIC_METHOD && !klass->is_virtual) + THROW(E_NARRAY); + } + + //nargs contains the number of parameters, since object and value are swapped + + if (nargs < desc->method.npmin) + THROW(E_NEPARAM); + if (nargs > desc->method.npmax && !desc->method.npvar) + THROW(E_TMPARAM); + + can_quick = !(desc->method.npmin < desc->method.npmax || nargs != desc->method.npmin || desc->method.npvar); + + if (can_quick){ + JIT_conv(this->val, desc->method.signature[0]); + for(int i=1; imethod.signature[i]); + } + } + on_stack = true; + for(int i=0; imust_on_stack(); + this->val->must_on_stack(); + } +} +/*PushPureObjectUnknownFunctionExpression::PushPureObjectUnknownFunctionExpression(Expression* obj) : obj(obj){ + CLASS* klass = (CLASS*)(void*)obj->type; + + type = T_FUNCTION; + if (klass->must_check){ + ref_stack(); + obj->must_on_stack(); + } + + function_class = klass; + function_object = obj; + function_defined = true; + function_expr_type = UnknownFn; +}*/ +PushPureObjectFunctionExpression::PushPureObjectFunctionExpression(Expression* obj, int index, const char* unknown_name) +: PushPureObjectExpression(obj, index){ + type = T_FUNCTION; + if (klass()->must_check){ + ref_stack(); + obj->must_on_stack(); + } + + function_class = klass(); + function_object = obj; + function_desc = desc(); + function_kind = desc()->method.native ? -1 : FUNCTION_PUBLIC; + function_defined = !unknown_name; + function_unknown = unknown_name; + function_index = index; + function_expr_type = PureObjectFn; +} +PushPureObjectStaticFunctionExpression::PushPureObjectStaticFunctionExpression(Expression* obj, int index, const char* unknown_name) +: PushPureObjectExpression(obj, index){ + type = T_FUNCTION; + + function_class = klass(); + function_object = obj; + function_desc = desc(); + function_kind = desc()->method.native ? -1 : FUNCTION_PUBLIC; + function_defined = !unknown_name; + function_unknown = unknown_name; + function_index = index; + function_expr_type = PureObjectStaticFn; +} +PushVirtualFunctionExpression::PushVirtualFunctionExpression(Expression* obj, int index, const char* unknown_name) +: PushPureObjectExpression(obj, index) { + type = T_FUNCTION; + + function_class = klass(); + function_object = obj; + function_desc = desc(); + function_kind = FUNCTION_NATIVE; + function_defined = !unknown_name; + function_unknown = unknown_name; + function_index = index; + function_expr_type = VirtualObjectFn; +} +PushVirtualStaticFunctionExpression::PushVirtualStaticFunctionExpression(Expression* obj, int index, const char* unknown_name) +: PushPureObjectExpression(obj, index) { + //type = desc()->method.type; + type = T_FUNCTION; + + function_class = klass(); + function_object = obj; + function_desc = desc(); + function_kind = FUNCTION_NATIVE; + function_defined = !unknown_name; + function_unknown = unknown_name; + function_index = index; + function_expr_type = VirtualObjectStaticFn; +} +PushStaticPropertyExpression::PushStaticPropertyExpression(Expression* obj, int index) +: obj(obj), index(index) { + CLASS* c = ((PushClassExpression*)obj)->klass; + CLASS_DESC* desc = c->table[index].desc; + type = desc->property.type; + ref_stack(); + obj->must_on_stack(); +} +PushStaticFunctionExpression::PushStaticFunctionExpression(Expression* obj, int index, const char* unknown_name) +: obj(obj), index(index) { + //type = desc()->method.type; + type = T_FUNCTION; + ref_stack(); + obj->must_on_stack(); + + function_class = klass(); + function_object = obj; + function_desc = desc(); + function_kind = function_desc->method.native ? (function_desc->method.subr ? FUNCTION_SUBR : FUNCTION_NATIVE) : FUNCTION_PUBLIC; + function_defined = true; + function_unknown = unknown_name; + function_index = index; + function_expr_type = StaticFn; +} +PopVirtualPropertyExpression::PopVirtualPropertyExpression(Expression* obj, Expression* val, int index, const char* name, bool is_static) +: PopPureObjectExpression(obj, val, index), name(name), is_static(is_static) { + type = desc()->property.type; + ref_stack(); + obj->must_on_stack(); + this->val->must_on_stack(); + JIT_conv(this->val, type); +} +PopPureObjectPropertyExpression::PopPureObjectPropertyExpression(Expression* obj, Expression* val, int index) +: PopPureObjectExpression(obj, val, index) { + type = desc()->property.type; + ref_stack(); + val->must_on_stack(); + obj->must_on_stack(); + JIT_conv(this->val, type); +} +PopStaticPropertyExpression::PopStaticPropertyExpression(CLASS* klass, Expression* val, int index) +: klass(klass), val(val), index(index) { + CLASS_DESC* desc = klass->table[index].desc; + type = desc->property.type; + ref_stack(); + JIT_conv(this->val, type); + this->val->must_on_stack(); +} +CallExpression::CallExpression(Expression* function, int nargs, Expression** it, unsigned short* pc) : func(function), pc(pc) { + on_stack = true; //Gets replaced by false if EventFn or Extern + desc = NULL; + variant_call = false; + ref_stack(); + args.resize(nargs); + for(int i=0; i(func)){ + CLASS_EXTERN* ext; + if (ee->object_to_release) + ext = &ee->klass->load->ext[ee->klass->table[ee->index].desc->ext.exec]; //An extern method with the correct signature + else + ext = &ee->klass->load->ext[ee->index]; + + if (nargs < ext->n_param) + THROW(E_NEPARAM); + if (!ext->vararg && nargs > ext->n_param) + THROW(E_TMPARAM); + + for(int i=0; in_param; i++){ + TYPE t = ext->param[i].type; + + if (t == T_VOID || t == T_DATE || t == T_VARIANT || t == T_CLASS || t == T_FUNCTION) + THROW(E_UTYPE); + + JIT_conv(args[i], t); + + if (args[i]->type == T_STRING || TYPE_is_object(t)) + args[i]->must_on_stack(); + } + + for(int i=ext->n_param; itype; + + if (t == T_VOID || t == T_DATE || t == T_CLASS || t == T_FUNCTION) + THROW(E_UTYPE); + + if (t == T_VARIANT) + ee->must_variant_dispatch = true; + + if (args[i]->type == T_STRING || TYPE_is_object(t)) + args[i]->must_on_stack(); + } + + if (ee->must_variant_dispatch) + for(int i=0; imust_on_stack(); + + type = ext->type; + on_stack = false; + return; + } + + if (TYPE_is_pure_object(func->type)){ + JIT_load_class((CLASS*)(void*)func->type); + CLASS_DESC_METHOD* method_desc = JR_CLASS_get_special_desc((CLASS*)(void*)func->type, SPEC_CALL); + if (method_desc == NULL) + THROW(E_NFUNC); + //Can be virtual static... See Draw.Clip._call + //Atm no virtual non-static methods are written + + //Note: func can be Super + + + desc = (CLASS_DESC*)(void*)method_desc; //Works, since CLASS_DESC is a union containing CLASS_DESC_METHOD + klass = method_desc->klass; + index = klass->special[SPEC_CALL]; + + switch(CLASS_DESC_get_type(desc)){ + case CD_METHOD: + if (klass->is_virtual) + func = new PushVirtualFunctionExpression(func, index, NULL); + else + func = new PushPureObjectFunctionExpression(func, index, NULL); + break; + case CD_STATIC_METHOD: + if (klass->is_virtual) + func = new PushVirtualStaticFunctionExpression(func, index, NULL); + else + func = new PushPureObjectStaticFunctionExpression(func, index, NULL); + break; + default: + THROW(E_NFUNC); + } + } + func->must_on_stack(); + + if (FunctionExpression* fe = dynamic_cast(func)){ + switch(fe->function_expr_type){ + case PrivateFn: { + index = fe->function_index; + kind = FUNCTION_PRIVATE; + klass = CP; + + FUNCTION* fp = &CP->load->func[index]; + if (nargs < fp->npmin) + THROW(E_NEPARAM); + if (nargs > fp->n_param && !fp->vararg) + THROW(E_TMPARAM); + + can_quick = !(fp->npmin < fp->n_param || nargs != fp->npmin || fp->vararg); + if (can_quick){ + for(int i=0; iparam[i].type); + } + type = fp->type; + goto _SET_ON_STACK; + return; + } + case EventFn: { + on_stack = false; + type = T_BOOLEAN; + goto _SET_ON_STACK; + return; + } + /*case UnknownFn: { + type = T_VARIANT; + goto _SET_ON_STACK; + }*/ + } + desc = fe->function_desc; + index = fe->function_index; + klass = fe->function_class; + type = fe->function_unknown ? T_VARIANT : desc->method.type; + + goto _NORMAL_METHOD; + } + + if (PushClassExpression* ce = dyn_cast(func)){ + int special_call_function_index = ce->klass->special[SPEC_CALL]; + if (special_call_function_index == NO_SYMBOL){ + //Then it is a cast: Dim obj2 As SomeClass = SomeClass(obj1); + if (nargs != 1) + THROW(E_NFUNC); + + desc = NULL; + type = (TYPE)(void*)ce->klass; + JIT_conv(args[0], (TYPE)(void*)ce->klass); + return; + } + CLASS_DESC_METHOD* method_desc = &CLASS_get_desc(ce->klass, special_call_function_index)->method; + desc = (CLASS_DESC*)(void*)method_desc; //Works, since CLASS_DESC is a union containing CLASS_DESC_METHOD + index = special_call_function_index; + klass = method_desc->klass; + type = method_desc->type; + + goto _NORMAL_METHOD; + } + + if (func->type != T_OBJECT && func->type != T_VARIANT) + THROW(E_NFUNC); + else { + type = T_VARIANT; + can_quick = false; + variant_call = true; + goto _SET_ON_STACK; + } + + return; //Line unreachable + + _NORMAL_METHOD: + if (nargs < desc->method.npmin) + THROW(E_NEPARAM); + if (nargs > desc->method.npmax && !desc->method.npvar) + THROW(E_TMPARAM); + + can_quick = !(desc->method.npmin < desc->method.npmax || nargs != desc->method.npmin || desc->method.npvar); + + if (can_quick){ + for(int i=0; imethod.signature[i]); + } + } + + _SET_ON_STACK: + for(int i=0; imust_on_stack(); + } +} +JumpFirstExpression::JumpFirstExpression(int ctrl_to, Expression* to_expr, Expression* step_expr, int local_var, int body_addr, int done_addr) +: to(to_expr), step(step_expr), ctrl_to(ctrl_to), local_var(local_var), body_addr(body_addr), done_addr(done_addr) { + TYPE type = FP->local[local_var].type.id; + + if (type < T_BYTE || type > T_FLOAT) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); + + if (TYPE_is_integer(type)) + JIT_conv(step, T_INTEGER); + else + JIT_conv(step, type); + + JIT_conv(to, type); + + set_ctrl_type(type, ctrl_to); + set_ctrl_type(step->type, ctrl_to+1); + + mark_address_taken(body_addr); + mark_address_taken(done_addr); +} +JumpEnumNextExpression::JumpEnumNextExpression(JumpEnumFirstExpression* jfirst, int cont_addr, int addr, unsigned short* pc, bool drop, OnStackExpression* retval) +: jfirst(jfirst), retval(retval), cont_addr(cont_addr), addr(addr), pc(pc), drop(drop) { + mark_address_taken(cont_addr); + mark_address_taken(addr); + TYPE obj_type = get_ctrl_type(jfirst->ctrl); + + CLASS* klass; + + if (obj_type == T_VARIANT || obj_type == T_OBJECT){ + defined = false; + type = T_VARIANT; + } else if (obj_type == T_CLASS) { + defined = true; + PushClassExpression* pce = dyn_cast(jfirst->obj); + assert(pce); + klass = pce->klass; + + /*if (klass->auto_create && !klass->enum_static){ + delete pce; + jfirst->obj = PushAutoCreate(klass); + obj_type = (CLASS*)(void*)klass; + }*/ + } else if (TYPE_is_pure_object(obj_type)){ + defined = true; + klass = (CLASS*)(void*)obj_type; + } else { + THROW(E_NOBJECT); + } + + if (defined){ + short index = klass->special[SPEC_NEXT]; + if (index == NO_SYMBOL) + THROW(E_ENUM); + + CLASS_DESC* desc = CLASS_get_desc(klass, index); + type = desc->method.type; + } + + if (!drop) + retval->type = type; + + set_ctrl_type(T_OBJECT, jfirst->ctrl+1); +} +NewExpression::NewExpression(Expression** it, int nargs, bool event) : event(event) { + pc = get_current_read_pos(); + must_on_stack(); + args.resize(nargs); + for(int i=0; imust_on_stack(); + } + + if (PushClassExpression* pce = dyn_cast(args[0])){ + /*if (pce->klass->override) + pce->klass = pce->klass->override;*/ + type = (TYPE)(void*)pce->klass; + } else { + type = T_OBJECT; + } +} +ReturnExpression::ReturnExpression(Expression* expr, int kind) : retval(expr), kind(kind) { + pc = get_current_read_pos(); + type = FP->type; + if (retval){ + /*if (TYPE_is_pure_object(type) && ((CLASS*)type)->override) + type = (TYPE)(((CLASS*)type)->override);*/ + JIT_conv(retval, type); + } +} +QuitExpression::QuitExpression(Expression* quitval) : quitval(quitval) { + if (quitval) + JIT_conv(this->quitval, T_BYTE); +} +SubrExpression::SubrExpression(int digit, Expression** it, int nargs, int extra) : digit(digit), extra(extra) { + args.resize(nargs); + for(int i=0; imust_on_stack(); + } + + switch(digit){ + case 0x41: //Mid + if (nargs == 3) + JIT_conv(args[2], T_INTEGER); + //Fallthrough + ref_stack(); + case 0x40: //Left + case 0x42: //Right + if (nargs != 1) + JIT_conv(args[1], T_INTEGER); + /*type = args[0]->type; //Want T_STRING or T_CSTRING + if (type == T_VARIANT || type = T_NULL) + type = T_STRING; + if (!TYPE_is_string(type)) + THROW(E_TYPE, JIF.F_TYPE_get_name(T_STRING), JIF.F_TYPE_get_name(type)); + if (type == T_VARIANT) + ref_stack();*/ + check_string(args[0]); + type = args[0]->type == T_CSTRING ? T_CSTRING : T_STRING; + break; + + case 0x43: //Len + check_string(args[0]); + type = T_INTEGER; + break; + + case 0x45: //String + check_string(args[1]); + //Fallthrough + case 0x44: //Space + check_integer(args[0]); + ref_stack(); + type = T_STRING; + break; + + case 0x4A: //Asc + check_string(args[0]); + if (nargs == 2) + check_integer(args[1]); + type = T_INTEGER; + break; + + case 0x46: //Trim + check_string(args[0]); + type = args[0]->type; + break; + + case 0x48: //LCase + extra = 1; + case 0x47: //UCase + check_string(args[0]); + type = T_STRING; + break; + + case 0x49: //Chr + ref_stack(); + JIT_conv(args[0], T_INTEGER); + type = T_CSTRING; + break; + + case 0x4B: //Instr + case 0x4C: //RInStr + type = T_INTEGER; + check_string(args[0]); + check_string(args[1]); + if (nargs >= 3) + check_integer(args[2]); + if (nargs == 4) + check_integer(args[3]); + break; + + case 0x4D: //Subst + type = T_STRING; + check_string(args[0]); + for(int i=1; i= 2){ + check_string(args[1]); + if (nargs >= 3){ + check_string(args[2]); + if (nargs >= 4){ + JIT_conv(args[3], T_BOOLEAN); + if (nargs == 5) + JIT_conv(args[4], T_BOOLEAN); + } + } + } + break; + + case 0x50: //Scan + type = (TYPE)(void*)GB.FindClass("String[]"); + check_string(args[0]); + check_string(args[1]); + break; + + case 0x51: //Comp + type = T_INTEGER; + JIT_conv(args[0], T_STRING); + JIT_conv(args[1], T_STRING); + if (nargs == 2) + check_integer(args[2]); + break; + + case 0x52: //Conv + ref_stack(); + type = T_STRING; + check_string(args[0]); + check_string(args[1]); + check_string(args[2]); + break; + + case 0x53: //DConv + ref_stack(); + type = T_STRING; + check_string(args[0]); + break; + + case 0x54: //Abs + case 0x55: //Int + case 0x56: //Fix + pc = get_current_read_pos(); + type = args[0]->type; + if (!TYPE_is_number(type) && type != T_VARIANT) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); + if (type == T_VARIANT){ + args[0]->must_on_stack(); + no_ref_variant = true; + on_stack = true; + } + break; + + case 0x57: //Sgn + type = T_INTEGER; + if (!TYPE_is_number(args[0]->type) && args[0]->type != T_VARIANT) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(args[0]->type)); + if (args[0]->type == T_VARIANT){ + args[0]->must_on_stack(); + no_ref_variant = true; + on_stack = true; + } + break; + + case 0x58: //Math + ref_stack(); + type = T_FLOAT; + JIT_conv(args[0], T_FLOAT); + break; + + case 0x59: //Pi + type = T_FLOAT; + if (nargs == 1) + JIT_conv(args[0], T_FLOAT); + break; + + case 0x5A: //Round + type = T_FLOAT; + JIT_conv(args[0], T_FLOAT); + if (nargs == 2){ + check_integer(args[1]); + JIT_conv(args[1], T_INTEGER); + } + break; + + case 0x5B: //Randomize + type = T_VOID; + if (nargs != 0){ + check_integer(args[0]); + JIT_conv(args[0], T_INTEGER); + } + break; + + case 0x5C: //Rnd + type = T_FLOAT; + if (nargs >= 1) + JIT_conv(args[0], T_FLOAT); + if (nargs == 2) + JIT_conv(args[1], T_FLOAT); + break; + + case 0x5D: //Min + case 0x5E: { //Max + TYPE t1 = args[0]->type, t2 = args[1]->type; + + if (!TYPE_is_number_date(t1) && t1 != T_VARIANT) + THROW(E_TYPE, "Number or date", JIF.F_TYPE_get_name(t1)); + if (!TYPE_is_number_date(t2) && t2 != T_VARIANT) + THROW(E_TYPE, "Number or date", JIF.F_TYPE_get_name(t2)); + + type = Max(t1, t2); + if (type != T_VARIANT){ + if (type == T_SINGLE) + type = T_FLOAT; + if (t1 != type) JIT_conv(args[0], type); + else if (t2 != type) JIT_conv(args[1], type); + } else { + /*if (t1 == T_VARIANT && !args[0]->no_ref_variant) + ref_variant(args[0]); + if (t2 == T_VARIANT && !args[1]->no_ref_variant) + ref_variant(args[1]);*/ + ref_stack(); + if (t1 == T_VARIANT) + args[0]->must_on_stack(); + if (t2 == T_VARIANT) + args[1]->must_on_stack(); + no_ref_variant = true; + } + break; + } + + case 0x5F: //IIf + if (args[0]->type == T_VOID || args[1]->type == T_VOID || args[2]->type == T_VOID) + THROW(E_NRETURN); + type = check_good_type(args[1]->type, args[2]->type); + JIT_conv(args[0], T_BOOLEAN); + if (type == T_VARIANT && (args[1]->type != T_VARIANT || args[2]->type != T_VARIANT)){ + /*ref_stack(); + args[1]->ref_on_stack(); + on_stack = true;*/ + } else { + JIT_conv(args[1], type); + JIT_conv(args[2], type); + } + break; + + case 0x60: //Choose + JIT_conv(args[0], T_INTEGER); + type = args[1]->type; + extra = 1; //All same type + for(int i=1; itype == T_VOID) + THROW(E_NRETURN); + if (args[i]->type != type && !(TYPE_is_string(type) && TYPE_is_string(args[i]->type))) + extra = 0; //Different types + } + if (extra == 0 || nargs > 3) + type = T_VARIANT; + break; + + case 0x61: { //Array + for(int i=0; itype == T_VOID) + THROW(E_NRETURN); + type2 = args[0]->type; + if (type2 > T_VARIANT && type2 < T_OBJECT) + THROW(E_TYPE, "Standard type", JIF.F_TYPE_get_name(type2)); + for(int i=1; itype); + } + if (type2 == T_CSTRING) + type2 = T_STRING; + for(int i=0; itype; + if (type != T_VARIANT && (type <= T_BOOLEAN || type > T_LONG)) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); + if (type == T_VARIANT && (extra & 0x1F) != 3) + no_ref_variant = true; + ref_stack(); + JIT_conv(args[1], T_INTEGER); + + if ((extra & 0x1F) == 3) //BTst + type = T_BOOLEAN; + break; + + case 0x65: //IsBoolean ... + type = T_BOOLEAN; + if (extra != T_NULL) + check_string(args[0]); + break; + + case 0x66: //TypeOf + type = T_INTEGER; + if (extra){ + check_integer(args[0]); + JIT_conv(args[0], T_INTEGER); + } + break; + + /*case 0x67: //CBool ... + JIT_conv(args[0], extra); + type = extra; + break; + */ //Done in jit_read.cpp + + case 0x68: //Bin + case 0x69: //Hex + type = T_STRING; + JIT_conv(args[0], T_LONG); + if (nargs == 2){ + ref_stack(); + JIT_conv(args[1], T_INTEGER); + } + break; + + case 0x6A: //Val + check_string(args[0]); + type = T_VARIANT; + no_ref_variant = true; + break; + + case 0x6B: //Str + type = T_STRING; + if (args[0]->type == T_VOID) + THROW(E_NRETURN); + break; + + case 0x6C: //Format + type = T_STRING; + ref_stack(); + break; + + case 0x6D: //Timer + type = T_FLOAT; + break; + + case 0x6E: //Now + type = T_DATE; + break; + + case 0x6F: //Year + JIT_conv(args[0], T_DATE); + type = T_INTEGER; + break; + + case 0x70: //Week + if (nargs >= 1){ + JIT_conv(args[0], T_DATE); + if (nargs >= 2){ + check_integer(args[1]); + if (nargs == 3) + JIT_conv(args[2], T_BOOLEAN); + } + } + type = T_INTEGER; + break; + + case 0x71: //Date + case 0x72: //Time + if (nargs == 1) + JIT_conv(args[0], T_DATE); + else if (nargs >= 3){ + for(int i=0; itype != T_POINTER) + THROW(E_TYPE, "Pointer", JIF.F_TYPE_get_name(args[0]->type)); + else if (extra == 0) + check_string(args[0]); + type = (TYPE)(void*)GB.FindClass("File"); + //ref_stack(); Nothing else can be on the stack ;) + break; + + case 0x79: //Close + //ref_stack(); + type = T_VOID; + break; + + case 0x7A: //Input + //ref_stack(); + type = -1; //Unknown type + break; + + case 0x7B: //Line Input + //ref_stack(); + type = T_STRING; + break; + + case 0x7C: //Print + type = T_VOID; + if (nargs < 1) + THROW(E_NEPARAM); + for(int i=1; itype == T_FUNCTION) + assert(false && "Jit compiler cannot call Print with a Function as argument yet."); + if (args[i]->type == T_CLASS) + assert(false && "Jit compiler cannot call Print with a Class as argument yet."); + } + break; + + case 0x7D: //Read + if (extra){ + //ReadBytes + JIT_conv(args[1], T_INTEGER); + type = T_STRING; + } else { + //Read + if (args[1]->type == T_INTEGER){ + auto int_expr = dyn_cast(args[1]); + assert(int_expr != NULL); + type = int_expr->i; + } else if (args[1]->type == T_CLASS){ + auto class_expr = dyn_cast(args[1]); + assert(class_expr != NULL); + type = (TYPE)(void*)class_expr->klass; + } else + THROW_ILLEGAL(); + } + break; + + case 0x7E: //Write + if (extra){ + //WriteBytes + JIT_conv(args[2], T_INTEGER); + if (args[1]->type != T_POINTER) + check_string(args[1]); + } else { + //Write + TYPE type; + if (args[2]->type == T_INTEGER){ + auto int_expr = dyn_cast(args[2]); + assert(int_expr != NULL); + type = int_expr->i; + } else if (args[2]->type == T_CLASS){ + auto class_expr = dyn_cast(args[2]); + assert(class_expr != NULL); + type = (TYPE)(void*)class_expr->klass; + } else + THROW_ILLEGAL(); + JIT_conv(args[1], type); + } + type = T_VOID; + break; + + case 0x7F: //Flush + type = T_VOID; + break; + + case 0x80: //Lock + if (extra){ + //Unlock + type = T_VOID; + } else { + //Lock + check_string(args[0]); + type = (TYPE)(void*)GB.FindClass("File"); + } + break; + + case 0x81: //Input From, Output To, Error To + //extra contains 0, 1 or 2 + type = T_VOID; + break; + + case 0x82: //Eof + ref_stack(); + type = T_BOOLEAN; + break; + + case 0x83: //Lof + ref_stack(); + type = T_LONG; + break; + + case 0x84: //Seek + ref_stack(); + if (nargs >= 2){ + JIT_conv(args[1], T_LONG); + if (nargs == 3){ + JIT_conv(args[2], T_INTEGER); + } + type = T_VOID; + } else + type = T_LONG; + break; + + case 0x85: //Kill + case 0x87: //Rmdir, deprecated + //New syntax: Kill(0) = Kill, Kill(1) = Mkdir, Kill(2) = Rmdir + //extra = 0, 1 or 2 + check_string(args[0]); + type = T_VOID; + break; + + case 0x86: //Mkdir, deprecated -> Even() & Odd() + if (extra == 0){ + check_string(args[0]); + type = T_VOID; + } else if (extra == 1 || extra == 2){ + JIT_conv(args[0], T_LONG); + type = T_BOOLEAN; + } else { + THROW_ILLEGAL(); + } + break; + + case 0x88: //Move + case 0x89: //Copy, deprecated + //New syntax: func = [Move, Copy, Link, Chmod, Chown, Chgrp][extra] + check_string(args[0]); + check_string(args[1]); + type = T_VOID; + break; + + case 0x8A: //Link, deprecated -> IsNan() & IsInf() + if (extra == 0){ + check_string(args[0]); + check_string(args[1]); + type = T_VOID; + } else if (extra == 1){ + JIT_conv(args[0], T_FLOAT); + type = T_BOOLEAN; + } else if (extra == 2){ + JIT_conv(args[0], T_FLOAT); + type = T_INTEGER; + } else { + THROW_ILLEGAL(); + } + break; + + case 0x8B: //Exist + check_string(args[0]); + if (nargs == 2) + JIT_conv(args[1], T_BOOLEAN); + type = T_BOOLEAN; + break; + + case 0x8C: //Access + check_string(args[0]); + if (nargs != 1) + JIT_conv(args[1], T_INTEGER); + type = T_BOOLEAN; + break; + + case 0x8D: //Stat + ref_stack(); + check_string(args[0]); + if (nargs == 2) + JIT_conv(args[1], T_BOOLEAN); + type = (TYPE)(void*)GB.FindClass("Stat"); + break; + + case 0x8E: //Dfree + check_string(args[0]); + type = T_LONG; + break; + + case 0x8F: //Temp + if (nargs != 0) + check_string(args[0]); + type = T_STRING; + break; + + case 0x90: //IsDir + check_string(args[0]); + type = T_BOOLEAN; + break; + + case 0x91: //Dir + ref_stack(); + check_string(args[0]); + if (nargs >= 2){ + check_string(args[1]); + if (nargs == 3) + check_integer(args[2]); + } + type = (TYPE)(void*)GB.FindClass("String[]"); + break; + + case 0x92: //RDir + ref_stack(); + check_string(args[0]); + if (nargs >= 2){ + check_string(args[1]); + if (nargs >= 3){ + check_integer(args[2]); + if (nargs == 4) + JIT_conv(args[3], T_BOOLEAN); + } + } + type = (TYPE)(void*)GB.FindClass("String[]"); + break; + + case 0x93: { //Exec + if (extra){ + //Shell + check_string(args[0]); + } else { + JIT_conv(args[0], (TYPE)(void*)GB.FindClass("String[]")); + } + auto int_expr = dyn_cast(args[2]); + assert(int_expr != NULL); + check_string(args[3]); + if (int_expr->i & 8){ // 8 = TS_EXEC_STRING = PM_STRING + type = T_STRING; + } else { + type = (TYPE)(void*)GB.FindClass("Process"); + } + break; + } + + case 0x94: //Alloc + ref_stack(); + if (nargs == 2) + check_integer(args[1]); + if (!TYPE_is_string(args[0]->type)) + check_integer(args[0]); + type = T_POINTER; + break; + + case 0x95: //Free + check_pointer(args[0]); + type = T_VOID; + break; + + case 0x96: //Realloc + ref_stack(); + if (nargs == 3) + check_integer(args[2]); + check_integer(args[1]); + check_pointer(args[0]); + type = T_POINTER; + break; + + case 0x97: //StrPtr + ref_stack(); + check_pointer(args[0]); + if (nargs != 1) + check_integer(args[1]); + type = T_CSTRING; + break; + + case 0x98: //Sleep + check_float(args[0]); + type = T_VOID; + break; + + case 0x99: { //VarPtr + type = T_POINTER; + //FIXME check numeric, pointer or string + auto pie = dyn_cast(args[0]); + assert(pie); + ushort op = pie->i; + if ((op & 0xFF00) == C_PUSH_LOCAL){ + CLASS_LOCAL* var = &FP->local[op & 0xFF]; + TYPE t = ctype_to_type(&var->type); + if (t == T_VOID || t > T_POINTER) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(t)); + } else if ((op & 0xF800) == C_PUSH_DYNAMIC){ + if (OP == NULL) + THROW_ILLEGAL(); + } else if ((op & 0xF800) == C_PUSH_STATIC){ + } else + THROW_ILLEGAL(); + break; + } + + case 0x9A: //Collection + ref_stack(); + type = (TYPE)(void*)GB.FindClass("Collection"); + break; + + case 0x9B: //Tr + JIT_conv(args[0], T_STRING); + type = T_CSTRING; + break; + + case 0x9C: //Quote, Shell, Html + case 0x9D: //Unquote + JIT_conv(args[0], T_STRING); + type = T_STRING; + break; + + case 0x9E: //MkInteger, ... + if (extra == 0 || (extra > T_DATE && extra < T_POINTER) || extra > T_POINTER) + THROW_ILLEGAL(); + JIT_conv(args[0], extra); + type = extra <= T_BYTE ? T_CSTRING : T_STRING; + if (extra > T_BYTE) + on_stack = true; + break; + + case 0x9F: //Ptr + ref_stack(); + type = args[0]->type; + if (type != T_VARIANT && !TYPE_is_string(type) && type != T_POINTER) + THROW(E_TYPE, "Pointer", JIF.F_TYPE_get_name(type)); + type = extra; + break; + } +} +EqExpression::EqExpression(Expression** it) : BinOpExpression(it) { + type = T_BOOLEAN; + + if (left->type == T_VOID || right->type == T_VOID) + THROW(E_NRETURN); + + if (left->type == T_NULL || right->type == T_NULL){ + t = T_NULL; + return; + } + + if (left->type == T_VARIANT || right->type == T_VARIANT){ + ref_stack(); + left->must_on_stack(); + right->must_on_stack(); + t = T_VARIANT; + return; + } + + t = Max(left->type, right->type); + if (TYPE_is_object(left->type) && TYPE_is_object(right->type)){ + t = T_OBJECT; + left->must_on_stack(); + right->must_on_stack(); + } + else if (TYPE_is_object(t)) + THROW(E_TYPE, "Object", JIF.F_TYPE_get_name(Min(left->type, right->type))); + + if (!TYPE_is_object(t)){ + JIT_conv(left, t); + JIT_conv(right, t); + } +} +NotExpression::NotExpression(Expression* expr) : UnaryExpression(expr) { + type = expr->type; + if (type >= T_BOOLEAN && type <= T_LONG) return; + if (type == T_VARIANT){ + ref_stack(); //FIXME might not always be needed + expr->must_on_stack(); + on_stack = true; + no_ref_variant = true; + return; + } + if (TYPE_is_object(type) || type == T_NULL || TYPE_is_string(type)) + type = T_BOOLEAN; + else + THROW(E_TYPE, "Number, String or Object", JIF.F_TYPE_get_name(type)); +} +LessExpression::LessExpression(Expression** it) : BinOpExpression(it) { + type = T_BOOLEAN; + + if (left->type == T_VOID || right->type == T_VOID) + THROW(E_NRETURN); + + if (left->type == T_VARIANT || right->type == T_VARIANT){ + ref_stack(); //FIXME might not always be needed + left->must_on_stack(); + right->must_on_stack(); + must_on_stack(); + t = T_VARIANT; + return; + } + + t = Max(left->type, right->type); + if (t == T_NULL || TYPE_is_string(t)){ + TYPE typem = Min(left->type, right->type); + if (!TYPE_is_string(typem)) + THROW(E_TYPE, JIF.F_TYPE_get_name(typem), JIF.F_TYPE_get_name(t)); + } else if (TYPE_is_object(t)) + THROW(E_TYPE, "Number, Date or String", JIF.F_TYPE_get_name(type)); + + if (t == T_BYTE && Min(left->type, right->type) == T_BOOLEAN){ + //To get the expected result: + JIT_conv(left, T_INTEGER); + JIT_conv(right, T_INTEGER); + } else { + JIT_conv(left, t); + JIT_conv(right, t); + } +} +AddSubBaseExpression::AddSubBaseExpression(Expression** it) : BinOpExpression(it) { + type = Max(left->type, right->type); + + if (left->type == T_VOID || right->type == T_VOID) + THROW(E_NRETURN); + + if (left->type == T_VARIANT || right->type == T_VARIANT){ + ref_stack(); //FIXME might not always be needed + left->must_on_stack(); + right->must_on_stack(); + type = T_VARIANT; + no_ref_variant = true; + on_stack = true; + return; + } + + if (TYPE_is_number_date(type) || TYPE_is_pointer(type)){ + if (type == T_DATE){ + JIT_conv(left, T_FLOAT); + JIT_conv(right, T_FLOAT); + } else { + JIT_conv(left, type); + JIT_conv(right, type); + } + } + + if (TYPE_is_string(left->type)){ + JIT_conv(left, T_FLOAT); + } + + if (TYPE_is_string(right->type)){ + JIT_conv(right, T_FLOAT); + } + + if (left->type == T_NULL || right->type == T_NULL) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(T_NULL)); + + type = Max(left->type, right->type); + + if (TYPE_is_number_date(type) || TYPE_is_pointer(type)){ + if (type == T_DATE){ + JIT_conv(left, T_FLOAT); + JIT_conv(right, T_FLOAT); + } else { + JIT_conv(left, type); + JIT_conv(right, type); + } + } else + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); +} +MulExpression::MulExpression(Expression** it) : BinOpExpression(it) { + type = Max(left->type, right->type); + + if (left->type == T_VOID || right->type == T_VOID) + THROW(E_NRETURN); + + if (left->type == T_VARIANT || right->type == T_VARIANT){ + ref_stack(); //FIXME might not always be needed + left->must_on_stack(); + right->must_on_stack(); + type = T_VARIANT; + no_ref_variant = true; + on_stack = true; + return; + } + + if (TYPE_is_number_date(type)){ + JIT_conv(left, type); + JIT_conv(right, type); + return; + } + + if (TYPE_is_string(left->type)){ + JIT_conv(left, T_FLOAT); + } + + if (TYPE_is_string(right->type)){ + JIT_conv(right, T_FLOAT, left); + } + + if (left->type == T_NULL || right->type == T_NULL) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(T_NULL)); + + type = Max(left->type, right->type); + + if (TYPE_is_number_date(type)){ + JIT_conv(left, type); + JIT_conv(right, type, left); + } else + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); +} +QuoRemBaseExpression::QuoRemBaseExpression(Expression** it) : BinOpExpression(it) { + type = Max(left->type, right->type); + + if (left->type == T_VOID || right->type == T_VOID) + THROW(E_NRETURN); + + ref_stack(); //Division by zero possible + + if (left->type == T_VARIANT || right->type == T_VARIANT){ + /*left->must_on_stack(); + right->must_on_stack(); + type = T_VARIANT; + no_ref_variant = true;*/ + //FIXME is this good/ok to use Long here? + type = T_LONG; + JIT_conv(left, T_LONG); + JIT_conv(right, T_LONG); + return; + } + + if (left->type == T_NULL || right->type == T_NULL) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(T_NULL)); + + if (TYPE_is_integer_long(type)){ + JIT_conv(left, type); + JIT_conv(right, type, left); + } else { + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); + } +} +AndOrXorBaseExpression::AndOrXorBaseExpression(Expression** it) : BinOpExpression(it) { + if (left->type == T_VOID || right->type == T_VOID) + THROW(E_NRETURN); + + if (TYPE_is_variant(left->type) || TYPE_is_variant(right->type)){ + ref_stack(); + left->must_on_stack(); + right->must_on_stack(); + type = T_VARIANT; + no_ref_variant = true; + on_stack = true; + return; + } + + if (TYPE_is_string(left->type)) + JIT_conv(left, T_BOOLEAN); + if (TYPE_is_string(right->type)) + JIT_conv(right, T_BOOLEAN); + + type = Max(left->type, right->type); + + if (TYPE_is_null(left->type) || TYPE_is_null(right->type)) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(T_NULL)); + + if (TYPE_is_number_date(type)){ + JIT_conv(left, type); + JIT_conv(right, type); + } else + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); +} diff --git a/gb.jit.llvm/src/jit_gambas_pass.cpp b/gb.jit.llvm/src/jit_gambas_pass.cpp new file mode 100644 index 00000000..ab73edf4 --- /dev/null +++ b/gb.jit.llvm/src/jit_gambas_pass.cpp @@ -0,0 +1,139 @@ +/*************************************************************************** + + jit_gambas_pass.cpp + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "llvm/Pass.h" +#if (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 3) +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Function.h" +#elif (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR == 2) +#include "llvm/IRBuilder.h" +#include "llvm/Function.h" +#else +#include "llvm/Support/IRBuilder.h" +#include "llvm/Function.h" +#endif +#include "llvm/Support/raw_ostream.h" + +#include "main.h" + +#ifdef __CYGWIN__ +#define __finite finite +#define __isnan __isnand +#define __isinf __isinfd +#endif +#define FUNCTION_NAME(s) _FN(s) +#define _FN(s) #s + +using namespace llvm; + +namespace { + +struct GambasPass : public FunctionPass { + static char ID; + GambasPass() : FunctionPass(ID) {} + + virtual bool runOnFunction(Function &F); +}; + +} + +char GambasPass::ID = 0; + +static RegisterPass X("gamabs-pass", "Gambas Pass", false, false); + +FunctionPass* createGambasPass(){ + return new GambasPass(); +} + +bool GambasPass::runOnFunction(Function &F){ + IRBuilder<> Builder(F.getContext()); + + bool changed = false; + for(Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB) { + for(BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ){ + ICmpInst* ICI = dyn_cast(I); + CallInst* CI = dyn_cast(I++); + + if (ICI && ICI->hasMetadata() && ICI->getMetadata("unref_slt") && dyn_cast(ICI->getOperand(0))){ + ICI->replaceAllUsesWith(ConstantInt::get(ICI->getType(), false)); + ICI->eraseFromParent(); + changed = true; + continue; + } + + if (!CI) + continue; + + Function* callee = CI->getCalledFunction(); + if (callee == NULL || !callee->isDeclaration()) + continue; + + StringRef name = callee->getName(); + if (name == "JR_release_variant" || name == "JR_borrow_variant"){ + ConstantInt* vtype_int = dyn_cast(CI->getArgOperand(0)); + if (!vtype_int) + continue; + + uint64_t vtype = vtype_int->getZExtValue(); + if (TYPE_is_string(vtype) || TYPE_is_object(vtype)) + continue; + + CI->eraseFromParent(); + changed = true; + } else if (name == FUNCTION_NAME(__finite)){ + ConstantFP* op = dyn_cast(CI->getArgOperand(0)); + if (!op) + continue; + + int val = __finite(op->getValueAPF().convertToDouble()); + Constant* res = ConstantInt::get(CI->getType(), val); + CI->replaceAllUsesWith(res); + CI->eraseFromParent(); + changed = true; + } else if (name == FUNCTION_NAME(__isnan)){ + ConstantFP* op = dyn_cast(CI->getArgOperand(0)); + if (!op) + continue; + + int val = __isnan(op->getValueAPF().convertToDouble()); + Constant* res = ConstantInt::get(CI->getType(), val); + CI->replaceAllUsesWith(res); + CI->eraseFromParent(); + changed = true; + } else if (name == FUNCTION_NAME(__isinf)){ + ConstantFP* op = dyn_cast(CI->getArgOperand(0)); + if (!op) + continue; + + int val = __isinf(op->getValueAPF().convertToDouble()); + Constant* res = ConstantInt::get(CI->getType(), val); + CI->replaceAllUsesWith(res); + CI->eraseFromParent(); + changed = true; + } + } + } + return changed; +} diff --git a/gb.jit.llvm/src/jit_gambas_pass.h b/gb.jit.llvm/src/jit_gambas_pass.h new file mode 100644 index 00000000..e50afbc1 --- /dev/null +++ b/gb.jit.llvm/src/jit_gambas_pass.h @@ -0,0 +1,31 @@ +/*************************************************************************** + + jit_gambas_pass.h + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __JIT_GAMBAS_PASS_H +#define __JIT_GAMBAS_PASS_H + +llvm::FunctionPass* createGambasPass(void); + +#endif diff --git a/gb.jit.llvm/src/jit_read.cpp b/gb.jit.llvm/src/jit_read.cpp new file mode 100644 index 00000000..1a686442 --- /dev/null +++ b/gb.jit.llvm/src/jit_read.cpp @@ -0,0 +1,1100 @@ +/*************************************************************************** + + jit_read.c + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __JIT_READ_C + +#include "jit.h" +#include "gb_pcode.h" + +#include +#include + +static unsigned short* code; +static int size; +static int pos; +static int addr; +static uint stack_start; + +static bool in_dup = false; + +unsigned short* get_current_read_pos(){ + return code + pos; +} + +static std::vector _stack; + +void ref_stack(){ + for(size_t i=0, e=_stack.size(); i!=e; i++) + _stack[i]->ref_on_stack(); +} + +static int stack_size(){ + return _stack.size(); +} + +static bool stack_empty(){ + return _stack.size() == stack_start; +} + +static void push(Expression* expr){ + _stack.push_back(expr); +} + +static Expression*& top(){ + return _stack.back(); +} + +static Expression* pop(){ + Expression* val = _stack.back(); + _stack.pop_back(); + return val; +} + +static Expression** extract(int num){ + size_t s = _stack.size(); + size_t start = (int)s-num; + Expression** it = &_stack[start]; + _stack.resize(start); + return it; +} + +static Expression** extract_reverse(int num){ + Expression** it = extract(num); + std::reverse(it, it+num); + return it; +} + +std::vector taken_addresses; + +void mark_address_taken(int addr){ + taken_addresses.push_back(addr); +} + +static void push_statement(Expression* expr){ + Statement* s = new Statement(); + s->addr = addr; + s->address_taken = false; + s->expr = expr; + all_statements.push_back(s); +} + +static Expression*& top_statement(){ + return all_statements.back()->expr; +} + +static Expression* pop_statement(){ + Expression* ret = top_statement(); + delete all_statements.back(); + all_statements.pop_back(); + return ret; +} + +static void push_constant(CLASS_DESC* desc){ + if (TYPE_is_string(desc->constant.type) && desc->constant.translate){ + char* addr = (char *)GB.Translate(desc->constant.value._string); + push(new PushCStringExpression(addr, 0, strlen(addr))); + return; + } + void* value = &desc->constant.value; + switch(desc->constant.type){ + case T_BOOLEAN: + push(new PushIntegerExpression(1, *(bool*)value)); return; + case T_BYTE: + push(new PushIntegerExpression(8, *(unsigned char*)value)); return; + case T_SHORT: + push(new PushIntegerExpression(16, *(short*)value)); return; + case T_INTEGER: + push(new PushIntegerExpression(32, *(int*)value)); return; + case T_LONG: + push(new PushIntegerExpression(64, *(int64_t*)value)); return; + case T_SINGLE: + push(new PushFloatExpression(32, *(float*)value)); return; + case T_FLOAT: + push(new PushFloatExpression(64, *(double*)value)); return; + case T_CSTRING: + push(new PushCStringExpression(*(char**)value, 0, strlen(*(char**)value))); return; + default: + assert(false && "Illegal constant type"); + } +} + +static int get_subr_nargs(int digit, int extra){ + int nargs = extra; + switch(digit){ + case 0x6D: //Timer + case 0x6E: //Now + case 0x75: //Error + case 0x76: //Debug + nargs = 0; break; + + case 0x43: //Len + case 0x44: //Space + case 0x46: //Trim + case 0x47: //UCase + case 0x48: //LCase + case 0x49: //Chr + case 0x53: //DConv + case 0x54: //Abs + case 0x55: //Int + case 0x56: //Fix + case 0x57: //Sgn + case 0x58: //Math + case 0x63: //IsAscii, IsLetter ... + case 0x65: //IsBoolean ... + case 0x66: //TypeOf + case 0x67: //CBool ... + case 0x6A: //Val + case 0x6B: //Str + case 0x6F: //Year + case 0x79: //Close + case 0x7B: //Line Input + case 0x7F: //Flush + case 0x80: //Lock + case 0x81: //Input From, Output To, Error To + case 0x85: //Kill + case 0x86: //Mkdir, deprecated -> Even() & Odd() + case 0x87: //Rmdir + case 0x8E: //DFree + case 0x90: //IsDir + case 0x95: //Free + case 0x98: //Sleep + case 0x99: //VarPtr + case 0x9B: //Tr + case 0x9C: //Quote, Shell, Html + case 0x9D: //Unquote + case 0x9E: //MkInteger, ... + case 0x9F: //Bool@, Byte@, ... + nargs = 1; break; + + case 0x45: //String + case 0x50: //Scan + case 0x5D: //Min + case 0x5E: //Max + case 0x62: //Math2 + case 0x64: //Bit + case 0x78: //Open + case 0x7D: //Read + case 0x88: //Move + case 0x89: //Copy + nargs = 2; break; + + case 0x8A: //Link, deprecated -> IsNan() & IsInf() + nargs = extra == 0 ? 2 : 1; break; + + case 0x52: //Conv + case 0x5F: //IIf + case 0x73: //DateAdd, DateDiff + case 0x7E: //Write + nargs = 3; break; + + case 0x93: //Exec + nargs = 4; break; + } + return nargs; +} + +static void JIT_push_unknown(){ + const char* name = CP->load->unknown[code[pos+1]]; + pos += 2; + bool defined; + + if (TYPE_is_pure_object(top()->type)){ + //These can now be on top of the stack: + //T_NULL, a null object of type top()->type + //A pure object (type > T_OBJECT) with type top()->type + //When top()->type is a virtual class: + // T_NULL can be returned (only by non-static methods at the moment) + // An object represented as a virtual class top()->type is the virtual class, + // but the real type can be something else. + // A T_CLASS of the class top()->type: + // happens when a native static method says it returns a virtual object + // But beware, a native static method might as well return a virtual object + // of type .SubCollection + defined = true; + CLASS* klass = (CLASS*)(void*)top()->type; + + JIT_load_class(klass); + + int index = CLASS_find_symbol(klass, name); + + bool is_unknown = false; + + if (index == NO_SYMBOL){ + if (klass->special[SPEC_UNKNOWN] == NO_SYMBOL) + THROW(E_NSYMBOL, klass->name, name); + + if (klass->special[SPEC_PROPERTY] != NO_SYMBOL){ + push(new PushPureObjectUnknownExpression(pop(), name, code[pos-1])); + return; + } + + index = klass->special[SPEC_UNKNOWN]; + is_unknown = true; + } + + CLASS_DESC* desc = klass->table[index].desc; + + switch (CLASS_DESC_get_type(desc)){ + case CD_CONSTANT: + if (klass->is_virtual){ + //FIXME OK to extract the value and ignore actually evaluating, and what if we got a null value..??? + /*delete*/ pop(); + push_constant(desc); + return; + } + push(new PushPureObjectConstantExpression(pop(), index)); + return; + case CD_VARIABLE: + /*int offset = desc->variable.offset; + TYPE type = desc->variable.ctype;*/ + push(new PushPureObjectVariableExpression(pop(), index)); + return; + + case CD_STATIC_VARIABLE: + THROW(E_STATIC, klass->name, name); + //push(new PushPureObjectStaticVariableExpression(pop(), index)); + return; + + case CD_PROPERTY: + case CD_PROPERTY_READ: + if (klass->is_virtual) + push(new PushVirtualPropertyExpression(pop(), index)); + else + push(new PushPureObjectPropertyExpression(pop(), index)); + return; + + case CD_STRUCT_FIELD: + push(new PushPureObjectStructFieldExpression(pop(), index)); + return; + + case CD_STATIC_PROPERTY: + case CD_STATIC_PROPERTY_READ: + //See System.User why this is a problem ... + //Runtime check is now done. + //if (!klass->is_virtual) + // THROW(E_STATIC, klass->name, name); + //push(new PushVirtualClassStaticProperty(pop(), index)); + push(new PushPureObjectStaticPropertyExpression(pop(), index, name)); + return; + + case CD_METHOD: + if (klass->is_virtual) + push(new PushVirtualFunctionExpression(pop(), index, is_unknown ? name : NULL)); + else + push(new PushPureObjectFunctionExpression(pop(), index, is_unknown ? name : NULL)); + return; + + case CD_STATIC_METHOD: + if (klass->is_virtual) + push(new PushVirtualStaticFunctionExpression(pop(), index, is_unknown ? name : NULL)); + else + push(new PushPureObjectStaticFunctionExpression(pop(), index, is_unknown ? name : NULL)); + return; + + case CD_EXTERN: + push(new PushExternExpression(klass, index, pop())); + return; + + default: THROW(E_NSYMBOL, klass->name, name); + } + + } else if (top()->type == T_OBJECT){ + defined = false; + push(new PushUnknownExpression(pop(), code[pos-1], code + pos - 2)); + } else if (top()->type == T_CLASS){ + defined = true; + + PushClassExpression* pce = dyn_cast(top()); + assert(pce); + + CLASS* klass = pce->klass; + + int index = CLASS_find_symbol(klass, name); + + if (index == NO_SYMBOL){ + if (klass->special[SPEC_UNKNOWN] == NO_SYMBOL) + THROW(E_NSYMBOL, klass->name, name); + + if (klass->special[SPEC_PROPERTY] != NO_SYMBOL){ + pop(); + if (!klass->unknown_static || !klass->property_static){ + if (!klass->auto_create) + THROW(E_DYNAMIC); + push(new PushPureObjectUnknownExpression(new PushAutoCreateExpression(klass), name, code[pos-1])); + return; + } + push(new PushStaticUnknownExpression(klass, name, code[pos-1])); + return; + } + + index = klass->special[SPEC_UNKNOWN]; + + if (!klass->unknown_static){ + pop(); + if (!klass->auto_create) + THROW(E_DYNAMIC); + + push(new PushPureObjectFunctionExpression(new PushAutoCreateExpression(klass), index, name)); + return; + } + + push(new PushStaticFunctionExpression(pop(), index, name)); + return; + } + + CLASS_DESC* desc = klass->table[index].desc; + + switch (CLASS_DESC_get_type(desc)){ + case CD_CONSTANT: { + /*delete*/ pop(); + push_constant(desc); + return; + } + case CD_VARIABLE: + if (!klass->auto_create) + THROW(E_DYNAMIC, klass->name, name); + /*delete*/ pop(); + push(new PushPureObjectVariableExpression(new PushAutoCreateExpression(klass), index)); + return; + + case CD_STRUCT_FIELD: + THROW(E_DYNAMIC, klass->name, name); + + case CD_STATIC_VARIABLE: + /*delete*/ pop(); + push(new PushStaticVariableExpression(&desc->variable)); + return; + + case CD_PROPERTY: + case CD_PROPERTY_READ: + if (!klass->auto_create) + THROW(E_DYNAMIC, klass->name, name); + /*delete*/ pop(); + push(new PushPureObjectPropertyExpression(new PushAutoCreateExpression(klass), index)); + return; + + case CD_STATIC_PROPERTY: + case CD_STATIC_PROPERTY_READ: + push(new PushStaticPropertyExpression(pop(), index)); + return; + + case CD_METHOD: + if (!klass->auto_create) + THROW(E_DYNAMIC, klass->name, name); + /*delete*/ pop(); + push(new PushPureObjectFunctionExpression(new PushAutoCreateExpression(klass), index, NULL)); + return; + + case CD_STATIC_METHOD: + /*int kind = FUNCTION_PUBLIC; + if (FUNCTION_is_native(&desc->method)){ + if (desc->method.subr) + kind = FUNCTION_SUBR; + else + kind = FUNCTION_NATIVE; + }*/ + push(new PushStaticFunctionExpression(pop(), index)); + return; + + case CD_EXTERN: + pop(); + push(new PushExternExpression(klass, desc->ext.exec, NULL)); + return; + + default: THROW(E_NSYMBOL, klass->name, name); + + } + + } else if (top()->type == T_FUNCTION){ + assert(false && "Syntax error"); + } else if (top()->type == T_VARIANT){ + defined = false; + push(new PushUnknownExpression(pop(), code[pos-1], code + pos - 2)); + } else if (top()->type == T_NULL) { + THROW(E_NULL); + } else { + THROW(E_NOBJECT); + } +} + +static void JIT_pop_unknown(){ + const char* name = CP->load->unknown[code[pos+1]]; + pos += 2; + + if (TYPE_is_pure_object(top()->type)){ + //These can now be on top of the stack: + //T_NULL, a null object of type top()->type + //A pure object (type > T_OBJECT) with type top()->type + //When top()->type is a virtual class: + // T_NULL can be returned (only by non-static methods at the moment) + // An object represented as a virtual class top()->type is the virtual class, + // but the real type can be something else. + // A T_CLASS of the class top()->type: + // happens when a native static method says it returns a virtual object + // But beware, a native static method might as well return a virtual object + // of type .SubCollection + // By the time of this writing, there are no write properties taking virtual classes. + CLASS* klass = (CLASS*)(void*)top()->type; + + int index = CLASS_find_symbol(klass, name); + + if (index == NO_SYMBOL){ + if (klass->special[SPEC_UNKNOWN] == NO_SYMBOL) + THROW(E_NSYMBOL, klass->name, name); + + if (klass->special[SPEC_PROPERTY] == NO_SYMBOL) + THROW(E_NPROPERTY, klass->name, name); + + //Unknown property + Expression* obj = pop(); + Expression* val = pop(); + push_statement(new PopUnknownPropertyUnknownExpression(obj, val, name)); + return; + } + + CLASS_DESC* desc = klass->table[index].desc; + + Expression* obj = pop(); + Expression* val = pop(); + + switch(CLASS_DESC_get_type(desc)){ + case CD_CONSTANT: + THROW(E_NPROPERTY, klass->name, name); + + case CD_VARIABLE: + push_statement(new PopPureObjectVariableExpression(obj, val, index)); + return; + + case CD_STRUCT_FIELD: + if (desc->variable.ctype.id == TC_STRUCT || desc->variable.ctype.id == TC_ARRAY) + THROW(E_NWRITE, klass->name, name); + push_statement(new PopPureObjectStructFieldExpression(obj, val, index)); + return; + + case CD_STATIC_VARIABLE: + THROW(E_STATIC, klass->name, name); + //push_statement(new PopPureObjectStaticVariableExpression(obj, val, index)); + return; + + case CD_PROPERTY: + if (klass->is_virtual) + push_statement(new PopVirtualPropertyExpression(obj, val, index, name, false)); + else + push_statement(new PopPureObjectPropertyExpression(obj, val, index)); + return; + + case CD_STATIC_PROPERTY: + //Should only be used on virtual classes. Runtime check is added if not, throwing E_STATIC + //THROW(E_STATIC, klass->name, name); + push_statement(new PopVirtualPropertyExpression(obj, val, index, name, true)); + return; + + case CD_PROPERTY_READ: + case CD_STATIC_PROPERTY_READ: + THROW(E_NWRITE, klass->name, name); + + case CD_METHOD: + case CD_STATIC_METHOD: + THROW(E_NPROPERTY, klass->name, name); + + default: THROW(E_NSYMBOL, klass->name, name); + } + } else if (top()->type == T_OBJECT){ + Expression* obj = pop(); + Expression* val = pop(); + push_statement(new PopUnknownExpression(obj, val, code[pos-1], code + pos - 2)); + } else if (top()->type == T_CLASS){ + + PushClassExpression* pce = dyn_cast(top()); + assert(pce); + + CLASS* klass = pce->klass; + + int index = CLASS_find_symbol(klass, name); + + if (index == NO_SYMBOL){ + if (klass->special[SPEC_UNKNOWN] == NO_SYMBOL) + THROW(E_NSYMBOL, klass->name, name); + + if (klass->special[SPEC_PROPERTY] == NO_SYMBOL) + THROW(E_NPROPERTY, klass->name, name); + + //Unknown property + Expression* obj = pop(); + Expression* val = pop(); + push_statement(new PopUnknownPropertyUnknownExpression(obj, val, name)); + return; + } + + CLASS_DESC* desc = klass->table[index].desc; + + pop(); + Expression* val = pop(); + + switch(CLASS_DESC_get_type(desc)){ + case CD_CONSTANT: + THROW(E_NPROPERTY, klass->name, name); + + case CD_VARIABLE: + if (!klass->auto_create) + THROW(E_DYNAMIC, klass->name, name); + //delete obj; + push_statement(new PopPureObjectVariableExpression(new PushAutoCreateExpression(klass), val, index)); + return; + + case CD_STRUCT_FIELD: + THROW(E_DYNAMIC, klass->name, name); + + case CD_STATIC_VARIABLE: + //delete obj; + if (desc->variable.ctype.id == TC_ARRAY || desc->variable.ctype.id == TC_STRUCT) + THROW_ILLEGAL(); + push_statement(new PopStaticVariableExpression(desc->variable.type, (char*)desc->variable.klass->stat + desc->variable.offset, val)); + return; + + case CD_PROPERTY: + if (!klass->auto_create) + THROW(E_DYNAMIC, klass->name, name); + //delete obj; + push_statement(new PopPureObjectPropertyExpression(new PushAutoCreateExpression(klass), val, index)); + return; + + case CD_STATIC_PROPERTY: + //delete obj; + push_statement(new PopStaticPropertyExpression(klass, val, index)); + return; + + case CD_PROPERTY_READ: + case CD_STATIC_PROPERTY_READ: + THROW(E_NWRITE, klass->name, name); + + case CD_METHOD: + case CD_STATIC_METHOD: + THROW(E_NPROPERTY, klass->name, name); + + default: THROW(E_NSYMBOL, klass->name, name); + } + } else if (top()->type == T_FUNCTION){ + assert(false && "syntax error"); + } else if (top()->type == T_VARIANT){ + Expression* obj = pop(); + Expression* val = pop(); + push_statement(new PopUnknownExpression(obj, val, code[pos-1], code + pos - 2)); + return; + } else if (top()->type == T_NULL) { + THROW(E_NULL); + } else { + THROW(E_NOBJECT); + } +} + +static void check_swap(){ + if (!in_dup && !stack_empty()){ + /* Then this have happened: + Push A + Push B + Pop A + Pop B + and we have just made the Pop A + So we replace Push A, Push B, Pop A to a SwapExpression, + that is taken as input for Pop B */ + push(new SwapExpression(pop(), pop_statement())); + } +} + +#define NEXT pos+=ncode; goto __NEXT; +#define CONT goto __NEXT; + +static void JIT_read_statement(){ + while(pos < size){ + unsigned short op = code[pos]; + int ncode; + switch (op & 0xFF00){ + case C_PUSH_UNKNOWN: case C_POP_UNKNOWN: + case C_PUSH_INTEGER: + case C_JUMP: case C_JUMP_IF_TRUE: case C_JUMP_IF_FALSE: case C_GOSUB: + case C_NEXT: case C_JUMP_NEXT: + case C_TRY: + + ncode = 2; + break; + + case C_PUSH_LONG: + + ncode = 3; + break; + + case C_BYREF: + ncode = 2 + (op & 0xFF); + break; + + default: + + if ((op & 0xFF00) == (C_PUSH_CONST | 0xF00)) + ncode = 2; + else + ncode = 1; + } + + unsigned short digit = (op >> 12U); + int value = op & 0xFFF; + if (value >= 0x800) value |= 0xFFFFF000; + + switch (digit){ + case 0xF: + push(new PushIntegerExpression(32, value)); + NEXT + case 0xE: { + VALUE val; + unsigned short ind = op & 0xFFF; + if ((op & 0xF00) == 0xF00) + ind = code[++pos]; + #define LOCAL_gettext GB.Translate + VALUE_class_constant_inline(CP, &val, ind); + #undef LOCAL_gettext + + switch(val.type){ + case T_INTEGER: + push(new PushIntegerExpression(32, val._integer.value)); + NEXT + case T_LONG: + push(new PushIntegerExpression(64, val._long.value)); + NEXT + case T_SINGLE: + push(new PushFloatExpression(32, val._single.value)); + NEXT + case T_FLOAT: + push(new PushFloatExpression(64, val._float.value)); + NEXT + case T_CSTRING: + push(new PushCStringExpression(val._string.addr, val._string.start, val._string.len)); + NEXT + case T_POINTER: + push(new PushNullPointerExpression()); + NEXT + } + assert(false && "Invalid constant type"); + } + case 0xD: + if (value & 0x800) + push_statement(new PopStaticExpression(pop(), value & 0x7FF)); + else + push_statement(new PopDynamicExpression(pop(), value & 0x7FF)); + check_swap(); + NEXT + case 0xC: + if (value & 0x800) + push(new PushStaticExpression(value & 0x7FF)); + else + push(new PushDynamicExpression(value & 0x7FF)); + NEXT + case 0xB: + if (value & 0x800) + push(new PushFunctionExpression(value & 0x7FF)); + else + push(new PushClassExpression(value & 0x7FF)); + NEXT + case 0xA: + push(new AddQuickExpression(pop(), value)); + NEXT + + default: { + digit = op & 0xFF00; + value = op & 0xFF; + if (value >= 0x80) value |= 0xFFFFFF00; + + switch(digit){ + case C_PUSH_LOCAL: + if (value >= FP->n_local && get_ctrl_type(value) == T_CLASS){ + //Control variable + push(new PushClassExpression(get_ctrl_class(value))); + } else { + push(new PushLocalExpression(value)); + } + NEXT + case C_PUSH_PARAM: + push(new PushParamExpression(value)); + NEXT + case C_POP_LOCAL: + push_statement(new PopLocalExpression(pop(), value)); + check_swap(); + NEXT + case C_POP_PARAM: + push_statement(new PopParamExpression(pop(), value)); + check_swap(); + NEXT + case C_PUSH_UNKNOWN: + JIT_push_unknown(); + CONT + case C_POP_UNKNOWN: + JIT_pop_unknown(); + check_swap(); + CONT + case C_POP_CTRL: + in_dup = false; //From Dup + push_statement(new PopCtrlExpression(pop(), value)); + NEXT + case C_POP_OPTIONAL: + push_statement(new PopOptionalExpression(pop(), value)); + NEXT + case C_PUSH_EXTERN: + push(new PushExternExpression(CP, value, NULL)); + NEXT + case C_PUSH_EVENT: + if ((unsigned char)op == 0xFF){ + push(new PushEventExpression(0, CP->load->unknown[code[pos+1]])); + pos++; + } else { + push(new PushEventExpression(value, NULL)); + } + NEXT + case C_PUSH_ARRAY: + push(new PushArrayExpression(extract(value), value)); + NEXT + case C_POP_ARRAY: { + Expression** it = extract(value); + Expression* val = pop(); + push_statement(new PopArrayExpression(it, value, val)); + check_swap(); + NEXT + } + case C_CALL: { + Expression** it = extract(value); + Expression* func = pop(); + CallExpression* ce = new CallExpression(func, value, it, code + pos); + push(ce); + if ((code[pos+1] & 0xFF00) == C_BYREF){ + int nbyref_codes = 1 + (code[pos+1] & 0xFF); + uint64_t byref_mask = 0; + for(int i=0; idesc){ + signature = ce->desc->method.signature; + npmax = ce->desc->method.npmax; + } else if (ce->kind == FUNCTION_PRIVATE){ + FunctionExpression* fe = dynamic_cast(func); + int index = fe->function_index; + FUNCTION* fp = &CP->load->func[index]; + if (sizeof(TYPE) != sizeof(CLASS_PARAM)){ + //Something has changed... + abort(); + } + signature = (TYPE*)fp->param; + npmax = fp->n_param; + } + + for(int i=value; i --> 0;) if (1ULL<= npmax) + THROW(E_BYREF); + auto ex = new OnStackExpression(); + ex->type = signature ? signature[i] : -1; + + uint stack_start_save = stack_start; + stack_start = stack_size(); + push(ex); + JIT_read_statement(); + stack_start = stack_start_save; + + ce->byref_expressions.push_back(pop_statement()); + } + CONT + } else { + NEXT + } + } + case C_BYREF: { + int nbyref_codes = 1 + (code[pos] & 0xFF); + for(int i=0; iparent)); + } else { + if (OP) + push(new PushMeExpression()); + else + push(new PushClassExpression(CP)); + } + NEXT + } + case C_PUSH_MISC: + switch(value){ + case CPM_NULL: push(new PushNullExpression()); NEXT + case CPM_VOID: push(new PushVoidExpression()); NEXT + case CPM_FALSE: push(new PushIntegerExpression(1, false)); goto push_false_true; + case CPM_TRUE: push(new PushIntegerExpression(1, true)); goto push_false_true; + case CPM_LAST: push(new PushLastExpression()); NEXT + case CPM_STRING: push(new PushCStringExpression(NULL, 0, 0)); NEXT + case CPM_PINF: push(new PushFloatExpression(64, INFINITY)); NEXT + case CPM_MINF: push(new PushFloatExpression(64, -INFINITY)); NEXT + case CPM_COMPLEX: push(new PushComplexExpression(pop())); NEXT + } + assert(false && "Illegal Push Misc"); + push_false_true: { + if ((code[pos+1] & 0xFF00) == C_JUMP){ + //Bytecode representation of And if, Or if is implemented in this strange way + int addr = pos + 3 + code[pos+2]; + //There is a JUMP IF FALSE at addr + int false_addr = addr + code[addr+1] + 2; + push_statement(new JumpIfExpression(pop(), false_addr, addr + 2, false)); + pos += 3; + CONT + } + NEXT + } + case C_ON: { + int num_cases = value; + std::vector destinations; + destinations.resize(num_cases); + for(int i=0; i= EC){ + push_statement(new EndTryExpression()); + push_statement(new JumpExpression(pos + (short)code[pos+1] + 2 + 1)); + } else {*/ + push_statement(new JumpExpression(pos + (short)code[pos+1] + 2)); + //} + NEXT + case C_JUMP_IF_TRUE: case C_JUMP_IF_FALSE: + push_statement(new JumpIfExpression(pop(), pos + (short)code[pos+1] + 2, pos + 2, digit == C_JUMP_IF_TRUE)); + NEXT + case C_JUMP_FIRST: { + Expression* step = pop(); + Expression* to = pop(); + push_statement(new JumpFirstExpression(value, to, step, code[pos+3] & 0xFF, pos + 4, pos + code[pos+2] + 3)); + NEXT + } + case C_JUMP_NEXT: + push_statement(new JumpNextExpression(code[pos-1] & 0xFF, code[pos+2] & 0xFF, pos + 3, pos + code[pos+1] + 2)); + pos += 3; + CONT + case C_GOSUB: { + ngosubs++; + push_statement(new GosubExpression(value, pos + (short)code[pos+1] + 2)); + NEXT + } + case C_FIRST: { + Expression* obj = pop(); + push_statement(new JumpEnumFirstExpression(value, obj)); + NEXT + } + case C_NEXT: + if (value) //Drop + push_statement(new JumpEnumNextExpression((JumpEnumFirstExpression*)top_statement(), pos + 2, pos + code[pos+1] + 2, code + pos, true, NULL)); + else { + OnStackExpression* ose = new OnStackExpression(); + push_statement(new JumpEnumNextExpression((JumpEnumFirstExpression*)top_statement(), pos + 2, pos + code[pos+1] + 2, code + pos, false, ose)); + push(ose); + addr = pos+2; + } + NEXT + case C_DROP: + push_statement(new DropExpression(pop())); + NEXT + case C_DUP: + in_dup = true; //Becomes false again after Pop Ctrl + push(new DupExpression(top())); + NEXT + case C_NEW: { + int nargs = value & 0x3F; + Expression** it = extract(nargs); + push(new NewExpression(it, nargs, value & CODE_NEW_EVENT)); + NEXT + } + case C_BREAK: + //PC = code+pos; + if (EXEC_profile) + push_statement(new ProfileLineExpression(code + pos)); + else + push_statement(new NopExpression(true/*JIF.F_DEBUG_get_current_position()*/)); + //PC = code; + //push_statement(new NopExpression()); + NEXT + case C_RETURN: + push_statement(new ReturnExpression(value == 1 ? pop() : NULL, value)); + NEXT + case C_QUIT: + switch(value){ + case 0: default: push_statement(new QuitExpression(NULL)); NEXT + case 1: push_statement(new NopExpression(false)); NEXT //FIXME breakpoint + case 2: push_statement(new StopEventExpression()); NEXT + case 3: push_statement(new QuitExpression(pop())); NEXT + } + case C_PUSH_CHAR: + push(new PushCStringExpression((char*)&STRING_char_string[value * 2], 0, 1)); + NEXT + case C_TRY: + push_statement(new TryExpression(pos + 2, pos + code[pos+1] + 2)); + NEXT + case C_END_TRY: + push_statement(new EndTryExpression()); + NEXT + case C_CATCH: + if (code + pos < EC) // No finally + push_statement(new ReturnExpression(NULL, 2)); + else // We have a finally somewhere above + push_statement(new CatchExpression()); + NEXT + + case C_EQ: push(new EqExpression(extract(2))); NEXT + case C_NE: push(new NotExpression(new EqExpression(extract(2)))); NEXT + case C_GT: push(new LessExpression(extract_reverse(2))); NEXT + case C_LE: push(new NotExpression(new LessExpression(extract_reverse(2)))); NEXT + case C_LT: push(new LessExpression(extract(2))); NEXT + case C_GE: push(new NotExpression(new LessExpression(extract(2)))); NEXT + case C_NEAR: push(new NearExpression(extract(2))); NEXT + case C_CASE: assert(false && "C_CASE is not used anymore?"); + #define op(_c, _n) case _c: push(new _n##Expression(extract(2))); NEXT + op(C_ADD, Add) + op(C_SUB, Sub) + op(C_MUL, Mul) + op(C_DIV, Div) + + op(C_QUO, Quo) + op(C_REM, Rem) + op(C_POW, Pow) + op(C_AND, And) + op(C_OR, Or) + op(C_XOR, Xor) + + op(C_IS, Is) + #undef op + case C_NEG: push(new NegExpression(pop())); NEXT + case C_NOT: push(new NotExpression(pop())); NEXT + case C_CAT: push(new CatExpression(extract(value), value)); NEXT + case C_FILE: push(new FileExpression(extract(value), value)); NEXT + case C_LIKE: push(new LikeExpression(extract(2), value)); NEXT + + + default: { + digit >>= 8U; + // 0x40 <= digit <= 0x9F + int extra = value & 0x3F; + int nargs = get_subr_nargs(digit, extra); + switch(digit){ + case 0x67: //CBool ... + JIT_conv(top(), extra); + goto _next; + case 0x6B: //Str + if (TYPE_is_string(top()->type)) + goto _next; + break; + } + push(new SubrExpression(digit, extract(nargs), nargs, extra)); + _next: + NEXT + } + } + } + } + __NEXT: + if (stack_empty()) //Next code row + return; + } +} + +void JIT_read(){ + code = FP->code; + size = ((int*)code)[-1] / sizeof(short); + if (FP->code[size-1] == 0) + --size; + + pos = 0; + + TRY { + if (EC != NULL){ + addr = -1; + push_statement(new LargeTryExpression()); + } + + func_byref_mask = 0; + + stack_start = 0; + while(pos < size){ + addr = pos; + PC = code + pos; + JIT_read_statement(); + } + + std::sort(taken_addresses.begin(), taken_addresses.end()); + for(size_t i=0, j=0, e1=taken_addresses.size(), e2=all_statements.size(); i!=e1 && j!=e2;){ + int a = taken_addresses[i]; + int b = all_statements[j]->addr; + if (a == b){ + all_statements[j]->address_taken = true; + i++, j++; + } else if (a < b){ + i++; + } else if (a > b){ + j++; + } + } + taken_addresses.clear(); + } + CATCH { + //puts("Fel:"); + //ERROR_print_at(stderr, 1, 1); + //abort(); + for(size_t i=0, e=all_statements.size(); i!=e; i++) + delete all_statements[i]; + all_statements.clear(); + free_all_expressions(); + taken_addresses.clear(); + JIF.F_ERROR_propagate(); + } + END_TRY +} diff --git a/gb.jit.llvm/src/jit_runtime.c b/gb.jit.llvm/src/jit_runtime.c new file mode 100644 index 00000000..a6b6df5a --- /dev/null +++ b/gb.jit.llvm/src/jit_runtime.c @@ -0,0 +1,1090 @@ +/*************************************************************************** + + jit_runtime.c + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __JIT_RUNTIME_C + +#include "gb_common.h" +#include "gbx_type.h" +#include "gbx_string.h" +//#include "gbx_object.h" +//#include "gbx_exec.h" +#include "gbx_number.h" + +#include "main.h" + +#include "jit_runtime.h" + +#define JR_STRING_unref(_p) \ +({ \ + char **pptr = _p; \ + char *ptr = *pptr; \ + STRING *str; \ + if (LIKELY(ptr != NULL)) \ + { \ + str = STRING_from_ptr(ptr); \ + if ((--str->ref) <= 0) \ + { \ + JIF.F_STRING_free_real(ptr); \ + *pptr = NULL; \ + } \ + } \ +}) + +#define JR_OBJECT_unref(_object) \ +{ \ + if (_object) \ + { \ + if ((--((OBJECT *)(_object))->ref) <= 0) \ + { \ + void *temp = (void *)(_object); \ + _object = NULL; \ + JIF.F_CLASS_free(temp); \ + } \ + } \ +} + +#define MANAGE_VARIANT(_func) \ +({ \ + if (P1->type == T_VARIANT) \ + JIF.F_VALUE_undo_variant(P1); \ + if (P2->type == T_VARIANT) \ + JIF.F_VALUE_undo_variant(P2); \ + \ + if (TYPE_is_string(P1->type)) \ + JIF.F_VALUE_convert_float(P1); \ + \ + if (TYPE_is_string(P2->type)) \ + JIF.F_VALUE_convert_float(P2); \ + \ + if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) \ + type = T_NULL; \ + else \ + type = Max(P1->type, P2->type); \ + \ + if (TYPE_is_number_date(type)) \ + { \ + (_func)(code | type); \ + JIF.F_VALUE_convert_variant(P1); \ + return; \ + } \ +}) + +#define MANAGE_VARIANT_POINTER(_func) \ +({ \ + if (P1->type == T_VARIANT) \ + JIF.F_VALUE_undo_variant(P1); \ + if (P2->type == T_VARIANT) \ + JIF.F_VALUE_undo_variant(P2); \ + \ + if (TYPE_is_string(P1->type)) \ + JIF.F_VALUE_convert_float(P1); \ + \ + if (TYPE_is_string(P2->type)) \ + JIF.F_VALUE_convert_float(P2); \ + \ + if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) \ + type = T_NULL; \ + else \ + type = Max(P1->type, P2->type); \ + \ + if (TYPE_is_number_date(type) || TYPE_is_pointer(type)) \ + { \ + (_func)(code | type); \ + JIF.F_VALUE_convert_variant(P1); \ + return; \ + } \ +}) + +static const char JR_EXEC_should_borrow[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, 0, 0 }; + +#define JR_RELEASE(_value) \ +do { \ + VALUE *_v = (_value); \ + TYPE type = _v->type; \ + if (TYPE_is_object(type)) \ + { \ + JR_OBJECT_unref(_v->_object.object); \ + } \ + else if (JR_EXEC_should_borrow[type]) \ + { \ + if (type == T_STRING) \ + JR_STRING_unref(&_v->_string.addr); \ + else \ + JIF.F_EXEC_release(type, _v); \ + } \ +} while (0) + +CLASS_DESC_METHOD *JR_CLASS_get_special_desc(CLASS *class, int spec) +{ + short index = class->special[spec]; + + if (index == NO_SYMBOL) + return NULL; + else + return &CLASS_get_desc(class, index)->method; +} + +typedef +union { + char _boolean; + unsigned char _byte; + short _short; + int _integer; + int64_t _long; + float _single; + double _float; + DATE _date; + char *_string; + void *_pointer; + void *_object; + int64_t data; + } +VARIANT_value; + +void JR_release_variant(long vtype, char* val){ + if (vtype == T_STRING) + JR_STRING_unref(&val); + else if (TYPE_is_object(vtype)) + JR_OBJECT_unref(val); +} + +void JR_borrow_variant(long vtype, char* val){ + if (vtype == T_STRING) + STRING_ref(val); + else if (TYPE_is_object(vtype)) + OBJECT_REF(val); +} + +static double JR_date_to_float(DATE dateval){ + double date = dateval.date; + double time = dateval.time; + return date + time / 86400000.0; +} + +static double JR_string_to_float(char* addr){ + VALUE val; + int len = STRING_from_ptr(addr)->len; + int res = JIF.F_NUMBER_from_string(NB_READ_FLOAT, addr, len, &val); + JR_STRING_unref(&addr); + if (res) + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(T_STRING)); + return val._float.value; +} + +void JR_aq_variant(int add){ + static void *_aq_jump[] = { + NULL, &&__AQ_BOOLEAN, &&__AQ_BYTE, &&__AQ_SHORT, &&__AQ_INTEGER, &&__AQ_LONG, &&__AQ_SINGLE, &&__AQ_FLOAT, + &&__AQ_DATE, &&__AQ_STRING, &&__AQ_STRING, &&__AQ_POINTER + }; + + TYPE NO_WARNING(type); + int NO_WARNING(value); + VALUE * NO_WARNING(P1); + void * NO_WARNING(jump_end); + + P1 = SP - 1; + + jump_end = &&__AQ_VARIANT_END; + JIF.F_VALUE_undo_variant(P1); + + type = P1->type; + value = add; + + if (LIKELY(type <= T_POINTER)) + goto *_aq_jump[type]; + + __AQ_BOOLEAN: + + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); + + __AQ_BYTE: + + P1->_integer.value = (unsigned char)(P1->_integer.value + value); + goto *jump_end; + + __AQ_SHORT: + + P1->_integer.value = (short)(P1->_integer.value + value); + goto *jump_end; + + __AQ_INTEGER: + + P1->_integer.value += value; + goto *jump_end; + + __AQ_LONG: + + P1->_long.value += (int64_t)value; + goto *jump_end; + + __AQ_SINGLE: + + P1->_single.value += (float)value; + goto *jump_end; + + __AQ_DATE: + __AQ_STRING: + + JIF.F_VALUE_convert_float(P1); + + __AQ_FLOAT: + + P1->_float.value += (double)value; + goto *jump_end; + + __AQ_POINTER: + + P1->_pointer.value += value; + goto *jump_end; + + __AQ_VARIANT_END: + + JIF.F_VALUE_convert_variant(P1); + +} + +void JR_variant_equal(){ + static void *jump[17] = { + &&__SC_VARIANT, &&__SC_BOOLEAN, &&__SC_BYTE, &&__SC_SHORT, &&__SC_INTEGER, &&__SC_LONG, &&__SC_SINGLE, &&__SC_FLOAT, &&__SC_DATE, + &&__SC_STRING, &&__SC_STRING, &&__SC_POINTER, &&__SC_ERROR, &&__SC_ERROR, &&__SC_ERROR, &&__SC_NULL, &&__SC_OBJECT + }; + + char NO_WARNING(result); + VALUE *NO_WARNING(P1); + VALUE *NO_WARNING(P2); + + P1 = SP - 2; + P2 = SP - 1; + + goto __SC_VARIANT; + +__SC_BOOLEAN: +__SC_BYTE: +__SC_SHORT: +__SC_INTEGER: + + result = P1->_integer.value == P2->_integer.value; + goto __SC_END; + +__SC_LONG: + + if (P1->type != T_LONG) + JIF.F_VALUE_convert(P1, T_LONG); + if (P2->type != T_LONG) + JIF.F_VALUE_convert(P2, T_LONG); + + result = P1->_long.value == P2->_long.value; + goto __SC_END; + +__SC_DATE: + + if (P1->type != T_DATE) + JIF.F_VALUE_convert(P1, T_DATE); + if (P2->type != T_DATE) + JIF.F_VALUE_convert(P2, T_DATE); + + result = JIF.F_DATE_comp((DATE *)&P1->_date.date, (DATE *)&P2->_date.date) == 0; + goto __SC_END; + +__SC_NULL: + + if (P2->type == T_NULL) + { + result = JIF.F_VALUE_is_null(P1); + goto __SC_END_RELEASE; + } + else if (P1->type == T_NULL) + { + result = JIF.F_VALUE_is_null(P2); + goto __SC_END_RELEASE; + } + +__SC_STRING: + + if (!TYPE_is_string(P1->type)) + JIF.F_VALUE_convert_string(P1); + if (!TYPE_is_string(P2->type)) + JIF.F_VALUE_convert_string(P2); + + if (P1->_string.len != P2->_string.len) + result = 0; + else + //result = STRING_equal_same(P1->_string.addr + P1->_string.start, P2->_string.addr + P2->_string.start, P1->_string.len); + result = memcmp(P1->_string.addr + P1->_string.start, P2->_string.addr + P2->_string.start, P1->_string.len) == 0; + + if (P1->type == T_STRING) + JR_STRING_unref(&P1->_string.addr); + if (P2->type == T_STRING) + JR_STRING_unref(&P2->_string.addr); + goto __SC_END; + +__SC_SINGLE: + + if (P1->type != T_SINGLE) + JIF.F_VALUE_convert(P1, T_SINGLE); + if (P2->type != T_SINGLE) + JIF.F_VALUE_convert(P2, T_SINGLE); + + result = P1->_single.value == P2->_single.value; + goto __SC_END; + +__SC_FLOAT: + + if (P1->type != T_FLOAT) + JIF.F_VALUE_convert_float(P1); + if (P2->type != T_FLOAT) + JIF.F_VALUE_convert_float(P2); + + result = P1->_float.value == P2->_float.value; + goto __SC_END; + +__SC_POINTER: + + if (P1->type != T_POINTER) + JIF.F_VALUE_convert(P1, T_POINTER); + if (P2->type != T_POINTER) + JIF.F_VALUE_convert(P2, T_POINTER); + + result = P1->_pointer.value == P2->_pointer.value; + goto __SC_END; + +__SC_OBJECT: + + result = JIF.F_OBJECT_comp_value(P1, P2) == 0; + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __SC_END_RELEASE; + +__SC_VARIANT: + + { + TYPE type; + + if (TYPE_is_variant(P1->type)) + { + JIF.F_VALUE_undo_variant(P1); + } + + if (TYPE_is_variant(P2->type)) + { + JIF.F_VALUE_undo_variant(P2); + } + + type = Max(P1->type, P2->type); + + if (TYPE_is_object_null(P1->type) && TYPE_is_object_null(P2->type)) + type = T_OBJECT; + else if (TYPE_is_object(type)) + THROW(E_TYPE, "Object", JIF.F_TYPE_get_name(Min(P1->type, P2->type))); + else if (TYPE_is_void(type)) + THROW(E_NRETURN); + + goto *jump[type]; + } + +__SC_ERROR: + + THROW(E_TYPE, "Something comparable", JIF.F_TYPE_get_name(T_VARIANT)); + +__SC_END_RELEASE: + + JR_RELEASE(P1); + JR_RELEASE(P2); + +__SC_END: + + P1->type = T_BOOLEAN; + SP--; + P1->_boolean.value = -result; +} + +void JR_variant_compi_less_than(void) +{ + static void *jump[17] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__ERROR, &&__ERROR, &&__ERROR, &&__NULL, &&__OBJECT + }; + + char NO_WARNING(result); + VALUE *P1; + VALUE *P2; + TYPE type; + + P1 = SP - 2; + P2 = P1 + 1; + + type = T_VARIANT; + goto __VARIANT; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + result = P1->_integer.value < P2->_integer.value ? -1 : 0; + goto __END; + +__LONG: + + JIF.F_VALUE_convert(P1, T_LONG); + JIF.F_VALUE_convert(P2, T_LONG); + + result = P1->_long.value < P2->_long.value ? -1 : 0; + goto __END; + +__DATE: + + JIF.F_VALUE_convert(P1, T_DATE); + JIF.F_VALUE_convert(P2, T_DATE); + + result = JIF.F_DATE_comp((DATE *)&P1->_date.date, (DATE *)&P2->_date.date) < 0 ? -1 : 0; + goto __END; + +__NULL: +__STRING: + + JIF.F_VALUE_convert_string(P1); + JIF.F_VALUE_convert_string(P2); + + result = JIF.F_STRING_compare(P1->_string.addr + P1->_string.start, P1->_string.len, P2->_string.addr + P2->_string.start, P2->_string.len) < 0 ? -1 : 0; + + if (P1->type == T_STRING) + JR_STRING_unref(&P1->_string.addr); + if (P2->type == T_STRING) + JR_STRING_unref(&P2->_string.addr); + goto __END; + +__SINGLE: + + JIF.F_VALUE_convert(P1, T_SINGLE); + JIF.F_VALUE_convert(P2, T_SINGLE); + + result = P1->_single.value < P2->_single.value ? -1 : 0; + goto __END; + +__FLOAT: + + JIF.F_VALUE_convert_float(P1); + JIF.F_VALUE_convert_float(P2); + + result = P1->_float.value < P2->_float.value ? -1 : 0; + goto __END; + +__POINTER: + + JIF.F_VALUE_convert(P1, T_POINTER); + JIF.F_VALUE_convert(P2, T_POINTER); + + result = P1->_pointer.value < P2->_pointer.value ? -1 : 0; + goto __END; + +__OBJECT: + + result = JIF.F_OBJECT_comp_value(P1, P2) < 0 ? -1 : 0; + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __END_RELEASE; + +__VARIANT: + + { + bool variant = FALSE; + + if (TYPE_is_variant(P1->type)) + { + JIF.F_VALUE_undo_variant(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + JIF.F_VALUE_undo_variant(P2); + variant = TRUE; + } + + type = Max(P1->type, P2->type); + + if (type == T_NULL || TYPE_is_string(type)) + { + TYPE typem = Min(P1->type, P2->type); + if (!TYPE_is_string(typem)) + THROW(E_TYPE, JIF.F_TYPE_get_name(typem), JIF.F_TYPE_get_name(type)); + } + else if (TYPE_is_object(type)) + goto __ERROR; + + goto *jump[type]; + } + +__ERROR: + + THROW(E_TYPE, "Number, Date or String", JIF.F_TYPE_get_name(type)); + +__END_RELEASE: + + JR_RELEASE(P1); + JR_RELEASE(P2); + +__END: + + P1->type = T_BOOLEAN; + P1->_boolean.value = result; + SP--; + return; +} +void JR_add(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, NULL, NULL, &&__POINTER + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x0F; + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_BOOLEAN; + P1->_integer.value = P1->_integer.value | P2->_integer.value; goto __END; + +__BYTE: + + P1->type = T_BYTE; + P1->_integer.value = (unsigned char)(P1->_integer.value + P2->_integer.value); goto __END; + +__SHORT: + + P1->type = T_SHORT; + P1->_integer.value = (short)(P1->_integer.value + P2->_integer.value); goto __END; + +__INTEGER: + + P1->type = T_INTEGER; + P1->_integer.value += P2->_integer.value; goto __END; + +__LONG: + + JIF.F_VALUE_convert(P1, T_LONG); + JIF.F_VALUE_convert(P2, T_LONG); + + P1->_long.value += P2->_long.value; goto __END; + +__SINGLE: + + JIF.F_VALUE_convert(P1, T_SINGLE); + JIF.F_VALUE_convert(P2, T_SINGLE); + + P1->_single.value += P2->_single.value; goto __END; + +__DATE: +__FLOAT: + + JIF.F_VALUE_convert_float(P1); + JIF.F_VALUE_convert_float(P2); + + P1->_float.value += P2->_float.value; + //fprintf(stderr, "+: %.24g\n", P1->_float.value); + goto __END; + +__POINTER: + + JIF.F_VALUE_convert(P1, T_POINTER); + JIF.F_VALUE_convert(P2, T_POINTER); + + P1->_pointer.value += (intptr_t)P2->_pointer.value; goto __END; + +__VARIANT: + + MANAGE_VARIANT_POINTER(JR_add); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); + +__END: + + SP--; +} + +void JR_sub(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, NULL, NULL, &&__POINTER + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x0F; + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_BOOLEAN; + P1->_integer.value = P1->_integer.value ^ P2->_integer.value; goto __END; + +__BYTE: + + P1->type = T_BYTE; + P1->_integer.value = (unsigned char)(P1->_integer.value - P2->_integer.value); goto __END; + +__SHORT: + + P1->type = T_SHORT; + P1->_integer.value = (short)(P1->_integer.value - P2->_integer.value); goto __END; + +__INTEGER: + + P1->type = T_INTEGER; + P1->_integer.value -= P2->_integer.value; goto __END; + +__LONG: + + JIF.F_VALUE_convert(P1, T_LONG); + JIF.F_VALUE_convert(P2, T_LONG); + + P1->_long.value -= P2->_long.value; goto __END; + +__SINGLE: + + JIF.F_VALUE_convert(P1, T_SINGLE); + JIF.F_VALUE_convert(P2, T_SINGLE); + + P1->_single.value -= P2->_single.value; goto __END; + +__DATE: +__FLOAT: + + JIF.F_VALUE_convert_float(P1); + JIF.F_VALUE_convert_float(P2); + + P1->_float.value -= P2->_float.value; goto __END; + +__POINTER: + + JIF.F_VALUE_convert(P1, T_POINTER); + JIF.F_VALUE_convert(P2, T_POINTER); + + P1->_pointer.value -= (intptr_t)P2->_pointer.value; goto __END; + +__VARIANT: + + MANAGE_VARIANT_POINTER(JR_sub); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); + +__END: + + SP--; +} + +void JR_mul(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x0F; + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_BOOLEAN; + P1->_integer.value = P1->_integer.value & P2->_integer.value; goto __END; + +__BYTE: + + P1->type = T_BYTE; + P1->_integer.value = (unsigned char)(P1->_integer.value * P2->_integer.value); goto __END; + +__SHORT: + + P1->type = T_SHORT; + P1->_integer.value = (short)(P1->_integer.value * P2->_integer.value); goto __END; + +__INTEGER: + + P1->type = T_INTEGER; + P1->_integer.value *= P2->_integer.value; goto __END; + +__LONG: + + JIF.F_VALUE_convert(P1, T_LONG); + JIF.F_VALUE_convert(P2, T_LONG); + + P1->_long.value *= P2->_long.value; goto __END; + +__SINGLE: + + JIF.F_VALUE_convert(P1, T_SINGLE); + JIF.F_VALUE_convert(P2, T_SINGLE); + + P1->_single.value *= P2->_single.value; goto __END; + +__FLOAT: + + JIF.F_VALUE_convert_float(P1); + JIF.F_VALUE_convert_float(P2); + + P1->_float.value *= P2->_float.value; + //fprintf(stderr, "*: %.24g\n", P1->_float.value); + goto __END; + +__VARIANT: + + MANAGE_VARIANT(JR_mul); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", JIF.F_TYPE_get_name(type)); + +__END: + + SP--; +} + +void JR_push_unknown_property_unknown(const char *name, int name_id, CLASS *class, void *object){ + bool call_static = object == NULL; + EXEC_unknown_name = name; + JIF.F_EXEC_special(SPEC_PROPERTY, class, class->property_static ? NULL : object, 0, FALSE); + if (class->unknown_static){ + JR_OBJECT_unref(object); + object = NULL; + } + if (SP[-1]._boolean.value){ + SP--; + EXEC_unknown_name = name; + JIF.F_EXEC_special(SPEC_UNKNOWN, class, object, 0, FALSE); + JIF.F_VALUE_convert_variant(&SP[-1]); + if (!call_static){ + SP[-2]._variant = SP[-1]._variant; + SP--; + } + JR_OBJECT_unref(object); + } else { + SP -= call_static ? 1 : 2; + SP->type = T_FUNCTION; + SP->_function.class = class; + SP->_function.object = object; + SP->_function.kind = FUNCTION_UNKNOWN; + SP->_function.index = name_id; + SP->_function.defined = FALSE; + SP++; + } +} + +void JR_pop_unknown_property_unknown(CLASS *class, void *object, const char *name){ + EXEC_unknown_name = name; + JIF.F_EXEC_special(SPEC_PROPERTY, class, object, 0, FALSE); + SP--; + if (!SP->_boolean.value) + THROW(E_NPROPERTY, class->name, name); + + EXEC_unknown_name = name; + + *SP = SP[-2]; + SP[-2].type = T_VOID; + SP++; + + JIF.F_EXEC_special(SPEC_UNKNOWN, class, object, 1, TRUE); + JR_OBJECT_unref(object); + + SP -= 2; +} + +void JR_call(int nparam){ + static const void *call_jump[] = + { + &&__CALL_NULL, &&__CALL_NATIVE, &&__CALL_PRIVATE, &&__CALL_PUBLIC, + NULL, &&__CALL_EXTERN, &&__CALL_UNKNOWN, &&__CALL_CALL, + NULL + }; + + VALUE * NO_WARNING(val); + + int ind = nparam; + val = &SP[-(ind + 1)]; + + if (UNLIKELY(!TYPE_is_function(val->type))) + { + bool defined = FALSE; + if (TYPE_is_variant(val->type)) + EXEC.class = JIF.F_EXEC_object_variant(val, (OBJECT **)&EXEC.object); + else + JIF.F_EXEC_object_other(val, &EXEC.class, (OBJECT **)&EXEC.object); + + val->type = T_FUNCTION; + val->_function.kind = FUNCTION_CALL; + val->_function.defined = defined; + val->_function.class = EXEC.class; + val->_function.object = EXEC.object; + //goto _CALL; + } + else + { + EXEC.class = val->_function.class; + EXEC.object = val->_function.object; + } + + EXEC.nparam = ind; + EXEC.use_stack = TRUE; + + if (UNLIKELY(!val->_function.defined)) + *PC |= CODE_CALL_VARIANT; + + goto *call_jump[(int)val->_function.kind]; + +__CALL_NULL: + + while (ind > 0) + { + SP--; + JR_RELEASE(SP); + ind--; + } + + SP--; + JR_RELEASE(SP); + + //if (!PCODE_is_void(code)) + { + /*VALUE_default(SP, (TYPE)(val->_function.function));*/ + SP->type = T_NULL; + SP++; + } + + return; + +__CALL_NATIVE: + + EXEC.native = TRUE; + EXEC.index = val->_function.index; + EXEC.desc = &EXEC.class->table[EXEC.index].desc->method; + //EXEC.use_stack = TRUE; + + goto __EXEC_NATIVE; + +__CALL_PRIVATE: + + EXEC.native = FALSE; + EXEC.index = val->_function.index; + + goto __EXEC_ENTER; + +__CALL_PUBLIC: + + EXEC.native = FALSE; + EXEC.desc = &EXEC.class->table[val->_function.index].desc->method; + EXEC.index = (int)(intptr_t)(EXEC.desc->exec); + EXEC.class = EXEC.desc->class; + + goto __EXEC_ENTER; + +__EXEC_ENTER: + + JIF.F_EXEC_enter(); + if (EXEC.class->load->func[EXEC.index].fast) + JR_EXEC_jit_execute_function(); + else + JIF.F_EXEC_function_loop(); + return; + +__EXEC_NATIVE: + + JIF.F_EXEC_native(); + return; + +__CALL_UNKNOWN: + + EXEC_unknown_name = CP->load->unknown[val->_function.index]; + EXEC.desc = JR_CLASS_get_special_desc(EXEC.class, SPEC_UNKNOWN); + //EXEC.use_stack = TRUE; + goto __CALL_SPEC; + +__CALL_CALL: + + EXEC.desc = JR_CLASS_get_special_desc(EXEC.class, SPEC_CALL); + if (UNLIKELY(!EXEC.desc && !EXEC.object && EXEC.nparam == 1 && !EXEC.class->is_virtual)) + { + SP[-2] = SP[-1]; + SP--; + JIF.F_VALUE_convert(SP - 1, (TYPE)EXEC.class); + return; + } + + goto __CALL_SPEC; + +__CALL_SPEC: + + if (UNLIKELY(!EXEC.desc)) + THROW(E_NFUNC); + + EXEC.native = FUNCTION_is_native(EXEC.desc); + + if (EXEC.native) + { + JIF.F_EXEC_native(); + return; + } + else + { + EXEC.index = (int)(intptr_t)(EXEC.desc->exec); + EXEC.class = EXEC.desc->class; + JIF.F_EXEC_enter(); + if (EXEC.class->load->func[EXEC.index].fast) + JR_EXEC_jit_execute_function(); + else + JIF.F_EXEC_function_loop(); + return; + } + +__CALL_EXTERN: + + EXEC.index = val->_function.index; + JIF.F_EXTERN_call(); + return; +} + +OBJECT* JR_object_cast(OBJECT* object, CLASS* target_class){ + CLASS* class = object->class; + if ((class == target_class) || JIF.F_CLASS_inherits(class, target_class)) + return object; + + if (class->has_convert){ + OBJECT* conv = ((OBJECT *(*)())(class->convert))(object, target_class); + if (conv){ + OBJECT_REF(conv); + JR_OBJECT_unref(object); + return conv; + } + } + + JR_OBJECT_unref(object); + + THROW(E_TYPE, JIF.F_TYPE_get_name((TYPE)(void*)target_class), JIF.F_TYPE_get_name((TYPE)(void*)class)); +} + +void* JR_extern_dispatch_object(OBJECT* object, int index){ + if (object == NULL) + THROW(E_NULL); + CLASS* class = object->class; + JR_OBJECT_unref(object); + return JIF.F_EXTERN_get_function_info(&class->load->ext[class->table[index].desc->ext.exec]).call; +} + +void JR_exec_enter_quick(CLASS* klass, void* object, int index){ + CLASS_DESC_METHOD* desc = &klass->table[index].desc->method; + EXEC.desc = desc; + EXEC.index = (int)(intptr_t)(desc->exec); + EXEC.class = EXEC.desc->class; + EXEC.object = object; + JIF.F_EXEC_enter_quick(); + if (FP->fast) + JR_EXEC_jit_execute_function(); + else + JIF.F_EXEC_function_loop(); +} +void JR_exec_enter(CLASS* klass, void* object, int index){ + CLASS_DESC_METHOD* desc = &klass->table[index].desc->method; + EXEC.desc = desc; + EXEC.index = (int)(intptr_t)(desc->exec); + EXEC.class = EXEC.desc->class; + EXEC.object = object; + JIF.F_EXEC_enter(); + if (FP->fast) + JR_EXEC_jit_execute_function(); + else + JIF.F_EXEC_function_loop(); +} + +void JR_EXEC_jit_execute_function(){ + (*CP->jit_functions[EXEC.index])(); +} + +/*void JR_call_native_or_public(CLASS* klass, void* object, int index, int nparam){ + CLASS_DESC_METHOD* desc = &klass->table[index].desc->method; + if (desc->native){ + if (desc->subr){ + } else { + EXEC.native = TRUE; + EXEC.index = index; + EXEC.desc = desc; + EXEC.class = klass; + EXEC.object = object; + EXEC.nparam = nparam; + } + } else { + EXEC.desc = desc; + EXEC.index = (int)(intptr_t)(desc->exec); + EXEC.class = EXEC.desc->class; + EXEC.object = object; + EXEC_enter(); + EXEC_jit_execute_function(); + } +}*/ +void* JR_try(ERROR_CONTEXT* err){ + ERROR_enter(err); + return err->env; +} +void JR_end_try(ERROR_CONTEXT* err){ + ERROR_leave(err); +} + +//FIXME Can this happen: Try native_call -> do non_native call -> non_native throw, directly back to first try? +//FIXME FIXME exec_function_keep måste ju sätta upp en try catch .. väl .. +void JR_try_unwind(VALUE* stack_start){ + JIF.F_ERROR_set_last(EP != NULL ? FALSE : TRUE); + + JIF.F_ERROR_lock(); + while(EC == NULL){ + JIF.F_EXEC_leave_drop(); + } + while(SP > stack_start){ + SP--; + JR_RELEASE(SP); + } + JIF.F_ERROR_unlock(); + + EP = NULL; +} diff --git a/gb.jit.llvm/src/jit_runtime.h b/gb.jit.llvm/src/jit_runtime.h new file mode 100644 index 00000000..e1cf9335 --- /dev/null +++ b/gb.jit.llvm/src/jit_runtime.h @@ -0,0 +1,64 @@ +/*************************************************************************** + + jit_runtime.h + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __JIT_RUNTIME_H +#define __JIT_RUNTIME_H + +#include "gambas.h" + +#ifdef __cplusplus +extern "C" { +#endif +void JR_release_variant(long vtype, char* data); +void JR_borrow_variant(long vtype, char* data); +void JR_aq_variant(int add); +void JR_variant_equal(void); +void JR_variant_compi_less_than(void); +void JR_add(ushort code); +void JR_sub(ushort code); +void JR_mul(ushort code); +void JR_call(int nparam); + +void JR_push_unknown_property_unknown(const char *name, int name_id, CLASS *klass, void *object); +void JR_pop_unknown_property_unknown(CLASS *klass, void *object, const char *name); + +void* JR_extern_dispatch_object(OBJECT* object, int index); + +void JR_exec_enter_quick(CLASS* klass, void* object, int index); +void JR_exec_enter(CLASS* klass, void* object, int index); +OBJECT* JR_object_cast(OBJECT* object, CLASS* target_class); + +void JR_EXEC_jit_execute_function(void); + +void* JR_try(ERROR_CONTEXT* err); +void JR_end_try(ERROR_CONTEXT* err); +void JR_try_unwind(VALUE* stack_start); + +CLASS_DESC_METHOD *JR_CLASS_get_special_desc(CLASS *klass, int spec); +#ifdef __cplusplus +} +#endif + +#endif /* __JIT_RUNTIME_H */ diff --git a/gb.jit.llvm/src/main.cpp b/gb.jit.llvm/src/main.cpp new file mode 100644 index 00000000..b191a215 --- /dev/null +++ b/gb.jit.llvm/src/main.cpp @@ -0,0 +1,78 @@ +/*************************************************************************** + + main.c + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gambas.h" +#include "main.h" +// Don't use if we just need the version +#include + +extern "C" { + GB_INTERFACE GB EXPORT; + bool MAIN_debug = false; +} + +void *GB_JIT_1[] EXPORT = { + (void *)1, + (void *)JIT_init, + (void *)JIT_compile_and_execute, + (void *)JIT_load_class, + NULL +}; + + +GB_DESC *GB_CLASSES[] EXPORT = +{ + NULL +}; + + +extern "C" int EXPORT GB_INIT(void) +{ + char *env; + + env = getenv("GB_JIT_DEBUG"); + if (!env || !*env) + env = getenv("GB_JIT"); + + if (env && env[0] && strcmp(env, "0") != 0) + MAIN_debug = true; + + if (LLVM_VERSION_MAJOR < 3 || (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 1)) + fprintf(stderr, "gb.jit: warning: LLVM %d.%d is not supported.\n", LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR); + else + { + if (MAIN_debug) + fprintf(stderr, "gb.jit: using LLVM %d.%d.\n", LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR); + } + + return 0; +} + +extern "C" void EXPORT GB_EXIT() +{ + JIT_end(); +} diff --git a/gb.jit.llvm/src/main.h b/gb.jit.llvm/src/main.h new file mode 100644 index 00000000..371ad401 --- /dev/null +++ b/gb.jit.llvm/src/main.h @@ -0,0 +1,257 @@ +/*************************************************************************** + + main.h + + gb.jit component + + (c) 2012 Emil Lenngren + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" + +#ifdef __cplusplus +#define class klass +#endif + +#include "gbx_value.h" +#include "gbx_stack.h" +#include "gbx_class.h" +#include "gbx_object.h" + +#ifdef __cplusplus +#undef class +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __MAIN_CPP +extern GB_INTERFACE GB; +extern bool MAIN_debug; +#endif + +typedef + struct { +#ifdef __cplusplus + CLASS *klass; +#else + CLASS *class; +#endif + OBJECT *object; + int index; + CLASS_DESC_METHOD *desc; + char nparam; + bool native; + bool use_stack; + } + EXEC_GLOBAL; + +/*typedef + struct { + char code; // Error code + char native; // A native method has raised an error + char free; // If 'msg' sould be freed + char _reserved; + void *cp; + void *fp; + void *pc; + char *msg; + } + ERROR_INFO; + +typedef + struct _ERROR_CONTEXT { + struct _ERROR_CONTEXT *prev; + struct _ERROR_HANDLER *handler; + ERROR_INFO info; + jmp_buf env; + char ret; + } + ERROR_CONTEXT; + +typedef + struct _ERROR_HANDLER { + struct _ERROR_HANDLER *prev; + ERROR_CONTEXT *context; + void (*handler)(); + } + ERROR_HANDLER; + +#define ERROR_LEAVE_DONE ((ERROR_CONTEXT *)-1)*/ + +#include "gb.jit.h" + +#ifndef __JIT_API_CPP +extern GB_JIT_INTERFACE JIF; +extern char **_STACK_limit; +extern STACK_CONTEXT *_EXEC_current; +extern VALUE **_SP; +extern VALUE *_TEMP; +extern VALUE *_RET; + +extern char *_GAMBAS_StopEvent; +extern char **_EXEC_enum; +extern EXEC_GLOBAL *_EXEC; +extern const char **_EXEC_unknown_name; +extern char *_EXEC_profile; +extern char *_EXEC_profile_instr; +extern unsigned char *_EXEC_quit_value; + +extern void **_EVENT_Last; + +extern ERROR_CONTEXT **_ERROR_current; +extern ERROR_HANDLER **_ERROR_handler; + +extern const char *_STRING_char_string; +#endif + +#ifdef __cplusplus +} +#endif + +#define STACK_limit (*_STACK_limit) +#define EXEC_current (*_EXEC_current) +#define SP (*_SP) +#define TEMP (*_TEMP) +#define RET (*_RET) + +#define GAMBAS_StopEvent (*_GAMBAS_StopEvent) +#define EXEC_enum (*_EXEC_enum) +#define EXEC (*_EXEC) +#define EXEC_unknown_name (*_EXEC_unknown_name) +#define EXEC_profile (*_EXEC_profile) +#define EXEC_profile_instr (*_EXEC_profile_instr) +#define EXEC_quit_value (*_EXEC_quit_value) + +#define EVENT_Last (*_EVENT_Last) + +#define ERROR_current (*_ERROR_current) +#define ERROR_handler (*_ERROR_handler) + +#define STRING_char_string (_STRING_char_string) + +// Local variables base pointer +#define BP EXEC_current.bp +// Arguments base pointer +#define PP EXEC_current.pp +// Current class +#define CP EXEC_current.cp +// Current object +#define OP EXEC_current.op +// Save stack pointer for a TRY +#define EP EXEC_current.ep +// Current function +#define FP EXEC_current.fp +// Program counter +#define PC EXEC_current.pc +// Where to go if there is an error +#define EC EXEC_current.ec +// Save register for TRY +#define ET EXEC_current.et +// Last break in the function +#define TC EXEC_current.tc +// Stack at the last break in the function +#define TP EXEC_current.tp +// GoSub stack +#define GP EXEC_current.gp + +// Function return value pointer +#define RP (&RET) + +#define THROW JIF.F_THROW +#define THROW_ILLEGAL() THROW(E_ILLEGAL) +#define SYMBOL_find JIF.F_SYMBOL_find + +#undef TRY +#undef CATCH +#undef END_TRY +#undef ERROR_enter +#undef ERROR_leave + +#define TRY \ + { \ + ERROR_CONTEXT __err_context; \ + { \ + ERROR_CONTEXT *__err = &__err_context; \ + ERROR_enter(__err); \ + __err->ret = setjmp(__err->env); \ + if (__err->ret == 0) + +#define CATCH \ + else + +#define END_TRY \ + ERROR_leave(__err); \ + } \ + } + + +#define ERROR_enter(_err) \ +do { \ + _err->prev = ERROR_current; \ + _err->info.code = 0; \ + _err->info.native = 0; \ + _err->handler = ERROR_handler; \ + ERROR_current = _err; \ +} while(0) + +#define ERROR_leave(_err) \ +do { \ + ERROR_CONTEXT *_prev = (_err); \ + if (_prev->prev != ERROR_LEAVE_DONE) \ + { \ + ERROR_current = _prev->prev; \ + if (ERROR_current) \ + { \ + if (_prev->info.code) \ + { \ + JIF.F_ERROR_reset(&ERROR_current->info); \ + ERROR_current->info = _prev->info; \ + ERROR_current->info.native = FALSE; \ + } \ + } \ + else \ + JIF.F_ERROR_reset(&_prev->info); \ + _prev->prev = ERROR_LEAVE_DONE; \ + } \ +} while(0) + + +#ifdef __cplusplus +extern "C" { +#endif + +void JIT_init(GB_JIT_INTERFACE *jif, char **__STACK_limit, STACK_CONTEXT *__EXEC_current, VALUE **__SP, VALUE *__TEMP, + VALUE *__RET, char *__GAMBAS_StopEvent, char ** __EXEC_enum, EXEC_GLOBAL *__EXEC, + const char **__EXEC_unknown_name, char *__EXEC_profile, char *__EXEC_profile_instr, unsigned char *__EXEC_quit_value, + void **__EVENT_Last, ERROR_CONTEXT **__ERROR_current, ERROR_HANDLER **__ERROR_handler, const char *__STRING_char_string); +void JIT_compile_and_execute(void); +void JIT_end(void); +void JIT_load_class(CLASS *klass); + +#ifdef __cplusplus +} +#endif + +#endif /* __MAIN_H */ diff --git a/gb.libxml/AUTHORS b/gb.libxml/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.libxml/COPYING b/gb.libxml/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.libxml/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.libxml/ChangeLog b/gb.libxml/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.libxml/INSTALL b/gb.libxml/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.libxml/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.libxml/Makefile.am b/gb.libxml/Makefile.am new file mode 100644 index 00000000..beea2ef3 --- /dev/null +++ b/gb.libxml/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @XML_DIR@ +EXTRA_DIST = reconf spec gambas.h diff --git a/gb.libxml/NEWS b/gb.libxml/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.libxml/README b/gb.libxml/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.libxml/acinclude.m4 b/gb.libxml/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.libxml/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.libxml/component.am b/gb.libxml/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.libxml/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.libxml/configure.ac b/gb.libxml/configure.ac new file mode 100644 index 00000000..7c62c5e4 --- /dev/null +++ b/gb.libxml/configure.ac @@ -0,0 +1,19 @@ +dnl ---- configure.ac for gb.xml + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-libxml, GB_VERSION, GB_MAIL, [], GB_URL) + +AC_CONFIG_MACRO_DIR([m4]) +GB_INIT(gb.libxml) +AC_PROG_LIBTOOL + +GB_COMPONENT_PKG_CONFIG( + xml, XML, gb.libxml, [src], + libxml-2.0) + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.libxml/gambas.h b/gb.libxml/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.libxml/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.libxml/m4 b/gb.libxml/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.libxml/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.libxml/reconf b/gb.libxml/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.libxml/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.libxml/src/CXMLDocument.c b/gb.libxml/src/CXMLDocument.c new file mode 100644 index 00000000..5de5545e --- /dev/null +++ b/gb.libxml/src/CXMLDocument.c @@ -0,0 +1,174 @@ +/*************************************************************************** + + CXMLDocument.c + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CXMLDOCUMENT_C + +#include +#include +#include +#include +#include "main.h" +#include "CXMLDocument.h" + +CXMLNODE *XML_CreateNode(CXMLDOCUMENT *doc, xmlNode *node) +{ + CXMLNODE *p; + + if (!node) + return NULL; + + p = GB.New(GB.FindClass("XmlNode"), NULL, NULL); + p->node = node; + p->doc = doc; + GB.Ref(doc); + return p; +} + +static void free_document(CXMLDOCUMENT *_object) +{ + if (THIS->doc) + { + xmlFreeDoc(THIS->doc); + THIS->doc = NULL; + } +} + + +void XML_InitDocument(CXMLDOCUMENT *_object, xmlDoc *doc, const char *err) +{ + if (!doc) + { + GB.Error(err ? err : "Unable to parse XML file"); + return; + } + + free_document(THIS); + THIS->doc = doc; +} + + +BEGIN_METHOD_VOID (CXMLDocument_Free) + + free_document(THIS); + +END_METHOD + + +BEGIN_METHOD (CXMLDocument_Open,GB_STRING FileName;) + + const char *path; + + path = GB.RealFileName(STRING(FileName), LENGTH(FileName)); + XML_InitDocument(THIS, xmlParseFile(path), NULL); + +END_METHOD + +BEGIN_METHOD (CXMLDocument_FromString,GB_STRING Data;) + + XML_InitDocument(THIS, xmlParseDoc((xmlChar *)GB.ToZeroString(ARG(Data))), NULL); + +END_METHOD + +BEGIN_METHOD (CXMLDocument_HtmlFromString,GB_STRING Data;) + + XML_InitDocument(THIS, htmlParseDoc((xmlChar *)GB.ToZeroString(ARG(Data)),NULL), NULL); + +END_METHOD + + +BEGIN_METHOD (CXMLDocument_Write,GB_STRING FileName; GB_STRING Encoding;) + + char *enc; + + if (!THIS->doc) + { + GB.Error("Unable to write NULL document"); + return; + } + + if (MISSING(Encoding)) + enc="UTF-8"; + else + enc=GB.ToZeroString(ARG(Encoding)); + + xmlSaveFormatFileEnc(GB.ToZeroString(ARG(FileName)), THIS->doc,enc , 1); + +END_METHOD + +BEGIN_METHOD(CXMLDocument_ToString, GB_STRING Encoding) + + xmlChar *mem; + int size; + char *encoding; + + if (!THIS->doc) + { + GB.ReturnVoidString(); + return; + } + + if (MISSING(Encoding)) + encoding = "UTF-8"; + else + encoding = GB.ToZeroString(ARG(Encoding)); + + xmlDocDumpFormatMemoryEnc(THIS->doc, &mem, &size, encoding, 1); + + GB.ReturnNewString((char*)mem,size); + xmlFree(mem); + +END_METHOD + +BEGIN_PROPERTY (CXMLDocument_Root) + + GB.ReturnObject(XML_CreateNode(THIS, xmlDocGetRootElement(THIS->doc))); + +END_PROPERTY + + +GB_DESC CXmlDocumentDesc[] = +{ + + GB_DECLARE("XmlDocument", sizeof(CXMLDOCUMENT)), + + GB_PROPERTY_READ("Root","XmlNode",CXMLDocument_Root), + + GB_METHOD("_free",NULL,CXMLDocument_Free,NULL), + + GB_METHOD("Open", NULL, CXMLDocument_Open, "(FileName)s"), + GB_METHOD("FromString", NULL, CXMLDocument_FromString, "(Data)s"), + GB_METHOD("HtmlFromString", NULL, CXMLDocument_HtmlFromString, "(Data)s"), + GB_METHOD("Write", NULL, CXMLDocument_Write, "(FileName)s[(Encoding)s]"), + //GB_METHOD("Write",NULL,CXMLDocument_Write,"(FileName)s[(Encoding)s]"), + GB_METHOD("ToString", "s", CXMLDocument_ToString, "[(Encoding)s]"), + + + + GB_END_DECLARE +}; + + + + + + diff --git a/gb.libxml/src/CXMLDocument.h b/gb.libxml/src/CXMLDocument.h new file mode 100644 index 00000000..2d8f5a52 --- /dev/null +++ b/gb.libxml/src/CXMLDocument.h @@ -0,0 +1,54 @@ +/*************************************************************************** + + CXMLDocument.h + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CXMLDOCUMENT_H +#define __CXMLDOCUMENT_H + +#include +#include +#include "gambas.h" +#include "CXMLNode.h" + +#ifndef __CXMLDOCUMENT_C + +extern GB_DESC CXmlDocumentDesc[]; + +#else + +#define THIS ((CXMLDOCUMENT *)_object) + +#endif + +typedef + struct _CXMLDOCUMENT + { + GB_BASE ob; + xmlDoc *doc; + } + CXMLDOCUMENT; + +CXMLNODE *XML_CreateNode(CXMLDOCUMENT *doc, xmlNode *node); +void XML_InitDocument(CXMLDOCUMENT *_object, xmlDoc *doc, const char *err); + +#endif + diff --git a/gb.libxml/src/CXMLNode.c b/gb.libxml/src/CXMLNode.c new file mode 100644 index 00000000..5da94bc0 --- /dev/null +++ b/gb.libxml/src/CXMLNode.c @@ -0,0 +1,300 @@ +/*************************************************************************** + + CXMLNode.c + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CXMLNODE_C + +#include +#include +#include "main.h" +#include "CXMLDocument.h" +#include "CXMLNode.h" + +/*int CXMLNode_check(void *_object) +{ + return !THIS->parent; +}*/ + + +BEGIN_PROPERTY(CXMLNode_Next) + + if (!THIS->node->next) + GB.ReturnNull(); + else + GB.ReturnObject(XML_CreateNode(THIS->doc, THIS->node->next)); + +END_PROPERTY + +BEGIN_PROPERTY(CXMLNode_Prev) + + if (!THIS->node->prev) + GB.ReturnNull(); + else + GB.ReturnObject(XML_CreateNode(THIS->doc, THIS->node->prev)); + +END_PROPERTY + +BEGIN_PROPERTY(CXMLNode_Parent) + + if (!THIS->node->parent) + GB.ReturnNull(); + else + GB.ReturnObject(XML_CreateNode(THIS->doc, THIS->node->parent)); + +END_PROPERTY + + +BEGIN_PROPERTY(CXMLNode_Name) + + if (READ_PROPERTY) + GB.ReturnNewZeroString((const char *)THIS->node->name); + else + xmlNodeSetName(THIS->node, (const xmlChar *)GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +BEGIN_PROPERTY(CXMLNode_Value) + + if (READ_PROPERTY) + GB.ReturnNewZeroString((const char *)xmlNodeGetContent(THIS->node)); + else + fprintf(stderr, "*NOT IMPLEMENTED*"); + +END_PROPERTY + + +BEGIN_PROPERTY(CXmlNode_c_count) + + int nval=0; + xmlNode *ch; + + ch=THIS->node->children; + + while(ch) + { + nval++; + ch=ch->next; + } + + GB.ReturnInteger(nval); + +END_METHOD + +/*************************************************************** + "Creating" functions + *************************************************************/ +BEGIN_METHOD(CXMLNode_AddAttr,GB_STRING Name; GB_STRING Value) + + char *name,*value; + + name=GB.ToZeroString(ARG(Name)); + value=GB.ToZeroString(ARG(Value)); + + if (!xmlNewProp(THIS->node,BAD_CAST name,BAD_CAST value)) + GB.Error("Unable to add XML Attribute"); + +END_METHOD + +BEGIN_METHOD(CXMLNode_AddElement,GB_STRING Name; GB_STRING Value) + + char *name,*value; + + name=GB.ToZeroString(ARG(Name)); + value=GB.ToZeroString(ARG(Value)); + + + if (!xmlNewChild(THIS->node, NULL, BAD_CAST name, BAD_CAST value)) + GB.Error("Unable to add XML Element"); + +END_METHOD + +/************************************************************** + NODE CHILDREN +***************************************************************/ +BEGIN_METHOD(CXmlNode_c_get,GB_INTEGER Element) + + int nval=0; + int nMax; + xmlNode *ch; + + nMax = VARG(Element); + + ch = THIS->node->children; + + if (!ch) + { + GB.Error("Out of bounds"); + return; + } + + for (nval=0;nvalnext; + if (!ch) break; + } + + if (!ch) + { + GB.Error("Out of bounds"); + return; + } + + GB.ReturnObject(XML_CreateNode(THIS->doc, ch)); + +END_METHOD + +/****************************************************************** + NODE ATTRIBUTES +*******************************************************************/ + +BEGIN_METHOD_VOID(CXmlNode_a_next) + + xmlNodePtr attr; + int bucle; + long *wenum; + + wenum=(long*)GB.GetEnum(); + + attr=(xmlNodePtr)THIS->node->properties; + + if (!attr) + { + GB.StopEnum(); + return; + } + + for(bucle=0;bucle<(*wenum);bucle++) + { + attr=attr->next; + if (!attr) + { + GB.StopEnum(); + return; + } + } + + (*wenum)++; + + GB.ReturnObject(XML_CreateNode(THIS->doc, attr)); + +END_METHOD + +BEGIN_PROPERTY(CXmlNode_a_count) + + int nval=0; + xmlAttr *ch; + + ch=THIS->node->properties; + + while(ch) + { + nval++; + ch=ch->next; + } + + GB.ReturnInteger(nval); + +END_METHOD + + +BEGIN_METHOD_VOID(CXMLNode_Free) + + GB.Unref(POINTER(&THIS->doc)); + +END_METHOD + +GB_DESC CXmlNodeChildrenDesc[] = +{ + GB_DECLARE(".XmlNode.Children", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", "XmlNode",CXmlNode_c_get, "(Element)i"), + GB_PROPERTY_READ("Count", "i", CXmlNode_c_count), + + GB_END_DECLARE +}; + + +GB_DESC CXmlNodeAttributesDesc[] = +{ + GB_DECLARE(".XmlNode.Attributes", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_next", "XmlNode", CXmlNode_a_next, NULL), + GB_PROPERTY_READ("Count", "i", CXmlNode_a_count), + + GB_END_DECLARE +}; + + + +GB_DESC CXmlNodeDesc[] = +{ + GB_DECLARE("XmlNode", sizeof(CXMLNODE)), GB_NOT_CREATABLE(), + + GB_NOT_CREATABLE(), + //GB_HOOK_CHECK(CXMLNode_check), + + GB_CONSTANT("ElementNode", "i", XML_ELEMENT_NODE), + GB_CONSTANT("AttributeNode", "i", XML_ATTRIBUTE_NODE), + GB_CONSTANT("TextNode", "i", XML_TEXT_NODE), + GB_CONSTANT("CDataSectionNode", "i", XML_CDATA_SECTION_NODE), + GB_CONSTANT("EntityRefNode", "i", XML_ENTITY_REF_NODE), + GB_CONSTANT("EntityNode", "i", XML_ENTITY_NODE), + GB_CONSTANT("PiNode", "i", XML_PI_NODE), + GB_CONSTANT("CommentNode", "i", XML_COMMENT_NODE), + GB_CONSTANT("DocumentNode", "i", XML_DOCUMENT_NODE), + GB_CONSTANT("DocumentTypeNode", "i", XML_DOCUMENT_TYPE_NODE), + GB_CONSTANT("DocumentFragNode", "i", XML_DOCUMENT_FRAG_NODE), + GB_CONSTANT("NotationNode", "i", XML_NOTATION_NODE), + GB_CONSTANT("HtmlDocumentNode", "i", XML_HTML_DOCUMENT_NODE), + GB_CONSTANT("DtdNode", "i", XML_DTD_NODE), + GB_CONSTANT("ElementDecl", "i", XML_ELEMENT_DECL), + GB_CONSTANT("AttributeDecl", "i", XML_ATTRIBUTE_DECL), + GB_CONSTANT("EntityDecl", "i", XML_ENTITY_DECL), + GB_CONSTANT("NamespaceDecl", "i", XML_NAMESPACE_DECL), + GB_CONSTANT("XIncludeStart", "i", XML_XINCLUDE_START), + GB_CONSTANT("XIncludeEnd", "i", XML_XINCLUDE_END), + GB_CONSTANT("DocbDocumentNode", "i", XML_DOCB_DOCUMENT_NODE), + + GB_PROPERTY("Name","s",CXMLNode_Name), + GB_PROPERTY("Value","s",CXMLNode_Value), + + GB_PROPERTY_READ("Parent","XmlNode",CXMLNode_Parent), + GB_PROPERTY_READ("Next","XmlNode",CXMLNode_Next), + GB_PROPERTY_READ("Previous","XmlNode",CXMLNode_Prev), + GB_PROPERTY_SELF("Children",".XmlNode.Children"), + GB_PROPERTY_SELF("Attributes",".XmlNode.Attributes"), + + GB_METHOD("NewAttribute",NULL,CXMLNode_AddAttr,"(Name)s(Value)s"), + GB_METHOD("NewElement",NULL,CXMLNode_AddElement,"(Name)s(Value)s"), + + GB_METHOD("_free",NULL,CXMLNode_Free,NULL), + + GB_END_DECLARE +}; + + + + + + + + diff --git a/gb.libxml/src/CXMLNode.h b/gb.libxml/src/CXMLNode.h new file mode 100644 index 00000000..8232528c --- /dev/null +++ b/gb.libxml/src/CXMLNode.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + CXMLNode.h + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CXMLNODE_H +#define __CXMLNODE_H + +#include +#include "gambas.h" + +#ifndef __CXMLNODE_C + + +extern GB_DESC CXmlNodeAttributesDesc[]; +extern GB_DESC CXmlNodeChildrenDesc[]; +extern GB_DESC CXmlNodeDesc[]; + +#else + +#define THIS ((CXMLNODE *)_object) + +#endif + +typedef + struct + { + GB_BASE ob; + xmlNode *node; + struct _CXMLDOCUMENT *doc; + } + CXMLNODE; + +#endif diff --git a/gb.libxml/src/CXMLReader.c b/gb.libxml/src/CXMLReader.c new file mode 100644 index 00000000..d0104f58 --- /dev/null +++ b/gb.libxml/src/CXMLReader.c @@ -0,0 +1,517 @@ +/*************************************************************************** + + CXMLReader.c + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CXMLREADER_C + +#include +#include +#include +#include +#include "main.h" +#include "CXMLReader.h" + +unsigned char b64value(char car) +{ + if ( (car>=65) && (car<=90) ) return car-65; + if ( (car>=97) && (car<=122) ) return car-71; + if ( (car>=48) && (car<=57) ) return car+4; + if (car=='+') return 62; + if (car=='/') return 63; + if (car=='=') return 254; + return 255; + +} + +void FromBinHex(char *src,char *dst) +{ + char b; + unsigned long bucle; + int zone=0; + + for (bucle=0;bucle>4); + dst[retval-2]=car<<4; + zone++; + break; + case 2: + dst[retval-2]+=car>>2; + dst[retval-1]=car<<6; + zone++; + break; + case 3: + dst[retval-1]+=car; + zone=0; + break; + case 4: + return retval; + } + } + } + + return retval-pads; +} + + +int Check_Reader(CXMLREADER *test) +{ + if (!test->reader) + { + GB.Error("No XML file or string to read from"); + return 1; + } + + if (test->eof) + { + GB.Error("Reached end of file"); + return 1; + } + + return 0; +} + +void Free_Reader(CXMLREADER *test) +{ + if (test->buffer)GB.Free(POINTER(&test->buffer)); + if (test->reader) + { + xmlTextReaderClose(test->reader); + xmlFreeTextReader(test->reader); + test->reader=NULL; + } + test->eof=0; +} + +BEGIN_METHOD (CXmlReader_Open,GB_STRING FileName) + + Free_Reader(THIS); + THIS->reader=xmlReaderForFile(GB.ToZeroString(ARG(FileName)),NULL,0); + if (!THIS->reader) GB.Error ("Unable to parse XML file"); + + +END_METHOD + +BEGIN_METHOD (CXmlReader_FromString,GB_STRING Buffer;GB_STRING URL;) + + if (!LENGTH(Buffer)) + { + GB.Error ("Unable to parse NULL string"); + return; + } + + Free_Reader(THIS); + + GB.Alloc(POINTER(&THIS->buffer),LENGTH(Buffer)); + memcpy (THIS->buffer,STRING(Buffer),LENGTH(Buffer)); + + if (!MISSING(URL)) + THIS->reader=xmlReaderForMemory(THIS->buffer,LENGTH(Buffer),GB.ToZeroString(ARG(URL)),NULL,0); + else + THIS->reader=xmlReaderForMemory(THIS->buffer,LENGTH(Buffer),"",NULL,0); + + if (!THIS->reader) GB.Error ("Unable to parse XML file"); + + +END_METHOD + +BEGIN_METHOD_VOID (CXmlReader_Read) + + int retval; + + if (Check_Reader(THIS)) return; + + retval=xmlTextReaderRead(THIS->reader); + switch(retval) + { + case 0: + THIS->eof=1; + break; + + case -1: + Free_Reader(THIS); + GB.Error("Error parsing XML file"); + break; + } + + +END_METHOD + +BEGIN_METHOD (CXmlReader_Decode,GB_STRING Data;GB_STRING Encoding;) + + char *dst=NULL; + unsigned long len; + + if (!strcasecmp(GB.ToZeroString(ARG(Encoding)),"base64") ) { + if (!LENGTH(Data)) return; + + GB.Alloc (POINTER(&dst),LENGTH(Data) ); + len=FromBase64(GB.ToZeroString(ARG(Data)),dst); + GB.ReturnNewString(dst,len); + GB.Free(POINTER(&dst)); + return; + } + + if (!strcasecmp(GB.ToZeroString(ARG(Encoding)),"binhex") ) { + if (!LENGTH(Data)) return; + if (LENGTH(Data)%2) return; + + dst=STRING(Data); + for(len=0;lenreader); + else eval=xmlTextReaderMoveToNextAttribute(THIS->reader); + + if (eval==-1) + { + xmlFreeTextReader(THIS->reader); + THIS->reader=NULL; + GB.StopEnum(); + GB.Error("Error parsing XML file"); + return; + } + + if (!eval) + { + if (wenum[0]) xmlTextReaderMoveToElement(THIS->reader); + GB.StopEnum(); + return; + } + + wenum[0]=1; + RETURN_SELF(); + + + +END_METHOD + +BEGIN_PROPERTY(CXmlReader_count) + + int nval; + + if (Check_Reader(THIS)) return; + + nval=xmlTextReaderAttributeCount(THIS->reader); + + if (nval==-1) + { + xmlFreeTextReader(THIS->reader); + THIS->reader=NULL; + GB.Error("Error parsing XML file"); + return; + } + + GB.ReturnInteger(nval); + +END_METHOD + +BEGIN_PROPERTY(CXMLReader_EOF) + + if (!THIS->reader) + { + GB.ReturnBoolean(1); + return; + } + + GB.ReturnBoolean(THIS->eof); + +END_PROPERTY + + +BEGIN_PROPERTY(CRNODE_BaseUri) + + if (Check_Reader(THIS)) return; + GB.ReturnNewZeroString((const char *)xmlTextReaderBaseUri(THIS->reader)); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_Depth) + + if (Check_Reader(THIS)) return; + GB.ReturnInteger( xmlTextReaderDepth(THIS->reader)); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_IsDefault) + + if (Check_Reader(THIS)) return; + GB.ReturnBoolean(xmlTextReaderIsDefault(THIS->reader)); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_IsEmptyElement) + + if (Check_Reader(THIS)) return; + GB.ReturnBoolean(xmlTextReaderIsEmptyElement(THIS->reader)); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_LocalName) + + if (Check_Reader(THIS)) return; + GB.ReturnNewZeroString((const char *)xmlTextReaderLocalName(THIS->reader)); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_Name) + + if (Check_Reader(THIS)) return; + GB.ReturnNewZeroString((const char *)xmlTextReaderName(THIS->reader)); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_NamespaceUri) + + if (Check_Reader(THIS)) return; + GB.ReturnNewZeroString((const char *)xmlTextReaderNamespaceUri(THIS->reader)); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_Prefix) + + if (Check_Reader(THIS)) return; + GB.ReturnNewZeroString((const char *)xmlTextReaderPrefix(THIS->reader)); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_QuoteChar) + + char car='\"'; + + if (Check_Reader(THIS)) return; + + car=(char)xmlTextReaderQuoteChar(THIS->reader); + GB.ReturnNewString(&car,1); + +END_PROPERTY + + +BEGIN_PROPERTY(CRNODE_Value) + + char *buf; + + if (Check_Reader(THIS)) return; + + buf=(char *)xmlTextReaderValue(THIS->reader); + GB.ReturnNewZeroString(buf); + if (buf) xmlFree(buf); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_Type) + + if (Check_Reader(THIS)) return; + GB.ReturnInteger( xmlTextReaderNodeType(THIS->reader)); + +END_PROPERTY + +BEGIN_PROPERTY(CRNODE_XmlLang) + + if (Check_Reader(THIS)) return; + GB.ReturnNewZeroString((char *)xmlTextReaderXmlLang(THIS->reader)); + +END_PROPERTY + + +GB_DESC CXmlReaderNodeTypeDesc[] = +{ + GB_DECLARE("XmlReaderNodeType", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", XML_READER_TYPE_NONE), + GB_CONSTANT("Element", "i", XML_READER_TYPE_ELEMENT), + GB_CONSTANT("Attribute", "i", XML_READER_TYPE_ATTRIBUTE), + GB_CONSTANT("Text", "i",XML_READER_TYPE_TEXT), + GB_CONSTANT("CDATA", "i", XML_READER_TYPE_CDATA), + GB_CONSTANT("EntityReference", "i", XML_READER_TYPE_ENTITY_REFERENCE), + GB_CONSTANT("Entity", "i",XML_READER_TYPE_ENTITY), + GB_CONSTANT("ProcessingInstruction", "i",XML_READER_TYPE_PROCESSING_INSTRUCTION), + GB_CONSTANT("Comment", "i", XML_READER_TYPE_COMMENT), + GB_CONSTANT("Document", "i", XML_READER_TYPE_DOCUMENT), + GB_CONSTANT("DocumentType", "i", XML_READER_TYPE_DOCUMENT_TYPE), + GB_CONSTANT("DocumentFragment", "i", XML_READER_TYPE_DOCUMENT_FRAGMENT), + GB_CONSTANT("Notation", "i", XML_READER_TYPE_NOTATION), + GB_CONSTANT("Whitespace", "i",XML_READER_TYPE_WHITESPACE), + GB_CONSTANT("SignificantWhitespace", "i",XML_READER_TYPE_SIGNIFICANT_WHITESPACE), + GB_CONSTANT("EndElement", "i", XML_READER_TYPE_END_ELEMENT), + GB_CONSTANT("EndEntity", "i", XML_READER_TYPE_END_ENTITY), + GB_CONSTANT("XmlDeclaration", "i",XML_READER_TYPE_XML_DECLARATION), + + GB_END_DECLARE +}; + +GB_DESC CXmlReaderNodeAttributesDesc[] = +{ + GB_DECLARE(".XmlReader.Node.Attributes", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_next", ".XmlReader.Node", CXmlReader_next, NULL), + GB_PROPERTY_READ("Count", "i", CXmlReader_count), + + GB_END_DECLARE +}; + +GB_DESC CXmlReaderNodeDesc[] = +{ + GB_DECLARE(".XmlReader.Node", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Attributes", ".XmlReader.Node.Attributes",CXMLReader_Node), + GB_PROPERTY_READ("BaseUri","s",CRNODE_BaseUri), + GB_PROPERTY_READ("Depth","i",CRNODE_Depth), + GB_PROPERTY_READ("IsDefault","b",CRNODE_IsDefault), + GB_PROPERTY_READ("IsEmptyElement","b",CRNODE_IsEmptyElement), + GB_PROPERTY_READ("LocalName","s",CRNODE_LocalName), + GB_PROPERTY_READ("Name", "s", CRNODE_Name), + GB_PROPERTY_READ("NamespaceUri", "s", CRNODE_NamespaceUri), + GB_PROPERTY_READ("Prefix", "s", CRNODE_Prefix), + GB_PROPERTY_READ("QuoteChar", "s", CRNODE_QuoteChar), + GB_PROPERTY_READ("Type","i",CRNODE_Type), + GB_PROPERTY_READ("Value", "s", CRNODE_Value), + GB_PROPERTY_READ("XmlLang", "s", CRNODE_XmlLang), + + GB_END_DECLARE +}; + +GB_DESC CXmlReaderDesc[] = +{ + GB_DECLARE("XmlReader", sizeof(CXMLREADER)), + + GB_STATIC_METHOD("Decode","s",CXmlReader_Decode,"(Data)s(Encoding)s"), + + GB_METHOD("_free",NULL,CXmlReader_Free,NULL), + + GB_METHOD("Open", NULL, CXmlReader_Open,"(FileName)s"), + GB_METHOD("Close",NULL,CXmlReader_Free,NULL), + GB_METHOD("FromString",NULL,CXmlReader_FromString,"(Buffer)s[(URL)s]"), + + GB_METHOD("Read",NULL,CXmlReader_Read,NULL), + + GB_PROPERTY_READ("Eof","b",CXMLReader_EOF), + GB_PROPERTY_READ("Node",".XmlReader.Node",CXMLReader_Node), + + GB_END_DECLARE +}; + + + + diff --git a/gb.libxml/src/CXMLReader.h b/gb.libxml/src/CXMLReader.h new file mode 100644 index 00000000..0c38e214 --- /dev/null +++ b/gb.libxml/src/CXMLReader.h @@ -0,0 +1,54 @@ +/*************************************************************************** + + CXMLReader.h + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CXMLREADER_H +#define __CXMLREADER_H + +#include "gambas.h" +#include + +#ifndef __CXMLREADER_C + +extern GB_DESC CXmlReaderNodeTypeDesc[]; +extern GB_DESC CXmlReaderNodeDesc[]; +extern GB_DESC CXmlReaderNodeAttributesDesc[]; +extern GB_DESC CXmlReaderDesc[]; + +#else + +#define THIS ((CXMLREADER *)_object) + +#endif + + +typedef struct +{ + GB_BASE ob; + xmlTextReaderPtr reader; + char *buffer; + int eof; + +} CXMLREADER; + + +#endif diff --git a/gb.libxml/src/CXMLWriter.c b/gb.libxml/src/CXMLWriter.c new file mode 100644 index 00000000..81d263df --- /dev/null +++ b/gb.libxml/src/CXMLWriter.c @@ -0,0 +1,429 @@ +/*************************************************************************** + + CXMLWriter.c + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CXMLWRITER_C + +#include +#include +#include +#include "main.h" +#include "CXMLWriter.h" + + + +int Check_Writer(CXMLWRITER *test) +{ + if (!test->writer) + { + GB.Error("No XML file or string to write to"); + return 1; + } + return 0; +} + +void Free_Writer(CXMLWRITER *test) +{ + if (test->writer) + { + xmlTextWriterEndDocument(test->writer); + xmlFreeTextWriter(test->writer); + test->writer=NULL; + } + if (test->buffer) xmlBufferFree(test->buffer); + test->buffer=NULL; +} + +int Resul_Writer(CXMLWRITER *test,int value) +{ + if (value==-1) + { + Free_Writer(test); + GB.Error("Error writing XML data"); + return -1; + } + return 0; +} + +BEGIN_METHOD_VOID(CXmlWriter_Free) + + Free_Writer(THIS); + +END_METHOD + +BEGIN_METHOD (CXmlWriter_Open,GB_STRING FileName; GB_BOOLEAN Indent; GB_STRING Encoding;) + + int res; + int indent=0; + char *encoding=NULL; + + if (!MISSING(Indent)) + if (VARG(Indent)) indent=1; + + if (!MISSING(Encoding)) + encoding=GB.ToZeroString(ARG(Encoding)); + + Free_Writer (THIS); + + if (!LENGTH(FileName)) + { + THIS->buffer=xmlBufferCreate(); + THIS->writer = xmlNewTextWriterMemory(THIS->buffer, 0); + xmlTextWriterSetIndent(THIS->writer,indent); + } + else + { + THIS->writer = xmlNewTextWriterFilename(GB.ToZeroString(ARG(FileName)),0); + xmlTextWriterSetIndent(THIS->writer,indent); + } + + if (!THIS->writer) + { + GB.Error("Unable to write XML file"); + return; + } + + res=xmlTextWriterStartDocument(THIS->writer, NULL,encoding, NULL); + + if (res==-1) + { + Free_Writer (THIS); + GB.Error("Unable to write XML file"); + return; + } + + + +END_METHOD + + +BEGIN_METHOD (CXmlWriter_StartElement,GB_STRING Name; GB_OBJECT Attributes;GB_STRING Prefix;GB_STRING URI;) + + int bucle; + int nmax; + int res; + char *sname; + char *svalue; + char *prefix=NULL; + char *uri=NULL; + + if (!MISSING(Prefix)) prefix=GB.ToZeroString(ARG(Prefix)); + if (!MISSING(URI)) uri=GB.ToZeroString(ARG(URI)); + + if (Check_Writer(THIS)) return; + + if (prefix || uri) + res=xmlTextWriterStartElementNS(THIS->writer,(xmlChar *)prefix,(xmlChar *)GB.ToZeroString(ARG(Name)),(xmlChar *)uri); + else + res=xmlTextWriterStartElement(THIS->writer,(xmlChar *)GB.ToZeroString(ARG(Name))); + + if (Resul_Writer(THIS,res)) return; + + if (MISSING(Attributes)) return; + if (!VARG(Attributes)) return; + + nmax=GB.Array.Count(VARG(Attributes)); + + for (bucle=0;buclewriter,(xmlChar *)sname,(xmlChar *)svalue); + if (Resul_Writer(THIS,res)) return; + } + + +END_METHOD + +BEGIN_METHOD_VOID (CXmlWriter_EndElement) + + if (Check_Writer(THIS)) return; + + Resul_Writer(THIS,xmlTextWriterEndElement(THIS->writer)); + + +END_METHOD + +BEGIN_METHOD(CXmlWriter_Element,GB_STRING Name;GB_STRING Value;GB_STRING Prefix;GB_STRING URI;) + + xmlChar *name,*value; + int resul; + char *prefix=NULL; + char *uri=NULL; + + if (!MISSING(Prefix)) prefix=GB.ToZeroString(ARG(Prefix)); + if (!MISSING(URI)) uri=GB.ToZeroString(ARG(URI)); + + if (Check_Writer(THIS)) return; + + name=(xmlChar *)GB.ToZeroString(ARG(Name)); + if (!MISSING(Value)) + { + value=(xmlChar *)GB.ToZeroString(ARG(Value)); + + if ( prefix || uri ) + resul=xmlTextWriterWriteElementNS(THIS->writer,(xmlChar *)prefix,name,(xmlChar *)uri,value); + else + resul=xmlTextWriterWriteElement(THIS->writer,name,value); + } + else + { + if ( prefix || uri ) + resul=xmlTextWriterStartElementNS(THIS->writer,(xmlChar *)prefix,name,(xmlChar *)uri); + else + resul=xmlTextWriterStartElement(THIS->writer,name); + if (resul != -1) resul=xmlTextWriterEndElement(THIS->writer); + } + + + Resul_Writer(THIS,resul); + +END_METHOD + +BEGIN_METHOD(CXmlWriter_Text,GB_STRING Name;) + + xmlChar *name; + + if (Check_Writer(THIS)) return; + name=(xmlChar *)GB.ToZeroString(ARG(Name)); + Resul_Writer(THIS,xmlTextWriterWriteString(THIS->writer,name)); + +END_METHOD + +BEGIN_METHOD(CXmlWriter_Base64,GB_STRING Name;) + + if (Check_Writer(THIS)) return; + Resul_Writer(THIS,xmlTextWriterWriteBase64 (THIS->writer,STRING(Name),0,LENGTH(Name))); + +END_METHOD + +BEGIN_METHOD(CXmlWriter_BinHex,GB_STRING Name;) + + if (Check_Writer(THIS)) return; + Resul_Writer(THIS,xmlTextWriterWriteBinHex (THIS->writer,STRING(Name),0,LENGTH(Name))); + +END_METHOD + +BEGIN_METHOD(CXmlWriter_CDATA,GB_STRING Name;) + + if (Check_Writer(THIS)) return; + Resul_Writer(THIS,xmlTextWriterWriteCDATA(THIS->writer,(xmlChar *)GB.ToZeroString(ARG(Name)))); + +END_METHOD + + +BEGIN_METHOD(CXmlWriter_Attribute,GB_STRING Name;GB_STRING Value;GB_STRING Prefix;GB_STRING URI;) + + char *name,*value; + char *prefix=NULL; + char *uri=NULL; + int res; + + if (!MISSING(Prefix)) prefix=GB.ToZeroString(ARG(Prefix)); + if (!MISSING(URI)) uri=GB.ToZeroString(ARG(URI)); + + if (Check_Writer(THIS)) return; + + name=GB.ToZeroString(ARG(Name)); + value=GB.ToZeroString(ARG(Value)); + + if (prefix || uri) + res=xmlTextWriterWriteAttributeNS(THIS->writer,(xmlChar *)prefix,(xmlChar *)name,(xmlChar *)uri,(xmlChar *)value); + else + res=xmlTextWriterWriteAttribute(THIS->writer,(xmlChar *)name,(xmlChar *)value); + + Resul_Writer(THIS,res); + + +END_METHOD + +BEGIN_METHOD(CXmlWriter_WritePI,GB_STRING Target;GB_STRING Content;) + + char *target,*content; + + if (Check_Writer(THIS)) return; + target=GB.ToZeroString(ARG(Target)); + content=GB.ToZeroString(ARG(Content)); + Resul_Writer(THIS,xmlTextWriterWritePI(THIS->writer,(xmlChar *)target,(xmlChar *)content)); + + +END_METHOD + + + + +BEGIN_METHOD(CXmlWriter_Comment,GB_STRING Comment) + + if (Check_Writer(THIS)) return; + Resul_Writer(THIS,xmlTextWriterWriteComment(THIS->writer,(xmlChar *)GB.ToZeroString(ARG(Comment)))); + +END_METHOD + +BEGIN_METHOD_VOID(CXmlWriter_EndDocument) + + if (Check_Writer(THIS)) return; + + xmlTextWriterEndDocument(THIS->writer); + xmlFreeTextWriter(THIS->writer); + THIS->writer=NULL; + if (!THIS->buffer) + { + GB.ReturnVoidString(); + return; + } + GB.ReturnNewZeroString((char *)THIS->buffer->content); + xmlBufferFree(THIS->buffer); + THIS->buffer=NULL; + +END_METHOD + + +BEGIN_PROPERTY(CXMLWriter_DTD) + + RETURN_SELF(); + +END_PROPERTY + + +BEGIN_METHOD(CXmlWriter_StartDTD,GB_STRING Name;GB_STRING PubID;GB_STRING SysID;) + + char *name,*pubid=NULL,*sysid=NULL; + + if (Check_Writer(THIS)) return; + + name=GB.ToZeroString(ARG(Name)); + if (!MISSING(PubID)) pubid=GB.ToZeroString(ARG(PubID)); + if (!MISSING(SysID)) pubid=GB.ToZeroString(ARG(SysID)); + Resul_Writer(THIS,xmlTextWriterStartDTD(THIS->writer,(xmlChar *)name,(xmlChar *)pubid,(xmlChar *)sysid)); + +END_METHOD + +BEGIN_METHOD_VOID (CXmlWriter_EndDTD) + + if (Check_Writer(THIS)) return; + + Resul_Writer(THIS,xmlTextWriterEndDTD(THIS->writer)); + +END_METHOD + +BEGIN_METHOD (CXmlWriter_DTDElement,GB_STRING Name;GB_STRING Content;) + + char *name,*content; + int resul; + + if (Check_Writer(THIS)) return; + + name=GB.ToZeroString(ARG(Name)); + if (!MISSING(Content)) + { + content=GB.ToZeroString(ARG(Content)); + resul=xmlTextWriterWriteDTDElement (THIS->writer,(xmlChar *)name,(xmlChar *)content); + } + else + { + resul=xmlTextWriterStartDTDElement (THIS->writer,(xmlChar *)name); + if (resul != 1) resul=xmlTextWriterEndDTDElement(THIS->writer); + } + + Resul_Writer(THIS,resul); + +END_METHOD + + +BEGIN_METHOD (CXmlWriter_DTDInternalEntity,GB_STRING Name;GB_STRING Content;GB_BOOLEAN IsParameter;) + + char *name,*content; + int isparameter=0; + + if (Check_Writer(THIS)) return; + + name=GB.ToZeroString(ARG(Name)); + content=GB.ToZeroString(ARG(Content)); + if (!MISSING(IsParameter)) isparameter=VARG(IsParameter); + Resul_Writer(THIS,xmlTextWriterWriteDTDInternalEntity(THIS->writer,isparameter,(xmlChar *)name,(xmlChar *)content)); + +END_METHOD + + +BEGIN_METHOD (CXmlWriter_DTDAttList,GB_STRING Name;GB_STRING Content;) + + char *name,*content; + + if (Check_Writer(THIS)) return; + + name=GB.ToZeroString(ARG(Name)); + content=GB.ToZeroString(ARG(Content)); + Resul_Writer(THIS,xmlTextWriterWriteDTDAttlist(THIS->writer,(xmlChar *)name,(xmlChar *)content)); + + +END_METHOD + + + +GB_DESC CXmlWriterDTDDesc[] = +{ + // TOOD: external entity + // Namespace + GB_DECLARE(".XmlWriter.DTD", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("Start",NULL,CXmlWriter_StartDTD,"(Name)s[(PubID)s(SysID)s]"), + GB_METHOD("Element",NULL,CXmlWriter_DTDElement,"(Name)s[(Content)s]"), + GB_METHOD("InternalEntity",NULL,CXmlWriter_DTDInternalEntity,"(Name)s(Content)s[(IsParameter)b]"), + GB_METHOD("AttList",NULL,CXmlWriter_DTDAttList,"(Name)s(Content)s"), + GB_METHOD("End",NULL,CXmlWriter_EndDTD,NULL), + + GB_END_DECLARE +}; + +GB_DESC CXmlWriterDesc[] = +{ + GB_DECLARE("XmlWriter", sizeof(CXMLWRITER)), + + GB_PROPERTY_READ("DTD",".XmlWriter.DTD",CXMLWriter_DTD), + + GB_METHOD("_free",NULL,CXmlWriter_Free,NULL), + + GB_METHOD("Open", NULL, CXmlWriter_Open,"(FileName)s[(Indent)b(Encoding)s]"), + + GB_METHOD("StartElement",NULL,CXmlWriter_StartElement,"(Name)s[(Attributes)String[];(Prefix)s(URI)s]"), + GB_METHOD("Attribute",NULL,CXmlWriter_Attribute,"(Name)s(Value)s[(Prefix)s(URI)s]"), + GB_METHOD("Element",NULL,CXmlWriter_Element,"(Name)s[(Value)s(Prefix)s(URI)s]"), + GB_METHOD("Comment",NULL,CXmlWriter_Comment,"(Comment)s"), + GB_METHOD("Text",NULL,CXmlWriter_Text,"(Data)s"), + GB_METHOD("Base64",NULL,CXmlWriter_Base64,"(Data)s"), + GB_METHOD("BinHex",NULL,CXmlWriter_BinHex,"(Data)s"), + GB_METHOD("CDATA",NULL,CXmlWriter_CDATA,"(Data)s"), + GB_METHOD("EndElement",NULL,CXmlWriter_EndElement,NULL), + GB_METHOD("EndDocument","s",CXmlWriter_EndDocument,NULL), + GB_METHOD("PI",NULL,CXmlWriter_WritePI,"(Target)s(Content)s"), + + GB_END_DECLARE +}; + + + + diff --git a/gb.libxml/src/CXMLWriter.h b/gb.libxml/src/CXMLWriter.h new file mode 100644 index 00000000..1d45f8e0 --- /dev/null +++ b/gb.libxml/src/CXMLWriter.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + CXMLWriter.h + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CXMLWRITER_H +#define __CXMLWRITER_H + +#include "gambas.h" +#include + +#ifndef __CXMLWRITER_C + +extern GB_DESC CXmlWriterDTDDesc[]; +extern GB_DESC CXmlWriterDesc[]; + +#else + +#define THIS ((CXMLWRITER *)_object) + +#endif + + +typedef struct +{ + GB_BASE ob; + xmlTextWriterPtr writer; + xmlBufferPtr buffer; + + +} CXMLWRITER; + + +#endif diff --git a/gb.libxml/src/Makefile.am b/gb.libxml/src/Makefile.am new file mode 100644 index 00000000..48dc277d --- /dev/null +++ b/gb.libxml/src/Makefile.am @@ -0,0 +1,15 @@ +COMPONENT = gb.libxml +include $(top_srcdir)/component.am + +EXTRA_DIST = *.kateproject + +gblib_LTLIBRARIES = gb.libxml.la + +gb_libxml_la_LIBADD = @XML_LIB@ +gb_libxml_la_LDFLAGS = -module @LD_FLAGS@ @XML_LDFLAGS@ +gb_libxml_la_CPPFLAGS = @XML_INC@ + +gb_libxml_la_SOURCES = \ + main.h main.c CXMLNode.h CXMLNode.c CXMLReader.h CXMLReader.c \ + CXMLWriter.h CXMLWriter.c CXMLDocument.h CXMLDocument.c + diff --git a/gb.libxml/src/gb.libxml.component b/gb.libxml/src/gb.libxml.component new file mode 100644 index 00000000..62f74101 --- /dev/null +++ b/gb.libxml/src/gb.libxml.component @@ -0,0 +1,7 @@ +[Component] +Key=gb.xml +Author=Daniel Campos Fernández +State=Deprecated +Implements=XML + + diff --git a/gb.libxml/src/libxml.kateproject b/gb.libxml/src/libxml.kateproject new file mode 100644 index 00000000..c23d8d79 --- /dev/null +++ b/gb.libxml/src/libxml.kateproject @@ -0,0 +1,7 @@ +[Project Dir] +Dirs= +Files=CXMLReader.h/main.h/Makefile.am/CXMLNode.h/CXMLDocument.h/CXMLDocument.c/CXMLNode.c/CXMLReader.c/main.c/CXMLWriter.c/CXMLWriter.h + +[Project File] +Name=LibXML +Type=Default diff --git a/gb.libxml/src/main.c b/gb.libxml/src/main.c new file mode 100644 index 00000000..fbf773b8 --- /dev/null +++ b/gb.libxml/src/main.c @@ -0,0 +1,73 @@ +/*************************************************************************** + + main.c + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include "main.h" +#include "CXMLReader.h" +#include "CXMLWriter.h" +#include "CXMLNode.h" +#include "CXMLDocument.h" + + + +//extern "C" { + + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + + CXmlReaderNodeTypeDesc, + CXmlReaderNodeAttributesDesc, + CXmlReaderNodeDesc, + CXmlReaderDesc, + + CXmlWriterDTDDesc, + CXmlWriterDesc, + + CXmlNodeAttributesDesc, + CXmlNodeChildrenDesc, + CXmlNodeDesc, + CXmlDocumentDesc, + + NULL +}; + + +int EXPORT GB_INIT(void) +{ + return -1; +} + +void EXPORT GB_EXIT() +{ + +} + +//} + + + diff --git a/gb.libxml/src/main.h b/gb.libxml/src/main.h new file mode 100644 index 00000000..dd9b6fe4 --- /dev/null +++ b/gb.libxml/src/main.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + main.h + + (c) 2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + + +#include "gambas.h" + + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/gb.media/AUTHORS b/gb.media/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.media/COPYING b/gb.media/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.media/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.media/ChangeLog b/gb.media/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.media/INSTALL b/gb.media/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.media/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.media/Makefile.am b/gb.media/Makefile.am new file mode 100644 index 00000000..61a826f8 --- /dev/null +++ b/gb.media/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @MEDIA_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.media/NEWS b/gb.media/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.media/README b/gb.media/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.media/acinclude.m4 b/gb.media/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.media/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.media/component.am b/gb.media/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.media/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.media/configure.ac b/gb.media/configure.ac new file mode 100644 index 00000000..36e3e111 --- /dev/null +++ b/gb.media/configure.ac @@ -0,0 +1,16 @@ +dnl ---- configure.ac for gb.media + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-media, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.media) +AC_PROG_LIBTOOL + +GB_COMPONENT_PKG_CONFIG( + media, MEDIA, gb.media, [src], + gstreamer-1.0 gstreamer-video-1.0 +) +AC_OUTPUT( Makefile src/Makefile ) + +GB_PRINT_MESSAGES diff --git a/gb.media/gambas.h b/gb.media/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.media/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.media/gb.image.h b/gb.media/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.media/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.media/gb_common.h b/gb.media/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.media/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.media/m4 b/gb.media/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.media/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.media/reconf b/gb.media/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.media/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.media/src/Makefile.am b/gb.media/src/Makefile.am new file mode 100644 index 00000000..7ddd3708 --- /dev/null +++ b/gb.media/src/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.media +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.media.la + +gb_media_la_LIBADD = @THREAD_LIB@ @MEDIA_LIB@ +gb_media_la_LDFLAGS = -module @LD_FLAGS@ @MEDIA_LDFLAGS@ +gb_media_la_CPPFLAGS = @THREAD_INC@ @MEDIA_INC@ + +gb_media_la_SOURCES = \ + main.c main.h \ + c_media.c c_media.h \ + c_mediaplayer.c c_mediaplayer.h diff --git a/gb.media/src/c_media.c b/gb.media/src/c_media.c new file mode 100644 index 00000000..ae73e659 --- /dev/null +++ b/gb.media/src/c_media.c @@ -0,0 +1,2079 @@ +/*************************************************************************** + + c_media.c + + gb.media component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_MEDIA_C + +#include "c_media.h" +//#include +#include +#include +#include + +static void *_from_element = NULL; + +void MEDIA_raise_event(void *_object, int event) +{ + gst_element_post_message(ELEMENT, gst_message_new_application(GST_OBJECT(ELEMENT), gst_structure_new("SendEvent", "event", G_TYPE_INT, event, NULL))); +} + +/*void MEDIA_raise_event_arg(void *_object, int event, char *arg) +{ + gst_element_post_message(ELEMENT, gst_message_new_application(GST_OBJECT(ELEMENT), gst_structure_new("SendEvent", "event", G_TYPE_INT, event, "arg", G_TYPE_STRING, arg, NULL))); +}*/ + +static const char *get_element_type(GstElement *element) +{ + GstElementFactory *factory = gst_element_get_factory(element); + if (!factory) + return NULL; + else + return gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory)); +} + +CMEDIACONTROL *MEDIA_get_control_from_element(void *element, bool create) +{ + CMEDIACONTROL *ctrl; + GB_CLASS klass; + //const char *type; + + if (!element) + return NULL; + + ctrl = (CMEDIACONTROL *)g_object_get_data(G_OBJECT(element), "gambas-control"); + if (create) + { + if (!ctrl) + { + _from_element = element; + + /*type = get_element_type(element); + + if (type && strcmp(type, "v4l2src") == 0) + klass = GB.FindClass("MediaVideo"); + else*/ + if (GST_IS_PIPELINE(element)) + klass = GB.FindClass("MediaPipeline"); + else if (GST_IS_BIN(element)) + klass = GB.FindClass("MediaContainer"); + else + klass = GB.FindClass("MediaControl"); + + ctrl = (CMEDIACONTROL *)GB.New(klass, NULL, NULL); + } + } + else + { + if (ctrl && ctrl->borrow) + ctrl = NULL; + } + + return ctrl; +} + +bool MEDIA_set_state(void *_object, int state, bool error) +{ + GstStateChangeReturn status; + + status = gst_element_set_state(ELEMENT, state); + + if (status == GST_STATE_CHANGE_ASYNC) + status = gst_element_get_state(ELEMENT, NULL, NULL, GST_SECOND); + + if (status == GST_STATE_CHANGE_FAILURE) + { + if (error) GB.Error("Cannot set status"); + return TRUE; + } + + return FALSE; +} + +bool MEDIA_get_flag(void *element, char *property, int flag) +{ + int flags; + + g_object_get(G_OBJECT(element), property, &flags, NULL); + return (flags & flag) != 0; +} + +void MEDIA_set_flag(void *element, char *property, int flag, bool value) +{ + int flags; + + g_object_get(G_OBJECT(element), property, &flags, NULL); + if (value) + flags |= flag; + else + flags &= ~flag; + g_object_set(G_OBJECT(element), property, flags, NULL); +} + + + +static GB_TYPE to_gambas_type(const GValue *value) +{ + switch (G_VALUE_TYPE(value)) + { + case G_TYPE_BOOLEAN: return GB_T_BOOLEAN; + case G_TYPE_INT: return GB_T_INTEGER; + case G_TYPE_UINT: return GB_T_INTEGER; + case G_TYPE_UINT64: return GB_T_LONG; + case G_TYPE_STRING: return GB_T_STRING; + case G_TYPE_FLOAT: return GB_T_FLOAT; + case G_TYPE_DOUBLE: return GB_T_FLOAT; + default: + if (G_VALUE_HOLDS(value, G_TYPE_DATE)) + return GB_T_DATE; + else if (GST_VALUE_HOLDS_DATE_TIME(value)) + return GB_T_DATE; + else + { + fprintf(stderr, "gb.media: warning: unsupported data type: %s\n", G_VALUE_TYPE_NAME(value)); + //GB.Error("Unsupported property datatype"); + return GB_T_NULL; + } + } +} + +static void to_gambas_value(const GValue *value, GB_VALUE *gvalue) +{ + switch (G_VALUE_TYPE(value)) + { + case G_TYPE_BOOLEAN: + gvalue->type = GB_T_BOOLEAN; + gvalue->_boolean.value = g_value_get_boolean(value) == 0 ? 0 : -1; + break; + + case G_TYPE_INT: + gvalue->type = GB_T_INTEGER; + gvalue->_integer.value = g_value_get_int(value); + break; + + case G_TYPE_UINT: + gvalue->type = GB_T_INTEGER; + gvalue->_integer.value = (int)g_value_get_uint(value); + break; + + case G_TYPE_UINT64: + gvalue->type = GB_T_LONG; + gvalue->_long.value = (int64_t)g_value_get_uint64(value); + break; + + case G_TYPE_STRING: + gvalue->type = GB_T_STRING; + gvalue->_string.value.addr = GB.NewZeroString(g_value_get_string(value)); + gvalue->_string.value.start = 0; + gvalue->_string.value.len = GB.StringLength(gvalue->_string.value.addr); + break; + + case G_TYPE_FLOAT: + gvalue->type = GB_T_FLOAT; + gvalue->_float.value = (double)g_value_get_float(value); + break; + + case G_TYPE_DOUBLE: + gvalue->type = GB_T_FLOAT; + gvalue->_float.value = g_value_get_double(value); + break; + + default: + + if (G_VALUE_HOLDS(value, G_TYPE_DATE)) + { + GB_DATE_SERIAL ds; + GDate *date = (GDate *)g_value_get_boxed(value); + + CLEAR(&ds); + ds.year = date->year; + ds.month = date->month; + ds.day = date->day; + + if (ds.year && (ds.month == 0 || ds.day == 0)) + { + ds.month = 1; + ds.day = 1; + } + + GB.MakeDate(&ds, (GB_DATE *)gvalue); + break; + } + else if (GST_VALUE_HOLDS_DATE_TIME(value)) + { + GB_DATE_SERIAL ds; + GstDateTime *date = (GstDateTime *)g_value_get_boxed(value); + + CLEAR(&ds); + if (gst_date_time_has_year(date)) + ds.year = gst_date_time_get_year(date); + if (gst_date_time_has_month(date)) + ds.month = gst_date_time_get_month(date); + if (gst_date_time_has_day(date)) + ds.day = gst_date_time_get_day(date); + + if (ds.year && (ds.month == 0 || ds.day == 0)) + { + ds.month = 1; + ds.day = 1; + } + + if (gst_date_time_has_time(date)) + { + ds.hour = gst_date_time_get_hour(date); + ds.min = gst_date_time_get_minute(date); + ds.sec = gst_date_time_get_second(date); + ds.msec = gst_date_time_get_microsecond(date); + } + + GB.MakeDate(&ds, (GB_DATE *)gvalue); + break; + } + else + gvalue->type = GB_T_NULL; + } +} + +static GParamSpec *get_property(GstElement *element, const char *property) +{ + GParamSpec *desc; + + desc = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(element)), property); + if (!desc) + GB.Error("Unknown property: '&1'", property); + + return desc; +} + +static void return_value(const GValue *value) +{ + if(value == NULL) + { + GB.ReturnNull(); + return; + } + GType type = G_VALUE_TYPE(value); + + switch (type) + { + case G_TYPE_BOOLEAN: GB.ReturnBoolean(g_value_get_boolean(value)); break; + case G_TYPE_INT: GB.ReturnInteger(g_value_get_int(value)); break; + case G_TYPE_UINT: GB.ReturnInteger(g_value_get_uint(value)); break; + case G_TYPE_UINT64: GB.ReturnLong(g_value_get_uint64(value)); break; + case G_TYPE_STRING: GB.ReturnNewZeroString(g_value_get_string(value)); break; + case G_TYPE_FLOAT: GB.ReturnFloat(g_value_get_float(value)); break; + case G_TYPE_DOUBLE: GB.ReturnFloat(g_value_get_double(value)); break; + default: + if (G_VALUE_HOLDS(value, G_TYPE_DATE) || GST_VALUE_HOLDS_DATE_TIME(value)) + { + GB_VALUE date; + to_gambas_value(value, &date); + GB.ReturnDate((GB_DATE *)&date); + } + else if (G_TYPE_IS_ENUM(type)) + { + GEnumValue *enum_value; + + enum_value = g_enum_get_value(G_ENUM_CLASS(g_type_class_ref(type)), g_value_get_enum(value)); + if (!enum_value) + { + char buf[16]; + sprintf(buf, "%d", g_value_get_enum(value)); + GB.ReturnNewZeroString(buf); + } + else + GB.ReturnNewZeroString(enum_value->value_nick); + } + else if (type == GST_TYPE_CAPS) + { + char *caps; + caps = gst_caps_to_string((GstCaps *)g_value_get_boxed(value)); + GB.ReturnNewZeroString(caps); + g_free(caps); + } + else if(GST_VALUE_HOLDS_LIST(value)) + { + guint listSize = gst_value_list_get_size(value); + GB_ARRAY array; + + if(listSize <= 0) + { + GB.Array.New(&array, GB_T_VARIANT, 0); + } + else + { + GB.Array.New(&array, GB_T_VARIANT, listSize); + + GB_VALUE val; + int i; + for (i = 0; i < listSize; i++) + { + to_gambas_value(gst_value_list_get_value(value, i), &val); + GB.Store(GB_T_VARIANT, &val, GB.Array.Get(array, i)); + GB.ReleaseValue(&val); + } + } + + GB.ReturnObject(array); + } +#ifdef G_TYPE_VALUE_ARRAY + else if (G_VALUE_HOLDS(value, G_TYPE_VALUE_ARRAY)) + { + GValueArray *garray = (GValueArray *)g_value_get_boxed(value); + guint len = garray->n_values; + GB_ARRAY array; + + GB.Array.New(&array, GB_T_VARIANT, len); + + if (len <= 0) + { + GB.Array.New(&array, GB_T_VARIANT, 0); + } + else + { + GB_VALUE val; + int i; + for (i = 0; i < len; i++) + { + to_gambas_value(g_value_array_get_nth(garray, i), &val); + GB.Store(GB_T_VARIANT, &val, GB.Array.Get(array, i)); + GB.ReleaseValue(&val); + } + } + + GB.ReturnObject(array); + } +#endif + else + { + fprintf(stderr, "gb.media: warning: unsupported datatype: %s\n", G_VALUE_TYPE_NAME(value)); + GB.ReturnNull(); + } + } +} + +void MEDIA_return_property(void *_object, const char *property) +{ + GParamSpec *desc; + GValue value = G_VALUE_INIT; + + desc = get_property(ELEMENT, property); + if (!desc) + return; + + //fprintf(stderr, "type = %s\n", g_type_name(desc->value_type)); + g_value_init(&value, desc->value_type); + g_object_get_property(G_OBJECT(ELEMENT), property, &value); + return_value(&value); + g_value_unset(&value); +} + + +static bool set_value(GValue *value, GB_VALUE *v, GParamSpec *desc) +{ + GType type = desc->value_type; + + g_value_init(value, type); + + switch (type) + { + case G_TYPE_BOOLEAN: + if (GB.Conv(v, GB_T_BOOLEAN)) + return TRUE; + g_value_set_boolean(value, v->_boolean.value); + break; + + case G_TYPE_INT: + if (GB.Conv(v, GB_T_INTEGER)) + return TRUE; + g_value_set_int(value, v->_integer.value); + break; + + case G_TYPE_UINT: + if (GB.Conv(v, GB_T_INTEGER)) + return TRUE; + g_value_set_uint(value, (uint)v->_integer.value); + break; + + case G_TYPE_UINT64: + if (GB.Conv(v, GB_T_LONG)) + return TRUE; + g_value_set_uint64(value, (guint64)v->_long.value); + break; + + case G_TYPE_STRING: + if (GB.Conv(v, GB_T_STRING)) + return TRUE; + g_value_set_string(value, GB.ToZeroString((GB_STRING *)v)); + break; + + case G_TYPE_FLOAT: + if (GB.Conv(v, GB_T_FLOAT)) + return TRUE; + g_value_set_float(value, v->_float.value); + break; + + case G_TYPE_DOUBLE: + if (GB.Conv(v, GB_T_FLOAT)) + return TRUE; + g_value_set_double(value, v->_float.value); + break; + + default: + + if (G_TYPE_IS_ENUM(type)) + { + GEnumValue *enum_value; + int real_value; + + if (!GB.Conv(v, GB_T_INTEGER)) + { + real_value = ((GB_INTEGER *)v)->value; + } + else + { + if (GB.Conv(v, GB_T_STRING)) + return TRUE; + + enum_value = g_enum_get_value_by_nick(G_ENUM_CLASS(g_type_class_ref(type)), GB.ToZeroString((GB_STRING *)v)); + if (!enum_value) + { + GB.Error("Unknown enumeration value"); + return TRUE; + } + + real_value = enum_value->value; + } + + g_value_set_enum(value, real_value); + } + else if (type == GST_TYPE_CAPS) + { + GstCaps *caps; + + if (GB.Conv(v, GB_T_STRING)) + return TRUE; + + caps = gst_caps_from_string(GB.ToZeroString((GB_STRING *)v)); + if (!caps) + { + GB.Error("Incorrect filter"); + return TRUE; + } + + g_value_take_boxed(value, caps); + } + else + { + GB.Error("Unsupported datatype: &1", g_type_name(type)); + return TRUE; + } + } + + return FALSE; +} + +void MEDIA_set_property(void *_object, const char *property, GB_VALUE *v) +{ + GParamSpec *desc; + GValue value = G_VALUE_INIT; + + desc = get_property(ELEMENT, property); + if (!desc) + return; + + if (set_value(&value, v, desc)) + return; + + g_object_set_property(G_OBJECT(ELEMENT), property, &value); + g_value_unset(&value); +} + +GB_IMG *MEDIA_get_image_from_sample(GstSample *sample, bool convert) +{ + GstSample *temp; + GError *err = NULL; + GstStructure *s; + GstCaps *to_caps, *sample_caps; + gint outwidth = 0; + gint outheight = 0; + GstMemory *memory; + GstMapInfo info; + const char *format; + int gb_format; + GB_IMG *img; + + switch (IMAGE.GetDefaultFormat()) + { + case GB_IMAGE_BGRA: + case GB_IMAGE_BGRP: + format = "BGR"; + gb_format = GB_IMAGE_BGR; + break; + + case GB_IMAGE_RGBA: + case GB_IMAGE_RGBP: + format = "RGB"; + gb_format = GB_IMAGE_RGB; + break; + + default: + GB.Error("Unsupported default image format"); + return NULL; + } + + if (convert) + { + /* our desired output format (RGB or BGR) */ + to_caps = gst_caps_new_simple ("video/x-raw", + "format", G_TYPE_STRING, format, + /* Note: we don't ask for a specific width/height here, so that + * videoscale can adjust dimensions from a non-1/1 pixel aspect + * ratio to a 1/1 pixel-aspect-ratio. We also don't ask for a + * specific framerate, because the input framerate won't + * necessarily match the output framerate if there's a deinterlacer + * in the pipeline. */ + "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, + NULL); + + temp = gst_video_convert_sample(sample, to_caps, 25 * GST_SECOND, &err); + + if (temp == NULL && err) + { + GB.Error(err->message); + gst_caps_unref(to_caps); + gst_sample_unref(sample); + g_error_free(err); + return NULL; + } + + gst_sample_unref(sample); + gst_caps_unref(to_caps); + + sample = temp; + } + + if (!sample) + { + GB.Error("Unable to retrieve or convert video frame"); + return NULL; + } + + sample_caps = gst_sample_get_caps(sample); + if (!sample_caps) + { + GB.Error("No caps on video frame"); + gst_sample_unref(sample); + return NULL; + } + + //fprintf(stderr, "frame caps: %" GST_PTR_FORMAT, sample_caps); + + s = gst_caps_get_structure (sample_caps, 0); + gst_structure_get_int (s, "width", &outwidth); + gst_structure_get_int (s, "height", &outheight); + if (outwidth <= 0 || outheight <= 0) + { + GB.Error("Bad image dimensions"); + gst_sample_unref(sample); + return NULL; + } + + memory = gst_buffer_get_memory (gst_sample_get_buffer (sample), 0); + gst_memory_map(memory, &info, GST_MAP_READ); + + /* create pixbuf from that - use our own destroy function */ + /*pixbuf = gdk_pixbuf_new_from_data (info.data, + GDK_COLORSPACE_RGB, FALSE, 8, outwidth, outheight, + GST_ROUND_UP_4 (outwidth * 3), destroy_pixbuf, sample);*/ + + img = IMAGE.Create(outwidth, outheight, gb_format, info.data); + + gst_memory_unmap(memory, &info); + + gst_sample_unref(sample); + return img; +} + +static GB_IMG *get_last_image(void *_object) +{ + GstElement *elt = GST_ELEMENT(ELEMENT); + GstSample *sample; + + if (!GST_IS_BASE_SINK(elt)) + { + GB.Error("Not supported on this control"); + return NULL; + } + + sample = gst_base_sink_get_last_sample(GST_BASE_SINK(elt)); + if (sample == NULL) + return NULL; + + return MEDIA_get_image_from_sample(sample, TRUE); +} + +#if 0 +//---- MediaSignalArguments ----------------------------------------------- + +static int check_signal_arguments(void *_object) +{ + return THIS_ARG->param_values == NULL; +} + +BEGIN_METHOD(MediaSignalArguments_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= THIS_ARG->n_param_values) + { + GB.Error(GB_ERR_BOUND); + return; + } + + return_value(&THIS_ARG->param_values[index]); + +END_METHOD +#endif + +//---- MediaTagList ------------------------------------------------------- + +static int MediaTagList_check(void *_object) +{ + return THIS_TAGLIST->tags == NULL; +} + +static CMEDIATAGLIST *create_tag_list(GstTagList *tags) +{ + CMEDIATAGLIST *ob; + + ob = GB.New(GB.FindClass("MediaTagList"), NULL, NULL); + ob->tags = tags; + return ob; +} + +BEGIN_METHOD(MediaTagList_get, GB_STRING name) + + char *name = GB.ToZeroString(ARG(name)); + GstTagList *tags = THIS_TAGLIST->tags; + const GValue *value; + int nvalue; + + nvalue = gst_tag_list_get_tag_size(tags, name); + + if (nvalue <= 0) + { + GB.ReturnNull(); + } + else if (nvalue == 1) + { + value = gst_tag_list_get_value_index(tags, name, 0); + return_value(value); + } + else + { + GB_ARRAY array; + GB_TYPE type; + GB_VALUE gvalue; + int i; + + value = gst_tag_list_get_value_index(tags, name, 0); + type = to_gambas_type(value); + if (type == GB_T_NULL) + GB.ReturnNull(); + else + { + GB.Array.New(&array, type, nvalue); + for (i = 0; i < nvalue; i++) + { + value = gst_tag_list_get_value_index(tags, name, i); + to_gambas_value(value, &gvalue); + GB.Store(type, &gvalue, GB.Array.Get(array, i)); + GB.ReleaseValue(&gvalue); + } + + GB.ReturnObject(array); + } + } + + GB.ReturnConvVariant(); + +END_METHOD + +BEGIN_PROPERTY(MediaTagList_Tags) + + GB_ARRAY array; + GstTagList *tags = THIS_TAGLIST->tags; + int ntags, i; + + ntags = gst_tag_list_n_tags(tags); + + GB.Array.New(&array, GB_T_STRING, ntags); + + for (i = 0; i < ntags; i++) + *((char **)GB.Array.Get(array, i)) = GB.NewZeroString(gst_tag_list_nth_tag_name(tags, i)); + + GB.ReturnObject(array); + +END_PROPERTY + +//---- MediaMessage ------------------------------------------------------- + +#define MESSAGE_DATA (gst_message_get_structure(THIS_MESSAGE->message)) + +static int MediaMessage_check(void *_object) +{ + return THIS_MESSAGE->message == NULL; +} + +static CMEDIAMESSAGE *create_message(GstMessage *message) +{ + CMEDIAMESSAGE *ob; + + ob = GB.New(GB.FindClass("MediaMessage"), NULL, NULL); + ob->message = message; + ob->lastKey = NULL; + return ob; +} + +BEGIN_METHOD(MediaMessage_get, GB_STRING name) + + char *name = GB.ToZeroString(ARG(name)); + const GValue *value; + + value = gst_structure_get_value(MESSAGE_DATA, name); + return_value(value); + + GB.ReturnConvVariant(); + +END_METHOD + +BEGIN_PROPERTY(MediaMessage_Keys) + + GB_ARRAY array; + const GstStructure *data = MESSAGE_DATA; + int nfields, i; + + nfields = gst_structure_n_fields(data); + + GB.Array.New(&array, GB_T_STRING, nfields); + + for (i = 0; i < nfields; i++) + *((char **)GB.Array.Get(array, i)) = GB.NewZeroString(gst_structure_nth_field_name(data, i)); + + GB.ReturnObject(array); + +END_PROPERTY + +BEGIN_PROPERTY(MediaMessage_Type) + + GB.ReturnInteger(GST_MESSAGE_TYPE(THIS_MESSAGE->message)); + +END_PROPERTY + +BEGIN_PROPERTY(MediaMessage_Name) + + GB.ReturnNewZeroString(gst_structure_get_name(MESSAGE_DATA)); + +END_PROPERTY + +BEGIN_PROPERTY(MediaMessage_Count) + + GB.ReturnInteger(gst_structure_n_fields(MESSAGE_DATA)); + +END_PROPERTY + +BEGIN_PROPERTY(MediaMessage_Key) + + GB.ReturnNewZeroString(THIS_MESSAGE->lastKey); + +END_PROPERTY + +BEGIN_METHOD_VOID(MediaMessage_next) + + const GstStructure *data = MESSAGE_DATA; + int count = gst_structure_n_fields(data); + int *index = (int *)GB.GetEnum(); + + if (*index < 0 || *index >= count) + GB.StopEnum(); + else + { + THIS_MESSAGE->lastKey = gst_structure_nth_field_name(data, *index); + const GValue *value = gst_structure_get_value(data, THIS_MESSAGE->lastKey); + return_value(value); + + GB.ReturnConvVariant(); + (*index)++; + } + +END_PROPERTY + +//---- MediaLink ---------------------------------------------------------- + +static CMEDIALINK *create_link(GstPad *pad) +{ + CMEDIALINK *ob; + + ob = GB.New(GB.FindClass("MediaLink"), NULL, NULL); + ob->pad = pad; + return ob; +} + +BEGIN_METHOD_VOID(MediaLink_free) + + gst_object_unref(LINK); + +END_METHOD + +BEGIN_PROPERTY(MediaLink_Name) + + char *name = gst_pad_get_name(LINK); + GB.ReturnNewZeroString(name); + g_free(name); + +END_PROPERTY + +BEGIN_PROPERTY(MediaLink_Peer) + + GstPad *peer = gst_pad_get_peer(LINK); + + if (!peer) + { + GB.ReturnNull(); + return; + } + + GB.ReturnObject(MEDIA_get_control_from_element(gst_pad_get_parent_element(peer), TRUE)); + gst_object_unref(peer); + +END_PROPERTY + +static void return_peer_name(void *_object, GstPadDirection dir) +{ + if (gst_pad_get_direction(LINK) == dir) + { + GstPad *peer = gst_pad_get_peer(LINK); + if (peer) + { + char *name = gst_pad_get_name(peer); + GB.ReturnNewZeroString(name); + g_free(name); + gst_object_unref(peer); + return; + } + } + + GB.ReturnVoidString(); +} + +BEGIN_PROPERTY(MediaLink_Input) + + return_peer_name(THIS_LINK, GST_PAD_SRC); + +END_PROPERTY + +BEGIN_PROPERTY(MediaLink_Output) + + return_peer_name(THIS_LINK, GST_PAD_SINK); + +END_PROPERTY + +//---- MediaControl ------------------------------------------------------- + +DECLARE_EVENT(EVENT_State); +//DECLARE_EVENT(EVENT_Signal); + +typedef + struct { + char *klass; + char *type; + } + MEDIA_TYPE; + +static MEDIA_TYPE _types[] = +{ + { "MediaContainer", "bin" }, + { "MediaPipeline", "pipeline" }, + { "Media", "pipeline" }, + { "MediaPlayer", "playbin" }, + { "MediaFilter", "capsfilter" }, + { "MediaVideo", "v4l2src" }, + //{ "MediaDecoder", "decodebin" }, + { NULL, NULL } +}; + +static void cb_pad_added(GstElement *element, GstPad *pad, CMEDIACONTROL *_object) +{ + char *name; + + if (!THIS->dest) + return; + + name = gst_pad_get_name(pad); + gst_element_link_pads(ELEMENT, name, ((CMEDIACONTROL *)THIS->dest)->elt, NULL); + g_free(name); +} + +BEGIN_METHOD(MediaControl_new, GB_OBJECT parent; GB_STRING type) + + char *type; + CMEDIACONTAINER *parent; + MEDIA_TYPE *mtp; + GB_CLASS klass; + char *filter = NULL; + + //fprintf(stderr, "MediaControl_new: %p\n", THIS); + + THIS->tag.type = GB_T_NULL; + + if (_from_element) + { + //GstState state = GST_STATE_NULL; + + THIS->borrow = TRUE; + THIS->elt = _from_element; + _from_element = NULL; + + gst_object_ref(GST_OBJECT(ELEMENT)); + + g_object_set_data(G_OBJECT(ELEMENT), "gambas-control", THIS); + + //gst_element_get_state(ELEMENT, &state, NULL, GST_SECOND); + THIS->state = GST_STATE_NULL; + + //THIS->type = GB.NewZeroString(gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(gst_element_get_factory(ELEMENT)))); + } + else + { + if (MISSING(type)) + { + klass = GB.GetClass(THIS); + type = NULL; + + for (mtp = _types; mtp->klass; mtp++) + { + if (GB.FindClass(mtp->klass) == klass) + { + type = mtp->type; + break; + } + } + + if (!type) + { + GB.Error("The type must be specified"); + return; + } + } + else + { + type = GB.ToZeroString(ARG(type)); + if (strchr(type, '/')) + { + filter = type; + type = "capsfilter"; + } + } + + THIS->state = GST_STATE_NULL; + //THIS->type = GB.NewZeroString(type); + + + ELEMENT = gst_element_factory_make(type, NULL); + if (!ELEMENT) + { + GB.Error("Unable to create media control"); + return; + } + + gst_object_ref(GST_OBJECT(ELEMENT)); + g_object_set_data(G_OBJECT(ELEMENT), "gambas-control", THIS); + + parent = VARGOPT(parent, NULL); + if (parent) + { + gst_bin_add(GST_BIN(parent->elt), ELEMENT); + gst_element_sync_state_with_parent(ELEMENT); + } + else if (!GST_IS_PIPELINE(ELEMENT)) + GB.CheckObject(parent); + + if (filter) + MEDIA_set_property(THIS, "caps", (GB_VALUE *)ARG(type)); + } + +END_METHOD + +BEGIN_METHOD_VOID(MediaControl_free) + + //fprintf(stderr, "MediaControl_free: %p\n", THIS); + + GB.Unref(POINTER(&THIS->dest)); + //GB.FreeString(&THIS->type); + GB.StoreVariant(NULL, &THIS->tag); + + if (ELEMENT) + { + if (!THIS->borrow) + gst_element_set_state(ELEMENT, GST_STATE_NULL); + + g_object_set_data(G_OBJECT(ELEMENT), "gambas-control", NULL); + gst_object_unref(GST_OBJECT(ELEMENT)); + } + +END_METHOD + +BEGIN_PROPERTY(MediaControl_Tag) + + if (READ_PROPERTY) + GB.ReturnVariant(&THIS->tag); + else + GB.StoreVariant(PROP(GB_VARIANT), POINTER(&THIS->tag)); + +END_METHOD + +BEGIN_PROPERTY(MediaControl_Type) + + const char *type = get_element_type(ELEMENT); + + if (!type) + GB.ReturnConstZeroString("?"); + else + GB.ReturnNewZeroString(type); + +END_PROPERTY + +BEGIN_PROPERTY(MediaControl_Parent) + + GstElement *parent = GST_ELEMENT(gst_element_get_parent(ELEMENT)); + GB.ReturnObject(MEDIA_get_control_from_element(parent, TRUE)); + if (parent) + gst_object_unref(parent); + +END_PROPERTY + +BEGIN_PROPERTY(MediaControl_Name) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(gst_element_get_name(ELEMENT)); + else + gst_element_set_name(ELEMENT, GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +BEGIN_PROPERTY(MediaControl_State) + + if (READ_PROPERTY) + { + GB.ReturnInteger(THIS->state); + /*GstState state; + + status = gst_element_get_state(ELEMENT, &state, NULL, GST_SECOND); + + if (status != GST_STATE_CHANGE_SUCCESS) + GB.ReturnInteger(-1); + else + GB.ReturnInteger(state);*/ + } + else + { + MEDIA_set_state(THIS, VPROP(GB_INTEGER), TRUE); + } + +END_PROPERTY + +BEGIN_METHOD(MediaControl_get, GB_STRING property) + + char *property = GB.ToZeroString(ARG(property)); + GParamSpec *desc; + GValue value = G_VALUE_INIT; + + desc = get_property(ELEMENT, property); + if (!desc) + return; + + //fprintf(stderr, "type = %s\n", g_type_name(desc->value_type)); + g_value_init(&value, desc->value_type); + g_object_get_property(G_OBJECT(ELEMENT), property, &value); + return_value(&value); + g_value_unset(&value); + GB.ReturnConvVariant(); + +END_METHOD + +BEGIN_METHOD(MediaControl_put, GB_VARIANT value; GB_STRING property) + + char *property = GB.ToZeroString(ARG(property)); + GB_VALUE *v = (GB_VALUE *)ARG(value); + + MEDIA_set_property(THIS, property, v); + +END_METHOD + +BEGIN_METHOD(MediaControl_LinkTo, GB_OBJECT dest; GB_STRING output; GB_STRING input) + + CMEDIACONTROL *dest = (CMEDIACONTROL *)VARG(dest); + char *output; + char *input; + + if (GB.CheckObject(dest)) + return; + + output = MISSING(output) ? NULL : GB.ToZeroString(ARG(output)); + if (output && !*output) output = NULL; + input = MISSING(input) ? NULL : GB.ToZeroString(ARG(input)); + if (input && !*input) input = NULL; + + if (output) + { + GstPad *pad = gst_element_get_static_pad (ELEMENT, output); + if (pad) + { + if (GST_PAD_IS_SRC(pad)) + { + GstPad *peer = gst_pad_get_peer(pad); + gst_pad_unlink(pad, peer); + gst_object_unref(peer); + } + gst_object_unref(pad); + } + } + + if (!gst_element_link_pads(ELEMENT, output, dest->elt, input)) + GB.Error("Unable to link controls"); + +END_METHOD + +BEGIN_METHOD(MediaControl_LinkLaterTo, GB_OBJECT dest) + + CMEDIACONTROL *dest = (CMEDIACONTROL *)VARG(dest); + + if (GB.CheckObject(dest)) + return; + + GB.Unref(POINTER(&THIS->dest)); + GB.Ref(dest); + THIS->dest = dest; + g_signal_connect(ELEMENT, "pad-added", G_CALLBACK(cb_pad_added), THIS); + +END_METHOD + +static GstIteratorResult iterator_next_pad(GstIterator *iter, GstPad **pad) +{ + GstIteratorResult ret; + GValue value = G_VALUE_INIT; + + ret = gst_iterator_next(iter, &value); + if (ret == GST_ITERATOR_OK) + { + if (G_VALUE_HOLDS_BOXED(&value)) + *pad = g_value_get_boxed(&value); + else + *pad = (GstPad *)g_value_get_object(&value); + } + + return ret; +} + +static void fill_pad_list(GB_ARRAY array, GstIterator *iter) +{ + bool done = FALSE; + GstPad *pad; + char *name; + + while (!done) + { + switch (iterator_next_pad(iter, &pad)) + { + case GST_ITERATOR_OK: + name = gst_pad_get_name(pad); + *((char **)GB.Array.Add(array)) = GB.NewZeroString(name); + g_free(name); + gst_object_unref(pad); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync(iter); + break; + case GST_ITERATOR_ERROR: + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + + gst_iterator_free(iter); +} + +BEGIN_PROPERTY(MediaControl_Inputs) + + GstIterator *iter; + GB_ARRAY array; + + GB.Array.New(&array, GB_T_STRING, 0); + iter = gst_element_iterate_sink_pads(ELEMENT); + fill_pad_list(array, iter); + GB.ReturnObject(array); + +END_PROPERTY + +BEGIN_PROPERTY(MediaControl_Outputs) + + GstIterator *iter; + GB_ARRAY array; + + GB.Array.New(&array, GB_T_STRING, 0); + iter = gst_element_iterate_src_pads(ELEMENT); + fill_pad_list(array, iter); + GB.ReturnObject(array); + +END_PROPERTY + +#if 0 +static void closure_marshal(GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + GObject *src; + CMEDIACONTROL *_object; + CMEDIASIGNALARGUMENTS *arg; + + src = g_value_peek_pointer (param_values + 0); + _object = get_control_from_element(src); + + arg = GB.New(GB.FindClass("MediaSignalArguments"), NULL, NULL); + arg->n_param_values = n_param_values; + arg->param_values = param_values; + + GB.Ref(arg); + GB.Raise(THIS, EVENT_Signal, 1, GB_T_OBJECT, arg); + MEDIA_raise_event_arg(THIS, EVENT_Signal, ); + + arg->n_param_values = 0; + arg->param_values = NULL; + GB.Unref(POINTER(&arg)); +} + +static GClosure *get_closure() +{ + static GClosure *closure = NULL; + + if (!closure) + { + closure = g_closure_new_simple(sizeof(GClosure), NULL); + g_closure_set_marshal(closure, closure_marshal); + } + + return closure; +} + +BEGIN_METHOD(MediaControl_Activate, GB_STRING signal) + + char *signal = GB.ToZeroString(ARG(signal)); + GClosure *closure = get_closure(); + + if (g_signal_handler_find(ELEMENT, G_SIGNAL_MATCH_CLOSURE | G_SIGNAL_MATCH_ID, g_signal_lookup(signal, G_OBJECT_TYPE(ELEMENT)), (GQuark)0, closure, NULL, NULL)) + { + GB.Error("Signal is already activated"); + return; + } + + g_signal_connect_closure(ELEMENT, GB.ToZeroString(ARG(signal)), closure, FALSE); + +END_METHOD +#endif + +BEGIN_METHOD(MediaControl_SetWindow, GB_OBJECT control; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + void *control = VARG(control); + long wid; + int x, y, w, h; + + if (!GST_IS_VIDEO_OVERLAY(ELEMENT)) + { + GB.Error("Not supported on this control"); + return; + } + + if (control && GB.CheckObject(control)) + return; + + if (control) + { + wid = MAIN_get_x11_handle(control); + if (wid == 0) + return; + } + else + wid = 0; + + gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(ELEMENT), (guintptr)wid); + + if (wid && !MISSING(x) && !MISSING(y) && !MISSING(w) && !MISSING(h)) + { + x = VARG(x); + y = VARG(y); + w = VARG(w); + h = VARG(h); + + if (w > 0 && h > 0) + gst_video_overlay_set_render_rectangle(GST_VIDEO_OVERLAY(ELEMENT), x, y, w, h); + } + gst_video_overlay_expose(GST_VIDEO_OVERLAY(ELEMENT)); + +END_PROPERTY + +#if 0 +BEGIN_PROPERTY(MediaControl_Protocols) + + char **protocols; + GB_ARRAY array; + + if (!GST_IS_URI_HANDLER(ELEMENT)) + { + GB.ReturnNull(); + return; + } + + protocols = (char **)gst_uri_handler_get_protocols(GST_URI_HANDLER(ELEMENT)); + GB.Array.New(&array, GB_T_STRING, 0); + while (*protocols) + { + *((char **)GB.Array.Add(array)) = GB.NewZeroString(*protocols); + protocols++; + } + GB.ReturnObject(array); + +END_PROPERTY +#endif + +BEGIN_METHOD(MediaControl_GetLink, GB_STRING name) + + bool done = FALSE; + GstIterator *iter; + GstPad *pad; + char *name; + char *search; + CMEDIALINK *link = NULL; + + search = GB.ToZeroString(ARG(name)); + iter = gst_element_iterate_pads(ELEMENT); + + while (!done) + { + switch (iterator_next_pad(iter, &pad)) + { + case GST_ITERATOR_OK: + name = gst_pad_get_name(pad); + if (strcmp(name, search) == 0) + { + link = create_link(pad); + done = TRUE; + break; + } + g_free(name); + gst_object_unref(pad); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync(iter); + break; + case GST_ITERATOR_ERROR: + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + + gst_iterator_free(iter); + GB.ReturnObject(link); + +END_METHOD + +BEGIN_METHOD_VOID(MediaControl_GetLastImage) + + GB.ReturnObject(get_last_image(THIS)); + +END_PROPERTY + + +//---- MediaFilter -------------------------------------------------------- + +BEGIN_PROPERTY(MediaFilter_Filter) + + if (READ_PROPERTY) + MEDIA_return_property(THIS, "caps"); + else + MEDIA_set_property(THIS, "caps", PROP(GB_VALUE)); + +END_PROPERTY + +//---- MediaContainerChildren --------------------------------------------- + +BEGIN_PROPERTY(MediaContainerChildren_Count) + + GB.ReturnInteger(gst_child_proxy_get_children_count(GST_CHILD_PROXY(ELEMENT))); + +END_PROPERTY + +BEGIN_METHOD(MediaContainerChildren_get, GB_INTEGER index) + + int count = gst_child_proxy_get_children_count(GST_CHILD_PROXY(ELEMENT)); + int index = VARG(index); + + if (index < 0 || index >= count) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(MEDIA_get_control_from_element(gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(ELEMENT), index), TRUE)); + +END_PROPERTY + +BEGIN_METHOD_VOID(MediaContainerChildren_next) + + int count = gst_child_proxy_get_children_count(GST_CHILD_PROXY(ELEMENT)); + int *index = (int *)GB.GetEnum(); + + if (*index < 0 || *index >= count) + GB.StopEnum(); + else + { + GB.ReturnObject(MEDIA_get_control_from_element(gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(ELEMENT), *index), TRUE)); + (*index)++; + } + +END_PROPERTY + +//---- MediaContainer ----------------------------------------------------- + +static bool add_input_output(void *_object, CMEDIACONTROL *child, char *name, int direction, const char *dir_error, const char *unknown_error) +{ + GstPad *pad; + GstIterator *iter; + GstIteratorResult res; + + if (GB.CheckObject(child)) + return TRUE; + + if (!name) + { + if (direction == GST_PAD_SINK) + iter = gst_element_iterate_sink_pads(child->elt); + else + iter = gst_element_iterate_src_pads(child->elt); + + for(;;) + { + res = iterator_next_pad(iter, &pad); + if (res == GST_ITERATOR_RESYNC) + gst_iterator_resync(iter); + else + break; + } + + gst_iterator_free(iter); + + if (res != GST_ITERATOR_OK) + { + GB.Error(unknown_error); + return TRUE; + } + } + else + { + pad = gst_element_get_static_pad(child->elt, name); + if (!pad) + { + GB.Error(unknown_error); + return TRUE; + } + + if (gst_pad_get_direction(pad) != direction) + { + gst_object_unref (GST_OBJECT(pad)); + GB.Error(dir_error); + return TRUE; + } + } + + gst_element_add_pad(ELEMENT, gst_ghost_pad_new(name, pad)); + gst_object_unref(GST_OBJECT(pad)); + + return FALSE; +} + +// Just there for the documentation wiki! + +BEGIN_METHOD_VOID(MediaContainer_new) + +END_METHOD + +BEGIN_METHOD(MediaContainer_AddInput, GB_OBJECT child; GB_STRING name) + + add_input_output(THIS, (CMEDIACONTROL *)VARG(child), MISSING(name) ? NULL : GB.ToZeroString(ARG(name)), GST_PAD_SINK, "Not an input", "Unknown input"); + +END_METHOD + +BEGIN_METHOD(MediaContainer_AddOutput, GB_OBJECT child; GB_STRING name) + + add_input_output(THIS, (CMEDIACONTROL *)VARG(child), MISSING(name) ? NULL : GB.ToZeroString(ARG(name)), GST_PAD_SRC, "Not an output", "Unknown output"); + +END_METHOD + +//---- MediaPipeline ------------------------------------------------------ + +DECLARE_EVENT(EVENT_Start); +DECLARE_EVENT(EVENT_End); +DECLARE_EVENT(EVENT_Message); +DECLARE_EVENT(EVENT_Tag); +DECLARE_EVENT(EVENT_Event); +DECLARE_EVENT(EVENT_Buffering); +DECLARE_EVENT(EVENT_Duration); +DECLARE_EVENT(EVENT_Progress); +DECLARE_EVENT(EVENT_AboutToFinish); + +static int cb_message(CMEDIAPIPELINE *_object) +{ + GstMessage *msg; + GstMessageType type; + int msg_type; + GstBus *bus; + CMEDIACONTROL *control; + + if (THIS_PIPELINE->in_message) + return FALSE; + + THIS_PIPELINE->in_message = TRUE; + + bus = gst_pipeline_get_bus(PIPELINE); + + for(;;) + { + msg = gst_bus_pop(bus); + if (msg == NULL) + break; + + type = GST_MESSAGE_TYPE(msg); + control = MEDIA_get_control_from_element(GST_MESSAGE_SRC(msg), FALSE); + + if (type == GST_MESSAGE_APPLICATION && control) + { + //CMEDIACONTROL *target = (CMEDIACONTROL *)g_value_get_pointer(gst_structure_get_value(gst_message_get_structure(msg), "control")); + int event = g_value_get_int(gst_structure_get_value(gst_message_get_structure(msg), "event")); + GB.Raise(control, event, 0); + } + else if (type == GST_MESSAGE_STATE_CHANGED && control) + { + GstState old_state, new_state; + + gst_message_parse_state_changed(msg, &old_state, &new_state, NULL); + control->state = new_state; + if (new_state == GST_STATE_NULL) + control->error = FALSE; + GB.Raise(control, EVENT_State, 0); + } + else //if (GST_MESSAGE_SRC(msg) == GST_OBJECT(PIPELINE)) + { + if (GB.CanRaise(THIS, EVENT_Event)) + { + CMEDIAMESSAGE *ob = create_message(msg); + GB.Ref(ob); + + GB.Raise(THIS, EVENT_Event, 1, GB_T_OBJECT, ob); + + ob->message = NULL; + GB.Unref(POINTER(&ob)); + } + + switch (type) + { + case GST_MESSAGE_EOS: + THIS->eos = TRUE; + GB.Raise(THIS, EVENT_End, 0); + break; + + case GST_MESSAGE_ERROR: + case GST_MESSAGE_WARNING: + case GST_MESSAGE_INFO: + { + gchar *debug; + GError *error; + + if (type == GST_MESSAGE_ERROR) + { + gst_message_parse_error(msg, &error, &debug); + msg_type = 2; + if (control) + control->error = TRUE; + THIS->error = TRUE; + } + else if (type == GST_MESSAGE_WARNING) + { + gst_message_parse_warning(msg, &error, &debug); + msg_type = 1; + } + else + { + gst_message_parse_info(msg, &error, &debug); + msg_type = 0; + } + + g_free(debug); + + if (!control) + control = THIS; + + GB.Ref(control); + GB.Raise(THIS, EVENT_Message, 3, GB_T_OBJECT, control, GB_T_INTEGER, msg_type, GB_T_STRING, error->message, -1); + g_error_free(error); + GB.Unref(POINTER(&control)); + break; + } + + case GST_MESSAGE_TAG: + { + GstTagList *tags = NULL; + CMEDIATAGLIST *ob; + //char *list; + + gst_message_parse_tag(msg, &tags); + + //list = gst_tag_list_to_string(tags); + //fprintf(stderr, "--> %s\n", list); + //g_free(list); + + ob = create_tag_list(tags); + GB.Ref(ob); + + GB.Raise(THIS, EVENT_Tag, 1, GB_T_OBJECT, ob); + ob->tags = NULL; + GB.Unref(POINTER(&ob)); + + gst_tag_list_free(tags); + + break; + } + case GST_MESSAGE_BUFFERING: GB.Raise(THIS, EVENT_Buffering, 0); break; + case GST_MESSAGE_DURATION: GB.Raise(THIS, EVENT_Duration, 0); break; + case GST_MESSAGE_PROGRESS: GB.Raise(THIS, EVENT_Progress, 0); break; + + case GST_MESSAGE_STREAM_START: + THIS_PIPELINE->about_to_finish = FALSE; + THIS_PIPELINE->pos = 0; + gst_element_query_duration(ELEMENT, GST_FORMAT_TIME, &THIS_PIPELINE->duration); + GB.Raise(THIS, EVENT_Start, 0); + break; + + default: break; + } + } + + gst_message_unref(msg); + } + + gst_object_unref(bus); + + THIS_PIPELINE->in_message = FALSE; + + if (THIS->state == GST_STATE_PLAYING && !THIS->error) + { + gst_element_query_position(ELEMENT, GST_FORMAT_TIME, &THIS_PIPELINE->pos); + //fprintf(stderr, "%" PRId64 " / %" PRId64 "\n", THIS_PIPELINE->pos, THIS_PIPELINE->duration); + if (!THIS_PIPELINE->about_to_finish && THIS_PIPELINE->pos > (THIS_PIPELINE->duration - 2000000000)) + { + THIS_PIPELINE->about_to_finish = TRUE; + GB.Raise(THIS, EVENT_AboutToFinish, 0); + } + + } + + return FALSE; +} + +void MEDIA_stop_pipeline(CMEDIACONTROL *_object) +{ + int try; + + // "It is not allowed to post GST_MESSAGE_EOS when not in the PLAYING state." says the documentation + if ((THIS->state == GST_STATE_PLAYING) && !THIS->eos) + { + try = 0; + gst_element_send_event(ELEMENT, gst_event_new_eos()); + while (!THIS->eos) + { + try++; + if (try > 25) + { + fprintf(stderr, "gb.media: warning: could not catch end of stream\n"); + break; + } + cb_message(THIS_PIPELINE); + usleep(10000); + } + } + + MEDIA_set_state(THIS, GST_STATE_READY, TRUE); + cb_message(THIS_PIPELINE); +} + + +BEGIN_METHOD(MediaPipeline_new, GB_INTEGER polling) + + if (!_from_element) + { + int polling = VARGOPT(polling, 250); + + if (polling <= 0) + polling = 250; + else if (polling < 10) + polling = 10; + else if (polling >= 1000) + polling = 1000; + + THIS_PIPELINE->polling = polling; + THIS_PIPELINE->watch = GB.Every(polling, (GB_TIMER_CALLBACK)cb_message, (intptr_t)THIS); + } + +END_METHOD + +BEGIN_METHOD_VOID(MediaPipeline_free) + + MEDIA_stop_pipeline(THIS); + if (THIS_PIPELINE->watch) + { + //GB.Wait(THIS_PIPELINE->polling); + GB.Unref(POINTER(&THIS_PIPELINE->watch)); + } + +END_METHOD + +BEGIN_METHOD_VOID(MediaPipeline_Play) + + THIS->eos = FALSE; + MEDIA_set_state(THIS, GST_STATE_PLAYING, TRUE); + cb_message(THIS_PIPELINE); + +END_METHOD + +BEGIN_METHOD_VOID(MediaPipeline_Stop) + + MEDIA_stop_pipeline(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(MediaPipeline_Close) + + MEDIA_set_state(THIS, GST_STATE_NULL, TRUE); + cb_message(THIS_PIPELINE); + +END_METHOD + +BEGIN_METHOD_VOID(MediaPipeline_Pause) + + if (THIS->state != GST_STATE_PLAYING) + return; + + MEDIA_set_state(THIS, GST_STATE_PAUSED, TRUE); + cb_message(THIS_PIPELINE); + +END_METHOD + +BEGIN_PROPERTY(MediaPipeline_Position) + + if (READ_PROPERTY) + { + GB.ReturnFloat((double)(THIS_PIPELINE->pos / 1000) / 1E6); + } + else + { + gint64 pos = VPROP(GB_FLOAT) * 1E9; + + if (pos < 0) + pos = 0; + + gst_element_seek_simple(ELEMENT, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, (guint64)pos); + } + +END_PROPERTY + +BEGIN_PROPERTY(MediaPipeline_Duration) + + GB.ReturnFloat((double)(THIS_PIPELINE->duration / 1000) / 1E6); + +END_PROPERTY + +//---- Media -------------------------------------------------------------- + +BEGIN_METHOD(Media_Link, GB_OBJECT controls) + + GB_OBJECT *controls = ARG(controls); + int i; + CMEDIACONTROL *c1, *c2; + + if (GB.CheckObject(VARG(controls))) + return; + + // GB.NParam() returns 0 when MediaPipeline_Link has just two arguments + + for (i = 0; i <= GB.NParam(); i++) + { + c1 = VALUE(&controls[i]); + c2 = VALUE(&controls[i + 1]); + if ((i == 0 && GB.CheckObject(c1)) || GB.CheckObject(c2)) + return; + gst_element_link(c1->elt, c2->elt); + } + +END_METHOD + +BEGIN_METHOD(Media_Time, GB_FLOAT second) + + GB.ReturnLong(VARG(second) * 1E9); + +END_METHOD + +BEGIN_METHOD(Media_URL, GB_STRING path) + + char *path = GB.RealFileName(STRING(path), LENGTH(path)); + + path = g_filename_to_uri(path, NULL, NULL); + GB.ReturnNewZeroString(path); + g_free(path); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC MediaTagListDesc[] = +{ + GB_DECLARE("MediaTagList", sizeof(CMEDIATAGLIST)), + GB_NOT_CREATABLE(), + + GB_HOOK_CHECK(MediaTagList_check), + + GB_METHOD("_get", "v", MediaTagList_get, "(Name)s"), + GB_PROPERTY_READ("Tags", "String[]", MediaTagList_Tags), + + GB_END_DECLARE +}; + +GB_DESC MediaMessageDesc[] = +{ + GB_DECLARE("MediaMessage", sizeof(CMEDIAMESSAGE)), + GB_NOT_CREATABLE(), + + GB_HOOK_CHECK(MediaMessage_check), + + GB_PROPERTY_READ("Type", "i", MediaMessage_Type), + GB_PROPERTY_READ("Name", "s", MediaMessage_Name), + + + GB_METHOD("_get", "v", MediaMessage_get, "(Name)s"), + GB_METHOD("_next", "v", MediaMessage_next, NULL), + GB_PROPERTY_READ("Key", "s", MediaMessage_Key), + GB_PROPERTY_READ("Keys", "String[]", MediaMessage_Keys), + GB_PROPERTY_READ("Count", "i", MediaMessage_Count), + + + //Constants + GB_CONSTANT("Eos", "i", GST_MESSAGE_EOS), + GB_CONSTANT("Error", "i", GST_MESSAGE_ERROR), + GB_CONSTANT("Warning", "i", GST_MESSAGE_WARNING), + GB_CONSTANT("Info", "i", GST_MESSAGE_INFO), + GB_CONSTANT("Tag", "i", GST_MESSAGE_TAG), + GB_CONSTANT("Buffering", "i", GST_MESSAGE_BUFFERING), + GB_CONSTANT("StateChanged", "i", GST_MESSAGE_STATE_CHANGED), + GB_CONSTANT("StepStart", "i", GST_MESSAGE_STEP_START), + GB_CONSTANT("StepDone", "i", GST_MESSAGE_STEP_DONE), + GB_CONSTANT("ClockLost", "i", GST_MESSAGE_CLOCK_LOST), + GB_CONSTANT("NewClock", "i", GST_MESSAGE_NEW_CLOCK), + GB_CONSTANT("Status", "i", GST_MESSAGE_STREAM_STATUS), + GB_CONSTANT("Element", "i", GST_MESSAGE_ELEMENT), + GB_CONSTANT("SegmentDone", "i", GST_MESSAGE_SEGMENT_DONE), + GB_CONSTANT("DurationChanged", "i", GST_MESSAGE_DURATION_CHANGED), + GB_CONSTANT("Latency", "i", GST_MESSAGE_LATENCY), + GB_CONSTANT("StateAsync", "i", GST_MESSAGE_ASYNC_DONE), + GB_CONSTANT("RequestState", "i", GST_MESSAGE_REQUEST_STATE), + GB_CONSTANT("Qos", "i", GST_MESSAGE_QOS), + GB_CONSTANT("Progress", "i", GST_MESSAGE_PROGRESS), + GB_CONSTANT("Toc", "i", GST_MESSAGE_TOC), + GB_CONSTANT("Start", "i", GST_MESSAGE_STREAM_START), + + GB_END_DECLARE +}; + + +GB_DESC MediaLinkDesc[] = +{ + GB_DECLARE("MediaLink", sizeof(CMEDIALINK)), + GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, MediaLink_free, NULL), + + GB_PROPERTY_READ("Name", "s", MediaLink_Name), + GB_PROPERTY_READ("Peer", "MediaControl", MediaLink_Peer), + GB_PROPERTY_READ("Input", "s", MediaLink_Input), + GB_PROPERTY_READ("Output", "s", MediaLink_Output), + + GB_END_DECLARE +}; + + +GB_DESC MediaControlDesc[] = +{ + GB_DECLARE("MediaControl", sizeof(CMEDIACONTROL)), + + GB_METHOD("_new", NULL, MediaControl_new, "[(Parent)MediaContainer;(Type)s]"), + GB_METHOD("_free", NULL, MediaControl_free, NULL), + + GB_PROPERTY("Name", "s", MediaControl_Name), + GB_PROPERTY_READ("Type", "s", MediaControl_Type), + GB_PROPERTY_READ("Parent", "MediaContainer", MediaControl_Parent), + GB_PROPERTY("State", "i", MediaControl_State), + GB_PROPERTY("Tag", "v", MediaControl_Tag), + + GB_METHOD("_put", NULL, MediaControl_put, "(Value)v(Property)s"), + GB_METHOD("_get", "v", MediaControl_get, "(Property)s"), + + GB_METHOD("LinkTo", NULL, MediaControl_LinkTo, "(Target)MediaControl;[(Output)s(Input)s]"), + GB_METHOD("LinkLaterTo", NULL, MediaControl_LinkLaterTo, "(Target)MediaControl;"), + + GB_PROPERTY_READ("Inputs", "String[]", MediaControl_Inputs), + GB_PROPERTY_READ("Outputs", "String[]", MediaControl_Outputs), + + GB_METHOD("GetLink", "MediaLink", MediaControl_GetLink, "(Name)s"), + + GB_METHOD("SetWindow", NULL, MediaControl_SetWindow, "(Control)Control;[(X)i(Y)i(Width)i(Height)i]"), + GB_METHOD("GetLastImage", "Image", MediaControl_GetLastImage, NULL), + + GB_EVENT("State", NULL, NULL, &EVENT_State), + //GB_EVENT("Signal", NULL, "(Arg)MediaSignalArguments", &EVENT_Signal), + + //GB_PROPERTY_READ("Protocols", "String[]", MediaControl_Protocols), + + GB_END_DECLARE +}; + +GB_DESC MediaFilterDesc[] = +{ + GB_DECLARE("MediaFilter", sizeof(CMEDIACONTROL)), + GB_INHERITS("MediaControl"), + + //GB_METHOD("_new", NULL, MediaFilter_new, NULL), + + GB_PROPERTY("Filter", "s", MediaFilter_Filter), + + GB_END_DECLARE +}; + +GB_DESC MediaContainerChildrenDesc[] = +{ + GB_DECLARE_VIRTUAL(".MediaContainer.Children"), + + GB_PROPERTY_READ("Count", "i", MediaContainerChildren_Count), + GB_METHOD("_get", "MediaControl", MediaContainerChildren_get, "(Index)i"), + GB_METHOD("_next", "MediaControl", MediaContainerChildren_next, NULL), + + GB_END_DECLARE +}; + +GB_DESC MediaContainerDesc[] = +{ + GB_DECLARE("MediaContainer", sizeof(CMEDIACONTAINER)), + GB_INHERITS("MediaControl"), + + GB_METHOD("_new", NULL, MediaContainer_new, NULL), + + GB_METHOD("AddInput", NULL, MediaContainer_AddInput, "(Child)MediaControl;[(Name)s]"), + GB_METHOD("AddOutput", NULL, MediaContainer_AddOutput, "(Child)MediaControl;[(Name)s]"), + + GB_PROPERTY_SELF("Children", ".MediaContainer.Children"), + + GB_END_DECLARE +}; + +GB_DESC MediaPipelineDesc[] = +{ + GB_DECLARE("MediaPipeline", sizeof(CMEDIAPIPELINE)), + GB_INHERITS("MediaContainer"), + + GB_METHOD("_new", NULL, MediaPipeline_new, "[(Polling)i]"), + GB_METHOD("_free", NULL, MediaPipeline_free, NULL), + + /*GB_CONSTANT("Null", "i", GST_STATE_NULL), + GB_CONSTANT("Ready", "i", GST_STATE_READY), + GB_CONSTANT("Paused", "i", GST_STATE_PAUSED), + GB_CONSTANT("Playing", "i", GST_STATE_PLAYING), + + GB_CONSTANT("Info", "i", 0), + GB_CONSTANT("Warning", "i", 1), + GB_CONSTANT("Error", "i", 2),*/ + + GB_PROPERTY("Position", "f", MediaPipeline_Position), + GB_PROPERTY_READ("Duration", "f", MediaPipeline_Duration), + GB_PROPERTY_READ("Length", "f", MediaPipeline_Duration), + + GB_METHOD("Play", NULL, MediaPipeline_Play, NULL), + GB_METHOD("Stop", NULL, MediaPipeline_Stop, NULL), + GB_METHOD("Pause", NULL, MediaPipeline_Pause, NULL), + GB_METHOD("Close", NULL, MediaPipeline_Close, NULL), + + GB_EVENT("Start", NULL, NULL, &EVENT_Start), + GB_EVENT("End", NULL, NULL, &EVENT_End), + GB_EVENT("Message", NULL, "(Source)MediaControl;(Type)i(Message)s", &EVENT_Message), + GB_EVENT("Tag", NULL, "(TagList)MediaTagList;", &EVENT_Tag), + GB_EVENT("Event", NULL, "(Message)MediaMessage;", &EVENT_Event), + GB_EVENT("Buffering", NULL, NULL, &EVENT_Buffering), + GB_EVENT("Duration", NULL, NULL, &EVENT_Duration), + GB_EVENT("Progress", NULL, NULL, &EVENT_Progress), + GB_EVENT("AboutToFinish", NULL, NULL, &EVENT_AboutToFinish), + + GB_END_DECLARE +}; + +GB_DESC MediaDesc[] = +{ + GB_DECLARE_VIRTUAL("Media"), + + GB_CONSTANT("Unknown", "i", -1), + + GB_CONSTANT("Null", "i", GST_STATE_NULL), + GB_CONSTANT("Ready", "i", GST_STATE_READY), + GB_CONSTANT("Paused", "i", GST_STATE_PAUSED), + GB_CONSTANT("Playing", "i", GST_STATE_PLAYING), + + GB_CONSTANT("Info", "i", 0), + GB_CONSTANT("Warning", "i", 1), + GB_CONSTANT("Error", "i", 2), + + GB_STATIC_METHOD("Link", NULL, Media_Link, "(FirstControl)MediaControl;(SecondControl)MediaControl;."), + GB_STATIC_METHOD("Time", "l", Media_Time, "(Seconds)f"), + GB_STATIC_METHOD("URL", "s", Media_URL, "(Path)s"), + + GB_END_DECLARE +}; + diff --git a/gb.media/src/c_media.h b/gb.media/src/c_media.h new file mode 100644 index 00000000..c5731263 --- /dev/null +++ b/gb.media/src/c_media.h @@ -0,0 +1,136 @@ +/*************************************************************************** + + c_media.h + + gb.media component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_MEDIA_H +#define __C_MEDIA_H + +#include "main.h" + +#ifndef __C_MEDIA_C + +//extern GB_DESC MediaSignalArgumentsDesc[]; +extern GB_DESC MediaTagListDesc[]; +extern GB_DESC MediaMessageDesc[]; +extern GB_DESC MediaLinkDesc[]; +extern GB_DESC MediaControlDesc[]; +extern GB_DESC MediaFilterDesc[]; +extern GB_DESC MediaContainerChildrenDesc[]; +extern GB_DESC MediaContainerDesc[]; +extern GB_DESC MediaPipelineDesc[]; +extern GB_DESC MediaDesc[]; + +#else + +#define THIS ((CMEDIACONTROL *)_object) +#define THIS_ARG ((CMEDIASIGNALARGUMENTS *)_object) +#define ELEMENT THIS->elt +#define PIPELINE ((GstPipeline *)THIS->elt) + +#define THIS_TAGLIST ((CMEDIATAGLIST *)_object) +#define THIS_MESSAGE ((CMEDIAMESSAGE *)_object) +#define THIS_PIPELINE ((CMEDIAPIPELINE *)_object) + +#define THIS_LINK ((CMEDIALINK *)_object) +#define LINK THIS_LINK->pad + +#endif + +typedef + struct { + GB_BASE ob; + GstPad *pad; + } + CMEDIALINK; + +typedef + struct { + GB_BASE ob; + GstElement *elt; + void *dest; + GB_VARIANT_VALUE tag; + unsigned state : 3; + unsigned error : 1; + unsigned borrow : 1; + unsigned eos : 1; + } + CMEDIACONTROL; + +typedef + CMEDIACONTROL CMEDIACONTAINER; + +typedef + struct { + CMEDIACONTROL control; + GB_TIMER *watch; + int polling; + gint64 pos; + gint64 duration; + unsigned in_message : 1; + unsigned about_to_finish : 1; + } + CMEDIAPIPELINE; + +typedef + struct { + GB_BASE ob; + GstTagList *tags; + } + CMEDIATAGLIST; + +typedef + struct { + GB_BASE ob; + GstMessage *message; + const char *lastKey; + } + CMEDIAMESSAGE; + +#if 0 +typedef + struct { + GB_BASE ob; + guint n_param_values; + const GValue *param_values; + } + CMEDIASIGNALARGUMENTS; +#endif + +#define TO_SECOND(_time) ((double)((_time) / 1000) / 1E6) +#define TO_TIME(_second) ((gint64)((_second) * 1E9)) + +void MEDIA_raise_event(void *_object, int event); +CMEDIACONTROL *MEDIA_get_control_from_element(void *element, bool create); +bool MEDIA_set_state(void *_object, int state, bool error); + +bool MEDIA_get_flag(void *element, char *property, int flag); +void MEDIA_set_flag(void *element, char *property, int flag, bool value); + +void MEDIA_set_property(void *_object, const char *property, GB_VALUE *v); + +GB_IMG *MEDIA_get_image_from_sample(GstSample *sample, bool convert); + +void MEDIA_stop_pipeline(CMEDIACONTROL *_object); + +#endif /* __C_MEDIA_H */ diff --git a/gb.media/src/c_mediaplayer.c b/gb.media/src/c_mediaplayer.c new file mode 100644 index 00000000..e2a12f60 --- /dev/null +++ b/gb.media/src/c_mediaplayer.c @@ -0,0 +1,658 @@ +/*************************************************************************** + + c_mediaplayer.c + + gb.media component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_MEDIAPLAYER_C + +#include "c_mediaplayer.h" + +// GStreamer playbin documents a enum that is not defined anywhere!? +// If someone knows where I can find it... + +typedef enum { + GST_PLAY_FLAG_VIDEO = (1 << 0), + GST_PLAY_FLAG_AUDIO = (1 << 1), + GST_PLAY_FLAG_TEXT = (1 << 2), + GST_PLAY_FLAG_VIS = (1 << 3), + GST_PLAY_FLAG_SOFT_VOLUME = (1 << 4), + GST_PLAY_FLAG_NATIVE_AUDIO = (1 << 5), + GST_PLAY_FLAG_NATIVE_VIDEO = (1 << 6), + GST_PLAY_FLAG_DOWNLOAD = (1 << 7), + GST_PLAY_FLAG_BUFFERING = (1 << 8), + GST_PLAY_FLAG_DEINTERLACE = (1 << 9) +} GstPlayFlags; + + +static int get_int(CMEDIAPLAYER *_object, char *name) +{ + int value; + g_object_get(G_OBJECT(ELEMENT), name, &value, NULL); + return value; +} + +static void set_int(CMEDIAPLAYER *_object, char *name, int value) +{ + g_object_set(G_OBJECT(ELEMENT), name, value, NULL); +} + +static int get_int64(CMEDIAPLAYER *_object, char *name) +{ + gint64 value; + g_object_get(G_OBJECT(ELEMENT), name, &value, NULL); + return value; +} + +static void set_int64(CMEDIAPLAYER *_object, char *name, gint64 value) +{ + g_object_set(G_OBJECT(ELEMENT), name, value, NULL); +} + +static double get_double(CMEDIAPLAYER *_object, char *name) +{ + double value; + g_object_get(G_OBJECT(ELEMENT), name, &value, NULL); + return value; +} + +static void set_double(CMEDIAPLAYER *_object, char *name, double value) +{ + g_object_set(G_OBJECT(ELEMENT), name, value, NULL); +} + +static gboolean get_boolean(CMEDIAPLAYER *_object, char *name) +{ + gboolean value; + g_object_get(G_OBJECT(ELEMENT), name, &value, NULL); + return value; +} + +static void set_boolean(CMEDIAPLAYER *_object, char *name, gboolean value) +{ + g_object_set(G_OBJECT(ELEMENT), name, value, NULL); +} + +static char *get_string(CMEDIAPLAYER *_object, char *name) +{ + gchar *value; + g_object_get(G_OBJECT(ELEMENT), name, &value, NULL); + return value; +} + +static void set_string(CMEDIAPLAYER *_object, char *name, gchar *value) +{ + g_object_set(G_OBJECT(ELEMENT), name, value, NULL); +} + +static CMEDIACONTROL *get_control(CMEDIAPLAYER *_object, char *name) +{ + GstElement *value; + g_object_get(G_OBJECT(ELEMENT), name, &value, NULL); + return MEDIA_get_control_from_element(value, TRUE); +} + +static void set_control(CMEDIAPLAYER *_object, char *name, CMEDIACONTROL *control) +{ + GstElement *elt; + GstBin *parent; + + if (!control) + { + g_object_set(G_OBJECT(ELEMENT), name, NULL, NULL); + return; + } + + elt = control->elt; + parent = GST_BIN(gst_element_get_parent(elt)); + if (parent) + { + gst_object_ref(elt); + gst_bin_remove(parent, elt); + } + + g_object_set(G_OBJECT(ELEMENT), name, elt, NULL); + + if (parent) + gst_object_unref(elt); +} + +#define set_flag(_object, _flag, _value) MEDIA_set_flag(ELEMENT, "flags", _flag, _value) + +#define IMPLEMENT_FLAG(_func, _flag) \ +BEGIN_PROPERTY(_func) \ +\ + if (READ_PROPERTY) \ + GB.ReturnBoolean(MEDIA_get_flag(ELEMENT, "flags", _flag)); \ + else \ + MEDIA_set_flag(ELEMENT, "flags", _flag, VPROP(GB_BOOLEAN)); \ +\ +END_PROPERTY + +static GB_IMG *get_frame(void *_object) +{ + GstElement *play = GST_ELEMENT(ELEMENT); + GstSample *sample; + GstCaps *to_caps; + const char *format; + + //g_return_val_if_fail (play != NULL, NULL); + //g_return_val_if_fail (GST_IS_ELEMENT (play), NULL); + + switch (IMAGE.GetDefaultFormat()) + { + case GB_IMAGE_BGRA: + case GB_IMAGE_BGRP: + format = "BGR"; + break; + + case GB_IMAGE_RGBA: + case GB_IMAGE_RGBP: + format = "RGB"; + break; + + default: + GB.Error("Unsupported default image format"); + return NULL; + } + + /* our desired output format (RGB or BGR) */ + to_caps = gst_caps_new_simple ("video/x-raw", + "format", G_TYPE_STRING, format, + /* Note: we don't ask for a specific width/height here, so that + * videoscale can adjust dimensions from a non-1/1 pixel aspect + * ratio to a 1/1 pixel-aspect-ratio. We also don't ask for a + * specific framerate, because the input framerate won't + * necessarily match the output framerate if there's a deinterlacer + * in the pipeline. */ + "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, + NULL); + + /* get frame */ + g_signal_emit_by_name (play, "convert-sample", to_caps, &sample); + gst_caps_unref(to_caps); + + return MEDIA_get_image_from_sample(sample, FALSE); +} + + +//---- MediaPlayerAudio -------------------------------------------------- + +BEGIN_PROPERTY(MediaPlayerAudio_Count) + + GB.ReturnInteger(get_int(THIS, "n-audio")); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerAudio_Current) + + if (READ_PROPERTY) + GB.ReturnInteger(get_int(THIS, "current-audio")); + else + set_int(THIS, "current-audio", VPROP(GB_INTEGER)); + +END_PROPERTY + +IMPLEMENT_FLAG(MediaPlayerAudio_Enabled, GST_PLAY_FLAG_AUDIO) +IMPLEMENT_FLAG(MediaPlayerAudio_SoftwareVolume, GST_PLAY_FLAG_SOFT_VOLUME) +IMPLEMENT_FLAG(MediaPlayerAudio_NativeOnly, GST_PLAY_FLAG_NATIVE_AUDIO) + +BEGIN_PROPERTY(MediaPlayerAudio_Volume) + + if (READ_PROPERTY) + GB.ReturnFloat(get_double(THIS, "volume")); + else + set_double(THIS, "volume", VPROP(GB_FLOAT)); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerAudio_Mute) + + if (READ_PROPERTY) + GB.ReturnBoolean(get_boolean(THIS, "mute")); + else + set_boolean(THIS, "mute", VPROP(GB_BOOLEAN) != 0); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerAudio_Offset) + + if (READ_PROPERTY) + GB.ReturnFloat(TO_SECOND(get_int64(THIS, "av-offset"))); + else + set_int64(THIS, "av-offset", TO_TIME(VPROP(GB_FLOAT))); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerAudio_Output) + + if (READ_PROPERTY) + GB.ReturnObject(get_control(THIS, "audio-sink")); + else + set_control(THIS, "audio-sink", VPROP(GB_OBJECT)); + +END_PROPERTY + +//---- MediaPlayerVideo -------------------------------------------------- + +BEGIN_PROPERTY(MediaPlayerVideo_Count) + + GB.ReturnInteger(get_int(THIS, "n-video")); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerVideo_Current) + + if (READ_PROPERTY) + GB.ReturnInteger(get_int(THIS, "current-video")); + else + set_int(THIS, "current-video", VPROP(GB_INTEGER)); + +END_PROPERTY + +IMPLEMENT_FLAG(MediaPlayerVideo_Enabled, GST_PLAY_FLAG_VIDEO) +IMPLEMENT_FLAG(MediaPlayerVideo_NativeOnly, GST_PLAY_FLAG_NATIVE_VIDEO) +IMPLEMENT_FLAG(MediaPlayerVideo_Deinterlace, GST_PLAY_FLAG_DEINTERLACE) + +BEGIN_PROPERTY(MediaPlayerVideo_Output) + + if (READ_PROPERTY) + GB.ReturnObject(get_control(THIS, "video-sink")); + else + set_control(THIS, "video-sink", VPROP(GB_OBJECT)); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerVideo_Visualisation) + + if (READ_PROPERTY) + GB.ReturnObject(get_control(THIS, "vis-plugin")); + else + { + CMEDIACONTROL *vis = VPROP(GB_OBJECT); + //CMEDIACONTROL *old = get_control(THIS, "vis-plugin"); + bool playing; + + set_flag(THIS, GST_PLAY_FLAG_VIS, FALSE); + + playing = THIS_CONTROL->state == GST_STATE_PLAYING; + if (playing) + MEDIA_set_state(THIS, GST_STATE_PAUSED, FALSE); + + //set_control(THIS, "vis-plugin", NULL); + //if (vis) + set_control(THIS, "vis-plugin", vis); + if (vis) + set_flag(THIS, GST_PLAY_FLAG_VIS, TRUE); + + if (playing) + MEDIA_set_state(THIS, GST_STATE_PLAYING, FALSE); + } + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerVideo_Image) + + GB.ReturnObject(get_frame(THIS)); + +END_PROPERTY + +//---- MediaPlayerSubtitles ---------------------------------------------- + +BEGIN_PROPERTY(MediaPlayerSubtitles_Count) + + GB.ReturnInteger(get_int(THIS, "n-text")); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerSubtitles_Current) + + if (READ_PROPERTY) + GB.ReturnInteger(get_int(THIS, "current-text")); + else + set_int(THIS, "current-text", VPROP(GB_INTEGER)); + +END_PROPERTY + +IMPLEMENT_FLAG(MediaPlayerSubtitles_Enabled, GST_PLAY_FLAG_TEXT) + +BEGIN_PROPERTY(MediaPlayerSubtitles_Charset) + + if (READ_PROPERTY) + { + char *charset = get_string(THIS, "subtitle-encoding"); + GB.ReturnNewZeroString(charset); + g_free(charset); + } + else + set_string(THIS, "subtitle-encoding", GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerSubtitles_URL) + + if (READ_PROPERTY) + { + char *charset = get_string(THIS, "suburi"); + GB.ReturnNewZeroString(charset); + g_free(charset); + } + else + set_string(THIS, "suburi", GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerSubtitles_Output) + + if (READ_PROPERTY) + GB.ReturnObject(get_control(THIS, "text-sink")); + else + set_control(THIS, "text-sink", VPROP(GB_OBJECT)); + +END_PROPERTY + +//---- MediaPlayerBalanceChannel ----------------------------------------- + +static GstColorBalanceChannel *get_channel(void *_object) +{ + GList *channels = (GList *)gst_color_balance_list_channels(BALANCE); + GstColorBalanceChannel *channel = (GstColorBalanceChannel *)g_list_nth_data(channels, THIS->channel); + if (!channel) GB.Error(GB_ERR_ARG); + return channel; +} + +BEGIN_PROPERTY(MediaPlayerBalanceChannel_Name) + + GstColorBalanceChannel *channel = get_channel(THIS); + if (channel) + GB.ReturnNewZeroString(channel->label); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerBalanceChannel_Min) + + GstColorBalanceChannel *channel = get_channel(THIS); + if (channel) + GB.ReturnInteger(channel->min_value); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerBalanceChannel_Max) + + GstColorBalanceChannel *channel = get_channel(THIS); + if (channel) + GB.ReturnInteger(channel->max_value); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerBalanceChannel_Value) + + GstColorBalanceChannel *channel = get_channel(THIS); + if (!channel) + return; + + if (READ_PROPERTY) + GB.ReturnInteger(gst_color_balance_get_value(BALANCE, channel)); + else + gst_color_balance_set_value(BALANCE, channel, VPROP(GB_INTEGER)); + +END_PROPERTY + +//---- MediaPlayerBalance ------------------------------------------------ + +BEGIN_PROPERTY(MediaPlayerBalance_Count) + + GB.ReturnInteger(g_list_length((GList *)gst_color_balance_list_channels(BALANCE))); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayerBalance_Hardware) + + GB.ReturnBoolean(gst_color_balance_get_balance_type(BALANCE) == GST_COLOR_BALANCE_HARDWARE); + +END_PROPERTY + +BEGIN_METHOD(MediaPlayerBalance_get, GB_INTEGER index) + + GList *channels = (GList *)gst_color_balance_list_channels(BALANCE); + int index = VARG(index); + + if (index < 0 || index >= g_list_length(channels)) + GB.Error(GB_ERR_BOUND); + else + { + THIS->channel = index; + RETURN_SELF(); + } + +END_PROPERTY + +//---- MediaPlayer ------------------------------------------------------- + +DECLARE_EVENT(EVENT_AudioChanged); +DECLARE_EVENT(EVENT_SubtitlesChanged); +DECLARE_EVENT(EVENT_VideoChanged); +//DECLARE_EVENT(EVENT_SourceSetup); + +static void cb_about_to_finish(void *playbin, void *_object) +{ + g_mutex_lock(&THIS->next_uri_mutex); + if (THIS->next_uri) + { + set_string(THIS, "uri", THIS->next_uri); + GB.StoreString(NULL, &THIS->next_uri); + } + g_mutex_unlock(&THIS->next_uri_mutex); +} + +static void cb_audio_changed(void *playbin, void *_object) +{ + MEDIA_raise_event(THIS, EVENT_AudioChanged); +} + +static void cb_text_changed(void *playbin, void *_object) +{ + MEDIA_raise_event(THIS, EVENT_SubtitlesChanged); +} + +static void cb_video_changed(void *playbin, void *_object) +{ + MEDIA_raise_event(THIS, EVENT_VideoChanged); +} + +/*static void cb_source_setup(void *playbin, GstElement *source, void *_object) +{ + MEDIA_raise_event(THIS, EVENT_SourceSetup); +}*/ + +BEGIN_METHOD_VOID(MediaPlayer_new) + + g_mutex_init(&THIS->next_uri_mutex); + + g_signal_connect(ELEMENT, "about-to-finish", G_CALLBACK(cb_about_to_finish), THIS); + g_signal_connect(ELEMENT, "audio-changed", G_CALLBACK(cb_audio_changed), THIS); + g_signal_connect(ELEMENT, "text-changed", G_CALLBACK(cb_text_changed), THIS); + g_signal_connect(ELEMENT, "video-changed", G_CALLBACK(cb_video_changed), THIS); + //g_signal_connect(ELEMENT, "source-setup", G_CALLBACK(cb_source_setup), THIS); + +END_METHOD + +BEGIN_METHOD_VOID(MediaPlayer_free) + + g_mutex_lock(&THIS->next_uri_mutex); + GB.StoreString(NULL, &THIS->next_uri); + g_mutex_unlock(&THIS->next_uri_mutex); + g_mutex_clear(&THIS->next_uri_mutex); + +END_METHOD + +BEGIN_PROPERTY(MediaPlayer_ConnectionSpeed) + + if (READ_PROPERTY) + GB.ReturnLong(get_int64(THIS, "connection-speed")); + else + set_int64(THIS, "connection-speed", VPROP(GB_LONG)); + +END_PROPERTY + +IMPLEMENT_FLAG(MediaPlayer_ProgressiveDownload, GST_PLAY_FLAG_DOWNLOAD) +IMPLEMENT_FLAG(MediaPlayer_Buffering, GST_PLAY_FLAG_BUFFERING) + +BEGIN_PROPERTY(MediaPlayer_URL) + + if (READ_PROPERTY) + { + char *charset = get_string(THIS, "uri"); + GB.ReturnNewZeroString(charset); + g_free(charset); + } + else + { + MEDIA_stop_pipeline(THIS_CONTROL); + set_string(THIS, "uri", GB.ToZeroString(PROP(GB_STRING))); + } + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayer_NextURL) + + g_mutex_lock(&THIS->next_uri_mutex); + if (READ_PROPERTY) + GB.ReturnString(THIS->next_uri); + else + GB.StoreString(PROP(GB_STRING), &THIS->next_uri); + g_mutex_unlock(&THIS->next_uri_mutex); + +END_PROPERTY + +BEGIN_PROPERTY(MediaPlayer_Input) + + GB.ReturnObject(get_control(THIS, "source")); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC MediaPlayerAudioDesc[] = +{ + GB_DECLARE(".MediaPlayer.Audio", sizeof(CMEDIAPLAYER)), + GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Count", "i", MediaPlayerAudio_Count), + GB_PROPERTY("Current", "i", MediaPlayerAudio_Current), + GB_PROPERTY("Enabled", "b", MediaPlayerAudio_Enabled), + GB_PROPERTY("SoftwareVolume", "b", MediaPlayerAudio_SoftwareVolume), + GB_PROPERTY("NativeOnly", "b", MediaPlayerAudio_NativeOnly), + GB_PROPERTY("Volume", "f", MediaPlayerAudio_Volume), + GB_PROPERTY("Mute", "b", MediaPlayerAudio_Mute), + GB_PROPERTY("Offset", "f", MediaPlayerAudio_Offset), + GB_PROPERTY("Output", "MediaControl", MediaPlayerAudio_Output), + + GB_END_DECLARE +}; + +GB_DESC MediaPlayerVideoDesc[] = +{ + GB_DECLARE(".MediaPlayer.Video", sizeof(CMEDIAPLAYER)), + GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Count", "i", MediaPlayerVideo_Count), + GB_PROPERTY("Current", "i", MediaPlayerVideo_Current), + GB_PROPERTY("Enabled", "b", MediaPlayerVideo_Enabled), + GB_PROPERTY("NativeOnly", "b", MediaPlayerVideo_NativeOnly), + GB_PROPERTY("Deinterlace", "b", MediaPlayerVideo_Deinterlace), + GB_PROPERTY("Output", "MediaControl", MediaPlayerVideo_Output), + GB_PROPERTY("Visualisation", "MediaControl", MediaPlayerVideo_Visualisation), + GB_PROPERTY_READ("Image", "Image", MediaPlayerVideo_Image), + + GB_END_DECLARE +}; + +GB_DESC MediaPlayerSubtitlesDesc[] = +{ + GB_DECLARE(".MediaPlayer.Subtitles", sizeof(CMEDIAPLAYER)), + GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Count", "i", MediaPlayerSubtitles_Count), + GB_PROPERTY("Current", "i", MediaPlayerSubtitles_Current), + GB_PROPERTY("Enabled", "b", MediaPlayerSubtitles_Enabled), + GB_PROPERTY("Charset", "s", MediaPlayerSubtitles_Charset), + GB_PROPERTY("URL", "s", MediaPlayerSubtitles_URL), + GB_PROPERTY("Output", "MediaControl", MediaPlayerSubtitles_Output), + + GB_END_DECLARE +}; + +GB_DESC MediaPlayerBalanceChannelDesc[] = +{ + GB_DECLARE(".MediaPlayer.Balance.Channel", sizeof(CMEDIAPLAYER)), + GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Name", "s", MediaPlayerBalanceChannel_Name), + GB_PROPERTY_READ("Min", "i", MediaPlayerBalanceChannel_Min), + GB_PROPERTY_READ("Max", "i", MediaPlayerBalanceChannel_Max), + GB_PROPERTY("Value", "i", MediaPlayerBalanceChannel_Value), + + GB_END_DECLARE +}; + +GB_DESC MediaPlayerBalanceDesc[] = +{ + GB_DECLARE(".MediaPlayer.Balance", sizeof(CMEDIAPLAYER)), + GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Count", "i", MediaPlayerBalance_Count), + GB_PROPERTY_READ("Hardware", "b", MediaPlayerBalance_Hardware), + GB_METHOD("_get", ".MediaPlayer.Balance.Channel", MediaPlayerBalance_get, "(Channel)i"), + + GB_END_DECLARE +}; + +GB_DESC MediaPlayerDesc[] = +{ + GB_DECLARE("MediaPlayer", sizeof(CMEDIAPLAYER)), + GB_INHERITS("MediaPipeline"), + + GB_METHOD("_new", NULL, MediaPlayer_new, NULL), + GB_METHOD("_free", NULL, MediaPlayer_free, NULL), + + GB_PROPERTY_SELF("Audio", ".MediaPlayer.Audio"), + GB_PROPERTY_SELF("Video", ".MediaPlayer.Video"), + GB_PROPERTY_SELF("Subtitles", ".MediaPlayer.Subtitles"), + GB_PROPERTY_SELF("Balance", ".MediaPlayer.Balance"), + //GB_PROPERTY_SELF("Visualisation", ".MediaPlayer.Visualisation"), + + GB_PROPERTY("ConnectionSpeed", "l", MediaPlayer_ConnectionSpeed), + GB_PROPERTY("ProgressiveDownload", "b", MediaPlayer_ProgressiveDownload), + GB_PROPERTY("Buffering", "b", MediaPlayer_Buffering), + GB_PROPERTY("URL", "s", MediaPlayer_URL), + GB_PROPERTY("NextURL", "s", MediaPlayer_NextURL), + GB_PROPERTY_READ("Input", "MediaControl", MediaPlayer_Input), + + GB_EVENT("AudioChanged", NULL, NULL, &EVENT_AudioChanged), + GB_EVENT("SubtitlesChanged", NULL, NULL, &EVENT_SubtitlesChanged), + GB_EVENT("VideoChanged", NULL, NULL, &EVENT_VideoChanged), + //GB_EVENT("SourceSetup", NULL, NULL, &EVENT_SourceSetup), + + GB_END_DECLARE +}; + diff --git a/gb.media/src/c_mediaplayer.h b/gb.media/src/c_mediaplayer.h new file mode 100644 index 00000000..e11dc8ae --- /dev/null +++ b/gb.media/src/c_mediaplayer.h @@ -0,0 +1,62 @@ +/*************************************************************************** + + c_mediaplayer.h + + gb.media component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_MEDIAPLAYER_H +#define __C_MEDIAPLAYER_H + +#include "main.h" +#include "c_media.h" + +#include +#include + +#ifndef __C_MEDIAPLAYER_C + +extern GB_DESC MediaPlayerDesc[]; +extern GB_DESC MediaPlayerAudioDesc[]; +extern GB_DESC MediaPlayerVideoDesc[]; +extern GB_DESC MediaPlayerSubtitlesDesc[]; +extern GB_DESC MediaPlayerBalanceDesc[]; +extern GB_DESC MediaPlayerBalanceChannelDesc[]; + +#else + +#define THIS ((CMEDIAPLAYER *)_object) +#define THIS_CONTROL (&(THIS->base.control)) +#define ELEMENT ((GstPipeline *)(THIS_CONTROL->elt)) +#define BALANCE (GST_COLOR_BALANCE(ELEMENT)) + +#endif + +typedef + struct { + CMEDIAPIPELINE base; + int channel; + GMutex next_uri_mutex; + char *next_uri; + } + CMEDIAPLAYER; + +#endif /* __C_MEDIAPLAYER_H */ diff --git a/gb.media/src/c_mediavideo.c b/gb.media/src/c_mediavideo.c new file mode 100644 index 00000000..1329ed8f --- /dev/null +++ b/gb.media/src/c_mediavideo.c @@ -0,0 +1,73 @@ +/*************************************************************************** + + c_mediavideo.c + + gb.media component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_MEDIAVIDEO_C + +#include + +#include "c_mediavideo.h" + +BEGIN_METHOD_VOID(MediaVideo_new) + +END_METHOD + +//------------------------------------------------------------------------- + +#define IMPLEMENT_FLAG(_proc, _flag) \ +BEGIN_PROPERTY(MediaVideo_##_proc) \ +\ + GB.ReturnBoolean(MEDIA_get_flag(ELEMENT, "flags", _flag)); \ +\ +END_PROPERTY + +IMPLEMENT_FLAG(HasCapture, V4L2_CAP_VIDEO_CAPTURE) +IMPLEMENT_FLAG(HasOutput, V4L2_CAP_VIDEO_OUTPUT) +IMPLEMENT_FLAG(HasOverlay, V4L2_CAP_VIDEO_OVERLAY) +IMPLEMENT_FLAG(HasVBICapture, V4L2_CAP_VBI_CAPTURE) +IMPLEMENT_FLAG(HasVBIOutput, V4L2_CAP_VBI_OUTPUT) +IMPLEMENT_FLAG(HasTuner, V4L2_CAP_TUNER) +IMPLEMENT_FLAG(HasAudio, V4L2_CAP_AUDIO) + +GB_DESC MediaVideoDesc[] = +{ + GB_DECLARE("MediaVideo", sizeof(CMEDIAVIDEO)), + GB_INHERITS("MediaControl"), + + GB_METHOD("_new", NULL, MediaVideo_new, NULL), + + //GB_PROPERTY("Device", "s", MediaVideo_Device), + //GB_PROPERTY_READ("Name", "s", MediaVideo_Name), + + GB_PROPERTY_READ("HasCapture", "b", MediaVideo_HasCapture), + GB_PROPERTY_READ("HasOutput", "b", MediaVideo_HasOutput), + GB_PROPERTY_READ("HasOverlay", "b", MediaVideo_HasOverlay), + GB_PROPERTY_READ("HasVBICapture", "b", MediaVideo_HasVBICapture), + GB_PROPERTY_READ("HasVBIOutput", "b", MediaVideo_HasVBIOutput), + GB_PROPERTY_READ("HasTuner", "b", MediaVideo_HasTuner), + GB_PROPERTY_READ("HasAudio", "b", MediaVideo_HasAudio), + + GB_END_DECLARE +}; + diff --git a/gb.media/src/c_mediavideo.h b/gb.media/src/c_mediavideo.h new file mode 100644 index 00000000..f75f434f --- /dev/null +++ b/gb.media/src/c_mediavideo.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + c_mediavideo.h + + gb.media component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_MEDIAVIDEO_H +#define __C_MEDIAVIDEO_H + +#include "main.h" +#include "c_media.h" + +#include +#include + +#ifndef __C_MEDIAVIDEO_C + +extern GB_DESC MediaVideoDesc[]; + +#else + +#define THIS ((CMEDIAVIDEO *)_object) +#define ELEMENT ((GstElement *)THIS->elt) + +#endif + +typedef + CMEDIACONTROL + CMEDIAVIDEO; + +#endif /* __C_MEDIAVIDEO_H */ diff --git a/gb.media/src/gb.media.component b/gb.media/src/gb.media.component new file mode 100644 index 00000000..4edd9a70 --- /dev/null +++ b/gb.media/src/gb.media.component @@ -0,0 +1,4 @@ +[Component] +Author=Benoît Minisini +State=Stable +Require=gb.image \ No newline at end of file diff --git a/gb.media/src/main.c b/gb.media/src/main.c new file mode 100644 index 00000000..75d03199 --- /dev/null +++ b/gb.media/src/main.c @@ -0,0 +1,91 @@ +/*************************************************************************** + + main.c + + gb.media component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" +#include "c_media.h" +#include "c_mediaplayer.h" + +GB_INTERFACE GB EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + //MediaSignalArgumentsDesc, + MediaTagListDesc, + MediaMessageDesc, + MediaLinkDesc, + MediaControlDesc, + MediaFilterDesc, + MediaContainerChildrenDesc, + MediaContainerDesc, + MediaPipelineDesc, + MediaDesc, + MediaPlayerAudioDesc, + MediaPlayerVideoDesc, + MediaPlayerSubtitlesDesc, + MediaPlayerBalanceChannelDesc, + MediaPlayerBalanceDesc, + MediaPlayerDesc, + NULL +}; + +int MAIN_get_x11_handle(void *control) +{ + int (*get_handle)(void *) = NULL; + + if (!get_handle) + { + GB.Component.GetInfo("GET_HANDLE", (void **)&get_handle); + if (!get_handle) + { + GB.Error("Unable to get window handle"); + return 0; + } + } + + return (*get_handle)(control); +} + +static void *_old_hook_main; + +static void hook_main(int *argc, char ***argv) +{ + gst_init(argc, argv); + CALL_HOOK_MAIN(_old_hook_main, argc, argv); +} + +int EXPORT GB_INIT() +{ + _old_hook_main = GB.Hook(GB_HOOK_MAIN, (void *)hook_main); + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + return 0; +} + +void EXPORT GB_EXIT() +{ +} + diff --git a/gb.media/src/main.h b/gb.media/src/main.h new file mode 100644 index 00000000..48be7ab4 --- /dev/null +++ b/gb.media/src/main.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + main.h + + gb.media component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gambas.h" +#include "gb.image.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +#endif + +int MAIN_get_x11_handle(void *control); + +#endif /* __MAIN_H */ diff --git a/gb.mime/AUTHORS b/gb.mime/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.mime/COPYING b/gb.mime/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.mime/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.mime/ChangeLog b/gb.mime/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.mime/INSTALL b/gb.mime/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.mime/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.mime/Makefile.am b/gb.mime/Makefile.am new file mode 100644 index 00000000..12966aca --- /dev/null +++ b/gb.mime/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @MIME_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.mime/NEWS b/gb.mime/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.mime/README b/gb.mime/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.mime/acinclude.m4 b/gb.mime/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.mime/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.mime/component.am b/gb.mime/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.mime/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.mime/configure.ac b/gb.mime/configure.ac new file mode 100644 index 00000000..6c5cc839 --- /dev/null +++ b/gb.mime/configure.ac @@ -0,0 +1,57 @@ +dnl ---- configure.ac for gb.mime + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-mime, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.mime) +AC_PROG_LIBTOOL + +gb_in_component_search=yes + +GB_COMPONENT_PKG_CONFIG( + mime, + MIME, + gb.mime, + [src], + gmime-3.0) + +if test "$have_mime" = "no"; then + + rm -f DISABLED DISABLED.gb.mime + + GB_COMPONENT_PKG_CONFIG( + mime, + MIME, + gb.mime, + [src], + gmime-2.6) + + if test "$have_mime" = "yes"; then + GB_WARNING([But gmime 2.6 has been detected!]) + fi + +fi + +gb_in_component_search=no + +if test "$have_mime" = "no"; then + + rm -f DISABLED DISABLED.gb.mime + + GB_COMPONENT_PKG_CONFIG( + mime, + MIME, + gb.mime, + [src], + gmime-2.6) + + if test "$have_mime" = "yes"; then + GB_WARNING([But gmime 2.6 has been detected!]) + fi + +fi + +AC_OUTPUT(Makefile src/Makefile) + +GB_PRINT_MESSAGES diff --git a/gb.mime/gambas.h b/gb.mime/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.mime/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.mime/gb_common.h b/gb.mime/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.mime/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.mime/m4 b/gb.mime/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.mime/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.mime/reconf b/gb.mime/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.mime/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.mime/src/Makefile.am b/gb.mime/src/Makefile.am new file mode 100644 index 00000000..201d68c3 --- /dev/null +++ b/gb.mime/src/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.mime +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.mime.la + +gb_mime_la_LIBADD = @MIME_LIB@ +gb_mime_la_LDFLAGS = -module @LD_FLAGS@ @MIME_LDFLAGS@ +gb_mime_la_CPPFLAGS = @MIME_INC@ + +gb_mime_la_SOURCES = main.c main.h \ + c_mime.h c_mime.c \ + c_mimemessage.h c_mimemessage.c \ + c_mimepart.h c_mimepart.c diff --git a/gb.mime/src/c_mime.c b/gb.mime/src/c_mime.c new file mode 100644 index 00000000..82e15f24 --- /dev/null +++ b/gb.mime/src/c_mime.c @@ -0,0 +1,108 @@ +/*************************************************************************** + + c_mime.c + + gb.mime component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_MIME_C + +#include "c_mime.h" + +//------------------------------------------------------------------------- + +BEGIN_METHOD(Mime_Encode, GB_STRING data; GB_INTEGER encoding) + + GMimeEncoding menc; + int encoding = VARG(encoding); + size_t outlen; + char *outbuf; + + switch(encoding) + { + case GMIME_CONTENT_ENCODING_BASE64: + case GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE: + case GMIME_CONTENT_ENCODING_UUENCODE: + break; + default: + GB.Error("Bad encoding"); + return; + } + + g_mime_encoding_init_encode(&menc, encoding); + outlen = g_mime_encoding_outlen(&menc, LENGTH(data)); + outbuf = GB.NewString(NULL, outlen); + outlen = g_mime_encoding_step(&menc, STRING(data), LENGTH(data), outbuf); + outbuf = GB.ExtendString(outbuf, outlen); + + GB.ReturnString(GB.FreeStringLater(outbuf)); + +END_METHOD + +BEGIN_METHOD(Mime_Decode, GB_STRING data; GB_INTEGER encoding) + + GMimeEncoding menc; + int encoding = VARG(encoding); + size_t outlen; + char *outbuf; + + switch(encoding) + { + case GMIME_CONTENT_ENCODING_BASE64: + case GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE: + case GMIME_CONTENT_ENCODING_UUENCODE: + break; + default: + GB.Error("Bad encoding"); + return; + } + + g_mime_encoding_init_decode(&menc, encoding); + outlen = g_mime_encoding_outlen(&menc, LENGTH(data)); + outbuf = GB.NewString(NULL, outlen); + outlen = g_mime_encoding_step(&menc, STRING(data), LENGTH(data), outbuf); + outbuf = GB.ExtendString(outbuf, outlen); + + GB.ReturnString(GB.FreeStringLater(outbuf)); + +END_METHOD + +//------------------------------------------------------------------------- + + + +GB_DESC MimeDesc[] = +{ + GB_DECLARE_VIRTUAL("Mime"), + + GB_CONSTANT("Default", "i", GMIME_CONTENT_ENCODING_DEFAULT), + GB_CONSTANT("7Bit", "i", GMIME_CONTENT_ENCODING_7BIT), + GB_CONSTANT("8Bit", "i", GMIME_CONTENT_ENCODING_8BIT), + GB_CONSTANT("Binary", "i", GMIME_CONTENT_ENCODING_BINARY), + GB_CONSTANT("Base64", "i", GMIME_CONTENT_ENCODING_BASE64), + GB_CONSTANT("QuotedPrintable", "i", GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE), + GB_CONSTANT("UUEncode", "i", GMIME_CONTENT_ENCODING_UUENCODE), + + GB_STATIC_METHOD("Encode", "s", Mime_Encode, "(Data)s(Encoding)i"), + GB_STATIC_METHOD("Decode", "s", Mime_Decode, "(Data)s(Encoding)i"), + + GB_END_DECLARE +}; \ No newline at end of file diff --git a/gb.mime/src/c_mime.h b/gb.mime/src/c_mime.h new file mode 100644 index 00000000..e8d66477 --- /dev/null +++ b/gb.mime/src/c_mime.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + c_mime.h + + gb.mime component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_MIME_H +#define __C_MIME_H + +#include "main.h" + +#ifndef __C_MIME_C +extern GB_DESC MimeDesc[]; +#endif + +#endif /* __C_MIME_H */ diff --git a/gb.mime/src/c_mimemessage.c b/gb.mime/src/c_mimemessage.c new file mode 100644 index 00000000..fc28e198 --- /dev/null +++ b/gb.mime/src/c_mimemessage.c @@ -0,0 +1,304 @@ +/*************************************************************************** + + c_mimemessage.c + + gb.mime component + + (c) 2000-2017 Benoît Minisini + (c) 2018 Bastian Germann + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_MIMEMESSAGE_C + +#include "c_mimemessage.h" + +#define THIS ((CMIMEMESSAGE *)_object) +#define MESSAGE THIS->message + +//------------------------------------------------------------------------- + +static GMimeMessage *_message = NULL; + +CMIMEMESSAGE *CMIMEMESSAGE_create(GMimeMessage *message) +{ + CMIMEMESSAGE *mmsg; + + if (!message) + return NULL; + + mmsg = (CMIMEMESSAGE *)g_object_get_data(G_OBJECT(message), "gambas-object"); + if (!mmsg) + { + _message = message; + g_object_ref(message); + mmsg = (CMIMEMESSAGE *)GB.New(GB.FindClass("MimeMessage"), NULL, NULL); + _message = NULL; + } + + return mmsg; +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(MimeMessage_new, GB_STRING contents) + + GMimeMessage *message = _message; + + if (!message) + { + if (MISSING(contents)) + { + message = g_mime_message_new(FALSE); + // Add a default part? + } + else + { + GMimeParser *parser; + GMimeStream *stream; + + /* create a stream to read from memory */ + stream = g_mime_stream_mem_new_with_buffer(STRING(contents), LENGTH(contents)); + + /* create a new parser object to parse the stream */ + parser = g_mime_parser_new_with_stream (stream); + + /* unref the stream (parser owns a ref, so this object does not actually get free'd until we destroy the parser) */ + g_object_unref(stream); + + /* parse the message from the stream */ + message = g_mime_parser_construct_message (parser NULLGM3); + + /* free the parser (and the stream) */ + g_object_unref(parser); + + if (!message) + { + GB.Error("Unable to parse message"); + return; + } + } + } + + THIS->message = message; + g_object_set_data(G_OBJECT(message), "gambas-object", THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(MimeMessage_free) + + if (MESSAGE) + { + g_object_set_data(G_OBJECT(MESSAGE), "gambas-object", NULL); + g_object_unref(MESSAGE); + } + +END_METHOD + + +BEGIN_PROPERTY(MimeMessage_Subject) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(g_mime_message_get_subject(MESSAGE)); + else + g_mime_message_set_subject(MESSAGE, GB.ToZeroString(PROP(GB_STRING)) NULLGM3); + +END_PROPERTY + + +#define IMPLEMENT_STRING_PROPERTY(_name, _func) \ +BEGIN_PROPERTY(MimeMessage_##_name) \ +\ + if (READ_PROPERTY) \ + GB.ReturnNewZeroString(g_mime_message_get_##_func(MESSAGE)); \ + else \ + g_mime_message_set_##_func(MESSAGE, GB.ToZeroString(PROP(GB_STRING))); \ +\ +END_PROPERTY + + +#define IMPLEMENT_LIST_PROPERTY(_name, _func) \ +BEGIN_PROPERTY(MimeMessage_##_name) \ +\ + InternetAddressList *list; \ + InternetAddress *addr; \ + if (READ_PROPERTY) \ + { \ + list = g_mime_message_get_##_func(MESSAGE); \ + addr = internet_address_list_get_address(list, 0); \ + GB.ReturnNewZeroString(internet_address_to_string(addr, NULL, FALSE)); \ + } \ + else \ + { \ + addr = internet_address_mailbox_new("", GB.ToZeroString(PROP(GB_STRING))); \ + list = g_mime_message_get_##_func(MESSAGE); \ + internet_address_list_set_address(list, 0, addr); \ + } \ +\ +END_PROPERTY + +#if GMIME_MAJOR_VERSION < 3 + IMPLEMENT_STRING_PROPERTY(Sender, sender) + IMPLEMENT_STRING_PROPERTY(ReplyTo, reply_to) + IMPLEMENT_STRING_PROPERTY(Id, message_id) +#else + IMPLEMENT_LIST_PROPERTY(Sender, sender) + IMPLEMENT_LIST_PROPERTY(ReplyTo, reply_to) + IMPLEMENT_STRING_PROPERTY(Id, message_id) +#endif + + +#define IMPLEMENT_RECIPIENT_PROPERTY(_name, _type, _adr_func, _string) \ +BEGIN_PROPERTY(MimeMessage_##_name) \ +\ + InternetAddressList *addr = g_mime_message_get_##_adr_func(MESSAGE, _type); \ + \ + if (READ_PROPERTY) \ + { \ + char *list = internet_address_list_to_string(addr NULLGM3, FALSE); \ + GB.ReturnNewZeroString(list); \ + g_free(list); \ + } \ + else \ + { \ + InternetAddressList *new_addr; \ + \ + internet_address_list_clear(addr); \ + \ + new_addr = internet_address_list_parse##_string(GM3NULL GB.ToZeroString(PROP(GB_STRING))); \ + internet_address_list_append(addr, new_addr); \ + g_object_unref(new_addr); \ + } \ +\ +END_PROPERTY + +#if GMIME_MAJOR_VERSION < 3 + IMPLEMENT_RECIPIENT_PROPERTY(To, GMIME_RECIPIENT_TYPE_TO, recipients, _string); + IMPLEMENT_RECIPIENT_PROPERTY(Cc, GMIME_RECIPIENT_TYPE_CC, recipients, _string); + IMPLEMENT_RECIPIENT_PROPERTY(Bcc, GMIME_RECIPIENT_TYPE_BCC, recipients, _string); +#else + IMPLEMENT_RECIPIENT_PROPERTY(To, GMIME_ADDRESS_TYPE_TO, addresses,); + IMPLEMENT_RECIPIENT_PROPERTY(Cc, GMIME_ADDRESS_TYPE_CC, addresses,); + IMPLEMENT_RECIPIENT_PROPERTY(Bcc, GMIME_ADDRESS_TYPE_BCC, addresses,); +#endif + +BEGIN_PROPERTY(MimeMessage_Part) + + if (READ_PROPERTY) + GB.ReturnObject(CMIMEPART_create(g_mime_message_get_mime_part(MESSAGE))); + else + { + CMIMEPART *mpart = VPROP(GB_OBJECT); + g_mime_message_set_mime_part(MESSAGE, mpart ? mpart->part : NULL); + } + +END_PROPERTY + +BEGIN_PROPERTY(MimeMessage_Body) + + GB.ReturnObject(CMIMEPART_create(g_mime_message_get_body(MESSAGE))); + +END_PROPERTY + +BEGIN_METHOD_VOID(MimeMessage_ToString) + + char *str = g_mime_object_to_string((GMimeObject *)MESSAGE NULLGM3); + GB.ReturnNewZeroString(str); + g_free(str); + +END_METHOD + +/*BEGIN_PROPERTY(MimeMessage_Date) + + if (READ_PROPERTY) + { + time_t time; + GB_DATE date; + + g_mime_message_get_date(MESSAGE, &time, NULL); + GB.MakeDateFromTime(time, 0, &date); + GB.ReturnDate(&date); + } + else + { + GB_DATE_SERIAL *date; + struct tm time; + + date = GB.SplitDate(PROP(GB_DATE)); + time.tm_sec = date. + } + +END_PROPERTY*/ + +//------------------------------------------------------------------------- + +BEGIN_METHOD(MimeMessage_Headers_get, GB_STRING name) + + GB.ReturnNewZeroString(g_mime_object_get_header((GMimeObject *)MESSAGE, GB.ToZeroString(ARG(name)))); + +END_METHOD + +BEGIN_METHOD(MimeMessage_Headers_put, GB_STRING value; GB_STRING name) + + if (LENGTH(name)) + g_mime_object_set_header((GMimeObject *)MESSAGE, GB.ToZeroString(ARG(name)), GB.ToZeroString(ARG(value)) NULLGM3); + else + g_mime_object_remove_header((GMimeObject *)MESSAGE, GB.ToZeroString(ARG(name))); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC MimeMessageHeadersDesc[] = +{ + GB_DECLARE_VIRTUAL(".MimeMessage.Headers"), + + GB_METHOD("_get", "s", MimeMessage_Headers_get, "(Name)s"), + GB_METHOD("_put", NULL, MimeMessage_Headers_put, "(Value)s(Name)s"), + + GB_END_DECLARE +}; + +//------------------------------------------------------------------------- + +GB_DESC MimeMessageDesc[] = +{ + GB_DECLARE("MimeMessage", sizeof(CMIMEMESSAGE)), + + GB_METHOD("_new", NULL, MimeMessage_new, "[(Contents)s]"), + GB_METHOD("_free", NULL, MimeMessage_free, NULL), + + GB_PROPERTY("Sender", "s", MimeMessage_Sender), + GB_PROPERTY("ReplyTo", "s", MimeMessage_ReplyTo), + GB_PROPERTY("To", "s", MimeMessage_To), + GB_PROPERTY("Cc", "s", MimeMessage_Cc), + GB_PROPERTY("Bcc", "s", MimeMessage_Bcc), + GB_PROPERTY("Subject", "s", MimeMessage_Subject), + //GB_PROPERTY("Date", "d", MimeMessage_Date), + GB_PROPERTY("Id", "s", MimeMessage_Id), + + GB_PROPERTY_SELF("Headers", ".MimeMessage.Headers"), + + GB_PROPERTY("Part", "MimePart", MimeMessage_Part), + GB_PROPERTY_READ("Body", "MimePart", MimeMessage_Body), + + GB_METHOD("ToString", "s", MimeMessage_ToString, NULL), + + GB_END_DECLARE +}; diff --git a/gb.mime/src/c_mimemessage.h b/gb.mime/src/c_mimemessage.h new file mode 100644 index 00000000..9fc30008 --- /dev/null +++ b/gb.mime/src/c_mimemessage.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + c_mimemessage.h + + gb.mime component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_MIMEMESSAGE_H +#define __C_MIMEMESSAGE_H + +#include "main.h" +#include "c_mimepart.h" + +#ifndef __C_MIMEMESSAGE_C +extern GB_DESC MimeMessageDesc[]; +extern GB_DESC MimeMessageHeadersDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + GMimeMessage *message; + CMIMEPART *part; + CMIMEPART *body; + } + CMIMEMESSAGE; + +CMIMEMESSAGE *CMIMEMESSAGE_create(GMimeMessage *message); + +#endif /* __MAIN_H */ diff --git a/gb.mime/src/c_mimepart.c b/gb.mime/src/c_mimepart.c new file mode 100644 index 00000000..907323bf --- /dev/null +++ b/gb.mime/src/c_mimepart.c @@ -0,0 +1,459 @@ +/*************************************************************************** + + c_mimepart.c + + gb.mime component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_MIMEPART_C + +#include + +#include "c_mimemessage.h" +#include "c_mimepart.h" + +#define THIS ((CMIMEPART *)_object) +#define PART THIS->part +#define MPART ((GMimePart *)THIS->part) +#define MMPART ((GMimeMultipart *)THIS->part) +#define MSPART ((GMimeMessagePart *)THIS->part) + +//------------------------------------------------------------------------- + +static bool _do_not_create_part = FALSE; + +CMIMEPART *CMIMEPART_create(GMimeObject *part) +{ + CMIMEPART *mpart; + + if (!part) + return NULL; + + mpart = (CMIMEPART *)g_object_get_data(G_OBJECT(part), "gambas-object"); + if (!mpart) + { + _do_not_create_part = TRUE; + mpart = (CMIMEPART *)GB.New(GB.FindClass("MimePart"), NULL, NULL); + _do_not_create_part = FALSE; + mpart->part = part; + g_object_ref(part); + g_object_set_data(G_OBJECT(part), "gambas-object", (gpointer)mpart); + } + + return mpart; +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(MimePart_new, GB_STRING ctype) + + GMimeObject *part; + GMimeContentType *ctype; + const char *content_type; + + if (_do_not_create_part) + return; + + if (MISSING(ctype)) + content_type = "text/plain;charset=utf-8"; + else + content_type = GB.ToZeroString(ARG(ctype)); + +#if GMIME_MAJOR_VERSION < 3 + ctype = g_mime_content_type_new_from_string(content_type); +#else + ctype = g_mime_content_type_parse(NULL, content_type); +#endif + + if (g_mime_content_type_is_type(ctype, "multipart", "*")) + part = (GMimeObject *)g_mime_multipart_new_with_subtype(g_mime_content_type_get_media_subtype(ctype)); + else if (g_mime_content_type_is_type(ctype, "message", "*")) + part = (GMimeObject *)g_mime_message_part_new(g_mime_content_type_get_media_subtype(ctype)); + else + { + part = (GMimeObject* )g_mime_part_new(); + g_mime_object_set_content_type(part, ctype); + + if (g_mime_content_type_is_type(ctype, "text", "*")) + g_mime_part_set_content_encoding((GMimePart *)part, GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE); + else + g_mime_part_set_content_encoding((GMimePart *)part, GMIME_CONTENT_ENCODING_BASE64); + } + + PART = part; + //g_object_ref(part); + g_object_set_data(G_OBJECT(part), "gambas-object", (gpointer)THIS); + +END_METHOD + +BEGIN_METHOD_VOID(MimePart_free) + + g_object_set_data(G_OBJECT(PART), "gambas-object", NULL); + g_object_unref(PART); + +END_METHOD + +#define IMPLEMENT_STRING_PROPERTY(_name, _func) \ +BEGIN_PROPERTY(MimePart_##_name) \ +\ + if (READ_PROPERTY) \ + GB.ReturnNewZeroString(g_mime_object_get_##_func(PART)); \ + else \ + g_mime_object_set_##_func(PART, GB.ToZeroString(PROP(GB_STRING))); \ +\ +END_PROPERTY + +IMPLEMENT_STRING_PROPERTY(Disposition, disposition); +IMPLEMENT_STRING_PROPERTY(ContentId, content_id); + +BEGIN_PROPERTY(MimePart_ContentType) + + GMimeContentType *ctype; + + if (READ_PROPERTY) + { + ctype = g_mime_object_get_content_type(PART); + #if GMIME_MAJOR_VERSION < 3 + char *str = g_mime_content_type_to_string(ctype); + #else + char *str = g_mime_content_type_encode(ctype, NULL); + #endif + GB.ReturnNewZeroString(str); + g_free(str); + } + else + { + #if GMIME_MAJOR_VERSION < 3 + ctype = g_mime_content_type_new_from_string(GB.ToZeroString(PROP(GB_STRING))); + #else + ctype = g_mime_content_type_parse(NULL, GB.ToZeroString(PROP(GB_STRING))); + #endif + g_mime_object_set_content_type(PART, ctype); + g_object_unref(ctype); + } + +END_PROPERTY + + +BEGIN_PROPERTY(MimePart_ContentDisposition) + + GMimeContentDisposition *cdisp; + + //if (READ_PROPERTY) + //{ + cdisp = g_mime_object_get_content_disposition(PART); + char *str; + #if GMIME_MAJOR_VERSION < 3 + str = g_mime_content_disposition_to_string(cdisp, TRUE); + #else + str = g_mime_content_disposition_encode(cdisp, NULL); + #endif + GB.ReturnNewZeroString(str); + g_free(str); + /*} + else + { + cdisp = g_mime_content_disposition_new_from_string(GB.ToZeroString(PROP(GB_STRING))); + g_mime_object_set_content_disposition(PART, cdisp); + g_object_unref(cdisp); + }*/ + +END_PROPERTY + + +BEGIN_METHOD_VOID(MimePart_ToString) + + char *str = g_mime_object_to_string(PART NULLGM3); + GB.ReturnNewZeroString(str); + g_free(str); + +END_METHOD + + +//------------------------------------------------------------------------- + +#define CHECK_PART() if (!GMIME_IS_PART(PART)) { GB.Error("Not a part"); return; } + +BEGIN_PROPERTY(MimePart_ContentEncoding) + + CHECK_PART(); + + if (READ_PROPERTY) + GB.ReturnInteger(g_mime_part_get_content_encoding(MPART)); + else + g_mime_part_set_content_encoding(MPART, VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(MimePart_Data) + + GMimeDataWrapper *content; + GMimeStream *stream; + GByteArray *array; + + if (READ_PROPERTY) + { + if (!GMIME_IS_PART(PART)) + { + GB.ReturnNull(); + return; + } + + #if GMIME_MAJOR_VERSION < 3 + content = g_mime_part_get_content_object(MPART); + #else + content = g_mime_part_get_content(MPART); + #endif + + array = g_byte_array_new(); + + /* create a new stream for writing to memory */ + stream = g_mime_stream_mem_new_with_byte_array(array); + g_mime_stream_mem_set_owner((GMimeStreamMem *)stream, TRUE); + + /* write the contents to the stream */ + g_mime_data_wrapper_write_to_stream(content, stream); + + if (!array->data) + GB.ReturnVoidString(); + else + GB.ReturnNewString((char *)array->data, (int)array->len); + + /* free the output stream */ + g_object_unref (stream); + + } + else + { + CHECK_PART(); + + /* create the parts' content stream */ + stream = g_mime_stream_mem_new_with_buffer(PSTRING(), PLENGTH()); + + /* create the content object - since the stream is not base64 + or QP encoded or anything, we can use + GMIME_CONTENT_ENCODING_DEFAULT as the encoding type (_DEFAULT + is the same as saying "nothing specified") */ + content = g_mime_data_wrapper_new_with_stream (stream, GMIME_CONTENT_ENCODING_DEFAULT); + g_object_unref (stream); + + /* set the content object on the new mime part */ + #if GMIME_MAJOR_VERSION < 3 + g_mime_part_set_content_object(MPART, content); + #else + g_mime_part_set_content(MPART, content); + #endif + g_object_unref(content); + + /* if we want, we can tell GMime that the content should be base64 encoded when written to disk... */ + //g_mime_part_set_content_encoding (mime_part, GMIME_CONTENT_ENCODING_BASE64); + } + +END_PROPERTY + + +BEGIN_PROPERTY(MimePart_FileName) + + if (READ_PROPERTY) + { + if (GMIME_IS_PART(PART)) + GB.ReturnNewZeroString(g_mime_part_get_filename(MPART)); + else + GB.ReturnNull(); + } + else + { + CHECK_PART(); + g_mime_part_set_filename(MPART, GB.ToZeroString(PROP(GB_STRING))); + } + +END_PROPERTY + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(MimePart_Count) + + if (!GMIME_IS_MULTIPART(PART)) + GB.ReturnInteger(0); + else + GB.ReturnInteger(g_mime_multipart_get_count(MMPART)); + +END_PROPERTY + + +BEGIN_METHOD(MimePart_get, GB_INTEGER index) + + int count; + int index; + + if (!GMIME_IS_MULTIPART(PART)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + count = g_mime_multipart_get_count(MMPART); + index = VARG(index); + + if (index < 0 || index >= count) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(CMIMEPART_create(g_mime_multipart_get_part(MMPART, index))); + +END_METHOD + + +BEGIN_METHOD_VOID(MimePart_next) + + int count; + int *index; + + if (!GMIME_IS_MULTIPART(PART)) + goto __STOP; + + count = g_mime_multipart_get_count(MMPART); + index = (int *)GB.GetEnum(); + + if (*index >= count) + goto __STOP; + + GB.ReturnObject(CMIMEPART_create(g_mime_multipart_get_part(MMPART, *index))); + (*index)++; + return; + +__STOP: + + GB.StopEnum(); + +END_METHOD + + +BEGIN_METHOD(MimePart_Add, GB_OBJECT part) + + CMIMEPART *part = VARG(part); + + if (GB.CheckObject(part)) + return; + + if (!GMIME_IS_MULTIPART(PART)) + { + GB.Error("Not a multipart"); + return; + } + + g_mime_multipart_add(MMPART, part->part); + +END_METHOD + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(MimePart_Message) + + if (READ_PROPERTY) + { + if (!GMIME_IS_MESSAGE_PART(PART)) + GB.ReturnNull(); + else + GB.ReturnObject(CMIMEMESSAGE_create(g_mime_message_part_get_message(MSPART))); + } + else + { + CMIMEMESSAGE *mmsg; + + if (!GMIME_IS_MESSAGE_PART(PART)) + { + GB.Error("Not a message part"); + return; + } + + mmsg = VPROP(GB_OBJECT); + g_mime_message_part_set_message(MSPART, mmsg ? mmsg->message : NULL); + } + +END_PROPERTY + +//------------------------------------------------------------------------- + +BEGIN_METHOD(MimePart_Headers_get, GB_STRING name) + + GB.ReturnNewZeroString(g_mime_object_get_header(PART, GB.ToZeroString(ARG(name)))); + +END_METHOD + +BEGIN_METHOD(MimePart_Headers_put, GB_STRING value; GB_STRING name) + + if (LENGTH(name)) + g_mime_object_set_header(PART, GB.ToZeroString(ARG(name)), GB.ToZeroString(ARG(value)) NULLGM3); + else + g_mime_object_remove_header(PART, GB.ToZeroString(ARG(name))); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC MimePartHeadersDesc[] = +{ + GB_DECLARE_VIRTUAL(".MimePart.Headers"), + + GB_METHOD("_get", "s", MimePart_Headers_get, "(Name)s"), + GB_METHOD("_put", NULL, MimePart_Headers_put, "(Value)s(Name)s"), + + GB_END_DECLARE +}; + +GB_DESC MimePartDesc[] = +{ + GB_DECLARE("MimePart", sizeof(CMIMEPART)), + + GB_METHOD("_new", NULL, MimePart_new, "[(ContentType)s]"), + GB_METHOD("_free", NULL, MimePart_free, NULL), + + GB_PROPERTY("ContentType", "s", MimePart_ContentType), + GB_PROPERTY("Disposition", "s", MimePart_Disposition), + GB_PROPERTY("ContentDisposition", "s", MimePart_ContentDisposition), + GB_PROPERTY("ContentId", "s", MimePart_ContentId), + + GB_PROPERTY_SELF("Headers", ".MimePart.Headers"), + + GB_METHOD("ToString", "s", MimePart_ToString, NULL), + + // Specific to GMimePart + + //GB_PROPERTY("ContentDescription", "s", MimePart_ContentDescription), + //GB_PROPERTY("ContentMD5", "s", MimePart_ContentMD5), + //GB_PROPERTY("ContentLocation", "s", MimePart_ContentLocation), + GB_PROPERTY("ContentEncoding", "i", MimePart_ContentEncoding), + GB_PROPERTY("FileName", "s", MimePart_FileName), + GB_PROPERTY("Data", "s", MimePart_Data), + GB_PROPERTY("Message", "MimeMessage", MimePart_Message), + + GB_PROPERTY_READ("Count", "i", MimePart_Count), + //GB_METHOD("Clear", NULL, MimePart_Clear, NULL), + GB_METHOD("_get", "MimePart", MimePart_get, "(Index)i"), + GB_METHOD("_next", "MimePart", MimePart_next, NULL), + GB_METHOD("Add", NULL, MimePart_Add, "(Part)MimePart"), + + GB_END_DECLARE +}; diff --git a/gb.mime/src/c_mimepart.h b/gb.mime/src/c_mimepart.h new file mode 100644 index 00000000..0f75a7c8 --- /dev/null +++ b/gb.mime/src/c_mimepart.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + c_mimepart.h + + gb.mime component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_MIMEPART_H +#define __C_MIMEPART_H + +#include "main.h" + +#ifndef __C_MIMEPART_C +extern GB_DESC MimePartDesc[]; +extern GB_DESC MimePartHeadersDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + GMimeObject *part; + } + CMIMEPART; + +CMIMEPART *CMIMEPART_create(GMimeObject *part); + +#endif /* __MAIN_H */ diff --git a/gb.mime/src/gb.mime.component b/gb.mime/src/gb.mime.component new file mode 100644 index 00000000..2ed93b42 --- /dev/null +++ b/gb.mime/src/gb.mime.component @@ -0,0 +1,3 @@ +[Component] +Author=Benoît Minisini +State=Stable diff --git a/gb.mime/src/main.c b/gb.mime/src/main.c new file mode 100644 index 00000000..3404e82f --- /dev/null +++ b/gb.mime/src/main.c @@ -0,0 +1,61 @@ +/*************************************************************************** + + main.c + + gb.mime component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +#include "c_mime.h" +#include "c_mimemessage.h" +#include "c_mimepart.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + MimeDesc, + MimePartHeadersDesc, + MimePartDesc, + MimeMessageHeadersDesc, + MimeMessageDesc, + NULL +}; + +int EXPORT GB_INIT() +{ + g_mime_init( + #if GMIME_MAJOR_VERSION < 3 + 0 + #endif + ); + return 0; +} + + +void EXPORT GB_EXIT() +{ + g_mime_shutdown(); +} diff --git a/gb.mime/src/main.h b/gb.mime/src/main.h new file mode 100644 index 00000000..d3f20b76 --- /dev/null +++ b/gb.mime/src/main.h @@ -0,0 +1,46 @@ +/*************************************************************************** + + main.h + + gb.mime component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" + +#include +#include + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#if GMIME_MAJOR_VERSION < 3 +#define NULLGM3 +#define GM3NULL +#else +#define NULLGM3 ,NULL +#define GM3NULL NULL, +#endif + +#endif /* __MAIN_H */ diff --git a/gb.ncurses/AUTHORS b/gb.ncurses/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.ncurses/COPYING b/gb.ncurses/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.ncurses/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.ncurses/ChangeLog b/gb.ncurses/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.ncurses/INSTALL b/gb.ncurses/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.ncurses/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.ncurses/Makefile.am b/gb.ncurses/Makefile.am new file mode 100644 index 00000000..57cb2965 --- /dev/null +++ b/gb.ncurses/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @NCURSES_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.ncurses/NEWS b/gb.ncurses/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.ncurses/README b/gb.ncurses/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.ncurses/acinclude.m4 b/gb.ncurses/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.ncurses/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.ncurses/component.am b/gb.ncurses/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.ncurses/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.ncurses/configure.ac b/gb.ncurses/configure.ac new file mode 100644 index 00000000..f09e5e48 --- /dev/null +++ b/gb.ncurses/configure.ac @@ -0,0 +1,19 @@ +dnl ---- configure.ac for gb.ncurses + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-ncurses, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.ncurses) +AC_PROG_LIBTOOL + +GB_COMPONENT_PKG_CONFIG( + ncurses, NCURSES, gb.ncurses, [src], + ncurses panel) + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.ncurses/gambas.h b/gb.ncurses/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.ncurses/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.ncurses/gb_common.h b/gb.ncurses/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.ncurses/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.ncurses/m4 b/gb.ncurses/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.ncurses/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.ncurses/reconf b/gb.ncurses/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.ncurses/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.ncurses/src/Makefile.am b/gb.ncurses/src/Makefile.am new file mode 100644 index 00000000..b9c54bd5 --- /dev/null +++ b/gb.ncurses/src/Makefile.am @@ -0,0 +1,17 @@ +COMPONENT = gb.ncurses +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.ncurses.la + +gb_ncurses_la_LIBADD = @NCURSES_LIB@ +gb_ncurses_la_LDFLAGS = -module @LD_FLAGS@ @NCURSES_LDFLAGS@ +gb_ncurses_la_CPPFLAGS = @NCURSES_INC@ + +gb_ncurses_la_SOURCES = \ + main.h main.c \ + c_window.h c_window.c \ + c_key.h c_key.c \ + c_color.h c_color.c \ + c_screen.h c_screen.c \ + c_input.h c_input.c + diff --git a/gb.ncurses/src/c_color.c b/gb.ncurses/src/c_color.c new file mode 100644 index 00000000..83d12686 --- /dev/null +++ b/gb.ncurses/src/c_color.c @@ -0,0 +1,279 @@ +/* + * c_color.c - gb.ncurses Color static class + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_COLOR_C + +#include + +#include "../gambas.h" + +#include "main.h" +#include "c_screen.h" + +#define PAIR_VALID(p) (p >= 0 && p < COLOR_PAIRS) +#define COLOR_VALID(c) (c >= -1 && c < COLORS) + +static int _color; +static short colors[] = { + COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, + COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE +}; + +void COLOR_init() +{ + start_color(); + //use_default_colors(); + + /* + * Initialise all possible pairs + */ +#define ARRAY_NUM(arr) (sizeof(arr) / sizeof(arr[0])) + int i, j, n; + + for (n = 0, i = 0; i < ARRAY_NUM(colors); i++) + for (j = 0; j < ARRAY_NUM(colors); j++) + init_pair(++n, colors[i], colors[j]); +} + +/* + * Color + */ + +BEGIN_PROPERTY(Color_Available) + + GB.ReturnBoolean(has_colors()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_CanChange) + + GB.ReturnBoolean(can_change_color()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_Count) + + GB.ReturnInteger(COLORS); + +END_PROPERTY + +BEGIN_METHOD(Color_get, GB_INTEGER index) + + if (!COLOR_VALID(VARG(index))) { + GB.Error(GB_ERR_BOUND); + return; + } + _color = VARG(index); + RETURN_SELF(); + +END_METHOD + +int CCOLOR_setcolor(short index, float r, float g, float b) +{ + return init_color(index, r * 1000, g * 1000, b * 1000); +} + +BEGIN_METHOD(Color_Set, GB_INTEGER color; GB_FLOAT r; GB_FLOAT g;GB_FLOAT b) + + if (!COLOR_VALID(VARG(color))) { + GB.Error(GB_ERR_BOUND); + return; + } + CCOLOR_setcolor(VARG(color), VARG(r), VARG(g), VARG(b)); + REAL_REFRESH(); + +END_METHOD + +GB_DESC CColorDesc[] = { + GB_DECLARE("Color", 0), + GB_NOT_CREATABLE(), + + GB_CONSTANT("Black", "i", COLOR_BLACK), + GB_CONSTANT("Red", "i", COLOR_RED), + GB_CONSTANT("Green", "i", COLOR_GREEN), + GB_CONSTANT("Yellow", "i", COLOR_YELLOW), + GB_CONSTANT("Blue", "i", COLOR_BLUE), + GB_CONSTANT("Magenta", "i", COLOR_MAGENTA), + GB_CONSTANT("Cyan", "i", COLOR_CYAN), + GB_CONSTANT("White", "i", COLOR_WHITE), + + GB_STATIC_PROPERTY_READ("Available", "b", Color_Available), + GB_STATIC_PROPERTY_READ("CanChange", "b", Color_CanChange), + GB_STATIC_PROPERTY_READ("Count", "i", Color_Count), + + GB_STATIC_METHOD("_get", ".ColorInfo", Color_get, "(Index)i"), + GB_STATIC_METHOD("Set", NULL, Color_Set, "(Color)i(R)i(G)i(B)i"), + + GB_END_DECLARE +}; + +/* + * .ColorInfo + */ + +enum { + COLOR_R, + COLOR_G, + COLOR_B +}; + +static int CCOLOR_content(short color, float *r, float *g, float *b) +{ + short ar, ag, ab; + + color_content(color, &ar, &ag, &ab); + if (r) + *r = (float) ar / 1000; + if (g) + *g = (float) ag / 1000; + if (b) + *b = (float) ab / 1000; + return 0; +} + +int CCOLOR_setcolor_one(short index, float val, int which) +{ + short r, g, b; + float rf, gf, bf; + + color_content(index, &r, &g, &b); + rf = ((float) r) / 1000; + gf = ((float) g) / 1000; + bf = ((float) b) / 1000; + switch (which) { + case COLOR_R: + rf = val; + break; + case COLOR_G: + gf = val; + break; + case COLOR_B: + bf = val; + break; + default: + return -1; + } + return CCOLOR_setcolor(index, rf, gf, bf); +} + +BEGIN_PROPERTY(ColorInfo_Red) + + float red; + + if (READ_PROPERTY) { + CCOLOR_content(_color, &red, NULL, NULL); + GB.ReturnFloat(red); + return; + } + CCOLOR_setcolor_one(_color, VPROP(GB_FLOAT), COLOR_R); + REAL_REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Green) + + float green; + + if (READ_PROPERTY) { + CCOLOR_content(_color, NULL, &green, NULL); + GB.ReturnFloat(green); + return; + } + CCOLOR_setcolor_one(_color, VPROP(GB_FLOAT), COLOR_G); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Blue) + + float blue; + + if (READ_PROPERTY) { + CCOLOR_content(_color, NULL, NULL, &blue); + GB.ReturnFloat(blue); + return; + } + CCOLOR_setcolor_one(_color, VPROP(GB_FLOAT), COLOR_B); + +END_PROPERTY + +GB_DESC CColorInfoDesc[] = { + GB_DECLARE(".ColorInfo", 0), + GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("Red", "i", ColorInfo_Red), + GB_STATIC_PROPERTY("Green", "i", ColorInfo_Green), + GB_STATIC_PROPERTY("Blue", "i", ColorInfo_Blue), + + GB_END_DECLARE +}; + +/* + * Pair + */ + +BEGIN_PROPERTY(Pair_Count) + + GB.ReturnInteger(COLOR_PAIRS); + +END_PROPERTY + +short CPAIR_get(short fg, short bg) +{ + short i, j; + int n; + + i = j = -1; + for (n = 0; n < ARRAY_NUM(colors); n++) { + if (colors[n] == fg) + i = fg; + if (colors[n] == bg) + j = bg; + if (i != -1 && j != -1) + break; + } + if (n == ARRAY_NUM(colors)) + return -1; + /* See COLOR_init() */ + return i * ARRAY_NUM(colors) + j + 1; +} + +BEGIN_METHOD(Pair_get, GB_INTEGER fore; GB_INTEGER back) + + short pairn = CPAIR_get(VARG(fore), VARG(back)); + + if (pairn == -1) { + GB.Error(GB_ERR_BOUND); + return; + } + GB.ReturnInteger(pairn); + +END_METHOD + +GB_DESC CPairDesc[] = { + GB_DECLARE("Pair", 0), + GB_NOT_CREATABLE(), + + GB_STATIC_PROPERTY_READ("Count", "i", Pair_Count), + + GB_STATIC_METHOD("_get", "i", Pair_get, "(Fore)i(Back)i"), + + GB_END_DECLARE +}; diff --git a/gb.ncurses/src/c_color.h b/gb.ncurses/src/c_color.h new file mode 100644 index 00000000..0b7a9a44 --- /dev/null +++ b/gb.ncurses/src/c_color.h @@ -0,0 +1,13 @@ +#ifndef __C_COLOR_H +#define __C_COLOR_H + +extern GB_DESC CColorDesc[]; +extern GB_DESC CColorInfoDesc[]; +extern GB_DESC CPairDesc[]; + +extern void COLOR_init(); +extern int CCOLOR_setcolor(short index, float r, float g, float b); +extern int CCOLOR_setcolor_one(short index, float val, int which); +extern short CPAIR_get(short fg, short bg); + +#endif /* __C_COLOR_H */ diff --git a/gb.ncurses/src/c_input.c b/gb.ncurses/src/c_input.c new file mode 100644 index 00000000..384fe000 --- /dev/null +++ b/gb.ncurses/src/c_input.c @@ -0,0 +1,632 @@ +/* + * c_input.c - gb.ncurses opaque input routines + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_INPUT_C + +#include +#include +/* +#include +#include +#include +*/ +#include +#include +#include + +#include + +#include "gambas.h" +#include "gb_common.h" + +#include "main.h" +#include "c_input.h" +#include "c_window.h" + +#define E_UNSUPP "Unsupported input mode" + +static char _watch_fd = -1; + +int INPUT_init() +{ + INPUT_watch(0); + return 0; +} + +void INPUT_exit() +{ + INPUT_watch(-1); +} + +static void INPUT_watch(int fd) +{ + if (fd == _watch_fd) + return; + + if (_watch_fd != -1) + GB.Watch(_watch_fd, GB_WATCH_NONE, NULL, 0); + _watch_fd = fd; + if (_watch_fd == -1) + return; + + GB.Watch(_watch_fd, GB_WATCH_READ, INPUT_callback, 0); +} + +static void INPUT_callback(int fd, int flag, intptr_t arg) +{ + CWINDOW_raise_read(NULL); +} + +void INPUT_mode(CSCREEN *scr, int mode) +{ + if (mode == scr->input) + return; + + switch (mode) { + case INPUT_COOKED: + nocbreak(); + break; + case INPUT_CBREAK: + cbreak(); + break; + case INPUT_RAW: + raw(); + break; + default: + GB.Error(E_UNSUPP); + return; + } + scr->input = mode; +} + +void INPUT_drain() +{ + flushinp(); +} + +static int INPUT_get_ncurses(int time_out) +{ + int ret; + + if (time_out >= 0) + timeout(time_out); + ret = getch(); + if (ret == ERR) { + /* Had a timeout, the manual doesn't define any errors to + happen for wgetch() besides NULL pointer arguments. The + only source of ERR is timeout expired. */ + if (time_out >= 0) + ret = 0; + } + + if (time_out >= 0) + timeout(-1); + return ret; +} + +int INPUT_get(int timeout) +{ + return INPUT_get_ncurses(timeout); +} + +#if 0 +BEGIN_PROPERTY(Input_IsConsole) + + int fd = NODELAY_consolefd(); + + if (fd == -1) { + GB.ReturnBoolean(FALSE); + return; + } + close(fd); + GB.ReturnBoolean(TRUE); + +END_PROPERTY + +BEGIN_PROPERTY(Input_RepeatDelay) + + if (READ_PROPERTY) { + GB.ReturnInteger(NODELAY_repeater_delay(INPUT_RETURN)); + return; + } + if (NODELAY_repeater_delay(VPROP(GB_INTEGER)) == -1) { + GB.Error("Invalid value"); + return; + } + +END_PROPERTY +#endif + +GB_DESC CInputDesc[] = { + GB_DECLARE("Input", 0), + GB_NOT_CREATABLE(), + + GB_CONSTANT("NoTimeout", "i", TIMEOUT_NOTIMEOUT), + + GB_CONSTANT("Cooked", "i", INPUT_COOKED), + GB_CONSTANT("CBreak", "i", INPUT_CBREAK), + GB_CONSTANT("Raw", "i", INPUT_RAW), +#if 0 + GB_CONSTANT("NoDelay", "i", INPUT_NODELAY), + + GB_STATIC_PROPERTY_READ("IsConsole", "b", Input_IsConsole), + GB_STATIC_PROPERTY("RepeatDelay", "i", Input_RepeatDelay), +#endif + GB_END_DECLARE +}; + +#if 0 +/* + * NODELAY routines + */ +static struct { + struct { + struct termios term; + int kbmode; + void (*error_hook)(); + } old; + int fd; + unsigned short pressed; + unsigned int delay; + GB_TIMER *timer; +} no_delay; +static char _exiting_nodelay = 0; + +/** + * Init NoDelay mode + * We save old settings and prepare the TTY driver and Gambas + * Precisely: + * - (TTY driver:) + * - Save all related values + * - Set terminal to (ncurses) raw()-like input mode as base for NoDelay + * - Set keyboard to K_MEDIUMRAW mode to see key make and break codes + * - (Gambas:) + * - Install specific error hook + * - Reset repeater timer + * - Begin watching console fd + */ +static int NODELAY_init() +{ + int fd = NODELAY_consolefd(); + struct termios term; + + if (fd == -1) + return -1; + + /* TODO: implement switching between vts, need available signals to + * be sent */ + + tcgetattr(fd, &no_delay.old.term); + ioctl(fd, KDGKBMODE, &no_delay.old.kbmode); + + memcpy(&term, &no_delay.old.term, sizeof(term)); + term.c_lflag &= ~(ICANON | ECHO | ISIG); + term.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON); + /* Have no timeout per default */ + term.c_cc[VMIN] = 0; + term.c_cc[VTIME] = TIMEOUT_NOTIMEOUT; + tcsetattr(fd, TCSAFLUSH, &term); + no_delay.old.error_hook = GB.Hook(GB_HOOK_ERROR, + NODELAY_error_hook); + no_delay.fd = fd; + + no_delay.timer = NULL; + + ioctl(no_delay.fd, KDSKBMODE, K_MEDIUMRAW); + + INPUT_watch(fd); + + return 0; +} + +/** + * Cleanup NoDelay mode + * Restore old settings, see NODELAY_init() for details + * This function gets called by our error hook and the error hook is removed + * here. When removing a hook, it gets automatically called: Hence + * @_exiting_nodelay + */ +static int NODELAY_exit() +{ + if (_exiting_nodelay) + return 0; + + _exiting_nodelay = 1; + + INPUT_watch(0); + + ioctl(no_delay.fd, KDSKBMODE, no_delay.old.kbmode); + + if (no_delay.timer) + GB.Unref((void **) &no_delay.timer); + + GB.Hook(GB_HOOK_ERROR, no_delay.old.error_hook); + tcsetattr(no_delay.fd, TCSANOW, &no_delay.old.term); + + close(no_delay.fd); + + _exiting_nodelay = 0; + + return 0; +} + +/** + * The NoDelay mode error hook + * This calls the former error hook, saved by NODELAY_init() to not + * disturb any piece code + */ +static void NODELAY_error_hook() +{ + if (_exiting_nodelay) + return; + NODELAY_exit(); + no_delay.old.error_hook(); +} + +/** + * Return if the given fd can be used with console_ioctls + * @fd: file descriptor to test + * The idea was derived from "kbd" package, getfd.c, is_a_console() + */ +static inline char NODELAY_is_cons(int fd) +{ + char type; + + if (fd != -1 && isatty(fd) && ioctl(fd, KDGKBTYPE, &type) != -1 + && (type == KB_101 || type == KB_84)) + return 1; + return 0; +} + +/** + * Returns an fd that can be used with console_ioctls or -1 if none + * available + */ +static int NODELAY_consolefd() +{ + int fd; + + if (NODELAY_is_cons(0)) + return 0; + + fd = open("/dev/tty", O_RDWR); + if (fd == -1) + return -1; + if (NODELAY_is_cons(fd)) + return fd; + + close(fd); + return -1; +} + +/** + * Drain NoDelay input queue + */ +static inline void NODELAY_drain() +{ + tcflush(no_delay.fd, TCIFLUSH); +} + +/** + * Return or set the repeater delay + * @val: value to set the delay to. This value must be at least 1 or an + * error is returned. If it is REPEATER_RETURN, the current value is + * returned to the caller. + * Note that this setting affects the repeater function itself, that gets + * called in this interval to generate events and the INPUT_get_nodelay() + * function which will wait to return the amount of milliseconds if it is to + * return the pressed key. + */ +static int NODELAY_repeater_delay(int val) +{ + if (val == INPUT_RETURN) + return no_delay.delay; + if (val < 1) + return -1; + no_delay.delay = (unsigned int) val; + return 0; +} + +/** + * Post callback to insert new read event during next event loop. + * This is important because the NODELAY_repeater() gets called by the + * timer. If we raise an event from there, the event handler may destroy the + * timer we are currently in by issuing a NODELAY_change_pressed(). This has + * consequently be done outside the timer tick. + * @arg: unused + */ +static void NODELAY_post_read(intptr_t arg) +{ + WINDOW_raise_read(NULL); +} + +/** + * NoDelay mode event repeater. This function is the timer callback + * Used to insert Window_Read events if there is a key pressed + */ +static int NODELAY_repeater() +{ + MY_DEBUG(); + + + if (!no_delay.pressed) + return TRUE; + GB.Post(NODELAY_post_read, 0); + return FALSE; +} + +/* + * Return codes from NODELAY_trans_keycode() + */ +enum { + TRANS_NEED_MORE = -1, + TRANS_KEY_MIN +}; + +/* + * States of the modifier keys that we recognise + */ +enum { + MOD_NONE = 0, + MOD_SHIFT = 1, + MOD_CTRL = 2, + MOD_ALT = 4 +}; + +#define IS_BREAK(k) ((k) & 0x80) + +/** + * Translate a keycode (or a sequence) to an ncurses compatible int + * @kc: keycode to translate + * This function returns one of the above codes. TRANS_NEED_MORE means that + * the given @kc is considered part of a multi-keycode sequence (or is a + * Shift, Control, Alt), so we need more keys which are (in first case + * likely) available without waiting from the tty driver then; in the latter + * case (modifier keys), it does not count as a keypress anyway. + * Note that after a usual tty key sequence is assembled it is passed to the + * driver to try to get an escape sequence for it. If there is no escape + * seqeuence for that key, we can simply return the plain value. Otherwise + * we exploit ncurses key_defined() routine to translate the sequence to an + * int for us. This gives an int like ncurses getch() would do. + */ +static int NODELAY_trans_keycode(unsigned char kc) +{ + /* Pause/Break has the largest scancode: e1 1d 45 e1 9d c5 */ + static unsigned char codes[8]; + static int num = 0; + static int modifiers = MOD_NONE; + + struct kbentry kbe; + struct kbsentry kbs; + register int mod; + + + + MY_DEBUG(); + + + +#define KEYCODE_LCTRL 0x1d +#define KEYCODE_RCTRL 0x61 +#define KEYCODE_ALT 0x38 +#define KEYCODE_LSHIFT 0x2a +#define KEYCODE_RSHIFT 0x36 + /* Modifiers */ + switch (kc) { + case KEYCODE_LCTRL: + case KEYCODE_RCTRL: + mod = MOD_CTRL; + goto apply_mod; + case KEYCODE_ALT: + mod = MOD_ALT; + goto apply_mod; + case KEYCODE_LSHIFT: + case KEYCODE_RSHIFT: + mod = MOD_SHIFT; + goto apply_mod; + default: + goto account_key; + } +apply_mod: + if (IS_BREAK(kc)) + modifiers &= ~mod; + else + modifiers |= mod; + return TRANS_NEED_MORE; + +account_key: + codes[num++] = kc; + /* Break key, sends make and break code together */ + if (codes[0] == '\xe1') { + if (num == 6) + return KEY_BREAK; + else + return TRANS_NEED_MORE; + } + /* Keys with two keycodes, no matter, those correspond to + * single-keycode keys, we can safely use @kc != 0xe0 */ + if (codes[0] == '\xe0' && num != 2) + return TRANS_NEED_MORE; + + /* TODO: what to do with ctrl- ? */ + + /* Set table and get action code */ + if (modifiers & MOD_ALT) { + if (modifiers & MOD_SHIFT) + kbe.kb_table = K_ALTSHIFTTAB; + else + kbe.kb_table = K_ALTTAB; + } else if (modifiers & MOD_SHIFT) { + kbe.kb_table = K_SHIFTTAB; + } else { + kbe.kb_table = K_NORMTAB; + } + kbe.kb_index = kc & 0x7f; + kbe.kb_value = 0; + ioctl(no_delay.fd, KDGKBENT, &kbe); + /* Has an escape sequence? */ + kbs.kb_func = (unsigned char) kbe.kb_value; + ioctl(no_delay.fd, KDGKBSENT, &kbs); + if (kbs.kb_string[0]) + return key_defined((char *) kbs.kb_string); + else + return (int) ((unsigned char) kbe.kb_value); +} + +/** + * Change the currently pressed key + * @key: current key. 0 means that no key is pressed + * This installs the repeater + */ +static void NODELAY_change_pressed(int key) +{ + + + MY_DEBUG(); + + + +/* if (key == no_delay.pressed) + return; + if (key == 0) { + GB.Unref((void **) no_delay.timer); + } else { + no_delay.timer = GB.Every(no_delay.delay, + (GB_TIMER_CALLBACK) NODELAY_repeater, 0); + } +*/ no_delay.pressed = key; + //NODELAY_repeater(); + WINDOW_raise_read(NULL); +} + +/** + * Retrieve input in NoDelay mode, using console_ioctl(4). + * If there is a key pressed and no input available on the input queue, + * the pressed key is returned. + * @timeout: timeout in milliseconds. If that timeout expires, we return 0. + * Note that @timeout can only be in the scope of deciseconds and is + * silently cut down to those. + * The Repeater Delay applies to this function, too, in that we only return + * a pressed key when we waited for that delay. + * Note carefully, that there is an emergency exit: pressing ESC thrice + * during one second will immediately abort NoDelay mode and enter CBreak. + */ +static int NODELAY_get(int timeout) +{ + static char esc = 0; + struct termios old, new; + unsigned char b; + static time_t stamp; + int ret, res, num; + int key; /* This will already be ncurses compatible */ + struct timeval tv1, tv2; + + + + + MY_DEBUG(); + + + + if (timeout > -1) { + gettimeofday(&tv1, NULL); + tcgetattr(no_delay.fd, &old); + memcpy(&new, &old, sizeof(new)); + } + +recalc_timeout: +#define USEC2MSEC(us) (us / 1000) +#define MSEC2USEC(ms) (ms * 1000) +#define MSEC2DSEC(ms) (ms / 100) +#define DSEC2MSEC(ds) (ds * 100) +#define SEC2MSEC(s) (s * 1000) +#define MSEC2SEC(ms) (ms / 1000) + + if (timeout > -1) { + gettimeofday(&tv2, NULL); + timeout -= USEC2MSEC(tv2.tv_usec - tv1.tv_usec); + timeout -= SEC2MSEC(tv2.tv_sec - tv1.tv_sec); + if (timeout < 0) { + ret = 0; + goto cleanup; + } + } + + /* Set timeout */ + ioctl(no_delay.fd, TIOCINQ, &num); + /* We don't need to set any timeout if there are bytes available */ + if (!num && timeout > -1) { + new.c_cc[VTIME] = MSEC2DSEC(timeout); + tcsetattr(no_delay.fd, TCSANOW, &new); + gettimeofday(&tv1, NULL); + } + + /* Begin reading */ + if (no_delay.pressed && !num) { + usleep(MSEC2USEC(no_delay.delay)); + ret = no_delay.pressed; + goto cleanup; + } + /* Try to stick to the user-supplied timeout */ + ioctl(no_delay.fd, TIOCINQ, &num); + if ((res = read(no_delay.fd, &b, 1)) == -1 && errno == EINTR) { + goto recalc_timeout; + } else if (res == 0) { /* Timeout expired */ + ret = 0; + goto cleanup; + } else { /* Got a key */ + /* Emergency exit from NoDelay mode */ +#define KEYCODE_ESC 0x01 + if (b == KEYCODE_ESC) { + if (time(NULL) - stamp > 0) + esc = 0; + if (++esc == 3) { + NODELAY_exit(); + INPUT_mode(INPUT_CBREAK); + return 0; + } + stamp = time(NULL); + } + /* We use ncurses keys for operations on our no_delay data */ + if ((key = NODELAY_trans_keycode(b)) == TRANS_NEED_MORE) + goto recalc_timeout; + /* Ignore break codes, except when it is the currently + pressed key */ + if (IS_BREAK(b)) { + if (no_delay.pressed == key) +// NODELAY_change_pressed(0); + /* Key release isn't visible to the gambas programmer + * and thus not really an event to gb.ncurses. If + * time is left, we try again reading another key */ + goto recalc_timeout; + } else { +// NODELAY_change_pressed(key); + } + } + + ret = key; + +cleanup: + if (timeout > -1) + tcsetattr(no_delay.fd, TCSANOW, &old); + return ret; +} +#endif diff --git a/gb.ncurses/src/c_input.h b/gb.ncurses/src/c_input.h new file mode 100644 index 00000000..839c0b3e --- /dev/null +++ b/gb.ncurses/src/c_input.h @@ -0,0 +1,63 @@ +/* + * c_input.h - gb.ncurses opaque input routines for use of either ncurses or + * the terminal driver directly + * + * Copyright (C) 2012 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_INPUT_H +#define __C_INPUT_H + +#include "c_screen.h" + +enum { + /* Return the current mode */ + INPUT_RETURN = -1, + /* Line discipline, signal generation */ + INPUT_COOKED, + /* No line discipline, signal generation */ + INPUT_CBREAK, + /* No line discipline, no signal generation */ + INPUT_RAW, +#if 0 + /* Use terminal driver, enabled to use raw scancodes + (which are internally converted to ncurses keys but enable to + distinguish key press and release) */ + INPUT_NODELAY +#endif +}; + +enum { + TIMEOUT_NOTIMEOUT = -1 +}; + +#ifndef __C_INPUT_C +extern GB_DESC CInputDesc[]; +#endif + +int INPUT_init(); +void INPUT_exit(); +void INPUT_mode(CSCREEN *scr, int mode); +int INPUT_get(int); +void INPUT_drain(); +#ifdef __C_INPUT_C +static void INPUT_watch(int); +static void INPUT_callback(int, int, intptr_t); +#endif + +#endif /* __C_INPUT_H */ diff --git a/gb.ncurses/src/c_key.c b/gb.ncurses/src/c_key.c new file mode 100644 index 00000000..11a78b56 --- /dev/null +++ b/gb.ncurses/src/c_key.c @@ -0,0 +1,76 @@ +/* + * c_key.c - gb.ncurses Key static class + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_KEY_C + +#include + +#include "../gambas.h" + +#include "main.h" +#include "c_key.h" + +BEGIN_METHOD(Key_get, GB_STRING key) + + GB.ReturnInteger((int) *STRING(key)); + +END_METHOD + +GB_DESC CKeyDesc[] = { + GB_DECLARE("Key", 0), + GB_NOT_CREATABLE(), + + GB_CONSTANT("Return", "i", (int) '\n'), + GB_CONSTANT("Esc", "i", (int) '\x1b'), + + GB_CONSTANT("Break", "i", KEY_BREAK), + GB_CONSTANT("Home", "i", KEY_HOME), + GB_CONSTANT("Backspace", "i", KEY_BACKSPACE), + GB_CONSTANT("PageUp", "i", KEY_PPAGE), + GB_CONSTANT("PageDown", "i", KEY_NPAGE), + GB_CONSTANT("Enter", "i", KEY_ENTER), + + /* Arrow Keys */ + GB_CONSTANT("Left", "i", KEY_LEFT), + GB_CONSTANT("Right", "i", KEY_RIGHT), + GB_CONSTANT("Up", "i", KEY_UP), + GB_CONSTANT("Down", "i", KEY_DOWN), + + /* F keys */ + GB_CONSTANT("F1", "i", KEY_F(1)), + GB_CONSTANT("F2", "i", KEY_F(2)), + GB_CONSTANT("F3", "i", KEY_F(3)), + GB_CONSTANT("F4", "i", KEY_F(4)), + GB_CONSTANT("F5", "i", KEY_F(5)), + GB_CONSTANT("F6", "i", KEY_F(6)), + GB_CONSTANT("F7", "i", KEY_F(7)), + GB_CONSTANT("F8", "i", KEY_F(8)), + GB_CONSTANT("F9", "i", KEY_F(9)), + GB_CONSTANT("F10", "i", KEY_F(10)), + GB_CONSTANT("F11", "i", KEY_F(11)), + GB_CONSTANT("F12", "i", KEY_F(12)), + + /* ncurses.h is full of other ones. Just tell me what you need. */ + + GB_STATIC_METHOD("_get", "i", Key_get, "(Key)s"), + + GB_END_DECLARE +}; diff --git a/gb.ncurses/src/c_key.h b/gb.ncurses/src/c_key.h new file mode 100644 index 00000000..c32b2b3e --- /dev/null +++ b/gb.ncurses/src/c_key.h @@ -0,0 +1,29 @@ +/* + * c_key.h - gb.ncurses Key static class + * + * Copyright (C) 2012 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_KEY_H +#define __C_KEY_H + +#ifndef __C_KEY_C +extern GB_DESC CKeyDesc[]; +#endif + +#endif /* __C_KEY_H */ diff --git a/gb.ncurses/src/c_screen.c b/gb.ncurses/src/c_screen.c new file mode 100644 index 00000000..eac2df77 --- /dev/null +++ b/gb.ncurses/src/c_screen.c @@ -0,0 +1,272 @@ +/* + * c_screen.c - gb.ncurses Screen class + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_SCREEN_C + +#include +#include +#include + +#include +#include + +#include "gambas.h" + +#include "main.h" +#include "c_screen.h" +#include "c_input.h" + +#define THIS _active + +#define E_UNSUPP "Unsupported value" + +static CSCREEN *_active; + +GB_SIGNAL_CALLBACK *_sigwinch_cb; + +DECLARE_EVENT(EVENT_Resize); + +static void SCREEN_sigwinch(int signum, intptr_t data) +{ + /* TODO: ncurses may be configured to provide its own SIGWINCH + * handler. See resizeterm(3X). */ + if (signum == SIGWINCH) + GB.Raise(_active, EVENT_Resize, 0); +} + +int SCREEN_init() +{ + _sigwinch_cb = GB.Signal.Register(SIGWINCH, SCREEN_sigwinch, + (intptr_t) NULL); + return 0; +} + +void SCREEN_exit() +{ + GB.Signal.Unregister(SIGWINCH, _sigwinch_cb); +} + +void SCREEN_refresh() +{ + if (!NCURSES_RUNNING) + return; + update_panels(); + doupdate(); +} + +BEGIN_METHOD_VOID(Screen_init) + + _active = GB.AutoCreate(GB.FindClass("Screen"), 0); + +END_METHOD + +static int CSCREEN_cursor(CSCREEN *scr, int mode) +{ + if (mode >= 0 && mode <= 2) + curs_set(mode); + else + return -1; + scr->cursor = mode; + return 0; +} + +static void CSCREEN_echo(CSCREEN *scr, int mode) +{ + if (mode) + echo(); + else + noecho(); + scr->echo = mode; +} + +BEGIN_METHOD_VOID(Screen_new) + + CSCREEN_cursor(THIS, 1); + CSCREEN_echo(THIS, 1); + INPUT_mode(THIS, INPUT_CBREAK); + +END_METHOD + +#if 0 +BEGIN_METHOD(Screen_new, GB_STRING termpath) + + char path[LENGTH(termpath) + 1]; + FILE *finout; + + strncpy(path, STRING(termpath), LENGTH(termpath)); + path[LENGTH(termpath)] = 0; + + finout = fopen(path, "r+"); + if (!finout) { + GB.Error(NULL); + return; + } + assert(_maxscreen < MAX_SCREENS); + _screens[_maxscreen] = THIS; + THIS->index = _curscreen = _maxscreen++; + THIS->screen = newterm(NULL, finout, finout); + set_term(THIS->screen); + THIS->finout = finout; + THIS->buffered = 1; + CSCREEN_cursor(THIS, 0); + CSCREEN_echo(THIS, 0); + INPUT_mode(THIS, INPUT_CBREAK); + refresh(); + +END_METHOD + +BEGIN_METHOD_VOID(Screen_free) + + void *dst, *src; + + if (THIS->index != _maxscreen - 1) { + dst = &_screens[THIS->index]; + src = dst + sizeof(_screens[0]); + memmove(dst, src, _maxscreen - THIS->index); + } + _screens[THIS->index] = NULL; + if (_curscreen) + _curscreen--; + _maxscreen--; + endwin(); + fclose(THIS->finout); + delscreen(THIS->screen); + if (!_maxscreen) { + SCREEN_exit(); + return; + } + set_term(_active->screen); + +END_METHOD +#endif + +BEGIN_METHOD_VOID(Screen_Beep) + + beep(); + +END_METHOD + +BEGIN_METHOD_VOID(Screen_Flash) + + flash(); + +END_METHOD + +BEGIN_METHOD_VOID(Screen_Refresh) + + SCREEN_refresh(); + +END_METHOD + +BEGIN_METHOD(Screen_Resize, GB_INTEGER lines; GB_INTEGER cols) + + resizeterm(VARG(lines), VARG(cols)); + +END_METHOD + +GB_DESC CCursorDesc[] = { + GB_DECLARE("Cursor", 0), + GB_NOT_CREATABLE(), + + /* According to curs_set */ + GB_CONSTANT("Hidden", "i", 0), + GB_CONSTANT("Visible", "i", 1), + GB_CONSTANT("VeryVisible", "i", 2), + + GB_END_DECLARE +}; + +BEGIN_PROPERTY(Screen_Cursor) + + if (READ_PROPERTY) { + GB.ReturnInteger(THIS->cursor); + return; + } + + if (CSCREEN_cursor(THIS, VPROP(GB_INTEGER)) == -1) + GB.Error(E_UNSUPP); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Echo) + + if (READ_PROPERTY) { + GB.ReturnBoolean(THIS->echo); + return; + } + CSCREEN_echo(THIS, !!VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Input) + + if (READ_PROPERTY) { + GB.ReturnInteger(THIS->input); + return; + } + INPUT_mode(THIS, VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Lines) + + if (READ_PROPERTY) { + GB.ReturnInteger(LINES); + return; + } + resizeterm(VPROP(GB_INTEGER), COLS); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Cols) + + if (READ_PROPERTY) { + GB.ReturnInteger(COLS); + return; + } + resizeterm(LINES, VPROP(GB_INTEGER)); + +END_PROPERTY + +GB_DESC CScreenDesc[] = { + GB_DECLARE("Screen", sizeof(CSCREEN)), + GB_AUTO_CREATABLE(), + GB_NOT_CREATABLE(), + + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + + GB_STATIC_METHOD("_init", NULL, Screen_init, NULL), + GB_STATIC_METHOD("Beep", NULL, Screen_Beep, NULL), + GB_STATIC_METHOD("Flash", NULL, Screen_Flash, NULL), + GB_STATIC_METHOD("Refresh", NULL, Screen_Refresh, NULL), + GB_STATIC_METHOD("Resize", NULL, Screen_Resize, "(Lines)i(Cols)i"), + + GB_STATIC_PROPERTY("Cursor", "i", Screen_Cursor), + GB_STATIC_PROPERTY("Echo", "b", Screen_Echo), + GB_STATIC_PROPERTY("Input", "i", Screen_Input), + + GB_STATIC_PROPERTY("Height", "i", Screen_Lines), + GB_STATIC_PROPERTY("H", "i", Screen_Lines), + GB_STATIC_PROPERTY("Width", "i", Screen_Cols), + GB_STATIC_PROPERTY("W", "i", Screen_Cols), + + GB_END_DECLARE +}; diff --git a/gb.ncurses/src/c_screen.h b/gb.ncurses/src/c_screen.h new file mode 100644 index 00000000..9767dc38 --- /dev/null +++ b/gb.ncurses/src/c_screen.h @@ -0,0 +1,49 @@ +/* + * c_screen.h - gb.ncurses Screen class + * + * Copyright (C) 2012 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include "gambas.h" +#include "c_input.h" + +#ifndef __C_SCREEN_H +#define __C_SCREEN_H + +/* This will produce final output on terminal screen */ +#define REAL_REFRESH() SCREEN_refresh() + +#ifndef __C_SCREEN_C +extern GB_DESC CScreenDesc[]; +extern GB_DESC CCursorDesc[]; +#endif + +typedef struct { + GB_BASE ob; + int index; + int echo; + int cursor; + int input; + int buffered; +} CSCREEN; + +int SCREEN_init(); +void SCREEN_exit(); +void SCREEN_refresh(); + +#endif /* __C_SCREEN_H */ diff --git a/gb.ncurses/src/c_window.c b/gb.ncurses/src/c_window.c new file mode 100644 index 00000000..ffb581f8 --- /dev/null +++ b/gb.ncurses/src/c_window.c @@ -0,0 +1,944 @@ +/* + * c_window.c - gb.ncurses Window class + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_WINDOW_C + +#include +#include + +#include +#include + +#include "gambas.h" + +#include "main.h" +#include "c_window.h" +#include "c_screen.h" +#include "c_color.h" + +#define THIS ((CWINDOW *) _object) + +#define MAKE_THING(snip, win, a, b) \ +do { \ + if ((a) == -1) \ + a = get##snip##x(win); \ + if ((b) == -1) \ + b = get##snip##y(win); \ +} while (0) + +#define MAKE_CURSOR(win, x, y) MAKE_THING(cur, win, x, y) +#define MAKE_COORDS(win, x, y) MAKE_THING(beg, win, x, y) +#define MAKE_DIM(win, x, y) MAKE_THING(max, win, w, h) + +#define CHECK_RAISE_THING(snip, win, a, b) \ +if ((a) < 0 || (a) > get##snip##x(win) \ + || (b) < 0 || (b) > get##snip##y(win)) { /* >= ? */ \ + GB.Error(GB_ERR_BOUND); \ + return; \ +} + +#define CHECK_RAISE_COORDS(win, x, y) CHECK_RAISE_THING(max, win, x, y) +#define CHECK_RAISE_DIM(win, x, y) CHECK_RAISE_THING(max, win, x, y) + +static CWINDOW *_curwin = NULL; + +DECLARE_EVENT(EVENT_Read); + +/* This macro is mostly called by Gambas implementation functions to request + * output on screen (read: to check if the output is buffered and if not + * produce output by means of REAL_REFRESH()) */ +#define REFRESH() CWINDOW_refresh(THIS) + +static void CWINDOW_refresh(CWINDOW *win) +{ + if (win->buffered) + return; + REAL_REFRESH(); +} + +void CWINDOW_raise_read(CWINDOW *win) +{ + if (!win) { + if (!_curwin) + return; + win = _curwin; + } + + if (GB.CanRaise(win, EVENT_Read)) + GB.Raise(win, EVENT_Read, 0); +} + +static void CWINDOW_from_ncurses(CWINDOW *win, WINDOW *ncwin, bool border) +{ + win->main = ncwin; + win->pan = new_panel(ncwin); + keypad(ncwin, TRUE); + win->has_border = border; + win->border = 0; + win->buffered = 0; + win->wrap = 1; + if (border) { + WINDOW *new = derwin(ncwin, getmaxy(ncwin) - 2, + getmaxx(ncwin) - 2, + 1, 1); + syncok(new, TRUE); + win->content = new; + } else { + win->content = win->main; + } + win->caption = NULL; +} + +BEGIN_METHOD(Window_new, GB_BOOLEAN out_border; GB_INTEGER x; GB_INTEGER y; + GB_INTEGER w; GB_INTEGER h) + + WINDOW *new; + int w, h; + + w = VARGOPT(w, COLS); + h = VARGOPT(h, LINES); + if (VARGOPT(out_border, 1)) { + w = MIN(w + 2, COLS); + h = MIN(h + 2, LINES); + } + new = newwin(h, w, VARGOPT(y, 0), VARGOPT(x, 0)); + CWINDOW_from_ncurses(THIS, new, VARGOPT(out_border, 1)); + REFRESH(); + +END_METHOD + +BEGIN_METHOD_VOID(Window_free) + + if (_curwin == THIS) { + _curwin = NULL; + INPUT_exit(); + } + del_panel(THIS->pan); + if (THIS->has_border) + delwin(THIS->content); + delwin(THIS->main); + if (THIS->caption) + GB.FreeString(&THIS->caption); + REAL_REFRESH(); + +END_METHOD + +BEGIN_METHOD(Window_get, GB_INTEGER y; GB_INTEGER x) + + THIS->pos.line = VARG(y); + THIS->pos.col = VARG(x); + RETURN_SELF(); + +END_METHOD + +/**G + * Wait until the user typed one of the characters listed in Opts or until + * they typed wrong Tries times (infinity if not given). + * + * If there is an uppercase letter in the Opts string and the user hits + * Return, the first uppercase letter found in Opts is taken as the default + * value and the function returns. The case of letters does not matter in + * any other regard (this function is case-insensitive). + * + * This function returns the typed character (always in lowercase!). + **/ +BEGIN_METHOD(Window_Ask, GB_STRING opts; GB_INTEGER tries) + + int t, ch, i; + char *o, c; + + t = VARGOPT(tries, -1); + o = STRING(opts); + + while (t--) { + ch = INPUT_get(TIMEOUT_NOTIMEOUT); + /* XXX: not meant for ncurses special keys */ + if (ch > 127) + continue; + if (ch == '\n') + for (i = 0; i < LENGTH(opts); i++) + if (isupper(o[i])) + goto found; + for (i = 0; i < LENGTH(opts); i++) + if (tolower(o[i]) == (char) ch) + goto found; + } + GB.ReturnNull(); + return; + +found: + c = tolower(o[i]); + GB.ReturnNewString(&c, 1); + +END_METHOD + +BEGIN_METHOD_VOID(Window_Lower) + + bottom_panel(THIS->pan); + REFRESH(); + +END_METHOD + +static void CWINDOW_clear(CWINDOW *win) +{ + werase(win->content); +} + +BEGIN_METHOD_VOID(Window_Clear) + + CWINDOW_clear(THIS); + REFRESH(); + +END_METHOD + +BEGIN_METHOD_VOID(Window_Drain) + + INPUT_drain(); + +END_METHOD + +BEGIN_METHOD(Window_DrawHLine, GB_INTEGER x; GB_INTEGER y; GB_INTEGER len; + GB_STRING ch) + + mvwhline(THIS->content, VARG(y), VARG(x), *STRING(ch), VARG(len)); + REFRESH(); + +END_METHOD + +BEGIN_METHOD(Window_DrawVLine, GB_INTEGER x; GB_INTEGER y; GB_INTEGER len; + GB_STRING ch) + + mvwvline(THIS->content, VARG(y), VARG(x), *STRING(ch), VARG(len)); + REFRESH(); + +END_METHOD + +static void CWINDOW_get(CWINDOW *win, int x, int y, unsigned int len, + char **ret) +{ + char *buf; + + MAKE_COORDS(win->content, x, y); + CHECK_RAISE_COORDS(win->content, x, y); + if (len == -1) + len = getmaxx(win->content) - getcurx(win->content); + len = MIN(len, (getmaxy(win->content) - getcury(win->content)) * + getmaxx(win->content) - getcurx(win->content) - 1); + + GB.Alloc((void **) &buf, len + 1); + len = mvwinnstr(win->content, y, x, buf, len); + if (len != ERR) + buf[len] = 0; + else + GB.Free((void **) &buf); + *ret = buf; +} + +BEGIN_METHOD(Window_Get, GB_INTEGER x; GB_INTEGER y; GB_INTEGER len) + + int len; + char *ret; + + len = VARGOPT(len, -1); + CWINDOW_get(THIS, VARG(x), VARG(y), len, &ret); + GB.ReturnNewZeroString(ret); + GB.Free((void **) &ret); + +END_METHOD + +BEGIN_METHOD_VOID(Window_Hide) + + hide_panel(THIS->pan); + REFRESH(); + +END_METHOD + +static void CWINDOW_locate(CWINDOW *win, int x, int y) +{ + MAKE_CURSOR(win->content, x, y); + CHECK_RAISE_COORDS(win->content, x, y); + wmove(win->content, y, x); +} + +BEGIN_METHOD(Window_Locate, GB_INTEGER x; GB_INTEGER y) + + CWINDOW_locate(THIS, VARG(x), VARG(y)); + REFRESH(); + +END_METHOD + +static void CWINDOW_move(CWINDOW *win, int x, int y) +{ + MAKE_COORDS(win->main, x, y); + CHECK_RAISE_COORDS(stdscr, x, y); + + move_panel(win->pan, y, x); +#if 0 + if (HAS_BORDER) { + mvwin(THIS->content, y + 1, x + 1); + WINDOW_draw_border(THIS, 1); + } +#endif +} + +BEGIN_METHOD(Window_Move, GB_INTEGER x; GB_INTEGER y) + + CWINDOW_move(THIS, VARGOPT(x, -1), VARGOPT(y, -1)); + REFRESH(); + +END_METHOD + +BEGIN_METHOD_VOID(Window_Center) + + int x, y; + + x = (COLS - getmaxx(THIS->main)) / 2; + y = (LINES - getmaxy(THIS->main)) / 2; + CWINDOW_move(THIS, x, y); + REFRESH(); + +END_METHOD + +static void CWINDOW_print(CWINDOW *win, char *str, int x, int y, + attr_t attr, int pair) +{ + int width; + char *p, *q; + attr_t asave; short psave; + + wattr_get(win->content, &asave, &psave, NULL); + if (attr == -1) + attr = asave; + if (pair == -1) + pair = psave; + wattr_set(win->content, attr, pair, NULL); + + p = str; + do { + CWINDOW_locate(win, x, y); + width = strlen(p); + if (!win->wrap) + width = MIN(width, getmaxx(win->content) - x); + + /* waddnstr, being subsequent calls to waddch, rests at the + * end of the current line but we want to go to the next + * one. */ + width = MIN(width, getmaxx(win->content) * + (getmaxy(win->content) - y) - x); + if ((q = strchr(p, '\n'))) + width = MIN(width, q - p); + waddnstr(win->content, p, width); + p += width; + x = getcurx(win->content); + y = getcury(win->content); + if (y == getmaxy(win->content) - 1) + break; + if (*p == '\n') { + y++; + p++; + } + if (*p) + x = 0; + } while (*p); + CWINDOW_locate(win, x, y); + wattr_set(win->content, asave, psave, NULL); +} + +BEGIN_METHOD(Window_Print, GB_STRING text; GB_INTEGER x; GB_INTEGER y; + GB_INTEGER attr; GB_INTEGER pair) + + char text[LENGTH(text) + 1]; + + strncpy(text, STRING(text), LENGTH(text)); + text[LENGTH(text)] = 0; + CWINDOW_print(THIS, text, VARGOPT(x, -1), VARGOPT(y, -1), + VARGOPT(attr, -1), VARGOPT(pair, -1)); + REFRESH(); + +END_METHOD + +BEGIN_METHOD_VOID(Window_Raise) + + top_panel(THIS->pan); + REFRESH(); + +END_METHOD + +BEGIN_METHOD(Window_PrintCenter, GB_STRING text; GB_INTEGER attr; + GB_INTEGER pair) + + int lines = 1; + int x, y; + char text[LENGTH(text) + 1]; + char *p, *q; + attr_t attr = VARGOPT(attr, -1); + short pair = VARGOPT(pair, -1); + + memcpy(text, STRING(text), LENGTH(text)); + text[LENGTH(text)] = 0; + p = text; + while ((q = strchr(p, '\n'))) { + lines++; + p = q + 1; + } + + p = text; + y = (getmaxy(THIS->content) - lines) / 2; + while (lines--) { + if (!lines) { + x = (getmaxx(THIS->content) - strlen(p)) / 2; + CWINDOW_print(THIS, p, x, y, attr, pair); + } else { + q = strchr(p, '\n'); + if (q == p + 1) { + y++; + continue; + } + *q = 0; + x = (getmaxx(THIS->content) - (q - p)) / 2; + CWINDOW_print(THIS, p, x, y, attr, pair); + y++; + p = q + 1; + *q = '\n'; + } + } + REFRESH(); + +END_METHOD + +static void CWINDOW_draw_border(CWINDOW *win) +{ + switch (win->border) { + case BORDER_NONE: + wborder(win->main, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '); + break; + case BORDER_ASCII: + wborder(win->main, '|', '|', '-', '-', '+', '+', '+', '+'); + break; + case BORDER_ACS: + box(win->main, 0, 0); + break; + } + if (win->border != BORDER_NONE && win->caption) { + int width = getmaxx(win->main) - 2; + + width = MIN(width, strlen(win->caption)); + mvwaddnstr(win->main, 0, 1, win->caption, width); + } +} + +static void CWINDOW_resize(CWINDOW *win, int w, int h) +{ + int x, y; + + MAKE_DIM(win->main, w, h); + + getbegyx(win->main, y, x); + if (win->has_border) { + w += 2; + h += 2; + } + /* XXX: Not rather raise an error? */ + w = MIN(w, COLS - x); + h = MIN(h, LINES - y); + + /* TODO: With the auto-created Window it does not work properly in + * Screen_Resize() from within xterm (my testcase) */ + if (win->border) + wborder(win->main, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '); + wresize(win->main, h, w); + if (win->has_border) + wresize(win->content, h - 2, w - 2); + replace_panel(win->pan, win->main); + CWINDOW_draw_border(win); +} + +BEGIN_METHOD(Window_Resize, GB_INTEGER w; GB_INTEGER h) + + CWINDOW_resize(THIS, VARGOPT(w, -1), VARGOPT(h, -1)); + REAL_REFRESH(); + +END_METHOD + +BEGIN_METHOD_VOID(Window_SetFullscreen) + + CWINDOW_move(THIS, 0, 0); + CWINDOW_resize(THIS, COLS, LINES); + REFRESH(); + +END_METHOD + +BEGIN_METHOD(Window_Read, GB_INTEGER timeout) + + int t; + int ret; + + t = VARGOPT(timeout, -1); + + ret = INPUT_get(t); + GB.ReturnInteger(ret); + +END_METHOD + +BEGIN_METHOD_VOID(Window_ReadLine) + + char line[256]; /* XXX: Must be enough! */ + int ret; + + bzero(line, sizeof(line)); + ret = wgetnstr(THIS->main, line, sizeof(line) - 1); + if (ret == ERR) { + GB.ReturnNull(); + return; + } + GB.ReturnNewZeroString(line); + +END_METHOD + +BEGIN_METHOD_VOID(Window_Show) + + show_panel(THIS->pan); + REFRESH(); + +END_METHOD + +static void CWINDOW_setfocus(CWINDOW *win) +{ + if (!_curwin) + INPUT_init(); + _curwin = win; +} + +BEGIN_METHOD_VOID(Window_SetFocus) + + CWINDOW_setfocus(THIS); + +END_METHOD + +BEGIN_PROPERTY(Window_Attributes) + + if (READ_PROPERTY) { + attr_t attr = 0; + short pair; + + wattr_get(THIS->content, &attr, &pair, NULL); + GB.ReturnInteger(attr); + return; + } + wattrset(THIS->content, VPROP(GB_INTEGER)); + +END_PROPERTY + +/* This is *not* a shortcut to setting Window.{Fore,Back}ground. The latter + * two set the colour of everything in the window. This function only sets + * the colour pair for all subsequent writes! */ +BEGIN_PROPERTY(Window_Pair) + + chtype bkgd; + + bkgd = getbkgd(THIS->content); + + if (READ_PROPERTY) { + GB.ReturnInteger(PAIR_NUMBER(bkgd & A_COLOR)); + return; + } + bkgd = COLOR_PAIR(VPROP(GB_INTEGER)); + wbkgdset(THIS->content, bkgd); + +END_PROPERTY + +#define COLOR_METHOD(r, f, b) \ +short pair = 0, fg, bg; \ +attr_t attr; \ + \ +wattr_get(THIS->content, &attr, &pair, NULL); \ +pair_content(pair, &fg, &bg); \ +if (READ_PROPERTY) { \ + GB.ReturnInteger(r); \ + return; \ +} \ +pair = CPAIR_get(f, b); \ +if (pair == -1) { \ + GB.Error(GB_ERR_BOUND); \ + return; \ +} \ +wbkgd(THIS->content, COLOR_PAIR(pair) | ' '); \ +REFRESH(); + +/* + * One should be careful when using these two properties. wbkgd() changes + * fore- *and* background of *all* characters in the window. So setting + * the background may result in resetting even the foreground of differently + * coloured characters. + * + * Best is to only set Background/Foreground once at the beginning of the + * program when using colours extensively during the program. + */ + +BEGIN_PROPERTY(Window_Background) + + COLOR_METHOD(bg, fg, VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Foreground) + + COLOR_METHOD(fg, VPROP(GB_INTEGER), bg); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Border) + + if (READ_PROPERTY) { + GB.ReturnInteger(THIS->border); + return; + } + + THIS->border = VPROP(GB_INTEGER); + CWINDOW_draw_border(THIS); + REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Buffered) + + if (READ_PROPERTY) { + GB.ReturnBoolean(THIS->buffered); + return; + } + THIS->buffered = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Caption) + + if (READ_PROPERTY) { + GB.ReturnString(THIS->caption); + return; + } + + if (THIS->caption) + GB.FreeString(&THIS->caption); + THIS->caption = GB.NewZeroString(PSTRING()); + CWINDOW_draw_border(THIS); + REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(Window_CursorX) + + if (READ_PROPERTY) { + GB.ReturnInteger(getcurx(THIS->content)); + return; + } + CWINDOW_locate(THIS, VPROP(GB_INTEGER), -1); + REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(Window_CursorY) + + if (READ_PROPERTY) { + GB.ReturnInteger(getcury(THIS->content)); + return; + } + CWINDOW_locate(THIS, -1, VPROP(GB_INTEGER)); + REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Height) + + if (READ_PROPERTY) { + GB.ReturnInteger(getmaxy(THIS->content)); + return; + } + CWINDOW_resize(THIS, -1, VPROP(GB_INTEGER)); + REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Wrap) + + if (READ_PROPERTY) { + GB.ReturnBoolean(THIS->wrap); + return; + } + THIS->wrap = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Width) + + if (READ_PROPERTY) { + GB.ReturnInteger(getmaxx(THIS->content)); + return; + } + CWINDOW_resize(THIS, VPROP(GB_INTEGER), -1); + REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(Window_X) + + if (READ_PROPERTY) { + GB.ReturnInteger(getbegx(THIS->main)); + return; + } + CWINDOW_move(THIS, VPROP(GB_INTEGER), -1); + REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Y) + + if (READ_PROPERTY) { + GB.ReturnInteger(getbegy(THIS->main)); + return; + } + CWINDOW_move(THIS, -1, VPROP(GB_INTEGER)); + REFRESH(); + +END_PROPERTY + +GB_DESC CWindowDesc[] = { + GB_DECLARE("Window", sizeof(CWINDOW)), + GB_AUTO_CREATABLE(), + + GB_EVENT("Read", NULL, NULL, &EVENT_Read), + + /* Methods */ + GB_METHOD("_new", NULL, Window_new, "[(BorderFrame)b(X)i(Y)i(W)i(H)i]"), + GB_METHOD("_free", NULL, Window_free, NULL), + GB_METHOD("_get", ".Char.Attrs", Window_get, "(Y)i(X)i"), + + GB_METHOD("Ask", "s", Window_Ask, "(Opts)s[(Tries)i]"), + GB_METHOD("Read", "i", Window_Read, "[(Timeout)i]"), + GB_METHOD("ReadLine", "s", Window_ReadLine, NULL), + GB_METHOD("Drain", NULL, Window_Drain, NULL), + + GB_METHOD("Get", "s", Window_Get, "[(X)i(Y)i(Len)i]"), + + GB_METHOD("Clear", NULL, Window_Clear, NULL), + GB_METHOD("Cls", NULL, Window_Clear, NULL), + + GB_METHOD("Lower", NULL, Window_Lower, NULL), + GB_METHOD("Raise", NULL, Window_Raise, NULL), + GB_METHOD("Hide", NULL, Window_Hide, NULL), + GB_METHOD("Show", NULL, Window_Show, NULL), + + GB_METHOD("DrawHLine", NULL, Window_DrawHLine,"(X)i(Y)i(Len)i(C)s"), + GB_METHOD("DrawVLine", NULL, Window_DrawVLine,"(X)i(Y)i(Len)i(C)s"), + GB_METHOD("Print", NULL, Window_Print, "(Text)s[(X)i(Y)i(Attr)i(Pair)i]"), + GB_METHOD("PrintCenter", NULL, Window_PrintCenter, "(Text)s[(Attr)i(Pair)i]"), + + GB_METHOD("Locate", NULL, Window_Locate, "(X)i(Y)i"), + + GB_METHOD("Move", NULL, Window_Move, "[(X)i(Y)i]"), + GB_METHOD("Center", NULL, Window_Center, NULL), + GB_METHOD("Resize", NULL, Window_Resize, "[(W)i(H)i]"), + GB_METHOD("SetFullscreen", NULL, Window_SetFullscreen, NULL), + + GB_METHOD("SetFocus", NULL, Window_SetFocus, NULL), + + GB_PROPERTY("Attributes", "i", Window_Attributes), + + GB_PROPERTY("Background", "i", Window_Background), + GB_PROPERTY("Paper", "i", Window_Background), + GB_PROPERTY("Foreground", "i", Window_Foreground), + GB_PROPERTY("Pen", "i", Window_Foreground), + GB_PROPERTY("Pair", "i", Window_Pair), + + GB_PROPERTY("Border", "i", Window_Border), + GB_PROPERTY("Buffered", "b", Window_Buffered), + GB_PROPERTY("Caption", "s", Window_Caption), + + GB_PROPERTY("CursorX", "i", Window_CursorX), + GB_PROPERTY("CursorY", "i", Window_CursorY), + + /* GB_PROPERTY("ClientHeight", ...), etc */ + GB_PROPERTY("Height", "i", Window_Height), + GB_PROPERTY("H", "i", Window_Height), + GB_PROPERTY("Width", "i", Window_Width), + GB_PROPERTY("W", "i", Window_Width), + + GB_PROPERTY("Wrap", "b", Window_Wrap), + + GB_PROPERTY("X", "i", Window_X), + GB_PROPERTY("Y", "i", Window_Y), + + GB_END_DECLARE +}; + +/* + * Attrs + */ + +GB_DESC CWindowAttrsDesc[] = { + GB_DECLARE("Attr", 0), + GB_NOT_CREATABLE(), + + GB_CONSTANT("Normal", "i", A_NORMAL), + GB_CONSTANT("Underline", "i", A_UNDERLINE), + GB_CONSTANT("Reverse", "i", A_REVERSE), + GB_CONSTANT("Blink", "i", A_BLINK), + GB_CONSTANT("Bold", "i", A_BOLD), + + GB_END_DECLARE +}; + +/* + * .Char.Attrs + */ + +/* Seemingly, chgat() doesn't mark the window dirty. We use wtouchln() and + * wsyncup(). */ +#define CHAR_ATTR_METHOD(a) \ +int ox, oy; \ +chtype ch; \ + \ +getyx(THIS->content, oy, ox); \ +ch = mvwinch(THIS->content, THIS->pos.line, THIS->pos.col); \ +if (READ_PROPERTY) { \ + GB.ReturnBoolean(ch & a); \ + return; \ +} \ +if (VPROP(GB_BOOLEAN)) \ + wchgat(THIS->content, 1, (ch & A_ATTRIBUTES) | a, \ + PAIR_NUMBER(ch), NULL); \ +else \ + wchgat(THIS->content, 1, (ch & A_ATTRIBUTES) & ~a, \ + PAIR_NUMBER(ch), NULL); \ +wtouchln(THIS->content, THIS->pos.line, 1, 1); \ +wsyncup(THIS->content); \ +wmove(THIS->content, oy, ox); \ +REFRESH(); + +BEGIN_PROPERTY(CharAttrs_Normal) + + int ox, oy; + chtype ch; + + getyx(THIS->content, oy, ox); + ch = mvwinch(THIS->content, THIS->pos.line, THIS->pos.col); + if (READ_PROPERTY) { + GB.ReturnBoolean((ch & A_ATTRIBUTES) == A_NORMAL); + return; + } + if (VPROP(GB_BOOLEAN)) + wchgat(THIS->content, 1, A_NORMAL, PAIR_NUMBER(ch), NULL); + wtouchln(THIS->content, THIS->pos.line, 1, 1); + wmove(THIS->content, oy, ox); + REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(CharAttrs_Underline) + + CHAR_ATTR_METHOD(A_UNDERLINE); + +END_PROPERTY + +BEGIN_PROPERTY(CharAttrs_Reverse) + + CHAR_ATTR_METHOD(A_REVERSE); + +END_PROPERTY + +BEGIN_PROPERTY(CharAttrs_Blink) + + CHAR_ATTR_METHOD(A_BLINK); + +END_PROPERTY + +BEGIN_PROPERTY(CharAttrs_Bold) + + CHAR_ATTR_METHOD(A_BOLD); + +END_PROPERTY + +#define CHAR_COLOR_METHOD(r, f, b) \ +short pair; \ +int ox, oy; \ +chtype ch; \ +short fg, bg; \ + \ +getyx(THIS->content, oy, ox); \ +ch = mvwinch(THIS->content, THIS->pos.line, THIS->pos.col);\ +pair = PAIR_NUMBER(ch & A_COLOR); \ +pair_content(pair, &fg, &bg); \ +if (READ_PROPERTY) { \ + GB.ReturnInteger(r); \ + return; \ +} \ +pair = CPAIR_get(f, b); \ +if (pair == -1) { \ + GB.Error(GB_ERR_BOUND); \ + return; \ +} \ +wchgat(THIS->content, 1, (ch & A_ATTRIBUTES), pair, NULL);\ +wtouchln(THIS->content, THIS->pos.line, 1, 1); \ +wsyncup(THIS->content); \ +wmove(THIS->content, oy, ox); \ +REFRESH(); + +BEGIN_PROPERTY(CharAttrs_Background) + + CHAR_COLOR_METHOD(bg, fg, VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CharAttrs_Foreground) + + CHAR_COLOR_METHOD(fg, VPROP(GB_INTEGER), bg); + +END_PROPERTY + +GB_DESC CCharAttrsDesc[] = { + GB_DECLARE(".Char.Attrs", 0), + GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Normal", "b", CharAttrs_Normal), + GB_PROPERTY("Underline", "b", CharAttrs_Underline), + GB_PROPERTY("Reverse", "b", CharAttrs_Reverse), + GB_PROPERTY("Blink", "b", CharAttrs_Blink), + GB_PROPERTY("Bold", "b", CharAttrs_Bold), + + GB_PROPERTY("Background", "i", CharAttrs_Background), + GB_PROPERTY("Foreground", "i", CharAttrs_Foreground), + + GB_END_DECLARE +}; + +/* + * Border + */ + +GB_DESC CBorderDesc[] = { + GB_DECLARE("Border", 0), + GB_NOT_CREATABLE(), + + GB_CONSTANT("None", "i", BORDER_NONE), + GB_CONSTANT("Ascii", "i", BORDER_ASCII), + GB_CONSTANT("ACS", "i", BORDER_ACS), + + GB_END_DECLARE +}; diff --git a/gb.ncurses/src/c_window.h b/gb.ncurses/src/c_window.h new file mode 100644 index 00000000..65597531 --- /dev/null +++ b/gb.ncurses/src/c_window.h @@ -0,0 +1,63 @@ +/* + * c_window.h - gb.ncurses Window class + * + * Copyright (C) 2012 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_WINDOW_H +#define __C_WINDOW_H + +#include +#include + +#include "gambas.h" +#include "gb_common.h" + +/* Border constants */ +enum { + BORDER_NONE = 0, + BORDER_ASCII, + BORDER_ACS +}; + +typedef struct { + GB_BASE ob; + WINDOW *main; + WINDOW *content; + PANEL *pan; + bool has_border; + int border; + bool buffered; + bool wrap; + char *caption; + struct { + int line; + int col; + } pos; +} CWINDOW; + +#ifndef __C_WINDOW_C +extern GB_DESC CWindowDesc[]; +extern GB_DESC CWindowAttrsDesc[]; +extern GB_DESC CCharAttrsDesc[]; +extern GB_DESC CBorderDesc[]; +#endif + +void CWINDOW_raise_read(CWINDOW *win); + +#endif /* __C_WINDOW_C */ diff --git a/gb.ncurses/src/gb.ncurses.component b/gb.ncurses/src/gb.ncurses.component new file mode 100644 index 00000000..7bb19321 --- /dev/null +++ b/gb.ncurses/src/gb.ncurses.component @@ -0,0 +1,3 @@ +[Component] +Author=Tobias Boege +State=Unfinished diff --git a/gb.ncurses/src/main.c b/gb.ncurses/src/main.c new file mode 100644 index 00000000..1548e06c --- /dev/null +++ b/gb.ncurses/src/main.c @@ -0,0 +1,105 @@ +/* + * main.c - gb.ncurses main object + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __MAIN_C + +#include "c_window.h" +#include "c_key.h" +#include "c_color.h" +#include "c_screen.h" +#include "c_input.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = { + CScreenDesc, + CInputDesc, + CCursorDesc, + + CWindowDesc, + CWindowAttrsDesc, + CCharAttrsDesc, + CBorderDesc, + + CKeyDesc, + + CColorDesc, + CColorInfoDesc, + CPairDesc, + + NULL +}; + +static bool _init = FALSE; + +bool MAIN_running() +{ + return _init && (!isendwin() || stdscr); +} + +static void MAIN_init() +{ + if (_init) + return; + + initscr(); + keypad(stdscr, TRUE); + + SCREEN_init(); + COLOR_init(); + + refresh(); + + _init = TRUE; +} + +static void MAIN_exit() +{ + if (_init) { + SCREEN_exit(); + endwin(); + _init = FALSE; + } +} + +static void MAIN_hook_error(int code, char *error, char *where) +{ + MAIN_exit(); +} + +static void MAIN_hook_main(int *argc, char **argv) +{ + MAIN_init(); +} + +int EXPORT GB_INIT() +{ + GB.Hook(GB_HOOK_ERROR, (void *) MAIN_hook_error); + GB.Hook(GB_HOOK_MAIN, (void *) MAIN_hook_main); + return 0; +} + + +void EXPORT GB_EXIT() +{ + MAIN_exit(); +} diff --git a/gb.ncurses/src/main.h b/gb.ncurses/src/main.h new file mode 100644 index 00000000..46b63967 --- /dev/null +++ b/gb.ncurses/src/main.h @@ -0,0 +1,37 @@ +/* + * main.h - gb.ncurses main object + * + * Copyright (C) 2012 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __MAIN_H +#define __MAIN_H + + +#include "gb_common.h" +#include "gambas.h" + +#define NCURSES_RUNNING MAIN_running() + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +bool MAIN_running(); + +#endif /* __MAIN_H */ diff --git a/gb.net.curl/AUTHORS b/gb.net.curl/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.net.curl/COPYING b/gb.net.curl/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.net.curl/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.net.curl/ChangeLog b/gb.net.curl/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.net.curl/INSTALL b/gb.net.curl/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.net.curl/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.net.curl/Makefile.am b/gb.net.curl/Makefile.am new file mode 100644 index 00000000..35e0fec0 --- /dev/null +++ b/gb.net.curl/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @CURL_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.net.curl/NEWS b/gb.net.curl/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.net.curl/README b/gb.net.curl/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.net.curl/acinclude.m4 b/gb.net.curl/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.net.curl/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.net.curl/component.am b/gb.net.curl/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.net.curl/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.net.curl/configure.ac b/gb.net.curl/configure.ac new file mode 100644 index 00000000..98e1bf39 --- /dev/null +++ b/gb.net.curl/configure.ac @@ -0,0 +1,20 @@ +dnl ---- configure.ac for gb.net.curl + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-net-curl, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.net.curl) +AC_PROG_LIBTOOL + +GB_COMPONENT_PKG_CONFIG( + curl, CURL, gb.net.curl, [src], + 'libcurl >= 7.13' + ) + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.net.curl/gambas.h b/gb.net.curl/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.net.curl/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.net.curl/gb_common.h b/gb.net.curl/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.net.curl/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.net.curl/m4 b/gb.net.curl/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.net.curl/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.net.curl/reconf b/gb.net.curl/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.net.curl/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.net.curl/src/CCurl.c b/gb.net.curl/src/CCurl.c new file mode 100644 index 00000000..94b2c074 --- /dev/null +++ b/gb.net.curl/src/CCurl.c @@ -0,0 +1,776 @@ +/*************************************************************************** + + CCurl.c + + (c) 2003-2008 Daniel Campos Fernández + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCURL_C + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "main.h" +#include "gambas.h" +#include "CCurl.h" +#include "CProxy.h" + +DECLARE_EVENT(EVENT_Finished); +DECLARE_EVENT(EVENT_Error); +DECLARE_EVENT(EVENT_Connect); +DECLARE_EVENT(EVENT_Read); +DECLARE_EVENT(EVENT_Progress); +DECLARE_EVENT(EVENT_Cancel); + +static CCURL *_async_list = NULL; + +static void add_to_async_list(CCURL *_object) +{ + if (THIS->in_list) + return; + + #ifdef DEBUG + fprintf(stderr, "add_to_async_list: %p\n", THIS); + #endif + + GB.List.Add(&_async_list, THIS, &THIS->list); + THIS->in_list = TRUE; + GB.Ref(THIS); +} + +static void remove_from_async_list(CCURL *_object) +{ + if (!THIS->in_list) + return; + + #ifdef DEBUG + fprintf(stderr, "remove_from_async_list: %p\n", THIS); + #endif + + GB.List.Remove(&_async_list, THIS, &THIS->list); + THIS->in_list = FALSE; + GB.Unref(POINTER(&_object)); +} + +/***************************************************** +CURLM : a pointer to use curl_multi interface, +allowing asynchrnous work without using threads +in this class. +******************************************************/ +CURLM *CCURL_multicurl; +int CCURL_pipe[2] = {-1, -1}; + +/****************************************************** +Events from this class +******************************************************/ +GB_STREAM_DESC CurlStream = +{ + open: CCURL_stream_open, + close: CCURL_stream_close, + read: CCURL_stream_read, + write: CCURL_stream_write, + seek: CCURL_stream_seek, + tell: CCURL_stream_tell, + flush: CCURL_stream_flush, + eof: CCURL_stream_eof, + lof: CCURL_stream_lof, + handle: CCURL_stream_handle, +}; + +//////////////////////////////////////////////////////////////////// +// STREAM // +//////////////////////////////////////////////////////////////////// + +/* not allowed stream methods */ + +int CCURL_stream_handle(GB_STREAM *stream) { return -1;} +int CCURL_stream_open(GB_STREAM *stream, const char *path, int mode, void *data){return -1;} +int CCURL_stream_seek(GB_STREAM *stream, int64_t pos, int whence){ return -1;} +int CCURL_stream_tell(GB_STREAM *stream, int64_t *pos){return -1; } +int CCURL_stream_flush(GB_STREAM *stream) { return 0;} +int CCURL_stream_close(GB_STREAM *stream) { return 0;} +int CCURL_stream_write(GB_STREAM *stream, char *buffer, int len){return -1;} + +int CCURL_stream_lof(GB_STREAM *stream, int64_t *len) +{ + void *_object = STREAM_TO_OBJECT(stream); + + *len = 0; + + if ((THIS_STATUS != NET_RECEIVING_DATA ) && (THIS_STATUS != NET_INACTIVE)) + return -1; + + *len = GB.StringLength(THIS->data); + return 0; +} + +int CCURL_stream_eof(GB_STREAM *stream) +{ + void *_object = STREAM_TO_OBJECT(stream); + + if ((THIS_STATUS != NET_RECEIVING_DATA ) && (THIS_STATUS != NET_INACTIVE)) return -1; + if (!GB.StringLength(THIS->data)) return -1; + return 0; +} + +int CCURL_stream_read(GB_STREAM *stream, char *buffer, int len) +{ + void *_object = STREAM_TO_OBJECT(stream); + int len_data; + char *new_data; + + if ((THIS_STATUS != NET_RECEIVING_DATA ) && (THIS_STATUS != NET_INACTIVE)) return -1; + + len_data = GB.StringLength(THIS->data); + + if (len_data < len) + len = len_data; + + memcpy(buffer, THIS->data, len); + + len_data -= len; + + if (len_data > 0) + new_data = GB.NewString(THIS->data + len, len_data); + else + new_data = NULL; + + GB.FreeString(&THIS->data); + THIS->data = new_data; + + return len; +} + +static void raise_event(void *_object, int event) +{ + GB.Raise(THIS, event, 0); + GB.Unref(POINTER(&_object)); +} + +void CURL_raise_finished(void *_object) +{ + raise_event(THIS, EVENT_Finished); +} + +void CURL_raise_error(void *_object) +{ + raise_event(THIS, EVENT_Error); +} + +void CURL_raise_cancel(void *_object) +{ + raise_event(THIS, EVENT_Cancel); +} + +void CURL_raise_connect(void *_object) +{ + raise_event(THIS, EVENT_Connect); +} + +void CURL_raise_read(void *_object) +{ + if (GB.CanRaise(THIS, EVENT_Read)) + { + GB.Raise(THIS, EVENT_Read, 0); + + if (!GB.Stream.Eof(&THIS->stream)) + { + GB.Ref(THIS); + GB.Post(CURL_raise_read, (intptr_t)THIS); + } + } + + GB.Unref(POINTER(&_object)); +} + +void CURL_manage_error(void *_object, int error) +{ + if (THIS_FILE) + { + fclose(THIS_FILE); + THIS_FILE = NULL; + } + + switch (error) + { + case CURLE_OK: + + if (THIS->async) + { + #if DEBUG + fprintf(stderr, "-- [%p] curl_multi_remove_handle(%p)\n", THIS, THIS_CURL); + #endif + curl_multi_remove_handle(CCURL_multicurl,THIS_CURL); + } + GB.Ref(THIS); + GB.Post(CURL_raise_finished, (intptr_t)THIS); + CURL_stop(THIS); + THIS_STATUS = NET_INACTIVE; + break; + + default: + + if (THIS->async) + { + #if DEBUG + fprintf(stderr, "-- [%p] curl_multi_remove_handle(%p)\n", THIS, THIS_CURL); + #endif + curl_multi_remove_handle(CCURL_multicurl,THIS_CURL); + } + GB.Ref(THIS); + GB.Post(CURL_raise_error, (intptr_t)THIS); + CURL_stop(THIS); + THIS_STATUS = (- (1000 + error)); + break; + } +} + +void CURL_init_stream(void *_object) +{ + THIS->stream.desc = &CurlStream; + THIS->stream.tag = THIS; + GB.Stream.SetAvailableNow(&THIS->stream, TRUE); +} + +void CURL_init_options(void *_object) +{ + curl_easy_setopt(THIS_CURL, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(THIS_CURL, CURLOPT_TIMEOUT, THIS->timeout); + curl_easy_setopt(THIS_CURL, CURLOPT_VERBOSE, (bool)THIS->debug); + curl_easy_setopt(THIS_CURL, CURLOPT_PRIVATE, (char*)_object); + + if (THIS->buffer_size) + curl_easy_setopt(THIS_CURL, CURLOPT_BUFFERSIZE, THIS->buffer_size); + + curl_easy_setopt(THIS_CURL, CURLOPT_SSL_VERIFYPEER, THIS->ssl_verify_peer ? 1 : 0); + curl_easy_setopt(THIS_CURL, CURLOPT_SSL_VERIFYHOST , THIS->ssl_verify_host ? 2 : 0); + + CURL_proxy_set(&THIS->proxy.proxy, THIS_CURL); + CURL_user_set(&THIS->user, THIS_CURL); + curl_easy_setopt(THIS_CURL, CURLOPT_URL, THIS_URL); +} + +#define CHECK_PROGRESS_VAL(_var) if (THIS->_var != (int64_t)_var) { THIS->_var = (int64_t)_var; raise = TRUE; } + +static int curl_progress(void *_object, double dltotal, double dlnow, double ultotal, double ulnow) +{ + bool raise = FALSE; + + if (THIS->progresscb) + (*THIS->progresscb)(THIS, &dltotal, &dlnow, &ultotal, &ulnow); + + CHECK_PROGRESS_VAL(dltotal); + CHECK_PROGRESS_VAL(dlnow); + CHECK_PROGRESS_VAL(ultotal); + CHECK_PROGRESS_VAL(ulnow); + + if (raise) + GB.RaiseLater(THIS, EVENT_Progress); + + return 0; +} + + +/*************************************************************** +This CallBack is called each event loop by Gambas to test +the status of curl descriptors +***************************************************************/ + +static void stop_post() +{ + if (CCURL_pipe[0] < 0) return; + + GB.Watch (CCURL_pipe[0], GB_WATCH_NONE, NULL, 0); + close(CCURL_pipe[0]); + close(CCURL_pipe[1]); + CCURL_pipe[0]=-1; +} + +static void CCURL_post_curl(intptr_t data) +{ + CURLMsg *Msg; + int nread; + int post=1; + void *_object; + char *tmp; + + do + { + usleep(1000); + } + while(CURLM_CALL_MULTI_PERFORM == curl_multi_perform(CCURL_multicurl,&nread)); + + if (!nread) post=0; + + do + { + Msg=curl_multi_info_read(CCURL_multicurl,&nread); + if (!Msg) nread=0; + if (Msg) + { + curl_easy_getinfo(Msg->easy_handle,CURLINFO_PRIVATE,&tmp); + _object=(void*)tmp; + CURL_manage_error(THIS,Msg->data.result); + } + } + while (nread); + + if (!post) + stop_post(); +} + +void CURL_stop(void *_object) +{ + if (THIS_STATUS == NET_INACTIVE) + return; + + if (THIS_CURL) + { + #if DEBUG + fprintf(stderr, "-- CURL_stop: [%p] curl_multi_remove_handle(%p)\n", THIS, THIS_CURL); + #endif + curl_multi_remove_handle(CCURL_multicurl,THIS_CURL); + #if DEBUG + fprintf(stderr, "-- CURL_stop: [%p] curl_easy_cleanup(%p)\n", THIS, THIS_CURL); + #endif + curl_easy_cleanup(THIS_CURL); + THIS_CURL = NULL; + } + + if (THIS_FILE) + { + fclose(THIS_FILE); + THIS_FILE = NULL; + } + + THIS_STATUS = NET_INACTIVE; + + remove_from_async_list(THIS); +} + +static void init_post(void) +{ + if (CCURL_pipe[0]!=-1) return; + + if (pipe(CCURL_pipe)) + { + fprintf(stderr, "gb.net.curl: warning: unable to create the client watching pipe: %s\n", strerror(errno)); + return; + } + + GB.Watch (CCURL_pipe[0], GB_WATCH_READ, CCURL_post_curl, 0); + if (write(CCURL_pipe[1], "1", sizeof(char)) != 1) + fprintf(stderr, "gb.net.curl: warning: unable to write to the client watching pipe: %s\n", strerror(errno)); +} + +void CURL_start_post(void *_object) +{ + init_post(); + curl_multi_add_handle(CCURL_multicurl, THIS_CURL); + add_to_async_list(THIS); +} + +bool CURL_check_active(void *_object) +{ + if (THIS_STATUS > 0) + { + GB.Error("Property is read-only while client is active"); + return TRUE; + } + else + return FALSE; +} + +void CURL_set_progress(void *_object, bool progress, CURL_FIX_PROGRESS_CB cb) +{ + #ifdef DEBUG + fprintf(stderr, "CURL_set_progress: %p %d\n", _object, progress); + #endif + + curl_easy_setopt(THIS_CURL, CURLOPT_NOPROGRESS, progress ? 0 : 1); + if (progress) + { + curl_easy_setopt(THIS_CURL, CURLOPT_PROGRESSFUNCTION , curl_progress); + curl_easy_setopt(THIS_CURL, CURLOPT_PROGRESSDATA , _object); + } + + THIS->progresscb = cb; +} + +#define COPY_STRING(_field) \ +{ \ + GB.FreeString(&dest->_field); \ + dest->_field = src->_field; \ + if (dest->_field) dest->_field = GB.NewString(dest->_field, GB.StringLength(dest->_field)); \ +} + +bool CURL_copy_from(CCURL *dest, CCURL *src) +{ + if (CURL_check_active(dest)) + return TRUE; + + dest->async = src->async; + dest->timeout = src->timeout; + dest->debug = src->debug; + dest->buffer_size = src->buffer_size; + COPY_STRING(url); + + dest->user.auth = src->user.auth; + COPY_STRING(user.user); + COPY_STRING(user.userpwd); + COPY_STRING(user.pwd); + + dest->proxy.proxy.type = src->proxy.proxy.type; + dest->proxy.proxy.auth = src->proxy.proxy.auth; + COPY_STRING(proxy.proxy.host); + COPY_STRING(proxy.proxy.user); + COPY_STRING(proxy.proxy.pwd); + COPY_STRING(proxy.proxy.userpwd); + + return FALSE; +} + +//--------------------------------------------------------------------------- + + +BEGIN_PROPERTY(Curl_User) + + if (READ_PROPERTY) + GB.ReturnString(THIS->user.user); + else + { + if (CURL_check_active(THIS)) + return; + + GB.StoreString(PROP(GB_STRING), &(THIS->user.user)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Curl_Async) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->async); + else + { + if (CURL_check_active(THIS)) + return; + + THIS->async = VPROP(GB_BOOLEAN); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Curl_Timeout) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->timeout); + else + { + int timeout; + + if (CURL_check_active(THIS)) + return; + + timeout = VPROP(GB_INTEGER); + if (timeout < 0) + timeout = 0; + + THIS->timeout = timeout; + } + +END_PROPERTY + + +BEGIN_PROPERTY(Curl_BufferSize) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->buffer_size); + else + { + int buffer_size; + + if (CURL_check_active(THIS)) + return; + + buffer_size = VPROP(GB_INTEGER); + if (buffer_size <= 0) + buffer_size = 0; + else if (buffer_size < 1024) + buffer_size = 1024; + else if (buffer_size > CURL_MAX_READ_SIZE) + buffer_size = CURL_MAX_READ_SIZE; + + THIS->buffer_size = buffer_size; + } + +END_PROPERTY + + +BEGIN_PROPERTY(Curl_Password) + + if (READ_PROPERTY) + GB.ReturnString(THIS->user.pwd); + else + { + if (CURL_check_active(THIS)) + return; + + GB.StoreString(PROP(GB_STRING), &(THIS->user.pwd)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Curl_Status) + + GB.ReturnInteger(THIS_STATUS); + +END_PROPERTY + + +BEGIN_PROPERTY(Curl_ErrorText) + + if (THIS_STATUS >= 0) + GB.ReturnVoidString(); + else + GB.ReturnConstZeroString(curl_easy_strerror((-THIS_STATUS) - 1000)); + +END_PROPERTY + + +BEGIN_PROPERTY(Curl_URL) + + if (READ_PROPERTY) + { + GB.ReturnString(THIS_URL); + return; + } + + if (CURL_check_active(THIS)) + return; + + CURL_set_url(THIS, PSTRING(), PLENGTH()); + +END_PROPERTY + +BEGIN_METHOD_VOID(Curl_new) + + #if DEBUG + fprintf(stderr, "Curl_new: %p\n", THIS); + #endif + + CURL_user_init(&THIS->user); + CURL_proxy_init(&THIS->proxy.proxy); + + THIS->ssl_verify_peer = TRUE; + THIS->ssl_verify_host = TRUE; + + THIS->proxy.parent_status = (int*)&THIS_STATUS; + +END_METHOD + +BEGIN_METHOD_VOID(Curl_free) + + #if DEBUG + fprintf(stderr, "Curl_free: %p\n", THIS); + #endif + + CURL_stop(THIS); + + GB.FreeString(&THIS_URL); + GB.FreeString(&THIS->target); + + CURL_user_clear(&THIS->user); + CURL_proxy_clear(&THIS->proxy.proxy); + +END_METHOD + +BEGIN_METHOD_VOID(Curl_init) + + #if DEBUG + fprintf(stderr, "-- curl_multi_init()\n"); + #endif + CCURL_multicurl = curl_multi_init(); + +END_METHOD + +BEGIN_METHOD_VOID(Curl_exit) + + CCURL *curl, *next; + + #if DEBUG + fprintf(stderr, "-- CURL_exit: clear async list\n"); + #endif + + curl = _async_list; + while (curl) + { + next = curl->list.next; + remove_from_async_list(curl); + curl = next; + } + + #if DEBUG + fprintf(stderr, "-- curl_multi_cleanup()\n"); + #endif + + curl_multi_cleanup(CCURL_multicurl); + +END_METHOD + +BEGIN_METHOD_VOID(Curl_Peek) + + GB.ReturnString(THIS->data); + +END_METHOD + +BEGIN_PROPERTY(Curl_Debug) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->debug); + else + THIS->debug = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(Curl_Downloaded) + + GB.ReturnLong(THIS->dlnow); + +END_PROPERTY + +BEGIN_PROPERTY(Curl_Uploaded) + + GB.ReturnLong(THIS->ulnow); + +END_PROPERTY + +BEGIN_PROPERTY(Curl_TotalDownloaded) + + GB.ReturnLong(THIS->dltotal); + +END_PROPERTY + +BEGIN_PROPERTY(Curl_TotalUploaded) + + GB.ReturnLong(THIS->ultotal); + +END_PROPERTY + +BEGIN_PROPERTY(Curl_TargetFile) + + if (READ_PROPERTY) + GB.ReturnString(THIS->target); + else + GB.StoreString(PROP(GB_STRING), &THIS->target); + +END_PROPERTY + + +//--------------------------------------------------------------------------- + +BEGIN_PROPERTY(Curl_SSL_VerifyPeer) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->ssl_verify_peer); + else + { + THIS->ssl_verify_peer = VPROP(GB_BOOLEAN); + curl_easy_setopt(THIS_CURL, CURLOPT_SSL_VERIFYPEER, THIS->ssl_verify_peer ? 1 : 0); + } + +END_PROPERTY + +BEGIN_PROPERTY(Curl_SSL_VerifyHost) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->ssl_verify_host); + else + { + THIS->ssl_verify_host = VPROP(GB_BOOLEAN); + curl_easy_setopt(THIS_CURL, CURLOPT_SSL_VERIFYHOST , THIS->ssl_verify_host ? 2 : 0); + } + +END_PROPERTY + +//--------------------------------------------------------------------------- + +GB_DESC CurlSSLDesc[] = +{ + GB_DECLARE_VIRTUAL(".Curl.SSL"), + + GB_PROPERTY("VerifyPeer", "b", Curl_SSL_VerifyPeer), + GB_PROPERTY("VerifyHost", "b", Curl_SSL_VerifyHost), + + GB_END_DECLARE +}; + +GB_DESC CurlDesc[] = +{ + GB_DECLARE("Curl", sizeof(CCURL)), GB_NOT_CREATABLE(), + + GB_INHERITS("Stream"), + + GB_STATIC_METHOD("_init", NULL, Curl_init, NULL), + GB_STATIC_METHOD("_exit", NULL, Curl_exit, NULL), + + GB_METHOD("_new", NULL, Curl_new, NULL), + GB_METHOD("_free", NULL, Curl_free, NULL), + + GB_METHOD("Peek","s", Curl_Peek, NULL), + + GB_PROPERTY("URL", "s", Curl_URL), + GB_PROPERTY("User", "s", Curl_User), + GB_PROPERTY("Password", "s", Curl_Password), + GB_PROPERTY("Async", "b", Curl_Async), + GB_PROPERTY("Timeout", "i", Curl_Timeout), + GB_PROPERTY_SELF("Proxy", ".Curl.Proxy"), + GB_PROPERTY_SELF("SSL", ".Curl.SSL"), + GB_PROPERTY_READ("Status", "i", Curl_Status), + GB_PROPERTY_READ("ErrorText", "s", Curl_ErrorText), + GB_PROPERTY("Debug", "b", Curl_Debug), + GB_PROPERTY("BufferSize", "i", Curl_BufferSize), + GB_PROPERTY("TargetFile", "s", Curl_TargetFile), + + GB_PROPERTY_READ("Downloaded", "l", Curl_Downloaded), + GB_PROPERTY_READ("Uploaded", "l", Curl_Uploaded), + GB_PROPERTY_READ("TotalDownloaded", "l", Curl_TotalDownloaded), + GB_PROPERTY_READ("TotalUploaded", "l", Curl_TotalUploaded), + + GB_EVENT("Finished", NULL, NULL, &EVENT_Finished), + GB_EVENT("Connect", NULL, NULL, &EVENT_Connect), + GB_EVENT("Read", NULL, NULL, &EVENT_Read), + GB_EVENT("Error", NULL, NULL, &EVENT_Error), + GB_EVENT("Progress", NULL, NULL, &EVENT_Progress), + GB_EVENT("Cancel", NULL, NULL, &EVENT_Cancel), + + GB_END_DECLARE +}; + diff --git a/gb.net.curl/src/CCurl.h b/gb.net.curl/src/CCurl.h new file mode 100644 index 00000000..ef2088a2 --- /dev/null +++ b/gb.net.curl/src/CCurl.h @@ -0,0 +1,128 @@ +/*************************************************************************** + + CCurl.h + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCURL_H +#define __CCURL_H + +#include "gambas.h" +#include "gb_common.h" +#include "gbcurl.h" +#include "CNet.h" +#include "CProxy.h" +#include +#include + +#ifndef CURL_MAX_READ_SIZE +#define CURL_MAX_READ_SIZE 524288 +#endif + +//#define DEBUG 1 + +#ifndef __CCURL_C + +extern GB_DESC CurlDesc[]; +extern GB_DESC CurlSSLDesc[]; +extern GB_STREAM_DESC CurlStream; + +#endif + +#define THIS ((CCURL *)_object) +#define THIS_STATUS THIS->status +#define THIS_CURL THIS->curl +#define THIS_URL THIS->url +#define THIS_FILE THIS->file + +typedef + struct { + int *parent_status; + CURL_PROXY proxy; + } + CPROXY; + +typedef + void (*CURL_FIX_PROGRESS_CB)(void *, double *, double *, double *, double *); + +typedef + struct { + GB_BASE ob; + GB_STREAM stream; + GB_LIST list; // List of async curl objects + int status; + CURL *curl; + char *url; + FILE *file; + CPROXY proxy; + CURL_USER user; + int timeout; + int buffer_size; + int method; // 0->Get, 1->Put + char *data; + char *target; + int64_t dltotal; + int64_t dlnow; + int64_t ultotal; + int64_t ulnow; + CURL_FIX_PROGRESS_CB progresscb; + unsigned async : 1; + unsigned in_list : 1; + unsigned debug : 1; + unsigned ssl_verify_peer : 1; + unsigned ssl_verify_host : 1; + } + CCURL; + +#define STREAM_TO_OBJECT(_stream) (_stream->tag) + +void CCURL_stream_init(GB_STREAM *stream,int fd); +int CCURL_stream_read(GB_STREAM *stream, char *buffer, int len); +int CCURL_stream_write(GB_STREAM *stream, char *buffer, int len); +int CCURL_stream_eof(GB_STREAM *stream); +int CCURL_stream_lof(GB_STREAM *stream, int64_t *len); +int CCURL_stream_open(GB_STREAM *stream, const char *path, int mode, void *data); +int CCURL_stream_seek(GB_STREAM *stream, int64_t pos, int whence); +int CCURL_stream_tell(GB_STREAM *stream, int64_t *pos); +int CCURL_stream_flush(GB_STREAM *stream); +int CCURL_stream_close(GB_STREAM *stream); +int CCURL_stream_handle(GB_STREAM *stream); + +void CURL_raise_finished(void *_object); +void CURL_raise_error(void *_object); +void CURL_raise_cancel(void *_object); +void CURL_raise_connect(void *_object); +void CURL_raise_read(void *_object); + +void CURL_start_post(void *_object); +void CURL_stop(void *_object); + +void CURL_manage_error(void *_object, int error); + +void CURL_init_stream(void *_object); +void CURL_init_options(void *_object); + +bool CURL_check_active(void *_object); + +void CURL_set_progress(void *_object, bool progress, CURL_FIX_PROGRESS_CB cb); + +bool CURL_copy_from(CCURL *dest, CCURL *src); + +#endif diff --git a/gb.net.curl/src/CFtpClient.c b/gb.net.curl/src/CFtpClient.c new file mode 100644 index 00000000..74307736 --- /dev/null +++ b/gb.net.curl/src/CFtpClient.c @@ -0,0 +1,333 @@ +/*************************************************************************** + + CFtpClient.c + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CFTPCLIENT_C + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "main.h" +#include "gambas.h" +#include "CCurl.h" +#include "CFtpClient.h" +#include "CProxy.h" + +#define EXEC_GET 0 +#define EXEC_PUT 1 +#define EXEC_CMD 2 + + +static int ftp_read_curl(void *buffer, size_t size, size_t nmemb, void *_object) +{ + FILE *file = THIS_FILE; + THIS_STATUS = NET_RECEIVING_DATA; + + if (!feof(file)) + nmemb = fread(buffer, size, nmemb, file); + else + nmemb = 0; + + return nmemb; +} + + +static int ftp_write_curl(void *buffer, size_t size, size_t nmemb, void *_object) +{ + THIS_STATUS = NET_RECEIVING_DATA; + nmemb *= size; + + if (THIS_FILE) + { + return fwrite(buffer,size,nmemb,THIS_FILE); + } + else + { + THIS->data = GB.AddString(THIS->data, buffer, nmemb); + } + + if (THIS->async) + { + GB.Ref(THIS); + GB.Post(CURL_raise_read, (intptr_t)THIS); + } + + return nmemb; +} + +static void ftp_reset(void *_object) +{ + GB.FreeString(&THIS->data); + GB.Unref(&THIS_FTP->commands); +} + + +static void ftp_initialize_curl_handle(void *_object) +{ + if (THIS_CURL) + { + if (CURL_check_userpwd(&THIS->user)) + { + CURL_stop(_object); + ftp_reset(_object); + THIS_CURL = curl_easy_init(); + #if DEBUG + fprintf(stderr, "-- [%p] curl_easy_init() -> %p\n", THIS, THIS_CURL); + #endif + } + } + else + { + THIS_CURL = curl_easy_init(); + #if DEBUG + fprintf(stderr, "-- [%p] curl_easy_init() -> %p\n", THIS, THIS_CURL); + #endif + } + + CURL_init_options(THIS); + + curl_easy_setopt(THIS_CURL, CURLOPT_FTP_USE_EPSV, (long)(THIS_FTP->no_epsv ? 0 : 1)); + + ftp_reset(THIS_FTP); + THIS_STATUS = NET_CONNECTING; + + CURL_init_stream(THIS); +} + + +static int ftp_exec(void *_object, int what, GB_ARRAY commands) +{ + struct stat info; + struct curl_slist *list; + int i; + + if (THIS_STATUS > 0) + return 1; + + THIS->method = what == EXEC_PUT ? 1 : 0; + + ftp_initialize_curl_handle(THIS); + + switch(what) + { + case EXEC_GET: + + curl_easy_setopt(THIS_CURL, CURLOPT_WRITEFUNCTION , (curl_write_callback)ftp_write_curl); + curl_easy_setopt(THIS_CURL, CURLOPT_WRITEDATA , _object); + curl_easy_setopt(THIS_CURL, CURLOPT_UPLOAD , 0); + + CURL_set_progress(THIS, TRUE, NULL); + + break; + + case EXEC_PUT: + + if (THIS_FILE && fstat(fileno(THIS_FILE), &info) == 0) + curl_easy_setopt(THIS_CURL, CURLOPT_INFILESIZE_LARGE, (curl_off_t)info.st_size); + + curl_easy_setopt(THIS_CURL, CURLOPT_READFUNCTION , (curl_read_callback)ftp_read_curl); + curl_easy_setopt(THIS_CURL, CURLOPT_READDATA , _object); + curl_easy_setopt(THIS_CURL, CURLOPT_UPLOAD , 1); + + CURL_set_progress(THIS, TRUE, NULL); + + break; + + case EXEC_CMD: + + curl_easy_setopt(THIS_CURL, CURLOPT_NOBODY, 1); + + if (commands) + { + char *cmd; + + GB.Unref(&THIS_FTP->commands); + THIS_FTP->commands = commands; + GB.Ref(commands); + + list = NULL; + for (i = 0; i < GB.Array.Count(commands); i++) { + cmd = *((char **) GB.Array.Get(commands, i)); + if (!cmd) + continue; + list = curl_slist_append(list, cmd); + } + if (list) + curl_easy_setopt(THIS_CURL, CURLOPT_QUOTE, list); + } + + break; + } + + if (THIS->async) + { + #if DEBUG + fprintf(stderr, "-- [%p] curl_multi_add_handle(%p)\n", THIS, THIS_CURL); + #endif + CURL_start_post(THIS); + return 0; + } + + CURL_manage_error(_object, curl_easy_perform(THIS_CURL)); + return 0; +} + + +BEGIN_METHOD(FtpClient_Get, GB_STRING target) + + const char *target = NULL; + + if (MISSING(target)) + target = THIS->target; + else + target = GB.FileName(STRING(target), LENGTH(target)); + + if (target && *target) + { + /*if (THIS_STATUS > 0) + { + GB.Error("Still active"); + return; + }*/ + + THIS_FILE = fopen(target, "w"); + + if (!THIS_FILE) + { + GB.Error("Unable to open file for writing"); + return; + } + } + + if (ftp_exec(THIS, EXEC_GET, NULL)) + GB.Error("Still active"); + +END_METHOD + + +BEGIN_METHOD(FtpClient_Put, GB_STRING SourceFile) + + if (THIS_STATUS > 0) + { + GB.Error("Still active"); + return; + } + + THIS_FILE = fopen(GB.FileName(STRING(SourceFile), LENGTH(SourceFile)),"r"); + if (!THIS_FILE) + { + GB.Error("Unable to open file for reading"); + return; + } + + if (ftp_exec(THIS, EXEC_PUT, NULL)) + GB.Error("Still active"); + +END_METHOD + + +BEGIN_METHOD(FtpClient_Exec, GB_OBJECT commands) + + if (THIS_STATUS > 0) + { + GB.Error("Still active"); + return; + } + + ftp_exec(THIS, EXEC_CMD, VARG(commands)); + +END_METHOD + + +BEGIN_METHOD_VOID(FtpClient_Stop) + + CURL_stop(THIS); + ftp_reset(THIS); + + GB.Ref(THIS); + CURL_raise_cancel(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(FtpClient_new) + + THIS_URL = GB.NewZeroString("ftp://127.0.0.1:21"); + + THIS->async = TRUE; + + CURL_user_set_auth(&THIS->user, CURLAUTH_BASIC); + + THIS->user.user = GB.NewZeroString("anonymous"); + +END_METHOD + + +BEGIN_METHOD_VOID(FtpClient_free) + + //CURL_stop(_object); + ftp_reset(THIS_FTP); + +END_METHOD + +BEGIN_PROPERTY(FtpClient_NoEPSV) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS_FTP->no_epsv); + else + THIS_FTP->no_epsv = VPROP(GB_BOOLEAN); + +END_PROPERTY + + +GB_DESC CFtpClientDesc[] = +{ + GB_DECLARE("FtpClient", sizeof(CFTPCLIENT)), + + GB_INHERITS("Curl"), + + GB_METHOD("_new", NULL, FtpClient_new, NULL), + GB_METHOD("_free", NULL, FtpClient_free, NULL), + + GB_METHOD("Stop", NULL, FtpClient_Stop, NULL), + GB_METHOD("Get", NULL, FtpClient_Get, "[(TargetFile)s]"), + GB_METHOD("Exec", NULL, FtpClient_Exec, "(Commands)String[]"), + GB_METHOD("Put", NULL, FtpClient_Put, "(LocalFile)s"), + + GB_PROPERTY("NoEPSV", "b", FtpClient_NoEPSV), + + GB_CONSTANT("_IsControl", "b", TRUE), + GB_CONSTANT("_IsVirtual", "b", TRUE), + GB_CONSTANT("_Group", "s", "Network"), + GB_CONSTANT("_Properties", "s", FTP_PROPERTIES), + GB_CONSTANT("_DefaultEvent", "s", "Read"), + + GB_END_DECLARE +}; + diff --git a/gb.net.curl/src/CFtpClient.h b/gb.net.curl/src/CFtpClient.h new file mode 100644 index 00000000..1d88ca72 --- /dev/null +++ b/gb.net.curl/src/CFtpClient.h @@ -0,0 +1,56 @@ +/*************************************************************************** + + CFtpClient.h + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CFTPCLIENT_H +#define __CFTPCLIENT_H + +#include "gambas.h" +#include "gbcurl.h" +#include "CCurl.h" +#include "CProxy.h" +#include +#include + +#ifndef __CFTPCLIENT_C + + +extern GB_DESC CFtpClientDesc[]; +extern GB_STREAM_DESC FtpStream; + +#else + +#define THIS_FTP ((CFTPCLIENT *)_object) + +#endif + +typedef + struct { + CCURL curl; + GB_ARRAY commands; + unsigned no_epsv : 1; + } + CFTPCLIENT; + +#define FTP_PROPERTIES "URL=127.0.0.1:21,Async=True,Timeout=0,User,Password" + +#endif diff --git a/gb.net.curl/src/CHttpClient.c b/gb.net.curl/src/CHttpClient.c new file mode 100644 index 00000000..e5f3df5d --- /dev/null +++ b/gb.net.curl/src/CHttpClient.c @@ -0,0 +1,701 @@ +/*************************************************************************** + + CHttpClient.c + + (c) 2003-2008 Daniel Campos Fernández + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CHTTPCLIENT_C + +#include +#include +#include +#include + +#include +#include +#include + +#include "main.h" +#include "gambas.h" +#include "CCurl.h" +#include "CHttpClient.h" +#include "CProxy.h" + +#define ERR_STILL_ACTIVE "Still active" +#define ERR_INVALID_CONTENT_TYPE "Invalid content type" +#define ERR_INVALID_DATA "Invalid data" + +#define SEND_POST 1 +#define SEND_PUT 2 +#define SEND_FILE 4 + +static void http_parse_header(CHTTPCLIENT *_object) +{ + char *header; + int len; + char *p; + int ret; + + if (THIS_HTTP->return_string) + return; + + if (!THIS_HTTP->headers || GB.Array.Count(THIS_HTTP->headers) == 0) + return; + + header = *(char **)GB.Array.Get(THIS_HTTP->headers, 0); + len = GB.StringLength(header); + + p = strchr(header, ' '); + if (!p) + return; + p++; + + ret = 0; + while (isdigit(*p)) + { + ret = (ret * 10) + (*p - '0'); + p++; + } + + + if (*p != ' ') + return; + + p++; + + THIS_HTTP->return_code = ret; + //GB.FreeString(&THIS_HTTP->return_string); + THIS_HTTP->return_string = GB.NewString(p, header + len - p); +} + + +static int http_header_curl(void *buffer, size_t size, size_t nmemb, void *_object) +{ + if (!THIS_HTTP->headers) + { + GB.Array.New(&THIS_HTTP->headers, GB_T_STRING, 0); + GB.Ref(THIS_HTTP->headers); + } + + if (nmemb > 2) + *(char **)GB.Array.Add(THIS_HTTP->headers) = GB.NewString(buffer, (nmemb - 2) * size); + + if ((THIS_STATUS == NET_CONNECTING) && THIS->async) + { + THIS_STATUS = NET_RECEIVING_DATA; + GB.Ref(THIS); + GB.Post(CURL_raise_connect, (intptr_t)THIS); + } + + return size * nmemb; +} + + +static size_t http_read_curl(void *ptr, size_t size, size_t nmemb, void *_object) +{ + size *= nmemb; + + if (size > (THIS_HTTP->len_data - THIS_HTTP->len_sent)) + size = THIS_HTTP->len_data - THIS_HTTP->len_sent; + + if (size > 0) + { + memcpy(ptr, THIS_HTTP->data + THIS_HTTP->len_sent, size); + THIS_HTTP->len_sent += size; + } + + return size; +} + + +static int http_write_curl(void *buffer, size_t size, size_t nmemb, void *_object) +{ + http_parse_header(THIS_HTTP); + + nmemb *= size; + + if (THIS_FILE) + { + return fwrite(buffer,size,nmemb,THIS_FILE); + } + else + { + THIS->data = GB.AddString(THIS->data, buffer, nmemb); + } + + if (THIS->async) + { + GB.Ref(THIS); + GB.Post(CURL_raise_read, (intptr_t)THIS); + } + + return nmemb; +} + + +static void http_reset(void *_object) +{ + GB.FreeString(&THIS->data); + + GB.Unref(&THIS_HTTP->headers); + THIS_HTTP->headers = NULL; + GB.Unref(&THIS_HTTP->sent_headers); + THIS_HTTP->sent_headers = NULL; + + if (THIS_HTTP->sContentType) + { + GB.Free((void**)POINTER(&THIS_HTTP->sContentType)); + THIS_HTTP->sContentType=NULL; + } + + if (THIS_HTTP->data) + { + if (THIS_HTTP->send_file) + GB.ReleaseFile(THIS_HTTP->data, THIS_HTTP->len_data); + else + GB.Free(POINTER(&THIS_HTTP->data)); + THIS_HTTP->data = NULL; + } + + THIS_HTTP->send_file = FALSE; +} + + +static void http_initialize_curl_handle(void *_object, GB_ARRAY custom_headers) +{ + if (THIS_CURL) + { + if (CURL_check_userpwd(&THIS->user)) + { + CURL_stop(_object); + http_reset(_object); + THIS_CURL = curl_easy_init(); + } + } + else + { + THIS_CURL = curl_easy_init(); + } + + CURL_init_options(THIS); + + curl_easy_setopt(THIS_CURL, CURLOPT_USERAGENT, THIS_HTTP->sUserAgent); + curl_easy_setopt(THIS_CURL, CURLOPT_ENCODING, THIS_HTTP->encoding); + curl_easy_setopt(THIS_CURL, CURLOPT_HEADERFUNCTION, (curl_write_callback)http_header_curl); + curl_easy_setopt(THIS_CURL, CURLOPT_WRITEFUNCTION, (curl_write_callback)http_write_curl); + curl_easy_setopt(THIS_CURL, CURLOPT_WRITEDATA, _object); + curl_easy_setopt(THIS_CURL, CURLOPT_WRITEHEADER, _object); + curl_easy_setopt(THIS_CURL, CURLOPT_COOKIEFILE, THIS_HTTP->cookiesfile); + curl_easy_setopt(THIS_CURL, CURLOPT_FOLLOWLOCATION, (long)THIS_HTTP->redirect); + + if (THIS_HTTP->updatecookies) + curl_easy_setopt(THIS_CURL, CURLOPT_COOKIEJAR, THIS_HTTP->cookiesfile); + else + curl_easy_setopt(THIS_CURL, CURLOPT_COOKIEJAR, NULL); + + THIS_HTTP->return_code = 0; + GB.FreeString(&THIS_HTTP->return_string); + + http_reset(_object); + THIS_STATUS = NET_CONNECTING; + + if (custom_headers) + { + GB.Unref(&THIS_HTTP->sent_headers); + THIS_HTTP->sent_headers = custom_headers; + GB.Ref(custom_headers); + } + + CURL_init_stream(THIS); +} + + +static bool check_request(void *_object, char *contentType, char *data, int len) +{ + int i; + unsigned char c; + + if (THIS_STATUS > 0) + { + GB.Error(ERR_STILL_ACTIVE); + return TRUE; + } + + if (!contentType) + { + GB.Error(ERR_INVALID_CONTENT_TYPE); + return TRUE; + } + + for (i = 0; i < strlen(contentType); i++) + { + c = contentType[i]; + if (isalnum(c) || c == '-' || c == '+' || c == '.' || c == '/' || c == ';' || c == ' ' || c == '=') + continue; + GB.Error(ERR_INVALID_CONTENT_TYPE); + return TRUE; + } + + /*if (!data || !len) + { + GB.Error(ERR_INVALID_DATA); + return TRUE; + };*/ + + return FALSE; +} + +static void http_fix_progress_cb(void *_object, double *dltotal, double *dlnow, double *ultotal, double *ulnow) +{ + *ultotal = THIS_HTTP->len_data; + *ulnow = THIS_HTTP->len_sent; +} + +static void http_get(void *_object, GB_ARRAY custom_headers, char *target, CURLoption method) +{ + struct curl_slist *headers = NULL; + int i; + + if (THIS_STATUS > 0) + { + GB.Error(ERR_STILL_ACTIVE); + return; + } + + if (!target) + target = THIS->target; + + if (target && *target) + { + target = GB.FileName(target, 0); + THIS_FILE = fopen(target, "w"); + if (!THIS_FILE) + { + GB.Error("Unable to open file for writing: &1", target); + return; + } + } + + THIS->method = 0; + + http_initialize_curl_handle(_object, custom_headers); + + curl_easy_setopt(THIS_CURL, method, 1); + + if (THIS_HTTP->sent_headers) + { + for(i = 0; i < GB.Array.Count(THIS_HTTP->sent_headers); i++) + headers = curl_slist_append(headers, *(char **)GB.Array.Get(THIS_HTTP->sent_headers, i)); + } + + curl_easy_setopt(THIS_CURL, CURLOPT_HTTPHEADER, headers); + CURL_set_progress(THIS, TRUE, NULL); + + if (THIS->async) + { + CURL_start_post(THIS); + return; + } + + CURL_manage_error(_object, curl_easy_perform(THIS_CURL)); +} + + +static void http_send(void *_object, int type, char *sContent, char *sData, int lendata, GB_ARRAY custom_headers, char *target) +{ + int mylen; + struct curl_slist *headers = NULL; + int i; + + if (check_request(_object, sContent, sData, lendata)) + return; + + if (!target) + target = THIS->target; + + if (target && *target) + { + target = GB.FileName(target, 0); + THIS_FILE = fopen(target, "w"); + if (!THIS_FILE) + { + GB.Error("Unable to open file for writing: &1", target); + return; + } + } + + http_initialize_curl_handle(_object, custom_headers); + + if (type & SEND_FILE) + { + if (GB.LoadFile(sData, lendata, &THIS_HTTP->data, &mylen)) + return; + + THIS_HTTP->len_data = mylen; + THIS_HTTP->send_file = TRUE; + } + else + { + THIS_HTTP->send_file = FALSE; + THIS_HTTP->len_data = lendata; + + if (lendata) + { + GB.Alloc((void*)&THIS_HTTP->data, lendata + 1); + strncpy(THIS_HTTP->data, sData, lendata); + } + else + THIS_HTTP->data = NULL; + + } + + THIS_HTTP->len_sent = 0; + + mylen = strlen(sContent) + strlen("Content-Type: ") + 1; + GB.Alloc((void*)&THIS_HTTP->sContentType, mylen); + + THIS_HTTP->sContentType[0] = 0; + strcpy(THIS_HTTP->sContentType, "Content-Type: " ); + strcat(THIS_HTTP->sContentType, sContent); + + THIS->method = 1; + headers = curl_slist_append(headers, THIS_HTTP->sContentType); + + if (THIS_HTTP->sent_headers) + { + for (i = 0; i < GB.Array.Count(THIS_HTTP->sent_headers); i++) + headers = curl_slist_append(headers, *(char **)GB.Array.Get(THIS_HTTP->sent_headers, i)); + } + + curl_easy_setopt(THIS_CURL, CURLOPT_HTTPHEADER, headers); + + if (type == SEND_PUT) + { + curl_easy_setopt(THIS_CURL, CURLOPT_INFILESIZE_LARGE, (curl_off_t)lendata); + curl_easy_setopt(THIS_CURL, CURLOPT_UPLOAD, 1); + } + else // SEND_POST + { + curl_easy_setopt(THIS_CURL, CURLOPT_POSTFIELDSIZE, lendata); + curl_easy_setopt(THIS_CURL, CURLOPT_POSTFIELDS, NULL); + } + + curl_easy_setopt(THIS_CURL, CURLOPT_READFUNCTION, http_read_curl); + curl_easy_setopt(THIS_CURL, CURLOPT_READDATA, _object); + + CURL_set_progress(THIS, TRUE, http_fix_progress_cb); + + if (THIS->async) + { + CURL_start_post(THIS); + return; + } + + CURL_manage_error(THIS, curl_easy_perform(THIS_CURL)); +} + + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(HttpClient_UpdateCookies) + + if (READ_PROPERTY) + { + GB.ReturnBoolean(THIS_HTTP->updatecookies); + return; + } + + if (THIS_STATUS > 0) + { + GB.Error ("UpdateCookies property can not be changed if the client is active"); + return; + } + + if (VPROP(GB_BOOLEAN)) + THIS_HTTP->updatecookies=1; + else + THIS_HTTP->updatecookies=0; + +END_PROPERTY + + +BEGIN_PROPERTY(HttpClient_CookiesFile) + + const char *file; + + if (READ_PROPERTY) + { + GB.ReturnString(THIS_HTTP->cookiesfile); + return; + } + + if (CURL_check_active(THIS)) + return; + + if (THIS_HTTP->cookiesfile) + GB.FreeString(&THIS_HTTP->cookiesfile); + + file = GB.FileName(PSTRING(), PLENGTH()); + + if (file) + THIS_HTTP->cookiesfile = GB.NewZeroString(file); + +END_PROPERTY + + +BEGIN_PROPERTY(HttpClient_Auth) + + if (READ_PROPERTY) + { + GB.ReturnInteger(THIS_HTTP->auth); + return; + } + + if (CURL_check_active(THIS)) + return; + + if (CURL_user_set_auth(&THIS->user, VPROP(GB_INTEGER))) + GB.Error ("Unknown authentication method"); + else + THIS_HTTP->auth = VPROP(GB_INTEGER); + + +END_PROPERTY + + +BEGIN_PROPERTY(HttpClient_UserAgent) + + if (READ_PROPERTY) + GB.ReturnString(THIS_HTTP->sUserAgent); + else + { + if (CURL_check_active(THIS)) + return; + + GB.StoreString(PROP(GB_STRING), &THIS_HTTP->sUserAgent); + } + +END_PROPERTY + + +BEGIN_PROPERTY(HttpClient_Encoding) + + if (READ_PROPERTY) + GB.ReturnString(THIS_HTTP->encoding); + else + { + if (CURL_check_active(THIS)) + return; + + GB.StoreString(PROP(GB_STRING), &THIS_HTTP->encoding); + } + +END_PROPERTY + + +BEGIN_PROPERTY(HttpClient_ReturnCode) + + http_parse_header(THIS_HTTP); + GB.ReturnInteger(THIS_HTTP->return_code); + +END_PROPERTY + + +BEGIN_PROPERTY(HttpClient_ReturnString) + + http_parse_header(THIS_HTTP); + GB.ReturnString(THIS_HTTP->return_string); + +END_PROPERTY + + +BEGIN_PROPERTY(HttpClient_Headers) + + http_parse_header(THIS_HTTP); + GB.ReturnObject(THIS_HTTP->headers); + +END_PROPERTY + + +BEGIN_METHOD_VOID(HttpClient_new) + + THIS_URL = GB.NewZeroString("http://127.0.0.1:80"); + THIS_HTTP->sUserAgent = GB.NewZeroString("Gambas/" GAMBAS_FULL_VERSION_STRING " (gb.net.curl; " SYSTEM ")"); + THIS->async = TRUE; + +END_METHOD + + +BEGIN_METHOD_VOID(HttpClient_free) + + //CURL_stop(_object); + http_reset(THIS); + + GB.FreeString(&THIS_HTTP->sUserAgent); + GB.FreeString(&THIS_HTTP->encoding); + GB.FreeString(&THIS_HTTP->cookiesfile); + GB.FreeString(&THIS_HTTP->return_string); + +END_METHOD + + +BEGIN_METHOD(HttpClient_Get, GB_OBJECT headers; GB_STRING target) + + http_get(THIS, VARGOPT(headers, 0), MISSING(target) ? NULL : GB.ToZeroString(ARG(target)), CURLOPT_HTTPGET); + +END_METHOD + + +BEGIN_METHOD(HttpClient_Head, GB_OBJECT headers) + + http_get(THIS, VARGOPT(headers, 0), NULL, CURLOPT_NOBODY); + +END_METHOD + + +BEGIN_METHOD(HttpClient_Post, GB_STRING contentType; GB_STRING data; GB_OBJECT headers; GB_STRING target) + + http_send(THIS, SEND_POST, GB.ToZeroString(ARG(contentType)), STRING(data), LENGTH(data), VARGOPT(headers, NULL), MISSING(target) ? NULL : GB.ToZeroString(ARG(target))); + +END_METHOD + + +BEGIN_METHOD(HttpClient_PostFile, GB_STRING contentType; GB_STRING file; GB_OBJECT headers; GB_STRING target) + + http_send(THIS, SEND_POST | SEND_FILE, GB.ToZeroString(ARG(contentType)), STRING(file), LENGTH(file), VARGOPT(headers, NULL), MISSING(target) ? NULL : GB.ToZeroString(ARG(target))); + +END_METHOD + + +BEGIN_METHOD(HttpClient_Put, GB_STRING contentType; GB_STRING data; GB_OBJECT headers; GB_STRING target) + + http_send(THIS, SEND_PUT, GB.ToZeroString(ARG(contentType)), STRING(data), LENGTH(data), VARGOPT(headers, NULL), MISSING(target) ? NULL : GB.ToZeroString(ARG(target))); + +END_METHOD + + +BEGIN_METHOD(HttpClient_PutFile, GB_STRING contentType; GB_STRING file; GB_OBJECT headers; GB_STRING target) + + http_send(THIS, SEND_PUT | SEND_FILE, GB.ToZeroString(ARG(contentType)), STRING(file), LENGTH(file), VARGOPT(headers, NULL), MISSING(target) ? NULL : GB.ToZeroString(ARG(target))); + +END_METHOD + + +BEGIN_METHOD_VOID(HttpClient_Stop) + + CURL_stop(THIS); + http_reset(_object); + + GB.Ref(THIS); + CURL_raise_cancel(THIS); + +END_METHOD + +#define COPY_STRING(_field) \ +{ \ + GB.FreeString(&THIS_HTTP->_field); \ + THIS_HTTP->_field = from->_field; \ + if (THIS_HTTP->_field) THIS_HTTP->_field = GB.NewString(THIS_HTTP->_field, GB.StringLength(THIS_HTTP->_field)); \ +} + +BEGIN_METHOD(HttpClient_CopyFrom, GB_OBJECT from) + + CHTTPCLIENT *from = (CHTTPCLIENT *)VARG(from); + + if (GB.CheckObject(from)) + return; + + if (CURL_copy_from((CCURL *)THIS, (CCURL *)from)) + return; + + THIS_HTTP->updatecookies = from->updatecookies; + THIS_HTTP->auth = from->auth; + COPY_STRING(sUserAgent); + COPY_STRING(encoding); + COPY_STRING(cookiesfile); + +END_METHOD + + +BEGIN_METHOD(HttpClient_Download, GB_STRING url; GB_OBJECT headers) + + _object = GB.New(GB.FindClass("HttpClient"), NULL, NULL); + + GB.Ref(THIS); + THIS->async = FALSE; + if (CURL_set_url(THIS, STRING(url), LENGTH(url))) + return; + + http_get(THIS, VARGOPT(headers, NULL), NULL, CURLOPT_HTTPGET); + GB.ReturnString(THIS->data); + GB.Unref(POINTER(&_object)); + +END_METHOD + + +BEGIN_PROPERTY(HttpClient_Redirect) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS_HTTP->redirect); + else + THIS_HTTP->redirect = VPROP(GB_BOOLEAN); + +END_PROPERTY + +GB_DESC HttpClientDesc[] = +{ + GB_DECLARE("HttpClient", sizeof(CHTTPCLIENT)), + + GB_INHERITS("Curl"), + + GB_METHOD("_new", NULL, HttpClient_new, NULL), + GB_METHOD("_free", NULL, HttpClient_free, NULL), + GB_METHOD("Stop", NULL, HttpClient_Stop, NULL), + GB_METHOD("Get", NULL, HttpClient_Get, "[(Headers)String[];(TargetFile)s]"), + GB_METHOD("Head", NULL, HttpClient_Head, "[(Headers)String[]]"), + GB_METHOD("Post", NULL, HttpClient_Post, "(ContentType)s(Data)s[(Headers)String[];(TargetFile)s]"), + GB_METHOD("Put", NULL, HttpClient_Put, "(ContentType)s(Data)s[(Headers)String[];(TargetFile)s]"), + GB_METHOD("PostFile", NULL, HttpClient_PostFile, "(ContentType)s(Path)s[(Headers)String[];(TargetFile)s]"), + GB_METHOD("PutFile", NULL, HttpClient_PutFile, "(ContentType)s(Path)s[(Headers)String[];(TargetFile)s]"), + + GB_PROPERTY("Auth", "i", HttpClient_Auth), + GB_PROPERTY("CookiesFile", "s",HttpClient_CookiesFile), + GB_PROPERTY("UpdateCookies", "b",HttpClient_UpdateCookies), + GB_PROPERTY_READ("Headers", "String[]", HttpClient_Headers), + GB_PROPERTY("UserAgent", "s", HttpClient_UserAgent), + GB_PROPERTY("Encoding", "s", HttpClient_Encoding), + GB_PROPERTY("Redirect", "b", HttpClient_Redirect), + + GB_PROPERTY_READ("Code", "i", HttpClient_ReturnCode), + GB_PROPERTY_READ("Reason", "s", HttpClient_ReturnString), + + GB_METHOD("CopyFrom", NULL, HttpClient_CopyFrom, "(HttpClient)Source"), + + GB_STATIC_METHOD("Download", "s", HttpClient_Download, "(URL)s[(Headers)String[];]"), + + GB_CONSTANT("_IsControl", "b", TRUE), + GB_CONSTANT("_IsVirtual", "b", TRUE), + GB_CONSTANT("_Group", "s", "Network"), + GB_CONSTANT("_Properties", "s", HTTP_PROPERTIES), + GB_CONSTANT("_DefaultEvent", "s", "Read"), + + GB_END_DECLARE +}; diff --git a/gb.net.curl/src/CHttpClient.h b/gb.net.curl/src/CHttpClient.h new file mode 100644 index 00000000..596803a8 --- /dev/null +++ b/gb.net.curl/src/CHttpClient.h @@ -0,0 +1,77 @@ +/*************************************************************************** + + CHttpClient.h + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CHTTPCLIENT_H +#define __CHTTPCLIENT_H + +#include "gambas.h" +#include "gbcurl.h" +#include "CCurl.h" +#include "CProxy.h" +#include +#include + +#ifndef __CHTTPCLIENT_C + + +extern GB_DESC HttpClientDesc[]; +extern GB_STREAM_DESC HttpStream; + +#else + +#define THIS_HTTP ((CHTTPCLIENT *)_object) + +#endif + +typedef + struct { + CCURL curl; + int auth; + char *cookiesfile; + int updatecookies; + char *sContentType; + char *sUserAgent; + char *encoding; + GB_ARRAY headers; + GB_ARRAY sent_headers; + int return_code; + char *return_string; + char *data; + size_t len_data; + size_t len_sent; + unsigned send_file : 1; + unsigned redirect : 1; + } + CHTTPCLIENT; + + +/*int http_find_info (CURL *curlfind); +int http_header_curl(void *buffer, size_t size, size_t nmemb, void *c_handle); +int http_write_curl(void *buffer, size_t size, size_t nmemb, void *c_handle); +void http_parse_header(CHTTPCLIENT *mythis); +void http_reset(void *_object); +void http_stop(void *_object);*/ + +#define HTTP_PROPERTIES "URL=127.0.0.1:80,User,Password,Auth=0,Async=True,Timeout=0,CookiesFile,UpdateCookies=FALSE,UserAgent=Gambas (gb.net.curl) HTTP/1.0" + +#endif diff --git a/gb.net.curl/src/CNet.c b/gb.net.curl/src/CNet.c new file mode 100644 index 00000000..c248b1aa --- /dev/null +++ b/gb.net.curl/src/CNet.c @@ -0,0 +1,201 @@ +/*************************************************************************** + + CNet.c + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CNET_C +#include "main.h" +#include +#include + +#include "CNet.h" + +#define GBCURL(x) (-(1000+x)) + +// Constants not defined in old versions of libcurl + +#ifndef CURLE_FTP_PRET_FAILED +#define CURLE_FTP_PRET_FAILED 84 +#endif +#ifndef CURLE_FTP_BAD_FILE_LIST +#define CURLE_FTP_BAD_FILE_LIST 87 +#endif +#ifndef CURLE_SSL_CRL_BADFILE +#define CURLE_SSL_CRL_BADFILE 82 /* was on 7.19.0 */ +#endif +#ifndef CURLAUTH_DIGEST_IE +#define CURLAUTH_DIGEST_IE (1<<4) /* Digest with IE flavour */ +#endif +#ifndef CURLE_SSL_ISSUER_ERROR +#define CURLE_SSL_ISSUER_ERROR 83 +#endif +#ifndef CURLE_CHUNK_FAILED +#define CURLE_CHUNK_FAILED 88 +#endif + +#ifndef CURLAUTH_NONE +#define CURLAUTH_NONE ((unsigned long)0) /* nothing */ +#define CURLAUTH_BASIC (((unsigned long)1)<<0) /* Basic (default) */ +#define CURLAUTH_DIGEST (((unsigned long)1)<<1) /* Digest */ +#define CURLAUTH_GSSNEGOTIATE (((unsigned long)1)<<2) /* GSS-Negotiate */ +#define CURLAUTH_NTLM (((unsigned long)1)<<3) /* NTLM */ +#define CURLAUTH_ANY ~0 /* all types set */ +#define CURLAUTH_ANYSAFE (~CURLAUTH_BASIC) +#endif + +#ifndef CURLAUTH_DIGEST_IE +#define CURLAUTH_DIGEST_IE (((unsigned long)1)<<4) +#undef CURLAUTH_ANY +#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE) +#undef CURLAUTH_ANYSAGE +#define CURLAUTH_ANYSAFE (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE)) +#endif + +#ifndef CURLAUTH_NTLM_WB +#define CURLAUTH_NTLM_WB (((unsigned long)1)<<5) +#endif + +#if LIBCURL_VERSION_NUM < 0x071202 +#define CURLE_AGAIN 81 +#endif + +#if LIBCURL_VERSION_NUM < 0x071300 +#define CURLE_SSL_CRL_BADFILE 82 +#define CURLE_SSL_ISSUER_ERROR 83 +#define CURLE_FTP_PRET_FAILED 84 +#define CURLE_RTSP_CSEQ_ERROR 85 +#define CURLE_RTSP_SESSION_ERROR 86 +#define CURLE_FTP_BAD_FILE_LIST 87 +#define CURLE_CHUNK_FAILED 88 +#endif + +#if LIBCURL_VERSION_NUM < 0x071505 +#define CURLE_NOT_BUILT_IN 4 +#endif + +#if LIBCURL_VERSION_NUM < 0x071800 +#define CURLE_FTP_ACCEPT_FAILED 10 +#define CURLE_FTP_ACCEPT_TIMEOUT 12 +#endif + +GB_DESC CNetDesc[] = +{ + GB_DECLARE("Net", 0), GB_VIRTUAL_CLASS(), + + // Net states used by curl + //GB_CONSTANT("Inactive", "i", 0), + //GB_CONSTANT("ReceivingData","i",4), + //GB_CONSTANT("Connecting", "i", 6), + + GB_CONSTANT ("Synchronous", "i", 0), + GB_CONSTANT ("Asynchronous", "i", 1), + /* net-curl proxies */ + GB_CONSTANT ("ProxyHTTP", "i", CURLPROXY_HTTP), + GB_CONSTANT ("ProxySocks5", "i", CURLPROXY_SOCKS5), + /* net-curl autohorization */ + GB_CONSTANT("AuthNone", "i", CURLAUTH_NONE), + GB_CONSTANT("AuthBasic", "i", CURLAUTH_BASIC), + GB_CONSTANT("AuthNtlm", "i", CURLAUTH_NTLM), + GB_CONSTANT("AuthDigest", "i", CURLAUTH_DIGEST), + GB_CONSTANT("AuthDigestIE", "i", CURLAUTH_DIGEST_IE), + GB_CONSTANT("AuthNtlmWb", "i", CURLAUTH_NTLM_WB), + GB_CONSTANT("AuthGssNegotiate", "i", CURLAUTH_GSSNEGOTIATE), + GB_CONSTANT("AuthAny", "i", CURLAUTH_ANY), + GB_CONSTANT("AuthAnySafe", "i", CURLAUTH_ANYSAFE), + + GB_CONSTANT("UnsupportedProtocol", "i", GBCURL(CURLE_UNSUPPORTED_PROTOCOL)), + GB_CONSTANT("FailedInit", "i", GBCURL(CURLE_FAILED_INIT)), + GB_CONSTANT("URLMalformat", "i", GBCURL(CURLE_URL_MALFORMAT)), + GB_CONSTANT("UnableToResolveProxy", "i", GBCURL(CURLE_COULDNT_RESOLVE_PROXY)), + GB_CONSTANT("UnableToResolveHost", "i", GBCURL(CURLE_COULDNT_RESOLVE_HOST)), + GB_CONSTANT("UnableToConnect", "i", GBCURL(CURLE_COULDNT_CONNECT)), + GB_CONSTANT("FTPWeirdServerReply", "i", GBCURL(CURLE_FTP_WEIRD_SERVER_REPLY)), + GB_CONSTANT("RemoteAccessDenied", "i", GBCURL(CURLE_REMOTE_ACCESS_DENIED)), + GB_CONSTANT("FTPWeirdPassReply", "i", GBCURL(CURLE_FTP_WEIRD_PASS_REPLY)), + GB_CONSTANT("FTPWeirdPasvReply", "i", GBCURL(CURLE_FTP_WEIRD_PASV_REPLY)), + GB_CONSTANT("FTPWeird227Format", "i", GBCURL(CURLE_FTP_WEIRD_227_FORMAT)), + GB_CONSTANT("FTPUnableToGetHost", "i", GBCURL(CURLE_FTP_CANT_GET_HOST)), + GB_CONSTANT("FTPUnableToSetType", "i", GBCURL(CURLE_FTP_COULDNT_SET_TYPE)), + GB_CONSTANT("PartialFile", "i", GBCURL(CURLE_PARTIAL_FILE)), + GB_CONSTANT("FTPUnableToRETRFile", "i", GBCURL(CURLE_FTP_COULDNT_RETR_FILE)), + GB_CONSTANT("QuoteError", "i", GBCURL(CURLE_QUOTE_ERROR)), + GB_CONSTANT("HttpReturnedError", "i", GBCURL(CURLE_HTTP_RETURNED_ERROR)), + GB_CONSTANT("WriteError", "i", GBCURL(CURLE_WRITE_ERROR)), + GB_CONSTANT("UploadFailed", "i", GBCURL(CURLE_UPLOAD_FAILED)), + GB_CONSTANT("ReadError", "i", GBCURL(CURLE_READ_ERROR)), + GB_CONSTANT("OutOfMemory", "i", GBCURL(CURLE_OUT_OF_MEMORY)), + GB_CONSTANT("OperationTimeout", "i", GBCURL(CURLE_OPERATION_TIMEDOUT)), + GB_CONSTANT("FTPPortFailed", "i", GBCURL(CURLE_FTP_PORT_FAILED)), + GB_CONSTANT("FTPUnableToUseRest", "i", GBCURL(CURLE_FTP_COULDNT_USE_REST)), + GB_CONSTANT("RangeError", "i", GBCURL(CURLE_RANGE_ERROR)), + GB_CONSTANT("HTTPPostError", "i", GBCURL(CURLE_HTTP_POST_ERROR)), + GB_CONSTANT("SSLConnectError", "i", GBCURL(CURLE_SSL_CONNECT_ERROR)), + GB_CONSTANT("BadDownloadResume", "i", GBCURL(CURLE_BAD_DOWNLOAD_RESUME)), + //GB_CONSTANT("FileUnableToReadFile", "i", GBCURL(CURLE_FILE_COULDNT_READ_FILE)), + //GB_CONSTANT("LDAPCannotBind", "i", GBCURL(CURLE_LDAP_CANNOT_BIND)), + //GB_CONSTANT("LDAPSearchFailed", "i", GBCURL(CURLE_LDAP_SEARCH_FAILED)), + GB_CONSTANT("FunctionNotFound", "i", GBCURL(CURLE_FUNCTION_NOT_FOUND)), + GB_CONSTANT("AbortedByCallback", "i", GBCURL(CURLE_ABORTED_BY_CALLBACK)), + GB_CONSTANT("BadFunctionArgument", "i", GBCURL(CURLE_BAD_FUNCTION_ARGUMENT)), + GB_CONSTANT("InterfaceFailed", "i", GBCURL(CURLE_INTERFACE_FAILED)), + GB_CONSTANT("TooManyRedirects", "i", GBCURL(CURLE_TOO_MANY_REDIRECTS )), + //GB_CONSTANT("UnknownTelnetOption", "i", GBCURL(CURLE_UNKNOWN_TELNET_OPTION)), + //GB_CONSTANT("TelnetOptionSyntax", "i", GBCURL(CURLE_TELNET_OPTION_SYNTAX)), + GB_CONSTANT("PeerFailedVerification", "i", GBCURL(CURLE_PEER_FAILED_VERIFICATION)), + GB_CONSTANT("GotNothing", "i", GBCURL(CURLE_GOT_NOTHING)), + GB_CONSTANT("SSLEngineNotFound", "i", GBCURL(CURLE_SSL_ENGINE_NOTFOUND)), + GB_CONSTANT("SSLEngineSetFailed", "i", GBCURL(CURLE_SSL_ENGINE_SETFAILED)), + GB_CONSTANT("SendError", "i", GBCURL(CURLE_SEND_ERROR)), + GB_CONSTANT("RecvError", "i", GBCURL(CURLE_RECV_ERROR)), + GB_CONSTANT("SSLCertProblem", "i", GBCURL(CURLE_SSL_CERTPROBLEM)), + GB_CONSTANT("SSLCipher", "i", GBCURL(CURLE_SSL_CIPHER)), + GB_CONSTANT("SSLCacert", "i", GBCURL(CURLE_SSL_CACERT)), + GB_CONSTANT("BadContentEncoding", "i", GBCURL(CURLE_BAD_CONTENT_ENCODING)), + //GB_CONSTANT("LDAPInvalidURL", "i", GBCURL(CURLE_LDAP_INVALID_URL)), + GB_CONSTANT("FileSizeExceeded", "i", GBCURL(CURLE_FILESIZE_EXCEEDED)), + //GB_CONSTANT("UseSSLFailed", "i", GBCURL(CURLE_USE_SSL_FAILED)), + GB_CONSTANT("SendFailRewind", "i", GBCURL(CURLE_SEND_FAIL_REWIND)), + GB_CONSTANT("SSLEngineInitFailed", "i", GBCURL(CURLE_SSL_ENGINE_INITFAILED)), + GB_CONSTANT("LoginDenied", "i", GBCURL(CURLE_LOGIN_DENIED)), + //GB_CONSTANT("TFTPNotFound", "i", GBCURL(CURLE_TFTP_NOTFOUND)), + //GB_CONSTANT("TFTPPerm", "i", GBCURL(CURLE_TFTP_PERM)), + GB_CONSTANT("RemoteDiskFull", "i", GBCURL(CURLE_REMOTE_DISK_FULL)), + //GB_CONSTANT("TFTPIllegal", "i", GBCURL(CURLE_TFTP_ILLEGAL)), + //GB_CONSTANT("TFTPUnknownID", "i", GBCURL(CURLE_TFTP_UNKNOWNID)), + GB_CONSTANT("RemoteFileExists", "i", GBCURL(CURLE_REMOTE_FILE_EXISTS)), + //GB_CONSTANT("TFTPNoSuchUser", "i", GBCURL(CURLE_TFTP_NOSUCHUSER)), + GB_CONSTANT("ConvFailed", "i", GBCURL(CURLE_CONV_FAILED)), + GB_CONSTANT("ConvRequired", "i", GBCURL(CURLE_CONV_REQD)), + GB_CONSTANT("SSLCacertBadFile", "i", GBCURL(CURLE_SSL_CACERT_BADFILE)), + GB_CONSTANT("RemoteFileNotFound", "i", GBCURL(CURLE_REMOTE_FILE_NOT_FOUND)), + //GB_CONSTANT("SSH", "i", GBCURL(CURLE_SSH)), + GB_CONSTANT("SSLShutdownFailed", "i", GBCURL(CURLE_SSL_SHUTDOWN_FAILED)), + //GB_CONSTANT("Again", "i", GBCURL(CURLE_AGAIN)), + GB_CONSTANT("SSLCrlBadfile", "i", GBCURL(CURLE_SSL_CRL_BADFILE)), + GB_CONSTANT("SSLIssuerError", "i", GBCURL(CURLE_SSL_ISSUER_ERROR)), + GB_CONSTANT("FTPPretFailed", "i", GBCURL(CURLE_FTP_PRET_FAILED)), + //GB_CONSTANT("RTSPCSeqError", "i", GBCURL(CURLE_RTSP_CSEQ_ERROR)), + //GB_CONSTANT("RTSPSessionError", "i", GBCURL(CURLE_RTSP_SESSION_ERROR)), + GB_CONSTANT("FTPBadFileList", "i", GBCURL(CURLE_FTP_BAD_FILE_LIST)), + GB_CONSTANT("ChunkFailed", "i", GBCURL(CURLE_CHUNK_FAILED)), + + GB_END_DECLARE +}; diff --git a/gb.net.curl/src/CNet.h b/gb.net.curl/src/CNet.h new file mode 100644 index 00000000..de0e549c --- /dev/null +++ b/gb.net.curl/src/CNet.h @@ -0,0 +1,47 @@ +/*************************************************************************** + + CNet.h + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CNET_H +#define __CNET_H + +#include "gambas.h" + +#ifndef __CNET_C + +extern GB_DESC CNetDesc[]; + +#else + +#define THIS ((CNET *)_object) + +#endif + +enum +{ + NET_INACTIVE = 0, + NET_ACTIVE = 1, + NET_RECEIVING_DATA = 4, + NET_CONNECTING = 6, +}; + +#endif diff --git a/gb.net.curl/src/CProxy.c b/gb.net.curl/src/CProxy.c new file mode 100644 index 00000000..af71cea8 --- /dev/null +++ b/gb.net.curl/src/CProxy.c @@ -0,0 +1,135 @@ +/*************************************************************************** + + CProxy.c + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPROXY_C +#include +#include +#include +#include "main.h" + +#include "CProxy.h" + +static bool check_active(CPROXY *proxy) +{ + if (*(proxy->parent_status) > 0) + { + GB.Error("Proxy cannot be modified while client is active"); + return TRUE; + } + else + return FALSE; +} + +BEGIN_PROPERTY(CurlProxy_Auth) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->proxy.auth); + else + { + if (check_active(THIS)) + return; + + if (CURL_proxy_set_auth(&THIS->proxy, VPROP(GB_INTEGER))) + GB.Error("Unknown authentication method"); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CurlProxy_Type) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->proxy.type); + else + { + if (check_active(THIS)) + return; + + if (CURL_proxy_set_type(&THIS->proxy, VPROP(GB_INTEGER))) + GB.Error("Unknown proxy type"); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CurlProxy_User) + + if (READ_PROPERTY) + GB.ReturnString(THIS->proxy.user); + else + { + if (check_active(THIS)) + return; + + GB.StoreString(PROP(GB_STRING), &THIS->proxy.user); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CurlProxy_Password) + + if (READ_PROPERTY) + GB.ReturnString(THIS->proxy.pwd); + else + { + if (check_active(THIS)) + return; + + GB.StoreString(PROP(GB_STRING), &THIS->proxy.pwd); + } + +END_PROPERTY + + +BEGIN_PROPERTY (CurlProxy_Host) + + if (READ_PROPERTY) + GB.ReturnString(THIS->proxy.host); + else + { + if (check_active(THIS)) + return; + + GB.StoreString(PROP(GB_STRING), &THIS->proxy.host); + } + +END_PROPERTY + + +GB_DESC CProxyDesc[] = +{ + GB_DECLARE(".Curl.Proxy", sizeof(CPROXY)), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Host", "s", CurlProxy_Host), + GB_PROPERTY("User", "s", CurlProxy_User), + GB_PROPERTY("Password", "s", CurlProxy_Password), + GB_PROPERTY("Type", "i", CurlProxy_Type), + GB_PROPERTY("Auth", "i", CurlProxy_Auth), + + GB_END_DECLARE +}; + + + + diff --git a/gb.net.curl/src/CProxy.h b/gb.net.curl/src/CProxy.h new file mode 100644 index 00000000..cf3d8022 --- /dev/null +++ b/gb.net.curl/src/CProxy.h @@ -0,0 +1,43 @@ +/*************************************************************************** + + CProxy.h + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPROXY_H +#define __CPROXY_H + +#include "gambas.h" +#include "gbcurl.h" + +#include "CCurl.h" + +#ifndef __CPROXY_C + +extern GB_DESC CProxyDesc[]; + +#else + +#undef THIS +#define THIS (&((CCURL *)_object)->proxy) + +#endif + +#endif diff --git a/gb.net.curl/src/Makefile.am b/gb.net.curl/src/Makefile.am new file mode 100644 index 00000000..f2738cdb --- /dev/null +++ b/gb.net.curl/src/Makefile.am @@ -0,0 +1,21 @@ +COMPONENT = gb.net.curl +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.net.curl.la + +gb_net_curl_la_LIBADD = @THREAD_LIB@ @CURL_LIB@ +gb_net_curl_la_LDFLAGS = -module @LD_FLAGS@ @CURL_LDFLAGS@ +gb_net_curl_la_CPPFLAGS = @THREAD_INC@ @CURL_INC@ + +gb_net_curl_la_SOURCES = \ + gbcurl.h gbcurl.c \ + main.h main.c \ + CCurl.h CCurl.c \ + CHttpClient.h CHttpClient.c \ + CFtpClient.h CFtpClient.c \ + CNet.h CNet.c \ + CProxy.h CProxy.c + + + + diff --git a/gb.net.curl/src/gb.net.curl.component b/gb.net.curl/src/gb.net.curl.component new file mode 100644 index 00000000..4c35d9d5 --- /dev/null +++ b/gb.net.curl/src/gb.net.curl.component @@ -0,0 +1,12 @@ +[Component] +Key=gb.net.curl +Require=gb.net +Name=Network high-level protocols management +Name[es]=Manejo de protocolos de alto nivel de red +Name[fr]=Gestion des protocoles réseaux avancés +Name[pl]=Zarządzanie sieciowymi protokołami wysokiego poziomu +Name[tr]=Yüksek-seviye ağ protokolleri yönetimi +Author=Daniel Campos Fernández + +[Network] +Virtual=HttpClient,FtpClient diff --git a/gb.net.curl/src/gb.net.curl/.directory b/gb.net.curl/src/gb.net.curl/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/gb.net.curl/src/gb.net.curl/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/gb.net.curl/src/gb.net.curl/.icon.png b/gb.net.curl/src/gb.net.curl/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a9254f949f86671b58a744a6a93623576acc5217 GIT binary patch literal 10569 zcmb_?cQl*t`~Q;=d(|%0q_kGmUPW!Cwzpk-6ty=+lUS)$bSY|6vs6*L#HMOj?b@qW zlp+Mbr|-{qobUg?b8?QHJaOIEec#u3U9VSS^>x*$DcC3g0HA)Lq52O1K)|;UfQ$tE zW9eJ$1OR+=4^)+mf~R*f`;u->WX`t=9VN`VZAH&HRzJ|R#L!Dz?|6s~UlS%upw?nx z7p7vR6P7=v#kIBIoooU4_SiKtcymUWq^%7V+#Jch9 zsvRjg9*fT%8C?8H&eOtQA zwe`!zGliCue5Kdnl$3xXML7E%rfqSLjLad4!qnm7hHjE~!-ZeTr;#ad48G5**%`d1 zoZ3p84n3W=4^^W^eswN-jjzAbmRM(1>8(3T=s69HSR2|1%6d2c;|J>L$zE%#-&K^w zQiz`&B>8dHb4=v%kaDP;*9-!K-R{or(|+8>{qc&uiI7=^(}ASl8~F=M zd1M?L`|<=8tXyIDpGA4f>H6WpdcRlG_q_PM9IMU^B~T%H4Ciuc2hQsBJh)6xwHkJX zFA1bk^*=4+V9B3G^ee~yX^V?Q(PZ1W>IPaG>RFJET_^zEll6B3XQ8P_dv6#7h@zQZ zZwbV6jdy<-PE36=SFkp89+WTxzZ5L25SkqmpR67NZ?w$ zP)z$Vu7*SYpwUMqZEdI}a%hy$<<}LuBwe>AbiHCMTIyF`L%_NaQga_zViX*Q_PR_k zxl6n$A#0XqexAL0Ack3H&!1ado)B`WmlvsP&S{VIhY4MKLWCCI_&l5y%Q&Gc-w#LpfWD*H zNT*3{AF!9C`~--mOTczyWJlJAtSK4u&bwlyow}ffVHaX~=j&PP>dZCpbwTa3lWHff z?)2syJf%rnl{kt-kN%J*3ktC9{Clwf$f2bN+Xc{{Zsp`z89JR3%o7l(LHq-omtSmq zBybN#E|fEFR0|e9J$yWneV)Oxju&Zhl97L54(pwfk!Lp`R4_EiOishIes z{~617f|_L!<-{fw=pu?q6PSheCf-+5t1i8Ej1PCXf;4ck$V%s$9^xVLG_Pr$>@hze zER%H0Uq%H8kI=3lpYAsbC@6lbztA4AsZkVsaFFc?)0ac1uF(qslE(&h65?)OC)d?W zrOZ6)XNZE)G+EExSWu_|s<{Fj8R5ih)LVnEu51e!I*bOz4+b zOB+Fod@fCT{!YMk?Qq3$?fNU^y+Tm}#YEP7{M7e7`|U z_4c0r(~RU0N4?mw@LKvQrSR}43%%B2IqG+|&>^GMER2ptwzM!J{k^s>b>Tu35YLi=AXx@bPn@rI#k8k?Hcall|j7Hzf!`+L%__Wlkt6;d@8WGXWV&0V6d&M@I z0FWdca0s3>$qxA({_ay1PDY^ny&-~&#by-Sd9ukxBTOy4T5SA|2jnqf=Lzodxzh60 z7tdz|B>O3L3MQ)hLcb2K8pb`&K*+MbyVjq!uoS%q1q5CN?>R6)0rQLeSe$o8ctaxcc;27n{;OMPyOi%eUvR9GDsl8qH%~_A4n9&N}LlM)z=cqu2x-mjgv93|~w+ zi_)rI1P5Y;nz)68MMt`gL`Po|A&EtV+#W$3QyZZWnjsd$^n01>>VdmaH{m84Yf6%4 zl<6o}tn+a)d9uK`6#Hj$*&^4A*&Q>50X;e%A3I1TuYeVV;hOc$(M_2bZofY)bf+0U zox+zytKt`&Urp$&{i%%g2`ya{3d4vi_`EUGqC|9P+zCf48ES>}j9ieTT8i=26H^N; z4g)qB+!--H*F8ST@$oWHJ>jeL$HgnNPdO{5XV=?*D2FB6{qhYFPN=3k-Tys36~B2i z`-ASW>Zj-JpBv~Eln&Mv98xWzRLN^R6)-msh$Cy`wLqntzo&p4VX@?aS{>DW$_MhS zl;PU}lDE;yp$%)Gdy#sNFCuO!feU>7FeiY0z}g7ym(J3ab@QXfOl^MQwUqf%$umKC z5{EmnA0>1KtZJG8Pn?Zm%4C}~j0|3JebSF>tqWJY*QtAI0yq$ir)okxPv}s{&{$okznp?o5m1SCOy0oLw(Ar@|RN z3Q1~$R=6!6fL5B@u|_Zh&P3-Ej(*Z!nGDy9B~C7FbCT+xufUVJ;g(J6&0NY)>XZ?a zh78RjN_N|Q@J1&+n^(+q8rrMR8u=A%7UNKM`g?buZy?Fu$?o)e0Y28(XBDjO-{>{y zedIzEv<~zhdyf)SuQm`<+CIG>)y^8lk7X#yif_2=LNZsAL%?TG8mg)Ro?KB9W4q`- zgk==?8L`32!%m>kZu0_}L0Qswa8Q6J&+GkM4g6+z@RhBk0>9yt;r1BfD4b$)z|cQI zuq4}dAIBBVyL~*JP_%Tom{Ld&Q!cwatoAGU;N}Kdk?M8&PHUF4#sWl0EAbLvoP>IP zI6Wd?GOG9t6Xv%1b-Pnp0`=`KK^jAcRk(OIAK6F47pV9_VuG7?LBOo^bgW!e(D=ab z#zK#Xyz*Pq=fMt{*vQDya@t4efkx_AL}^K$YV)L;o7MjRo1 zU*M$k2?<8wvU;ygREKQ6uXHaFv6ko0tf*^3vv4h4VTC*|qQPdx0^SlW8|0--MswZY z^l1>W2+Qhcpz31CM~727;^rVx%$WOU_LH{b7B2P!S)gbE?jynb5hhFug(}f=rHLNL zZ=_@3=No#-uq#n0VomtX3X1o2fG#IJx&8HQ*BipZ1bnt;D;!YWR@TQQ)hj2%gw;5u zm628duHF21?bW?#RQ<1AhR%liO2GS-?f7vey`%*-VSyBt+RDNiP9{^f%dC_^Sp`@_ ztj2K5@bf-L;*mXQMyu~T;!hM6=k_`MPSblsh=c6)&ST3c%mJ3DzfUj`XqIU34HHHHO{u&FmW;b~u;gn5-|?s1WJ@ol z>wp|Ggpwt^7lw$TSAn7tn&0rwkcu=~{mb?2ibl##imMlIX-(#$-zYN!O0QYz0b&Dt z-Ydn5)l;2cLXU@CKby=k*+ULP?#UqutmjD7xRp7(wGv=C?`wLzJJ+Fy{LK({IHF#* zOYsS~E*FeWQn+BHh06zrnx|p9N-%g7<=`aq8HlyqL(Bwvt8zSlg z$nrXqf&X@liX4dG6=5X4`BkSc+xa22#_RMdg`pKFooeh2h$7uB4DnwFDWLSLg)2j1 z=6@a40RHd8f4z5MM)OMDLS^+q|60D)7W2|@cRfwuukjkTWL}9!s4PA*jo0s9Dh$;K zVVTgB;Zc;T868PI>aV>!EfH6s8Sw;jCx9_p=@C_WdrldJfJfLy9MsrOe=_8(m7Il) zfku${`ysMIx%DIcO`rlGXTdEI-&1JMVz3<|uiT5;z+ai=4QF`}PUpu56_0KkWi;}K z)mG#^+N?Ja^T~HwavD(VA2k>;;{6puTOho+5qyJF(AJHDzmZ~#;_!@L32IB-C*A`0 zleSC9f-m<{`z&B4{Rj^rblEq6nomG|8U^~!jN**waE-sXHg!scq6OE$*J#CWutw{s z`RmW={K6+Jm^MoP35-7_aZJhhU^15Cq9q_9V<1u3v zFj2w5NNmZq^u1>HBi>bd^I#kDeV7rnIY6(Olx?g2I)1$?l47`X;uV_!Y%h)Z5`qA?56}v^j{kHGQ zx5!;v^s+wOH=a-%N)n;6uQ%}B7t+|bPh?34i zx~1@rP<$$-v|WTj>cTL!2nB;n!VS%pZ|wu=v8B0f`fs_&qLPm{qC}1|fNZ$*i9agl zW24}GJE$NK-Di%5O#<&R#8@_A3Wl(`Ke(wjx>juo)I<`q=t(@8hF#?z$q8yGRQgbv zDxY=FYhvH2A7fzt5-2V_y8cHO$bH*A?ZZG)Hju34XAjm=w+`f5$58{Al zb5QMjd!dDB)F*>F-v3boyt!zkDCm6e&eK5BgmS8)yNOl#=bB@dhpG2xI zh>6h^@Ws8O)8Kkp?1UAqmC*g(yNo1Y5^ymo$0;je`@w3%lss?{^o0zxf$P({x;YQG z(mrsGTsF?$L@Z9iqlFn^GfbqIJgm{GPukUBPUCaY2EOk6{44TL?sl#};{rEKh$2eH zQx)>leg!P4ZUQFgjCH+A*~9%hA#hFLdgq6aFz zAvn0_EO|!_(PsLoorz@! zSKBWjXS(MKqu)s|K(V{SJ@Izvbs{*2P!#(ujVN?_Vn$TId)pI%NcX^;t!F2M;qdaZ z;$Qr(-Cb~&8|XxyI|(*lt}Pvan^J*$hBZ5vw%fXvP%1NHm2=`0wQe4qha#iYIaq80 zEU7wHzO{W6-lUzNrj~N!w)Qf?9bumDltQeGg{ka$1;#6tS4IM?4D`jsH~3ZB->)v- z7iquyff|A(rw<$V84(Yd^_rQi!r#U(rL6_!6LUzlzg6e@n+;MWQwrsHF{5t%5hJ5R zwMJkSne~|2HrBE1G_jeibuTnn2DdoyVzG#iNSEha4Vq*mV+>Ik2xc-^yPSN&;b}F@bmDSF>PH_C`~C%T)|$h)yT-c^2sN>X4yV5 zFbGSWr9++BBP1?!Bfs!vqu7k?irM%}G5ke5EJ|Q+@P(vkbizWA36Tadkp|Keud|^! zSu_{4Uf=h3-ciq%T97#;t`pJP99xF@tmLfs;B7~lFzLBVCIg*K782V=IsmqfSHt4X zLn)aK`j~n}g&3dI3e|1Ah@=OsQD*-9xBrV#UQO>6%*k(;DDWl;tQ+Dna_e+k;bZQkQ5IKWCaPPCp?Hnm#!iAhC8ci=j{a7%C_)>y7)cm;ZL~ zowJcY(?CF9AY#`4L0f061dx?J#`#>Y2O?B$gIE{9 zf`{9@pi7de$oLgMlz87UMQ>nO0}vpi2eeeVFM7kphX7$Cn1@!+oC$1xwsZ;E1hp{T zx8GQPFyCQn#iIT*_@^9KJPWwpSq~GO)65{McJ6Af$gp=Yzdq}lFRbc)pHL?7QR0Kx z8HZmuVr_%eprru67f2fX-3PplV82;egad1Up~~F%9HGZn%2Nn%(-Eo5*cWT4bh7CrYhD+T3=_%$}N+KQz=PO#&L zk`I3b2V@DnVaoMWmNnoMCbQp`g=8IlArQt|o)Ph2u}O41_PZxRue}e?S)VH07hgWB zqL4>+icmRROMuF4o}DZWw?%$|WKla-3fnXY^n-hLy0T^;ApP;lJ55f{;zoA3E+*wmD(w-mHZitUvWa)=<40; zaSRIC$Fe${zm*>cP(`qENOkrbpjn8n#)FH1NQCu7Bttk$b~tG1=@$Pl3<+3EOR2;! zSiqioi*r;P8%6o;6`@QRtMABhx%4+e*_k-NmjNf;{*+;y2n#EQywD9g-56K**2J5Ue5NQ!%{CmVzmKVciE zT_Pj{W}H;MK1uCsWu9AVisQq$M((%$Z0S@PbH!;rFhw;AGzc|=*qLRlqqw9r z3}YwM0mlH^QcPT+n)=ww&s5Bs|4C3V)vkryu$F>Yig9q z6)B$)W1)Se^D=e`J@;5zw?Z~f56X!E$0gkf+I!8_yS9W`f%R;rhuh67Y@?DF#*`X%+nj3@v>2tRqPEit+UzBnQD?8Fg; zwx-M6s*Xj^imX4O?GSf)`}jOgbiR>*(3Scr&9>;Q*LX6J8<{ADQb>s|Df!QY(Efex zgN(1;*K@g7uu4TvL17AqZ5c?*Ze#E?N+LUyQPWhB3}Rur*hTI+(bI%aFP)X<5_`)a zNavHL0G45O&-5bH!a}tQ=@5y$eT#e{>V8U&f3;KLcw(#?SboCmA2VKVRm8u|2XtA7 ztS*|!SbX8(AV9I6ZOc!Lmzt5Z_Ig-vRag0FtC*cFAtGA==Wwll^l9+)F|K~yK8Q7E zSuN&OfB$roHh+4YMF3^pFJ}IO9~@kQ!~;+PI4ztAMnlR3!t#ZO6&l^*)J@24J%ZW%$N3RW{>Mo%Ni3|FmZI}2yg2~EYxKiAG zibT?bh*~TE&!tubc+0(EI1nm!VY8b^7MdNm&L{4~U6!V%M{vww2hzg}OLQ0&ZZK2E zPQmE>5_94CTNtn>&E8!;F&KP)MdBin=_84L}(C$@aLH|F^^2s1v1SJ zaJOp9#6(Y|GxMMN2K+O1DGH_nuwH&?W8%o2KYOy`+0mZa82zD(I7R?Z3G=50_apqDgtZd z!Z*K1ru;7>mGsogFw@jLF* zGVar7UXS#tY$0Sf{Eivq=YLo@u~Czv?O3^D@DKU9-}Hq)6*$>Go=udZqfbN1?qp7e}Zg-U~_MJG((K7yEdECe! zE-E7PU!mq{QRX`GDh9qfUM`vsUzjP)4Oc#9pDf;RF_*CytXx0Y2tHuCD`C{Iu^u?r zxpfB&(wbe=xIUdXM3}p-Zq$e*W#QYrxe4sJy!LtBv}E8jr1k1?7w&iMtjy=3WTG0+ z-a8GF0vzvZzaN1PmzRd8+&cc{kkFR&q>;!Q{jur#eLn&1Qa1jdt~0^T)!<01s%8?Rl@~kO1-B^gh^^fL$jNY>Wc@9f7EJx(-z#}*QYbglnS^aycpV`o-QW8gMwSl%*zA|*cK#Wx*a{tR8C2|RM{v|z*o}H|$(%Br#orv_9-=YbK&UMn*xg03x-!&Y( zoK~s^C>NSn%Oh+2kADhdU$EG*au>6^?}oPn#oFNyy~~6g68emvZz7?3p(g|3uO_5Z z)elUmnLxk{-ayCJ07dN&pSpRl>VPE#P$7Tam&!BGzx2g4 z6UXtxKzXylbO+~zMbWqor*ldM8mR;yVT%3w^`)_=q}kiFAk^sh=ow@cry!zId#JSM zIgZ;61gkZJB<$h`4Lv}qXv$Qh&G9ZJyzxf7rRFRA;ft{YE`qBUaAyu*^l0<;{u}j@ z)=PYNBsdy|jR*LwVEoPqn9>}N$HDo=ZFOEh_J0Q6(F@@IL3vsx0K5?~Ym6(r6|^;1 zOTFE6V^%B>#->KzvfI$GX*u?&201e2YtG~#_415d4u1L<->;LZgw^iZq8JC?#JpbR z5Z9sh@-hNVQhU{UY)_If{yv+a2fd82Yu;87-BBqB?e(!h=?rEme9A?H!$(O&cJwqhIval!-x z1ym6I&G};*|E|~M2JA`}b(sGb{R%p{ML$ab$+<<2L~bZPqH2Vy#y-C6x|g@mCeC9-Xw(GS9-jT25%Bi20=kisS!!clK|3o^&|KRcBk~qz=6(V8?WUKcIsKTshYZZ zroVW=PMf6l7Y1|Y!JaSNM#+PIM-j1+K>kMn7Q}c<+FJ!=9BJl&rVg<5#(`z%+G`eK zMKd~}f)SVsi7=h}s9&+`0Jb;JaqX~5!;U&naopg2CTgw72cc(HS9!_mi3N1pb{eA* zDdT?ibbp8GOov=LL}a8-`^d3F)F;bGlqU<~Y7?MzdTMOIL}Fp+10wdIy&4>;lxuMQ zqkPZDZq>ccp8qwgfCRL^F$n0Rk88Kc7{U!w5t^hca_>9tu9UVR7O{7q{^?f+Nw1V{ ziC@fM2ruS?49I+?fmqGB@zECmB)j`yW}E?r9ibKyyi}~jz7sk3c+h@owfI_b7znoZ z+)eh?>nu*XotUItNnDM_kH0E2g{Q=n@K2Vqku^L|WPv6t)mZVQ-sY~PsIt!qQ0aJ{ z9u6Mep2q~PoIRlVM+2eQ>X6#El0PLBlJrf3YCL$fJ9%s@Hzb@jGDN`N0 z$x8n)hqAb!I@e%S6Q)ULx__?`1Sz*(-s&{=ht&m?+>53g9-WFPdJ2z348EPI_+?to zM^#&q8Z~^U+dFkXWuV5T;(rLMV->P zMi~-g`j;dIFE+jOup!8`>ZSgj@}c=E@#bqVZf=o7N+0I0*e{+-Z-zK;>>z=oz_BV=UAz-$X z=F@TLHIjKY`g)lR3(v&s88Vt}k`#be-UTp#lb7cYZFeJ3McAA%4PY7<&s}%b+qrU} zTn)IXn#_F6)7WW#OzACO!4fP?;Y@)>SV@@G5Lfvwm`5)-Kyw~1-XBn%0hJ76Vaw)0 z?3;rBkoMZO>6Lk9Aj}-mELthFLJO>C7fFz=63!>RvNxd{{MtIZfKj`hUIExQ9jeBo zF#N~3vP^Fd7T-v^#3@jt(gl-L0%4J5@qJbNF!l)uyC=}!{y_}tO=Ua(xBjDZNdJFy zq5lY1|E22~(I@cU1^LRp|53gEueen8zsge?gz~Sr6^yeTpF=QrMv~GFZ~fsOv|3+( za=rMvVt7vye0e!Z;eOfp!vND582Au(vcGH` Download.Finish Then + bKeepRunning = True + Endif + hDownload = Null + Next + + If Not hDownload Then + Wait + If bKeepRunning Then + $hRunTimer.Trigger + Else + $bStart = False + Raise End + Endif + Return + Endif + + hDownload._State = Download.Downloading + + hClient = hDownload._GetClient() + hClient.Tag = hDownload._Key + hClient.TargetFile = Temp$() + Object.Attach(hClient, Me, "Client") + + hObject = hClient + hObject.Get() + + $aClient.Add(hClient) + + Wend + +End + +Public Sub Start() + + If Not $bStart Then Raise Progress + $bStart = True + $fSpeedTimer = Timer + Run() + +End + + +Public Sub Stop() + + Dim hClient As Object + + $bStart = False + For Each hClient In $aClient + hClient.Stop() + Next + $fProgress = 0 + $iSize = 0 + +End + +Private Sub DownloadIsReady(hDownload As Download) + + If hDownload._State = Download.Ready Then Return + hDownload._State = Download.Ready + Raise Size(hDownload._Key) + UpdateProgress + $hRunTimer.Trigger + +End + + +Public Sub GetSize_Progress() + + Dim hClient As Object = Last + Dim hDownload As Download + + If hClient.TotalDownloaded > 0 Then + hDownload = $cDownload[hClient.Tag] + hDownload._Size = hClient.TotalDownloaded + hClient.Stop() + DownloadIsReady(hDownload) + Endif + +End + +Public Sub GetSize_Error() + + Dim hClient As Object = Last + Dim hDownload As Download + + hDownload = $cDownload[hClient.Tag] + DownloadIsReady(hDownload) + +End + +Public Sub GetSize_Finished() + + GetSize_Error + +End + + +Public Sub Run_Timer() + + Run() + +End + +Public Sub Client_Connect() + + Dim hDownload As Download = $cDownload[Last.Tag] + Raise Message(hDownload._Key, Subst(("Connected to &1"), hDownload._GetHost())) + +End + +Private Sub UpdateProgress() + + Dim hDownload As Download + Dim iSize As Long + Dim fProgress As Float + Dim iTotalSize As Long + Dim bDownloadWithSize As Boolean + Dim iFullSize As Long + Dim bRaiseProgress As Boolean + + For Each hDownload In $cDownload + iFullSize += hDownload._Current + If hDownload._Size Then + iSize += hDownload._Current + iTotalSize += hDownload._Size + If hDownload.State = Download.Downloading Then + bDownloadWithSize = True + Endif + Endif + Next + + $iSize = iFullSize + + If (Timer - $fSpeedTimer) >= 1 Then + Try $iSpeed = (iFullSize - $iSpeedSize) / (Timer - $fSpeedTimer) + $iSpeedSize = iFullSize + $fSpeedTimer = Timer + bRaiseProgress = True + Endif + + If iTotalSize And If bDownloadWithSize Then + fProgress = Round(iSize / iTotalSize, -3) + Else + fProgress = 0 + Endif + + If bRaiseProgress Or If fProgress <> $fProgress Then + $fProgress = fProgress + Raise Progress + Endif + +End + +Public Sub Client_Progress() + + Dim hClient As Curl = Last + Dim hDownload As Download = $cDownload[hClient.Tag] + + hDownload._Current = hClient.Downloaded + UpdateProgress + +End + +Private Function Progress_Read() As Float + + Return $fProgress + +End + +Private Sub Finish(hClient As Curl, iState As Integer) + + Dim sKey As String + Dim hDownload As Download + + sKey = hClient.Tag + + hDownload = $cDownload[sKey] + hDownload._State = iState + hDownload._ErrorText = hClient.ErrorText + + $aClient.Remove($aClient.FindByRef(hClient)) + + $hRunTimer.Trigger + +End + +Public Sub Client_Cancel() + + Dim hClient As Curl = Last + Finish(hClient, Download.Error) + +End + +Public Sub Client_Error() + + Dim hClient As Curl = Last + Raise Error(hClient.Tag) + Finish(Last, Download.Error) + Stop() + +End + + +Public Sub Client_Finished() + + Dim hClient As Curl = Last + $cDownload[hClient.Tag]._Path = hClient.TargetFile + Finish(hClient, Download.Finish) + Raise Finish(hClient.Tag) + +End + +Private Function MaxClient_Read() As Integer + + Return $nClientMax + +End + +Private Sub MaxClient_Write(Value As Integer) + + If Value < 1 Or If Value > 32 Then Error.Raise("Bad argument") + If $nClientMax = Value Then Return + $nClientMax = Value + $hRunTimer.Trigger + +End + +Public Sub _get(Key As String) As Download + + Return $cDownload[Key] + +End + +Private Function Size_Read() As Long + + Return $iSize + +End + +Private Function Speed_Read() As Integer + + Return $iSpeed + +End + +Private Function Count_Read() As Integer + + Return $cDownload.Count + +End + +Private Function State_Read() As Integer + + Dim hDownload As Download + Dim iState As Integer + + For Each hDownload In $cDownload + iState = Max(iState, hDownload.State) + Next + + Return iState + +End diff --git a/gb.net.curl/src/gb.net.curl/.src/HttpForm.class b/gb.net.curl/src/gb.net.curl/.src/HttpForm.class new file mode 100644 index 00000000..b5f91264 --- /dev/null +++ b/gb.net.curl/src/gb.net.curl/.src/HttpForm.class @@ -0,0 +1,124 @@ +' Gambas class file + +Export + +Inherits HttpClient + +Public Const _IsControl As Boolean = False + +Private $cVal As New Collection +Private $aField As New String[] +Private $cFile As New Collection + +Public Sub Add(Field As String, Value As String) + + If Not $aField.Exist(Field) Then $aField.Add(Field) + $cVal[Field] = Value + +End + +Public Sub AddFile(Field As String, Path As String, Optional Name As String) + + If Left(Path) <> "/" Then Path = ".." &/ Path + If Not Exist(Path) Then Error.Raise("Path does not exist") + If IsDir(Path) Then Error.Raise("Path is a directory") + + If Not Name Then Name = File.Name(Path) + Add(Field, Name) + $cFile[Field] = Path + +End + +Public Sub Remove(Field As String) + + Dim iPos As Integer + + iPos = $aField.Find(Field) + If iPos < 0 Then Return + + $aField.Remove(iPos) + $cVal.Remove(Field) + $cFile.Remove(Field) + +End + +Public Sub _get(Field As String) As String + + Return $cVal[Field] + +End + +Public Sub _put(Value As String, Field As String) + + Add(Field, Value) + +End + +Public Sub Clear() + + $cVal.Clear + $aField.Clear + +End + + +Public Sub Submit() + + Dim sBoundary As String + Dim sPath As Variant + Dim I As Integer + Dim sPost As String + Dim hPost As File + Dim sField As String + + Do + + sBoundary = "------" + For I = 1 To 4 + sBoundary &= Hex$(Int(Rnd(65536)), 4) + Next + + For Each sPath In $cFile + If InStr(File.Load(sPath), sBoundary) Then Continue + Next + + Break + + Loop + + sPost = Temp$() + + hPost = Open sPost For Create + hPost.EndOfLine = gb.Windows + + For Each sField In $aField + + Print #hPost, "--"; sBoundary + + sPath = $cFile[sField] + + If sPath Then + + Print #hPost, "Content-Disposition: form-data; name=\""; sField; "\"; filename=\""; $cVal[sField]; "\"" + Print #hPost, "Content-Type: application/octet-stream" + Print #hPost + Print #hPost, File.Load(sPath) + + Else + + Print #hPost, "Content-Disposition: form-data; name=\""; sField; "\"" + Print #hPost, "Content-Type: text/plain; charset=\"utf-8\"" + Print #hPost + Print #hPost, $cVal[sField] + + Endif + + Next + + Print #hPost, "--"; sBoundary; "--" + + hPost.Close + + Me.PostFile("multipart/form-data; boundary=" & sBoundary, sPost, ["Content-Length: " & CStr(Stat(sPost).Size)]) + +End diff --git a/gb.net.curl/src/gb.net.curl/.src/MMain.module b/gb.net.curl/src/gb.net.curl/.src/MMain.module new file mode 100644 index 00000000..7dd1c4c1 --- /dev/null +++ b/gb.net.curl/src/gb.net.curl/.src/MMain.module @@ -0,0 +1,42 @@ +' Gambas module file + +Private $hManager As DownloadManager + +Public Sub Main() + + $hManager = New DownloadManager As "Manager" + $hManager.MaxClient = 3 + + '$hManager.Add("https://gitlab.com/gambas/gambas/-/archive/master/gambas-master.tar.bz2") + '$hManager.Add("http://ipv4.download.thinkbroadband.com/512MB.zip") + '$hManager.Add("https://gitlab.com/gambas/gambas/-/archive/master/gambas-master.tar.gz") + '$hManager.Add("http://gambaswiki.org/zorglub.txt") + $hManager.Add("http://gambaswiki.org/gambas-wiki.tar.bz2") + $hManager.Start() + +End + +Public Sub Manager_Progress() + + Print Format($hManager.Progress * 100, "##0"); "% "; Format($hManager.Speed / 1024, "#.#"); " KB/s"; Space$(10); "\r"; + Flush + +End + +Public Sub Manager_Message(Key As String, Text As String) + + Print "["; Key; "] "; Text + +End + +Public Sub Manager_Finish(Key As String) + + Print "["; Key; "] ==> "; $hManager[Key].Path + +End + +Public Sub Manager_Size(Key As String) + + Print "["; Key; "] size = "; $hManager[Key].Size + +End diff --git a/gb.net.curl/src/gbcurl.c b/gb.net.curl/src/gbcurl.c new file mode 100644 index 00000000..26a45cdf --- /dev/null +++ b/gb.net.curl/src/gbcurl.c @@ -0,0 +1,372 @@ +/*************************************************************************** + + gbcurl.c + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/***************************** + NOTE THAT : + libcurl <= 7.10.7 lacks CURLE_LDAP_INVALID_URL and CURLE_FILESIZE_EXCEEDED constants + libcurl <= 7.10.6 lacks proxy authentication support + libcurl <= 7.10.5 lacks user authentication support + *****************************/ +#include +#include +#include +#include "gbcurl.h" +#include "CCurl.h" + +static char *_protocols[] = { "ftp://", "ftps://", "http://", "https://", NULL }; + +static void warning(const char *msg) +{ + fprintf(stderr, "gb.net.curl: warning: %s\n", msg); +} + +#ifndef CURLAUTH_NONE +static void warning_auth(void) +{ + warning("This component was compiled without authentication support."); + warning("Use libcurl version 7.10.7 or later."); +} +#define CURLAUTH_NONE 0 +#define LACKS_AUTH +#warning ####################################################################### +#warning COMPILING WITHOUT AUTHENTICATION SUPPORT - YOU MUST USE LIBCURL>=7.10.6 +#warning ####################################################################### +#endif + + +static void warning_proxy_auth(void) +{ + warning("This component was compiled without proxy authentication support."); + warning("Use libcurl version 7.10.8 or later."); +} + + +static char *CURL_get_protocol(char *url, char *default_protocol) +{ + char **p; + char *pos; + + for (p = _protocols; *p; p++) + { + if (!strncmp(url, *p, strlen(*p))) + return *p; + } + + pos = strstr(url, "://"); + if (pos) + return "?"; + + return default_protocol; +} + +bool CURL_set_url(void *_object, const char *src, int len) +{ + char *url, *tmp, *protocol; + + if (len == 0) + goto UNKNOWN_PROTOCOL; + + url = GB.NewString(src, len); + + if (GB.Is(THIS, GB.FindClass("FtpClient"))) + { + protocol = CURL_get_protocol(url, "ftp://"); + if (strcmp(protocol, "ftp://") && strcmp(protocol, "ftps://")) + goto UNKNOWN_PROTOCOL; + } + else if (GB.Is(THIS, GB.FindClass("HttpClient"))) + { + protocol = CURL_get_protocol(url, "http://"); + if (strcmp(protocol, "http://") && strcmp(protocol, "https://")) + goto UNKNOWN_PROTOCOL; + } + else + goto UNKNOWN_PROTOCOL; + + if (strncmp(url, protocol, strlen(protocol))) + { + tmp = GB.NewZeroString(protocol); + tmp = GB.AddString(tmp, url, GB.StringLength(url)); + GB.FreeString(&url); + url = tmp; + } + + GB.FreeString(&THIS_URL); + THIS_URL = url; + return FALSE; + +UNKNOWN_PROTOCOL: + + GB.Error("Unknown protocol"); + return TRUE; +} + +#if 0 +void Adv_correct_url(char **buf,char *protocol) +{ + char *buftmp; + int len; + int myloop,myloop2; + int pos=-1; + int myok=1; + + len=strlen(*buf); + for (myloop=0;myloop0x39) ) + { + myok=0; + break; + } + } + if (!myok) pos=myloop; + break; + } + } + } + } + + myok=0; + + if (pos==-1) + { + + GB.Alloc((void**)POINTER(&buftmp),len+1); + strcpy(buftmp,*buf); + GB.Free((void**)POINTER(buf)); + GB.Alloc((void**)POINTER(buf),len+strlen(protocol)+1); + } + else + { + GB.Alloc((void**)POINTER(&buftmp),(len-pos)+1); + strcpy(buftmp,*buf+pos+1); + GB.Free((void**)POINTER(buf)); + GB.Alloc((void**)POINTER(buf),strlen(buftmp)+strlen(protocol)+1); + } + + strcpy(*buf,protocol); + if (strlen(buftmp)>=2) + { + if ( buftmp[0]=='/') myok++; + if ( buftmp[1]=='/') myok++; + } + strcat(*buf,buftmp+myok); + GB.Free((void**)POINTER(&buftmp)); +} +#endif + +bool CURL_check_userpwd(CURL_USER *user) +{ + char *tmp = NULL; + bool ret; + + if (user->user || user->pwd) + { + tmp = GB.AddString(tmp, user->user, 0); + tmp = GB.AddChar(tmp, ':'); + tmp = GB.AddString(tmp, user->pwd, 0); + } + + if (tmp && user->userpwd) + ret = (strcmp(tmp, user->userpwd) != 0); + else + ret = (tmp == user->userpwd); + + GB.FreeString(&tmp); + return ret; +} + +/***************************************************************************/ + +void CURL_proxy_init(CURL_PROXY *proxy) +{ + proxy->type = CURLPROXY_HTTP; + proxy->auth = CURLAUTH_NONE; + proxy->user = NULL; + proxy->pwd = NULL; + proxy->host = NULL; + proxy->userpwd = NULL; +} + + +void CURL_proxy_clear(CURL_PROXY *proxy) +{ + GB.FreeString(&proxy->host); + GB.FreeString(&proxy->user); + GB.FreeString(&proxy->pwd); + GB.FreeString(&proxy->userpwd); +} + + +void CURL_proxy_set(CURL_PROXY *proxy, CURL *curl) +{ + GB.FreeString(&proxy->userpwd); + + if (proxy->user || proxy->pwd) + { + proxy->userpwd = GB.AddString(proxy->userpwd, proxy->user, 0); + proxy->userpwd = GB.AddChar(proxy->userpwd, ':'); + proxy->userpwd = GB.AddString(proxy->userpwd, proxy->pwd, 0); + } + + if (!proxy->host) + { + curl_easy_setopt(curl, CURLOPT_PROXY, NULL); + if ( LIBCURL_VERSION_NUM >= 0x070a08 ) + curl_easy_setopt(curl, 111, CURLAUTH_NONE); + return; + } + + curl_easy_setopt(curl, CURLOPT_PROXYTYPE, proxy->type); + curl_easy_setopt(curl, CURLOPT_PROXY, proxy->host); + if ( LIBCURL_VERSION_NUM >= 0x070a08 ) + { + curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy->userpwd); + curl_easy_setopt(curl, 111, proxy->auth); + } + else + warning_proxy_auth(); +} + +bool CURL_proxy_set_auth(CURL_PROXY *proxy, int auth) +{ + #ifdef LACKS_AUTH + return FALSE; + #else + if (LIBCURL_VERSION_NUM < 0x070a08) + { + if (auth) + warning_proxy_auth(); + return FALSE; + } + + switch (auth) + { + case CURLAUTH_NONE: + case CURLAUTH_BASIC: + case CURLAUTH_NTLM: + proxy->auth=auth; + return FALSE; + default: + return TRUE; + } + #endif +} + +bool CURL_proxy_set_type(CURL_PROXY *proxy, int type) +{ + switch (type) + { + case CURLPROXY_HTTP: + case CURLPROXY_SOCKS5: + proxy->type = type; + return FALSE; + default: + return TRUE; + } +} + +/***************************************************************************/ + +void CURL_user_init(CURL_USER *user) +{ + user->auth = CURLAUTH_NONE; + user->user = NULL; + user->pwd = NULL; + user->userpwd = NULL; +} + + +void CURL_user_clear(CURL_USER *user) +{ + GB.FreeString(&user->user); + GB.FreeString(&user->pwd); + GB.FreeString(&user->userpwd); +} + + +void CURL_user_set(CURL_USER *user, CURL *curl) +{ + #ifdef LACKS_AUTH + return; + #else + + if (user->auth == CURLAUTH_NONE) + { + curl_easy_setopt(curl, CURLOPT_USERPWD, NULL); + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_NONE); + return; + } + + GB.FreeString(&user->userpwd); + user->userpwd = GB.AddString(user->userpwd, user->user, 0); + user->userpwd = GB.AddChar(user->userpwd, ':'); + user->userpwd = GB.AddString(user->userpwd, user->pwd, 0); + + curl_easy_setopt(curl, CURLOPT_USERPWD, user->userpwd); + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, user->auth); + + #endif +} + +bool CURL_user_set_auth(CURL_USER *user, int auth) +{ + #ifdef LACKS_AUTH + if (auth) + warning_auth(); + return FALSE; + #else + switch (auth) + { + case CURLAUTH_NONE: + case CURLAUTH_BASIC: + case CURLAUTH_NTLM: + case CURLAUTH_GSSNEGOTIATE: + case CURLAUTH_DIGEST: + user->auth = auth; + return FALSE; + + default: + return TRUE; + } + #endif +} diff --git a/gb.net.curl/src/gbcurl.h b/gb.net.curl/src/gbcurl.h new file mode 100644 index 00000000..9bbaf673 --- /dev/null +++ b/gb.net.curl/src/gbcurl.h @@ -0,0 +1,71 @@ +/*************************************************************************** + + gbcurl.h + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_CURL_H +#define __GB_CURL_H + +#include +#include "main.h" + +typedef + struct + { + int type; + int auth; + char *host; + char *user; + char *pwd; + char *userpwd; + } + CURL_PROXY; + +typedef + struct + { + char *user; + char *pwd; + char *userpwd; + int auth; + } + CURL_USER; + +//char *CURL_get_protocol(char *url, char *default_protocol); +bool CURL_set_url(void *_object, const char *src, int len); + +bool CURL_check_userpwd(CURL_USER *user); + +void CURL_proxy_init(CURL_PROXY *proxy); +void CURL_proxy_clear(CURL_PROXY *proxy); +void CURL_proxy_set(CURL_PROXY *proxy, CURL *curl); +bool CURL_proxy_set_auth(CURL_PROXY *user, int auth); +bool CURL_proxy_set_type(CURL_PROXY *proxy, int type); + +void CURL_user_init(CURL_USER *user); +void CURL_user_clear(CURL_USER *user); +void CURL_user_set(CURL_USER *user,CURL *curl); +bool CURL_user_set_auth(CURL_USER *user,int auth); + +#endif + + + diff --git a/gb.net.curl/src/main.c b/gb.net.curl/src/main.c new file mode 100644 index 00000000..edc03690 --- /dev/null +++ b/gb.net.curl/src/main.c @@ -0,0 +1,60 @@ +/*************************************************************************** + + main.c + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include + +#include "CCurl.h" +#include "CHttpClient.h" +#include "CFtpClient.h" +#include "CProxy.h" +#include "CNet.h" + +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CNetDesc, + CProxyDesc, + CurlSSLDesc, + CurlDesc, + HttpClientDesc, + CFtpClientDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + curl_global_init(CURL_GLOBAL_ALL); + return 0; +} + +void EXPORT GB_EXIT() +{ + curl_global_cleanup(); +} + diff --git a/gb.net.curl/src/main.h b/gb.net.curl/src/main.h new file mode 100644 index 00000000..223fdac5 --- /dev/null +++ b/gb.net.curl/src/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2003-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/gb.net/AUTHORS b/gb.net/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.net/COPYING b/gb.net/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.net/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.net/ChangeLog b/gb.net/ChangeLog new file mode 100644 index 00000000..86f0c1cb --- /dev/null +++ b/gb.net/ChangeLog @@ -0,0 +1,241 @@ +0.4.3 (net - STABLE / curl - ALPHA - compiles on Gambas 0.94) + +* Fixed a "segmentation fault", when changing user name or password in both HttpClient and FtpClient + +0.4.2 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93b) + +* FtpClient external interface stabilized. + +* Finished 'Curl' class code, and code sharing between 'HttpClient' and 'FtpClient'. + +0.4.1 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93b) + +* Added a new class 'Curl', that is the base for the rest of classes in this component + +* Now HttpClient and FtpClient inherits from 'Curl', so they share a lot of code + +0.4.0 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93b) + +* HttpClient : some code improvements so now it is faster,smaller and wastes less memory. + +* Added 'FtpClient' class. + +0.3.1 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93) + + * Added two new properties to Socket class. They are read/write, the first is called 'Port'. if + value is zero (Net.Local), connection will try to stablish a Local socket, else a TCP connection + will be stablished. The second, 'HostOrPath' can be a Host or a local path. + + * Socket constructor has now no parameters. + + * Conect method has now two optional parameters. The first can override 'HostOrPath' property, and the + second overrides 'Port' property + +0.3.0 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93) + +* Added code to let the IDE show icons for all net classes. +* Removed lots of code to make the component lighter and faster. + +0.2.3 (net - STABLE / curl - ALPHA - compiles on Gambas 0.90) + +* Fixed a bug in UdpClient : segmentation fault when reading or writing data +* Fixed a bug in UDPServerClient example : trying to use CLOSE when UdpClient is not active +* defined before to allow compile on FreeBSD + +0.2.2 (ALPHA - compiles on Gambas 0.81) + +* 'NetCode' and 'AdvancedCode' classes changed to 'Net' +* Proxy properties from 'HttpClient' has been added into a new class called 'Proxy' +* 'ReturnCode' and 'ReturnString' properties are now called 'Code' and 'Reason' +* This version should compile now using libcurl 7.10.3, 7.10.4, 7.10.5, 7.10.6, 7.10.7, 7.10.8 and 7.11.0 +* now is after in all files to allow compile it on FreeBSD + +0.2.1 (ALPHA - compiles on Gambas 0.81) + +* Examples Updated +* New HttpClient interface defined +* 'AdvancedCode' class provides constants for 'Net.Advanced' component + +0.2.0 (ALPHA - compiles on Gambas 0.80) + +* Examples updated +* Modifications in configuration scripts to detect libcurl +* 'NetCode' provide the constants needed to work with all network classes +* 'Net advanced' includes not : CHttpClient, and will include other classes using libcurl +* 'Net' includes now : Socket, SerialPort,ServerSocket,UdpSocket,DnsClient and NetCode, + that is, basic networking stuff +* 'Net' component splitted it two components: 'Net' and 'Advanced net' + +0.1.4 (STABLE - Gambas 0.80) + +BM - Changes to allow the component compile on systems without MSG_NOSIGNAL flag + +0.1.3 (Gambas 0.74) + +BM - 20 Dec 2003 - Let component compile with gcc 2.95 + +0.1.2 + +* Added HTTP proxy support for 'HttpClient' class + +0.1.1 (Gambas 0.73) + +* Corrected bug in 'HttpClient' class that didn't convert correctly + document query to HTTP codification +* Added support for Solaris + +0.1.0 + +* Added 'HttpClient.Local' constant as sinonym of 'HttpClient.Unix' +* Changed 'HttpClient.Inet' constant to 'HttpClient.Internet' + +0.1.0pre7 + +* Using sys/un.h instead of linux/un.h in 'Socket' and 'ServerSocket' + classes +* Added option 'SO_REUSEADDR' to socket in 'ServerSocket' class +* 'ServerSocket' example fixed +* Documentation fixed + + +0.1.0pre6 + +* Memory allocation bug fixed in 'Socket' class +* Memory allocation bug fixed in 'SerialPort' class +* Lots of internal code reorganization +* Some memory optimizations in 'Socket' and 'ServerSocket' +* UDPServerClient example fixed +* ServerSocket example fixed + +0.1.0pre5 + +* Constant names changed in all classes to be more simple +* Parameters in methods and events does not include its type + as a prefix now +* 'ConnectUnix' and 'ConnectSocket' methods merged in one + method : 'Connect' +* 'ServerSocket' 'SocketType' property changed to 'Type' +* 'ConnectionRequest' event in 'ServerSocket' changed to 'Connection' +* 'Accept' method from 'ServerSocket' does not take any parameter + now +* 'HostFound' events from 'Socket' and 'HttpClient' changed to 'Found' +* Datagram class now inherits from '.Stream' +* Removed 'DataPacket' class +* New properties 'SourceHost', 'SourcePort', 'TargetHost', 'TargetPort' + in 'Datagram' class +* New method 'Peek' in 'Datagram' class +* Removed methods 'Stop','Receive' and 'Send' from 'Datagram' class +* 'Start' method from 'Datagram' Changed to 'Bind' +* 'Datagram' class changed its name to 'UdpSocket' +* Documentation updated +* Examples updated + + + +0.1.0pre4 + +* Removed Close() method from 'Socket' and 'SerialPort' classes, + translated to standard stream methods +* 'LookingHostIP' constant in 'Socket' and 'HttpClient' classes, + changed to 'LookingUpHostIP' +* Examples updated +* Documentation updated + + +0.1.0pre3 + + +* 'Accept' method from 'ServerSocket' changed its way to + act. Now it returns a new 'Socket' and accpets an optional + Event Handler +* Old method 'Receive' from HttpClient, splitted + in two new methods : 'Receive' and 'Peek' +* Now 'Socket' class inherits from '.Stream' +* Now 'SerialPort' class inherits from 'Stream' +* Removed 'Send' and 'Receive' methods from 'Socket' +* Removed 'Send' and 'Receive' methods from 'SerialPort' +* Added 'Peek' method to 'Socket' +* Adaptations from generic stream methods to 'Socket' characteristics +* Adaptations from generic stream methods to 'SerialPort' characteristics +* Documentation updated +* Examples updated + + +0.1.0pre2 + +* Added 0.0.17 = 0.1.0pre1 to CHANGELOG file +* 'SocketError' event from Socket and Datagram now is called 'Error' +* 'Error' codes are now negative values in Status property +* 'Error' events from classes which support it, now takes + zero parameters +* References to class names removed from 'GB.Error()' messages +* Class 'ClientSocket' changed its name to 'Socket' +* 'RemoteHostIP' and 'LocalHostIP' properties from 'Socket' + class changed its name to 'RemoteHost' and 'LocalHost' +* Constants from ServerSocket changed its name "TypeTCP"->"iNet", + "TypeUnix"->"Unix" +* 'DataAvailable' event changed its name to 'Read' in all classes + which supports it. +* 'Wait' method from ServerSocket changed to 'Pause' +* 'Path' property from 'Socket' class now returns IP:Port when + connected using TCP sockets. +* 'CloseSocket' method from 'Socket' class changed to 'Close' +* 'Socket' Class has a new constructor. You can both use no + parameters, or pass a string as parameter, which can be : + 'HostName:Port' or 'HostIP:Port' for TCP connections, or + 'Absolute_Path' for Unix connections. +* 'ServerSocket' Class has a new constructor. You can both use no + parameters, or pass a string and a number as parameter, which can be : + ':Port' for TCP connections, or 'Absolute_Path' for Unix connections. +* 'Datagram' Class has a new constructor. You can both use no + parameters, or pass an integer as Port value to start its + work. +* 'MaxConn' parameter from 'Listen' method if ServerSocket class + now is optional. +* 'SendData' and 'ReceiveData' changed to 'Send' and 'Receive' in all + classes containing that methods. + + +0.0.17 = 0.1.0pre1 + +* Removed prefix 'Is' in constats beginning with that prefix. +* 'GetData' method changed its name to 'ReceiveData' is some + classes +* 'Connected' event from 'ClientSocket' changed its name + to 'Connect'. +* Examples updated. +* HttpClient class finished. +* HttpClient documentation added. + + +0.0.16 + +* Added CHANGELOG file. +* Changed component name from 'networking' to 'net'. +* Changed 'sData' property from DataPacket to 'Data'. +* Documentation updated for DataPacket class. +* Changed example names. +* Constant names and values have changed in ClientSocket, some + new constants have been added. +* ConnectSocket and ConnectUnix methods from ClientSocket + have changed, now they not return any value, as error + codes are managed by "SocketError" event. +* Documentation updated for ClientSocket class. +* Example "ClientSocket" updated. +* DnsClient has two new constants. +* Documentation updated for DnsClient class. +* Some ServerSocket constants have changed its name. +* New Constants added to ServerSocket. +* A new static property 'UnixMaxPath' added to ServerSocket. +* ServerSocket 'Listen' method does not return any value now. +* ServerSocket example updated. +* Documentation updated for ServerSocket class. +* Datagram class has new constants. +* Datagram 'Start' method does not return any value now. +* Datagram 'SocketError' event implemented. +* Documentation updated for Datagram class. +* Example "UDPServerClient" updated. +* SerialPort 'GetData' method now is called 'ReceiveData'. +* Added constants to SerialPort class. +* Documentation updated for SerialPort class. +* SerialPort Example updated. diff --git a/gb.net/INSTALL b/gb.net/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.net/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.net/Makefile.am b/gb.net/Makefile.am new file mode 100644 index 00000000..71cb56ec --- /dev/null +++ b/gb.net/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @NET_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.net/NEWS b/gb.net/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.net/README b/gb.net/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.net/acinclude.m4 b/gb.net/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.net/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.net/component.am b/gb.net/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.net/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.net/configure.ac b/gb.net/configure.ac new file mode 100644 index 00000000..0085d800 --- /dev/null +++ b/gb.net/configure.ac @@ -0,0 +1,19 @@ +dnl ---- configure.ac for gb.net + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-net, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.net) +AC_PROG_LIBTOOL + +GB_COMPONENT( + net, NET, gb.net, [src], + [], + [], + [$C_LIB $THREAD_LIB], + [$THREAD_INC]) + +AC_OUTPUT(Makefile src/Makefile) + +GB_PRINT_MESSAGES diff --git a/gb.net/gambas.h b/gb.net/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.net/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.net/gb_common.h b/gb.net/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.net/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.net/m4 b/gb.net/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.net/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.net/reconf b/gb.net/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.net/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.net/src/CDnsClient.c b/gb.net/src/CDnsClient.c new file mode 100644 index 00000000..e7522872 --- /dev/null +++ b/gb.net/src/CDnsClient.c @@ -0,0 +1,720 @@ +/*************************************************************************** + + CDnsClient.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDNSCLIENT_C + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "main.h" + +#include "CDnsClient.h" +#ifndef _REENTRANT +#define _REENTRANT +#endif + +static void **dns_object=NULL; +static int dns_count=0; +static sem_t dns_th_pipe; +static int dns_r_pipe=-1; +static int dns_w_pipe=-1; +static int dns_async_count=0; /* protected by dns_th_pipe */ + +DECLARE_EVENT(EVENT_Finished); + +int dns_init(void) +{ + int dpipe[2]; + + if (pipe(dpipe)) + return 1; + dns_r_pipe=dpipe[0]; + dns_w_pipe=dpipe[1]; + sem_init(&dns_th_pipe,0,1); + return 0; +} + +void dns_exit(void) +{ + close(dns_r_pipe); + close(dns_w_pipe); + dns_r_pipe=-1; + dns_w_pipe=-1; +} + +/********************************************************** + DnsClient pipe message protocol: + + 1) (void*)DNSCLIENT* + 2) Action : '0' --> dns_get_name, '1' --> dns_get_ip + 3) Result : string finished with '\x10' + *********************************************************/ + +static bool read_dns_pipe(void *data, size_t length) +{ + if (read(dns_r_pipe, data, length) != length) + { + fprintf(stderr, "gb.net: cannot read DNS pipe: %s\n", strerror(errno)); + return TRUE; + } + + return FALSE; +} + +/* Callers hold the dns_th_pipe semaphore as we change dns_async_count! */ +static void dns_start_async(void) +{ + assert(dns_async_count >= 0); + if (!dns_async_count++) + GB.Watch(dns_r_pipe, GB_WATCH_READ, (void *) dns_callback, 0); +} + +/* Callers hold the dns_th_pipe semaphore */ +static void dns_stop_async(void) +{ + if (!--dns_async_count) + GB.Watch(dns_r_pipe, GB_WATCH_NONE, (void *) dns_callback, 0); + assert(dns_async_count >= 0); +} + +void dns_callback(intptr_t lParam) +{ + /*********************************************************** + This function reads a message sent by a thread, and then + raises "Finished" event if necessary, and fills HostName + and HostIP properties. This function will run on the main + thread. + ************************************************************/ + CDNSCLIENT *mythis; + void *v_obj; + char Action[1]; + char BufRead[1]; + char *Buf=NULL; + + int Position; + int myloop; + int test_id; + struct pollfd mypoll; + int idata=1; + if (dns_r_pipe==-1) return; + sem_wait(&dns_th_pipe); + + for(;;) + { + Position=0; + BufRead[0]='\0'; + mypoll.fd=dns_r_pipe; + mypoll.events=POLLIN; + mypoll.revents=0; + idata=poll(&mypoll,1,0); + if (idata <= 0) break; + read_dns_pipe(&v_obj,sizeof(void*)); + read_dns_pipe(&test_id,sizeof(int)); + read_dns_pipe(Action,sizeof(char)); + GB.Alloc(POINTER(&Buf),sizeof(char)); + + while (BufRead[0] != '\x10') + { + read_dns_pipe(BufRead,sizeof(char)); + if (BufRead[0]!='\x10') + { + Buf[Position]=BufRead[0]; + Position++; + GB.Realloc(POINTER(&Buf),(Position+1)*sizeof(char)); + } + else + Buf[Position]='\0'; + } + Position=-1; + for (myloop=0;myloop=0) + { + mythis=(CDNSCLIENT*)v_obj; + if (mythis->iStatus && (mythis->i_id==test_id)) + { + if (Action[0]=='1') + { + GB.FreeString(&mythis->sHostIP); + mythis->sHostIP = GB.NewZeroString(Buf); + } + else + { + GB.FreeString(&mythis->sHostName); + mythis->sHostName = GB.NewZeroString(Buf); + } + mythis->iStatus=0; + if (mythis->finished_callback) + { + //GB.Ref(mythis); + GB.Post(mythis->finished_callback, (intptr_t)mythis->CliParent); + } + else + { + GB.Ref(mythis); + GB.Post((void (*)())dns_event, (intptr_t)mythis); + } + } + /* Release one DnsClient in async mode */ + if (mythis->iAsync) + dns_stop_async(); + } + GB.Free(POINTER(&Buf)); + } + + sem_post(&dns_th_pipe); +} +/************************************************** + To raise an event + **************************************************/ + +void dns_event(CDNSCLIENT *mythis) +{ + GB.Raise(mythis,EVENT_Finished,0); + GB.Unref(POINTER(&mythis)); +} + +static void write_dns_pipe(void *data, size_t length) +{ + if (write(dns_w_pipe, data, length) != length) + fprintf(stderr, "gb.net: cannot write to DNS pipe: %s\n", strerror(errno)); +} + +void* dns_get_name(void* v_obj) +{ + /**************************************************************** + This function will run in a thread different from main thread, + and when it finish its proccess, it sends a message to the + main thread using a pipe + *****************************************************************/ + char Buf[1]; + int myid; + int res; + CDNSCLIENT *mythis; + + char host[1024]; + struct sockaddr_in sa; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); + Buf[0]='0'; + + mythis=(CDNSCLIENT*)v_obj; + sem_wait(&mythis->sem_id); + myid=mythis->i_id; + sem_post(&mythis->sem_id); + + + //((struct sockaddr*)&sa)->sa_family=AF_INET; + sa.sin_family = AF_INET; + bzero(host,1024); + sa.sin_port=0; + inet_aton(mythis->sHostIP, &sa.sin_addr); + + res=getnameinfo((struct sockaddr*)&sa,sizeof(struct sockaddr_in),host,1024*sizeof(char),NULL,0,NI_NAMEREQD); + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); + sem_wait(&dns_th_pipe); + write_dns_pipe(&v_obj,sizeof(void*)); /* object */ + write_dns_pipe(&myid,sizeof(int)); /* id */ + write_dns_pipe(Buf,sizeof(char)); /* Action */ + if (!res) write_dns_pipe(host,strlen(host)*sizeof(char)); + write_dns_pipe("\x10",sizeof(char)); + sem_post(&dns_th_pipe); + return NULL; +} + + +/**************************************************************** +This function will run in a thread different from main thread, +and when it finish its proccess, it sends a message to the +main thread using a pipe +*****************************************************************/ + +void* dns_get_ip(void* v_obj) +{ + char Buf[1]; + char *BufData; + int myid; + struct addrinfo *stHost; + struct sockaddr_in *addr; + CDNSCLIENT *mythis; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); + + Buf[0]='1'; + + mythis=(CDNSCLIENT*)v_obj; + sem_wait(&mythis->sem_id); + myid=mythis->i_id; + sem_post(&mythis->sem_id); + + if (getaddrinfo(mythis->sHostName,NULL,NULL,&stHost)) stHost=NULL; + if (stHost) if ( stHost[0].ai_family!=PF_INET ) stHost=NULL; + + sem_wait(&dns_th_pipe); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); + + write_dns_pipe(&v_obj, sizeof(void*)); /* object */ + write_dns_pipe(&myid, sizeof(int)); /* id */ + write_dns_pipe(Buf, sizeof(char)); /* action */ + + if (stHost!=NULL) + { + addr=(struct sockaddr_in*)stHost[0].ai_addr; + BufData=inet_ntoa(addr->sin_addr); + if (BufData) write_dns_pipe(BufData,strlen(BufData)*sizeof(char)); + } + write_dns_pipe("\x10",sizeof(char)); + sem_post(&dns_th_pipe); + return NULL; +} + + +void dns_close_all(CDNSCLIENT *mythis) +{ + if (mythis->iStatus) + { + pthread_cancel(mythis->th_id); /* cancel thread */ + pthread_join(mythis->th_id,NULL); + sem_destroy(&mythis->sem_id); + mythis->iStatus=0; /* inactive */ + dns_callback(dns_r_pipe); /* freeing pipe data */ + + } +} +int dns_thread_getname(CDNSCLIENT *mythis) +{ + sem_wait(&mythis->sem_id); + mythis->i_id++; + sem_post(&mythis->sem_id); + + mythis->iStatus=1; + /* We need to register the watch in the main thread to not anger qt */ + sem_wait(&dns_th_pipe); + dns_start_async(); + sem_post(&dns_th_pipe); + + if (pthread_create(&mythis->th_id,NULL,dns_get_name,(void*)mythis) ) + { + mythis->iStatus=0; + return 1; + } + pthread_detach(mythis->th_id); + return 0; +} + +int dns_thread_getip(CDNSCLIENT *mythis) +{ + sem_wait(&mythis->sem_id); + mythis->i_id++; + sem_post(&mythis->sem_id); + + mythis->iStatus=1; + sem_wait(&dns_th_pipe); + dns_start_async(); + sem_post(&dns_th_pipe); + + if (pthread_create(&mythis->th_id,NULL,dns_get_ip,(void*)mythis) ) + { + mythis->iStatus=0; + return 1; + } + pthread_detach(mythis->th_id); + return 0; +} + +void dns_set_async_mode(int myval, CDNSCLIENT *mythis) +{ + mythis->iAsync = myval; +} + +/************************************************************************************* +###################################################################################### + --------------------- DNSCLIENT GAMBAS INTERFACE IMPLEMENTATION ----------------- +###################################################################################### +**************************************************************************************/ + +/********************************************* + This property gets/sets the name of a Host + *********************************************/ +BEGIN_PROPERTY ( HostName ) + + if (READ_PROPERTY) + { + if (THIS->iStatus){ GB.ReturnString(NULL); return; } + GB.ReturnString(THIS->sHostName); + return; + } + + if (THIS->iStatus) { GB.Error("HostIP can not be changed while working"); return;} + GB.FreeString(&THIS->sHostName); + GB.StoreString(PROP(GB_STRING), &THIS->sHostName); + +END_PROPERTY + +/************************************************* + This property gets/sets the IP address of a Host + *************************************************/ +BEGIN_PROPERTY ( HostIP ) + + if (READ_PROPERTY) + { + if (THIS->iStatus) { GB.ReturnString(NULL); return; } + GB.ReturnString(THIS->sHostIP); + return; + } + + if (THIS->iStatus) { GB.Error("HostIP can not be changed while working"); return; } + GB.FreeString(&THIS->sHostIP); + GB.StoreString(PROP(GB_STRING), &THIS->sHostIP); + +END_PROPERTY + +/************************************************* + This property gets/sets asynchronous state: + + FALSE : Synchronous + TRUE : Asynchronous + *************************************************/ + + +BEGIN_PROPERTY ( CDNSCLIENT_Async ) + + + if (READ_PROPERTY) + { + GB.ReturnBoolean(THIS->iAsync); + return; + } + THIS->iAsync = VPROP(GB_BOOLEAN); + +END_PROPERTY + +/********************************************************** + This property gets status : 0 --> Inactive, 1 --> Working + **********************************************************/ +BEGIN_PROPERTY ( CDNSCLIENT_Status ) + + GB.ReturnInteger(THIS->iStatus); + +END_PROPERTY +/************************************************* + Gambas object "Constructor" + *************************************************/ +BEGIN_METHOD_VOID(CDNSCLIENT_new) + + THIS->CliParent=NULL; + THIS->finished_callback=NULL; + THIS->sHostIP=NULL; + THIS->sHostName=NULL; + THIS->iStatus=0; + THIS->iAsync=0; + THIS->i_id=0; + sem_init(&THIS->sem_id,0,1); + dns_count++; + if (dns_object==NULL) + GB.Alloc(POINTER(&dns_object),sizeof(void*)); + else + GB.Realloc(POINTER(&dns_object),dns_count*sizeof(void*)); + + dns_object[dns_count-1]=(void*)THIS; + +END_METHOD + +/************************************************* + Gambas object "Destructor" + *************************************************/ +BEGIN_METHOD_VOID(CDNSCLIENT_free) + + int myloop; + int Position=-1; + dns_close_all(THIS); + GB.FreeString(&THIS->sHostIP); + GB.FreeString(&THIS->sHostName); + for (myloop=0;myloop=0) + { + for (myloop=Position;myloop< (dns_count-1);myloop++) + { + dns_object[myloop]=dns_object[myloop+1]; + } + dns_count--; + if (!dns_count) + GB.Free(POINTER(&dns_object)); + } + +END_METHOD + +/************************************************* + To cancel an asynchronous operation + *************************************************/ +BEGIN_METHOD_VOID(CDNSCLIENT_Stop) + + dns_close_all(THIS); + +END_METHOD +/***************************************************************************** + This method takes the Host IP, which was stored using HostName property, + and trasnlates it to IP address + *****************************************************************************/ +BEGIN_METHOD_VOID(CDNSCLIENT_GetHostName) + + struct hostent *stHost=NULL; + struct in_addr addr; + + + if (THIS->iStatus) + { + GB.Error("Object is already working"); + return; + } + + if (THIS->sHostIP) + { + if ( THIS->iAsync) + { /* Asynchronous mode */ + sem_wait(&THIS->sem_id); + THIS->i_id++; + sem_post(&THIS->sem_id); + THIS->iStatus=1; + if (dns_thread_getname(THIS)) + { + GB.Error("No resources available to create a thread"); + return; + } + } + else + { /* Synchronous mode */ + inet_aton(THIS->sHostIP,&addr); +#ifdef __sun__ + stHost=gethostbyaddr((const char *)&addr,sizeof(addr),AF_INET); +#else + stHost=gethostbyaddr(&addr,sizeof(addr),AF_INET); +#endif + if (stHost==NULL) + { + GB.FreeString(&THIS->sHostName); + } + else + { + GB.FreeString(&THIS->sHostName); + THIS->sHostName = GB.NewZeroString(stHost->h_name); + } + GB.Raise((void*)THIS,EVENT_Finished,0); + } + } + else + { + GB.FreeString(&THIS->sHostName); + } + + +END_METHOD + +/***************************************************************************** + This method takes the Host IP, which was stored using HostIP property, + and trasnlates it to host name + *****************************************************************************/ +BEGIN_METHOD_VOID(CDNSCLIENT_GetHostIP) + + struct hostent *stHost=NULL; + if (THIS->iStatus) + { + GB.Error("Object is already working"); + return; + } + + + if (THIS->sHostName) + { + if ( THIS->iAsync ) + { /* Asynchronous mode */ + + sem_wait(&THIS->sem_id); + THIS->i_id++; + sem_post(&THIS->sem_id); + THIS->iStatus=1; + if (dns_thread_getip(THIS)) + { + GB.Error("No resource available to create a thread"); + return; + } + } + else + { /* Synchronous mode */ + stHost=gethostbyname(THIS->sHostName); + if (stHost==NULL) + { + GB.FreeString(&THIS->sHostIP); + } + else + { + GB.FreeString(&THIS->sHostIP); + THIS->sHostIP = GB.NewZeroString(inet_ntoa(*((struct in_addr*)stHost->h_addr))); + } + GB.Raise((void*)THIS,EVENT_Finished,0); + } + } + else + { + GB.FreeString(&THIS->sHostIP); + } + + + +END_METHOD + + +/*************************************************************** + Here we declare the public interface of DnsClient class + ***************************************************************/ +GB_DESC CDnsClientDesc[] = +{ + + GB_DECLARE("DnsClient", sizeof(CDNSCLIENT)), + + GB_EVENT("Finished", NULL, NULL, &EVENT_Finished), + + GB_METHOD("_new", NULL, CDNSCLIENT_new, NULL), + GB_METHOD("_free", NULL, CDNSCLIENT_free, NULL), + GB_METHOD("Stop", NULL, CDNSCLIENT_Stop, NULL), + GB_METHOD("GetHostName", NULL, CDNSCLIENT_GetHostName, NULL), + GB_METHOD("GetHostIP", NULL, CDNSCLIENT_GetHostIP, NULL), + + GB_PROPERTY("HostName", "s", HostName), + GB_PROPERTY("HostIP", "s", HostIP), + GB_PROPERTY("Async", "b", CDNSCLIENT_Async), + GB_PROPERTY_READ("Status", "i", CDNSCLIENT_Status), + + GB_CONSTANT("_IsControl", "b", TRUE), + GB_CONSTANT("_IsVirtual", "b", TRUE), + GB_CONSTANT("_Group", "s", "Network"), + GB_CONSTANT("_Properties", "s", "HostName,HostIP,Async"), + GB_CONSTANT("_DefaultEvent", "s", "Finished"), + + GB_END_DECLARE +}; + + +/****************************************************************************** + +I do not know if Solaris accepts getaddrinfo and getnameinfo, so here +is the old implementation for that O.S. + +*******************************************************************************/ +/* +void* dns_get_ip(void* v_obj) +{ + char Buf[1]; + char *BufData; + int myid; + int herr; + + struct hostent hostbuf, *stHost; + size_t hstbuflen; + char tmphstbuf[1024]; + CDNSCLIENT *mythis; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); + hstbuflen=1024; + Buf[0]='1'; + + mythis=(CDNSCLIENT*)v_obj; + sem_wait(&mythis->sem_id); + myid=mythis->i_id; + sem_post(&mythis->sem_id); + + stHost=gethostbyname_r (mythis->sHostName, &hostbuf, tmphstbuf, hstbuflen, &herr); + + sem_wait(&dns_th_pipe); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); + write (dns_w_pipe,&v_obj,sizeof(void*)); + write (dns_w_pipe,&myid,sizeof(int)); + write (dns_w_pipe,Buf,sizeof(char)); + if (stHost!=NULL) + { + BufData=inet_ntoa(*( (struct in_addr*)stHost->h_addr )); + write (dns_w_pipe,BufData,strlen(BufData)*sizeof(char)); + } + write (dns_w_pipe,"\x10",sizeof(char)); + sem_post(&dns_th_pipe); + return NULL; +} + + +void* dns_get_name(void* v_obj) +{ + + char Buf[1]; + int myid; + int herr; + size_t hstbuflen; + char tmphstbuf[2048]; + struct hostent hostbuf,*stHost=NULL; + CDNSCLIENT *mythis; + struct in_addr addr; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); + hstbuflen=2048; + Buf[0]='0'; + + mythis=(CDNSCLIENT*)v_obj; + sem_wait(&mythis->sem_id); + myid=mythis->i_id; + sem_post(&mythis->sem_id); + inet_aton(mythis->sHostIP,&addr); + stHost=gethostbyaddr_r((const char *)&addr, sizeof (addr), AF_INET, &hostbuf,tmphstbuf,hstbuflen,&herr); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); + sem_wait(&dns_th_pipe); + write (dns_w_pipe,&v_obj,sizeof(void*)); + write (dns_w_pipe,&myid,sizeof(int)); + write (dns_w_pipe,Buf,sizeof(char)); + if (stHost!=NULL) write (dns_w_pipe,stHost->h_name,strlen(stHost->h_name)*sizeof(char)); + write (dns_w_pipe,"\x10",sizeof(char)); + sem_post(&dns_th_pipe); + return NULL; +} +*/ diff --git a/gb.net/src/CDnsClient.h b/gb.net/src/CDnsClient.h new file mode 100644 index 00000000..6b6734b9 --- /dev/null +++ b/gb.net/src/CDnsClient.h @@ -0,0 +1,68 @@ +/*************************************************************************** + + CDnsClient.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDNSCLIENT_H +#define __CDNSCLIENT_H + +#include +#include +#include "gambas.h" + +#ifndef __CDNSCLIENT_C + +extern GB_DESC CDnsClientDesc[]; + +#else + +#define THIS ((CDNSCLIENT *)_object) + +#endif + +typedef + struct + { + GB_BASE ob; + char* sHostName; + char* sHostIP; + int iStatus; + int iAsync; + int i_id; + pthread_t th_id; + sem_t sem_id; + void (*finished_callback)(void*); + void *CliParent; + } + CDNSCLIENT; + +int dns_init(void); +void dns_exit(void); + +void dns_callback(intptr_t lParam); +void* dns_get_name(void* v_obj); +void* dns_get_ip(void* v_obj); +void dns_event(CDNSCLIENT *mythis); +void dns_close_all(CDNSCLIENT *mythis); +int dns_thread_getname(CDNSCLIENT *mythis); +int dns_thread_getip(CDNSCLIENT *mythis); +void dns_set_async_mode(int myval, CDNSCLIENT *mythis); +#endif diff --git a/gb.net/src/CNet.c b/gb.net/src/CNet.c new file mode 100644 index 00000000..c3499a1c --- /dev/null +++ b/gb.net/src/CNet.c @@ -0,0 +1,160 @@ +/*************************************************************************** + + CNet.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CNET_C +#include "main.h" +#include +#include + +#include "CNet.h" + + +size_t NET_get_address_size(NET_ADDRESS *addr) +{ + switch (addr->a.sa_family) + { + case PF_UNIX: return sizeof(struct sockaddr_un); + case PF_INET: return sizeof(struct sockaddr_in); + default: return 0; + } +} + +void ToIPv4(char *src,char *dst,int leadzero) +{ + int myloop; + int zone=0; + int np=0; + int nc[4]={0,0,0,0}; + + dst[0]=0; + + if (!src) return; + + for (myloop=0;myloop57) ) + { + if (src[myloop]=='.') { if (++np > 3) return; } + else { return; } + } + else + { + nc[np]*=10; + nc[np]+=(src[myloop]-48); + if (nc[np]>255) return; + } + } + break; + + case 2: + if (src[myloop]!=' ') return; + break; + + } + } + + if (!leadzero) + sprintf(dst,"%d.%d.%d.%d",nc[0],nc[1],nc[2],nc[3]); + else + sprintf(dst,"%03d.%03d.%03d.%03d",nc[0],nc[1],nc[2],nc[3]); +} + +BEGIN_METHOD(CNET_Format,GB_STRING IpString;GB_INTEGER Format;GB_BOOLEAN LeadZero;) + + int leadzero=0; + + char dst[16]; + + if (!MISSING(Format)) + if (VARG(Format)!=0) { GB.Error("Unknown Format"); return; } + + if (!MISSING(LeadZero)) leadzero=VARG(LeadZero); + if (!LENGTH(IpString)) return; + + ToIPv4 (STRING(IpString),dst,leadzero); + GB.ReturnNewZeroString(dst); + + +END_METHOD + +/*************************************************************** +Here we declare the public interface of NetCode class +***************************************************************/ +GB_DESC CNetDesc[] = +{ + GB_DECLARE("Net", 0), GB_VIRTUAL_CLASS(), + + /* IP Formatting */ + GB_CONSTANT("IPv4","i",0), + /* normal operation */ + GB_CONSTANT("Inactive", "i", NET_INACTIVE), + GB_CONSTANT("Active", "i", NET_ACTIVE), + GB_CONSTANT("Pending", "i", NET_PENDING), + GB_CONSTANT("Accepting", "i", NET_ACCEPTING), + GB_CONSTANT("ReceivingData", "i", NET_RECEIVING_DATA), + GB_CONSTANT("Searching", "i", NET_SEARCHING), + GB_CONSTANT("Connecting", "i", NET_CONNECTING), + GB_CONSTANT("Connected", "i", NET_CONNECTED), + /* net error codes */ + GB_CONSTANT("CannotCreateSocket", "i", NET_CANNOT_CREATE_SOCKET), + GB_CONSTANT("ConnectionRefused", "i", NET_CONNECTION_REFUSED), + GB_CONSTANT("CannotRead", "i", NET_CANNOT_READ), + GB_CONSTANT("CannotWrite", "i", NET_CANNOT_WRITE), + GB_CONSTANT("HostNotFound", "i", NET_HOST_NOT_FOUND), + GB_CONSTANT("CannotBindSocket", "i", NET_CANNOT_BIND_SOCKET), + GB_CONSTANT("CannotListen", "i", NET_CANNOT_LISTEN), + GB_CONSTANT("CannotBindInterface", "i", NET_CANNOT_BIND_INTERFACE), + GB_CONSTANT("CannotAuthenticate", "i", NET_CANNOT_AUTHENTICATE), + /* SeverSocket, type */ + GB_CONSTANT("Internet", "i", NET_TYPE_INTERNET), + GB_CONSTANT("Local", "i", NET_TYPE_LOCAL), + GB_CONSTANT("Unix", "i", NET_TYPE_LOCAL), + + // Max path length for local sockets + //GB_CONSTANT("MaxPathLength", "i", NET_UNIX_PATH_MAX), + + GB_STATIC_METHOD("Format", "s", CNET_Format, "(IpString)s[(Format)i(LeadZero)b]"), + + GB_END_DECLARE +}; + + + + diff --git a/gb.net/src/CNet.h b/gb.net/src/CNet.h new file mode 100644 index 00000000..b0a12467 --- /dev/null +++ b/gb.net/src/CNet.h @@ -0,0 +1,85 @@ +/*************************************************************************** + + CNet.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CNET_H +#define __CNET_H + +#include "gambas.h" + +#include +#include +#include +#include +#include + +#ifndef __CNET_C + +extern GB_DESC CNetDesc[]; + +#else + +#define THIS ((CNET *)_object) + +#endif + +typedef + union + { + struct sockaddr a; + struct sockaddr_in in; + struct sockaddr_un un; + } + NET_ADDRESS; + +#define NET_UNIX_PATH_MAX 108 + +enum +{ + NET_INACTIVE = 0, + NET_ACTIVE = 1, + NET_PENDING = 2, + NET_ACCEPTING = 3, + NET_RECEIVING_DATA = 4, + NET_SEARCHING = 5, + NET_CONNECTING = 6, + NET_CONNECTED = 7, + NET_CANNOT_CREATE_SOCKET = -2, + NET_CONNECTION_REFUSED = -3, + NET_CANNOT_READ = -4, + NET_CANNOT_WRITE = -5, + NET_HOST_NOT_FOUND = -6, + NET_CANNOT_BIND_SOCKET = -10, + NET_CANNOT_LISTEN = -14, + NET_CANNOT_BIND_INTERFACE = -15, + NET_CANNOT_AUTHENTICATE = -16 // For gb.net.pop3 +}; + +enum +{ + NET_TYPE_LOCAL = 0, + NET_TYPE_INTERNET = 1 +}; + +size_t NET_get_address_size(NET_ADDRESS *addr); + +#endif diff --git a/gb.net/src/CSerialPort.c b/gb.net/src/CSerialPort.c new file mode 100644 index 00000000..338ab33f --- /dev/null +++ b/gb.net/src/CSerialPort.c @@ -0,0 +1,752 @@ +/*************************************************************************** + + CSerialPort.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSERIALPORT_C + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __CYGWIN__ +/* Cygwin defines FIONREAD in . */ +#include +/* TIOCOUTQ is not implemented on Cygwin */ +#define TIOCOUTQ ((unsigned int) -1) +#endif /* __CYGWIN__ */ + +#ifndef TIOCINQ +#define TIOCINQ FIONREAD +#endif + +#include "main.h" +#include "tools.h" + +#include "CSerialPort.h" + +#define MAX_SERIAL_BUFFER_SIZE 65536 + + +GB_STREAM_DESC SerialStream = +{ + open: CSerialPort_stream_open, + close: CSerialPort_stream_close, + read: CSerialPort_stream_read, + write: CSerialPort_stream_write, + seek: CSerialPort_stream_seek, + tell: CSerialPort_stream_tell, + flush: CSerialPort_stream_flush, + handle: CSerialPort_stream_handle +}; + +DECLARE_EVENT(EVENT_Read); +DECLARE_EVENT(EVENT_DTR); +DECLARE_EVENT(EVENT_DSR); +DECLARE_EVENT(EVENT_RTS); +DECLARE_EVENT(EVENT_CTS); +DECLARE_EVENT(EVENT_DCD); +DECLARE_EVENT(EVENT_RNG); + +static SERIAL_SIGNAL get_signals(CSERIALPORT *_object) +{ + int ist = 0; + SERIAL_SIGNAL signals = { 0 }; + + ioctl(THIS->port, TIOCMGET, &ist); + + signals.DSR = (ist & TIOCM_DSR) != 0; + signals.DTR = (ist & TIOCM_DTR) != 0; + signals.RTS = (ist & TIOCM_RTS) != 0; + signals.CTS = (ist & TIOCM_CTS) != 0; + signals.DCD = (ist & TIOCM_CAR) != 0; + signals.RNG = (ist & TIOCM_RNG) != 0; + + return signals; +} + +static void raise_event(CSERIALPORT *_object, intptr_t event) +{ + int val = 0; + + if (event == EVENT_DSR) + val = THIS->signals.DSR; + else if (event == EVENT_DTR) + val = THIS->signals.DTR; + else if (event == EVENT_RTS) + val = THIS->signals.RTS; + else if (event == EVENT_CTS) + val = THIS->signals.CTS; + else if (event == EVENT_DCD) + val = THIS->signals.DCD; + else if (event == EVENT_RNG) + val = THIS->signals.RNG; + + GB.Raise(THIS, (int)event, 1, GB_T_BOOLEAN, val); + GB.Unref(POINTER(&_object)); +} + +#define CHECK_SIGNAL(_signal) \ +if (THIS->signals._signal != new_signals._signal) \ +{ \ + THIS->signals._signal = new_signals._signal; \ + GB.Ref(THIS); \ + GB.Post2(raise_event, (intptr_t)THIS, (intptr_t)EVENT_##_signal); \ +} + + +static int cb_change(intptr_t _object) +{ + SERIAL_SIGNAL new_signals; + //struct timespec mywait; + + // Just sleeping a bit to reduce CPU waste + //mywait.tv_sec = 0; + //mywait.tv_nsec = 1000000; // 1 ms + //nanosleep(&mywait, NULL); + + /* Serial port signals status */ + new_signals = get_signals(THIS); + + CHECK_SIGNAL(DSR); + CHECK_SIGNAL(DTR); + CHECK_SIGNAL(RTS); + CHECK_SIGNAL(CTS); + CHECK_SIGNAL(DCD); + CHECK_SIGNAL(RNG); + + return FALSE; +} + +static void cb_read(int fd, int type, CSERIALPORT *_object) +{ + GB.Raise(THIS, EVENT_Read, 0); +} + +static void assign_callback(CSERIALPORT *_object, int polling) +{ + int port = THIS->port; + + if (GB.CanRaise(THIS, EVENT_Read)) + GB.Watch(port, GB_WATCH_READ, (void *)cb_read, (intptr_t)THIS); + + if (GB.CanRaise(THIS, EVENT_DTR) + || GB.CanRaise(THIS, EVENT_CTS) + || GB.CanRaise(THIS, EVENT_DCD) + || GB.CanRaise(THIS, EVENT_DSR) + || GB.CanRaise(THIS, EVENT_RNG) + || GB.CanRaise(THIS, EVENT_RTS)) + { + // Polling is 50ms by default + THIS->every = GB.Every(polling, cb_change, (intptr_t)THIS); + } +} + +static void release_callback(CSERIALPORT *_object) +{ + GB.Watch(THIS->port, GB_WATCH_NONE, 0, 0); +} + +static void close_serial_port(CSERIALPORT *_object) +{ + if (THIS->every) + { + GB.Unref(POINTER(&THIS->every)); + THIS->every = NULL; + } + + if (THIS->status) + { + release_callback(THIS); + THIS->stream.desc = NULL; + CloseSerialPort(THIS->port, &THIS->oldtio); + THIS->status = 0; + } +} + +/**************************************************************************** + + Stream implementation + +****************************************************************************/ + +int CSerialPort_stream_open(GB_STREAM *stream, const char *path, int mode, void *data) +{ + return -1; /* not allowed */ +} + +int CSerialPort_stream_seek(GB_STREAM *stream, int64_t pos, int whence) +{ + return -1; /* not allowed */ +} + +int CSerialPort_stream_tell(GB_STREAM *stream, int64_t *pos) +{ + return -1; /* not allowed */ +} + +int CSerialPort_stream_flush(GB_STREAM *stream) +{ + void *_object = stream->tag; + return tcdrain(THIS->port); +} + +int CSerialPort_stream_handle(GB_STREAM *stream) +{ + void *_object = stream->tag; + return THIS->port; /* OK */ +} + +int CSerialPort_stream_close(GB_STREAM *stream) +{ + void *_object = stream->tag; + + if (!_object) + return -1; + + close_serial_port(THIS); + return 0; +} + +/*int CSerialPort_stream_lof(GB_STREAM *stream, int64_t *len) +{ + void *_object = stream->tag; + int bytes; + + *len=0; + if (!_object) return -1; + + if (ioctl(THIS->port,FIONREAD,&bytes)) return -1; + *len = bytes; + return 0; +} + +int CSerialPort_stream_eof(GB_STREAM *stream) +{ + void *_object = stream->tag; + int bytes; + + if (!_object) return -1; + + if (ioctl(THIS->port,FIONREAD,&bytes)) return -1; + if (!bytes) return -1; + return 0; +}*/ + +int CSerialPort_stream_read(GB_STREAM *stream, char *buffer, int len) +{ + void *_object = stream->tag; + return read(THIS->port, (void*)buffer, len); +} + +/*int CSerialPort_stream_read(GB_STREAM *stream, char *buffer, int len) +{ + void *_object = stream->tag; + int npos = -1; + int no_block = 0; + int bytes; + + if (!_object) + return -1; + + if (ioctl(THIS->port, FIONREAD, &bytes)) + return -1; + + if (bytes < len) + return -1; + + ioctl(THIS->port, FIONBIO, &no_block); + + npos = read(THIS->port, (void*)buffer, len); + + no_block++; + ioctl(THIS->port, FIONBIO, &no_block); + + if (npos != len) + return -1; + + return 0; +}*/ + +int CSerialPort_stream_write(GB_STREAM *stream, char *buffer, int len) +{ + void *_object = stream->tag; + return write(THIS->port, (void*)buffer, len); +} + + +/*int CSerialPort_stream_write(GB_STREAM *stream, char *buffer, int len) +{ + void *_object = stream->tag; + int npos = -1; + int no_block = 0; + + if (!_object) + return -1; + + ioctl(THIS->port, FIONBIO, &no_block); + + npos = write(THIS->port, (void*)buffer, len); + + no_block++; + ioctl(THIS->port, FIONBIO, &no_block); + + if (npos < 0) + return -1; + + return 0; +}*/ + +static bool check_open(CSERIALPORT *_object) +{ + if (!THIS->status) + { + GB.Error("Port is closed"); + return TRUE; + } + else + return FALSE; +} + +static bool check_close(CSERIALPORT *_object) +{ + if (THIS->status) + { + GB.Error("Port must be closed first"); + return TRUE; + } + else + return FALSE; +} + +/**************************************************************************** + + SerialPort + +****************************************************************************/ + +BEGIN_PROPERTY(SerialPort_Status) + + GB.ReturnInteger(THIS->status); + +END_PROPERTY + +// Data Set Ready + +BEGIN_PROPERTY(SerialPort_DSR) + + if (!THIS->status) + GB.ReturnBoolean(0); + else + { + THIS->signals = get_signals(THIS); + GB.ReturnBoolean(THIS->signals.DSR); + } + +END_PROPERTY + +// Data Transmission Ready + +BEGIN_PROPERTY(SerialPort_DTR) + + int ist; + + if (READ_PROPERTY) + { + if (!THIS->status) + GB.ReturnBoolean(0); + else + { + THIS->signals = get_signals(THIS); + GB.ReturnBoolean(THIS->signals.DTR); + } + } + else + { + if (check_open(THIS)) + return; + + ioctl(THIS->port, TIOCMGET, &ist); + if (!VPROP(GB_BOOLEAN)) + ist &= ~TIOCM_DTR; + else + ist = ist | TIOCM_DTR; + ioctl(THIS->port, TIOCMSET, &ist); + } + +END_PROPERTY + +// Ready to send + +BEGIN_PROPERTY(SerialPort_RTS) + + int ist; + + if (READ_PROPERTY) + { + if (!THIS->status) + GB.ReturnBoolean(0); + else + { + THIS->signals = get_signals(THIS); + GB.ReturnBoolean(THIS->signals.RTS); + } + } + else + { + if (check_open(THIS)) + return; + + ioctl(THIS->port, TIOCMGET, &ist); + if(!VPROP(GB_BOOLEAN)) + ist &= ~TIOCM_RTS; + else + ist = ist | TIOCM_RTS; + ioctl(THIS->port, TIOCMSET, &ist); + } + +END_PROPERTY + +// Clear to send + +BEGIN_PROPERTY(SerialPort_CTS) + + if (!THIS->status) + GB.ReturnBoolean(0); + else + { + THIS->signals = get_signals(THIS); + GB.ReturnBoolean(THIS->signals.CTS); + } + +END_PROPERTY + +// Data Carrier Detect + +BEGIN_PROPERTY(SerialPort_DCD) + + if (!THIS->status) + GB.ReturnBoolean(0); + else + { + THIS->signals = get_signals(THIS); + GB.ReturnBoolean(THIS->signals.DCD); + } + +END_PROPERTY + +// Ring + +BEGIN_PROPERTY(SerialPort_RNG) + + if (!THIS->status) + GB.ReturnBoolean(0); + else + { + THIS->signals = get_signals(THIS); + GB.ReturnBoolean(THIS->signals.RNG); + } + +END_PROPERTY + +// Gets / Sets serial port name (on Linux /dev/ttyS0, etc) + +BEGIN_PROPERTY(SerialPort_Port) + + if (READ_PROPERTY) + GB.ReturnString(THIS->portName); + else + { + if (check_close(THIS)) + return; + GB.StoreString(PROP(GB_STRING), &THIS->portName); + } + +END_PROPERTY + +// FlowControl : 1->CRTSCTS , 2-> XON/XOFF , 3-> XON/OFF plus CRTSCTS, 0 --> NONE + +BEGIN_PROPERTY(SerialPort_FlowControl) + + int flow; + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->flow); + else + { + if (check_close(THIS)) + return; + + flow = VPROP(GB_INTEGER); + if (flow < 0 || flow > 3) + { + GB.Error("Invalid flow control value"); + return; + } + + THIS->flow = VPROP(GB_INTEGER); + } + +END_PROPERTY + +// Gets / Sets serial parity (E,O,N) + +BEGIN_PROPERTY(SerialPort_Parity) + + int parity; + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->parity); + else + { + if (check_close(THIS)) + return; + + parity = VPROP(GB_INTEGER); + if (parity < 0 || parity > 2) + { + GB.Error("Invalid parity"); + return; + } + + THIS->parity = parity; + } + +END_PROPERTY + +// Gets / Sets serial port Speed + +BEGIN_PROPERTY(SerialPort_Speed) + + int speed; + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->speed); + else + { + if (check_close(THIS)) + return; + + speed = VPROP(GB_INTEGER); + + if (ConvertBaudRate(speed) == -1) + GB.Error("Invalid speed value"); + else + THIS->speed = speed; + } + +END_PROPERTY + +// Gets / Sets serial port Data Bits + +BEGIN_PROPERTY(SerialPort_DataBits) + + int value; + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->dataBits); + else + { + if (check_close(THIS)) + return; + + value = VPROP(GB_INTEGER); + + if (ConvertDataBits(value) == -1) + GB.Error("Invalid data bits value"); + else + THIS->dataBits = value; + } + +END_PROPERTY + +// Gets / Sets serial port Stop Bits + +BEGIN_PROPERTY(SerialPort_StopBits) + + int value; + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->stopBits); + else + { + if (check_close(THIS)) + return; + + value = VPROP(GB_INTEGER); + + if (ConvertStopBits(value) == -1) + GB.Error("Invalid stop bits value"); + else + THIS->stopBits = value; + } + +END_PROPERTY + +// Gambas object "Constructor" + +BEGIN_METHOD_VOID(SerialPort_new) + + THIS->portName = GB.NewZeroString("/dev/ttyS0"); + THIS->speed = 19200; + THIS->parity = 0; + THIS->dataBits = 8; + THIS->stopBits = 1; + THIS->flow = 1; + +END_METHOD + +// Gambas object "Destructor" + +BEGIN_METHOD_VOID(SerialPort_free) + + close_serial_port(THIS); + GB.FreeString(&THIS->portName); + +END_METHOD + +// To open the port + +BEGIN_METHOD(SerialPort_Open, GB_INTEGER polling) + + int err; + char buffer[8]; + int polling = VARGOPT(polling, 50); + + if (THIS->status) + { + GB.Error("Port is already opened"); + return; + } + + if ((err = OpenSerialPort(&THIS->port, THIS->flow, &THIS->oldtio, THIS->portName, THIS->speed, THIS->parity, THIS->dataBits, THIS->stopBits))) + { + sprintf(buffer, "#%d", err); + GB.Error("Cannot open serial port (&1)", buffer); + return; + } + + THIS->signals = get_signals(THIS); + THIS->stream.desc = &SerialStream; + THIS->stream.tag = THIS; + assign_callback(THIS, polling); + + THIS->status = 1; + +END_METHOD + +BEGIN_PROPERTY(SerialPort_InputBufferSize) + + int ret = 0; + + if (THIS->status) + { + if (ioctl(THIS->port, TIOCINQ, &ret)) + GB.Error("Unable to read input buffer size: &1", strerror(errno)); + } + + GB.ReturnInteger(ret); + +END_PROPERTY + +BEGIN_PROPERTY(SerialPort_OutputBufferSize) + + int ret = 0; + + if (THIS->status) + { + if (ioctl(THIS->port, TIOCOUTQ, &ret)) + GB.Error("Unable to read output buffer size: &1", strerror(errno)); + } + + GB.ReturnInteger(ret); + +END_PROPERTY + +// Here we declare the public interface of SerialPort class + +GB_DESC CSerialPortDesc[] = +{ + GB_DECLARE("SerialPort", sizeof(CSERIALPORT)), + + GB_INHERITS("Stream"), + + GB_CONSTANT("None", "i", 0), + + GB_CONSTANT("Hardware", "i", 1), + GB_CONSTANT("Software", "i", 2), + GB_CONSTANT("Both", "i", 3), + + GB_CONSTANT("Even", "i", 1), + GB_CONSTANT("Odd", "i", 2), + + GB_CONSTANT("Bits1", "i", 1), + GB_CONSTANT("Bits2", "i", 2), + + GB_CONSTANT("Bits5", "i", 5), + GB_CONSTANT("Bits6", "i", 6), + GB_CONSTANT("Bits7", "i", 7), + GB_CONSTANT("Bits8", "i", 8), + + GB_EVENT("Read", NULL, NULL, &EVENT_Read), + GB_EVENT("DTRChange", NULL, "(CurrentValue)b", &EVENT_DTR), + GB_EVENT("DSRChange", NULL, "(CurrentValue)b", &EVENT_DSR), + GB_EVENT("RTSChange", NULL, "(CurrentValue)b", &EVENT_RTS), + GB_EVENT("CTSChange", NULL, "(CurrentValue)b", &EVENT_CTS), + GB_EVENT("DCDChange", NULL, "(CurrentValue)b", &EVENT_DCD), + GB_EVENT("RNGChange", NULL, "(CurrentValue)b", &EVENT_RNG), + + GB_METHOD("_new", NULL, SerialPort_new, NULL), + GB_METHOD("_free", NULL, SerialPort_free, NULL), + GB_METHOD("Open", NULL, SerialPort_Open, "[(Polling)i]"), + + GB_PROPERTY("FlowControl","i",SerialPort_FlowControl), + GB_PROPERTY("PortName", "s", SerialPort_Port), + GB_PROPERTY("Parity", "i", SerialPort_Parity), + GB_PROPERTY("Speed", "i", SerialPort_Speed), + + GB_PROPERTY("DataBits", "i", SerialPort_DataBits), + GB_PROPERTY("StopBits", "i", SerialPort_StopBits), + GB_PROPERTY("DTR", "b", SerialPort_DTR), + GB_PROPERTY("RTS", "b", SerialPort_RTS), + GB_PROPERTY_READ("Status", "i", SerialPort_Status), + GB_PROPERTY_READ("DSR", "b", SerialPort_DSR), + GB_PROPERTY_READ("CTS", "b", SerialPort_CTS), + GB_PROPERTY_READ("DCD", "b", SerialPort_DCD), + GB_PROPERTY_READ("RNG", "b", SerialPort_RNG), + + GB_PROPERTY_READ("InputBufferSize", "i", SerialPort_InputBufferSize), + GB_PROPERTY_READ("OutputBufferSize", "i", SerialPort_OutputBufferSize), + + GB_CONSTANT("_IsControl", "b", TRUE), + GB_CONSTANT("_IsVirtual", "b", TRUE), + GB_CONSTANT("_Group", "s", "Network"), + GB_CONSTANT("_Properties", "s", "FlowControl{SerialPort.None;Hardware;Software;Both}=Hardware,PortName,Parity{SerialPort.None;Even;Odd}=None,Speed=19200,DataBits{SerialPort.Bits5;Bits6;Bits7;Bits8}=Bits8,StopBits{SerialPort.Bits1;Bits2}=Bits1"), + GB_CONSTANT("_DefaultEvent", "s", "Read"), + + GB_END_DECLARE +}; + + diff --git a/gb.net/src/CSerialPort.h b/gb.net/src/CSerialPort.h new file mode 100644 index 00000000..384d5f71 --- /dev/null +++ b/gb.net/src/CSerialPort.h @@ -0,0 +1,82 @@ +/*************************************************************************** + + CSerialPort.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSERIALPORT_H +#define __CSERIALPORT_H + +#include "gambas.h" +#include + +#ifndef __CSERIALPORT_C + +extern GB_DESC CSerialPortDesc[]; +extern GB_STREAM_DESC SerialStream; + +#else + +#define THIS ((CSERIALPORT *)_object) + +#endif + +typedef + struct + { + unsigned DSR : 1; + unsigned DTR : 1; + unsigned RTS : 1; + unsigned CTS : 1; + unsigned DCD : 1; + unsigned RNG : 1; + } + SERIAL_SIGNAL; + +typedef + struct + { + GB_BASE ob; + GB_STREAM stream; + int port; + int status; + char *portName; + int parity; + int speed; + int dataBits; + int stopBits; + int flow; + int polling; + GB_TIMER *every; + SERIAL_SIGNAL signals; + struct termios oldtio; + } + CSERIALPORT; + +int CSerialPort_stream_read(GB_STREAM *stream, char *buffer, int len); +int CSerialPort_stream_write(GB_STREAM *stream, char *buffer, int len); +int CSerialPort_stream_open(GB_STREAM *stream, const char *path, int mode, void *data); +int CSerialPort_stream_seek(GB_STREAM *stream, int64_t pos, int whence); +int CSerialPort_stream_tell(GB_STREAM *stream, int64_t *pos); +int CSerialPort_stream_flush(GB_STREAM *stream); +int CSerialPort_stream_close(GB_STREAM *stream); +int CSerialPort_stream_handle(GB_STREAM *stream); + +#endif diff --git a/gb.net/src/CServerSocket.c b/gb.net/src/CServerSocket.c new file mode 100644 index 00000000..93fb0a8c --- /dev/null +++ b/gb.net/src/CServerSocket.c @@ -0,0 +1,629 @@ +/*************************************************************************** + + CServerSocket.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSERVERSOCKET_C + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "tools.h" + +#include "CServerSocket.h" +#include "CSocket.h" + +DECLARE_EVENT(EVENT_Connection); +DECLARE_EVENT(EVENT_Error); + +static void srvsock_post_error(CSERVERSOCKET *_object) +{ + GB.Raise(THIS, EVENT_Error, 0); + GB.Unref(POINTER(&_object)); +} + +static void CServerSocket_CallBack(int fd, int type, intptr_t lParam) +{ + int okval = 0; + char *remote_ip; + unsigned int clen; + CSERVERSOCKET *_object = (CSERVERSOCKET*)lParam; + + if (SOCKET->status != NET_ACTIVE) return; + + SOCKET->status = NET_PENDING; + clen = sizeof(struct sockaddr_in); + THIS->Client = accept(SOCKET->socket, (struct sockaddr*)&THIS->so_client.in, &clen); + + if (THIS->Client == -1) + { + //close(THIS->Client); + SOCKET->status = NET_ACTIVE; + return; + } + + if ((!THIS->iMaxConn) || (THIS->iCurConn < THIS->iMaxConn)) + okval = 1; + + if ((!THIS->iPause) && (okval)) + { + remote_ip = GB.NewZeroString(inet_ntoa(THIS->so_client.in.sin_addr)); + GB.Raise(THIS, EVENT_Connection, 1, GB_T_STRING, remote_ip, GB.StringLength(remote_ip)); + GB.FreeString(&remote_ip); + } + + if (SOCKET->status == NET_PENDING) + { + close(THIS->Client); + THIS->Client = -1; + } + + SOCKET->status = NET_ACTIVE; +} + +static void CServerSocket_CallBackUnix(int fd, int type, intptr_t lParam) +{ + //int position=0; + int okval=0; + unsigned int ClientLen; + CSERVERSOCKET *_object = (CSERVERSOCKET*)lParam; + + if ( SOCKET->status != NET_ACTIVE) return; + + SOCKET->status = NET_PENDING; + ClientLen=sizeof(struct sockaddr_un); + THIS->Client=accept(SOCKET->socket,(struct sockaddr*)&THIS->so_client.un,&ClientLen); + if (THIS->Client == -1) + { + close(THIS->Client); + SOCKET->status = NET_ACTIVE; + return; + } + if ( (!THIS->iMaxConn) || (THIS->iCurConn < THIS->iMaxConn) ) okval=1; + if ( (!THIS->iPause) && (okval) ) + GB.Raise(THIS,EVENT_Connection,1,GB_T_STRING,NULL,0); + if ( SOCKET->status == NET_PENDING) close(THIS->Client); + SOCKET->status = NET_ACTIVE; + +} + + +/********************************************************* + Starts listening (TCP/UDP/UNIX) + **********************************************************/ + +static int do_srvsock_listen(CSERVERSOCKET* _object, int mymax) +{ + int retval; + int auth = 1; + + if (THIS->iPort == 0 && THIS->type == NET_TYPE_INTERNET) + return 8; + + if (SOCKET->status > NET_INACTIVE) return 1; + + if (mymax < 0) + return 13; + + if (THIS->type == NET_TYPE_LOCAL && !THIS->sPath) + return 7; + + if (THIS->type == NET_TYPE_INTERNET) + { + THIS->so_server.in.sin_family = AF_INET; + THIS->so_server.in.sin_addr.s_addr = INADDR_ANY; + THIS->so_server.in.sin_port = htons(THIS->iPort); + SOCKET->socket = socket(PF_INET, SOCK_STREAM, 0); + } + else + { + unlink(THIS->sPath); + THIS->so_server.un.sun_family = AF_UNIX; + strcpy(THIS->so_server.un.sun_path, THIS->sPath); + SOCKET->socket=socket(AF_UNIX, SOCK_STREAM,0); + } + + if ( SOCKET->socket==-1 ) + { + SOCKET->status = NET_CANNOT_CREATE_SOCKET; + GB.Ref(THIS); + GB.Post(srvsock_post_error,(intptr_t)THIS); + return 2; + } + // thanks to Benoit : this option allows non-root users to reuse the + // port after closing it and reopening it in a short interval of time. + // However, If you are porting this component to other O.S., be careful, + // as this is not a standard unix option + setsockopt(SOCKET->socket, SOL_SOCKET, SO_REUSEADDR, &auth, sizeof(int)); + + // Define specific interface: does not really work... :-/ + #ifdef SO_BINDTODEVICE + if (THIS->interface) + { + if (setsockopt(SOCKET->socket, SOL_SOCKET, SO_BINDTODEVICE, THIS->interface, GB.StringLength(THIS->interface))) + { + fprintf(stderr, "unable to bind socket to interface: %s\n", strerror(errno)); + SOCKET->status = NET_CANNOT_BIND_INTERFACE; + return 15; + } + } + #endif + + SOCKET_update_timeout(SOCKET); + // + if (THIS->type == NET_TYPE_INTERNET) + retval = bind(SOCKET->socket, (struct sockaddr *)&THIS->so_server.in, sizeof(struct sockaddr_in)); + else + retval = bind(SOCKET->socket, (struct sockaddr *)&THIS->so_server.un, sizeof(struct sockaddr_un)); + + if (retval == -1) + { + close(SOCKET->socket); + SOCKET->status = NET_CANNOT_BIND_SOCKET; + GB.Ref(THIS); + GB.Post(srvsock_post_error,(intptr_t)THIS); + return 10; + } + + // Set socket to non-blocking mode + SOCKET_set_blocking(SOCKET, FALSE); + + if ( listen(SOCKET->socket,mymax) == -1 ) + { + close(SOCKET->socket); + SOCKET->status = NET_CANNOT_LISTEN; + GB.Ref(THIS); + GB.Post(srvsock_post_error,(intptr_t)THIS); + return 14; + } + THIS->iCurConn=0; + THIS->iMaxConn=mymax; + SOCKET->status = NET_ACTIVE; + + //CServerSocket_AssignCallBack((intptr_t)THIS,SOCKET->socket); + if (THIS->type == NET_TYPE_INTERNET) + GB.Watch (SOCKET->socket , GB_WATCH_READ , (void *)CServerSocket_CallBack,(intptr_t)THIS); + else + GB.Watch (SOCKET->socket , GB_WATCH_READ , (void *)CServerSocket_CallBackUnix,(intptr_t)THIS); + + return 0; +} + +static void srvsock_listen(CSERVERSOCKET *_object, int max) +{ + switch(do_srvsock_listen(THIS, max)) + { + case 1: GB.Error("Socket is already listening"); break; + case 2: GB.Error("Cannot create socket"); break; + case 7: GB.Error("Path is not defined"); break; + case 8: GB.Error("Port is not defined"); break; + case 10: GB.Error("Cannot bind to socket"); break; + case 13: GB.Error("Invalid maximum number of connections"); break; + case 14: GB.Error("Cannot listen on socket"); break; + case 15: GB.Error("Unable to bind socket to interface"); break; + default: break; + } +} + +static void add_child(CSERVERSOCKET *_object, CSOCKET *child) +{ + *((CSOCKET **)GB.Add(&THIS->children)) = child; + child->parent = THIS; + GB.Ref(child); +} + +static void unref_child_later(CSOCKET *child) +{ + GB.Unref(POINTER(&child)); +} + +static void remove_child(CSERVERSOCKET *_object, CSOCKET *child) +{ + int i; + + for (i = 0; i < GB.Count(THIS->children); i++) + { + if (THIS->children[i] == child) + { + child->parent = NULL; + GB.Remove(&THIS->children, i, 1); + GB.Post(unref_child_later, (intptr_t)child); + return; + } + } +} + +void CServerSocket_OnClose(void *child) +{ + CSERVERSOCKET *_object; + + if (!child) return; + + _object = (CSERVERSOCKET*)(((CSOCKET *)child)->parent); + + if (!THIS) return; + + remove_child(THIS, child); + THIS->iCurConn--; +} + +/*************************************************** + This property reflects current status of the + socket (closed, listening...) + ***************************************************/ +BEGIN_PROPERTY(ServerSocket_Status) + + GB.ReturnInteger(SOCKET->status); + +END_PROPERTY + +/****************************************************************** + This property gets/sets the port to listen to (TCP or UDP sockets) + ******************************************************************/ +BEGIN_PROPERTY(ServerSocket_Port) + + if (READ_PROPERTY) + { + GB.ReturnInteger(THIS->iPort); + return; + } + if (SOCKET->status > NET_INACTIVE) + { + GB.Error("Port cannot be changed when socket is active"); + return; + } + if ( (VPROP(GB_INTEGER)<1) || (VPROP(GB_INTEGER)>65535) ) + { + GB.Error("Invalid port Value"); + return; + } + THIS->iPort=VPROP(GB_INTEGER); + +END_PROPERTY + + +/*************************************************************** + This property gets/sets the address to listen to (UNIX socket) + ***************************************************************/ +BEGIN_PROPERTY(ServerSocket_Path) + + if (READ_PROPERTY) + { + GB.ReturnString(THIS->sPath); + return; + } + if (SOCKET->status > NET_INACTIVE) + { + GB.Error("Path cannot be changed while socket is active"); + return; + } + if (PLENGTH() > NET_UNIX_PATH_MAX) + { + GB.Error ("Path is too long"); + return; + } + GB.StoreString(PROP(GB_STRING), &THIS->sPath); + +END_PROPERTY + + +BEGIN_PROPERTY(ServerSocket_Interface) + + if (READ_PROPERTY) + { + GB.ReturnString(THIS->sPath); + } + else + { + if (SOCKET->status > NET_INACTIVE) + { + GB.Error("Interface cannot be changed while socket is active"); + return; + } + if (PLENGTH() > IFNAMSIZ) + { + GB.Error ("Interface name is too long"); + return; + } + + GB.StoreString(PROP(GB_STRING), &THIS->interface); + } + +END_PROPERTY + + +/*************************************************************** + This property gets/sets the socket type (0 -> TCP, 1 -> UNIX) + ***************************************************************/ +BEGIN_PROPERTY ( ServerSocket_Type ) + + if (READ_PROPERTY) + { + GB.ReturnInteger(THIS->type); + return; + } + + if (SOCKET->status > NET_INACTIVE) + { + GB.Error("Type cannot be changed when the socket is active"); + return; + } + + switch(VPROP(GB_INTEGER)) + { + case NET_TYPE_LOCAL: THIS->type = NET_TYPE_LOCAL; break; + case NET_TYPE_INTERNET: THIS->type = NET_TYPE_INTERNET; break; + default: GB.Error("Invalid socket type"); + } + +END_PROPERTY + + +BEGIN_METHOD(ServerSocket_new, GB_STRING sPath; GB_INTEGER iMaxConn) + + char *buf = NULL; + int nport = 0; + int iMax; + + THIS->type = NET_TYPE_INTERNET; + GB.NewArray(&THIS->children, sizeof(void *), 0); + + if (MISSING(sPath)) return; + if (LENGTH(sPath) == 0) return; + + iMax = VARGOPT(iMaxConn, 0); + + switch(IsHostPath(STRING(sPath), LENGTH(sPath), &buf, &nport)) + { + case 0: + GB.Error("Invalid Host or Path"); + return; + + case 1: + if (buf) + { + GB.Free(POINTER(&buf)); + GB.Error("Invalid Host"); + return; + } + if (nport<1) + { + GB.Error("Invalid Port"); + return; + } + + THIS->type = NET_TYPE_INTERNET; + THIS->iPort=nport; + srvsock_listen(THIS, iMax); + break; + + case 2: + THIS->type = NET_TYPE_LOCAL; + if (LENGTH(sPath) >NET_UNIX_PATH_MAX) + { + GB.Error ("Path is too long"); + return; + } + GB.StoreString(ARG(sPath), &THIS->sPath); + break; + } + +END_METHOD + +void close_server(CSERVERSOCKET *_object) +{ + CSOCKET *chd; + + if (SOCKET->status <= NET_INACTIVE) return; + + GB.Watch (SOCKET->socket , GB_WATCH_NONE , (void *)CServerSocket_CallBack,0); + close(SOCKET->socket); + SOCKET->status = NET_INACTIVE; + + while (GB.Count(THIS->children)) + { + chd = THIS->children[0]; + if (chd->common.stream.desc) CSocket_stream_close(&chd->common.stream); + remove_child(THIS, chd); + } +} + +BEGIN_METHOD_VOID(ServerSocket_free) + + close_server(THIS); + GB.FreeArray(&THIS->children); + GB.FreeString(&THIS->sPath); + GB.FreeString(&THIS->interface); + +END_METHOD + +/******************************************************** + Stops listening (TCP/UDP/UNIX), and closes all client sockets associated + to this server + *********************************************************/ +BEGIN_METHOD_VOID(ServerSocket_Close) + + close_server(THIS); + +END_METHOD + +/******************************************************** + Do not accept more connections until Resume is used + *********************************************************/ +BEGIN_METHOD_VOID(ServerSocket_Pause) + + THIS->iPause=1; + +END_METHOD + +/******************************************************** + Accept connections again + *********************************************************/ +BEGIN_METHOD_VOID(ServerSocket_Resume) + + THIS->iPause=0; + +END_METHOD + +BEGIN_METHOD(ServerSocket_Listen, GB_INTEGER MaxConn) + + srvsock_listen(THIS, VARGOPT(MaxConn, 0)); + +END_METHOD + +/****************************************************************** + To accept a pending connection and delegate it to a Socket object +*******************************************************************/ + +BEGIN_METHOD_VOID(ServerSocket_Accept) + + CSOCKET *socket; + struct sockaddr_in myhost; + unsigned int mylen; + + if ( SOCKET->status != NET_PENDING) + { + GB.Error("No connection to accept"); + return; + } + + socket = GB.New(GB.FindClass("Socket"), "Socket", NULL); + socket->common.socket = THIS->Client; + socket->common.status = NET_CONNECTED; + socket->OnClose = CServerSocket_OnClose; + + THIS->iCurConn++; + + GB.FreeString(&socket->sRemoteHostIP); + GB.FreeString(&socket->sLocalHostIP); + GB.FreeString(&socket->sPath); + + socket->iLocalPort = 0; + socket->iPort = 0; + socket->conn_type = THIS->type; + + if (THIS->type == NET_TYPE_INTERNET) + { + socket->sRemoteHostIP = GB.NewZeroString(inet_ntoa(THIS->so_client.in.sin_addr)); + socket->Host = GB.NewZeroString (inet_ntoa(THIS->so_client.in.sin_addr)); + mylen = sizeof(struct sockaddr); + getsockname(socket->common.socket, (struct sockaddr*)&myhost, &mylen); + socket->sLocalHostIP = GB.NewZeroString(inet_ntoa(myhost.sin_addr)); + socket->iLocalPort = ntohs(myhost.sin_port); + socket->iPort = ntohs(THIS->so_client.in.sin_port); + socket->iUsePort = ntohs(THIS->so_client.in.sin_port); + } + else + { + socket->conn_type=1; + socket->sPath = GB.NewZeroString(THIS->sPath); + socket->Path = GB.NewZeroString(THIS->sPath); + } + + add_child(THIS, socket); + + CSOCKET_init_connected(socket); + // Socket returned by accept is non-blocking by default + GB.Stream.Block(&socket->common.stream, FALSE); + //socket->stream._free[0]=(intptr_t)socket; + + GB.Ref(socket); + GB.Post(CSocket_post_connected,(intptr_t)socket); + SOCKET->status = NET_ACCEPTING; + + GB.ReturnObject((void*)socket); + +END_METHOD + +// BM: Enumeration of child sockets + +BEGIN_METHOD_VOID(ServerSocket_next) + + int *index = (int *)GB.GetEnum(); + + if (*index >= GB.Count(THIS->children)) + GB.StopEnum(); + else + { + GB.ReturnObject(THIS->children[*index]); + (*index)++; + } + +END_METHOD + +BEGIN_PROPERTY(ServerSocket_count) + + GB.ReturnInteger(GB.Count(THIS->children)); + +END_PROPERTY + + +/**************************************************************** + Here we declare public structure of the ServerSocket class +*****************************************************************/ +GB_DESC CServerSocketDesc[] = +{ + GB_DECLARE("ServerSocket", sizeof(CSERVERSOCKET)), + + GB_EVENT("Connection", NULL, "(RemoteHostIP)s", &EVENT_Connection), + GB_EVENT("Error", NULL,NULL, &EVENT_Error), + + GB_METHOD("_new", NULL, ServerSocket_new, "[(Path)s(MaxConn)i]"), + GB_METHOD("_free", NULL, ServerSocket_free, NULL), + GB_METHOD("Listen",NULL, ServerSocket_Listen, "[(MaxConn)i]"), + GB_METHOD("Pause", NULL, ServerSocket_Pause, NULL), + GB_METHOD("Resume", NULL, ServerSocket_Resume, NULL), + GB_METHOD("Accept","Socket",ServerSocket_Accept,NULL), + GB_METHOD("Close",NULL,ServerSocket_Close,NULL), + + GB_PROPERTY("Type","i",ServerSocket_Type), + GB_PROPERTY("Path","s",ServerSocket_Path), + GB_PROPERTY("Port", "i", ServerSocket_Port), + GB_PROPERTY("Interface", "s", ServerSocket_Interface), + GB_PROPERTY_READ("Status","i",ServerSocket_Status), + + GB_METHOD("_next", "Socket", ServerSocket_next, NULL), + GB_PROPERTY_READ("Count", "i", ServerSocket_count), + + GB_PROPERTY("Timeout", "i", Socket_Timeout), + + GB_CONSTANT("_IsControl", "b", TRUE), + GB_CONSTANT("_IsVirtual", "b", TRUE), + GB_CONSTANT("_Group", "s", "Network"), + GB_CONSTANT("_Properties", "s", "Type=Local,Path,Port"), + GB_CONSTANT("_DefaultEvent", "s", "Connection"), + + GB_END_DECLARE +}; + + + + diff --git a/gb.net/src/CServerSocket.h b/gb.net/src/CServerSocket.h new file mode 100644 index 00000000..7ec4f5ab --- /dev/null +++ b/gb.net/src/CServerSocket.h @@ -0,0 +1,69 @@ +/*************************************************************************** + + CServerSocket.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSERVERSOCKET_H +#define __CSERVERSOCKET_H + +#include "gambas.h" +#include "CNet.h" +#include "CSocket.h" + +#ifndef __CSERVERSOCKET_C + +extern GB_DESC CServerSocketDesc[]; + +#else + +#define THIS ((CSERVERSOCKET *)_object) +#define SOCKET (&THIS->common) + +#endif + + +typedef + union + { + struct sockaddr_in in; + struct sockaddr_un un; + } + st_so_sock; + +typedef + struct + { + CSOCKET_COMMON common; + int type; + int iPort; + char *sPath; + int iPause; + int iMaxConn; + int iCurConn; + st_so_sock so_server; + st_so_sock so_client; + int Client; + CSOCKET **children; + char *interface; + } + CSERVERSOCKET; + +#endif diff --git a/gb.net/src/CSocket.c b/gb.net/src/CSocket.c new file mode 100644 index 00000000..0a140a8e --- /dev/null +++ b/gb.net/src/CSocket.c @@ -0,0 +1,937 @@ +/*************************************************************************** + + CSocket.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSOCKET_C + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "tools.h" + +#include "CSocket.h" +#include "CServerSocket.h" +#include "CDnsClient.h" + +//#define DEBUG_ME 1 + +#define MAX_CLIENT_BUFFER_SIZE 65536 +#define UNIXPATHMAX 108 + +DECLARE_EVENT (EVENT_Error); +DECLARE_EVENT (EVENT_Close); +DECLARE_EVENT (EVENT_Found); +DECLARE_EVENT (EVENT_Ready); +DECLARE_EVENT (EVENT_Read); +DECLARE_EVENT (EVENT_Write); + +GB_STREAM_DESC SocketStream = +{ + open: CSocket_stream_open, + close: CSocket_stream_close, + read: CSocket_stream_read, + write: CSocket_stream_write, + seek: CSocket_stream_seek, + tell: CSocket_stream_tell, + flush: CSocket_stream_flush, + /*eof: CSocket_stream_eof, + lof: CSocket_stream_lof,*/ + handle: CSocket_stream_handle +}; + +#if DEBUG_ME +static void set_status(CSOCKET *_object, int status) +{ + static const char *status_name[] = { "Inactive", "Active", "Pending", "Accepting", "Receiving data", "Searching", "Connecting", "Connected" }; + static const char *error_name[] = { NULL, NULL, "Cannot create socket", "Connection refused", "Cannot read", "Cannot write", "Host not found", NULL, NULL, NULL, + "Cannot bind socket", NULL, NULL, NULL, "Cannot listen", "Cannot bind interface", "Cannot authenticate" }; + SOCKET->status = status; + + fprintf(stderr, "gb.net: socket %p: ", THIS); + if (status >= 0 && status < 7) + fprintf(stderr, "%s", status_name[status]); + else if (status >= -16 && status < 0) + fprintf(stderr, "%s", error_name[-status]); + + fprintf(stderr, " (%d)\n", status); +} +#else +#define set_status(_object, _status) SOCKET->status = (_status) +#endif + +bool SOCKET_update_timeout(CSOCKET_COMMON *socket) +{ + struct timeval timeout; + + if (socket->socket < 0) + return TRUE; + + timeout.tv_sec = socket->timeout / 1000; + timeout.tv_usec = (socket->timeout % 1000) * 1000; + + if (setsockopt(socket->socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0) + { + GB.Error("Cannot set sending timeout"); + return TRUE; + } + + if (setsockopt(socket->socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) + { + GB.Error("Cannot set receiving timeout"); + return TRUE; + } + + return FALSE; +} + +void SOCKET_set_blocking(CSOCKET_COMMON *socket, bool block) +{ + int do_not_block = block ? 0 : 1; + + ioctl(socket->socket, FIONBIO, &do_not_block); + SOCKET_update_timeout(socket); +} + +/********************************** +Routines to call events +**********************************/ + +static void CSocket_post_error(void *_object) +{ + GB.Raise(THIS,EVENT_Error,0); + GB.Unref(POINTER(&_object)); +} + +static void CSocket_post_closed(void *_object) +{ + GB.Raise(THIS, EVENT_Close, 0); + GB.Unref(POINTER(&_object)); +} + +static void CSocket_post_hostfound(void *_object) +{ + GB.Raise(THIS,EVENT_Found,0); + GB.Unref(POINTER(&_object)); +} + +void CSocket_post_connected(void *_object) +{ + GB.Raise(THIS,EVENT_Ready,0); + GB.Unref(POINTER(&_object)); +} + +static void CSocket_close(CSOCKET *_object) +{ + int fd; + + if (THIS->DnsTool) + { + dns_close_all(THIS->DnsTool); + GB.Unref(POINTER(&THIS->DnsTool)); + THIS->DnsTool = NULL; + } + + fd = SOCKET->socket; + if (fd >= 0) + { + //fprintf(stderr, "CSocket_close: %p: set fd %d to -1\n", THIS, fd); + SOCKET->socket = -1; + + GB.Watch(fd , GB_WATCH_NONE, NULL, 0); + SOCKET->stream.desc = NULL; + close(fd); + + set_status(THIS, NET_INACTIVE); + } + + if (THIS->OnClose) + THIS->OnClose(_object); +} + + +/* + This function is called by DnsClient to inform + that it has finished its work +*/ + +void CSocket_CallBackFromDns(void *_object) +{ + int myval=0; + + if (SOCKET->status != NET_SEARCHING) + return; + + if (!THIS->DnsTool->sHostIP) + { + // Host not found + CSocket_stream_internal_error(THIS, NET_HOST_NOT_FOUND, TRUE); + return; + } + + GB.FreeString (&THIS->sRemoteHostIP); + THIS->sRemoteHostIP = GB.NewZeroString (THIS->DnsTool->sHostIP); + + // We connect to the socket + + THIS->Server.sin_family = AF_INET; + THIS->Server.sin_port = htons(THIS->iPort); + THIS->Server.sin_addr.s_addr = inet_addr(THIS->DnsTool->sHostIP); + bzero(&(THIS->Server.sin_zero), 8); + + // Don't block, so that connect() returns immediately + + SOCKET_set_blocking(SOCKET, FALSE); + myval = connect(SOCKET->socket,(struct sockaddr*)&(THIS->Server), sizeof(struct sockaddr)); + SOCKET_set_blocking(SOCKET, TRUE); + + if (!myval || errno == EINPROGRESS) // Rhis is the good answer : connect in progress + { + set_status(THIS, NET_CONNECTING); + GB.Watch(SOCKET->socket, GB_WATCH_WRITE, (void *)CSocket_CallBackConnecting, (intptr_t)THIS); + } + else + { + GB.Watch(SOCKET->socket , GB_WATCH_NONE, NULL, 0); + SOCKET->stream.desc = NULL; + close(SOCKET->socket); + SOCKET->socket = -1; + set_status(THIS, NET_INACTIVE); + } + + if (THIS->DnsTool) + { + dns_close_all(THIS->DnsTool); + GB.Unref(POINTER(&THIS->DnsTool)); + THIS->DnsTool = NULL; + } + + if (SOCKET->status <= NET_INACTIVE) + { + CSocket_stream_internal_error(THIS, NET_CONNECTION_REFUSED, TRUE); + return; + } + + GB.Ref(THIS); + GB.Post(CSocket_post_hostfound, (intptr_t)THIS); +} + + +void CSOCKET_init_connected(CSOCKET *_object) +{ + GB.Watch(SOCKET->socket, GB_WATCH_READ, (void *)CSocket_CallBack, (intptr_t)THIS); + SOCKET->stream.desc = &SocketStream; + SOCKET_update_timeout(SOCKET); +} + +/******************************************************************* +This CallBack is used while waiting to finish a connection process +******************************************************************/ +void CSocket_CallBackConnecting(int t_sock,int type,intptr_t param) +{ + struct sockaddr_in myhost; + int mylen; + void *_object = (void *)param; + + GB.Watch(SOCKET->socket, GB_WATCH_NONE, NULL, 0); + + if (SOCKET->status != NET_CONNECTING) return; + + /**************************************************** + Checks if Connection was Stablished or there was + an error trying to connect + ****************************************************/ + + set_status(THIS, CheckConnection(SOCKET->socket)); + if (SOCKET->status == NET_INACTIVE) + { + CSocket_stream_internal_error(THIS, NET_CONNECTION_REFUSED, TRUE); + return; + } + if (SOCKET->status != NET_CONNECTED) return; + // we obtain local IP and host + + mylen=sizeof(struct sockaddr); + getsockname (SOCKET->socket,(struct sockaddr*)&myhost,(socklen_t *)&mylen); + THIS->iLocalPort=ntohs(myhost.sin_port); + GB.FreeString( &THIS->sLocalHostIP); + THIS->sLocalHostIP = GB.NewZeroString(inet_ntoa(myhost.sin_addr)); + + CSOCKET_init_connected(THIS); + GB.Stream.SetSwapping(&SOCKET->stream, htons(1234) != 1234); + + GB.Ref(THIS); + GB.Post(CSocket_post_connected,(intptr_t)THIS); +} + +/******************************************************************* +This CallBack is used while socket is connected to remote host +******************************************************************/ +static void callback_write(int t_sock,int type, CSOCKET *_object) +{ + //fprintf(stderr, "callback write %p\n", THIS); + THIS->watch_write = FALSE; + GB.Watch(SOCKET->socket, GB_WATCH_WRITE, NULL, 0); + GB.Raise(THIS, EVENT_Write, 0); +} + +void CSocket_CallBack(int t_sock,int type, CSOCKET *_object) +{ + char buf[1]; + struct pollfd mypoll; + int numpoll; + struct timespec mywait; + + //fprintf(stderr, "callback read %p\n", THIS); + /* Just sleeping a little to reduce CPU waste */ + mywait.tv_sec=0; + mywait.tv_nsec=100000; + nanosleep(&mywait,NULL); + + /* is there data available or an error? */ + if (SOCKET->status != NET_CONNECTED) return; + + mypoll.fd=t_sock; + mypoll.events=POLLIN | POLLNVAL; + mypoll.revents=0; + numpoll=poll(&mypoll,1,0); + if (numpoll<=0) return; + /* there's data available */ + + USE_MSG_NOSIGNAL(numpoll=recv(t_sock,(void*)buf,sizeof(char),MSG_PEEK | MSG_NOSIGNAL)); + if (!numpoll) + { /* socket error, no valid data received */ + + CSocket_close(THIS); + GB.Ref(THIS); + GB.Post(CSocket_post_closed, (intptr_t)THIS); + return; + } + /****************************************************** + There's data available to read, so we'll raise event + EVENT_Read + *******************************************************/ + + GB.Raise(THIS, EVENT_Read, 0); + //GB.Ref(_object); + //GB.Post(CSocket_post_data_available,(intptr_t)THIS); +} + + +void CSocket_stream_internal_error(void *_object, int ncode, bool post) +{ + CSocket_close(THIS); + + /* fatal socket error handling */ + set_status(THIS, ncode); + + if (post) + { + GB.Ref(THIS); + GB.Post(CSocket_post_error, (intptr_t)THIS); + } +} + +////////////////////////////////////////////////////////////////////////////////// +//################################################################################ +/********************************************************************************* + "PUBLIC" C/C++ INTERFACE +**********************************************************************************/ +//################################################################################ +////////////////////////////////////////////////////////////////////////////////// + +/* not allowed methods */ +int CSocket_stream_open(GB_STREAM *stream, const char *path, int mode, void *data){return -1;} +int CSocket_stream_seek(GB_STREAM *stream, int64_t pos, int whence){return -1;} +int CSocket_stream_tell(GB_STREAM *stream, int64_t *pos){return -1;} + +int CSocket_stream_flush(GB_STREAM *stream) +{ + return 0; /* OK */ +} + +int CSocket_stream_handle(GB_STREAM *stream) +{ + void *_object = stream->tag; + return SOCKET->socket; +} + +int CSocket_stream_close(GB_STREAM *stream) +{ + void *_object = stream->tag; + + if (!THIS) return -1; + CSocket_close(THIS); + + return 0; +} + +/*int CSocket_stream_lof(GB_STREAM *stream, int64_t *len) +{ + void *_object = stream->tag; + int bytes; + + *len=0; + if (!THIS) return -1; + + if (ioctl(SOCKET->socket,FIONREAD,&bytes)) + { + CSocket_stream_internal_error(THIS, NET_CANNOT_READ, FALSE); + return -1; + } + *len=bytes; + return 0; +} + +int CSocket_stream_eof(GB_STREAM *stream) +{ + void *_object = stream->tag; + int bytes; + + if (!THIS) return -1; + + if (ioctl(SOCKET->socket,FIONREAD,&bytes)) + { + CSocket_stream_internal_error(THIS, NET_CANNOT_READ, FALSE); + return -1; + } + if (!bytes) return -1; + return 0; +}*/ + +int CSocket_stream_read(GB_STREAM *stream, char *buffer, int len) +{ + void *_object = stream->tag; + int npos=-1; + //int bytes; + + if (!THIS) return -1; + + /*if (ioctl(SOCKET->socket,FIONREAD,&bytes)) + { + CSocket_stream_internal_error(THIS, NET_CANNOT_READ, FALSE); + return -1; + } + //if (bytes < len) return -1; + if (bytes < len) + len = bytes;*/ + + USE_MSG_NOSIGNAL(npos = recv(SOCKET->socket,(void*)buffer,len*sizeof(char),MSG_NOSIGNAL)); + + if (npos < 0 && errno != EAGAIN) + CSocket_stream_internal_error(THIS, NET_CANNOT_READ, FALSE); + + return npos; +} + +int CSocket_stream_write(GB_STREAM *stream, char *buffer, int len) +{ + void *_object = stream->tag; + int npos=-1; + + if (!THIS) return -1; + + USE_MSG_NOSIGNAL(npos = send(SOCKET->socket,(void*)buffer,len*sizeof(char),MSG_NOSIGNAL)); + + if (npos >= 0 || errno == EAGAIN) + { + if (GB.CanRaise(THIS, EVENT_Write) && !THIS->watch_write) + { + //fprintf(stderr, "watch write %p\n", THIS); + THIS->watch_write = TRUE; + GB.Watch(SOCKET->socket, GB_WATCH_WRITE, (void *)callback_write, (intptr_t)THIS); + } + } + + if ((npos < 0) && errno != EAGAIN) + CSocket_stream_internal_error(THIS, NET_CANNOT_WRITE, FALSE); + + return npos; +} + + + +/************************************************************************** +To start a UNIX connection +**************************************************************************/ +int CSocket_connect_unix(void *_object,char *sPath, int lenpath) +{ + int ret; + + if ( SOCKET->status > NET_INACTIVE ) return 1; + if (!sPath) return 7; + if ( (lenpath<1) || (lenpath>UNIXPATHMAX) ) return 7; + + GB.FreeString(&THIS->sRemoteHostIP); + GB.FreeString(&THIS->sLocalHostIP); + + THIS->UServer.sun_family=AF_UNIX; + strcpy(THIS->UServer.sun_path,sPath); + if ( (SOCKET->socket=socket(AF_UNIX,SOCK_STREAM,0))==-1 ) + { + set_status(THIS, NET_CANNOT_CREATE_SOCKET); + GB.Ref (THIS); + CSocket_post_error(_object); /* Unable to create socket */ + return 2; + } + + GB.FreeString(&THIS->sPath); + THIS->sPath = GB.NewZeroString(THIS->UServer.sun_path); + + THIS->conn_type = NET_TYPE_INTERNET; + + ret = connect(SOCKET->socket,(struct sockaddr*)&THIS->UServer,sizeof(struct sockaddr_un)); + + // Set socket to blocking mode, after the connect() call! + SOCKET_set_blocking(SOCKET, TRUE); + + if (ret == 0) + { + set_status(THIS, NET_CONNECTED); + CSOCKET_init_connected(THIS); + + // $BM + if (THIS->Host) GB.FreeString(&THIS->Host); + if (THIS->Path) GB.FreeString(&THIS->Path); + + THIS->Path = GB.NewZeroString(sPath); + GB.Ref (THIS); + CSocket_post_connected(_object); + + return 0; + } + + // Error + SOCKET->stream.desc = NULL; + close(SOCKET->socket); + GB.FreeString(&THIS->sPath); + set_status(THIS, NET_CONNECTION_REFUSED); + + GB.Ref (THIS); + CSocket_post_error(_object); /* Unable to connect to remote host */ + + return 3; +} + +/************************************************************************** +To start a TCP connection +**************************************************************************/ +int CSocket_connect_socket(void *_object,char *sHost,int lenhost,int myport) +{ + if ( SOCKET->status > NET_INACTIVE ) return 1; + if (!lenhost) return 9; + if (!sHost) return 9; + if ( (myport<1) || (myport>65535) ) return 8; + + GB.FreeString(&THIS->sRemoteHostIP); + GB.FreeString(&THIS->sLocalHostIP); + + if ( (SOCKET->socket=socket(AF_INET,SOCK_STREAM,0))==-1 ) + { + set_status(THIS, NET_CANNOT_CREATE_SOCKET); + GB.Ref (THIS); + CSocket_post_error(_object); + return 2; + } + + // Set socket to blocking mode + SOCKET_set_blocking(SOCKET, TRUE); + + THIS->iPort=myport; + THIS->conn_type = NET_TYPE_INTERNET; + + /****************************************** + Let's turn hostname into host IP + *******************************************/ + if (!THIS->DnsTool) + { + THIS->DnsTool = GB.New(GB.FindClass("DnsClient"), NULL, NULL); + THIS->DnsTool->CliParent=_object; + } + + if (THIS->DnsTool->iStatus > 0 ) dns_close_all(THIS->DnsTool); + + dns_set_async_mode(1,THIS->DnsTool); + GB.FreeString (&(THIS->DnsTool->sHostName)); + THIS->DnsTool->sHostName = GB.NewString(sHost,lenhost); + THIS->DnsTool->finished_callback=CSocket_CallBackFromDns; + + /******************************************** + We start DNS lookup, when it is finished + it will call to CSocket_CallBack_fromDns, + and we'll continue there connection proccess + ********************************************/ + set_status(THIS, NET_SEARCHING); /* looking for IP */ + dns_thread_getip(THIS->DnsTool); + SOCKET->stream.desc=&SocketStream; + THIS->iUsePort=THIS->iPort; + + // $BM + if (THIS->Path) GB.FreeString(&THIS->Path); + + if (sHost != THIS->Host) + { + if (THIS->Host) GB.FreeString(&THIS->Host); + THIS->Host = GB.NewZeroString(sHost); + } + + return 0; +} + +/********************************************** +This function is used to peek data from socket, +you have to pass 3 parameters: + +_object-> CSocket object +buf -> Data Buffer (you have to free it after using it!) +MaxLen -> 0 no limit, >0 max. data t read +***********************************************/ +int CSocket_peek_data(void *_object,char **buf,int MaxLen) +{ + int retval=0; + int nread=0; + int bytes=0; + + (*buf)=NULL; + nread=ioctl(SOCKET->socket,FIONREAD,&bytes); /* Is there anythig to read? */ + if (nread) + retval=-1; + else + retval=0; + if ( (!retval) && (bytes) ) /* if there's anything to receive */ + { + if (bytes > MAX_CLIENT_BUFFER_SIZE) bytes=MAX_CLIENT_BUFFER_SIZE; + if (MaxLen >0 ) bytes=MaxLen; + GB.Alloc((void**)buf,bytes); + (*buf)[0]='\0'; + USE_MSG_NOSIGNAL(retval=recv(SOCKET->socket,(void*)(*buf),bytes*sizeof(char),MSG_PEEK|MSG_NOSIGNAL)); + } + + if (retval==-1) + { + /* An error happened while trying to receive data : SOCKET ERROR */ + if (*buf) + { + GB.Free(POINTER(buf)); + buf=NULL; + } + GB.Watch (SOCKET->socket , GB_WATCH_NONE , (void *)CSocket_CallBack,0); + SOCKET->stream.desc=NULL; + close(SOCKET->socket); + set_status(THIS, NET_CANNOT_READ); + GB.Ref (THIS); + CSocket_post_error(_object); + return -1; + } + + return retval; +} + +/***************************************************************************************** +########################################################################################## +-------------------------SOCKET GAMBAS INTERFACE IMPLEMENTATION--------------------- +########################################################################################## +******************************************************************************************/ +/******************************************************************** +Returns current Status of the socket (connected,connecting,closed) +*********************************************************************/ +BEGIN_PROPERTY(Socket_Status) + + GB.ReturnInteger(SOCKET->status); + +END_PROPERTY + +/******************************************************************** +Port to connect to. Can be 'Net.Local' for Unix sockets, or +1-65535 for TCP sockets +********************************************************************/ +BEGIN_PROPERTY(Socket_Port) + + int port; + + if (READ_PROPERTY) + { + GB.ReturnInteger(THIS->iUsePort); + return; + } + + if (SOCKET->status > NET_INACTIVE) + { + GB.Error("Port property cannot be changed while the socket is active"); + return; + } + + port = VPROP(GB_INTEGER); + + if (port < 0 || port > 65535) + { + GB.Error("Invalid port number"); + return; + } + + THIS->iUsePort = port; + +END_PROPERTY + +/********************************************************************* + Host or Path to connect to. If 'Port' value is zero, this + will be a local path, else, a remote host name +**********************************************************************/ + +BEGIN_PROPERTY (Socket_Host) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(THIS->Host); + else + GB.StoreString(PROP(GB_STRING), &THIS->Host); + +END_PROPERTY + +BEGIN_PROPERTY (Socket_Path) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(THIS->Path); + else + GB.StoreString(PROP(GB_STRING), &THIS->Path); + +END_PROPERTY + +/******************************************************************** +Returns current TCP remote port (only when connected via TCP) +*********************************************************************/ +BEGIN_PROPERTY(Socket_RemotePort) + + if (SOCKET->status != NET_CONNECTED || THIS->conn_type != NET_TYPE_INTERNET) + GB.ReturnInteger(0); + else + GB.ReturnInteger(THIS->iPort); + +END_PROPERTY + +/******************************************************************** +Returns current TCP local port (only when connected via TCP) +*********************************************************************/ +BEGIN_PROPERTY(Socket_LocalPort) + + if (SOCKET->status != NET_CONNECTED || THIS->conn_type != NET_TYPE_INTERNET) + GB.ReturnInteger(0); + else + GB.ReturnInteger(THIS->iLocalPort); + +END_PROPERTY + +/*********************************************************************** +Returns current foreing host IP (only when connected via TCP) +***********************************************************************/ +BEGIN_PROPERTY(Socket_RemoteHost) + + if (SOCKET->status != NET_CONNECTED || THIS->conn_type != NET_TYPE_INTERNET) + GB.ReturnVoidString(); + else + GB.ReturnString(THIS->sRemoteHostIP); + +END_PROPERTY + +/*********************************************************************** +Returns current local host IP (only when connected via TCP) +***********************************************************************/ +BEGIN_PROPERTY(Socket_LocalHost) + + if (SOCKET->status != NET_CONNECTED || THIS->conn_type != NET_TYPE_INTERNET) + GB.ReturnVoidString(); + else + GB.ReturnString(THIS->sLocalHostIP); + +END_PROPERTY + +/**************************************************** +Gambas object "Constructor" +****************************************************/ +BEGIN_METHOD_VOID(Socket_new) + + SOCKET->stream.tag = THIS; + THIS->iUsePort = 80; + SOCKET->socket = -1; + +END_METHOD + +/************************************************** +Gambas object "Destructor" +**************************************************/ +BEGIN_METHOD_VOID(Socket_free) + + CSocket_close(THIS); + + GB.FreeString(&THIS->sPath); + GB.FreeString(&THIS->sLocalHostIP); + GB.FreeString(&THIS->sRemoteHostIP); + GB.FreeString(&THIS->Host); + GB.FreeString(&THIS->Path); + +END_METHOD + +/************************************************************* +To Peek data arrived from the other side of the socket +**************************************************************/ +BEGIN_METHOD_VOID(Socket_Peek) + + char *buf=NULL; + int retval=0; + + if (SOCKET->status != NET_CONNECTED) /* if socket is not connected we can't receive anything */ + { + GB.Error("Socket is not connected"); + return; + } + + retval = CSocket_peek_data(_object, &buf, 0); + + if (retval == -1) + { + /* An error happened while trying to receive data : SOCKET ERROR */ + if (buf) GB.Free(POINTER(&buf)); + GB.ReturnVoidString(); + return; + } + + if (retval > 0) + GB.ReturnNewString(buf, retval); + else + GB.ReturnVoidString(); + + if (buf) GB.Free(POINTER(&buf)); + +END_METHOD + + + +/************************************************************************** +To start a TCP or UNIX connection +**************************************************************************/ + +BEGIN_METHOD(Socket_Connect, GB_STRING HostOrPath; GB_INTEGER Port) + + int port; + int err; + + port = VARGOPT(Port, THIS->iUsePort); + + if (!port) + { + if (MISSING(HostOrPath)) + err = CSocket_connect_unix(_object,THIS->Path,GB.StringLength(THIS->Path)); + else + err = CSocket_connect_unix(_object,STRING(HostOrPath),LENGTH(HostOrPath)); + } + else + { + if (MISSING(HostOrPath)) + err = CSocket_connect_socket(_object,THIS->Host,GB.StringLength(THIS->Host),port); + else + err = CSocket_connect_socket(_object,STRING(HostOrPath),LENGTH(HostOrPath),port); + } + + switch (err) + { + case 1: GB.Error("Socket is already connected"); return; + case 2: GB.Error("Invalid path length"); return; + case 8: GB.Error("Port value out of range"); return; + case 9: GB.Error("Invalid host name"); return; + } + +END_METHOD + +BEGIN_PROPERTY(Socket_Timeout) + + if (READ_PROPERTY) + GB.ReturnInteger(SOCKET->timeout); + else + { + int val = VPROP(GB_INTEGER); + if (val < 0) + val = 0; + SOCKET->timeout = val; + SOCKET_update_timeout(SOCKET); + } + +END_PROPERTY + +BEGIN_PROPERTY(Socket_Server) + + GB.ReturnObject(THIS->parent); + +END_PROPERTY + +/********************************************************** +Here we declare public structure of Socket Class +***********************************************************/ + +GB_DESC CSocketDesc[] = +{ + GB_DECLARE("Socket", sizeof(CSOCKET)), GB_INHERITS("Stream"), + + GB_EVENT("Error", NULL, NULL, &EVENT_Error), + GB_EVENT("Ready", NULL, NULL, &EVENT_Ready), + GB_EVENT("Closed", NULL, NULL, &EVENT_Close), + GB_EVENT("Found", NULL, NULL, &EVENT_Found), + GB_EVENT("Read", NULL, NULL, &EVENT_Read), + GB_EVENT("Write", NULL, NULL, &EVENT_Write), + + GB_METHOD("_new", NULL, Socket_new, NULL), + GB_METHOD("_free", NULL, Socket_free, NULL), + GB_METHOD("Peek", "s", Socket_Peek, NULL), + GB_METHOD("Connect",NULL, Socket_Connect,"[(HostOrPath)s(Port)i]"), + + GB_PROPERTY_READ("Status", "i", Socket_Status), + GB_PROPERTY_READ("RemotePort", "i", Socket_RemotePort), + GB_PROPERTY_READ("LocalPort", "i", Socket_LocalPort), + GB_PROPERTY_READ("RemoteHost", "s", Socket_RemoteHost), + GB_PROPERTY_READ("LocalHost", "s", Socket_LocalHost), + + GB_PROPERTY("Timeout", "i", Socket_Timeout), + + GB_PROPERTY("Host", "s", Socket_Host), + GB_PROPERTY("Path", "s", Socket_Path), + GB_PROPERTY("Port", "i", Socket_Port), + GB_PROPERTY_READ("Server", "ServerSocket", Socket_Server), + + GB_CONSTANT("_IsControl", "b", TRUE), + GB_CONSTANT("_IsVirtual", "b", TRUE), + GB_CONSTANT("_Group", "s", "Network"), + GB_CONSTANT("_Properties", "s", "Host,Path,Port=80,Timeout{Range:0;3600000;10;ms}"), + GB_CONSTANT("_DefaultEvent", "s", "Read"), + + GB_END_DECLARE +}; diff --git a/gb.net/src/CSocket.h b/gb.net/src/CSocket.h new file mode 100644 index 00000000..64564585 --- /dev/null +++ b/gb.net/src/CSocket.h @@ -0,0 +1,112 @@ +/*************************************************************************** + + CSocket.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSOCKET_H +#define __CSOCKET_H + +#include "gambas.h" +#include "gb_common.h" + +#include +#include +#include +#include +#include +#include +#include +#include "CDnsClient.h" + +#ifndef __CSOCKET_C + +extern GB_DESC CSocketDesc[]; +extern GB_STREAM_DESC SocketStream; + +#else + +#define THIS ((CSOCKET *)_object) +#define SOCKET (&THIS->common) + +#endif + +typedef + struct { + GB_BASE ob; + GB_STREAM stream; + int socket; + int status; + int timeout; + } + CSOCKET_COMMON; + +typedef + struct + { + CSOCKET_COMMON common; + struct sockaddr_in Server; /* struct for TCP connections */ + struct sockaddr_un UServer; /* struct for UNIX connections */ + int iUsePort; + int iPort; + int iLocalPort; + int conn_type; + char *sPath; + char *sLocalHostIP; + char *sRemoteHostIP; + char *Host; + char *Path; + CDNSCLIENT *DnsTool; + void *parent; + void (*OnClose)(void *sck); + bool watch_write; + } + CSOCKET; + +void CSocket_CallBack(int t_sock,int type, CSOCKET *_object); +void CSocket_CallBackConnecting(int t_sock,int type,intptr_t lParam); + +void CSOCKET_init_connected(CSOCKET *_object); + +void CSocket_post_connected(void *_object); +// +int CSocket_connect_unix(void *_object, char *sPath, int lenpath); +int CSocket_connect_socket(void *_object, char *sHost,int lenhost,int myport); +int CSocket_peek_data(void *_object,char **buf,int MaxLen); +// +void CSocket_stream_internal_error(void *_object,int ncode, bool post); +// +int CSocket_stream_read(GB_STREAM *stream, char *buffer, int len); +int CSocket_stream_write(GB_STREAM *stream, char *buffer, int len); +int CSocket_stream_eof(GB_STREAM *stream); +int CSocket_stream_lof(GB_STREAM *stream, int64_t *len); +int CSocket_stream_open(GB_STREAM *stream, const char *path, int mode, void *data); +int CSocket_stream_seek(GB_STREAM *stream, int64_t pos, int whence); +int CSocket_stream_tell(GB_STREAM *stream, int64_t *pos); +int CSocket_stream_flush(GB_STREAM *stream); +int CSocket_stream_close(GB_STREAM *stream); +int CSocket_stream_handle(GB_STREAM *stream); + +bool SOCKET_update_timeout(CSOCKET_COMMON *socket); +void SOCKET_set_blocking(CSOCKET_COMMON *socket, bool block); + +DECLARE_METHOD(Socket_Timeout); + +#endif diff --git a/gb.net/src/CUdpSocket.c b/gb.net/src/CUdpSocket.c new file mode 100644 index 00000000..f3c20909 --- /dev/null +++ b/gb.net/src/CUdpSocket.c @@ -0,0 +1,647 @@ +/*************************************************************************** + + CUdpSocket.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CUDPSOCKET_C + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "tools.h" + +#include "CUdpSocket.h" + +GB_STREAM_DESC UdpSocketStream = { + open: CUdpSocket_stream_open, + close: CUdpSocket_stream_close, + read: CUdpSocket_stream_read, + write: CUdpSocket_stream_write, + seek: CUdpSocket_stream_seek, + tell: CUdpSocket_stream_tell, + flush: CUdpSocket_stream_flush, + eof: CUdpSocket_stream_eof, + lof: CUdpSocket_stream_lof, + handle: CUdpSocket_stream_handle +}; + + +DECLARE_EVENT (CUDPSOCKET_Read); +DECLARE_EVENT (CUDPSOCKET_SocketError); + +void CUdpSocket_post_data(intptr_t Param) +{ + CUDPSOCKET *t_obj; + t_obj=(CUDPSOCKET*)Param; + GB.Raise(t_obj,CUDPSOCKET_Read,0); + GB.Unref(POINTER(&t_obj)); +} +void CUdpSocket_post_error(intptr_t Param) +{ + CUDPSOCKET *t_obj; + t_obj=(CUDPSOCKET*)Param; + GB.Raise(t_obj,CUDPSOCKET_SocketError,0); + GB.Unref(POINTER(&t_obj)); +} + +static void clear_buffer(CUDPSOCKET *_object) +{ + if (THIS->buffer) + { + GB.Free(POINTER(&THIS->buffer)); + THIS->buffer_pos = 0; + THIS->buffer_len = 0; + } +} + +static void fill_buffer(CUDPSOCKET *_object) +{ + socklen_t host_len; + int ret, block; + char buffer[1]; + + //fprintf(stderr, "fill_buffer\n"); + + clear_buffer(THIS); + + host_len = sizeof(THIS->addr); + + block = GB.Stream.Block(&SOCKET->stream, TRUE); + USE_MSG_NOSIGNAL(ret = recvfrom(SOCKET->socket, (void*)buffer, sizeof(char), MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&THIS->addr, &host_len)); + GB.Stream.Block(&SOCKET->stream, block); + + if (ioctl(SOCKET->socket, FIONREAD, &THIS->buffer_len)) + return; + + //fprintf(stderr, "buffer_len = %d\n", THIS->buffer_len); + + if (THIS->buffer_len) + GB.Alloc(POINTER(&THIS->buffer), THIS->buffer_len); + + USE_MSG_NOSIGNAL(ret = recvfrom(SOCKET->socket, (void *)THIS->buffer, THIS->buffer_len, MSG_NOSIGNAL, (struct sockaddr*)&THIS->addr, &host_len)); + + //fprintf(stderr, "recvfrom() -> %d\n", ret); + + if (ret < 0) + { + CUdpSocket_stream_close(&SOCKET->stream); + SOCKET->status = NET_CANNOT_READ; + return; + } + +// THIS->sport = ntohs(host.sin_port); +// GB.FreeString(&THIS->shost); +// GB.NewString (&THIS->shost , inet_ntoa(host.sin_addr) ,0); +} + +void CUdpSocket_CallBack(int t_sock,int type, intptr_t param) +{ + //struct sockaddr_in t_test; + //unsigned int t_test_len; + struct timespec mywait; + CUDPSOCKET *_object = (CUDPSOCKET *)param; + + /* Just sleeping a little to reduce CPU waste */ + mywait.tv_sec=0; + mywait.tv_nsec=100000; + nanosleep(&mywait,NULL); + + if (SOCKET->status <= NET_INACTIVE) return; + + //t_test.sin_port=0; + //t_test_len=sizeof(struct sockaddr); + + //USE_MSG_NOSIGNAL(numpoll=recvfrom(t_sock,POINTER(buf), sizeof(char), MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&t_test, &t_test_len)); + fill_buffer(THIS); + + if (THIS->buffer) + { + GB.Ref((void*)THIS); + GB.Post(CUdpSocket_post_data, (intptr_t)THIS); + } +} +/* not allowed methods */ +int CUdpSocket_stream_open(GB_STREAM *stream, const char *path, int mode, void *data){return -1;} +int CUdpSocket_stream_seek(GB_STREAM *stream, int64_t pos, int whence){return -1;} +int CUdpSocket_stream_tell(GB_STREAM *stream, int64_t *pos) +{ + *pos=0; + return -1; /* not allowed */ +} + +int CUdpSocket_stream_flush(GB_STREAM *stream) +{ + return 0; /* OK */ +} + +int CUdpSocket_stream_handle(GB_STREAM *stream) +{ + void *_object = stream->tag; + return SOCKET->socket; +} + +int CUdpSocket_stream_close(GB_STREAM *stream) +{ + void *_object = stream->tag; + + if ( !_object ) return -1; + stream->desc=NULL; + if (SOCKET->status > NET_INACTIVE) + { + GB.Watch (SOCKET->socket,GB_WATCH_NONE,(void *)CUdpSocket_CallBack,(intptr_t)THIS); + close(SOCKET->socket); + SOCKET->status = NET_INACTIVE; + } + GB.FreeString(&THIS->thost); + GB.FreeString(&THIS->tpath); + + if (THIS->path) + { + unlink(THIS->path); + GB.FreeString(&THIS->path); + } + + THIS->tport=0; + SOCKET->status = NET_INACTIVE; + clear_buffer(THIS); + return 0; +} + +int CUdpSocket_stream_lof(GB_STREAM *stream, int64_t *len) +{ + void *_object = stream->tag; + *len = THIS->buffer_len - THIS->buffer_pos; + return 0; +} + +int CUdpSocket_stream_eof(GB_STREAM *stream) +{ + void *_object = stream->tag; + return THIS->buffer_pos >= THIS->buffer_len; +} + +int CUdpSocket_stream_read(GB_STREAM *stream, char *buffer, int len) +{ + void *_object = stream->tag; + int len_max; + + if ( !_object ) return TRUE; + + len_max = THIS->buffer_len - THIS->buffer_pos; + + if (len_max <= 0) + return TRUE; + + if (len > len_max) + len = len_max; + + memcpy(buffer, &THIS->buffer[THIS->buffer_pos], len); + THIS->buffer_pos += len; + + return len; +} + +int CUdpSocket_stream_write(GB_STREAM *stream, char *buffer, int len) +{ + void *_object = stream->tag; + int retval; + struct in_addr dest_ip; + NET_ADDRESS dest; + size_t size; + struct sockaddr *addr; + + if (!THIS) return -1; + + CLEAR(&dest); + + if (THIS->tpath && *THIS->tpath) + { + dest.un.sun_family = PF_UNIX; + strcpy(dest.un.sun_path, THIS->tpath); + size = sizeof(struct sockaddr_un); + addr = (struct sockaddr *)&dest.un; + } + else + { + /*if (THIS->broadcast) + { + fprintf(stderr, "broadcast\n"); + dest.in.sin_addr.s_addr = INADDR_BROADCAST; + } + else*/ + { + if (!inet_aton((const char*)THIS->thost, &dest_ip)) + return -1; + dest.in.sin_addr.s_addr = dest_ip.s_addr; + } + + dest.in.sin_family = PF_INET; + dest.in.sin_port = htons(THIS->tport); + size = sizeof(struct sockaddr); + addr = (struct sockaddr *)&dest.in; + } + + //fprintf(stderr, "write: %s %d %d '%.*s'\n", THIS->thost, THIS->tport, len, len, buffer); + + USE_MSG_NOSIGNAL(retval = sendto(SOCKET->socket, (void*)buffer, len, MSG_NOSIGNAL, addr, size)); + + if (retval < 0) + { + CUdpSocket_stream_close(stream); + SOCKET->status= NET_CANNOT_WRITE; + } + + return retval; +} + +/************************************************************************************************ +################################################################################################ +--------------------UDPSOCKET CLASS GAMBAS INTERFACE IMPLEMENTATION------------------------------ +################################################################################################ +***********************************************************************************************/ + +static bool update_broadcast(CUDPSOCKET *_object) +{ + if (SOCKET->socket < 0) + return FALSE; + + if (setsockopt(SOCKET->socket, SOL_SOCKET, SO_BROADCAST, (char *)&THIS->broadcast, sizeof(int)) < 0) + { + GB.Error("Cannot set broadcast socket option"); + return TRUE; + } + else + return FALSE; +} + +static void dgram_start(CUDPSOCKET *_object) +{ + sa_family_t domain; + size_t size; + struct stat info; + struct sockaddr *addr; + + if (SOCKET->status > NET_INACTIVE) + { + GB.Error("Socket is active"); + return; + } + + if (THIS->path && *THIS->path) + { + domain = PF_UNIX; + if (strlen(THIS->path) >= NET_UNIX_PATH_MAX) + { + GB.Error("Socket path is too long"); + return; + } + } + else + { + domain = PF_INET; + if (THIS->port < 0 || THIS->port > 65535) + { + GB.Error("Invalid port number"); + return; + } + } + + if ((SOCKET->socket = socket(domain, SOCK_DGRAM, 0)) < 0) + { + SOCKET->status = NET_CANNOT_CREATE_SOCKET; + GB.Ref(THIS); + GB.Post(CUdpSocket_post_error, (intptr_t)THIS); + return; + } + + if (update_broadcast(THIS) || SOCKET_update_timeout(SOCKET)) + { + SOCKET->status = NET_CANNOT_CREATE_SOCKET; + GB.Ref(THIS); + GB.Post(CUdpSocket_post_error,(intptr_t)THIS); + return; + } + + CLEAR(&THIS->addr); + + if (domain == PF_UNIX) + { + if (stat(THIS->path, &info) >= 0 && S_ISSOCK(info.st_mode)) + unlink(THIS->path); + THIS->addr.un.sun_family = domain; + strcpy(THIS->addr.un.sun_path, THIS->path); + size = sizeof(struct sockaddr_un); + addr = (struct sockaddr *)&THIS->addr.un; + } + else + { + THIS->addr.in.sin_family = domain; + THIS->addr.in.sin_addr.s_addr = htonl(INADDR_ANY); + THIS->addr.in.sin_port = htons(THIS->port); + size = sizeof(struct sockaddr_in); + addr = (struct sockaddr *)&THIS->addr.in; + //bzero(&(THIS->addr.in.sin_zero), 8); + } + + if (bind(SOCKET->socket, addr, size) < 0) + { + close(SOCKET->socket); + SOCKET->status = NET_CANNOT_BIND_SOCKET; + GB.Ref(THIS); + GB.Post(CUdpSocket_post_error, (intptr_t)THIS); + return; + } + + SOCKET->status = NET_ACTIVE; + SOCKET->stream.desc = &UdpSocketStream; + GB.Stream.SetSwapping(&SOCKET->stream, htons(0x1234) != 0x1234); + + GB.Watch(SOCKET->socket, GB_WATCH_READ, (void *)CUdpSocket_CallBack, (intptr_t)THIS); +} + + +BEGIN_PROPERTY(CUDPSOCKET_Status) + + GB.ReturnInteger(SOCKET->status); + +END_PROPERTY + + +BEGIN_PROPERTY(CUDPSOCKET_SourceHost) + + if (THIS->addr.a.sa_family == PF_INET) + GB.ReturnNewZeroString(inet_ntoa(THIS->addr.in.sin_addr)); + else + GB.ReturnVoidString(); + +END_PROPERTY + +BEGIN_PROPERTY(CUDPSOCKET_SourcePort) + + if (THIS->addr.a.sa_family == PF_INET) + GB.ReturnInteger(ntohs(THIS->addr.in.sin_port)); + else + GB.ReturnInteger(0); + +END_PROPERTY + +BEGIN_PROPERTY(CUDPSOCKET_SourcePath) + + if (THIS->addr.a.sa_family == PF_UNIX) + GB.ReturnNewZeroString(THIS->addr.un.sun_path); + else + GB.ReturnVoidString(); + +END_PROPERTY + +BEGIN_PROPERTY ( CUDPSOCKET_TargetHost ) + + char *strtmp; + struct in_addr rem_ip; + if (READ_PROPERTY) + { + GB.ReturnString(THIS->thost); + return; + } + + strtmp=GB.ToZeroString(PROP(GB_STRING)); + if ( !inet_aton(strtmp,&rem_ip) ) + { + GB.Error("Invalid IP address"); + return; + } + GB.StoreString(PROP(GB_STRING), &THIS->thost); + +END_PROPERTY + +BEGIN_PROPERTY ( CUDPSOCKET_TargetPort ) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->tport); + else + { + int port = VPROP(GB_INTEGER); + + if (port < 1 || port > 65535) + { + GB.Error("Invalid port number"); + return; + } + + THIS->tport = port; + } + +END_PROPERTY + +BEGIN_PROPERTY(CUDPSOCKET_TargetPath) + + if (READ_PROPERTY) + GB.ReturnString(THIS->tpath); + else + { + if (PLENGTH() >= NET_UNIX_PATH_MAX) + { + GB.Error("Socket path is too long"); + return; + } + GB.StoreString(PROP(GB_STRING), &THIS->tpath); + } + +END_PROPERTY + +/************************************************* +Gambas object "Constructor" +*************************************************/ +BEGIN_METHOD_VOID(CUDPSOCKET_new) + + SOCKET->stream.tag = _object; + SOCKET->socket = -1; + +END_METHOD + +/************************************************* +Gambas object "Destructor" +*************************************************/ +BEGIN_METHOD_VOID(CUDPSOCKET_free) + + CUdpSocket_stream_close(&SOCKET->stream); + +END_METHOD + + +BEGIN_METHOD_VOID (CUDPSOCKET_Peek) + + char *sData=NULL; + socklen_t host_len; + int retval=0; + //int NoBlock=0; + int peeking; + int bytes=0; + if (SOCKET->status <= NET_INACTIVE) + { + GB.Error("Socket is inactive"); + return; + } + + peeking = MSG_NOSIGNAL | MSG_PEEK; + + ioctl(SOCKET->socket,FIONREAD,&bytes); + if (bytes) + { + GB.Alloc( POINTER(&sData),bytes*sizeof(char) ); + host_len = sizeof(THIS->addr); + //ioctl(SOCKET->socket,FIONBIO,&NoBlock); + USE_MSG_NOSIGNAL(retval=recvfrom(SOCKET->socket, (void*)sData, 1024 * sizeof(char), peeking, (struct sockaddr*)&THIS->addr, &host_len)); + if (retval<0) + { + GB.Free(POINTER(&sData)); + CUdpSocket_stream_close(&SOCKET->stream); + SOCKET->status = NET_CANNOT_READ; + GB.Raise(THIS,CUDPSOCKET_SocketError,0); + GB.ReturnVoidString(); + return; + } + //NoBlock++; + //ioctl(SOCKET->socket,FIONBIO,&NoBlock); + if (retval>0) + GB.ReturnNewString(sData,retval); + else + GB.ReturnVoidString(); + GB.Free(POINTER(&sData)); + } + else + { + GB.ReturnVoidString(); + } + +END_METHOD + +BEGIN_METHOD_VOID(CUDPSOCKET_Bind) + + dgram_start(THIS); + +END_METHOD + + +BEGIN_PROPERTY(CUDPSOCKET_broadcast) + + if (READ_PROPERTY) + { + GB.ReturnBoolean(THIS->broadcast); + } + else + { + THIS->broadcast = VPROP(GB_BOOLEAN); + update_broadcast(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(CUDPSOCKET_Port) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->port); + else + { + int port = VPROP(GB_INTEGER); + if (port < 0 || port > 65535) + { + GB.Error("Invalid port value"); + return; + } + if (SOCKET->status > NET_INACTIVE) + { + GB.Error("Socket is active"); + return; + } + THIS->port = port; + } + +END_PROPERTY + +BEGIN_PROPERTY(CUDPSOCKET_Path) + + if (READ_PROPERTY) + GB.ReturnString(THIS->path); + else + { + if (SOCKET->status > NET_INACTIVE) + { + GB.Error("Socket is active"); + return; + } + GB.StoreString(PROP(GB_STRING), &THIS->path); + } + +END_PROPERTY + + +/*************************************************************** +Here we declare the public interface of UdpSocket class +***************************************************************/ +GB_DESC CUdpSocketDesc[] = +{ + GB_DECLARE("UdpSocket", sizeof(CUDPSOCKET)), + + GB_INHERITS("Stream"), + + GB_EVENT("Error", NULL, NULL, &CUDPSOCKET_SocketError), + GB_EVENT("Read", NULL, NULL, &CUDPSOCKET_Read), + + GB_METHOD("_new", NULL, CUDPSOCKET_new, NULL), + GB_METHOD("_free", NULL, CUDPSOCKET_free, NULL), + GB_METHOD("Bind", NULL, CUDPSOCKET_Bind, NULL), + GB_METHOD("Peek","s",CUDPSOCKET_Peek,NULL), + + GB_PROPERTY_READ("Status", "i", CUDPSOCKET_Status), + GB_PROPERTY_READ("SourceHost", "s", CUDPSOCKET_SourceHost), + GB_PROPERTY_READ("SourcePort", "i", CUDPSOCKET_SourcePort), + GB_PROPERTY_READ("SourcePath", "i", CUDPSOCKET_SourcePath), + GB_PROPERTY("TargetHost", "s", CUDPSOCKET_TargetHost), + GB_PROPERTY("TargetPort", "i", CUDPSOCKET_TargetPort), + GB_PROPERTY("TargetPath", "s", CUDPSOCKET_TargetPath), + + GB_PROPERTY("Port", "i", CUDPSOCKET_Port), + GB_PROPERTY("Path", "s", CUDPSOCKET_Path), + + GB_PROPERTY("Broadcast", "b", CUDPSOCKET_broadcast), + GB_PROPERTY("Timeout", "i", Socket_Timeout), + + GB_CONSTANT("_IsControl", "b", TRUE), + GB_CONSTANT("_IsVirtual", "b", TRUE), + GB_CONSTANT("_Group", "s", "Network"), + GB_CONSTANT("_Properties", "s", "Port{Range:0;65535},Path,TargetHost,TargetPort,Broadcast"), + GB_CONSTANT("_DefaultEvent", "s", "Read"), + + GB_END_DECLARE +}; + + diff --git a/gb.net/src/CUdpSocket.h b/gb.net/src/CUdpSocket.h new file mode 100644 index 00000000..dfb2e568 --- /dev/null +++ b/gb.net/src/CUdpSocket.h @@ -0,0 +1,72 @@ +/*************************************************************************** + + CUdpSocket.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CUDPSOCKET_H +#define __CUDPSOCKET_H + +#include "gambas.h" +#include "CNet.h" +#include "CSocket.h" + +#ifndef __CUDPSOCKET_C + +extern GB_DESC CUdpSocketDesc[]; +extern GB_STREAM_DESC UdpSocketStream; + +#else + +#define THIS ((CUDPSOCKET *)_object) +#define SOCKET (&THIS->common) + +#endif + +typedef + struct + { + CSOCKET_COMMON common; + NET_ADDRESS addr; + int iPort; + char *thost; + int tport; + char *tpath; + int broadcast; + char *buffer; + int buffer_pos; + int buffer_len; + char *path; + int port; + } + CUDPSOCKET; + +int CUdpSocket_stream_read(GB_STREAM *stream, char *buffer, int len); +int CUdpSocket_stream_write(GB_STREAM *stream, char *buffer, int len); +int CUdpSocket_stream_eof(GB_STREAM *stream); +int CUdpSocket_stream_lof(GB_STREAM *stream, int64_t *len); +int CUdpSocket_stream_open(GB_STREAM *stream, const char *path, int mode, void *data); +int CUdpSocket_stream_seek(GB_STREAM *stream, int64_t pos, int whence); +int CUdpSocket_stream_tell(GB_STREAM *stream, int64_t *pos); +int CUdpSocket_stream_flush(GB_STREAM *stream); +int CUdpSocket_stream_close(GB_STREAM *stream); +int CUdpSocket_stream_handle(GB_STREAM *stream); + +#endif diff --git a/gb.net/src/Makefile.am b/gb.net/src/Makefile.am new file mode 100644 index 00000000..5081287b --- /dev/null +++ b/gb.net/src/Makefile.am @@ -0,0 +1,16 @@ +COMPONENT = gb.net +include $(top_srcdir)/component.am + +EXTRA_DIST = doc + +gblib_LTLIBRARIES = gb.net.la + +gb_net_la_LIBADD = @NET_LIB@ +gb_net_la_LDFLAGS = -module @LD_FLAGS@ @NET_LDFLAGS@ +gb_net_la_CPPFLAGS = @NET_INC@ + +gb_net_la_SOURCES = gb_network.h main.h main.c tools.h tools.c CDnsClient.h CDnsClient.c CSocket.h CSocket.c CServerSocket.h CServerSocket.c CUdpSocket.h CUdpSocket.c CSerialPort.h CSerialPort.c CNet.h CNet.c + + + + diff --git a/gb.net/src/doc/README b/gb.net/src/doc/README new file mode 100644 index 00000000..2024770b --- /dev/null +++ b/gb.net/src/doc/README @@ -0,0 +1,146 @@ +Net Advanced Component +---------------------- + +WARNING :READ THIS DOCUMENT CAREFULLY AS IT CONTAINS IMPORTANT +INFORMATION ABOUT NET AND NET-ADVENCED COMPONENTS + + +New network structure for Gambas +-------------------------------- + + Previous versions of 'net' component (included from Gambas-0.73 to +Gambas 0.80) were a simple package in which there was the following +classes: + +- Socket +- ServerSocket +- UdpSocket +- SerialPort +- DnsClient +- HttpClient + + 1) The first five classes listed, could be considered the basic stuff +or low-level stuff to manage communications, but HttpClient is in the +next floor,(levels 4-5) according to ISO/OSI model. + 2) In the future there will be new classes : FtpClient, TelnetClient... + 3) HTTP, FTP , TELNET , etc protocols, have a lot of features, and +it is hard to implement all stuff using just standard Unix C libraries, +so it would take a lot of time to implement it. + 4) The answer to that problem is libcurl ( http://curl.haxx.se/ ), which +can manage a lot of protocols, and is designed to be multi-platform. + 5) So, the new implementation of 'net' is the following: + +'Net' class: + +- Socket +- ServerSocket +- UdpSocket +- SerialPort +- DnsClient + + This class does not need libcurl to work, as it uses just Unix C libraries, +if you don't need to manage HTTP, FTP, etc, you don't need more. + +'Net Advanced' class: + +- HttpClient +... (more in the future) + + It uses libcurl to perform its work, so you need to download and install +libcurl in your computer to use it. You'll need binary libraries, and, if +you want to compile it, you'll need also devel stuff, that contains headers +and config tools to compile a program that uses libcurl. + + You'll need at least 7.10.8 version from libcurl and Gambas 0.80 (previous +versions from both programs probably won't work or even compile). + +You have to ways to install libcurl: + +* You can compile it from sources, once copiled it will provide you both +binaries and headers. +* You can download it in binary format, but remember you have to select +a version of binary package that contains all devel stuff. + +You can find both sources and binaries from: + +http://curl.haxx.se/download.html + +it is almost sure that your O.S. will be supported! + +Remember also, that if you want SSL support, libcurl uses OpenSSL, so you +need also to download and install that package: http://www.openssl.org/ is +tha main page, but probably you will find packages for you own distribution, +and even for Windows. + +COMPILING THE COMPONENTS +------------------------ + +You need gambas-0.80 source package, download and uncompress it. + +- Go to [Sources]/src/lib, delete the old 'net' folder, and place the new +'net' folder instead. +- Go to [Sources] root, delete the old 'configure.in' file, and place +the new 'configure.in' that is in current folder with this document. +- Into [Sources] root, type: + +./reconf + +- Now, type + +./configure + +(adding all parameters you need, as usual, for example:) + +./configure --prefix=/opt --disable-mysql-driver + +To compile all Gambas package type: + +make + +and then, to install + +make install + +If you just want to compile the new 'net' and 'net advanced' components, + +go to [Sources]/src/lib/net + +and type + +./make + +to compile them and + +./make install + +to install it + +Probably you will need to do also: + +gbi gb.net > /opt/lib/info/gb.net.info + +gbi gb.net.advanced > /opt/lib/info/gb.net.advanced.info + +(if you installed Gambas in other prefix different than /opt, +you have to select the right folder, for instance, for /usr/local +as prefix, type gbi gb.net > /usr/local/info/gb.net.info ...) + + + +KNOWN PROBLEMS +-------------- + + +- This is an Alpha component, and sure this is a problem! :) + +- Actuallly (7.10.8) libcurl does not perform DNS requests in asynchronous mode, +so your Gambas application will not respond to events until this part +of the connection process has finished (they say they will correct it in +future versions) + +- The rest of bugs listed in http://curl.haxx.se/docs/knownbugs.html + +- The bugs added by me to adding libcurl to Gambas! + + + \ No newline at end of file diff --git a/gb.net/src/doc/changes.txt b/gb.net/src/doc/changes.txt new file mode 100644 index 00000000..2a8649d1 --- /dev/null +++ b/gb.net/src/doc/changes.txt @@ -0,0 +1,87 @@ +Net changes +----------- + +* Now net are two components: + + - 'net' : basic stuff + - 'advanced net' : classes that needs libcurl to work (HttpClient at this moment) + +* Now there's a new class 'NetCode' in which you'll find all constants that were placed +in all the rest of classes in previous versions (status, error codes, etc) + +* 'Network advanced' has also a class to do the same as 'NetCode', called 'AdvancedCode' +which will include all constants needed for HTTP,FTP, etc + +The new HttpClient +------------------ + +* Host, Document and Port properties do not exist anymore, now, there's a +property called "URL" in which you can put all data in url format, some +examples: + +127.0.0.1 + +http://localhost/index.html + +yntp://localhost/index.html <-- the 'wrong' part "yntp" will be ignored + +http://stratoria.dynu.net:85/Actualites.html <-- port 85 instead of default 80 + +* Cookies: + + - CookiesFile : if you place here a zero string (""), HttpClient will not manage + cookies (default), but if you place here the path to a file containing cookies + in plain, netscape or mozilla format, it will use use them and it will also manage cookies + from current session in memory. If you place the path to a non existing or not readable + file, it will also manage cookies from current session in memory. By default, cookies from + current session are deleted once you free the object or call Stop() method + + - UpdateCookies : by default, cookie file (if any) is not updated with the new cookies + arrived from current session, so they're lost when you free the object or call 'Stop' + method. If you set this value to TRUE, cookies file will be updated with the new cookies + once you free the object or call to 'Stop' method + +* Stop method : previous versions of 'HttpClient' were working with 'connection : close' header, +so once a document was received from server, connection with server was broken. Now it uses +'connection : keep-alive', that means that (if server accepts it), connection will be alive to +reuse it to receive new documents from that server, with less waste of resources. Now, if you +use 'Stop' method when status is 0 (Inactive), connection with server will be closed if it +was alive, and cookies will be also stored in a file if UpdateCookies=TRUE and CookiesFile is +set to a valid path + +* 'Receive' method exists no more, as now, HttpClient inherits from '.Stream', so you can use + 'READ' standard method instead ('READ' is the only stream method that owrks, at this moment) + +* 'DataHeader' property has been replaced, now it exists as: + + Property Headers As String[] + + Each string in the returned array contains one line of that header + +* 'Get' and 'Put' methods have changed : Now they are: + + Get (Optional TargetFile As String) + + - if you do not use 'TargetFile' as parameter, you will receive the URL requested as usual: when + data is coming from server, HttpClient saves it in memory until you use 'READ' stream method, + and each time new data arrives, 'HttpClient.Read' event raises. + + - if you use 'TargetFile' as parameter, requested URL will be saved in the local file you have + specified as parameter, instead of saving it in memory, and you will not receive 'HttpClient.Read' events. + + Put(ContentType As String,Data As String, Optional TargetFile As String) + +* User and authentication : + + - User : the name and password of the user to access to HTTP server, you must set it + in user:password format, for example User="myself:mypass" + - Auth : authentication type supported by HTTP server : basic, NTLM, Digest, GSS + +* Proxy support : now HttpClient can manage authentication : + + - Proxy : URL from Proxy, for instance : 192.168.0.1:8080 + - ProxyType : can be a HTTP proxy, or a Socks-5 proxy + - ProxyAuth : authentication supported by Proxy : Basic or NTLM + + + diff --git a/gb.net/src/doc/threading.sxw b/gb.net/src/doc/threading.sxw new file mode 100644 index 0000000000000000000000000000000000000000..896748028b424178b7003cd87e0f1df3b9c48dfa GIT binary patch literal 8432 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 literal 0 HcmV?d00001 diff --git a/gb.net/src/gb.net.component b/gb.net/src/gb.net.component new file mode 100644 index 00000000..13d2d101 --- /dev/null +++ b/gb.net/src/gb.net.component @@ -0,0 +1,12 @@ +[Component] +Key=gb.net +Name=Networking component +Name[es]=Componente de red +Name[fr]=Composant d'accès au réseau +Name[pl]=Komponent sieci +Name[tr]=Ağ bileşeni +Author=Daniel Campos Fernández + +[Network] +Virtual=DnsClient,ServerSocket,SerialPort,Socket,UdpSocket + diff --git a/gb.net/src/gb_network.h b/gb.net/src/gb_network.h new file mode 100644 index 00000000..0a836fdd --- /dev/null +++ b/gb.net/src/gb_network.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + gb_network.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CSocket.h" + +typedef + struct + { + struct + { + int (*ConnectLocal)(void *_object, char *sPath, int lenpath); + int (*ConnectTCP)(void *_object, char *sHost, int lenhost, int myport); + int (*Peek)(void *_object, char **buf, int MaxLen); + } Socket; + } + NETWORK_INTERFACE; + diff --git a/gb.net/src/main.c b/gb.net/src/main.c new file mode 100644 index 00000000..4974e1de --- /dev/null +++ b/gb.net/src/main.c @@ -0,0 +1,67 @@ +/*************************************************************************** + + main.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include + +#include "CUdpSocket.h" +#include "CDnsClient.h" +#include "CSocket.h" +#include "CServerSocket.h" +#include "CSerialPort.h" +#include "CNet.h" +#include "gb_network.h" +#include "main.h" + + +NETWORK_INTERFACE NET EXPORT; + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CDnsClientDesc, + CSocketDesc, + CServerSocketDesc, + CUdpSocketDesc, + CSerialPortDesc, + CNetDesc, + NULL +}; + + +int EXPORT GB_INIT(void) +{ + NET.Socket.ConnectLocal=CSocket_connect_unix; + NET.Socket.ConnectTCP=CSocket_connect_socket; + NET.Socket.Peek=CSocket_peek_data; + + return dns_init(); +} + +void EXPORT GB_EXIT() +{ + dns_exit(); +} + diff --git a/gb.net/src/main.h b/gb.net/src/main.h new file mode 100644 index 00000000..47cad6d0 --- /dev/null +++ b/gb.net/src/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/gb.net/src/tools.c b/gb.net/src/tools.c new file mode 100644 index 00000000..0fd2296b --- /dev/null +++ b/gb.net/src/tools.c @@ -0,0 +1,427 @@ +/*************************************************************************** + + tools.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "tools.h" +#include "main.h" +#include "gambas.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __CYGWIN__ +#define FIONREAD TIOCINQ +#endif + +void correct_url(char **buf,char *protocol) +{ + char *buftmp; + int len; + int myloop,myloop2; + int pos=-1; + int myok=1; + + len=strlen(*buf); + for (myloop=0;myloop0x39) ) + { + myok=0; + break; + } + } + if (!myok) pos=myloop; + break; + } + } + } + } + + myok=0; + + if (pos==-1) + { + + GB.Alloc(POINTER(&buftmp),len+1); + strcpy(buftmp,*buf); + GB.Free(POINTER(buf)); + GB.Alloc(POINTER(buf),len+strlen(protocol)+1); + strcpy(*buf,protocol); + if (strlen(buftmp)>=2) + { + if ( buftmp[0]=='/') myok++; + if ( buftmp[1]=='/') myok++; + } + strcat(*buf,buftmp+myok); + GB.Free(POINTER(&buftmp)); + } + else + { + GB.Alloc(POINTER(&buftmp),(len-pos)+1); + strcpy(buftmp,*buf+pos+1); + GB.Free(POINTER(buf)); + GB.Alloc(POINTER(buf),strlen(buftmp)+strlen(protocol)+1); + strcpy(*buf,protocol); + if (strlen(buftmp)>=2) + { + if ( buftmp[0]=='/') myok++; + if ( buftmp[1]=='/') myok++; + } + strcat(*buf,buftmp+myok); + GB.Free(POINTER(&buftmp)); + } +} + +void Alloc_CallBack_Pointers(long nobjs,long **objs,long **scks) +{ + if (!nobjs) + { + if (*objs) + { + GB.Free((void**)objs); + GB.Free((void**)scks); + *objs=NULL; + } + return; + } + if (*objs) + { + GB.Realloc((void**)objs,nobjs*sizeof(long)); + GB.Realloc((void**)scks,nobjs*sizeof(long)); + } + else + { + GB.Alloc((void**)objs,sizeof(long)); + GB.Alloc((void**)scks,sizeof(long)); + } +} + +int CheckConnection(int Socket) +{ + struct pollfd mypoll; + int numpoll; + int retval=6; + + mypoll.fd=Socket; + mypoll.events=POLLERR; + mypoll.revents=0; + numpoll=poll(&mypoll,1,0); + if (numpoll>=0) + { + if (!numpoll) + { + mypoll.fd=Socket; + mypoll.events=POLLIN | POLLOUT; + mypoll.revents=0; + numpoll=poll(&mypoll,1,0); + if (numpoll<0) + { + retval=0; + } + else + { + if (numpoll>0) retval=7; + } + } + else + { + retval=0; + } + + } + else + { + retval=0; + + } + return retval; +} + +/* free "buf" after using it! */ +int IsHostPath(char *sCad, int lenCad, char **buf,int *port) +{ + /******************* + 0 --> Error + 1 --> TCP + 2 -> Unix + *******************/ + int npos=0; + int npoint=0; + int myloop; + int bufport=0; + *port=0; + *buf=NULL; + if ( sCad[0] == '/' ) + { + return 2; + } + for (myloop=0;myloop'9') ) return 0; + bufport*=10; + bufport+= (sCad[myloop]-48); + if ( (bufport) >65535) return 0; + } + *port=bufport; + if (npos>0) + { + GB.Alloc((void**)buf,npos); + *buf[0]=0; + sCad[npos]=0; + strcpy(*buf,sCad); + sCad[npos]=':'; + } + return 1; + +} +/******************************************************** + Watching stuff + ********************************************************/ +int search_by_integer(long *objlist,long nobj,long iData) +{ + int myloop; + int position=0; + for (myloop=0;myloop + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include + +#ifdef __sun__ + + #include + #include + +#endif + +#ifdef MSG_PEEK +#ifdef MSG_NOSIGNAL + + #define USE_MSG_NOSIGNAL(_code) _code + +#else + + #include + #define MSG_NOSIGNAL 0 + #define USE_MSG_NOSIGNAL(_code) \ + { \ + void (*_oldsigpipe)(int) = signal(SIGPIPE, SIG_IGN); \ + _code ; \ + signal(SIGPIPE, _oldsigpipe); \ + } + +#endif +#endif + +void Alloc_CallBack_Pointers(long nobjs,long **objs,long **scks); + +int search_by_integer(long *objlist,long nobj,long iData); + +int CheckConnection(int Socket); +int IsHostPath(char *sCad, int lenCad, char **buf,int *port); + +void correct_url(char **buf,char *protocol); +int ConvertBaudRate(int nBauds); +int ConvertStopBits(int nStop); +int ConvertDataBits(int nBits); +int ConvertParity(int parity); +void CloseSerialPort(int fd,struct termios *oldtio); +int OpenSerialPort(int *fd,int iflow,struct termios *oldtio, \ + char *sName,int nBauds,int parity,int nBits,int nStop); diff --git a/gb.openal/AUTHORS b/gb.openal/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.openal/COPYING b/gb.openal/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.openal/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.openal/ChangeLog b/gb.openal/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.openal/INSTALL b/gb.openal/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.openal/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.openal/Makefile.am b/gb.openal/Makefile.am new file mode 100644 index 00000000..e0ebd8ce --- /dev/null +++ b/gb.openal/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @OPENAL_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.openal/NEWS b/gb.openal/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.openal/README b/gb.openal/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.openal/acinclude.m4 b/gb.openal/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.openal/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.openal/component.am b/gb.openal/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.openal/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.openal/configure.ac b/gb.openal/configure.ac new file mode 100644 index 00000000..5f3fe96f --- /dev/null +++ b/gb.openal/configure.ac @@ -0,0 +1,17 @@ +dnl ---- configure.ac for gb.openal + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-openal, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.openal) +AC_PROG_LIBTOOL + +GB_COMPONENT_PKG_CONFIG( + openal, + OPENAL, + gb.openal, + [src], + ['openal >= 1.13' alure]) +AC_OUTPUT( Makefile src/Makefile ) +GB_PRINT_MESSAGES diff --git a/gb.openal/gambas.h b/gb.openal/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.openal/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.openal/gb_common.h b/gb.openal/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.openal/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.openal/m4 b/gb.openal/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.openal/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.openal/reconf b/gb.openal/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.openal/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.openal/src/Makefile.am b/gb.openal/src/Makefile.am new file mode 100644 index 00000000..506c2568 --- /dev/null +++ b/gb.openal/src/Makefile.am @@ -0,0 +1,10 @@ +COMPONENT = gb.openal +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.openal.la + +gb_openal_la_LIBADD = @OPENAL_LIB@ +gb_openal_la_LDFLAGS = -module @LD_FLAGS@ @OPENAL_LDFLAGS@ +gb_openal_la_CPPFLAGS = @OPENAL_INC@ + +gb_openal_la_SOURCES = main.c main.h c_al.h c_al.c c_alc.h c_alc.c c_alure.h c_alure.c diff --git a/gb.openal/src/c_al.c b/gb.openal/src/c_al.c new file mode 100644 index 00000000..e7347b76 --- /dev/null +++ b/gb.openal/src/c_al.c @@ -0,0 +1,673 @@ +/*************************************************************************** + + c_al.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_AL_C + +#include "c_al.h" + +/* + * LISTENER + * Listener represents the location and orientation of the + * 'user' in 3D-space. + * + * Properties include: - + * + * Gain AL_GAIN ALfloat + * Position AL_POSITION ALfloat[3] + * Velocity AL_VELOCITY ALfloat[3] + * Orientation AL_ORIENTATION ALfloat[6] (Forward then Up vectors) +*/ + +static int get_listener_float_param_size(int param) +{ + switch(param) + { + case AL_GAIN: return 1; + case AL_POSITION: return 3; + case AL_VELOCITY: return 3; + case AL_ORIENTATION: return 6; + default: return 0; + } +} + +static int get_listener_integer_param_size(int param) +{ + return 0; +} + +/* + * SOURCE + * Sources represent individual sound objects in 3D-space. + * Sources take the PCM data provided in the specified Buffer, + * apply Source-specific modifications, and then + * submit them to be mixed according to spatial arrangement etc. + * + * Properties include: - + * + * Gain AL_GAIN ALfloat + * Min Gain AL_MIN_GAIN ALfloat + * Max Gain AL_MAX_GAIN ALfloat + * Position AL_POSITION ALfloat[3] + * Velocity AL_VELOCITY ALfloat[3] + * Direction AL_DIRECTION ALfloat[3] + * Head Relative Mode AL_SOURCE_RELATIVE ALint (AL_TRUE or AL_FALSE) + * Reference Distance AL_REFERENCE_DISTANCE ALfloat + * Max Distance AL_MAX_DISTANCE ALfloat + * RollOff Factor AL_ROLLOFF_FACTOR ALfloat + * Inner Angle AL_CONE_INNER_ANGLE ALint or ALfloat + * Outer Angle AL_CONE_OUTER_ANGLE ALint or ALfloat + * Cone Outer Gain AL_CONE_OUTER_GAIN ALint or ALfloat + * Pitch AL_PITCH ALfloat + * Looping AL_LOOPING ALint (AL_TRUE or AL_FALSE) + * MS Offset AL_MSEC_OFFSET ALint or ALfloat + * Byte Offset AL_BYTE_OFFSET ALint or ALfloat + * Sample Offset AL_SAMPLE_OFFSET ALint or ALfloat + * Attached Buffer AL_BUFFER ALint + * State (Query only) AL_SOURCE_STATE ALint + * Buffers Queued (Query only) AL_BUFFERS_QUEUED ALint + * Buffers Processed (Query only) AL_BUFFERS_PROCESSED ALint + */ + +static int get_source_float_param_size(int param) +{ + switch(param) + { + case AL_GAIN: case AL_MIN_GAIN: case AL_MAX_GAIN: case AL_REFERENCE_DISTANCE: case AL_MAX_DISTANCE: case AL_ROLLOFF_FACTOR: + case AL_CONE_INNER_ANGLE: case AL_CONE_OUTER_ANGLE: case AL_CONE_OUTER_GAIN: case AL_PITCH: case AL_BYTE_OFFSET: + case AL_SAMPLE_OFFSET: + //case AL_MSEC_OFFSET: + return 1; + + case AL_POSITION: case AL_VELOCITY: case AL_DIRECTION: + return 3; + + default: + return 0; + } +} + +static int get_source_integer_param_size(int param) +{ + switch(param) + { + case AL_SOURCE_RELATIVE: case AL_CONE_INNER_ANGLE: case AL_CONE_OUTER_ANGLE: case AL_CONE_OUTER_GAIN: case AL_LOOPING: + //case AL_MSEC_OFFSET: + case AL_BYTE_OFFSET: case AL_SAMPLE_OFFSET: case AL_BUFFER: case AL_SOURCE_STATE: case AL_BUFFERS_QUEUED: case AL_BUFFERS_PROCESSED: + return 1; + + default: + return 0; + } +} + +/* + * BUFFER + * Buffer objects are storage space for sample data. + * Buffers are referred to by Sources. One Buffer can be used + * by multiple Sources. + * + * Properties include: - + * + * Frequency (Query only) AL_FREQUENCY ALint + * Size (Query only) AL_SIZE ALint + * Bits (Query only) AL_BITS ALint + * Channels (Query only) AL_CHANNELS ALint + */ + +static int get_buffer_float_param_size(int param) +{ + return 0; +} + +static int get_buffer_integer_param_size(int param) +{ + switch(param) + { + case AL_FREQUENCY: case AL_SIZE: case AL_BITS: case AL_CHANNELS: + return 1; + + default: + return 0; + } +} + + +//--------------------------------------------------------------------------- + + +BEGIN_METHOD_VOID(AL_GetError) + + GB.ReturnInteger(alGetError()); + +END_METHOD + +BEGIN_METHOD(AL_Enable, GB_INTEGER cap) + + alEnable(VARG(cap)); + +END_METHOD + +BEGIN_METHOD(AL_Disable, GB_INTEGER cap) + + alDisable(VARG(cap)); + +END_METHOD + +BEGIN_METHOD(AL_IsEnabled, GB_INTEGER cap) + + GB.ReturnBoolean(alIsEnabled(VARG(cap))); + +END_METHOD + +BEGIN_METHOD(AL_GenBuffers, GB_INTEGER count) + + int count = VARG(count); + GB_ARRAY array; + + if (count <= 0) + { + GB.ReturnNull(); + return; + } + + GB.Array.New(&array, GB_T_INTEGER, count); + alGenBuffers(VARG(count), (ALuint *)GB.Array.Get(array, 0)); + GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(AL_GenSources, GB_INTEGER count) + + int count = VARG(count); + GB_ARRAY array; + + if (count <= 0) + { + GB.ReturnNull(); + return; + } + + GB.Array.New(&array, GB_T_INTEGER, count); + alGenSources(VARG(count), (ALuint *)GB.Array.Get(array, 0)); + GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(AL_DeleteBuffers, GB_OBJECT array) + + GB_ARRAY array = (GB_ARRAY)VARG(array); + int count; + + if (GB.CheckObject(array)) + return; + + count = GB.Array.Count(array); + if (count <= 0) + return; + + alDeleteBuffers(count, GB.Array.Get(array, 0)); + +END_METHOD + +BEGIN_METHOD(AL_DeleteSources, GB_OBJECT array) + + GB_ARRAY array = (GB_ARRAY)VARG(array); + int count; + + if (GB.CheckObject(array)) + return; + + count = GB.Array.Count(array); + if (count <= 0) + return; + + alDeleteSources(count, GB.Array.Get(array, 0)); + +END_METHOD + +BEGIN_METHOD(AL_IsBuffer, GB_INTEGER name) + + GB.ReturnBoolean(alIsBuffer(VARG(name))); + +END_METHOD + +BEGIN_METHOD(AL_IsSource, GB_INTEGER name) + + GB.ReturnBoolean(alIsSource(VARG(name))); + +END_METHOD + +BEGIN_METHOD(AL_GetBoolean, GB_INTEGER param) + + GB.ReturnBoolean(alGetBoolean(VARG(param))); + +END_METHOD + +BEGIN_METHOD(AL_GetInteger, GB_INTEGER param) + + GB.ReturnInteger(alGetInteger(VARG(param))); + +END_METHOD + +BEGIN_METHOD(AL_GetFloat, GB_INTEGER param) + + GB.ReturnSingle(alGetFloat(VARG(param))); + +END_METHOD + +BEGIN_METHOD(AL_GetDouble, GB_INTEGER param) + + GB.ReturnFloat(alGetDouble(VARG(param))); + +END_METHOD + +BEGIN_METHOD(AL_GetString, GB_INTEGER param) + + GB.ReturnNewZeroString(alGetString(VARG(param))); + +END_METHOD + +BEGIN_METHOD(AL_IsExtensionPresent, GB_STRING name) + + GB.ReturnBoolean(alIsExtensionPresent(GB.ToZeroString(ARG(name)))); + +END_METHOD + +BEGIN_METHOD(AL_GetEnumValue, GB_STRING name) + + GB.ReturnInteger(alGetEnumValue(GB.ToZeroString(ARG(name)))); + +END_METHOD + +BEGIN_METHOD(AL_DistanceModel, GB_INTEGER model) + + alDistanceModel(VARG(model)); + +END_METHOD + +BEGIN_METHOD(AL_DopplerFactor, GB_FLOAT factor) + + alDopplerFactor((float)VARG(factor)); + +END_METHOD + +BEGIN_METHOD(AL_DopplerVelocity, GB_FLOAT velocity) + + alDopplerVelocity((float)VARG(velocity)); + +END_METHOD + +BEGIN_METHOD(AL_SpeedOfSound, GB_FLOAT speed) + + alSpeedOfSound((float)VARG(speed)); + +END_METHOD + +#define IMPLEMENT_X(_name, _type) \ +BEGIN_METHOD(AL_##_name, ID_PARAM GB_INTEGER param; _type value) \ + al##_name(ID_ARG VARG(param), VARG(value)); \ +END_METHOD + +#define IMPLEMENT_3X(_name, _type) \ +BEGIN_METHOD(AL_##_name, ID_PARAM GB_INTEGER param; _type value1; _type value2; _type value3) \ + al##_name(ID_ARG VARG(param), VARG(value1), VARG(value2), VARG(value3)); \ +END_METHOD + +#define IMPLEMENT_XV(_name, _type) \ +BEGIN_METHOD(AL_##_name, ID_PARAM GB_INTEGER param; GB_OBJECT array) \ + GB_ARRAY array = VARG(array); \ + if (GB.CheckObject(array)) \ + return; \ + al##_name(ID_ARG VARG(param), GB.Array.Get(array, 0)); \ +END_METHOD + +#define IMPLEMENT_GET_X(_name, _return, _ctype) \ +BEGIN_METHOD(AL_##_name, ID_PARAM GB_INTEGER param) \ + _ctype val; \ + al##_name(ID_ARG VARG(param), &val); \ + GB.Return##_return(val); \ +END_METHOD + +#define IMPLEMENT_GET_XV(_name, _type, _get_size) \ +BEGIN_METHOD(AL_##_name, ID_PARAM GB_INTEGER param) \ + GB_ARRAY array; \ + int size = _get_size(VARG(param)); \ + if (size == 0) \ + GB.ReturnNull(); \ + else \ + { \ + GB.Array.New(&array, _type, size); \ + al##_name(ID_ARG VARG(param), GB.Array.Get(array, 0)); \ + GB.ReturnObject(array); \ + } \ +END_METHOD + +#define ID_PARAM +#define ID_ARG + +IMPLEMENT_X(Listenerf, GB_SINGLE) +IMPLEMENT_3X(Listener3f, GB_SINGLE) +IMPLEMENT_XV(Listenerfv, GB_SINGLE) +IMPLEMENT_X(Listeneri, GB_INTEGER) +IMPLEMENT_3X(Listener3i, GB_INTEGER) +IMPLEMENT_XV(Listeneriv, GB_INTEGER) + +IMPLEMENT_GET_X(GetListenerf, Single, ALfloat) +IMPLEMENT_GET_XV(GetListenerfv, GB_T_SINGLE, get_listener_float_param_size) +IMPLEMENT_GET_X(GetListeneri, Integer, ALint) +IMPLEMENT_GET_XV(GetListeneriv, GB_T_INTEGER, get_listener_integer_param_size) + +#undef ID_PARAM +#define ID_PARAM GB_INTEGER id; +#undef ID_ARG +#define ID_ARG VARG(id), + +IMPLEMENT_X(Sourcef, GB_SINGLE) +IMPLEMENT_3X(Source3f, GB_SINGLE) +IMPLEMENT_XV(Sourcefv, GB_SINGLE) +IMPLEMENT_X(Sourcei, GB_INTEGER) +IMPLEMENT_3X(Source3i, GB_INTEGER) +IMPLEMENT_XV(Sourceiv, GB_INTEGER) + +IMPLEMENT_GET_X(GetSourcef, Single, ALfloat) +IMPLEMENT_GET_XV(GetSourcefv, GB_T_SINGLE, get_source_float_param_size) +IMPLEMENT_GET_X(GetSourcei, Integer, ALint) +IMPLEMENT_GET_XV(GetSourceiv, GB_T_INTEGER, get_source_integer_param_size) + +#define IMPLEMENT_ACTION(_name) \ +BEGIN_METHOD(AL_##_name, GB_INTEGER id) \ + al##_name(VARG(id)); \ +END_METHOD + +#define IMPLEMENT_ACTION_V(_name) \ +BEGIN_METHOD(AL_##_name, GB_OBJECT ids) \ + GB_ARRAY array = VARG(ids); \ + if (GB.CheckObject(array)) \ + return; \ + al##_name(GB.Array.Count(array), GB.Array.Get(array, 0)); \ +END_METHOD + +IMPLEMENT_ACTION(SourcePlay) +IMPLEMENT_ACTION(SourceStop) +IMPLEMENT_ACTION(SourceRewind) +IMPLEMENT_ACTION(SourcePause) + +IMPLEMENT_ACTION_V(SourcePlayv) +IMPLEMENT_ACTION_V(SourceStopv) +IMPLEMENT_ACTION_V(SourceRewindv) +IMPLEMENT_ACTION_V(SourcePausev) + +#define IMPLEMENT_QUEUE_BUFFER(_name) \ +BEGIN_METHOD(AL_##_name, GB_INTEGER source; GB_OBJECT buffers) \ + GB_ARRAY buffers = VARG(buffers); \ + if (GB.CheckObject(buffers)) \ + return; \ + al##_name(VARG(source), GB.Array.Count(buffers), GB.Array.Get(buffers, 0)); \ +END_METHOD + +IMPLEMENT_QUEUE_BUFFER(SourceQueueBuffers) +IMPLEMENT_QUEUE_BUFFER(SourceUnqueueBuffers) + +BEGIN_METHOD(AL_BufferData, GB_INTEGER buffer; GB_INTEGER format; GB_VARIANT data; GB_INTEGER size; GB_INTEGER freq) + + void *data; + int size = VARGOPT(size, -1); + int max_size = -1; + + if (VARG(data).type == GB_T_STRING) + { + data = VARG(data).value._string; + max_size = GB.StringLength(data); + } + else if (VARG(data).type == GB_T_POINTER) + { + data = (void *)VARG(data).value._pointer; + max_size = size; + if (max_size < 0) + max_size = 0; + } + else if (VARG(data).type == GB_T_OBJECT) + { + void *object = VARG(data).value._object; + + if (GB.Is(object, GB.FindClass("Byte[]")) + || GB.Is(object, GB.FindClass("Short[]")) + || GB.Is(object, GB.FindClass("Integer[]"))) + { + int count = GB.Array.Count((GB_ARRAY)object); + + if (count == 0) + { + data = NULL; + max_size = 0; + } + else + { + data = GB.Array.Get((GB_ARRAY)object, 0); + max_size = count * (GB.Array.Get((GB_ARRAY)object, 1) - data); + } + } + } + + if (max_size < 0) + { + GB.Error("Unsupported data type. String, Pointer, Byte[], Short[] or Integer[] expected"); + return; + } + + if (size < 0) + size = max_size; + else if (size > max_size) + size = max_size; + + if (size <= 0) + return; + + alBufferData(VARG(buffer), VARG(format), data, size, VARGOPT(freq, 44100)); + +END_METHOD + +#undef ID_PARAM +#define ID_PARAM GB_INTEGER id; +#undef ID_ARG +#define ID_ARG VARG(id), + +IMPLEMENT_X(Bufferf, GB_SINGLE) +IMPLEMENT_3X(Buffer3f, GB_SINGLE) +IMPLEMENT_XV(Bufferfv, GB_SINGLE) +IMPLEMENT_X(Bufferi, GB_INTEGER) +IMPLEMENT_3X(Buffer3i, GB_INTEGER) +IMPLEMENT_XV(Bufferiv, GB_INTEGER) + +IMPLEMENT_GET_X(GetBufferf, Single, ALfloat) +IMPLEMENT_GET_XV(GetBufferfv, GB_T_SINGLE, get_buffer_float_param_size) +IMPLEMENT_GET_X(GetBufferi, Integer, ALint) +IMPLEMENT_GET_XV(GetBufferiv, GB_T_INTEGER, get_buffer_integer_param_size) + + +//--------------------------------------------------------------------------- + +GB_DESC AlDesc[] = +{ + GB_DECLARE_VIRTUAL("Al"), + + GB_STATIC_METHOD("GetError", "i", AL_GetError, NULL), + + GB_STATIC_METHOD("Enable", NULL, AL_Enable, "(Capability)i"), + GB_STATIC_METHOD("Disable", NULL, AL_Disable, "(Capability)i"), + GB_STATIC_METHOD("IsEnabled", "b", AL_IsEnabled, "(Capability)i"), + + GB_STATIC_METHOD("GetBoolean", "b", AL_GetBoolean, "(Param)i"), + GB_STATIC_METHOD("GetInteger", "i", AL_GetInteger, "(Param)i"), + GB_STATIC_METHOD("GetFloat", "g", AL_GetFloat, "(Param)i"), + GB_STATIC_METHOD("GetDouble", "f", AL_GetDouble, "(Param)i"), + + //GB_STATIC_METHOD("GetBooleanv", "Boolean[]", AL_GetBooleanv, "(Param)i"), + //GB_STATIC_METHOD("GetIntegerv", "Integer[]", AL_GetIntegerv, "(Param)i"), + //GB_STATIC_METHOD("GetFloatv", "Single[]", AL_GetFloatv, "(Param)i"), + //GB_STATIC_METHOD("GetDoublev", "Float[]", AL_GetDoublev, "(Param)i"), + + GB_STATIC_METHOD("GetString", "s", AL_GetString, "(Param)i"), + + GB_STATIC_METHOD("IsExtensionPresent", "b", AL_IsExtensionPresent, "(Name)s"), + + GB_STATIC_METHOD("GetEnumValue", "i", AL_GetEnumValue, "(Name)s"), + + GB_STATIC_METHOD("Listenerf", NULL, AL_Listenerf, "(Param)i(Value)g"), + GB_STATIC_METHOD("Listener3f", NULL, AL_Listener3f, "(Param)i(Value1)g(Value2)g(Value3)g"), + GB_STATIC_METHOD("Listenerfv", NULL, AL_Listenerfv, "(Param)i(Values)Single[];"), + GB_STATIC_METHOD("Listeneri", NULL, AL_Listeneri, "(Param)i(Value)i"), + GB_STATIC_METHOD("Listener3i", NULL, AL_Listener3i, "(Param)i(Value1)i(Value2)i(Value3)i"), + GB_STATIC_METHOD("Listeneriv", NULL, AL_Listeneriv, "(Param)i(Values)Integer[];"), + + GB_STATIC_METHOD("GetListenerf", "g", AL_GetListenerf, "(Param)i"), + GB_STATIC_METHOD("GetListener3f", "Single[]", AL_GetListenerfv, "(Param)i"), + GB_STATIC_METHOD("GetListenerfv", "Single[]", AL_GetListenerfv, "(Param)i"), + GB_STATIC_METHOD("GetListeneri", "i", AL_GetListenerf, "(Param)i"), + GB_STATIC_METHOD("GetListener3i", "Integer[]", AL_GetListenerfv, "(Param)i"), + GB_STATIC_METHOD("GetListeneriv", "Integer[]", AL_GetListenerfv, "(Param)i"), + + GB_STATIC_METHOD("GenSources", "Integer[]", AL_GenSources, "(Count)i"), + GB_STATIC_METHOD("DeleteSources", NULL, AL_DeleteSources, "(Sources)Integer[];"), + GB_STATIC_METHOD("IsSource", "b", AL_IsSource, "(Source)i"), + + GB_STATIC_METHOD("Sourcef", NULL, AL_Sourcef, "(Source)i(Param)i(Value)g"), + GB_STATIC_METHOD("Source3f", NULL, AL_Source3f, "(Source)i(Param)i(Value1)g(Value2)g(Value3)g"), + GB_STATIC_METHOD("Sourcefv", NULL, AL_Sourcefv, "(Source)i(Param)i(Values)Single[];"), + GB_STATIC_METHOD("Sourcei", NULL, AL_Sourcei, "(Source)i(Param)i(Value)i"), + GB_STATIC_METHOD("Source3i", NULL, AL_Source3i, "(Source)i(Param)i(Value1)i(Value2)i(Value3)i"), + GB_STATIC_METHOD("Sourceiv", NULL, AL_Sourceiv, "(Source)i(Param)i(Values)Integer[];"), + + GB_STATIC_METHOD("GetSourcef", "g", AL_GetSourcef, "(Source)i(Param)i"), + GB_STATIC_METHOD("GetSource3f", "Single[]", AL_GetSourcefv, "(Source)i(Param)i"), + GB_STATIC_METHOD("GetSourcefv", "Single[]", AL_GetSourcefv, "(Source)i(Param)i"), + GB_STATIC_METHOD("GetSourcei", "i", AL_GetSourcef, "(Source)i(Param)i"), + GB_STATIC_METHOD("GetSource3i", "Integer[]", AL_GetSourcefv, "(Source)i(Param)i"), + GB_STATIC_METHOD("GetSourceiv", "Integer[]", AL_GetSourcefv, "(Source)i(Param)i"), + + GB_STATIC_METHOD("SourcePlay", NULL, AL_SourcePlay, "(Source)i"), + GB_STATIC_METHOD("SourceStop", NULL, AL_SourceStop, "(Source)i"), + GB_STATIC_METHOD("SourceRewind", NULL, AL_SourceRewind, "(Source)i"), + GB_STATIC_METHOD("SourcePause", NULL, AL_SourcePause, "(Source)i"), + + GB_STATIC_METHOD("SourcePlayv", NULL, AL_SourcePlayv, "(Sources)Integer[];"), + GB_STATIC_METHOD("SourceStopv", NULL, AL_SourceStopv, "(Sources)Integer[];"), + GB_STATIC_METHOD("SourceRewindv", NULL, AL_SourceRewindv, "(Sources)Integer[];"), + GB_STATIC_METHOD("SourcePausev", NULL, AL_SourcePausev, "(Sources)Integer[];"), + + GB_STATIC_METHOD("SourceQueueBuffers", NULL, AL_SourceQueueBuffers, "(Source)i(Buffers)Integer[];"), + GB_STATIC_METHOD("SourceUnqueueBuffers", NULL, AL_SourceUnqueueBuffers, "(Source)i(Buffers)Integer[];"), + + GB_STATIC_METHOD("GenBuffers", "Integer[]", AL_GenBuffers, "(Count)i"), + GB_STATIC_METHOD("DeleteBuffers", NULL, AL_DeleteBuffers, "(Buffers)Integer[];"), + GB_STATIC_METHOD("IsBuffer", "b", AL_IsBuffer, "(Buffer)i"), + + GB_STATIC_METHOD("BufferData", NULL, AL_BufferData, "(Buffer)i(Format)i(Data)v[(Size)i(Frequency)i]"), + + GB_STATIC_METHOD("Bufferf", NULL, AL_Bufferf, "(Buffer)i(Param)i(Value)g"), + GB_STATIC_METHOD("Buffer3f", NULL, AL_Buffer3f, "(Buffer)i(Param)i(Value1)g(Value2)g(Value3)g"), + GB_STATIC_METHOD("Bufferfv", NULL, AL_Bufferfv, "(Buffer)i(Param)i(Values)Single[];"), + GB_STATIC_METHOD("Bufferi", NULL, AL_Bufferi, "(Buffer)i(Param)i(Value)i"), + GB_STATIC_METHOD("Buffer3i", NULL, AL_Buffer3i, "(Buffer)i(Param)i(Value1)i(Value2)i(Value3)i"), + GB_STATIC_METHOD("Bufferiv", NULL, AL_Bufferiv, "(Buffer)i(Param)i(Values)Integer[];"), + + GB_STATIC_METHOD("GetBufferf", "g", AL_GetBufferf, "(Buffer)i(Param)i"), + GB_STATIC_METHOD("GetBuffer3f", "Single[]", AL_GetBufferfv, "(Buffer)i(Param)i"), + GB_STATIC_METHOD("GetBufferfv", "Single[]", AL_GetBufferfv, "(Buffer)i(Param)i"), + GB_STATIC_METHOD("GetBufferi", "i", AL_GetBufferf, "(Buffer)i(Param)i"), + GB_STATIC_METHOD("GetBuffer3i", "Integer[]", AL_GetBufferfv, "(Buffer)i(Param)i"), + GB_STATIC_METHOD("GetBufferiv", "Integer[]", AL_GetBufferfv, "(Buffer)i(Param)i"), + + GB_STATIC_METHOD("DopplerFactor", NULL, AL_DopplerFactor, "(DopplerFactor)f"), + GB_STATIC_METHOD("DopplerVelocity", NULL, AL_DopplerVelocity, "(DopplerVelocity)f"), + GB_STATIC_METHOD("SpeedOfSound", NULL, AL_SpeedOfSound, "(SpeedOfSound)f"), + GB_STATIC_METHOD("DistanceModel", NULL, AL_DistanceModel, "(DistanceModel)i"), + + GB_CONSTANT("NONE", "i", AL_NONE), + GB_CONSTANT("FALSE", "i", AL_FALSE), + GB_CONSTANT("TRUE", "i", AL_TRUE), + GB_CONSTANT("SOURCE_RELATIVE", "i", AL_SOURCE_RELATIVE), + GB_CONSTANT("CONE_INNER_ANGLE", "i", AL_CONE_INNER_ANGLE), + GB_CONSTANT("CONE_OUTER_ANGLE", "i", AL_CONE_OUTER_ANGLE), + GB_CONSTANT("PITCH", "i", AL_PITCH), + GB_CONSTANT("POSITION", "i", AL_POSITION), + GB_CONSTANT("DIRECTION", "i", AL_DIRECTION), + GB_CONSTANT("VELOCITY", "i", AL_VELOCITY), + GB_CONSTANT("LOOPING", "i", AL_LOOPING), + GB_CONSTANT("BUFFER", "i", AL_BUFFER), + GB_CONSTANT("GAIN", "i", AL_GAIN), + GB_CONSTANT("MIN_GAIN", "i", AL_MIN_GAIN), + GB_CONSTANT("MAX_GAIN", "i", AL_MAX_GAIN), + GB_CONSTANT("ORIENTATION", "i", AL_ORIENTATION), + GB_CONSTANT("SOURCE_STATE", "i", AL_SOURCE_STATE), + GB_CONSTANT("INITIAL", "i", AL_INITIAL), + GB_CONSTANT("PLAYING", "i", AL_PLAYING), + GB_CONSTANT("PAUSED", "i", AL_PAUSED), + GB_CONSTANT("STOPPED", "i", AL_STOPPED), + GB_CONSTANT("BUFFERS_QUEUED", "i", AL_BUFFERS_QUEUED), + GB_CONSTANT("BUFFERS_PROCESSED", "i", AL_BUFFERS_PROCESSED), + GB_CONSTANT("SEC_OFFSET", "i", AL_SEC_OFFSET), + GB_CONSTANT("SAMPLE_OFFSET", "i", AL_SAMPLE_OFFSET), + GB_CONSTANT("BYTE_OFFSET", "i", AL_BYTE_OFFSET), + GB_CONSTANT("SOURCE_TYPE", "i", AL_SOURCE_TYPE), + GB_CONSTANT("STATIC", "i", AL_STATIC), + GB_CONSTANT("STREAMING", "i", AL_STREAMING), + GB_CONSTANT("UNDETERMINED", "i", AL_UNDETERMINED), + GB_CONSTANT("FORMAT_MONO8", "i", AL_FORMAT_MONO8), + GB_CONSTANT("FORMAT_MONO16", "i", AL_FORMAT_MONO16), + GB_CONSTANT("FORMAT_STEREO8", "i", AL_FORMAT_STEREO8), + GB_CONSTANT("FORMAT_STEREO16", "i", AL_FORMAT_STEREO16), + GB_CONSTANT("REFERENCE_DISTANCE", "i", AL_REFERENCE_DISTANCE), + GB_CONSTANT("ROLLOFF_FACTOR", "i", AL_ROLLOFF_FACTOR), + GB_CONSTANT("CONE_OUTER_GAIN", "i", AL_CONE_OUTER_GAIN), + GB_CONSTANT("MAX_DISTANCE", "i", AL_MAX_DISTANCE), + GB_CONSTANT("FREQUENCY", "i", AL_FREQUENCY), + GB_CONSTANT("BITS", "i", AL_BITS), + GB_CONSTANT("CHANNELS", "i", AL_CHANNELS), + GB_CONSTANT("SIZE", "i", AL_SIZE), + GB_CONSTANT("UNUSED", "i", AL_UNUSED), + GB_CONSTANT("PENDING", "i", AL_PENDING), + GB_CONSTANT("PROCESSED", "i", AL_PROCESSED), + GB_CONSTANT("NO_ERROR", "i", AL_NO_ERROR), + GB_CONSTANT("INVALID_NAME", "i", AL_INVALID_NAME), + GB_CONSTANT("INVALID_ENUM", "i", AL_INVALID_ENUM), + GB_CONSTANT("INVALID_VALUE", "i", AL_INVALID_VALUE), + GB_CONSTANT("INVALID_OPERATION", "i", AL_INVALID_OPERATION), + GB_CONSTANT("OUT_OF_MEMORY", "i", AL_OUT_OF_MEMORY), + GB_CONSTANT("VENDOR", "i", AL_VENDOR), + GB_CONSTANT("VERSION", "i", AL_VERSION), + GB_CONSTANT("RENDERER", "i", AL_RENDERER), + GB_CONSTANT("EXTENSIONS", "i", AL_EXTENSIONS), + GB_CONSTANT("DOPPLER_FACTOR", "i", AL_DOPPLER_FACTOR), + GB_CONSTANT("DOPPLER_VELOCITY", "i", AL_DOPPLER_VELOCITY), + GB_CONSTANT("SPEED_OF_SOUND", "i", AL_SPEED_OF_SOUND), + GB_CONSTANT("DISTANCE_MODEL", "i", AL_DISTANCE_MODEL), + GB_CONSTANT("INVERSE_DISTANCE", "i", AL_INVERSE_DISTANCE), + GB_CONSTANT("INVERSE_DISTANCE_CLAMPED", "i", AL_INVERSE_DISTANCE_CLAMPED), + GB_CONSTANT("LINEAR_DISTANCE", "i", AL_LINEAR_DISTANCE), + GB_CONSTANT("LINEAR_DISTANCE_CLAMPED", "i", AL_LINEAR_DISTANCE_CLAMPED), + GB_CONSTANT("EXPONENT_DISTANCE", "i", AL_EXPONENT_DISTANCE), + GB_CONSTANT("EXPONENT_DISTANCE_CLAMPED", "i", AL_EXPONENT_DISTANCE_CLAMPED), + + GB_END_DECLARE +}; diff --git a/gb.openal/src/c_al.h b/gb.openal/src/c_al.h new file mode 100644 index 00000000..a78c0f3d --- /dev/null +++ b/gb.openal/src/c_al.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + c_al.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_AL_H +#define __C_AL_H + +#include "main.h" + +#ifndef __C_AL_C +extern GB_DESC AlDesc[]; +#endif + +#endif diff --git a/gb.openal/src/c_alc.c b/gb.openal/src/c_alc.c new file mode 100644 index 00000000..e068ed13 --- /dev/null +++ b/gb.openal/src/c_alc.c @@ -0,0 +1,509 @@ +/*************************************************************************** + + c_alc.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_ALC_C + +#include "c_alc.h" + +//--------------------------------------------------------------------------- + +#define THIS ((CALCCONTEXT *)_object) + +static CALCCONTEXT *_current_context = NULL; + +static int check_context(void *_object) +{ + return THIS->context == NULL; +} + +static void init_context(CALCCONTEXT *_object, CALCDEVICE *device, GB_ARRAY array) +{ + ALCint *attrs = NULL; + int count; + + if (GB.CheckObject(device)) + return; + + if (array) + { + count = GB.Array.Count(array); + if (count) + { + attrs = alloca(sizeof(ALCint) * (count + 1)); + memcpy(attrs, GB.Array.Get(array, 0), count * sizeof(ALCint)); + attrs[count] = 0; + } + } + + THIS->context = alcCreateContext(device->device, attrs); + THIS->device = device; + GB.Ref(device); +} + +static void set_current_context(CALCCONTEXT *_object) +{ + if (_current_context == THIS) + return; + + GB.Unref(POINTER(&_current_context)); + + _current_context = THIS; + + if (THIS) + GB.Ref(THIS); +} + +static void destroy_context(CALCCONTEXT *_object) +{ + if (_current_context == THIS) + set_current_context(NULL); + + if (!THIS->context) + return; + + alcDestroyContext(THIS->context); + + THIS->context = NULL; + + GB.Unref(POINTER(&THIS->device)); +} + +BEGIN_METHOD_VOID(AlcContext_exit) + + GB.Unref(POINTER(&_current_context)); + +END_METHOD + +BEGIN_METHOD(AlcContext_new, GB_OBJECT device; GB_OBJECT attrs) + + init_context(THIS, VARG(device), VARGOPT(attrs, NULL)); + +END_METHOD + +BEGIN_METHOD_VOID(AlcContext_free) + + destroy_context(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(AlcContext_MakeCurrent) + + ALCboolean err = alcMakeContextCurrent(THIS->context); + if (err == ALC_FALSE) + set_current_context(THIS); + GB.ReturnBoolean(err); + +END_METHOD + +//--------------------------------------------------------------------------- + +#undef THIS +#define THIS ((CALCDEVICE *)_object) + +static int check_device(void *_object) +{ + return THIS->device == NULL; +} + +static CALCDEVICE *create_device(ALCdevice *device) +{ + CALCDEVICE *_object; + + if (!device) + return NULL; + + _object = GB.New(GB.FindClass("AlcDevice"), NULL, NULL); + THIS->device = device; + return THIS; +} + +static bool close_device(CALCDEVICE *_object) +{ + bool err = FALSE; + + if (THIS->device) + { + if (THIS->capture) + err = alcCaptureCloseDevice(THIS->device); + else + err = alcCloseDevice(THIS->device); + + THIS->device = NULL; + } + + return err; +} + +BEGIN_METHOD_VOID(AlcDevice_free) + + close_device(THIS); + +END_METHOD + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(ALC_CreateContext, GB_OBJECT device; GB_OBJECT attrs) + + CALCCONTEXT *context; + + context = GB.Create(GB.FindClass("AlcContext"), NULL, NULL); + init_context(context, VARG(device), VARGOPT(attrs, NULL)); + if (context->context) + GB.ReturnObject(context); + else + { + GB.Unref(POINTER(&context)); + GB.ReturnNull(); + } + +END_METHOD + +#define GET_CONTEXT() \ + CALCCONTEXT *context = VARG(context); \ + if (GB.CheckObject(context)) \ + return; + +BEGIN_METHOD(ALC_MakeContextCurrent, GB_OBJECT context) + + CALCCONTEXT *context = VARG(context); + ALCboolean err = alcMakeContextCurrent(context ? context->context : NULL); + if (err == ALC_FALSE) + set_current_context(context); + GB.ReturnBoolean(err); + +END_METHOD + +BEGIN_METHOD(ALC_ProcessContext, GB_OBJECT context) + + GET_CONTEXT(); + alcProcessContext(context->context); + +END_METHOD + +BEGIN_METHOD(ALC_SuspendContext, GB_OBJECT context) + + GET_CONTEXT(); + alcSuspendContext(context->context); + +END_METHOD + +BEGIN_METHOD(ALC_DestroyContext, GB_OBJECT context) + + GET_CONTEXT(); + destroy_context(context); + +END_METHOD + +BEGIN_METHOD_VOID(ALC_GetCurrentContext) + + GB.ReturnObject(_current_context); + +END_METHOD + +BEGIN_METHOD(ALC_GetContextsDevice, GB_OBJECT context) + + GET_CONTEXT(); + GB.ReturnObject(THIS->device); + +END_METHOD + +BEGIN_METHOD(ALC_OpenDevice, GB_STRING name) + + GB.ReturnObject(create_device(alcOpenDevice(GB.ToZeroString(ARG(name))))); + +END_METHOD + +#define GET_DEVICE() \ + CALCDEVICE *device = VARG(device); \ + if (GB.CheckObject(device)) \ + return; + +BEGIN_METHOD(ALC_CloseDevice, GB_OBJECT device) + + GET_DEVICE(); + GB.ReturnBoolean(close_device(device)); + +END_METHOD + +BEGIN_METHOD(ALC_GetError, GB_OBJECT device) + + GET_DEVICE(); + GB.ReturnInteger(alcGetError(device->device)); + +END_METHOD + +BEGIN_METHOD(ALC_IsExtensionPresent, GB_OBJECT device; GB_STRING ext) + + GET_DEVICE(); + GB.ReturnBoolean(alcIsExtensionPresent(device->device, GB.ToZeroString(ARG(ext)))); + +END_METHOD + +BEGIN_METHOD(ALC_GetString, GB_OBJECT device; GB_INTEGER param) + + CALCDEVICE *device = VARG(device); + ALCenum param = VARG(param); + + if (device && GB.CheckObject(device)) + return; + + if (!device && (param == ALC_DEVICE_SPECIFIER || param == ALC_CAPTURE_DEVICE_SPECIFIER)) + { + GB.Error("This query actually returns a string array. Use ALC_GetStringv instead"); + return; + } + + GB.ReturnNewZeroString(alcGetString(device ? device->device : NULL, param)); + +END_METHOD + +BEGIN_METHOD(ALC_GetStringv, GB_OBJECT device; GB_INTEGER param) + + CALCDEVICE *device = VARG(device); + ALCenum param = VARG(param); + const char *result, *p; + int len; + GB_ARRAY array; + + if (!(!device && (param == ALC_DEVICE_SPECIFIER || param == ALC_CAPTURE_DEVICE_SPECIFIER))) + { + GB.ReturnNull(); + return; + } + + result = alcGetString(NULL, param); + if (!result) + { + GB.ReturnNull(); + return; + } + + GB.Array.New(&array, GB_T_STRING, 0); + + for(;;) + { + p = index(result, 0); + if (!p) + break; + len = p - result; + if (len <= 0) + break; + + *((char **)GB.Array.Add(array)) = GB.NewString(result, len); + result = p + 1; + } + + GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(ALC_GetIntegerv, GB_OBJECT device; GB_INTEGER param; GB_INTEGER size) + + GB_ARRAY array; + int size = VARG(size); + + GET_DEVICE(); + + if (size <= 0 || size >= 65536) + { + GB.ReturnNull(); + return; + } + + GB.Array.New(&array, GB_T_INTEGER, size); + alcGetIntegerv(device->device, VARG(param), size, GB.Array.Get(array, 0)); + GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(ALC_CaptureOpenDevice, GB_STRING name; GB_INTEGER freq; GB_INTEGER format; GB_INTEGER buffer_size) + + CALCDEVICE *device = create_device(alcCaptureOpenDevice(GB.ToZeroString(ARG(name)), VARG(freq), VARG(format), VARG(buffer_size))); + + if (device) + { + device->capture = TRUE; + + switch(VARG(format)) + { + case AL_FORMAT_MONO8: device->sampleSize = 1; break; + case AL_FORMAT_MONO16: case AL_FORMAT_STEREO8: device->sampleSize = 2; break; + case AL_FORMAT_STEREO16: device->sampleSize = 4; break; + default: device->sampleSize = 0; + } + } + + GB.ReturnObject(device); + +END_METHOD + +BEGIN_METHOD(ALC_CaptureStart, GB_OBJECT device) + + GET_DEVICE(); + alcCaptureStart(device->device); + +END_METHOD + +BEGIN_METHOD(ALC_CaptureStop, GB_OBJECT device) + + GET_DEVICE(); + alcCaptureStop(device->device); + +END_METHOD + +BEGIN_METHOD(ALC_CaptureSamples, GB_OBJECT device; GB_INTEGER samples; GB_POINTER buffer) + + GB_ARRAY array = NULL; + int samples = VARG(samples); + void *buffer; + + GET_DEVICE(); + + if (samples <= 0) + { + GB.ReturnNull(); + return; + } + + if (MISSING(buffer)) + { + if (device->sampleSize == 0) + { + GB.Error("Unknown sample format"); + return; + } + GB.Array.New(&array, device->sampleSize == 1 ? GB_T_BYTE : device->sampleSize == 2 ? GB_T_SHORT : GB_T_INTEGER, samples); + buffer = GB.Array.Get(array, 0); + } + else + buffer = (void *)VARG(buffer); + + alcCaptureSamples(device->device, buffer, samples); + + GB.ReturnObject(array); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC AlcDeviceDesc[] = +{ + GB_DECLARE("AlcDevice", sizeof(CALCDEVICE)), + GB_NOT_CREATABLE(), + GB_HOOK_CHECK(check_device), + + GB_METHOD("_free", NULL, AlcDevice_free, NULL), + + GB_END_DECLARE +}; + +GB_DESC AlcContextDesc[] = +{ + GB_DECLARE("AlcContext", sizeof(CALCCONTEXT)), + GB_HOOK_CHECK(check_context), + + GB_STATIC_METHOD("_exit", NULL, AlcContext_exit, NULL), + + GB_METHOD("_new", NULL, AlcContext_new, "(Device)AlcDevice;[(Attributes)Integer[];]"), + GB_METHOD("MakeCurrent", "b", AlcContext_MakeCurrent, NULL), + + GB_END_DECLARE +}; + +GB_DESC AlcDesc[] = +{ + GB_DECLARE_VIRTUAL("Alc"), + + GB_CONSTANT("FALSE", "i", ALC_FALSE), + GB_CONSTANT("TRUE", "i", ALC_TRUE), + GB_CONSTANT("FREQUENCY", "i", ALC_FREQUENCY), + GB_CONSTANT("REFRESH", "i", ALC_REFRESH), + GB_CONSTANT("SYNC", "i", ALC_SYNC), + GB_CONSTANT("MONO_SOURCES", "i", ALC_MONO_SOURCES), + GB_CONSTANT("STEREO_SOURCES", "i", ALC_STEREO_SOURCES), + GB_CONSTANT("NO_ERROR", "i", ALC_NO_ERROR), + GB_CONSTANT("INVALID_DEVICE", "i", ALC_INVALID_DEVICE), + GB_CONSTANT("INVALID_CONTEXT", "i", ALC_INVALID_CONTEXT), + GB_CONSTANT("INVALID_ENUM", "i", ALC_INVALID_ENUM), + GB_CONSTANT("INVALID_VALUE", "i", ALC_INVALID_VALUE), + GB_CONSTANT("OUT_OF_MEMORY", "i", ALC_OUT_OF_MEMORY), + GB_CONSTANT("DEFAULT_DEVICE_SPECIFIER", "i", ALC_DEFAULT_DEVICE_SPECIFIER), + GB_CONSTANT("DEVICE_SPECIFIER", "i", ALC_DEVICE_SPECIFIER), + GB_CONSTANT("EXTENSIONS", "i", ALC_EXTENSIONS), + GB_CONSTANT("MAJOR_VERSION", "i", ALC_MAJOR_VERSION), + GB_CONSTANT("MINOR_VERSION", "i", ALC_MINOR_VERSION), + GB_CONSTANT("ATTRIBUTES_SIZE", "i", ALC_ATTRIBUTES_SIZE), + GB_CONSTANT("ALL_ATTRIBUTES", "i", ALC_ALL_ATTRIBUTES), + GB_CONSTANT("EXT_CAPTURE", "i", ALC_EXT_CAPTURE), + GB_CONSTANT("CAPTURE_DEVICE_SPECIFIER", "i", ALC_CAPTURE_DEVICE_SPECIFIER), + GB_CONSTANT("CAPTURE_DEFAULT_DEVICE_SPECIFIER", "i", ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER), + GB_CONSTANT("CAPTURE_SAMPLES", "i", ALC_CAPTURE_SAMPLES), + GB_CONSTANT("ENUMERATE_ALL_EXT", "i", ALC_ENUMERATE_ALL_EXT), + GB_CONSTANT("DEFAULT_ALL_DEVICES_SPECIFIER", "i", ALC_DEFAULT_ALL_DEVICES_SPECIFIER), + GB_CONSTANT("ALL_DEVICES_SPECIFIER", "i", ALC_ALL_DEVICES_SPECIFIER), + + GB_STATIC_METHOD("CreateContext", "AlcContext", ALC_CreateContext, "(Device)AlcDevice;[(Attributes)Integer[];]"), + GB_STATIC_METHOD("MakeContextCurrent", "b", ALC_MakeContextCurrent, "(Context)AlcContext;"), + GB_STATIC_METHOD("ProcessContext", NULL, ALC_ProcessContext, "(Context)AlcContext;"), + GB_STATIC_METHOD("SuspendContext", NULL, ALC_SuspendContext, "(Context)AlcContext;"), + GB_STATIC_METHOD("DestroyContext", NULL, ALC_DestroyContext, "(Context)AlcContext;"), + GB_STATIC_METHOD("GetCurrentContext", "AlcContext", ALC_GetCurrentContext, NULL), + GB_STATIC_METHOD("GetContextsDevice", "AlcDevice", ALC_GetContextsDevice, "(Context)AlcContext;"), + GB_STATIC_METHOD("OpenDevice", "AlcDevice", ALC_OpenDevice, "(Name)s"), + GB_STATIC_METHOD("CloseDevice", "b", ALC_CloseDevice, "(Device)AlcDevice;"), + GB_STATIC_METHOD("GetError", "i", ALC_GetError, "(Device)AlcDevice;"), + GB_STATIC_METHOD("IsExtensionPresent", "b", ALC_IsExtensionPresent, "(Device)AlcDevice;(ExtensionName)s"), + GB_STATIC_METHOD("GetString", "s", ALC_GetString, "(Device)AlcDevice;(Param)i"), + GB_STATIC_METHOD("GetStringv", "String[]", ALC_GetStringv, "(Device)AlcDevice;(Param)i"), + GB_STATIC_METHOD("GetIntegerv", "Integer[]", ALC_GetIntegerv, "(Device)AlcDevice;(Param)i;(Size)i"), + GB_STATIC_METHOD("CaptureOpenDevice", "AlcDevice", ALC_CaptureOpenDevice, "(Name)s(Frequency)i(Format)i(BufferSize)i"), + GB_STATIC_METHOD("CaptureCloseDevice", "b", ALC_CloseDevice, "(Device)AlcDevice;"), + GB_STATIC_METHOD("CaptureStart", NULL, ALC_CaptureStart, "(Device)AlcDevice;"), + GB_STATIC_METHOD("CaptureStop", NULL, ALC_CaptureStop, "(Device)AlcDevice;"), + GB_STATIC_METHOD("CaptureSamples", "v", ALC_CaptureSamples, "(Device)AlcDevice;(Samples)i[(Buffer)p]"), + + GB_END_DECLARE +}; + +#ifdef TOTO +// ALC_API ALCcontext * ALC_APIENTRY alcCreateContext( ALCdevice *device, const ALCint* attrlist ); +// ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent( ALCcontext *context ); +// ALC_API void ALC_APIENTRY alcProcessContext( ALCcontext *context ); +// ALC_API void ALC_APIENTRY alcSuspendContext( ALCcontext *context ); +// ALC_API void ALC_APIENTRY alcDestroyContext( ALCcontext *context ); +// ALC_API ALCcontext * ALC_APIENTRY alcGetCurrentContext( void ); +// ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice( ALCcontext *context ); +// ALC_API ALCdevice * ALC_APIENTRY alcOpenDevice( const ALCchar *devicename ); +// ALC_API ALCboolean ALC_APIENTRY alcCloseDevice( ALCdevice *device ); +// ALC_API ALCenum ALC_APIENTRY alcGetError( ALCdevice *device ); +// ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent( ALCdevice *device, const ALCchar *extname ); +// ALC_API void * ALC_APIENTRY alcGetProcAddress( ALCdevice *device, const ALCchar *funcname ); +// ALC_API ALCenum ALC_APIENTRY alcGetEnumValue( ALCdevice *device, const ALCchar *enumname ); +// ALC_API const ALCchar * ALC_APIENTRY alcGetString( ALCdevice *device, ALCenum param ); +// ALC_API void ALC_APIENTRY alcGetIntegerv( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *data ); +// ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize ); +// ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice( ALCdevice *device ); +// ALC_API void ALC_APIENTRY alcCaptureStart( ALCdevice *device ); +// ALC_API void ALC_APIENTRY alcCaptureStop( ALCdevice *device ); +// ALC_API void ALC_APIENTRY alcCaptureSamples( ALCdevice *device, ALCvoid *buffer, ALCsizei samples ); +#endif diff --git a/gb.openal/src/c_alc.h b/gb.openal/src/c_alc.h new file mode 100644 index 00000000..45dd861b --- /dev/null +++ b/gb.openal/src/c_alc.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + c_alc.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_ALC_H +#define __C_ALC_H + +#include "main.h" + +#ifndef __C_AL_C +extern GB_DESC AlcDesc[]; +extern GB_DESC AlcContextDesc[]; +extern GB_DESC AlcDeviceDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + ALCdevice *device; + unsigned capture : 1; + unsigned sampleSize : 3; + } + CALCDEVICE; + +typedef + struct { + GB_BASE ob; + ALCcontext *context; + CALCDEVICE *device; + } + CALCCONTEXT; + +#endif diff --git a/gb.openal/src/c_alure.c b/gb.openal/src/c_alure.c new file mode 100644 index 00000000..ea4f3839 --- /dev/null +++ b/gb.openal/src/c_alure.c @@ -0,0 +1,411 @@ +/*************************************************************************** + + c_alure.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_ALURE_C + +#include "c_alure.h" + +static char *_version = NULL; +static GB_ARRAY _stop = NULL; +static bool _register_stop = FALSE; + +static void cb_play(void *userData, ALuint source) +{ + if (_register_stop) + { + if (!_stop) + GB.Array.New(&_stop, GB_T_INTEGER, 0); + + *((ALuint *)GB.Array.Add(_stop)) = source; + } +} + +//--------------------------------------------------------------------------- + +#define THIS ((CALURESTREAM *)_object) + +static int check_stream(CALURESTREAM *_object) +{ + return THIS->stream == NULL; +} + +CALURESTREAM *create_stream(int num_buf) +{ + CALURESTREAM *stream = GB.New(GB.FindClass("AlureStream"), NULL, NULL); + + if (num_buf > 0) + GB.Array.New(&stream->buffers, GB_T_INTEGER, num_buf); + + return stream; +} + +bool destroy_stream(CALURESTREAM *_object) +{ + ALsizei nbuf; + ALuint *buffers; + bool err; + + if (THIS->buffers) + { + nbuf = GB.Array.Count(THIS->buffers); + buffers = GB.Array.Get(THIS->buffers, 0); + } + else + { + nbuf = 0; + buffers = NULL; + } + + err = alureDestroyStream(THIS->stream, nbuf, buffers); + + GB.Unref(POINTER(&THIS->buffers)); + + if (THIS->addr) + { + GB.ReleaseFile(THIS->addr, THIS->len); + THIS->addr = NULL; + } + + THIS->stream = NULL; + + return err; +} + +BEGIN_METHOD_VOID(AlureStream_free) + + destroy_stream(THIS); + +END_METHOD + +//--------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Alure_exit) + + GB.FreeString(&_version); + +END_METHOD + +BEGIN_METHOD_VOID(Alure_GetVersion) + + ALuint major, minor; + char buffer[32]; + + if (!_version) + { + alureGetVersion(&major, &minor); + sprintf(buffer, "%d.%d", major, minor); + _version = GB.NewZeroString(buffer); + } + + GB.ReturnString(_version); + +END_METHOD + +BEGIN_METHOD_VOID(Alure_GetErrorString) + + GB.ReturnNewZeroString(alureGetErrorString()); + +END_METHOD + +BEGIN_METHOD(Alure_GetDeviceNames, GB_BOOLEAN all) + + GB_ARRAY array; + const ALchar **devices; + ALsizei count; + int i; + + devices = alureGetDeviceNames(VARG(all), &count); + + GB.Array.New(&array, GB_T_STRING, count); + for (i = 0; i < count; i++) + *((char**)GB.Array.Get(array, i)) = GB.NewZeroString(devices[i]); + + alureFreeDeviceNames(devices); + + GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(Alure_InitDevice, GB_STRING name; GB_OBJECT array) + + GB_ARRAY array = VARG(array); + ALCint *attrs = NULL; + int count; + + if (array) + { + count = GB.Array.Count(array); + if (count) + { + attrs = alloca(sizeof(ALCint) * (count + 1)); + memcpy(attrs, GB.Array.Get(array, 0), count * sizeof(ALCint)); + attrs[count] = 0; + } + } + + GB.ReturnBoolean(alureInitDevice(MISSING(name) ? NULL : GB.ToZeroString(ARG(name)), attrs)); + +END_METHOD + +BEGIN_METHOD_VOID(Alure_ShutdownDevice) + + GB.ReturnBoolean(alureShutdownDevice()); + +END_METHOD + +BEGIN_METHOD(Alure_GetSampleFormat, GB_INTEGER channels; GB_INTEGER bits; GB_INTEGER fbits) + + GB.ReturnInteger(alureGetSampleFormat(VARG(channels), VARG(bits), VARG(fbits))); + +END_METHOD + +BEGIN_METHOD(Alure_Sleep, GB_FLOAT duration) + + GB.ReturnBoolean(alureSleep(VARG(duration))); + +END_METHOD + +BEGIN_METHOD(Alure_StreamSizeIsMicroSec, GB_BOOLEAN useus) + + GB.ReturnBoolean(alureStreamSizeIsMicroSec(VARG(useus))); + +END_METHOD + +BEGIN_METHOD(Alure_CreateBufferFromFile, GB_STRING path) + + char *addr; + int len; + + if (GB.LoadFile(STRING(path), LENGTH(path), &addr, &len)) + return; + + GB.ReturnInteger(alureCreateBufferFromMemory((const ALubyte *)addr, len)); + + GB.ReleaseFile(addr, len); + +END_METHOD + +BEGIN_METHOD(Alure_BufferDataFromFile, GB_STRING path; GB_INTEGER buffer) + + char *addr; + int len; + + if (GB.LoadFile(STRING(path), LENGTH(path), &addr, &len)) + return; + + GB.ReturnBoolean(alureBufferDataFromMemory((const ALubyte *)addr, len, VARG(buffer))); + + GB.ReleaseFile(addr, len); + +END_METHOD + +BEGIN_METHOD(Alure_CreateStreamFromFile, GB_STRING path; GB_INTEGER chunck_length; GB_INTEGER num_buf) + + CALURESTREAM *stream; + char *addr; + int len; + int num_buf = VARG(num_buf); + ALuint *buffers; + + if (GB.LoadFile(STRING(path), LENGTH(path), &addr, &len)) + return; + + stream = create_stream(num_buf); + stream->addr = addr; + stream->len = len; + + if (stream->buffers) + buffers = (ALuint *)GB.Array.Get(stream->buffers, 0); + else + buffers = NULL; + + stream->stream = alureCreateStreamFromStaticMemory((const ALubyte *)addr, len, VARG(chunck_length), num_buf, buffers); + + GB.ReturnObject(stream); + +END_METHOD + +#define GET_STREAM() \ + CALURESTREAM *stream = VARG(stream); \ + if (GB.CheckObject(stream)) \ + return; + +BEGIN_METHOD(Alure_GetStreamBuffers, GB_OBJECT stream) + + GET_STREAM(); + GB.ReturnObject(stream->buffers); + +END_METHOD + +BEGIN_METHOD(Alure_GetStreamLength, GB_OBJECT stream) + + GET_STREAM(); + GB.ReturnLong(alureGetStreamLength(stream->stream)); + +END_METHOD + +BEGIN_METHOD(Alure_GetStreamFrequency, GB_OBJECT stream) + + GET_STREAM(); + GB.ReturnInteger(alureGetStreamFrequency(stream->stream)); + +END_METHOD + +BEGIN_METHOD(Alure_BufferDataFromStream, GB_OBJECT stream; GB_OBJECT buffers) + + GB_ARRAY buffers = VARG(buffers); + + GET_STREAM(); + + if (GB.CheckObject(buffers)) + return; + + GB.ReturnInteger(alureBufferDataFromStream(stream->stream, GB.Array.Count(buffers), GB.Array.Get(buffers, 0))); + +END_METHOD + +BEGIN_METHOD(Alure_RewindStream, GB_OBJECT stream) + + GET_STREAM(); + GB.ReturnBoolean(alureRewindStream(stream->stream)); + +END_METHOD + +BEGIN_METHOD(Alure_SetStreamOrder, GB_OBJECT stream; GB_INTEGER order) + + GET_STREAM(); + GB.ReturnBoolean(alureSetStreamOrder(stream->stream, VARG(order))); + +END_METHOD + +BEGIN_METHOD(Alure_SetStreamPatchset, GB_OBJECT stream; GB_STRING patchset) + + GET_STREAM(); + GB.ReturnBoolean(alureSetStreamPatchset(stream->stream, GB.ToZeroString(ARG(patchset)))); + +END_METHOD + +BEGIN_METHOD(Alure_DestroyStream, GB_OBJECT stream) + + GET_STREAM(); + GB.ReturnBoolean(destroy_stream(stream)); + +END_METHOD + +BEGIN_METHOD_VOID(Alure_Update) + + GB_ARRAY stop; + + _register_stop = TRUE; + alureUpdate(); + _register_stop = FALSE; + + stop = _stop; + _stop = NULL; + GB.ReturnObject(stop); + +END_METHOD + +BEGIN_METHOD(Alure_UpdateInterval, GB_FLOAT interval) + + GB.ReturnBoolean(alureUpdateInterval(VARG(interval))); + +END_METHOD + +BEGIN_METHOD(Alure_PlaySourceStream, GB_INTEGER source; GB_OBJECT stream; GB_INTEGER num_bufs; GB_INTEGER loop_count) + + GET_STREAM(); + + GB.ReturnBoolean(alurePlaySourceStream(VARG(source), stream->stream, VARG(num_bufs), VARG(loop_count), cb_play, NULL)); + +END_METHOD + +BEGIN_METHOD(Alure_PlaySource, GB_INTEGER source) + + GB.ReturnBoolean(alurePlaySource(VARG(source), cb_play, NULL)); + +END_METHOD + +BEGIN_METHOD(Alure_StopSource, GB_INTEGER source) + + GB.ReturnBoolean(alureStopSource(VARG(source), TRUE)); + +END_METHOD + +BEGIN_METHOD(Alure_ResumeSource, GB_INTEGER source) + + GB.ReturnBoolean(alureResumeSource(VARG(source))); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC AlureStreamDesc[] = +{ + GB_DECLARE("AlureStream", sizeof(CALURESTREAM)), + GB_NOT_CREATABLE(), + GB_HOOK_CHECK(check_stream), + + GB_METHOD("_free", NULL, AlureStream_free, NULL), + + GB_END_DECLARE +}; + +GB_DESC AlureDesc[] = +{ + GB_DECLARE_VIRTUAL("Alure"), + + GB_STATIC_METHOD("_exit", NULL, Alure_exit, NULL), + + GB_STATIC_METHOD("GetVersion", "s", Alure_GetVersion, NULL), + GB_STATIC_METHOD("GetErrorString", "s", Alure_GetErrorString, NULL), + GB_STATIC_METHOD("GetDeviceNames", "String[]", Alure_GetDeviceNames, "(All)b"), + GB_STATIC_METHOD("InitDevice", "b", Alure_InitDevice, "[(Name)s(Attributes)Integer[];]"), + GB_STATIC_METHOD("ShutdownDevice", "b", Alure_ShutdownDevice, NULL), + GB_STATIC_METHOD("GetSampleFormat", "i", Alure_GetSampleFormat, "(Channels)i(Bits)i(FloatBits)i"), + GB_STATIC_METHOD("Sleep", "b", Alure_Sleep, "(Duration)f"), + GB_STATIC_METHOD("StreamSizeIsMicroSec", "b", Alure_StreamSizeIsMicroSec, "(UseMicroSeconds)b"), + GB_STATIC_METHOD("CreateBufferFromFile", "i", Alure_CreateBufferFromFile, "(Path)s"), + // GB_STATIC_METHOD("CreateBufferFromMemory", "i", Alure_CreateBufferFromMemory, "(Data)p(Size)i"), + GB_STATIC_METHOD("BufferDataFromFile", "b", Alure_BufferDataFromFile, "(Path)s(Buffer)i"), + // GB_STATIC_METHOD("BufferDataFromMemory", "b", Alure_BufferDataFromMemory, "(Data)p(Size)i(Buffer)i"), + GB_STATIC_METHOD("CreateStreamFromFile", "AlureStream", Alure_CreateStreamFromFile, "(Path)s(ChunkLength)i(NumBuf)i"), + // GB_STATIC_METHOD("CreateStreamFromMemory", "AlureStream", Alure_CreateStreamFromFile, "(Data)p(Length)i(ChunkLength)i(NumBuf)i"), + // GB_STATIC_METHOD("CreateStreamFromStaticMemory", "AlureStream", Alure_CreateStreamFromFile, "(Data)p(Length)i(ChunkLength)i(NumBuf)i"), + GB_STATIC_METHOD("GetStreamBuffers", "Integer[]", Alure_GetStreamBuffers, "(Stream)AlureStream;"), + GB_STATIC_METHOD("GetStreamLength", "l", Alure_GetStreamLength, "(Stream)AlureStream;"), + GB_STATIC_METHOD("GetStreamFrequency", "i", Alure_GetStreamFrequency, "(Stream)AlureStream;"), + GB_STATIC_METHOD("BufferDataFromStream", "i", Alure_BufferDataFromStream, "(Stream)AlureStream;(Buffers)Integer[];"), + GB_STATIC_METHOD("RewindStream", "b", Alure_RewindStream, "(Stream)AlureStream;"), + GB_STATIC_METHOD("SetStreamOrder", "b", Alure_SetStreamOrder, "(Stream)AlureStream;(Order)i"), + GB_STATIC_METHOD("SetStreamPatchset", "b", Alure_SetStreamPatchset, "(Stream)AlureStream;(Patchset)s"), + GB_STATIC_METHOD("DestroyStream", "b", Alure_DestroyStream, "(Stream)AlureStream;"), + GB_STATIC_METHOD("Update", "Integer[]", Alure_Update, NULL), + GB_STATIC_METHOD("UpdateInterval", "b", Alure_UpdateInterval, "(Interval)f"), + GB_STATIC_METHOD("PlaySourceStream", "b", Alure_PlaySourceStream, "(Source)i(Stream)AlureStream;(NumBufs)i(LoopCount)i"), + GB_STATIC_METHOD("PlaySource", "b", Alure_PlaySource, "(Source)i"), + GB_STATIC_METHOD("StopSource", "b", Alure_StopSource, "(Source)i"), + GB_STATIC_METHOD("ResumeSource", "b", Alure_StopSource, "(Source)i"), + + GB_END_DECLARE +}; diff --git a/gb.openal/src/c_alure.h b/gb.openal/src/c_alure.h new file mode 100644 index 00000000..4e80d4cd --- /dev/null +++ b/gb.openal/src/c_alure.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + c_alure.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_ALURE_H +#define __C_ALURE_H + +#include "main.h" + +#ifndef __C_ALURE_C +extern GB_DESC AlureDesc[]; +extern GB_DESC AlureStreamDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + alureStream *stream; + GB_ARRAY buffers; + char *addr; + int len; + volatile int stop; + } + CALURESTREAM; + +#endif diff --git a/gb.openal/src/gb.openal.component b/gb.openal/src/gb.openal.component new file mode 100644 index 00000000..23a4d791 --- /dev/null +++ b/gb.openal/src/gb.openal.component @@ -0,0 +1,3 @@ +[Component] +Author=Benoît Minisini +State=Finished diff --git a/gb.openal/src/main.c b/gb.openal/src/main.c new file mode 100644 index 00000000..04147c59 --- /dev/null +++ b/gb.openal/src/main.c @@ -0,0 +1,55 @@ +/*************************************************************************** + + main.c + + gb.openal component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "c_al.h" +#include "c_alc.h" +#include "c_alure.h" + +#include "main.h" + + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + AlDesc, + AlcDesc, + AlcContextDesc, + AlcDeviceDesc, + AlureStreamDesc, + AlureDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.openal/src/main.h b/gb.openal/src/main.h new file mode 100644 index 00000000..b9624a16 --- /dev/null +++ b/gb.openal/src/main.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + main.h + + gb.openal component + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" + +#include +#include +#include +#include + +#ifndef ALC_EXT_CAPTURE +#define ALC_EXT_CAPTURE 1 +#endif + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.opengl/AUTHORS b/gb.opengl/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.opengl/COPYING b/gb.opengl/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.opengl/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.opengl/ChangeLog b/gb.opengl/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.opengl/INSTALL b/gb.opengl/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.opengl/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.opengl/Makefile.am b/gb.opengl/Makefile.am new file mode 100644 index 00000000..e9f331fd --- /dev/null +++ b/gb.opengl/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @OPENGL_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h diff --git a/gb.opengl/NEWS b/gb.opengl/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.opengl/README b/gb.opengl/README new file mode 100644 index 00000000..53f792ab --- /dev/null +++ b/gb.opengl/README @@ -0,0 +1,7 @@ +TODO : +GLrasterisation : +glBitmap() +glPolygonStipple() +glGetPolygonStipple() + +Waiting for texture implementation under components (needed to test !) diff --git a/gb.opengl/acinclude.m4 b/gb.opengl/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.opengl/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.opengl/component.am b/gb.opengl/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.opengl/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.opengl/configure.ac b/gb.opengl/configure.ac new file mode 100644 index 00000000..87276674 --- /dev/null +++ b/gb.opengl/configure.ac @@ -0,0 +1,34 @@ +dnl ---- configure.ac for gb.opengl + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-opengl, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.opengl) +AC_PROG_LIBTOOL + +GB_COMPONENT_PKG_CONFIG( + opengl, OPENGL, gb.opengl, [src], + [gl glew]) + +GB_COMPONENT_PKG_CONFIG( + glsl, GLSL, gb.opengl.glsl, [glsl], + [gl glew]) + +GB_COMPONENT_PKG_CONFIG( + glu, GLU, gb.opengl.glu, [glu], + [glu]) + +GB_COMPONENT_PKG_CONFIG( + sge, SGE, gb.opengl.sge, [sge], + [gl glew]) + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +src/glu/Makefile \ +src/glsl/Makefile \ +src/sge/Makefile +) + +GB_PRINT_MESSAGES diff --git a/gb.opengl/gambas.h b/gb.opengl/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.opengl/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.opengl/gb.image.h b/gb.opengl/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.opengl/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.opengl/gb_common.h b/gb.opengl/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.opengl/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.opengl/m4 b/gb.opengl/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.opengl/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.opengl/reconf b/gb.opengl/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.opengl/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.opengl/src/GL.c b/gb.opengl/src/GL.c new file mode 100644 index 00000000..251aa9c7 --- /dev/null +++ b/gb.opengl/src/GL.c @@ -0,0 +1,1069 @@ +/*************************************************************************** + + GL.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GL_C + +#include "GL.h" + +#include "GLclipping.h" +#include "GLcolorLighting.h" +#include "GLcoordTransf.h" +#include "GLdisplayList.h" +#include "GLfog.h" +#include "GLframeBufferOps.h" +#include "GLmodesExec.h" +#include "GLpixelOperations.h" +#include "GLprimitives.h" +#include "GLrasterization.h" +#include "GLtextureMapping.h" +#include "GLinfo.h" +#include "GLselectFeedback.h" +#include "GLeval.h" +#include "framebufferobject.h" + +#define MAX_COMPAT_WARNING 16 + +/**************************************************************************/ + +BEGIN_METHOD_VOID(GLCHECKERROR) + + GB.ReturnInteger(glGetError()); + +END_METHOD + +BEGIN_METHOD(GLCHECKEXTENSIONS, GB_STRING extlist) + + GB.ReturnBoolean(glewIsSupported(GB.ToZeroString(ARG(extlist)))); + +END_METHOD + + +BEGIN_METHOD_VOID(GL_property) + + GB.ReturnBoolean(TRUE); + +END_METHOD + +BEGIN_METHOD_VOID(GL_unknown) + + static int nwarn = 0; + static void *klass = NULL; + static const char *same_name[] = { "VIEWPORT", "ACCUM", "CLEAR", NULL}; + static const int same_value[] = { GL_VIEWPORT, GL_ACCUM, GL_CLEAR }; + + char *name = GB.GetUnknown(); + const char **p; + bool add_underscore; + int same_index; + + if (strncasecmp(name, "GL_", 3)) + { + GB.Error(GB_ERR_NSYMBOL, "Gl", name); + return; + } + + if (GB.NParam()) + { + GB.Error(GB_ERR_NPROPERTY, "Gl", name); + return; + } + + name += 3; + + add_underscore = FALSE; + same_index = 0; + for (p = same_name; *p; p++, same_index++) + { + if (!strcasecmp(*p, name)) + { + add_underscore = TRUE; + break; + } + } + + nwarn++; + if (nwarn <= MAX_COMPAT_WARNING) + fprintf(stderr, "gb.opengl: warning: Gl.GL_%s constant is deprecated. Use Gl.%s%s now.\n", name, name, add_underscore ? "_" : ""); + else if (nwarn == (MAX_COMPAT_WARNING + 1)) + fprintf(stderr, "gb.opengl: warning: too many deprecated constant warnings.\n"); + + if (!klass) + klass = (void *)GB.FindClass("Gl"); + + if (add_underscore) + GB.ReturnInteger(same_value[same_index]); + else + GB.GetProperty(klass, name); + + GB.ReturnConvVariant(); + +END_METHOD + +/**************************************************************************/ + +GB_DESC Cgl[] = +{ + GB_DECLARE("Gl",0), GB_NOT_CREATABLE(), + + /* Check errors */ + GB_STATIC_METHOD("GetError", "i", GLCHECKERROR, NULL), + /* Check an extension or a driver ability */ + GB_STATIC_METHOD("CheckExtensions", "b", GLCHECKEXTENSIONS, "(Extensions)s"), + + /* Primitives - see GLprimitives.h */ + GB_STATIC_METHOD("Begin", NULL, GLBEGIN, "(Primitive)i"), + GB_STATIC_METHOD("EdgeFlag", NULL, GLEDGEFLAG, "(Flag)b"), + GB_STATIC_METHOD("End", NULL, GLEND, NULL), + GB_STATIC_METHOD("Rectf", NULL, GLRECTF, "(X1)f(Y1)f(X2)f(Y2)f"), + GB_STATIC_METHOD("Recti", NULL, GLRECTI, "(X1)i(Y1)i(X2)i(Y2)i"), + GB_STATIC_METHOD("Vertex2f", NULL, GLVERTEX2F, "(X)f(Y)f"), + GB_STATIC_METHOD("Vertex3f", NULL, GLVERTEX3F, "(X)f(Y)f(Z)f"), + GB_STATIC_METHOD("Vertex4f", NULL, GLVERTEXF, "(X)f(Y)f(Z)f(W)f"), + GB_STATIC_METHOD("Vertexf", NULL, GLVERTEXF, "(X)f(Y)f[(Z)f(W)f]"), + GB_STATIC_METHOD("Vertex2i", NULL, GLVERTEX2I, "(X)i(Y)i"), + GB_STATIC_METHOD("Vertex3i", NULL, GLVERTEX3I, "(X)i(Y)i(Z)i"), + GB_STATIC_METHOD("Vertex4i", NULL, GLVERTEXI, "(X)i(Y)i(Z)i(W)i"), + GB_STATIC_METHOD("Vertexi", NULL, GLVERTEXI, "(X)i(Y)i[(Z)i(W)i]"), + GB_STATIC_METHOD("Vertexfv", NULL, GLVERTEXFV, "(Coords)Float[]"), + GB_STATIC_METHOD("Vertexiv", NULL, GLVERTEXIV, "(Coords)Integer[]"), + + /* Display lists - see GLdisplayList.h */ + GB_STATIC_METHOD("CallList", NULL, GLCALLLIST, "(Index)i"), + GB_STATIC_METHOD("CallLists", NULL, GLCALLLISTS, "(Lists)Integer[]"), + GB_STATIC_METHOD("DeleteLists", NULL, GLDELETELISTS, "(Index)i(Range)i"), + GB_STATIC_METHOD("EndList", NULL, GLENDLIST, NULL), + GB_STATIC_METHOD("GenLists", "i", GLGENLISTS, "(Count)i"), + GB_STATIC_METHOD("IsList", "b", GLISLIST, "(Index)i"), + GB_STATIC_METHOD("ListBase", NULL, GLLISTBASE, "(Index)i"), + GB_STATIC_METHOD("NewList", NULL, GLNEWLIST, "(Index)i(Mode)i"), + + /* Coordinate Transformation - see GLcoordTransf.h */ + GB_STATIC_METHOD("DepthRange", NULL, GLDEPTHRANGE, "(Near)f(Far)f"), + GB_STATIC_METHOD("Frustum", NULL, GLFRUSTUM, "(Left)f(Right)f(Bottom)f(Top)f(Near)f(Far)f"), + GB_STATIC_METHOD("LoadIdentity", NULL, GLLOADIDENTITY, NULL), + GB_STATIC_METHOD("LoadMatrixf", NULL, GLLOADMATRIXF, "(Matrix)Float[]"), + GB_STATIC_METHOD("MatrixMode", NULL, GLMATRIXMODE, "(Mode)i"), + GB_STATIC_METHOD("MultMatrixf", NULL, GLMULTMATRIXF, "(Matrix)Float[]"), + GB_STATIC_METHOD("Ortho", NULL, GLORTHO, "(Left)f(Right)f(Bottom)f(Top)f(Near)f(Far)f"), + GB_STATIC_METHOD("PopMatrix", NULL, GLPOPMATRIX, NULL), + GB_STATIC_METHOD("PushMatrix", NULL, GLPUSHMATRIX, NULL), + GB_STATIC_METHOD("Rotatef", NULL, GLROTATEF, "(Angle)f(X)f(Y)f(Z)f"), + GB_STATIC_METHOD("Scalef", NULL, GLSCALEF, "(X)f(Y)f(Z)f"), + GB_STATIC_METHOD("Translatef", NULL, GLTRANSLATEF, "(X)f(Y)f(Z)f"), + GB_STATIC_METHOD("Viewport", NULL, GLVIEWPORT, "(X)i(Y)i(Width)i(Height)i"), + + /* Coloring and Lighting - see GLcolorLighting.h */ + GB_STATIC_METHOD("Color3f", NULL, GLCOLOR3F, "(Red)f(Green)f(Blue)f"), + GB_STATIC_METHOD("Color4f", NULL, GLCOLORF, "(Red)f(Green)f(Blue)f(Alpha)f"), + GB_STATIC_METHOD("Colorf", NULL, GLCOLORF, "(Red)f(Green)f(Blue)f[(Alpha)f]"), + GB_STATIC_METHOD("Color3i", NULL, GLCOLOR3I, "(Red)i(Green)i(Blue)i"), + GB_STATIC_METHOD("Color4i", NULL, GLCOLORI, "(Red)i(Green)i(Blue)i(Alpha)i"), + GB_STATIC_METHOD("Colori", NULL, GLCOLORI, "(Red)i(Green)i(Blue)i[(Alpha)i]"), + GB_STATIC_METHOD("Colorfv", NULL, GLCOLORFV, "(Colors)Float[]"), + GB_STATIC_METHOD("Coloriv", NULL, GLCOLORIV, "(Colors)Integer[]"), + GB_STATIC_METHOD("ColorMaterial", NULL, GLCOLORMATERIAL, "(Face)i(Mode)i"), + GB_STATIC_METHOD("FrontFace", NULL, GLFRONTFACE, "(Mode)i"), + GB_STATIC_METHOD("GetLightfv", "Float[]", GLGETLIGHTFV, "(Light)i(Pname)i"), + GB_STATIC_METHOD("GetLightiv", "Integer[]", GLGETLIGHTIV, "(Light)i(Pname)i"), + GB_STATIC_METHOD("GetMaterialfv", "Float[]", GLGETMATERIALFV, "(Face)i(Pname)i"), + GB_STATIC_METHOD("GetMaterialiv", "Integer[]", GLGETMATERIALIV, "(Face)i(Pname)i"), + GB_STATIC_METHOD("Indexf", NULL, GLINDEXF, "(Index)f"), + GB_STATIC_METHOD("Indexi", NULL, GLINDEXI, "(Index)i"), + GB_STATIC_METHOD("Lightf", NULL, GLLIGHTF, "(Light)i(Pname)i(Param)f"), + GB_STATIC_METHOD("Lighti", NULL, GLLIGHTI, "(Light)i(Pname)i(Param)i"), + GB_STATIC_METHOD("Lightfv", NULL, GLLIGHTFV, "(Light)i(Pname)i(Params)Float[]"), + GB_STATIC_METHOD("Lightiv", NULL, GLLIGHTIV, "(Light)i(Pname)i(Params)Integer[]"), + GB_STATIC_METHOD("LightModelf", NULL, GLLIGHTMODELF, "(Pname)i(Param)f"), + GB_STATIC_METHOD("LightModeli", NULL, GLLIGHTMODELI, "(Pname)i(Param)i"), + GB_STATIC_METHOD("LightModelfv", NULL, GLLIGHTMODELFV, "(Pname)i(Params)Float[]"), + GB_STATIC_METHOD("LightModeliv", NULL, GLLIGHTMODELIV, "(Pname)i(Params)Integer[]"), + GB_STATIC_METHOD("Materialf", NULL, GLMATERIALF, "(Face)i(Pname)i(Param)f"), + GB_STATIC_METHOD("Materiali", NULL, GLMATERIALI, "(Face)i(Pname)i(Param)i"), + GB_STATIC_METHOD("Materialfv", NULL, GLMATERIALFV, "(Face)i(Pname)i(Params)Float[]"), + GB_STATIC_METHOD("Materialiv", NULL, GLMATERIALIV, "(Face)i(Pname)i(Params)Integer[]"), + GB_STATIC_METHOD("Normal3f", NULL, GLNORMAL3F, "(Nx)f(Ny)f(Nz)f"), + GB_STATIC_METHOD("Normal3i", NULL, GLNORMAL3I, "(Nx)i(Ny)i(Nz)i"), + GB_STATIC_METHOD("Normal3fv", NULL, GLNORMAL3FV, "(Params)Float[]"), + GB_STATIC_METHOD("Normal3iv", NULL, GLNORMAL3IV, "(Params)Integer[]"), + GB_STATIC_METHOD("ShadeModel", NULL, GLSHADEMODEL, "(Model)i"), + + /* Clipping - see GLclipping.h */ + GB_STATIC_METHOD("ClipPlane", NULL, GLCLIPPLANE, "(Plane)i(Equation)Float[]"), + GB_STATIC_METHOD("GetClipPlane", "Float[]", GLGETCLIPPLANE, "(Plane)i"), + + /* Rasterization - see GLrasterization.h */ + //GB_STATIC_METHOD("Bitmap", NULL, GLBITMAP, NULL), //TODO + GB_STATIC_METHOD("CullFace", NULL, GLCULLFACE, "(Mode)i"), + //GB_STATIC_METHOD("GetPolygonStipple", "i", GLPOLYGONSTIPPLE, NULL), //TODO + GB_STATIC_METHOD("LineStipple", NULL, GLLINESTIPPLE, "(Factor)i(Pattern)i"), + GB_STATIC_METHOD("LineWidth", NULL, GLLINEWIDTH, "(Width)f"), + GB_STATIC_METHOD("PointSize", NULL, GLPOINTSIZE, "(Size)f"), + GB_STATIC_METHOD("PolygonMode", NULL, GLPOLYGONMODE, "(Face)i(Mode)i"), + GB_STATIC_METHOD("PolygonOffset", NULL, GLPOLYGONOFFSET, "(Factor)f(Units)f"), + //GB_STATIC_METHOD("PolygonStipple", NULL, GLPOLYGONSTIPPLE, "(Mask)i"), //TODO + GB_STATIC_METHOD("RasterPos2f", NULL, GLRASTERPOS2F, "(X)f(Y)f"), + GB_STATIC_METHOD("RasterPos3f", NULL, GLRASTERPOS3F, "(X)f(Y)f(Z)f"), + GB_STATIC_METHOD("RasterPos4f", NULL, GLRASTERPOSF, "(X)f(Y)f(Z)f(W)f"), + GB_STATIC_METHOD("RasterPosf", NULL, GLRASTERPOSF, "(X)f(Y)f[(Z)f(W)f]"), + GB_STATIC_METHOD("RasterPos2i", NULL, GLRASTERPOS2I, "(X)i(Y)i"), + GB_STATIC_METHOD("RasterPos3i", NULL, GLRASTERPOS3I, "(X)i(Y)i(Z)i"), + GB_STATIC_METHOD("RasterPos4i", NULL, GLRASTERPOSI, "(X)i(Y)i(Z)i(W)i"), + GB_STATIC_METHOD("RasterPosi", NULL, GLRASTERPOSI, "(X)i(Y)i[(Z)i(W)i]"), + GB_STATIC_METHOD("RasterPosfv", NULL, GLRASTERPOSFV, "(Coords)Float[]"), + GB_STATIC_METHOD("RasterPosiv", NULL, GLRASTERPOSIV, "(Coords)Integer[]"), + + /* PixelOperations - see GLpixelOperations.h */ + // TODO glReadPixels + GB_STATIC_METHOD("CopyPixels", NULL, GLCOPYPIXELS, "(X)i(Y)i(Width)i(Height)i(Buffer)i"), + GB_STATIC_METHOD("PixelStoref", NULL, GLPIXELSTOREF, "(Pname)i(Param)f"), + GB_STATIC_METHOD("PixelStorei", NULL, GLPIXELSTOREI, "(Pname)i(Param)i"), + GB_STATIC_METHOD("PixelTransferf", NULL, GLPIXELTRANSFERF, "(Pname)i(Param)f"), + GB_STATIC_METHOD("PixelTransferi", NULL, GLPIXELTRANSFERI, "(Pname)i(Param)i"), + GB_STATIC_METHOD("ReadBuffer", NULL, GLREADBUFFER, "(Mode)i"), + GB_STATIC_METHOD("DrawPixels", NULL, GLDRAWPIXELS, "(Image)Image;"), + + /* Modes and Execution - see GLmodesExec.h */ + GB_STATIC_METHOD("Disable", NULL, GLDISABLE, "(Capacity)i"), + GB_STATIC_METHOD("Enable", NULL, GLENABLE, "(Capacity)i"), + GB_STATIC_METHOD("Flush", NULL, GLFLUSH, NULL), + GB_STATIC_METHOD("Finish", NULL, GLFINISH, NULL), + GB_STATIC_METHOD("Hint", NULL, GLHINT, "(Target)i(Mode)i"), + GB_STATIC_METHOD("IsEnabled", "b", GLISENABLED, "(Capacity)i"), + + /* Fog - see GLfog.h */ + GB_STATIC_METHOD("Fogf", NULL, GLFOGF, "(Pname)i(Param)f"), + GB_STATIC_METHOD("Fogi", NULL, GLFOGI, "(Pname)i(Param)i"), + GB_STATIC_METHOD("Fogfv", NULL, GLFOGFV, "(Pname)i(Params)Float[]"), + GB_STATIC_METHOD("Fogiv", NULL, GLFOGIV, "(Pname)i(Params)Integer[]"), + + /* Texture Mapping - see GLtextureMapping.h */ + GB_STATIC_METHOD("BindTexture", NULL, GLBINDTEXTURE, "(Target)i(Texture)i"), + GB_STATIC_METHOD("ActiveTexture", NULL, GLACTIVETEXTURE, "(Texture)i" ), + // TODO adapt to gambas + GB_STATIC_METHOD("CopyTexImage1D", NULL, GLCOPYTEXIMAGE1D, "(Target)i(Level)i(Format)i(X)i(Y)i(Width)i(Border)i"), + GB_STATIC_METHOD("CopyTexImage2D", NULL, GLCOPYTEXIMAGE2D, "(Target)i(Level)i(Format)i(X)i(Y)i(Width)i(Height)i(Border)i"), + GB_STATIC_METHOD("DeleteTextures", NULL, GLDELETETEXTURES, "(Textures)Integer[]"), + GB_STATIC_METHOD("GenTextures", "Integer[]", GLGENTEXTURES, "(Count)i"), + GB_STATIC_METHOD("IsTexture", "b", GLISTEXTURE, "(Texture)i"), + GB_STATIC_METHOD("TexCoord1f", NULL, GLTEXCOORD1F, "(S)f"), + GB_STATIC_METHOD("TexCoord2f", NULL, GLTEXCOORD2F, "(S)f(T)f"), + GB_STATIC_METHOD("TexCoord3f", NULL, GLTEXCOORD3F, "(S)f(T)f(R)f"), + GB_STATIC_METHOD("TexCoord4f", NULL, GLTEXCOORDF, "(S)f(T)f(R)f(Q)f"), + GB_STATIC_METHOD("TexCoordf", NULL, GLTEXCOORDF, "(S)f[(T)f(R)f(Q)f]"), + GB_STATIC_METHOD("TexCoord1i", NULL, GLTEXCOORD1I, "(S)i"), + GB_STATIC_METHOD("TexCoord2i", NULL, GLTEXCOORD2I, "(S)i(T)i"), + GB_STATIC_METHOD("TexCoord3i", NULL, GLTEXCOORD3I, "(S)i(T)i(R)i"), + GB_STATIC_METHOD("TexCoord4i", NULL, GLTEXCOORDI, "(S)i(T)i(R)i(Q)i"), + GB_STATIC_METHOD("TexCoordi", NULL, GLTEXCOORDI, "(S)i[(T)i(R)i(Q)i]"), + GB_STATIC_METHOD("TexEnvf", NULL, GLTEXENVF, "(Target)i(Pname)i(Param)f"), + GB_STATIC_METHOD("TexEnvfv", NULL, GLTEXENVFV, "(Target)i(Pname)i(Params)Float[]"), + GB_STATIC_METHOD("TexEnvi", NULL, GLTEXENVI, "(Target)i(Pname)i(Param)i"), + GB_STATIC_METHOD("TexEnviv", NULL, GLTEXENVIV, "(Target)i(Pname)i(Params)Integer[]"), + GB_STATIC_METHOD("TexImage1D", NULL, GLTEXIMAGE1D, "(Image)Image;[(Level)i(Border)i]"), + GB_STATIC_METHOD("TexImage2D", NULL, GLTEXIMAGE2D,"(Image)Image;[(Level)i(Border)i]"), + GB_STATIC_METHOD("TexSubImage1D", NULL, GLTEXSUBIMAGE2D,"(Image)Image;(XOffset)i(Width)i[(Level)i]"), + GB_STATIC_METHOD("TexSubImage2D", NULL, GLTEXSUBIMAGE2D,"(Image)Image;(XOffset)i(YOffset)i(Width)i(Height)i[(Level)i]"), + GB_STATIC_METHOD("TexParameterf", NULL, GLTEXPARAMETERF, "(Target)i(Pname)i(Param)f"), + GB_STATIC_METHOD("TexParameterfv", NULL, GLTEXPARAMETERFV, "(Target)i(Pname)i(Params)Float[]"), + GB_STATIC_METHOD("TexParameteri", NULL, GLTEXPARAMETERI, "(Target)i(Pname)i(Param)i"), + GB_STATIC_METHOD("TexParameteriv", NULL, GLTEXPARAMETERIV, "(Target)i(Pname)i(Params)Integer[]"), + GB_STATIC_METHOD("TexGeni", NULL, GLTEXGENI, "(Coord)i(Pname)i(Param)i"), + GB_STATIC_METHOD("MultiTexCoord2f", NULL, GLMULTITEXCOORD2F, "(Target)i(S)f(T)f"), + + /* FrameBuffer Operations - see GLframeBufferOps.h */ + GB_STATIC_METHOD("Accum", NULL, GLACCUM, "(Operation)i(Value)f"), + GB_STATIC_METHOD("AlphaFunc", NULL, GLALPHAFUNC, "(Function)i(Reference)f"), + GB_STATIC_METHOD("BlendFunc", NULL, GLBLENDFUNC, "(SrcFactor)i(DstFactor)i"), + GB_STATIC_METHOD("Clear", NULL, GLCLEAR, "(Mask)i"), + GB_STATIC_METHOD("ClearAccum", NULL, GLCLEARACCUM, "(Red)f(Green)f(Blue)f(Alpha)f"), + GB_STATIC_METHOD("ClearColor", NULL, GLCLEARCOLOR, "(Red)f(Green)f(Blue)f(Alpha)f"), + GB_STATIC_METHOD("ClearDepth", NULL, GLCLEARDEPTH, "(Depth)f"), + GB_STATIC_METHOD("ClearIndex", NULL, GLCLEARINDEX, "(Value)f"), + GB_STATIC_METHOD("ClearStencil", NULL, GLCLEARSTENCIL, "(Value)i"), + GB_STATIC_METHOD("ColorMask", NULL, GLCOLORMASK, "(Red)b(Green)b(Blue)b(Alpha)b"), + GB_STATIC_METHOD("DrawBuffer", NULL, GLDRAWBUFFER, "(Mode)i"), + GB_STATIC_METHOD("DepthFunc", NULL, GLDEPTHFUNC, "(Function)i"), + GB_STATIC_METHOD("DepthMask", NULL, GLDEPTHMASK, "(Flag)b"), + GB_STATIC_METHOD("IndexMask", NULL, GLINDEXMASK, "(Mask)i"), + GB_STATIC_METHOD("LogicOp", NULL, GLLOGICOP, "(Opcode)i"), + GB_STATIC_METHOD("Scissor", NULL, GLSCISSOR, "(X)i(Y)i(Width)i(Height)i"), + GB_STATIC_METHOD("StencilFunc", NULL, GLSTENCILFUNC, "(Function)i(Reference)i(Mask)i"), + GB_STATIC_METHOD("StencilMask", NULL, GLSTENCILMASK, "(Mask)i"), + GB_STATIC_METHOD("StencilOp", NULL, GLSTENCILOP, "(Fail)i(Zfail)i(Zpass)i"), + + /* Selection and Feedback - see GLselectPixmap.h */ + GB_STATIC_METHOD("FeedbackBuffer", NULL, GLFEEDBACKBUFFER, "(Type)i"), + GB_STATIC_METHOD("InitNames", NULL, GLINITNAMES, NULL), + GB_STATIC_METHOD("LoadName", NULL, GLLOADNAME, "(Name)i"), + GB_STATIC_METHOD("PassThrough", NULL, GLPASSTHROUGH, "(Token)f"), + GB_STATIC_METHOD("PopName", NULL, GLPOPNAME, NULL), + GB_STATIC_METHOD("PushName", NULL, GLPUSHNAME, "(Name)i"), + GB_STATIC_METHOD("RenderMode", "Array", GLRENDERMODE, "(Mode)i"), + GB_STATIC_METHOD("SelectBuffer", NULL, GLSELECTBUFFER, NULL), + + /* Evaluators - see Gleval.h/c */ + GB_STATIC_METHOD("Map1f", NULL, GLMAP1F, "(Target)i(U1)f(U2)f(Stride)i(Order)i(Points)Float[]"), + GB_STATIC_METHOD("Map2f", NULL, GLMAP2F, "(Target)i(U1)f(U2)f(Ustride)i(Uorder)i(V1)f(V2)f(Vstride)i(Vorder)i(Points)Float[]"), + GB_STATIC_METHOD("EvalCoord1f", NULL, GLEVALCOORD1F, "(U)f"), + GB_STATIC_METHOD("EvalCoord2f", NULL, GLEVALCOORD2F, "(U)f(V)f"), + GB_STATIC_METHOD("EvalCoord2fv", NULL, GLEVALCOORD2FV, "(Coords)Float[]"), + GB_STATIC_METHOD("MapGrid1f", NULL, GLMAPGRID1F, "(Un)i(U)f(V)f"), + GB_STATIC_METHOD("MapGrid2f", NULL, GLMAPGRID2F, "(Un)i(U1)f(U2)f(Vn)i(V1)f(V2)f"), + GB_STATIC_METHOD("EvalPoint1", NULL, GLEVALPOINT1, "(I)i"), + GB_STATIC_METHOD("EvalPoint2", NULL, GLEVALPOINT2, "(I)i(J)i"), + GB_STATIC_METHOD("EvalMesh1", NULL, GLEVALMESH1, "(Mode)i(I1)i(I2)i"), + GB_STATIC_METHOD("EvalMesh2", NULL, GLEVALMESH2, "(Mode)i(I1)i(I2)i(J1)i(J2)i"), + + /* glGetxxxx calls - see GLinfo.h/c */ + GB_STATIC_METHOD("GetBooleanv", "Boolean[]", GLGETBOOLEANV, "(Parameter)i"), + GB_STATIC_METHOD("GetFloatv", "Float[]", GLGETFLOATV, "(Parameter)i"), + GB_STATIC_METHOD("GetIntegerv", "Integer[]", GLGETINTEGERV, "(Parameter)i]"), + GB_STATIC_METHOD("GetString", "s", GLGETSTRING, "(Name)i"), + + /*********************/ + /* Opengl Extensions */ + /*********************/ + /* See http://www.opengl.org/registry/ */ + + /* ARB extensions */ + + /* 38; GL_ARB_texture_rectangle */ + /* Only constants */ + + /* EXT and Vendors extensions */ + + /* 310; GL_EXT_framebuffer_object */ + /* framebufferobject.c */ + GB_STATIC_METHOD("BindFramebufferEXT", NULL, GLBINDFRAMEBUFFEREXT, "(Target)i(Framebuffer)i"), + GB_STATIC_METHOD("BindRenderbufferEXT",NULL, GLBINDRENDERBUFFEREXT, "(Target)i(Renderbuffer)i"), + GB_STATIC_METHOD("CheckFramebufferStatusEXT", "i", GLCHECKFRAMEBUFFERSTATUSEXT, "(Target)i"), + GB_STATIC_METHOD("DeleteFramebuffersEXT", NULL, GLDELETEFRAMEBUFFERSEXT, "(Framebuffers)Integer[]"), + GB_STATIC_METHOD("DeleteRenderbuffersEXT", NULL, GLDELETERENDERBUFFERSEXT, "(Renderbuffers)Integer[]"), + GB_STATIC_METHOD("FramebufferRenderbufferEXT", NULL, GLFRAMEBUFFERRENDERBUFFEREXT, "(Target)i(Attachment)i(RenderbufferTarget)i(Renderbuffer)i"), + GB_STATIC_METHOD("FramebufferTexture1DEXT", NULL, GLFRAMEBUFFERTEXTURE1DEXT, "(Target)i(Attachment)i(TexTarget)i(Texture)i(Level)i"), + GB_STATIC_METHOD("FramebufferTexture2DEXT", NULL, GLFRAMEBUFFERTEXTURE2DEXT, "(Target)i(Attachment)i(TexTarget)i(Texture)i(Level)i"), + GB_STATIC_METHOD("FramebufferTexture3DEXT", NULL, GLFRAMEBUFFERTEXTURE3DEXT, "(Target)i(Attachment)i(TexTarget)i(Texture)i(Level)i(Zoffset)i"), + GB_STATIC_METHOD("GenerateMipmapEXT", NULL, GLGENERATEMIPMAPEXT, "(Target)i"), + GB_STATIC_METHOD("GenFramebuffersEXT", "Integer[]", GLGENFRAMEBUFFERSEXT, "(Count)i"), + GB_STATIC_METHOD("GenRenderbuffersEXT", "Integer[]", GLGENRENDERBUFFERSEXT, "(Count)i"), + GB_STATIC_METHOD("GetFramebufferAttachmentParameterivEXT", "Integer[]", GLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXT, "(Target)i(Attachment)i(Pname)i"), + GB_STATIC_METHOD("GetRenderbufferParameterivEXT", "Integer[]", GLGETRENDERBUFFERPARAMETERIVEXT, "(Target)i(Pname)i"), + GB_STATIC_METHOD("IsFramebufferEXT", "b", GLISFRAMEBUFFEREXT, "(Framebuffer)i"), + GB_STATIC_METHOD("IsRenderbufferEXT", "b", GLISRENDERBUFFEREXT, "(Renderbuffer)i"), + GB_STATIC_METHOD("RenderbufferStorageEXT", NULL, GLRENDERBUFFERSTORAGEEXT, "(Target)i(InternalFormat)i(Width)i(Height)i"), + + // Old constant naming support + GB_STATIC_METHOD("_unknown", "v", GL_unknown, "."), + GB_STATIC_METHOD("_property", "b", GL_property, NULL), + + /********************/ + /* opengl constants */ + /********************/ + + GB_CONSTANT("FLOAT" , "i", GL_FLOAT), + GB_CONSTANT("INTEGER" , "i", GL_FLOAT), + GB_CONSTANT("UNSIGNED_BYTE", "i", GL_UNSIGNED_BYTE), + GB_CONSTANT("UNSIGNED_INT", "i", GL_UNSIGNED_INT), + GB_CONSTANT("DOUBLE", "i", GL_DOUBLE), + + /* GetString */ + GB_CONSTANT("EXTENSIONS", "i", GL_EXTENSIONS), + GB_CONSTANT("VENDOR", "i", GL_VENDOR), + GB_CONSTANT("RENDERER", "i", GL_RENDERER), + GB_CONSTANT("VERSION", "i", GL_VERSION), + GB_CONSTANT("SHADING_LANGUAGE_VERSION", "i", GL_SHADING_LANGUAGE_VERSION), + + /* Primitives */ + GB_CONSTANT("POINTS", "i", GL_POINTS), + GB_CONSTANT("LINES", "i", GL_LINES), + GB_CONSTANT("LINE_LOOP", "i", GL_LINE_LOOP), + GB_CONSTANT("LINE_STRIP", "i", GL_LINE_STRIP), + GB_CONSTANT("TRIANGLES", "i", GL_TRIANGLES), + GB_CONSTANT("TRIANGLE_STRIP", "i", GL_TRIANGLE_STRIP), + GB_CONSTANT("TRIANGLE_FAN", "i", GL_TRIANGLE_FAN), + GB_CONSTANT("QUADS", "i", GL_QUADS), + GB_CONSTANT("QUAD_STRIP", "i", GL_QUAD_STRIP), + GB_CONSTANT("POLYGON", "i", GL_POLYGON), + GB_CONSTANT("VERTEX_ARRAY", "i", GL_VERTEX_ARRAY), + GB_CONSTANT("COLOR_ARRAY", "i", GL_COLOR_ARRAY), + GB_CONSTANT("NORMAL_ARRAY", "i", GL_NORMAL_ARRAY), + GB_CONSTANT("TEXTURE_COORD_ARRAY", "i", GL_TEXTURE_COORD_ARRAY), + + /* Matrix Mode */ + GB_CONSTANT("MATRIX_MODE", "i", GL_MATRIX_MODE), + GB_CONSTANT("MODELVIEW", "i", GL_MODELVIEW), + GB_CONSTANT("PROJECTION", "i", GL_PROJECTION), + GB_CONSTANT("TEXTURE", "i", GL_TEXTURE), + + /* Points */ + GB_CONSTANT("POINT_SMOOTH", "i", GL_POINT_SMOOTH), + GB_CONSTANT("POINT_SIZE", "i", GL_POINT_SIZE), + GB_CONSTANT("SMOOTH_POINT_SIZE_GRANULARITY", "i", GL_SMOOTH_POINT_SIZE_GRANULARITY), + GB_CONSTANT("SMOOTH_POINT_SIZE_RANGE", "i", GL_SMOOTH_POINT_SIZE_RANGE), + + /* Display Lists */ + GB_CONSTANT("COMPILE", "i", GL_COMPILE), + GB_CONSTANT("COMPILE_AND_EXECUTE", "i", GL_COMPILE_AND_EXECUTE), + GB_CONSTANT("LIST_BASE", "i", GL_LIST_BASE), + GB_CONSTANT("LIST_INDEX", "i", GL_LIST_INDEX), + GB_CONSTANT("LIST_MODE" ,"i", GL_LIST_MODE), + + /* Accumulation buffer */ + GB_CONSTANT("ACCUM_", "i", GL_ACCUM), + GB_CONSTANT("ACCUM_ALPHA_BITS", "i", GL_ACCUM_ALPHA_BITS), + GB_CONSTANT("ACCUM_CLEAR_VALUE", "i", GL_ACCUM_CLEAR_VALUE), + GB_CONSTANT("ACCUM_BLUE_BITS", "i", GL_ACCUM_BLUE_BITS), + GB_CONSTANT("ACCUM_GREEN_BITS", "i", GL_ACCUM_GREEN_BITS), + GB_CONSTANT("ACCUM_RED_BITS", "i", GL_ACCUM_RED_BITS), + GB_CONSTANT("ADD", "i", GL_ADD), + GB_CONSTANT("LOAD", "i", GL_LOAD), + GB_CONSTANT("MULT", "i", GL_MULT), + GB_CONSTANT("RETURN", "i", GL_RETURN), + + /* User clipping planes */ + GB_CONSTANT("CLIP_PLANE0", "i", GL_CLIP_PLANE0), + GB_CONSTANT("CLIP_PLANE1", "i", GL_CLIP_PLANE1), + GB_CONSTANT("CLIP_PLANE2", "i", GL_CLIP_PLANE2), + GB_CONSTANT("CLIP_PLANE3", "i", GL_CLIP_PLANE3), + GB_CONSTANT("CLIP_PLANE4", "i", GL_CLIP_PLANE4), + GB_CONSTANT("CLIP_PLANE5", "i", GL_CLIP_PLANE5), + + /* Alpha testing */ + GB_CONSTANT("ALPHA_TEST", "i", GL_ALPHA_TEST), + GB_CONSTANT("ALPHA_TEST_REF", "i", GL_ALPHA_TEST_REF), + GB_CONSTANT("ALPHA_TEST_FUNC", "i", GL_ALPHA_TEST_FUNC), + + /* glPush/PopAttrib bits */ + GB_CONSTANT("ACCUM_BUFFER_BIT", "i", GL_ACCUM_BUFFER_BIT), + GB_CONSTANT("ALL_ATTRIB_BITS", "i", GL_ALL_ATTRIB_BITS), + GB_CONSTANT("CURRENT_BIT", "i", GL_CURRENT_BIT), + GB_CONSTANT("COLOR_BUFFER_BIT", "i", GL_COLOR_BUFFER_BIT), + GB_CONSTANT("DEPTH_BUFFER_BIT", "i", GL_DEPTH_BUFFER_BIT), + GB_CONSTANT("ENABLE_BIT", "i", GL_ENABLE_BIT), + GB_CONSTANT("EVAL_BIT", "i", GL_EVAL_BIT), + GB_CONSTANT("FOG_BIT", "i", GL_FOG_BIT), + GB_CONSTANT("HINT_BIT", "i", GL_HINT_BIT), + GB_CONSTANT("LIGHTING_BIT", "i", GL_LIGHTING_BIT), + GB_CONSTANT("LINE_BIT", "i", GL_LINE_BIT), + GB_CONSTANT("LIST_BIT", "i", GL_LIST_BIT), + GB_CONSTANT("PIXEL_MODE_BIT", "i", GL_PIXEL_MODE_BIT), + GB_CONSTANT("POINT_BIT", "i", GL_POINT_BIT), + GB_CONSTANT("POLYGON_BIT", "i", GL_POLYGON_BIT), + GB_CONSTANT("POLYGON_STIPPLE_BIT", "i", GL_POLYGON_STIPPLE_BIT), + GB_CONSTANT("SCISSOR_BIT", "i", GL_SCISSOR_BIT), + GB_CONSTANT("STENCIL_BUFFER_BIT", "i", GL_STENCIL_BUFFER_BIT), + GB_CONSTANT("TEXTURE_BIT", "i", GL_TEXTURE_BIT), + GB_CONSTANT("TRANSFORM_BIT", "i", GL_TRANSFORM_BIT), + GB_CONSTANT("VIEWPORT_BIT", "i", GL_VIEWPORT_BIT), + + /* Polygons */ + GB_CONSTANT("POINT", "i", GL_POINT), + GB_CONSTANT("LINE", "i", GL_LINE), + GB_CONSTANT("FILL", "i", GL_FILL), + GB_CONSTANT("CW", "i", GL_CW), + GB_CONSTANT("CCW", "i", GL_CCW), + GB_CONSTANT("FRONT", "i", GL_FRONT), + GB_CONSTANT("BACK", "i", GL_BACK), + GB_CONSTANT("POLYGON_MODE", "i", GL_POLYGON_MODE), + GB_CONSTANT("POLYGON_SMOOTH", "i", GL_POLYGON_SMOOTH), + GB_CONSTANT("POLYGON_STIPPLE", "i", GL_POLYGON_STIPPLE), + GB_CONSTANT("EDGE_FLAG", "i", GL_EDGE_FLAG), + GB_CONSTANT("CULL_FACE", "i", GL_CULL_FACE), + GB_CONSTANT("CULL_FACE_MODE", "i", GL_CULL_FACE_MODE), + GB_CONSTANT("FRONT_FACE", "i", GL_FRONT_FACE), + GB_CONSTANT("POLYGON_OFFSET_FACTOR", "i", GL_POLYGON_OFFSET_FACTOR), + GB_CONSTANT("POLYGON_OFFSET_UNITS", "i", GL_POLYGON_OFFSET_UNITS), + GB_CONSTANT("POLYGON_OFFSET_POINT", "i", GL_POLYGON_OFFSET_POINT), + GB_CONSTANT("POLYGON_OFFSET_LINE", "i", GL_POLYGON_OFFSET_LINE), + GB_CONSTANT("POLYGON_OFFSET_FILL", "i", GL_POLYGON_OFFSET_FILL), + + /* Depth buffer */ + GB_CONSTANT("NEVER", "i", GL_NEVER), + GB_CONSTANT("LESS", "i", GL_LESS), + GB_CONSTANT("EQUAL", "i", GL_EQUAL), + GB_CONSTANT("LEQUAL", "i", GL_LEQUAL), + GB_CONSTANT("GREATER", "i", GL_GREATER), + GB_CONSTANT("NOTEQUAL", "i", GL_NOTEQUAL), + GB_CONSTANT("GEQUAL", "i", GL_GEQUAL), + GB_CONSTANT("ALWAYS", "i", GL_ALWAYS), + GB_CONSTANT("DEPTH_TEST", "i", GL_DEPTH_TEST), + GB_CONSTANT("DEPTH_BITS", "i", GL_DEPTH_BITS), + GB_CONSTANT("DEPTH_CLEAR_VALUE", "i", GL_DEPTH_CLEAR_VALUE), + GB_CONSTANT("DEPTH_FUNC", "i", GL_DEPTH_FUNC), + GB_CONSTANT("DEPTH_RANGE", "i", GL_DEPTH_RANGE), + GB_CONSTANT("DEPTH_WRITEMASK", "i", GL_DEPTH_WRITEMASK), + GB_CONSTANT("DEPTH_COMPONENT", "i", GL_DEPTH_COMPONENT), + + /* Lines */ + GB_CONSTANT("LINE_SMOOTH", "i", GL_LINE_SMOOTH), + GB_CONSTANT("LINE_STIPPLE", "i", GL_LINE_STIPPLE), + GB_CONSTANT("LINE_STIPPLE_PATTERN", "i", GL_LINE_STIPPLE_PATTERN), + GB_CONSTANT("LINE_STIPPLE_REPEAT", "i", GL_LINE_STIPPLE_REPEAT), + GB_CONSTANT("LINE_WIDTH", "i", GL_LINE_WIDTH), + GB_CONSTANT("SMOOTH_LINE_WIDTH_GRANULARITY", "i", GL_SMOOTH_LINE_WIDTH_GRANULARITY), + GB_CONSTANT("SMOOTH_LINE_WIDTH_RANGE", "i", GL_SMOOTH_LINE_WIDTH_RANGE), + + /* Render Mode */ + GB_CONSTANT("FEEDBACK", "i", GL_FEEDBACK), + GB_CONSTANT("RENDER", "i", GL_RENDER), + GB_CONSTANT("SELECT", "i", GL_SELECT), + + /* Feedback */ + GB_CONSTANT("_2D", "i", GL_2D), + GB_CONSTANT("_3D", "i", GL_3D), + GB_CONSTANT("_3D_COLOR", "i", GL_3D_COLOR), + GB_CONSTANT("_3D_COLOR_TEXTURE", "i", GL_3D_COLOR_TEXTURE), + GB_CONSTANT("_4D_COLOR_TEXTURE", "i", GL_4D_COLOR_TEXTURE), + GB_CONSTANT("POINT_TOKEN", "i", GL_POINT_TOKEN), + GB_CONSTANT("LINE_TOKEN", "i", GL_LINE_TOKEN), + GB_CONSTANT("LINE_RESET_TOKEN", "i", GL_LINE_RESET_TOKEN), + GB_CONSTANT("POLYGON_TOKEN", "i", GL_POLYGON_TOKEN), + GB_CONSTANT("BITMAP_TOKEN", "i", GL_BITMAP_TOKEN), + GB_CONSTANT("DRAW_PIXEL_TOKEN", "i", GL_DRAW_PIXEL_TOKEN), + GB_CONSTANT("COPY_PIXEL_TOKEN", "i", GL_COPY_PIXEL_TOKEN), + GB_CONSTANT("PASS_THROUGH_TOKEN", "i", GL_PASS_THROUGH_TOKEN), + GB_CONSTANT("FEEDBACK_BUFFER_POINTER", "i", GL_FEEDBACK_BUFFER_POINTER), + GB_CONSTANT("FEEDBACK_BUFFER_SIZE", "i", GL_FEEDBACK_BUFFER_SIZE), + GB_CONSTANT("FEEDBACK_BUFFER_TYPE", "i", GL_FEEDBACK_BUFFER_TYPE), + + /* Selection */ + GB_CONSTANT("SELECTION_BUFFER_POINTER", "i", GL_SELECTION_BUFFER_POINTER), + GB_CONSTANT("SELECTION_BUFFER_SIZE", "i", GL_SELECTION_BUFFER_SIZE), + + /* Fog */ + GB_CONSTANT("FOG", "i", GL_FOG), + GB_CONSTANT("FOG_MODE", "i", GL_FOG_MODE), + GB_CONSTANT("FOG_DENSITY", "i", GL_FOG_DENSITY), + GB_CONSTANT("FOG_COLOR", "i", GL_FOG_COLOR), + GB_CONSTANT("FOG_INDEX", "i", GL_FOG_INDEX), + GB_CONSTANT("FOG_START", "i", GL_FOG_START), + GB_CONSTANT("FOG_END", "i", GL_FOG_END), + GB_CONSTANT("LINEAR", "i", GL_LINEAR), + GB_CONSTANT("EXP", "i", GL_EXP), + GB_CONSTANT("EXP2", "i", GL_EXP2), + + /* Errors */ + GB_CONSTANT("NO_ERROR", "i", GL_NO_ERROR), + GB_CONSTANT("INVALID_VALUE", "i", GL_INVALID_VALUE), + GB_CONSTANT("INVALID_ENUM", "i", GL_INVALID_ENUM), + GB_CONSTANT("INVALID_OPERATION", "i", GL_INVALID_OPERATION), + GB_CONSTANT("STACK_OVERFLOW", "i", GL_STACK_OVERFLOW), + GB_CONSTANT("STACK_UNDERFLOW", "i", GL_STACK_UNDERFLOW), + GB_CONSTANT("OUT_OF_MEMORY", "i", GL_OUT_OF_MEMORY), + + /* Lighting */ + GB_CONSTANT("LIGHTING", "i", GL_LIGHTING), + GB_CONSTANT("LIGHT0", "i", GL_LIGHT0), + GB_CONSTANT("LIGHT1", "i", GL_LIGHT1), + GB_CONSTANT("LIGHT2", "i", GL_LIGHT2), + GB_CONSTANT("LIGHT3", "i", GL_LIGHT3), + GB_CONSTANT("LIGHT4", "i", GL_LIGHT4), + GB_CONSTANT("LIGHT5", "i", GL_LIGHT5), + GB_CONSTANT("LIGHT6", "i", GL_LIGHT6), + GB_CONSTANT("LIGHT7", "i", GL_LIGHT7), + GB_CONSTANT("SPOT_EXPONENT", "i", GL_SPOT_EXPONENT), + GB_CONSTANT("SPOT_CUTTOFF", "i", GL_SPOT_CUTOFF), + GB_CONSTANT("CONSTANT_ATTENUATION", "i", GL_CONSTANT_ATTENUATION), + GB_CONSTANT("LINEAR_ATTENUATION", "i", GL_LINEAR_ATTENUATION), + GB_CONSTANT("QUADRATIC_ATTENUATION", "i", GL_QUADRATIC_ATTENUATION), + GB_CONSTANT("AMBIENT", "i", GL_AMBIENT), + GB_CONSTANT("DIFFUSE", "i", GL_DIFFUSE), + GB_CONSTANT("SPECULAR", "i", GL_SPECULAR), + GB_CONSTANT("SHININESS", "i", GL_SHININESS), + GB_CONSTANT("EMISSION", "i", GL_EMISSION), + GB_CONSTANT("POSITION", "i", GL_POSITION), + GB_CONSTANT("SPOT_DIRECTION", "i", GL_SPOT_DIRECTION), + GB_CONSTANT("AMBIENT_AND_DIFFUSE", "i", GL_AMBIENT_AND_DIFFUSE), + GB_CONSTANT("COLOR_INDEXES", "i", GL_COLOR_INDEXES), + GB_CONSTANT("LIGHT_MODEL_TWO_SIDE", "i", GL_LIGHT_MODEL_TWO_SIDE), + GB_CONSTANT("LIGHT_MODEL_LOCAL_VIEWER", "i", GL_LIGHT_MODEL_LOCAL_VIEWER), + GB_CONSTANT("LIGHT_MODEL_AMBIENT", "i", GL_LIGHT_MODEL_AMBIENT), + GB_CONSTANT("FRONT_AND_BACK", "i", GL_FRONT_AND_BACK), + GB_CONSTANT("SHADE_MODEL", "i", GL_SHADE_MODEL), + GB_CONSTANT("FLAT", "i", GL_FLAT), + GB_CONSTANT("SMOOTH", "i", GL_SMOOTH), + GB_CONSTANT("COLOR_MATERIAL", "i", GL_COLOR_MATERIAL), + GB_CONSTANT("COLOR_MATERIAL_FACE", "i", GL_COLOR_MATERIAL_FACE), + GB_CONSTANT("COLOR_MATERIAL_PARAMETER", "i", GL_COLOR_MATERIAL_PARAMETER), + GB_CONSTANT("NORMALIZE", "i", GL_NORMALIZE), + + /* Blending */ + GB_CONSTANT("BLEND", "i", GL_BLEND), + GB_CONSTANT("BLEND_SRC", "i", GL_BLEND_SRC), + GB_CONSTANT("BLEND_DST", "i", GL_BLEND_DST), + GB_CONSTANT("ZERO", "i", GL_ZERO), + GB_CONSTANT("ONE", "i", GL_ONE), + GB_CONSTANT("SRC_COLOR", "i", GL_SRC_COLOR), + GB_CONSTANT("ONE_MINUS_SRC_COLOR", "i", GL_ONE_MINUS_SRC_COLOR), + GB_CONSTANT("SRC_ALPHA", "i", GL_SRC_ALPHA), + GB_CONSTANT("ONE_MINUS_SRC_ALPHA", "i", GL_ONE_MINUS_SRC_ALPHA), + GB_CONSTANT("DST_ALPHA", "i", GL_DST_ALPHA), + GB_CONSTANT("ONE_MINUS_DST_ALPHA", "i", GL_ONE_MINUS_DST_ALPHA), + GB_CONSTANT("DST_COLOR", "i", GL_DST_COLOR), + GB_CONSTANT("ONE_MINUS_DST_COLOR", "i", GL_ONE_MINUS_DST_COLOR), + GB_CONSTANT("SRC_ALPHA_SATURATE", "i", GL_SRC_ALPHA_SATURATE), + + /* Logic Ops */ + GB_CONSTANT("LOGIC_OP", "i", GL_LOGIC_OP), + GB_CONSTANT("INDEX_LOGIC_OP", "i", GL_INDEX_LOGIC_OP), + GB_CONSTANT("COLOR_LOGIC_OP", "i", GL_COLOR_LOGIC_OP), + GB_CONSTANT("LOGIC_OP_MODE", "i", GL_LOGIC_OP_MODE), + GB_CONSTANT("CLEAR_", "i", GL_CLEAR), + GB_CONSTANT("SET", "i", GL_SET), + GB_CONSTANT("COPY", "i", GL_COPY), + GB_CONSTANT("COPY_INVERTED", "i", GL_COPY_INVERTED), + GB_CONSTANT("NOOP", "i", GL_NOOP), + GB_CONSTANT("INVERT", "i", GL_INVERT), + GB_CONSTANT("AND", "i", GL_AND), + GB_CONSTANT("NAND", "i", GL_NAND), + GB_CONSTANT("OR", "i", GL_OR), + GB_CONSTANT("NOR", "i", GL_NOR), + GB_CONSTANT("XOR", "i", GL_XOR), + GB_CONSTANT("EQUIV", "i", GL_EQUIV), + GB_CONSTANT("AND_REVERSE", "i", GL_AND_REVERSE), + GB_CONSTANT("AND_INVERTED", "i", GL_AND_INVERTED), + GB_CONSTANT("OR_REVERSE", "i", GL_OR_REVERSE), + GB_CONSTANT("OR_INVERTED", "i", GL_OR_INVERTED), + + /* Stencil */ + GB_CONSTANT("STENCIL_BITS", "i", GL_STENCIL_BITS), + GB_CONSTANT("STENCIL_TEST", "i", GL_STENCIL_TEST), + GB_CONSTANT("STENCIL_CLEAR_VALUE", "i", GL_STENCIL_CLEAR_VALUE), + GB_CONSTANT("STENCIL_FUNC", "i", GL_STENCIL_FUNC), + GB_CONSTANT("STENCIL_VALUE_MASK", "i", GL_STENCIL_VALUE_MASK), + GB_CONSTANT("STENCIL_FAIL", "i", GL_STENCIL_FAIL), + GB_CONSTANT("STENCIL_PASS_DEPTH_FAIL", "i", GL_STENCIL_PASS_DEPTH_FAIL), + GB_CONSTANT("STENCIL_PASS_DEPTH_PASS", "i", GL_STENCIL_PASS_DEPTH_PASS), + GB_CONSTANT("STENCIL_REF", "i", GL_STENCIL_REF), + GB_CONSTANT("STENCIL_WRITEMASK", "i", GL_STENCIL_WRITEMASK), + GB_CONSTANT("STENCIL_INDEX", "i", GL_STENCIL_INDEX), + GB_CONSTANT("KEEP", "i", GL_KEEP), + GB_CONSTANT("REPLACE", "i", GL_REPLACE), + GB_CONSTANT("INCR", "i", GL_INCR), + GB_CONSTANT("DECR", "i", GL_DECR), + + /* Buffers, Pixel Drawing/Reading */ + GB_CONSTANT("AUX0", "i", GL_AUX0), + GB_CONSTANT("AUX1", "i", GL_AUX1), + GB_CONSTANT("AUX2", "i", GL_AUX2), + GB_CONSTANT("AUX3", "i", GL_AUX3), + GB_CONSTANT("BACK_LEFT", "i", GL_BACK_LEFT), + GB_CONSTANT("BACK_RIGHT", "i", GL_BACK_RIGHT), + GB_CONSTANT("FRONT_LEFT", "i", GL_FRONT_LEFT), + GB_CONSTANT("FRONT_RIGHT", "i", GL_FRONT_RIGHT), + GB_CONSTANT("LEFT", "i", GL_LEFT), + GB_CONSTANT("NONE", "i", GL_NONE), + GB_CONSTANT("RIGHT", "i", GL_RIGHT), + GB_CONSTANT("COLOR_INDEX", "i", GL_COLOR_INDEX), + GB_CONSTANT("RED", "i", GL_RED), + GB_CONSTANT("GREEN", "i", GL_GREEN), + GB_CONSTANT("BLUE", "i", GL_BLUE), + GB_CONSTANT("ALPHA", "i", GL_ALPHA), + GB_CONSTANT("LUMINANCE", "i", GL_LUMINANCE), + GB_CONSTANT("LUMINANCE_ALPHA", "i", GL_LUMINANCE_ALPHA), + GB_CONSTANT("ALPHA_BITS", "i", GL_ALPHA_BITS), + GB_CONSTANT("RED_BITS", "i", GL_RED_BITS), + GB_CONSTANT("GREEN_BITS", "i", GL_GREEN_BITS), + GB_CONSTANT("BLUE_BITS", "i", GL_BLUE_BITS), + GB_CONSTANT("INDEX_BITS", "i", GL_INDEX_BITS), + GB_CONSTANT("SUBPIXEL_BITS", "i", GL_SUBPIXEL_BITS), + GB_CONSTANT("AUX_BUFFERS", "i", GL_AUX_BUFFERS), + GB_CONSTANT("READ_BUFFER", "i", GL_READ_BUFFER), + GB_CONSTANT("DRAW_BUFFER", "i", GL_DRAW_BUFFER), + GB_CONSTANT("DOUBLEBUFFER", "i", GL_DOUBLEBUFFER), + GB_CONSTANT("STEREO", "i", GL_STEREO), + GB_CONSTANT("BITMAP_", "i", GL_BITMAP), + GB_CONSTANT("COLOR", "i", GL_COLOR), + GB_CONSTANT("DEPTH", "i", GL_DEPTH), + GB_CONSTANT("STENCIL", "i", GL_STENCIL), + GB_CONSTANT("DITHER", "i", GL_DITHER), + GB_CONSTANT("RGB", "i", GL_RGB), + GB_CONSTANT("RGBA", "i", GL_RGBA), + GB_CONSTANT("BGR", "i", GL_BGR), + GB_CONSTANT("BGRA", "i", GL_BGRA), + + /* Implementation limits */ + GB_CONSTANT("MAX_LIST_NESTING", "i", GL_MAX_LIST_NESTING), + GB_CONSTANT("MAX_ATTRIB_STACK_DEPTH", "i", GL_MAX_ATTRIB_STACK_DEPTH), + GB_CONSTANT("MAX_NAME_STACK_DEPTH", "i", GL_MAX_NAME_STACK_DEPTH), + GB_CONSTANT("MAX_PROJECTION_STACK_DEPTH", "i", GL_MAX_PROJECTION_STACK_DEPTH), + GB_CONSTANT("MAX_TEXTURE_STACK_DEPTH", "i", GL_MAX_TEXTURE_STACK_DEPTH), + GB_CONSTANT("MAX_EVAL_ORDER", "i", GL_MAX_EVAL_ORDER), + GB_CONSTANT("MAX_LIGHTS", "i", GL_MAX_LIGHTS), + GB_CONSTANT("MAX_CLIP_PLANES", "i", GL_MAX_CLIP_PLANES), + GB_CONSTANT("MAX_TEXTURE_SIZE", "i", GL_MAX_TEXTURE_SIZE), + GB_CONSTANT("MAX_PIXEL_MAP_TABLE", "i", GL_MAX_PIXEL_MAP_TABLE), + GB_CONSTANT("MAX_VIEWPORT_DIMS", "i", GL_MAX_VIEWPORT_DIMS), + GB_CONSTANT("MAX_CLIENT_ATTRIB_STACK_DEPTH", "i", GL_MAX_CLIENT_ATTRIB_STACK_DEPTH), + GB_CONSTANT("MAX_MODELVIEW_STACK_DEPTH", "i", GL_MAX_MODELVIEW_STACK_DEPTH), + + /* Gets */ + GB_CONSTANT("ATTRIB_STACK_DEPTH", "i", GL_ATTRIB_STACK_DEPTH), + GB_CONSTANT("CLIENT_ATTRIB_STACK_DEPTH", "i", GL_CLIENT_ATTRIB_STACK_DEPTH), + GB_CONSTANT("COLOR_CLEAR_VALUE", "i", GL_COLOR_CLEAR_VALUE), + GB_CONSTANT("COLOR_WRITEMASK", "i", GL_COLOR_WRITEMASK), + GB_CONSTANT("CURRENT_INDEX", "i", GL_CURRENT_INDEX), + GB_CONSTANT("CURRENT_COLOR", "i", GL_CURRENT_COLOR), + GB_CONSTANT("CURRENT_NORMAL", "i", GL_CURRENT_NORMAL), + GB_CONSTANT("CURRENT_RASTER_COLOR", "i", GL_CURRENT_RASTER_COLOR), + GB_CONSTANT("CURRENT_RASTER_DISTANCE", "i", GL_CURRENT_RASTER_DISTANCE), + GB_CONSTANT("CURRENT_RASTER_INDEX", "i", GL_CURRENT_RASTER_INDEX), + GB_CONSTANT("CURRENT_RASTER_POSITION", "i", GL_CURRENT_RASTER_POSITION), + GB_CONSTANT("CURRENT_RASTER_TEXTURE_COORDS", "i", GL_CURRENT_RASTER_TEXTURE_COORDS), + GB_CONSTANT("CURRENT_RASTER_POSITION_VALID", "i", GL_CURRENT_RASTER_POSITION_VALID), + GB_CONSTANT("CURRENT_TEXTURE_COORDS", "i", GL_CURRENT_TEXTURE_COORDS), + GB_CONSTANT("INDEX_CLEAR_VALUE", "i", GL_INDEX_CLEAR_VALUE), + GB_CONSTANT("INDEX_MODE", "i", GL_INDEX_MODE), + GB_CONSTANT("INDEX_WRITEMASK", "i", GL_INDEX_WRITEMASK), + GB_CONSTANT("MODELVIEW_MATRIX", "i", GL_MODELVIEW_MATRIX), + GB_CONSTANT("MODELVIEW_STACK_DEPTH", "i", GL_MODELVIEW_STACK_DEPTH), + GB_CONSTANT("NAME_STACK_DEPTH", "i", GL_NAME_STACK_DEPTH), + GB_CONSTANT("PROJECTION_MATRIX", "i", GL_PROJECTION_MATRIX), + GB_CONSTANT("PROJECTION_STACK_DEPTH", "i", GL_PROJECTION_STACK_DEPTH), + GB_CONSTANT("RENDER_MODE", "i", GL_RENDER_MODE), + GB_CONSTANT("RGBA_MODE", "i", GL_RGBA_MODE), + GB_CONSTANT("TEXTURE_MATRIX", "i", GL_TEXTURE_MATRIX), + GB_CONSTANT("TEXTURE_STACK_DEPTH", "i", GL_TEXTURE_STACK_DEPTH), + GB_CONSTANT("VIEWPORT_", "i", GL_VIEWPORT), + + /* Evaluators */ + GB_CONSTANT("AUTO_NORMAL", "i", GL_AUTO_NORMAL), + GB_CONSTANT("MAP1_COLOR_4","i", GL_MAP1_COLOR_4), + GB_CONSTANT("MAP1_INDEX","i", GL_MAP1_INDEX), + GB_CONSTANT("MAP1_NORMAL","i", GL_MAP1_NORMAL), + GB_CONSTANT("MAP1_TEXTURE_COORD_1","i", GL_MAP1_TEXTURE_COORD_1), + GB_CONSTANT("MAP1_TEXTURE_COORD_2","i", GL_MAP1_TEXTURE_COORD_2), + GB_CONSTANT("MAP1_TEXTURE_COORD_3","i", GL_MAP1_TEXTURE_COORD_3), + GB_CONSTANT("MAP1_TEXTURE_COORD_4","i", GL_MAP1_TEXTURE_COORD_4), + GB_CONSTANT("MAP1_VERTEX_3","i", GL_MAP1_VERTEX_3), + GB_CONSTANT("MAP1_VERTEX_4","i", GL_MAP1_VERTEX_4), + GB_CONSTANT("MAP2_COLOR_4","i", GL_MAP2_COLOR_4), + GB_CONSTANT("MAP2_INDEX","i", GL_MAP2_INDEX), + GB_CONSTANT("MAP2_NORMAL","i", GL_MAP2_NORMAL), + GB_CONSTANT("MAP2_TEXTURE_COORD_1","i", GL_MAP2_TEXTURE_COORD_1), + GB_CONSTANT("MAP2_TEXTURE_COORD_2","i", GL_MAP2_TEXTURE_COORD_2), + GB_CONSTANT("MAP2_TEXTURE_COORD_3","i", GL_MAP2_TEXTURE_COORD_3), + GB_CONSTANT("MAP2_TEXTURE_COORD_4","i", GL_MAP2_TEXTURE_COORD_4), + GB_CONSTANT("MAP2_VERTEX_3","i", GL_MAP2_VERTEX_3), + GB_CONSTANT("MAP2_VERTEX_4","i", GL_MAP2_VERTEX_4), + GB_CONSTANT("MAP1_GRID_DOMAIN","i", GL_MAP1_GRID_DOMAIN), + GB_CONSTANT("MAP1_GRID_SEGMENTS","i", GL_MAP1_GRID_SEGMENTS), + GB_CONSTANT("MAP2_GRID_DOMAIN","i", GL_MAP2_GRID_DOMAIN), + GB_CONSTANT("MAP2_GRID_SEGMENTS","i", GL_MAP2_GRID_SEGMENTS), + GB_CONSTANT("COEFF","i", GL_COEFF), + GB_CONSTANT("ORDER","i", GL_ORDER), + GB_CONSTANT("DOMAIN","i", GL_DOMAIN), + + /* Hints */ + GB_CONSTANT("FOG_HINT", "i", GL_FOG_HINT), + GB_CONSTANT("LINE_SMOOTH_HINT", "i", GL_LINE_SMOOTH_HINT), + GB_CONSTANT("PERSPECTIVE_CORRECTION_HINT", "i", GL_PERSPECTIVE_CORRECTION_HINT), + GB_CONSTANT("POINT_SMOOTH_HINT", "i", GL_POINT_SMOOTH_HINT), + GB_CONSTANT("POLYGON_SMOOTH_HINT", "i", GL_POLYGON_SMOOTH_HINT), + GB_CONSTANT("DONT_CARE", "i", GL_DONT_CARE), + GB_CONSTANT("FASTEST", "i", GL_FASTEST), + GB_CONSTANT("NICEST", "i", GL_NICEST), + + /* Scissor box */ + GB_CONSTANT("SCISSOR_BOX", "i", GL_SCISSOR_BOX), + GB_CONSTANT("SCISSOR_TEST", "i", GL_SCISSOR_TEST), + + /* Pixel Mode / Transfer */ + GB_CONSTANT("MAP_COLOR", "i", GL_MAP_COLOR), + GB_CONSTANT("MAP_STENCIL", "i", GL_MAP_STENCIL), + GB_CONSTANT("INDEX_SHIFT", "i", GL_INDEX_SHIFT), + GB_CONSTANT("INDEX_OFFSET", "i", GL_INDEX_OFFSET), + GB_CONSTANT("RED_SCALE", "i", GL_RED_SCALE), + GB_CONSTANT("RED_BIAS", "i", GL_RED_BIAS), + GB_CONSTANT("GREEN_SCALE", "i", GL_GREEN_SCALE), + GB_CONSTANT("GREEN_BIAS", "i", GL_GREEN_BIAS), + GB_CONSTANT("BLUE_SCALE", "i", GL_BLUE_SCALE), + GB_CONSTANT("BLUE_BIAS", "i", GL_BLUE_BIAS), + GB_CONSTANT("ALPHA_SCALE", "i", GL_ALPHA_SCALE), + GB_CONSTANT("ALPHA_BIAS", "i", GL_ALPHA_BIAS), + GB_CONSTANT("DEPTH_SCALE", "i", GL_DEPTH_SCALE), + GB_CONSTANT("DEPTH_BIAS", "i", GL_DEPTH_BIAS), + GB_CONSTANT("PIXEL_MAP_S_TO_S_SIZE", "i", GL_PIXEL_MAP_S_TO_S_SIZE), + GB_CONSTANT("PIXEL_MAP_I_TO_I_SIZE", "i", GL_PIXEL_MAP_I_TO_I_SIZE), + GB_CONSTANT("PIXEL_MAP_I_TO_R_SIZE", "i", GL_PIXEL_MAP_I_TO_R_SIZE), + GB_CONSTANT("PIXEL_MAP_I_TO_G_SIZE", "i", GL_PIXEL_MAP_I_TO_G_SIZE), + GB_CONSTANT("PIXEL_MAP_I_TO_B_SIZE", "i", GL_PIXEL_MAP_I_TO_B_SIZE), + GB_CONSTANT("PIXEL_MAP_I_TO_A_SIZE", "i", GL_PIXEL_MAP_I_TO_A_SIZE), + GB_CONSTANT("PIXEL_MAP_R_TO_R_SIZE", "i", GL_PIXEL_MAP_R_TO_R_SIZE), + GB_CONSTANT("PIXEL_MAP_G_TO_G_SIZE", "i", GL_PIXEL_MAP_G_TO_G_SIZE), + GB_CONSTANT("PIXEL_MAP_B_TO_B_SIZE", "i", GL_PIXEL_MAP_B_TO_B_SIZE), + GB_CONSTANT("PIXEL_MAP_A_TO_A_SIZE", "i", GL_PIXEL_MAP_A_TO_A_SIZE), + GB_CONSTANT("PIXEL_MAP_S_TO_S", "i", GL_PIXEL_MAP_S_TO_S), + GB_CONSTANT("PIXEL_MAP_I_TO_I", "i", GL_PIXEL_MAP_I_TO_I), + GB_CONSTANT("PIXEL_MAP_I_TO_R", "i", GL_PIXEL_MAP_I_TO_R), + GB_CONSTANT("PIXEL_MAP_I_TO_G", "i", GL_PIXEL_MAP_I_TO_G), + GB_CONSTANT("PIXEL_MAP_I_TO_B", "i", GL_PIXEL_MAP_I_TO_B), + GB_CONSTANT("PIXEL_MAP_I_TO_A", "i", GL_PIXEL_MAP_I_TO_A), + GB_CONSTANT("PIXEL_MAP_R_TO_R", "i", GL_PIXEL_MAP_R_TO_R), + GB_CONSTANT("PIXEL_MAP_G_TO_G", "i", GL_PIXEL_MAP_G_TO_G), + GB_CONSTANT("PIXEL_MAP_B_TO_B", "i", GL_PIXEL_MAP_B_TO_B), + GB_CONSTANT("PIXEL_MAP_A_TO_A", "i", GL_PIXEL_MAP_A_TO_A), + GB_CONSTANT("PACK_ALIGNMENT", "i", GL_PACK_ALIGNMENT), + GB_CONSTANT("PACK_LSB_FIRST", "i", GL_PACK_LSB_FIRST), + GB_CONSTANT("PACK_ROW_LENGTH", "i", GL_PACK_ROW_LENGTH), + GB_CONSTANT("PACK_SKIP_PIXELS", "i", GL_PACK_SKIP_PIXELS), + GB_CONSTANT("PACK_SKIP_ROWS", "i", GL_PACK_SKIP_ROWS), + GB_CONSTANT("PACK_SWAP_BYTES", "i", GL_PACK_SWAP_BYTES), + GB_CONSTANT("UNPACK_ALIGNMENT", "i", GL_UNPACK_ALIGNMENT), + GB_CONSTANT("UNPACK_LSB_FIRST", "i", GL_UNPACK_LSB_FIRST), + GB_CONSTANT("UNPACK_ROW_LENGTH", "i", GL_UNPACK_ROW_LENGTH), + GB_CONSTANT("UNPACK_SKIP_PIXELS", "i", GL_UNPACK_SKIP_PIXELS), + GB_CONSTANT("UNPACK_SKIP_ROWS", "i", GL_UNPACK_SKIP_ROWS), + GB_CONSTANT("UNPACK_SWAP_BYTES", "i", GL_UNPACK_SWAP_BYTES), + GB_CONSTANT("ZOOM_X", "i", GL_ZOOM_X), + GB_CONSTANT("ZOOM_Y", "i", GL_ZOOM_Y), + + /* Texture mapping */ + GB_CONSTANT("TEXTURE_ENV", "i", GL_TEXTURE_ENV), + GB_CONSTANT("TEXTURE_ENV_MODE", "i", GL_TEXTURE_ENV_MODE), + GB_CONSTANT("TEXTURE_1D", "i", GL_TEXTURE_1D), + GB_CONSTANT("TEXTURE_2D", "i", GL_TEXTURE_2D), + GB_CONSTANT("TEXTURE_WRAP_S", "i", GL_TEXTURE_WRAP_S), + GB_CONSTANT("TEXTURE_WRAP_T", "i", GL_TEXTURE_WRAP_T), + GB_CONSTANT("TEXTURE_MAG_FILTER", "i", GL_TEXTURE_MAG_FILTER), + GB_CONSTANT("TEXTURE_MIN_FILTER", "i", GL_TEXTURE_MIN_FILTER), + GB_CONSTANT("TEXTURE_ENV_COLOR", "i", GL_TEXTURE_ENV_COLOR), + GB_CONSTANT("TEXTURE_GEN_S", "i", GL_TEXTURE_GEN_S), + GB_CONSTANT("TEXTURE_GEN_T", "i", GL_TEXTURE_GEN_T), + GB_CONSTANT("TEXTURE_GEN_MODE", "i", GL_TEXTURE_GEN_MODE), + GB_CONSTANT("TEXTURE_BORDER_COLOR", "i", GL_TEXTURE_BORDER_COLOR), + GB_CONSTANT("TEXTURE_WIDTH", "i", GL_TEXTURE_WIDTH), + GB_CONSTANT("TEXTURE_HEIGHT", "i", GL_TEXTURE_HEIGHT), + GB_CONSTANT("TEXTURE_BORDER", "i", GL_TEXTURE_BORDER), + GB_CONSTANT("TEXTURE_COMPONENTS", "i", GL_TEXTURE_COMPONENTS), + GB_CONSTANT("TEXTURE_RED_SIZE", "i", GL_TEXTURE_RED_SIZE), + GB_CONSTANT("TEXTURE_GREEN_SIZE", "i", GL_TEXTURE_GREEN_SIZE), + GB_CONSTANT("TEXTURE_BLUE_SIZE", "i", GL_TEXTURE_BLUE_SIZE), + GB_CONSTANT("TEXTURE_ALPHA_SIZE", "i", GL_TEXTURE_ALPHA_SIZE), + GB_CONSTANT("TEXTURE_LUMINANCE_SIZE", "i", GL_TEXTURE_LUMINANCE_SIZE), + GB_CONSTANT("TEXTURE_INTENSITY_SIZE", "i", GL_TEXTURE_INTENSITY_SIZE), + GB_CONSTANT("NEAREST_MIPMAP_NEAREST", "i", GL_NEAREST_MIPMAP_NEAREST), + GB_CONSTANT("NEAREST_MIPMAP_LINEAR", "i", GL_NEAREST_MIPMAP_LINEAR), + GB_CONSTANT("LINEAR_MIPMAP_NEAREST", "i", GL_LINEAR_MIPMAP_NEAREST), + GB_CONSTANT("LINEAR_MIPMAP_LINEAR", "i", GL_LINEAR_MIPMAP_LINEAR), + GB_CONSTANT("OBJECT_LINEAR", "i", GL_OBJECT_LINEAR), + GB_CONSTANT("OBJECT_PLANE", "i", GL_OBJECT_PLANE), + GB_CONSTANT("EYE_LINEAR", "i", GL_EYE_LINEAR), + GB_CONSTANT("EYE_PLANE", "i", GL_EYE_PLANE), + GB_CONSTANT("SPHERE_MAP", "i", GL_SPHERE_MAP), + GB_CONSTANT("DECAL", "i", GL_DECAL), + GB_CONSTANT("MODULATE", "i", GL_MODULATE), + GB_CONSTANT("NEAREST", "i", GL_NEAREST), + GB_CONSTANT("REPEAT", "i", GL_REPEAT), + GB_CONSTANT("CLAMP", "i", GL_CLAMP), + GB_CONSTANT("S", "i", GL_S), + GB_CONSTANT("T", "i", GL_T), + GB_CONSTANT("R", "i", GL_R), + GB_CONSTANT("Q", "i", GL_Q), + GB_CONSTANT("TEXTURE_GEN_R", "i", GL_TEXTURE_GEN_R), + GB_CONSTANT("TEXTURE_GEN_Q", "i", GL_TEXTURE_GEN_Q), + GB_CONSTANT("GENERATE_MIPMAP", "i", GL_GENERATE_MIPMAP), + GB_CONSTANT("TEXTURE_MAX_LEVEL", "i", GL_TEXTURE_MAX_LEVEL), + GB_CONSTANT("COMPRESSED_RGBA", "i", GL_COMPRESSED_RGBA), + + /* Multitexture Mapping ARB*/ + GB_CONSTANT("TEXTURE0_ARB", "i", GL_TEXTURE0_ARB), + GB_CONSTANT("TEXTURE1_ARB", "i", GL_TEXTURE1_ARB), + GB_CONSTANT("TEXTURE2_ARB", "i", GL_TEXTURE2_ARB), + GB_CONSTANT("TEXTURE3_ARB", "i", GL_TEXTURE3_ARB), + GB_CONSTANT("TEXTURE4_ARB", "i", GL_TEXTURE4_ARB), + + GB_CONSTANT("TEXTURE0", "i", GL_TEXTURE0), + GB_CONSTANT("TEXTURE1", "i", GL_TEXTURE1), + GB_CONSTANT("TEXTURE2", "i", GL_TEXTURE2), + GB_CONSTANT("TEXTURE3", "i", GL_TEXTURE3), + GB_CONSTANT("TEXTURE4", "i", GL_TEXTURE4), + GB_CONSTANT("COMBINE", "i", GL_COMBINE), + GB_CONSTANT("COMBINE_RGB", "i", GL_COMBINE_RGB), + GB_CONSTANT("COMBINE_ALPHA", "i", GL_COMBINE_ALPHA), + GB_CONSTANT("COMBINE_EXT", "i", GL_COMBINE_EXT), + GB_CONSTANT("COMBINE_RGB_EXT", "i", GL_COMBINE_RGB_EXT), + GB_CONSTANT("COMBINE_ALPHA_EXT", "i", GL_COMBINE_ALPHA_EXT), + GB_CONSTANT("COMBINE_ARB", "i", GL_COMBINE), + GB_CONSTANT("COMBINE_RGB_ARB", "i", GL_COMBINE_RGB_ARB), + GB_CONSTANT("COMBINE_ALPHA_ARB", "i", GL_COMBINE_ALPHA_ARB), + GB_CONSTANT("SOURCE0_RGB_ARB", "i", GL_SOURCE0_RGB_ARB), + GB_CONSTANT("SOURCE1_RGB_ARB", "i", GL_SOURCE1_RGB_ARB), + GB_CONSTANT("SOURCE2_RGB_ARB", "i", GL_SOURCE2_RGB_ARB), + GB_CONSTANT("SOURCE0_ALPHA_ARB", "i", GL_SOURCE0_ALPHA_ARB), + GB_CONSTANT("SOURCE1_ALPHA_ARB", "i", GL_SOURCE1_ALPHA_ARB), + GB_CONSTANT("SOURCE2_ALPHA_ARB", "i", GL_SOURCE2_ALPHA_ARB), + GB_CONSTANT("OPERAND0_RGB_ARB", "i", GL_OPERAND0_RGB_ARB), + GB_CONSTANT("OPERAND1_RGB_ARB", "i", GL_OPERAND1_RGB_ARB), + GB_CONSTANT("OPERAND2_RGB_ARB", "i", GL_OPERAND2_RGB_ARB), + GB_CONSTANT("OPERAND0_ALPHA_ARB", "i", GL_OPERAND0_ALPHA_ARB), + GB_CONSTANT("OPERAND1_ALPHA_ARB", "i", GL_OPERAND1_ALPHA_ARB), + GB_CONSTANT("OPERAND2_ALPHA_ARB", "i", GL_OPERAND2_ALPHA_ARB), + GB_CONSTANT("RGB_SCALE_ARB", "i", GL_RGB_SCALE_ARB), + GB_CONSTANT("ADD_SIGNED_ARB", "i", GL_ADD_SIGNED_ARB), + GB_CONSTANT("INTERPOLATE_ARB", "i", GL_INTERPOLATE_ARB), + GB_CONSTANT("SUBTRACT_ARB", "i", GL_SUBTRACT_ARB), + GB_CONSTANT("CONSTANT_ARB", "i", GL_CONSTANT_ARB), + GB_CONSTANT("PRIMARY_COLOR_ARB", "i", GL_PRIMARY_COLOR_ARB), + GB_CONSTANT("PREVIOUS_ARB", "i", GL_PREVIOUS_ARB), + GB_CONSTANT("COMPRESSED_RGBA_S3TC_DXT1_EXT", "i", GL_COMPRESSED_RGBA_S3TC_DXT1_EXT), + GB_CONSTANT("COMPRESSED_RGBA_S3TC_DXT3_EXT", "i", GL_COMPRESSED_RGBA_S3TC_DXT3_EXT), + GB_CONSTANT("COMPRESSED_RGBA_S3TC_DXT5_EXT", "i", GL_COMPRESSED_RGBA_S3TC_DXT5_EXT), + + /* 38; GL_ARB_texture_rectangle */ + GB_CONSTANT("TEXTURE_RECTANGLE_ARB", "i", GL_TEXTURE_RECTANGLE_ARB), + GB_CONSTANT("TEXTURE_BINDING_RECTANGLE_ARB", "i", GL_TEXTURE_BINDING_RECTANGLE_ARB), + GB_CONSTANT("PROXY_TEXTURE_RECTANGLE_ARB", "i", GL_PROXY_TEXTURE_RECTANGLE_ARB), + GB_CONSTANT("MAX_RECTANGLE_TEXTURE_SIZE_ARB", "i", GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB), + GB_CONSTANT("SAMPLER_2D_RECT_ARB", "i", GL_SAMPLER_2D_RECT_ARB), + GB_CONSTANT("SAMPLER_2D_RECT_SHADOW_ARB", "i", GL_SAMPLER_2D_RECT_SHADOW_ARB), + + /* 310; GL_EXT_framebuffer_object */ + GB_CONSTANT("ELEMENT_ARRAY_BUFFER", "i", GL_ELEMENT_ARRAY_BUFFER), + GB_CONSTANT("FRAMEBUFFER_EXT", "i", GL_FRAMEBUFFER_EXT), + GB_CONSTANT("RENDERBUFFER_EXT", "i", GL_RENDERBUFFER_EXT), + GB_CONSTANT("STENCIL_INDEX1_EXT", "i", GL_STENCIL_INDEX1_EXT), + GB_CONSTANT("STENCIL_INDEX4_EXT", "i", GL_STENCIL_INDEX4_EXT), + GB_CONSTANT("STENCIL_INDEX8_EXT", "i", GL_STENCIL_INDEX8_EXT), + GB_CONSTANT("STENCIL_INDEX16_EXT", "i", GL_STENCIL_INDEX16_EXT), + GB_CONSTANT("RENDERBUFFER_WIDTH_EXT", "i", GL_RENDERBUFFER_WIDTH_EXT), + GB_CONSTANT("RENDERBUFFER_HEIGHT_EXT", "i", GL_RENDERBUFFER_HEIGHT_EXT), + GB_CONSTANT("RENDERBUFFER_INTERNAL_FORMAT_EXT", "i", GL_RENDERBUFFER_INTERNAL_FORMAT_EXT), + GB_CONSTANT("RENDERBUFFER_RED_SIZE_EXT", "i", GL_RENDERBUFFER_RED_SIZE_EXT), + GB_CONSTANT("RENDERBUFFER_GREEN_SIZE_EXT", "i", GL_RENDERBUFFER_GREEN_SIZE_EXT), + GB_CONSTANT("RENDERBUFFER_BLUE_SIZE_EXT", "i", GL_RENDERBUFFER_BLUE_SIZE_EXT), + GB_CONSTANT("RENDERBUFFER_ALPHA_SIZE_EXT", "i", GL_RENDERBUFFER_ALPHA_SIZE_EXT), + GB_CONSTANT("RENDERBUFFER_DEPTH_SIZE_EXT", "i", GL_RENDERBUFFER_DEPTH_SIZE_EXT), + GB_CONSTANT("RENDERBUFFER_STENCIL_SIZE_EXT", "i", GL_RENDERBUFFER_STENCIL_SIZE_EXT), + GB_CONSTANT("FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT", "i", GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT), + GB_CONSTANT("FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT", "i", GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT), + GB_CONSTANT("FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT", "i", GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT), + GB_CONSTANT("FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT", "i", GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT), + GB_CONSTANT("FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT", "i", GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT), + GB_CONSTANT("COLOR_ATTACHMENT0_EXT", "i", GL_COLOR_ATTACHMENT0_EXT), + GB_CONSTANT("COLOR_ATTACHMENT1_EXT", "i", GL_COLOR_ATTACHMENT1_EXT), + GB_CONSTANT("COLOR_ATTACHMENT2_EXT", "i", GL_COLOR_ATTACHMENT2_EXT), + GB_CONSTANT("COLOR_ATTACHMENT3_EXT", "i", GL_COLOR_ATTACHMENT3_EXT), + GB_CONSTANT("COLOR_ATTACHMENT4_EXT", "i", GL_COLOR_ATTACHMENT4_EXT), + GB_CONSTANT("COLOR_ATTACHMENT5_EXT", "i", GL_COLOR_ATTACHMENT5_EXT), + GB_CONSTANT("COLOR_ATTACHMENT6_EXT", "i", GL_COLOR_ATTACHMENT6_EXT), + GB_CONSTANT("COLOR_ATTACHMENT7_EXT", "i", GL_COLOR_ATTACHMENT7_EXT), + GB_CONSTANT("COLOR_ATTACHMENT8_EXT", "i", GL_COLOR_ATTACHMENT8_EXT), + GB_CONSTANT("COLOR_ATTACHMENT9_EXT", "i", GL_COLOR_ATTACHMENT9_EXT), + GB_CONSTANT("COLOR_ATTACHMENT10_EXT", "i", GL_COLOR_ATTACHMENT10_EXT), + GB_CONSTANT("COLOR_ATTACHMENT11_EXT", "i", GL_COLOR_ATTACHMENT11_EXT), + GB_CONSTANT("COLOR_ATTACHMENT12_EXT", "i", GL_COLOR_ATTACHMENT12_EXT), + GB_CONSTANT("COLOR_ATTACHMENT13_EXT", "i", GL_COLOR_ATTACHMENT13_EXT), + GB_CONSTANT("COLOR_ATTACHMENT14_EXT", "i", GL_COLOR_ATTACHMENT14_EXT), + GB_CONSTANT("COLOR_ATTACHMENT15_EXT", "i", GL_COLOR_ATTACHMENT15_EXT), + GB_CONSTANT("DEPTH_ATTACHMENT_EXT", "i", GL_DEPTH_ATTACHMENT_EXT), + GB_CONSTANT("STENCIL_ATTACHMENT_EXT", "i", GL_STENCIL_ATTACHMENT_EXT), + GB_CONSTANT("FRAMEBUFFER_COMPLETE_EXT", "i", GL_FRAMEBUFFER_COMPLETE_EXT), + GB_CONSTANT("FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT", "i", GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT), + GB_CONSTANT("FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT", "i", GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT), + GB_CONSTANT("FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT", "i", GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT), + GB_CONSTANT("FRAMEBUFFER_INCOMPLETE_FORMATS_EXT", "i", GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT), + GB_CONSTANT("FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT", "i", GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT), + GB_CONSTANT("FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT", "i", GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT), + GB_CONSTANT("FRAMEBUFFER_UNSUPPORTED_EXT", "i", GL_FRAMEBUFFER_UNSUPPORTED_EXT), + GB_CONSTANT("FRAMEBUFFER_BINDING_EXT", "i", GL_FRAMEBUFFER_BINDING_EXT), + GB_CONSTANT("RENDERBUFFER_BINDING_EXT", "i", GL_RENDERBUFFER_BINDING_EXT), + GB_CONSTANT("MAX_COLOR_ATTACHMENTS_EXT", "i", GL_MAX_COLOR_ATTACHMENTS_EXT), + GB_CONSTANT("MAX_RENDERBUFFER_SIZE_EXT", "i", GL_MAX_RENDERBUFFER_SIZE_EXT), + GB_CONSTANT("INVALID_FRAMEBUFFER_OPERATION_EXT", "i", GL_INVALID_FRAMEBUFFER_OPERATION_EXT), + GB_CONSTANT("SAMPLES_PASSED", "i", GL_SAMPLES_PASSED), + GB_CONSTANT("QUERY_RESULT", "i", GL_QUERY_RESULT), + GB_CONSTANT("TRUE", "i", GL_TRUE), + GB_CONSTANT("FALSE", "i", GL_FALSE), + GB_CONSTANT("PRIMITIVES_GENERATED", "i", GL_PRIMITIVES_GENERATED), + GB_CONSTANT("TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN", "i", GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN), + //GB_CONSTANT("TIME_ELAPSED", "i", GL_TIME_ELAPSED), + //GB_CONSTANT("TIMESTAMP", "i", ​ GL_TIMESTAMP​), + GB_CONSTANT("CURRENT_QUERY", "i", GL_CURRENT_QUERY), + GB_CONSTANT("QUERY_COUNTER_BITS", "i", GL_QUERY_COUNTER_BITS), + + /* GL Array */ + GB_CONSTANT("ARRAY_BUFFER", "i", GL_ARRAY_BUFFER), + GB_CONSTANT("STATIC_DRAW", "i", GL_STATIC_DRAW), + + + + + + +#if 0 + +/* OpenGL 1.1 */ + + GB_CONSTANT("ProxyTexture1d", "i", GL_PROXY_TEXTURE_1D), + GB_CONSTANT("ProxyTexture2d", "i", GL_PROXY_TEXTURE_2D), + GB_CONSTANT("TexturePriority", "i", GL_TEXTURE_PRIORITY), + GB_CONSTANT("TextureResident", "i", GL_TEXTURE_RESIDENT), + GB_CONSTANT("TextureBinding1d", "i", GL_TEXTURE_BINDING_1D), + GB_CONSTANT("TextureBinding2d", "i", GL_TEXTURE_BINDING_2D), + GB_CONSTANT("TextureInternalFormat", "i", GL_TEXTURE_INTERNAL_FORMAT), + GB_CONSTANT("Alpha4", "i", GL_ALPHA4), + GB_CONSTANT("Alpha8", "i", GL_ALPHA8), + GB_CONSTANT("Alpha12", "i", GL_ALPHA12), + GB_CONSTANT("Alpha16", "i", GL_ALPHA16), + GB_CONSTANT("Luminance4", "i", GL_LUMINANCE4), + GB_CONSTANT("Luminance8", "i", GL_LUMINANCE8), + GB_CONSTANT("Luminance12", "i", GL_LUMINANCE12), + GB_CONSTANT("Luminance16", "i", GL_LUMINANCE16), + GB_CONSTANT("Luminance4Alpha4", "i", GL_LUMINANCE4_ALPHA4), + GB_CONSTANT("Luminance6Alpha2", "i", GL_LUMINANCE6_ALPHA2), + GB_CONSTANT("Luminance8Alpha8", "i", GL_LUMINANCE8_ALPHA8), + GB_CONSTANT("Luminance12Alpha4", "i", GL_LUMINANCE12_ALPHA4), + GB_CONSTANT("Luminance12Alpha12", "i", GL_LUMINANCE12_ALPHA12), + GB_CONSTANT("Luminance16Alpha16", "i", GL_LUMINANCE16_ALPHA16), + GB_CONSTANT("Intensity", "i", GL_INTENSITY), + GB_CONSTANT("Intensity4", "i", GL_INTENSITY4), + GB_CONSTANT("Intensity8", "i", GL_INTENSITY8), + GB_CONSTANT("Intensity12", "i", GL_INTENSITY12), + GB_CONSTANT("Intensity16", "i", GL_INTENSITY16), + GB_CONSTANT("R3G3B2", "i", GL_R3_G3_B2), + GB_CONSTANT("Rgb4", "i", GL_RGB4), + GB_CONSTANT("Rgb5", "i", GL_RGB5), + GB_CONSTANT("Rgb8", "i", GL_RGB8), + GB_CONSTANT("Rgb10", "i", GL_RGB10), + GB_CONSTANT("Rgb12", "i", GL_RGB12), + GB_CONSTANT("Rgb16", "i", GL_RGB16), + GB_CONSTANT("Rgba2", "i", GL_RGBA2), + GB_CONSTANT("Rgba4", "i", GL_RGBA4), + GB_CONSTANT("Rgb5A1", "i", GL_RGB5_A1), + GB_CONSTANT("Rgba8", "i", GL_RGBA8), + GB_CONSTANT("Rgb10A2", "i", GL_RGB10_A2), + GB_CONSTANT("Rgba12", "i", GL_RGBA12), + GB_CONSTANT("Rgba16", "i", GL_RGBA16), + GB_CONSTANT("ClientPixelStoreBit", "i", GL_CLIENT_PIXEL_STORE_BIT), + GB_CONSTANT("ClientVertexArrayBit", "i", GL_CLIENT_VERTEX_ARRAY_BIT), + GB_CONSTANT("AllClientAttribBits", "i", GL_ALL_CLIENT_ATTRIB_BITS), + GB_CONSTANT("ClientAllAttribBits", "i", GL_CLIENT_ALL_ATTRIB_BITS), + +/* texture_border_clamp */ + + GB_CONSTANT("ClampToBorder", "i", GL_CLAMP_TO_BORDER), +#endif + + GB_END_DECLARE +}; diff --git a/gb.opengl/src/GL.h b/gb.opengl/src/GL.h new file mode 100644 index 00000000..4a343bd3 --- /dev/null +++ b/gb.opengl/src/GL.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + GL.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GL_H +#define __GL_H + +#include "main.h" + +#include + +#ifndef __GL_C +extern GB_DESC Cgl[]; +#endif /* __GL_C */ + +#endif /* __GL_H */ diff --git a/gb.opengl/src/GLclipping.c b/gb.opengl/src/GLclipping.c new file mode 100644 index 00000000..05fe9a96 --- /dev/null +++ b/gb.opengl/src/GLclipping.c @@ -0,0 +1,58 @@ +/*************************************************************************** + + GLclipping.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLCLIPPING_C + +#include "GL.h" + +BEGIN_METHOD(GLCLIPPLANE, GB_INTEGER plane; GB_OBJECT equation) + + GLdouble params[4]; + GB_ARRAY fArray = (GB_ARRAY) VARG(equation); + int count = GB.Array.Count(fArray); + uint i; + + count = (count > 4 ? 4 : count); + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLCLIPPING_H +#define __GLCLIPPING_H + +#include "main.h" + +DECLARE_METHOD(GLCLIPPLANE); +DECLARE_METHOD(GLGETCLIPPLANE); + +#endif /* __GLCLIPPING_H */ diff --git a/gb.opengl/src/GLcolorLighting.c b/gb.opengl/src/GLcolorLighting.c new file mode 100644 index 00000000..a7d6d717 --- /dev/null +++ b/gb.opengl/src/GLcolorLighting.c @@ -0,0 +1,421 @@ +/*************************************************************************** + + GLcolorLighting.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLCOLORLIGHTING_C + +#include "GL.h" + +BEGIN_METHOD(GLCOLOR3F, GB_FLOAT red; GB_FLOAT green; GB_FLOAT blue) + + glColor3d(VARG(red), VARG(green), VARG(blue)); + +END_METHOD + +BEGIN_METHOD(GLCOLORF, GB_FLOAT red; GB_FLOAT green; GB_FLOAT blue; GB_FLOAT alpha) + + if (MISSING(alpha)) + glColor3d(VARG(red), VARG(green), VARG(blue)); + else + glColor4d(VARG(red), VARG(green), VARG(blue), VARG(alpha)); + +END_METHOD + +BEGIN_METHOD(GLCOLOR3I, GB_INTEGER red; GB_INTEGER green; GB_INTEGER blue) + + glColor3i(VARG(red), VARG(green), VARG(blue)); + +END_METHOD + +BEGIN_METHOD(GLCOLORI, GB_INTEGER red; GB_INTEGER green; GB_INTEGER blue; GB_INTEGER alpha) + + if (MISSING(alpha)) + glColor3i(VARG(red), VARG(green), VARG(blue)); + else + glColor4i(VARG(red), VARG(green), VARG(blue), VARG(alpha)); + +END_METHOD + +BEGIN_METHOD(GLCOLORFV, GB_OBJECT array) + + GLdouble r,g,b,a; + GB_ARRAY color = (GB_ARRAY) VARG(array); + int count = GB.Array.Count(color); + + if (count<3) + return; + + r = *((GLdouble *)GB.Array.Get(color,0)); + g = *((GLdouble *)GB.Array.Get(color,1)); + b = *((GLdouble *)GB.Array.Get(color,2)); + + if (count==3) + glColor3d(r, g, b); + else + { + a = *((GLdouble *)GB.Array.Get(color,3)); + glColor4d(r, g, b, a); + } + +END_METHOD + +BEGIN_METHOD(GLCOLORIV, GB_OBJECT array) + + GLint r,g,b,a; + GB_ARRAY color = (GB_ARRAY) VARG(array); + int count = GB.Array.Count(color); + + if (count<3) + return; + + r = *((GLint *)GB.Array.Get(color,0)); + g = *((GLint *)GB.Array.Get(color,1)); + b = *((GLint *)GB.Array.Get(color,2)); + + if (count==3) + glColor3i(r, g, b); + else + { + a = *((GLint *)GB.Array.Get(color,3)); + glColor4i(r, g, b, a); + } + +END_METHOD + +BEGIN_METHOD(GLCOLORMATERIAL, GB_INTEGER face; GB_INTEGER mode) + + glColorMaterial(VARG(face), VARG(mode)); + +END_METHOD + +BEGIN_METHOD(GLFRONTFACE, GB_INTEGER mode) + + glFrontFace(VARG(mode)); + +END_METHOD + +BEGIN_METHOD(GLGETLIGHTFV, GB_INTEGER light; GB_INTEGER pname) + + GLfloat params[4]; + GB_ARRAY fArray; + int i, count=1; + + switch(VARG(pname)) + { + case GL_AMBIENT : + case GL_DIFFUSE : + case GL_SPECULAR : + case GL_POSITION : + count = 4; + break; + case GL_SPOT_DIRECTION : + count = 3; + break; + } + + GB.Array.New(&fArray , GB_T_FLOAT , count); + glGetLightfv(VARG(light), VARG(pname), params); + + for (i=0; i 4 ? 4 : count); + + for (i=0; i 4 ? 4 : count); + + for (i=0; i 4 ? 4 : count); + + for (i=0; i 4 ? 4 : count); + + for (i=0; i 4 ? 4 : count); + + for (i=0; i 4 ? 4 : count); + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLCOLORLIGHTING_H +#define __GLCOLORLIGHTING_H + +#include "main.h" + +DECLARE_METHOD(GLCOLOR3F); +DECLARE_METHOD(GLCOLORF); +DECLARE_METHOD(GLCOLOR3I); +DECLARE_METHOD(GLCOLORI); +DECLARE_METHOD(GLCOLORFV); +DECLARE_METHOD(GLCOLORIV); +DECLARE_METHOD(GLCOLORMATERIAL); +DECLARE_METHOD(GLFRONTFACE); +DECLARE_METHOD(GLGETLIGHTFV); +DECLARE_METHOD(GLGETLIGHTIV); +DECLARE_METHOD(GLGETMATERIALFV); +DECLARE_METHOD(GLGETMATERIALIV); +DECLARE_METHOD(GLINDEXF); +DECLARE_METHOD(GLINDEXI); +DECLARE_METHOD(GLLIGHTF); +DECLARE_METHOD(GLLIGHTI); +DECLARE_METHOD(GLLIGHTFV); +DECLARE_METHOD(GLLIGHTIV); +DECLARE_METHOD(GLLIGHTMODELF); +DECLARE_METHOD(GLLIGHTMODELI); +DECLARE_METHOD(GLLIGHTMODELFV); +DECLARE_METHOD(GLLIGHTMODELIV); +DECLARE_METHOD(GLMATERIALF); +DECLARE_METHOD(GLMATERIALI); +DECLARE_METHOD(GLNORMAL3FV); +DECLARE_METHOD(GLNORMAL3IV); +DECLARE_METHOD(GLMATERIALFV); +DECLARE_METHOD(GLMATERIALIV); +DECLARE_METHOD(GLNORMAL3F); +DECLARE_METHOD(GLNORMAL3I); +DECLARE_METHOD(GLSHADEMODEL); + +#endif /* __GLCOLORLIGHTING_H */ diff --git a/gb.opengl/src/GLcoordTransf.c b/gb.opengl/src/GLcoordTransf.c new file mode 100644 index 00000000..eeeb7595 --- /dev/null +++ b/gb.opengl/src/GLcoordTransf.c @@ -0,0 +1,122 @@ +/*************************************************************************** + + GLcoordTransf.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLCOORDTRANSF_C + +#include "GL.h" + +BEGIN_METHOD(GLDEPTHRANGE, GB_FLOAT near; GB_FLOAT far) + + glDepthRange(VARG(near),VARG(far)); + +END_METHOD + +BEGIN_METHOD(GLFRUSTUM, GB_FLOAT left; GB_FLOAT right; GB_FLOAT bottom; GB_FLOAT top; GB_FLOAT near; GB_FLOAT far) + + glFrustum(VARG(left),VARG(right),VARG(bottom),VARG(top),VARG(near),VARG(far)); + +END_METHOD + +BEGIN_METHOD_VOID(GLLOADIDENTITY) + + glLoadIdentity(); + +END_METHOD + +BEGIN_METHOD(GLLOADMATRIXF, GB_OBJECT array) + + GLdouble params[16]; + GB_ARRAY matrix = (GB_ARRAY) VARG(array); + int i, count = GB.Array.Count(matrix); + + count = (count > 16 ? 16 : count); + + for (i=0; i 16 ? 16 : count); + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLCOORDTRANSF_H +#define __GLCOORDTRANSF_H + +#include "main.h" + +DECLARE_METHOD(GLDEPTHRANGE); +DECLARE_METHOD(GLFRUSTUM); +DECLARE_METHOD(GLLOADIDENTITY); +DECLARE_METHOD(GLLOADMATRIXF); +DECLARE_METHOD(GLMATRIXMODE); +DECLARE_METHOD(GLMULTMATRIXF); +DECLARE_METHOD(GLORTHO); +DECLARE_METHOD(GLPOPMATRIX); +DECLARE_METHOD(GLPUSHMATRIX); +DECLARE_METHOD(GLROTATEF); +DECLARE_METHOD(GLSCALEF); +DECLARE_METHOD(GLTRANSLATEF); +DECLARE_METHOD(GLVIEWPORT); + +#endif /* __GLCOORDTRANSF_H */ diff --git a/gb.opengl/src/GLdisplayList.c b/gb.opengl/src/GLdisplayList.c new file mode 100644 index 00000000..45e6e540 --- /dev/null +++ b/gb.opengl/src/GLdisplayList.c @@ -0,0 +1,81 @@ +/*************************************************************************** + + GLdisplayList.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLDISPLAYLIST_C + +#include "GL.h" + +BEGIN_METHOD(GLCALLLIST, GB_INTEGER index) + + glCallList(VARG(index)); + +END_METHOD + +BEGIN_METHOD(GLCALLLISTS, GB_OBJECT lists) + + GB_ARRAY iArray = (GB_ARRAY) VARG(lists); + int i,count = GB.Array.Count(iArray); + + if (!count) + return; + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLDISPLAYLIST_H +#define __GLDISPLAYLIST_H + +#include "main.h" + +DECLARE_METHOD(GLCALLLIST); +DECLARE_METHOD(GLCALLLISTS); +DECLARE_METHOD(GLDELETELISTS); +DECLARE_METHOD(GLENDLIST); +DECLARE_METHOD(GLGENLISTS); +DECLARE_METHOD(GLISLIST); +DECLARE_METHOD(GLLISTBASE); +DECLARE_METHOD(GLNEWLIST); + +#endif /* __GLDISPLAYLIST_H */ diff --git a/gb.opengl/src/GLeval.c b/gb.opengl/src/GLeval.c new file mode 100644 index 00000000..42388fbf --- /dev/null +++ b/gb.opengl/src/GLeval.c @@ -0,0 +1,115 @@ +/*************************************************************************** + + GLeval.c + + (c) 2005-2011 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLEVAL_C + +#include "GL.h" + +BEGIN_METHOD(GLMAP1F, GB_INTEGER target; GB_FLOAT u1; GB_FLOAT u2; GB_INTEGER stride; GB_INTEGER order; GB_OBJECT array) + + GB_ARRAY matrix = (GB_ARRAY) VARG(array); + int i, count = GB.Array.Count(matrix); + GLdouble params[count]; + + for (i=0; i0 ? *((double *)GB.Array.Get(fArray,0)) : 0; + params[1] = count>1 ? *((double *)GB.Array.Get(fArray,1)) : 0; + + glEvalCoord2dv(params); + +END_METHOD + +BEGIN_METHOD(GLMAPGRID1F, GB_INTEGER un; GB_FLOAT u1; GB_FLOAT u2) + + glMapGrid1d(VARG(un), VARG(u1), VARG(u2)); + +END_METHOD + +BEGIN_METHOD(GLMAPGRID2F, GB_INTEGER un; GB_FLOAT u1; GB_FLOAT u2; GB_INTEGER vn; GB_FLOAT v1; GB_FLOAT v2) + + glMapGrid2d(VARG(un), VARG(u1), VARG(u2), VARG(vn), VARG(v1), VARG(v2)); + +END_METHOD + +BEGIN_METHOD(GLEVALPOINT1, GB_INTEGER i) + + glEvalPoint1(VARG(i)); + +END_METHOD + +BEGIN_METHOD(GLEVALPOINT2, GB_INTEGER i; GB_INTEGER j) + + glEvalPoint2(VARG(i), VARG(j)); + +END_METHOD + +BEGIN_METHOD(GLEVALMESH1, GB_INTEGER mode; GB_INTEGER i1; GB_INTEGER i2) + + glEvalMesh1(VARG(mode), VARG(i1), VARG(i2)); + +END_METHOD + +BEGIN_METHOD(GLEVALMESH2, GB_INTEGER mode; GB_INTEGER i1; GB_INTEGER i2; GB_INTEGER j1; GB_INTEGER j2) + + glEvalMesh2(VARG(mode), VARG(i1), VARG(i2), VARG(j1), VARG(j2)); + +END_METHOD diff --git a/gb.opengl/src/GLeval.h b/gb.opengl/src/GLeval.h new file mode 100644 index 00000000..854760e3 --- /dev/null +++ b/gb.opengl/src/GLeval.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + GLeval.h + + (c) 2005-2011 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLEVAL_H +#define __GLEVAL_H + +#include "main.h" + +DECLARE_METHOD(GLMAP1F); +DECLARE_METHOD(GLMAP2F); +//DECLARE_METHOD(GLGETMAPFV); +//DECLARE_METHOD(GLGETMAPIV); +DECLARE_METHOD(GLEVALCOORD1F); +DECLARE_METHOD(GLEVALCOORD2F); +DECLARE_METHOD(GLEVALCOORD2FV); +DECLARE_METHOD(GLMAPGRID1F); +DECLARE_METHOD(GLMAPGRID2F); +DECLARE_METHOD(GLEVALPOINT1); +DECLARE_METHOD(GLEVALPOINT2); +DECLARE_METHOD(GLEVALMESH1); +DECLARE_METHOD(GLEVALMESH2); + +#endif /* __GLEVAL_H */ + + diff --git a/gb.opengl/src/GLfog.c b/gb.opengl/src/GLfog.c new file mode 100644 index 00000000..69701a07 --- /dev/null +++ b/gb.opengl/src/GLfog.c @@ -0,0 +1,68 @@ +/*************************************************************************** + + GLfog.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLFOG_C + +#include "GL.h" + +BEGIN_METHOD(GLFOGF, GB_INTEGER pname; GB_FLOAT param) + + glFogf(VARG(pname), VARG(param)); + +END_METHOD + +BEGIN_METHOD(GLFOGI, GB_INTEGER face; GB_INTEGER pname; GB_INTEGER param) + + glMateriali(VARG(face), VARG(pname), VARG(param)); + +END_METHOD + +BEGIN_METHOD(GLFOGFV, GB_INTEGER pname; GB_OBJECT params) + + GLfloat params[4]; + GB_ARRAY fArray = (GB_ARRAY) VARG(params); + uint i, count = GB.Array.Count(fArray); + + count = (count > 4 ? 4 : count); + + for (i=0; i 4 ? 4 : count); + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLFOG_H +#define __GLFOG_H + +#include "main.h" + +DECLARE_METHOD(GLFOGF); +DECLARE_METHOD(GLFOGI); +DECLARE_METHOD(GLFOGFV); +DECLARE_METHOD(GLFOGIV); + +#endif /* __GLFOG_H */ diff --git a/gb.opengl/src/GLframeBufferOps.c b/gb.opengl/src/GLframeBufferOps.c new file mode 100644 index 00000000..4ed24402 --- /dev/null +++ b/gb.opengl/src/GLframeBufferOps.c @@ -0,0 +1,142 @@ +/*************************************************************************** + + GLframeBufferOps.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLFRAMEBUFFEROPS_C + +#include "GL.h" + +/***************************************************************************/ + +BEGIN_METHOD(GLACCUM, GB_INTEGER operation; GB_FLOAT value) + + glAccum(VARG(operation), VARG(value)); + +END_METHOD + +BEGIN_METHOD(GLALPHAFUNC, GB_INTEGER function; GB_FLOAT reference) + + glAlphaFunc(VARG(function), VARG(reference)); + +END_METHOD + +BEGIN_METHOD(GLBLENDFUNC, GB_INTEGER sfactor; GB_INTEGER dfactor) + + glBlendFunc(VARG(sfactor), VARG(dfactor)); + +END_METHOD + +BEGIN_METHOD(GLCLEAR, GB_INTEGER mask) + + glClear(VARG(mask)); + +END_METHOD + +BEGIN_METHOD(GLCLEARACCUM, GB_FLOAT red; GB_FLOAT green; GB_FLOAT blue; GB_FLOAT alpha) + + glClearAccum(VARG(red), VARG(green), VARG(blue), VARG(alpha)); + +END_METHOD + +BEGIN_METHOD(GLCLEARCOLOR, GB_FLOAT red; GB_FLOAT green; GB_FLOAT blue; GB_FLOAT alpha) + + glClearColor(VARG(red), VARG(green), VARG(blue), VARG(alpha)); + +END_METHOD + +BEGIN_METHOD(GLCLEARDEPTH, GB_FLOAT depth) + + glClearDepth(VARG(depth)); + +END_METHOD + +BEGIN_METHOD(GLCLEARINDEX, GB_FLOAT value) + + glClearIndex(VARG(value)); + +END_METHOD + +BEGIN_METHOD(GLCLEARSTENCIL, GB_INTEGER value) + + glClearStencil(VARG(value)); + +END_METHOD + +BEGIN_METHOD(GLCOLORMASK, GB_BOOLEAN red; GB_BOOLEAN green; GB_BOOLEAN blue; GB_BOOLEAN alpha) + + glColorMask(VARG(red), VARG(green), VARG(blue), VARG(alpha)); + +END_METHOD + +BEGIN_METHOD(GLDEPTHFUNC, GB_INTEGER function) + + glDepthFunc(VARG(function)); + +END_METHOD + +BEGIN_METHOD(GLDEPTHMASK, GB_BOOLEAN flag) + + glDepthMask(VARG(flag)); + +END_METHOD + +BEGIN_METHOD(GLDRAWBUFFER, GB_INTEGER mode) + + glDrawBuffer(VARG(mode)); + +END_METHOD + +BEGIN_METHOD(GLINDEXMASK, GB_INTEGER mask) + + glIndexMask(VARG(mask)); + +END_METHOD + +BEGIN_METHOD(GLLOGICOP, GB_INTEGER opcode) + + glLogicOp(VARG(opcode)); + +END_METHOD + +BEGIN_METHOD(GLSCISSOR, GB_INTEGER x; GB_INTEGER y; GB_INTEGER width; GB_INTEGER height) + + glScissor(VARG(x), VARG(y), VARG(width), VARG(height)); + +END_METHOD + +BEGIN_METHOD(GLSTENCILFUNC, GB_INTEGER function; GB_INTEGER reference; GB_INTEGER mask) + + glStencilFunc(VARG(function), VARG(reference), VARG(mask)); + +END_METHOD + +BEGIN_METHOD(GLSTENCILMASK, GB_INTEGER mask) + + glStencilMask(VARG(mask)); + +END_METHOD + +BEGIN_METHOD(GLSTENCILOP, GB_INTEGER fail; GB_INTEGER zfail; GB_INTEGER zpass) + + glStencilOp(VARG(fail), VARG(zfail), VARG(zpass)); + +END_METHOD diff --git a/gb.opengl/src/GLframeBufferOps.h b/gb.opengl/src/GLframeBufferOps.h new file mode 100644 index 00000000..f6c9ef21 --- /dev/null +++ b/gb.opengl/src/GLframeBufferOps.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + GLframeBufferOps.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLFRAMEBUFFEROPS_H +#define __GLFRAMEBUFFEROPS_H + +#include "main.h" + +DECLARE_METHOD(GLACCUM); +DECLARE_METHOD(GLALPHAFUNC); +DECLARE_METHOD(GLBLENDFUNC); +DECLARE_METHOD(GLCLEAR); +DECLARE_METHOD(GLCLEARACCUM); +DECLARE_METHOD(GLCLEARCOLOR); +DECLARE_METHOD(GLCLEARDEPTH); +DECLARE_METHOD(GLCLEARINDEX); +DECLARE_METHOD(GLCLEARSTENCIL); +DECLARE_METHOD(GLCOLORMASK); +DECLARE_METHOD(GLDEPTHFUNC); +DECLARE_METHOD(GLDEPTHMASK); +DECLARE_METHOD(GLDRAWBUFFER); +DECLARE_METHOD(GLINDEXMASK); +DECLARE_METHOD(GLLOGICOP); +DECLARE_METHOD(GLSCISSOR); +DECLARE_METHOD(GLSTENCILFUNC); +DECLARE_METHOD(GLSTENCILMASK); +DECLARE_METHOD(GLSTENCILOP); + +#endif /* __GLFRAMEBUFFEROPS_H */ diff --git a/gb.opengl/src/GLinfo.c b/gb.opengl/src/GLinfo.c new file mode 100644 index 00000000..606bca67 --- /dev/null +++ b/gb.opengl/src/GLinfo.c @@ -0,0 +1,501 @@ +/*************************************************************************** + + GLinfo.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLINFO_C + +#include "GL.h" + +int checkSize(GLenum value) +{ + int retSize = 0; + + if (value == GL_COMPRESSED_TEXTURE_FORMATS) + { + GLint size; + glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &size); + return size; + } + + switch(value) + { + case GL_ACCUM_ALPHA_BITS: + case GL_ACCUM_BLUE_BITS: + case GL_ACCUM_GREEN_BITS: + case GL_ACCUM_RED_BITS: + case GL_ACTIVE_TEXTURE: + case GL_ALPHA_BIAS: + case GL_ALPHA_BITS: + case GL_ALPHA_SCALE: + case GL_ALPHA_TEST: + case GL_ALPHA_TEST_FUNC: + case GL_ALPHA_TEST_REF: + case GL_ARRAY_BUFFER_BINDING: + case GL_ATTRIB_STACK_DEPTH: + case GL_AUTO_NORMAL: + case GL_AUX_BUFFERS: + case GL_BLEND: + case GL_BLEND_DST: + case GL_BLEND_DST_ALPHA: + case GL_BLEND_DST_RGB: + case GL_BLEND_EQUATION_ALPHA: + case GL_BLEND_EQUATION_RGB: + case GL_BLEND_SRC: + case GL_BLEND_SRC_ALPHA: + case GL_BLEND_SRC_RGB: + case GL_BLUE_BIAS: + case GL_BLUE_BITS: + case GL_BLUE_SCALE: + case GL_CLIENT_ACTIVE_TEXTURE: + case GL_CLIENT_ATTRIB_STACK_DEPTH: + case GL_CLIP_PLANE0: + case GL_CLIP_PLANE1: + case GL_CLIP_PLANE2: + case GL_CLIP_PLANE3: + case GL_CLIP_PLANE4: + case GL_CLIP_PLANE5: + case GL_COLOR_ARRAY: + case GL_COLOR_ARRAY_BUFFER_BINDING: + case GL_COLOR_ARRAY_SIZE: + case GL_COLOR_ARRAY_STRIDE: + case GL_COLOR_ARRAY_TYPE: + case GL_COLOR_LOGIC_OP: + case GL_COLOR_MATERIAL: + case GL_COLOR_MATERIAL_FACE: + case GL_COLOR_MATERIAL_PARAMETER: + case GL_COLOR_MATRIX_STACK_DEPTH: + case GL_COLOR_SUM: + case GL_COLOR_TABLE: + case GL_CONVOLUTION_1D: + case GL_CONVOLUTION_2D: + case GL_CULL_FACE: + case GL_CULL_FACE_MODE: + case GL_CURRENT_FOG_COORD: + case GL_CURRENT_INDEX: + case GL_CURRENT_PROGRAM: + case GL_CURRENT_RASTER_DISTANCE: + case GL_CURRENT_RASTER_INDEX: + case GL_CURRENT_RASTER_POSITION_VALID: + case GL_DEPTH_BIAS: + case GL_DEPTH_BITS: + case GL_DEPTH_CLEAR_VALUE: + case GL_DEPTH_FUNC: + case GL_DEPTH_SCALE: + case GL_DEPTH_TEST: + case GL_DEPTH_WRITEMASK: + case GL_DITHER: + case GL_DOUBLEBUFFER: + case GL_DRAW_BUFFER: + case GL_EDGE_FLAG: + case GL_EDGE_FLAG_ARRAY: + case GL_EDGE_FLAG_ARRAY_BUFFER_BINDING: + case GL_EDGE_FLAG_ARRAY_STRIDE: + case GL_ELEMENT_ARRAY_BUFFER_BINDING: + case GL_FEEDBACK_BUFFER_SIZE: + case GL_FEEDBACK_BUFFER_TYPE: + case GL_FOG: + case GL_FOG_COORD_ARRAY: + case GL_FOG_COORD_ARRAY_BUFFER_BINDING: + case GL_FOG_COORD_ARRAY_STRIDE: + case GL_FOG_COORD_ARRAY_TYPE: + case GL_FOG_COORD_SRC: + case GL_FOG_DENSITY: + case GL_FOG_END: + case GL_FOG_HINT: + case GL_FOG_INDEX: + case GL_FOG_MODE: + case GL_FOG_START: + case GL_FRAGMENT_SHADER_DERIVATIVE_HINT: + case GL_FRONT_FACE: + case GL_GENERATE_MIPMAP_HINT: + case GL_GREEN_BIAS: + case GL_GREEN_BITS: + case GL_GREEN_SCALE: + case GL_HISTOGRAM: + case GL_INDEX_ARRAY: + case GL_INDEX_ARRAY_BUFFER_BINDING: + case GL_INDEX_ARRAY_STRIDE: + case GL_INDEX_ARRAY_TYPE: + case GL_INDEX_BITS: + case GL_INDEX_CLEAR_VALUE: + case GL_INDEX_LOGIC_OP: + case GL_INDEX_MODE: + case GL_INDEX_OFFSET: + case GL_INDEX_SHIFT: + case GL_INDEX_WRITEMASK: + case GL_LIGHT0: + case GL_LIGHT1: + case GL_LIGHT2: + case GL_LIGHT3: + case GL_LIGHT4: + case GL_LIGHT5: + case GL_LIGHT6: + case GL_LIGHT7: + case GL_LIGHTING: + case GL_LIGHT_MODEL_COLOR_CONTROL: + case GL_LIGHT_MODEL_LOCAL_VIEWER: + case GL_LIGHT_MODEL_TWO_SIDE: + case GL_LINE_SMOOTH: + case GL_LINE_SMOOTH_HINT: + case GL_LINE_STIPPLE: + case GL_LINE_STIPPLE_PATTERN: + case GL_LINE_STIPPLE_REPEAT: + case GL_LINE_WIDTH: + // GL_LINE_WIDTH_GRANULARITY deprecated see GL_SMOOTH_LINE_WIDTH_GRANULARITY + case GL_LIST_BASE: + case GL_LIST_INDEX: + case GL_LIST_MODE: + // GL_LOGIC_OP same as GL_LOGIC_OP_INDEX + case GL_LOGIC_OP_MODE: + case GL_MAP1_COLOR_4: + case GL_MAP1_GRID_SEGMENTS: + case GL_MAP1_INDEX: + case GL_MAP1_NORMAL: + case GL_MAP1_TEXTURE_COORD_1: + case GL_MAP1_TEXTURE_COORD_2: + case GL_MAP1_TEXTURE_COORD_3: + case GL_MAP1_TEXTURE_COORD_4: + case GL_MAP1_VERTEX_3: + case GL_MAP1_VERTEX_4: + case GL_MAP2_COLOR_4: + case GL_MAP2_INDEX: + case GL_MAP2_NORMAL: + case GL_MAP2_TEXTURE_COORD_1: + case GL_MAP2_TEXTURE_COORD_2: + case GL_MAP2_TEXTURE_COORD_3: + case GL_MAP2_TEXTURE_COORD_4: + case GL_MAP2_VERTEX_3: + case GL_MAP2_VERTEX_4: + case GL_MAP_COLOR: + case GL_MAP_STENCIL: + case GL_MATRIX_MODE: + case GL_MAX_3D_TEXTURE_SIZE: + case GL_MAX_ATTRIB_STACK_DEPTH: + case GL_MAX_CLIENT_ATTRIB_STACK_DEPTH: + case GL_MAX_CLIP_PLANES: + case GL_MAX_COLOR_MATRIX_STACK_DEPTH: + case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: + case GL_MAX_CUBE_MAP_TEXTURE_SIZE: + case GL_MAX_DRAW_BUFFERS: + case GL_MAX_ELEMENTS_INDICES: + case GL_MAX_ELEMENTS_VERTICES: + case GL_MAX_EVAL_ORDER: + case GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: + case GL_MAX_LIGHTS: + case GL_MAX_LIST_NESTING: + case GL_MAX_MODELVIEW_STACK_DEPTH: + case GL_MAX_NAME_STACK_DEPTH: + case GL_MAX_PIXEL_MAP_TABLE: + case GL_MAX_PROJECTION_STACK_DEPTH: + case GL_MAX_TEXTURE_COORDS: + case GL_MAX_TEXTURE_IMAGE_UNITS: + case GL_MAX_TEXTURE_LOD_BIAS: + case GL_MAX_TEXTURE_SIZE: + case GL_MAX_TEXTURE_STACK_DEPTH: + case GL_MAX_TEXTURE_UNITS: + case GL_MAX_VARYING_FLOATS: + case GL_MAX_VERTEX_ATTRIBS: + case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: + case GL_MAX_VERTEX_UNIFORM_COMPONENTS: + case GL_MINMAX: + case GL_MODELVIEW_STACK_DEPTH: + case GL_NAME_STACK_DEPTH: + case GL_NORMAL_ARRAY: + case GL_NORMAL_ARRAY_BUFFER_BINDING: + case GL_NORMAL_ARRAY_STRIDE: + case GL_NORMAL_ARRAY_TYPE: + case GL_NORMALIZE: + case GL_NUM_COMPRESSED_TEXTURE_FORMATS: + case GL_PACK_ALIGNMENT: + case GL_PACK_IMAGE_HEIGHT: + case GL_PACK_LSB_FIRST: + case GL_PACK_ROW_LENGTH: + case GL_PACK_SKIP_IMAGES: + case GL_PACK_SKIP_PIXELS: + case GL_PACK_SKIP_ROWS: + case GL_PACK_SWAP_BYTES: + case GL_PERSPECTIVE_CORRECTION_HINT: + case GL_PIXEL_MAP_A_TO_A_SIZE: + case GL_PIXEL_MAP_B_TO_B_SIZE: + case GL_PIXEL_MAP_G_TO_G_SIZE: + case GL_PIXEL_MAP_I_TO_A_SIZE: + case GL_PIXEL_MAP_I_TO_B_SIZE: + case GL_PIXEL_MAP_I_TO_G_SIZE: + case GL_PIXEL_MAP_I_TO_I_SIZE: + case GL_PIXEL_MAP_I_TO_R_SIZE: + case GL_PIXEL_MAP_R_TO_R_SIZE: + case GL_PIXEL_MAP_S_TO_S_SIZE: + case GL_PIXEL_PACK_BUFFER_BINDING: + case GL_PIXEL_UNPACK_BUFFER_BINDING: + case GL_POINT_FADE_THRESHOLD_SIZE: + case GL_POINT_SIZE: + // GL_POINT_SIZE_GRANULARITY deprecated see GL_SMOOTH_POINT_SIZE_GRANULARITY + case GL_POINT_SIZE_MAX: + case GL_POINT_SIZE_MIN: + case GL_POINT_SMOOTH: + case GL_POINT_SMOOTH_HINT: + case GL_POINT_SPRITE: + case GL_POLYGON_OFFSET_FACTOR: + case GL_POLYGON_OFFSET_UNITS: + case GL_POLYGON_OFFSET_FILL: + case GL_POLYGON_OFFSET_LINE: + case GL_POLYGON_OFFSET_POINT: + case GL_POLYGON_SMOOTH: + case GL_POLYGON_SMOOTH_HINT: + case GL_POLYGON_STIPPLE: + case GL_POST_COLOR_MATRIX_COLOR_TABLE: + case GL_POST_COLOR_MATRIX_RED_BIAS: + case GL_POST_COLOR_MATRIX_GREEN_BIAS: + case GL_POST_COLOR_MATRIX_BLUE_BIAS: + case GL_POST_COLOR_MATRIX_ALPHA_BIAS: + case GL_POST_COLOR_MATRIX_RED_SCALE: + case GL_POST_COLOR_MATRIX_GREEN_SCALE: + case GL_POST_COLOR_MATRIX_BLUE_SCALE: + case GL_POST_COLOR_MATRIX_ALPHA_SCALE: + case GL_POST_CONVOLUTION_COLOR_TABLE: + case GL_POST_CONVOLUTION_RED_BIAS: + case GL_POST_CONVOLUTION_GREEN_BIAS: + case GL_POST_CONVOLUTION_BLUE_BIAS: + case GL_POST_CONVOLUTION_ALPHA_BIAS: + case GL_POST_CONVOLUTION_RED_SCALE: + case GL_POST_CONVOLUTION_GREEN_SCALE: + case GL_POST_CONVOLUTION_BLUE_SCALE: + case GL_POST_CONVOLUTION_ALPHA_SCALE: + case GL_PROJECTION_STACK_DEPTH: + case GL_READ_BUFFER: + case GL_RED_BIAS: + case GL_RED_BITS: + case GL_RED_SCALE: + case GL_RENDER_MODE: + case GL_RESCALE_NORMAL: + case GL_RGBA_MODE: + case GL_SAMPLE_BUFFERS: + case GL_SAMPLE_COVERAGE_VALUE: + case GL_SAMPLE_COVERAGE_INVERT: + case GL_SAMPLES: + case GL_SCISSOR_TEST: + case GL_SECONDARY_COLOR_ARRAY: + case GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING: + case GL_SECONDARY_COLOR_ARRAY_SIZE: + case GL_SECONDARY_COLOR_ARRAY_STRIDE: + case GL_SECONDARY_COLOR_ARRAY_TYPE: + case GL_SELECTION_BUFFER_SIZE: + case GL_SEPARABLE_2D: + case GL_SMOOTH_LINE_WIDTH_GRANULARITY: + case GL_SMOOTH_POINT_SIZE_GRANULARITY: + case GL_SHADE_MODEL: + case GL_STENCIL_BACK_FAIL: + case GL_STENCIL_BACK_FUNC: + case GL_STENCIL_BACK_PASS_DEPTH_FAIL: + case GL_STENCIL_BACK_PASS_DEPTH_PASS: + case GL_STENCIL_BACK_REF: + case GL_STENCIL_BACK_VALUE_MASK: + case GL_STENCIL_BACK_WRITEMASK: + case GL_STENCIL_BITS: + case GL_STENCIL_CLEAR_VALUE: + case GL_STENCIL_FAIL: + case GL_STENCIL_FUNC: + case GL_STENCIL_PASS_DEPTH_FAIL: + case GL_STENCIL_PASS_DEPTH_PASS: + case GL_STENCIL_REF: + case GL_STENCIL_TEST: + case GL_STENCIL_VALUE_MASK: + case GL_STENCIL_WRITEMASK: + case GL_STEREO: + case GL_SUBPIXEL_BITS: + case GL_TEXTURE_1D: + case GL_TEXTURE_BINDING_1D: + case GL_TEXTURE_2D: + case GL_TEXTURE_BINDING_2D: + case GL_TEXTURE_3D: + case GL_TEXTURE_BINDING_3D: + case GL_TEXTURE_BINDING_CUBE_MAP: + case GL_TEXTURE_COMPRESSION_HINT: + case GL_TEXTURE_COORD_ARRAY: + case GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING: + case GL_TEXTURE_COORD_ARRAY_SIZE: + case GL_TEXTURE_COORD_ARRAY_STRIDE: + case GL_TEXTURE_COORD_ARRAY_TYPE: + case GL_TEXTURE_CUBE_MAP: + case GL_TEXTURE_ENV_MODE: + case GL_TEXTURE_GEN_S: + case GL_TEXTURE_GEN_R: + case GL_TEXTURE_GEN_T: + case GL_TEXTURE_GEN_Q: + case GL_TEXTURE_STACK_DEPTH: + case GL_UNPACK_ALIGNMENT: + case GL_UNPACK_IMAGE_HEIGHT: + case GL_UNPACK_LSB_FIRST: + case GL_UNPACK_ROW_LENGTH: + case GL_UNPACK_SKIP_IMAGES: + case GL_UNPACK_SKIP_PIXELS: + case GL_UNPACK_SKIP_ROWS: + case GL_UNPACK_SWAP_BYTES: + case GL_VERTEX_ARRAY: + case GL_VERTEX_ARRAY_BUFFER_BINDING: + case GL_VERTEX_ARRAY_SIZE: + case GL_VERTEX_ARRAY_STRIDE: + case GL_VERTEX_ARRAY_TYPE: + case GL_VERTEX_PROGRAM_POINT_SIZE: + case GL_VERTEX_PROGRAM_TWO_SIDE: + case GL_ZOOM_X: + case GL_ZOOM_Y: + retSize = 1; + break; + case GL_ALIASED_POINT_SIZE_RANGE: + case GL_ALIASED_LINE_WIDTH_RANGE: + case GL_DEPTH_RANGE: + // GL_LINE_WIDTH_RANGE deprecated see GL_SMOOTH_LINE_WIDTH_RANGE + case GL_MAP1_GRID_DOMAIN: + case GL_MAP2_GRID_SEGMENTS: + case GL_MAX_VIEWPORT_DIMS: + // GL_POINT_SIZE_RANGE deprecated see GL_SMOOTH_POINT_SIZE_RANGE + case GL_POLYGON_MODE: + case GL_SMOOTH_LINE_WIDTH_RANGE: + case GL_SMOOTH_POINT_SIZE_RANGE: + retSize = 2; + break; + case GL_CURRENT_NORMAL: + case GL_POINT_DISTANCE_ATTENUATION: + retSize = 3; + break; + case GL_ACCUM_CLEAR_VALUE: + case GL_BLEND_COLOR: + case GL_COLOR_CLEAR_VALUE: + case GL_COLOR_WRITEMASK: + case GL_CURRENT_COLOR: + case GL_CURRENT_RASTER_COLOR: + case GL_CURRENT_RASTER_POSITION: + case GL_CURRENT_RASTER_SECONDARY_COLOR: + case GL_CURRENT_RASTER_TEXTURE_COORDS: + case GL_CURRENT_SECONDARY_COLOR: + case GL_CURRENT_TEXTURE_COORDS: + case GL_FOG_COLOR: + case GL_LIGHT_MODEL_AMBIENT: + case GL_MAP2_GRID_DOMAIN: + case GL_SCISSOR_BOX: + case GL_TEXTURE_ENV_COLOR: + case GL_VIEWPORT: + retSize = 4; + break; + case GL_COLOR_MATRIX: + case GL_MODELVIEW_MATRIX: + case GL_PROJECTION_MATRIX: + case GL_TEXTURE_MATRIX: + case GL_TRANSPOSE_COLOR_MATRIX: + case GL_TRANSPOSE_MODELVIEW_MATRIX: + case GL_TRANSPOSE_PROJECTION_MATRIX: + case GL_TRANSPOSE_TEXTURE_MATRIX: + retSize = 16; + break; + } + + return retSize; +} + +/**************************************************************************/ + +BEGIN_METHOD(GLGETBOOLEANV, GB_INTEGER parameter) + + GB_ARRAY bArray; + int size = checkSize(VARG(parameter)); + + if (!size) + { + GB.Error("Unknown parameter"); + return; + } + + int i; + GLboolean boolArray[size]; + + GB.Array.New(&bArray , GB_T_BOOLEAN , size); + glGetBooleanv(VARG(parameter), boolArray); + + for (i=0;i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLINFO_H +#define __GLINFO_H + +#include "main.h" + +DECLARE_METHOD(GLGETBOOLEANV); +DECLARE_METHOD(GLGETFLOATV); +DECLARE_METHOD(GLGETINTEGERV); +DECLARE_METHOD(GLGETSTRING); + +#endif /* __GLINFO_H */ diff --git a/gb.opengl/src/GLmodesExec.c b/gb.opengl/src/GLmodesExec.c new file mode 100644 index 00000000..43d42a37 --- /dev/null +++ b/gb.opengl/src/GLmodesExec.c @@ -0,0 +1,64 @@ +/*************************************************************************** + + GLmodesExec.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLMODESEXEC_C + +#include "GL.h" + +/**************************************************************************/ + +BEGIN_METHOD(GLDISABLE, GB_INTEGER capacity) + + glDisable(VARG(capacity)); + +END_METHOD + +BEGIN_METHOD(GLENABLE, GB_INTEGER capacity) + + glEnable(VARG(capacity)); + +END_METHOD + +BEGIN_METHOD_VOID(GLFLUSH) + + glFlush(); + +END_METHOD + +BEGIN_METHOD_VOID(GLFINISH) + + glFinish(); + +END_METHOD + +BEGIN_METHOD(GLHINT, GB_INTEGER target; GB_INTEGER mode) + + glHint(VARG(target), VARG(mode)); + +END_METHOD + +BEGIN_METHOD(GLISENABLED, GB_INTEGER capacity) + + GB.ReturnBoolean(glIsEnabled(VARG(capacity))); + +END_METHOD diff --git a/gb.opengl/src/GLmodesExec.h b/gb.opengl/src/GLmodesExec.h new file mode 100644 index 00000000..83b610e5 --- /dev/null +++ b/gb.opengl/src/GLmodesExec.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + GLmodesExec.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLMODESEXEC_H +#define __GLMODESEXEC_H + +#include "main.h" + +DECLARE_METHOD(GLDISABLE); +DECLARE_METHOD(GLENABLE); +DECLARE_METHOD(GLFLUSH); +DECLARE_METHOD(GLFINISH); +DECLARE_METHOD(GLHINT); +DECLARE_METHOD(GLISENABLED); + +#endif /* __GLMODESEXEC_H */ diff --git a/gb.opengl/src/GLpixelOperations.c b/gb.opengl/src/GLpixelOperations.c new file mode 100644 index 00000000..19208bef --- /dev/null +++ b/gb.opengl/src/GLpixelOperations.c @@ -0,0 +1,75 @@ +/*************************************************************************** + + GLpixelOperations.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLPIXELOPERATIONS_C + +#include "GL.h" + +BEGIN_METHOD(GLCOPYPIXELS, GB_INTEGER x; GB_INTEGER y; GB_INTEGER width; GB_INTEGER height; GB_INTEGER type) + + glCopyPixels(VARG(x), VARG(y), VARG(width), VARG(height), VARG(type)); + +END_METHOD + +BEGIN_METHOD(GLPIXELSTOREF, GB_INTEGER pname; GB_FLOAT param) + + glPixelStoref(VARG(pname), VARG(param)); + +END_METHOD + +BEGIN_METHOD(GLPIXELSTOREI, GB_INTEGER pname; GB_INTEGER param) + + glPixelStorei(VARG(pname), VARG(param)); + +END_METHOD + +BEGIN_METHOD(GLPIXELTRANSFERF, GB_INTEGER pname; GB_FLOAT param) + + glPixelTransferf(VARG(pname), VARG(param)); + +END_METHOD + +BEGIN_METHOD(GLPIXELTRANSFERI, GB_INTEGER pname; GB_INTEGER param) + + glPixelTransferi(VARG(pname), VARG(param)); + +END_METHOD + +BEGIN_METHOD(GLREADBUFFER, GB_INTEGER mode) + + glReadBuffer(VARG(mode)); + +END_METHOD + +BEGIN_METHOD(GLDRAWPIXELS, GB_OBJECT Image) + + GB_IMG *image; + int format; + + if (IMAGE_get(ARG(Image), &image, &format)) + return; + + glDrawPixels(image->width, image->height, format, GL_UNSIGNED_BYTE, image->data); + +END_METHOD + diff --git a/gb.opengl/src/GLpixelOperations.h b/gb.opengl/src/GLpixelOperations.h new file mode 100644 index 00000000..804736e1 --- /dev/null +++ b/gb.opengl/src/GLpixelOperations.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + GLpixelOperations.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLPIXELOPERATIONS_H +#define __GLPIXELOPERATIONS_H + +#include "main.h" + +DECLARE_METHOD(GLCOPYPIXELS); +DECLARE_METHOD(GLPIXELSTOREF); +DECLARE_METHOD(GLPIXELSTOREI); +DECLARE_METHOD(GLPIXELTRANSFERF); +DECLARE_METHOD(GLPIXELTRANSFERI); +DECLARE_METHOD(GLREADBUFFER); +DECLARE_METHOD(GLDRAWPIXELS); + +#endif /* __GLPIXELOPERATIONS_H */ diff --git a/gb.opengl/src/GLprimitives.c b/gb.opengl/src/GLprimitives.c new file mode 100644 index 00000000..7a4d5792 --- /dev/null +++ b/gb.opengl/src/GLprimitives.c @@ -0,0 +1,178 @@ +/*************************************************************************** + + GLprimitives.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLPRIMITIVES_C + +#include "GL.h" + +#include + +BEGIN_METHOD(GLBEGIN, GB_INTEGER primitive) + + glBegin(VARG(primitive)); + +END_METHOD + +BEGIN_METHOD(GLEDGEFLAG, GB_BOOLEAN bValue) + + glEdgeFlag(VARG(bValue)); + +END_METHOD + +BEGIN_METHOD_VOID(GLEND) + + glEnd(); + +END_METHOD + +BEGIN_METHOD(GLRECTF, GB_FLOAT x1; GB_FLOAT y1; GB_FLOAT x2; GB_FLOAT y2) + + glRectf(VARG(x1), VARG(y1), VARG(x2), VARG(y2)); + +END_METHOD + +BEGIN_METHOD(GLRECTI, GB_INTEGER x1; GB_INTEGER y1; GB_INTEGER x2; GB_INTEGER y2) + + glRecti(VARG(x1), VARG(y1), VARG(x2), VARG(y2)); + +END_METHOD + +BEGIN_METHOD(GLVERTEX2F, GB_FLOAT x; GB_FLOAT y) + + glVertex2d(VARG(x), VARG(y)); + +END_METHOD + +BEGIN_METHOD(GLVERTEX3F, GB_FLOAT x; GB_FLOAT y; GB_FLOAT z) + + glVertex3d(VARG(x), VARG(y), VARG(z)); + +END_METHOD + +BEGIN_METHOD(GLVERTEXF, GB_FLOAT x; GB_FLOAT y; GB_FLOAT z; GB_FLOAT w) + + if (MISSING(z)) + { + glVertex2d(VARG(x), VARG(y)); + return; + } + + if (MISSING(w)) + { + glVertex3d(VARG(x), VARG(y), VARG(z)); + return; + } + + glVertex4d(VARG(x), VARG(y), VARG(z), VARG(w)); + +END_METHOD + +BEGIN_METHOD(GLVERTEX2I, GB_INTEGER x; GB_INTEGER y) + + glVertex2i(VARG(x), VARG(y)); + +END_METHOD + +BEGIN_METHOD(GLVERTEX3I, GB_INTEGER x; GB_INTEGER y; GB_INTEGER z) + + glVertex3i(VARG(x), VARG(y), VARG(z)); + +END_METHOD + +BEGIN_METHOD(GLVERTEXI, GB_INTEGER x; GB_INTEGER y; GB_INTEGER z; GB_INTEGER w) + + if (MISSING(z)) + { + glVertex2i(VARG(x), VARG(y)); + return; + } + + if (MISSING(w)) + { + glVertex3i(VARG(x), VARG(y), VARG(z)); + return; + } + + glVertex4i(VARG(x), VARG(y), VARG(z), VARG(w)); + +END_METHOD + +BEGIN_METHOD(GLVERTEXFV, GB_OBJECT array) + + GLfloat x,y,z,w; + GB_ARRAY vertex = (GB_ARRAY) VARG(array); + int count = GB.Array.Count(vertex); + + if (count<2) + return; + + x = *((double *)GB.Array.Get(vertex,0)); + y = *((double *)GB.Array.Get(vertex,1)); + + if (count==2) + { + glVertex2d(x, y); + return; + } + + z = *((double *)GB.Array.Get(vertex,2)); + + if (count==3) + glVertex3d(x, y, z); + else + { + w = *((double *)GB.Array.Get(vertex,3)); + glVertex4d(x, y, z, w); + } + +END_METHOD + +BEGIN_METHOD(GLVERTEXIV, GB_OBJECT array) + + GLint x,y,z,w; + GB_ARRAY vertex = (GB_ARRAY) VARG(array); + int count = GB.Array.Count(vertex); + + if (count<2) + return; + + x = *((GLint *)GB.Array.Get(vertex,0)); + y = *((GLint *)GB.Array.Get(vertex,1)); + + if (count==2) + { + glVertex2i(x, y); + return; + } + + z = *((GLint *)GB.Array.Get(vertex,2)); + + if (count==3) + glVertex3i(x, y, z); + else + { + w = *((GLint *)GB.Array.Get(vertex,3)); + glVertex4i(x, y, z, w); + } + +END_METHOD diff --git a/gb.opengl/src/GLprimitives.h b/gb.opengl/src/GLprimitives.h new file mode 100644 index 00000000..b7c838bd --- /dev/null +++ b/gb.opengl/src/GLprimitives.h @@ -0,0 +1,43 @@ +/*************************************************************************** + + GLprimitives.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLPRIMITIVES_H +#define __GLPRIMITIVES_H + +#include "main.h" + +DECLARE_METHOD(GLBEGIN); +DECLARE_METHOD(GLEDGEFLAG); +DECLARE_METHOD(GLEND); +DECLARE_METHOD(GLRECTF); +DECLARE_METHOD(GLRECTI); +DECLARE_METHOD(GLVERTEX2F); +DECLARE_METHOD(GLVERTEX3F); +DECLARE_METHOD(GLVERTEXF); +DECLARE_METHOD(GLVERTEX2I); +DECLARE_METHOD(GLVERTEX3I); +DECLARE_METHOD(GLVERTEXI); +DECLARE_METHOD(GLVERTEXFV); +DECLARE_METHOD(GLVERTEXIV); + +#endif /* __GLPRIMITIVES_H */ diff --git a/gb.opengl/src/GLrasterization.c b/gb.opengl/src/GLrasterization.c new file mode 100644 index 00000000..2f2f3f11 --- /dev/null +++ b/gb.opengl/src/GLrasterization.c @@ -0,0 +1,214 @@ +/*************************************************************************** + + GLrasterization.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLRASTERIZATION_C + +#include "GL.h" + +BEGIN_METHOD_VOID(GLBITMAP) + + WARNING("glBitmap() Not implemented !"); + +END_METHOD + +BEGIN_METHOD(GLCULLFACE, GB_INTEGER mode) + + glCullFace(VARG(mode)); + +END_METHOD + +BEGIN_METHOD_VOID(GLGETPOLYGONSTIPPLE) + + WARNING("glGetPolygonStipple() Not implemented !"); + +END_METHOD + +BEGIN_METHOD(GLLINESTIPPLE, GB_INTEGER factor; GB_INTEGER pattern) + + glLineStipple(VARG(factor), VARG(pattern)); + +END_METHOD + +BEGIN_METHOD(GLLINEWIDTH, GB_FLOAT width) + + glLineWidth(VARG(width)); + +END_METHOD + +BEGIN_METHOD(GLPOINTSIZE, GB_FLOAT size) + + glPointSize(VARG(size)); + +END_PROPERTY + +BEGIN_METHOD(GLPOLYGONMODE, GB_INTEGER face; GB_INTEGER mode) + + glPolygonMode(VARG(face), VARG(mode)); + +END_METHOD +#if 0 +BEGIN_METHOD(GLPOLYGONSTIPPLE, GB_INTEGER mask) + + GLubyte mask[4*32]; + GB_ARRAY iArray = (GB_ARRAY) VARG(mask); + uint i; + + if (GB.Array.Count(iArray) != 32) + return; + + for (i=0; i<32; i++) + mask[i*4] = *((GLint *)GB.Array.Get(iArray,i)); + + glPolygonStipple(mask); + +END_METHOD +#endif +BEGIN_METHOD(GLPOLYGONOFFSET, GB_FLOAT factor; GB_FLOAT units) + + glPolygonOffset(VARG(factor), VARG(units)); + +END_METHOD + +BEGIN_METHOD(GLRASTERPOS2F, GB_FLOAT x; GB_FLOAT y) + + glRasterPos2d(VARG(x), VARG(y)); + +END_PROPERTY + +BEGIN_METHOD(GLRASTERPOS3F, GB_FLOAT x; GB_FLOAT y; GB_FLOAT z) + + glRasterPos3d(VARG(x), VARG(y), VARG(z)); + +END_PROPERTY + +BEGIN_METHOD(GLRASTERPOSF, GB_FLOAT x; GB_FLOAT y; GB_FLOAT z; GB_FLOAT w) + + if (MISSING(z)) + { + glRasterPos2d(VARG(x), VARG(y)); + return; + } + + if (MISSING(w)) + { + glRasterPos3d(VARG(x), VARG(y), VARG(z)); + return; + } + + glRasterPos4d(VARG(x), VARG(y), VARG(z), VARG(w)); + +END_PROPERTY + +BEGIN_METHOD(GLRASTERPOS2I, GB_INTEGER x; GB_INTEGER y) + + glRasterPos2i(VARG(x), VARG(y)); + +END_PROPERTY + +BEGIN_METHOD(GLRASTERPOS3I, GB_INTEGER x; GB_INTEGER y; GB_INTEGER z) + + glRasterPos3i(VARG(x), VARG(y), VARG(z)); + +END_PROPERTY + +BEGIN_METHOD(GLRASTERPOSI, GB_INTEGER x; GB_INTEGER y; GB_INTEGER z; GB_INTEGER w) + + if (MISSING(z)) + { + glRasterPos2i(VARG(x), VARG(y)); + return; + } + + if (MISSING(w)) + { + glRasterPos3i(VARG(x), VARG(y), VARG(z)); + return; + } + + glRasterPos4i(VARG(x), VARG(y), VARG(z), VARG(w)); + +END_PROPERTY + +BEGIN_METHOD(GLRASTERPOSFV, GB_OBJECT array) + + GLdouble x,y,z,w; + GB_ARRAY fArray = (GB_ARRAY) VARG(array); + int count = GB.Array.Count(fArray); + + if (count<2) + return; + + x = *((GLdouble *)GB.Array.Get(fArray,0)); + y = *((GLdouble *)GB.Array.Get(fArray,1)); + + if (count==2) + { + glRasterPos2d(x, y); + return; + } + + z = *((GLdouble *)GB.Array.Get(fArray,2)); + + if (count==3) + { + glRasterPos3d(x, y, z); + } + else + { + w = *((double *)GB.Array.Get(fArray,3)); + glRasterPos4d(x, y, z, w); + } + +END_METHOD + +BEGIN_METHOD(GLRASTERPOSIV, GB_OBJECT array) + + GLint x,y,z,w; + GB_ARRAY fArray = (GB_ARRAY) VARG(array); + int count = GB.Array.Count(fArray); + + if (count<2) + return; + + x = *((GLint *)GB.Array.Get(fArray,0)); + y = *((GLint *)GB.Array.Get(fArray,1)); + + if (count==2) + { + glRasterPos2i(x, y); + return; + } + + z = *((GLint *)GB.Array.Get(fArray,2)); + + if (count==3) + { + glRasterPos3i(x, y, z); + } + else + { + w = *((double *)GB.Array.Get(fArray,3)); + glRasterPos4i(x, y, z, w); + } + +END_METHOD diff --git a/gb.opengl/src/GLrasterization.h b/gb.opengl/src/GLrasterization.h new file mode 100644 index 00000000..aa186084 --- /dev/null +++ b/gb.opengl/src/GLrasterization.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + GLrasterization.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLRASTERIZATION_H +#define __GLRASTERIZATION_H + +#include "main.h" + +// TODO +DECLARE_METHOD(GLBITMAP); +DECLARE_METHOD(GLPOLYGONSTIPPLE); +DECLARE_METHOD(GLGETPOLYGONSTIPPLE); +// +DECLARE_METHOD(GLCULLFACE); +DECLARE_METHOD(GLLINESTIPPLE); +DECLARE_METHOD(GLLINEWIDTH); +DECLARE_METHOD(GLPOINTSIZE); +DECLARE_METHOD(GLPOLYGONMODE); +DECLARE_METHOD(GLPOLYGONOFFSET); +DECLARE_METHOD(GLRASTERPOS2F); +DECLARE_METHOD(GLRASTERPOS3F); +DECLARE_METHOD(GLRASTERPOSF); +DECLARE_METHOD(GLRASTERPOS2I); +DECLARE_METHOD(GLRASTERPOS3I); +DECLARE_METHOD(GLRASTERPOSI); +DECLARE_METHOD(GLRASTERPOSFV); +DECLARE_METHOD(GLRASTERPOSIV); + +#endif /* __GLRASTERIZATION_H */ diff --git a/gb.opengl/src/GLselectFeedback.c b/gb.opengl/src/GLselectFeedback.c new file mode 100644 index 00000000..1280126f --- /dev/null +++ b/gb.opengl/src/GLselectFeedback.c @@ -0,0 +1,150 @@ +/*************************************************************************** + + GLselectFeedback.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLSELECTPIXMAP_C + +#include "GL.h" + +#define MAX_BUFFER_SIZE 2048 + +/* for feedback and select buffers */ +static GLuint selectbuffer[MAX_BUFFER_SIZE]; +static GLfloat feedbuffer[MAX_BUFFER_SIZE]; + +BEGIN_METHOD(GLFEEDBACKBUFFER, GB_INTEGER Type) + + glFeedbackBuffer(MAX_BUFFER_SIZE, VARG(Type), feedbuffer); + +END_METHOD + +BEGIN_METHOD_VOID(GLINITNAMES) + + glInitNames(); + +END_METHOD + +BEGIN_METHOD(GLLOADNAME, GB_INTEGER Name) + + glLoadName(VARG(Name)); + +END_METHOD + +BEGIN_METHOD(GLPASSTHROUGH, GB_FLOAT Token) + + glPassThrough(VARG(Token)); + +END_METHOD + +BEGIN_METHOD_VOID(GLPOPNAME) + + glPopName(); + +END_METHOD + +BEGIN_METHOD(GLPUSHNAME, GB_INTEGER Name) + + glPushName(VARG(Name)); + +END_METHOD + +BEGIN_METHOD(GLRENDERMODE, GB_INTEGER Mode) + + static GLuint oldrendermode = GL_RENDER; /* default render mode */ + GLint result, mode = VARG(Mode); + + /* invalid mode or same render mode: + return null object, and let propagate an opengl error */ + if ((mode < GL_RENDER) || (mode > GL_SELECT) || (mode == oldrendermode)) + { + GB.ReturnNull(); + glRenderMode(mode); + return; + } + + result = glRenderMode(mode); + + if (!result) + { + GB.ReturnNull(); + oldrendermode = mode; + return; + } + + if (result < 0) + { + GB.Error ("Gl.RenderMode, buffer is too small !"); + return; + } + + if (oldrendermode == GL_SELECT) + { + GB_ARRAY hitArray; + GLuint *hitbuffer = selectbuffer; + int idxhit, idxname; + + GB.Array.New(&hitArray, GB.FindClass("Integer[]"), result); + //GB.New(POINTER(&hitArray), GB.FindClass("Integer[][]"), NULL, NULL); + + for (idxhit=0; idxhit < result; idxhit++) + { + GB_ARRAY childhitArray; + + int names = *hitbuffer; + GB.Array.New(&childhitArray, GB_T_INTEGER, names + 3); + *((GLuint *)GB.Array.Get(childhitArray, 0)) = *hitbuffer++; // maxname + *((GLuint *)GB.Array.Get(childhitArray, 1)) = *hitbuffer++; // zmin + *((GLuint *)GB.Array.Get(childhitArray, 2)) = *hitbuffer++; // zmax + + for (idxname=0; idxname < names; idxname++) + *((GLuint *)GB.Array.Get(childhitArray, 3 + idxname)) = *hitbuffer++; // names + + GB.Ref(childhitArray); + *((GB_ARRAY *)GB.Array.Get(hitArray, idxhit)) = childhitArray; + } + + GB.ReturnObject(hitArray); + return; + } + + if (oldrendermode == GL_FEEDBACK) + { + GB_ARRAY feedArray; + GLfloat *resultbuffer = feedbuffer; + int idxfeed; + + GB.Array.New(&feedArray, GB_T_FLOAT, result); + + for (idxfeed=0; idxfeed < result; idxfeed++) + *((GLfloat *)GB.Array.Get(feedArray, idxfeed)) = *resultbuffer++; + + GB.ReturnObject(feedArray); + return; + } + +END_METHOD + +BEGIN_METHOD_VOID(GLSELECTBUFFER) + + glSelectBuffer(MAX_BUFFER_SIZE, selectbuffer); + +END_METHOD diff --git a/gb.opengl/src/GLselectFeedback.h b/gb.opengl/src/GLselectFeedback.h new file mode 100644 index 00000000..91026d43 --- /dev/null +++ b/gb.opengl/src/GLselectFeedback.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + GLselectFeedback.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLSELECTPIXMAP_H +#define __GLSELECTPIXMAP_H + +#include "main.h" + +DECLARE_METHOD(GLFEEDBACKBUFFER); +DECLARE_METHOD(GLINITNAMES); +DECLARE_METHOD(GLLOADNAME); +DECLARE_METHOD(GLPASSTHROUGH); +DECLARE_METHOD(GLPOPNAME); +DECLARE_METHOD(GLPUSHNAME); +DECLARE_METHOD(GLRENDERMODE); +DECLARE_METHOD(GLSELECTBUFFER); + +#endif /* __GLSELECTPIXMAP_H */ diff --git a/gb.opengl/src/GLtextureMapping.c b/gb.opengl/src/GLtextureMapping.c new file mode 100644 index 00000000..e548c77c --- /dev/null +++ b/gb.opengl/src/GLtextureMapping.c @@ -0,0 +1,334 @@ +/*************************************************************************** + + GLtextureMapping.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLTEXTUREMAPPING_C + +#include "GL.h" + +BEGIN_METHOD(GLBINDTEXTURE, GB_INTEGER target; GB_INTEGER texture) + + glBindTexture(VARG(target), VARG(texture)); + +END_METHOD + +BEGIN_METHOD(GLACTIVETEXTURE, GB_INTEGER texture) + + glActiveTexture(VARG(texture)); + +END_METHOD + +BEGIN_METHOD(GLCOPYTEXIMAGE1D, GB_INTEGER Ta; GB_INTEGER Le; GB_INTEGER Fo; GB_INTEGER X; GB_INTEGER Y; GB_INTEGER Wi; GB_INTEGER Bo) + + glCopyTexImage1D(VARG(Ta), VARG(Le), VARG(Fo), VARG(X), VARG(Y), VARG(Wi), VARG(Bo)); + +END_METHOD + +BEGIN_METHOD(GLCOPYTEXIMAGE2D, GB_INTEGER Ta; GB_INTEGER Le; GB_INTEGER Fo; GB_INTEGER X; GB_INTEGER Y; GB_INTEGER Wi; GB_INTEGER He; GB_INTEGER Bo) + + glCopyTexImage2D(VARG(Ta), VARG(Le), VARG(Fo), VARG(X), VARG(Y), VARG(Wi), VARG(He), VARG(Bo)); + +END_METHOD + +BEGIN_METHOD(GLDELETETEXTURES, GB_OBJECT textures) + + + GB_ARRAY iArray = (GB_ARRAY) VARG(textures); + int i,count = GB.Array.Count(iArray); + GLuint texture[1]; + + if (count<=0) + return; + + for (i=0;i 4 ? 4 : count); + + for (i=0; i 4 ? 4 : count); + + for (i=0; iwidth, VARGOPT(Border, 0), + format, GL_UNSIGNED_BYTE, image->data); + +END_METHOD + +BEGIN_METHOD(GLTEXIMAGE2D, GB_OBJECT Image; GB_INTEGER Level; GB_INTEGER Border) + + GB_IMG *image; + int format; + + if (IMAGE_get(ARG(Image), &image, &format)) + return; + + glTexImage2D(GL_TEXTURE_2D, VARGOPT(Level, 0), IMAGE_get_ncolors(format), image->width, image->height, + VARGOPT(Border, 0), format, GL_UNSIGNED_BYTE, image->data); + +END_METHOD + +BEGIN_METHOD(GLTEXSUBIMAGE1D, GB_OBJECT Image; GB_INTEGER XOffset; GB_INTEGER Width; GB_INTEGER Level) + + GB_IMG *image; + int format; + + if (IMAGE_get(ARG(Image), &image, &format)) + return; + + glTexSubImage1D(GL_TEXTURE_1D, VARGOPT(Level, 0), VARG(XOffset), VARG(Width), + format, GL_UNSIGNED_BYTE, image->data); + +END_METHOD + +BEGIN_METHOD(GLTEXSUBIMAGE2D, GB_OBJECT Image; GB_INTEGER XOffset; GB_INTEGER YOffset; GB_INTEGER Width; GB_INTEGER Height; GB_INTEGER Level) + + GB_IMG *image; + int format; + + if (IMAGE_get(ARG(Image), &image, &format)) + return; + + glTexSubImage2D(GL_TEXTURE_2D, VARGOPT(Level, 0), VARG(XOffset), VARG(YOffset), VARG(Width), VARG(Height), + format, GL_UNSIGNED_BYTE, image->data); + +END_METHOD + +BEGIN_METHOD(GLTEXPARAMETERF, GB_INTEGER Target; GB_INTEGER Pname; GB_FLOAT Param) + + glTexParameterf(VARG(Target), VARG(Pname), VARG(Param)); + +END_METHOD + +BEGIN_METHOD(GLTEXPARAMETERFV, GB_INTEGER Target; GB_INTEGER Pname; GB_OBJECT Params) + + GLfloat params[4]; + GB_ARRAY fArray = (GB_ARRAY) VARG(Params); + int count = GB.Array.Count(fArray); + int i; + + count = (count > 4 ? 4 : count); + + for (i=0; i 4 ? 4 : count); + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLTEXTUREMAPPING_H +#define __GLTEXTUREMAPPING_H + +#include "main.h" + +DECLARE_METHOD(GLBINDTEXTURE); +DECLARE_METHOD(GLACTIVETEXTURE); +DECLARE_METHOD(GLCOPYTEXIMAGE1D); +DECLARE_METHOD(GLCOPYTEXIMAGE2D); +DECLARE_METHOD(GLDELETETEXTURES); +DECLARE_METHOD(GLGENTEXTURES); +DECLARE_METHOD(GLISTEXTURE); +DECLARE_METHOD(GLTEXCOORD1F); +DECLARE_METHOD(GLTEXCOORD2F); +DECLARE_METHOD(GLTEXCOORD3F); +DECLARE_METHOD(GLTEXCOORDF); +DECLARE_METHOD(GLTEXCOORD1I); +DECLARE_METHOD(GLTEXCOORD2I); +DECLARE_METHOD(GLTEXCOORD3I); +DECLARE_METHOD(GLTEXCOORDI); +DECLARE_METHOD(GLTEXENVF); +DECLARE_METHOD(GLTEXENVFV); +DECLARE_METHOD(GLTEXENVI); +DECLARE_METHOD(GLTEXENVIV); +DECLARE_METHOD(GLTEXIMAGE1D); +DECLARE_METHOD(GLTEXIMAGE2D); +DECLARE_METHOD(GLTEXSUBIMAGE1D); +DECLARE_METHOD(GLTEXSUBIMAGE2D); +DECLARE_METHOD(GLTEXPARAMETERF); +DECLARE_METHOD(GLTEXPARAMETERFV); +DECLARE_METHOD(GLTEXPARAMETERI); +DECLARE_METHOD(GLTEXPARAMETERIV); +DECLARE_METHOD(GLTEXGENI); +DECLARE_METHOD(GLMULTITEXCOORD2F); + +#endif /* __GLTEXTUREMAPPING_H */ diff --git a/gb.opengl/src/Makefile.am b/gb.opengl/src/Makefile.am new file mode 100644 index 00000000..fc741bd4 --- /dev/null +++ b/gb.opengl/src/Makefile.am @@ -0,0 +1,29 @@ +COMPONENT = gb.opengl +include $(top_srcdir)/component.am + +SUBDIRS = . @GLSL_DIR@ @GLU_DIR@ @SGE_DIR@ + +gblib_LTLIBRARIES = gb.opengl.la + +gb_opengl_la_LIBADD = @OPENGL_LIB@ +gb_opengl_la_LDFLAGS = -module @LD_FLAGS@ @OPENGL_LDFLAGS@ +gb_opengl_la_CPPFLAGS = @OPENGL_INC@ + +gb_opengl_la_SOURCES = \ + main.h main.c gb.gl.h \ + GL.h GL.c \ + GLinfo.h GLinfo.c \ + GLclipping.c GLclipping.h \ + GLcolorLighting.h GLcolorLighting.c \ + GLcoordTransf.h GLcoordTransf.c \ + GLdisplayList.h GLdisplayList.c \ + GLeval.h GLeval.c \ + GLfog.h GLfog.c \ + GLframeBufferOps.h GLframeBufferOps.c \ + GLmodesExec.h GLmodesExec.c \ + GLprimitives.h GLprimitives.c \ + GLpixelOperations.h GLpixelOperations.c \ + GLrasterization.h GLrasterization.c \ + GLtextureMapping.h GLtextureMapping.c \ + GLselectFeedback.h GLselectFeedback.c \ + framebufferobject.h framebufferobject.c diff --git a/gb.opengl/src/framebufferobject.c b/gb.opengl/src/framebufferobject.c new file mode 100644 index 00000000..3aebad97 --- /dev/null +++ b/gb.opengl/src/framebufferobject.c @@ -0,0 +1,194 @@ +/*************************************************************************** + + framebufferobject.c + + (c) 2011 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __FBO_C + +#include "GL.h" + +BEGIN_METHOD(GLBINDFRAMEBUFFEREXT, GB_INTEGER target; GB_INTEGER framebuffer) + + glBindFramebufferEXT(VARG(target), VARG(framebuffer)); + +END_METHOD + +BEGIN_METHOD(GLBINDRENDERBUFFEREXT, GB_INTEGER target; GB_INTEGER renderbuffer) + + glBindRenderbufferEXT(VARG(target), VARG(renderbuffer)); + +END_METHOD + +BEGIN_METHOD(GLCHECKFRAMEBUFFERSTATUSEXT, GB_INTEGER target) + + GB.ReturnInteger(glCheckFramebufferStatusEXT(VARG(target))); + +END_METHOD + +BEGIN_METHOD(GLDELETEFRAMEBUFFERSEXT, GB_OBJECT buffers) + + + GB_ARRAY iArray = (GB_ARRAY) VARG(buffers); + int i,count = GB.Array.Count(iArray); + GLuint buffer[1]; + + if (count<=0) + return; + + for (i=0;i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __FBO_H +#define __FBO_H + +#include "main.h" +DECLARE_METHOD(GLBINDFRAMEBUFFEREXT); +DECLARE_METHOD(GLBINDRENDERBUFFEREXT); +DECLARE_METHOD(GLCHECKFRAMEBUFFERSTATUSEXT); +DECLARE_METHOD(GLDELETEFRAMEBUFFERSEXT); +DECLARE_METHOD(GLDELETERENDERBUFFERSEXT); +DECLARE_METHOD(GLFRAMEBUFFERRENDERBUFFEREXT); +DECLARE_METHOD(GLFRAMEBUFFERTEXTURE1DEXT); +DECLARE_METHOD(GLFRAMEBUFFERTEXTURE2DEXT); +DECLARE_METHOD(GLFRAMEBUFFERTEXTURE3DEXT); +DECLARE_METHOD(GLGENERATEMIPMAPEXT); +DECLARE_METHOD(GLGENFRAMEBUFFERSEXT); +DECLARE_METHOD(GLGENRENDERBUFFERSEXT); +DECLARE_METHOD(GLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXT); +DECLARE_METHOD(GLGETRENDERBUFFERPARAMETERIVEXT); +DECLARE_METHOD(GLISFRAMEBUFFEREXT); +DECLARE_METHOD(GLISRENDERBUFFEREXT); +DECLARE_METHOD(GLRENDERBUFFERSTORAGEEXT); + +#endif /* __FBO_H */ diff --git a/gb.opengl/src/gb.gl.h b/gb.opengl/src/gb.gl.h new file mode 100644 index 00000000..23d05711 --- /dev/null +++ b/gb.opengl/src/gb.gl.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + gb.gl.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_GL_H +#define __GB_GL_H + +#include "gambas.h" + +#define GL_INTERFACE_VERSION 1 + +typedef + struct { + intptr_t version; + // Must be called after the context is init ! + //** Perhaps also when context is changed but not tested ** + bool (*Init)(void); + + void *_null; + } + GL_INTERFACE; + +#endif diff --git a/gb.opengl/src/gb.opengl.component b/gb.opengl/src/gb.opengl.component new file mode 100644 index 00000000..49416dd4 --- /dev/null +++ b/gb.opengl/src/gb.opengl.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.opengl +Author=Laurent Carlier +Need=OpenGLViewer +Requires=gb.image +State=Stable diff --git a/gb.opengl/src/glsl/GL.c b/gb.opengl/src/glsl/GL.c new file mode 100644 index 00000000..b664a2a3 --- /dev/null +++ b/gb.opengl/src/glsl/GL.c @@ -0,0 +1,114 @@ +/*************************************************************************** + + GL.c + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GL_C + +#include "GL.h" + +#include "GLshader.h" +#include "GLprogram.h" +#include "GLuniform.h" +#include "GLattributes.h" + +GB_DESC Cgl[] = +{ + GB_DECLARE("Gl",0), GB_NOT_CREATABLE(), + + /* GLshader.c */ + GB_STATIC_METHOD("AttachShader", NULL, GLATTACHSHADER, "(Program)i(Shader)i"), + GB_STATIC_METHOD("CompileShader", NULL, GLCOMPILESHADER, "(Shader)i"), + GB_STATIC_METHOD("CreateShader", "i", GLCREATESHADER, "(ShaderType)i"), + GB_STATIC_METHOD("DeleteShader", NULL, GLDELETESHADER, "(Shader)i"), + GB_STATIC_METHOD("DetachShader", NULL, GLDETACHSHADER, "(Program)i(Shader)i"), + GB_STATIC_METHOD("GetAttachedShaders", "Integer[]", GLGETATTACHEDSHADERS, "(Program)i"), + GB_STATIC_METHOD("GetShaderInfoLog", "s", GLGETSHADERINFOLOG, "(Shader)i"), + GB_STATIC_METHOD("GetShaderiv", "Integer[]", GLGETSHADERIV, "(Shader)i(Pname)i"), + GB_STATIC_METHOD("GetShaderSource", "s", GLGETSHADERSOURCE, "(Shader)i"), + GB_STATIC_METHOD("IsShader", "b", GLISSHADER, "(Shader)i"), + GB_STATIC_METHOD("ShaderSource", NULL, GLSHADERSOURCE, "(Shader)i(Source)s"), + /* GLprogram.c */ + GB_STATIC_METHOD("CreateProgram", "i", GLCREATEPROGRAM, NULL), + GB_STATIC_METHOD("DeleteProgram", NULL, GLDELETEPROGRAM, "(Program)i"), + GB_STATIC_METHOD("GetProgramInfoLog", "s", GLGETPROGRAMINFOLOG, "(Program)i"), + GB_STATIC_METHOD("GetProgramiv", "Integer[]", GLGETPROGRAMIV, "(Program)i(Pname)i"), + GB_STATIC_METHOD("IsProgram", "b", GLISPROGRAM, "(Program)i"), + GB_STATIC_METHOD("LinkProgram", NULL, GLLINKPROGRAM, "(Program)i"), + GB_STATIC_METHOD("UseProgram", NULL, GLUSEPROGRAM, "(Program)i"), + GB_STATIC_METHOD("ValidateProgram", NULL, GLVALIDATEPROGRAM, "(Program)i"), + /* GLuniform.c */ + GB_STATIC_METHOD("GetUniformLocation", "i", GLGETUNIFORMLOCATION, "(Program)i(Name)s"), + GB_STATIC_METHOD("Uniform1f", NULL, GLUNIFORM1F, "(Location)i(V0)f"), + GB_STATIC_METHOD("Uniform2f", NULL, GLUNIFORM2F, "(Location)i(V0)f(V1)f"), + GB_STATIC_METHOD("Uniform3f", NULL, GLUNIFORM3F, "(Location)i(V0)f(V1)f(V2)f"), + GB_STATIC_METHOD("Uniform4f", NULL, GLUNIFORM4F, "(Location)i(V0)f(V1)f(V3)f(V3)f"), + GB_STATIC_METHOD("Uniform1i", NULL, GLUNIFORM1I, "(Location)i(V0)i"), + GB_STATIC_METHOD("Uniform2i", NULL, GLUNIFORM2I, "(Location)i(V0)i(V1)i"), + GB_STATIC_METHOD("Uniform3i", NULL, GLUNIFORM3I, "(Location)i(V0)i(V1)i(V2)i"), + GB_STATIC_METHOD("Uniform4i", NULL, GLUNIFORM4I, "(Location)i(V0)i(V1)i(V3)i(V3)i"), + GB_STATIC_METHOD("Uniform1fv", NULL, GLUNIFORM1FV, "(Location)i(Values)Float[]"), + GB_STATIC_METHOD("Uniform2fv", NULL, GLUNIFORM2FV, "(Location)i(Values)Float[]"), + GB_STATIC_METHOD("Uniform3fv", NULL, GLUNIFORM3FV, "(Location)i(Values)Float[]"), + GB_STATIC_METHOD("Uniform4fv", NULL, GLUNIFORM4FV, "(Location)i(Values)Float[]"), + GB_STATIC_METHOD("Uniform1iv", NULL, GLUNIFORM1IV, "(Location)i(Values)Integer[]"), + GB_STATIC_METHOD("Uniform2iv", NULL, GLUNIFORM2IV, "(Location)i(Values)Integer[]"), + GB_STATIC_METHOD("Uniform3iv", NULL, GLUNIFORM3IV, "(Location)i(Values)Integer[]"), + GB_STATIC_METHOD("Uniform4iv", NULL, GLUNIFORM4IV, "(Location)i(Values)Integer[]"), + GB_STATIC_METHOD("UniformMatrix2fv", NULL, GLUNIFORMMATRIX2FV, "(Location)i(Transpose)b(Values)Float[]"), + GB_STATIC_METHOD("UniformMatrix3fv", NULL, GLUNIFORMMATRIX3FV, "(Location)i(Transpose)b(Values)Float[]"), + GB_STATIC_METHOD("UniformMatrix4fv", NULL, GLUNIFORMMATRIX4FV, "(Location)i(Transpose)b(Values)Float[]"), + GB_STATIC_METHOD("UniformMatrix2x3fv", NULL, GLUNIFORMMATRIX2X3FV, "(Location)i(Transpose)b(Values)Float[]"), + GB_STATIC_METHOD("UniformMatrix3x2fv", NULL, GLUNIFORMMATRIX3X2FV, "(Location)i(Transpose)b(Values)Float[]"), + GB_STATIC_METHOD("UniformMatrix2x4fv", NULL, GLUNIFORMMATRIX2X4FV, "(Location)i(Transpose)b(Values)Float[]"), + GB_STATIC_METHOD("UniformMatrix4x2fv", NULL, GLUNIFORMMATRIX4X2FV, "(Location)i(Transpose)b(Values)Float[]"), + GB_STATIC_METHOD("UniformMatrix3x4fv", NULL, GLUNIFORMMATRIX3X4FV, "(Location)i(Transpose)b(Values)Float[]"), + GB_STATIC_METHOD("UniformMatrix4x3fv", NULL, GLUNIFORMMATRIX4X3FV, "(Location)i(Transpose)b(Values)Float[]"), + /* GLattributes.c */ + GB_STATIC_METHOD("BindAttribLocation",NULL, GLBINDATTRIBLOCATION, "(Program)i(Index)i(Name)s"), + GB_STATIC_METHOD("VertexAttrib1f", NULL, GLVERTEXATTRIB1F, "(Index)i(X)f"), + GB_STATIC_METHOD("VertexAttrib1fv", NULL, GLVERTEXATTRIB1FV, "(Index)i(V)Float[]"), + GB_STATIC_METHOD("VertexAttrib2f", NULL, GLVERTEXATTRIB2F, "(Index)i(X)f(Y)f"), + GB_STATIC_METHOD("VertexAttrib2fv", NULL, GLVERTEXATTRIB2FV, "(Index)i(V)Float[]"), + GB_STATIC_METHOD("VertexAttrib3f", NULL, GLVERTEXATTRIB3F, "(Index)i(X)f(Y)f(Z)f"), + GB_STATIC_METHOD("VertexAttrib3fv", NULL, GLVERTEXATTRIB3FV, "(Index)i(V)Float[]"), + GB_STATIC_METHOD("VertexAttrib4f", NULL, GLVERTEXATTRIB4F, "(Index)i(X)f(Y)f(Z)f(W)f"), + GB_STATIC_METHOD("VertexAttrib4fv", NULL, GLVERTEXATTRIB4FV, "(Index)i(V)Float[]"), + + /* Contants */ + GB_CONSTANT("ACTIVE_ATTRIBUTES", "i", GL_ACTIVE_ATTRIBUTES), + GB_CONSTANT("ACTIVE_ATTRIBUTE_MAX_LENGTH", "i", GL_ACTIVE_ATTRIBUTE_MAX_LENGTH), + GB_CONSTANT("ACTIVE_UNIFORMS", "i", GL_ACTIVE_UNIFORMS), + GB_CONSTANT("ACTIVE_UNIFORM_MAX_LENGTH", "i", GL_ACTIVE_UNIFORM_MAX_LENGTH), + GB_CONSTANT("ATTACHED_SHADERS", "i", GL_ATTACHED_SHADERS), + GB_CONSTANT("COMPILE_STATUS", "i", GL_COMPILE_STATUS), + GB_CONSTANT("CURRENT_PROGRAM", "i", GL_CURRENT_PROGRAM), + GB_CONSTANT("DELETE_STATUS", "i", GL_DELETE_STATUS), + GB_CONSTANT("FRAGMENT_SHADER", "i", GL_FRAGMENT_SHADER), + GB_CONSTANT("INFO_LOG_LENGTH", "i", GL_INFO_LOG_LENGTH), + GB_CONSTANT("LINK_STATUS", "i", GL_LINK_STATUS), + GB_CONSTANT("SHADER_SOURCE_LENGTH", "i", GL_SHADER_SOURCE_LENGTH), + GB_CONSTANT("SHADER_TYPE", "i", GL_SHADER_TYPE), + GB_CONSTANT("VALIDATE_STATUS", "i", GL_VALIDATE_STATUS), + GB_CONSTANT("VERTEX_SHADER", "i", GL_VERTEX_SHADER), + + GB_END_DECLARE +}; diff --git a/gb.opengl/src/glsl/GL.h b/gb.opengl/src/glsl/GL.h new file mode 100644 index 00000000..0d057e40 --- /dev/null +++ b/gb.opengl/src/glsl/GL.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + GL.h + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GL_H +#define __GL_H + +#include "main.h" + +#include + +#ifndef __GL_C +extern GB_DESC Cgl[]; +#endif /* __GL_C */ + +#endif /* __GL_H */ diff --git a/gb.opengl/src/glsl/GLattributes.c b/gb.opengl/src/glsl/GLattributes.c new file mode 100644 index 00000000..891776fb --- /dev/null +++ b/gb.opengl/src/glsl/GLattributes.c @@ -0,0 +1,175 @@ +/*************************************************************************** + + GLattributes.c + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLATTRIBUTES_C + +#include "GL.h" + + + + + +/*GLAPI void APIENTRY glVertexAttrib4Niv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4Nuiv (GLuint index, const GLuint *v); + + +GLAPI void APIENTRY glVertexAttrib4iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4uiv (GLuint index, const GLuint *v); +//GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer); + + + + GB_STATIC_METHOD("glVertexAttrib4Niv", NULL, GLVERTEXATTRIB4NIV, "(Index)i(V)Integer[]"), + GB_STATIC_METHOD("glVertexAttrib4Nuiv", NULL, GLVERTEXATTRIB4NUIV, "(Index)i(V)Integer[]"), + + GB_STATIC_METHOD("glVertexAttrib4iv", NULL, GLVERTEXATTRIB4IV, "(Index)i(V)Integer[]"), + GB_STATIC_METHOD("glVertexAttrib4uiv", NULL, GLVERTEXATTRIB4UIV, "(Index)i(V)Integer[]"), + //GB_STATIC_METHOD("glVertexAttribPointer", NULL, GLVERTEXATTRIBPOINTER, "(Index)i(Size)i(Type)i(Normalized)b(Stride)i(Pointer)p"),*/ + +BEGIN_METHOD(GLBINDATTRIBLOCATION, GB_INTEGER program; GB_INTEGER index; GB_STRING name) + + glBindAttribLocation (VARG(program), VARG(index), GB.ToZeroString(ARG(name))); + +END_METHOD + +BEGIN_METHOD(GLVERTEXATTRIB1F, GB_INTEGER index; GB_FLOAT x) + + glVertexAttrib1d(VARG(index), VARG(x)); + +END_METHOD + +BEGIN_METHOD(GLVERTEXATTRIB2F, GB_INTEGER index; GB_FLOAT x; GB_FLOAT y) + + glVertexAttrib2d(VARG(index), VARG(x), VARG(y)); + +END_METHOD + +BEGIN_METHOD(GLVERTEXATTRIB3F, GB_INTEGER index; GB_FLOAT x; GB_FLOAT y; GB_FLOAT z) + + glVertexAttrib3d(VARG(index), VARG(x), VARG(y), VARG(z)); + +END_METHOD + +BEGIN_METHOD(GLVERTEXATTRIB4F, GB_INTEGER index; GB_FLOAT x; GB_FLOAT y; GB_FLOAT z; GB_FLOAT w) + + glVertexAttrib4d(VARG(index), VARG(x), VARG(y), VARG(z), VARG(w)); + +END_METHOD + + + +BEGIN_METHOD(GLVERTEXATTRIB1FV, GB_INTEGER index; GB_OBJECT v) + + GB_ARRAY fArray = VARG(v); + int count = GB.Array.Count(fArray); + + if (!count) + return; + + GLdouble values[count]; + int i; + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLATTRIBUTES_H +#define __GLATTRIBUTES_H + +#include "main.h" +DECLARE_METHOD(GLBINDATTRIBLOCATION); +DECLARE_METHOD(GLVERTEXATTRIB1F); +DECLARE_METHOD(GLVERTEXATTRIB1FV); +DECLARE_METHOD(GLVERTEXATTRIB2F); +DECLARE_METHOD(GLVERTEXATTRIB2FV); +DECLARE_METHOD(GLVERTEXATTRIB3F); +DECLARE_METHOD(GLVERTEXATTRIB3FV); +DECLARE_METHOD(GLVERTEXATTRIB4F); +DECLARE_METHOD(GLVERTEXATTRIB4FV); + +#endif /* __GLATTRIBUTES_H */ diff --git a/gb.opengl/src/glsl/GLinfo.h b/gb.opengl/src/glsl/GLinfo.h new file mode 100644 index 00000000..ed736acc --- /dev/null +++ b/gb.opengl/src/glsl/GLinfo.h @@ -0,0 +1,31 @@ +/*************************************************************************** + + GLinfo.h + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLINFO_H +#define __GLINFO_H + +#include "main.h" + +int CheckSize(GLanum value); + +#endif /* __GLINFO_H */ diff --git a/gb.opengl/src/glsl/GLprogram.c b/gb.opengl/src/glsl/GLprogram.c new file mode 100644 index 00000000..53359b5c --- /dev/null +++ b/gb.opengl/src/glsl/GLprogram.c @@ -0,0 +1,98 @@ +/*************************************************************************** + + GLprogram.c + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLPROGRAM_C + +#include "GL.h" + +BEGIN_METHOD_VOID(GLCREATEPROGRAM) + + GLuint prog; + prog = glCreateProgram(); + GB.ReturnInteger(prog); + +END_METHOD + +BEGIN_METHOD(GLDELETEPROGRAM, GB_INTEGER program) + + glDeleteProgram(VARG(program)); + +END_METHOD + +BEGIN_METHOD(GLGETPROGRAMINFOLOG, GB_INTEGER program) + + GLint length; + + glGetProgramiv(VARG(program), GL_INFO_LOG_LENGTH, &length); + + if (!length) + { + GB.ReturnVoidString(); + return; + } + else + { + GLchar log[length]; + + glGetProgramInfoLog(VARG(program), length, NULL, log); + GB.ReturnNewZeroString((const char *)log); + } + +END_METHOD + +BEGIN_METHOD(GLGETPROGRAMIV, GB_INTEGER program; GB_INTEGER pname) + + GLint value; + GB_ARRAY iArray; + + glGetProgramiv(VARG(program), VARG(pname), &value); + + GB.Array.New(&iArray , GB_T_INTEGER , 1); + *((int *)GB.Array.Get(iArray, 0)) = value; + GB.ReturnObject(iArray); + +END_METHOD + +BEGIN_METHOD(GLISPROGRAM, GB_INTEGER program) + + GB.ReturnBoolean(glIsProgram(VARG(program))); + +END_METHOD + +BEGIN_METHOD(GLLINKPROGRAM, GB_INTEGER program) + + glLinkProgram(VARG(program)); + +END_METHOD + +BEGIN_METHOD(GLUSEPROGRAM, GB_INTEGER program) + + glUseProgram(VARG(program)); + +END_METHOD + +BEGIN_METHOD(GLVALIDATEPROGRAM, GB_INTEGER program) + + glValidateProgram(VARG(program)); + +END_METHOD diff --git a/gb.opengl/src/glsl/GLprogram.h b/gb.opengl/src/glsl/GLprogram.h new file mode 100644 index 00000000..ea072d50 --- /dev/null +++ b/gb.opengl/src/glsl/GLprogram.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + GLprogram.h + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLPROGRAM_H +#define __GLPROGRAM_H + +#include "main.h" + +DECLARE_METHOD(GLCREATEPROGRAM); +DECLARE_METHOD(GLDELETEPROGRAM); +DECLARE_METHOD(GLGETPROGRAMINFOLOG); +DECLARE_METHOD(GLGETPROGRAMIV); +DECLARE_METHOD(GLISPROGRAM); +DECLARE_METHOD(GLLINKPROGRAM); +DECLARE_METHOD(GLUSEPROGRAM); +DECLARE_METHOD(GLVALIDATEPROGRAM); + +#endif /* __GLPROGRAM_H */ diff --git a/gb.opengl/src/glsl/GLshader.c b/gb.opengl/src/glsl/GLshader.c new file mode 100644 index 00000000..13053a30 --- /dev/null +++ b/gb.opengl/src/glsl/GLshader.c @@ -0,0 +1,154 @@ +/*************************************************************************** + + GLshader.c + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLSHADER_C + +#include "GL.h" + +BEGIN_METHOD(GLATTACHSHADER, GB_INTEGER program; GB_INTEGER shader) + + glAttachShader(VARG(program), VARG(shader)); + +END_METHOD + +BEGIN_METHOD(GLCOMPILESHADER, GB_INTEGER shader) + + glCompileShader(VARG(shader)); + +END_METHOD + +BEGIN_METHOD(GLCREATESHADER, GB_INTEGER type) + + GLuint shader; + shader = glCreateShader(VARG(type)); + GB.ReturnInteger(shader); + +END_METHOD + +BEGIN_METHOD(GLDELETESHADER, GB_INTEGER shader) + + glDeleteShader(VARG(shader)); + +END_METHOD + +BEGIN_METHOD(GLDETACHSHADER, GB_INTEGER program; GB_INTEGER shader) + + glDetachShader(VARG(program), VARG(shader)); + +END_METHOD + +BEGIN_METHOD(GLGETATTACHEDSHADERS, GB_INTEGER program) + + GLint count; + GB_ARRAY iArray; + + glGetProgramiv(VARG(program), GL_ATTACHED_SHADERS, &count); + + if (!count) + { + GB.ReturnNull(); + return; + } + else + { + GLuint params[count]; + int i; + + GB.Array.New(&iArray , GB_T_INTEGER , count); + glGetAttachedShaders(VARG(program), count, NULL, params); + + for (i=0;i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLSHADER_H +#define __GLSHADER_H + +#include "main.h" + +DECLARE_METHOD(GLATTACHSHADER); +DECLARE_METHOD(GLCOMPILESHADER); +DECLARE_METHOD(GLCREATESHADER); +DECLARE_METHOD(GLDELETESHADER); +DECLARE_METHOD(GLDETACHSHADER); +DECLARE_METHOD(GLGETATTACHEDSHADERS); +DECLARE_METHOD(GLGETSHADERINFOLOG); +DECLARE_METHOD(GLGETSHADERIV); +DECLARE_METHOD(GLGETSHADERSOURCE); +DECLARE_METHOD(GLISSHADER); +DECLARE_METHOD(GLSHADERSOURCE); + +#endif /* __GLSHADER_H */ diff --git a/gb.opengl/src/glsl/GLuniform.c b/gb.opengl/src/glsl/GLuniform.c new file mode 100644 index 00000000..6ade97c1 --- /dev/null +++ b/gb.opengl/src/glsl/GLuniform.c @@ -0,0 +1,519 @@ +/*************************************************************************** + + GLuniform.c + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLPROGRAM_C + +#include "GL.h" + +BEGIN_METHOD(GLGETUNIFORMLOCATION, GB_INTEGER program; GB_STRING name) + + if (!LENGTH(name)) + { + GB.ReturnInteger(0); + return; + } + + GB.ReturnInteger(glGetUniformLocation(VARG(program), GB.ToZeroString(ARG(name)))); + +END_METHOD + +BEGIN_METHOD(GLUNIFORM1F, GB_INTEGER location; GB_FLOAT v0) + + glUniform1f(VARG(location), VARG(v0)); + +END_METHOD + +BEGIN_METHOD(GLUNIFORM2F, GB_INTEGER location; GB_FLOAT v0; GB_FLOAT v1) + + glUniform2f(VARG(location), VARG(v0), VARG(v1)); + +END_METHOD + +BEGIN_METHOD(GLUNIFORM3F, GB_INTEGER location; GB_FLOAT v0; GB_FLOAT v1; GB_FLOAT v2) + + glUniform3f(VARG(location), VARG(v0), VARG(v1), VARG(v2)); + +END_METHOD + +BEGIN_METHOD(GLUNIFORM4F, GB_INTEGER location; GB_FLOAT v0; GB_FLOAT v1; GB_FLOAT v2; GB_FLOAT v3) + + glUniform4f(VARG(location), VARG(v0), VARG(v1), VARG(v2), VARG(v3)); + +END_METHOD + +BEGIN_METHOD(GLUNIFORM1I, GB_INTEGER location; GB_INTEGER v0) + + glUniform1i(VARG(location), VARG(v0)); + +END_METHOD + +BEGIN_METHOD(GLUNIFORM2I, GB_INTEGER location; GB_INTEGER v0; GB_INTEGER v1) + + glUniform2i(VARG(location), VARG(v0), VARG(v1)); + +END_METHOD + +BEGIN_METHOD(GLUNIFORM3I, GB_INTEGER location; GB_INTEGER v0; GB_INTEGER v1; GB_INTEGER v2) + + glUniform3i(VARG(location), VARG(v0), VARG(v1), VARG(v2)); + +END_METHOD + +BEGIN_METHOD(GLUNIFORM4I, GB_INTEGER location; GB_INTEGER v0; GB_INTEGER v1; GB_INTEGER v2; GB_INTEGER v3) + + glUniform4i(VARG(location), VARG(v0), VARG(v1), VARG(v2), VARG(v3)); + +END_METHOD + +BEGIN_METHOD(GLUNIFORM1FV, GB_INTEGER location; GB_OBJECT array) + + GB_ARRAY fArray = VARG(array); + int count = GB.Array.Count(fArray); + + if (!count) + return; + + GLfloat values[count]; + int i; + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLUNIFORM_H +#define __GLUNIFORM_H + +#include "main.h" + +DECLARE_METHOD(GLGETUNIFORMLOCATION); +DECLARE_METHOD(GLUNIFORM1F); +DECLARE_METHOD(GLUNIFORM2F); +DECLARE_METHOD(GLUNIFORM3F); +DECLARE_METHOD(GLUNIFORM4F); +DECLARE_METHOD(GLUNIFORM1I); +DECLARE_METHOD(GLUNIFORM2I); +DECLARE_METHOD(GLUNIFORM3I); +DECLARE_METHOD(GLUNIFORM4I); +DECLARE_METHOD(GLUNIFORM1FV); +DECLARE_METHOD(GLUNIFORM2FV); +DECLARE_METHOD(GLUNIFORM3FV); +DECLARE_METHOD(GLUNIFORM4FV); +DECLARE_METHOD(GLUNIFORM1IV); +DECLARE_METHOD(GLUNIFORM2IV); +DECLARE_METHOD(GLUNIFORM3IV); +DECLARE_METHOD(GLUNIFORM4IV); +DECLARE_METHOD(GLUNIFORMMATRIX2FV); +DECLARE_METHOD(GLUNIFORMMATRIX3FV); +DECLARE_METHOD(GLUNIFORMMATRIX4FV); +DECLARE_METHOD(GLUNIFORMMATRIX2X3FV); +DECLARE_METHOD(GLUNIFORMMATRIX3X2FV); +DECLARE_METHOD(GLUNIFORMMATRIX2X4FV); +DECLARE_METHOD(GLUNIFORMMATRIX4X2FV); +DECLARE_METHOD(GLUNIFORMMATRIX3X4FV); +DECLARE_METHOD(GLUNIFORMMATRIX4X3FV); + +#endif /* __GLUNIFORM_H */ diff --git a/gb.opengl/src/glsl/Makefile.am b/gb.opengl/src/glsl/Makefile.am new file mode 100644 index 00000000..7a1ac085 --- /dev/null +++ b/gb.opengl/src/glsl/Makefile.am @@ -0,0 +1,17 @@ +COMPONENT = gb.opengl.glsl +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.opengl.glsl.la + +gb_opengl_glsl_la_LIBADD = @OPENGL_LIB@ +gb_opengl_glsl_la_LDFLAGS = -module @LD_FLAGS@ @OPENGL_LDFLAGS@ +gb_opengl_glsl_la_CPPFLAGS = @OPENGL_INC@ + +gb_opengl_glsl_la_SOURCES = \ + main.h main.c \ + GLprogram.h GLprogram.c \ + GLshader.h GLshader.c \ + GLuniform.h GLuniform.c \ + GLattributes.h GLattributes.c \ + GL.h GL.c + diff --git a/gb.opengl/src/glsl/gb.opengl.glsl.component b/gb.opengl/src/glsl/gb.opengl.glsl.component new file mode 100644 index 00000000..cc9cdb98 --- /dev/null +++ b/gb.opengl/src/glsl/gb.opengl.glsl.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.opengl.glsl +Author=Laurent Carlier +Need=OpenGLViewer +Require=gb.opengl +State=Stable diff --git a/gb.opengl/src/glsl/main.c b/gb.opengl/src/glsl/main.c new file mode 100644 index 00000000..9cda454a --- /dev/null +++ b/gb.opengl/src/glsl/main.c @@ -0,0 +1,48 @@ +/*************************************************************************** + + main.c + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gambas.h" +#include "main.h" + +#include "GL.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ +/* GL */ + Cgl, + + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.opengl/src/glsl/main.h b/gb.opengl/src/glsl/main.h new file mode 100644 index 00000000..ed695ef2 --- /dev/null +++ b/gb.opengl/src/glsl/main.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + main.h + + (c) 2009 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#ifndef WARNING +#define WARNING(c) printf("WARNING: " c) +#endif /* WARNING */ + +#endif + diff --git a/gb.opengl/src/glu/GLU.c b/gb.opengl/src/glu/GLU.c new file mode 100644 index 00000000..4e36d931 --- /dev/null +++ b/gb.opengl/src/glu/GLU.c @@ -0,0 +1,151 @@ +/*************************************************************************** + + GLU.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLU_C + +#include "gb.image.h" + +#include "GLU.h" + +#include "GLUcoordTransf.h" +#include "GLUtextureImage.h" +#include "GLUquadratic.h" +#include "GLUnurb.h" +#include "GLUproject.h" + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Glu_Color, GB_INTEGER color) + + int r, g, b, a; + + GB_COLOR_SPLIT(VARG(color), r, g, b, a); + //fprintf(stderr, "Glu_Color: %d %d %d %d\n", r, g, b, a); + if (a == 0) + glColor3d(r / 255.0, g / 255.0, b / 255.0); + else + glColor4d(r / 255.0, g / 255.0, b / 255.0, a / 255.0); + +END_METHOD + +BEGIN_METHOD(Glu_ClearColor, GB_INTEGER color) + + int r, g, b, a; + + GB_COLOR_SPLIT(VARG(color), r, g, b, a); + glClearColor(r / 255.0, g / 255.0, b / 255.0, a / 255.0); + +END_METHOD + +BEGIN_METHOD(GLUERRORSTRING, GB_INTEGER code) + + const GLubyte *errStr = gluErrorString(VARG(code)); + GB.ReturnNewZeroString((char *) errStr); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC Cglu[] = +{ + GB_DECLARE("Glu",0), GB_NOT_CREATABLE(), + + /* Get error string */ + GB_STATIC_METHOD("ErrorString", "s", GLUERRORSTRING, "(ErrorCode)i"), + + /* Coordinate Transformation - see GLUcoordTransf.h */ + GB_STATIC_METHOD("LookAt", NULL, GLULOOKAT, "(EyeX)f(EyeY)f(EyeZ)f(CenterX)f(CenterY)f(CenterZ)f(UpX)f(UpY)f(UpZ)f"), + GB_STATIC_METHOD("Ortho2D", NULL, GLUORTHO2D, "(Left)f(Right)f(Bottom)f(Top)f"), + GB_STATIC_METHOD("Perspective", NULL, GLUPERSPECTIVE, "(Fovy)f(Aspect)f(ZNear)f(ZFar)f"), + GB_STATIC_METHOD("PickMatrix", NULL, GLUPICKMATRIX, "(X)f(Y)f(DelX)f(DelY)f(Viewport)Integer[]"), + + /* Texture Image - see GLUtextureImage.h */ + GB_STATIC_METHOD("Build1DMipmaps", "i", GLUBUILD1DMIPMAPS, "(Image)Image;"), + GB_STATIC_METHOD("Build2DMipmaps", "i", GLUBUILD2DMIPMAPS, "(Image)Image;"), + + /* Quadratics - see GLUquadratic.h */ + GB_STATIC_METHOD("NewQuadric", "GluQuadric", GLUNEWQUADRIC, NULL), + GB_STATIC_METHOD("QuadricNormals", NULL, GLUQUADRICNORMALS, "(Quad)GluQuadric;(Normal)i"), + GB_STATIC_METHOD("QuadricTexture", NULL, GLUQUADRICTEXTURE, "(Quad)GluQuadric;(Texture)b"), + GB_STATIC_METHOD("QuadricOrientation", NULL, GLUQUADRICORIENTATION, "(Quad)GluQuadric;(Orientation)i"), + GB_STATIC_METHOD("QuadricDrawStyle", NULL, GLUQUADRICDRAWSTYLE, "(Quad)GluQuadric;(DrawStyle)i"), + GB_STATIC_METHOD("Sphere", NULL, GLUSPHERE, "(Quad)GluQuadric;(Radius)f(Slices)i(Stacks)i"), + GB_STATIC_METHOD("Cylinder", NULL, GLUCYLINDER, "(Quad)GluQuadric;(Base)f(Top)f(Height)f(Slices)i(Stacks)i"), + GB_STATIC_METHOD("Disk", NULL, GLUDISK, "(Quad)GluQuadric;(Inner)f(Outer)f(Slices)i(Loops)i"), + GB_STATIC_METHOD("PartialDisk", NULL, GLUPARTIALDISK, "(Quad)GluQuadric;(Inner)f(Outer)f(Slices)i(Loops)i(Start)f(Sweep)f"), + + /* NURBS - SEE GLUnurbs.h */ + GB_STATIC_METHOD("BeginCurve", NULL, GLUBEGINCURVE, "(Nurb)GluNurb"), + GB_STATIC_METHOD("BeginSurface", NULL, GLUBEGINSURFACE, "(Nurb)GluNurb"), + GB_STATIC_METHOD("BeginTrim", NULL, GLUBEGINTRIM, "(Nurb)GluNurb"), + GB_STATIC_METHOD("DeleteNurbsRenderer", NULL, GLUDELETENURBSRENDERER, "(Nurb)GluNurb"), + GB_STATIC_METHOD("EndCurve", NULL, GLUENDCURVE, "(Nurb)GluNurb"), + GB_STATIC_METHOD("EndSurface", NULL, GLUENDSURFACE, "(Nurb)GluNurb"), + GB_STATIC_METHOD("EndTrim", NULL, GLUENDTRIM, "(Nurb)GluNurb"), + GB_STATIC_METHOD("NurbsCurve", NULL, GLUNURBSCURVE, "(Nurb)GluNurb;(KnotCount)i(Knots)Single[];(Stride)i(Control)Single[];(Order)i(Type)i"), + GB_STATIC_METHOD("NurbsProperty", NULL, GLUNURBSPROPERTY, "(Nurb)GluNurb;(Property)i(Value)f"), + GB_STATIC_METHOD("NurbsSurface", NULL, GLUNURBSSURFACE, "(Nurb)GluNurb;(SKnotCount)i(SKnots)Single[];(TKnotCount)i(TKnots)Single[];(SStride)i(TStride)i(SOrder)i(TOrder)i(Type)i(Control)Single[]"), + GB_STATIC_METHOD("NewNurbsRenderer","GluNurb", GLUNEWNURBSRENDERER, NULL), + + /* Projections - see GLUproject.h */ + GB_STATIC_METHOD("Project", "Float[]", GLUPROJECT, "(ObjectX)f(ObjectY)f(ObjectZ)f(Modelview)Float[];(Projection)Float[];(Viewport)Integer[];"), + GB_STATIC_METHOD("UnProject", "Float[]", GLUUNPROJECT, "(WindowX)f(WindowY)f(WindowZ)f(Modelview)Float[];(Projection)Float[];(Viewport)Integer[];"), + GB_STATIC_METHOD("UnProject4", "Float[]", GLUUNPROJECT4, "(WindowX)f(WindowY)f(WindowZ)f(ClipW)f(Modelview)Float[];(Projection)Float[];(Viewport)Integer[];(NearValue)f(FarValue)f"), + + /* Setting a Gambas color */ + GB_STATIC_METHOD("ClearColor", NULL, Glu_ClearColor, "(Color)i"), + GB_STATIC_METHOD("Color", NULL, Glu_Color, "(Color)i"), + + /********************/ + /* opengl constants */ + /********************/ + + /* Errors */ + GB_CONSTANT("INVALID_ENUM", "i", GLU_INVALID_ENUM), + GB_CONSTANT("INVALID_VALUE", "i", GLU_INVALID_OPERATION), + GB_CONSTANT("OUT_OF_MEMORY", "i", GLU_OUT_OF_MEMORY), + + GB_CONSTANT("NONE", "i", GLU_NONE), + GB_CONSTANT("FLAT", "i", GLU_FLAT), + GB_CONSTANT("SMOOTH", "i", GLU_SMOOTH), + + GB_CONSTANT("OUTSIDE", "i", GLU_OUTSIDE), + GB_CONSTANT("INSIDE", "i", GLU_INSIDE), + + GB_CONSTANT("FILL", "i", GLU_FILL), + GB_CONSTANT("LINE", "i", GLU_LINE), + GB_CONSTANT("POINT", "i", GLU_POINT), + GB_CONSTANT("SILHOUETTE", "i", GLU_SILHOUETTE), + + GB_CONSTANT("CULLING", "i", GLU_CULLING), + GB_CONSTANT("SAMPLING_TOLERANCE", "i", GLU_SAMPLING_TOLERANCE), + GB_CONSTANT("SAMPLING_METHOD", "i", GLU_SAMPLING_METHOD), + GB_CONSTANT("PARAMETRIC_TOLERANCE", "i", GLU_PARAMETRIC_TOLERANCE), + GB_CONSTANT("DISPLAY_MODE", "i", GLU_DISPLAY_MODE), + GB_CONSTANT("AUTO_LOAD_MATRIX", "i", GLU_AUTO_LOAD_MATRIX), + GB_CONSTANT("U_STEP", "i", GLU_U_STEP), + GB_CONSTANT("V_STEP", "i", GLU_V_STEP), + GB_CONSTANT("NURBS_MODE", "i", GLU_NURBS_MODE), + + GB_END_DECLARE +}; diff --git a/gb.opengl/src/glu/GLU.h b/gb.opengl/src/glu/GLU.h new file mode 100644 index 00000000..f68ddd2c --- /dev/null +++ b/gb.opengl/src/glu/GLU.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + GLU.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLU_H +#define __GLU_H + +#include "main.h" + +#ifndef __GLU_C +extern GB_DESC Cglu[]; +#endif /* __GLU_C */ + +#endif /* __GLU_H */ diff --git a/gb.opengl/src/glu/GLUcoordTransf.c b/gb.opengl/src/glu/GLUcoordTransf.c new file mode 100644 index 00000000..eec2ec03 --- /dev/null +++ b/gb.opengl/src/glu/GLUcoordTransf.c @@ -0,0 +1,61 @@ +/*************************************************************************** + + GLUcoordTransf.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLUCOORDTRANSF_C + +#include "GLU.h" + +/**************************************************************************/ + +BEGIN_METHOD(GLULOOKAT, GB_FLOAT eyex; GB_FLOAT eyey; GB_FLOAT eyez; GB_FLOAT centerx; GB_FLOAT centery; GB_FLOAT centerz; GB_FLOAT upx; GB_FLOAT upy; GB_FLOAT upz) + + gluLookAt(VARG(eyex),VARG(eyey),VARG(eyez),VARG(centerx),VARG(centery),VARG(centerz),VARG(upx),VARG(upy),VARG(upz)); + +END_METHOD + +BEGIN_METHOD(GLUORTHO2D, GB_FLOAT left; GB_FLOAT right; GB_FLOAT bottom; GB_FLOAT top) + + gluOrtho2D(VARG(left),VARG(right),VARG(bottom),VARG(top)); + +END_METHOD + +BEGIN_METHOD(GLUPERSPECTIVE, GB_FLOAT fovy; GB_FLOAT aspect; GB_FLOAT znear; GB_FLOAT zfar) + + gluPerspective(VARG(fovy),VARG(aspect),VARG(znear),VARG(zfar)); + +END_METHOD + +BEGIN_METHOD(GLUPICKMATRIX, GB_FLOAT x; GB_FLOAT y; GB_FLOAT width; GB_FLOAT height; GB_OBJECT viewport) + + GLint iparams[4]; + GB_ARRAY viewport = (GB_ARRAY) VARG(viewport); + int i, count = GB.Array.Count(viewport); + + count = (count > 4 ? 4 : count); + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLUCOORDTRANSF_H +#define __GLUCOORDTRANSF_H + +#include "main.h" + +DECLARE_METHOD(GLULOOKAT); +DECLARE_METHOD(GLUORTHO2D); +DECLARE_METHOD(GLUPERSPECTIVE); +DECLARE_METHOD(GLUPICKMATRIX); + +#endif /* __GLUCOORDTRANSF_H */ diff --git a/gb.opengl/src/glu/GLUnurb.c b/gb.opengl/src/glu/GLUnurb.c new file mode 100644 index 00000000..9c22984c --- /dev/null +++ b/gb.opengl/src/glu/GLUnurb.c @@ -0,0 +1,153 @@ +/*************************************************************************** + + GLUnurb.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLUNURB_C + +#include "cglunurb.h" +#include "GLU.h" + +static GLUnurbsObj *get_nurb(void *ob) +{ + if (GB.CheckObject(ob)) + return NULL; + else + return ((CGLUNURB *)ob)->nurb; +} + +#define GET_NURB() \ + GLUnurbsObj *thenurb = get_nurb(VARG(nurb)); \ + if (!thenurb) return; + +BEGIN_METHOD(GLUBEGINCURVE, GB_OBJECT nurb) + + GET_NURB(); + gluBeginCurve(thenurb); + +END_METHOD + +BEGIN_METHOD(GLUBEGINSURFACE, GB_OBJECT nurb) + + GET_NURB(); + gluBeginSurface(thenurb); + +END_METHOD + +BEGIN_METHOD(GLUBEGINTRIM, GB_OBJECT nurb) + + GET_NURB(); + gluBeginTrim(thenurb); + +END_METHOD + +BEGIN_METHOD(GLUDELETENURBSRENDERER, GB_OBJECT nurb) + + GET_NURB(); + gluDeleteNurbsRenderer(thenurb); + // Make the nurb Gambas object invalid + ((CGLUNURB *)VARG(nurb))->nurb = NULL; + +END_METHOD + +BEGIN_METHOD(GLUENDCURVE, GB_OBJECT nurb) + + GET_NURB(); + gluEndCurve(thenurb); + +END_METHOD + +BEGIN_METHOD(GLUENDSURFACE, GB_OBJECT nurb) + + GET_NURB(); + gluEndSurface(thenurb); + +END_METHOD + +BEGIN_METHOD(GLUENDTRIM, GB_OBJECT nurb) + + GET_NURB(); + gluEndTrim(thenurb); + +END_METHOD + +BEGIN_METHOD(GLUNURBSCURVE, GB_OBJECT nurb; GB_INTEGER knotCount; GB_OBJECT knots; GB_INTEGER stride; GB_OBJECT control; GB_INTEGER order; GB_INTEGER type) + + GET_NURB(); + + GB_ARRAY knot = (GB_ARRAY) VARG(knots); + GB_ARRAY controll = (GB_ARRAY) VARG(control); + int i; + int count1 = GB.Array.Count(knot); + int count2 = GB.Array.Count(controll); + GLfloat param1[count1], param2[count2]; + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLUNURB_H +#define __GLUNURB_H + +#include "main.h" +DECLARE_METHOD(GLUBEGINCURVE); +DECLARE_METHOD(GLUBEGINSURFACE); +DECLARE_METHOD(GLUBEGINTRIM); +DECLARE_METHOD(GLUDELETENURBSRENDERER); +DECLARE_METHOD(GLUENDCURVE); +DECLARE_METHOD(GLUENDSURFACE); +DECLARE_METHOD(GLUENDTRIM); +/*skip ***DECLARE_METHOD(GLUGETNURBSPROPERTY); +skip ***DECLARE_METHOD(GLULOADSAMPLINGMATRICES); +skip ***DECLARE_METHOD(GLUNURBSCALLBACK); +skip ***DECLARE_METHOD(GLUNURBSCALLBACKDATA); +skip ***DECLARE_METHOD(GLUNURBSCALLBACKDATAEXT);*/ +DECLARE_METHOD(GLUNURBSCURVE); +DECLARE_METHOD(GLUNURBSPROPERTY); +DECLARE_METHOD(GLUNURBSSURFACE); +DECLARE_METHOD(GLUNEWNURBSRENDERER); +//DECLARE_METHOD(GLUPWLCURVE); + + +#endif /* __GLUNURB_H */ diff --git a/gb.opengl/src/glu/GLUproject.c b/gb.opengl/src/glu/GLUproject.c new file mode 100644 index 00000000..fa206ff0 --- /dev/null +++ b/gb.opengl/src/glu/GLUproject.c @@ -0,0 +1,138 @@ +/*************************************************************************** + + GLUproject.c + + (c) 2005-2012 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLUPROJECT_C + +#include "GLU.h" + +BEGIN_METHOD(GLUPROJECT, GB_FLOAT ObjX; GB_FLOAT ObjY; GB_FLOAT ObjZ; GB_OBJECT Model; GB_OBJECT Proj; GB_OBJECT View) + + GLdouble model[16], proj[16], win[3]; + GLint view[4]; + int i, status; + GB_ARRAY fArray; + + if ((GB.Array.Count(VARG(Model)) != 16) || (GB.Array.Count(VARG(Proj)) != 16) || (GB.Array.Count(VARG(View)) != 4)) + { + GB.ReturnNull(); + return; + } + + for (i=0; i<16; i++) + model[i] = *((GLdouble *)GB.Array.Get(VARG(Model),i)); + for (i=0; i<16; i++) + proj[i] = *((GLdouble *)GB.Array.Get(VARG(Proj),i)); + for (i=0; i<4; i++) + view[i] = *((GLint *)GB.Array.Get(VARG(View),i)); + + status = gluProject(VARG(ObjX), VARG(ObjY), VARG(ObjZ), model, proj, view, &win[0], &win[1], &win[2]); + + if (status == GLU_FALSE) + { + GB.ReturnNull(); + return; + } + + GB.Array.New(&fArray , GB_T_FLOAT , 3); + + for (i=0; i<3; i++) + *((GLdouble *)GB.Array.Get(fArray, i)) = win[i]; + + GB.ReturnObject(fArray); + +END_METHOD + +BEGIN_METHOD(GLUUNPROJECT, GB_FLOAT WinX; GB_FLOAT WinY; GB_FLOAT WinZ; GB_OBJECT Model; GB_OBJECT Proj; GB_OBJECT View) + + + GLdouble model[16], proj[16], obj[3]; + GLint view[4]; + int i, status; + GB_ARRAY fArray; + + if ((GB.Array.Count(VARG(Model)) != 16) || (GB.Array.Count(VARG(Proj)) != 16) || (GB.Array.Count(VARG(View)) != 4)) + { + GB.ReturnNull(); + return; + } + + for (i=0; i<16; i++) + model[i] = *((GLdouble *)GB.Array.Get(VARG(Model),i)); + for (i=0; i<16; i++) + proj[i] = *((GLdouble *)GB.Array.Get(VARG(Proj),i)); + for (i=0; i<4; i++) + view[i] = *((GLint *)GB.Array.Get(VARG(View),i)); + + status = gluUnProject(VARG(WinX), VARG(WinY), VARG(WinZ), model, proj, view, &obj[0], &obj[1], &obj[2]); + + if (status == GLU_FALSE) + { + GB.ReturnNull(); + return; + } + + GB.Array.New(&fArray , GB_T_FLOAT , 3); + + for (i=0; i<3; i++) + *((GLdouble *)GB.Array.Get(fArray, i)) = obj[i]; + + GB.ReturnObject(fArray); + +END_METHOD + +BEGIN_METHOD(GLUUNPROJECT4, GB_FLOAT WinX; GB_FLOAT WinY; GB_FLOAT WinZ; GB_FLOAT ClipW; GB_OBJECT Model; GB_OBJECT Proj; GB_OBJECT View; GB_FLOAT NearVal; GB_FLOAT FarVal) + + GLdouble model[16], proj[16], obj[4]; + GLint view[4]; + int i, status; + GB_ARRAY fArray; + + if ((GB.Array.Count(VARG(Model)) != 16) || (GB.Array.Count(VARG(Proj)) != 16) || (GB.Array.Count(VARG(View)) != 4)) + { + GB.ReturnNull(); + return; + } + + for (i=0; i<16; i++) + model[i] = *((GLdouble *)GB.Array.Get(VARG(Model),i)); + for (i=0; i<16; i++) + proj[i] = *((GLdouble *)GB.Array.Get(VARG(Proj),i)); + for (i=0; i<4; i++) + view[i] = *((GLint *)GB.Array.Get(VARG(View),i)); + + status = gluUnProject4(VARG(WinX), VARG(WinY), VARG(WinZ), VARG(ClipW), model, proj, view, VARG(NearVal), VARG(FarVal), &obj[0], &obj[1], &obj[2], &obj[3]); + + if (status == GLU_FALSE) + { + GB.ReturnNull(); + return; + } + + GB.Array.New(&fArray , GB_T_FLOAT , 4); + + for (i=0; i<4; i++) + *((GLdouble *)GB.Array.Get(fArray, i)) = obj[i]; + + GB.ReturnObject(fArray); + +END_METHOD diff --git a/gb.opengl/src/glu/GLUproject.h b/gb.opengl/src/glu/GLUproject.h new file mode 100644 index 00000000..39d4e78d --- /dev/null +++ b/gb.opengl/src/glu/GLUproject.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + GLUproject.h + + (c) 2005-2012 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLUPROJECT_H +#define __GLUPROJECT_H + +#include "main.h" + +DECLARE_METHOD(GLUPROJECT); +DECLARE_METHOD(GLUUNPROJECT); +DECLARE_METHOD(GLUUNPROJECT4); + +#endif /* __GLUPROJECT_H */ + diff --git a/gb.opengl/src/glu/GLUquadratic.c b/gb.opengl/src/glu/GLUquadratic.c new file mode 100644 index 00000000..bbac3088 --- /dev/null +++ b/gb.opengl/src/glu/GLUquadratic.c @@ -0,0 +1,112 @@ +/*************************************************************************** + + GLUquadratic.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLUQUADRATIC_C + +#include "GLU.h" +#include "cgluquadric.h" + +static GLUquadric *get_quadric(void *ob) +{ + if (GB.CheckObject(ob)) + return NULL; + else + return ((CGLUQUADRIC *)ob)->quadric; +} + +#define GET_QUADRIC() \ + GLUquadric *quad = get_quadric(VARG(Quad)); \ + if (!quad) return; + +BEGIN_METHOD_VOID(GLUNEWQUADRIC) + + GB.ReturnObject(CGLUQUADRIC_create()); + +END_METHOD + +BEGIN_METHOD(GLUQUADRICNORMALS, GB_OBJECT Quad; GB_INTEGER Normal) + + GET_QUADRIC(); + gluQuadricNormals(quad, VARG(Normal)); + +END_METHOD + +BEGIN_METHOD(GLUQUADRICORIENTATION, GB_OBJECT Quad; GB_INTEGER Orientation) + + GET_QUADRIC(); + gluQuadricOrientation(quad, VARG(Orientation)); + +END_METHOD + +BEGIN_METHOD(GLUQUADRICDRAWSTYLE, GB_OBJECT Quad; GB_INTEGER DrawStyle) + + GET_QUADRIC(); + gluQuadricDrawStyle(quad, VARG(DrawStyle)); + +END_METHOD + +BEGIN_METHOD(GLUQUADRICTEXTURE, GB_OBJECT Quad; GB_BOOLEAN Texture) + + GET_QUADRIC(); + gluQuadricTexture(quad, VARG(Texture)); + +END_METHOD + +BEGIN_METHOD(GLUDELETEQUADRIC, GB_OBJECT Quad) + + GET_QUADRIC(); + gluDeleteQuadric(quad); + // Make the quadric Gambas object invalid + ((CGLUQUADRIC *)VARG(Quad))->quadric = NULL; + +END_METHOD + +BEGIN_METHOD(GLUSPHERE, GB_OBJECT Quad; GB_FLOAT Radius; GB_INTEGER Slices; GB_INTEGER Stacks) + + GET_QUADRIC(); + gluSphere(quad, VARG(Radius), VARG(Slices), VARG(Stacks)); + +END_METHOD + +BEGIN_METHOD(GLUCYLINDER, GB_OBJECT Quad; GB_FLOAT Base; GB_FLOAT Top; GB_FLOAT Height; GB_INTEGER Slices; GB_INTEGER Stacks) + + GET_QUADRIC(); + gluCylinder(quad, VARG(Base), VARG(Top), VARG(Height), VARG(Slices), VARG(Stacks)); + +END_METHOD + +BEGIN_METHOD(GLUDISK, GB_OBJECT Quad; GB_FLOAT Inner; GB_FLOAT Outer; GB_INTEGER Slices; GB_INTEGER Loops) + + GET_QUADRIC(); + gluDisk(quad, VARG(Inner), VARG(Outer), VARG(Slices), VARG(Loops)); + +END_METHOD + +BEGIN_METHOD(GLUPARTIALDISK, GB_OBJECT Quad; GB_FLOAT Inner; GB_FLOAT Outer; GB_INTEGER Slices; GB_INTEGER Loops; GB_FLOAT Start; GB_FLOAT Sweep) + + GET_QUADRIC(); + gluPartialDisk(quad, VARG(Inner), VARG(Outer), VARG(Slices), VARG(Loops), VARG(Start), VARG(Sweep)); + +END_METHOD + + diff --git a/gb.opengl/src/glu/GLUquadratic.h b/gb.opengl/src/glu/GLUquadratic.h new file mode 100644 index 00000000..c6030602 --- /dev/null +++ b/gb.opengl/src/glu/GLUquadratic.h @@ -0,0 +1,40 @@ +/*************************************************************************** + + GLUquadratic.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLUQUADRATIC_H +#define __GLUQUADRATIC_H + +#include "main.h" +#include "cgluquadric.h" + +DECLARE_METHOD(GLUNEWQUADRIC); +DECLARE_METHOD(GLUQUADRICNORMALS); +DECLARE_METHOD(GLUQUADRICTEXTURE); +DECLARE_METHOD(GLUQUADRICORIENTATION); +DECLARE_METHOD(GLUQUADRICDRAWSTYLE); +DECLARE_METHOD(GLUSPHERE); +DECLARE_METHOD(GLUCYLINDER); +DECLARE_METHOD(GLUDISK); +DECLARE_METHOD(GLUPARTIALDISK); + +#endif /* __GLUQUADRATIC_H */ diff --git a/gb.opengl/src/glu/GLUtextureImage.c b/gb.opengl/src/glu/GLUtextureImage.c new file mode 100644 index 00000000..cf47fdd1 --- /dev/null +++ b/gb.opengl/src/glu/GLUtextureImage.c @@ -0,0 +1,53 @@ +/*************************************************************************** + + GLUtextureImage.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GLUTEXTUREIMAGE_C + +#include "GLU.h" + +BEGIN_METHOD(GLUBUILD1DMIPMAPS, GB_OBJECT Image) + + GB_IMG *image; + int format; + int status; + + if (IMAGE_get(ARG(Image), &image, &format)) + return; + + status = gluBuild1DMipmaps(GL_TEXTURE_1D, 4, image->width, format, GL_UNSIGNED_BYTE, image->data); + + GB.ReturnInteger(status); + +END_METHOD + +BEGIN_METHOD(GLUBUILD2DMIPMAPS, GB_OBJECT Image) + + GB_IMG *image = VARG(Image); + int status = 0; + + status = gluBuild2DMipmaps(GL_TEXTURE_2D, 4, image->width, image->height, IMAGE_get_pixel_format(image), GL_UNSIGNED_BYTE, + image->data); + + GB.ReturnInteger(status); + +END_METHOD diff --git a/gb.opengl/src/glu/GLUtextureImage.h b/gb.opengl/src/glu/GLUtextureImage.h new file mode 100644 index 00000000..d2554fbd --- /dev/null +++ b/gb.opengl/src/glu/GLUtextureImage.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + GLUtextureImage.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GLUTEXTUREIMAGE_H +#define __GLUTEXTUREIMAGE_H + +#include "main.h" + +DECLARE_METHOD(GLUBUILD1DMIPMAPS); +DECLARE_METHOD(GLUBUILD2DMIPMAPS); + +#endif /* __GLUTEXTUREIMAGE_H */ + diff --git a/gb.opengl/src/glu/Makefile.am b/gb.opengl/src/glu/Makefile.am new file mode 100644 index 00000000..41a824b7 --- /dev/null +++ b/gb.opengl/src/glu/Makefile.am @@ -0,0 +1,20 @@ +COMPONENT = gb.opengl.glu +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.opengl.glu.la + +gb_opengl_glu_la_LIBADD = @GLU_LIB@ +gb_opengl_glu_la_LDFLAGS = -module @LD_FLAGS@ @GLU_LDFLAGS@ +gb_opengl_glu_la_CPPFLAGS = @GLU_INC@ + +gb_opengl_glu_la_SOURCES = \ + main.h main.c \ + GLU.h GLU.c \ + GLUcoordTransf.h GLUcoordTransf.c \ + GLUtextureImage.h GLUtextureImage.c \ + GLUquadratic.h GLUquadratic.c \ + cgluquadric.h cgluquadric.c \ + cglunurb.h cglunurb.c\ + GLUnurb.h GLUnurb.c \ + GLUproject.h GLUproject.c + diff --git a/gb.opengl/src/glu/cglunurb.c b/gb.opengl/src/glu/cglunurb.c new file mode 100644 index 00000000..81f7e0bb --- /dev/null +++ b/gb.opengl/src/glu/cglunurb.c @@ -0,0 +1,74 @@ +/*************************************************************************** + + cglunurb.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CGLUNURB_C + +#include "gb_common.h" +#include "cglunurb.h" + +CGLUNURB *CGLUNURB_create() +{ + CGLUNURB *ob = GB.New(GB.FindClass("GluNurb"), NULL, NULL); + return ob; +} + +static int GluNurb_check(void *_object) +{ + return NURB == NULL; +} + + +BEGIN_METHOD_VOID(GluNurb_new) + + THIS->nurb = gluNewNurbsRenderer(); + +END_METHOD + +BEGIN_METHOD_VOID(GluNurb_free) + + if (NURB) + gluDeleteNurbsRenderer(NURB); + +END_METHOD + + +GB_DESC GluNurbsDesc[] = +{ + GB_DECLARE("GluNurb", sizeof(CGLUNURB)), + + GB_HOOK_CHECK(GluNurb_check), + + GB_METHOD("_new", NULL, GluNurb_new, NULL), + GB_METHOD("_free", NULL, GluNurb_free, NULL), + + GB_END_DECLARE +}; + +/*BEGIN_METHOD(CGLUQUADRIC_free, GB_OBJECT quadric) + + if (!quadric) + return; + + gluDeleteNurbsRenderer (quadric); + +END_METHOD*/ diff --git a/gb.opengl/src/glu/cglunurb.h b/gb.opengl/src/glu/cglunurb.h new file mode 100644 index 00000000..9862e913 --- /dev/null +++ b/gb.opengl/src/glu/cglunurb.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + cglunurb.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CGLUNURB_H +#define __CGLUNURB_H + +#include "gambas.h" +#include "main.h" +#include "GLU.h" + +#ifndef __CGLUNURB_C +extern GB_DESC GluNurbsDesc[]; +#else + +#define THIS OBJECT(CGLUNURB) +#define NURB (THIS->nurb) + +#endif + +typedef + struct { + GB_BASE ob; + GLUnurbs *nurb; + } + CGLUNURB; + +CGLUNURB *CGLUNURB_create(); + + +#endif diff --git a/gb.opengl/src/glu/cgluquadric.c b/gb.opengl/src/glu/cgluquadric.c new file mode 100644 index 00000000..be985afe --- /dev/null +++ b/gb.opengl/src/glu/cgluquadric.c @@ -0,0 +1,64 @@ +/*************************************************************************** + + cgluquadric.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CGLUQUADRIC_C + +#include "gb_common.h" +#include "cgluquadric.h" + +CGLUQUADRIC *CGLUQUADRIC_create() +{ + return GB.New(GB.FindClass("GluQuadric"), NULL, NULL); +}; + +static int GluQuadric_check(void *_object) +{ + return QUADRIC == NULL; +} + + +BEGIN_METHOD_VOID(GluQuadric_new) + + THIS->quadric = gluNewQuadric(); + +END_METHOD + +BEGIN_METHOD_VOID(GluQuadric_free) + + if (QUADRIC) + gluDeleteQuadric(QUADRIC); + +END_METHOD + + +GB_DESC GluQuadricDesc[] = +{ + GB_DECLARE("GluQuadric", sizeof(CGLUQUADRIC)), + GB_HOOK_CHECK(GluQuadric_check), + + GB_METHOD("_new", NULL, GluQuadric_new, NULL), + GB_METHOD("_free", NULL, GluQuadric_free, NULL), + + GB_END_DECLARE +}; + diff --git a/gb.opengl/src/glu/cgluquadric.h b/gb.opengl/src/glu/cgluquadric.h new file mode 100644 index 00000000..110215ac --- /dev/null +++ b/gb.opengl/src/glu/cgluquadric.h @@ -0,0 +1,63 @@ +/*************************************************************************** + + cgluquadric.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CGLUQUADRIC_H +#define __CGLUQUADRIC_H + +#include "gambas.h" +#include "main.h" +#include "GLU.h" + +#ifndef __CGLUQUADRIC_C + +extern GB_DESC GluQuadricDesc[]; + +#else + +#define THIS OBJECT(CGLUQUADRIC) +#define QUADRIC (THIS->quadric) + +#endif + +typedef + struct { + GB_BASE ob; + GLUquadric *quadric; + } + CGLUQUADRIC; + +CGLUQUADRIC *CGLUQUADRIC_create(); + +/* Gluquadric structure as described in glu.h source + + struct GLUquadric + { + GLint normals; + GLboolean textureCoords; + GLint orientation; + GLint drawStyle; + void (GLAPIENTRY *errorCallback)( GLint ); + }; +*/ + +#endif diff --git a/gb.opengl/src/glu/gb.opengl.glu.component b/gb.opengl/src/glu/gb.opengl.glu.component new file mode 100644 index 00000000..8409f1c8 --- /dev/null +++ b/gb.opengl/src/glu/gb.opengl.glu.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.opengl.glu +Author=Laurent Carlier +Need=OpenGLViewer +Requires=gb.opengl +State=Stable diff --git a/gb.opengl/src/glu/main.c b/gb.opengl/src/glu/main.c new file mode 100644 index 00000000..30b899a0 --- /dev/null +++ b/gb.opengl/src/glu/main.c @@ -0,0 +1,84 @@ +/*************************************************************************** + + main.c + + (c) 2005-2011 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" +#include "cglunurb.h" +#include "cgluquadric.h" +#include "GLU.h" + +GB_INTERFACE GB EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + Cglu, + GluQuadricDesc, + GluNurbsDesc, + NULL +}; + + +int IMAGE_get_pixel_format(GB_IMG *image) +{ + if ((image->format == GB_IMAGE_RGBA) || (image->format == GB_IMAGE_RGBX)) + return GL_RGBA; + // is it good ? + else if ((image->format == GB_IMAGE_BGRA) || (image->format == GB_IMAGE_BGRX)) + return GL_BGRA; + else if (image->format == GB_IMAGE_RGB) + return GL_RGB; + else if (image->format == GB_IMAGE_BGR) + return GL_BGR; + else + return 0; +} + +bool IMAGE_get(GB_OBJECT *arg, GB_IMG **img, int *format) +{ + *img = arg->value; + + if (GB.CheckObject(*img)) + return TRUE; + + *format = IMAGE_get_pixel_format(*img); + if (!*format) + { + IMAGE.Convert(*img, GB_IMAGE_RGBA); + *format = GL_RGBA; + } + + return FALSE; +} + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.opengl/src/glu/main.h b/gb.opengl/src/glu/main.h new file mode 100644 index 00000000..37f6da08 --- /dev/null +++ b/gb.opengl/src/glu/main.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + main.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" +#include "gb.image.h" +#include + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +#endif + +#ifndef WARNING +#define WARNING(c) printf("WARNING: " c) +#endif /* WARNING */ + +int IMAGE_get_pixel_format(GB_IMG *image); +bool IMAGE_get(GB_OBJECT *arg, GB_IMG **img, int *format); + +#endif + diff --git a/gb.opengl/src/main.c b/gb.opengl/src/main.c new file mode 100644 index 00000000..4ea414b7 --- /dev/null +++ b/gb.opengl/src/main.c @@ -0,0 +1,111 @@ +/*************************************************************************** + + main.c + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "GL.h" + +#include "gb.gl.h" + +GB_INTERFACE GB EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +bool Init(void); + +GB_DESC *GB_CLASSES[] EXPORT = +{ + Cgl, + NULL +}; + +void *GB_OPENGL_1[] EXPORT = { + + (void *)1, + (void *)Init, + + NULL +}; + +int IMAGE_get_pixel_format(GB_IMG *image) +{ + if ((image->format == GB_IMAGE_RGBA) || (image->format == GB_IMAGE_RGBX)) + return GL_RGBA; + // is it good ? + else if ((image->format == GB_IMAGE_BGRA) || (image->format == GB_IMAGE_BGRX)) + return GL_BGRA; + else if (image->format == GB_IMAGE_RGB) + return GL_RGB; + else if (image->format == GB_IMAGE_BGR) + return GL_BGR; + else + return 0; +} + +bool IMAGE_get(GB_OBJECT *arg, GB_IMG **img, int *format) +{ + *img = arg->value; + + if (GB.CheckObject(*img)) + return TRUE; + + *format = IMAGE_get_pixel_format(*img); + if (!*format) + { + IMAGE.Convert(*img, GB_IMAGE_RGBA); + *format = GL_RGBA; + } + + return FALSE; +} + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +bool Init(void) +{ + static bool _init = FALSE; + + if (_init) + return FALSE; + + GLenum err = glewInit(); + if (GLEW_OK != err) + { + /* Problem: glewInit failed, something is seriously wrong. */ + GB.Error("Failed to init GLEW: &1\n", glewGetErrorString(err)); + return TRUE; + } + else + { + _init = TRUE; + return FALSE; + } +} \ No newline at end of file diff --git a/gb.opengl/src/main.h b/gb.opengl/src/main.h new file mode 100644 index 00000000..706096aa --- /dev/null +++ b/gb.opengl/src/main.h @@ -0,0 +1,47 @@ +/*************************************************************************** + + main.h + + (c) 2005-2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" +#include "gb.image.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +#endif + +#ifndef WARNING +#define WARNING(_c) printf("warning: " _c) +#endif /* WARNING */ + +int IMAGE_get_pixel_format(GB_IMG *image); +bool IMAGE_get(GB_OBJECT *arg, GB_IMG **img, int *format); + +#define IMAGE_get_ncolors(_format) (GB_IMAGE_FMT_IS_24_BITS(_format) ? 3 : 4) + + +#endif + diff --git a/gb.opengl/src/sge/Makefile.am b/gb.opengl/src/sge/Makefile.am new file mode 100644 index 00000000..4aa679ec --- /dev/null +++ b/gb.opengl/src/sge/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.opengl.sge +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.opengl.sge.la + +gb_opengl_sge_la_LIBADD = @SGE_LIB@ +gb_opengl_sge_la_LDFLAGS = -module @LD_FLAGS@ @SGE_LDFLAGS@ +gb_opengl_sge_la_CPPFLAGS = @SGE_INC@ + +gb_opengl_sge_la_SOURCES = \ + main.c main.h \ + cmd2model.c cmd2model.h \ + cmd2object.c cmd2object.h diff --git a/gb.opengl/src/sge/cmd2model.c b/gb.opengl/src/sge/cmd2model.c new file mode 100644 index 00000000..a57eeba7 --- /dev/null +++ b/gb.opengl/src/sge/cmd2model.c @@ -0,0 +1,589 @@ +/*************************************************************************** + + cmd2model.c + + (c) 2012 Tomasz Kołodziejczyk "Tommyline" + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CMD2MODEL_C + +#include "gb_common.h" +#include "cmd2model.h" +#include + +#define THIS OBJECT(CMD2MODEL) + +/* Table of precalculated normals */ +GLfloat anorms_table[162][3] = +{ + { -0.525731f, 0.000000f, 0.850651f }, + { -0.442863f, 0.238856f, 0.864188f }, + { -0.295242f, 0.000000f, 0.955423f }, + { -0.309017f, 0.500000f, 0.809017f }, + { -0.162460f, 0.262866f, 0.951056f }, + { 0.000000f, 0.000000f, 1.000000f }, + { 0.000000f, 0.850651f, 0.525731f }, + { -0.147621f, 0.716567f, 0.681718f }, + { 0.147621f, 0.716567f, 0.681718f }, + { 0.000000f, 0.525731f, 0.850651f }, + { 0.309017f, 0.500000f, 0.809017f }, + { 0.525731f, 0.000000f, 0.850651f }, + { 0.295242f, 0.000000f, 0.955423f }, + { 0.442863f, 0.238856f, 0.864188f }, + { 0.162460f, 0.262866f, 0.951056f }, + { -0.681718f, 0.147621f, 0.716567f }, + { -0.809017f, 0.309017f, 0.500000f }, + { -0.587785f, 0.425325f, 0.688191f }, + { -0.850651f, 0.525731f, 0.000000f }, + { -0.864188f, 0.442863f, 0.238856f }, + { -0.716567f, 0.681718f, 0.147621f }, + { -0.688191f, 0.587785f, 0.425325f }, + { -0.500000f, 0.809017f, 0.309017f }, + { -0.238856f, 0.864188f, 0.442863f }, + { -0.425325f, 0.688191f, 0.587785f }, + { -0.716567f, 0.681718f, -0.147621f }, + { -0.500000f, 0.809017f, -0.309017f }, + { -0.525731f, 0.850651f, 0.000000f }, + { 0.000000f, 0.850651f, -0.525731f }, + { -0.238856f, 0.864188f, -0.442863f }, + { 0.000000f, 0.955423f, -0.295242f }, + { -0.262866f, 0.951056f, -0.162460f }, + { 0.000000f, 1.000000f, 0.000000f }, + { 0.000000f, 0.955423f, 0.295242f }, + { -0.262866f, 0.951056f, 0.162460f }, + { 0.238856f, 0.864188f, 0.442863f }, + { 0.262866f, 0.951056f, 0.162460f }, + { 0.500000f, 0.809017f, 0.309017f }, + { 0.238856f, 0.864188f, -0.442863f }, + { 0.262866f, 0.951056f, -0.162460f }, + { 0.500000f, 0.809017f, -0.309017f }, + { 0.850651f, 0.525731f, 0.000000f }, + { 0.716567f, 0.681718f, 0.147621f }, + { 0.716567f, 0.681718f, -0.147621f }, + { 0.525731f, 0.850651f, 0.000000f }, + { 0.425325f, 0.688191f, 0.587785f }, + { 0.864188f, 0.442863f, 0.238856f }, + { 0.688191f, 0.587785f, 0.425325f }, + { 0.809017f, 0.309017f, 0.500000f }, + { 0.681718f, 0.147621f, 0.716567f }, + { 0.587785f, 0.425325f, 0.688191f }, + { 0.955423f, 0.295242f, 0.000000f }, + { 1.000000f, 0.000000f, 0.000000f }, + { 0.951056f, 0.162460f, 0.262866f }, + { 0.850651f, -0.525731f, 0.000000f }, + { 0.955423f, -0.295242f, 0.000000f }, + { 0.864188f, -0.442863f, 0.238856f }, + { 0.951056f, -0.162460f, 0.262866f }, + { 0.809017f, -0.309017f, 0.500000f }, + { 0.681718f, -0.147621f, 0.716567f }, + { 0.850651f, 0.000000f, 0.525731f }, + { 0.864188f, 0.442863f, -0.238856f }, + { 0.809017f, 0.309017f, -0.500000f }, + { 0.951056f, 0.162460f, -0.262866f }, + { 0.525731f, 0.000000f, -0.850651f }, + { 0.681718f, 0.147621f, -0.716567f }, + { 0.681718f, -0.147621f, -0.716567f }, + { 0.850651f, 0.000000f, -0.525731f }, + { 0.809017f, -0.309017f, -0.500000f }, + { 0.864188f, -0.442863f, -0.238856f }, + { 0.951056f, -0.162460f, -0.262866f }, + { 0.147621f, 0.716567f, -0.681718f }, + { 0.309017f, 0.500000f, -0.809017f }, + { 0.425325f, 0.688191f, -0.587785f }, + { 0.442863f, 0.238856f, -0.864188f }, + { 0.587785f, 0.425325f, -0.688191f }, + { 0.688191f, 0.587785f, -0.425325f }, + { -0.147621f, 0.716567f, -0.681718f }, + { -0.309017f, 0.500000f, -0.809017f }, + { 0.000000f, 0.525731f, -0.850651f }, + { -0.525731f, 0.000000f, -0.850651f }, + { -0.442863f, 0.238856f, -0.864188f }, + { -0.295242f, 0.000000f, -0.955423f }, + { -0.162460f, 0.262866f, -0.951056f }, + { 0.000000f, 0.000000f, -1.000000f }, + { 0.295242f, 0.000000f, -0.955423f }, + { 0.162460f, 0.262866f, -0.951056f }, + { -0.442863f, -0.238856f, -0.864188f }, + { -0.309017f, -0.500000f, -0.809017f }, + { -0.162460f, -0.262866f, -0.951056f }, + { 0.000000f, -0.850651f, -0.525731f }, + { -0.147621f, -0.716567f, -0.681718f }, + { 0.147621f, -0.716567f, -0.681718f }, + { 0.000000f, -0.525731f, -0.850651f }, + { 0.309017f, -0.500000f, -0.809017f }, + { 0.442863f, -0.238856f, -0.864188f }, + { 0.162460f, -0.262866f, -0.951056f }, + { 0.238856f, -0.864188f, -0.442863f }, + { 0.500000f, -0.809017f, -0.309017f }, + { 0.425325f, -0.688191f, -0.587785f }, + { 0.716567f, -0.681718f, -0.147621f }, + { 0.688191f, -0.587785f, -0.425325f }, + { 0.587785f, -0.425325f, -0.688191f }, + { 0.000000f, -0.955423f, -0.295242f }, + { 0.000000f, -1.000000f, 0.000000f }, + { 0.262866f, -0.951056f, -0.162460f }, + { 0.000000f, -0.850651f, 0.525731f }, + { 0.000000f, -0.955423f, 0.295242f }, + { 0.238856f, -0.864188f, 0.442863f }, + { 0.262866f, -0.951056f, 0.162460f }, + { 0.500000f, -0.809017f, 0.309017f }, + { 0.716567f, -0.681718f, 0.147621f }, + { 0.525731f, -0.850651f, 0.000000f }, + { -0.238856f, -0.864188f, -0.442863f }, + { -0.500000f, -0.809017f, -0.309017f }, + { -0.262866f, -0.951056f, -0.162460f }, + { -0.850651f, -0.525731f, 0.000000f }, + { -0.716567f, -0.681718f, -0.147621f }, + { -0.716567f, -0.681718f, 0.147621f }, + { -0.525731f, -0.850651f, 0.000000f }, + { -0.500000f, -0.809017f, 0.309017f }, + { -0.238856f, -0.864188f, 0.442863f }, + { -0.262866f, -0.951056f, 0.162460f }, + { -0.864188f, -0.442863f, 0.238856f }, + { -0.809017f, -0.309017f, 0.500000f }, + { -0.688191f, -0.587785f, 0.425325f }, + { -0.681718f, -0.147621f, 0.716567f }, + { -0.442863f, -0.238856f, 0.864188f }, + { -0.587785f, -0.425325f, 0.688191f }, + { -0.309017f, -0.500000f, 0.809017f }, + { -0.147621f, -0.716567f, 0.681718f }, + { -0.425325f, -0.688191f, 0.587785f }, + { -0.162460f, -0.262866f, 0.951056f }, + { 0.442863f, -0.238856f, 0.864188f }, + { 0.162460f, -0.262866f, 0.951056f }, + { 0.309017f, -0.500000f, 0.809017f }, + { 0.147621f, -0.716567f, 0.681718f }, + { 0.000000f, -0.525731f, 0.850651f }, + { 0.425325f, -0.688191f, 0.587785f }, + { 0.587785f, -0.425325f, 0.688191f }, + { 0.688191f, -0.587785f, 0.425325f }, + { -0.955423f, 0.295242f, 0.000000f }, + { -0.951056f, 0.162460f, 0.262866f }, + { -1.000000f, 0.000000f, 0.000000f }, + { -0.850651f, 0.000000f, 0.525731f }, + { -0.955423f, -0.295242f, 0.000000f }, + { -0.951056f, -0.162460f, 0.262866f }, + { -0.864188f, 0.442863f, -0.238856f }, + { -0.951056f, 0.162460f, -0.262866f }, + { -0.809017f, 0.309017f, -0.500000f }, + { -0.864188f, -0.442863f, -0.238856f }, + { -0.951056f, -0.162460f, -0.262866f }, + { -0.809017f, -0.309017f, -0.500000f }, + { -0.681718f, 0.147621f, -0.716567f }, + { -0.681718f, -0.147621f, -0.716567f }, + { -0.850651f, 0.000000f, -0.525731f }, + { -0.688191f, 0.587785f, -0.425325f }, + { -0.587785f, 0.425325f, -0.688191f }, + { -0.425325f, 0.688191f, -0.587785f }, + { -0.425325f, -0.688191f, -0.587785f }, + { -0.587785f, -0.425325f, -0.688191f }, + { -0.688191f, -0.587785f, -0.425325f } +}; + + +CMD2MODEL *MD2MODEL_create(void) +{ + return (CMD2MODEL*)GB.New(GB.FindClass("Md2Model"), NULL, NULL); +}; + +int MD2MODEL_draw(CMD2MODEL *_object, double frame, int texture, float *pos, float *scale, float *rotate) +{ + int i; + int n, n2; + double interp; + GLfloat v_curr[3], v_next[3], v[3], norm[3]; + GLfloat *n_curr, *n_next; + const framemd2 *pframe1, *pframe2; + const vertexmd2 *pvert1, *pvert2; + bool enabled; + int nvert = 0; + + int *pglcmds; + glcmd *packet; + + if (texture < 0) + return 0; + + n = (int)frame; + interp = frame - n; + + if (n < 0 || n >= THIS->num_frames) + return 0; + + n2 = n + 1; + if (n2 >= THIS->num_frames) + n2 = 0; + + enabled = glIsEnabled(GL_TEXTURE_2D); + if (!enabled) + glEnable(GL_TEXTURE_2D); + + glPushMatrix(); + + if (pos) + glTranslatef(pos[0], pos[1], pos[2]); + + glRotatef(-90, 1, 0, 0); + glRotatef(-90, 0, 0, 1); + + if (rotate && rotate[0] != 0) + glRotatef(rotate[0], rotate[1], rotate[2], rotate[3]); + + glScalef(THIS->scale[0], THIS->scale[1], THIS->scale[2]); + + if (scale) + glScalef(scale[0], scale[1], scale[2]); + + glBindTexture(GL_TEXTURE_2D, texture); + +#if 1 + // pglcmds points at the start of the command list + pglcmds = THIS->glcmds; + + pframe1 = &THIS->frames[n]; + pframe2 = &THIS->frames[n2]; + + //fprintf(stderr, "\n******** %p: n = %d / %d | %p %p\n", _object, n, THIS->num_frames, pframe1->verts, pframe2->verts); + + // Draw the model + while ((i = *(pglcmds++)) != 0) + { + //fprintf(stderr, "i = %d\n", i); + + if (i < 0) + { + glBegin(GL_TRIANGLE_FAN); + i = -i; + } + else + { + glBegin(GL_TRIANGLE_STRIP); + } + + // Draw each vertex of this group + for (/* Nothing */ ; i > 0; --i, pglcmds += 3) + { + packet = (glcmd *)pglcmds; + + //fprintf(stderr, "%d (%d) ", i, packet->index); + + pvert1 = &pframe1->verts[packet->index]; + pvert2 = &pframe2->verts[packet->index]; + //if (!pvert1 || !pvert2) + // continue; + + // Pass texture coordinates to OpenGL + //glTexCoord2f (pGLcmd->s, 1.0f - pGLcmd->t); + glTexCoord2f(packet->s, packet->t); + + // Compute interpolated normal vector + n_curr = anorms_table[pvert1->normalIndex]; + n_next = anorms_table[pvert2->normalIndex]; + + norm[0] = n_curr[0] + interp * (n_next[0] - n_curr[0]); + norm[1] = n_curr[1] + interp * (n_next[1] - n_curr[1]); + norm[2] = n_curr[2] + interp * (n_next[2] - n_curr[2]); + + // Normal vector + glNormal3fv(norm); + + v_curr[0] = pframe1->scale[0] * pvert1->v[0] + pframe1->translate[0]; + v_curr[1] = pframe1->scale[1] * pvert1->v[1] + pframe1->translate[1]; + v_curr[2] = pframe1->scale[2] * pvert1->v[2] + pframe1->translate[2]; + + v_next[0] = pframe2->scale[0] * pvert2->v[0] + pframe2->translate[0]; + v_next[1] = pframe2->scale[1] * pvert2->v[1] + pframe2->translate[1]; + v_next[2] = pframe2->scale[2] * pvert2->v[2] + pframe2->translate[2]; + + v[0] = v_curr[0] + interp * (v_next[0] - v_curr[0]); + v[1] = v_curr[1] + interp * (v_next[1] - v_curr[1]); + v[2] = v_curr[2] + interp * (v_next[2] - v_curr[2]); + + glVertex3fv(v); + nvert++; + } + + //fprintf(stderr, "\n"); + + glEnd(); + } +#else + { + int j; + GLfloat s, t; + + glBegin (GL_TRIANGLES); + + for (i = 0; i < THIS->num_tris; ++i) + { + for (j = 0; j < 3; ++j) + { + pframe1 = &THIS->frames[n]; + pframe2 = &THIS->frames[n2]; + pvert1 = &pframe1->verts[THIS->triangles[i].vertex[j]]; + pvert2 = &pframe2->verts[THIS->triangles[i].vertex[j]]; + + s = (GLfloat)THIS->texcoords[THIS->triangles[i].st[j]].s / THIS->skinwidth; + t = (GLfloat)THIS->texcoords[THIS->triangles[i].st[j]].t / THIS->skinheight; + + glTexCoord2f (s, t); + + n_curr = anorms_table[pvert1->normalIndex]; + n_next = anorms_table[pvert2->normalIndex]; + + norm[0] = n_curr[0] + interp * (n_next[0] - n_curr[0]); + norm[1] = n_curr[1] + interp * (n_next[1] - n_curr[1]); + norm[2] = n_curr[2] + interp * (n_next[2] - n_curr[2]); + + glNormal3fv (norm); + + v_curr[0] = pframe1->scale[0] * pvert1->v[0] + pframe1->translate[0]; + v_curr[1] = pframe1->scale[1] * pvert1->v[1] + pframe1->translate[1]; + v_curr[2] = pframe1->scale[2] * pvert1->v[2] + pframe1->translate[2]; + + v_next[0] = pframe2->scale[0] * pvert2->v[0] + pframe2->translate[0]; + v_next[1] = pframe2->scale[1] * pvert2->v[1] + pframe2->translate[1]; + v_next[2] = pframe2->scale[2] * pvert2->v[2] + pframe2->translate[2]; + + v[0] = v_curr[0] + interp * (v_next[0] - v_curr[0]); + v[1] = v_curr[1] + interp * (v_next[1] - v_curr[1]); + v[2] = v_curr[2] + interp * (v_next[2] - v_curr[2]); + + glVertex3fv (v); + nvert++; + } + } + + glEnd(); + } +#endif + + //glScalef(1/THIS->scale[0], 1/THIS->scale[1], 1/THIS->scale[2]); + glPopMatrix(); + + if (!enabled) + glDisable(GL_TEXTURE_2D); + + return nvert; +} + + +//--------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Md2Model_new) + + THIS->texture = -1; + +END_METHOD + +BEGIN_METHOD_VOID(Md2Model_free) + + int i; + + GB.Free ((void *) &THIS->skins); + GB.Free ((void *) &THIS->texcoords); + GB.Free ((void *) &THIS->triangles); + + for (i = 0; i < THIS->num_frames; ++i) + GB.Free ((void *) &THIS->frames[i].verts); + + GB.Free ((void *) &THIS->frames); + GB.Free ((void *) &THIS->glcmds); + +END_METHOD + +/* + This method creates and loads new Md2 model. To use this in the program you should declare + it first as ie. + Public model as Md2Model + Then initiate it loading + model = Md2Model.Load(name as string) +*/ + +BEGIN_METHOD(Md2Model_Load, GB_STRING name) + + char *addr; + int len; + CMD2MODEL *mdl = NULL; + int i; + + if (GB.LoadFile(STRING(name), LENGTH(name), &addr, &len)) + return; + + mdl = MD2MODEL_create(); + + // Read header + { + int *p = (int *)addr; + + mdl->ident = *p++; + mdl->version = *p++; + mdl->skinwidth = *p++; + mdl->skinheight = *p++; + mdl->framesize = *p++; + mdl->num_skins = *p++; + mdl->num_vertices = *p++; + mdl->num_st = *p++; + mdl->num_tris = *p++; + mdl->num_glcmds = *p++; + mdl->num_frames = *p++; + mdl->offset_skins = *p++; + mdl->offset_st = *p++; + mdl->offset_tris = *p++; + mdl->offset_frames = *p++; + mdl->offset_glcmds = *p++; + mdl->offset_end = *p++; + } + + if ((mdl->ident != 844121161) || (mdl->version != 8)) + { + // Error! + GB.Error("Bad version or identifier"); + goto __ERROR; + } + + // Memory allocations + GB.Alloc ((void *) &mdl->skins, sizeof (skinmd2) * mdl->num_skins ); + GB.Alloc ((void *) &mdl->texcoords, sizeof (texCoordmd2) * mdl->num_st); + GB.Alloc ((void *) &mdl->triangles, sizeof (trianglemd2) * mdl->num_tris); + GB.Alloc ((void *) &mdl->frames, sizeof (framemd2) * mdl->num_frames); + GB.Alloc ((void *) &mdl->glcmds, sizeof (int) * mdl->num_glcmds); + + // Read model data + memcpy(mdl->skins, &addr[mdl->offset_skins], sizeof(skinmd2) * mdl->num_skins); + memcpy(mdl->texcoords, &addr[mdl->offset_st], sizeof(texCoordmd2) * mdl->num_st); + memcpy(mdl->triangles, &addr[mdl->offset_tris], sizeof(trianglemd2) * mdl->num_tris); + memcpy(mdl->glcmds, &addr[mdl->offset_glcmds], sizeof(int) * mdl->num_glcmds); + + // Read frames + { + char *p = &addr[mdl->offset_frames]; + + for (i = 0; i < mdl->num_frames; ++i) + { + // Memory allocation for vertices of this frame + GB.Alloc((void *) &mdl->frames[i].verts, sizeof (vertexmd2) * mdl->num_vertices); + + // Read frame data + memcpy(mdl->frames[i].scale, p, sizeof(float) * 3); p += sizeof(float) * 3; + memcpy(mdl->frames[i].translate, p, sizeof(float) * 3); p += sizeof(float) * 3; + memcpy(mdl->frames[i].name, p, 16); p += 16; + memcpy(mdl->frames[i].verts, p, sizeof(vertexmd2) * mdl->num_vertices); p += sizeof(vertexmd2) * mdl->num_vertices; + } + } + + mdl->scale[0] = 1; + mdl->scale[1] = 1; + mdl->scale[2] = 1; + + GB.ReleaseFile(addr, len); + GB.ReturnObject(mdl); + return; + +__ERROR: + + GB.ReleaseFile(addr, len); + GB.Unref(POINTER(&mdl)); + +END_METHOD + +BEGIN_METHOD(Md2Model_Scale, GB_FLOAT sx; GB_FLOAT sy; GB_FLOAT sz) + + THIS->scale[0] = VARG(sx); + THIS->scale[1] = VARG(sy); + THIS->scale[2] = VARG(sz); + +END_METHOD + +BEGIN_PROPERTY(Md2Model_Count) + + GB.ReturnInteger(THIS->num_frames); + +END_PROPERTY + +BEGIN_METHOD(Md2Model_get, GB_INTEGER frame) + + int frame = VARG(frame); + + if (frame < 0 || frame >= THIS->num_frames) + { + GB.Error(GB_ERR_BOUND); + return; + } + + THIS->frame = frame; + RETURN_SELF(); + +END_METHOD + +BEGIN_PROPERTY(Md2Model_Texture) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->texture); + else + THIS->texture = VPROP(GB_INTEGER); + +END_PROPERTY + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Md2Model_Frame_Draw, GB_FLOAT interp; GB_INTEGER texture) + + MD2MODEL_draw(THIS, (double)THIS->frame + VARGOPT(interp, 0.0), VARGOPT(texture, THIS->texture), NULL, NULL, NULL); + +END_METHOD + +BEGIN_PROPERTY(Md2Model_Frame_Name) + + GB.ReturnNewZeroString(THIS->frames[THIS->frame].name); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC Md2ModelFrameDesc[] = +{ + GB_DECLARE_VIRTUAL(".Md2Model.Frame"), + + GB_PROPERTY_READ("Name", "s", Md2Model_Frame_Name), + GB_METHOD("Draw", NULL, Md2Model_Frame_Draw, "[(Interpolation)f(Texture)i]"), + + GB_END_DECLARE +}; + +GB_DESC Md2ModelDesc[] = +{ + GB_DECLARE("Md2Model", sizeof(CMD2MODEL)), GB_NOT_CREATABLE(), + + GB_METHOD("_new", NULL, Md2Model_new, NULL), + GB_METHOD("_free", NULL, Md2Model_free, NULL), + + GB_STATIC_METHOD("Load", "Md2Model" , Md2Model_Load, "(Name)s"), + + GB_PROPERTY_READ("Count", "i", Md2Model_Count), + GB_METHOD("_get", ".Md2Model.Frame", Md2Model_get, "(Frame)i"), + + GB_METHOD("Scale", NULL, Md2Model_Scale, "(SX)f(SY)f(SZ)f" ), + + GB_PROPERTY("Texture", "i", Md2Model_Texture), + + GB_END_DECLARE +}; + + diff --git a/gb.opengl/src/sge/cmd2model.h b/gb.opengl/src/sge/cmd2model.h new file mode 100644 index 00000000..b3fa2c39 --- /dev/null +++ b/gb.opengl/src/sge/cmd2model.h @@ -0,0 +1,167 @@ +/*************************************************************************** + + cmd2model.h + + (c) 2012 Tomasz Kolodziejczyk "Tommyline" + + Based on David HENRY's md2.c md2 model loader and renderer +========================================================================= + * + * md2.c -- md2 model loader + * last modification: aug. 14, 2007 + * + * Copyright (c) 2005-2007 David HENRY + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * gcc -Wall -ansi -lGL -lGLU -lglut md2.c -o md2 + * +========================================================================= + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General License for more details. + + You should have received a copy of the GNU General License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CMD2MODEL_H +#define __CMD2MODEL_H + +#include "gambas.h" +#include "main.h" +#include "cmd2object.h" + +#ifndef __CMD2MODEL_C +extern GB_DESC Md2ModelFrameDesc[]; +extern GB_DESC Md2ModelDesc[]; +#endif + +/* Vector */ +typedef float vec3[3]; + + +/* Texture name */ +typedef +struct md2_skin +{ + char name[68]; +}skinmd2; + +/* Texture coords */ +typedef +struct md2_texCoordmd2 +{ + short s; + short t; +}texCoordmd2; + +/* Triangle info */ +typedef +struct md2_trianglemd2 +{ + unsigned short vertex[3]; + unsigned short st[3]; +}trianglemd2; + +/* Compressed vertexmd2 */ +typedef +struct md2_vertexmd2 +{ + unsigned char v[3]; + unsigned char normalIndex; +}vertexmd2; + +/* Model frame */ +typedef +struct md2_frame +{ + float scale[3]; + float translate[3]; + char name[16]; + vertexmd2 *verts; +}framemd2; + +/* GL command packet */ +typedef +struct md2_glcmd +{ + float s; + float t; + int index; +}glcmd; + + +typedef + struct CMD2MODEL { + GB_BASE ob; + //Header + int ident; + int version; + + int skinwidth; + int skinheight; + + int framesize; + + int num_skins; + int num_vertices; + int num_st; + int num_tris; + int num_glcmds; + int num_frames; + + int offset_skins; + int offset_st; + int offset_tris; + int offset_frames; + int offset_glcmds; + int offset_end; + //End header + + skinmd2 *skins; + texCoordmd2 *texcoords; + trianglemd2 *triangles; + framemd2 *frames; + int *glcmds; + + //Model specific data + float scale[3]; + int frame; // frame being accessed with the [] operator + GLuint texture; + //End model specific data + } + CMD2MODEL; + + +CMD2MODEL *MD2MODEL_create(void); +int MD2MODEL_draw(CMD2MODEL *_object, double frame, int texture, float *pos, float *rotate, float *scale); + +#endif diff --git a/gb.opengl/src/sge/cmd2object.c b/gb.opengl/src/sge/cmd2object.c new file mode 100644 index 00000000..96194144 --- /dev/null +++ b/gb.opengl/src/sge/cmd2object.c @@ -0,0 +1,175 @@ +/*************************************************************************** + + cmd2object.c + + (c) 2012 Tomasz Kołodziejczyk "Tommyline" + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CMD2OBJECT_C + +#include "cmd2object.h" + +#define THIS OBJECT(CMD2OBJECT) + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Md2Object_new, GB_OBJECT model) + + CMD2MODEL *model = VARG(model); + + if (GB.CheckObject(model)) + return; + + THIS->model = model; + GB.Ref(model); + + THIS->texture = -1; + + THIS->scale[0] = THIS->scale[1] = THIS->scale[2] = 1; + +END_METHOD + +BEGIN_METHOD_VOID(Md2Object_free) + + GB.Unref(POINTER(&THIS->model)); + +END_METHOD + + +BEGIN_METHOD(Md2Object_Move, GB_FLOAT x; GB_FLOAT y; GB_FLOAT z) + + THIS->pos[0] = VARG(x); + THIS->pos[1] = VARG(y); + THIS->pos[2] = VARG(z); + +END_METHOD + +BEGIN_PROPERTY(Md2Object_X) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->pos[0]); + else + THIS->pos[0] = VPROP(GB_FLOAT); + +END_PROPERTY + +BEGIN_PROPERTY(Md2Object_Y) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->pos[1]); + else + THIS->pos[1] = VPROP(GB_FLOAT); + +END_PROPERTY + +BEGIN_PROPERTY(Md2Object_Z) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->pos[2]); + else + THIS->pos[2] = VPROP(GB_FLOAT); + +END_PROPERTY + +BEGIN_METHOD(Md2Object_Scale, GB_FLOAT sx; GB_FLOAT sy; GB_FLOAT sz) + + THIS->scale[0] = VARG(sx); + THIS->scale[1] = VARG(sy); + THIS->scale[2] = VARG(sz); + +END_METHOD + +BEGIN_METHOD(Md2Object_Rotate, GB_FLOAT angle; GB_FLOAT rx; GB_FLOAT ry; GB_FLOAT rz) + + THIS->rotate[0] = VARG(angle); + THIS->rotate[1] = VARG(rx); + THIS->rotate[2] = VARG(ry); + THIS->rotate[3] = VARG(rz); + +END_METHOD + +BEGIN_PROPERTY(Md2Object_Texture) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->texture); + else + THIS->texture = VPROP(GB_INTEGER); + +END_PROPERTY + +BEGIN_PROPERTY(Md2Object_Frame) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->frame); + else + THIS->frame = VPROP(GB_FLOAT); + +END_PROPERTY + +BEGIN_METHOD_VOID(Md2Object_Draw) + + int texture = THIS->texture; + + if (texture < 0) + texture = THIS->model->texture; + + GB.ReturnInteger(MD2MODEL_draw(THIS->model, THIS->frame, texture, THIS->pos, THIS->scale, THIS->rotate)); + +END_METHOD + +BEGIN_PROPERTY(Md2Object_Model) + + GB.ReturnObject(THIS->model); + +END_PROPERTY + +BEGIN_PROPERTY(Md2Object_Count) + + GB.ReturnInteger(THIS->model->num_frames); + +END_PROPERTY + +//--------------------------------------------------------------------------- + +GB_DESC Md2ObjectDesc[] = +{ + GB_DECLARE("Md2Object", sizeof(CMD2OBJECT)), + + GB_METHOD("_new", NULL, Md2Object_new, "(Model)Md2Model;"), + GB_METHOD("_free", NULL, Md2Object_free, NULL), + + GB_PROPERTY_READ("X", "f", Md2Object_X), + GB_PROPERTY_READ("Y", "f", Md2Object_Y), + GB_PROPERTY_READ("Z", "f", Md2Object_Z), + + GB_METHOD("Move", NULL, Md2Object_Move, "(X)f(Y)f(Z)f"), + GB_METHOD("Scale", NULL, Md2Object_Scale, "(SX)f(SY)f(SZ)f"), + GB_METHOD("Rotate", NULL, Md2Object_Rotate, "(Angle)f(RX)f(RY)f(RZ)f"), + + GB_PROPERTY("Texture", "i", Md2Object_Texture), + GB_PROPERTY("Frame", "f", Md2Object_Frame), + GB_PROPERTY_READ("Count", "i", Md2Object_Count), + GB_METHOD("Draw", "i", Md2Object_Draw, NULL), + + GB_PROPERTY_READ("Model", "Md2Model", Md2Object_Model), + + GB_END_DECLARE +}; + + diff --git a/gb.opengl/src/sge/cmd2object.h b/gb.opengl/src/sge/cmd2object.h new file mode 100644 index 00000000..3ab385b5 --- /dev/null +++ b/gb.opengl/src/sge/cmd2object.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + cmd2object.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General License for more details. + + You should have received a copy of the GNU General License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CMD2OBJECT_H +#define __CMD2OBJECT_H + +#include "gambas.h" +#include "main.h" +#include "cmd2model.h" + +#ifndef __CMD2OBJECT_C +extern GB_DESC Md2ObjectDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + struct CMD2MODEL *model; + float pos[3]; + float scale[3]; + float rotate[4]; + double frame; + GLuint texture; + GB_VARIANT tag; + } + CMD2OBJECT; + +#endif diff --git a/gb.opengl/src/sge/gb.opengl.sge.component b/gb.opengl/src/sge/gb.opengl.sge.component new file mode 100644 index 00000000..c10cba65 --- /dev/null +++ b/gb.opengl/src/sge/gb.opengl.sge.component @@ -0,0 +1,4 @@ +[Component] +Author=Benoît Minisini +State=Stable +Requires=gb.opengl diff --git a/gb.opengl/src/sge/main.c b/gb.opengl/src/sge/main.c new file mode 100644 index 00000000..03fddcb9 --- /dev/null +++ b/gb.opengl/src/sge/main.c @@ -0,0 +1,49 @@ +/*************************************************************************** + + main.c + + gb.sge component + + (c) 2013 Tomek Kolodziejczyk + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" +#include "cmd2model.h" +#include "cmd2object.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + Md2ModelFrameDesc, + Md2ModelDesc, + Md2ObjectDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/gb.opengl/src/sge/main.h b/gb.opengl/src/sge/main.h new file mode 100644 index 00000000..07fd9360 --- /dev/null +++ b/gb.opengl/src/sge/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + gb.sge component + + (c) 2013 Tomek Kolodziejczyk + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "GL/gl.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.openssl/AUTHORS b/gb.openssl/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.openssl/COPYING b/gb.openssl/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.openssl/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.openssl/ChangeLog b/gb.openssl/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.openssl/INSTALL b/gb.openssl/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.openssl/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.openssl/Makefile.am b/gb.openssl/Makefile.am new file mode 100644 index 00000000..fa6d9c5d --- /dev/null +++ b/gb.openssl/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @OPENSSL_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.openssl/NEWS b/gb.openssl/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.openssl/README b/gb.openssl/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.openssl/acinclude.m4 b/gb.openssl/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.openssl/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.openssl/component.am b/gb.openssl/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.openssl/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.openssl/configure.ac b/gb.openssl/configure.ac new file mode 100644 index 00000000..da48fd17 --- /dev/null +++ b/gb.openssl/configure.ac @@ -0,0 +1,20 @@ +dnl ---- configure.ac for gb.openssl + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-openssl, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.openssl) +AC_PROG_LIBTOOL + +# We really only want to make sure that EVP_MD_do_all() exists. +# Don't do a version check because some systems may have the +# function from somewhere but don't have a high enough version. +#GB_COMPONENT_PKG_CONFIG(openssl, OPENSSL, gb.openssl, [src], 'openssl >= 1.0.0' libcrypto) + +AC_CHECK_LIB(crypto,EVP_MD_do_all,,touch DISABLED) + +GB_COMPONENT_PKG_CONFIG(openssl, OPENSSL, gb.openssl, [src], libcrypto) + +AC_OUTPUT(Makefile src/Makefile) +GB_PRINT_MESSAGES diff --git a/gb.openssl/gambas.h b/gb.openssl/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.openssl/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.openssl/gb_common.h b/gb.openssl/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.openssl/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.openssl/m4 b/gb.openssl/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.openssl/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.openssl/reconf b/gb.openssl/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.openssl/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.openssl/src/Makefile.am b/gb.openssl/src/Makefile.am new file mode 100644 index 00000000..01454e70 --- /dev/null +++ b/gb.openssl/src/Makefile.am @@ -0,0 +1,16 @@ +COMPONENT = gb.openssl +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.openssl.la + +gb_openssl_la_LIBADD = @OPENSSL_LIB@ +gb_openssl_la_LDFLAGS = -module @LD_FLAGS@ @OPENSSL_LDFLAGS@ +gb_openssl_la_CPPFLAGS = @OPENSSL_INC@ -I../../main/gbx -I../../main/share + +gb_openssl_la_SOURCES = \ + main.h main.c \ + c_digest.h c_digest.c \ + c_cipher.h c_cipher.c \ + c_hmac.h c_hmac.c + + # c_signature.h c_signature.c diff --git a/gb.openssl/src/c_cipher.c b/gb.openssl/src/c_cipher.c new file mode 100644 index 00000000..46490b36 --- /dev/null +++ b/gb.openssl/src/c_cipher.c @@ -0,0 +1,487 @@ +/* + * c_cipher.c - Cipher, .Cipher.Method and CipherText classes + * + * Copyright (C) 2013,4 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_CIPHER_C + +#include + +#include +#include +#include + +#include "main.h" +#include "c_cipher.h" + +/* + * Cipher + */ + +static GB_ARRAY _clist = NULL; + +static void clist_func(const EVP_CIPHER *ciph, const char *from, + const char *to, void *arg) +{ + const char *src; + + if (!ciph) + return; + src = EVP_CIPHER_name(ciph); + *((const char **) GB.Array.Add(_clist)) = GB.NewZeroString(src); +} + +static void get_clist(void) +{ + GB.Array.New(&_clist, GB_T_STRING, 0); + /* XXX: There is EVP_CIPHER_do_all_sorted() but that still returns + * entries twice (NOT next to each other), so we sort manually */ + EVP_CIPHER_do_all(clist_func, NULL); + sort_and_dedupe(_clist); +} + +/**G + * Return a list of all ciphers present in the local OpenSSL crypto library. + **/ +BEGIN_PROPERTY(Cipher_List) + + GB_FUNCTION copyfn; + GB_VALUE *copy; + + if (!_clist) + get_clist(); + + if (GB.GetFunction(©fn, _clist, "Copy", NULL, NULL)) { + GB.Error("Can't copy array"); + return; + } + copy = GB.Call(©fn, 0, 0); + GB.ReturnObject(copy->_object.value); + +END_PROPERTY + +BEGIN_METHOD_VOID(Cipher_init) + + OpenSSL_add_all_ciphers(); + +END_METHOD + +BEGIN_METHOD_VOID(Cipher_exit) + + if (!_clist) + return; + GB.Unref((void **) &_clist); + _clist = NULL; + +END_METHOD + +const static EVP_CIPHER *_method; + +/**G + * Return a virtual object representing a cipher algorithm by giving its + * name. Valid names can be looked up from the Cipher.List property. + **/ +BEGIN_METHOD(Cipher_get, GB_STRING method) + + _method = EVP_get_cipherbyname(GB.ToZeroString(ARG(method))); + if (!_method) { + GB.Error("Unknown cipher method"); + return; + } + RETURN_SELF(); + +END_METHOD + +/**G + * Check if the named cipher algorithm is supported. + **/ +BEGIN_METHOD(Cipher_IsSupported, GB_STRING method) + + _method = EVP_get_cipherbyname(STRING(method)); + GB.ReturnBoolean(!!_method); + +END_METHOD + +GB_DESC CCipher[] = { + GB_DECLARE("Cipher", 0), + GB_NOT_CREATABLE(), + + GB_STATIC_PROPERTY_READ("List", "String[]", Cipher_List), + + GB_STATIC_METHOD("_init", NULL, Cipher_init, NULL), + GB_STATIC_METHOD("_exit", NULL, Cipher_exit, NULL), + GB_STATIC_METHOD("_get", ".Cipher.Method", Cipher_get, "(Method)s"), + GB_STATIC_METHOD("IsSupported", "b", Cipher_IsSupported, "(Method)s"), + + GB_END_DECLARE +}; + +/* + * .Cipher.Method + */ + +/**G + * Return the key length that this cipher algorithm expects. + * + * See also: Cipher.Encrypt() + **/ +BEGIN_PROPERTY(CipherMethod_KeyLength) + + if (READ_PROPERTY) { + GB.ReturnInteger(EVP_CIPHER_key_length(_method)); + return; + } + +END_PROPERTY + +/**G + * Return the initialisation vector length that this cipher algorithm + * expects. + * + * See also: Cipher.Encrypt() + **/ +BEGIN_PROPERTY(CipherMethod_IvLength) + + if (READ_PROPERTY) { + GB.ReturnInteger(EVP_CIPHER_iv_length(_method)); + return; + } + +END_PROPERTY + +static char *do_cipher(const unsigned char *data, unsigned int dlen, + const unsigned char *key, const unsigned char *iv, + unsigned int *length, int enc, char **errmsg) +{ + EVP_CIPHER_CTX *ctx; + unsigned char block[1024 + EVP_MAX_BLOCK_LENGTH]; + char *out; + unsigned int ilen; + int blen; + + *errmsg = NULL; + ctx = EVP_CIPHER_CTX_new(); + + if (!ctx) { + *errmsg = "Could not allocate cipher context"; + return NULL; + } + EVP_CIPHER_CTX_init(ctx); + if (!EVP_CipherInit_ex(ctx, _method, NULL, key, iv, enc)) + return NULL; + + out = NULL; + *length = 0; + memset(block, 0, sizeof(block)); + while (dlen) { + ilen = MIN(dlen, 1024); + if (!EVP_CipherUpdate(ctx, block, &blen, data, ilen)) + goto __ERROR; + out = GB.AddString(out, (char *) block, blen); + *length += blen; + data += ilen; + dlen -= ilen; + } + if (!EVP_CipherFinal_ex(ctx, block, &blen)) + goto __ERROR; + if (!EVP_CIPHER_CTX_cleanup(ctx)) + goto __ERROR; + if (blen) { + out = GB.AddString(out, (char *) block, blen); + *length += blen; + } + EVP_CIPHER_CTX_free(ctx); + return out; + +__ERROR: + GB.FreeString(&out); + EVP_CIPHER_CTX_free(ctx); + return NULL; +} + +typedef struct { + GB_BASE ob; + char *cipher, *key, *iv; +} CCIPHERTEXT; + +/**G + * Encrypt a plaintext using the given key and initialisation vector. + **/ +BEGIN_METHOD(CipherMethod_Encrypt, GB_STRING plain; GB_STRING key; + GB_STRING iv) + + unsigned char key[EVP_CIPHER_key_length(_method)]; + unsigned char iv[EVP_CIPHER_iv_length(_method)]; + unsigned int length; + char *cipher, *errmsg; + CCIPHERTEXT *res; + + bzero(key, sizeof(key)); + bzero(iv, sizeof(iv)); + if (MISSING(key)) { + assert(RAND_bytes(key, sizeof(key))); + } else { + if (LENGTH(key) != sizeof(key)) { + GB.Error("Key length does not match method"); + return; + } + memcpy(key, STRING(key), sizeof(key)); + } + if (MISSING(iv)) { + assert(RAND_bytes(iv, sizeof(iv))); + } else { + if (LENGTH(iv) != sizeof(iv)) { + GB.Error("InitVector length does not match method"); + return; + } + memcpy(iv, STRING(iv), sizeof(iv)); + } + + cipher = do_cipher((uchar *) STRING(plain), LENGTH(plain), key, iv, + &length, 1, &errmsg); + if (!cipher) { + GB.Error(errmsg ? errmsg : "Encryption failed"); + return; + } + + GB.Push(3, GB_T_STRING, cipher, length, + GB_T_STRING, key, sizeof(key), + GB_T_STRING, iv, sizeof(iv)); + res = GB.New(GB.FindClass("CipherText"), NULL, (void*)(intptr_t) 3); + GB.FreeString(&cipher); + GB.ReturnObject(res); + +END_METHOD + +/**G + * Decrypt a ciphertext generated by Encrypt(). + **/ +BEGIN_METHOD(CipherMethod_Decrypt, GB_OBJECT ciph) + + CCIPHERTEXT *ciph = VARG(ciph); + unsigned int length; + char *plain, *errmsg; + + plain = do_cipher((uchar *) ciph->cipher, + GB.StringLength(ciph->cipher), + (uchar *) ciph->key, (uchar *) ciph->iv, + &length, 0, &errmsg); + if (!plain) { + GB.Error(errmsg ? errmsg : "Decryption failed"); + return; + } + GB.ReturnNewString(plain, length); + GB.FreeString(&plain); + +END_METHOD + +/* + * Retain compatibility with the 'openssl' program for now. + */ + +#define ITER 1 + +/**G# + * Encrypt a plaintext with a password to a standalone string. + * + * If 'salt' is given, it should be an 8-byte string. If it is not, it is + * padded with zeros or truncated to a size of 8. If the parameter is not + * given, a pseudo-random salt is generated. + * + * This uses a less up-to-date method (PKCS#5 v1.5) to generate an + * encryption key from the password. This is for compatibility with the + * openssl program when invoked like: + * + * `echo -n Plain | openssl CipherMethod -k Password -S HexSalt` + **/ +BEGIN_METHOD(CipherMethod_EncryptSalted, GB_STRING plain; GB_STRING passwd; + GB_STRING salt) + + unsigned char salt[8]; + unsigned char key[EVP_CIPHER_key_length(_method)]; + unsigned char iv[EVP_CIPHER_iv_length(_method)]; + unsigned int length; + char *cipher, *res, *errmsg; + + bzero(salt, sizeof(salt)); + if (MISSING(salt)) { + assert(RAND_bytes(salt, sizeof(salt))); + } else { + bzero(salt, sizeof(salt)); + memcpy(salt, STRING(salt), MIN(sizeof(salt), LENGTH(salt))); + } + +// "openssl enc" >= 1.1.0 uses SHA256 by default instead of MD5 +// EVP_BytesToKey(_method, EVP_sha256(), salt, (uchar *) STRING(passwd), +// LENGTH(passwd), ITER, key, iv); + EVP_BytesToKey(_method, EVP_md5(), salt, (uchar *) STRING(passwd), + LENGTH(passwd), ITER, key, iv); + cipher = do_cipher((uchar *) STRING(plain), LENGTH(plain), key, iv, + &length, 1, &errmsg); + if (!cipher) { + GB.Error(errmsg ? errmsg : "Encryption failed"); + return; + } + res = GB.NewZeroString("Salted__"); + res = GB.AddString(res, (char *) salt, sizeof(salt)); + res = GB.AddString(res, cipher, length); + GB.FreeString(&cipher); + GB.ReturnString(res); + GB.ReturnBorrow(); + GB.FreeString(&res); + GB.ReturnRelease(); + +END_METHOD + +/**G# + * Decrypt a ciphertext obtained from EncryptSalted(). + **/ +BEGIN_METHOD(CipherMethod_DecryptSalted, GB_STRING cipher; GB_STRING passwd) + + unsigned char salt[8], *cipher; + unsigned char key[EVP_CIPHER_key_length(_method)]; + unsigned char iv[EVP_CIPHER_iv_length(_method)]; + unsigned int clen, length; + char *plain, *errmsg; + + if (!strstr(STRING(cipher), "Salted__")) { + GB.Error("Unrecognised cipher string format"); + return; + } + /* salt begins at STRING(cipher) + strlen("Salted__") */ + memcpy(salt, STRING(cipher) + 8, sizeof(salt)); + +// "openssl enc" >= 1.1.0 uses SHA256 by default instead of MD5 +// EVP_BytesToKey(_method, EVP_sha256(), salt, (uchar *) STRING(passwd), +// LENGTH(passwd), ITER, key, iv); + EVP_BytesToKey(_method, EVP_md5(), salt, (uchar *) STRING(passwd), + LENGTH(passwd), ITER, key, iv); + cipher = (uchar *) STRING(cipher) + 8 + sizeof(salt); + clen = LENGTH(cipher) - (uint) (cipher - (uchar *) STRING(cipher)); + plain = do_cipher((uchar *) cipher, clen, key, iv, &length, 0, &errmsg); + if (!plain) { + GB.Error(errmsg ? errmsg : "Decryption failed"); + return; + } + GB.ReturnNewString(plain, length); + GB.FreeString(&plain); + +END_METHOD + +#if 0 +/**G + * Encrypt a plaintext with a password to a standalone string. + * + * We use the PKCS#5-v2.0-conforming PBKDF2 HMAC-SHA1 to generate an + * encryption key from a password. + **/ +BEGIN_METHOD(CipherMethod_EncryptSalted, GB_STRING plain; GB_STRING passwd) + + char salt[8]; /* 8 is recommended as minimum */ + char key[EVP_CIPHER_key_length(_method)]; + + bzero(key, sizeof(key)); + RAND_bytes(salt, sizeof(salt)); + PKCS5_PBKDF_HMAC_SHA1(STRING(passwd), LENGTH(passwd), salt, + sizeof(salt), ITER, sizeof(key), key); + + +END_METHOD + +/**G + * Decrypt a ciphertext obtained from EncryptSalted(). + **/ +BEGIN_METHOD(CipherMethod_DecryptSalted, GB_STRING cipher; GB_STRING passwd) + + + +END_METHOD +#endif + +GB_DESC CCipherMethod[] = { + /* + * TODO: Maybe add a stream interface? + */ + GB_DECLARE(".Cipher.Method", 0), + GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY_READ("KeyLength", "i", CipherMethod_KeyLength), + GB_STATIC_PROPERTY_READ("IvLength", "i", CipherMethod_IvLength), + + GB_STATIC_METHOD("Encrypt", "CipherText", CipherMethod_Encrypt, "(Plain)s[(Key)s(InitVector)s]"), + GB_STATIC_METHOD("Decrypt", "s", CipherMethod_Decrypt, "(Cipher)CipherText"), + + GB_STATIC_METHOD("EncryptSalted", "s", CipherMethod_EncryptSalted, "(Plain)s(Password)s[(Salt)s]"), + GB_STATIC_METHOD("DecryptSalted", "s", CipherMethod_DecryptSalted, "(Cipher)s(Password)s"), + + GB_END_DECLARE +}; + +/* + * CipherText + */ + +#define THIS ((CCIPHERTEXT *) _object) + +BEGIN_PROPERTY(CipherText_Cipher) + + GB.ReturnString(THIS->cipher); + +END_PROPERTY + +BEGIN_PROPERTY(CipherText_Key) + + GB.ReturnString(THIS->key); + +END_PROPERTY + +BEGIN_PROPERTY(CipherText_InitVector) + + GB.ReturnString(THIS->iv); + +END_PROPERTY + +BEGIN_METHOD(CipherText_new, GB_STRING ciph; GB_STRING key; GB_STRING iv) + + THIS->cipher = GB.NewString(STRING(ciph), LENGTH(ciph)); + THIS->key = GB.NewString(STRING(key), LENGTH(key)); + THIS->iv = GB.NewString(STRING(iv), LENGTH(iv)); + +END_METHOD + +BEGIN_METHOD_VOID(CipherText_free) + + GB.FreeString(&THIS->cipher); + GB.FreeString(&THIS->key); + GB.FreeString(&THIS->iv); + +END_METHOD + +GB_DESC CCipherText[] = { + GB_DECLARE("CipherText", sizeof(CCIPHERTEXT)), + + GB_PROPERTY_READ("Cipher", "s", CipherText_Cipher), + GB_PROPERTY_READ("Key", "s", CipherText_Key), + GB_PROPERTY_READ("InitVector", "s", CipherText_InitVector), + + GB_METHOD("_new", NULL, CipherText_new, "(Cipher)s(Key)s(InitVector)s"), + GB_METHOD("_free", NULL, CipherText_free, NULL), + + GB_END_DECLARE +}; diff --git a/gb.openssl/src/c_cipher.h b/gb.openssl/src/c_cipher.h new file mode 100644 index 00000000..e4cf7ff0 --- /dev/null +++ b/gb.openssl/src/c_cipher.h @@ -0,0 +1,29 @@ +/* + * c_cipher.h + * + * Copyright (C) 2013 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_CIPHER_H +#define __C_CIPHER_H + +#ifndef __C_CIPHER_C +extern GB_DESC CCipher[], CCipherMethod[], CCipherText[]; +#endif + +#endif /* __C_CIPHER_H */ diff --git a/gb.openssl/src/c_digest.c b/gb.openssl/src/c_digest.c new file mode 100644 index 00000000..87016a05 --- /dev/null +++ b/gb.openssl/src/c_digest.c @@ -0,0 +1,276 @@ +/* + * c_digest.c - Digest and .Digest.Method classes + * + * Copyright (C) 2013,4 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_DIGEST_C + +#include +#include +#include + +#include "main.h" +#include "c_digest.h" + +/* + * Digest + */ + +static GB_ARRAY _dlist = NULL; + +static void dlist_func(const EVP_MD *dgst, const char *from, + const char *to, void *arg) +{ + const char *src; + + if (!dgst) + return; + src = EVP_MD_name(dgst); + *((const char **) GB.Array.Add(_dlist)) = GB.NewZeroString(src); +} + +static void get_dlist(void) +{ + GB.Array.New(&_dlist, GB_T_STRING, 0); + EVP_MD_do_all(dlist_func, NULL); + sort_and_dedupe(_dlist); +} + +/**G + * Return a list of all digests present in the local OpenSSL crypto library. + **/ +BEGIN_PROPERTY(Digest_List) + + GB_FUNCTION copyfn; + GB_VALUE *copy; + + if (!_dlist) + get_dlist(); + + if (GB.GetFunction(©fn, _dlist, "Copy", NULL, NULL)) { + GB.Error("Can't copy array"); + return; + } + copy = GB.Call(©fn, 0, 0); + GB.ReturnObject(copy->_object.value); + +END_PROPERTY + +BEGIN_METHOD_VOID(Digest_init) + + OpenSSL_add_all_digests(); + +END_METHOD + +BEGIN_METHOD_VOID(Digest_exit) + + if (!_dlist) + return; + GB.Unref((void **) &_dlist); + _dlist = NULL; + +END_METHOD + +const static EVP_MD *_method; + +/**G + * Return a virtual object representing a digest algorithm by giving its + * name. Valid names can be looked up from Digest.List. + **/ +BEGIN_METHOD(Digest_get, GB_STRING method) + + _method = EVP_get_digestbyname(GB.ToZeroString(ARG(method))); + if (!_method) { + GB.Error("Unknown digest method"); + return; + } + RETURN_SELF(); + +END_METHOD + +/**G + * Check whether the named digest algorithm is valid. + **/ +BEGIN_METHOD(Digest_IsSupported, GB_STRING method) + + _method = EVP_get_digestbyname(STRING(method)); + GB.ReturnBoolean(!!_method); + +END_METHOD + +GB_DESC CDigest[] = { + GB_DECLARE("Digest", 0), + GB_NOT_CREATABLE(), + + GB_STATIC_PROPERTY_READ("List", "String[]", Digest_List), + + GB_STATIC_METHOD("_init", NULL, Digest_init, NULL), + GB_STATIC_METHOD("_exit", NULL, Digest_exit, NULL), + GB_STATIC_METHOD("_get", ".Digest.Method", Digest_get, "(Method)s"), + GB_STATIC_METHOD("IsSupported", "b", Digest_IsSupported, "(Method)s"), + + GB_END_DECLARE +}; + +/* + * .Digest.Method + */ + +/* + * Forward compatibility for openssl 1.1 + * + * OPENSSL_zalloc() and OPENSSL_clear_free() have been written according to + * the behaviour described in their manpage. + */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L +/* + * Special treatment for OpenSSL 0.9.8*. The version hex below is 0.9.8zh. + * The *_new() and *_free() routines were called *_create() and *_destroy() + * there. + */ +#if OPENSSL_VERSION_NUMBER <= 0x0090821fL +inline int EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx) +{ + /* Don't assume ctx->md_data was cleaned in EVP_Digest_Final, + * because sometimes only copies of the context are ever finalised. + */ + if (ctx->digest && ctx->digest->cleanup + && !M_EVP_MD_CTX_test_flags(ctx,EVP_MD_CTX_FLAG_CLEANED)) + ctx->digest->cleanup(ctx); + if (ctx->digest && ctx->digest->ctx_size && ctx->md_data + && !M_EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_REUSE)) { + OPENSSL_cleanse(ctx->md_data,ctx->digest->ctx_size); + OPENSSL_free(ctx->md_data); + } +#ifndef OPENSSL_NO_ENGINE + if(ctx->engine) + /* The EVP_MD we used belongs to an ENGINE, release the + * functional reference we held for this reason. */ + do_engine_finish(ctx->engine); +#endif + memset(ctx,'\0',sizeof *ctx); + return 1; +} + +inline void EVP_MD_CTX_init(EVP_MD_CTX *ctx) +{ + memset(ctx, '\0', sizeof *ctx); +} + +inline EVP_MD_CTX *EVP_MD_CTX_new(void) +{ + EVP_MD_CTX *ctx = OPENSSL_malloc(sizeof *ctx); + + if (ctx) + EVP_MD_CTX_init(ctx); + return ctx; +} + +inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) +{ + EVP_MD_CTX_cleanup(ctx); + OPENSSL_free(ctx); +} +#else /* Anything recent */ +static void OPENSSL_clear_free(void *str, size_t num) +{ + OPENSSL_cleanse(str, num); + OPENSSL_free(str); +} + +static void *OPENSSL_zalloc(size_t num) +{ + void *ret = OPENSSL_malloc(num); + + if (!ret) + return NULL; + memset(ret, 0, num); + return ret; +} + +static int EVP_MD_CTX_reset(EVP_MD_CTX *ctx) +{ + if (ctx == NULL) + return 1; + + /* + * Don't assume ctx->md_data was cleaned in EVP_Digest_Final, because + * sometimes only copies of the context are ever finalised. + */ + if (ctx->digest && ctx->digest->cleanup + && !EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_CLEANED)) + ctx->digest->cleanup(ctx); + if (ctx->digest && ctx->digest->ctx_size && ctx->md_data + && !EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_REUSE)) { + OPENSSL_clear_free(ctx->md_data, ctx->digest->ctx_size); + } + EVP_PKEY_CTX_free(ctx->pctx); +#ifndef OPENSSL_NO_ENGINE + ENGINE_finish(ctx->engine); +#endif + OPENSSL_cleanse(ctx, sizeof(*ctx)); + + return 1; +} + +static EVP_MD_CTX *EVP_MD_CTX_new(void) +{ + return OPENSSL_zalloc(sizeof(EVP_MD_CTX)); +} + +static void EVP_MD_CTX_free(EVP_MD_CTX *ctx) +{ + EVP_MD_CTX_reset(ctx); + OPENSSL_free(ctx); +} +#endif +#endif + +/**G + * Hash the given string using this digest algorithm. + **/ +BEGIN_METHOD(DigestMethod_Hash, GB_STRING data) + + EVP_MD_CTX *ctx; + char digest[EVP_MAX_MD_SIZE]; + unsigned int length; + + ctx = EVP_MD_CTX_new(); + if (!ctx) { + GB.Error("Could not allocate digest context"); + return; + } + memset(digest, 0, sizeof(digest)); + EVP_DigestInit(ctx, _method); + EVP_DigestUpdate(ctx, STRING(data), LENGTH(data)); + EVP_DigestFinal(ctx, (unsigned char *) digest, &length); + EVP_MD_CTX_free(ctx); + GB.ReturnNewString(digest, length); + +END_METHOD + +GB_DESC CDigestMethod[] = { + GB_DECLARE(".Digest.Method", 0), + GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("Hash", "s", DigestMethod_Hash, "(Data)s"), + GB_STATIC_METHOD("_call", "s", DigestMethod_Hash, "(Data)s"), + + GB_END_DECLARE +}; diff --git a/gb.openssl/src/c_digest.h b/gb.openssl/src/c_digest.h new file mode 100644 index 00000000..d0a44ae8 --- /dev/null +++ b/gb.openssl/src/c_digest.h @@ -0,0 +1,30 @@ +/* + * c_digest.h + * + * Copyright (C) 2013 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_DIGEST_H +#define __C_DIGEST_H + +#ifndef __C_DIGEST_C +extern GB_DESC CDigest[], CDigestMethod[]; +#endif + +#endif /* __C_DIGEST_H */ + diff --git a/gb.openssl/src/c_hmac.c b/gb.openssl/src/c_hmac.c new file mode 100644 index 00000000..7ab87ad2 --- /dev/null +++ b/gb.openssl/src/c_hmac.c @@ -0,0 +1,73 @@ +/* + * c_hmac.c - HMac class + * + * Copyright (C) 2013 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_HMAC_C + +#include + +#include "main.h" +#include "c_hmac.h" + +/* + * HMac + */ + +/**G + * Make an HMAC authentication code out of a key and some data. + * + * *Method* may be HMac.Sha1 or HMac.RipeMD160. By default it is HMac.Sha1. + **/ +BEGIN_METHOD(HMac_call, GB_STRING key; GB_STRING data; GB_INTEGER method) + + char hash[EVP_MAX_MD_SIZE]; + unsigned int len; + const EVP_MD *method; + + method = EVP_get_digestbynid(VARGOPT(method, NID_sha1)); + if (!method) { + GB.Error("Unknown method"); + return; + } + + memset(hash, 0, sizeof(hash)); + HMAC(method, STRING(key), LENGTH(key), (unsigned char *) STRING(data), + LENGTH(data), (unsigned char *) hash, &len); + GB.ReturnNewString(hash, len); + +END_METHOD + +GB_DESC CHMac[] = { + GB_DECLARE("HMac", 0), + GB_NOT_CREATABLE(), + + /**G HMac Sha1 + * Use the SHA1 algorithm. + **/ + GB_CONSTANT("Sha1", "i", NID_sha1), + /**G HMac RipeMD160 + * Use the RIPEMD160 algorithm. + **/ + GB_CONSTANT("RipeMD160", "i", NID_ripemd160), + + GB_STATIC_METHOD("_call", "s", HMac_call, "(Key)s(Data)s[(Method)i]"), + + GB_END_DECLARE +}; diff --git a/gb.openssl/src/c_hmac.h b/gb.openssl/src/c_hmac.h new file mode 100644 index 00000000..0ea98f51 --- /dev/null +++ b/gb.openssl/src/c_hmac.h @@ -0,0 +1,30 @@ +/* + * c_hmac.h + * + * Copyright (C) 2013 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_HMAC_H +#define __C_HMAC_H + +#ifndef __C_HMAC_C +extern GB_DESC CHMac[]; +#endif + +#endif /* __C_HMAC_H */ + diff --git a/gb.openssl/src/gb.openssl.component b/gb.openssl/src/gb.openssl.component new file mode 100644 index 00000000..33a5dc85 --- /dev/null +++ b/gb.openssl/src/gb.openssl.component @@ -0,0 +1,3 @@ +[Component] +Author=Tobias Boege +State=Stable diff --git a/gb.openssl/src/main.c b/gb.openssl/src/main.c new file mode 100644 index 00000000..ea2a814f --- /dev/null +++ b/gb.openssl/src/main.c @@ -0,0 +1,92 @@ +/* + * main.c - gb.openssl main object + * + * Copyright (C) 2013,4 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __MAIN_C + +#include + +#include +#include +#include + +#include "main.h" +#include "c_digest.h" +#include "c_cipher.h" +#include "c_hmac.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = { + CDigest, + CDigestMethod, + + CCipher, + CCipherMethod, + CCipherText, + + CHMac, + +// CSignature, +// CSignatureMethod, + + NULL +}; + +void sort_and_dedupe(GB_ARRAY list) +{ + GB_FUNCTION sortfn, removefn; + CARRAY *arr; + int i; + + if (GB.GetFunction(&sortfn, list, "Sort", NULL, NULL)) { + GB.Error("Can't sort array"); + return; + } + GB.Push(1, GB_T_INTEGER, GB_COMP_ASCENT | GB_COMP_NOCASE); + GB.Call(&sortfn, 1, 0); + + if (GB.GetFunction(&removefn, list, "Remove", NULL, NULL)) { + GB.Error("Can't remove duplicates"); + return; + } + arr = (CARRAY *) list; + for (i = 0; i < arr->count - 1;) { + char *a = ((char **) arr->data)[i], + *b = ((char **) arr->data)[i + 1]; + + if (!strcasecmp(a, b)) { + GB.Push(1, GB_T_INTEGER, i); + GB.Call(&removefn, 1, 0); + } else { + i++; + } + } +} + +int EXPORT GB_INIT() +{ + return 0; +} + +void EXPORT GB_EXIT() +{ + EVP_cleanup(); +} diff --git a/gb.openssl/src/main.h b/gb.openssl/src/main.h new file mode 100644 index 00000000..e94f11f2 --- /dev/null +++ b/gb.openssl/src/main.h @@ -0,0 +1,34 @@ +/* + * main.h + * + * Copyright (C) 2013 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +void sort_and_dedupe(GB_ARRAY list); + +#endif /* __MAIN_H */ diff --git a/gb.pcre/AUTHORS b/gb.pcre/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.pcre/COPYING b/gb.pcre/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.pcre/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.pcre/ChangeLog b/gb.pcre/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.pcre/INSTALL b/gb.pcre/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.pcre/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.pcre/Makefile.am b/gb.pcre/Makefile.am new file mode 100644 index 00000000..38fcf96a --- /dev/null +++ b/gb.pcre/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @PCRE_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.pcre/NEWS b/gb.pcre/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.pcre/README b/gb.pcre/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.pcre/acinclude.m4 b/gb.pcre/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.pcre/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.pcre/component.am b/gb.pcre/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.pcre/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.pcre/configure.ac b/gb.pcre/configure.ac new file mode 100644 index 00000000..3fe2ea06 --- /dev/null +++ b/gb.pcre/configure.ac @@ -0,0 +1,19 @@ +dnl ---- configure.ac for gb.pcre + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-pcre, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.pcre) +AC_PROG_LIBTOOL + +GB_COMPONENT_PKG_CONFIG( + pcre, PCRE, gb.pcre, [src], + libpcre) + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.pcre/gambas.h b/gb.pcre/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.pcre/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.pcre/gb_common.h b/gb.pcre/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.pcre/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.pcre/m4 b/gb.pcre/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.pcre/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.pcre/reconf b/gb.pcre/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.pcre/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.pcre/src/Makefile.am b/gb.pcre/src/Makefile.am new file mode 100644 index 00000000..4467958f --- /dev/null +++ b/gb.pcre/src/Makefile.am @@ -0,0 +1,15 @@ +COMPONENT = gb.pcre +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.pcre.la + +gb_pcre_la_LIBADD = @PCRE_LIB@ +gb_pcre_la_LDFLAGS = -module @LD_FLAGS@ @PCRE_LDFLAGS@ +gb_pcre_la_CPPFLAGS = @PCRE_INC@ + +gb_pcre_la_SOURCES = \ + main.h main.c \ + regexp.h regexp.c \ + gb.pcre.h + + diff --git a/gb.pcre/src/README b/gb.pcre/src/README new file mode 100644 index 00000000..58c9bac9 --- /dev/null +++ b/gb.pcre/src/README @@ -0,0 +1,62 @@ +gb.pcre + +Gambas PCRE Component + +version 0.04 - 4 October 2004 + +Copyright 2004 Rob Kudla and Benoit Minisini + +This component is licensed under the same terms as Gambas itself, +namely those of the GNU General Public License. + +This component implements perl-style regular expressions using the +PCRE (Perl-Compatible Regular Expressions) library. It contains one +class, Regex, as described below. Currently it's only useful for +matching, and replacing will have to be done in Gambas code... I hope +to provide example Match() and MatchReplace() functions soon. + +Synopsis: + +dim re as Regex +re = new Regex("stringtosearch","regularexpression") + +Regex.Offset: The offset within the string where a match occurred. +Regex.Match: The text that matched the pattern. + +If you put parentheses around part of your pattern, and the whole +pattern matches, the parts in parens become submatches. + +Regex.SubMatchCount: The number of submatches found. +Regex.SubMatch(i as integer): The text of submatch i. +Regex.SubMatchOffset(i as integer): The offset of submatch i. + +Example: + +dim i as integer +dim re as Regex +re = new Regex("the quick brown fox","quick(.+)fox") +if re.Offset >= 0 then + print re.Match + for i = 1 to re.SubMatchCount + print re.SubMatch(i) + next +endif + +This should generate the following output: + +quick brown fox + brown + +To compile this component, you must have a Gambas 0.99 source tree +available and place this tarball's contents (the whole pcre directory) +under src/lib in the gambas source tree. Then you must modify a +number of files in the gambas source tree and rebuild according to the +instructions in: + +http://www.binara.com/gambas-wiki/bin/view/Gambas/HowToProgramComponentsQuick + +I'll try to make a Mandrake package of this and announce it on the +gambas-user@sourceforge.net list. + +Rob Kudla +pcre-component@kudla.org diff --git a/gb.pcre/src/gb.pcre.component b/gb.pcre/src/gb.pcre.component new file mode 100644 index 00000000..c0eaacf8 --- /dev/null +++ b/gb.pcre/src/gb.pcre.component @@ -0,0 +1,7 @@ +[Component] +Key=gb.pcre +Name=Perl-compatible Regular Expression Matching +Name[fr]=Expressions rationnelles compatibles avec Perl +Name[tr]=Perl-uyumlu Düzenli İfade Eşleme +Author=Benoît Minisini,Rob Kudla +State=Stable diff --git a/gb.pcre/src/gb.pcre.h b/gb.pcre/src/gb.pcre.h new file mode 100644 index 00000000..e8292786 --- /dev/null +++ b/gb.pcre/src/gb.pcre.h @@ -0,0 +1,40 @@ +/*************************************************************************** + + gb.pcre.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_PCRE_H +#define __GB_PCRE_H + +#include "gambas.h" + +#define PCRE_INTERFACE_VERSION 1 + +typedef + struct { + int version; + bool (*Match)(const char *subject, int lsubject, const char *pattern, int lpattern, int coptions, int eoptions); + } + PCRE_INTERFACE; + +#endif + + diff --git a/gb.pcre/src/main.c b/gb.pcre/src/main.c new file mode 100644 index 00000000..14a653d8 --- /dev/null +++ b/gb.pcre/src/main.c @@ -0,0 +1,62 @@ +/*************************************************************************** + + main.c + + (c) 2004 Rob Kudla + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include + +#include "regexp.h" + +#include "main.h" + +GB_INTERFACE *GB_PTR EXPORT; + +void *GB_PCRE_1[] EXPORT = { + + (void *)PCRE_INTERFACE_VERSION, + (void *)REGEXP_match, + NULL + }; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CRegexpDesc, + CRegexpSubmatchesDesc, + CRegexpSubmatchDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + + +void EXPORT GB_EXIT() +{ +} + diff --git a/gb.pcre/src/main.h b/gb.pcre/src/main.h new file mode 100644 index 00000000..440a05a1 --- /dev/null +++ b/gb.pcre/src/main.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + main.h + + (c) 2004 Rob Kudla + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb.pcre.h" + +#ifndef __MAIN_C +extern GB_INTERFACE *GB_PTR; +#endif + +#define GB (*GB_PTR) + +#endif /* __MAIN_H */ diff --git a/gb.pcre/src/regexp.c b/gb.pcre/src/regexp.c new file mode 100644 index 00000000..142e58dc --- /dev/null +++ b/gb.pcre/src/regexp.c @@ -0,0 +1,532 @@ +/*************************************************************************** + + regexp.c + + (c) 2004 Rob Kudla + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __REGEXP_C + +#include "gb_common.h" + +#include "regexp.h" +#include "main.h" + +//#define DEBUG_REPLACE 1 + +#define OVECSIZE_INC 99 + +#define PCRE_GREEDY 0x80000000 // The highest possible pcre constant. It must be not used by pcre of course! + +DECLARE_METHOD(RegExp_free); + +//--------------------------------------------------------------------------- + +static void compile(void *_object) +{ + int errptr; + const char *errstr; + + if (!THIS->pattern) { + GB.Error("No pattern provided"); + return; + } + + if (THIS->code) + free(THIS->code); + + THIS->code = pcre_compile(THIS->pattern, THIS->copts, &errstr, &errptr, NULL); + + if (!THIS->code) + { + THIS->error = errptr; + GB.Error(errstr); + } +} + +static void exec(void *_object, int lsubject) +{ + int ret; + char code[16]; + + if (!THIS->code) + { + GB.Error("No pattern compiled yet"); + return; + } + + if (lsubject < 0) + lsubject = GB.StringLength(THIS->subject); + + if (!THIS->subject) + { + GB.Error("No subject provided"); + return; + } + + for(;;) + { + ret = pcre_exec(THIS->code, + NULL, + THIS->subject, + lsubject, + 0, + THIS->eopts, + THIS->ovector, + THIS->ovecsize); + + if (ret > 0) + { + THIS->error = 0; + THIS->count = ret; + break; + } + else if (ret < 0) + { + THIS->error = ret; + + switch (ret) + { + case PCRE_ERROR_NOMATCH: + THIS->count = 0; return; + case PCRE_ERROR_NULL: + GB.Error("Pattern or subject is null"); return; + case PCRE_ERROR_BADOPTION: + GB.Error("Unknown option"); return; + case PCRE_ERROR_BADMAGIC: + case PCRE_ERROR_UNKNOWN_OPCODE: + GB.Error("Incorrect PCRE bytecode"); return; + case PCRE_ERROR_NOMEMORY: + GB.Error("Out of memory"); return; + case PCRE_ERROR_BADUTF8: + #ifdef PCRE_ERROR_SHORTUTF8 + case PCRE_ERROR_SHORTUTF8: + #endif + GB.Error("Bad UTF-8 string"); return; + #ifdef PCRE_ERROR_BADUTF8_OFFSET + case PCRE_ERROR_BADUTF8_OFFSET: + GB.Error("Bad UTF-8 offset"); return; + #endif + case PCRE_ERROR_INTERNAL: + GB.Error("Unexpected internal error"); return; + case PCRE_ERROR_BADNEWLINE: + GB.Error("Invalid combination of newline options"); return; + //case PCRE_ERROR_RECURSELOOP: + // GB.Error("Recursion loop detected"); return; + //case PCRE_ERROR_JIT_STACKLIMIT: + // GB.Error("JIT stack limit reached"); return; + default: + sprintf(code, "%d", -ret); + GB.Error("Unable to run regular expression: error #&1", code); + return; + } + } + + THIS->ovecsize += OVECSIZE_INC; + GB.Realloc(POINTER(&THIS->ovector), THIS->ovecsize * sizeof(int)); + } +} + +static void return_match(void *_object, int index) +{ + int len; + + if (index < 0 || index >= THIS->count) + { + GB.Error("Out of bounds"); + return; + } + + index *= 2; + len = THIS->ovector[index + 1] - THIS->ovector[index]; + if (len <= 0) + GB.ReturnVoidString(); + else + GB.ReturnNewString(&THIS->subject[THIS->ovector[index]], len); +} + +bool REGEXP_match(const char *subject, int lsubject, const char *pattern, int lpattern, int coptions, int eoptions) +{ + /* + * The gb.pcre internal routines don't require the GB_BASE to be + * initialised by Gambas! + */ + + CREGEXP tmp; + bool ret = FALSE; + + if (lsubject <= 0) + return (lpattern <= 0); + + CLEAR(&tmp); + tmp.ovecsize = OVECSIZE_INC; + GB.Alloc(POINTER(&tmp.ovector), sizeof(int) * tmp.ovecsize); + tmp.copts = coptions; + tmp.pattern = GB.NewString(pattern, lpattern); + + compile(&tmp); + + if (tmp.code) + { + tmp.eopts = eoptions; + tmp.subject = GB.NewString(subject, lsubject); + + exec(&tmp, -1); + ret = (tmp.ovector[0] != -1); + } + + RegExp_free(&tmp, NULL); + + return ret; +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(RegExp_Compile, GB_STRING pattern; GB_INTEGER coptions) + + THIS->copts = VARGOPT(coptions, 0); + + GB.FreeString(&THIS->pattern); + THIS->pattern = GB.NewString(STRING(pattern), LENGTH(pattern)); + compile(THIS); + +END_METHOD + + +BEGIN_METHOD(RegExp_Exec, GB_STRING subject; GB_INTEGER eoptions) + + THIS->eopts = VARGOPT(eoptions, 0); + + GB.FreeString(&THIS->subject); + THIS->subject = GB.NewString(STRING(subject), LENGTH(subject)); + exec(THIS, -1); + +END_METHOD + + +BEGIN_METHOD(RegExp_new, GB_STRING subject; GB_STRING pattern; GB_INTEGER coptions; GB_INTEGER eoptions) + + THIS->ovecsize = OVECSIZE_INC; + GB.Alloc(POINTER(&THIS->ovector), sizeof(int) * THIS->ovecsize); + + if (MISSING(pattern)) // the user didn't provide a pattern. + return; + + THIS->copts = VARGOPT(coptions, 0); + THIS->pattern = GB.NewString(STRING(pattern), LENGTH(pattern)); + THIS->code = NULL; + + compile(THIS); + if (!THIS->code) // we didn't get a compiled pattern back. + return; + + if (MISSING(subject)) // the user didn't specify any subject text. + return; + + THIS->eopts = VARGOPT(eoptions, 0); + THIS->subject = GB.NewString(STRING(subject), LENGTH(subject)); + + exec(THIS, -1); + +END_METHOD + + +BEGIN_METHOD_VOID(RegExp_free) + + if (THIS->code) + free(THIS->code); + GB.FreeString(&THIS->subject); + GB.FreeString(&THIS->pattern); + GB.Free(POINTER(&THIS->ovector)); + +END_METHOD + + +BEGIN_METHOD(RegExp_Match, GB_STRING subject; GB_STRING pattern; GB_INTEGER coptions; GB_INTEGER eoptions) + + GB.ReturnBoolean(REGEXP_match(STRING(subject), LENGTH(subject), STRING(pattern), LENGTH(pattern), VARGOPT(coptions, 0), VARGOPT(eoptions, 0))); + +END_METHOD + + +BEGIN_PROPERTY(RegExp_Pattern) + + GB.ReturnString(THIS->pattern); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Subject) + + GB.ReturnString(THIS->subject); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Offset) + + GB.ReturnInteger(THIS->ovector[0]); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Text) + + if (THIS->count == 0) + GB.ReturnVoidString(); + else + return_match(THIS, 0); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Error) + + GB.ReturnInteger(THIS->error); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Submatches) + + GB.Deprecated("gb.pcre", "Regexp.Submatches", NULL); + GB.ReturnSelf(THIS); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Submatches_Count) + + GB.ReturnInteger(THIS->count - 1); + +END_PROPERTY + + +BEGIN_METHOD(RegExp_Submatches_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= THIS->count) + { + GB.Error("Out of bounds"); + return; + } + + THIS->_submatch = index; + RETURN_SELF(); + +END_METHOD + + +BEGIN_PROPERTY(RegExp_Submatch_Text) + + return_match(THIS, THIS->_submatch); + +END_PROPERTY + + +BEGIN_PROPERTY(RegExp_Submatch_Offset) + + GB.ReturnInteger(THIS->ovector[2 * THIS->_submatch]); + +END_PROPERTY + + +static CREGEXP *_subst_regexp = NULL; + +static void subst_get_submatch(int index, const char **p, int *lp) +{ + if (index <= 0 || index >= _subst_regexp->count) + { + *p = NULL; + *lp = 0; + } + else + { + index *= 2; + *p = &_subst_regexp->subject[_subst_regexp->ovector[index]]; + *lp = _subst_regexp->ovector[index + 1] - _subst_regexp->ovector[index]; + } +} + +BEGIN_METHOD(RegExp_Replace, GB_STRING subject; GB_STRING pattern; GB_STRING replace; GB_INTEGER coptions; GB_INTEGER eoptions) + + CREGEXP r; + char *replace; + char *result = NULL; + char *subject; + int offset; + + CLEAR(&r); + r.ovecsize = OVECSIZE_INC; + GB.Alloc(POINTER(&r.ovector), sizeof(int) * r.ovecsize); + r.copts = VARGOPT(coptions, 0); + if (r.copts & PCRE_GREEDY) + r.copts &= ~PCRE_GREEDY; + else + r.copts |= PCRE_UNGREEDY; + r.pattern = GB.NewString(STRING(pattern), LENGTH(pattern)); + + compile(&r); + + if (r.code) + { + r.eopts = VARGOPT(eoptions, 0); + subject = GB.NewString(STRING(subject), LENGTH(subject)); + + offset = 0; + + while (offset < LENGTH(subject)) + { + r.subject = &subject[offset]; + #if DEBUG_REPLACE + fprintf(stderr, "\nsubject: (%d) %s\n", offset, r.subject); + #endif + exec(&r, GB.StringLength(subject) - offset); + + if (r.ovector[0] < 0) + break; + + _subst_regexp = &r; + + if (r.ovector[0] > 0) + { + #if DEBUG_REPLACE + fprintf(stderr, "add: (%d) %.*s\n", r.ovector[0], r.ovector[0], r.subject); + #endif + result = GB.AddString(result, r.subject, r.ovector[0]); + #if DEBUG_REPLACE + fprintf(stderr, "result = %s\n", result); + #endif + } + + replace = GB.SubstString(STRING(replace), LENGTH(replace), (GB_SUBST_CALLBACK)subst_get_submatch); + #if DEBUG_REPLACE + fprintf(stderr, "replace = %s\n", replace); + #endif + result = GB.AddString(result, replace, GB.StringLength(replace)); + #if DEBUG_REPLACE + fprintf(stderr, "result = %s\n", result); + #endif + + offset += r.ovector[1]; + + if (*r.pattern == '^') + break; + } + + if (offset < LENGTH(subject)) + result = GB.AddString(result, &subject[offset], LENGTH(subject) - offset); + + _subst_regexp = NULL; + + GB.FreeStringLater(result); + #if DEBUG_REPLACE + fprintf(stderr, "result = %s\n", result); + #endif + r.subject = subject; + } + + RegExp_free(&r, NULL); + + GB.ReturnString(result); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC CRegexpDesc[] = +{ + GB_DECLARE("RegExp", sizeof(CREGEXP)), + + GB_METHOD("_new", NULL, RegExp_new, "[(Subject)s(Pattern)s(CompileOptions)i(ExecOptions)i]"), + GB_METHOD("_free", NULL, RegExp_free, NULL), + + GB_METHOD("Compile", NULL, RegExp_Compile, "(Pattern)s[(CompileOptions)i]"), + GB_METHOD("Exec", NULL, RegExp_Exec, "(Subject)s[(ExecOptions)i]"), + + GB_STATIC_METHOD("Match", "b", RegExp_Match, "(Subject)s(Pattern)s[(CompileOptions)i(ExecOptions)i]"), + GB_STATIC_METHOD("Replace", "s", RegExp_Replace, "(Subject)s(Pattern)s(Replace)s[(CompileOptions)i(ExecOptions)i]"), + + GB_CONSTANT("Caseless", "i", PCRE_CASELESS), + GB_CONSTANT("MultiLine", "i", PCRE_MULTILINE), + GB_CONSTANT("DotAll", "i", PCRE_DOTALL), + GB_CONSTANT("Extended", "i", PCRE_EXTENDED), + GB_CONSTANT("Anchored", "i", PCRE_ANCHORED), + GB_CONSTANT("DollarEndOnly", "i", PCRE_DOLLAR_ENDONLY), + GB_CONSTANT("Extra", "i", PCRE_EXTRA), + GB_CONSTANT("NotBOL", "i", PCRE_NOTBOL), + GB_CONSTANT("NotEOL", "i", PCRE_NOTEOL), + GB_CONSTANT("Ungreedy", "i", PCRE_UNGREEDY), + GB_CONSTANT("NotEmpty", "i", PCRE_NOTEMPTY), + GB_CONSTANT("UTF8", "i", PCRE_UTF8), + GB_CONSTANT("NoAutoCapture", "i", PCRE_NO_AUTO_CAPTURE), + GB_CONSTANT("NoUTF8Check", "i", PCRE_NO_UTF8_CHECK), + GB_CONSTANT("NoMatch", "i", PCRE_ERROR_NOMATCH), + GB_CONSTANT("Null", "i", PCRE_ERROR_NULL), + GB_CONSTANT("BadOption", "i", PCRE_ERROR_BADOPTION), + GB_CONSTANT("BadMagic", "i", PCRE_ERROR_BADMAGIC), + GB_CONSTANT("UnknownNode", "i", PCRE_ERROR_UNKNOWN_NODE), + GB_CONSTANT("NoMemory", "i", PCRE_ERROR_NOMEMORY), + GB_CONSTANT("NoSubstring", "i", PCRE_ERROR_NOSUBSTRING), + GB_CONSTANT("MatchLimit", "i", PCRE_ERROR_MATCHLIMIT), + GB_CONSTANT("Callout", "i", PCRE_ERROR_CALLOUT), + GB_CONSTANT("BadUTF8", "i", PCRE_ERROR_BADUTF8), +#if (((PCRE_MAJOR == 4) && (PCRE_MINOR < 5)) || (PCRE_MAJOR < 4)) + GB_CONSTANT("BadUTF8Offset", "i", 65535), /* PCRE_ERROR_BADUTF8_OFFSET not defined < 4.5 */ +#else + GB_CONSTANT("BadUTF8Offset", "i", PCRE_ERROR_BADUTF8_OFFSET), +#endif + GB_CONSTANT("Greedy", "i", PCRE_GREEDY), + + GB_PROPERTY_READ("SubMatches", ".Regexp.Submatches", RegExp_Submatches), + + GB_PROPERTY_READ("Text", "s", RegExp_Text), /* this is the string matched by the entire pattern */ + GB_PROPERTY_READ("Offset", "i", RegExp_Offset), /* this is the string matched by the entire pattern */ + GB_PROPERTY_READ("Pattern", "s", RegExp_Pattern), + GB_PROPERTY_READ("Subject", "s", RegExp_Subject), + GB_PROPERTY_READ("Error", "i", RegExp_Error), + + GB_METHOD("_get", ".Regexp.Submatch", RegExp_Submatches_get, "(Index)i"), + GB_PROPERTY_READ("Count", "i", RegExp_Submatches_Count), + + GB_END_DECLARE +}; + +GB_DESC CRegexpSubmatchesDesc[] = +{ + GB_DECLARE(".Regexp.Submatches", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", ".Regexp.Submatch", RegExp_Submatches_get, "(Index)i"), + GB_PROPERTY_READ("Count", "i", RegExp_Submatches_Count), + + GB_END_DECLARE +}; + +GB_DESC CRegexpSubmatchDesc[] = +{ + GB_DECLARE(".Regexp.Submatch", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Offset", "i", RegExp_Submatch_Offset), + GB_PROPERTY_READ("Text", "s", RegExp_Submatch_Text), + + GB_END_DECLARE +}; + diff --git a/gb.pcre/src/regexp.h b/gb.pcre/src/regexp.h new file mode 100644 index 00000000..76544bdf --- /dev/null +++ b/gb.pcre/src/regexp.h @@ -0,0 +1,63 @@ +/*************************************************************************** + + regexp.h + + (c) 2004 Rob Kudla + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __REGEXP_H +#define __REGEXP_H + +#include "gambas.h" + +#include "pcre.h" + +#ifndef __REGEXP_C + +extern GB_DESC CRegexpDesc[]; +extern GB_DESC CRegexpSubmatchesDesc[]; +extern GB_DESC CRegexpSubmatchDesc[]; + +#else + +typedef + struct + { + GB_BASE ob; + char *subject; + char *pattern; + int *ovector; + int ovecsize; + int count; + int eopts; + int copts; + pcre *code; + int _submatch; + int error; + } + CREGEXP; + +#define THIS OBJECT(CREGEXP) + +#endif + +bool REGEXP_match(const char *subject, int lsubject, const char *pattern, int lpattern, int coptions, int eoptions); + +#endif diff --git a/gb.pdf/AUTHORS b/gb.pdf/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.pdf/COPYING b/gb.pdf/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.pdf/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.pdf/ChangeLog b/gb.pdf/ChangeLog new file mode 100644 index 00000000..3ab04654 --- /dev/null +++ b/gb.pdf/ChangeLog @@ -0,0 +1,15 @@ +051030: + +* Removed glib dependencies from the library. +* "GetPicture" method removed. +* "GetImage" supports rotation. + +051006: + +* Added this Changelog. +* Added an "Info" interface to get document properties. + + +050929: + +* Initial stable release. \ No newline at end of file diff --git a/gb.pdf/INSTALL b/gb.pdf/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.pdf/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.pdf/Makefile.am b/gb.pdf/Makefile.am new file mode 100644 index 00000000..56782c85 --- /dev/null +++ b/gb.pdf/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @POPPLER_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.pdf/NEWS b/gb.pdf/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.pdf/README b/gb.pdf/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.pdf/acinclude.m4 b/gb.pdf/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.pdf/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.pdf/component.am b/gb.pdf/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.pdf/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.pdf/configure.ac b/gb.pdf/configure.ac new file mode 100644 index 00000000..b7d888bb --- /dev/null +++ b/gb.pdf/configure.ac @@ -0,0 +1,47 @@ +dnl ---- configure.ac for gb.pdf + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-pdf, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.pdf) +AC_PROG_LIBTOOL + +GB_COMPONENT_SEARCH( + poppler, POPPLER, gb.pdf, [src], + 'poppler >= 0.5', + [GB_FIND(PDFDoc.h, `pkg-config --variable=includedir poppler`, poppler)], + [], + [], + [] +) + +if test "$have_poppler" = "yes"; then + pkg-config --atleast-version=0.5 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_0_5, $((1-$?)), Poppler version >= 0.5) + pkg-config --atleast-version=0.5.91 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_0_6, $((1-$?)), Poppler version >= 0.6) + pkg-config --atleast-version=0.8.0 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_0_8, $((1-$?)), Poppler version >= 0.8) + pkg-config --atleast-version=0.11.3 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_0_11_3, $((1-$?)), Poppler version >= 0.11.3) + pkg-config --atleast-version=0.17.0 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_0_17, $((1-$?)), Poppler version >= 0.17) + pkg-config --atleast-version=0.20.0 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_0_20, $((1-$?)), Poppler version >= 0.20) + pkg-config --atleast-version=0.58.0 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_0_58, $((1-$?)), Poppler version >= 0.58) + pkg-config --atleast-version=0.64.0 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_0_64, $((1-$?)), Poppler version >= 0.64) + pkg-config --atleast-version=0.71.0 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_0_71, $((1-$?)), Poppler version >= 0.71) + pkg-config --atleast-version=0.72.0 poppler + AC_DEFINE_UNQUOTED(POPPLER_VERSION_0_72, $((1-$?)), Poppler version >= 0.72) +fi + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.pdf/gambas.h b/gb.pdf/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.pdf/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.pdf/gb.gtk.h b/gb.pdf/gb.gtk.h new file mode 120000 index 00000000..6719229a --- /dev/null +++ b/gb.pdf/gb.gtk.h @@ -0,0 +1 @@ +../gb.gtk/src/gb.gtk.h \ No newline at end of file diff --git a/gb.pdf/gb.image.h b/gb.pdf/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.pdf/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.pdf/gb_common.h b/gb.pdf/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.pdf/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.pdf/m4 b/gb.pdf/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.pdf/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.pdf/reconf b/gb.pdf/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.pdf/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.pdf/src/CPdfDocument.cpp b/gb.pdf/src/CPdfDocument.cpp new file mode 100644 index 00000000..3162f0de --- /dev/null +++ b/gb.pdf/src/CPdfDocument.cpp @@ -0,0 +1,1517 @@ +/*************************************************************************** + + CPdfDocument.cpp + + (C) 2005-2007 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPDFDOCUMENT_C + +#include "CPdfDocument.h" + +#include "gambas.h" +#include "main.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if POPPLER_VERSION_0_72 +#define getCString c_str +#endif + +/***************************************************************************/ + +static CPDFRECT *create_rect(void) +{ + return (CPDFRECT *)GB.New(GB.FindClass("PdfRect"), NULL, NULL); +} + +BEGIN_PROPERTY(PdfRect_X) + + GB.ReturnFloat(THIS_RECT->x); + +END_PROPERTY + +BEGIN_PROPERTY(PdfRect_Y) + + GB.ReturnFloat(THIS_RECT->y); + +END_PROPERTY + +BEGIN_PROPERTY(PdfRect_Width) + + GB.ReturnFloat(THIS_RECT->w); + +END_PROPERTY + +BEGIN_PROPERTY(PdfRect_Height) + + GB.ReturnFloat(THIS_RECT->h); + +END_PROPERTY + +BEGIN_PROPERTY(PdfRect_Right) + + GB.ReturnFloat(THIS_RECT->x + THIS_RECT->w); + +END_PROPERTY + +BEGIN_PROPERTY(PdfRect_Bottom) + + GB.ReturnFloat(THIS_RECT->y + THIS_RECT->h); + +END_PROPERTY + + + +/**************************************************************************** + + Translations from Poppler universe to Gambas universe + +****************************************************************************/ + +static void return_unicode_string(const Unicode *unicode, int len) +{ + static UnicodeMap *uMap = NULL; + + GooString gstr; + char buf[8]; /* 8 is enough for mapping an unicode char to a string */ + int i, n; + + if (uMap == NULL) + { + GooString *enc = new GooString("UTF-8"); + uMap = globalParams->getUnicodeMap(enc); + uMap->incRefCnt(); + delete enc; + } + + for (i = 0; i < len; ++i) { + n = uMap->mapUnicode(unicode[i], buf, sizeof(buf)); + gstr.append(buf, n); + } + + GB.ReturnNewZeroString(gstr.getCString()); +} + + +static void aux_return_string_info(void *_object, const char *key) +{ + Object obj; + Object dst; + const_GooString *goo_value; + Dict *info_dict; + char *tmpstr; + + #if POPPLER_VERSION_0_58 + obj = THIS->doc->getDocInfo (); + #else + THIS->doc->getDocInfo (&obj); + #endif + if (!obj.isDict()) { GB.ReturnNewZeroString(""); return; } + + info_dict=obj.getDict(); + #if POPPLER_VERSION_0_58 + dst = info_dict->lookup ((char *)key); + #else + info_dict->lookup ((char *)key, &dst); + #endif + if (!dst.isString ()) { GB.ReturnNewZeroString(""); } + else { + goo_value = dst.getString(); + + if (goo_value->hasUnicodeMarker()) + { + GB.ConvString (&tmpstr,goo_value->getCString()+2,goo_value->getLength()-2,"UTF-16BE","UTF-8"); + GB.ReturnNewZeroString(tmpstr); + } + else + GB.ReturnNewString(goo_value->getCString(),goo_value->getLength()); + } + #if ! POPPLER_VERSION_0_58 + dst.free(); + obj.free(); + #endif +} + +static void aux_return_date_info(void *_object, const char *key) +{ + // TODO: Y2K effect + GB_DATE_SERIAL ds; + GB_DATE ret; + Object obj; + Object dst; + const_GooString *goo; + Dict *info_dict; + char *datestr=NULL,*tofree=NULL; + int nnum; + + GB.ReturnDate(NULL); + + #if POPPLER_VERSION_0_58 + obj = THIS->doc->getDocInfo (); + #else + THIS->doc->getDocInfo (&obj); + #endif + if (!obj.isDict()) return; + + info_dict=obj.getDict(); + #if POPPLER_VERSION_0_58 + dst = info_dict->lookup ((char *)key); + #else + info_dict->lookup ((char *)key, &dst); + #endif + if (dst.isString ()) + { + goo = dst.getString(); + if (goo->hasUnicodeMarker()) + GB.ConvString (&datestr,goo->getCString()+2,goo->getLength()-2,"UTF-16BE","UTF-8"); + else + { + datestr = GB.NewString(goo->getCString(),goo->getLength()); + tofree=datestr; + } + + if (datestr) + { + if (datestr[0] == 'D' && datestr[1] == ':') datestr += 2; + nnum=sscanf(datestr, "%4d%2d%2d%2d%2d%2d",&ds.year, &ds.month, &ds.day, &ds.hour, &ds.min, &ds.sec); + if (nnum == 6) + { + if (!GB.MakeDate(&ds,&ret)) + GB.ReturnDate(&ret); + } + } + + } + + if (tofree) GB.FreeString(&tofree); + #if ! POPPLER_VERSION_0_58 + dst.free(); + obj.free(); + #endif +} + +static const_LinkDest *get_dest(const_LinkAction *act) +{ + if (!act) + return 0; + + switch (act->getKind()) + { + case actionGoTo: return ((LinkGoTo*)act)->getDest(); + case actionGoToR: return ((LinkGoToR*)act)->getDest(); + default: return 0; + } +} + +static uint32_t aux_get_page_from_action(void *_object, const_LinkAction *act) +{ + Ref pref; + const_LinkDest *dest = get_dest(act); + #if POPPLER_VERSION_0_6 + const_GooString *name; + #else + UGooString *name; + #endif + + if (!dest) + { + // try to use NamedDest to get dest + if (!act) + return 0; + if (act->getKind () == actionGoTo) + { + name = ((LinkGoTo*)act)->getNamedDest(); + if (name) { + #if POPPLER_VERSION_0_64 + dest = THIS->doc->findDest(name); + #elif POPPLER_VERSION_0_6 + dest = THIS->doc->findDest((GooString *) name); + #else + dest = THIS->doc->findDest((UGooString *) name); + #endif + } + } + } + + if (!dest) + return 0; + + if (dest->isPageRef() ) + { + pref= dest->getPageRef(); + return THIS->doc->findPage(pref.num, pref.gen); + } + else + return dest->getPageNum(); +} + + +static void aux_get_dimensions_from_action(const_LinkAction *act, CPDFRECT *rect) +{ + const_LinkDest *dest = get_dest(act); + if (!dest) + return; + + rect->x = dest->getLeft(); + rect->w = dest->getRight() - rect->x; + rect->y = dest->getTop(); + rect->h = dest->getBottom() - rect->y; +} + +static double aux_get_zoom_from_action(const_LinkAction *act) +{ + const_LinkDest *dest = get_dest(act); + if (dest) + return dest->getZoom(); + else + return 1; +} + +static char* aux_get_target_from_action(const_LinkAction *act) +{ + char *vl=NULL; + char *uni=NULL; + const_GooString *tmp=NULL; + + switch (act->getKind()) + { + case actionGoToR: + tmp=((LinkGoToR*)act)->getFileName(); break; + + case actionLaunch: + tmp=((LinkLaunch*)act)->getFileName(); break; + + case actionURI: + tmp=((LinkURI*)act)->getURI(); break; + + case actionNamed: + tmp=((LinkNamed*)act)->getName(); break; + + case actionMovie: + #if POPPLER_VERSION_0_8 + tmp=((LinkMovie*)act)->getAnnotTitle(); break; + #else + tmp=((LinkMovie*)act)->getTitle(); break; + #endif + + default: + break; + } + + if (!tmp) return NULL; + + if (tmp->hasUnicodeMarker()) + { + GB.ConvString (&uni,tmp->getCString()+2,tmp->getLength()-2,"UTF-16BE","UTF-8"); + vl = GB.AddString(vl, uni, 0); + } + else + vl = GB.AddString(vl,tmp->getCString(),tmp->getLength()); + + + return vl; + +} + +/***************************************************************************** + + PDF document + +******************************************************************************/ + + +static void free_all(void *_object) +{ + if (THIS->doc) + { + delete THIS->doc; + THIS->doc=NULL; + } + + if (THIS->dev) + { + delete THIS->dev; + THIS->dev=NULL; + } + + if (THIS->buf) + { + GB.ReleaseFile(THIS->buf,THIS->len); + THIS->buf=NULL; + } + + if (THIS->Found) + { + GB.FreeArray(POINTER(&THIS->Found)); + THIS->Found=NULL; + } + + if (THIS->links) + { + delete THIS->links; + THIS->links=NULL; + } + + if (THIS->pindex) + { + GB.FreeArray(POINTER(&THIS->pindex)); + GB.FreeArray(POINTER(&THIS->oldindex)); + THIS->pindex=NULL; + THIS->oldindex=NULL; + } + + THIS->index=NULL; + THIS->currpage=-1; +} + +BEGIN_METHOD_VOID (PDFDOCUMENT_free) + + free_all(_object); + +END_METHOD + +BEGIN_PROPERTY(PDFDOCUMENT_scale) + + if (READ_PROPERTY){ GB.ReturnFloat(THIS->scale); return; } + + if (VPROP(GB_FLOAT)>0) { THIS->scale = VPROP(GB_FLOAT); return; } + + GB.Error("Zoom must be a positive value"); + +END_PROPERTY + +BEGIN_PROPERTY(PDFDOCUMENT_rotation) + + int32_t rot; + + if (READ_PROPERTY) + { + GB.ReturnInteger(THIS->rotation); + return; + } + + rot=VPROP(GB_INTEGER); + + while (rot<0) rot+=360; + while (rot>=360) rot-=360; + + switch (rot) + { + case 0: + case 90: + case 180: + case 270: + THIS->rotation = VPROP(GB_INTEGER); + break; + } + +END_PROPERTY + + +int32_t open_document (void *_object, char *sfile, int32_t lfile) +{ + SplashColor white; + PDFDoc *test; + MemStream *stream; + Object obj; + Outline *outline; + char *buf=NULL; + int32_t len=0; + int32_t ret; + + + if ( GB.LoadFile(sfile,lfile,&buf,&len) ) return -1; + + #if POPPLER_VERSION_0_58 + stream=new MemStream(buf,0,(Guint)len,std::move(obj)); + #else + obj.initNull(); + stream=new MemStream(buf,0,(Guint)len,&obj); + #endif + test=new PDFDoc (stream,0,0); + + if (!test->isOk()) + { + GB.ReleaseFile(buf,len); + ret=test->getErrorCode(); + delete test; + test=NULL; + if (ret == errEncrypted) return -2; + return -3; + } + + free_all(_object); + + THIS->doc=test; + THIS->buf=buf; + THIS->len=len; + + white[0] = 0xFF; white[1] = 0xFF; white[2] = 0xFF; + THIS->dev=new SplashOutputDev(splashModeRGB8, 3, false, white); + + #if POPPLER_VERSION_0_20 + THIS->dev->startDoc(THIS->doc); + #else + THIS->dev->startDoc(THIS->doc->getXRef ()); + #endif + + outline=THIS->doc->getOutline(); + if (outline) THIS->index=outline->getItems(); + + //if (THIS->index) + // if (!THIS->index->getLength()) THIS->index=NULL; + + THIS->currindex=0; + THIS->currpage=-1; + + return 0; + +} + + +BEGIN_METHOD(PDFDOCUMENT_new, GB_STRING File) + + THIS->scale = 1; + THIS->rotation = 0; + + if (!MISSING(File)) + { + switch (open_document( _object, STRING(File), LENGTH(File)) ) + { + case -1: GB.Error("File not found"); return; + case -2: GB.Error("PDF is encrypted"); return; + case -3: GB.Error("Bad PDF File"); return; + } + } + +END_METHOD + +BEGIN_METHOD (PDFDOCUMENT_open, GB_STRING File;) + + switch (open_document( _object, STRING(File), LENGTH(File)) ) + { + case -1: GB.Error("File not found"); return; + case -2: GB.Error("PDF is encrypted"); return; + case -3: GB.Error("Bad PDF File"); return; + } + +END_METHOD + +BEGIN_METHOD_VOID(PDFDOCUMENT_close) + + free_all(_object); + +END_METHOD + +BEGIN_METHOD(PDFDOCUMENT_get,GB_INTEGER index;) + + if (!THIS->doc || (VARG(index)<1) || ( VARG(index)>THIS->doc->getNumPages() ) ) + { + GB.Error("Invalid page number"); + return; + } + + if (THIS->currpage != (uint32_t)VARG(index) ) + { + if (THIS->Found) + { + GB.FreeArray(POINTER(&THIS->Found)); + THIS->Found=NULL; + } + + if (THIS->links) + { + delete THIS->links; + THIS->links=NULL; + } + + THIS->page=THIS->doc->getCatalog()->getPage(VARG(index)); + THIS->currpage=VARG(index); + } + + RETURN_SELF(); + +END_METHOD + +BEGIN_PROPERTY(PDFDOCUMENT_ready) + + GB.ReturnBoolean( (bool)THIS->doc ); + +END_PROPERTY + +BEGIN_PROPERTY(PDFDOCUMENT_count) + + GB.ReturnInteger( (int32_t) (THIS->doc ? THIS->doc->getNumPages() : 0)); + +END_PROPERTY + +BEGIN_PROPERTY(PDFDOCUMENT_info) + + if (THIS->doc) RETURN_SELF(); + else GB.ReturnNull(); + +END_PROPERTY + +/***************************************************************************** + +PDF document information + +******************************************************************************/ + +BEGIN_PROPERTY(PDFINFO_title) + + aux_return_string_info(_object,"Title"); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_format) + + char ctx[16]; + #if POPPLER_VERSION_0_11_3 + snprintf(ctx, sizeof(ctx), "%.2g", THIS->doc->getPDFMajorVersion () + THIS->doc->getPDFMinorVersion() / 10.0); + #else + snprintf(ctx, sizeof(ctx), "%.2g", THIS->doc->getPDFVersion()); + #endif + GB.ReturnNewZeroString(ctx); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_author) + + aux_return_string_info(_object,"Author"); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_subject) + + aux_return_string_info(_object,"Subject"); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_keywords) + + aux_return_string_info(_object,"Keywords"); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_creator) + + aux_return_string_info(_object,"Creator"); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_producer) + + aux_return_string_info(_object,"Producer"); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_linearized) + + GB.ReturnBoolean(THIS->doc->isLinearized()); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_layout) + + Catalog *catalog; + + catalog=THIS->doc->getCatalog(); + if (!catalog) { GB.ReturnInteger(Catalog::pageLayoutNone); return; } + if (!catalog->isOk()) { GB.ReturnInteger(Catalog::pageLayoutNone); return; } + + GB.ReturnInteger(catalog->getPageLayout()); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_mode) + + Catalog *catalog; + + catalog=THIS->doc->getCatalog(); + if (!catalog) { GB.ReturnInteger(Catalog::pageModeNone); return; } + if (!catalog->isOk()) { GB.ReturnInteger(Catalog::pageModeNone); return; } + + GB.ReturnInteger(catalog->getPageMode()); + + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_canprint) + + GB.ReturnBoolean(THIS->doc->okToPrint()); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_canmodify) + + GB.ReturnBoolean(THIS->doc->okToChange()); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_cancopy) + + GB.ReturnBoolean(THIS->doc->okToCopy()); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_canaddnotes) + + GB.ReturnBoolean(THIS->doc->okToAddNotes()); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_creation) + + aux_return_date_info(_object,"CreationDate"); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINFO_modification) + + aux_return_date_info(_object,"ModDate"); + +END_PROPERTY + + +/***************************************************************************** + +PDF document index + +******************************************************************************/ + + +BEGIN_PROPERTY(PDFDOCUMENT_has_index) + + GB.ReturnBoolean(THIS->index && THIS->index->getLength()); + +END_PROPERTY + +BEGIN_PROPERTY(PDFDOCUMENT_index) + + if (!THIS->index) { GB.ReturnNull(); return; } + + THIS->action=((OutlineItem*)THIS->index->get(THIS->currindex))->getAction(); + RETURN_SELF(); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINDEX_count) + + GB.ReturnInteger(THIS->index->getLength()); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINDEX_has_children) + + OutlineItem *item; + + item = (OutlineItem *)THIS->index->get (THIS->currindex); + GB.ReturnBoolean(item->getKids() && item->getKids()->getLength()); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINDEX_is_open) + + OutlineItem *item; + + item = (OutlineItem *)THIS->index->get (THIS->currindex); + + if (READ_PROPERTY) + { GB.ReturnBoolean(item->isOpen()); return; } + + if (VPROP(GB_INTEGER)) item->open(); + else item->close(); + +END_PROPERTY + +BEGIN_PROPERTY(PDFINDEX_title) + + OutlineItem *item; + + item = (OutlineItem *)THIS->index->get (THIS->currindex); + return_unicode_string(item->getTitle(), item->getTitleLength()); + +END_PROPERTY + + +BEGIN_METHOD_VOID(PDFINDEX_root) + + Outline *outline; + + outline=THIS->doc->getOutline(); + if (outline) THIS->index=outline->getItems(); + THIS->currindex=0; + if (THIS->pindex) { GB.FreeArray(POINTER(&THIS->pindex)); THIS->pindex=NULL; } + if (THIS->oldindex) { GB.FreeArray(POINTER(&THIS->oldindex)); THIS->oldindex=NULL; } + +END_METHOD + +BEGIN_METHOD_VOID(PDFINDEX_prev) + + if (!THIS->currindex) { GB.ReturnBoolean(true); return; } + + THIS->currindex--; + GB.ReturnBoolean(false); + +END_METHOD + +BEGIN_METHOD_VOID(PDFINDEX_next) + + if ( (THIS->currindex+1) >= (uint32_t)THIS->index->getLength() ) + { GB.ReturnBoolean(true); return; } + + THIS->currindex++; + GB.ReturnBoolean(false); + +END_METHOD + +BEGIN_METHOD_VOID(PDFINDEX_child) + + OutlineItem *item; + + item = (OutlineItem *)THIS->index->get (THIS->currindex); + + if (!item->hasKids() || item->getKids()->getLength() == 0) { GB.ReturnBoolean(true); return; } + + if (THIS->pindex) + { + GB.Add(POINTER(&THIS->pindex)); + GB.Add(POINTER(&THIS->oldindex)); + } + else + { + GB.NewArray(POINTER(&THIS->pindex),sizeof(void*),1); + GB.NewArray(POINTER(&THIS->oldindex),sizeof(uint32_t),1); + } + + if (!item->isOpen()) item->open(); + THIS->pindex[GB.Count(POINTER(THIS->pindex))-1]=(void*)THIS->index; + THIS->oldindex[GB.Count(POINTER(THIS->pindex))-1]=THIS->currindex; + THIS->index=item->getKids(); + THIS->currindex=0; + + GB.ReturnBoolean(false); + +END_METHOD + +BEGIN_METHOD_VOID(PDFINDEX_parent) + + if (!THIS->pindex) { GB.ReturnBoolean(true); return; } + + THIS->index=(GooList*)THIS->pindex[GB.Count(POINTER(THIS->pindex))-1]; + THIS->currindex=THIS->oldindex[GB.Count(POINTER(THIS->pindex))-1]; + if (GB.Count(POINTER(THIS->pindex))==1) + { + GB.FreeArray(POINTER(&THIS->pindex)); + GB.FreeArray(POINTER(&THIS->oldindex)); + THIS->oldindex=NULL; + THIS->pindex=NULL; + } + else + { + GB.Remove(POINTER(&THIS->pindex),GB.Count(POINTER(THIS->pindex))-1,1); + GB.Remove(POINTER(&THIS->oldindex),GB.Count(POINTER(THIS->oldindex))-1,1); + } + + GB.ReturnBoolean(false); + +END_METHOD + +/***************************************************************************** + + PDF pages + +******************************************************************************/ + +static int get_rotation(void *_object) +{ + return (THIS->rotation + THIS->page->getRotate() + 720) % 360; +} + +static void get_page_size(void *_object, int *w, int *h) +{ + int rotation = get_rotation(THIS); + + if (rotation == 90 || rotation == 270) + { + if (w) *w = (int)(THIS->page->getMediaHeight() * THIS->scale); + if (h) *h = (int)(THIS->page->getMediaWidth() * THIS->scale); + } + else + { + if (w) *w = (int)(THIS->page->getMediaWidth() * THIS->scale); + if (h) *h = (int)(THIS->page->getMediaHeight() * THIS->scale); + } +} + +BEGIN_PROPERTY (PDFPAGE_width) + + int w; + get_page_size(THIS, &w, NULL); + GB.ReturnInteger(w); + +END_PROPERTY + +BEGIN_PROPERTY (PDFPAGE_height) + + int h; + get_page_size(THIS, NULL, &h); + GB.ReturnInteger(h); + +END_PROPERTY + +static uint32_t *get_page_data(CPDFDOCUMENT *_object, int32_t x, int32_t y, int32_t *width, int32_t *height, double scale, int32_t rotation) +{ + SplashBitmap *map; + uint32_t *data; + int32_t w, h; + int rw; + int rh; + + get_page_size(THIS, &rw, &rh); + + w = *width; + h = *height; + + if (w < 0) w = rw; + if (h < 0) h = rh; + + if (x<0) x=0; + if (y<0) y=0; + if (w<1) w=1; + if (h<1) h=1; + + + if ( (x+w) > rw ) w=rw-x; + if ( (y+h) > rh ) h=rh-y; + + if ( (w<0) || (h<0) ) return NULL; + + #if POPPLER_VERSION_0_20 + THIS->page->displaySlice(THIS->dev,72.0*scale,72.0*scale, + rotation, + false, + true, + x,y,w,h, + false); + #else + THIS->page->displaySlice(THIS->dev,72.0*scale,72.0*scale, + rotation, + false, + true, + x,y,w,h, + false, + THIS->doc->getCatalog ()); + #endif + + map=THIS->dev->getBitmap(); + + data=(uint32_t*)map->getDataPtr(); + + + *width = w; + *height = h; + + return data; +} + +BEGIN_METHOD(PDFPAGE_image, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + uint32_t *data; + int32_t x,y, w, h; + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + w = VARGOPT(w, -1); + h = VARGOPT(h, -1); + + data = get_page_data(THIS, x, y, &w, &h, THIS->scale, THIS->rotation); + if (!data) { GB.ReturnNull(); return; } + /*GB.Image.Create(&img, data, w, h, GB_IMAGE_RGB); + GB.ReturnObject(img);*/ + + GB.ReturnObject(IMAGE.Create(w, h, GB_IMAGE_RGB, (unsigned char *)data)); + +END_METHOD + +BEGIN_PROPERTY (PDFPAGE_property_image) + + int32_t w=-1; + int32_t h=-1; + uint32_t *data; + + data = get_page_data(THIS, 0, 0, &w, &h, THIS->scale, THIS->rotation); + if (!data) { GB.ReturnNull(); return; } + /*GB.Image.Create(&img, data, w, h, GB_IMAGE_RGB); + GB.ReturnObject(img);*/ + + GB.ReturnObject(IMAGE.Create(w, h, GB_IMAGE_RGB, (unsigned char *)data)); + +END_PROPERTY + +BEGIN_METHOD(PDFPAGE_select, GB_INTEGER X; GB_INTEGER Y; GB_INTEGER W; GB_INTEGER H) + + TextOutputDev *dev; + GooString *str; + Gfx *gfx; + int32_t x,y,w,h; + + x = VARGOPT(X, 0); + y = VARGOPT(Y, 0); + w = VARGOPT(W, (int32_t)THIS->page->getMediaWidth()); + h = VARGOPT(H, (int32_t)THIS->page->getMediaHeight()); + + #if POPPLER_VERSION_0_20 + dev = new TextOutputDev (NULL, true, 0, false, false); + gfx = THIS->page->createGfx(dev,72.0,72.0,0,false,true,-1, -1, -1, -1, false, NULL, NULL); + #else + dev = new TextOutputDev (NULL, true, false, false); + gfx = THIS->page->createGfx(dev,72.0,72.0,0,false,true,-1, -1, -1, -1, false,THIS->doc->getCatalog (),NULL, NULL, NULL, NULL); + #endif + + THIS->page->display(gfx); + dev->endPage(); + + str=dev->getText((double)x,(double)y,(double)(w+x),(double)(h+y)); + + delete gfx; + delete dev; + + if (!str) + { + GB.ReturnNewZeroString(""); + return; + } + + GB.ReturnNewString(str->getCString(),str->getLength()); + delete str; + +END_METHOD + +/***************************************************************************** + + Bookmarks of a PDF page + +******************************************************************************/ + +void aux_fill_links(void *_object) +{ + #if POPPLER_VERSION_0_20 + THIS->links = new Links (THIS->page->getAnnots ()); + #elif POPPLER_VERSION_0_17 + THIS->links = new Links (THIS->page->getAnnots (THIS->doc->getCatalog())); + #else + Object obj; + + THIS->links = new Links (THIS->page->getAnnots (&obj),THIS->doc->getCatalog()->getBaseURI ()); + obj.free(); + #endif +} + +BEGIN_PROPERTY (PDFPAGELINKS_count) + + if (!THIS->links) aux_fill_links(_object); + if (!THIS->links) { GB.ReturnInteger(0); return; } + GB.ReturnInteger(THIS->links->getNumLinks()); + + +END_PROPERTY + +BEGIN_METHOD (PDFPAGELINKS_get,GB_INTEGER ind;) + + bool pok=true; + + if (!THIS->links) aux_fill_links(_object); + if (!THIS->links) pok=false; + else + { + if (VARG(ind)<0) pok=false; + else + { + if (VARG(ind)>=THIS->links->getNumLinks()) pok=false; + } + } + + if (!pok) { GB.Error("Out of bounds"); return; } + + THIS->lcurrent=VARG(ind); + THIS->action=THIS->links->getLink(THIS->lcurrent)->getAction(); + + RETURN_SELF(); + +END_METHOD + +BEGIN_PROPERTY (PDFPAGELINKDATA_parameters) + + if (THIS->action->getKind() != actionLaunch ) + { + GB.ReturnNewZeroString(""); + return; + } + + GB.ReturnNewZeroString(((LinkLaunch*)THIS->action)->getParams()->getCString()); + +END_PROPERTY + +BEGIN_PROPERTY (PDFPAGELINKDATA_uri) + + char *uri; + + uri=aux_get_target_from_action(THIS->action); + + GB.ReturnNewZeroString(uri); + if (uri) GB.FreeString(&uri); + +END_PROPERTY + +BEGIN_PROPERTY(PdfPageLinkData_Rect) + + CPDFRECT *rect = create_rect(); + aux_get_dimensions_from_action(THIS->action, rect); + GB.ReturnObject(rect); + +END_PROPERTY + +BEGIN_PROPERTY(PDFPAGELINKDATA_zoom) + + GB.ReturnFloat(aux_get_zoom_from_action(THIS->action)); + +END_PROPERTY + +BEGIN_PROPERTY(PDFPAGELINKDATA_page) + + GB.ReturnInteger(aux_get_page_from_action(_object,THIS->action)); + +END_PROPERTY + +BEGIN_PROPERTY (PDFPAGELINKDATA_type) + + GB.ReturnInteger ( (int32_t)THIS->action->getKind() ); + +END_PROPERTY + +BEGIN_PROPERTY(PDFPAGELINKDATA_check) + + if (THIS->action) + RETURN_SELF(); + else + GB.ReturnNull(); + +END_PROPERTY + +static void aux_get_link_dimensions(void *_object, CPDFRECT *rect) +{ + double l,t,w,h; + double pw,ph; + + pw=THIS->page->getMediaWidth(); + ph=THIS->page->getMediaHeight(); + + THIS->links->getLink(THIS->lcurrent)->getRect(&l, &t, &w, &h); + w=w-l; + h=h-t; + + switch (get_rotation(THIS)) + { + case 0: + rect->x = (l*THIS->scale); + rect->y = ((ph-t-h)*THIS->scale); + rect->w = (w*THIS->scale); + rect->h = (h*THIS->scale); + break; + + case 90: + rect->y = (l*THIS->scale); + rect->x = (t*THIS->scale); + rect->h = (w*THIS->scale); + rect->w = (h*THIS->scale); + break; + + case 180: + rect->x = ((l-w)*THIS->scale); + rect->y = (t*THIS->scale); + rect->w = (w*THIS->scale); + rect->h = (h*THIS->scale); + break; + + case 270: + rect->y = ((pw-l-w)*THIS->scale); + rect->x = ((ph-t-h)*THIS->scale); + rect->h = (w*THIS->scale); + rect->w = (h*THIS->scale); + break; + } +} + +BEGIN_PROPERTY(PdfPageLink_rect) + + CPDFRECT *rect = create_rect(); + aux_get_link_dimensions(THIS, rect); + GB.ReturnObject(rect); + +END_PROPERTY + + +/***************************************************************************** + + Finding a text in a PDF page + +******************************************************************************/ + +BEGIN_METHOD (PDFPAGE_find,GB_STRING Text; GB_BOOLEAN Sensitive;) + + TextOutputDev *textdev; + double x0=0, y0=0; + double x1, y1; + CPDFFIND *el; + Unicode *block=NULL; + int nlen=0; + bool sensitive=false; + int count; + double x, y, w, h, wp, hp; + int rotation; + + // TODO: Use UCS-4BE on big endian systems? + if (GB.ConvString ((char **)(void *)&block,STRING(Text),LENGTH(Text),"UTF-8",GB_SC_UNICODE)) + { + GB.Error("Invalid UTF-8 string"); + return; + } + + nlen=GB.StringLength((char*)block)/sizeof(Unicode); + + if (!MISSING(Sensitive)) sensitive=VARG(Sensitive); + + #if POPPLER_VERSION_0_20 + textdev = new TextOutputDev (NULL, true, 0, false, false); + THIS->page->display (textdev, 72, 72, 0, false, false, false); + #else + textdev = new TextOutputDev (NULL, true, false, false); + THIS->page->display (textdev, 72, 72, 0, false, false, false, THIS->doc->getCatalog()); + #endif + + if (THIS->Found) { GB.FreeArray(POINTER(&THIS->Found)); THIS->Found=NULL; } + + count = 0; + #if POPPLER_VERSION_0_20 + while (textdev->findText (block,nlen,false,true,true,false,sensitive,false,false,&x0,&y0,&x1,&y1)) + #else + while (textdev->findText (block,nlen,false,true,true,false,sensitive,false,&x0,&y0,&x1,&y1)) + #endif + { + if (!THIS->Found) + GB.NewArray(POINTER(&THIS->Found),sizeof(CPDFFIND),1); + else + GB.Add(POINTER(&THIS->Found)); + + el = &(THIS->Found[count++]); //(CPDFFIND*)&((CPDFFIND*)THIS->Found)[GB.Count(POINTER(THIS->Found))-1]; + + x = x0; + y = y0; + w = x1 - x0; + h = y1 - y0; + + wp = THIS->page->getMediaWidth(); + hp = THIS->page->getMediaHeight(); + rotation = THIS->page->getRotate(); + if (rotation == 90 || rotation == 270) + { + x0 = wp; wp = hp; hp = x0; + } + + rotation = THIS->rotation; //get_rotation(THIS); + while (rotation > 0) + { + x0 = wp; wp = hp; hp = x0; + + x0 = wp - y - h; + y0 = x; + + x = w; w = h; h = x; + + x = x0; + y = y0; + + rotation -= 90; + } + + el->x0 = x * THIS->scale; + el->y0 = y * THIS->scale; + el->x1 = w * THIS->scale; + el->y1 = h * THIS->scale; + } + + delete textdev; + + GB.ReturnBoolean(count == 0); + +END_METHOD + + +BEGIN_METHOD(PDFPAGERESULT_get,GB_INTEGER Index) + + CPDFRECT *rect; + CPDFFIND *el; + int index; + + index = VARG(Index); + + if (!THIS->Found || index < 0 || index >= GB.Count(THIS->Found)) + { + GB.Error("Out of bounds"); + return; + } + + el = &(THIS->Found[index]); + rect = create_rect(); + + rect->x = el->x0; + rect->y = el->y0; + rect->w = el->x1; + rect->h = el->y1; + + GB.ReturnObject(rect); + +END_METHOD + +BEGIN_PROPERTY (PDFPAGERESULT_count) + + if (!THIS->Found) { GB.ReturnInteger(0); return; } + GB.ReturnInteger( GB.Count(POINTER(THIS->Found)) ); + +END_PROPERTY + + +/********************************************************************** + +Gambas Interface + +***********************************************************************/ + +GB_DESC PdfRectDesc[] = +{ + GB_DECLARE("PdfRect", sizeof(CPDFRECT)), GB_NOT_CREATABLE(), + + GB_PROPERTY_READ("X", "f", PdfRect_X), + GB_PROPERTY_READ("Y", "f", PdfRect_Y), + GB_PROPERTY_READ("Width", "f", PdfRect_Width), + GB_PROPERTY_READ("Height", "f", PdfRect_Height), + GB_PROPERTY_READ("W", "f", PdfRect_Width), + GB_PROPERTY_READ("H", "f", PdfRect_Height), + GB_PROPERTY_READ("Left", "f", PdfRect_X), + GB_PROPERTY_READ("Top", "f", PdfRect_Y), + GB_PROPERTY_READ("Right", "f", PdfRect_Right), + GB_PROPERTY_READ("Bottom", "f", PdfRect_Bottom), + + GB_END_DECLARE +}; + + +GB_DESC PdfResultDesc[]= +{ + GB_DECLARE(".PdfDocumentPage.Result",0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get","PdfRect",PDFPAGERESULT_get,"(Index)i"), + GB_PROPERTY_READ("Count","i",PDFPAGERESULT_count), + + GB_END_DECLARE +}; + + +GB_DESC PdfLinkDataDesc[]= +{ + GB_DECLARE(".PdfDocumentPage.Link.Data",0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Type","i",PDFPAGELINKDATA_type), + GB_PROPERTY_READ("Target","s",PDFPAGELINKDATA_uri), + GB_PROPERTY_READ("Parameters","s",PDFPAGELINKDATA_parameters), + GB_PROPERTY_READ("Page","i",PDFPAGELINKDATA_page), + GB_PROPERTY_READ("Zoom","f",PDFPAGELINKDATA_zoom), + GB_PROPERTY_READ("Rect", "PdfRect", PdfPageLinkData_Rect), + + GB_END_DECLARE +}; + + +GB_DESC PdfLinkDesc[]= +{ + GB_DECLARE(".PdfDocumentPage.Link",0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Rect", "PdfRect", PdfPageLink_rect), + GB_PROPERTY_READ("Data",".PdfDocumentPage.Link.Data", PDFPAGELINKDATA_check), + + GB_END_DECLARE +}; + + +GB_DESC PdfIndexDesc[]= +{ + GB_DECLARE(".PdfDocument.Index",0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Expanded","b",PDFINDEX_is_open), + GB_PROPERTY_READ("Count","i",PDFINDEX_count), + GB_PROPERTY_READ("HasChildren","b",PDFINDEX_has_children), + GB_PROPERTY_READ("Title","s",PDFINDEX_title), + GB_PROPERTY_READ("Text","s",PDFINDEX_title), + + GB_PROPERTY_READ("Data", ".PdfDocumentPage.Link.Data", PDFPAGELINKDATA_check), + GB_METHOD("MovePrevious","b",PDFINDEX_prev,0), + GB_METHOD("MoveNext","b",PDFINDEX_next,0), + GB_METHOD("MoveChild","b",PDFINDEX_child,0), + GB_METHOD("MoveParent","b",PDFINDEX_parent,0), + GB_METHOD("MoveRoot",0,PDFINDEX_root,0), + + GB_END_DECLARE +}; + + +GB_DESC PdfPageDesc[]= +{ + GB_DECLARE(".PdfDocumentPage",0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("W","f",PDFPAGE_width), + GB_PROPERTY_READ("H","f",PDFPAGE_height), + GB_PROPERTY_READ("Width","f",PDFPAGE_width), + GB_PROPERTY_READ("Height","f",PDFPAGE_height), + + GB_PROPERTY_READ("Image","Image",PDFPAGE_property_image), + GB_PROPERTY_SELF("Result",".PdfDocumentPage.Result"), + + GB_METHOD("GetImage","Image",PDFPAGE_image,"[(X)i(Y)i(Width)i(Height)i]"), + GB_METHOD("Find","b",PDFPAGE_find,"(Text)s[(CaseSensitive)b]"), + GB_METHOD("Select","s",PDFPAGE_select,"[(X)i(Y)i(W)i(H)i]"), + + GB_METHOD("_get",".PdfDocumentPage.Link",PDFPAGELINKS_get,"(Index)i"), + GB_PROPERTY_READ("Count","i",PDFPAGELINKS_count), + + GB_END_DECLARE +}; + + +GB_DESC PdfDocumentInfo[] = +{ + GB_DECLARE(".PdfDocument.Info",0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Title","s",PDFINFO_title), + GB_PROPERTY_READ("Format","s",PDFINFO_format), + GB_PROPERTY_READ("Author","s",PDFINFO_author), + GB_PROPERTY_READ("Subject","s",PDFINFO_subject), + GB_PROPERTY_READ("Keywords","s",PDFINFO_keywords), + GB_PROPERTY_READ("Creator","s",PDFINFO_creator), + GB_PROPERTY_READ("Producer","s",PDFINFO_producer), + GB_PROPERTY_READ("CreationDate","d",PDFINFO_creation), + GB_PROPERTY_READ("ModificationDate","d",PDFINFO_modification), + GB_PROPERTY_READ("Linearized","b",PDFINFO_linearized), + GB_PROPERTY_READ("Layout","i",PDFINFO_layout), + GB_PROPERTY_READ("Mode","i",PDFINFO_mode), + GB_PROPERTY_READ("CanCopy","b",PDFINFO_cancopy), + GB_PROPERTY_READ("CanModify","b",PDFINFO_canmodify), + GB_PROPERTY_READ("CanPrint","b",PDFINFO_canprint), + GB_PROPERTY_READ("CanAddNotes","b",PDFINFO_canaddnotes), + + GB_END_DECLARE +}; + + +GB_DESC PdfLayoutDesc[] = +{ + GB_DECLARE("PdfLayout", 0), GB_NOT_CREATABLE(), + + GB_CONSTANT("Unset","i",Catalog::pageLayoutNone), + GB_CONSTANT("SinglePage","i",Catalog::pageLayoutSinglePage), + GB_CONSTANT("OneColumn","i",Catalog::pageLayoutOneColumn), + GB_CONSTANT("TwoColumnLeft","i",Catalog::pageLayoutTwoColumnLeft), + GB_CONSTANT("TwoColumnRight","i",Catalog::pageLayoutTwoColumnRight), + GB_CONSTANT("TwoPageLeft","i",Catalog::pageLayoutTwoPageLeft), + GB_CONSTANT("TwoPageRight","i",Catalog::pageLayoutTwoPageRight), + + GB_END_DECLARE +}; + + +GB_DESC PdfModeDesc[] = +{ + GB_DECLARE("PdfPageMode",0), GB_NOT_CREATABLE(), + + GB_CONSTANT("Unset","i",Catalog::pageModeNone), + GB_CONSTANT("UseOutlines","i",Catalog::pageModeOutlines), + GB_CONSTANT("UseThumbs","i",Catalog::pageModeThumbs), + GB_CONSTANT("FullScreen","i",Catalog::pageModeFullScreen), + GB_CONSTANT("UseOC","i",Catalog::pageModeOC), + GB_CONSTANT("UseAttachments","i",Catalog::pageModeAttach), + + GB_END_DECLARE +}; + +GB_DESC PdfDocumentDesc[] = +{ + GB_DECLARE("PdfDocument", sizeof(CPDFDOCUMENT)), + + GB_CONSTANT("Unknown","i",actionUnknown), /* unknown action */ + GB_CONSTANT("Goto","i",actionGoTo), /* go to destination */ + GB_CONSTANT("GotoRemote","i",actionGoToR), /* go to destination in new file */ + GB_CONSTANT("Launch","i",actionLaunch), /* launch app or open doc. */ + GB_CONSTANT("Uri","i",actionURI), /* URI */ + GB_CONSTANT("Named","i",actionNamed), /* named action*/ + GB_CONSTANT("Movie","i",actionMovie), /* movie action */ + + GB_CONSTANT("Normal","i",0), + GB_CONSTANT("Sideways","i",90), + GB_CONSTANT("Inverted","i",180), + GB_CONSTANT("SidewaysInverted","i",270), + + GB_METHOD("_new", 0, PDFDOCUMENT_new, "[(File)s]"), + GB_METHOD("_free", 0, PDFDOCUMENT_free, 0), + + GB_METHOD("Open",0,PDFDOCUMENT_open,"(File)s"), + GB_METHOD("Close",0,PDFDOCUMENT_close,0), + GB_METHOD("_get",".PdfDocumentPage",PDFDOCUMENT_get,"(Index)i"), + + GB_PROPERTY("Zoom", "f", PDFDOCUMENT_scale), + GB_PROPERTY("Orientation", "i", PDFDOCUMENT_rotation), + + GB_PROPERTY_READ("Ready","b",PDFDOCUMENT_ready), + GB_PROPERTY_READ("Count","i",PDFDOCUMENT_count), + GB_PROPERTY_READ("HasIndex","b",PDFDOCUMENT_has_index), + GB_PROPERTY_READ("Index",".PdfDocument.Index",PDFDOCUMENT_index), + GB_PROPERTY_READ("Info",".PdfDocument.Info",PDFDOCUMENT_info), + + GB_END_DECLARE +}; + + diff --git a/gb.pdf/src/CPdfDocument.h b/gb.pdf/src/CPdfDocument.h new file mode 100644 index 00000000..fa80d47a --- /dev/null +++ b/gb.pdf/src/CPdfDocument.h @@ -0,0 +1,115 @@ +/*************************************************************************** + + CPdfDocument.h + + (C) 2005-2007 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPDFDOCUMENT_H +#define __CPDFDOCUMENT_H + +#include "gambas.h" + +#include +#include +#include +#include +#include + +#if POPPLER_VERSION_0_64 +#define const_LinkAction const LinkAction +#define const_LinkDest const LinkDest +#define const_GooList const GooList +#define const_GooString const GooString +#else +#define const_LinkAction LinkAction +#define const_LinkDest LinkDest +#define const_GooList GooList +#define const_GooString GooString +#endif + +#ifndef __CPDFDOCUMENT_C + +extern GB_DESC PdfRectDesc[]; +extern GB_DESC PdfDocumentDesc[]; +extern GB_DESC PdfPageDesc[]; +extern GB_DESC PdfResultDesc[]; +extern GB_DESC PdfLinkDesc[]; +extern GB_DESC PdfLinkDataDesc[]; +extern GB_DESC PdfIndexDesc[]; +extern GB_DESC PdfDocumentInfo[]; +extern GB_DESC PdfLayoutDesc[]; +extern GB_DESC PdfModeDesc[]; + +#else + +#define THIS ((CPDFDOCUMENT *)_object) +#define THIS_RECT ((CPDFRECT *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + double x, y, w, h; + } + CPDFRECT; + +typedef + struct { + double x0; + double y0; + double x1; + double y1; + } + CPDFFIND; + +typedef + struct { + GB_BASE ob; + + char *buf; + int len; + + PDFDoc *doc; + SplashOutputDev *dev; + Page *page; + uint currpage; + + void **pindex; // Parent of current index entries + const_GooList *index; // Current entries + + uint currindex; // Current entry + uint *oldindex; // Parent entry + + Links *links; // Page bookmarks + uint lcurrent; // Current bookmark + + CPDFFIND *Found; // Found text elements + + const_LinkAction *action; // Current link action + + double scale; + int rotation; + } + CPDFDOCUMENT; + + + +#endif diff --git a/gb.pdf/src/Makefile.am b/gb.pdf/src/Makefile.am new file mode 100644 index 00000000..eda3f0f3 --- /dev/null +++ b/gb.pdf/src/Makefile.am @@ -0,0 +1,16 @@ +COMPONENT = gb.pdf +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.pdf.la + +gb_pdf_la_LIBADD = @POPPLER_LIB@ +gb_pdf_la_LDFLAGS = -module @LD_FLAGS@ @POPPLER_LDFLAGS@ +gb_pdf_la_CXXFLAGS = $(AM_CXXFLAGS) $(GB_CXXFLAGS_STD_CPP11) +gb_pdf_la_CPPFLAGS = @POPPLER_INC@ + +gb_pdf_la_SOURCES = main.h main.cpp \ + CPdfDocument.h CPdfDocument.cpp + + + + diff --git a/gb.pdf/src/gb.pdf.component b/gb.pdf/src/gb.pdf.component new file mode 100644 index 00000000..51434485 --- /dev/null +++ b/gb.pdf/src/gb.pdf.component @@ -0,0 +1,8 @@ +[Component] +Key=gb.pdf +Author=Daniel Campos Fernández,Benoît Minisini,Ian Haywood +Requires=gb.image +State=Stable + + + diff --git a/gb.pdf/src/main.cpp b/gb.pdf/src/main.cpp new file mode 100644 index 00000000..93af4b1f --- /dev/null +++ b/gb.pdf/src/main.cpp @@ -0,0 +1,85 @@ +/*************************************************************************** + + main.cpp + + (C) 2005 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "CPdfDocument.h" +#include "main.h" + +#include + +#include + +extern "C" { + +GB_INTERFACE GB EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + PdfRectDesc, + PdfDocumentDesc, + PdfPageDesc, + PdfResultDesc, + PdfIndexDesc, + PdfLinkDesc, + PdfLinkDataDesc, + PdfDocumentInfo, + PdfLayoutDesc, + PdfModeDesc, + NULL +}; + + +int EXPORT GB_INIT(void) +{ + if (!globalParams) + { +#if POPPLER_VERSION_0_6 + globalParams = new GlobalParams(); +#else + globalParams = new GlobalParams("/etc/xpdfrc"); +#endif + +#if POPPLER_VERSION_0_5 +#else + globalParams->setupBaseFontsFc(NULL); +#endif + } + + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + + return 0; +} + + + +void EXPORT GB_EXIT() +{ + +} + + +} + + diff --git a/gb.pdf/src/main.h b/gb.pdf/src/main.h new file mode 100644 index 00000000..2877c7d3 --- /dev/null +++ b/gb.pdf/src/main.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + main.h + + (C) 2005 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb.image.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +#endif + +#endif diff --git a/gb.qt4/AUTHORS b/gb.qt4/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.qt4/COPYING b/gb.qt4/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.qt4/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.qt4/ChangeLog b/gb.qt4/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.qt4/INSTALL b/gb.qt4/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.qt4/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.qt4/Makefile.am b/gb.qt4/Makefile.am new file mode 100644 index 00000000..a3db3e0d --- /dev/null +++ b/gb.qt4/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @QT_DIR@ +EXTRA_DIST = reconf share gb*.h gambas.h diff --git a/gb.qt4/NEWS b/gb.qt4/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.qt4/README b/gb.qt4/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.qt4/acinclude.m4 b/gb.qt4/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.qt4/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.qt4/component.am b/gb.qt4/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.qt4/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.qt4/configure.ac b/gb.qt4/configure.ac new file mode 100644 index 00000000..6f6119d7 --- /dev/null +++ b/gb.qt4/configure.ac @@ -0,0 +1,55 @@ +dnl ---- configure.ac for gb.qt + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-qt4, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.qt4) +AC_PROG_LIBTOOL + +AC_ARG_ENABLE( + qt-translation, + [ --enable-qt-translation use qt translation files (default: yes)], + gb_use_qt_translation=$enableval, + gb_use_qt_translation=yes +) + +if test "$gb_use_qt_translation" = "yes"; then + AC_DEFINE(USE_QT_TRANSLATION, 1, [Use QT translation]) +fi + +GB_CHECK_XWINDOW +AM_CONDITIONAL(XWINDOW, test x"$have_x" = xyes) + +GB_COMPONENT_PKG_CONFIG( + qt, QT, gb.qt4, [src], + 'QtCore >= 4.5.0' QtGui QtSvg x11 +) + +GB_COMPONENT_PKG_CONFIG( + qtext, QTEXT, gb.qt4.ext, [ext], + 'QtCore >= 4.5.0' QtGui Qt3Support x11 +) + +GB_COMPONENT_PKG_CONFIG( + qtwebkit, QTWEBKIT, gb.qt4.webkit, [webkit], + 'QtCore >= 4.5.0' QtGui QtNetwork QtDBus QtXml 'QtWebKit >= 4.5.0' x11 +) + +GB_COMPONENT_PKG_CONFIG( + qtopengl, QTOPENGL, gb.qt4.opengl, [opengl], + 'QtCore >= 4.5.0' QtGui QtOpenGL x11 gl +) + +MOC=`pkg-config --variable=moc_location QtCore` +AC_SUBST(MOC) + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +src/ext/Makefile \ +src/webkit/Makefile \ +src/opengl/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.qt4/gambas.h b/gb.qt4/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.qt4/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.qt4/gb.draw.h b/gb.qt4/gb.draw.h new file mode 120000 index 00000000..82ba0778 --- /dev/null +++ b/gb.qt4/gb.draw.h @@ -0,0 +1 @@ +../main/lib/draw/gb.draw.h \ No newline at end of file diff --git a/gb.qt4/gb.eval.h b/gb.qt4/gb.eval.h new file mode 120000 index 00000000..33d332f0 --- /dev/null +++ b/gb.qt4/gb.eval.h @@ -0,0 +1 @@ +../main/lib/eval/gb.eval.h \ No newline at end of file diff --git a/gb.qt4/gb.geom.h b/gb.qt4/gb.geom.h new file mode 120000 index 00000000..abc5659e --- /dev/null +++ b/gb.qt4/gb.geom.h @@ -0,0 +1 @@ +../main/lib/geom/gb.geom.h \ No newline at end of file diff --git a/gb.qt4/gb.gl.h b/gb.qt4/gb.gl.h new file mode 120000 index 00000000..ff28a726 --- /dev/null +++ b/gb.qt4/gb.gl.h @@ -0,0 +1 @@ +../gb.opengl/src/gb.gl.h \ No newline at end of file diff --git a/gb.qt4/gb.image.h b/gb.qt4/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.qt4/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.qt4/gb.paint.h b/gb.qt4/gb.paint.h new file mode 120000 index 00000000..a516fe05 --- /dev/null +++ b/gb.qt4/gb.paint.h @@ -0,0 +1 @@ +../main/lib/draw/gb.paint.h \ No newline at end of file diff --git a/gb.qt4/gb.qt.am b/gb.qt4/gb.qt.am new file mode 100644 index 00000000..a475f791 --- /dev/null +++ b/gb.qt4/gb.qt.am @@ -0,0 +1,6 @@ +CLEANFILES = *_moc.cpp + +.h_moc.cpp: + $(MOC) -o $@ $< + +SUFFIXES = .h _moc.cpp \ No newline at end of file diff --git a/gb.qt4/gb_common.h b/gb.qt4/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.qt4/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.qt4/gbc_read_common.h b/gb.qt4/gbc_read_common.h new file mode 120000 index 00000000..9dd882ab --- /dev/null +++ b/gb.qt4/gbc_read_common.h @@ -0,0 +1 @@ +../main/share/gbc_read_common.h \ No newline at end of file diff --git a/gb.qt4/m4 b/gb.qt4/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.qt4/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.qt4/reconf b/gb.qt4/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.qt4/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.qt4/share/gb.form.action.h b/gb.qt4/share/gb.form.action.h new file mode 100644 index 00000000..93cb199c --- /dev/null +++ b/gb.qt4/share/gb.form.action.h @@ -0,0 +1,93 @@ +/*************************************************************************** + + gb.form.action.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_FORM_ACTION_H + +/*************************************************************************** + +#define HAS_ACTION(_widget) +If a widget has the action flag set. + +#define SET_ACTION(_widget, _flag) +Sets or clears the action flag. + +***************************************************************************/ + +static GB_FUNCTION _action_register_func; +static GB_FUNCTION _action_raise_func; +//static GB_FUNCTION _action_get_func; + +static void init_action() +{ + static bool init = false; + void *klass; + + if (init) + return; + + klass = (void *)GB.FindClass("Action"); + GB.GetFunction(&_action_register_func, klass, "_Register", "oss", ""); + GB.GetFunction(&_action_raise_func, klass, "Raise", "o", ""); + //GB.GetFunction(&_action_get_func, klass, "Get", "o", "s"); + + init = true; +} + +void CACTION_register(void *control, const char *old, const char *key) +{ + //qDebug("CACTION_register: (%s %p) %s", GB.GetClassName(control), control, key); + //fprintf(stderr, "CACTION_register: (%s %p %p) %s\n", GB.GetClassName(control), control, ((CWIDGET *)control)->widget, key); + + if ((!key || !*key) && !HAS_ACTION(control)) + //&& !GB.Is(control, CLASS_UserControl) && !GB.Is(control, CLASS_UserContainer)) + return; + + init_action(); + + //GB.Ref(control); + + SET_ACTION(control, key && *key); + + GB.Push(3, + GB_T_OBJECT, control, + GB_T_STRING, old, 0, + GB_T_STRING, key, 0); + + // The register function must not raise an error, otherwise bad things may happen + GB.Call(&_action_register_func, 3, true); + + //GB.Unref(&control); +} + +void CACTION_raise(void *control) +{ + init_action(); + + //qDebug("CACTION_raise: (%s %p)", GB.GetClassName(THIS), THIS); + + if (!HAS_ACTION(control)) + return; + + GB.Push(1, GB_T_OBJECT, control); + GB.Call(&_action_raise_func, 1, true); +} diff --git a/gb.qt4/share/gb.form.arrangement.h b/gb.qt4/share/gb.form.arrangement.h new file mode 100644 index 00000000..fb1fd171 --- /dev/null +++ b/gb.qt4/share/gb.form.arrangement.h @@ -0,0 +1,692 @@ +/*************************************************************************** + + gb.form.arrangement.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_FORM_ARRANGEMENT_H + +/*************************************************************************** + +#define WIDGET_TYPE +Datatype of variables that points at a widget + +#define CONTAINER_TYPE +Datatype of variables that points at a container widget + +#define ARRANGEMENT_TYPE +Datatype of variables that points at the arrangement structure. +This structure contains all the arrangement flags, and must have (at least) +the following fields: +- mode : the arrangement style. +- spacing : if children must be separate by Desktop.Scale +- padding : number of pixels around the arrangement area. +- margin : if the arrangement area must have a margin of Desktop.Scale +- indent: if the arrangement area must have an indentation of Desktop.Scale +- autoresize : if the container must try to fit its contents. +- locked : if the container is being arranged. +- user : if the container is a UserControl or a UserContainer. + +#define IS_RIGHT_TO_LEFT() +If the current language is right to left written + +#define GET_WIDGET(_object) +Returns the widget associated with the gambas control + +#define GET_CONTAINER(_object) +Returns the container widget inside the gambas control that contains the +child widgets. It can be the same for a Panel, for example, or a sub widget +for something more complex, like a TabStrip. + +#define GET_ARRANGEMENT(_object) +Returns a pointer to the arrangement flags of a Gambas control. + +#define IS_EXPAND(_object) +Returns if the Expand property of a control is set or not. + +#define IS_DESIGN(_object) +Returns if the control is in design mode. + +#define IS_WIDGET_VISIBLE(_widget) +Returns if a widget is visible to the screen. + +#define GET_WIDGET_CONTENTS(_widget, _x, _y, _w, _h) +Sets the _x, _y, _w & _h variables with the dimensions of the area +inside the container that must be arranged, in container coordinates. +If the container has a border, for example, then _x > 0 & _y > 0. + +#define GET_WIDGET_X(_widget) +Returns the x position of a widget inside its parent. + +#define GET_WIDGET_Y(_widget) +Returns the y position of a widget inside its parent. + +#define GET_WIDGET_W(_widget) +Returns the width of a widget. + +#define GET_WIDGET_H(_widget) +Returns the height of a widget. + +#define MOVE_WIDGET(_object, _widget, _x, _y) +Moves a widget inside its parent. + +#define RESIZE_WIDGET(_object, _widget, _w, _h) +Resizes a widget. + +#define MOVE_RESIZE_WIDGET(_object, _widget, _x, _y, _w, _h) +Move & resize a widget simultaneously. + +#define RESIZE_CONTAINER(_object, _w, _h) +Resizes the container object by resizing the widget itself. + +#define INIT_CHECK_CHILDREN_LIST(_widget) +Initializes the list of children of _widget. Must return if there is no +children. + +#define RESET_CHILDREN_LIST() +Resets the children list so that its next enumeration starts from the first child. + +#define GET_NEXT_CHILD_WIDGET() +Returns the next child to arrange, or NULL if there is no child anymore. +Make a function, as this macro is called several times. + +#define GET_OBJECT_FROM_WIDGET(_widget) +Returns the Gambas object associated with the specified widget, or NULL if there is +no Gambas object. + +#define DESKTOP_SCALE +Returns the value of Desktop.Scale + +#define RAISE_ARRANGE_EVENT(_object) +Code to raise the Arrange event of containers. + +#define RAISE_BEFORE_ARRANGE_EVENT(_object) +Code to raise the BeforeArrange event of containers. + +#define FUNCTION_NAME +This is the name of the arrangement function + + +***************************************************************************/ + +#define ARRANGE_ROW ARRANGE_LEFT_RIGHT +#define ARRANGE_COLUMN ARRANGE_TOP_BOTTOM + +void FUNCTION_NAME(void *_object) //(QFrame *cont) +{ + CONTAINER_TYPE cont; + ARRANGEMENT_TYPE arr; + + WIDGET_TYPE wid; + int x, y, w, h, i; + int xc, yc, wc, hc; + #ifndef GET_MAX_SIZE + int wf, hf; + #endif + int dmax = 0, d; + int sexp, nexp; + bool swap, autoresize, invert; + #ifndef GET_MAX_SIZE + bool has_expand_children = false; + #endif + bool redo; + bool first; + void *ob; + bool rtl; + int rtlm; + int padding; + int spacing; + int indent; + + if (!CAN_ARRANGE(_object)) + return; + + cont = (CONTAINER_TYPE)GET_CONTAINER(_object); + arr = GET_ARRANGEMENT(_object); + + //if (!IS_WIDGET_VISIBLE(cont) && !IS_WIDGET_VISIBLE(GET_WIDGET(_object))) + // return; + + //fprintf(stderr, "CCONTAINER_arrange: %s: locked %d: mode %d: autoresize: %d\n", GET_OBJECT_NAME(_object), arr->locked, arr->mode, arr->autoresize); + + if (arr->locked) + return; + +// if (GET_WIDGET_W(cont) <= 1 || GET_WIDGET_H(cont) <= 1) +// return; + + //if (qstrcmp(GB.GetClassName(THIS), "FOutput") == 0) + // qDebug("CCONTAINER_arrange: do it!"); + + arr->locked = true; + + RAISE_BEFORE_ARRANGE_EVENT(_object); + + if (arr->mode != ARRANGE_NONE) + { + // g_debug("arrange: %s (%d %d) (%d %d)", ((gControl *)_object)->name(), ((gContainer *)_object)->width(), ((gContainer *)_object)->height(), ((gContainer *)_object)->clientWidth(), ((gContainer *)_object)->clientHeight()); + //usleep(50000); + + if (arr->user) + cont = (CONTAINER_TYPE)GET_WIDGET(_object); + + // INIT_CHECK_CHILDREN_LIST() can return + arr->locked = false; + if (!cont) + goto __RETURN; + + INIT_CHECK_CHILDREN_LIST(cont); + + //fprintf(stderr, "CCONTAINER_arrange: %s: children: %d\n", GET_OBJECT_NAME(_object), list.count()); + + if (!HAS_CHILDREN()) + goto __RETURN; + + arr->locked = true; + + //fprintf(stderr, "arrange: %s %d (%d %d) (%d %d)\n", ((gControl *)_object)->name(), arr->mode, ((gContainer *)_object)->width(), ((gContainer *)_object)->height(), ((gContainer *)_object)->clientWidth(), ((gContainer *)_object)->clientHeight()); + + invert = arr->invert; + rtl = IS_RIGHT_TO_LEFT(); + swap = (arr->mode & 1) == 0; // means "vertical" + + if (!swap && invert) + { + rtl = !rtl; + invert = false; + } + + rtlm = rtl ? -1 : 1; + + autoresize = arr->autoresize; // && !IS_EXPAND(_object); + + if (arr->margin) + padding = arr->padding ? arr->padding : DESKTOP_SCALE; + else if (!arr->spacing) + padding = arr->padding; + else + padding = 0; + + if (arr->spacing) + spacing = arr->padding ? arr->padding : DESKTOP_SCALE; + else + spacing = 0; + + indent = arr->indent * DESKTOP_SCALE; + + for(i = 0; i < 3; i++) + { + redo = false; + + GET_WIDGET_CONTENTS(cont, xc, yc, wc, hc); + #ifndef GET_MAX_SIZE + wf = GET_WIDGET_W(cont) - wc; + hf = GET_WIDGET_H(cont) - hc; + #endif + + //fprintf(stderr, "cont: %s: %d %d %d %d (%d %d)\n", ((gControl *)_object)->name(), xc, yc, wc, hc, wf, hf); + + //if (hc > GET_WIDGET_H(cont)) + // qDebug("hc = %d H = %d ?", hc, GET_WIDGET_H(cont)); + + xc += padding; + yc += padding; + wc -= padding * 2; + hc -= padding * 2; + + //if (!strcmp(GET_OBJECT_NAME(_object), "TabStrip1")) + // fprintf(stderr, "#0: %d %d %d %d\n", xc, yc, wc, hc); + + if (indent) + { + if (swap) + { + if (!invert) + yc += indent; + hc -= indent; + } + else + { + if (!rtl) + xc += indent; + wc -= indent; + } + } + + // Beware! This is not the same test! + if (autoresize) + { + if (wc <= 0 && hc <= 0) + break; + } + else + { + if (wc <= 0 || hc <= 0) + break; + } + + //if (!strcmp(GET_OBJECT_NAME(_object), "HBox1")) + // fprintf(stderr, "#1\n"); + + x = xc; + y = yc; + w = h = 0; + + if (swap) + { + if (invert) + y += hc; + } + else + { + if (rtl) + x += wc; + } + + wid = 0; + RESET_CHILDREN_LIST(); + + switch (arr->mode) + { + case ARRANGE_HORIZONTAL: + case ARRANGE_VERTICAL: + + sexp = 0; + nexp = 0; + dmax = 0; + + for(;;) + { + wid = GET_NEXT_CHILD_WIDGET(); + + if (!wid) + break; + + ob = GET_OBJECT_FROM_WIDGET(wid); + + if (!ob || IS_IGNORE(ob)) + continue; + + if (IS_EXPAND(ob)) + nexp++; + else + sexp += (swap ? GET_WIDGET_H(wid) : GET_WIDGET_W(wid)); + + if (autoresize) + { + d = swap ? GET_WIDGET_W(wid) : GET_WIDGET_H(wid); + if (d > dmax) + { + dmax = d; + } + //fprintf(stderr, "%s: %s: d = %d dmax = %d\n", ((gControl *)_object)->name(), wid->name(), d, dmax); + } + + sexp += spacing; + } + + #ifndef GET_MAX_SIZE + has_expand_children = nexp > 0; + #endif + + if (autoresize) + { + // TODO: use rtl + if (swap) + wc = dmax + indent; + else + hc = dmax + indent; + //fprintf(stderr, "%s: dmax = %d\n", ((CWIDGET *)_object)->name, dmax); + } + + sexp -= spacing; + sexp = (swap ? hc : wc) - sexp; + if (sexp < 0) + sexp = 0; + + RESET_CHILDREN_LIST(); + wid = 0; + + for(;;) + { + first = wid == 0; + + wid = GET_NEXT_CHILD_WIDGET(); + if (!wid) + break; + + ob = GET_OBJECT_FROM_WIDGET(wid); + if (!ob || IS_IGNORE(ob)) + continue; + + if (!first) + { + if (swap) + { + if (invert) + y -= spacing; + else + y += spacing; + } + else + x += spacing * rtlm; + } + + if (swap) + { + if (IS_EXPAND(ob)) // && !autoresize) + { + h = sexp / nexp; + sexp -= h; + nexp--; + if (h <= 0) + MOVE_WIDGET(ob, wid, -GET_WIDGET_W(wid), GET_WIDGET_Y(wid)); + } + else + h = GET_WIDGET_H(wid); + + //w = autoresize ? GET_WIDGET_W(wid) : wc; + w = wc; + + if (h >= 0 && w >= 0) + { + if (w != GET_WIDGET_W(wid) || h != GET_WIDGET_H(wid)) + redo = true; + + if (invert) + { + y -= h; + MOVE_RESIZE_WIDGET(ob, wid, x, y, w, h); + } + else + { + MOVE_RESIZE_WIDGET(ob, wid, x, y, w, h); + y += h; + } + } + } + else + { + if (IS_EXPAND(ob)) // && !autoresize) + { + w = sexp / nexp; + sexp -= w; + nexp--; + if (w <= 0) + MOVE_WIDGET(ob, wid, GET_WIDGET_X(wid), -GET_WIDGET_H(wid)); + } + else + w = GET_WIDGET_W(wid); + + //h = autoresize ? GET_WIDGET_H(wid) : hc; + h = hc; + + if (w >= 0 && h >= 0) + { + if (w != GET_WIDGET_W(wid) || h != GET_WIDGET_H(wid)) + redo = true; + + if (rtl) + MOVE_RESIZE_WIDGET(ob, wid, x - w, y, w, h); + else + MOVE_RESIZE_WIDGET(ob, wid, x, y, w, h); + x += w * rtlm; + } + } + } + break; + + case ARRANGE_ROW: + case ARRANGE_COLUMN: + + wc += xc; + hc += yc; + + for(;;) + { + wid = GET_NEXT_CHILD_WIDGET(); + if (!wid) + break; + + ob = GET_OBJECT_FROM_WIDGET(wid); + if (!ob || IS_IGNORE(ob)) + continue; + + if (swap) + { + if (invert) + { + if ((y < hc) && ((y - (IS_EXPAND(ob) ? DESKTOP_SCALE : GET_WIDGET_H(wid))) < yc)) + { + y = hc; + x += w + spacing; + w = 0; + } + + if (IS_EXPAND(ob)) + { + if (rtl) + MOVE_RESIZE_WIDGET(ob, wid, wc - (x - xc) - GET_WIDGET_W(wid), yc, GET_WIDGET_W(wid), y - yc); + else + MOVE_RESIZE_WIDGET(ob, wid, x, yc, GET_WIDGET_W(wid), y - yc); + y = yc - spacing; + } + else + { + if (rtl) + MOVE_WIDGET(ob, wid, wc - (x - xc) - GET_WIDGET_W(wid), y - GET_WIDGET_H(wid)); + else + MOVE_WIDGET(ob, wid, x, y - GET_WIDGET_H(wid)); + y -= GET_WIDGET_H(wid) + spacing; + } + + if (GET_WIDGET_W(wid) > w) + w = GET_WIDGET_W(wid); + } + else + { + if ((y > yc) && ((y + (IS_EXPAND(ob) ? DESKTOP_SCALE : GET_WIDGET_H(wid))) > hc)) + { + y = yc; + x += w + spacing; + w = 0; + } + + if (IS_EXPAND(ob)) + { + if (rtl) + MOVE_RESIZE_WIDGET(ob, wid, wc - (x - xc) - GET_WIDGET_W(wid), y, GET_WIDGET_W(wid), hc - y); + else + MOVE_RESIZE_WIDGET(ob, wid, x, y, GET_WIDGET_W(wid), hc - y); + y = hc + spacing; + } + else + { + if (rtl) + MOVE_WIDGET(ob, wid, wc - (x - xc) - GET_WIDGET_W(wid), y); + else + MOVE_WIDGET(ob, wid, x, y); + y += GET_WIDGET_H(wid) + spacing; + } + + if (GET_WIDGET_W(wid) > w) + w = GET_WIDGET_W(wid); + } + } + else + { + if (rtl) + { + if ((x < wc) && ((x - (IS_EXPAND(ob) ? DESKTOP_SCALE : GET_WIDGET_W(wid))) < xc)) + { + x = wc; + y += h + spacing; + h = 0; + } + + if (IS_EXPAND(ob)) + { + MOVE_RESIZE_WIDGET(ob, wid, xc, y, x - xc, GET_WIDGET_H(wid)); + x = xc - spacing; + } + else + { + MOVE_WIDGET(ob, wid, x - GET_WIDGET_W(wid), y); + x -= GET_WIDGET_W(wid) + spacing; + } + + if (GET_WIDGET_H(wid) > h) + h = GET_WIDGET_H(wid); + } + else + { + if ((x > xc) && ((x + (IS_EXPAND(ob) ? DESKTOP_SCALE : GET_WIDGET_W(wid))) > wc)) + { + x = xc; + y += h + spacing; + h = 0; + } + + if (IS_EXPAND(ob)) + { + MOVE_RESIZE_WIDGET(ob, wid, x, y, wc - x, GET_WIDGET_H(wid)); + x = wc + spacing; + } + else + { + MOVE_WIDGET(ob, wid, x, y); + x += GET_WIDGET_W(wid) + spacing; + } + + if (GET_WIDGET_H(wid) > h) + h = GET_WIDGET_H(wid); + } + } + } + break; + + case ARRANGE_FILL: + + w = h = 0; + + for(;;) + { + wid = GET_NEXT_CHILD_WIDGET(); + if (!wid) + break; + + ob = GET_OBJECT_FROM_WIDGET(wid); + if (!ob || IS_IGNORE(ob)) + continue; + + MOVE_RESIZE_WIDGET(ob, wid, xc, yc, wc, hc); + + if (GET_WIDGET_H(wid) > h) + h = GET_WIDGET_H(wid); + + if (GET_WIDGET_W(wid) > w) + w = GET_WIDGET_W(wid); + } + + break; + } + + if (autoresize) + { + switch(arr->mode) + { + case ARRANGE_HORIZONTAL: +// #ifndef QNAMESPACE_H +// fprintf(stderr, "%s: HORIZONTAL: x = %d autoresize (%d, %d) pad %d spc %d [%d] rtl %d\n", +// ((gControl *)_object)->name(), x, x + arr->padding + wf, GET_WIDGET_H(cont), arr->padding, spacing, i, rtl); +// #else +// fprintf(stderr, "%s: HORIZONTAL: x = %d autoresize (%d, %d) pad %d spc %d [%d] rtl %d hec %d\n", +// ((CWIDGET *)_object)->name, x, has_expand_children ? GET_WIDGET_W(cont) : x + arr->padding + wf, +// dmax + hf, arr->padding, spacing, i, rtl, has_expand_children); +// #endif + //cont->resize(x + cont->width() - wc - xc, cont->height()); + if (dmax > 0) + { + if (rtl) + //RESIZE_WIDGET(cont, xc - x + arr->padding + wf, GET_WIDGET_H(cont)); + RESIZE_CONTAINER(_object, cont, has_expand_children ? GET_WIDGET_W(cont) : xc - x + padding + wf, dmax + hf + padding * 2); + else + //RESIZE_WIDGET(cont, x + arr->padding + wf, GET_WIDGET_H(cont)); + RESIZE_CONTAINER(_object, cont, has_expand_children ? GET_WIDGET_W(cont) : x + padding + wf, dmax + hf + padding * 2); + } + + break; + + case ARRANGE_VERTICAL: +// #ifndef QNAMESPACE_H +// fprintf(stderr, "%s: VERTICAL: x = %d autoresize (%d, %d) pad %d spc %d [%d] rtl %d\n", +// ((gControl *)_object)->name(), x, x + arr->padding + wf, GET_WIDGET_H(cont), arr->padding, spacing, i, rtl); +// #else +// fprintf(stderr, "%s: VERTICAL: y = %d autoresize (%d, %d) pad %d spc %d [%d] rtl %d hec %d\n", +// ((CWIDGET *)_object)->name, y, dmax + wf, has_expand_children ? GET_WIDGET_H(cont) : y + arr->padding + hf, +// arr->padding, spacing, i, rtl, has_expand_children); +// #endif + //RESIZE_WIDGET(cont, GET_WIDGET_W(cont), y + arr->padding + hf); + if (dmax > 0) + RESIZE_CONTAINER(_object, cont, dmax + wf + padding * 2, has_expand_children ? GET_WIDGET_H(cont) : y + padding + hf); + break; + + case ARRANGE_COLUMN: + if (rtl) + RESIZE_CONTAINER(_object, cont, (wc - x) + w + padding + wf, GET_WIDGET_H(cont)); + else + RESIZE_CONTAINER(_object, cont, x + w + padding + wf, GET_WIDGET_H(cont)); + break; + + case ARRANGE_ROW: + //if ((y + h + arr->padding + GET_WIDGET_H(cont) - hc - yc) < 16) + // qDebug("y = %d h = %d arr->padding = %d H = %d hc = %d yc = %d -> %d", y, h, arr->padding, GET_WIDGET_H(cont), hc, yc, (y + h + arr->padding + GET_WIDGET_H(cont) - hc - yc)); + RESIZE_CONTAINER(_object, cont, GET_WIDGET_W(cont), y + h + padding + hf); + break; + + case ARRANGE_FILL: +// #ifndef QNAMESPACE_H +// if (strncmp(((gControl *)_object)->name(), "DataControl", 11) == 0) +// fprintf(stderr, "%s: RESIZE_CONTAINER(%p, %p, %d, %d)\n", ((gControl *)_object)->name(), GET_WIDGET(_object), cont, w, h); +// #endif + RESIZE_CONTAINER(_object, cont, w + padding * 2, h + padding * 2); + break; + } + } + + if (!redo) + break; + } + + } + +__RETURN: + + RAISE_ARRANGE_EVENT(_object); + + arr->locked = false; + + //qDebug("%p: dirty = FALSE", THIS); + //arr->dirty = false; + + //qDebug("CCONTAINER_arrange: END %p", THIS); + + //qDebug("CCONTAINER_arrange: %p: END", cont); +} diff --git a/gb.qt4/share/gb.form.const.h b/gb.qt4/share/gb.form.const.h new file mode 100644 index 00000000..a9717185 --- /dev/null +++ b/gb.qt4/share/gb.form.const.h @@ -0,0 +1,112 @@ +/*************************************************************************** + + gb.form.const.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_FORM_H +#define __GB_FORM_H + +#include "gb.geom.h" + +#define CONST_MAGIC 0x12345678 + +enum { + BORDER_NONE = 0, + BORDER_PLAIN = 1, + BORDER_SUNKEN = 2, + BORDER_RAISED = 3, + BORDER_ETCHED = 4 + }; + +enum { + ARRANGE_NONE = 0, + ARRANGE_HORIZONTAL = 1, + ARRANGE_VERTICAL = 2, + ARRANGE_ROW = 3, + ARRANGE_LEFT_RIGHT = 3, + ARRANGE_COLUMN = 4, + ARRANGE_TOP_BOTTOM = 4, + ARRANGE_FILL = 5, + ARRANGE_TABLE = 6 + }; + +enum +{ + SELECT_NONE = 0, + SELECT_SINGLE = 1, + SELECT_MULTIPLE = 2 +}; + +enum +{ + LINE_NONE = 0, //Qt::NoPen, + LINE_SOLID = 1, //Qt::SolidLine, + LINE_DASH = 2, //Qt::DashLine, + LINE_DOT = 3, //Qt::DotLine, + LINE_DASH_DOT = 4, //Qt::DashDotLine, + LINE_DASH_DOT_DOT = 5, //Qt::DashDotDotLine +}; + +enum +{ + FILL_NONE = 0, //Qt::NoBrush, + FILL_SOLID = 1, //Qt::SolidPattern, + FILL_DENSE_94 = 2, //Qt::Dense1Pattern, + FILL_DENSE_88 = 3, //Qt::Dense2Pattern, + FILL_DENSE_63 = 4, //Qt::Dense3Pattern, + FILL_DENSE_50 = 5, //Qt::Dense4Pattern, + FILL_DENSE_37 = 6, //Qt::Dense5Pattern, + FILL_DENSE_12 = 7, //Qt::Dense6Pattern, + FILL_DENSE_06 = 8, //Qt::Dense7Pattern, + FILL_HORIZONTAL = 9, //Qt::HorPattern, + FILL_VERTICAL = 10, //Qt::VerPattern, + FILL_CROSS = 11, //Qt::CrossPattern, + FILL_DIAGONAL = 12, //Qt::BDiagPattern, + FILL_BACK_DIAGONAL = 13, //Qt::FDiagPattern, + FILL_CROSS_DIAGONAL = 14, //Qt::DiagCrossPattern +}; + +enum { + SCROLL_NONE = 0, + SCROLL_HORIZONTAL = 1, + SCROLL_VERTICAL = 2, + SCROLL_BOTH = 3 + }; + +enum { + POINTER_MOUSE = 0, + POINTER_PEN = 1, + POINTER_ERASER = 2, + POINTER_CURSOR = 3 +}; + +enum { + MOUSE_LEFT = 1, + MOUSE_MIDDLE = 2, + MOUSE_RIGHT = 4, + MOUSE_SHIFT = 256, + MOUSE_CTRL = 512, + MOUSE_ALT = 1024, + MOUSE_META = 2048 +}; + + +#endif diff --git a/gb.qt4/share/gb.form.font.h b/gb.qt4/share/gb.form.font.h new file mode 100644 index 00000000..1bae6655 --- /dev/null +++ b/gb.qt4/share/gb.form.font.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + gb.form.font.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_FORM_FONT_H +#define __GB_FORM_FONT_H + +#define FONT_STEP 20 +#define GRADE_TO_SIZE(_grade, _desktop) ((int)(powf(_desktop, 1.0 + ((_grade) / (double)FONT_STEP)) + 0.5)) +#define SIZE_TO_GRADE(_size, _desktop) ((int)(FONT_STEP * (logf(_size) / logf(_desktop)) + 0.5) - FONT_STEP) +#define GET_DESKTOP_SCALE(_font_size, _dpi) (1 + ((_font_size) * (_dpi) * 2 / 3 / 96)) + +#define FONT_GRADE_MIN -8 +#define FONT_GRADE_MAX 24 + +#endif + + diff --git a/gb.qt4/share/gb.form.print.h b/gb.qt4/share/gb.form.print.h new file mode 100644 index 00000000..5f734757 --- /dev/null +++ b/gb.qt4/share/gb.form.print.h @@ -0,0 +1,55 @@ +/*************************************************************************** + + gb.form.print.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_FORM_PRINT_H +#define __GB_FORM_PRINT_H + +enum { + GB_PRINT_PORTRAIT, + GB_PRINT_LANDSCAPE + //GB_PRINT_REVERSE_PORTRAIT, + //GB_PRINT_REVERSE_LANDSCAPE +}; + +enum { + GB_PRINT_CUSTOM, + GB_PRINT_A3, + GB_PRINT_A4, + GB_PRINT_A5, + GB_PRINT_B5, + GB_PRINT_LETTER, + GB_PRINT_EXECUTIVE, + GB_PRINT_LEGAL +}; + +enum { + GB_PRINT_SIMPLEX, + GB_PRINT_DUPLEX_HORIZONTAL, + GB_PRINT_DUPLEX_VERTICAL +}; + + + +#endif + + diff --git a/gb.qt4/share/gb.form.properties.h b/gb.qt4/share/gb.form.properties.h new file mode 100644 index 00000000..97984779 --- /dev/null +++ b/gb.qt4/share/gb.form.properties.h @@ -0,0 +1,194 @@ +/*************************************************************************** + + gb.form.properties.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_FORM_PROPERTIES_H +#define __GB_FORM_PROPERTIES_H + +#define CCONTROL_PROPERTIES \ + "X{Position},Y{Position},Width{Dimension},Height{Dimension},Visible=True,Enabled=True,Font{Font}," \ + "Background{Color}=-1,Foreground{Color}=-1," \ + "Tag,Tracking,NoTabFocus,Mouse{Mouse.Default;Blank;Arrow;Cross;Wait;Text;SizeAll;SizeH;SizeV;SizeN;SizeS;SizeW;" \ + "SizeE;SizeNWSE;SizeNESW;SplitH;SplitV;Pointing}=Default,ToolTip,Drop,Expand,Ignore,PopupMenu{Menu}" +#define CARRANGEMENT_PROPERTY "Arrangement{Arrange.None;Horizontal;Vertical;Row;Column;Fill}" +#define CPADDING_PROPERTIES "Spacing,Margin,Padding{Range:0;63},Indent" +#define CWINDOW_PADDING_PROPERTIES "Spacing,Margin,Padding{Range:0;63}" +#define CARRANGEMENT_PROPERTIES CARRANGEMENT_PROPERTY ",AutoResize," CPADDING_PROPERTIES ",Invert" +#define CWINDOW_ARRANGEMENT_PROPERTIES CARRANGEMENT_PROPERTY ",AutoResize," CWINDOW_PADDING_PROPERTIES ",Invert" +#define CUSERCONTROL_PROPERTIES "*" +#define CUSERCONTAINER_PROPERTIES "*," CARRANGEMENT_PROPERTIES + +#define CBUTTON_PROPERTIES "*,Action,AutoResize,Text,Picture,Border=True,Default,Cancel" +#define CCHECKBOX_PROPERTIES "*,Action,AutoResize,Text,Tristate,Value{CheckBox.False;True;None}" +#define CCOMBOBOX_PROPERTIES "*,Action,ReadOnly,List,Text,Password,MaxLength,Sorted,Border=True" +#define CDIAL_PROPERTIES "*,MinValue=0,MaxValue=100,Step=1,PageStep=10,Wrap,Mark=True" +#define CDRAWINGAREA_PROPERTIES "*," CARRANGEMENT_PROPERTIES ",Border{Border.None;Plain;Sunken;Raised;Etched},Cached,Focus,NoBackground,Tablet" +#define CEDITOR_PROPERTIES "*,Font,Border=True,ScrollBar{Scroll.*}=Both,Highlight{Highlight.None;Custom;Gambas;HTML;CSS;WebPage;Diff;JavaScript;SQL}=None,ReadOnly=False,TabSize{Range:1;16}=2" +#define CFRAME_PROPERTIES "*," CARRANGEMENT_PROPERTIES ",Text" +#define CHBOX_PROPERTIES "*,AutoResize," CPADDING_PROPERTIES ",Invert" +#define CLABEL_PROPERTIES "*,Padding{Range:0;64},AutoResize,Text,Alignment{Align.*}=Normal,Border{Border.None;Plain;Sunken;Raised;Etched},Transparent" +#define CLCDNUMBER_PROPERTIES "*,Value,Digits{Range:1;64}=1,SmallDecimalPoint,Style{LCDNumber.Outline;Filled;Flat}=Outline,Mode{LCDNumber.Decimal;Hexadecimal;Binary}=Decimal,Border{Border.None;Plain;Sunken;Raised;Etched}" +#define CLISTBOX_PROPERTIES "*,List,Border=True,Mode{Select.*}=Single,Sorted" +#define CMENU_PROPERTIES "Action,Text,Picture,Enabled=True,Radio,Toggle,Checked,Visible=True,Tag,Shortcut" +#define CMOVIEBOX_PROPERTIES "*,Path,Playing,Alignment{Align.*}=TopLeft,Border{Border.None;Plain;Sunken;Raised;Etched}" +#define CPANEL_PROPERTIES "*," CARRANGEMENT_PROPERTIES ",Border{Border.None;Plain;Sunken;Raised;Etched}" +#define CPRINTER_PROPERTIES "Orientation{Printer.Portrait;Landscape}=Portrait,Paper{Printer.A3;A4;A5;B5;Letter;Executive}=A4,CollateCopies,ReverseOrder,Duplex{Printer.Simplex;Horizontal;Vertical}=Simplex,GrayScale,FullPage" +#define CRADIOBUTTON_PROPERTIES "*,AutoResize,Text,Value" +#define CSCROLLBAR_PROPERTIES "*,MinValue=0,MaxValue=100,Step=1,PageStep=10,Tracking=True" +#define CSCROLLVIEW_PROPERTIES "*," CARRANGEMENT_PROPERTY "," CPADDING_PROPERTIES ",Border=True,ScrollBar{Scroll.*}=Both" +#define CSEPARATOR_PROPERTIES "*" +#define CSLIDER_PROPERTIES "*,Action,MinValue=0,MaxValue=100,Step=1,PageStep=10,Tracking=True,Mark,Value" +#define CTABSTRIP_PROPERTIES "*," CARRANGEMENT_PROPERTIES ",Count{Range:1;256}=1,Index,Text,TextFont,Picture,Orientation{Align.Top;Bottom;Left;Right}=Top,Closable" +#define CTEXTAREA_PROPERTIES "*,Text,Alignment{Align.Normal;Left;Center;Right}=Normal,ReadOnly,Wrap,Border=True,ScrollBar{Scroll.*}=Both" +#define CTEXTBOX_PROPERTIES "*,Action,Text,Alignment{Align.Normal;Left;Center;Right}=Normal,ReadOnly,Password,MaxLength,Border=True" +#define CTEXTEDIT_PROPERTIES "*,ReadOnly,Wrap,Border=True,ScrollBar{Scroll.*}=Both" +#define CTEXTLABEL_PROPERTIES "*,Padding{Range:0;63},AutoResize,Text,Alignment{Align.*}=TopNormal,Wrap=True,Border{Border.None;Plain;Sunken;Raised;Etched},Transparent" +#define CTOGGLEBUTTON_PROPERTIES "*,Action,AutoResize,Text,Picture,Border=True,Radio,Value" +#define CTOOLBUTTON_PROPERTIES "*,Action,AutoResize,Text,Picture,Border,Radio,Toggle,Value" +#define CTRAYICON_PROPERTIES "Visible=False,Tag,Tooltip,Picture,PopupMenu{Menu}" +#define CVBOX_PROPERTIES "*,AutoResize," CPADDING_PROPERTIES ",Invert" +#define CWINDOW_PROPERTIES "*,Action,Text,Icon,Picture,Mask,Persistent,Resizable=True,Border=True,Utility,TakeFocus=True,Stacking{Window.Normal;Above;Below}=Normal,Minimized,Maximized,FullScreen,Sticky,SkipTaskbar,Opacity{Range:0;100}=100,Transparent," CWINDOW_ARRANGEMENT_PROPERTIES + +#define DESCRIBE_CONTROL(_prop, _event, _size) \ + GB_CONSTANT("_Properties", "s", _prop), \ + GB_CONSTANT("_DefaultEvent", "s", _event), \ + GB_CONSTANT("_DefaultSize", "s", _size) + +#define DESCRIBE_VIEW_CONTROL(_prop, _event, _size) \ + GB_CONSTANT("_Properties", "s", _prop), \ + GB_CONSTANT("_DefaultEvent", "s", _event), \ + GB_CONSTANT("_DefaultSize", "s", _size), \ + GB_CONSTANT("_Group", "s", "View") + +#define DESCRIBE_SPECIAL_CONTROL(_prop, _event, _size) \ + DESCRIBE_CONTROL(_prop, _event, _size), \ + GB_CONSTANT("_IsControl", "b", TRUE), \ + GB_CONSTANT("_IsVirtual", "b", TRUE), \ + GB_CONSTANT("_Family", "s", "Form"), \ + GB_CONSTANT("_Group", "s", "Special") + +#define DESCRIBE_CONTAINER(_prop, _event) \ + GB_CONSTANT("_Properties", "s", _prop), \ + GB_CONSTANT("_DefaultEvent", "s", _event), \ + GB_CONSTANT("_DefaultSize", "s", "24,24") + +#define DESCRIBE_CONTAINER_SMALL(_prop, _event) \ + GB_CONSTANT("_Properties", "s", _prop), \ + GB_CONSTANT("_DefaultEvent", "s", _event), \ + GB_CONSTANT("_DefaultSize", "s", "8,8") + +#define DESCRIBE_CONTAINER_ARR(_prop, _event, _arr) \ + DESCRIBE_CONTAINER(_prop, _event), \ + GB_CONSTANT("_DefaultArrangement", "s", _arr) + +#define DESCRIBE_CONTAINER_SMALL_ARR(_prop, _event, _arr) \ + DESCRIBE_CONTAINER_SMALL(_prop, _event), \ + GB_CONSTANT("_DefaultArrangement", "s", _arr) + +#define DESCRIBE_MULTI_CONTAINER(_prop, _event) \ + GB_CONSTANT("_IsMultiContainer", "b", TRUE), \ + DESCRIBE_CONTAINER(_prop, _event, _arr) + +#define DESCRIBE_MULTI_CONTAINER_ARR(_prop, _event, _arr) \ + GB_CONSTANT("_IsMultiContainer", "b", TRUE), \ + DESCRIBE_CONTAINER_ARR(_prop, _event, _arr) + +#define CONTROL_DESCRIPTION \ + DESCRIBE_CONTROL(CCONTROL_PROPERTIES, "MouseDown", "16,16"), \ + GB_CONSTANT("_IsControl", "b", TRUE), \ + GB_CONSTANT("_Family", "s", "Form") \ + +#define CONTAINER_DESCRIPTION \ + GB_CONSTANT("_IsContainer", "b", TRUE), \ + GB_CONSTANT("_Group", "s", "Container") \ + +#define SIMILAR(_similar) GB_CONSTANT("_Similar", "s", _similar) + +#define BUTTON_DESCRIPTION DESCRIBE_CONTROL(CBUTTON_PROPERTIES, "Click", "16,4") +#define CHECKBOX_DESCRIPTION DESCRIBE_CONTROL(CCHECKBOX_PROPERTIES, "Click", "24,4"), SIMILAR("Button") +#define COMBOBOX_DESCRIPTION DESCRIBE_CONTROL(CCOMBOBOX_PROPERTIES, "Click", "24,4"), SIMILAR("TextBox") +#define DIAL_DESCRIPTION DESCRIBE_CONTROL(CDIAL_PROPERTIES, "Change", "6,6"), SIMILAR("Slider") +#define DRAWINGAREA_DESCRIPTION DESCRIBE_CONTAINER(CDRAWINGAREA_PROPERTIES, "Draw") +#define EDITOR_DESCRIPTION DESCRIBE_CONTROL(CEDITOR_PROPERTIES, "KeyPress", "16,16"), SIMILAR("TextArea") +#define EMBEDDER_DESCRIPTION DESCRIBE_CONTROL("*", "Embed", "24,24"), GB_CONSTANT("_Group", "s", "Deprecated") +#define FRAME_DESCRIPTION DESCRIBE_CONTAINER(CFRAME_PROPERTIES, "MouseDown"), SIMILAR("Panel") +#define HBOX_DESCRIPTION DESCRIBE_CONTAINER_SMALL_ARR(CHBOX_PROPERTIES, "MouseDown", "H"), SIMILAR("Panel") +#define HPANEL_DESCRIPTION DESCRIBE_CONTAINER_SMALL_ARR(CHBOX_PROPERTIES, "MouseDown", "R"), SIMILAR("Panel") +#define LABEL_DESCRIPTION DESCRIBE_CONTROL(CLABEL_PROPERTIES, "MouseDown", "24,4") +#define LCDNUMBER_DESCRIPTION DESCRIBE_CONTROL(CLCDNUMBER_PROPERTIES, "MouseDown", "24,6"), SIMILAR("Label"), GB_CONSTANT("_Group", "s", "Deprecated") +#define LISTBOX_DESCRIPTION DESCRIBE_CONTROL(CLISTBOX_PROPERTIES, "Click", "16,16") +#define MOVIEBOX_DESCRIPTION DESCRIBE_CONTROL(CMOVIEBOX_PROPERTIES, "MouseDown", "16,16") +#define PANEL_DESCRIPTION DESCRIBE_CONTAINER_SMALL_ARR(CPANEL_PROPERTIES, "MouseDown", "F") +#define RADIOBUTTON_DESCRIPTION DESCRIBE_CONTROL(CRADIOBUTTON_PROPERTIES, "Click", "24,4"), SIMILAR("Button") +#define SCROLLBAR_DESCRIPTION DESCRIBE_CONTROL(CSCROLLBAR_PROPERTIES, "Change", "36,4"), SIMILAR("Slider") +#define SCROLLVIEW_DESCRIPTION DESCRIBE_CONTAINER_ARR(CSCROLLVIEW_PROPERTIES, "MouseDown", "F"), SIMILAR("Panel") +#define SEPARATOR_DESCRIPTION DESCRIBE_CONTROL(CSEPARATOR_PROPERTIES, "MouseDown", "1,4") +#define SLIDER_DESCRIPTION DESCRIBE_CONTROL(CSLIDER_PROPERTIES, "Change", "24,4") +#define SPINBOX_DESCRIPTION DESCRIBE_CONTROL(CSPINBOX_PROPERTIES, "Change", "9,4"), SIMILAR("TextBox,Slider") +#define TABSTRIP_DESCRIPTION DESCRIBE_MULTI_CONTAINER_ARR(CTABSTRIP_PROPERTIES, "Click", "F") +#define TEXTAREA_DESCRIPTION DESCRIBE_CONTROL(CTEXTAREA_PROPERTIES, "KeyPress", "16,16"), SIMILAR("TextBox") +#define TEXTBOX_DESCRIPTION DESCRIBE_CONTROL(CTEXTBOX_PROPERTIES, "KeyPress", "24,4") +#define TEXTEDIT_DESCRIPTION DESCRIBE_CONTROL(CTEXTEDIT_PROPERTIES, "Change", "16,16"), SIMILAR("TextArea") +#define TEXTLABEL_DESCRIPTION DESCRIBE_CONTROL(CTEXTLABEL_PROPERTIES, "MouseDown", "24,4"), SIMILAR("Label") +#define TOGGLEBUTTON_DESCRIPTION DESCRIBE_CONTROL(CTOGGLEBUTTON_PROPERTIES, "Click", "16,4"), SIMILAR("Button") +#define TOOLBUTTON_DESCRIPTION DESCRIBE_CONTROL(CTOOLBUTTON_PROPERTIES, "Click", "4,4"), SIMILAR("Button") +#define USERCONTAINER_DESCRIPTION DESCRIBE_CONTAINER_ARR(CUSERCONTAINER_PROPERTIES, "MouseDown", "F") +#define VBOX_DESCRIPTION DESCRIBE_CONTAINER_SMALL_ARR(CVBOX_PROPERTIES, "MouseDown", "V"), SIMILAR("Panel") +#define VPANEL_DESCRIPTION DESCRIBE_CONTAINER_SMALL_ARR(CVBOX_PROPERTIES, "MouseDown", "C"), SIMILAR("Panel") +#define WINDOW_DESCRIPTION DESCRIBE_CONTAINER_ARR(CWINDOW_PROPERTIES, "Open", "F") + +#define MENU_DESCRIPTION \ + GB_CONSTANT("_IsControl", "b", TRUE), \ + GB_CONSTANT("_IsContainer", "b", TRUE), \ + GB_CONSTANT("_Family", "s", "Form"), \ + GB_CONSTANT("_Properties", "s", CMENU_PROPERTIES), \ + GB_CONSTANT("_DefaultEvent", "s", "Click") + +#define FORM_DESCRIPTION \ + GB_CONSTANT("_IsForm", "b", TRUE), \ + GB_CONSTANT("_HiddenControls", "s", "Form,Control,Menu,Container,UserControl,UserContainer,Window") + +#define TRAYICON_DESCRIPTION \ + GB_CONSTANT("_IsControl", "b", TRUE), \ + GB_CONSTANT("_Family", "s", "*"), \ + GB_CONSTANT("_IsVirtual", "b", TRUE), \ + GB_CONSTANT("_Group", "s", "Special"), \ + GB_CONSTANT("_DefaultEvent", "s", "Click"), \ + GB_CONSTANT("_Properties", "s", CTRAYICON_PROPERTIES) + +#define PRINTER_DESCRIPTION \ + GB_CONSTANT("_IsControl", "b", TRUE), \ + GB_CONSTANT("_Family", "s", "*"), \ + GB_CONSTANT("_IsVirtual", "b", TRUE), \ + GB_CONSTANT("_Group", "s", "Special"), \ + GB_CONSTANT("_Properties", "s", CPRINTER_PROPERTIES), \ + GB_CONSTANT("_DefaultEvent", "s", "Draw") + +#define USERCONTROL_DESCRIPTION \ + DESCRIBE_CONTROL(CUSERCONTROL_PROPERTIES, "MouseDown", "16,16"), \ + GB_CONSTANT("_Group", "s", ""), \ + GB_CONSTANT("_IsContainer", "b", FALSE) + + +#endif + + diff --git a/gb.qt4/share/gb.form.trayicon.h b/gb.qt4/share/gb.form.trayicon.h new file mode 100644 index 00000000..b3b6a0f7 --- /dev/null +++ b/gb.qt4/share/gb.form.trayicon.h @@ -0,0 +1,615 @@ +/*************************************************************************** + + gb.form.trayicon.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define DEFAULT_TRAYICON_WIDTH 24 +#define DEFAULT_TRAYICON_HEIGHT 24 + +#ifdef QT_INTERFACE_VERSION + +static const unsigned char _default_trayicon_data[] = +{ +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 19, 12, 3, 80, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 6, +33, 23, 9, 54, 35, 28, 9, 180, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 25, 0, 0, 0, 69, +0, 0, 0, 78, 0, 0, 0, 37, +0, 0, 0, 2, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 3, 0, 0, 0, 11, +0, 0, 0, 10, 33, 25, 7, 108, +63, 48, 16, 172, 70, 55, 18, 198, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 4, 0, 0, 0, 61, +3, 3, 3, 196, 35, 35, 35, 225, +49, 49, 49, 230, 7, 7, 7, 202, +0, 0, 0, 102, 0, 0, 0, 9, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 20, 10, 0, 25, +18, 14, 3, 138, 16, 12, 4, 123, +30, 23, 6, 150, 125, 98, 32, 235, +121, 94, 31, 242, 95, 74, 24, 211, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 6, +4, 4, 4, 103, 59, 59, 59, 236, +210, 210, 188, 254, 254, 254, 212, 255, +254, 254, 224, 255, 234, 234, 229, 254, +124, 124, 124, 250, 41, 41, 41, 111, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 1, 45, 30, 7, 34, +78, 61, 19, 104, 114, 89, 29, 216, +84, 65, 21, 232, 123, 95, 31, 238, +159, 123, 41, 248, 193, 151, 50, 254, +179, 139, 46, 254, 91, 71, 22, 179, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 23, +75, 75, 75, 170, 209, 209, 189, 252, +237, 237, 110, 255, 167, 167, 71, 255, +187, 187, 124, 255, 254, 254, 245, 255, +248, 248, 248, 255, 132, 132, 132, 225, +58, 47, 15, 96, 62, 48, 16, 94, +80, 61, 18, 108, 130, 102, 34, 195, +157, 123, 40, 239, 188, 147, 49, 254, +173, 135, 45, 254, 193, 150, 50, 255, +194, 151, 50, 255, 193, 150, 50, 255, +147, 115, 38, 245, 74, 58, 16, 92, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 32, 29, 21, 70, +152, 151, 148, 222, 250, 250, 139, 255, +141, 141, 31, 255, 20, 20, 7, 255, +147, 147, 141, 255, 254, 254, 254, 255, +255, 255, 255, 255, 226, 226, 226, 255, +138, 108, 37, 239, 168, 130, 43, 238, +171, 133, 44, 243, 189, 148, 49, 254, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 188, 146, 49, 255, +120, 95, 31, 220, 54, 43, 10, 47, +0, 0, 0, 0, 36, 24, 0, 21, +82, 63, 20, 136, 130, 105, 49, 239, +193, 187, 172, 254, 255, 255, 85, 255, +48, 48, 10, 255, 1, 1, 1, 255, +144, 144, 144, 255, 224, 224, 221, 255, +253, 253, 247, 255, 254, 254, 254, 255, +165, 136, 67, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +193, 151, 50, 255, 159, 123, 40, 250, +92, 69, 22, 146, 18, 0, 0, 14, +30, 30, 0, 17, 94, 72, 24, 138, +166, 129, 43, 241, 184, 147, 58, 254, +204, 193, 167, 255, 255, 255, 97, 255, +72, 72, 15, 255, 0, 0, 0, 255, +22, 22, 22, 255, 126, 126, 111, 255, +251, 251, 238, 255, 251, 251, 251, 255, +186, 149, 62, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 194, 151, 51, 255, +180, 140, 46, 255, 113, 87, 28, 212, +46, 34, 8, 60, 0, 0, 0, 0, +66, 50, 16, 61, 139, 107, 35, 227, +192, 149, 50, 254, 192, 150, 51, 255, +199, 179, 130, 255, 244, 244, 164, 255, +189, 189, 68, 255, 70, 70, 54, 255, +41, 41, 27, 255, 194, 194, 157, 255, +250, 248, 232, 255, 217, 202, 165, 255, +194, 152, 52, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 185, 144, 48, 255, +151, 117, 39, 253, 55, 41, 12, 124, +0, 0, 0, 5, 0, 0, 0, 0, +92, 72, 24, 176, 187, 146, 49, 254, +195, 152, 51, 255, 195, 152, 51, 255, +187, 148, 57, 255, 213, 198, 162, 255, +244, 244, 222, 255, 252, 252, 229, 255, +247, 242, 166, 255, 231, 213, 154, 255, +212, 181, 109, 255, 201, 164, 74, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +188, 147, 49, 255, 157, 122, 40, 251, +80, 63, 20, 186, 0, 0, 0, 3, +0, 0, 0, 0, 0, 0, 0, 0, +104, 81, 26, 200, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +182, 142, 47, 255, 164, 130, 50, 255, +203, 181, 129, 255, 221, 203, 148, 255, +214, 187, 115, 255, 203, 166, 81, 255, +199, 159, 65, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 193, 150, 50, 255, +163, 127, 42, 252, 105, 82, 28, 200, +51, 38, 12, 59, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +107, 82, 26, 209, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +188, 146, 49, 255, 152, 118, 39, 255, +127, 99, 34, 255, 181, 143, 52, 255, +196, 154, 56, 255, 195, 152, 52, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +192, 149, 50, 255, 176, 137, 45, 255, +192, 149, 50, 255, 174, 136, 45, 255, +112, 87, 28, 213, 56, 43, 13, 77, +0, 0, 0, 5, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +107, 85, 27, 216, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 194, 151, 50, 255, +83, 69, 35, 255, 52, 49, 44, 255, +57, 52, 46, 255, 76, 64, 47, 255, +111, 90, 44, 255, 131, 105, 50, 255, +107, 87, 44, 255, 72, 61, 34, 255, +110, 87, 32, 255, 188, 146, 49, 255, +165, 129, 43, 252, 120, 92, 31, 221, +35, 27, 7, 64, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +99, 77, 25, 190, 191, 149, 50, 255, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +144, 115, 50, 255, 50, 51, 114, 255, +42, 48, 181, 255, 43, 49, 185, 255, +45, 49, 135, 255, 48, 52, 142, 255, +54, 58, 159, 255, 64, 61, 91, 255, +158, 124, 47, 255, 160, 124, 41, 251, +110, 86, 29, 201, 63, 49, 14, 88, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +84, 66, 20, 100, 162, 126, 42, 245, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +182, 142, 50, 255, 81, 67, 90, 255, +6, 15, 221, 255, 3, 15, 254, 255, +6, 17, 239, 255, 7, 18, 236, 255, +14, 24, 221, 255, 88, 74, 84, 255, +153, 120, 41, 255, 81, 63, 20, 185, +52, 38, 14, 53, 0, 0, 0, 5, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +49, 35, 7, 36, 118, 92, 30, 194, +185, 143, 48, 252, 194, 151, 50, 255, +195, 152, 51, 255, 195, 152, 51, 255, +194, 151, 50, 255, 180, 141, 48, 255, +90, 74, 77, 255, 15, 21, 194, 255, +6, 16, 221, 255, 12, 19, 212, 255, +59, 51, 96, 251, 116, 90, 31, 226, +68, 52, 17, 150, 0, 0, 0, 5, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 2, 47, 35, 7, 64, +121, 94, 31, 205, 192, 149, 50, 254, +195, 152, 51, 255, 195, 152, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +192, 149, 50, 255, 125, 100, 57, 255, +103, 84, 83, 255, 73, 59, 58, 240, +98, 75, 25, 168, 52, 39, 13, 78, +21, 21, 0, 12, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 3, +49, 35, 8, 57, 108, 84, 27, 203, +166, 130, 43, 246, 194, 151, 51, 255, +195, 152, 51, 255, 195, 152, 51, 255, +188, 146, 49, 255, 134, 105, 35, 237, +98, 78, 26, 196, 76, 59, 19, 90, +31, 21, 0, 24, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 9, +45, 36, 12, 85, 64, 50, 16, 142, +75, 59, 18, 151, 75, 58, 18, 149, +51, 39, 11, 115, 0, 0, 0, 22, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +}; + +#else + +static const unsigned char _default_trayicon_data[] = +{ +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 3, 12, 19, 80, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 6, +9, 23, 33, 54, 9, 28, 35, 180, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 25, 0, 0, 0, 69, +0, 0, 0, 78, 0, 0, 0, 37, +0, 0, 0, 2, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 3, 0, 0, 0, 11, +0, 0, 0, 10, 7, 25, 33, 108, +16, 48, 63, 172, 18, 55, 70, 198, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 4, 0, 0, 0, 61, +3, 3, 3, 196, 35, 35, 35, 225, +49, 49, 49, 230, 7, 7, 7, 202, +0, 0, 0, 102, 0, 0, 0, 9, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 10, 20, 25, +3, 14, 18, 138, 4, 12, 16, 123, +6, 23, 30, 150, 32, 98, 125, 235, +31, 94, 121, 242, 24, 74, 95, 211, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 6, +4, 4, 4, 103, 59, 59, 59, 236, +188, 210, 210, 254, 212, 254, 254, 255, +224, 254, 254, 255, 229, 234, 234, 254, +124, 124, 124, 250, 41, 41, 41, 111, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 1, 7, 30, 45, 34, +19, 61, 78, 104, 29, 89, 114, 216, +21, 65, 84, 232, 31, 95, 123, 238, +41, 123, 159, 248, 50, 151, 193, 254, +46, 139, 179, 254, 22, 71, 91, 179, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 23, +75, 75, 75, 170, 189, 209, 209, 252, +110, 237, 237, 255, 71, 167, 167, 255, +124, 187, 187, 255, 245, 254, 254, 255, +248, 248, 248, 255, 132, 132, 132, 225, +15, 47, 58, 96, 16, 48, 62, 94, +18, 61, 80, 108, 34, 102, 130, 195, +40, 123, 157, 239, 49, 147, 188, 254, +45, 135, 173, 254, 50, 150, 193, 255, +50, 151, 194, 255, 50, 150, 193, 255, +38, 115, 147, 245, 16, 58, 74, 92, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 21, 29, 32, 70, +148, 151, 152, 222, 139, 250, 250, 255, +31, 141, 141, 255, 7, 20, 20, 255, +141, 147, 147, 255, 254, 254, 254, 255, +255, 255, 255, 255, 226, 226, 226, 255, +37, 108, 138, 239, 43, 130, 168, 238, +44, 133, 171, 243, 49, 148, 189, 254, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 49, 146, 188, 255, +31, 95, 120, 220, 10, 43, 54, 47, +0, 0, 0, 0, 0, 24, 36, 21, +20, 63, 82, 136, 49, 105, 130, 239, +172, 187, 193, 254, 85, 255, 255, 255, +10, 48, 48, 255, 1, 1, 1, 255, +144, 144, 144, 255, 221, 224, 224, 255, +247, 253, 253, 255, 254, 254, 254, 255, +67, 136, 165, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +50, 151, 193, 255, 40, 123, 159, 250, +22, 69, 92, 146, 0, 0, 18, 14, +0, 30, 30, 17, 24, 72, 94, 138, +43, 129, 166, 241, 58, 147, 184, 254, +167, 193, 204, 255, 97, 255, 255, 255, +15, 72, 72, 255, 0, 0, 0, 255, +22, 22, 22, 255, 111, 126, 126, 255, +238, 251, 251, 255, 251, 251, 251, 255, +62, 149, 186, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 151, 194, 255, +46, 140, 180, 255, 28, 87, 113, 212, +8, 34, 46, 60, 0, 0, 0, 0, +16, 50, 66, 61, 35, 107, 139, 227, +50, 149, 192, 254, 51, 150, 192, 255, +130, 179, 199, 255, 164, 244, 244, 255, +68, 189, 189, 255, 54, 70, 70, 255, +27, 41, 41, 255, 157, 194, 194, 255, +232, 248, 250, 255, 165, 202, 217, 255, +52, 152, 194, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 48, 144, 185, 255, +39, 117, 151, 253, 12, 41, 55, 124, +0, 0, 0, 5, 0, 0, 0, 0, +24, 72, 92, 176, 49, 146, 187, 254, +51, 152, 195, 255, 51, 152, 195, 255, +57, 148, 187, 255, 162, 198, 213, 255, +222, 244, 244, 255, 229, 252, 252, 255, +166, 242, 247, 255, 154, 213, 231, 255, +109, 181, 212, 255, 74, 164, 201, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +49, 147, 188, 255, 40, 122, 157, 251, +20, 63, 80, 186, 0, 0, 0, 3, +0, 0, 0, 0, 0, 0, 0, 0, +26, 81, 104, 200, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +47, 142, 182, 255, 50, 130, 164, 255, +129, 181, 203, 255, 148, 203, 221, 255, +115, 187, 214, 255, 81, 166, 203, 255, +65, 159, 199, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 50, 150, 193, 255, +42, 127, 163, 252, 28, 82, 105, 200, +12, 38, 51, 59, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +26, 82, 107, 209, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +49, 146, 188, 255, 39, 118, 152, 255, +34, 99, 127, 255, 52, 143, 181, 255, +56, 154, 196, 255, 52, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +50, 149, 192, 255, 45, 137, 176, 255, +50, 149, 192, 255, 45, 136, 174, 255, +28, 87, 112, 213, 13, 43, 56, 77, +0, 0, 0, 5, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +27, 85, 107, 216, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 50, 151, 194, 255, +35, 69, 83, 255, 44, 49, 52, 255, +46, 52, 57, 255, 47, 64, 76, 255, +44, 90, 111, 255, 50, 105, 131, 255, +44, 87, 107, 255, 34, 61, 72, 255, +32, 87, 110, 255, 49, 146, 188, 255, +43, 129, 165, 252, 31, 92, 120, 221, +7, 27, 35, 64, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +25, 77, 99, 190, 50, 149, 191, 255, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +50, 115, 144, 255, 114, 51, 50, 255, +181, 48, 42, 255, 185, 49, 43, 255, +135, 49, 45, 255, 142, 52, 48, 255, +159, 58, 54, 255, 91, 61, 64, 255, +47, 124, 158, 255, 41, 124, 160, 251, +29, 86, 110, 201, 14, 49, 63, 88, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +20, 66, 84, 100, 42, 126, 162, 245, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +50, 142, 182, 255, 90, 67, 81, 255, +221, 15, 6, 255, 254, 15, 3, 255, +239, 17, 6, 255, 236, 18, 7, 255, +221, 24, 14, 255, 84, 74, 88, 255, +41, 120, 153, 255, 20, 63, 81, 185, +14, 38, 52, 53, 0, 0, 0, 5, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +7, 35, 49, 36, 30, 92, 118, 194, +48, 143, 185, 252, 50, 151, 194, 255, +51, 152, 195, 255, 51, 152, 195, 255, +50, 151, 194, 255, 48, 141, 180, 255, +77, 74, 90, 255, 194, 21, 15, 255, +221, 16, 6, 255, 212, 19, 12, 255, +96, 51, 59, 251, 31, 90, 116, 226, +17, 52, 68, 150, 0, 0, 0, 5, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 2, 7, 35, 47, 64, +31, 94, 121, 205, 50, 149, 192, 254, +51, 152, 195, 255, 51, 152, 195, 255, +51, 152, 195, 255, 51, 152, 195, 255, +50, 149, 192, 255, 57, 100, 125, 255, +83, 84, 103, 255, 58, 59, 73, 240, +25, 75, 98, 168, 13, 39, 52, 78, +0, 21, 21, 12, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 3, +8, 35, 49, 57, 27, 84, 108, 203, +43, 130, 166, 246, 51, 151, 194, 255, +51, 152, 195, 255, 51, 152, 195, 255, +49, 146, 188, 255, 35, 105, 134, 237, +26, 78, 98, 196, 19, 59, 76, 90, +0, 21, 31, 24, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 9, +12, 36, 45, 85, 16, 50, 64, 142, +18, 59, 75, 151, 18, 58, 75, 149, +11, 39, 51, 115, 0, 0, 0, 22, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, +}; + +#endif diff --git a/gb.qt4/share/gb.form.trayicon.large.h b/gb.qt4/share/gb.form.trayicon.large.h new file mode 100644 index 00000000..c8cf923a --- /dev/null +++ b/gb.qt4/share/gb.form.trayicon.large.h @@ -0,0 +1,263 @@ +/*************************************************************************** + + gb.form.trayicon.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define DEFAULT_TRAYICON_WIDTH 64 +#define DEFAULT_TRAYICON_HEIGHT 64 + +#ifdef QT_INTERFACE_VERSION + +static const unsigned char _default_trayicon_data[] = +{ +137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, +0, 0, 0, 64, 0, 0, 0, 64, 8, 6, 0, 0, 0, 170, 105, 113, +222, 0, 0, 14, 35, 73, 68, 65, 84, 120, 218, 237, 154, 107, 120, 85, +213, 153, 199, 127, 107, 173, 125, 206, 201, 61, 64, 2, 134, 91, 128, 128, +220, 68, 228, 18, 20, 42, 80, 91, 25, 69, 199, 94, 102, 106, 199, 62, +79, 109, 71, 208, 90, 113, 170, 117, 70, 234, 76, 189, 128, 143, 90, 160, +80, 43, 106, 103, 28, 28, 177, 66, 109, 173, 118, 112, 218, 194, 192, 168, +8, 142, 40, 96, 212, 138, 160, 32, 23, 73, 8, 152, 144, 144, 144, 251, +57, 103, 159, 189, 247, 122, 231, 67, 78, 32, 4, 144, 36, 196, 118, 90, +242, 255, 144, 147, 103, 159, 179, 215, 218, 255, 247, 254, 190, 107, 67, 55, +186, 209, 141, 110, 116, 163, 27, 221, 56, 39, 144, 157, 125, 78, 211, 87, +128, 65, 169, 111, 158, 171, 2, 8, 3, 15, 2, 114, 46, 91, 193, 126, +101, 28, 105, 101, 17, 231, 142, 249, 103, 142, 152, 16, 9, 247, 236, 35, +105, 131, 70, 74, 74, 94, 126, 79, 64, 211, 242, 231, 47, 28, 67, 209, +218, 52, 126, 188, 125, 195, 208, 239, 62, 100, 197, 247, 8, 226, 177, 244, +150, 47, 157, 46, 222, 76, 3, 6, 240, 90, 93, 115, 146, 126, 23, 252, +81, 105, 107, 237, 40, 165, 95, 149, 192, 175, 196, 218, 175, 139, 181, 159, +203, 28, 89, 136, 210, 26, 16, 213, 250, 129, 187, 2, 45, 130, 156, 9, +20, 1, 135, 129, 163, 64, 5, 176, 3, 184, 62, 249, 125, 168, 203, 35, +187, 49, 10, 99, 218, 250, 180, 163, 148, 126, 62, 103, 242, 149, 211, 209, +250, 43, 202, 56, 59, 70, 207, 91, 41, 214, 79, 224, 71, 27, 208, 78, +168, 169, 171, 181, 62, 27, 112, 147, 154, 182, 90, 107, 107, 140, 177, 198, +24, 171, 148, 178, 201, 235, 2, 252, 168, 235, 168, 171, 102, 97, 134, 195, +131, 209, 218, 73, 94, 171, 5, 80, 218, 60, 154, 81, 48, 70, 46, 94, +185, 77, 148, 113, 108, 206, 37, 87, 74, 225, 83, 91, 165, 240, 201, 45, +98, 210, 179, 100, 240, 13, 247, 154, 179, 13, 130, 173, 45, 103, 3, 32, +161, 80, 232, 24, 209, 11, 47, 188, 80, 102, 206, 156, 41, 83, 166, 76, +105, 33, 46, 225, 112, 184, 229, 251, 93, 93, 96, 112, 202, 164, 103, 78, +75, 174, 183, 223, 164, 164, 245, 5, 130, 212, 126, 5, 162, 180, 249, 126, +36, 183, 175, 76, 90, 94, 36, 227, 30, 94, 35, 105, 249, 195, 101, 210, +242, 34, 153, 248, 196, 235, 114, 209, 195, 255, 45, 104, 35, 167, 35, 210, +17, 88, 224, 60, 224, 45, 173, 245, 23, 0, 110, 188, 241, 70, 85, 92, +92, 140, 136, 176, 125, 251, 118, 214, 173, 91, 199, 230, 205, 155, 17, 17, +234, 235, 235, 89, 177, 98, 133, 106, 86, 88, 120, 4, 80, 214, 73, 173, +135, 1, 148, 97, 181, 14, 165, 188, 126, 254, 237, 143, 160, 195, 17, 9, +226, 209, 178, 113, 75, 255, 71, 185, 149, 7, 69, 71, 82, 151, 142, 93, +252, 123, 108, 34, 78, 237, 246, 45, 140, 158, 183, 18, 155, 136, 163, 140, +193, 111, 56, 26, 96, 131, 61, 109, 171, 163, 206, 104, 223, 2, 18, 137, +68, 36, 35, 35, 67, 237, 222, 189, 155, 156, 156, 28, 130, 32, 192, 24, +67, 77, 242, 135, 61, 91, 164, 37, 2, 34, 104, 173, 185, 245, 214, 91, +121, 242, 201, 39, 9, 130, 224, 101, 224, 234, 118, 6, 71, 3, 4, 78, +70, 246, 223, 248, 141, 117, 191, 26, 116, 253, 93, 161, 190, 127, 61, 203, +188, 59, 103, 58, 226, 39, 184, 232, 39, 107, 64, 44, 149, 27, 87, 73, +222, 204, 235, 149, 77, 184, 205, 228, 156, 16, 226, 55, 199, 227, 160, 169, +158, 234, 173, 47, 37, 14, 254, 230, 177, 69, 192, 252, 214, 11, 119, 180, +156, 124, 28, 56, 228, 56, 206, 156, 17, 35, 70, 168, 146, 146, 18, 76, +56, 140, 163, 53, 139, 181, 102, 25, 176, 89, 132, 29, 192, 7, 74, 17, +3, 250, 43, 133, 163, 20, 34, 194, 53, 215, 92, 67, 78, 78, 14, 47, +189, 244, 210, 80, 17, 249, 55, 224, 211, 2, 82, 8, 176, 161, 172, 94, +35, 172, 27, 123, 45, 163, 224, 194, 219, 70, 207, 91, 105, 210, 7, 143, +212, 54, 225, 226, 55, 213, 202, 200, 127, 89, 166, 36, 225, 130, 210, 164, +15, 30, 173, 196, 182, 146, 167, 181, 199, 254, 141, 31, 62, 64, 197, 171, +47, 104, 175, 161, 230, 30, 196, 150, 39, 149, 216, 41, 11, 232, 13, 84, +2, 34, 34, 202, 179, 150, 168, 214, 204, 0, 6, 4, 1, 125, 140, 33, +7, 200, 20, 161, 64, 41, 28, 224, 50, 32, 167, 205, 34, 115, 230, 204, +97, 217, 178, 101, 149, 34, 50, 160, 85, 144, 84, 40, 101, 16, 113, 1, +156, 244, 172, 175, 249, 77, 245, 243, 82, 251, 21, 140, 29, 114, 211, 124, +63, 125, 200, 5, 142, 141, 71, 65, 117, 236, 177, 173, 239, 225, 86, 148, +178, 251, 225, 219, 152, 240, 228, 155, 250, 237, 235, 199, 210, 82, 18, 171, +206, 165, 88, 29, 47, 46, 46, 142, 12, 24, 48, 0, 173, 53, 195, 129, +188, 68, 130, 212, 170, 42, 222, 190, 226, 10, 106, 62, 252, 176, 57, 84, +165, 167, 227, 53, 54, 18, 156, 198, 212, 148, 82, 86, 105, 51, 199, 201, +200, 222, 134, 54, 141, 226, 123, 189, 253, 104, 195, 120, 108, 112, 37, 48, +51, 103, 202, 85, 244, 187, 102, 182, 159, 58, 96, 168, 227, 55, 213, 163, +180, 233, 84, 232, 136, 149, 237, 71, 124, 223, 238, 90, 112, 227, 251, 192, +132, 83, 229, 239, 118, 115, 79, 79, 79, 207, 204, 206, 206, 142, 228, 231, +231, 227, 1, 223, 0, 156, 32, 32, 81, 95, 207, 166, 254, 253, 209, 161, +227, 169, 254, 209, 37, 75, 112, 93, 151, 72, 36, 114, 202, 197, 54, 110, +220, 168, 191, 250, 79, 247, 63, 161, 64, 219, 120, 20, 39, 35, 155, 180, +65, 35, 252, 236, 11, 38, 75, 230, 200, 137, 226, 213, 86, 41, 17, 113, +130, 88, 83, 167, 201, 75, 224, 97, 227, 81, 202, 86, 63, 29, 0, 115, +91, 197, 176, 142, 7, 193, 148, 188, 65, 17, 255, 200, 193, 223, 110, 217, +250, 214, 204, 194, 194, 194, 99, 129, 174, 31, 176, 47, 63, 31, 175, 188, +28, 241, 125, 84, 210, 68, 109, 43, 31, 60, 21, 130, 32, 32, 111, 250, +151, 25, 244, 247, 119, 163, 148, 66, 2, 31, 9, 2, 208, 186, 211, 132, +219, 34, 122, 104, 31, 38, 28, 97, 231, 67, 179, 176, 9, 87, 37, 57, +75, 7, 44, 64, 105, 16, 11, 220, 225, 30, 249, 228, 145, 200, 128, 17, +254, 136, 225, 195, 9, 128, 117, 64, 96, 45, 137, 67, 135, 72, 28, 60, +120, 60, 100, 27, 195, 140, 25, 51, 206, 28, 218, 141, 129, 163, 101, 205, +228, 173, 5, 165, 81, 78, 215, 181, 39, 65, 60, 138, 248, 9, 106, 119, +22, 121, 54, 225, 62, 210, 98, 20, 29, 170, 3, 116, 74, 74, 14, 16, +239, 61, 237, 203, 139, 38, 253, 252, 29, 156, 30, 125, 156, 244, 244, 52, +44, 176, 22, 8, 249, 62, 53, 203, 151, 183, 245, 109, 66, 161, 19, 171, +94, 145, 230, 125, 253, 54, 235, 79, 159, 52, 17, 249, 44, 58, 116, 165, +112, 43, 15, 161, 67, 17, 202, 215, 173, 12, 153, 212, 140, 5, 167, 178, +120, 231, 12, 202, 159, 96, 93, 247, 221, 11, 23, 174, 146, 80, 86, 47, +21, 52, 213, 227, 228, 228, 161, 181, 198, 7, 74, 0, 35, 66, 188, 184, +184, 57, 50, 183, 144, 244, 125, 86, 175, 94, 125, 194, 82, 245, 245, 245, +248, 217, 217, 100, 182, 217, 162, 223, 144, 161, 148, 32, 93, 222, 156, 187, +149, 135, 64, 44, 209, 3, 251, 252, 32, 214, 180, 10, 168, 59, 83, 73, +219, 22, 83, 117, 40, 252, 238, 164, 229, 69, 132, 123, 244, 86, 202, 56, +160, 20, 61, 47, 154, 138, 239, 121, 40, 96, 32, 96, 149, 34, 52, 112, +32, 173, 213, 40, 34, 40, 165, 80, 74, 49, 99, 198, 12, 242, 7, 13, +226, 162, 139, 47, 38, 230, 251, 39, 141, 99, 106, 83, 115, 81, 93, 108, +1, 18, 4, 4, 177, 70, 148, 19, 162, 244, 215, 63, 117, 116, 36, 245, +166, 211, 197, 59, 125, 170, 220, 164, 156, 80, 190, 114, 66, 155, 10, 255, +99, 11, 54, 17, 59, 33, 239, 246, 186, 224, 98, 98, 241, 56, 74, 132, +43, 128, 132, 49, 244, 248, 214, 183, 78, 126, 136, 164, 64, 54, 110, 220, +200, 193, 210, 82, 174, 186, 238, 58, 68, 228, 164, 13, 247, 230, 140, 234, +242, 41, 85, 236, 208, 62, 16, 161, 106, 243, 218, 192, 38, 220, 7, 172, +27, 107, 60, 221, 38, 167, 179, 60, 153, 240, 179, 13, 40, 231, 228, 238, +213, 104, 205, 195, 227, 83, 153, 90, 56, 30, 128, 12, 96, 16, 176, 175, +111, 95, 188, 35, 71, 154, 163, 120, 27, 95, 68, 132, 18, 17, 194, 64, +94, 155, 77, 39, 45, 123, 163, 11, 233, 43, 226, 21, 7, 177, 110, 19, +202, 56, 236, 124, 104, 54, 218, 9, 25, 235, 185, 182, 61, 93, 93, 11, +110, 239, 253, 249, 191, 77, 156, 174, 218, 10, 108, 192, 173, 47, 22, 29, +11, 104, 151, 39, 77, 110, 204, 166, 77, 39, 147, 111, 54, 5, 230, 44, +89, 194, 209, 68, 2, 221, 138, 188, 0, 207, 188, 246, 46, 182, 11, 53, +111, 221, 40, 214, 109, 2, 20, 123, 150, 222, 97, 17, 59, 226, 211, 200, +183, 21, 128, 74, 106, 236, 209, 130, 155, 230, 135, 149, 113, 78, 43, 229, +212, 130, 177, 124, 176, 191, 20, 7, 248, 29, 224, 27, 195, 63, 14, 27, +134, 136, 32, 34, 60, 255, 252, 243, 199, 165, 185, 114, 37, 215, 222, 113, +7, 38, 28, 38, 23, 136, 121, 150, 239, 252, 126, 59, 147, 150, 189, 193, +111, 203, 33, 172, 187, 38, 252, 137, 181, 196, 43, 74, 1, 104, 216, 253, +30, 126, 67, 237, 122, 96, 79, 123, 154, 155, 22, 83, 117, 148, 19, 154, +154, 61, 106, 210, 43, 5, 55, 63, 232, 156, 202, 252, 91, 35, 110, 225, +237, 111, 143, 35, 146, 150, 78, 141, 82, 39, 213, 250, 27, 173, 165, 88, +105, 122, 168, 230, 98, 41, 167, 46, 198, 63, 252, 231, 54, 92, 63, 224, +159, 167, 13, 99, 123, 69, 61, 107, 247, 86, 118, 89, 10, 140, 30, 248, +40, 233, 110, 150, 93, 11, 111, 70, 135, 34, 218, 122, 238, 25, 87, 119, +90, 153, 170, 47, 94, 98, 118, 223, 47, 205, 22, 218, 81, 133, 165, 106, +152, 250, 243, 34, 222, 190, 249, 115, 244, 116, 34, 208, 74, 147, 141, 192, +102, 173, 201, 21, 33, 132, 226, 199, 191, 40, 162, 162, 209, 101, 241, 21, +163, 112, 180, 226, 135, 235, 63, 194, 11, 164, 163, 61, 205, 105, 243, 125, +236, 147, 253, 199, 2, 245, 174, 133, 55, 11, 208, 179, 61, 228, 79, 21, +4, 155, 38, 63, 183, 51, 213, 171, 171, 86, 237, 190, 89, 107, 158, 189, +172, 55, 195, 207, 63, 31, 17, 33, 80, 138, 120, 82, 178, 171, 62, 40, +99, 233, 27, 31, 243, 249, 33, 185, 124, 119, 98, 62, 63, 120, 101, 23, +165, 117, 49, 140, 234, 162, 172, 175, 20, 110, 85, 25, 65, 83, 61, 74, +107, 74, 158, 89, 64, 244, 147, 253, 55, 32, 118, 69, 71, 250, 251, 150, +79, 113, 50, 122, 200, 248, 199, 215, 99, 221, 88, 251, 42, 100, 154, 7, +29, 94, 96, 233, 31, 14, 184, 103, 84, 152, 75, 47, 189, 20, 128, 111, +255, 215, 251, 20, 29, 170, 225, 233, 175, 92, 196, 235, 7, 170, 89, 181, +243, 112, 151, 31, 201, 248, 13, 53, 36, 106, 42, 65, 105, 170, 223, 92, +67, 229, 107, 47, 174, 75, 14, 89, 58, 60, 205, 85, 225, 158, 125, 242, +76, 106, 6, 226, 37, 218, 19, 113, 168, 121, 103, 3, 251, 159, 154, 79, +225, 83, 91, 113, 172, 203, 17, 27, 102, 206, 14, 48, 239, 188, 66, 40, +100, 176, 38, 204, 226, 191, 26, 197, 220, 151, 119, 17, 243, 131, 179, 215, +186, 200, 9, 245, 72, 16, 109, 108, 38, 15, 196, 14, 237, 181, 149, 175, +189, 88, 213, 81, 242, 173, 179, 128, 178, 158, 155, 231, 100, 246, 192, 182, +71, 0, 74, 211, 235, 146, 43, 232, 123, 205, 108, 254, 48, 103, 58, 65, +172, 17, 27, 248, 132, 9, 48, 41, 169, 88, 19, 38, 53, 100, 146, 190, +110, 59, 79, 94, 4, 9, 124, 156, 204, 30, 196, 43, 15, 30, 75, 160, +65, 60, 138, 123, 228, 16, 98, 45, 137, 186, 163, 65, 201, 138, 133, 137, +228, 140, 178, 195, 48, 199, 102, 28, 161, 200, 128, 112, 175, 243, 190, 211, +171, 240, 242, 246, 181, 162, 98, 201, 26, 53, 137, 158, 227, 47, 99, 199, +15, 191, 70, 195, 158, 247, 200, 26, 49, 129, 72, 159, 129, 4, 209, 6, +188, 32, 32, 228, 116, 98, 250, 44, 130, 245, 61, 76, 106, 58, 126, 83, +29, 165, 191, 252, 9, 38, 53, 131, 180, 254, 67, 147, 185, 62, 142, 91, +113, 160, 57, 222, 196, 99, 193, 190, 199, 239, 52, 201, 209, 89, 167, 164, +220, 114, 147, 113, 210, 179, 38, 165, 13, 30, 189, 101, 216, 156, 5, 156, +41, 5, 158, 40, 135, 128, 112, 175, 243, 40, 250, 198, 168, 99, 69, 77, +175, 241, 211, 201, 186, 96, 50, 125, 46, 191, 14, 29, 73, 33, 112, 163, +4, 209, 198, 100, 87, 173, 154, 77, 185, 197, 42, 146, 3, 83, 65, 8, +101, 246, 196, 38, 226, 148, 254, 114, 9, 94, 99, 29, 57, 147, 103, 210, +171, 112, 6, 65, 188, 9, 68, 8, 220, 24, 110, 69, 41, 98, 3, 108, +194, 245, 247, 60, 242, 125, 71, 135, 194, 97, 235, 123, 1, 34, 157, 170, +169, 156, 227, 86, 109, 170, 131, 104, 67, 135, 200, 39, 239, 67, 188, 4, +22, 184, 115, 238, 92, 42, 43, 43, 57, 92, 81, 193, 129, 45, 171, 40, +122, 118, 113, 243, 143, 178, 114, 233, 49, 108, 12, 41, 61, 123, 19, 238, +145, 75, 36, 183, 63, 225, 156, 60, 172, 151, 192, 107, 168, 33, 94, 94, +140, 91, 117, 152, 35, 239, 108, 4, 183, 121, 70, 122, 201, 179, 59, 240, +234, 171, 155, 201, 35, 4, 241, 38, 220, 202, 131, 136, 181, 248, 77, 245, +222, 190, 159, 221, 21, 50, 225, 212, 112, 224, 197, 109, 103, 201, 183, 22, +128, 152, 140, 236, 74, 191, 161, 6, 229, 132, 17, 63, 209, 161, 69, 188, +250, 163, 228, 92, 114, 37, 219, 183, 239, 32, 127, 64, 127, 10, 6, 13, +98, 252, 152, 49, 28, 168, 60, 66, 93, 125, 45, 229, 101, 135, 137, 186, +181, 12, 176, 154, 216, 193, 18, 106, 222, 127, 153, 192, 6, 84, 214, 215, +147, 149, 145, 193, 176, 252, 193, 100, 244, 200, 34, 229, 75, 87, 209, 16, +141, 177, 205, 244, 37, 136, 30, 159, 1, 250, 141, 245, 184, 213, 229, 128, +194, 173, 42, 75, 20, 47, 127, 64, 0, 29, 120, 113, 131, 200, 89, 157, +57, 182, 245, 27, 153, 252, 220, 78, 188, 186, 234, 78, 69, 232, 119, 111, +153, 198, 13, 179, 102, 83, 252, 214, 102, 174, 110, 168, 101, 218, 121, 189, +25, 156, 149, 69, 86, 74, 132, 114, 55, 193, 75, 229, 21, 172, 193, 161, +247, 184, 241, 76, 222, 253, 1, 19, 243, 242, 40, 119, 93, 118, 85, 215, +176, 91, 41, 182, 226, 80, 242, 254, 31, 24, 245, 227, 213, 152, 244, 44, +208, 6, 175, 166, 2, 175, 174, 26, 29, 142, 112, 244, 237, 87, 253, 242, +181, 43, 170, 128, 190, 45, 103, 5, 103, 223, 62, 157, 136, 213, 99, 30, +120, 110, 102, 164, 119, 63, 167, 57, 207, 119, 164, 7, 247, 41, 251, 245, +35, 220, 80, 180, 134, 133, 61, 211, 112, 125, 31, 227, 56, 205, 46, 229, +56, 136, 49, 248, 198, 144, 146, 158, 206, 156, 226, 50, 158, 24, 210, 143, +132, 19, 66, 180, 131, 53, 154, 212, 212, 20, 42, 140, 195, 192, 125, 181, +140, 187, 247, 233, 99, 163, 236, 32, 218, 128, 73, 203, 224, 208, 11, 143, +83, 187, 99, 243, 155, 192, 212, 182, 115, 189, 46, 18, 128, 114, 148, 214, +151, 101, 143, 155, 182, 182, 96, 246, 188, 80, 71, 99, 65, 114, 142, 207, +91, 179, 10, 113, 211, 5, 130, 128, 112, 235, 103, 148, 227, 159, 113, 129, +251, 173, 98, 81, 68, 33, 201, 1, 168, 104, 197, 224, 170, 56, 189, 126, +186, 22, 147, 150, 73, 236, 147, 253, 216, 68, 12, 29, 78, 97, 239, 99, +115, 9, 98, 13, 11, 108, 194, 189, 167, 171, 39, 103, 186, 181, 14, 197, +6, 235, 107, 223, 251, 223, 80, 168, 71, 110, 167, 22, 243, 26, 106, 24, +183, 232, 69, 114, 234, 124, 194, 234, 196, 151, 4, 104, 153, 199, 106, 136, +24, 88, 20, 22, 176, 22, 229, 251, 4, 9, 151, 245, 110, 64, 195, 23, +175, 197, 164, 103, 17, 61, 176, 27, 177, 1, 126, 83, 131, 191, 243, 193, +27, 16, 235, 207, 192, 113, 238, 251, 12, 38, 135, 167, 60, 175, 168, 243, +142, 86, 126, 33, 107, 228, 68, 167, 163, 163, 105, 165, 52, 40, 77, 230, +196, 203, 184, 115, 253, 239, 248, 65, 138, 66, 147, 236, 147, 212, 169, 253, +78, 146, 247, 77, 242, 83, 24, 252, 189, 37, 68, 75, 119, 99, 210, 50, +168, 126, 115, 13, 7, 159, 95, 90, 9, 100, 90, 63, 81, 38, 137, 132, +255, 89, 8, 224, 180, 19, 161, 137, 255, 190, 233, 164, 242, 179, 221, 241, +192, 79, 224, 86, 29, 230, 195, 251, 191, 201, 91, 25, 134, 139, 85, 128, +109, 101, 4, 199, 136, 3, 49, 20, 185, 65, 6, 195, 31, 248, 21, 238, +209, 10, 172, 27, 183, 123, 150, 222, 161, 149, 214, 255, 106, 189, 196, 247, +186, 210, 223, 219, 39, 0, 173, 181, 137, 164, 14, 69, 233, 61, 227, 31, +123, 229, 216, 233, 106, 199, 7, 147, 62, 202, 9, 241, 209, 221, 215, 146, +93, 125, 152, 13, 25, 138, 225, 173, 234, 31, 207, 10, 155, 98, 240, 213, +252, 209, 12, 154, 117, 31, 54, 17, 167, 234, 141, 53, 84, 190, 182, 234, +168, 147, 150, 117, 190, 31, 107, 104, 64, 196, 227, 51, 198, 167, 168, 87, +221, 150, 58, 160, 224, 177, 11, 230, 255, 226, 212, 163, 174, 14, 180, 172, +241, 178, 98, 138, 151, 221, 139, 91, 87, 133, 177, 22, 147, 158, 69, 250, +176, 177, 156, 127, 203, 2, 106, 247, 110, 195, 61, 82, 198, 254, 167, 230, +163, 157, 240, 66, 235, 39, 238, 110, 123, 124, 245, 39, 18, 0, 128, 122, +34, 181, 255, 144, 91, 70, 223, 183, 242, 236, 172, 80, 4, 17, 139, 14, +69, 80, 74, 97, 61, 151, 68, 205, 17, 226, 229, 37, 124, 188, 236, 30, +36, 8, 182, 73, 224, 143, 79, 22, 102, 62, 127, 68, 156, 217, 193, 149, +158, 171, 180, 90, 50, 110, 233, 203, 232, 112, 132, 179, 154, 97, 41, 5, +214, 82, 183, 99, 51, 197, 207, 44, 32, 136, 55, 150, 25, 39, 92, 16, +120, 174, 127, 182, 21, 221, 103, 39, 128, 230, 7, 31, 136, 72, 241, 192, +175, 223, 110, 250, 204, 248, 187, 164, 49, 72, 199, 136, 11, 84, 108, 248, +13, 135, 94, 120, 20, 180, 222, 171, 148, 30, 43, 214, 186, 136, 253, 147, +190, 186, 170, 58, 70, 66, 158, 0, 110, 25, 60, 235, 94, 114, 167, 92, +213, 124, 160, 249, 169, 198, 163, 169, 251, 96, 43, 37, 43, 23, 225, 213, +85, 129, 200, 131, 192, 188, 227, 149, 147, 3, 190, 207, 159, 135, 0, 142, +119, 127, 142, 216, 224, 71, 74, 235, 187, 76, 90, 166, 205, 157, 114, 53, +105, 67, 70, 161, 67, 17, 252, 198, 122, 154, 74, 63, 162, 110, 199, 102, +188, 218, 42, 157, 204, 32, 139, 129, 251, 77, 36, 45, 22, 184, 81, 254, +191, 161, 211, 115, 170, 80, 86, 47, 229, 213, 215, 100, 128, 140, 6, 46, +1, 210, 104, 126, 65, 114, 27, 176, 95, 135, 34, 113, 235, 185, 137, 214, +167, 67, 127, 137, 80, 167, 113, 151, 115, 234, 77, 236, 110, 116, 163, 27, +221, 232, 70, 55, 254, 76, 241, 127, 245, 103, 83, 89, 108, 29, 88, 9, +0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130 +}; + +#endif diff --git a/gb.qt4/src/CButton.cpp b/gb.qt4/src/CButton.cpp new file mode 100644 index 00000000..ca7bdea5 --- /dev/null +++ b/gb.qt4/src/CButton.cpp @@ -0,0 +1,685 @@ +/*************************************************************************** + + CButton.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CBUTTON_CPP + +#include +#include +#include +#include +#include +#include +#include +#include +//Added by qt3to4: +#include +#include +#include +#include + +#include "gambas.h" + +#include "CWidget.h" +#include "CPicture.h" +#include "CWindow.h" +#include "CButton.h" + +/*#define DEBUG_CBUTTON*/ + +DECLARE_EVENT(EVENT_Click); +DECLARE_EVENT(EVENT_ClickToggle); +DECLARE_EVENT(EVENT_ClickTool); + +//--------------------------------------------------------------------------- + +static void set_button_text(CBUTTON *_object, const QString &text) +{ + WIDGET->setText(text); + WIDGET->calcMinimumSize(); +} + +static void set_button_picture(CBUTTON *_object) +{ + QPixmap p; + QIcon icon; + + if (THIS->picture) + { + p = *(THIS->picture->pixmap); + CWIDGET_iconset(icon, p); + WIDGET->setIcon(icon); + WIDGET->setIconSize(p.size()); + } + else + { + WIDGET->setIcon(icon); + } + + WIDGET->calcMinimumSize(); +} + +static void set_tool_button(CBUTTON *_object, bool set_text, QString text) +{ + QPixmap p; + QIcon icon; + + if (!set_text) + text = WIDGET_TOOL->text(); + + if (THIS->picture) + { + p = *(THIS->picture->pixmap); + + WIDGET_TOOL->setText(text); + CWIDGET_iconset(icon, p); + + WIDGET_TOOL->setIcon(icon); + WIDGET->setIconSize(p.size()); + //WIDGET_TOOL->setUsesTextLabel(qtext.length() > 0); + if (text.length()) + WIDGET_TOOL->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + else + WIDGET_TOOL->setToolButtonStyle(Qt::ToolButtonIconOnly); + } + else + { + WIDGET_TOOL->setIcon(icon); + WIDGET_TOOL->setText(text); + WIDGET_TOOL->setToolButtonStyle(Qt::ToolButtonTextOnly); + //WIDGET_TOOL->setUsesTextLabel(qtext.length() > 0); + } + + WIDGET->calcMinimumSize(); +} + +//--------------------------------------------------------------------------- + + +BEGIN_METHOD(CBUTTON_new, GB_OBJECT parent) + + MyPushButton *wid = new MyPushButton(QCONTAINER(VARG(parent))); + + QObject::connect(wid, SIGNAL(clicked()), &CButton::manager, SLOT(clicked())); + + wid->setAutoDefault(false); + + CWIDGET_new(wid, (void *)_object); + + // We assume that the button widget destructor will always be called before + // its gambas window is released. + WIDGET->top = CWidget::getWindow((CWIDGET *)THIS); + +END_METHOD + + +BEGIN_METHOD(CTOGGLEBUTTON_new, GB_OBJECT parent) + + QPushButton *wid = new MyPushButton(QCONTAINER(VARG(parent))); + + QObject::connect(wid, SIGNAL(toggled(bool)), &CButton::manager, SLOT(clickedToggle())); + + //wid->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + wid->setAutoDefault(false); + wid->setCheckable(TRUE); + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_METHOD(CTOOLBUTTON_new, GB_OBJECT parent) + + MyToolButton *wid = new MyToolButton(QCONTAINER(VARG(parent))); + + QObject::connect(wid, SIGNAL(clicked()), &CButton::manager, SLOT(clickedTool())); + + //wid->setToggleButton(TRUE); + wid->setAutoRaise(true); + + CWIDGET_new(wid, (void *)_object); + wid->calcMinimumSize(); + +END_METHOD + + +BEGIN_METHOD_VOID(CBUTTON_free) + + CLEAR_PICTURE(&(OBJECT(CBUTTON)->picture)); + +END_METHOD + +BEGIN_PROPERTY(CBUTTON_text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->text()); + else + set_button_text(THIS, QSTRING_PROP()); + +END_PROPERTY + + +BEGIN_PROPERTY(CBUTTON_picture) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->picture); + else + { + GB.StoreObject(PROP(GB_OBJECT), POINTER(&(THIS->picture))); + set_button_picture(THIS); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CTOOLBUTTON_text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->text()); + else + set_tool_button(THIS, true, QSTRING_PROP()); + +END_PROPERTY + + +BEGIN_PROPERTY(CTOOLBUTTON_picture) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->picture); + else + { + GB.StoreObject(PROP(GB_OBJECT), POINTER(&(THIS->picture))); + set_tool_button(THIS, false, QString()); + } + +END_PROPERTY + +/* +BEGIN_PROPERTY(CTOOLBUTTON_picture) + + QToolButton *wid = QTOOLBUTTON(_object); + char **pict = (char **)&(OBJECT(CBUTTON)->picture); + + if (READ_PROPERTY) + GB.ReturnString(*pict, 0); + else + wid->setIconSet(QIconSet(*PIXMAP_set_widget(PROPERTY(GB_STRING), pict), QIconSet::Small)); + +END_PROPERTY +*/ + +BEGIN_PROPERTY(CBUTTON_value) + + if (READ_PROPERTY) + GB.ReturnBoolean(0); + else if (VPROP(GB_BOOLEAN)) + WIDGET->animateClick(); + +END_PROPERTY + + +BEGIN_PROPERTY(CTOGGLEBUTTON_value) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isChecked()); + else + WIDGET->setChecked(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTOOLBUTTON_value) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET_TOOL->isChecked()); + else + { + if (WIDGET->isCheckable()) + WIDGET_TOOL->setChecked(VPROP(GB_BOOLEAN)); + else + WIDGET->animateClick(); + //qApp->postEvent(WIDGET_TOOL, new QEvent(QEvent::Leave)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CBUTTON_flat) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isFlat()); + else + WIDGET->setFlat(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CBUTTON_border) + + if (READ_PROPERTY) + GB.ReturnBoolean(!WIDGET->isFlat()); + else + WIDGET->setFlat(!VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTOOLBUTTON_border) + + if (READ_PROPERTY) + GB.ReturnBoolean(!WIDGET_TOOL->autoRaise()); + else + WIDGET_TOOL->setAutoRaise(!VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTOOLBUTTON_toggle) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET_TOOL->isCheckable()); + else + { + WIDGET_TOOL->setCheckable(VPROP(GB_BOOLEAN)); + QObject::disconnect(WIDGET_TOOL, 0, &CButton::manager, 0); + if (VPROP(GB_BOOLEAN)) + QObject::connect(WIDGET_TOOL, SIGNAL(toggled(bool)), &CButton::manager, SLOT(clickedTool())); + else + QObject::connect(WIDGET_TOOL, SIGNAL(clicked()), &CButton::manager, SLOT(clickedTool())); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CBUTTON_default) + + CWINDOW *top = CWidget::getWindow((CWIDGET *)THIS); + + if (READ_PROPERTY) + GB.ReturnBoolean(top->defaultButton == WIDGET); + else + CWINDOW_set_default_button(top, WIDGET, VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CBUTTON_cancel) + + CWINDOW *top = CWidget::getWindow((CWIDGET *)THIS); + + if (READ_PROPERTY) + GB.ReturnBoolean(top->cancelButton == WIDGET); + else + CWINDOW_set_cancel_button(top, WIDGET, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CBUTTON_radio) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->radio); + else + THIS->radio = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(CBUTTON_autoresize) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->autoresize); + else if (THIS->autoresize != VPROP(GB_BOOLEAN)) + { + THIS->autoresize = VPROP(GB_BOOLEAN); + WIDGET->calcMinimumSize(); + } + +END_PROPERTY + +BEGIN_PROPERTY(CTOOLBUTTON_autoresize) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->autoresize); + else if (THIS->autoresize != VPROP(GB_BOOLEAN)) + { + THIS->autoresize = VPROP(GB_BOOLEAN); + WIDGET_TOOL->calcMinimumSize(); + } + +END_PROPERTY + +#if 0 +BEGIN_PROPERTY(CBUTTON_stretch) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->stretch); + else if (THIS->stretch != VPROP(GB_BOOLEAN)) + { + THIS->stretch = VPROP(GB_BOOLEAN); + set_button(THIS, NULL, true); + } + +END_PROPERTY + +BEGIN_PROPERTY(CTOOLBUTTON_stretch) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->stretch); + else if (THIS->stretch != VPROP(GB_BOOLEAN)) + { + THIS->stretch = VPROP(GB_BOOLEAN); + set_tool_button(THIS, NULL, true); + } + +END_PROPERTY +#endif + +#define CTOGGLEBUTTON_free CBUTTON_free +#define CTOGGLEBUTTON_text CBUTTON_text +#define CTOGGLEBUTTON_picture CBUTTON_picture +#define CTOGGLEBUTTON_border CBUTTON_border +#define CTOGGLEBUTTON_radio CBUTTON_radio +#define CTOGGLEBUTTON_stretch CBUTTON_stretch + +#define CTOOLBUTTON_free CBUTTON_free +#define CTOOLBUTTON_radio CBUTTON_radio + +GB_DESC CButtonDesc[] = +{ + GB_DECLARE("Button", sizeof(CBUTTON)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CBUTTON_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, CBUTTON_free, NULL), + + GB_PROPERTY("Text", "s", CBUTTON_text), + GB_PROPERTY("Caption", "s", CBUTTON_text), + GB_PROPERTY("Picture", "Picture", CBUTTON_picture), + + GB_PROPERTY("Border", "b", CBUTTON_border), + GB_PROPERTY("Default", "b", CBUTTON_default), + GB_PROPERTY("Cancel", "b", CBUTTON_cancel), + GB_PROPERTY("Value", "b", CBUTTON_value), + GB_PROPERTY("AutoResize", "b", CBUTTON_autoresize), + + BUTTON_DESCRIPTION, + + GB_EVENT("Click", NULL, NULL, &EVENT_Click), + + GB_END_DECLARE +}; + +GB_DESC CToggleButtonDesc[] = +{ + GB_DECLARE("ToggleButton", sizeof(CBUTTON)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CTOGGLEBUTTON_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, CTOGGLEBUTTON_free, NULL), + + GB_PROPERTY("Text", "s", CTOGGLEBUTTON_text), + GB_PROPERTY("Caption", "s", CTOGGLEBUTTON_text), + GB_PROPERTY("Picture", "Picture", CTOGGLEBUTTON_picture), + GB_PROPERTY("Value", "b", CTOGGLEBUTTON_value), + //GB_PROPERTY("Flat", "b", CBUTTON_flat), + GB_PROPERTY("Border", "b", CTOGGLEBUTTON_border), + GB_PROPERTY("Radio", "b", CTOGGLEBUTTON_radio), + GB_PROPERTY("AutoResize", "b", CBUTTON_autoresize), + + TOGGLEBUTTON_DESCRIPTION, + + GB_EVENT("Click", NULL, NULL, &EVENT_ClickToggle), + + GB_END_DECLARE +}; + + +GB_DESC CToolButtonDesc[] = +{ + GB_DECLARE("ToolButton", sizeof(CBUTTON)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CTOOLBUTTON_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, CTOOLBUTTON_free, NULL), + + GB_PROPERTY("Text", "s", CTOOLBUTTON_text), + GB_PROPERTY("Caption", "s", CTOOLBUTTON_text), + GB_PROPERTY("Picture", "Picture", CTOOLBUTTON_picture), + GB_PROPERTY("Value", "b", CTOOLBUTTON_value), + GB_PROPERTY("Toggle", "b", CTOOLBUTTON_toggle), + GB_PROPERTY("Border", "b", CTOOLBUTTON_border), + GB_PROPERTY("Radio", "b", CTOOLBUTTON_radio), + GB_PROPERTY("AutoResize", "b", CTOOLBUTTON_autoresize), + + TOOLBUTTON_DESCRIPTION, + + GB_EVENT("Click", NULL, NULL, &EVENT_ClickTool), + + GB_END_DECLARE +}; + + + +/** class MyPushButton *****************************************************/ + +MyPushButton::MyPushButton(QWidget *parent) : + QPushButton(parent) +{ + calcMinimumSize(); + top = 0; +} + +MyPushButton::~MyPushButton() +{ + if (top) + { + CWINDOW_set_default_button(top, this, false); + CWINDOW_set_cancel_button(top, this, false); + } +} + +/*QSize MyPushButton::sizeHint(void) const +{ + return QSize(width(), height()); +}*/ + +void MyPushButton::changeEvent(QEvent *e) +{ + QPushButton::changeEvent(e); + if (e->type() == QEvent::FontChange || e->type() == QEvent::StyleChange) + calcMinimumSize(); +} + +void MyPushButton::calcMinimumSize() +{ + CBUTTON *_object = (CBUTTON *)CWidget::getReal(this); + QSize size; + + if (!THIS || CWIDGET_test_flag(THIS, WF_DESIGN_LEADER)) + return; + + if (text().length() > 0) + { + QFontMetrics fm = fontMetrics(); + setMinimumHeight(fm.lineSpacing() + 4); + } + else + setMinimumHeight(0); + + setMinimumWidth(0); + if (THIS->autoresize) + { + size = sizeHint(); + CWIDGET_resize(THIS, size.width(), height()); + setMinimumWidth(size.width()); + } + + //qDebug("%p: %s: %d", this, text().latin1(), minimumHeight()); +} + +/*void MyPushButton::resizeEvent(QResizeEvent *e) +{ + set_button((CBUTTON *)CWidget::get(this), NULL, true); +}*/ + +/*void MyPushButton::paintEvent(QPaintEvent *) +{ + CBUTTON *_object = (CBUTTON *)CWidget::get(this); + QStylePainter p(this); + QStyleOptionToolButton opt; + initStyleOption(&opt); + if (THIS->picture) + opt.iconSize = THIS->picture->pixmap->size(); + p.drawComplexControl(QStyle::CC_PushButton, opt); +}*/ + + +/** class MyToolButton *****************************************************/ + +MyToolButton::MyToolButton(QWidget *parent) : + QToolButton(parent) +{ +} + +MyToolButton::~MyToolButton() +{ +} + +void MyToolButton::changeEvent(QEvent *e) +{ + QToolButton::changeEvent(e); + if (e->type() == QEvent::FontChange || e->type() == QEvent::StyleChange) + calcMinimumSize(); +} + +void MyToolButton::calcMinimumSize() +{ + CBUTTON *_object = (CBUTTON *)CWidget::get(this); + QSize size; + + if (!THIS || CWIDGET_test_flag(THIS, WF_DESIGN)) + return; + + if (text().length() > 0) + { + QFontMetrics fm = fontMetrics(); + setMinimumHeight(fm.lineSpacing() + 4); + } + else + setMinimumHeight(0); + + setMinimumWidth(0); + if (THIS->autoresize) + { + size = sizeHint(); + CWIDGET_resize(THIS, size.width(), height()); + setMinimumWidth(size.width()); + } +} + +/*void MyToolButton::resizeEvent(QResizeEvent *e) +{ + //set_tool_button((CBUTTON *)CWidget::get(this), NULL, true); +}*/ + +/*void MyToolButton::paintEvent(QPaintEvent *) +{ + CBUTTON *_object = (CBUTTON *)CWidget::get(this); + QStylePainter p(this); + QStyleOptionToolButton opt; + initStyleOption(&opt); + if (THIS->picture) + opt.iconSize = THIS->picture->pixmap->size(); + p.drawComplexControl(QStyle::CC_ToolButton, opt); +}*/ + + +/* Class CButton */ + +CButton CButton::manager; + +void CButton::onlyMe(CBUTTON *_object) +{ + QWidget *parent = WIDGET->parentWidget(); + QObjectList list = parent->children(); + int i; + QObject *o; + CBUTTON *other; + + for (i = 0; i < list.count(); i++) + { + o = list.at(i); + if (!o->isWidgetType()) + continue; + other = (CBUTTON *)CWidget::get(o); + if (other == THIS) + continue; + if (other->widget.ob.klass != THIS->widget.ob.klass) + continue; + if (!other->radio) + continue; + o->blockSignals(true); + /*if (qobject_cast(o)) + ((MyPushButton *)o)->setChecked(false); + else + ((MyToolButton *)o)->setChecked(false);*/ + (qobject_cast(o))->setChecked(false); + o->blockSignals(false); + } +} + +void CButton::clicked(void) +{ + RAISE_EVENT_ACTION(EVENT_Click); +} + +void CButton::clickedToggle(void) +{ + GET_SENDER(); + + if (THIS->radio) + { + if (WIDGET->isChecked()) + onlyMe(THIS); + else + { + WIDGET->setChecked(true); + return; + } + } + + RAISE_EVENT_ACTION(EVENT_ClickToggle); +} + +void CButton::clickedTool(void) +{ + GET_SENDER(); + + if (THIS->radio) + { + if (WIDGET_TOOL->isChecked()) + onlyMe(THIS); + else + { + WIDGET_TOOL->setChecked(true); + return; + } + } + + RAISE_EVENT_ACTION(EVENT_ClickTool); +} diff --git a/gb.qt4/src/CButton.h b/gb.qt4/src/CButton.h new file mode 100644 index 00000000..2cd5204a --- /dev/null +++ b/gb.qt4/src/CButton.h @@ -0,0 +1,114 @@ +/*************************************************************************** + + CButton.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CBUTTON_H +#define __CBUTTON_H + +#include +#include +#include +#include +//Added by qt3to4: +#include + +#include "gambas.h" +#include "CWidget.h" +#include "CPicture.h" +#include "CWindow.h" + +#ifndef __CBUTTON_CPP +extern GB_DESC CButtonDesc[]; +extern GB_DESC CToggleButtonDesc[]; +extern GB_DESC CToolButtonDesc[]; +#else + +#define QBUTTON(object) ((QButton *)((CWIDGET *)object)->widget) +#define QPUSHBUTTON(object) ((MyPushButton *)((CWIDGET *)object)->widget) +#define QTOOLBUTTON(object) ((MyToolButton *)((CWIDGET *)object)->widget) + +#define THIS OBJECT(CBUTTON) +#define WIDGET QPUSHBUTTON(_object) +#define WIDGET_TOOL QTOOLBUTTON(_object) + +#endif + +typedef + struct { + CWIDGET widget; + CPICTURE *picture; + unsigned radio : 1; + unsigned autoresize : 1; + } + CBUTTON; + +class MyPushButton : public QPushButton +{ + Q_OBJECT + +public: + + MyPushButton(QWidget *parent); + ~MyPushButton(); + virtual void changeEvent(QEvent *e); + void calcMinimumSize(); + //virtual void resizeEvent(QResizeEvent *e); + //virtual void paintEvent(QPaintEvent *e); + + CWINDOW *top; +}; + + +class MyToolButton : public QToolButton +{ + Q_OBJECT + +public: + + MyToolButton(QWidget *parent); + ~MyToolButton(); + virtual void changeEvent(QEvent *e); + void calcMinimumSize(); + //virtual void resizeEvent(QResizeEvent *e); + //virtual void paintEvent(QPaintEvent *e); +}; + + +class CButton : public QObject +{ + Q_OBJECT + +public: + + static CButton manager; + + static void onlyMe(CBUTTON *); + +public slots: + + void clicked(void); + void clickedToggle(void); + void clickedTool(void); +}; + + +#endif diff --git a/gb.qt4/src/CCheckBox.cpp b/gb.qt4/src/CCheckBox.cpp new file mode 100644 index 00000000..be5e3f54 --- /dev/null +++ b/gb.qt4/src/CCheckBox.cpp @@ -0,0 +1,184 @@ +/*************************************************************************** + + CCheckBox.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCHECKBOX_CPP + +#include "gambas.h" + +#include + +#include "CStyle.h" +#include "CCheckBox.h" + +/** MyCheckBox *************************************************************/ + +MyCheckBox::MyCheckBox(QWidget *parent) : QCheckBox(parent) +{ + _autoResize = false; +} + + +void MyCheckBox::changeEvent(QEvent *e) +{ + QCheckBox::changeEvent(e); + if (e->type() == QEvent::FontChange || e->type() == QEvent::StyleChange) + adjust(); +} + +void MyCheckBox::adjust(bool force) +{ + void *_object = CWidget::getReal(this); + bool a; + QSize hint; + + if (!THIS || (!_autoResize && !force) || CWIDGET_test_flag(THIS, WF_DESIGN) || text().length() <= 0) + return; + + a = _autoResize; + _autoResize = false; + hint = sizeHint(); + CWIDGET_resize(THIS, hint.width(), qMax(hint.height(), height())); + _autoResize = a; +} + +void MyCheckBox::resizeEvent(QResizeEvent *e) +{ + QCheckBox::resizeEvent(e); + + if (_autoResize && e->oldSize().width() != e->size().width()) + adjust(); +} + +/** CheckBox ***************************************************************/ + +DECLARE_EVENT(EVENT_Click); + + +BEGIN_METHOD(CCHECKBOX_new, GB_OBJECT parent) + + QCheckBox *wid = new MyCheckBox(QCONTAINER(VARG(parent))); + + QObject::connect(wid, SIGNAL(stateChanged(int)), &CCheckBox::manager, SLOT(clicked())); + + wid->setMinimumHeight(wid->sizeHint().height()); + + CWIDGET_new(wid, (void *)_object); + THIS->widget.flag.fillBackground = CSTYLE_fix_breeze; + +END_METHOD + + +BEGIN_PROPERTY(CheckBox_Text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->text()); + else + { + WIDGET->setText(QSTRING_PROP()); + WIDGET->adjust(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CheckBox_Value) + + /*if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isChecked()); + else + WIDGET->setChecked(VPROP(GB_BOOLEAN));*/ + + if (READ_PROPERTY) + { + switch(WIDGET->checkState()) + { + case Qt::Unchecked: GB.ReturnInteger(0); break; + case Qt::Checked: GB.ReturnInteger(-1); break; + case Qt::PartiallyChecked: GB.ReturnInteger(1); break; + } + } + else + { + if (WIDGET->isTristate() && VPROP(GB_INTEGER) == 1) + WIDGET->setCheckState(Qt::PartiallyChecked); + else + WIDGET->setCheckState(VPROP(GB_INTEGER) == 0 ? Qt::Unchecked : Qt::Checked); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CheckBox_TriState) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isTristate()); + else + WIDGET->setTristate(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CheckBox_AutoResize) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isAutoResize()); + else + WIDGET->setAutoResize(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +GB_DESC CCheckBoxDesc[] = +{ + GB_DECLARE("CheckBox", sizeof(CCHECKBOX)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CCHECKBOX_new, "(Parent)Container;"), + + GB_CONSTANT("False", "i", 0), + GB_CONSTANT("True", "i", -1), + GB_CONSTANT("None", "i", 1), + + GB_PROPERTY("Text", "s", CheckBox_Text), + GB_PROPERTY("Caption", "s", CheckBox_Text), + GB_PROPERTY("Value", "i", CheckBox_Value), + GB_PROPERTY("Tristate", "b", CheckBox_TriState), + GB_PROPERTY("AutoResize", "b", CheckBox_AutoResize), + + CHECKBOX_DESCRIPTION, + + GB_EVENT("Click", NULL, NULL, &EVENT_Click), + + GB_END_DECLARE +}; + + +/** CCheckBox *************************************************************/ + +CCheckBox CCheckBox::manager; + +void CCheckBox::clicked(void) +{ + RAISE_EVENT_ACTION(EVENT_Click); +} + + diff --git a/gb.qt4/src/CCheckBox.h b/gb.qt4/src/CCheckBox.h new file mode 100644 index 00000000..d20a11d9 --- /dev/null +++ b/gb.qt4/src/CCheckBox.h @@ -0,0 +1,83 @@ +/*************************************************************************** + + CCheckBox.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCHECKBOX_H +#define __CCHECKBOX_H + +#include "gambas.h" + +#include + +#include "CWidget.h" + +#ifndef __CCHECKBOX_CPP +extern GB_DESC CCheckBoxDesc[]; +#else + +#define THIS ((CCHECKBOX *)_object) +#define WIDGET ((MyCheckBox *)((CWIDGET *)_object)->widget) + +#endif + +typedef + struct { + CWIDGET widget; + } + CCHECKBOX; + +class MyCheckBox : public QCheckBox +{ + Q_OBJECT + +public: + + MyCheckBox(QWidget *parent); + void adjust(bool force = false); + bool isAutoResize() const { return _autoResize; } + void setAutoResize(bool a) { _autoResize = a; adjust(); } + +protected: + + virtual void changeEvent(QEvent *); + virtual void resizeEvent(QResizeEvent *); + +private: + + unsigned _autoResize : 1; +}; + +class CCheckBox : public QObject +{ + Q_OBJECT + +public: + + static CCheckBox manager; + +public slots: + + void clicked(void); + +}; + +#endif diff --git a/gb.qt4/src/CClipboard.cpp b/gb.qt4/src/CClipboard.cpp new file mode 100644 index 00000000..b4978214 --- /dev/null +++ b/gb.qt4/src/CClipboard.cpp @@ -0,0 +1,963 @@ +/*************************************************************************** + + CClipboard.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCLIPBOARD_CPP + +#include "gambas.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CWidget.h" +#include "CImage.h" +#include "CClipboard.h" + +CDRAG_INFO CDRAG_info = { 0 }; +bool CDRAG_dragging = false; +void *CDRAG_destination = 0; + +static CPICTURE *_picture = 0; +static int _picture_x = -1; +static int _picture_y = -1; + +enum { MIME_UNKNOWN, MIME_TEXT, MIME_IMAGE }; +enum { CLIPBOARD_DEFAULT, CLIPBOARD_SELECTION }; + +static int _current_clipboard = CLIPBOARD_DEFAULT; + +static int get_type(const QMimeData *src) +{ + if (src->hasImage()) + return MIME_IMAGE; + else if (src->formats().indexOf(QRegExp("text/.*")) >= 0) + return MIME_TEXT; + else + return MIME_UNKNOWN; +} + +static QString get_format(const QMimeData *src, int i = 0, bool charset = false) +{ + QStringList formats = src->formats(); + QString format; + + if (i >= 0 && i < formats.count()) + { + format = formats.at(i); + + if (!charset) + { + int pos = format.indexOf(';'); + if (pos >= 0) + format = format.left(pos); + } + } + + return format; +} + +static void get_formats(const QMimeData *src, GB_ARRAY array) +{ + int i, j; + QStringList formats = src->formats(); + QString fmt; + + for (i = 0; i < formats.count(); i++) + { + fmt = get_format(src, i, true); + if (!fmt[0].isLower()) + continue; + for (j = 0; j < GB.Array.Count(array); j++) + { + if (strcasecmp(fmt.toUtf8().data(), *((char **)GB.Array.Get(array, j))) == 0) + break; + } + if (j < GB.Array.Count(array)) + continue; + //fmt = get_format(src, i); + + *((char **)GB.Array.Add(array)) = GB.NewZeroString(fmt.toUtf8().data()); + } +} + +static QString get_first_format(const QMimeData *src) +{ + int i; + QString format; + + for (i = 0;; i++) + { + format = get_format(src, i); + if (format.length() && !format[0].isLower()) + continue; + break; + } + + return format; +} + +static bool paste(const QMimeData *data, const char *fmt) +{ + QString format; + QByteArray ba; + int type; +#if QT5 +#else + QTextCodec *codec = NULL; +#endif + + if (fmt) + format = fmt; + else + format = get_first_format(data); + + if (!data->hasFormat(format)) + { + GB.ReturnVariant(NULL); + return TRUE; + } + + if (format.startsWith("text/")) + type = MIME_TEXT; + else + type = get_type(data); + + switch(type) + { + case MIME_TEXT: + + ba = data->data(format); + + if (ba.size()) + { +#if QT5 + GB.ReturnNewString(ba.constData(), ba.size()); +#else + if (((uchar)ba[0] == 0xFE && (uchar)ba[1] == 0xFF) || ((uchar)ba[0] == 0xFF && (uchar)ba[1] == 0xFE)) + codec = QTextCodec::codecForUtfText(ba, NULL); + + if (codec) + RETURN_NEW_STRING(codec->toUnicode(ba)); + else + GB.ReturnNewString(ba.constData(), ba.size()); +#endif + } + else + GB.ReturnNull(); + break; + + case MIME_IMAGE: + { + QImage *image = new QImage(); + *image = qvariant_cast(data->imageData()); + *image = image->convertToFormat(QImage::Format_ARGB32_Premultiplied); + GB.ReturnObject(CIMAGE_create(image)); + } + break; + + default: + GB.ReturnNull(); + } + + GB.ReturnConvVariant(); + return FALSE; +} + + + +/*************************************************************************** + + Clipboard + +***************************************************************************/ + +static GB_ARRAY _clipboard_formats[2] = { NULL }; +static bool _clipboard_has_changed[2] = { FALSE }; + +#define CURRENT_MODE() (_current_clipboard == CLIPBOARD_SELECTION ? QClipboard::Selection : QClipboard::Clipboard) + +void CLIPBOARD_has_changed(QClipboard::Mode mode) +{ + int clipboard = mode == QClipboard::Selection ? CLIPBOARD_SELECTION : CLIPBOARD_DEFAULT; + GB.Unref(POINTER(&_clipboard_formats[clipboard])); + _clipboard_formats[clipboard] = NULL; + _clipboard_has_changed[clipboard] = TRUE; +} + +static GB_ARRAY load_clipboard_formats() +{ + if (!_clipboard_formats[_current_clipboard]) + { + //qDebug("load clipboard formats"); + GB.Array.New(&_clipboard_formats[_current_clipboard], GB_T_STRING, 0); + get_formats(QApplication::clipboard()->mimeData(CURRENT_MODE()), _clipboard_formats[_current_clipboard]); + GB.Ref(_clipboard_formats[_current_clipboard]); + } + + return _clipboard_formats[_current_clipboard]; +} + +static int get_clipboard_type() +{ + int i; + QString format; + GB_ARRAY formats; + + formats = load_clipboard_formats(); + + for (i = 0; i < GB.Array.Count(formats); i++) + { + format = *((char **)GB.Array.Get(formats, i)); + if (format.startsWith("text/")) + return MIME_TEXT; + else if (format.startsWith("image/")) + return MIME_IMAGE; + else if (format == "application/x-qt-image") + return MIME_IMAGE; + } + + return MIME_UNKNOWN; +} + +BEGIN_METHOD_VOID(Clipboard_exit) + + CLIPBOARD_has_changed(QClipboard::Clipboard); + CLIPBOARD_has_changed(QClipboard::Selection); + +END_METHOD + + +BEGIN_METHOD_VOID(Clipboard_Clear) + + QApplication::clipboard()->clear(CURRENT_MODE()); + +END_METHOD + + +BEGIN_PROPERTY(Clipboard_Format) + + GB_ARRAY formats = load_clipboard_formats(); + + if (GB.Array.Count(formats) == 0) + GB.ReturnVoidString(); + else + GB.ReturnString(*((char **)GB.Array.Get(formats, 0))); + +END_PROPERTY + + +BEGIN_PROPERTY(Clipboard_Formats) + + GB.ReturnObject(load_clipboard_formats()); + +END_PROPERTY + + +BEGIN_PROPERTY(Clipboard_Type) + + GB.ReturnInteger(get_clipboard_type()); + +END_PROPERTY + + +BEGIN_METHOD(Clipboard_Copy, GB_VARIANT data; GB_STRING format) + + QString format; + QMimeData *data = new QMimeData(); + + if (VARG(data).type == GB_T_STRING) + { + if (MISSING(format)) + format = "text/plain"; + else + { + format = TO_QSTRING(GB.ToZeroString(ARG(format))); + if (format.left(5) != "text/") + goto _BAD_FORMAT; + if (format.length() == 5) + goto _BAD_FORMAT; + } + + data->setData(format, QByteArray(VARG(data).value._string, GB.StringLength(VARG(data).value._string))); + QApplication::clipboard()->setMimeData(data, CURRENT_MODE()); + } + else if (VARG(data).type >= GB_T_OBJECT && GB.Is(VARG(data).value._object, CLASS_Image)) + { + QImage img; + + if (!MISSING(format)) + goto _BAD_FORMAT; + + img = *CIMAGE_get((CIMAGE *)VARG(data).value._object); + img.detach(); + + QApplication::clipboard()->setImage(img, CURRENT_MODE()); + } + else + goto _BAD_FORMAT; + + return; + +_BAD_FORMAT: + + GB.Error("Bad clipboard format"); + +END_METHOD + + +BEGIN_METHOD(Clipboard_Paste, GB_STRING format) + + if (!paste(QApplication::clipboard()->mimeData(CURRENT_MODE()), MISSING(format) ? NULL : GB.ToZeroString(ARG(format)))) + _clipboard_has_changed[_current_clipboard] = FALSE; + +END_METHOD + +BEGIN_PROPERTY(Clipboard_Current) + + if (READ_PROPERTY) + GB.ReturnInteger(_current_clipboard); + else + { + int val = VPROP(GB_INTEGER); + if (val != CLIPBOARD_DEFAULT && val != CLIPBOARD_SELECTION) + GB.Error(GB_ERR_ARG); + else + _current_clipboard = val; + } + +END_PROPERTY + + +BEGIN_PROPERTY(Clipboard_HasChanged) + + GB.ReturnBoolean(_clipboard_has_changed[_current_clipboard]); + +END_PROPERTY + +GB_DESC CClipboardDesc[] = +{ + GB_DECLARE_STATIC("Clipboard"), + + GB_STATIC_METHOD("_exit", NULL, Clipboard_exit, NULL), + + GB_CONSTANT("None", "i", 0), + GB_CONSTANT("Text", "i", 1), + GB_CONSTANT("Image", "i", 2), + + GB_CONSTANT("Default", "i", 0), + GB_CONSTANT("Selection", "i", 1), + + GB_STATIC_METHOD("Clear", NULL, Clipboard_Clear, NULL), + + GB_STATIC_PROPERTY_READ("Format", "s", Clipboard_Format), + GB_STATIC_PROPERTY_READ("Formats", "String[]", Clipboard_Formats), + GB_STATIC_PROPERTY_READ("Type", "i", Clipboard_Type), + GB_STATIC_PROPERTY_READ("HasChanged", "b", Clipboard_HasChanged), + + GB_STATIC_PROPERTY("Current", "i", Clipboard_Current), + + GB_STATIC_METHOD("Copy", NULL, Clipboard_Copy, "(Data)v[(Format)s]"), + GB_STATIC_METHOD("Paste", "v", Clipboard_Paste, "[(Format)s]"), + + GB_END_DECLARE +}; + +/** Drag frame ***********************************************************/ + +//MyDragFrame::MyDragFrame() : QWidget(0, 0, Qt::WType_TopLevel | Qt::WStyle_Customize | Qt::WStyle_NoBorder | Qt::WStyle_StaysOnTop | Qt::WX11BypassWM) +MyDragFrame::MyDragFrame(QWidget *parent) : + QWidget(parent, Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint) +{ + setAutoFillBackground(true); + QPalette pal(palette()); + pal.setColor(QPalette::Window, QColor(0, 0, 0, 128)); + setPalette(pal); + //setWindowOpacity(0.5); +} + +/*MyDragFrame::paintEvent(QPaintEvent *e) +{ + +}*/ + +static QWidget *_frame[4] = { 0 }; +static bool _frame_visible = false; +static CWIDGET *_frame_control =0; + +static void hide_frame(CWIDGET *control) +{ + int i; + + if (!_frame_visible) + return; + + if (control && control != _frame_control) + return; + + for (i = 0; i < 4; i++) + delete _frame[i]; + + _frame_visible = false; + GB.Unref(POINTER(&_frame_control)); + _frame_control = NULL; +} + +void CDRAG_hide_frame(CWIDGET *control) +{ + hide_frame(control); +} + +static void show_frame(CWIDGET *control, int x, int y, int w, int h) +{ + QWidget *wid; + //QPoint p = wid->mapToGlobal(QPoint(0, 0)); + int i; + + if (GB.Is(control, CLASS_Container)) + wid = QCONTAINER(control); + else + wid = QWIDGET(control); + + if (w <= 0 || h <= 0) + { + x = y = 0; + w = wid->width(); + h = wid->height(); + } + + //x += p.x(); + //y += p.y(); + + if (control != _frame_control) + { + hide_frame(NULL); + _frame_control = control; + GB.Ref(control); + } + + if (!_frame_visible) + { + for (i = 0; i < 4; i++) + _frame[i] = new MyDragFrame(wid); + } + + //x -= 2; + //y -= 2; + //w += 4; + //h += 4; + if (w < 4 || h < 4) + return; + + _frame[0]->setGeometry(x, y, w, 2); + _frame[3]->setGeometry(x, y + h - 2, w, 2); + _frame[1]->setGeometry(x, y + 2, 2, h - 4); + _frame[2]->setGeometry(x + w - 2, y + 2, 2, h - 4); + + for (i = 0; i < 4; i++) + _frame[i]->show(); + + _frame_visible = true; +} + + +/** Drag *****************************************************************/ + +void CDRAG_clear(bool valid) +{ + if (valid) + CDRAG_info.valid++; + else + CDRAG_info.valid--; + + if (CDRAG_info.valid == 0) + CLEAR(&CDRAG_info); +} + +static void post_exit_drag(intptr_t param) +{ + CDRAG_dragging = false; +} + +void *CDRAG_drag(CWIDGET *source, GB_VARIANT_VALUE *data, GB_STRING *fmt) +{ + QDrag *drag; + QMimeData *mimeData; + QString format; + void *dest; + + if (GB.CheckObject(source)) + return NULL; + + if (CDRAG_dragging) + { + GB.Error("Undergoing drag"); + return NULL; + } + + mimeData = new QMimeData(); + + if (data->type == GB_T_STRING) + { + if (fmt == NULL) + format = "text/plain"; + else + { + format = TO_QSTRING(GB.ToZeroString(fmt)); + if (format.left(5) != "text/") + goto _BAD_FORMAT; + if (format.length() == 5) + goto _BAD_FORMAT; + } + + mimeData->setData(format, QByteArray(data->value._string, GB.StringLength(data->value._string))); + } + else if (data->type >= GB_T_OBJECT && GB.Is(data->value._object, CLASS_Image)) + { + QImage img; + + if (fmt) + goto _BAD_FORMAT; + + img = *CIMAGE_get((CIMAGE *)data->value._object); + img.detach(); + + mimeData->setImageData(img); + } + else + goto _BAD_FORMAT; + + source->flag.dragging = true; + + drag = new QDrag(source->widget); + drag->setMimeData(mimeData); + + if (_picture) + { + drag->setPixmap(*(_picture->pixmap)); + if (_picture_x >= 0 && _picture_y >= 0) + drag->setHotSpot(QPoint(_picture_x, _picture_y)); + } + + CDRAG_dragging = true; + + GB.Unref(POINTER(&CDRAG_destination)); + CDRAG_destination = 0; + + //qDebug("start drag"); + drag->exec(); + + source->flag.dragging = false; + //qDebug("end drag"); + + hide_frame(NULL); + GB.Post((GB_CALLBACK)post_exit_drag, 0); + + if (CDRAG_destination) + GB.Unref(POINTER(&CDRAG_destination)); + + dest = CDRAG_destination; + CDRAG_destination = 0; + + return dest; + +_BAD_FORMAT: + + GB.Error("Bad drag format"); + return NULL; +} + + +bool CDRAG_drag_enter(QWidget *w, CWIDGET *control, QDropEvent *e) +{ + bool cancel; + + //qDebug("CDRAG_drag_enter: (%s %p) %p", GB.GetClassName(control), control, qobject_cast(QWIDGET(control))); + + // Hack for QScrollView + /*if (CWIDGET_test_flag(control, WF_SCROLLVIEW) && qobject_cast(QWIDGET(control))) + ((MyListView *)QWIDGET(control))->contentsDragEnterEvent((QDragEnterEvent *)e);*/ + + if (!GB.CanRaise(control, EVENT_Drag)) + { + if (GB.CanRaise(control, EVENT_DragMove) || GB.CanRaise(control, EVENT_Drop)) + e->acceptProposedAction(); + else + { + if (qobject_cast(w) || qobject_cast(w)) + return false; + + e->ignore(); + } + return true; + } + + CDRAG_clear(true); + CDRAG_info.event = e; + + cancel = GB.Raise(control, EVENT_Drag, 0); + + CDRAG_clear(false); + + if (cancel) + e->ignore(); + else + e->acceptProposedAction(); + return cancel; +} + +#define EXT(_ob) ((CWIDGET_EXT *)((CWIDGET *)_ob)->ext) + +void CDRAG_drag_leave(CWIDGET *control) +{ + CDRAG_hide_frame(control); + + //while (EXT(control) && EXT(control)->proxy) + // control = (CWIDGET *)(EXT(control)->proxy); + +__DRAG_LEAVE_TRY_PROXY: + + GB.Raise(control, EVENT_DragLeave, 0); + + if (EXT(control) && EXT(control)->proxy) + { + control = (CWIDGET *)(EXT(control)->proxy); + goto __DRAG_LEAVE_TRY_PROXY; + } +} + + +bool CDRAG_drag_move(QWidget *w, CWIDGET *control, QDropEvent *e) +{ + bool cancel; + QPoint p; + + //qDebug("CDRAG_drag_move: (%s %p) %p", GB.GetClassName(control), control, qobject_cast(QWIDGET(control))); + + // Hack for QScrollView + + /*if (CWIDGET_test_flag(control, WF_SCROLLVIEW) && qobject_cast(QWIDGET(control))) + { + accepted = e->isAccepted(); + ((MyListView *)QWIDGET(control))->contentsDragMoveEvent((QDragMoveEvent *)e); + if (accepted) + e->acceptProposedAction(); + else + e->ignore(); + }*/ + + if (!GB.CanRaise(control, EVENT_DragMove)) + { + /*if (GB.CanRaise(control, EVENT_Drop)) + e->accept(); + else + e->ignore();*/ + return true; + } + + CDRAG_clear(true); + CDRAG_info.event = e; + + p = e->pos(); + p = w->mapTo(QWIDGET(control), p); + CDRAG_info.x = p.x(); + CDRAG_info.y = p.y(); + + cancel = GB.Raise(control, EVENT_DragMove, 0); + if (cancel) + e->ignore(); + else + e->acceptProposedAction(); + + CDRAG_clear(false); + return cancel; +} + +bool CDRAG_drag_drop(QWidget *w, CWIDGET *control, QDropEvent *e) +{ + QPoint p; + + //hide_frame(); + + if (!GB.CanRaise(control, EVENT_Drop)) + return false; + + // Hack for QScrollView + /*if (CWIDGET_test_flag(control, WF_SCROLLVIEW) && qobject_cast(QWIDGET(control))) + ((MyListView *)QWIDGET(control))->contentsDropEvent((QDragMoveEvent *)e);*/ + + CDRAG_clear(true); + CDRAG_info.event = e; + CDRAG_destination = control; + GB.Ref(CDRAG_destination); + + p = e->pos(); + p = w->mapTo(QWIDGET(control), p); + CDRAG_info.x = p.x(); + CDRAG_info.y = p.y(); + + GB.Raise(control, EVENT_Drop, 0); + + if (!CDRAG_dragging) // DnD run outside of the application + { + GB.Unref(&CDRAG_destination); + hide_frame(control); + } + + CDRAG_clear(false); + + return true; +} + +BEGIN_METHOD(Drag_call, GB_OBJECT source; GB_VARIANT data; GB_STRING format) + + GB.ReturnObject(CDRAG_drag((CWIDGET *)VARG(source), &VARG(data), MISSING(format) ? NULL : ARG(format))); + +END_METHOD + +BEGIN_METHOD_VOID(Drag_exit) + + hide_frame(NULL); + GB.Unref(POINTER(&_picture)); + +END_METHOD + +BEGIN_PROPERTY(Drag_Icon) + + if (READ_PROPERTY) + GB.ReturnObject(_picture); + else + GB.StoreObject(PROP(GB_OBJECT), POINTER(&_picture)); + +END_PROPERTY + +BEGIN_PROPERTY(Drag_IconX) + + if (READ_PROPERTY) + GB.ReturnInteger(_picture_x); + else + _picture_x = VPROP(GB_INTEGER); + +END_PROPERTY + +BEGIN_PROPERTY(Drag_IconY) + + if (READ_PROPERTY) + GB.ReturnInteger(_picture_y); + else + _picture_y = VPROP(GB_INTEGER); + +END_PROPERTY + +#define CHECK_VALID() \ + if (!CDRAG_info.valid) \ + { \ + GB.Error("No drag data"); \ + return; \ + } + + +BEGIN_PROPERTY(Drag_Type) + + CHECK_VALID(); + + GB.ReturnInteger(get_type(CDRAG_info.event->mimeData())); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_Format) + + CHECK_VALID(); + + RETURN_NEW_STRING(get_format(CDRAG_info.event->mimeData())); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_Formats) + + GB_ARRAY array; + + CHECK_VALID(); + + GB.Array.New(&array, GB_T_STRING, 0); + get_formats(CDRAG_info.event->mimeData(), array); + GB.ReturnObject(array); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_Data) + + if (!CDRAG_info.valid) + { + GB.ReturnVariant(NULL); + return; + } + + paste(CDRAG_info.event->mimeData(), NULL); + +END_PROPERTY + + +BEGIN_METHOD(CDRAG_paste, GB_STRING format) + + if (!CDRAG_info.valid) + { + GB.ReturnVariant(NULL); + return; + } + + paste(CDRAG_info.event->mimeData(), MISSING(format) ? NULL : GB.ToZeroString(ARG(format))); + +END_METHOD + + +BEGIN_PROPERTY(Drag_Action) + + CHECK_VALID(); + + switch(CDRAG_info.event->dropAction()) + { + case Qt::LinkAction: + GB.ReturnInteger(1); + break; + + case Qt::MoveAction: + GB.ReturnInteger(2); + break; + + default: + GB.ReturnInteger(0); + break; + } + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_Source) + + CHECK_VALID(); + + GB.ReturnObject(CWidget::get(CDRAG_info.event->source())); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_X) + + CHECK_VALID(); + + GB.ReturnInteger(CDRAG_info.x); + +END_PROPERTY + + +BEGIN_PROPERTY(Drag_Y) + + CHECK_VALID(); + + GB.ReturnInteger(CDRAG_info.y); + +END_PROPERTY + +BEGIN_PROPERTY(Drag_Pending) + + GB.ReturnBoolean(CDRAG_dragging); + +END_PROPERTY + +BEGIN_METHOD(Drag_Show, GB_OBJECT control; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + if (GB.CheckObject(VARG(control))) + return; + + /*if (!CDRAG_dragging) + { + GB.Error("No undergoing drag"); + return; + }*/ + + if (MISSING(x) || MISSING(y) || MISSING(w) || MISSING(h)) + show_frame((CWIDGET *)VARG(control), 0, 0, -1, -1); + else + show_frame((CWIDGET *)VARG(control), VARG(x), VARG(y), VARG(w), VARG(h)); + +END_METHOD + +BEGIN_METHOD_VOID(Drag_Hide) + + hide_frame(NULL); + +END_METHOD + + +GB_DESC CDragDesc[] = +{ + GB_DECLARE_STATIC("Drag"), + + GB_CONSTANT("None", "i", MIME_UNKNOWN), + GB_CONSTANT("Text", "i", MIME_TEXT), + GB_CONSTANT("Image", "i", MIME_IMAGE), + + GB_CONSTANT("Copy", "i", 0), + GB_CONSTANT("Link", "i", 1), + GB_CONSTANT("Move", "i", 2), + + GB_STATIC_PROPERTY("Icon", "Picture", Drag_Icon), + GB_STATIC_PROPERTY("IconX", "i", Drag_IconX), + GB_STATIC_PROPERTY("IconY", "i", Drag_IconY), + + GB_STATIC_PROPERTY_READ("Data", "v", Drag_Data), + GB_STATIC_PROPERTY_READ("Format", "s", Drag_Format), + GB_STATIC_PROPERTY_READ("Formats", "String[]", Drag_Formats), + GB_STATIC_PROPERTY_READ("Type", "i", Drag_Type), + GB_STATIC_PROPERTY_READ("Action", "i", Drag_Action), + GB_STATIC_PROPERTY_READ("Source", "Control", Drag_Source), + GB_STATIC_PROPERTY_READ("X", "i", Drag_X), + GB_STATIC_PROPERTY_READ("Y", "i", Drag_Y), + GB_STATIC_PROPERTY_READ("Pending", "b", Drag_Pending), + + GB_STATIC_METHOD("_call", "Control", Drag_call, "(Source)Control;(Data)v[(Format)s]"), + GB_STATIC_METHOD("_exit", NULL, Drag_exit, NULL), + GB_STATIC_METHOD("Show", NULL, Drag_Show, "(Control)Control;[(X)i(Y)i(Width)i(Height)i]"), + GB_STATIC_METHOD("Hide", NULL, Drag_Hide, NULL), + GB_STATIC_METHOD("Paste", "v", CDRAG_paste, "[(Format)s]"), + + GB_END_DECLARE +}; + + + diff --git a/gb.qt4/src/CClipboard.h b/gb.qt4/src/CClipboard.h new file mode 100644 index 00000000..01b8048f --- /dev/null +++ b/gb.qt4/src/CClipboard.h @@ -0,0 +1,72 @@ +/*************************************************************************** + + CClipboard.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCLIPBOARD_H +#define __CCLIPBOARD_H + +#include "gambas.h" +#include "CWidget.h" +#include "CPicture.h" +#include +#include + +typedef + struct { + const QDropEvent *event; + int x; + int y; + unsigned valid : 1; + } + CDRAG_INFO; + +#ifndef __CCLIPBOARD_CPP +extern GB_DESC CClipboardDesc[]; +extern GB_DESC CDragDesc[]; +extern CDRAG_INFO CDRAG_info; +extern bool CDRAG_dragging; +#endif + +class MyDragFrame: public QWidget +{ + Q_OBJECT + +public: + + MyDragFrame(QWidget *); + +protected: + + //virtual paintEvent(QPaintEvent *e); +}; + +void CDRAG_clear(bool valid); +void *CDRAG_drag(CWIDGET *source, GB_VARIANT_VALUE *data, GB_STRING *fmt); +bool CDRAG_drag_enter(QWidget *w, CWIDGET *control, QDropEvent *e); +void CDRAG_drag_leave(CWIDGET *control); +bool CDRAG_drag_move(QWidget *w, CWIDGET *control, QDropEvent *e); +bool CDRAG_drag_drop(QWidget *w, CWIDGET *control, QDropEvent *e); +void CDRAG_hide_frame(CWIDGET *control); + +void CLIPBOARD_has_changed(QClipboard::Mode mode); + +#endif diff --git a/gb.qt4/src/CColor.cpp b/gb.qt4/src/CColor.cpp new file mode 100644 index 00000000..05c0ca81 --- /dev/null +++ b/gb.qt4/src/CColor.cpp @@ -0,0 +1,181 @@ +/*************************************************************************** + + CColor.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCOLOR_CPP + +#include +#include +#include + +#include "gambas.h" + +#include "CWidget.h" +#include "CColor.h" + +static uint get_light_foreground() +{ + return IMAGE.MergeColor(qApp->palette().color(QPalette::Window).rgb() & 0xFFFFFF, qApp->palette().color(QPalette::WindowText).rgb() & 0xFFFFFF, 0.3); +} + +QColor CCOLOR_light_foreground() +{ + return TO_QCOLOR(get_light_foreground()); +} + +QColor CCOLOR_make(GB_COLOR color) +{ + int b = color & 0xFF; + int g = (color >> 8) & 0xFF; + int r = (color >> 16) & 0xFF; + int a = (color >> 24) ^ 0xFF; + + return QColor(r, g, b, a); +} + +static void return_color(QPalette::ColorRole role) +{ + GB.ReturnInteger(QApplication::palette().color(role).rgb() & 0xFFFFFF); +} + +BEGIN_PROPERTY(Color_Background) + + return_color(QPalette::Window); + +END_PROPERTY + +BEGIN_PROPERTY(Color_Foreground) + + return_color(QPalette::WindowText); + +END_PROPERTY + +BEGIN_PROPERTY(Color_TextBackground) + + return_color(QPalette::Base); + +END_PROPERTY + +BEGIN_PROPERTY(Color_TextForeground) + + return_color(QPalette::Text); + +END_PROPERTY + +BEGIN_PROPERTY(Color_SelectedBackground) + + return_color(QPalette::Highlight); + +END_PROPERTY + +BEGIN_PROPERTY(Color_SelectedForeground) + + return_color(QPalette::HighlightedText); + +END_PROPERTY + +BEGIN_PROPERTY(Color_ButtonBackground) + + return_color(QPalette::Button); + +END_PROPERTY + +BEGIN_PROPERTY(Color_ButtonForeground) + + return_color(QPalette::ButtonText); + +END_PROPERTY + +BEGIN_PROPERTY(Color_LightBackground) + + uint col = IMAGE.MergeColor(qApp->palette().color(QPalette::Base).rgb() & 0xFFFFFF, qApp->palette().color(QPalette::Highlight).rgb() & 0xFFFFFF, 0.5); + GB.ReturnInteger(col); + +END_PROPERTY + +BEGIN_PROPERTY(Color_LightForeground) + + GB.ReturnInteger(get_light_foreground()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_TooltipBackground) + + return_color(QPalette::ToolTipBase); + +END_PROPERTY + +static int get_luminance(QColor col) +{ + return (int)(0.299 * col.red() + 0.587 * col.green() + 0.114 * col.blue()); +} + +BEGIN_PROPERTY(Color_TooltipForeground) + + QColor bg = qApp->palette().color(QPalette::ToolTipBase); + QColor fg = qApp->palette().color(QPalette::ToolTipText); + int lbg = get_luminance(bg); + int lfg = get_luminance(fg); + + if (abs(lbg - lfg) <= 64) + fg.setHsv(fg.hue(), fg.saturation(), 255 - fg.value()); + + GB.ReturnInteger(fg.rgb() & 0xFFFFFF); + +END_PROPERTY + +BEGIN_PROPERTY(Color_LinkForeground) + + return_color(QPalette::Link); + +END_PROPERTY + +BEGIN_PROPERTY(Color_VisitedForeground) + + return_color(QPalette::LinkVisited); + +END_PROPERTY + +GB_DESC CColorDesc[] = +{ + GB_DECLARE_STATIC("Color"), + + GB_STATIC_PROPERTY_READ("Background", "i", Color_Background), + GB_STATIC_PROPERTY_READ("SelectedBackground", "i", Color_SelectedBackground), + GB_STATIC_PROPERTY_READ("LightBackground", "i", Color_LightBackground), + GB_STATIC_PROPERTY_READ("TextBackground", "i", Color_TextBackground), + GB_STATIC_PROPERTY_READ("ButtonBackground", "i", Color_ButtonBackground), + GB_STATIC_PROPERTY_READ("TooltipBackground", "i", Color_TooltipBackground), + + GB_STATIC_PROPERTY_READ("Foreground", "i", Color_Foreground), + GB_STATIC_PROPERTY_READ("LightForeground", "i", Color_LightForeground), + GB_STATIC_PROPERTY_READ("SelectedForeground", "i", Color_SelectedForeground), + GB_STATIC_PROPERTY_READ("TextForeground", "i", Color_TextForeground), + GB_STATIC_PROPERTY_READ("ButtonForeground", "i", Color_ButtonForeground), + GB_STATIC_PROPERTY_READ("TooltipForeground", "i", Color_TooltipForeground), + GB_STATIC_PROPERTY_READ("LinkForeground", "i", Color_LinkForeground), + GB_STATIC_PROPERTY_READ("VisitedForeground", "i", Color_VisitedForeground), + + GB_END_DECLARE +}; + + diff --git a/gb.qt4/src/CColor.h b/gb.qt4/src/CColor.h new file mode 100644 index 00000000..c47bf0d7 --- /dev/null +++ b/gb.qt4/src/CColor.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + CColor.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCOLOR_H +#define __CCOLOR_H + +#include "gambas.h" +#include "gb.image.h" + +#ifndef __CCOLOR_CPP +extern GB_DESC CColorDesc[]; +#endif + +QColor CCOLOR_make(GB_COLOR color); +QColor CCOLOR_light_foreground(); + +#endif diff --git a/gb.qt4/src/CConst.cpp b/gb.qt4/src/CConst.cpp new file mode 100644 index 00000000..5720ccd2 --- /dev/null +++ b/gb.qt4/src/CConst.cpp @@ -0,0 +1,251 @@ +/*************************************************************************** + + CConst.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCONST_CPP + + +#include "gambas.h" + +#include + +#include +#include + +#include "main.h" +#include "CContainer.h" +#include "CConst.h" + +static int _alignment[] = +{ + ALIGN_NORMAL, Qt::AlignVCenter + Qt::AlignLeft, + ALIGN_LEFT, Qt::AlignVCenter + Qt::AlignLeft + Qt::AlignAbsolute, + ALIGN_RIGHT, Qt::AlignVCenter + Qt::AlignRight + Qt::AlignAbsolute, + ALIGN_CENTER, Qt::AlignVCenter + Qt::AlignHCenter, + ALIGN_TOP_NORMAL, Qt::AlignTop + Qt::AlignLeft, + ALIGN_TOP_LEFT, Qt::AlignTop + Qt::AlignLeft + Qt::AlignAbsolute, + ALIGN_TOP_RIGHT, Qt::AlignTop + Qt::AlignRight + Qt::AlignAbsolute, + ALIGN_TOP, Qt::AlignTop + Qt::AlignHCenter, + ALIGN_BOTTOM_NORMAL, Qt::AlignBottom + Qt::AlignLeft, + ALIGN_BOTTOM_LEFT, Qt::AlignBottom + Qt::AlignLeft + Qt::AlignAbsolute, + ALIGN_BOTTOM_RIGHT, Qt::AlignBottom + Qt::AlignRight + Qt::AlignAbsolute, + ALIGN_BOTTOM, Qt::AlignBottom + Qt::AlignHCenter, + ALIGN_JUSTIFY, Qt::AlignVCenter + Qt::AlignJustify, + CONST_MAGIC +}; + +static int _horizontal_alignment[] = +{ + ALIGN_NORMAL, Qt::AlignLeft, + ALIGN_LEFT, Qt::AlignLeft + Qt::AlignAbsolute, + ALIGN_RIGHT, Qt::AlignRight + Qt::AlignAbsolute, + ALIGN_CENTER, Qt::AlignHCenter, + ALIGN_JUSTIFY, Qt::AlignJustify, + CONST_MAGIC +}; + +static int _line_style[] = +{ + LINE_NONE, Qt::NoPen, + LINE_SOLID, Qt::SolidLine, + LINE_DASH, Qt::DashLine, + LINE_DOT, Qt::DotLine, + LINE_DASH_DOT, Qt::DashDotLine, + LINE_DASH_DOT_DOT, Qt::DashDotDotLine, + CONST_MAGIC +}; + +static int _fill_style[] = +{ + FILL_NONE, Qt::NoBrush, + FILL_SOLID, Qt::SolidPattern, + FILL_DENSE_94, Qt::Dense1Pattern, + FILL_DENSE_88, Qt::Dense2Pattern, + FILL_DENSE_63, Qt::Dense3Pattern, + FILL_DENSE_50, Qt::Dense4Pattern, + FILL_DENSE_37, Qt::Dense5Pattern, + FILL_DENSE_12, Qt::Dense6Pattern, + FILL_DENSE_06, Qt::Dense7Pattern, + FILL_HORIZONTAL, Qt::HorPattern, + FILL_VERTICAL, Qt::VerPattern, + FILL_CROSS, Qt::CrossPattern, + FILL_DIAGONAL, Qt::BDiagPattern, + FILL_BACK_DIAGONAL, Qt::FDiagPattern, + FILL_CROSS_DIAGONAL, Qt::DiagCrossPattern, + CONST_MAGIC +}; + +int CCONST_convert(int *tab, int value, int def, bool to_qt) +{ + int *p = tab; + int ret; + + if (to_qt) + { + ret = p[1]; + + for(;;) + { + if (*p == CONST_MAGIC) + return ret; + else if (*p == def) + ret = p[1]; + else if (*p == value) + return p[1]; + p += 2; + } + } + else + { + for(;;) + { + if (*p == CONST_MAGIC) + return def; + else if (p[1] == value) + return p[0]; + p += 2; + } + } +} + +int CCONST_alignment(int value, int def, bool to_qt) +{ + return CCONST_convert(_alignment, value, def, to_qt); +} + +int CCONST_horizontal_alignment(int value, int def, bool to_qt) +{ + return CCONST_convert(_horizontal_alignment, value, def, to_qt); +} + +int CCONST_line_style(int value, int def, bool to_qt) +{ + return CCONST_convert(_line_style, value, def, to_qt); +} + +int CCONST_fill_style(int value, int def, bool to_qt) +{ + return CCONST_convert(_fill_style, value, def, to_qt); +} + +#define IMPLEMENT_ALIGN(_method, _code) \ +BEGIN_METHOD(_method, GB_INTEGER align) \ + int a = VARG(align); \ + GB.ReturnBoolean(_code); \ +END_METHOD + +IMPLEMENT_ALIGN(Align_IsTop, ALIGN_IS_TOP(a)) +IMPLEMENT_ALIGN(Align_IsBottom, ALIGN_IS_BOTTOM(a)) +IMPLEMENT_ALIGN(Align_IsMiddle, ALIGN_IS_MIDDLE(a)) +IMPLEMENT_ALIGN(Align_IsLeft, ALIGN_IS_LEFT(a)) +IMPLEMENT_ALIGN(Align_IsRight, ALIGN_IS_RIGHT(a)) +IMPLEMENT_ALIGN(Align_IsCenter, ALIGN_IS_CENTER(a)) + +GB_DESC CAlignDesc[] = +{ + GB_DECLARE("Align", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("Normal", "i", ALIGN_NORMAL), + GB_CONSTANT("Left", "i", ALIGN_LEFT), + GB_CONSTANT("Right", "i", ALIGN_RIGHT), + GB_CONSTANT("Center", "i", ALIGN_CENTER), + + GB_CONSTANT("TopNormal", "i", ALIGN_TOP_NORMAL), + GB_CONSTANT("TopLeft", "i", ALIGN_TOP_LEFT), + GB_CONSTANT("TopRight", "i", ALIGN_TOP_RIGHT), + GB_CONSTANT("Top", "i", ALIGN_TOP), + + GB_CONSTANT("BottomNormal", "i", ALIGN_BOTTOM_NORMAL), + GB_CONSTANT("BottomLeft", "i", ALIGN_BOTTOM_LEFT), + GB_CONSTANT("BottomRight", "i", ALIGN_BOTTOM_RIGHT), + GB_CONSTANT("Bottom", "i", ALIGN_BOTTOM), + + GB_CONSTANT("Justify", "i", ALIGN_JUSTIFY), + + GB_STATIC_METHOD("IsTop", "b", Align_IsTop, "(Alignment)i"), + GB_STATIC_METHOD("IsBottom", "b", Align_IsBottom, "(Alignment)i"), + GB_STATIC_METHOD("IsMiddle", "b", Align_IsMiddle, "(Alignment)i"), + GB_STATIC_METHOD("IsLeft", "b", Align_IsLeft, "(Alignment)i"), + GB_STATIC_METHOD("IsCenter", "b", Align_IsCenter, "(Alignment)i"), + GB_STATIC_METHOD("IsRight", "b", Align_IsRight, "(Alignment)i"), + + GB_END_DECLARE +}; + + +GB_DESC CArrangeDesc[] = +{ + GB_DECLARE("Arrange", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", ARRANGE_NONE), + GB_CONSTANT("Horizontal", "i", ARRANGE_HORIZONTAL), + GB_CONSTANT("Vertical", "i", ARRANGE_VERTICAL), + GB_CONSTANT("LeftRight", "i", ARRANGE_ROW), + GB_CONSTANT("TopBottom", "i", ARRANGE_COLUMN), + GB_CONSTANT("Row", "i", ARRANGE_ROW), + GB_CONSTANT("Column", "i", ARRANGE_COLUMN), + GB_CONSTANT("Fill", "i", ARRANGE_FILL), + + GB_END_DECLARE +}; + + +GB_DESC CBorderDesc[] = +{ + GB_DECLARE("Border", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", BORDER_NONE), + GB_CONSTANT("Plain", "i", BORDER_PLAIN), + GB_CONSTANT("Sunken", "i", BORDER_SUNKEN), + GB_CONSTANT("Raised", "i", BORDER_RAISED), + GB_CONSTANT("Etched", "i", BORDER_ETCHED), + + GB_END_DECLARE +}; + + +GB_DESC CScrollDesc[] = +{ + GB_DECLARE("Scroll", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", SCROLL_NONE), + GB_CONSTANT("Horizontal", "i", SCROLL_HORIZONTAL), + GB_CONSTANT("Vertical", "i", SCROLL_VERTICAL), + GB_CONSTANT("Both", "i", SCROLL_BOTH), + + GB_END_DECLARE +}; + + +GB_DESC CSelectDesc[] = +{ + GB_DECLARE("Select", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", SELECT_NONE), + GB_CONSTANT("Single", "i", SELECT_SINGLE), + GB_CONSTANT("Multiple", "i", SELECT_MULTIPLE), + + GB_END_DECLARE +}; + + + diff --git a/gb.qt4/src/CConst.h b/gb.qt4/src/CConst.h new file mode 100644 index 00000000..c02e224f --- /dev/null +++ b/gb.qt4/src/CConst.h @@ -0,0 +1,46 @@ +/*************************************************************************** + + CConst.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCONST_H +#define __CCONST_H + +#include "gambas.h" +#include "gb.form.const.h" + +#ifndef __CCONST_CPP +extern GB_DESC CAlignDesc[]; +extern GB_DESC CArrangeDesc[]; +extern GB_DESC CBorderDesc[]; +extern GB_DESC CScrollDesc[]; +extern GB_DESC CLineDesc[]; +extern GB_DESC CFillDesc[]; +extern GB_DESC CSelectDesc[]; +#endif + +int CCONST_convert(int *tab, int value, int def, bool to_qt); +int CCONST_alignment(int value, int def, bool to_qt); +int CCONST_horizontal_alignment(int value, int def, bool to_qt); +int CCONST_line_style(int value, int def, bool to_qt); +int CCONST_fill_style(int value, int def, bool to_qt); + +#endif diff --git a/gb.qt4/src/CContainer.cpp b/gb.qt4/src/CContainer.cpp new file mode 100755 index 00000000..7f23c1ea --- /dev/null +++ b/gb.qt4/src/CContainer.cpp @@ -0,0 +1,1483 @@ +/*************************************************************************** + + CContainer.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCONTAINER_CPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gambas.h" +#include "gb_common.h" + +#include "CWidget.h" +#include "CWindow.h" +#include "CConst.h" +#include "CTabStrip.h" +#include "CColor.h" + +#include "CContainer.h" + +#if QT5 +#include +#define QStyleOptionFrameV3 QStyleOptionFrame +#else +#include +#endif + + +//#define DEBUG_ME +//#define USE_CACHE 1 + +DECLARE_EVENT(EVENT_Insert); +//DECLARE_EVENT(EVENT_Remove); +DECLARE_EVENT(EVENT_BeforeArrange); +DECLARE_EVENT(EVENT_Arrange); + +#if 0 +static int _count_move, _count_resize, _count_set_geom; + +static void move_widget(QWidget *wid, int x, int y) +{ + if (wid->x() != x || wid->y() != y) + { + #if DEBUG_CONTAINER + _count_move++; + #endif + wid->move(x, y); + } +} + +static void resize_widget(QWidget *wid, int w, int h) +{ + if (wid->width() != w || wid->height() != h) + { + #if DEBUG_CONTAINER + _count_resize++; + #endif + wid->resize(w, h); + } +} + +static void move_resize_widget(QWidget *wid, int x, int y, int w, int h) +{ + if (wid->x() != x || wid->y() != y || wid->width() != w || wid->height() != h) + { + #if DEBUG_CONTAINER + _count_set_geom++; + #endif + wid->setGeometry(x, y, w, h); + } +} +#endif + +#if USE_CACHE +static QHash _cache; +static int _cache_level = 0; +#endif + +static QWidget *get_next_widget(QObjectList &list, int &index) +{ + QObject *ob; + + for(;;) + { + if (index >= list.count()) + return NULL; + + ob = list.at(index); // ob might be null if we are inside the QWidget destructor + index++; + + if (ob && ob->isWidgetType()) + { + QWidget *w = (QWidget *)ob; + if (!w->isHidden() && !qobject_cast(w)) + return w; + } + } +} + +#if USE_CACHE + +static QRect *ensure_widget_geometry(QWidget *w) +{ + if (_cache.contains(w)) + return _cache.value(w); + else + { + QRect *r = new QRect(w->geometry()); + _cache.insert(w, r); + return r; + } +} + +static QRect *get_widget_geometry(QWidget *w) +{ + static QRect wg; + + if (_cache.contains(w)) + return _cache.value(w); + else + { + wg = w->geometry(); + return &wg; + } +} + +static void move_widget(void *_object, int x, int y) +{ + QRect *r = ensure_widget_geometry(WIDGET); + if (x == r->x() && y == r->y()) + return; + r->setX(x); + r->setY(y); + CWIDGET_move_cached(THIS, x, y); +} + +static void resize_widget(void *_object, int w, int h) +{ + QRect *r = ensure_widget_geometry(WIDGET); + if (w == r->width() && h == r->height()) + return; + r->setWidth(w); + r->setHeight(h); + CWIDGET_resize_cached(THIS, w, h); +} + +static void move_resize_widget(void *_object, int x, int y, int w, int h) +{ + QRect *r = ensure_widget_geometry(WIDGET); + if (x == r->x() && y == r->y() && w == r->width() && h == r->height()) + return; + r->setRect(x, y, w, h); + CWIDGET_move_resize_cached(THIS, x, y, w, h); +} + +static void get_widget_contents(QWidget *wid, int &x, int &y, int &w, int &h) +{ + QRect wc = wid->contentsRect(); + QRect wg = wid->geometry(); + QRect *g = get_widget_geometry(wid); + + x = wc.x(); + y = wc.y(); + w = wc.width() + g->width() - wg.width(); + h = wc.height() + g->height() - wg.height(); +} + +static void flush_cache() +{ + QHash hash; + QWidget *w; + QRect *r; + + hash = _cache; + _cache.clear(); + + QHashIterator it(_cache); + while (it.hasNext()) + { + it.next(); + w = it.key(); + r = it.value(); + w->setGeometry(*r); + delete r; + //CWIDGET_move_resize(CWidget::getReal(w), r->x(), r->y(), r->width(), r->height()); + } + + _cache.clear(); +} + +#else +#if 0 + +static void get_widget_contents(QWidget *wid, int &x, int &y, int &w, int &h) +{ + QRect wc; + + wc = wid->contentsRect(); + + x = wc.x(); + y = wc.y(); + w = wc.width(); + h = wc.height(); + +} +#endif +#endif + +static void resize_container(void *_object, QWidget *cont, int w, int h) +{ + QWidget *wid = ((CWIDGET *)_object)->widget; + #if USE_CACHE + resize_widget(_object, w + wid->width() - cont->width(), h + wid->height() - cont->height()); + #else + CWIDGET_resize(_object, w + wid->width() - cont->width(), h + wid->height() - cont->height()); + #endif +} + + +#define WIDGET_TYPE QWidget * +#define CONTAINER_TYPE QWidget * +#define ARRANGEMENT_TYPE CCONTAINER_ARRANGEMENT * + +#define IS_RIGHT_TO_LEFT() qApp->isRightToLeft() + +#define GET_WIDGET(_object) ((CWIDGET *)_object)->widget +#define GET_CONTAINER(_object) ((CCONTAINER *)_object)->container +#define GET_ARRANGEMENT(_object) ((CCONTAINER_ARRANGEMENT *)_object) +#define IS_EXPAND(_object) (((CWIDGET *)_object)->flag.expand) +#define IS_IGNORE(_object) (((CWIDGET *)_object)->flag.ignore) +#define IS_DESIGN(_object) (CWIDGET_test_flag(_object, WF_DESIGN) && CWIDGET_test_flag(_object, WF_DESIGN_LEADER)) +//#define IS_WIDGET_VISIBLE(_widget) (_widget)->isVisible() + +//#define CAN_ARRANGE(_object) ((_object) && !CWIDGET_test_flag(_object, WF_DELETED) && (!GB.Is(_object, CLASS_Window) || (((CWINDOW *)_object)->opened))) +#define CAN_ARRANGE(_object) ((_object) && ((CWIDGET *)(_object))->flag.shown && !CWIDGET_test_flag(_object, WF_DELETED)) + +#if USE_CACHE + +#define GET_WIDGET_CONTENTS(_widget, _x, _y, _w, _h) get_widget_contents(_widget, _x, _y, _w, _h) +#define GET_WIDGET_X(_widget) get_widget_geometry(_widget)->x() +#define GET_WIDGET_Y(_widget) get_widget_geometry(_widget)->y() +#define GET_WIDGET_W(_widget) get_widget_geometry(_widget)->width() +#define GET_WIDGET_H(_widget) get_widget_geometry(_widget)->height() +#define MOVE_WIDGET(_object, _widget, _x, _y) move_widget((_object), (_x), (_y)) +#define RESIZE_WIDGET(_object, _widget, _w, _h) resize_widget((_object), (_w), (_h)) +#define MOVE_RESIZE_WIDGET(_object, _widget, _x, _y, _w, _h) move_resize_widget((_object), (_x), (_y), (_w), (_h)) +#define RESIZE_CONTAINER(_object, _cont, _w, _h) resize_container((_object), (_cont), (_w), (_h)) + +#else + +//#define GET_WIDGET_CONTENTS(_widget, _x, _y, _w, _h) get_widget_contents(_widget, _x, _y, _w, _h) +#define GET_WIDGET_CONTENTS(_widget, _x, _y, _w, _h) \ + _x = (_widget)->contentsRect().x(); \ + _y = (_widget)->contentsRect().y(); \ + _w = (_widget)->contentsRect().width(); \ + _h = (_widget)->contentsRect().height(); +#define GET_WIDGET_X(_widget) (_widget)->x() +#define GET_WIDGET_Y(_widget) (_widget)->y() +#define GET_WIDGET_W(_widget) (_widget)->width() +#define GET_WIDGET_H(_widget) (_widget)->height() +#define MOVE_WIDGET(_object, _widget, _x, _y) CWIDGET_move((_object), (_x), (_y)) +#define RESIZE_WIDGET(_object, _widget, _w, _h) CWIDGET_resize((_object), (_w), (_h)) +#define MOVE_RESIZE_WIDGET(_object, _widget, _x, _y, _w, _h) CWIDGET_move_resize((_object), (_x), (_y), (_w), (_h)) +#define RESIZE_CONTAINER(_object, _cont, _w, _h) resize_container((_object), (_cont), (_w), (_h)) + +#endif + +#define INIT_CHECK_CHILDREN_LIST(_widget) \ + QObjectList list = (_widget)->children(); \ + int list_index = 0; + +#define HAS_CHILDREN() (list.count() != 0) + +#define RESET_CHILDREN_LIST() list_index = 0 +#define GET_NEXT_CHILD_WIDGET() get_next_widget(list, list_index) + +#define GET_OBJECT_FROM_WIDGET(_widget) CWidget::getRealExisting(_widget) + +#define GET_OBJECT_NAME(_object) (((CWIDGET *)_object)->name) + +#define RAISE_ARRANGE_EVENT(_object) \ +{ \ + GB.Raise(_object, EVENT_Arrange, 0); \ +} + +#define RAISE_BEFORE_ARRANGE_EVENT(_object) \ +{ \ + GB.Raise(_object, EVENT_BeforeArrange, 0); \ +} + +#define DESKTOP_SCALE MAIN_scale + +#define FUNCTION_NAME CCONTAINER_arrange_real + +#include "gb.form.arrangement.h" + +void CCONTAINER_arrange(void *_object) +{ + #if DEBUG_CONTAINER + static int level = 0; + + if (!level) + _count_move = _count_resize = _count_set_geom = 0; + level++; + #endif + + #if USE_CACHE + qDebug("CCONTAINER_arrange: %s %p", GB.GetClassName(THIS), THIS->widget.widget); + _cache_level++; + #endif + + if (GB.Is(THIS, CLASS_TabStrip)) + CTABSTRIP_arrange(THIS); + + CCONTAINER_arrange_real(_object); + + #if USE_CACHE + _cache_level--; + + if (!_cache_level) + { + flush_cache(); + } + #endif + + //QWidget *cont = GET_CONTAINER(_object); + //if (cont->isA("MyContents")) + // ((MyContents *)cont)->afterArrange(); + + #if DEBUG_CONTAINER + level--; + if (!level) + { + if (_count_move || _count_resize || _count_set_geom) + qDebug("CCONTAINER_arrange: (%s %s): move = %d resize = %d setGeometry = %d", GB.GetClassName(THIS), THIS->widget.name, _count_move, _count_resize, _count_set_geom); + } + #endif +} + +static int max_w, max_h; +static int _gms_x, _gms_y, _gms_w, _gms_h; + +static void gms_move_widget(QWidget *wid, int x, int y) +{ + int w = x + wid->width(); + int h = y + wid->height(); + + if (w > max_w) max_w = w; + if (h > max_h) max_h = h; +} + +static void gms_move_resize_widget(QWidget *wid, int x, int y, int w, int h) +{ + w += x; + h += y; + + if (w > max_w) max_w = w; + if (h > max_h) max_h = h; +} + +#undef MOVE_WIDGET +#define MOVE_WIDGET(_object, _widget, _x, _y) gms_move_widget(_widget, _x, _y) +#undef RESIZE_WIDGET +#define RESIZE_WIDGET(_object, _widget, _w, _h) (0) +#undef MOVE_RESIZE_WIDGET +#define MOVE_RESIZE_WIDGET(_object, _widget, _x, _y, _w, _h) gms_move_resize_widget(_widget, _x, _y, _w, _h) +#undef RAISE_BEFORE_ARRANGE_EVENT +#define RAISE_BEFORE_ARRANGE_EVENT(_object) (0) +#undef RAISE_ARRANGE_EVENT +#define RAISE_ARRANGE_EVENT(_object) (0) +#undef FUNCTION_NAME +#define FUNCTION_NAME get_max_size +#undef RESIZE_CONTAINER +#define RESIZE_CONTAINER(_object, _cont, _w, _h) (0) +#undef GET_WIDGET_CONTENTS +#define GET_WIDGET_CONTENTS(_widget, _x, _y, _w, _h) \ + _x = _gms_x; \ + _y = _gms_y; \ + _w = _gms_w; \ + _h = _gms_h; + +//#undef IS_WIDGET_VISIBLE +//#define IS_WIDGET_VISIBLE(_cont) (1) +#define GET_MAX_SIZE + +#include "gb.form.arrangement.h" + +void CCONTAINER_get_max_size(void *_object, int xc, int yc, int wc, int hc, int *w, int *h) +{ + int add; + CCONTAINER_ARRANGEMENT *arr = THIS_ARRANGEMENT; + bool locked; + + locked = arr->locked; + arr->locked = false; + + max_w = 0; + max_h = 0; + _gms_x = xc; + _gms_y = yc; + _gms_w = wc; + _gms_h = hc; + get_max_size(THIS); + + if (arr->margin) + add = arr->padding ? arr->padding : MAIN_scale; + else if (!arr->spacing) + add = arr->padding; + else + add = 0; + + *w = max_w + add; + *h = max_h + add; + + arr->locked = locked; +} + + +#define arrange_later arrange_now +#define arrange_now(_widget) CCONTAINER_arrange(CWidget::get(_widget)) + +#if 0 +static void post_arrange_later(void *_object) +{ + if (WIDGET && THIS_ARRANGEMENT->dirty) + CCONTAINER_arrange(THIS); + + GB.Unref(&_object); +} + +static void arrange_later(QWidget *cont) +{ + void *_object = CWidget::get(cont); + + if (THIS_ARRANGEMENT->dirty || THIS_ARRANGEMENT->mode == ARRANGE_NONE) + return; + + GB.Ref(_object); + //qDebug("later: %p: dirty = TRUE", THIS); + THIS_ARRANGEMENT->dirty = TRUE; + GB.Post((void (*)())post_arrange_later, (intptr_t)THIS); +} +#endif + +void CCONTAINER_insert_child(void *_object) +{ + CWIDGET *parent = CWidget::get(WIDGET->parentWidget()); + if (parent) + GB.Raise(parent, EVENT_Insert, 1, GB_T_OBJECT, THIS); +} + +void CCONTAINER_decide(CWIDGET *control, bool *width, bool *height) +{ + void *_object = CWIDGET_get_parent(control); + + *width = *height = FALSE; + + if (!THIS || control->flag.ignore || THIS_ARRANGEMENT->autoresize) + return; + + if ((THIS_ARRANGEMENT->mode == ARRANGE_VERTICAL) + || (THIS_ARRANGEMENT->mode == ARRANGE_HORIZONTAL && control->flag.expand) + || (THIS_ARRANGEMENT->mode == ARRANGE_ROW && control->flag.expand)) + *width = TRUE; + + if ((THIS_ARRANGEMENT->mode == ARRANGE_HORIZONTAL) + || (THIS_ARRANGEMENT->mode == ARRANGE_VERTICAL && control->flag.expand) + || (THIS_ARRANGEMENT->mode == ARRANGE_COLUMN && control->flag.expand)) + *height = TRUE; +} + + +/*************************************************************************** + + class MyFrame + +***************************************************************************/ + +MyFrame::MyFrame(QWidget *parent) +: QWidget(parent),_frame(0),_pixmap(0),_bg(false) +{ +} + +void MyFrame::setStaticContents(bool on) +{ + void *_object = CWidget::get(this); + setAttribute(Qt::WA_StaticContents, on && _frame == BORDER_NONE && (_pixmap || THIS->widget.flag.fillBackground)); +} + +void MyFrame::setFrameStyle(int frame) +{ + int margin; + + _frame = frame; + + setStaticContents(true); + + margin = frameWidth(); + setContentsMargins(margin, margin, margin, margin); + + update(); +} + +static void _draw_border(QPainter *p, int frame, QWidget *w, QStyleOptionFrame &opt) +{ + QStyle *style; + QStyleOptionFrameV3 optv3; + bool a; + QBrush save_brush; + //QRect rect = opt.rect; + + if (frame == BORDER_NONE) + return; + + if (w) + style = w->style(); + else + style = QApplication::style(); + + switch (frame) + { + case BORDER_PLAIN: + //qDrawPlainRect(p, opt.rect, CCOLOR_light_foreground()); + a = p->testRenderHint(QPainter::Antialiasing); + if (a) + p->setRenderHint(QPainter::Antialiasing, false); + p->setPen(CCOLOR_light_foreground()); + p->setBrush(Qt::NoBrush); + opt.rect = QRect(opt.rect.x(), opt.rect.y(), opt.rect.width() - 1, opt.rect.height() - 1); + p->drawRect(opt.rect); + if (a) + p->setRenderHint(QPainter::Antialiasing, true); + break; + + case BORDER_SUNKEN: + optv3.rect = opt.rect; + optv3.state = opt.state | QStyle::State_Sunken; + optv3.frameShape = QFrame::StyledPanel; + + save_brush = p->brush(); + p->setBrush(QBrush()); + style->drawPrimitive(QStyle::PE_Frame, &optv3, p, w); + p->setBrush(save_brush); + //style->drawControl(QStyle::CE_ShapedFrame, &optv3, p, w); + break; + + case BORDER_RAISED: + optv3.rect = opt.rect; + optv3.state = opt.state | QStyle::State_Raised; + optv3.frameShape = QFrame::StyledPanel; + style->drawPrimitive(QStyle::PE_Frame, &optv3, p, w); + /*opt.lineWidth = 2; + opt.midLineWidth = 2; + opt.state |= QStyle::State_Raised; + style->drawPrimitive(QStyle::PE_Frame, &opt, p, w);*/ + break; + + case BORDER_ETCHED: + optv3.rect = opt.rect; + //optv3.state = opt.state | QStyle::State_Raised; + optv3.frameShape = QFrame::StyledPanel; + style->drawPrimitive(QStyle::PE_FrameGroupBox, &optv3, p, w); + //qDrawShadeRect(p, opt.rect, opt.palette, true, 1, 0); + break; + + default: + return; + } +} + +void CCONTAINER_draw_border(QPainter *p, char frame, QWidget *wid) +{ + QStyleOptionFrame opt; + opt.init(wid); + opt.rect = QRect(0, 0, wid->width(), wid->height()); + _draw_border(p, frame, wid, opt); +} + +void CCONTAINER_draw_border_without_widget(QPainter *p, char frame, QStyleOptionFrame &opt) +{ + _draw_border(p, frame, NULL, opt); +} + + +int CCONTAINER_get_border_width(char border) +{ + switch (border) + { + case BORDER_PLAIN: + return 1; + break; + + case BORDER_SUNKEN: + case BORDER_RAISED: + case BORDER_ETCHED: + + return qApp->style()->pixelMetric(QStyle::QStyle::PM_ComboBoxFrameWidth); + break; + + default: + return 0; + } +} + +void CCONTAINER_set_border(char *border, char new_border, QWidget *wid) +{ + int m; + + if (new_border < BORDER_NONE || new_border > BORDER_ETCHED || new_border == *border) + return; + + *border = new_border; + m = CCONTAINER_get_border_width(new_border); + + wid->setContentsMargins(m, m, m, m); + wid->update(); +} + +void MyFrame::drawFrame(QPainter *p) +{ + CCONTAINER_draw_border(p, _frame, this); +} + +int MyFrame::frameWidth() +{ + switch (_frame) + { + case BORDER_PLAIN: + return 1; + + case BORDER_SUNKEN: + case BORDER_RAISED: + return style()->pixelMetric(QStyle::PM_ComboBoxFrameWidth); + + case BORDER_ETCHED: + return 2; + + default: + return 0; + } +} + +void MyFrame::setPixmap(QPixmap *pixmap) +{ + if (_pixmap != pixmap) + { + _pixmap = pixmap; + setAttribute(Qt::WA_OpaquePaintEvent, _pixmap != 0); + setStaticContents(_pixmap != 0); + } +} + +void MyFrame::paintEvent(QPaintEvent *e) +{ + QPainter painter(this); + + if (_bg || (_pixmap && _pixmap->hasAlphaChannel())) + { + CWIDGET *window = CWidget::get(parentWidget()); + GB_COLOR col = CWIDGET_get_background(window); + if (col != COLOR_DEFAULT) + painter.fillRect(e->rect(), CCOLOR_make(col)); + } + + if (_pixmap) + painter.drawTiledPixmap(0, 0, width(), height(), *_pixmap); + + drawFrame(&painter); +} + + +/*************************************************************************** + + class MyContainer + +***************************************************************************/ + +MyContainer::MyContainer(QWidget *parent) +: MyFrame(parent) +{ + //setStaticContents(true); + //setAttribute(Qt::WA_StaticContents); + //setAttribute(Qt::WA_OpaquePaintEvent); //, _pixmap != 0); +} + +MyContainer::~MyContainer() +{ + CWIDGET *_object = CWidget::getReal(this); + if (THIS) + CWIDGET_set_flag(THIS, WF_DELETED); +} + +void MyContainer::showEvent(QShowEvent *e) +{ + void *_object = CWidget::get(this); + QWidget::showEvent(e); + THIS->widget.flag.shown = TRUE; + /*if (!qstrcmp(GB.GetClassName(THIS), "HBox")) + { + qDebug("MyContainer::showEvent: %s %p: shown = 1 (%d %d)", THIS->widget.name, THIS, THIS->widget.widget->isVisible() , !THIS->widget.widget->isHidden()); + //BREAKPOINT(); + }*/ + CCONTAINER_arrange(THIS); +} + +void MyContainer::hideEvent(QHideEvent *e) +{ + void *_object = CWidget::get(this); + QWidget::hideEvent(e); + THIS->widget.flag.shown = FALSE; + /*if (!qstrcmp(GB.GetClassName(THIS), "HBox")) + { + qDebug("MyContainer::hideEvent: %s %p: shown = 0", THIS->widget.name, THIS); + //BREAKPOINT(); + }*/ +} + + + +/*void MyContainer::childEvent(QChildEvent *e) +{ + //void *_object = CWidget::get(this); + void *child; + //qDebug("MyContainer::childEvent %p", CWidget::get(this)); + + QFrame::childEvent(e); + + if (!e->child()->isWidgetType()) + return; + + child = CWidget::get((QWidget *)e->child()); + + if (e->added()) + { + //e->child()->installEventFilter(this); + //qApp->sendEvent(WIDGET, new QEvent(EVENT_INSERT)); + //if (THIS_ARRANGEMENT->user) + // GB.Raise(THIS, EVENT_Insert, 1, GB_T_OBJECT, child); + } + else if (e->removed()) + { + //e->child()->removeEventFilter(this); + //if (THIS_ARRANGEMENT->user) + // GB.Raise(THIS, EVENT_Remove, 1, GB_T_OBJECT, child); + } + + arrange_later(this); +}*/ + +/*bool MyContainer::eventFilter(QObject *o, QEvent *e) +{ + int type = e->type(); + + if (type == QEvent::Move || type == QEvent::Resize || type == QEvent::Show || type == QEvent::Hide || type == EVENT_EXPAND) + { + CWIDGET *ob = CWidget::getReal(o); + if (ob && (type == EVENT_EXPAND || !ob->flag.ignore)) + arrange_now(this); + } + + return QObject::eventFilter(o, e); +}*/ + + +/*************************************************************************** + + CContainer + +***************************************************************************/ + +static QRect getRect(void *_object) +{ + QWidget *w = CONTAINER; + + if (qobject_cast(WIDGET)) + ((MyMainWindow *)WIDGET)->configure(); + + if (qobject_cast(WIDGET)) + return QRect(0, 0, w->width(), w->height()); + + return w->contentsRect(); +} + +BEGIN_PROPERTY(Container_X) + + #ifdef DEBUG + if (!CONTAINER) + qDebug("Null container"); + #endif + + QRect r = getRect(THIS); //_CONTAINER); + QPoint p(r.x(), r.y()); + + GB.ReturnInteger(CONTAINER->mapTo(WIDGET, p).x()); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Y) + + #ifdef DEBUG + if (!CONTAINER) + qDebug("Null container"); + #endif + + QRect r = getRect(THIS); // _CONTAINER); + QPoint p(r.x(), r.y()); + + GB.ReturnInteger(CONTAINER->mapTo(WIDGET, p).y()); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Width) + + #ifdef DEBUG + if (!CONTAINER) + qDebug("Null container"); + #endif + + GB.ReturnInteger(getRect(THIS).width()); + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Height) + + #ifdef DEBUG + if (!CONTAINER) + qDebug("Null container"); + #endif + + GB.ReturnInteger(getRect(THIS).height()); + +END_PROPERTY + +BEGIN_PROPERTY(Container_Border) + + MyContainer *w = qobject_cast(THIS->container); + + if (!w) + return; + + if (READ_PROPERTY) + GB.ReturnInteger(w->frameStyle()); + else + { + w->setFrameStyle(VPROP(GB_INTEGER)); + arrange_now(CONTAINER); + } + +END_PROPERTY + +BEGIN_PROPERTY(Container_SimpleBorder) + + MyContainer *w = qobject_cast(THIS->container); + + if (!w) + return; + + if (READ_PROPERTY) + GB.ReturnBoolean(w->frameStyle()); + else + { + w->setFrameStyle(VPROP(GB_BOOLEAN) ? BORDER_PLAIN : BORDER_NONE); + arrange_now(CONTAINER); + } + +END_PROPERTY + +BEGIN_PROPERTY(Container_Arrangement) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS_ARRANGEMENT->mode); + else + { + int arr = VPROP(GB_INTEGER); + if (arr < 0 || arr > 8 || arr == THIS_ARRANGEMENT->mode) + return; + THIS_ARRANGEMENT->mode = arr; + arrange_now(CONTAINER); + } + +END_PROPERTY + +BEGIN_PROPERTY(UserContainer_Arrangement) + + CCONTAINER *cont = (CCONTAINER *)CWidget::get(CONTAINER); + Container_Arrangement(cont, _param); + if (!READ_PROPERTY) + { + THIS_USERCONTAINER->save = cont->arrangement; + //qDebug("(%s %p): save = %08X (arrangement %d)", GB.GetClassName(THIS), THIS, THIS_USERCONTAINER->save, val); + } + +END_PROPERTY + +BEGIN_PROPERTY(UserContainer_Focus) + + if (READ_PROPERTY) + GB.ReturnBoolean(CWIDGET_get_allow_focus(THIS)); + else + CWIDGET_set_allow_focus(THIS, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Container_AutoResize) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS_ARRANGEMENT->autoresize); + else + { + bool v = VPROP(GB_BOOLEAN); + if (v == THIS_ARRANGEMENT->autoresize) + return; + + THIS_ARRANGEMENT->autoresize = v; + arrange_now(CONTAINER); + } + +END_PROPERTY + +BEGIN_PROPERTY(UserContainer_AutoResize) + + CCONTAINER *cont = (CCONTAINER *)CWidget::get(CONTAINER); + Container_AutoResize(cont, _param); + if (!READ_PROPERTY) + { + THIS_USERCONTAINER->save = cont->arrangement; + //qDebug("(%s %p): save = %08X (autoresize)", GB.GetClassName(THIS), THIS, THIS_USERCONTAINER->save); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Margin) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS_ARRANGEMENT->margin); + else + { + bool val = VPROP(GB_BOOLEAN); + if (val != THIS_ARRANGEMENT->margin) + { + THIS_ARRANGEMENT->margin = val; + arrange_now(CONTAINER); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(UserContainer_Margin) + + CCONTAINER *cont = (CCONTAINER *)CWidget::get(CONTAINER); + Container_Margin(cont, _param); + if (!READ_PROPERTY) + { + THIS_USERCONTAINER->save = cont->arrangement; + //qDebug("(%s %p): save = %08X (padding)", GB.GetClassName(THIS), THIS, THIS_USERCONTAINER->save); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Spacing) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS_ARRANGEMENT->spacing); + else + { + bool val = VPROP(GB_BOOLEAN); + if (val != THIS_ARRANGEMENT->spacing) + { + THIS_ARRANGEMENT->spacing = val; + arrange_now(CONTAINER); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(UserContainer_Spacing) + + CCONTAINER *cont = (CCONTAINER *)CWidget::get(CONTAINER); + Container_Spacing(cont, _param); + if (!READ_PROPERTY) + { + THIS_USERCONTAINER->save = cont->arrangement; + //qDebug("(%s %p): save = %08X (spacing)", GB.GetClassName(THIS), THIS, THIS_USERCONTAINER->save); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Invert) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS_ARRANGEMENT->invert); + else + { + bool val = VPROP(GB_BOOLEAN); + if (val != THIS_ARRANGEMENT->invert) + { + THIS_ARRANGEMENT->invert = val; + arrange_now(CONTAINER); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(UserContainer_Invert) + + CCONTAINER *cont = (CCONTAINER *)CWidget::get(CONTAINER); + Container_Invert(cont, _param); + if (!READ_PROPERTY) + THIS_USERCONTAINER->save = cont->arrangement; + +END_PROPERTY + + +BEGIN_PROPERTY(Container_Padding) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS_ARRANGEMENT->padding); + else + { + int val = VPROP(GB_INTEGER); + if (val != THIS_ARRANGEMENT->padding && val >= 0 && val <= 255) + { + THIS_ARRANGEMENT->padding = val; + arrange_now(CONTAINER); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(UserContainer_Padding) + + CCONTAINER *cont = (CCONTAINER *)CWidget::get(CONTAINER); + Container_Padding(cont, _param); + if (!READ_PROPERTY) + { + THIS_USERCONTAINER->save = cont->arrangement; + //qDebug("(%s %p): save = %08X (spacing)", GB.GetClassName(THIS), THIS, THIS_USERCONTAINER->save); + } + +END_PROPERTY + +BEGIN_PROPERTY(Container_Indent) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS_ARRANGEMENT->indent); + else + { + int val = VPROP(GB_INTEGER); + if (val < 0) val = 1; + if (val != THIS_ARRANGEMENT->indent && val >= 0 && val <= 7) + { + THIS_ARRANGEMENT->indent = val; + arrange_now(CONTAINER); + } + } + +END_PROPERTY + +BEGIN_METHOD(UserControl_new, GB_OBJECT parent) + + MyContainer *wid = new MyContainer(QCONTAINER(VARG(parent))); + + THIS->container = wid; + THIS_ARRANGEMENT->mode = ARRANGE_FILL; + THIS_ARRANGEMENT->user = true; + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_PROPERTY(UserControl_Container) + + void *current = (CCONTAINER *)CWidget::get(CONTAINER); + + if (READ_PROPERTY) + { + GB.ReturnObject(current); + } + else + { + CCONTAINER *cont = (CCONTAINER *)VPROP(GB_OBJECT); + QWidget *w; + QWidget *p; + + // sanity checks + + if (!cont) + { + if (current) + CWIDGET_container_for(current, NULL); + THIS->container = WIDGET; + CWIDGET_register_proxy(THIS, NULL); + return; + } + + if (GB.CheckObject(cont)) + return; + + w = cont->container; + if (w == THIS->container) + return; + + for (p = w; p; p = p->parentWidget()) + { + if (p == WIDGET) + break; + } + + if (!p) + GB.Error("Container must be a child control"); + else + { + GB_COLOR bg = CWIDGET_get_background((CWIDGET *)current, true); + GB_COLOR fg = CWIDGET_get_foreground((CWIDGET *)current, true); + + if (current) + CWIDGET_container_for(current, NULL); + CWIDGET_container_for(cont, THIS); + + THIS->container = w; + + CWIDGET_update_design((CWIDGET *)THIS); + CCONTAINER_arrange(THIS); + + CWIDGET_set_color((CWIDGET *)cont, bg, fg, true); + + CWIDGET_register_proxy(THIS, cont); + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Indent) + + CCONTAINER *cont = (CCONTAINER *)CWidget::get(CONTAINER); + Container_Indent(cont, _param); + if (!READ_PROPERTY) + { + THIS_USERCONTAINER->save = cont->arrangement; + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Container) + + //CCONTAINER *before; + CCONTAINER *after; + + if (READ_PROPERTY) + UserControl_Container(_object, _param); + else + { + UserControl_Container(_object, _param); + + after = (CCONTAINER *)CWidget::get(THIS->container); + bool old_locked = ((CCONTAINER_ARRANGEMENT *)after)->locked; + after->arrangement = THIS_USERCONTAINER->save; + ((CCONTAINER_ARRANGEMENT *)after)->locked = old_locked; + //qDebug("(%s %p): arrangement = %08X", GB.GetClassName(THIS), THIS, after->arrangement); + CCONTAINER_arrange(after); + } + +END_PROPERTY + + +BEGIN_PROPERTY(UserContainer_Design) + + Control_Design(_object, _param); + + if (!READ_PROPERTY && VPROP(GB_BOOLEAN)) + { + CCONTAINER *cont = (CCONTAINER *)CWidget::get(CONTAINER); + + cont->arrangement = 0; + ((CCONTAINER_ARRANGEMENT *)cont)->user = true; + THIS_USERCONTAINER->save = cont->arrangement; + } + +END_PROPERTY + + +BEGIN_METHOD(Container_FindChild, GB_INTEGER x; GB_INTEGER y) + + QObjectList list = CONTAINER->children(); + int index = 0; + QWidget *w; + void *control; + + for(;;) + { + w = get_next_widget(list, index); + if (!w) + break; + if (w->geometry().contains(VARG(x), VARG(y))) + { + control = CWidget::get(w); + if (control && control != THIS) + { + GB.ReturnObject(control); + return; + } + } + } + + GB.ReturnNull(); + +END_METHOD + + +BEGIN_METHOD(Container_unknown, GB_VALUE x; GB_VALUE y) + + char *name = GB.GetUnknown(); + int nparam = GB.NParam(); + + if (strcasecmp(name, "Find")) + { + GB.Error(GB_ERR_NSYMBOL, GB.GetClassName(NULL), name); + return; + } + + if (nparam < 2) + { + GB.Error("Not enough argument"); + return; + } + else if (nparam > 2) + { + GB.Error("Too many argument"); + return; + } + + GB.Deprecated(QT_NAME, "Container.Find", "Container.FindChild"); + + if (GB.Conv(ARG(x), GB_T_INTEGER) || GB.Conv(ARG(y), GB_T_INTEGER)) + return; + + Container_FindChild(_object, _param); + + GB.ReturnConvVariant(); + +END_METHOD + + +//--------------------------------------------------------------------------- + + +BEGIN_PROPERTY(Container_Children) + + CCONTAINERCHILDREN *children = (CCONTAINERCHILDREN *)GB.New(CLASS_ContainerChildren, NULL, NULL); + QObjectList list = CONTAINER->children(); + CWIDGET *child; + int i; + + children->container = THIS; + GB.Ref(THIS); + + GB.NewArray(POINTER(&children->children), sizeof(void *), 0); + + for (i = 0; i < list.count(); i++) + { + child = CWidget::getRealExisting(list.at(i)); + if (!child) + continue; + GB.Ref(child); + *(void **)GB.Add(&children->children) = child; + } + + GB.ReturnObject(children); + +END_PROPERTY + + +BEGIN_METHOD_VOID(ContainerChildren_free) + + int i; + CWIDGET **array = THIS_CHILDREN->children; + + for (i = 0; i < GB.Count(array); i++) + GB.Unref(POINTER(&array[i])); + + GB.FreeArray(&THIS_CHILDREN->children); + GB.Unref(POINTER(&THIS_CHILDREN->container)); + +END_METHOD + + +BEGIN_PROPERTY(ContainerChildren_Count) + + GB.ReturnInteger(GB.Count(THIS_CHILDREN->children)); + +END_PROPERTY + + +BEGIN_PROPERTY(ContainerChildren_Max) + + GB.ReturnInteger(GB.Count(THIS_CHILDREN->children) - 1); + +END_PROPERTY + + +BEGIN_METHOD(ContainerChildren_get, GB_INTEGER index) + + CWIDGET **array = THIS_CHILDREN->children; + int index = VARG(index); + + if (index < 0 || index >= GB.Count(array)) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(array[index]); + +END_METHOD + + +BEGIN_METHOD_VOID(ContainerChildren_next) + + CWIDGET **array = THIS_CHILDREN->children; + int index; + + index = ENUM(int); + + if (index >= GB.Count(array)) + GB.StopEnum(); + else + { + ENUM(int) = index + 1; + GB.ReturnObject(array[index]); + } + +END_METHOD + + +BEGIN_METHOD_VOID(ContainerChildren_Clear) + + CCONTAINER_ARRANGEMENT *container = (CCONTAINER_ARRANGEMENT *)THIS_CHILDREN->container; + CWIDGET **children = THIS_CHILDREN->children; + bool locked; + int i; + + locked = container->locked; + container->locked = true; + + for (i = 0; i < GB.Count(children); i++) + CWIDGET_destroy(children[i]); + + container->locked = locked; + CCONTAINER_arrange(container); + +END_METHOD + + +//--------------------------------------------------------------------------- + + +GB_DESC ContainerChildrenDesc[] = +{ + GB_DECLARE("ContainerChildren", sizeof(CCONTAINER)), GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, ContainerChildren_free, NULL), + GB_METHOD("_next", "Control", ContainerChildren_next, NULL), + GB_METHOD("_get", "Control", ContainerChildren_get, "(Index)i"), + GB_PROPERTY_READ("Count", "i", ContainerChildren_Count), + GB_PROPERTY_READ("Max", "i", ContainerChildren_Max), + GB_METHOD("Clear", NULL, ContainerChildren_Clear, NULL), + + GB_END_DECLARE +}; + + +GB_DESC ContainerDesc[] = +{ + GB_DECLARE("Container", sizeof(CCONTAINER)), GB_INHERITS("Control"), + GB_NOT_CREATABLE(), + + GB_PROPERTY_READ("Children", "ContainerChildren", Container_Children), + + GB_PROPERTY_READ("ClientX", "i", Container_X), + GB_PROPERTY_READ("ClientY", "i", Container_Y), + GB_PROPERTY_READ("ClientW", "i", Container_Width), + GB_PROPERTY_READ("ClientWidth", "i", Container_Width), + GB_PROPERTY_READ("ClientH", "i", Container_Height), + GB_PROPERTY_READ("ClientHeight", "i", Container_Height), + + GB_METHOD("_unknown", "v", Container_unknown, "."), + GB_METHOD("FindChild", "Control", Container_FindChild, "(X)i(Y)i"), + + CONTAINER_DESCRIPTION, + + GB_EVENT("BeforeArrange", NULL, NULL, &EVENT_BeforeArrange), + GB_EVENT("Arrange", NULL, NULL, &EVENT_Arrange), + GB_EVENT("NewChild", NULL, "(Child)Control", &EVENT_Insert), + + GB_END_DECLARE +}; + + +GB_DESC UserControlDesc[] = +{ + GB_DECLARE("UserControl", sizeof(CCONTAINER)), GB_INHERITS("Container"), + GB_NOT_CREATABLE(), + + GB_METHOD("_new", NULL, UserControl_new, "(Parent)Container;"), + + GB_PROPERTY("_Container", "Container", UserControl_Container), + GB_PROPERTY("_AutoResize", "b", Container_AutoResize), + GB_PROPERTY("_Arrangement", "i", Container_Arrangement), + GB_PROPERTY("_Padding", "i", Container_Padding), + GB_PROPERTY("_Spacing", "b", Container_Spacing), + GB_PROPERTY("_Margin", "b", Container_Margin), + GB_PROPERTY("_Indent", "b", Container_Indent), + GB_PROPERTY("_Invert", "b", Container_Invert), + + USERCONTROL_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC UserContainerDesc[] = +{ + GB_DECLARE("UserContainer", sizeof(CUSERCONTAINER)), GB_INHERITS("Container"), + GB_NOT_CREATABLE(), + + GB_METHOD("_new", NULL, UserControl_new, "(Parent)Container;"), + + GB_PROPERTY("_Container", "Container", UserContainer_Container), + GB_PROPERTY("_Arrangement", "i", Container_Arrangement), + + GB_PROPERTY("Arrangement", "i", UserContainer_Arrangement), + GB_PROPERTY("AutoResize", "b", UserContainer_AutoResize), + GB_PROPERTY("Padding", "i", UserContainer_Padding), + GB_PROPERTY("Spacing", "b", UserContainer_Spacing), + GB_PROPERTY("Margin", "b", UserContainer_Margin), + GB_PROPERTY("Indent", "b", UserContainer_Indent), + GB_PROPERTY("Invert", "b", UserContainer_Invert), + + GB_PROPERTY("Design", "b", UserContainer_Design), + + //GB_PROPERTY("Focus", "b", UserContainer_Focus), + + USERCONTAINER_DESCRIPTION, + + GB_END_DECLARE +}; + + diff --git a/gb.qt4/src/CContainer.h b/gb.qt4/src/CContainer.h new file mode 100644 index 00000000..bbea8d80 --- /dev/null +++ b/gb.qt4/src/CContainer.h @@ -0,0 +1,170 @@ +/*************************************************************************** + + CContainer.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCONTAINER_H +#define __CCONTAINER_H + +#include +#include +#include +#include +#include +#include + +#include "gambas.h" + +#include "CConst.h" +#include "CWidget.h" + +typedef + struct { + unsigned mode : 4; + unsigned user : 1; + unsigned locked : 1; + unsigned margin : 1; + unsigned spacing : 1; + unsigned padding : 8; + unsigned indent : 4; + unsigned dirty : 1; + unsigned autoresize : 1; + unsigned invert : 1; + unsigned _reserved: 8; + } + CARRANGEMENT; + +#ifndef __CCONTAINER_CPP + +extern GB_DESC ContainerDesc[]; +extern GB_DESC ContainerChildrenDesc[]; +extern GB_DESC UserControlDesc[]; +extern GB_DESC UserContainerDesc[]; + +#else + +typedef + struct { + CWIDGET widget; + QWidget *container; + unsigned mode : 4; + unsigned user : 1; + unsigned locked : 1; + unsigned margin : 1; + unsigned spacing : 1; + unsigned padding : 8; + unsigned indent : 4; + unsigned dirty : 1; + unsigned autoresize : 1; + unsigned invert : 1; + unsigned _reserved: 9; + } + CCONTAINER_ARRANGEMENT; + +typedef + struct { + CCONTAINER parent; + int32_t save; + } + CUSERCONTAINER; + +typedef + struct { + GB_BASE ob; + CCONTAINER *container; + CWIDGET **children; + } + CCONTAINERCHILDREN; + +#define WIDGET QWIDGET(_object) +#define THIS ((CCONTAINER *)_object) +#define THIS_CHILDREN ((CCONTAINERCHILDREN *)_object) +#define CONTAINER (THIS->container) +#define THIS_ARRANGEMENT (((CCONTAINER_ARRANGEMENT *)_object)) +#define THIS_USERCONTAINER (((CUSERCONTAINER *)_object)) + +//#define CCONTAINER_PROPERTIES CWIDGET_PROPERTIES ",Arrangement" + +#endif + +DECLARE_PROPERTY(Container_Arrangement); +DECLARE_PROPERTY(Container_AutoResize); +DECLARE_PROPERTY(Container_Padding); +DECLARE_PROPERTY(Container_Spacing); +DECLARE_PROPERTY(Container_Margin); +DECLARE_PROPERTY(Container_Indent); +DECLARE_PROPERTY(Container_Border); +DECLARE_PROPERTY(Container_SimpleBorder); +DECLARE_PROPERTY(Container_Invert); + +void CCONTAINER_arrange(void *_object); +void CCONTAINER_get_max_size(void *_object, int xc, int yc, int wc, int hc, int *w, int *h); +void CCONTAINER_insert_child(void *_object); +void CCONTAINER_remove_child(void *_object); +void CCONTAINER_decide(CWIDGET *control, bool *width, bool *height); + +void CCONTAINER_draw_border(QPainter *p, char border, QWidget *w); +void CCONTAINER_draw_border_without_widget(QPainter *p, char border, QStyleOptionFrame &opt); +void CCONTAINER_set_border(char *border, char new_border, QWidget *wid); +int CCONTAINER_get_border_width(char border); + +class MyFrame : public QWidget +{ + Q_OBJECT + +public: + MyFrame(QWidget *); + + int frameStyle() const { return _frame; } + void setFrameStyle(int frame); + void drawFrame(QPainter *); + int frameWidth(); + void setPixmap(QPixmap *pixmap); + void setPaintBackgroundColor(bool bg) { _bg = bg; } + +protected: + + virtual void setStaticContents(bool on); + virtual void paintEvent(QPaintEvent *); + +private: + + int _frame; + QPixmap *_pixmap; + bool _bg; +}; + +class MyContainer : public MyFrame +{ + Q_OBJECT + +public: + + MyContainer(QWidget *); + ~MyContainer(); + +protected: + + virtual void showEvent(QShowEvent *); + virtual void hideEvent(QHideEvent *); +}; + +#endif diff --git a/gb.qt4/src/CDialog.cpp b/gb.qt4/src/CDialog.cpp new file mode 100644 index 00000000..862e9fd2 --- /dev/null +++ b/gb.qt4/src/CDialog.cpp @@ -0,0 +1,403 @@ +/*************************************************************************** + + CDialog.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDIALOG_CPP + +#include +#include +#include +#include +#include + +#include "gambas.h" +#include "CColor.h" +#include "CFont.h" + +#include "CDialog.h" + +static QString dialog_title; +static GB_ARRAY dialog_filter = NULL; +static QString dialog_path; +static GB_ARRAY dialog_paths = NULL; +static CFONT *dialog_font = NULL; +static bool dialog_show_hidden = false; + +static unsigned int dialog_color = 0; + + +static QString get_filter(void) +{ + QString s; + int i; + QString filter; + + if (dialog_filter) + { + for (i = 0; i < (GB.Array.Count(dialog_filter) / 2); i++) + { + filter = TO_QSTRING(*((char **)GB.Array.Get(dialog_filter, i * 2))); + if (filter == "*") + continue; + + if (s.length()) + s += ";;"; + s += TO_QSTRING(*((char **)GB.Array.Get(dialog_filter, i * 2 + 1))); + s += " (" + filter.replace(";", " ") + ")"; + } + + s += ";;"; + s += TO_QSTRING(GB.Translate("All files")); + s += " (*)"; + } + + return s; +} + +BEGIN_METHOD_VOID(CDIALOG_exit) + + GB.StoreObject(NULL, POINTER(&dialog_filter)); + GB.StoreObject(NULL, POINTER(&dialog_paths)); + GB.StoreObject(NULL, POINTER(&dialog_font)); + +END_METHOD + + +BEGIN_PROPERTY(Dialog_Title) + + if (READ_PROPERTY) + RETURN_NEW_STRING(dialog_title); + else + dialog_title = QSTRING_PROP(); + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_Filter) + + if (READ_PROPERTY) + GB.ReturnObject(dialog_filter); + else + GB.StoreObject(PROP(GB_OBJECT), POINTER(&dialog_filter)); + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_ShowHidden) + + if (READ_PROPERTY) + GB.ReturnBoolean(dialog_show_hidden); + else + dialog_show_hidden = VPROP(GB_BOOLEAN); + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_Path) + + if (READ_PROPERTY) + RETURN_NEW_STRING(dialog_path); + else + dialog_path = QSTRING_PROP(); + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_Paths) + + GB.ReturnObject(dialog_paths); + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_Font) + + if (READ_PROPERTY) + GB.ReturnObject(dialog_font); + else + { + CFONT *font = (CFONT *)VPROP(GB_OBJECT); + + GB.StoreObject(NULL, POINTER(&dialog_font)); + if (font) + { + dialog_font = CFONT_create(*font->font); + GB.Ref(dialog_font); + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(Dialog_Color) + + if (READ_PROPERTY) + GB.ReturnInteger(dialog_color); + else + dialog_color = VPROP(GB_INTEGER); + +END_PROPERTY + + +static QString my_getOpenFileName() +{ + QFileDialog dialog(qApp->activeWindow(), dialog_title, dialog_path, get_filter()); + + dialog.setFileMode(QFileDialog::ExistingFile); + dialog.setOption(QFileDialog::DontUseNativeDialog); + dialog.setFilter(dialog_show_hidden ? (dialog.filter() | QDir::Hidden | QDir::System) : (dialog.filter() & ~(QDir::Hidden | QDir::System))); + + if (dialog.exec() == QDialog::Accepted) + return dialog.selectedFiles().value(0); + else + return QString(); +} + +static QStringList my_getOpenFileNames() +{ + QFileDialog dialog(qApp->activeWindow(), dialog_title, dialog_path, get_filter()); + + dialog.setFileMode(QFileDialog::ExistingFiles); + dialog.setOption(QFileDialog::DontUseNativeDialog); + dialog.setFilter(dialog_show_hidden ? (dialog.filter() | QDir::Hidden | QDir::System) : (dialog.filter() & ~(QDir::Hidden | QDir::System))); + + if (dialog.exec() == QDialog::Accepted) + return dialog.selectedFiles(); + else + return QStringList(); +} + +static QString my_getSaveFileName() +{ + QString dir, file; + + dir = dialog_path; + if (!dialog_path.endsWith('/')) + { + int pos = dialog_path.lastIndexOf('/'); + if (pos >= 0) + { + dir = dialog_path.left(pos); + file = dialog_path.mid(pos + 1); + } + } + + QFileDialog dialog(qApp->activeWindow(), dialog_title, dir, get_filter()); + dialog.selectFile(file); + + dialog.setAcceptMode(QFileDialog::AcceptSave); + dialog.setFileMode(QFileDialog::AnyFile); + dialog.setOption(QFileDialog::DontUseNativeDialog); + dialog.setFilter(dialog_show_hidden ? (dialog.filter() | QDir::Hidden | QDir::System) : (dialog.filter() & ~(QDir::Hidden | QDir::System))); + + if (dialog.exec() == QDialog::Accepted) + return dialog.selectedFiles().value(0); + else + return QString(); +} + +static QString my_getExistingDirectory() +{ + QFileDialog dialog(qApp->activeWindow(), dialog_title, dialog_path); + + dialog.setFileMode(QFileDialog::Directory); + dialog.setOption(QFileDialog::DontUseNativeDialog); + + if (dialog.exec() == QDialog::Accepted) + return dialog.selectedFiles().value(0); + else + return QString(); +} + + +BEGIN_METHOD(Dialog_OpenFile, GB_BOOLEAN multi) + + if (!VARGOPT(multi, false)) + { + QString file; + + file = my_getOpenFileName(); + + if (file.isNull()) + GB.ReturnBoolean(true); + else + { + dialog_path = file; + GB.ReturnBoolean(false); + } + } + else + { + QStringList files; + GB_ARRAY list; + GB_OBJECT ob; + int i; + + files = my_getOpenFileNames(); + + if (files.isEmpty()) + { + GB.StoreObject(NULL, POINTER(&dialog_paths)); + GB.ReturnBoolean(true); + } + else + { + GB.Array.New(&list, GB_T_STRING, files.count()); + ob.value = list; + GB.StoreObject(&ob, POINTER(&dialog_paths)); + + for (i = 0; i < files.count(); i++) + *(char **)GB.Array.Get(list, i) = NEW_STRING(files[i]); + + GB.ReturnBoolean(false); + } + } + + dialog_title = QString::null; + +END_METHOD + + +BEGIN_METHOD_VOID(Dialog_SaveFile) + + QString file; + + file = my_getSaveFileName(); + + if (file.isNull()) + GB.ReturnBoolean(true); + else + { + dialog_path = file; + GB.ReturnBoolean(false); + } + + dialog_title = QString::null; + +END_METHOD + + +BEGIN_METHOD_VOID(Dialog_SelectDirectory) + + QString file; + + file = my_getExistingDirectory(); + + if (file.isNull()) + GB.ReturnBoolean(true); + else + { + dialog_path = file; + GB.ReturnBoolean(false); + } + + dialog_title = QString::null; + +END_METHOD + + +BEGIN_METHOD_VOID(Dialog_SelectColor) + + QColor color; + + color = QColorDialog::getColor(dialog_color, qApp->activeWindow(), dialog_title); //, qApp->activeWindow()); + + if (!color.isValid()) + GB.ReturnBoolean(true); + else + { + dialog_color = color.rgb() & 0xFFFFFF; + GB.ReturnBoolean(false); + } + +END_METHOD + + +BEGIN_METHOD_VOID(Dialog_SelectFont) + + QFont qfont; + bool ok; + #ifdef USE_DPI + int dpiX, dpiY; + #endif + + if (dialog_font) + qfont = *dialog_font->font; + else + qfont = QApplication::font(); + //qDebug("AVANT: %g --> %g", qfont.pointSizeFloat(), SIZE_REAL_TO_VIRTUAL(qfont.pointSizeFloat())); + qfont.setPointSizeF(SIZE_REAL_TO_VIRTUAL(qfont.pointSizeF())); + + #ifdef USE_DPI + dpiX = QPaintDevice::x11AppDpiX(); + dpiY = QPaintDevice::x11AppDpiY(); + QPaintDevice::x11SetAppDpiX(CFONT_dpi); + QPaintDevice::x11SetAppDpiY(CFONT_dpi); + #endif + + qfont = QFontDialog::getFont(&ok, qfont, qApp->activeWindow(), dialog_title); + + #ifdef USE_DPI + QPaintDevice::x11SetAppDpiX(dpiX); + QPaintDevice::x11SetAppDpiY(dpiY); + #endif + + //qDebug("APRES: %g --> %g", qfont.pointSizeFloat(), SIZE_VIRTUAL_TO_REAL(qfont.pointSizeFloat())); + qfont.setPointSizeF(SIZE_VIRTUAL_TO_REAL(qfont.pointSizeF())); + + if (!ok) + GB.ReturnBoolean(true); + else + { + GB.StoreObject(NULL, POINTER(&dialog_font)); + dialog_font = CFONT_create(qfont); + GB.Ref(dialog_font); + GB.ReturnBoolean(false); + } + +END_METHOD + +GB_DESC CDialogDesc[] = +{ + GB_DECLARE("Dialog", 0), GB_VIRTUAL_CLASS(), + + //GB_STATIC_METHOD("_init", NULL, CDIALOG_init, NULL), + GB_STATIC_METHOD("_exit", NULL, CDIALOG_exit, NULL), + + GB_STATIC_METHOD("OpenFile", "b", Dialog_OpenFile, "[(Multi)b]"), + GB_STATIC_METHOD("SaveFile", "b", Dialog_SaveFile, NULL), + GB_STATIC_METHOD("SelectDirectory", "b", Dialog_SelectDirectory, NULL), + GB_STATIC_METHOD("SelectColor", "b", Dialog_SelectColor, NULL), + GB_STATIC_METHOD("SelectFont", "b", Dialog_SelectFont, NULL), + + GB_STATIC_PROPERTY("Title", "s", Dialog_Title), + GB_STATIC_PROPERTY("Path", "s", Dialog_Path), + GB_STATIC_PROPERTY_READ("Paths", "String[]", Dialog_Paths), + GB_STATIC_PROPERTY("Filter", "String[]", Dialog_Filter), + GB_STATIC_PROPERTY("Color", "i", Dialog_Color), + GB_STATIC_PROPERTY("Font", "Font", Dialog_Font), + GB_STATIC_PROPERTY("ShowHidden", "b", Dialog_ShowHidden), + + GB_END_DECLARE +}; + + diff --git a/gb.qt4/src/CDialog.h b/gb.qt4/src/CDialog.h new file mode 100644 index 00000000..a83f8094 --- /dev/null +++ b/gb.qt4/src/CDialog.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + CDialog.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDIALOG_H +#define __CDIALOG_H + +#include "gambas.h" +#include "CWidget.h" + +#ifndef __CDIALOG_CPP +extern GB_DESC CDialogDesc[]; +#endif + +#endif diff --git a/gb.qt4/src/CDraw.cpp b/gb.qt4/src/CDraw.cpp new file mode 100644 index 00000000..8b2f54a5 --- /dev/null +++ b/gb.qt4/src/CDraw.cpp @@ -0,0 +1,281 @@ +/*************************************************************************** + + CDraw.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDRAW_CPP + +#ifdef OS_SOLARIS +/* Make math.h define M_PI and a few other things */ +#define __EXTENSIONS__ +/* Get definition for finite() */ +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +//Added by qt3to4: +#include +#include +#include +#include +#include +#include +#include + +#ifndef NO_X_WINDOW +#include +#endif + +#include "gambas.h" + +#include "CConst.h" +#include "CFont.h" +#include "CWidget.h" +#include "CWindow.h" +#include "CPicture.h" +#include "CImage.h" +#include "CDrawingArea.h" +#include "CColor.h" +#include "CDraw.h" + +typedef + QT_DRAW_EXTRA GB_DRAW_EXTRA; + +#define EXTRA(d) ((GB_DRAW_EXTRA *)(d->extra)) +#define DP(d) (EXTRA(d)->p) +#define DPM(d) (EXTRA(d)->pm) + +#define COLOR_TO_INT(color) ((color).rgba() ^ 0xFF000000) +#define MASK_COLOR(col) ((col & 0xFF000000) ? Qt::color0 : Qt::color1) + +DRAW_INTERFACE DRAW EXPORT; + +/*static void set_background(GB_DRAW *d, int col); +static void set_foreground(GB_DRAW *d, int col); +static void set_fill_color(GB_DRAW *d, int col);*/ + +void DRAW_init() +{ + GB.GetInterface("gb.draw", DRAW_INTERFACE_VERSION, &DRAW); +} + +static Qt::Alignment get_horizontal_alignment(Qt::Alignment align) +{ + align &= Qt::AlignHorizontal_Mask; + switch (align) + { + case Qt::AlignLeft: + if (QApplication::isRightToLeft()) + return Qt::AlignRight; + break; + + case Qt::AlignRight: + if (QApplication::isRightToLeft()) + return Qt::AlignLeft; + break; + + default: + break; + } + + return align & ~Qt::AlignAbsolute; +} + +static QStringList text_sl; +static QVector text_w; +static int text_line; + +static int get_text_width(QPainter *dp, QString &s) +{ + int w, width = 0; + int i; + + text_sl = s.split('\n', QString::KeepEmptyParts); + + text_w.resize(text_sl.count()); + + for (i = 0; i < (int)text_sl.count(); i++) + { + w = dp->fontMetrics().width(text_sl[i]); + if (w > width) width = w; + text_w[i] = w; + } + + return width; +} + +static int get_text_height(QPainter *dp, QString &s) +{ + text_line = dp->fontMetrics().height(); + return text_line * (1 + s.count('\n')); +} + +void DRAW_text(QPainter *p, const QString &text, float x, float y, float w, float h, int align, QPainter *p2) +{ + QPen pen, penm; + QString t = text; + int xx, ww; + int tw, th; + int i; + + tw = get_text_width(p, t); + th = get_text_height(p, t); + + if (w < 0) w = tw; + if (h < 0) h = th; + + y += p->fontMetrics().ascent(); + + switch(align & Qt::AlignVertical_Mask) + { + case Qt::AlignBottom: y += h - th; break; + case Qt::AlignVCenter: y += (h - th) / 2; break; + default: break; + } + + align = get_horizontal_alignment((Qt::Alignment)align); + + for (i = 0; i < (int)text_sl.count(); i++) + { + t = text_sl[i]; + ww = text_w[i]; + + switch(align) + { + case Qt::AlignRight: xx = x + w - ww; break; + case Qt::AlignHCenter: xx = x + (w - ww) / 2; break; + default: xx = x; break; + } + + //(*callback)(xx, y, t); + p->drawText(xx, y, t); + if (p2) + p2->drawText(xx, y, t); + + y += text_line; + } +} + +void DRAW_rich_text(QPainter *p, const QString &text, float x, float y, float w, float h, int align, QPainter *p2) +{ + static QTextDocument *doc = NULL; + + QString a; + float tw, th; + QColor fg = p->pen().color(); + QString t = "" + text + ""; + qreal opacity = 1.0; + bool hasAlpha = fg.alpha() < 255; + int margin; + + switch(get_horizontal_alignment((Qt::Alignment)align)) + { + case Qt::AlignRight: a = "right"; break; + case Qt::AlignHCenter: a = "center"; break; + case Qt::AlignJustify: a = "justify"; break; + } + + if (a.length()) + t = "
    " + t + "
    "; + + if (!doc) + { + doc = new QTextDocument; + doc->setDocumentMargin(0); + } + + doc->setDefaultFont(p->font()); + margin = p->font().pointSize() * p->device()->physicalDpiY() / 96; + doc->setDefaultStyleSheet(QString("p { margin-bottom: %1px; } h1,h2,h3,h4,h5,h6 { margin-bottom: %1px; }").arg(margin)); + doc->setHtml(t); + + if (w > 0) + doc->setTextWidth(w); + + tw = doc->idealWidth(); + th = doc->size().height(); + + if (w < 0) w = tw; + if (h < 0) h = th; + + switch(align & Qt::AlignVertical_Mask) + { + case Qt::AlignBottom: y += h - th; break; + case Qt::AlignVCenter: y += (h - th) / 2; break; + default: break; + } + + if (hasAlpha) + { + opacity = p->opacity(); + p->setOpacity(p->opacity() * fg.alpha() / 255.0); + } + + p->translate(x, y); + doc->drawContents(p); + p->translate(-x, -y); + + if (hasAlpha) + p->setOpacity(opacity); + + if (p2) + { + p2->translate(x, y); + doc->drawContents(p2); + p2->translate(-x, -y); + } +} + +void DRAW_aligned_pixmap(QPainter *p, const QPixmap &pix, int x, int y, int w, int h, int align) +{ + int xp, yp; + + if (pix.isNull() || pix.width() == 0 || pix.height() == 0) + return; + + xp = x; + switch(get_horizontal_alignment((Qt::Alignment)align)) + { + case Qt::AlignRight: xp += w - pix.width(); break; + case Qt::AlignHCenter: xp += (w - pix.width()) / 2; break; + default: break; + } + + yp = y; + switch(align & Qt::AlignVertical_Mask) + { + case Qt::AlignBottom: yp += h - pix.height(); break; + case Qt::AlignVCenter: yp += (h - pix.height()) / 2; break; + default: break; + } + + p->drawPixmap(xp, yp, pix); +} + diff --git a/gb.qt4/src/CDraw.h b/gb.qt4/src/CDraw.h new file mode 100644 index 00000000..f7fcd631 --- /dev/null +++ b/gb.qt4/src/CDraw.h @@ -0,0 +1,47 @@ +/*************************************************************************** + + CDraw.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDRAW_H +#define __CDRAW_H + +#include "gambas.h" +#include "gb.draw.h" + +#include +#include + +#ifndef __CDRAW_C + +extern DRAW_INTERFACE DRAW; + +#endif + +typedef + void (*DRAW_TEXT_CB)(float, float, QString &); + +void DRAW_init(); +void DRAW_text(QPainter *p, const QString &text, float x, float y, float w, float h, int align, QPainter *p2 = 0); +void DRAW_rich_text(QPainter *p, const QString &text, float x, float y, float w, float h, int align, QPainter *p2 = 0); +void DRAW_aligned_pixmap(QPainter *p, const QPixmap &pix, int x, int y, int w, int h, int align); + +#endif diff --git a/gb.qt4/src/CDrawingArea.cpp b/gb.qt4/src/CDrawingArea.cpp new file mode 100644 index 00000000..5d5fca47 --- /dev/null +++ b/gb.qt4/src/CDrawingArea.cpp @@ -0,0 +1,761 @@ +/*************************************************************************** + + CDrawingArea.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDRAWINGAREA_CPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CDraw.h" +#include "cpaint_impl.h" +#include "CColor.h" +#include "CDrawingArea.h" + +#ifndef QT5 +#ifndef NO_X_WINDOW +#include +#include +#endif + +#ifdef FontChange +#undef FontChange +#endif +#endif + +DECLARE_EVENT(EVENT_Draw); +DECLARE_EVENT(EVENT_Font); +DECLARE_EVENT(EVENT_Change); + + +static void send_change_event_widget(CWIDGET *widget) +{ + if (GB.Is(widget, CLASS_DrawingArea)) + GB.Raise(widget, EVENT_Change, 0); +} + +void CDRAWINGAREA_send_change_event() +{ + CWidget::each(send_change_event_widget); +} + + +/*************************************************************************** + + class MyDrawingArea + +***************************************************************************/ + +int MyDrawingArea::_in_any_draw_event = 0; + +MyDrawingArea::MyDrawingArea(QWidget *parent) : MyContainer(parent) +{ + drawn = 0; + cache = 0; +#ifndef QT5 + _background = (Qt::HANDLE)0; +#endif + _frozen = false; + _event_mask = 0; + _set_background = true; + _cached = false; + _no_background = false; + _in_draw_event = false; + _draw_event = EVENT_Draw; + + setAttribute(Qt::WA_KeyCompression, false); + setAttribute(Qt::WA_PaintOnScreen, false); + setAttribute(Qt::WA_OpaquePaintEvent, false); + setAttribute(Qt::WA_StaticContents, false); + + setAllowFocus(false); +} + + +MyDrawingArea::~MyDrawingArea() +{ + deleteBackground(); +} + +void MyDrawingArea::setVisible(bool visible) +{ + MyContainer::setVisible(visible); +#ifndef QT5 + if (_cached) + { + if (visible) + QTimer::singleShot(10, this, SLOT(setBackground())); + else + parentWidget()->update(); + } +#endif +} + +void MyDrawingArea::setAllowFocus(bool f) +{ + if (f) + { + void *_object = CWidget::getReal(this); + setFocusPolicy(GB.CanRaise(THIS, EVENT_MouseWheel) ? Qt::WheelFocus : Qt::StrongFocus); + setAttribute(Qt::WA_InputMethodEnabled, true); + } + else + { + setFocusPolicy(Qt::NoFocus); + } +} + +void MyDrawingArea::setFrozen(bool f) +{ + if (f == _frozen) + return; + +#ifndef QT5 +#ifndef NO_X_WINDOW + XWindowAttributes attr; + + if (f) + { + //setBackgroundMode(Qt::NoBackground); + XGetWindowAttributes(QX11Info::display(), winId(), &attr); + _event_mask = attr.your_event_mask; + XSelectInput(QX11Info::display(), winId(), ExposureMask); + //clearWFlags(Qt::WPaintClever); + //qDebug("frozen"); + } + else + { + //setBackgroundMode(Qt::PaletteBackground); + XSelectInput(QX11Info::display(), winId(), _event_mask); + //qDebug("unfrozen"); + } + XFlush(QX11Info::display()); +#endif +#endif + + _frozen = f; +} + +static void cleanup_drawing(intptr_t _object) +{ + PAINT_end(); +} + +void MyDrawingArea::redraw(QRect &r, bool frame) +{ + QPainter *p; + void *_object = CWidget::get(this); + int fw; + GB_COLOR bg; + GB_RAISE_HANDLER handler; + + if (!_object) + return; + + //qDebug("paint: %d %d %d %d", r.x(), r.y(), r.width(), r.height()); + + _in_draw_event = true; + _in_any_draw_event++; + + PAINT_begin(THIS); + p = PAINT_get_current(); + + /*if (!isTransparent()) + { + p->translate(-r.x(), -r.y()); + }*/ + + //p->save(); + + fw = frameWidth(); + + bg = CWIDGET_get_background((CWIDGET *)THIS); + if (bg != COLOR_DEFAULT) + { + p->fillRect(fw, fw, width() - fw * 2, height() - fw * 2, TO_QCOLOR(bg)); + } + + PAINT_clip(r.x(), r.y(), r.width(), r.height()); + + //p->setClipRegion(event->region().intersect(contentsRect())); + //p->setBrushOrigin(-r.x(), -r.y()); + + handler.callback = cleanup_drawing; + handler.data = (intptr_t)THIS; + + GB.RaiseBegin(&handler); + GB.Raise(THIS, _draw_event, 0); + GB.RaiseEnd(&handler); + + //p->restore(); + + if (frame) + { + QPainter pf(this); + pf.setClipping(false); + pf.initFrom(this); + pf.setRenderHint(QPainter::Antialiasing, false); + drawFrame(&pf); + } + + PAINT_end(); + + _in_draw_event = false; + _in_any_draw_event--; +} + +void MyDrawingArea::createBackground(int w, int h) +{ + void *_object = CWidget::get(this); + QPixmap p; +#ifndef QT5 + QX11Info xinfo = x11Info(); + Qt::HANDLE old = _background; +#endif + +#ifndef QT5 + _background = (Qt::HANDLE)XCreatePixmap(QX11Info::display(), RootWindow(QX11Info::display(), xinfo.screen()), w, h, xinfo.depth()); + _background_pixmap = QPixmap::fromX11Pixmap(_background, QPixmap::ExplicitlyShared); +#else + _background_pixmap = QPixmap(w, h); +#endif + _background_w = w; + _background_h = h; + + //qDebug("createBackground: %d x %d : %d -> %08X / winId = %08X", w, h, xinfo.depth(), (int)_background, (int)winId()); + //p = QPixmap::fromX11Pixmap(_background, QPixmap::ExplicitlyShared); + //qDebug("color = %06X -> %06X", palette().color(backgroundRole()).rgb(), QColormap::instance().pixel((unsigned long)palette().color(backgroundRole()).rgb())); + +#if 0 + gc = XCreateGC(QX11Info::display(), _background, 0, 0); + //XSetForeground(QX11Info::display(), gc, QColormap::instance().pixel((unsigned long)palette().color(backgroundRole()).rgb())); + XSetForeground(QX11Info::display(), gc, QColormap::instance().pixel(CWIDGET_get_real_background((CWIDGET *)THIS))); + XFillRectangle(QX11Info::display(), _background, gc, 0, 0, w, h); + XFreeGC(QX11Info::display(), gc); +#endif + + _background_pixmap.fill(CCOLOR_make(CWIDGET_get_real_background((CWIDGET *)THIS))); + + //qDebug("XSetWindowBackgroundPixmap: %08X %08X", (int)winId(), (int)_background); +#ifndef QT5 + XSetWindowBackgroundPixmap(QX11Info::display(), winId(), _background); + XClearArea(QX11Info::display(), winId(), 0, 0, 0, 0, True); + + if (old) + XFreePixmap(QX11Info::display(), (Pixmap)old); + + XFlush(QX11Info::display()); +#else + update(); +#endif + + _cached = true; +} + +void MyDrawingArea::deleteBackground() +{ + if (hasCacheBackground()) + { +#ifndef QT5 + //qDebug("XSetWindowBackgroundPixmap: %08X None", (int)winId()); + XSetWindowBackgroundPixmap(QX11Info::display(), winId(), None); + XFreePixmap(QX11Info::display(), (Pixmap)_background); + XFlush(QX11Info::display()); + _background = 0; +#else + _background_pixmap = QPixmap(); +#endif + _cached = false; + _background_w = _background_h = 0; + } +} + +QPixmap *MyDrawingArea::getBackgroundPixmap() +{ + if (!hasCacheBackground()) + return NULL; + else + return &_background_pixmap; +} + +void MyDrawingArea::paintEvent(QPaintEvent *event) +{ + if (_cached) + { +#ifndef QT5 +#ifndef NO_X_WINDOW + if (_set_background) + { + //qDebug("XSetWindowBackgroundPixmap: %08X %08X (paint)", (int)winId(), (int)_background); + XSetWindowBackgroundPixmap(QX11Info::display(), winId(), _background); + XFlush(QX11Info::display()); + _set_background = false; + } +#endif +#endif + + QPainter p(this); + +#ifdef QT5 + p.drawPixmap(0, 0, _background_pixmap); +#endif + + if (frameWidth()) + { + QRegion r(0, 0, width(), height()); + r = r.subtracted(QRegion(frameWidth(), frameWidth(), width() - frameWidth() * 2, height() - frameWidth() * 2)); + p.setClipRegion(r); + p.setClipping(true); + //p.drawPixmap(0, 0, *getBackgroundPixmap()); + } + + drawFrame(&p); + } + else + { + //QPainter paint( this ); + QRect r; + + r = event->rect().intersected(contentsRect()); + if (r.isValid()) + { + /*if (!isTransparent()) + { + cache = new QPixmap(r.width(), r.height()); + cache->fill(this, r.x(), r.y()); + }*/ + + redraw(r, true); + + /*if (!isTransparent()) + { + paint.drawPixmap(r.x(), r.y(), *cache); + delete cache; + cache = 0; + }*/ + } + } +} + +void MyDrawingArea::setBackground() +{ +#ifndef QT5 + if (_cached) + { + XSetWindowBackgroundPixmap(QX11Info::display(), winId(), _background); + XFlush(QX11Info::display()); + refreshBackground(); + } +#endif +} + +void MyDrawingArea::refreshBackground() +{ + if (_cached) + { +#ifndef QT5 + int fw = frameWidth(); + XClearArea(QX11Info::display(), winId(), fw, fw, width() - fw * 2, height() - fw * 2, False); + XFlush(QX11Info::display()); +#else + update(); +#endif + } +} + + +void MyDrawingArea::clearBackground() +{ + if (drawn) + { + GB.Error("DrawingArea is being painted"); + return; + } + + if (_cached) + { + createBackground(width(), height()); + } + else + { +#ifndef QT5 + XClearArea(QX11Info::display(), winId(), 0, 0, 0, 0, True); + XFlush(QX11Info::display()); +#else + update(); +#endif + } +} + +void MyDrawingArea::resizeEvent(QResizeEvent *e) +{ + MyContainer::resizeEvent(e); + updateBackground(); +} + +void MyDrawingArea::updateBackground() +{ + int w, h; +#ifndef QT5 + int wb, hb; +#endif + + if (_cached) + { + if (drawn) + { + GB.Error("DrawingArea is being drawn"); + return; + } + + w = qMax(width(), 1); + h = qMax(height(), 1); + + if (w != _background_w || h != _background_h) + { +#ifndef QT5 + Qt::HANDLE old = _background; + + wb = qMin(w, _background_w); + hb = qMin(h, _background_h); + + _background = 0; + createBackground(w, h); + + GC gc = XCreateGC(QX11Info::display(), old, 0, 0); + XCopyArea(QX11Info::display(), old, _background, gc, 0, 0, wb, hb, 0, 0); + XFreeGC(QX11Info::display(), gc); + + XFreePixmap(QX11Info::display(), old); +#else + QPixmap old = _background_pixmap; + createBackground(w, h); + QPainter p(&_background_pixmap); + p.drawPixmap(0, 0, old); + p.end(); +#endif + + setBackground(); + } + } +} + +void MyDrawingArea::setStaticContents(bool on) +{ +} + + +void MyDrawingArea::updateCache() +{ + //qDebug("updateCache: %d %08X", _cached, (int)winId()); + + if (_cached) // && !_transparent) + { +#ifndef QT5 + setAttribute(Qt::WA_DontCreateNativeAncestors, true); + setAttribute(Qt::WA_NativeWindow, true); + setAttribute(Qt::WA_PaintOnScreen, true); +#endif + setAttribute(Qt::WA_OpaquePaintEvent, true); + setAttribute(Qt::WA_StaticContents, true); + createBackground(width(), height()); + setBackground(); + } + else //if (_background) + { + deleteBackground(); + setAttribute(Qt::WA_PaintOnScreen, false); + setAttribute(Qt::WA_OpaquePaintEvent, false); + setAttribute(Qt::WA_StaticContents, false); + #ifdef NO_X_WINDOW + setBackgroundMode(Qt::NoBackground); + #else + //XClearArea(QX11Info::display(), winId(), 0, 0, 0, 0, True); + //repaint(); + update(); + #endif + } + + updateNoBackground(); +} + +void MyDrawingArea::setCached(bool c) +{ + if (c == _cached) + return; + + _cached = c; + updateCache(); +} + +void MyDrawingArea::setPalette(const QPalette &pal) +{ + if (_cached) + return; + MyContainer::setPalette(pal); + update(); +} + +void MyDrawingArea::updateNoBackground() +{ + setAttribute(Qt::WA_NoSystemBackground, _no_background); + if (_cached) + setBackground(); +} + +void MyDrawingArea::setNoBackground(bool on) +{ + _no_background = on; + updateNoBackground(); +} + +void MyDrawingArea::hideEvent(QHideEvent *e) +{ + if (_cached) + _set_background = true; + MyContainer::hideEvent(e); +} + +void MyDrawingArea::changeEvent(QEvent *e) +{ + if (e->type() == QEvent::FontChange) + { + MyContainer::changeEvent(e); + void *_object = CWidget::get(this); + GB.Raise(THIS, EVENT_Font, 0); + } +} + +/*************************************************************************** + + DrawingArea + +***************************************************************************/ + +BEGIN_METHOD(DrawingArea_new, GB_OBJECT parent) + + MyDrawingArea *wid = new MyDrawingArea(QCONTAINER(VARG(parent))); + + //THIS->widget.background = QColorGroup::Base; + THIS->container = wid; + THIS->widget.flag.noBackground = true; + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_PROPERTY(DrawingArea_Cached) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isCached()); + else + { + GB_COLOR bg = CWIDGET_get_background((CWIDGET *)THIS); + GB_COLOR fg = CWIDGET_get_foreground((CWIDGET *)THIS); + + if (bg == COLOR_DEFAULT) + { + CWIDGET_set_color((CWIDGET *)THIS, WIDGET->palette().color(WIDGET->backgroundRole()).rgb() & 0xFFFFFF, fg); + WIDGET->clearBackground(); + } + WIDGET->setCached(VPROP(GB_BOOLEAN)); + } + +END_PROPERTY + + +DECLARE_METHOD(Control_Background); + + +BEGIN_METHOD_VOID(DrawingArea_Clear) + + WIDGET->clearBackground(); + +END_METHOD + + +BEGIN_PROPERTY(DrawingArea_Background) + + Control_Background(_object, _param); + + if (!READ_PROPERTY) + WIDGET->clearBackground(); + +END_PROPERTY + + +BEGIN_PROPERTY(DrawingArea_NoBackground) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->hasNoBackground()); + else + WIDGET->setNoBackground(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(DrawingArea_Border) + + Container_Border(_object, _param); + + if (!READ_PROPERTY) + { + WIDGET->clearBackground(); + } + +END_PROPERTY + +BEGIN_PROPERTY(DrawingArea_Enabled) + + Control_Enabled(_object, _param); + + if (!READ_PROPERTY) + WIDGET->setFrozen(!VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(DrawingArea_Focus) + + if (READ_PROPERTY) + GB.ReturnBoolean(CWIDGET_get_allow_focus(THIS)); + else + CWIDGET_set_allow_focus(THIS, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(DrawingArea_Painted) + + static bool deprecated = false; + + if (!deprecated) + { + deprecated = true; + GB.Deprecated(QT_NAME, "DrawingArea.Painted", NULL); + } + + if (READ_PROPERTY) + GB.ReturnBoolean(true); + +END_PROPERTY + +#if 0 +BEGIN_PROPERTY(CDRAWINGAREA_transparent) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isTransparent()); + else + { + WIDGET->setTransparent(VPROP(GB_BOOLEAN)); + //THIS->widget.flag.fillBackground = !WIDGET->isTransparent(); + //CWIDGET_reset_color((CWIDGET *)THIS); + } + +END_PROPERTY +#endif + +BEGIN_METHOD(DrawingArea_Refresh, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + int x, y, w, h; + + /*if (WIDGET->isCached()) + { + QRect r; + + if (!MISSING(x) && !MISSING(y)) + r.setRect(VARG(x), VARG(y), VARGOPT(w, WIDGET->width()), VARGOPT(h, WIDGET->height())); + else + r.setRect(0, 0, WIDGET->width(), WIDGET->height()); + + WIDGET->redraw(r, false); + }*/ + + if (!MISSING(x) && !MISSING(y)) + { + x = VARG(x); + y = VARG(y); + w = VARGOPT(w, QWIDGET(_object)->width()); + h = VARGOPT(h, QWIDGET(_object)->height()); + WIDGET->update(x, y, w, h); + } + else + { + WIDGET->update(); + } + +END_METHOD + +BEGIN_PROPERTY(DrawingArea_Tablet) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->widget.flag.use_tablet); + else + THIS->widget.flag.use_tablet = VPROP(GB_BOOLEAN); + +END_PROPERTY + + +GB_DESC CDrawingAreaDesc[] = +{ + GB_DECLARE("DrawingArea", sizeof(CDRAWINGAREA)), GB_INHERITS("Container"), + + GB_METHOD("_new", NULL, DrawingArea_new, "(Parent)Container;"), + + GB_PROPERTY("Cached", "b", DrawingArea_Cached), + + GB_PROPERTY("Arrangement", "i", Container_Arrangement), + GB_PROPERTY("AutoResize", "b", Container_AutoResize), + GB_PROPERTY("Spacing", "b", Container_Spacing), + GB_PROPERTY("Margin", "b", Container_Margin), + GB_PROPERTY("Padding", "i", Container_Padding), + GB_PROPERTY("Indent", "b", Container_Indent), + GB_PROPERTY("Invert", "b", Container_Invert), + + GB_PROPERTY("Border", "i", DrawingArea_Border), + GB_PROPERTY("NoBackground", "b", DrawingArea_NoBackground), + GB_PROPERTY("Background", "i", DrawingArea_Background), + + GB_PROPERTY("Focus", "b", DrawingArea_Focus), + GB_PROPERTY("Enabled", "b", DrawingArea_Enabled), + GB_PROPERTY("Painted", "b", DrawingArea_Painted), + GB_PROPERTY("Tablet", "b", DrawingArea_Tablet), + + GB_METHOD("Clear", NULL, DrawingArea_Clear, NULL), + GB_METHOD("Refresh", NULL, DrawingArea_Refresh, "[(X)i(Y)i(Width)i(Height)i]"), + + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + GB_EVENT("Font", NULL, NULL, &EVENT_Font), + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + + //GB_INTERFACE("Draw", &DRAW_Interface), + GB_INTERFACE("Paint", &PAINT_Interface), + + DRAWINGAREA_DESCRIPTION, + + GB_END_DECLARE +}; diff --git a/gb.qt4/src/CDrawingArea.h b/gb.qt4/src/CDrawingArea.h new file mode 100644 index 00000000..b693b5eb --- /dev/null +++ b/gb.qt4/src/CDrawingArea.h @@ -0,0 +1,147 @@ +/*************************************************************************** + + CDrawingArea.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDRAWINGAREA_H +#define __CDRAWINGAREA_H + +#include "gambas.h" + +#include +#include +#include +//#include + +#include "CWidget.h" +#include "CContainer.h" + +#ifndef __CDRAWINGAREA_CPP +extern GB_DESC CDrawingAreaDesc[]; +#else + +#define THIS ((CDRAWINGAREA *)_object) +#define WIDGET ((MyDrawingArea *)((CWIDGET *)_object)->widget) + +#endif + +typedef + struct { + CWIDGET widget; + QWidget *container; + CARRANGEMENT arrangement; + } + CDRAWINGAREA; + +class MyDrawingArea : public MyContainer +{ + Q_OBJECT + + friend class MyScrollArea; + +public: + + MyDrawingArea(QWidget *parent); + ~MyDrawingArea(); + + int drawn; + QPixmap *cache; + + virtual void setVisible(bool visible); + + //void setTransparent(bool); + //bool isTransparent(void) { return transparent; } + + void updateCache(); + void setCached(bool); + bool isCached() const { return _cached; } + + void clearBackground(); +#ifndef QT5 + Qt::HANDLE background() const { return _background; } +#endif + void refreshBackground(); + void updateBackground(); + + void setFrozen(bool f); + bool isFrozen() const { return _frozen; } + + void setAllowFocus(bool f); + bool isAllowFocus() const { return focusPolicy() != Qt::NoFocus; } + + void redraw(QRect &r, bool frame = false); + + bool hasNoBackground() const { return _no_background; } + void setNoBackground(bool on); + void updateNoBackground(); + + void setDrawEvent(int event) { _draw_event = event; } + bool inDrawEvent() const { return _in_draw_event; } + static bool inAnyDrawEvent() { return _in_any_draw_event > 0; } + + void createBackground(int w, int h); +#ifndef QT5 + bool hasCacheBackground() const { return _cached && _background; } +#else + bool hasCacheBackground() const { return _cached && !_background_pixmap.isNull(); } +#endif + void deleteBackground(); + + QPixmap *getBackgroundPixmap(); + +public slots: + + void setBackground(); + //bool isTransparent() { return _transparent; } + //void setTransparent(bool on); + +protected: + + virtual void setStaticContents(bool on); + virtual void resizeEvent(QResizeEvent *); + virtual void paintEvent(QPaintEvent *); + virtual void hideEvent(QHideEvent *); + //virtual void drawContents(QPainter *p); + virtual void setPalette(const QPalette &); + virtual void changeEvent(QEvent *); + +private: + + QPixmap _background_pixmap; +#ifndef QT5 + Qt::HANDLE _background; +#endif + int _background_w, _background_h; + bool _frozen; + bool _merge; + bool _focus; + int _event_mask; + bool _set_background; + bool _cached; + bool _no_background; + bool _in_draw_event; + int _draw_event; + static int _in_any_draw_event; +}; + +void CDRAWINGAREA_send_change_event(void); + +#endif diff --git a/gb.qt4/src/CEmbedder.cpp b/gb.qt4/src/CEmbedder.cpp new file mode 100644 index 00000000..05acabc7 --- /dev/null +++ b/gb.qt4/src/CEmbedder.cpp @@ -0,0 +1,143 @@ +/*************************************************************************** + + CEmbedder.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CEMBEDDER_CPP + +#include "CEmbedder.h" + +DECLARE_EVENT(EVENT_Embed); +DECLARE_EVENT(EVENT_Close); +DECLARE_EVENT(EVENT_Error); + +#ifdef NO_X_WINDOW + +BEGIN_METHOD(CEMBEDDER_new, GB_OBJECT parent) + + QWidget *wid = new QWidget(QCONTAINER(VARG(parent))); + + //QObject::connect(wid, SIGNAL(clientIsEmbedded()), &CEmbedder::manager, SLOT(embedded())); + //QObject::connect(wid, SIGNAL(clientClosed()), &CEmbedder::manager, SLOT(closed())); + //QObject::connect(wid, SIGNAL(error(QX11EmbedContainer::Error)), &CEmbedder::manager, SLOT(error())); + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_PROPERTY(CEMBEDDER_client) + + GB.ReturnInteger(0); + +END_PROPERTY + + +BEGIN_METHOD(CEMBEDDER_embed, GB_INTEGER client) + + //WIDGET->embedClient(VARG(client)); + +END_METHOD + + +BEGIN_METHOD_VOID(CEMBEDDER_discard) + + //WIDGET->discardClient(); + +END_METHOD + +#else + +BEGIN_METHOD(CEMBEDDER_new, GB_OBJECT parent) + + QX11EmbedContainer *wid = new QX11EmbedContainer(QCONTAINER(VARG(parent))); + + QObject::connect(wid, SIGNAL(clientIsEmbedded()), &CEmbedder::manager, SLOT(embedded())); + QObject::connect(wid, SIGNAL(clientClosed()), &CEmbedder::manager, SLOT(closed())); + QObject::connect(wid, SIGNAL(error(QX11EmbedContainer::Error)), &CEmbedder::manager, SLOT(error())); + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_PROPERTY(CEMBEDDER_client) + + GB.ReturnInteger((int)WIDGET->clientWinId()); + +END_PROPERTY + + +BEGIN_METHOD(CEMBEDDER_embed, GB_INTEGER client) + + WIDGET->embedClient(VARG(client)); + +END_METHOD + + +BEGIN_METHOD_VOID(CEMBEDDER_discard) + + WIDGET->discardClient(); + +END_METHOD + +/*--- CEmbedder -----------------------------------------------------------------------------------------*/ + +CEmbedder CEmbedder::manager; + +void CEmbedder::embedded() +{ + RAISE_EVENT(EVENT_Embed); +} + +void CEmbedder::closed() +{ + RAISE_EVENT(EVENT_Close); +} + +void CEmbedder::error() +{ + RAISE_EVENT(EVENT_Error); +} + +#endif + +GB_DESC CEmbedderDesc[] = +{ + GB_DECLARE("Embedder", sizeof(CEMBEDDER)), + GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CEMBEDDER_new, "(Parent)Container;"), + + GB_PROPERTY_READ("Client", "i", CEMBEDDER_client), + GB_METHOD("Embed", NULL, CEMBEDDER_embed, "(Client)i"), + GB_METHOD("Discard", NULL, CEMBEDDER_discard, NULL), + + EMBEDDER_DESCRIPTION, + + GB_EVENT("Embed", NULL, NULL, &EVENT_Embed), + GB_EVENT("Close", NULL, NULL, &EVENT_Close), + GB_EVENT("Error", NULL, NULL, &EVENT_Error), + + GB_END_DECLARE +}; + + diff --git a/gb.qt4/src/CEmbedder.h b/gb.qt4/src/CEmbedder.h new file mode 100644 index 00000000..8f37770b --- /dev/null +++ b/gb.qt4/src/CEmbedder.h @@ -0,0 +1,67 @@ +/*************************************************************************** + + CEmbedder.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CEMBEDDER_H +#define __CEMBEDDER_H + +#include "gambas.h" +#include "CWidget.h" + +#ifndef __CEMBEDDER_CPP +extern GB_DESC CEmbedderDesc[]; +#else + +typedef + struct { + CWIDGET widget; + } + CEMBEDDER; + +#define THIS ((CEMBEDDER *)_object) +#endif + +#ifndef NO_X_WINDOW + +#include +#include + +#define WIDGET ((QX11EmbedContainer *)((CWIDGET *)_object)->widget) + +class CEmbedder : public QObject +{ + Q_OBJECT + +public: + + static CEmbedder manager; + +public slots: + + void error(void); + void embedded(void); + void closed(void); + +}; +#endif + +#endif diff --git a/gb.qt4/src/CFont.cpp b/gb.qt4/src/CFont.cpp new file mode 100644 index 00000000..c3fd1b9b --- /dev/null +++ b/gb.qt4/src/CFont.cpp @@ -0,0 +1,654 @@ +/*************************************************************************** + + CFont.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CFONT_CPP + +#include "gambas.h" +#include "main.h" + +#include + +#include +#include +#include +#include +#include + +#include "CWidget.h" +#include "CDraw.h" +#include "CFont.h" + +#include "gb.form.font.h" + +#ifdef USE_DPI +int CFONT_dpi = 96; +#endif + +static GB_CLASS CLASS_Font; + +static QStringList _families; +static QFontDatabase *_info = 0; + +static void init_font_database() +{ + if (_info) + return; + + _info = new QFontDatabase(); + _families = _info->families(); +} + +static void exit_font_database() +{ + if (_info) + delete _info; +} + + +CFONT *CFONT_create(const QFont &font, FONT_FUNC func, void *object) +{ + CFONT *_object = (CFONT *)GB.New(CLASS_Font, NULL, NULL); + + *(THIS->font) = font; + THIS->func = func; + THIS->object = object; + if (object) + GB.Ref(object); + + return THIS; +} + +void CFONT_set(FONT_FUNC func, void *font, void *object) +{ + if (!font) + { + QFont f; + (*func)(f, object); + } + else + (*func)(*(((CFONT *)font)->font), object); +} + + +double CFONT_size_real_to_virtual(double size) +{ + #ifdef USE_DPI + return size * (double)QPaintDevice::x11AppDpiY() / CFONT_dpi; + #else + return size; + #endif +} + +double CFONT_size_virtual_to_real(double size) +{ + #ifdef USE_DPI + return size * CFONT_dpi / (double)QPaintDevice::x11AppDpiY(); + #else + return size; + #endif +} + + +static void set_font_from_string(CFONT *_object, QString &str) +{ + QStringList list; + QString name, elt, flag; + double size = 0; + bool number; + QFont f; + + // (!) Remove this test later, it is for backward compatibility + + if (str.length()) + { + list = str.split(","); + + for (QStringList::Iterator it = list.begin(); it != list.end(); ++it ) + { + elt = (*it); + elt = elt.trimmed(); + flag = elt.toUpper(); + size = elt.toDouble(&number); + + if (flag == "BOLD") + { + f.setBold(true); +#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0) + f.setStyleName("Bold"); +#endif + } + else if (flag == "ITALIC") + f.setItalic(true); + else if (flag == "UNDERLINE") + f.setUnderline(true); + else if (flag == "STRIKEOUT") + f.setStrikeOut(true); + else if (flag[0] == '+' || flag[0] == '-' || flag[0] == '0') + { + //f.setPointSizeFloat((int)(powf(qApp->font().pointSizeFloat(), 1.0 + ((int)size / 10.0)) + 0.5)); + f.setPointSizeF(GRADE_TO_SIZE(size, qApp->font().pointSizeF())); + } + else if (number && size > 0.0) + f.setPointSizeF(SIZE_VIRTUAL_TO_REAL(size)); + else if (elt.length()) + { + f.setBold(false); + f.setItalic(false); + f.setUnderline(false); + f.setStrikeOut(false); + f.setFamily(elt); +#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0) + f.setStyleName(""); +#endif + } + } + } + + *(THIS->font) = f; +} + + +BEGIN_METHOD_VOID(Font_init) + + CLASS_Font = GB.FindClass("Font"); + +END_METHOD + + +BEGIN_METHOD_VOID(Font_exit) + + exit_font_database(); + +END_METHOD + + +BEGIN_METHOD(Font_new, GB_STRING font) + + QString s; + + THIS->font = new QFont; + THIS->func = 0; + THIS->object = 0; + + if (!MISSING(font)) + QString s = QSTRING_ARG(font); + + set_font_from_string(THIS, s); + +END_METHOD + + +BEGIN_METHOD_VOID(Font_free) + + if (THIS->object) + GB.Unref(POINTER(&THIS->object)); + delete THIS->font; + +END_METHOD + + +static void CFONT_manage(int prop, CFONT *_object, void *_param) +{ + bool noResize = false; + QFont *f = THIS->font; + double size; + + noResize = true; //((long)THIS->control == CFONT_DRAW && !DRAW_must_resize_font()); + + if (READ_PROPERTY) + { + switch(prop) + { + case CFONT::Name: GB.ReturnNewZeroString(f->family().toUtf8()); break; + case CFONT::Size: + if (noResize) + GB.ReturnFloat(f->pointSizeF()); + else + GB.ReturnFloat(SIZE_REAL_TO_VIRTUAL(f->pointSizeF())); + break; + case CFONT::Grade: + GB.ReturnInteger(SIZE_TO_GRADE(f->pointSizeF(), qApp->font().pointSizeF())); + break; + case CFONT::Bold: GB.ReturnBoolean(f->bold()); break; + case CFONT::Italic: GB.ReturnBoolean(f->italic()); break; + case CFONT::Underline: GB.ReturnBoolean(f->underline()); break; + case CFONT::Strikeout: GB.ReturnBoolean(f->strikeOut()); break; + } + } + else + { + switch (prop) + { + case CFONT::Name: f->setFamily(GB.ToZeroString(PROP(GB_STRING))); break; + case CFONT::Size: + if (noResize) + size = VPROP(GB_FLOAT); + else + size = SIZE_VIRTUAL_TO_REAL(VPROP(GB_FLOAT)); + + if (size <= 0) + { + GB.Error("Bad font size"); + return; + } + + f->setPointSizeF(size); + break; + case CFONT::Grade: + { + int g = VPROP(GB_INTEGER); + if (g < FONT_GRADE_MIN) + g = FONT_GRADE_MIN; + else if (g > FONT_GRADE_MAX) + g = FONT_GRADE_MAX; + f->setPointSizeF(GRADE_TO_SIZE(g, qApp->font().pointSizeF())); + } + break; + case CFONT::Bold: f->setBold(VPROP(GB_BOOLEAN)); break; + case CFONT::Italic: f->setItalic(VPROP(GB_BOOLEAN)); break; + case CFONT::Underline: f->setUnderline(VPROP(GB_BOOLEAN)); break; + case CFONT::Strikeout: f->setStrikeOut(VPROP(GB_BOOLEAN)); break; + } + + if (THIS->func) + (*(THIS->func))(*f, THIS->object); + else if (THIS->object) + { + GB_VALUE value; + value.type = GB_T_OBJECT; + value._object.value = THIS; + GB.SetProperty(THIS->object, "Font", &value); + } + + THIS->modified = TRUE; + } +} + + +BEGIN_PROPERTY(Font_Name) + + CFONT_manage(CFONT::Name, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Size) + + CFONT_manage(CFONT::Size, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Grade) + + CFONT_manage(CFONT::Grade, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Bold) + + CFONT_manage(CFONT::Bold, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Italic) + + CFONT_manage(CFONT::Italic, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Underline) + + CFONT_manage(CFONT::Underline, OBJECT(CFONT), _param); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Strikeout) + + CFONT_manage(CFONT::Strikeout, OBJECT(CFONT), _param); + +END_PROPERTY + + +static void add(QString &str, const QString& data) +{ + if (str.length()) + str += ','; + + str += data; +} + +BEGIN_METHOD_VOID(Font_ToString) + + QFont *f = THIS->font; + QString str; + double size; + + //str = qfont.family().left(1).upper() + qfont.family().mid(1).lower() + " " + QString::number(qfont.pointSize()); + add(str, f->family()); + size = SIZE_REAL_TO_VIRTUAL(f->pointSizeF()); + size = (double)((int)(size * 10 + 0.5)) / 10; + add(str, QString::number(size)); + if (f->bold()) + add(str, "Bold"); + if (f->italic()) + add(str, "Italic"); + if (f->underline()) + add(str, "Underline"); + if (f->strikeOut()) + add(str, "StrikeOut"); + + RETURN_NEW_STRING(str); + +END_METHOD + + +BEGIN_METHOD(Font_get, GB_STRING str) + + CFONT *font; + QString s = QSTRING_ARG(str); + + //qDebug(">> Font_get: %s", s.latin1()); + + font = (CFONT *)GB.New(CLASS_Font, NULL, NULL); + set_font_from_string(font, s); + + GB.ReturnObject(font); + + //qDebug("<< Font_get"); + +END_METHOD + +BEGIN_METHOD_VOID(Font_Copy) + + GB.ReturnObject(CFONT_create(*THIS->font)); + +END_METHOD + + +BEGIN_PROPERTY(Font_Ascent) + + QFontMetrics fm(*(THIS->font)); + + GB.ReturnInteger(fm.ascent()); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Descent) + + QFontMetrics fm(*(THIS->font)); + + GB.ReturnInteger(fm.descent()); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Height) + + QFontMetrics fm(*(THIS->font)); + + GB.ReturnInteger(fm.height() + fm.leading()); + +END_PROPERTY + + +BEGIN_METHOD(Font_TextHeight, GB_STRING text) + + QFontMetrics fm(*(THIS->font)); + QString s; + int nl; + + if (!MISSING(text)) + s = QSTRING_ARG(text); + nl = s.count('\n'); + + GB.ReturnInteger(fm.height() * (1 + nl) + fm.leading() * nl); + +END_METHOD + + +BEGIN_METHOD(Font_TextWidth, GB_STRING text) + + QFontMetricsF fm(*(THIS->font)); + QStringList sl; + qreal w, width = 0; + int i; + + QString str = QSTRING_ARG(text); + + sl = str.split('\n'); + + for (i = 0; i < (int)sl.count(); i++) + { + w = fm.width(sl[i]); + if (w > width) width = w; + } + + GB.ReturnInteger((int)(width + 0.5)); + +END_METHOD + + +static void rich_text_size(CFONT *_object, char *text, int len, int sw, int *w, int *h) +{ + QTextDocument rt; + + rt.setDocumentMargin(0); + rt.setHtml(QString::fromUtf8((const char *)text, len)); + rt.setDefaultFont(*(THIS->font)); + + if (sw > 0) + rt.setTextWidth(sw); + + if (w) *w = rt.idealWidth(); + if (h) *h = rt.size().height(); +} + + +BEGIN_METHOD(Font_RichTextWidth, GB_STRING text) + + int w; + + rich_text_size(THIS, STRING(text), LENGTH(text), -1, &w, NULL); + GB.ReturnInteger(w); + +END_METHOD + + +BEGIN_METHOD(Font_RichTextHeight, GB_STRING text; GB_INTEGER width) + + int h; + + rich_text_size(THIS, STRING(text), LENGTH(text), VARGOPT(width, -1), NULL, &h); + GB.ReturnInteger(h); + +END_METHOD + + +#ifdef USE_DPI +BEGIN_PROPERTY(Font_Resolution) + + if (READ_PROPERTY) + GB.ReturnInteger(CFONT_dpi); + else + { + CFONT_dpi = VPROP(GB_INTEGER); + if (CFONT_dpi < 1) + CFONT_dpi = 96; + } + +END_PROPERTY +#endif + +BEGIN_METHOD_VOID(Fonts_next) + + QString s; + int *index = (int *)GB.GetEnum(); + + if (*index == 0) + init_font_database(); + + if (*index >= _families.count()) + GB.StopEnum(); + else + { + s = _families[*index]; + RETURN_NEW_STRING(s); + (*index)++; + } + +END_METHOD + + +BEGIN_METHOD(Fonts_Exist, GB_STRING family) + + int i; + char *family = GB.ToZeroString(ARG(family)); + + init_font_database(); + + for (i = 0; i < _families.count(); i++) + { + if (_families[i] == family) + { + GB.ReturnBoolean(true); + return; + } + } + + GB.ReturnBoolean(false); + +END_METHOD + + +BEGIN_PROPERTY(Fonts_Count) + + init_font_database(); + GB.ReturnInteger(_families.count()); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Fixed) + + init_font_database(); + GB.ReturnBoolean(_info->isFixedPitch(THIS->font->family())); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Scalable) + + init_font_database(); + GB.ReturnBoolean(_info->isSmoothlyScalable(THIS->font->family())); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Styles) + + QStringList styles; + GB_ARRAY array; + int i; + + init_font_database(); + styles = _info->styles(THIS->font->family()); + + GB.Array.New(&array, GB_T_STRING, styles.count()); + for (i = 0; i < styles.count(); i++) + *(char **)GB.Array.Get(array, i) = NEW_STRING(styles[i]); + + GB.ReturnObject(array); + +END_PROPERTY + + +BEGIN_PROPERTY(Font_Modified) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->modified); + else + THIS->modified = VPROP(GB_BOOLEAN); + +END_PROPERTY + +//--------------------------------------------------------------------------- + +GB_DESC CFontsDesc[] = +{ + GB_DECLARE_STATIC("Fonts"), + + GB_STATIC_METHOD("Exist", "b", Fonts_Exist, "(Family)s"), + GB_STATIC_METHOD("_next", "s", Fonts_next, NULL), + GB_STATIC_PROPERTY_READ("Count", "i", Fonts_Count), + + GB_END_DECLARE +}; + + +GB_DESC CFontDesc[] = +{ + GB_DECLARE("Font", sizeof(CFONT)), + //GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("_init", NULL, Font_init, NULL), + GB_STATIC_METHOD("_exit", NULL, Font_exit, NULL), + GB_METHOD("_new", NULL, Font_new, "[(Font)s]"), + GB_METHOD("_free", NULL, Font_free, NULL), + GB_METHOD("Copy", "Font", Font_Copy, NULL), + + GB_PROPERTY("Name", "s", Font_Name), + GB_PROPERTY("Size", "f", Font_Size), + GB_PROPERTY("Grade", "i", Font_Grade), + GB_PROPERTY("Bold", "b", Font_Bold), + GB_PROPERTY("Italic", "b", Font_Italic), + GB_PROPERTY("Underline", "b", Font_Underline), + GB_PROPERTY("Strikeout", "b", Font_Strikeout), + GB_PROPERTY("Modified", "b", Font_Modified), + + GB_METHOD("ToString", "s", Font_ToString, NULL), + + GB_METHOD("TextWidth", "i", Font_TextWidth, "(Text)s"), + GB_METHOD("TextHeight", "i", Font_TextHeight, "(Text)s"), + + GB_METHOD("RichTextWidth", "i", Font_RichTextWidth, "(Text)s"), + GB_METHOD("RichTextHeight", "i", Font_RichTextHeight, "(Text)s[(Width)i]"), + + GB_STATIC_METHOD("_get", "Font", Font_get, "(Font)s"), + + #ifdef USE_DPI + GB_STATIC_PROPERTY("Resolution", "i", Font_Resolution), + #endif + + GB_PROPERTY_READ("Ascent", "i", Font_Ascent), + GB_PROPERTY_READ("Descent", "i", Font_Descent), + GB_PROPERTY_READ("Height", "i", Font_Height), + GB_PROPERTY_READ("H", "i", Font_Height), + + GB_PROPERTY_READ("Fixed", "b", Font_Fixed), + GB_PROPERTY_READ("Scalable", "b", Font_Scalable), + GB_PROPERTY_READ("Styles", "String[]", Font_Styles), + + GB_END_DECLARE +}; + + diff --git a/gb.qt4/src/CFont.h b/gb.qt4/src/CFont.h new file mode 100644 index 00000000..d36e99ce --- /dev/null +++ b/gb.qt4/src/CFont.h @@ -0,0 +1,85 @@ +/*************************************************************************** + + CFont.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CFONT_H +#define __CFONT_H + +#include + +#include "gambas.h" +//#include "main.h" +#include "CWidget.h" + +#ifndef NO_X_WINDOW +//#define USE_DPI +#endif + +#ifndef __CFONT_CPP +extern GB_DESC CFontDesc[]; +extern GB_DESC CFontsDesc[]; + +#ifdef USE_DPI +extern int CFONT_dpi; +#endif + +#else + +#define THIS OBJECT(CFONT) + +#endif + +typedef + void (*FONT_FUNC)(QFont &, void *); + +typedef + struct { + GB_BASE ob; + QString *family; + } + CFONTINFO; + +typedef + struct { + GB_BASE ob; + QFont *font; + FONT_FUNC func; + void *object; + unsigned modified : 1; + enum { Name, Size, Grade, Bold, Italic, Underline, Strikeout }; + } + CFONT; + +//#define CFONT_NORMAL 0 +//#define CFONT_APPLICATION 1 +//#define CFONT_DRAW 2 + +CFONT *CFONT_create(const QFont &font, FONT_FUNC func = 0, void *object = 0); +void CFONT_set(FONT_FUNC func, void *font, void *object); +//CFONT *CFONT_create_control(CWIDGET *control); +double CFONT_size_real_to_virtual(double); +double CFONT_size_virtual_to_real(double); + +#define SIZE_REAL_TO_VIRTUAL(_size) CFONT_size_real_to_virtual((double)(_size)) +#define SIZE_VIRTUAL_TO_REAL(_size) CFONT_size_virtual_to_real((double)(_size)) + +#endif diff --git a/gb.qt4/src/CFrame.cpp b/gb.qt4/src/CFrame.cpp new file mode 100644 index 00000000..e58be37a --- /dev/null +++ b/gb.qt4/src/CFrame.cpp @@ -0,0 +1,118 @@ +/*************************************************************************** + + CFrame.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CFRAME_CPP + +#include +#include + +#include "gambas.h" + +#include "CFrame.h" + +//--------------------------------------------------------------------------- + +MyGroupBox::MyGroupBox(QWidget *parent) : QGroupBox(parent) +{ +} + +void MyGroupBox::changeEvent(QEvent *e) +{ + QGroupBox::changeEvent(e); + if (e->type() == QEvent::FontChange || e->type() == QEvent::StyleChange) + updateInside(); +} + +void MyGroupBox::updateInside() +{ + int f = qApp->style()->pixelMetric(QStyle::QStyle::PM_ComboBoxFrameWidth); + setContentsMargins(f, fontMetrics().height() * 3 / 2, f, f); +} + +// Warning! showEvent and hideEvent must be the same as in MyContainer class. + +void MyGroupBox::showEvent(QShowEvent *e) +{ + void *_object = CWidget::get(this); + QWidget::showEvent(e); + THIS->widget.flag.shown = TRUE; + CCONTAINER_arrange(THIS); +} + +void MyGroupBox::hideEvent(QHideEvent *e) +{ + void *_object = CWidget::get(this); + QWidget::hideEvent(e); + THIS->widget.flag.shown = FALSE; +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Frame_new, GB_OBJECT parent) + + MyGroupBox *wid = new MyGroupBox(QCONTAINER(VARG(parent))); + + THIS->container = wid; + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_PROPERTY(Frame_Text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->title()); + else + { + WIDGET->setTitle(QSTRING_PROP()); + WIDGET->updateInside(); + } + +END_PROPERTY + + +GB_DESC CFrameDesc[] = +{ + GB_DECLARE("Frame", sizeof(CFRAME)), GB_INHERITS("Container"), + + GB_METHOD("_new", NULL, Frame_new, "(Parent)Container;"), + + GB_PROPERTY("Caption", "s", Frame_Text), + GB_PROPERTY("Text", "s", Frame_Text), + GB_PROPERTY("Title", "s", Frame_Text), + + GB_PROPERTY("Arrangement", "i", Container_Arrangement), + GB_PROPERTY("AutoResize", "b", Container_AutoResize), + GB_PROPERTY("Spacing", "b", Container_Spacing), + GB_PROPERTY("Margin", "b", Container_Margin), + GB_PROPERTY("Padding", "i", Container_Padding), + GB_PROPERTY("Indent", "b", Container_Indent), + GB_PROPERTY("Invert", "b", Container_Invert), + + FRAME_DESCRIPTION, + + GB_END_DECLARE +}; + + diff --git a/gb.qt4/src/CFrame.h b/gb.qt4/src/CFrame.h new file mode 100644 index 00000000..1075ca99 --- /dev/null +++ b/gb.qt4/src/CFrame.h @@ -0,0 +1,68 @@ +/*************************************************************************** + + CFrame.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CFRAME_H +#define __CFRAME_H + +#include "gambas.h" + +#include "CWidget.h" +#include "CContainer.h" + +#include + +#ifndef __CFRAME_CPP +extern GB_DESC CFrameDesc[]; +#else + +#define THIS ((CFRAME *)_object) +#define WIDGET ((MyGroupBox *)((CWIDGET *)_object)->widget) + +#endif + +typedef + struct { + CWIDGET widget; + QWidget *container; + CARRANGEMENT arrangement; + } + CFRAME; + +class MyGroupBox : public QGroupBox +{ + Q_OBJECT + +public: + + MyGroupBox(QWidget *parent); + void updateInside(); + +protected: + + virtual void changeEvent(QEvent *e); + virtual void showEvent(QShowEvent *); + virtual void hideEvent(QHideEvent *); +}; + + +#endif diff --git a/gb.qt4/src/CImage.cpp b/gb.qt4/src/CImage.cpp new file mode 100644 index 00000000..6528fc06 --- /dev/null +++ b/gb.qt4/src/CImage.cpp @@ -0,0 +1,344 @@ +/*************************************************************************** + + CImage.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CIMAGE_CPP + +#include + +#include +#include +#include +#include +#include + +#ifdef OS_SOLARIS +/* Make math.h define M_PI and a few other things */ +#define __EXTENSIONS__ +/* Get definition for finite() */ +#include +#endif +#include + +#include "gambas.h" +#include "main.h" + +#include "CScreen.h" +#include "CPicture.h" +#include "CDraw.h" +#include "cpaint_impl.h" +#include "CImage.h" + +const char *CIMAGE_get_format(QString path) +{ + int pos; + + pos = path.lastIndexOf('.'); + if (pos < 0) + return NULL; + + path = path.mid(pos + 1).toLower(); + + if (path == "png") + return "PNG"; + else if (path == "jpg" || path == "jpeg") + return "JPEG"; + else if (path == "gif") + return "GIF"; + else if (path == "bmp") + return "BMP"; + else if (path == "xpm") + return "XPM"; + else + return NULL; +} + + +/******************************************************************************* + + Image + +*******************************************************************************/ + +static void free_image(GB_IMG *img, void *image) +{ + delete (QImage *)image; +} + +static void *temp_image(GB_IMG *img) +{ + QImage *image; + + if (!img->data) + image = new QImage(); + else + image = new QImage((uchar *)img->data, img->width, img->height, QImage::Format_ARGB32_Premultiplied); + + return image; +} + +static GB_IMG_OWNER _image_owner = { + QT_NAME, + GB_IMAGE_BGRP, + free_image, + free_image, + temp_image, + NULL, + }; + +QImage *CIMAGE_get(CIMAGE *_object) +{ + return (QImage *)IMAGE.Check(THIS_IMAGE, &_image_owner); +} + +#define check_image CIMAGE_get + +static void take_image(CIMAGE *_object, QImage *image) +{ + bool d = image->isDetached(); + //qDebug("take_image: %d x %d / %d [%c]", image->width(), image->height(), image->bytesPerLine(), image->isDetached() ? 'D' : '+'); + const uchar *data = ((const QImage *)image)->bits(); + //qDebug("take_image: [%c]", image->isDetached() ? 'D' : '+'); + if (image->isDetached() != d) + qDebug("image has been detached! %d x %d", image->width(), image->height()); + IMAGE.Take(THIS_IMAGE, &_image_owner, image, image->width(), image->height(), (uchar *)data); +} + +CIMAGE *CIMAGE_create(QImage *image) +{ + CIMAGE *img; + static GB_CLASS class_id = 0; + + if (!class_id) + class_id = GB.FindClass("Image"); + + img = (CIMAGE *)GB.New(class_id, NULL, NULL); + + if (image) + { + if (!image->isNull() && image->format() != QImage::Format_ARGB32_Premultiplied) + *image = image->convertToFormat(QImage::Format_ARGB32_Premultiplied); + take_image(img, image); + } + else + take_image(img, new QImage()); + + return img; +} + +BEGIN_PROPERTY(Image_Picture) + + CPICTURE *pict; + QImage img; + + check_image(THIS); + + pict = (CPICTURE *)GB.New(GB.FindClass("Picture"), NULL, NULL); + if (!QIMAGE->isNull()) + { + img = *QIMAGE; + img.detach(); + *pict->pixmap = QPixmap::fromImage(img); + } + + GB.ReturnObject(pict); + +END_PROPERTY + +BEGIN_METHOD(Image_Load, GB_STRING path) + + QImage *p; + CIMAGE *img; + + if (CPICTURE_load_image(&p, STRING(path), LENGTH(path))) + { + //p->convertToFormat(QImage::Format_ARGB32); + img = CIMAGE_create(p); + GB.ReturnObject(img); + } + else + GB.Error("Unable to load image"); + +END_METHOD + +BEGIN_METHOD(Image_FromString, GB_STRING data) + + QImage *p; + CIMAGE *img; + + if (CPICTURE_from_string(&p, STRING(data), LENGTH(data))) + { + //p->convertToFormat(QImage::Format_ARGB32); + img = CIMAGE_create(p); + GB.ReturnObject(img); + } + else + GB.Error("Unable to load image"); + +END_METHOD + +BEGIN_METHOD(Image_Save, GB_STRING path; GB_INTEGER quality) + + QString path = TO_QSTRING(GB.FileName(STRING(path), LENGTH(path))); + bool ok = false; + const char *fmt = CIMAGE_get_format(path); + + if (!fmt) + { + GB.Error("Unknown format"); + return; + } + + check_image(THIS); + + ok = QIMAGE->save(path, fmt, VARGOPT(quality, -1)); + + if (!ok) + GB.Error("Unable to save picture"); + +END_METHOD + +BEGIN_METHOD(Image_Stretch, GB_INTEGER width; GB_INTEGER height) + + //static int count = 0; + QImage *stretch; + int w, h; + + check_image(THIS); + + stretch = new QImage(); + + if (!QIMAGE->isNull()) + { + w = VARG(width); + h = VARG(height); + + if (w < 0 && h > 0) + w = QIMAGE->width() * h / QIMAGE->height(); + else if (h < 0 && w > 0) + h = QIMAGE->height() * w / QIMAGE->width(); + + if (w > 0 && h > 0) + { + *stretch = QIMAGE->scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + stretch->detach(); + } + } + + GB.ReturnObject(CIMAGE_create(stretch)); + +END_METHOD + + +BEGIN_METHOD(Image_Rotate, GB_FLOAT angle) + + QImage *rotate = new QImage(); + double angle = VARG(angle); + + check_image(THIS); + + if (angle != 0.0) + { + QMatrix mat; + mat.rotate(VARG(angle) * -360.0 / 2 / M_PI); + *rotate = QIMAGE->transformed(mat); + } + else + *rotate = QIMAGE->copy(); + + GB.ReturnObject(CIMAGE_create(rotate)); + +END_METHOD + + +BEGIN_METHOD(Image_PaintImage, GB_OBJECT img; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER sx; GB_INTEGER sy; GB_INTEGER sw; GB_INTEGER sh) + + int x, y, w, h, sx, sy, sw, sh; + CIMAGE *image = (CIMAGE *)VARG(img); + QImage *src, *dst; + double scale_x, scale_y; + + if (GB.CheckObject(image)) + return; + + src = check_image(image); + dst = check_image(THIS); + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + w = VARGOPT(w, -1); + h = VARGOPT(h, -1); + + sx = VARGOPT(sx, 0); + sy = VARGOPT(sy, 0); + sw = VARGOPT(sw, -1); + sh = VARGOPT(sh, -1); + + DRAW_NORMALIZE(x, y, w, h, sx, sy, sw, sh, src->width(), src->height()); + + if (w != sw || h != sh) + { + QImage tmp; + + scale_x = (double)w / sw; + scale_y = (double)h / sh; + + tmp = src->scaled((int)(src->width() * scale_x + 0.5), (int)(src->height() * scale_y + 0.5), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + sx = (int)(sx * scale_x + 0.5); + sy = (int)(sy * scale_y + 0.5); + sw = w; + sh = h; + + QPainter p(dst); + p.drawImage(x, y, tmp, sx, sy, sw, sh); + p.end(); + } + else + { + QPainter p(dst); + p.drawImage(x, y, *src, sx, sy, sw, sh); + p.end(); + } + +END_METHOD + +GB_DESC CImageDesc[] = +{ + GB_DECLARE("Image", sizeof(CIMAGE)), + + GB_STATIC_METHOD("Load", "Image", Image_Load, "(Path)s"), + GB_STATIC_METHOD("FromString", "Image", Image_FromString, "(Data)s"), + GB_METHOD("Save", NULL, Image_Save, "(Path)s[(Quality)i]"), + + GB_METHOD("Stretch", "Image", Image_Stretch, "(Width)i(Height)i"), + GB_METHOD("Rotate", "Image", Image_Rotate, "(Angle)f"), + + GB_METHOD("PaintImage", NULL, Image_PaintImage, "(Image)Image;(X)i(Y)i[(Width)i(Height)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + + GB_PROPERTY_READ("Picture", "Picture", Image_Picture), + + GB_INTERFACE("Paint", &PAINT_Interface), + GB_INTERFACE("PaintMatrix", &PAINT_MATRIX_Interface), + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/CImage.h b/gb.qt4/src/CImage.h new file mode 100644 index 00000000..bdbf310c --- /dev/null +++ b/gb.qt4/src/CImage.h @@ -0,0 +1,56 @@ +/*************************************************************************** + + CImage.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CIMAGE_H +#define __CIMAGE_H + +#include +#include + +#include "gambas.h" +#include "gb.image.h" + +typedef + struct { + GB_IMG img; + } + CIMAGE; + +#ifndef __CIMAGE_CPP + +extern GB_DESC CImageDesc[]; + +#else + +#define THIS OBJECT(CIMAGE) +#define THIS_IMAGE (&THIS->img) +#define QIMAGE ((QImage *)THIS_IMAGE->temp_handle) +#define GET_QIMAGE(_image) ((QImage *)(_image->img.temp_handle)) + +#endif + +const char *CIMAGE_get_format(QString path); +CIMAGE *CIMAGE_create(QImage *image); +QImage *CIMAGE_get(CIMAGE *); + +#endif diff --git a/gb.qt4/src/CKey.cpp b/gb.qt4/src/CKey.cpp new file mode 100644 index 00000000..b3849cb9 --- /dev/null +++ b/gb.qt4/src/CKey.cpp @@ -0,0 +1,222 @@ +/*************************************************************************** + + CKey.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CKEY_CPP + + +#include "gambas.h" + +#include "main.h" +#include "CKey.h" + +CKEY_INFO CKEY_info = { 0 }; + +void CKEY_clear(int valid) +{ + if (valid) + CKEY_info.valid++; + else + CKEY_info.valid--; + + if (CKEY_info.valid == 0) + { + GB.FreeString(&CKEY_info.text); + CKEY_info = { 0 }; + } +} + +BEGIN_METHOD_VOID(CKEY_exit) + + GB.FreeString(&CKEY_info.text); + +END_METHOD + + +BEGIN_METHOD(CKEY_get, GB_STRING key) + + char *str = GB.ToZeroString(ARG(key)); + QKeySequence ks(str); + +#if QT_VERSION <= 0x030005 + GB.ReturnInteger(ks); +#else + GB.ReturnInteger(ks[0] & ~Qt::UNICODE_ACCEL); +#endif + +END_METHOD + +#define CHECK_VALID() \ + if (!CKEY_is_valid()) \ + { \ + GB.Error("No keyboard event data"); \ + return; \ + } + +BEGIN_PROPERTY(Key_Text) + + CHECK_VALID(); + GB.ReturnString(CKEY_info.text); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Code) + + CHECK_VALID(); + + /*switch(CKEY_info.code) + { + case Qt::Key_Shift: + case Qt::Key_Control: + case Qt::Key_Alt: + case Qt::Key_Meta: + GB.ReturnInteger(0); + break; + + default:*/ + GB.ReturnInteger(CKEY_info.code); + //} + +END_PROPERTY + +BEGIN_PROPERTY(Key_State) + + CHECK_VALID(); + GB.ReturnInteger(CKEY_info.state); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Shift) + + CHECK_VALID(); + GB.ReturnBoolean(CKEY_info.state & Qt::ShiftModifier); // || (CKEY_info.code == Qt::Key_Shift)); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Control) + + CHECK_VALID(); + GB.ReturnBoolean(CKEY_info.state & Qt::ControlModifier); // || (CKEY_info.code == Qt::Key_Control)); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Alt) + + CHECK_VALID(); + GB.ReturnBoolean(CKEY_info.state & Qt::AltModifier); // || (CKEY_info.code == Qt::Key_Alt)); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Meta) + + CHECK_VALID(); + GB.ReturnBoolean(CKEY_info.state & Qt::MetaModifier); // || (CKEY_info.code == Qt::Key_Meta)); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Normal) + + CHECK_VALID(); + GB.ReturnBoolean((CKEY_info.state & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier)) == 0); + +END_PROPERTY + + +GB_DESC CKeyDesc[] = +{ + GB_DECLARE("Key", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_get", "i", CKEY_get, "(Key)s"), + GB_STATIC_METHOD("_exit", NULL, CKEY_exit, NULL), + + GB_CONSTANT("Esc", "i", Qt::Key_Escape), + GB_CONSTANT("Escape", "i", Qt::Key_Escape), + GB_CONSTANT("Tab", "i", Qt::Key_Tab), + GB_CONSTANT("BackTab", "i", Qt::Key_Backtab), + GB_CONSTANT("BackSpace", "i", Qt::Key_Backspace), + GB_CONSTANT("Return", "i", Qt::Key_Return), + GB_CONSTANT("Enter", "i", Qt::Key_Enter), + GB_CONSTANT("Ins", "i", Qt::Key_Insert), + GB_CONSTANT("Del", "i", Qt::Key_Delete), + GB_CONSTANT("Insert", "i", Qt::Key_Insert), + GB_CONSTANT("Delete", "i", Qt::Key_Delete), + GB_CONSTANT("Pause", "i", Qt::Key_Pause), + GB_CONSTANT("Print", "i", Qt::Key_Print), + GB_CONSTANT("SysReq", "i", Qt::Key_SysReq), + GB_CONSTANT("Home", "i", Qt::Key_Home), + GB_CONSTANT("End", "i", Qt::Key_End), + GB_CONSTANT("Left", "i", Qt::Key_Left), + GB_CONSTANT("Up", "i", Qt::Key_Up), + GB_CONSTANT("Right", "i", Qt::Key_Right), + GB_CONSTANT("Down", "i", Qt::Key_Down), + GB_CONSTANT("PgUp", "i", Qt::Key_PageUp), + GB_CONSTANT("PgDown", "i", Qt::Key_PageDown), + GB_CONSTANT("PageUp", "i", Qt::Key_PageUp), + GB_CONSTANT("PageDown", "i", Qt::Key_PageDown), + GB_CONSTANT("ShiftKey", "i", Qt::Key_Shift), + GB_CONSTANT("ControlKey", "i", Qt::Key_Control), + GB_CONSTANT("MetaKey", "i", Qt::Key_Meta), + GB_CONSTANT("AltKey", "i", Qt::Key_Alt), + GB_CONSTANT("AltGrKey", "i", Qt::Key_AltGr), + GB_CONSTANT("CapsLock", "i", Qt::Key_CapsLock), + GB_CONSTANT("NumLock", "i", Qt::Key_NumLock), + GB_CONSTANT("ScrollLock", "i", Qt::Key_ScrollLock), + GB_CONSTANT("F1", "i", Qt::Key_F1), + GB_CONSTANT("F2", "i", Qt::Key_F2), + GB_CONSTANT("F3", "i", Qt::Key_F3), + GB_CONSTANT("F4", "i", Qt::Key_F4), + GB_CONSTANT("F5", "i", Qt::Key_F5), + GB_CONSTANT("F6", "i", Qt::Key_F6), + GB_CONSTANT("F7", "i", Qt::Key_F7), + GB_CONSTANT("F8", "i", Qt::Key_F8), + GB_CONSTANT("F9", "i", Qt::Key_F9), + GB_CONSTANT("F10", "i", Qt::Key_F10), + GB_CONSTANT("F11", "i", Qt::Key_F11), + GB_CONSTANT("F12", "i", Qt::Key_F12), + GB_CONSTANT("F13", "i", Qt::Key_F13), + GB_CONSTANT("F14", "i", Qt::Key_F14), + GB_CONSTANT("F15", "i", Qt::Key_F15), + GB_CONSTANT("F16", "i", Qt::Key_F16), + GB_CONSTANT("F17", "i", Qt::Key_F17), + GB_CONSTANT("F18", "i", Qt::Key_F18), + GB_CONSTANT("F19", "i", Qt::Key_F19), + GB_CONSTANT("F20", "i", Qt::Key_F20), + GB_CONSTANT("F21", "i", Qt::Key_F21), + GB_CONSTANT("F22", "i", Qt::Key_F22), + GB_CONSTANT("F23", "i", Qt::Key_F23), + GB_CONSTANT("F24", "i", Qt::Key_F24), + GB_CONSTANT("Menu", "i", Qt::Key_Menu), + GB_CONSTANT("Help", "i", Qt::Key_Help), + GB_CONSTANT("Space", "i", Qt::Key_Space), + + GB_STATIC_PROPERTY_READ("Text", "s", Key_Text), + GB_STATIC_PROPERTY_READ("Code", "i", Key_Code), + GB_STATIC_PROPERTY_READ("State", "i", Key_State), + GB_STATIC_PROPERTY_READ("Shift", "b", Key_Shift), + GB_STATIC_PROPERTY_READ("Control", "b", Key_Control), + GB_STATIC_PROPERTY_READ("Alt", "b", Key_Alt), + GB_STATIC_PROPERTY_READ("Meta", "b", Key_Meta), + GB_STATIC_PROPERTY_READ("Normal", "b", Key_Normal), + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/CKey.h b/gb.qt4/src/CKey.h new file mode 100644 index 00000000..44c553bb --- /dev/null +++ b/gb.qt4/src/CKey.h @@ -0,0 +1,51 @@ +/*************************************************************************** + + CKey.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CKEY_H +#define __CKEY_H + +#include "gambas.h" + +#include + +typedef + struct { + int valid; + char *text; + int code; + Qt::KeyboardModifiers state; + int cancel; + bool release; + } + CKEY_INFO; + +#ifndef __CKEY_CPP +extern GB_DESC CKeyDesc[]; +extern CKEY_INFO CKEY_info; +#endif + +void CKEY_clear(int valid); + +#define CKEY_is_valid() (CKEY_info.valid != 0) + +#endif diff --git a/gb.qt4/src/CLabel.cpp b/gb.qt4/src/CLabel.cpp new file mode 100644 index 00000000..1e7d6bfb --- /dev/null +++ b/gb.qt4/src/CLabel.cpp @@ -0,0 +1,445 @@ +/*************************************************************************** + + CLabel.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CLABEL_CPP + +#include +#include +#include +#include +#include +//Added by qt3to4: +#include +#include +#include +#include +#include + +#include "gambas.h" + +#include "CConst.h" +#include "CColor.h" +#include "CLabel.h" + +/*#define DEBUG_CBUTTON*/ + + +/***************************************************************************/ + +BEGIN_METHOD(Label_new, GB_OBJECT parent) + + QLabel *wid = new MyLabel(QCONTAINER(VARG(parent))); + + wid->setTextFormat(Qt::PlainText); + wid->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); // + Qt::WordBreak); + //THIS->widget.flag.fillBackground = TRUE; + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_METHOD(TextLabel_new, GB_OBJECT parent) + + MyLabel *wid = new MyLabel(QCONTAINER(VARG(parent))); + + wid->setTextFormat(Qt::RichText); + wid->setAlignment(Qt::AlignLeft | Qt::AlignTop); + wid->setWordWrap(true); + //THIS->widget.flag.fillBackground = TRUE; + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_PROPERTY(Label_Text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->text()); + else + WIDGET->setText(QSTRING_PROP()); + +END_PROPERTY + + +BEGIN_PROPERTY(Label_Alignment) + + if (READ_PROPERTY) + GB.ReturnInteger(CCONST_alignment(WIDGET->alignment() & ALIGN_MASK, ALIGN_NORMAL, false)); + else + WIDGET->setAlignment((Qt::Alignment)CCONST_alignment(VPROP(GB_INTEGER), ALIGN_NORMAL, true)); + +END_PROPERTY + + +BEGIN_PROPERTY(Label_AutoResize) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->getAutoResize()); + else + WIDGET->setAutoResize(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Label_Padding) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->margin()); + else + { + WIDGET->setMargin(VPROP(GB_INTEGER)); + WIDGET->calcMinimumHeight(); + WIDGET->update(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(TextLabel_Alignment) + + if (READ_PROPERTY) + GB.ReturnInteger(CCONST_alignment(WIDGET->alignment() & ALIGN_MASK, ALIGN_NORMAL, false)); + else + WIDGET->setAlignment((Qt::Alignment)CCONST_alignment(VPROP(GB_INTEGER), ALIGN_NORMAL, true)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Label_Adjust) + + WIDGET->adjust(); + +END_METHOD + +BEGIN_PROPERTY(Label_Transparent) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->transparent); + else + THIS->transparent = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(Label_Wrap) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->wordWrap()); + else + WIDGET->setWordWrap(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Label_Border) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->border()); + else + { + WIDGET->setBorder(VPROP(GB_INTEGER)); + WIDGET->calcMinimumHeight(); + } + +END_PROPERTY + +//--------------------------------------------------------------------------- + +GB_DESC CLabelDesc[] = +{ + GB_DECLARE("Label", sizeof(CLABEL)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, Label_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", Label_Text), + GB_PROPERTY("Caption", "s", Label_Text), + GB_PROPERTY("Alignment", "i", Label_Alignment), + GB_PROPERTY("Border", "i", Label_Border), + GB_PROPERTY("AutoResize", "b", Label_AutoResize), + GB_PROPERTY("Padding", "i", Label_Padding), + GB_PROPERTY("Transparent", "b", Label_Transparent), + GB_METHOD("Adjust", NULL, Label_Adjust, NULL), + + LABEL_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CTextLabelDesc[] = +{ + GB_DECLARE("TextLabel", sizeof(CLABEL)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, TextLabel_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", Label_Text), + GB_PROPERTY("Caption", "s", Label_Text), + GB_PROPERTY("Alignment", "i", TextLabel_Alignment), + GB_PROPERTY("Border", "i", Label_Border), + GB_PROPERTY("AutoResize", "b", Label_AutoResize), + GB_PROPERTY("Padding", "i", Label_Padding), + GB_PROPERTY("Transparent", "b", Label_Transparent), + GB_PROPERTY("Wrap", "b", Label_Wrap), + GB_METHOD("Adjust", NULL, Label_Adjust, NULL), + + TEXTLABEL_DESCRIPTION, + + GB_END_DECLARE +}; + + +/***************************************************************************/ + +BEGIN_METHOD(CSEPARATOR_new, GB_OBJECT parent) + + MySeparator *wid = new MySeparator(QCONTAINER(VARG(parent))); + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + +GB_DESC CSeparatorDesc[] = +{ + GB_DECLARE("Separator", sizeof(CSEPARATOR)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CSEPARATOR_new, "(Parent)Container;"), + + SEPARATOR_DESCRIPTION, + + GB_END_DECLARE +}; + + +/** MyLabel *****************************************************************/ + +MyLabel::MyLabel(QWidget *parent) : QLabel(parent) +{ + autoResize = false; + locked = false; + _border = BORDER_NONE; + setIndent(0); + calcMinimumHeight(); +} + + +void MyLabel::changeEvent(QEvent *e) +{ + QLabel::changeEvent(e); + if (e->type() == QEvent::FontChange || e->type() == QEvent::StyleChange) + calcMinimumHeight(); +} + +void MyLabel::setText(const QString &text) +{ + QLabel::setText(text); + calcMinimumHeight(); + //qDebug("%s: %d", text.latin1(), isVisible()); +} + +void MyLabel::calcMinimumHeight(bool adjust) +{ + void *_object = CWidget::getReal(this); + + if (!THIS || (!autoResize && !adjust) || CWIDGET_test_flag(THIS, WF_DESIGN) || text().length() <= 0) + return; + + //qDebug("calcMinimumHeight: %p %s", ob, ((CWIDGET *)ob)->name); + + int w, h, nw, nh; + int f = contentsMargins().left() + margin(); + QRect br; + + if (textFormat() == Qt::RichText) + { + QTextDocument doc; + + doc.setDefaultFont(font()); + doc.setDocumentMargin(0); + doc.setHtml(text()); + + if (wordWrap()) + { + w = width() - f * 2; + doc.setTextWidth(w); + + if (adjust) + { + nw = doc.idealWidth(); + doc.setTextWidth(nw); + nh = doc.size().height(); + nw = doc.size().width(); + + } + else + { + nh = doc.size().height(); + nw = w; + } + } + else + { + nh = doc.size().height(); + nw = doc.size().width(); + } + + //nw += 2; // Why? Don't know... + //nh += 2; + } + else + { + QFontMetrics fm = fontMetrics(); + br = fm.boundingRect(0, 0, QWIDGETSIZE_MAX, QWIDGETSIZE_MAX, alignment(), text()); + nw = br.width(); + nh = br.height(); + } + + w = nw + f * 2; + h = nh + f * 2; + + int a = CCONST_alignment(WIDGET->alignment() & ALIGN_MASK, ALIGN_NORMAL, false); + if ((a == ALIGN_CENTER || a == ALIGN_LEFT || a == ALIGN_NORMAL || a == ALIGN_RIGHT) && nh < height()) + nh = height(); + + locked = true; + CWIDGET_resize(THIS, w, h); + locked = false; +} + +#if 0 +void MyLabel::calcMinimumHeight(bool adjust) +{ + void *ob = CWidget::get(this); + int w, h, nw, nh; + + if (!adjust && (!autoResize || CWIDGET_test_flag(ob, WF_DESIGN) || text().length() <= 0)) + { + setMinimumSize(0, 0); + setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + } + else + { + int f = frameWidth(); + QRect br; + + if (f > 0 && f < 4) + f = 4; + + if (textFormat() == Qt::RichText) + { + QTextDocument doc; + + doc.setHtml(text()); + doc.setDefaultFont(font()); + + w = width() - f * 2; + + doc.setTextWidth(w); + nh = doc.size().height(); + nw = adjust ? doc.idealWidth() : w; + } + else + { + QFontMetrics fm = fontMetrics(); + br = fm.boundingRect(0, 0, QWIDGETSIZE_MAX, QWIDGETSIZE_MAX, alignment(), text()); + nw = br.width(); + nh = br.height(); + if (alignment() & Qt::AlignVCenter && (nh + f * 2) < height()) + nh = height() - f * 2; + } + + w = nw + f * 2; + h = nh + f * 2; + + + //setMinimumSize(w, h); + //setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + + //if (textFormat() != Qt::RichText && h < height()) + // h = height(); + if (!noresize) + { + locked = true; + //qDebug("%p: resize(%d, %d)", this, w, h); + resize(w, h); + locked = false; + } + } +} +#endif + +void MyLabel::resizeEvent(QResizeEvent *e) +{ + QLabel::resizeEvent(e); + + if (autoResize && !locked && textFormat() == Qt::RichText && e->oldSize().width() != e->size().width()) + calcMinimumHeight(); +} + +void MyLabel::adjust() +{ + calcMinimumHeight(true); +} + +void MyLabel::paintEvent(QPaintEvent *e) +{ + QPainter p(this); + CCONTAINER_draw_border(&p, _border, this); + QLabel::paintEvent(e); +} + +/** class MySeparator ******************************************************/ + + +MySeparator::MySeparator(QWidget *parent) +: QWidget(parent) +{ +} + +void MySeparator::paintEvent( QPaintEvent * ) +{ + QPainter p(this); + + if (width() == 1 || height() == 1) + { + void *_object = CWidget::getReal(this); + uint color = CWIDGET_get_foreground(&THIS->widget); + p.setPen(color == COLOR_DEFAULT ? CCOLOR_light_foreground() : TO_QCOLOR(color)); + if (width() >= height()) + p.drawLine(0, height() / 2, width() - 1, height() / 2); + else + p.drawLine(width() / 2, 0, width() / 2, height() - 1); + } + else + { + QStyleOption opt; + + opt.rect = rect(); + opt.palette = palette(); + opt.state |= QStyle::State_Enabled; + + if (width() < height()) + opt.state |= QStyle::State_Horizontal; + + style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, &p); + } +} + diff --git a/gb.qt4/src/CLabel.h b/gb.qt4/src/CLabel.h new file mode 100644 index 00000000..0ebc5d31 --- /dev/null +++ b/gb.qt4/src/CLabel.h @@ -0,0 +1,106 @@ +/*************************************************************************** + + CLabel.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CLABEL_H +#define __CLABEL_H + +#include "gambas.h" + +#include +#include +#include +#include + +#include "CWidget.h" +#include "CPicture.h" +#include "CContainer.h" + +#ifndef __CLABEL_CPP +extern GB_DESC CLabelDesc[]; +extern GB_DESC CTextLabelDesc[]; +extern GB_DESC CSeparatorDesc[]; +#else + +#define QLABEL(object) ((QLabel *)((CWIDGET *)object)->widget) + +#define THIS ((CLABEL *)_object) +#define WIDGET ((MyLabel *)((CWIDGET *)_object)->widget) +#define SEPARATOR ((MySeparator *)((CWIDGET *)_object)->widget) + +#endif + +typedef + struct { + CWIDGET widget; + bool transparent; + } + CLABEL; + +typedef + struct { + CWIDGET widget; + } + CSEPARATOR; + +class MyLabel : public QLabel +{ + Q_OBJECT + +public: + + MyLabel(QWidget *parent); + void calcMinimumHeight(bool adjust = false); + bool getAutoResize() { return autoResize; } + void setAutoResize(bool a) { autoResize = a; calcMinimumHeight(); } + virtual void setText(const QString &); + void adjust(); + int border() const { return _border; } + void setBorder(int border) { CCONTAINER_set_border(&_border, border, this); } + +protected: + + virtual void changeEvent(QEvent *); + virtual void resizeEvent(QResizeEvent *); + virtual void paintEvent(QPaintEvent *); + +private: + + unsigned autoResize : 1; + unsigned locked : 1; + char _border; +}; + +class MySeparator : public QWidget +{ + Q_OBJECT + +public: + + MySeparator(QWidget *); + +protected: + + void paintEvent(QPaintEvent *); +}; + +#endif diff --git a/gb.qt4/src/CMenu.cpp b/gb.qt4/src/CMenu.cpp new file mode 100644 index 00000000..bec54615 --- /dev/null +++ b/gb.qt4/src/CMenu.cpp @@ -0,0 +1,1220 @@ +/*************************************************************************** + + CMenu.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CMENU_CPP + +#include +#include +#include +#include + +#include "gambas.h" +#include "gb_common.h" + +#include "CWidget.h" +#include "CWindow.h" +#include "CMenu.h" + +//#define DEBUG_MENU 1 + +DECLARE_EVENT(EVENT_Click); +DECLARE_EVENT(EVENT_Show); +DECLARE_EVENT(EVENT_Hide); + +DECLARE_METHOD(Control_Window); +DECLARE_METHOD(Control_Name); +DECLARE_METHOD(Menu_Hide); + +static bool _popup_immediate = false; +static CMENU *_popup_menu_clicked = NULL; +int MENU_popup_count = 0; + +static void clear_menu(CMENU *_object); + +static GB_FUNCTION _init_shortcut_func; + + +#define EXT(_ob) ((CMENU_EXT *)((CWIDGET *)_ob)->ext) +#define ENSURE_EXT(_ob) (EXT(_ob) ? EXT(_ob) : alloc_ext((CMENU *)(_ob))) + +#define HANDLE_PROXY(_ob) \ + while (EXT(_ob) && EXT(_ob)->proxy) \ + _ob = (__typeof__ _ob)(EXT(_ob)->proxy); + +static CMENU_EXT *alloc_ext(CMENU *_object) +{ + GB.Alloc(POINTER(&(THIS->widget.ext)), sizeof(CMENU_EXT)); + THIS_EXT->proxy = NULL; + THIS_EXT->action = NULL; + THIS_EXT->tag.type = GB_T_NULL; + return THIS_EXT; +} + + +static void register_proxy(void *_object, CMENU *proxy) +{ + void *check = proxy; + CMENU *old_proxy = NULL; + + while (check) + { + if (check == THIS) + { + GB.Error("Circular proxy chain"); + return; + } + check = EXT(check) ? EXT(check)->proxy : NULL; + } + + //if (THIS_EXT && THIS_EXT->proxy && EXT(THIS_EXT->proxy)) + // EXT(THIS_EXT->proxy)->proxy_for = NULL; + + if (THIS_EXT && THIS_EXT->proxy) + { + old_proxy = (CMENU *)THIS_EXT->proxy; + THIS_EXT->proxy = NULL; + } + + if (proxy) + { + GB.Ref(proxy); + ENSURE_EXT(THIS)->proxy = proxy; + } + + if (ACTION) + { + if (!proxy || !proxy->menu) + ACTION->setMenu(THIS->menu); + else + ACTION->setMenu(proxy->menu); + + if (old_proxy) + ((QAction *)old_proxy->widget.widget)->setMenu(old_proxy->menu); + } + + if (old_proxy) + GB.Unref(POINTER(&old_proxy)); +} + +static int check_menu(void *_object) +{ + return THIS->deleted || ACTION == 0; +} + +static void refresh_menubar(CMENU *menu) +{ + int i; + QList list; + QAction *action; + MyMainWindow *toplevel; + CWINDOW *window; + QMenuBar *menuBar; + + if (!CMENU_is_toplevel(menu)) + return; + + toplevel = (MyMainWindow *)(menu->toplevel); + window = ((CWINDOW *)(menu->parent)); + menuBar = window->menuBar; + if (!menuBar) + return; + + list = menuBar->actions(); + + for (i = 0; i < list.count(); i++) + { + action = list.at(i); + menu = CMenu::dict[action]; + if (!menu || menu->deleted) + continue; + if (action->isVisible() && !action->isSeparator()) + break; + } + + window->hideMenuBar = i == list.count(); + + toplevel->configure(); +} + +static void unregister_menu(CMENU *_object) +{ + if (THIS_EXT && THIS_EXT->action) + { + CACTION_register((CWIDGET *)THIS, THIS_EXT->action, NULL); + GB.FreeString(&THIS_EXT->action); + } +} + +static void set_menu_visible(void *_object, bool v) +{ + THIS->visible = v; + ACTION->setVisible(v); + refresh_menubar(THIS); + //update_accel_recursive(THIS); +} + +static void delete_menu(CMENU *_object) +{ + if (THIS->deleted) + return; + + //qDebug("delete_menu: %s %p action %p", THIS->widget.name, THIS, ACTION); + + THIS->deleted = true; + + register_proxy(THIS, NULL); + + clear_menu(THIS); + + if (THIS->menu) + { + //GB.Post((GB_CALLBACK)delete_later, (intptr_t)THIS->menu); + THIS->menu->deleteLater(); + THIS->menu = 0; + } + + if (THIS->accel) + delete THIS->accel; + + if (ACTION) + { + refresh_menubar(THIS); + delete ACTION; + } + +// if (ACTION) +// { +// QAction *action = ACTION; +// THIS->widget.widget = 0; +// delete action; +// } +} + +static void clear_menu(CMENU *_object) +{ + int i; + CMENU *menu; + + if (THIS->menu) + { + QList list = THIS->menu->actions(); + + for (i = 0; i < list.count(); i++) + { + menu = CMenu::dict[list.at(i)]; + //GB.Ref(menu); + //delete ((QAction *)(menu->widget.widget)); + if (menu) + delete_menu(menu); + //GB.Unref(POINTER(&menu)); + } + + THIS->init_shortcut = FALSE; + } +} + +static bool is_fully_enabled(CMENU *_object) +{ + for(;;) + { + if (THIS->exec) + return true; + + if (THIS->disabled) + return false; + + if (CMENU_is_toplevel(THIS)) + return true; + + _object = (CMENU *)THIS->parent; + } +} + +static void update_accel(CMENU *_object) +{ + if (CMENU_is_toplevel(THIS)) + return; + + if (THIS->accel && !THIS->accel->isEmpty() && is_fully_enabled(THIS)) + { + //if (!qstrcmp(THIS->widget.name, "mnuCopy")) + // qDebug("update_accel: %s: %s", THIS->widget.name, (const char *)THIS->accel->toString().toUtf8()); + ACTION->setShortcut(*(THIS->accel)); + } + else + { + //if (!qstrcmp(THIS->widget.name, "mnuCopy")) + // qDebug("update_accel: %s: NULL", THIS->widget.name); + ACTION->setShortcut(QKeySequence()); + } +} + +static void update_accel_recursive(CMENU *_object) +{ + if (THIS->exec) + return; + + update_accel(THIS); + + if (THIS->menu) + { + int i; + + for (i = 0; i < THIS->menu->actions().count(); i++) + update_accel_recursive(CMenu::dict[THIS->menu->actions().at(i)]); + } +} + +static void update_check(CMENU *_object) +{ + if (THIS->checked || THIS->toggle || THIS->radio) + { + ACTION->setCheckable(true); + ACTION->setChecked(THIS->checked); + } + else + { + ACTION->setCheckable(false); + ACTION->setChecked(false); + } +} + +#if 0 +static void toggle_menu(CMENU *_object) +{ + if (CMENU_is_toplevel(THIS)) + return; + + qDebug("toggle_menu: %s %d", TO_UTF8(ACTION->text()), ACTION->isChecked()); + + //ACTION->setCheckable(true); + ACTION->setChecked(!ACTION->isChecked()); + //ACTION->setCheckable(false); + + qDebug("--> %d", ACTION->isChecked()); +} +#endif + +static void update_action_group(QMenu *parent) +{ + int i; + QAction *action; + CMENU *menu; + QActionGroup *group = NULL; + + for (i = 0; i < parent->actions().count(); i++) + { + action = parent->actions().at(i); + menu = CMenu::dict[action]; + if (!menu || menu->deleted) + continue; + + if (menu->radio) + { + if (!group) + { + if (action->actionGroup()) + group = action->actionGroup(); + else + group = new QActionGroup(parent); + } + action->setActionGroup(group); + } + else + { + group = NULL; + action->setActionGroup(NULL); + } + + //qDebug("action '%s' -> %p", TO_UTF8(action->text()), (void *)group); + } +} + +//--------------------------------------------------------------------------- + +MyAction::MyAction(QObject *parent): QAction(parent) +{ +} + +bool MyAction::event(QEvent *e) +{ + if (e->type() == QEvent::Shortcut) + { + activate(Trigger); + return true; + } + + return QAction::event(e); +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Menu_new, GB_OBJECT parent; GB_BOOLEAN hidden) + + QAction *action; + void *parent = VARG(parent); + QWidget *topLevel = 0; + QMenuBar *menuBar = 0; + + //printf("Menu_new %p\n", _object); + + if (GB.CheckObject(parent)) + return; + + //qDebug("Menu_new: (%s %p)", GB.GetClassName(THIS), THIS); + + if (GB.Is(parent, CLASS_Menu)) + { + CMENU *menu = (CMENU *)parent; + + topLevel = menu->toplevel; + + if (!menu->menu) + { +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) && QT_VERSION < QT_VERSION_CHECK(5,5,0) + menu->menu = new MyMenu(); +#else + menu->menu = new QMenu(); +#endif + menu->menu->setSeparatorsCollapsible(true); + ((QAction *)(menu->widget.widget))->setMenu(menu->menu); + + //QObject::connect(menu->menu, SIGNAL(triggered(QAction *)), &CMenu::manager, SLOT(slotTriggered(QAction *))); + QObject::connect(menu->menu, SIGNAL(aboutToShow()), &CMenu::manager, SLOT(slotShown())); + QObject::connect(menu->menu, SIGNAL(aboutToHide()), &CMenu::manager, SLOT(slotHidden())); + } + + action = new MyAction(menu->menu); + action->setSeparator(true); + QObject::connect(action, SIGNAL(toggled(bool)), &CMenu::manager, SLOT(slotToggled(bool))); + QObject::connect(action, SIGNAL(destroyed()), &CMenu::manager, SLOT(slotDestroyed())); + QObject::connect(action, SIGNAL(triggered()), &CMenu::manager, SLOT(slotTriggered())); + + menu->menu->addAction(action); + //qDebug("New action %p for Menu %p", action, THIS); + } + else if (GB.Is(parent, CLASS_Window)) + { + CWINDOW *window = (CWINDOW *)parent; + + topLevel = QWIDGET(CWidget::getWindow((CWIDGET *)parent)); + menuBar = window->menuBar; + if (!menuBar) + { + menuBar = new QMenuBar(topLevel); + //menuBar->setAutoFillBackground(true); + window->menuBar = menuBar; + } + + action = new MyAction(menuBar); + menuBar->addAction(action); + + action->setSeparator(true); + QObject::connect(action, SIGNAL(destroyed()), &CMenu::manager, SLOT(slotDestroyed())); + + //qDebug("New action %p for top level Menu %p", action, THIS); + } + else + { + GB.Error("Type mismatch. The parent control of a Menu must be a Window or another Menu."); + return; + } + + THIS->widget.widget = (QWidget *)action; + CMenu::dict.insert(action, THIS); + set_menu_visible(THIS, !VARGOPT(hidden, FALSE)); + + THIS->parent = parent; + THIS->widget.name = NULL; + THIS->picture = NULL; + THIS->deleted = false; + + CWIDGET_init_name((CWIDGET *)THIS); + +#ifdef DEBUG_MENU + //qDebug("Menu_new: item = %p (%d) parent = %p (%d) toplevel = %p", item, item->id, item->parent, item->parent ? item->parent->id : 0, item->toplevel); +#endif + + THIS->toplevel = topLevel; + refresh_menubar(THIS); + //qDebug("*** Menu_new %p", _object); + GB.Ref(THIS); + + //qDebug("Menu_new: (%s %p)", THIS->widget.name, THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(Menu_free) + +#ifdef DEBUG_MENU + qDebug("Menu_free: item = %p", THIS); +#endif + + //qDebug("Menu_free: (%s %p)", THIS->widget.name, THIS); + + delete_menu(THIS); + + GB.StoreObject(NULL, POINTER(&(THIS->picture))); + + if (THIS_EXT) + { + //CACTION_register(THIS, THIS_EXT->action, NULL); + //GB.FreeString(&THIS_EXT->action); + + /*if (THIS_EXT->proxy) + EXT(THIS_EXT->proxy)->proxy_for = NULL; + if (THIS_EXT->proxy_for) + EXT(THIS_EXT->proxy_for)->proxy = NULL;*/ + + GB.StoreVariant(NULL, &THIS_EXT->tag); + GB.FreeString(&THIS_EXT->action); + + GB.Free(POINTER(&THIS->widget.ext)); + } + + //qDebug("free_name: %s %p (Menu_free)", THIS->widget.name, THIS->widget.name); + //if (!strcmp(THIS->widget.name, "mnuCut")) + // BREAKPOINT(); + GB.FreeString(&THIS->widget.name); + GB.FreeString(&THIS->save_text); + + #ifdef DEBUG_MENU + qDebug("Menu_free: item = %p is freed!", THIS); + #endif + +END_METHOD + + +BEGIN_PROPERTY(Menu_Text) + + if (READ_PROPERTY) + { + if (THIS->save_text) + GB.ReturnString(THIS->save_text); + else + RETURN_NEW_STRING(ACTION->text()); + } + else + { + QString text = QSTRING_PROP(); + ACTION->setText(text); + ACTION->setSeparator(text.isEmpty()); + refresh_menubar(THIS); + + if (!CMENU_is_toplevel(THIS)) + ((CMENU *)THIS->parent)->init_shortcut = FALSE; + + GB.FreeString(&THIS->save_text); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Picture) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->picture); + else + { + QIcon icon; + + GB.StoreObject(PROP(GB_OBJECT), POINTER(&(THIS->picture))); + if (THIS->picture) + icon = QIcon(*THIS->picture->pixmap); + ACTION->setIcon(icon); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Enabled) + + if (READ_PROPERTY) + GB.ReturnBoolean(!THIS->disabled); + else + { + bool b = VPROP(GB_BOOLEAN); + THIS->disabled = !b; + ACTION->setEnabled(b); + //CMenu::enableAccel(THIS, b && !THIS->noshortcut); + update_accel_recursive(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(Menu_Checked) + + if (CMENU_is_toplevel(THIS)) + { + if (READ_PROPERTY) + GB.ReturnBoolean(0); + } + else + { + if (READ_PROPERTY) + { + GB.ReturnBoolean(THIS->checked); + } + else + { + THIS->checked = VPROP(GB_BOOLEAN); + update_check(THIS); + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Toggle) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->toggle); + else + { + THIS->toggle = VPROP(GB_BOOLEAN); + update_check(THIS); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Radio) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->radio); + else if (THIS->radio != VPROP(GB_BOOLEAN)) + { + THIS->radio = VPROP(GB_BOOLEAN); + if (!CMENU_is_toplevel(THIS)) + update_action_group(((CMENU *)THIS->parent)->menu); + update_check(THIS); + } + +END_PROPERTY + + +static void send_click_event(CMENU *_object); + +BEGIN_PROPERTY(Menu_Value) + + if (THIS->toggle || THIS->radio) + { + Menu_Checked(_object, _param); + return; + } + + if (READ_PROPERTY) + { + GB.ReturnBoolean(0); + } + else if (!CMENU_is_toplevel(THIS)) + { + GB.Ref(THIS); + send_click_event(THIS); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Shortcut) + + if (CMENU_is_toplevel(THIS) || THIS->menu) + { + if (READ_PROPERTY) + GB.ReturnVoidString(); + + return; + } + + if (READ_PROPERTY) + GB.ReturnNewZeroString(THIS->accel ? (const char *)THIS->accel->toString().toUtf8() : NULL); + else + { + if (THIS->accel) + delete THIS->accel; + THIS->accel = new QKeySequence; + *(THIS->accel) = QKeySequence::fromString(QSTRING_PROP()); + + update_accel(THIS); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->visible); + else + set_menu_visible(THIS, VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Menu_Show) + + set_menu_visible(THIS, true); + +END_METHOD + + +BEGIN_METHOD_VOID(Menu_Hide) + + set_menu_visible(THIS, false); + +END_METHOD + + +BEGIN_METHOD_VOID(Menu_Delete) + + delete_menu(THIS); + +END_METHOD + + +BEGIN_PROPERTY(MenuChildren_Count) + + if (THIS->menu) + GB.ReturnInteger(THIS->menu->actions().count()); + else + GB.ReturnInteger(0); + +END_PROPERTY + + +BEGIN_METHOD_VOID(MenuChildren_next) + + int index; + + if (!THIS->menu) + { + GB.StopEnum(); + return; + } + + index = ENUM(int); + + if (index >= THIS->menu->actions().count()) + { + GB.StopEnum(); + return; + } + + GB.ReturnObject(CMenu::dict[THIS->menu->actions().at(index)]); + + ENUM(int) = index + 1; + +END_METHOD + + +BEGIN_METHOD(MenuChildren_get, GB_INTEGER index) + + int index = VARG(index); + + if (!THIS->menu || index < 0 || index >= THIS->menu->actions().count()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(CMenu::dict[THIS->menu->actions().at(index)]); + +END_METHOD + + +BEGIN_METHOD_VOID(MenuChildren_Clear) + + clear_menu(THIS); + +END_METHOD + +void CMENU_popup(CMENU *_object, const QPoint &pos) +{ + bool disabled; + void *save; + + HANDLE_PROXY(_object); + + if (THIS->menu && !THIS->exec) + { + disabled = THIS->disabled; + if (disabled) + { + THIS->disabled = false; + update_accel_recursive(THIS); + THIS->disabled = true; + } + + //qDebug("CMENU_popup: %p: open", THIS); + + // The Click event is posted, it does not occur immediately. + save = CWIDGET_enter_popup(); + THIS->exec = true; + _popup_immediate = true; + THIS->menu->exec(pos); + _popup_immediate = false; + THIS->exec = false; + CWIDGET_leave_popup(save); + + //qDebug("_popup_menu_clicked = %p", _popup_menu_clicked); + update_accel_recursive(THIS); + + //CWIDGET_check_hovered(); + + //qDebug("CMENU_popup: %p: close", THIS); + + if (_popup_menu_clicked) + { + CMENU *menu = _popup_menu_clicked; + _popup_menu_clicked = NULL; + send_click_event(menu); + //GB.Unref(POINTER(&menu)); + } + + //qDebug("CMENU_popup: %p: end", THIS); + + MENU_popup_count++; + + //MyMainWindow *toplevel = (MyMainWindow *)(THIS->toplevel); + //CWINDOW_fix_menubar((CWINDOW *)CWidget::get(toplevel)); + } +} + +BEGIN_METHOD(Menu_Popup, GB_INTEGER x; GB_INTEGER y) + + QPoint pos; + + if (MISSING(x) || MISSING(y)) + pos = QCursor::pos(); + else + pos = QPoint(VARG(x), VARG(y)); + + CMENU_popup(THIS, pos); + +END_METHOD + +BEGIN_METHOD_VOID(Menu_Close) + + HANDLE_PROXY(_object); + if (THIS->menu) + THIS->menu->close(); + +END_METHOD + +BEGIN_PROPERTY(Menu_Window) + + GB.ReturnObject(CWidget::get(THIS->toplevel)); + +END_PROPERTY + +BEGIN_PROPERTY(Menu_Action) + + char *current = THIS_EXT ? THIS_EXT->action : NULL; + + if (READ_PROPERTY) + GB.ReturnString(current); + else + { + char *action = PLENGTH() ? GB.NewString(PSTRING(), PLENGTH()) : NULL; + + CACTION_register(THIS, current, action); + + if (THIS_EXT) + GB.FreeString(&THIS_EXT->action); + + if (action) + ENSURE_EXT(THIS)->action = action; + } +END_PROPERTY + + +BEGIN_PROPERTY(Menu_SaveText) + + if (READ_PROPERTY) + GB.ReturnString(THIS->save_text); + else + GB.StoreString(PROP(GB_STRING), &THIS->save_text); + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Proxy) + + if (READ_PROPERTY) + GB.ReturnObject(THIS_EXT ? THIS_EXT->proxy : NULL); + else + { + CMENU *menu = (CMENU *)VPROP(GB_OBJECT); + + if (menu && GB.CheckObject(menu)) + return; + + register_proxy(THIS, menu); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Menu_Tag) + + if (READ_PROPERTY) + { + if (THIS_EXT) + GB.ReturnVariant(&THIS_EXT->tag); + else + { + GB.ReturnNull(); + GB.ReturnConvVariant(); + } + } + else + GB.StoreVariant(PROP(GB_VARIANT), POINTER(&(ENSURE_EXT(THIS)->tag))); + +END_METHOD + + +BEGIN_PROPERTY(Menu_Closed) + + HANDLE_PROXY(_object); + GB.ReturnBoolean(!THIS->opened); + +END_PROPERTY + +//--------------------------------------------------------------------------- + +GB_DESC CMenuChildrenDesc[] = +{ + GB_DECLARE(".Menu.Children", sizeof(CMENU)), GB_VIRTUAL_CLASS(), + + GB_METHOD("_next", "Menu", MenuChildren_next, NULL), + GB_METHOD("_get", "Menu", MenuChildren_get, "(Index)i"), + GB_METHOD("Clear", NULL, MenuChildren_Clear, NULL), + GB_PROPERTY_READ("Count", "i", MenuChildren_Count), + + GB_END_DECLARE +}; + + +GB_DESC CMenuDesc[] = +{ + GB_DECLARE("Menu", sizeof(CMENU)), //GB_HOOK_CHECK(CWIDGET_check), + GB_HOOK_CHECK(check_menu), + + GB_METHOD("_new", NULL, Menu_new, "(Parent)o[(Hidden)b]"), + GB_METHOD("_free", NULL, Menu_free, NULL), + + GB_PROPERTY("Name", "s", Control_Name), + GB_PROPERTY("Caption", "s", Menu_Text), + GB_PROPERTY("Text", "s", Menu_Text), + GB_PROPERTY("_Text", "s", Menu_SaveText), + GB_PROPERTY("Enabled", "b", Menu_Enabled), + GB_PROPERTY("Checked", "b", Menu_Checked), + GB_PROPERTY("Tag", "v", Menu_Tag), + GB_PROPERTY("Picture", "Picture", Menu_Picture), + GB_PROPERTY("Shortcut", "s", Menu_Shortcut), + GB_PROPERTY("Visible", "b", Menu_Visible), + GB_PROPERTY("Toggle", "b", Menu_Toggle), + GB_PROPERTY("Radio", "b", Menu_Radio), + GB_PROPERTY("Value", "b", Menu_Value), + GB_PROPERTY("Action", "s", Menu_Action), + GB_PROPERTY_READ("Window", "Window", Menu_Window), + GB_PROPERTY("Proxy", "Menu", Menu_Proxy), + + GB_PROPERTY_SELF("Children", ".Menu.Children"), + + MENU_DESCRIPTION, + + GB_METHOD("Popup", NULL, Menu_Popup, "[(X)i(Y)i]"), + GB_METHOD("Close", NULL, Menu_Close, NULL), + GB_METHOD("Delete", NULL, Menu_Delete, NULL), + GB_METHOD("Show", NULL, Menu_Show, NULL), + GB_METHOD("Hide", NULL, Menu_Hide, NULL), + + GB_PROPERTY_READ("Closed", "b", Menu_Closed), + + //GB_EVENT("Delete", NULL, NULL, &EVENT_Destroy), // Must be first + GB_EVENT("Click", NULL, NULL, &EVENT_Click), + GB_EVENT("Show", NULL, NULL, &EVENT_Show), + GB_EVENT("Hide", NULL, NULL, &EVENT_Hide), + + GB_END_DECLARE +}; + + + +/* CMenu class */ + +CMenu CMenu::manager; +QHash CMenu::dict; + +static void send_click_event(CMENU *_object) +{ + if (THIS->toggle && !THIS->radio) + { + THIS->checked = !THIS->checked; + update_check(THIS); + } + + GB.Raise(THIS, EVENT_Click, 0); + //qDebug("CACTION_raise: %s", THIS->widget.name); + CACTION_raise((CWIDGET *)THIS); + GB.Unref(POINTER(&_object)); +} + +static void send_menu_event(CMENU *_object, intptr_t event) +{ + GB.Raise(THIS, event, 0); + GB.Unref(POINTER(&_object)); +} + +/*void CMenu::slotTriggered(QAction *action) +{ + GET_MENU_SENDER(parent); + CMENU *menu = CMenu::dict[action]; + + qDebug("slotTriggered: %s %s from %s", menu->widget.name, (const char *)action->text().toUtf8(), parent->widget.name); + + if (GB.Is(parent->parent, CLASS_Menu)) + return; + + GB.Ref(menu); + + if (_popup_immediate) + _popup_menu_clicked = menu; + else + GB.Post((GB_CALLBACK)send_click_event, (intptr_t)menu); +}*/ + +void CMenu::slotTriggered() +{ + CMENU *_object = dict[(QAction *)sender()]; + + if (!_object) + return; + + //qDebug("slotTriggered: %s %s from %s", menu->widget.name, (const char *)action->text().toUtf8(), parent->widget.name); + + GB.Ref(THIS); + + if (_popup_immediate) + _popup_menu_clicked = THIS; + else + GB.Post((GB_CALLBACK)send_click_event, (intptr_t)THIS); +} + +void CMenu::slotShown(void) +{ + static bool init = FALSE; + + //qDebug("slotShown: sender = %p menuAction = %p", sender(), ((QMenu *)sender())->menuAction()); + GET_MENU_SENDER(menu); + HANDLE_PROXY(menu); + + GB.Ref(menu); + + menu->opened = TRUE; + + GB.Raise(menu, EVENT_Show, 0); + + if (!init) + { + GB.GetFunction(&_init_shortcut_func, (void *)GB.FindClass("_Gui"), "_DefineShortcut", NULL, NULL); + init = TRUE; + } + + GB.Push(1, GB_T_OBJECT, menu); + GB.Call(&_init_shortcut_func, 1, FALSE); + + GB.Unref(POINTER(&menu)); +} + +void CMenu::slotHidden(void) +{ + //qDebug("slotHidden: sender = %p menuAction = %p", sender(), ((QMenu *)sender())->menuAction()); + GET_MENU_SENDER(menu); + HANDLE_PROXY(menu); + + menu->opened = FALSE; + + if (GB.CanRaise(menu, EVENT_Hide)) + { + GB.Ref(menu); + GB.Post2((GB_CALLBACK)send_menu_event, (intptr_t)menu, EVENT_Hide); + } +} + + +#if 0 +void CMenu::enableAccel(CMENU *item, bool enable, bool rec) +{ + // Do not disable shortcuts when a menu is executed + if (item->exec && !enable) + return; + + if (!rec) + qDebug("CMenu::enableAccel: %s: %s", item->widget.name, enable ? "ON" : "OFF"); + + item->noshortcut = !enable; + update_accel(item); + + if (item->menu) + { + int i; + + for (i = 0; i < item->menu->actions().count(); i++) + CMenu::enableAccel(CMenu::dict[item->menu->actions().at(i)], enable, true); + } +} +#endif + +void CMenu::hideSeparators(CMENU *item) +{ + if (!item->menu) + return; + + #if 0 + CMENU *child; + CMENU *last_child; + //QListIterator it(*item->children); + bool is_sep; + bool last_sep; + QList children = *(item->children); + int i; + + //qDebug("checking separators"); + + last_sep = true; + last_child = 0; + + for(i = 0; i < children.count(); i++) + { + child = children.at(i); + + is_sep = CMENU_is_separator(child); + + //qDebug("separator = %d visible = %d (%p -> %p)", is_sep, CMENU_is_visible(child), child, it.current()); + + if (is_sep) + { + if (last_sep) + { + hide_menu(child); + } + else + { + show_menu(child); + last_sep = true; + last_child = child; + } + } + else + { + if (CMENU_is_visible(child)) + last_sep = false; + } + } + + if (last_sep && last_child) + hide_menu(last_child); + #endif +} + + +/* +void CMenu::unrefChildren(QWidget *w) +{ + int i; + QList list = w->actions(); + CMENU *child; + + for (i = 0; i < list.count(); i++) + { + child = dict[list.at(i)]; + //qDebug("CMenu::unrefChildren: (%s %p)", GB.GetClassName(child), child); + GB.Detach(child); + unregister_menu(child); + //qDebug("*** CMenu::destroy %p (child)", child); + GB.Unref(POINTER(&child)); + } +}*/ + +void CMenu::slotToggled(bool checked) +{ + CMENU *_object = dict[(QAction *)sender()]; + + if (!_object) + return; + + if (!THIS->radio) + return; + + THIS->checked = checked; +} + +void CMenu::slotDestroyed(void) +{ + CMENU *_object = dict[(QAction *)sender()]; + + #ifdef DEBUG_MENU + qDebug("*** { CMenu::destroy %p", THIS); + #endif + + //qDebug("CMenu::slotDestroyed: action = %p THIS = %p", sender(), _object); + + if (!_object) + return; + + CMenu::dict.remove(ACTION); + //qDebug("CMenu::slotDestroyed: (%s %p)", GB.GetClassName(THIS), THIS); + + //if (THIS->menu) + // unrefChildren(THIS->menu); + + #ifdef DEBUG_MENU + qDebug("UNREF (%s %p)", THIS->widget.name, THIS); + #endif + + unregister_menu(THIS); + THIS->widget.widget = NULL; + GB.Unref(POINTER(&_object)); + + //menu->dict = dict; + + #ifdef DEBUG_MENU + qDebug("*** } CMenu::destroy: %p", THIS); + #endif +} + +//--------------------------------------------------------------------------- + +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) && QT_VERSION < QT_VERSION_CHECK(5,5,0) + +void MyMenu::setVisible(bool visible) +{ + if (!visible) + setAttribute(Qt::WA_NoMouseReplay); + QMenu::setVisible(visible); +} + +#endif diff --git a/gb.qt4/src/CMenu.h b/gb.qt4/src/CMenu.h new file mode 100644 index 00000000..50cb9b55 --- /dev/null +++ b/gb.qt4/src/CMenu.h @@ -0,0 +1,136 @@ +/*************************************************************************** + + CMenu.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CMENU_H +#define __CMENU_H + +#include "gambas.h" + +#include +#include +#include +#include +#include +#include + +#include "CWidget.h" +#include "CPicture.h" + +#ifndef __CMENU_CPP + +extern GB_DESC CMenuDesc[]; +extern GB_DESC CMenuChildrenDesc[]; +extern int MENU_popup_count; + +#else + +#define THIS OBJECT(CMENU) +#define THIS_EXT ((CMENU_EXT *)(THIS->widget.ext)) +#define ACTION ((QAction *)((CWIDGET *)_object)->widget) +#define PARENT_ACTION ((QAction *)((CWIDGET *)(THIS->parent))->widget) + +#define CMENU_is_toplevel(_menu) (GB.Is((_menu)->parent, CLASS_Window)) + +#define GET_MENU_SENDER(_menu) CMENU *_menu = CMenu::dict[((QMenu *)sender())->menuAction()] + +#endif + +typedef + struct { + GB_VARIANT_VALUE tag; + void *proxy; + char *action; + } + CMENU_EXT; + +typedef + struct _CMENU { + CWIDGET widget; + void *parent; + QWidget *toplevel; + QMenu *menu; + QKeySequence *accel; + CPICTURE *picture; + char *save_text; + unsigned deleted : 1; + unsigned toggle : 1; + unsigned radio : 1; + unsigned exec : 1; + unsigned checked : 1; + unsigned disabled : 1; + unsigned visible : 1; + unsigned init_shortcut : 1; + unsigned opened : 1; + } + CMENU; + +typedef + QList CMenuList; + +class MyAction : public QAction +{ + Q_OBJECT + +public: + + MyAction(QObject *parent); + +protected: + + virtual bool event(QEvent *); +}; + +class CMenu : public QObject +{ + Q_OBJECT + +public: + + static CMenu manager; + static QHash dict; + + //static void unrefChildren(QWidget *wid); + //static void enableAccel(CMENU *item, bool enable, bool rec = false); + static void hideSeparators(CMENU *item); + +public slots: + + void slotTriggered(); + void slotToggled(bool); + void slotDestroyed(); + void slotShown(); + void slotHidden(); +}; + +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) && QT_VERSION < QT_VERSION_CHECK(5,5,0) +class MyMenu: public QMenu +{ +public: + + virtual void setVisible(bool visible); +}; +#endif + +void CMENU_popup(CMENU *_object, const QPoint &pos); + +#endif diff --git a/gb.qt4/src/CMessage.cpp b/gb.qt4/src/CMessage.cpp new file mode 100644 index 00000000..74ee29f5 --- /dev/null +++ b/gb.qt4/src/CMessage.cpp @@ -0,0 +1,554 @@ +/*************************************************************************** + + CMessage.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CMESSAGE_CPP + +#include "gambas.h" +#include "main.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gb.qt.h" +#include "CWindow.h" +#include "CPicture.h" +#include "CMessage.h" + +typedef + struct { + GB_STRING msg; + GB_STRING btn[3]; + } + MSG_PARAM; + +typedef + struct { + const char *text; + //QMessageBox::ButtonRole role; + QMessageBox::StandardButton button; + } + MSG_BUTTON_ROLE; + +static char *_title = 0; + +static int make_message(int type, int nbmax, void *_param) +{ + MSG_PARAM *_p = (MSG_PARAM *)_param; + QString msg = QSTRING_ARG(msg); + QPushButton *button[3]; + int ret, nbutton; + QMessageBox::Icon icon; + //const char *stock; + QString title; + QWidget *parent; + int i, n; + QMessageBox *mb; + bool busy = FALSE; + + if (MAIN_CHECK_INIT()) + return 0; + + if (MAIN_in_message_box) + { + GB.Error("Message box already displayed"); + return 0; + } + + MAIN_in_message_box++; + + // Make message box + + parent = qApp->activeWindow(); + if (!parent) + { + if (CWINDOW_Current) + parent = CWINDOW_Current->widget.widget; + else if (CWINDOW_Main) + parent = CWINDOW_Main->widget.widget; + } + + mb = new QMessageBox(parent); + + // Number of buttons + + nbutton = 0; + for (i = 0; i < nbmax; i++) + { + if (MISSING(btn[i])) + continue; + nbutton++; + } + + // Create the buttons with their role + + for (i = 0, n = 0; i < nbmax; i++) + { + if (MISSING(btn[i])) + continue; + + /*std = get_standard_button(GB.ToZeroString(ARG(btn[i]))); + if (std) + { + button[n] = mb->addButton(std); + } + else + {*/ + /*if (n == 0) + role = QMessageBox::AcceptRole; + else if (n == (nbutton - 1)) + role = QMessageBox::RejectRole; + else + role = QMessageBox::ActionRole;*/ + + button[n] = mb->addButton(QSTRING_ARG(btn[i]), QMessageBox::ActionRole); + //} + + n++; + } + + // Default and cancel buttons + + if (nbutton) + { + mb->setDefaultButton(button[0]); + mb->setEscapeButton(button[nbutton - 1]); + } + + // Icon + + switch (type) + { + case MSG_INFO: + icon = QMessageBox::Information; + //stock = "icon:/48/info"; + break; + case MSG_WARNING: + icon = QMessageBox::Warning; + //stock = "icon:/48/warning"; + break; + case MSG_ERROR: + icon = QMessageBox::Critical; + //stock = "icon:/48/error"; + break; + case MSG_QUESTION: + icon = QMessageBox::Question; + //stock = "icon:/48/question"; + break; + case MSG_DELETE: + icon = QMessageBox::Warning; + //stock = "icon:/48/trash"; + break; + default: + icon = QMessageBox::Information; + //stock = 0; + } + + mb->setIcon(icon); + /*if (stock) + { + CPICTURE *pict = CPICTURE_get_picture(stock); + if (pict) + mb->setIconPixmap(*pict->pixmap); + }*/ + + // Title + + if (_title && *_title) + { + title = TO_QSTRING(_title); + GB.FreeString(&_title); + } + else + title = TO_QSTRING(GB.Application.Title()); + + mb->setWindowTitle(title); + + // Message + + if (Qt::mightBeRichText(msg)) + msg = msg.replace("\n","
    "); + + mb->setText(msg); + + // Run the message box + + if (qApp->overrideCursor()) + { + qApp->restoreOverrideCursor(); + busy = TRUE; + } + + + GB.Debug.EnterEventLoop(); + mb->exec(); + GB.Debug.LeaveEventLoop(); + + if (busy) + qApp->setOverrideCursor(Qt::WaitCursor); + + //CWINDOW_ensure_active_window(); + if (parent) + parent->activateWindow(); + + // Returned value + + if (nbutton) + { + ret = nbutton; + for (i = 0; i < nbutton; i++) + { + if (button[i] == mb->clickedButton()) + ret = i + 1; + } + } + else + ret = 1; + + MAIN_in_message_box--; + MAIN_check_quit(); + + delete mb; + + return ret; +} + +#if 0 +static int old_make_message(int type, int nbmax, void *_param) +{ + MSG_PARAM *_p = (MSG_PARAM *)_param; + + QString msg = QSTRING_ARG(msg); + QString btn[3]; + QString swap; + int mbtn[3]; + int i, cancel, ret; + QMessageBox::Icon icon; + QPoint p; + QWidget *parent; + const char *stock = 0; + QString title; + + if (_global_lock) + { + GB.Error("Message box already displayed"); + return 0; + } + + _global_lock++; + + if (!MISSING(btn[0])) + btn[0] = QSTRING_ARG(btn[0]); + + if (nbmax >= 2 && !MISSING(btn[1])) + btn[1] = QSTRING_ARG(btn[1]); + + if (nbmax >= 3 && !MISSING(btn[2])) + btn[2] = QSTRING_ARG(btn[2]); + + if (btn[0].isNull() && !btn[1].isNull()) + swap = btn[0], btn[0] = btn[1], btn[1] = swap; + + if (btn[1].isNull() && !btn[2].isNull()) + swap = btn[1], btn[1] = btn[2], btn[2] = swap; + + for (i = 0; i < 3; i++) + mbtn[i] = btn[i].isNull() ? QMessageBox::NoButton : (i + 1) ; + + mbtn[0] |= QMessageBox::Default; + + cancel = 0; + + for (i = 2; i >= 0; i--) + { + if (!btn[i].isNull()) + { + cancel = i; + break; + } + } + + mbtn[cancel] |= QMessageBox::Escape; + + switch (type) + { + case MSG_INFO: + icon = QMessageBox::Information; + stock = "icon:/32/info"; + break; + case MSG_WARNING: + icon = QMessageBox::Warning; + stock = "icon:/32/warning"; + break; + case MSG_ERROR: + icon = QMessageBox::Critical; + stock = "icon:/32/error"; + break; + case MSG_QUESTION: + icon = QMessageBox::Information; + stock = "icon:/32/question"; + break; + case MSG_DELETE: + icon = QMessageBox::Information; + stock = "icon:/32/trash"; + break; + default: + icon = QMessageBox::Information; + } + + parent = qApp->activeWindow(); + if (!parent) + { + if (CWINDOW_Main) + parent = CWINDOW_Main->widget.widget; + } + + if (_title && *_title) + { + title = TO_QSTRING(_title); + GB.FreeString(&_title); + } + else + title = TO_QSTRING(GB.Application.Title()); + + QMessageBox *mb = new QMessageBox(title, msg, icon, mbtn[0], mbtn[1], mbtn[2], parent); + + for (i = 0; i < 3; i++) + { + if (!btn[i].isNull()) + mb->setButtonText(i + 1, btn[i]); + } + + if (stock) + { + CPICTURE *pict = CPICTURE_get_picture(stock); + if (pict) + mb->setIconPixmap(*pict->pixmap); + } + + #if 0 + if (type == MSG_QUESTION) + { + if (_question_pixmap == 0) + _question_pixmap = new QPixmap(_question_xpm); + + mb->setIconPixmap(*_question_pixmap); + } + else if (type == MSG_DELETE) + { + if (_trash_pixmap == 0) + _trash_pixmap = new QPixmap(_trash_xpm); + + mb->setIconPixmap(*_trash_pixmap); + } + #endif + + //mb->adjustSize(); + if (mb->width() < 256) + mb->resize(256, mb->height()); + + /*mb->setMinimumSize(mb->width(), mb->height()); + mb->setMaximumSize(mb->width(), mb->height()); + mb->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));*/ + + mb->setParent(parent, mb->windowFlags()); // WShowModal + mb->installEventFilter(&CMessage::manager); + + //qDebug("message-box: parent = %p", mb->parentWidget()); + ret = mb->exec(); + + if (ret == 0) + ret = cancel + 1; + + delete mb; + + _global_lock--; + + return ret; +} +#endif + +BEGIN_METHOD(CMESSAGE_info, GB_STRING msg; GB_STRING btn) + + GB.ReturnInteger(make_message(MSG_INFO, 1, (void *)_p)); + +END_METHOD + + +BEGIN_METHOD(CMESSAGE_warning, GB_STRING msg; GB_STRING btn1; GB_STRING btn2; GB_STRING btn3) + + GB.ReturnInteger(make_message(MSG_WARNING, 3, (void *)_p)); + +END_METHOD + + +BEGIN_METHOD(CMESSAGE_question, GB_STRING msg; GB_STRING btn1; GB_STRING btn2; GB_STRING btn3) + + GB.ReturnInteger(make_message(MSG_QUESTION, 3, (void *)_p)); + +END_METHOD + + +BEGIN_METHOD(CMESSAGE_delete, GB_STRING msg; GB_STRING btn1; GB_STRING btn2; GB_STRING btn3) + + GB.ReturnInteger(make_message(MSG_DELETE, 3, (void *)_p)); + +END_METHOD + + +BEGIN_METHOD(CMESSAGE_error, GB_STRING msg; GB_STRING btn1; GB_STRING btn2; GB_STRING btn3) + + GB.ReturnInteger(make_message(MSG_ERROR, 3, (void *)_p)); + +END_METHOD + + +BEGIN_METHOD_VOID(CMESSAGE_exit) + + GB.FreeString(&_title); + +END_METHOD + + +BEGIN_PROPERTY(CMESSAGE_title) + + if (READ_PROPERTY) + GB.ReturnString(_title); + else + GB.StoreString(PROP(GB_STRING), &_title); + +END_PROPERTY + +GB_DESC CMessageDesc[] = +{ + GB_DECLARE("Message", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_exit", NULL, CMESSAGE_exit, NULL), + + GB_STATIC_METHOD("_call", "i", CMESSAGE_info, "(Message)s[(Button)s]"), + GB_STATIC_METHOD("Info", "i", CMESSAGE_info, "(Message)s[(Button)s]"), + GB_STATIC_METHOD("Warning", "i", CMESSAGE_warning, "(Message)s[(Button1)s(Button2)s(Button3)s]"), + GB_STATIC_METHOD("Question", "i", CMESSAGE_question, "(Message)s[(Button1)s(Button2)s(Button3)s]"), + GB_STATIC_METHOD("Error", "i", CMESSAGE_error, "(Message)s[(Button1)s(Button2)s(Button3)s]"), + GB_STATIC_METHOD("Delete", "i", CMESSAGE_delete, "(Message)s[(Button1)s(Button2)s(Button3)s]"), + + GB_STATIC_PROPERTY("Title", "s", CMESSAGE_title), + + GB_END_DECLARE +}; + + + +/*************************************************************************** + + MyMessageBox + +***************************************************************************/ + +#if 0 +MyMessageBox::MyMessageBox( + const QString& caption, const QString &text, Icon icon, + int button0, int button1, int button2) +: QMessageBox(caption, text, icon, button0, button1, button2, qApp->activeWindow()) +{ + center = true; + + adjustSize(); + if (width() < 256) + resize(256, height()); + + setMinimumSize(width(), height()); + setMaximumSize(width(), height()); + setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); +} + +void MyMessageBox::showEvent(QShowEvent *e) +{ + QMessageBox::showEvent(e); + + if (center) + { + QPoint p( + (qApp->desktop()->width() - width()) / 2, + (qApp->desktop()->height() - height()) / 2 + ); + + //qDebug("%d x %d / %d x %d", qApp->desktop()->width(), qApp->desktop()->height(), mb->width(), mb->height()); + //qDebug("(%d, %d) / (%d, %d)", p.x(), p.y(), mb->parentWidget()->x(), mb->parentWidget()->y()); + //p = mb->parentWidget()->mapFromGlobal(p); + //qDebug("==> (%d, %d)", p.x(), p.y()); + //mb->reparent(mb->parentWidget(), Qt::WStyle_Customize | Qt::WStyle_DialogBorder | Qt::WStyle_Title | Qt::WStyle_SysMenu | Qt::WType_Dialog, p); + //qDebug("move MessageBox to (%d %d)", mb->pos().x(), mb->pos().y()); + + move(64, 64); + center = false; + } +} + +int MyMessageBox::run() +{ + QPoint p(64, 64); + + clearWFlags(Qt::WDestructiveClose); + clearWFlags(Qt::WShowModal); + + show(); + reparent(qApp->activeWindow(), getWFlags() | Qt::WStyle_DialogBorder | Qt::WShowModal, p); // WShowModal + move(p); + show(); + + qApp->eventLoop()->enterLoop(); + + return result(); +} +#endif + + + + +/*************************************************************************** + + CMessage + +***************************************************************************/ + +CMessage CMessage::manager; + +bool CMessage::eventFilter(QObject *o, QEvent *e) +{ + if (e->type() == QEvent::Show) + { + QMessageBox *mb = (QMessageBox *)o; + + mb->move((qApp->desktop()->availableGeometry().width() - mb->width()) / 2, (qApp->desktop()->availableGeometry().height() - mb->height()) / 2); + mb->removeEventFilter(this); + } + + return QObject::eventFilter(o, e); // standard event processing +} diff --git a/gb.qt4/src/CMessage.h b/gb.qt4/src/CMessage.h new file mode 100644 index 00000000..873dc73d --- /dev/null +++ b/gb.qt4/src/CMessage.h @@ -0,0 +1,64 @@ +/*************************************************************************** + + CMessage.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CMESSAGE_H +#define __CMESSAGE_H + +#include +#include +//Added by qt3to4: +#include + +#include "gambas.h" + +#ifndef __CMESSAGE_CPP +extern GB_DESC CMessageDesc[]; +#else + +enum { + MSG_INFO = 0, + MSG_WARNING = 1, + MSG_QUESTION = 2, + MSG_ERROR = 3, + MSG_DELETE = 4 + }; + +#endif + +class CMessage : public QObject +{ + Q_OBJECT + +public: + + static CMessage manager; + +protected: + + bool eventFilter(QObject *, QEvent *); +}; + + + + +#endif diff --git a/gb.qt4/src/CMouse.cpp b/gb.qt4/src/CMouse.cpp new file mode 100644 index 00000000..6a3adf0c --- /dev/null +++ b/gb.qt4/src/CMouse.cpp @@ -0,0 +1,575 @@ +/*************************************************************************** + + CMouse.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CMOUSE_CPP + +#include +#include +#include +#include + +#include "gambas.h" +#include "main.h" + +#include "gb.form.const.h" +#include "CWidget.h" +#include "CPicture.h" +#include "CMouse.h" + +MOUSE_INFO MOUSE_info = { 0 }; +POINTER_INFO POINTER_info = { 0 }; +static int _dx = 0; +static int _dy = 0; + +void CMOUSE_clear(int valid) +{ + if (valid) + MOUSE_info.valid++; + else + MOUSE_info.valid--; + + if (MOUSE_info.valid == 0) + CLEAR(&POINTER_info); +} + +void CMOUSE_reset_translate() +{ + _dx = _dy = 0; +} + +//int CMOUSE_last_state = 0; + +//static CCURSOR PredefinedCursor[LastCursor + 1] = { { { 0, 0 }, NULL, NULL } }; +//static int MouseClassID; + +#if 0 +static int translate_state(int s) +{ + int bst = 0; + + if (s & Button1Mask) + bst |= Qt::LeftButton; + if ( s & Button2Mask) + bst |= Qt::MidButton; + if ( s & Button3Mask) + bst |= Qt::RightButton; + if ( s & ShiftMask) + bst |= Qt::ShiftModifier; + if ( s & ControlMask) + bst |= Qt::ControlModifier; + if ( s & qt_alt_mask) + bst |= Qt::AltModifier; + if ( s & qt_meta_mask) + bst |= Qt::MetaModifier; + + return bst; +} + +static int get_state() +{ + Window root; + Window child; + int root_x, root_y, win_x, win_y; + uint state; + Display* dpy = QPaintDevice::x11AppDisplay(); + + for (int i = 0; i < ScreenCount(dpy); i++) + { + if (XQueryPointer(dpy, RootWindow(dpy, i), &root, &child, + &root_x, &root_y, &win_x, &win_y, &state)) + return translate_state(state); + } + + return 0; +} +#endif + +//------------------------------------------------------------------------- + +BEGIN_METHOD(Cursor_new, GB_OBJECT picture; GB_INTEGER x; GB_INTEGER y) + + CPICTURE *pict = (CPICTURE *)VARG(picture); + + THIS->x = VARGOPT(x, -1); + THIS->y = VARGOPT(y, -1); + + //GB.StoreObject(ARG(picture), POINTER(&THIS->picture)); + if (GB.CheckObject(pict)) + return; + + if (THIS->x < 0 || THIS->x >= pict->pixmap->width()) + THIS->x = -1; + + if (THIS->y < 0 || THIS->y >= pict->pixmap->height()) + THIS->y = -1; + + THIS->cursor = new QCursor(*(pict->pixmap), THIS->x, THIS->y); + +END_METHOD + + +BEGIN_METHOD_VOID(Cursor_Delete) + + //GB.Unref(POINTER(&THIS->picture)); + delete THIS->cursor; + +END_METHOD + + +/*BEGIN_PROPERTY(CCURSOR_picture) + + GB.ReturnObject(THIS->picture); + +END_PROPERTY*/ + + +BEGIN_PROPERTY(Cursor_X) + + GB.ReturnInteger(THIS->x); + +END_PROPERTY + + +BEGIN_PROPERTY(Cursor_Y) + + GB.ReturnInteger(THIS->y); + +END_PROPERTY + + +// BEGIN_METHOD(CCURSOR_get, int shape) +// +// int shape = PARAM(shape); +// CCURSOR *p; +// +// if (shape < 0 || shape > LastCursor) +// GB.ReturnObject(NULL); +// +// p = &PredefinedCursor[shape]; +// if (p->ob.klass == 0) +// { +// p->ob.klass = MouseClassID; +// p->cursor = new QCursor(shape); +// GB.Ref(p); +// } +// +// GB.ReturnObject(p); +// +// END_METHOD + + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Mouse_ScreenX) + + GB.ReturnInteger(QCursor::pos().x()); + +END_PROPERTY + + +BEGIN_PROPERTY(Mouse_ScreenY) + + GB.ReturnInteger(QCursor::pos().y()); + +END_PROPERTY + + +BEGIN_METHOD(Mouse_Move, GB_INTEGER x; GB_INTEGER y) + + QCursor::setPos(VARG(x), VARG(y)); + +END_PROPERTY + +#define CHECK_VALID() \ + if (MOUSE_info.valid == 0) \ + { \ + GB.Error("No mouse event data"); \ + return; \ + } + +BEGIN_PROPERTY(Mouse_X) + + CHECK_VALID(); + GB.ReturnInteger(MOUSE_info.x + _dx); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Y) + + CHECK_VALID(); + GB.ReturnInteger(MOUSE_info.y + _dy); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_StartX) + + CHECK_VALID(); + GB.ReturnInteger(MOUSE_info.sx + _dx); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_StartY) + + CHECK_VALID(); + GB.ReturnInteger(MOUSE_info.sy + _dy); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Button) + + CHECK_VALID(); + GB.ReturnInteger((int)MOUSE_info.button); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_State) + + CHECK_VALID(); + int state = (int)MOUSE_info.state; + if (MOUSE_info.modifier & Qt::ShiftModifier) + state |= MOUSE_SHIFT; + if (MOUSE_info.modifier & Qt::ControlModifier) + state |= MOUSE_CTRL; + if (MOUSE_info.modifier & Qt::AltModifier) + state |= MOUSE_ALT; + if (MOUSE_info.modifier & Qt::MetaModifier) + state |= MOUSE_META; + + GB.ReturnInteger(state); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Left) + + CHECK_VALID(); + GB.ReturnBoolean((MOUSE_info.state | MOUSE_info.button) & Qt::LeftButton); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Right) + + CHECK_VALID(); + GB.ReturnBoolean((MOUSE_info.state | MOUSE_info.button) & Qt::RightButton); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Middle) + + CHECK_VALID(); + GB.ReturnBoolean((MOUSE_info.state | MOUSE_info.button) & Qt::MidButton); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Shift) + + //CHECK_VALID(); + GB.ReturnBoolean(MOUSE_info.modifier & Qt::ShiftModifier); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Control) + + //CHECK_VALID(); + GB.ReturnBoolean(MOUSE_info.modifier & Qt::ControlModifier); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Alt) + + //CHECK_VALID(); + GB.ReturnBoolean(MOUSE_info.modifier & Qt::AltModifier); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Meta) + + //CHECK_VALID(); + GB.ReturnBoolean(MOUSE_info.modifier & Qt::MetaModifier); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Normal) + + //CHECK_VALID(); + GB.ReturnBoolean(MOUSE_info.modifier == Qt::NoModifier); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Orientation) + + CHECK_VALID(); + GB.ReturnInteger(MOUSE_info.orientation); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Delta) + + CHECK_VALID(); + GB.ReturnFloat((double)MOUSE_info.delta / 120); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Forward) + + CHECK_VALID(); + GB.ReturnBoolean(MOUSE_info.delta > 0); + +END_PROPERTY + +BEGIN_METHOD(Mouse_Inside, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + QPoint pos; + + if (GB.CheckObject(control)) + return; + + if (!CWIDGET_is_visible(control)) + { + GB.ReturnBoolean(false); + return; + } + + pos = QCursor::pos() - QWIDGET(control)->mapToGlobal(QPoint(0, 0)); + GB.ReturnBoolean(pos.x() >= 0 && pos.x() < QWIDGET(control)->width() && pos.y() >= 0 && pos.y() < QWIDGET(control)->height()); + +END_METHOD + +BEGIN_METHOD(Mouse_Translate, GB_INTEGER dx; GB_INTEGER dy) + + CHECK_VALID(); + + _dx = VARG(dx); + _dy = VARG(dy); + +END_METHOD + +#if 0 +BEGIN_METHOD(Mouse_Begin, GB_OBJECT control; GB_INTEGER x; GB_INTEGER y; GB_INTEGER state) + + void *control = VARG(control); + int state = VARGOPT(state, 0); + int x = VARG(x); + int y = VARG(y); + + if (GB.CheckObject(control)) + return; + + CMOUSE_clear(true); + MOUSE_info.x = x; + MOUSE_info.y = y; + QPoint p = QWIDGET(control)->mapToGlobal(QPoint(x, y)); + MOUSE_info.sx = p.x(); + MOUSE_info.sy = p.y(); + + MOUSE_info.state = (Qt::MouseButtons)(state & 0xFF); + if (state & MOUSE_LEFT) + MOUSE_info.button = Qt::LeftButton; + else if (state & MOUSE_MIDDLE) + MOUSE_info.button = Qt::MiddleButton; + else if (state & MOUSE_RIGHT) + MOUSE_info.button = Qt::RightButton; + + MOUSE_info.modifier = (Qt::KeyboardModifiers)0; + if (state & MOUSE_SHIFT) + MOUSE_info.modifier |= Qt::ShiftModifier; + if (state & MOUSE_CTRL) + MOUSE_info.modifier |= Qt::ShiftModifier; + if (state & MOUSE_ALT) + MOUSE_info.modifier |= Qt::ShiftModifier; + +END_METHOD + +BEGIN_METHOD_VOID(Mouse_End) + + CMOUSE_clear(false); + +END_METHOD +#endif + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Pointer_X) + + CHECK_VALID(); + GB.ReturnFloat((double)(MOUSE_info.x + _dx) + (POINTER_info.tx - (int)POINTER_info.tx)); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_Y) + + CHECK_VALID(); + GB.ReturnFloat((double)(MOUSE_info.y + _dy) + (POINTER_info.ty - (int)POINTER_info.ty)); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_ScreenX) + + CHECK_VALID(); + GB.ReturnFloat(POINTER_info.tx); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_ScreenY) + + CHECK_VALID(); + GB.ReturnFloat(POINTER_info.ty); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_XTilt) + + CHECK_VALID(); + GB.ReturnFloat(POINTER_info.xtilt); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_YTilt) + + CHECK_VALID(); + GB.ReturnFloat(POINTER_info.ytilt); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_Pressure) + + CHECK_VALID(); + GB.ReturnFloat(POINTER_info.pressure); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_Rotation) + + CHECK_VALID(); + GB.ReturnFloat(POINTER_info.rotation); + +END_PROPERTY + +BEGIN_PROPERTY(Pointer_Type) + + CHECK_VALID(); + GB.ReturnInteger(POINTER_info.type); + +END_PROPERTY + + +//------------------------------------------------------------------------- + +GB_DESC CCursorDesc[] = +{ + GB_DECLARE("Cursor", sizeof(CCURSOR)), + + GB_METHOD("_new", NULL, Cursor_new, "(Picture)Picture;[(X)i(Y)i]"), + GB_METHOD("_free", NULL, Cursor_Delete, NULL), + + GB_PROPERTY_READ("X", "i", Cursor_X), + GB_PROPERTY_READ("Y", "i", Cursor_Y), + //GB_PROPERTY_READ("Picture", "Picture", CCURSOR_picture), + + GB_END_DECLARE +}; + + +GB_DESC CMouseDesc[] = +{ + GB_DECLARE("Mouse", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY_READ("ScreenX", "i", Mouse_ScreenX), + GB_STATIC_PROPERTY_READ("ScreenY", "i", Mouse_ScreenY), + GB_STATIC_METHOD("Move", NULL, Mouse_Move, "(X)i(Y)i"), + GB_STATIC_METHOD("Inside", "b", Mouse_Inside, "(Control)Control"), + + GB_CONSTANT("Default", "i", CMOUSE_DEFAULT), + GB_CONSTANT("Custom", "i", CMOUSE_CUSTOM), + GB_CONSTANT("Blank", "i", Qt::BlankCursor), + GB_CONSTANT("Arrow", "i", Qt::ArrowCursor), + GB_CONSTANT("Cross", "i", Qt::CrossCursor), + GB_CONSTANT("Wait", "i", Qt::WaitCursor), + GB_CONSTANT("Text", "i", Qt::IBeamCursor), + GB_CONSTANT("SizeAll", "i", Qt::SizeAllCursor), + GB_CONSTANT("SizeH", "i", Qt::SizeHorCursor), + GB_CONSTANT("SizeV", "i", Qt::SizeVerCursor), + GB_CONSTANT("SizeN", "i", Qt::SizeVerCursor), + GB_CONSTANT("SizeS", "i", Qt::SizeVerCursor), + GB_CONSTANT("SizeW", "i", Qt::SizeHorCursor), + GB_CONSTANT("SizeE", "i", Qt::SizeHorCursor), + GB_CONSTANT("SizeNW", "i", Qt::SizeFDiagCursor), + GB_CONSTANT("SizeSE", "i", Qt::SizeFDiagCursor), + GB_CONSTANT("SizeNE", "i", Qt::SizeBDiagCursor), + GB_CONSTANT("SizeSW", "i", Qt::SizeBDiagCursor), + GB_CONSTANT("SizeNWSE", "i", Qt::SizeFDiagCursor), + GB_CONSTANT("SizeNESW", "i", Qt::SizeBDiagCursor), + GB_CONSTANT("SplitH", "i", Qt::SplitHCursor), + GB_CONSTANT("SplitV", "i", Qt::SplitVCursor), + GB_CONSTANT("Pointing", "i", Qt::PointingHandCursor), + + GB_CONSTANT("Horizontal", "i", Qt::Horizontal), + GB_CONSTANT("Vertical", "i", Qt::Vertical), + + GB_STATIC_PROPERTY_READ("X", "i", Mouse_X), + GB_STATIC_PROPERTY_READ("Y", "i", Mouse_Y), + GB_STATIC_PROPERTY_READ("StartX", "i", Mouse_StartX), + GB_STATIC_PROPERTY_READ("StartY", "i", Mouse_StartY), + GB_STATIC_PROPERTY_READ("Left", "b", Mouse_Left), + GB_STATIC_PROPERTY_READ("Right", "b", Mouse_Right), + GB_STATIC_PROPERTY_READ("Middle", "b", Mouse_Middle), + GB_STATIC_PROPERTY_READ("State", "i", Mouse_State), + GB_STATIC_PROPERTY_READ("Button", "i", Mouse_Button), + GB_STATIC_PROPERTY_READ("Shift", "b", Mouse_Shift), + GB_STATIC_PROPERTY_READ("Control", "b", Mouse_Control), + GB_STATIC_PROPERTY_READ("Alt", "b", Mouse_Alt), + GB_STATIC_PROPERTY_READ("Meta", "b", Mouse_Meta), + GB_STATIC_PROPERTY_READ("Normal", "b", Mouse_Normal), + GB_STATIC_PROPERTY_READ("Orientation", "i", Mouse_Orientation), + GB_STATIC_PROPERTY_READ("Delta", "f", Mouse_Delta), + GB_STATIC_PROPERTY_READ("Forward", "b", Mouse_Forward), + + GB_STATIC_METHOD("Translate", NULL, Mouse_Translate, "(DX)i(DY)i"), + //GB_STATIC_METHOD("Begin", NULL, Mouse_Begin, "(Control)Control;(X)i(Y)i[(State)i]"), + //GB_STATIC_METHOD("End", NULL, Mouse_End, NULL), + + GB_END_DECLARE +}; + +GB_DESC CPointerDesc[] = +{ + GB_DECLARE_VIRTUAL("Pointer"), + + GB_CONSTANT("Mouse", "i", POINTER_MOUSE), + GB_CONSTANT("Pen", "i", POINTER_PEN), + GB_CONSTANT("Eraser", "i", POINTER_ERASER), + GB_CONSTANT("Cursor", "i", POINTER_CURSOR), + + GB_STATIC_PROPERTY_READ("Type", "i", Pointer_Type), + GB_STATIC_PROPERTY_READ("X", "f", Pointer_X), + GB_STATIC_PROPERTY_READ("Y", "f", Pointer_Y), + GB_STATIC_PROPERTY_READ("ScreenX", "f", Pointer_ScreenX), + GB_STATIC_PROPERTY_READ("ScreenY", "f", Pointer_ScreenY), + GB_STATIC_PROPERTY_READ("XTilt", "f", Pointer_XTilt), + GB_STATIC_PROPERTY_READ("YTilt", "f", Pointer_YTilt), + GB_STATIC_PROPERTY_READ("Pressure", "f", Pointer_Pressure), + GB_STATIC_PROPERTY_READ("Rotation", "f", Pointer_Rotation), + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/CMouse.h b/gb.qt4/src/CMouse.h new file mode 100644 index 00000000..aa3c6527 --- /dev/null +++ b/gb.qt4/src/CMouse.h @@ -0,0 +1,100 @@ +/*************************************************************************** + + CMouse.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CMOUSE_H +#define __CMOUSE_H + +#include +#include + +#include "gambas.h" + +#include "CPicture.h" + +typedef + struct { + int valid; + int x; + int y; + int sx; + int sy; + Qt::MouseButton button; + Qt::MouseButtons state; + Qt::KeyboardModifiers modifier; + int orientation; + int delta; + int screenX; + int screenY; + int dx; + int dy; + } + MOUSE_INFO; + +typedef + struct { + double tx; + double ty; + int xtilt; + int ytilt; + int type; + double pressure; + double rotation; + } + POINTER_INFO; + +#ifndef __CMOUSE_CPP +extern GB_DESC CMouseDesc[]; +extern GB_DESC CCursorDesc[]; +extern GB_DESC CPointerDesc[]; + +extern MOUSE_INFO MOUSE_info; +extern POINTER_INFO POINTER_info; +#else + +#define THIS ((CCURSOR *)_object) + +#endif + +typedef + struct _CCURSOR { + GB_BASE ob; + int x; + int y; + QCursor *cursor; + } + CCURSOR; + +#define CMOUSE_DEFAULT (-1) +#define CMOUSE_CUSTOM (-2) + + +// ### QT_WIDGET_PROPERTIES must be modified with this constant + +#define MOUSE_CONSTANTS \ + "Mouse,Default,Blank,Arrow,Cross,Wait,Text,SizeAll,SizeH,SizeV,SizeN,SizeS,SizeW,SizeE,SizeNWSE," \ + "SizeNESW,SplitH,SplitV,Pointing" + +void CMOUSE_clear(int valid); +void CMOUSE_reset_translate(void); + +#endif diff --git a/gb.qt4/src/CMovieBox.cpp b/gb.qt4/src/CMovieBox.cpp new file mode 100644 index 00000000..8c6931c6 --- /dev/null +++ b/gb.qt4/src/CMovieBox.cpp @@ -0,0 +1,179 @@ +/*************************************************************************** + + CMovieBox.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CMOVIEBOX_CPP + +#include "gambas.h" +#include "main.h" + +#include +//Added by qt3to4: +#include + +#include "CConst.h" +#include "CMovieBox.h" + + +static void free_movie(void *_object) +{ + if (!THIS->movie) + return; + + delete THIS->movie; + THIS->movie = 0; + + THIS->buffer->close(); + delete THIS->buffer; + + THIS->data->clear(); + delete THIS->data; + + GB.ReleaseFile(THIS->addr, THIS->len); + + GB.StoreString(NULL, &THIS->path); + + if (WIDGET) + WIDGET->setText(""); +} + +static bool load_movie(void *_object, char *path, int len) +{ + free_movie(THIS); + + if (len > 0) + { + //qDebug("load_movie: %.*s", (int)len, path); + if (GB.LoadFile(path, len, &THIS->addr, &THIS->len)) + return true; + + THIS->data = new QByteArray(); + *THIS->data = QByteArray::fromRawData((const char *)THIS->addr, THIS->len); + THIS->buffer = new QBuffer(THIS->data); + THIS->buffer->open(QIODevice::ReadOnly); + THIS->movie = new QMovie(THIS->buffer); + + THIS->path = GB.NewString(path, len); + + //qDebug("setMovie"); + WIDGET->setMovie(THIS->movie); + } + + return false; +} + + +BEGIN_METHOD(CMOVIEBOX_new, GB_OBJECT parent) + + QLabel *wid = new QLabel(QCONTAINER(VARG(parent))); + + CWIDGET_new(wid, _object); + + wid->setAlignment(Qt::AlignLeft | Qt::AlignTop); + +END_METHOD + +BEGIN_METHOD_VOID(CMOVIEBOX_free) + + free_movie(THIS); + +END_METHOD + + +BEGIN_PROPERTY(CMOVIEBOX_path) + + if (READ_PROPERTY) + GB.ReturnString(THIS->path); + else + { + bool playing = false; + + if (THIS->movie) + playing = THIS->movie->state() == QMovie::Running; + else + playing = FALSE; + + if (load_movie(THIS, PSTRING(), PLENGTH())) + return; + + if (!playing && THIS->movie) + THIS->movie->setPaused(true); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CMOVIEBOX_playing) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->movie ? THIS->movie->state() == QMovie::Running : FALSE); + else if (THIS->movie) + { + if (VPROP(GB_BOOLEAN)) + THIS->movie->setPaused(false); + else + THIS->movie->setPaused(true); + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(CMOVIEBOX_rewind) + + if (!THIS->movie) + return; + + THIS->movie->stop(); + THIS->movie->start(); + +END_METHOD + +BEGIN_PROPERTY(MovieBox_Alignment) + + if (READ_PROPERTY) + GB.ReturnInteger(CCONST_alignment(WIDGET->alignment() & ALIGN_MASK, ALIGN_NORMAL, false)); + else + WIDGET->setAlignment((Qt::Alignment)CCONST_alignment(VPROP(GB_INTEGER), ALIGN_NORMAL, true)); + +END_PROPERTY + + +GB_DESC CMovieBoxDesc[] = +{ + GB_DECLARE("MovieBox", sizeof(CMOVIEBOX)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CMOVIEBOX_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, CMOVIEBOX_free, NULL), + + GB_PROPERTY("Path", "s", CMOVIEBOX_path), + GB_PROPERTY("Playing", "b", CMOVIEBOX_playing), + //GB_PROPERTY("Alignment", "i", CMOVIEBOX_alignment), + GB_PROPERTY("Border", "i", CWIDGET_border_full), + GB_PROPERTY("Alignment", "i", MovieBox_Alignment), + + GB_METHOD("Rewind", NULL, CMOVIEBOX_rewind, NULL), + + MOVIEBOX_DESCRIPTION, + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/CMovieBox.h b/gb.qt4/src/CMovieBox.h new file mode 100644 index 00000000..812e1d48 --- /dev/null +++ b/gb.qt4/src/CMovieBox.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + CMovieBox.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CMOVIEBOX_H +#define __CMOVIEBOX_H + +#include "gambas.h" +#include "gb.qt.h" + +#include +#include +#include + +#include "CWidget.h" + +#ifndef __CMOVIEBOX_CPP +extern GB_DESC CMovieBoxDesc[]; +#else + +#define THIS ((CMOVIEBOX *)_object) +#define WIDGET ((QLabel *)((QT_WIDGET *)_object)->widget) + +#endif + +typedef + struct { + QT_WIDGET widget; + char *path; + QByteArray *data; + QBuffer *buffer; + QMovie *movie; + char *addr; + int len; + } + CMOVIEBOX; + +#endif diff --git a/gb.qt4/src/CPanel.cpp b/gb.qt4/src/CPanel.cpp new file mode 100644 index 00000000..510b0ac0 --- /dev/null +++ b/gb.qt4/src/CPanel.cpp @@ -0,0 +1,194 @@ +/*************************************************************************** + + CPanel.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPANEL_CPP + +#include +#include + +#include "gambas.h" + +#include "CConst.h" +#include "CPanel.h" + +BEGIN_METHOD(CPANEL_new, GB_OBJECT parent) + + MyContainer *wid = new MyContainer(QCONTAINER(VARG(parent))); + THIS->container = wid; + + //THIS->widget.flag.fillBackground = true; + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_METHOD(CHBOX_new, GB_OBJECT parent) + + MyContainer *wid = new MyContainer(QCONTAINER(VARG(parent))); + + THIS->container = wid; + THIS->arrangement.mode = ARRANGE_HORIZONTAL; + //THIS->arrangement.autoresize = true; + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_METHOD(CVBOX_new, GB_OBJECT parent) + + MyContainer *wid = new MyContainer(QCONTAINER(VARG(parent))); + + THIS->container = wid; + THIS->arrangement.mode = ARRANGE_VERTICAL; + //THIS->arrangement.autoresize = true; + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_METHOD(CHPANEL_new, GB_OBJECT parent) + + MyContainer *wid = new MyContainer(QCONTAINER(VARG(parent))); + + THIS->container = wid; + THIS->arrangement.mode = ARRANGE_ROW; + //THIS->arrangement.autoresize = true; + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_METHOD(CVPANEL_new, GB_OBJECT parent) + + MyContainer *wid = new MyContainer(QCONTAINER(VARG(parent))); + + THIS->container = wid; + THIS->arrangement.mode = ARRANGE_COLUMN; + //THIS->arrangement.autoresize = true; + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + + +GB_DESC CPanelDesc[] = +{ + GB_DECLARE("Panel", sizeof(CPANEL)), GB_INHERITS("Container"), + + GB_METHOD("_new", NULL, CPANEL_new, "(Parent)Container;"), + + GB_PROPERTY("Border", "i", Container_Border), + GB_PROPERTY("Arrangement", "i", Container_Arrangement), + GB_PROPERTY("AutoResize", "b", Container_AutoResize), + GB_PROPERTY("Spacing", "b", Container_Spacing), + GB_PROPERTY("Margin", "b", Container_Margin), + GB_PROPERTY("Padding", "i", Container_Padding), + GB_PROPERTY("Indent", "b", Container_Indent), + GB_PROPERTY("Invert", "b", Container_Invert), + + PANEL_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CHBoxDesc[] = +{ + GB_DECLARE("HBox", sizeof(CPANEL)), GB_INHERITS("Container"), + + GB_METHOD("_new", NULL, CHBOX_new, "(Parent)Container;"), + + GB_PROPERTY("AutoResize", "b", Container_AutoResize), + GB_PROPERTY("Spacing", "b", Container_Spacing), + GB_PROPERTY("Margin", "b", Container_Margin), + GB_PROPERTY("Padding", "i", Container_Padding), + GB_PROPERTY("Indent", "b", Container_Indent), + GB_PROPERTY("Invert", "b", Container_Invert), + + HBOX_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CVBoxDesc[] = +{ + GB_DECLARE("VBox", sizeof(CPANEL)), GB_INHERITS("Container"), + + GB_METHOD("_new", NULL, CVBOX_new, "(Parent)Container;"), + + GB_PROPERTY("AutoResize", "b", Container_AutoResize), + GB_PROPERTY("Spacing", "b", Container_Spacing), + GB_PROPERTY("Margin", "b", Container_Margin), + GB_PROPERTY("Padding", "i", Container_Padding), + GB_PROPERTY("Indent", "b", Container_Indent), + GB_PROPERTY("Invert", "b", Container_Invert), + + VBOX_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CHPanelDesc[] = +{ + GB_DECLARE("HPanel", sizeof(CPANEL)), GB_INHERITS("Container"), + + GB_METHOD("_new", NULL, CHPANEL_new, "(Parent)Container;"), + + GB_PROPERTY("AutoResize", "b", Container_AutoResize), + GB_PROPERTY("Spacing", "b", Container_Spacing), + GB_PROPERTY("Margin", "b", Container_Margin), + GB_PROPERTY("Padding", "i", Container_Padding), + GB_PROPERTY("Indent", "b", Container_Indent), + GB_PROPERTY("Invert", "b", Container_Invert), + + HPANEL_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CVPanelDesc[] = +{ + GB_DECLARE("VPanel", sizeof(CPANEL)), GB_INHERITS("Container"), + + GB_METHOD("_new", NULL, CVPANEL_new, "(Parent)Container;"), + + GB_PROPERTY("AutoResize", "b", Container_AutoResize), + GB_PROPERTY("Spacing", "b", Container_Spacing), + GB_PROPERTY("Margin", "b", Container_Margin), + GB_PROPERTY("Padding", "i", Container_Padding), + GB_PROPERTY("Indent", "b", Container_Indent), + GB_PROPERTY("Invert", "b", Container_Invert), + + VPANEL_DESCRIPTION, + + GB_END_DECLARE +}; + + diff --git a/gb.qt4/src/CPanel.h b/gb.qt4/src/CPanel.h new file mode 100644 index 00000000..d9815899 --- /dev/null +++ b/gb.qt4/src/CPanel.h @@ -0,0 +1,67 @@ +/*************************************************************************** + + CPanel.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPANEL_H +#define __CPANEL_H + +#include "gambas.h" + +#include + +#include "CWidget.h" +#include "CContainer.h" + +#ifndef __CPANEL_CPP +extern GB_DESC CPanelDesc[]; +extern GB_DESC CHBoxDesc[]; +extern GB_DESC CVBoxDesc[]; +extern GB_DESC CHPanelDesc[]; +extern GB_DESC CVPanelDesc[]; +#else + +#define THIS ((CPANEL *)_object) +#define WIDGET ((MyPanel *)((CWIDGET *)_object)->widget) + +#endif + +typedef + struct { + CWIDGET widget; + QWidget *container; + CARRANGEMENT arrangement; + } + CPANEL; + +// class MyPanel: public QFrame +// { +// Q_OBJECT +// +// public: +// MyPanel(QWidget *parent); +// void configure(); +// +// protected: +// virtual void resizeEvent(QResizeEvent *); +// }; + +#endif diff --git a/gb.qt4/src/CPicture.cpp b/gb.qt4/src/CPicture.cpp new file mode 100644 index 00000000..847ca5f1 --- /dev/null +++ b/gb.qt4/src/CPicture.cpp @@ -0,0 +1,392 @@ +/*************************************************************************** + + CPicture.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPICTURE_CPP + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "gambas.h" +#include "main.h" + +#include "CDraw.h" +#include "cpaint_impl.h" +#include "CScreen.h" +#include "CImage.h" +#include "CPicture.h" + +#ifdef QT5 +#include +#include +#endif + +#include +#include + + +static CPICTURE *create() +{ + return (CPICTURE *)GB.New(GB.FindClass("Picture"), NULL, NULL); +} + +#define CREATE_IMAGE_FROM_MEMORY(_image, _addr, _len, _ok) \ +{ \ + QImage img; \ + _ok = img.loadFromData((const uchar *)_addr, (uint)_len); \ + if (_ok) \ + { \ + if (img.depth() < 32 && !img.isNull()) \ + img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied); \ + } \ + _image = new QImage(img); \ +} + +#define DELETE_IMAGE(_image) delete _image + +#define CREATE_PICTURE_FROM_IMAGE(_cpicture, _image) \ +{ \ + _cpicture = create(); \ + if ((_image) && !(_image)->isNull()) \ + *((_cpicture)->pixmap) = QPixmap::fromImage(*(_image)); \ +} + + +bool CPICTURE_from_string(QImage **p, const char *addr, int len) +{ + bool ok; + + *p = 0; + CREATE_IMAGE_FROM_MEMORY(*p, addr, len, ok) + return ok; +} + + +bool CPICTURE_load_image(QImage **p, const char *path, int lenp) +{ + char *addr; + int len; + bool ok; + + *p = 0; + + if (GB.LoadFile(path, lenp, &addr, &len)) + { + GB.Error(NULL); + return FALSE; + } + + ok = CPICTURE_from_string(p, addr, len); + + GB.ReleaseFile(addr, len); + return ok; +} + +CPICTURE *CPICTURE_grab(QWidget *wid, int screen, int x, int y, int w, int h) +{ + CPICTURE *pict; + + pict = create(); + + if (!wid) + { + if (w <= 0 || h <= 0) + { + x = 0; y = 0; w = -1; h = -1; + } + +#ifdef QT5 + *pict->pixmap = QGuiApplication::primaryScreen()->grabWindow(QX11Info::appRootWindow(), x, y, w, h); +#else + *pict->pixmap = QPixmap::grabWindow(QX11Info::appRootWindow(), x, y, w, h); +#endif + } + else + { +#ifdef QT5 + *pict->pixmap = QGuiApplication::screens().at(QApplication::desktop()->screenNumber(wid))->grabWindow(wid->winId()); +#else + *pict->pixmap = QPixmap::grabWindow(wid->winId()); +#endif + } + + return pict; +} + + +/*void CPICTURE_update_mask(CPICTURE *_object) +{ + if (THIS->pixmap && THIS->pixmap->hasAlpha()) + THIS->pixmap->setMask(THIS->pixmap->createHeuristicMask()); +}*/ + +CPICTURE *CPICTURE_create(const QPixmap *pixmap) +{ + CPICTURE *pict = create(); + if (pixmap) *pict->pixmap = *pixmap; + return pict; +} + +/******************************************************************************* + + class Picture + +*******************************************************************************/ + + +BEGIN_METHOD(Picture_new, GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN trans) + + int w, h; + + if (!MISSING(w) && !MISSING(h)) + { + w = VARG(w); + h = VARG(h); + if (h <= 0 || w <= 0) + { + GB.Error("Bad dimension"); + return; + } + + THIS->pixmap = new QPixmap(w, h); + + if (VARGOPT(trans, false)) + { + QBitmap b(w, h); + b.fill(Qt::color0); + THIS->pixmap->setMask(b); + } + } + else + THIS->pixmap = new QPixmap; + +END_METHOD + + +BEGIN_METHOD_VOID(Picture_free) + + delete THIS->pixmap; + THIS->pixmap = 0; + +END_METHOD + + +BEGIN_METHOD(Picture_Resize, GB_INTEGER width; GB_INTEGER height) + + QPixmap *pixmap = new QPixmap(VARG(width), VARG(height)); + + QPainter p(pixmap); + p.drawPixmap(0, 0, *THIS->pixmap); + p.end(); + + delete THIS->pixmap; + THIS->pixmap = pixmap; + +END_METHOD + + +BEGIN_PROPERTY(Picture_Width) + + GB.ReturnInteger(THIS->pixmap->width()); + +END_PROPERTY + + +BEGIN_PROPERTY(Picture_Height) + + GB.ReturnInteger(THIS->pixmap->height()); + +END_PROPERTY + + +BEGIN_PROPERTY(Picture_Depth) + + GB.ReturnInteger(THIS->pixmap->depth()); + +END_PROPERTY + + +BEGIN_METHOD(Picture_Load, GB_STRING path) + + CPICTURE *pict; + QImage *img; + + if (!CPICTURE_load_image(&img, STRING(path), LENGTH(path))) + { + GB.Error("Unable to load picture"); + return; + } + + CREATE_PICTURE_FROM_IMAGE(pict, img); + DELETE_IMAGE(img); + GB.ReturnObject(pict); + +END_METHOD + +BEGIN_METHOD(Picture_FromString, GB_STRING data) + + CPICTURE *pict; + QImage *img; + + if (!CPICTURE_from_string(&img, STRING(data), LENGTH(data))) + { + GB.Error("Unable to load picture"); + return; + } + + CREATE_PICTURE_FROM_IMAGE(pict, img); + DELETE_IMAGE(img); + GB.ReturnObject(pict); + +END_METHOD + +BEGIN_METHOD(Picture_Save, GB_STRING path; GB_INTEGER quality) + + QString path = TO_QSTRING(GB.FileName(STRING(path), LENGTH(path))); + bool ok = false; + const char *fmt = CIMAGE_get_format(path); + + if (!fmt) + { + GB.Error("Unknown format"); + return; + } + + ok = THIS->pixmap->save(path, fmt, VARGOPT(quality, -1)); + + if (!ok) + GB.Error("Unable to save picture"); + +END_METHOD + + +BEGIN_METHOD_VOID(Picture_Clear) + + delete THIS->pixmap; + THIS->pixmap = new QPixmap; + +END_METHOD + + +BEGIN_METHOD(Picture_Fill, GB_INTEGER col) + + int col = VARG(col); + QBitmap mask; + + THIS->pixmap->fill(QColor(QRgb(col & 0xFFFFFF))); + +END_METHOD + + +BEGIN_METHOD(Picture_Copy, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + CPICTURE *pict; + int x = VARGOPT(x, 0); + int y = VARGOPT(y, 0); + int w = VARGOPT(w, THIS->pixmap->width()); + int h = VARGOPT(h, THIS->pixmap->height()); + + pict = create(); + delete pict->pixmap; + pict->pixmap = new QPixmap(w, h); + + QPainter p(pict->pixmap); + p.drawPixmap(0, 0, *THIS->pixmap, x, y, w, h); + p.end(); + + GB.ReturnObject(pict); + +END_METHOD + + +BEGIN_PROPERTY(Picture_Image) + + QImage *image = new QImage(); + + *image = THIS->pixmap->toImage(); + image->detach(); + + GB.ReturnObject(CIMAGE_create(image)); + +END_PROPERTY + + +BEGIN_PROPERTY(Picture_Transparent) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->pixmap->hasAlpha()); + else + { + bool a = THIS->pixmap->hasAlpha(); + + if (a == VPROP(GB_BOOLEAN)) + return; + + if (a) + THIS->pixmap->setMask(QBitmap()); + else + THIS->pixmap->setMask(THIS->pixmap->createHeuristicMask()); + } + +END_PROPERTY + + +GB_DESC CPictureDesc[] = +{ + GB_DECLARE("Picture", sizeof(CPICTURE)), + + GB_METHOD("_new", NULL, Picture_new, "[(Width)i(Height)i(Transparent)b]"), + GB_METHOD("_free", NULL, Picture_free, NULL), + + GB_PROPERTY_READ("W", "i", Picture_Width), + GB_PROPERTY_READ("Width", "i", Picture_Width), + GB_PROPERTY_READ("H", "i", Picture_Height), + GB_PROPERTY_READ("Height", "i", Picture_Height), + GB_PROPERTY_READ("Depth", "i", Picture_Depth), + + GB_STATIC_METHOD("Load", "Picture", Picture_Load, "(Path)s"), + GB_STATIC_METHOD("FromString", "Picture", Picture_FromString, "(Data)s"), + GB_METHOD("Save", NULL, Picture_Save, "(Path)s[(Quality)i]"), + GB_METHOD("Resize", NULL, Picture_Resize, "(Width)i(Height)i"), + + GB_METHOD("Clear", NULL, Picture_Clear, NULL), + GB_METHOD("Fill", NULL, Picture_Fill, "(Color)i"), + //GB_METHOD("Mask", NULL, CPICTURE_mask, "[(Color)i]"), + + GB_PROPERTY("Transparent", "b", Picture_Transparent), + + GB_METHOD("Copy", "Picture", Picture_Copy, "[(X)i(Y)i(Width)i(Height)i]"), + GB_PROPERTY_READ("Image", "Image", Picture_Image), + + //GB_INTERFACE("Draw", &DRAW_Interface), + GB_INTERFACE("Paint", &PAINT_Interface), + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/CPicture.h b/gb.qt4/src/CPicture.h new file mode 100644 index 00000000..b4bbcd18 --- /dev/null +++ b/gb.qt4/src/CPicture.h @@ -0,0 +1,78 @@ +/*************************************************************************** + + CPicture.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPICTURE_H +#define __CPICTURE_H + +#include +#include + +#include "gambas.h" + +typedef + struct { + GB_BASE ob; + QPixmap *pixmap; + } + CPICTURE; + +#ifndef __CPICTURE_CPP + +extern GB_DESC CPictureDesc[]; +//extern GB_DESC CPicturePixelsDesc[]; + +#else + + +#define THIS OBJECT(CPICTURE) + +#endif + +#if 0 +#define CPICTURE_is_pixmap(_pict) ((_pict)->pixmap != 0) +#define CPICTURE_is_picture(_pict) ((_pict)->picture != 0) +#define CPICTURE_is_image(_pict) ((_pict)->image != 0) +#endif + +#define SET_PIXMAP(_method_pixmap, _store, _object) \ +{ \ + CPICTURE *_pict = (CPICTURE *)((_object) ? (_object)->value : NULL); \ + GB.StoreObject(_object, POINTER(_store)); \ + if (_pict && !((_pict)->pixmap->isNull())) \ + _method_pixmap(*((_pict)->pixmap)); \ + else \ + _method_pixmap(QPixmap()); \ +} + +#define SET_PICTURE(_method_pixmap, _method_picture, _store, _object) \ + SET_PIXMAP(_method_pixmap, _store, _object) + +#define CLEAR_PICTURE(_store) GB.StoreObject(NULL, (void **)(void *)_store) + +CPICTURE *CPICTURE_grab(QWidget *wid, int screen = -1, int x = 0, int y = 0, int w = -1, int h = -1); +CPICTURE *CPICTURE_get_picture(const char *path); +bool CPICTURE_load_image(QImage **p, const char *path, int lenp); +bool CPICTURE_from_string(QImage **p, const char *addr, int len); +CPICTURE *CPICTURE_create(const QPixmap *pixmap); + +#endif diff --git a/gb.qt4/src/CRadioButton.cpp b/gb.qt4/src/CRadioButton.cpp new file mode 100644 index 00000000..2dafbfc9 --- /dev/null +++ b/gb.qt4/src/CRadioButton.cpp @@ -0,0 +1,195 @@ +/*************************************************************************** + + CRadioButton.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CRADIOBUTTON_CPP + +#include +#include +#include + +#include "gambas.h" + +#include "CStyle.h" +#include "CRadioButton.h" + + +/** MyRadioButton **********************************************************/ + +MyRadioButton::MyRadioButton(QWidget *parent) : QRadioButton(parent) +{ + _autoResize = false; +} + + +void MyRadioButton::changeEvent(QEvent *e) +{ + QRadioButton::changeEvent(e); + if (e->type() == QEvent::FontChange || e->type() == QEvent::StyleChange) + adjust(); +} + +void MyRadioButton::adjust(bool force) +{ + void *_object = CWidget::getReal(this); + bool a; + QSize hint; + + if (!THIS || (!_autoResize && !force) || CWIDGET_test_flag(THIS, WF_DESIGN) || text().length() <= 0) + return; + + a = _autoResize; + _autoResize = false; + hint = sizeHint(); + CWIDGET_resize(THIS, hint.width(), qMax(hint.height(), height())); + _autoResize = a; +} + +void MyRadioButton::resizeEvent(QResizeEvent *e) +{ + QRadioButton::resizeEvent(e); + + if (_autoResize && e->oldSize().width() != e->size().width()) + adjust(); +} + +/** RadioButton ************************************************************/ + +DECLARE_EVENT(EVENT_Click); + + +BEGIN_METHOD(RadioButton_new, GB_OBJECT parent) + + MyRadioButton *wid = new MyRadioButton(QCONTAINER(VARG(parent))); + + QObject::connect(wid, SIGNAL(toggled(bool)), &CRadioButton::manager, SLOT(clicked(bool))); + + CWIDGET_new(wid, (void *)_object); + THIS->widget.flag.fillBackground = CSTYLE_fix_breeze; + +END_METHOD + + +BEGIN_PROPERTY(RadioButton_Text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->text()); + else + { + WIDGET->setText(QSTRING_PROP()); + WIDGET->adjust(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(RadioButton_Value) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isChecked()); + else + WIDGET->setChecked(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(RadioButton_AutoResize) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isAutoResize()); + else + WIDGET->setAutoResize(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +// BEGIN_METHOD_VOID(RadioButton_Adjust) +// +// WIDGET->adjust(true); +// +// END_METHOD + + +GB_DESC CRadioButtonDesc[] = +{ + GB_DECLARE("RadioButton", sizeof(CRADIOBUTTON)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, RadioButton_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", RadioButton_Text), + GB_PROPERTY("Caption", "s", RadioButton_Text), + GB_PROPERTY("Value", "b", RadioButton_Value), + GB_PROPERTY("AutoResize", "b", RadioButton_AutoResize), + + GB_EVENT("Click", NULL, NULL, &EVENT_Click), + + RADIOBUTTON_DESCRIPTION, + + GB_END_DECLARE +}; + + +/** CCheckBox **************************************************************/ + +CRadioButton CRadioButton::manager; + +void CRadioButton::clicked(bool on) +{ + QRadioButton *wid = (QRadioButton *)sender(); + GET_SENDER(); + QObject *parent = wid->parent(); + + QList list = parent->findChildren(); + int i; + QRadioButton *obj = 0; + + if (on) + { + for (i = 0; i < list.count(); i++) + { + obj = list.at(i); + if (obj != wid && obj->isChecked()) + { + obj->setChecked(false); + on = false; + } + } + + //if (!on) + GB.Raise(THIS, EVENT_Click, 0); + } + else + { + for (i = 0; i < list.count(); i++) + { + obj = list.at(i); + if (obj->isChecked()) + break; + } + + if (!obj) + wid->setChecked(true); + //else + // RAISE_EVENT(EVENT_Click); + } +} + diff --git a/gb.qt4/src/CRadioButton.h b/gb.qt4/src/CRadioButton.h new file mode 100644 index 00000000..f2f65c1d --- /dev/null +++ b/gb.qt4/src/CRadioButton.h @@ -0,0 +1,83 @@ +/*************************************************************************** + + CRadioButton.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CRADIOBUTTON_H +#define __CRADIOBUTTON_H + +#include "gambas.h" + +#include + +#include "CWidget.h" + +#ifndef __CRADIOBUTTON_CPP +extern GB_DESC CRadioButtonDesc[]; +#else + +#define THIS ((CRADIOBUTTON *)_object) +#define WIDGET ((MyRadioButton *)((CWIDGET *)_object)->widget) + +#endif + +typedef + struct { + CWIDGET widget; + } + CRADIOBUTTON; + +class MyRadioButton : public QRadioButton +{ + Q_OBJECT + +public: + + MyRadioButton(QWidget *parent); + void adjust(bool force = false); + bool isAutoResize() const { return _autoResize; } + void setAutoResize(bool a) { _autoResize = a; adjust(); } + +protected: + + virtual void changeEvent(QEvent *); + virtual void resizeEvent(QResizeEvent *); + +private: + + unsigned _autoResize : 1; +}; + +class CRadioButton : public QObject +{ + Q_OBJECT + +public: + + static CRadioButton manager; + +public slots: + + void clicked(bool on); + +}; + +#endif diff --git a/gb.qt4/src/CScreen.cpp b/gb.qt4/src/CScreen.cpp new file mode 100644 index 00000000..0972f38b --- /dev/null +++ b/gb.qt4/src/CScreen.cpp @@ -0,0 +1,498 @@ +/*************************************************************************** + + CScreen.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSCREEN_CPP + +#include +#include +#include +#include +#include + +#include "gambas.h" +#include "main.h" +#include "gb.draw.h" +#include "cpaint_impl.h" +#include "CPicture.h" +#include "CWidget.h" +#include "CWindow.h" +#include "CFont.h" +#include "CDrawingArea.h" +#include "CScreen.h" + +#include +#include "x11.h" +#include "desktop.h" + +#if QT_VERSION >= 0x040600 +#define NUM_SCREENS() (QApplication::desktop()->screenCount()) +#else +#define NUM_SCREENS() (QApplication::desktop()->numScreens()) +#endif + +#define MAX_SCREEN 16 + +char *CAPPLICATION_Theme = 0; +GB_ARRAY CAPPLICATION_Restart = NULL; + +static int screen_busy = 0; +static CSCREEN *_screens[MAX_SCREEN] = { NULL }; + +static bool _animations = FALSE; +static bool _shadows = FALSE; + +static CSCREEN *get_screen(int num) +{ + if (num < 0 || num >= MAX_SCREEN || num >= NUM_SCREENS()) + { + GB.Error(GB_ERR_ARG); + return NULL; + } + + if (!_screens[num]) + { + _screens[num] = (CSCREEN *)GB.New(GB.FindClass("Screen"), NULL, 0); + _screens[num]->index = num; + GB.Ref(_screens[num]); + } + + return _screens[num]; +} + +static void free_screens(void) +{ + int i; + + for (i = 0; i < MAX_SCREEN; i++) + { + if (_screens[i]) + GB.Unref(POINTER(&_screens[i])); + } +} + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Desktop_X) + + GB.ReturnInteger(QApplication::desktop()->availableGeometry().x()); + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Y) + + GB.ReturnInteger(QApplication::desktop()->availableGeometry().y()); + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Width) + + GB.ReturnInteger(QApplication::desktop()->availableGeometry().width()); + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Height) + + GB.ReturnInteger(QApplication::desktop()->availableGeometry().height()); + +END_PROPERTY + + +BEGIN_PROPERTY(Desktop_Resolution) + + #ifdef NO_X_WINDOW + GB.ReturnInteger(72); + #else + GB.ReturnInteger(QX11Info::appDpiY()); + #endif + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_HasSystemTray) + + #ifdef NO_X_WINDOW + GB.Return(FALSE); + #else + GB.ReturnBoolean(QSystemTrayIcon::isSystemTrayAvailable()); + //GB.ReturnBoolean(X11_get_system_tray() != None); + #endif + +END_PROPERTY + +BEGIN_METHOD(Desktop_Screenshot, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + GB.ReturnObject(CPICTURE_grab(0, -1, VARGOPT(x, 0), VARGOPT(y, 0), VARGOPT(w, 0), VARGOPT(h, 0))); + +END_METHOD + + +BEGIN_PROPERTY(Desktop_Scale) + + GB.ReturnInteger(MAIN_scale); + +END_PROPERTY + +BEGIN_PROPERTY(Desktop_Type) + + GB.ReturnConstZeroString(DESKTOP_get_type()); + +END_PROPERTY + +//------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Application_exit) + + GB.FreeString(&CAPPLICATION_Theme); + GB.StoreObject(NULL, POINTER(&CAPPLICATION_Restart)); + free_screens(); + +END_METHOD + +static void set_font(QFont &font, void *object = 0) +{ + MAIN_update_scale(font); + qApp->setFont(font); +} + +BEGIN_PROPERTY(Application_Font) + + if (READ_PROPERTY) + GB.ReturnObject(CFONT_create(qApp->font(), set_font)); + else + CFONT_set(set_font, (CFONT *)VPROP(GB_OBJECT), NULL); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_ActiveWindow) + + //GB.ReturnObject(CWidget::get(qApp->activeWindow())); + GB.ReturnObject(CWINDOW_Active); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_ActiveControl) + + GB.ReturnObject(CWIDGET_active_control); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_PreviousControl) + + GB.ReturnObject(CWIDGET_previous_control); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Busy) + + int busy; + + if (READ_PROPERTY) + GB.ReturnInteger(screen_busy); + else + { + busy = VPROP(GB_INTEGER); + + if (screen_busy == 0 && busy > 0) + qApp->setOverrideCursor(Qt::WaitCursor); + else if (screen_busy > 0 && busy == 0) + qApp->restoreOverrideCursor(); + + screen_busy = busy; + if (MAIN_debug_busy) + qDebug("%s: Application.Busy = %d", GB.Debug.GetCurrentPosition(), busy); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_ShowTooltips) + + if (READ_PROPERTY) + GB.ReturnBoolean(MyApplication::isTooltipEnabled()); + else + MyApplication::setTooltipEnabled(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Animations) + + if (READ_PROPERTY) + GB.ReturnBoolean(_animations); + else if (_animations != VPROP(GB_BOOLEAN)) + { + _animations = VPROP(GB_BOOLEAN); + CDRAWINGAREA_send_change_event(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Shadows) + + if (READ_PROPERTY) + GB.ReturnBoolean(_shadows); + else if (_shadows != VPROP(GB_BOOLEAN)) + { + _shadows = VPROP(GB_BOOLEAN); + CDRAWINGAREA_send_change_event(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_MainWindow) + + if (READ_PROPERTY) + GB.ReturnObject(CWINDOW_Main); + else + { + CWINDOW_Main = (CWINDOW *)VPROP(GB_OBJECT); + if (CWINDOW_Main && CWINDOW_MainDesktop >= 0) + { + MyMainWindow *win = (MyMainWindow *)CWINDOW_Main->widget.widget; + X11_window_set_desktop(win->winId(), win->isVisible(), CWINDOW_MainDesktop); + CWINDOW_MainDesktop = -1; + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Embedder) + +#ifdef QT5 + if (READ_PROPERTY) + GB.ReturnInteger(0); + else + GB.Deprecated("gb.qt5", "Application.Embedder", NULL); +#else + if (READ_PROPERTY) + GB.ReturnInteger(CWINDOW_Embedder); + else + { + if (CWINDOW_Embedded) + { + GB.Error("Application is already embedded"); + return; + } + + CWINDOW_Embedder = VPROP(GB_INTEGER); + } + +#endif + +END_PROPERTY + +BEGIN_PROPERTY(Application_Theme) + + if (READ_PROPERTY) + GB.ReturnString(CAPPLICATION_Theme); + else + GB.StoreString(PROP(GB_STRING), &CAPPLICATION_Theme); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Restart) + + if (READ_PROPERTY) + GB.ReturnObject(CAPPLICATION_Restart); + else + GB.StoreObject(PROP(GB_OBJECT), POINTER(&CAPPLICATION_Restart)); + +END_PROPERTY + +BEGIN_PROPERTY(Application_DblClickTime) + + GB.ReturnInteger(QApplication::doubleClickInterval()); + +END_PROPERTY + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Screens_Count) + + GB.ReturnInteger(NUM_SCREENS()); + +END_PROPERTY + + +/*BEGIN_PROPERTY(Screens_Primary) + + GB.ReturnInteger(QApplication::desktop()->primaryScreen()); + +END_PROPERTY*/ + + +BEGIN_METHOD(Screens_get, GB_INTEGER screen) + + GB.ReturnObject(get_screen(VARG(screen))); + +END_METHOD + + +BEGIN_METHOD_VOID(Screens_next) + + int *index = (int *)GB.GetEnum(); + + if (*index >= NUM_SCREENS()) + GB.StopEnum(); + else + { + GB.ReturnObject(get_screen(*index)); + (*index)++; + } + +END_METHOD + + +BEGIN_PROPERTY(Screen_X) + + GB.ReturnInteger(QApplication::desktop()->screenGeometry(SCREEN->index).x()); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Y) + + GB.ReturnInteger(QApplication::desktop()->screenGeometry(SCREEN->index).y()); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Width) + + GB.ReturnInteger(QApplication::desktop()->screenGeometry(SCREEN->index).width()); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_Height) + + GB.ReturnInteger(QApplication::desktop()->screenGeometry(SCREEN->index).height()); + +END_PROPERTY + + +BEGIN_PROPERTY(Screen_AvailableX) + + GB.ReturnInteger(QApplication::desktop()->availableGeometry(SCREEN->index).x()); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_AvailableY) + + GB.ReturnInteger(QApplication::desktop()->availableGeometry(SCREEN->index).y()); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_AvailableWidth) + + GB.ReturnInteger(QApplication::desktop()->availableGeometry(SCREEN->index).width()); + +END_PROPERTY + +BEGIN_PROPERTY(Screen_AvailableHeight) + + GB.ReturnInteger(QApplication::desktop()->availableGeometry(SCREEN->index).height()); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC ScreenDesc[] = +{ + GB_DECLARE("Screen", sizeof(CSCREEN)), GB_NOT_CREATABLE(), GB_AUTO_CREATABLE(), + + GB_PROPERTY_READ("X", "i", Screen_X), + GB_PROPERTY_READ("Y", "i", Screen_Y), + GB_PROPERTY_READ("W", "i", Screen_Width), + GB_PROPERTY_READ("H", "i", Screen_Height), + GB_PROPERTY_READ("Width", "i", Screen_Width), + GB_PROPERTY_READ("Height", "i", Screen_Height), + + GB_PROPERTY_READ("AvailableX", "i", Screen_AvailableX), + GB_PROPERTY_READ("AvailableY", "i", Screen_AvailableY), + GB_PROPERTY_READ("AvailableWidth", "i", Screen_AvailableWidth), + GB_PROPERTY_READ("AvailableHeight", "i", Screen_AvailableHeight), + + GB_END_DECLARE +}; + +GB_DESC ScreensDesc[] = +{ + GB_DECLARE("Screens", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY_READ("Count", "i", Screens_Count), + GB_STATIC_METHOD("_get", "Screen", Screens_get, "(Screen)i"), + GB_STATIC_METHOD("_next", "Screen", Screens_next, NULL), + + GB_END_DECLARE +}; + +GB_DESC DesktopDesc[] = +{ + GB_DECLARE("Desktop", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY_READ("X", "i", Desktop_X), + GB_STATIC_PROPERTY_READ("Y", "i", Desktop_Y), + GB_STATIC_PROPERTY_READ("W", "i", Desktop_Width), + GB_STATIC_PROPERTY_READ("H", "i", Desktop_Height), + GB_STATIC_PROPERTY_READ("Width", "i", Desktop_Width), + GB_STATIC_PROPERTY_READ("Height", "i", Desktop_Height), + + GB_CONSTANT("Charset", "s", "UTF-8"), + GB_STATIC_PROPERTY_READ("Resolution", "i", Desktop_Resolution), + GB_STATIC_PROPERTY_READ("Scale", "i", Desktop_Scale), + + GB_STATIC_PROPERTY_READ("HasSystemTray", "b", Desktop_HasSystemTray), + + GB_STATIC_METHOD("Screenshot", "Picture", Desktop_Screenshot, "[(X)i(Y)i(Width)i(Height)i]"), + + GB_STATIC_PROPERTY_READ("Type", "s", Desktop_Type), + + GB_END_DECLARE +}; + +GB_DESC ApplicationDesc[] = +{ + GB_DECLARE("Application", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_exit", NULL, Application_exit, NULL), + GB_STATIC_PROPERTY("Font", "Font", Application_Font), + GB_STATIC_PROPERTY_READ("ActiveWindow", "Window", Application_ActiveWindow), + GB_STATIC_PROPERTY_READ("ActiveControl", "Control", Application_ActiveControl), + GB_STATIC_PROPERTY_READ("PreviousControl", "Control", Application_PreviousControl), + GB_STATIC_PROPERTY("MainWindow", "Window", Application_MainWindow), + GB_STATIC_PROPERTY("Busy", "i", Application_Busy), + GB_STATIC_PROPERTY("ShowTooltips", "b", Application_ShowTooltips), + GB_STATIC_PROPERTY("Animations", "b", Application_Animations), + GB_STATIC_PROPERTY("Shadows", "b", Application_Shadows), + GB_STATIC_PROPERTY("Embedder", "i", Application_Embedder), + GB_STATIC_PROPERTY("Theme", "s", Application_Theme), + GB_STATIC_PROPERTY("Restart", "String[]", Application_Restart), + GB_STATIC_PROPERTY_READ("DblClickTime", "i", Application_DblClickTime), + + GB_END_DECLARE +}; diff --git a/gb.qt4/src/CScreen.h b/gb.qt4/src/CScreen.h new file mode 100644 index 00000000..55168a6c --- /dev/null +++ b/gb.qt4/src/CScreen.h @@ -0,0 +1,53 @@ +/*************************************************************************** + + CScreen.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSCREEN_H +#define __CSCREEN_H + +#include "gambas.h" + +#ifndef __CSCREEN_CPP +extern GB_DESC DesktopDesc[]; +extern GB_DESC ApplicationSessionDesc[]; +extern GB_DESC ApplicationDesc[]; +extern GB_DESC ScreensDesc[]; +extern GB_DESC ScreenDesc[]; +extern GB_DESC StyleDesc[]; + +extern char *CAPPLICATION_Theme; +extern GB_ARRAY CAPPLICATION_Restart; +#else + +#define SCREEN ((CSCREEN *)_object) + +#endif + +typedef + struct + { + GB_BASE ob; + int index; + } + CSCREEN; + +#endif diff --git a/gb.qt4/src/CScrollBar.cpp b/gb.qt4/src/CScrollBar.cpp new file mode 100644 index 00000000..b37a0578 --- /dev/null +++ b/gb.qt4/src/CScrollBar.cpp @@ -0,0 +1,226 @@ +/*************************************************************************** + + CScrollBar.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSCROLLBAR_CPP + +#include "main.h" +#include "gambas.h" + +#include +#include +#include +#include + +#include "CWidget.h" +#include "CScrollBar.h" + +DECLARE_EVENT(EVENT_Change); + +/*************************************************************************** + + class MyScrollBar + +***************************************************************************/ + + +MyScrollBar::MyScrollBar(QWidget *parent) +: QScrollBar(parent) +{ +} + + +void MyScrollBar::resizeEvent(QResizeEvent *e) +{ + //CSCROLLVIEW *ob = (CSCROLLVIEW *)CWidget::get(this); + + QScrollBar::resizeEvent(e); + + if (width() >= height()) + setOrientation(Qt::Horizontal); + else + setOrientation(Qt::Vertical); +} + + +/*************************************************************************** + + ScrollBar + +***************************************************************************/ + +BEGIN_METHOD(CSCROLLBAR_new, GB_OBJECT parent) + + MyScrollBar *wid = new MyScrollBar(QCONTAINER(VARG(parent))); + + THIS->widget.flag.wheel = true; + + QObject::connect(wid, SIGNAL(valueChanged(int)), &CScrollBar::manager, + SLOT(event_change())); + + wid->setTracking(true); + wid->setMinimum(0); + wid->setMaximum(100); + wid->setSingleStep(1); + wid->setPageStep(10); + + CWIDGET_new(wid, _object); + +END_METHOD + + +BEGIN_PROPERTY(CSCROLLBAR_tracking) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->hasTracking()); + else + WIDGET->setTracking(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +/* +BEGIN_PROPERTY(CSCROLLBAR_orientation) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->orientation()); + else + { + switch PROPERTY(GB_INTEGER) + { + case Qt::Vertical: WIDGET->setOrientation(Qt::Vertical);break; + case Qt::Horizontal: WIDGET->setOrientation(Qt::Horizontal);break; + default: WIDGET->setOrientation(Qt::Vertical); + } + } + +END_PROPERTY +*/ + +BEGIN_PROPERTY(CSCROLLBAR_value) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->value()); + else + WIDGET->setValue(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CSCROLLBAR_minval) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->minimum()); + else + WIDGET->setMinimum(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CSCROLLBAR_maxval) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->maximum()); + else + WIDGET->setMaximum(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CSCROLLBAR_linestep) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->singleStep()); + else + WIDGET->setSingleStep(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CSCROLLBAR_pagestep) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->pageStep()); + else + WIDGET->setPageStep(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(ScrollBar_DefaultSize) + + GB.ReturnInteger(WIDGET->style()->pixelMetric(QStyle::PM_ScrollBarExtent)); + +END_PROPERTY + +/*************************************************************************** + + class CScrollBar + +***************************************************************************/ + +CScrollBar CScrollBar::manager; + +void CScrollBar::event_change(void) +{ + GET_SENDER(); + GB.Raise(THIS, EVENT_Change, 0); +} + +/*void CScrollBar::event_sliderpressed(void) +{ + void *object = QT.GetObject((QWidget *)sender()); + GB.Raise(object, EVENT_SliderPressed, NULL); +} */ + +/*void CScrollBar::event_slidermove(void) +{ + void *object = QT.GetObject((QWidget *)sender()); + GB.Raise(object, EVENT_SliderMove, NULL); +}*/ +/* +void CScrollBar::event_sliderreleased(void) +{ + void *object = QT.GetObject((QWidget *)sender()); + GB.Raise(object, EVENT_SliderReleased, NULL); +} +*/ + +GB_DESC CScrollBarDesc[] = +{ + GB_DECLARE("ScrollBar", sizeof(CSCROLLBAR)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CSCROLLBAR_new, "(Parent)Container;"), + + GB_PROPERTY_READ("DefaultSize", "i", ScrollBar_DefaultSize), + + //GB_CONSTANT("Vertical","i", Qt::Vertical), + //GB_CONSTANT("Horizontal","i", Qt::Horizontal), + GB_PROPERTY("Tracking", "b", CSCROLLBAR_tracking), + GB_PROPERTY("Value", "i", CSCROLLBAR_value), + GB_PROPERTY("MinValue", "i", CSCROLLBAR_minval), + GB_PROPERTY("MaxValue", "i", CSCROLLBAR_maxval), + GB_PROPERTY("Step", "i", CSCROLLBAR_linestep), + GB_PROPERTY("PageStep", "i", CSCROLLBAR_pagestep), + //GB_PROPERTY("Orientation", "i", CSCROLLBAR_orientation), + + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + + SCROLLBAR_DESCRIPTION, + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/CScrollBar.h b/gb.qt4/src/CScrollBar.h new file mode 100644 index 00000000..56b7b805 --- /dev/null +++ b/gb.qt4/src/CScrollBar.h @@ -0,0 +1,81 @@ +/*************************************************************************** + + CScrollBar.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSCROLLBAR_H +#define __CSCROLLBAR_H + +#include +//Added by qt3to4: +#include +#include "gb.qt.h" +#include "gambas.h" +#include "CWidget.h" + +#ifndef __CSCROLLBAR_CPP + +extern GB_DESC CScrollBarDesc[]; + +#else + +#define THIS ((CSCROLLBAR *)_object) +#define WIDGET ((QScrollBar *)((QT_WIDGET *)_object)->widget) + +#endif + + +typedef + struct { + CWIDGET widget; + } + CSCROLLBAR; + + +class MyScrollBar : public QScrollBar +{ + Q_OBJECT + +public: + + MyScrollBar(QWidget *); + +protected: + + void resizeEvent(QResizeEvent *); +}; + + +class CScrollBar : public QObject +{ + Q_OBJECT + +public: + + static CScrollBar manager; + +public slots: + + void event_change(void); + +}; + +#endif diff --git a/gb.qt4/src/CSlider.cpp b/gb.qt4/src/CSlider.cpp new file mode 100644 index 00000000..18244f25 --- /dev/null +++ b/gb.qt4/src/CSlider.cpp @@ -0,0 +1,283 @@ +/*************************************************************************** + + CSlider.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSLIDER_CPP + +#include "main.h" +#include "gambas.h" + +#include +#include +//Added by qt3to4: +#include + +#include "CWidget.h" +#include "CSlider.h" + +DECLARE_EVENT(EVENT_Change); + + +/*************************************************************************** + + class MySlider + +***************************************************************************/ + + +MySlider::MySlider(QWidget *parent) +: QSlider(parent) +{ +} + + +void MySlider::resizeEvent(QResizeEvent *e) +{ + //CSCROLLVIEW *ob = (CSCROLLVIEW *)CWidget::get(this); + + QSlider::resizeEvent(e); + + if (width() >= height()) + setOrientation(Qt::Horizontal); + else + setOrientation(Qt::Vertical); +} + + +/*************************************************************************** + + Slider + +***************************************************************************/ + +BEGIN_METHOD(CSLIDER_new, GB_OBJECT parent) + + MySlider *wid = new MySlider(QCONTAINER(VARG(parent))); + + THIS->widget.flag.wheel = true; + + QObject::connect(wid, SIGNAL(valueChanged(int)), &CSlider::manager, SLOT(event_change())); + //QObject::connect(wid, SIGNAL(sliderPressed()), &CSlider::manager, + //SLOT(event_sliderpressed())); + //QObject::connect(wid, SIGNAL(sliderMoved(int)), &CSlider::manager, + //SLOT(event_slidermove())); + //QObject::connect(wid, SIGNAL(sliderReleased()), &CSlider::manager, + //SLOT(event_sliderreleased())); + + wid->setTracking(true); //Set the tracking off by default + wid->setMinimum(0); + wid->setMaximum(100); + wid->setSingleStep(1); + wid->setPageStep(10); + + CWIDGET_new(wid, _object); + +END_METHOD + + +BEGIN_PROPERTY(CSLIDER_tracking) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->hasTracking()); + else + WIDGET->setTracking(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +/*BEGIN_PROPERTY(CSLIDER_tickinterval) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->tickInterval()); + else + WIDGET->setTickInterval(VPROP(GB_INTEGER)); + +END_PROPERTY*/ + +/* +BEGIN_PROPERTY(CSLIDER_orientation) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->orientation()); + else + { + switch PROPERTY(GB_INTEGER) + { + case Qt::Vertical: WIDGET->setOrientation(Qt::Vertical);break; + case Qt::Horizontal: WIDGET->setOrientation(Qt::Horizontal);break; + default: WIDGET->setOrientation(Qt::Vertical); + } + } + +END_PROPERTY +*/ + +BEGIN_PROPERTY(CSLIDER_value) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->value()); + else + WIDGET->setValue(VPROP(GB_INTEGER)); + +END_PROPERTY + +/* +BEGIN_PROPERTY(CSLIDER_tickmarks) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->tickmarks()); + else + { + switch PROPERTY(GB_INTEGER) + { + case QSlider::NoMarks: WIDGET->setTickmarks(QSlider::NoMarks);break; + case QSlider::Both: WIDGET->setTickmarks(QSlider::Both);break; + case QSlider::Above: WIDGET->setTickmarks(QSlider::Above);break; + case QSlider::Below: WIDGET->setTickmarks(QSlider::Below);break; + //case QSlider::Left: WIDGET->setTickmarks(QSlider::Left);break; + //case QSlider::Right: WIDGET->setTickmarks(QSlider::Right);break; + default: WIDGET->setTickmarks(QSlider::NoMarks); + } + } + +END_PROPERTY +*/ + + +BEGIN_PROPERTY(CSLIDER_mark) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->tickPosition() != QSlider::NoTicks); + else + { + if (VPROP(GB_BOOLEAN)) + WIDGET->setTickPosition(QSlider::TicksBothSides); + else + WIDGET->setTickPosition(QSlider::NoTicks); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CSLIDER_minval) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->minimum()); + else + WIDGET->setMinimum(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CSLIDER_maxval) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->maximum()); + else + WIDGET->setMaximum(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CSLIDER_linestep) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->singleStep()); + else + WIDGET->setSingleStep(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CSLIDER_pagestep) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->pageStep()); + else + { + WIDGET->setPageStep(VPROP(GB_INTEGER)); + WIDGET->update(); + } + +END_PROPERTY + + +/*************************************************************************** + + class CSlider + +***************************************************************************/ + +CSlider CSlider::manager; + +void CSlider::event_change(void) +{ + GET_SENDER(); + GB.Raise(THIS, EVENT_Change, 0); +} + +/*void CSlider::event_sliderpressed(void) +{ + void *object = QT.GetObject((QWidget *)sender()); + GB.Raise(object, EVENT_SliderPressed, NULL); +}*/ + +/*void CSlider::event_slidermove(void) +{ + void *object = QT.GetObject((QWidget *)sender()); + GB.Raise(object, EVENT_SliderMove, NULL); +}*/ + +/* +void CSlider::event_sliderreleased(void) +{ + void *object = QT.GetObject((QWidget *)sender()); + GB.Raise(object, EVENT_SliderReleased, NULL); +} +*/ + +GB_DESC CSliderDesc[] = +{ + GB_DECLARE("Slider", sizeof(CSLIDER)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CSLIDER_new, "(Parent)Container;"), + + //GB_CONSTANT("NoMarks","i", QSlider::NoMarks), + //GB_CONSTANT("Both","i", QSlider::Both), + //GB_CONSTANT("AboveOrLeft","i", QSlider::Above), //when orientation is horizontal Above is valid, when Vertical Left + //GB_CONSTANT("BelowOrRight","i", QSlider::Below), + //GB_CONSTANT("Vertical","i", Qt::Vertical), + //GB_CONSTANT("Horizontal","i", Qt::Horizontal), + GB_PROPERTY("Tracking", "b", CSLIDER_tracking), + GB_PROPERTY("Value", "i", CSLIDER_value), + //GB_PROPERTY("MarkGap", "i", CSLIDER_tickinterval), + GB_PROPERTY("Mark", "b", CSLIDER_mark), + GB_PROPERTY("MinValue", "i", CSLIDER_minval), + GB_PROPERTY("MaxValue", "i", CSLIDER_maxval), + GB_PROPERTY("Step", "i", CSLIDER_linestep), + GB_PROPERTY("PageStep", "i", CSLIDER_pagestep), + //GB_PROPERTY("Orientation", "i", CSLIDER_orientation), + + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + + SLIDER_DESCRIPTION, + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/CSlider.h b/gb.qt4/src/CSlider.h new file mode 100644 index 00000000..d6e1a4d9 --- /dev/null +++ b/gb.qt4/src/CSlider.h @@ -0,0 +1,81 @@ +/*************************************************************************** + + CSlider.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSLIDER_H +#define __CSLIDER_H + +#include "gambas.h" +#include "CWidget.h" +#include +#include + +#ifndef __CSLIDER_CPP + +extern GB_DESC CSliderDesc[]; + +#else + +#define THIS ((CSLIDER *)_object) +#define WIDGET ((QSlider *)((QT_WIDGET *)_object)->widget) + +#endif + +typedef + struct { + CWIDGET widget; + } + CSLIDER; + +class MySlider : public QSlider +{ + Q_OBJECT + +public: + + MySlider(QWidget *); + +protected: + + void resizeEvent(QResizeEvent *); +}; + + + +class CSlider : public QObject +{ + Q_OBJECT + +public: + + static CSlider manager; + +public slots: + + void event_change(void); + //void event_sliderpressed(void); + //void event_slidermove(void); + //void event_sliderreleased(void); + +}; + +#endif diff --git a/gb.qt4/src/CStyle.cpp b/gb.qt4/src/CStyle.cpp new file mode 100644 index 00000000..3969cf85 --- /dev/null +++ b/gb.qt4/src/CStyle.cpp @@ -0,0 +1,526 @@ +/*************************************************************************** + + CStyle.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CStyle_CPP + +#include +#include + +#include "gambas.h" +#include "main.h" +#include "gb.draw.h" +#include "cpaint_impl.h" +#include "CPicture.h" +#include "CWidget.h" +#include "CWindow.h" +#include "CFont.h" +#include "CScreen.h" + +#ifndef NO_X_WINDOW +#include +#include "x11.h" +#endif + +bool CSTYLE_fix_breeze = false; +bool CSTYLE_fix_oxygen = false; + +static char *_style_name = NULL; + +static QWidget *_fake = 0; + +static QWidget *get_fake_widget() +{ + if (!_fake) + _fake = new QWidget; + return _fake; +} + +static char *get_style_name() +{ + if (!_style_name) + { + if (CSTYLE_fix_breeze) + { + _style_name = GB.NewZeroString("Breeze"); + } + else if (CSTYLE_fix_oxygen) + { + _style_name = GB.NewZeroString("Oxygen"); + } + else + { + const char *name = qApp->style()->metaObject()->className(); + int len = strlen(name); + + if (len >= 6 && strncasecmp(&name[len - 5], "style", 5) == 0) + len -= 5; + if (len >= 3 && strncmp(&name[len - 2], "::", 2) == 0) + len -= 2; + if (name[0] == 'Q' && isupper(name[1])) + { + len--; + name++; + } + + _style_name = GB.NewString(name, len); + } + } + + return _style_name; +} + +static void init_option(QStyleOption &opt, int x, int y, int w, int h, int state, GB_COLOR color = COLOR_DEFAULT, QPalette::ColorRole role = QPalette::Window) +{ + opt.rect = QRect(x, y, w ,h); + opt.state = QStyle::State_None; + + if (!(state & GB_DRAW_STATE_DISABLED)) + opt.state |= QStyle::State_Enabled; + if (state & GB_DRAW_STATE_FOCUS) + opt.state |= QStyle::State_HasFocus; + if (state & GB_DRAW_STATE_HOVER) + opt.state |= QStyle::State_MouseOver; + if (state & GB_DRAW_STATE_ACTIVE) + opt.state |= QStyle::State_On | QStyle::State_Sunken | QStyle::State_Active; + + if (color != GB_COLOR_DEFAULT) + { + QPalette palette; + palette.setColor(role, TO_QCOLOR(color)); + opt.palette = palette; + } +} + +static void paint_focus(QPainter *p, int x, int y, int w, int h, int state) +{ + //bool do_clip = FALSE; + QStyleOptionFocusRect opt; + + if ((state & GB_DRAW_STATE_DISABLED) || !(state & GB_DRAW_STATE_FOCUS)) + return; + + init_option(opt, x, y, w, h, state); + + /*if (::strcmp(qApp->style()->metaObject()->className(), "QtCurve::Style") == 0) + { + QPainterPath clip; + + p->save(); + + clip.addRect(x, y, w, 1); + clip.addRect(x, y, 1, h); + clip.addRect(x, y + h - 1, w, 1); + clip.addRect(x + w - 1, y, 1, h); + p->setClipPath(clip); + do_clip = TRUE; + }*/ + + p->save(); + p->setBrush(QBrush()); + + QApplication::style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p); + + p->restore(); + + /*if (do_clip) + p->restore();*/ +} + +static void style_arrow(QPainter *p, int x, int y, int w, int h, int type, int state) +{ + QStyleOption opt; + QStyle::PrimitiveElement pe; + + init_option(opt, x, y, w, h, state); + + switch (type) + { + case ALIGN_NORMAL: pe = GB.System.IsRightToLeft() ? QStyle::PE_IndicatorArrowLeft : QStyle::PE_IndicatorArrowRight; break; + case ALIGN_LEFT: pe = QStyle::PE_IndicatorArrowLeft; break; + case ALIGN_RIGHT: pe = QStyle::PE_IndicatorArrowRight; break; + case ALIGN_TOP: pe = QStyle::PE_IndicatorArrowUp; break; + case ALIGN_BOTTOM: pe = QStyle::PE_IndicatorArrowDown; break; + default: + return; + } + + QApplication::style()->drawPrimitive(pe, &opt, p); +} + +static void style_check(QPainter *p, int x, int y, int w, int h, int value, int state) +{ + QStyleOptionButton opt; + init_option(opt, x, y, w, h, state); + + if (value) + { + if (value == 1) + opt.state |= QStyle::State_NoChange; + else + opt.state |= QStyle::State_On; + } + + QApplication::style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &opt, p); + paint_focus(p, x, y, w, h, state); +} + +static void style_option(QPainter *p, int x, int y, int w, int h, int value, int state) +{ + QStyleOptionButton opt; + init_option(opt, x, y, w, h, state); + + if (value) + opt.state |= QStyle::State_On; + + QApplication::style()->drawPrimitive(QStyle::PE_IndicatorRadioButton, &opt, p); + paint_focus(p, x, y, w, h, state); +} + +static void style_separator(QPainter *p, int x, int y, int w, int h, int vertical, int state) +{ + QStyleOption opt; + init_option(opt, x, y, w, h, state); + + if (vertical) + opt.state |= QStyle::State_Horizontal; + + QApplication::style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, p); +} + + +static void style_button(QPainter *p, int x, int y, int w, int h, int value, int state, int flat) +{ + if (flat) + { + QStyleOptionToolButton opt; + + init_option(opt, x, y, w, h, state); + + //opt.state |= QStyle::State_Raised; + + if (value) + opt.state |= QStyle::State_On; + + //opt.state &= ~QStyle::State_HasFocus; + opt.state |= QStyle::State_AutoRaise; + if (opt.state & QStyle::State_MouseOver) + opt.state |= QStyle::State_Raised; + + if (opt.state & (QStyle::State_Sunken | QStyle::State_On | QStyle::State_MouseOver)) + { + QApplication::style()->drawPrimitive(QStyle::PE_PanelButtonTool, &opt, p); + } + } + else + { + QStyleOptionButton opt; + + init_option(opt, x, y, w, h, state); + + opt.state |= QStyle::State_Raised; + + if (value) + opt.state |= QStyle::State_On; + + QApplication::style()->drawPrimitive(QStyle::PE_PanelButtonCommand, &opt, p); + } + + paint_focus(p, x, y, w, h, state); +} + +static void style_panel(QPainter *p, int x, int y, int w, int h, int border, int state) +{ + QStyleOptionFrame opt; + init_option(opt, x, y, w, h, state); + + CCONTAINER_draw_border_without_widget(p, border, opt); +} + +static void style_handle(QPainter *p, int x, int y, int w, int h, int vertical, int state) +{ + QStyleOption opt; + init_option(opt, x, y, w, h, state); + + if (!vertical) + opt.state |= QStyle::State_Horizontal; + + QApplication::style()->drawPrimitive(QStyle::PE_IndicatorDockWidgetResizeHandle, &opt, p); + paint_focus(p, x, y, w, h, state); +} + + +static void style_box(QPainter *p, int x, int y, int w, int h, int state, GB_COLOR color) +{ + QStyleOptionFrame opt; + + //if (GB.Is(d->device, CLASS_DrawingArea)) + // opt.initFrom(QWIDGET(d->device)); + + init_option(opt, x, y, w, h, state, color, QPalette::Base); + + opt.lineWidth = QApplication::style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt); + opt.midLineWidth = 0; + opt.state |= QStyle::State_Sunken; + p->save(); + p->setBrush(Qt::NoBrush); + //opt.features = QStyleOptionFrameV2::None; + + if (color == GB_COLOR_DEFAULT) + QApplication::style()->drawPrimitive(QStyle::PE_FrameLineEdit, &opt, p); + else + { + if (::strcmp(qApp->style()->metaObject()->className(), "QGtkStyle") == 0) + { + QWidget *w = get_fake_widget(); + w->setAttribute(Qt::WA_SetPalette, true); + QApplication::style()->drawPrimitive(QStyle::PE_PanelLineEdit, &opt, p, w); + w->setAttribute(Qt::WA_SetPalette, false); + } + else + QApplication::style()->drawPrimitive(QStyle::PE_PanelLineEdit, &opt, p); + } + + p->restore(); + //paint_focus(d, x, y, w, h, state); + //if (state & GB_DRAW_STATE_FOCUS) + // QApplication::style()->drawControl(QStyle::CE_FocusFrame, &opt, DP(d), GB.Is(d->device, CLASS_DrawingArea) ? QWIDGET(d->device) : NULL); +} + +BEGIN_PROPERTY(Style_ScrollbarSize) + + GB.ReturnInteger(qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent)); + +END_PROPERTY + +BEGIN_PROPERTY(Style_ScrollbarSpacing) + + if (::strcmp(get_style_name(), "Breeze") == 0) + GB.ReturnInteger(0); + else + GB.ReturnInteger(qMax(0, qApp->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing))); + +END_PROPERTY + +BEGIN_PROPERTY(Style_FrameWidth) + + if (::strcmp(get_style_name(), "Breeze") == 0) + GB.ReturnInteger(2); + else + GB.ReturnInteger(qApp->style()->pixelMetric(QStyle::QStyle::PM_ComboBoxFrameWidth)); + +END_PROPERTY + +BEGIN_PROPERTY(Style_BoxFrameWidth) + + int w = qApp->style()->pixelMetric(QStyle::QStyle::PM_ComboBoxFrameWidth); + GB.ReturnInteger(w); + +END_PROPERTY + +BEGIN_PROPERTY(Style_BoxFrameHeight) + + int w = qApp->style()->pixelMetric(QStyle::QStyle::PM_ComboBoxFrameWidth); + GB.ReturnInteger(w); + +END_PROPERTY + +BEGIN_PROPERTY(Style_Name) + + GB.ReturnString(get_style_name()); + +END_PROPERTY + +#define GET_COORD() \ + int x, y, w, h; \ +\ + QPainter *p = PAINT_get_current(); \ + if (!p) \ + return; \ +\ + x = VARG(x); \ + y = VARG(y); \ + w = VARG(w); \ + h = VARG(h); \ +\ + if (w < 1 || h < 1) \ + return; + +BEGIN_METHOD(Style_PaintArrow, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER type; GB_INTEGER state) + + GET_COORD(); + style_arrow(p, x, y, w, h, VARG(type), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(Style_PaintCheck, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER value; GB_INTEGER state) + + GET_COORD(); + style_check(p, x, y, w, h, VARG(value), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(Style_PaintOption, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN value; GB_INTEGER state) + + GET_COORD(); + style_option(p, x, y, w, h, VARG(value), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(Style_PaintSeparator, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN vertical; GB_INTEGER state) + + GET_COORD(); + style_separator(p, x, y, w, h, VARGOPT(vertical, FALSE), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(Style_PaintButton, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN value; GB_INTEGER state; GB_BOOLEAN flat) + + GET_COORD(); + style_button(p, x, y, w, h, VARG(value), VARGOPT(state, GB_DRAW_STATE_NORMAL), VARGOPT(flat, FALSE)); + +END_METHOD + +BEGIN_METHOD(Style_PaintPanel, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER border; GB_INTEGER state) + + GET_COORD(); + style_panel(p, x, y, w, h, VARG(border), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(Style_PaintHandle, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN vertical; GB_INTEGER state) + + GET_COORD(); + style_handle(p, x, y, w, h, VARGOPT(vertical, FALSE), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(Style_PaintBox, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER state; GB_INTEGER color) + + GET_COORD(); + style_box(p, x, y, w, h, VARGOPT(state, GB_DRAW_STATE_NORMAL), VARGOPT(color, GB_COLOR_DEFAULT)); + +END_METHOD + +BEGIN_METHOD(Style_StateOf, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + QWidget *widget; + int state; + bool design; + + if (GB.CheckObject(control)) + return; + + widget = QWIDGET(control); + design = CWIDGET_is_design(control); + + state = GB_DRAW_STATE_NORMAL; + if (!widget->isEnabled()) + state |= GB_DRAW_STATE_DISABLED; + if (widget->hasFocus() && !design) + state |= GB_DRAW_STATE_FOCUS; + if (CWIDGET_is_visible(control) && control->flag.inside && !design) + state |= GB_DRAW_STATE_HOVER; + + GB.ReturnInteger(state); + +END_METHOD + +BEGIN_METHOD(Style_BackgroundOf, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + + if (GB.CheckObject(control)) + return; + + GB.ReturnInteger(CWIDGET_get_real_background(control)); + +END_METHOD + +BEGIN_METHOD(Style_ForegroundOf, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + + if (GB.CheckObject(control)) + return; + + GB.ReturnInteger(CWIDGET_get_real_foreground(control)); + +END_METHOD + +#if 0 +BEGIN_METHOD(Style_FontOf, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + + if (GB.CheckObject(control)) + return; + + GB.ReturnObject(CWIDGET_get_real_font(control)); + +END_METHOD +#endif + +BEGIN_METHOD_VOID(Style_exit) + + GB.FreeString(&_style_name); + +END_METHOD + +GB_DESC StyleDesc[] = +{ + GB_DECLARE("Style", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_exit", NULL, Style_exit, NULL), + + GB_STATIC_PROPERTY_READ("ScrollbarSize", "i", Style_ScrollbarSize), + GB_STATIC_PROPERTY_READ("ScrollbarSpacing", "i", Style_ScrollbarSpacing), + GB_STATIC_PROPERTY_READ("FrameWidth", "i", Style_FrameWidth), + GB_STATIC_PROPERTY_READ("TextBoxFrameWidth", "i", Style_FrameWidth), + GB_STATIC_PROPERTY_READ("BoxFrameWidth", "i", Style_BoxFrameWidth), + GB_STATIC_PROPERTY_READ("BoxFrameHeight", "i", Style_BoxFrameHeight), + GB_STATIC_PROPERTY_READ("Name", "s", Style_Name), + + GB_STATIC_METHOD("PaintArrow", NULL, Style_PaintArrow, "(X)i(Y)i(Width)i(Height)i(Type)i[(Flag)i]"), + GB_STATIC_METHOD("PaintCheck", NULL, Style_PaintCheck, "(X)i(Y)i(Width)i(Height)i(Value)i[(Flag)i]"), + GB_STATIC_METHOD("PaintOption", NULL, Style_PaintOption, "(X)i(Y)i(Width)i(Height)i(Value)b[(Flag)i]"), + GB_STATIC_METHOD("PaintSeparator", NULL, Style_PaintSeparator, "(X)i(Y)i(Width)i(Height)i[(Vertical)b(Flag)i]"), + GB_STATIC_METHOD("PaintButton", NULL, Style_PaintButton, "(X)i(Y)i(Width)i(Height)i(Value)b[(Flag)i(Flat)b]"), + GB_STATIC_METHOD("PaintPanel", NULL, Style_PaintPanel, "(X)i(Y)i(Width)i(Height)i(Border)i[(Flag)i]"), + GB_STATIC_METHOD("PaintHandle", NULL, Style_PaintHandle, "(X)i(Y)i(Width)i(Height)i[(Vertical)b(Flag)i]"), + GB_STATIC_METHOD("PaintBox", NULL, Style_PaintBox, "(X)i(Y)i(Width)i(Height)i[(Flag)i(Color)i]"), + + GB_CONSTANT("Normal", "i", GB_DRAW_STATE_NORMAL), + GB_CONSTANT("Disabled", "i", GB_DRAW_STATE_DISABLED), + GB_CONSTANT("HasFocus", "i", GB_DRAW_STATE_FOCUS), + GB_CONSTANT("Hovered", "i", GB_DRAW_STATE_HOVER), + GB_CONSTANT("Active", "i", GB_DRAW_STATE_ACTIVE), + + GB_STATIC_METHOD("StateOf", "i", Style_StateOf, "(Control)Control;"), + GB_STATIC_METHOD("BackgroundOf", "i", Style_BackgroundOf, "(Control)Control;"), + GB_STATIC_METHOD("ForegroundOf", "i", Style_ForegroundOf, "(Control)Control;"), + //GB_STATIC_METHOD("FontOf", "Font", Style_FontOf, "(Control)Control;"), + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/CStyle.h b/gb.qt4/src/CStyle.h new file mode 100644 index 00000000..274726e7 --- /dev/null +++ b/gb.qt4/src/CStyle.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + CStyle.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSTYLE_H +#define __CSTYLE_H + +#include "gambas.h" + +#ifndef __CSTYLE_CPP +extern GB_DESC StyleDesc[]; +extern bool CSTYLE_fix_breeze; +extern bool CSTYLE_fix_oxygen; +#endif + +#endif diff --git a/gb.qt4/src/CTabStrip.cpp b/gb.qt4/src/CTabStrip.cpp new file mode 100644 index 00000000..2c24ded5 --- /dev/null +++ b/gb.qt4/src/CTabStrip.cpp @@ -0,0 +1,934 @@ +/*************************************************************************** + + CTabStrip.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTABSTRIP_CPP + +#include +#include +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "gambas.h" + +#include "CPicture.h" +#include "CConst.h" +#include "CTabStrip.h" + + +DECLARE_METHOD(Container_X); +DECLARE_METHOD(Container_Y); + +DECLARE_EVENT(EVENT_Click); +DECLARE_EVENT(EVENT_Close); + +void CTABSTRIP_arrange(void *_object) +{ + WIDGET->layoutContainer(); +} + +/** CTab *****************************************************************/ + +class CTab +{ +public: + QWidget *widget; + QString text; + CPICTURE *icon; + int id; + bool visible; + bool enabled; + + CTab(CTABSTRIP *parent, QWidget *page); + ~CTab(); + + int index() const { return WIDGET->indexOf(widget); } + void ensureVisible(); + void setEnabled(bool e); + bool isEnabled() const { return enabled; } + bool isVisible() const { return visible; } + void setVisible(bool v); + void updateIcon(); + void updateText(); + int count() const; + bool isEmpty() const { return count() == 0; } + +private: + CTABSTRIP *_object; +}; + +CTab::CTab(CTABSTRIP *parent, QWidget *page) +{ + _object = parent; + widget = page; + icon = 0; + id = WIDGET->stack.count(); + visible = true; + setEnabled(true); + + //page->setAutoFillBackground(true); + page->hide(); +} + +CTab::~CTab() +{ + GB.Unref(POINTER(&icon)); +} + +void CTab::ensureVisible() +{ + setVisible(true); + int i = index(); + if (i >= 0) + { + WIDGET->setCurrentIndex(i); + if (!WIDGET->isVisible()) + WIDGET->layoutContainer(); + } +} + +int CTab::count() const +{ + QObjectList list = widget->children(); + QObject *ob; + int n = 0; + int i; + + for(i = 0; i < list.count(); i++) + { + ob = list.at(i); + if (ob->isWidgetType() && CWidget::getRealExisting(ob)) + n++; + } + + return n; +} + +void CTab::setVisible(bool v) +{ + int i, ind; + CTab *page; + + if (v == visible) + return; + + visible = v; + + if (!visible) + { + i = index(); + if (i >= 0) + { + text = WIDGET->tabText(i); + WIDGET->removeTab(i); + } + } + else + { + ind = 0; + for (i = 0; i < (int)WIDGET->stack.count(); i++) + { + page = WIDGET->stack.at(i); + if (!page->isVisible()) + continue; + if (id == WIDGET->stack.at(i)->id) + break; + ind++; + } + WIDGET->insertTab(ind, widget, text); + setEnabled(enabled); + updateIcon(); + if (WIDGET->count() == 1) + ensureVisible(); + } +} + +void CTab::setEnabled(bool e) +{ + int i = index(); + enabled = e; + if (i >= 0) + WIDGET->setTabEnabled(i, e && WIDGET->isEnabled()); +} + +void CTab::updateIcon() +{ + int i = index(); + QIcon iconset; + + if (icon) + CWIDGET_iconset(iconset, *(icon->pixmap)); + + if (i >= 0) + WIDGET->setTabIcon(i, iconset); +} + +void CTab::updateText() +{ + int i = index(); + if (i >= 0) + WIDGET->setTabText(i, text); +} + +/** MyTabWidget **********************************************************/ + +MyTabWidget::MyTabWidget(QWidget *parent) : QTabWidget(parent) +{ + _oldw = _oldh = 0; + //setMovable(true); + //tabBar()->installEventFilter(this); +} + +MyTabWidget::~MyTabWidget() +{ + int i; + CWIDGET *_object = CWidget::getReal(this); + + for (i = 0; i < stack.count(); i++) + delete stack.at(i); + + CWIDGET_set_flag(THIS, WF_DELETED); +} + +void MyTabWidget::setEnabled(bool e) +{ + CWIDGET *_object = CWidget::get(this); + int i; + + QTabWidget::setEnabled(e); + + for (i = 0; i < (int)WIDGET->stack.count(); i++) + WIDGET->stack.at(i)->widget->setEnabled(e); +} + +void MyTabWidget::layoutContainer() +{ + CWIDGET *_object = CWidget::get(this); + #if QT_VERSION >= 0x040600 && QT_VERSION < 0x050700 + QStyleOptionTabWidgetFrameV2 option; + #else + QStyleOptionTabWidgetFrame option; + #endif + QWidget *w = findChild(); + QRect contentsRect; + + if (_oldw != width() || _oldh != height()) + { + initStyleOption(&option); + contentsRect = style()->subElementRect(QStyle::SE_TabWidgetTabContents, &option, this); + _oldw = width(); + _oldh = height(); + w->setGeometry(contentsRect); + } + else + contentsRect = w->geometry(); + + if (THIS->container) + THIS->container->setGeometry(0, 0, contentsRect.width(), contentsRect.height()); +} + +void MyTabWidget::updateTextFont() +{ + CWIDGET *_object = CWidget::get(this); + if (THIS->textFont) + tabBar()->setFont(*THIS->textFont->font); + else + tabBar()->setFont(QFont()); +} + +#if 0 +bool MyTabWidget::eventFilter(QObject *o, QEvent *e) +{ + if (e->type() == QEvent::Wheel && qobject_cast(o)) + { + QWheelEvent *event = (QWheelEvent *)e; + QTabBar *tabBar = (QTabBar *)o; + int id = tabBar->currentIndex(); + + if (id >= 0) + { + for(;;) + { + if (event->delta() < 0) + id++; + else + id--; + + if (id < 0 || id >= tabBar->count()) + break; + + if (tabBar->isTabEnabled(id)) + { + tabBar->setCurrentIndex(id); + break; + } + } + } + + return true; + } + + return QObject::eventFilter(o, e); +} +#endif + +/*************************************************************************** + + TabStrip + +***************************************************************************/ + +static bool remove_page(void *_object, int i) +{ + CTab *tab = WIDGET->stack.at(i); + + if (!tab->isEmpty()) + { + GB.Error("Tab is not empty"); + return true; + } + + THIS->lock = true; + WIDGET->stack.removeAt(i); + + i = tab->index(); + if (i >= 0) + WIDGET->removeTab(i); + + delete tab->widget; + delete tab; + THIS->lock = false; + + return false; +} + +static void set_current_index(void *_object, int index) +{ + if (index < 0) + return; + + if (index >= (int)WIDGET->stack.count()) + index = WIDGET->stack.count() - 1; + + while (index > 0 && !WIDGET->stack.at(index)->isVisible()) + index--; + + WIDGET->stack.at(index)->ensureVisible(); + THIS->container = WIDGET->stack.at(index)->widget; +} + +static bool set_tab_count(void *_object, int new_count) +{ + int count = WIDGET->stack.count(); + int i; + int index; + QString label; + CTab *tab; + + if (new_count < 1 || new_count > 256) + { + GB.Error(GB_ERR_ARG); + return true; + } + + if (new_count == count) + return false; + + if (new_count > count) + { + for (i = count; i < new_count; i++) + { + tab = new CTab(THIS, new MyContainer(WIDGET)); + + label.sprintf("Tab %d", i); + WIDGET->addTab(tab->widget, label); + + WIDGET->stack.append(tab); + } + + index = new_count - 1; + + set_current_index(THIS, index); + } + else + { + index = WIDGET->currentIndex(); + //same = (id == PARAM(index)); + + for (i = new_count; i < count; i++) + { + if (!WIDGET->stack.at(i)->isEmpty()) + { + GB.Error("Tab is not empty"); + return true; + } + } + + if (index >= new_count) + index = new_count - 1; + + set_current_index(THIS, index); + + for (i = count - 1; i >= new_count; i--) + { + remove_page(THIS, i); + } + + //WIDGET->stack.resize(new_count); + //THIS->icon->resize(new_count); + } + + return false; +} + +static bool check_index(CTABSTRIP *_object, int index) +{ + if (index < 0 || index >= (int)WIDGET->stack.count()) + { + GB.Error("Bad index"); + return true; + } + else + return false; +} + +static int get_real_index(CTABSTRIP *_object) +{ + int i; + QWidget *current = WIDGET->currentWidget(); + + for (i = 0; i < (int)WIDGET->stack.count(); i++) + { + if (WIDGET->stack.at(i)->widget == current) + return i; + } + + return -1; +} + + +BEGIN_METHOD(TabStrip_new, GB_OBJECT parent) + + MyTabWidget *wid = new MyTabWidget(QCONTAINER(VARG(parent))); //, 0, Qt::WNoMousePropagation); + + QObject::connect(wid, SIGNAL(currentChanged(int)), &CTabStrip::manager, SLOT(currentChanged(int))); + QObject::connect(wid, SIGNAL(tabCloseRequested(int)), &CTabStrip::manager, SLOT(tabCloseRequested(int))); + + //THIS->widget.flag.fillBackground = TRUE; + THIS->container = NULL; + THIS->index = -1; + + CWIDGET_new(wid, (void *)_object); + set_tab_count(THIS, 1); + + //wid->updateLayout(); + +END_METHOD + + +BEGIN_METHOD_VOID(TabStrip_free) + + GB.Unref(POINTER(&THIS->textFont)); + +END_METHOD + + +BEGIN_PROPERTY(TabStrip_Count) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->stack.count()); + else + set_tab_count(THIS, VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_Index) + + if (READ_PROPERTY) + { + GB.ReturnInteger(get_real_index(THIS)); + } + else + { + int index = VPROP(GB_INTEGER); + + if (check_index(THIS, index)) + return; + + if (index == get_real_index(THIS)) + return; + + if (!WIDGET->stack.at(index)->isVisible()) + return; + + WIDGET->stack.at(index)->ensureVisible(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_Current) + + THIS->index = get_real_index(THIS); + if (THIS->index < 0) + GB.ReturnNull(); + else + RETURN_SELF(); + +END_PROPERTY + + +BEGIN_METHOD(TabStrip_get, GB_INTEGER index) + + int index = VARG(index); + + if (check_index(THIS, index)) + return; + + THIS->index = index; + RETURN_SELF(); + +END_METHOD + + +BEGIN_PROPERTY(TabStrip_Orientation) + + if (READ_PROPERTY) + { + switch(WIDGET->tabPosition()) + { + case QTabWidget::North: GB.ReturnInteger(ALIGN_TOP); break; + case QTabWidget::South: GB.ReturnInteger(ALIGN_BOTTOM); break; + case QTabWidget::West: GB.ReturnInteger(ALIGN_LEFT); break; + case QTabWidget::East: GB.ReturnInteger(ALIGN_RIGHT); break; + default: GB.ReturnInteger(ALIGN_NORMAL); break; + } + } + else + { + switch(VPROP(GB_INTEGER)) + { + case ALIGN_TOP: WIDGET->setTabPosition(QTabWidget::North); break; + case ALIGN_BOTTOM: WIDGET->setTabPosition(QTabWidget::South); break; + case ALIGN_LEFT: WIDGET->setTabPosition(QTabWidget::West); break; + case ALIGN_RIGHT: WIDGET->setTabPosition(QTabWidget::East); break; + } + } + +END_PROPERTY + + + +/*************************************************************************** + + .Tab + +***************************************************************************/ + +BEGIN_PROPERTY(CTAB_text) + + int index = THIS->index; + if (index < 0) + index = get_real_index(THIS); + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->stack.at(index)->text); + else + { + WIDGET->stack.at(index)->text = QSTRING_PROP(); + WIDGET->stack.at(index)->updateText(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_TextFont) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->textFont); + else + { + GB.StoreObject(PROP(GB_OBJECT), POINTER(&THIS->textFont)); + WIDGET->updateTextFont(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CTAB_picture) + + int index; + + index = THIS->index; + if (index < 0) + index = get_real_index(THIS); + + if (READ_PROPERTY) + { + if (index < 0) + GB.ReturnNull(); + else + GB.ReturnObject(WIDGET->stack.at(index)->icon); + } + else if (index >= 0) + { + GB.StoreObject(PROP(GB_OBJECT), POINTER(&(WIDGET->stack.at(index))->icon)); + WIDGET->stack.at(index)->updateIcon(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CTAB_enabled) + + CTab *tab = WIDGET->stack.at(THIS->index); + + if (READ_PROPERTY) + GB.ReturnBoolean(tab->isEnabled()); + else + tab->setEnabled(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTAB_visible) + + CTab *tab = WIDGET->stack.at(THIS->index); + + if (READ_PROPERTY) + GB.ReturnBoolean(tab->isVisible()); + else + tab->setVisible(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CTAB_next) + + CTABSTRIP_ENUM *iter; + QWidget *page; + QObjectList list; + int child; + CWIDGET *widget; + + iter = (CTABSTRIP_ENUM *)GB.GetEnum(); + if (!iter->init) + { + iter->child = 0; + iter->index = THIS->index; + iter->init = true; + } + + //qDebug("CTAB_next: iter = (%d, %d, %d)", iter->init, iter->index, iter->child); + + page = WIDGET->stack.at(iter->index)->widget; + list = page->children(); + + for(;;) + { + child = iter->child; + + if (child >= list.count()) + { + GB.StopEnum(); + return; + } + + iter->child = child + 1; + + widget = CWidget::getRealExisting(list.at(child)); + if (widget) + { + GB.ReturnObject(widget); + return; + } + } + +END_METHOD + +BEGIN_METHOD(CTAB_get, GB_INTEGER index) + + QObjectList list = WIDGET->stack.at(THIS->index)->widget->children(); + int index = VARG(index); + int i; + CWIDGET *widget; + + if (index >= 0) + { + i = 0; + for(i = 0; i < list.count(); i++) + { + widget = CWidget::getRealExisting(list.at(i)); + if (!widget) + continue; + if (index == 0) + { + GB.ReturnObject(widget); + return; + } + index--; + } + } + + GB.Error(GB_ERR_BOUND); + +END_METHOD + + +BEGIN_PROPERTY(CTAB_count) + + GB.ReturnInteger(WIDGET->stack.at(THIS->index)->count()); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CTAB_delete) + + int index = get_real_index(THIS); + + if (WIDGET->stack.count() == 1) + { + GB.Error("TabStrip cannot be empty"); + return; + } + + if (remove_page(THIS, THIS->index)) + return; + + set_current_index(THIS, index); + + THIS->index = -1; + +END_METHOD + + +BEGIN_PROPERTY(TabStrip_Enabled) + + int i; + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isEnabled()); + else + { + WIDGET->setEnabled(VPROP(GB_BOOLEAN)); + for (i = 0; i < WIDGET->stack.count(); i++) + WIDGET->stack.at(i)->setEnabled(VPROP(GB_BOOLEAN)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_Text) + + THIS->index = -1; + CTAB_text(_object, _param); + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_Picture) + + THIS->index = -1; + CTAB_picture(_object, _param); + +END_PROPERTY + +BEGIN_PROPERTY(TabStrip_Closable) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->tabsClosable()); + else + WIDGET->setTabsClosable(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_ClientX) + + Container_X(_object, _param); + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_ClientY) + + Container_Y(_object, _param); + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_ClientWidth) + + GB.ReturnInteger(THIS->container->width()); + +END_PROPERTY + + +BEGIN_PROPERTY(TabStrip_ClientHeight) + + GB.ReturnInteger(THIS->container->height()); + +END_PROPERTY + + +BEGIN_METHOD(TabStrip_FindIndex, GB_OBJECT child) + + void *child = VARG(child); + int i; + QWidget *parent; + + if (GB.CheckObject(child)) + return; + + parent = QWIDGET(child)->parentWidget(); + + for (i = 0; i < WIDGET->stack.count(); i++) + { + if (parent == WIDGET->stack.at(i)->widget) + { + GB.ReturnInteger(i); + return; + } + } + + GB.ReturnInteger(-1); + +END_METHOD + + +/** Class CTabStrip ******************************************************/ + + +CTabStrip CTabStrip::manager; + +void CTabStrip::currentChanged(int index) +{ + QWidget *wid; + GET_SENDER(); + + wid = WIDGET->currentWidget(); // wid can be null! + + if (wid != THIS->container) + { + //qDebug("CTabStrip::currentChanged: %d %p -> %p", index, THIS->container, wid); + if (THIS->container) + THIS->container->hide(); + THIS->container = wid; + + if (wid) + wid->show(); + + CCONTAINER_arrange(THIS); + + //if (wid->isVisible() && !THIS->lock) + if (!THIS->lock) + RAISE_EVENT(EVENT_Click); + } +} + +void CTabStrip::tabCloseRequested(int index) +{ + GET_SENDER(); + + GB.Raise(THIS, EVENT_Close, 1, GB_T_INTEGER, index); +} + + +/** Descriptions *********************************************************/ + +GB_DESC CTabStripContainerChildrenDesc[] = +{ + GB_DECLARE_VIRTUAL(".TabStripContainer.Children"), + + GB_METHOD("_next", "Control", CTAB_next, NULL), + GB_PROPERTY_READ("Count", "i", CTAB_count), + GB_METHOD("_get", "Control", CTAB_get, "(Index)i"), + + GB_END_DECLARE +}; + + +GB_DESC CTabStripContainerDesc[] = +{ + GB_DECLARE_VIRTUAL(".TabStripContainer"), + + GB_PROPERTY("Text", "s", CTAB_text), + GB_PROPERTY("Picture", "Picture", CTAB_picture), + GB_PROPERTY("Caption", "s", CTAB_text), + GB_PROPERTY("Enabled", "b", CTAB_enabled), + GB_PROPERTY("Visible", "b", CTAB_visible), + GB_PROPERTY_SELF("Children", ".TabStripContainer.Children"), + GB_METHOD("Delete", NULL, CTAB_delete, NULL), + + GB_END_DECLARE +}; + + +GB_DESC CTabStripDesc[] = +{ + GB_DECLARE("TabStrip", sizeof(CTABSTRIP)), GB_INHERITS("Container"), + + GB_METHOD("_new", NULL, TabStrip_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, TabStrip_free, NULL), + + GB_PROPERTY("Count", "i", TabStrip_Count), + GB_PROPERTY("Text", "s", TabStrip_Text), + GB_PROPERTY("TextFont", "Font", TabStrip_TextFont), + GB_PROPERTY("Picture", "Picture", TabStrip_Picture), + GB_PROPERTY("Closable", "b", TabStrip_Closable), + GB_PROPERTY("Caption", "s", TabStrip_Text), + GB_PROPERTY_READ("Current", ".TabStripContainer", TabStrip_Current), + GB_PROPERTY("Index", "i", TabStrip_Index), + GB_PROPERTY("Orientation", "i", TabStrip_Orientation), + GB_PROPERTY("Enabled", "b", TabStrip_Enabled), + + GB_PROPERTY_READ("ClientX", "i", TabStrip_ClientX), + GB_PROPERTY_READ("ClientY", "i", TabStrip_ClientY), + GB_PROPERTY_READ("ClientW", "i", TabStrip_ClientWidth), + GB_PROPERTY_READ("ClientWidth", "i", TabStrip_ClientWidth), + GB_PROPERTY_READ("ClientH", "i", TabStrip_ClientHeight), + GB_PROPERTY_READ("ClientHeight", "i", TabStrip_ClientHeight), + + GB_PROPERTY("Arrangement", "i", Container_Arrangement), + GB_PROPERTY("AutoResize", "b", Container_AutoResize), + GB_PROPERTY("Padding", "i", Container_Padding), + GB_PROPERTY("Spacing", "b", Container_Spacing), + GB_PROPERTY("Margin", "b", Container_Margin), + GB_PROPERTY("Indent", "b", Container_Indent), + GB_PROPERTY("Invert", "b", Container_Invert), + + GB_METHOD("_get", ".TabStripContainer", TabStrip_get, "(Index)i"), + GB_METHOD("FindIndex", "i", TabStrip_FindIndex, "(Child)Control;"), + + TABSTRIP_DESCRIPTION, + + GB_EVENT("Click", NULL, NULL, &EVENT_Click), + GB_EVENT("Close", NULL, "(Index)i", &EVENT_Close), + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/CTabStrip.h b/gb.qt4/src/CTabStrip.h new file mode 100644 index 00000000..d01d0c11 --- /dev/null +++ b/gb.qt4/src/CTabStrip.h @@ -0,0 +1,106 @@ +/*************************************************************************** + + CTabStrip.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTABSTRIP_H +#define __CTABSTRIP_H + +#include "gambas.h" + +#include "CWidget.h" +#include "CContainer.h" +#include "CPicture.h" +#include "CFont.h" + +#include +#include +#include + +#ifndef __CTABSTRIP_CPP +extern GB_DESC CTabStripDesc[]; +extern GB_DESC CTabStripContainerDesc[]; +extern GB_DESC CTabStripContainerChildrenDesc[]; +#else + +#define QTABWIDGET(object) ((MyTabWidget *)((CWIDGET *)object)->widget) + +#define WIDGET QTABWIDGET(_object) +#define THIS OBJECT(CTABSTRIP) + +typedef + struct { + int index; + int child; + bool init; + } + CTABSTRIP_ENUM; + +typedef + struct { + CWIDGET widget; + QWidget *container; + CARRANGEMENT arrangement; + CFONT *textFont; + int index; + unsigned lock : 1; + } + CTABSTRIP; + +#endif + +class CTab; + +class MyTabWidget : public QTabWidget +{ +Q_OBJECT + +public: + + QList stack; + + MyTabWidget(QWidget *parent); + virtual ~MyTabWidget(); + virtual void setEnabled(bool e); + void layoutContainer(); + void updateTextFont(); + +private: + int _oldw, _oldh; +}; + +class CTabStrip : public QObject +{ +Q_OBJECT + +public: + + static CTabStrip manager; + +public slots: + + void currentChanged(int); + void tabCloseRequested(int); +}; + +void CTABSTRIP_arrange(void *_object); + +#endif diff --git a/gb.qt4/src/CTextArea.cpp b/gb.qt4/src/CTextArea.cpp new file mode 100644 index 00000000..22b66e93 --- /dev/null +++ b/gb.qt4/src/CTextArea.cpp @@ -0,0 +1,696 @@ +/*************************************************************************** + + CTextArea.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTEXTAREA_CPP + +#include +#include +#include +#include +#include + +#include "gambas.h" +#include "main.h" +#include "CConst.h" +#include "CFont.h" +#include "CTextArea.h" + + +DECLARE_EVENT(EVENT_Change); +DECLARE_EVENT(EVENT_Cursor); +DECLARE_EVENT(EVENT_Link); + +static int get_length(void *_object) +{ + if (THIS->length < 0) + { + QTextBlock block = WIDGET->document()->begin(); + int len = 0; + + while (block.isValid()) + { + len += block.length(); + block = block.next(); + } + + THIS->length = len - 1; + } + + return THIS->length; +} + +static void to_pos(QTextEdit *wid, int par, int car, int *pos) +{ + QTextCursor cursor; + QTextBlock block; + int p = 0; + + cursor = wid->textCursor(); + cursor.movePosition(QTextCursor::Start); + + block = cursor.block(); + + while (par) + { + if (!block.isValid()) + break; + p += block.length(); + block = block.next(); + par--; + } + + if (block.isValid()) + car = qMin(block.length() - 1, car); + + *pos = p + car; +} + + +static void from_pos(CTEXTAREA *_object, int pos, int *par, int *car) +{ + QTextCursor cursor = WIDGET->textCursor(); + + if (pos >= get_length(THIS)) + cursor.movePosition(QTextCursor::End); + else + cursor.setPosition(pos); + + *par = cursor.blockNumber(); + *car = cursor.position() - cursor.block().position(); +} + + +static void get_selection(QTextEdit *wid, int *start, int *length) +{ + QTextCursor cursor = wid->textCursor(); + + *start = cursor.selectionStart(); + *length = cursor.selectionEnd() - *start; +} + +static void update_alignment(void *_object) +{ + THIS->no_change = TRUE; + + QTextOption opt = WIDGET->document()->defaultTextOption(); + opt.setAlignment((Qt::Alignment)CCONST_horizontal_alignment(THIS->align, ALIGN_NORMAL, true)); + WIDGET->document()->setDefaultTextOption(opt); + + THIS->no_change = FALSE; +} + +static void set_text_color(void *_object) +{ + QTextCharFormat fmt; + QBrush col; + GB_COLOR fg = CWIDGET_get_foreground((CWIDGET *)THIS); + + fmt = WIDGET->currentCharFormat(); + + if (fg == COLOR_DEFAULT) + { + fmt.clearForeground(); + //col = WIDGET->palette().text(); + } + else + { + fmt.setForeground(TO_QCOLOR(fg)); + } + + //WIDGET->setTextColor(col); + THIS->no_change = TRUE; + WIDGET->setCurrentCharFormat(fmt); + THIS->no_change = FALSE; +} + +void CTEXTAREA_set_foreground(void *_object) +{ + THIS->no_change = TRUE; + + QTextCursor oldCursor = WIDGET->textCursor(); + + WIDGET->selectAll(); + + WIDGET->setTextColor(Qt::black); + set_text_color(THIS); + + WIDGET->setTextCursor(oldCursor); + + set_text_color(THIS); + + THIS->no_change = FALSE; +} + + +/** TextArea ***************************************************************/ + +BEGIN_METHOD(CTEXTAREA_new, GB_OBJECT parent) + + QTextEdit *wid = new QTextEdit(QCONTAINER(VARG(parent))); + + QObject::connect(wid, SIGNAL(textChanged()), &CTextArea::manager, SLOT(changed())); + QObject::connect(wid, SIGNAL(cursorPositionChanged()), &CTextArea::manager, SLOT(cursor())); + + wid->setLineWrapMode(QTextEdit::NoWrap); + wid->setAcceptRichText(false); + + THIS->widget.flag.wheel = true; + THIS->widget.flag.fillBackground = true; + CWIDGET_new(wid, (void *)_object); + + THIS->length = -1; + THIS->align = ALIGN_NORMAL; + + wid->document()->setDocumentMargin(2); + +END_METHOD + + +BEGIN_PROPERTY(CTEXTAREA_text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->toPlainText()); + else + { + WIDGET->document()->setPlainText(QSTRING_PROP()); + update_alignment(THIS); + CTEXTAREA_set_foreground(THIS); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_length) + + GB.ReturnInteger(get_length(THIS)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_read_only) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isReadOnly()); + else + WIDGET->setReadOnly(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_wrap) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->lineWrapMode() != QTextEdit::NoWrap); + else + WIDGET->setLineWrapMode(VPROP(GB_BOOLEAN) ? QTextEdit::WidgetWidth : QTextEdit::NoWrap); + +END_PROPERTY + + +/* +BEGIN_PROPERTY(CTEXTAREA_max_length) + + int max; + + if (READ_PROPERTY) + { + max = WIDGET->maxLength(); + GB.ReturnInteger((max < 0) ? 0 : max); + } + else + { + max = PROPERTY(int); + if (max <= 0) + max = -1; + WIDGET->setMaxLength(max); + } + +END_PROPERTY +*/ + +static int get_column(CTEXTAREA *_object) +{ + QTextCursor cursor = WIDGET->textCursor(); + return cursor.position() - cursor.block().position(); +} + +BEGIN_PROPERTY(CTEXTAREA_column) + + QTextCursor cursor = WIDGET->textCursor(); + + if (READ_PROPERTY) + //GB.ReturnInteger(WIDGET->textCursor().columnNumber()); + GB.ReturnInteger(get_column(THIS)); + else + { + int col = VPROP(GB_INTEGER); + + if (col <= 0) + cursor.movePosition(QTextCursor::QTextCursor::StartOfBlock); + else if (col >= cursor.block().length()) + cursor.movePosition(QTextCursor::QTextCursor::EndOfBlock); + else + cursor.setPosition(cursor.block().position() + col); + + WIDGET->setTextCursor(cursor); + } + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_line) + + QTextCursor cursor = WIDGET->textCursor(); + + if (READ_PROPERTY) + GB.ReturnInteger(cursor.blockNumber()); + else + { + int col = get_column(THIS); + int line = VPROP(GB_INTEGER); + + if (line < 0) + cursor.movePosition(QTextCursor::Start); + else if (line >= WIDGET->document()->blockCount()) + cursor.movePosition(QTextCursor::End); + else + { + cursor.setPosition(WIDGET->document()->findBlockByNumber(line).position()); + if (col > 0) + { + if (col >= cursor.block().length()) + cursor.movePosition(QTextCursor::QTextCursor::EndOfBlock); + else + cursor.setPosition(cursor.block().position() + col); + } + } + + WIDGET->setTextCursor(cursor); + } + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_pos) + + if (READ_PROPERTY) + { + GB.ReturnInteger(WIDGET->textCursor().position()); + } + else + { + int pos = VPROP(GB_INTEGER); + QTextCursor cursor = WIDGET->textCursor(); + + if (pos >= get_length(THIS)) + cursor.movePosition(QTextCursor::End); + else + cursor.setPosition(pos); + + WIDGET->setTextCursor(cursor); + } + +END_PROPERTY + + +/* +BEGIN_METHOD(CTEXTAREA_select, int line; int col; int selline; int selcol) + + MyMultiLineEdit *wid = QMULTILINEEDIT(_object); + + int line = PARAM(line); + int col = PARAM(col); + + look_pos(wid, &line, &col); + wid->setCursorPosition(line, col); + + line = PARAM(selline); + col = PARAM(selcol); + + look_pos(wid, &line, &col); + wid->setCursorPosition(line, col, TRUE); + +END_METHOD +*/ + +BEGIN_METHOD_VOID(CTEXTAREA_clear) + + WIDGET->clear(); + +END_METHOD + + +BEGIN_METHOD(CTEXTAREA_insert, GB_STRING text) + + WIDGET->textCursor().insertText(QSTRING_ARG(text)); + +END_METHOD + +/* +BEGIN_METHOD(CTEXTAREA_line_get, int line) + + int line = PARAM(line); + + if (line < 0 || line >= WIDGET->numLines()) + GB.ReturnVoidString(); + else + GB.ReturnNewZeroString(WIDGET->textLine(PARAM(line))); + +END_METHOD + + +BEGIN_PROPERTY(CTEXTAREA_line_count) + + GB.ReturnInteger(WIDGET->numLines()); + +END_PROPERTY +*/ + +/*************************************************************************** + + .TextArea.Selection + +***************************************************************************/ + + +BEGIN_PROPERTY(CTEXTAREA_sel_text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->textCursor().selection().toPlainText()); + else + WIDGET->textCursor().insertText(QSTRING_PROP()); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_sel_length) + + int start, length; + + get_selection(WIDGET, &start, &length); + GB.ReturnInteger(length); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_sel_start) + + int start, length; + + get_selection(WIDGET, &start, &length); + GB.ReturnInteger(start); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CTEXTAREA_sel_clear) + + QTextCursor cursor = WIDGET->textCursor(); + cursor.clearSelection(); + WIDGET->setTextCursor(cursor); + +END_METHOD + + +BEGIN_PROPERTY(CTEXTAREA_selected) + + GB.ReturnBoolean(WIDGET->textCursor().hasSelection()); + +END_PROPERTY + + +BEGIN_METHOD(CTEXTAREA_sel_select, GB_INTEGER start; GB_INTEGER length) + + if (MISSING(start) && MISSING(length)) + WIDGET->textCursor().select(QTextCursor::Document); + else if (!MISSING(start) && !MISSING(length)) + { + QTextCursor cursor = WIDGET->textCursor(); + + cursor.setPosition(VARG(start)); + cursor.setPosition(VARG(start) + VARG(length), QTextCursor::KeepAnchor); + + WIDGET->setTextCursor(cursor); + } + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_sel_all) //, GB_BOOLEAN sel) + + QTextCursor cursor = WIDGET->textCursor(); + cursor.select(QTextCursor::Document); + WIDGET->setTextCursor(cursor); + +END_METHOD + + +BEGIN_METHOD(CTEXTAREA_to_pos, GB_INTEGER line; GB_INTEGER col) + + int pos; + + to_pos(WIDGET, VARG(line), VARG(col), &pos); + + GB.ReturnInteger(pos); + +END_METHOD + + +BEGIN_METHOD(CTEXTAREA_to_line, GB_INTEGER pos) + + int line, col; + + from_pos(THIS, VARG(pos), &line, &col); + + GB.ReturnInteger(line); + +END_METHOD + + +BEGIN_METHOD(CTEXTAREA_to_col, GB_INTEGER pos) + + int line, col; + + from_pos(THIS, VARG(pos), &line, &col); + + GB.ReturnInteger(col); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_copy) + + WIDGET->copy(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_cut) + + WIDGET->cut(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_paste) + + WIDGET->paste(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_undo) + + WIDGET->undo(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_redo) + + WIDGET->redo(); + +END_METHOD + + +/*BEGIN_PROPERTY(CTEXTAREA_scrollbar) + + int scroll; + + if (READ_PROPERTY) + { + scroll = 0; + if (WIDGET->hScrollBarMode() == QScrollView::Auto) + scroll += 1; + if (WIDGET->vScrollBarMode() == QScrollView::Auto) + scroll += 2; + + GB.ReturnInteger(scroll); + } + else + { + scroll = VPROP(GB_INTEGER) & 3; + WIDGET->setHScrollBarMode( (scroll & 1) ? QScrollView::Auto : QScrollView::AlwaysOff); + WIDGET->setVScrollBarMode( (scroll & 2) ? QScrollView::Auto : QScrollView::AlwaysOff); + } + +END_PROPERTY*/ + + +BEGIN_METHOD_VOID(CTEXTAREA_ensure_visible) + + WIDGET->ensureCursorVisible(); + +END_METHOD + + +BEGIN_PROPERTY(TextArea_Alignment) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->align); + else + { + THIS->align = VPROP(GB_INTEGER); + update_alignment(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(TextArea_Border) + + CWIDGET_border_simple(_object, _param); + + if (!READ_PROPERTY) + WIDGET->document()->setDocumentMargin(VPROP(GB_BOOLEAN) ? 2 : 0); + +END_PROPERTY + +BEGIN_METHOD(TextArea_CursorAt, GB_INTEGER pos) + + QRect rect; + QTextCursor cursor = WIDGET->textCursor(); + + if (!MISSING(pos)) + cursor.setPosition(VARG(pos)); + + rect = WIDGET->cursorRect(cursor); + + GB.ReturnObject(GEOM.CreatePoint(rect.x() + WIDGET->viewport()->x(), rect.bottom() + WIDGET->viewport()->y())); + +END_PROPERTY + +GB_DESC CTextAreaSelectionDesc[] = +{ + GB_DECLARE(".TextArea.Selection", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Text", "s", CTEXTAREA_sel_text), + GB_PROPERTY_READ("Length", "i", CTEXTAREA_sel_length), + GB_PROPERTY_READ("Start", "i", CTEXTAREA_sel_start), + GB_PROPERTY_READ("Pos", "i", CTEXTAREA_sel_start), + + //GB_METHOD("Clear", NULL, CTEXTAREA_sel_clear, NULL), + GB_METHOD("Hide", NULL, CTEXTAREA_sel_clear, NULL), + + GB_END_DECLARE +}; + +GB_DESC CTextAreaDesc[] = +{ + GB_DECLARE("TextArea", sizeof(CTEXTAREA)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CTEXTAREA_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", CTEXTAREA_text), + GB_PROPERTY_READ("Length", "i", CTEXTAREA_length), + GB_PROPERTY("ReadOnly", "b", CTEXTAREA_read_only), + + //GB_PROPERTY_READ("Lines", ".TextArea.Line", CTEXTAREA_line_or_selection), + + GB_PROPERTY("ScrollBar", "i", CWIDGET_scrollbar), + GB_PROPERTY("Wrap", "b", CTEXTAREA_wrap), + GB_PROPERTY("Border", "b", TextArea_Border), + GB_PROPERTY("Alignment", "i", TextArea_Alignment), + + GB_PROPERTY("Line", "i", CTEXTAREA_line), + GB_PROPERTY("Column", "i", CTEXTAREA_column), + GB_PROPERTY("Pos", "i", CTEXTAREA_pos), + + GB_PROPERTY_SELF("Selection", ".TextArea.Selection"), + GB_METHOD("Select", NULL, CTEXTAREA_sel_select, "[(Start)i(Length)i]"), + GB_METHOD("SelectAll", NULL, CTEXTAREA_sel_all, NULL), + GB_METHOD("Unselect", NULL, CTEXTAREA_sel_clear, NULL), + GB_PROPERTY_READ("Selected", "b", CTEXTAREA_selected), + + GB_METHOD("Clear", NULL, CTEXTAREA_clear, NULL), + GB_METHOD("Insert", NULL, CTEXTAREA_insert, "(Text)s"), + + GB_METHOD("Copy", NULL, CTEXTAREA_copy, NULL), + GB_METHOD("Cut", NULL, CTEXTAREA_cut, NULL), + GB_METHOD("Paste", NULL, CTEXTAREA_paste, NULL), + GB_METHOD("Undo", NULL, CTEXTAREA_undo, NULL), + GB_METHOD("Redo", NULL, CTEXTAREA_redo, NULL), + + GB_METHOD("ToPos", "i", CTEXTAREA_to_pos, "(Line)i(Column)i"), + GB_METHOD("ToLine", "i", CTEXTAREA_to_line, "(Pos)i"), + GB_METHOD("ToColumn", "i", CTEXTAREA_to_col, "(Pos)i"), + + GB_METHOD("CursorAt", "Point", TextArea_CursorAt, "[(Pos)i]"), + + GB_METHOD("EnsureVisible", NULL, CTEXTAREA_ensure_visible, NULL), + + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + GB_EVENT("Cursor", NULL, NULL, &EVENT_Cursor), + + TEXTAREA_DESCRIPTION, + + GB_END_DECLARE +}; + + +/** CTextArea **************************************************************/ + +CTextArea CTextArea::manager; + +void CTextArea::changed(void) +{ + GET_SENDER(); + + if (THIS->no_change) + return; + + set_text_color(THIS); + THIS->length = -1; + GB.Raise(THIS, EVENT_Change, 0); +} + +void CTextArea::cursor(void) +{ + GET_SENDER(); + //set_text_color(THIS); + GB.Raise(THIS, EVENT_Cursor, 0); +} + +void CTextArea::link(const QString &path) +{ + GET_SENDER(); + const char *str = TO_UTF8(path); + GB.Raise(THIS, EVENT_Link, 1, GB_T_STRING, str, LAST_UTF8_LENGTH()); +} diff --git a/gb.qt4/src/CTextArea.h b/gb.qt4/src/CTextArea.h new file mode 100644 index 00000000..7abefa6f --- /dev/null +++ b/gb.qt4/src/CTextArea.h @@ -0,0 +1,70 @@ +/*************************************************************************** + + CTextArea.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTEXTAREA_H +#define __CTEXTAREA_H + +#include "gambas.h" + +#include "CWidget.h" + +#include + +#ifndef __CTEXTAREA_CPP +extern GB_DESC CTextAreaDesc[]; +extern GB_DESC CTextAreaSelectionDesc[]; +#else + +#define WIDGET ((QTextEdit *)((CWIDGET *)_object)->widget) +#define MYTEXTEDIT ((MyTextEdit *)((CWIDGET *)_object)->widget) +#define THIS ((CTEXTAREA *)_object) + +#endif + +typedef + struct { + CWIDGET widget; + int length; + int align; + unsigned no_change : 1; + } + CTEXTAREA; + +class CTextArea : public QObject +{ + Q_OBJECT + +public: + + static CTextArea manager; + +public slots: + + void changed(void); + void cursor(void); + void link(const QString &); +}; + +void CTEXTAREA_set_foreground(void *_object); + +#endif diff --git a/gb.qt4/src/CTextBox.cpp b/gb.qt4/src/CTextBox.cpp new file mode 100644 index 00000000..06ba6ddf --- /dev/null +++ b/gb.qt4/src/CTextBox.cpp @@ -0,0 +1,961 @@ +/*************************************************************************** + + CTextBox.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTEXTBOX_CPP + +#include +#include +#include +#include +#include +#include + +#include "gambas.h" + +#include "CConst.h" +#include "CTextBox.h" + +DECLARE_EVENT(EVENT_Change); +DECLARE_EVENT(EVENT_Activate); +DECLARE_EVENT(EVENT_Click); + +#define MAX_LEN 32767 + + +/*************************************************************************** + + TextBox + +***************************************************************************/ + +static bool get(void *_object, QLineEdit **wid, bool error = true) +{ + QComboBox *combo; + + if (qobject_cast(TEXTBOX)) + { + *wid = TEXTBOX; + return false; + } + + combo = COMBOBOX; + if (!combo->isEditable()) + { + if (error) + GB.Error("ComboBox is read-only"); + return true; + } + + *wid = combo->lineEdit(); + return false; +} + +#define GET_TEXT_BOX() \ + QLineEdit *textbox; \ + if (get(_object, &textbox)) \ + return; + + + +BEGIN_METHOD(TextBox_new, GB_OBJECT parent) + + QLineEdit *wid = new QLineEdit(QCONTAINER(VARG(parent))); + + QObject::connect(wid, SIGNAL(textChanged(const QString &)), &CTextBox::manager, SLOT(onChange())); + QObject::connect(wid, SIGNAL(returnPressed()), &CTextBox::manager, SLOT(onActivate())); + //QObject::connect(wid, SIGNAL(selectionChanged()), &CTextBox::manager, SLOT(onSelectionChanged())); + + wid->setAlignment(Qt::AlignLeft); + + CWIDGET_new(wid, (void *)_object); + +END_METHOD + + +BEGIN_METHOD_VOID(TextBox_Clear) + + TEXTBOX->clear(); + +END_METHOD + +BEGIN_METHOD(TextBox_Insert, GB_STRING text) + + GET_TEXT_BOX(); + + //textbox->insert(QString(GB.ToZeroString(ARG(text)))); + textbox->insert(QSTRING_ARG(text)); + +END_METHOD + + +BEGIN_PROPERTY(TextBox_Text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(TEXTBOX->text()); + else + { + TEXTBOX->deselect(); + TEXTBOX->setText(QSTRING_PROP()); + } + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Length) + + GB.ReturnInteger(TEXTBOX->text().length()); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Alignment) + + if (READ_PROPERTY) + GB.ReturnInteger(CCONST_alignment(TEXTBOX->alignment() + Qt::AlignVCenter, ALIGN_NORMAL, false)); + else + TEXTBOX->setAlignment((Qt::Alignment)CCONST_alignment(VPROP(GB_INTEGER), ALIGN_NORMAL, true) & Qt::AlignHorizontal_Mask); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Pos) + + GET_TEXT_BOX(); + + if (READ_PROPERTY) + GB.ReturnInteger(textbox->cursorPosition()); + else + textbox->setCursorPosition(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_ReadOnly) + + if (READ_PROPERTY) + GB.ReturnBoolean(TEXTBOX->isReadOnly()); + else + TEXTBOX->setReadOnly(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Border) + + GET_TEXT_BOX(); + + if (READ_PROPERTY) + GB.ReturnBoolean(textbox->hasFrame()); + else + textbox->setFrame(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Password) + + GET_TEXT_BOX(); + + if (READ_PROPERTY) + GB.ReturnBoolean(textbox->echoMode() != QLineEdit::Normal); + else + textbox->setEchoMode(VPROP(GB_BOOLEAN) ? QLineEdit::Password : QLineEdit::Normal); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_MaxLength) + + int max; + + GET_TEXT_BOX(); + + if (READ_PROPERTY) + { + max = textbox->maxLength(); + GB.ReturnInteger(max >= MAX_LEN ? 0 : max); + } + else + { + max = VPROP(GB_INTEGER); + if (max < 1 || max > MAX_LEN) + max = MAX_LEN; + + textbox->setMaxLength(max); + } + +END_PROPERTY + + +BEGIN_METHOD(TextBox_CursorAt, GB_INTEGER pos) + + QRect rect; + int save = -1; + + GET_TEXT_BOX(); + + if (!MISSING(pos)) + { + save = textbox->cursorPosition(); + textbox->setCursorPosition(VARG(pos)); + } + + // Hack to call cursorRect() + rect = textbox->inputMethodQuery(Qt::ImMicroFocus).toRect(); + + if (save >= 0) + textbox->setCursorPosition(save); + + GB.ReturnObject(GEOM.CreatePoint((rect.left() + rect.right()) / 2 + 1, rect.bottom())); + +END_PROPERTY + +/*************************************************************************** + + .TextBox.Selection + +***************************************************************************/ + +BEGIN_PROPERTY(TextBox_Selection_Text) + + GET_TEXT_BOX(); + + if (READ_PROPERTY) + RETURN_NEW_STRING(textbox->selectedText()); + else + textbox->insert(QSTRING_PROP()); + +END_PROPERTY + + +static void set_selection(QLineEdit *textbox, int start, int length) +{ + int len = (int)textbox->text().length(); + + if (start < 0 || start >= len) + { + start = textbox->cursorPosition(); + length = 0; + } + + textbox->setCursorPosition(start); + + if (length <= 0) + textbox->deselect(); + else + { + if ((start + length) >= len) + length = len - start; + textbox->setSelection(start, length); + } +} + +static void get_selection(QLineEdit *textbox, int *start, int *length) +{ + *start = textbox->selectionStart(); + if (*start < 0) + *start = textbox->cursorPosition(); + if (!textbox->hasSelectedText()) + *length = 0; + else + *length = textbox->selectedText().length(); +} + + +BEGIN_PROPERTY(TextBox_Selection_Length) + + int start, length; + + GET_TEXT_BOX(); + + get_selection(textbox, &start, &length); + + GB.ReturnInteger(length); + +END_PROPERTY + + +BEGIN_PROPERTY(TextBox_Selection_Start) + + int start, length; + + GET_TEXT_BOX(); + + get_selection(textbox, &start, &length); + GB.ReturnInteger(start); + +END_PROPERTY + + +BEGIN_METHOD_VOID(TextBox_Unselect) + + GET_TEXT_BOX(); + + textbox->deselect(); + +END_METHOD + +BEGIN_METHOD_VOID(ComboBox_Selected) + + GET_TEXT_BOX(); + + GB.ReturnBoolean(textbox->hasSelectedText()); + +END_METHOD + + +BEGIN_METHOD(TextBox_Select, GB_INTEGER start; GB_INTEGER length) + + GET_TEXT_BOX(); + + if (MISSING(start) && MISSING(length)) + textbox->selectAll(); + else if (!MISSING(start) && !MISSING(length)) + set_selection(textbox, VARG(start), VARG(length)); + +END_METHOD + +BEGIN_METHOD_VOID(TextBox_SelectAll) + + GET_TEXT_BOX(); + + textbox->selectAll(); + +END_METHOD + + +/*************************************************************************** + + ComboBox + +***************************************************************************/ + +#undef THIS +#define THIS OBJECT(CCOMBOBOX) + +static void raise_click_event(void *_object) +{ + if (THIS->click) + return; + THIS->click = true; + GB.Raise(THIS, EVENT_Click, 0); + THIS->click = false; + +} + +static int combo_find_item(void *_object, const QString& s) +{ + COMBOBOX->sort(); + for (int i = 0; i < (int)COMBOBOX->count(); i++) + { + if (COMBOBOX->itemText(i) == s) + return i; + } + + return (-1); +} + +static int combo_get_current_item(void *_object) +{ + COMBOBOX->sort(); + + if (COMBOBOX->isEditable()) + return combo_find_item(THIS, COMBOBOX->lineEdit()->text()); + else + return COMBOBOX->count() == 0 ? -1 : COMBOBOX->currentIndex(); +} + +static void combo_set_current_item(void *_object, int item) +{ + COMBOBOX->sort(); + + if (COMBOBOX->isEditable()) + { + if (item < 0 || item >= COMBOBOX->count()) + COMBOBOX->lineEdit()->clear(); + else + { + COMBOBOX->setCurrentIndex(item); + COMBOBOX->lineEdit()->setText(COMBOBOX->itemText(item)); + } + } + else + { + if (item != combo_get_current_item(THIS)) + { + if (item < COMBOBOX->count()) + COMBOBOX->setCurrentIndex(item); + } + } + + if (item >= 0 && !COMBOBOX->signalsBlocked()) + raise_click_event(THIS); +} + +static void combo_set_text(CCOMBOBOX *_object, QString &text) +{ + int pos; + + pos = combo_find_item(THIS, text); + if (!COMBOBOX->isEditable() || pos >= 0) + combo_set_current_item(_object, pos); + if (COMBOBOX->isEditable()) + { + COMBOBOX->lineEdit()->deselect(); + COMBOBOX->lineEdit()->setText(text); + } +} + +static void combo_set_editable(void *_object, bool ed) +{ + QLineEdit *textbox; + QString text; + bool hasFocus = COMBOBOX->hasFocus(); + + if (ed == COMBOBOX->isEditable()) + return; + + COMBOBOX->blockSignals(true); + text = COMBOBOX->currentText(); + + if (ed) + { + //CWidget::removeFilter(COMBOBOX); + COMBOBOX->setEditable(true); + COMBOBOX->setCompleter(0); + //CWidget::installFilter(COMBOBOX); + QObject::connect(COMBOBOX->lineEdit(), SIGNAL(returnPressed()), &CTextBox::manager, SLOT(onActivate())); + //QObject::connect(COMBOBOX->lineEdit(), SIGNAL(selectionChanged()), &CTextBox::manager, SLOT(onSelectionChanged())); + + if (CWIDGET_test_flag(THIS, WF_DESIGN)) + { + get(_object, &textbox); + //textbox->removeEventFilter(COMBOBOX); + COMBOBOX->setFocusProxy(0); + } + } + else + { + get(THIS, &textbox); + textbox->setFocusProxy(0); + COMBOBOX->setEditable(false); + COMBOBOX->update(); + } + + combo_set_text(THIS, text); + CWIDGET_reset_color((CWIDGET *)THIS); + + if (hasFocus) + COMBOBOX->setFocus(); + + if (CWIDGET_test_flag(THIS, WF_DESIGN)) + COMBOBOX->setFocusPolicy(Qt::NoFocus); + + COMBOBOX->blockSignals(false); +} + +static void combo_get_list(void *_object, GB_ARRAY array) +{ + int i; + + COMBOBOX->sort(); + for (i = 0; i < COMBOBOX->count(); i++) + { + *((char **)GB.Array.Get(array, i)) = NEW_STRING(COMBOBOX->itemText(i)); + } +} + + +static void combo_set_list(void *_object, GB_ARRAY array) +{ + int i; + QString text = COMBOBOX->currentText(); + + COMBOBOX->blockSignals(true); + COMBOBOX->clear(); + + if (array) + { + for (i = 0; i < GB.Array.Count(array); i++) + { + COMBOBOX->insertItem(i, TO_QSTRING(*((char **)GB.Array.Get(array, i)))); + } + } + + COMBOBOX->setDirty(); + combo_set_text(THIS, text); + + if (!COMBOBOX->isEditable()) + { + if (combo_get_current_item(THIS) < 0) + combo_set_current_item(THIS, 0); + } + + COMBOBOX->blockSignals(false); +} + + + +BEGIN_METHOD(ComboBox_new, GB_OBJECT parent) + + MyComboBox *wid = new MyComboBox(QCONTAINER(VARG(parent))); + + THIS->widget.flag.wheel = true; + + QObject::connect(wid, SIGNAL(editTextChanged(const QString &)), &CTextBox::manager, SLOT(onChange())); + QObject::connect(wid, SIGNAL(activated(int)), &CTextBox::manager, SLOT(onClick())); + + CWIDGET_new(wid, (void *)_object); + + combo_set_editable(_object, true); + +END_METHOD + + +BEGIN_METHOD_VOID(ComboBox_Clear) + + COMBOBOX->clear(); + COMBOBOX->clearEditText(); + +END_METHOD + + +BEGIN_METHOD_VOID(ComboBox_Popup) + + COMBOBOX->showPopup(); + +END_METHOD + + +BEGIN_PROPERTY(ComboBox_Text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(COMBOBOX->currentText()); + else + { + QString text = QSTRING_PROP(); + combo_set_text(THIS, text); + } + +END_PROPERTY + + +BEGIN_PROPERTY(ComboBox_Length) + + GB.ReturnInteger(COMBOBOX->currentText().length()); + +END_PROPERTY + + + +BEGIN_PROPERTY(ComboBox_ReadOnly) + + if (READ_PROPERTY) + GB.ReturnBoolean(!COMBOBOX->isEditable()); + else + combo_set_editable(_object, !VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_METHOD(ComboBox_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= (int)COMBOBOX->count()) + { + GB.Error("Bad index"); + return; + } + + THIS->index = index; + + RETURN_SELF(); + +END_METHOD + + +BEGIN_PROPERTY(CCOMBOBOX_item_text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(COMBOBOX->itemText(THIS->index)); + else + { + COMBOBOX->blockSignals(true); + COMBOBOX->setItemText(THIS->index, QSTRING_PROP()); + COMBOBOX->setDirty(); + COMBOBOX->blockSignals(false); + } + +END_PROPERTY + + +BEGIN_METHOD(ComboBox_Add, GB_STRING item; GB_INTEGER pos) + + int index; + int pos = VARGOPT(pos, -1); + + COMBOBOX->blockSignals(true); + index = combo_get_current_item(THIS); + + if (pos < 0 || pos >= COMBOBOX->count()) + pos = -1; + + if (pos < 0) + COMBOBOX->addItem(QSTRING_ARG(item)); + else + COMBOBOX->insertItem(pos, QSTRING_ARG(item)); + + COMBOBOX->setDirty(); + + if (index >= 0) + combo_set_current_item(THIS, index); + else + combo_set_current_item(THIS, 0); + + COMBOBOX->blockSignals(false); + +END_METHOD + + +BEGIN_METHOD(ComboBox_Remove, GB_INTEGER pos) + + COMBOBOX->blockSignals(true); + COMBOBOX->removeItem(VARG(pos)); + COMBOBOX->setDirty(); + COMBOBOX->blockSignals(false); + +END_METHOD + + +BEGIN_PROPERTY(ComboBox_Sorted) + + if (READ_PROPERTY) + GB.ReturnBoolean(COMBOBOX->isSortingEnabled()); + else + COMBOBOX->setSortingEnabled(VPROP(GB_BOOLEAN)); + +END_METHOD + + +BEGIN_PROPERTY(ComboBox_Count) + + GB.ReturnInteger(COMBOBOX->count()); + +END_PROPERTY + + +BEGIN_PROPERTY(ComboBox_Index) + + if (READ_PROPERTY) + GB.ReturnInteger(combo_get_current_item(THIS)); + else + combo_set_current_item(THIS, VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(ComboBox_Current) + + THIS->index = combo_get_current_item(THIS); + + if (THIS->index < 0) + GB.ReturnNull(); + else + RETURN_SELF(); + +END_PROPERTY + + +/* +BEGIN_PROPERTY(CCOMBOBOX_mouse) + + if (READ_PROPERTY) + GB.ReturnInteger(COMBOBOX->cursor().shape()); + else + { + if (COMBOBOX->editable()) + COMBOBOX->lineEdit()->setCursor(PROPERTY(int)); + + COMBOBOX->setCursor(PROPERTY(int)); + } + +END_METHOD +*/ + +BEGIN_METHOD(ComboBox_Find, GB_STRING item) + + GB.ReturnInteger(combo_find_item(THIS, QSTRING_ARG(item))); + +END_METHOD + + +BEGIN_PROPERTY(ComboBox_List) + + GB_ARRAY array; + + if (READ_PROPERTY) + { + GB.Array.New(&array, GB_T_STRING, COMBOBOX->count()); + combo_get_list(THIS, array); + GB.ReturnObject(array); + } + else + { + combo_set_list(THIS, (GB_ARRAY)VPROP(GB_OBJECT)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(ComboBox_Border) + + if (READ_PROPERTY) + GB.ReturnBoolean(COMBOBOX->hasFrame()); + else + { + COMBOBOX->setFrame(VPROP(GB_BOOLEAN)); + QEvent e(QEvent::FontChange); + COMBOBOX->changeEvent(&e); + } + +END_PROPERTY + + + + +/*************************************************************************** + + class MyComboBox + +***************************************************************************/ + +MyComboBox::MyComboBox(QWidget *parent) : + QComboBox(parent) +{ + _sorted = _dirty = false; + setCompleter(0); + setInsertPolicy(NoInsert); + calcMinimumHeight(); +} + + +void MyComboBox::changeEvent(QEvent *e) +{ + QComboBox::changeEvent(e); + if (e->type() == QEvent::FontChange || e->type() == QEvent::StyleChange) + calcMinimumHeight(); +} + +void MyComboBox::calcMinimumHeight() +{ + QFontMetrics fm = fontMetrics(); + + if (isEditable()) + setMinimumHeight(fm.lineSpacing() + height() - lineEdit()->height()); + else + setMinimumHeight(fm.lineSpacing() + 2); +} + +void MyComboBox::sort() +{ + if (_sorted && _dirty) + { + model()->sort(0); + _dirty = false; + } +} + +void MyComboBox::showPopup() +{ + sort(); + QComboBox::showPopup(); +} + +/*************************************************************************** + + class CTextBox + +***************************************************************************/ + +CTextBox CTextBox::manager; + +void CTextBox::onChange(void) +{ + RAISE_EVENT(EVENT_Change); +} + + +void CTextBox::onActivate(void) +{ + RAISE_EVENT(EVENT_Activate); +} + + +void CTextBox::onClick() +{ + GET_SENDER(); + raise_click_event(THIS); +} + + +/*void CTextBox::onSelectionChanged(void) +{ + GET_SENDER(); + GET_TEXT_BOX(); + + if (THIS->locked) + return; + + if (CCONTROL_last_event_type == QEvent::MetaCall) + { + THIS->locked = true; + set_selection(textbox, THIS->start, THIS->length); + THIS->locked = false; + } + else + { + get_selection(textbox, &THIS->start, &THIS->length); + } +}*/ + + + + +/*************************************************************************** + + Descriptions + +***************************************************************************/ + +GB_DESC CTextBoxSelectionDesc[] = +{ + GB_DECLARE(".TextBox.Selection", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Text", "s", TextBox_Selection_Text), + GB_PROPERTY_READ("Length", "i", TextBox_Selection_Length), + GB_PROPERTY_READ("Start", "i", TextBox_Selection_Start), + GB_PROPERTY_READ("Pos", "i", TextBox_Selection_Start), + + GB_METHOD("Hide", NULL, TextBox_Unselect, NULL), + + GB_END_DECLARE +}; + + +GB_DESC CTextBoxDesc[] = +{ + GB_DECLARE("TextBox", sizeof(CTEXTBOX)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, TextBox_new, "(Parent)Container;"), + + GB_PROPERTY("Text", "s", TextBox_Text), + GB_PROPERTY("Alignment", "i", TextBox_Alignment), + GB_PROPERTY_READ("Length", "i", TextBox_Length), + GB_PROPERTY("Pos", "i", TextBox_Pos), + GB_PROPERTY("ReadOnly", "b", TextBox_ReadOnly), + GB_PROPERTY("Border", "b", TextBox_Border), + GB_PROPERTY("Password", "b", TextBox_Password), + GB_PROPERTY("MaxLength", "i", TextBox_MaxLength), + + GB_PROPERTY_SELF("Selection", ".TextBox.Selection"), + GB_METHOD("Select", NULL, TextBox_Select, "[(Start)i(Length)i]"), + GB_METHOD("SelectAll", NULL, TextBox_SelectAll, NULL), + GB_METHOD("Unselect", NULL, TextBox_Unselect, NULL), + GB_PROPERTY_READ("Selected", "b", ComboBox_Selected), + + GB_METHOD("Clear", NULL, TextBox_Clear, NULL), + GB_METHOD("Insert", NULL, TextBox_Insert, "(Text)s"), + + GB_METHOD("CursorAt", "Point", TextBox_CursorAt, "[(Pos)i]"), + + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + GB_EVENT("Activate", NULL, NULL, &EVENT_Activate), + + TEXTBOX_DESCRIPTION, + + GB_END_DECLARE +}; + + +GB_DESC CComboBoxItemDesc[] = +{ + GB_DECLARE(".ComboBox.Item", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Text", "s", CCOMBOBOX_item_text), + + GB_END_DECLARE +}; + + +GB_DESC CComboBoxDesc[] = +{ + GB_DECLARE("ComboBox", sizeof(CCOMBOBOX)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, ComboBox_new, "(Parent)Container;"), + GB_METHOD("_get", ".ComboBox.Item", ComboBox_get, "(Index)i"), + + GB_PROPERTY("Text", "s", ComboBox_Text), + GB_PROPERTY_READ("Length", "i", ComboBox_Length), + GB_PROPERTY("Pos", "i", TextBox_Pos), + GB_PROPERTY("ReadOnly", "b", ComboBox_ReadOnly), + GB_PROPERTY("Password", "b", TextBox_Password), + GB_PROPERTY("MaxLength", "i", TextBox_MaxLength), + GB_PROPERTY("Border", "b", ComboBox_Border), + + GB_PROPERTY_SELF("Selection", ".TextBox.Selection"), + GB_METHOD("Select", NULL, TextBox_Select, "[(Start)i(Length)i]"), + GB_METHOD("SelectAll", NULL, TextBox_SelectAll, NULL), + GB_METHOD("Unselect", NULL, TextBox_Unselect, NULL), + GB_PROPERTY_READ("Selected", "b", ComboBox_Selected), + + GB_METHOD("Popup", NULL, ComboBox_Popup, NULL), + GB_METHOD("Clear", NULL, ComboBox_Clear, NULL), + GB_METHOD("Insert", NULL, TextBox_Insert, "(Text)s"), + + GB_METHOD("Add", NULL, ComboBox_Add, "(Item)s[(Index)i]"), + GB_METHOD("Remove", NULL, ComboBox_Remove, "(Index)i"), + + GB_METHOD("Find", "i", ComboBox_Find, "(Item)s"), + + GB_PROPERTY("Sorted", "b", ComboBox_Sorted), + + GB_PROPERTY("List", "String[]", ComboBox_List), + //GB_PROPERTY("Contents", "s", ComboBox_List), + + GB_PROPERTY_READ("Count", "i", ComboBox_Count), + GB_PROPERTY_READ("Current", ".ComboBox.Item", ComboBox_Current), + GB_PROPERTY("Index", "i", ComboBox_Index), + + GB_METHOD("CursorAt", "Point", TextBox_CursorAt, "[(Pos)i]"), + + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + GB_EVENT("Activate", NULL, NULL, &EVENT_Activate), + GB_EVENT("Click", NULL, NULL, &EVENT_Click), + + COMBOBOX_DESCRIPTION, + + GB_END_DECLARE +}; + + diff --git a/gb.qt4/src/CTextBox.h b/gb.qt4/src/CTextBox.h new file mode 100644 index 00000000..7df59df8 --- /dev/null +++ b/gb.qt4/src/CTextBox.h @@ -0,0 +1,112 @@ +/*************************************************************************** + + CTextBox.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTEXTBOX_H +#define __CTEXTBOX_H + +#include "gambas.h" + +#include +#include + +#include "CWidget.h" + +#ifndef __CTEXTBOX_CPP + +extern GB_DESC CTextBoxSelectionDesc[]; +extern GB_DESC CTextBoxDesc[]; +extern GB_DESC CComboBoxDesc[]; +extern GB_DESC CComboBoxItemDesc[]; + +#else + +#define QLINEEDIT(object) ((QLineEdit *)((CWIDGET *)object)->widget) + +#define TEXTBOX ((QLineEdit *)((CWIDGET *)_object)->widget) +#define COMBOBOX ((MyComboBox *)((CWIDGET *)_object)->widget) +#define THIS ((CTEXTBOX *)_object) + +#endif + +typedef + struct { + CWIDGET widget; + int start; + int length; + int locked; + } + CTEXTBOX; + +typedef + struct { + CWIDGET widget; + int start; + int length; + int locked; + int index; + bool click; + } + CCOMBOBOX; + + +class MyComboBox : public QComboBox +{ + Q_OBJECT + +public: + + MyComboBox(QWidget *parent); + virtual void changeEvent(QEvent *e); + void calcMinimumHeight(); + bool isSortingEnabled() const { return _sorted; } + void setSortingEnabled(bool v) { _sorted = v; if (v) setDirty(); } + void setDirty() { _dirty = true; } + void sort(); + + virtual void showPopup(); + +private: + bool _sorted; + bool _dirty; +}; + + +class CTextBox : public QObject +{ + Q_OBJECT + +public: + + static CTextBox manager; + +public slots: + + void onChange(void); + void onActivate(void); + void onClick(void); + //void onSelectionChanged(void); + +}; + + +#endif diff --git a/gb.qt4/src/CWatch.cpp b/gb.qt4/src/CWatch.cpp new file mode 100644 index 00000000..142a0590 --- /dev/null +++ b/gb.qt4/src/CWatch.cpp @@ -0,0 +1,153 @@ +/*************************************************************************** + + CWatch.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWATCH_CPP + +//#include "gb_common.h" +#include "main.h" +#include "CWatch.h" + +QHash CWatch::readDict; +QHash CWatch::writeDict; +int CWatch::count = 0; + +void CWatch::watch(int fd, int type, GB_WATCH_CALLBACK callback, intptr_t param) +{ + CWatch *watch; + + //qDebug("CWatch::watch: %d %d", fd, type); + + switch (type) + { + case GB_WATCH_NONE: + + watch = readDict[fd]; + if (watch) delete watch; + + watch = writeDict[fd]; + if (watch) delete watch; + + break; + + case GB_WATCH_READ: + + if (callback) + new CWatch(fd, QSocketNotifier::Read, callback, param); + else + { + watch = readDict[fd]; + if (watch) delete watch; + } + break; + + case GB_WATCH_WRITE: + + if (callback) + new CWatch(fd, QSocketNotifier::Write, callback, param); + else + { + watch = writeDict[fd]; + if (watch) delete watch; + } + break; + } +} + +void CWatch::stop() +{ + int fd; + + for (fd = 0; count > 0; fd++) + watch(fd, GB_WATCH_NONE, 0, 0); +} + +CWatch::CWatch(int fd, QSocketNotifier::Type type, GB_WATCH_CALLBACK callback, intptr_t param) +{ + count++; + + if (type == QSocketNotifier::Read) + { + if (readDict[fd]) + delete readDict[fd]; + } + else if (type == QSocketNotifier::Write) + { + if (writeDict[fd]) + delete writeDict[fd]; + } + + notifier = new QSocketNotifier(fd, type); + this->callback = callback; + this->param = param; + + if (type == QSocketNotifier::Read) + { + //qDebug("CWatch: %d (read)", fd); + + readDict.insert(fd, this); + QObject::connect(notifier, SIGNAL(activated(int)), this, SLOT(read(int))); + } + else if (type == QSocketNotifier::Write) + { + //qDebug("CWatch: %d (write)", fd); + + writeDict.insert(fd, this); + QObject::connect(notifier, SIGNAL(activated(int)), this, SLOT(write(int))); + } +} + + +CWatch::~CWatch() +{ + if (notifier->type() == QSocketNotifier::Read) + { + //qDebug("~CWatch: %d (read)", notifier->socket()); + readDict.remove(notifier->socket()); + } + else if (notifier->type() == QSocketNotifier::Write) + { + //qDebug("~CWatch: %d (write)", notifier->socket()); + writeDict.remove(notifier->socket()); + } + + //BREAKPOINT(); + + delete notifier; + + count--; + MAIN_check_quit(); +} + +void CWatch::read(int fd) +{ + //qDebug("CWatch::read: fd = %d readDict[fd] = %p", fd, readDict[fd]); + if (readDict[fd]) + (*callback)(fd, GB_WATCH_READ, param); +} + +void CWatch::write(int fd) +{ + //qDebug("CWatch::write: fd = %d writeDict[fd] = %p", fd, writeDict[fd]); + if (writeDict[fd]) + (*callback)(fd, GB_WATCH_WRITE, param); +} diff --git a/gb.qt4/src/CWatch.h b/gb.qt4/src/CWatch.h new file mode 100644 index 00000000..60e114eb --- /dev/null +++ b/gb.qt4/src/CWatch.h @@ -0,0 +1,62 @@ +/*************************************************************************** + + CWatch.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWATCH_H +#define __CWATCH_H + +#include +#include +#include + +#include "gambas.h" + +class CWatch: public QObject +{ + Q_OBJECT + +public: + + static void watch(int fd, int type, GB_WATCH_CALLBACK callback, intptr_t param); + static void stop(); + static int count; + + CWatch(int fd, QSocketNotifier::Type type, GB_WATCH_CALLBACK callback, intptr_t param); + ~CWatch(); + +private: + + static QHash readDict; + static QHash writeDict; + + QSocketNotifier *notifier; + GB_WATCH_CALLBACK callback; + intptr_t param; + +public slots: + + void read(int); + void write(int); +}; + + +#endif diff --git a/gb.qt4/src/CWatcher.cpp b/gb.qt4/src/CWatcher.cpp new file mode 100644 index 00000000..c708f115 --- /dev/null +++ b/gb.qt4/src/CWatcher.cpp @@ -0,0 +1,179 @@ +/*************************************************************************** + + CWatcher.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWATCHER_CPP + +#include + +#include "main.h" +#include "CWatcher.h" + +DECLARE_EVENT(EVENT_Move); +DECLARE_EVENT(EVENT_Resize); +DECLARE_EVENT(EVENT_Show); +DECLARE_EVENT(EVENT_Hide); +//DECLARE_EVENT(EVENT_Remove); + +/** ChildEvent class ********************************************************/ + +struct ChildEvent +{ + int event; + void *watcher; + void *child; + + ChildEvent(int e, void *w, void *c); + ~ChildEvent(); +}; + +ChildEvent::ChildEvent(int e, void *w, void *c) +{ + event = e; + watcher = w; + child = c; + + GB.Ref(watcher); + if (child) + GB.Ref(child); +} + +ChildEvent::~ChildEvent() +{ + GB.Unref(&watcher); + if (child) + GB.Unref(&child); +} + +/** CWatcher class ********************************************************/ + +CWatcher::CWatcher(CWATCHER *w, CWIDGET *o) +{ + watcher = w; + control = o; + + GB.Ref(control); + + widget = QWIDGET(control); + cont = 0; + + if (GB.Is(control, CLASS_Container)) + cont = QCONTAINER(control); + if (cont == widget) + cont = 0; + + widget->installEventFilter(this); + if (cont) + cont->installEventFilter(this); + QObject::connect(widget, SIGNAL(destroyed()), this, SLOT(destroy())); +} + +CWatcher::~CWatcher() +{ + if (control) + { + if (control->widget) + { + if (cont) + cont->removeEventFilter(this); + widget->removeEventFilter(this); + } + + GB.Unref(POINTER(&control)); + } +} + +void send_event(void *watcher, intptr_t event) +{ + GB.Raise(watcher, (int)event, 0); + GB.Unref(&watcher); +} + +bool CWatcher::eventFilter(QObject* o, QEvent *e) +{ + if (o == widget) + { + if (e->type() == QEvent::Move) + GB.Raise(watcher, EVENT_Move, 0); + else if (e->type() == QEvent::Resize) + GB.Raise(watcher, EVENT_Resize, 0); + else if (e->type() == QEvent::Show) + GB.Raise(watcher, EVENT_Show, 0); + else if (e->type() == QEvent::Hide) + GB.Raise(watcher, EVENT_Hide, 0); + } + + return false; //return QObject::eventFilter(o, e); +} + +void CWatcher::destroy() +{ + GB.Unref(POINTER(&control)); + control = 0; +} + +/** Watcher class *********************************************************/ + +BEGIN_METHOD(CWATCHER_new, GB_OBJECT control) + + CWIDGET *control = (CWIDGET *)VARG(control); + + if (GB.CheckObject(control)) + return; + + THIS->watcher = new CWatcher(THIS, control); + + // No need to reference control, as it is already referenced as event observer! + +END_METHOD + +BEGIN_METHOD_VOID(CWATCHER_free) + + delete THIS->watcher; + THIS->watcher = 0; + +END_METHOD + +BEGIN_PROPERTY(CWATCHER_control) + + GB.ReturnObject(THIS->watcher->getControl()); + +END_PROPERTY + +GB_DESC CWatcherDesc[] = +{ + GB_DECLARE("Watcher", sizeof(CWATCHER)), + + GB_METHOD("_new", NULL, CWATCHER_new, "(Control)Control;"), + GB_METHOD("_free", NULL, CWATCHER_free, NULL), + + GB_PROPERTY("Control", "Control", CWATCHER_control), + + GB_EVENT("Move", NULL, NULL, &EVENT_Move), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + GB_EVENT("Show", NULL, NULL, &EVENT_Show), + GB_EVENT("Hide", NULL, NULL, &EVENT_Hide), + + GB_CONSTANT("_DefaultEvent", "s", "Resize"), + + GB_END_DECLARE +}; diff --git a/gb.qt4/src/CWatcher.h b/gb.qt4/src/CWatcher.h new file mode 100644 index 00000000..8a4a91cc --- /dev/null +++ b/gb.qt4/src/CWatcher.h @@ -0,0 +1,74 @@ +/*************************************************************************** + + CWatcher.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWATCHER_H +#define __CWATCHER_H + +#include +//Added by qt3to4: +#include + +#include "gambas.h" +#include "CWidget.h" + +class CWatcher; + +typedef + struct { + GB_BASE ob; + CWatcher *watcher; + } + CWATCHER; + +#ifndef __CWATCHER_CPP +extern GB_DESC CWatcherDesc[]; +#else +#define THIS OBJECT(CWATCHER) +#endif + +class CWatcher: public QObject +{ + Q_OBJECT + +public: + + CWatcher(CWATCHER *watcher, CWIDGET *o); + ~CWatcher(); + + bool eventFilter(QObject *, QEvent *); + CWIDGET *getControl() { return control; } + +public slots: + + void destroy(); + +private: + + CWATCHER *watcher; + CWIDGET *control; + QWidget *widget; + QWidget *cont; +}; + + +#endif diff --git a/gb.qt4/src/CWidget.cpp b/gb.qt4/src/CWidget.cpp new file mode 100644 index 00000000..22380718 --- /dev/null +++ b/gb.qt4/src/CWidget.cpp @@ -0,0 +1,3435 @@ +/*************************************************************************** + + CWidget.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWIDGET_CPP + +#undef QT3_SUPPORT + +#include "gambas.h" +#include "gb_common.h" + +#include +#include + +#include "CWidget.h" +#include "CFont.h" +#include "CMouse.h" +#include "CKey.h" +#include "CWindow.h" +#include "CConst.h" +#include "CColor.h" +#include "CClipboard.h" +#include "CMenu.h" +#include "CDrawingArea.h" +#include "CTextArea.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef NO_X_WINDOW +static QMap _x11_to_qt_keycode; +#endif + +CWIDGET *CWIDGET_active_control = 0; +CWIDGET *CWIDGET_previous_control = 0; +CWIDGET *CWIDGET_hovered = 0; + +int CCONTROL_last_event_type = 0; + +static bool _focus_change = false; +static CWIDGET *_old_active_control = 0; +static CWIDGET *_hovered = 0; +static CWIDGET *_official_hovered = 0; + +#ifdef QT5 +static CWIDGET *_last_entered = NULL; +#else +static QSet *_enter_leave_set = NULL; +#endif + +static QT_COLOR_FUNC _after_set_color = NULL; + +#define EXT(_ob) ((CWIDGET_EXT *)((CWIDGET *)_ob)->ext) +#define ENSURE_EXT(_ob) (EXT(_ob) ? EXT(_ob) : alloc_ext((CWIDGET *)(_ob))) + +#define HANDLE_PROXY(_ob) \ + while (EXT(_ob) && EXT(_ob)->proxy) \ + _ob = (__typeof__ _ob)(EXT(_ob)->proxy); + +static CWIDGET_EXT *alloc_ext(CWIDGET *_object) +{ + GB.Alloc(POINTER(&(THIS->ext)), sizeof(CWIDGET_EXT)); + CLEAR(THIS_EXT); + THIS_EXT->bg = COLOR_DEFAULT; + THIS_EXT->fg = COLOR_DEFAULT; + THIS_EXT->tag.type = GB_T_NULL; + return THIS_EXT; +} + +static void set_mouse(QWidget *w, int mouse, void *cursor) +{ + QObjectList children; + QObject *child; + int i; + + if (mouse == CMOUSE_DEFAULT) + w->unsetCursor(); + else if (mouse == CMOUSE_CUSTOM) + { + if (cursor) + w->setCursor(*((CCURSOR *)cursor)->cursor); + else + w->unsetCursor(); + } + else + w->setCursor(QCursor((Qt::CursorShape)mouse)); + + children = w->children(); + + for (i = 0; i < children.count(); i++) + { + child = children.at(i); + + if (child->isWidgetType() && !CWidget::getReal(child)) + set_mouse((QWidget *)child, CMOUSE_DEFAULT, 0); + } +} + +static void set_design_object(CWIDGET *_object) +{ + if (CWIDGET_test_flag(THIS, WF_DESIGN)) + return; + + //qDebug("%s %p (%p): DESIGN", GB.GetClassName(THIS), THIS, WIDGET); + CWIDGET_set_flag(THIS, WF_DESIGN); + + CWidget::removeFocusPolicy(WIDGET); + set_mouse(WIDGET, CMOUSE_DEFAULT, 0); + //THIS->flag.fillBackground = true; +} + +static void set_design_recursive(QWidget *w, bool set = false) +{ + QObjectList children; + int i; + QObject *child; + CWIDGET *ob = CWidget::getReal(w); + + if (ob) + set_design_object(ob); + + children = w->children(); + + for (i = 0; i < children.count(); i++) + { + child = children.at(i); + + if (child->isWidgetType()) + set_design_recursive((QWidget *)child, true); + } +} + +static void set_design(CWIDGET *_object) +{ + CWIDGET *cont; + + if (GB.Is(THIS, CLASS_UserControl)) + set_design_recursive(WIDGET); + else if (!GB.Is(THIS, CLASS_Container)) + set_design_object(THIS); + + CWIDGET_set_flag(THIS, WF_DESIGN_LEADER); + + if (GB.Is(THIS, CLASS_Container)) + { + //qDebug("(%s %p - %p): LEADER / %p %p", GB.GetClassName(THIS), THIS, WIDGET, QCONTAINER(THIS), CWidget::getReal(QCONTAINER(THIS))); + + cont = CWidget::get(QCONTAINER(THIS)); + //debugObject(cont); + if (cont && cont != THIS) + set_design_object(cont); + } + + if (GB.Is(THIS, CLASS_TabStrip)) + { + THIS->flag.fillBackground = TRUE; + CWIDGET_reset_color(THIS); + } +} + +void CWIDGET_set_name(CWIDGET *_object, const char *name) +{ + CWINDOW *window; + MyMainWindow *win = 0; + + if (GB.Is(THIS, CLASS_Menu)) + { + if (qobject_cast(((CMENU *)THIS)->toplevel)) + win = (MyMainWindow *)((CMENU *)THIS)->toplevel; + } + else + { + window = CWidget::getWindow(THIS); + if (window) + win = (MyMainWindow *)QWIDGET(window); + + if (win) + { + if (name) + win->setName(name, THIS); + else + win->setName(THIS->name, 0); + } + } + + GB.FreeString(&THIS->name); + + if (name) + THIS->name = GB.NewZeroString(name); +} + +void *CWIDGET_get_parent(void *_object) +{ + QWidget *parent = WIDGET->parentWidget(); + + if (!parent || (GB.Is(THIS, CLASS_Window) && ((CWINDOW *)_object)->toplevel)) + return NULL; + else + return CWidget::get(parent); +} + +void *CWIDGET_get_parent_container(void *_object) +{ + void *parent = CWIDGET_get_parent(THIS); + + if (!parent) + return NULL; + + if (EXT(parent) && EXT(parent)->container_for) + parent = EXT(parent)->container_for; + + return parent; +} + +int CWIDGET_get_handle(void *_object) +{ + return (int)WIDGET->winId(); +} + +bool CWIDGET_is_visible(void *_object) +{ + return THIS->flag.visible; // || !QWIDGET(_object)->isHidden(); +} + +void CWIDGET_register_proxy(void *_object, void *proxy) +{ + void *check = proxy; + + while (check) + { + if (check == THIS) + { + GB.Error("Circular proxy chain"); + return; + } + check = EXT(check) ? EXT(check)->proxy : NULL; + } + + if (THIS_EXT && THIS_EXT->proxy && EXT(THIS_EXT->proxy)) + EXT(THIS_EXT->proxy)->proxy_for = NULL; + + if (proxy) + ENSURE_EXT(THIS)->proxy = proxy; + else if (EXT(THIS)) + EXT(THIS)->proxy = NULL; + + if (proxy) + ENSURE_EXT(proxy)->proxy_for = THIS; +} + +int CWIDGET_check(void *_object) +{ + return WIDGET == NULL || CWIDGET_test_flag(THIS, WF_DELETED); +} + +static QWidget *get_viewport(QWidget *w) +{ + if (qobject_cast(w)) + return ((QAbstractScrollArea *)w)->viewport(); + //else if (qobject_cast(w)) + // return ((Q3ScrollView *)w)->viewport(); + //else if (qobject_cast(w)) + // return ((Q3ListView *)w)->viewport(); + else + return 0; +} + +void CWIDGET_update_design(CWIDGET *_object) +{ + if (!CWIDGET_test_flag(THIS, WF_DESIGN) && !CWIDGET_test_flag(THIS, WF_DESIGN_LEADER)) + return; + + //qDebug("CWIDGET_update_design: %s %p", GB.GetClassName(THIS), THIS); + set_design(THIS); +} + +void CWIDGET_init_name(CWIDGET *_object) +{ + static int n = 0; + char *name = GB.GetLastEventName(); + + if (!name) + { + char buffer[16]; + n++; + sprintf(buffer, "#%d", n); + CWIDGET_set_name(THIS, buffer); + } + else + CWIDGET_set_name(THIS, name); +} + +bool CWIDGET_container_for(void *_object, void *container_for) +{ + if (THIS_EXT) + { + if (container_for) + { + if (!THIS_EXT->container_for) + { + THIS_EXT->container_for = container_for; + return false; + } + } + else + { + THIS_EXT->container_for = NULL; + return false; + } + } + else + { + if (container_for) + ENSURE_EXT(THIS)->container_for = container_for; + return false; + } + + return true; +} + +static void CWIDGET_enter(void *_object) +{ + CWIDGET *parent = (CWIDGET *)CWIDGET_get_parent(THIS); + + if (parent && !parent->flag.inside) + CWIDGET_enter(parent); + + if (!THIS->flag.inside) + { + //qDebug("CWIDGET_enter: %p %s", THIS, THIS->name); +#ifdef QT5 + _last_entered = THIS; +#endif + THIS->flag.inside = true; + GB.Raise(THIS, EVENT_Enter, 0); + } +} + +static void CWIDGET_leave(void *_object) +{ +#ifdef QT5 + if (_last_entered == THIS) + _last_entered = (CWIDGET *)CWIDGET_get_parent((void *)_last_entered); +#endif + if (THIS->flag.inside) + { + //qDebug("CWIDGET_leave: %p %s", THIS, THIS->name); + THIS->flag.inside = false; + GB.Raise(THIS, EVENT_Leave, 0); + } +} + +bool CWIDGET_get_allow_focus(void *_object) +{ + return WIDGET->focusPolicy() != Qt::NoFocus; +} + +void CWIDGET_set_allow_focus(void *_object, bool f) +{ + if (f) + { + WIDGET->setFocusPolicy(GB.CanRaise(THIS, EVENT_MouseWheel) ? Qt::WheelFocus : Qt::StrongFocus); + WIDGET->setAttribute(Qt::WA_InputMethodEnabled, true); + } + else + { + WIDGET->setFocusPolicy(Qt::NoFocus); + } +} + + +void CWIDGET_new(QWidget *w, void *_object, bool no_show, bool no_filter, bool no_init) +{ + //QAbstractScrollArea *sa; + + CWidget::add(w, _object, no_filter); + + //QWidget *p = w->parentWidget(); + //qDebug("CWIDGET_new: %s %p: %p in (%s %p)", GB.GetClassName(THIS), THIS, w, p ? GB.GetClassName(CWidget::get(p)) : "", CWidget::get(p)); + + THIS->widget = w; + THIS->level = MAIN_loop_level; + + if (!no_init) + CWIDGET_init_name(THIS); + + if (qobject_cast(w)) // || qobject_cast(w)) + CWIDGET_set_flag(THIS, WF_SCROLLVIEW); + + //w->setAttribute(Qt::WA_PaintOnScreen, true); + + CWIDGET_reset_color(THIS); //w->setPalette(QApplication::palette()); + + //THIS->flag.fillBackground = GB.Is(THIS, CLASS_Container); + //w->setAutoFillBackground(THIS->flag.fillBackground); + + if (!no_show) + { + w->setGeometry(-16, -16, 8, 8); + CWIDGET_set_visible(THIS, true); + w->raise(); + } + + CCONTAINER_insert_child(THIS); +} + + +QString CWIDGET_Utf8ToQString(GB_STRING *str) +{ + return QString::fromUtf8((const char *)(str->value.addr + str->value.start), str->value.len); +} + +#ifdef QT5 +void CWIDGET_leave_popup(void *) +{ + while (_last_entered) + CWIDGET_leave(_last_entered); +} +#else +void *CWIDGET_enter_popup() +{ + void *save = _enter_leave_set; + + _enter_leave_set = new QSet; + return save; +} + +void CWIDGET_leave_popup(void *save) +{ + CWIDGET *_object; + QSetIterator i(*_enter_leave_set); + + while (i.hasNext()) + { + _object = i.next(); + GB.Unref(POINTER(&_object)); + if (_object) + { + if (THIS->flag.inside_later != THIS->flag.inside) + { + if (THIS->flag.inside_later) + CWIDGET_enter(THIS); + else + CWIDGET_leave(THIS); + } + } + } + + delete _enter_leave_set; + _enter_leave_set = (QSet*) save; +} + +static void insert_enter_leave_event(CWIDGET *control, bool in) +{ + control->flag.inside_later = in; + + if (_enter_leave_set->contains(control)) + return; + + _enter_leave_set->insert(control); + GB.Ref(control); +} +#endif + +static bool _post_check_hovered = false; +static CWIDGET *_post_check_hovered_window = NULL; + +static void post_check_hovered(intptr_t) +{ + void *_object = _post_check_hovered_window; + + if (!THIS) + _object = CWINDOW_Active; + + if (THIS && WIDGET) + { + //qDebug("post_check_hovered"); + const QPoint globalPos(QCursor::pos()); + QPoint pos = WIDGET->mapFromGlobal(globalPos); + _hovered = CWidget::getRealExisting(WIDGET->childAt(pos)); + if (_hovered) + CWIDGET_enter(_hovered); + } + + _post_check_hovered = false; + _post_check_hovered_window = NULL; + +} + +void CWIDGET_check_hovered() +{ + _post_check_hovered_window = NULL; + post_check_hovered(0); +} + +void CWIDGET_destroy(CWIDGET *_object) +{ + if (!THIS || !WIDGET) + return; + + if (CWIDGET_test_flag(THIS, WF_DELETED)) + return; + + if (THIS->flag.dragging) + { + GB.Error("Control is being dragged"); + return; + } + +#ifdef QT5 + if (_last_entered == THIS) + _last_entered = NULL; +#endif + + //qDebug("CWIDGET_destroy: %s %p", GB.GetClassName(THIS), THIS); + + CWIDGET_set_visible(THIS, false); + CWIDGET_set_flag(THIS, WF_DELETED); + + WIDGET->deleteLater(); +} + + +//#if QT_VERSION >= 0x030005 +// #define COORD(_c) (WIDGET->pos()._c()) +//#else +#define COORD(_c) ((qobject_cast(WIDGET) && WIDGET->isWindow()) ? ((CWINDOW *)_object)->_c : WIDGET->pos()._c()) +//#define WIDGET_POS(_c) ((WIDGET->isWindow()) ? ((CWINDOW *)_object)->_c : WIDGET->pos()._c()) +//#define WIDGET_SIZE(_c) ((WIDGET->isA("MyMainWindow")) ? ((CWINDOW *)_object)->_c : WIDGET->pos()._c()) +//#endif + +#if 0 +static QWidget *get_widget(void *_object) +{ + QWidget *w = THIS->widget; + //if (w->isVisible() && CWIDGET_test_flag(THIS, WF_PARENT_GEOMETRY)) + // w = w->parentWidget(); + + if (WIDGET->isA("MyMainWindow")) + { + CWINDOW *win = (CWINDOW *)THIS; + if (win->toplevel && win->embedded) + { + QWidget *p = w->parentWidget(); + if (p && p->isA("QWorkspaceChild")) + w = p; + } + } + + return w; +} + +static QWidget *get_widget_resize(void *_object) +{ + QWidget *w = THIS->widget; + return w; +} +#endif + +#define get_widget(_object) QWIDGET(_object) +#define get_widget_resize(_object) QWIDGET(_object) + +static void arrange_parent(CWIDGET *_object) +{ + void *parent = CWIDGET_get_parent(THIS); + if (!parent) + return; + if (CWIDGET_check(parent)) + return; + CCONTAINER_arrange(parent); +} + +void CWIDGET_check_visibility(CWIDGET *_object) +{ + if (!THIS->flag.resized) + { + THIS->flag.resized = TRUE; + //qDebug("CWIDGET_check_visibility: %s %s %d", GB.GetClassName(THIS), THIS->name, THIS->flag.visible); + CWIDGET_set_visible(THIS, THIS->flag.visible); + } +} + +static void CWIDGET_after_geometry_change(void *_object, bool arrange) +{ + if (arrange) + { + if (GB.Is(THIS, CLASS_Container)) + CCONTAINER_arrange(THIS); + if (GB.Is(THIS, CLASS_DrawingArea)) + ((MyDrawingArea *)((CWIDGET *)_object)->widget)->updateBackground(); + } + + if (!THIS->flag.ignore) + arrange_parent(THIS); +} + +void CWIDGET_move(void *_object, int x, int y) +{ + QWidget *wid = get_widget(THIS); + + if (GB.Is(THIS, CLASS_Window)) + { + CWINDOW *win = (CWINDOW *)_object; + win->x = x; + win->y = y; + if (!win->moved && (x || y)) + win->moved = true; + } + + if (wid) + { + if (x == wid->x() && y == wid->y()) + return; + + wid->move(x, y); + } + + CWIDGET_after_geometry_change(THIS, false); +} + +/* +void CWIDGET_move_cached(void *_object, int x, int y) +{ + if (GB.Is(THIS, CLASS_Window)) + { + ((CWINDOW *)_object)->x = x; + ((CWINDOW *)_object)->y = y; + } + + CWIDGET_after_geometry_change(THIS, false); +} +*/ + +void CWIDGET_resize(void *_object, int w, int h) +{ + QWidget *wid = get_widget_resize(THIS); + bool window, toplevel; + bool resizable = true; + bool decide_w, decide_h; + + if (!wid) + return; + + window = GB.Is(THIS, CLASS_Window); + toplevel = wid->isTopLevel(); + + if (w < 0 && h < 0) + return; + + CWIDGET_check_visibility(THIS); + + CCONTAINER_decide(THIS, &decide_w, &decide_h); + + if (w < 0 || decide_w) + w = wid->width(); + + if (h < 0 || decide_h) + h = wid->height(); + + if (w == wid->width() && h == wid->height()) + return; + + if (window) + { + MyMainWindow *win = (MyMainWindow *)wid; + + if (toplevel) + { + resizable = win->isResizable(); + if (!resizable) + win->setResizable(true); + } + + wid->resize(qMax(0, w), qMax(0, h)); + + ((CWINDOW *)_object)->w = w; + ((CWINDOW *)_object)->h = h; + win->configure(); + + if (toplevel) + ((MyMainWindow *)wid)->setResizable(resizable); + } + else + wid->resize(qMax(0, w), qMax(0, h)); + + CWIDGET_after_geometry_change(THIS, true); +} + + +void CWIDGET_move_resize(void *_object, int x, int y, int w, int h) +{ + QWidget *wid = get_widget(THIS); + bool window, toplevel; + + if (wid) + { + if (w < 0) + w = wid->width(); + + if (h < 0) + h = wid->height(); + } + + window = GB.Is(THIS, CLASS_Window); + toplevel = wid->isTopLevel(); + + if (window) + { + CWINDOW *win = (CWINDOW *)_object; + win->x = x; + win->y = y; + win->w = w; + win->h = h; + + if (!win->moved && (x || y)) + win->moved = true; + } + + CWIDGET_check_visibility(THIS); + + if (wid) + { + if (w < 0) // || decide_w) + w = wid->width(); + + if (h < 0) // || decide_h) + h = wid->height(); + + if (x == wid->x() && y == wid->y() && w == wid->width() && h == wid->height()) + return; + + if (window) + { + MyMainWindow *win = (MyMainWindow *)wid; + bool resize = w != wid->width() || h != wid->height(); + bool resizable = true; + + if (x != wid->x() || y != wid->y()) + wid->move(x, y); + + if (resize) + { + if (toplevel) + { + resizable = win->isResizable(); + if (!resizable) + win->setResizable(true); + } + + wid->resize(qMax(0, w), qMax(0, h)); + + if (toplevel) + win->setResizable(resizable); + + win->configure(); + } + } + else + wid->setGeometry(x, y, qMax(0, w), qMax(0, h)); + } + + CWIDGET_after_geometry_change(THIS, true); +} + +#if 0 +void CWIDGET_move_resize(void *_object, int x, int y, int w, int h) +{ + QWidget *wid = get_widget(THIS); + + if (wid) + { +// if (wid->isA("QWorkspaceChild")) +// { +// CWIDGET_move(THIS, x, y); +// CWIDGET_resize(THIS, w, h); +// return; +// } + + if (w < 0) + w = wid->width(); + + if (h < 0) + h = wid->height(); + + if (x == wid->x() && y == wid->y() && w == wid->width() && h == wid->height()) + return; + wid->setGeometry(x, y, qMax(0, w), qMax(0, h)); + } + + if (GB.Is(THIS, CLASS_Window)) + { + ((CWINDOW *)_object)->x = x; + ((CWINDOW *)_object)->y = y; + ((CWINDOW *)_object)->w = w; + ((CWINDOW *)_object)->h = h; + //((CWINDOW *)_object)->container->resize(w, h); + } + + CWIDGET_after_geometry_change(THIS, true); +} +#endif + +/* +void CWIDGET_move_resize_cached(void *_object, int x, int y, int w, int h) +{ + if (GB.Is(THIS, CLASS_Window)) + { + ((CWINDOW *)_object)->x = x; + ((CWINDOW *)_object)->y = y; + ((CWINDOW *)_object)->w = w; + ((CWINDOW *)_object)->h = h; + } + + CWIDGET_after_geometry_change(THIS, true); +} +*/ + +#if 0 +void CWIDGET_check_hovered() +{ + //qDebug("CWIDGET_check_hovered: %p %s -> %p %s", _hovered, _hovered ? _hovered->name : 0, _official_hovered, _official_hovered ? _official_hovered->name : 0); + + if (_official_hovered != _hovered) + { + if (_official_hovered) + CWIDGET_leave(_official_hovered); + + if (_hovered) + CWIDGET_enter(_hovered); + + _official_hovered = _hovered; + } +} +#endif + +bool CWIDGET_is_design(CWIDGET *_object) +{ + return CWIDGET_test_flag(THIS, WF_DESIGN) || CWIDGET_test_flag(THIS, WF_DESIGN_LEADER); +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Control_new) + + MAIN_CHECK_INIT(); + +END_METHOD + +BEGIN_PROPERTY(Control_X) + + if (READ_PROPERTY) + GB.ReturnInteger(COORD(x)); + else + { + CWIDGET_move(_object, VPROP(GB_INTEGER), COORD(y)); + /*if (WIDGET->isWindow()) + qDebug("X: %d ==> X = %d", PROPERTY(int), WIDGET->x());*/ + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_ScreenX) + + GB.ReturnInteger(WIDGET->mapToGlobal(QPoint(0, 0)).x()); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Y) + + if (READ_PROPERTY) + GB.ReturnInteger(COORD(y)); + else + CWIDGET_move(_object, COORD(x), VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_ScreenY) + + GB.ReturnInteger(WIDGET->mapToGlobal(QPoint(0, 0)).y()); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Width) + + if (READ_PROPERTY) + GB.ReturnInteger(get_widget_resize(THIS)->width()); + else + CWIDGET_resize(_object, VPROP(GB_INTEGER), -1); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Height) + + if (READ_PROPERTY) + GB.ReturnInteger(get_widget_resize(THIS)->height()); + else + CWIDGET_resize(_object, -1, VPROP(GB_INTEGER)); + +END_PROPERTY + +void *CWIDGET_get_real_font(CWIDGET *_object) +{ + if (THIS->font) + return CFONT_create(*((CFONT *)THIS->font)->font); + + CWIDGET *parent = (CWIDGET *)CWIDGET_get_parent(THIS); + if (parent) + return CWIDGET_get_real_font(parent); + else + return CFONT_create(qApp->font()); +} + +BEGIN_PROPERTY(Control_Font) + + CFONT *font; + + if (!THIS->font) + { + THIS->font = CFONT_create(WIDGET->font(), 0, THIS); + GB.Ref(THIS->font); + } + + if (READ_PROPERTY) + { + *(((CFONT *)THIS->font)->font) = WIDGET->font(); + GB.ReturnObject(THIS->font); + } + else + { + font = (CFONT *)VPROP(GB_OBJECT); + + if (!font) + { + WIDGET->setFont(QFont()); + GB.Unref(POINTER(&THIS->font)); + THIS->font = NULL; + } + else + { + WIDGET->setFont(*(font->font)); + *(((CFONT *)THIS->font)->font) = WIDGET->font(); + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Design) + + if (READ_PROPERTY) + { + GB.ReturnBoolean(CWIDGET_is_design(THIS)); + return; + } + + if (VPROP(GB_BOOLEAN)) + { + set_design(THIS); + //CWIDGET_set_flag(THIS, WF_DESIGN); + } + else if (CWIDGET_test_flag(_object, WF_DESIGN) || CWIDGET_test_flag(_object, WF_DESIGN_LEADER)) + GB.Error("Design property cannot be reset"); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Enabled) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isEnabled()); + else + WIDGET->setEnabled(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_HasFocus) + + HANDLE_PROXY(_object); + + GB.ReturnBoolean(WIDGET->hasFocus()); + +END_PROPERTY + +BEGIN_PROPERTY(Control_Hovered) + + if (!CWIDGET_is_visible(THIS)) + GB.ReturnBoolean(false); + else + GB.ReturnBoolean(THIS->flag.inside); + +END_PROPERTY + +BEGIN_PROPERTY(Control_Expand) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->flag.expand); + else if (THIS->flag.expand != VPROP(GB_BOOLEAN)) + { + THIS->flag.expand = VPROP(GB_BOOLEAN); + CWIDGET_check_visibility(THIS); + arrange_parent(THIS); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Ignore) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->flag.ignore); + else if (THIS->flag.ignore != VPROP(GB_BOOLEAN)) + { + THIS->flag.ignore = VPROP(GB_BOOLEAN); + arrange_parent(THIS); + } + +END_PROPERTY + + +BEGIN_METHOD(Control_Move, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + CWIDGET_move_resize(_object, VARG(x), VARG(y), VARGOPT(w, -1), VARGOPT(h, -1)); + +END_METHOD + + +BEGIN_METHOD(Control_Resize, GB_INTEGER w; GB_INTEGER h) + + CWIDGET_resize(_object, VARG(w), VARG(h)); + +END_METHOD + + +BEGIN_METHOD(Control_MoveScaled, GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h) + + int x, y, w, h; + + x = (int)(VARG(x) * MAIN_scale + 0.5); + y = (int)(VARG(y) * MAIN_scale + 0.5); + w = (MISSING(w) ? -1 : (VARG(w) * MAIN_scale + 0.5)); + h = (MISSING(h) ? -1 : (VARG(h) * MAIN_scale + 0.5)); + + if (w == 0) w = 1; + if (h == 0) h = 1; + + CWIDGET_move_resize(_object, x, y, w, h); + +END_METHOD + + +BEGIN_METHOD(Control_ResizeScaled, GB_FLOAT w; GB_FLOAT h) + + int w, h; + + w = (int)(VARG(w) * MAIN_scale + 0.5); + h = (int)(VARG(h) * MAIN_scale + 0.5); + + if (w == 0) w = 1; + if (h == 0) h = 1; + + CWIDGET_resize(_object, w , h); + +END_METHOD + + +BEGIN_METHOD_VOID(Control_Delete) + + //if (WIDGET) + // qDebug("CWIDGET_delete: %p (%p)", THIS, WIDGET); + + CWIDGET_destroy(THIS); + +END_METHOD + + +void CWIDGET_set_visible(CWIDGET *_object, bool v) +{ + bool arrange = false; + + THIS->flag.visible = v; + + if (!THIS->flag.resized) + return; + + if (THIS->flag.visible) + { + arrange = !WIDGET->isVisible(); + QWIDGET(_object)->show(); + if (GB.Is(THIS, CLASS_Container)) + CCONTAINER_arrange(THIS); + } + else + { + arrange = !WIDGET->isHidden(); + QWIDGET(_object)->hide(); + } + + if (arrange) + arrange_parent(THIS); +} + + + +BEGIN_PROPERTY(Control_Visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(CWIDGET_is_visible(THIS)); + else + { + CWIDGET_set_visible(THIS, VPROP(GB_BOOLEAN)); + CWIDGET_check_visibility(THIS); + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(Control_Show) + + CWIDGET_set_visible(THIS, true); + CWIDGET_check_visibility(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(Control_Hide) + + CWIDGET_set_visible(THIS, false); + CWIDGET_check_visibility(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(Control_Raise) + + QWIDGET(_object)->raise(); + arrange_parent(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(Control_Lower) + + QWIDGET(_object)->lower(); + arrange_parent(THIS); + +END_METHOD + + +BEGIN_METHOD(Control_Move_under, GB_OBJECT control) + + CWIDGET *ob = (CWIDGET *)VARG(control); + + if (GB.CheckObject(ob)) + return; + + WIDGET->stackUnder(ob->widget); + +END_METHOD + + +static QWidget *get_next(QWidget *w) +{ + QWidget *parent; + QObjectList children; + int i; + QObject *current = NULL; + + parent = w->parentWidget(); + if (parent) + { + children = w->parentWidget()->children(); + i = children.indexOf(w) + 1; + if (i > 0 && i < children.count()) + current = children.at(i); + } + + return (QWidget *)current; +} + +BEGIN_PROPERTY(Control_Next) + + if (READ_PROPERTY) + { + QWidget *next = get_next(WIDGET); + + if (next) + GB.ReturnObject(CWidget::get(next)); + else + GB.ReturnNull(); + } + else + { + CWIDGET *ob = (CWIDGET *)VPROP(GB_OBJECT); + + if (!ob) + WIDGET->raise(); + else + { + if (GB.CheckObject(ob)) + return; + + WIDGET->stackUnder(ob->widget); + } + arrange_parent(THIS); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Previous) + + if (READ_PROPERTY) + { + QWidget *parent; + QObjectList children; + int i; + QObject *current = NULL; + + parent = WIDGET->parentWidget(); + if (parent) + { + children = WIDGET->parentWidget()->children(); + i = children.indexOf(WIDGET); + if (i > 0) + current = children.at(i - 1); + } + + if (current) + GB.ReturnObject(CWidget::get(current)); + else + GB.ReturnNull(); + } + else + { + CWIDGET *ob = (CWIDGET *)VPROP(GB_OBJECT); + QWidget *w; + + if (!ob) + { + WIDGET->lower(); + } + else + { + if (GB.CheckObject(ob)) + return; + + w = get_next(ob->widget); + if (w) + { + //w = get_next(w); + //if (w) + WIDGET->stackUnder(w); + } + } + arrange_parent(THIS); + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(Control_Refresh) //, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + QWIDGET(_object)->update(); + if (CWIDGET_test_flag(THIS, WF_SCROLLVIEW)) + get_viewport(WIDGET)->update(); + +END_METHOD + +static void set_focus(void *_object) +{ + CWINDOW *win; + + HANDLE_PROXY(_object); + + win = CWidget::getTopLevel(THIS); + + if (win->opened && QWIDGET(win)->isVisible()) + { + //qDebug("set focus on %s for %s", THIS->name, ((CWIDGET *)win)->name); + WIDGET->setFocus(); + } + else if ((CWIDGET *)win != THIS) + { + //qDebug("delayed focus on %s for %s", THIS->name, ((CWIDGET *)win)->name); + GB.Unref(POINTER(&win->focus)); + win->focus = THIS; + GB.Ref(THIS); + } +} + +BEGIN_METHOD_VOID(Control_SetFocus) + + set_focus(THIS); + +END_METHOD + + +BEGIN_PROPERTY(Control_Tag) + + if (READ_PROPERTY) + { + if (THIS_EXT) + GB.ReturnVariant(&THIS_EXT->tag); + else + { + GB.ReturnNull(); + GB.ReturnConvVariant(); + } + } + else + GB.StoreVariant(PROP(GB_VARIANT), POINTER(&(ENSURE_EXT(THIS)->tag))); + +END_METHOD + + +BEGIN_PROPERTY(Control_Mouse) + + QWidget *wid; + int shape; + + HANDLE_PROXY(_object); + + wid = QWIDGET(_object); + + if (READ_PROPERTY) + { + if (wid->testAttribute(Qt::WA_SetCursor)) + { + shape = wid->cursor().shape(); + if (shape == Qt::BitmapCursor) + GB.ReturnInteger(CMOUSE_CUSTOM); + else + GB.ReturnInteger(shape); + } + else + GB.ReturnInteger(CMOUSE_DEFAULT); + } + else + set_mouse(wid, VPROP(GB_INTEGER), THIS_EXT ? THIS_EXT->cursor : NULL); + +END_METHOD + + +BEGIN_PROPERTY(Control_Cursor) + + HANDLE_PROXY(_object); + + if (READ_PROPERTY) + GB.ReturnObject(THIS_EXT ? THIS_EXT->cursor : NULL); + else + { + GB.StoreObject(PROP(GB_OBJECT), &(ENSURE_EXT(THIS)->cursor)); + set_mouse(WIDGET, CMOUSE_CUSTOM, THIS_EXT->cursor); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_NoTabFocus) + + HANDLE_PROXY(_object); + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->flag.noTabFocus); + else + { + bool v = VPROP(GB_BOOLEAN); + Qt::FocusPolicy policy; + + + if (THIS->flag.noTabFocus == v) + return; + + THIS->flag.noTabFocus = v; + + if (v) + { + policy = WIDGET->focusPolicy(); + + ENSURE_EXT(THIS)->focusPolicy = (int)policy; + + switch (policy) + { + case Qt::TabFocus: policy = Qt::NoFocus; break; + case Qt::StrongFocus: policy = Qt::ClickFocus; break; + case Qt::WheelFocus: policy = Qt::ClickFocus; break; + default: break; + } + } + else + { + policy = (Qt::FocusPolicy)THIS_EXT->focusPolicy; + } + + WIDGET->setFocusPolicy(policy); + } + +END_PROPERTY + + +static QWidget *get_color_widget(CWIDGET *_object) +{ + QWidget *view = get_viewport(WIDGET); + if (view) + return view; + + return WIDGET; +} + +QT_COLOR_FUNC CWIDGET_after_set_color(QT_COLOR_FUNC func) +{ + QT_COLOR_FUNC old = _after_set_color; + _after_set_color = func; + return old; +} + + +void CWIDGET_reset_color(CWIDGET *_object) +{ + GB_COLOR fg, bg; + QPalette palette; + QWidget *w; + + HANDLE_PROXY(_object); + //qDebug("reset_color: %s", THIS->name); + //qDebug("set_color: (%s %p) bg = %08X (%d) fg = %08X (%d)", GB.GetClassName(THIS), THIS, THIS->bg, w->backgroundRole(), THIS->fg, w->foregroundRole()); + + w = get_color_widget(THIS); + + if (!THIS_EXT || (THIS_EXT->bg == COLOR_DEFAULT && THIS_EXT->fg == COLOR_DEFAULT)) + { + w->setPalette(QPalette()); + w->setAutoFillBackground(!THIS->flag.noBackground && THIS->flag.fillBackground); + } + else + { + bg = THIS_EXT->bg; + fg = THIS_EXT->fg; + + if (qobject_cast(w)) + { + //QComboBox *cb = (QComboBox *)w; + palette = QPalette(); + + if (bg != COLOR_DEFAULT) + { + palette.setColor(QPalette::Base, TO_QCOLOR(bg)); + palette.setColor(QPalette::Window, TO_QCOLOR(bg)); + palette.setColor(QPalette::Button, TO_QCOLOR(bg)); + //w->setAutoFillBackground(true); + } + else + w->setAutoFillBackground(false); + + + if (fg != COLOR_DEFAULT) + { + palette.setColor(QPalette::Text, TO_QCOLOR(fg)); + palette.setColor(QPalette::WindowText, TO_QCOLOR(fg)); + palette.setColor(QPalette::ButtonText, TO_QCOLOR(fg)); + } + + w->setPalette(palette); + } + else if (qobject_cast(w)) + { + palette = QPalette(); + + if (bg != COLOR_DEFAULT) + palette.setColor(QPalette::Base, TO_QCOLOR(bg)); + + if (fg != COLOR_DEFAULT) + palette.setColor(QPalette::Text, TO_QCOLOR(fg)); + + w->setPalette(palette); + } + else + { + palette = QPalette(); + + if (bg != COLOR_DEFAULT) + palette.setColor(w->backgroundRole(), TO_QCOLOR(bg)); + + if (fg != COLOR_DEFAULT) + palette.setColor(w->foregroundRole(), TO_QCOLOR(fg)); + + w->setAutoFillBackground(!THIS->flag.noBackground && (THIS->flag.fillBackground || ((THIS_EXT && THIS_EXT->bg != COLOR_DEFAULT) && w->backgroundRole() == QPalette::Window))); + //qDebug("%s: %d bg role = %d", THIS->name, w->autoFillBackground(), w->backgroundRole()); + w->setPalette(palette); + } + + } + + //w->setAutoFillBackground(THIS->bg != COLOR_DEFAULT); + + if (GB.Is(THIS, CLASS_TextArea)) + CTEXTAREA_set_foreground(THIS); + + if (_after_set_color) + (*_after_set_color)(THIS); + + if (!GB.Is(THIS, CLASS_Container)) + return; + + if (GB.Is(THIS, CLASS_Window)) + CWINDOW_define_mask((CWINDOW *)THIS); +} + + +void CWIDGET_set_color(CWIDGET *_object, int bg, int fg, bool handle_proxy) +{ + if (handle_proxy) { HANDLE_PROXY(_object); } + + ENSURE_EXT(THIS); + THIS_EXT->bg = bg; + THIS_EXT->fg = fg; + + CWIDGET_reset_color(THIS); +} + + +GB_COLOR CWIDGET_get_background(CWIDGET *_object, bool handle_proxy) +{ + if (handle_proxy) { HANDLE_PROXY(_object); } + + return THIS_EXT ? THIS_EXT->bg : COLOR_DEFAULT; +} + + +GB_COLOR CWIDGET_get_real_background(CWIDGET *_object) +{ + GB_COLOR bg = CWIDGET_get_background(THIS); + + if (bg != COLOR_DEFAULT) + return bg; + + CWIDGET *parent = (CWIDGET *)CWIDGET_get_parent(THIS); + + if (parent) + return CWIDGET_get_real_background(parent); + else + return QApplication::palette().color(QPalette::Window).rgb() & 0xFFFFFF; +} + + +GB_COLOR CWIDGET_get_foreground(CWIDGET *_object, bool handle_proxy) +{ + if (handle_proxy) { HANDLE_PROXY(_object); } + + return THIS_EXT ? THIS_EXT->fg : COLOR_DEFAULT; +} + + +GB_COLOR CWIDGET_get_real_foreground(CWIDGET *_object) +{ + GB_COLOR fg = CWIDGET_get_foreground(THIS); + + if (fg != COLOR_DEFAULT) + return fg; + + CWIDGET *parent = (CWIDGET *)CWIDGET_get_parent(THIS); + + if (parent) + return CWIDGET_get_real_foreground(parent); + else + return QApplication::palette().color(QPalette::WindowText).rgb() & 0xFFFFFF; +} + + +BEGIN_PROPERTY(Control_Background) + + if (THIS_EXT && THIS_EXT->proxy) + { + if (READ_PROPERTY) + GB.GetProperty(THIS_EXT->proxy, "Background"); + else + { + GB_VALUE value; + value.type = GB_T_INTEGER; + value._integer.value = VPROP(GB_INTEGER); + GB.SetProperty(THIS_EXT->proxy, "Background", &value); + } + + return; + } + + if (READ_PROPERTY) + GB.ReturnInteger(CWIDGET_get_background(THIS)); + else + { + GB_COLOR col = VPROP(GB_INTEGER); + if (col != CWIDGET_get_background(THIS)) + CWIDGET_set_color(THIS, col, CWIDGET_get_foreground(THIS)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Foreground) + + if (THIS_EXT && THIS_EXT->proxy) + { + if (READ_PROPERTY) + GB.GetProperty(THIS_EXT->proxy, "Foreground"); + else + { + GB_VALUE value; + value.type = GB_T_INTEGER; + value._integer.value = VPROP(GB_INTEGER); + GB.SetProperty(THIS_EXT->proxy, "Foreground", &value); + } + + return; + } + + if (READ_PROPERTY) + GB.ReturnInteger(CWIDGET_get_foreground(THIS)); + else + { + GB_COLOR col = VPROP(GB_INTEGER); + if (col != CWIDGET_get_foreground(THIS)) + CWIDGET_set_color(THIS, CWIDGET_get_background(THIS), col); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Parent) + + GB.ReturnObject(CWIDGET_get_parent_container(THIS)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control__Parent) + + GB.ReturnObject(CWIDGET_get_parent(THIS)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Window) + + GB.ReturnObject(CWidget::getWindow(THIS)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Id) + + GB.ReturnInteger((int)WIDGET->winId()); + +END_PROPERTY + + +/*static QString remove_ampersand(const QString &s) +{ + QString r; + uint i; + + for (i = 0; i < s.length(); i++) + { + if (s[i] == '&') + { + i++; + if (i < s.length()) + r += s[i]; + } + else + { + r += s[i]; + } + } + + return r; +}*/ + + +BEGIN_PROPERTY(Control_Tooltip) + + //QWidget *w; + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->toolTip()); + else + { + QString tip = QSTRING_PROP(); + if (THIS->flag.inside) + { + if (tip.isEmpty()) + QToolTip::hideText(); + else if (QToolTip::isVisible()) + { + QToolTip::hideText(); + QToolTip::showText(QCursor::pos(), tip, WIDGET); + } + } + WIDGET->setToolTip(tip); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Name) + + if (READ_PROPERTY) + GB.ReturnString(THIS->name); + else + CWIDGET_set_name(THIS, GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Action) + + char *current = THIS_EXT ? THIS_EXT->action : NULL; + + if (READ_PROPERTY) + GB.ReturnString(current); + else + { + char *action = PLENGTH() ? GB.NewString(PSTRING(), PLENGTH()) : NULL; + + CACTION_register(THIS, current, action); + + if (THIS_EXT) + GB.FreeString(&THIS_EXT->action); + + if (action) + ENSURE_EXT(THIS)->action = action; + } + +END_PROPERTY + + +BEGIN_PROPERTY(Control_Proxy) + + if (READ_PROPERTY) + GB.ReturnObject(THIS_EXT ? THIS_EXT->proxy : NULL); + else + CWIDGET_register_proxy(THIS, VPROP(GB_OBJECT)); + +END_PROPERTY + + +BEGIN_PROPERTY(Control_PopupMenu) + + if (READ_PROPERTY) + GB.ReturnString(THIS_EXT ? THIS_EXT->popup : NULL); + else + GB.StoreString(PROP(GB_STRING), &(ENSURE_EXT(THIS)->popup)); + +END_PROPERTY + + +/*BEGIN_METHOD_VOID(Control_Screenshot) + + GB.ReturnObject(CPICTURE_grab(QWIDGET(_object))); + +END_METHOD*/ + + +BEGIN_METHOD(Control_Drag, GB_VARIANT data; GB_STRING format) + + GB.ReturnObject(CDRAG_drag(OBJECT(CWIDGET), &VARG(data), MISSING(format) ? NULL : ARG(format))); + +END_METHOD + + +BEGIN_METHOD(Control_Reparent, GB_OBJECT container; GB_INTEGER x; GB_INTEGER y) + + QPoint p(WIDGET->pos()); + bool show; + + if (!MISSING(x) && !MISSING(y)) + { + p.setX(VARG(x)); + p.setY(VARG(y)); + } + + if (GB.CheckObject(VARG(container))) + return; + + show = CWIDGET_is_visible(THIS); + CWIDGET_set_visible(THIS, false); + WIDGET->setParent(QCONTAINER(VARG(container))); + WIDGET->move(p); + CCONTAINER_insert_child(THIS); + CWIDGET_set_visible(THIS, show); + +END_METHOD + + +BEGIN_PROPERTY(Control_Drop) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->flag.drop); + else + { + THIS->flag.drop = VPROP(GB_BOOLEAN); + if (CWIDGET_test_flag(THIS, WF_SCROLLVIEW)) + get_viewport(WIDGET)->setAcceptDrops(VPROP(GB_BOOLEAN)); + else + WIDGET->setAcceptDrops(VPROP(GB_BOOLEAN)); + } + +END_PROPERTY + +static bool has_tracking(CWIDGET *_object) +{ + HANDLE_PROXY(_object); + return THIS->flag.tracking; +} + +BEGIN_PROPERTY(Control_Tracking) + + HANDLE_PROXY(_object); + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->flag.tracking); + else + { + if (VPROP(GB_BOOLEAN) != THIS->flag.tracking) + { + THIS->flag.tracking = VPROP(GB_BOOLEAN); + if (THIS->flag.tracking) + { + THIS->flag.old_tracking = WIDGET->hasMouseTracking(); + WIDGET->setMouseTracking(true); + } + else + { + WIDGET->setMouseTracking(THIS->flag.old_tracking); + } + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_border_full) + + QFrame *wid = (QFrame *)QWIDGET(_object); + int border, lw; + + if (READ_PROPERTY) + { + if (wid->frameStyle() == (QFrame::Box + QFrame::Plain) && wid->lineWidth() == 1) + border = BORDER_PLAIN; + else if (wid->frameStyle() == (QFrame::StyledPanel + QFrame::Sunken)) + border = BORDER_SUNKEN; + else if (wid->frameStyle() == (QFrame::StyledPanel + QFrame::Raised)) + border = BORDER_RAISED; + else if (wid->frameStyle() == (QFrame::StyledPanel + QFrame::Plain)) + border = BORDER_ETCHED; + else + border = BORDER_NONE; + + GB.ReturnInteger(border); + } + else + { + switch (VPROP(GB_INTEGER)) + { + case BORDER_PLAIN: border = QFrame::Box + QFrame::Plain; lw = 1; break; + case BORDER_SUNKEN: border = QFrame::StyledPanel + QFrame::Sunken; lw = 2; break; + case BORDER_RAISED: border = QFrame::StyledPanel + QFrame::Raised; lw = 2; break; + case BORDER_ETCHED: border = QFrame::StyledPanel + QFrame::Plain; lw = 2; break; + default: border = QFrame::NoFrame; lw = 0; break; + } + + wid->setFrameStyle(border); + wid->setLineWidth(lw); + wid->update(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_border_simple) + + QFrame *wid = (QFrame *)QWIDGET(_object); + + if (READ_PROPERTY) + { + GB.ReturnBoolean(wid->frameStyle() != QFrame::NoFrame); + } + else + { + //qDebug("frameStyle = %d", wid->frameStyle()); + + if (VPROP(GB_BOOLEAN)) + { + wid->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + //wid->setFrameStyle(QFrame::LineEditPanel); + wid->setLineWidth(2); + } + else + { + wid->setFrameStyle(QFrame::NoFrame); + wid->setLineWidth(0); + } + + //qDebug("--> %s %d %d %d %d", THIS->name, wid->contentsRect().x(), wid->contentsRect().y(), wid->contentsRect().width(), wid->contentsRect().height()); + //wid->style()->polish(wid); + wid->update(); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CWIDGET_scrollbar) + + QAbstractScrollArea *wid = qobject_cast(WIDGET); + //Q3ScrollView *sw = qobject_cast(WIDGET); + int scroll; + + if (wid) + { + if (READ_PROPERTY) + { + scroll = 0; + if (wid->horizontalScrollBarPolicy() == Qt::ScrollBarAsNeeded) + scroll += 1; + if (wid->verticalScrollBarPolicy() == Qt::ScrollBarAsNeeded) + scroll += 2; + + GB.ReturnInteger(scroll); + } + else + { + scroll = VPROP(GB_INTEGER) & 3; + wid->setHorizontalScrollBarPolicy( (scroll & 1) ? Qt::ScrollBarAsNeeded : Qt::ScrollBarAlwaysOff); + wid->setVerticalScrollBarPolicy( (scroll & 2) ? Qt::ScrollBarAsNeeded : Qt::ScrollBarAlwaysOff); + } + } + /*else if (sw) + { + if (READ_PROPERTY) + { + scroll = 0; + if (sw->hScrollBarMode() == Q3ScrollView::Auto) + scroll += 1; + if (sw->vScrollBarMode() == Q3ScrollView::Auto) + scroll += 2; + + GB.ReturnInteger(scroll); + } + else + { + scroll = VPROP(GB_INTEGER) & 3; + sw->setHScrollBarMode( (scroll & 1) ? Q3ScrollView::Auto : Q3ScrollView::AlwaysOff); + sw->setVScrollBarMode( (scroll & 2) ? Q3ScrollView::Auto : Q3ScrollView::AlwaysOff); + } + }*/ + +END_PROPERTY + +void CWIDGET_grab(CWIDGET *_object) +{ + QEventLoop eventLoop; + QEventLoop *old; + + if (THIS->flag.grab) + return; + + THIS->flag.grab = true; + WIDGET->grabMouse(WIDGET->cursor()); + WIDGET->grabKeyboard(); + + old = MyApplication::eventLoop; + MyApplication::eventLoop = &eventLoop; + eventLoop.exec(); + MyApplication::eventLoop = old; + + WIDGET->releaseMouse(); + WIDGET->releaseKeyboard(); + THIS->flag.grab = false; + +} + +BEGIN_METHOD_VOID(Control_Grab) + + CWIDGET_grab(THIS); + +END_METHOD + + +/* Classe CWidget */ + +CWidget CWidget::manager; +QHash CWidget::dict; +bool CWidget::real; + +#if 0 +bool haveChildren; + +void CWidget::installFilter(QObject *o) +{ + QObjectList *children; + QObject *child; + + children = (QObjectList *)(o->children()); + + o->installEventFilter(&manager); + + if (!children) + return; + + child = children->first(); + while (child) + { + if (child->isWidgetType()) + { + haveChildren = true; + CWidget::installFilter(child); + } + + child = children->next(); + } +} + +void CWidget::removeFilter(QObject *o) +{ + QObjectList *children = (QObjectList *)(o->children()); + QObject *child; + + if (!o->isWidgetType()) + return; + + o->removeEventFilter(&manager); + + if (!children) + return; + + child = children->first(); + while (child) + { + CWidget::removeFilter(child); + child = children->next(); + } +} +#endif + +void CWidget::removeFocusPolicy(QWidget *w) +{ + QObjectList children; + int i; + QObject *child; + + w->clearFocus(); + w->setFocusPolicy(Qt::NoFocus); + + children = w->children(); + + for (i = 0; i < children.count(); i++) + { + child = children.at(i); + + if (child->isWidgetType()) + CWidget::removeFocusPolicy((QWidget *)child); + } +} + + +void CWidget::add(QObject *o, void *object, bool no_filter) +{ + //if (!no_filter) + QObject::connect(o, SIGNAL(destroyed()), &manager, SLOT(destroy())); + + dict.insert(o, (CWIDGET *)object); + + /* + if (!no_filter) + { + haveChildren = false; + CWidget::installFilter(o); + if (haveChildren) + CWIDGET_set_flag(object, WF_NO_EVENT); + } + */ + + GB.Ref(object); +} + +CWIDGET *CWidget::get(QObject *o) +{ + CWIDGET *ob; + + real = true; + + while (o) + { + ob = dict[o]; + if (ob) + return ob; + if (((QWidget *)o)->isWindow()) + return NULL; + + o = o->parent(); + real = false; + } + + return NULL; +} + +CWIDGET *CWidget::getRealExisting(QObject *o) +{ + CWIDGET *_object = dict[o]; + + if (THIS && CWIDGET_test_flag(THIS, WF_DELETED)) + _object = 0; + + return _object; +} + + +CWIDGET *CWidget::getDesign(QObject *o) +{ + CWIDGET *ob; + + if (!o->isWidgetType()) + return NULL; + + real = true; + + while (o) + { + ob = dict[o]; + if (ob) + break; + if (((QWidget *)o)->isWindow()) + return NULL; + + o = o->parent(); + real = false; + } + + if (!o) + return NULL; + + if (!CWIDGET_test_flag(ob, WF_DESIGN)) + return ob; + + while (o) + { + ob = dict[o]; + if (ob && CWIDGET_test_flag(ob, WF_DESIGN_LEADER)) + return ob; + if (((QWidget *)o)->isWindow()) + return NULL; + + o = o->parent(); + real = false; + } + + return NULL; +} + +/* +static void debugObject(void *ob) +{ + if (!ob) + return; + qDebug(" (%s %p) %s%s", ob ? GB.GetClassName(ob) : "", ob, CWIDGET_test_flag(ob, WF_DESIGN) ? "D" : "", CWIDGET_test_flag(ob, WF_DESIGN_LEADER) ? "L" : ""); +} +*/ + +#if 0 +static CWIDGET *getDesignDebug(QObject *o) +{ + CWIDGET *ob; + + if (!o->isWidgetType()) + return NULL; + + while (o) + { + ob = CWidget::getReal(o); + debugObject(ob); + if (ob) + break; + + o = o->parent(); + } + + if (!o) + return NULL; + + if (!CWIDGET_test_flag(ob, WF_DESIGN)) + return ob; + + while (o) + { + ob = CWidget::getReal(o); + debugObject(ob); + if (ob && CWIDGET_test_flag(ob, WF_DESIGN_LEADER)) + return ob; + + o = o->parent(); + } + + return NULL; +} +#endif + +QWidget *CWidget::getContainerWidget(CCONTAINER *object) +{ + if (GB.CheckObject(object)) + GB.Propagate(); + + if (object->container == NULL) + { + GB.Error("Null container"); + GB.Propagate(); + } + + //qDebug("Container = %p", object->container); + + return (object->container); +} + +CWINDOW *CWidget::getWindow(CWIDGET *ob) +{ + //QWidget *p = w->parentWidget(); + for(;;) + { + if (GB.Is(ob, CLASS_Window)) // && ((CWINDOW *)ob)->window) + break; + + ob = CWidget::get(QWIDGET(ob)->parentWidget()); + if (!ob) + break; + } + + return (CWINDOW *)ob; +} + + +CWINDOW *CWidget::getTopLevel(CWIDGET *ob) +{ + //QWidget *p = w->parentWidget(); + for(;;) + { + if (GB.Is(ob, CLASS_Window) && ((CWINDOW *)ob)->toplevel) + break; + + ob = CWidget::get(QWIDGET(ob)->parentWidget()); + if (!ob) + break; + } + + return (CWINDOW *)ob; +} + + +#if 0 +void CWidget::setName(CWIDGET *object, const char *name) +{ + QWidget *w = QWIDGET(object); + CTOPLEVEL *top = (CTOPLEVEL *)CWidget::get(w->topLevelWidget()); + + if (QWIDGET(top) == w) + return; + + if (w->name() != NULL) + { + /*qDebug("- %s", w->name());*/ + top->dict->remove(w->name()); + } + + if (name != NULL) + { + top->dict->insert((const char *)name, object); + w->setName(name); + /*qDebug("+ %s", w->name());*/ + } +} +#endif + +#define CLEAN_POINTER(_ptr) if ((_ptr) == THIS) _ptr = NULL + +void CWidget::destroy() +{ + QWidget *w = (QWidget *)sender(); + CWIDGET *_object = CWidget::get(w); + + if (!THIS) + return; + + //qDebug("CWidget::destroy: (%s %p) %s [%p]", GB.GetClassName(THIS), THIS, THIS->name, _hovered); + + if (!_post_check_hovered) + { + CWIDGET *top = (CWIDGET *)CWidget::getTopLevel(THIS); + if (top == THIS) + top = NULL; + _post_check_hovered = true; + _post_check_hovered_window = top; + GB.Post((void (*)())post_check_hovered, (intptr_t)0); + } + + CLEAN_POINTER(_hovered); + CLEAN_POINTER(_official_hovered); + CLEAN_POINTER(_post_check_hovered_window); + CLEAN_POINTER(CWIDGET_active_control); + CLEAN_POINTER(CWIDGET_previous_control); + CLEAN_POINTER(CWIDGET_hovered); + CLEAN_POINTER(_old_active_control); +#if QT5 + CLEAN_POINTER(_last_entered); +#endif + + if (THIS_EXT) + { + CACTION_register(THIS, THIS_EXT->action, NULL); + GB.FreeString(&THIS_EXT->action); + + if (THIS_EXT->proxy) + EXT(THIS_EXT->proxy)->proxy_for = NULL; + if (THIS_EXT->proxy_for) + EXT(THIS_EXT->proxy_for)->proxy = NULL; + + if (THIS_EXT->container_for) + { + ((CCONTAINER *)THIS_EXT->container_for)->container = ((CWIDGET *)THIS_EXT->container_for)->widget; + THIS_EXT->container_for = NULL; + } + + GB.Unref(POINTER(&THIS_EXT->cursor)); + GB.FreeString(&THIS_EXT->popup); + GB.StoreVariant(NULL, &THIS_EXT->tag); + GB.Free(POINTER(&THIS->ext)); + } + + CWIDGET_set_name(THIS, 0); + + dict.remove(w); + + QWIDGET(THIS) = NULL; + GB.Unref(POINTER(&THIS->font)); + + //qDebug(">> CWidget::destroy %p (%p) :%p:%ld #2", ob, ob->widget, ob->ob.klass, ob->ob.ref); + //if (!CWIDGET_test_flag(ob, WF_NODETACH)) + GB.Detach(THIS); + + GB.Unref(POINTER(&_object)); +} + +void CWidget::each(void (*func)(CWIDGET *)) +{ + QHashIterator i(dict); + while (i.hasNext()) + { + i.next(); + (*func)(i.value()); + } +} + +/*static void post_dblclick_event(void *control) +{ + GB.Raise(control, EVENT_DblClick, 0); + GB.Unref(&control); +}*/ + +static void post_focus_change(void *) +{ + CWIDGET *current, *control; + + if (!_focus_change) + return; + + //qDebug("post_focus_change"); + + for(;;) + { + current = CWIDGET_active_control; + if (current == _old_active_control) + break; + + control = _old_active_control; + while (control) + { + GB.Raise(control, EVENT_LostFocus, 0); + control = (CWIDGET *)(EXT(control) ? EXT(control)->proxy_for : NULL); + } + + _old_active_control = current; + CWINDOW_activate(current); + + control = current; + while (control) + { + GB.Raise(control, EVENT_GotFocus, 0); + control = (CWIDGET *)(EXT(control) ? EXT(control)->proxy_for : NULL); + } + } + + _focus_change = FALSE; +} + +static void handle_focus_change() +{ + if (_focus_change) + return; + + _focus_change = TRUE; + GB.Post((void (*)())post_focus_change, (intptr_t)NULL); +} + +void CWIDGET_finish_focus(void) +{ + post_focus_change(NULL); +} + +void CWIDGET_handle_focus(CWIDGET *control, bool on) +{ + if (on == (CWIDGET_active_control == control)) + return; + + //qDebug("CWIDGET_handle_focus: %s %d", control->name, on); + + if (CWIDGET_active_control && !_focus_change) + CWIDGET_previous_control = CWIDGET_active_control; + CWIDGET_active_control = on ? control : NULL; + handle_focus_change(); +} + +static bool raise_key_event_to_parent_window(void *control, int event) +{ + for(;;) + { + control = CWIDGET_get_parent(control); + if (!control) + break; + control = CWidget::getWindow((CWIDGET *)control); + if (GB.Raise(control, event, 0)) + return true; + } + + return false; +} + +bool CWidget::eventFilter(QObject *widget, QEvent *event) +{ + CWIDGET *control; + int event_id; + int type = event->type(); + bool real; + bool design; + bool original; + bool cancel; + QPoint p; + void *jump; + bool parent_got_it; + + CCONTROL_last_event_type = type; + + //if (widget->isA("MyMainWindow")) + // getDesignDebug(widget); + switch (type) + { + case QEvent::Enter: + jump = &&__ENTER; break; + case QEvent::Leave: + jump = &&__LEAVE; break; + case QEvent::FocusIn: + jump = &&__FOCUS_IN; break; + case QEvent::FocusOut: + jump = &&__FOCUS_OUT; break; + case QEvent::ContextMenu: + jump = &&__CONTEXT_MENU; break; + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + case QEvent::MouseButtonDblClick: + jump = &&__MOUSE; break; + //jump = &&__DBL_CLICK; break; + case QEvent::KeyPress: + case QEvent::KeyRelease: + jump = &&__KEY; break; + case QEvent::Shortcut: + jump = &&_DESIGN; break; + case QEvent::InputMethod: + jump = &&__INPUT_METHOD; break; + case QEvent::Wheel: + jump = &&__MOUSE_WHEEL; break; + case QEvent::DragEnter: + jump = &&__DRAG_ENTER; break; + case QEvent::DragMove: + jump = &&__DRAG_MOVE; break; + case QEvent::Drop: + jump = &&__DROP; break; + case QEvent::DragLeave: + jump = &&__DRAG_LEAVE; break; + case QEvent::DeferredDelete: + control = CWidget::getDesign(widget); + if (!control || CWIDGET_test_flag(control, WF_DELETED)) + { + QObject::eventFilter(widget, event); + return false; + } + else + goto _STANDARD; + case QEvent::TabletMove: + case QEvent::TabletPress: + case QEvent::TabletRelease: + jump = &&__TABLET; break; + default: + goto _STANDARD; + } + + control = CWidget::getDesign(widget); + //for(;;) + //{ + if (!control || GB.Is(control, CLASS_Menu)) + goto _STANDARD; + // if (control->widget->isEnabled()) + // break; + // control = (CWIDGET *)CWIDGET_get_parent(control); + //} + + real = CWidget::real; + design = CWIDGET_test_flag(control, WF_DESIGN); // && !GB.Is(control, CLASS_Container); + original = event->spontaneous(); + + goto *jump; + + __ENTER: + { +#ifndef QT5 + QWidget *popup = qApp->activePopupWidget(); +#endif + + //qDebug("enter %p %s real = %d inside = %d", widget, control->name, real, control->flag.inside); + + if (real) + { +#ifdef QT5 + CWIDGET_enter(control); +#else + if (!popup || CWidget::getReal(popup)) + CWIDGET_enter(control); + else if (_enter_leave_set) + insert_enter_leave_event(control, true); +#endif + } + + goto __NEXT; + } + + __LEAVE: + { +#ifndef QT5 + QWidget *popup = qApp->activePopupWidget(); +#endif + + //qDebug("leave %p %s real = %d inside = %d", widget, control->name, real, control->flag.inside); + + if (real) + { +#ifdef QT5 + CWIDGET_leave(control); +#else + if (!popup || CWidget::getReal(popup)) + CWIDGET_leave(control); + else if (_enter_leave_set) + insert_enter_leave_event(control, false); +#endif + } + + goto __NEXT; + } + + __FOCUS_IN: + { + CWIDGET_handle_focus(control, true); + goto __NEXT; + } + + __FOCUS_OUT: + { + CWIDGET_handle_focus(control, false); + goto __NEXT; + } + + __CONTEXT_MENU: + { + while (EXT(control) && EXT(control)->proxy_for) + control = (CWIDGET *)(EXT(control)->proxy_for); + + __MENU_TRY_PROXY: + + // if (real && GB.CanRaise(control, EVENT_Menu)) + //qDebug("Menu event! %p %d", control, EVENT_Menu); + if (GB.CanRaise(control, EVENT_Menu)) + { + int old = MENU_popup_count; + + ((QContextMenuEvent *)event)->accept(); + + if (GB.Raise(control, EVENT_Menu, 0) || MENU_popup_count != old) + return true; + //else + // goto __NEXT; + } + + if (EXT(control) && EXT(control)->popup) + { + CWINDOW *window = CWidget::getWindow(control); + CMENU *menu = CWindow::findMenu(window, EXT(control)->popup); + if (menu) + CMENU_popup(menu, QCursor::pos()); + return true; + } + + if (EXT(control) && EXT(control)->proxy) + { + control = (CWIDGET *)(EXT(control)->proxy); + goto __MENU_TRY_PROXY; + } + + goto __NEXT; + } + + __MOUSE: + { + QMouseEvent *mevent = (QMouseEvent *)event; + + if (!original) + goto _DESIGN; + + /*if (type == QEvent::MouseButtonPress) + { + qDebug("mouse event on [%s %s %p] (%s %p) %s%s%s", widget->metaObject()->className(), qPrintable(widget->objectName()), widget, + control ? GB.GetClassName(control) : "-", control, real ? "REAL " : "", design ? "DESIGN " : "", original ? "ORIGINAL ": ""); + //getDesignDebug(widget); + }*/ + + if (!real) + { + CWIDGET *cont = CWidget::get(widget); + if (CWIDGET_test_flag(cont, WF_SCROLLVIEW)) + { + if (qobject_cast(widget)) + goto _STANDARD; + /*if (widget != get_viewport(QWIDGET(cont))) + { + if (!widget->objectName().isNull()) + goto _STANDARD; + }*/ + } + } + + //while (control->proxy_for) + // control = (CWIDGET *)control->proxy_for; + + __MOUSE_TRY_PROXY: + + if (!design && !QWIDGET(control)->isEnabled()) + goto __NEXT; + + p.setX(mevent->globalX()); + p.setY(mevent->globalY()); + p = QWIDGET(control)->mapFromGlobal(p); + + if (type == QEvent::MouseButtonPress) + { + //qDebug("MouseDown on %p (%s %p) %s%s", widget, control ? GB.GetClassName(control) : "-", control, real ? "REAL " : "", design ? "DESIGN " : ""); + + event_id = EVENT_MouseDown; + //state = mevent->buttons(); + + MOUSE_info.sx = p.x(); + MOUSE_info.sy = p.y(); + //qDebug("MouseEvent: %d %d", mevent->x(), mevent->y()); + } + else if (type == QEvent::MouseButtonDblClick) + { + event_id = EVENT_DblClick; + } + else + { + event_id = (type == QEvent::MouseButtonRelease) ? EVENT_MouseUp : EVENT_MouseMove; + //state = mevent->buttons(); + } + + if (event_id == EVENT_MouseMove && mevent->buttons() == Qt::NoButton && !has_tracking(control)) + goto _DESIGN; + + + /* GB.Raise() can free the control, so we must reference it as we may raise two successive events now */ + GB.Ref(control); + cancel = false; + + if (GB.CanRaise(control, event_id) || (event_id == EVENT_DblClick && GB.CanRaise(control, EVENT_MouseDown))) + { + /*if (!design && CWIDGET_test_flag(control, WF_SCROLLVIEW)) + { + if (widget != ((QScrollView *)QWIDGET(control))->viewport() + && widget->name(0)) + { + qDebug("cancel"); + goto _DESIGN; + } + }*/ + + CMOUSE_clear(true); + MOUSE_info.x = p.x(); + MOUSE_info.y = p.y(); + MOUSE_info.screenX = mevent->globalX(); + MOUSE_info.screenY = mevent->globalY(); + MOUSE_info.button = mevent->button(); + MOUSE_info.state = mevent->buttons(); + MOUSE_info.modifier = mevent->modifiers(); + + //if (type == QEvent::MouseButtonPress) + // qDebug("GB.Raise on %p (%s %p) %d", widget, control ? GB.GetClassName(control) : "-", control, event_id); + + if (event_id == EVENT_DblClick) + cancel = GB.Raise(control, EVENT_MouseDown, 0); //, GB_T_INTEGER, p.x(), GB_T_INTEGER, p.y(), GB_T_INTEGER, state); + + if (!cancel) + cancel = GB.Raise(control, event_id, 0); //, GB_T_INTEGER, p.x(), GB_T_INTEGER, p.y(), GB_T_INTEGER, state); + + CMOUSE_clear(false); + + /*if (CDRAG_dragging) + return true;*/ + } + + if (event_id == EVENT_MouseMove && !cancel && (mevent->buttons() != Qt::NoButton) && GB.CanRaise(control, EVENT_MouseDrag) && !CDRAG_dragging + && ((abs(p.x() - MOUSE_info.sx) + abs(p.y() - MOUSE_info.sy)) > 8)) // QApplication::startDragDistance())) + { + /*if (!design && CWIDGET_test_flag(control, WF_SCROLLVIEW)) + { + if (widget != ((QScrollView *)QWIDGET(control))->viewport() + && widget->name(0)) + { + goto _DESIGN; + } + }*/ + + CMOUSE_clear(true); + MOUSE_info.x = p.x(); + MOUSE_info.y = p.y(); + MOUSE_info.screenX = mevent->globalX(); + MOUSE_info.screenY = mevent->globalY(); + MOUSE_info.button = mevent->button(); + MOUSE_info.state = mevent->buttons(); + MOUSE_info.modifier = mevent->modifiers(); + + //qDebug("MouseDrag: %s", control->name); + cancel = GB.Raise(control, EVENT_MouseDrag, 0); + + CMOUSE_clear(false); + } + + GB.Unref(POINTER(&control)); + + if (!control) + goto __MOUSE_RETURN_TRUE; + + if (control->flag.grab && event_id == EVENT_MouseUp) + MyApplication::eventLoop->exit(); + + if (cancel) + goto __MOUSE_RETURN_TRUE; + + if (EXT(control) && EXT(control)->proxy_for) + { + control = (CWIDGET *)(EXT(control)->proxy_for); + goto __MOUSE_TRY_PROXY; + } + + CMOUSE_reset_translate(); + goto __NEXT; + + __MOUSE_RETURN_TRUE: + + CMOUSE_reset_translate(); + return true; + } + + __TABLET: + { + QTabletEvent *tevent = (QTabletEvent *)event; + + if (!original) + goto _DESIGN; + + if (!real) + { + CWIDGET *cont = CWidget::get(widget); + if (CWIDGET_test_flag(cont, WF_SCROLLVIEW)) + { + if (qobject_cast(widget)) + goto _STANDARD; + /*if (widget != get_viewport(QWIDGET(cont))) + { + if (!widget->objectName().isNull()) + goto _STANDARD; + }*/ + } + } + + if (!control->flag.use_tablet) + goto __NEXT; + + __TABLET_TRY_PROXY: + + p.setX(tevent->globalX()); + p.setY(tevent->globalY()); + p = QWIDGET(control)->mapFromGlobal(p); + + if (type == QEvent::TabletPress) + { + //qDebug("MouseDown on %p (%s %p) %s%s", widget, control ? GB.GetClassName(control) : "-", control, real ? "REAL " : "", design ? "DESIGN " : ""); + + event_id = EVENT_MouseDown; + //state = mevent->buttons(); + + //MOUSE_info.sx = p.x(); + //MOUSE_info.sy = p.y(); + + control->flag.tablet_pressed = true; + //qDebug("MouseEvent: %d %d", mevent->x(), mevent->y()); + } + else if (type == QEvent::TabletMove) + { + //if (!control->flag.tracking && !control->flag.tablet_pressed) + // return false; + + event_id = EVENT_MouseMove; + } + else //if (type == QEvent::TabletRelease) + { + event_id = EVENT_MouseUp; + //state = mevent->buttons(); + } + + //if (event_id == EVENT_MouseMove && mevent->buttons() == Qt::NoButton && !QWIDGET(control)->hasMouseTracking()) + // goto _DESIGN; + + + cancel = false; + + if (GB.CanRaise(control, event_id)) + { + //MOUSE_info.x = p.x(); + //MOUSE_info.y = p.y(); + //POINTER_info.screenX = tevent->globalX(); + //POINTER_info.screenY = tevent->globalY(); + //MOUSE_info.modifier = tevent->modifiers(); + POINTER_info.tx = tevent->hiResGlobalX(); + POINTER_info.ty = tevent->hiResGlobalY(); + POINTER_info.pressure = tevent->pressure(); + POINTER_info.rotation = tevent->rotation(); + POINTER_info.xtilt = tevent->xTilt(); + POINTER_info.ytilt = tevent->yTilt(); + + switch(tevent->pointerType()) + { + case QTabletEvent::Pen: POINTER_info.type = POINTER_PEN; break; + case QTabletEvent::Eraser: POINTER_info.type = POINTER_ERASER; break; + case QTabletEvent::Cursor: POINTER_info.type = POINTER_CURSOR; break; + default: POINTER_info.type = POINTER_MOUSE; + } + + //cancel = GB.Raise(control, event_id, 0); + + //CMOUSE_clear(false); + } + + //if (control->flag.grab && event_id == EVENT_MouseUp) + // MyApplication::eventLoop->exit(); + + if (event_id == EVENT_MouseUp) + control->flag.tablet_pressed = false; + + //if (cancel) + // return true; + + if (EXT(control) && EXT(control)->proxy_for) + { + control = (CWIDGET *)(EXT(control)->proxy_for); + goto __TABLET_TRY_PROXY; + } + + CMOUSE_reset_translate(); + return false; // We fill the information, and then expect Qt to generate a Mouse event from the Tablet event + } + + __KEY: + { + QKeyEvent *kevent = (QKeyEvent *)event; + + if (MAIN_key_debug) + { +#ifdef QT5 + qDebug("gb.qt5" +#else + qDebug("gb.qt4" +#endif + ": %s: real = %d original = %d no_keyboard = %d", + (type == QEvent::KeyRelease ? "KeyRelease" : + (type == QEvent::KeyPress ? "KeyPress" : "?")), + real, original, control->flag.no_keyboard); + } + + #if QT_VERSION <= 0x030005 + if (!real || !original) + goto _DESIGN; + #endif + + if (control->flag.no_keyboard) + goto _DESIGN; + + event_id = (type == QEvent::KeyRelease) ? EVENT_KeyRelease : EVENT_KeyPress; + cancel = false; + parent_got_it = false; + + #if QT_VERSION > 0x030005 + if (!original && type != QEvent::InputMethod) + goto _DESIGN; //_ACCEL; + #endif + + if (type == QEvent::KeyRelease && kevent->isAutoRepeat()) + goto __NEXT; + + if (MAIN_key_debug) + { + qDebug(" " + "(%s %s) -> %d `%s' %s", + GB.GetClassName(control), control->name, + kevent->key(), (const char *)kevent->text().toLatin1(), kevent->isAutoRepeat() ? "AR" : "--"); + } + //qDebug("CWidget::eventFilter: KeyPress on %s %p", GB.GetClassName(control), control); + + __KEY_TRY_PROXY: + + CKEY_clear(true); + + GB.FreeString(&CKEY_info.text); + CKEY_info.text = NEW_STRING(kevent->text()); + CKEY_info.state = kevent->modifiers(); + CKEY_info.code = kevent->key(); + CKEY_info.release = type == QEvent::KeyRelease; + + #ifndef NO_X_WINDOW + if (type == QEvent::KeyPress && CKEY_info.code) + _x11_to_qt_keycode.insert(MAIN_x11_last_key_code, CKEY_info.code); + else if (type == QEvent::KeyRelease && CKEY_info.code == 0) + { + if (_x11_to_qt_keycode.contains(MAIN_x11_last_key_code)) + { + CKEY_info.code = _x11_to_qt_keycode[MAIN_x11_last_key_code]; + _x11_to_qt_keycode.remove(MAIN_x11_last_key_code); + } + } + #endif + + GB.Ref(control); + + if (!parent_got_it) + { + parent_got_it = true; + if (!cancel) + cancel = raise_key_event_to_parent_window(control, event_id); + } + + if (!cancel) + cancel = GB.Raise(control, event_id, 0); + + GB.Unref(POINTER(&control)); + + CKEY_clear(false); + + if ((cancel && (type != QEvent::KeyRelease)) || !control) + return true; + + if (EXT(control) && EXT(control)->proxy_for) + { + control = (CWIDGET *)(EXT(control)->proxy_for); + goto __KEY_TRY_PROXY; + } + + if (control->flag.grab && event_id == EVENT_KeyPress && kevent->key() == Qt::Key_Escape) + MyApplication::eventLoop->exit(); + + goto __NEXT; + } + + __INPUT_METHOD: + { + QInputMethodEvent *imevent = (QInputMethodEvent *)event; + + if (MAIN_key_debug) + { +#ifdef QT5 + qDebug("gb.qt5" +#else + qDebug("gb.qt4" +#endif + ": InputMethod: real = %d original = %d no_keyboard = %d", + real, original, control->flag.no_keyboard); + } + + #if QT_VERSION <= 0x030005 + if (!real || !original) + goto _DESIGN; + #endif + + if (!imevent->commitString().isEmpty()) + { + if (MAIN_key_debug) + { + qDebug(" " + "(%s %s) -> `%s'", + GB.GetClassName(control), control->name, + (const char *)imevent->commitString().toUtf8()); + } + + event_id = EVENT_KeyPress; + cancel = false; + + __IM_TRY_PROXY: + + if (GB.CanRaise(control, event_id)) + { + CKEY_clear(true); + + GB.FreeString(&CKEY_info.text); + //qDebug("IMEnd: %s", imevent->text().latin1()); + CKEY_info.text = NEW_STRING(imevent->commitString()); + CKEY_info.state = 0; + CKEY_info.code = 0; + + if (EXT(control) && EXT(control)->proxy_for) + cancel = GB.Raise(EXT(control)->proxy_for, event_id, 0); + if (!cancel) + cancel = GB.Raise(control, event_id, 0); + + CKEY_clear(false); + + if (cancel) + return true; + } + + if (EXT(control) && EXT(control)->proxy_for) + { + control = (CWIDGET *)(EXT(control)->proxy_for); + goto __IM_TRY_PROXY; + } + } + + goto __NEXT; + } + + __MOUSE_WHEEL: + { + QWheelEvent *ev = (QWheelEvent *)event; + + //qDebug("Event on %p %s%s%s", widget, + // real ? "REAL " : "", design ? "DESIGN " : "", child ? "CHILD " : ""); + + if (!original) + goto _DESIGN; + + __MOUSE_WHEEL_TRY_PROXY: + + if (design || QWIDGET(control)->isEnabled()) + { + if (GB.CanRaise(control, EVENT_MouseWheel)) + { + // Automatic focus for wheel events + set_focus(control); + + p.setX(ev->x()); + p.setY(ev->y()); + + p = ((QWidget *)widget)->mapTo(QWIDGET(control), p); + + CMOUSE_clear(true); + MOUSE_info.x = p.x(); + MOUSE_info.y = p.y(); + MOUSE_info.screenX = ev->globalX(); + MOUSE_info.screenY = ev->globalY(); + MOUSE_info.state = ev->buttons(); + MOUSE_info.modifier = ev->modifiers(); + MOUSE_info.orientation = ev->orientation(); + MOUSE_info.delta = ev->delta(); + + cancel = GB.Raise(control, EVENT_MouseWheel, 0); + + CMOUSE_clear(false); + + if (cancel) + { + event->accept(); + return true; + } + } + + if (EXT(control) && EXT(control)->proxy_for) + { + control = (CWIDGET *)(EXT(control)->proxy_for); + goto __MOUSE_WHEEL_TRY_PROXY; + } + } + + if (!control->flag.wheel) + { + control = (CWIDGET *)CWIDGET_get_parent(control); + if (control) + goto __MOUSE_WHEEL_TRY_PROXY; + } + + goto __NEXT; + } + + __DRAG_ENTER: + { + if (!control->flag.drop) + goto __NEXT; + + if (CDRAG_drag_enter((QWidget *)widget, control, (QDropEvent *)event)) + { + if (!((QDropEvent *)event)->isAccepted()) + CDRAG_hide_frame(control); + return true; + } + + goto __NEXT; + } + + __DRAG_MOVE: + { + if (!control->flag.drop) + goto __NEXT; + + for(;;) + { + if (GB.CanRaise(control, EVENT_DragMove)) + { + if (CDRAG_drag_move(QWIDGET(control), control, (QDropEvent *)event)) + { + if (!((QDropEvent *)event)->isAccepted()) + CDRAG_hide_frame(control); + break; + } + } + + if (EXT(control) && EXT(control)->proxy) + { + control = (CWIDGET *)(EXT(control)->proxy); + continue; + } + else + break; + } + + goto __NEXT; + } + + __DRAG_LEAVE: + { + if (!control->flag.drop) + goto __NEXT; + + CDRAG_drag_leave(control); + goto __NEXT; + } + + __DROP: + { + if (!control->flag.drop) + goto __NEXT; + + //if (!CWIDGET_test_flag(control, WF_NO_DRAG)) + CDRAG_drag_leave(control); + if (CDRAG_drag_drop((QWidget *)widget, control, (QDropEvent *)event)) + return true; + goto __NEXT; + } + + __NEXT: + + if (!control || CWIDGET_test_flag(control, WF_DELETED)) + { + QObject::eventFilter(widget, event); + return (type != QEvent::DeferredDelete); + } + + /*if (CWIDGET_check(control)) + { + qDebug("CWidget::eventFilter: %p was destroyed", control); + return true; + }*/ + +_DESIGN: + + if (design) + { + if ((type == QEvent::MouseButtonPress) + || (type == QEvent::MouseButtonRelease) + || (type == QEvent::MouseButtonDblClick) + || (type == QEvent::MouseMove) + || (type == QEvent::Wheel) + || (type == QEvent::ContextMenu) + || (type == QEvent::KeyPress) + || (type == QEvent::KeyRelease) + || (type == QEvent::InputMethod) + || (type == QEvent::Shortcut) + || (type == QEvent::Enter) + || (type == QEvent::Leave) + || (type == QEvent::FocusIn) + || (type == QEvent::FocusOut) + || (type == QEvent::DragEnter) + || (type == QEvent::DragMove) + || (type == QEvent::DragLeave) + || (type == QEvent::Drop) + || (type == QEvent::TabletMove) + || (type == QEvent::TabletPress) + || (type == QEvent::TabletRelease) + ) + return true; + } + +_STANDARD: + + return QObject::eventFilter(widget, event); // standard event processing +} + +/** Action *****************************************************************/ + +#define HAS_ACTION(_control) ((CWIDGET *)(_control))->flag.has_action +#define SET_ACTION(_control, _flag) (((CWIDGET *)(_control))->flag.has_action = (_flag)) + +#include "gb.form.action.h" + +#if 0 +static void gray_image(QImage &img) +{ + register uchar *b(img.bits()); + register uchar *g(img.bits() + 1); + register uchar *r(img.bits() + 2); + + uchar * end(img.bits() + img.numBytes()); + + while (b != end) { + + *b = *g = *r = 0x80 | (((*r + *b) >> 1) + *g) >> 2; // (r + b + g) / 3 + + b += 4; + g += 4; + r += 4; + } +} +#endif + +void CWIDGET_iconset(QIcon &icon, const QPixmap &pixmap, int size) +{ + QImage img; + //QPixmap disabled; + QPixmap normal; + + if (pixmap.isNull()) + return; + + if (size > 0) + { + img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied); + size = ((size + 1) & ~3); + img = img.scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); + normal = QPixmap::fromImage(img); + } + else + normal = pixmap; + + icon = QIcon(normal); + + /*gray_image(img); + + disabled.convertFromImage(img); + icon.setPixmap(disabled, QIcon::Small, QIcon::Disabled);*/ +} + + +GB_DESC CControlDesc[] = +{ + GB_DECLARE("Control", sizeof(CCONTROL)), GB_NOT_CREATABLE(), + + GB_HOOK_CHECK(CWIDGET_check), + + GB_METHOD("_new", NULL, Control_new, NULL), + GB_METHOD("_free", NULL, Control_Delete, NULL), + + GB_METHOD("Move", NULL, Control_Move, "(X)i(Y)i[(Width)i(Height)i]"), + GB_METHOD("Resize", NULL, Control_Resize, "(Width)i(Height)i"), + + GB_METHOD("MoveScaled", NULL, Control_MoveScaled, "(X)f(Y)f[(Width)f(Height)f]"), + GB_METHOD("ResizeScaled", NULL, Control_ResizeScaled, "(Width)f(Height)f"), + + GB_METHOD("Delete", NULL, Control_Delete, NULL), + GB_METHOD("Show", NULL, Control_Show, NULL), + GB_METHOD("Hide", NULL, Control_Hide, NULL), + + GB_METHOD("Raise", NULL, Control_Raise, NULL), + GB_METHOD("Lower", NULL, Control_Lower, NULL), + + GB_PROPERTY("Next", "Control", Control_Next), + GB_PROPERTY("Previous", "Control", Control_Previous), + + GB_METHOD("SetFocus", NULL, Control_SetFocus, NULL), + GB_METHOD("Refresh", NULL, Control_Refresh, NULL), + GB_METHOD("Drag", "Control", Control_Drag, "(Data)v[(Format)s]"), + GB_METHOD("Grab", NULL, Control_Grab, NULL), + + GB_METHOD("Reparent", NULL, Control_Reparent, "(Parent)Container;[(X)i(Y)i]"), + + GB_PROPERTY("X", "i", Control_X), + GB_PROPERTY("Y", "i", Control_Y), + GB_PROPERTY_READ("ScreenX", "i", Control_ScreenX), + GB_PROPERTY_READ("ScreenY", "i", Control_ScreenY), + GB_PROPERTY("W", "i", Control_Width), + GB_PROPERTY("H", "i", Control_Height), + GB_PROPERTY("Left", "i", Control_X), + GB_PROPERTY("Top", "i", Control_Y), + GB_PROPERTY("Width", "i", Control_Width), + GB_PROPERTY("Height", "i", Control_Height), + + GB_PROPERTY("Visible", "b", Control_Visible), + GB_PROPERTY("Enabled", "b", Control_Enabled), + GB_PROPERTY_READ("HasFocus", "b", Control_HasFocus), + GB_PROPERTY_READ("Hovered", "b", Control_Hovered), + + GB_PROPERTY("Expand", "b", Control_Expand), + GB_PROPERTY("Ignore", "b", Control_Ignore), + + GB_PROPERTY("Font", "Font", Control_Font), + GB_PROPERTY("Background", "i", Control_Background), + GB_PROPERTY("Foreground", "i", Control_Foreground), + + GB_PROPERTY("Design", "b", Control_Design), + GB_PROPERTY("Name", "s", Control_Name), + GB_PROPERTY("Tag", "v", Control_Tag), + GB_PROPERTY("Tracking", "b", Control_Tracking), + GB_PROPERTY("Mouse", "i", Control_Mouse), + GB_PROPERTY("Cursor", "Cursor", Control_Cursor), + GB_PROPERTY("Tooltip", "s", Control_Tooltip), + GB_PROPERTY("Drop", "b", Control_Drop), + GB_PROPERTY("Action", "s", Control_Action), + GB_PROPERTY("PopupMenu", "s", Control_PopupMenu), + GB_PROPERTY("Proxy", "Control", Control_Proxy), + GB_PROPERTY("NoTabFocus", "b", Control_NoTabFocus), + + GB_PROPERTY_READ("Parent", "Container", Control_Parent), + GB_PROPERTY_READ("_Parent", "Container", Control__Parent), + GB_PROPERTY_READ("Window", "Window", Control_Window), + GB_PROPERTY_READ("Id", "i", Control_Id), + GB_PROPERTY_READ("Handle", "i", Control_Id), + + GB_EVENT("Enter", NULL, NULL, &EVENT_Enter), + GB_EVENT("GotFocus", NULL, NULL, &EVENT_GotFocus), + GB_EVENT("LostFocus", NULL, NULL, &EVENT_LostFocus), + GB_EVENT("KeyPress", NULL, NULL, &EVENT_KeyPress), + GB_EVENT("KeyRelease", NULL, NULL, &EVENT_KeyRelease), + GB_EVENT("Leave", NULL, NULL, &EVENT_Leave), + GB_EVENT("MouseDown", NULL, NULL, &EVENT_MouseDown), + GB_EVENT("MouseMove", NULL, NULL, &EVENT_MouseMove), + GB_EVENT("MouseDrag", NULL, NULL, &EVENT_MouseDrag), + GB_EVENT("MouseUp", NULL, NULL, &EVENT_MouseUp), + GB_EVENT("MouseWheel", NULL, NULL, &EVENT_MouseWheel), + GB_EVENT("DblClick", NULL, NULL, &EVENT_DblClick), + GB_EVENT("Menu", NULL, NULL, &EVENT_Menu), + GB_EVENT("Drag", NULL, NULL, &EVENT_Drag), + GB_EVENT("DragMove", NULL, NULL, &EVENT_DragMove), + GB_EVENT("Drop", NULL, NULL, &EVENT_Drop), + GB_EVENT("DragLeave", NULL, NULL, &EVENT_DragLeave), + + CONTROL_DESCRIPTION, + + GB_END_DECLARE +}; + + + diff --git a/gb.qt4/src/CWidget.h b/gb.qt4/src/CWidget.h new file mode 100644 index 00000000..1a0f1d54 --- /dev/null +++ b/gb.qt4/src/CWidget.h @@ -0,0 +1,306 @@ +/*************************************************************************** + + CWidget.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWIDGET_H +#define __CWIDGET_H + +#include "main.h" +#include "share/gb.form.properties.h" + +#include +#include +#include +#include +#include +#include + +typedef + struct { + GB_COLOR fg; + GB_COLOR bg; + GB_VARIANT_VALUE tag; + void *cursor; + char *popup; + void *proxy; + void *proxy_for; + char *action; + int focusPolicy; + void *container_for; + } + CWIDGET_EXT; + +typedef + struct CWIDGET { + GB_BASE ob; + QWidget *widget; + void *ext; + struct { + unsigned char f; + unsigned expand : 1; + unsigned ignore : 1; + unsigned notified : 1; + unsigned visible : 1; + unsigned fillBackground : 1; + unsigned noBackground : 1; + unsigned shown : 1; // for containers + unsigned tracking : 1; + unsigned old_tracking : 1; + unsigned grab : 1; + unsigned dragging: 1; + unsigned noTabFocus : 1; + unsigned inside : 1; + unsigned inside_later : 1; + unsigned use_tablet : 1; + unsigned no_keyboard : 1; + unsigned tablet_pressed : 1; + unsigned has_action : 1; + unsigned drop : 1; + unsigned resized : 1; + unsigned wheel : 1; // eat wheel events + unsigned _reserved : 3; + } flag; + int level; + char *name; + void *font; + } + PACKED + CWIDGET; // BEWARE: gb.qt.h MUST be updated accordingly! + +typedef + CWIDGET CCONTROL; + +typedef + struct { + CWIDGET widget; + QWidget *container; + int32_t arrangement; + } + CCONTAINER; + +enum { + WF_DESIGN = (1 << 0), + WF_DESIGN_LEADER = (1 << 1), + WF_PERSISTENT = (1 << 2), + WF_CLOSED = (1 << 3), + WF_DELETED = (1 << 4), + WF_VISIBLE = (1 << 5), // Only for menus + WF_SCROLLVIEW = (1 << 6), // Inherits QScrollView + }; + + + +#ifndef __CWIDGET_CPP + +extern GB_DESC CControlDesc[]; +extern CWIDGET *CWIDGET_active_control; +extern CWIDGET *CWIDGET_previous_control; +extern CWIDGET *CWIDGET_hovered; + +extern int CCONTROL_last_event_type; + +#else + +#define THIS (((CWIDGET *)_object)) +#define THIS_EXT ((CWIDGET_EXT *)((CWIDGET *)_object)->ext) +#define WIDGET QWIDGET(_object) + +#endif + +#define QWIDGET(object) (((CWIDGET *)object)->widget) +#define QCONTAINER(_ob) CWidget::getContainerWidget((CCONTAINER *)_ob) + +DECLARE_METHOD(Control_Delete); +DECLARE_METHOD(Control_Refresh); +DECLARE_PROPERTY(Control_Tag); +DECLARE_PROPERTY(CWIDGET_border_full); +DECLARE_PROPERTY(CWIDGET_border_simple); +DECLARE_PROPERTY(CWIDGET_scrollbar); +DECLARE_PROPERTY(Control_Background); +DECLARE_PROPERTY(Control_Design); +DECLARE_PROPERTY(Control_Enabled); +DECLARE_PROPERTY(Control_Font); +DECLARE_PROPERTY(Control_Action); +DECLARE_PROPERTY(Control_Mouse); + +#define CWIDGET_set_flag(wid, _f) (((CWIDGET *)wid)->flag.f |= _f) +#define CWIDGET_clear_flag(wid, _f) (((CWIDGET *)wid)->flag.f &= ~_f) +#define CWIDGET_test_flag(wid, _f) ((((CWIDGET *)wid)->flag.f & _f) != 0) + +#define RAISE_EVENT(_event) \ +{ \ + GET_SENDER(); \ +\ + if (_object == NULL) \ + return; \ +\ + GB.Raise(_object, _event, 0); \ +} + +#define RAISE_EVENT_ACTION(_event) \ +{ \ + GET_SENDER(); \ +\ + if (_object == NULL) \ + return; \ +\ + GB.Ref(_object); \ + GB.Raise(_object, _event, 0); \ + CACTION_raise(_object); \ + GB.Unref(POINTER(&_object)); \ +} + + +/*#define ALIGN_MASK (Qt::AlignLeft | Qt::AlignRight | Qt::AlignTop | Qt::AlignBottom | Qt::AlignCenter) +#define ALIGN_HMASK (Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter) +#define ALIGN_VMASK (Qt::AlignTop | Qt::Bottom | Qt::AlignVCenter)*/ + +#define ALIGN_HMASK (Qt::AlignHorizontal_Mask) +#define ALIGN_VMASK (Qt::AlignVertical_Mask) +#define ALIGN_MASK (ALIGN_HMASK | ALIGN_VMASK) + +#define SET_WIDGET(_ob, _wid) (((CWIDGET *)_ob)->widget = (_wid)) +#define CLEAR_WIDGET(_ob) SET_WIDGET(_ob, 0) + +#define EMBED_WAIT 0 +#define EMBED_OK 1 +#define EMBED_ERROR 2 + +void CWIDGET_new(QWidget *w, void *_object, bool no_show = false, bool no_filter = false, bool no_tag = false); +void CWIDGET_init_name(CWIDGET *_object); +void CWIDGET_set_name(CWIDGET *_object, const char *name); +int CWIDGET_check(void *object); +QString CWIDGET_Utf8ToQString(GB_STRING *str); +void CWIDGET_destroy(CWIDGET *_object); +void CWIDGET_update_design(CWIDGET *_object); +void CWIDGET_iconset(QIcon &icon, const QPixmap &p, int size = 0); +void CWIDGET_set_color(CWIDGET *_object, int bg, int fg, bool handle_proxy = false); +void CWIDGET_reset_color(CWIDGET *_object); +QT_COLOR_FUNC CWIDGET_after_set_color(QT_COLOR_FUNC func); +GB_COLOR CWIDGET_get_background(CWIDGET *_object, bool handle_proxy = false); +GB_COLOR CWIDGET_get_foreground(CWIDGET *_object, bool handle_proxy = false); + +GB_COLOR CWIDGET_get_real_background(CWIDGET *_object); +GB_COLOR CWIDGET_get_real_foreground(CWIDGET *_object); +void *CWIDGET_get_real_font(CWIDGET *_object); + +void *CWIDGET_get_parent(void *_object); +int CWIDGET_get_handle(void *_object); +bool CWIDGET_is_visible(void *_object); +void CWIDGET_set_visible(CWIDGET *_object, bool v); +void CWIDGET_grab(CWIDGET *_object); +void CWIDGET_move(void *_object, int x, int y); +void CWIDGET_resize(void *_object, int w, int h); +void CWIDGET_move_resize(void *_object, int x, int y, int w, int h); +void CWIDGET_move_cached(void *_object, int x, int y); +void CWIDGET_resize_cached(void *_object, int w, int h); +void CWIDGET_move_resize_cached(void *_object, int x, int y, int w, int h); +void CWIDGET_handle_focus(CWIDGET *control, bool on); +void CWIDGET_finish_focus(void); +void CWIDGET_register_proxy(void *_object, void *proxy); +bool CWIDGET_container_for(void *_object, void *container_for); +#ifdef QT5 +#define CWIDGET_enter_popup() (0) +void CWIDGET_leave_popup(void *); +#else +void *CWIDGET_enter_popup(); +void CWIDGET_leave_popup(void *save); +#endif +void CACTION_register(void *control, const char *old, const char *key); +void CACTION_raise(void *control); +bool CWIDGET_get_allow_focus(void *_object); +void CWIDGET_set_allow_focus(void *_object, bool f); +bool CWIDGET_is_design(CWIDGET *_object); +void CWIDGET_check_visibility(CWIDGET *_object); +void CWIDGET_check_hovered(); + +#ifndef DO_NOT_DECLARE_EVENTS +#ifndef __CWIDGET_CPP +extern +#endif +int +EVENT_MouseDown, +EVENT_MouseUp, +EVENT_MouseMove, +EVENT_MouseDrag, +EVENT_MouseWheel, +EVENT_DblClick, +EVENT_KeyPress, +EVENT_KeyRelease, +EVENT_Enter, +EVENT_Leave, +EVENT_GotFocus, +EVENT_LostFocus, +EVENT_Menu, +EVENT_Drag, +EVENT_DragMove, +EVENT_Drop, +EVENT_DragLeave; +#endif + +struct CWINDOW; + +class CWidget : public QObject +{ + Q_OBJECT + +public: + + static CWidget manager; + + static void add(QObject *, void *, bool no_filter); + static CWIDGET *get(QObject *); + static CWIDGET *getReal(QObject *o) { return dict[o]; } + static CWIDGET *getRealExisting(QObject *); + static CWIDGET *getDesign(QObject *); + + static QWidget *getContainerWidget(CCONTAINER *object); + + static CWINDOW *getWindow(CWIDGET *object); + static CWINDOW *getTopLevel(CWIDGET *object); + + static void each(void (*func)(CWIDGET *)); + + //static void setName(CWIDGET *, const char *); + //static void installFilter(QObject *); + //static void removeFilter(QObject *); + + //static const char *getProperties(const void *klass); + //static void setProperties(const void *klass, const char *prop); + + static void removeFocusPolicy(QWidget *); + +public slots: + + void destroy(void); + +protected: + + bool eventFilter(QObject *, QEvent *); + +private: + + static bool real; + static QHash dict; +}; + +#endif diff --git a/gb.qt4/src/CWindow.cpp b/gb.qt4/src/CWindow.cpp new file mode 100644 index 00000000..c9759f00 --- /dev/null +++ b/gb.qt4/src/CWindow.cpp @@ -0,0 +1,2908 @@ +/*************************************************************************** + + CWindow.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWINDOW_CPP + +#include +#include +#include +#include +#include +#include +#include +#include + +//Added by qt3to4: +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + +#ifndef NO_X_WINDOW +#ifndef QT5 +#include +#include +#endif +#endif + +#include "gambas.h" + +#include "CWidget.h" +#include "CMenu.h" +#include "CKey.h" +#include "CDraw.h" +#include "CWindow.h" + +#ifndef NO_X_WINDOW +#include "x11.h" +#undef FontChange +#else +enum +{ + _NET_WM_WINDOW_TYPE_NORMAL, + _NET_WM_WINDOW_TYPE_DESKTOP, + _NET_WM_WINDOW_TYPE_DOCK, + _NET_WM_WINDOW_TYPE_TOOLBAR, + _NET_WM_WINDOW_TYPE_MENU, + _NET_WM_WINDOW_TYPE_UTILITY, + _NET_WM_WINDOW_TYPE_SPLASH, + _NET_WM_WINDOW_TYPE_DIALOG, + _NET_WM_WINDOW_TYPE_DROPDOWN_MENU, + _NET_WM_WINDOW_TYPE_POPUP_MENU, + _NET_WM_WINDOW_TYPE_TOOLTIP, + _NET_WM_WINDOW_TYPE_NOTIFICATION, + _NET_WM_WINDOW_TYPE_COMBO, + _NET_WM_WINDOW_TYPE_DND +}; +#endif + +//#define DEBUG_WINDOW 1 + +DECLARE_EVENT(EVENT_Open); +DECLARE_EVENT(EVENT_Close); +DECLARE_EVENT(EVENT_Activate); +DECLARE_EVENT(EVENT_Deactivate); +DECLARE_EVENT(EVENT_Move); +DECLARE_EVENT(EVENT_Resize); +DECLARE_EVENT(EVENT_Show); +DECLARE_EVENT(EVENT_Hide); +DECLARE_EVENT(EVENT_Title); +DECLARE_EVENT(EVENT_Icon); +DECLARE_EVENT(EVENT_Font); + +DECLARE_METHOD(Window_Show); + +CWINDOW *CWINDOW_Main = 0; +int CWINDOW_MainDesktop = -1; +CWINDOW *CWINDOW_Current = 0; +CWINDOW *CWINDOW_LastActive = 0; +CWINDOW *CWINDOW_Active = 0; + +#ifndef QT5 +int CWINDOW_Embedder = 0; +bool CWINDOW_Embedded = false; +static int CWINDOW_EmbedState = 0; +#endif + +// Fix a QT little boring visual bug on menubars +#if 0 +void CWINDOW_fix_menubar(CWINDOW *window) +{ + if (window && window->menuBar) + { + QWidget *save = qApp->focusWidget(); + window->menuBar->setFocus(); + QKeyEvent e(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier); + qApp->sendEvent(window->menuBar, &e); + if (save) + save->setFocus(); + } +} +#endif + +/*---- Utility routines --------------------------------------------------------------*/ + +static void clear_mask(CWINDOW *_object) +{ + WINDOW->clearMask(); + + if (THIS->toplevel) + { + #ifndef NO_X_WINDOW + bool v = !WINDOW->isHidden() && WINDOW->isVisible(); + //WINDOW->setBorder(WINDOW->hasBorder(), true); + //WINDOW->setResizable(WINDOW->isResizable(), true); + if (v && THIS->reallyMasked) + { + X11_window_remap(WINDOW->effectiveWinId()); + WINDOW->initProperties(PROP_ALL); + } + #endif + } +} + +void CWINDOW_define_mask(CWINDOW *_object) +{ + QPixmap background; + QColor c; + QPalette palette; + + //qDebug("define_mask: (%s %p) picture = %p masked = %d", GB.GetClassName(THIS), THIS, THIS->picture, THIS->masked); + + //if (THIS->embedded) + // return; + + if (THIS->picture) + background = *(THIS->picture->pixmap); + + if (background.isNull()) + { + clear_mask(THIS); + THIS->reallyMasked = false; + THIS->container->setPixmap(0); + //THIS->container->setPalette(WINDOW->palette()); + } + else + { + if (THIS->masked && background.hasAlpha()) + { + THIS->reallyMasked = true; + WINDOW->setBetterMask(background); + } + else + { + clear_mask(THIS); + THIS->reallyMasked = false; + } + + THIS->container->setPixmap(THIS->picture->pixmap); + } + + THIS->container->update(); +} + +static bool emit_open_event(void *_object) +{ + //if (THIS->opening) + // return true; + + if (THIS->opened) + return false; + + CWIDGET_clear_flag(THIS, WF_CLOSED); + THIS->opened = true; + + if (!THIS->minw && !THIS->minh) + { + THIS->minw = THIS->w; + THIS->minh = THIS->h; + } + #if DEBUG_WINDOW + qDebug("emit_open_event: %s %p", GB.GetClassName(THIS), THIS); + #endif + //THIS->opening = true; + //WINDOW->configure(); + GB.Raise(THIS, EVENT_Open, 0); + //THIS->opening = false; + if (CWIDGET_test_flag(THIS, WF_CLOSED)) + { + #if DEBUG_WINDOW + qDebug("emit_open_event: %s %p [CANCELED]", GB.GetClassName(THIS), THIS); + #endif + THIS->opened = false; + return true; + } + + THIS->opened = true; + THIS->hidden = false; + return false; +} + +static void handle_focus(CWINDOW *_object) +{ + if (THIS->focus) + { + //qDebug("handle_focus on %s", THIS->focus->name); + THIS->focus->widget->setFocus(); + GB.Unref(POINTER(&THIS->focus)); + THIS->focus = NULL; + } +} + +static void raise_resize_event(void *_object) +{ + if (WINDOW->width() != THIS->last_resize_w || WINDOW->height() != THIS->last_resize_h) + { + THIS->last_resize_w = WINDOW->width(); + THIS->last_resize_h = WINDOW->height(); + GB.Raise(THIS, EVENT_Resize, 0); + } +} + +static void post_show_event(void *_object) +{ + GB.Raise(THIS, EVENT_Move, 0); + raise_resize_event(THIS); + handle_focus(THIS); +} + +static void reparent_window(CWINDOW *_object, void *parent, bool move, int x = 0, int y = 0) +{ + QPoint p; + QWidget *newParentWidget; + bool moved = THIS->moved; + + if (move) + { + p.setX(x); + p.setY(y); + moved = true; + } + else if (THIS->toplevel) + { + p.setX(THIS->x); + p.setY(THIS->y); + } + else + p = WIDGET->pos(); + + if (!parent) + newParentWidget = 0; + else + { + if (GB.CheckObject(parent)) + return; + newParentWidget = QCONTAINER(parent); + } + + if (newParentWidget != WINDOW->parentWidget()) + { + //qDebug("reparent_window: -> %s %p", parent ? ((CWIDGET *)parent)->name : "", parent); + WINDOW->doReparent(newParentWidget, p); + } + else + CWIDGET_move(THIS, p.x(), p.y()); + + THIS->moved = moved; +} + +void CWINDOW_ensure_active_window() +{ + void *_object = CWINDOW_Active; + + if (THIS) + WINDOW->activateWindow(); +} + + +//-- Window --------------------------------------------------------------- + +static void show_later(CWINDOW *_object) +{ + /* If the user has explicitely hidden the window since the posting of this routines + then do nothing + */ + + //qDebug("show_later %s %p: hidden = %d", GB.GetClassName(THIS), THIS, THIS->hidden); + if (!THIS->hidden && WIDGET) + { + if (!emit_open_event(THIS)) + CWIDGET_set_visible((CWIDGET *)THIS, true); + } + GB.Unref(POINTER(&_object)); +} + +BEGIN_METHOD(Window_new, GB_OBJECT parent) + + MyMainWindow *win = 0; + MyContainer *container; +#ifndef NO_X_WINDOW +#ifndef QT5 + QX11EmbedWidget *client = 0; +#endif +#endif + const char *name = GB.GetClassName(THIS); + + //THIS->widget.flag.fillBackground = true; + + if (MISSING(parent) || !VARG(parent)) + { +#ifndef NO_X_WINDOW +#ifndef QT5 + if (CWINDOW_Embedder && !CWINDOW_Embedded) + { + client = new QX11EmbedWidget; + + win = new MyMainWindow(client, name, true); + container = new MyContainer(win); + container->raise(); + THIS->embedded = true; + THIS->toplevel = false; + THIS->xembed = true; + } + else +#endif +#endif + { + //win = new MyMainWindow(CWINDOW_Main ? (MyMainWindow *)QWIDGET(CWINDOW_Main) : 0, name); + win = new MyMainWindow(0, name); + container = new MyContainer(win); + container->raise(); + THIS->embedded = false; + THIS->toplevel = true; + THIS->xembed = false; + } + } + else + { + if (GB.Conv((GB_VALUE *)(void *)ARG(parent), (GB_TYPE)CLASS_Container)) + return; + + //frame = new MyEmbeddedWindow(QCONTAINER(VARG(parent))); + //frame->setName(name); + //container = frame; + //THIS->embedded = true; + //THIS->toplevel = false; + //container->installEventFilter(&CWindow::manager); + //CWIDGET_new(frame, (void *)_object, NULL); + + win = new MyMainWindow(QCONTAINER(VARG(parent)), name, true); + container = new MyContainer(win); + container->raise(); + THIS->embedded = true; + THIS->toplevel = false; + THIS->xembed = false; + } + /*else + { + GB.Error("The parent of a Window must be a Container or a Workspace"); + return; + }*/ + + THIS->container = container; + CWIDGET_new(win, (CWIDGET *)THIS, true); + THIS->widget.flag.resized = TRUE; + + if (win) + { + win->_object = THIS; + win->installEventFilter(&CWindow::manager); + } + + if (THIS->toplevel || THIS->xembed) + { + CWindow::insertTopLevel(THIS); + + /*if (CWINDOW_Main == 0) + { + #if DEBUG_WINDOW + qDebug("CWINDOW_Main -> %p", THIS); + #endif + CWINDOW_Main = THIS; + }*/ + } + +#ifndef NO_X_WINDOW +#ifndef QT5 + if (THIS->xembed) + { + CWINDOW_Embedded = true; + + QObject::connect(XEMBED, SIGNAL(embedded()), &CWindow::manager, SLOT(embedded())); + QObject::connect(XEMBED, SIGNAL(containerClosed()), &CWindow::manager, SLOT(closed())); + QObject::connect(XEMBED, SIGNAL(error(int)), &CWindow::manager, SLOT(error())); + + //qDebug("XEMBED: EmbedInto %ld", CWINDOW_Embedder); + XEMBED->embedInto(CWINDOW_Embedder); + //qDebug("XEMBED: show"); + //XEMBED->show(); + //define_mask(THIS); + + for(;;) + { + MAIN_process_events(); + if (CWINDOW_EmbedState) + break; + usleep(10000); + } + + //qDebug("XEMBED: EmbedState: %d", CWINDOW_EmbedState); + + if (CWINDOW_EmbedState == EMBED_ERROR) + { + CWINDOW_Embedded = false; + CWINDOW_Embedder = 0; + GB.Error("Embedding has failed"); + } + } +#endif +#endif + + #if 1 + if (THIS->embedded && !THIS->xembed) + { + /* ### This can call post_show_event() directly, whereas the function is not terminated */ + #if DEBUG_WINDOW + qDebug("post show_later %s %p", GB.GetClassName(THIS), THIS); + #endif + GB.Ref(THIS); + //show_later(THIS); + GB.Post((void (*)())show_later, (intptr_t)THIS); + //WIDGET->show(); + } + //else + // THIS->hidden = TRUE; + #endif + + THIS->showMenuBar = true; + +END_METHOD + + +BEGIN_METHOD_VOID(CFORM_new) + + if (!GB.Parent(_object)) + GB.Attach(_object, _object, "Form"); + + CWIDGET_set_name((CWIDGET *)THIS, GB.GetClassName((void *)THIS)); + +END_METHOD + + +BEGIN_METHOD_VOID(CFORM_main) + + CWINDOW *form = (CWINDOW *)GB.AutoCreate(GB.GetClass(NULL), 0); + + if (!form->hidden) + Window_Show(form, NULL); + +END_METHOD + + +BEGIN_METHOD(CFORM_load, GB_OBJECT parent) + + //qDebug("CFORM_load"); + reparent_window((CWINDOW *)GB.AutoCreate(GB.GetClass(NULL), 0), VARGOPT(parent, 0), false); + +END_METHOD + + +BEGIN_METHOD_VOID(Window_free) + + //qDebug("Window_free"); + + GB.StoreObject(NULL, POINTER(&(THIS->icon))); + GB.StoreObject(NULL, POINTER(&(THIS->picture))); + GB.Unref(POINTER(&THIS->focus)); + +END_METHOD + + +BEGIN_METHOD_VOID(CWINDOW_next) + + int index = ENUM(int); + + if (index >= CWindow::list.count()) + { + GB.StopEnum(); + return; + } + + GB.ReturnObject(CWindow::list.at(index)); + ENUM(int) = index + 1; + +END_METHOD + + +BEGIN_PROPERTY(CWINDOW_count) + + GB.ReturnInteger(CWindow::list.count()); + +END_PROPERTY + + +BEGIN_METHOD(CWINDOW_get_from_id, GB_INTEGER id) + + QWidget *wid = QWidget::find(VARG(id)); + + //qDebug("id = %d wid = %p", PARAM(id), wid); + + if (wid != 0 && wid->isWindow()) + { + //qDebug("-> %p", CWidget::getReal(wid)); + GB.ReturnObject(CWidget::getReal(wid)); + } + else + { + //qDebug("-> %p", 0); + GB.ReturnNull(); + } + +END_METHOD + + +static bool do_close(CWINDOW *_object, int ret, bool destroyed = false) +{ + bool closed; + + #if DEBUG_WINDOW + qDebug("do_close: (%s %p) %d %d", GB.GetClassName(THIS), THIS, THIS->closing, CWIDGET_test_flag(THIS, WF_CLOSED)); + #endif + + if (THIS->closing || CWIDGET_test_flag(THIS, WF_CLOSED)) // || WIDGET->isHidden()) + return false; + + if (!THIS->toplevel) + { + //qDebug("Close event: %s %p opened = %d", GB.GetClassName(THIS), THIS, THIS->opened); + if (THIS->opened) + { + THIS->closing = true; + closed = !GB.Raise(THIS, EVENT_Close, 0); + THIS->closing = false; + } + else + closed = true; + + if (destroyed || closed) + { + CWIDGET_set_flag(THIS, WF_CLOSED); + THIS->opened = false; + } + + if (closed) + { + WIDGET->hide(); + if (!CWIDGET_test_flag(_object, WF_PERSISTENT)) + CWIDGET_destroy((CWIDGET *)THIS); + } + } + else + { + if (!THIS->opened) + { + #if DEBUG_WINDOW + qDebug("send close event"); + #endif + QCloseEvent e; + QApplication::sendEvent(WINDOW, &e); + closed = e.isAccepted(); + } + else + { + #if DEBUG_WINDOW + qDebug("call WINDOW->close()"); + #endif + closed = WINDOW->close(); + } + #if DEBUG_WINDOW + qDebug("--> closed = %d", closed); + #endif + } + + #if 0 + if (closed || destroyed) + { + if (CWINDOW_Active == THIS) + CWINDOW_activate(CWidget::get(WIDGET->parentWidget())); + if (CWINDOW_LastActive == THIS) + { + //GB.Unref(POINTER(&CWINDOW_LastActive)); + CWINDOW_LastActive = 0; + //qDebug("CWINDOW_LastActive = 0"); + } + THIS->opened = FALSE; + } + #endif + + if (closed) + THIS->ret = ret; + + return (!closed); +} + +BEGIN_METHOD(Window_Close, GB_INTEGER ret) + + int ret = VARGOPT(ret, 0); + + GB.ReturnBoolean(do_close(THIS, ret)); + +END_METHOD + + +BEGIN_METHOD_VOID(Window_Raise) + + if (!THIS->toplevel) + { + if (!WIDGET->isVisible()) + CWIDGET_set_visible((CWIDGET *)THIS, true); + WIDGET->raise(); + } + else + { + if (!WINDOW->isVisible()) + WINDOW->showActivate(); + else + WINDOW->raise(); + } + +END_METHOD + +static bool check_opened(CWINDOW *_object, bool modal) +{ + if (THIS->toplevel && THIS->opened) + { + if (modal || WINDOW->isModal()) + { + GB.Error("Window is already opened"); + return TRUE; + } + } + + return FALSE; +} + +BEGIN_METHOD_VOID(Window_Show) + + if (check_opened(THIS, FALSE)) + return; + + if (emit_open_event(THIS)) + return; + + if (!THIS->toplevel) + { + CWIDGET_set_visible((CWIDGET *)THIS, true); + CWIDGET_check_visibility((CWIDGET *)THIS); +#ifndef NO_X_WINDOW +#ifndef QT5 + if (THIS->xembed) + XEMBED->show(); +#endif +#endif + post_show_event(THIS); + } + else + { + WINDOW->showActivate(); + } + +END_METHOD + + +BEGIN_METHOD_VOID(Window_Hide) + + THIS->hidden = true; + + if (THIS->toplevel && WINDOW->isModal()) + { + do_close(THIS, 0); + //THIS->widget.flag.visible = false; + } + else + CWIDGET_set_visible((CWIDGET *)THIS, false); + +END_METHOD + + +BEGIN_METHOD_VOID(Window_ShowModal) + + if (check_opened(THIS, TRUE)) + return; + + THIS->ret = 0; + + if (!emit_open_event(THIS)) + { + if (THIS->toplevel) + { + //THIS->widget.flag.visible = true; + WINDOW->showModal(); + //THIS->widget.flag.visible = false; + } + } + + GB.ReturnInteger(THIS->ret); + +END_METHOD + + +BEGIN_METHOD(Window_ShowPopup, GB_INTEGER x; GB_INTEGER y) + + QPoint pos; + + if (check_opened(THIS, TRUE)) + return; + + if (MISSING(x) || MISSING(y)) + pos = QCursor::pos(); + else + pos = QPoint(VARG(x), VARG(y)); + + THIS->ret = 0; + + if (THIS->toplevel) + { + if (!emit_open_event(THIS)) + WINDOW->showPopup(pos); + } + + GB.ReturnInteger(THIS->ret); + +END_METHOD + + +BEGIN_PROPERTY(Window_Modal) + + if (THIS->toplevel) + GB.ReturnBoolean(WINDOW->isModal()); + else + GB.ReturnBoolean(false); + +END_PROPERTY + + +BEGIN_PROPERTY(Window_TopLevel) + + GB.ReturnBoolean(THIS->toplevel); + +END_PROPERTY + +/*BEGIN_METHOD_VOID(CWINDOW_dialog) + + CWINDOW *win; + + GB.New(POINTER(&win), GB.GetClass(NULL), NULL, NULL); + + win->ret = 0; + ((MyMainWindow *)win->widget.widget)->showModal(); + GB.ReturnInteger(win->ret); + +END_METHOD*/ + + +BEGIN_PROPERTY(Window_Persistent) + + /* + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isPersistent()); + else + WIDGET->setPersistent(PROPERTY(char) != 0); + */ + + if (!THIS->toplevel) + { + if (READ_PROPERTY) + GB.ReturnBoolean(true); + } + else + { + if (READ_PROPERTY) + GB.ReturnBoolean(CWIDGET_test_flag(THIS, WF_PERSISTENT)); + else + { + if (VPROP(GB_BOOLEAN)) + CWIDGET_set_flag(THIS, WF_PERSISTENT); + else + CWIDGET_clear_flag(THIS, WF_PERSISTENT); + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->windowTitle()); + else + { + QString s = QSTRING_PROP(); + THIS->title = s.length() > 0; + WIDGET->setWindowTitle(s); + GB.Raise(THIS, EVENT_Title, 0); + } + + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Border) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->hasBorder()); + else + WINDOW->setBorder(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Resizable) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->isResizable()); + else + WINDOW->setResizable(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Icon) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->icon); + else + { + SET_PIXMAP(WIDGET->setWindowIcon, &(THIS->icon), PROP(GB_OBJECT)); + GB.Raise(THIS, EVENT_Icon, 0); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Picture) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->picture); + else + { + CPICTURE *new_pict = (CPICTURE *)VPROP(GB_OBJECT); + + if (new_pict != THIS->picture) + { + CPICTURE *old = THIS->picture; + GB.Ref(new_pict); + THIS->picture = new_pict; + //CWINDOW_define_mask(THIS); + CWIDGET_reset_color((CWIDGET *)THIS); + GB.Unref(POINTER(&old)); + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Mask) + + /*if (THIS->embedded) + { + if (READ_PROPERTY) + GB.ReturnBoolean(FALSE); + } + else*/ + { + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->masked); + else + { + bool new_masked = VPROP(GB_BOOLEAN); + + if (new_masked != THIS->masked) + { + THIS->masked = new_masked; + //CWINDOW_define_mask(THIS); + CWIDGET_reset_color((CWIDGET *)THIS); + } + } + } + +END_PROPERTY + + +static void manage_window_state(void *_object, void *_param, Qt::WindowState state) +{ + if (!THIS->toplevel) + { + if (READ_PROPERTY) + GB.ReturnBoolean(FALSE); + } + else + { + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->getState() & state); + else + { + if (VPROP(GB_BOOLEAN)) + WINDOW->setState(WINDOW->getState() | state); + else + WINDOW->setState(WINDOW->getState() & ~state); + } + } +} + +BEGIN_PROPERTY(Window_Minimized) + + manage_window_state(_object, _param, Qt::WindowMinimized); + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Maximized) + + manage_window_state(_object, _param, Qt::WindowMaximized); + +END_PROPERTY + + +BEGIN_PROPERTY(Window_FullScreen) + + manage_window_state(_object, _param, Qt::WindowFullScreen); + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Stacking) + + int p; + + if (!THIS->toplevel) + { + if (READ_PROPERTY) + GB.ReturnInteger(0); + return; + } + + if (READ_PROPERTY) + { + GB.ReturnInteger(THIS->stacking); + } + else + { + p = VPROP(GB_INTEGER); + if (p >= 0 && p <= 2) + { + THIS->stacking = p; + WINDOW->initProperties(PROP_STACKING); + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(Window_TopOnly) + + if (!THIS->toplevel) + { + if (READ_PROPERTY) + GB.ReturnBoolean(FALSE); + return; + } + + if (READ_PROPERTY) + { + GB.ReturnBoolean(THIS->stacking == 1); + } + else + { + THIS->stacking = VPROP(GB_BOOLEAN) ? 1 : 0; + WINDOW->initProperties(PROP_STACKING); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Window_SkipTaskbar) + + if (!THIS->toplevel) + { + if (READ_PROPERTY) + GB.ReturnBoolean(FALSE); + return; + } + + if (READ_PROPERTY) + { + GB.ReturnBoolean(THIS->skipTaskbar); + } + else + { + THIS->skipTaskbar = VPROP(GB_BOOLEAN); + WINDOW->initProperties(PROP_SKIP_TASKBAR); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Sticky) + + if (!THIS->toplevel) + { + if (READ_PROPERTY) + GB.ReturnBoolean(FALSE); + return; + } + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->sticky); + else + { + THIS->sticky = VPROP(GB_BOOLEAN); + WINDOW->initProperties(PROP_STICKY); + } + +END_PROPERTY + +BEGIN_PROPERTY(Window_Utility) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOW->isUtility()); //WINDOW->getType() == _NET_WM_WINDOW_TYPE_UTILITY); + else + { + //WINDOW->setType(VPROP(GB_BOOLEAN) ? _NET_WM_WINDOW_TYPE_UTILITY : _NET_WM_WINDOW_TYPE_NORMAL); + WINDOW->setUtility(VPROP(GB_BOOLEAN)); + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(Window_Center) + + if (!THIS->toplevel) + return; + + WINDOW->center(); + +END_METHOD + + +BEGIN_METHOD_VOID(Window_Delete) + + //qDebug("Window_Delete %p", THIS); + + do_close(THIS, 0); + + if (THIS->toplevel) + CWIDGET_clear_flag(THIS, WF_PERSISTENT); + + CWIDGET_destroy((CWIDGET *)THIS); + +END_METHOD + + +BEGIN_PROPERTY(Window_Visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(!WINDOW->isHidden()); + else + { + bool show = !!VPROP(GB_BOOLEAN); + + if (show == WINDOW->isHidden()) + { + if (show) + Window_Show(_object, _param); + else + Window_Hide(_object, _param); + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(Window_Controls_Count) + + QList list = WINDOW->findChildren(); + int i; + int n = 0; + CWIDGET *control; + + for (i = 0; i < list.count(); i++) + { + control = CWidget::getReal(list.at(i)); + if (control && !CWIDGET_check(control)) + n++; + } + + GB.ReturnInteger(n); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Window_Controls_next) + + QList children = WINDOW->findChildren(); + CWIDGET *control; + int index; + + index = ENUM(int); + + control = NULL; + + do + { + if (index >= children.count()) + { + GB.StopEnum(); + return; + } + + control = CWidget::getReal(children.at(index)); + index++; + } + while (!control || CWIDGET_check(control)); + + ENUM(int) = index; + GB.ReturnObject(control); + +END_PROPERTY + + +BEGIN_METHOD(Window_Reparent, GB_OBJECT container; GB_INTEGER x; GB_INTEGER y) + + //qDebug("Window_Reparent"); + reparent_window(THIS, VARG(container), !MISSING(x) && !MISSING(y), VARG(x), VARG(y)); + +END_METHOD + + +BEGIN_METHOD(Window_Controls_get, GB_STRING name) + + CWIDGET *control = WINDOW->names[GB.ToZeroString(ARG(name))]; + + if (!control || CWIDGET_check(control)) + GB.ReturnNull(); + else + GB.ReturnObject(control); + +END_METHOD + + +BEGIN_PROPERTY(Window_Closed) + + GB.ReturnBoolean(!THIS->opened); + +END_PROPERTY + +/***************************************************************************/ + +BEGIN_PROPERTY(CWINDOW_menu_count) + + if (THIS->menuBar) + GB.ReturnInteger(THIS->menuBar->actions().count()); + else + GB.ReturnInteger(0); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CWINDOW_menu_next) + + int index; + + if (!THIS->menuBar) + { + GB.StopEnum(); + return; + } + + index = ENUM(int); + + if (index >= THIS->menuBar->actions().count()) + { + GB.StopEnum(); + return; + } + + GB.ReturnObject(CMenu::dict[THIS->menuBar->actions().at(index)]); + + ENUM(int) = index + 1; + +END_PROPERTY + + +BEGIN_METHOD(CWINDOW_menu_get, GB_INTEGER index) + + int index = VARG(index); + + if (!THIS->menuBar || index < 0 || index >= THIS->menuBar->actions().count()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(CMenu::dict[THIS->menuBar->actions().at(index)]); + +END_PROPERTY + + +static bool are_menus_visible(void *_object) +{ + return !THIS->hideMenuBar && THIS->showMenuBar; +} + + +static void set_menus_visible(void *_object, bool v) +{ + THIS->showMenuBar = v; + WINDOW->configure(); +} + +BEGIN_PROPERTY(CWINDOW_menu_visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(are_menus_visible(THIS)); + else + set_menus_visible(THIS, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD_VOID(CWINDOW_menu_show) + + set_menus_visible(THIS, true); + +END_METHOD + +BEGIN_METHOD_VOID(CWINDOW_menu_hide) + + set_menus_visible(THIS, false); + +END_METHOD + +BEGIN_PROPERTY(Window_Opacity) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->windowOpacity() * 100); + else + { + double opacity = VPROP(GB_INTEGER) / 100.0; + + if (opacity < 0.0) + opacity = 0.0; + else if (opacity > 1.0) + opacity = 1.0; + + WIDGET->setWindowOpacity(opacity); + } + +END_PROPERTY + +BEGIN_PROPERTY(Window_Screen) + + GB.ReturnInteger(QApplication::desktop()->screenNumber(WIDGET)); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Transparent) + + bool trans = WINDOW->testAttribute(Qt::WA_TranslucentBackground); + + if (READ_PROPERTY) + GB.ReturnBoolean(trans); + else + { + bool new_trans = VPROP(GB_BOOLEAN); + if (trans == new_trans) + return; + + if (!new_trans) + { + GB.Error("Transparent property cannot be reset"); + return; + } + + WINDOW->setAttribute(Qt::WA_TranslucentBackground, true); + THIS->container->setPaintBackgroundColor(true); + THIS->widget.flag.noBackground = true; + CWIDGET_reset_color((CWIDGET *)THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(Window_TakeFocus) + + if (READ_PROPERTY) + GB.ReturnBoolean(!THIS->noTakeFocus); + else + THIS->noTakeFocus = !VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_METHOD_VOID(Window_Activate) + + if (THIS->toplevel && WINDOW->isVisible() && !WINDOW->isHidden()) + WINDOW->activateWindow(); + +END_METHOD + + +/***************************************************************************/ + +GB_DESC CWindowMenusDesc[] = +{ + GB_DECLARE(".Window.Menus", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_next", "Menu", CWINDOW_menu_next, NULL), + GB_METHOD("_get", "Menu", CWINDOW_menu_get, "(Index)i"), + GB_PROPERTY_READ("Count", "i", CWINDOW_menu_count), + GB_METHOD("Show", NULL, CWINDOW_menu_show, NULL), + GB_METHOD("Hide", NULL, CWINDOW_menu_hide, NULL), + GB_PROPERTY("Visible", "b", CWINDOW_menu_visible), + + GB_END_DECLARE +}; + +GB_DESC CWindowControlsDesc[] = +{ + GB_DECLARE(".Window.Controls", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_next", "Control", Window_Controls_next, NULL), + GB_METHOD("_get", "Control", Window_Controls_get, "(Name)s"), + GB_PROPERTY_READ("Count", "i", Window_Controls_Count), + + GB_END_DECLARE +}; + +#if 0 +GB_DESC CWindowTypeDesc[] = +{ + GB_DECLARE("WindowType", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("Normal", "i", _NET_WM_WINDOW_TYPE_NORMAL), + GB_CONSTANT("Dock", "i", _NET_WM_WINDOW_TYPE_DOCK), + GB_CONSTANT("Toolbar", "i", _NET_WM_WINDOW_TYPE_TOOLBAR), + GB_CONSTANT("Menu", "i", _NET_WM_WINDOW_TYPE_MENU), + GB_CONSTANT("Utility", "i", _NET_WM_WINDOW_TYPE_UTILITY), + GB_CONSTANT("Splash", "i", _NET_WM_WINDOW_TYPE_SPLASH), + GB_CONSTANT("Dialog", "i", _NET_WM_WINDOW_TYPE_DIALOG), + GB_CONSTANT("DropDownMenu", "i", _NET_WM_WINDOW_TYPE_DROPDOWN_MENU), + GB_CONSTANT("PopupMenu", "i", _NET_WM_WINDOW_TYPE_POPUP_MENU), + GB_CONSTANT("Tooltip", "i", _NET_WM_WINDOW_TYPE_TOOLTIP), + GB_CONSTANT("Notification", "i", _NET_WM_WINDOW_TYPE_NOTIFICATION), + GB_CONSTANT("Combo", "i", _NET_WM_WINDOW_TYPE_COMBO), + GB_CONSTANT("DragAndDrop", "i", _NET_WM_WINDOW_TYPE_DND), + GB_CONSTANT("Desktop", "i", _NET_WM_WINDOW_TYPE_DESKTOP), + + GB_END_DECLARE +}; +#endif + +GB_DESC CWindowDesc[] = +{ + GB_DECLARE("Window", sizeof(CWINDOW)), GB_INHERITS("Container"), + + GB_CONSTANT("Normal", "i", 0), + GB_CONSTANT("Above", "i", 1), + GB_CONSTANT("Below", "i", 2), + + GB_METHOD("_new", NULL, Window_new, "[(Parent)Control;]"), + GB_METHOD("_free", NULL, Window_free, NULL), + GB_METHOD("_get", "Control", Window_Controls_get, "(Name)s"), + + GB_METHOD("Close", "b", Window_Close, "[(Return)i]"), + GB_METHOD("Raise", NULL, Window_Raise, NULL), + GB_METHOD("Show", NULL, Window_Show, NULL), + GB_METHOD("Hide", NULL, Window_Hide, NULL), + GB_METHOD("ShowModal", "i", Window_ShowModal, NULL), + GB_METHOD("ShowDialog", "i", Window_ShowModal, NULL), + GB_METHOD("ShowPopup", "i", Window_ShowPopup, "[(X)i(Y)i]"), + GB_METHOD("Center", NULL, Window_Center, NULL), + GB_METHOD("Activate", NULL, Window_Activate, NULL), + + GB_PROPERTY_READ("Modal", "b", Window_Modal), + GB_PROPERTY_READ("TopLevel", "b", Window_TopLevel), + GB_PROPERTY_READ("Closed", "b", Window_Closed), + + GB_METHOD("Delete", NULL, Window_Delete, NULL), + + GB_METHOD("Reparent", NULL, Window_Reparent, "(Container)Container;[(X)i(Y)i]"), + + GB_PROPERTY("Persistent", "b", Window_Persistent), + GB_PROPERTY("Text", "s", Window_Text), + GB_PROPERTY("Title", "s", Window_Text), + GB_PROPERTY("Caption", "s", Window_Text), + GB_PROPERTY("Icon", "Picture", Window_Icon), + GB_PROPERTY("Picture", "Picture", Window_Picture), + GB_PROPERTY("Mask", "b", Window_Mask), + GB_PROPERTY("Minimized", "b", Window_Minimized), + GB_PROPERTY("Maximized", "b", Window_Maximized), + GB_PROPERTY("FullScreen", "b", Window_FullScreen), + GB_PROPERTY("TopOnly", "b", Window_TopOnly), + GB_PROPERTY("Stacking", "i", Window_Stacking), + GB_PROPERTY("Sticky", "b", Window_Sticky), + GB_PROPERTY("SkipTaskbar", "b", Window_SkipTaskbar), + GB_PROPERTY("Visible", "b", Window_Visible), + GB_PROPERTY("Opacity", "i", Window_Opacity), + GB_PROPERTY("Transparent", "b", Window_Transparent), + GB_PROPERTY("TakeFocus", "b", Window_TakeFocus), + + GB_PROPERTY("Arrangement", "i", Container_Arrangement), + GB_PROPERTY("Padding", "i", Container_Padding), + GB_PROPERTY("Spacing", "b", Container_Spacing), + GB_PROPERTY("Margin", "b", Container_Margin), + GB_PROPERTY("AutoResize", "b", Container_AutoResize), + GB_PROPERTY("Invert", "b", Container_Invert), + GB_PROPERTY("Indent", "b", Container_Indent), + + //GB_PROPERTY("Type", "i", CWINDOW_type), + GB_PROPERTY("Utility", "b", Window_Utility), + GB_PROPERTY("Border", "b", Window_Border), + GB_PROPERTY("Resizable", "b", Window_Resizable), + + GB_PROPERTY_READ("Screen", "i", Window_Screen), + + GB_PROPERTY_SELF("Menus", ".Window.Menus"), + GB_PROPERTY_SELF("Controls", ".Window.Controls"), + + WINDOW_DESCRIPTION, + + GB_EVENT("Close", NULL, NULL, &EVENT_Close), + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + GB_EVENT("Activate", NULL, NULL, &EVENT_Activate), + GB_EVENT("Deactivate", NULL, NULL, &EVENT_Deactivate), + GB_EVENT("Move", NULL, NULL, &EVENT_Move), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + GB_EVENT("Show", NULL, NULL, &EVENT_Show), + GB_EVENT("Hide", NULL, NULL, &EVENT_Hide), + GB_EVENT("Title", NULL, NULL, &EVENT_Title), + GB_EVENT("Icon", NULL, NULL, &EVENT_Icon), + GB_EVENT("Font", NULL, NULL, &EVENT_Font), + + //GB_INTERFACE("Draw", &DRAW_Interface), + + GB_END_DECLARE +}; + + +GB_DESC CWindowsDesc[] = +{ + GB_DECLARE("Windows", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_next", "Window", CWINDOW_next, NULL), + GB_STATIC_METHOD("_get", "Window", CWINDOW_get_from_id, "(Id)i"), + GB_STATIC_PROPERTY_READ("Count", "i", CWINDOW_count), + + GB_END_DECLARE +}; + + +GB_DESC CFormDesc[] = +{ + GB_DECLARE("Form", sizeof(CFORM)), GB_INHERITS("Window"), + GB_AUTO_CREATABLE(), + + GB_STATIC_METHOD("Main", NULL, CFORM_main, NULL), + GB_STATIC_METHOD("Load", NULL, CFORM_load, "[(Parent)Control;]"), + GB_METHOD("_new", NULL, CFORM_new, NULL), + + FORM_DESCRIPTION, + + GB_END_DECLARE +}; + + +/*************************************************************************** + + MyMainWindow + +***************************************************************************/ + +MyMainWindow::MyMainWindow(QWidget *parent, const char *name, bool embedded) : + QWidget::QWidget(parent, embedded ? Qt::Widget : Qt::Window) +{ + sg = 0; + _border = true; + _resizable = true; + _deleted = false; + _type = _NET_WM_WINDOW_TYPE_NORMAL; + _enterLoop = false; + _utility = false; + _state = windowState(); + _screen = -1; + + //setAttribute(Qt::WA_KeyCompression, true); + //setAttribute(Qt::WA_InputMethodEnabled, true); + setAttribute(Qt::WA_QuitOnClose, false); + setAttribute(Qt::WA_StaticContents, true); + setObjectName(name); + setFocusPolicy(Qt::NoFocus); + + _activate = false; +} + + +MyMainWindow::~MyMainWindow() +{ + CWINDOW *_object = (CWINDOW *)CWidget::get(this); + + #if DEBUG_WINDOW + qDebug("~MyMainWindow: %s %s %p", GB.GetClassName(THIS), THIS->widget.name, THIS); + #endif + + do_close(THIS, 0, true); + + if (CWINDOW_Active == THIS) + CWINDOW_Active = 0; + + if (CWINDOW_LastActive == THIS) + CWINDOW_LastActive = 0; + + if (sg) + delete sg; + + GB.Detach(THIS); + + if (THIS->menuBar) + { + //CMenu::unrefChildren(THIS->menuBar); + //qDebug("delete menuBar"); + QMenuBar *menuBar = THIS->menuBar; + THIS->menuBar = 0; + delete menuBar; + } + + CWindow::removeTopLevel(THIS); + + _deleted = true; + + //qDebug("~MyMainWindow %p (end)", this); +} + +#if 0 +bool MyMainWindow::event(QEvent *e) +{ + if (e->spontaneous() && !CWIDGET_test_flag(THIS, WF_DELETED)) + { + /*if (e->type() == QEvent::WindowActivate) + { + qDebug("activate: %s %p", GB.GetClassName(THIS), THIS); + //CWINDOW_activate((CWIDGET *)THIS); + GB.Ref(THIS); + GB.Post((void (*)())activate_later, (intptr_t)THIS); + + } + else*/ if (e->type() == QEvent::WindowDeactivate) + { + qDebug("deactivate: %s %p", GB.GetClassName(THIS), THIS); + if (THIS == CWINDOW_Active) + CWINDOW_activate(NULL); + } + } + + return QWidget::event(e); +} +#endif + +void MyMainWindow::showEvent(QShowEvent *e) +{ + CWINDOW *_object = (CWINDOW *)CWidget::get(this); + + //qDebug("showEvent: %s\n", GB.GetClassName(THIS)); + + emit_open_event(THIS); + + //CWINDOW_fix_menubar(THIS); + + if (_activate) + { + //qDebug("showEvent: activate: %s", THIS->widget.name); + raise(); + //setFocus(); + activateWindow(); + //X11_window_activate(effectiveWinId()); + _activate = false; + } + + QWidget::showEvent(e); +} + + +void MyMainWindow::initProperties(int which) +{ + #ifndef NO_X_WINDOW + CWIDGET *_object = CWidget::get(this); + + if (!THIS->toplevel || effectiveWinId() == 0) + return; + + if (!THIS->title && _border) + setWindowTitle(TO_QSTRING(GB.Application.Title())); + + //qDebug("initProperties: %d", which); + X11_flush(); + + if (which & (PROP_STACKING | PROP_SKIP_TASKBAR)) + { + X11_window_change_begin(effectiveWinId(), isVisible()); + + if (which & PROP_STACKING) + { + X11_window_change_property(X11_atom_net_wm_state_above, THIS->stacking == 1); + X11_window_change_property(X11_atom_net_wm_state_stays_on_top, THIS->stacking == 1); + X11_window_change_property(X11_atom_net_wm_state_below, THIS->stacking == 2); + } + if (which & PROP_SKIP_TASKBAR) + X11_window_change_property(X11_atom_net_wm_state_skip_taskbar, THIS->skipTaskbar); + + X11_window_change_end(); + } + + //if (which == PROP_ALL) + // X11_set_window_type(effectiveWinId(), _type); + + if (which & PROP_BORDER) + X11_set_window_decorated(effectiveWinId(), _border); + + if (which & PROP_STICKY) + X11_window_set_desktop(effectiveWinId(), isVisible(), THIS->sticky ? 0xFFFFFFFF : X11_get_current_desktop()); + + X11_flush(); + #endif +} + +void MyMainWindow::setEventLoop() +{ + if (!CWIDGET_test_flag(THIS, WF_CLOSED)) + THIS->loopLevel = CWINDOW_Current ? CWINDOW_Current->loopLevel : 0; +} + +void MyMainWindow::activateLater() +{ + activateWindow(); +} + +void MyMainWindow::present(QWidget *parent) +{ + /*CWIDGET *_object = CWidget::get(this); + CWIDGET *_parent = parent ? CWidget::get(parent) : 0; + + qDebug("present: %p %s: parent = %p %s", THIS, _object->name, _parent, _parent ? _parent->name : "");*/ + + if (parent) + _screen = QApplication::desktop()->screenNumber(parent); + else + _screen = -1; + + if (!isVisible()) + { + //X11_window_startup(WINDOW->effectiveWinId(), THIS->x, THIS->y, THIS->w, THIS->h); + + if (isUtility() && _resizable) + setMinimumSize(THIS->minw, THIS->minh); + + setAttribute(Qt::WA_ShowWithoutActivating, THIS->noTakeFocus); + +#ifndef QT5 + if (effectiveWinId() == 0) + { + createWinId(); + } + initProperties(PROP_ALL); +#endif + + if (getState() & Qt::WindowMinimized) + showMinimized(); + else if (getState() & Qt::WindowFullScreen) + showFullScreen(); + else if (getState() & Qt::WindowMaximized) + showMaximized(); + else + show(); + +#ifdef QT5 + //qDebug("createWinId: %p", (void *)effectiveWinId()); + if (THIS->noTakeFocus) + X11_window_set_user_time(effectiveWinId(), 0); + initProperties(PROP_ALL); + if (THIS->noTakeFocus) + X11_window_set_user_time(effectiveWinId(), 0); +#else + initProperties(PROP_SKIP_TASKBAR); +#endif + + /*if (isUtility() && _resizable) + setSizeGrip(true); + else + setSizeGrip(false);*/ + } + else + { + //_activate = true; + + if (getState() & Qt::WindowMinimized) + { + setState(windowState() & ~Qt::WindowMinimized); + //qDebug("_activate set #2"); + } + } + + if (!THIS->noTakeFocus) // && (parent || hasBorder())) + activateWindow(); + + if (parent) + X11_set_transient_for(effectiveWinId(), parent->effectiveWinId()); + + raise(); +} + +void MyMainWindow::showActivate(QWidget *transient) +{ + QWidget *newParentWidget = 0; + + //qDebug("showActivate: %s %d", THIS->widget.name, isToolbar()); + + // Reparent the window if, for example, there is an already modal window displayed + + if (CWINDOW_Current && THIS != CWINDOW_Current) + { + newParentWidget = CWINDOW_Current->widget.widget; + + if (!isVisible()) + { + if (newParentWidget && parentWidget() != newParentWidget) + { + doReparent(newParentWidget, pos()); + } + } + } + + //qDebug("showActivate %p", _object); + + //CWIDGET_clear_flag(THIS, WF_CLOSED); + + if (isUtility()) + { + if (!newParentWidget && CWINDOW_Main && THIS != CWINDOW_Main) + newParentWidget = CWidget::getTopLevel((CWIDGET *)CWINDOW_Main)->widget.widget; + } + + present(newParentWidget); + setEventLoop(); +} + +void on_error_show_modal(MODAL_INFO *info) +{ + #ifdef DEBUG_WINDOW + qDebug("on_error_show_modal"); + #endif + + // info->that can be NULL if the dialog is destroyed during the event loop + + if (info->that) + info->that->_enterLoop = false; + + MyApplication::eventLoop->exit(); + + GB.Debug.LeaveEventLoop(); + + MyApplication::eventLoop = info->old; + CWINDOW_Current = info->save; + + if (info->that && info->that->isPersistent()) + { + info->that->setSizeGrip(false); + info->that->setWindowModality(Qt::NonModal); + } +} + +void MyMainWindow::showModal(void) +{ + //Qt::WindowFlags flags = windowFlags() & ~Qt::WindowType_Mask; + CWIDGET *_object = CWidget::get(this); + CWINDOW *parent; + bool persistent = CWIDGET_test_flag(THIS, WF_PERSISTENT); + //QPoint p = pos(); + QEventLoop eventLoop; + GB_ERROR_HANDLER handler; + MODAL_INFO info; + + if (isModal()) + return; + + CWIDGET_finish_focus(); + + info.that = this; + info.old = MyApplication::eventLoop; + info.save = CWINDOW_Current; + + MyApplication::eventLoop = &eventLoop; + + setWindowModality(Qt::ApplicationModal); + + if (_resizable && _border) + { + setMinimumSize(THIS->minw, THIS->minh); + setSizeGrip(true); + } + + _enterLoop = false; // Do not call exitLoop() if we do not entered the loop yet! + + parent = CWINDOW_Current; + if (!parent) + { + parent = CWINDOW_Main; + if (!parent) + parent = CWINDOW_Active; + } + + present(parent ? CWidget::getTopLevel((CWIDGET *)parent)->widget.widget : 0); + setEventLoop(); + + THIS->loopLevel++; + CWINDOW_Current = THIS; + + _enterLoop = true; + + GB.Debug.EnterEventLoop(); + + handler.handler = (GB_CALLBACK)on_error_show_modal; + handler.arg1 = (intptr_t)&info; + + GB.OnErrorBegin(&handler); + + eventLoop.exec(); + + GB.OnErrorEnd(&handler); + + GB.Debug.LeaveEventLoop(); + //eventLoop.processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::DeferredDeletion, 0); + + MyApplication::eventLoop = info.old; + CWINDOW_Current = info.save; + + if (persistent) + { + setSizeGrip(false); + setWindowModality(Qt::NonModal); + } + + CWINDOW_ensure_active_window(); +} + +void MyMainWindow::showPopup(QPoint &pos) +{ + Qt::WindowFlags flags = windowFlags() & ~Qt::WindowType_Mask; + CWIDGET *_object = CWidget::get(this); + bool persistent = CWIDGET_test_flag(THIS, WF_PERSISTENT); + CWINDOW *save = CWINDOW_Current; + void *save_popup; + + if (isModal()) + return; + + setWindowFlags(Qt::Popup | flags); + setWindowModality(Qt::ApplicationModal); + THIS->popup = true; + + /*if (_resizable && _border) + { + setMinimumSize(THIS->minw, THIS->minh); + setSizeGrip(true); + }*/ + + _enterLoop = false; // Do not call exitLoop() if we do not entered the loop yet! + + move(0, 0); + move(pos); + setFocus(); + show(); + raise(); + setEventLoop(); + //QTimer::singleShot(50, this, SLOT(activateLater())); + + THIS->loopLevel++; + CWINDOW_Current = THIS; + + //handle_focus(THIS); + //activateWindow(); + + save_popup = CWIDGET_enter_popup(); + + _enterLoop = true; + + QEventLoop eventLoop; + QEventLoop *old; + + old = MyApplication::eventLoop; + MyApplication::eventLoop = &eventLoop; + GB.Debug.EnterEventLoop(); + eventLoop.exec(); + GB.Debug.LeaveEventLoop(); + MyApplication::eventLoop = old; + //eventLoop.exec(); + //eventLoop.processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::DeferredDeletion, 0); + + CWINDOW_Current = save; + + if (persistent) + { + setWindowModality(Qt::NonModal); + setWindowFlags(Qt::Window | flags); + THIS->popup = false; + } + + CWIDGET_leave_popup(save_popup); + + //CWIDGET_check_hovered(); +} + +#if 0 +void MyMainWindow::setTool(bool t) +{ + WFlags f = getWFlags(); + + if (t) + f |= WStyle_Tool | WStyle_Customize; + else + f &= ~WStyle_Tool; + + doReparent(CWINDOW_Main ? (MyMainWindow *)QWIDGET(CWINDOW_Main) : 0, f, pos()); +} +#endif + +void MyMainWindow::moveSizeGrip() +{ + CWINDOW *window; + QWidget *cont; + + if (sg == 0) + return; + + window = (CWINDOW *)CWidget::get(this); + cont = window->container; + + if (qApp->isRightToLeft()) + sg->move(cont->rect().bottomLeft() - sg->rect().bottomLeft()); + else + sg->move(cont->rect().bottomRight() - sg->rect().bottomRight()); +} + +void MyMainWindow::setSizeGrip(bool on) +{ + if (on == (sg != 0)) + return; + + if (!on) + { + delete sg; + sg = 0; + } + else //if (!parentWidget()) + { + sg = new QSizeGrip(((CWINDOW *)CWidget::get(this))->container); + sg->adjustSize(); + moveSizeGrip(); + sg->lower(); + //if (paletteBackgroundPixmap()) + // sg->setBackgroundOrigin(QWidget::AncestorOrigin); + sg->show(); + } +} + +void MyMainWindow::setBorder(bool b) +{ + if (_border == b) + return; + + _border = b; + if (!isWindow()) + return; + + if (effectiveWinId()) + { + //qDebug("effectiveWinId"); + initProperties(PROP_BORDER); + X11_window_remap(effectiveWinId()); + } +#ifndef QT5 + doReparent(parentWidget(), pos()); +#endif +} + +void MyMainWindow::setResizable(bool b) +{ + if (_resizable == b) + return; + + _resizable = b; + if (!isWindow()) + return; + doReparent(parentWidget(), pos()); +} + +void MyMainWindow::setUtility(bool b) +{ + Qt::WindowFlags flags; + + if (_utility == b) + return; + + _utility = b; + + doReparent(parentWidget(), pos()); +} + +#ifdef NO_X_WINDOW +#else +int MyMainWindow::getType() +{ + if (!isWindow()) + return 0; + else + return _type; + //return X11_get_window_type(effectiveWinId()); +} + +void MyMainWindow::setType(int type) +{ + if (!isWindow()) + return; + X11_set_window_type(effectiveWinId(), type); + _type = type; +} +#endif + +void MyMainWindow::moveEvent(QMoveEvent *e) +{ + CWIDGET *_object = CWidget::getReal(this); + + //qDebug("Move: (%s %p) %d %d", GB.GetClassName(THIS), THIS, e->pos().x(), e->pos().y()); + + QWidget::moveEvent(e); + + //qDebug("Move (pos %d %d) (oldPos %d %d)", e->pos().x(), e->pos().y(), e->oldPos().x(), e->oldPos().y()); + //qDebug(" (geom %d %d) (fgeom %d %d)", geometry().x(), geometry().y(), frameGeometry().x(), frameGeometry().y()); + //qDebug(" Visible = %s Hidden = %s", (isVisible() ? "Yes" : "No"), (isHidden() ? "Yes" : "No")); + //qDebug(" Flags = 0x%s State = 0x%s", QString::number(getWFlags(), 16).latin1(), QString::number(getWState(), 16).latin1()); + + //if (CWIDGET_test_flag(ob, WF_IGNORE_MOVE)) + + //if (THIS->embedded) + // return; + + if (THIS->toplevel) + { + if (hasBorder() && !THIS->reallyMasked) + if (geometry().x() == frameGeometry().x() && geometry().y() == frameGeometry().y()) + return; + + if (!isHidden()) + { + THIS->x = x(); + THIS->y = y(); + //qDebug("moveEvent: x= %d y = %d", x(), y()); + } + } + + //qDebug("moveEvent %ld %ld isHidden:%s shown:%s ", THIS->x, THIS->y, isHidden() ? "1" : "0", shown ? "1" : "0"); + + if (THIS->opened) + GB.Raise(THIS, EVENT_Move, 0); +} + + + +/* +static void post_resize_event(CWINDOW *_object) +{ + qDebug("post resize: %d %d", THIS->w, THIS->h); + WINDOW->resize(THIS->w, THIS->h); + GB.Unref(POINTER(&_object)); +} +*/ + +void MyMainWindow::resizeEvent(QResizeEvent *e) +{ + CWINDOW *_object = (CWINDOW *)CWidget::getReal(this); + //int w, h; + + //qDebug("Resize %p: %d %d <- %d %d", _object, e->size().width(), e->size().height(), e->oldSize().width(), e->oldSize().height()); + + //QMainWindow::resizeEvent(e); + + configure(); + + if (sg) + moveSizeGrip(); + + if (!isHidden()) + { + THIS->w = THIS->container->width(); + THIS->h = THIS->container->height(); + if (isTopLevel()) + CCONTAINER_arrange(THIS); + } + +#ifndef NO_X_WINDOW +#ifndef QT5 + if (THIS->xembed) + XEMBED->resize(THIS->w, THIS->h); +#endif +#endif + + //qDebug("resizeEvent %ld %ld isHidden:%s shown:%s ", THIS->w, THIS->h, isHidden() ? "1" : "0", shown ? "1" : "0"); + //qDebug("THIS->h = %ld THIS->container->height() = %ld height() = %ld", THIS->h, THIS->container->height(), height()); + + if (THIS->opened) + raise_resize_event(THIS); +} + + +void MyMainWindow::keyPressEvent(QKeyEvent *e) +{ + CWINDOW *_object = (CWINDOW *)CWidget::getReal(this); + QPushButton *test = 0; + CWIDGET *ob; + + e->ignore(); + + //qDebug("MyMainWindow::keyPressEvent: (%p '%s' %s)", this, this ? this->caption().latin1() : 0, GB.GetClassName(CWidget::get(this))); + + if ((e->modifiers() == Qt::NoModifier || (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter ))) + { + switch (e->key()) + { + case Qt::Key_Enter: + case Qt::Key_Return: + + test = THIS->defaultButton; + break; + + case Qt::Key_Escape: + + test = THIS->cancelButton; + break; + } + + if (!test) + return; + + ob = CWidget::get(test); + if (!ob) + return; + + if (CWIDGET_test_flag(ob, WF_DESIGN)) + return; + + if (!test->isVisible() || !test->isEnabled()) + return; + + test->setFocus(); + test->animateClick(); + e->accept(); + } +} + + +bool CWINDOW_close_all(bool main) +{ + QList list(CWindow::list); + CWINDOW *win; + int i; + bool ret = false; + + #if DEBUG_WINDOW + qDebug("<<< CLOSE ALL"); + #endif + + for (i = 0; i < list.count(); i++) + { + win = list.at(i); + if (win != CWINDOW_Main && do_close(win, 0)) + { + ret = true; + break; + } + } + + if (main && CWINDOW_Main) + ret = do_close(CWINDOW_Main, 0); + + #if DEBUG_WINDOW + qDebug(">>> CLOSE ALL"); + #endif + + return ret; +} + +void CWINDOW_delete_all(bool main) +{ + QList list(CWindow::list); + CWINDOW *win; + int i; + + #if DEBUG_WINDOW + qDebug("<<< DELETE ALL"); + #endif + + for (i = 0; i < list.count(); i++) + { + win = CWindow::list.at(i); + if (win != CWINDOW_Main) + { + //qDebug("destroy window %s", GB.GetClassName(win)); + CWIDGET_destroy((CWIDGET *)win); + } + } + + if (main && CWINDOW_Main) + { + //qDebug("destroy main window %s", GB.GetClassName(CWINDOW_Main)); + CWIDGET_destroy((CWIDGET *)CWINDOW_Main); + } + + #if DEBUG_WINDOW + qDebug("DELETE ALL >>>"); + #endif + + //qApp->eventLoop()->processEvents(QEventLoop::AllEvents); +} + +bool CWINDOW_must_quit() +{ + CWINDOW *win; + int i; + + for (i = 0; i < CWindow::list.count(); i++) + { + win = CWindow::list.at(i); + if (win->opened) + return false; + } + + return true; +} + + +void MyMainWindow::closeEvent(QCloseEvent *e) +{ + CWINDOW *_object = (CWINDOW *)CWidget::get(this); + bool cancel = false; + //bool modal; + + e->ignore(); + + #if DEBUG_WINDOW + qDebug("closeEvent: CWINDOW_Current = %p / %d <-> %p / %d", CWINDOW_Current, CWINDOW_Current ? CWINDOW_Current->loopLevel : -1, THIS, THIS->loopLevel); + #endif + + if (THIS->opened) + { + // If a window is not opened, then it can be closed whatever the loop level is + if (CWINDOW_Current && (THIS->loopLevel != CWINDOW_Current->loopLevel)) + { + goto IGNORE; + } + + //qDebug("THIS->opened = %d: %p: %s", THIS->opened, THIS, GB.GetClassName(THIS)); + THIS->closing = true; + //qDebug("Close event: %s %p", GB.GetClassName(THIS), THIS); + cancel = GB.Raise(THIS, EVENT_Close, 0); + THIS->closing = false; + } + + if (!cancel && THIS == CWINDOW_Main) + { + if (CWINDOW_close_all(false)) + cancel = true; + } + + if (cancel) + goto IGNORE; + + //modal = isModal(); //testWFlags(Qt::WShowModal); // && THIS->opened; + + CWIDGET_set_flag(THIS, WF_CLOSED); + //qApp->sendEvent(WIDGET, new QEvent(EVENT_CLOSE)); + + /*if (CWINDOW_Active == THIS) + { + //qDebug("closeEvent activate: %p %p", CWidget::get(WIDGET->parentWidget()), CWINDOW_Active); + CWINDOW_activate(CWidget::get(WIDGET->parentWidget())); + }*/ + if (CWINDOW_LastActive == THIS) + { + //GB.Unref(POINTER(&CWINDOW_LastActive)); + CWINDOW_LastActive = NULL; + //qDebug("CWINDOW_LastActive = 0"); + } + + if (THIS == CWINDOW_Active) + CWINDOW_activate(NULL); + + if (!CWIDGET_test_flag(THIS, WF_PERSISTENT)) + { + if (CWINDOW_Main == THIS) + { + CWINDOW_delete_all(false); + #if DEBUG_WINDOW + qDebug("CWINDOW_Main -> NULL"); + #endif + CWINDOW_Main = NULL; + } + + CWIDGET_destroy((CWIDGET *)THIS); + } + + e->accept(); + + if (isModal() && _enterLoop) + { + _enterLoop = false; + MyApplication::eventLoop->exit(); + } + + #if DEBUG_WINDOW + qDebug("THIS->opened <- false: %p: %s", THIS, GB.GetClassName(THIS)); + #endif + THIS->opened = false; + MAIN_check_quit(); + + return; + +IGNORE: + + CWIDGET_clear_flag(THIS, WF_CLOSED); + e->ignore(); +} + + +bool MyMainWindow::isPersistent(void) +{ + return !testAttribute(Qt::WA_DeleteOnClose); //testWFlags(WDestructiveClose); +} + + +void MyMainWindow::setPersistent(bool pers) +{ + setAttribute(Qt::WA_DeleteOnClose, !pers); +} + +void MyMainWindow::doReparent(QWidget *parent, const QPoint &pos) +{ + CWINDOW *_object = (CWINDOW *)CWidget::get(this); + QIcon icon; + bool old_toplevel; + bool hidden; + bool reparented = false; + Qt::WindowFlags f = windowFlags(); + #ifndef NO_X_WINDOW + bool active = qApp->activeWindow() == this; + #endif + + icon = windowIcon(); + + old_toplevel = THIS->toplevel; + THIS->toplevel = !parent || parent->isWindow(); + THIS->embedded = !THIS->toplevel; + + f &= ~Qt::WindowType_Mask; + + if (THIS->toplevel) + { + if (_utility) + f |= Qt::Tool; + else + f |= Qt::Window; + + if (!old_toplevel) + CWindow::insertTopLevel(THIS); + } + else + { + if (old_toplevel) + { + THIS->toplevel = true; + CWindow::removeTopLevel(THIS); + THIS->toplevel = false; + } + } + + //qDebug("doReparent: %s %p: visible = %d opened = %d hidden = %d isVisible = %d isHidden = %d shown = %d", + // THIS->widget.name, THIS, THIS->widget.flag.visible, THIS->opened, THIS->hidden, isVisible(), isHidden(), THIS->widget.flag.shown); + + //if (!THIS->hidden) showIt = true; + //hide(); + + hidden = THIS->hidden || !WIDGET->isVisible(); + if (parent != parentWidget() || f != windowFlags()) + { + reparented = true; + setParent(parent, f); + //qDebug("setParent %d", f != windowFlags()); + } + + move(pos); + //qDebug("doReparent: (%s %p) (%d %d) -> (%d %d)", GB.GetClassName(THIS), THIS, pos.x(), pos.y(), WIDGET->x(), WIDGET->y()); + + if (!THIS->embedded) + { + #ifndef NO_X_WINDOW + initProperties(PROP_ALL); + if (active && hasBorder()) + activateWindow(); + #endif + + setWindowIcon(icon); + } + + if (!_resizable && _border && isWindow()) + { + setMinimumSize(width(), height()); + setMaximumSize(width(), height()); + } + else + { + setMinimumSize(0, 0); + setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + } + + /*if (parentWidget()) + qDebug("doReparent (%s %p): new parent = (%s %p)", THIS->widget.name, THIS, CWidget::get(parentWidget())->name, CWidget::get(parentWidget())); + else + qDebug("doReparent (%s %p): new parent = 0", THIS->widget.name, THIS);*/ + + if (reparented) + { + if (!hidden) + Window_Show(THIS, NULL); + } +} + +int MyMainWindow::currentScreen() const +{ + if (_screen >= 0) + return _screen; + + if (CWINDOW_Active) + return QApplication::desktop()->screenNumber(CWINDOW_Active->widget.widget); + else if (CWINDOW_Main) + return QApplication::desktop()->screenNumber(CWINDOW_Main->widget.widget); + else + return QApplication::desktop()->primaryScreen(); +} + +void MyMainWindow::center() +{ + CWINDOW *_object = (CWINDOW *)CWidget::get(this); + QPoint p; + QRect r; + + r = QApplication::desktop()->availableGeometry(currentScreen()); + + CWIDGET_move(THIS, r.x() + (r.width() - width()) / 2, r.y() + (r.height() - height()) / 2); +} + +void MyMainWindow::configure() +{ + CWINDOW *_object = (CWINDOW *)CWidget::get(this); + QMenuBar *menuBar = THIS->menuBar; + bool arrange = false; + QRect geom; + + //qDebug("THIS->menuBar = %p menuBar() = %p", THIS->menuBar, menuBar()); + + if (menuBar && THIS->showMenuBar && !THIS->hideMenuBar) + { + int h = menuBar->sizeHint().height(); + + if (h == 0) + h = menuBar->height(); + + menuBar->show(); + geom = QRect(0, h, this->width(), this->height() - h); + + if (THIS->container->geometry() != geom) + { + arrange = true; + THIS->container->setGeometry(geom); + } + menuBar->setGeometry(0, 0, this->width(), h); + } + else + { + if (menuBar) + { + menuBar->move(0, -menuBar->height()); + menuBar->lower(); + } + + geom = QRect(0, 0, this->width(), this->height()); + + if (THIS->container->geometry() != geom) + { + arrange = true; + THIS->container->setGeometry(geom); + } + + THIS->container->raise(); + } + + if (arrange) + CCONTAINER_arrange(THIS); + + //qDebug(">>> THIS->menuBar = %p menuBar() = %p", THIS->menuBar, menuBar()); + + //qDebug("configure: %p (%d %d %d %d)", THIS, ((QFrame *)(THIS->container))->contentsRect().x(), ((QFrame *)(THIS->container))->contentsRect().y(), ((QFrame *)(THIS->container))->contentsRect().width(), ((QFrame *)(THIS->container))->contentsRect().height()); +} + + +void MyMainWindow::setName(const char *name, CWIDGET *control) +{ + if (_deleted) + return; + + names.remove(name); + if (control) + names.insert(name, control); +} + +void MyMainWindow::resize(int w, int h) +{ + bool save = _resizable; + + if (!_resizable && _border) + setResizable(true); + + QWidget::resize(w, h); + + if (_resizable != save) + setResizable(save); +} + +void MyMainWindow::setGeometry(int x, int y, int w, int h) +{ + bool save = _resizable; + + if (!_resizable && _border) + setResizable(true); + + QWidget::setGeometry(x, y, w, h); + + if (_resizable != save) + setResizable(save); +} + + +void MyMainWindow::changeEvent(QEvent *e) +{ + QWidget::changeEvent(e); + + if (e->type() == QEvent::StyleChange || e->type() == QEvent::FontChange) + { + configure(); + void *_object = CWidget::get(this); + GB.Raise(THIS, EVENT_Font, 0); + } + /*else if (e->type() == QEvent::WindowStateChange) + { + qDebug("WindowStateChange"); + CWINDOW *_object = (CWINDOW *)CWidget::get(this); + GB.Raise(THIS, EVENT_State, 0); + }*/ +} + +Qt::WindowStates MyMainWindow::getState() const +{ + return isVisible() ? windowState() : _state; +} + +void MyMainWindow::setState(Qt::WindowStates state) +{ + if (isVisible()) + setWindowState(state); + else + _state = state; +} + +void MyMainWindow::setVisible(bool visible) +{ + if (!visible) + setAttribute(Qt::WA_NoMouseReplay); + QWidget::setVisible(visible); +} + +void MyMainWindow::setBetterMask(QPixmap &bg) +{ + if (!bg.hasAlphaChannel()) + return; + + static const uchar qt_pixmap_bit_mask[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; + const QImage img = bg.toImage(); + const QImage image = (img.depth() < 32 ? img.convertToFormat(QImage::Format_ARGB32_Premultiplied) : img); + const int w = image.width(); + const int h = image.height(); + + QImage mask(w, h, QImage::Format_MonoLSB); + if (mask.isNull()) // allocation failed + return; + + mask.setColorCount(2); + mask.setColor(0, QColor(Qt::color0).rgba()); + mask.setColor(1, QColor(Qt::color1).rgba()); + + const int bpl = mask.bytesPerLine(); + + for (int y = 0; y < h; ++y) { + const QRgb *src = reinterpret_cast(image.scanLine(y)); + uchar *dest = mask.scanLine(y); + memset(dest, 0, bpl); + for (int x = 0; x < w; ++x) { + if (qAlpha(*src) >= 128) + dest[x >> 3] |= qt_pixmap_bit_mask[x & 7]; + ++src; + } + } + + setMask(QBitmap::fromImage(mask)); +} + +/*************************************************************************** + + CWindow + +***************************************************************************/ + +CWindow CWindow::manager; +int CWindow::count = 0; +QList CWindow::list; + +/*static void post_activate_event(void *ob) +{ + GB.Raise(ob, EVENT_Activate, 0); + GB.Unref(&ob); +} + +static void post_deactivate_event(void *ob) +{ + GB.Raise(ob, EVENT_Deactivate, 0); + GB.Unref(&ob); +}*/ + +void CWINDOW_activate(CWIDGET *ob) +{ + CWINDOW *active; + + //qDebug("CWINDOW_activate: %s", ob ? ob->name : NULL); + + if (ob) + { + active = CWidget::getWindow(ob); + for(;;) + { + if (active->toplevel) + break; + if (GB.CanRaise(active, EVENT_Activate)) + break; + active = CWidget::getWindow(CWidget::get(QWIDGET(active)->parentWidget())); + } + } + else + active = 0; + + if (active == CWINDOW_Active) + return; + + //qDebug("CWINDOW_activate: (%s %p): (%s %p) -> (%s %p)", ob ? GB.GetClassName(ob) : "", ob, CWINDOW_Active ? GB.GetClassName(CWINDOW_Active) : "", CWINDOW_Active, active ? GB.GetClassName(active) : "", active); + + if (CWINDOW_Active) + { + GB.Raise(CWINDOW_Active, EVENT_Deactivate, 0); + CWINDOW_Active = 0; + } + + if (active) + GB.Raise(active, EVENT_Activate, 0); + + CWINDOW_Active = active; + + CWIDGET_check_hovered(); +} + +void CWINDOW_set_default_button(CWINDOW *win, QPushButton *button, bool on) +{ + if (on) + { + if (win->defaultButton) + win->defaultButton->setDefault(false); + + win->defaultButton = button; + button->setDefault(true); + } + else + { + if (win->defaultButton == button) + { + button->setDefault(false); + win->defaultButton = 0; + } + } +} + +void CWINDOW_set_cancel_button(CWINDOW *win, QPushButton *button, bool on) +{ + //qDebug("CWINDOW_set_cancel_button: (%s %p) (%s %p) %d", GB.GetClassName(win), win, GB.GetClassName(CWidget::get(button)), CWidget::get(button), on); + if (on) + { + win->cancelButton = button; + } + else + { + if (button == win->cancelButton) + win->cancelButton = 0; + } +} + + +bool CWindow::eventFilter(QObject *o, QEvent *e) +{ + CWINDOW *_object = (CWINDOW *)CWidget::get(o); + + if (THIS && !CWIDGET_test_flag(THIS, WF_DELETED)) + { + if (e->type() == QEvent::Show) // && !e->spontaneous()) + { + MyMainWindow *w = (MyMainWindow *)o; + + if (THIS->toplevel && !THIS->popup && !THIS->moved) + w->center(); + + //handle_focus(THIS); + emit_open_event(THIS); + + //qDebug("eventFilter: Show: %s %d (%d) focus = %p", GB.GetClassName(THIS), !WINDOW->isHidden(), e->spontaneous(), THIS->focus); + + post_show_event(THIS); + //CWINDOW_define_mask(THIS); + + GB.Raise(THIS, EVENT_Show, 0); + if (!e->spontaneous()) + CACTION_raise(THIS); + } + else if (e->type() == QEvent::Hide) // && !e->spontaneous()) + { + //qDebug("Hide: %s %d (%d)", GB.GetClassName(THIS), WINDOW->isHidden(), e->spontaneous()); + //if (WINDOW->isHidden()) + { + GB.Raise(THIS, EVENT_Hide, 0); + if (!e->spontaneous()) + CACTION_raise(THIS); + } + } + } + + return QObject::eventFilter(o, e); // standard event processing +} + +#ifdef QT5 +void CWindow::error(void) +{ +} + +void CWindow::embedded(void) +{ +} + +void CWindow::closed(void) +{ +} +#else +void CWindow::error(void) +{ + //CWINDOW *_object = (CWINDOW *)CWidget::getReal((QObject *)sender()); + //qDebug("XEMBED: CWindow::error %p -> %p", sender(), THIS); + CWINDOW_EmbedState = EMBED_ERROR; +} + +void CWindow::embedded(void) +{ + //CWINDOW *_object = (CWINDOW *)CWidget::getReal((QObject *)sender()); + //qDebug("XEMBED: CWindow::embedded %p -> %p", sender(), THIS); + CWINDOW_EmbedState = EMBED_OK; +} + +void CWindow::closed(void) +{ + //CWINDOW *_object = (CWINDOW *)CWidget::getReal((QObject *)sender()); + //qDebug("XEMBED: CWindow::closed %p -> %p", sender(), THIS); + //CWIDGET_destroy(CWidget::getReal((QObject *)sender())); + delete sender(); +} +#endif + +void CWindow::destroy(void) +{ + CWINDOW *_object = (CWINDOW *)CWidget::getReal((QObject *)sender()); + //qDebug("XEMBED: CWindow::destroy %p -> %p", sender(), THIS); + + if (THIS) + { + do_close(THIS, 0, true); + CWindow::removeTopLevel(THIS); + } + +#ifndef QT5 + CWINDOW_EmbedState = EMBED_WAIT; + CWINDOW_Embedded = false; + CWINDOW_Embedder = 0; +#endif +} + +void CWindow::insertTopLevel(CWINDOW *_object) +{ + if (!THIS->toplevel) + return; + + list.append(THIS); + count = list.count(); + + #if DEBUG_WINDOW + qDebug("insertTopLevel: count = %d (%p %s %s)", count, _object, THIS->widget.name, THIS->embedded ? "E" : "W"); + #endif +} + +void CWindow::removeTopLevel(CWINDOW *_object) +{ + if (!THIS->toplevel) + return; + + list.removeAll(THIS); + count = list.count(); + + #if DEBUG_WINDOW + qDebug("removeTopLevel: count = %d (%p %s %s)", count, THIS, THIS->widget.name, THIS->embedded ? "E" : "W"); + #endif + + MAIN_check_quit(); +} + +CMENU *CWindow::findMenu(CWINDOW *_object, const char *name) +{ + int i; + CMENU *menu; + CWIDGET *parent; + + for(;;) + { + if (THIS->menuBar) + { + for (i = 0; i < THIS->menuBar->actions().count(); i++) + { + menu = CMenu::dict[THIS->menuBar->actions().at(i)]; + if (!menu) + continue; + if (!strcasecmp(menu->widget.name, name)) + return menu; + } + } + + parent = (CWIDGET *)CWIDGET_get_parent(THIS); + if (!parent) + break; + _object = CWidget::getWindow(parent); + if (!THIS) + break; + } + + return NULL; +} + diff --git a/gb.qt4/src/CWindow.h b/gb.qt4/src/CWindow.h new file mode 100644 index 00000000..0c0ed7c7 --- /dev/null +++ b/gb.qt4/src/CWindow.h @@ -0,0 +1,278 @@ +/*************************************************************************** + + CWindow.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWINDOW_H +#define __CWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gambas.h" +#include "CContainer.h" +#include "CMenu.h" +#include "CPicture.h" + +//#define DEBUG_WINDOW 1 + +typedef + struct CWINDOW { + CWIDGET widget; + MyContainer *container; + CARRANGEMENT arrangement; + QMenuBar *menuBar; + int ret; + CPICTURE *icon; + CPICTURE *picture; + CWIDGET *focus; + QPushButton *defaultButton; + QPushButton *cancelButton; + int loopLevel; + int x; + int y; + int w; + int h; + int minw; + int minh; + int last_resize_w; + int last_resize_h; + unsigned toplevel : 1; + unsigned embedded : 1; + unsigned xembed : 1; + unsigned stacking : 2; + unsigned skipTaskbar : 1; + unsigned masked : 1; + unsigned reallyMasked : 1; + unsigned opened : 1; + unsigned hidden : 1; + unsigned toolbar : 1; + unsigned scale : 1; + unsigned minsize : 1; + unsigned title : 1; + unsigned stateChange : 1; + unsigned closing : 1; + unsigned hideMenuBar : 1; + unsigned showMenuBar : 1; + unsigned sticky : 1; + unsigned noTakeFocus : 1; + unsigned moved : 1; + unsigned popup : 1; + } + CWINDOW; + +typedef + struct { + CWINDOW window; + } + CFORM; + + +#ifndef __CWINDOW_CPP + +extern GB_DESC CWindowDesc[]; +extern GB_DESC CWindowMenusDesc[]; +extern GB_DESC CWindowControlsDesc[]; +//extern GB_DESC CWindowTypeDesc[]; +extern GB_DESC CWindowsDesc[]; +extern GB_DESC CFormDesc[]; + +extern CWINDOW *CWINDOW_Main; +extern int CWINDOW_MainDesktop; +extern CWINDOW *CWINDOW_Current; +extern CWINDOW *CWINDOW_Active; +extern CWINDOW *CWINDOW_LastActive; +#ifndef QT5 +extern int CWINDOW_Embedder; +extern bool CWINDOW_Embedded; +#endif + +#else + +#define THIS ((CWINDOW *)_object) +#define WIDGET ((QWidget *)(((CWIDGET *)_object)->widget)) +#define WINDOW ((MyMainWindow *)WIDGET) + +#ifndef QT5 +#define XEMBED ((QX11EmbedWidget *)(WIDGET->parent())) +#endif + +#endif + +class CWindow : public QObject +{ + Q_OBJECT + +public: + + static QList list; + + static CWindow manager; + static int count; + + static void insertTopLevel(CWINDOW *_object); + static void removeTopLevel(CWINDOW *_object); + static CMENU *findMenu(CWINDOW *_object, const char *name); + +protected: + + bool eventFilter(QObject *, QEvent *); + +public slots: + + void error(void); + void embedded(void); + void closed(void); + void destroy(void); +}; + +class MyMainWindow; + +typedef + struct { + QPointer that; + QEventLoop *old; + CWINDOW *save; + } + MODAL_INFO; + +enum { PROP_ALL = -1, PROP_STACKING = 1, PROP_SKIP_TASKBAR = 2, PROP_BORDER = 4, PROP_STICKY = 8 }; + +class MyMainWindow : public QWidget +{ + Q_OBJECT + +private: + + QSizeGrip *sg; + QMenuBar *mb; + bool _activate; + bool _border; + bool _resizable; + bool _deleted; + bool _enterLoop; + bool _utility; + int _type; + Qt::WindowStates _state; + int _screen; + +protected: + + virtual void showEvent(QShowEvent *); + virtual void resizeEvent(QResizeEvent *); + virtual void moveEvent(QMoveEvent *); + virtual void keyPressEvent(QKeyEvent *); + virtual void closeEvent(QCloseEvent *); + virtual void changeEvent(QEvent *); + + //bool eventFilter(QObject *, QEvent *); + +public slots: + + void activateLater(); + +public: + + enum { BorderNone = 0, BorderFixed = 1, BorderResizable = 2 }; + QHash names; + void *_object; + + MyMainWindow(QWidget *parent, const char *name, bool embedded = false); + ~MyMainWindow(); + + virtual void setVisible(bool visible); + + void initProperties(int which); + void present(QWidget *parent = 0); + void showActivate(QWidget *parent = 0); + //void activateLater() { _activate = true; } + void showModal(); + void showPopup(QPoint &pos); + void setEventLoop(); + //bool isModal() { return testWFlags(WShowModal); } + void doReparent(QWidget *w, const QPoint &p); + + bool hasBorder(void) const { return _border; } + void setBorder(bool); + + bool isResizable(void) const { return _resizable; } + void setResizable(bool); + + #ifdef NO_X_WINDOW + #else + int getType(void); + void setType(int type); + #endif + + //bool getTool(void) { return testWFlags(WStyle_Tool); } + //void setTool(bool); + bool isUtility(void) const { return _utility; } + void setUtility(bool b); + + void setSizeGrip(bool); + void moveSizeGrip(); + + bool isPersistent(void); + void setPersistent(bool); + + void center(); + void configure(); + + void setName(const char *, CWIDGET *); + + void setState(Qt::WindowStates state); + Qt::WindowStates getState() const; + + void setBetterMask(QPixmap &bg); + + int currentScreen() const; + + virtual void resize(int w, int h); + virtual void setGeometry(int x, int y, int w, int h); + + friend void on_error_show_modal(MODAL_INFO *info); +}; + + +//void CWINDOW_set_top_only(QWidget *w, bool top); +void CWINDOW_activate(CWIDGET *ob); +void CWINDOW_set_default_button(CWINDOW *win, QPushButton *button, bool on); +void CWINDOW_set_cancel_button(CWINDOW *win, QPushButton *button, bool on); +void CWINDOW_define_mask(CWINDOW *_object); +void CWINDOW_ensure_active_window(); +bool CWINDOW_must_quit(); +bool CWINDOW_close_all(bool main); +void CWINDOW_delete_all(bool main); +//void CWINDOW_fix_menubar(CWINDOW *window); + +#endif diff --git a/gb.qt4/src/Makefile.am b/gb.qt4/src/Makefile.am new file mode 100644 index 00000000..a0fbe3ee --- /dev/null +++ b/gb.qt4/src/Makefile.am @@ -0,0 +1,61 @@ +COMPONENT = gb.qt4 +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +SUBDIRS = . @QTEXT_DIR@ @QTWEBKIT_DIR@ @QTOPENGL_DIR@ +EXTRA_DIST = trayicon.xpm + +gblib_LTLIBRARIES = gb.qt4.la + +gb_qt4_la_LIBADD = @THREAD_LIB@ @QT_LIB@ +gb_qt4_la_LDFLAGS = -module @LD_FLAGS@ @QT_LDFLAGS@ +gb_qt4_la_CXXFLAGS = @THREAD_INC@ -DGB_QT_COMPONENT $(AM_CXXFLAGS) +gb_qt4_la_CPPFLAGS = @QT_INC@ -I$(top_srcdir)/share/ + +gb_qt4_la_SOURCES = \ + x11.h x11.c \ + desktop.h desktop.c \ + gb.qt.h main.h main_moc.cpp main.cpp \ + CFont.h CFont.cpp \ + CScreen.h CScreen.cpp \ + CStyle.h CStyle.cpp \ + CWidget.h CWidget_moc.cpp CWidget.cpp \ + CWindow.h CWindow_moc.cpp CWindow.cpp \ + CButton.h CButton_moc.cpp CButton.cpp \ + CContainer.h CContainer_moc.cpp CContainer.cpp \ + CLabel.h CLabel_moc.cpp CLabel.cpp \ + CTextBox.h CTextBox_moc.cpp CTextBox.cpp \ + CMenu.h CMenu_moc.cpp CMenu.cpp \ + CMouse.h CMouse_moc.cpp CMouse.cpp \ + CKey.h CKey_moc.cpp CKey.cpp \ + CColor.h CColor_moc.cpp CColor.cpp \ + CConst.h CConst.cpp \ + CCheckBox.h CCheckBox_moc.cpp CCheckBox.cpp \ + CFrame.h CFrame_moc.cpp CFrame.cpp \ + CPanel.h CPanel_moc.cpp CPanel.cpp \ + CRadioButton.h CRadioButton_moc.cpp CRadioButton.cpp \ + CTextArea.h CTextArea_moc.cpp CTextArea.cpp \ + CTabStrip.h CTabStrip_moc.cpp CTabStrip.cpp \ + CDialog.h CDialog_moc.cpp CDialog.cpp \ + CPicture.h CPicture_moc.cpp CPicture.cpp \ + CImage.h CImage_moc.cpp CImage.cpp \ + CClipboard.h CClipboard_moc.cpp CClipboard.cpp \ + CDraw.h CDraw.cpp \ + cpaint_impl.h cpaint_impl.cpp \ + CWatch.h CWatch_moc.cpp CWatch.cpp \ + CDrawingArea.h CDrawingArea_moc.cpp CDrawingArea.cpp \ + CMessage.h CMessage_moc.cpp CMessage.cpp \ + CSlider.h CSlider_moc.cpp CSlider.cpp \ + CScrollBar.h CScrollBar_moc.cpp CScrollBar.cpp \ + CMovieBox.h CMovieBox_moc.cpp CMovieBox.cpp \ + ctrayicon.h ctrayicon_moc.cpp ctrayicon.cpp \ + CWatcher.h CWatcher_moc.cpp CWatcher.cpp \ + cprinter.h cprinter_moc.cpp cprinter.cpp \ + csvgimage.h csvgimage_moc.cpp csvgimage.cpp \ + fix_breeze.h fix_breeze.cpp \ + CEmbedder.h CEmbedder.cpp + +if XWINDOW +gb_qt4_la_SOURCES += CEmbedder_moc.cpp +endif + diff --git a/gb.qt4/src/cpaint_impl.cpp b/gb.qt4/src/cpaint_impl.cpp new file mode 100644 index 00000000..e030a329 --- /dev/null +++ b/gb.qt4/src/cpaint_impl.cpp @@ -0,0 +1,1584 @@ +/*************************************************************************** + + cpaint_impl.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPAINT_IMPL_CPP + +#ifdef OS_SOLARIS +/* Make math.h define M_PI and a few other things */ +#define __EXTENSIONS__ +/* Get definition for finite() */ +#include +#endif +#include + +#include +#include +#include +#include +#include + +#ifndef NO_X_WINDOW +#include +#endif + +#include "gambas.h" + +#include "CConst.h" +#include "CFont.h" +#include "CWidget.h" +#include "CWindow.h" +#include "CPicture.h" +#include "CImage.h" +#include "CDrawingArea.h" +#include "CColor.h" +#include "CDraw.h" +#include "cprinter.h" +#include "csvgimage.h" +#include "cpaint_impl.h" + +/*class ClipInfo +{ +public: + QPainterPath *path; + GB_RECT *rect; + + ClipInfo() { path = NULL; rect = NULL; } + ~ClipInfo() { delete path; delete rect; } +};*/ + +typedef + struct { + QPainter *painter; + QPainterPath *path; + int fillRule; + QTransform *init; + float bx, by; + //QPainterPath *clip; + //GB_RECT *clipRect; + //QList *clipStack; + } + QT_PAINT_EXTRA; + +#define EXTRA(d) ((QT_PAINT_EXTRA *)d->extra) + +#define COLOR_TO_INT(color) ((color).rgba() ^ 0xFF000000) +#define MASK_COLOR(col) ((col & 0xFF000000) ? Qt::color0 : Qt::color1) + +#define PAINTER(d) EXTRA(d)->painter +#define PATH(d) EXTRA(d)->path +//#define CLIP(d) EXTRA(d)->clip + +static inline qreal to_deg(float angle) +{ + return (qreal)(angle * 180 / M_PI); +} + +static bool init_painting(GB_PAINT *d, QPaintDevice *device) +{ + QPen pen; + + d->area.width = device->width(); + d->area.height = device->height(); + d->resolutionX = device->physicalDpiX(); + d->resolutionY = device->physicalDpiY(); + + if (!PAINTER(d)) + { + if (device->paintingActive()) + { + GB.Error("Device already being painted"); + return TRUE; + } + + EXTRA(d)->painter = new QPainter(device); + } + +#ifndef QT5 + MyPaintEngine *engine = (MyPaintEngine *)device->paintEngine(); + engine->patchFeatures(); +#endif + + //EXTRA(d)->path = NULL; + //EXTRA(d)->clip = NULL; + //EXTRA(d)->clipRect = NULL; + + EXTRA(d)->init = new QTransform(); + *(EXTRA(d)->init) = PAINTER(d)->worldTransform(); + + PAINTER(d)->setRenderHints(QPainter::Antialiasing, true); + PAINTER(d)->setRenderHints(QPainter::TextAntialiasing, true); + PAINTER(d)->setRenderHints(QPainter::SmoothPixmapTransform, true); + + pen = PAINTER(d)->pen(); + pen.setCapStyle(Qt::FlatCap); + pen.setJoinStyle(Qt::MiterJoin); + pen.setMiterLimit(10.0); + pen.setWidthF(1.0); + PAINTER(d)->setPen(pen); + PAINTER(d)->setBrush(Qt::black); + + return FALSE; +} + +static QColor get_color(GB_PAINT *d, GB_COLOR col) +{ + if (col == GB_COLOR_DEFAULT) + { + if (GB.Is(d->device, CLASS_Control)) + col = CWIDGET_get_real_background((CWIDGET *)d->device); + else + col = 0xFFFFFF; + } + + return CCOLOR_make(col); +} + +/*static void begin_clipping(GB_PAINT *d) +{ + if (CLIP(d)) + { + GB_RECT *r; + QTransform save = PAINTER(d)->worldTransform(); + PAINTER(d)->resetTransform(); + r = EXTRA(d)->clipRect; + if (r) + PAINTER(d)->setClipRect(r->x, r->y, r->w, r->h); + else + PAINTER(d)->setClipPath(*CLIP(d)); + PAINTER(d)->setWorldTransform(save); + } +} + +static void end_clipping(GB_PAINT *d) +{ + if (CLIP(d)) + PAINTER(d)->setClipping(false); +}*/ + +#define begin_clipping(_d) +#define end_clipping(_d) + +//--------------------------------------------------------------------------- + +static int Begin(GB_PAINT *d) +{ + void *device = d->device; + QPaintDevice *target = NULL; + + if (GB.Is(device, CLASS_Picture)) + { + QPixmap *pixmap = ((CPICTURE *)device)->pixmap; + + if (pixmap->isNull()) + { + GB.Error("Bad picture"); + return TRUE; + } + + target = pixmap; + } + else if (GB.Is(device, CLASS_Image)) + { + QImage *image = CIMAGE_get((CIMAGE *)device); + + if (image->isNull()) + { + GB.Error("Bad image"); + return TRUE; + } + + target = image; + } + else if (GB.Is(device, CLASS_DrawingArea)) + { + MyDrawingArea *wid; + + wid = (MyDrawingArea *)(((CWIDGET *)device)->widget); + + if (wid->isCached()) + target = wid->getBackgroundPixmap(); + else if (wid->cache) + target = wid->cache; + else + { + if (!wid->inDrawEvent()) + { + GB.Error("Cannot paint outside of Draw event handler"); + return TRUE; + } + + target = wid; + } + + wid->drawn++; + + if (init_painting(d, target)) + return TRUE; + + if (wid->isCached()) + PAINTER(d)->initFrom(wid); + + d->area.width = wid->width(); + d->area.height = wid->height(); + return FALSE; + } + else if (GB.Is(device, CLASS_Printer)) + { + CPRINTER *printer = (CPRINTER *)device; + + if (!printer->printing) + { + GB.Error("Printer is not printing"); + return TRUE; + } + + target = printer->printer; + } + else if (GB.Is(device, CLASS_SvgImage)) + { + CSVGIMAGE *svgimage = (CSVGIMAGE *)device; + target = SVGIMAGE_begin(svgimage, &EXTRA(d)->painter); + if (!target) + { + GB.Error("SvgImage size is not defined"); + return TRUE; + } + } + + return init_painting(d, target); +} + +static void End(GB_PAINT *d) +{ + void *device = d->device; + QT_PAINT_EXTRA *dx = EXTRA(d); + + if (GB.Is(device, CLASS_DrawingArea)) + { + MyDrawingArea *wid; + + wid = (MyDrawingArea *)(((CWIDGET *)device)->widget); + + if (wid) + { + if (wid->isCached()) + wid->refreshBackground(); + + wid->drawn--; + } + } + else if (GB.Is(device, CLASS_SvgImage)) + { + PAINTER(d)->end(); // ?? + } + + /*if (dx->clipStack) + { + while (!dx->clipStack->isEmpty()) + delete dx->clipStack->takeLast(); + delete dx->clipStack; + }*/ + + delete dx->init; + delete dx->path; + //delete dx->clip; + delete dx->painter; +} + +static void Save(GB_PAINT *d) +{ + //QT_PAINT_EXTRA *dx = EXTRA(d); + //ClipInfo *ci; + + PAINTER(d)->save(); + + /*if (!dx->clipStack) + dx->clipStack = new QList; + + ci = new ClipInfo; + if (dx->clip) + ci->path = new QPainterPath(*dx->clip); + if (dx->clipRect) + { + ci->rect = new GB_RECT; + *ci->rect = *dx->clipRect; + } + + dx->clipStack->append(ci);*/ +} + +static void Restore(GB_PAINT *d) +{ + //QT_PAINT_EXTRA *dx = EXTRA(d); + + PAINTER(d)->restore(); + + /*if (dx->clipStack && !dx->clipStack->isEmpty()) + { + ClipInfo *ci = dx->clipStack->takeLast(); + + delete dx->clip; + dx->clip = ci->path ? new QPainterPath(*(ci->path)) : NULL; + + delete dx->clipRect; + if (ci->rect) + { + dx->clipRect = new GB_RECT; + *dx->clipRect = *ci->rect; + } + else + dx->clipRect = NULL; + + delete ci; + }*/ +} + +static void Antialias(GB_PAINT *d, int set, int *antialias) +{ + if (set) + PAINTER(d)->setRenderHint(QPainter::Antialiasing, *antialias); + else + *antialias = PAINTER(d)->testRenderHint(QPainter::Antialiasing) ? 1 : 0; +} + +static void apply_font(QFont &font, void *object = 0) +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + + PAINTER(d)->setFont(font); +} + +static void Font(GB_PAINT *d, int set, GB_FONT *font) +{ + if (set) + { + QFont f; + + if (*font) + f = QFont(*((CFONT *)(*font))->font); + else if ((GB.Is(d->device, CLASS_DrawingArea))) + f = (((CWIDGET *)d->device)->widget)->font(); + PAINTER(d)->setFont(f); + + // Strange bug of QT. Sometimes the font does not apply (cf. DrawTextShadow) + if (f != PAINTER(d)->font()) + { + f.fromString(f.toString()); + PAINTER(d)->setFont(f); + } + } + else + *font = CFONT_create(PAINTER(d)->font(), apply_font); +} + +static void init_path(GB_PAINT *d) +{ + switch (EXTRA(d)->fillRule) + { + case GB_PAINT_FILL_RULE_WINDING: + PATH(d)->setFillRule(Qt::WindingFill); + break; + case GB_PAINT_FILL_RULE_EVEN_ODD: + default: + PATH(d)->setFillRule(Qt::OddEvenFill); + } +} + +#define CHECK_PATH(_d) \ + if (!PATH(_d)) \ + return; \ + else \ + init_path(_d); + +#define PRESERVE_PATH(_d, _p) \ + if (!(_p)) \ + { \ + delete PATH(_d); \ + EXTRA(_d)->path = 0; \ + } + +#define CREATE_PATH(_d) \ + if (!PATH(_d)) \ + EXTRA(_d)->path = new QPainterPath(); + +/*static void delete_clip_rect(GB_PAINT *d) +{ + if (EXTRA(d)->clipRect) + { + delete EXTRA(d)->clipRect; + EXTRA(d)->clipRect = NULL; + } +}*/ + +static void Clip(GB_PAINT *d, int preserve) +{ + CHECK_PATH(d); + + PAINTER(d)->setClipPath(*PATH(d), PAINTER(d)->hasClipping() ? Qt::IntersectClip : Qt::ReplaceClip); + + /*QPainterPath path = PAINTER(d)->worldTransform().map(*PATH(d)); + + if (CLIP(d)) + path = CLIP(d)->intersected(path); + + delete EXTRA(d)->clip; + EXTRA(d)->clip = new QPainterPath(path); + delete_clip_rect(d);*/ + + PRESERVE_PATH(d, preserve); +} + +static void ResetClip(GB_PAINT *d) +{ + /*delete CLIP(d); + EXTRA(d)->clip = 0; + delete_clip_rect(d);*/ + PAINTER(d)->setClipping(false); +} + +static void get_path_extents(QPainterPath *path, GB_EXTENTS *ext, const QTransform &transform) +{ + if (!path) + { + ext->x1 = ext->x2 = ext->y1 = ext->y2 = 0.0; + return; + } + + QRectF rect = transform.inverted().mapRect(path->boundingRect()); + + ext->x1 = (float)rect.left(); + ext->y1 = (float)rect.top(); + ext->x2 = (float)rect.right(); + ext->y2 = (float)rect.bottom(); +} + +static void ClipExtents(GB_PAINT *d, GB_EXTENTS *ext) +{ + /*GB_RECT *rect = EXTRA(d)->clipRect; + if (rect) + { + ext->x1 = (float)rect->x; + ext->y1 = (float)rect->y; + ext->x2 = (float)(rect->x + rect->w); + ext->y2 = (float)(rect->y + rect->h); + } + else*/ + QPainterPath path = PAINTER(d)->clipPath(); + get_path_extents(&path, ext, QTransform()); //PAINTER(d)->transform()); +} + +static void Fill(GB_PAINT *d, int preserve) +{ + CHECK_PATH(d); + + //if (!CLIP(d)) + PAINTER(d)->fillPath(*PATH(d), PAINTER(d)->brush()); + /*else + { + QPainterPath path = PAINTER(d)->worldTransform().inverted().map(*CLIP(d)); + path = path.intersected(*PATH(d)); + PAINTER(d)->fillPath(path, PAINTER(d)->brush()); + }*/ + + PRESERVE_PATH(d, preserve); +} + +static void Stroke(GB_PAINT *d, int preserve) +{ + CHECK_PATH(d); + + if (PAINTER(d)->pen().widthF() > 0.0) + { + //if (!CLIP(d)) + PAINTER(d)->strokePath(*PATH(d), PAINTER(d)->pen()); + /*else + { + QPainterPathStroker stroker; + QPen pen = PAINTER(d)->pen(); + + stroker.setCapStyle(pen.capStyle()); + stroker.setDashOffset(pen.dashOffset()); + stroker.setDashPattern(pen.dashPattern()); + stroker.setJoinStyle(pen.joinStyle()); + stroker.setMiterLimit(pen.miterLimit()); + stroker.setWidth(pen.widthF()); + + QPainterPath path = PAINTER(d)->worldTransform().inverted().map(*CLIP(d)); + path = path.intersected(stroker.createStroke(*PATH(d))); + PAINTER(d)->fillPath(path, PAINTER(d)->brush()); + }*/ + } + + PRESERVE_PATH(d, preserve); +} + +static void PathExtents(GB_PAINT *d, GB_EXTENTS *ext) +{ + get_path_extents(PATH(d), ext, PAINTER(d)->transform()); +} + +static int PathContains(GB_PAINT *d, float x, float y) +{ + if (!PATH(d)) + return FALSE; + + QPointF point((qreal)x, (qreal)y); + return PATH(d)->contains(point); +} + +static void PathOutline(GB_PAINT *d, GB_PAINT_OUTLINE_CB cb) +{ + QPainterPath *p = PATH(d); + int i, j; + + if (!p) + return; + + QList qoutline = p->toSubpathPolygons(QTransform()); + + for (i = 0; i < qoutline.count(); i++) + { + QPolygonF qpolygon = qoutline.at(i); + + for (j = 0; j < qpolygon.count(); j++) + { + QPointF qpoint = qpolygon.at(j); + (*cb)(j == 0 ? GB_PAINT_PATH_MOVE : GB_PAINT_PATH_LINE, qpoint.x(), qpoint.y()); + } + } +} + +#define DASH_ZERO 0.0009765625 + +static void Dash(GB_PAINT *d, int set, float **dashes, int *count) +{ + QPen pen = PAINTER(d)->pen(); + + if (set) + { + if (!*count) + pen.setStyle(Qt::SolidLine); + else + { + QVector dv; + qreal d = 0; + + for (int i = 0; i < *count; i++) + { + d = (*dashes)[i]; + if (d == 0.0) + d = DASH_ZERO; + dv << (qreal)d; + } + + if (*count == 1) + dv << (qreal)d; + + pen.setStyle(Qt::CustomDashLine); + pen.setDashPattern(dv); + } + PAINTER(d)->setPen(pen); + } + else + { + if (pen.style() == Qt::CustomDashLine) + { + QVector dv = pen.dashPattern(); + float d; + *count = dv.count(); + GB.Alloc(POINTER(dashes), sizeof(float) * *count); + for (int i = 0; i < *count; i++) + { + d = (float)dv[i]; + if (d <= DASH_ZERO) + d = 0.0; + (*dashes)[i] = d; + } + } + else + { + *count = 0; + *dashes = NULL; + } + } +} + +static void DashOffset(GB_PAINT *d, int set, float *offset) +{ + QPen pen = PAINTER(d)->pen(); + + if (set) + { + pen.setDashOffset((qreal)*offset); + PAINTER(d)->setPen(pen); + } + else + { + *offset = (float)pen.dashOffset(); + } +} + + +static void FillRule(GB_PAINT *d, int set, int *value) +{ + if (set) + { + EXTRA(d)->fillRule = *value; + } + else + *value = EXTRA(d)->fillRule; +} + +static void FillStyle(GB_PAINT *d, int set, int *style) +{ + /*if (set) + { + EXTRA(d)->fillRule = *value; + } + else + *value = EXTRA(d)->fillRule;*/ +} + +static void LineCap(GB_PAINT *d, int set, int *value) +{ + QPen pen = PAINTER(d)->pen(); + + if (set) + { + switch (*value) + { + case GB_PAINT_LINE_CAP_ROUND: + pen.setCapStyle(Qt::RoundCap); break; + case GB_PAINT_LINE_CAP_SQUARE: + pen.setCapStyle(Qt::SquareCap); break; + case GB_PAINT_LINE_CAP_BUTT: + default: + pen.setCapStyle(Qt::FlatCap); + } + PAINTER(d)->setPen(pen); + } + else + { + switch (pen.capStyle()) + { + case Qt::RoundCap: *value = GB_PAINT_LINE_CAP_ROUND; break; + case Qt::SquareCap: *value = GB_PAINT_LINE_CAP_SQUARE; break; + case Qt::FlatCap: default: *value = GB_PAINT_LINE_CAP_BUTT; + } + } +} + +static void LineJoin(GB_PAINT *d, int set, int *value) +{ + QPen pen = PAINTER(d)->pen(); + + if (set) + { + switch (*value) + { + case GB_PAINT_LINE_JOIN_ROUND: + pen.setJoinStyle(Qt::RoundJoin); break; + case GB_PAINT_LINE_JOIN_BEVEL: + pen.setJoinStyle(Qt::BevelJoin); break; + case GB_PAINT_LINE_JOIN_MITER: + default: + pen.setJoinStyle(Qt::MiterJoin); + } + PAINTER(d)->setPen(pen); + } + else + { + switch (pen.joinStyle()) + { + case Qt::RoundJoin: *value = GB_PAINT_LINE_JOIN_ROUND; break; + case Qt::BevelJoin: *value = GB_PAINT_LINE_JOIN_BEVEL; break; + case Qt::MiterJoin: default: *value = GB_PAINT_LINE_JOIN_MITER; + } + } +} + +static void LineWidth(GB_PAINT *d, int set, float *value) +{ + QPen pen = PAINTER(d)->pen(); + if (set) + { + pen.setWidthF((qreal)*value); + PAINTER(d)->setPen(pen); + } + else + *value = (float)pen.widthF(); +} + +static void MiterLimit(GB_PAINT *d, int set, float *value) +{ + QPen pen = PAINTER(d)->pen(); + if (set) + { + pen.setMiterLimit((qreal)*value); + PAINTER(d)->setPen(pen); + } + else + *value = (float)pen.miterLimit(); +} + + +static void Operator(GB_PAINT *d, int set, int *value) +{ + QPainter::CompositionMode mode; + + if (set) + { + switch (*value) + { + case GB_PAINT_OPERATOR_CLEAR: mode = QPainter::CompositionMode_Clear; break; + case GB_PAINT_OPERATOR_SOURCE: mode = QPainter::CompositionMode_Source; break; + case GB_PAINT_OPERATOR_IN: mode = QPainter::CompositionMode_SourceIn; break; + case GB_PAINT_OPERATOR_OUT: mode = QPainter::CompositionMode_SourceOut; break; + case GB_PAINT_OPERATOR_ATOP: mode = QPainter::CompositionMode_SourceAtop; break; + case GB_PAINT_OPERATOR_DEST: mode = QPainter::CompositionMode_Destination; break; + case GB_PAINT_OPERATOR_DEST_OVER: mode = QPainter::CompositionMode_DestinationOver; break; + case GB_PAINT_OPERATOR_DEST_IN: mode = QPainter::CompositionMode_DestinationIn; break; + case GB_PAINT_OPERATOR_DEST_OUT: mode = QPainter::CompositionMode_DestinationOut; break; + case GB_PAINT_OPERATOR_DEST_ATOP: mode = QPainter::CompositionMode_DestinationAtop; break; + case GB_PAINT_OPERATOR_XOR: mode = QPainter::CompositionMode_Xor; break; + case GB_PAINT_OPERATOR_ADD: mode = QPainter::CompositionMode_Plus; break; + case GB_PAINT_OPERATOR_SATURATE: mode = QPainter::CompositionMode_Multiply; break; + case GB_PAINT_OPERATOR_OVER: default: mode = QPainter::CompositionMode_SourceOver; + } + PAINTER(d)->setCompositionMode(mode); + } + else + { + switch (PAINTER(d)->compositionMode()) + { + case QPainter::CompositionMode_Clear: *value = GB_PAINT_OPERATOR_CLEAR; break; + case QPainter::CompositionMode_Source: *value = GB_PAINT_OPERATOR_SOURCE; break; + case QPainter::CompositionMode_SourceIn: *value = GB_PAINT_OPERATOR_IN; break; + case QPainter::CompositionMode_SourceOut: *value = GB_PAINT_OPERATOR_OUT; break; + case QPainter::CompositionMode_SourceAtop: *value = GB_PAINT_OPERATOR_ATOP; break; + case QPainter::CompositionMode_Destination: *value = GB_PAINT_OPERATOR_DEST; break; + case QPainter::CompositionMode_DestinationOver: *value = GB_PAINT_OPERATOR_DEST_OVER; break; + case QPainter::CompositionMode_DestinationIn: *value = GB_PAINT_OPERATOR_DEST_IN; break; + case QPainter::CompositionMode_DestinationOut: *value = GB_PAINT_OPERATOR_DEST_OUT; break; + case QPainter::CompositionMode_DestinationAtop: *value = GB_PAINT_OPERATOR_DEST_ATOP; break; + case QPainter::CompositionMode_Xor: *value = GB_PAINT_OPERATOR_XOR; break; + case QPainter::CompositionMode_Plus: *value = GB_PAINT_OPERATOR_ADD; break; + case QPainter::CompositionMode_Multiply: *value = GB_PAINT_OPERATOR_SATURATE; break; + case QPainter::CompositionMode_SourceOver: default: *value = GB_PAINT_OPERATOR_OVER; + } + } +} + + +static void NewPath(GB_PAINT *d) +{ + PRESERVE_PATH(d, FALSE); +} + +static void ClosePath(GB_PAINT *d) +{ + CHECK_PATH(d); + PATH(d)->closeSubpath(); +} + + +static void Arc(GB_PAINT *d, float xc, float yc, float radius, float angle, float length, bool pie) +{ + CREATE_PATH(d); + + QRectF rect; + rect.setCoords((qreal)(xc - radius), (qreal)(yc - radius), (qreal)(xc + radius), (qreal)(yc + radius)); + + angle = - angle; + length = - length; + + if (pie) + PATH(d)->moveTo(xc, yc); + else + PATH(d)->arcMoveTo(rect, to_deg(angle)); + + PATH(d)->arcTo(rect, to_deg(angle), to_deg(length)); + + if (pie) + PATH(d)->closeSubpath(); +} + +static void Ellipse(GB_PAINT *d, float x, float y, float width, float height, float angle, float length, bool pie) +{ + CREATE_PATH(d); + + QRectF rect; + rect.setCoords((qreal)x, (qreal)y, (qreal)x + width, (qreal)y + height); + + angle = - angle; + length = - length; + + if (pie) + PATH(d)->moveTo(x + width / 2, y + height / 2); + else + PATH(d)->arcMoveTo(rect, to_deg(angle)); + + PATH(d)->arcTo(rect, to_deg(angle), to_deg(length)); + if (pie) + //PATH(d)->lineTo(x + width / 2, y + height / 2); + PATH(d)->closeSubpath(); +} + +static void Rectangle(GB_PAINT *d, float x, float y, float width, float height) +{ + CREATE_PATH(d); + PATH(d)->addRect((qreal)x, (qreal)y, (qreal)width, (qreal)height); +} + +static void ClipRect(GB_PAINT *d, int x, int y, int w, int h) +{ + //GB_RECT *rect; + ResetClip(d); + Rectangle(d, x, y, w, h); + Clip(d, FALSE); + + /*rect = new GB_RECT; + rect->x = x; + rect->y = y; + rect->w = w; + rect->h = h; + EXTRA(d)->clipRect = rect;*/ +} + +static void GetCurrentPoint(GB_PAINT *d, float *x, float *y) +{ + if (!PATH(d)) + { + *x = 0; + *y = 0; + return; + } + + QPointF pt = PATH(d)->currentPosition(); + *x = (float)pt.x(); + *y = (float)pt.y(); +} + +static void MoveTo(GB_PAINT *d, float x, float y) +{ + CREATE_PATH(d); + PATH(d)->moveTo((qreal)x, (qreal)y); +} + +static void LineTo(GB_PAINT *d, float x, float y) +{ + CREATE_PATH(d); + PATH(d)->lineTo((qreal)x, (qreal)y); +} + +static void CurveTo(GB_PAINT *d, float x1, float y1, float x2, float y2, float x3, float y3) +{ + CREATE_PATH(d); + PATH(d)->cubicTo(QPointF((qreal)x1, (qreal)y1), QPointF((qreal)x2, (qreal)y2), QPointF((qreal)x3, (qreal)y3)); +} + +static QStringList text_sl; +static QVector text_w; +static int text_line; + +static int get_text_width(QPainter *dp, QString &s) +{ + int w, width = 0; + int i; + + text_sl = s.split('\n', QString::KeepEmptyParts); + + text_w.resize(text_sl.count()); + + for (i = 0; i < (int)text_sl.count(); i++) + { + w = dp->fontMetrics().width(text_sl[i]); + if (w > width) width = w; + text_w[i] = w; + } + + return width; +} + +static int get_text_height(QPainter *dp, QString &s) +{ + text_line = dp->fontMetrics().height(); + return text_line * (1 + s.count('\n')); +} + +static QPainterPath *_draw_path; +static float _draw_x, _draw_y; + +static void draw_text(GB_PAINT *d, bool rich, const char *text, int len, float w, float h, int align, bool draw) +{ + QPointF pos; + + GetCurrentPoint(d, &_draw_x, &_draw_y); + + if (w < 0 && h < 0) + _draw_y -= PAINTER(d)->fontMetrics().ascent(); + + if (draw) + { + begin_clipping(d); + + if (rich) + DRAW_rich_text(PAINTER(d), QString::fromUtf8(text, len), _draw_x, _draw_y, w, h, CCONST_alignment(align, ALIGN_TOP_NORMAL, true)); + else + DRAW_text(PAINTER(d), QString::fromUtf8(text, len), _draw_x, _draw_y, w, h, CCONST_alignment(align, ALIGN_TOP_NORMAL, true)); + + end_clipping(d); + } + else + { + CREATE_PATH(d); + + _draw_path = PATH(d); + + MyPaintDevice device; + QPainter p(&device); + + p.setFont(PAINTER(d)->font()); + p.setPen(PAINTER(d)->pen()); + p.setBrush(PAINTER(d)->brush()); + + if (rich) + DRAW_rich_text(&p, QString::fromUtf8(text, len), 0, 0, w, h, CCONST_alignment(align, ALIGN_TOP_NORMAL, true)); + else + DRAW_text(&p, QString::fromUtf8(text, len), 0, 0, w, h, CCONST_alignment(align, ALIGN_TOP_NORMAL, true)); + + p.end(); + _draw_path = NULL; + } +} + +static void Text(GB_PAINT *d, const char *text, int len, float w, float h, int align, bool draw) +{ + draw_text(d, false, text, len, w, h, align, draw); +} + +static void RichText(GB_PAINT *d, const char *text, int len, float w, float h, int align, bool draw) +{ + draw_text(d, true, text, len, w, h, align, draw); +} + +static void get_text_extents(GB_PAINT *d, bool rich, const char *text, int len, GB_EXTENTS *ext, float width) +{ + QPainterPath path; + MyPaintDevice device; + QPainter p(&device); + + p.setFont(PAINTER(d)->font()); + _draw_path = &path; + GetCurrentPoint(d, &_draw_x, &_draw_y); + _draw_y -= PAINTER(d)->fontMetrics().ascent(); + + if (rich) + DRAW_rich_text(&p, QString::fromUtf8(text, len), 0, 0, width, -1, CCONST_alignment(ALIGN_TOP_NORMAL, ALIGN_TOP_NORMAL, true)); + else + DRAW_text(&p, QString::fromUtf8(text, len), 0, 0, -1, -1, CCONST_alignment(ALIGN_TOP_NORMAL, ALIGN_TOP_NORMAL, true)); + + p.end(); + + get_path_extents(&path, ext, QTransform()); + _draw_path = NULL; +} + +static void TextExtents(GB_PAINT *d, const char *text, int len, GB_EXTENTS *ext) +{ + get_text_extents(d, false, text, len, ext, -1); +} + +static void RichTextExtents(GB_PAINT *d, const char *text, int len, GB_EXTENTS *ext, float width) +{ + get_text_extents(d, true, text, len, ext, width); +} + +static void TextSize(GB_PAINT *d, const char *text, int len, float *w, float *h) +{ + QString s = QString::fromUtf8((const char *)text, len); + *w = get_text_width(PAINTER(d), s); + *h = get_text_height(PAINTER(d), s); +} + +static void RichTextSize(GB_PAINT *d, const char *text, int len, float sw, float *w, float *h) +{ + QTextDocument rt; + + rt.setDocumentMargin(0); + rt.setHtml(QString::fromUtf8((const char *)text, len)); + rt.setDefaultFont(PAINTER(d)->font()); + + if (sw > 0) + rt.setTextWidth(sw); + + *w = rt.idealWidth(); + *h = rt.size().height(); +} + +static void Matrix(GB_PAINT *d, int set, GB_TRANSFORM matrix) +{ + QTransform *t = (QTransform *)matrix; + + if (set) + { + if (t) + PAINTER(d)->setWorldTransform(*t); + else + PAINTER(d)->setWorldTransform(*EXTRA(d)->init); + } + else + *t = PAINTER(d)->worldTransform(); +} + + +static void SetBrush(GB_PAINT *d, GB_BRUSH brush) +{ + QBrush *b = (QBrush *)brush; + PAINTER(d)->setBrush(*b); + + QPen p = PAINTER(d)->pen(); + p.setBrush(*b); + PAINTER(d)->setPen(p); + //PAINTER(d)->setBrushOrigin(QPointF((qreal)x, (qreal)y)); +} + +static void BrushOrigin(GB_PAINT *d, int set, float *x, float *y) +{ + if (set) + { + EXTRA(d)->bx = *x; + EXTRA(d)->by = *y; + PAINTER(d)->setBrushOrigin(*x, *y); + } + else + { + *x = EXTRA(d)->bx; + *y = EXTRA(d)->by; + } +} + +static void Background(GB_PAINT *d, int set, GB_COLOR *color) +{ + if (set) + { + QBrush b(get_color(d, *color)); + SetBrush(d, (GB_BRUSH)&b); + } + else + { + *color = COLOR_TO_INT(PAINTER(d)->brush().color()); + } +} + + +static void Invert(GB_PAINT *d, int set, int *invert) +{ + if (set) + { + #if QT_VERSION >= QT_VERSION_CHECK(4, 5, 0) + PAINTER(d)->setCompositionMode(*invert ? QPainter::RasterOp_SourceXorDestination : QPainter::CompositionMode_SourceOver); + #else + fprintf(stderr, "gb.qt4: warning: Draw.Invert needs at least Qt 4.5\n"); + #endif + } + else + { + #if QT_VERSION >= QT_VERSION_CHECK(4, 5, 0) + *invert = PAINTER(d)->compositionMode() == QPainter::RasterOp_SourceXorDestination; //QPainter::CompositionMode_Xor; + #else + *invert = FALSE; + #endif + } +} + +static void DrawImage(GB_PAINT *d, GB_IMAGE image, float x, float y, float w, float h, float opacity, GB_RECT *source) +{ + QImage *img = CIMAGE_get((CIMAGE *)image); + QRectF rect(x, y, w, h); + + begin_clipping(d); + + PAINTER(d)->setOpacity(opacity); + + if (source) + { + bool smooth = PAINTER(d)->testRenderHint(QPainter::SmoothPixmapTransform); + + if (w >= source->w && h >= source->h && w == (int)w && h == (int)h && ((int)w % source->w) == 0 && ((int)h % source->h) == 0) + PAINTER(d)->setRenderHint(QPainter::SmoothPixmapTransform, false); + + QRectF srect(source->x, source->y, source->w, source->h); + PAINTER(d)->drawImage(rect, *img, srect); + + PAINTER(d)->setRenderHint(QPainter::SmoothPixmapTransform, smooth); + } + else + PAINTER(d)->drawImage(rect, *img); + + PAINTER(d)->setOpacity(1.0); + + end_clipping(d); +} + +static void DrawPicture(GB_PAINT *d, GB_PICTURE picture, float x, float y, float w, float h, GB_RECT *source) +{ + QPixmap *pix = ((CPICTURE *)picture)->pixmap; + QRectF rect(x, y, w, h); + QRectF srect; + + if (source) + srect = QRectF(source->x, source->y, source->w, source->h); + else + srect = QRectF(0, 0, pix->width(), pix->height()); + + begin_clipping(d); + + PAINTER(d)->drawPixmap(rect, *pix, srect); + + end_clipping(d); +} + +static void GetPictureInfo(GB_PAINT *d, GB_PICTURE picture, GB_PICTURE_INFO *info) +{ + QPixmap *p = ((CPICTURE *)picture)->pixmap; + + info->width = p->width(); + info->height = p->height(); +} + +static void FillRect(GB_PAINT *d, float x, float y, float w, float h, GB_COLOR color) +{ + begin_clipping(d); + PAINTER(d)->fillRect(x, y, w, h, get_color(d, color)); + end_clipping(d); +} + +static void BrushFree(GB_BRUSH brush) +{ + delete (QBrush *)brush; +} + +static void BrushColor(GB_BRUSH *brush, GB_COLOR color) +{ + QBrush *br = new QBrush(CCOLOR_make(color)); + *brush = (GB_BRUSH)br; +} + +static void BrushImage(GB_BRUSH *brush, GB_IMAGE image) +{ + QImage img(*CIMAGE_get((CIMAGE *)image)); + + img.detach(); + QBrush *br = new QBrush(img); + *brush = (GB_BRUSH)br; +} + +static void BrushLinearGradient(GB_BRUSH *brush, float x0, float y0, float x1, float y1, int nstop, double *positions, GB_COLOR *colors, int extend) +{ + QLinearGradient gradient; + int i; + + gradient.setStart((qreal)x0, (qreal)y0); + gradient.setFinalStop((qreal)x1, (qreal)y1); + + for (i = 0; i < nstop; i++) + gradient.setColorAt((qreal)positions[i], CCOLOR_make(colors[i])); + + switch (extend) + { + case GB_PAINT_EXTEND_REPEAT: + gradient.setSpread(QGradient::RepeatSpread); break; + case GB_PAINT_EXTEND_REFLECT: + gradient.setSpread(QGradient::ReflectSpread); break; + case GB_PAINT_EXTEND_PAD: + default: + gradient.setSpread(QGradient::PadSpread); + } + + QBrush *br = new QBrush(gradient); + *brush = br; +} + +static void BrushRadialGradient(GB_BRUSH *brush, float cx, float cy, float r, float fx, float fy, int nstop, double *positions, GB_COLOR *colors, int extend) +{ + QRadialGradient gradient; + int i; + + gradient.setCenter((qreal)cx, (qreal)cy); + gradient.setRadius((qreal)r); + gradient.setFocalPoint((qreal)fx, (qreal)fy); + + for (i = 0; i < nstop; i++) + gradient.setColorAt((qreal)positions[i], CCOLOR_make(colors[i])); + + switch (extend) + { + case GB_PAINT_EXTEND_REPEAT: + gradient.setSpread(QGradient::RepeatSpread); break; + case GB_PAINT_EXTEND_REFLECT: + gradient.setSpread(QGradient::ReflectSpread); break; + case GB_PAINT_EXTEND_PAD: + default: + gradient.setSpread(QGradient::PadSpread); + } + + QBrush *br = new QBrush(gradient); + *brush = br; +} + +static void BrushMatrix(GB_BRUSH brush, int set, GB_TRANSFORM matrix) +{ + QBrush *b = (QBrush *)brush; + QTransform *t = (QTransform *)matrix; + + if (set) + { + if (t) + b->setTransform(*t); + else + b->setTransform(QTransform()); + } + else + *t = b->transform(); +} + +static void TransformCreate(GB_TRANSFORM *matrix) +{ + *matrix = (GB_TRANSFORM)(new QTransform()); +} + +static void TransformCopy(GB_TRANSFORM *matrix, GB_TRANSFORM copy) +{ + *matrix = (GB_TRANSFORM)(new QTransform(*(QTransform *)copy)); +} + +static void TransformDelete(GB_TRANSFORM *matrix) +{ + delete (QTransform *)*matrix; + *matrix = 0; +} + +static void TransformInit(GB_TRANSFORM matrix, float xx, float yx, float xy, float yy, float x0, float y0) +{ + QTransform *t = (QTransform *)matrix; + QMatrix m((qreal)xx, (qreal)yx, (qreal)xy, (qreal)yy, (qreal)x0, (qreal)y0); + QTransform t2(m); + *t = t2; +} + +static void TransformTranslate(GB_TRANSFORM matrix, float tx, float ty) +{ + QTransform *t = (QTransform *)matrix; + t->translate((qreal)tx, (qreal)ty); +} + +static void TransformScale(GB_TRANSFORM matrix, float sx, float sy) +{ + QTransform *t = (QTransform *)matrix; + t->scale((qreal)sx, (qreal)sy); +} + +static void TransformRotate(GB_TRANSFORM matrix, float angle) +{ + QTransform *t = (QTransform *)matrix; + t->rotateRadians(-angle); +} + +static int TransformInvert(GB_TRANSFORM matrix) +{ + QTransform *t = (QTransform *)matrix; + bool inv; + QTransform it = t->inverted(&inv); + if (inv) + { + *t = it; + return FALSE; + } + else + return TRUE; +} + +static void TransformMultiply(GB_TRANSFORM matrix, GB_TRANSFORM matrix2) +{ + QTransform *t = (QTransform *)matrix; + QTransform *t2 = (QTransform *)matrix2; + + *t = *t * *t2; +} + +static void TransformMap(GB_TRANSFORM matrix, double *x, double *y) +{ + qreal xx, yy; + + xx = *x; + yy = *y; + + ((QTransform *)matrix)->map(xx, yy, &xx, &yy); + + *x = xx; + *y = yy; +} + + +GB_PAINT_DESC PAINT_Interface = +{ + // Size of the GB_PAINT structure extra data + sizeof(QT_PAINT_EXTRA), + Begin, + End, + Save, + Restore, + Antialias, + Font, + Background, + Invert, + Clip, + ResetClip, + ClipExtents, + ClipRect, + Fill, + Stroke, + PathExtents, + PathContains, + PathOutline, + Dash, + DashOffset, + FillRule, + FillStyle, + LineCap, + LineJoin, + LineWidth, + MiterLimit, + Operator, + NewPath, + ClosePath, + Arc, + Ellipse, + Rectangle, + GetCurrentPoint, + MoveTo, + LineTo, + CurveTo, + Text, + TextExtents, + TextSize, + RichText, + RichTextExtents, + RichTextSize, + Matrix, + SetBrush, + BrushOrigin, + DrawImage, + DrawPicture, + GetPictureInfo, + FillRect, + { + BrushFree, + BrushColor, + BrushImage, + BrushLinearGradient, + BrushRadialGradient, + BrushMatrix, + } +}; + +GB_PAINT_MATRIX_DESC PAINT_MATRIX_Interface = +{ + TransformCreate, + TransformCopy, + TransformDelete, + TransformInit, + TransformTranslate, + TransformScale, + TransformRotate, + TransformInvert, + TransformMultiply, + TransformMap +}; + +void PAINT_begin(void *device) +{ + DRAW.Paint.Begin(device); +} + +void PAINT_end() +{ + DRAW.Paint.End(); +} + +QPainter *PAINT_get_current() +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + return d ? PAINTER(d) : NULL; +} + +void PAINT_get_current_point(float *x, float *y) +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + if (!d) + return; + GetCurrentPoint(d, x, y); +} + +void PAINT_clip(int x, int y, int w, int h) +{ + GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent(); + if (d) + ClipRect(d, x, y, w, h); +} + +/*************************************************************************/ + +MyPaintEngine::MyPaintEngine() : QPaintEngine(0) {} +MyPaintEngine::~MyPaintEngine() {} + +void MyPaintEngine::patchFeatures() +{ + if (type() == PostScript || type() == Pdf) + { + QPaintEngine::PaintEngineFeatures f = QPaintEngine::AllFeatures; + f &= (QPaintEngine::PorterDuff | QPaintEngine::PerspectiveTransform + | QPaintEngine::ObjectBoundingModeGradients + | QPaintEngine::LinearGradientFill + | QPaintEngine::RadialGradientFill + | QPaintEngine::ConicalGradientFill); + //qWarning("warning: patching current paint engine"); + gccaps = f; //PerspectiveTransform; + } +} + +bool MyPaintEngine::begin(QPaintDevice *pdev) +{ + setActive(true); + return true; +} + +bool MyPaintEngine::end() +{ + setActive(false); + return true; +} + +void MyPaintEngine::updateState(const QPaintEngineState &state) +{ + //qDebug("MyPaintEngine::updateState: %04X", (int)state.state()); +} + +void MyPaintEngine::drawRects(const QRectF *rects, int rectCount) +{ + //qDebug("MyPaintEngine::drawRects"); +} + +void MyPaintEngine::drawLines(const QLineF *lines, int lineCount) +{ + //qDebug("MyPaintEngine::drawLines"); +} + +void MyPaintEngine::drawEllipse(const QRectF &r) +{ + //qDebug("MyPaintEngine::drawEllipse"); +} + +void MyPaintEngine::drawPath(const QPainterPath &path) +{ + //qDebug("MyPaintEngine::drawPath"); +} + +void MyPaintEngine::drawPoints(const QPointF *points, int pointCount) +{ + //qDebug("MyPaintEngine::drawPoints"); +} + +void MyPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) +{ + //qDebug("MyPaintEngine::drawPolygon"); +} + + +void MyPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) +{ + //qDebug("MyPaintEngine::drawPixmap"); +} + +void MyPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s) +{ + //qDebug("MyPaintEngine::drawTiledPixmap"); +} + +void MyPaintEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags) +{ + //qDebug("MyPaintEngine::drawImage"); +} + + +//QPoint MyPaintEngine::coordinateOffset() const; + +MyPaintEngine::Type MyPaintEngine::type() const +{ + return QPaintEngine::User; +} + +void MyPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem) +{ + //qDebug("MyPaintEngine::drawTextItem: %g %g [%s] '%s'", p.x() + _draw_x, p.y() + _draw_y, (const char *)textItem.font().toString().toUtf8(), (const char *)textItem.text().toUtf8()); + //_draw_path->moveTo(p.x() + _draw_x, p.y() + _draw_y); + _draw_path->addText(p.x() + _draw_x + painter()->worldTransform().dx(), p.y() + _draw_y + painter()->worldTransform().dy(), textItem.font(), textItem.text()); +} + +/*************************************************************************/ + +MyPaintEngine MyPaintDevice::engine; + +MyPaintDevice::MyPaintDevice() : QPaintDevice() +{ +} + +QPaintEngine *MyPaintDevice::paintEngine() const +{ + return &engine; +} + +int MyPaintDevice::metric(PaintDeviceMetric m) const +{ + QPaintDevice *d = PAINT_get_current()->device(); + + switch(m) + { + case PdmWidth: return d->width(); + case PdmHeight: return d->height(); + case PdmWidthMM: return d->widthMM(); + case PdmHeightMM: return d->heightMM(); + #if QT_VERSION <= QT_VERSION_CHECK(4, 6, 0) + case PdmNumColors: return d->numColors(); + #else + case PdmNumColors: return d->colorCount(); + #endif + case PdmDepth: return d->depth(); + case PdmDpiX: return d->logicalDpiX(); + case PdmDpiY: return d->logicalDpiY(); + case PdmPhysicalDpiX: return d->physicalDpiX(); + case PdmPhysicalDpiY: return d->physicalDpiY(); + default: return 0; + } +} diff --git a/gb.qt4/src/cpaint_impl.h b/gb.qt4/src/cpaint_impl.h new file mode 100644 index 00000000..e439bb2b --- /dev/null +++ b/gb.qt4/src/cpaint_impl.h @@ -0,0 +1,92 @@ +/*************************************************************************** + + cpaint_impl.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPAINT_IMPL_H +#define __CPAINT_IMPL_H + +#include "gambas.h" +#include "gb.paint.h" + +#include +#include +#include +#include +#include + +#ifndef __CPAINT_IMPL_C + +extern GB_PAINT_DESC PAINT_Interface; +extern GB_PAINT_MATRIX_DESC PAINT_MATRIX_Interface; + +#endif + +void PAINT_begin(void *device); +void PAINT_end(); +QPainter *PAINT_get_current(); +void PAINT_get_current_point(float *x, float *y); +void PAINT_clip(int x, int y, int w, int h); + +class MyPaintEngine: public QPaintEngine +{ +public: + MyPaintEngine(); + virtual ~MyPaintEngine(); + + virtual bool begin(QPaintDevice *pdev); + virtual bool end(); + + virtual void updateState(const QPaintEngineState &state); + + virtual void drawRects(const QRectF *rects, int rectCount); + virtual void drawLines(const QLineF *lines, int lineCount); + virtual void drawEllipse(const QRectF &r); + virtual void drawPath(const QPainterPath &path); + virtual void drawPoints(const QPointF *points, int pointCount); + virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode); + virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); + virtual void drawTextItem(const QPointF &p, const QTextItem &textItem); + virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s); + virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags = Qt::AutoColor); + + //virtual QPoint coordinateOffset() const; + + virtual Type type() const; + + void patchFeatures(); +}; + +class MyPaintDevice: public QPaintDevice +{ +public: + MyPaintDevice(); + virtual QPaintEngine *paintEngine() const; + +protected: + virtual int metric(PaintDeviceMetric m) const; + +private: + static MyPaintEngine engine; +}; + + +#endif diff --git a/gb.qt4/src/cprinter.cpp b/gb.qt4/src/cprinter.cpp new file mode 100644 index 00000000..6e784098 --- /dev/null +++ b/gb.qt4/src/cprinter.cpp @@ -0,0 +1,519 @@ +/*************************************************************************** + + cprinter.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPRINTER_CPP + +#include +#include +#include + +#include "gb.form.print.h" +#include "gb.form.properties.h" +#include "cpaint_impl.h" +#include "cprinter.h" + +DECLARE_EVENT(EVENT_Begin); +DECLARE_EVENT(EVENT_End); +DECLARE_EVENT(EVENT_Paginate); +DECLARE_EVENT(EVENT_Draw); + +static bool configure_printer(CPRINTER *_object) +{ + QPrinter *printer = THIS->printer; + + QPrintDialog dialog(printer, qApp->activeWindow()); + return (dialog.exec() != QDialog::Accepted); +} + +static bool run_printer(CPRINTER *_object) +{ + QEventLoop loop; + bool ret = true; + int page; + int firstPage, lastPage; + bool reverse; + int num_copies_out, num_copies_in, repeat_out, repeat_in; + + THIS->cancel = false; + + THIS->printing = true; + PAINT_begin(THIS); + + GB.Raise(THIS, EVENT_Begin, 0); + + if (GB.CanRaise(THIS, EVENT_Paginate)) + { + while (!THIS->cancel && !THIS->page_count_set) + { + GB.Raise(THIS, EVENT_Paginate, 0); + loop.processEvents(); + } + } + + if (THIS->cancel) + goto __CANCEL; + + if (PRINTER->fromPage() == 0) + { + firstPage = 1; + lastPage = THIS->page_count; + } + else if (PRINTER->toPage() == 0) + { + firstPage = PRINTER->fromPage(); + lastPage = THIS->page_count; + } + else + { + firstPage = PRINTER->fromPage(); + lastPage = PRINTER->toPage(); + } + + if (firstPage > THIS->page_count) + goto __CANCEL; + if (lastPage > THIS->page_count) + lastPage = THIS->page_count; + + reverse = PRINTER->pageOrder() == QPrinter::LastPageFirst; + if (PRINTER->collateCopies()) + { + num_copies_out = PRINTER->numCopies(); // Always return 1 if the driver manages it + num_copies_in = 1; + } + else + { + num_copies_in = PRINTER->numCopies(); // Always return 1 if the driver manages it + num_copies_out = 1; + } + + for(repeat_out = 0; repeat_out < num_copies_out; repeat_out++) + { + for (page = firstPage; page <= lastPage; page++) + { + for(repeat_in = 0; repeat_in < num_copies_in; repeat_in++) + { + loop.processEvents(); + if (THIS->cancel) + goto __CANCEL; + + if (reverse) + THIS->page = firstPage + lastPage - page; + else + THIS->page = page; + + GB.Raise(THIS, EVENT_Draw, 0); + + if (page != lastPage) + PRINTER->newPage(); + } + } + } + +__CANCEL: + + GB.Raise(THIS, EVENT_End, 0); + + PAINT_end(); + + THIS->page_count_set = false; + + THIS->printing = false; + + return ret; +} + +static void update_duplex(CPRINTER *_object) +{ + QPrinter::DuplexMode duplex; + + switch(THIS->duplex) + { + case GB_PRINT_DUPLEX_HORIZONTAL: + duplex = PRINTER->orientation() == QPrinter::Portrait ? QPrinter::DuplexShortSide : QPrinter::DuplexLongSide; + break; + case GB_PRINT_DUPLEX_VERTICAL: + duplex = PRINTER->orientation() == QPrinter::Portrait ? QPrinter::DuplexLongSide : QPrinter::DuplexShortSide; + break; + case GB_PRINT_SIMPLEX: + default: + duplex = QPrinter::DuplexNone; + } + + PRINTER->setDuplex(duplex); +} + +BEGIN_METHOD_VOID(Printer_new) + + if (MAIN_CHECK_INIT()) + return; + + THIS->printer = new QPrinter(QPrinter::HighResolution); + THIS->page_count = 1; + +END_METHOD + +BEGIN_METHOD_VOID(Printer_free) + + delete THIS->printer; + +END_METHOD + +BEGIN_METHOD_VOID(Printer_Print) + + GB.ReturnBoolean(run_printer(THIS)); + +END_METHOD + +BEGIN_METHOD_VOID(Printer_Configure) + + GB.ReturnBoolean(configure_printer(THIS)); + +END_METHOD + +BEGIN_METHOD_VOID(Printer_Cancel) + + THIS->cancel = true; + PRINTER->abort(); + +END_METHOD + +BEGIN_PROPERTY(Printer_Count) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->page_count); + else + { + THIS->page_count = VPROP(GB_INTEGER); + THIS->page_count_set = true; + } + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Page) + + GB.ReturnInteger(THIS->page); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Name) + + if (READ_PROPERTY) + RETURN_NEW_STRING(PRINTER->printerName()); + else + PRINTER->setPrinterName(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Orientation) + + if (READ_PROPERTY) + { + switch(PRINTER->orientation()) + { + case QPrinter::Landscape: GB.ReturnInteger(GB_PRINT_LANDSCAPE); break; + case QPrinter::Portrait: default: GB.ReturnInteger(GB_PRINT_PORTRAIT); + } + } + else + { + QPrinter::Orientation orient; + + switch (VPROP(GB_INTEGER)) + { + case GB_PRINT_LANDSCAPE: orient = QPrinter::Landscape; break; + case GB_PRINT_PORTRAIT: default: orient = QPrinter::Portrait; break; + } + + PRINTER->setOrientation(orient); + update_duplex(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Paper) + + if (READ_PROPERTY) + { + int val; + + switch(PRINTER->paperSize()) + { + case QPrinter::A3: val = GB_PRINT_A3; break; + case QPrinter::A4: val = GB_PRINT_A4; break; + case QPrinter::A5: val = GB_PRINT_A5; break; + case QPrinter::B5: val = GB_PRINT_B5; break; + case QPrinter::Letter: val = GB_PRINT_LETTER; break; + case QPrinter::Executive: val = GB_PRINT_EXECUTIVE; break; + case QPrinter::Legal: val = GB_PRINT_LEGAL; break; + default: val = GB_PRINT_CUSTOM; + } + + GB.ReturnInteger(val); + } + else + { + QPrinter::PaperSize paper; + + switch(VPROP(GB_INTEGER)) + { + case GB_PRINT_A3: paper = QPrinter::A3; break; + case GB_PRINT_A5: paper = QPrinter::A5; break; + case GB_PRINT_B5: paper = QPrinter::B5; break; + case GB_PRINT_LETTER: paper = QPrinter::Letter; break; + case GB_PRINT_EXECUTIVE: paper = QPrinter::Executive; break; + case GB_PRINT_LEGAL: paper = QPrinter::Legal; break; + case GB_PRINT_A4: default: paper = QPrinter::A4; + } + + PRINTER->setPaperSize(paper); + } + +END_PROPERTY + +BEGIN_PROPERTY(Printer_PaperWidth) + + QSizeF size = PRINTER->paperSize(QPrinter::Millimeter); + + if (READ_PROPERTY) + GB.ReturnFloat(floor((double)size.width() * 1E6) / 1E6); + else + { + qreal width = (qreal)VPROP(GB_FLOAT); + if (width != size.width()) + { + size.setWidth(width); + PRINTER->setPaperSize(size, QPrinter::Millimeter); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(Printer_PaperHeight) + + QSizeF size = PRINTER->paperSize(QPrinter::Millimeter); + + if (READ_PROPERTY) + GB.ReturnFloat(floor((double)size.height() * 1E6) / 1E6); + else + { + qreal height = (qreal)VPROP(GB_FLOAT); + if (height != size.height()) + { + size.setHeight(height); + PRINTER->setPaperSize(size, QPrinter::Millimeter); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(Printer_CollateCopies) + + if (READ_PROPERTY) + GB.ReturnBoolean(PRINTER->collateCopies()); + else + PRINTER->setCollateCopies(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_ReverseOrder) + + if (READ_PROPERTY) + GB.ReturnBoolean(PRINTER->pageOrder() == QPrinter::LastPageFirst); + else + PRINTER->setPageOrder(VPROP(GB_BOOLEAN) ? QPrinter::LastPageFirst : QPrinter::FirstPageFirst); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Duplex) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->duplex); + else + { + THIS->duplex = VPROP(GB_INTEGER); + update_duplex(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(Printer_GrayScale) + + if (READ_PROPERTY) + GB.ReturnBoolean(PRINTER->colorMode() == QPrinter::GrayScale); + else + PRINTER->setColorMode(VPROP(GB_BOOLEAN) ? QPrinter::GrayScale : QPrinter::Color); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_NumCopies) + +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + if (PRINTER->supportsMultipleCopies()) + { + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->copyCount()); + else + PRINTER->setCopyCount(VPROP(GB_INTEGER)); + } + else +#endif + { + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->numCopies()); + else + PRINTER->setNumCopies(VPROP(GB_INTEGER)); + } + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Resolution) + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->resolution()); + else + PRINTER->setResolution(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_FirstPage) + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->fromPage()); + else + PRINTER->setFromTo(VPROP(GB_INTEGER), PRINTER->toPage()); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_LastPage) + + if (READ_PROPERTY) + GB.ReturnInteger(PRINTER->toPage()); + else + PRINTER->setFromTo(PRINTER->fromPage(), VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_FullPage) + + if (READ_PROPERTY) + GB.ReturnBoolean(PRINTER->fullPage()); + else + PRINTER->setFullPage(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_OutputFile) + + if (READ_PROPERTY) + RETURN_NEW_STRING(PRINTER->outputFileName()); + else + PRINTER->setOutputFileName(TO_QSTRING(GB.FileName(PSTRING(), PLENGTH()))); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_Default) + + QPrinterInfo info = QPrinterInfo::defaultPrinter(); + + if (info.isNull()) + GB.ReturnNull(); + else + RETURN_NEW_STRING(info.printerName()); + +END_PROPERTY + +BEGIN_PROPERTY(Printer_List) + + GB_ARRAY array; + QList list = QPrinterInfo::availablePrinters(); + + GB.Array.New(&array, GB_T_STRING, list.length()); + for (int i = 0; i < list.length(); i++) + *((char **)GB.Array.Get(array, i)) = NEW_STRING(list.at(i).printerName()); + + GB.ReturnObject(array); + +END_PROPERTY + +GB_DESC PrinterDesc[] = +{ + GB_DECLARE("Printer", sizeof(CPRINTER)), + + GB_STATIC_PROPERTY_READ("Default", "s", Printer_Default), + GB_STATIC_PROPERTY_READ("List", "String[]", Printer_List), + + GB_CONSTANT("Portrait", "i", GB_PRINT_PORTRAIT), + GB_CONSTANT("Landscape", "i", GB_PRINT_LANDSCAPE), + //GB_CONSTANT("ReversePortrait", "i", GB_PRINT_REVERSE_PORTRAIT), + //GB_CONSTANT("ReverseLandscape", "i", GB_PRINT_REVERSE_LANDSCAPE), + + GB_CONSTANT("Custom", "i", GB_PRINT_CUSTOM), + GB_CONSTANT("A3", "i", GB_PRINT_A3), + GB_CONSTANT("A4", "i", GB_PRINT_A4), + GB_CONSTANT("A5", "i", GB_PRINT_A5), + GB_CONSTANT("B5", "i", GB_PRINT_B5), + GB_CONSTANT("Letter", "i", GB_PRINT_LETTER), + GB_CONSTANT("Executive", "i", GB_PRINT_EXECUTIVE), + GB_CONSTANT("Legal", "i", GB_PRINT_LEGAL), + + GB_CONSTANT("Simplex", "i", GB_PRINT_SIMPLEX), + GB_CONSTANT("Horizontal", "i", GB_PRINT_DUPLEX_HORIZONTAL), + GB_CONSTANT("Vertical", "i", GB_PRINT_DUPLEX_VERTICAL), + + GB_METHOD("_new", NULL, Printer_new, NULL), + GB_METHOD("_free", NULL, Printer_free, NULL), + + GB_METHOD("Print", "b", Printer_Print, NULL), + GB_METHOD("Configure", "b", Printer_Configure, NULL), + GB_METHOD("Cancel", NULL, Printer_Cancel, NULL), + + GB_PROPERTY("Count", "i", Printer_Count), + GB_PROPERTY_READ("Page", "i", Printer_Page), + + GB_PROPERTY("Name", "s", Printer_Name), + GB_PROPERTY("Orientation", "i", Printer_Orientation), + GB_PROPERTY("Paper", "i", Printer_Paper), + GB_PROPERTY("PaperWidth", "f", Printer_PaperWidth), + GB_PROPERTY("PaperHeight", "f", Printer_PaperHeight), + GB_PROPERTY("CollateCopies", "b", Printer_CollateCopies), + GB_PROPERTY("ReverseOrder", "b", Printer_ReverseOrder), + GB_PROPERTY("Duplex", "i", Printer_Duplex), + GB_PROPERTY("GrayScale", "b", Printer_GrayScale), + GB_PROPERTY("NumCopies", "i", Printer_NumCopies), + GB_PROPERTY("Resolution", "i", Printer_Resolution), + GB_PROPERTY("FirstPage", "i", Printer_FirstPage), + GB_PROPERTY("LastPage", "i", Printer_LastPage), + GB_PROPERTY("FullPage", "b", Printer_FullPage), + GB_PROPERTY("OutputFile", "s", Printer_OutputFile), + + GB_EVENT("Begin", NULL, NULL, &EVENT_Begin), + GB_EVENT("End", NULL, NULL, &EVENT_End), + GB_EVENT("Paginate", NULL, NULL, &EVENT_Paginate), + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + + GB_INTERFACE("Paint", &PAINT_Interface), + + PRINTER_DESCRIPTION, + + GB_END_DECLARE +}; diff --git a/gb.qt4/src/cprinter.h b/gb.qt4/src/cprinter.h new file mode 100644 index 00000000..5a73eeb3 --- /dev/null +++ b/gb.qt4/src/cprinter.h @@ -0,0 +1,55 @@ +/*************************************************************************** + + cprinter.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPRINTER_H +#define __CPRINTER_H + +#include "main.h" + +#include + +typedef + struct { + GB_BASE ob; + QPrinter *printer; + int page; + int page_count; + int duplex; + bool cancel; + bool page_count_set; + bool printing; + } + CPRINTER; + +#ifndef __CPRINTER_CPP + +extern GB_DESC PrinterDesc[]; + +#else + +#define THIS ((CPRINTER *)_object) +#define PRINTER (THIS->printer) + +#endif + +#endif diff --git a/gb.qt4/src/csvgimage.cpp b/gb.qt4/src/csvgimage.cpp new file mode 100644 index 00000000..d4d6ac61 --- /dev/null +++ b/gb.qt4/src/csvgimage.cpp @@ -0,0 +1,274 @@ +/*************************************************************************** + + csvgimage.cpp + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSVGIMAGE_CPP + +#include "main.h" +#include "gambas.h" +#include "cpaint_impl.h" +#include "csvgimage.h" + +#include + +#define MM_TO_PT(_mm) ((_mm) * 72 / 25.4) +#define PT_TO_MM(_pt) ((_pt) / 72 * 25.4) + +static void release(CSVGIMAGE *_object) +{ + if (RENDERER) + { + delete RENDERER; + RENDERER = NULL; + } + + if (GENERATOR) + { + delete GENERATOR; + THIS->generator = NULL; + unlink(THIS->file); + GB.FreeString(&THIS->file); + } + + THIS->width = THIS->height = 0; +} + +QSvgGenerator *SVGIMAGE_begin(CSVGIMAGE *_object, QPainter **painter) +{ + if (!GENERATOR) + { + if (THIS->width <= 0 || THIS->height <= 0) + { + GB.Error("SvgImage size is not defined"); + return NULL; + } + + THIS->file = GB.NewZeroString(GB.TempFile(NULL)); + THIS->generator = new QSvgGenerator(); + GENERATOR->setSize(QSize(THIS->width, THIS->height)); + //GENERATOR->setViewBox(QRectF(0, 0, THIS->width, THIS->height)); + GENERATOR->setFileName(THIS->file); + + if (RENDERER) + { + *painter = new QPainter(GENERATOR); + RENDERER->render(*painter, QRectF(0, 0, THIS->width, THIS->height)); + } + else + *painter = NULL; + } + + return GENERATOR; +} + +BEGIN_METHOD(SvgImage_new, GB_FLOAT width; GB_FLOAT height) + + THIS->width = VARGOPT(width, 0); + THIS->height = VARGOPT(height, 0); + +END_METHOD + +BEGIN_METHOD_VOID(SvgImage_free) + + release(THIS); + +END_METHOD + +BEGIN_PROPERTY(SvgImage_Width) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->width); + else + THIS->width = VPROP(GB_FLOAT); + +END_PROPERTY + +BEGIN_PROPERTY(SvgImage_Height) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->height); + else + THIS->height = VPROP(GB_FLOAT); + +END_PROPERTY + +BEGIN_METHOD(SvgImage_Resize, GB_FLOAT width; GB_FLOAT height) + + THIS->width = VARG(width); + THIS->height = VARG(height); + +END_METHOD + +#ifdef QT5 +static void myMessageHandler(QtMsgType, const QMessageLogContext &, const QString &) +#else +static void myMessageHandler(QtMsgType, const char *) +#endif +{ +} + +static const char *load_file(CSVGIMAGE *_object, const char *path, int len_path) +{ + QSvgRenderer *renderer; + QByteArray data; + char *addr; + int len; + const char *error = NULL; + + if (GB.LoadFile(path, len_path, &addr, &len)) + return "Unable to load SVG file"; + + data = QByteArray::fromRawData(addr, len); + +#ifdef QT5 + qInstallMessageHandler(myMessageHandler); +#else + qInstallMsgHandler(myMessageHandler); +#endif + renderer = new QSvgRenderer(data); +#ifdef QT5 + qInstallMessageHandler(0); +#else + qInstallMsgHandler(0); +#endif + if (!renderer->isValid()) + { + error = "Unable to load SVG file: unable to create renderer"; + goto __RETURN; + } + + release(THIS); + THIS->renderer = renderer; + THIS->width = renderer->defaultSize().width(); + THIS->height = renderer->defaultSize().height(); + + renderer = NULL; + +__RETURN: + + if (renderer) + delete renderer; + + GB.ReleaseFile(addr, len); + return error; +} + +BEGIN_METHOD(SvgImage_Load, GB_STRING path) + + CSVGIMAGE *svgimage; + const char *err; + + svgimage = (CSVGIMAGE *)GB.New(CLASS_SvgImage, NULL, NULL); + + err = load_file(svgimage, STRING(path), LENGTH(path)); + if (err) + { + GB.Unref(POINTER(&svgimage)); + GB.Error(err); + return; + } + + GB.ReturnObject(svgimage); + +END_METHOD + +BEGIN_METHOD(SvgImage_Paint, GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h) + + QPainter *painter = PAINT_get_current(); + float xc, yc; + const char *err; + + if (!painter) + return; + + if (THIS->file) + { + err = load_file(THIS, THIS->file, GB.StringLength(THIS->file)); + if (err) + { + GB.Error(err); + return; + } + } + + if (!RENDERER) + return; + + if (THIS->width <= 0 || THIS->height <= 0) + return; + + PAINT_get_current_point(&xc, &yc); + RENDERER->render(painter, QRectF(VARGOPT(x, xc), VARGOPT(y, yc), VARGOPT(w, THIS->width), VARGOPT(h, THIS->height))); + +END_METHOD + +BEGIN_METHOD(SvgImage_Save, GB_STRING file) + + if (!THIS->file) + { + QPainter *painter; + if (!SVGIMAGE_begin(THIS, &painter)) + { + GB.Error("Void image"); + return; + } + if (painter) + delete painter; + } + + if (GB.CopyFile(THIS->file, GB.FileName(STRING(file), LENGTH(file)))) + return; + + load_file(THIS, THIS->file, GB.StringLength(THIS->file)); + +END_METHOD + +BEGIN_METHOD_VOID(SvgImage_Clear) + + release(THIS); + +END_METHOD + +GB_DESC SvgImageDesc[] = +{ + GB_DECLARE("SvgImage", sizeof(CSVGIMAGE)), + + GB_METHOD("_new", NULL, SvgImage_new, "[(Width)f(Height)f]"), + GB_METHOD("_free", NULL, SvgImage_free, NULL), + + GB_PROPERTY("Width", "f", SvgImage_Width), + GB_PROPERTY("W", "f", SvgImage_Width), + GB_PROPERTY("Height", "f", SvgImage_Height), + GB_PROPERTY("H", "f", SvgImage_Height), + GB_METHOD("Resize", NULL, SvgImage_Resize, "(Width)f(Height)f"), + + GB_STATIC_METHOD("Load", "SvgImage", SvgImage_Load, "(Path)s"), + GB_METHOD("Save", NULL, SvgImage_Save, "(Path)s"), + GB_METHOD("Paint", NULL, SvgImage_Paint, "[(X)f(Y)f(Width)f(Height)f]"), + + GB_METHOD("Clear", NULL, SvgImage_Clear, NULL), + + GB_INTERFACE("Paint", &PAINT_Interface), + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/csvgimage.h b/gb.qt4/src/csvgimage.h new file mode 100644 index 00000000..34ff1685 --- /dev/null +++ b/gb.qt4/src/csvgimage.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + csvgimage.h + + (c) 2004-2006 - Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSVGIMAGE_H +#define __CSVGIMAGE_H + +#include "gambas.h" +#include +#include + +typedef + struct + { + GB_BASE ob; + QSvgGenerator *generator; + QSvgRenderer *renderer; + char *file; + double width; + double height; + } + CSVGIMAGE; + +#ifndef __CSVGIMAGE_CPP + +extern GB_DESC SvgImageDesc[]; + +#else + +#define THIS OBJECT(CSVGIMAGE) +#define GENERATOR THIS->generator +#define RENDERER THIS->renderer + +#endif + +QSvgGenerator *SVGIMAGE_begin(CSVGIMAGE *_object, QPainter **painter); + +#endif diff --git a/gb.qt4/src/ctrayicon.cpp b/gb.qt4/src/ctrayicon.cpp new file mode 100644 index 00000000..585111fc --- /dev/null +++ b/gb.qt4/src/ctrayicon.cpp @@ -0,0 +1,456 @@ +/*************************************************************************** + + ctrayicon.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTRAYICON_CPP + +#include "gambas.h" +#include "main.h" + +#include +#include + +#include "gb.form.properties.h" +#include "ctrayicon.h" + +DECLARE_EVENT(EVENT_Click); +DECLARE_EVENT(EVENT_MiddleClick); +DECLARE_EVENT(EVENT_Scroll); + +static QList _list; +static QPixmap *_default_trayicon = NULL; + +#include "gb.form.trayicon.large.h" + +//--------------------------------------------------------------------------- + +static void destroy_trayicon(CTRAYICON *_object) +{ + if (TRAYICON) + { + TRAYICON->deleteLater(); + THIS->indicator = NULL; + QT_PreventQuit(false); + } +} + +static void delete_all(void) +{ + CTRAYICON *_object, *last = 0; + int i; + + GB.StopAllEnum((void *)GB.FindClass("TrayIcons")); + + i = 0; + for (;;) + { + if (i >= _list.count()) + break; + + _object = _list.at(i); + + if (_object == last) + { + i++; + continue; + } + + last = _object; + destroy_trayicon(THIS); + GB.Unref(POINTER(&_object)); + } + + _list.clear(); +} + +static void define_tooltip(CTRAYICON *_object) +{ + if (!TRAYICON) + return; + + TRAYICON->setToolTip(TO_QSTRING(THIS->tooltip)); +} + +static void define_icon(CTRAYICON *_object) +{ + if (!TRAYICON) + return; + + if (THIS->icon) + TRAYICON->setIcon(*THIS->icon->pixmap); + else + { + if (!_default_trayicon) + { + _default_trayicon = new QPixmap; + _default_trayicon->loadFromData(_default_trayicon_data, sizeof(_default_trayicon_data), "PNG"); + } + + TRAYICON->setIcon(*_default_trayicon); + } +} + +static void define_menu(CTRAYICON *_object) +{ + QMenu *menu; + + if (!TRAYICON) + return; + + menu = NULL; + + if (THIS->popup) + menu = QT_FindMenu(GB.Parent(THIS), THIS->popup); + + TRAYICON->setContextMenu(menu); +} + +static CTRAYICON *find_trayicon(const QObject *o) +{ + int i; + CTRAYICON *_object; + + for (i = 0; i < _list.count(); i++) + { + _object = _list.at(i); + if (TRAYICON && THIS->indicator == o) + return THIS; + } + + return NULL; +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(TrayIcon_new) + + THIS->tag.type = GB_T_NULL; + _list.append(THIS); + GB.Ref(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(TrayIcon_free) + + //qDebug("TrayIcon_free"); + + _list.removeAt(_list.indexOf(THIS)); + + GB.StoreObject(NULL, POINTER(&THIS->icon)); + GB.FreeString(&THIS->tooltip); + GB.FreeString(&THIS->popup); + GB.StoreVariant(NULL, &THIS->tag); + + destroy_trayicon(THIS); + +END_METHOD + + +BEGIN_PROPERTY(TrayIcon_Picture) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->icon); + else + { + GB.StoreObject(PROP(GB_OBJECT), POINTER(&THIS->icon)); + define_icon(THIS); + } + + +END_PROPERTY + + +BEGIN_PROPERTY(TrayIcon_Text) + + if (READ_PROPERTY) + GB.ReturnString(THIS->tooltip); + else + { + GB.StoreString(PROP(GB_STRING), &(THIS->tooltip)); + define_tooltip(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(TrayIcon_PopupMenu) + + if (READ_PROPERTY) + GB.ReturnString(THIS->popup); + else + { + GB.StoreString(PROP(GB_STRING), &(THIS->popup)); + define_menu(THIS); + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(TrayIcon_Show) + + if (!TRAYICON) + { + QSystemTrayIcon *indicator = new QSystemTrayIcon(); + + QObject::connect(indicator, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), &TrayIconManager::manager, SLOT(activated(QSystemTrayIcon::ActivationReason))); + indicator->installEventFilter(&TrayIconManager::manager); + + THIS->indicator = indicator; + QT_PreventQuit(true); + + define_tooltip(THIS); + define_icon(THIS); + define_menu(THIS); + + TRAYICON->show(); + } + +END_METHOD + + +BEGIN_METHOD_VOID(TrayIcon_Hide) + + destroy_trayicon(THIS); + +END_METHOD + + +BEGIN_PROPERTY(TrayIcon_Visible) + + if (READ_PROPERTY) + { + GB.ReturnBoolean(TRAYICON != NULL); + return; + } + + if (VPROP(GB_BOOLEAN)) + TrayIcon_Show(_object, _param); + else + TrayIcon_Hide(_object, _param); + +END_PROPERTY + + +BEGIN_PROPERTY(TrayIcon_Tag) + + if (READ_PROPERTY) + GB.ReturnVariant(&THIS->tag); + else + GB.StoreVariant(PROP(GB_VARIANT), (void *)&THIS->tag); + +END_METHOD + + +BEGIN_METHOD_VOID(TrayIcons_next) + + int index; + + index = ENUM(int); + + if (index >= _list.count()) + { + GB.StopEnum(); + return; + } + + ENUM(int) = index + 1; + + GB.ReturnObject(_list.at(index)); + +END_METHOD + + +BEGIN_METHOD(TrayIcons_get, GB_INTEGER index) + + int index = (uint)VARG(index); + + if (index >= _list.count()) + { + GB.Error("Bad index"); + return; + } + + GB.ReturnObject(_list.at(index)); + +END_METHOD + + +BEGIN_PROPERTY(TrayIcons_Count) + + GB.ReturnInteger(_list.count()); + +END_PROPERTY + +BEGIN_METHOD_VOID(TrayIcon_unknown) + + static char prop[32]; + char *name = GB.GetUnknown(); + + if (strcasecmp(name, "ScreenX") == 0 || strcasecmp(name, "ScreenY") == 0) + { + sprintf(prop, "TrayIcon.%s", name); + GB.Deprecated(QT_NAME, prop, NULL); + + if (READ_PROPERTY) + { + GB.ReturnInteger(0); + GB.ReturnConvVariant(); + return; + } + else + GB.Error(GB_ERR_NWRITE, GB.GetClassName(NULL), name); + } + else if (strcasecmp(name, "W") == 0 || strcasecmp(name, "Width") == 0 || strcasecmp(name, "H") == 0 || strcasecmp(name, "Height") == 0) + { + sprintf(prop, "TrayIcon.%s", name); + GB.Deprecated(QT_NAME, prop, NULL); + + if (READ_PROPERTY) + { + GB.ReturnInteger(24); + GB.ReturnConvVariant(); + return; + } + else + GB.Error(GB_ERR_NWRITE, GB.GetClassName(NULL), name); + } + else + { + GB.Error(GB_ERR_NSYMBOL, GB.GetClassName(NULL), name); + } + +END_METHOD + +BEGIN_METHOD_VOID(TrayIcon_exit) + + if (_default_trayicon) + delete _default_trayicon; + +END_METHOD + +BEGIN_METHOD_VOID(TrayIcons_DeleteAll) + + delete_all(); + +END_METHOD + +//--------------------------------------------------------------------------- + +TrayIconManager TrayIconManager::manager; + +void TrayIconManager::activated(QSystemTrayIcon::ActivationReason reason) +{ + CTRAYICON *_object = find_trayicon(sender()); + if (THIS) + { + //qDebug("reason = %d", (int)reason); + switch(reason) + { + //case QSystemTrayIcon::DoubleClick: + // GB.Raise(THIS, EVENT_TrayIconDblClick, 0); + // break; + + case QSystemTrayIcon::Trigger: + GB.Raise(THIS, EVENT_Click, 0); + break; + + case QSystemTrayIcon::MiddleClick: + GB.Raise(THIS, EVENT_MiddleClick, 0); + break; + + default: + break; + } + } +} + +bool TrayIconManager::eventFilter(QObject *o, QEvent *e) +{ + if (e->type() == QEvent::Wheel) + { + CTRAYICON *_object = find_trayicon(o); + if (THIS) + { + bool cancel; + QWheelEvent *ev = (QWheelEvent *)e; + + cancel = GB.Raise(THIS, EVENT_Scroll, 2, GB_T_FLOAT, ev->delta() / 120.0, GB_T_INTEGER, ev->orientation() == Qt::Vertical); + + if (cancel) + return true; + } + } + + return QObject::eventFilter(o, e); +} + +//--------------------------------------------------------------------------- + +GB_DESC TrayIconsDesc[] = +{ + GB_DECLARE_STATIC("TrayIcons"), + + GB_STATIC_METHOD("_next", "TrayIcon", TrayIcons_next, NULL), + GB_STATIC_METHOD("_get", "TrayIcon", TrayIcons_get, "(Index)i"), + GB_STATIC_PROPERTY_READ("Count", "i", TrayIcons_Count), + GB_STATIC_METHOD("DeleteAll", NULL, TrayIcons_DeleteAll, NULL), + + GB_END_DECLARE +}; + + +GB_DESC TrayIconDesc[] = +{ + GB_DECLARE("TrayIcon", sizeof(CTRAYICON)), + + GB_STATIC_METHOD("_exit", NULL, TrayIcon_exit, NULL), + + GB_CONSTANT("Horizontal", "i", 0), + GB_CONSTANT("Vertical", "i", 1), + + GB_METHOD("_new", NULL, TrayIcon_new, NULL), + GB_METHOD("_free", NULL, TrayIcon_free, NULL), + + GB_METHOD("Show", NULL, TrayIcon_Show, NULL), + GB_METHOD("Hide", NULL, TrayIcon_Hide, NULL), + GB_METHOD("Delete", NULL, TrayIcon_Hide, NULL), + + GB_PROPERTY("Picture", "Picture", TrayIcon_Picture), + GB_PROPERTY("Icon", "Picture", TrayIcon_Picture), + GB_PROPERTY("Visible", "b", TrayIcon_Visible), + + GB_PROPERTY("Text", "s", TrayIcon_Text), + GB_PROPERTY("PopupMenu", "s", TrayIcon_PopupMenu), + GB_PROPERTY("Tooltip", "s", TrayIcon_Text), + GB_PROPERTY("Tag", "v", TrayIcon_Tag), + + GB_EVENT("Click", NULL, NULL, &EVENT_Click), + GB_EVENT("MiddleClick", NULL, NULL, &EVENT_MiddleClick), + GB_EVENT("Scroll", NULL, "(Delta)f(Orientation)i", &EVENT_Scroll), + + GB_METHOD("_unknown", "v", TrayIcon_unknown, "."), + + TRAYICON_DESCRIPTION, + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/ctrayicon.h b/gb.qt4/src/ctrayicon.h new file mode 100644 index 00000000..d0781ccc --- /dev/null +++ b/gb.qt4/src/ctrayicon.h @@ -0,0 +1,70 @@ +/*************************************************************************** + + ctrayicon.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTRAYICON_H +#define __CTRAYICON_H + +#include "gambas.h" +#include "main.h" + +#include "CPicture.h" +#include + +#ifndef __CTRAYICON_CPP +extern GB_DESC TrayIconsDesc[]; +extern GB_DESC TrayIconDesc[]; +#else + +#define THIS ((CTRAYICON *)_object) +#define TRAYICON (THIS->indicator) + +#endif + +void TRAYICON_close_all(void); + +typedef + struct { + GB_BASE ob; + QSystemTrayIcon *indicator; + GB_VARIANT_VALUE tag; + CPICTURE *icon; + char *tooltip; + char *popup; + } + CTRAYICON; + +class TrayIconManager : public QObject +{ + Q_OBJECT + +public: + + static TrayIconManager manager; + + virtual bool eventFilter(QObject *o, QEvent *e); + +public slots: + + void activated(QSystemTrayIcon::ActivationReason); +}; +#endif diff --git a/gb.qt4/src/desktop.c b/gb.qt4/src/desktop.c new file mode 100644 index 00000000..12cfce12 --- /dev/null +++ b/gb.qt4/src/desktop.c @@ -0,0 +1,119 @@ +/*************************************************************************** + + desktop.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __DESKTOP_C + +#include "desktop.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const GB_INTERFACE *GB_PTR; +#define GB (*GB_PTR) + +#ifdef __cplusplus +} +#endif + +static bool _desktop_done = FALSE; +static char _desktop[32]; + +static const char *calc_desktop_type() +{ + char *env; + + env = getenv("KDE_FULL_SESSION"); + if (env && strcasecmp(env, "true") == 0) + { + env = getenv("KDE_SESSION_VERSION"); + if (env) + { + if (strcmp(env, "4") == 0) + return "KDE4"; + if (strcmp(env, "5") == 0) + return "KDE5"; + } + return "KDE"; + } + + env = getenv("XDG_CURRENT_DESKTOP"); + if (env && *env && strlen(env) < sizeof(_desktop)) + { + if (env[0] == 'X' && env[1] == '-') + env += 2; + + return env; + } + + env = getenv("GNOME_DESKTOP_SESSION_ID"); + if (env && *env) + return "GNOME"; + + env = getenv("MATE_DESKTOP_SESSION_ID"); + if (env && *env) + return "MATE"; + + env = getenv("E_BIN_DIR"); + if (env && *env) + { + env = getenv("E_LIB_DIR"); + if (env && *env) + return "ENLIGHTENMENT"; + } + + env = getenv("WMAKER_BIN_NAME"); + if (env && *env) + return "WINDOWMAKER"; + + env = getenv("DESKTOP_SESSION"); + if (env && strcasecmp(env, "XFCE") == 0) + return "XFCE"; + + env = getenv("XDG_MENU_PREFIX"); + if (env && strncasecmp(env, "XFCE", 4) == 0) + return "XFCE"; + + env = getenv("XDG_DATA_DIRS"); + if (env && strstr(env, "/xfce")) + return "XFCE"; + + return ""; +} + +const char *DESKTOP_get_type() +{ + const char *type; + char *p; + + if (!_desktop_done) + { + type = calc_desktop_type(); + p = _desktop; + + while ((*p++ = toupper(*type++))); + _desktop_done = TRUE; + } + + return _desktop; +} diff --git a/gb.qt4/src/desktop.h b/gb.qt4/src/desktop.h new file mode 100644 index 00000000..66ba376c --- /dev/null +++ b/gb.qt4/src/desktop.h @@ -0,0 +1,40 @@ +/*************************************************************************** + + desktop.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __DESKTOP_H +#define __DESKTOP_H + +#include "gambas.h" +#include "gb_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +const char *DESKTOP_get_type(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gb.qt4/src/ext/CDial.cpp b/gb.qt4/src/ext/CDial.cpp new file mode 100644 index 00000000..f572ba4b --- /dev/null +++ b/gb.qt4/src/ext/CDial.cpp @@ -0,0 +1,201 @@ +/*************************************************************************** + + CDial.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDIAL_CPP + +#include "main.h" +#include "gambas.h" + +#include +#include + +#include "CDial.h" + +DECLARE_EVENT(EVENT_Change); +//DECLARE_EVENT(EVENT_Activate); +//DECLARE_EVENT(EVENT_Deactivate); + +BEGIN_METHOD(CDIAL_new, GB_OBJECT parent) + + QDial *wid = new QDial(QT.GetContainer(VARG(parent))); + + QT.InitWidget(wid, _object, false); + //QT.SetBackgroundRole(_object, QColorGroup::Base); + + QObject::connect(wid, SIGNAL(valueChanged(int)), &CDial::manager, SLOT(event_change())); + //QObject::connect(wid, SIGNAL(dialPressed()), &CDial::manager, + //SLOT(event_activate())); + //QObject::connect(wid, SIGNAL(dialMoved(int)), &CDial::manager, + //SLOT(event_change())); + //QObject::connect(wid, SIGNAL(dialReleased()), &CDial::manager, + //SLOT(event_deactivate())); + + wid->setMinimum(0); + wid->setMaximum(100); + wid->setSingleStep(1); + wid->setPageStep(10); + wid->setNotchesVisible(true); + + wid->show(); + +END_METHOD + + +BEGIN_PROPERTY(CDIAL_tracking) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->hasTracking()); + else + WIDGET->setTracking(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CDIAL_notchsize) + + GB.ReturnInteger(WIDGET->notchSize()); + +END_PROPERTY + +/* +BEGIN_PROPERTY(CDIAL_notchtarget) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->notchTarget()); + else + WIDGET->setNotchTarget(PROPERTY(GB_INTEGER)); + +END_PROPERTY +*/ + +BEGIN_PROPERTY(CDIAL_wrapping) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->wrapping()); + else + WIDGET->setWrapping(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CDIAL_value) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->value()); + else + WIDGET->setValue(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CDIAL_notchesvisible) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->notchesVisible()); + else + WIDGET->setNotchesVisible(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CDIAL_minval) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->minimum()); + else + WIDGET->setMinimum(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CDIAL_maxval) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->maximum()); + else + WIDGET->setMaximum(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CDIAL_linestep) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->singleStep()); + else + { + int step = VPROP(GB_INTEGER); + if (step > 0) + WIDGET->setSingleStep(step); + } + +END_PROPERTY + +BEGIN_PROPERTY(CDIAL_pagestep) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->pageStep()); + else + { + int step = VPROP(GB_INTEGER); + if (step > 0) + WIDGET->setPageStep(step); + } + +END_PROPERTY + + +/*************************************************************************** + + class CDial + +***************************************************************************/ + +CDial CDial::manager; + +void CDial::event_change(void) +{ + void *object = QT.GetObject((QWidget *)sender()); + GB.Raise(object, EVENT_Change, 0); +} + + + +GB_DESC CDialDesc[] = +{ + GB_DECLARE("Dial", sizeof(CDIAL)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CDIAL_new, "(Parent)Container;"), + + //GB_PROPERTY("Tracking", "b", CDIAL_tracking), + //GB_PROPERTY_READ("NotchSize", "i", CDIAL_notchsize), + GB_PROPERTY("Value", "i", CDIAL_value), + //GB_PROPERTY("MarkGap", "i", CDIAL_notchtarget), + GB_PROPERTY("Mark", "b", CDIAL_notchesvisible), + GB_PROPERTY("MinValue", "i", CDIAL_minval), + GB_PROPERTY("MaxValue", "i", CDIAL_maxval), + GB_PROPERTY("Step", "i", CDIAL_linestep), + GB_PROPERTY("Wrap", "b", CDIAL_wrapping), + GB_PROPERTY("PageStep", "i", CDIAL_pagestep), + + DIAL_DESCRIPTION, + + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + + GB_END_DECLARE +}; + diff --git a/gb.qt4/src/ext/CDial.h b/gb.qt4/src/ext/CDial.h new file mode 100644 index 00000000..eb396a5b --- /dev/null +++ b/gb.qt4/src/ext/CDial.h @@ -0,0 +1,60 @@ +/*************************************************************************** + + CDial.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDIAL_H +#define __CDIAL_H + +#include "gambas.h" +#include "../gb.qt.h" + +#ifndef __CDIAL_CPP + +extern GB_DESC CDialDesc[]; + +#else + +#define THIS ((CDIAL *)_object) +#define WIDGET ((QDial *)((QT_WIDGET *)_object)->widget) + +#endif + +typedef + struct { + QT_WIDGET widget; + } + CDIAL; + +class CDial : public QObject +{ + Q_OBJECT + +public: + + static CDial manager; + +public slots: + + void event_change(void); +}; + +#endif diff --git a/gb.qt4/src/ext/CEditor.cpp b/gb.qt4/src/ext/CEditor.cpp new file mode 100644 index 00000000..f7b06eac --- /dev/null +++ b/gb.qt4/src/ext/CEditor.cpp @@ -0,0 +1,1873 @@ +/*************************************************************************** + + CEditor.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CEDITOR_CPP + +#include "main.h" + +#include +#include +#include + +#include "gdocument.h" + +#include "CEditor.h" + +#define MAX_CONSOLE_WIDTH 256 + +DECLARE_EVENT(EVENT_Change); +DECLARE_EVENT(EVENT_Cursor); +DECLARE_EVENT(EVENT_Scroll); +DECLARE_EVENT(EVENT_Highlight); +DECLARE_EVENT(EVENT_Margin); + +enum { LIMIT_NONE, LIMIT_LINE, LIMIT_BLEND, LIMIT_BACKGROUND }; + +enum +{ + HIGHLIGHT_CUSTOM = GDocument::Custom, + HIGHLIGHT_HTML, + HIGHLIGHT_CSS, + HIGHLIGHT_WEBPAGE, + HIGHLIGHT_DIFF, + HIGHLIGHT_JAVASCRIPT, + HIGHLIGHT_C, + HIGHLIGHT_CPLUSPLUS, + HIGHLIGHT_SQL +}; + +typedef + struct { + int style; + const char *name; + } + HIGHLIGHT_NAME; + +typedef + struct + { + const char *cmd; + void *label; + bool select; + } + COMMAND; + +static HIGHLIGHT_NAME _highlight_name[] = { + { HIGHLIGHT_HTML, "_DoHtml" }, + { HIGHLIGHT_CSS, "_DoCss" }, + { HIGHLIGHT_WEBPAGE, "_DoWebpage" }, + { HIGHLIGHT_DIFF, "_DoDiff" }, + { HIGHLIGHT_JAVASCRIPT, "_DoJavascript" }, + { HIGHLIGHT_C, "_DoC" }, + { HIGHLIGHT_CPLUSPLUS, "_DoCPlusPlus" }, + { HIGHLIGHT_SQL, "_DoSQL" }, + { HIGHLIGHT_CUSTOM, NULL } +}; + + +static int _x1, _y1, _x2, _y2; +static int _style; + +static int _highlight_state = 0; +static bool _highlight_alternate = false; +static int _highlight_tag = 0; +static bool _highlight_show_limit = false; +static QString _highlight_text = ""; +static GHighlightArray *_highlight_data = NULL; +static int _highlight_line = -1; + +static QT_PICTURE _breakpoint_picture = 0; +static QT_PICTURE _bookmark_picture = 0; + +static void highlightCustom(GEditor *master, int line, uint &state, bool &alternate, int &tag, GString &s, GHighlightArray *data, bool &proc) +{ + void *_object = QT.GetObject((QWidget *)master); + + _highlight_state = state; + _highlight_alternate = alternate; + _highlight_tag = tag; + _highlight_text = s.getString(); + _highlight_show_limit = proc; + _highlight_data = data; + _highlight_line = line; + + GB.NewArray(_highlight_data, sizeof(GHighlight), 0); + + if (DOC->getHighlightMode() == GDocument::Custom) + GB.Raise(THIS, EVENT_Highlight, 0); + else + GB.Call(&THIS->highlight, 0, FALSE); + + state = _highlight_state; + alternate = _highlight_alternate; + tag = _highlight_tag; + s = GString(_highlight_text); + proc = _highlight_show_limit; + _highlight_data = 0; +} + +/**************************************************************************** + + Highlight + +****************************************************************************/ + +BEGIN_PROPERTY(Highlight_Line) + + GB.ReturnInteger(_highlight_line); + +END_PROPERTY + +BEGIN_PROPERTY(Highlight_State) + + if (READ_PROPERTY) + GB.ReturnInteger(_highlight_state); + else + _highlight_state = VPROP(GB_INTEGER); + +END_PROPERTY + +BEGIN_PROPERTY(Highlight_AlternateState) + + if (READ_PROPERTY) + GB.ReturnBoolean(_highlight_alternate); + else + _highlight_alternate = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(Highlight_Tag) + + if (READ_PROPERTY) + GB.ReturnInteger(_highlight_tag); + else + _highlight_tag = VPROP(GB_INTEGER); + +END_PROPERTY + +BEGIN_PROPERTY(Highlight_ShowLimit) + + if (READ_PROPERTY) + GB.ReturnBoolean(_highlight_show_limit); + else + _highlight_show_limit = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(Highlight_Text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(_highlight_text); + else + _highlight_text = QSTRING_PROP(); + +END_PROPERTY + +BEGIN_METHOD(Highlight_Add, GB_INTEGER state; GB_INTEGER len) + + GHighlight *h; + + if (!_highlight_data) + return; + + int count = GB.Count(*_highlight_data) - 1; + int state = VARG(state); + int len = VARGOPT(len, 1); + + if (len < 1) + return; + + if (count < 0 || (*_highlight_data)[count].state != (uint)state || (*_highlight_data)[count].alternate != _highlight_alternate || ((*_highlight_data)[count].len + len) > HIGHLIGHT_LEN_MAX) + { + count++; + h = (GHighlight *)GB.Add(_highlight_data); + h->state = state; + h->alternate = _highlight_alternate; + h->len = len; + } + else + (*_highlight_data)[count].len += len; + +END_METHOD + +BEGIN_PROPERTY(Highlight_Alternate) + + GB.Deprecated("gb.qt4.ext", "Highlight.Alternate", NULL); + GB.ReturnInteger(-1); + +END_PROPERTY + + +/**************************************************************************** + + Editor + +****************************************************************************/ + +BEGIN_PROPERTY(Editor_Text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(DOC->getText().getString()); + else + DOC->setText(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(Editor_Length) + + GB.ReturnInteger(DOC->getLength()); + +END_PROPERTY + +BEGIN_PROPERTY(Editor_EndOfLine) + + if (READ_PROPERTY) + GB.ReturnInteger(DOC->getEndOfLine()); + else + DOC->setEndOfLine(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_METHOD_VOID(Editor_HighlightAll) + + DOC->colorizeAll(); + +END_METHOD + +BEGIN_PROPERTY(CEDITOR_tab_length) + + if (READ_PROPERTY) + GB.ReturnInteger(DOC->getTabWidth()); + else + DOC->setTabWidth(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_METHOD_VOID(CEDITOR_reset) + + DOC->reset(true); + +END_METHOD + +BEGIN_PROPERTY(CEDITOR_read_only) + + if (READ_PROPERTY) + GB.ReturnBoolean(DOC->isReadOnly()); + else + DOC->setReadOnly(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_overwrite) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->getInsertMode()); + else + WIDGET->setInsertMode(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD_VOID(CEDITOR_clear) + + DOC->clear(); + +END_METHOD + +BEGIN_METHOD(CEDITOR_insert, GB_STRING str; GB_INTEGER y; GB_INTEGER x) + + if (MISSING(y) || MISSING(x)) + WIDGET->insert(QSTRING_ARG(str)); + else + DOC->insert(VARG(y), VARG(x), QSTRING_ARG(str)); + +END_METHOD + +static void print_newline(void *_object) +{ + int line, col; + + WIDGET->getCursor(&line, &col); + + if (line < (DOC->numLines() - 1)) + WIDGET->cursorGoto(line + 1, WIDGET->getColumn(), false); + else + { + WIDGET->cursorGoto(line, DOC->lineLength(line), false); + WIDGET->insert("\n"); + } + + if (THIS->terminal) + WIDGET->cursorGoto(WIDGET->getLine(), col, false); + + //qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 0); +} + +static void print_text(void *_object, const char *str, int lstr, bool esc = false) +{ + QString s = QString::fromUtf8(str, lstr); + int line, col; + uint i, len; + + //fprintf(stderr, "-> %.*s\n", lstr, str); + + WIDGET->getCursor(&line, &col); + /*if (col == 0) + { + DOC->remove(line, 0, line, DOC->lineLength(line)); + WIDGET->cursorGoto(line, 0, false); + }*/ + +// if (col < DOC->lineLength(line)) +// { +// end = col + s.length(); +// if (end > DOC->lineLength(line)) +// end = DOC->lineLength(line); +// DOC->remove(line, col, line, end); +// } + + if (!esc) + { + i = 0; + for (;;) + { + if (col == MAX_CONSOLE_WIDTH) + { + print_newline(THIS); + col = 0; + } + len = s.length() - i; + if ((col + len) >= MAX_CONSOLE_WIDTH) + len = MAX_CONSOLE_WIDTH - col; + DOC->remove(WIDGET->getLine(), col, WIDGET->getLine(), col + len); + WIDGET->insert(s.mid(i, len)); + i += len; + if (i >= (uint)s.length()) + break; + col += len; + } + } + else + { + if (col >= MAX_CONSOLE_WIDTH) + print_newline(THIS); + DOC->remove(WIDGET->getLine(), col, WIDGET->getLine(), col + s.length()); + WIDGET->insert(s); + } + + //qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 0); + //BREAKPOINT(); +} + +static int ansi_read_integer(const char *str, int len, int def, int *pos) +{ + int value = 0; + int n = 0; + uchar c; + + str += *pos; + len -= *pos; + + while (len > 0) + { + c = str[n]; + if (c < '0' || c > '9') + break; + value = value * 10 + c - '0'; + len--; + n++; + if (n > 6) + { + value = -1; + break; + } + } + + if (n == 0) + value = def; + + while (len > 0) + { + c = str[n]; + len--; + n++; + if (c == ';' || c < '0' || c > '9') + break; + } + + *pos += n; + return value; +} + +static int ansi_process(void *_object, const char *str, int len) +{ + uchar c; + int n, m, l, pos; + bool print; + + if (len == 0) + return 0; + + c = *str; + if (c == '[' || c == ']' || c == '(' || c == ')') + { + for (l = 0; l < len; l++) + { + c = str[l]; + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) + break; + } + } + else + l = 0; + + if (l >= len) + return 0; + + print = false; + + if (*str == '[') + { + pos = 1; + + switch(str[l]) + { + case 'A': + n = ansi_read_integer(str, l, 1, &pos); + if (n > 0) WIDGET->cursorRelGoto(-n, 0, false); + break; + + case 'B': + n = ansi_read_integer(str, l, 1, &pos); + if (n > 0) WIDGET->cursorRelGoto(n, 0, false); + break; + + case 'C': + n = ansi_read_integer(str, l, 1, &pos); + if (n > 0) WIDGET->cursorRelGoto(0, n, false); + break; + + case 'D': + n = ansi_read_integer(str, l, 1, &pos); + if (n > 0) WIDGET->cursorRelGoto(0, -n, false); + break; + + case 'G': + n = ansi_read_integer(str, l, 1, &pos); + if (n > 0) WIDGET->cursorGoto(WIDGET->getLine(), n - 1, false); + break; + + case 'd': + n = ansi_read_integer(str, l, 1, &pos); + if (n > 0) WIDGET->cursorGoto(n - 1, WIDGET->getColumn(), false); + break; + + case 'H': case 'f': + n = ansi_read_integer(str, l, 1, &pos); + m = ansi_read_integer(str, l, 1, &pos); + + while (DOC->numLines() < n) + DOC->insertLine(DOC->numLines()); + + WIDGET->cursorGoto(n - 1, m - 1, false); + THIS->terminal = TRUE; + //qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 0); + break; + + case 'J': + n = ansi_read_integer(str, l, 1, &pos); + switch(n) + { + case 0: WIDGET->clearDocument(false, true); break; + case 1: WIDGET->clearDocument(true, false); break; + case 2: WIDGET->clearDocument(true, true); break; + } + + case 'K': + n = ansi_read_integer(str, l, 0, &pos); + switch (n) + { + case 0: WIDGET->clearLine(false, true); break; + case 1: WIDGET->clearLine(true, false); break; + case 2: WIDGET->clearLine(true, true); break; + } + break; + + case 's': + WIDGET->saveCursor(); + break; + + case 'u': + WIDGET->restoreCursor(); + break; + + case 'X': + n = ansi_read_integer(str, l, 1, &pos); + WIDGET->clearAfter(n); + break; + + default: + print = FALSE; + } + } + + if (print) + { + fprintf(stderr, "ESC "); + for (n = 0; n <= l; n++) + { + uchar c = str[n]; + + if (c < 32) + fprintf(stderr, "\\x%02X", c); + else + fputc(c, stderr); + } + fputc('\n', stderr); + + //qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 0); + //BREAKPOINT(); + } + + return l; +} + +BEGIN_METHOD(CEDITOR_print, GB_STRING str; GB_INTEGER y; GB_INTEGER x) + + char *str = STRING(str); + int len = LENGTH(str); + int i, j; + unsigned char c; + + DOC->begin(); + + if (!MISSING(y) && !MISSING(x)) + WIDGET->cursorGoto(VARG(y), VARG(x), false); + + j = 0; + for (i = 0; i < len; i++) + { + //if (s[i].latin1() < 32) + // qDebug("%d", s[i].latin1()); + + c = str[i]; + if (c < 32) + { + if (i > j) + print_text(THIS, &str[j], i - j); + + j = i + 1; + + /*if (c != 27) + fprintf(stderr, "\\x%02X\n", c);*/ + + if (c == '\t') + { + WIDGET->insert("\t"); + } + else if (c == '\r') + { + WIDGET->cursorGoto(WIDGET->getLine(), 0, false); + } + else if (c == '\n') + { + print_newline(THIS); + } + else if (c == '\f') // CTRL+L + { + DOC->clear(); + } + else if (c == '\a') + { + WIDGET->flash(); + } + else if (c == '\b') + { + WIDGET->cursorRelGoto(0, -1, false); //WIDGET->getLine(), WIDGET->getColumn() - 1, false); + } + else if (c == 27) + { + i++; + i += ansi_process(THIS, &str[i], len - i); + j = i + 1; + i--; + } + else + { + QString tmp; + tmp.sprintf("^%c", c + 64); + print_text(THIS, tmp, 2, true); + } + } + } + + if (i > j) + print_text(THIS, &str[j], i - j); + + DOC->end(); + +END_METHOD + + +BEGIN_METHOD(CEDITOR_remove, GB_INTEGER y; GB_INTEGER x; GB_INTEGER y2; GB_INTEGER x2) + + DOC->remove(VARG(y), VARG(x), VARG(y2), VARG(x2)); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_undo) + + WIDGET->undo(); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_redo) + + WIDGET->redo(); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_begin) + + DOC->begin(); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_end) + + DOC->end(); + +END_METHOD + +BEGIN_METHOD(CEDITOR_find_next_breakpoint, GB_INTEGER line) + + int line = VARG(line); + + GB.Deprecated("gb.qt4.ext", "Editor.FindNextBreakpoint", "Editor.Breakpoints"); + + for(;;) + { + if (line >= DOC->numLines()) + { + GB.ReturnInteger(-1); + break; + } + + if (DOC->getLineFlag(line, GLine::BreakpointFlag)) + { + GB.ReturnInteger(line); + break; + } + + line++; + } + +END_METHOD + +static void return_flagged_lines(CEDITOR *_object, int flag) +{ + GB_ARRAY array; + int i; + + GB.Array.New(&array, GB_T_INTEGER, 0); + + for (i = 0; i < DOC->numLines(); i++) + { + if (DOC->getLineFlag(i, flag)) + *((int *)GB.Array.Add(array)) = i; + } + + GB.ReturnObject(array); +} + +static void set_flagged_lines(CEDITOR *_object, int flag, GB_ARRAY array) +{ + int i, line; + + if (GB.CheckObject(array)) + return; + + for (i = 0; i < DOC->numLines(); i++) + { + if (DOC->getLineFlag(i, flag)) + DOC->setLineFlag(i, flag, false); + } + + for (i = 0; i < GB.Array.Count(array); i++) + { + line = *((int *)GB.Array.Get(array, i)); + DOC->setLineFlag(line, flag, true); + } +} + +BEGIN_PROPERTY(Editor_Breakpoints) + + if (READ_PROPERTY) + return_flagged_lines(THIS, GLine::BreakpointFlag); + else + set_flagged_lines(THIS, GLine::BreakpointFlag, VPROP(GB_OBJECT)); + +END_PROPERTY + +BEGIN_PROPERTY(Editor_Bookmarks) + + if (READ_PROPERTY) + return_flagged_lines(THIS, GLine::BookmarkFlag); + else + set_flagged_lines(THIS, GLine::BookmarkFlag, VPROP(GB_OBJECT)); + +END_PROPERTY + +BEGIN_PROPERTY(Editor_CurrentLine) + + if (READ_PROPERTY) + GB.ReturnInteger(DOC->currentLine()); + else + DOC->setCurrentLine(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_METHOD(CEDITOR_find_next_limit, GB_INTEGER line) + + GB.ReturnInteger(DOC->getNextLimit(VARG(line))); + +END_METHOD + +BEGIN_METHOD(CEDITOR_find_next_word, GB_STRING word; GB_INTEGER line) + + int line = VARG(line); + QString word = QSTRING_ARG(word); + QString s; + + for(;;) + { + if (line >= DOC->numLines()) + { + GB.ReturnInteger(-1); + break; + } + + s = DOC->getLine(line).getString(); + if (s.find(word, 0, false) >= 0) + { + GB.ReturnInteger(line); + break; + } + + line++; + } + +END_METHOD + + +BEGIN_PROPERTY(CEDITOR_highlight) + + HIGHLIGHT_NAME *p; + int mode; + + if (READ_PROPERTY) + GB.ReturnInteger(DOC->getHighlightMode()); + else + { + mode = VPROP(GB_INTEGER); + + if (mode == GDocument::Gambas) + { + if (MAIN_load_eval_component()) + { + GB.Error("Cannot load Gambas syntax highlighter"); + return; + } + } + else if (mode > GDocument::Custom) + { + if (GB.Component.Load("gb.eval.highlight")) + { + GB.Error("Cannot load Gambas custom syntax highlighter component"); + return; + } + + p = _highlight_name; + while (p->name) + { + if (p->style == mode) + { + if (GB.GetFunction(&THIS->highlight, (void *)GB.FindClass("Highlight"), p->name, "", "") == 0) + { + p = 0; + break; + } + } + p++; + } + + if (p) + mode = GDocument::Custom; + } + + DOC->setHighlightMode(mode, highlightCustom); + } + +END_PROPERTY + + +/**************************************************************************** + + Editor.Selection + +****************************************************************************/ + +BEGIN_PROPERTY(CEDITOR_selected) + + GB.ReturnBoolean(WIDGET->hasSelection()); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_sel) + + if (WIDGET->hasSelection()) + WIDGET->getSelection(&_y1, &_x1, &_y2, &_x2); + else + _x1 = _y1 = _x2 = _y2 = -1; + + RETURN_SELF(); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_sel_text) + + RETURN_NEW_STRING(WIDGET->getSelectedText().getString()); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_sel_start_line) + + GB.ReturnInteger(_y1); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_sel_start_column) + + GB.ReturnInteger(_x1); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_sel_end_line) + + GB.ReturnInteger(_y2); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_sel_end_column) + + GB.ReturnInteger(_x2); + +END_PROPERTY + +BEGIN_METHOD_VOID(CEDITOR_sel_hide) + + WIDGET->hideSelection(); + +END_METHOD + + + +/**************************************************************************** + + Editor.Lines + +****************************************************************************/ + +BEGIN_PROPERTY(CEDITOR_lines_count) + + GB.ReturnInteger((int)DOC->numLines()); + +END_PROPERTY + +BEGIN_METHOD(CEDITOR_lines_get, GB_INTEGER line) + + int line = VARG(line); + + if (line < 0 || line >= DOC->numLines()) + GB.ReturnNull(); + else + { + THIS->line = line; + RETURN_SELF(); + } + +END_METHOD + +BEGIN_METHOD_VOID(Editor_ExpandAll) + + WIDGET->unfoldAll(); + +END_METHOD + +BEGIN_METHOD_VOID(Editor_CollapseAll) + + WIDGET->foldAll(); + +END_METHOD + +/**************************************************************************** + + Editor.Line + +****************************************************************************/ + +BEGIN_PROPERTY(CEDITOR_line_text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(DOC->getLine(THIS->line).getString()); + else + { + GString s = GString(QSTRING_PROP()); + DOC->setLine(THIS->line, s); + } + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_line_length) + + GB.ReturnInteger(DOC->lineLength(THIS->line)); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_line_expanded) + + if (READ_PROPERTY) + GB.ReturnBoolean(!WIDGET->isFolded(THIS->line)); + else + { + if (VPROP(GB_BOOLEAN)) + WIDGET->unfoldLine(THIS->line); + else + WIDGET->foldLine(THIS->line); + } + +END_PROPERTY + +BEGIN_PROPERTY(EditorLine_Limit) + + GB.ReturnBoolean(DOC->hasLimit(THIS->line)); + +END_PROPERTY + +/*BEGIN_PROPERTY(EditorLine_Edited) + + GB.ReturnBoolean(DOC->isLineEditedSomewhere(THIS->line)); + +END_PROPERTY*/ + +BEGIN_METHOD_VOID(CEDITOR_line_get_initial_state) + + uint state; + int tag; + bool alternate; + + DOC->getState(THIS->line, true, state, tag, alternate); + + _highlight_state = state; + _highlight_tag = tag; + _highlight_alternate = alternate; + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_line_refresh) + + DOC->invalidate(THIS->line); + DOC->colorize(THIS->line); + WIDGET->updateLine(THIS->line); + +END_METHOD + +BEGIN_PROPERTY(EditorLine_Bookmark) + + if (READ_PROPERTY) + GB.ReturnBoolean(DOC->getLineFlag(THIS->line, GLine::BookmarkFlag)); + else + DOC->setLineFlag(THIS->line, GLine::BookmarkFlag, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(EditorLine_Breakpoint) + + if (READ_PROPERTY) + GB.ReturnBoolean(DOC->getLineFlag(THIS->line, GLine::BreakpointFlag)); + else + DOC->setLineFlag(THIS->line, GLine::BreakpointFlag, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD(CEDITOR_line_purge, GB_BOOLEAN comment; GB_BOOLEAN string; GB_STRING replace) + + bool comment = VARGOPT(comment, FALSE); + bool string = VARGOPT(string, FALSE); + GString line, result; + uint i, state; + GString replace; + + if (MISSING(replace)) + replace = " "; + else + replace = QSTRING_ARG(replace); + + line = DOC->getLine(THIS->line); + + for (i = 0; i < line.length(); i++) + { + state = DOC->getCharState(THIS->line, i); + if ((!string && state == GLine::String) || (!comment && (state == GLine::Comment || state == GLine::Help))) + result += replace; + else + result += line.at(i); + } + + RETURN_NEW_STRING(result.getString()); + +END_METHOD + + + +/**************************************************************************** + + Editor (again) + +****************************************************************************/ + +BEGIN_METHOD(CEDITOR_new, GB_OBJECT parent) + + GEditor *wid = new GEditor(QT.GetContainer(VARG(parent))); + + QObject::connect(wid, SIGNAL(cursorMoved()), &CEditor::manager, SLOT(moved())); + QObject::connect(wid, SIGNAL(textChanged()), &CEditor::manager, SLOT(changed())); + QObject::connect(wid, SIGNAL(marginDoubleClicked(int)), &CEditor::manager, SLOT(marginDoubleClicked(int))); + QObject::connect(wid, SIGNAL(contentsMoving(int, int)), &CEditor::manager, SLOT(scrolled(int, int))); + + QT.InitWidget(wid, _object, true); + QT.SetWheelFlag(_object); + + THIS->line = -1; + + wid->show(); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_free) + + GB.Unref((void **)&THIS->view); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_exit) + + GB.Unref((void **)&_breakpoint_picture); + GB.Unref((void **)&_bookmark_picture); + +END_METHOD + +BEGIN_PROPERTY(CEDITOR_view) + + if (READ_PROPERTY) + { + if (THIS->view) + GB.ReturnObject(THIS->view); + else + RETURN_SELF(); + } + else + { + GB.StoreObject(PROP(GB_OBJECT), (void **)&THIS->view); + if (THIS->view && THIS->view != THIS) + { + GEditor *view = (GEditor *)((QT_WIDGET *)THIS->view)->widget; + WIDGET->setDocument(view->getDocument()); + } + else + { + WIDGET->setDocument(0); + GB.StoreObject(NULL, (void **)&THIS->view); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_line) + + int line, col; + + WIDGET->getCursor(&line, &col); + GB.ReturnInteger(line); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_column) + + int line, col; + + WIDGET->getCursor(&line, &col); + GB.ReturnInteger(col); + +END_PROPERTY + +BEGIN_METHOD(CEDITOR_goto, GB_INTEGER line; GB_INTEGER col; GB_BOOLEAN center) + + if (VARGOPT(center, FALSE)) + WIDGET->cursorCenter(); + WIDGET->cursorGoto(VARG(line), VARG(col), false); + +END_METHOD + +BEGIN_METHOD(CEDITOR_flags_get, GB_INTEGER flag) + + GB.ReturnBoolean(WIDGET->getFlag(VARG(flag))); + +END_METHOD + +BEGIN_METHOD(CEDITOR_flags_set, GB_BOOLEAN value; GB_INTEGER flag) + + WIDGET->setFlag(VARG(flag), VARG(value)); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_indent) + + WIDGET->tab(false); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_unindent) + + WIDGET->tab(true); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_copy) + + WIDGET->copy(); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_cut) + + WIDGET->cut(); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_paste) + + WIDGET->paste(); + +END_METHOD + +BEGIN_METHOD(CEDITOR_to_pos_x, GB_INTEGER x; GB_INTEGER y) + + int line, col; + int px, py; + + WIDGET->getCursor(&line, &col); + WIDGET->cursorToPos(VARGOPT(y, line), VARG(x), &px, &py); + GB.ReturnInteger(px); + +END_METHOD + +BEGIN_METHOD(CEDITOR_pos_to_line, GB_INTEGER y) + + int line = WIDGET->posToLine(VARG(y)); + + if (WIDGET->isPosOutside()) + GB.ReturnInteger(-1); + else + GB.ReturnInteger(line); + +END_METHOD + +BEGIN_METHOD(CEDITOR_pos_to_column, GB_INTEGER x; GB_INTEGER y) + + int line, col; + + if (WIDGET->posToCursor(VARG(x), VARG(y), &line, &col)) + GB.ReturnInteger(-1); + else + GB.ReturnInteger(col); + +END_METHOD + +BEGIN_METHOD(CEDITOR_styles_get, GB_INTEGER style) + + _style = VARG(style); + RETURN_SELF(); + +END_METHOD + +BEGIN_PROPERTY(CEDITOR_style_color) + + GHighlightStyle style; + + WIDGET->getStyle(_style, &style); + + if (READ_PROPERTY) + GB.ReturnInteger(style.color.rgb() & 0xFFFFFF); + else + { + style.color = QColor(VPROP(GB_INTEGER) & 0xFFFFFF); + WIDGET->setStyle(_style, &style); + } + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_style_background) + + GHighlightStyle style; + + WIDGET->getStyle(_style, &style); + + if (READ_PROPERTY) + { + if (style.background) + GB.ReturnInteger(style.backgroundColor.rgb() & 0xFFFFFF); + else + GB.ReturnInteger(-1); + } + else + { + if (VPROP(GB_INTEGER) == -1) + style.background = false; + else + { + style.background = true; + style.backgroundColor = QColor(VPROP(GB_INTEGER) & 0xFFFFFF); + } + WIDGET->setStyle(_style, &style); + } + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_style_bold) + + GHighlightStyle style; + + WIDGET->getStyle(_style, &style); + + if (READ_PROPERTY) + GB.ReturnBoolean(style.bold); + else + { + style.bold = VPROP(GB_BOOLEAN); + WIDGET->setStyle(_style, &style); + } + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_style_underline) + + GHighlightStyle style; + + WIDGET->getStyle(_style, &style); + + if (READ_PROPERTY) + GB.ReturnBoolean(style.underline); + else + { + style.underline = VPROP(GB_BOOLEAN); + WIDGET->setStyle(_style, &style); + } + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_style_italic) + + GHighlightStyle style; + + WIDGET->getStyle(_style, &style); + + if (READ_PROPERTY) + GB.ReturnBoolean(style.italic); + else + { + style.italic = VPROP(GB_BOOLEAN); + WIDGET->setStyle(_style, &style); + } + +END_PROPERTY + + +BEGIN_METHOD(CEDITOR_select, GB_INTEGER y1; GB_INTEGER x1; GB_INTEGER y2; GB_INTEGER x2) + + WIDGET->cursorGoto(VARG(y1), VARG(x1), false); + WIDGET->cursorGoto(VARG(y2), VARG(x2), true); + +END_METHOD + +BEGIN_METHOD_VOID(CEDITOR_select_all) + + WIDGET->selectAll(); + +END_METHOD + +BEGIN_PROPERTY(CEDITOR_cursor_x) + + int x, y, px, py; + + WIDGET->getCursor(&y, &x); + WIDGET->cursorToPos(y, x, &px, &py); + GB.ReturnInteger(px); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_cursor_y) + + int x, y, px, py; + + WIDGET->getCursor(&y, &x); + WIDGET->cursorToPos(y, x, &px, &py); + GB.ReturnInteger(py); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_line_height) + + GB.ReturnInteger(WIDGET->getLineHeight()); + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_char_width) + + GB.ReturnInteger(WIDGET->getCharWidth('m')); + +END_PROPERTY + +BEGIN_PROPERTY(Editor_BreakpointPicture) + + if (READ_PROPERTY) + GB.ReturnObject(_breakpoint_picture); + else + { + GB.StoreObject(PROP(GB_OBJECT), (void **)&_breakpoint_picture); + GEditor::setBreakpointPixmap(QT.GetPixmap(_breakpoint_picture)); + } + +END_PROPERTY + +BEGIN_PROPERTY(Editor_BookmarkPicture) + + if (READ_PROPERTY) + GB.ReturnObject(_bookmark_picture); + else + { + GB.StoreObject(PROP(GB_OBJECT), (void **)&_bookmark_picture); + GEditor::setBookmarkPixmap(QT.GetPixmap(_bookmark_picture)); + } + +END_PROPERTY + +BEGIN_PROPERTY(CEDITOR_keywords_ucase) + + if (READ_PROPERTY) + GB.ReturnBoolean(DOC->isKeywordsUseUpperCase()); + else + DOC->setKeywordsUseUpperCase(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD(CEDITOR_show_string, GB_STRING str; GB_BOOLEAN ignoreCase) + + GString s; + + if (!MISSING(str)) + s = GString(QSTRING_ARG(str)); + + WIDGET->showString(s, VARGOPT(ignoreCase, false)); + +END_METHOD + +/*BEGIN_METHOD(CEDITOR_show_word, GB_INTEGER line; GB_INTEGER col; GB_INTEGER len) + + int line = VARGOPT(line, -1); + int col = VARGOPT(col, -1); + int len = VARGOPT(len, -1); + + WIDGET->showWord(line, col, len); + +END_METHOD*/ + +BEGIN_PROPERTY(CEDITOR_border) + + //QT.BorderProperty(_object, _param); + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->hasBorder()); + else + WIDGET->setBorder(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Editor_ScrollBar) + + int scroll; + + if (READ_PROPERTY) + { + scroll = 0; + if (WIDGET->hScrollBarMode() == Q3ScrollView::Auto) + scroll += 1; + if (WIDGET->vScrollBarMode() == Q3ScrollView::Auto) + scroll += 2; + + GB.ReturnInteger(scroll); + } + else + { + scroll = VPROP(GB_INTEGER) & 3; + WIDGET->setHScrollBarMode((scroll & 1) ? Q3ScrollView::Auto : Q3ScrollView::AlwaysOff); + WIDGET->setVScrollBarMode((scroll & 2) ? Q3ScrollView::Auto : Q3ScrollView::AlwaysOff); + } + +END_PROPERTY + +BEGIN_PROPERTY(Editor_ScrollX) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->horizontalScrollBar()->value()); + else + WIDGET->horizontalScrollBar()->setValue(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Editor_ScrollY) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->verticalScrollBar()->value()); + else + WIDGET->verticalScrollBar()->setValue(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_METHOD(Editor_Scroll, GB_INTEGER x; GB_INTEGER y) + + WIDGET->horizontalScrollBar()->setValue(VARG(x)); + WIDGET->verticalScrollBar()->setValue(VARG(y)); + +END_METHOD + +BEGIN_PROPERTY(Editor_LineOffset) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->lineOffset()); + else + WIDGET->setLineOffset(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Editor_Mouse) + + QT.MouseProperty(_object, _param); + + if (!READ_PROPERTY) + WIDGET->saveMouseCursor(); + +END_PROPERTY + +#if 0 +BEGIN_METHOD(Editor_Command, GB_STRING command) + + static COMMAND CommandList[] = + { + { "move-left", &&__MOVE_LEFT }, + { "move-left-word", &&__MOVE_LEFT_WORD }, + { "select-left", &&__MOVE_LEFT, TRUE }, + { "select-left-word", &&__MOVE_LEFT_WORD, TRUE }, + { "move-right", &&__MOVE_RIGHT }, + { "move-right-word", &&__MOVE_RIGHT_WORD }, + { "select-right", &&__MOVE_RIGHT, TRUE }, + { "select-right-word", &&__MOVE_RIGHT_WORD, TRUE }, + { "move-up", &&__MOVE_UP }, + { "move-right-word", &&__MOVE_RIGHT_WORD }, + { "select-right", &&__MOVE_RIGHT, TRUE }, + { "select-right-word", &&__MOVE_RIGHT_WORD, TRUE }, + }; + +END_METHOD +#endif + +#if 0 +BEGIN_PROPERTY(Editor_ShowLimits) + + int limit; + + if (READ_PROPERTY) + { + if (!WIDGET->getFlag(GEditor::ShowProcedureLimits)) + limit = LIMIT_NONE; + else if (WIDGET->getFlag(GEditor::BlendedProcedureLimits)) + limit = LIMIT_BLEND; + else if (WIDGET->getFlags(GEditor::ChangeBackgroundAtLimit)) + limit = LIMIT_BACKGROUND; + else + limit = LIMIT_LINE; + + GB.ReturnInteger(limit); + } + else + { + limit = VPROP(GB_INTEGER); + + WIDGET->setFlag(GEditor::ShowProcedureLimits, limit != LIMIT_NONE); + switch(limit) + { + case LIMIT_BLEND: + WIDGET->setFlag(GEditor::BlendedProcedureLimits, true); + WIDGET->setFlag(GEditor::ChangeBackgroundAtLimit, false); + break; + case LIMIT_BACKGROUND: + WIDGET->setFlag(GEditor::BlendedProcedureLimits, false); + WIDGET->setFlag(GEditor::ChangeBackgroundAtLimit, true); + break; + default: + WIDGET->setFlag(GEditor::BlendedProcedureLimits, false); + WIDGET->setFlag(GEditor::ChangeBackgroundAtLimit, false); + break; + } + } + +END_PROPERTY + +#define IMPLEMENT_FLAG_PROPERTY(_name, _constant) \ +BEGIN_PROPERTY(Editor_##_name) \ +\ + if (READ_PROPERTY) \ + GB.ReturnBoolean(WIDGET->getFlag(GEditor::_constant)); \ + else \ + WIDGET->setFlag(GEditor::_constant, VPROP(GB_BOOLEAN)); \ +\ +END_PROPERTY + +IMPLEMENT_FLAG_PROPERTY(ShowLimits, ShowProcedureLimits) + + GB_CONSTANT("ShowLimits", "i", GEditor::ShowProcedureLimits), + GB_CONSTANT("BlendedLimits", "i", GEditor::BlendedProcedureLimits), + GB_CONSTANT("BackgroundLimits", "i", GEditor::ChangeBackgroundAtLimit), + //GB_CONSTANT("DrawWithRelief", "i", GEditor::DrawWithRelief), + GB_CONSTANT("ShowModifiedLines", "i", GEditor::ShowModifiedLines), + GB_CONSTANT("ShowCurrentLine", "i", GEditor::ShowCurrentLine), + GB_CONSTANT("ShowLineNumbers", "i", GEditor::ShowLineNumbers), + GB_CONSTANT("HighlightBraces", "i", GEditor::HighlightBraces), + GB_CONSTANT("HighlightImmediately", "i", GEditor::HighlightImmediately), + GB_CONSTANT("ShowDots", "i", GEditor::ShowDots), + //GB_CONSTANT("ShowCursorPosition", "i", GEditor::ShowCursorPosition), + GB_CONSTANT("HideMargin", "i", GEditor::HideMargin), + Gb_CONSTANT("BlinkCursor", "i", GEditor::BlinkCursor), +#endif + +/***************************************************************************/ + +GB_DESC CHighlightDesc[] = +{ + GB_DECLARE("Highlight", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", GDocument::None), + GB_CONSTANT("Gambas", "i", GDocument::Gambas), + GB_CONSTANT("Custom", "i", GDocument::Custom), + GB_CONSTANT("HTML", "i", HIGHLIGHT_HTML), + GB_CONSTANT("CSS", "i", HIGHLIGHT_CSS), + GB_CONSTANT("WebPage", "i", HIGHLIGHT_WEBPAGE), + GB_CONSTANT("Diff", "i", HIGHLIGHT_DIFF), + GB_CONSTANT("JavaScript", "i", HIGHLIGHT_JAVASCRIPT), + GB_CONSTANT("C", "i", HIGHLIGHT_C), + GB_CONSTANT("CPlusPlus", "i", HIGHLIGHT_CPLUSPLUS), + GB_CONSTANT("SQL", "i", HIGHLIGHT_SQL), + + GB_CONSTANT("Background", "i", HIGHLIGHT_BACKGROUND), + GB_CONSTANT("Normal", "i", HIGHLIGHT_NORMAL), + GB_CONSTANT("Keyword", "i", HIGHLIGHT_KEYWORD), + GB_CONSTANT("Function", "i", HIGHLIGHT_SUBR), + GB_CONSTANT("Operator", "i", HIGHLIGHT_OPERATOR), + GB_CONSTANT("Symbol", "i", HIGHLIGHT_SYMBOL), + GB_CONSTANT("Number", "i", HIGHLIGHT_NUMBER), + GB_CONSTANT("String", "i", HIGHLIGHT_STRING), + GB_CONSTANT("Comment", "i", HIGHLIGHT_COMMENT), + GB_CONSTANT("Breakpoint", "i", HIGHLIGHT_BREAKPOINT), + GB_CONSTANT("Current", "i", HIGHLIGHT_CURRENT), + GB_CONSTANT("DataType", "i", HIGHLIGHT_DATATYPE), + GB_CONSTANT("Selection", "i", HIGHLIGHT_SELECTION), + GB_CONSTANT("Highlight", "i", HIGHLIGHT_HIGHLIGHT), + GB_CONSTANT("CurrentLine", "i", HIGHLIGHT_LINE), + GB_CONSTANT("Error", "i", HIGHLIGHT_ERROR), + GB_CONSTANT("Help", "i", HIGHLIGHT_HELP), + GB_CONSTANT("Preprocessor", "i", HIGHLIGHT_PREPROCESSOR), + GB_STATIC_PROPERTY_READ("Alternate", "i", Highlight_Alternate), + + GB_STATIC_PROPERTY_READ("Line", "i", Highlight_Line), + GB_STATIC_PROPERTY("State", "i", Highlight_State), + GB_STATIC_PROPERTY("Tag", "i", Highlight_Tag), + GB_STATIC_PROPERTY("ShowLimit", "b", Highlight_ShowLimit), + GB_STATIC_PROPERTY("Text", "s", Highlight_Text), + GB_STATIC_PROPERTY("AlternateState", "b", Highlight_AlternateState), + GB_STATIC_METHOD("Add", NULL, Highlight_Add, "(State)i[(Count)i]"), + + GB_END_DECLARE +}; + + +GB_DESC CEditorSelectionDesc[] = +{ + GB_DECLARE(".Editor.Selection", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Text", "s", CEDITOR_sel_text), + GB_PROPERTY_READ("StartLine", "i", CEDITOR_sel_start_line), + GB_PROPERTY_READ("StartColumn", "i", CEDITOR_sel_start_column), + GB_PROPERTY_READ("EndLine", "i", CEDITOR_sel_end_line), + GB_PROPERTY_READ("EndColumn", "i", CEDITOR_sel_end_column), + + GB_METHOD("Hide", NULL, CEDITOR_sel_hide, NULL), + + GB_END_DECLARE +}; + +GB_DESC CEditorLineDesc[] = +{ + GB_DECLARE(".Editor.Line", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Text", "s", CEDITOR_line_text), + GB_PROPERTY_READ("Length", "i", CEDITOR_line_length), + GB_PROPERTY("Expanded", "b", CEDITOR_line_expanded), + GB_PROPERTY("Bookmark", "b", EditorLine_Bookmark), + GB_PROPERTY("Breakpoint", "b", EditorLine_Breakpoint), + GB_PROPERTY_READ("Limit", "b", EditorLine_Limit), + //GB_PROPERTY_READ("Edited", "b", EditorLine_Edited), + GB_METHOD("GetInitialState", NULL, CEDITOR_line_get_initial_state, NULL), + GB_METHOD("Refresh", NULL, CEDITOR_line_refresh, NULL), + GB_METHOD("Purge", "s", CEDITOR_line_purge, "[(Comment)b(String)b(Replace)s]"), + + GB_END_DECLARE +}; + +GB_DESC CEditorLinesDesc[] = +{ + GB_DECLARE(".Editor.Lines", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", ".Editor.Line", CEDITOR_lines_get, "(Line)i"), + GB_PROPERTY_READ("Count", "i", CEDITOR_lines_count), + + GB_END_DECLARE +}; + +GB_DESC CEditorStyleDesc[] = +{ + GB_DECLARE(".Editor.Style", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Color", "i", CEDITOR_style_color), + GB_PROPERTY("Background", "i", CEDITOR_style_background), + GB_PROPERTY("Foreground", "i", CEDITOR_style_color), + GB_PROPERTY("Bold", "b", CEDITOR_style_bold), + GB_PROPERTY("Italic", "b", CEDITOR_style_italic), + GB_PROPERTY("Underline", "b", CEDITOR_style_underline), + + GB_END_DECLARE +}; + +GB_DESC CEditorStylesDesc[] = +{ + GB_DECLARE(".Editor.Styles", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", ".Editor.Style", CEDITOR_styles_get, "(Style)i"), + + GB_END_DECLARE +}; + +GB_DESC CEditorFlagsDesc[] = +{ + GB_DECLARE(".Editor.Flags", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", "b", CEDITOR_flags_get, "(Flag)i"), + GB_METHOD("_put", NULL, CEDITOR_flags_set, "(Value)b(Flag)i"), + + GB_END_DECLARE +}; + +GB_DESC CEditorDesc[] = +{ + GB_DECLARE("Editor", sizeof(CEDITOR)), GB_INHERITS("Control"), + + GB_CONSTANT("ShowLimits", "i", GEditor::ShowProcedureLimits), + GB_CONSTANT("BlendedLimits", "i", GEditor::BlendedProcedureLimits), + GB_CONSTANT("BackgroundLimits", "i", GEditor::ChangeBackgroundAtLimit), + GB_CONSTANT("ShowModifiedLines", "i", GEditor::ShowModifiedLines), + GB_CONSTANT("ShowCurrentLine", "i", GEditor::ShowCurrentLine), + GB_CONSTANT("ShowLineNumbers", "i", GEditor::ShowLineNumbers), + GB_CONSTANT("HighlightBraces", "i", GEditor::HighlightBraces), + GB_CONSTANT("HighlightImmediately", "i", GEditor::HighlightImmediately), + GB_CONSTANT("ShowDots", "i", GEditor::ShowDots), + GB_CONSTANT("HideMargin", "i", GEditor::HideMargin), + GB_CONSTANT("NoFolding", "i", GEditor::NoFolding), + GB_CONSTANT("AlwaysShowCursor", "i", GEditor::AlwaysShowCursor), + + GB_METHOD("_new", NULL, CEDITOR_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, CEDITOR_free, NULL), + GB_STATIC_METHOD("_exit", NULL, CEDITOR_exit, NULL), + + GB_STATIC_PROPERTY("BreakpointPicture", "Picture", Editor_BreakpointPicture), + GB_STATIC_PROPERTY("BookmarkPicture", "Picture", Editor_BookmarkPicture), + + GB_PROPERTY("View", "Editor", CEDITOR_view), + + GB_PROPERTY_READ("Line", "i", CEDITOR_line), + GB_PROPERTY_READ("Column", "i", CEDITOR_column), + GB_METHOD("Goto", NULL, CEDITOR_goto, "(Line)i(Column)i[(Center)b]"), + + GB_PROPERTY_SELF("Flags", ".Editor.Flags"), + + GB_CONSTANT("NoLimit", "i", LIMIT_NONE), + GB_CONSTANT("LineLimit", "i", LIMIT_LINE), + GB_CONSTANT("BlendLimit","i", LIMIT_BLEND), + GB_CONSTANT("BackgroundLimit", "i", LIMIT_BACKGROUND), + + /*GB_PROPERTY("ShowLimits", "i", Editor_ShowLimits), + GB_PROPERTY("ShowModifiedLines", "b", Editor_ShowModifiedLines), + GB_PROPERTY("ShowCurrentLine", "b", Editor_ShowCurrentLines), + GB_PROPERTY("ShowLineNumbers", "b", Editor_ShowLineNumbers), + GB_PROPERTY("HighlightBraces", "b", Editor_HighlightBraces), + GB_PROPERTY("HighlightImmediately", "b", Editor_HighlightImmediately), + GB_PROPERTY("ShowDots", "b", Editor_ShowDots), + GB_PROPERTY("HideMargin", "b", Editor_HideMargin), + GB_PROPERTY("BlinkCursor", "b", Editor_BlinkCursor),*/ + + GB_METHOD("Indent", NULL, CEDITOR_indent, NULL), + GB_METHOD("Unindent", NULL, CEDITOR_unindent, NULL), + GB_METHOD("Copy", NULL, CEDITOR_copy, NULL), + GB_METHOD("Cut", NULL, CEDITOR_cut, NULL), + GB_METHOD("Paste", NULL, CEDITOR_paste, NULL), + GB_METHOD("Undo", NULL, CEDITOR_undo, NULL), + GB_METHOD("Redo", NULL, CEDITOR_redo, NULL), + + //GB_METHOD("Command", NULL, Editor_command, "(Command)s"), + + GB_PROPERTY_SELF("Styles", ".Editor.Styles"), + + GB_METHOD("Select", NULL, CEDITOR_select, "(StartLine)i(StartColumn)i(EndLine)i(EndColumn)i"), + GB_METHOD("SelectAll", NULL, CEDITOR_select_all, NULL), + + GB_PROPERTY_READ("CursorX", "i", CEDITOR_cursor_x), + GB_PROPERTY_READ("CursorY", "i", CEDITOR_cursor_y), + GB_METHOD("ToPosX", "i", CEDITOR_to_pos_x, "(Column)i[(Row)i]"), + GB_METHOD("PosToLine","i", CEDITOR_pos_to_line, "(Y)i"), + GB_METHOD("PosToColumn","i", CEDITOR_pos_to_column, "(X)i(Y)i"), + GB_PROPERTY_READ("LineHeight", "i", CEDITOR_line_height), + GB_PROPERTY_READ("CharWidth", "i", CEDITOR_char_width), + + GB_PROPERTY("Border", "b", CEDITOR_border), + GB_PROPERTY("ScrollBar", "i", Editor_ScrollBar), + //GB_PROPERTY("Background", "i", CEDITOR_background), + + GB_METHOD("ExpandAll", NULL, Editor_ExpandAll, NULL), + GB_METHOD("CollapseAll", NULL, Editor_CollapseAll, NULL), + + GB_METHOD("HighlightAll", NULL, Editor_HighlightAll, NULL), + GB_PROPERTY("Text", "s", Editor_Text), + GB_PROPERTY_READ("Length", "i", Editor_Length), + GB_PROPERTY("EndOfLine", "i", Editor_EndOfLine), + + GB_PROPERTY("Highlight", "i", CEDITOR_highlight), + GB_PROPERTY("KeywordsUseUpperCase", "b", CEDITOR_keywords_ucase), + GB_METHOD("ShowString", NULL, CEDITOR_show_string, "[(String)s(IgnoreCase)b]"), + //GB_METHOD("ShowWord", NULL, CEDITOR_show_word, "[(Line)i(Column)i(Length)i]"), + + GB_PROPERTY("TabSize", "i", CEDITOR_tab_length), + GB_PROPERTY("LineNumberOffset", "i", Editor_LineOffset), + GB_METHOD("Reset", NULL, CEDITOR_reset, NULL), + + GB_PROPERTY("ReadOnly", "b", CEDITOR_read_only), + GB_PROPERTY("Overwrite", "b", CEDITOR_overwrite), + + GB_METHOD("Clear", NULL, CEDITOR_clear, NULL), + + GB_METHOD("Insert", NULL, CEDITOR_insert, "(Text)s[(Line)i(Column)i]"), + GB_METHOD("Print", NULL, CEDITOR_print, "(Text)s[(Line)i(Column)i]"), + GB_METHOD("Remove", NULL, CEDITOR_remove, "(Line)i(Column)i(EndLine)i(EndColumn)i"), + GB_METHOD("Begin", NULL, CEDITOR_begin, NULL), + GB_METHOD("End", NULL, CEDITOR_end, NULL), + + GB_PROPERTY_READ("Selected", "b", CEDITOR_selected), + GB_PROPERTY_READ("Selection", ".Editor.Selection", CEDITOR_sel), + GB_PROPERTY_SELF("Lines", ".Editor.Lines"), + + GB_METHOD("FindNextBreakpoint", "i", CEDITOR_find_next_breakpoint, "(Line)i"), + GB_METHOD("FindNextWord", "i", CEDITOR_find_next_word, "(Word)s(Line)i"), + GB_METHOD("FindNextLimit", "i", CEDITOR_find_next_limit, "(Line)i"), + //GB_METHOD("FindPreviousLimit", "i", CEDITOR_find_next_limit, "(Line)i"), + + GB_PROPERTY("Breakpoints", "Integer[]", Editor_Breakpoints), + GB_PROPERTY("Bookmarks", "Integer[]", Editor_Bookmarks), + GB_PROPERTY("CurrentLine", "i", Editor_CurrentLine), + + GB_PROPERTY("ScrollX", "i", Editor_ScrollX), + GB_PROPERTY("ScrollY", "i", Editor_ScrollY), + GB_METHOD("Scroll", NULL, Editor_Scroll, "(X)i(Y)i"), + + GB_PROPERTY("Mouse", "i", Editor_Mouse), + + GB_EVENT("Cursor", NULL, NULL, &EVENT_Cursor), + GB_EVENT("Scroll", NULL, NULL, &EVENT_Scroll), + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + GB_EVENT("Highlight", NULL, NULL, &EVENT_Highlight), + GB_EVENT("Margin", NULL, "(LineNumber)i", &EVENT_Margin), + + EDITOR_DESCRIPTION, + + GB_END_DECLARE +}; + + +CEditor CEditor::manager; + +void CEditor::changed(void) +{ + void *object = QT.GetObject((QWidget *)sender()); + GB.Raise(object, EVENT_Change, 0); +} + +static void post_event(void *_object, int event) +{ + GB.Raise(THIS, event, 0); + GB.Unref(&_object); +} + +void CEditor::moved(void) +{ + void *_object = QT.GetObject((QWidget *)sender()); + GB.Ref(THIS); + GB.Post2((GB_CALLBACK)post_event, (intptr_t)THIS, EVENT_Cursor); +} + +void CEditor::scrolled(int, int) +{ + void *_object = QT.GetObject((QWidget *)sender()); + GB.Ref(THIS); + GB.Post2((GB_CALLBACK)post_event, (intptr_t)THIS, EVENT_Scroll); +} + +void CEditor::marginDoubleClicked(int line) +{ + void *object = QT.GetObject((QWidget *)sender()); + GB.Raise(object, EVENT_Margin, 1, GB_T_INTEGER, line); +} + diff --git a/gb.qt4/src/ext/CEditor.h b/gb.qt4/src/ext/CEditor.h new file mode 100644 index 00000000..6ef473d3 --- /dev/null +++ b/gb.qt4/src/ext/CEditor.h @@ -0,0 +1,80 @@ +/*************************************************************************** + + CEditor.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CEDITOR_H +#define __CEDITOR_H + +#include "gambas.h" +#include "../gb.qt.h" + +#include "gdocument.h" +#include "gview.h" + +#ifndef __CEDITOR_CPP + +extern GB_DESC CHighlightDesc[]; +extern GB_DESC CEditorLineDesc[]; +extern GB_DESC CEditorLinesDesc[]; +extern GB_DESC CEditorSelectionDesc[]; +extern GB_DESC CEditorStyleDesc[]; +extern GB_DESC CEditorStylesDesc[]; +extern GB_DESC CEditorFlagsDesc[]; +extern GB_DESC CEditorDesc[]; + +#else + +#define THIS ((CEDITOR *)_object) +#define WIDGET ((GEditor *)((QT_WIDGET *)_object)->widget) +#define DOC (WIDGET->getDocument()) +#define MANAGER &CEditor::manager + +#endif + +typedef + struct { + QT_WIDGET widget; + void *view; + int line; + GB_FUNCTION highlight; + bool terminal; + } + CEDITOR; + +class CEditor : public QObject +{ + Q_OBJECT + +public: + + static CEditor manager; + +public slots: + + void changed(void); + void moved(void); + void scrolled(int, int); + //void marginClicked(int); + void marginDoubleClicked(int); +}; + +#endif diff --git a/gb.qt4/src/ext/CLCDNumber.cpp b/gb.qt4/src/ext/CLCDNumber.cpp new file mode 100644 index 00000000..21e22a37 --- /dev/null +++ b/gb.qt4/src/ext/CLCDNumber.cpp @@ -0,0 +1,178 @@ +/*************************************************************************** + + CLCDNumber.cpp + + (c) 2002-2003 Nigel Gerrard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CLCDNUMBER_CPP + +#include "main.h" + +#include +#include + +#include "gambas.h" + +#include "CLCDNumber.h" + +/* #define DEBUG_CLCDNUMBER */ + + +BEGIN_METHOD(CLCDNUMBER_new, GB_OBJECT parent) + + QLCDNumber *wid = new QLCDNumber(QT.GetContainer(VARG(parent))); + + QT.InitWidget(wid, _object, false); + //QT.SetBackgroundRole(_object, QColorGroup::Base); + + wid->setFrameStyle(QFrame::NoFrame); + + wid->show(); + +END_METHOD + +BEGIN_PROPERTY(CLCDNUMBER_value) + + if (READ_PROPERTY) + GB.ReturnFloat(WIDGET->value()); + else + WIDGET->display(VPROP(GB_FLOAT)); + +END_PROPERTY + +BEGIN_PROPERTY(CLCDNUMBER_digits) + + if (READ_PROPERTY) + #if (HAVE_QTEXT_COMPONENT) + GB.ReturnInteger(WIDGET->numDigits()); + #else + GB.ReturnInteger(WIDGET->digitCount()); + #endif + else + { + int n = VPROP(GB_INTEGER); + + if (n < 1) + n = 1; + else if (n > 32) + n = 32; + + #if (HAVE_QTEXT_COMPONENT) + WIDGET->setNumDigits(n); + #else + WIDGET->setDigitCount(n); + #endif + /* Increasing the number of digits does not redisplay value */ + WIDGET->repaint(); + WIDGET->display(WIDGET->value()); + } + +END_PROPERTY + +BEGIN_PROPERTY(CLCDNUMBER_decimalpoint) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->smallDecimalPoint()); + else + { + WIDGET->setSmallDecimalPoint(VPROP(GB_BOOLEAN)); + WIDGET->repaint(); + WIDGET->display(WIDGET->value()); /* Solves issue where decimal disappears */ + } + +END_PROPERTY + +BEGIN_PROPERTY(CLCDNUMBER_overflow) + + GB.ReturnBoolean(WIDGET->checkOverflow(WIDGET->value())); + +END_PROPERTY + + +BEGIN_PROPERTY(CLCDNUMBER_mode) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->mode()); + else + { + switch (VPROP(GB_INTEGER)) + { + case QLCDNumber::Hex: WIDGET->setHexMode(); break; + case QLCDNumber::Dec: WIDGET->setDecMode(); break; + //case QLCDNumber::OCT: WIDGET->setOctMode(); break; + case QLCDNumber::Bin: WIDGET->setBinMode(); break; + } + WIDGET->repaint(); + WIDGET->display(WIDGET->value()); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CLCDNUMBER_segmentStyle) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->segmentStyle()); + else + { + switch VPROP(GB_INTEGER) + { + case QLCDNumber::Outline: WIDGET->setSegmentStyle(QLCDNumber::Outline); break; + case QLCDNumber::Filled: WIDGET->setSegmentStyle(QLCDNumber::Filled); break; + case QLCDNumber::Flat: WIDGET->setSegmentStyle(QLCDNumber::Flat); break; + } + } + +END_PROPERTY + +BEGIN_PROPERTY(CLCDNUMBER_border) + + QT.FullBorderProperty(_object, _param); + +END_PROPERTY + + +GB_DESC CLCDNumberDesc[] = +{ + GB_DECLARE("LCDNumber", sizeof(CLCDNUMBER)), GB_INHERITS("Control"), + + GB_CONSTANT("Hexadecimal","i", QLCDNumber::Hex), + GB_CONSTANT("Decimal","i", QLCDNumber::Dec), + //GB_CONSTANT("Octal","i", QLCDNumber::OCT), + GB_CONSTANT("Binary","i", QLCDNumber::Bin), + + GB_CONSTANT("Outline","i", QLCDNumber::Outline), + GB_CONSTANT("Filled","i", QLCDNumber::Filled), + GB_CONSTANT("Flat","i", QLCDNumber::Flat), + + GB_METHOD("_new", NULL, CLCDNUMBER_new, "(Parent)Container;"), + + GB_PROPERTY("Value", "f", CLCDNUMBER_value), + GB_PROPERTY("Mode", "i", CLCDNUMBER_mode), + GB_PROPERTY("SmallDecimalPoint", "b", CLCDNUMBER_decimalpoint), + GB_PROPERTY_READ("Overflow", "b", CLCDNUMBER_overflow), + GB_PROPERTY("Digits", "i", CLCDNUMBER_digits), + GB_PROPERTY("Border", "i", CLCDNUMBER_border), + GB_PROPERTY("Style", "i", CLCDNUMBER_segmentStyle), + + LCDNUMBER_DESCRIPTION, + + GB_END_DECLARE +}; diff --git a/gb.qt4/src/ext/CLCDNumber.h b/gb.qt4/src/ext/CLCDNumber.h new file mode 100644 index 00000000..a467a84b --- /dev/null +++ b/gb.qt4/src/ext/CLCDNumber.h @@ -0,0 +1,43 @@ +/*************************************************************************** + + CLCDNumber.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CLCDNUMBER_H +#define __CLCDNUMBER_H + +#include "gambas.h" +#include "../gb.qt.h" + +#ifndef __CLCDNUMBER_C +extern GB_DESC CLCDNumberDesc[]; +#endif + +typedef + struct { + QT_WIDGET widget; + } + CLCDNUMBER; + +#define THIS ((CLCDNUMBER *)_object) +#define WIDGET ((QLCDNumber *)((QT_WIDGET *)_object)->widget) + +#endif diff --git a/gb.qt4/src/ext/CTextEdit.cpp b/gb.qt4/src/ext/CTextEdit.cpp new file mode 100644 index 00000000..8e5cfca9 --- /dev/null +++ b/gb.qt4/src/ext/CTextEdit.cpp @@ -0,0 +1,734 @@ +/*************************************************************************** + + CTextEdit.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTEXTEDIT_CPP + +#include +#include +#include +#include +#include +#include +#include + +#include "gambas.h" +#include "main.h" +#include "../CConst.h" +#include "../CFont.h" +#include "CTextEdit.h" + + +DECLARE_EVENT(EVENT_Change); +DECLARE_EVENT(EVENT_Cursor); + +#if 0 +static void to_pos(Q3TextEdit *wid, int par, int car, int *pos) +{ + int i, l; + int p = 0; + + for (i = 0; i < par; i++) + { + l = wid->paragraphLength(i); + if (l < 0) + break; + p += l + 1; + } + + *pos = p + car; +} + + +static void from_pos(Q3TextEdit *wid, int pos, int *par, int *car) +{ + int i; + int l; + + for (i = 0; i <= wid->paragraphs(); i++) + { + l = wid->paragraphLength(i); + if (l < 0) + { + pos = wid->length(); + i--; + break; + } + if (pos <= l) + break; + pos -= l + 1; + } + + *par = i; + *car = pos; +} + + +static void look_pos(Q3TextEdit *wid, int *line, int *col) +{ + if (*line == -1) + *line = wid->paragraphs(); + + if (*col == -1) + *col = wid->paragraphLength(*line); +} + + +static void get_selection(Q3TextEdit *wid, int *start, int *length) +{ + int pStart, iStart, pEnd, iEnd; + int posEnd; + + wid->getSelection(&pStart, &iStart, &pEnd, &iEnd); + + if (pStart < 0) + { + wid->getCursorPosition(&pStart, &iStart); + to_pos(wid, pStart, iStart, start); + *length = 0; + return; + } + + to_pos(wid, pStart, iStart, start); + to_pos(wid, pEnd, iEnd, &posEnd); + + *length = posEnd - *start; +} +#endif + +static int get_length(void *_object) +{ + if (THIS->length < 0) + { + QTextBlock block = WIDGET->document()->begin(); + int len = 0; + + while (block.isValid()) + { + len += block.length(); + block = block.next(); + } + + THIS->length = len - 1; + } + + return THIS->length; +} + +static int get_column(void *_object) +{ + QTextCursor cursor = WIDGET->textCursor(); + return cursor.position() - cursor.block().position(); +} + +static void to_pos(QTextEdit *wid, int par, int car, int *pos) +{ + QTextCursor cursor = wid->textCursor(); + QTextBlock block = cursor.block(); + int p = 0; + + while (par) + { + if (!block.isValid()) + break; + p += block.length() + 1; + block = block.next(); + par--; + } + + *pos = p + car; +} + + +static void from_pos(void *_object, int pos, int *par, int *car) +{ + QTextCursor cursor = WIDGET->textCursor(); + + if (pos >= get_length(THIS)) + cursor.movePosition(QTextCursor::End); + else + cursor.setPosition(pos); + + *par = cursor.blockNumber(); + *car = cursor.position() - cursor.block().position(); +} + + +static void get_selection(QTextEdit *wid, int *start, int *length) +{ + QTextCursor cursor = wid->textCursor(); + + *start = cursor.selectionStart(); + *length = cursor.selectionEnd() - *start; +} + + +/** MyTextEdit *************************************************************/ + +MyTextEdit::MyTextEdit(QWidget *parent) : QTextEdit(parent) +{ + setTabChangesFocus(true); + //viewport()->setMouseTracking(true); +} + +MyTextEdit::~MyTextEdit() +{ +} + +void MyTextEdit::emitLinkClicked(const QString &s) +{ + //d->textOrSourceChanged = FALSE; + emit linkClicked( s ); + //if ( !d->textOrSourceChanged ) + // setSource( s ); +} + +/** TextArea ***************************************************************/ + +BEGIN_PROPERTY(CTEXTAREA_text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->document()->toPlainText()); + else + { + WIDGET->document()->setPlainText(QSTRING_PROP()); + //THIS->length = -1; + } + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_rich_text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->document()->toHtml("utf-8")); + else + WIDGET->document()->setHtml(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_length) + + GB.ReturnInteger(get_length(THIS)); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_read_only) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->isReadOnly()); + else + WIDGET->setReadOnly(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_wrap) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->lineWrapMode() != QTextEdit::NoWrap); + else + WIDGET->setLineWrapMode(VPROP(GB_BOOLEAN) ? QTextEdit::WidgetWidth : QTextEdit::NoWrap); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_column) + + QTextCursor cursor = WIDGET->textCursor(); + + if (READ_PROPERTY) + //GB.ReturnInteger(WIDGET->textCursor().columnNumber()); + GB.ReturnInteger(get_column(THIS)); + else + { + int col = VPROP(GB_INTEGER); + + if (col <= 0) + cursor.movePosition(QTextCursor::QTextCursor::StartOfBlock); + else if (col >= cursor.block().length()) + cursor.movePosition(QTextCursor::QTextCursor::EndOfBlock); + else + cursor.setPosition(cursor.block().position() + col); + + WIDGET->setTextCursor(cursor); + } + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_line) + + QTextCursor cursor = WIDGET->textCursor(); + + if (READ_PROPERTY) + GB.ReturnInteger(cursor.blockNumber()); + else + { + int col = get_column(THIS); + int line = VPROP(GB_INTEGER); + + if (line < 0) + cursor.movePosition(QTextCursor::Start); + else if (line >= WIDGET->document()->blockCount()) + cursor.movePosition(QTextCursor::End); + else + { + cursor.setPosition(WIDGET->document()->findBlockByNumber(line).position()); + if (col > 0) + { + if (col >= cursor.block().length()) + cursor.movePosition(QTextCursor::QTextCursor::EndOfBlock); + else + cursor.setPosition(cursor.block().position() + col); + } + } + + WIDGET->setTextCursor(cursor); + } + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_pos) + + if (READ_PROPERTY) + { + GB.ReturnInteger(WIDGET->textCursor().position()); + } + else + { + int pos = VPROP(GB_INTEGER); + QTextCursor cursor = WIDGET->textCursor(); + + if (pos >= get_length(THIS)) + cursor.movePosition(QTextCursor::End); + else + cursor.setPosition(pos); + + WIDGET->setTextCursor(cursor); + } + +END_PROPERTY + +BEGIN_METHOD_VOID(CTEXTAREA_clear) + + WIDGET->clear(); + +END_METHOD + +BEGIN_METHOD(CTEXTAREA_insert, GB_STRING text) + + WIDGET->textCursor().insertText(QSTRING_ARG(text)); + +END_METHOD + + +/** .TextEdit.Selection ****************************************************/ + +BEGIN_PROPERTY(CTEXTAREA_sel_text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->textCursor().selection().toPlainText()); + else + WIDGET->textCursor().insertText(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_sel_rich_text) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->textCursor().selection().toHtml()); + else + WIDGET->textCursor().insertFragment(QTextDocumentFragment::fromHtml(QSTRING_PROP())); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_sel_length) + + int start, length; + + get_selection(WIDGET, &start, &length); + GB.ReturnInteger(length); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTAREA_sel_start) + + int start, length; + + get_selection(WIDGET, &start, &length); + GB.ReturnInteger(start); + +END_PROPERTY + +BEGIN_METHOD_VOID(CTEXTAREA_sel_clear) + + QTextCursor cursor = WIDGET->textCursor(); + cursor.clearSelection(); + WIDGET->setTextCursor(cursor); + +END_METHOD + +BEGIN_PROPERTY(CTEXTAREA_selected) + + GB.ReturnBoolean(WIDGET->textCursor().hasSelection()); + +END_PROPERTY + +BEGIN_METHOD(CTEXTAREA_sel_select, GB_INTEGER start; GB_INTEGER length) + + if (MISSING(start) && MISSING(length)) + WIDGET->textCursor().select(QTextCursor::Document); + else if (!MISSING(start) && !MISSING(length)) + { + QTextCursor cursor = WIDGET->textCursor(); + + cursor.setPosition(VARG(start)); + cursor.setPosition(VARG(start) + VARG(length), QTextCursor::KeepAnchor); + + WIDGET->setTextCursor(cursor); + } + +END_METHOD + +BEGIN_METHOD_VOID(CTEXTAREA_sel_all) + + QTextCursor cursor = WIDGET->textCursor(); + cursor.select(QTextCursor::Document); + WIDGET->setTextCursor(cursor); + +END_METHOD + +/***************************************************************************/ + +BEGIN_METHOD(CTEXTAREA_to_pos, GB_INTEGER line; GB_INTEGER col) + + int pos; + + to_pos(WIDGET, VARG(line), VARG(col), &pos); + + GB.ReturnInteger(pos); + +END_METHOD + +BEGIN_METHOD(CTEXTAREA_to_line, GB_INTEGER pos) + + int line, col; + + from_pos(THIS, VARG(pos), &line, &col); + + GB.ReturnInteger(line); + +END_METHOD + +BEGIN_METHOD(CTEXTAREA_to_col, GB_INTEGER pos) + + int line, col; + + from_pos(THIS, VARG(pos), &line, &col); + + GB.ReturnInteger(col); + +END_METHOD + +BEGIN_METHOD_VOID(CTEXTAREA_copy) + + WIDGET->copy(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_cut) + + WIDGET->cut(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_paste) + + WIDGET->paste(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_undo) + + WIDGET->undo(); + +END_METHOD + + +BEGIN_METHOD_VOID(CTEXTAREA_redo) + + WIDGET->redo(); + +END_METHOD + +BEGIN_METHOD_VOID(CTEXTAREA_ensure_visible) + + WIDGET->ensureCursorVisible(); + +END_METHOD + +BEGIN_PROPERTY(CTEXTAREA_border) + + QT.BorderProperty(_object, _param); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTAREA_scrollbar) + + QT.ScrollBarProperty(_object, _param); + +END_PROPERTY + + +/** TextEdit ***************************************************************/ + +BEGIN_METHOD(CTEXTEDIT_new, GB_OBJECT parent) + + QTextEdit *wid = new QTextEdit(QT.GetContainer(VARG(parent))); + + QObject::connect(wid, SIGNAL(textChanged()), &CTextArea::manager, SLOT(changed())); + QObject::connect(wid, SIGNAL(cursorPositionChanged()), &CTextArea::manager, SLOT(cursor())); + + wid->setLineWrapMode(QTextEdit::NoWrap); + + QT.InitWidget(wid, _object, true); + QT.SetWheelFlag(_object); + + THIS->length = -1; + +END_METHOD + +BEGIN_PROPERTY(CTEXTEDIT_scroll_x) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->horizontalScrollBar()->value()); + else + WIDGET->horizontalScrollBar()->setValue(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_scroll_y) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->verticalScrollBar()->value()); + else + WIDGET->verticalScrollBar()->setValue(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_text_width) + + if (WIDGET->document()->isEmpty()) + GB.ReturnInteger(0); + else + GB.ReturnInteger(WIDGET->document()->documentLayout()->documentSize().toSize().width()); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_text_height) + + if (WIDGET->document()->isEmpty()) + GB.ReturnInteger(0); + else + GB.ReturnInteger(WIDGET->document()->documentLayout()->documentSize().toSize().height()); + +END_PROPERTY + +/***************************************************************************/ + +BEGIN_PROPERTY(CTEXTEDIT_format_alignment) + + if (READ_PROPERTY) + GB.ReturnInteger(QT.Alignment(WIDGET->alignment() + Qt::AlignVCenter, ALIGN_NORMAL, false)); + else + WIDGET->setAlignment((Qt::Alignment)(QT.Alignment(VPROP(GB_INTEGER), ALIGN_NORMAL, true) & Qt::AlignHorizontal_Mask)); + +END_PROPERTY + + +/*BEGIN_PROPERTY(CTEXTEDIT_format_position) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->verticalAlignment()); + else + WIDGET->setVerticalAlignment((QTextEdit::VerticalAlignment)VPROP(GB_INTEGER)); + +END_PROPERTY*/ + +static void set_font(QFont &font, void *_object = 0) +{ + WIDGET->setCurrentFont(font); +} + +BEGIN_PROPERTY(CTEXTEDIT_format_font) + + if (READ_PROPERTY) + GB.ReturnObject(QT.CreateFont(WIDGET->currentFont(), set_font, _object)); + else + QT.SetFont(set_font, VPROP(GB_OBJECT), THIS); + +END_PROPERTY + + +BEGIN_PROPERTY(CTEXTEDIT_format_color) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->textColor().rgb() & 0xFFFFFF); + else + WIDGET->setTextColor(QColor((QRgb)VPROP(GB_INTEGER))); + +END_PROPERTY + +BEGIN_PROPERTY(CTEXTEDIT_format_background) + + if (READ_PROPERTY) + GB.ReturnInteger(WIDGET->textBackgroundColor().rgb() & 0xFFFFFF); + else + WIDGET->setTextBackgroundColor(QColor((QRgb)VPROP(GB_INTEGER))); + +END_PROPERTY + +/***************************************************************************/ + +GB_DESC CTextEditFormatDesc[] = +{ + GB_DECLARE(".TextEdit.Format", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Alignment", "i", CTEXTEDIT_format_alignment), + //GB_PROPERTY("Position", "i", CTEXTEDIT_format_position), + GB_PROPERTY("Font", "Font", CTEXTEDIT_format_font), + GB_PROPERTY("Color", "i", CTEXTEDIT_format_color), + GB_PROPERTY("Background", "i", CTEXTEDIT_format_background), + + GB_END_DECLARE +}; + +GB_DESC CTextEditSelectionDesc[] = +{ + GB_DECLARE(".TextEdit.Selection", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Text", "s", CTEXTAREA_sel_text), + GB_PROPERTY("RichText", "s", CTEXTAREA_sel_rich_text), + GB_PROPERTY_READ("Length", "i", CTEXTAREA_sel_length), + GB_PROPERTY_READ("Start", "i", CTEXTAREA_sel_start), + GB_METHOD("Hide", NULL, CTEXTAREA_sel_clear, NULL), + + GB_END_DECLARE +}; + +GB_DESC CTextEditDesc[] = +{ + GB_DECLARE("TextEdit", sizeof(CTEXTEDIT)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CTEXTEDIT_new, "(Parent)Container;"), + + //GB_CONSTANT("Normal", "i", QTextEdit::AlignNormal), + //GB_CONSTANT("SubScript", "i", QTextEdit::AlignSubScript), + //GB_CONSTANT("SuperScript", "i", QTextEdit::AlignSuperScript), + + GB_PROPERTY("ReadOnly", "b", CTEXTAREA_read_only), + + GB_METHOD("Clear", NULL, CTEXTAREA_clear, NULL), + + GB_PROPERTY("Text", "s", CTEXTAREA_text), + GB_PROPERTY("RichText", "s", CTEXTAREA_rich_text), + GB_METHOD("Insert", NULL, CTEXTAREA_insert, "(Text)s"), + + GB_PROPERTY("Paragraph", "i", CTEXTAREA_line), + GB_PROPERTY("Index", "i", CTEXTAREA_column), + GB_PROPERTY("Pos", "i", CTEXTAREA_pos), + //GB_PROPERTY_READ("Line", "i", CTEXTEDIT_line), + + GB_METHOD("ToPos", "i", CTEXTAREA_to_pos, "(Paragraph)i(Index)i"), + GB_METHOD("ToParagraph", "i", CTEXTAREA_to_line, "(Pos)i"), + GB_METHOD("ToIndex", "i", CTEXTAREA_to_col, "(Pos)i"), + + GB_METHOD("EnsureVisible", NULL, CTEXTAREA_ensure_visible, NULL), + + GB_PROPERTY_SELF("Selection", ".TextEdit.Selection"), + GB_METHOD("Select", NULL, CTEXTAREA_sel_select, "[(Start)i(Length)i]"), + GB_METHOD("SelectAll", NULL, CTEXTAREA_sel_all, NULL), + GB_METHOD("Unselect", NULL, CTEXTAREA_sel_clear, NULL), + GB_PROPERTY_READ("Selected", "b", CTEXTAREA_selected), + + GB_METHOD("Copy", NULL, CTEXTAREA_copy, NULL), + GB_METHOD("Cut", NULL, CTEXTAREA_cut, NULL), + GB_METHOD("Paste", NULL, CTEXTAREA_paste, NULL), + GB_METHOD("Undo", NULL, CTEXTAREA_undo, NULL), + GB_METHOD("Redo", NULL, CTEXTAREA_redo, NULL), + + GB_PROPERTY("Border", "b", CTEXTAREA_border), + GB_PROPERTY("ScrollBar", "i", CTEXTAREA_scrollbar), + GB_PROPERTY("Wrap", "b", CTEXTAREA_wrap), + + GB_PROPERTY("ScrollX", "i", CTEXTEDIT_scroll_x), + GB_PROPERTY("ScrollY", "i", CTEXTEDIT_scroll_y), + + GB_PROPERTY_READ("TextWidth", "i", CTEXTEDIT_text_width), + GB_PROPERTY_READ("TextHeight", "i", CTEXTEDIT_text_height), + + GB_PROPERTY_SELF("Format", ".TextEdit.Format"), + + GB_EVENT("Change", NULL, NULL, &EVENT_Change), + GB_EVENT("Cursor", NULL, NULL, &EVENT_Cursor), + //GB_EVENT("Link", NULL, "(Path)s", &EVENT_Link), + + TEXTEDIT_DESCRIPTION, + + GB_END_DECLARE +}; + + +/** CTextArea **************************************************************/ + +CTextArea CTextArea::manager; + +void CTextArea::changed(void) +{ + void *_object = QT.GetObject((QWidget *)sender()); + GB.Raise(THIS, EVENT_Change, 0); +} + +void CTextArea::cursor(void) +{ + void *_object = QT.GetObject((QWidget *)sender()); + GB.Raise(THIS, EVENT_Cursor, 0); +} + +#if 0 +void CTextArea::link(const QString &path) +{ + void *_object = QT.GetObject((QWidget *)sender()); + + //THIS_EDIT->change = false; + + GB.Raise(_object, EVENT_Link, 1, + GB_T_STRING, TO_UTF8(path), 0); + + /*if (!THIS_EDIT->change) + { + // This cancels the click on the link (see qt source code) + WIDGET->setSource(WIDGET->source()); + }*/ +} +#endif diff --git a/gb.qt4/src/ext/CTextEdit.h b/gb.qt4/src/ext/CTextEdit.h new file mode 100644 index 00000000..98418e1b --- /dev/null +++ b/gb.qt4/src/ext/CTextEdit.h @@ -0,0 +1,86 @@ +/*************************************************************************** + + CTextEdit.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTEXTEDIT_H +#define __CTEXTEDIT_H + +#include "gambas.h" +#include "../gb.qt.h" + +#include + +#ifndef __CTEXTEDIT_CPP +extern GB_DESC CTextEditSelectionDesc[]; +extern GB_DESC CTextEditFormatDesc[]; +extern GB_DESC CTextEditDesc[]; +#else + +#define WIDGET ((QTextEdit *)((QT_WIDGET *)_object)->widget) +#define MYTEXTEDIT ((MyTextEdit *)((QT_WIDGET *)_object)->widget) +#define THIS ((CTEXTEDIT *)_object) + +#endif + +typedef + struct { + QT_WIDGET widget; + int length; + bool change; + } + CTEXTEDIT; + +class MyTextEdit: public QTextEdit +{ + Q_OBJECT + +public: + MyTextEdit(QWidget *parent = 0); + ~MyTextEdit(); + +signals: + //void highlighted(const QString&); + void linkClicked(const QString&); + +private: + //void popupDetail( const QString& contents, const QPoint& pos ); + bool linksEnabled() const { return true; } + //void emitHighlighted( const QString &s ); + void emitLinkClicked( const QString &s ); +}; + +class CTextArea : public QObject +{ + Q_OBJECT + +public: + + static CTextArea manager; + +public slots: + + void changed(void); + void cursor(void); + //void link(const QString &); +}; + +#endif diff --git a/gb.qt4/src/ext/Makefile.am b/gb.qt4/src/ext/Makefile.am new file mode 100644 index 00000000..07c4afb6 --- /dev/null +++ b/gb.qt4/src/ext/Makefile.am @@ -0,0 +1,20 @@ +COMPONENT = gb.qt4.ext +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +gblib_LTLIBRARIES = gb.qt4.ext.la + +gb_qt4_ext_la_LIBADD = @QTEXT_LIB@ +gb_qt4_ext_la_LDFLAGS = -module @LD_FLAGS@ @QTEXT_LDFLAGS@ +gb_qt4_ext_la_CPPFLAGS = -DQT3_SUPPORT @QTEXT_INC@ -I$(top_srcdir)/share -I$(top_srcdir)/src/share + +gb_qt4_ext_la_SOURCES = \ + main.h main.cpp \ + CLCDNumber.h CLCDNumber_moc.cpp CLCDNumber.cpp \ + CDial.h CDial_moc.cpp CDial.cpp \ + garray.h garray.cpp \ + gstring.h gstring.cpp \ + gdocument.h gdocument.cpp \ + gview.h gview_moc.cpp gview.cpp \ + CEditor.h CEditor_moc.cpp CEditor.cpp \ + CTextEdit.h CTextEdit_moc.cpp CTextEdit.cpp diff --git a/gb.qt4/src/ext/garray.cpp b/gb.qt4/src/ext/garray.cpp new file mode 100644 index 00000000..2d675080 --- /dev/null +++ b/gb.qt4/src/ext/garray.cpp @@ -0,0 +1,71 @@ +/*************************************************************************** + + garray.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __G_ARRAY_CPP + +#include "garray.h" + +void GArrayImpl::clear() +{ + GB.FreeArray((void **)(void *)&array); + GB.NewArray((void **)(void *)&array, sizeof(void *), 0); + _count = 0; +} + +void *GArrayImpl::take() +{ + void *d; + + if (count() == 0) + return 0; + + d = array[count() - 1]; + GB.Remove((void **)(void *)&array, count() - 1, 1); + _count--; + return d; +} + +void GArrayImpl::insert(uint i, const void *d) +{ + GB.Insert((void **)(void *)&array, i, 1); + array[i] = (void *)d; + _count++; +} + + +void GArrayImpl::remove(uint i) +{ + GB.Remove((void **)(void *)&array, i, 1); + _count--; +} + +int GArrayImpl::find(const void *d) +{ + int i; + + for (i = 0; i < (int)count(); i++) + if (array[i] == d) + return i; + + return (-1); +} diff --git a/gb.qt4/src/ext/garray.h b/gb.qt4/src/ext/garray.h new file mode 100644 index 00000000..b35a3597 --- /dev/null +++ b/gb.qt4/src/ext/garray.h @@ -0,0 +1,112 @@ +/*************************************************************************** + + garray.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GARRAY_H +#define __GARRAY_H + +#include "gb_common.h" +#include "gambas.h" + +extern "C" GB_INTERFACE GB; + +class GArrayImpl +{ +protected: + void **array; + bool autoDelete; + uint pos; + uint _count; + +public: + GArrayImpl() { GB.NewArray((void **)(void *)&array, sizeof(void *), 0); autoDelete = false; _count = 0; } + ~GArrayImpl() { GB.FreeArray((void **)(void *)&array); } + + uint count() const { return _count; } + void setAutoDelete(bool a) { autoDelete = a; } + void *last() const { return count() ? array[count() - 1] : 0; } + void append(const void *d) { *((const void **)GB.Add((void **)(void *)&array)) = d; _count++; } + void clear(); + void *take(); + void *at(uint i) const { return array[i]; } + void insert(uint i, const void *d); + void remove(uint i); + int find(const void *d); + void *first() { pos = 0; return next(); } + void *next() { return (pos >= count()) ? 0 : array[pos++]; } +}; + +template +class GArray : GArrayImpl +{ +public: + GArray() {} + ~GArray() { clear(); } + + uint count() const { return GArrayImpl::count(); } + bool isEmpty() const { return GArrayImpl::count() == 0; } + void setAutoDelete(bool a) { GArrayImpl::setAutoDelete(a); } + type *last() const { return (type *)GArrayImpl::last(); } + void append(const type *d) { GArrayImpl::append(d); } + + void clear(); + type *take() { return (type *)GArrayImpl::take(); } + type *at(uint i) const { return (type *)GArrayImpl::at(i); } + void insert(uint i, const type *d) { GArrayImpl::insert(i, d); } + void remove(uint i); + int find(const type *d) { return GArrayImpl::find(d); } + void removeRef(const type *d); + + type *first() { return (type *)GArrayImpl::first(); } + type *next() { return (type *)GArrayImpl::next(); } +}; + + +template inline void GArray::clear() +{ + if (autoDelete) + { + for (uint i = 0; i < count(); i++) + delete (type *)array[i]; + } + + GArrayImpl::clear(); +} + +template inline void GArray::remove(uint i) +{ + if (autoDelete) + delete (type *)array[i]; + + GArrayImpl::remove(i); +} + + +template inline void GArray::removeRef(const type *d) +{ + int i = find(d); + if (i >= 0) + remove((uint )i); +} + + +#endif diff --git a/gb.qt4/src/ext/gb.qt4.ext.component b/gb.qt4/src/ext/gb.qt4.ext.component new file mode 100644 index 00000000..4a54be62 --- /dev/null +++ b/gb.qt4/src/ext/gb.qt4.ext.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.qt4.ext +Author=Nigel Gerrard,Benoît Minisini +Require=gb.qt4 diff --git a/gb.qt4/src/ext/gdocument.cpp b/gb.qt4/src/ext/gdocument.cpp new file mode 100644 index 00000000..176dcd31 --- /dev/null +++ b/gb.qt4/src/ext/gdocument.cpp @@ -0,0 +1,1786 @@ +/*************************************************************************** + + gdocument.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __G_DOCUMENT_CPP + +#include + +#include "main.h" +#include "gview.h" +#include "gdocument.h" + +//#define DEBUG_UNDO 1 + +#define GMAX(x, y) ((x) > (y) ? (x) : (y)) +#define GMIN(x, y) ((x) < (y) ? (x) : (y)) + +#define COLORIZE_FROM(_y) \ + if (colorizeFrom > (_y)) \ + { \ + colorizeFrom = (_y); \ + /*fprintf(stderr, "colorizeFrom -> %d\n", (_y));*/ \ + } + +/**---- GLine -----------------------------------------------------------*/ + +GLine::GLine() +{ + s = ""; + state = Normal; + alternate = false; + modified = false; + changed = false; + saved = false; + flag = 0; + tag = 0; + proc = false; + unicode = false; + highlight = NULL; + nobreak = false; + baptized = false; + tab = false; +} + +GLine::~GLine() +{ + GB.FreeArray(&highlight); +} + +void GLine::insert(uint pos, const GString &text) +{ + s.insert(pos, text); + if (text.hasUnicode()) + unicode = true; +} + +bool GLine::isEmpty() const +{ + uint i; + + for (i = 0; i < s.length(); i++) + { + if (!s.isSpace(i)) + return false; + } + + return true; +} + +/**---- GCommand -----------------------------------------------------------*/ + +struct GCommandDocument +{ +public: + GEditor *view; + int cx; + int cy; + int sx; + int sy; + int sx2; + int sy2; + + GCommandDocument() { } + GCommandDocument(GDocument *doc); + void process(GDocument *doc) const; + void print() const; + bool equals(GCommandDocument *o) const; +}; + +GCommandDocument::GCommandDocument(GDocument *doc) +{ + view = doc->currentView(); + + view->getCursor(&cy, &cx); + + if (doc->hasSelection()) + doc->getSelection(&sy, &sx, &sy2, &sx2, false); // TODO: store insertMode + else + sx = sy = sx2 = sy2 = -1; +} + +void GCommandDocument::process(GDocument *doc) const +{ + //qDebug("goto %d %d", cx, cy); + view->cursorGoto(cy, cx, false); + if (sx >= 0) + { + //qDebug("select %d %d %d %d", sx, sy, sx2, sy2); + doc->startSelection(view, sy, sx); + doc->endSelection(sy2, sx2); + } +} + +void GCommandDocument::print() const +{ + qDebug("- %d %d [%d %d %d %d]", cx, cy, sx, sy, sx2, sy2); +} + +bool GCommandDocument::equals(GCommandDocument *o) const +{ + return view == o->view && cx == o->cx && cy == o->cy + && sx == o->sx && sy == o->sy && sx2 == o->sx2 && sy2 == o->sy2; +} + +class GCommand +{ +public: + enum Type + { + None, Begin, End, Move, Insert, Delete, Indent, Unindent + }; + GCommandDocument info; + + virtual ~GCommand() { } + virtual Type type() const { return None; } + virtual int nest() const { return 0; } + virtual void print() const { } + virtual bool merge(GCommand *) const { return false; } + virtual void process(GDocument *doc, bool undo) const { } + virtual bool linked() const { return false; } + virtual bool remove(GCommand *) const { return false; } +}; + +class GBeginCommand: public GCommand +{ +public: + bool _linked; + + GBeginCommand(GCommandDocument *info, bool linked = false) { _linked = linked; this->info = *info; } + Type type() const { return Begin; } + void print() const { qDebug("Begin"); info.print(); } + int nest() const { return 1; } + bool linked() const { return _linked; } + void process(GDocument *doc, bool undo) const { info.process(doc); } +}; + +class GEndCommand: public GCommand +{ +public: + bool _linked; + + GEndCommand(bool linked = false) { _linked = linked; } + Type type() const { return End; } + void print() const { qDebug("End"); } + int nest() const { return -1; } + bool linked() const { return _linked; } + bool remove(GCommand *o) const { return (o->type() == Begin); } +}; + +class GDeleteCommand: public GCommand +{ +public: + int x, y, x2, y2; + GString str; + + GDeleteCommand(GCommandDocument *info, int y, int x, int y2, int x2, const GString &str) + { + this->x = x; this->y = y; this->x2 = x2; this->y2 = y2; this->str = str; + this->info = *info; + } + + Type type() const { return Delete; } + void print() const { qDebug("Delete: (%d %d)-(%d %d): '%s'", x, y, x2, y2, TO_UTF8(str.getString())); info.print(); } + + bool merge(GCommand *c) const + { + if (c->type() != type()) + return false; + + GDeleteCommand *o = (GDeleteCommand *)c; + + if (info.view != o->info.view) + return false; + + if (x2 != o->x || y2 != o->y || o->y != o->y2) + return false; + + o->str.prepend(str); + o->x = x; + o->y = y; + //o->info = info; + return true; + } + + void process(GDocument *doc, bool undo) const + { + if (undo) + doc->insert(y, x, str); + else + doc->remove(y, x, y2, x2); + + info.process(doc); + } +}; + +class GInsertCommand: public GDeleteCommand +{ +public: + GInsertCommand(GCommandDocument *info, int y, int x, int y2, int x2, const GString &str): + GDeleteCommand(info, y, x, y2, x2, str) {} + Type type() const { return Insert; } + void print() const { qDebug("Insert: (%d %d)-(%d %d): '%s'", x, y, x2, y2, TO_UTF8(str.getString())); info.print(); } + + bool merge(GCommand *c) const + { + if (c->type() != type()) + return false; + + if (str.length() && str.isNewLine(0)) + return false; + + GInsertCommand *o = (GInsertCommand *)c; + + if (o->info.view != info.view) + return false; + + if (o->str.length() && o->str.isNewLine(str.length() - 1)) + return false; + + if (x != o->x2 || y != o->y2) + return false; + + o->str += str; + o->x2 = x2; + o->y2 = y2; + return true; + } + + void process(GDocument *doc, bool undo) const + { + GDeleteCommand::process(doc, !undo); + } +}; + + +/**---- GDocument -----------------------------------------------------------*/ + +#define FOR_EACH_VIEW(_view) \ + for (GEditor *_view = views.first(); _view ; _view = views.next()) + +void GDocument::init() +{ + selector = NULL; + colorizeFrom = 0; + _disableColorize = 0; + _currentLine = -1; +} + +GDocument::GDocument() +{ + oldCount = 0; + blockUndo = false; + tabWidth = 2; + readOnly = false; + highlightMode = None; + keywordsUseUpperCase = false; + _currentView = NULL; + setEndOfLine(GB_EOL_UNIX); + + //lines = new GArray; + lines.setAutoDelete(true); + + undoList.setAutoDelete(true); + redoList.setAutoDelete(true); + + clear(); + //setText(""); //GString("Gambas\nAlmost\nMeans\nBASIC!\n")); +} + +GDocument::~GDocument() +{ +} + +void GDocument::clear() +{ + uint i; + + clearUndo(); + lines.clear(); + lines.append(new GLine); + init(); + + updateViews(); + + for (i = 0; i < views.count(); i++) + views.at(i)->reset(); +} + + +void GDocument::reset(bool saved) +{ + if (saved) + { + for (uint i = 0; i < lines.count(); i++) + { + lines.at(i)->saved |= lines.at(i)->changed; + lines.at(i)->changed = false; + } + } + else + { + for (uint i = 0; i < lines.count(); i++) + lines.at(i)->saved = lines.at(i)->changed = false; + } + + updateViews(); +} + +GString GDocument::getText() +{ + GString tmp; + + if (lines.count()) + { + for (uint i = 0; i < (lines.count() - 1); i++) + { + tmp += lines.at(i)->s; + tmp += _eol; + } + + tmp += lines.at(lines.count() - 1)->s; + + updateViews(); + } + + return tmp; +} + +GString GDocument::getSelectedText(bool insertMode) +{ + GString tmp, line; + int x1, y1, x2, y2, i; + + if (lines.count() && hasSelection()) + { + getSelection(&y1, &x1, &y2, &x2, insertMode); + + if (insertMode) + { + for (i = y1; i <= y2; i++) + { + line = lines.at(i)->s.mid(x1, x2 - x1); + while ((int)line.length() < (x2 - x1)) + line.append(' '); + + tmp += line; + if (i < y2) + tmp += _eol; + } + } + else + { + if (y1 == y2) + tmp = lines.at(y1)->s.mid(x1, x2 - x1); + else + { + tmp = lines.at(y1)->s.mid(x1); + tmp += _eol; + for (i = y1 + 1; i < y2; i++) + { + tmp += lines.at(i)->s; + tmp += _eol; + } + + tmp += lines.at(y2)->s.left(x2); + } + } + } + + return tmp; +} + +int GDocument::getIndent(int y, bool *empty) +{ + int i; + GString s = lines.at(y)->s; + bool e = true; + + for (i = 0; i < (int)s.length(); i++) + { + if (!s.isSpace(i)) + { + e = false; + break; + } + } + + if (empty) + *empty = e; + + return i; +} + +void GDocument::modifyLine(GLine *l, int y) +{ + //if (!l->modified) + // fprintf(stderr, "modifyLine: %d\n", y); + l->modified = l->changed = true; + updateLineWidth(y); + COLORIZE_FROM(y); +} + +void GDocument::updateLineWidth(int y) +{ + FOR_EACH_VIEW(v) + { + v->updateWidth(y); + } +} + +void GDocument::insertLine(int y) +{ + lines.insert(y, new GLine()); + modifyLine(lines.at(y), y); + FOR_EACH_VIEW(v) { v->lineInserted(y); } +} + +void GDocument::insert(int y, int x, const GString &text, bool doNotMove) +{ + int pos; + int len; + int pos2; + int xs, ys; + GLine *l; + int n = 1; + int nl = 0; + GString rest; + int yy; + int i, ns; + GCommandDocument info(this); + + if (readOnly || text.length() == 0) + { + xAfter = x; + yAfter = y; + return; + } + + FOR_EACH_VIEW(v) + { + v->nx = v->x; + v->ny = v->y; + } + + disableColorize(); + + while (y >= (int)lines.count()) + { + yy = (int)lines.count(); + insertLine(yy); + nl++; + } + + l = lines.at(y); + + ns = x - (int)l->s.length(); + if (ns > 0) + { + GString str; + + for (i = 0; i < ns; i++) + str.append(' '); + + insert(y, x - ns, str, doNotMove); + } + + xs = x; + ys = y; + pos = 0; + + for(;;) + { + pos2 = text.findNextLine(pos, len); + + l = lines.at(y); + + if (len > 0) + { + l->insert(x, text.mid(pos, len)); + modifyLine(l, y); + + //maxLength = GMAX(maxLength, (int)l->s.length()); + //updateLineWidth(y); + + FOR_EACH_VIEW(v) + { + if (v->ny == y && v->nx >= x) + v->nx += len; + } + + x += len; + } + + pos = pos2; + + if (pos == 0) + break; + + if (x < (int)l->s.length()) + { + rest = l->s.mid(x); + + l->s.remove(x, rest.length()); + modifyLine(l, y); + } + + FOR_EACH_VIEW(v) + { + if (v->ny >= y) + v->ny++; + } + + y++; + + insertLine(y); + nl++; + + n = -1; + x = 0; + + } + + if (n < 0 && rest.length()) + { + l->insert(x, rest); + modifyLine(l, y); + //maxLength = GMAX(maxLength, (int)l->s.length()); + //updateLineWidth(y); + } + + FOR_EACH_VIEW(v) + { + v->foldInsert(ys, nl); + } + + begin(); + addUndo(new GInsertCommand(&info, ys, xs, y, x, text)); + enableColorize(); + end(); + + updateViews(ys, n); + + yAfter = y; + xAfter = x; + + emitTextChanged(); + + if (!doNotMove) + { + FOR_EACH_VIEW(v) + { + v->cursorGoto(v->ny, v->nx, FALSE); + } + } +} + +void GDocument::removeLine(int y) +{ + lines.remove(y); + COLORIZE_FROM(y); + FOR_EACH_VIEW(v) { v->lineRemoved(y); } +} + +void GDocument::remove(int y1, int x1, int y2, int x2) +{ + GLine *l, *l2; + GString text, rest; + int y; + GCommandDocument info(this); + + yAfter = y1; + xAfter = x1; + + if (readOnly) + return; + + FOR_EACH_VIEW(v) + { + v->nx = v->x; + v->ny = v->y; + } + + disableColorize(); + + l = lines.at(y1); + + if (y1 == y2) + { + if (x2 >= x1 && x1 < lineLength(y1)) + { + text = l->s.mid(x1, x2 - x1); + + l->s.remove(x1, x2 - x1); + if (text.hasUnicode()) + l->unicode = l->s.hasUnicode(); + + modifyLine(l, y1); + + FOR_EACH_VIEW(v) + { + v->foldRemove(y1); + if (v->ny == y1 && v->nx > x1) + v->nx = GMAX(x1, v->nx - (x2 - x1)); + } + + updateViews(y1); + } + } + else + { + l2 = lines.at(y2); + text = l->s.mid(x1) + _eol; + rest = l2->s.left(x2); + + if (x1 < (int)l->s.length() || x2 < (int)l2->s.length()) + { + l->s = l->s.left(x1) + lines.at(y2)->s.mid(x2); + l->state = 0; // force highlighting of next line. + modifyLine(l, y1); + } + else + updateLineWidth(y1); + + for (y = y1 + 1; y < y2; y++) + text += lines.at(y)->s + _eol; + text += rest; + + for (y = y1 + 1; y <= y2; y++) + { + removeLine(y1 + 1); + } + + FOR_EACH_VIEW(v) + { + v->foldRemove(y1 + 1, y2); + if (v->ny > y1) + { + v->ny = GMAX(y1, v->ny - (y2 - y1)); + if (v->ny == y1) + v->nx = x1; + } + else if (v->ny == y1 && v->nx > x1) + v->nx = x1; + } + + updateViews(y1, -1); + } + + begin(); + addUndo(new GDeleteCommand(&info, y1, x1, y2, x2, text)); + enableColorize(); + end(); + + FOR_EACH_VIEW(v) + { + v->cursorGoto(v->ny, v->nx, false); + } + + emitTextChanged(); +} + +void GDocument::setText(const GString &text) +{ + bool oldReadOnly = readOnly; + int mode; + uint l; + + readOnly = false; + blockUndo = true; + + clear(); + clearUndo(); + + mode = GB_EOL_UNIX; + + l = text.length(); + for (uint i = 0; i < l; i++) + { + char c = text.at(i); + if (c == '\n') + { + break; + } + if (c == '\r') + { + if ((i < (l - 1)) && text.at(i + 1) == '\n') + mode = GB_EOL_WINDOWS; + else + mode = GB_EOL_MAC; + break; + } + } + + setEndOfLine(mode); + + undoLevel++; // Prevent textChanged emission + insert(0, 0, text, true); + reset(false); + undoLevel--; + + blockUndo = false; + readOnly = oldReadOnly; + + FOR_EACH_VIEW(v) + { + v->cursorGoto(0, 0, false); + } + + emitTextChanged(); +} + +void GDocument::unsubscribe(GEditor *view) +{ + //qDebug("unsubscribe %p -> %p", view, this); + views.removeRef(view); + + if (views.count() == 0) + delete this; + else + { + if (_currentView == view) + _currentView = views.at(0); + } +} + +void GDocument::subscribe(GEditor *view) +{ + //qDebug("subscribe %p -> %p", view, this); + views.removeRef(view); + views.append(view); + view->setNumRows(lines.count()); + view->updateContents(); + + if (!_currentView) + _currentView = view; +} + +void GDocument::updateViews(int row, int count) +{ + uint i; + + if (lines.count() > oldCount) + { + oldCount = lines.count(); + FOR_EACH_VIEW(v) + { + v->setNumRows(oldCount); + v->updateHeight(); + } + } + + /*if (maxLength != oldMaxLength) + { + oldMaxLength = maxLength; + FOR_EACH_VIEW(v) + v->updateLength(); + }*/ + + if (row < 0) + { + row = 0; + count = oldCount; + } + else if (count < 0) + { + count = oldCount - row; + } + + count = GMIN((int)oldCount - row, count); + + if (((row + count) < numLines()) && lines.at(row + count)->proc) + count++; + + FOR_EACH_VIEW(v) + { + for (i = row; i < (uint)(row + count); i++) + { + v->updateLine(i); + } + } + + if (lines.count() < oldCount) + { + oldCount = lines.count(); + FOR_EACH_VIEW(v) + { + v->setNumRows(oldCount); + v->updateHeight(); + } + } + + FOR_EACH_VIEW(v) + v->checkMatching(); +} + + +void GDocument::getSelection(int *y1, int *x1, int *y2, int *x2, bool insertMode) +{ + if (!selector) + return; + + if (ys2 >= numLines()) + { + ys2 = numLines() - 1; + if (!insertMode) + xs2 = lineLength(ys2); + } + + if (ys >= numLines()) + { + ys = numLines() - 1; + xs = lineLength(ys); + } + + if (ys2 > ys || (ys2 == ys && xs2 > xs)) + { + *y1 = ys; + *y2 = ys2; + if (x1) *x1 = xs; + if (x2) *x2 = xs2; + } + else + { + *y1 = ys2; + *y2 = ys; + if (x1) *x1 = xs2; + if (x2) *x2 = xs; + } + + if (!insertMode) + { + *x1 = QMIN(*x1, lineLength(*y1)); + *x2 = QMIN(*x2, lineLength(*y2)); + } +} + +void GDocument::startSelection(GEditor *view, int y, int x) +{ + hideSelection(); + ys = y; + xs = x; + ys2 = ys; + xs2 = xs; + selector = view; + updateViews(y); +} + +void GDocument::endSelection(int y, int x) +{ + int y1a, y2a, y1b, y2b; + + getSelection(&y1a, NULL, &y2a, NULL, true); + ys2 = y; + xs2 = x; + getSelection(&y1b, NULL, &y2b, NULL, true); + + /*if (y1a == y1b) + updateViews(GMIN(y2a, y2b), GMAX(y2a, y2b) - GMIN(y2a, y2b) + 1); + else if (y2a == y2b) + updateViews(GMIN(y1a, y1b), GMAX(y1a, y1b) - GMIN(y1a, y1b) + 1); + else*/ + updateViews(GMIN(y1a, y1b), GMAX(y2a, y2b) - GMIN(y1a, y1b) + 1); + + updateViews(y); +} + +void GDocument::hideSelection() +{ + int y1, y2; + + if (!selector) + return; + + getSelection(&y1, NULL, &y2, NULL, true); + selector = NULL; + updateViews(y1, y2 - y1 + 1); +} + +void GDocument::eraseSelection(bool insertMode) +{ + int y1, y2, x1, x2, i; + + if (!selector) + return; + + begin(); + getSelection(&y1, &x1, &y2, &x2, false); + selector = NULL; + if (!insertMode) + remove(y1, x1, y2, x2); + else + { + for (i = y1; i <= y2; i++) + remove(i, x1, i, x2); + } + end(); +} + +void GDocument::clearUndo() +{ + undoList.clear(); + redoList.clear(); + undoLevel = 0; +} + +void GDocument::addUndo(GCommand *c) +{ + if (blockUndo) + return; + + #if DEBUG_UNDO + fprintf(stderr, "addUndo: "); + c->print(); + #endif + + if (!undoList.isEmpty()) + { + if (c->merge(undoList.last())) + { + #if DEBUG_UNDO + qDebug(" MERGE"); + #endif + delete c; + return; + } + else if (c->remove(undoList.last())) + { + #if DEBUG_UNDO + qDebug(" REMOVE"); + #endif + delete c; + delete undoList.take(); + return; + } + } + + if (c->type() == GCommand::End && undoList.count() >= 2 && undoList.at(undoList.count() - 2)->type() == GCommand::Begin) + { + GCommand *o = undoList.take(); + GCommand *begin = undoList.take(); + #if DEBUG_UNDO + qDebug(" NO BEGIN / END"); + #endif + o->info = begin->info; + delete begin; + delete c; + addUndo(o); + return; + /*else + { + undoList.append(begin); + undoList.append(o); + }*/ + } + + undoList.append(c); + + if (!redoList.isEmpty()) + { + redoList.clear(); + //emit redoAvailable(false); + } +} + +void GDocument::begin(bool linked) +{ + if (undoLevel == 0) + textHasChanged = false; + undoLevel++; + if (!blockUndo && (undoLevel == 1)) + { + GCommandDocument info(this); + addUndo(new GBeginCommand(&info, linked)); + } +} + +void GDocument::end(bool linked) +{ + undoLevel--; + if (!blockUndo && undoLevel == 0) addUndo(new GEndCommand(linked)); + if (undoLevel == 0 && textHasChanged) + emitTextChanged(); +} + +void GDocument::addRedo(GCommand * c) +{ + if (blockUndo) + return; + + redoList.append(c); +} + +bool GDocument::undo() +{ + int nest; + + if (undoList.isEmpty() || isReadOnly() || blockUndo) + return true; + + disableColorize(); + + blockUndo = true; + + nest = 0; + begin(); + + #ifdef DEBUG_UNDO + qDebug("BEGIN UNDO"); + #endif + + for(;;) + { + GCommand *c = undoList.take(); + if (!c) + break; + #ifdef DEBUG_UNDO + c->print(); + #endif + c->process(this, true); + nest += c->nest(); + //if (d->undoList.isEmpty()) + // emit undoAvailable(false); + redoList.append(c); + if (!nest && !c->linked()) + break; + } + + end(); + + #ifdef DEBUG_UNDO + qDebug("END UNDO: %d", nest); + #endif + + blockUndo = false; + + enableColorize(); + + return false; +} + +bool GDocument::redo() +{ + int nest; + + if (redoList.isEmpty() || isReadOnly() || blockUndo) + return true; + + disableColorize(); + + blockUndo = true; + + nest = 0; + begin(); + + #ifdef DEBUG_UNDO + qDebug("BEGIN REDO"); + #endif + + for(;;) + { + GCommand *c = redoList.take(); + if (!c) + break; + #ifdef DEBUG_UNDO + c->print(); + #endif + c->process(this, false); + nest += c->nest(); + + /*if (d->redoList.isEmpty()) + emit redoAvailable(false); + if (d->undoList.isEmpty()) + emit undoAvailable(true);*/ + undoList.append(c); + if (!nest && !c->linked()) + break; + } + + end(); + + #ifdef DEBUG_UNDO + qDebug("END REDO: %d", nest); + #endif + + blockUndo = false; + + enableColorize(); + + return false; +} + +int GDocument::wordLeft(int y, int x, bool word) +{ + int xw = x; + GString s = lines.at(y)->s; + + if (!word) + { + while (xw > 0 && s.isSpace(xw - 1)) + xw--; + } + + if (xw > 0) + { + if (s.isWordChar(xw - 1)) + { + for(;;) + { + xw--; + if (xw <= 0 || !s.isWordChar(xw - 1)) + break; + } + } + else if (!word) + { + for(;;) + { + xw--; + if (xw <= 0 || s.isWordChar(xw - 1) || s.isSpace(xw - 1)) + break; + } + } + } + + return xw; +} + +int GDocument::wordRight(int y, int x, bool word) +{ + int xw = x; + GString s = lines.at(y)->s; + int len = s.length(); + + if (xw < len) + { + if (s.isWordChar(xw)) + { + for(;;) + { + xw++; + if (xw >= len || !s.isWordChar(xw)) + break; + } + } + else if (!word) + { + for(;;) + { + xw++; + if (xw >= len || s.isWordChar(xw) || s.isSpace(xw)) + break; + } + } + } + + if (!word) + { + while (xw < len && s.isSpace(xw)) + xw++; + } + + return xw; +} + +int GDocument::getLength() const +{ + uint i, len; + + if (lines.count()) + return 0; + + len = 0; + + for (i = 0; i < lines.count(); i++) + len += lines.at(i)->s.length() + 1; + + return (len - 1); +} + + +GString GDocument::getLine(int y) const +{ + GString tmp; + + if (y >= 0 || y < (int)lines.count()) + tmp = lines.at(y)->s; + + return tmp; +} + +void GDocument::setLine(int y, GString & str) +{ + if (y < 0 || y >= (int)lines.count()) + return; + + begin(); + if (lines.at(y)->s.length()) + remove(y, 0, y, lines.at(y)->s.length()); + if (str.length()) + insert(y, 0, str); + end(); + updateViews(y); +} + +bool GDocument::getLineFlag(int y, int f) const +{ + if (y >= 0 && y < (int)lines.count()) + return lines.at(y)->flag & (1 << f); + else + return false; +} + +void GDocument::setLineFlag(int y, int f, bool b) +{ + if (y < 0 || y >= (int)lines.count()) + return; + + if (b) + lines.at(y)->flag |= (1 << f); + else + lines.at(y)->flag &= ~(1 << f); + + updateViews(y); +} + +int GDocument::convState(int state) +{ + switch(state) + { + case RT_END: return GLine::Normal; + case RT_RESERVED: return GLine::Keyword; + case RT_IDENTIFIER: return GLine::Symbol; + case RT_CLASS: return GLine::Datatype; + case RT_NUMBER: return GLine::Number; + case RT_STRING: return GLine::String; + case RT_SUBR: return GLine::Subr; + case RT_COMMENT: return GLine::Comment; + case RT_OPERATOR: return GLine::Operator; + case RT_DATATYPE: return GLine::Datatype; + case RT_ERROR: return GLine::Error; + case RT_HELP: return GLine::Help; + case RT_PREPROCESSOR: return GLine::Preprocessor; + default: return GLine::Normal; + } +} + +void GDocument::highlightGambas(GEditor *editor, int line, uint &state, bool &alternate, int &tag, GString &s, GHighlightArray *data, bool &proc) +{ + const char *src; + EVAL_ANALYZE result; + int i; + + src = TO_UTF8(s.getString()); + EVAL.Analyze(src, LAST_UTF8_LENGTH(), state == GLine::Comment ? RT_COMMENT : RT_END, &result, TRUE); + + GB.NewArray(data, sizeof(GHighlight), result.len); + + for (i = 0; i < result.len; i++) + { + //qDebug("state = %d -> %d len = %d", result.color[i].state, QEditor::convState[result.color[i].state], result.color[i].len); + (*data)[i].state = convState(result.color[i].state); + (*data)[i].alternate = result.color[i].alternate; + (*data)[i].len = result.color[i].len; + } + + s = result.str; + GB.FreeString(&result.str); + + proc = result.proc; + state = convState(result.state); +} + +#if 0 +bool GDocument::highlightTest(GEditor *, uint &state, GString &s, GHighlightArray &data, bool &proc) +{ + QMemArray d(s.length()); + uint i; + int j; + uint nstate = 0; + + if (s.length() == 0) + { + proc = false; + return false; + } + + for (i = 0; i < s.length(); i++) + { + nstate = state; + + if (state == GLine::Normal) + { + if (s[i] == '/' && i < (s.length() - 1) && s[i + 1] == '*') + state = nstate = GLine::Comment; + } + else if (state == GLine::Comment) + { + if (s[i] == '/' && i > 0 && s[i - 1] == '*') + nstate = GLine::Normal; + } + + d[i] = state; + state = nstate; + } + + j = -1; + data.resize(s.length()); + + for (i = 0; i < s.length(); i++) + { + if (i == 0 || d[i] != data[j].state) + { + j++; + data[j].state = d[i]; + data[j].len = 1; + } + else + data[j].len++; + } + + if (j >= 0) + data.resize(j + 1); + + proc = false; + + return false; //qstrcmp((const char *)cs, result.str); +} +#endif + +void GDocument::invalidate(int y) +{ + if (y >= 0 && y < numLines()) + { + lines.at(y)->modified = true; + COLORIZE_FROM(y); + } +} + +void GDocument::colorize(int y, bool force) +{ + GLine *l; + //bool modif; + bool proc; + GString old; + uint state; + bool alternate; + int tag; + bool changed = false; + bool updateAll = false; + int yy; + int updateFrom; + bool undo; + + if (highlightMode == None || highlightCallback == NULL) + return; + + if (y < 0) + return; + + if (force) + { + if (colorizeFrom > y) + colorizeFrom = y; + } + + if (_disableColorize) + { + if (_disableColorizeStart < 0) + _disableColorizeStart = y; + else if (_disableColorizeStart > y) + _disableColorizeStart = y; + return; + } + + if (y < colorizeFrom) + return; + + yy = colorizeFrom; + updateFrom = -1; + + undo = false; + + while (yy < numLines()) + { + l = lines.at(yy); + + if (!l->modified) // || ( isLineEditedSomewhere(yy))) + { + if (yy < y) + { + yy++; + continue; + } + else + break; + } + + if (updateFrom < 0) + updateFrom = yy; + //modif = false; + + if (!force && l->baptized && isLineEditedSomewhere(yy)) + { + yy++; + continue; + } + + //fprintf(stderr, "colorize %d\n", yy); + + getState(yy, false, state, tag, alternate); + + if (l->s.length()) + { + GCommandDocument info(this); + + old = l->s; + GB.FreeArray(&l->highlight); + proc = l->proc; + (*highlightCallback)(views.first(), yy, state, alternate, tag, l->s, &l->highlight, proc); + + updateAll |= proc != l->proc; + l->proc = proc; + + if (old != l->s) + { + if (!undo) + { + undo = true; + begin(); + } + addUndo(new GDeleteCommand(&info, yy, 0, yy, old.length(), old)); + if (l->s.length()) + addUndo(new GInsertCommand(&info, yy, 0, yy, l->s.length(), l->s)); + + //maxLength = GMAX(maxLength, (int)l->s.length()); + updateLineWidth(yy); + //qDebug("colorize: %d has changed: '%s' -> '%s'", y, old.utf8(), l->s.utf8()); + l->changed = true; + changed = true; + } + } + else + { + GB.FreeArray(&l->highlight); + updateAll |= l->proc; + l->proc = false; + } + + if (yy == 0) + l->proc = true; + + //fprintf(stderr, "unmodifyLine: %d\n", yy); + l->modified = false; + l->baptized = true; + + state &= 0x1F; + tag &= 0xFFFF; + + if (l->state == state && l->tag == tag && l->alternate == alternate) + { + if (yy < y) + { + yy++; + continue; + } + else + break; + } + + l->state = state; + l->alternate = alternate; + l->tag = tag; + + yy++; + if (yy < numLines()) + { + //if (!lines.at(yy)->modified) + // fprintf(stderr, "modifyLine: %d\n", yy); + lines.at(yy)->modified = true; + } + } + + if (undo) + end(); + + //if (yedit < 0 || yy < yedit) + colorizeFrom = yy + 1; + //else + // colorizeFrom = yedit; + + //fprintf(stderr, "colorizeFrom -> %d\n", colorizeFrom); + + if (changed) + emitTextChanged(); + + if (updateFrom >= 0) + updateViews(updateFrom, yy - updateFrom + 1); + + if (updateAll) + updateContents(); +} + +void GDocument::colorizeAll() +{ + int y; + + if (highlightMode == None) + return; + + FOR_EACH_VIEW(v) + { + //if (v->viewport()->hasFocus()) + v->leaveCurrentLine(); + } + + for (y = colorizeFrom; y < numLines(); y++) + colorize(y); +} + +#if 0 +void GDocument::baptizeUntil(int y) +{ + /*bool busy = (y - baptismLimit) > 256; + + if (busy) + qApp->setOverrideCursor(Qt::waitCursor);*/ + + while (baptismLimit <= y) + { + colorize(baptismLimit); + baptismLimit++; + } + + /*if (busy) + qApp->restoreOverrideCursor();*/ +} +#endif + +void GDocument::invalidateHighlight() +{ + int i; + + for (i = 0; i < numLines(); i++) + lines.at(i)->modified = true; + + colorizeFrom = 0; + + updateMargin(); + updateViews(); +} + +void GDocument::setHighlightMode(int mode, GHighlightCallback cb) +{ + highlightMode = mode; + + if (mode == Gambas) + highlightCallback = GDocument::highlightGambas; + else + highlightCallback = cb; + + invalidateHighlight(); +} + +void GDocument::setKeywordsUseUpperCase(bool v) +{ + if (v == keywordsUseUpperCase) + return; + + keywordsUseUpperCase = v; + invalidateHighlight(); +} + +void GDocument::emitTextChanged() +{ + if (undoLevel > 0) + { + textHasChanged = true; + return; + } + + FOR_EACH_VIEW(v) + v->docTextChanged(); +} + + +int GDocument::getNextLimit(int y) +{ + for(;;) + { + y++; + if (y >= numLines()) + return (-1); + if (hasLimit(y)) + return y; + } +} + +int GDocument::getPreviousLimit(int y) +{ + for(;;) + { + y--; + if (y < 0) + return (-1); + if (y == 0 || hasLimit(y)) + return y; + } +} + +void GDocument::getState(int y, bool col, uint &state, int &tag, bool &alternate) +{ + if (y == 0) + { + state = GLine::Normal; + alternate = false; + tag = 0; + } + else + { + if (col) + colorize(y - 1); + GLine *l = lines.at(y - 1); + state = l->state; + alternate = l->alternate; + tag = l->tag; + } +} + +int GDocument::getCharState(int y, int x) +{ + GLine *l; + int i; + GHighlight *hl; + + l = lines.at(y); + if (l->modified) + { + if (x < 0 || x > (int)l->s.length()) + return GLine::Background; + else + return GLine::Normal; + } + + for (i = 0; i < GB.Count(l->highlight); i++) + { + hl = &l->highlight[i]; + if (x < hl->len) + return hl->state; + x -= hl->len; + } + + return GLine::Background; +} + +bool GDocument::isLineEditedSomewhere(int y) +{ + //fprintf(stderr, "isLineEditedSomewhere %d ?\n", y); + if (!lines.at(y)->modified) + { + //fprintf(stderr, "--> false (not modified)\n"); + return false; + } + + FOR_EACH_VIEW(v) + { + //fprintf(stderr, "check with %d\n", v->y); + if (v->y == y && !v->getFlag(GEditor::HighlightImmediately)) + { + //fprintf(stderr, "--> true\n"); + return true; + } + } + + //fprintf(stderr, "--> false (no view matches)\n"); + return false; +} + +int GDocument::getLimitIndex(int y) +{ + int i = 0; + + if (y >= numLines()) + y = numLines() - 1; + + if (y < 0) + return -1; + + while (y > 0) + { + if (lines.at(y)->proc) + i++; + y--; + } + + return i; +} + +void GDocument::updateMargin() +{ + FOR_EACH_VIEW(v) + { + v->updateMargin(); + } +} + +void GDocument::disableColorize() +{ + if (_disableColorize == 0) + _disableColorizeStart = -1; + _disableColorize++; +} + +void GDocument::enableColorize() +{ + _disableColorize--; + if (_disableColorize == 0 && _disableColorizeStart >= 0) + { + colorize(_disableColorizeStart); + _disableColorizeStart = -1; + } +} + +void GDocument::updateContents() +{ + FOR_EACH_VIEW(v) + { + v->updateContents(); + } +} + +void GDocument::setCurrentLine(int y) +{ + _currentLine = y; + updateContents(); +} + +void GDocument::setEndOfLine(int mode) +{ + _eol_mode = mode; + + switch (mode) + { + case GB_EOL_WINDOWS: _eol = GString("\r\n"); break; + case GB_EOL_MAC: _eol = GString("\r"); break; + default: _eol = GString("\n"); break; + } +} diff --git a/gb.qt4/src/ext/gdocument.h b/gb.qt4/src/ext/gdocument.h new file mode 100644 index 00000000..a8ab3d1f --- /dev/null +++ b/gb.qt4/src/ext/gdocument.h @@ -0,0 +1,238 @@ +/*************************************************************************** + + gdocument.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GDOCUMENT_H +#define __GDOCUMENT_H + +#include "garray.h" +#include "gstring.h" + +struct GHighlight +{ + unsigned state : 5; + unsigned alternate : 1; + unsigned len : 10; +}; + +#define HIGHLIGHT_LEN_MAX 1023 + +typedef + GHighlight *GHighlightArray; + +class GLine +{ +public: + + enum + { + Background = 0, Normal, Keyword, Subr, + Operator, Symbol, Number, String, + Comment, Breakpoint, Current, Datatype, + Selection, Highlight, Line, Error, + Help, Preprocessor, + NUM_STATE + }; + + enum + { + BookmarkFlag = 0, + BreakpointFlag = 1 + }; + + GString s; + GHighlightArray highlight; + unsigned state : 5; + unsigned alternate : 1; + unsigned modified : 1; + unsigned changed : 1; + unsigned saved : 1; + unsigned flag : 2; + unsigned proc : 1; + unsigned unicode : 1; + unsigned tab : 1; + unsigned baptized : 1; + unsigned nobreak : 1; + signed tag : 16; + + GLine(); + ~GLine(); + void insert(uint pos, const GString &text); + bool isEmpty() const; +}; + +class GCommand; +class GEditor; + +typedef + void (*GHighlightCallback)(GEditor *master, int line, uint &state, bool &alternate, int &tag, GString &s, GHighlightArray *highlight, bool &proc); + + +class GDocument +{ +private: + + static int convState(int state); + + GArray undoList; + GArray redoList; + int undoLevel; + int highlightMode; + GHighlightCallback highlightCallback; + uint oldCount; + GEditor *selector; + GEditor *_currentView; + GString _eol; + int xs, ys, xs2, ys2; + int tabWidth; + int colorizeFrom; + int _disableColorize; + int _disableColorizeStart; + int _currentLine; + unsigned _eol_mode : 2; + unsigned readOnly : 1; + unsigned blockUndo : 1; + unsigned keywordsUseUpperCase : 1; + unsigned textHasChanged : 1; + + void init(); + void clearUndo(); + void addUndo(GCommand *); + void addRedo(GCommand *); + void updateViews(int row = -1, int count = 1); + void updateMargin(); + void updateContents(); + +public: + + enum GHighlightMode + { + None = 0, + Gambas = 1, + Custom = 2 + }; + + static void highlightGambas(GEditor *master, int line, uint &state, bool &alternate, int &tag, GString &s, GHighlightArray *data, bool &proc); + + GArray lines; + GArray views; + int xAfter, yAfter; + + GDocument(); + ~GDocument(); + + void clear(); + void reset(bool saved); + + void setCurrentView(GEditor *view) { _currentView = view; } + GEditor *currentView() const { return _currentView; } + + GString getText(); + void setText(const GString & text); + int getLength() const; + + int getEndOfLine() const { return _eol_mode; } + void setEndOfLine(int mode); + GString getEndOfLineText() const { return _eol; } + + void subscribe(GEditor *view); + void unsubscribe(GEditor *view); + + void setReadOnly(bool ro) { readOnly = ro; }; + bool isReadOnly() const { return readOnly; } + + void setTabWidth(int tw) { tabWidth = tw; } + int getTabWidth() const { return tabWidth; } + + GString getLine(int y) const; + void setLine(int y, GString & str); + + int currentLine() const { return _currentLine; } + void setCurrentLine(int y); + + bool getLineFlag(int y, int f) const; + void setLineFlag(int y, int f, bool b); + + bool isLineEditedSomewhere(int y); + + int lineLength(int y) const { return lines.at(y)->s.length(); } + int numLines() const { return lines.count(); } + + bool hasLimit(int y) { colorize(y); return lines.at(y)->proc; } + int getLimitIndex(int y); + + void getState(int y, bool colorize, uint &state, int &tag, bool &alternate); + int getCharState(int y, int x); + + int getNextLimit(int y); + int getPreviousLimit(int y); + + void insert(int y, int x, const GString &str, bool doNotMove = false); + void remove(int y, int x, int y2, int x2); + + bool undo(); + bool redo(); + void begin(bool linked = false); + void end(bool linked = false); + + void setHighlightMode(int mode, GHighlightCallback cb = 0); + int getHighlightMode() const { return highlightMode; } + + void setKeywordsUseUpperCase(bool v); + bool isKeywordsUseUpperCase() const { return keywordsUseUpperCase; } + + void colorize(int y, bool force = false); + void colorizeAll(); + void invalidate(int y); + + int getIndent(int y, bool *empty = NULL); + int wordLeft(int y, int x, bool word = false); + int wordRight(int y, int x, bool word = false); + + bool hasSelection() { return selector != NULL && (ys != ys2 || xs != xs2); } + bool hasSelection(GEditor *view) { return hasSelection() && selector == view; } + void getSelection(int *y1, int *x1, int *y2, int *x2, bool insertMode); + GString getSelectedText(bool insertMode); + + void startSelection(GEditor *view, int y, int x); + void endSelection(int y, int x); + void hideSelection(); + void eraseSelection(bool insertMode); + + bool insideUndo() const { return undoLevel; } + + void emitTextChanged(); + + void insertLine(int y); + void removeLine(int y); + +private: + + void checkSelection(); + void updateLineWidth(int y); + void modifyLine(GLine *l, int y); + void disableColorize(); + void enableColorize(); + void invalidateHighlight(); +}; + +#endif diff --git a/gb.qt4/src/ext/gstring.cpp b/gb.qt4/src/ext/gstring.cpp new file mode 100644 index 00000000..2a333476 --- /dev/null +++ b/gb.qt4/src/ext/gstring.cpp @@ -0,0 +1,70 @@ +/*************************************************************************** + + gstring.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __G_ARRAY_CPP + +#include "garray.h" +#include "gstring.h" + +bool GString::hasUnicode() const +{ + for (uint i = 0; i < (uint)s.length(); i++) + { + int c = s[i].unicode(); + if (!isStandardChar(c)) + return true; + } + + return false; +} + +int GString::findNextLine(int index, int &len) const +{ + uint l = s.length(); + + for (uint i = index; i < l; i++) + { + int c = s[i].unicode(); + if (c == '\n') + { + len = i - index; + return i + 1; + } + if (c == '\r') + { + if ((i < (l - 1)) && s[i + 1].unicode() == '\n') + { + len = i - index; + return i + 2; + } + else + { + len = i - index; + return i + 1; + } + } + } + + len = l - index; + return 0; +} \ No newline at end of file diff --git a/gb.qt4/src/ext/gstring.h b/gb.qt4/src/ext/gstring.h new file mode 100644 index 00000000..c1a7bc7c --- /dev/null +++ b/gb.qt4/src/ext/gstring.h @@ -0,0 +1,275 @@ +/*************************************************************************** + + gstring.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GSTRING_H +#define __GSTRING_H + +#include "gb_common.h" +#include "gambas.h" +#include "../gb.qt.h" + +#include + +extern "C" GB_INTERFACE GB; +extern "C" QT_INTERFACE QT; + +class GString +{ +private: + QString s; + +public: + GString() { s = ""; } + ~GString() {} + GString(const QString &str); + GString(const GString &str); + QString getString() const { return s; } + uint length() const { return s.length(); } + GString &operator=(const GString &str); + GString &operator=(const QString &str); + GString &operator=(const char *utf8); + GString &operator+=(const GString &str); + GString &operator+=(const char *utf8); + GString &operator+=(const char c); + GString left(uint len) const; + GString right(uint len) const; + GString mid(uint index, uint len = 0xffffffff) const; + GString &remove(uint index, uint len); + GString &append(const GString &str); + GString &append(char c); + GString &prepend(const GString &str); + GString &prepend(char c); + GString &insert(uint index, const GString &str); + bool isNewLine(uint pos) const; + bool isSpace(uint pos) const; + bool isWordChar(uint pos) const; + int indexOf(char c, int index = 0) const; + int indexOf(const GString &str, int index = 0, bool cs = true) const; + char at(uint pos) const; + GString lower() const; + GString upper() const; + bool hasUnicode() const; + bool hasTab() const { return indexOf('\t') >= 0; } + void clear() { s.clear(); } + int findNextLine(int index, int &len) const; + + static bool isStandardChar(ushort c); +}; + +inline GString::GString(const GString &str) +{ + s = str.getString(); +} + +inline GString::GString(const QString &str) +{ + s = str; +} + +inline GString &GString::operator=(const GString &str) +{ + s = str.getString(); + return *this; +} + +inline GString &GString::operator=(const QString &str) +{ + s = str; + return *this; +} + +inline GString &GString::operator=(const char *utf8) +{ + s = TO_QSTRING(utf8); + return *this; +} + +inline GString &GString::operator+=(const GString &str) +{ + s += str.getString(); + return *this; +} + +inline GString &GString::operator+=(const char *utf8) +{ + s += TO_QSTRING(utf8); + return *this; +} + +inline GString &GString::operator+=(const char c) +{ + s += c; + return *this; +} + +inline GString GString::left(uint len) const +{ + return GString(s.left(len)); +} + +inline GString GString::right(uint len) const +{ + return GString(s.right(len)); +} + +inline GString GString::mid(uint index, uint len) const +{ + return GString(s.mid(index, len)); +} + +inline GString GString::lower() const +{ + QString sl = s; + int i; + + for (i = 0; i < s.length(); i++) + sl[i] = tolower(s[i].latin1()); + + return GString(sl); +} + +inline GString GString::upper() const +{ + QString sl = s; + int i; + + for (i = 0; i < s.length(); i++) + sl[i] = toupper(s[i].latin1()); + + return GString(sl); +} + + +inline GString &GString::remove(uint index, uint len) +{ + s.remove(index, len); + return *this; +} + +inline GString &GString::insert(uint index, const GString &str) +{ + s.insert(index, str.getString()); + return *this; +} + +inline GString &GString::append(const GString &str) +{ + s.append(str.getString()); + return *this; +} + +inline GString &GString::append(char c) +{ + s.append(c); + return *this; +} + +inline GString &GString::prepend(const GString &str) +{ + s.prepend(str.getString()); + return *this; +} + +inline GString &GString::prepend(char c) +{ + s.prepend(c); + return *this; +} + +inline int GString::indexOf(char c, int index) const +{ + return s.indexOf(c, index); +} + +inline int GString::indexOf(const GString &str, int index, bool cs) const +{ + return s.indexOf(str.getString(), index, cs ? Qt::CaseSensitive : Qt::CaseInsensitive); +} + +inline bool GString::isSpace(uint pos) const +{ + if (pos >= (uint)s.length()) + return false; + return s[pos].isSpace(); +} + +inline bool GString::isNewLine(uint pos) const +{ + if (pos >= (uint)s.length()) + return false; + return s[pos] == '\n'; +} + +inline bool GString::isWordChar(uint pos) const +{ + if (pos >= (uint)s.length()) + return false; + QChar c = s[pos]; + return c.isLetterOrNumber() || c == '_' || c == '$'; +} + +inline bool operator==(const GString &s1, const GString &s2) +{ + return s1.getString() == s2.getString(); +} + +inline bool operator!=(const GString &s1, const GString &s2) +{ + return s1.getString() != s2.getString(); +} + +inline const GString operator+(const GString &s1, const GString &s2) +{ + GString tmp(s1); + tmp += s2; + return tmp; +} + +inline char GString::at(uint pos) const +{ + if (pos >= (uint)s.length()) + return 0; + return s[pos].latin1(); +} + +inline const GString operator+(const GString &s1, const char c) +{ + GString tmp(s1); + tmp += c; + return tmp; +} + +inline const GString operator+(const char c, const GString &str) +{ + GString tmp; + tmp += c; + tmp += str; + return tmp; +} + +inline bool GString::isStandardChar(ushort c) +{ + return !(c < 32 || (c >= 127 && c < 160) || c == 173 || c > 255); +} + +#endif diff --git a/gb.qt4/src/ext/gview.cpp b/gb.qt4/src/ext/gview.cpp new file mode 100644 index 00000000..85ed4fcf --- /dev/null +++ b/gb.qt4/src/ext/gview.cpp @@ -0,0 +1,3447 @@ +/*************************************************************************** + + gview.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __G_VIEW_CPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "main.h" +#include "gview.h" + +#define ROUND_WIDTH(_w) ((int)((_w) + 0.4999)) + +#if 0 +static const char *breakpoint_xpm[] = +{ +"8 8 3 1", +" c None", +". c #FF7F7F", +"+ c #FF0000", +" .++++. ", +".++++++.", +"++++++++", +"++++++++", +"++++++++", +"++++++++", +".++++++.", +" .++++. " +}; +#endif + +static QColor defaultColors[GLine::NUM_STATE] = +{ + Qt::white, + Qt::black, + QColor(0x00, 0x80, 0xFF), + Qt::blue, + Qt::blue, + Qt::black, + Qt::red, + Qt::magenta, + Qt::gray, + QColor(0xD0, 0x00, 0x00), + QColor(0x00, 0x80, 0xFF), + QColor(0x00, 0x80, 0xFF), + QColor(0xC0, 0xC0, 0xFF), + QColor(0xFF, 0xFF, 0x00), + QColor(0xE8, 0xE8, 0xF8), + Qt::red, + Qt::gray, + Qt::green +}; + + +/**---- GEditor -----------------------------------------------------------*/ + +QPixmap *GEditor::_cache = 0; +QPixmap *GEditor::_breakpoint = 0; +QPixmap *GEditor::_bookmark = 0; +int GEditor::count = 0; +QStyle *GEditor::_style = 0; + +QImage *_blend_pattern = 0; + +void GEditor::reset() +{ + x = y = xx = 0; + x1m = x2m = 0; + y1m = y2m = -1; + lastx = -1; + _cursor = false; + lineNumberLength = 0; + center = false; + largestLine = 0; + flashed = false; + painting = false; + _showRow = -1; + _showCol = 0; + _showLen = 0; + _posOutside = false; + _checkCache = true; + _ensureCursorVisibleLater = false; + _save_x = _save_y = 0; + + foldClear(); +} + +GEditor::GEditor(QWidget *parent) + : Q3ScrollView(parent), + fm(font()) +{ + int i; + //QStyle *oldStyle; + + if (count == 0) + { + _cache = new QPixmap(); + _style = new QWindowsStyle; + } + + count++; + + setKeyCompression(true); + setFocusPolicy(Qt::WheelFocus); + setAttribute(Qt::WA_InputMethodEnabled, true); + + _border = true; + + setMouseTracking(true); + viewport()->setMouseTracking(true); + viewport()->setCursor(Qt::ibeamCursor); + saveCursor(); + viewport()->setBackgroundRole(QPalette::Base); + viewport()->setPaletteBackgroundColor(defaultColors[GLine::Background]); + viewport()->setFocusProxy(this); + + //oldStyle = style(); + //Q3ScrollView::setStyle(_style); + //viewport()->setStyle(_style); + //Q3ScrollView::setStyle(oldStyle); + ensurePolished(); + updateViewportAttributes(); + + margin = 0; + doc = 0; + _showStringIgnoreCase = false; + _insertMode = false; + _cellw = _cellh = 0; + _oddLine = false; + _dblclick = false; + _firstLineNumber = 0; + + for (i = 0; i < GLine::NUM_STATE; i++) + { + styles[i].color = defaultColors[i]; + styles[i].bold = i == GLine::Keyword || i == GLine::Help; + styles[i].italic = i == GLine::Comment; + styles[i].underline = i == GLine::Error; + if (i == GLine::Comment || i == GLine::Help) + { + styles[i].background = true; + styles[i].backgroundColor = QColor(0xE8, 0xE8, 0xE8); + } + else + styles[i].background = false; + } + + flags = 1 << ShowDots; + + reset(); + + setDocument(NULL); + + setFont(QFont("monospace", QApplication::font().pointSize())); + + blinkTimer = new QTimer(this); + connect(blinkTimer, SIGNAL(timeout()), this, SLOT(blinkTimerTimeout())); + //startBlink(); + + scrollTimer = new QTimer(this); + connect(scrollTimer, SIGNAL(timeout()), this, SLOT(scrollTimerTimeout())); + + //connect(this, SIGNAL(contentsMoving(int, int)), this, SLOT(baptizeVisible(int, int))); +} + +GEditor::~GEditor() +{ + doc->unsubscribe(this); + count--; + if (count == 0) + { + delete _cache; + delete _breakpoint; + delete _bookmark; + _cache = 0; + _breakpoint = 0; + _bookmark = 0; + delete _style; + } +} + +void GEditor::setBreakpointPixmap(QPixmap *p) +{ + if (!_breakpoint) + _breakpoint = new QPixmap; + + *_breakpoint = *p; +} + +void GEditor::setBookmarkPixmap(QPixmap *p) +{ + if (!_bookmark) + _bookmark = new QPixmap; + + *_bookmark = *p; +} + +void GEditor::setDocument(GDocument *d) +{ + if (doc) + { + doc->unsubscribe(this); + //disconnect(doc, 0, this, 0); + } + + doc = d; + if (!doc) + doc = new GDocument; + + doc->subscribe(this); + findLargestLine(); + //connect(doc, SIGNAL(textChanged()), this, SLOT(docTextChanged())); +} + +int GEditor::getStringWidth(const QString &s, int len, bool unicode) const +{ + int i; + ushort c; + double w = 0; + int us = -1, ul = 0; + + //if (len < 0) + // len = s.length(); + + if (len == 0) + return 0; + + if (_sameWidth && !unicode) + return ROUND_WIDTH(len * _sameWidth); + + for (i = 0; i < len; i++) + { + c = s.at(i).unicode(); + if (c != 9 && !GString::isStandardChar(c)) + { + if (us < 0) + { + us = i; + ul = 0; + } + ul++; + } + else + { + if (us >= 0) + { + w += fm.width(s.mid(us), ul); + us = -1; + } + if (c == 9) + w = (ROUND_WIDTH(w) + _tabWidth) / _tabWidth * _tabWidth; + else + w += _charWidth[c]; + } + } + + if (us >= 0) + w += fm.width(s.mid(us), ul); + + //qDebug("getStringWidth: %g / %d (%g %d)", w, fm.width(s, len), _sameWidth, unicode); + + return w; +} + +void GEditor::updateCache() +{ + //int nw = QMAX(cache->width(), QMIN(visibleWidth(), _cellw)); + //int nh = QMAX(cache->height(), QMIN(visibleHeight(), _cellh)); + int nw = QMAX(_cache->width(), ROUND_WIDTH(visibleWidth() + _charWidth['m'] * 2)); + int nh = QMAX(_cache->height(), visibleHeight() + _cellh); + + if (nw > 0 && nh > 0 && (nw != _cache->width() || nh != _cache->height())) + _cache->resize(nw, nh); + _checkCache = false; +} + +int GEditor::lineWidth(int y) const +{ + // Add 2 or a character width so that we see the cursor at end of line + GLine *l = doc->lines.at(y); + return margin + getStringWidth(l->s.getString(), l->s.length(), l->unicode) + (_insertMode ? _charWidth['W'] : 2); +} + + +int GEditor::lineWidth(int y, int len) +{ + int ns; + + if (len <= 0) + return margin; + else + { + GLine *l = doc->lines.at(y); + QString s = l->s.getString(); + ns = QMAX(0, len - s.length()); + len = QMIN(len, s.length()); + + /*if (y != lineWidthCacheY) + { + lineWidthCacheY = y; + lineWidthCache.clear(); + //qDebug("y = %d", y); + } + + int lw = lineWidthCache[len]; + if (!lw) + { + //qDebug("lineWidthCache: %d", len); + lw = getStringWidth(s, len); //fm.width(s, len); + lineWidthCache.insert(len, lw); + }*/ + + int lw = getStringWidth(s, len, l->unicode); + lw += margin; + if (ns) lw += ns * _charWidth[' ']; + return lw; + } +} + +int GEditor::findLargestLine() +{ + int w, mw = 0; + + //qDebug("search largest line..."); + + for (int y = 0; y < doc->numLines(); y++) + { + w = lineWidth(y); + if (w > mw) + { + mw = w; + largestLine = y; + } + } + + return mw; +} + +void GEditor::updateWidth(int y) +{ + int w; + + if (largestLine < 0 || largestLine >= numLines()) + { + findLargestLine(); + y = -1; + } + + if (y < 0) + { + w = lineWidth(largestLine); + goto UPDATE_WIDTH; + } + else + { + w = lineWidth(y); + + //qDebug("contentsWidth() = %d _cellw = %d", contentsWidth(), _cellw); + //setCellWidth(QMAX(visibleWidth(), margin + charWidth * doc->getMaxLineLength() + 2)); + + if (w > _cellw) + { + largestLine = y; + goto UPDATE_WIDTH; + } + else if (w < _cellw && y == largestLine) + { + w = findLargestLine(); + goto UPDATE_WIDTH; + } + } + + return; + +UPDATE_WIDTH: + + w = QMAX(visibleWidth(), w); + if (w != _cellw) + { + _cellw = w; + updateViewport(); + } +} + +void GEditor::updateHeight() +{ + _cellh = fm.ascent() + fm.descent() + 3; + //updateCache(); + _checkCache = true; + + updateViewport(); +} + +void GEditor::redrawContents() +{ + updateContents(); + //if (contentsHeight() < visibleHeight()) + // repaintContents(contentsX(), contentsHeight(), visibleWidth(), visibleHeight() - contentsHeight() + contentsX(), true); +} + +static double *get_char_width_table(QFontMetrics &fm, QFont &font) +{ + static QHash cache; + + int i; + QString c, fd; + double *cw; + + fd = font.toString(); + + if (cache.contains(fd)) + return cache[fd]; + + //qDebug("get_char_width_table: %s", (const char *)fd.toUtf8()); + + cw = new double[256]; + + for (i = 0; i < 256; i++) + { + c = QChar(i); + c = c.repeated(64); + cw[i] = (double)fm.width(c) / 64; + //qDebug("_charWidth[%d] = %g", i, _charWidth[i]); + } + + cache.insert(fd, cw); + return cw; +} + +void GEditor::updateFont() +{ + QFont f; + int i; + QString c; + + normalFont = QFont(font()); + normalFont.setKerning(false); + italicFont = QFont(font()); + italicFont.setKerning(false); + italicFont.setItalic(true); + + fm = QFontMetrics(normalFont); + _ytext = fm.ascent() + 1; + + _charWidth = get_char_width_table(fm, normalFont); + + _sameWidth = _charWidth[' ']; + _tabWidth = _charWidth['m'] * 8; + //_charWidth[9] = _sameWidth * 8; + + for (i = 33; i < 127; i++) + { + if (_charWidth[i] != _sameWidth) + { + //qDebug("[%d] = %g!", i, _charWidth[i]); + _sameWidth = 0; + break; + } + } + + if (_sameWidth) + { + for (i = 160; i < 256; i++) + { + if (i == 173) + continue; + if (_charWidth[i] != _sameWidth) + { + //qDebug("[%d] = %g!", i, _charWidth[i]); + _sameWidth = 0; + break; + } + } + } + + //qDebug("%p: sameWidth = %g tabWidth = %d", this, _sameWidth, _tabWidth); + + if (_sameWidth) + { + QString t = "AbCdEfGh01#@WwmM"; + t = t.repeated(4); + + _sameWidth = (double)fm.width(t) / t.length(); + //qDebug("_sameWidth -> %g", _sameWidth); + } + + updateMargin(); + updateWidth(); + updateHeight(); + updateContents(); +} + +void GEditor::changeEvent(QEvent *e) +{ + Q3ScrollView::changeEvent(e); + if (e->type() == QEvent::FontChange) + updateFont(); +} + +static int find_last_non_space(const QString &s) +{ + int i; + ushort c; + + for (i = s.length() - 1; i >= 0; i--) + { + c = s[i].unicode(); + if (c > 32 || c == 9) + return i; + } + return -1; +} + +void GEditor::paintDottedSpaces(QPainter &p, int row, int ps, int ls) +{ + QPoint pa[ls]; + int i, y; + double x, w; + + w = _charWidth[' ']; + x = lineWidth(row, ps) + w / 2; + y = _cellh / 2; + for (i = 0; i < ls; i++) + { + pa[i].setX(ROUND_WIDTH(x)); + pa[i].setY(y); + x += w; + } + + p.setOpacity(0.5); + p.drawPoints(pa, i); + p.setOpacity(1); +} + +static QColor merge_color(const QColor &ca, const QColor &cb) +{ + int r, g, b; + + if (cb.value() >= 128) + { + r = ca.red() * cb.red() / 255; + g = ca.green() * cb.green() / 255; + b = ca.blue() * cb.blue() / 255; + } + else + { + r = ca.red() * (255-cb.red()) / 255; + g = ca.green() * (255-cb.green()) / 255; + b = ca.blue() * (255-cb.blue()) / 255; + } + + return QColor(r, g, b); +} + +/*static QColor blend_color(const QColor &ca, const QColor &cb) +{ + return QColor((ca.red() + cb.red()) / 2, (ca.green() + cb.green()) / 2, (ca.blue() + cb.blue()) / 2); +}*/ + +static QColor calc_color(QColor ca, QColor cb, QColor cd) +{ + if (!ca.isValid() && !cb.isValid()) + return cd; + + if (!ca.isValid()) + return cb; + + if (!cb.isValid()) + return ca; + + return QColor((ca.red() + cb.red()) / 2, (ca.green() + cb.green()) / 2, (ca.blue() + cb.blue()) / 2); +} + +void GEditor::drawTextWithTab(QPainter &p, int sx, int x, int y, const QString &s) +{ + int tp, tnp, yt = y; + + tp = 0; + + for(;;) //while (pos < s.length()) + { + tnp = s.indexOf('\t', tp); + if (tnp < 0) + { + p.drawText(x, y, s.mid(tp)); + break; + } + + if (tnp > tp) + { + p.drawText(x, y, s.mid(tp, tnp - tp)); + x += fm.width(s.mid(tp, tnp - tp)); + } + p.setOpacity(0.5); + p.drawLine(x, yt - 2, x, yt); + p.drawLine(x + 1, yt, x + 2, yt); + p.setOpacity(1); + x = sx + ((x - sx) + _tabWidth) / _tabWidth * _tabWidth; + tp = tnp + 1; + } +} + +static void highlight_text(QPainter &p, int x, int x2, int h, QColor color, QColor) +{ + //int i, j; + + //p.setPen(color); + + /*for (i = -1; i <= 1; i++) + for (j = -1; j <= 1; j++) + p.drawText(x + i, y + j, s);*/ + //color.setAlpha(192); + //p.setCompositionMode(QPainter::CompositionMode_Overlay); + p.fillRect(x, 0, x2 - x, h, color); + //p.setCompositionMode(QPainter::CompositionMode_SourceOver); + //border.setAlpha(128); + //p.setPen(QColor(border)); + //p.drawRect(x, 0, x2 - x, h); +} + +void GEditor::paintText(QPainter &p, GLine *l, int x, int y, int xmin, int lmax, int h, int xs1, int xs2, int row, QColor &fbg) +{ + int i; + int len, style, pos; + bool alternate; + QString sd; + GHighlightStyle *st; + bool italic = false; + int nx; + int ps; + QColor bg; + bool draw_bg; + bool show_dots = getFlag(ShowDots); + int xs = x; + + if (l->s.length() == 0) + { + if (l->alternate) + { + nx = _cellw; + + int x1 = x; + int x2 = nx; + int xd1 = xs1; + int xd2 = xs2; + + if (xd1 < x1) + xd1 = x1; + if (xd2 > x2) + xd2 = x2; + + p.fillRect(x, 0, nx - x, h, merge_color(_altBackground, fbg)); + + if (xd2 > xd1) + p.fillRect(xd1, 0, xd2 - xd1, h, styles[GLine::Selection].color); + } + + return; + } + + p.setFont(normalFont); + + pos = 0; + ps = find_last_non_space(l->s.getString()) + 1; + + for (i = 0; i < GB.Count(l->highlight); i++) + { + if (pos > (xmin + lmax)) + break; + + len = l->highlight[i].len; + nx = lineWidth(row, pos + len); + //w = len * charWidth; + + if ((pos + len) >= xmin) + { + style = l->highlight[i].state; + alternate = l->highlight[i].alternate; + st = &styles[style]; + draw_bg = false; + if (st->background) + { + draw_bg = true; + bg = st->backgroundColor; + } + else if (alternate) + { + draw_bg = true; + bg = _altBackground; + if (i == (GB.Count(l->highlight) - 1) && l->alternate) + nx = _cellw; + } + + if (style == GLine::Keyword && doc->isKeywordsUseUpperCase()) + sd = l->s.mid(pos, len).upper().getString(); + else + sd = l->s.mid(pos, len).getString(); + + if (draw_bg) + { + int x1 = x; + int x2 = nx; + int xd1 = xs1; + int xd2 = xs2; + + if (xd1 < x1) + xd1 = x1; + if (xd2 > x2) + xd2 = x2; + + p.fillRect(x, 0, nx - x, h, merge_color(bg, fbg)); + + if (xd2 > xd1) + p.fillRect(xd1, 0, xd2 - xd1, h, styles[GLine::Selection].color); + } + + // Highlight braces + if (getFlag(HighlightBraces)) + { + if (row == y1m && x1m >= 0 && x1m >= pos && x1m < (pos + len)) + highlight_text(p, lineWidth(y1m, x1m), lineWidth(y1m, x1m + 1), _cellh - 1, styles[GLine::Highlight].color, styles[GLine::Normal].color); + if (row == y2m && x2m >= 0 && x2m >= pos && x2m < (pos + len)) + highlight_text(p, lineWidth(y2m, x2m), lineWidth(y2m, x2m + 1), _cellh - 1, styles[GLine::Highlight].color, styles[GLine::Normal].color); + } + + if (ps >= 0 && pos >= ps) + { + if (show_dots) + paintDottedSpaces(p, row, pos, QMIN(xmin + lmax - pos, sd.length())); + } + else + { + if (st->italic != italic) + { + italic = st->italic; + p.setFont(italic ? italicFont : normalFont); + } + + if (st->bold) + { + QColor c = st->color; + c.setAlpha(128); + p.setPen(c); + drawTextWithTab(p, xs, x + 1, y, sd); + } + + p.setPen(st->color); + drawTextWithTab(p, xs, x, y, sd); + } + + if (st->underline) + { + p.setOpacity(0.5); + p.drawLine(x, y + 2, nx - 1, y + 2); + p.setOpacity(1); + } + } + + pos += len; + x = nx; + } + + if (pos < (int)l->s.length() && pos < (xmin + lmax)) + { + p.setPen(styles[GLine::Normal].color); + //p.drawText(x, y, l->s.mid(pos).getString()); + drawTextWithTab(p, xs, x, y, l->s.mid(pos).getString()); + if (ps >= 0 && pos >= ps && show_dots) + paintDottedSpaces(p, row, pos, QMIN(xmin + lmax - pos, (int)l->s.length() - pos)); + } +} + +void GEditor::paintShowString(QPainter &p, GLine *l, int x, int y, int xmin, int lmax, int h, int row) +{ + int pos; + QString sd; + int nx; + int ps; + QColor bg; + + bg = styles[GLine::Highlight].color; + + if (_showString.length()) + { + pos = 0; + for(;;) + { + if (pos >= (int)l->s.length()) + break; + pos = l->s.indexOf(_showString, pos, !_showStringIgnoreCase); + if (pos < 0) + break; + ps = lineWidth(row, pos); + //if (ps > (xmin + lmax)) + // break; + nx = lineWidth(row, pos + _showString.length()); + p.fillRect(ps, 0, nx - ps, h, bg); + pos += _showString.length(); + } + } + + if (row == _showRow && _showLen > 0 && _showCol >= 0 && _showCol < (int)l->s.length()) + { + ps = lineWidth(row, _showCol); + nx = lineWidth(row, QMIN((int)l->s.length(), _showCol + _showLen)); + p.fillRect(ps, 0, nx - ps, h, bg); + } +} + +static void make_blend(QImage *pix, QColor start) //, bool loop = false) +{ + double r, g, b, a, da; + int n = pix->height(); + int i; + QPainter p; + + pix->fill(0); + + r = start.red(); + g = start.green(); + b = start.blue(); + + if (n == 0) + n = 1; + + a = 0; + da = (128 / ((n + 1) / 2) - 1); + + p.begin(pix); + + for (i = 0; i < ((n + 1) / 2); i++) + { + QBrush brush(QColor((int)r, (int)g, (int)b, qMin(255,(int)a))); + p.fillRect(0, i, pix->width(), 1, brush); + p.fillRect(0, n - i - 1, pix->width(), 1, brush); + a += da; + } + + p.end(); +} + +/*static QColor blend_color(QColor a, QColor b, QColor r) +{ + #define f(c) ((a.c()*r.c() + (255 - a.c())*(255 - r.c())) * b.c() / 255 / 255) + return QColor(f(red), f(green), f(blue)); +}*/ + +void GEditor::paintCell(QPainter &p, int row, int) +{ + QRect ur; + GLine *l; + int x1, y1, x2, y2, xs1, xs2; + QColor color, a, b, c; + int xmin, lmax; + int i; + int realRow; + bool folded; + bool highlight; + bool odd; + bool drawSep; + int ysep, wsep; + + ur = QRect(0, row * _cellh, _cellw, _cellh); + contentsToViewport(ur.x(), ur.y(), x1, y1); + ur.setRect(-x1, -y1, ur.width(), ur.height()); + + if (row < 0) + realRow = row; + else + realRow = viewToReal(row); + + if (realRow < 0 || realRow >= numLines()) + { + QColor color = viewport()->paletteBackgroundColor(); + if (flashed) + color = QColor(QRgb(color.rgb() ^ 0x00FFFFFF)); + p.fillRect(0, 0, _cellw, _cellh, color); + return; + } + + // Colorize as soon as possible + highlight = (doc->getHighlightMode() != GDocument::None) && (getFlag(HighlightImmediately) || !doc->isLineEditedSomewhere(realRow)); //(l->modified && realRow == y && !getFlag(HighlightCurrent))); + if (highlight) + { + painting = true; + doc->colorize(realRow); + painting = false; + } + + l = doc->lines.at(realRow); + + folded = l->proc && isFolded(realRow); + + drawSep = false; + ysep = 0; + if (realRow > 0) + { + int nextRow = viewToReal(row + 1); + + if (l->isEmpty() && nextRow < numLines()) + { + GLine *l2 = doc->lines.at(nextRow); + if (l2->proc && !isFolded(nextRow)) + { + drawSep = true; + ysep = _cellh / 2; + } + } + else if (l->proc && (folded || !doc->lines.at(viewToReal(row - 1))->isEmpty())) + drawSep = true; + } + + if (getFlag(ChangeBackgroundAtLimit)) //) && doc->getHighlightMode() != GDocument::None) + { + if (l->proc) + _oddLine = !_oddLine; + odd = _oddLine; + //drawSep = false; + } + else + odd = false; + + //xmin = (ur.left() - margin) / charWidth; + xmin = posToColumn(realRow, 0) - 1; + if (xmin < 0) + xmin = 0; + //lmax = 2 + visibleWidth() / charWidth; + lmax = 2 + posToColumn(realRow, visibleWidth()) - xmin; + //if (row == 0) + // qDebug("%d: %d %d %d %d (%d %d)", row, ur.left(), ur.top(), ur.width(), ur.height(), xmin, lmax); + + // Line background + if (realRow == doc->currentLine()) //l->flag & (1 << GLine::CurrentFlag)) + a = styles[GLine::Current].color; + + if (getFlag(ShowCurrentLine) && realRow == y) + b = styles[GLine::Line].color; + + //QPainter p(cache); + + if (isEnabled()) + color = calc_color(a, b, odd ? _oddBackground : styles[GLine::Background].color); + else + color = palette().color(QPalette::Disabled, QPalette::Base); + + p.fillRect(0, 0, _cellw, _cellh, color); + + if (drawSep && getFlag(ChangeBackgroundAtLimit) && isEnabled()) + { + if (ysep) + { + //int xsep = qMax(0, margin - qMin(_cellh, 12) - contentsX()); + p.fillRect(0, ysep, visibleWidth(), _cellh - ysep, !odd ? _oddBackground : styles[GLine::Background].color); + } + + drawSep = false; + } + + p.setFont(normalFont); + //p.setFont(painter->font()); + //p.translate(-ur.left(), 0); + + p.translate(-contentsX(), 0); + + // Show string + if ((_showRow == realRow || _showString.length())) + paintShowString(p, l, margin, _ytext, xmin, lmax, _cellh, realRow); + + // Selection background + xs1 = 0; + xs2 = 0; + if (doc->hasSelection()) + { + doc->getSelection(&y1, &x1, &y2, &x2, _insertMode); + + if (realRow >= y1 && realRow <= y2 && !(realRow == y2 && x2 == 0)) + { + if (_insertMode) + { + x1 = lineWidth(y1, x1); + x2 = lineWidth(y2, x2); + } + else + { + if (realRow > y1 || x1 == 0) + x1 = margin; + else + x1 = lineWidth(y1, x1); + + if (realRow < y2) + x2 = _cellw + 1; + else + x2 = lineWidth(y2, x2); + } + + p.fillRect(x1, 0, x2 - x1, _cellh, styles[GLine::Selection].color); + xs1 = x1; + xs2 = x2; + } + } + + // Procedure separation + + if (drawSep) + { + int xsep = 0; //qMax(0, margin - qMin(_cellh, 12) - contentsX()); + + p.translate(contentsX(), 0); + + if (getFlag(BlendedProcedureLimits)) + { + if (!_blend_pattern) + { + _blend_pattern = new QImage(64, _cellh / 2, QImage::Format_ARGB32_Premultiplied); + make_blend(_blend_pattern, styles[GLine::Selection].color); + } + + wsep = visibleWidth(); + + for (i = xsep; i < wsep; i += _blend_pattern->width()) + p.drawImage(i, ysep, *_blend_pattern, 0, 0, QMIN(wsep - i, _blend_pattern->width()), _cellh - ysep); + + if (xs1 && xs2) + p.fillRect(x1, 0, x2 - x1, _cellh, styles[GLine::Selection].color); + //p.drawTiledImage(0, ysep, visibleWidth(), _cellh - ysep, pattern); + } + else if (getFlag(ChangeBackgroundAtLimit)) + { + if (ysep) + { + p.fillRect(0, ysep, visibleWidth(), _cellh - ysep, !odd ? _oddBackground : styles[GLine::Background].color); + if (xs1 && xs2) + p.fillRect(x1, 0, x2 - x1, _cellh, styles[GLine::Selection].color); + } + } + else if (getFlag(ShowProcedureLimits)) + { + //QBrush brush(styles[GLine::Selection].color, Qt::Dense4Pattern); + //p.fillRect(0, 0, cache->width(), 1, brush); + p.setPen(styles[GLine::Selection].color); + p.setOpacity(0.5); + p.drawLine(xsep, ysep, visibleWidth() - 1, ysep); + p.setOpacity(1); + } + + p.translate(-contentsX(), 0); + } + + // Margin + if (!getFlag(HideMargin) && (margin > ur.left())) + { + //if (!l->flag) + // p.fillRect(0, 0, margin, _cellh, color); //styles[GLine::Background].color); + + if (getFlag(ShowModifiedLines)) + { + if (l->changed || l->saved) + { + int w = qMax(2, margin - qMin(_cellh, 12) - 1); + p.setOpacity(0.5); + p.fillRect(0, 0, w, _cellh, styles[l->changed ? GLine::Breakpoint : GLine::Highlight].color); + p.setOpacity(1); + } + //if (l->modified) + // p.fillRect((margin - 2) / 2, _cellh / 2, 1, 1, styles[GLine::Breakpoint].color); + } + //else + // p.fillRect(0, 0, margin - 2, _cellh, odd ? _oddBackground : styles[GLine::Background].color); + /*else if (getFlag(ShowCurrentLine)) + p.fillRect(0, 0, margin - 2, _cellh, styles[GLine::Line].color);*/ + + //x1 = 0; + + if (getFlag(ShowLineNumbers) && (!drawSep || ysep == 0)) + { + int n = realRow + 1 + _firstLineNumber; + if ((n % 10) == 0 || (l->proc && folded)) + p.setPen(styles[GLine::Normal].color); + else + p.setPen(styles[GLine::Selection].color); + p.drawText(2, _ytext, QString::number(n).rightJustify(lineNumberLength)); + } + } + + // Line text + if (!highlight) + { + // Highlight braces + if (getFlag(HighlightBraces)) + { + if (realRow == y1m && x1m >= 0) + highlight_text(p, lineWidth(y1m, x1m), lineWidth(y1m, x1m + 1), _cellh - 1, styles[GLine::Highlight].color, styles[GLine::Normal].color); + if (realRow == y2m && x2m >= 0) + highlight_text(p, lineWidth(y2m, x2m), lineWidth(y2m, x2m + 1), _cellh - 1, styles[GLine::Highlight].color, styles[GLine::Normal].color); + } + + if (l->s.length()) + { + p.setPen(styles[GLine::Normal].color); + //p.drawText(margin + xmin * charWidth, fm.ascent() + 1, l->s.getString().mid(xmin, lmax)); + //p.drawText(QRect(lineWidth(realRow, xmin), 0, visibleWidth(), _cellh), l->s.getString().mid(xmin, lmax), textOption); + //p.drawText(lineWidth(realRow, xmin), fm.ascent(), l->s.getString().mid(xmin, lmax)); + drawTextWithTab(p, lineWidth(realRow, 0), lineWidth(realRow, xmin), _ytext, l->s.getString().mid(xmin, lmax)); + if (getFlag(ShowDots)) + { + i = find_last_non_space(l->s.getString()) + 1; + if (i >= 0 && i < (xmin + lmax)) + paintDottedSpaces(p, realRow, i, QMIN(xmin + lmax, (int)l->s.length()) - i); + } + } + } + else + { + paintText(p, l, margin, _ytext, xmin, lmax, _cellh, xs1, xs2, realRow, color); + } + + // Folding symbol + if (!getFlag(NoFolding) && margin && l->proc) + { + QPalette pal; + QStyleOption opt; + int w; + + pal.setColor(QColorGroup::Foreground, styles[GLine::Normal].color); + w = qMin(_cellh, 12); + //opt.rect = QRect(margin - 12, 0, 12, 12); + opt.rect = QRect(margin - w - 2, 0, w, _cellh); + opt.palette = pal; + opt.state |= QStyle::State_Enabled; + + style()->drawPrimitive(folded ? QStyle::PE_IndicatorArrowRight : QStyle::PE_IndicatorArrowDown, &opt, &p); + } + + // Breakpoint symbol + if ((l->flag & (1 << GLine::BreakpointFlag)) && _breakpoint && !_breakpoint->isNull()) + { + //p.fillRect(margin - 10, 0, 8, _cellh, styles[GLine::Breakpoint].color); + //updateBreakpoint(styles[GLine::Background].color.rgb(), styles[GLine::Breakpoint].color.rgb()); + p.drawPixmap(margin - _breakpoint->width() - 2, (_cellh - _breakpoint->height()) / 2, *_breakpoint); + } + + // Bookmark symbol + if ((l->flag & (1 << GLine::BookmarkFlag)) && _bookmark && !_bookmark->isNull()) + { + //p.fillRect(margin - 10, 0, 8, _cellh, styles[GLine::Breakpoint].color); + //updateBreakpoint(styles[GLine::Background].color.rgb(), styles[GLine::Breakpoint].color.rgb()); + p.drawPixmap(margin - _bookmark->width() - 2, (_cellh - _bookmark->height()) / 2, *_bookmark); + } + + // Text cursor + if (realRow == y && (_cursor || getFlag(AlwaysShowCursor))) + { + QColor color = styles[GLine::Normal].color; + int xc = lineWidth(realRow, x); + int wc = 2; + //p.fillRect(QMIN((int)l->s.length(), x) * charWidth + margin, 0, 1, _cellh, styles[GLine::Normal].color); + if (_insertMode) + { + wc = lineWidth(realRow, x + 1) - xc; + color.setAlpha(80); + } + else + color.setAlpha(160); + + p.fillRect(xc, 0, wc, _cellh, color); + } + + p.translate(contentsX(), 0); + + // Flash + if (flashed) + { + //p.setCompositionMode(QPainter::CompositionMode_Xor); + p.fillRect(0, 0, visibleWidth(), _cellh, Qt::black); + } + + + //if (cache->width() < visibleWidth()) + // qDebug("cache->width() = %d < visibleWidth() = %d", cache->width(), visibleWidth()); + //painter->drawPixmap(ur.left(), 0, *cache, 0, 0, cache->width(), cache->height()); //, _cellw, _cellh); +} + + +void GEditor::drawContents(QPainter *p, int cx, int cy, int cw, int ch) +{ + int rowfirst = rowAt(cy); + int rowlast = rowAt(cy + ch - 1); + + if (getFlag(ChangeBackgroundAtLimit) && rowfirst > 0) + _oddLine = doc->getLimitIndex(viewToReal(rowfirst - 1)) & 1; + else + _oddLine = true; + + if (_checkCache) + updateCache(); + + QPainter pc(_cache); + + // Go through the rows + for (int r = rowfirst; r <= rowlast; ++r) + { + paintCell(pc, r, 0); + pc.translate(0, _cellh); + } + + pc.end(); + + //ur = QRect(0, row * _cellh, _cellw, _cellh); + //qDebug("drawContents: %d %d %d %d : %d %d . %d : %d %d", cx, cy, cw, ch, contentsX(), contentsY(), rowfirst * _cellh, viewport()->x(), viewport()->y()); + //p->setClipRect(cx, cy, cw, ch); + p->drawPixmap(contentsX(), rowfirst * _cellh, *_cache, 0, 0, _cellw, _cellh * (rowlast - rowfirst + 1)); //, _cellw, _cellh); + + //qDebug("%d %d", contentsX(), contentsY()); + //p->setPen(styles[GLine::Normal].color); + //p->drawText(contentsX(), contentsY() + 16, QString::number(y) + " : " + QString::number(x)); + + if (_blend_pattern) + { + delete _blend_pattern; + _blend_pattern = 0; + } +} + +void GEditor::checkMatching() +{ + static GString look("()[]{}"); + + GString str; + char c, match, search; + int pos, len; + bool backward; + int old_y1m, old_y2m, old_x1m, old_x2m; + int level; + bool ignore; + + old_y1m = y1m; + old_y2m = y2m; + old_x1m = x1m; + old_x2m = x2m; + + if (y < 0 || y >= (int)numLines()) + goto __NOT_FOUND; + + str = doc->lines.at(y)->s; + len = str.length(); + + pos = -1; + + if (x > 0) + { + c = str.at(x - 1); + pos = look.indexOf(c); + x1m = x - 1; + } + + if (pos < 0 && x < len) + { + c = str.at(x); + pos = look.indexOf(c); + x1m = x; + } + + if (pos < 0) + goto __NOT_FOUND; + + y1m = y; + + backward = pos & 1; + match = c; + search = look.at(pos + (backward ? -1 : 1)); + + //fprintf(stderr, "find '%c' at pos %d. Searching %s for '%c'\n", match, x1m, backward ? "backward" : "forward", search); + + y2m = y1m; + x2m = x1m; + level = 0; + ignore = false; + + for(;;) + { + if (backward) + { + x2m--; + if (x2m < 0) + { + y2m--; + if (y2m < 0) + goto __NOT_FOUND; + str = doc->lines.at(y2m)->s; + len = str.length(); + x2m = len; + continue; + } + } + else + { + x2m++; + if (x2m >= len) + { + y2m++; + if (y2m >= (int)numLines()) + goto __NOT_FOUND; + str = doc->lines.at(y2m)->s; + len = str.length(); + x2m = -1; + continue; + } + } + + c = str.at(x2m); + + if (ignore) + { + if (c == '"' && (x2m == 0 || str.at(x2m - 1) != '\\')) + ignore = false; + continue; + } + + if (c == search) + { + if (level <= 0) + goto __FOUND; + level--; + } + else if (c == match) + { + level++; + } + else if (c == '"') + { + ignore = true; + } + } + +__NOT_FOUND: + + y1m = y2m = -1; + x1m = x2m = -1; + +__FOUND: + + if (y1m != old_y1m || y2m != old_y2m || x1m != old_x1m || x2m != old_x2m) + { + if (old_y1m >= 0) updateLine(old_y1m); + if (old_y2m >= 0) updateLine(old_y2m); + if (y1m >= 0) updateLine(y1m); + if (y2m >= 0) updateLine(y2m); + //fprintf(stderr, "y1m = %d x1m = %d y2m = %d x2m = %d\n", y1m, x1m, y2m, x2m); + } +} + +#if 0 +void GEditor::oldCheckMatching() +{ + + + GString str = doc->lines.at(y)->s; + int len = (int)str.length(); + + char c, cr; + int pos, d, l, bx, by, bx2; + char s[len]; + bool ignore; + int i; + + bx = -1; + bx2 = -1; + by = -1; + ignore = false; + + if (len == 0 || x > len) + goto __OK; + + pos = -1; + + for (i = 0; i < len; i++) + { + c = str.at(i); + + if (ignore) + { + if (c == '\\') + { + s[i] = c; + i++; + s[i] = (i < len) ? str.at(i) : 0; + continue; + } + + if (c == '"') + ignore = false; + else + c = ' '; + } + else + { + if (c == '"') + ignore = true; + } + + s[i] = c; + } + + c = 0; + + if (x > 0 && x <= len) + { + bx2 = x - 1; + c = s[bx2]; + pos = look.find(c); + } + + if (pos < 0 && x < len) + { + bx2 = x; + c = s[bx2]; + pos = look.find(c); + } + + if (c && pos >= 0) + { + d = (pos & 1) ? -1 : 1; + cr = look.at(pos + d); + + pos = bx2; + l = 0; + for(;;) + { + pos += d; + if (pos < 0 || pos >= len) + break; + + if (s[pos] == c) + l++; + else if (s[pos] == cr) + { + l--; + if (l < 0) + { + bx = pos; + by = y; + break; + } + } + } + } + +__OK: + + if (by != ym || bx != x1m || bx2 != x2m) + { + if (ym >= 0) + updateLine(ym); + ym = by; + x1m = bx; + x2m = bx2; + if (ym >= 0) + updateLine(ym); + } +} +#endif + +void GEditor::leaveCurrentLine() +{ + if (y < 0 || y >= numLines()) + return; + + doc->colorize(y, true); + + if (!_insertMode && x > lineLength(y)) + x = lineLength(y); +} + + +bool GEditor::cursorGoto(int ny, int nx, bool mark) +{ + bool setxx; + bool change = false; + + if (!mark) + { + if (doc->hasSelection()) + doc->hideSelection(); + } + + setxx = xx != nx; + + if (ny == y) + { + if (nx < 0 && ny > 0) + { + ny = viewToReal(realToView(y) - 1); + nx = lineLength(ny); + } + else if (!_insertMode && nx > lineLength(ny) && ny < (numLines() - 1)) + { + ny = viewToReal(realToView(y) + 1); + if (ny >= numLines()) + ny = y; + else + nx = 0; + } + } + + if (ny < 0) + { + nx = QMAX(0, nx); + ny = 0; + } + else if (ny >= numLines()) + { + ny = numLines() - 1; + nx = lineLength(ny); + } + else + ny = checkFolded(ny); + + if (nx < 0) + nx = 0; + else + { + int xmax = _insertMode ? QMAX((int)(_cellw / _charWidth[' ']) + 1, lineLength(largestLine)) : lineLength(ny); + + if (nx > xmax) + nx = xmax; + } + + if (ny != y) + leaveCurrentLine(); + + if (y != ny || x != nx) + { + int oy = y; + + if (mark) + { + if (!doc->hasSelection(this)) + doc->startSelection(this, y, x); + } + + y = ny; + x = nx; + if (setxx) + xx = x; + + updateLine(oy); + + if (hasFocus()) + startBlink(); + else + updateLine(y); + + ensureCursorVisible(); + //QTimer::singleShot(0, this, SLOT(ensureCursorVisible())); + + change = true; + + if (mark) + doc->endSelection(y, x); + + if (oy != y && !doc->insideUndo()) + _cutBuffer.clear(); + + emit cursorMoved(); + } + else if (center) + ensureCursorVisible(); + + checkMatching(); + + return change; +} + +void GEditor::insert(QString text) +{ + doc->begin(); + doc->eraseSelection(_insertMode); + unfoldLine(y); + doc->insert(y, x, text); + doc->end(); + cursorGoto(doc->yAfter, doc->xAfter, false); +} + +void GEditor::cursorLeft(bool shift, bool ctrl) +{ + if (ctrl && x > 0) + { + int nx = doc->wordLeft(y, x); + cursorGoto(y, nx, shift); + } + else + cursorGoto(y, x - 1, shift); +} + + +void GEditor::cursorRight(bool shift, bool ctrl) +{ + if (ctrl && x < lineLength(y)) + { + int nx = doc->wordRight(y, x); + cursorGoto(y, nx, shift); + } + else + cursorGoto(y, x + 1, shift); +} + + +void GEditor::cursorUp(bool shift, bool ctrl, bool alt) +{ + if (alt) + { + if (ctrl) + movePreviousSameIndent(shift); + else + { + QString str; + int x1, y1, x2, y2; + bool hasSelection; + + hasSelection = doc->hasSelection(); + + if (hasSelection) + { + doc->getSelection(&y1, &x1, &y2, &x2, _insertMode); + if (x2) y2++; + } + else + { + y1 = y; + y2 = y + 1; + x1 = x; + } + + if (y1 > 0) + { + str = doc->lines.at(y1 - 1)->s.getString() + '\n'; + + doc->begin(); + doc->remove(y1 - 1, 0, y1, 0); + doc->insert(y2 - 1, 0, str); + if (hasSelection) + { + cursorGoto(y1 - 1, 0, false); + doc->startSelection(this, y1 - 1, 0); + doc->endSelection(y2 - 1, 0); + } + doc->end(); + } + } + } + else if (ctrl) + { + int yl = doc->getPreviousLimit(y); + if (yl >= 0) + cursorGoto(yl, xx, shift); + } + else + cursorGoto(viewToReal(realToView(y) - 1), xx, shift); +} + + +void GEditor::cursorDown(bool shift, bool ctrl, bool alt) +{ + if (alt) + { + if (ctrl) + moveNextSameIndent(shift); + else + { + QString str; + int x1, y1, x2, y2; + bool hasSelection; + + hasSelection = doc->hasSelection(); + + if (hasSelection) + { + doc->getSelection(&y1, &x1, &y2, &x2, _insertMode); + if (x2) y2++; + } + else + { + y1 = y; + y2 = y + 1; + x1 = x; + } + + if (y2 < (numLines() - 1)) + { + str = doc->lines.at(y2)->s.getString() + '\n'; + + doc->begin(); + doc->remove(y2, 0, y2 + 1, 0); + doc->insert(y1, 0, str); + if (hasSelection) + { + cursorGoto(y2 + 1, 0, false); + doc->startSelection(this, y1 + 1, 0); + doc->endSelection(y2 + 1, 0); + } + doc->end(); + } + } + } + else if (!ctrl) + cursorGoto(QMIN(numLines() - 1, viewToReal(realToView(y) + 1)), xx, shift); + else + { + int yl = doc->getNextLimit(y); + + if (yl >= 0) + cursorGoto(yl, xx, shift); + else + cursorGoto(numLines(), 0, shift); + } +} + +void GEditor::movePreviousSameIndent(bool shift) +{ + int indent = doc->getIndent(y); + int i2, y2; + + for(y2 = y - 1; y2 >= 0; y2--) + { + i2 = doc->getIndent(y2); + if (i2 == indent && i2 < doc->lineLength(y2)) + { + cursorGoto(y2, x, shift); + break; + } + } +} + +void GEditor::cursorHome(bool shift, bool ctrl, bool alt) +{ + if (ctrl) + cursorGoto(0, 0, shift); + else + { + int indent = doc->getIndent(y); + if (x == indent) + cursorGoto(y, 0, shift); + else + cursorGoto(y, indent, shift); + } +} + +void GEditor::moveNextSameIndent(bool shift) +{ + int indent = doc->getIndent(y); + int i2, y2; + + for(y2 = y + 1; y2 < numLines(); y2++) + { + i2 = doc->getIndent(y2); + if (i2 == indent && i2 < doc->lineLength(y2)) + { + cursorGoto(y2, x, shift); + break; + } + } +} + +void GEditor::cursorEnd(bool shift, bool ctrl, bool alt) +{ + if (ctrl) + cursorGoto(numLines(), 0, shift); + else + cursorGoto(y, lineLength(y), shift); +} + +void GEditor::cursorPageUp(bool shift, bool alt) +{ + int page = visibleHeight() / _cellh; + cursorGoto(viewToReal(realToView(y) - page), 0, shift); +} + +void GEditor::cursorPageDown(bool shift, bool alt) +{ + int page = visibleHeight() / _cellh; + cursorGoto(viewToReal(realToView(y) + page), 0, shift); +} + +void GEditor::newLine() +{ + doc->begin(); + doc->eraseSelection(_insertMode); + doc->insert(y, x, '\n' + doc->lines.at(y)->s.left(QMIN(x, doc->getIndent(y)))); + cursorGoto(doc->yAfter, doc->xAfter, false); + doc->end(); +} + +void GEditor::del(bool ctrl) +{ + if (doc->hasSelection()) + { + doc->eraseSelection(_insertMode); + return; + } + + doc->begin(); + + if (x >= lineLength(y)) + { + if (y < (numLines() - 1)) + { + if (_insertMode) + doc->insert(y, x, QString(), TRUE); + doc->remove(y, x, y + 1, 0); + } + } + else + { + if (ctrl && x < lineLength(y)) + { + int nx = doc->wordRight(y, x); + doc->remove(y, x, y, nx); + } + else + doc->remove(y, x, y, x + 1); + } + + doc->end(); +} + +void GEditor::backspace(bool ctrl) +{ + int indent; + int yy; + bool empty; + + if (doc->hasSelection()) + { + doc->eraseSelection(_insertMode); + return; + } + + doc->begin(); + + indent = doc->getIndent(y); + + if (x > 0 && x <= indent) + { + yy = y; + indent = 0; + while (yy > 0) + { + yy--; + indent = doc->getIndent(yy, &empty); + if (!empty && x > indent) + break; + } + cursorGoto(y, indent, true); + del(false); + } + else + { + if (ctrl && x > 0) + { + int nx = doc->wordLeft(y, x); + doc->remove(y, nx, y, x); + } + else + { + if (cursorGoto(y, x - 1, false)) + del(false); + } + } + + doc->end(); +} + +void GEditor::selectCurrentLine() +{ + cursorGoto(y, 0, FALSE); + cursorGoto(y + 1, 0, TRUE); +} + +void GEditor::deleteCurrentLine() +{ + bool im; + + if (doc->hasSelection()) + { + doc->eraseSelection(_insertMode); + return; + } + + im = _insertMode; + _insertMode = FALSE; + doc->begin(); + selectCurrentLine(); + del(FALSE); + doc->end(); + _insertMode = im; +} + +void GEditor::clearLine(bool before, bool after) +{ + int ox = x; + + doc->begin(); + + if (before) + { + QString ins; + + doc->remove(y, 0, y, x + 1); + ins.fill(' ', ox + 1); + doc->insert(y, 0, ins); + x = ox; + } + + if (after) + { + doc->remove(y, x, y, doc->lineLength(y) -1); + } + + doc->end(); +} + +void GEditor::clearAfter(int n) +{ + int ox = x; + QString ins; + + doc->begin(); + doc->remove(y, x, y, x + n); + ins.fill(' ', n); + doc->insert(y, ox, ins); + x = ox; + doc->end(); +} + +void GEditor::clearDocument(bool before, bool after) +{ + int i; + int ox = x; + + doc->begin(); + + if (before && after) + { + for (i = 0; i < doc->numLines(); i++) + doc->remove(i, 0, i, doc->lineLength(i)); + } + else if (before) + { + QString ins; + + for (i = 0; i < y; i++) + doc->remove(i, 0, i, doc->lineLength(i)); + + doc->remove(y, 0, y, x); + ins.fill(' ', ox); + doc->insert(y, 0, ins); + x = ox; + } + else if (after) + { + doc->remove(y, x, y, doc->lineLength(y) -1); + + for (i = y + 1; i < doc->numLines(); i++) + doc->remove(i, 0, i, doc->lineLength(i)); + } + + x = ox; + doc->end(); +} + +void GEditor::tab(bool back) +{ + QString ins; + int y1, x1, y2, x2, yy; + int indent, i; + bool empty; + int tw = doc->getTabWidth(); + + if (!doc->hasSelection()) + { + if (!back) + { + ins.fill(' ', tw - (x % tw)); + insert(ins); + return; + } + + doc->startSelection(this, y, 0); + doc->endSelection(y + 1, 0); + } + + doc->getSelection(&y1, &x1, &y2, &x2, _insertMode); + doc->startSelection(this, y1, 0); + if (x2) + y2++; + doc->endSelection(y2, 0); + + indent = 65536; + for (yy = y1; yy < y2; yy++) + { + i = doc->getIndent(yy, &empty); + if (!empty) + indent = QMIN(indent, i); + } + + if (back && indent <= 0) + return; + + doc->begin(); + + if (!back) + { + ins.fill(' ', tw - (indent % tw)); + + for (yy = y1; yy < y2; yy++) + { + doc->insert(yy, 0, ins); + doc->colorize(yy); + } + } + else + { + indent %= tw; + if (indent == 0) + indent = tw; + + ins.fill(' ', indent); + + for (yy = y1; yy < y2; yy++) + { + if (doc->lines.at(yy)->s.left(indent) == ins) + { + doc->remove(yy, 0, yy, indent); + doc->colorize(yy); + } + } + } + + doc->startSelection(this, y1, 0); + doc->endSelection(y2, 0); + + doc->end(); +} + + +void GEditor::copy(bool mouse) +{ + if (!doc->hasSelection()) + return; + + QString text = doc->getSelectedText(_insertMode).getString(); + QApplication::clipboard()->setText(text, mouse ? QClipboard::Selection : QClipboard::Clipboard); +} + +void GEditor::cut() +{ + if (!doc->hasSelection()) + { + doc->begin(); + selectCurrentLine(); + _cutBuffer += doc->getSelectedText(_insertMode); + QApplication::clipboard()->setText(_cutBuffer.getString(), QClipboard::Clipboard); + doc->eraseSelection(_insertMode); + doc->end(); + return; + } + + copy(false); + doc->eraseSelection(_insertMode); +} + +void GEditor::paste(bool mouse) +{ + QString text; + GString gtext; + QString subType("plain"); + int i, i2, xs, len; + QString tab; + + text = QApplication::clipboard()->text(subType, mouse ? QClipboard::Selection : QClipboard::Clipboard); + + if (text.length() == 0) + return; + + tab.fill(' ', doc->getTabWidth()); + text.replace("\t", tab); + + for (i = 0; i < text.length(); i++) + { + if ((text[i] < ' ' || text[i].isSpace()) && text[i] != '\n' && text[i] != '\r') + text[i] = ' '; + } + + if (_insertMode) + { + gtext = text; + doc->begin(); + i = 0; + while (i < (int)text.length()) + { + i2 = gtext.findNextLine(i, len); + xs = x; + insert(text.mid(i, len)); + x = xs; + y++; + if (y >= doc->numLines()) + insert("\n"); + i = i2; + } + doc->end(); + } + else + insert(text); +} + +void GEditor::undo() +{ + doc->undo(); + //cursorGoto(doc->yAfter, doc->xAfter, false); +} + +void GEditor::redo() +{ + doc->redo(); + //cursorGoto(doc->yAfter, doc->xAfter, false); +} + +void GEditor::selectAll() +{ + cursorGoto(0, 0, false); + cursorGoto(numLines(), 0, true); +} + +void GEditor::keyPressEvent(QKeyEvent *e) +{ + bool shift = e->state() & Qt::ShiftButton; + bool ctrl = e->state() & Qt::ControlButton; + bool alt = e->state() & Qt::AltButton; + + e->accept(); + + if (doc->isReadOnly()) + { + switch (e->key()) + { + case Qt::Key_Left: + cursorLeft(shift, ctrl); return; + case Qt::Key_Right: + cursorRight(shift, ctrl); return; + case Qt::Key_Up: + cursorUp(shift, ctrl, false); return; + case Qt::Key_Down: + cursorDown(shift, ctrl, false); return; + case Qt::Key_Home: + cursorHome(shift, ctrl, alt); return; + case Qt::Key_End: + cursorEnd(shift, ctrl, alt); return; + case Qt::Key_Prior: + cursorPageUp(shift, alt); return; + case Qt::Key_Next: + cursorPageDown(shift, alt); return; + case Qt::Key_Return: + if (ctrl) + expand(shift); + return; + } + + if (!ctrl) + goto __IGNORE; + + switch(e->key()) + { + case Qt::Key_C: + copy(); return; + case Qt::Key_A: + selectAll(); return; + } + } + else + { + QString t = e->text(); + if (t.length() && (t.at(0).isPrint() || (t.at(0).unicode() == 9 && ctrl)) + && e->key() != Qt::Key_Return + && e->key() != Qt::Key_Enter + && e->key() != Qt::Key_Delete + && e->key() != Qt::Key_Backspace) + { + if (_insertMode) + { + doc->begin(); + del(false); + insert(t); + doc->end(); + } + else + insert(t); + return; + } + + switch (e->key()) + { + case Qt::Key_Left: + cursorLeft(shift, ctrl); return; + case Qt::Key_Right: + cursorRight(shift, ctrl); return; + case Qt::Key_Up: + cursorUp(shift, ctrl, alt); return; + case Qt::Key_Down: + cursorDown(shift, ctrl, alt); return; + case Qt::Key_Enter: + newLine(); return; + case Qt::Key_Return: + if (ctrl) + expand(shift); + else + newLine(); + return; + case Qt::Key_Home: + cursorHome(shift, ctrl, alt); return; + case Qt::Key_End: + cursorEnd(shift, ctrl, alt); return; + case Qt::Key_Backspace: + backspace(ctrl); return; + case Qt::Key_Delete: + del(ctrl); return; + case Qt::Key_Prior: + cursorPageUp(shift, alt); return; + case Qt::Key_Next: + cursorPageDown(shift, alt); return; + case Qt::Key_Tab: + tab(false); return; + case Qt::Key_BackTab: + tab(true); return; + case Qt::Key_Insert: + setInsertMode(!_insertMode); + } + + if (!ctrl) + goto __IGNORE; + + switch(e->key()) + { + case Qt::Key_C: + copy(); return; + case Qt::Key_X: + cut(); return; + case Qt::Key_V: + paste(); return; + case Qt::Key_Z: + undo(); return; + case Qt::Key_Y: + redo(); return; + case Qt::Key_A: + selectAll(); return; + case Qt::Key_D: + deleteCurrentLine(); return; + } + } + +__IGNORE: + + e->ignore(); +} + + +int GEditor::posToColumn(int y, int px) +{ + int i = -1, lw; + int len = doc->lineLength(y); + QString s = doc->lines.at(y)->s.getString(); + int d, f; + + if (px < margin || px >= visibleWidth()) + _posOutside = true; + + if (len == 0) + return (px - margin) / _charWidth[' ']; + + px += contentsX(); + + d = 0; + f = len; + while (f > d) + { + if (i < 0) + i = px / _charWidth['m']; + else + i = (d + f) / 2; + + lw = lineWidth(y, i); + if (px < lw) + { + f = i; + continue; + } + + /*if (_sameWidth) + lw += _sameWidth; + else*/ + lw = lineWidth(y, i + 1); //+= getStringWidth(s.mid(i, 1)); //fm.width(s[i]); + if (px >= lw) + { + d = i + 1; + continue; + } + + d = i; + break; + } + + _posOutside = d > len; + return d; +} + + +int GEditor::posToLine(int py) +{ + int ny; + + ny = rowAt(contentsY() + py); + _posOutside = true; + if (ny < 0) + ny = 0; + else if (ny >= visibleLines()) + ny = visibleLines() - 1; + else + _posOutside = false; + + return viewToReal(ny); +} + +bool GEditor::posToCursor(int px, int py, int *y, int *x) +{ + int nx, ny; + bool outside; + + ny = posToLine(py); + outside = _posOutside; + nx = posToColumn(ny, px); + if (_insertMode) + nx = QMAX(0, nx); //QMIN(nx, lineLength(ny))); + else + nx = QMAX(0, QMIN(nx, lineLength(ny))); + + *y = ny; + *x = nx; + + return outside || _posOutside; +} + +void GEditor::cursorToPos(int y, int x, int *px, int *py) +{ + int npx, npy; + + y = realToView(y); + + npy = y * _cellh - contentsY(); + //npx = x * charWidth - contentsX() + margin; + npx = lineWidth(y, x) - contentsX(); + + *py = npy; + *px = npx; +} + +bool GEditor::updateCursor() +{ + if (contentsX() + lastx >= margin) + { + viewport()->setCursor(_saveCursor); + return false; + } + else + { + viewport()->setCursor(Qt::ArrowCursor); + return true; + } +} + +void GEditor::mousePressEvent(QMouseEvent *e) +{ + bool shift = e->state() & Qt::ShiftButton; + int nx, ny; + //stopAutoScroll(); + //d->dnd_startpos = e->pos(); + + if (e->button() != Qt::LeftButton && e->button() != Qt::MidButton) + return; + + posToCursor(e->pos().x(), e->pos().y(), &ny, &nx); + + lastx = e->pos().x(); + left = updateCursor(); + + if (!left) + cursorGoto(ny, nx, shift); +} + +void GEditor::mouseMoveEvent(QMouseEvent *e) +{ + if ((e->buttons() & Qt::MouseButtonMask) == Qt::LeftButton) + { + if (left) + { + //ly = posToLine(e->pos().y()); + if (!scrollTimer->isActive()) + cursorGoto(posToLine(e->pos().y()), 0, false); + } + + if (!scrollTimer->isActive()) + { + stopBlink(); + scrollTimer->start(25, false); + } + } + + lastx = e->pos().x(); + left = updateCursor(); +} + +void GEditor::mouseDoubleClickEvent(QMouseEvent *e) +{ + int ny; + + _dblclick = true; + + if (left) + { + ny = posToLine(e->pos().y()); + if (!getFlag(NoFolding) && doc->lines.at(ny)->proc) + { + if (isFolded(ny)) + foldAll(); + else + unfoldAll(); + } + emit marginDoubleClicked(ny); + return; + } + + if (e->button() == Qt::LeftButton && !(e->state() & Qt::ShiftButton)) + { + int xl, xr; + + xl = doc->wordLeft(y, x, true); + xr = doc->wordRight(y, x, true); + + if (xr > xl) + { + doc->hideSelection(); + cursorGoto(y, xl, false); + cursorGoto(y, xr, true); + copy(true); + } + } +} + +void GEditor::mouseReleaseEvent(QMouseEvent *e) +{ + if (scrollTimer->isActive()) + { + scrollTimer->stop(); + startBlink(); + copy(true); + } + else + { + if (left && !_dblclick) + { + int ny = posToLine(e->pos().y()); + if (!getFlag(NoFolding) && doc->lines.at(ny)->proc) + { + if (isFolded(ny)) + unfoldLine(ny); + else + foldLine(ny); + } + emit marginClicked(ny); + } + + if (e->button() == Qt::MidButton) + paste(true); + } + + _dblclick = false; +} + + +void GEditor::resizeEvent(QResizeEvent *e) +{ + Q3ScrollView::resizeEvent(e); + //baptizeVisible(); + updateWidth(); +} + +void GEditor::viewportResizeEvent(QResizeEvent *e) +{ + Q3ScrollView::viewportResizeEvent(e); + updateWidth(); + if (!_ensureCursorVisibleLater) + { + _ensureCursorVisibleLater = true; + QTimer::singleShot(0, this, SLOT(ensureCursorVisible())); + } + _checkCache = true; +} + +bool GEditor::isCursorVisible() +{ + int px, py; + + cursorToPos(y, x, &px, &py); + + return !(px < margin || px > (visibleWidth() - QMAX(2, margin)) || py < 0 || py > (visibleHeight() - _cellh)); +} + +void GEditor::ensureCursorVisible() +{ + int xx, yy; + + if (!isUpdatesEnabled() || !isVisible()) + return; + + xx = lineWidth(y, x); // + _charWidth['m'] / 2 + yy = realToView(y) * _cellh + _cellh / 2; + + //fprintf(stderr, "%p: xx = %d yy = %d vw = %d vh = %d center = %d contentX() = %d", this, xx, yy, visibleWidth(), visibleHeight(), center, contentsX()); + + if (xx >= visibleWidth() || contentsX() > 0) + ensureVisible(xx, yy, margin + 2, center ? (visibleHeight() / 2) : _cellh); + else + ensureVisible(0, yy, margin + 2, center ? (visibleHeight() / 2) : _cellh); + + //fprintf(stderr, " -> %d\n", contentsX()); + + center = false; + _ensureCursorVisibleLater = false; +} + +void GEditor::startBlink() +{ + blinkTimer->start(QApplication::cursorFlashTime() / 2, false); + _cursor = true; + updateLine(y); +} + +void GEditor::stopBlink() +{ + blinkTimer->stop(); + _cursor = false; + updateLine(y); +} + +void GEditor::blinkTimerTimeout() +{ + if (!doc->isReadOnly()) + { + _cursor = !_cursor; + updateLine(y); + } +} + +void GEditor::focusInEvent(QFocusEvent *e) +{ + startBlink(); + //ensureCursorVisible(); + Q3ScrollView::focusInEvent(e); + doc->setCurrentView(this); +} + +void GEditor::focusOutEvent(QFocusEvent *e) +{ + stopBlink(); + //leaveCurrentLine(); + Q3ScrollView::focusOutEvent(e); +} + + +void GEditor::scrollTimerTimeout() +{ + int ny, nx; + QPoint pos = mapFromGlobal(QCursor::pos()); + + posToCursor(pos.x(), pos.y(), &ny, &nx); + cursorGoto(ny, nx, true); +} + +void GEditor::setStyle(int index, GHighlightStyle *style) +{ + int sat; + int gray; + + if (index < 0 || index >= GLine::NUM_STATE) + return; + + styles[index] = *style; + + if (index == GLine::Background) + { + viewport()->setPaletteBackgroundColor(style->color); + redrawContents(); + } + else + updateContents(); + + if (index == GLine::Background) + { + _oddBackground = style->color; + + sat = _oddBackground.saturation(); + gray = 128 + (_oddBackground.value() - 128) * 0.8; + + _altBackground = QColor(gray, gray, gray); + + if (_oddBackground.value() > 127) + _oddBackground.setHsv(_oddBackground.hue(), sat, _oddBackground.value() - 16); + else + _oddBackground.setHsv(_oddBackground.hue(), sat, _oddBackground.value() + 16); + } +} + +void GEditor::setNumRows(int n) +{ + _nrows = realToView(n - 1) + 1; + //Q3GridView::setNumRows(n); + updateViewport(); + updateContents(); + //Q3ScrollView::updateScrollBars(); + //if (contentsHeight() < visibleHeight()) + // repaintContents(contentsX(), contentsHeight(), visibleWidth(), visibleHeight() - contentsHeight() + contentsX(), true); + //if (contentsHeight() < visibleHeight()) + //repaintContents(true); +} + +void GEditor::getStyle(int index, GHighlightStyle *style) const +{ + if (index < 0 || index >= GLine::NUM_STATE) + index = GLine::Background; + + *style = styles[index]; +} + +void GEditor::setFlag(int f, bool v) +{ + if (v) + flags |= (1 << f); + else + flags &= ~(1 << f); + + if (getFlag(NoFolding)) + unfoldAll(); + + updateMargin(); + updateContents(); +} + +void GEditor::updateMargin() +{ + int charWidth = _charWidth['m']; + int nm = 1, lnl = 0; + + if (!getFlag(HideMargin)) + { + nm = 2; + + //if (doc->getHighlightMode() != GDocument::None) + { + int bw = 8; + + if (_breakpoint && !_breakpoint->isNull()) + bw = qMax(bw, _breakpoint->width() + 2); + + if (_bookmark && !_bookmark->isNull()) + bw = qMax(bw, _bookmark->width() + 2); + + nm += bw; + } + //else if (getFlag(ShowLineNumbers)) + // nm += 4; + + if (getFlag(ShowLineNumbers)) + { + int cnt = numLines() + _firstLineNumber; + + while (cnt) + { + nm += charWidth; + lnl++; + cnt /= 10; + } + + nm += 4; + } + + if (getFlag(ShowModifiedLines) && nm < 6) + nm = 6; + } + + if (nm != margin) + { + margin = nm; + lineNumberLength = lnl; + updateContents(); + updateCursor(); + } +} + +void GEditor::docTextChangedLater() +{ + emit textChanged(); +} + +void GEditor::docTextChanged() +{ + if (painting) + QTimer::singleShot(0, this, SLOT(docTextChangedLater())); + else + docTextChangedLater(); +} + +void GEditor::inputMethodEvent(QInputMethodEvent *e) +{ + //qDebug("inputMethodEvent: %s\n", (const char *)e->commitString().toUtf8()); + + if (doc->isReadOnly()) + { + e->ignore(); + return; + } + + insert(e->commitString()); + + #if 0 + int priorState = d->undoState; + d->removeSelectedText(); + + int c = d->cursor; // cursor position after insertion of commit string + if (e->replacementStart() <= 0) + c += e->commitString().length() + qMin(-e->replacementStart(), e->replacementLength()); + + d->cursor += e->replacementStart(); + + // insert commit string + if (e->replacementLength()) { + d->selstart = d->cursor; + d->selend = d->selstart + e->replacementLength(); + d->removeSelectedText(); + } + if (!e->commitString().isEmpty()) + d->insert(e->commitString()); + + d->cursor = c; + + d->textLayout.setPreeditArea(d->cursor, e->preeditString()); + d->preeditCursor = e->preeditString().length(); + d->hideCursor = false; + QList formats; + for (int i = 0; i < e->attributes().size(); ++i) { + const QInputMethodEvent::Attribute &a = e->attributes().at(i); + if (a.type == QInputMethodEvent::Cursor) { + d->preeditCursor = a.start; + d->hideCursor = !a.length; + } else if (a.type == QInputMethodEvent::TextFormat) { + QTextCharFormat f = qvariant_cast(a.value).toCharFormat(); + if (f.isValid()) { + QTextLayout::FormatRange o; + o.start = a.start + d->cursor; + o.length = a.length; + o.format = f; + formats.append(o); + } + } + } + d->textLayout.setAdditionalFormats(formats); + d->updateTextLayout(); + update(); + if (!e->commitString().isEmpty()) + d->emitCursorPositionChanged(); + d->finishChange(priorState); +#ifndef QT_NO_COMPLETER + if (!e->commitString().isEmpty()) + d->complete(Qt::Key_unknown); +#endif + #endif +} + +QVariant GEditor::inputMethodQuery(Qt::InputMethodQuery property) const +{ + //qDebug("inputMethodQuery: %d\n", (int)property); + GEditor *that = (GEditor *)this; + + switch(property) + { + case Qt::ImMicroFocus: + { + int cx, cy, px, py; + + getCursor(&cy, &cx); + that->cursorToPos(cy, cx, &px, &py); + return QRect(px, py, 1, _cellh); + } + case Qt::ImFont: + return font(); + case Qt::ImCursorPosition: + return QVariant(x); + case Qt::ImSurroundingText: + return QVariant(doc->getLine(y).getString()); + case Qt::ImCurrentSelection: + return QVariant(QString()); + case Qt::ImAnchorPosition: + return QVariant(x); + default: + return QVariant(); + } +} + + +bool GEditor::focusNextPrevChild(bool) +{ + return false; +} + +static bool is_power_of_ten(int n) +{ + for(;;) + { + if (n % 10) + return false; + n /= 10; + if (n == 1) + return true; + } +} + +void GEditor::lineInserted(int y) +{ + if (largestLine >= y) + largestLine++; + + if (getFlag(ShowLineNumbers) && is_power_of_ten(numLines())) + updateMargin(); +} + +void GEditor::lineRemoved(int y) +{ + if (largestLine == y) + updateWidth(y); + else if (largestLine > y) + largestLine--; + + if (getFlag(ShowLineNumbers) && is_power_of_ten(numLines() + 1)) + updateMargin(); +} + +void GEditor::flash() +{ + if (flashed) + return; + + flashed = true; + setPaletteBackgroundColor(QColor(QRgb(styles[GLine::Background].color.rgb() ^ 0xFFFFFF))); + redrawContents(); + QTimer::singleShot(50, this, SLOT(unflash())); +} + +void GEditor::unflash() +{ + flashed = false; + setPaletteBackgroundColor(styles[GLine::Background].color); + redrawContents(); +} + +#if 0 +static void dump_fold(GArray &fold) +{ + uint i; + GFoldedProc *fp; + + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + fprintf(stderr, "[%d %d] ", fp->start, fp->end); + } + + fprintf(stderr, "\n"); +} +#endif + +int GEditor::checkCursor(int y) +{ + uint i; + GFoldedProc *fp; + int ny; + + ny = y; + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + if (y > fp->start && y <= fp->end) + { + ny = fp->start; + break; + } + } + + return ny; +} + +void GEditor::foldLine(int row, bool no_refresh) +{ + uint i; + int pos; + int start, end; + GFoldedProc *fp; + int ny; + + if (getFlag(NoFolding)) + return; + + if (!doc->hasLimit(row)) + row = doc->getPreviousLimit(row); + + if (row < 0 || row >= numLines()) + return; + + //fprintf(stderr, "foldLine %d\n", row); + + start = row; + end = doc->getNextLimit(row); + if (end < 0) + end = numLines() - 1; + else + end--; + /*else + { + while (end > start) + { + end--; + l = doc->lines.at(end); + //qDebug("[%d] state = %d %d", end, l->highlight ? l->highlight[0].state : -1, l->highlight ? l->highlight[0].len : -1); + if (!l->highlight || (l->highlight[0].state != GLine::Comment && l->highlight[0].state != GLine::Help)) + break; + } + }*/ + + pos = -1; + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + if (end >= fp->start && start <= fp->end) + return; + if (pos < 0 && start < fp->start) + pos = i; + } + + fp = new GFoldedProc; + fp->start = start; + fp->end = end; + + if (pos < 0) + fold.append(fp); + else + fold.insert(pos, fp); + + //dump_fold(fold); + + ny = checkCursor(y); + if (ny != y) + cursorGoto(ny, x, false); + + if (!no_refresh) + { + setNumRows(numLines()); + //redrawContents(); + } +} + +void GEditor::unfoldLine(int row) +{ + uint i; + GFoldedProc *fp; + + //fprintf(stderr, "unfoldLine %d\n", row); + + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + if (row >= fp->start && row <= fp->end) + { + fold.remove(i); + //dump_fold(fold); + setNumRows(numLines()); + //redrawContents(); + return; + } + } + +} + +int GEditor::viewToReal(int row) const +{ + uint i; + GFoldedProc *fp; + + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + if (row <= fp->start) + break; + if (fp->end < (numLines() - 1)) + row += fp->end - fp->start; + else + row = numLines(); + } + + return row; +} + +int GEditor::realToView(int row) const +{ + uint i; + GFoldedProc *fp; + int y; + + //fprintf(stderr, "realToView: %d -> ", row); + + y = row; + + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + if (row < fp->start) + continue; + if (row <= fp->end) + y -= row - fp->start; + else + y -= fp->end - fp->start; + } + + //fprintf(stderr, "%d\n", y); + + return y; +} + +int GEditor::visibleLines() const +{ + int n; + uint i; + GFoldedProc *fp; + + n = numLines(); + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + n -= fp->end - fp->start; + } + + return n; +} + +bool GEditor::isFolded(int row) +{ + uint i; + GFoldedProc *fp; + int d, f; + + d = 0; + f = fold.count(); + + while (f > d) + { + i = (f + d) / 2; + fp = fold.at(i); + if (fp->start == row) + return true; + else if (fp->start < row) + d = i + 1; + else + f = i; + } + + return false; +} + +/*bool GEditor::insideFolded(int row) +{ + uint i; + GFoldedProc *fp; + + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + if (row > fp->start && row <= fp->end) + return true; + } + + return false; +}*/ + +int GEditor::checkFolded(int row) +{ + uint i; + GFoldedProc *fp; + + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + if (row <= fp->end) + { + if (row > fp->start) + return fp->start; + else + break; + } + } + + return row; +} + +void GEditor::foldAll() +{ + int row; + + if (getFlag(NoFolding)) + return; + + row = 0; + for(;;) + { + foldLine(row, true); + row = doc->getNextLimit(row); + if (row < 0) + break; + } + + setNumRows(numLines()); + //updateContents(); +} + +void GEditor::unfoldAll() +{ + foldClear(); + setNumRows(numLines()); + ensureCursorVisible(); + //redrawContents(); +} + + +void GEditor::foldRemove(int y1, int y2) +{ + uint i; + GFoldedProc *fp; + int n; + + if (getFlag(NoFolding)) + return; + + if (y2 < 0) + { + unfoldLine(y1); + return; + } + + n = y2 - y1 + 1; + + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + if (y2 < fp->start) + { + fp->start -= n; + fp->end -= n; + } + else if (y1 > fp->end) + continue; + else + { + fold.remove(i); + i--; + } + } +} + + +void GEditor::foldInsert(int y, int n) +{ + uint i; + GFoldedProc *fp; + + if (getFlag(NoFolding)) + return; + + if (n == 0) + { + unfoldLine(y); + return; + } + + for (i = 0; i < fold.count(); i++) + { + fp = fold.at(i); + if (fp->start > y) + { + fp->start += n; + fp->end += n; + } + else if (fp->end >= y) + { + fp->end += n; + fold.remove(i); //unfoldLine(fp->start); + i--; + } + } +} + +void GEditor::showString(GString s, bool ignoreCase) +{ + _showString = s; + _showStringIgnoreCase = ignoreCase; + updateContents(); +} + +void GEditor::showWord(int y, int x, int len) +{ + _showRow = y; + _showCol = x; + _showLen = len; + updateLine(y); +} + +void GEditor::updateLine(int y) +{ + QRect r(0, realToView(y) * _cellh, _cellw, _cellh); + updateContents(r); +} + +#if 0 +void GEditor::paintEmptyArea(QPainter *p, int cx, int cy, int cw, int ch) +{ + if ((_nrows * _cellh) >= contentsHeight()) + return; + + contentsToViewport(cx, cy, cx, cy); + QRegion reg(QRect(cx, cy, cw, ch)); + QSize size(_cellw, _nrows * _cellh); + reg = reg.subtracted(QRect(contentsToViewport(QPoint(0, 0)), size)); + + // And draw the rectangles (transformed as needed) + QVector r = reg.rects(); + for (int i = 0; i < (int)r.count(); ++i) + p->fillRect(r[i], viewport()->paletteBackgroundColor()); +} + +void GEditor::viewportPaintEvent(QPaintEvent *e) +{ + QString info; + QRect rect; + QColor color; + + Q3ScrollView::viewportPaintEvent(e); + + if (getFlag(ShowCursorPosition)) + { + getInfo(&rect, &info); + if (rect.intersects(e->rect())) + { + QPainter p(viewport()); + color = styles[GLine::Current].color; + color.setAlpha(128); + p.fillRect(rect, color); + //rect.translate(0, 2); + p.setPen(styles[GLine::Normal].color); + p.drawText(rect, Qt::AlignCenter, info); + /*color = styles[GLine::Normal].color; + color.setAlpha(128); + p.setPen(color); + //rect.translate(0, -2); + p.drawRect(rect);*/ + } + } +} +#endif + +void GEditor::updateViewport() +{ + int vw, vh; + + /*vw = contentsRect().width(); + vh = contentsRect().height(); + + if (doc) + { + vw = QMAX(_cellw, vw); + vh = QMAX(_cellh * _nrows, vh); + //qDebug("updateViewport: h = %d vh = %d", _cellh * numLines(), contentsRect().height()); + }*/ + + vw = QMAX(visibleWidth(), _cellw); + vh = QMAX(visibleHeight(), _cellh * _nrows); + + if (vw != contentsWidth() || vh != contentsHeight()) + Q3ScrollView::resizeContents(vw, vh); + + //updateCache(); + _checkCache = true; +} + +void GEditor::resizeContents(int w, int h) +{ + updateViewport(); +} + +void GEditor::setInsertMode(bool mode) +{ + int x1, y1, x2, y2, i; + + if (mode !=_insertMode) + { + _insertMode = mode; + if (!_insertMode) + x = QMIN(x, lineLength(y)); + + if (doc->hasSelection()) + { + doc->getSelection(&y1, &x1, &y2, &x2, _insertMode); + x = x2; y = y2; + + for (i = y1; i <= y2; i++) + updateLine(i); + } + else + updateLine(y); + } +} + +void GEditor::updateViewportAttributes() +{ + bool b; + + //if (!::strcmp(style()->metaObject()->className(), "Oxygen::Style")) + b = !hasBorder(); + //else + // b = true; + + viewport()->setAttribute(Qt::WA_NoSystemBackground, b); + viewport()->setAttribute(Qt::WA_PaintOnScreen, b); +} + +void GEditor::setBorder(bool b) +{ + if (_border == b) + return; + + style()->unpolish(this); + setFrameStyle(b ? StyledPanel + Sunken : NoFrame); + style()->polish(this); + updateViewportAttributes(); +} + +void GEditor::setLineOffset(int l) +{ + _firstLineNumber = l; + update(); +} + +void GEditor::expand(bool shift) +{ + bool e; + + e = isFolded(y); + + if (shift) + { + if (e) + unfoldAll(); + else + foldAll(); + } + else + { + if (e) + unfoldLine(y); + else + foldLine(y); + } +} + +void GEditor::saveMouseCursor() +{ + _saveCursor = viewport()->cursor(); +} + +void GEditor::saveCursor() +{ + _save_x = x; + _save_y = y; +} + +void GEditor::restoreCursor() +{ + cursorGoto(_save_x, _save_y, false); +} + +bool GEditor::cursorRelGoto(int dy, int dx, bool mark) +{ + return cursorGoto(QMAX(0, y + dy), QMAX(0, x + dx), mark); +} diff --git a/gb.qt4/src/ext/gview.h b/gb.qt4/src/ext/gview.h new file mode 100644 index 00000000..c48c22f6 --- /dev/null +++ b/gb.qt4/src/ext/gview.h @@ -0,0 +1,317 @@ +/*************************************************************************** + + gview.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GVIEW_H +#define __GVIEW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gdocument.h" +#include "../gb.qt.h" + +struct GHighlightStyle +{ + QColor color; + QColor backgroundColor; + bool bold; + bool italic; + bool underline; + bool background; +}; + +struct GFoldedProc +{ + int start; + int end; +}; + +class GEditor; + +class GEditor : public Q3ScrollView +{ + Q_OBJECT + + friend class GDocument; + +private: + + static QPixmap *_cache; + static QPixmap *_breakpoint; + static QPixmap *_bookmark; + static QStyle *_style; + static int count; + + GDocument *doc; + QFontMetrics fm; + int _ytext; + int largestLine; + int x, y, xx; + int nx, ny; + bool _cursor; + QTimer *blinkTimer; + QTimer *scrollTimer; + int x1m, x2m, y1m, y2m; + int margin; + int lineNumberLength; + bool center; + bool flashed; + bool painting; + GString _showString; + bool _showStringIgnoreCase; + int _showRow, _showCol, _showLen; + bool _posOutside; + int _cellw, _cellh; + int _nrows; + bool _insertMode; + double *_charWidth; + double _sameWidth; + int _tabWidth; + bool _oddLine; + QColor _altBackground; + QColor _oddBackground; + bool _checkCache; + bool _border; + bool _ensureCursorVisibleLater; + int _firstLineNumber; + QCursor _saveCursor; + GString _cutBuffer; + int _save_x, _save_y; + + int lastx; + bool left; + bool _dblclick; + GArray fold; + + QFont normalFont; + QFont italicFont; + GHighlightStyle styles[GLine::NUM_STATE]; + int flags; + + int lineLength(int y) const { return doc->lineLength(y); } + int numLines() const { return doc->numLines(); } + int visibleLines() const; + void startBlink(); + void stopBlink(); + bool updateCursor(); + + int lineWidth(int y) const; + int lineWidth(int y, int len); + void updateWidth(int y = -1); + void updateMargin(); + void updateHeight(); + void updateCache(); + + void lineInserted(int y); + void lineRemoved(int y); + int findLargestLine(); + + void drawTextWithTab(QPainter &p, int sx, int x, int y, const QString &s); + void paintText(QPainter &p, GLine *l, int x, int y, int xmin, int lmax, int h, int xs1, int xs2, int row, QColor &); + void paintShowString(QPainter &p, GLine *l, int x, int y, int xmin, int lmax, int h, int row); + void paintDottedSpaces(QPainter &p, int row, int ps, int ls); + //void paintEmptyArea(QPainter *p, int cx, int cy, int cw, int ch); + + void docTextChanged(); + void redrawContents(); + + int viewToReal(int row) const; + int realToView(int row) const; + int checkCursor(int y); + bool isCursorVisible(); + + void updateViewport(); + void updateFont(); + + int getStringWidth(const QString &s, int len, bool unicode) const; + + void updateViewportAttributes(); + +private slots: + + void blinkTimerTimeout(); + void scrollTimerTimeout(); + //void baptizeVisible(); + //void baptizeVisible(int x, int y); + void unflash(); + void docTextChangedLater(); + void ensureCursorVisible(); + +protected: + + virtual void paintCell(QPainter &, int row, int); + virtual void changeEvent(QEvent *e); + virtual void keyPressEvent(QKeyEvent *e); + virtual void mousePressEvent(QMouseEvent *e); + virtual void mouseReleaseEvent(QMouseEvent *e); + virtual void mouseMoveEvent(QMouseEvent *e); + virtual void mouseDoubleClickEvent(QMouseEvent *e); + virtual void resizeEvent(QResizeEvent *e); + virtual void focusInEvent(QFocusEvent *); + virtual void focusOutEvent(QFocusEvent *); + virtual bool focusNextPrevChild(bool); + virtual void inputMethodEvent(QInputMethodEvent *e); + virtual void drawContents(QPainter *p, int cx, int cy, int cw, int ch); + virtual void viewportResizeEvent(QResizeEvent *e); + +public: + + enum Flag + { + ShowProcedureLimits = 1, + DrawWithRelief = 2, + ShowModifiedLines = 3, + ShowCurrentLine = 4, + ShowLineNumbers = 5, + HighlightBraces = 6, + HighlightImmediately = 7, + BlendedProcedureLimits = 8, + ShowDots = 9, + ShowCursorPosition = 10, + ChangeBackgroundAtLimit = 11, + HideMargin = 12, + BlinkCursor = 13, + NoFolding = 14, + AlwaysShowCursor = 15 + }; + + static void setBreakpointPixmap(QPixmap *p); + static void setBookmarkPixmap(QPixmap *p); + + GEditor(QWidget *parent); + ~GEditor(); + void reset(); + + virtual QVariant inputMethodQuery(Qt::InputMethodQuery property) const; + + void setDocument(GDocument *doc); + GDocument *getDocument() const { return doc; } + + void getCursor(int *yc, int *xc) const { *yc = y; *xc = x; } + int getLine() const { return y; } + int getColumn() const { return x; } + void insert(QString text); + bool cursorGoto(int ny, int nx, bool mark); + bool cursorRelGoto(int dy, int dx, bool mark); + void cursorCenter() { center = true; } + void cursorLeft(bool shift, bool ctrl); + void cursorRight(bool shift, bool ctrl); + void cursorUp(bool shift, bool ctrl, bool alt); + void cursorDown(bool shift, bool ctrl, bool alt); + void cursorPageUp(bool shift, bool alt); + void cursorPageDown(bool shift, bool alt); + void cursorHome(bool shift, bool ctrl, bool alt); + void cursorEnd(bool shift, bool ctrl, bool alt); + void newLine(); + void backspace(bool ctrl); + void del(bool ctrl); + void copy(bool mouse); + void copy() { copy(false); } + void cut(); + void paste(bool mouse); + void paste() { paste(false); } + void undo(); + void redo(); + void tab(bool back); + void selectAll(); + void movePreviousSameIndent(bool shift); + void moveNextSameIndent(bool shift); + void expand(bool shift); + void selectCurrentLine(); + void deleteCurrentLine(); + + void clearLine(bool before, bool after); + void clearAfter(int nchar); + void clearDocument(bool before, bool after); + void saveCursor(); + void restoreCursor(); + + bool getInsertMode() const { return _insertMode; } + void setInsertMode(bool mode); + + void setStyle(int index, GHighlightStyle *style); + void getStyle(int index, GHighlightStyle *style) const; + bool getFlag(int f) const { return flags & (1 << f); } + void setFlag(int f, bool v); + bool hasBorder() const { return _border; } + void setBorder(bool b); + void setLineOffset(int l); + + int rowAt(int y) const { return y / _cellh; } + int getLineHeight() const { return _cellh; } + int getCharWidth(unsigned char c) const { return _charWidth[c]; } + void cursorToPos(int y, int x, int *px, int *py); + bool isPosOutside() const { return _posOutside; } + int posToLine(int py); + int posToColumn(int y, int px); + bool posToCursor(int px, int py, int *y, int *x); + int lastVisibleRow(int y) const { return rowAt(y + visibleHeight() - 1); } + int lastVisibleRow() const { return lastVisibleRow(contentsY()); } + void updateLine(int y); + void setNumRows(int); + void leaveCurrentLine(); + int lineOffset() const { return _firstLineNumber; } + + virtual void resizeContents(int w, int h); + + void checkMatching(); + void flash(); + void showString(GString s, bool ignoreCase); + void showWord(int y, int x, int len); + + void foldClear() { fold.clear(); } + void foldLine(int row, bool no_refresh = false); + void foldAll(); + void unfoldAll(); + void unfoldLine(int row); + bool isFolded(int row); + //bool insideFolded(int row); + int checkFolded(int row); + void foldRemove(int y1, int y2 = -1); + void foldInsert(int y, int n); + + bool hasSelection() { return doc->hasSelection(); } + void getSelection(int *y1, int *x1, int *y2, int *x2) { return doc->getSelection(y1, x1, y2, x2, _insertMode); } + GString getSelectedText() { return doc->getSelectedText(_insertMode); } + void hideSelection() { doc->hideSelection(); } + + void saveMouseCursor(); + +signals: + + void cursorMoved(); + void textChanged(); + void marginClicked(int); + void marginDoubleClicked(int); +}; + +#endif diff --git a/gb.qt4/src/ext/main.cpp b/gb.qt4/src/ext/main.cpp new file mode 100644 index 00000000..c4b31b5f --- /dev/null +++ b/gb.qt4/src/ext/main.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** + + main.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include + +/* +#include +#include +#include +*/ + +#include "main.h" + +#include "CLCDNumber.h" +#include "CDial.h" +#include "CEditor.h" +#include "CTextEdit.h" + +extern "C" { + +GB_INTERFACE GB EXPORT; +QT_INTERFACE QT; +EVAL_INTERFACE EVAL; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CLCDNumberDesc, + + CDialDesc, + + CHighlightDesc, + CEditorLineDesc, + CEditorLinesDesc, + CEditorSelectionDesc, + CEditorStyleDesc, + CEditorStylesDesc, + CEditorFlagsDesc, + CEditorDesc, + + CTextEditSelectionDesc, + CTextEditFormatDesc, + CTextEditDesc, + + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.qt4", QT_INTERFACE_VERSION, &QT); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +} + +bool MAIN_load_eval_component(void) +{ + if (GB.Component.Load("gb.eval")) + return true; + + GB.GetInterface("gb.eval", EVAL_INTERFACE_VERSION, &EVAL); + return false; +} + diff --git a/gb.qt4/src/ext/main.h b/gb.qt4/src/ext/main.h new file mode 100644 index 00000000..a3586687 --- /dev/null +++ b/gb.qt4/src/ext/main.h @@ -0,0 +1,41 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "../gb.qt.h" +#include "gb.draw.h" +#include "gb.eval.h" +#include "gb.form.properties.h" + +#ifndef __MAIN_C +extern "C" GB_INTERFACE GB; +extern "C" QT_INTERFACE QT; +extern "C" EVAL_INTERFACE EVAL; +#endif + +bool MAIN_load_eval_component(void); + +#endif diff --git a/gb.qt4/src/fix_breeze.cpp b/gb.qt4/src/fix_breeze.cpp new file mode 100644 index 00000000..3839b6af --- /dev/null +++ b/gb.qt4/src/fix_breeze.cpp @@ -0,0 +1,263 @@ +/*************************************************************************** + + fix_breeze.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __FIX_BREEZE_CPP + +#include +#include +#include +#include + +#include "gb_common.h" +#include "CStyle.h" +#include "fix_breeze.h" + +QFontMetrics *FixBreezeStyle::fm = NULL; + +void FixBreezeStyle::fixFontMetrics(QStyleOption *option) +{ + if (!fm) + { + QFont f = qApp->font(); + f.setPointSize(1); + fm = new QFontMetrics(f); + } + + option->fontMetrics = *fm; +} + +QRect FixBreezeStyle::subControlRect(ComplexControl element, const QStyleOptionComplex* option, SubControl subControl, const QWidget* widget) const +{ + if (element == CC_SpinBox) + { + const QStyleOptionSpinBox *spinBoxOption(qstyleoption_cast(option)); + const bool flat( !spinBoxOption->frame ); + QRect rect(option->rect); + + if (subControl == SC_SpinBoxEditField) + { + QRect labelRect; + + labelRect = QRect( + rect.left(), rect.top(), + rect.width() - 20, //Metrics::SpinBox_ArrowButtonWidth, + rect.height() ); + + // remove right side line editor margins + const int frameWidth( pixelMetric( PM_SpinBoxFrameWidth, option, widget ) ); + if (!flat) + { + if (CSTYLE_fix_breeze) + labelRect.adjust(frameWidth, 2, 0, -2 ); + else if (CSTYLE_fix_oxygen) + labelRect.adjust(frameWidth, 4, 0, -4 ); + } + + return visualRect( option, labelRect ); + } + else if (subControl == SC_SpinBoxUp || subControl == SC_SpinBoxDown) + { + // take out frame width + if (!flat) + rect.adjust(2, 2, -2, -2); // = insideMargin( rect, 2); //Metrics::Frame_FrameWidth ); + + QRect arrowRect; + arrowRect = QRect( + rect.right() - 20 /*Metrics::SpinBox_ArrowButtonWidth*/ + 1, + rect.top(), + 20, //Metrics::SpinBox_ArrowButtonWidth, + rect.height() ); + + const int arrowHeight( qMin( rect.height(), 20)); //int(Metrics::SpinBox_ArrowButtonWidth) ) ); + arrowRect = centerRect( arrowRect, 20 /*Metrics::SpinBox_ArrowButtonWidth*/, arrowHeight ); + arrowRect.setHeight( arrowHeight/2 ); + if( subControl == SC_SpinBoxDown ) arrowRect.translate( 0, arrowHeight/2 ); + + return visualRect( option, arrowRect ); + } + } + else if (element == CC_ComboBox) + { + if (subControl == SC_ComboBoxEditField) + { + const QStyleOptionComboBox *comboBoxOption( qstyleoption_cast(option)); + const bool editable( comboBoxOption->editable ); + const bool flat( editable && !comboBoxOption->frame ); + QRect rect(option->rect); + QRect labelRect; + + const int frameWidth(pixelMetric(PM_ComboBoxFrameWidth, option, widget)); + labelRect = QRect( + rect.left(), rect.top(), + rect.width() - 20, //Metrics::MenuButton_IndicatorWidth, + rect.height() ); + + // remove margins + if (!flat) + { + if (CSTYLE_fix_breeze) + labelRect.adjust(frameWidth, 2, 0, -2 ); + else if (CSTYLE_fix_oxygen) + labelRect.adjust(frameWidth, 4, 0, -4 ); + } + + return visualRect( option, labelRect ); + } + } + else if (element == CC_Slider) + { + const QStyleOptionSlider *sliderOption( qstyleoption_cast( option ) ); + const bool horizontal( sliderOption->orientation == Qt::Horizontal ); + + QRect result(QProxyStyle::subControlRect(element, option, subControl, widget)); + + if (horizontal) + result.moveTop((widget->height() - result.height()) / 2); + else + result.moveLeft((widget->width() - result.width()) / 2); + + return result; + } + + return QProxyStyle::subControlRect(element, option, subControl, widget); +} + +QRect FixBreezeStyle::subElementRect(SubElement element, const QStyleOption* option, const QWidget* widget) const +{ + if (element == SE_LineEditContents) + { + const QStyleOptionFrame* frameOption(qstyleoption_cast(option)); + + const bool flat( frameOption->lineWidth == 0 ); + if(flat) + return option->rect; + + QRect rect( option->rect ); + + const int frameWidth(pixelMetric(PM_DefaultFrameWidth, option, widget)); + rect.adjust(frameWidth, 2, -frameWidth, -2); + + return rect; + } + + return QProxyStyle::subElementRect(element, option, widget); +} + +void FixBreezeStyle::drawPrimitive( PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget ) const +{ + QStyleOption newOption; + + if (element == PE_FrameLineEdit) + { + newOption = *option; + fixFontMetrics(&newOption); + option = &newOption; + //qDebug("PE_FrameLineEdit: %d / %d", option->fontMetrics.height(), option->rect.height()); + } + + QProxyStyle::drawPrimitive(element, option, painter, widget); +} + +void FixBreezeStyle::drawComplexControl(ComplexControl element, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget) const +{ + if (element == CC_SpinBox) + { + QStyleOptionSpinBox newOption; + const QStyleOptionSpinBox *spinBoxOption( qstyleoption_cast( option ) ); + + if (option->subControls & SC_SpinBoxFrame) + { + if (spinBoxOption->frame) + { + if( option->subControls & SC_SpinBoxFrame ) + { + newOption = *spinBoxOption; + newOption.subControls &= ~SC_SpinBoxFrame; + option = &newOption; + + drawPrimitive( PE_FrameLineEdit, option, painter, widget ); + } + } + } + } + else if (element == CC_ComboBox) + { + QStyleOptionComboBox newOption; + const QStyleOptionComboBox* comboBoxOption( qstyleoption_cast( option ) ); + + if (option->subControls & SC_ComboBoxFrame) + { + if (comboBoxOption->editable) + { + if (comboBoxOption->frame) + { + newOption = *comboBoxOption; + newOption.subControls &= ~SC_ComboBoxFrame; + option = &newOption; + + drawPrimitive(PE_FrameLineEdit, option, painter, widget ); + } + } + } + } + else if (element == CC_Slider) + { + //QStyleOptionSlider newOption; + const QStyleOptionSlider *sliderOption( qstyleoption_cast( option ) ); + const bool horizontal( sliderOption->orientation == Qt::Horizontal ); + + if (!(sliderOption->subControls & SC_SliderTickmarks)) + { + QRect handle(QProxyStyle::subControlRect(element, option, SC_SliderHandle, widget)); + //newOption = *sliderOption; + //option = &newOption; + + painter->save(); + if (horizontal) + painter->translate(0, (widget->height() - handle.height()) / 2); + else + painter->translate((option->rect.width() - handle.width()) / 2, 0); + //newOption.rect = QRect(newOption.rect.x(), (newOption.rect.height() - handle.height()) / 2, newOption.rect.width(), handle.height()); + + QProxyStyle::drawComplexControl(element, option, painter, widget); + painter->restore(); + return; + } + } + + QProxyStyle::drawComplexControl(element, option, painter, widget); +} + +void FixBreezeStyle::drawControl(ControlElement element, const QStyleOption * option, QPainter * painter, const QWidget * widget) const +{ + QStyleOptionButton newOption; + + if (element == CE_PushButtonBevel) + { + newOption = *(QStyleOptionButton *)option; + newOption.iconSize = QSize(0, 0); + option = &newOption; + } + + QProxyStyle::drawControl(element, option, painter, widget); +} diff --git a/gb.qt4/src/fix_breeze.h b/gb.qt4/src/fix_breeze.h new file mode 100644 index 00000000..07666445 --- /dev/null +++ b/gb.qt4/src/fix_breeze.h @@ -0,0 +1,55 @@ +/*************************************************************************** + + fix_breeze.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __FIX_BREEZE_H +#define __FIX_BREEZE_H + +#include +#include + +class FixBreezeStyle : public QProxyStyle +{ +public: + + virtual QRect subControlRect(ComplexControl, const QStyleOptionComplex*, SubControl, const QWidget*) const; + virtual QRect subElementRect(SubElement, const QStyleOption*, const QWidget*) const; + + void drawComplexControl(ComplexControl, const QStyleOptionComplex*, QPainter*, const QWidget*) const; + void drawPrimitive(PrimitiveElement, const QStyleOption*, QPainter*, const QWidget*) const; + void drawControl(ControlElement, const QStyleOption *, QPainter *, const QWidget *) const; + + QRect visualRect(const QStyleOption* opt, const QRect& subRect) const + { return QProxyStyle::visualRect(opt->direction, opt->rect, subRect); } + + QRect centerRect(const QRect &rect, int width, int height) const + { return QRect(rect.left() + (rect.width() - width)/2, rect.top() + (rect.height() - height)/2, width, height); } + +private: + static QFontMetrics *fm; + static void fixFontMetrics(QStyleOption *); +}; + + +#endif + + diff --git a/gb.qt4/src/gb.qt.h b/gb.qt4/src/gb.qt.h new file mode 100644 index 00000000..74cf860a --- /dev/null +++ b/gb.qt4/src/gb.qt.h @@ -0,0 +1,172 @@ +/*************************************************************************** + + gb.qt.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_QT_H +#define __GB_QT_H + +#include "gambas.h" +#include "gb.image.h" + +#ifdef OS_MACOSX +//#define NO_X_WINDOW 1 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define QT_INTERFACE_VERSION 1 + +#if QT_VERSION >= 0x050000 +#define QT5 1 +#define QT_NAME "gb.qt5" +#else +#define QT_NAME "gb.qt4" +#endif + +#define TO_QSTRING(_str) (QString::fromUtf8((const char *)(_str))) + +#ifdef DO_NOT_USE_QT_INTERFACE + + #define TO_UTF8(_str) QT_ToUtf8(_str) + #define LAST_UTF8_LENGTH() QT_GetLastUtf8Length() + #define NEW_STRING(_str) QT_NewString(_str) + #define RETURN_NEW_STRING(_str) QT_ReturnNewString(_str) + #define GET_SENDER() void *_object = QT_GetObject((QWidget*)sender()) + +#else + + #define TO_UTF8(_str) QT.ToUtf8(_str) + #define LAST_UTF8_LENGTH() QT.GetLastUtf8Length() + #define NEW_STRING(_str) QT.NewString(_str) + #define RETURN_NEW_STRING(_str) QT.ReturnNewString(_str) + #define GET_SENDER() void *_object = QT.GetObject((QWidget*)sender()) + +#endif + +#define QSTRING_ARG(_arg) (QString::fromUtf8((const char *)(VARG(_arg).addr + VARG(_arg).start), VARG(_arg).len)) +#define QSTRING_PROP() (QString::fromUtf8((const char *)(VPROP(GB_STRING).addr + VPROP(GB_STRING).start), VPROP(GB_STRING).len)) + +#define TO_QCOLOR(_col) QColor::fromRgba((QRgb)((_col) ^ 0xFF000000)) + +typedef + struct { + GB_BASE ob; + QWidget *widget; + void *_r0; + unsigned short flag; + unsigned short _r1; + int _r2; + void *_r3; + void *_r4; + } + QT_WIDGET; + +typedef + struct { + QT_WIDGET widget; + QWidget *container; + int arrangement; + } + QT_CONTAINER; + +typedef + struct { + GB_BASE ob; + QFont *font; + void *func; + void *object; + } + QT_FONT; + +typedef + void *QT_PICTURE; + +typedef + struct { + QPainter *p; + QPainter *pm; + QBitmap *mask; + int fg; + int fillColor; + } + QT_DRAW_EXTRA; + +typedef + void (*QT_FONT_FUNC)(QFont &, void *); + +typedef + void (*QT_COLOR_FUNC)(void *); + +typedef + struct { + intptr_t version; + void (*InitEventLoop)(void); + void (*Init)(void); + void (*InitWidget)(QWidget *, void *, int); + void (*SetWheelFlag)(void *); + void *(*GetObject)(QWidget *); + QWidget *(*GetContainer)(void *); + void (*BorderProperty)(void *, void *); + void (*FullBorderProperty)(void *, void *); + void (*ScrollBarProperty)(void *, void *); + void (*FontProperty)(void *, void *); + QT_FONT *(*CreateFont)(const QFont &, QT_FONT_FUNC, void *); + void (*SetFont)(QT_FONT_FUNC, void*, void *); + QT_PICTURE (*CreatePicture)(const QPixmap &); + //QMimeSourceFactory *(*MimeSourceFactory)(void); + QPixmap *(*GetPixmap)(QT_PICTURE); + const char *(*ToUtf8)(const QString &); + int (*GetLastUtf8Length)(); + char *(*NewString)(const QString &); + void (*ReturnNewString)(const QString &); + bool (*EventFilter)(QEvent *); + bool (*Notify)(void *, bool); + int (*Alignment)(int, int, bool); + void (*Link)(QObject *, void *); + void *(*GetLink)(QObject *); + QPainter *(*GetCurrentPainter)(); + uint (*GetBackgroundColor)(void *); + void (*MouseProperty)(void *, void *); + QT_COLOR_FUNC (*AfterSetColor)(QT_COLOR_FUNC); + void *_null; + } + QT_INTERFACE; + + +#define QT_WIDGET_PROPERTIES "*" + +#define QT_EVENT_FIRST ((QEvent::Type)(QEvent::User + 10)) + +#endif + diff --git a/gb.qt4/src/gb.qt4.component b/gb.qt4/src/gb.qt4.component new file mode 100644 index 00000000..a07eb600 --- /dev/null +++ b/gb.qt4/src/gb.qt4.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.qt4 +Author=Benoît Minisini +Implements=Form,EventLoop,ImageIO +Requires=gb.image +Type=Form diff --git a/gb.qt4/src/main.cpp b/gb.qt4/src/main.cpp new file mode 100644 index 00000000..99241e3e --- /dev/null +++ b/gb.qt4/src/main.cpp @@ -0,0 +1,1556 @@ +/*************************************************************************** + + main.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include +#include +#include +#include +#include +#include + +#include "gambas.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gb.image.h" +#include "gb.qt.h" +#include "gb.form.font.h" + +#include "CFont.h" +#include "CScreen.h" +#include "CStyle.h" +#include "CWidget.h" +#include "CWindow.h" +#include "CButton.h" +#include "CContainer.h" +#include "CLabel.h" +#include "CTextBox.h" +#include "CTextArea.h" +#include "CMenu.h" +#include "CPanel.h" +#include "CMouse.h" +#include "CKey.h" +#include "CColor.h" +#include "CConst.h" +#include "CCheckBox.h" +#include "CFrame.h" +#include "CRadioButton.h" +#include "CTabStrip.h" +#include "CDialog.h" +#include "CPicture.h" +#include "CImage.h" +#include "CClipboard.h" +#include "CDraw.h" +#include "CWatch.h" +#include "CDrawingArea.h" +#include "CMessage.h" +#include "CSlider.h" +#include "CScrollBar.h" +#include "CMovieBox.h" +#include "CWatcher.h" +#include "cprinter.h" +#include "csvgimage.h" +#include "cpaint_impl.h" +#include "ctrayicon.h" + +#include +#ifndef QT5 +#include "CEmbedder.h" +#endif + +#include "desktop.h" +#include "x11.h" + +#ifdef QT5 +#include +#include +#include +#endif + +#include "fix_breeze.h" +#include "main.h" + +/*#define DEBUG*/ + +extern "C" { +const GB_INTERFACE *GB_PTR EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; +GEOM_INTERFACE GEOM EXPORT; +} + +int MAIN_in_wait = 0; +int MAIN_in_message_box = 0; +int MAIN_loop_level = 0; +int MAIN_scale = 6; +#ifndef NO_X_WINDOW +int MAIN_x11_last_key_code = 0; +#endif +bool MAIN_debug_busy = false; +bool MAIN_init = false; +bool MAIN_key_debug = false; + +GB_CLASS CLASS_Control; +GB_CLASS CLASS_Container; +GB_CLASS CLASS_ContainerChildren; +GB_CLASS CLASS_UserControl; +GB_CLASS CLASS_UserContainer; +GB_CLASS CLASS_TabStrip; +GB_CLASS CLASS_Window; +GB_CLASS CLASS_Menu; +GB_CLASS CLASS_Picture; +GB_CLASS CLASS_Drawing; +GB_CLASS CLASS_DrawingArea; +GB_CLASS CLASS_Printer; +GB_CLASS CLASS_Image; +GB_CLASS CLASS_SvgImage; +GB_CLASS CLASS_TextArea; + +static bool in_event_loop = false; +static int _no_destroy = 0; +static QTranslator *_translator = NULL; +static bool _application_keypress = false; +static GB_FUNCTION _application_keypress_func; +static bool _check_quit_posted = false; +static int _prevent_quit = 0; + +#ifndef NO_X_WINDOW +static void (*_x11_event_filter)(XEvent *) = 0; +#endif + +static QHash _link_map; + +static QPointer MAIN_mouseGrabber = 0; +static QPointer MAIN_keyboardGrabber = 0; + +static QByteArray _utf8_buffer[UTF8_NBUF]; +static int _utf8_count = 0; +static int _utf8_length = 0; + +static void QT_Init(void); + +#ifdef QT5 + +static QtMessageHandler _previousMessageHandler; + +static void myMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg ) +{ + //fprintf(stderr, "---- `%s'\n", QT_ToUtf8(msg)); + + if (msg == "QXcbClipboard: SelectionRequest too old") + return; + + _previousMessageHandler(type, context, msg); +} +#endif + +//static MyApplication *myApp; + +/*************************************************************************** + + MyMimeSourceFactory + + Create a QMimeSourceFactory to handle files stored in an archive + +***************************************************************************/ + +#if 0 +class MyMimeSourceFactory: public Q3MimeSourceFactory +{ +public: + + MyMimeSourceFactory(); + + virtual const QMimeSource* data(const QString& abs_name) const; + +private: + + QMap extensions; +}; + + +MyMimeSourceFactory::MyMimeSourceFactory() +{ + extensions.replace("htm", "text/html;charset=UTF-8"); + extensions.replace("html", "text/html;charset=UTF-8"); + extensions.replace("txt", "text/plain"); + extensions.replace("xml", "text/xml;charset=UTF-8"); + extensions.replace("jpg", "image/jpeg"); + extensions.replace("png", "image/png"); + extensions.replace("gif", "image/gif"); +} + + +const QMimeSource* MyMimeSourceFactory::data(const QString& abs_name) const +{ + char *addr; + int len; + Q3StoredDrag* sr = 0; + char *path; + + //qDebug("MyMimeSourceFactory::data: %s", (char *)abs_name.latin1()); + + path = (char *)abs_name.latin1(); + + if (true) //abs_name[0] != '/') + { + if (GB.LoadFile(path, 0, &addr, &len)) + GB.Error(NULL); + else + { + QByteArray ba; + ba.setRawData((const char *)addr, len); + + QFileInfo fi(abs_name); + QString e = fi.extension(FALSE); + Q3CString mimetype = "text/html"; //"application/octet-stream"; + + const char* imgfmt; + + if ( extensions.contains(e) ) + mimetype = extensions[e].latin1(); + else + { + QBuffer buffer(&ba); + + buffer.open(QIODevice::ReadOnly); + if (( imgfmt = QImageReader::imageFormat( &buffer ) ) ) + mimetype = Q3CString("image/")+Q3CString(imgfmt).lower(); + buffer.close(); + } + + sr = new Q3StoredDrag( mimetype ); + sr->setEncodedData( ba ); + + ba.resetRawData((const char*)addr, len); + + //qDebug("MimeSource: %s %s", abs_name.latin1(), (const char *)mimetype); + + GB.ReleaseFile(addr, len); + } + } + + return sr; +} + +static MyMimeSourceFactory myMimeSourceFactory; +#endif + +#if 0 +/*************************************************************************** + + MyAbstractEventDispatcher + + Manage window deletion + +***************************************************************************/ + +class MyAbstractEventDispatcher : public QAbstractEventDispatcher +{ +public: + MyAbstractEventDispatcher(); + virtual bool processEvents(QEventLoop::ProcessEventsFlags flags); +}; + +MyAbstractEventDispatcher::MyAbstractEventDispatcher() +: QAbstractEventDispatcher() +{ +} + +bool MyAbstractEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + bool ret; + CWIDGET **ptr; + CWIDGET *ob; + + MAIN_loop_level++; + ret = QAbstractEventDispatcher::processEvents(flags); + MAIN_loop_level--; + + for(;;) + { + ptr = &CWIDGET_destroy_list; + + for(;;) + { + ob = *ptr; + if (!ob) + return ret; + + //if (MAIN_loop_level <= ob->level && !ob->flag.notified) + if (!ob->flag.notified) + { + //qDebug("delete: %s %p", GB.GetClassName(ob), ob); + //qDebug(">> delete %p (%p) :%p:%ld", ob, ob->widget, ob->ob.klass, ob->ob.ref); + //*ptr = ob->next; + delete ob->widget; + break; + //GB.Unref(POINTER(&ob)); + //qDebug(" delete %p (%p) :%p:%ld #2", ob, ob->widget, ob->ob.klass, ob->ob.ref); + //qDebug("<< delete %p (%p)", ob, ob->widget); + } + else + { + //qDebug("cannot delete: %s %p", GB.GetClassName(ob), ob); + ptr = &ob->next; + } + } + } + //return ret; +} +#endif + +void MAIN_process_events(void) +{ + _no_destroy++; + qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 0); + _no_destroy--; +} + +void MAIN_init_error() +{ + GB.Error("GUI is not initialized"); +} + +/** MyApplication **********************************************************/ + +bool MyApplication::_tooltip_disable = false; +int MyApplication::_event_filter = 0; +QEventLoop *MyApplication::eventLoop = 0; + +MyApplication::MyApplication(int &argc, char **argv) +: QApplication(argc, argv) +{ + if (isSessionRestored()) + { + bool ok; + int desktop; + + if (argc >= 2 && ::strcmp(argv[argc - 2], "-session-desktop") == 0) + { + desktop = QString(argv[argc - 1]).toInt(&ok); + if (ok) + CWINDOW_MainDesktop = desktop; + + //qDebug("session desktop: %d", CWINDOW_MainDesktop); + + argc -= 2; + } + } + + QObject::connect(this, SIGNAL(commitDataRequest(QSessionManager &)), SLOT(commitDataRequested(QSessionManager &))); +} + +void MyApplication::initClipboard() +{ + QObject::connect(clipboard(), SIGNAL(changed(QClipboard::Mode)), qApp, SLOT(clipboardHasChanged(QClipboard::Mode))); +} + +void MyApplication::clipboardHasChanged(QClipboard::Mode m) +{ + CLIPBOARD_has_changed(m); +} + +static bool QT_EventFilter(QEvent *e) +{ + bool cancel; + + if (!_application_keypress) + return false; + + if (e->type() == QEvent::KeyPress) + { + QKeyEvent *kevent = (QKeyEvent *)e; + + CKEY_clear(true); + + GB.FreeString(&CKEY_info.text); + CKEY_info.text = GB.NewZeroString(QT_ToUtf8(kevent->text())); + CKEY_info.state = kevent->modifiers(); + CKEY_info.code = kevent->key(); + + } + else if (e->type() == QEvent::InputMethod) + { + QInputMethodEvent *imevent = (QInputMethodEvent *)e; + + if (!imevent->commitString().isEmpty()) + { + CKEY_clear(true); + + GB.FreeString(&CKEY_info.text); + //qDebug("IMEnd: %s", imevent->text().latin1()); + CKEY_info.text = GB.NewZeroString(QT_ToUtf8(imevent->commitString())); + CKEY_info.state = 0; + CKEY_info.code = 0; + } + } + + GB.Call(&_application_keypress_func, 0, FALSE); + cancel = GB.Stopped(); + + CKEY_clear(false); + + return cancel; +} + +static bool QT_Notify(CWIDGET *object, bool value) +{ + bool old = object->flag.notified; + //qDebug("QT_Notify: %s %p %d", GB.GetClassName(object), object, value); + object->flag.notified = value; + return old; +} + +bool MyApplication::eventFilter(QObject *o, QEvent *e) +{ + if (o->isWidgetType()) + { + if ((e->spontaneous() && e->type() == QEvent::KeyPress) || e->type() == QEvent::InputMethod) + { + if (QT_EventFilter(e)) + return true; + } + else if (e->type() == QEvent::ToolTip) + { + if (_tooltip_disable) + return true; + } + else + { + QWidget *widget = (QWidget *)o; + CWIDGET *control; + + if (widget->isWindow()) + { + if (e->type() == QEvent::WindowActivate) + { + control = CWidget::getReal(widget); + //qDebug("WindowActivate: %p %s", widget, control ? control->name : "NULL"); + if (control) + CWIDGET_handle_focus(control, true); + else + CWINDOW_activate(NULL); + } + else if (e->type() == QEvent::WindowDeactivate) + { + control = CWidget::getReal(widget); + //qDebug("WindowDeactivate: %p %s", widget, control ? control->name : "NULL"); + if (control) + CWIDGET_handle_focus(control, false); + } + } + } + } + + return QApplication::eventFilter(o, e); +} + +/*bool MyApplication::notify(QObject *o, QEvent *e) +{ + if (o->isWidgetType()) + { + CWIDGET *ob = CWidget::get(o); + bool old, res; + + if (ob) + { + old = QT_Notify(ob, true); + res = QApplication::notify(o, e); + QT_Notify(ob, old); + return res; + } + } + + return QApplication::notify(o, e); +}*/ + +void MyApplication::setEventFilter(bool set) +{ + if (set) + { + _event_filter++; + if (_event_filter == 1) + qApp->installEventFilter(qApp); + } + else + { + _event_filter--; + if (_event_filter == 0) + qApp->removeEventFilter(qApp); + } +} + +void MyApplication::setTooltipEnabled(bool b) +{ + b = !b; + if (b == _tooltip_disable) + return; + + _tooltip_disable = b; + setEventFilter(b); +} + +void MyApplication::commitDataRequested(QSessionManager &session) +{ + QStringList cmd; + + if (CAPPLICATION_Restart) + { + int i; + char **str; + + str = (char **)GB.Array.Get(CAPPLICATION_Restart, 0); + for (i = 0; i < GB.Array.Count(CAPPLICATION_Restart); i++) + { + if (str[i]) + cmd += str[i]; + else + cmd += ""; + } + } + else + cmd += arguments().at(0); + + cmd += "-session"; + cmd += sessionId(); + + if (CWINDOW_Main) + { + cmd += "-session-desktop"; + cmd += QString::number(X11_window_get_desktop(CWINDOW_Main->widget.widget->winId())); + /*cmd += "-session-data"; + cmd += QString::number(CWINDOW_Main->x) + "," + + QString::number(CWINDOW_Main->y) + "," + + QString::number(CWINDOW_Main->w) + "," + + QString::number(CWINDOW_Main->h) + "," + + QString::number(QApplication::desktop()->screenNumber(CWINDOW_Main->widget.widget));*/ + } + + session.setRestartCommand(cmd); +} + +//--------------------------------------------------------------------------- + +static void x11_set_event_filter(void (*filter)(XEvent *)) +{ + _x11_event_filter = filter; +} + +#ifdef QT5 + +class MyNativeEventFilter: public QAbstractNativeEventFilter +{ +public: + + static MyNativeEventFilter manager; + + virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *) + { + xcb_generic_event_t *ev = static_cast(message); + int type = ev->response_type & ~0x80; + + switch(type) + { + case XCB_KEY_PRESS: + case XCB_KEY_RELEASE: + MAIN_x11_last_key_code = ((xcb_key_press_event_t *)ev)->detail; + break; + } + + if (_x11_event_filter) + { + XEvent xev; + + CLEAR(&xev); + xev.xany.type = type; + xev.xany.display = QX11Info::display(); + xev.xany.send_event = ev->response_type & 0x80 ? 1 : 0; + + switch (type) + { + //case XCB_KEY_PRESS: + //case XCB_KEY_RELEASE: + case XCB_EXPOSE: + { + xcb_expose_event_t *e = (xcb_expose_event_t *)ev; + xev.xexpose.window = e->window; + xev.xexpose.x = e->x; + xev.xexpose.y = e->y; + xev.xexpose.width = e->width; + xev.xexpose.height = e->height; + xev.xexpose.count = e->count; + break; + } + + case XCB_VISIBILITY_NOTIFY: + { + xcb_visibility_notify_event_t *e = (xcb_visibility_notify_event_t *)ev; + xev.xvisibility.window = e->window; + xev.xvisibility.state = e->state; + break; + } + + case XCB_DESTROY_NOTIFY: + { + xcb_destroy_notify_event_t *e = (xcb_destroy_notify_event_t *)ev; + xev.xdestroywindow.event = e->event; + xev.xdestroywindow.window = e->window; + break; + } + + case XCB_MAP_NOTIFY: + { + xcb_map_notify_event_t *e = (xcb_map_notify_event_t *)ev; + xev.xmap.event = e->event; + xev.xmap.window = e->window; + xev.xmap.override_redirect = e->override_redirect; + break; + } + + case XCB_UNMAP_NOTIFY: + { + xcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *)ev; + xev.xunmap.event = e->event; + xev.xunmap.window = e->window; + xev.xunmap.from_configure = e->from_configure; + break; + } + + case XCB_REPARENT_NOTIFY: + { + xcb_reparent_notify_event_t *e = (xcb_reparent_notify_event_t *)ev; + xev.xreparent.event = e->event; + xev.xreparent.window = e->window; + xev.xreparent.parent = e->parent; + xev.xreparent.x = e->x; + xev.xreparent.y = e->y; + xev.xreparent.override_redirect = e->override_redirect; + break; + } + + case XCB_CONFIGURE_NOTIFY: + { + xcb_configure_notify_event_t *e = (xcb_configure_notify_event_t *)ev; + xev.xconfigure.event = e->event; + xev.xconfigure.window = e->window; + xev.xconfigure.x = e->x; + xev.xconfigure.y = e->y; + xev.xconfigure.width = e->width; + xev.xconfigure.height = e->height; + xev.xconfigure.border_width = e->border_width; + xev.xconfigure.override_redirect = e->override_redirect; + break; + } + case XCB_PROPERTY_NOTIFY: + { + xcb_property_notify_event_t *e = (xcb_property_notify_event_t *)ev; + xev.xproperty.window = e->window; + xev.xproperty.atom = e->atom; + xev.xproperty.time = e->time; + xev.xproperty.state = e->state; + break; + } + + case XCB_SELECTION_CLEAR: + { + xcb_selection_clear_event_t *e = (xcb_selection_clear_event_t *)ev; + xev.xselectionclear.window = e->owner; + xev.xselectionclear.selection = e->selection; + xev.xselectionclear.time = e->time; + break; + } + + case XCB_SELECTION_REQUEST: + { + xcb_selection_request_event_t *e = (xcb_selection_request_event_t *)ev; + xev.xselectionrequest.owner = e->owner; + xev.xselectionrequest.requestor = e->requestor; + xev.xselectionrequest.selection = e->selection; + xev.xselectionrequest.target = e->target; + xev.xselectionrequest.property = e->property; + xev.xselectionrequest.time = e->time; + break; + } + + case XCB_SELECTION_NOTIFY: + { + xcb_selection_notify_event_t *e = (xcb_selection_notify_event_t *)ev; + xev.xselection.requestor = e->requestor; + xev.xselection.selection = e->selection; + xev.xselection.target = e->target; + xev.xselection.property = e->property; + xev.xselection.time = e->time; + break; + } + + case XCB_CLIENT_MESSAGE: + { + xcb_client_message_event_t *e = (xcb_client_message_event_t *)ev; + xev.xclient.window = e->window; + xev.xclient.message_type = e->type; + xev.xclient.format = e->format; + memcpy(&xev.xclient.data, &e->data, 20); + break; + } + + default: + //qDebug("gb.qt5: warning: unhandled xcb event: %d", type); + return false; + } + + (*_x11_event_filter)(&xev); + } + + return false; + } +}; + +MyNativeEventFilter MyNativeEventFilter::manager; + +#else + +bool MyApplication::x11EventFilter(XEvent *e) +{ + // Workaround for input methods that void the key code of KeyRelease eventFilter + if (e->type == XKeyPress) + MAIN_x11_last_key_code = e->xkey.keycode; + else if (e->type == XKeyRelease) + MAIN_x11_last_key_code = e->xkey.keycode; + + if (_x11_event_filter) + (*_x11_event_filter)(e); + + return false; +} + +#endif + +//--------------------------------------------------------------------------- + +MyTimer::MyTimer(GB_TIMER *t) : QObject(0) +{ + timer = t; + id = startTimer(t->delay); +} + +MyTimer::~MyTimer() +{ + killTimer(id); +} + +void MyTimer::timerEvent(QTimerEvent *e) +{ + if (timer) + GB.RaiseTimer(timer); +} + +//--------------------------------------------------------------------------- + +static void release_grab() +{ + MAIN_mouseGrabber = QWidget::mouseGrabber(); + MAIN_keyboardGrabber = QWidget::keyboardGrabber(); + + if (MAIN_mouseGrabber) + { + //qDebug("releaseMouse"); + MAIN_mouseGrabber->releaseMouse(); + } + if (MAIN_keyboardGrabber) + { + //qDebug("releaseKeyboard"); + MAIN_keyboardGrabber->releaseKeyboard(); + } + + #ifndef NO_X_WINDOW + if (qApp->activePopupWidget()) + { + XUngrabPointer(QX11Info::display(), CurrentTime); + XFlush(QX11Info::display()); + } + #endif +} + + +static void unrelease_grab() +{ + if (MAIN_mouseGrabber) + { + //qDebug("grabMouse"); + MAIN_mouseGrabber->grabMouse(); + MAIN_mouseGrabber = 0; + } + + if (MAIN_keyboardGrabber) + { + //qDebug("grabKeyboard"); + MAIN_keyboardGrabber->grabKeyboard(); + MAIN_keyboardGrabber = 0; + } +} + +static bool must_quit(void) +{ + #if DEBUG_WINDOW + qDebug("must_quit: Window = %d Watch = %d in_event_loop = %d MAIN_in_message_box = %d _prevent_quit = %d", CWindow::count, CWatch::count, in_event_loop, MAIN_in_message_box, _prevent_quit); + #endif + return CWINDOW_must_quit() && CWatch::count == 0 && in_event_loop && MAIN_in_message_box == 0 && _prevent_quit == 0; +} + +static void check_quit_now(intptr_t param) +{ + static bool exit_called = false; + + if (must_quit() && !exit_called) + { + if (QApplication::instance()) + { + GB_FUNCTION func; + + if (GB.ExistClass("TrayIcons")) + { + if (!GB.GetFunction(&func, (void *)GB.FindClass("TrayIcons"), "DeleteAll", NULL, NULL)) + GB.Call(&func, 0, FALSE); + } + +#ifndef QT5 + qApp->syncX(); +#endif + qApp->exit(); + exit_called = true; + } + } + else + _check_quit_posted = false; +} + +void MAIN_check_quit(void) +{ + if (_check_quit_posted) + return; + + GB.Post((GB_CALLBACK)check_quit_now, 0); + _check_quit_posted = true; +} + +void MAIN_update_scale(const QFont &font) +{ + MAIN_scale = GET_DESKTOP_SCALE(font.pointSize(), QX11Info::appDpiY()); +} + +static void QT_InitEventLoop(void) +{ +} + +//extern void qt_x11_set_global_double_buffer(bool); + + +static bool try_to_load_translation(QString &locale) +{ +#ifdef QT5 + return (!_translator->load(QString("qt_") + locale, QString(getenv("QTDIR")) + "/translations") + && !_translator->load(QString("qt_") + locale, QString("/usr/lib/qt5/translations")) + && !_translator->load(QString("qt_") + locale, QString("/usr/share/qt5/translations"))); +#else + return (!_translator->load(QString("qt_") + locale, QString(getenv("QTDIR")) + "/translations") + && !_translator->load(QString("qt_") + locale, QString("/usr/lib/qt4/translations")) + && !_translator->load(QString("qt_") + locale, QString("/usr/share/qt4/translations"))); +#endif +} + +static void init_lang(char *lang, bool rtl) +{ + int pos; + QString locale(lang); + + pos = locale.lastIndexOf("."); + if (pos >= 0) locale = locale.left(pos); + + if (_translator) + { + qApp->removeTranslator(_translator); + delete _translator; + _translator = NULL; + } + + _translator = new QTranslator(); + + if (!try_to_load_translation(locale)) + goto __INSTALL_TRANSLATOR; + + pos = locale.lastIndexOf("_"); + if (pos >= 0) + { + locale = locale.left(pos); + if (!try_to_load_translation(locale)) + goto __INSTALL_TRANSLATOR; + } + + delete _translator; + _translator = NULL; + + //if (strcmp(lang, "C")) + // qDebug("gb.qt4: warning: unable to load Qt translation: %s", lang); + + goto __SET_DIRECTION; + +__INSTALL_TRANSLATOR: + qApp->installTranslator(_translator); + +__SET_DIRECTION: + if (rtl) + qApp->setLayoutDirection(Qt::RightToLeft); +} + +static void hook_lang(char *lang, int rtl) +{ + if (!qApp) + return; + + init_lang(lang, rtl); + + //locale = QTextCodec::locale(); +} + +#if 0 +static int (*_old_handler)(Display *d, XErrorEvent *e) = NULL; + +static int X11_error_handler(Display *d, XErrorEvent *e) +{ + qDebug("X11 ERROR"); + //BREAKPOINT(); + + if (_old_handler) + return (*_old_handler)(d, e); + else + return 0; +} +#endif + +static void *_old_hook_main; + +static void hook_main(int *argc, char ***argv) +{ + char *env; + + env = getenv("GB_X11_INIT_THREADS"); + if (env && atoi(env)) + XInitThreads(); + + new MyApplication(*argc, *argv); + + QT_Init(); + init_lang(GB.System.Language(), GB.System.IsRightToLeft()); + + MAIN_init = true; + + //_old_handler = XSetErrorHandler(X11_error_handler); + + CALL_HOOK_MAIN(_old_hook_main, argc, argv); +} + + +static void hook_quit() +{ + GB_FUNCTION func; + + CWINDOW_close_all(true); + CWINDOW_delete_all(true); + + qApp->sendPostedEvents(); //processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::DeferredDeletion, 0); + qApp->sendPostedEvents(0, QEvent::DeferredDelete); + + if (!GB.GetFunction(&func, (void *)GB.FindClass("_Gui"), "_Quit", NULL, NULL)) + GB.Call(&func, 0, FALSE); +} + + +static void hook_loop() +{ + //qDebug("**** ENTERING EVENT LOOP"); + + qApp->sendPostedEvents(); + //qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::DeferredDeletion, 0); + + in_event_loop = true; + + if (!must_quit()) + qApp->exec(); + else + MAIN_check_quit(); + + hook_quit(); +} + + +static void hook_wait(int duration) +{ + if (MyDrawingArea::inAnyDrawEvent()) + { + GB.Error("Wait is forbidden during a repaint event"); + return; + } + + MAIN_in_wait++; + if (duration > 0) + { + if (CKEY_is_valid()) + fprintf(stderr, QT_NAME ": warning: calling the event loop during a keyboard event handler is ignored\n"); + else + qApp->processEvents(QEventLoop::AllEvents, duration); + } + else + qApp->processEvents(QEventLoop::ExcludeUserInputEvents, duration); + MAIN_in_wait--; +} + + +static void hook_timer(GB_TIMER *timer, bool on) +{ + if (timer->id) + { + MyTimer *t = (MyTimer *)(timer->id); + t->clearTimer(); + t->deleteLater(); + timer->id = 0; + } + + if (on) + timer->id = (intptr_t)(new MyTimer(timer)); +} + + +static void hook_watch(int fd, int type, void *callback, intptr_t param) +{ + CWatch::watch(fd, type, (GB_WATCH_CALLBACK)callback, param); +} + + +static void hook_post(void) +{ + static MyPostCheck check; + + //qDebug("hook_post ?"); + + if (MyPostCheck::in_check) + return; + + //qDebug("hook_post !"); + + MyPostCheck::in_check = true; + QTimer::singleShot(0, &check, SLOT(check())); +} + + +static void hook_error(int code, char *error, char *where) +{ + QString msg; + + qApp->restoreOverrideCursor(); + while (qApp->activePopupWidget()) + delete qApp->activePopupWidget(); + CWatch::stop(); + + msg = "This application has raised an unexpected
    error and must abort.


    "; + + if (code > 0) + { + msg = msg + "[%1] %2.
    %3"; + msg = msg.arg(code).arg(TO_QSTRING(error)).arg(where); + } + else + { + msg = msg + "%1.
    %2"; + msg = msg.arg(TO_QSTRING(error)).arg(where); + } + + release_grab(); + MAIN_in_message_box++; + QMessageBox::critical(0, TO_QSTRING(GB.Application.Name()), msg); + MAIN_in_message_box--; + unrelease_grab(); + MAIN_check_quit(); + + //qApp->exit(); +} + +static void QT_Init(void) +{ + static bool init = false; + QFont f; + char *env; + + if (init) + return; + + //qApp->setAttribute(Qt::AA_ImmediateWidgetCreation); + + X11_init(QX11Info::display(), QX11Info::appRootWindow()); + +#ifdef QT5 + _previousMessageHandler = qInstallMessageHandler(myMessageHandler); +#endif + + /*QX11Info::setAppDpiX(0, 92); + QX11Info::setAppDpiY(0, 92);*/ + + /*fcntl(ConnectionNumber(qt_xdisplay()), F_SETFD, FD_CLOEXEC);*/ + + if (::strcmp(qApp->style()->metaObject()->className(), "Breeze::Style") == 0) + { + env = getenv("GB_QT_NO_BREEZE_FIX"); + if (!env || atoi(env) == 0) + { + CSTYLE_fix_breeze = TRUE; + qApp->setStyle(new FixBreezeStyle); + } + } + else if (::strcmp(qApp->style()->metaObject()->className(), "Oxygen::Style") == 0) + { + env = getenv("GB_QT_NO_OXYGEN_FIX"); + if (!env || atoi(env) == 0) + { + CSTYLE_fix_oxygen = TRUE; + qApp->setStyle(new FixBreezeStyle); + } + } + + MAIN_update_scale(qApp->desktop()->font()); + + qApp->installEventFilter(&CWidget::manager); +#ifdef QT5 + qApp->installNativeEventFilter(&MyNativeEventFilter::manager); +#endif + + MyApplication::setEventFilter(true); + + if (GB.GetFunction(&_application_keypress_func, (void *)GB.Application.StartupClass(), "Application_KeyPress", "", "") == 0) + { + _application_keypress = true; + MyApplication::setEventFilter(true); + } + + //qt_x11_set_global_double_buffer(false); + + qApp->setQuitOnLastWindowClosed(false); + + MyApplication::initClipboard(); + + env = getenv("GB_QT_KEY_DEBUG"); + if (env && atoi(env) != 0) + MAIN_key_debug = TRUE; + + GB.Hook(GB_HOOK_LOOP, (void *)hook_loop); + GB.Hook(GB_HOOK_WAIT, (void *)hook_wait); + GB.Hook(GB_HOOK_TIMER, (void *)hook_timer); + GB.Hook(GB_HOOK_WATCH, (void *)hook_watch); + GB.Hook(GB_HOOK_POST, (void *)hook_post); + + init = true; +} + + +static void QT_InitWidget(QWidget *widget, void *object, int fill_bg) +{ + ((CWIDGET *)object)->flag.fillBackground = fill_bg; + CWIDGET_new(widget, object); +} + +static void QT_SetWheelFlag(void *object) +{ + ((CWIDGET *)object)->flag.wheel = true; +} + +void *QT_GetObject(QWidget *widget) +{ + return CWidget::get((QObject *)widget); +} + +static QWidget *QT_GetContainer(void *object) +{ + return QCONTAINER(object); +} + +/*static bool QT_IsDestroyed(void *object) +{ + return CWIDGET_test_flag(object, WF_DELETED); +}*/ + +static QPixmap *QT_GetPixmap(CPICTURE *pict) +{ + return pict->pixmap; +} + +const char *QT_ToUtf8(const QString &str) +{ + const char *res; + + _utf8_buffer[_utf8_count] = str.toUtf8(); + res = _utf8_buffer[_utf8_count].data(); + _utf8_length = _utf8_buffer[_utf8_count].length(); + _utf8_count++; + if (_utf8_count >= UTF8_NBUF) + _utf8_count = 0; + + return res; +} + +int QT_GetLastUtf8Length() +{ + return _utf8_length; +} + +char *QT_NewString(const QString &str) +{ + const char *res = QT_ToUtf8(str); + return GB.NewString(res, _utf8_length); +} + +void QT_ReturnNewString(const QString &str) +{ + const char *res = QT_ToUtf8(str); + GB.ReturnNewString(res, _utf8_length); +} + +static void *QT_CreatePicture(const QPixmap &p) +{ + return CPICTURE_create(&p); +} + +void MyApplication::linkDestroyed(QObject *qobject) +{ + void *object = _link_map.value(qobject, 0); + _link_map.remove(qobject); + if (object) + GB.Unref(POINTER(&object)); +} + +void QT_Link(QObject *qobject, void *object) +{ + _link_map.insert(qobject, object); + QObject::connect(qobject, SIGNAL(destroyed(QObject *)), qApp, SLOT(linkDestroyed(QObject *))); + GB.Ref(object); +} + +void *QT_GetLink(QObject *qobject) +{ + return _link_map.value(qobject, 0); +} + +void QT_PreventQuit(bool inc) +{ + if (inc) + _prevent_quit++; + else + { + _prevent_quit--; + MAIN_check_quit(); + } +} + +QMenu *QT_FindMenu(void *parent, const char *name) +{ + CMENU *menu = NULL; + + if (parent && GB.Is(parent, CLASS_Control)) + { + CWINDOW *window = CWidget::getWindow((CWIDGET *)parent); + menu = CWindow::findMenu(window, name); + } + + return menu ? menu->menu : NULL; +} + +static void declare_tray_icon() +{ + GB.Component.Declare(TrayIconDesc); + GB.Component.Declare(TrayIconsDesc); +} + +extern "C" { + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CBorderDesc, CColorDesc, + CAlignDesc, CArrangeDesc, CScrollDesc, CKeyDesc, CSelectDesc, + CMessageDesc, + CImageDesc, CPictureDesc, + CFontDesc, CFontsDesc, + CMouseDesc, CCursorDesc, CPointerDesc, + CClipboardDesc, CDragDesc, + StyleDesc, ScreenDesc, ScreensDesc, DesktopDesc, + ApplicationDesc, + CControlDesc, ContainerChildrenDesc, ContainerDesc, + UserControlDesc, UserContainerDesc, + CMenuChildrenDesc, CMenuDesc, + CLabelDesc, CTextLabelDesc, CSeparatorDesc, + CButtonDesc, CToggleButtonDesc, CToolButtonDesc, + CCheckBoxDesc, CRadioButtonDesc, + CTextBoxSelectionDesc, CTextBoxDesc, CComboBoxItemDesc, CComboBoxDesc, + CTextAreaSelectionDesc, CTextAreaDesc, + CFrameDesc, CPanelDesc, CHBoxDesc, CVBoxDesc, CHPanelDesc, CVPanelDesc, + CTabStripContainerChildrenDesc, CTabStripContainerDesc, CTabStripDesc, + CDrawingAreaDesc, + CSliderDesc, CMovieBoxDesc, CScrollBarDesc, + CWindowMenusDesc, CWindowControlsDesc, CWindowDesc, CWindowsDesc, CFormDesc, + CDialogDesc, +#ifndef QT5 + CEmbedderDesc, +#endif + CWatcherDesc, + PrinterDesc, + SvgImageDesc, + NULL +}; + +#ifdef QT5 +void *GB_QT5_1[] EXPORT = +#else +void *GB_QT4_1[] EXPORT = +#endif +{ + (void *)1, + + (void *)QT_InitEventLoop, + (void *)QT_Init, + (void *)QT_InitWidget, + (void *)QT_SetWheelFlag, + (void *)QT_GetObject, + (void *)QT_GetContainer, + (void *)CWIDGET_border_simple, + (void *)CWIDGET_border_full, + (void *)CWIDGET_scrollbar, + (void *)Control_Font, + (void *)CFONT_create, + (void *)CFONT_set, + (void *)QT_CreatePicture, + //(void *)QT_MimeSourceFactory, + (void *)QT_GetPixmap, + (void *)QT_ToUtf8, + (void *)QT_GetLastUtf8Length, + (void *)QT_NewString, + (void *)QT_ReturnNewString, + (void *)QT_EventFilter, + (void *)QT_Notify, + (void *)CCONST_alignment, + (void *)QT_Link, + (void *)QT_GetLink, + (void *)PAINT_get_current, + (void *)CWIDGET_get_background, + (void *)Control_Mouse, + (void *)CWIDGET_after_set_color, + NULL +}; + +const char *GB_INCLUDE EXPORT = "gb.draw,gb.gui.base"; + +int EXPORT GB_INIT(void) +{ + char *env; + + // Do not disable GLib support + + /*env = getenv("KDE_FULL_SESSION"); + if (env && !strcasecmp(env, "true")) + putenv((char *)"QT_NO_GLIB=1");*/ + + env = getenv("GB_GUI_BUSY"); + if (env && atoi(env)) + MAIN_debug_busy = true; + + //putenv((char *)"QT_SLOW_TOPLEVEL_RESIZE=1"); + + _old_hook_main = GB.Hook(GB_HOOK_MAIN, (void *)hook_main); + GB.Hook(GB_HOOK_QUIT, (void *)hook_quit); + GB.Hook(GB_HOOK_ERROR, (void *)hook_error); + GB.Hook(GB_HOOK_LANG, (void *)hook_lang); + + GB.Component.Load("gb.draw"); + GB.Component.Load("gb.image"); + GB.Component.Load("gb.gui.base"); + + GB.GetInterface("gb.geom", GEOM_INTERFACE_VERSION, &GEOM); + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + + IMAGE.SetDefaultFormat(GB_IMAGE_BGRP); + + DRAW_init(); + + CLASS_Control = GB.FindClass("Control"); + CLASS_Container = GB.FindClass("Container"); + CLASS_ContainerChildren = GB.FindClass("ContainerChildren"); + CLASS_UserControl = GB.FindClass("UserControl"); + CLASS_UserContainer = GB.FindClass("UserContainer"); + CLASS_TabStrip = GB.FindClass("TabStrip"); + CLASS_Window = GB.FindClass("Window"); + CLASS_Menu = GB.FindClass("Menu"); + CLASS_Picture = GB.FindClass("Picture"); + CLASS_Drawing = GB.FindClass("Drawing"); + CLASS_DrawingArea = GB.FindClass("DrawingArea"); + CLASS_Printer = GB.FindClass("Printer"); + CLASS_Image = GB.FindClass("Image"); + CLASS_SvgImage = GB.FindClass("SvgImage"); + CLASS_TextArea = GB.FindClass("TextArea"); + + QT_InitEventLoop(); + + #ifdef OS_CYGWIN + return 1; + #else + return 0; + #endif +} + +void EXPORT GB_EXIT() +{ + #ifndef NO_X_WINDOW + X11_exit(); + #endif + //qApp->setStyle("windows"); + delete qApp; +} + +#ifndef NO_X_WINDOW +int EXPORT GB_INFO(const char *key, void **value) +{ + if (!strcasecmp(key, "DISPLAY")) + { + *value = (void *)QX11Info::display(); + return TRUE; + } + else if (!strcasecmp(key, "ROOT_WINDOW")) + { + *value = (void *)QX11Info::appRootWindow(); + return TRUE; + } + else if (!strcasecmp(key, "SET_EVENT_FILTER")) + { + *value = (void *)x11_set_event_filter; + return TRUE; + } + else if (!strcasecmp(key, "GET_HANDLE")) + { + *value = (void *)CWIDGET_get_handle; + return TRUE; + } + else if (!strcasecmp(key, "TIME")) + { + *value = (void *)QX11Info::appTime(); + return TRUE; + } + else if (!strcasecmp(key, "DECLARE_TRAYICON")) + { + *value = (void *)declare_tray_icon; + return TRUE; + } + else + return FALSE; +} +#endif + +/*#ifndef NO_X_WINDOW +extern Time qt_x_time; +#endif*/ + +static void activate_main_window(intptr_t value) +{ + CWINDOW *active; + + active = CWINDOW_Active; + if (!active) active = CWINDOW_LastActive; + + if (!active) + return; + + MyMainWindow *win = (MyMainWindow *)active->widget.widget; + if (win && !win->isWindow()) + win = (MyMainWindow *)win->window(); + if (win) + { + /*#ifndef NO_X_WINDOW + qt_x_time = CurrentTime; + #endif*/ + win->raise(); + win->activateWindow(); + } +} + +void EXPORT GB_SIGNAL(int signal, void *param) +{ + if (!qApp) + return; + + switch(signal) + { + case GB_SIGNAL_DEBUG_BREAK: + release_grab(); + break; + + case GB_SIGNAL_DEBUG_FORWARD: + //while (qApp->activePopupWidget()) + // delete qApp->activePopupWidget(); +#ifndef QT5 + qApp->syncX(); +#endif + break; + + case GB_SIGNAL_DEBUG_CONTINUE: + GB.Post((GB_CALLBACK)activate_main_window, 0); + unrelease_grab(); + break; + } +} + +} // extern "C" + +/* class MyPostCheck */ + +bool MyPostCheck::in_check = false; + +void MyPostCheck::check(void) +{ + //qDebug("MyPostCheck::check"); + in_check = false; + GB.CheckPost(); +} + diff --git a/gb.qt4/src/main.h b/gb.qt4/src/main.h new file mode 100644 index 00000000..d6311876 --- /dev/null +++ b/gb.qt4/src/main.h @@ -0,0 +1,166 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" + +#include +#include +#include +#include +#include +#include +#include + +#define DO_NOT_USE_QT_INTERFACE +#include "gb.qt.h" +#include "gb.image.h" +#include "gb.geom.h" + +#ifndef __MAIN_CPP +extern "C" const GB_INTERFACE *GB_PTR; +extern "C" IMAGE_INTERFACE IMAGE; +extern "C" GEOM_INTERFACE GEOM; + +extern int MAIN_in_wait; +extern int MAIN_in_message_box; +extern int MAIN_loop_level; +extern int MAIN_scale; +extern bool MAIN_debug_busy; +extern bool MAIN_init; +extern bool MAIN_key_debug; +#ifndef NO_X_WINDOW +extern int MAIN_x11_last_key_code; +#endif + +extern GB_CLASS CLASS_Control; +extern GB_CLASS CLASS_Container; +extern GB_CLASS CLASS_ContainerChildren; +extern GB_CLASS CLASS_UserControl; +extern GB_CLASS CLASS_UserContainer; +extern GB_CLASS CLASS_TabStrip; +extern GB_CLASS CLASS_Window; +extern GB_CLASS CLASS_Menu; +extern GB_CLASS CLASS_Picture; +extern GB_CLASS CLASS_Drawing; +extern GB_CLASS CLASS_DrawingArea; +extern GB_CLASS CLASS_ScrollArea; +extern GB_CLASS CLASS_Printer; +extern GB_CLASS CLASS_Image; +extern GB_CLASS CLASS_SvgImage; +extern GB_CLASS CLASS_TextArea; + +#endif + +#define GB (*GB_PTR) + +class MyPostCheck: public QObject +{ + Q_OBJECT + +public: + + static bool in_check; + +public slots: + + void check(void); +}; + +class MyApplication: public QApplication +{ + Q_OBJECT + +public: + + MyApplication(int &argc, char **argv); +#ifndef QT5 + virtual bool x11EventFilter(XEvent *e); +#endif + virtual bool eventFilter(QObject *o, QEvent *e); + //virtual bool notify(QObject *o, QEvent *e); + + static void setEventFilter(bool set); + + static bool isTooltipEnabled() { return !_tooltip_disable; } + static void setTooltipEnabled(bool b); + + static void initClipboard(); + + static QEventLoop *eventLoop; + +public slots: + + void linkDestroyed(QObject *); + void clipboardHasChanged(QClipboard::Mode); + void commitDataRequested(QSessionManager &); + +private: + static bool _tooltip_disable; + static int _event_filter; +}; + +class MyTimer : public QObject +{ +public: + + MyTimer(GB_TIMER *timer); + ~MyTimer(); + void clearTimer() { timer = 0; } + +protected: + + void timerEvent(QTimerEvent *); + +private: + + GB_TIMER *timer; + intptr_t id; +}; + + +#define UTF8_NBUF 4 + +void MAIN_check_quit(void); +void MAIN_update_scale(const QFont &); +void MAIN_process_events(void); +void MAIN_init_error(void); + +#define MAIN_CHECK_INIT() (MAIN_init ? 0 : (MAIN_init_error(), 1)) + +const char *QT_ToUtf8(const QString &str); +int QT_GetLastUtf8Length(); +char *QT_NewString(const QString &str); +void QT_ReturnNewString(const QString &str); +void QT_RegisterAction(void *object, const char *key, int on); +void QT_RaiseAction(const char *key); +void *QT_GetObject(QWidget *); +void QT_Link(QObject *, void *); +void *QT_GetLink(QObject *); +void QT_PreventQuit(bool inc); +QMenu *QT_FindMenu(void *parent, const char *name); + +#endif diff --git a/gb.qt4/src/opengl/CGLarea.cpp b/gb.qt4/src/opengl/CGLarea.cpp new file mode 100644 index 00000000..1385db30 --- /dev/null +++ b/gb.qt4/src/opengl/CGLarea.cpp @@ -0,0 +1,202 @@ +/*************************************************************************** + + CGLarea.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CGLAREA_CPP + +#include "gb.image.h" +#include "CGLarea.h" + +//#include "gl.h" +//#include + + +static int glWidgetCount = 0; +static QGLWidget *sharedWidget = 0; + +DECLARE_EVENT(EVENT_Open); +DECLARE_EVENT(EVENT_Draw); +DECLARE_EVENT(EVENT_Resize); + +static void makeSharing() +{ + if (sharedWidget) + return; + + sharedWidget = new QGLWidget(); + //qDebug("make_sharing: %p", sharedWidget); +} + +BEGIN_METHOD_VOID(CGLAREA_exit) + + //qDebug("CGLAREA_exit: %p", sharedWidget); + delete sharedWidget; + sharedWidget = 0; + +END_METHOD + +BEGIN_METHOD(CGLAREA_new, GB_OBJECT parent) + + if (!QGLFormat::hasOpenGL()) + { + GB.Error( "This system has no OpenGL support"); + return; + } + + makeSharing(); + GLarea *area = new GLarea(QT.GetContainer(VARG(parent)), THIS, sharedWidget); + glWidgetCount++; + + QT.InitWidget(area, _object, false); + area->show(); + +END_METHOD + +BEGIN_METHOD_VOID(CGLAREA_free) + + glWidgetCount--; + + // clean up the shared datas between GLwidgets if there is no more CGLarea + if (glWidgetCount <= 0) + { + delete sharedWidget; + sharedWidget = 0; + makeSharing(); + glWidgetCount = 0; + } + + +END_METHOD + +BEGIN_METHOD_VOID(CGLAREA_update) + + WIDGET->updateGL(); + +END_METHOD + +#if 0 +BEGIN_METHOD_VOID(CGLAREA_select) + + WIDGET->makeCurrent(); + // really needed ? + GL.Init(); + +END_METHOD +#endif + +BEGIN_METHOD(CGLAREA_text, GB_STRING text; GB_INTEGER x; GB_INTEGER y) + + QString text; + int x, y; + #ifdef GL_LIGHTING + GLboolean _LIGHTING = glIsEnabled(GL_LIGHTING); + #endif + GLboolean _TEXTURE_2D = glIsEnabled(GL_TEXTURE_2D); + + #ifdef GL_LIGHTING + if (_LIGHTING) + glDisable(GL_LIGHTING); + #endif + if (_TEXTURE_2D) + glDisable(GL_TEXTURE_2D); + + text = QSTRING_ARG(text); + x = VARG(x); + y = VARG(y); + + WIDGET->renderText(x, y, text, WIDGET->font()); + + #ifdef GL_LIGHTING + if (_LIGHTING) + glEnable(GL_LIGHTING); + #endif + if (_TEXTURE_2D) + glEnable(GL_TEXTURE_2D); + +END_METHOD + +/**************************************************************************/ + +GB_DESC CGlareaDesc[] = +{ + GB_DECLARE("GLArea", sizeof(CGLAREA)), GB_INHERITS("Control"), + + GB_STATIC_METHOD("_exit", NULL, CGLAREA_exit, NULL), + + GB_METHOD("_new", NULL, CGLAREA_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, CGLAREA_free, NULL), + //GB_METHOD("Update", NULL, CGLAREA_update, NULL), + GB_METHOD("Refresh", NULL, CGLAREA_update, NULL), + //GB_METHOD("Select", NULL, CGLAREA_select, NULL), + //GB_METHOD("Text", NULL, CGLAREA_text, "(Text)s(X)i(Y)i"), + + GB_CONSTANT("_Group", "s", "Special"), + + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + + GB_END_DECLARE +}; + +/* class GLarea */ + +GLarea::GLarea(QWidget *parent,CGLAREA *object, QGLWidget *sharing): QGLWidget(parent, sharing) +{ + setFocusPolicy(Qt::WheelFocus); + setAttribute(Qt::WA_InputMethodEnabled, true); + _area = object; +}; + + +void GLarea::initializeGL() +{ + GL.Init(); + GB.Raise(_area, EVENT_Open, 0); +} + +void GLarea::paintGL() +{ + static bool CleanupOnFirstShow = 0; + + uint color; + if (!CleanupOnFirstShow) + { + // clear to avoid garbage + color = QT.GetBackgroundColor(_area); + if (color == GB_COLOR_DEFAULT) + color = 0; + + qglClearColor(QColor((QRgb)color)); + glClear(GL_COLOR_BUFFER_BIT); + + CleanupOnFirstShow = true; + } + + GB.Raise(_area, EVENT_Draw, 0); +} + +void GLarea::resizeGL(int w, int h) +{ + GB.Raise(_area, EVENT_Resize, 0); +} + diff --git a/gb.qt4/src/opengl/CGLarea.h b/gb.qt4/src/opengl/CGLarea.h new file mode 100644 index 00000000..e824042b --- /dev/null +++ b/gb.qt4/src/opengl/CGLarea.h @@ -0,0 +1,60 @@ +/*************************************************************************** + + CGLarea.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CGLAREA_H +#define __CGLAREA_H + +#include "main.h" + +#include + +typedef + struct { + QT_WIDGET widget; + } + CGLAREA; + +#ifndef __CGLAREA_CPP +extern GB_DESC CGlareaDesc[]; +#else + +#define THIS ((CGLAREA *)_object) +#define WIDGET ((GLarea *)((QT_WIDGET *)_object)->widget) + +#endif /* __CGLAREA_CPP */ + +class GLarea : public QGLWidget +{ +Q_OBJECT +public: + GLarea(QWidget *parent,CGLAREA *object, QGLWidget *sharing = 0); + ~GLarea() {}; +protected: + void initializeGL(); + void paintGL(); + void resizeGL(int w, int h); +private: + CGLAREA *_area; +}; + +#endif diff --git a/gb.qt4/src/opengl/Makefile.am b/gb.qt4/src/opengl/Makefile.am new file mode 100644 index 00000000..6e9a597b --- /dev/null +++ b/gb.qt4/src/opengl/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.qt4.opengl +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +gblib_LTLIBRARIES = gb.qt4.opengl.la + +gb_qt4_opengl_la_LIBADD = @QTOPENGL_LIB@ +gb_qt4_opengl_la_LDFLAGS = -module @LD_FLAGS@ @QTOPENGL_LDFLAGS@ +gb_qt4_opengl_la_CPPFLAGS = @QTOPENGL_INC@ + +gb_qt4_opengl_la_SOURCES = \ + main.h main.cpp \ + CGLarea.h CGLarea_moc.cpp CGLarea.cpp + diff --git a/gb.qt4/src/opengl/gb.qt4.opengl.component b/gb.qt4/src/opengl/gb.qt4.opengl.component new file mode 100644 index 00000000..8738f7c1 --- /dev/null +++ b/gb.qt4/src/opengl/gb.qt4.opengl.component @@ -0,0 +1,6 @@ +[Component] +Author=Laurent Carlier,Benoît Minisini +Require=gb.qt4,gb.opengl +Type=Form +Implement=OpenGLViewer +State=Stable diff --git a/gb.qt4/src/opengl/main.cpp b/gb.qt4/src/opengl/main.cpp new file mode 100644 index 00000000..b0aad7b7 --- /dev/null +++ b/gb.qt4/src/opengl/main.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** + + main.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include "main.h" +#include "CGLarea.h" + +extern "C" { + +GB_INTERFACE GB EXPORT; +QT_INTERFACE QT; +GL_INTERFACE GL; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CGlareaDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface(QT_NAME, QT_INTERFACE_VERSION, &QT); + GB.GetInterface("gb.opengl", GL_INTERFACE_VERSION, &GL); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +} diff --git a/gb.qt4/src/opengl/main.h b/gb.qt4/src/opengl/main.h new file mode 100644 index 00000000..26349161 --- /dev/null +++ b/gb.qt4/src/opengl/main.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + main.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "../gb.qt.h" +#include "gb.gl.h" + +#ifndef __MAIN_CPP +extern GB_INTERFACE GB; +extern QT_INTERFACE QT; +extern GL_INTERFACE GL; +#endif + +#endif diff --git a/gb.qt4/src/trayicon.xpm b/gb.qt4/src/trayicon.xpm new file mode 100644 index 00000000..7a764cf4 --- /dev/null +++ b/gb.qt4/src/trayicon.xpm @@ -0,0 +1,163 @@ +/* XPM */ +static char * _default_trayicon[] = { +"24 24 136 2", +" c None", +". c #4167C6", +"+ c #446ECF", +"@ c #4975D8", +"# c #4E7DDF", +"$ c #5586E7", +"% c #314F9E", +"& c #2D4A91", +"* c #3153A9", +"= c #304F9B", +"- c #365AB7", +"; c #3C64C5", +"> c #446FD3", +", c #4D7DE1", +"' c #5A8EED", +") c #2D4371", +"! c #46628B", +"~ c #688CB7", +"{ c #5D7FAA", +"] c #668AB6", +"^ c #3B5CA7", +"/ c #3556A2", +"( c #365BB8", +"_ c #3F67C9", +": c #4977DC", +"< c #568AEB", +"[ c #33496F", +"} c #749CC7", +"| c #80AAD7", +"1 c #81ADD9", +"2 c #82AED9", +"3 c #81ACD8", +"4 c #7AA4D0", +"5 c #729BCA", +"6 c #3457AE", +"7 c #476AB0", +"8 c #577CB9", +"9 c #4870C1", +"0 c #5486E6", +"a c #7BA4D0", +"b c #7DA8D5", +"c c #7AA5D3", +"d c #80ABD8", +"e c #82AEDA", +"f c #83AFDA", +"g c #7EA8D6", +"h c #668CC1", +"i c #7EA9D5", +"j c #81ADD8", +"k c #8CB5DC", +"l c #9FBDDC", +"m c #BBD7ED", +"n c #9AAEC3", +"o c #79A2D1", +"p c #77A1D1", +"q c #5278BB", +"r c #3D5DA1", +"s c #6F97C8", +"t c #587DBA", +"u c #7EA9D6", +"v c #82ADDA", +"w c #81ACD9", +"x c #7FAAD6", +"y c #6F91B9", +"z c #6A85A0", +"A c #9DC4E5", +"B c #849AA4", +"C c #1E1F20", +"D c #6189C5", +"E c #335195", +"F c #395BA9", +"G c #4667A2", +"H c #4E72B6", +"I c #2E4E9F", +"J c #729ACA", +"K c #7FAAD7", +"L c #83AFDB", +"M c #7CA6D3", +"N c #202B38", +"O c #020202", +"P c #495B6D", +"Q c #C4E6FB", +"R c #718186", +"S c #4A6EB5", +"T c #3E5E9E", +"U c #587DBC", +"V c #78A1CF", +"W c #466AB6", +"X c #5F84BB", +"Y c #78A0CE", +"Z c #7FA9D6", +"` c #24313F", +" . c #324256", +".. c #97C1E6", +"+. c #C7E9FC", +"@. c #2F4E98", +"#. c #719ACA", +"$. c #6990C2", +"%. c #81ABD6", +"&. c #78A0CA", +"*. c #5E7DA0", +"=. c #77A0CB", +"-. c #80ABD7", +";. c #93BEE4", +">. c #C4E5FA", +",. c #4B6DA9", +"'. c #6F97C5", +"). c #84AFDA", +"!. c #82ADD9", +"~. c #8AB5DE", +"{. c #B0D6F1", +"]. c #719ACC", +"^. c #7197C3", +"/. c #85B0DB", +"(. c #84B0DB", +"_. c #91BCE2", +":. c #638DD3", +"<. c #799DDB", +"[. c #4A6EC2", +"}. c #718AB8", +"|. c #8DA6CA", +"1. c #92ACD1", +"2. c #C4D9EE", +"3. c #AEC6E7", +"4. c #38539B", +"5. c #405A99", +"6. c #7D95BC", +"7. c #9EB6D7", +"8. c #C3D8EE", +"9. c #5771AD", +"0. c #25418F", +"a. c #4863A1", +"b. c #C5D9EF", +"c. c #6384CA", +"d. c #3D65C5", +"e. c #395AB0", +" ", +" ", +" . + @ # $ ", +" % & * = - ; > , ' ", +" ) ! ~ { ] ^ / ( _ : < ", +" [ } | 1 2 3 4 5 6 7 8 9 0 ", +" a b c d 1 e f g h i j k l m n ", +" o p q r s t u v w f v x y z A B C ", +" D E F G H I J K f L e M N O P Q R ", +" S T U V W X Y u f L f Z ` O ...+. ", +" @.#.$. %.e L L e &.*.=.-.;.>. ", +" ,.'. ).L L f e e e !.~.{. ", +" ].^. /.(.L L f f f f _. ", +" b ", +" -. ", +" ", +" :. ", +" <.[.}.|.1. ", +" 2.3.4.5.6.7. ", +" 8.9.0.a. ", +" b.c.d.e. ", +" ", +" ", +" "}; diff --git a/gb.qt4/src/webkit/Makefile.am b/gb.qt4/src/webkit/Makefile.am new file mode 100644 index 00000000..4eac9e0f --- /dev/null +++ b/gb.qt4/src/webkit/Makefile.am @@ -0,0 +1,19 @@ +COMPONENT = gb.qt4.webkit +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +gblib_LTLIBRARIES = gb.qt4.webkit.la + +gb_qt4_webkit_la_LIBADD = @QTWEBKIT_LIB@ +gb_qt4_webkit_la_LDFLAGS = -module @LD_FLAGS@ @QTWEBKIT_LDFLAGS@ +gb_qt4_webkit_la_CPPFLAGS = @QTWEBKIT_INC@ -I$(top_srcdir)/share -I$(top_srcdir)/src/share + +gb_qt4_webkit_la_SOURCES = \ + main.h main.cpp \ + cwebsettings.h cwebsettings.cpp \ + cwebframe.h cwebframe.cpp cwebframe_moc.cpp \ + cwebelement.h cwebelement.cpp \ + cwebview.h cwebview.cpp cwebview_moc.cpp \ + ccookiejar.h ccookiejar.cpp ccookiejar_moc.cpp \ + cwebhittest.h cwebhittest.cpp \ + cwebdownload.h cwebdownload.cpp cwebdownload_moc.cpp diff --git a/gb.qt4/src/webkit/ccookiejar.cpp b/gb.qt4/src/webkit/ccookiejar.cpp new file mode 100644 index 00000000..72457f1d --- /dev/null +++ b/gb.qt4/src/webkit/ccookiejar.cpp @@ -0,0 +1,188 @@ +/*************************************************************************** + + ccookiejar.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCOOKIEJAR_CPP + +#include "ccookiejar.h" + +#include + +BEGIN_PROPERTY(Cookie_Domain) + + if (READ_PROPERTY) + RETURN_NEW_STRING(COOKIE->domain()); + else + COOKIE->setDomain(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(Cookie_ExpirationDate) + + GB_DATE date; + GB_DATE_SERIAL ds; + QDateTime d; + + if (READ_PROPERTY) + { + if (COOKIE->isSessionCookie()) + { + GB.ReturnDate(NULL); + return; + } + + d = COOKIE->expirationDate(); + + ds.year = d.date().year(); + ds.month = d.date().month(); + ds.day = d.date().day(); + ds.hour = d.time().hour(); + ds.min = d.time().minute(); + ds.sec = d.time().second(); + ds.msec = d.time().msec(); + + GB.MakeDate(&ds, &date); + GB.ReturnDate(&date); + } + else + { + ds = *GB.SplitDate(PROP(GB_DATE)); + d = QDateTime(QDate(ds.year, ds.month, ds.day), QTime(ds.hour, ds.min, ds.sec, ds.msec)); + COOKIE->setExpirationDate(d); + } + +END_PROPERTY + +BEGIN_PROPERTY(Cookie_HttpOnly) + + if (READ_PROPERTY) + GB.ReturnBoolean(COOKIE->isHttpOnly()); + else + COOKIE->setHttpOnly(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Cookie_Secure) + + if (READ_PROPERTY) + GB.ReturnBoolean(COOKIE->isSecure()); + else + COOKIE->setSecure(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Cookie_Session) + + if (READ_PROPERTY) + GB.ReturnBoolean(COOKIE->isSessionCookie()); + else + { + QDateTime d; + COOKIE->setExpirationDate(d); + } + +END_PROPERTY + +BEGIN_PROPERTY(Cookie_Name) + + if (READ_PROPERTY) + { + QByteArray ba = COOKIE->name(); + GB.ReturnNewString((const char *)ba, ba.size()); + } + else + { + COOKIE->setName(QByteArray(PSTRING(), PLENGTH())); + } + +END_PROPERTY + +BEGIN_PROPERTY(Cookie_Path) + + if (READ_PROPERTY) + RETURN_NEW_STRING(COOKIE->path()); + else + COOKIE->setPath(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(Cookie_Value) + + if (READ_PROPERTY) + { + QByteArray ba = COOKIE->value(); + GB.ReturnNewString((const char *)ba, ba.size()); + } + else + { + COOKIE->setValue(QByteArray(PSTRING(), PLENGTH())); + } + +END_PROPERTY + +BEGIN_METHOD_VOID(Cookie_new) + + COOKIE = new QNetworkCookie; + +END_METHOD + +BEGIN_METHOD_VOID(Cookie_free) + + delete COOKIE; + +END_METHOD + +GB_DESC CookieDesc[] = +{ + GB_DECLARE("Cookie", sizeof(CCOOKIE)), + + GB_METHOD("_new", NULL, Cookie_new, NULL), + GB_METHOD("_free", NULL, Cookie_free, NULL), + + GB_PROPERTY("Domain", "s", Cookie_Domain), + GB_PROPERTY("ExpirationDate", "d", Cookie_ExpirationDate), + GB_PROPERTY("HttpOnly", "b", Cookie_HttpOnly), + GB_PROPERTY("Secure", "b", Cookie_Secure), + GB_PROPERTY("Session", "b", Cookie_Session), + GB_PROPERTY("Name", "s", Cookie_Name), + GB_PROPERTY("Path", "s", Cookie_Path), + GB_PROPERTY("Value", "s", Cookie_Value), + + GB_END_DECLARE +}; + +/***************************************************************************/ + +CCOOKIE *WEB_create_cookie(const QNetworkCookie &cookie) +{ + CCOOKIE *_object = (CCOOKIE *)GB.New(GB.FindClass("Cookie"), NULL, NULL); + *(THIS_COOKIE->cookie) = cookie; + + return THIS_COOKIE; +} + +/***************************************************************************/ + +MyCookieJar::MyCookieJar(QObject *parent) : QNetworkCookieJar(parent) +{ +} + diff --git a/gb.qt4/src/webkit/ccookiejar.h b/gb.qt4/src/webkit/ccookiejar.h new file mode 100644 index 00000000..2de32cd8 --- /dev/null +++ b/gb.qt4/src/webkit/ccookiejar.h @@ -0,0 +1,77 @@ +/*************************************************************************** + + ccookiejar.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCOOKIEJAR_H +#define __CCOOKIEJAR_H + +#include "main.h" + +#include +#include +#include + +#ifndef __CCOOKIEJAR_CPP + +extern GB_DESC CookieDesc[]; +//extern GB_DESC CCookieJarDesc[]; + +#else + +#define THIS_COOKIE ((CCOOKIE *)_object) +#define COOKIE (THIS_COOKIE->cookie) + +#endif + +class MyCookieJar : public QNetworkCookieJar +{ + Q_OBJECT + +public: + + MyCookieJar(QObject *parent = 0); + + QList allCookies () const { return QNetworkCookieJar::allCookies(); } + void setAllCookies(const QList &cookieList) { QNetworkCookieJar::setAllCookies(cookieList); } + + //virtual QList cookiesForUrl(const QUrl & url) const; + //virtual bool setCookiesFromUrl(const QList &cookieList, const QUrl &url); +}; + +typedef + struct + { + GB_BASE ob; + MyCookieJar *jar; + } + CCOOKIEJAR; + +typedef + struct { + GB_BASE ob; + QNetworkCookie *cookie; + } + CCOOKIE; + +CCOOKIE *WEB_create_cookie(const QNetworkCookie &cookie); + +#endif diff --git a/gb.qt4/src/webkit/control/webview.png b/gb.qt4/src/webkit/control/webview.png new file mode 100644 index 0000000000000000000000000000000000000000..7b9ef3e26d3d764a819d70db6792a28351728d25 GIT binary patch literal 1160 zcmV;31b6$1P)31nLr6`z z@x}`iyfH?U3sEEiAt*&4K;^3kX;0hJ!fAWDyEEg(E|k_DI8-c7@?fr?vY| zATgUoJ)@R>A3_Vo2~7}cmenR$nsxp=0^?vN@SD>VYmKvA0 zmZaI-mLXZCwpG1$d<6Df);v)I>lX-twwx;ZytucIFV2oq<2ax#m#&9=(p_TR-Xceb z!h1vDmxAT5F@pzO7`6BszC2svtDZ5+A?!X~qOYXEC@=y>aiwH<^T!h3^alSf1-}m& z8XTd?6@sKt^bH^WJiw8Hj}^fxg;+od+}evUVGO%YP4IBWp}ns1J}W*M3!zj7Z7sgG zT=z8}9U9 zf4Iu=0Ur!t1+&%`R~Qx3jn3QGQ6oB^W^doLOe*Fs)b2b zE==QM5CR`XYQR;3%%RFII`z#0~6Y%*}YCEHj~4wj9grs8Gf51a@{L@owW&Zu>c2(Wuzbks>LU{XHRjx&!=>8PR`z>h6-7{X7v0&SRb|Om zSVYK9{Z;GTvlS3AE+g6H%F4vOuxvX^(f-cz9Ksy{bK-mw;_+Jpg{~HFRIm z6vr*DE9`tN&GIIPwpqCRS-Ijh&5AY6_r}rR_y2 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWEBDOWNLOAD_CPP + +#include + +#include "cwebdownload.h" + +/***************************************************************************/ + +static CWEBDOWNLOAD *get_download(QNetworkReply *reply) +{ + CWEBDOWNLOAD *download = 0; + sscanf(TO_UTF8(reply->objectName()), "gb-download-%p", &download); + return download; +} + +static void set_error(CWEBDOWNLOAD *_object, const char *error) +{ + GB.FreeString(&THIS->error); + if (error) THIS->error = GB.NewZeroString(error); +} + +static void abort_download(CWEBDOWNLOAD *_object, const char *error) +{ + REPLY->abort(); + if (error) + { + set_error(THIS, error); + THIS->status = STATUS_ERROR; + } +} + +/***************************************************************************/ + +static CWEBDOWNLOAD **_downloads = NULL; + +int WEB_get_downloads_count() +{ + if (_downloads) + return GB.Count(_downloads); + else + return 0; +} + +#define get_downloads_count WEB_get_downloads_count + +static int find_download(CWEBDOWNLOAD *download) +{ + int i; + + for (i = 0; i < get_downloads_count(); i++) + { + if (_downloads[i] == download) + return i; + } + + return (-1); +} + +void WEB_remove_download(CWEBDOWNLOAD *_object) +{ + int index; + + abort_download(THIS, NULL); + + index = find_download(THIS); + + if (index >= 0) + { + GB.Unref(POINTER(&_downloads[index])); + GB.Remove(&_downloads, index, 1); + } +} + +/***************************************************************************/ + +BEGIN_PROPERTY(WebDownload_Status) + + GB.ReturnInteger(THIS->status); + +END_PROPERTY + +BEGIN_PROPERTY(WebDownload_Url) + + RETURN_NEW_STRING(REPLY->url().toString()); + +END_PROPERTY + +BEGIN_PROPERTY(WebDownload_Path) + + if (READ_PROPERTY) + GB.ReturnString(THIS->path); + else + GB.StoreString(PROP(GB_STRING), &THIS->path); + +END_PROPERTY + +BEGIN_PROPERTY(WebDownload_Size) + + qulonglong size = 0; + + size = REPLY->header(QNetworkRequest::ContentLengthHeader).toULongLong(); + + GB.ReturnLong(size); + +END_PROPERTY + +BEGIN_PROPERTY(WebDownload_ErrorText) + + GB.ReturnString(THIS->error); + +END_PROPERTY + +BEGIN_PROPERTY(WebDownload_Progress) + + GB.ReturnFloat(THIS->progress); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebDownload_Cancel) + + abort_download(THIS, NULL); + +END_METHOD + +BEGIN_METHOD_VOID(WebDownload_Delete) + + WEB_remove_download(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(WebDownload_free) + + if (THIS->reply) + THIS->reply->abort(); + + delete THIS->output; + THIS->reply->deleteLater(); + set_error(THIS, NULL); + GB.FreeString(&THIS->path); + +END_METHOD + +GB_DESC WebDownloadDesc[] = +{ + GB_DECLARE("WebDownload", sizeof(CWEBDOWNLOAD)), + + GB_METHOD("_free", NULL, WebDownload_free, NULL), + + GB_CONSTANT("Created", "i", STATUS_CREATED), + GB_CONSTANT("Downloading", "i", STATUS_DOWNLOADING), + GB_CONSTANT("Finished", "i", STATUS_FINISHED), + GB_CONSTANT("Cancelled", "i", STATUS_CANCELLED), + GB_CONSTANT("Error", "i", STATUS_ERROR), + + GB_PROPERTY("Path", "s", WebDownload_Path), + GB_PROPERTY_READ("Url", "s", WebDownload_Url), + GB_PROPERTY_READ("Size", "l", WebDownload_Size), + GB_PROPERTY_READ("Progress", "f", WebDownload_Progress), + GB_PROPERTY_READ("Status", "i", WebDownload_Status), + GB_PROPERTY_READ("ErrorText", "s", WebDownload_ErrorText), + + GB_METHOD("Cancel", NULL, WebDownload_Cancel, NULL), + GB_METHOD("Delete", NULL, WebDownload_Delete, NULL), + + GB_END_DECLARE +}; + +/***************************************************************************/ + +BEGIN_PROPERTY(WebDownloads_Count) + + GB.ReturnInteger(get_downloads_count()); + +END_PROPERTY + +BEGIN_METHOD(WebDownloads_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= get_downloads_count()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(_downloads[index]); + +END_METHOD + +BEGIN_METHOD(WebDownloads_Remove, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= get_downloads_count()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + WEB_remove_download(_downloads[index]); + +END_METHOD + +BEGIN_METHOD(WebDownloads_Find, GB_OBJECT download) + + CWEBDOWNLOAD *download = (CWEBDOWNLOAD *)VARG(download); + + GB.ReturnInteger(find_download(download)); + +END_METHOD + +BEGIN_METHOD_VOID(WebDownloads_exit) + + while (get_downloads_count()) + WEB_remove_download(_downloads[0]); + + GB.FreeArray(&_downloads); + +END_METHOD + +GB_DESC WebDownloadsDesc[] = +{ + GB_DECLARE("WebDownloads", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_exit", NULL, WebDownloads_exit, NULL), + GB_STATIC_PROPERTY_READ("Count", "i", WebDownloads_Count), + GB_STATIC_METHOD("_get", "WebDownload", WebDownloads_get, "(Index)i"), + GB_STATIC_METHOD("Remove", NULL, WebDownloads_Remove, "(Index)i"), + GB_STATIC_METHOD("Find", "i", WebDownloads_Find, "(Download)WebDownload"), + + GB_END_DECLARE +}; + + +/***************************************************************************/ + +CWEBDOWNLOAD *WEB_create_download(QNetworkReply *reply) +{ + CWEBDOWNLOAD *_object; + char name[32]; + int index; + + _object = (CWEBDOWNLOAD *)GB.New(GB.FindClass("WebDownload"), NULL, NULL); + THIS->reply = reply; + reply->setParent(0); + sprintf(name, "gb-download-%p", THIS); + reply->setObjectName(name); + + QObject::connect(reply, SIGNAL(readyRead()), &CWebDownload::manager, SLOT(readyRead())); + QObject::connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), &CWebDownload::manager, SLOT(error(QNetworkReply::NetworkError))); + QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)), &CWebDownload::manager, SLOT(downloadProgress(qint64,qint64))); + QObject::connect(reply, SIGNAL(finished()), &CWebDownload::manager, SLOT(finished())); + + index = get_downloads_count(); + + if (!_downloads) + GB.NewArray(&_downloads, sizeof(*_downloads), 1); + else + GB.Add(&_downloads); + + _downloads[index] = THIS; + GB.Ref(THIS); + + return THIS; +} + +/***************************************************************************/ + +CWebDownload CWebDownload::manager; + +#define GET_DOWNLOAD() \ + QNetworkReply *reply = (QNetworkReply *)sender(); \ + CWEBDOWNLOAD *_object = get_download(reply); + +void CWebDownload::readyRead() +{ + GET_DOWNLOAD(); + + if (!THIS->path) + { + //abort_download(THIS, "No file name specified"); + return; + } + + if (!THIS->output) + { + THIS->output = new QFile(TO_QSTRING(THIS->path)); + + if (!THIS->output->open(QIODevice::WriteOnly)) + { + char *error = NULL; + error = GB.AddString(error, "Unable to save file: ", 0); + error = GB.AddString(error, TO_UTF8(THIS->output->errorString()), 0); + abort_download(THIS, error); + return; + } + } + + if (THIS->output->write(reply->readAll()) < 0) + { + abort_download(THIS, TO_UTF8(THIS->output->errorString())); + return; + } + + THIS->status = STATUS_DOWNLOADING; +} + +void CWebDownload::error(QNetworkReply::NetworkError code) +{ + GET_DOWNLOAD(); + + if (code == QNetworkReply::OperationCanceledError) + { + THIS->status = STATUS_CANCELLED; + GB.FreeString(&THIS->error); + } + else + { + THIS->status = STATUS_ERROR; + if (!THIS->error) + THIS->error = GB.NewZeroString(TO_UTF8(reply->errorString())); + } +} + +void CWebDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) +{ + GET_DOWNLOAD(); + + if (bytesTotal < 0) + THIS->progress = 0; + else + THIS->progress = (double)bytesReceived / bytesTotal; +} + +void CWebDownload::finished() +{ + GET_DOWNLOAD(); + + if (THIS->status == STATUS_DOWNLOADING) + { + THIS->output->close(); + THIS->status = STATUS_FINISHED; + } + + THIS->progress = 1; +} diff --git a/gb.qt4/src/webkit/cwebdownload.h b/gb.qt4/src/webkit/cwebdownload.h new file mode 100644 index 00000000..7b89ac55 --- /dev/null +++ b/gb.qt4/src/webkit/cwebdownload.h @@ -0,0 +1,89 @@ +/*************************************************************************** + + cwebdownload.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWEBDOWNLOAD_H +#define __CWEBDOWNLOAD_H + +#include +#include + +#include "main.h" + +#ifndef __CWEBDOWNLOAD_CPP + +extern GB_DESC WebDownloadDesc[]; +extern GB_DESC WebDownloadsDesc[]; + +#else + +#define THIS ((CWEBDOWNLOAD *)_object) +#define REPLY (THIS->reply) + +#endif + +enum { + STATUS_CREATED, + STATUS_DOWNLOADING, + STATUS_ERROR, + STATUS_CANCELLED, + STATUS_FINISHED +}; + +typedef + struct + { + GB_BASE ob; + QNetworkReply *reply; + int status; + char *path; + char *error; + double progress; + QFile *output; + } + CWEBDOWNLOAD; + +class CWebDownload : public QObject +{ + Q_OBJECT + +public: + + static CWebDownload manager; + +public slots: + + void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); + void error(QNetworkReply::NetworkError code); + void finished(); + void readyRead(); + //void metaDataChanged () + //void sslErrors ( const QList & errors ) + //void uploadProgress(qint64 bytesSent, qint64 bytesTotal); +}; + + +CWEBDOWNLOAD *WEB_create_download(QNetworkReply *reply); +int WEB_get_downloads_count(); +void WEB_remove_download(CWEBDOWNLOAD *_object); + +#endif diff --git a/gb.qt4/src/webkit/cwebelement.cpp b/gb.qt4/src/webkit/cwebelement.cpp new file mode 100644 index 00000000..c5378c8b --- /dev/null +++ b/gb.qt4/src/webkit/cwebelement.cpp @@ -0,0 +1,399 @@ +/*************************************************************************** + + cwebelement.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWEBELEMENT_CPP + +#include "cwebframe.h" +#include "cwebelement.h" + +typedef + struct { + GB_BASE ob; + int x, y, w, h; + } + CRECT; + +CWEBELEMENT *CWEBELEMENT_create(const QWebElement &elt) +{ + void *_object; + + if (elt.isNull()) + return NULL; + + _object = GB.New(GB.FindClass("WebElement"), 0, 0); + ELT = new QWebElement(elt); + //qDebug("create WebElement %p / %p (%ld)", THIS, ELT, THIS->ob.ref); + return THIS; +} + +static int check_element(void *_object) +{ + return !ELT || ELT->isNull(); +} + +//--------------------------------------------------------------------------- + +/*BEGIN_METHOD_VOID(WebElement_new) + + ELT = new QWebElement; + +END_METHOD*/ + +BEGIN_METHOD_VOID(WebElement_free) + + //qDebug("WebElement_free: %p / %p", THIS, ELT); + delete ELT; + +END_METHOD + +BEGIN_PROPERTY(WebElement_HTML) + + if (READ_PROPERTY) + RETURN_NEW_STRING(ELT->toOuterXml()); + else + ELT->setOuterXml(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(WebElement_InnerHTML) + + if (READ_PROPERTY) + RETURN_NEW_STRING(ELT->toInnerXml()); + else + ELT->setInnerXml(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(WebElement_Frame) + + GB.ReturnObject(CWEBFRAME_get(ELT->webFrame())); + +END_PROPERTY + +BEGIN_METHOD(WebElement_Eval, GB_STRING javascript) + + QVariant result = ELT->evaluateJavaScript(QSTRING_ARG(javascript)); + MAIN_return_qvariant(result); + +END_METHOD + +BEGIN_PROPERTY(WebElement_Child) + + GB.ReturnObject(CWEBELEMENT_create(ELT->firstChild())); + +END_PROPERTY + +BEGIN_PROPERTY(WebElement_Last) + + GB.ReturnObject(CWEBELEMENT_create(ELT->lastChild())); + +END_PROPERTY + +BEGIN_PROPERTY(WebElement_Next) + + GB.ReturnObject(CWEBELEMENT_create(ELT->nextSibling())); + +END_PROPERTY + +BEGIN_PROPERTY(WebElement_Previous) + + GB.ReturnObject(CWEBELEMENT_create(ELT->previousSibling())); + +END_PROPERTY + +BEGIN_PROPERTY(WebElement_Parent) + + GB.ReturnObject(CWEBELEMENT_create(ELT->parent())); + +END_PROPERTY + +BEGIN_PROPERTY(WebElement_Document) + + GB.ReturnObject(CWEBELEMENT_create(ELT->document())); + +END_PROPERTY + +BEGIN_PROPERTY(WebElement_HasFocus) + + GB.ReturnBoolean(ELT->hasFocus()); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebElement_SetFocus) + + ELT->setFocus(); + +END_METHOD + +BEGIN_METHOD(WebElement_FindFirst, GB_STRING selector) + + GB.ReturnObject(CWEBELEMENT_create(ELT->findFirst(QSTRING_ARG(selector)))); + +END_METHOD + +BEGIN_METHOD(WebElement_FindAll, GB_STRING selector) + + GB_ARRAY array; + int i; + QWebElementCollection result = ELT->findAll(QSTRING_ARG(selector)); + CWEBELEMENT *elt; + + GB.Array.New(&array, GB.FindClass("WebElement"), result.count()); + + for (i = 0; i < result.count(); i++) + { + elt = CWEBELEMENT_create(result.at(i)); + GB.Ref(elt); + *((CWEBELEMENT **)GB.Array.Get(array, i)) = elt; + } + + GB.ReturnObject(array); + +END_METHOD + +BEGIN_PROPERTY(WebElement_Tag) + + RETURN_NEW_STRING(ELT->tagName()); + +END_PROPERTY + +BEGIN_PROPERTY(WebElement_Classes) + + GB_ARRAY array; + int i; + QStringList classes = ELT->classes(); + + GB.Array.New(&array, GB_T_STRING, classes.count()); + for (i = 0; i < classes.count(); i++) + *((const char **)GB.Array.Get(array, i)) = NEW_STRING(classes.at(i)); + + GB.ReturnObject(array); + +END_PROPERTY + +BEGIN_METHOD(WebElement_AddClass, GB_STRING klass) + + ELT->addClass(QSTRING_ARG(klass)); + +END_METHOD + +BEGIN_METHOD(WebElement_RemoveClass, GB_STRING klass) + + ELT->removeClass(QSTRING_ARG(klass)); + +END_METHOD + +BEGIN_METHOD(WebElement_get, GB_STRING attr) + + RETURN_NEW_STRING(ELT->attribute(QSTRING_ARG(attr))); + +END_METHOD + +BEGIN_METHOD(WebElement_put, GB_STRING value; GB_STRING attr) + + ELT->setAttribute(QSTRING_ARG(attr), QSTRING_ARG(value)); + +END_METHOD + +BEGIN_METHOD(WebElement_HasAttribute, GB_STRING attr) + + GB.ReturnBoolean(ELT->hasAttribute(QSTRING_ARG(attr))); + +END_METHOD + +BEGIN_METHOD(WebElement_RemoveAttribute, GB_STRING attr) + + ELT->removeAttribute(QSTRING_ARG(attr)); + +END_METHOD + +BEGIN_PROPERTY(WebElement_Geometry) + + QRect r = ELT->geometry(); + + GB.Push(4, GB_T_INTEGER, r.x(), GB_T_INTEGER, r.y(), GB_T_INTEGER, r.width(), GB_T_INTEGER, r.height()); + GB.ReturnObject(GB.New(GB.FindClass("Rect"), 0, (void *)4)); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebElement_Delete) + + ELT->removeFromDocument(); + +END_METHOD + +BEGIN_METHOD(WebElement_Add, GB_STRING markup) + + ELT->appendInside(QSTRING_ARG(markup)); + +END_METHOD + +BEGIN_METHOD(WebElement_AddFirst, GB_STRING markup) + + ELT->prependInside(QSTRING_ARG(markup)); + +END_METHOD + +BEGIN_METHOD(WebElement_AddBefore, GB_STRING markup) + + ELT->prependOutside(QSTRING_ARG(markup)); + +END_METHOD + +BEGIN_METHOD(WebElement_AddAfter, GB_STRING markup) + + ELT->appendOutside(QSTRING_ARG(markup)); + +END_METHOD + +BEGIN_METHOD(WebElement_Enclose, GB_STRING markup) + + ELT->encloseContentsWith(QSTRING_ARG(markup)); + +END_METHOD + +BEGIN_METHOD(WebElement_Replace, GB_STRING markup) + + ELT->replace(QSTRING_ARG(markup)); + +END_METHOD + +BEGIN_METHOD_VOID(WebElement_Clear) + + ELT->removeAllChildren(); + +END_METHOD + +BEGIN_METHOD(WebElement_Paint, GB_OBJECT clip) + + QPainter *painter = QT.GetCurrentPainter(); + + if (!painter) + return; + + #if QT_VERSION >= 0x040800 + + if (MISSING(clip)) + ELT->render(painter); + else + { + CRECT *rect = (CRECT *)VARG(clip); + QRect clip(rect->x, rect->y, rect->w, rect->h); + ELT->render(painter, clip); + } + + #else + + ELT->render(painter); + + #endif + +END_METHOD + + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(WebElementStyle_get, GB_STRING property) + + RETURN_NEW_STRING(ELT->styleProperty(QSTRING_ARG(property), QWebElement::InlineStyle)); + +END_METHOD + +BEGIN_METHOD(WebElementStyle_put, GB_STRING value; GB_STRING property) + + ELT->setStyleProperty(QSTRING_ARG(property), QSTRING_ARG(value)); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC WebElementStyleDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebElement.Style"), + GB_HOOK_CHECK(check_element), + + GB_METHOD("_get", "s", WebElementStyle_get, "(Property)s"), + GB_METHOD("_put", "s", WebElementStyle_put, "(Value)s(Property)s"), + + GB_END_DECLARE +}; + +GB_DESC WebElementDesc[] = +{ + GB_DECLARE("WebElement", sizeof(CWEBELEMENT)), + GB_HOOK_CHECK(check_element), GB_NOT_CREATABLE(), + + //GB_METHOD("_new", NULL, WebElement_new, NULL), + GB_METHOD("_free", NULL, WebElement_free, NULL), + + GB_PROPERTY("HTML", "s", WebElement_HTML), + GB_PROPERTY("InnerHTML", "s", WebElement_InnerHTML), + GB_PROPERTY_READ("Frame", "WebFrame", WebElement_Frame), + + GB_PROPERTY_READ("Tag", "s", WebElement_Tag), + + GB_PROPERTY_READ("Classes", "String[]", WebElement_Classes), + GB_METHOD("AddClass", NULL, WebElement_AddClass, "(Class)s"), + GB_METHOD("RemoveClass", NULL, WebElement_RemoveClass, "(Class)s"), + + GB_PROPERTY_READ("Child", "WebElement", WebElement_Child), + GB_PROPERTY_READ("First", "WebElement", WebElement_Child), + GB_PROPERTY_READ("Last", "WebElement", WebElement_Last), + GB_PROPERTY_READ("Next", "WebElement", WebElement_Next), + GB_PROPERTY_READ("Previous", "WebElement", WebElement_Previous), + GB_PROPERTY_READ("Parent", "WebElement", WebElement_Parent), + GB_PROPERTY_READ("Document", "WebElement", WebElement_Document), + + GB_PROPERTY_READ("HasFocus", "b", WebElement_HasFocus), + GB_METHOD("SetFocus", NULL, WebElement_SetFocus, NULL), + + GB_METHOD("Eval", "v", WebElement_Eval, "(JavaScript)s"), + + GB_METHOD("FindFirst", "WebElement", WebElement_FindFirst, "(Selector)s"), + GB_METHOD("FindAll", "WebElement[]", WebElement_FindAll, "(Selector)s"), + + GB_METHOD("_get", "s", WebElement_get, "(Attribute)s"), + GB_METHOD("_put", "s", WebElement_put, "(Value)s(Attribute)s"), + GB_METHOD("HasAttribute", "b", WebElement_HasAttribute, "(Attribute)s"), + GB_METHOD("RemoveAttribute", NULL, WebElement_RemoveAttribute, "(Attribute)s"), + + GB_PROPERTY_READ("Geometry", "Rect", WebElement_Geometry), + + GB_METHOD("Delete", NULL, WebElement_Delete, NULL), + GB_METHOD("Clear", NULL, WebElement_Clear, NULL), + + GB_METHOD("Add", NULL, WebElement_Add, "(Markup)s"), + GB_METHOD("AddFirst", NULL, WebElement_AddFirst, "(Markup)s"), + GB_METHOD("AddBefore", NULL, WebElement_AddBefore, "(Markup)s"), + GB_METHOD("AddAfter", NULL, WebElement_AddAfter, "(Markup)s"), + GB_METHOD("Enclose", NULL, WebElement_Enclose, "(Markup)s"), + GB_METHOD("Replace", NULL, WebElement_Replace, "(Markup)s"), + + GB_METHOD("Paint", NULL, WebElement_Paint, "[(Clip)Rect;]"), + + GB_PROPERTY_SELF("Style", ".WebElement.Style"), + + GB_END_DECLARE +}; + + diff --git a/gb.qt4/src/webkit/cwebelement.h b/gb.qt4/src/webkit/cwebelement.h new file mode 100644 index 00000000..5f0ee5b9 --- /dev/null +++ b/gb.qt4/src/webkit/cwebelement.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + cwebelement.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWEBELEMENT_H +#define __CWEBELEMENT_H + +#include "main.h" +#include + +#ifndef __CWEBELEMENT_CPP + +extern GB_DESC WebElementStyleDesc[]; +extern GB_DESC WebElementDesc[]; + +#else + +#define THIS ((CWEBELEMENT *)_object) +#define ELT THIS->elt + +#endif + +typedef + struct + { + GB_BASE ob; + QWebElement *elt; + } + CWEBELEMENT; + +CWEBELEMENT *CWEBELEMENT_create(const QWebElement &elt); + +#endif diff --git a/gb.qt4/src/webkit/cwebframe.cpp b/gb.qt4/src/webkit/cwebframe.cpp new file mode 100644 index 00000000..64e1a208 --- /dev/null +++ b/gb.qt4/src/webkit/cwebframe.cpp @@ -0,0 +1,177 @@ +/*************************************************************************** + + cwebframe.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWEBFRAME_CPP + +#include +#include +#include +#include +#include + +#include "cwebelement.h" +#include "cwebframe.h" +#include "../cprinter.h" + +CWEBFRAME *CWEBFRAME_get(QWebFrame *frame) +{ + void *_object; + + if (!frame) return 0; + + _object = QT.GetLink(frame); + + if (!_object) + { + _object = GB.New(GB.FindClass("WebFrame"), 0, 0); + //qDebug("create WebFrame %p", _object); + QT.Link(frame, _object); + THIS->frame = frame; + } + + return (CWEBFRAME *)_object; +} + +void CWEBFRAME_eval(QWebFrame *frame, const QString &javascript) +{ + QVariant result = frame->evaluateJavaScript(javascript); + MAIN_return_qvariant(result); +} + +BEGIN_METHOD_VOID(WebFrame_free) + + //qDebug("WebFrame_free: %p", THIS); + +END_METHOD + +BEGIN_PROPERTY(WebFrame_Name) + + RETURN_NEW_STRING(FRAME->frameName()); + +END_PROPERTY + +BEGIN_PROPERTY(WebFrame_Parent) + + GB.ReturnObject(CWEBFRAME_get(FRAME->parentFrame())); + +END_PROPERTY + +BEGIN_PROPERTY(WebFrame_Url) + + if (READ_PROPERTY) + RETURN_NEW_STRING(FRAME->url().toString()); + else + FRAME->setUrl(QUrl(QSTRING_PROP())); + +END_PROPERTY + + +BEGIN_PROPERTY(WebFrameChildren_Count) + + GB.ReturnInteger(FRAME->childFrames().count()); + +END_PROPERTY + +BEGIN_METHOD(WebFrameChildren_get, GB_INTEGER index) + + int index = VARG(index); + QList children = FRAME->childFrames(); + + if (index < 0 || index >= children.count()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(CWEBFRAME_get(children.at(index))); + +END_METHOD + +BEGIN_METHOD(WebFrame_Print, GB_OBJECT printer) + + CPRINTER *printer = (CPRINTER *)VARG(printer); + + if (GB.CheckObject(printer)) + return; + + FRAME->print(printer->printer); + +END_METHOD + +BEGIN_PROPERTY(WebFrame_HTML) + + RETURN_NEW_STRING(FRAME->toHtml()); + +END_PROPERTY + +BEGIN_PROPERTY(WebFrame_Text) + + RETURN_NEW_STRING(FRAME->toPlainText()); + +END_PROPERTY + +BEGIN_METHOD(WebFrame_EvalJavaScript, GB_STRING javascript) + + CWEBFRAME_eval(FRAME, QSTRING_ARG(javascript)); + +END_METHOD + +BEGIN_PROPERTY(WebFrame_Document) + + GB.ReturnObject(CWEBELEMENT_create(FRAME->documentElement())); + +END_PROPERTY + +//--------------------------------------------------------------------------- + +GB_DESC WebFrameChildrenDesc[] = +{ + GB_DECLARE(".WebFrame.Children", sizeof(CWEBFRAME)), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Count", "i", WebFrameChildren_Count), + GB_METHOD("_get", "WebFrame", WebFrameChildren_get, "(Index)i"), + + GB_END_DECLARE +}; + +GB_DESC WebFrameDesc[] = +{ + GB_DECLARE("WebFrame", sizeof(CWEBFRAME)), + + GB_METHOD("_free", NULL, WebFrame_free, NULL), + + GB_PROPERTY_READ("Name", "s", WebFrame_Name), + GB_PROPERTY_SELF("Children", ".WebFrame.Children"), + GB_PROPERTY_READ("Parent", "WebFrame", WebFrame_Parent), + GB_PROPERTY("Url", "s", WebFrame_Url), + GB_METHOD("Print", NULL, WebFrame_Print, "(Printer)Printer;"), + GB_PROPERTY_READ("HTML", "s", WebFrame_HTML), + GB_PROPERTY_READ("Text", "s", WebFrame_Text), + GB_METHOD("Eval", "v", WebFrame_EvalJavaScript, "(JavaScript)s"), + GB_PROPERTY_READ("Document", "WebElement", WebFrame_Document), + + GB_END_DECLARE +}; + + + diff --git a/gb.qt4/src/webkit/cwebframe.h b/gb.qt4/src/webkit/cwebframe.h new file mode 100644 index 00000000..46ff7f8a --- /dev/null +++ b/gb.qt4/src/webkit/cwebframe.h @@ -0,0 +1,56 @@ +/*************************************************************************** + + cwebframe.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWEBFRAME_H +#define __CWEBFRAME_H + +#include "main.h" + +#include +#include +#include + +#ifndef __CWEBFRAME_CPP + +extern GB_DESC WebFrameChildrenDesc[]; +extern GB_DESC WebFrameDesc[]; + +#else + +#define THIS ((CWEBFRAME *)_object) +#define FRAME (THIS->frame) + +#endif + +typedef + struct + { + GB_BASE ob; + QWebFrame *frame; + } + CWEBFRAME; + +CWEBFRAME *CWEBFRAME_get(QWebFrame *frame); +void CWEBFRAME_eval(QWebFrame *frame, const QString &javascript); + +#endif diff --git a/gb.qt4/src/webkit/cwebhittest.cpp b/gb.qt4/src/webkit/cwebhittest.cpp new file mode 100644 index 00000000..4a721da9 --- /dev/null +++ b/gb.qt4/src/webkit/cwebhittest.cpp @@ -0,0 +1,121 @@ +/*************************************************************************** + + cwebhittest.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWEBHITTEST_CPP + +#include "cwebelement.h" +#include "cwebhittest.h" + +BEGIN_PROPERTY(WebHitTest_Document) + + GB.ReturnBoolean(RESULT->linkUrl().isEmpty() && RESULT->imageUrl().isEmpty()); + +END_PROPERTY + +BEGIN_PROPERTY(WebHitTest_Link) + + GB.ReturnBoolean(!RESULT->linkUrl().isEmpty()); + +END_PROPERTY + +BEGIN_PROPERTY(WebHitTest_Image) + + GB.ReturnBoolean(!RESULT->imageUrl().isEmpty()); + +END_PROPERTY + +BEGIN_PROPERTY(WebHitTest_Selected) + + GB.ReturnBoolean(RESULT->isContentSelected()); + +END_PROPERTY + +BEGIN_PROPERTY(WebHitTest_Editable) + + GB.ReturnBoolean(RESULT->isContentEditable()); + +END_PROPERTY + +BEGIN_PROPERTY(WebHitTest_Url) + + QUrl url; + + url = RESULT->linkUrl(); + if (url.isEmpty()) + url = RESULT->imageUrl(); + + RETURN_NEW_STRING(url.toString()); + +END_PROPERTY + +BEGIN_PROPERTY(WebHitTest_Html) + + QWebElement element; + element = RESULT->element(); + + RETURN_NEW_STRING(element.toOuterXml()); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebHitTest_free) + + delete RESULT; + +END_METHOD + +BEGIN_PROPERTY(WebHitTest_Element) + + GB.ReturnObject(CWEBELEMENT_create(RESULT->element())); + +END_PROPERTY + +GB_DESC WebHitTestDesc[] = +{ + GB_DECLARE("WebHitTest", sizeof(CWEBHITTEST)), GB_NOT_CREATABLE(), + + //GB_METHOD("_new", NULL, WebHitTest_new, NULL), + GB_METHOD("_free", NULL, WebHitTest_free, NULL), + + GB_PROPERTY_READ("Document", "b", WebHitTest_Document), + GB_PROPERTY_READ("Link", "b", WebHitTest_Link), + GB_PROPERTY_READ("Image", "b", WebHitTest_Image), + //GB_PROPERTY_READ("Media", "b", WebHitTest_Media), + GB_PROPERTY_READ("Selected", "b", WebHitTest_Selected), + GB_PROPERTY_READ("Editable", "b", WebHitTest_Editable), + GB_PROPERTY_READ("Url", "s", WebHitTest_Url), + GB_PROPERTY_READ("HTML", "s", WebHitTest_Html), + GB_PROPERTY_READ("Element", "WebElement", WebHitTest_Element), + + GB_END_DECLARE +}; + +/***************************************************************************/ + +CWEBHITTEST *WEB_create_hit_test(const QWebHitTestResult &result) +{ + CWEBHITTEST *_object = (CWEBHITTEST *)GB.New(GB.FindClass("WebHitTest"), NULL, NULL); + RESULT = new QWebHitTestResult; + *RESULT = result; + + return THIS; +} diff --git a/gb.qt4/src/webkit/cwebhittest.h b/gb.qt4/src/webkit/cwebhittest.h new file mode 100644 index 00000000..90f13fb2 --- /dev/null +++ b/gb.qt4/src/webkit/cwebhittest.h @@ -0,0 +1,54 @@ +/*************************************************************************** + + cwebhittest.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWEBHITTEST_H +#define __CWEBHITTEST_H + +#include "main.h" + +#include +#include +#include + +#ifndef __CWEBHITTEST_CPP + +extern GB_DESC WebHitTestDesc[]; + +#else + +#define THIS ((CWEBHITTEST *)_object) +#define RESULT (THIS->result) + +#endif + +typedef + struct + { + GB_BASE ob; + QWebHitTestResult *result; + } + CWEBHITTEST; + +CWEBHITTEST *WEB_create_hit_test(const QWebHitTestResult &result); + +#endif diff --git a/gb.qt4/src/webkit/cwebsettings.cpp b/gb.qt4/src/webkit/cwebsettings.cpp new file mode 100644 index 00000000..84bd83df --- /dev/null +++ b/gb.qt4/src/webkit/cwebsettings.cpp @@ -0,0 +1,489 @@ +/*************************************************************************** + + cwebsettings.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWEBSETTINGS_CPP + +#include +#include + +#include +#include + +#include "cwebview.h" +#include "cwebsettings.h" + +//static QNetworkDiskCache *_cache = 0; +static bool _cache_enabled = false; +static char *_cache_path = 0; + +static void set_cache(bool on) +{ + QNetworkDiskCache *cache; + + if (!_cache_path) + return; + + _cache_enabled = on; + + if (on) + { + cache = new QNetworkDiskCache(0); + cache->setCacheDirectory(TO_QSTRING(_cache_path)); + WEBVIEW_get_network_manager()->setCache(cache); + } + else + WEBVIEW_get_network_manager()->setCache(0); +} + + +static QWebSettings *get_settings(void *_object) +{ + if (!_object) + return QWebSettings::globalSettings(); + else + return WEBVIEW->settings(); +} + +/***************************************************************************/ + +static void handle_font_family(QWebSettings::FontFamily font, void *_object, void *_param) +{ + if (READ_PROPERTY) + RETURN_NEW_STRING(get_settings(_object)->fontFamily(font)); + else + get_settings(_object)->setFontFamily(font, QSTRING_PROP()); +} + +BEGIN_PROPERTY(WebSettingsFonts_CursiveFont) + + handle_font_family(QWebSettings::CursiveFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_FantasyFont) + + handle_font_family(QWebSettings::FantasyFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_FixedFont) + + handle_font_family(QWebSettings::FixedFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_SansSerifFont) + + handle_font_family(QWebSettings::SansSerifFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_SerifFont) + + handle_font_family(QWebSettings::SerifFont, _object, _param); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsFonts_StandardFont) + + handle_font_family(QWebSettings::StandardFont, _object, _param); + +END_METHOD + +static void handle_font_size(QWebSettings::FontSize size, void *_object, void *_param) +{ + if (READ_PROPERTY) + GB.ReturnInteger(get_settings(_object)->fontSize(size) - 3); + else + get_settings(_object)->setFontSize(size, VPROP(GB_INTEGER) + 3); +} + +BEGIN_PROPERTY(WebSettingsFonts_DefaultFixedFontSize) + + handle_font_size(QWebSettings::DefaultFixedFontSize, _object, _param); + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsFonts_DefaultFontSize) + + handle_font_size(QWebSettings::DefaultFontSize, _object, _param); + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsFonts_MinimumFontSize) + + handle_font_size(QWebSettings::MinimumFontSize, _object, _param); + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsFonts_MinimumLogicalFontSize) + + handle_font_size(QWebSettings::MinimumLogicalFontSize, _object, _param); + +END_PROPERTY + + +/***************************************************************************/ + +BEGIN_METHOD_VOID(WebSettingsIconDatabase_Clear) + + QWebSettings::clearIconDatabase(); + +END_METHOD + +BEGIN_PROPERTY(WebSettingsIconDatabase_Path) + + if (READ_PROPERTY) + RETURN_NEW_STRING(QWebSettings::iconDatabasePath()); + else + QWebSettings::setIconDatabasePath(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_METHOD(WebSettingsIconDatabase_get, GB_STRING url) + + QIcon icon; + QSize size; + QSize max_size; + + icon = QWebSettings::iconForUrl(QSTRING_ARG(url)); + if (icon.isNull()) + { + GB.ReturnNull(); + return; + } + + foreach(size, icon.availableSizes()) + { + if ((size.width() * size.height()) > (max_size.width() * max_size.height())) + max_size = size; + } + + GB.ReturnObject(QT.CreatePicture(icon.pixmap(max_size))); + +END_METHOD + + +/***************************************************************************/ + +BEGIN_PROPERTY(WebSettingsCache_Path) + + if (READ_PROPERTY) + GB.ReturnString(_cache_path); + else + { + char *path = GB.FileName(PSTRING(), PLENGTH()); + QString qpath = QString(path); + QString root = QString(GB.System.Home()); + + if (root.at(root.length() - 1) != '/') + root += '/'; + root += ".cache/"; + if (!qpath.startsWith(root)) + { + GB.Error("Cache directory must be located inside ~/.cache"); + return; + } + + GB.FreeString(&_cache_path); + _cache_path = GB.NewZeroString(path); + set_cache(_cache_enabled); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsCache_Enabled) + + if (READ_PROPERTY) + GB.ReturnBoolean(_cache_enabled); + else + set_cache(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +static int _clear_error; +static char *_clear_path; + +static void remove_file(const char *path) +{ + if (rmdir(path) == 0) + return; + + if (errno == ENOTDIR) + { + if (unlink(path) == 0) + return; + } + + if (_clear_error == 0) + { + _clear_error = errno; + _clear_path = GB.NewZeroString(path); + } +} + +BEGIN_METHOD_VOID(WebSettingsCache_Clear) + + if (!_cache_path || !*_cache_path) + return; + + _clear_error = 0; + GB.BrowseDirectory(_cache_path, NULL, remove_file); + + if (_clear_error) + { + GB.Error("Unable to remove '&1': &2", _clear_path, strerror(_clear_error)); + GB.FreeString(&_clear_path); + } + + set_cache(_cache_enabled); + +END_METHOD + + +/***************************************************************************/ + +BEGIN_METHOD(WebSettings_get, GB_INTEGER flag) + + QWebSettings *settings = get_settings(_object); + QWebSettings::WebAttribute flag(QWebSettings::WebAttribute(VARG(flag))); + + GB.ReturnBoolean(settings->testAttribute(flag)); + +END_METHOD + +BEGIN_METHOD(WebSettings_put, GB_BOOLEAN value; GB_INTEGER flag) + + QWebSettings *settings = get_settings(_object); + QWebSettings::WebAttribute flag(QWebSettings::WebAttribute(VARG(flag))); + + settings->setAttribute(flag, VARG(value)); + +END_METHOD + +BEGIN_METHOD(WebSettings_Reset, GB_INTEGER flag) + + QWebSettings *settings = get_settings(_object); + QWebSettings::WebAttribute flag(QWebSettings::WebAttribute(VARG(flag))); + + settings->resetAttribute(flag); + +END_METHOD + + +/***************************************************************************/ + +BEGIN_METHOD_VOID(WebSettings_exit) + + GB.FreeString(&_cache_path); + +END_METHOD + +/***************************************************************************/ + +BEGIN_PROPERTY(WebSettingsProxy_Host) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + RETURN_NEW_STRING(proxy.hostName()); + else + { + proxy.setHostName(QSTRING_PROP()); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_User) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + RETURN_NEW_STRING(proxy.user()); + else + { + proxy.setUser(QSTRING_PROP()); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_Password) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + RETURN_NEW_STRING(proxy.password()); + else + { + proxy.setPassword(QSTRING_PROP()); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_Port) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + GB.ReturnInteger(proxy.port()); + else + { + proxy.setPort(VPROP(GB_INTEGER)); + nam->setProxy(proxy); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebSettingsProxy_Type) + + QNetworkAccessManager *nam = WEBVIEW_get_network_manager(); + QNetworkProxy proxy = nam->proxy(); + + if (READ_PROPERTY) + GB.ReturnInteger(proxy.type()); + else + { + int type = VPROP(GB_INTEGER); + if (type == QNetworkProxy::DefaultProxy || type == QNetworkProxy::NoProxy || type == QNetworkProxy::Socks5Proxy || type == QNetworkProxy::HttpProxy) + { + proxy.setType((QNetworkProxy::ProxyType)type); + nam->setProxy(proxy); + } + } + +END_PROPERTY + +/***************************************************************************/ + +GB_DESC WebViewSettingsDesc[] = +{ + GB_DECLARE(".WebView.Settings", 0), + + GB_METHOD("_get", "b", WebSettings_get, "(Flag)i"), + GB_METHOD("_put", NULL, WebSettings_put, "(Value)b(Flag)i"), + GB_METHOD("Reset", NULL, WebSettings_Reset, "(Flag)i"), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsFontsDesc[] = +{ + GB_DECLARE(".WebSettings.Fonts", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("StandardFont", "s", WebSettingsFonts_StandardFont), + GB_STATIC_PROPERTY("FixedFont", "s", WebSettingsFonts_FixedFont), + GB_STATIC_PROPERTY("SerifFont", "s", WebSettingsFonts_SerifFont), + GB_STATIC_PROPERTY("SansSerifFont", "s", WebSettingsFonts_SansSerifFont), + GB_STATIC_PROPERTY("CursiveFont", "s", WebSettingsFonts_CursiveFont), + GB_STATIC_PROPERTY("FantasyFont", "s", WebSettingsFonts_FantasyFont), + + GB_STATIC_PROPERTY("MinimumFontSize", "i", WebSettingsFonts_MinimumFontSize), + GB_STATIC_PROPERTY("MinimumLogicalFontSize", "i", WebSettingsFonts_MinimumLogicalFontSize), + GB_STATIC_PROPERTY("DefaultFontSize", "i", WebSettingsFonts_DefaultFontSize), + GB_STATIC_PROPERTY("DefaultFixedFontSize", "i", WebSettingsFonts_DefaultFixedFontSize), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsIconDatabaseDesc[] = +{ + GB_DECLARE(".WebSettings.IconDatabase", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("Clear", NULL, WebSettingsIconDatabase_Clear, NULL), + GB_STATIC_PROPERTY("Path", "s", WebSettingsIconDatabase_Path), + GB_STATIC_METHOD("_get", "Picture", WebSettingsIconDatabase_get, "(Url)s"), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsCacheDesc[] = +{ + GB_DECLARE(".WebSettings.Cache", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("Enabled", "b", WebSettingsCache_Enabled), + GB_STATIC_PROPERTY("Path", "s", WebSettingsCache_Path), + GB_STATIC_METHOD("Clear", NULL, WebSettingsCache_Clear, NULL), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsProxyDesc[] = +{ + GB_DECLARE(".WebSettings.Proxy", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("Type", "i", WebSettingsProxy_Type), + GB_STATIC_PROPERTY("Host", "s", WebSettingsProxy_Host), + GB_STATIC_PROPERTY("Port", "i", WebSettingsProxy_Port), + GB_STATIC_PROPERTY("User", "s", WebSettingsProxy_User), + GB_STATIC_PROPERTY("Password", "s", WebSettingsProxy_Password), + + GB_END_DECLARE +}; + +GB_DESC WebSettingsDesc[] = +{ + GB_DECLARE("WebSettings", 0), + + GB_CONSTANT("AutoLoadImages", "i", QWebSettings::AutoLoadImages), + GB_CONSTANT("JavascriptEnabled", "i", QWebSettings::JavascriptEnabled), + GB_CONSTANT("JavaEnabled", "i", QWebSettings::JavaEnabled), + GB_CONSTANT("PluginsEnabled", "i", QWebSettings::PluginsEnabled), + GB_CONSTANT("PrivateBrowsingEnabled", "i", QWebSettings::PrivateBrowsingEnabled), + GB_CONSTANT("JavascriptCanOpenWindows", "i", QWebSettings::JavascriptCanOpenWindows), + GB_CONSTANT("JavascriptCanAccessClipboard", "i", QWebSettings::JavascriptCanAccessClipboard), + GB_CONSTANT("DeveloperExtrasEnabled", "i", QWebSettings::DeveloperExtrasEnabled), + GB_CONSTANT("LinksIncludedInFocusChain", "i", QWebSettings::LinksIncludedInFocusChain), + GB_CONSTANT("ZoomTextOnly", "i", QWebSettings::ZoomTextOnly), + GB_CONSTANT("PrintElementBackgrounds", "i", QWebSettings::PrintElementBackgrounds), + GB_CONSTANT("OfflineStorageDatabaseEnabled", "i", QWebSettings::OfflineStorageDatabaseEnabled), + GB_CONSTANT("OfflineWebApplicationCacheEnabled", "i", QWebSettings::OfflineWebApplicationCacheEnabled), + GB_CONSTANT("LocalStorageDatabaseEnabled", "i", QWebSettings::LocalStorageDatabaseEnabled), + + GB_CONSTANT("DefaultProxy", "i", QNetworkProxy::DefaultProxy), + GB_CONSTANT("NoProxy", "i", QNetworkProxy::NoProxy), + GB_CONSTANT("Socks5Proxy", "i", QNetworkProxy::Socks5Proxy), + GB_CONSTANT("HttpProxy", "i", QNetworkProxy::HttpProxy), + + GB_STATIC_PROPERTY_SELF("Fonts", ".WebSettings.Fonts"), + GB_STATIC_PROPERTY_SELF("IconDatabase", ".WebSettings.IconDatabase"), + GB_STATIC_PROPERTY_SELF("Cache", ".WebSettings.Cache"), + GB_STATIC_PROPERTY_SELF("Proxy", ".WebSettings.Proxy"), + + GB_STATIC_METHOD("_get", "b", WebSettings_get, "(Flag)i"), + GB_STATIC_METHOD("_put", NULL, WebSettings_put, "(Value)b(Flag)i"), + + GB_STATIC_METHOD("_exit", NULL, WebSettings_exit, NULL), + +GB_END_DECLARE +}; + +/***************************************************************************/ + + diff --git a/gb.qt4/src/webkit/cwebsettings.h b/gb.qt4/src/webkit/cwebsettings.h new file mode 100644 index 00000000..b666653f --- /dev/null +++ b/gb.qt4/src/webkit/cwebsettings.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + cwebsettings.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWEBSETTINGS_H +#define __CWEBSETTINGS_H + +#include "main.h" + +#include +#include +#include + +#ifndef __CWEBSETTINGS_CPP + +extern GB_DESC WebViewSettingsDesc[]; +extern GB_DESC WebSettingsFontsDesc[]; +extern GB_DESC WebSettingsIconDatabaseDesc[]; +extern GB_DESC WebSettingsCacheDesc[]; +extern GB_DESC WebSettingsProxyDesc[]; +extern GB_DESC WebSettingsDesc[]; + +#else + +#define WEBVIEW ((QWebView *)((QT_WIDGET *)_object)->widget) + +#endif + +//void WEBSETTINGS_set_cache(QWebView *view, bool on); + +#endif diff --git a/gb.qt4/src/webkit/cwebview.cpp b/gb.qt4/src/webkit/cwebview.cpp new file mode 100644 index 00000000..10cfdf3f --- /dev/null +++ b/gb.qt4/src/webkit/cwebview.cpp @@ -0,0 +1,943 @@ +/*************************************************************************** + + cwebview.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWEBVIEW_CPP + +#include +#include +#include +#include +#include +#include + +#include "ccookiejar.h" +#include "cwebsettings.h" +#include "cwebelement.h" +#include "cwebframe.h" +#include "cwebhittest.h" +#include "cwebview.h" + +/*typedef + struct { + const char *name; + QWebPage::WebAction action; + } + WEBVIEW_ACTION;*/ + +DECLARE_EVENT(EVENT_CLICK); +DECLARE_EVENT(EVENT_LINK); +DECLARE_EVENT(EVENT_PROGRESS); +DECLARE_EVENT(EVENT_LOAD); +DECLARE_EVENT(EVENT_ERROR); +DECLARE_EVENT(EVENT_ICON); +DECLARE_EVENT(EVENT_TITLE); +DECLARE_EVENT(EVENT_SELECT); +DECLARE_EVENT(EVENT_STATUS); +DECLARE_EVENT(EVENT_NEW_WINDOW); +DECLARE_EVENT(EVENT_NEW_FRAME); +DECLARE_EVENT(EVENT_AUTH); +DECLARE_EVENT(EVENT_DOWNLOAD); + +#define HISTORY (WIDGET->history()) + + +static QNetworkAccessManager *_network_access_manager = 0; +static CWEBVIEW *_network_access_manager_view = 0; +static QT_COLOR_FUNC _old_after_set_color; +static bool _ignore_png_warnings = false; + +/* +static WEBVIEW_ACTION _actions[] = +{ + { "OpenLink", QWebPage::OpenLink }, + { "OpenLinkInNewWindow", QWebPage::OpenLinkInNewWindow }, + { "OpenFrameInNewWindow", QWebPage::OpenFrameInNewWindow }, + { "DownloadLinkToDisk", QWebPage::DownloadLinkToDisk }, + { "CopyLinkToClipboard", QWebPage::CopyLinkToClipboard }, + { "OpenImageInNewWindow", QWebPage::OpenImageInNewWindow }, + { "DownloadImageToDisk", QWebPage::DownloadImageToDisk }, + { "CopyImageToClipboard", QWebPage::CopyImageToClipboard }, + { "Back", QWebPage::Back }, + { "Forward", QWebPage::Forward }, + { "Stop", QWebPage::Stop }, + { "StopScheduledPageRefresh", QWebPage::StopScheduledPageRefresh }, + { "Reload", QWebPage::Reload }, + { "ReloadAndBypassCache", QWebPage::ReloadAndBypassCache }, + { "Cut", QWebPage::Cut }, + { "Copy", QWebPage::Copy }, + { "Paste", QWebPage::Paste }, + { "Undo", QWebPage::Undo }, + { "Redo", QWebPage::Redo }, + { "MoveToNextChar", QWebPage::MoveToNextChar }, + { "MoveToPreviousChar", QWebPage::MoveToPreviousChar }, + { "MoveToNextWord", QWebPage::MoveToNextWord }, + { "MoveToPreviousWord", QWebPage::MoveToPreviousWord }, + { "MoveToNextLine", QWebPage::MoveToNextLine }, + { "MoveToPreviousLine", QWebPage::MoveToPreviousLine }, + { "MoveToStartOfLine", QWebPage::MoveToStartOfLine }, + { "MoveToEndOfLine", QWebPage::MoveToEndOfLine }, + { "MoveToStartOfBlock", QWebPage::MoveToStartOfBlock }, + { "MoveToEndOfBlock", QWebPage::MoveToEndOfBlock }, + { "MoveToStartOfDocument", QWebPage::MoveToStartOfDocument }, + { "MoveToEndOfDocument", QWebPage::MoveToEndOfDocument }, + { "SelectNextChar", QWebPage::SelectNextChar }, + { "SelectPreviousChar", QWebPage::SelectPreviousChar }, + { "SelectNextWord", QWebPage::SelectNextWord }, + { "SelectPreviousWord", QWebPage::SelectPreviousWord }, + { "SelectNextLine", QWebPage::SelectNextLine }, + { "SelectPreviousLine", QWebPage::SelectPreviousLine }, + { "SelectStartOfLine", QWebPage::SelectStartOfLine }, + { "SelectEndOfLine", QWebPage::SelectEndOfLine }, + { "SelectStartOfBlock", QWebPage::SelectStartOfBlock }, + { "SelectEndOfBlock", QWebPage::SelectEndOfBlock }, + { "SelectStartOfDocument", QWebPage::SelectStartOfDocument }, + { "SelectEndOfDocument", QWebPage::SelectEndOfDocument }, + { "DeleteStartOfWord", QWebPage::DeleteStartOfWord }, + { "DeleteEndOfWord", QWebPage::DeleteEndOfWord }, + { "SetTextDirectionDefault", QWebPage::SetTextDirectionDefault }, + { "SetTextDirectionLeftToRight", QWebPage::SetTextDirectionLeftToRight }, + { "SetTextDirectionRightToLeft", QWebPage::SetTextDirectionRightToLeft }, + { "ToggleBold", QWebPage::ToggleBold }, + { "ToggleItalic", QWebPage::ToggleItalic }, + { "ToggleUnderline", QWebPage::ToggleUnderline }, + { "InspectElement", QWebPage::InspectElement }, + { "InsertParagraphSeparator", QWebPage::InsertParagraphSeparator }, + { "InsertLineSeparator", QWebPage::InsertLineSeparator }, + { "SelectAll", QWebPage::SelectAll }, + { "PasteAndMatchStyle", QWebPage::PasteAndMatchStyle }, + { "RemoveFormat", QWebPage::RemoveFormat }, + { "ToggleStrikethrough", QWebPage::ToggleStrikethrough }, + { "ToggleSubscript", QWebPage::ToggleSubscript }, + { "ToggleSuperscript", QWebPage::ToggleSuperscript }, + { "InsertUnorderedList", QWebPage::InsertUnorderedList }, + { "InsertOrderedList", QWebPage::InsertOrderedList }, + { "Indent", QWebPage::Indent }, + { "Unindent", QWebPage::Outdent }, + { "AlignCenter", QWebPage::AlignCenter }, + { "AlignJustified", QWebPage::AlignJustified }, + { "AlignLeft", QWebPage::AlignLeft }, + { "AlignRight", QWebPage::AlignRight }, + { NULL, QWebPage::NoWebAction } +}; +*/ + +QNetworkAccessManager *WEBVIEW_get_network_manager() +{ + if (!_network_access_manager) + { + _network_access_manager = new QNetworkAccessManager(); + _network_access_manager->setCookieJar(new MyCookieJar); + } + + return _network_access_manager; +} + +/* +static QWebPage::WebAction get_action(const char *name) +{ + WEBVIEW_ACTION *p; + + for (p = _actions; p->name; p++) + { + if (!strcasecmp(p->name, name)) + return p->action; + } + + return QWebPage::NoWebAction; +} +*/ + +static void after_set_color(void *_object) +{ + if (!GB.Is(THIS, CLASS_WebView)) + { + if (_old_after_set_color) + (*_old_after_set_color)(THIS); + return; + } + + if (QT.GetBackgroundColor(THIS) == GB_COLOR_DEFAULT) + { + QPalette palette = WIDGET->palette(); + WIDGET->page()->setPalette(palette); + WIDGET->setAttribute(Qt::WA_OpaquePaintEvent, true); + } + else + { + qDebug("after_set_color"); + QPalette palette = WIDGET->palette(); + palette.setBrush(QPalette::Base, Qt::transparent); + WIDGET->page()->setPalette(palette); + WIDGET->setAttribute(Qt::WA_OpaquePaintEvent, false); + } +} + +static void stop_view(void *_object) +{ + //fprintf(stderr, "stop_view\n"); + THIS->stopping = TRUE; + WIDGET->stop(); + THIS->stopping = FALSE; +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(WebView_new, GB_OBJECT parent) + + int fd_save = -1; + + if (!_ignore_png_warnings) + { + int fd = ::open("/dev/null", O_RDWR); + fd_save = ::dup(2); + ::dup2(fd, 2); + ::close(fd); + } + + MyWebView *wid = new MyWebView(QT.GetContainer(VARG(parent))); + + if (!_ignore_png_warnings) + { + ::dup2(fd_save, 2); + ::close(fd_save); + _ignore_png_warnings = true; + } + + QT.InitWidget(wid, _object, false); + QT.SetWheelFlag(_object); + + WEBVIEW_get_network_manager(); + + wid->page()->setNetworkAccessManager(_network_access_manager); + wid->page()->setForwardUnsupportedContent(true); + + //QObject::connect(wid, SIGNAL(linkClicked(const QUrl &)), &CWebView::manager, SLOT(linkClicked(const QUrl &))); + QObject::connect(wid, SIGNAL(loadFinished(bool)), &CWebView::manager, SLOT(loadFinished(bool))); + QObject::connect(wid, SIGNAL(loadProgress(int)), &CWebView::manager, SLOT(loadProgress(int))); + QObject::connect(wid, SIGNAL(loadStarted()), &CWebView::manager, SLOT(loadStarted())); + QObject::connect(wid, SIGNAL(selectionChanged()), &CWebView::manager, SLOT(selectionChanged())); + QObject::connect(wid, SIGNAL(statusBarMessage(const QString &)), &CWebView::manager, SLOT(statusBarMessage(const QString &))); + QObject::connect(wid, SIGNAL(titleChanged(const QString &)), &CWebView::manager, SLOT(titleChanged(const QString &))); + + QObject::connect(wid->page(), SIGNAL(linkHovered(const QString &, const QString &, const QString &)), &CWebView::manager, + SLOT(linkHovered(const QString &, const QString &, const QString &))); + QObject::connect(wid->page(), SIGNAL(frameCreated(QWebFrame *)), &CWebView::manager, SLOT(frameCreated(QWebFrame *))); + QObject::connect(wid->page(), SIGNAL(downloadRequested(QNetworkRequest)), &CWebView::manager, SLOT(downloadRequested(QNetworkRequest))); + QObject::connect(wid->page(), SIGNAL(unsupportedContent(QNetworkReply*)), &CWebView::manager, SLOT(handleUnsupportedContent(QNetworkReply*))); + + QObject::connect(wid, SIGNAL(iconChanged()), &CWebView::manager, SLOT(iconChanged())); + QObject::connect(wid->page()->mainFrame(), SIGNAL(urlChanged(const QUrl &)), &CWebView::manager, SLOT(urlChanged(const QUrl &))); + + QObject::connect(wid->page()->networkAccessManager(), SIGNAL(authenticationRequired(QNetworkReply *, QAuthenticator *)), &CWebView::manager, + SLOT(authenticationRequired(QNetworkReply *, QAuthenticator *))); +END_METHOD + +BEGIN_METHOD_VOID(WebView_free) + + if (_network_access_manager_view == THIS) + _network_access_manager_view = 0; + + GB.FreeString(&THIS->status); + GB.FreeString(&THIS->userAgent); + GB.Unref(POINTER(&THIS->icon)); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_init) + + _old_after_set_color = QT.AfterSetColor(after_set_color); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_exit) + + delete _network_access_manager; + +END_METHOD + +BEGIN_PROPERTY(WebView_Url) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->url().toString()); + else + { + stop_view(THIS); + WIDGET->setUrl(QSTRING_PROP()); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebView_HTML) + + if (READ_PROPERTY) + RETURN_NEW_STRING(WIDGET->page()->mainFrame()->toHtml()); + else + WIDGET->setHtml(QSTRING_PROP()); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Text) + + RETURN_NEW_STRING(WIDGET->page()->mainFrame()->toPlainText()); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Icon) + + if (!THIS->icon) + { + QIcon icon = WIDGET->icon(); + + if (icon.isNull()) + icon = QWebSettings::iconForUrl(WIDGET->url()); + + if (!icon.isNull()) + { + THIS->icon = QT.CreatePicture(icon.pixmap(16, 16)); + GB.Ref(THIS->icon); + } + } + + GB.ReturnObject(THIS->icon); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_SelectedText) + + RETURN_NEW_STRING(WIDGET->selectedText()); + +END_PROPERTY + +#if QT_VERSION >= QT_VERSION_CHECK(4, 5, 0) +BEGIN_PROPERTY(WebView_Zoom) + + if (READ_PROPERTY) + GB.ReturnFloat(WIDGET->zoomFactor()); + else + WIDGET->setZoomFactor(VPROP(GB_FLOAT)); + +END_PROPERTY +#endif + +BEGIN_PROPERTY(WebView_TextZoom) + + if (READ_PROPERTY) + GB.ReturnFloat(WIDGET->textSizeMultiplier()); + else + WIDGET->setTextSizeMultiplier(VPROP(GB_FLOAT)); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Title) + + RETURN_NEW_STRING(WIDGET->title()); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebView_Back) + + WIDGET->back(); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_Forward) + + WIDGET->forward(); + +END_METHOD + +BEGIN_METHOD(WebView_Reload, GB_BOOLEAN bypass) + + bool bypass = VARGOPT(bypass, false); + stop_view(THIS); + if (bypass) + WIDGET->page()->triggerAction(QWebPage::ReloadAndBypassCache); + else + WIDGET->reload(); + +END_METHOD + +BEGIN_METHOD_VOID(WebView_Stop) + + stop_view(THIS); + +END_METHOD + +BEGIN_PROPERTY(WebView_NewView) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->new_view); + else + GB.StoreObject(PROP(GB_OBJECT), &THIS->new_view); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Progress) + + GB.ReturnFloat(THIS->progress); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Status) + + if (READ_PROPERTY) + GB.ReturnString(THIS->status); + else + GB.StoreString(PROP(GB_STRING), &THIS->status); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Frame) + + GB.ReturnObject(CWEBFRAME_get(WIDGET->page()->mainFrame())); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Current) + + GB.ReturnObject(CWEBFRAME_get(WIDGET->page()->currentFrame())); + +END_PROPERTY + + +BEGIN_PROPERTY(WebViewAuth_Url) + + if (THIS->reply) + RETURN_NEW_STRING(THIS->reply->url().toString()); + else + GB.ReturnVoidString(); + +END_PROPERTY + +BEGIN_PROPERTY(WebViewAuth_Realm) + + if (THIS->authenticator) + RETURN_NEW_STRING(THIS->authenticator->realm()); + else + GB.ReturnVoidString(); + +END_PROPERTY + +BEGIN_PROPERTY(WebViewAuth_User) + + if (READ_PROPERTY) + { + if (THIS->authenticator) + RETURN_NEW_STRING(THIS->authenticator->user()); + else + GB.ReturnVoidString(); + } + else + { + if (THIS->authenticator) + THIS->authenticator->setUser(QSTRING_PROP()); + else + GB.Error("No authentication required"); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebViewAuth_Password) + + if (READ_PROPERTY) + { + if (THIS->authenticator) + RETURN_NEW_STRING(THIS->authenticator->password()); + else + GB.ReturnVoidString(); + } + else + { + if (THIS->authenticator) + THIS->authenticator->setPassword(QSTRING_PROP()); + else + GB.Error("No authentication required"); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Cookies) + + MyCookieJar *cookieJar = static_cast(_network_access_manager->cookieJar()); + QList list; + GB_ARRAY cookies; + int i; + + if (READ_PROPERTY) + { + list = cookieJar->allCookies(); + + GB.Array.New(POINTER(&cookies), GB.FindClass("Cookie"), list.count()); + + for (i = 0; i < list.count(); i++) + { + CCOOKIE *cookie = WEB_create_cookie(list.at(i)); + *((CCOOKIE **)(GB.Array.Get(cookies, i))) = cookie; + GB.Ref(cookie); + } + + GB.ReturnObject(cookies); + } + else + { + // TODO what todo? + cookies = VPROP(GB_OBJECT); + if (GB.CheckObject(cookies)) + return; + + for (i = 0; i < GB.Array.Count(cookies); i++) + { + CCOOKIE *cookie = *((CCOOKIE **)GB.Array.Get(cookies, i)); + if (GB.CheckObject(cookie)) + continue; + list.append(*(cookie->cookie)); + } + + cookieJar->setAllCookies(list); + } + +END_PROPERTY + +BEGIN_METHOD(WebView_HitTest, GB_INTEGER X; GB_INTEGER Y) + + GB.ReturnObject(WEB_create_hit_test(WIDGET->page()->mainFrame()->hitTestContent(QPoint(VARG(X), VARG(Y))))); + +END_METHOD + +BEGIN_METHOD(WebView_FindText, GB_STRING text; GB_BOOLEAN backward; GB_BOOLEAN case_sensitive; GB_BOOLEAN wrap) + + QString text; + QWebPage::FindFlags flags = 0; + + if (!MISSING(text)) + text = QSTRING_ARG(text); + + if (VARGOPT(backward, false)) flags |= QWebPage::FindBackward; + if (VARGOPT(case_sensitive, false)) flags |= QWebPage::FindCaseSensitively; + if (VARGOPT(wrap, false)) flags |= QWebPage::FindWrapsAroundDocument; + //if (VARGOPT(highlight, false)) flags |= QWebPage::HighlightAllOccurrences; + + GB.ReturnBoolean(!WIDGET->findText(text, flags)); + +END_METHOD + +BEGIN_PROPERTY(WebView_Editable) + + if (READ_PROPERTY) + GB.ReturnBoolean(WIDGET->page()->isContentEditable()); + else + WIDGET->page()->setContentEditable(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +#if 0 +BEGIN_METHOD(WebView_Exec, GB_STRING action; GB_VARIANT arg) + + QWebPage::WebAction action = get_action(GB.ToZeroString(ARG(action))); + + if (action == QWebPage::NoWebAction) + { + GB.Error("Unknown action"); + return; + } + + if (MISSING(arg)) + { + WIDGET->page()->triggerAction(action); + } + else + { + if (GB.Conv((GB_VALUE *)ARG(arg), GB_T_BOOLEAN)) + return; + + WIDGET->page()->triggerAction(action, ((GB_BOOLEAN *)ARG(arg))->value); + } + +END_METHOD +#endif + +/* +BEGIN_METHOD(WebView_CanExec, GB_STRING action) + + QWebPage::Action action = get_action(GB.ToZeroString(ARG(action))); + + if (action == QWebPage::NoWebAction) + { + GB.Error("Unknown action"); + return; + } + +END_METHOD +*/ + +BEGIN_METHOD(WebView_Eval, GB_STRING javascript) + + CWEBFRAME_eval(WIDGET->page()->currentFrame(), QSTRING_ARG(javascript)); + +END_METHOD + +BEGIN_PROPERTY(WebView_UserAgent) + + if (READ_PROPERTY) + GB.ReturnString(THIS->userAgent); + else + GB.StoreString(PROP(GB_STRING), &THIS->userAgent); + +END_PROPERTY + +BEGIN_PROPERTY(WebView_Document) + + GB.ReturnObject(CWEBELEMENT_create(WIDGET->page()->mainFrame()->documentElement())); + +END_PROPERTY + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(WebViewHistory_Count) + + GB.ReturnInteger(HISTORY->count()); + +END_PROPERTY + +BEGIN_PROPERTY(WebViewHistory_Max) + + GB.ReturnInteger(HISTORY->count() - 1); + +END_PROPERTY + +BEGIN_PROPERTY(WebViewHistory_Index) + + if (READ_PROPERTY) + GB.ReturnInteger(HISTORY->currentItemIndex()); + else + { + int index = VPROP(GB_INTEGER); + if (index < 0 || index >= HISTORY->count()) + { + GB.Error(GB_ERR_ARG); + return; + } + HISTORY->goToItem(HISTORY->itemAt(index)); + } + +END_PROPERTY + +BEGIN_PROPERTY(WebViewHistory_MaxSize) + + if (READ_PROPERTY) + GB.ReturnInteger(HISTORY->maximumItemCount()); + else + HISTORY->setMaximumItemCount(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_METHOD_VOID(WebViewHistory_Clear) + + int max = HISTORY->maximumItemCount(); + HISTORY->setMaximumItemCount(0); + HISTORY->setMaximumItemCount(max); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC WebViewAuthDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebView.Auth"), + + GB_PROPERTY_READ("Url", "s", WebViewAuth_Url), + GB_PROPERTY_READ("Realm", "s", WebViewAuth_Realm), + GB_PROPERTY("User", "s", WebViewAuth_User), + GB_PROPERTY("Password", "s", WebViewAuth_Password), + + GB_END_DECLARE +}; + +GB_DESC WebViewHistoryDesc[] = +{ + GB_DECLARE_VIRTUAL(".WebView.History"), + + GB_PROPERTY_READ("Count", "i", WebViewHistory_Count), + GB_PROPERTY_READ("Max", "i", WebViewHistory_Max), + GB_PROPERTY("Index", "i", WebViewHistory_Index), + GB_PROPERTY("MaxSize", "i", WebViewHistory_MaxSize), + GB_METHOD("Clear", NULL, WebViewHistory_Clear, NULL), + + GB_END_DECLARE +}; + +GB_DESC WebViewDesc[] = +{ + GB_DECLARE("WebView", sizeof(CWEBVIEW)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, WebView_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, WebView_free, NULL), + GB_METHOD("_init", NULL, WebView_init, NULL), + GB_METHOD("_exit", NULL, WebView_exit, NULL), + + GB_PROPERTY("Url", "s", WebView_Url), + GB_PROPERTY("Status", "s", WebView_Status), + + GB_PROPERTY("HTML", "s", WebView_HTML), + GB_PROPERTY_READ("Text", "s", WebView_Text), + GB_PROPERTY_READ("Icon", "Picture", WebView_Icon), + GB_PROPERTY_READ("SelectedText", "s", WebView_SelectedText), + GB_PROPERTY_READ("Progress", "f", WebView_Progress), + #if QT_VERSION >= QT_VERSION_CHECK(4, 5, 0) + GB_PROPERTY("Zoom", "f", WebView_Zoom), + #else + GB_PROPERTY("Zoom", "f", WebView_TextZoom), + #endif + GB_PROPERTY("TextZoom", "f", WebView_TextZoom), + GB_PROPERTY_READ("Title", "s", WebView_Title), + + GB_PROPERTY_READ("Frame", "WebFrame", WebView_Frame), + GB_PROPERTY_READ("Current", "WebFrame", WebView_Current), + GB_PROPERTY_READ("Document", "WebElement", WebView_Document), + + GB_PROPERTY_SELF("Settings", ".WebView.Settings"), + GB_PROPERTY_SELF("Auth", ".WebView.Auth"), + GB_PROPERTY_SELF("History", ".WebView.History"), + + GB_METHOD("Back", NULL, WebView_Back, NULL), + GB_METHOD("Forward", NULL, WebView_Forward, NULL), + GB_METHOD("Reload", NULL, WebView_Reload, "[(BypassCache)b]"), + GB_METHOD("Stop", NULL, WebView_Stop, NULL), + + GB_PROPERTY("NewView", "WebView", WebView_NewView), + + GB_PROPERTY("Cookies", "Cookie[]", WebView_Cookies), + + GB_METHOD("HitTest", "WebHitTest", WebView_HitTest, "(X)i(Y)i"), + GB_METHOD("FindText", "b", WebView_FindText, "[(Text)s(Backward)b(CaseSensitive)b(Wrap)b]"), + + GB_PROPERTY("Editable", "b", WebView_Editable), + //GB_METHOD("Exec", NULL, WebView_Exec, "(Action)s[(Argument)v]"), + //GB_METHOD("CanExec", "b", WebView_CanExec, "(Action)s"), + GB_METHOD("Eval", "v", WebView_Eval, "(JavaScript)s"), + + GB_PROPERTY("UserAgent", "s", WebView_UserAgent), + + GB_CONSTANT("_Properties", "s", "*,Url,Editable"), + GB_CONSTANT("_Group", "s", "View"), + + GB_EVENT("Click", NULL, "(Frame)WebFrame", &EVENT_CLICK), + GB_EVENT("Link", NULL, "(Url)s", &EVENT_LINK), + GB_EVENT("Progress", NULL, NULL, &EVENT_PROGRESS), + GB_EVENT("Load", NULL, NULL, &EVENT_LOAD), + GB_EVENT("Error", NULL, NULL, &EVENT_ERROR), + GB_EVENT("Icon", NULL, NULL, &EVENT_ICON), + GB_EVENT("Title", NULL, NULL, &EVENT_TITLE), + GB_EVENT("Select", NULL, NULL, &EVENT_SELECT), + GB_EVENT("Status", NULL, NULL, &EVENT_STATUS), + GB_EVENT("NewWindow", NULL, "(Modal)b", &EVENT_NEW_WINDOW), + GB_EVENT("NewFrame", NULL, "(Frame)WebFrame", &EVENT_NEW_FRAME), + GB_EVENT("Auth", NULL, NULL, &EVENT_AUTH), + GB_EVENT("Download", NULL, "(Download)WebDownload", &EVENT_DOWNLOAD), + + GB_END_DECLARE +}; + +/***************************************************************************/ + +MyWebPage::MyWebPage(QObject *parent) : QWebPage(parent) +{ +} + +QString MyWebPage::userAgentForUrl(const QUrl& url) const +{ + MyWebView *view = (MyWebView *)parent(); + void *_object = QT.GetObject(view); + + if (THIS->userAgent) + return (const char *)THIS->userAgent; + else + return QWebPage::userAgentForUrl(url); +}; + + +MyWebView::MyWebView(QWidget *parent) : QWebView(parent) +{ + //settings()->setFontFamily(QWebSettings::FixedFont, "monospace"); + setPage(new MyWebPage(this)); +} + +QWebView *MyWebView::createWindow(QWebPage::WebWindowType type) +{ + void *_object = QT.GetObject(this); + QWebView *new_view; + + GB.Raise(THIS, EVENT_NEW_WINDOW, 1, GB_T_BOOLEAN, type == QWebPage::WebModalDialog); + + if (!THIS->new_view) + return 0; + + new_view = (QWebView *)(((CWEBVIEW *)THIS->new_view)->widget.widget); + GB.Unref(POINTER(&THIS->new_view)); + THIS->new_view = 0; + return new_view; +} + +/***************************************************************************/ + +CWebView CWebView::manager; + +// void CWebView::linkClicked(const QUrl &url) +// { +// GET_SENDER(); +// WIDGET->page()->currentFrame()->setUrl(url); +// //WIDGET->setUrl(url); +// GB.Raise(THIS, EVENT_CLICK, 0); +// } + +void CWebView::loadFinished(bool ok) +{ + GET_SENDER(); + + //fprintf(stderr, "loadFinished %d (%d)\n", ok, THIS->stopping); + + THIS->progress = 1; + + if (ok) + GB.Raise(THIS, EVENT_LOAD, 0); + else if (!THIS->stopping) + GB.RaiseLater(THIS, EVENT_ERROR); +} + +void CWebView::loadProgress(int progress) +{ + GET_SENDER(); + + double v = progress / 100.0; + if (THIS->progress == v) + return; + + THIS->progress = v; + GB.Raise(THIS, EVENT_PROGRESS, 0); +} + +void CWebView::loadStarted() +{ + GET_SENDER(); + + //fprintf(stderr, "loadStarted\n"); + + THIS->progress = 0; + _network_access_manager_view = THIS; + GB.Raise(THIS, EVENT_PROGRESS, 0); +} + +void CWebView::selectionChanged() +{ + GET_SENDER(); + GB.Raise(THIS, EVENT_SELECT, 0); +} + +void CWebView::statusBarMessage(const QString &text) +{ + GET_SENDER(); + GB.FreeString(&THIS->status); + THIS->status = NEW_STRING(text); + GB.Raise(THIS, EVENT_STATUS, 0); +} + +void CWebView::titleChanged(const QString &title) +{ + GET_SENDER(); + GB.Raise(THIS, EVENT_TITLE, 0); +} + +void CWebView::linkHovered(const QString &link, const QString &title, const QString &textContent) +{ + void *_object = QT.GetObject(((QWebPage*)sender())->view()); + const char *str = TO_UTF8(link); + GB.Raise(THIS, EVENT_LINK, 1, GB_T_STRING, str, LAST_UTF8_LENGTH()); +} + +void CWebView::frameCreated(QWebFrame *frame) +{ + QObject::connect(frame, SIGNAL(urlChanged(const QUrl &)), &CWebView::manager, SLOT(urlChanged(const QUrl &))); + void *_object = QT.GetObject(((QWebPage*)sender())->view()); + GB.Raise(THIS, EVENT_NEW_FRAME, 1, GB_T_OBJECT, CWEBFRAME_get(frame)); +} + +void CWebView::authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator) +{ + //qDebug("CWebView::authenticationRequired: %p", _network_access_manager_view); + + void *_object = _network_access_manager_view; //QT.GetObject((QWidget *)((QNetworkAccessManager*)sender())->parent()); + if (!THIS) + return; + + THIS->reply = reply; + THIS->authenticator = authenticator; + + GB.Raise(THIS, EVENT_AUTH, 0); + + THIS->reply = 0; + THIS->authenticator = 0; +} + +void CWebView::iconChanged() +{ + //void *_object = QT.GetObject(((QWebFrame *)sender())->page()->view()); + GET_SENDER(); + GB.Unref(POINTER(&THIS->icon)); + THIS->icon = NULL; + GB.RaiseLater(THIS, EVENT_ICON); +} + +void CWebView::urlChanged(const QUrl &) +{ + QWebFrame *frame = (QWebFrame *)sender(); + //fprintf(stderr, "urlChanged: %p\n", frame); + void *_object = QT.GetObject(frame->page()->view()); + GB.Raise(THIS, EVENT_CLICK, 1, GB_T_OBJECT, CWEBFRAME_get(frame)); +} + +void CWebView::downloadRequested(const QNetworkRequest &request) +{ + void *_object = QT.GetObject(((QWebPage*)sender())->view()); + CWEBDOWNLOAD *download; + + download = WEB_create_download(_network_access_manager->get(request)); + + if (GB.Raise(THIS, EVENT_DOWNLOAD, 1, GB_T_OBJECT, download) || !download->path || !*download->path) + WEB_remove_download(download); +} + +void CWebView::handleUnsupportedContent(QNetworkReply *reply) +{ + void *_object = QT.GetObject(((QWebPage*)sender())->view()); + CWEBDOWNLOAD *download; + + if (reply->error() == QNetworkReply::NoError) + { + download = WEB_create_download(reply); + + if (GB.Raise(THIS, EVENT_DOWNLOAD, 1, GB_T_OBJECT, download) || !download->path || !*download->path) + WEB_remove_download(download); + } + else + delete reply; +} diff --git a/gb.qt4/src/webkit/cwebview.h b/gb.qt4/src/webkit/cwebview.h new file mode 100644 index 00000000..4c5d9a45 --- /dev/null +++ b/gb.qt4/src/webkit/cwebview.h @@ -0,0 +1,122 @@ +/*************************************************************************** + + cwebview.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWEBVIEW_H +#define __CWEBVIEW_H + +#include +#include +#include +#include +#include +#include +#include + +#include "cwebdownload.h" +#include "main.h" + +#ifndef __CWEBVIEW_CPP + +extern GB_DESC WebViewAuthDesc[]; +extern GB_DESC WebViewHistoryDesc[]; +extern GB_DESC WebViewDownloadsDesc[]; +extern GB_DESC WebViewDesc[]; + +#else + +#define THIS ((CWEBVIEW *)_object) +#define WIDGET ((MyWebView *)((QT_WIDGET *)_object)->widget) + +#endif + +class MyWebPage : public QWebPage +{ + Q_OBJECT + +public: + + MyWebPage(QObject *parent); + +protected: + + virtual QString userAgentForUrl(const QUrl& url) const; +}; + +class MyWebView : public QWebView +{ + Q_OBJECT + +public: + + MyWebView(QWidget *parent); + +protected: + + virtual QWebView *createWindow(QWebPage::WebWindowType type); +}; + +typedef + struct + { + QT_WIDGET widget; + void *new_view; + double progress; + char *status; + QT_PICTURE icon; + QNetworkReply *reply; + QAuthenticator *authenticator; + char *userAgent; + unsigned stopping : 1; + } + CWEBVIEW; + +class CWebView : public QObject +{ + Q_OBJECT + +public: + + static CWebView manager; + +public slots: + + void iconChanged(); + //void linkClicked(const QUrl &url); + void loadFinished(bool ok); + void loadProgress(int progress); + void loadStarted(); + void selectionChanged(); + void statusBarMessage(const QString &text); + void titleChanged(const QString &title); + void linkHovered(const QString &link, const QString &title, const QString &textContent); + //void downloadRequested(const QNetworkRequest &); + void frameCreated(QWebFrame *); + void authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator); + void urlChanged(const QUrl &); + void downloadRequested(const QNetworkRequest &); + void handleUnsupportedContent(QNetworkReply*); +}; + +QNetworkAccessManager *WEBVIEW_get_network_manager(); + +#endif diff --git a/gb.qt4/src/webkit/gb.qt4.webkit.component b/gb.qt4/src/webkit/gb.qt4.webkit.component new file mode 100644 index 00000000..ba4504f0 --- /dev/null +++ b/gb.qt4/src/webkit/gb.qt4.webkit.component @@ -0,0 +1,10 @@ +[Component] +Key=gb.qt4.webkit +Author=Benoît Minisini +Type=Form +Require=gb.qt4 +State=Stable + +[Form] +Control=WebView + diff --git a/gb.qt4/src/webkit/main.cpp b/gb.qt4/src/webkit/main.cpp new file mode 100644 index 00000000..f88ddb6d --- /dev/null +++ b/gb.qt4/src/webkit/main.cpp @@ -0,0 +1,143 @@ +/*************************************************************************** + + main.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include +#include +#include +#include + +#include + +#include "ccookiejar.h" +#include "cwebhittest.h" +#include "cwebsettings.h" +#include "cwebelement.h" +#include "cwebframe.h" +#include "cwebdownload.h" +#include "cwebview.h" +#include "main.h" + +GB_CLASS CLASS_WebView; + +extern "C" { + +GB_INTERFACE GB EXPORT; +QT_INTERFACE QT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + WebDownloadDesc, + WebDownloadsDesc, + WebHitTestDesc, + CookieDesc, + WebSettingsIconDatabaseDesc, + WebSettingsCacheDesc, + WebSettingsFontsDesc, + WebSettingsProxyDesc, + WebSettingsDesc, + WebElementStyleDesc, + WebElementDesc, + WebFrameChildrenDesc, + WebFrameDesc, + WebViewSettingsDesc, + WebViewAuthDesc, + WebViewHistoryDesc, + WebViewDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ +#ifdef QT5 + GB.GetInterface("gb.qt5", QT_INTERFACE_VERSION, &QT); +#else + GB.GetInterface("gb.qt4", QT_INTERFACE_VERSION, &QT); +#endif + CLASS_WebView = GB.FindClass("WebView"); + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +} + +void MAIN_return_qvariant(const QVariant &result) +{ + GB_DATE date; + GB_DATE_SERIAL ds; + QDateTime qdate; + + switch (result.type()) + { + case QVariant::Bool: + GB.ReturnBoolean(result.toBool()); + break; + + case QVariant::Date: + case QVariant::DateTime: + qdate = result.toDateTime(); + ds.year = qdate.date().year(); + ds.month = qdate.date().month(); + ds.day = qdate.date().day(); + ds.hour = qdate.time().hour(); + ds.min = qdate.time().minute(); + ds.sec = qdate.time().second(); + ds.msec = qdate.time().msec(); + GB.MakeDate(&ds, &date); + GB.ReturnDate(&date); + break; + + case QVariant::Double: + GB.ReturnFloat(result.toDouble()); + break; + + case QVariant::Int: + case QVariant::UInt: + GB.ReturnInteger(result.toInt()); + break; + + case QVariant::LongLong: + case QVariant::ULongLong: + GB.ReturnLong(result.toLongLong()); + break; + + case QVariant::String: + RETURN_NEW_STRING(result.toString()); + break; + + // TODO: Handle these three datatypes + case QVariant::Hash: + case QVariant::List: + case QVariant::RegExp: + default: + GB.ReturnNull(); + break; + } + + GB.ReturnConvVariant(); +} + diff --git a/gb.qt4/src/webkit/main.h b/gb.qt4/src/webkit/main.h new file mode 100644 index 00000000..b8d8e4dd --- /dev/null +++ b/gb.qt4/src/webkit/main.h @@ -0,0 +1,41 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" +#include "../gb.qt.h" + +#include + +#ifndef __MAIN_CPP +extern "C" GB_INTERFACE GB; +extern "C" QT_INTERFACE QT; +extern GB_CLASS CLASS_WebView; +#endif + +void MAIN_return_qvariant(const QVariant &result); + +#endif diff --git a/gb.qt4/src/x11.c b/gb.qt4/src/x11.c new file mode 100644 index 00000000..25415a3f --- /dev/null +++ b/gb.qt4/src/x11.c @@ -0,0 +1,810 @@ +/*************************************************************************** + + x11.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __X11_C + +#include +#include +#include +#include +#include +#include + +#include "x11.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const GB_INTERFACE *GB_PTR; +#define GB (*GB_PTR) + +#ifdef __cplusplus +} + +#endif + + +#define MAX_WINDOW_PROP 16 + +Atom X11_atom_net_wm_state; +Atom X11_atom_net_wm_state_above; +Atom X11_atom_net_wm_state_below; +Atom X11_atom_net_wm_state_stays_on_top; +Atom X11_atom_net_wm_state_skip_taskbar; +Atom X11_atom_net_wm_window_type; +Atom X11_atom_net_wm_window_type_normal; +Atom X11_atom_net_wm_window_type_utility; +Atom X11_atom_net_wm_desktop; +Atom X11_atom_net_wm_user_time; +Atom X11_atom_net_current_desktop; +Atom X11_atom_net_workarea = None; +Atom X11_atom_motif_wm_hints = None; +Atom X11_atom_net_system_tray = None; + +Atom X11_atom_net_supported; +Atom *_supported = NULL; + +static Display *_display; +static Window _root; + +static bool _atom_init = FALSE; + +typedef + struct { + char *name; + Atom atom; + } + X11_ATOM; + +typedef + struct { + int count; + Atom atoms[MAX_WINDOW_PROP]; + bool changed; + } + X11_PROPERTY; + +static Window _window = 0; +static bool _window_visible; +static X11_PROPERTY _window_prop; +static X11_PROPERTY _window_save[2]; + +static char *_property_value = NULL; + +static X11_ATOM _atoms[] = +{ + {"_NET_WM_WINDOW_TYPE_NORMAL"}, + {"_NET_WM_WINDOW_TYPE_DESKTOP"}, + {"_NET_WM_WINDOW_TYPE_DOCK"}, + {"_NET_WM_WINDOW_TYPE_TOOLBAR"}, + {"_NET_WM_WINDOW_TYPE_MENU"}, + {"_NET_WM_WINDOW_TYPE_UTILITY"}, + {"_NET_WM_WINDOW_TYPE_SPLASH"}, + {"_NET_WM_WINDOW_TYPE_DIALOG"}, + {"_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"}, + {"_NET_WM_WINDOW_TYPE_POPUP_MENU"}, + {"_NET_WM_WINDOW_TYPE_TOOLTIP"}, + {"_NET_WM_WINDOW_TYPE_NOTIFICATION"}, + {"_NET_WM_WINDOW_TYPE_COMBO"}, + {"_NET_WM_WINDOW_TYPE_DND"}, + {NULL} +}; + +typedef + struct { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long input_mode; + unsigned long status; + } + MwmHints; + +static void init_atoms() +{ + if (_atom_init) + return; + + X11_atom_net_current_desktop = XInternAtom(_display, "_NET_CURRENT_DESKTOP", True); + + X11_atom_net_wm_state = XInternAtom(_display, "_NET_WM_STATE", True); + X11_atom_net_wm_state_above = XInternAtom(_display, "_NET_WM_STATE_ABOVE", True); + X11_atom_net_wm_state_below = XInternAtom(_display, "_NET_WM_STATE_BELOW", True); + X11_atom_net_wm_state_stays_on_top = XInternAtom(_display, "_NET_WM_STATE_STAYS_ON_TOP", True); + X11_atom_net_wm_state_skip_taskbar = XInternAtom(_display, "_NET_WM_STATE_SKIP_TASKBAR", True); + X11_atom_net_wm_desktop = XInternAtom(_display, "_NET_WM_DESKTOP", True); + X11_atom_net_wm_window_type = XInternAtom(_display, "_NET_WM_WINDOW_TYPE", True); + X11_atom_net_wm_window_type_normal = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_NORMAL", True); + X11_atom_net_wm_window_type_utility = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_UTILITY", True); + X11_atom_net_wm_user_time = XInternAtom(_display, "_NET_WM_USER_TIME", True); + + X11_atom_net_supported = XInternAtom(_display, "_NET_SUPPORTED", True); + + _atom_init = TRUE; +} + +static Atom get_atom(int index) +{ + X11_ATOM *a = &_atoms[index]; + + if (!a->atom) + a->atom = XInternAtom(_display, a->name, True); + + return a->atom; +} + +static int find_atom(Atom atom) +{ + int i = 0; + X11_ATOM *p = _atoms; + + while (p->name) + { + if (!p->atom) + p->atom = XInternAtom(_display, p->name, True); + if (p->atom == atom) + return i; + p++; + i++; + } + return (-1); +} + +#define PROPERTY_START_READ 1024 +#define PROPERTY_NEXT_READ 1024 + +#if 0 + // On 64 bits OS, format 32 are actually long, i.e. int padded to 64 bits! + #if OS_64BITS + if (format == 32) + { + int *p = (int *)_property_value; + for (i = 0; i < count; i++) + p[i] = *((long *)data + i); + } + else + #endif + { + memcpy(_property_value, (char *)data, count * size); + } +#endif + +void X11_flush() +{ + XFlush(_display); +} + +char *X11_get_property(Window wid, Atom prop, Atom *type, int *format, int *pcount) +{ + uchar *data; + unsigned long count; + unsigned long after; + unsigned long offset; + int size, offset_size; + + *pcount = 0; + + if (XGetWindowProperty(_display, wid, prop, 0, PROPERTY_START_READ / sizeof(int32_t), + False, AnyPropertyType, type, format, + &count, &after, &data) != Success) + return NULL; + + *pcount += count; + + size = *format == 32 ? sizeof(long) : ( *format == 16 ? sizeof(short) : 1 ); + offset_size = *format == 32 ? sizeof(int32_t) : ( *format == 16 ? sizeof(short) : 1 ); + + //fprintf(stderr, "X11_get_property: format = %d size = %d count = %ld after = %ld\n", *format, size, count, after); + + GB.FreeString(&_property_value); + _property_value = GB.NewString((char *)data, count * size); + XFree(data); + + offset = count * offset_size / sizeof(int32_t); + + while (after) + { + //fprintf(stderr, "X11_get_property: offset = %ld read = %ld\n", offset, Min(after, PROPERTY_NEXT_READ) / sizeof(int32_t)); + + if (XGetWindowProperty(_display, wid, prop, offset, Min(after, PROPERTY_NEXT_READ) / sizeof(int32_t), + False, AnyPropertyType, type, format, + &count, &after, &data) != Success) + return NULL; + + *pcount += count; + offset += count * offset_size / sizeof(int32_t); + + //fprintf(stderr, "X11_get_property: format = %d size = %d count = %ld after = %ld next offset = %ld\n", *format, size, count, after, offset); + + _property_value = GB.AddString(_property_value, (char *)data, count * size); + XFree(data); + } + + return _property_value; +} + +static char *get_property(Window wid, Atom prop, int *count) +{ + Atom type; + int format; + + return X11_get_property(wid, prop, &type, &format, count); +} + +static void load_window_state(Window win, Atom prop) +{ + int length; + char *data; + + _window_prop.count = 0; + _window_prop.changed = FALSE; + + //get_property(win, X11_atom_net_wm_state, MAX_WINDOW_STATE * sizeof(Atom), &data, &length); + data = get_property(win, prop, &length); + + if (length > MAX_WINDOW_PROP) + length = MAX_WINDOW_PROP; + + _window_prop.count = length; + if (data) + memcpy(_window_prop.atoms, data, length * sizeof(Atom)); +} + +static void save_window_state(Window win, Atom prop) +{ + if (_window_prop.changed) + { + //fprintf(stderr, "XChangeProperty: %ld %ld\n", (long)win, (long)prop); + XChangeProperty(_display, win, prop, XA_ATOM, 32, PropModeReplace, (unsigned char *)_window_prop.atoms, _window_prop.count); + } +} + +static bool has_window_state(Atom prop) +{ + int i; + + for (i = 0; i < _window_prop.count; i++) + { + if (_window_prop.atoms[i] == prop) + return TRUE; + } + + return FALSE; +} + +static void set_window_state(Atom prop) +{ + if (has_window_state(prop)) + return; + + if (_window_prop.count == MAX_WINDOW_PROP) + { + fprintf(stderr, "X11: set_window_state: Too many properties in window\n"); + return; + } + + _window_prop.atoms[_window_prop.count++] = prop; + _window_prop.changed = TRUE; +} + +static void clear_window_state(Atom prop) +{ + int i; + + for (i = 0; i < _window_prop.count; i++) + { + if (_window_prop.atoms[i] == prop) + { + _window_prop.count--; + + for (; i < _window_prop.count; i++) + _window_prop.atoms[i] = _window_prop.atoms[i + 1]; + + _window_prop.changed = TRUE; + return; + } + } +} + + +static void init_net_supported() +{ + char *data; + int count; + + if (_supported) + GB.FreeArray(&_supported); + + data = get_property(_root, X11_atom_net_supported, &count); + if (!data) + return; + + GB.NewArray(&_supported, sizeof(Atom), count); + memcpy(_supported, data, sizeof(Atom) * count); +} + + +void X11_init(Display *display, Window root) +{ + _display = display; + _root = root; + init_atoms(); + init_net_supported(); +} + +void X11_exit() +{ + if (_supported) + GB.FreeArray(&_supported); + if (_property_value) + GB.FreeString(&_property_value); +} + + +void X11_window_change_property(Atom property, bool set) +{ + XEvent e; + + if (_window_visible) + { + e.xclient.type = ClientMessage; + e.xclient.message_type = X11_atom_net_wm_state; + e.xclient.display = _display; + e.xclient.window = _window; + e.xclient.format = 32; + e.xclient.data.l[0] = set ? 1 : 0; + e.xclient.data.l[1] = property; + e.xclient.data.l[2] = 0; + e.xclient.data.l[3] = 0; + e.xclient.data.l[4] = 0; + + XSendEvent(_display, _root, False, (SubstructureRedirectMask | SubstructureNotifyMask), &e); + } + else + { + if (set) + set_window_state(property); + else + clear_window_state(property); + } +} + +void X11_window_change_begin(Window window, bool visible) +{ + _window = window; + _window_visible = visible; + + if (!visible) + load_window_state(window, X11_atom_net_wm_state); +} + +void X11_window_change_end() +{ + if (!_window_visible) + save_window_state(_window, X11_atom_net_wm_state); + + XFlush(_display); + _window = (Window)0; +} + + +bool X11_window_has_property(Window window, Atom property) +{ + load_window_state(window, X11_atom_net_wm_state); + return has_window_state(property); +} + +void X11_sync(void) +{ + XSync(_display, False); +} + +void X11_window_save_properties(Window window) +{ + load_window_state(window, X11_atom_net_wm_state); + _window_save[0] = _window_prop; + //load_window_state(window, X11_atom_net_wm_window_type); + //_window_save[1] = _window_prop; +} + +void X11_window_restore_properties(Window window) +{ + _window_prop = _window_save[0]; + save_window_state(window, X11_atom_net_wm_state); + //_window_prop = _window_save[1]; + //save_window_state(window, X11_atom_net_wm_window_type); +} + +void X11_window_startup(Window window, int x, int y, int w, int h) +{ + XSizeHints s; + + s.flags = USPosition | PPosition | USSize | PSize; + + s.x = x; + s.y = y; + s.width = w; + s.height = h; + + XSetWMNormalHints(_display, window, &s); +} + +// ### Do not forget to call XFree() on window_list once finished with it + +void X11_find_windows(Window **window_list, int *count) +{ + static Atom _net_client_list = 0; + + if (!_net_client_list) + _net_client_list = XInternAtom(_display, "_NET_CLIENT_LIST", True); + + *window_list = (Window *)get_property(_root, _net_client_list, count); +} + +// ### Do not forget to call XFree() on result once finished with it + +void X11_get_window_title(Window window, char **result, int *length) +{ + int l; + *result = get_property(window, XA_WM_NAME, &l); + *length = l; +} + +void X11_get_window_class(Window window, char **result, int *length) +{ + int l; + *result = get_property(window, XA_WM_CLASS, &l); + *length = l; +} + +void X11_get_window_role(Window window, char **result, int *length) +{ + static Atom wm_window_role = (Atom)0; + int l; + + if (!wm_window_role) + wm_window_role = XInternAtom(_display, "WM_WINDOW_ROLE", True); + + *result = get_property(window, wm_window_role, &l); + *length = l; +} + + +// Makes a tool window + +void X11_set_window_tool(Window window, int tool, Window parent) +{ + load_window_state(window, X11_atom_net_wm_window_type); + + if (tool) + { + set_window_state(X11_atom_net_wm_window_type_utility); + clear_window_state(X11_atom_net_wm_window_type_normal); + if (parent) + XSetTransientForHint(_display, window, parent); + } + else + { + clear_window_state(X11_atom_net_wm_window_type_utility); + set_window_state(X11_atom_net_wm_window_type_normal); + } + + save_window_state(window, X11_atom_net_wm_window_type); +} + +int X11_get_window_tool(Window window) +{ + load_window_state(window, X11_atom_net_wm_window_type); + return has_window_state(X11_atom_net_wm_window_type_utility); +} + + +// Set window desktop + +void X11_window_set_desktop(Window window, bool visible, int desktop) +{ + XEvent e; + long mask = (SubstructureRedirectMask | SubstructureNotifyMask); + + if (visible) + { + e.xclient.type = ClientMessage; + e.xclient.message_type = X11_atom_net_wm_desktop; + e.xclient.display = _display; + e.xclient.window = window; + e.xclient.format = 32; + e.xclient.data.l[0] = desktop; + e.xclient.data.l[1] = 1; + e.xclient.data.l[2] = 0; + e.xclient.data.l[3] = 0; + e.xclient.data.l[4] = 0; + + XSendEvent(_display, _root, False, mask, &e); + } + else + { + XChangeProperty(_display, window, X11_atom_net_wm_desktop, XA_CARDINAL, 32, PropModeReplace, + (unsigned char *)&desktop, 1); + XFlush(_display); + } +} + + +int X11_window_get_desktop(Window window) +{ + int length; + char *data = NULL; + int desktop = 0; + + data = get_property(window, X11_atom_net_wm_desktop, &length); + if (data) + desktop = *((int *)data); + + return desktop; +} + +int X11_get_current_desktop() +{ + int length; + char *data; + int desktop = 0; + + data = get_property(_root, X11_atom_net_current_desktop, &length); + if (data) + desktop = *((int *)data); + + return desktop; +} + +int X11_get_window_type(Window window) +{ + int index; + + load_window_state(window, X11_atom_net_wm_window_type); + index = find_atom(_window_prop.atoms[0]); + if (index < 0) + return _NET_WM_WINDOW_TYPE_NORMAL; + else + return index; +} + +void X11_set_window_type(Window window, int type) +{ + _window_prop.count = 1; + _window_prop.atoms[0] = get_atom(type); + save_window_state(window, X11_atom_net_wm_window_type); +} + +void X11_set_transient_for(Window window, Window parent) +{ + XSetTransientForHint(_display, window, parent); +} + +void X11_set_window_decorated(Window window, bool decorated) +{ + // Motif structures + + MwmHints *hints; + MwmHints new_hints; + + uchar *data; + Atom type; + int format; + ulong nitems; + ulong bytes_after; + + if (X11_atom_motif_wm_hints == None) + X11_atom_motif_wm_hints = XInternAtom(_display, "_MOTIF_WM_HINTS", True); + + XGetWindowProperty (_display, window, + X11_atom_motif_wm_hints, 0, sizeof(MwmHints)/sizeof(long), + False, AnyPropertyType, &type, &format, &nitems, + &bytes_after, &data); + + if (type == None) + { + hints = &new_hints; + hints->flags = 0; + hints->functions = 0; + hints->input_mode = 0; + hints->status = 0; + hints->decorations = 0; + } + else + hints = (MwmHints *)data; + + hints->flags |= (1 << 1); + hints->decorations = decorated ? 1 : 0; + + XChangeProperty(_display, window, + X11_atom_motif_wm_hints, X11_atom_motif_wm_hints, 32, PropModeReplace, + (uchar *)hints, sizeof (MwmHints)/sizeof(long)); + + if (hints != &new_hints) + XFree(hints); + + XFlush(_display); +} + +void X11_window_remap(Window window) +{ + XWithdrawWindow(_display, window, DefaultScreen(_display)); + XUnmapWindow(_display, window); + XMapWindow(_display, window); + XFlush(_display); +} + +void X11_window_activate(Window window) +{ + XSetInputFocus(_display, window, RevertToParent, CurrentTime); + XFlush(_display); +} + +bool X11_get_available_geometry(int screen, int *x, int *y, int *w, int *h) +{ + if (X11_atom_net_workarea == None) + X11_atom_net_workarea = XInternAtom(_display, "_NET_WORKAREA", True); + + Atom ret; + int format, e; + unsigned char *data = NULL; + unsigned long nitems, after; + bool err = TRUE; + + e = XGetWindowProperty(_display, RootWindow(_display, screen), + X11_atom_net_workarea, 0, 4, False, XA_CARDINAL, + &ret, &format, &nitems, &after, &data); + + if (e == Success && ret == XA_CARDINAL && format == 32 && nitems == 4) + { + long *workarea = (long *)data; + *x = workarea[0]; + *y = workarea[1]; + *w = workarea[2]; + *h = workarea[3]; + err = FALSE; + } + + if (data) + XFree(data); + + return err; +} + +bool X11_is_supported_by_WM(Atom atom) +{ + int i; + + if (_supported) + { + for (i = 0; i < GB.Count(_supported); i++) + { + if (_supported[i] == atom) + return TRUE; + } + } + + return FALSE; +} + +bool X11_send_move_resize_event(Window window, int x, int y, int w, int h) +{ + static Atom _net_moveresize_window = None; + XEvent e; + + if (!_net_moveresize_window) + _net_moveresize_window = XInternAtom(_display, "_NET_MOVERESIZE_WINDOW", True); + + if (!X11_is_supported_by_WM(_net_moveresize_window)) + return TRUE; + + e.xclient.type = ClientMessage; + e.xclient.message_type = _net_moveresize_window; + e.xclient.display = _display; + e.xclient.window = window; + e.xclient.format = 32; + e.xclient.data.l[0] = StaticGravity | 1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12; + e.xclient.data.l[1] = x; + e.xclient.data.l[2] = y; + e.xclient.data.l[3] = w; + e.xclient.data.l[4] = h; + XSendEvent(_display, _root, FALSE, (SubstructureNotifyMask | SubstructureRedirectMask), &e); + + return FALSE; +} + +static Atom get_net_system_tray(void) +{ + char buf[64]; + + if (X11_atom_net_system_tray == None) + { + sprintf(buf, "_NET_SYSTEM_TRAY_S%d", XScreenNumberOfScreen(DefaultScreenOfDisplay(_display))); + X11_atom_net_system_tray = XInternAtom(_display, buf, 0); + } + + return X11_atom_net_system_tray; +} + +Window X11_get_system_tray() +{ + return XGetSelectionOwner(_display, get_net_system_tray()); +} + +#if 0 +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +#define OPCODE "_NET_SYSTEM_TRAY_OPCODE" + +bool X11_window_dock(Window window) +{ + Window xmanager=None; + XClientMessageEvent ev; + Atom OpCodeAtom; + + XGrabServer(_display); + + xmanager = X11_get_system_tray(); + if (xmanager != None) + XSelectInput(_display, xmanager, StructureNotifyMask); + + XUngrabServer(_display); + XFlush(_display); + + if (xmanager == None) + return TRUE; + + /*********************************************** + Dock Tray Icon + ************************************************/ + + OpCodeAtom = XInternAtom(_display, OPCODE, 0); + + ev.type = ClientMessage; + ev.window = xmanager; + ev.message_type = OpCodeAtom; + ev.format = 32; + ev.data.l[0] = 0; + ev.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK; + ev.data.l[2] = window; + ev.data.l[3] = 0; + ev.data.l[4] = 0; + + XSendEvent(_display, xmanager, 0, NoEventMask, (XEvent *)&ev); + XSync(_display, 0); + usleep(10000); + + return FALSE; +} +#endif + +void X11_window_set_user_time(Window win, int timestamp) +{ + ulong timestamp_long = (long)timestamp; + + XChangeProperty(_display, win, X11_atom_net_wm_user_time, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)×tamp_long, 1); + XFlush(_display); +} diff --git a/gb.qt4/src/x11.h b/gb.qt4/src/x11.h new file mode 100644 index 00000000..01410aa4 --- /dev/null +++ b/gb.qt4/src/x11.h @@ -0,0 +1,132 @@ +/*************************************************************************** + + x11.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __X11_H +#define __X11_H + +#include +#include +#include +//#include +const int XFocusIn = FocusIn; +#undef FocusIn +const int XFocusOut = FocusOut; +#undef FocusOut +const int XKeyPress = KeyPress; +#undef KeyPress +const int XKeyRelease = KeyRelease; +#undef KeyRelease + +#include "gambas.h" +#include "gb_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __X11_C +EXTERN Atom X11_atom_net_wm_state; +EXTERN Atom X11_atom_net_wm_state_above; +EXTERN Atom X11_atom_net_wm_state_below; +EXTERN Atom X11_atom_net_wm_state_stays_on_top; +EXTERN Atom X11_atom_net_wm_state_skip_taskbar; +EXTERN Atom X11_atom_net_wm_window_type; +EXTERN Atom X11_atom_net_wm_desktop; +#endif + +typedef + struct { + char *title; + char *klass; + char *role; + } + X11_WINDOW_INFO; + +enum +{ + _NET_WM_WINDOW_TYPE_NORMAL, + _NET_WM_WINDOW_TYPE_DESKTOP, + _NET_WM_WINDOW_TYPE_DOCK, + _NET_WM_WINDOW_TYPE_TOOLBAR, + _NET_WM_WINDOW_TYPE_MENU, + _NET_WM_WINDOW_TYPE_UTILITY, + _NET_WM_WINDOW_TYPE_SPLASH, + _NET_WM_WINDOW_TYPE_DIALOG, + _NET_WM_WINDOW_TYPE_DROPDOWN_MENU, + _NET_WM_WINDOW_TYPE_POPUP_MENU, + _NET_WM_WINDOW_TYPE_TOOLTIP, + _NET_WM_WINDOW_TYPE_NOTIFICATION, + _NET_WM_WINDOW_TYPE_COMBO, + _NET_WM_WINDOW_TYPE_DND +}; + +void X11_init(Display *display, Window root); +void X11_exit(); +void X11_sync(void); + +/* Functions to deal with the _NET_WM_STATE and _NET_WM_TYPE property */ +void X11_window_change_property(Atom property, bool set); +void X11_window_change_begin(Window window, bool visible); +void X11_window_change_end(); + +bool X11_window_has_property(Window window, Atom property); +void X11_window_save_properties(Window window); +void X11_window_restore_properties(Window window); +/* Function to dock a window in the system tray */ +bool X11_window_dock(Window window); +Window X11_get_system_tray(); +/* Function to define startup position hints for a window being shown */ +void X11_window_startup(Window window, int x, int y, int w, int h); +/* Functions to search for a specific top-level window */ +void X11_find_windows(Window **window_list, int *count); +void X11_get_window_title(Window window, char **result, int *length); +void X11_get_window_class(Window window, char **result, int *length); +void X11_get_window_role(Window window, char **result, int *length); +/* Function to make a tool window */ +void X11_set_window_tool(Window window, int tool, Window parent); +int X11_get_window_tool(Window window); +void X11_window_set_desktop(Window window, bool visible, int desktop); +int X11_window_get_desktop(Window window); +void X11_window_set_user_time(Window win, int timestamp); + +int X11_get_current_desktop(); + +int X11_get_window_type(Window window); +void X11_set_window_type(Window window, int type); +void X11_set_transient_for(Window window, Window parent); +void X11_set_window_decorated(Window window, bool decorated); +void X11_window_remap(Window window); +void X11_window_activate(Window window); +bool X11_get_available_geometry(int screen, int *x, int *y, int *w, int *h); + +char *X11_get_property(Window wid, Atom prop, Atom *type, int *format, int *pcount); +bool X11_is_supported_by_WM(Atom atom); +bool X11_send_move_resize_event(Window window, int x, int y, int w, int h); + +void X11_flush(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gb.qt5/AUTHORS b/gb.qt5/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.qt5/COPYING b/gb.qt5/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.qt5/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.qt5/ChangeLog b/gb.qt5/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.qt5/INSTALL b/gb.qt5/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.qt5/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.qt5/Makefile.am b/gb.qt5/Makefile.am new file mode 100644 index 00000000..11b5fd4c --- /dev/null +++ b/gb.qt5/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @QT5_DIR@ +EXTRA_DIST = reconf share gb*.h gambas.h diff --git a/gb.qt5/NEWS b/gb.qt5/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.qt5/README b/gb.qt5/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.qt5/acinclude.m4 b/gb.qt5/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.qt5/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.qt5/component.am b/gb.qt5/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.qt5/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.qt5/configure.ac b/gb.qt5/configure.ac new file mode 100644 index 00000000..e0fe7d81 --- /dev/null +++ b/gb.qt5/configure.ac @@ -0,0 +1,53 @@ +dnl ---- configure.ac for gb.qt5 + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-qt5, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.qt5) +AC_PROG_LIBTOOL + +GB_CHECK_XWINDOW +AM_CONDITIONAL(XWINDOW, test x"$have_x" = xyes) + +GB_COMPONENT_PKG_CONFIG( + qt5, QT5, gb.qt5, [src], + 'Qt5Core >= 5.3.0' Qt5Gui Qt5Widgets Qt5Svg Qt5PrintSupport Qt5X11Extras x11 +) + +GB_COMPONENT_PKG_CONFIG( + qt5webkit, QT5WEBKIT, gb.qt5.webkit, [webkit], + 'Qt5Core >= 5.3.0' Qt5Gui Qt5Widgets Qt5Network Qt5Xml Qt5WebKit Qt5WebKitWidgets Qt5PrintSupport x11 +) + +pkg-config --atleast-version 5.4.0 Qt5Core +if test $? != 0; then + AC_DEFINE_UNQUOTED(QT_OLD_OPENGL, 1, Use deprecated OpenGL widget) + QT_OPENGL_SUPPORT=Qt5OpenGL + GB_MESSAGE([Qt version is <= 5.3, using deprecated OpenGL widget]) +fi + +AM_CONDITIONAL(QT_OLD_OPENGL, [test x$QT_OPENGL_SUPPORT != x]) + +GB_COMPONENT_PKG_CONFIG( + qt5opengl, QT5OPENGL, gb.qt5.opengl, [opengl], + 'Qt5Core >= 5.3.0' Qt5Gui Qt5Widgets $QT_OPENGL_SUPPORT gl x11 +) + +GB_COMPONENT_PKG_CONFIG( + qt5ext, QT5EXT, gb.qt5.ext, [ext], + 'Qt5Core >= 5.3.0' Qt5Gui Qt5Widgets x11 +) + +MOC=`pkg-config --variable=host_bins Qt5Core`/moc +AC_SUBST(MOC) + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +src/webkit/Makefile \ +src/opengl/Makefile \ +src/ext/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.qt5/gambas.h b/gb.qt5/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.qt5/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.qt5/gb.draw.h b/gb.qt5/gb.draw.h new file mode 120000 index 00000000..82ba0778 --- /dev/null +++ b/gb.qt5/gb.draw.h @@ -0,0 +1 @@ +../main/lib/draw/gb.draw.h \ No newline at end of file diff --git a/gb.qt5/gb.eval.h b/gb.qt5/gb.eval.h new file mode 120000 index 00000000..33d332f0 --- /dev/null +++ b/gb.qt5/gb.eval.h @@ -0,0 +1 @@ +../main/lib/eval/gb.eval.h \ No newline at end of file diff --git a/gb.qt5/gb.geom.h b/gb.qt5/gb.geom.h new file mode 120000 index 00000000..abc5659e --- /dev/null +++ b/gb.qt5/gb.geom.h @@ -0,0 +1 @@ +../main/lib/geom/gb.geom.h \ No newline at end of file diff --git a/gb.qt5/gb.gl.h b/gb.qt5/gb.gl.h new file mode 120000 index 00000000..ff28a726 --- /dev/null +++ b/gb.qt5/gb.gl.h @@ -0,0 +1 @@ +../gb.opengl/src/gb.gl.h \ No newline at end of file diff --git a/gb.qt5/gb.image.h b/gb.qt5/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.qt5/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.qt5/gb.paint.h b/gb.qt5/gb.paint.h new file mode 120000 index 00000000..a516fe05 --- /dev/null +++ b/gb.qt5/gb.paint.h @@ -0,0 +1 @@ +../main/lib/draw/gb.paint.h \ No newline at end of file diff --git a/gb.qt5/gb.qt.am b/gb.qt5/gb.qt.am new file mode 100644 index 00000000..a475f791 --- /dev/null +++ b/gb.qt5/gb.qt.am @@ -0,0 +1,6 @@ +CLEANFILES = *_moc.cpp + +.h_moc.cpp: + $(MOC) -o $@ $< + +SUFFIXES = .h _moc.cpp \ No newline at end of file diff --git a/gb.qt5/gb_common.h b/gb.qt5/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.qt5/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.qt5/m4 b/gb.qt5/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.qt5/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.qt5/reconf b/gb.qt5/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.qt5/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.qt5/share b/gb.qt5/share new file mode 120000 index 00000000..638490a4 --- /dev/null +++ b/gb.qt5/share @@ -0,0 +1 @@ +../gb.qt4/share \ No newline at end of file diff --git a/gb.qt5/src/CButton.cpp b/gb.qt5/src/CButton.cpp new file mode 120000 index 00000000..edde5955 --- /dev/null +++ b/gb.qt5/src/CButton.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CButton.cpp \ No newline at end of file diff --git a/gb.qt5/src/CButton.h b/gb.qt5/src/CButton.h new file mode 120000 index 00000000..cd3da8da --- /dev/null +++ b/gb.qt5/src/CButton.h @@ -0,0 +1 @@ +../../gb.qt4/src/CButton.h \ No newline at end of file diff --git a/gb.qt5/src/CCheckBox.cpp b/gb.qt5/src/CCheckBox.cpp new file mode 120000 index 00000000..7b3905ec --- /dev/null +++ b/gb.qt5/src/CCheckBox.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CCheckBox.cpp \ No newline at end of file diff --git a/gb.qt5/src/CCheckBox.h b/gb.qt5/src/CCheckBox.h new file mode 120000 index 00000000..681844c2 --- /dev/null +++ b/gb.qt5/src/CCheckBox.h @@ -0,0 +1 @@ +../../gb.qt4/src/CCheckBox.h \ No newline at end of file diff --git a/gb.qt5/src/CClipboard.cpp b/gb.qt5/src/CClipboard.cpp new file mode 120000 index 00000000..467a4897 --- /dev/null +++ b/gb.qt5/src/CClipboard.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CClipboard.cpp \ No newline at end of file diff --git a/gb.qt5/src/CClipboard.h b/gb.qt5/src/CClipboard.h new file mode 120000 index 00000000..1f21021e --- /dev/null +++ b/gb.qt5/src/CClipboard.h @@ -0,0 +1 @@ +../../gb.qt4/src/CClipboard.h \ No newline at end of file diff --git a/gb.qt5/src/CColor.cpp b/gb.qt5/src/CColor.cpp new file mode 120000 index 00000000..5b5d22c1 --- /dev/null +++ b/gb.qt5/src/CColor.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CColor.cpp \ No newline at end of file diff --git a/gb.qt5/src/CColor.h b/gb.qt5/src/CColor.h new file mode 120000 index 00000000..f78de373 --- /dev/null +++ b/gb.qt5/src/CColor.h @@ -0,0 +1 @@ +../../gb.qt4/src/CColor.h \ No newline at end of file diff --git a/gb.qt5/src/CConst.cpp b/gb.qt5/src/CConst.cpp new file mode 120000 index 00000000..9b497bd3 --- /dev/null +++ b/gb.qt5/src/CConst.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CConst.cpp \ No newline at end of file diff --git a/gb.qt5/src/CConst.h b/gb.qt5/src/CConst.h new file mode 120000 index 00000000..49486004 --- /dev/null +++ b/gb.qt5/src/CConst.h @@ -0,0 +1 @@ +../../gb.qt4/src/CConst.h \ No newline at end of file diff --git a/gb.qt5/src/CContainer.cpp b/gb.qt5/src/CContainer.cpp new file mode 120000 index 00000000..147be167 --- /dev/null +++ b/gb.qt5/src/CContainer.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CContainer.cpp \ No newline at end of file diff --git a/gb.qt5/src/CContainer.h b/gb.qt5/src/CContainer.h new file mode 120000 index 00000000..b65b2660 --- /dev/null +++ b/gb.qt5/src/CContainer.h @@ -0,0 +1 @@ +../../gb.qt4/src/CContainer.h \ No newline at end of file diff --git a/gb.qt5/src/CDialog.cpp b/gb.qt5/src/CDialog.cpp new file mode 120000 index 00000000..bd7f78dc --- /dev/null +++ b/gb.qt5/src/CDialog.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CDialog.cpp \ No newline at end of file diff --git a/gb.qt5/src/CDialog.h b/gb.qt5/src/CDialog.h new file mode 120000 index 00000000..f49264bf --- /dev/null +++ b/gb.qt5/src/CDialog.h @@ -0,0 +1 @@ +../../gb.qt4/src/CDialog.h \ No newline at end of file diff --git a/gb.qt5/src/CDraw.cpp b/gb.qt5/src/CDraw.cpp new file mode 120000 index 00000000..9f5bfa3c --- /dev/null +++ b/gb.qt5/src/CDraw.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CDraw.cpp \ No newline at end of file diff --git a/gb.qt5/src/CDraw.h b/gb.qt5/src/CDraw.h new file mode 120000 index 00000000..f1c19158 --- /dev/null +++ b/gb.qt5/src/CDraw.h @@ -0,0 +1 @@ +../../gb.qt4/src/CDraw.h \ No newline at end of file diff --git a/gb.qt5/src/CDrawingArea.cpp b/gb.qt5/src/CDrawingArea.cpp new file mode 120000 index 00000000..ac971bd6 --- /dev/null +++ b/gb.qt5/src/CDrawingArea.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CDrawingArea.cpp \ No newline at end of file diff --git a/gb.qt5/src/CDrawingArea.h b/gb.qt5/src/CDrawingArea.h new file mode 120000 index 00000000..833d414d --- /dev/null +++ b/gb.qt5/src/CDrawingArea.h @@ -0,0 +1 @@ +../../gb.qt4/src/CDrawingArea.h \ No newline at end of file diff --git a/gb.qt5/src/CEmbedder.cpp b/gb.qt5/src/CEmbedder.cpp new file mode 120000 index 00000000..ba9c054c --- /dev/null +++ b/gb.qt5/src/CEmbedder.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CEmbedder.cpp \ No newline at end of file diff --git a/gb.qt5/src/CEmbedder.h b/gb.qt5/src/CEmbedder.h new file mode 120000 index 00000000..eab36064 --- /dev/null +++ b/gb.qt5/src/CEmbedder.h @@ -0,0 +1 @@ +../../gb.qt4/src/CEmbedder.h \ No newline at end of file diff --git a/gb.qt5/src/CFont.cpp b/gb.qt5/src/CFont.cpp new file mode 120000 index 00000000..a55857bf --- /dev/null +++ b/gb.qt5/src/CFont.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CFont.cpp \ No newline at end of file diff --git a/gb.qt5/src/CFont.h b/gb.qt5/src/CFont.h new file mode 120000 index 00000000..8a5df5ae --- /dev/null +++ b/gb.qt5/src/CFont.h @@ -0,0 +1 @@ +../../gb.qt4/src/CFont.h \ No newline at end of file diff --git a/gb.qt5/src/CFrame.cpp b/gb.qt5/src/CFrame.cpp new file mode 120000 index 00000000..37de5ef4 --- /dev/null +++ b/gb.qt5/src/CFrame.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CFrame.cpp \ No newline at end of file diff --git a/gb.qt5/src/CFrame.h b/gb.qt5/src/CFrame.h new file mode 120000 index 00000000..31394758 --- /dev/null +++ b/gb.qt5/src/CFrame.h @@ -0,0 +1 @@ +../../gb.qt4/src/CFrame.h \ No newline at end of file diff --git a/gb.qt5/src/CImage.cpp b/gb.qt5/src/CImage.cpp new file mode 120000 index 00000000..a0845ff7 --- /dev/null +++ b/gb.qt5/src/CImage.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CImage.cpp \ No newline at end of file diff --git a/gb.qt5/src/CImage.h b/gb.qt5/src/CImage.h new file mode 120000 index 00000000..4cf3932c --- /dev/null +++ b/gb.qt5/src/CImage.h @@ -0,0 +1 @@ +../../gb.qt4/src/CImage.h \ No newline at end of file diff --git a/gb.qt5/src/CKey.cpp b/gb.qt5/src/CKey.cpp new file mode 120000 index 00000000..bc762ca7 --- /dev/null +++ b/gb.qt5/src/CKey.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CKey.cpp \ No newline at end of file diff --git a/gb.qt5/src/CKey.h b/gb.qt5/src/CKey.h new file mode 120000 index 00000000..52a533bb --- /dev/null +++ b/gb.qt5/src/CKey.h @@ -0,0 +1 @@ +../../gb.qt4/src/CKey.h \ No newline at end of file diff --git a/gb.qt5/src/CLabel.cpp b/gb.qt5/src/CLabel.cpp new file mode 120000 index 00000000..36ce1627 --- /dev/null +++ b/gb.qt5/src/CLabel.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CLabel.cpp \ No newline at end of file diff --git a/gb.qt5/src/CLabel.h b/gb.qt5/src/CLabel.h new file mode 120000 index 00000000..5812f629 --- /dev/null +++ b/gb.qt5/src/CLabel.h @@ -0,0 +1 @@ +../../gb.qt4/src/CLabel.h \ No newline at end of file diff --git a/gb.qt5/src/CMenu.cpp b/gb.qt5/src/CMenu.cpp new file mode 120000 index 00000000..ee26dbde --- /dev/null +++ b/gb.qt5/src/CMenu.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CMenu.cpp \ No newline at end of file diff --git a/gb.qt5/src/CMenu.h b/gb.qt5/src/CMenu.h new file mode 120000 index 00000000..2a70ae26 --- /dev/null +++ b/gb.qt5/src/CMenu.h @@ -0,0 +1 @@ +../../gb.qt4/src/CMenu.h \ No newline at end of file diff --git a/gb.qt5/src/CMessage.cpp b/gb.qt5/src/CMessage.cpp new file mode 120000 index 00000000..293fa570 --- /dev/null +++ b/gb.qt5/src/CMessage.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CMessage.cpp \ No newline at end of file diff --git a/gb.qt5/src/CMessage.h b/gb.qt5/src/CMessage.h new file mode 120000 index 00000000..07338428 --- /dev/null +++ b/gb.qt5/src/CMessage.h @@ -0,0 +1 @@ +../../gb.qt4/src/CMessage.h \ No newline at end of file diff --git a/gb.qt5/src/CMouse.cpp b/gb.qt5/src/CMouse.cpp new file mode 120000 index 00000000..1b102882 --- /dev/null +++ b/gb.qt5/src/CMouse.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CMouse.cpp \ No newline at end of file diff --git a/gb.qt5/src/CMouse.h b/gb.qt5/src/CMouse.h new file mode 120000 index 00000000..23047135 --- /dev/null +++ b/gb.qt5/src/CMouse.h @@ -0,0 +1 @@ +../../gb.qt4/src/CMouse.h \ No newline at end of file diff --git a/gb.qt5/src/CMovieBox.cpp b/gb.qt5/src/CMovieBox.cpp new file mode 120000 index 00000000..2a8b2241 --- /dev/null +++ b/gb.qt5/src/CMovieBox.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CMovieBox.cpp \ No newline at end of file diff --git a/gb.qt5/src/CMovieBox.h b/gb.qt5/src/CMovieBox.h new file mode 120000 index 00000000..6596b160 --- /dev/null +++ b/gb.qt5/src/CMovieBox.h @@ -0,0 +1 @@ +../../gb.qt4/src/CMovieBox.h \ No newline at end of file diff --git a/gb.qt5/src/CPanel.cpp b/gb.qt5/src/CPanel.cpp new file mode 120000 index 00000000..65e453c7 --- /dev/null +++ b/gb.qt5/src/CPanel.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CPanel.cpp \ No newline at end of file diff --git a/gb.qt5/src/CPanel.h b/gb.qt5/src/CPanel.h new file mode 120000 index 00000000..40eaad65 --- /dev/null +++ b/gb.qt5/src/CPanel.h @@ -0,0 +1 @@ +../../gb.qt4/src/CPanel.h \ No newline at end of file diff --git a/gb.qt5/src/CPicture.cpp b/gb.qt5/src/CPicture.cpp new file mode 120000 index 00000000..16533dbd --- /dev/null +++ b/gb.qt5/src/CPicture.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CPicture.cpp \ No newline at end of file diff --git a/gb.qt5/src/CPicture.h b/gb.qt5/src/CPicture.h new file mode 120000 index 00000000..2e73612e --- /dev/null +++ b/gb.qt5/src/CPicture.h @@ -0,0 +1 @@ +../../gb.qt4/src/CPicture.h \ No newline at end of file diff --git a/gb.qt5/src/CRadioButton.cpp b/gb.qt5/src/CRadioButton.cpp new file mode 120000 index 00000000..a3382401 --- /dev/null +++ b/gb.qt5/src/CRadioButton.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CRadioButton.cpp \ No newline at end of file diff --git a/gb.qt5/src/CRadioButton.h b/gb.qt5/src/CRadioButton.h new file mode 120000 index 00000000..89361f82 --- /dev/null +++ b/gb.qt5/src/CRadioButton.h @@ -0,0 +1 @@ +../../gb.qt4/src/CRadioButton.h \ No newline at end of file diff --git a/gb.qt5/src/CScreen.cpp b/gb.qt5/src/CScreen.cpp new file mode 120000 index 00000000..eab9f080 --- /dev/null +++ b/gb.qt5/src/CScreen.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CScreen.cpp \ No newline at end of file diff --git a/gb.qt5/src/CScreen.h b/gb.qt5/src/CScreen.h new file mode 120000 index 00000000..0de19f1e --- /dev/null +++ b/gb.qt5/src/CScreen.h @@ -0,0 +1 @@ +../../gb.qt4/src/CScreen.h \ No newline at end of file diff --git a/gb.qt5/src/CScrollBar.cpp b/gb.qt5/src/CScrollBar.cpp new file mode 120000 index 00000000..649da75b --- /dev/null +++ b/gb.qt5/src/CScrollBar.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CScrollBar.cpp \ No newline at end of file diff --git a/gb.qt5/src/CScrollBar.h b/gb.qt5/src/CScrollBar.h new file mode 120000 index 00000000..2648c351 --- /dev/null +++ b/gb.qt5/src/CScrollBar.h @@ -0,0 +1 @@ +../../gb.qt4/src/CScrollBar.h \ No newline at end of file diff --git a/gb.qt5/src/CSlider.cpp b/gb.qt5/src/CSlider.cpp new file mode 120000 index 00000000..9d9ca5ea --- /dev/null +++ b/gb.qt5/src/CSlider.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CSlider.cpp \ No newline at end of file diff --git a/gb.qt5/src/CSlider.h b/gb.qt5/src/CSlider.h new file mode 120000 index 00000000..06bff278 --- /dev/null +++ b/gb.qt5/src/CSlider.h @@ -0,0 +1 @@ +../../gb.qt4/src/CSlider.h \ No newline at end of file diff --git a/gb.qt5/src/CStyle.cpp b/gb.qt5/src/CStyle.cpp new file mode 120000 index 00000000..dd2e6d6a --- /dev/null +++ b/gb.qt5/src/CStyle.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CStyle.cpp \ No newline at end of file diff --git a/gb.qt5/src/CStyle.h b/gb.qt5/src/CStyle.h new file mode 120000 index 00000000..2c4565c3 --- /dev/null +++ b/gb.qt5/src/CStyle.h @@ -0,0 +1 @@ +../../gb.qt4/src/CStyle.h \ No newline at end of file diff --git a/gb.qt5/src/CTabStrip.cpp b/gb.qt5/src/CTabStrip.cpp new file mode 120000 index 00000000..7ad3c567 --- /dev/null +++ b/gb.qt5/src/CTabStrip.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CTabStrip.cpp \ No newline at end of file diff --git a/gb.qt5/src/CTabStrip.h b/gb.qt5/src/CTabStrip.h new file mode 120000 index 00000000..a90b4589 --- /dev/null +++ b/gb.qt5/src/CTabStrip.h @@ -0,0 +1 @@ +../../gb.qt4/src/CTabStrip.h \ No newline at end of file diff --git a/gb.qt5/src/CTextArea.cpp b/gb.qt5/src/CTextArea.cpp new file mode 120000 index 00000000..0e1762e1 --- /dev/null +++ b/gb.qt5/src/CTextArea.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CTextArea.cpp \ No newline at end of file diff --git a/gb.qt5/src/CTextArea.h b/gb.qt5/src/CTextArea.h new file mode 120000 index 00000000..0bbccc6a --- /dev/null +++ b/gb.qt5/src/CTextArea.h @@ -0,0 +1 @@ +../../gb.qt4/src/CTextArea.h \ No newline at end of file diff --git a/gb.qt5/src/CTextBox.cpp b/gb.qt5/src/CTextBox.cpp new file mode 120000 index 00000000..1a71c919 --- /dev/null +++ b/gb.qt5/src/CTextBox.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CTextBox.cpp \ No newline at end of file diff --git a/gb.qt5/src/CTextBox.h b/gb.qt5/src/CTextBox.h new file mode 120000 index 00000000..a39a4c8f --- /dev/null +++ b/gb.qt5/src/CTextBox.h @@ -0,0 +1 @@ +../../gb.qt4/src/CTextBox.h \ No newline at end of file diff --git a/gb.qt5/src/CWatch.cpp b/gb.qt5/src/CWatch.cpp new file mode 120000 index 00000000..8f71615d --- /dev/null +++ b/gb.qt5/src/CWatch.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CWatch.cpp \ No newline at end of file diff --git a/gb.qt5/src/CWatch.h b/gb.qt5/src/CWatch.h new file mode 120000 index 00000000..1d432e66 --- /dev/null +++ b/gb.qt5/src/CWatch.h @@ -0,0 +1 @@ +../../gb.qt4/src/CWatch.h \ No newline at end of file diff --git a/gb.qt5/src/CWatcher.cpp b/gb.qt5/src/CWatcher.cpp new file mode 120000 index 00000000..2333e2fe --- /dev/null +++ b/gb.qt5/src/CWatcher.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CWatcher.cpp \ No newline at end of file diff --git a/gb.qt5/src/CWatcher.h b/gb.qt5/src/CWatcher.h new file mode 120000 index 00000000..eb51173e --- /dev/null +++ b/gb.qt5/src/CWatcher.h @@ -0,0 +1 @@ +../../gb.qt4/src/CWatcher.h \ No newline at end of file diff --git a/gb.qt5/src/CWidget.cpp b/gb.qt5/src/CWidget.cpp new file mode 120000 index 00000000..f7eb671b --- /dev/null +++ b/gb.qt5/src/CWidget.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CWidget.cpp \ No newline at end of file diff --git a/gb.qt5/src/CWidget.h b/gb.qt5/src/CWidget.h new file mode 120000 index 00000000..224b6ce2 --- /dev/null +++ b/gb.qt5/src/CWidget.h @@ -0,0 +1 @@ +../../gb.qt4/src/CWidget.h \ No newline at end of file diff --git a/gb.qt5/src/CWindow.cpp b/gb.qt5/src/CWindow.cpp new file mode 120000 index 00000000..7f8071c6 --- /dev/null +++ b/gb.qt5/src/CWindow.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/CWindow.cpp \ No newline at end of file diff --git a/gb.qt5/src/CWindow.h b/gb.qt5/src/CWindow.h new file mode 120000 index 00000000..1124e0ff --- /dev/null +++ b/gb.qt5/src/CWindow.h @@ -0,0 +1 @@ +../../gb.qt4/src/CWindow.h \ No newline at end of file diff --git a/gb.qt5/src/Makefile.am b/gb.qt5/src/Makefile.am new file mode 100644 index 00000000..823b020d --- /dev/null +++ b/gb.qt5/src/Makefile.am @@ -0,0 +1,55 @@ +COMPONENT = gb.qt5 +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +SUBDIRS = . @QT5WEBKIT_DIR@ @QT5OPENGL_DIR@ @QT5EXT_DIR@ + +gblib_LTLIBRARIES = gb.qt5.la + +gb_qt5_la_LIBADD = @THREAD_LIB@ @QT5_LIB@ +gb_qt5_la_LDFLAGS = -module @LD_FLAGS@ @QT5_LDFLAGS@ +gb_qt5_la_CXXFLAGS = @THREAD_INC@ -DGB_QT_COMPONENT $(AM_CXXFLAGS) -std=c++11 +gb_qt5_la_CPPFLAGS = @QT5_INC@ -I$(top_srcdir)/share/ + +gb_qt5_la_SOURCES = \ + x11.h x11.c \ + desktop.h desktop.c \ + gb.qt.h main.h main_moc.cpp main.cpp \ + CFont.h CFont.cpp \ + CScreen.h CScreen.cpp \ + CStyle.h CStyle.cpp \ + CWidget.h CWidget_moc.cpp CWidget.cpp \ + CWindow.h CWindow_moc.cpp CWindow.cpp \ + CButton.h CButton_moc.cpp CButton.cpp \ + CContainer.h CContainer_moc.cpp CContainer.cpp \ + CLabel.h CLabel_moc.cpp CLabel.cpp \ + CTextBox.h CTextBox_moc.cpp CTextBox.cpp \ + CMenu.h CMenu_moc.cpp CMenu.cpp \ + CMouse.h CMouse_moc.cpp CMouse.cpp \ + CKey.h CKey_moc.cpp CKey.cpp \ + CColor.h CColor_moc.cpp CColor.cpp \ + CConst.h CConst.cpp \ + CCheckBox.h CCheckBox_moc.cpp CCheckBox.cpp \ + CFrame.h CFrame_moc.cpp CFrame.cpp \ + CPanel.h CPanel_moc.cpp CPanel.cpp \ + CRadioButton.h CRadioButton_moc.cpp CRadioButton.cpp \ + CTextArea.h CTextArea_moc.cpp CTextArea.cpp \ + CTabStrip.h CTabStrip_moc.cpp CTabStrip.cpp \ + CDialog.h CDialog_moc.cpp CDialog.cpp \ + CPicture.h CPicture_moc.cpp CPicture.cpp \ + CImage.h CImage_moc.cpp CImage.cpp \ + CClipboard.h CClipboard_moc.cpp CClipboard.cpp \ + CDraw.h CDraw.cpp \ + cpaint_impl.h cpaint_impl.cpp \ + CWatch.h CWatch_moc.cpp CWatch.cpp \ + CDrawingArea.h CDrawingArea_moc.cpp CDrawingArea.cpp \ + CMessage.h CMessage_moc.cpp CMessage.cpp \ + CSlider.h CSlider_moc.cpp CSlider.cpp \ + CScrollBar.h CScrollBar_moc.cpp CScrollBar.cpp \ + CMovieBox.h CMovieBox_moc.cpp CMovieBox.cpp \ + ctrayicon.h ctrayicon_moc.cpp ctrayicon.cpp \ + CWatcher.h CWatcher_moc.cpp CWatcher.cpp \ + cprinter.h cprinter_moc.cpp cprinter.cpp \ + csvgimage.h csvgimage_moc.cpp csvgimage.cpp \ + fix_breeze.h fix_breeze.cpp + diff --git a/gb.qt5/src/cpaint_impl.cpp b/gb.qt5/src/cpaint_impl.cpp new file mode 120000 index 00000000..52abf3ae --- /dev/null +++ b/gb.qt5/src/cpaint_impl.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/cpaint_impl.cpp \ No newline at end of file diff --git a/gb.qt5/src/cpaint_impl.h b/gb.qt5/src/cpaint_impl.h new file mode 120000 index 00000000..32338433 --- /dev/null +++ b/gb.qt5/src/cpaint_impl.h @@ -0,0 +1 @@ +../../gb.qt4/src/cpaint_impl.h \ No newline at end of file diff --git a/gb.qt5/src/cprinter.cpp b/gb.qt5/src/cprinter.cpp new file mode 120000 index 00000000..99340598 --- /dev/null +++ b/gb.qt5/src/cprinter.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/cprinter.cpp \ No newline at end of file diff --git a/gb.qt5/src/cprinter.h b/gb.qt5/src/cprinter.h new file mode 120000 index 00000000..8c4a778d --- /dev/null +++ b/gb.qt5/src/cprinter.h @@ -0,0 +1 @@ +../../gb.qt4/src/cprinter.h \ No newline at end of file diff --git a/gb.qt5/src/csvgimage.cpp b/gb.qt5/src/csvgimage.cpp new file mode 120000 index 00000000..57a2c5a2 --- /dev/null +++ b/gb.qt5/src/csvgimage.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/csvgimage.cpp \ No newline at end of file diff --git a/gb.qt5/src/csvgimage.h b/gb.qt5/src/csvgimage.h new file mode 120000 index 00000000..6dddd772 --- /dev/null +++ b/gb.qt5/src/csvgimage.h @@ -0,0 +1 @@ +../../gb.qt4/src/csvgimage.h \ No newline at end of file diff --git a/gb.qt5/src/ctrayicon.cpp b/gb.qt5/src/ctrayicon.cpp new file mode 120000 index 00000000..90841e21 --- /dev/null +++ b/gb.qt5/src/ctrayicon.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/ctrayicon.cpp \ No newline at end of file diff --git a/gb.qt5/src/ctrayicon.h b/gb.qt5/src/ctrayicon.h new file mode 120000 index 00000000..64e8e147 --- /dev/null +++ b/gb.qt5/src/ctrayicon.h @@ -0,0 +1 @@ +../../gb.qt4/src/ctrayicon.h \ No newline at end of file diff --git a/gb.qt5/src/desktop.c b/gb.qt5/src/desktop.c new file mode 120000 index 00000000..d7d642ed --- /dev/null +++ b/gb.qt5/src/desktop.c @@ -0,0 +1 @@ +../../gb.qt4/src/desktop.c \ No newline at end of file diff --git a/gb.qt5/src/desktop.h b/gb.qt5/src/desktop.h new file mode 120000 index 00000000..f260187e --- /dev/null +++ b/gb.qt5/src/desktop.h @@ -0,0 +1 @@ +../../gb.qt4/src/desktop.h \ No newline at end of file diff --git a/gb.qt5/src/ext/CDial.cpp b/gb.qt5/src/ext/CDial.cpp new file mode 120000 index 00000000..a2e816fa --- /dev/null +++ b/gb.qt5/src/ext/CDial.cpp @@ -0,0 +1 @@ +../../../gb.qt4/src/ext/CDial.cpp \ No newline at end of file diff --git a/gb.qt5/src/ext/CDial.h b/gb.qt5/src/ext/CDial.h new file mode 120000 index 00000000..e9982c63 --- /dev/null +++ b/gb.qt5/src/ext/CDial.h @@ -0,0 +1 @@ +../../../gb.qt4/src/ext/CDial.h \ No newline at end of file diff --git a/gb.qt5/src/ext/CLCDNumber.cpp b/gb.qt5/src/ext/CLCDNumber.cpp new file mode 120000 index 00000000..fbf45409 --- /dev/null +++ b/gb.qt5/src/ext/CLCDNumber.cpp @@ -0,0 +1 @@ +../../../gb.qt4/src/ext/CLCDNumber.cpp \ No newline at end of file diff --git a/gb.qt5/src/ext/CLCDNumber.h b/gb.qt5/src/ext/CLCDNumber.h new file mode 120000 index 00000000..d4a7dc78 --- /dev/null +++ b/gb.qt5/src/ext/CLCDNumber.h @@ -0,0 +1 @@ +../../../gb.qt4/src/ext/CLCDNumber.h \ No newline at end of file diff --git a/gb.qt5/src/ext/CTextEdit.cpp b/gb.qt5/src/ext/CTextEdit.cpp new file mode 120000 index 00000000..adb78061 --- /dev/null +++ b/gb.qt5/src/ext/CTextEdit.cpp @@ -0,0 +1 @@ +../../../gb.qt4/src/ext/CTextEdit.cpp \ No newline at end of file diff --git a/gb.qt5/src/ext/CTextEdit.h b/gb.qt5/src/ext/CTextEdit.h new file mode 120000 index 00000000..bc66b6fe --- /dev/null +++ b/gb.qt5/src/ext/CTextEdit.h @@ -0,0 +1 @@ +../../../gb.qt4/src/ext/CTextEdit.h \ No newline at end of file diff --git a/gb.qt5/src/ext/Makefile.am b/gb.qt5/src/ext/Makefile.am new file mode 100644 index 00000000..40636741 --- /dev/null +++ b/gb.qt5/src/ext/Makefile.am @@ -0,0 +1,16 @@ +COMPONENT = gb.qt5.ext +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +gblib_LTLIBRARIES = gb.qt5.ext.la + +gb_qt5_ext_la_LIBADD = @QT5EXT_LIB@ +gb_qt5_ext_la_LDFLAGS = -module @LD_FLAGS@ @QT5EXT_LDFLAGS@ +gb_qt5_ext_la_CXXFLAGS = @THREAD_INC@ $(AM_CXXFLAGS) -std=c++11 +gb_qt5_ext_la_CPPFLAGS = @QT5EXT_INC@ -I$(top_srcdir)/share -I$(top_srcdir)/src/share + +gb_qt5_ext_la_SOURCES = \ + main.h main.cpp \ + CLCDNumber.h CLCDNumber_moc.cpp CLCDNumber.cpp \ + CDial.h CDial_moc.cpp CDial.cpp \ + CTextEdit.h CTextEdit_moc.cpp CTextEdit.cpp diff --git a/gb.qt5/src/ext/gb.qt5.ext.component b/gb.qt5/src/ext/gb.qt5.ext.component new file mode 100644 index 00000000..41fc6724 --- /dev/null +++ b/gb.qt5/src/ext/gb.qt5.ext.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.qt5.ext +Author=Benoît Minisini +Require=gb.qt5 diff --git a/gb.qt5/src/ext/main.cpp b/gb.qt5/src/ext/main.cpp new file mode 100644 index 00000000..1d049765 --- /dev/null +++ b/gb.qt5/src/ext/main.cpp @@ -0,0 +1,67 @@ +/*************************************************************************** + + main.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include + +#include "main.h" + +#include "CLCDNumber.h" +#include "CDial.h" +#include "CTextEdit.h" + +extern "C" { + +GB_INTERFACE GB EXPORT; +QT_INTERFACE QT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CLCDNumberDesc, + + CDialDesc, + + CTextEditSelectionDesc, + CTextEditFormatDesc, + CTextEditDesc, + + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.qt5", QT_INTERFACE_VERSION, &QT); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +} + diff --git a/gb.qt5/src/ext/main.h b/gb.qt5/src/ext/main.h new file mode 100644 index 00000000..b63dd008 --- /dev/null +++ b/gb.qt5/src/ext/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "../gb.qt.h" +#include "gb.form.properties.h" + +#ifndef __MAIN_C +extern "C" GB_INTERFACE GB; +extern "C" QT_INTERFACE QT; +#endif + +#endif diff --git a/gb.qt5/src/fix_breeze.cpp b/gb.qt5/src/fix_breeze.cpp new file mode 120000 index 00000000..8a20476c --- /dev/null +++ b/gb.qt5/src/fix_breeze.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/fix_breeze.cpp \ No newline at end of file diff --git a/gb.qt5/src/fix_breeze.h b/gb.qt5/src/fix_breeze.h new file mode 120000 index 00000000..08ef8e19 --- /dev/null +++ b/gb.qt5/src/fix_breeze.h @@ -0,0 +1 @@ +../../gb.qt4/src/fix_breeze.h \ No newline at end of file diff --git a/gb.qt5/src/gb.qt.h b/gb.qt5/src/gb.qt.h new file mode 120000 index 00000000..ef5a2ece --- /dev/null +++ b/gb.qt5/src/gb.qt.h @@ -0,0 +1 @@ +../../gb.qt4/src/gb.qt.h \ No newline at end of file diff --git a/gb.qt5/src/gb.qt5.component b/gb.qt5/src/gb.qt5.component new file mode 100644 index 00000000..2dda0f44 --- /dev/null +++ b/gb.qt5/src/gb.qt5.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.qt5 +Author=Benoît Minisini +Implements=Form,EventLoop,ImageIO +Requires=gb.image +Type=Form diff --git a/gb.qt5/src/main.cpp b/gb.qt5/src/main.cpp new file mode 120000 index 00000000..71d429db --- /dev/null +++ b/gb.qt5/src/main.cpp @@ -0,0 +1 @@ +../../gb.qt4/src/main.cpp \ No newline at end of file diff --git a/gb.qt5/src/main.h b/gb.qt5/src/main.h new file mode 120000 index 00000000..6a1f638d --- /dev/null +++ b/gb.qt5/src/main.h @@ -0,0 +1 @@ +../../gb.qt4/src/main.h \ No newline at end of file diff --git a/gb.qt5/src/opengl/CGLarea.cpp b/gb.qt5/src/opengl/CGLarea.cpp new file mode 100644 index 00000000..f225b1d9 --- /dev/null +++ b/gb.qt5/src/opengl/CGLarea.cpp @@ -0,0 +1,158 @@ +/*************************************************************************** + + CGLarea.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CGLAREA_CPP + +#include "gb.image.h" + +#include "CGLarea.h" + +DECLARE_EVENT(EVENT_Open); +DECLARE_EVENT(EVENT_Draw); +DECLARE_EVENT(EVENT_Resize); + +BEGIN_METHOD(CGLAREA_new, GB_OBJECT parent) + + /*if (!QGLFormat::hasOpenGL()) + { + GB.Error( "This system has no OpenGL support"); + return; + }*/ + + GLarea *area = new GLarea(QT.GetContainer(VARG(parent)), THIS); + QT.InitWidget(area, _object, false); + area->show(); + +END_METHOD + +#if 0 +BEGIN_METHOD_VOID(CGLAREA_update) + + WIDGET->updateGL(); + +END_METHOD +#endif + +#if 0 +BEGIN_METHOD_VOID(CGLAREA_select) + + WIDGET->makeCurrent(); + // really needed ? + GL.Init(); + +END_METHOD +#endif + +#if 0 +BEGIN_METHOD(CGLAREA_text, GB_STRING text; GB_INTEGER x; GB_INTEGER y) + + QString text; + int x, y; + #ifdef GL_LIGHTING + GLboolean _LIGHTING = glIsEnabled(GL_LIGHTING); + #endif + GLboolean _TEXTURE_2D = glIsEnabled(GL_TEXTURE_2D); + + #ifdef GL_LIGHTING + if (_LIGHTING) + glDisable(GL_LIGHTING); + #endif + if (_TEXTURE_2D) + glDisable(GL_TEXTURE_2D); + + text = QSTRING_ARG(text); + x = VARG(x); + y = VARG(y); + + WIDGET->renderText(x, y, text, WIDGET->font()); + + #ifdef GL_LIGHTING + if (_LIGHTING) + glEnable(GL_LIGHTING); + #endif + if (_TEXTURE_2D) + glEnable(GL_TEXTURE_2D); + +END_METHOD +#endif + +/**************************************************************************/ + +GB_DESC CGlareaDesc[] = +{ + GB_DECLARE("GLArea", sizeof(CGLAREA)), GB_INHERITS("Control"), + + GB_METHOD("_new", NULL, CGLAREA_new, "(Parent)Container;"), + + GB_CONSTANT("_Group", "s", "Special"), + + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + + GB_END_DECLARE +}; + +/* class GLarea */ + +GLarea::GLarea(QWidget *parent, CGLAREA *object): QOpenGLWidget(parent) +{ + setFocusPolicy(Qt::WheelFocus); + setAttribute(Qt::WA_InputMethodEnabled, true); + _area = object; +}; + + +void GLarea::initializeGL() +{ + GL.Init(); + GB.Raise(_area, EVENT_Open, 0); +} + +void GLarea::paintGL() +{ + /*static bool CleanupOnFirstShow = 0; + + uint color; + + if (!CleanupOnFirstShow) + { + // clear to avoid garbage + color = QT.GetBackgroundColor(_area); + if (color == GB_COLOR_DEFAULT) + color = 0; + + qglClearColor(QColor((QRgb)color)); + glClear(GL_COLOR_BUFFER_BIT); + + CleanupOnFirstShow = true; + }*/ + + GB.Raise(_area, EVENT_Draw, 0); +} + +void GLarea::resizeGL(int w, int h) +{ + GB.Raise(_area, EVENT_Resize, 0); +} + diff --git a/gb.qt5/src/opengl/CGLarea.h b/gb.qt5/src/opengl/CGLarea.h new file mode 100644 index 00000000..0c6020ce --- /dev/null +++ b/gb.qt5/src/opengl/CGLarea.h @@ -0,0 +1,60 @@ +/*************************************************************************** + + CGLarea.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CGLAREA_H +#define __CGLAREA_H + +#include "main.h" + +#include + +typedef + struct { + QT_WIDGET widget; + } + CGLAREA; + +#ifndef __CGLAREA_CPP +extern GB_DESC CGlareaDesc[]; +#else + +#define THIS ((CGLAREA *)_object) +#define WIDGET ((GLarea *)((QT_WIDGET *)_object)->widget) + +#endif /* __CGLAREA_CPP */ + +class GLarea : public QOpenGLWidget +{ +Q_OBJECT +public: + GLarea(QWidget *parent, CGLAREA *object); + ~GLarea() {}; +protected: + void initializeGL(); + void paintGL(); + void resizeGL(int w, int h); +private: + CGLAREA *_area; +}; + +#endif diff --git a/gb.qt5/src/opengl/COldGLarea.cpp b/gb.qt5/src/opengl/COldGLarea.cpp new file mode 100644 index 00000000..1b3d3cde --- /dev/null +++ b/gb.qt5/src/opengl/COldGLarea.cpp @@ -0,0 +1,204 @@ +/*************************************************************************** + + CGLarea.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CGLAREA_CPP + +#include "gb.image.h" +#include "COldGLarea.h" + +//#include "gl.h" + + +//#include + + +static int glWidgetCount = 0; +static QGLWidget *sharedWidget = 0; + +DECLARE_EVENT(EVENT_Open); +DECLARE_EVENT(EVENT_Draw); +DECLARE_EVENT(EVENT_Resize); + +static void makeSharing() +{ + if (sharedWidget) + return; + + sharedWidget = new QGLWidget(); + //qDebug("make_sharing: %p", sharedWidget); +} + +BEGIN_METHOD_VOID(CGLAREA_exit) + + //qDebug("CGLAREA_exit: %p", sharedWidget); + delete sharedWidget; + sharedWidget = 0; + +END_METHOD + +BEGIN_METHOD(CGLAREA_new, GB_OBJECT parent) + + if (!QGLFormat::hasOpenGL()) + { + GB.Error( "This system has no OpenGL support"); + return; + } + + makeSharing(); + GLarea *area = new GLarea(QT.GetContainer(VARG(parent)), THIS, sharedWidget); + glWidgetCount++; + + QT.InitWidget(area, _object, false); + area->show(); + +END_METHOD + +BEGIN_METHOD_VOID(CGLAREA_free) + + glWidgetCount--; + + // clean up the shared datas between GLwidgets if there is no more CGLarea + if (glWidgetCount <= 0) + { + delete sharedWidget; + sharedWidget = 0; + makeSharing(); + glWidgetCount = 0; + } + + +END_METHOD + +BEGIN_METHOD_VOID(CGLAREA_update) + + WIDGET->updateGL(); + +END_METHOD + +#if 0 +BEGIN_METHOD_VOID(CGLAREA_select) + + WIDGET->makeCurrent(); + // really needed ? + GL.Init(); + +END_METHOD +#endif + +BEGIN_METHOD(CGLAREA_text, GB_STRING text; GB_INTEGER x; GB_INTEGER y) + + QString text; + int x, y; + #ifdef GL_LIGHTING + GLboolean _LIGHTING = glIsEnabled(GL_LIGHTING); + #endif + GLboolean _TEXTURE_2D = glIsEnabled(GL_TEXTURE_2D); + + #ifdef GL_LIGHTING + if (_LIGHTING) + glDisable(GL_LIGHTING); + #endif + if (_TEXTURE_2D) + glDisable(GL_TEXTURE_2D); + + text = QSTRING_ARG(text); + x = VARG(x); + y = VARG(y); + + WIDGET->renderText(x, y, text, WIDGET->font()); + + #ifdef GL_LIGHTING + if (_LIGHTING) + glEnable(GL_LIGHTING); + #endif + if (_TEXTURE_2D) + glEnable(GL_TEXTURE_2D); + +END_METHOD + +/**************************************************************************/ + +GB_DESC CGlareaDesc[] = +{ + GB_DECLARE("GLArea", sizeof(CGLAREA)), GB_INHERITS("Control"), + + GB_STATIC_METHOD("_exit", NULL, CGLAREA_exit, NULL), + + GB_METHOD("_new", NULL, CGLAREA_new, "(Parent)Container;"), + GB_METHOD("_free", NULL, CGLAREA_free, NULL), + //GB_METHOD("Update", NULL, CGLAREA_update, NULL), + GB_METHOD("Refresh", NULL, CGLAREA_update, NULL), + //GB_METHOD("Select", NULL, CGLAREA_select, NULL), + //GB_METHOD("Text", NULL, CGLAREA_text, "(Text)s(X)i(Y)i"), + + GB_CONSTANT("_Group", "s", "Special"), + + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + + GB_END_DECLARE +}; + +/* class GLarea */ + +GLarea::GLarea(QWidget *parent, CGLAREA *object, QGLWidget *sharing): QGLWidget(parent, sharing) +{ + setFocusPolicy(Qt::WheelFocus); + setAttribute(Qt::WA_InputMethodEnabled, true); +_area = object; +}; + + +void GLarea::initializeGL() +{ + GL.Init(); + GB.Raise(_area, EVENT_Open, 0); +} + +void GLarea::paintGL() +{ + /*static bool CleanupOnFirstShow = 0; + + uint color; + if (!CleanupOnFirstShow) + { + // clear to avoid garbage + color = QT.GetBackgroundColor(_area); + if (color == GB_COLOR_DEFAULT) + color = 0; + + qglClearColor(QColor((QRgb)color)); + glClear(GL_COLOR_BUFFER_BIT); + + CleanupOnFirstShow = true; + }*/ + + GB.Raise(_area, EVENT_Draw, 0); +} + +void GLarea::resizeGL(int w, int h) +{ + GB.Raise(_area, EVENT_Resize, 0); +} + diff --git a/gb.qt5/src/opengl/COldGLarea.h b/gb.qt5/src/opengl/COldGLarea.h new file mode 100644 index 00000000..88cd1c2e --- /dev/null +++ b/gb.qt5/src/opengl/COldGLarea.h @@ -0,0 +1,60 @@ +/*************************************************************************** + + CGLarea.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CGLAREA_H +#define __CGLAREA_H + +#include "main.h" + +#include + +typedef + struct { + QT_WIDGET widget; + } + CGLAREA; + +#ifndef __CGLAREA_CPP +extern GB_DESC CGlareaDesc[]; +#else + +#define THIS ((CGLAREA *)_object) +#define WIDGET ((GLarea *)((QT_WIDGET *)_object)->widget) + +#endif /* __CGLAREA_CPP */ + +class GLarea : public QGLWidget +{ +Q_OBJECT +public: + GLarea(QWidget *parent, CGLAREA *object, QGLWidget *sharing = 0); + ~GLarea() {}; +protected: + void initializeGL(); + void paintGL(); + void resizeGL(int w, int h); +private: + CGLAREA *_area; +}; + +#endif diff --git a/gb.qt5/src/opengl/Makefile.am b/gb.qt5/src/opengl/Makefile.am new file mode 100644 index 00000000..5fcca25e --- /dev/null +++ b/gb.qt5/src/opengl/Makefile.am @@ -0,0 +1,23 @@ +COMPONENT = gb.qt5.opengl +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +gblib_LTLIBRARIES = gb.qt5.opengl.la + +gb_qt5_opengl_la_LIBADD = @QT5OPENGL_LIB@ @QT5OPENGL_LIB@ +gb_qt5_opengl_la_LDFLAGS = -module @LD_FLAGS@ @QT5OPENGL_LDFLAGS@ +gb_qt5_opengl_la_CXXFLAGS = @THREAD_INC@ $(AM_CXXFLAGS) -std=c++11 +gb_qt5_opengl_la_CPPFLAGS = @QT5OPENGL_INC@ + +gb_qt5_opengl_la_SOURCES = main.h main.cpp + +if QT_OLD_OPENGL +gb_qt5_opengl_la_SOURCES += COldGLarea_moc.cpp COldGLarea.h COldGLarea.cpp +else +gb_qt5_opengl_la_SOURCES += CGLarea_moc.cpp CGLarea.h CGLarea.cpp +endif + + + + + diff --git a/gb.qt5/src/opengl/gb.qt5.opengl.component b/gb.qt5/src/opengl/gb.qt5.opengl.component new file mode 100644 index 00000000..cba9aa62 --- /dev/null +++ b/gb.qt5/src/opengl/gb.qt5.opengl.component @@ -0,0 +1,6 @@ +[Component] +Author=Laurent Carlier,Benoît Minisini +Require=gb.qt5,gb.opengl +Type=Form +Implement=OpenGLViewer +State=Stable diff --git a/gb.qt5/src/opengl/main.cpp b/gb.qt5/src/opengl/main.cpp new file mode 100644 index 00000000..8eff0422 --- /dev/null +++ b/gb.qt5/src/opengl/main.cpp @@ -0,0 +1,58 @@ +/*************************************************************************** + + main.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include "main.h" + +#if QT_OLD_OPENGL +#include "COldGLarea.h" +#else +#include "CGLarea.h" +#endif + +extern "C" { + +GB_INTERFACE GB EXPORT; +QT_INTERFACE QT; +GL_INTERFACE GL; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CGlareaDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface(QT_NAME, QT_INTERFACE_VERSION, &QT); + GB.GetInterface("gb.opengl", GL_INTERFACE_VERSION, &GL); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +} diff --git a/gb.qt5/src/opengl/main.h b/gb.qt5/src/opengl/main.h new file mode 100644 index 00000000..26349161 --- /dev/null +++ b/gb.qt5/src/opengl/main.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + main.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "../gb.qt.h" +#include "gb.gl.h" + +#ifndef __MAIN_CPP +extern GB_INTERFACE GB; +extern QT_INTERFACE QT; +extern GL_INTERFACE GL; +#endif + +#endif diff --git a/gb.qt5/src/trayicon.xpm b/gb.qt5/src/trayicon.xpm new file mode 120000 index 00000000..065f8f44 --- /dev/null +++ b/gb.qt5/src/trayicon.xpm @@ -0,0 +1 @@ +../../gb.qt4/src/trayicon.xpm \ No newline at end of file diff --git a/gb.qt5/src/webkit/Makefile.am b/gb.qt5/src/webkit/Makefile.am new file mode 100644 index 00000000..ec9c3011 --- /dev/null +++ b/gb.qt5/src/webkit/Makefile.am @@ -0,0 +1,20 @@ +COMPONENT = gb.qt5.webkit +include $(top_srcdir)/component.am +include $(top_srcdir)/gb.qt.am + +gblib_LTLIBRARIES = gb.qt5.webkit.la + +gb_qt5_webkit_la_LIBADD = @QT5WEBKIT_LIB@ +gb_qt5_webkit_la_LDFLAGS = -module @LD_FLAGS@ @QT5WEBKIT_LDFLAGS@ +gb_qt5_webkit_la_CXXFLAGS = @THREAD_INC@ $(AM_CXXFLAGS) -std=c++11 +gb_qt5_webkit_la_CPPFLAGS = @QT5WEBKIT_INC@ -I$(top_srcdir)/share -I$(top_srcdir)/src/share + +gb_qt5_webkit_la_SOURCES = \ + main.h main.cpp \ + cwebsettings.h cwebsettings.cpp \ + cwebframe.h cwebframe.cpp cwebframe_moc.cpp \ + cwebelement.h cwebelement.cpp \ + cwebview.h cwebview.cpp cwebview_moc.cpp \ + ccookiejar.h ccookiejar.cpp ccookiejar_moc.cpp \ + cwebhittest.h cwebhittest.cpp \ + cwebdownload.h cwebdownload.cpp cwebdownload_moc.cpp diff --git a/gb.qt5/src/webkit/ccookiejar.cpp b/gb.qt5/src/webkit/ccookiejar.cpp new file mode 120000 index 00000000..b5ea8b4f --- /dev/null +++ b/gb.qt5/src/webkit/ccookiejar.cpp @@ -0,0 +1 @@ +../../../gb.qt4/src/webkit/ccookiejar.cpp \ No newline at end of file diff --git a/gb.qt5/src/webkit/ccookiejar.h b/gb.qt5/src/webkit/ccookiejar.h new file mode 120000 index 00000000..93f3e108 --- /dev/null +++ b/gb.qt5/src/webkit/ccookiejar.h @@ -0,0 +1 @@ +../../../gb.qt4/src/webkit/ccookiejar.h \ No newline at end of file diff --git a/gb.qt5/src/webkit/control/webview.png b/gb.qt5/src/webkit/control/webview.png new file mode 100644 index 0000000000000000000000000000000000000000..3f8789b0026559711b4a8386dd471255ac8118ef GIT binary patch literal 1572 zcmV+<2HW|GP)LiI!86h!ZiUd+r z1QG+psBKy(G*wYlnz~6H$94RQJUT; zajeK?ERIeObNuKiqa(%tIr~@uTHn7wDaDmHZgYKh9XI2G5Nuc5cwT_l2d#XgC}8C? zlm_zjjg*)?G|bp!A8yK~-UujEviOO?rC+|m{QTyB?C2T5FeF-QUjE~CqF57j6{hQ8 zyB5-vU`m8-qAi2%-4@Mu#9B3Ed8@_j+y*yqZIZ9#u(D}h`qf|YyygRI;ei&s_WCWB zS3Q&v)LIdrJvc}q@8SnBi|Y+Gy^!KSfkeQ{T8-Olb-W;^9wuyhA$Qh1R`1pMU~ZF_ z|8V`2S>Sm-Z@#sNn|AR0E+@Y@!PrO2FH5QkvL_+eHE4Nu$t@aM^-uvs!ez--XG^I+0eS-xO1nnpx>?-z;m3ijiAUE#p z^3j%0Xc+7SF^#SU$E2e*k%EbXqYOSZjE~9de_QAs;O|!#Szd2YZFJbI1?<*CkOFB+ zT-Tznn4;bcSlemh$BM)>P?m{sY|OMnECq=axLKD{B}Zkb#G7y5=^bExVTT0x$GdC% z>(&;r(u7fhA9k@U$wsZk>UM*t#!Eai)rXaFz_pRCg|sb%ZDKek`GU*XP#)VhxU*W{ z6JXCqRBv?s68US@P(j#i}bI*J>cK7Bcdb|T)GB-C{ zxG9^N0~Puz8I)t9Yy)ANNK29kNuiL&aZKjsH(6d-r@m8Xppxkw;KcE9Of&|XLdGVD z5=JU1s*z%`7V)hkLp*zI1Z3 zcMG09GRBe54e;JlgTRl7?1*LYgnM@WP_T_Bm*PF~O?Lw!EX-FiRsnHzge{k$UVfF@4N@JQPFZ|?docjC#1w-RF z7HC-5Xz|WUon{oXzhE((GwDki`0Wl}!$&BM<=RN4nJ7B^;)T=n_vg|26O3ai1zKzR z`g8pJXQ%k~Oh4r)U?OKTRZcUQGRWJKv@Pf=_^1}*`5jy-unmdyLyiu){QBZ^OdlQr zK#0A^@dH;fA?`y|`trQ^)30#kl{tR@>H=%q4JyQ}^r!G@0e5Qwt#(A+3s|1pU?^yD z_8XJ@@XTQC7)%y8n^7_VJj=IZfz+j&5 zP51Nllg~0SQ33wD);%`B$98kd)Tu-vIhR2`V_!~`@hsDrGEp7 WE}(cmn$(m40000 +Ahmad Kahmal +Benoît Minisini diff --git a/gb.sdl.sound/COPYING b/gb.sdl.sound/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.sdl.sound/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.sdl.sound/ChangeLog b/gb.sdl.sound/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.sdl.sound/INSTALL b/gb.sdl.sound/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.sdl.sound/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.sdl.sound/Makefile.am b/gb.sdl.sound/Makefile.am new file mode 100644 index 00000000..4611a218 --- /dev/null +++ b/gb.sdl.sound/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @SDLSOUND_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.sdl.sound/NEWS b/gb.sdl.sound/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.sdl.sound/README b/gb.sdl.sound/README new file mode 100644 index 00000000..e2a7ea07 --- /dev/null +++ b/gb.sdl.sound/README @@ -0,0 +1,10 @@ +Welcome to the "SDL SOUND component for gambas !" +------------------------------------------------- + +Following versions are known to work + +Currently SDL component needs are : + - SDL 1.2.8 with cdrom support + - SDL mixer 1.2.6 + +Have fun :-) diff --git a/gb.sdl.sound/acinclude.m4 b/gb.sdl.sound/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.sdl.sound/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.sdl.sound/component.am b/gb.sdl.sound/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.sdl.sound/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.sdl.sound/configure.ac b/gb.sdl.sound/configure.ac new file mode 100644 index 00000000..e3788f73 --- /dev/null +++ b/gb.sdl.sound/configure.ac @@ -0,0 +1,23 @@ +dnl ---- configure.ac for gb.sdl + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-sdl-sound, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.sdl.sound) +AC_PROG_LIBTOOL + +dnl TODO: add a version test in GB_COMPONENT! +GB_PATH_SDL(1.2.8, [] , [touch DISABLED]) + +dnl check for SDL libs +GB_COMPONENT_PKG_CONFIG( + sdlsound, SDLSOUND, gb.sdl.sound, [src], + SDL_mixer) + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.sdl.sound/gambas.h b/gb.sdl.sound/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.sdl.sound/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.sdl.sound/gb_common.h b/gb.sdl.sound/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.sdl.sound/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.sdl.sound/m4 b/gb.sdl.sound/m4 new file mode 120000 index 00000000..038bb10a --- /dev/null +++ b/gb.sdl.sound/m4 @@ -0,0 +1 @@ +../gb.sdl/m4 \ No newline at end of file diff --git a/gb.sdl.sound/reconf b/gb.sdl.sound/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.sdl.sound/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.sdl.sound/src/Makefile.am b/gb.sdl.sound/src/Makefile.am new file mode 100644 index 00000000..06250363 --- /dev/null +++ b/gb.sdl.sound/src/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.sdl.sound +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.sdl.sound.la + +gb_sdl_sound_la_LIBADD = @SDLSOUND_LIB@ @MATH_LIB@ +gb_sdl_sound_la_LDFLAGS = -module @LD_FLAGS@ @SDLSOUND_LDFLAGS@ +gb_sdl_sound_la_CPPFLAGS = @SDLSOUND_INC@ + +gb_sdl_sound_la_SOURCES = \ + main.h main.c \ + sound.h sound.c \ + cdrom.h cdrom.c diff --git a/gb.sdl.sound/src/cdrom.c b/gb.sdl.sound/src/cdrom.c new file mode 100644 index 00000000..63ff41d0 --- /dev/null +++ b/gb.sdl.sound/src/cdrom.c @@ -0,0 +1,491 @@ +/*************************************************************************** + + cdrom.c + + (c) 2004,2005 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDROM_C + +#include "config.h" + +#include "main.h" +#include "cdrom.h" + +/* CDROM volume control */ +#ifdef OS_LINUX +#include +#include +#include +#endif + +#ifdef OS_BSD /* Is this the good headers ? */ +#include +#include +#include +#endif + +#include +#include "SDL.h" + + +/*************************************************************************** + + CDROMS - detect available CDROMs + +***************************************************************************/ + +BEGIN_METHOD(CDROMS_get, GB_INTEGER index) + + int numdrives = SDL_CDNumDrives(); + + if (numdrives==0) + { + GB.Error("no CDROM found !"); + return; + } + + if (VARG(index)>numdrives) + { + GB.Error("CDROM &1 not available !", VARG(index)); + return; + } + + GB.ReturnConstZeroString(SDL_CDName(VARG(index)-1)); + +END_METHOD + +BEGIN_PROPERTY(CDROMS_count) + + GB.ReturnInteger(SDL_CDNumDrives()); + +END_METHOD + +/*************************************************************************** + + Track - managing a Track + +***************************************************************************/ + +BEGIN_METHOD(TRACK_play, GB_INTEGER start; GB_INTEGER length) + + int track = THIS->index; + int start = VARGOPT(start,0); + int length = VARGOPT(length,0); + + if (CDROM->track[track-1].type!=SDL_AUDIO_TRACK) + return; + + if ((track>CDROM->numtracks) || !track) + return; + + if (start>((CDROM->track[track-1].length)/CD_FPS)) + return; + + if ((start+length)>((CDROM->track[track-1].length)/CD_FPS)) + length = 0; + + if (length==0) + { + if (SDL_CDPlayTracks(CDROM, track-1, start*CD_FPS, 1, 0)==-1) + GB.Error(SDL_GetError()); + } + else + { + if (SDL_CDPlayTracks(CDROM, track-1, start*CD_FPS, 0, length*CD_FPS)==-1) + GB.Error(SDL_GetError()); + } + +END_METHOD + +BEGIN_PROPERTY(TRACK_length) + + if ((THIS->index>CDROM->numtracks) || (CDROM->track[(THIS->index)-1].type!=SDL_AUDIO_TRACK)) + GB.ReturnInteger(0); + else + GB.ReturnInteger((CDROM->track[(THIS->index)-1].length)/CD_FPS); + +END_PROPERTY + +BEGIN_PROPERTY(TRACK_position) + + if (CDROM->cur_track!=((THIS->index)-1)) + GB.ReturnInteger(0); + else + GB.ReturnInteger((CDROM->cur_frame)/CD_FPS); + +END_PROPERTY + +BEGIN_PROPERTY(TRACK_playable) + + if (THIS->index>CDROM->numtracks) + GB.ReturnBoolean(0); + + if (CDROM->track[(THIS->index)-1].type==SDL_AUDIO_TRACK) + GB.ReturnBoolean(1); + else + GB.ReturnBoolean(0); + +END_PROPERTY + +/*************************************************************************** + + Tracks - managing the Tracks + +***************************************************************************/ + +BEGIN_METHOD(TRACKS_get, GB_INTEGER index) + + CDstatus status = SDL_CDStatus(CDROM); + long index = VARG(index); + + if (!CD_INDRIVE(status)) + { + GB.Error("CDROM not available !"); + return; + } + + THIS->index = index; + RETURN_SELF(); + +END_METHOD + +BEGIN_PROPERTY(TRACKS_count) + + SDL_CDStatus(CDROM); + GB.ReturnInteger(CDROM->numtracks); + +END_PROPERTY + +BEGIN_PROPERTY(TRACKS_current) + + CDstatus status = SDL_CDStatus(CDROM); + + if (!CD_INDRIVE(status)) + GB.ReturnInteger(0); + else + GB.ReturnInteger(CDROM->cur_track+1); + +END_PROPERTY + +/*************************************************************************** + + CDrom - use a detected CDROM + +***************************************************************************/ + +BEGIN_METHOD(CDROM_new, GB_INTEGER index) + + int numdrives = SDL_CDNumDrives(); + int index = VARGOPT(index, 0); + + if (numdrives==0) + { + GB.Error("no CDROM found !"); + return; + } + + if (!MISSING(index)) + { + CDROM = SDL_CDOpen(index); + THIS->id = index; + + if (!CDROM) + GB.Error(SDL_GetError()); + + return; + } + else + { + CDROM = SDL_CDOpen(0); + THIS->id = 0; + + if (!CDROM) + GB.Error(SDL_GetError()); + + return; + } + +END_METHOD + +BEGIN_METHOD_VOID(CDROM_free) + + SDL_CDStop(CDROM); + SDL_CDClose(CDROM); + +END_METHOD + +BEGIN_METHOD_VOID(CDROM_eject) + + if (SDL_CDEject(CDROM)==-1) + GB.Error(SDL_GetError()); + +END_METHOD + +BEGIN_METHOD_VOID(CDROM_stop) + + if (SDL_CDStop(CDROM)==-1) + GB.Error(SDL_GetError()); + +END_METHOD + +BEGIN_METHOD_VOID(CDROM_pause) + + if (SDL_CDPause(CDROM)==-1) + GB.Error(SDL_GetError()); + +END_METHOD + +BEGIN_METHOD_VOID(CDROM_resume) + + if (SDL_CDResume(CDROM)==-1) + GB.Error(SDL_GetError()); + +END_METHOD + +BEGIN_METHOD(CDROM_play, GB_INTEGER start; GB_INTEGER tracks) + + CDstatus status = SDL_CDStatus(CDROM); + int start = VARGOPT(start,1); + int tracks = VARGOPT(tracks,0); + + if (status == CD_ERROR) + GB.Error(SDL_GetError()); + + if (!CD_INDRIVE(status)) + return; + + if (start>CDROM->numtracks) + return; + + if ((start+(tracks-1))>CDROM->numtracks) + tracks = (CDROM->numtracks)-start; + + if (SDL_CDPlayTracks(CDROM, start-1, 0, tracks, 0)==-1) + GB.Error(SDL_GetError()); + +END_METHOD + +BEGIN_PROPERTY(CDROM_ready) + + CDstatus status = SDL_CDStatus(CDROM); + + GB.ReturnBoolean(CD_INDRIVE(status)); + +END_PROPERTY + +BEGIN_PROPERTY(CDROM_paused) + + CDstatus status = SDL_CDStatus(CDROM); + + if (status == CD_ERROR) + GB.Error(SDL_GetError()); + + GB.ReturnBoolean(status == CD_PAUSED); + +END_PROPERTY + +BEGIN_PROPERTY(CDROM_playing) + + CDstatus status = SDL_CDStatus(CDROM); + + GB.ReturnBoolean(status == CD_PLAYING); + +END_PROPERTY + +BEGIN_PROPERTY(CDROM_stopped) + + CDstatus status = SDL_CDStatus(CDROM); + + GB.ReturnBoolean(status == CD_STOPPED); + +END_PROPERTY + +BEGIN_PROPERTY(CDROM_length) + + CDstatus status = SDL_CDStatus(CDROM); + int i, length = 0; + + if (!CD_INDRIVE(status)) + { + GB.ReturnInteger(0); + } + else + { + for (i=0;i<((CDROM->numtracks)-1);i++) + { + if (CDROM->track[i].type==SDL_AUDIO_TRACK) + length = length + (CDROM->track[i].length); + } + + GB.ReturnInteger(length/CD_FPS); + } + +END_PROPERTY + +BEGIN_PROPERTY(CDROM_position) + + CDstatus status = SDL_CDStatus(CDROM); + int i, length =0; + + if (!CD_INDRIVE(status)) + GB.ReturnInteger(0); + else + { + for (i=0;i<(CDROM->cur_track);i++) + { + if (CDROM->track[i].type==SDL_AUDIO_TRACK) + length = length + (CDROM->track[i].length); + } + + GB.ReturnInteger(((CDROM->cur_frame)+length)/CD_FPS); + } + +END_PROPERTY + +BEGIN_PROPERTY(CDROM_volume) + +#ifdef OS_LINUX + + struct cdrom_volctrl vol; + + if (READ_PROPERTY) + { + ioctl(CDROM->id, CDROMVOLREAD, (void *) &vol); + GB.ReturnInteger(vol.channel0); + } + else + { + int volume = VPROP(GB_INTEGER); + + if (volume<0) + volume = 0; + + if (volume>255) + volume = 255; + + vol.channel0 = volume; + vol.channel1 = volume; + ioctl(CDROM->id, CDROMVOLCTRL,(void *) &vol); + } + +#endif + +#ifdef OS_BSD + + struct ioc_vol vol; + + if (READ_PROPERTY) + { + ioctl(CDROM->id, CDIOCGETVOL, &vol); + GB.ReturnInteger(vol.vol[0]); + } + else + { + int volume = VPROP(GB_INTEGER); + + if (volume <0) + volume = 0; + if (volume>255) + volume = 255; + + vol.vol[0] = volume; + vol.vol[1] = volume; + vol.vol[2] = 0; + vol.vol[3] = 0; + ioctl(CDROM->id, CDIOCSETVOL, &vol); + } + +#endif + +#if !defined(OS_LINUX) && !defined(OS_BSD) + + if (!READ_PROPERTY) + GB.ReturnInteger(0); + +#endif + +END_PROPERTY + +/**************************************************************************/ + +GB_DESC Cquerycdrom[] = +{ + GB_DECLARE("CDRoms", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_get", "s", CDROMS_get, "(Index)i"), + GB_STATIC_PROPERTY_READ("Count", "i", CDROMS_count), + + GB_END_DECLARE +}; + +GB_DESC Ctrack[] = +{ + GB_DECLARE(".Track",0), GB_VIRTUAL_CLASS(), + + GB_METHOD("Play", NULL, TRACK_play, "[(Start)i(Length)i]"), + + GB_PROPERTY_READ("Length", "i", TRACK_length), + GB_PROPERTY_READ("Position", "i", TRACK_position), + GB_PROPERTY_READ("Playable", "b", TRACK_playable), + + GB_END_DECLARE +}; + +GB_DESC Ctracks[] = +{ + GB_DECLARE(".Tracks",0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", ".Track", TRACKS_get, "(Index)i"), + + GB_PROPERTY_READ("Count", "i", TRACKS_count), + GB_PROPERTY_READ("Current", "i", TRACKS_current), + + GB_END_DECLARE +}; + +GB_DESC Ccdrom[] = +{ + GB_DECLARE("CDRom", sizeof(CCDROM)), + + GB_METHOD("_new", NULL, CDROM_new, "[(Index)i]"), + GB_METHOD("_free", NULL, CDROM_free, NULL), + + /* cdrom commands */ + GB_METHOD("Eject", NULL, CDROM_eject, NULL), + GB_METHOD("Stop", NULL, CDROM_stop, NULL), + GB_METHOD("Pause", NULL, CDROM_pause, NULL), + GB_METHOD("Resume", NULL, CDROM_resume, NULL), + GB_METHOD("Play", NULL, CDROM_play, "[(StartTrack)i(NbrTracks)i]"), + + /* cdrom status */ + GB_PROPERTY_READ("Ready", "b", CDROM_ready), + GB_PROPERTY_READ("Paused", "b", CDROM_paused), + GB_PROPERTY_READ("Playing", "b", CDROM_playing), + GB_PROPERTY_READ("Stopped", "b", CDROM_stopped), + GB_PROPERTY_READ("Length", "i", CDROM_length), + GB_PROPERTY_READ("Position", "i", CDROM_position), + + GB_PROPERTY("Volume", "i", CDROM_volume), + + /* Track management */ + GB_PROPERTY_SELF("Tracks", ".Tracks"), + + GB_END_DECLARE +}; diff --git a/gb.sdl.sound/src/cdrom.h b/gb.sdl.sound/src/cdrom.h new file mode 100644 index 00000000..0751a3aa --- /dev/null +++ b/gb.sdl.sound/src/cdrom.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + cdrom.h + + (c) 2004,2005 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDROM_H +#define __CDROM_H + +#include "gambas.h" + +#include "SDL.h" + +typedef + struct { + GB_BASE ob; + SDL_CD *cdrom; + int id; + long index; + } + CCDROM; + +#ifndef __CDROM_C +extern GB_DESC Cquerycdrom[]; +extern GB_DESC Ctrack[]; +extern GB_DESC Ctracks[]; +extern GB_DESC Ccdrom[]; +#else + +#define CDROM ((CCDROM *)_object)->cdrom +#define THIS ((CCDROM *)_object) + +#endif /* __CDROM_C */ + +#endif /* __CDROM_H */ diff --git a/gb.sdl.sound/src/gb.sdl.sound.component b/gb.sdl.sound/src/gb.sdl.sound.component new file mode 100644 index 00000000..13426a30 --- /dev/null +++ b/gb.sdl.sound/src/gb.sdl.sound.component @@ -0,0 +1,9 @@ +[Component] +Key=gb.sdl.sound +Name=Sound library based on SDL +Name[fr]=Bibliothèque sonore basée sur SDL +Name[pl]=Biblioteka dźwięku bazująca na SDL +Name[es]=Biblioteca de sonido basada en SDL +Name[tr]=SDL tabanlı ses kitaplığı +Author=Benoît Minisini,Ahmad Kahmal,Laurent Carlier +State=Deprecated diff --git a/gb.sdl.sound/src/main.c b/gb.sdl.sound/src/main.c new file mode 100644 index 00000000..0e6b876a --- /dev/null +++ b/gb.sdl.sound/src/main.c @@ -0,0 +1,97 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include + +#include "sound.h" +#include "cdrom.h" +#include "main.h" + +#include "SDL.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CSoundDesc, + CMusicDesc, + CChannelsDesc, + CChannelDesc, + //CMusic, + Cquerycdrom, + Ctracks, Ctrack, + Ccdrom, + + NULL +}; + + +int EXPORT GB_INIT(void) +{ + Uint32 sysInit = SDL_WasInit(SDL_INIT_EVERYTHING); + + // if video is defined, sdl was init by gb.sdl component ! + if (sysInit & SDL_INIT_VIDEO) + { + if (SDL_InitSubSystem(SDL_INIT_AUDIO | SDL_INIT_CDROM)<0) + { + GB.Error(SDL_GetError()); + return 0; + } + } + else + { + if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_CDROM | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE)<0) + { + GB.Error(SDL_GetError()); + return 0; + } + } + + SOUND_init(); + + return -1; +} + + +void EXPORT GB_EXIT() +{ + Uint32 sysInit = SDL_WasInit(SDL_INIT_EVERYTHING); + SOUND_exit(); + + // if video is defined, gb.sdl component still not closed ! + if (sysInit & SDL_INIT_VIDEO) + SDL_QuitSubSystem(SDL_INIT_AUDIO | SDL_INIT_CDROM); + else + SDL_Quit(); + +} + + + + diff --git a/gb.sdl.sound/src/main.h b/gb.sdl.sound/src/main.h new file mode 100644 index 00000000..ab5d0f7d --- /dev/null +++ b/gb.sdl.sound/src/main.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif /* __MAIN_H */ diff --git a/gb.sdl.sound/src/sound.c b/gb.sdl.sound/src/sound.c new file mode 100644 index 00000000..2c6e78c1 --- /dev/null +++ b/gb.sdl.sound/src/sound.c @@ -0,0 +1,604 @@ +/*************************************************************************** + + sound.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __SOUND_C + +#include + +#include "gb_common.h" + +#include "sound.h" +#include "main.h" + +static SOUND_INFO info = { 0 }; + +static CCHANNEL *channel_cache[MAX_CHANNEL] = { 0 }; +static int channel_count; + +static double music_ref_time = 0; +static double music_ref_pos = 0; + +static int _ch_pipe[2]; +static int _ch_playing = 0; +static int _init = 0; + +#if 0 +static void musicDone() +{ +/* This is the function that we told SDL_Mixer to call when the music + was finished. In our case, we're going to simply unload the music + as though the player wanted it stopped. In other applications, a + different music file might be loaded and played. */ + + /* BM: You cannot raise an event from a static class at the moment. */ + + printf("The music stopped playing NOW\n"); //This should raise a gambas event! +} +#endif + +static void set_audio_properties() +{ + /* We're going to be requesting certain things from our audio + device, so we set them up beforehand */ + + //Of course this should all come from gambas properties! + info.rate = 44100; //could be: 22050; + info.format = MIX_DEFAULT_FORMAT; //16-bit stereo + info.channels = 2; //The only one that opens on my machine! SB_PCI128 + // BM: This is stereo. It does not matter. + info.buffers = 4096; + +} + +static void free_channel(CCHANNEL *ch) +{ + if (!ch->sound) + return; + + GB.Unref(POINTER(&ch->sound)); + ch->sound = NULL; + ch->free = FALSE; + + _ch_playing--; + if (_ch_playing == 0) + GB.Watch(_ch_pipe[0], GB_WATCH_NONE, (void *)0, 0); +} + +static void free_finished_channels(void) +{ + int i; + CCHANNEL *ch; + char foo; + + if (read(_ch_pipe[0], &foo, 1) != 1) + return; + + for (i = 0; i < MAX_CHANNEL; i++) + { + ch = channel_cache[i]; + if (ch && ch->free) + free_channel(ch); + } +} + +static void channel_finished(int channel) +{ + CCHANNEL *ch = channel_cache[channel]; + char foo = 0; + + if (!ch) + return; + + /*printf("channel_finished: %p\n", ch->sound);*/ + + // TODO: do not use GB.Post(), because we are not in the main thread. Write to a pipe and watch it in the main thread + ch->free = write(_ch_pipe[1], &foo, 1) == 1; +} + +static int play_channel(int channel, CSOUND *sound, int loops, int fadein) +{ + _ch_playing++; + if (_ch_playing == 1) + GB.Watch(_ch_pipe[0], GB_WATCH_READ , (void *)free_finished_channels,0); + + if (fadein > 0) + return Mix_FadeInChannel(channel, sound->chunk, loops, fadein); + else + return Mix_PlayChannel(channel, sound->chunk, loops); +} + +static bool start_sound_engine() +{ + /* This is where we open up our audio device. Mix_OpenAudio takes + as its parameters the audio format we'd /like/ to have. */ + if(Mix_OpenAudio(info.rate, info.format, info.channels, info.buffers)) + { + GB.Error("Unable to open audio"); + return TRUE; + } + + if (pipe(_ch_pipe)) + { + GB.Error("Unable to initialize channel pipe"); + return TRUE; + } + + Mix_QuerySpec(&info.rate, &info.format, &info.channels); + //fprintf(stderr, "Mix_QuerySpec: %d %d %d\n", info.rate, info.format, info.channels); + + channel_count = Mix_AllocateChannels(-1); + + Mix_ChannelFinished(channel_finished); + + return FALSE; +} + + +static void free_music(void) +{ + if (!info.music) + return; + + Mix_HaltMusic(); + Mix_RewindMusic(); + Mix_FreeMusic(info.music); + info.music = NULL; +} + + +static void stop_sound_engine() +{ + if (_ch_playing) + GB.Watch(_ch_pipe[0], GB_WATCH_NONE, (void *)0, 0); + close(_ch_pipe[0]); + close(_ch_pipe[1]); + free_music(); + Mix_CloseAudio(); +} + +void SOUND_init(void) +{ + _init++; + if (_init > 1) + return; + + set_audio_properties(); //Fill audio structures with gambas properties + start_sound_engine(); //Start the sound engine +} + + +void SOUND_exit(void) +{ + _init--; + if (_init > 0) + return; + + stop_sound_engine(); +} + + +static void return_channel(int channel, CSOUND *sound) +{ + CCHANNEL *ch = NULL; + + if (channel < 0 || channel >= channel_count) + { + if (sound) + GB.Unref(POINTER(&sound)); + + GB.ReturnNull(); + return; + } + + ch = channel_cache[channel]; + if (!ch) + { + ch = GB.New(GB.FindClass("Channel"), NULL, NULL); + channel_cache[channel] = ch; + ch->channel = channel; + GB.Ref(ch); + } + + free_channel(ch); + if (sound) + ch->sound = sound; + + GB.ReturnObject(ch); +} + + +static double volume_from_sdl(int vol) +{ + return log(1 + (M_E - 1) * (double)vol / MIX_MAX_VOLUME); +} + +static int volume_to_sdl(double vol) +{ + return (exp(vol) - 1) / (M_E - 1) * MIX_MAX_VOLUME; +} + +/*************************************************************************** + + Sound + +***************************************************************************/ + +#define THIS ((CSOUND *)_object) + + +BEGIN_METHOD(CSOUND_new, GB_STRING file) + + char *addr; + int len; + + if (GB.LoadFile(STRING(file), LENGTH(file), &addr, &len)) + return; + + THIS->chunk = Mix_LoadWAV_RW(SDL_RWFromMem(addr, len), TRUE); + + GB.ReleaseFile(addr, len); + + if (!THIS->chunk) + GB.Error(Mix_GetError()); + +END_METHOD + + +BEGIN_METHOD_VOID(CSOUND_free) + + Mix_FreeChunk(THIS->chunk); + THIS->chunk = NULL; + +END_METHOD + + +BEGIN_METHOD(CSOUND_play, GB_INTEGER loops; GB_FLOAT fadein) + + int loops = VARGOPT(loops, 0); + int channel; + + GB.Ref(THIS); + channel = play_channel(-1, THIS, loops, MISSING(fadein) ? 0 : (int)(VARG(fadein) * 1000)); + return_channel(channel, THIS); + +END_METHOD + + +GB_DESC CSoundDesc[] = +{ + GB_DECLARE("Sound", sizeof(CSOUND)), + + //GB_STATIC_METHOD("_init", NULL, CSOUND_init, NULL), + //GB_STATIC_METHOD("_exit", NULL, CSOUND_exit, NULL), + + GB_METHOD("_new", NULL, CSOUND_new, "(File)s"), + GB_METHOD("_free", NULL, CSOUND_free, NULL), + + //GB_PROPERTY("Volume", "e", CSOUND_volume), + + GB_METHOD("Play", "Channel", CSOUND_play, "[(Loops)i(FadeIn)f]"), + + GB_END_DECLARE +}; + +/*************************************************************************** + + Channel + +***************************************************************************/ + +#undef THIS +#define THIS ((CCHANNEL *)_object) + + +BEGIN_METHOD(CCHANNEL_get, GB_INTEGER index) + + return_channel(VARG(index), NULL); + +END_METHOD + + +BEGIN_PROPERTY(CCHANNEL_count) + + int nchan; + + if (READ_PROPERTY) + GB.ReturnInteger(Mix_AllocateChannels(-1)); + else + { + nchan = VPROP(GB_INTEGER); + if (nchan < 0) + nchan = 0; + else if (nchan >= MAX_CHANNEL) + nchan = MAX_CHANNEL; + + Mix_AllocateChannels(nchan); + channel_count = Mix_AllocateChannels(-1); + } + +END_PROPERTY + + +BEGIN_METHOD(CCHANNEL_play, GB_OBJECT sound; GB_INTEGER loops; GB_FLOAT fadein) + + CSOUND *sound; + + if (Mix_Paused(THIS->channel)) + Mix_Resume(THIS->channel); + + sound = VARGOPT(sound, NULL); + if (!sound) + return; + + /*printf("Ref %p\n", sound);*/ + GB.Ref(sound); + THIS->sound = sound; + play_channel(THIS->channel, sound, VARGOPT(loops, 0), MISSING(fadein) ? 0 : (int)(VARG(fadein) * 1000)); + +END_METHOD + + +BEGIN_METHOD_VOID(CCHANNEL_pause) + + Mix_Pause(THIS->channel); + +END_METHOD + + +BEGIN_METHOD(CCHANNEL_stop, GB_FLOAT fadeout) + + if (MISSING(fadeout)) + Mix_HaltChannel(THIS->channel); + else + Mix_FadeOutChannel(THIS->channel, (int)(VARG(fadeout) * 1000)); + +END_METHOD + + +BEGIN_METHOD_VOID(CCHANNEL_exit) + + int i; + CCHANNEL *ch; + + for (i = 0; i < MAX_CHANNEL; i++) + { + ch = channel_cache[i]; + if (!ch) + continue; + + free_channel(ch); + GB.Unref(POINTER(&ch)); + } + +END_METHOD + + +BEGIN_PROPERTY(CCHANNEL_volume) + + int channel; + + channel = THIS ? THIS->channel : -1; + + if (READ_PROPERTY) + GB.ReturnFloat(volume_from_sdl(Mix_Volume(channel, -1))); + else + Mix_Volume(channel, volume_to_sdl(VPROP(GB_FLOAT))); + +END_PROPERTY + + + +GB_DESC CChannelDesc[] = +{ + GB_DECLARE("Channel", sizeof(CCHANNEL)), GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("_exit", NULL, CCHANNEL_exit, NULL), + //GB_STATIC_PROPERTY("Volume", "e", CCHANNEL_volume), + GB_METHOD("Play", NULL, CCHANNEL_play, "[(Sound)Sound;(Loops)i(FadeIn)f]"), + GB_METHOD("Pause", NULL, CCHANNEL_pause, NULL), + GB_METHOD("Stop", NULL, CCHANNEL_stop, "[(FadeOut)f]"), + + GB_PROPERTY("Volume", "f", CCHANNEL_volume), + + GB_END_DECLARE +}; + +GB_DESC CChannelsDesc[] = +{ + GB_DECLARE("Channels", 0), GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("_get", "Channel", CCHANNEL_get, "(Index)i"), + //GB_STATIC_METHOD("_next", "Channel", CCHANNEL_next, NULL), + + GB_STATIC_PROPERTY("Count", "i", CCHANNEL_count), + GB_STATIC_PROPERTY("Volume", "f", CCHANNEL_volume), + + GB_END_DECLARE +}; + +/*************************************************************************** + + Music + +***************************************************************************/ + +static double get_music_pos(void) +{ + double time; + + if (Mix_PlayingMusic()) + { + if (!Mix_PausedMusic()) + { + GB.GetTime(&time, FALSE); + return music_ref_pos + time - music_ref_time; + } + else + return music_ref_pos; + } + else + return 0; +} + + +BEGIN_METHOD(CMUSIC_load, GB_STRING file) + + free_music(); + + /* Note that the music cannot be stored inside the project ! */ + + info.music = Mix_LoadMUS(GB.RealFileName(STRING(file), LENGTH(file))); + if (!info.music) + GB.Error(Mix_GetError()); + + music_ref_pos = 0; + music_ref_time = 0; + +END_METHOD + + +BEGIN_METHOD(CMUSIC_play, GB_INTEGER loops; GB_FLOAT fadein) + + double fadevalue=0; + + if (!info.music) + return; + + GB.GetTime(&music_ref_time, FALSE); + + if (Mix_PausedMusic()) + { + Mix_ResumeMusic(); + return; + } + + /* We want to know when our music has stopped playing so we + can free it up and set 'music' back to NULL. SDL_Mixer + provides us with a callback routine we can use to do + exactly that */ + + /*Mix_HookMusicFinished(musicDone);*/ + + //The 'Looping' param should be optional in gambas, default=0. Don't know how? + //BM Now do you know ? ;-) + + fadevalue = VARGOPT(fadein, 0) * 1000; + // if fadevalue is too small -> music doesn't want to play ! + if (fadevalue<100) + { + fadevalue=0; + } + + Mix_FadeInMusic(info.music, VARGOPT(loops, 1), fadevalue); + +END_METHOD + + +BEGIN_METHOD_VOID(CMUSIC_pause) + + music_ref_pos = get_music_pos(); + Mix_PauseMusic(); + +END_METHOD + + +BEGIN_METHOD(CMUSIC_stop, GB_FLOAT fadeout) + + if (MISSING(fadeout)) + Mix_HaltMusic(); + else + Mix_FadeOutMusic(VARG(fadeout)*1000); + + music_ref_pos = 0; + +END_METHOD + +BEGIN_PROPERTY(CMUSIC_pos) + + double pos; + + if (READ_PROPERTY) + { + GB.ReturnFloat(get_music_pos()); + } + else + { + pos = VPROP(GB_FLOAT); + Mix_RewindMusic(); + if (Mix_SetMusicPosition(pos) == 0) + music_ref_pos = pos; + else + music_ref_pos = 0; + GB.GetTime(&music_ref_time, FALSE); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CMUSIC_volume) + + if (READ_PROPERTY) + GB.ReturnFloat(volume_from_sdl(Mix_VolumeMusic(-1))); + else + Mix_VolumeMusic(volume_to_sdl(VPROP(GB_FLOAT))); + +END_PROPERTY + + +BEGIN_PROPERTY(Music_State) + + if (Mix_PlayingMusic()) + { + if (Mix_PausedMusic()) + GB.ReturnInteger(2); + else + GB.ReturnInteger(1); + } + else + GB.ReturnInteger(0); + +END_PROPERTY + +GB_DESC CMusicDesc[] = +{ + GB_DECLARE("Music", 0), + + //GB_STATIC_METHOD("_init", NULL, CSOUND_init, NULL), + //GB_STATIC_METHOD("_exit", NULL, CSOUND_exit, NULL), + + GB_STATIC_METHOD("Load", NULL, CMUSIC_load, "(File)s"), + GB_STATIC_METHOD("Play", NULL, CMUSIC_play, "[(Loops)i(FadeIn)f]"), + GB_STATIC_METHOD("Pause", NULL, CMUSIC_pause, NULL), + GB_STATIC_METHOD("Stop", NULL, CMUSIC_stop, "[(FadeOut)f]"), + + GB_STATIC_PROPERTY("Volume", "f", CMUSIC_volume), + GB_STATIC_PROPERTY("Pos", "f", CMUSIC_pos), + + GB_STATIC_PROPERTY_READ("State", "i", Music_State), + + GB_CONSTANT("Stopped", "i", 0), + GB_CONSTANT("Playing", "i", 1), + GB_CONSTANT("Paused", "i", 2), + + GB_END_DECLARE +}; diff --git a/gb.sdl.sound/src/sound.h b/gb.sdl.sound/src/sound.h new file mode 100644 index 00000000..b3fef122 --- /dev/null +++ b/gb.sdl.sound/src/sound.h @@ -0,0 +1,72 @@ +/*************************************************************************** + + sound.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SOUND_H +#define __SOUND_H + +#include "gambas.h" + +#include "SDL.h" +#include "SDL_mixer.h" + +#ifndef __SOUND_C +extern GB_DESC CSoundDesc[]; +extern GB_DESC CMusicDesc[]; +extern GB_DESC CChannelDesc[]; +extern GB_DESC CChannelsDesc[]; +#else + +typedef + struct { + Mix_Music *music; + int rate; + Uint16 format; + int channels; + int buffers; + } + SOUND_INFO; + +#endif + +#define MAX_CHANNEL 64 + +typedef + struct { + GB_BASE ob; + Mix_Chunk *chunk; + } + CSOUND; + +typedef + struct { + GB_BASE ob; + int channel; + CSOUND *sound; + char free; + } + CCHANNEL; + +void SOUND_init(void); +void SOUND_exit(void); + +#endif /* __SOUND_H */ diff --git a/gb.sdl/AUTHORS b/gb.sdl/AUTHORS new file mode 100644 index 00000000..27dce17c --- /dev/null +++ b/gb.sdl/AUTHORS @@ -0,0 +1,6 @@ +Codes/ideas/contributions (in alphabetic order): + +Fabien Bodard +Daniel Campos FernànDez +Laurent Carlier +Benoît Minisini diff --git a/gb.sdl/COPYING b/gb.sdl/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.sdl/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.sdl/ChangeLog b/gb.sdl/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.sdl/INSTALL b/gb.sdl/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.sdl/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.sdl/Makefile.am b/gb.sdl/Makefile.am new file mode 100644 index 00000000..29bfebd9 --- /dev/null +++ b/gb.sdl/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @SDL_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.sdl/NEWS b/gb.sdl/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.sdl/README b/gb.sdl/README new file mode 100644 index 00000000..9070dfba --- /dev/null +++ b/gb.sdl/README @@ -0,0 +1,12 @@ +Welcome to the "SDL component for gambas !" +------------------------------------------- + +Following versions are known to work + +Currently SDL component needs are : + - SDL 1.2.8 + - SDL image 1.2.4 + - OpenGL support + - Xcursor support (provide with Xfree 4.x/xorg) + +Have fun :-) diff --git a/gb.sdl/acinclude.m4 b/gb.sdl/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.sdl/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.sdl/component.am b/gb.sdl/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.sdl/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.sdl/configure.ac b/gb.sdl/configure.ac new file mode 100644 index 00000000..0c2dac1b --- /dev/null +++ b/gb.sdl/configure.ac @@ -0,0 +1,48 @@ +dnl ---- configure.ac for gb.sdl + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-sdl, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.sdl) +AC_PROG_LIBTOOL + +GB_CHECK_XWINDOW + +dnl TODO: add a version test in GB_COMPONENT! +dnl GB_PATH_SDL([1.2.8], [], [touch DISABLED]) + +dnl check Xcursor support +dnl AC_CHECK_LIB(Xcursor, XcursorLibraryLoadCursor, +dnl [echo -n +dnl LDFLAGS="$LDFLAGS -lXcursor"], +dnl [AC_MSG_WARN([libXcursor not found. Check 'config.log' for more details.]) +dnl touch DISABLED], +dnl $X_LIBS) + +dnl check for SDL libs +dnl GB_COMPONENT( +dnl sdl, +dnl SDL, +dnl [SDL], +dnl [src], +dnl [GB_FIND(SDL_opengl.h SDL.h SDL_ttf.h, `sdl-config --prefix`, include/SDL)], +dnl [GB_FIND(libSDL_ttf.$SHLIBEXT libGLEW.$SHLIBEXT, /usr /usr/X11R6 /usr/local `sdl-config --prefix`, lib)], +dnl [$X_LIBS -lSDL_ttf -lGLEW -lXcursor], +dnl [$SDL_CFLAGS]) + +GB_COMPONENT_SEARCH( + sdl, SDL, gb.sdl, [src], + 'sdl > 1.2.8' SDL_ttf glew xcursor x11, + [GB_FIND(GL/glew.h SDL_ttf.h X11/Xcursor/Xcursor.h, /usr /usr/local `sdl-config --prefix`, include include/SDL)], + [GB_FIND(libSDL_ttf.$SHLIBEXT libGLEW.$SHLIBEXT libXcursor.$SHLIBEXT, /usr /usr/local `sdl-config --prefix`, lib)], + [$X_LIBS -lSDL_ttf -lGLEW -lXcursor], + [$SDL_CFLAGS] +) + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.sdl/gambas.h b/gb.sdl/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.sdl/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.sdl/gb.image.h b/gb.sdl/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.sdl/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.sdl/gb_common.h b/gb.sdl/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.sdl/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.sdl/m4 b/gb.sdl/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.sdl/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.sdl/reconf b/gb.sdl/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.sdl/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.sdl/src/Cconst.cpp b/gb.sdl/src/Cconst.cpp new file mode 100644 index 00000000..b38f5637 --- /dev/null +++ b/gb.sdl/src/Cconst.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** + + Cconst.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCONST_CPP + +#include "gambas.h" + +#include "main.h" +#include "Cconst.h" +#include "SDL_h.h" + +GB_DESC CLine[] = +{ + GB_DECLARE("Line", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", SDL::NoLine), + GB_CONSTANT("Solid", "i", SDL::SolidLine), + GB_CONSTANT("Dash", "i", SDL::DashLine), + GB_CONSTANT("Dot", "i", SDL::DotLine), + GB_CONSTANT("DashDot", "i", SDL::DashDotLine), + GB_CONSTANT("DashDotDot", "i", SDL::DashDotDotLine), + + GB_END_DECLARE +}; + +GB_DESC CFill[] = +{ + GB_DECLARE("Fill", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", SDL::NoFill), + GB_CONSTANT("Solid", "i", SDL::SolidFill), + GB_CONSTANT("Dense94", "i", SDL::Dense1Fill), + GB_CONSTANT("Dense88", "i", SDL::Dense2Fill), + GB_CONSTANT("Dense63", "i", SDL::Dense3Fill), + GB_CONSTANT("Dense50", "i", SDL::Dense4Fill), + GB_CONSTANT("Dense37", "i", SDL::Dense5Fill), + GB_CONSTANT("Dense12", "i", SDL::Dense6Fill), + GB_CONSTANT("Dense6", "i", SDL::Dense7Fill), + + GB_CONSTANT("Horizontal", "i", SDL::HorizontalFill), + GB_CONSTANT("Vertical", "i", SDL::VerticalFill), + GB_CONSTANT("Cross", "i", SDL::CrossFill), + GB_CONSTANT("BackDiagonal", "i", SDL::BackDiagFill), + GB_CONSTANT("Diagonal", "i", SDL::DiagFill), + GB_CONSTANT("CrossDiagonal", "i", SDL::DiagCrossFill), + + GB_END_DECLARE +}; + diff --git a/gb.sdl/src/Cconst.h b/gb.sdl/src/Cconst.h new file mode 100644 index 00000000..7f7d9e26 --- /dev/null +++ b/gb.sdl/src/Cconst.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + Cconst.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCONST_H +#define __CCONST_H + +#include "gambas.h" + +#ifndef __CCONST_CPP +extern GB_DESC CLine[]; +extern GB_DESC CFill[]; +#endif + +#endif diff --git a/gb.sdl/src/Cdesktop.cpp b/gb.sdl/src/Cdesktop.cpp new file mode 100644 index 00000000..72b9172c --- /dev/null +++ b/gb.sdl/src/Cdesktop.cpp @@ -0,0 +1,51 @@ +/*************************************************************************** + + Cdesktop.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDESKTOP_CPP + +#include "Cdesktop.h" +#include "SDLapp.h" + +BEGIN_PROPERTY(CDESKTOP_width) + + GB.ReturnInteger(SDLapp->DesktopWidth()); + +END_PROPERTY + +BEGIN_PROPERTY(CDESKTOP_height) + + GB.ReturnInteger(SDLapp->DesktopHeight()); + +END_PROPERTY + +GB_DESC CDesktop[] = +{ + GB_DECLARE("Desktop", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY_READ("W", "i", CDESKTOP_width), + GB_STATIC_PROPERTY_READ("H", "i", CDESKTOP_height), + GB_STATIC_PROPERTY_READ("Width", "i", CDESKTOP_width), + GB_STATIC_PROPERTY_READ("Height", "i", CDESKTOP_height), + + GB_END_DECLARE +}; diff --git a/gb.sdl/src/Cdesktop.h b/gb.sdl/src/Cdesktop.h new file mode 100644 index 00000000..f518967f --- /dev/null +++ b/gb.sdl/src/Cdesktop.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + Cdesktop.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDESKTOP_H +#define __CDESKTOP_H + +#include "gambas.h" +#include "main.h" + +#ifndef __CDESKTOP_CPP +extern GB_DESC CDesktop[]; +#endif + +#endif diff --git a/gb.sdl/src/Cdraw.cpp b/gb.sdl/src/Cdraw.cpp new file mode 100644 index 00000000..d65c99a4 --- /dev/null +++ b/gb.sdl/src/Cdraw.cpp @@ -0,0 +1,367 @@ +/*************************************************************************** + + Cdraw.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDRAW_CPP + +#include "main.h" +#include "Cdraw.h" +#include "Cwindow.h" +#include "Cimage.h" + +#define DRAW_STACK_MAX 8 + +static CDRAW draw_stack[DRAW_STACK_MAX]; +static CDRAW *draw_current = 0; +static CFONT *_default_font = NULL; + +#define THIS (draw_current) +#define GFX (THIS->graphic) +#define FONT (THIS->font) +#define WINDOWID(object) ((CWINDOW *)object)->id +#define IMAGEID(object) (CIMAGE_get((CIMAGE *)object)) + +/**************************************************************************/ + +static CFONT *get_default_font() +{ + if (!_default_font) + { + _default_font = (CFONT *)GB.New(GB.FindClass("Font"), NULL, NULL); + GB.Ref(_default_font); + } + + return _default_font; +} + +static bool check_graphic(void) +{ + if (UNLIKELY(THIS == NULL)) + { + GB.Error("No device"); + return true; + } + else + return false; +} + +#define CHECK_GRAPHIC() if (check_graphic()) return + +void DRAW_begin(void *device) +{ + if (THIS >= &draw_stack[DRAW_STACK_MAX - 1]) { + GB.Error("Too many nested drawings"); + return; + } + + if (GB.CheckObject(device)) + return; + + if (THIS == 0) + THIS = draw_stack; + else + THIS++; + + THIS->backcolor = 0x00000000; + THIS->forecolor = 0x00FFFFFF; + + if (FONT) + GB.Unref(POINTER(&FONT)); + + FONT = get_default_font(); + GB.Ref(FONT); + + if (GB.Is(device, CLASS_Window)) { + THIS->device = device; + THIS->graphic = new SDLgfx(WINDOWID(device)); + GB.Ref(THIS->device); + return; + } +#if 0 + if (GB.Is(device, CLASS_Image)) + { + THIS->device = device; + THIS->graphic = new SDLgfx(IMAGEID(device)); + GB.Ref(THIS->device); + return; + } +#endif + GB.Error("Device not supported !"); +} + + void DRAW_end() +{ + if (!THIS) + return; + + delete THIS->graphic; + GB.Unref(POINTER(&THIS->device)); + THIS->device = 0; + + if (FONT) + GB.Unref(POINTER(&FONT)); + + FONT = 0; + + if (THIS == draw_stack) + THIS = 0; + else + THIS--; +} + +BEGIN_METHOD_VOID(Draw_exit) + + if (_default_font) + GB.Unref(POINTER(&_default_font)); + +END_METHOD + +BEGIN_METHOD(CDRAW_begin, GB_OBJECT device) + + void *device = VARG(device); + DRAW_begin(device); + +END_METHOD + +BEGIN_METHOD_VOID(CDRAW_end) + + DRAW_end(); + +END_METHOD + +BEGIN_METHOD(CDRAW_rotate, GB_FLOAT angle) + + CHECK_GRAPHIC(); + + GFX->Rotate(VARG(angle)); + +END_METHOD + +BEGIN_METHOD(CDRAW_scale, GB_FLOAT x; GB_FLOAT y) + + CHECK_GRAPHIC(); + + GFX->Scale(VARG(x), VARG(y)); + +END_METHOD + +BEGIN_METHOD(CDRAW_point, GB_INTEGER x; GB_INTEGER y) + + CHECK_GRAPHIC(); + + GFX->SetColor(THIS->forecolor); + GFX->DrawPixel(VARG(x), VARG(y)); + +END_METHOD + +BEGIN_METHOD(CDRAW_line, GB_INTEGER x1; GB_INTEGER y1; GB_INTEGER x2; GB_INTEGER y2) + + CHECK_GRAPHIC(); + + GFX->SetColor(THIS->forecolor); + GFX->DrawLine(VARG(x1), VARG(y1), VARG(x2), VARG(y2)); + +END_METHOD + +BEGIN_METHOD(CDRAW_rect, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + CHECK_GRAPHIC(); + + GFX->SetColor(THIS->forecolor); + GFX->DrawRect(VARG(x), VARG(y), VARG(w), VARG(h)); + +END_METHOD + +BEGIN_METHOD(CDRAW_ellipse, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + CHECK_GRAPHIC(); + + GFX->SetColor(THIS->forecolor); + GFX->DrawEllipse(VARG(x), VARG(y), VARG(w), VARG(h)); + +END_METHOD + +BEGIN_METHOD(CDRAW_text, GB_STRING text; GB_INTEGER x; GB_INTEGER y) + + int scale; + + CHECK_GRAPHIC(); + + if (GB.CheckObject(THIS->font)) + return; + + SDLsurface *txt = FONT->font->RenderText(STRING(text), LENGTH(text)); + if (!txt) + return; + +/* if (THIS->backcolor != 0) + { + int fill = GFX->GetFillStyle(); + GFX->SetFillStyle(SDL::SolidFill); + GFX->SetColor(THIS->backcolor); + GFX->DrawRect(VARG(x), VARG(y), txt->GetWidth(), txt->GetHeight()); + GFX->SetFillStyle(fill); + }*/ + + GFX->SetColor(THIS->forecolor); + + scale = FONT->font->GetScale(); + + if (scale == 1) + GFX->Blit(txt, VARG(x), VARG(y)); + else + GFX->Blit(txt, VARG(x), VARG(y), 0, 0, -1, -1, txt->GetWidth() * scale, txt->GetHeight() * scale, true); + + txt->Unref(); + +END_METHOD + +BEGIN_METHOD(CDRAW_image, GB_OBJECT image; GB_INTEGER x; GB_INTEGER y; GB_INTEGER width; GB_INTEGER height; + GB_INTEGER srcx; GB_INTEGER srcy; GB_INTEGER srcw; GB_INTEGER srch) + + CHECK_GRAPHIC(); + + CIMAGE *image = (CIMAGE *) VARG(image); + + if (!image) + return; + + GFX->SetColor(THIS->forecolor); + GFX->Blit(IMAGEID(image), VARG(x), VARG(y), VARGOPT(srcx,0), VARGOPT(srcy,0), + VARGOPT(srcw,-1), VARGOPT(srch,-1), VARGOPT(width,-1), VARGOPT(height,-1)); + +END_METHOD + +BEGIN_PROPERTY(CDRAW_defaultfont) + + CHECK_GRAPHIC(); + +END_PROPERTY + +BEGIN_PROPERTY(CDRAW_font) + + CHECK_GRAPHIC(); + + if (READ_PROPERTY) + GB.ReturnObject(FONT); + else + { + if (FONT) + GB.Unref(POINTER(&FONT)); + + FONT = (CFONT *) VPROP(GB_OBJECT); + GB.Ref(FONT); + } + +END_PROPERTY + +BEGIN_PROPERTY(CDRAW_linestyle) + + CHECK_GRAPHIC(); + + if (READ_PROPERTY) + GB.ReturnInteger(GFX->GetLineStyle()); + else + GFX->SetLineStyle(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CDRAW_linewidth) + + CHECK_GRAPHIC(); + + if (READ_PROPERTY) + GB.ReturnInteger(GFX->GetLineWidth()); + else + GFX->SetLineWidth(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CDRAW_fillstyle) + + CHECK_GRAPHIC(); + + if (READ_PROPERTY) + GB.ReturnInteger(GFX->GetFillStyle()); + else + GFX->SetFillStyle(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_background) + + CHECK_GRAPHIC(); + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->backcolor); + else + THIS->backcolor = VPROP(GB_INTEGER); + +END_PROPERTY + +BEGIN_PROPERTY(CDRAW_foreground) + + CHECK_GRAPHIC(); + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->forecolor); + else + THIS->forecolor = VPROP(GB_INTEGER); + +END_PROPERTY + +/**************************************************************************/ + +GB_DESC CDraw[] = +{ + GB_DECLARE("Draw", 0), GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("_exit", NULL, Draw_exit, NULL), + + GB_STATIC_METHOD("Begin", NULL, CDRAW_begin, "(Device)o"), + GB_STATIC_METHOD("End", NULL, CDRAW_end, NULL), + + GB_STATIC_METHOD("Rotate", NULL, CDRAW_rotate, "(Angle)f"), + GB_STATIC_METHOD("Scale", NULL, CDRAW_scale, "(X)f(Y)f"), + + GB_STATIC_METHOD("Point", NULL, CDRAW_point, "(X)i(Y)i"), + GB_STATIC_METHOD("Line", NULL, CDRAW_line, "(X1)i(Y1)i(X2)i(Y2)i"), + GB_STATIC_METHOD("Rect", NULL, CDRAW_rect, "(X)i(Y)i(Width)i(Height)i"), + GB_STATIC_METHOD("Ellipse", NULL, CDRAW_ellipse, "(X)i(Y)i(Width)i(Height)i"), + + GB_STATIC_METHOD("Text", NULL, CDRAW_text, "(text)s(X)i(Y)i"), + GB_STATIC_METHOD("Image", NULL, CDRAW_image, "(Image)Image;(X)i(Y)i[(Width)i(Height)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + +// GB_STATIC_PROPERTY("DefaultFont", "Font", CDRAW_defaultfont), + GB_STATIC_PROPERTY("Font", "Font", CDRAW_font), + + GB_STATIC_PROPERTY("LineStyle", "i", CDRAW_linestyle), + GB_STATIC_PROPERTY("LineWidth", "i", CDRAW_linewidth), + GB_STATIC_PROPERTY("FillStyle", "i", CDRAW_fillstyle), + GB_STATIC_PROPERTY("Background", "i", CDRAW_background), + GB_STATIC_PROPERTY("BackColor", "i", CDRAW_background), + GB_STATIC_PROPERTY("Foreground", "i", CDRAW_foreground), + GB_STATIC_PROPERTY("ForeColor", "i", CDRAW_foreground), + + GB_END_DECLARE +}; diff --git a/gb.sdl/src/Cdraw.h b/gb.sdl/src/Cdraw.h new file mode 100644 index 00000000..da5c1cae --- /dev/null +++ b/gb.sdl/src/Cdraw.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + Cdraw.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDRAW_H +#define __CDRAW_H + +#include "main.h" +#include "SDLgfx.h" +#include "Cfont.h" + +typedef + struct { + void *device; + SDLgfx *graphic; + CFONT *font; + Uint32 forecolor; + Uint32 backcolor; + } + CDRAW; + +#ifndef __CDRAW_CPP +extern GB_DESC CDraw[]; +#endif /* __CDRAW_CPP */ + +void DRAW_begin(void *device); +void DRAW_end(void ); + +#endif /* __CDRAW_H */ + diff --git a/gb.sdl/src/Cfont.cpp b/gb.sdl/src/Cfont.cpp new file mode 100644 index 00000000..60e5cb58 --- /dev/null +++ b/gb.sdl/src/Cfont.cpp @@ -0,0 +1,243 @@ +/*************************************************************************** + + Cfont.cpp + + (c) 2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CFONT_CPP + +#include "Cfont.h" +#include "Cimage.h" +#if 0 +static StringList FontList; + +static void init_font_list(void ) +{ + if (FontList.empty()) + FontList = SDLfont::GetFontList(); +} + +BEGIN_METHOD_VOID(CFONTS_next) + + std::string s; + + uint *index = (uint *)GB.GetEnum(); + + if (*index == 0) + init_font_list(); + + if (*index >= FontList.size()) + GB.StopEnum(); + else + { + s = FontList[*index]; + GB.ReturnNewZeroString(s.c_str()); + (*index)++; + } + +END_METHOD + +BEGIN_PROPERTY(CFONTS_count) + + init_font_list(); + GB.ReturnInteger(FontList.size()); + +END_PROPERTY +#endif +BEGIN_METHOD(CFONT_load, GB_STRING path) + + CFONT *font; + font = (CFONT *)GB.New(CLASS_Font, NULL, NULL); + font->font = new SDLfont(GB.RealFileName(STRING(path), LENGTH(path))); + GB.ReturnObject(font); + +END_METHOD + +BEGIN_METHOD(CFONT_width, GB_STRING text) + + int width, height; + FONT->SizeText(STRING(text), LENGTH(text), &width, &height); + GB.ReturnInteger(width); + +END_METHOD + +BEGIN_METHOD(CFONT_height, GB_STRING text) + + int width, height; + FONT->SizeText(STRING(text), LENGTH(text), &width, &height); + GB.ReturnInteger(height); + +END_METHOD + +BEGIN_METHOD(CFONT_image, GB_STRING text) + + CIMAGE *img; + + SDLsurface *txt = FONT->RenderText(STRING(text), LENGTH(text)); + if (!txt) + { + GB.ReturnNull(); + return; + } + + img = CIMAGE_create(txt); + + GB.ReturnObject(img); + +END_METHOD + +BEGIN_METHOD_VOID(CFONT_new) + + THIS->font = new SDLfont(); + +END_METHOD + +BEGIN_METHOD_VOID(CFONT_free) + + if (FONT) + delete (FONT); + +END_METHOD + +BEGIN_PROPERTY(CFONT_name) + + GB.ReturnNewZeroString(FONT->GetFontName()); + +END_PROPERTY + +BEGIN_PROPERTY(CFONT_size) + + if (READ_PROPERTY) + GB.ReturnInteger(FONT->GetFontSize()); + else + FONT->SetFontSize(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CFONT_bold) + + if (READ_PROPERTY) + GB.ReturnBoolean(FONT->IsFontBold()); + else + FONT->SetFontBold(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CFONT_italic) + + if (READ_PROPERTY) + GB.ReturnBoolean(FONT->IsFontItalic()); + else + FONT->SetFontItalic(VPROP(GB_BOOLEAN)); + +END_PROPERTY +/* +BEGIN_PROPERTY(CFONT_strikeout) + + if (READ_PROPERTY) + GB.ReturnBoolean(FONT->IsFontStrikeout()); + else + FONT->SetFontStrikeout(VPROP(GB_BOOLEAN)); + +END_PROPERTY +*/ +BEGIN_PROPERTY(CFONT_underline) + + if (READ_PROPERTY) + GB.ReturnBoolean(FONT->IsFontUnderline()); + else + FONT->SetFontUnderline(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CFONT_ascent) + + GB.ReturnInteger(FONT->GetFontAscent()); + +END_PROPERTY + +BEGIN_PROPERTY(CFONT_descent) + + GB.ReturnInteger(FONT->GetFontDescent()); + +END_PROPERTY + +BEGIN_PROPERTY(CFONT_fixed) + + GB.ReturnBoolean(FONT->IsFontFixed()); + +END_PROPERTY + +BEGIN_PROPERTY(CFONT_scalable) + + GB.ReturnBoolean(FONT->IsFontScalable()); + +END_PROPERTY + +BEGIN_PROPERTY(Font_DefaultFontSize) + + GB.ReturnInteger(FONT->GetDefaultFontSize()); + +END_PROPERTY + +#if 0 +GB_DESC CFonts[] = +{ + GB_DECLARE("Fonts", 0), + GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("_next", "s", CFONTS_next, NULL), + GB_STATIC_PROPERTY_READ("Count", "i", CFONTS_count), + + GB_END_DECLARE +}; +#endif +GB_DESC CFont[] = +{ + GB_DECLARE("Font", sizeof(CFONT)), + + GB_STATIC_METHOD("Load", "Font", CFONT_load, "(Path)s"), + + GB_METHOD("_new", NULL, CFONT_new, NULL), + GB_METHOD("_free", NULL, CFONT_free, NULL), + + GB_PROPERTY("Size", "i", CFONT_size), + GB_PROPERTY("Bold", "b", CFONT_bold), + GB_PROPERTY("Italic", "b", CFONT_italic), +// GB_PROPERTY("StrikeOut", "b", CFONT_strikeout), + GB_PROPERTY("Underline", "b", CFONT_underline), + + GB_PROPERTY_READ("Name", "s", CFONT_name), + GB_PROPERTY_READ("Ascent", "i", CFONT_ascent), + GB_PROPERTY_READ("Descent", "i", CFONT_descent), + GB_PROPERTY_READ("Fixed", "b", CFONT_fixed), + GB_PROPERTY_READ("Scalable", "b", CFONT_scalable), + + GB_METHOD("Width", "i", CFONT_width, "(Text)s"), + GB_METHOD("Height", "i", CFONT_height, "(Text)s"), + GB_METHOD("Image", "Image", CFONT_image, "(Text)s"), + + GB_STATIC_PROPERTY_READ("DefaultFontSize", "i", Font_DefaultFontSize), +/* + GB_PROPERTY("Grade", "i", CFONT_grade), +*/ + GB_END_DECLARE +}; + diff --git a/gb.sdl/src/Cfont.h b/gb.sdl/src/Cfont.h new file mode 100644 index 00000000..7fb05038 --- /dev/null +++ b/gb.sdl/src/Cfont.h @@ -0,0 +1,47 @@ +/*************************************************************************** + + Cfont.h + + (c) 2007 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CFONT_H +#define __CFONT_H + +#include "SDLfont.h" + +#include "main.h" + +#ifndef __CFONT_CPP +extern GB_DESC CFont[]; +//extern GB_DESC CFonts[]; +#else + +#define THIS ((CFONT *)_object) +#define FONT ((CFONT *)_object)->font +#endif /* __CFONT_CPP */ + +typedef + struct { + GB_BASE ob; + SDLfont *font; + } + CFONT; + +#endif /* __CFONT_H */ diff --git a/gb.sdl/src/Cimage.cpp b/gb.sdl/src/Cimage.cpp new file mode 100644 index 00000000..4387ec92 --- /dev/null +++ b/gb.sdl/src/Cimage.cpp @@ -0,0 +1,160 @@ +/*************************************************************************** + + Cimage.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CIMAGE_CPP + +#include "Cimage.h" +#include "SDLtexture.h" + +#include +#include + +static void free_image(GB_IMG *img, void *image) +{ + ((SDLsurface *)image)->Unref(); +} + +static void check_modified(GB_IMG *img) +{ + // Vérifie le flag modified + if (img->modified) + { + // Synchronize l'image image->texture + + // Réinitialise le flag modified + img->modified = false; + } +} + +static void *temp_image(GB_IMG *img) +{ + SDLsurface *image; + + if (!img->data) + image = new SDLsurface(); + else + { + // Pas besoin de faire de synchro image->texture, vu qu'on crée une nouvelle surface ? + image = new SDLsurface((char *)img->data, img->width, img->height); + } + + image->SetAlphaBuffer(true); + return image; +} + +static void sync_image(GB_IMG *image) +{ + // Synchronize l'image texture->image + + // Puis mets le flag de synchro à false + image->sync = false; +} + +static GB_IMG_OWNER _image_owner = { + "gb.sdl", + DEFAULT_IMAGE_FORMAT, + free_image, + free_image, + temp_image, + sync_image, + }; + +SDLsurface *CIMAGE_get(CIMAGE *_object) +{ + GB_IMG *img = THIS_IMAGE; + + // Si ce n'est pas nécessaire de le faire systématiquement chaque fois qu'on a besoin de l'image, + // alors ne pas le faire ici, mais explicitement où c'est vraiment nécessaire. + check_modified(img); + return (SDLsurface *)IMAGE.Check(img, &_image_owner); +} + +#define check_image CIMAGE_get + +static void take_image(CIMAGE *_object, SDLsurface *image) +{ + IMAGE.Take(THIS_IMAGE, &_image_owner, image, image->width(), image->height(), image->data()); +} + +CIMAGE *CIMAGE_create(SDLsurface *image) +{ + CIMAGE *img; + + img = (CIMAGE *)GB.New(CLASS_Image, NULL, NULL); + + if (image) + { + (image->GetTexture())->Sync(); + take_image(img, image); + } + else + take_image(img, new SDLsurface()); + + return img; +} + +CIMAGE *CIMAGE_create_from_window(SDLwindow *window, int x, int y, int w, int h) +{ + GB_IMG *img; + uchar *line, *top, *bottom; + uint size; + + if (w < 0) + w = window->GetWidth(); + + if (h < 0) + h = window->GetHeight(); + + if (w <= 0 || h <= 0) + return NULL; + + img = IMAGE.Create(w, h, GB_IMAGE_RGBA, NULL); + glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, img->data); + + size = img->width * sizeof(uint); + GB.Alloc(POINTER(&line), size); + top = img->data; + bottom = img->data + img->height * size; + + for (y = 0; y < img->height / 2; y++) + { + bottom -= size; + memcpy(line, top, size); + memcpy(top, bottom, size); + memcpy(bottom, line, size); + top += size; + } + + GB.Free(POINTER(&line)); + + return (CIMAGE *)img; +} + +/***************************************************************************/ + +GB_DESC CImage[] = +{ + GB_DECLARE("Image", sizeof(CIMAGE)), + + GB_END_DECLARE +}; diff --git a/gb.sdl/src/Cimage.h b/gb.sdl/src/Cimage.h new file mode 100644 index 00000000..023c1603 --- /dev/null +++ b/gb.sdl/src/Cimage.h @@ -0,0 +1,58 @@ +/*************************************************************************** + + Cimage.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CIMAGE_H +#define __CIMAGE_H + +#include "main.h" +#include "SDLsurface.h" +#include "SDLwindow.h" + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define DEFAULT_IMAGE_FORMAT GB_IMAGE_ARGB +#else +#define DEFAULT_IMAGE_FORMAT GB_IMAGE_BGRA +#endif + + +typedef + struct { + GB_IMG img; + } + CIMAGE; + +#ifndef __CIMAGE_CPP +extern GB_DESC CImage[]; +#else + +#define THIS ((CIMAGE *)_object) +#define THIS_IMAGE (&THIS->img) +#define IMAGEID ((SDLsurface *)GB_IMG_HANDLE(&THIS->img)) + +#endif /* __CIMAGE_CPP */ + +SDLsurface *CIMAGE_get(CIMAGE *); +CIMAGE *CIMAGE_create(SDLsurface *); +CIMAGE *CIMAGE_create_from_window(SDLwindow *window, int x, int y, int w, int h); + +#endif /* __CIMAGE_H */ diff --git a/gb.sdl/src/Cjoystick.cpp b/gb.sdl/src/Cjoystick.cpp new file mode 100644 index 00000000..346d0ff0 --- /dev/null +++ b/gb.sdl/src/Cjoystick.cpp @@ -0,0 +1,296 @@ +/*************************************************************************** + + Cjoystick.cpp + + (c) 2011 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CJOYSTICK_CPP + +#include "Cjoystick.h" + +#include +#include + +// store joysticks infos +typedef + struct { + Uint8 Axes; + Uint8 Balls; + Uint8 Buttons; + Uint8 Hats; + std::string Name; + } + JOY_info; + +static std::map joyinfos; +static std::map joyobjects; +static int joyindex = 0; + +CJOY_INFO CJOY_info = { 0 }; + +#define CHECK_VALID() \ + if (UNLIKELY(!CJOY_info.valid)) \ + { \ + GB.Error("No joystick event data"); \ + return; \ + } + +static void filljoyinfos() +{ + + int numjoy = SDL_NumJoysticks(); + JOY_info myinfo; + + if (!numjoy) + return; + + for (int i=0; i=numjoy || index<0) + { + GB.Error("Joystick &1 not available !", VARG(index)); + return; + } + + joyindex = index; + + if (!joyinfos.size()) + filljoyinfos(); + + RETURN_SELF(); + +END_METHOD + +BEGIN_PROPERTY(JOYSTICKS_count) + + GB.ReturnInteger(SDL_NumJoysticks()); + +END_METHOD + +BEGIN_PROPERTY(JOYSTICK_device) + + CHECK_VALID(); + GB.ReturnInteger(CJOY_info.device); + +END_METHOD + +BEGIN_PROPERTY(JOYSTICK_id) + + CHECK_VALID(); + GB.ReturnInteger(CJOY_info.id); + +END_PROPERTY + +BEGIN_PROPERTY(JOYSTICK_axisvalue) + + CHECK_VALID(); + GB.ReturnInteger(CJOY_info.value1); + +END_PROPERTY + +BEGIN_PROPERTY(JOYSTICK_hatvalue) + + CHECK_VALID(); + GB.ReturnInteger(CJOY_info.value1); + +END_PROPERTY + +BEGIN_PROPERTY(JOYSTICK_ballx) + + CHECK_VALID(); + GB.ReturnInteger(CJOY_info.value1); + +END_PROPERTY + +BEGIN_PROPERTY(JOYSTICK_bally) + + CHECK_VALID(); + GB.ReturnInteger(CJOY_info.value2); + +END_PROPERTY + +/***************************************************************************/ + +GB_DESC CJoyInfos[] = +{ + GB_DECLARE(".Joystick", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("Enable", "b", JOYINFOS_enable), + + GB_STATIC_PROPERTY_READ("Axes", "i", JOYINFOS_numofaxes), + GB_STATIC_PROPERTY_READ("Balls", "i", JOYINFOS_numofballs), + GB_STATIC_PROPERTY_READ("Buttons", "i", JOYINFOS_numofbuts), + GB_STATIC_PROPERTY_READ("Hats", "i", JOYINFOS_numofhats), + GB_STATIC_PROPERTY_READ("Name", "s", JOYINFOS_name), + + GB_END_DECLARE +}; + +GB_DESC CQueryJoys[] = +{ + GB_DECLARE("Joysticks", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_get", ".Joystick", JOYSTICKS_get, "(Index)i"), + GB_STATIC_PROPERTY_READ("Count", "i", JOYSTICKS_count), + + GB_END_DECLARE +}; + +GB_DESC CJoystick[] = +{ + GB_DECLARE("Joystick", 0), GB_VIRTUAL_CLASS(), + +// TODO close all opened joysticks on exit ? +// GB_STATIC_METHOD("_exit", NULL, JOYSTICK_exit, NULL), + + GB_STATIC_PROPERTY_READ("Device", "i", JOYSTICK_device), + GB_STATIC_PROPERTY_READ("Id", "i", JOYSTICK_id), + GB_STATIC_PROPERTY_READ("Axis", "i", JOYSTICK_axisvalue), + GB_STATIC_PROPERTY_READ("Hat", "i", JOYSTICK_hatvalue), + GB_STATIC_PROPERTY_READ("BallX", "i", JOYSTICK_ballx), + GB_STATIC_PROPERTY_READ("BallY", "i", JOYSTICK_bally), + + GB_CONSTANT("LeftUp", "i", SDL_HAT_LEFTUP), + GB_CONSTANT("Left", "i", SDL_HAT_LEFT), + GB_CONSTANT("LeftDown", "i", SDL_HAT_LEFTDOWN), + GB_CONSTANT("Up", "i", SDL_HAT_UP), + GB_CONSTANT("Centered", "i", SDL_HAT_CENTERED), + GB_CONSTANT("Down", "i", SDL_HAT_DOWN), + GB_CONSTANT("RightUp", "i", SDL_HAT_RIGHTUP), + GB_CONSTANT("Right", "i", SDL_HAT_RIGHT), + GB_CONSTANT("RightDown", "i", SDL_HAT_RIGHTDOWN), + + GB_END_DECLARE +}; + diff --git a/gb.sdl/src/Cjoystick.h b/gb.sdl/src/Cjoystick.h new file mode 100644 index 00000000..29efee38 --- /dev/null +++ b/gb.sdl/src/Cjoystick.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + Cjoystick.h + + (c) 2011 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CJOYSTICK_H +#define __CJOYSTICK_H + +#include "main.h" +#include "SDL.h" + +typedef + struct { + bool valid; + Uint8 device; /* Joystick that raised the event */ + int id; /* Can be axis, ball, hat, button id */ + Sint16 value1; /* Can be value for axis, hat; xrel for ball */ + Sint16 value2; /* yrel value for ball */ + } + CJOY_INFO; + +#ifndef __CJOYSTICK_CPP +extern GB_DESC CJoyInfos[]; +extern GB_DESC CQueryJoys[]; +extern GB_DESC CJoystick[]; +extern CJOY_INFO CJOY_info; +#else + +#define JOYSTICK ((CJOYSTICK *)_object)->joy +#define THIS ((CJOYSTICK *)_object) + +#endif /* __CJOYSTICK_CPP */ +#endif /* __CJOYSTCIK_H */ + diff --git a/gb.sdl/src/Ckey.cpp b/gb.sdl/src/Ckey.cpp new file mode 100644 index 00000000..e73de8a9 --- /dev/null +++ b/gb.sdl/src/Ckey.cpp @@ -0,0 +1,235 @@ +/*************************************************************************** + + Ckey.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CKEY_CPP + +#include "gambas.h" +#include "main.h" + +#include "Ckey.h" + +#include "SDL.h" + +CKEY_INFO CKEY_info = { 0 }; + +static bool _key_repeat = false; + +#define CHECK_VALID() \ + if (CKEY_info.valid <= 0) \ + { \ + GB.Error("No keyboard event data"); \ + return; \ + } + +/***************************************************************************/ + +BEGIN_METHOD(CKEY_get, GB_STRING key) + + char *key = GB.ToZeroString(ARG(key)); + int code = 0; + + if (key[0] && !key[1] && !(key[0] & 0x80)) + { + GB.ReturnInteger(key[0]); + return; + } + else + { + for (code = 1; code <= 255; code++) + { + if (!strcasecmp(SDL_GetKeyName((SDLKey)code), key)) + { + GB.ReturnInteger(code); + return; + } + } + } + + GB.ReturnInteger(0); + +END_METHOD + +BEGIN_PROPERTY(CKEY_code) + + CHECK_VALID(); + GB.ReturnInteger(CKEY_info.code); + +END_PROPERTY + +BEGIN_PROPERTY(CKEY_state) + + CHECK_VALID(); + GB.ReturnInteger(CKEY_info.state); + +END_PROPERTY + +BEGIN_PROPERTY(CKEY_shift) + + CHECK_VALID(); + GB.ReturnBoolean(CKEY_info.state & KMOD_SHIFT); + +END_PROPERTY + +BEGIN_PROPERTY(CKEY_control) + + CHECK_VALID(); + GB.ReturnBoolean(CKEY_info.state & KMOD_CTRL); + +END_PROPERTY + +BEGIN_PROPERTY(CKEY_alt) + + CHECK_VALID(); + GB.ReturnBoolean(CKEY_info.state & KMOD_ALT); + +END_PROPERTY + +BEGIN_PROPERTY(CKEY_meta) + + CHECK_VALID(); + GB.ReturnBoolean(CKEY_info.state & KMOD_META); + +END_PROPERTY + +BEGIN_PROPERTY(CKEY_normal) + + CHECK_VALID(); + GB.ReturnBoolean(CKEY_info.state != 0); + +END_PROPERTY + +BEGIN_PROPERTY(CKEY_text) + + CHECK_VALID(); + GB.ReturnNewZeroString(CKEY_info.text); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Repeat) + + if (READ_PROPERTY) + GB.ReturnBoolean(_key_repeat); + else + { + _key_repeat = VPROP(GB_BOOLEAN); + SDL_EnableKeyRepeat(_key_repeat ? SDL_DEFAULT_REPEAT_DELAY : 0, SDL_DEFAULT_REPEAT_INTERVAL); + } + +END_PROPERTY + +/***************************************************************************/ + +GB_DESC CKey[] = +{ + GB_DECLARE("Key", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY("Repeat", "b", Key_Repeat), + + GB_STATIC_METHOD("_get", "i", CKEY_get, "(Key)s"), + + GB_STATIC_PROPERTY_READ("Code", "i", CKEY_code), + GB_STATIC_PROPERTY_READ("State", "i", CKEY_state), + GB_STATIC_PROPERTY_READ("Shift", "b", CKEY_shift), + GB_STATIC_PROPERTY_READ("Control", "b", CKEY_control), + GB_STATIC_PROPERTY_READ("Alt", "b", CKEY_alt), + GB_STATIC_PROPERTY_READ("Meta", "b", CKEY_meta), + GB_STATIC_PROPERTY_READ("Normal", "b", CKEY_normal), + GB_STATIC_PROPERTY_READ("Text", "s", CKEY_text), + + GB_CONSTANT("Backspace", "i", SDLK_BACKSPACE), + GB_CONSTANT("Tab", "i", SDLK_TAB), + //GB_CONSTANT("Clear", "i", SDLK_CLEAR = 12, + GB_CONSTANT("Return", "i", SDLK_RETURN), + GB_CONSTANT("Pause", "i", SDLK_PAUSE), + GB_CONSTANT("Escape", "i", SDLK_ESCAPE), + GB_CONSTANT("Esc", "i", SDLK_ESCAPE), + GB_CONSTANT("Space", "i", SDLK_SPACE), + GB_CONSTANT("Delete", "i", SDLK_DELETE), + GB_CONSTANT("KP0", "i", SDLK_KP0), + GB_CONSTANT("KP1", "i", SDLK_KP1), + GB_CONSTANT("KP2", "i", SDLK_KP2), + GB_CONSTANT("KP3", "i", SDLK_KP3), + GB_CONSTANT("KP4", "i", SDLK_KP4), + GB_CONSTANT("KP5", "i", SDLK_KP5), + GB_CONSTANT("KP6", "i", SDLK_KP6), + GB_CONSTANT("KP7", "i", SDLK_KP7), + GB_CONSTANT("KP8", "i", SDLK_KP8), + GB_CONSTANT("KP9", "i", SDLK_KP9), + GB_CONSTANT("KPPeriod", "i", SDLK_KP_PERIOD), + GB_CONSTANT("KPDivide", "i", SDLK_KP_DIVIDE), + GB_CONSTANT("KPMultiply", "i", SDLK_KP_MULTIPLY), + GB_CONSTANT("KPMinus", "i", SDLK_KP_MINUS), + GB_CONSTANT("KPPlus", "i", SDLK_KP_PLUS), + GB_CONSTANT("KPEnter", "i", SDLK_KP_ENTER), + GB_CONSTANT("KPEquals", "i", SDLK_KP_EQUALS), + GB_CONSTANT("Up", "i", SDLK_UP), + GB_CONSTANT("Down", "i", SDLK_DOWN), + GB_CONSTANT("Right", "i", SDLK_RIGHT), + GB_CONSTANT("Left", "i", SDLK_LEFT), + GB_CONSTANT("Insert", "i", SDLK_INSERT), + GB_CONSTANT("Home", "i", SDLK_HOME), + GB_CONSTANT("End", "i", SDLK_END), + GB_CONSTANT("PageUp", "i", SDLK_PAGEUP), + GB_CONSTANT("PageDown", "i", SDLK_PAGEDOWN), + GB_CONSTANT("F1", "i", SDLK_F1), + GB_CONSTANT("F2", "i", SDLK_F2), + GB_CONSTANT("F3", "i", SDLK_F3), + GB_CONSTANT("F4", "i", SDLK_F4), + GB_CONSTANT("F5", "i", SDLK_F5), + GB_CONSTANT("F6", "i", SDLK_F6), + GB_CONSTANT("F7", "i", SDLK_F7), + GB_CONSTANT("F8", "i", SDLK_F8), + GB_CONSTANT("F9", "i", SDLK_F9), + GB_CONSTANT("F10", "i", SDLK_F10), + GB_CONSTANT("F11", "i", SDLK_F11), + GB_CONSTANT("F12", "i", SDLK_F12), + GB_CONSTANT("F13", "i", SDLK_F13), + GB_CONSTANT("F14", "i", SDLK_F14), + GB_CONSTANT("F15", "i", SDLK_F15), + GB_CONSTANT("NumLock", "i", SDLK_NUMLOCK), + GB_CONSTANT("CapsLock", "i", SDLK_CAPSLOCK), + GB_CONSTANT("ScrollLock", "i", SDLK_SCROLLOCK), + GB_CONSTANT("RightShift", "i", SDLK_RSHIFT), + GB_CONSTANT("LeftShift", "i", SDLK_LSHIFT), + GB_CONSTANT("RightControl", "i", SDLK_RCTRL), + GB_CONSTANT("LeftControl", "i", SDLK_LCTRL), + GB_CONSTANT("RightAlt", "i", SDLK_RALT), + GB_CONSTANT("LeftAlt", "i", SDLK_LALT), + GB_CONSTANT("RightMeta", "i", SDLK_RMETA), + GB_CONSTANT("LeftMeta", "i", SDLK_LMETA), + GB_CONSTANT("RightSuper", "i", SDLK_RSUPER), + GB_CONSTANT("LeftSuper", "i", SDLK_LSUPER), + GB_CONSTANT("AltGr", "i", SDLK_MODE), + GB_CONSTANT("Compose", "i", SDLK_COMPOSE), + GB_CONSTANT("Help", "i", SDLK_HELP), + GB_CONSTANT("Print", "i", SDLK_PRINT), + GB_CONSTANT("SysReq", "i", SDLK_SYSREQ), + GB_CONSTANT("Break", "i", SDLK_BREAK), + GB_CONSTANT("Menu", "i", SDLK_MENU), + GB_CONSTANT("Power", "i", SDLK_POWER), + GB_CONSTANT("Euro", "i", SDLK_EURO), + GB_CONSTANT("Undo", "i", SDLK_UNDO), + + GB_END_DECLARE +}; + diff --git a/gb.sdl/src/Ckey.h b/gb.sdl/src/Ckey.h new file mode 100644 index 00000000..2763c408 --- /dev/null +++ b/gb.sdl/src/Ckey.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + Ckey.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CKEY_H +#define __CKEY_H + +#include "gambas.h" +#include "main.h" + +#include "SDL.h" +#include "SDL_syswm.h" +//#include "X11/keysym.h" + +typedef + struct { + int valid; + int code; // SDL virtual keysym + int state; // modifier state + //int cancel; + char text[4]; // UTF-8 text + } + CKEY_INFO; + +#ifndef __CKEY_CPP +extern GB_DESC CKey[]; +extern CKEY_INFO CKEY_info; +#endif /* __CKEY_CPP */ + +#endif /* __CKEY_H */ + diff --git a/gb.sdl/src/Cmouse.cpp b/gb.sdl/src/Cmouse.cpp new file mode 100644 index 00000000..88d4d0e8 --- /dev/null +++ b/gb.sdl/src/Cmouse.cpp @@ -0,0 +1,319 @@ +/*************************************************************************** + + Cmouse.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CMOUSE_CPP + +#include "gambas.h" +#include "main.h" + +#include "Cmouse.h" +#include "Cimage.h" + +#include "SDL_h.h" +#include "SDLapp.h" + +CMOUSE_INFO CMOUSE_info = { 0 }; + +#define CHECK_VALID() \ + if (UNLIKELY(!CMOUSE_info.valid)) \ + { \ + GB.Error("No mouse event data"); \ + return; \ + } + +/***************************************************************************/ + +#if 0 +BEGIN_METHOD(CURSOR_new, GB_OBJECT image; GB_INTEGER x; GB_INTEGER y) + +/* CIMAGE *img = (CIMAGE *)VARG(image); + + THIS->x = VARGOPT(x, -1); + THIS->y = VARGOPT(y, -1); + + if (GB.CheckObject(img)) + return; + + if (THIS->x < 0 || THIS->x >= img->id->GetWidth()) + THIS->x = -1; + + if (THIS->y < 0 || THIS->y >= img->id->GetHeight()) + THIS->y = -1; + + THIS->cursor = new SDLcursor(); + THIS->cursor->SetCursor(img->id, THIS->x, THIS->y); +*/ +END_METHOD + + +BEGIN_METHOD_VOID(CURSOR_delete) + +// delete THIS->cursor; + +END_METHOD + +BEGIN_PROPERTY(CURSOR_x) + + GB.ReturnInteger(THIS->x); + +END_PROPERTY + + +BEGIN_PROPERTY(CURSOR_y) + + GB.ReturnInteger(THIS->y); + +END_PROPERTY +#endif + +BEGIN_METHOD(CMOUSE_move, GB_INTEGER x; GB_INTEGER y) + + SDL_WarpMouse(VARG(x), VARG(y)); + +END_METHOD + +BEGIN_PROPERTY(CMOUSE_screenx) + + int x, y, toto; + Window tata; + unsigned int mask; + + SDLapp->LockX11(); + XQueryPointer(SDLapp->X11appDisplay(), SDLapp->X11appRootWin(), &tata, &tata, &x,&y, &toto,&toto, &mask); + SDLapp->UnlockX11(); + + GB.ReturnInteger(x); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_screeny) + + int x, y, toto; + Window tata; + unsigned int mask; + + SDLapp->LockX11(); + XQueryPointer(SDLapp->X11appDisplay(), SDLapp->X11appRootWin(), &tata, &tata, &x,&y, &toto,&toto, &mask); + SDLapp->UnlockX11(); + + GB.ReturnInteger(y); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_x) + + CHECK_VALID() + GB.ReturnInteger(CMOUSE_info.x); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_y) + + CHECK_VALID() + GB.ReturnInteger(CMOUSE_info.y); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_startx) + + CHECK_VALID() + GB.ReturnInteger(CMOUSE_info.x - CMOUSE_info.relx); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_starty) + + CHECK_VALID() + GB.ReturnInteger(CMOUSE_info.y - CMOUSE_info.rely); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_left) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.state == SDL_BUTTON_LEFT); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_right) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.state == SDL_BUTTON_RIGHT); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_middle) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.state == SDL_BUTTON_MIDDLE); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_wheelup) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.state == SDL_BUTTON_WHEELUP); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_wheeldown) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.state == SDL_BUTTON_WHEELDOWN); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_button) + + CHECK_VALID() + GB.ReturnInteger(CMOUSE_info.state); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_shift) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.keymod & KMOD_SHIFT); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_control) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.keymod & KMOD_CTRL); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_alt) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.keymod & KMOD_ALT); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_meta) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.keymod & KMOD_META); + +END_PROPERTY + +BEGIN_PROPERTY(CMOUSE_normal) + + CHECK_VALID() + GB.ReturnBoolean(CMOUSE_info.keymod < KMOD_NUM); + +END_PROPERTY + +BEGIN_METHOD_VOID(Mouse_Show) + + SDL_ShowCursor(SDL_ENABLE); + +END_METHOD + +BEGIN_METHOD_VOID(Mouse_Hide) + + SDL_ShowCursor(SDL_DISABLE); + +END_METHOD + +BEGIN_PROPERTY(Mouse_Visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE); + else + SDL_ShowCursor(VPROP(GB_BOOLEAN) ? SDL_ENABLE : SDL_DISABLE); + +END_PROPERTY + +/***************************************************************************/ +/* +GB_DESC CCursor[] = +{ + GB_DECLARE("Cursor", sizeof(CCURSOR)), + + GB_METHOD("_new", NULL, CURSOR_new, "(Image)Image;[(X)i(Y)i]"), + GB_METHOD("_free", NULL, CURSOR_delete, NULL), + + GB_PROPERTY_READ("X", "i", CURSOR_x), + GB_PROPERTY_READ("Y", "i", CURSOR_y), + + GB_END_DECLARE +}; +*/ +GB_DESC CMouse[] = +{ + GB_DECLARE("Mouse", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("Move", NULL, CMOUSE_move, "(X)i(Y)i"), + + GB_STATIC_METHOD("Show", NULL, Mouse_Show, NULL), + GB_STATIC_METHOD("Hide", NULL, Mouse_Hide, NULL), + GB_STATIC_PROPERTY("Visible", "b", Mouse_Visible), + + GB_STATIC_PROPERTY_READ("ScreenX", "i", CMOUSE_screenx), + GB_STATIC_PROPERTY_READ("ScreenY", "i", CMOUSE_screeny), + GB_STATIC_PROPERTY_READ("StartX", "i", CMOUSE_startx), + GB_STATIC_PROPERTY_READ("StartY", "i", CMOUSE_starty), + GB_STATIC_PROPERTY_READ("X", "i", CMOUSE_x), + GB_STATIC_PROPERTY_READ("Y", "i", CMOUSE_y), + + GB_STATIC_PROPERTY_READ("Left", "b", CMOUSE_left), + GB_STATIC_PROPERTY_READ("Right", "b", CMOUSE_right), + GB_STATIC_PROPERTY_READ("Middle", "b", CMOUSE_middle), + GB_STATIC_PROPERTY_READ("WheelUp", "b", CMOUSE_wheelup), + GB_STATIC_PROPERTY_READ("WheelDown", "b", CMOUSE_wheeldown), + GB_STATIC_PROPERTY_READ("Button", "i", CMOUSE_button), + GB_STATIC_PROPERTY_READ("Shift", "b", CMOUSE_shift), + GB_STATIC_PROPERTY_READ("Control", "b", CMOUSE_control), + GB_STATIC_PROPERTY_READ("Alt", "b", CMOUSE_alt), + GB_STATIC_PROPERTY_READ("Meta", "b", CMOUSE_meta), + GB_STATIC_PROPERTY_READ("Normal", "b", CMOUSE_normal), +/* + GB_CONSTANT("Default", "i", SDL::DefaultCursor), + GB_CONSTANT("Custom", "i", SDL::CustomCursor), + GB_CONSTANT("Blank", "i", SDL::BlankCursor), + GB_CONSTANT("Arrow", "i", SDL::ArrowCursor), + GB_CONSTANT("Cross", "i", SDL::CrossCursor), + GB_CONSTANT("Wait", "i", SDL::WaitCursor), + GB_CONSTANT("Text", "i", SDL::TextCursor), + GB_CONSTANT("SizeAll", "i", SDL::SizeAllCursor), + GB_CONSTANT("SizeH", "i", SDL::SizeHorCursor), + GB_CONSTANT("SizeV", "i", SDL::SizeVerCursor), + GB_CONSTANT("SizeN", "i", SDL::SizeVerCursor), + GB_CONSTANT("SizeS", "i", SDL::SizeVerCursor), + GB_CONSTANT("SizeW", "i", SDL::SizeHorCursor), + GB_CONSTANT("SizeE", "i", SDL::SizeHorCursor), + GB_CONSTANT("SizeNW", "i", SDL::SizeFDiagCursor), + GB_CONSTANT("SizeSE", "i", SDL::SizeFDiagCursor), + GB_CONSTANT("SizeNE", "i", SDL::SizeBDiagCursor), + GB_CONSTANT("SizeSW", "i", SDL::SizeBDiagCursor), + GB_CONSTANT("SizeNWSE", "i", SDL::SizeFDiagCursor), + GB_CONSTANT("SizeNESW", "i", SDL::SizeBDiagCursor), + GB_CONSTANT("SplitH", "i", SDL::SplitHCursor), + GB_CONSTANT("SplitV", "i", SDL::SplitVCursor), + GB_CONSTANT("Pointing", "i", SDL::PointingHandCursor), +*/ + GB_END_DECLARE +}; + diff --git a/gb.sdl/src/Cmouse.h b/gb.sdl/src/Cmouse.h new file mode 100644 index 00000000..45bb5f75 --- /dev/null +++ b/gb.sdl/src/Cmouse.h @@ -0,0 +1,65 @@ +/*************************************************************************** + + Cmouse.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CMOUSE_H +#define __CMOUSE_H + +#include "gambas.h" +#include "main.h" + +#include "SDL.h" +#include "SDLcursor.h" + +typedef + struct { + bool valid; + int x; + int y; + int relx; + int rely; + int state; + SDLMod keymod; + } + CMOUSE_INFO; + +#ifndef __CMOUSE_CPP +extern GB_DESC CMouse[]; +//extern GB_DESC CCursor[]; +extern CMOUSE_INFO CMOUSE_info; +#else + +#define THIS ((CCURSOR *)_object) + +#endif /* __CMOUSE_CPP */ + +typedef + struct _CCURSOR { + GB_BASE ob; + int x; + int y; + SDLcursor *cursor; + } + CCURSOR; + +#endif /* __CMOUSE_H */ + diff --git a/gb.sdl/src/Cwindow.cpp b/gb.sdl/src/Cwindow.cpp new file mode 100644 index 00000000..07006b29 --- /dev/null +++ b/gb.sdl/src/Cwindow.cpp @@ -0,0 +1,548 @@ +/*************************************************************************** + + Cwindow.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWINDOW_CPP + +#include + +#include "Cwindow.h" +#include "Cjoystick.h" +#include "Ckey.h" +#include "Cmouse.h" +#include "Cdraw.h" +#include "Cimage.h" + +#include "SDL.h" + +#define THIS ((CWINDOW *)_object) +#define WINDOWID ((CWINDOW *)_object)->id +// number of frames before counting FPS +#define FRAMECOUNT 100 + +// events +DECLARE_EVENT(EVENT_Close); +DECLARE_EVENT(EVENT_Resize); +DECLARE_EVENT(EVENT_Activate); +DECLARE_EVENT(EVENT_Deactivate); +DECLARE_EVENT(EVENT_Enter); +DECLARE_EVENT(EVENT_JoyAxisMotion); +DECLARE_EVENT(EVENT_JoyBallMotion); +DECLARE_EVENT(EVENT_JoyButtonPressed); +DECLARE_EVENT(EVENT_JoyButtonReleased); +DECLARE_EVENT(EVENT_JoyHatMotion); +DECLARE_EVENT(EVENT_Leave); +DECLARE_EVENT(EVENT_Refresh); +DECLARE_EVENT(EVENT_KeyPressed); +DECLARE_EVENT(EVENT_KeyReleased); +DECLARE_EVENT(EVENT_MouseMove); +DECLARE_EVENT(EVENT_MouseDown); +DECLARE_EVENT(EVENT_MouseUp); +DECLARE_EVENT(EVENT_Open); + +/***************************************************************************/ + +BEGIN_METHOD(CWINDOW_new, GB_BOOLEAN openGL) + + WINDOWID = new myWin(THIS); + WINDOWID->SetTitle(GB.Application.Name()); + THIS->openGL = VARGOPT(openGL, false); + THIS->lastTime = SDL_GetTicks(); + THIS->startTime = THIS->lastTime; + +END_METHOD + +BEGIN_METHOD_VOID(CWINDOW_free) + + GB.StoreObject(NULL, POINTER(&(THIS->cursor))); + delete WINDOWID; + +END_METHOD + +BEGIN_METHOD_VOID(CWINDOW_show) + + WINDOWID->Show(); + WINDOWID->Refresh(); + +END_METHOD + +BEGIN_METHOD_VOID(CWINDOW_close) + + WINDOWID->Quit(); + +END_METHOD + +BEGIN_METHOD_VOID(CWINDOW_clear) + + WINDOWID->Clear(); + +END_METHOD + +BEGIN_METHOD(CWINDOW_fill, GB_INTEGER color;) + + WINDOWID->Clear(VARG(color)); + +END_METHOD + +BEGIN_METHOD_VOID(CWINDOW_refresh) + + WINDOWID->Refresh(); + +END_METHOD + +BEGIN_PROPERTY(CWINDOW_framerate) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->currentFPS); + else + { + double val = VPROP(GB_FLOAT); + + if (val < 0) + return; + + THIS->FPSLimit = val ? 1000.0 / val : 0; + THIS->lastTime = SDL_GetTicks(); + } + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_text) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(WINDOWID->GetTitle()); + else + WINDOWID->SetTitle(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_fullscreen) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOWID->IsFullScreen()); + else + WINDOWID->SetFullScreen(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_resizable) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOWID->IsResizable()); + else + WINDOWID->SetResizable(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_mouse) + + if (READ_PROPERTY) + GB.ReturnInteger(WINDOWID->GetCursorShape()); + else + WINDOWID->SetCursorShape(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_tracking) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->tracking); + else + THIS->tracking = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_cursor) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->cursor); + else + { +/* + CCURSOR *curs = (CCURSOR *)VPROP(GB_OBJECT); + WINDOWID->SetCursor(curs->cursor); + return; +*/ + } + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_width) + + if (READ_PROPERTY) + GB.ReturnInteger(WINDOWID->GetWidth()); + else + THIS->id->SetWidth(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_height) + + if (READ_PROPERTY) + GB.ReturnInteger(WINDOWID->GetHeight()); + else + WINDOWID->SetHeight(VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_shown) + + GB.ReturnBoolean(WINDOWID->IsShown()); + +END_PROPERTY + +BEGIN_PROPERTY(CWINDOW_id) + + GB.ReturnInteger(WINDOWID->Id()); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Grabbed) + + if (READ_PROPERTY) + GB.ReturnBoolean(WINDOWID->IsInputGrabbed()); + else + WINDOWID->GrabInput(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_METHOD(Window_Screenshot, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + GB.ReturnObject(CIMAGE_create_from_window(WINDOWID, VARGOPT(x, 0), VARGOPT(y, 0), VARGOPT(w, -1), VARGOPT(h, -1))); + +END_METHOD + +/***************************************************************************/ + +GB_DESC CWindow[] = +{ + GB_DECLARE("Window", sizeof(CWINDOW)), + + GB_METHOD("_new", NULL, CWINDOW_new, "[(OpenGL)b]"), + GB_METHOD("_free", NULL, CWINDOW_free, NULL), + + GB_METHOD("Show", NULL, CWINDOW_show, NULL), + GB_METHOD("Close", NULL, CWINDOW_close, NULL), + GB_METHOD("Clear", NULL, CWINDOW_clear, NULL), + GB_METHOD("Fill", NULL, CWINDOW_fill, "(Color)i"), + GB_METHOD("Refresh", NULL, CWINDOW_refresh, NULL), + GB_METHOD("Update", NULL, CWINDOW_refresh, NULL), + + GB_PROPERTY("Caption", "s", CWINDOW_text), +// GB_PROPERTY("Cursor", "Cursor;", CWINDOW_cursor), + GB_PROPERTY("Framerate", "f", CWINDOW_framerate), + GB_PROPERTY("FullScreen", "b", CWINDOW_fullscreen), + GB_PROPERTY("Height", "i", CWINDOW_height), + GB_PROPERTY("Mouse", "i", CWINDOW_mouse), + GB_PROPERTY("Text", "s", CWINDOW_text), + GB_PROPERTY("Title", "s", CWINDOW_text), + GB_PROPERTY("Tracking", "b", CWINDOW_tracking), + GB_PROPERTY("Resizable", "b", CWINDOW_resizable), + GB_PROPERTY("Width", "i", CWINDOW_width), + + GB_PROPERTY_READ("Handle", "i", CWINDOW_id), + GB_PROPERTY_READ("Id", "i", CWINDOW_id), + GB_PROPERTY_READ("Shown", "b", CWINDOW_shown), + GB_PROPERTY("Grabbed", "b", Window_Grabbed), + + GB_METHOD("Screenshot", "Image", Window_Screenshot, "[(X)i(Y)i(Width)i(Height)i]"), + + GB_EVENT("Close", "b", NULL, &EVENT_Close), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + GB_EVENT("Activate", NULL, NULL, &EVENT_Activate), + GB_EVENT("Deactivate", NULL, NULL, &EVENT_Deactivate), + GB_EVENT("Enter", NULL, NULL, &EVENT_Enter), + GB_EVENT("JoyAxisMove", NULL, NULL, &EVENT_JoyAxisMotion), + GB_EVENT("JoyBallMove", NULL, NULL, &EVENT_JoyBallMotion), + GB_EVENT("JoyButtonPress", NULL, NULL, &EVENT_JoyButtonPressed), + GB_EVENT("JoyButtonRelease", NULL, NULL, &EVENT_JoyButtonReleased), + GB_EVENT("JoyHatMove", NULL, NULL, &EVENT_JoyHatMotion), + GB_EVENT("Leave", NULL, NULL, &EVENT_Leave), + GB_EVENT("Draw", "b", NULL, &EVENT_Refresh), + GB_EVENT("KeyPress", NULL, NULL, &EVENT_KeyPressed), + GB_EVENT("KeyRelease", NULL, NULL, &EVENT_KeyReleased), + GB_EVENT("MouseMove", NULL, NULL, &EVENT_MouseMove), + GB_EVENT("MouseDown", NULL, NULL, &EVENT_MouseDown), + GB_EVENT("MouseUp", NULL, NULL, &EVENT_MouseUp), + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + + GB_END_DECLARE +}; + +/***************************************************************************/ + +#define WINDOW(object) ((CWINDOW *)object) + +void myWin::Resize(void) +{ + GB.Raise(hWindow, EVENT_Resize, 0); +} + +void myWin::GotFocus(void) +{ + GB.Raise(hWindow, EVENT_Activate, 0); +} + +void myWin::LostFocus(void) +{ + GB.Raise(hWindow, EVENT_Deactivate, 0); +} + +void myWin::MouseEnter(void) +{ + GB.Raise(hWindow, EVENT_Enter, 0); +} + +void myWin::MouseLeave(void) +{ + GB.Raise(hWindow, EVENT_Leave, 0); +} + +void myWin::Quit(void) +{ + bool cancel = GB.Raise(hWindow, EVENT_Close, 0); + + if (!cancel) + this->Close(); +} + +void myWin::Update(void) +{ + Uint32 ticks, diff; + + // no refresh event + if (!GB.CanRaise(hWindow, EVENT_Refresh)) + { + SDL_Delay(1); + return; + } + + ticks = SDL_GetTicks(); + + // framerate limitation + if (WINDOW(hWindow)->FPSLimit > 0) + { + double d = WINDOW(hWindow)->lastTime + WINDOW(hWindow)->FPSLimit; + + //fprintf(stderr, "%d %g %g %d\n", ticks, d, WINDOW(hWindow)->lastTime, d < ticks); + + if (d > ticks) + { + SDL_Delay(1); + return; + } + + WINDOW(hWindow)->lastTime = d; + } + + DRAW_begin(hWindow); + bool cancel = GB.Raise(hWindow, EVENT_Refresh, 0); + DRAW_end(); + + // user doesn't want to refresh + if (cancel) + { + //SDL_Delay(1); + return; + } + else + this->Refresh(); + + // calculate the framerate + /*if (WINDOW(hWindow)->countFrames >= FRAMECOUNT) + { + double value = (ticks - WINDOW(hWindow)->startTime) / FRAMECOUNT; + + if (value > 0) + WINDOW(hWindow)->currentFPS = Uint32(1000 / value + 0.5); + else + WINDOW(hWindow)->currentFPS = 0; + + WINDOW(hWindow)->countFrames = 0; + WINDOW(hWindow)->startTime = ticks; + } + else + WINDOW(hWindow)->countFrames++;*/ + + WINDOW(hWindow)->countFrames++; + + diff = ticks - WINDOW(hWindow)->startTime; + if (diff > 1000) + { + WINDOW(hWindow)->currentFPS = WINDOW(hWindow)->countFrames; + WINDOW(hWindow)->countFrames = 0; + WINDOW(hWindow)->startTime += 1000; + } +} + +void myWin::Open(void) +{ + if (!(((CWINDOW *)hWindow)->openGL)) + { + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glViewport(0, 0, this->GetWidth(), this->GetHeight()); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0f, GLdouble(this->GetWidth()), GLdouble(this->GetHeight()), 0.0f, -1, 1); + glMatrixMode(GL_MODELVIEW); + } + + if (GB.CanRaise(hWindow, EVENT_Open)) + GB.Raise(hWindow, EVENT_Open,0); + + if ((((CWINDOW *)hWindow)->openGL)) + { + if (GB.CanRaise(hWindow, EVENT_Resize)) + GB.Raise(hWindow, EVENT_Resize,0); + } +} + +void myWin::JoyEvent(SDL_Event& event) +{ + CJOY_info.valid = true; + switch(event.type) + { + case SDL_JOYAXISMOTION: + { + CJOY_info.device = event.jaxis.which; + CJOY_info.id = event.jaxis.axis; + CJOY_info.value1 = event.jaxis.value; + CJOY_info.value2 = 0; + GB.Raise(hWindow, EVENT_JoyAxisMotion, 0); + break; + } + case SDL_JOYHATMOTION: + { + CJOY_info.device = event.jaxis.which; + CJOY_info.id = event.jhat.hat; + CJOY_info.value1 = event.jhat.value; + CJOY_info.value2 = 0; + GB.Raise(hWindow, EVENT_JoyHatMotion, 0); + break; + } + case SDL_JOYBALLMOTION: + { + CJOY_info.device = event.jaxis.which; + CJOY_info.id = event.jball.ball; + CJOY_info.value1 = event.jball.xrel; + CJOY_info.value2 = event.jball.yrel; + GB.Raise(hWindow, EVENT_JoyBallMotion, 0); + break; + } + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + { + CJOY_info.device = event.jaxis.which; + CJOY_info.id = event.jbutton.button; + CJOY_info.value1 = 0; + CJOY_info.value2 = 0; + if (event.jbutton.state == SDL_PRESSED) + GB.Raise(hWindow, EVENT_JoyButtonPressed, 0); + else + GB.Raise(hWindow, EVENT_JoyButtonReleased, 0); + break; + } + default: + break; + } + + CJOY_info.valid = false; +} + +static void convert_unicode_to_utf8(Uint16 unicode, char *buffer) +{ + if (unicode < 0x80) + { + buffer[0] = unicode; + buffer[1] = 0; + } + else if (unicode < 0x800) + { + buffer[0] = 0xC0 | (unicode >> 6); + buffer[1] = 0x80 | (unicode & 0x3F); + buffer[2] = 0; + } + else + { + buffer[0] = 0xE0 | (unicode >> 12); + buffer[1] = 0x80 | ((unicode >> 6) & 0x3F); + buffer[2] = 0x80 | (unicode & 0x3F); + buffer[3] = 0; + } +} + +void myWin::KeyEvent(SDL_KeyboardEvent *keyEvent, int eventType) +{ + CKEY_info.valid++; + + CKEY_info.code = keyEvent->keysym.sym; + CKEY_info.state = keyEvent->keysym.mod; + convert_unicode_to_utf8(keyEvent->keysym.unicode, CKEY_info.text); + + //SDLapp->LockX11(); + //CKEY_info.code = XKeycodeToKeysym(SDLapp->X11appDisplay(), keyEvent->keysym.scancode, 0); + //SDLapp->UnlockX11(); + //CKEY_info.state = keyEvent->keysym.mod; + + if (eventType == SDL_KEYDOWN) + GB.Raise(hWindow, EVENT_KeyPressed,0); + else + GB.Raise(hWindow, EVENT_KeyReleased,0); + + CKEY_info.valid--; +} + +void myWin::MouseButtonEvent(SDL_MouseButtonEvent *mouseEvent) +{ + CMOUSE_info.valid = true; + CMOUSE_info.x = mouseEvent->x; + CMOUSE_info.y = mouseEvent->y; + CMOUSE_info.state = mouseEvent->button; + CMOUSE_info.keymod = SDL_GetModState(); + + if (mouseEvent->type == SDL_MOUSEBUTTONDOWN) + GB.Raise(hWindow, EVENT_MouseDown,0); + else + GB.Raise(hWindow, EVENT_MouseUp,0); + + CMOUSE_info.valid = false; + +} + +void myWin::MouseMotionEvent(SDL_MouseMotionEvent *mouseEvent) +{ + CMOUSE_info.relx = mouseEvent->xrel; + CMOUSE_info.rely = mouseEvent->yrel; + + // do not raise event if no mouse button are pressed/released && tracking is not set + if ((!mouseEvent->state) && (!(((CWINDOW *)hWindow)->tracking))) + return; + + CMOUSE_info.valid = true; + CMOUSE_info.x = mouseEvent->x; + CMOUSE_info.y = mouseEvent->y; + CMOUSE_info.state = mouseEvent->state; + CMOUSE_info.keymod = SDL_GetModState(); + GB.Raise(hWindow, EVENT_MouseMove,0); + CMOUSE_info.valid = false; +} diff --git a/gb.sdl/src/Cwindow.h b/gb.sdl/src/Cwindow.h new file mode 100644 index 00000000..7c3138a9 --- /dev/null +++ b/gb.sdl/src/Cwindow.h @@ -0,0 +1,78 @@ +/*************************************************************************** + + Cwindow.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWINDOW_H +#define __CWINDOW_H + +#include "gambas.h" +#include "main.h" +#include "SDLwindow.h" +#include "Cfont.h" +#include "Cmouse.h" + +class myWin : public SDLwindow +{ +public: + myWin(void *win):SDLwindow() { hWindow = win; }; + ~myWin() {}; + + void Quit(void ); + void Resize(void ); + void GotFocus(void ); + void LostFocus(void ); + void MouseEnter(void ); + void MouseLeave(void ); + void Update(void ); + void Open(void ); + void JoyEvent(SDL_Event& ); + void KeyEvent(SDL_KeyboardEvent* , int); + void MouseButtonEvent(SDL_MouseButtonEvent* ); + void MouseMotionEvent(SDL_MouseMotionEvent* ); +private: + void *hWindow; +}; + +typedef + struct { + GB_BASE ob; + CCURSOR *cursor; + + myWin *id; + bool openGL; + bool tracking; + // framerate control + double FPSLimit; // duration of a frame in milliseconds, if 0 -> no framerate limit + double lastTime; + // framerate count + Uint32 startTime; + Uint32 countFrames; + double currentFPS; + } + CWINDOW; + +#ifndef __CWINDOW_CPP +extern GB_DESC CWindow[]; +#endif /* __CWINDOW_CPP */ + +#endif /* __CWINDOW_H */ + diff --git a/gb.sdl/src/Makefile.am b/gb.sdl/src/Makefile.am new file mode 100644 index 00000000..d40414ed --- /dev/null +++ b/gb.sdl/src/Makefile.am @@ -0,0 +1,34 @@ +COMPONENT = gb.sdl +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.sdl.la + +gb_sdl_la_LIBADD = @SDL_LIB@ +gb_sdl_la_LDFLAGS = -module @LD_FLAGS@ @SDL_LDFLAGS@ +gb_sdl_la_CXXFLAGS = $(AM_CXXFLAGS) -DDATA_DIR=\"$(DESTDIR)$(gbdatadir)/$(COMPONENT)\" +gb_sdl_la_CPPFLAGS = @SDL_INC@ + +gb_sdl_la_SOURCES = \ + SDL_h.h \ + SDLapp.h SDLapp.cpp \ + SDLcore.h SDLcore.cpp \ + SDLdebug.h SDLdebug.cpp \ + SDLerror.h SDLerror.cpp \ + SDLfont.h SDLfont.cpp \ + SDLgfx.h SDLgfx.cpp \ + SDLcursor.h SDLcursor.cpp \ + SDLosrender.h SDLosrender.cpp \ + SDLsurface.h SDLsurface.cpp \ + SDLtexture.h SDLtexture.cpp \ + SDLwindow.h SDLwindow.cpp \ + Cconst.h Cconst.cpp \ + Cdesktop.h Cdesktop.cpp \ + Cdraw.h Cdraw.cpp \ + Cfont.h Cfont.cpp \ + Cimage.h Cimage.cpp \ + Cjoystick.h Cjoystick.cpp \ + Ckey.h Ckey.cpp \ + Cmouse.h Cmouse.cpp \ + Cwindow.h Cwindow.cpp \ + default_font.h \ + main.h main.cpp diff --git a/gb.sdl/src/SDL_h.h b/gb.sdl/src/SDL_h.h new file mode 100644 index 00000000..62795dfa --- /dev/null +++ b/gb.sdl/src/SDL_h.h @@ -0,0 +1,84 @@ +/*************************************************************************** + + SDL_h.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDL_H_H +#define __SDL_H_H + +#include +#include +#include +#include + +#include "SDLapp.h" +#include "SDLerror.h" +#include "SDLdebug.h" + +namespace SDL +{ + // constant values for drawing + // line + enum LineStyle { + NoLine = 0, + SolidLine, + DashLine, + DotLine, + DashDotLine, + DashDotDotLine}; + // filling + enum FillStyle { + NoFill = 0, + SolidFill, + VerticalFill, + HorizontalFill, + CrossFill, + BackDiagFill, + DiagFill, + DiagCrossFill, + Dense1Fill, + Dense2Fill, + Dense3Fill, + Dense4Fill, + Dense5Fill, + Dense6Fill, + Dense7Fill}; + // cursor shapes + enum CursorsShape { + CustomCursor = -3, + DefaultCursor = -2, + BlankCursor = -1, + ArrowCursor = XC_left_ptr, + CrossCursor = XC_crosshair, + WaitCursor = XC_watch, + PointingHandCursor = XC_hand2, + SizeAllCursor = XC_fleur, + SizeHorCursor = XC_sb_h_double_arrow, + SizeVerCursor = XC_sb_v_double_arrow, + SizeFDiagCursor = XC_bottom_right_corner, + SizeBDiagCursor = XC_top_right_corner, + SplitHCursor = XC_sb_h_double_arrow, + SplitVCursor = XC_sb_v_double_arrow, + TextCursor = XC_xterm}; +} + +#endif /* __SDL_H_H */ + diff --git a/gb.sdl/src/SDLapp.cpp b/gb.sdl/src/SDLapp.cpp new file mode 100644 index 00000000..44398cfd --- /dev/null +++ b/gb.sdl/src/SDLapp.cpp @@ -0,0 +1,266 @@ +/*************************************************************************** + + SDLapp.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "SDLapp.h" + +#include "SDLcore.h" +#include "SDLwindow.h" +#include "SDL_ttf.h" + +#include +#include + +int SDLapplication::AppCount = 0; +int SDLapplication::LockX11Count = 0; + +SDLapplication *SDLapp; + +SDLapplication::SDLapplication(int &argc, char **argv) +{ + // init is already done ! + if (SDLapplication::AppCount) + { + SDLapplication::AppCount++; + return; + } + + std::string sMsg = "Failed to init: "; + Uint32 sysInit = SDL_WasInit(SDL_INIT_EVERYTHING); + + // if audio is defined, sdl was init by gb.sdl.sound component ! + if (sysInit & SDL_INIT_AUDIO) + { + if (SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK)<0) + { + sMsg =+ SDL_GetError(); + goto _error; + } + } + else + { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE | SDL_INIT_JOYSTICK)<0) + { + sMsg =+ SDL_GetError(); + goto _error; + } + } + + if (TTF_Init()<0) + { + sMsg =+ TTF_GetError(); + goto _error; + } + + SDLapp = this; + + SDL_EnableUNICODE(1); + + SDLcore::Init(); + SDLdebug::Init(); + + return; + +_error: + std::cout << sMsg << std::endl; + exit (-1); +} + +SDLapplication::~SDLapplication() +{ + // stop SDL only if it's the last ~SDLapplication call + if (SDLapplication::AppCount>1) + { + SDLapplication::AppCount--; + return; + } + + TTF_Quit(); + Uint32 sysInit = SDL_WasInit(SDL_INIT_EVERYTHING); + + // if audio is defined, gb.sdl.audio component still not closed ! + if (sysInit & SDL_INIT_AUDIO) + SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK); + else + SDL_Quit(); +} + +static int poll_event(SDL_Event *event, bool no_input) +{ + uint mask; + + SDL_PumpEvents(); + + mask = SDL_ALLEVENTS; + if (no_input) + mask ^= SDL_KEYEVENTMASK | SDL_MOUSEEVENTMASK | SDL_JOYEVENTMASK | SDL_QUITMASK; + + /* We can't return -1, just return 0 (no event) on error */ + if ( SDL_PeepEvents(event, 1, SDL_GETEVENT, mask) <= 0 ) + return 0; + return 1; +} + + +void SDLapplication::ManageEvents(bool no_input) +{ + SDL_Event event; + + while (poll_event(&event, no_input)) + { + if (!this->HaveWindows()) + break; + + switch(event.type) + { + case SDL_QUIT: + SDLcore::GetWindow()->Quit(); + break; + case SDL_ACTIVEEVENT: + if (event.active.state==SDL_APPINPUTFOCUS) + { + if (event.active.gain) + SDLcore::GetWindow()->GotFocus(); + else + SDLcore::GetWindow()->LostFocus(); + } + if (event.active.state==SDL_APPMOUSEFOCUS) + { + if (event.active.gain) + SDLcore::GetWindow()->MouseEnter(); + else + SDLcore::GetWindow()->MouseLeave(); + } + break; + case SDL_VIDEORESIZE: + SDLcore::GetWindow()->SetWidth(event.resize.w); + SDLcore::GetWindow()->SetHeight(event.resize.h); + SDLcore::GetWindow()->Resize(); + + // take care if window is closed during resize + if (this->HaveWindows()) + SDLcore::GetWindow()->Show(); + break; + case SDL_JOYAXISMOTION: + case SDL_JOYHATMOTION: + case SDL_JOYBALLMOTION: + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + // Only raise joysticks events if window has input keyboard focus + if (SDL_GetAppState() & SDL_APPINPUTFOCUS) + SDLcore::GetWindow()->JoyEvent(event); + break; + case SDL_KEYDOWN: + case SDL_KEYUP: + SDLcore::GetWindow()->KeyEvent(&event.key, event.type); + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + SDLcore::GetWindow()->MouseButtonEvent(&event.button); + break; + case SDL_MOUSEMOTION: + SDLcore::GetWindow()->MouseMotionEvent(&event.motion); + break; + default: + break; + } + } + + if (this->HaveWindows()) // take care if window wasn't close during events + SDLcore::GetWindow()->Update(); + +} + +bool SDLapplication::HaveWindows() +{ + return (SDLcore::GetWindow() ? true : false); +} + +int SDLapplication::DesktopWidth() +{ + LockX11(); + int Width = XDisplayWidth(display, DefaultScreen(display)); + UnlockX11(); + + return (Width); +} + +int SDLapplication::DesktopHeight() +{ + LockX11(); + int Height = XDisplayHeight(display, DefaultScreen(display)); + UnlockX11(); + + return (Height); +} + +Window SDLapplication::X11appRootWin() +{ + LockX11(); + Window win = XDefaultRootWindow(display); + UnlockX11(); + + return (win); +} + +Window SDLapplication::CurrentWin() +{ + LockX11(); + // refresh window variable ;) + UnlockX11(); + + return (window); +} + +Display *SDLapplication::X11appDisplay() +{ + LockX11(); + // refresh display variable ;) + UnlockX11(); + + return (display); +} + +void SDLapplication::LockX11() +{ + SDLapplication::LockX11Count++; + SDL_VERSION(&info.version); + SDL_GetWMInfo(&info); + + if (SDLapplication::LockX11Count==1) + info.info.x11.lock_func(); + + display = info.info.x11.display; + window = info.info.x11.window; +} + +void SDLapplication::UnlockX11() +{ + SDLapplication::LockX11Count--; + + if (SDLapplication::LockX11Count>1) + return; + + SDLapplication::LockX11Count = 0; + info.info.x11.unlock_func(); +} + diff --git a/gb.sdl/src/SDLapp.h b/gb.sdl/src/SDLapp.h new file mode 100644 index 00000000..026f2054 --- /dev/null +++ b/gb.sdl/src/SDLapp.h @@ -0,0 +1,61 @@ +/*************************************************************************** + + SDLapp.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLAPP_H +#define __SDLAPP_H + +#include "SDL_h.h" + +class SDLapplication +{ +public: + SDLapplication(int &argc, char **argv); + virtual ~SDLapplication(); + + void ManageEvents(bool no_input = false); + bool HaveWindows(void ); + + int DesktopWidth(void ); + int DesktopHeight(void ); + Window X11appRootWin(void ); + Window CurrentWin(void ); + Display* X11appDisplay(void ); + + virtual void ManageError(const char* ) = 0; + // needed if calling X11 funcs ! + void LockX11(void ); + void UnlockX11(void ); + +private: + // datas + static int AppCount; + static int LockX11Count; + SDL_SysWMinfo info; + Display *display; + Window window; +}; + +extern SDLapplication *SDLapp; + +#endif /* __SDLAPP_H */ + diff --git a/gb.sdl/src/SDLcore.cpp b/gb.sdl/src/SDLcore.cpp new file mode 100644 index 00000000..6ab75e2b --- /dev/null +++ b/gb.sdl/src/SDLcore.cpp @@ -0,0 +1,52 @@ +/*************************************************************************** + + SDLcore.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "SDLcore.h" + +#include "SDLwindow.h" +#include "SDLapp.h" + +const SDL_VideoInfo *SDLcore::hVideoInfo = NULL; +SDLwindow *SDLcore::hWindow = NULL; + +void SDLcore::RegisterWindow(SDLwindow *window) +{ + if (window) + { + if (hWindow) + hWindow->Close(); + } + + hWindow = window; +} + +void SDLcore::Init(void) +{ + hVideoInfo = SDL_GetVideoInfo(); +} + +void SDLcore::RaiseError(std::string error) +{ + if (SDLapp) + SDLapp->ManageError(error.c_str()); +} diff --git a/gb.sdl/src/SDLcore.h b/gb.sdl/src/SDLcore.h new file mode 100644 index 00000000..aeefda00 --- /dev/null +++ b/gb.sdl/src/SDLcore.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + SDLcore.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLCORE_H +#define __SDLCORE_H + +#include "SDL_h.h" + +#include + +class SDLwindow; +class SDLapplication; + +class SDLcore +{ +public: + static void Init(void); + + // this window will receive the events + static void RegisterWindow(SDLwindow* ); + static SDLwindow* GetWindow(void ) { return (hWindow); }; + static void RaiseError(std::string ); +private: + static const SDL_VideoInfo* hVideoInfo; + // shown window (for events) + static SDLwindow* hWindow; +}; + +#endif /* __SDLCORE_H */ + diff --git a/gb.sdl/src/SDLcursor.cpp b/gb.sdl/src/SDLcursor.cpp new file mode 100644 index 00000000..a24f5268 --- /dev/null +++ b/gb.sdl/src/SDLcursor.cpp @@ -0,0 +1,126 @@ +/*************************************************************************** + + SDLcursor.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "SDLcursor.h" +#include "SDLcore.h" + +#include + +SDLcursor::SDLcursor() +{ + Display *myDisplay = SDLapp->X11appDisplay(); + hCursor = XcursorLibraryLoadCursor(myDisplay, XcursorGetTheme(myDisplay)); + hShape = SDL::DefaultCursor; + hImgCursor = NULL; +} + +SDLcursor::SDLcursor(const SDLcursor& curs) +{ + hCursor = curs.hCursor; + hShape = curs.hShape; + hImgCursor = NULL; + + if (curs.hImgCursor) + { + std::cout << curs.hImgCursor->width << " " << curs.hImgCursor->height << std::endl; + hImgCursor = XcursorImageCreate(curs.hImgCursor->width, curs.hImgCursor->height); + memcpy (hImgCursor->pixels, curs.hImgCursor->pixels, hImgCursor->width * hImgCursor->height * 4); + } + +} + +SDLcursor::~SDLcursor() +{ + if (hImgCursor) + XcursorImageDestroy(hImgCursor); +} + +void SDLcursor::Show(Window w) +{ + Cursor cursor = hCursor; + int shape = hShape; + Display *myDisplay = SDLapp->X11appDisplay(); + + if (hShape == SDL::BlankCursor) + { + SDL_ShowCursor(SDL_DISABLE); + return; + } + + if (SDL_ShowCursor(SDL_QUERY) == SDL_DISABLE) + SDL_ShowCursor(SDL_ENABLE); + + + if (shape == SDL::DefaultCursor) + shape = SDL::ArrowCursor; + + SDLapp->LockX11(); + if (shape != SDL::CustomCursor) + cursor = XcursorShapeLoadCursor(myDisplay, shape); + else + cursor = XcursorImageLoadCursor(myDisplay, hImgCursor); + + XDefineCursor(myDisplay, w, cursor); + SDLapp->UnlockX11(); + +} + +void SDLcursor::SetShape(int shape) +{ + if (hShape == shape) + return; + + if (hShape == SDL::CustomCursor && !hImgCursor) + return; + + hShape = shape; + +} + +void SDLcursor::SetCursor(SDLsurface *image, int xhot, int yhot) +{ + if (image->IsNull()) { + hShape = SDL::BlankCursor; + return; + } + + if (hImgCursor) + XcursorImageDestroy(hImgCursor); + + hImgCursor = XcursorImageCreate(image->GetWidth(), image->GetHeight()); + + if ((xhot < 0)) + xhot = 0; + if (((unsigned int)xhot > hImgCursor->width)) + xhot = hImgCursor->width; + if ((yhot < 0)) + yhot = 0; + if (((unsigned int)yhot > hImgCursor->height)) + yhot = hImgCursor->height; + + memcpy (hImgCursor->pixels, image->GetData(), image->GetWidth() * image->GetHeight() * 4); + hImgCursor->xhot = xhot; + hImgCursor->yhot = yhot; + hShape = SDL::CustomCursor; +} + diff --git a/gb.sdl/src/SDLcursor.h b/gb.sdl/src/SDLcursor.h new file mode 100644 index 00000000..2054b205 --- /dev/null +++ b/gb.sdl/src/SDLcursor.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + SDLcursor.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLCURSOR_H +#define __SDLCURSOR_H + +#include "SDL_h.h" +#include "SDLsurface.h" + +#include +#include + +class SDLcursor +{ +public: + SDLcursor(); + SDLcursor(const SDLcursor& cursor); + ~SDLcursor(); + + void Show(Window ); + void SetShape(int ); + void SetCursor(SDLsurface* image, int xhot = -1, int yhot = -1); + + int GetShape(void ) { return hShape; } + +private: + Cursor hCursor; + int hShape; + XcursorImage *hImgCursor; +}; + +#endif /* SDLCURSOR_H */ diff --git a/gb.sdl/src/SDLdebug.cpp b/gb.sdl/src/SDLdebug.cpp new file mode 100644 index 00000000..4ca0f8ce --- /dev/null +++ b/gb.sdl/src/SDLdebug.cpp @@ -0,0 +1,98 @@ +/*************************************************************************** + + SDLdebug.cpp + + (c) 2006-2008 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "SDLdebug.h" + +#include +#include +#include + +#include +#include +#include + +std::string DebugString = ""; + +void SDLdebug::Init() +{ + char* debug = std::getenv("DEBUG_GB_SDL"); + + if (debug != NULL) + DebugString = debug; +} + +void SDLdebug::Print(const char* message, ...) +{ + std::string msg; + const char *p; + std::va_list list; + va_start(list, message); + + if (DebugString.empty()) + return; + + for (p = message ; *p ; p++) + { + std::stringstream res; + + if (*p != '%') + { + msg.push_back(*p); + continue; + } + + p++; + switch(*p) + { + int val; + char* strval; + + //long + case 'd' : + val = va_arg(list, long); + res << val; + break; + //hex + case 'h' : + val = va_arg(list, long); + res << std::hex << val; + break; + //boolean + case 'b' : + val = va_arg(list, int); + (val) ? res << "True": res << "False"; + break; + //char string + case 's' : + strval = va_arg(list, char*); + res << strval; + break; + default : + res << "%" << *p; + } + msg = msg.append(res.str()); + } + + va_end(list); + std::cerr << "==GB.SDL== " << msg << std::endl; +} diff --git a/gb.sdl/src/SDLdebug.h b/gb.sdl/src/SDLdebug.h new file mode 100644 index 00000000..6c791eab --- /dev/null +++ b/gb.sdl/src/SDLdebug.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + SDLdebug.h + + (c) 2006-2008 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLDEBUG_H +#define __SDLDEBUG_H + +namespace SDLdebug { + +void Init(void ); +void Print(const char* message, ...); + +} + +#endif /* __SDLDEBUG_H */ \ No newline at end of file diff --git a/gb.sdl/src/SDLerror.cpp b/gb.sdl/src/SDLerror.cpp new file mode 100644 index 00000000..ed7b10d1 --- /dev/null +++ b/gb.sdl/src/SDLerror.cpp @@ -0,0 +1,30 @@ +/*************************************************************************** + + SDLerror.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "SDLerror.h" +#include "SDLcore.h" + +void SDLerror::RaiseError(std::string error) +{ + SDLcore::RaiseError(error); +} diff --git a/gb.sdl/src/SDLerror.h b/gb.sdl/src/SDLerror.h new file mode 100644 index 00000000..0efc6c61 --- /dev/null +++ b/gb.sdl/src/SDLerror.h @@ -0,0 +1,41 @@ +/*************************************************************************** + + SDLerror.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLERROR_H +#define __SDLERROR_H + +#include + +#define COMP_WARN "gb.sdl warning: &1", +#define COMP_ERR "gb.sdl error: &1", +#define COMP_INFO "gb.sdl info: &1", + +class SDLerror +{ +public: + static void RaiseError(std::string ); +// static void RaiseWarning(std::string ); +}; + +#endif /* __SDLERROR_H */ + diff --git a/gb.sdl/src/SDLfont.cpp b/gb.sdl/src/SDLfont.cpp new file mode 100644 index 00000000..df621158 --- /dev/null +++ b/gb.sdl/src/SDLfont.cpp @@ -0,0 +1,586 @@ +/*************************************************************************** + + SDLfont.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "SDLfont.h" +#include "SDLapp.h" +#include "gb_common.h" +#include "main.h" +#include +#include + +#include "default_font.h" + +#if 0 +typedef struct { + std::string name; + std::string realname; + std::string foundry; + std::string path; + } fontdesc; + +static std::vector fontDB; +static StringList _FontList; + +#define DEFAULT_DPI 72 /* Default DPI size in SDL_TTF */ + +inline bool cmp_db_nocase(const fontdesc x, const fontdesc y) +{ + std::string a = x.name, b = y.name; + transform(a.begin(), a.end(), a.begin(), tolower); + transform(b.begin(), b.end(), b.begin(), tolower); + return (b>a) ? 1 : 0; +} + +StringList SDLfont::GetFontList(void ) +{ + return _FontList; +} + +void SDLfont::Init() +{ + Display *disp = XOpenDisplay(NULL); + int scr = XDefaultScreen(disp); + int i; + + XftFontSet *FntnameSet = XftListFonts(disp, scr, 0, + XFT_FAMILY, NULL); + + // Get the fonts name + for (i = 0; i < FntnameSet->nfont; i++) { + char *name[255]; + char *foundry[255]; + fontdesc font; + unsigned int j; + + XftResult res = XftPatternGetString(FntnameSet->fonts[i], XFT_FAMILY, 0, name); + + if (res!=XftResultMatch) + continue; + + XftFontSet *Fntdetail = XftListFonts(disp, scr, + XFT_FAMILY, XftTypeString, name[0], 0, + XFT_FOUNDRY, NULL); + j = Fntdetail->nfont; + + if (j>1) { + while (j) { + XftPatternGetString(Fntdetail->fonts[j-1], XFT_FOUNDRY, 0, foundry); + font.name = font.realname = name[0]; + font.foundry = foundry[0]; + font.name = font.name + " [" +font.foundry+ "]"; + fontDB.push_back(font); + j--; + } + } + else { + font.name = font.realname = name[0]; + XftPatternGetString(Fntdetail->fonts[0], XFT_FOUNDRY, 0, foundry); + font.foundry = foundry[0]; + fontDB.push_back(font); + } + XFree(Fntdetail); + } + + std::sort(fontDB.begin(), fontDB.end(), cmp_db_nocase); + XFree(FntnameSet); + + std::string font = ""; + i = 0; + while (ifonts[0], XFT_FILE, 0, res); + fontDB[i].path = res[0]; + _FontList.push_back(fontDB[i].name); + XFree(Fntdetail); + i++; + } + + XCloseDisplay(disp); +} +#endif + +#define UNICODE_INVALID 0xFFFFFFFFU + +static const char _char_length[256] = +{ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 +}; + +#define utf8_get_char_length(_c) ((int)_char_length[(unsigned char)(_c)]) + +static int utf8_get_length(const char *sstr, int len) +{ + const uchar *str = (const uchar *)sstr; + int ulen; + int i; + + ulen = 0; + + for (i = 0; i < len; i++) + { + if ((str[i] & 0xC0) != 0x80) + ulen++; + } + + return ulen; +} + +static uint utf8_to_unicode(const char *sstr, int len) +{ + const uchar *str = (const uchar *)sstr; + uint unicode; + + switch (len) + { + case 2: + unicode = (str[1] & 0x3F) + ((str[0] & 0x1F) << 6); + if (unicode < 0x80) + goto _INVALID; + break; + + case 3: + unicode = (str[2] & 0x3F) + ((str[1] & 0x3F) << 6) + ((str[0] & 0xF) << 12); + if (unicode < 0x800) + goto _INVALID; + break; + + case 4: + unicode = (str[3] & 0x3F) + ((str[2] & 0x3F) << 6) + ((str[1] & 0x3F) << 12) + ((str[0] & 0x7) << 18); + if (unicode < 0x10000) + goto _INVALID; + break; + + case 5: + unicode = (str[4] & 0x3F) + ((str[3] & 0x3F) << 6) + ((str[2] & 0x3F) << 12) + ((str[1] & 0x3F) << 18) + ((str[0] & 0x3) << 24); + if (unicode < 0x200000) + goto _INVALID; + break; + + case 6: + unicode = (str[5] & 0x3F) + ((str[4] & 0x3F) << 6) + ((str[3] & 0x3F) << 12) + ((str[2] & 0x3F) << 18) + ((str[1] & 0x3F) << 24) + ((str[0] & 0x1) << 30); + if (unicode < 0x4000000) + goto _INVALID; + break; + + default: + unicode = str[0]; + break; + } + + return unicode; + +_INVALID: + + return UNICODE_INVALID; +} + + +static void render_default_font(uint *dest, int size, const char *text, int len) +{ + static void *jump[] = { &&__0, &&__1, &&__2, &&__3, &&__4, &&__5, &&__6, &&__7, &&__8, &&__9, &&__A, &&__B, &&__C, &&__D, &&__E, &&__F }; + static void *jump2[] = { &&__00, &&__10, &&__20, &&__30, &&__40, &&__50, &&__60, &&__70, &&__80, &&__90, &&__A0, &&__B0, &&__C0, &&__D0, &&__E0, &&__F0 }; + + int lc; + const char *src; + uint *p; + int y; + uchar line; + uint code; + uint col = 0xFFFFFFFF; + + size *= DEFAULT_FONT_WIDTH; + + for(;;) + { + if (!*text) + break; + + lc = utf8_get_char_length(*text); + code = utf8_to_unicode(text, lc); + text += lc; + + if (code >= 33 && code <= 126) + src = _default_font_33_126 + DEFAULT_FONT_HEIGHT * (code - 33); + else if (code >= 160 && code <= 687) + src = _default_font_160_687 + DEFAULT_FONT_HEIGHT * (code - 160); + else + src = NULL; + + if (src) + { + p = dest; + + for (y = 0; y < DEFAULT_FONT_HEIGHT; y++) + { + line = *src++; + if (!line) + { + p += size; + continue; + } + + goto *jump[line & 0xF]; + + __1: p[0] = col; goto __0; + __2: p[1] = col; goto __0; + __3: p[0] = p[1] = col; goto __0; + __4: p[2] = col; goto __0; + __5: p[0] = p[2] = col; goto __0; + __6: p[1] = p[2] = col; goto __0; + __7: p[0] = p[1] = p[2] = col; goto __0; + __8: p[3] = col; goto __0; + __9: p[0] = p[3] = col; goto __0; + __A: p[1] = p[3] = col; goto __0; + __B: p[0] = p[1] = p[3] = col; goto __0; + __C: p[2] = p[3] = col; goto __0; + __D: p[0] = p[2] = p[3] = col; goto __0; + __E: p[1] = p[2] = p[3] = col; goto __0; + __F: p[0] = p[1] = p[2] = p[3] = col; goto __0; + __0: + + goto *jump2[line >> 4]; + + __10: p[4] = col; goto __00; + __20: p[5] = col; goto __00; + __30: p[4] = p[5] = col; goto __00; + __40: p[6] = col; goto __00; + __50: p[4] = p[6] = col; goto __00; + __60: p[5] = p[6] = col; goto __00; + __70: p[4] = p[5] = p[6] = col; goto __00; + __80: p[7] = col; goto __00; + __90: p[4] = p[7] = col; goto __00; + __A0: p[5] = p[7] = col; goto __00; + __B0: p[4] = p[5] = p[7] = col; goto __00; + __C0: p[6] = p[7] = col; goto __00; + __D0: p[4] = p[6] = p[7] = col; goto __00; + __E0: p[5] = p[6] = p[7] = col; goto __00; + __F0: p[4] = p[5] = p[6] = p[7] = col; goto __00; + __00: + + p += size; + } + } + + dest += DEFAULT_FONT_WIDTH; + } +} + + +//------------------------------------------------------------------------- + +SDLfont::SDLfont(const char *fontfile) +{ + hfontsize = DEFAULT_FONT_HEIGHT; + hSDLfont = 0; + _last_surface = 0; + _last_text = 0; + + /*if (!fontfile) + { + hfontname = GB.System.Path(); + hfontname += "/share/gambas" GAMBAS_VERSION_STRING "/gb.sdl/" DEFAULT_FONT; + } + else + hfontname = fontfile;*/ + + if (fontfile) + { + hfontname = fontfile; + OpenFont(hfontname.c_str()); + } +} + +SDLfont::~SDLfont() +{ + GB.FreeString(&_last_text); + if (_last_surface) + _last_surface->Unref(); + if (hSDLfont) + TTF_CloseFont(hSDLfont); +} + +void SDLfont::OpenFont(const char* file) +{ + if (hSDLfont) + TTF_CloseFont(hSDLfont); + + hSDLfont = TTF_OpenFont(file, hfontsize); + + if (UNLIKELY(hSDLfont == NULL)) + SDLerror::RaiseError(TTF_GetError()); +} +#if 0 +void SDLfont::SetFontName(char* name) +{ + std::string font = name; + int i=0; + + while (i= 1024) + return NULL; + + if (_last_surface) + { + if (len == GB.StringLength(_last_text) && strncmp(text, _last_text, len) == 0) + { + _last_surface->Ref(); + return _last_surface; + } + } + + if (hSDLfont) + { + SDL_Color fg = {0xFF, 0xFF, 0xFF}; + + result = TTF_RenderUTF8_Blended(hSDLfont, GB.TempString(text, len), fg); + } + else + { + int size = utf8_get_length(text, len); + + result = SDL_CreateRGBSurface(SDL_SWSURFACE, size * DEFAULT_FONT_WIDTH, DEFAULT_FONT_HEIGHT, 32, + #if SDL_BYTEORDER == SDL_BIG_ENDIAN + 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF); + #else + 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); + #endif + + if (SDL_MUSTLOCK(result)) + SDL_LockSurface(result); + render_default_font((uint *)result->pixels, size, text, len); + if (SDL_MUSTLOCK(result)) + SDL_UnlockSurface(result); + } + + GB.FreeString(&_last_text); + _last_text = GB.NewString(text, len); + + if (_last_surface) + _last_surface->Unref(); + _last_surface = new SDLsurface(result); + _last_surface->Ref(); + + return _last_surface; +} + +int SDLfont::GetDefaultFontSize() +{ + return DEFAULT_FONT_HEIGHT; +} diff --git a/gb.sdl/src/SDLfont.h b/gb.sdl/src/SDLfont.h new file mode 100644 index 00000000..f14d1b51 --- /dev/null +++ b/gb.sdl/src/SDLfont.h @@ -0,0 +1,95 @@ +/*************************************************************************** + + SDLfont.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLFONT_H +#define __SDLFONT_H + +#if 0 +#include +#include +#endif + +#include "SDL_ttf.h" + +#if 0 +#include +#include +#endif + +#include "SDLsurface.h" +#if 0 +typedef std::vector StringList; + +enum _fonttype { + SDLTTF_font = 1, + X_font}; +#endif +class SDLfont +{ +public: + SDLfont(const char* fontfile = 0); + ~SDLfont(); + +// static StringList GetFontList(void ); +// static void Init(void ); + +// void SetFontName(char* name); + void SetFontSize(int size); + void SetFontBold(bool state); + void SetFontItalic(bool state); + void SetFontStrikeout(bool state); + void SetFontUnderline(bool state); + + const char* GetFontName(void ); + int GetFontSize(void ) { return hfontsize; }; + int GetFontAscent(void ); + int GetFontDescent(void ); + + bool IsFontFixed(void ); + bool IsFontBold(void ); + bool IsFontItalic(void ); + bool IsFontStrikeout(void ); + bool IsFontUnderline(void ); + bool IsFontScalable(void ); + + void SizeText(const char* text, int len, int *width, int *height); + SDLsurface* RenderText(const char *text, int len); + + int GetScale(); + static int GetDefaultFontSize(); + +private: + void OpenFont(const char* file); + + SDLsurface *_last_surface; + char *_last_text; + + int hfontsize; + std::string hfontname; + int hSDLfontstyle; + + /* SDL_TTF font */ + TTF_Font *hSDLfont; +}; + +#endif /* _SDLFONT_H */ diff --git a/gb.sdl/src/SDLgfx.cpp b/gb.sdl/src/SDLgfx.cpp new file mode 100644 index 00000000..a6d57672 --- /dev/null +++ b/gb.sdl/src/SDLgfx.cpp @@ -0,0 +1,533 @@ +/*************************************************************************** + + SDLgfx.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "SDLgfx.h" +#include "SDLerror.h" +#include "SDLcore.h" +#include "SDLapp.h" +#include "SDLwindow.h" +#include "SDLsurface.h" +#include "SDLtexture.h" + +#include +#include + +// for ellipses +#define PI 3.14159265359 + +// debug +// #define DEBUG_GFX + +// fill patterns +static GLubyte VertPattern[] = {0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22}; + +static GLubyte HoriPattern[] = {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static GLubyte CrosPattern[] = {0xFF, 0xFF, 0xFF, 0xFF, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0xFF, 0xFF, 0xFF, 0xFF, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0xFF, 0xFF, 0xFF, 0xFF, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0xFF, 0xFF, 0xFF, 0xFF, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0xFF, 0xFF, 0xFF, 0xFF, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0xFF, 0xFF, 0xFF, 0xFF, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0xFF, 0xFF, 0xFF, 0xFF, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0xFF, 0xFF, 0xFF, 0xFF, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22}; + +static GLubyte BdiaPattern[] = {0x80, 0x80, 0x80, 0x80, 0x40, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20, 0x20, + 0x10, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x80, 0x80, 0x80, 0x80, + 0x40, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10, 0x10, + 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x02, 0x02, + 0x01, 0x01, 0x01, 0x01, 0x80, 0x80, 0x80, 0x80, 0x40, 0x40, 0x40, 0x40, + 0x20, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08, + 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x80, 0x80, 0x80, 0x80, 0x40, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20, 0x20, + 0x10, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01}; + +static GLubyte DiaPattern[] = {0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04, + 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, + 0x40, 0x40, 0x40, 0x40, 0x80, 0x80, 0x80, 0x80, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, + 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x40, 0x40, 0x40, 0x40, + 0x80, 0x80, 0x80, 0x80, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, + 0x20, 0x20, 0x20, 0x20, 0x40, 0x40, 0x40, 0x40, 0x80, 0x80, 0x80, 0x80, + 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04, + 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, + 0x40, 0x40, 0x40, 0x40, 0x80, 0x80, 0x80, 0x80}; + +static GLubyte DiaCPattern[] = {0x81, 0x81, 0x81, 0x81, 0x42, 0x42, 0x42, 0x42, 0x24, 0x24, 0x24, 0x24, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24, 0x24, + 0x42, 0x42, 0x42, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x42, 0x42, 0x42, 0x42, 0x24, 0x24, 0x24, 0x24, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24, 0x24, 0x42, 0x42, 0x42, 0x42, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x42, 0x42, 0x42, 0x42, + 0x24, 0x24, 0x24, 0x24, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x24, 0x24, 0x24, 0x24, 0x42, 0x42, 0x42, 0x42, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x42, 0x42, 0x42, 0x42, 0x24, 0x24, 0x24, 0x24, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24, 0x24, + 0x42, 0x42, 0x42, 0x42, 0x81, 0x81, 0x81, 0x81}; + +static GLubyte Dns1Pattern[] = {0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +static GLubyte Dns2Pattern[] = {0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0x77, 0x77, 0x77, 0x77, + 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, + 0x77, 0x77, 0x77, 0x77, 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, + 0xFF, 0xFF, 0xFF, 0xFF, 0x77, 0x77, 0x77, 0x77, 0xFF, 0xFF, 0xFF, 0xFF, + 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0x77, 0x77, 0x77, 0x77, + 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, + 0x77, 0x77, 0x77, 0x77, 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, + 0xFF, 0xFF, 0xFF, 0xFF, 0x77, 0x77, 0x77, 0x77, 0xFF, 0xFF, 0xFF, 0xFF, + 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0x77, 0x77, 0x77, 0x77, + 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, + 0x77, 0x77, 0x77, 0x77, 0xFF, 0xFF, 0xFF, 0xFF}; + +static GLubyte Dns3Pattern[] = {0xDD, 0xDD, 0xDD, 0xDD, 0xAA, 0xAA, 0xAA, 0xAA, 0x77, 0x77, 0x77, 0x77, + 0xAA, 0xAA, 0xAA, 0xAA, 0xDD, 0xDD, 0xDD, 0xDD, 0xAA, 0xAA, 0xAA, 0xAA, + 0x77, 0x77, 0x77, 0x77, 0xAA, 0xAA, 0xAA, 0xAA, 0xDD, 0xDD, 0xDD, 0xDD, + 0xAA, 0xAA, 0xAA, 0xAA, 0x77, 0x77, 0x77, 0x77, 0xAA, 0xAA, 0xAA, 0xAA, + 0xDD, 0xDD, 0xDD, 0xDD, 0xAA, 0xAA, 0xAA, 0xAA, 0x77, 0x77, 0x77, 0x77, + 0xAA, 0xAA, 0xAA, 0xAA, 0xDD, 0xDD, 0xDD, 0xDD, 0xAA, 0xAA, 0xAA, 0xAA, + 0x77, 0x77, 0x77, 0x77, 0xAA, 0xAA, 0xAA, 0xAA, 0xDD, 0xDD, 0xDD, 0xDD, + 0xAA, 0xAA, 0xAA, 0xAA, 0x77, 0x77, 0x77, 0x77, 0xAA, 0xAA, 0xAA, 0xAA, + 0xDD, 0xDD, 0xDD, 0xDD, 0xAA, 0xAA, 0xAA, 0xAA, 0x77, 0x77, 0x77, 0x77, + 0xAA, 0xAA, 0xAA, 0xAA, 0xDD, 0xDD, 0xDD, 0xDD, 0xAA, 0xAA, 0xAA, 0xAA, + 0x77, 0x77, 0x77, 0x77, 0xAA, 0xAA, 0xAA, 0xAA}; + +static GLubyte Dns4Pattern[] = {0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, + 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, + 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, + 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, + 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, + 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, + 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, + 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, + 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, + 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, + 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA}; + +static GLubyte Dns5Pattern[] = {0xAA, 0xAA, 0xAA, 0xAA, 0x44, 0x44, 0x44, 0x44, 0xAA, 0xAA, 0xAA, 0xAA, + 0x11, 0x11, 0x11, 0x11, 0xAA, 0xAA, 0xAA, 0xAA, 0x44, 0x44, 0x44, 0x44, + 0xAA, 0xAA, 0xAA, 0xAA, 0x11, 0x11, 0x11, 0x11, 0xAA, 0xAA, 0xAA, 0xAA, + 0x44, 0x44, 0x44, 0x44, 0xAA, 0xAA, 0xAA, 0xAA, 0x11, 0x11, 0x11, 0x11, + 0xAA, 0xAA, 0xAA, 0xAA, 0x44, 0x44, 0x44, 0x44, 0xAA, 0xAA, 0xAA, 0xAA, + 0x11, 0x11, 0x11, 0x11, 0xAA, 0xAA, 0xAA, 0xAA, 0x44, 0x44, 0x44, 0x44, + 0xAA, 0xAA, 0xAA, 0xAA, 0x11, 0x11, 0x11, 0x11, 0xAA, 0xAA, 0xAA, 0xAA, + 0x44, 0x44, 0x44, 0x44, 0xAA, 0xAA, 0xAA, 0xAA, 0x11, 0x11, 0x11, 0x11, + 0xAA, 0xAA, 0xAA, 0xAA, 0x44, 0x44, 0x44, 0x44, 0xAA, 0xAA, 0xAA, 0xAA, + 0x11, 0x11, 0x11, 0x11, 0xAA, 0xAA, 0xAA, 0xAA, 0x44, 0x44, 0x44, 0x44, + 0xAA, 0xAA, 0xAA, 0xAA, 0x11, 0x11, 0x11, 0x11}; + +static GLubyte Dns6Pattern[] = {0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00}; + +static GLubyte Dns7Pattern[] = {0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static void SetLinePattern(int value) +{ + GLushort pattern = 0xFFFF; + + if (value == SDL::SolidLine) + return; + if (value == SDL::DotLine) + pattern = 0xCCCC; + if (value == SDL::DashLine) + pattern = 0xAAAA; + if (value == SDL::DashDotLine) + pattern = 0xE4E4; + if (value == SDL::DashDotDotLine) + pattern = 0xF98C; + + glEnable(GL_LINE_STIPPLE); + glLineStipple(2, pattern); +} + +static void SetFillPattern(int value) +{ + if (!value) + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + else + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + if (value <= SDL::SolidFill) + return; + + glEnable(GL_POLYGON_STIPPLE); + + if (value == SDL::VerticalFill) + glPolygonStipple(VertPattern); + + if (value == SDL::HorizontalFill) + glPolygonStipple(HoriPattern); + + if (value == SDL::CrossFill) + glPolygonStipple(CrosPattern); + + if (value == SDL::BackDiagFill) + glPolygonStipple(BdiaPattern); + + if (value == SDL::DiagFill) + glPolygonStipple(DiaPattern); + + if (value == SDL::DiagCrossFill) + glPolygonStipple(DiaCPattern); + + if (value == SDL::Dense1Fill) + glPolygonStipple(Dns1Pattern); + + if (value == SDL::Dense2Fill) + glPolygonStipple(Dns2Pattern); + + if (value == SDL::Dense3Fill) + glPolygonStipple(Dns3Pattern); + + if (value == SDL::Dense4Fill) + glPolygonStipple(Dns4Pattern); + + if (value == SDL::Dense5Fill) + glPolygonStipple(Dns5Pattern); + + if (value == SDL::Dense6Fill) + glPolygonStipple(Dns6Pattern); + + if (value == SDL::Dense7Fill) + glPolygonStipple(Dns7Pattern); +} + +SDLgfx::SDLgfx(SDLwindow *window) +{ + hTex = NULL; + resetGfx(); +} + +SDLgfx::SDLgfx(SDLsurface *surface) +{ + if (!SDLcore::GetWindow()) + { + SDLerror::RaiseError("Window need to be opened first !"); + return; + } + + hTex = surface->GetTexture(); + resetGfx(); +} + +void SDLgfx::resetGfx(void) +{ + hLine = SDL::SolidLine; + hLineWidth = 1; + hFill = SDL::NoFill; + rotx = roty = rotz = 0; + scalex = scaley = 1.0f; +} + +void SDLgfx::SetColor(Uint32 color) +{ + glColor4f((GLfloat((color >> 16) & 0xFF)/255), (GLfloat((color >> 8) & 0xFF)/255), (GLfloat(color & 0xFF)/255), + (GLfloat(~(color >> 24) & 0xFF)/255)); +} + +void SDLgfx::SetLineStyle(int style) +{ + if (style>SDL::DashDotDotLine) + style = SDL::DashDotDotLine; + + hLine = style; +} + +void SDLgfx::SetFillStyle(int style) +{ + if (style>SDL::Dense7Fill) + style = SDL::Dense7Fill; + + hFill = style; +} + +void SDLgfx::Clear(void) +{ + SetContext(); + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); +} + +void SDLgfx::DrawPixel(int x, int y) +{ + SetContext(); + + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glBegin(GL_POINTS); + glVertex2i(x, y); + glEnd(); + + glPopAttrib(); +} + +void SDLgfx::DrawLine(int x1, int y1, int x2, int y2) +{ + if (!hLine) // SDLgfx::NoLine + return; + + SetContext(); + + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + SetLinePattern(hLine); + glLineWidth(GLfloat(hLineWidth)); + + glBegin(GL_LINES); + glVertex2i(x1, y1); + glVertex2i(x2, y2); + glEnd(); + + glPopAttrib(); +} + +void SDLgfx::DrawRect(int x, int y, int w, int h) +{ + if (!hFill && !hLine) + return; + + SetContext(); + + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + SetFillPattern(hFill); + + glBegin(GL_QUADS); + glVertex2i(x, y); + glVertex2i(x+w, y); + glVertex2i(x+w, y+h); + glVertex2i(x, y+h); + glEnd(); + + if (hFill>SDL::SolidFill) + { + SetFillPattern(SDL::NoFill); + SetLinePattern(hLine); + glLineWidth(GLfloat(hLineWidth)); + + glBegin(GL_QUADS); + glVertex2i(x, y); + glVertex2i(x+w, y); + glVertex2i(x+w, y+h); + glVertex2i(x, y+h); + glEnd(); + } + + SetFillPattern(SDL::SolidFill); + glPopAttrib(); +} + +void SDLgfx::DrawEllipse(int x, int y, int w, int h) +{ + if (!hFill && !hLine) + return; + + SetContext(); + + double angle; + double step = 2 * PI / 360; + + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glTranslatef(x, y, 0.0f); + SetFillPattern(hFill); + + glBegin(GL_POLYGON); + for (angle=0; angle < 2 * PI; angle += step) + glVertex2d(w * cos(angle), h * sin(angle)); + glEnd(); + + if (hFill>SDL::SolidFill) + { + SetFillPattern(SDL::NoFill); + SetLinePattern(hLine); + glLineWidth(GLfloat(hLineWidth)); + glBegin(GL_POLYGON); + for (angle=0; angle < 2 * PI; angle += step) + glVertex2d(w * cos(angle), h * sin(angle)); + glEnd(); + } + + SetFillPattern(SDL::SolidFill); + glPopAttrib(); + glLoadIdentity(); +} + +void SDLgfx::Blit(SDLsurface *surface, int x, int y, int srcX, int srcY, + int srcWidth, int srcHeight, int width, int height, bool no_filter) +{ + if ((srcX > surface->GetWidth()) || (srcY > surface->GetHeight())) + return; + + if (!surface->GetWidth() || !surface->GetHeight()) + return; + + SDL_Surface *destsurf = GetDestSurface(); + + if ((x > destsurf->w) || (y > destsurf->h)) + return; + + SetContext(); + texinfo info; + + glPushAttrib(GL_ENABLE_BIT); + SDLtexture *texture = surface->GetTexture(); + texture->GetAsTexture(&info); + + GLfloat myWidth = 0, myHeight = 0; + + if ((srcHeight<0) || ((srcY + srcHeight) > surface->GetHeight())) + myHeight = surface->GetHeight() - srcY; + else + myHeight = srcHeight; + + if ((srcWidth<0) || ((srcX + srcWidth) > surface->GetWidth())) + myWidth = surface->GetWidth() - srcX; + else + myWidth = srcWidth; + + GLdouble myTexX, myTexY, myTexHeight, myTexWidth; + + myTexX = ((srcX * info.Width) / surface->GetWidth()); + myTexY = ((srcY * info.Height) / surface->GetHeight()); + myTexWidth = (((srcX + myWidth)* info.Width) / surface->GetWidth()); + myTexHeight = (((srcY + myHeight)* info.Height) / surface->GetHeight()); + + if (width != -1) + myWidth = width; + + if (height != -1) + myHeight = height; + + myWidth = myWidth / 2; + myHeight = myHeight / 2; + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, info.Index); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, no_filter ? GL_NEAREST : GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, no_filter ? GL_NEAREST : GL_LINEAR); + + glTranslatef(myWidth + x, myHeight + y, 0.0f); + glRotatef(rotz, 0, 0, 1); + glScalef(scalex, scaley, 0.0f); + + glBegin(GL_QUADS); + glTexCoord2d(myTexX, myTexY); + glVertex2f(-myWidth, -myHeight); + + glTexCoord2d(myTexX, myTexHeight); + glVertex2f(-myWidth, myHeight); + + glTexCoord2d(myTexWidth, myTexHeight); + glVertex2f(myWidth, myHeight); + + glTexCoord2d(myTexWidth, myTexY); + glVertex2f(myWidth, -myHeight); + glEnd(); + + glPopAttrib(); + glLoadIdentity(); +} + +void SDLgfx::SetContext() +{ + if (!hTex) + SDLcore::GetWindow()->Select(); + else + hTex->Select(); +} + +SDL_Surface *SDLgfx::GetDestSurface() +{ + if (!hTex) + return SDLcore::GetWindow()->GetSdlSurface(); + else + return hTex->GetSurface()->GetSdlSurface(); +} + diff --git a/gb.sdl/src/SDLgfx.h b/gb.sdl/src/SDLgfx.h new file mode 100644 index 00000000..78b11692 --- /dev/null +++ b/gb.sdl/src/SDLgfx.h @@ -0,0 +1,76 @@ +/*************************************************************************** + + SDLgfx.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLGFX_H +#define __SDLGFX_H + +#include "SDL_h.h" + +class SDLwindow; +class SDLsurface; +class SDLtexture; + +class SDLgfx +{ +public: + SDLgfx(SDLwindow* window); + SDLgfx(SDLsurface* surface); + ~SDLgfx() {}; + + void resetGfx(void ); + void SetColor(Uint32 color); + void Scale(GLfloat x, GLfloat y) { scalex = x; scaley = y; } + void Rotate(GLfloat z) { rotz = z; } + + int GetLineStyle(void ) { return hLine; } + int GetLineWidth(void ) { return hLineWidth; } + int GetFillStyle(void ) { return hFill; } + void SetLineStyle(int style); + void SetLineWidth(int width) { hLineWidth = width; } + void SetFillStyle(int style); + + void Clear(void); + void Blit(SDLsurface* s, int x, int y, int srcX = 0, int srcY = 0, int srcWidth = -1, int srcHeight = -1, int width = -1, int height = -1, bool no_filter = false); + void DrawPixel(int x, int y); + void DrawLine(int x1, int y1, int x2, int y2); + void DrawRect(int x, int y, int w, int h); + void DrawEllipse(int x, int y, int w, int h); + +private: + // hTex is null if we are drawing on a window ! + SDLtexture* hTex; + + void SetContext(void ); + SDL_Surface* GetDestSurface(void ); + + // lines and fills + int hLine, hLineWidth; + int hFill; + + // rotations & scaling + GLfloat rotx, roty, rotz; + GLfloat scalex, scaley; +}; + +#endif /* __SDLGFX_H */ + diff --git a/gb.sdl/src/SDLosrender.cpp b/gb.sdl/src/SDLosrender.cpp new file mode 100644 index 00000000..3f2993d6 --- /dev/null +++ b/gb.sdl/src/SDLosrender.cpp @@ -0,0 +1,79 @@ +/*************************************************************************** + + SDLosrender.cpp + + (c) 2006-2008 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "SDLosrender.h" + +#include + +/****** FBO render ******/ +bool FBOrender::hBinded = false; + +FBOrender::FBOrender() +{ + glGenFramebuffersEXT(1, &hFbo); +} + +FBOrender::~FBOrender() +{ + if (hFbo) + { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + glDeleteFramebuffersEXT(1, &hFbo); + } +} + +void FBOrender::Bind(GLuint texid) +{ + GLenum status; + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, hFbo); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texid, 0); + + status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) + std::cerr << "FBO can't be completed : "<< std::hex << status << std::endl; + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, hFbo); + + FBOrender::hBinded = true; + std::cout << "FBO: binding " << hFbo << " with tex " << texid << std::endl; +} + +void FBOrender::Unbind(void ) +{ + if (!FBOrender::hBinded) + return; + + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + std::cout << "FBO: unbinding " << std::endl; + FBOrender::hBinded = false; +} + +bool FBOrender::Check(void ) +{ + return (GLEW_ARB_framebuffer_object || GLEW_EXT_framebuffer_object); +} diff --git a/gb.sdl/src/SDLosrender.h b/gb.sdl/src/SDLosrender.h new file mode 100644 index 00000000..726359d3 --- /dev/null +++ b/gb.sdl/src/SDLosrender.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + SDLosrender.h + + (c) 2006-2008 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLOSRENDER_H +#define __SDLOSRENDER_H + +#include + +class FBOrender +{ +public: + FBOrender(); + ~FBOrender(); + + void Bind(GLuint ); + + static void Unbind(void ); + static bool Check(void ); +private: + GLuint hFbo; + static bool hBinded; +}; + +#endif /* __SDLOSRENDER_H */ + diff --git a/gb.sdl/src/SDLsurface.cpp b/gb.sdl/src/SDLsurface.cpp new file mode 100644 index 00000000..caba3bed --- /dev/null +++ b/gb.sdl/src/SDLsurface.cpp @@ -0,0 +1,284 @@ +/*************************************************************************** + + SDLsurface.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include + +#include "SDLsurface.h" +#include "SDLcore.h" +#include "SDLtexture.h" + +// debugging +// #define DEBUGSURFACE + +SDLsurface::SDLsurface() +{ + hTexture = new SDLtexture(this); + hSurface = 0; + ref = 1; +} + +SDLsurface::SDLsurface(char *data, int width, int height) +{ + hTexture = new SDLtexture(this); + ref = 1; + + hSurface = SDL_CreateRGBSurfaceFrom((void *)data, width, height, 32, width * sizeof(int), +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF); +#else + 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); +#endif + + if (!hSurface) + SDLcore::RaiseError(SDL_GetError()); + else + hTexture->ToLoad(); +} + +SDLsurface::SDLsurface(const SDLsurface& surf) +{ +#define cpySurf surf.hSurface + + ref = 1; + hTexture = new SDLtexture(this); + hSurface = 0; + + Create(cpySurf->w, cpySurf->h, cpySurf->format->BitsPerPixel); + + if (!hSurface->w || !hSurface->h) + return; // no surface to copy + + // now we are copying the surface + Uint32 saved_flags; + Uint8 saved_alpha; + + saved_flags = cpySurf->flags & (SDL_SRCALPHA | SDL_RLEACCELOK); + saved_alpha = cpySurf->format->alpha; + + if ((saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA) + SDL_SetAlpha(cpySurf, 0, 0); + + /* Copy into the new surface */ + surf.hTexture->Sync(); + SDL_BlitSurface(cpySurf, NULL, hSurface, NULL); + + /* Restore the alpha blending attributes & set same attributes to new surface */ + if ((saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) + { + SDL_SetAlpha(cpySurf, saved_flags, saved_alpha); + SDL_SetAlpha(hSurface, saved_flags, saved_alpha); + } + +#undef cpySurf +} + +SDLsurface::SDLsurface(SDL_Surface* surf) +{ + ref = 1; + hTexture = new SDLtexture(this); + hSurface = surf; + hTexture->ToLoad(); +} + +SDLsurface::SDLsurface(int Width, int Height) +{ + ref = 1; + hTexture = new SDLtexture(this); + hSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, Width, Height, 32, + 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF); + + if (!hSurface) + SDLcore::RaiseError(SDL_GetError()); + + hTexture->ToLoad(); +} + +SDLsurface::~SDLsurface() +{ + if (hSurface) + SDL_FreeSurface(hSurface); + + if (hTexture) + delete hTexture; +} + +void SDLsurface::Create(int Width, int Height, int Depth) +{ + SDL_Surface *pSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, Width, Height, Depth, + 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF); + + if (!pSurface) + SDLcore::RaiseError(SDL_GetError()); + else + { + if (hSurface) + SDL_FreeSurface(hSurface); + + hSurface = pSurface; + } + hTexture->ToLoad(); +} +/* +void SDLsurface::LoadFromMem(char *addr, long len) +{ + SDL_Surface *surface = IMG_Load_RW(SDL_RWFromMem(addr, len), true); + + if (!surface) + SDLcore::RaiseError(SDL_GetError()); + + if (hSurface) + SDL_FreeSurface(hSurface); + + hSurface = surface; + hTexture->ToLoad(); + +#ifdef DEBUGSURFACE + if (hSurface->flags & SDL_SRCALPHA) + std::cout << "SDLsurface::LoadFromMem : alpha layer detected" << std::endl; +#endif +} +*/ +int SDLsurface::GetWidth() +{ + if (hSurface) + return (hSurface->w); + else + return (0); +} + +int SDLsurface::GetHeight() +{ + if (hSurface) + return (hSurface->h); + else + return (0); +} + +int SDLsurface::GetDepth() +{ + if (hSurface) + return (hSurface->format->BitsPerPixel); + else + return (0); +} + +void* SDLsurface::GetData() +{ + if (hSurface) + return (hSurface->pixels); + else + return (0); +} + +void SDLsurface::SetAlphaBuffer(bool choice) +{ + if (!hSurface) + return; + + Uint32 myflags = 0; + + if (choice) + myflags = SDL_SRCALPHA; + + if ((SDL_SetAlpha(hSurface, myflags, SDL_ALPHA_OPAQUE))<0) + SDLcore::RaiseError(SDL_GetError()); +} + +void SDLsurface::ConvertDepth(int depth) +{ + if (!hSurface) + return; + + if (hSurface->format->BitsPerPixel == depth) + return; + +#ifdef DEBUGSURFACE + std::cout << "SDLsurface::ConvertDepth : converting from " << + int(hSurface->format->BitsPerPixel) << " to " << depth << std::endl; +#endif + + SDL_Surface *tmpSurf = SDL_CreateRGBSurface(hSurface->flags, 1, 1, depth, + 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF); + + + if (!tmpSurf) + { + SDLcore::RaiseError(SDL_GetError()); + return; + } + + SDL_Surface *surface = SDL_ConvertSurface(hSurface, tmpSurf->format, tmpSurf->flags); + + if (!surface) + { + SDLcore::RaiseError(SDL_GetError()); + return; + } + + SDL_FreeSurface(tmpSurf); + SDL_FreeSurface(hSurface); + + hSurface = surface; + hTexture->ToLoad(); +} + +void SDLsurface::Fill(Uint32 color) +{ + if (!hSurface) + return; + + SDL_FillRect(hSurface, NULL, color); + hTexture->ToLoad(); +} + +void SDLsurface::Resize(int width, int height) +{ + if (!hSurface) + return; + + Uint32 saved_flags = hSurface->flags & (SDL_SRCALPHA | SDL_RLEACCELOK);; + Uint8 saved_alpha = hSurface->format->alpha; + + if ((saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA) + SDL_SetAlpha(hSurface, 0, 0); + + SDL_Surface *tmpSurf = SDL_CreateRGBSurface (SDL_SWSURFACE, width, height, + hSurface->format->BitsPerPixel, 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF); + + if (!tmpSurf) + { + SDLcore::RaiseError(SDL_GetError()); + return; + } + + /* Copy the surface into the GL texture image */ + SDL_BlitSurface(hSurface, NULL, tmpSurf, NULL); + + /* Restore the alpha blending attributes */ + if ((saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) + SDL_SetAlpha(tmpSurf, saved_flags, saved_alpha); + + SDL_FreeSurface(hSurface); + hSurface = tmpSurf; + hTexture->ToLoad(); +} diff --git a/gb.sdl/src/SDLsurface.h b/gb.sdl/src/SDLsurface.h new file mode 100644 index 00000000..d03fa8c6 --- /dev/null +++ b/gb.sdl/src/SDLsurface.h @@ -0,0 +1,74 @@ +/*************************************************************************** + + SDLsurface.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLSURFACE_H +#define __SDLSURFACE_H + +#include "SDL_h.h" + +class SDLtexture; + +class SDLsurface +{ +public: + SDLsurface(); + SDLsurface(const SDLsurface& surf); + SDLsurface(SDL_Surface* surf); /* SDLsurface will free surf automaticly */ + SDLsurface(int Width, int Height); + ~SDLsurface(); + + void Ref() { ref++; } + void Unref() { ref--; if (ref <= 0) delete this; } + + void Create(int Width, int Height, int Depth = 0); +// void LoadFromMem(char* addr, long len); + + int GetWidth(void ); + int GetHeight(void ); + int GetDepth(void ); + void* GetData(void ); + + SDL_Surface* GetSdlSurface(void ) { return hSurface; } + SDLtexture* GetTexture(void ) { return hTexture; }; + + void SetAlphaBuffer(bool ); + void ConvertDepth(int ); + void Fill(Uint32 color = 0); + void Resize(int width, int height); + + bool IsNull(void ) { return (hSurface ? true: false); }; + + // Compatibility // + SDLsurface(char *data, int width, int height); + int width(void) { return GetWidth(); } + int height(void) { return GetHeight(); } + unsigned char *data(void) { return (unsigned char *)GetData(); } + +private: + int ref; + SDLtexture *hTexture; + SDL_Surface *hSurface; +}; + +#endif /* __SDLSURFACE_H */ + diff --git a/gb.sdl/src/SDLtexture.cpp b/gb.sdl/src/SDLtexture.cpp new file mode 100644 index 00000000..4a3bdb89 --- /dev/null +++ b/gb.sdl/src/SDLtexture.cpp @@ -0,0 +1,177 @@ +/*************************************************************************** + + SDLtexture.cpp + + (c) 2008 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* manage textures and context switching if needed */ + +#include "SDLsurface.h" +#include "SDLtexture.h" +#include "SDLcore.h" + +#include +#include +#include + +SDLtexture::SDLtexture(SDLsurface *surface) +{ + hSurface = surface; + hTexinfo = new texinfo; + hTexinfo->Index = 0; + hRenderBuffer = 0; +} + +SDLtexture::~SDLtexture() +{ + if (hTexinfo->Index) + glDeleteTextures(1, &(hTexinfo->Index)); + + if (hRenderBuffer) + delete hRenderBuffer; + + delete hTexinfo; +} + +void SDLtexture::init() +{ + FBOrender::Check(); +} + +void SDLtexture::Select() +{ + if (!FBOrender::Check()) + SDLcore::RaiseError("Unable to draw on the texture, FBO not supported!"); + + /* Make sure our texture is properly loaded/synced */ + this->GetAsTexture(NULL); + + if (!hRenderBuffer) + hRenderBuffer = new FBOrender(); + + hRenderBuffer->Bind(hTexinfo->Index); + +/* glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glViewport(0, 0, hSurface->GetWidth(), hSurface->GetHeight());*/ +} + +void SDLtexture::Unselect() +{ + FBOrender::Unbind(); +} + +void SDLtexture::Sync() +{ +} + +void SDLtexture::GetAsTexture(texinfo *tex) +{ + if (!hTexinfo->Index) + { + glGenTextures(1, &(hTexinfo->Index)); + hTexinfo->State = TEXTURE_TO_UPLOAD; + } + + if (hTexinfo->State & TEXTURE_TO_UPLOAD) + { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, hTexinfo->Index); + + SDL_Surface *image; + + if (!GLEW_ARB_texture_non_power_of_two) + { + SDL_Surface *origin = hSurface->GetSdlSurface(); + + /* Use the surface width and height expanded to powers of 2 */ + int w = this->GetPowerOfTwo(origin->w); + int h = this->GetPowerOfTwo(origin->h); + hTexinfo->Width = GLdouble(origin->w) / w; /* Max X */ + hTexinfo->Height = GLdouble(origin->h) / h; /* Max Y */ + + image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, +// #if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */ +// 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 +// #else +// 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF +// #endif + 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF + ); + + if (!image) + { + std::cerr << __FILE__ << ":" << __LINE__ << ": Failed to create SDL_Surface() !" << std::endl; + return; + } + + /* Save the alpha blending attributes */ + Uint32 saved_flags = origin->flags & (SDL_SRCALPHA | SDL_RLEACCELOK); + Uint8 saved_alpha = origin->format->alpha; + + if ((saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA) + SDL_SetAlpha(origin, 0, 0); + + /* Copy the surface into the GL texture image */ + SDL_BlitSurface(origin, NULL, image, NULL); + + /* Restore the alpha blending attributes */ + if ((saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) + SDL_SetAlpha(origin, saved_flags, saved_alpha); + } + else + { + hTexinfo->Width = 1.0; + hTexinfo->Height = 1.0; + image = hSurface->GetSdlSurface(); + } + +// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->w, image->h, 0, GL_RGBA, + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->w, image->h, 0, GL_BGRA, + GL_UNSIGNED_BYTE, image->pixels); + /* Use of CLAMP_TO_EDGE, without it give a black line around the texture + with DRI radeon drivers */ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); +// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + if (!GLEW_ARB_texture_non_power_of_two) + SDL_FreeSurface(image); /* No longer needed */ + + hTexinfo->State = TEXTURE_OK; + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + } + + if (tex) + std::memcpy(tex, hTexinfo, sizeof(texinfo)); +} + +int SDLtexture::GetPowerOfTwo(int size) +{ + int value = 1; + + while ( value < size ) + value <<= 1; + + return value; +} + diff --git a/gb.sdl/src/SDLtexture.h b/gb.sdl/src/SDLtexture.h new file mode 100644 index 00000000..ca939ec9 --- /dev/null +++ b/gb.sdl/src/SDLtexture.h @@ -0,0 +1,73 @@ +/*************************************************************************** + + SDLtexture.h + + (c) 2008 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* manage transparently window/surfaces as texture */ + +#ifndef __SDLTEXTURE_H +#define __SDLTEXTURE_H + +#include "SDL_h.h" +#include "SDLosrender.h" + +// internal texture status values +#define TEXTURE_OK (0) +#define TEXTURE_TO_UPLOAD (1) +#define TEXTURE_TO_DOWNLOAD (2) + +class SDLwindow; +class SDLsurface; + +typedef struct { + GLuint Index; + GLdouble Width, Height; + GLint RealWidth, RealHeight; + Uint8 State; + } + texinfo; + +class SDLtexture +{ +public: + SDLtexture(SDLsurface* surface); + ~SDLtexture(); + + static void init(void ); + void Select(void ); + static void Unselect(void ); + SDLsurface* GetSurface(void ) { return hSurface; }; + void Sync(void ); + + void ToLoad(void ) { hTexinfo->State = TEXTURE_TO_UPLOAD; }; + + Uint8 GetStatus(void ) { return hTexinfo->State; }; + void GetAsTexture(texinfo *); + + static int GetPowerOfTwo(int size); +private: + SDLsurface *hSurface; + texinfo *hTexinfo; + FBOrender *hRenderBuffer; +}; + +#endif /* __SDLTEXTURE_H */ + diff --git a/gb.sdl/src/SDLwindow.cpp b/gb.sdl/src/SDLwindow.cpp new file mode 100644 index 00000000..70223e2e --- /dev/null +++ b/gb.sdl/src/SDLwindow.cpp @@ -0,0 +1,276 @@ +/*************************************************************************** + + SDLwindow.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "SDLwindow.h" +#include "SDLcore.h" +#include "SDLtexture.h" + +#include + +SDLwindow::SDLwindow(void ) +{ + hSurface = 0; + hCursor = new SDLcursor(); + hX = hY = 0; + hWidth = 640; + hHeight = 480; + hFullScreen = false; + hResizable = true; + hTitle = "Gambas SDL application"; +} + +SDLwindow::~SDLwindow() +{ + this->Close(); + delete hCursor; +} + +void SDLwindow::Show() +{ + Uint32 myFlags = (SDL_ASYNCBLIT | SDL_DOUBLEBUF | SDL_OPENGL); + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); + SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 8 ); + + if (hFullScreen) + myFlags = myFlags | SDL_FULLSCREEN; + + if (hResizable) + myFlags = myFlags | SDL_RESIZABLE; + + hSurface = SDL_SetVideoMode(hWidth, hHeight , 0, myFlags); + + if (!hSurface) + { + SDLcore::RaiseError(SDL_GetError()); + return; + } + + GLenum err = glewInit(); + if (GLEW_OK != err) + { + /* Problem: glewInit failed, something is seriously wrong. */ + SDLcore::RaiseError((char *)glewGetErrorString(err)); + return; + } + + hCtx = glXGetCurrentContext(); + hDrw = glXGetCurrentDrawable(); + hDpy = glXGetCurrentDisplay(); + + /* Set our custom cursor */ + hCursor->Show(SDLapp->CurrentWin()); + + SDL_WM_SetCaption(hTitle.c_str(), hTitle.c_str()); + + if (SDLcore::GetWindow() != this) + SDLcore::RegisterWindow(this); + +// GL::init(); + SDLtexture::init(); + + Clear(); + Open(); + +} + +void SDLwindow::Close() +{ + if (!hSurface) + return; + + GrabInput(false); + SDLcore::RegisterWindow(NULL); + hSurface = NULL; +} + +void SDLwindow::Refresh() +{ + if (!hSurface) + return; + + SDL_GL_SwapBuffers(); +} + +void SDLwindow::Select(void ) +{ + if (!hSurface) + return; + + if ((glXGetCurrentContext()!=hCtx) && (glXGetCurrentDrawable()!=hDrw)) + { + std::cout << "Set window current with glXMakeCurrent()" << std::endl; + glXMakeCurrent(hDpy, hDrw, hCtx); + } + else + { + SDLtexture::Unselect(); +/* this->Open();*/ + } +} + +void SDLwindow::Clear(Uint32 color) +{ + if (!hSurface) + return; + + glClearColor((GLfloat((color >> 16) & 0xFF)/255), (GLfloat((color >> 8) & 0xFF)/255), + (GLfloat((color & 0xFF)/255)), (GLfloat(~(color >> 24) & 0xFF)/255)); + glClear(GL_COLOR_BUFFER_BIT); + +} + +Uint32 SDLwindow::Id(void ) +{ + if (!hSurface) + return 0; + + return (SDLapp->CurrentWin()); +} + +int SDLwindow::GetWidth() +{ + if (hSurface) + return (hSurface->w); + else + return (hWidth); +} + +int SDLwindow::GetHeight() +{ + if (hSurface) + return (hSurface->h); + else + return (hHeight); +} + +int SDLwindow::GetDepth() +{ + if (hSurface) + return (hSurface->format->BitsPerPixel); + else + return (0); +} + +void* SDLwindow::GetData() +{ + if (hSurface) + return (hSurface->pixels); + else + return (0); +} + +void SDLwindow::SetCursorShape(int shape) +{ + if (!hCursor) + return; + + hCursor->SetShape(shape); + + if (this->IsShown()) + hCursor->Show(SDLapp->CurrentWin()); +} + +void SDLwindow::SetWidth(int width) +{ + hWidth = width; + + if (hSurface) + this->Show(); +} + +void SDLwindow::SetHeight(int height) +{ + hHeight = height; + + if (hSurface) + this->Show(); +} + +void SDLwindow::SetFullScreen(bool choice) +{ + if ((choice && !hFullScreen) || (!choice && hFullScreen)) + { + if (hSurface) + { + if (!SDL_WM_ToggleFullScreen(hSurface)) + SDLcore::RaiseError(SDL_GetError()); + } + hFullScreen = !hFullScreen; + } +} + +void SDLwindow::SetResizable(bool choice) +{ + if (!hSurface) + { + hResizable = choice; + return; + } + + if (((hSurface->flags & SDL_RESIZABLE) && !choice) || (!(hSurface->flags & SDL_RESIZABLE) && choice)) + { + hResizable = choice; + this->Show(); + } +} + +void SDLwindow::SetTitle(char *title) +{ + hTitle = title; + + if (hSurface) + SDL_WM_SetCaption(title, title); +} +/* +void SDLwindow::SetCursor(SDLcursor *cursor) +{ + if (hCursor) + delete hCursor; + + SDLcursor *curs=new SDLcursor(*cursor); + hCursor = curs; + + if (this->IsShown()) + hCursor->Show(); +} +*/ +bool SDLwindow::IsShown() +{ + if (!hSurface) + return (false); + + if (SDLcore::GetWindow() == this) + return (true); + else + return (false); +} + +void SDLwindow::GrabInput(bool grab) +{ + SDL_WM_GrabInput(grab ? SDL_GRAB_ON : SDL_GRAB_OFF); +} + +bool SDLwindow::IsInputGrabbed(void) const +{ + return SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON; +} diff --git a/gb.sdl/src/SDLwindow.h b/gb.sdl/src/SDLwindow.h new file mode 100644 index 00000000..0c77831f --- /dev/null +++ b/gb.sdl/src/SDLwindow.h @@ -0,0 +1,105 @@ +/*************************************************************************** + + SDLwindow.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SDLWINDOW_H +#define __SDLWINDOW_H + +#include "SDL_h.h" +#include "SDLcursor.h" + +#include + +class SDLwindow +{ +public: + SDLwindow(void ); + virtual ~SDLwindow(); + + void Show(void ); + void Close(void ); + void Refresh(); + void Select(void ); + void Clear(Uint32 color = 0); + Uint32 Id(void ); + + int GetWidth(void ); + int GetHeight(void ); + int GetDepth(void ); + void* GetData(void ); + + void Lock(void) { if (hSurface) SDL_LockSurface(hSurface); } + void Unlock(void) { if (hSurface) SDL_UnlockSurface(hSurface); } + + const char* GetTitle(void ) { return (hTitle.c_str()); }; + SDL_Surface* GetSdlSurface(void ) { return (hSurface); }; + SDLcursor* GetCursor(void ) { return (hCursor); } + int GetCursorShape(void ) { return (hCursor->GetShape()); } + + void SetX(int ); + void SetY(int ); + void SetWidth(int ); + void SetHeight(int ); + void SetFullScreen(bool ); + void SetResizable(bool ); + void SetTitle(char* ); + + void SetCursor(SDLcursor* ); + void SetCursorShape(int ); + + bool IsFullScreen(void ) {return (hFullScreen); }; + bool IsResizable(void ) { return (hResizable); }; + bool IsShown(void ); + + void GrabInput(bool grab); + bool IsInputGrabbed(void) const; + + // events :) + virtual void Quit(void ) = 0; + virtual void Resize(void ) = 0; + virtual void GotFocus(void ) = 0; + virtual void LostFocus(void ) = 0; + virtual void MouseEnter(void ) = 0; + virtual void MouseLeave(void ) = 0; + virtual void Update(void ) = 0; + virtual void JoyEvent(SDL_Event& ) = 0; + virtual void KeyEvent(SDL_KeyboardEvent* , int ) = 0; + virtual void MouseButtonEvent(SDL_MouseButtonEvent* ) = 0; + virtual void MouseMotionEvent(SDL_MouseMotionEvent* ) = 0; + virtual void Open(void ) =0; + +private: + SDL_Surface *hSurface; + SDLcursor *hCursor; + int hX, hY; + int hWidth, hHeight; + bool hFullScreen; + bool hResizable; + std::string hTitle; + // context and drawable (for GlxMakeCurrent) + GLXContext hCtx; + GLXDrawable hDrw; + Display *hDpy; +}; + +#endif /* __SDLWINDOW_H */ + diff --git a/gb.sdl/src/default_font.h b/gb.sdl/src/default_font.h new file mode 100644 index 00000000..daaa86af --- /dev/null +++ b/gb.sdl/src/default_font.h @@ -0,0 +1,632 @@ +#define DEFAULT_FONT_WIDTH 7 +#define DEFAULT_FONT_HEIGHT 13 +#define DEFAULT_FONT_ASCENT 10 +#define DEFAULT_FONT_DESCENT 3 + +const char _default_font_33_126[] = { + 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x14, 0x3E, 0x14, 0x14, 0x3E, 0x14, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x3C, 0x0A, 0x0A, 0x1C, 0x28, 0x28, 0x1E, 0x08, 0x08, 0x00, + 0x00, 0x02, 0x15, 0x12, 0x08, 0x08, 0x04, 0x04, 0x12, 0x2A, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x12, 0x12, 0x0C, 0x04, 0x2A, 0x11, 0x11, 0x2E, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x10, 0x00, + 0x00, 0x04, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x32, 0x2A, 0x26, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x0C, 0x0A, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x18, 0x14, 0x12, 0x11, 0x3F, 0x10, 0x10, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x1E, 0x20, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x04, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x10, 0x0C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x08, 0x04, + 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x20, 0x20, 0x10, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x39, 0x25, 0x25, 0x25, 0x19, 0x02, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x22, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x2A, 0x12, 0x2C, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x14, 0x14, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x14, 0x14, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x1C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1C, 0x00, + 0x00, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x1C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1C, 0x00, + 0x00, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x10, 0x10, 0x00, 0x18, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0C, + 0x00, 0x00, 0x02, 0x02, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x18, 0x04, 0x04, 0x04, 0x04, 0x02, 0x04, 0x04, 0x04, 0x04, 0x18, 0x00, + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, + 0x00, 0x0C, 0x10, 0x10, 0x10, 0x10, 0x20, 0x10, 0x10, 0x10, 0x10, 0x0C, 0x00, + 0x00, 0x24, 0x2A, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const char _default_font_160_687[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x3C, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x3C, 0x08, 0x08, 0x00, + 0x00, 0x00, 0x38, 0x04, 0x04, 0x1E, 0x04, 0x04, 0x04, 0x04, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x22, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x3E, 0x08, 0x3E, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x02, 0x04, 0x0A, 0x12, 0x14, 0x08, 0x10, 0x0E, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x59, 0x45, 0x45, 0x45, 0x59, 0x22, 0x1C, 0x00, 0x00, + 0x0C, 0x10, 0x1C, 0x12, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x12, 0x09, 0x12, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x4D, 0x55, 0x4D, 0x55, 0x55, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x12, 0x12, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0x0E, 0x10, 0x0C, 0x02, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0E, 0x10, 0x0C, 0x10, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x3C, 0x2E, 0x2E, 0x2E, 0x2C, 0x28, 0x28, 0x28, 0x28, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x06, + 0x04, 0x06, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x12, 0x12, 0x12, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x12, 0x24, 0x12, 0x09, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x03, 0x02, 0x02, 0x02, 0x30, 0x0C, 0x03, 0x10, 0x18, 0x14, 0x3C, 0x10, + 0x02, 0x03, 0x02, 0x02, 0x02, 0x30, 0x0C, 0x03, 0x1C, 0x20, 0x18, 0x04, 0x3C, + 0x07, 0x08, 0x06, 0x08, 0x07, 0x30, 0x0C, 0x03, 0x10, 0x18, 0x14, 0x3C, 0x10, + 0x00, 0x00, 0x08, 0x08, 0x00, 0x08, 0x08, 0x04, 0x02, 0x02, 0x1C, 0x00, 0x00, + 0x04, 0x08, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x08, 0x04, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x08, 0x14, 0x08, 0x00, 0x1C, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x09, 0x09, 0x09, 0x3F, 0x09, 0x09, 0x09, 0x39, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x08, 0x06, + 0x04, 0x08, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x10, 0x08, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x3E, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x14, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x04, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x14, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x27, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x32, 0x32, 0x22, 0x00, 0x00, + 0x04, 0x08, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x1C, 0x32, 0x32, 0x2A, 0x2A, 0x2A, 0x26, 0x26, 0x1C, 0x02, 0x00, + 0x04, 0x08, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x12, 0x12, 0x1A, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x08, 0x14, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x28, 0x28, 0x1E, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x08, 0x06, + 0x00, 0x04, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x04, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x0C, 0x30, 0x18, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x3E, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x1C, 0x32, 0x2A, 0x2A, 0x2A, 0x26, 0x1C, 0x02, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x10, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x10, 0x60, + 0x10, 0x08, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x14, 0x08, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x27, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x70, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x08, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x10, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x10, 0x60, + 0x14, 0x08, 0x00, 0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x22, 0x1C, 0x00, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x06, + 0x00, 0x00, 0x30, 0x08, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x7F, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x0F, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x1C, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x08, 0x30, + 0x00, 0x08, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, + 0x08, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x37, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1F, 0x00, 0x00, + 0x00, 0x24, 0x24, 0x00, 0x36, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x20, 0x18, + 0x10, 0x28, 0x00, 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0E, 0x00, 0x00, + 0x00, 0x10, 0x28, 0x00, 0x18, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0C, + 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x08, 0x06, + 0x00, 0x00, 0x02, 0x02, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x10, 0x08, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x08, 0x06, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x0C, + 0x14, 0x08, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x1A, 0x1A, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x04, 0x04, 0x34, 0x34, 0x04, 0x04, 0x04, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x06, 0x03, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x18, 0x0C, 0x08, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x10, 0x08, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x08, 0x06, + 0x14, 0x08, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x02, 0x02, 0x01, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x20, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x10, + 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x12, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x24, 0x12, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x09, 0x09, 0x09, 0x39, 0x09, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x29, 0x29, 0x19, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x10, 0x08, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x08, 0x06, + 0x14, 0x08, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x10, 0x08, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x08, 0x06, + 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x0C, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x10, 0x0C, + 0x14, 0x08, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x0E, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x08, 0x14, 0x08, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x08, 0x14, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x24, 0x12, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x24, 0x12, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x10, 0x60, + 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x14, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x10, 0x08, 0x3E, 0x20, 0x10, 0x10, 0x08, 0x04, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x08, 0x00, 0x3E, 0x20, 0x10, 0x10, 0x08, 0x04, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x07, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x25, 0x24, 0x24, 0x1C, 0x24, 0x24, 0x24, 0x1C, 0x00, 0x00, + 0x00, 0x1E, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x03, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x60, 0x10, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x10, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x27, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x00, 0x0E, 0x15, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x14, 0x0C, 0x00, 0x00, + 0x00, 0x3C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x14, 0x08, 0x10, 0x20, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x20, 0x20, 0x3E, 0x20, 0x20, 0x20, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x3E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, + 0x00, 0x00, 0x38, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, + 0x00, 0x40, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x06, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x60, 0x10, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x02, 0x62, 0x12, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x04, 0x1E, 0x08, 0x08, 0x14, 0x14, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x34, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x04, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x20, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x40, 0x5C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x5C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x26, 0x39, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x26, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x26, 0x39, 0x29, 0x29, 0x29, 0x29, 0x26, 0x20, 0x20, + 0x03, 0x04, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x04, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x20, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x1C, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x1C, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x12, 0x12, 0x1C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x10, 0x08, + 0x00, 0x00, 0x3E, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, 0x00, + 0x00, 0x40, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x36, 0x14, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x40, 0x22, 0x22, 0x22, 0x14, 0x14, 0x14, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x40, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x3E, 0x20, 0x10, 0x10, 0x3E, 0x04, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x3E, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x1C, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x1C, 0x02, 0x02, 0x02, 0x3C, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x20, 0x18, 0x20, 0x20, 0x1E, 0x01, 0x06, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x10, 0x3E, 0x04, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0x02, 0x02, 0x1E, 0x20, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x02, 0x02, 0x1E, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x1E, 0x02, 0x04, 0x18, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x12, 0x0E, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x3E, 0x08, 0x3E, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x28, 0x10, 0x3B, 0x25, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x0D, 0x3B, 0x00, 0x00, + 0x00, 0x28, 0x13, 0x05, 0x3D, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3B, 0x00, 0x00, + 0x00, 0x28, 0x14, 0x04, 0x3E, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x31, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x1F, 0x00, 0x00, + 0x00, 0x20, 0x21, 0x01, 0x31, 0x21, 0x21, 0x21, 0x21, 0x21, 0x2F, 0x20, 0x18, + 0x00, 0x20, 0x26, 0x04, 0x34, 0x24, 0x24, 0x24, 0x24, 0x24, 0x2E, 0x20, 0x18, + 0x00, 0x00, 0x29, 0x29, 0x2B, 0x2B, 0x2D, 0x2D, 0x29, 0x29, 0x19, 0x00, 0x00, + 0x00, 0x20, 0x29, 0x09, 0x2B, 0x2B, 0x2D, 0x2D, 0x29, 0x29, 0x29, 0x20, 0x18, + 0x00, 0x20, 0x20, 0x00, 0x37, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x20, 0x18, + 0x14, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x1C, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x04, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x04, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x3E, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x1C, 0x00, 0x14, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x1C, 0x00, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x3E, 0x09, 0x09, 0x3F, 0x09, 0x09, 0x09, 0x09, 0x39, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x1E, 0x28, 0x28, 0x1E, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x72, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x72, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x14, 0x08, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x14, 0x08, 0x02, 0x02, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x00, 0x00, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x14, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x18, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x1C, 0x20, 0x20, 0x20, 0x1E, + 0x00, 0x14, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x06, + 0x00, 0x00, 0x3B, 0x25, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x0D, 0x3B, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x05, 0x3D, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3B, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3E, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3E, 0x00, 0x00, + 0x10, 0x08, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x0A, 0x0A, 0x0A, 0x2A, 0x2E, 0x2A, 0x2A, 0x2A, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x02, 0x02, + 0x04, 0x08, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x04, 0x2A, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x04, 0x2A, 0x14, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x3E, 0x09, 0x09, 0x3F, 0x09, 0x09, 0x09, 0x39, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1E, 0x28, 0x28, 0x3E, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x08, 0x24, 0x1C, 0x32, 0x32, 0x2A, 0x2A, 0x2A, 0x26, 0x26, 0x1C, 0x02, 0x00, + 0x00, 0x10, 0x08, 0x20, 0x1C, 0x32, 0x2A, 0x2A, 0x2A, 0x26, 0x1C, 0x02, 0x00, + 0x12, 0x24, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x3E, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x3E, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x08, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x08, 0x04, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x04, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x08, 0x04, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x18, 0x20, 0x20, 0x20, 0x18, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x18, 0x20, 0x20, 0x20, 0x18, 0x06, + 0x14, 0x08, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x14, 0x08, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x20, + 0x00, 0x00, 0x04, 0x04, 0x06, 0x05, 0x05, 0x05, 0x15, 0x2D, 0x1E, 0x08, 0x08, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x10, 0x10, 0x08, 0x04, 0x04, 0x02, 0x3E, 0x20, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x20, 0x10, + 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x08, 0x06, + 0x1C, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x2C, 0x1A, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x2C, 0x1A, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x14, 0x2C, 0x18, 0x08, 0x08, + 0x00, 0x00, 0x03, 0x05, 0x05, 0x05, 0x05, 0x05, 0x15, 0x2D, 0x1D, 0x08, 0x08, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x12, 0x2A, 0x1C, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x06, + 0x00, 0x00, 0x08, 0x08, 0x1C, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x08, + 0x00, 0x10, 0x1C, 0x32, 0x32, 0x2A, 0x3E, 0x2A, 0x2A, 0x26, 0x26, 0x04, 0x00, + 0x00, 0x10, 0x3C, 0x12, 0x12, 0x0A, 0x0A, 0x0A, 0x0A, 0x06, 0x3C, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x3C, 0x12, 0x0A, 0x0A, 0x0A, 0x06, 0x3C, 0x04, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x07, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x28, 0x18, 0x08, 0x0C, 0x0A, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x04, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x04, 0x18, + 0x00, 0x00, 0x1C, 0x20, 0x20, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x10, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x22, 0x27, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x7F, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x14, 0x14, 0x14, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x10, 0x3E, 0x12, 0x12, 0x0A, 0x1E, 0x0A, 0x0A, 0x06, 0x3E, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x1C, 0x32, 0x2A, 0x3E, 0x0A, 0x06, 0x3C, 0x04, 0x00, + 0x00, 0x00, 0x38, 0x10, 0x10, 0x38, 0x10, 0x10, 0x10, 0x10, 0x0E, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x06, + 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x40, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1F, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x0F, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x3E, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x7F, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2C, 0x32, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1A, 0x26, 0x22, 0x22, 0x22, 0x26, 0x1A, 0x00, 0x00, + 0x0C, 0x02, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x12, 0x2A, 0x1C, 0x08, 0x08, + 0x00, 0x00, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x40, + 0x40, 0x20, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x3E, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2E, 0x50, 0x18, 0x24, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2E, 0x50, 0x10, 0x0C, 0x10, 0x10, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x1A, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x10, 0x10, 0x38, 0x10, 0x10, 0x10, 0x10, 0x0C, + 0x00, 0x00, 0x00, 0x40, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x08, 0x14, 0x14, 0x14, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, + 0x0C, 0x02, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x0C, 0x02, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x18, + 0x00, 0x08, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x2C, 0x2A, 0x1A, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x0C, 0x0A, 0x3C, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, + 0x00, 0x00, 0x03, 0x02, 0x3E, 0x22, 0x12, 0x0A, 0x12, 0x22, 0x22, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x3C, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x20, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x02, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x32, 0x32, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x09, 0x09, 0x39, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x2A, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x08, 0x1C, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x2E, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x2E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x2E, 0x20, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0C, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x0A, 0x1E, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x02, 0x0C, + 0x00, 0x00, 0x30, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x20, 0x18, 0x10, 0x10, 0x3C, 0x10, 0x10, 0x10, 0x10, 0x0C, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, + 0x00, 0x00, 0x30, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3C, 0x0A, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1E, 0x10, 0x10, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x7F, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x14, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x14, 0x14, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x2A, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x10, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x34, 0x4A, 0x3E, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x1C, 0x20, 0x20, 0x20, 0x1E, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x1C, 0x20, 0x3C, 0x22, 0x5C, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x10, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x2A, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x2C, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x08, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x10, 0x10, 0x00, 0x18, 0x10, 0x10, 0x10, 0x10, 0x10, 0x3C, 0x12, 0x0C, + 0x00, 0x00, 0x22, 0x24, 0x28, 0x30, 0x28, 0x24, 0x22, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x40, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x10, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x04, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3E, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3E, 0x25, 0x25, 0x15, 0x25, 0x25, 0x26, 0x20, 0x18, + 0x00, 0x00, 0x04, 0x04, 0x3E, 0x25, 0x15, 0x15, 0x35, 0x5D, 0x3E, 0x10, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x37, 0x09, 0x09, 0x11, 0x21, 0x21, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x21, 0x11, 0x17, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1E, 0x10, 0x08, + 0x00, 0x00, 0x01, 0x01, 0x37, 0x09, 0x09, 0x09, 0x19, 0x29, 0x1E, 0x08, 0x08, + 0x00, 0x00, 0x06, 0x01, 0x1D, 0x27, 0x25, 0x25, 0x25, 0x25, 0x25, 0x20, 0x10, + 0x00, 0x00, 0x03, 0x02, 0x32, 0x0A, 0x0A, 0x12, 0x22, 0x22, 0x1F, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x02, 0x3A, 0x22, 0x12, 0x12, 0x12, 0x0A, 0x3C, 0x00, 0x00, + 0x22, 0x22, 0x2A, 0x36, 0x22, 0x00, 0x22, 0x22, 0x2A, 0x36, 0x22, 0x00, 0x00, + 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x01, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x01, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x40, +}; diff --git a/gb.sdl/src/gb.sdl.component b/gb.sdl/src/gb.sdl.component new file mode 100644 index 00000000..5de72c24 --- /dev/null +++ b/gb.sdl/src/gb.sdl.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.sdl +Author=Laurent Carlier +Implements=EventLoop,OpenGLViewer +Requires=gb.image,gb.image.io +State=Deprecated diff --git a/gb.sdl/src/main.cpp b/gb.sdl/src/main.cpp new file mode 100644 index 00000000..c8abfbd8 --- /dev/null +++ b/gb.sdl/src/main.cpp @@ -0,0 +1,155 @@ +/*************************************************************************** + + main.cpp + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include "gambas.h" +#include "main.h" + +#include +#include "SDL_h.h" +#include "SDL_syswm.h" + +#include "SDLapp.h" +#include "SDLcore.h" + +#include "Cconst.h" +#include "Cdesktop.h" +#include "Cjoystick.h" +#include "Ckey.h" +#include "Cmouse.h" +#include "Cimage.h" +#include "Cdraw.h" +#include "Cwindow.h" +#include "Cfont.h" + +GB_CLASS CLASS_Window; +GB_CLASS CLASS_Image; +GB_CLASS CLASS_Font; + +class mySDLapp : public SDLapplication +{ +public: + mySDLapp(int &argc, char **argv):SDLapplication(argc, argv) {}; + virtual ~mySDLapp() {}; + + virtual void ManageError(const char *myError); +}; + +mySDLapp *myApp = NULL; + +static void my_main(int *argc, char **argv); +static int my_loop(void ); +static void my_wait(int duration); + +extern "C" +{ + GB_INTERFACE GB EXPORT; + IMAGE_INTERFACE IMAGE EXPORT; + + GB_DESC *GB_CLASSES[] EXPORT = + { + CLine, CFill, + CFont, + CDesktop, + CJoyInfos, CQueryJoys, CJoystick, + CKey, + CMouse, // CCursor, + CImage, + CDraw, + CWindow, + + NULL + }; + + int EXPORT GB_INIT(void) + { + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + IMAGE.SetDefaultFormat(GB_IMAGE_BGRA); + + GB.Hook(GB_HOOK_MAIN, (void *)my_main); + GB.Hook(GB_HOOK_LOOP, (void *)my_loop); + GB.Hook(GB_HOOK_WAIT, (void *)my_wait); + + CLASS_Window = GB.FindClass("Window"); + CLASS_Image = GB.FindClass("Image"); + CLASS_Font = GB.FindClass("Font"); + + return -1; + } + + void EXPORT GB_EXIT() + { + delete myApp; + } + + void EXPORT GB_SIGNAL(int signal, void *param) + { + static bool wasFullscreen = false; + + if (!SDLcore::GetWindow()) + return; + + if ((signal == GB_SIGNAL_DEBUG_BREAK) || (signal == GB_SIGNAL_DEBUG_CONTINUE)) + { + if (SDLcore::GetWindow()->IsFullScreen()) + { + wasFullscreen = true; + SDLcore::GetWindow()->SetFullScreen(false); + } + } + + if (signal == GB_SIGNAL_DEBUG_CONTINUE) + { + if (wasFullscreen) + SDLcore::GetWindow()->SetFullScreen(true); + } + } +} + +void mySDLapp::ManageError(const char *myError) +{ + GB.Error(COMP_ERR myError); +} + +static void my_main(int *argc, char **argv) +{ + myApp = new mySDLapp(*argc, argv); +} + +static int my_loop() +{ + while(myApp->HaveWindows()) + { + myApp->ManageEvents(); + GB.Loop(10); + } + + return 1; +} + +static void my_wait(int duration) +{ + myApp->ManageEvents(duration == 0); + GB.Loop(10); +} diff --git a/gb.sdl/src/main.h b/gb.sdl/src/main.h new file mode 100644 index 00000000..4552f2b6 --- /dev/null +++ b/gb.sdl/src/main.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + main.h + + (c) 2006 Laurent Carlier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "gb.image.h" + +#ifndef __MAIN_CPP +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; + +extern GB_CLASS CLASS_Window; +extern GB_CLASS CLASS_Image; +extern GB_CLASS CLASS_Font; +#endif + + +#endif /* __MAIN_H */ + diff --git a/gb.sdl2/AUTHORS b/gb.sdl2/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.sdl2/COPYING b/gb.sdl2/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.sdl2/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.sdl2/ChangeLog b/gb.sdl2/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/gb.sdl2/INSTALL b/gb.sdl2/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.sdl2/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.sdl2/Makefile.am b/gb.sdl2/Makefile.am new file mode 100644 index 00000000..f81e7575 --- /dev/null +++ b/gb.sdl2/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @SDL2_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h diff --git a/gb.sdl2/NEWS b/gb.sdl2/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.sdl2/README b/gb.sdl2/README new file mode 100644 index 00000000..e69de29b diff --git a/gb.sdl2/acinclude.m4 b/gb.sdl2/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.sdl2/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.sdl2/component.am b/gb.sdl2/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.sdl2/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.sdl2/configure.ac b/gb.sdl2/configure.ac new file mode 100644 index 00000000..2406d49f --- /dev/null +++ b/gb.sdl2/configure.ac @@ -0,0 +1,26 @@ +dnl ---- configure.ac for gb.sdl + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-sdl2, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.sdl2) +AC_PROG_LIBTOOL + +GB_COMPONENT_PKG_CONFIG( + sdl2, SDL2, gb.sdl2, [src], + 'sdl2 >= 2.0.2' 'SDL2_image >= 2.0.0' 'SDL2_ttf >= 2.0.12' +) + +GB_COMPONENT_PKG_CONFIG( + sdl2audio, SDL2AUDIO, gb.sdl2.audio, [audio], + 'sdl2 >= 2.0.2' 'SDL2_mixer >= 2.0.0' +) + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +src/audio/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.sdl2/gambas.h b/gb.sdl2/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.sdl2/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.sdl2/gb.geom.h b/gb.sdl2/gb.geom.h new file mode 120000 index 00000000..abc5659e --- /dev/null +++ b/gb.sdl2/gb.geom.h @@ -0,0 +1 @@ +../main/lib/geom/gb.geom.h \ No newline at end of file diff --git a/gb.sdl2/gb.image.h b/gb.sdl2/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.sdl2/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.sdl2/gb_common.h b/gb.sdl2/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.sdl2/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.sdl2/gb_list.h b/gb.sdl2/gb_list.h new file mode 120000 index 00000000..cfd35489 --- /dev/null +++ b/gb.sdl2/gb_list.h @@ -0,0 +1 @@ +../main/share/gb_list.h \ No newline at end of file diff --git a/gb.sdl2/gb_list_temp.h b/gb.sdl2/gb_list_temp.h new file mode 120000 index 00000000..0bd26c39 --- /dev/null +++ b/gb.sdl2/gb_list_temp.h @@ -0,0 +1 @@ +../main/share/gb_list_temp.h \ No newline at end of file diff --git a/gb.sdl2/m4 b/gb.sdl2/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.sdl2/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.sdl2/reconf b/gb.sdl2/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.sdl2/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.sdl2/src/Makefile.am b/gb.sdl2/src/Makefile.am new file mode 100644 index 00000000..0b44a74c --- /dev/null +++ b/gb.sdl2/src/Makefile.am @@ -0,0 +1,29 @@ +COMPONENT = gb.sdl2 +include $(top_srcdir)/component.am + +SUBDIRS = . @SDL2AUDIO_DIR@ + +gblib_LTLIBRARIES = gb.sdl2.la + +gb_sdl2_la_LIBADD = @SDL2_LIB@ libfont.la +gb_sdl2_la_LDFLAGS = -module @LD_FLAGS@ @SDL2_LDFLAGS@ +gb_sdl2_la_CFLAGS = $(AM_CFLAGS) +gb_sdl2_la_CPPFLAGS = @SDL2_INC@ + +gb_sdl2_la_SOURCES = \ + c_image.h c_image.c \ + c_window.h c_window.c \ + c_draw.h c_draw.c \ + c_mouse.h c_mouse.c \ + c_key.h c_key.c \ + c_font.h c_font.c \ + main.h main.c + +noinst_LTLIBRARIES = libfont.la + +libfont_la_CPPFLAGS = @SDL2_INC@ +libfont_la_CFLAGS = $(AM_CFLAGS_OPT) + +libfont_la_SOURCES = \ + default_font_data.h default_font.h default_font.c + diff --git a/gb.sdl2/src/audio/Makefile.am b/gb.sdl2/src/audio/Makefile.am new file mode 100644 index 00000000..d8b375b9 --- /dev/null +++ b/gb.sdl2/src/audio/Makefile.am @@ -0,0 +1,15 @@ +COMPONENT = gb.sdl2.audio +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.sdl2.audio.la + +gb_sdl2_audio_la_LIBADD = @SDL2AUDIO_LIB@ +gb_sdl2_audio_la_LDFLAGS = -module @LD_FLAGS@ @SDL2AUDIO_LDFLAGS@ +gb_sdl2_audio_la_CFLAGS = $(AM_CFLAGS) +gb_sdl2_audio_la_CPPFLAGS = @SDL2AUDIO_INC@ + +gb_sdl2_audio_la_SOURCES = \ + c_sound.h c_sound.c \ + c_music.h c_music.c \ + c_channel.h c_channel.c \ + main.h main.c diff --git a/gb.sdl2/src/audio/c_channel.c b/gb.sdl2/src/audio/c_channel.c new file mode 100644 index 00000000..69352a91 --- /dev/null +++ b/gb.sdl2/src/audio/c_channel.c @@ -0,0 +1,411 @@ +/*************************************************************************** + + c_channel.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_CHANNEL_C + +#include "c_channel.h" + +#define THIS ((CCHANNEL *)_object) + +DECLARE_EVENT(EVENT_Finish); + +static CCHANNEL *_cache[MAX_CHANNEL] = { 0 }; +static int _count = 0; + +static int _pipe[2]; +static int _pipe_usage = 0; + +//------------------------------------------------------------------------- + +static void free_channel(CCHANNEL *ch) +{ + if (!ch->sound) + return; + + GB.Unref(POINTER(&ch->sound)); + ch->sound = NULL; + ch->free = FALSE; + + _pipe_usage--; + if (_pipe_usage == 0) + { + //fprintf(stderr, "stop watch\n"); + GB.Watch(_pipe[0], GB_WATCH_NONE, NULL, 0); + } +} + +static void free_finished_channel(void) +{ + //int i; + CCHANNEL *ch; + char channel; + + if (read(_pipe[0], &channel, 1) != 1) + return; + + ch = _cache[(int)channel]; + if (ch) + { + if (ch->free) + free_channel(ch); + //fprintf(stderr, "raise finish %d\n", (int)channel); + GB.Raise(ch, EVENT_Finish, 0); + } + + /*for (i = 0; i < MAX_CHANNEL; i++) + { + ch = channel_cache[i]; + if (ch && ch->free) + free_channel(ch); + }*/ +} + +static void channel_finished_cb(int channel) +{ + CCHANNEL *ch = _cache[channel]; + char buf = (char)channel; + + if (!ch) + return; + + ch->free = (write(_pipe[1], &buf, 1) == 1); + //fprintf(stderr, "finish %d (%d)\n", channel, ch->free); +} + +static int find_free_channel(void) +{ + int i; + + for (i = 0; i < MAX_CHANNEL; i++) + { + if (!_cache[i]) + return i; + } + + return -1; +} + +void CHANNEL_return(int channel, CSOUND *sound) +{ + CCHANNEL *ch = NULL; + + + if (channel < 0 || channel >= _count) + { + if (sound) + GB.Unref(POINTER(&sound)); + + GB.ReturnNull(); + return; + } + + CHECK_AUDIO(); + + ch = _cache[channel]; + if (!ch) + { + ch = GB.New(CLASS_Channel, NULL, NULL); + _cache[channel] = ch; + ch->channel = channel; + //GB.Ref(ch); + } + + //free_channel(ch); + if (sound) + { + GB.Unref(POINTER(&ch->sound)); + ch->sound = sound; + } + + GB.ReturnObject(ch); +} + +bool CHANNEL_init(void) +{ + if (pipe(_pipe)) + { + GB.Error("Unable to initialize channel pipe"); + return TRUE; + } + + _count = Mix_AllocateChannels(-1); + + Mix_ChannelFinished(channel_finished_cb); + return FALSE; +} + +int CHANNEL_play_sound(int channel, CSOUND *sound, int loops, int fadein) +{ + _pipe_usage++; + if (_pipe_usage == 1) + { + //fprintf(stderr, "watch pipe\n"); + GB.Watch(_pipe[0], GB_WATCH_READ, (void *)free_finished_channel, 0); + } + + if (fadein > 0) + return Mix_FadeInChannel(channel, sound->chunk, loops, fadein); + else + return Mix_PlayChannel(channel, sound->chunk, loops); +} + +void CHANNEL_exit() +{ + int i; + CCHANNEL *ch; + + Mix_HaltChannel(-1); + + for (i = 0; i < MAX_CHANNEL; i++) + { + ch = _cache[i]; + if (!ch) + continue; + + free_channel(ch); + GB.Unref(POINTER(&_cache[i])); + } + + if (_pipe_usage) + { + GB.Watch(_pipe[0], GB_WATCH_NONE, NULL, 0); + _pipe_usage = 0; + } + + close(_pipe[0]); + close(_pipe[1]); +} + +static void update_channel_effect(CCHANNEL *_object) +{ + if (Mix_SetPosition(THIS->channel, THIS->angle, THIS->distance) == 0) + GB.Error("Unable to set effect: &1", Mix_GetError()); +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(Channels_get, GB_INTEGER index) + + CHANNEL_return(VARG(index), NULL); + +END_METHOD + +BEGIN_PROPERTY(Channels_Count) + + int nchan; + + CHECK_AUDIO(); + + if (READ_PROPERTY) + GB.ReturnInteger(Mix_AllocateChannels(-1)); + else + { + nchan = VPROP(GB_INTEGER); + if (nchan < 0 || nchan > MAX_CHANNEL) + { + GB.Error(GB_ERR_ARG); + return; + } + + Mix_AllocateChannels(nchan); + _count = Mix_AllocateChannels(-1); + } + +END_PROPERTY + +BEGIN_PROPERTY(Channels_Volume) + + CHECK_AUDIO(); + + if (READ_PROPERTY) + GB.ReturnInteger(Mix_Volume(-1, -1)); + else + Mix_Volume(-1, VPROP(GB_INTEGER)); + +END_PROPERTY + +//------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Channel_new) + + int channel = find_free_channel(); + + if (channel < 0) + { + GB.Error("No more channel available"); + return; + } + + THIS->channel = channel; + _cache[channel] = THIS; + GB.Ref(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Channel_exit) + + CHANNEL_exit(); + +END_METHOD + +BEGIN_METHOD(Channel_Play, GB_OBJECT sound; GB_INTEGER loops; GB_FLOAT fadein) + + CSOUND *sound; + + if (Mix_Paused(THIS->channel)) + Mix_Resume(THIS->channel); + + sound = VARGOPT(sound, NULL); + if (!sound) + return; + + while (THIS->sound) + { + Mix_HaltChannel(THIS->channel); + GB.Loop(10); + } + + GB.Ref(sound); + THIS->sound = sound; + CHANNEL_play_sound(THIS->channel, sound, VARGOPT(loops, 0), (int)(VARGOPT(fadein, 0) * 1000)); + +END_METHOD + + +BEGIN_METHOD_VOID(Channel_Pause) + + Mix_Pause(THIS->channel); + +END_METHOD + + +BEGIN_METHOD(Channel_Stop, GB_FLOAT fadeout) + + if (MISSING(fadeout)) + Mix_HaltChannel(THIS->channel); + else + Mix_FadeOutChannel(THIS->channel, (int)(VARG(fadeout) * 1000)); + +END_METHOD + +BEGIN_PROPERTY(Channel_Volume) + + int channel = THIS->channel; + + if (READ_PROPERTY) + GB.ReturnInteger(Mix_Volume(channel, -1)); + else + Mix_Volume(channel, VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Channel_Distance) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->distance); + else + { + int d = VPROP(GB_INTEGER); + + if (d < 0 || d > 255) + { + GB.Error(GB_ERR_ARG); + return; + } + + THIS->distance = d; + update_channel_effect(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(Channel_Angle) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->angle); + else + { + THIS->angle = VPROP(GB_INTEGER); + update_channel_effect(THIS); + } + +END_PROPERTY + +BEGIN_PROPERTY(Channel_Reverse) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->reverse); + else + { + bool v = VPROP(GB_BOOLEAN); + if (Mix_SetReverseStereo(THIS->channel, v)) + THIS->reverse = v; + else + GB.Error(Mix_GetError()); + } + +END_PROPERTY + +BEGIN_PROPERTY(Channel_Index) + + GB.ReturnInteger(THIS->channel); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC ChannelsDesc[] = +{ + GB_DECLARE_STATIC("Channels"), + + GB_STATIC_METHOD("_get", "Channel", Channels_get, "(Index)i"), + //GB_STATIC_METHOD("_next", "Channel", CCHANNEL_next, NULL), + + GB_STATIC_PROPERTY("Count", "i", Channels_Count), + GB_STATIC_PROPERTY("Volume", "i", Channels_Volume), + + GB_END_DECLARE +}; + +GB_DESC ChannelDesc[] = +{ + GB_DECLARE("Channel", sizeof(CCHANNEL)), + + GB_METHOD("_new", NULL, Channel_new, NULL), + + GB_STATIC_METHOD("_exit", NULL, Channel_exit, NULL), + GB_METHOD("Play", NULL, Channel_Play, "[(Sound)Sound;(Loops)i(FadeIn)f]"), + GB_METHOD("Pause", NULL, Channel_Pause, NULL), + GB_METHOD("Stop", NULL, Channel_Stop, "[(FadeOut)f]"), + + GB_PROPERTY_READ("Index", "i", Channel_Index), + GB_PROPERTY("Volume", "i", Channel_Volume), + GB_PROPERTY("Distance", "i", Channel_Distance), + GB_PROPERTY("Angle", "i", Channel_Angle), + GB_PROPERTY("Reverse", "b", Channel_Reverse), + + GB_EVENT("Finish", NULL, NULL, &EVENT_Finish), + + GB_END_DECLARE +}; + diff --git a/gb.sdl2/src/audio/c_channel.h b/gb.sdl2/src/audio/c_channel.h new file mode 100644 index 00000000..a279b443 --- /dev/null +++ b/gb.sdl2/src/audio/c_channel.h @@ -0,0 +1,55 @@ +/*************************************************************************** + + c_channel.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_CHANNEL_H +#define __C_CHANNEL_H + +#include "main.h" +#include "c_sound.h" + +typedef + struct { + GB_BASE ob; + int channel; + CSOUND *sound; + uchar distance; + ushort angle; + unsigned reverse : 1; + unsigned free : 1; + } + CCHANNEL; + +#ifndef __C_CHANNEL_C +extern GB_DESC ChannelDesc[]; +extern GB_DESC ChannelsDesc[]; +#endif + +#define MAX_CHANNEL 64 + +bool CHANNEL_init(void); +void CHANNEL_exit(void); +int CHANNEL_play_sound(int channel, CSOUND *sound, int loops, int fadein); +void CHANNEL_return(int channel, CSOUND *sound); + +#endif /* __C_CHANNEL_H */ + diff --git a/gb.sdl2/src/audio/c_music.c b/gb.sdl2/src/audio/c_music.c new file mode 100644 index 00000000..1932b936 --- /dev/null +++ b/gb.sdl2/src/audio/c_music.c @@ -0,0 +1,241 @@ +/*************************************************************************** + + c_music.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_MUSIC_C + +#include "c_music.h" + +static Mix_Music *_music = NULL; +static double _ref_time = 0; +static double _ref_pos = 0; +static int _volume = MIX_MAX_VOLUME; + +//------------------------------------------------------------------------- + +static double get_music_pos(void) +{ + double time; + + if (Mix_PlayingMusic()) + { + if (!Mix_PausedMusic()) + { + GB.GetTime(&time, FALSE); + return _ref_pos + time - _ref_time; + } + else + return _ref_pos; + } + else + return 0; +} + +void MUSIC_exit(void) +{ + if (!_music) + return; + + Mix_HaltMusic(); + Mix_RewindMusic(); + Mix_FreeMusic(_music); + _music = NULL; +} + +static void update_volume(void) +{ + if (!Mix_PlayingMusic()) + return; + + Mix_VolumeMusic(_volume); +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(Music_Load, GB_STRING path) + + CHECK_AUDIO(); + + MUSIC_exit(); + + // Note that the music cannot be stored inside the project. + + _music = Mix_LoadMUS(GB.RealFileName(STRING(path), LENGTH(path))); + if (!_music) + { + GB.Error(Mix_GetError()); + return; + } + + _ref_pos = 0; + _ref_time = 0; + +END_METHOD + +BEGIN_METHOD(Music_Play, GB_INTEGER loops; GB_FLOAT fadein) + + double fadevalue=0; + + CHECK_AUDIO(); + + if (!_music) + return; + + GB.GetTime(&_ref_time, FALSE); + + if (Mix_PausedMusic()) + { + Mix_ResumeMusic(); + return; + } + + fadevalue = VARGOPT(fadein, 0) * 1000; + + // if fadevalue is too small the music doesn't want to play. + if (fadevalue < 100) + fadevalue = 0; + + Mix_FadeInMusic(_music, VARGOPT(loops, 1), fadevalue); + update_volume(); + +END_METHOD + +BEGIN_METHOD_VOID(Music_Pause) + + CHECK_AUDIO(); + + _ref_pos = get_music_pos(); + Mix_PauseMusic(); + +END_METHOD + +BEGIN_METHOD(Music_Stop, GB_FLOAT fadeout) + + CHECK_AUDIO(); + + if (MISSING(fadeout)) + Mix_HaltMusic(); + else + Mix_FadeOutMusic(VARG(fadeout) * 1000); + + _ref_pos = 0; + +END_METHOD + +BEGIN_PROPERTY(Music_Pos) + + CHECK_AUDIO(); + + if (READ_PROPERTY) + GB.ReturnFloat(get_music_pos()); + else + { + double pos; + + if (!_music) + return; + + if (Mix_GetMusicType(_music) == MUS_MOD) + { + GB.Error("Seeking is not supported on MOD files"); + return; + } + + pos = VPROP(GB_FLOAT); + Mix_RewindMusic(); + if (Mix_SetMusicPosition(pos) == 0) + _ref_pos = pos; + else + _ref_pos = 0; + GB.GetTime(&_ref_time, FALSE); + } + +END_PROPERTY + +BEGIN_PROPERTY(Music_Volume) + + CHECK_AUDIO(); + + if (READ_PROPERTY) + GB.ReturnInteger(_volume); + else + { + _volume = VPROP(GB_INTEGER); + if (_volume < 0) + _volume = 0; + else if (_volume > MIX_MAX_VOLUME) + _volume = MIX_MAX_VOLUME; + + update_volume(); + } + +END_PROPERTY + +BEGIN_PROPERTY(Music_State) + + CHECK_AUDIO(); + + if (Mix_PlayingMusic()) + { + if (Mix_PausedMusic()) + GB.ReturnInteger(2); + else + GB.ReturnInteger(1); + } + else + GB.ReturnInteger(0); + +END_PROPERTY + +BEGIN_PROPERTY(Music_SoundFontPath) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(Mix_GetSoundFonts()); + else + Mix_SetSoundFonts(GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC MusicDesc[] = +{ + GB_DECLARE_STATIC("Music"), + + GB_STATIC_PROPERTY("SoundFontPath", "s", Music_SoundFontPath), + + GB_STATIC_METHOD("Load", NULL, Music_Load, "(File)s"), + GB_STATIC_METHOD("Play", NULL, Music_Play, "[(Loops)i(FadeIn)f]"), + GB_STATIC_METHOD("Pause", NULL, Music_Pause, NULL), + GB_STATIC_METHOD("Stop", NULL, Music_Stop, "[(FadeOut)f]"), + + GB_STATIC_PROPERTY("Volume", "i", Music_Volume), + GB_STATIC_PROPERTY("Pos", "f", Music_Pos), + + GB_STATIC_PROPERTY_READ("State", "i", Music_State), + + GB_CONSTANT("Stopped", "i", 0), + GB_CONSTANT("Playing", "i", 1), + GB_CONSTANT("Paused", "i", 2), + + GB_END_DECLARE +}; diff --git a/gb.sdl2/src/audio/c_music.h b/gb.sdl2/src/audio/c_music.h new file mode 100644 index 00000000..1c5075b3 --- /dev/null +++ b/gb.sdl2/src/audio/c_music.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + c_music.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_MUSIC_H +#define __C_MUSIC_H + +#include "main.h" + +#ifndef __C_MUSIC_C +extern GB_DESC MusicDesc[]; +#endif + +void MUSIC_exit(void); + +#endif /* __C_MUSIC_H */ + diff --git a/gb.sdl2/src/audio/c_sound.c b/gb.sdl2/src/audio/c_sound.c new file mode 100644 index 00000000..79e4ee5e --- /dev/null +++ b/gb.sdl2/src/audio/c_sound.c @@ -0,0 +1,141 @@ +/*************************************************************************** + + c_sound.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_SOUND_C + +#include "c_channel.h" +#include "c_sound.h" + +#define THIS ((CSOUND *)_object) + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Sound_Frequency) + + if (READ_PROPERTY) + GB.ReturnInteger(AUDIO_frequency); + else + { + if (AUDIO_initialized) + GB.Error("Read-only property. Audio has been initialized"); + else + AUDIO_frequency = VPROP(GB_INTEGER); + } + +END_PROPERTY + +BEGIN_PROPERTY(Sound_BufferSize) + + if (READ_PROPERTY) + GB.ReturnInteger(AUDIO_buffer_size); + else + { + if (AUDIO_initialized) + GB.Error("Read-only property. Audio has been initialized"); + else + AUDIO_buffer_size = VPROP(GB_INTEGER); + } + +END_PROPERTY + +BEGIN_METHOD(Sound_Load, GB_STRING path) + + char *addr; + int len; + Mix_Chunk *chunk; + CSOUND *sound; + + CHECK_AUDIO(); + + if (GB.LoadFile(STRING(path), LENGTH(path), &addr, &len)) + return; + + chunk = Mix_LoadWAV_RW(SDL_RWFromMem(addr, len), TRUE); + GB.ReleaseFile(addr, len); + + if (!chunk) + { + GB.Error(Mix_GetError()); + return; + } + + sound = (CSOUND *)GB.New(CLASS_Sound, NULL, NULL); + sound->chunk = chunk; + GB.ReturnObject(sound); + +END_METHOD + +BEGIN_METHOD_VOID(Sound_free) + + Mix_FreeChunk(THIS->chunk); + THIS->chunk = NULL; + +END_METHOD + +BEGIN_PROPERTY(Sound_Volume) + + if (READ_PROPERTY) + GB.ReturnInteger(Mix_VolumeChunk(THIS->chunk, -1)); + else + { + int vol = VPROP(GB_INTEGER); + if (vol < 0 || vol > MIX_MAX_VOLUME) + GB.Error(GB_ERR_ARG); + else + Mix_VolumeChunk(THIS->chunk, vol); + } + +END_PROPERTY + +BEGIN_METHOD(Sound_Play, GB_INTEGER loops; GB_FLOAT fadein) + + int loops = VARGOPT(loops, 0); + int channel; + + GB.Ref(THIS); + channel = CHANNEL_play_sound(-1, THIS, loops, MISSING(fadein) ? 0 : (int)(VARG(fadein) * 1000)); + CHANNEL_return(channel, THIS); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC SoundDesc[] = +{ + GB_DECLARE("Sound", sizeof(CSOUND)), GB_NOT_CREATABLE(), + + GB_CONSTANT("MaxVolume", "i", MIX_MAX_VOLUME), + + GB_STATIC_PROPERTY("Frequency", "i", Sound_Frequency), + GB_STATIC_PROPERTY("BufferSize", "i", Sound_BufferSize), + + GB_STATIC_METHOD("Load", "Sound", Sound_Load, "(Path)s"), + + GB_METHOD("_free", NULL, Sound_free, NULL), + + GB_PROPERTY("Volume", "i", Sound_Volume), + + GB_METHOD("Play", "Channel", Sound_Play, "[(Loops)i(FadeIn)f]"), + + GB_END_DECLARE +}; diff --git a/gb.sdl2/src/audio/c_sound.h b/gb.sdl2/src/audio/c_sound.h new file mode 100644 index 00000000..d930a2f0 --- /dev/null +++ b/gb.sdl2/src/audio/c_sound.h @@ -0,0 +1,41 @@ +/*************************************************************************** + + c_sound.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_SOUND_H +#define __C_SOUND_H + +#include "main.h" + +typedef + struct { + GB_BASE ob; + Mix_Chunk *chunk; + } + CSOUND; + +#ifndef __C_SOUND_C +extern GB_DESC SoundDesc[]; +#endif + +#endif /* __C_SOUND_H */ + diff --git a/gb.sdl2/src/audio/gb.sdl2.audio.component b/gb.sdl2/src/audio/gb.sdl2.audio.component new file mode 100644 index 00000000..4c09377b --- /dev/null +++ b/gb.sdl2/src/audio/gb.sdl2.audio.component @@ -0,0 +1,2 @@ +[Component] +Key=gb.sdl2.audio diff --git a/gb.sdl2/src/audio/main.c b/gb.sdl2/src/audio/main.c new file mode 100644 index 00000000..802b8a16 --- /dev/null +++ b/gb.sdl2/src/audio/main.c @@ -0,0 +1,159 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gambas.h" +#include "main.h" +#include "c_channel.h" +#include "c_music.h" +#include "c_sound.h" + +GB_INTERFACE GB EXPORT; + +GB_CLASS CLASS_Sound; +GB_CLASS CLASS_Channel; + +int AUDIO_frequency = 44100; +int AUDIO_buffer_size = 4096; +bool AUDIO_initialized = FALSE; + +//------------------------------------------------------------------------- + +static void init_mixer(int flag) +{ + if ((Mix_Init(flag) & flag) != flag) + fprintf(stderr, "gb.sdl2.audio: warning: %s\n", Mix_GetError()); +} + +bool AUDIO_init() +{ + Uint16 format; + int channels; + + if (AUDIO_initialized) + return FALSE; + + init_mixer(MIX_INIT_MP3); + init_mixer(MIX_INIT_OGG); + init_mixer(MIX_INIT_MOD); + init_mixer(MIX_INIT_FLAC); +#ifdef MIX_INIT_FLUIDSYNTH + init_mixer(MIX_INIT_FLUIDSYNTH); +#endif +#ifdef MIX_INIT_MID + init_mixer(MIX_INIT_MID); +#endif + + if (Mix_OpenAudio(AUDIO_frequency, MIX_DEFAULT_FORMAT, 2, AUDIO_buffer_size)) + { + GB.Error("Unable to initialize mixer"); + return TRUE; + } + + Mix_QuerySpec(&AUDIO_frequency, &format, &channels); + //fprintf(stderr, "AUDIO_init: %d %d %d\n", AUDIO_frequency, format, channels); + + if (CHANNEL_init()) + return TRUE; + + AUDIO_initialized = TRUE; + return FALSE; +} + +static void AUDIO_exit() +{ + if (!AUDIO_initialized) + return; + + // Don't free Gambas objects from GB_EXIT! + // CHANNEL_exit(); + MUSIC_exit(); + Mix_CloseAudio(); + + while (Mix_Init(0)) + Mix_Quit(); +} + +static void init_sdl() +{ + uint init = SDL_WasInit(SDL_INIT_EVERYTHING); + + // if video is defined, SDL was initialized by gb.sdl2 component ! + if (init & SDL_INIT_VIDEO) + { + if (SDL_InitSubSystem(SDL_INIT_AUDIO)) + goto __ERROR; + } + else + { + if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) + goto __ERROR; + } + + return; + +__ERROR: + + fprintf(stderr, "gb.sdl2.audio: unable to initialize SDL: %s\n", SDL_GetError()); + abort(); +} + +static void exit_sdl() +{ + uint init = SDL_WasInit(SDL_INIT_EVERYTHING); + + AUDIO_exit(); + + // if video is defined, gb.sdl2 component still not closed ! + if (init & SDL_INIT_VIDEO) + SDL_QuitSubSystem(SDL_INIT_AUDIO); + else + SDL_Quit(); +} + +//------------------------------------------------------------------------- + +GB_DESC *GB_CLASSES[] EXPORT = +{ + SoundDesc, + ChannelDesc, + ChannelsDesc, + MusicDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + CLASS_Sound = GB.FindClass("Sound"); + CLASS_Channel = GB.FindClass("Channel"); + + init_sdl(); + + return -1; +} + +void EXPORT GB_EXIT() +{ + exit_sdl(); +} diff --git a/gb.sdl2/src/audio/main.h b/gb.sdl2/src/audio/main.h new file mode 100644 index 00000000..50a3054e --- /dev/null +++ b/gb.sdl2/src/audio/main.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "gb_list.h" + +#include "SDL.h" +#include "SDL_mixer.h" + +#ifndef __MAIN_C + +extern GB_INTERFACE GB; + +extern GB_CLASS CLASS_Sound; +extern GB_CLASS CLASS_Channel; + +extern bool AUDIO_initialized; +extern int AUDIO_frequency; +extern int AUDIO_buffer_size; + +#endif + +bool AUDIO_init(void); + +#define CHECK_AUDIO() if (!AUDIO_initialized && AUDIO_init()) return + +#endif /* __MAIN_H */ + diff --git a/gb.sdl2/src/c_draw.c b/gb.sdl2/src/c_draw.c new file mode 100644 index 00000000..0290c3e3 --- /dev/null +++ b/gb.sdl2/src/c_draw.c @@ -0,0 +1,421 @@ +/*************************************************************************** + + c_draw.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_DRAW_C + +#include "c_window.h" +#include "c_image.h" +#include "c_font.h" +#include "c_draw.h" + +#define DRAW_STACK_MAX 8 +static CDRAW _draw_stack[DRAW_STACK_MAX]; +static CDRAW *_draw_current = NULL; +static CFONT *_default_font = NULL; + +#define THIS _draw_current +#define RENDERER _draw_current->renderer + +static bool check_device(void) +{ + if (THIS == NULL) + { + GB.Error("No device"); + return TRUE; + } + else + return FALSE; +} + +#define CHECK_DEVICE() if (check_device()) return + +static CFONT *get_default_font() +{ + if (!_default_font) + { + _default_font = FONT_create(); + GB.Ref(_default_font); + } + + return _default_font; +} + +/*static GB_COLOR get_background() +{ + uchar r, g, b, a; + SDL_GetRenderDrawColor(RENDERER, &r, &g, &b, &a); + return GB_COLOR_MAKE(r, g, b, a); +}*/ + +static void set_background(GB_COLOR col) +{ + uchar r, g, b, a; + + GB_COLOR_SPLIT(col, r, g, b, a); + SDL_SetRenderDrawColor(RENDERER, r, g, b, a); + + if (a != 255) + SDL_SetRenderDrawBlendMode(RENDERER, SDL_BLENDMODE_BLEND); + else + SDL_SetRenderDrawBlendMode(RENDERER, SDL_BLENDMODE_NONE); +} + +void DRAW_begin(void *device) +{ + if (THIS >= &_draw_stack[DRAW_STACK_MAX - 1]) + { + GB.Error("Too many nested drawings"); + return; + } + + if (GB.CheckObject(device)) + return; + + if (THIS == 0) + THIS = _draw_stack; + else + THIS++; + + THIS->font = get_default_font(); + GB.Ref(THIS->font); + + if (GB.Is(device, CLASS_Window)) + { + THIS->device = device; + THIS->renderer = ((CWINDOW *)device)->renderer; + GB.Ref(THIS->device); + THIS->color = (GB_COLOR)0xFFFFFF; + return; + } + + GB.Error("Unsupported device"); +} + +void DRAW_end(void) +{ + if (!THIS) + return; + + GB.Unref(POINTER(&THIS->device)); + THIS->device = NULL; + GB.Unref(POINTER(&THIS->font)); + THIS->font = NULL; + + if (THIS == _draw_stack) + THIS = NULL; + else + THIS--; +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Draw_exit) + + if (_default_font) + GB.Unref(POINTER(&_default_font)); + +END_METHOD + +BEGIN_METHOD(Draw_Begin, GB_OBJECT device) + + void *device = VARG(device); + DRAW_begin(device); + +END_METHOD + +BEGIN_METHOD_VOID(Draw_End) + + CHECK_DEVICE(); + DRAW_end(); + +END_METHOD + +BEGIN_METHOD(Draw_Clear, GB_INTEGER col) + + CHECK_DEVICE(); + + set_background(VARGOPT(col, 0)); + SDL_RenderClear(RENDERER); + +END_METHOD + +BEGIN_PROPERTY(Draw_Background) + + CHECK_DEVICE(); + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->color); + else + THIS->color = VPROP(GB_INTEGER); + +END_PROPERTY + +BEGIN_METHOD(Draw_Rect, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER col) + + SDL_Rect rect; + + CHECK_DEVICE(); + + rect.x = VARG(x); + rect.y = VARG(y); + rect.w = VARG(w); + rect.h = VARG(h); + + set_background(VARGOPT(col, THIS->color)); + SDL_RenderDrawRect(RENDERER, &rect); + +END_METHOD + +BEGIN_METHOD(Draw_FillRect, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER col) + + SDL_Rect rect; + + CHECK_DEVICE(); + + rect.x = VARG(x); + rect.y = VARG(y); + rect.w = VARG(w); + rect.h = VARG(h); + + set_background(VARGOPT(col, THIS->color)); + SDL_RenderFillRect(RENDERER, &rect); + +END_METHOD + +BEGIN_METHOD(Draw_Line, GB_INTEGER x1; GB_INTEGER y1; GB_INTEGER x2; GB_INTEGER y2; GB_INTEGER col) + + CHECK_DEVICE(); + + set_background(VARGOPT(col, THIS->color)); + SDL_RenderDrawLine(RENDERER, VARG(x1), VARG(y1), VARG(x2), VARG(y2)); + +END_METHOD + +BEGIN_METHOD(Draw_Point, GB_INTEGER x; GB_INTEGER y; GB_INTEGER col) + + CHECK_DEVICE(); + + set_background(VARGOPT(col, THIS->color)); + SDL_RenderDrawPoint(RENDERER, VARG(x), VARG(y)); + +END_METHOD + +BEGIN_METHOD(Draw_Points, GB_OBJECT points; GB_INTEGER col) + + GB_ARRAY points; + uint n; + + CHECK_DEVICE(); + + points = (GB_ARRAY)VARG(points); + if (GB.CheckObject(points)) + return; + + n = GB.Array.Count(points) / 2; + if (n == 0) + return; + + set_background(VARGOPT(col, THIS->color)); + SDL_RenderDrawPoints(RENDERER, (SDL_Point *)GB.Array.Get(points, 0), n); + +END_METHOD + +BEGIN_METHOD(Draw_Lines, GB_OBJECT points; GB_INTEGER col) + + GB_ARRAY points; + uint n; + + CHECK_DEVICE(); + + points = (GB_ARRAY)VARG(points); + if (GB.CheckObject(points)) + return; + + n = GB.Array.Count(points) / 2; + if (n == 0) + return; + + set_background(VARGOPT(col, THIS->color)); + SDL_RenderDrawLines(RENDERER, (SDL_Point *)GB.Array.Get(points, 0), n); + +END_METHOD + +BEGIN_METHOD(Draw_Rects, GB_OBJECT rects; GB_INTEGER col) + + GB_ARRAY rects; + uint n; + + CHECK_DEVICE(); + + rects = (GB_ARRAY)VARG(rects); + if (GB.CheckObject(rects)) + return; + + n = GB.Array.Count(rects) / 4; + if (n == 0) + return; + + set_background(VARGOPT(col, THIS->color)); + SDL_RenderDrawRects(RENDERER, (SDL_Rect *)GB.Array.Get(rects, 0), n); + +END_METHOD + +BEGIN_METHOD(Draw_FillRects, GB_OBJECT rects; GB_INTEGER col) + + GB_ARRAY rects; + uint n; + + CHECK_DEVICE(); + + rects = (GB_ARRAY)VARG(rects); + if (GB.CheckObject(rects)) + return; + + n = GB.Array.Count(rects) / 4; + if (n == 0) + return; + + if (!MISSING(col)) set_background(VARG(col)); + SDL_RenderFillRects(RENDERER, (SDL_Rect *)GB.Array.Get(rects, 0), n); + +END_METHOD + +BEGIN_METHOD(Draw_Image, GB_OBJECT image; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_OBJECT src; GB_FLOAT opacity; GB_FLOAT angle) + + CIMAGE *image; + SDL_Texture *texture; + GEOM_RECT *src; + SDL_Rect *rect; + SDL_Rect dest; + + CHECK_DEVICE(); + + image = VARG(image); + if (GB.CheckObject(image)) + return; + + texture = IMAGE_get_texture(image, (CWINDOW *)THIS->device); + + dest.x = VARG(x); + dest.y = VARG(y); + dest.w = VARGOPT(w, image->img.width); + dest.h = VARGOPT(h, image->img.height); + + src = VARGOPT(src, NULL); + if (src) + rect = (SDL_Rect *)&src->x; + else + rect = NULL; + + if (MISSING(opacity) && MISSING(angle)) + SDL_RenderCopy(RENDERER, texture, rect, &dest); + else + { + SDL_SetTextureAlphaMod(texture, VARGOPT(opacity, 1.0) * 255); + SDL_RenderCopyEx(RENDERER, texture, rect, &dest, VARGOPT(angle, 0.0), NULL, SDL_FLIP_NONE); + SDL_SetTextureAlphaMod(texture, 255); + } + +END_METHOD + +BEGIN_PROPERTY(Draw_Font) + + CHECK_DEVICE(); + + if (READ_PROPERTY) + GB.ReturnObject(THIS->font); + else + { + GB.StoreObject(PROP(GB_OBJECT), POINTER(&THIS->font)); + if (!THIS->font) + { + THIS->font = get_default_font(); + GB.Ref(THIS->font); + } + } + +END_PROPERTY + +BEGIN_METHOD(Draw_Text, GB_STRING text; GB_INTEGER x; GB_INTEGER y) + + SDL_Image *image; + SDL_Texture *texture; + SDL_Rect dest; + uint r, g, b, a; + + CHECK_DEVICE(); + + if (!LENGTH(text)) + return; + + dest.x = VARG(x); + dest.y = VARG(y); + + image = FONT_render_text(THIS->font, (CWINDOW *)THIS->device, STRING(text), LENGTH(text), &dest.w, &dest.h); + if (!image) + return; + + texture = SDL_GetTextureFromImage(image, (CWINDOW *)THIS->device); + if (image->surface) + { + SDL_FreeSurface(image->surface); + image->surface = NULL; + } + + GB_COLOR_SPLIT(THIS->color, r, g, b, a); + SDL_SetTextureColorMod(texture, r, g, b); + SDL_SetTextureAlphaMod(texture, a); + + SDL_RenderCopy(RENDERER, texture, NULL, &dest); + +END_METHOD + +//------------------------------------------------------------------------- + + +GB_DESC DrawDesc[] = +{ + GB_DECLARE_STATIC("Draw"), + + GB_STATIC_METHOD("_exit", NULL, Draw_exit, NULL), + + GB_STATIC_METHOD("Begin", NULL, Draw_Begin, "(Device)o"), + GB_STATIC_METHOD("End", NULL, Draw_End, NULL), + + GB_STATIC_METHOD("Clear", NULL, Draw_Clear, "[(Color)i]"), + GB_STATIC_METHOD("Rect", NULL, Draw_Rect, "(X)i(Y)i(Width)i(Height)i[(Color)i]"), + GB_STATIC_METHOD("Rects", NULL, Draw_Rects, "(Rectangles)Integer[];[(Color)i]"), + GB_STATIC_METHOD("FillRect", NULL, Draw_FillRect, "(X)i(Y)i(Width)i(Height)i[(Color)i]"), + GB_STATIC_METHOD("FillRects", NULL, Draw_FillRects, "(Rectangles)Integer[];[(Color)i]"), + GB_STATIC_METHOD("Line", NULL, Draw_Line, "(X1)i(Y1)i(X2)i(Y2)i[(Color)i]"), + GB_STATIC_METHOD("Lines", NULL, Draw_Lines, "(Lines)Integer[];[(Color)i]"), + GB_STATIC_METHOD("Point", NULL, Draw_Point, "(X)i(Y)i[(Color)i]"), + GB_STATIC_METHOD("Points", NULL, Draw_Points, "(Points)Integer[];[(Color)i]"), + GB_STATIC_METHOD("Image", NULL, Draw_Image, "(Image)Image;(X)i(Y)i[(Width)i(Height)i(Source)Rect;(Opacity)f(Angle)f"), + GB_STATIC_METHOD("Text", NULL, Draw_Text, "(Text)s(X)i(Y)i"), + + GB_STATIC_PROPERTY("Background", "i", Draw_Background), + GB_STATIC_PROPERTY("Font", "Font", Draw_Font), + + GB_END_DECLARE +}; diff --git a/gb.sdl2/src/c_draw.h b/gb.sdl2/src/c_draw.h new file mode 100644 index 00000000..b86f89a7 --- /dev/null +++ b/gb.sdl2/src/c_draw.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + c_draw.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_DRAW_H +#define __C_DRAW_H + +#include "main.h" +#include "c_font.h" + +typedef + struct { + void *device; + SDL_Renderer *renderer; + CFONT *font; + GB_COLOR color; + } + CDRAW; + +#ifndef __C_DRAW_C + +extern GB_DESC DrawDesc[]; + +void DRAW_begin(void *device); +void DRAW_end(); + +#endif + +#endif /* __C_DRAW_H */ + diff --git a/gb.sdl2/src/c_font.c b/gb.sdl2/src/c_font.c new file mode 100644 index 00000000..a5679ea6 --- /dev/null +++ b/gb.sdl2/src/c_font.c @@ -0,0 +1,527 @@ +/*************************************************************************** + + c_font.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_FONT_C + +#include "default_font.h" +#include "c_font.h" + +typedef + struct { + LIST list; + char *path; + char *name; + } + FONT_LOAD; + +static FONT_LOAD *_font_list = NULL; + +#define THIS ((CFONT *)_object) + +static void create_cache(CFONT *_object) +{ + if (!THIS->cache) + GB.HashTable.New(&THIS->cache, GB_COMP_BINARY); +} + +CFONT *FONT_create() +{ + CFONT *font = (CFONT *)GB.New(CLASS_Font, NULL, NULL); + create_cache(font); + return font; +} + +static FONT_LOAD *find_font(const char *name) +{ + FONT_LOAD *p; + + LIST_for_each(p, _font_list) + { + if (strcasecmp(name, p->name) == 0) + return p; + } + + return NULL; +} + +static bool load_font(char *path, char *name) +{ + FONT_LOAD *load; + char *addr; + int len; + + if (GB.LoadFile(path, strlen(path), &addr, &len)) + return TRUE; + + GB.ReleaseFile(addr, len); + + if (!name || !*name) + { + char *p = strrchr(path, '/'); + if (p) + name = p + 1; + else + name = path; + + p = strrchr(name, '.'); + if (p) + name = GB.TempString(name, p - name); + } + + if (find_font(name)) + { + GB.Error("Font already exists"); + return TRUE; + } + + GB.AllocZero(POINTER(&load), sizeof(FONT_LOAD)); + load->path = GB.NewZeroString(path); + load->name = GB.NewZeroString(name); + LIST_insert(&_font_list, load, &load->list); + + return FALSE; +} + +static void release_cache(CFONT *_object) +{ + GB.HashTable.Enum(THIS->cache, (GB_HASHTABLE_ENUM_FUNC)SDL_FreeImage); + GB.HashTable.Free(&THIS->cache); +} + +static bool init_font() +{ + if (!TTF_WasInit() && TTF_Init()) + { + GB.Error("Unable to initialize TTF library: &1", TTF_GetError()); + return TRUE; + } + else + return FALSE; +} + +static bool check_font(CFONT *_object) +{ + FONT_LOAD *font; + int style; + char *addr; + int len; + + if (!THIS->dirty) + return FALSE; + + if (THIS->font) + { + if (init_font()) + return TRUE; + TTF_CloseFont(THIS->font); + THIS->font = NULL; + } + + release_cache(THIS); + create_cache(THIS); + + if (!THIS->name) + { + THIS->dirty = FALSE; + return FALSE; + } + + font = find_font(THIS->name); + if (!font) + { + GB.Error("Unknown font: &1", THIS->name); + return TRUE; + } + + if (GB.LoadFile(font->path, strlen(font->path), &addr, &len)) + return TRUE; + + if (init_font()) + return TRUE; + + THIS->font = TTF_OpenFontRW(SDL_RWFromConstMem(addr, len), TRUE, THIS->size); + if (!THIS->font) + { + GB.Error("Unable to load font: &1: &2", THIS->name, TTF_GetError()); + return TRUE; + } + + style = TTF_STYLE_NORMAL; + if (THIS->bold) style |= TTF_STYLE_BOLD; + if (THIS->italic) style |= TTF_STYLE_ITALIC; + TTF_SetFontStyle(THIS->font, style); + + THIS->dirty = FALSE; + + return FALSE; +} + +static int get_font_ascent(void *_object) +{ + if (THIS->font) + return TTF_FontAscent(THIS->font); + else + return DEFAULT_FONT_ASCENT * THIS->size / DEFAULT_FONT_HEIGHT; +} + +static int get_font_descent(void *_object) +{ + if (THIS->font) + return TTF_FontDescent(THIS->font); + else + return DEFAULT_FONT_DESCENT * THIS->size / DEFAULT_FONT_HEIGHT; +} + +static void get_text_size(void *_object, const char *text, int *width, int *height) +{ + if (!text || !*text) + { + *width = 0; + *height = get_font_ascent(THIS) + get_font_descent(THIS); + return; + } + + if (THIS->font) + { + TTF_SizeUTF8(THIS->font, text, width, height); + return; + } + + *width = UTF8_get_length(text, strlen(text)) * DEFAULT_FONT_WIDTH * THIS->size / DEFAULT_FONT_HEIGHT; + *height = THIS->size; +} + + +SDL_Image *FONT_render_text(CFONT *_object, CWINDOW *window, char *text, int len, int *w, int *h) +{ + SDL_Surface *surface; + SDL_Image *image; + bool exist; + + if (check_font(THIS)) + return NULL; + + exist = !GB.HashTable.Get(THIS->cache, text, len, POINTER(&image)); + + if (exist) + { + if (image->window == window) + { + SDL_QueryTexture(image->texture, NULL, NULL, w, h); + if (!THIS->name) + { + *w = *w * THIS->size / DEFAULT_FONT_HEIGHT; + *h = *h * THIS->size / DEFAULT_FONT_HEIGHT; + } + + return image; + } + + SDL_FreeImage(image); + GB.HashTable.Remove(THIS->cache, text, len); + } + + if (THIS->name) + { + SDL_Color color = { 0xFF, 0xFF, 0xFF, 0xFF }; + char c = text[len]; + text[len] = 0; + surface = TTF_RenderUTF8_Blended(THIS->font, text, color); + text[len] = c; + *w = surface->w; + *h = surface->h; + } + else + { + int size = UTF8_get_length(text, len); + + surface = SDL_CreateRGBSurface(0, size * DEFAULT_FONT_WIDTH, DEFAULT_FONT_HEIGHT, 32, RMASK, GMASK, BMASK, AMASK); + + if (SDL_MUSTLOCK(surface)) + SDL_LockSurface(surface); + + FONT_render_default((uint *)surface->pixels, size, text, len); + + if (SDL_MUSTLOCK(surface)) + SDL_UnlockSurface(surface); + + *w = surface->w * THIS->size / DEFAULT_FONT_HEIGHT; + *h = surface->h * THIS->size / DEFAULT_FONT_HEIGHT; + } + + image = SDL_CreateImage(surface); + + if (GB.HashTable.Count(THIS->cache) >= 128) + { + release_cache(THIS); + create_cache(THIS); + } + + GB.HashTable.Add(THIS->cache, text, len, image); + return image; +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Font_exit) + + FONT_LOAD *next; + + while (_font_list) + { + next = _font_list->list.next; + GB.FreeString(&_font_list->path); + GB.FreeString(&_font_list->name); + GB.Free(POINTER(&_font_list)); + _font_list = next; + } + +END_METHOD + +BEGIN_METHOD(Font_Load, GB_STRING path; GB_STRING name) + + char *path = GB.ToZeroString(ARG(path)); + char *name; + + if (MISSING(name)) + name = NULL; + else + name = GB.ToZeroString(ARG(name)); + + load_font(path, name); + +END_METHOD + +BEGIN_METHOD(Font_get, GB_STRING font) + + CFONT *font; + char *desc = GB.ToZeroString(ARG(font)); + char *elt; + int val; + bool bold = FALSE; + bool italic = FALSE; + int size = 10; + char *name = NULL; + + for (elt = strtok(desc, ","); elt; elt = strtok(NULL, ",")) + { + if (strcasecmp(elt, "bold") == 0) + { + bold = TRUE; + continue; + } + + if (strcasecmp(elt, "italic") == 0) + { + italic = TRUE; + continue; + } + + val = atoi(elt); + if (val) + { + size = val; + continue; + } + + if (name) + { + GB.Error("Font name defined twice"); + goto ERROR; + } + + name = GB.NewZeroString(elt); + } + + if (size < 1 || size > 1024) + { + GB.Error("Incorrect font size"); + goto ERROR; + } + + font = FONT_create(); + font->name = name; + font->bold = bold; + font->italic = italic; + font->size = size; + font->dirty = TRUE; + + GB.ReturnObject(font); + return; + +ERROR: + + GB.FreeString(&name); + return; + +END_METHOD + +BEGIN_METHOD_VOID(Font_free) + + release_cache(THIS); + + if (THIS->font) + TTF_CloseFont(THIS->font); + + GB.FreeString(&THIS->name); + +END_METHOD + +BEGIN_PROPERTY(Font_Size) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->size); + else + { + int size = VPROP(GB_INTEGER); + if (size < 1 || size > 1024) + GB.Error("Incorrect font size"); + else if (THIS->size != size) + { + THIS->size = size; + if (THIS->name) + THIS->dirty = TRUE; + } + } + +END_PROPERTY + +BEGIN_PROPERTY(Font_Bold) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->bold); + else if (THIS->bold != VPROP(GB_BOOLEAN)) + { + THIS->bold = VPROP(GB_BOOLEAN); + if (THIS->font && !THIS->dirty) + TTF_SetFontStyle(THIS->font, (TTF_GetFontStyle(THIS->font) ^ TTF_STYLE_BOLD)); + } + +END_PROPERTY + +BEGIN_PROPERTY(Font_Italic) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->italic); + else if (THIS->italic != VPROP(GB_BOOLEAN)) + { + THIS->italic = VPROP(GB_BOOLEAN); + if (THIS->font && !THIS->dirty) + TTF_SetFontStyle(THIS->font, (TTF_GetFontStyle(THIS->font) ^ TTF_STYLE_ITALIC)); + } + +END_PROPERTY + +BEGIN_PROPERTY(Font_Name) + + if (READ_PROPERTY) + GB.ReturnString(THIS->name); + else + { + GB.StoreString(PROP(GB_STRING), &THIS->name); + THIS->dirty = TRUE; + } + +END_PROPERTY + +BEGIN_PROPERTY(Font_Ascent) + + if (check_font(THIS)) + return; + + GB.ReturnInteger(TTF_FontAscent(THIS->font)); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Descent) + + if (check_font(THIS)) + return; + + GB.ReturnInteger(TTF_FontDescent(THIS->font)); + +END_PROPERTY + +BEGIN_PROPERTY(Font_Fixed) + + if (check_font(THIS)) + return; + + GB.ReturnBoolean(TTF_FontFaceIsFixedWidth(THIS->font)); + +END_PROPERTY + +BEGIN_METHOD(Font_TextWidth, GB_STRING text) + + int w, h; + + if (check_font(THIS)) + return; + + get_text_size(THIS, GB.ToZeroString(ARG(text)), &w, &h); + GB.ReturnInteger(w); + +END_METHOD + +BEGIN_METHOD(Font_TextHeight, GB_STRING text) + + int w, h; + + if (check_font(THIS)) + return; + + get_text_size(THIS, GB.ToZeroString(ARG(text)), &w, &h); + GB.ReturnInteger(h); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC FontDesc[] = +{ + GB_DECLARE("Font", sizeof(CFONT)), GB_NOT_CREATABLE(), + + GB_CONSTANT("DefaultHeight", "i", DEFAULT_FONT_HEIGHT), + + GB_STATIC_METHOD("Load", NULL, Font_Load, "(Path)s[(Name)s]"), + GB_STATIC_METHOD("_get", "Font", Font_get, "(Font)s"), + GB_STATIC_METHOD("_exit", NULL, Font_exit, NULL), + + GB_METHOD("_free", NULL, Font_free, NULL), + + GB_PROPERTY("Size", "i", Font_Size), + GB_PROPERTY("Bold", "b", Font_Bold), + GB_PROPERTY("Italic", "b", Font_Italic), + //GB_PROPERTY("Underline", "b", Font_Underline), + + GB_PROPERTY_READ("Name", "s", Font_Name), + GB_PROPERTY_READ("Ascent", "i", Font_Ascent), + GB_PROPERTY_READ("Descent", "i", Font_Descent), + GB_PROPERTY_READ("Fixed", "b", Font_Fixed), + + GB_METHOD("TextWidth", "i", Font_TextWidth, "(Text)s"), + GB_METHOD("TextHeight", "i", Font_TextHeight, "(Text)s"), + /*GB_METHOD("GetImage", "Image", Font_GetImage, "(Text)s"),*/ + + GB_END_DECLARE +}; diff --git a/gb.sdl2/src/c_font.h b/gb.sdl2/src/c_font.h new file mode 100644 index 00000000..73e942ed --- /dev/null +++ b/gb.sdl2/src/c_font.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + c_font.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_FONT_H +#define __C_FONT_H + +#include "main.h" +#include "c_window.h" +#include "c_image.h" + +typedef + struct { + GB_BASE ob; + TTF_Font *font; + char *name; + int size; + GB_HASHTABLE cache; + unsigned bold : 1; + unsigned italic : 1; + unsigned dirty : 1; + } + CFONT; + +#ifndef __C_FONT_C +extern GB_DESC FontDesc[]; +#endif + +CFONT *FONT_create(); +SDL_Image *FONT_render_text(CFONT *_object, CWINDOW *window, char *text, int len, int *w, int *h); + +#endif /* __C_FONT_H */ + diff --git a/gb.sdl2/src/c_image.c b/gb.sdl2/src/c_image.c new file mode 100644 index 00000000..9b4d3b37 --- /dev/null +++ b/gb.sdl2/src/c_image.c @@ -0,0 +1,205 @@ +/*************************************************************************** + + c_image.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_IMAGE_C + +#include "c_image.h" + +#define THIS ((CIMAGE *)_object) +#define THIS_IMAGE (&THIS->img) + +SDL_Image *SDL_CreateImage(SDL_Surface *surface) +{ + SDL_Image *image; + + GB.Alloc(POINTER(&image), sizeof(SDL_Image)); + + image->texture = NULL; + image->window = NULL; + image->surface = surface; + + return image; +} + +void SDL_FreeImage(SDL_Image *image) +{ + if (image->texture) + { + SDL_DestroyTexture(image->texture); + image->texture = NULL; + GB.Unref(POINTER(&image->window)); + } + + if (image->surface) + { + SDL_FreeSurface(image->surface); + image->surface = NULL; + } + + GB.Free(POINTER(&image)); +} + +static void free_image(GB_IMG *img, void *image) +{ + SDL_FreeImage((SDL_Image *)image); +} + +static void *temp_image(GB_IMG *img) +{ + SDL_Surface *surface = NULL; + + if (img && img->data) + surface = SDL_CreateRGBSurfaceFrom(img->data, img->width, img->height, 32, img->width * sizeof(int), RMASK, GMASK, BMASK, AMASK); + + + return SDL_CreateImage(surface); +} + +/*static void sync_image(GB_IMG *image) +{ + // Synchronize l'image texture->image + + // Puis mets le flag de synchro à false + image->sync = false; +}*/ + +static GB_IMG_OWNER _image_owner = { + "gb.sdl2", + DEFAULT_IMAGE_FORMAT, + free_image, + free_image, + temp_image, + NULL, //sync_image, + }; + +SDL_Image *IMAGE_get(CIMAGE *_object) +{ + return (SDL_Image *)IMAGE.Check(THIS_IMAGE, &_image_owner); +} + +#define check_image IMAGE_get + +static void take_image(CIMAGE *_object, SDL_Image *image) +{ + if (image && image->surface) + IMAGE.Take(THIS_IMAGE, &_image_owner, image, image->surface->w, image->surface->h, image->surface->pixels); + else + IMAGE.Take(THIS_IMAGE, &_image_owner, image, 0, 0, NULL); +} + +CIMAGE *IMAGE_create(SDL_Image *image) +{ + CIMAGE *img; + + img = (CIMAGE *)GB.New(CLASS_Image, NULL, NULL); + take_image(img, image); + return img; +} + +SDL_Texture *SDL_GetTextureFromImage(SDL_Image *image, CWINDOW *window) +{ + if (image->texture && image->window != window) + { + SDL_DestroyTexture(image->texture); + GB.Unref(POINTER(&image->window)); + image->texture = NULL; + } + + if (!image->texture) + { + image->texture = SDL_CreateTextureFromSurface(window->renderer, image->surface); + //SDL_SetTextureBlendMode(image->texture, SDL_BLENDMODE_BLEND); + image->window = window; + GB.Ref(window); + } + + return image->texture; +} + +SDL_Texture *IMAGE_get_texture(CIMAGE *_object, CWINDOW *window) +{ + return SDL_GetTextureFromImage(IMAGE_get(THIS), window); +} + +CIMAGE *IMAGE_create_from_window(CWINDOW *window, int x, int y, int w, int h) +{ + SDL_Surface *surface; + SDL_Rect rect = { x, y, w, h }; + + surface = SDL_CreateRGBSurface(0, w, h, 32, RMASK, GMASK, BMASK, AMASK); + SDL_RenderReadPixels(window->renderer, &rect, DEFAULT_SDL_IMAGE_FORMAT, surface->pixels, surface->pitch); + return IMAGE_create(SDL_CreateImage(surface)); +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(Image_Load, GB_STRING path) + + char *addr; + int len; + SDL_Surface *surface; + SDL_Surface *csurface; + + if (GB.LoadFile(STRING(path), LENGTH(path), &addr, &len)) + return; + + surface = IMG_Load_RW(SDL_RWFromConstMem(addr, len), TRUE); + GB.ReleaseFile(addr, len); + + if (!surface) + { + GB.Error("Unable to load image: &1", IMG_GetError()); + return; + } + + if (surface->format->format != DEFAULT_SDL_IMAGE_FORMAT) + { + csurface = SDL_ConvertSurfaceFormat(surface, DEFAULT_SDL_IMAGE_FORMAT, 0); + SDL_FreeSurface(surface); + surface = csurface; + } + + GB.ReturnObject(IMAGE_create(SDL_CreateImage(surface))); + +END_METHOD + +BEGIN_METHOD(Image_Save, GB_STRING path) + + char *path = GB.FileName(STRING(path), LENGTH(path)); + + if (SDL_SaveBMP(IMAGE_get(THIS)->surface, path)) + RAISE_ERROR("Unable to save image: &1"); + +END_METHOD + +//------------------------------------------------------------------------- + +GB_DESC ImageDesc[] = +{ + GB_DECLARE("Image", sizeof(CIMAGE)), + + GB_STATIC_METHOD("Load", "Image", Image_Load, "(Path)s"), + GB_METHOD("Save", NULL, Image_Save, "(Path)s"), + + GB_END_DECLARE +}; diff --git a/gb.sdl2/src/c_image.h b/gb.sdl2/src/c_image.h new file mode 100644 index 00000000..1b198ec9 --- /dev/null +++ b/gb.sdl2/src/c_image.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + c_image.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_IMAGE_H +#define __C_IMAGE_H + +#include "main.h" +#include "c_window.h" + +typedef + struct { + SDL_Surface *surface; + SDL_Texture *texture; + CWINDOW *window; + } + SDL_Image; + +typedef + struct { + GB_IMG img; + } + CIMAGE; + +#ifndef __C_IMAGE_C +extern GB_DESC ImageDesc[]; +#endif + +CIMAGE *IMAGE_create(SDL_Image *image); +SDL_Texture *IMAGE_get_texture(CIMAGE *_object, CWINDOW *window); +CIMAGE *IMAGE_create_from_window(CWINDOW *window, int x, int y, int w, int h); + +SDL_Image *SDL_CreateImage(SDL_Surface *surface); +void SDL_FreeImage(SDL_Image *image); +SDL_Texture *SDL_GetTextureFromImage(SDL_Image *image, CWINDOW *window); + +#endif /* __C_IMAGE_H */ + diff --git a/gb.sdl2/src/c_key.c b/gb.sdl2/src/c_key.c new file mode 100644 index 00000000..7c230f84 --- /dev/null +++ b/gb.sdl2/src/c_key.c @@ -0,0 +1,256 @@ +/*************************************************************************** + + c_key.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_KEY_C + +#include "c_window.h" +#include "c_key.h" + +static SDL_Event *_current = NULL; +static bool _text_event = FALSE; + +//------------------------------------------------------------------------- + +static void update_event() +{ + if (!_current) + return; + + _text_event = _current->type == SDL_TEXTINPUT; +} + +SDL_Event *KEY_enter_event(SDL_Event *event) +{ + SDL_Event *old = _current; + _current = event; + update_event(); + return old; +} + +void KEY_leave_event(SDL_Event *event) +{ + _current = event; + update_event(); +} + +static bool check_event(void) +{ + if (!_current) + { + GB.Error("No keyboard event"); + return TRUE; + } + else + return FALSE; +} + +#define CHECK_EVENT() if (check_event()) return + +//------------------------------------------------------------------------- + +BEGIN_METHOD(Key_get, GB_STRING key) + + char *key = GB.ToZeroString(ARG(key)); + int code = 0; + + if (*key) + { + if (!key[1] && ((uchar)key[0] < 127)) + { + GB.ReturnInteger(key[0]); + return; + } + else + { + for (code = 127; code <= 255; code++) + { + if (!strcasecmp(SDL_GetKeyName((SDL_Keycode)code), key)) + { + GB.ReturnInteger(code); + return; + } + } + } + } + + GB.ReturnInteger(0); + +END_METHOD + +BEGIN_PROPERTY(Key_Code) + + CHECK_EVENT(); + GB.ReturnInteger(_text_event ? 0 : _current->key.keysym.sym); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Shift) + + CHECK_EVENT(); + GB.ReturnBoolean((_text_event ? SDL_GetModState() : _current->key.keysym.mod) & KMOD_SHIFT); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Control) + + CHECK_EVENT(); + GB.ReturnBoolean((_text_event ? SDL_GetModState() : _current->key.keysym.mod) & KMOD_CTRL); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Alt) + + CHECK_EVENT(); + GB.ReturnBoolean((_text_event ? SDL_GetModState() : _current->key.keysym.mod) & KMOD_ALT); + +END_PROPERTY + +BEGIN_PROPERTY(Key_AltGr) + + CHECK_EVENT(); + GB.ReturnBoolean((_text_event ? SDL_GetModState() : _current->key.keysym.mod) & KMOD_MODE); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Meta) + + CHECK_EVENT(); + GB.ReturnBoolean((_text_event ? SDL_GetModState() : _current->key.keysym.mod) & KMOD_GUI); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Normal) + + CHECK_EVENT(); + GB.ReturnBoolean(((_text_event ? SDL_GetModState() : _current->key.keysym.mod) & (KMOD_CTRL | KMOD_ALT | KMOD_MODE | KMOD_GUI)) == 0); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Text) + + CHECK_EVENT(); + if (!_text_event) + GB.ReturnNull(); + else + GB.ReturnNewZeroString(_current->text.text); + +END_PROPERTY + +BEGIN_PROPERTY(Key_Repeat) + + CHECK_EVENT(); + GB.ReturnBoolean(_text_event ? FALSE : _current->key.repeat); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC KeyDesc[] = +{ + GB_DECLARE_STATIC("Key"), + + GB_STATIC_METHOD("_get", "i", Key_get, "(Key)s"), + + GB_STATIC_PROPERTY_READ("Code", "i", Key_Code), + GB_STATIC_PROPERTY_READ("Shift", "b", Key_Shift), + GB_STATIC_PROPERTY_READ("Control", "b", Key_Control), + GB_STATIC_PROPERTY_READ("Alt", "b", Key_Alt), + GB_STATIC_PROPERTY_READ("AltGr", "b", Key_AltGr), + GB_STATIC_PROPERTY_READ("Meta", "b", Key_Meta), + GB_STATIC_PROPERTY_READ("Normal", "b", Key_Normal), + GB_STATIC_PROPERTY_READ("Text", "s", Key_Text), + GB_STATIC_PROPERTY("Repeat", "b", Key_Repeat), + + GB_CONSTANT("Backspace", "i", SDLK_BACKSPACE), + GB_CONSTANT("Tab", "i", SDLK_TAB), + GB_CONSTANT("Return", "i", SDLK_RETURN), + GB_CONSTANT("Pause", "i", SDLK_PAUSE), + GB_CONSTANT("Escape", "i", SDLK_ESCAPE), + GB_CONSTANT("Esc", "i", SDLK_ESCAPE), + GB_CONSTANT("Space", "i", SDLK_SPACE), + GB_CONSTANT("Delete", "i", SDLK_DELETE), + GB_CONSTANT("Num0", "i", SDLK_KP_0), + GB_CONSTANT("Num1", "i", SDLK_KP_1), + GB_CONSTANT("Num2", "i", SDLK_KP_2), + GB_CONSTANT("Num3", "i", SDLK_KP_3), + GB_CONSTANT("Num4", "i", SDLK_KP_4), + GB_CONSTANT("Num5", "i", SDLK_KP_5), + GB_CONSTANT("Num6", "i", SDLK_KP_6), + GB_CONSTANT("Num7", "i", SDLK_KP_7), + GB_CONSTANT("Num8", "i", SDLK_KP_8), + GB_CONSTANT("Num9", "i", SDLK_KP_9), + GB_CONSTANT("NumPeriod", "i", SDLK_KP_PERIOD), + GB_CONSTANT("NumDivide", "i", SDLK_KP_DIVIDE), + GB_CONSTANT("NumMultiply", "i", SDLK_KP_MULTIPLY), + GB_CONSTANT("NumMinus", "i", SDLK_KP_MINUS), + GB_CONSTANT("NumPlus", "i", SDLK_KP_PLUS), + GB_CONSTANT("NumEnter", "i", SDLK_KP_ENTER), + //GB_CONSTANT("NumEquals", "i", SDLK_KP_EQUALS), + GB_CONSTANT("Up", "i", SDLK_UP), + GB_CONSTANT("Down", "i", SDLK_DOWN), + GB_CONSTANT("Right", "i", SDLK_RIGHT), + GB_CONSTANT("Left", "i", SDLK_LEFT), + GB_CONSTANT("Insert", "i", SDLK_INSERT), + GB_CONSTANT("Home", "i", SDLK_HOME), + GB_CONSTANT("End", "i", SDLK_END), + GB_CONSTANT("PageUp", "i", SDLK_PAGEUP), + GB_CONSTANT("PageDown", "i", SDLK_PAGEDOWN), + GB_CONSTANT("F1", "i", SDLK_F1), + GB_CONSTANT("F2", "i", SDLK_F2), + GB_CONSTANT("F3", "i", SDLK_F3), + GB_CONSTANT("F4", "i", SDLK_F4), + GB_CONSTANT("F5", "i", SDLK_F5), + GB_CONSTANT("F6", "i", SDLK_F6), + GB_CONSTANT("F7", "i", SDLK_F7), + GB_CONSTANT("F8", "i", SDLK_F8), + GB_CONSTANT("F9", "i", SDLK_F9), + GB_CONSTANT("F10", "i", SDLK_F10), + GB_CONSTANT("F11", "i", SDLK_F11), + GB_CONSTANT("F12", "i", SDLK_F12), + GB_CONSTANT("F13", "i", SDLK_F13), + GB_CONSTANT("F14", "i", SDLK_F14), + GB_CONSTANT("F15", "i", SDLK_F15), + GB_CONSTANT("NumLock", "i", SDLK_NUMLOCKCLEAR), + GB_CONSTANT("CapsLock", "i", SDLK_CAPSLOCK), + GB_CONSTANT("ScrollLock", "i", SDLK_SCROLLLOCK), + GB_CONSTANT("RightShift", "i", SDLK_RSHIFT), + GB_CONSTANT("LeftShift", "i", SDLK_LSHIFT), + GB_CONSTANT("RightControl", "i", SDLK_RCTRL), + GB_CONSTANT("LeftControl", "i", SDLK_LCTRL), + GB_CONSTANT("RightAlt", "i", SDLK_RALT), + GB_CONSTANT("LeftAlt", "i", SDLK_LALT), + GB_CONSTANT("RightMeta", "i", SDLK_RGUI), + GB_CONSTANT("LeftMeta", "i", SDLK_LGUI), + GB_CONSTANT("AltGrKey", "i", SDLK_MODE), + //GB_CONSTANT("Compose", "i", SDLK_COMPOSE), + //GB_CONSTANT("Help", "i", SDLK_HELP), + //GB_CONSTANT("Print", "i", SDLK_PRINT), + GB_CONSTANT("SysReq", "i", SDLK_SYSREQ), + //GB_CONSTANT("Break", "i", SDLK_BREAK), + GB_CONSTANT("Menu", "i", SDLK_MENU), + //GB_CONSTANT("Power", "i", SDLK_POWER), + //GB_CONSTANT("Euro", "i", SDLK_EURO), + //GB_CONSTANT("Undo", "i", SDLK_UNDO), + + GB_END_DECLARE +}; diff --git a/gb.sdl2/src/c_key.h b/gb.sdl2/src/c_key.h new file mode 100644 index 00000000..888d3f17 --- /dev/null +++ b/gb.sdl2/src/c_key.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + c_key.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_KEY_H +#define __C_KEY_H + +#include "main.h" + +#ifndef __C_KEY_C +extern GB_DESC KeyDesc[]; +#endif + +SDL_Event *KEY_enter_event(SDL_Event *event); +void KEY_leave_event(SDL_Event *event); + +#endif /* __C_KEY_H */ + diff --git a/gb.sdl2/src/c_mouse.c b/gb.sdl2/src/c_mouse.c new file mode 100644 index 00000000..23212bd6 --- /dev/null +++ b/gb.sdl2/src/c_mouse.c @@ -0,0 +1,276 @@ +/*************************************************************************** + + c_mouse.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_MOUSE_C + +#include "c_window.h" +#include "c_mouse.h" + +static SDL_Event *_current = NULL; +static MOUSE_INFO *_info = NULL; + +//------------------------------------------------------------------------- + +static void update_event() +{ + MOUSE_INFO *info; + CWINDOW *window; + + _info = NULL; + + if (!_current) + return; + + window = WINDOW_get_from_event(_current); + if (!window) + return; + + _info = info = &window->mouse; + + switch (_current->type) + { + case SDL_MOUSEMOTION: + info->x = _current->motion.x; + info->y = _current->motion.y; + info->wheel_x = 0; + info->wheel_y = 0; + info->state = _current->motion.state; + info->button = 0; + break; + + case SDL_MOUSEWHEEL: + info->wheel_x = _current->wheel.x; + info->wheel_y = _current->wheel.y; + info->state = SDL_GetMouseState(&info->x, &info->y); + info->button = 0; +#if SDL_VERSION_ATLEAST(2,0,4) + if (_current->wheel.direction == SDL_MOUSEWHEEL_FLIPPED) + { + info->wheel_x = (- info->wheel_x); + info->wheel_y = (- info->wheel_y); + } +#endif + break; + + case SDL_MOUSEBUTTONDOWN: + info->x = _current->button.x; + info->y = _current->button.y; + info->wheel_x = 0; + info->wheel_y = 0; + info->state = SDL_GetMouseState(NULL, NULL); + info->button = _current->button.button; + info->start_x = info->x; + info->start_y = info->y; + break; + + case SDL_MOUSEBUTTONUP: + info->x = _current->button.x; + info->y = _current->button.y; + info->wheel_x = 0; + info->wheel_y = 0; + info->state = SDL_GetMouseState(NULL, NULL); + info->button = _current->button.button; + break; + } +} + +SDL_Event *MOUSE_enter_event(SDL_Event *event) +{ + SDL_Event *old = _current; + _current = event; + update_event(); + return old; +} + +void MOUSE_leave_event(SDL_Event *event) +{ + _current = event; + update_event(); +} + +static bool check_event(void) +{ + if (!_info) + { + GB.Error("No mouse event"); + return TRUE; + } + else + return FALSE; +} + +#define CHECK_EVENT() if (check_event()) return + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Mouse_X) + + CHECK_EVENT(); + GB.ReturnInteger(_info->x); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Y) + + CHECK_EVENT(); + GB.ReturnInteger(_info->y); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_WheelX) + + CHECK_EVENT(); + GB.ReturnInteger(_info->wheel_x); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_WheelY) + + CHECK_EVENT(); + GB.ReturnInteger(_info->wheel_y); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_StartX) + + GB.ReturnInteger(_info->start_x); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_StartY) + + GB.ReturnInteger(_info->start_y); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Left) + + CHECK_EVENT(); + GB.ReturnBoolean(_info->button ? _info->button == SDL_BUTTON_LEFT : _info->state & SDL_BUTTON_LMASK); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Middle) + + CHECK_EVENT(); + GB.ReturnBoolean(_info->button ? _info->button == SDL_BUTTON_MIDDLE : _info->state & SDL_BUTTON_MMASK); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Right) + + CHECK_EVENT(); + GB.ReturnBoolean(_info->button ? _info->button == SDL_BUTTON_RIGHT : _info->state & SDL_BUTTON_RMASK); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Button) + + CHECK_EVENT(); + GB.ReturnBoolean(_info->state); + +END_PROPERTY + +#if 0 +BEGIN_METHOD(Mouse_Move, GB_INTEGER x; GB_INTEGER y; GB_OBJECT window) + + CWINDOW *window = (CWINDOW *)VARGOPT(window, NULL); + + if (!window) + { +#if SDL_VERSION_ATLEAST(2,0,4) + SDL_WarpMouseGlobal(VARG(x), VARG(y)); +#else + fprintf(stderr, "gb.sdl2: warning: global mouse warp is not supported.\n"); +#endif + } + else + SDL_WarpMouseInWindow(window->window, VARG(x), VARG(y)); + +END_METHOD +#endif + +BEGIN_METHOD_VOID(Mouse_Show) + + SDL_ShowCursor(SDL_ENABLE); + +END_METHOD + +BEGIN_METHOD_VOID(Mouse_Hide) + + SDL_ShowCursor(SDL_DISABLE); + +END_METHOD + +BEGIN_PROPERTY(Mouse_Visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE); + else + SDL_ShowCursor(VPROP(GB_BOOLEAN) ? SDL_ENABLE : SDL_DISABLE); + +END_PROPERTY + +BEGIN_PROPERTY(Mouse_Relative) + + if (READ_PROPERTY) + GB.ReturnBoolean(SDL_GetRelativeMouseMode()); + else + SDL_SetRelativeMouseMode(VPROP(GB_BOOLEAN)); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC MouseDesc[] = +{ + GB_DECLARE_STATIC("Mouse"), + + //GB_STATIC_METHOD("Move", NULL, Mouse_Move, "(X)i(Y)i[(Window)Window]"), + + GB_STATIC_METHOD("Show", NULL, Mouse_Show, NULL), + GB_STATIC_METHOD("Hide", NULL, Mouse_Hide, NULL), + GB_STATIC_PROPERTY("Visible", "b", Mouse_Visible), + GB_STATIC_PROPERTY("Relative", "b", Mouse_Relative), + + //GB_STATIC_PROPERTY_READ("ScreenX", "i", Mouse_ScreenX), + //GB_STATIC_PROPERTY_READ("ScreenY", "i", Mouse_ScreenY), + GB_STATIC_PROPERTY_READ("StartX", "i", Mouse_StartX), + GB_STATIC_PROPERTY_READ("StartY", "i", Mouse_StartY), + GB_STATIC_PROPERTY_READ("X", "i", Mouse_X), + GB_STATIC_PROPERTY_READ("Y", "i", Mouse_Y), + GB_STATIC_PROPERTY_READ("WheelX", "i", Mouse_WheelX), + GB_STATIC_PROPERTY_READ("WheelY", "i", Mouse_WheelY), + + GB_STATIC_PROPERTY_READ("Left", "b", Mouse_Left), + GB_STATIC_PROPERTY_READ("Right", "b", Mouse_Right), + GB_STATIC_PROPERTY_READ("Middle", "b", Mouse_Middle), + GB_STATIC_PROPERTY_READ("Button", "i", Mouse_Button), + //GB_STATIC_PROPERTY_READ("Shift", "b", Mouse_Shift), + //GB_STATIC_PROPERTY_READ("Control", "b", Mouse_Control), + //GB_STATIC_PROPERTY_READ("Alt", "b", Mouse_Alt), + //GB_STATIC_PROPERTY_READ("Meta", "b", Mouse_Meta), + //GB_STATIC_PROPERTY_READ("Normal", "b", Mouse_Normal), + + GB_END_DECLARE +}; diff --git a/gb.sdl2/src/c_mouse.h b/gb.sdl2/src/c_mouse.h new file mode 100644 index 00000000..29e07a6c --- /dev/null +++ b/gb.sdl2/src/c_mouse.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + c_mouse.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_MOUSE_H +#define __C_MOUSE_H + +#include "main.h" + +#ifndef __C_MOUSE_C +extern GB_DESC MouseDesc[]; +#endif + +typedef + struct { + int x; + int y; + int wheel_x; + int wheel_y; + int state; + int button; + int start_x; + int start_y; + } + MOUSE_INFO; + +SDL_Event *MOUSE_enter_event(SDL_Event *event); +void MOUSE_leave_event(SDL_Event *event); + +#endif /* __C_MOUSE_H */ + diff --git a/gb.sdl2/src/c_window.c b/gb.sdl2/src/c_window.c new file mode 100644 index 00000000..fd16e804 --- /dev/null +++ b/gb.sdl2/src/c_window.c @@ -0,0 +1,670 @@ +/*************************************************************************** + + c_window.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_WINDOW_C + +#include "c_draw.h" +#include "c_image.h" +#include "c_window.h" + +#define THIS ((CWINDOW *)_object) +#define WINDOW THIS->window + +DECLARE_EVENT(EVENT_Open); +DECLARE_EVENT(EVENT_Close); +DECLARE_EVENT(EVENT_Show); +DECLARE_EVENT(EVENT_Hide); +DECLARE_EVENT(EVENT_Move); +DECLARE_EVENT(EVENT_Resize); +DECLARE_EVENT(EVENT_Enter); +DECLARE_EVENT(EVENT_Leave); +DECLARE_EVENT(EVENT_GotFocus); +DECLARE_EVENT(EVENT_LostFocus); +DECLARE_EVENT(EVENT_Draw); +DECLARE_EVENT(EVENT_KeyPress); +DECLARE_EVENT(EVENT_KeyRelease); +DECLARE_EVENT(EVENT_Text); +DECLARE_EVENT(EVENT_MouseMove); +DECLARE_EVENT(EVENT_MouseDown); +DECLARE_EVENT(EVENT_MouseUp); +DECLARE_EVENT(EVENT_MouseWheel); + +CWINDOW *WINDOW_list = NULL; +static int _id = 0; + +static void update_geometry(void *_object) +{ + if (!THIS->opened) + return; + + if (THIS->fullscreen) + { + SDL_SetWindowFullscreen(WINDOW, SDL_WINDOW_FULLSCREEN_DESKTOP); + if (!THIS->opengl) + SDL_RenderSetLogicalSize(THIS->renderer, THIS->width, THIS->height); + THIS->clear = TRUE; + } + else + { + SDL_SetWindowFullscreen(WINDOW, 0); + SDL_SetWindowPosition(WINDOW, THIS->x, THIS->y); + + if (!THIS->resizable) + { + SDL_SetWindowMinimumSize(WINDOW, THIS->width, THIS->height); + SDL_SetWindowMaximumSize(WINDOW, THIS->width, THIS->height); + } + SDL_SetWindowSize(WINDOW, THIS->width, THIS->height); + + GB.Raise(THIS, EVENT_Resize, 0); + } +} + +static void open_window(void *_object) +{ + if (THIS->opened) + return; + + if (GB.Raise(THIS, EVENT_Open, 0)) + return; + + THIS->opened = TRUE; + GB.Ref(THIS); + LIST_insert(&WINDOW_list, THIS, &THIS->list); + + SDL_ShowWindow(WINDOW); + update_geometry(THIS); + + /*if (!THIS->opengl) + { + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glViewport(0, 0, this->GetWidth(), this->GetHeight()); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0f, GLdouble(this->GetWidth()), GLdouble(this->GetHeight()), 0.0f, -1, 1); + glMatrixMode(GL_MODELVIEW); + }*/ + + /*if (THIS->opengl) + { + if (GB.CanRaise(hWindow, EVENT_Resize)) + GB.Raise(hWindow, EVENT_Resize,0); + }*/ +} + + +static void close_window(void *_object) +{ + if (!THIS->opened) + return; + + if (GB.Raise(THIS, EVENT_Close, 0)) + return; + + LIST_remove(&WINDOW_list, THIS, &THIS->list); + SDL_HideWindow(WINDOW); + THIS->opened = FALSE; + GB.Unref(POINTER(&_object)); +} + +static void raise_mouse_event(CWINDOW *_object, SDL_Event *event, int type) +{ + SDL_Event *old; + + if (!GB.CanRaise(THIS, type)) + return; + + old = MOUSE_enter_event(event); + GB.Raise(THIS, type, 0); + MOUSE_leave_event(old); +} + +static void raise_keyboard_event(CWINDOW *_object, SDL_Event *event, int type) +{ + SDL_Event *old; + + if (!GB.CanRaise(THIS, type)) + return; + + old = KEY_enter_event(event); + GB.Raise(THIS, type, 0); + KEY_leave_event(old); +} + +CWINDOW *WINDOW_get_from_event(SDL_Event *event) +{ + SDL_Window *window = SDL_GetWindowFromID(event->window.windowID); + return SDL_GetWindowData(window, "gambas-object"); +} + +void WINDOW_handle_event(SDL_Event *event) +{ + CWINDOW *_object; + + _object = WINDOW_get_from_event(event); + if (!THIS) + return; + + switch(event->type) + { + case SDL_WINDOWEVENT: + switch(event->window.event) + { + case SDL_WINDOWEVENT_SHOWN: + GB.Raise(THIS, EVENT_Show, 0); + break; + case SDL_WINDOWEVENT_HIDDEN: + GB.Raise(THIS, EVENT_Hide, 0); + break; + /*case SDL_WINDOWEVENT_EXPOSED:*/ + case SDL_WINDOWEVENT_MOVED: + THIS->x = event->window.data1; + THIS->y = event->window.data2; + GB.Raise(THIS, EVENT_Move, 0); + break; + case SDL_WINDOWEVENT_RESIZED: + THIS->width = event->window.data1; + THIS->height = event->window.data2; + GB.Raise(THIS, EVENT_Resize, 0); + break; + /*case SDL_WINDOWEVENT_MINIMIZED: + SDL_Log("Window %d minimized", event->window.windowID); + break; + case SDL_WINDOWEVENT_MAXIMIZED: + SDL_Log("Window %d maximized", event->window.windowID); + break; + case SDL_WINDOWEVENT_RESTORED: + SDL_Log("Window %d restored", event->window.windowID); + break;*/ + case SDL_WINDOWEVENT_ENTER: + GB.Raise(THIS, EVENT_Enter, 0); + break; + case SDL_WINDOWEVENT_LEAVE: + GB.Raise(THIS, EVENT_Leave, 0); + break; + case SDL_WINDOWEVENT_FOCUS_GAINED: + GB.Raise(THIS, EVENT_GotFocus, 0); + break; + case SDL_WINDOWEVENT_FOCUS_LOST: + GB.Raise(THIS, EVENT_LostFocus, 0); + break; + case SDL_WINDOWEVENT_CLOSE: + close_window(THIS); + break; + } + break; + + case SDL_MOUSEBUTTONDOWN: + raise_mouse_event(THIS, event, EVENT_MouseDown); + break; + case SDL_MOUSEBUTTONUP: + raise_mouse_event(THIS, event, EVENT_MouseUp); + break; + case SDL_MOUSEMOTION: + raise_mouse_event(THIS, event, EVENT_MouseMove); + break; + case SDL_MOUSEWHEEL: + raise_mouse_event(THIS, event, EVENT_MouseWheel); + break; + case SDL_KEYDOWN: + raise_keyboard_event(THIS, event, EVENT_KeyPress); + break; + case SDL_KEYUP: + raise_keyboard_event(THIS, event, EVENT_KeyRelease); + break; + case SDL_TEXTINPUT: + raise_keyboard_event(THIS, event, EVENT_Text); + break; + } +} + +void WINDOW_update(void) +{ + CWINDOW *_object; + uint current_time; + uint diff; + bool at_least_one = FALSE; + + current_time = SDL_GetTicks(); + + LIST_for_each(_object, WINDOW_list) + { + if (!GB.CanRaise(THIS, EVENT_Draw)) + continue; + + if (THIS->frame_time > 0) + { + double d = THIS->last_time + THIS->frame_time; + if (d > current_time) + continue; + THIS->last_time = d; + } + + if (THIS->clear) + { + if (!THIS->opengl) + { + SDL_SetRenderDrawColor(THIS->renderer, 0, 0, 0, 255); + SDL_RenderClear(THIS->renderer); + } + THIS->clear = FALSE; + } + + DRAW_begin(THIS); + GB.Raise(THIS, EVENT_Draw, 0); + DRAW_end(); + + if (THIS->opengl) + SDL_GL_SwapWindow(WINDOW); + else + SDL_RenderPresent(THIS->renderer); + + THIS->frame_count++; + THIS->total_frame_count++; + + if (THIS->start_time == 0) + THIS->start_time = current_time; + else + { + diff = current_time - THIS->start_time; + if (diff > 1000) + { + THIS->frame_rate = THIS->frame_count; + THIS->frame_count = 0; + THIS->start_time += 1000; + } + } + + at_least_one = TRUE; + } + + if (!at_least_one) + SDL_Delay(1); +} + +static void init_opengl(void) +{ + static bool _init = FALSE; + + if (_init) + return; + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + //SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + + _init = TRUE; +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD(Window_new, GB_BOOLEAN opengl) + + int flag; + + _id++; + THIS->id = _id; + + THIS->opengl = VARGOPT(opengl, FALSE); + THIS->fullscreen = FALSE; + THIS->width = 640; + THIS->height = 400; + + flag = SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE; + + if (THIS->opengl) + { + init_opengl(); + flag |= SDL_WINDOW_OPENGL; + } + + THIS->window = SDL_CreateWindow(GB.Application.Title(), 0, 0, THIS->width, THIS->height, flag); + if (!WINDOW) + { + RAISE_ERROR("Unable to create window"); + return; + } + + if (THIS->opengl) + { + THIS->context = SDL_GL_CreateContext(WINDOW); + if (!THIS->context) + { + RAISE_ERROR("Unable to create OpenGL context"); + return; + } + } + else + { + THIS->renderer = SDL_CreateRenderer(WINDOW, -1, SDL_RENDERER_ACCELERATED); + if (!THIS->renderer) + { + RAISE_ERROR("Unable to create renderer"); + return; + } + } + + SDL_SetWindowData(WINDOW, "gambas-object", THIS); + + SDL_SetWindowMinimumSize(WINDOW, THIS->width, THIS->height); + SDL_SetWindowMaximumSize(WINDOW, THIS->width, THIS->height); + +END_METHOD + +BEGIN_METHOD_VOID(Window_free) + + if (THIS->context) + SDL_GL_DeleteContext(THIS->context); + if (THIS->renderer) + SDL_DestroyRenderer(THIS->renderer); + SDL_DestroyWindow(WINDOW); + +END_METHOD + +BEGIN_METHOD_VOID(Window_Show) + + open_window(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Window_Hide) + + SDL_HideWindow(WINDOW); + +END_METHOD + +BEGIN_METHOD_VOID(Window_Close) + + close_window(THIS); + +END_METHOD + +BEGIN_PROPERTY(Window_Visible) + + if (READ_PROPERTY) + GB.ReturnBoolean(SDL_GetWindowFlags(WINDOW) & SDL_WINDOW_SHOWN); + else + { + if (VPROP(GB_BOOLEAN)) + open_window(THIS); + else + SDL_HideWindow(WINDOW); + } + +END_PROPERTY + +BEGIN_METHOD(Window_Move, GB_INTEGER x; GB_INTEGER y; GB_INTEGER width; GB_INTEGER height) + + int w = VARGOPT(width, -1); + int h = VARGOPT(height, -1); + + THIS->x = VARG(x); + THIS->y = VARG(y); + if (w > 0) THIS->width = w; + if (h > 0) THIS->height = h; + + update_geometry(THIS); + +END_METHOD + +BEGIN_METHOD(Window_Resize, GB_INTEGER width; GB_INTEGER height) + + int w = VARG(width); + int h = VARG(height); + + if (w > 0) THIS->width = w; + if (h > 0) THIS->height = h; + + update_geometry(THIS); + +END_METHOD + +BEGIN_PROPERTY(Window_X) + + GB.ReturnInteger(THIS->x); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Y) + + GB.ReturnInteger(THIS->y); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Width) + + GB.ReturnInteger(THIS->width); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Height) + + GB.ReturnInteger(THIS->height); + +END_PROPERTY + +BEGIN_PROPERTY(Window_FullScreen) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->fullscreen); + else + { + bool f = VPROP(GB_BOOLEAN); + + if (THIS->fullscreen != f) + { + THIS->fullscreen = f; + if (f) + { + THIS->save_x = THIS->x; + THIS->save_y = THIS->y; + THIS->save_width = THIS->width; + THIS->save_height = THIS->height; + } + else + { + THIS->x = THIS->save_x; + THIS->y = THIS->save_y; + THIS->width = THIS->save_width; + THIS->height = THIS->save_height; + } + update_geometry(THIS); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(Window_FrameRate) + + if (READ_PROPERTY) + GB.ReturnFloat(THIS->frame_rate); + else + { + double val = VPROP(GB_FLOAT); + + if (val < 0) + return; + + THIS->frame_time = val ? 1000.0 / val : 0; + THIS->last_time = SDL_GetTicks(); + } + +END_PROPERTY + +BEGIN_PROPERTY(Window_FrameCount) + + GB.ReturnInteger(THIS->total_frame_count); + +END_PROPERTY + +BEGIN_PROPERTY(Window_Text) + + if (READ_PROPERTY) + GB.ReturnNewZeroString(SDL_GetWindowTitle(WINDOW)); + else + SDL_SetWindowTitle(WINDOW, GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +BEGIN_METHOD(Window_Screenshot, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + GB.ReturnObject(IMAGE_create_from_window(THIS, VARGOPT(x, 0), VARGOPT(y, 0), VARGOPT(w, THIS->width), VARGOPT(h, THIS->height))); + +END_METHOD + +BEGIN_PROPERTY(Window_Resizable) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->resizable); + else + { + bool v = VPROP(GB_BOOLEAN); + if (v == THIS->resizable) + return; + THIS->resizable = v; + + if (v) + { + SDL_SetWindowMinimumSize(WINDOW, 1, 1); + SDL_SetWindowMaximumSize(WINDOW, 2048, 2048); + } + else + { + SDL_SetWindowMinimumSize(WINDOW, THIS->width, THIS->height); + SDL_SetWindowMaximumSize(WINDOW, THIS->width, THIS->height); + } + } + +END_PROPERTY + +BEGIN_PROPERTY(Window_Handle) + + GB.ReturnInteger(SDL_GetWindowID(WINDOW)); + +END_PROPERTY + +// Does not work +#if 0 +BEGIN_PROPERTY(Window_Border) + + if (READ_PROPERTY) + GB.ReturnBoolean((SDL_GetWindowFlags(WINDOW) & SDL_WINDOW_BORDERLESS) == 0); + else + { + SDL_SetWindowBordered(WINDOW, VPROP(GB_BOOLEAN)); + SDL_ShowWindow(WINDOW); + } + +END_PROPERTY +#endif + +BEGIN_PROPERTY(Window_Grabbed) + + if (READ_PROPERTY) + GB.ReturnBoolean(SDL_GetWindowGrab(WINDOW)); + else + SDL_SetWindowGrab(WINDOW, VPROP(GB_BOOLEAN)); + +END_PROPERTY + +//------------------------------------------------------------------------- + +GB_DESC WindowDesc[] = +{ + GB_DECLARE("Window", sizeof(CWINDOW)), + + GB_METHOD("_new", NULL, Window_new, "[(OpenGL)b]"), + GB_METHOD("_free", NULL, Window_free, NULL), + + GB_METHOD("Show", NULL, Window_Show, NULL), + GB_METHOD("Hide", NULL, Window_Hide, NULL), + GB_METHOD("Close", NULL, Window_Close, NULL), + GB_METHOD("Move", NULL, Window_Move, "(X)i(Y)i[(Width)i(Height)i]"), + GB_METHOD("Resize", NULL, Window_Resize, "(Width)i(Height)i"), + GB_METHOD("Screenshot", "Image", Window_Screenshot, "[(X)i(Y)i(Width)i(Height)i]"), + + GB_PROPERTY("Visible", "b", Window_Visible), + GB_PROPERTY("FullScreen", "b", Window_FullScreen), + //GB_PROPERTY("Border", "b", Window_Border), + GB_PROPERTY("Resizable", "b", Window_Resizable), + + GB_PROPERTY_READ("X", "i", Window_X), + GB_PROPERTY_READ("Y", "i", Window_Y), + GB_PROPERTY_READ("W", "i", Window_Width), + GB_PROPERTY_READ("H", "i", Window_Height), + GB_PROPERTY_READ("Width", "i", Window_Width), + GB_PROPERTY_READ("Height", "i", Window_Height), + + GB_PROPERTY_READ("Handle", "i", Window_Handle), + + GB_PROPERTY("FrameRate", "f", Window_FrameRate), + GB_PROPERTY_READ("FrameCount", "i", Window_FrameCount), + + GB_PROPERTY("Text", "s", Window_Text), + GB_PROPERTY("Title", "s", Window_Text), + + GB_PROPERTY("Grabbed", "b", Window_Grabbed), + + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + GB_EVENT("Close", NULL, NULL, &EVENT_Close), + GB_EVENT("Show", NULL, NULL, &EVENT_Show), + GB_EVENT("Hide", NULL, NULL, &EVENT_Hide), + GB_EVENT("Move", NULL, NULL, &EVENT_Move), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + GB_EVENT("Enter", NULL, NULL, &EVENT_Enter), + GB_EVENT("Leave", NULL, NULL, &EVENT_Leave), + GB_EVENT("GotFocus", NULL, NULL, &EVENT_GotFocus), + GB_EVENT("LostFocus", NULL, NULL, &EVENT_LostFocus), + GB_EVENT("Draw", NULL, NULL, &EVENT_Draw), + GB_EVENT("KeyPress", NULL, NULL, &EVENT_KeyPress), + GB_EVENT("KeyRelease", NULL, NULL, &EVENT_KeyRelease), + GB_EVENT("Text", NULL, NULL, &EVENT_Text), + GB_EVENT("MouseMove", NULL, NULL, &EVENT_MouseMove), + GB_EVENT("MouseDown", NULL, NULL, &EVENT_MouseDown), + GB_EVENT("MouseUp", NULL, NULL, &EVENT_MouseUp), + GB_EVENT("MouseWheel", NULL, NULL, &EVENT_MouseWheel), + +/* + GB_METHOD("Clear", NULL, CWINDOW_clear, NULL), + GB_METHOD("Fill", NULL, CWINDOW_fill, "(Color)i"), + GB_METHOD("Refresh", NULL, CWINDOW_refresh, NULL), + GB_METHOD("Update", NULL, CWINDOW_refresh, NULL), + + GB_PROPERTY("Tracking", "b", CWINDOW_tracking), + GB_PROPERTY("Resizable", "b", CWINDOW_resizable), + + GB_PROPERTY_READ("Handle", "i", CWINDOW_id), + GB_PROPERTY_READ("Id", "i", CWINDOW_id), + + GB_EVENT("Activate", NULL, NULL, &EVENT_Activate), + GB_EVENT("Deactivate", NULL, NULL, &EVENT_Deactivate), + GB_EVENT("JoyAxisMove", NULL, NULL, &EVENT_JoyAxisMotion), + GB_EVENT("JoyBallMove", NULL, NULL, &EVENT_JoyBallMotion), + GB_EVENT("JoyButtonPress", NULL, NULL, &EVENT_JoyButtonPressed), + GB_EVENT("JoyButtonRelease", NULL, NULL, &EVENT_JoyButtonReleased), + GB_EVENT("JoyHatMove", NULL, NULL, &EVENT_JoyHatMotion), +*/ + + GB_END_DECLARE +}; diff --git a/gb.sdl2/src/c_window.h b/gb.sdl2/src/c_window.h new file mode 100644 index 00000000..e4581b00 --- /dev/null +++ b/gb.sdl2/src/c_window.h @@ -0,0 +1,72 @@ +/*************************************************************************** + + c_window.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_WINDOW_H +#define __C_WINDOW_H + +#include "main.h" +#include "c_mouse.h" +#include "c_key.h" + +typedef + struct CWINDOW { + GB_BASE ob; + LIST list; + SDL_Window *window; + SDL_Renderer *renderer; + SDL_GLContext context; + int id; + int x; + int y; + int width; + int height; + int save_x, save_y, save_width, save_height; + uint start_time; + uint frame_count; + uint total_frame_count; + double last_time; + double frame_time; + double frame_rate; + MOUSE_INFO mouse; + unsigned opengl : 1; + unsigned opened : 1; + unsigned fullscreen : 1; + unsigned clear : 1; + unsigned resizable : 1; + } + CWINDOW; + +#ifndef __C_WINDOW_C + +extern GB_DESC WindowDesc[]; + +extern CWINDOW *WINDOW_list; + +CWINDOW *WINDOW_get_from_event(SDL_Event *event); +void WINDOW_handle_event(SDL_Event *event); +void WINDOW_update(); + +#endif + +#endif /* __C_WINDOW_H */ + diff --git a/gb.sdl2/src/default_font.c b/gb.sdl2/src/default_font.c new file mode 100644 index 00000000..8d82c105 --- /dev/null +++ b/gb.sdl2/src/default_font.c @@ -0,0 +1,200 @@ +/*************************************************************************** + + default_font.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __DEFAULT_FONT_C + +#include "default_font.h" +#include "default_font_data.h" + +#define UNICODE_INVALID 0xFFFFFFFFU + +static const char _char_length[256] = +{ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 +}; + +#define utf8_get_char_length(_c) ((int)_char_length[(unsigned char)(_c)]) + +int UTF8_get_length(const char *sstr, int len) +{ + const uchar *str = (const uchar *)sstr; + int ulen; + int i; + + ulen = 0; + + for (i = 0; i < len; i++) + { + if ((str[i] & 0xC0) != 0x80) + ulen++; + } + + return ulen; +} + +static uint utf8_to_unicode(const char *sstr, int len) +{ + const uchar *str = (const uchar *)sstr; + uint unicode; + + switch (len) + { + case 2: + unicode = (str[1] & 0x3F) + ((str[0] & 0x1F) << 6); + if (unicode < 0x80) + goto _INVALID; + break; + + case 3: + unicode = (str[2] & 0x3F) + ((str[1] & 0x3F) << 6) + ((str[0] & 0xF) << 12); + if (unicode < 0x800) + goto _INVALID; + break; + + case 4: + unicode = (str[3] & 0x3F) + ((str[2] & 0x3F) << 6) + ((str[1] & 0x3F) << 12) + ((str[0] & 0x7) << 18); + if (unicode < 0x10000) + goto _INVALID; + break; + + case 5: + unicode = (str[4] & 0x3F) + ((str[3] & 0x3F) << 6) + ((str[2] & 0x3F) << 12) + ((str[1] & 0x3F) << 18) + ((str[0] & 0x3) << 24); + if (unicode < 0x200000) + goto _INVALID; + break; + + case 6: + unicode = (str[5] & 0x3F) + ((str[4] & 0x3F) << 6) + ((str[3] & 0x3F) << 12) + ((str[2] & 0x3F) << 18) + ((str[1] & 0x3F) << 24) + ((str[0] & 0x1) << 30); + if (unicode < 0x4000000) + goto _INVALID; + break; + + default: + unicode = str[0]; + break; + } + + return unicode; + +_INVALID: + + return UNICODE_INVALID; +} + + +void FONT_render_default(uint *dest, int size, const char *text, int len) +{ + static void *jump[] = { &&__0, &&__1, &&__2, &&__3, &&__4, &&__5, &&__6, &&__7, &&__8, &&__9, &&__A, &&__B, &&__C, &&__D, &&__E, &&__F }; + static void *jump2[] = { &&__00, &&__10, &&__20, &&__30, &&__40, &&__50, &&__60, &&__70, &&__80, &&__90, &&__A0, &&__B0, &&__C0, &&__D0, &&__E0, &&__F0 }; + + int lc; + const char *src; + uint *p; + int y; + uchar line; + uint code; + const uint col = 0xFFFFFFFF; + + size *= DEFAULT_FONT_WIDTH; + + for(;;) + { + if (!*text) + break; + + lc = utf8_get_char_length(*text); + code = utf8_to_unicode(text, lc); + text += lc; + + if (code >= 33 && code <= 126) + src = _default_font_33_126 + DEFAULT_FONT_HEIGHT * (code - 33); + else if (code >= 160 && code <= 687) + src = _default_font_160_687 + DEFAULT_FONT_HEIGHT * (code - 160); + else + src = NULL; + + if (src) + { + p = dest; + + for (y = 0; y < DEFAULT_FONT_HEIGHT; y++) + { + line = *src++; + if (!line) + { + p += size; + continue; + } + + goto *jump[line & 0xF]; + + __1: p[0] = col; goto __0; + __2: p[1] = col; goto __0; + __3: p[0] = p[1] = col; goto __0; + __4: p[2] = col; goto __0; + __5: p[0] = p[2] = col; goto __0; + __6: p[1] = p[2] = col; goto __0; + __7: p[0] = p[1] = p[2] = col; goto __0; + __8: p[3] = col; goto __0; + __9: p[0] = p[3] = col; goto __0; + __A: p[1] = p[3] = col; goto __0; + __B: p[0] = p[1] = p[3] = col; goto __0; + __C: p[2] = p[3] = col; goto __0; + __D: p[0] = p[2] = p[3] = col; goto __0; + __E: p[1] = p[2] = p[3] = col; goto __0; + __F: p[0] = p[1] = p[2] = p[3] = col; goto __0; + __0: + + goto *jump2[line >> 4]; + + __10: p[4] = col; goto __00; + __20: p[5] = col; goto __00; + __30: p[4] = p[5] = col; goto __00; + __40: p[6] = col; goto __00; + __50: p[4] = p[6] = col; goto __00; + __60: p[5] = p[6] = col; goto __00; + __70: p[4] = p[5] = p[6] = col; goto __00; + __80: p[7] = col; goto __00; + __90: p[4] = p[7] = col; goto __00; + __A0: p[5] = p[7] = col; goto __00; + __B0: p[4] = p[5] = p[7] = col; goto __00; + __C0: p[6] = p[7] = col; goto __00; + __D0: p[4] = p[6] = p[7] = col; goto __00; + __E0: p[5] = p[6] = p[7] = col; goto __00; + __F0: p[4] = p[5] = p[6] = p[7] = col; goto __00; + __00: + + p += size; + } + } + + dest += DEFAULT_FONT_WIDTH; + } +} diff --git a/gb.sdl2/src/default_font.h b/gb.sdl2/src/default_font.h new file mode 100644 index 00000000..3ced9423 --- /dev/null +++ b/gb.sdl2/src/default_font.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + default_font.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __DEFAULT_FONT_H +#define __DEFAULT_FONT_H + +#include "main.h" + +#define DEFAULT_FONT_WIDTH 7 +#define DEFAULT_FONT_HEIGHT 13 +#define DEFAULT_FONT_ASCENT 10 +#define DEFAULT_FONT_DESCENT 3 + +int UTF8_get_length(const char *sstr, int len); +void FONT_render_default(uint *dest, int size, const char *text, int len); + +#endif /* __DEFAULT_FONT_H */ + diff --git a/gb.sdl2/src/default_font_data.h b/gb.sdl2/src/default_font_data.h new file mode 100644 index 00000000..be736d87 --- /dev/null +++ b/gb.sdl2/src/default_font_data.h @@ -0,0 +1,627 @@ +const char _default_font_33_126[] = { + 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x14, 0x3E, 0x14, 0x14, 0x3E, 0x14, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x3C, 0x0A, 0x0A, 0x1C, 0x28, 0x28, 0x1E, 0x08, 0x08, 0x00, + 0x00, 0x02, 0x15, 0x12, 0x08, 0x08, 0x04, 0x04, 0x12, 0x2A, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x12, 0x12, 0x0C, 0x04, 0x2A, 0x11, 0x11, 0x2E, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x10, 0x00, + 0x00, 0x04, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x32, 0x2A, 0x26, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x0C, 0x0A, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x18, 0x14, 0x12, 0x11, 0x3F, 0x10, 0x10, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x1E, 0x20, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x04, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x10, 0x0C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x08, 0x04, + 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x20, 0x20, 0x10, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x39, 0x25, 0x25, 0x25, 0x19, 0x02, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x22, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x2A, 0x12, 0x2C, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x14, 0x14, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x14, 0x14, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x1C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1C, 0x00, + 0x00, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x1C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1C, 0x00, + 0x00, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x10, 0x10, 0x00, 0x18, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0C, + 0x00, 0x00, 0x02, 0x02, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x18, 0x04, 0x04, 0x04, 0x04, 0x02, 0x04, 0x04, 0x04, 0x04, 0x18, 0x00, + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, + 0x00, 0x0C, 0x10, 0x10, 0x10, 0x10, 0x20, 0x10, 0x10, 0x10, 0x10, 0x0C, 0x00, + 0x00, 0x24, 0x2A, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const char _default_font_160_687[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x3C, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x3C, 0x08, 0x08, 0x00, + 0x00, 0x00, 0x38, 0x04, 0x04, 0x1E, 0x04, 0x04, 0x04, 0x04, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x22, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x3E, 0x08, 0x3E, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x02, 0x04, 0x0A, 0x12, 0x14, 0x08, 0x10, 0x0E, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x59, 0x45, 0x45, 0x45, 0x59, 0x22, 0x1C, 0x00, 0x00, + 0x0C, 0x10, 0x1C, 0x12, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x12, 0x09, 0x12, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x4D, 0x55, 0x4D, 0x55, 0x55, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x12, 0x12, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0x0E, 0x10, 0x0C, 0x02, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0E, 0x10, 0x0C, 0x10, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x3C, 0x2E, 0x2E, 0x2E, 0x2C, 0x28, 0x28, 0x28, 0x28, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x06, + 0x04, 0x06, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x12, 0x12, 0x12, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x12, 0x24, 0x12, 0x09, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x03, 0x02, 0x02, 0x02, 0x30, 0x0C, 0x03, 0x10, 0x18, 0x14, 0x3C, 0x10, + 0x02, 0x03, 0x02, 0x02, 0x02, 0x30, 0x0C, 0x03, 0x1C, 0x20, 0x18, 0x04, 0x3C, + 0x07, 0x08, 0x06, 0x08, 0x07, 0x30, 0x0C, 0x03, 0x10, 0x18, 0x14, 0x3C, 0x10, + 0x00, 0x00, 0x08, 0x08, 0x00, 0x08, 0x08, 0x04, 0x02, 0x02, 0x1C, 0x00, 0x00, + 0x04, 0x08, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x08, 0x04, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x08, 0x14, 0x08, 0x00, 0x1C, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x09, 0x09, 0x09, 0x3F, 0x09, 0x09, 0x09, 0x39, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x08, 0x06, + 0x04, 0x08, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x10, 0x08, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x3E, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x14, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x04, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x14, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x27, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x32, 0x32, 0x22, 0x00, 0x00, + 0x04, 0x08, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x1C, 0x32, 0x32, 0x2A, 0x2A, 0x2A, 0x26, 0x26, 0x1C, 0x02, 0x00, + 0x04, 0x08, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x12, 0x12, 0x1A, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x08, 0x14, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x28, 0x28, 0x1E, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x08, 0x06, + 0x00, 0x04, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x04, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x0C, 0x30, 0x18, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x3E, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x1C, 0x32, 0x2A, 0x2A, 0x2A, 0x26, 0x1C, 0x02, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x10, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x10, 0x60, + 0x10, 0x08, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x14, 0x08, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x27, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x70, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x08, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x10, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x10, 0x60, + 0x14, 0x08, 0x00, 0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x22, 0x1C, 0x00, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x06, + 0x00, 0x00, 0x30, 0x08, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x7F, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x0F, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x1C, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x08, 0x30, + 0x00, 0x08, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, + 0x08, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x37, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1F, 0x00, 0x00, + 0x00, 0x24, 0x24, 0x00, 0x36, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x20, 0x18, + 0x10, 0x28, 0x00, 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0E, 0x00, 0x00, + 0x00, 0x10, 0x28, 0x00, 0x18, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0C, + 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x08, 0x06, + 0x00, 0x00, 0x02, 0x02, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x10, 0x08, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x08, 0x06, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x0C, + 0x14, 0x08, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x1A, 0x1A, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x04, 0x04, 0x34, 0x34, 0x04, 0x04, 0x04, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x06, 0x03, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x18, 0x0C, 0x08, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x10, 0x08, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x08, 0x06, + 0x14, 0x08, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x02, 0x02, 0x01, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x20, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x10, + 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x12, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x24, 0x12, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x09, 0x09, 0x09, 0x39, 0x09, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x29, 0x29, 0x19, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x10, 0x08, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x08, 0x06, + 0x14, 0x08, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x10, 0x08, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x08, 0x06, + 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x0C, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x10, 0x0C, + 0x14, 0x08, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x0E, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x24, 0x2A, 0x12, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x22, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x22, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x08, 0x14, 0x08, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x08, 0x14, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x24, 0x12, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x24, 0x12, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x10, 0x60, + 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x08, 0x14, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x14, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x10, 0x08, 0x3E, 0x20, 0x10, 0x10, 0x08, 0x04, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x08, 0x00, 0x3E, 0x20, 0x10, 0x10, 0x08, 0x04, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x07, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x25, 0x24, 0x24, 0x1C, 0x24, 0x24, 0x24, 0x1C, 0x00, 0x00, + 0x00, 0x1E, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x03, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x60, 0x10, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x10, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x12, 0x22, 0x22, 0x27, 0x22, 0x22, 0x12, 0x0E, 0x00, 0x00, + 0x00, 0x0E, 0x15, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x14, 0x0C, 0x00, 0x00, + 0x00, 0x3C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x14, 0x08, 0x10, 0x20, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x20, 0x20, 0x3E, 0x20, 0x20, 0x20, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x3E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, + 0x00, 0x00, 0x38, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, + 0x00, 0x40, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x06, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x60, 0x10, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x02, 0x62, 0x12, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x04, 0x1E, 0x08, 0x08, 0x14, 0x14, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x34, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x04, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x20, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x40, 0x5C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x5C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x26, 0x39, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x26, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x26, 0x39, 0x29, 0x29, 0x29, 0x29, 0x26, 0x20, 0x20, + 0x03, 0x04, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x04, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02, + 0x00, 0x00, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x20, 0x00, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x1C, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x1C, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x12, 0x12, 0x1C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x10, 0x08, + 0x00, 0x00, 0x3E, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, 0x00, + 0x00, 0x40, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x36, 0x14, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x40, 0x22, 0x22, 0x22, 0x14, 0x14, 0x14, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x40, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x3E, 0x20, 0x10, 0x10, 0x3E, 0x04, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x3E, 0x04, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x1C, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x02, 0x04, 0x08, 0x1C, 0x02, 0x02, 0x02, 0x3C, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x20, 0x18, 0x20, 0x20, 0x1E, 0x01, 0x06, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x10, 0x3E, 0x04, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0x02, 0x02, 0x1E, 0x20, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x02, 0x02, 0x1E, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x1E, 0x02, 0x04, 0x18, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x12, 0x0E, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x3E, 0x08, 0x3E, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, + 0x28, 0x10, 0x3B, 0x25, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x0D, 0x3B, 0x00, 0x00, + 0x00, 0x28, 0x13, 0x05, 0x3D, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3B, 0x00, 0x00, + 0x00, 0x28, 0x14, 0x04, 0x3E, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x31, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x1F, 0x00, 0x00, + 0x00, 0x20, 0x21, 0x01, 0x31, 0x21, 0x21, 0x21, 0x21, 0x21, 0x2F, 0x20, 0x18, + 0x00, 0x20, 0x26, 0x04, 0x34, 0x24, 0x24, 0x24, 0x24, 0x24, 0x2E, 0x20, 0x18, + 0x00, 0x00, 0x29, 0x29, 0x2B, 0x2B, 0x2D, 0x2D, 0x29, 0x29, 0x19, 0x00, 0x00, + 0x00, 0x20, 0x29, 0x09, 0x2B, 0x2B, 0x2D, 0x2D, 0x29, 0x29, 0x29, 0x20, 0x18, + 0x00, 0x20, 0x20, 0x00, 0x37, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x20, 0x18, + 0x14, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x1C, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x04, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x04, 0x08, 0x00, 0x14, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x3E, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x1C, 0x00, 0x14, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x1C, 0x00, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x00, 0x3E, 0x09, 0x09, 0x3F, 0x09, 0x09, 0x09, 0x09, 0x39, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x1E, 0x28, 0x28, 0x1E, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x72, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x72, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x14, 0x08, 0x00, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x14, 0x08, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x14, 0x08, 0x02, 0x02, 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x00, 0x00, 0x1C, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x08, 0x30, + 0x14, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x18, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x14, 0x08, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x1C, 0x20, 0x20, 0x20, 0x1E, + 0x00, 0x14, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x06, + 0x00, 0x00, 0x3B, 0x25, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x0D, 0x3B, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x05, 0x3D, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3B, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3E, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3E, 0x00, 0x00, + 0x10, 0x08, 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x0A, 0x0A, 0x0A, 0x2A, 0x2E, 0x2A, 0x2A, 0x2A, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x12, 0x0A, 0x06, 0x02, 0x02, 0x02, + 0x04, 0x08, 0x22, 0x26, 0x26, 0x2A, 0x2A, 0x32, 0x32, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x04, 0x2A, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x04, 0x2A, 0x14, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x3E, 0x09, 0x09, 0x3F, 0x09, 0x09, 0x09, 0x39, 0x00, 0x00, + 0x00, 0x10, 0x08, 0x00, 0x1E, 0x28, 0x28, 0x3E, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x08, 0x24, 0x1C, 0x32, 0x32, 0x2A, 0x2A, 0x2A, 0x26, 0x26, 0x1C, 0x02, 0x00, + 0x00, 0x10, 0x08, 0x20, 0x1C, 0x32, 0x2A, 0x2A, 0x2A, 0x26, 0x1C, 0x02, 0x00, + 0x12, 0x24, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x3E, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x3E, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x12, 0x24, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x12, 0x24, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x1C, 0x22, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x20, 0x1E, 0x08, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x08, 0x04, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x04, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x08, 0x04, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x18, 0x20, 0x20, 0x20, 0x18, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x18, 0x20, 0x20, 0x20, 0x18, 0x06, + 0x14, 0x08, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x14, 0x08, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x20, + 0x00, 0x00, 0x04, 0x04, 0x06, 0x05, 0x05, 0x05, 0x15, 0x2D, 0x1E, 0x08, 0x08, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x20, 0x10, 0x10, 0x08, 0x04, 0x04, 0x02, 0x3E, 0x20, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x20, 0x10, + 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x02, 0x02, 0x02, 0x3E, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x3C, 0x08, 0x06, + 0x1C, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x00, 0x14, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x2C, 0x1A, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x2C, 0x1A, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x1C, 0x00, 0x08, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x14, 0x2C, 0x18, 0x08, 0x08, + 0x00, 0x00, 0x03, 0x05, 0x05, 0x05, 0x05, 0x05, 0x15, 0x2D, 0x1D, 0x08, 0x08, + 0x00, 0x00, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x12, 0x2A, 0x1C, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x06, + 0x00, 0x00, 0x08, 0x08, 0x1C, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x08, + 0x00, 0x10, 0x1C, 0x32, 0x32, 0x2A, 0x3E, 0x2A, 0x2A, 0x26, 0x26, 0x04, 0x00, + 0x00, 0x10, 0x3C, 0x12, 0x12, 0x0A, 0x0A, 0x0A, 0x0A, 0x06, 0x3C, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x3C, 0x12, 0x0A, 0x0A, 0x0A, 0x06, 0x3C, 0x04, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x07, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x08, 0x08, 0x28, 0x18, 0x08, 0x0C, 0x0A, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x04, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x04, 0x18, + 0x00, 0x00, 0x1C, 0x20, 0x20, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x20, 0x10, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x22, 0x27, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x22, 0x7F, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x14, 0x14, 0x14, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x10, 0x3E, 0x12, 0x12, 0x0A, 0x1E, 0x0A, 0x0A, 0x06, 0x3E, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x1C, 0x32, 0x2A, 0x3E, 0x0A, 0x06, 0x3C, 0x04, 0x00, + 0x00, 0x00, 0x38, 0x10, 0x10, 0x38, 0x10, 0x10, 0x10, 0x10, 0x0E, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x06, + 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x40, + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1F, 0x06, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x0F, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x3E, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x7F, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2C, 0x32, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1A, 0x26, 0x22, 0x22, 0x22, 0x26, 0x1A, 0x00, 0x00, + 0x0C, 0x02, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x12, 0x2A, 0x1C, 0x08, 0x08, + 0x00, 0x00, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x40, + 0x40, 0x20, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x3E, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2E, 0x50, 0x18, 0x24, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x20, 0x20, 0x1C, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2E, 0x50, 0x10, 0x0C, 0x10, 0x10, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x1A, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x10, 0x10, 0x38, 0x10, 0x10, 0x10, 0x10, 0x0C, + 0x00, 0x00, 0x00, 0x40, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x08, 0x14, 0x14, 0x14, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, + 0x0C, 0x02, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x0C, 0x02, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x18, + 0x00, 0x08, 0x08, 0x00, 0x0C, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x2C, 0x2A, 0x1A, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x0C, 0x0A, 0x3C, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, + 0x00, 0x00, 0x03, 0x02, 0x3E, 0x22, 0x12, 0x0A, 0x12, 0x22, 0x22, 0x20, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x3C, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x20, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x02, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x20, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x26, 0x26, 0x2A, 0x32, 0x32, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x09, 0x09, 0x39, 0x09, 0x09, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x2A, 0x2A, 0x2A, 0x2A, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x08, 0x1C, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x1C, 0x08, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x2E, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x2E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x2E, 0x20, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0C, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x0A, 0x12, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x0A, 0x1E, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x02, 0x1C, 0x20, 0x20, 0x1E, 0x02, 0x0C, + 0x00, 0x00, 0x30, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x20, 0x18, 0x10, 0x10, 0x3C, 0x10, 0x10, 0x10, 0x10, 0x0C, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, + 0x00, 0x00, 0x30, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3C, 0x0A, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1E, 0x10, 0x10, + 0x00, 0x00, 0x04, 0x04, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x7F, 0x22, 0x22, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x14, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x14, 0x14, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x2A, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x10, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x34, 0x4A, 0x3E, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x1C, 0x20, 0x20, 0x20, 0x1E, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x20, 0x10, 0x08, 0x1C, 0x20, 0x3C, 0x22, 0x5C, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x10, 0x20, 0x20, 0x20, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x2A, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x2C, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x08, 0x3C, 0x02, 0x02, 0x32, 0x22, 0x22, 0x1C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x10, 0x10, 0x00, 0x18, 0x10, 0x10, 0x10, 0x10, 0x10, 0x3C, 0x12, 0x0C, + 0x00, 0x00, 0x22, 0x24, 0x28, 0x30, 0x28, 0x24, 0x22, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x40, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x10, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x02, 0x02, 0x04, 0x08, 0x08, 0x1C, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3E, 0x25, 0x15, 0x15, 0x15, 0x0D, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x3E, 0x25, 0x25, 0x15, 0x25, 0x25, 0x26, 0x20, 0x18, + 0x00, 0x00, 0x04, 0x04, 0x3E, 0x25, 0x15, 0x15, 0x35, 0x5D, 0x3E, 0x10, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x37, 0x09, 0x09, 0x11, 0x21, 0x21, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x21, 0x11, 0x17, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1E, 0x10, 0x08, + 0x00, 0x00, 0x01, 0x01, 0x37, 0x09, 0x09, 0x09, 0x19, 0x29, 0x1E, 0x08, 0x08, + 0x00, 0x00, 0x06, 0x01, 0x1D, 0x27, 0x25, 0x25, 0x25, 0x25, 0x25, 0x20, 0x10, + 0x00, 0x00, 0x03, 0x02, 0x32, 0x0A, 0x0A, 0x12, 0x22, 0x22, 0x1F, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x02, 0x3A, 0x22, 0x12, 0x12, 0x12, 0x0A, 0x3C, 0x00, 0x00, + 0x22, 0x22, 0x2A, 0x36, 0x22, 0x00, 0x22, 0x22, 0x2A, 0x36, 0x22, 0x00, 0x00, + 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x01, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x01, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x40, +}; diff --git a/gb.sdl2/src/gb.sdl2.component b/gb.sdl2/src/gb.sdl2.component new file mode 100644 index 00000000..8076ceea --- /dev/null +++ b/gb.sdl2/src/gb.sdl2.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.sdl2 +Implements=EventLoop,OpenGLViewer,ImageIO +Requires=gb.image +State=1 diff --git a/gb.sdl2/src/main.c b/gb.sdl2/src/main.c new file mode 100644 index 00000000..de8c9d38 --- /dev/null +++ b/gb.sdl2/src/main.c @@ -0,0 +1,212 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +// Lazyfoo! + +#include "gambas.h" +#include "main.h" +#include "c_image.h" +#include "c_draw.h" +#include "c_window.h" +#include "c_mouse.h" +#include "c_font.h" + +#include "gb_list_temp.h" + +GB_INTERFACE GB EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; +GEOM_INTERFACE GEOM EXPORT; + +GB_CLASS CLASS_Window; +GB_CLASS CLASS_Image; +GB_CLASS CLASS_Font; + +//------------------------------------------------------------------------- + +static void init_sdl() +{ + uint init = SDL_WasInit(SDL_INIT_EVERYTHING); + const char *error; + + // if audio is defined, sdl was init by gb.sdl2.audio component ! + if (init & SDL_INIT_AUDIO) + { + if (SDL_InitSubSystem(SDL_INIT_VIDEO)) // | SDL_INIT_JOYSTICK)) + { + error = SDL_GetError(); + goto __ERROR; + } + } + else + { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) // | SDL_INIT_JOYSTICK)) + { + error = SDL_GetError(); + goto __ERROR; + } + } + + if (IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG) != (IMG_INIT_JPG | IMG_INIT_PNG)) + { + error = IMG_GetError(); + goto __ERROR; + } + + return; + +__ERROR: + + fprintf(stderr, "gb.sdl2: unable to initialize SDL2: %s\n", error); + abort(); +} + +static void exit_sdl() +{ + uint init; + + if (TTF_WasInit()) + TTF_Quit(); + + IMG_Quit(); + + init = SDL_WasInit(SDL_INIT_EVERYTHING); + + // if audio is defined, gb.sdl2.audio component still not closed ! + if (init & SDL_INIT_AUDIO) + SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK); + else + SDL_Quit(); +} + +static void event_loop() +{ + SDL_Event event; + + while (SDL_PollEvent(&event)) + { + switch(event.type) + { + case SDL_QUIT: + break; + case SDL_WINDOWEVENT: + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + case SDL_MOUSEMOTION: + case SDL_MOUSEWHEEL: + case SDL_KEYDOWN: + case SDL_KEYUP: + case SDL_TEXTINPUT: + WINDOW_handle_event(&event); + break; + } + } +} + +static void my_main(int *argc, char **argv) +{ + init_sdl(); + + CLASS_Window = GB.FindClass("Window"); + CLASS_Image = GB.FindClass("Image"); + CLASS_Font = GB.FindClass("Font"); +} + +static int my_loop() +{ + for(;;) + { + if (!GB.Loop(10) && !WINDOW_list) + break; + event_loop(); + WINDOW_update(); + } + + return 1; +} + +static void my_wait(int duration) +{ + GB.Loop(10); + if (duration > 0) + event_loop(); + WINDOW_update(); +} + +//------------------------------------------------------------------------- + +GB_DESC *GB_CLASSES[] EXPORT = +{ + ImageDesc, + DrawDesc, + WindowDesc, + KeyDesc, + MouseDesc, + FontDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.Component.Load("gb.geom"); + GB.GetInterface("gb.geom", GEOM_INTERFACE_VERSION, &GEOM); + GB.Component.Load("gb.image"); + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + IMAGE.SetDefaultFormat(GB_IMAGE_BGRA); + + GB.Hook(GB_HOOK_MAIN, (void *)my_main); + GB.Hook(GB_HOOK_LOOP, (void *)my_loop); + GB.Hook(GB_HOOK_WAIT, (void *)my_wait); + + return -1; +} + +void EXPORT GB_EXIT() +{ + exit_sdl(); +} + +void EXPORT GB_SIGNAL(int signal, void *param) +{ + //static bool wasFullscreen = false; + + //if (!SDLcore::GetWindow()) + // return; + + if ((signal == GB_SIGNAL_DEBUG_BREAK) || (signal == GB_SIGNAL_DEBUG_CONTINUE)) + { + /*if (SDLcore::GetWindow()->IsFullScreen()) + { + wasFullscreen = true; + SDLcore::GetWindow()->SetFullScreen(false); + }*/ + } + + if (signal == GB_SIGNAL_DEBUG_CONTINUE) + { + /*if (wasFullscreen) + SDLcore::GetWindow()->SetFullScreen(true);*/ + } +} + diff --git a/gb.sdl2/src/main.h b/gb.sdl2/src/main.h new file mode 100644 index 00000000..968e37cb --- /dev/null +++ b/gb.sdl2/src/main.h @@ -0,0 +1,78 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "gb_list.h" + +#include "gb.geom.h" +#include "gb.image.h" + +#include "SDL.h" +#include "SDL_image.h" +#include "SDL_ttf.h" +#include "SDL_opengl.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +extern GEOM_INTERFACE GEOM; + +extern GB_CLASS CLASS_Window; +extern GB_CLASS CLASS_Image; +extern GB_CLASS CLASS_Font; +#endif + +#define RAISE_ERROR(_msg) GB.Error(_msg ": &1", SDL_GetError()); + +#define SAME_COLORS(_col1, _col2) ((_col1)->r == (_col2)->r && (_col1)->g== (_col2)->g && (_col1)->b == (_col2)->b && (_col1)->a == (_col2)->a) + +#define SDL_COLOR_TO_UINT(_color) ((_color)->b | ((_color)->g << 8) | ((_color)->r << 16) | ((_color)->a << 24)) + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + +#define DEFAULT_IMAGE_FORMAT GB_IMAGE_ARGB +#define DEFAULT_SDL_IMAGE_FORMAT SDL_PIXELFORMAT_BGRA8888 + +#define BMASK 0xFF000000 +#define GMASK 0x00FF0000 +#define RMASK 0x0000FF00 +#define AMASK 0x000000FF + +#else + +#define DEFAULT_IMAGE_FORMAT GB_IMAGE_BGRA +#define DEFAULT_SDL_IMAGE_FORMAT SDL_PIXELFORMAT_ARGB8888 + +#define BMASK 0x000000FF +#define GMASK 0x0000FF00 +#define RMASK 0x00FF0000 +#define AMASK 0xFF000000 + +#endif + +#endif /* __MAIN_H */ + diff --git a/gb.v4l/AUTHORS b/gb.v4l/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/gb.v4l/COPYING b/gb.v4l/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.v4l/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.v4l/ChangeLog b/gb.v4l/ChangeLog new file mode 100644 index 00000000..86f0c1cb --- /dev/null +++ b/gb.v4l/ChangeLog @@ -0,0 +1,241 @@ +0.4.3 (net - STABLE / curl - ALPHA - compiles on Gambas 0.94) + +* Fixed a "segmentation fault", when changing user name or password in both HttpClient and FtpClient + +0.4.2 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93b) + +* FtpClient external interface stabilized. + +* Finished 'Curl' class code, and code sharing between 'HttpClient' and 'FtpClient'. + +0.4.1 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93b) + +* Added a new class 'Curl', that is the base for the rest of classes in this component + +* Now HttpClient and FtpClient inherits from 'Curl', so they share a lot of code + +0.4.0 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93b) + +* HttpClient : some code improvements so now it is faster,smaller and wastes less memory. + +* Added 'FtpClient' class. + +0.3.1 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93) + + * Added two new properties to Socket class. They are read/write, the first is called 'Port'. if + value is zero (Net.Local), connection will try to stablish a Local socket, else a TCP connection + will be stablished. The second, 'HostOrPath' can be a Host or a local path. + + * Socket constructor has now no parameters. + + * Conect method has now two optional parameters. The first can override 'HostOrPath' property, and the + second overrides 'Port' property + +0.3.0 (net - STABLE / curl - ALPHA - compiles on Gambas 0.93) + +* Added code to let the IDE show icons for all net classes. +* Removed lots of code to make the component lighter and faster. + +0.2.3 (net - STABLE / curl - ALPHA - compiles on Gambas 0.90) + +* Fixed a bug in UdpClient : segmentation fault when reading or writing data +* Fixed a bug in UDPServerClient example : trying to use CLOSE when UdpClient is not active +* defined before to allow compile on FreeBSD + +0.2.2 (ALPHA - compiles on Gambas 0.81) + +* 'NetCode' and 'AdvancedCode' classes changed to 'Net' +* Proxy properties from 'HttpClient' has been added into a new class called 'Proxy' +* 'ReturnCode' and 'ReturnString' properties are now called 'Code' and 'Reason' +* This version should compile now using libcurl 7.10.3, 7.10.4, 7.10.5, 7.10.6, 7.10.7, 7.10.8 and 7.11.0 +* now is after in all files to allow compile it on FreeBSD + +0.2.1 (ALPHA - compiles on Gambas 0.81) + +* Examples Updated +* New HttpClient interface defined +* 'AdvancedCode' class provides constants for 'Net.Advanced' component + +0.2.0 (ALPHA - compiles on Gambas 0.80) + +* Examples updated +* Modifications in configuration scripts to detect libcurl +* 'NetCode' provide the constants needed to work with all network classes +* 'Net advanced' includes not : CHttpClient, and will include other classes using libcurl +* 'Net' includes now : Socket, SerialPort,ServerSocket,UdpSocket,DnsClient and NetCode, + that is, basic networking stuff +* 'Net' component splitted it two components: 'Net' and 'Advanced net' + +0.1.4 (STABLE - Gambas 0.80) + +BM - Changes to allow the component compile on systems without MSG_NOSIGNAL flag + +0.1.3 (Gambas 0.74) + +BM - 20 Dec 2003 - Let component compile with gcc 2.95 + +0.1.2 + +* Added HTTP proxy support for 'HttpClient' class + +0.1.1 (Gambas 0.73) + +* Corrected bug in 'HttpClient' class that didn't convert correctly + document query to HTTP codification +* Added support for Solaris + +0.1.0 + +* Added 'HttpClient.Local' constant as sinonym of 'HttpClient.Unix' +* Changed 'HttpClient.Inet' constant to 'HttpClient.Internet' + +0.1.0pre7 + +* Using sys/un.h instead of linux/un.h in 'Socket' and 'ServerSocket' + classes +* Added option 'SO_REUSEADDR' to socket in 'ServerSocket' class +* 'ServerSocket' example fixed +* Documentation fixed + + +0.1.0pre6 + +* Memory allocation bug fixed in 'Socket' class +* Memory allocation bug fixed in 'SerialPort' class +* Lots of internal code reorganization +* Some memory optimizations in 'Socket' and 'ServerSocket' +* UDPServerClient example fixed +* ServerSocket example fixed + +0.1.0pre5 + +* Constant names changed in all classes to be more simple +* Parameters in methods and events does not include its type + as a prefix now +* 'ConnectUnix' and 'ConnectSocket' methods merged in one + method : 'Connect' +* 'ServerSocket' 'SocketType' property changed to 'Type' +* 'ConnectionRequest' event in 'ServerSocket' changed to 'Connection' +* 'Accept' method from 'ServerSocket' does not take any parameter + now +* 'HostFound' events from 'Socket' and 'HttpClient' changed to 'Found' +* Datagram class now inherits from '.Stream' +* Removed 'DataPacket' class +* New properties 'SourceHost', 'SourcePort', 'TargetHost', 'TargetPort' + in 'Datagram' class +* New method 'Peek' in 'Datagram' class +* Removed methods 'Stop','Receive' and 'Send' from 'Datagram' class +* 'Start' method from 'Datagram' Changed to 'Bind' +* 'Datagram' class changed its name to 'UdpSocket' +* Documentation updated +* Examples updated + + + +0.1.0pre4 + +* Removed Close() method from 'Socket' and 'SerialPort' classes, + translated to standard stream methods +* 'LookingHostIP' constant in 'Socket' and 'HttpClient' classes, + changed to 'LookingUpHostIP' +* Examples updated +* Documentation updated + + +0.1.0pre3 + + +* 'Accept' method from 'ServerSocket' changed its way to + act. Now it returns a new 'Socket' and accpets an optional + Event Handler +* Old method 'Receive' from HttpClient, splitted + in two new methods : 'Receive' and 'Peek' +* Now 'Socket' class inherits from '.Stream' +* Now 'SerialPort' class inherits from 'Stream' +* Removed 'Send' and 'Receive' methods from 'Socket' +* Removed 'Send' and 'Receive' methods from 'SerialPort' +* Added 'Peek' method to 'Socket' +* Adaptations from generic stream methods to 'Socket' characteristics +* Adaptations from generic stream methods to 'SerialPort' characteristics +* Documentation updated +* Examples updated + + +0.1.0pre2 + +* Added 0.0.17 = 0.1.0pre1 to CHANGELOG file +* 'SocketError' event from Socket and Datagram now is called 'Error' +* 'Error' codes are now negative values in Status property +* 'Error' events from classes which support it, now takes + zero parameters +* References to class names removed from 'GB.Error()' messages +* Class 'ClientSocket' changed its name to 'Socket' +* 'RemoteHostIP' and 'LocalHostIP' properties from 'Socket' + class changed its name to 'RemoteHost' and 'LocalHost' +* Constants from ServerSocket changed its name "TypeTCP"->"iNet", + "TypeUnix"->"Unix" +* 'DataAvailable' event changed its name to 'Read' in all classes + which supports it. +* 'Wait' method from ServerSocket changed to 'Pause' +* 'Path' property from 'Socket' class now returns IP:Port when + connected using TCP sockets. +* 'CloseSocket' method from 'Socket' class changed to 'Close' +* 'Socket' Class has a new constructor. You can both use no + parameters, or pass a string as parameter, which can be : + 'HostName:Port' or 'HostIP:Port' for TCP connections, or + 'Absolute_Path' for Unix connections. +* 'ServerSocket' Class has a new constructor. You can both use no + parameters, or pass a string and a number as parameter, which can be : + ':Port' for TCP connections, or 'Absolute_Path' for Unix connections. +* 'Datagram' Class has a new constructor. You can both use no + parameters, or pass an integer as Port value to start its + work. +* 'MaxConn' parameter from 'Listen' method if ServerSocket class + now is optional. +* 'SendData' and 'ReceiveData' changed to 'Send' and 'Receive' in all + classes containing that methods. + + +0.0.17 = 0.1.0pre1 + +* Removed prefix 'Is' in constats beginning with that prefix. +* 'GetData' method changed its name to 'ReceiveData' is some + classes +* 'Connected' event from 'ClientSocket' changed its name + to 'Connect'. +* Examples updated. +* HttpClient class finished. +* HttpClient documentation added. + + +0.0.16 + +* Added CHANGELOG file. +* Changed component name from 'networking' to 'net'. +* Changed 'sData' property from DataPacket to 'Data'. +* Documentation updated for DataPacket class. +* Changed example names. +* Constant names and values have changed in ClientSocket, some + new constants have been added. +* ConnectSocket and ConnectUnix methods from ClientSocket + have changed, now they not return any value, as error + codes are managed by "SocketError" event. +* Documentation updated for ClientSocket class. +* Example "ClientSocket" updated. +* DnsClient has two new constants. +* Documentation updated for DnsClient class. +* Some ServerSocket constants have changed its name. +* New Constants added to ServerSocket. +* A new static property 'UnixMaxPath' added to ServerSocket. +* ServerSocket 'Listen' method does not return any value now. +* ServerSocket example updated. +* Documentation updated for ServerSocket class. +* Datagram class has new constants. +* Datagram 'Start' method does not return any value now. +* Datagram 'SocketError' event implemented. +* Documentation updated for Datagram class. +* Example "UDPServerClient" updated. +* SerialPort 'GetData' method now is called 'ReceiveData'. +* Added constants to SerialPort class. +* Documentation updated for SerialPort class. +* SerialPort Example updated. diff --git a/gb.v4l/INSTALL b/gb.v4l/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.v4l/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.v4l/Makefile.am b/gb.v4l/Makefile.am new file mode 100644 index 00000000..547eb54e --- /dev/null +++ b/gb.v4l/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @V4L_DIR@ +EXTRA_DIST = reconf spec gambas.h gb*.h orig diff --git a/gb.v4l/NEWS b/gb.v4l/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/gb.v4l/README b/gb.v4l/README new file mode 100644 index 00000000..23bf9803 --- /dev/null +++ b/gb.v4l/README @@ -0,0 +1,4 @@ +This code is partially based in the code written and released under GPL by +Nick Andrew , for the "video-capture" program. +You can find the original sources of that program in the "orig" folder +in these sources. diff --git a/gb.v4l/acinclude.m4 b/gb.v4l/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.v4l/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.v4l/component.am b/gb.v4l/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.v4l/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.v4l/configure.ac b/gb.v4l/configure.ac new file mode 100644 index 00000000..9963cc0e --- /dev/null +++ b/gb.v4l/configure.ac @@ -0,0 +1,25 @@ +dnl ---- configure.ac for gb.v4l + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-v4l, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.v4l) +AC_PROG_LIBTOOL + +GB_COMPONENT_PKG_CONFIG( + v4lconvert, V4LCONVERT, gb.v4l, [src], + libv4lconvert) + +GB_COMPONENT( + v4l, V4L, gb.v4l, [src], + [GB_FIND(png.h jpeglib.h linux/videodev2.h, $prefix /usr/local/lib /usr/local /usr/lib /usr, include)], + [GB_FIND(libpng.$SHLIBEXT libjpeg.$SHLIBEXT, $prefix /usr/local /usr, lib)], + [$C_LIB -ljpeg -lpng]) + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +) + +GB_PRINT_MESSAGES diff --git a/gb.v4l/gambas.h b/gb.v4l/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.v4l/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.v4l/gb.image.h b/gb.v4l/gb.image.h new file mode 120000 index 00000000..c8c09bcc --- /dev/null +++ b/gb.v4l/gb.image.h @@ -0,0 +1 @@ +../main/lib/image/gb.image.h \ No newline at end of file diff --git a/gb.v4l/gb_common.h b/gb.v4l/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.v4l/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.v4l/m4 b/gb.v4l/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.v4l/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.v4l/orig/video-capture-0.2.tar.gz b/gb.v4l/orig/video-capture-0.2.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..8e1a3213088432479dee03cf4a708d60783a232d GIT binary patch literal 10049 zcmV-HC%)JpiwFR&yeKg^y|Y2 zsQ@KH(r5wG)A{c2-dk1L5X3e~`pud6oS6ox>ej8h-(MBE)Iac%W%KX8;9s-VY(CxJ z|E}3=wthKirvHn7t^L;CcgF{IbI= z$K-eQNjSB|+=0nwmci?j>{la88NP?MH*rI^?%CRTx7QzBT%Vl;wl%GXk!{x<9v(Jk zy5E@3=lH)mihoCUOrOv3WyAB`_uL4BMvmjN7Z>f9y_22uSM4{Q!A1A^-)=fDwTpB4 z*naQvv8L&^eOSuP^tsLTfFG9X@{^|3<7cUDCX6c?WC-p2P?1ur*1@vs<3F_050%UI z-#b+ndA6&YS}AlRW5SYk#PdU~ZO-Dl`CjkW{I58>TXucu9kZ($LEY9u>vhSy?A(RLpf%a_j?GECNr&q zjKQn_FlM8B$Vg84t6lLg`4ejXvTIQMf4VCEx0<_qN&J8M^Z>{2w_4xg|JV4_7-MZF zqbUnrHutTNGhMh16RyWbzCPtl`VfvD!vp-T4YpRdsgaGN8R`7x8 zAsazhXMxAL$tqTz*V*oY9MuT8;X0-ybJM=4);0We?#>+%7a9I-v)@|3XPp_+lJa6$ z33wVZKI8r(oB${Btli&JffJKlsDD6CLu<-y-3wqt_%@(LuV5ErJ+xdWS$6ODTMp|v zxX2Zl!Owvb7<_=;#sl}42Tt56r!hF_%=H$&HJ*g5a#m%#VEZ*1$J&(G*fAQ}uenrr zO0uwm(6@$B2;z$%Ov5H7oQ2$<1|T>3^7866yNQOjWv~m-nPZ4G`|v6wwOD;<_(;#T zMxgM3{R4&>dRRApu8|dl++o87((v`b(0?6I@9HLu1XX!36W65|u*-kU_Y8gdvl2_4 z^*Bjo5{BMkgAzCmh4~HQsvEL7EExqH?1Gfy0>eA*Xe6x+6$A_D()32YJB^J}rEH)h z?6bx|GOP0(vY%LCip6NJ)tp>Wjp_nFs2UHeMm~PKjE~WsVyn&*20fl1tj* z#Er%i=7MT{wOqjX9Tg{J2d#r0 z&N*45un&5XGZNv6ZzovsO!JyMV9Y`xMdKsK6K$B5JfU? zknaA4lZZl?NsFlODHfu&#|1RIV5 zwT~RDsBKt}$OITIVTkF13-g^}G#YVo0x2u7)diVVm>1N9C4+Q{EYV62 zzy%Z6!q*PJ3#EI_Nx}FK81c!H!R(pPvcZFR2c8c!!Jx!EX&tZHeb#@~VK=weFK^qI z?5uso&adP6sZ0uq*eaKR^LyTufvl(^mqTsW^=c_LVbnh(2#08~0jdUUzD8<7B@k0C zxE6#XJ(OXXF;K7vI>s&~U=>>KN3$+hQ(xbEWA?0?XVG#PG##|T!1{v+H4%6W?uR>?|NeJ8ZfDn=~LHa)>*9ii~nJI2l+zMTeRqBy=ns zdm3S=A!7#)^;mzv8rizfA@b`YELaBROqs*+FBLwSbUB|0`CK>2l4W$1p*=-iXjoU2 z4A+Oa?LqiNVda*CnfbgHyC^zBRxcRHC;E)|7`te0A1L{NW$9zoFfL4S7|a#Tlq!k3 zQrHWuVMtAC&8fZ+nZf5^sQ3W0&Q3VNT~ZG7lZ}{>ZtqBk0$U2KvBN67KCXkFxbifL ze=3atHzE?Kh(%i=xuK9ma!Bo$Plc*biq)8Hj7tv5T9yhTepn7#nlffqp-lr%I@D=y z zTmF|Qf97)(JfvNqV~wTBdWDbgbpTDJpMDn9FfZS zz@0m!j0Fo6+d6s(3LkYxaf9MtApAu4$I+BK)DHqr$U_I^P(Zx~W4hDl;K!5_LrN3E#xOljp{Vl% zMQF$&a&zWK4lR+jInnxDL%!`^oFCG+zoG2OzROen0|yO{VFe2u8Qcwm{v1C|z%(K# zK_;$^mGTzNyn!~$3ef2T73WPs^BPNLN@!j@29s_QWTi$b4fr6i$|g#uUayOSpyRxE z7hBYMe!3CVZaL%vZ0$s|+N!5JtouQvja4l)65M?Z!4#4UB=+t{0*CIe9r~Y%$9?U; z1oQoJYpg&2X*KuK_}_jD&wmbDzkI|0zQ*4%s{T|vo-1Qc zLRu9}%crZ}95?jSBD69?HVLWa)8sQvT?b$uH;_T(JOyJC>&u+qN4@3K^}r`GbZ@E! z#1uFx=)}xxpTbFhvcsdI4EfVIJOZooPqoM4rLp3*jZxD_L8ircX~NZKK}|P@Og|G| z`gy?8O_9>i1d}F8`b=Qy=Ris~gh@4|xG6^ZIRMg!p`$C{(a(g9-V+#2*~2P$G?!EE z1C!#ojWE&-zKAvWC-BiUW1E4aYk<*IcsXYeo zOi0Oqq@=qk+8M)~NGy$XW|g=p;;Eocr1faDlVHv)U!Mi;{3O(wU2`+YbJ?OF3U|s3 z1QxXcbilwDNE9mu*D8%o?A#|9#MUium8&{?n(szw9;Pb8B~R_vttM?`!-uwl%iR zeyx08&G)1`text{MpU+qIb>1G$apl&M-mUa(MD7H>CV`~0*gb@<&MHRgy|!;a3kv6 zA=t!d4ppU&*#K@thmCaL*&2-JCGreqTpH^ML}!Ihf(dnxNC#)UKpuLM2dJxnFqPCt zj9UaSU^9sV*;Q*uf;EQMF}A6(SYSdBnXO7?n++9mJhi@J`(_HWvP9&ZxE@zsuqZT# zT*5y!B!Ok(+iw5W_3J)sU%g{*+qbvvtNyzqO0?KoKt&-)Sx_8Wn1OZQht_*RS2ecT zW#{(n6-;SA?_PBK@31<5(d}P#dOh~y`j)k!Q@ibV&t6}&Z`sZ3+neiNr_Kngq8fO6 zorp)IB_DG%WM(^18-E9im3f?c4lf_VkwncBw^KOa?yOHv(~lNG1Cw*G{Md4h(9S-# z5s4`pFJ6#mf@9)44eTj0!px<5_AjkuIbmsTnc?I}e4Y@zCBK;5gBPVzqoFDrdLmwm z_G^Ic{}GPR?YX`P7!d=Z3PSK9H3h77FrDfijmHlaaETe1L)@FEpbXK}iHY%Jnkg8e z9b~{7)hGjpkit;B^BPaZE%2Zjd_Ky*mX-DlKjdFdL4It8|G)GG1OsoT%LJiAg944s;J-(}OXOfz z3cdpL{n9pVe<0LCTfh=Cskx>c{BI%JN$jfWa70OICS)`+mBV?HMPNapt^)fH;Lp&L_VDe$JqZq~jk4@(0%F zx?>Pbm>sU=XV1(5)X+g+%BJhzWYiAs<`Ll8I_4bj9O9t}uWk#w*J^k(~AMxP+{6Of3bi zrVO)+4(0xqjq3S_4^gC`iVXo=bY7g){Qd2E8h-qlq6vQkvW6h5F? zux)6tk<$0vfs*Z8U-tj$FMI0rW!6~V|LyLH-~Y<~kM4g!|M%_pUti^KRsUz?`af~v zD7wJRA>+3$@LL!7pW6k>=Bu2=PRi+y4~TI;+@$5h$B?+4+|zn73?A1QSNoMie2c4Rkon^0E?jeLNgN^pP9LHD*DV5 zrkYoLCVS2k7I(4ul-PrZqMH@ZQq8NPvDCB1(`Ph!CcmpD79KX`qolr1o{7dW_Z^x` z@klGBHuUjpwjK}bJwTUQNmy+KbhuTd#3t5rN>c^pxmhKXP&-C}z>Z5)pmXGQ&MB&c zU`0(@!P8o*7SZ{UckoLcieL25D$RV;J?~zhy}W$>`US*y+eYa?Y@D+x23ILCh>#El z#;&|CAaqXC1w5&+5JKZ3NxMJ&V z_lja%+(gLk0dvNfyHg%QpEl=WCcpFWq9q;V3Cq&t7PgAE47Amb=awTkTvFfb$|qz0 zJj%R|-81o38n8mZz2enM9>~x2E9GRA6j9p3q;PPLS6~)n*$O5Ch=0MPPR8*B9B&8) z8?!_z*j>YvGLe+fwo+1KfBwnxLfcA9O}OQ!2`ToLjqk1Q;|b!S+C3svV>**X8el#@ znqYY~mL)ECl!?FD5n{l{0`6Ar<6-PnGSo#5Q9+6S!Nbe9I4FH&+zz;`USJQ)D~WJK)++ZR zyo7j@Q>sdMUl8s^9&<-H_bppfWqG|Ya*DsC5G*k2RPA(HAQ{Dx{B~TlyL<2?r>B@o2|mYFHiQ${ z)X@$3ugUGxIl06zT|Z+z$uC~0Il`k930@%$d5EM8Q{hhl0i7honBTXHne&vI(`-28T z|F93`;t;}!8l7MN4_)lU>)^hqsOTQ&n!x<39d~Cqx+pY@QH4=iwu(Vche~a9BRCdT z!cDRV8gW7c*W{1hz1ZxnV>2oDh%@{tj?MKM57ae{KwMM}(w9&X5T{dkBB75Ef7at& z3e#FHc{V!ER2NKa?%X>e7Zon)9x8SjS<#RI9;sd*OeW%VFBM9)%r6Fl$e`co%oLtR zWsQpmqLfvcrQ%Vl#W`Y6o>;0&T`sJxqg0=}r07eT$776W8dnQTkbxSjK!Yr5K#gYt zXteRPmr8l{I|>R>e{kQ`3i)gb`Q)L>mTV^l6eR8L-PIY$Ecum{=)Vy@gZ}gbSW##R zTq*FNEgM;APXwvaTId<_w`6n&Sity7fl5NMl}c7ji%3@cx&AmAyQ(Dy9@LLYP7SvFIA034-2YxfECDIaC4ViIa~(y@mLy8Xysu^z`D!yF*x@_yH*K{i7+^ zgw$n(k#4J7-KoTzqFRF(n85a746WWU%pkcR^ zpF@UdVhu~5PgzSHOQ}*uo%!JVw+peAsg5{i4(jmB+xrLp)<lgSa7RxTU~1`xYxL?E;bh4Y}^)C z8^Hu<-!qSzT`%+{q)0zrk-t9_dKPE9iT+bKW8C}3)|_(@lCZdsM3%J@3n&2x^%*{g z(UO%j*8vX=)2ul3D?>;4WB@_Sm{ed5!GeMRnnntiJ*46#RvgeygSKUI!Xbp)4@bGl zZ@X8k=i*_=%Bj6|Q+sQtBBHT+Rd~5>Dpay-=3x>|JUBJ zcBQc-KM%j6B^rhZ%ELS^1n;^^jQ1+Ah{-wYdgh>j;RqvP(2!01?_00#=@|xeKir!y z&dD*_(_P(DT~%FOT~$p}gR}^`4Ohr6N5IOVoQ6Y5YODq1()LDzF7F@Im*w4~ZWC<^ z3VZi%G@~YgU}urn0Z)bbM&aFPo+g^+?~in`*oa9!w2j&XkGpL;QKV*Q2vPt!gu%Aa zD-Ac9p_Tx4q1{F=OH((!GK(rgs~Katuq#Qfho|z=N@iKf4l4`wB# z#!B`GGbF$@1a6z(Ecy;Xue5=2cxxqrLHNy55OMpR)sg~bmo`M}UvfKR`AZx!Y?PCm zi9hY5Z2TynuH-`fk{H3FIr#fmyW;(lClDweRkSlTIzSt98R#Pk?V(p3EIw|A8&rx zkjUN8%2g3O`;0#Oj6VB}Mtw$C`gYo7Xw{3BUH4}|KTmcy1?iVi`N>YGgDXE?5q6m{ zRxTT0=$O8V>EBkR|LSkykdRo-R^!mBJCRRJtV-8Ag>^AYQWSI;lgan~I8M5-RZ=dd zQR??%zU?{= zvV$>yqGn=c1k6T&9@JadRoMTBla6=R__NXc+;9q!(pKxlaf;ER#-ZbsB1MQefM*v$ zKYM4#F!HdMqlVY{1Zq>kF4=FM05>0X>P4#?yS!xe1?8vY!{S)IL80ypK$zl2vwecD zE_Q!ZhIyjwZ=%A}`u^UD{aIA1;TA%$|2o?{Hs$XMawfP<#-Y?*fLX{~KHZp*x}ExA zwlYeaN>ffW*JDn75^+vCJG?$aK(ujs&jLKyW$%6$~=f`3pG>F}wdsDQf6v>8E6-dC+NM zBB^88KUFvWs@{LS>!QtQH~vMGA8#NT%PLB$>EkKo7W1GE=J<2;$0_Thdg?vP#0AK) zL?Wi~x(T;7Kt}34GI+2EgYKBXubH5-aW^p)*&L|>TqaK!KA1uU7MyAKn*vrNry#VD zqbGu|_D=VYj?ikW0o^YL1Qc|MPDAL3jRNUf$0-kSC$2G4=e$l8(ZAGBH3wW?UMpNA zeB4C=Dn+>E6ee&*B6gXSbD^BdS;8q7;R)Pz_2-}`xW#OEW-j~zG$5vQoKMXtVwGPJ zARzND2G*(YRg(0k<2!EVWIOE# zfhUDmSQ@KC25)bC4fF`ViuF{z!k*9&f&Uv%PUY&TfU*fhzzSJopTPDAb_-xZGwK26 zfqQXZVi(wwM@R7&W{}E#Nlmi`2BS3|)L_43gj%r;@)9|d00W`Zcvk6{<1NSuhJ>K^e#BNfUKorp7uKLcWyReLF z%&G62n`B3#3UhuxhxZo6srhEDEXNU8d_KPbCatm5JfCltE~??H&Ekdisu+1yvR+-t zDF%NmX_g*rlKc@}N?^WLJ~F3@>^)4ff3p^4hzCI#tQfHQNF*$J-(#1>#0s=ia*3#` zpdyVxC0;xg1PzLq9j)wPu7*7Bsj!IkW2~{p?zp0A8E+e|Ffq5mVR|c?_9U@_)t)R> zmI)Hn7+H=O4ksJFf9(zZ!7bixIk@%cP=N|rLQHUsSPJunzX`|6EU^%A0krhsft2-5 zJME*!VUADioyOVmG3;B-Pa`%2a71$GF+_W#Qr($uroreYHXQv~L%`szX<~5}e<3jP z(Z_?fRY3#GY3vGS`CH!Q6n^7=^SIe&pF|KETz1sx)Z52<`*p9;Y}BPbR|W|UJ3d%? zxQVPN-0Z{AF|>=kut#?ZGTHuM{FPCU15Fq=Rnn}bnSBMXTM&W2EPxYYvdLzR@bIe4 zycO2&WAfpo?w1hyWSmIoTK^>1a_^*hPDFkS#aH zsH!6g(Ii&zxMRS+4Ca(PdM=(XS|`%f8_x!>E7BJa0Qzc`|Ko44--HJ7B1vFafvDO(Ghx%1e=>`SM6wjr3Jty zFwGeS`h;ErWbogz`j+c*G<^+vLY$Y8k0QaCv}VQ<-CW|_WBsb_)*NE{&C{@*jBeAj zg?H={&`~4QRig#PctPPJhEGJwfC$UQLFZ!q^=<~wpLU1<_G2@K5>S%SOfqaA7(BybZ+Pvq!d{K zno~xp*OPjN{GfmG97(DiNaX;x0>IK-7ezPPcR+f*6!G;k65<#KGY~LCvQtg5@M=6! zZkm@*hML7w_Cl7GwnZ#rDT#|kApZ4JfZ#kW2ZCJi1Sm`%8cae3F?A;6;p=jAA})mq zmmqPt42hJ(IWlnV$7^wK=MzH}nY#c)r+|J8PH0no99}5`Z#B5mYH&Ik_2UC%Bd$P8 zMC3>iDRL?=U&50TpA)Pk6SAPD9m6}6N3fuiAl{+{-D(l zP)k>bOg6pt!RNM7<*7>O9>fZFJ|6+&ESTvrqq(1%F=!ctIMwi9GK*~PEM$!a>ja~I zf5H~fXc%OC!YQqZNs$s$GtE&=PA3?Yk1=kEp)+bPhnezu;Px!8Ey>iOb84*E)=;zX zDh9lYCwbnY{X`>v$QPBOd&nin_&eYEp)E+C3IzQT`Hw$kSat-`TJo^8%HpyNsiC{= z??3~p!n@GkEevLRJE5~fQLq?->;bI(Bk#{G#)!tT^ujG9l|;8z7J1cCL$4^NGRmSZ zXDUoe;peaiH9-`yWH!w{MjWA>8qRo2tThi|ZcF_TdBXB2W-}0LL5u>mg}}s5FN#5t zM+-p7lR6RTBfwCO#i8+OK{7aFPfms#5yDB5i$!Dfh-;&_78j6+>_)TGuJ7%CtRE1j zZFaU@+!r#Z^fQq_gyD=?*A6P^F=f#=Y;NN34{})$ldm|A=kxT}uF~OeItH9*F7&y7 z_$3Z#;q7vNT_5QG_4f7aW%=KC%5UFj|F6Q^w>xzIjFx0u6XJ4qvut!$DbBf)+?~X2@!DF}eE%D@f;k5gB1JnO=L*G{FMe2o8 zCwKxIzx^=rwmYo@kXr@l`ZDYcLXpk>mTGZ(?Ikq>=sxD;0_6ldakFYet?Q<&A>$H> zF!<8BZ@1GPUU)Si&$DiDBZ(4eVAA&(^r9Kx&f`nAWq64&68e>)QC9`|=q67>1%HPp zKM+ypI4!xBgrLca*zT?zUTjJdB% z+T=!*KnBm3dyR(U;Vi3R1hV8i&LLiTsqg2cTV#g%r8QpKF_{71JC1!!o}-lBj8T0X z3w6ynYU$y0b-83vOY`A*dP`fZ5m$0G3}0==S0YWv2qrz32d_a(^Zh?6qYE>9HC-55 zedps~x1j}FM%>JxaxiBGM#$+B=dlk%V+_<`rY*mqtwgbOle)AV<|4Sca6(e7+Y||5~nIp&f_0VF5jm@Aba-FMq@Tb*D zDutRK)!CIj7!}amu!463eCYCMa|A8ahs&%FQxmFIkQ&?)#teefr?KGuL%qH#>82%a z3c3xQle9o#+SS8KQwqV}u)lB;VrIZP4_RMJS5u^Xsg_EwuY-0~O|k0I$`a=Rbxl2B`I-qzkoCb(znPhSX2)0tR8FRzQ&nHS^ zhKTAL`}FXwxV#zww}>vr8%P`vjEN^F*CIS-&gygxoQ#lW?bDB6rg1+%&(HJo X{5(I;&-3&A{MzS#Tj9>~0H6Q>_ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* + +Collection of conversion routines from various sources .. +All provided under GPL or "as-is" licenses. + +*/ + +#include +#include + +int convert_rgb_to_yuv_pixel(int r, int g, int b) +{ + unsigned int pixel32 = 0; + unsigned char *pixel = (unsigned char *)&pixel32; + int y, u, v; + + y = 0.299 * (r - 128) + 0.587 * (g - 128) + 0.114 * (b - 128) + 128; + u = - 0.147 * (r - 128) - 0.289 * (g - 128) + 0.436 * (b - 128) + 128; + v = 0.615 * (r - 128) - 0.515 * (g - 128) - 0.100 * (b - 128) + 128; + + if(y > 255) y = 255; + if(u > 255) u = 255; + if(v > 255) v = 255; + if(y < 0) y = 0; + if(u < 0) u = 0; + if(v < 0) v = 0; + + pixel[0] = y; + pixel[1] = u; + pixel[2] = v; + + return pixel32; +} + + +int convert_rgb_to_yuv_buffer(unsigned char *rgb, unsigned char *yuv, unsigned int width, unsigned int height) +{ + unsigned int in, out = 0; + unsigned int pixel32; + int y0, u0, v0, y1, u1, v1; + + for(in = 0; in < width * height * 3; in += 6) { + pixel32 = convert_rgb_to_yuv_pixel(rgb[in], rgb[in + 1], rgb[in + 2]); + y0 = (pixel32 & 0x000000ff); + u0 = (pixel32 & 0x0000ff00) >> 8; + v0 = (pixel32 & 0x00ff0000) >> 16; + + pixel32 = convert_rgb_to_yuv_pixel(rgb[in + 3], rgb[in + 4], rgb[in + 5]); + y1 = (pixel32 & 0x000000ff); + u1 = (pixel32 & 0x0000ff00) >> 8; + v1 = (pixel32 & 0x00ff0000) >> 16; + + yuv[out++] = y0; + yuv[out++] = (u0 + u1) / 2; + yuv[out++] = y1; + yuv[out++] = (v0 + v1) / 2; + } + + return 0; +} + +int convert_yuv_to_rgb_pixel(int y, int u, int v) +{ + unsigned int pixel32 = 0; + unsigned char *pixel = (unsigned char *)&pixel32; + int r, g, b; + + r = y + (1.370705 * (v-128)); + g = y - (0.698001 * (v-128)) - (0.337633 * (u-128)); + b = y + (1.732446 * (u-128)); + + if(r > 255) r = 255; + if(g > 255) g = 255; + if(b > 255) b = 255; + if(r < 0) r = 0; + if(g < 0) g = 0; + if(b < 0) b = 0; + + pixel[0] = r * 220 / 256; + pixel[1] = g * 220 / 256; + pixel[2] = b * 220 / 256; + + return pixel32; +} + + +int convert_yuv_to_rgb_buffer(unsigned char *yuv, unsigned char *rgb, unsigned int width, unsigned int height) +{ + unsigned int in, out = 0; + unsigned int pixel_16; + unsigned char pixel_24[3]; + unsigned int pixel32; + int y0, u, y1, v; + + for(in = 0; in < width * height * 2; in += 4) { + pixel_16 = + yuv[in + 3] << 24 | + yuv[in + 2] << 16 | + yuv[in + 1] << 8 | + yuv[in + 0]; + + y0 = (pixel_16 & 0x000000ff); + u = (pixel_16 & 0x0000ff00) >> 8; + y1 = (pixel_16 & 0x00ff0000) >> 16; + v = (pixel_16 & 0xff000000) >> 24; + + pixel32 = convert_yuv_to_rgb_pixel(y0, u, v); + pixel_24[0] = (pixel32 & 0x000000ff); + pixel_24[1] = (pixel32 & 0x0000ff00) >> 8; + pixel_24[2] = (pixel32 & 0x00ff0000) >> 16; + + rgb[out++] = pixel_24[0]; + rgb[out++] = pixel_24[1]; + rgb[out++] = pixel_24[2]; + + pixel32 = convert_yuv_to_rgb_pixel(y1, u, v); + pixel_24[0] = (pixel32 & 0x000000ff); + pixel_24[1] = (pixel32 & 0x0000ff00) >> 8; + pixel_24[2] = (pixel32 & 0x00ff0000) >> 16; + + rgb[out++] = pixel_24[0]; + rgb[out++] = pixel_24[1]; + rgb[out++] = pixel_24[2]; + } + + return 0; +} + +static inline void move_420_block (int yTL, int yTR, int yBL, int yBR, int u, + int v, int rowPixels, unsigned char *rgb, + int bits); + +#define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16))) + +void +yuv420p_to_rgb (unsigned char *image, unsigned char *image2, int x, int y, int z) { + const int numpix = x * y; + const int bytes = z; /* (z*8) >> 3; */ + int i, j, y00, y01, y10, y11, u, v; + unsigned char *pY = image; + unsigned char *pU = pY + numpix; + unsigned char *pV = pU + numpix / 4; + + for (j = 0; j <= y - 2; j += 2) { + for (i = 0; i <= x - 2; i += 2) { + y00 = *pY; + y01 = *(pY + 1); + y10 = *(pY + x); + y11 = *(pY + x + 1); + u = (*pU++) - 128; + v = (*pV++) - 128; + + move_420_block (y00, y01, y10, y11, u, v, x, image2, z * 8); + + pY += 2; + image2 += 2 * bytes; + } + pY += x; + image2 += x * bytes; + } +} + +void move_420_block (int yTL, int yTR, int yBL, int yBR, int u, int v, + int rowPixels, unsigned char *rgb, int bits) +{ + const int rvScale = 91881; + const int guScale = -22553; + const int gvScale = -46801; + const int buScale = 116129; + const int yScale = 65536; + int r, g, b; + + g = guScale * u + gvScale * v; + if (1) { + r = buScale * u; + b = rvScale * v; + } else { + r = rvScale * v; + b = buScale * u; + } + + yTL *= yScale; + yTR *= yScale; + yBL *= yScale; + yBR *= yScale; + + if (bits == 24) { + /* Write out top two pixels */ + rgb[0] = LIMIT (b + yTL); + rgb[1] = LIMIT (g + yTL); + rgb[2] = LIMIT (r + yTL); + + rgb[3] = LIMIT (b + yTR); + rgb[4] = LIMIT (g + yTR); + rgb[5] = LIMIT (r + yTR); + + /* Skip down to next line to write out bottom two pixels */ + rgb += 3 * rowPixels; + rgb[0] = LIMIT (b + yBL); + rgb[1] = LIMIT (g + yBL); + rgb[2] = LIMIT (r + yBL); + + rgb[3] = LIMIT (b + yBR); + rgb[4] = LIMIT (g + yBR); + rgb[5] = LIMIT (r + yBR); + } else if (bits == 16) { + /* Write out top two pixels */ + rgb[0] = ((LIMIT (b + yTL) >> 3) & 0x1F) + | ((LIMIT (g + yTL) << 3) & 0xE0); + rgb[1] = ((LIMIT (g + yTL) >> 5) & 0x07) + | (LIMIT (r + yTL) & 0xF8); + + rgb[2] = ((LIMIT (b + yTR) >> 3) & 0x1F) + | ((LIMIT (g + yTR) << 3) & 0xE0); + rgb[3] = ((LIMIT (g + yTR) >> 5) & 0x07) + | (LIMIT (r + yTR) & 0xF8); + + /* Skip down to next line to write out bottom two pixels */ + rgb += 2 * rowPixels; + + rgb[0] = ((LIMIT (b + yBL) >> 3) & 0x1F) + | ((LIMIT (g + yBL) << 3) & 0xE0); + rgb[1] = ((LIMIT (g + yBL) >> 5) & 0x07) + | (LIMIT (r + yBL) & 0xF8); + + rgb[2] = ((LIMIT (b + yBR) >> 3) & 0x1F) + | ((LIMIT (g + yBR) << 3) & 0xE0); + rgb[3] = ((LIMIT (g + yBR) >> 5) & 0x07) + | (LIMIT (r + yBR) & 0xF8); + } +} + diff --git a/gb.v4l/src/CWebcam.c b/gb.v4l/src/CWebcam.c new file mode 100644 index 00000000..d05e637c --- /dev/null +++ b/gb.v4l/src/CWebcam.c @@ -0,0 +1,1906 @@ +/*************************************************************************** + + CWebcam.c + + (C) 2005-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWEBCAM_C + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_STDLIB_H +#undef HAVE_STDLIB_H +#endif + +#ifdef EXTERN +#undef EXTERN +#endif + +#ifdef INLINE +#undef INLINE +#endif + +#include "main.h" +#include "CWebcam.h" + +#define DEF_WIDTH 320 +#define DEF_HEIGHT 240 +// default colour depth (changing this will break everything) +#define DEF_DEPTH 3 + +#define FMT_UNKNOWN 0 +#define FMT_PPM 1 +#define FMT_PNG 2 +#define FMT_JPEG 3 +#define FMT_DEFAULT FMT_PNG + +#define IN_TV 0 +#define IN_COMPOSITE1 1 +#define IN_COMPOSITE2 2 +#define IN_SVIDEO 3 +#define IN_DEFAULT IN_COMPOSITE1 + +#define NORM_PAL 0 +#define NORM_NTSC 1 +#define NORM_SECAM 2 +#define NORM_AUTO 3 +#define NORM_DEFAULT NORM_PAL + +#define QUAL_DEFAULT 80 + +GB_STREAM_DESC VideoStream = +{ + open: Video_stream_open, + close: Video_stream_close, + read: Video_stream_read, + write: Video_stream_write, + seek: Video_stream_seek, + tell: Video_stream_tell, + flush: Video_stream_flush, + eof: Video_stream_eof, + lof: Video_stream_lof, + handle: Video_stream_handle +}; + +extern bool gv4l2_debug_mode; // ++ + +/*********************************************************************************** + + Camera setup + +************************************************************************************/ + +static int vd_ioctl(video_device_t *vd, int cmd, void *arg) +{ + return ioctl(vd->dev, cmd, arg); +} + +static void vd_close(video_device_t *vd) +{ + if (vd->frame_buffer) + { + if (vd->use_mmap) munmap(vd->frame_buffer, vd->vmbuf.size); + else GB.Free(POINTER(&vd->frame_buffer)); + } + close(vd->dev); +} + +static video_device_t *vd_setup(int width, int height, int depth, int dev) +{ + video_device_t *vd; + + GB.Alloc(POINTER(&vd),sizeof(video_device_t)); + + vd->width = width; + vd->height = height; + vd->depth = depth; + vd->buffer_size = width * height * depth; + vd->dev = dev; + vd->use_mmap = 0; + vd->capturing = 0; + vd->frame_buffer=NULL; + + return vd; +} + +static int vd_get_capabilities(video_device_t *vd) +{ + if (vd_ioctl(vd, VIDIOCGCAP, &vd->vcap)) return 0; + + if (!(vd->vcap.type & VID_TYPE_CAPTURE)) vd->use_mmap = 0; + else vd->use_mmap = 1; + + if (vd->width > vd->vcap.maxwidth) vd->width=vd->vcap.maxwidth; + if (vd->width < vd->vcap.minwidth) vd->width=vd->vcap.minwidth; + if (vd->height > vd->vcap.maxheight) vd->height=vd->vcap.maxheight; + if (vd->height < vd->vcap.minheight) vd->height=vd->vcap.minheight; + + return 1; +} + + +// -- int vd_setup_capture_mode(video_device_t *vd) + +static int vd_setup_capture_mode(CWEBCAM *_object) // ++ +{ + video_device_t *vd = DEVICE; + + if (!vd_get_capabilities(vd)) return 0; + + // See if we can use mmap (to avoid copying data around) + // VIDIOCGMBUF tells us how many frames it is going to buffer + // for us, and we have to use them all!!! ??? + if (vd_ioctl(vd, VIDIOCGMBUF, &vd->vmbuf)) + { + if (vd->use_mmap) + { + if (vd->frame_buffer) + { + munmap(vd->frame_buffer, vd->vmbuf.size); + vd->frame_buffer=NULL; + } + vd->use_mmap = 0; + } + + // Issue VIDIOCGWIN to tell the driver what geometry we + // expect from read() + if (!vd_ioctl(vd, VIDIOCGWIN, &vd->vwin)) + { + vd->vwin.width = vd->width; + vd->vwin.height = vd->height; + if (vd_ioctl(vd, VIDIOCSWIN, &vd->vwin)) return 0; + if (vd_ioctl(vd, VIDIOCSWIN, &vd->vwin)) return 0; + vd->buffer_size = vd->height * vd->width; + } + + if (vd->frame_buffer) GB.Free(POINTER(&vd->frame_buffer)); + if (THIS->frame) GB.Free(POINTER(&THIS->frame)); // ++ + GB.Alloc(POINTER(&vd->frame_buffer),vd->buffer_size); + GB.Alloc(POINTER(&THIS->frame),vd->height * vd->width * 4); // ++ + return 1; + } + + // mmap is okay! + if (!vd->use_mmap) + { + if (vd->frame_buffer) GB.Free(POINTER(&vd->frame_buffer)); + vd->use_mmap = 1; + } + + vd->frame_buffer = mmap(0, vd->vmbuf.size, PROT_READ|PROT_WRITE, MAP_SHARED, vd->dev, 0); + vd->vmmap.format = VIDEO_PALETTE_RGB24; // KLUDGE ... + vd->vmmap.frame = 0; // Start at frame 0 + vd->vmmap.width = vd->width; + vd->vmmap.height = vd->height; + + if (THIS->frame) GB.Free(POINTER(&THIS->frame)); // ++ + GB.Alloc(&THIS->frame, vd->height * vd->width * 4); // ++ + + ioctl(vd->dev, VIDIOCGPICT, &vd->videopict); //++ Recover camera palette + vd->vmmap.format = vd->videopict.palette; //++ Save for future ref + return 1; +} + +static int vd_setup_video_source(video_device_t *vd, int input, int norm) { + + vd->vchan.channel = input; // Query desired channel + + if (vd_ioctl(vd, VIDIOCGCHAN, &vd->vchan)) return 0; + + // Now set the channel and the norm for this channel + + vd->vchan.norm = norm; + if (vd_ioctl(vd, VIDIOCSCHAN, &vd->vchan)) return 0; + + // KLUDGE ... the API leaves colour settings and tuning undefined + // after a channel change + return 1; +} + +/*********************************************************************************** + + Image capture + +************************************************************************************/ + +static void put_image_jpeg(char *image, int width, int height, int quality, int frame,FILE *fd) +{ + int y, x, line_width; + JSAMPROW row_ptr[1]; + struct jpeg_compress_struct cjpeg; + struct jpeg_error_mgr jerr; + char *line; + + GB.Alloc( POINTER(&line) ,width * 3); + if (!line) + return; + cjpeg.err = jpeg_std_error(&jerr); + jpeg_create_compress (&cjpeg); + cjpeg.image_width = width; + cjpeg.image_height= height; + cjpeg.input_components = 3; + cjpeg.in_color_space = JCS_RGB; + jpeg_set_defaults (&cjpeg); + + jpeg_set_quality (&cjpeg, quality, TRUE); + cjpeg.dct_method = JDCT_FASTEST; + jpeg_stdio_dest (&cjpeg, fd); + + jpeg_start_compress (&cjpeg, TRUE); + + row_ptr[0] = (JSAMPROW)line; + line_width = width * 3; + for ( y = 0; y < height; y++) { + for (x = 0; x < line_width; x+=3) { + line[x] = image[x+2]; + line[x+1] = image[x+1]; + line[x+2] = image[x]; + } + jpeg_write_scanlines (&cjpeg, row_ptr, 1); + image += line_width; + } + jpeg_finish_compress (&cjpeg); + jpeg_destroy_compress (&cjpeg); + GB.Free( POINTER(&line) ); + +} + +static void put_image_png(char *image, int width, int height, int frame, FILE *fd) +{ + int y; + char *p; + png_infop info_ptr; + png_structp png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL); + if (!png_ptr) + return; + info_ptr = png_create_info_struct (png_ptr); + if (!info_ptr) + return; + + png_init_io (png_ptr, fd); + png_set_IHDR (png_ptr, info_ptr, width, height, + 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_set_bgr (png_ptr); + png_write_info (png_ptr, info_ptr); + p = image; + for (y = 0; y < height; y++) { + png_write_row (png_ptr, (unsigned char*)p); + p+=width*3; + } + png_write_end (png_ptr, info_ptr); +} + +/* + * write ppm image to stdout + */ +#define WRITE_MODE "w" + +static void put_image_ppm_buffer(char *image, int width, int height, int frame, int *len,void *_object) +{ + int x; + int htot=width*height; + unsigned char *p = (unsigned char *)image; + unsigned char *bp; + + *len=(3 * htot)+15; + if (!THIS->membuf) GB.Alloc(POINTER(&THIS->membuf),((*len)*sizeof(unsigned char*))); + sprintf((char*)THIS->membuf, "P6\n%d %d\n%d\n", width, height, 255); + bp=THIS->membuf+strlen((const char*)THIS->membuf); + for (x = 0; x < htot; x++) { + *bp++ = p[2]; + *bp++ = p[1]; + *bp++ = p[0]; + p += 3; + } +} + +static void put_image_ppm(char *image, int width, int height, int binary, int frame,FILE *out_fp) +{ + int x, y, ls=0; + unsigned char *p = (unsigned char *)image; + + if (!binary) + { + fprintf(out_fp, "P3\n%d %d\n%d\n", width, height, 255); + for (x = 0; x < width; x++) + { + for (y = 0; y < height; y++) + { + fprintf(out_fp, "%03d %03d %03d ", p[2], p[1], p[0]); + p += 3; + if (ls++ > 4) + { + fprintf(out_fp, "\n"); + ls = 0; + } + } + } + fprintf(out_fp, "\n"); + } + else + { + unsigned char buff[3 * width * height]; + unsigned char *bp = buff; + + fprintf(out_fp, "P6\n%d %d\n%d\n", width, height, 255); + for (x = 0; x < width * height; x++) { + *bp++ = p[2]; + *bp++ = p[1]; + *bp++ = p[0]; + p += 3; + } + fwrite(buff, width * height, 3, out_fp); + } +} + +//unsigned char * vd_get_image(video_device_t *vd) + +static unsigned char *vd_get_image(CWEBCAM *_object) +{ + int len; + video_device_t *vd; + + vd = DEVICE; + + if (vd->use_mmap) { + if (!vd->capturing) { + + int i; + // Queue requests to capture successive frames + for (i = 0; i < vd->vmbuf.frames; ++i) { + vd->vmmap.frame = i; + if(vd_ioctl(vd, VIDIOCMCAPTURE, &vd->vmmap)) + return 0; + } + // And start reading from zero + vd->vmmap.frame = 0; + vd->capturing = 1; + } + // VIDIOCSYNC causes the driver to block until the specified + // frame is completely received + if (ioctl(vd->dev, VIDIOCSYNC, &vd->vmmap.frame)) return 0; + gv4l1_process_image (THIS,vd->frame_buffer + vd->vmbuf.offsets[vd->vmmap.frame]); + + //vd_post_process(vd,vd->frame_buffer + vd->vmbuf.offsets[vd->vmmap.frame]); + return THIS->frame; + + // Return the buffer, cause it should contain an image + //return vd->frame_buffer + vd->vmbuf.offsets[vd->vmmap.frame]; + } + + // Otherwise, we have to read the right number of bytes + + len = read(vd->dev, vd->frame_buffer, vd->buffer_size); + if (len <= 0) { + return 0; + } + + if (len != vd->buffer_size) return 0; + + return vd->frame_buffer; +} + +static int vd_image_done(video_device_t *vd) +{ + if (vd->use_mmap) + { + // vd->vmmap.frame contains the index of the recently-used buffer + // So tell the driver to reuse this one for the next frame + + if (ioctl(vd->dev, VIDIOCMCAPTURE, &vd->vmmap)) return 0; + + // Now cycle the frame number, so we sync the next frame + if (++vd->vmmap.frame >= vd->vmbuf.frames) { + vd->vmmap.frame = 0; + } + } + + return 1; +} + +/*********************************************************************************** + +Stream interface + +************************************************************************************/ + +static int fill_buffer(void *_object) +{ + char *buf; + int w,h; + + // -- buf=(char*)vd_get_image(DEVICE); + buf=(char*)vd_get_image(THIS); // ++ + if (!buf) return -1; + w=DEVICE->vmmap.width; + h=DEVICE->vmmap.height; + vd_image_done(DEVICE); + put_image_ppm_buffer (buf,w,h,0,&THIS->gotframe,_object); + THIS->posframe=0; + return 0; +} + +int Video_stream_read(GB_STREAM *stream, char *buffer, int len) +{ + void *_object=(void*)((VIDEO_STREAM*)stream)->handle; + + if (!_object) return -1; + if (!DEVICE) return -1; + + if (!THIS->gotframe) + if ( fill_buffer(_object) ) return -1; + + if ((len+THIS->posframe)>THIS->gotframe) return -1; + memcpy (buffer,THIS->membuf+THIS->posframe,len); + THIS->posframe+=len; + return 0; +} + +int Video_stream_eof(GB_STREAM *stream) +{ + void *_object=(void*)((VIDEO_STREAM*)stream)->handle; + + if (!_object) return -1; + if (!DEVICE) return -1; + + if (!THIS->gotframe) return 0; + + if (THIS->gotframe<=THIS->posframe) return -1; + return 0; +} + +int Video_stream_lof(GB_STREAM *stream, int64_t *len) +{ + void *_object=(void*)((VIDEO_STREAM*)stream)->handle; + + if (!_object) return -1; + if (!DEVICE) return -1; + + if (!THIS->gotframe) + if ( fill_buffer(_object) ) return -1; + + *len=(long long)THIS->gotframe; + return 0; +} + +int Video_stream_seek(GB_STREAM *stream, int64_t pos, int whence) +{ + void *_object=(void*)((VIDEO_STREAM*)stream)->handle; + + if (!_object) return -1; + if (!DEVICE) return -1; + + if (!THIS->gotframe) + if ( fill_buffer(_object) ) return -1; + + if (pos<0) return -1; + THIS->posframe=pos; + return 0; +} + +int Video_stream_tell(GB_STREAM *stream, int64_t *pos) +{ + void *_object=(void*)((VIDEO_STREAM*)stream)->handle; + + if (!_object) return -1; + if (!DEVICE) return -1; + + *pos=(long long)THIS->posframe; + return 0; + +} + +int Video_stream_flush(GB_STREAM *stream) +{ + void *_object=(void*)((VIDEO_STREAM*)stream)->handle; + + if (!_object) return -1; + if (!DEVICE) return -1; + + THIS->gotframe=0; + THIS->posframe=0; + return 0; +} + +int Video_stream_open(GB_STREAM *stream, const char *path, int mode, void *data) +{ + return -1; +} + +int Video_stream_close(GB_STREAM *stream) +{ + return -1; +} + +int Video_stream_write(GB_STREAM *stream, char *buffer, int len) +{ + return -1; +} + +int Video_stream_handle(GB_STREAM *stream) +{ + return 0; +} + +/*********************************************************************************** + + Gambas interface + +************************************************************************************/ + +int CWEBCAM_check(void *_object) +{ + //if((!DEVICE)&&(!THIS->is_v4l2)) return TRUE; + if(!THIS->device) return TRUE; // ++ V4L2 + return FALSE; +} + +static void handle_min(void *_object, int min) +{ + GB.ReturnInteger(THIS->is_v4l2 ? min : 0); +} + +static void handle_max(void *_object, int max) +{ + GB.ReturnInteger(THIS->is_v4l2 ? max : 65535); +} + +static void handle_default(void *_object, int min, int max, int def) +{ + if (!THIS->is_v4l2) + GB.ReturnInteger(32767); + else + { + if (!def) + GB.ReturnInteger((max - min) / 2); + else + GB.ReturnInteger(def); + } +} + +BEGIN_PROPERTY(VideoDevice_Contrast) + + if (!THIS->is_v4l2 ) + { + vd_ioctl(DEVICE, VIDIOCGPICT, &DEVICE->videopict); + + if (READ_PROPERTY) + GB.ReturnInteger(DEVICE->videopict.contrast); + else + { + DEVICE->videopict.contrast=VPROP(GB_INTEGER); + vd_ioctl (DEVICE, VIDIOCSPICT, &DEVICE->videopict); + } + } + else + { + if (READ_PROPERTY) + GB.ReturnInteger(gv4l2_contrast(THIS, -1)); + else + gv4l2_contrast(THIS, VPROP(GB_INTEGER)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_ContrastMax) + + handle_max(THIS, THIS->contrast_max); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_ContrastMin) + + handle_min(THIS, THIS->contrast_min); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_ContrastDefault) + + handle_default(THIS, THIS->contrast_min, THIS->contrast_max, THIS->contrast_def); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_Color) + + if( !THIS->is_v4l2 ) + { + vd_ioctl (DEVICE, VIDIOCGPICT, &DEVICE->videopict); + + if (READ_PROPERTY) + GB.ReturnInteger(DEVICE->videopict.colour); + else + { + DEVICE->videopict.colour=VPROP(GB_INTEGER); + vd_ioctl (DEVICE, VIDIOCSPICT, &DEVICE->videopict); + } + } + else + { + if (READ_PROPERTY) + GB.ReturnInteger(gv4l2_color(THIS, -1)); + else + gv4l2_color(THIS, VPROP(GB_INTEGER)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_ColorMax) + + handle_max(THIS, THIS->color_max); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_ColorMin) + + handle_min(THIS, THIS->color_min); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_ColorDefault) + + handle_default(THIS, THIS->color_min, THIS->color_max, THIS->color_def); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_Whiteness) + + if (!THIS->is_v4l2 ) + { + vd_ioctl (DEVICE, VIDIOCGPICT, &DEVICE->videopict); + if (READ_PROPERTY) + GB.ReturnInteger(DEVICE->videopict.whiteness>>8); + else + { + DEVICE->videopict.whiteness=VPROP(GB_INTEGER); + vd_ioctl (DEVICE, VIDIOCSPICT, &DEVICE->videopict); + } + } + else + { + if (READ_PROPERTY) + GB.ReturnInteger(gv4l2_whiteness(THIS, -1)); + else + gv4l2_whiteness(THIS, VPROP(GB_INTEGER)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_WhitenessMax) + + handle_max(THIS, THIS->whiteness_max); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_WhitenessMin) + + handle_min(THIS, THIS->whiteness_min); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_WhitenessDefault) + + handle_default(THIS, THIS->whiteness_min, THIS->whiteness_max, THIS->whiteness_def); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_Hue) + + if( !THIS->is_v4l2 ) + { + vd_ioctl (DEVICE, VIDIOCGPICT, &DEVICE->videopict); + if (READ_PROPERTY) + GB.ReturnInteger(DEVICE->videopict.hue>>8); + else + { + DEVICE->videopict.hue=VPROP(GB_INTEGER); + vd_ioctl (DEVICE, VIDIOCSPICT, &DEVICE->videopict); + } + } + else + { + if (READ_PROPERTY) + GB.ReturnInteger(gv4l2_hue(THIS, -1)); + else + gv4l2_hue(THIS, VPROP(GB_INTEGER)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_HueMax) + + handle_max(THIS, THIS->hue_max); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_HueMin) + + handle_min(THIS, THIS->whiteness_min); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_HueDefault) + + handle_default(THIS, THIS->hue_min, THIS->hue_max, THIS->hue_def); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_Brightness) + + if (!THIS->is_v4l2) + { + vd_ioctl (DEVICE, VIDIOCGPICT, &DEVICE->videopict); + if (READ_PROPERTY) + GB.ReturnInteger(DEVICE->videopict.brightness); + else + { + DEVICE->videopict.brightness=VPROP(GB_INTEGER); + vd_ioctl (DEVICE, VIDIOCSPICT, &DEVICE->videopict); + } + } + else + { + if (READ_PROPERTY) + GB.ReturnInteger(gv4l2_brightness(THIS, -1)); + else + gv4l2_brightness(THIS, VPROP(GB_INTEGER)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_BrightnessMax) + + handle_max(THIS, THIS->bright_max); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_BrightnessMin) + + handle_min(THIS, THIS->bright_min); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_BrightnessDefault) + + handle_default(THIS, THIS->bright_min, THIS->bright_max, THIS->bright_def); + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_Width) + + if (THIS->is_v4l2) + GB.ReturnInteger(THIS->fmt.fmt.pix.width); + else + GB.ReturnInteger(DEVICE->width); + +END_PROPERTY + +BEGIN_PROPERTY(VideoDevice_Height) + + if (THIS->is_v4l2) + GB.ReturnInteger(THIS->fmt.fmt.pix.height); + else + GB.ReturnInteger(DEVICE->height); + +END_PROPERTY + + +BEGIN_METHOD(VideoDevice_new, GB_STRING Device; GB_INTEGER Compat) + + struct video_tuner vtuner; + VIDEO_STREAM *str; + + // ++ V4L2 + // + // Open the device + // + THIS->device = GB.NewString(STRING(Device), LENGTH(Device)); + + THIS->io = gv4l2_open_device(THIS->device); + if (THIS->io == -1) + { + GB.Error("Unable to open device"); + return; + } + + switch (VARGOPT(Compat, 0)) + { + case MODE_ANY: + THIS->is_v4l2 = gv4l2_available( THIS ); + break; + case MODE_V4L: + THIS->is_v4l2 = 0; + break; + case MODE_V4L2: + THIS->is_v4l2 = 1; + break; + default: + GB.Error("Invalid mode flag"); + goto __ERROR; + } + + if (THIS->is_v4l2 ) + { + gv4l2_debug("Device is V4L2!"); + // + // Initialise the device + // + if(!gv4l2_init_device(THIS,DEF_WIDTH,DEF_HEIGHT)) + { + GB.Error("Unable to initialise the device"); + goto __ERROR; + } + // + THIS->stream.desc=&VideoStream; + str = (VIDEO_STREAM*)POINTER(&THIS->stream); + str->handle = (void*)THIS; + // + gv4l2_start_capture(THIS); + return; + } + + gv4l2_debug("Device is V4L!"); + // mydev=open (GB.FileName(STRING(Device),LENGTH(Device)),O_RDWR); + // if (mydev==-1) + // { + // GB.Error("Unable to open device"); + // return; + //} + // -- V4L2 + + DEVICE = vd_setup(DEF_WIDTH,DEF_HEIGHT,DEF_DEPTH,THIS->io); + +//-- if (!vd_setup_capture_mode(DEVICE)) + if (!vd_setup_capture_mode(THIS)) // ++ + { + GB.Free(POINTER(&DEVICE)); + GB.Error("Unable to setup capture mode"); + goto __ERROR; + } + + vd_setup_video_source(DEVICE,IN_DEFAULT,NORM_DEFAULT); + + // -- GB.Alloc(POINTER(&THIS->device),sizeof(char)*(LENGTH(Device)+1)); + // -- strcpy(THIS->device,STRING(Device)); + + if (vd_ioctl (DEVICE, VIDIOCGTUNER, &vtuner)) DEVICE->Freq2=1; + + THIS->stream.desc=&VideoStream; + str=(VIDEO_STREAM*)POINTER(&THIS->stream); + str->handle=(void*)THIS; + return; + +__ERROR: + + close(THIS->io); + +END_METHOD + + +BEGIN_METHOD_VOID(VideoDevice_free) + + // ++ V4L2 + GB.FreeString(&THIS->device); + if (THIS->frame) + GB.Free(POINTER(&THIS->frame)); + + if (THIS->is_v4l2) + { + gv4l2_stop_capture( THIS ); + gv4l2_uninit_device( THIS ); + gv4l2_close_device( THIS->io ); + return; + } + + // --if (THIS->device) GB.Free(POINTER(&THIS->device)); + // -- V4L2 + + if (THIS->membuf) GB.Free(POINTER(&THIS->membuf)); + + if (DEVICE) + { + vd_close(DEVICE); + GB.Free(POINTER(&DEVICE)); + } + +END_METHOD + + +BEGIN_METHOD(VideoDevice_Resize, GB_INTEGER Width; GB_INTEGER Height) + + struct video_tuner vtuner; + int w=VARG(Width); + int h=VARG(Height); + int mydev; + int norm; + int channel; + int colour,hue,whiteness,contrast,brightness; + + // ++ V4L2 + if( THIS->is_v4l2 ) { + gv4l2_resize( THIS , VARG(Width) , VARG(Height) ); + return; + } + // -- V4L2 + + if (hvcap.minheight) h=DEVICE->vcap.minheight; + if (h>DEVICE->vcap.maxheight) h=DEVICE->vcap.maxheight; + if (wvcap.minwidth) w=DEVICE->vcap.minwidth; + if (w>DEVICE->vcap.maxwidth) w=DEVICE->vcap.maxwidth; + + if ( (w==DEVICE->width) && (h==DEVICE->height) ) return; + + norm=DEVICE->vchan.norm; + channel=DEVICE->vchan.channel; + + vd_ioctl (DEVICE, VIDIOCGPICT, &DEVICE->videopict); + hue=DEVICE->videopict.hue; + contrast=DEVICE->videopict.contrast; + brightness=DEVICE->videopict.brightness; + colour=DEVICE->videopict.colour; + whiteness=DEVICE->videopict.whiteness; + + if (THIS->membuf) GB.Free(POINTER(&THIS->membuf)); + vd_close(DEVICE); + GB.Free(POINTER(&DEVICE)); + + mydev=open(THIS->device,O_RDWR); + if (mydev==-1) + { + GB.Error("Unable to open device"); + return; + } + DEVICE=vd_setup(w,h,DEF_DEPTH,mydev); + +//-- if (!vd_setup_capture_mode(DEVICE)) + if (!vd_setup_capture_mode(THIS)) // ++ + { + close(mydev); + GB.Free(POINTER(&DEVICE)); + GB.Error("Unable to setup capture mode"); + return; + } + + vd_setup_video_source(DEVICE,channel,norm); + + DEVICE->videopict.hue=hue; + DEVICE->videopict.contrast=contrast; + DEVICE->videopict.brightness=brightness; + DEVICE->videopict.colour=colour; + DEVICE->videopict.whiteness=whiteness; + vd_ioctl (DEVICE, VIDIOCSPICT, &DEVICE->videopict); + + if (vd_ioctl (DEVICE, VIDIOCGTUNER, &vtuner)) DEVICE->Freq2=1; + +END_METHOD + + +BEGIN_PROPERTY(VideoDevice_Source) +/* +http://www.linuxtv.org/downloads/video4linux/API/V4L2_API/spec/index.html +*/ +// video_device_t *vd = DEVICE; +// struct video_tuner vtuner; + + int Source=0,Norm=0; + + if( THIS->is_v4l2 ) { + gv4l2_debug("'Source' not currently implemented for V4L2"); + +// BM: What is that all? I comment everything... + return; + } + +#if 0 + if (READ_PROPERTY) + { +/* +Example 1-1. Information about the current video input +struct v4l2_input input; +int index; + +if (-1 == ioctl (fd, VIDIOC_G_INPUT, &index)) { + perror ("VIDIOC_G_INPUT"); + exit (EXIT_FAILURE); +} + +memset (&input, 0, sizeof (input)); +input.index = index; + +if (-1 == ioctl (fd, VIDIOC_ENUMINPUT, &input)) { + perror ("VIDIOC_ENUMINPUT"); + exit (EXIT_FAILURE); +} + +printf ("Current input: %s\n", input.name); +*/ + + struct v4l2_input input; + int index; + index=0; +//--------------------------vvvv +/* + if (-1 == vd_ioctl (DEVICE, VIDIOC_G_INPUT, &index)) { + // perror ("VIDIOC_G_INPUT"); + // exit (EXIT_FAILURE); + } +*/ +//-------------------------^^^^^ + +/* + memset (&input, 0, sizeof (input)); + input.index = index; + + if (-1 == ioctl (fd, VIDIOC_ENUMINPUT, &input)) { + perror ("VIDIOC_ENUMINPUT"); + exit (EXIT_FAILURE); + } + printf ("Current input: %s\n", input.name; +*/ + + // if (!vd_ioctl(DEVICE, VIDIOCGCHAN, &DEVICE->vchan)) + // { +//--------------------------vvvv +/* +*/ +//-------------------------^^^^^ + switch (index)//DEVICE->vchan.channel) + { + case IN_TV: Source=0; break; + case IN_COMPOSITE1: Source=1; break; + case IN_COMPOSITE2: Source=2; break; + case IN_SVIDEO: Source=3; break; + } + gv4l2_debug("Source=" + Source); + +/* +Example 1-6. Listing the video standards supported by the current input +struct v4l2_input input; +struct v4l2_standard standard; + +memset (&input, 0, sizeof (input)); + +if (-1 == ioctl (fd, VIDIOC_G_INPUT, &input.index)) { + perror ("VIDIOC_G_INPUT"); + exit (EXIT_FAILURE); +} + +if (-1 == ioctl (fd, VIDIOC_ENUMINPUT, &input)) { + perror ("VIDIOC_ENUM_INPUT"); + exit (EXIT_FAILURE); +} + +printf ("Current input %s supports:\n", input.name); + +memset (&standard, 0, sizeof (standard)); +standard.index = 0; + +while (0 == ioctl (fd, VIDIOC_ENUMSTD, &standard)) { + if (standard.id & input.std) + printf ("%s\n", standard.name); + + standard.index++; +} + +// EINVAL indicates the end of the enumeration, which cannot be +// empty unless this device falls under the USB exception. + +if (errno != EINVAL || standard.index == 0) { + perror ("VIDIOC_ENUMSTD"); + exit (EXIT_FAILURE); +} + */ +//dbl //struct v4l2_input input; +//nu struct v4l2_standard standard; + int std; + std=0; + std=NORM_SECAM; +//--------------------------vvvv +/* + if (-1 == vd_ioctl (DEVICE, VIDIOC_G_STD, &std)) { + // perror ("VIDIOC_G_INPUT"); + // exit (EXIT_FAILURE); + } +*/ +//-------------------------^^^^^ + switch(std) //DEVICE->vchan.norm) + { + case NORM_PAL: Norm=0; break; + case NORM_NTSC: Norm=4; break; + case NORM_SECAM: Norm=8; break; + case NORM_AUTO: Norm=12; break; + } + + //printf ("Current input: %lu\n",Source); + //printf ("Current norm: %lu\n", Norm); + + GB.ReturnInteger(Source+Norm); + + return; + } + +// property write starts here ********************************************* + Source=VPROP(GB_INTEGER) & 3; + Norm=(VPROP(GB_INTEGER)>>2) & 3; +gv4l2_debug("Source write2" ); + +/* +Example 1-2. Switching to the first video input +int index; + +index = 0; + +if (-1 == ioctl (fd, VIDIOC_S_INPUT, &index)) { + perror ("VIDIOC_S_INPUT"); + exit (EXIT_FAILURE); +} +*/ + +// vd_setup_video_source(DEVICE,channel,norm); +// vd_setup_video_source(DEVICE,Source,Norm); + +//vd_setup_video_source(video_device_t *vd, int input, int norm); +//vd_setup_video_source(DEVICE, Source, Norm); + + + +//--------------------------vvvv +/* + int index; + index = 2;// Source; //0 + if (!vd_ioctl (DEVICE, VIDIOC_S_INPUT, &DEVICE->vchan.channel)) { + gv4l2_debug ("VIDIOC_S_INPUT2"); + // exit (EXIT_FAILURE); + } + +*/ +//-------------------------^^^^^ + +/* +Example 1-7. Selecting a new video standard +struct v4l2_input input; +v4l2_std_id std_id; + +memset (&input, 0, sizeof (input)); + +if (-1 == ioctl (fd, VIDIOC_G_INPUT, &input.index)) { + perror ("VIDIOC_G_INPUT"); + exit (EXIT_FAILURE); +} + +if (-1 == ioctl (fd, VIDIOC_ENUMINPUT, &input)) { + perror ("VIDIOC_ENUM_INPUT"); + exit (EXIT_FAILURE); +} + +if (0 == (input.std & V4L2_STD_PAL_BG)) { + fprintf (stderr, "Oops. B/G PAL is not supported.\n"); + exit (EXIT_FAILURE); +} + +// Note this is also supposed to work when only B +// or G/PAL is supported + +std_id = V4L2_STD_PAL_BG; + +if (-1 == ioctl (fd, VIDIOC_S_STD, &std_id)) { + perror ("VIDIOC_S_STD"); + exit (EXIT_FAILURE); +} + +*/ + int std_id; + std_id = Norm; //V4L2_STD_PAL_BG; + +//--------------------------vvvv +/* + if (!vd_ioctl (DEVICE, VIDIOC_S_STD, &std_id)) { + gv4l2_debug ("VIDIOC_S_STD2"); + // exit (EXIT_FAILURE); + } +*/ +//-------------------------^^^^^ + gv4l2_debug ("VIDIOC_S_done2"); + + return; + } // end v4l2 + + gv4l2_debug("'Source' now for V4L1"); +#endif + + if (READ_PROPERTY) + { + if (!vd_ioctl(DEVICE, VIDIOCGCHAN, &DEVICE->vchan)) + { + switch (DEVICE->vchan.channel) + { + case IN_TV: Source=0; break; + case IN_COMPOSITE1: Source=1; break; + case IN_COMPOSITE2: Source=2; break; + case IN_SVIDEO: Source=3; break; + } + switch(DEVICE->vchan.norm) + { + case NORM_PAL: Norm=0; break; + case NORM_NTSC: Norm=4; break; + case NORM_SECAM: Norm=8; break; + case NORM_AUTO: Norm=12; break; + } + } + GB.ReturnInteger(Source+Norm); + return; + } + + Source=VPROP(GB_INTEGER) & 3; + Norm=(VPROP(GB_INTEGER)>>2) & 3; + + switch( Source ) + { + case 0: Source=IN_TV; break; + case 1: Source=IN_COMPOSITE1; break; + case 2: Source=IN_COMPOSITE2; break; + case 3: Source=IN_SVIDEO; break; + } + + switch( Norm ) + { + case 0: Norm=NORM_PAL; break; + case 1: Norm=NORM_NTSC; break; + case 2: Norm=NORM_SECAM; break; + case 3: Norm=NORM_AUTO; break; + } + + vd_setup_video_source(DEVICE,Source,Norm); + +END_METHOD +// +//============================================================================= +// +// VideoDevice_Debug() +// +BEGIN_PROPERTY(VideoDevice_Debug) + + if (READ_PROPERTY) + { + GB.ReturnBoolean( gv4l2_debug_mode ); + return; + } + gv4l2_debug_mode = VPROP(GB_BOOLEAN); + +END_PROPERTY + +// +//============================================================================= +// +// cwebcam_image +// +// Raw "get_image" routine that can be used elsewhere regardless of the +// version of V4L2 in play. Necessary refactoring I'm afraid ... +// +int cwebcam_image(CWEBCAM *_object) +{ + if( THIS->is_v4l2 ) + { + if( !gv4l2_read_frame(THIS)) return 0; + THIS->w=THIS->fmt.fmt.pix.width; + THIS->h=THIS->fmt.fmt.pix.height; + } + else + { + if( !vd_get_image(THIS)) return 0; + THIS->w = DEVICE->vmmap.width; + THIS->h = DEVICE->vmmap.height; + vd_image_done(DEVICE); + } + return 1; +} + +// +// VideoDevice_Image() +// +// Hopefully you will agree, that not only is the raw _image routine +// required, but the resulting code is much nicer .. :) +// +BEGIN_PROPERTY(VideoDevice_Image) + + if (!cwebcam_image(THIS)) + { + GB.Error("Unable to get image"); + return; + } + + GB.ReturnObject(IMAGE.Create(THIS->w, THIS->h, THIS->format, THIS->frame)); +/* + // Ok, this lot has been refactored, sorry + // Once I got to "save" it became more efficient .. + + // -- GB_IMAGE ret=NULL; + unsigned char *buf; + int w, h; + + // ++ V4L2 + if( THIS->is_v4l2 ) { + + if( !gv4l2_read_frame( THIS )) + { + GB.Error("Unable to get image"); + GB.ReturnNull(); + return; + } + w=THIS->fmt.fmt.pix.width; + h=THIS->fmt.fmt.pix.height; + GB.ReturnObject(IMAGE.Create(w, h, GB_IMAGE_BGR, THIS->frame)); + return; + } + // -- V4L2 + + // -- buf = (unsigned char*)vd_get_image(DEVICE); + buf = (unsigned char*)vd_get_image(THIS); // ++ + if (!buf) + { + GB.Error("Unable to get image"); + GB.ReturnNull(); + return; + } + + w = DEVICE->vmmap.width; + h = DEVICE->vmmap.height; + vd_image_done(DEVICE); + + GB.ReturnObject(IMAGE.Create(w, h, GB_IMAGE_BGR, buf)); +*/ + +END_PROPERTY + +BEGIN_METHOD(VideoDevice_Save,GB_STRING File; GB_INTEGER Quality;) + + char *File; + char *ext=NULL; + long b; + FILE *fd; + // -- char *buf; + int format=2; + int quality=80; + // -- int w,h; + + File=GB.FileName(STRING(File),LENGTH(File)); + + if (!File) + { + GB.Error("Unable to open file for writing"); + return; + } + + if (!MISSING(Quality)) + { + quality=VARG(Quality); + if (quality<0) quality=0; + if (quality>100) quality=100; + } + + format = 0; + + for (b=strlen(File)-1;b>=0;b--) + if (File[b]=='.') { ext=File+b+1; break; } + + if (ext) + { + if (!strcasecmp(ext,"jpeg")) format=3; + else if (!strcasecmp(ext,"jpg")) format=3; + else if (!strcasecmp(ext,"png")) format=2; + else if (!strcasecmp(ext,"ppm")) format=1; + } + + if (!format) + { + GB.Error("Unknown format (jpeg|jpg|png|ppm)"); + return; + } + + fd=fopen(File, "w"); + if (!fd) + { + GB.Error("Unable to open file for writing"); + return; + } + +/* V4L2 Refactoring + + // -- buf=(char*)vd_get_image(DEVICE); + buf=(char*)vd_get_image(THIS); // ++ + if (!buf) + { + fclose(fd); + GB.Error("Unable to get image"); + return; + } + + w=DEVICE->vmmap.width; + h=DEVICE->vmmap.height; + + switch(format) + { + case 1: put_image_ppm (buf,w,h,quality,0,fd); break; + case 2: put_image_png (buf,w,h,0,fd); break; + case 3: put_image_jpeg (buf,w,h,quality,0,fd); break; + } +*/ + + // + // V4L2 ++ + // + if( !cwebcam_image(THIS) ) { + fclose(fd); + GB.Error("Unable to get image"); + return; + } + switch(format) + { + case 1: + put_image_ppm (THIS->frame,THIS->w,THIS->h,quality,0,fd); + break; + case 2: + put_image_png (THIS->frame,THIS->w,THIS->h,0,fd); + break; + case 3: + put_image_jpeg(THIS->frame,THIS->w,THIS->h,quality,0,fd); + break; + } + // + // V4L2 -- + // + + fclose(fd); + // -- (Ooops!) vd_image_done(DEVICE); + +END_METHOD + +/************************************************************************** + +Features + +***************************************************************************/ +void return_array(char *array,long mmax) +{ + int bucle; + int max=mmax; + + for (bucle=0;bucleis_v4l2 ) + GB.ReturnNewZeroString(THIS->device); + else return_array(DEVICE->vcap.name,32); + +END_PROPERTY + +BEGIN_PROPERTY(VideoDevice_Driver) + + struct v4l2_capability vcap; + int dev; + + if( THIS->is_v4l2 ) + dev = THIS->io; + else dev = DEVICE->dev; + if ( ioctl(dev,VIDIOC_QUERYCAP,&vcap)!=0 ) + { + GB.ReturnVoidString(); + return; + } + return_array((char*)vcap.driver,16); + + +END_PROPERTY + +BEGIN_PROPERTY(VideoDevice_Bus) + + struct v4l2_capability vcap; + + int dev; + + if( THIS->is_v4l2 ) + dev = THIS->io; + else dev = DEVICE->dev; + + if ( ioctl(dev,VIDIOC_QUERYCAP,&vcap)!=0 ) + { + GB.ReturnVoidString(); + return; + } + + return_array((char*)vcap.bus_info,32); + + +END_PROPERTY + +BEGIN_PROPERTY(VideoDevice_Card) + + struct v4l2_capability vcap; + + int dev; + + if( THIS->is_v4l2 ) { + return_array((char*)THIS->cap.card,32); + return; + } + dev = DEVICE->dev; + + if ( ioctl(dev,VIDIOC_QUERYCAP,&vcap)!=0 ) + { + GB.ReturnVoidString(); + return; + } + return_array((char*)vcap.driver,16); + + +END_PROPERTY + + +BEGIN_PROPERTY(VideoDevice_Version) + + char arr[12]; + struct v4l2_capability vcap; + + int dev; + + if( THIS->is_v4l2 ) + dev = THIS->io; + else dev = DEVICE->dev; + + if ( ioctl(dev,VIDIOC_QUERYCAP,&vcap)!=0 ) + { + GB.ReturnVoidString(); + return; + } + + sprintf (arr,"%u.%u.%u",(vcap.version>>16)&0xFF,(vcap.version>> 8)&0xFF,vcap.version&0xFF); + GB.ReturnNewZeroString(arr); + + +END_PROPERTY + + + +BEGIN_PROPERTY(VideoDevice_MaxWidth) + + if( THIS->is_v4l2 ) { // ++ V4L2 + gv4l2_debug("maxWidth not implemented in V4l2"); + GB.ReturnInteger(1024); + return; // ++ V4L2 + } + GB.ReturnInteger(DEVICE->vcap.maxwidth); + +END_PROPERTY + +BEGIN_PROPERTY(VideoDevice_MinWidth) + + if( THIS->is_v4l2 ) { // ++ V4L2 + gv4l2_debug("minWidth not implemented in V4l2"); + GB.ReturnInteger(0); + return; // ++ V4L2 + } + GB.ReturnInteger(DEVICE->vcap.minwidth); + +END_PROPERTY + +BEGIN_PROPERTY(VideoDevice_MaxHeight) + + if( THIS->is_v4l2 ) { // ++ V4L2 + gv4l2_debug("maxHeight not implemented in V4l2"); + GB.ReturnInteger(768); + return; // ++ V4L2 + } + GB.ReturnInteger(DEVICE->vcap.maxheight); + +END_PROPERTY + +BEGIN_PROPERTY(VideoDevice_MinHeight) + + if( THIS->is_v4l2 ) { // ++ V4L2 + gv4l2_debug("minHeight not implemented in V4l2"); + GB.ReturnInteger(0); + return; // ++ V4L2 + } + GB.ReturnInteger(DEVICE->vcap.minheight); + +END_PROPERTY + +/************************************************************************** + +TV tuner + +***************************************************************************/ + +BEGIN_PROPERTY(CTUNER_name) + + struct video_tuner vtuner; + long bucle,mmax=32; + char * tuner = "'tuner' not currently implemented on V4L2"; + + if( THIS->is_v4l2 ) { + GB.ReturnNewZeroString(tuner); + return; + } + + if (vd_ioctl (DEVICE, VIDIOCGTUNER, &vtuner)!=0) + { + GB.ReturnVoidString(); + return; + } + + for (bucle=0;bucle<32;bucle++) + { + if (vtuner.name[bucle]==0) break; + mmax--; + } + + GB.ReturnNewString(vtuner.name,32-mmax); + +END_PROPERTY + +BEGIN_PROPERTY(CTUNER_signal) + + struct video_tuner vtuner; + + if( THIS->is_v4l2 ) { + GB.ReturnInteger(0); + return; + } + + if (vd_ioctl (DEVICE, VIDIOCGTUNER, &vtuner)!=0) + { + GB.ReturnInteger(0); + return; + } + GB.ReturnInteger(vtuner.signal); + +END_PROPERTY + +BEGIN_PROPERTY(CTUNER_low) + + struct video_tuner vtuner; + struct v4l2_frequency vfreq; + + if( THIS->is_v4l2 ) { + GB.ReturnBoolean(0); + return; + } + + if (DEVICE->Freq2) + { + if (READ_PROPERTY) + { + if (vd_ioctl (DEVICE, VIDIOC_G_FREQUENCY, &vfreq)!=0) + { + GB.ReturnBoolean(0); + return; + } + GB.ReturnBoolean(vfreq.type & V4L2_TUNER_CAP_LOW); + return; + } + } + + if (vd_ioctl (DEVICE, VIDIOCGTUNER, &vtuner)!=0) + { + GB.ReturnBoolean(0); + return; + } + GB.ReturnBoolean(vtuner.flags & VIDEO_TUNER_LOW); + + +END_PROPERTY + + +BEGIN_PROPERTY(CTUNER_frequency) + + struct video_tuner vtuner; + struct v4l2_frequency vfreq; + + if( THIS->is_v4l2 ) { + GB.ReturnInteger(0); + return; + } + + if (DEVICE->Freq2) + { + if (READ_PROPERTY) + { + if (vd_ioctl (DEVICE, VIDIOC_G_FREQUENCY, &vfreq)!=0) + { + GB.ReturnInteger(0); + return; + } + GB.ReturnInteger(vfreq.frequency); + return; + } + + if (vd_ioctl (DEVICE, VIDIOC_G_FREQUENCY, &vfreq)!=0) return; + vfreq.frequency=VPROP(GB_INTEGER); + vd_ioctl (DEVICE, VIDIOC_S_FREQUENCY, &vfreq); + return; + } + + if (READ_PROPERTY) + { + if (vd_ioctl (DEVICE, VIDIOCGTUNER, &vtuner)!=0) + { + + GB.ReturnInteger(0); + return; + } + GB.ReturnInteger(vtuner.signal); + return; + } + if (vd_ioctl (DEVICE, VIDIOCGTUNER, &vtuner)!=0) return; + vtuner.signal=VPROP(GB_INTEGER); + vd_ioctl (DEVICE, VIDIOCSTUNER, &vtuner); + +END_PROPERTY + +/*GB_DESC CFeaturesDesc[] = +{ + GB_DECLARE(".VideoDevice.Features", 0), + GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Name","s",VideoDevice_Name), + GB_PROPERTY_READ("Driver","s",VideoDevice_Driver), + GB_PROPERTY_READ("Bus","s",VideoDevice_Bus), + GB_PROPERTY_READ("Card","s",VideoDevice_Card), // ++ V4L2 + GB_PROPERTY_READ("Version","s",VideoDevice_Version), + GB_PROPERTY_READ("MaxWidth","i",VideoDevice_MaxWidth), + GB_PROPERTY_READ("MinWidth","i",VideoDevice_MinWidth), + GB_PROPERTY_READ("MaxHeight","i",VideoDevice_MaxHeight), + GB_PROPERTY_READ("MinHeight","i",VideoDevice_MinHeight), + + GB_END_DECLARE +};*/ + +GB_DESC CTunerDesc[] = +{ + GB_DECLARE(".VideoDevice.Tuner", 0), + GB_VIRTUAL_CLASS(), + + GB_PROPERTY_READ("Name","s",CTUNER_name), + GB_PROPERTY_READ("Quality","i",CTUNER_signal), + GB_PROPERTY_READ("Low","b",CTUNER_low), + + GB_PROPERTY("Frequency","i",CTUNER_frequency), + + GB_END_DECLARE +}; + +GB_DESC CWebcamDesc[] = +{ + + GB_DECLARE("VideoDevice", sizeof(CWEBCAM)), + GB_INHERITS("Stream"), + + GB_HOOK_CHECK(CWEBCAM_check), + + GB_CONSTANT("Hz","i",1), + GB_CONSTANT("Khz","i",0), + + GB_CONSTANT("Pal","i",0),//NORM_PAL), + GB_CONSTANT("Ntsc","i",4),//NORM_NTSC), + GB_CONSTANT("Secam","i",8),//NORM_SECAM), + GB_CONSTANT("Auto","i",12),//NORM_AUTO), + + GB_CONSTANT("Tv","i",0), //IN_TV), + GB_CONSTANT("Composite1","i",1), //IN_COMPOSITE1), + GB_CONSTANT("Composite2","i",2), //IN_COMPOSITE2), + GB_CONSTANT("SVideo","i",3), //IN_SVIDEO), + + GB_CONSTANT("Any", "i", MODE_ANY), + GB_CONSTANT("V4L", "i", MODE_V4L), // ++ force V4L1 + GB_CONSTANT("V4L2", "i", MODE_V4L2), // ++ force V4L2 + + GB_METHOD("_new",NULL,VideoDevice_new,"(Device)s[(Mode)i]"), + GB_METHOD("_free",NULL,VideoDevice_free,NULL), + + //GB_PROPERTY_SELF("Features",".VideoDevice.Features"), + + GB_PROPERTY_SELF("Tuner",".VideoDevice.Tuner"), + GB_PROPERTY("Source","i",VideoDevice_Source), + + GB_PROPERTY_READ("Width","i",VideoDevice_Width), + GB_PROPERTY_READ("Height","i",VideoDevice_Height), + + GB_PROPERTY("Contrast","i",VideoDevice_Contrast), + GB_PROPERTY_READ("ContrastMax","i",VideoDevice_ContrastMax), + GB_PROPERTY_READ("ContrastMin","i",VideoDevice_ContrastMin), + GB_PROPERTY_READ("ContrastDefault","i",VideoDevice_ContrastDefault), + GB_PROPERTY("Color","i",VideoDevice_Color), + GB_PROPERTY_READ("ColorMax","i",VideoDevice_ColorMax), + GB_PROPERTY_READ("ColorMin","i",VideoDevice_ColorMin), + GB_PROPERTY_READ("ColorDefault","i",VideoDevice_ColorDefault), + GB_PROPERTY("Whiteness","i",VideoDevice_Whiteness), + GB_PROPERTY_READ("WhitenessMax","i",VideoDevice_WhitenessMax), + GB_PROPERTY_READ("WhitenessMin","i",VideoDevice_WhitenessMin), + GB_PROPERTY_READ("WhitenessDefault","i",VideoDevice_WhitenessDefault), + GB_PROPERTY("Bright","i",VideoDevice_Brightness), + GB_PROPERTY_READ("BrightMax","i",VideoDevice_BrightnessMax), + GB_PROPERTY_READ("BrightMin","i",VideoDevice_BrightnessMin), + GB_PROPERTY_READ("BrightDefault","i",VideoDevice_BrightnessDefault), + GB_PROPERTY("Hue","i",VideoDevice_Hue), + GB_PROPERTY_READ("HueMax","i",VideoDevice_HueMax), + GB_PROPERTY_READ("HueMin","i",VideoDevice_HueMin), + GB_PROPERTY_READ("HueDefault","i",VideoDevice_HueDefault), + + GB_PROPERTY_READ("Name","s",VideoDevice_Name), + GB_PROPERTY_READ("Driver","s",VideoDevice_Driver), + GB_PROPERTY_READ("Bus","s",VideoDevice_Bus), + GB_PROPERTY_READ("Card","s",VideoDevice_Card), // ++ V4L2 + GB_PROPERTY_READ("Version","s",VideoDevice_Version), + GB_PROPERTY_READ("MaxWidth","i",VideoDevice_MaxWidth), + GB_PROPERTY_READ("MinWidth","i",VideoDevice_MinWidth), + GB_PROPERTY_READ("MaxHeight","i",VideoDevice_MaxHeight), + GB_PROPERTY_READ("MinHeight","i",VideoDevice_MinHeight), + //GB_PROPERTY_READ("MaxFrameRate","i",CFEATURES_maxRate), + + GB_PROPERTY("Image","Image",VideoDevice_Image), + GB_PROPERTY("Debug","b",VideoDevice_Debug), + + GB_METHOD("Resize",NULL,VideoDevice_Resize,"(Width)i(Height)i"), + GB_METHOD("Save",NULL,VideoDevice_Save,"(File)s[(Quality)i]"), + + GB_END_DECLARE +}; + + diff --git a/gb.v4l/src/CWebcam.h b/gb.v4l/src/CWebcam.h new file mode 100644 index 00000000..4bd0e501 --- /dev/null +++ b/gb.v4l/src/CWebcam.h @@ -0,0 +1,197 @@ +/*************************************************************************** + + CWebcam.h + + (C) 2005-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CWEBCAM_H +#define __CWEBCAM_H + +#include +#include +#include +#include "config.h" +#include +#include + +#ifdef OS_LINUX + #include + #include "videodev.h" +#else + #include +#endif + +#include "gambas.h" + +#ifndef __CWEBCAM_C + +extern GB_DESC CWebcamDesc[]; +extern GB_DESC CTunerDesc[]; +extern GB_STREAM_DESC VideoStream; + +#else + +#define THIS ((CWEBCAM *)_object) +#define DEVICE (THIS->dev) +// ++ V4L2 +#define MCLEAR(x) memset (&(x), 0, sizeof (x)) +#define MODE_ANY 0 +#define MODE_V4L 1 +#define MODE_V4L2 2 +// -- +#endif + +typedef + struct video_device + { + int width; + int height; + int depth; // colour depth + int buffer_size; // always width * height * depth + int use_mmap; // mmap() available for capturing a frame + int capturing; // our device is capturing frames for us + + struct video_capability vcap; + struct video_channel vchan; + struct video_mbuf vmbuf; + struct video_mmap vmmap; + struct video_window vwin; + struct video_picture videopict; + + unsigned char *frame_buffer; // not the video memory, but one image + int dev; // fd of the physical device + int Freq2; + } + video_device_t; + + +typedef + struct + { + GB_STREAM_BASE stream; + void *handle; + } + VIDEO_STREAM; + +// ++ V4L2 +typedef + struct gv4l2_buffer + { + void* start; + size_t length; + } + gv4l2_buffer_t; +//-- + +typedef + struct + { + GB_BASE ob; + GB_STREAM stream; + + char *device; + video_device_t *dev; + unsigned char *membuf; + int gotframe; + int posframe; + + // ++ YUYV->RGB conversion + void *frame; // "current" frame buffer + //-- + // ++ V4L2 + // + // There is some duplication here but we really don't want to use + // the v4l video_device_t structure ... + // + struct v4l2_capability cap; + struct v4l2_cropcap cropcap; + struct v4l2_crop crop; + struct v4l2_format fmt; + struct gv4l2_buffer *buffers; + // + int is_v4l2; // which version is this dev + int io; // raw device handle for V2 + int use_mmap; // is MMAP available + int buffer_count; // number of buffers + int w, h; // "current" dimensions + int format; // gb.image format + // + int bright_max; + int hue_max; + int contrast_max; + int whiteness_max; + int color_max; + // + int bright_min; + int hue_min; + int contrast_min; + int whiteness_min; + int color_min; + // + int bright_def; + int hue_def; + int contrast_def; + int whiteness_def; + int color_def; + // -- + struct v4lconvert_data *convert; + } + CWEBCAM; + + +int Video_stream_read(GB_STREAM *stream, char *buffer, int len); +int Video_stream_write(GB_STREAM *stream, char *buffer, int len); +int Video_stream_eof(GB_STREAM *stream); +int Video_stream_lof(GB_STREAM *stream, int64_t *len); +int Video_stream_open(GB_STREAM *stream, const char *path, int mode, void *data); +int Video_stream_seek(GB_STREAM *stream, int64_t pos, int whence); +int Video_stream_tell(GB_STREAM *stream, int64_t *pos); +int Video_stream_flush(GB_STREAM *stream); +int Video_stream_close(GB_STREAM *stream); +int Video_stream_handle(GB_STREAM *stream); + + +// ++ YUYV->RGB conversion +int convert_yuv_to_rgb_buffer(unsigned char *yuv, unsigned char *rgb, unsigned int width, unsigned int height); +void yuv420p_to_rgb (unsigned char *image, unsigned char *image2, int x, int y, int z); +// -- + +// ++ V4L2 +int gv4l2_available(CWEBCAM * _object); +void gv4l2_debug(const char *s); +int gv4l2_xioctl( int fd,int request,void * arg); +int gv4l2_open_device( char* name ); +void gv4l2_close_device( int id ); +int gv4l2_init_device(CWEBCAM * _object , int width , int height ); +int gv4l2_start_capture(CWEBCAM * _object); +int gv4l2_stop_capture(CWEBCAM * _object); +void gv4l2_uninit_device(CWEBCAM * _object); +void gv4l1_process_image (CWEBCAM * _object, void *start); +void gv4l2_process_image (CWEBCAM * _object, void *start); +int gv4l2_read_frame( CWEBCAM * _object ); +int gv4l2_resize( CWEBCAM * _object , int width , int height ); +int gv4l2_hue( CWEBCAM * _object , int hue ); +int gv4l2_brightness( CWEBCAM * _object , int hue ); +int gv4l2_contrast( CWEBCAM * _object , int value ); +int gv4l2_color( CWEBCAM * _object , int value ); +int gv4l2_whiteness( CWEBCAM * _object , int value ); +// -- V4L2 + +#endif diff --git a/gb.v4l/src/Makefile.am b/gb.v4l/src/Makefile.am new file mode 100644 index 00000000..f8318f13 --- /dev/null +++ b/gb.v4l/src/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.v4l +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.v4l.la + +gb_v4l_la_LIBADD = @V4L_LIB@ @V4LCONVERT_LIB@ +gb_v4l_la_LDFLAGS = -module @LD_FLAGS@ @V4L_LDFLAGS@ @V4LCONVERT_LDFLAGS@ +gb_v4l_la_CPPFLAGS = @V4L_INC@ @V4LCONVERT_INC@ + +gb_v4l_la_SOURCES = main.h main.c CWebcam.h CWebcam.c gv4l2.c CConverters.c videodev.h + + + + diff --git a/gb.v4l/src/gb.v4l.component b/gb.v4l/src/gb.v4l.component new file mode 100644 index 00000000..af796e9e --- /dev/null +++ b/gb.v4l/src/gb.v4l.component @@ -0,0 +1,10 @@ +[Component] +Key=gb.v4l +Name[fr]=Composant de capture vidéo +Name[es]=Componente para captura de vídeo +Name[tr]=Video yakalama bileşeni +Author=Daniel Campos Fernández,Gareth Bult +Requires=gb.image +State=Deprecated + + diff --git a/gb.v4l/src/gv4l2.c b/gb.v4l/src/gv4l2.c new file mode 100644 index 00000000..2afe4deb --- /dev/null +++ b/gb.v4l/src/gv4l2.c @@ -0,0 +1,803 @@ +/*************************************************************************** + + gv4l2.c + + (C) 2009 Gareth Bult + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CWEBCAM_C + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + + +#ifdef HAVE_STDLIB_H +#undef HAVE_STDLIB_H +#endif + +#include "main.h" +#include "CWebcam.h" + +#ifdef OS_LINUX + #include +#else + #include +#endif + +// +bool gv4l2_debug_mode = TRUE; +// +//============================================================================= +// +// gv4l2_available() +// +// Test for V4L2 availability +// +int gv4l2_available(CWEBCAM * _object) +{ + char dummy[256]; + + return(!(ioctl( THIS->io , VIDIOC_QUERYCAP , dummy ) == -1 )); +} +//============================================================================= +// +// v4l2_debug( string ) +// +// Debugging routine for V4L2 +// +void gv4l2_debug(const char *s) +{ + if (!gv4l2_debug_mode) return; + fprintf(stderr, "gb.v4l: v4l2: %s: %s\n", s, strerror(errno)); +} + +//============================================================================= +// +// xioctl( fd,request,arg ) +// +// Local swapper for ioctl to repeat on EINTR's +// +int gv4l2_xioctl( int fd,int request,void * arg) +{ + int r; + + do r = ioctl (fd, request, arg); + while (-1 == r && EINTR == errno); + return r; +} +// +//============================================================================= +// +// gv4l2_hue( THIS, value ) +// +void gv4l2_camera_setup(CWEBCAM *_object,int id,int *min,int *max,int *def) +{ + struct v4l2_queryctrl query; + + memset (&query, 0, sizeof (query)); + query.id = id; + + if(gv4l2_xioctl(THIS->io,VIDIOC_QUERYCTRL,&query)==-1)return; + + //printf("Name=%s,Min=%d,Max=%d,Value=%d\n", + // query.name, + // query.minimum, + // query.maximum, + // query.default_value); + //fflush(stdout); + + *max = query.maximum; + *min = query.minimum; + *def = query.default_value; +} +// +int gv4l2_camera_get( CWEBCAM * _object , int id , int value ) +{ + struct v4l2_control control; + int command; + int result; + + memset (&control, 0, sizeof (control)); + control.id = id; + control.value = value; + + if( value != -1 ) + command = VIDIOC_S_CTRL; + else command = VIDIOC_G_CTRL; + + result = gv4l2_xioctl (THIS->io, command , &control); + + if( result == -1 ) return result; + return control.value; +} +// +void gv4l2_hue_setup( CWEBCAM * _object ) +{ + gv4l2_camera_setup( THIS , + V4L2_CID_HUE , &THIS->hue_min , &THIS->hue_max ,&THIS->hue_def); +} +// +int gv4l2_hue( CWEBCAM * _object , int value ) +{ + return gv4l2_camera_get( THIS, V4L2_CID_HUE , value ); +} +// +//============================================================================= +// +// gv4l2_brightness( THIS, value ) +// +void gv4l2_brightness_setup( CWEBCAM * _object ) +{ + gv4l2_camera_setup( THIS, + V4L2_CID_BRIGHTNESS , &THIS->bright_min ,&THIS->bright_max,&THIS->bright_def); +} + +int gv4l2_brightness( CWEBCAM * _object , int value ) +{ + return gv4l2_camera_get( THIS, V4L2_CID_BRIGHTNESS , value ); +} +// +//============================================================================= +// +// gv4l2_contrast( THIS, value ) +// +void gv4l2_contrast_setup( CWEBCAM * _object ) +{ + gv4l2_camera_setup( THIS , + V4L2_CID_CONTRAST, &THIS->contrast_min , &THIS->contrast_max,&THIS->contrast_def); +} + +int gv4l2_contrast( CWEBCAM * _object , int value ) +{ + return gv4l2_camera_get( THIS, V4L2_CID_CONTRAST , value ); +} +// +//============================================================================= +// +// gv4l2_color( THIS, value ) +// +void gv4l2_color_setup( CWEBCAM * _object ) +{ + gv4l2_camera_setup( THIS , + V4L2_CID_SATURATION, &THIS->color_min, &THIS->color_max,&THIS->color_def ); +} + +int gv4l2_color( CWEBCAM * _object , int value ) +{ + return gv4l2_camera_get( THIS, V4L2_CID_SATURATION , value ); +} +// +//============================================================================= +// +// gv4l2_color( THIS, value ) +// +void gv4l2_whiteness_setup( CWEBCAM * _object ) +{ + gv4l2_camera_setup( THIS , + V4L2_CID_WHITENESS , &THIS->whiteness_min,&THIS->whiteness_max,&THIS->whiteness_def); +} +// +int gv4l2_whiteness( CWEBCAM * _object , int value ) +{ + return gv4l2_camera_get( THIS, V4L2_CID_WHITENESS , value ); +} +//============================================================================= +// +// +//============================================================================= +// +// v4l2_open_device( device_name ) +// +// Open the raw device (typically /dev/video?) , note that we're not +// using non-blocking mode as (a) it's not needed given we're recovering +// specific frames and (b) camera's are often "not ready" so it would +// require retries under Gambas. +// +// FIXME:: what happens when you unplug a camera when active ?? +// +int gv4l2_open_device( char* name ) +{ + struct stat file_info; + int status; + + // + // See if the file is there ... + // + status = stat(name,&file_info); + if( status == -1 ) { + gv4l2_debug("failed to stat device"); + return status; + } + // + // Make sure it's a character device (/dev/video?) + // + if( !S_ISCHR(file_info.st_mode) ) { + gv4l2_debug("not a character device"); + return status; + } + // + // Finally, try to open the file .. + // + return open( name,O_RDWR /* |O_NONBLOCK */ ,0 ); +} +//============================================================================ +// +// v4l2_close_device( id ) +// +// Close the device, got to be done and can't get much easier ! ;-) +// +void gv4l2_close_device( int id ) +{ + if( close( id ) == -1 ) { + gv4l2_debug("error closing device"); + } +} +//============================================================================ +// +// v4l2_init_device( THIS , Width , Height ) +// +// Initialise the device and associated data structures, this is the most +// complex operation in the code and has to cope with it's own MMAP +// handling whereas V4L did a lot of this for us. +// +// FIXME:: test the READ interface, I only use MMAP cameras ... +// +int gv4l2_init_device(CWEBCAM * _object , int width , int height ) +{ + unsigned int min; + static unsigned int n_buffers = 0; + int save; + + if ( gv4l2_xioctl (THIS->io, VIDIOC_QUERYCAP, &THIS->cap) == -1 ) { + gv4l2_debug("VIDIOC_QUERYCAP error"); + return 0; + } + + if (!(THIS->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { + gv4l2_debug("not video capture device"); + return 0; + } + // + // We need to choose which IO method to use, well try MMAP and + // if that fails, fall back to READ + // + if (!(THIS->cap.capabilities & V4L2_CAP_STREAMING)) { + // + // No MMAP support! + // + THIS->use_mmap = 0; + if (!(THIS->cap.capabilities & V4L2_CAP_READWRITE)) { + gv4l2_debug("device does not support mmap or read"); + return 0; + } + } + else + THIS->use_mmap = 1; + + MCLEAR(THIS->cropcap); + THIS->cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (!gv4l2_xioctl (THIS->io, VIDIOC_CROPCAP, &THIS->cropcap)) { + THIS->crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + THIS->crop.c = THIS->cropcap.defrect; + + if ( gv4l2_xioctl (THIS->io, VIDIOC_S_CROP, &THIS->crop) == -1 ) + { + if( errno == EINVAL ) { + gv4l2_debug("cropping not supported"); + } + } + } + + MCLEAR(THIS->fmt); + THIS->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if( gv4l2_xioctl( THIS->io, VIDIOC_G_FMT, &THIS->fmt ) == -1 ) { + gv4l2_debug("Unable to get Video formats"); + return 0; + } + + THIS->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + THIS->fmt.fmt.pix.width = width; + THIS->fmt.fmt.pix.height = height; + THIS->fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; + + save = THIS->fmt.fmt.pix.pixelformat; + + // + // Camera format should be picked up from VIDIOC_G_FMT above + // FIXME:: do cameras support multiple formats and so we want + // to be able to pick the format?? + // + // Try the supported formats; + // a. YUYV + // b. YUV420 + // c. revert to whatever the camera was set to + // + THIS->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + if ( gv4l2_xioctl ( THIS->io, VIDIOC_S_FMT, &THIS->fmt) == -1) { + gv4l2_debug("VIDIOC_S_FMT, can't set YUYV, trying YUV 420"); + THIS->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; + if ( gv4l2_xioctl ( THIS->io, VIDIOC_S_FMT, &THIS->fmt) == -1) { + gv4l2_debug("VIDIOC_S_FMT, can't set YUV420, defaulting "); + THIS->fmt.fmt.pix.pixelformat = save; + } + } + + // BM: Final image gb.image format + THIS->format = GB_IMAGE_BGR; //IMAGE.GetDefaultFormat(); + + // BM: Conversion structure + THIS->convert = v4lconvert_create(THIS->io); + + //THIS->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + + //if ( gv4l2_xioctl ( THIS->io, VIDIOC_S_FMT, &THIS->fmt) == -1) { + // gv4l2_debug("VIDIOC_S_FMT, unable to set format"); + // return 0; + //} + // THIS->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + // gv4l2_xioctl ( THIS->io, VIDIOC_S_FMT, &THIS->fmt); + + /* Note VIDIOC_S_FMT may change width and height. */ + + /* Buggy driver paranoia. */ + min = THIS->fmt.fmt.pix.width * 2; + if (THIS->fmt.fmt.pix.bytesperline < min) + THIS->fmt.fmt.pix.bytesperline = min; + min = THIS->fmt.fmt.pix.bytesperline * THIS->fmt.fmt.pix.height; + if (THIS->fmt.fmt.pix.sizeimage < min) + THIS->fmt.fmt.pix.sizeimage = min; + + GB.Alloc(&THIS->frame, THIS->fmt.fmt.pix.width * THIS->fmt.fmt.pix.height * (GB_IMAGE_FMT_IS_24_BITS(THIS->format) ? 3 : 4)); + + gv4l2_brightness_setup( THIS ); + gv4l2_contrast_setup( THIS ); + gv4l2_color_setup( THIS ); + gv4l2_whiteness_setup( THIS ); + gv4l2_hue_setup( THIS ); + + if( !THIS->use_mmap ) { + GB.Alloc( POINTER(&THIS->buffers) ,sizeof(*THIS->buffers)); + if( !THIS->buffers ) { + gv4l2_debug("Failed to allocate buffer space"); + return 0; + } + THIS->buffers[0].length = THIS->fmt.fmt.pix.sizeimage; + GB.Alloc( POINTER(&THIS->buffers[0].start),THIS->fmt.fmt.pix.sizeimage); + if( !THIS->buffers[0].start ) { + gv4l2_debug("Failed to allocate buffer space"); + return 0; + } + return 1; + } + // We don't support USERPTR in Gambas (!) + // So now we initialise MMAP + // + struct v4l2_requestbuffers req; + + MCLEAR(req); + req.count = 2; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + + if ( gv4l2_xioctl (THIS->io, VIDIOC_REQBUFS, &req) == -1 ) { + gv4l2_debug("mmap not supported or error"); + return 0; + } + if (req.count < 2) { + gv4l2_debug("not enough memory for mmap"); + return 0; + } + GB.Alloc ( POINTER(&THIS->buffers),req.count * sizeof (*THIS->buffers)); + if (!THIS->buffers) { + gv4l2_debug("not memory for mmap"); + return 0; + } + THIS->buffer_count = req.count; + for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { + struct v4l2_buffer buf; + + MCLEAR(buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = n_buffers; + + if( gv4l2_xioctl (THIS->io, VIDIOC_QUERYBUF, &buf) == -1 ) { + gv4l2_debug("VIDIOC_QUERYBUF"); + return 0; + } + + THIS->buffers[n_buffers].length = buf.length; + THIS->buffers[n_buffers].start = + mmap (NULL /* start anywhere */, + buf.length, + PROT_READ | PROT_WRITE /* required */, + MAP_SHARED /* recommended */, + THIS->io, buf.m.offset); + + if (MAP_FAILED == THIS->buffers[n_buffers].start) { + gv4l2_debug("mmap failed"); + return 0; + } + } + return 1; +} + +//============================================================================= +// +// v4l2_start_capture() +// +// Start capture mode, this should turn on the little green light on your +// camera. +// +// FIXME:: make sure we check the return status on this call +// +int gv4l2_start_capture(CWEBCAM * _object) +{ + int i; + enum v4l2_buf_type type; + // + gv4l2_debug("Capture ON"); + // + // Nothing to do unless we're using MMAP + // + if( !THIS->use_mmap) return 1; + // + for( i=0; ibuffer_count; i++ ) { + struct v4l2_buffer buf; + + MCLEAR (buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + + if( gv4l2_xioctl( THIS->io, VIDIOC_QBUF, &buf) == -1 ) { + gv4l2_debug("VIDIOC_QBUF error starting capture"); + return 0; + } + } + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if( gv4l2_xioctl( THIS->io, VIDIOC_STREAMON, &type) == -1 ) { + gv4l2_debug("VIDIOC_STREAMON error starting capture"); + return 0; + } + return 1; +} +//============================================================================= +// +// v4l2_stop_capture() +// +// Stop Capturing on device (turn little green light off!) +// +// FIXME:: check return status on this call! +// +int gv4l2_stop_capture(CWEBCAM * _object) +{ + enum v4l2_buf_type type; + + if( !THIS->use_mmap) return 1; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if( gv4l2_xioctl( THIS->io, VIDIOC_STREAMOFF, &type) == -1) + { + gv4l2_debug("VIDIOC_STREAMOFF error"); + return 0; + } + return 1; +} +//============================================================================= +// +// gv4l2_uninit_device(THIS) +// +// Uninitialise the device and free all the associated memory +// +void gv4l2_uninit_device(CWEBCAM * _object) +{ + unsigned int i; + + GB.Free( POINTER(&THIS->frame) ); + + v4lconvert_destroy(THIS->convert); + + if( !THIS->use_mmap) { + GB.Free ( POINTER(&THIS->buffers[0].start)); + GB.Free ( POINTER(&THIS->buffers)); + return; + } + for (i = 0; i < THIS->buffer_count; ++i ) + if(munmap(THIS->buffers[i].start,THIS->buffers[i].length)==-1) + gv4l2_debug("MUNMAP Error"); + + GB.Free ( POINTER(&THIS->buffers)); +} +//============================================================================= +// +// g4vl_process_image(THIS,start) +// +// Process the image found in start and dump the resulting RGB frame into +// our local frame buffer (THIS->frame). Width, Height and Image size can +// all be found in THIS->fmt.fmt +// +// FIXME:: there are lots of formats, I can *only* test YUYV. +// I'm *assuming* RGB32 is "raw" mode (no conversion) +// Do "other" RGB formats work without conversion? +// What other conversion routines do we need? +// Will BM be moving any/all of these to Image/Picture objects? +// +void gv4l1_process_image (CWEBCAM * _object, void *start) +{ + int format,w,h; + long size; + + format = THIS->dev->videopict.palette; + w = THIS->dev->width; + h = THIS->dev->height; + size = THIS->dev->buffer_size; + + switch(format) + { + case VIDEO_PALETTE_YUV411P: gv4l2_debug("YUV411P"); break; + case VIDEO_PALETTE_YUV420P: + //gv4l2_debug("YUV420P"); + case VIDEO_PALETTE_YUV420: + //gv4l2_debug("YUV420"); + yuv420p_to_rgb (start,THIS->frame,w,h,3); + return; + case VIDEO_PALETTE_YUYV: + //gv4l2_debug("YUYV"); + convert_yuv_to_rgb_buffer(start,THIS->frame,w,h); + return; + + case VIDEO_PALETTE_GREY: gv4l2_debug("GREY"); break; + case VIDEO_PALETTE_HI240: gv4l2_debug("HI240"); break; + case VIDEO_PALETTE_RGB565: gv4l2_debug("RGB5656"); break; + case VIDEO_PALETTE_RGB24: gv4l2_debug("RGB24"); break; + case VIDEO_PALETTE_RGB32: /* DEFAULT */ break; + case VIDEO_PALETTE_RGB555: gv4l2_debug("RGB555"); break; + case VIDEO_PALETTE_UYVY: gv4l2_debug("UYVY"); break; + case VIDEO_PALETTE_YUV411: gv4l2_debug("YUV411"); break; + case VIDEO_PALETTE_RAW: gv4l2_debug("RAW"); break; + case VIDEO_PALETTE_YUV422P: gv4l2_debug("YUV422P"); break; + case VIDEO_PALETTE_YUV410P: gv4l2_debug("YUV410P"); break; + case VIDEO_PALETTE_COMPONENT: gv4l2_debug("COMPONENT");break; + + default: + gv4l2_debug("Frame in unknown format"); + break; + } + memcpy(THIS->frame,start,size); +} +// +// v4l2 version (!) +// +void gv4l2_process_image (CWEBCAM * _object, void *start) +{ + struct v4l2_format dest = THIS->fmt; + + if (THIS->format != GB_IMAGE_BGR) + gv4l2_debug("Destination format not supported"); + + dest.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24; + dest.fmt.pix.sizeimage = THIS->fmt.fmt.pix.width * THIS->fmt.fmt.pix.height * 3; + + if (v4lconvert_convert(THIS->convert, &THIS->fmt, &dest, start, THIS->fmt.fmt.pix.sizeimage, THIS->frame, dest.fmt.pix.sizeimage) != dest.fmt.pix.sizeimage) + gv4l2_debug("Unable to convert webcam image to BGR24"); +} + +#if 0 + switch(format) + { +/* + For adding new formats use the #ifdef method. + It is not safe to asume every videodev2.h has the V4L2_PIX_FMT_ + V4L2_PIX_FMT_Y16 was not aviable in Ubuntu 8.04 but exists in Ubuntu 8.10 + Some other where also missing + Add by:R.Onstenk 22-feb-2009 +*/ + case V4L2_PIX_FMT_RGB332: gv4l2_debug("RGB332"); break; + case V4L2_PIX_FMT_RGB444: gv4l2_debug("RGB444"); break; + case V4L2_PIX_FMT_RGB555: gv4l2_debug("RGB555"); break; + case V4L2_PIX_FMT_RGB565: gv4l2_debug("YRGB565"); break; + case V4L2_PIX_FMT_RGB555X: gv4l2_debug("YRGB555X");break; + case V4L2_PIX_FMT_RGB565X: gv4l2_debug("RGB565X"); break; + case V4L2_PIX_FMT_BGR24: gv4l2_debug("BGR24"); break; + case V4L2_PIX_FMT_RGB24: gv4l2_debug("RGB24"); break; + case V4L2_PIX_FMT_BGR32: gv4l2_debug("BGR32"); break; + case V4L2_PIX_FMT_RGB32: /* DEFAULT - NO CONV */ break; + case V4L2_PIX_FMT_GREY: gv4l2_debug("GREY"); break; + +#ifdef V4L2_PIX_FMT_Y16 + case V4L2_PIX_FMT_Y16: gv4l2_debug("Y16"); break; +#endif + case V4L2_PIX_FMT_PAL8: gv4l2_debug("PAL8"); break; + case V4L2_PIX_FMT_YVU410: gv4l2_debug("YVU410"); break; + case V4L2_PIX_FMT_YVU420: gv4l2_debug("YVU420"); break; + + case V4L2_PIX_FMT_YUV420: + //gv4l2_debug("YUV420"); + yuv420p_to_rgb (start,THIS->frame,w,h,3); + return; + case V4L2_PIX_FMT_YUYV: + //gv4l2_debug("YUYV"); + convert_yuv_to_rgb_buffer(start,THIS->frame,w,h); + return; + + case V4L2_PIX_FMT_UYVY: gv4l2_debug("UYVY"); break; + case V4L2_PIX_FMT_YUV422P: gv4l2_debug("YUV422P"); break; + case V4L2_PIX_FMT_YUV411P: gv4l2_debug("YUV411P"); break; + case V4L2_PIX_FMT_Y41P: gv4l2_debug("Y41P"); break; + case V4L2_PIX_FMT_YUV444: gv4l2_debug("YUV444"); break; + case V4L2_PIX_FMT_YUV555: gv4l2_debug("YUV555"); break; + case V4L2_PIX_FMT_YUV565: gv4l2_debug("YUV565"); break; + case V4L2_PIX_FMT_YUV32: gv4l2_debug("YUV32"); break; + case V4L2_PIX_FMT_NV12: gv4l2_debug("NV12"); break; + case V4L2_PIX_FMT_NV21: gv4l2_debug("NV21"); break; + case V4L2_PIX_FMT_YUV410: gv4l2_debug("YUV410"); break; + case V4L2_PIX_FMT_YYUV: gv4l2_debug("YYUV"); break; + case V4L2_PIX_FMT_HI240: gv4l2_debug("HI240"); break; + case V4L2_PIX_FMT_HM12: gv4l2_debug("HM12"); break; + case V4L2_PIX_FMT_SBGGR8: gv4l2_debug("SBGGR8"); break; +#ifdef V4L2_PIX_FMT_SGBRG8 + case V4L2_PIX_FMT_SGBRG8: gv4l2_debug("SBGRG8"); break; +#endif +#ifdef V4L2_PIX_FMT_SBGGR16 + case V4L2_PIX_FMT_SBGGR16: gv4l2_debug("SBGGR16"); break; +#endif + + case V4L2_PIX_FMT_MJPEG: gv4l2_debug("MJPEG"); break; + case V4L2_PIX_FMT_JPEG: gv4l2_debug("JPEG"); break; + case V4L2_PIX_FMT_DV: gv4l2_debug("DV"); break; + case V4L2_PIX_FMT_MPEG: gv4l2_debug("MPEG"); break; + case V4L2_PIX_FMT_WNVA: gv4l2_debug("WNVA"); break; + case V4L2_PIX_FMT_SN9C10X: gv4l2_debug("SN9C10X"); break; + case V4L2_PIX_FMT_PWC1: gv4l2_debug("PWC1"); break; + case V4L2_PIX_FMT_PWC2: gv4l2_debug("PWC2"); break; + case V4L2_PIX_FMT_ET61X251: gv4l2_debug("ET61X251");break; +#ifdef V4L2_PIX_FMT_SPCA501 + case V4L2_PIX_FMT_SPCA501: gv4l2_debug("SPCA501"); break; +#endif +#ifdef V4L2_PIX_FMT_SPCA505 + case V4L2_PIX_FMT_SPCA505: gv4l2_debug("SPCA505"); break; +#endif +#ifdef V4L2_PIX_FMT_SPCA508 + case V4L2_PIX_FMT_SPCA508: gv4l2_debug("SPCA508"); break; +#endif +#ifdef V4L2_PIX_FMT_SPCA561 + case V4L2_PIX_FMT_SPCA561: gv4l2_debug("SPCA561"); break; +#endif +#ifdef V4L2_PIX_FMT_PAC207 + case V4L2_PIX_FMT_PAC207: gv4l2_debug("PAC207"); break; +#endif +#ifdef V4L2_PIX_FMT_PJPG + case V4L2_PIX_FMT_PJPG: gv4l2_debug("PJPG"); break; +#endif +#ifdef V4L2_PIX_FMT_YVYU + case V4L2_PIX_FMT_YVYU: gv4l2_debug("YVYU"); break; +#endif + + default: + gv4l2_debug("Frame in unknown format. Format:0x" + format); + break; + } + memcpy(THIS->frame,start,size); +} +#endif + +//============================================================================= +// +// gv4l2_read_frame( THIS ) +// +// Read a frame from the camera / video device +// +// FIXME:: test non mmap mode! +// +int gv4l2_read_frame( CWEBCAM * _object ) +{ + struct v4l2_buffer buf; + + if( !THIS->use_mmap ) { + gv4l2_debug("Using READ interface"); + if( read (THIS->io, THIS->buffers[0].start, THIS->buffers[0].length) == -1) { + switch (errno) { + case EAGAIN: + return 0; + case EIO: + /* Could ignore EIO, see spec. */ + /* fall through */ + default: + gv4l2_debug("READ ERROR"); + } + } + gv4l2_process_image (THIS,THIS->buffers[0].start); + return 1; + } + // + // This is the MMAP based read code + // + MCLEAR (buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + if( gv4l2_xioctl( THIS->io, VIDIOC_DQBUF, &buf) == -1 ) { + gv4l2_debug("DQBUF Error"); + switch (errno) { + case EAGAIN: + gv4l2_debug("EAGAIN"); + return 0; + case EIO: + /* Could ignore EIO, see spec. */ + /* fall through */ + default: + gv4l2_debug("VIDIOC_DQBUF READ ERROR"); + } + } + assert (buf.index < THIS->buffer_count); + gv4l2_process_image (THIS,THIS->buffers[buf.index].start); + + if( gv4l2_xioctl( THIS->io, VIDIOC_QBUF, &buf) == -1 ) { + gv4l2_debug("VIDIOC_QBUF READ ERROR"); + return 0; + } + return 1; +} +//============================================================================= +// +// gv4l2_resize( THIS , Width , Height ) +// +// Resize the display. +// Going to cheat a little here, easy way is to completely deactivate +// and let it start up with a new width and height .. :) +// +int gv4l2_resize( CWEBCAM * _object , int width , int height ) +{ + if(! gv4l2_stop_capture( THIS ) ) { + GB.Error("Failed to stop capturing on device"); + return 0; + } + gv4l2_uninit_device( THIS ); + + if( close(THIS->io ) == -1 ) { + gv4l2_debug("error closing device"); + } + + if ( !gv4l2_open_device( THIS->device )){ + GB.Error("Unable to reopen the device"); + return 0; + } + + if( !gv4l2_init_device(THIS , width , height ) ) { + GB.Error("Unable to initialise the device"); + return 0; + } + + gv4l2_start_capture( THIS ); + return 1; +} diff --git a/gb.v4l/src/main.c b/gb.v4l/src/main.c new file mode 100644 index 00000000..8d55c4f6 --- /dev/null +++ b/gb.v4l/src/main.c @@ -0,0 +1,50 @@ +/*************************************************************************** + + main.c + + (C) 2005-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include + +#include "CWebcam.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CWebcamDesc, + CTunerDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + return 0; +} + +void EXPORT GB_EXIT() +{ +} + diff --git a/gb.v4l/src/main.h b/gb.v4l/src/main.h new file mode 100644 index 00000000..8679e385 --- /dev/null +++ b/gb.v4l/src/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + (C) 2005-2008 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "gb.image.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +#endif + +#endif diff --git a/gb.v4l/src/videodev.h b/gb.v4l/src/videodev.h new file mode 100644 index 00000000..cd1f1547 --- /dev/null +++ b/gb.v4l/src/videodev.h @@ -0,0 +1,343 @@ +/*************************************************************************** + + videodev.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* + * Video for Linux version 1 - OBSOLETE + * + * Header file for v4l1 drivers and applications, for + * Linux kernels 2.2.x or 2.4.x. + * + * Provides header for legacy drivers and applications + * + * See http://linuxtv.org for more info + * + */ +#ifndef __LINUX_VIDEODEV_H +#define __LINUX_VIDEODEV_H + +#include +#include +#include + + +#define VID_TYPE_CAPTURE 1 /* Can capture */ +#define VID_TYPE_TUNER 2 /* Can tune */ +#define VID_TYPE_TELETEXT 4 /* Does teletext */ +#define VID_TYPE_OVERLAY 8 /* Overlay onto frame buffer */ +#define VID_TYPE_CHROMAKEY 16 /* Overlay by chromakey */ +#define VID_TYPE_CLIPPING 32 /* Can clip */ +#define VID_TYPE_FRAMERAM 64 /* Uses the frame buffer memory */ +#define VID_TYPE_SCALES 128 /* Scalable */ +#define VID_TYPE_MONOCHROME 256 /* Monochrome only */ +#define VID_TYPE_SUBCAPTURE 512 /* Can capture subareas of the image */ +#define VID_TYPE_MPEG_DECODER 1024 /* Can decode MPEG streams */ +#define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */ +#define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */ +#define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */ + +struct video_capability +{ + char name[32]; + int type; + int channels; /* Num channels */ + int audios; /* Num audio devices */ + int maxwidth; /* Supported width */ + int maxheight; /* And height */ + int minwidth; /* Supported width */ + int minheight; /* And height */ +}; + + +struct video_channel +{ + int channel; + char name[32]; + int tuners; + __u32 flags; +#define VIDEO_VC_TUNER 1 /* Channel has a tuner */ +#define VIDEO_VC_AUDIO 2 /* Channel has audio */ + __u16 type; +#define VIDEO_TYPE_TV 1 +#define VIDEO_TYPE_CAMERA 2 + __u16 norm; /* Norm set by channel */ +}; + +struct video_tuner +{ + int tuner; + char name[32]; + unsigned long rangelow, rangehigh; /* Tuner range */ + __u32 flags; +#define VIDEO_TUNER_PAL 1 +#define VIDEO_TUNER_NTSC 2 +#define VIDEO_TUNER_SECAM 4 +#define VIDEO_TUNER_LOW 8 /* Uses KHz not MHz */ +#define VIDEO_TUNER_NORM 16 /* Tuner can set norm */ +#define VIDEO_TUNER_STEREO_ON 128 /* Tuner is seeing stereo */ +#define VIDEO_TUNER_RDS_ON 256 /* Tuner is seeing an RDS datastream */ +#define VIDEO_TUNER_MBS_ON 512 /* Tuner is seeing an MBS datastream */ + __u16 mode; /* PAL/NTSC/SECAM/OTHER */ +#define VIDEO_MODE_PAL 0 +#define VIDEO_MODE_NTSC 1 +#define VIDEO_MODE_SECAM 2 +#define VIDEO_MODE_AUTO 3 + __u16 signal; /* Signal strength 16bit scale */ +}; + +struct video_picture +{ + __u16 brightness; + __u16 hue; + __u16 colour; + __u16 contrast; + __u16 whiteness; /* Black and white only */ + __u16 depth; /* Capture depth */ + __u16 palette; /* Palette in use */ +#define VIDEO_PALETTE_GREY 1 /* Linear greyscale */ +#define VIDEO_PALETTE_HI240 2 /* High 240 cube (BT848) */ +#define VIDEO_PALETTE_RGB565 3 /* 565 16 bit RGB */ +#define VIDEO_PALETTE_RGB24 4 /* 24bit RGB */ +#define VIDEO_PALETTE_RGB32 5 /* 32bit RGB */ +#define VIDEO_PALETTE_RGB555 6 /* 555 15bit RGB */ +#define VIDEO_PALETTE_YUV422 7 /* YUV422 capture */ +#define VIDEO_PALETTE_YUYV 8 +#define VIDEO_PALETTE_UYVY 9 /* The great thing about standards is ... */ +#define VIDEO_PALETTE_YUV420 10 +#define VIDEO_PALETTE_YUV411 11 /* YUV411 capture */ +#define VIDEO_PALETTE_RAW 12 /* RAW capture (BT848) */ +#define VIDEO_PALETTE_YUV422P 13 /* YUV 4:2:2 Planar */ +#define VIDEO_PALETTE_YUV411P 14 /* YUV 4:1:1 Planar */ +#define VIDEO_PALETTE_YUV420P 15 /* YUV 4:2:0 Planar */ +#define VIDEO_PALETTE_YUV410P 16 /* YUV 4:1:0 Planar */ +#define VIDEO_PALETTE_PLANAR 13 /* start of planar entries */ +#define VIDEO_PALETTE_COMPONENT 7 /* start of component entries */ +}; + +struct video_audio +{ + int audio; /* Audio channel */ + __u16 volume; /* If settable */ + __u16 bass, treble; + __u32 flags; +#define VIDEO_AUDIO_MUTE 1 +#define VIDEO_AUDIO_MUTABLE 2 +#define VIDEO_AUDIO_VOLUME 4 +#define VIDEO_AUDIO_BASS 8 +#define VIDEO_AUDIO_TREBLE 16 +#define VIDEO_AUDIO_BALANCE 32 + char name[16]; +#define VIDEO_SOUND_MONO 1 +#define VIDEO_SOUND_STEREO 2 +#define VIDEO_SOUND_LANG1 4 +#define VIDEO_SOUND_LANG2 8 + __u16 mode; + __u16 balance; /* Stereo balance */ + __u16 step; /* Step actual volume uses */ +}; + +struct video_clip +{ + __s32 x,y; + __s32 width, height; + struct video_clip *next; /* For user use/driver use only */ +}; + +struct video_window +{ + __u32 x,y; /* Position of window */ + __u32 width,height; /* Its size */ + __u32 chromakey; + __u32 flags; + struct video_clip *clips; /* Set only */ + int clipcount; +#define VIDEO_WINDOW_INTERLACE 1 +#define VIDEO_WINDOW_CHROMAKEY 16 /* Overlay by chromakey */ +#define VIDEO_CLIP_BITMAP -1 +/* bitmap is 1024x625, a '1' bit represents a clipped pixel */ +#define VIDEO_CLIPMAP_SIZE (128 * 625) +}; + +struct video_capture +{ + __u32 x,y; /* Offsets into image */ + __u32 width, height; /* Area to capture */ + __u16 decimation; /* Decimation divider */ + __u16 flags; /* Flags for capture */ +#define VIDEO_CAPTURE_ODD 0 /* Temporal */ +#define VIDEO_CAPTURE_EVEN 1 +}; + +struct video_buffer +{ + void *base; + int height,width; + int depth; + int bytesperline; +}; + +struct video_mmap +{ + unsigned int frame; /* Frame (0 - n) for double buffer */ + int height,width; + unsigned int format; /* should be VIDEO_PALETTE_* */ +}; + +struct video_key +{ + __u8 key[8]; + __u32 flags; +}; + +struct video_mbuf +{ + int size; /* Total memory to map */ + int frames; /* Frames */ + int offsets[VIDEO_MAX_FRAME]; +}; + +#define VIDEO_NO_UNIT (-1) + +struct video_unit +{ + int video; /* Video minor */ + int vbi; /* VBI minor */ + int radio; /* Radio minor */ + int audio; /* Audio minor */ + int teletext; /* Teletext minor */ +}; + +struct vbi_format { + __u32 sampling_rate; /* in Hz */ + __u32 samples_per_line; + __u32 sample_format; /* VIDEO_PALETTE_RAW only (1 byte) */ + __s32 start[2]; /* starting line for each frame */ + __u32 count[2]; /* count of lines for each frame */ + __u32 flags; +#define VBI_UNSYNC 1 /* can distingues between top/bottom field */ +#define VBI_INTERLACED 2 /* lines are interlaced */ +}; + +/* video_info is biased towards hardware mpeg encode/decode */ +/* but it could apply generically to any hardware compressor/decompressor */ +struct video_info +{ + __u32 frame_count; /* frames output since decode/encode began */ + __u32 h_size; /* current unscaled horizontal size */ + __u32 v_size; /* current unscaled veritcal size */ + __u32 smpte_timecode; /* current SMPTE timecode (for current GOP) */ + __u32 picture_type; /* current picture type */ + __u32 temporal_reference; /* current temporal reference */ + __u8 user_data[256]; /* user data last found in compressed stream */ + /* user_data[0] contains user data flags, user_data[1] has count */ +}; + +/* generic structure for setting playback modes */ +struct video_play_mode +{ + int mode; + int p1; + int p2; +}; + +/* for loading microcode / fpga programming */ +struct video_code +{ + char loadwhat[16]; /* name or tag of file being passed */ + int datasize; + __u8 *data; +}; + +#define VIDIOCGCAP _IOR('v',1,struct video_capability) /* Get capabilities */ +#define VIDIOCGCHAN _IOWR('v',2,struct video_channel) /* Get channel info (sources) */ +#define VIDIOCSCHAN _IOW('v',3,struct video_channel) /* Set channel */ +#define VIDIOCGTUNER _IOWR('v',4,struct video_tuner) /* Get tuner abilities */ +#define VIDIOCSTUNER _IOW('v',5,struct video_tuner) /* Tune the tuner for the current channel */ +#define VIDIOCGPICT _IOR('v',6,struct video_picture) /* Get picture properties */ +#define VIDIOCSPICT _IOW('v',7,struct video_picture) /* Set picture properties */ +#define VIDIOCCAPTURE _IOW('v',8,int) /* Start, end capture */ +#define VIDIOCGWIN _IOR('v',9, struct video_window) /* Get the video overlay window */ +#define VIDIOCSWIN _IOW('v',10, struct video_window) /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */ +#define VIDIOCGFBUF _IOR('v',11, struct video_buffer) /* Get frame buffer */ +#define VIDIOCSFBUF _IOW('v',12, struct video_buffer) /* Set frame buffer - root only */ +#define VIDIOCKEY _IOR('v',13, struct video_key) /* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */ +#define VIDIOCGFREQ _IOR('v',14, unsigned long) /* Set tuner */ +#define VIDIOCSFREQ _IOW('v',15, unsigned long) /* Set tuner */ +#define VIDIOCGAUDIO _IOR('v',16, struct video_audio) /* Get audio info */ +#define VIDIOCSAUDIO _IOW('v',17, struct video_audio) /* Audio source, mute etc */ +#define VIDIOCSYNC _IOW('v',18, int) /* Sync with mmap grabbing */ +#define VIDIOCMCAPTURE _IOW('v',19, struct video_mmap) /* Grab frames */ +#define VIDIOCGMBUF _IOR('v',20, struct video_mbuf) /* Memory map buffer info */ +#define VIDIOCGUNIT _IOR('v',21, struct video_unit) /* Get attached units */ +#define VIDIOCGCAPTURE _IOR('v',22, struct video_capture) /* Get subcapture */ +#define VIDIOCSCAPTURE _IOW('v',23, struct video_capture) /* Set subcapture */ +#define VIDIOCSPLAYMODE _IOW('v',24, struct video_play_mode) /* Set output video mode/feature */ +#define VIDIOCSWRITEMODE _IOW('v',25, int) /* Set write mode */ +#define VIDIOCGPLAYINFO _IOR('v',26, struct video_info) /* Get current playback info from hardware */ +#define VIDIOCSMICROCODE _IOW('v',27, struct video_code) /* Load microcode into hardware */ +#define VIDIOCGVBIFMT _IOR('v',28, struct vbi_format) /* Get VBI information */ +#define VIDIOCSVBIFMT _IOW('v',29, struct vbi_format) /* Set VBI information */ + + +#define BASE_VIDIOCPRIVATE 192 /* 192-255 are private */ + +/* VIDIOCSWRITEMODE */ +#define VID_WRITE_MPEG_AUD 0 +#define VID_WRITE_MPEG_VID 1 +#define VID_WRITE_OSD 2 +#define VID_WRITE_TTX 3 +#define VID_WRITE_CC 4 +#define VID_WRITE_MJPEG 5 + +/* VIDIOCSPLAYMODE */ +#define VID_PLAY_VID_OUT_MODE 0 + /* p1: = VIDEO_MODE_PAL, VIDEO_MODE_NTSC, etc ... */ +#define VID_PLAY_GENLOCK 1 + /* p1: 0 = OFF, 1 = ON */ + /* p2: GENLOCK FINE DELAY value */ +#define VID_PLAY_NORMAL 2 +#define VID_PLAY_PAUSE 3 +#define VID_PLAY_SINGLE_FRAME 4 +#define VID_PLAY_FAST_FORWARD 5 +#define VID_PLAY_SLOW_MOTION 6 +#define VID_PLAY_IMMEDIATE_NORMAL 7 +#define VID_PLAY_SWITCH_CHANNELS 8 +#define VID_PLAY_FREEZE_FRAME 9 +#define VID_PLAY_STILL_MODE 10 +#define VID_PLAY_MASTER_MODE 11 + /* p1: see below */ +#define VID_PLAY_MASTER_NONE 1 +#define VID_PLAY_MASTER_VIDEO 2 +#define VID_PLAY_MASTER_AUDIO 3 +#define VID_PLAY_ACTIVE_SCANLINES 12 + /* p1 = first active; p2 = last active */ +#define VID_PLAY_RESET 13 +#define VID_PLAY_END_MARK 14 + + +#endif /* __LINUX_VIDEODEV_H */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/gb.xml/AUTHORS b/gb.xml/AUTHORS new file mode 100755 index 00000000..e69de29b diff --git a/gb.xml/COPYING b/gb.xml/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/gb.xml/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/gb.xml/ChangeLog b/gb.xml/ChangeLog new file mode 100755 index 00000000..e69de29b diff --git a/gb.xml/INSTALL b/gb.xml/INSTALL new file mode 120000 index 00000000..99d491b4 --- /dev/null +++ b/gb.xml/INSTALL @@ -0,0 +1 @@ +../INSTALL \ No newline at end of file diff --git a/gb.xml/Makefile.am b/gb.xml/Makefile.am new file mode 100755 index 00000000..3765f66a --- /dev/null +++ b/gb.xml/Makefile.am @@ -0,0 +1,4 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = @XML_DIR@ +EXTRA_DIST = reconf gambas.h gb*.h + diff --git a/gb.xml/NEWS b/gb.xml/NEWS new file mode 100755 index 00000000..e69de29b diff --git a/gb.xml/README b/gb.xml/README new file mode 100755 index 00000000..53f792ab --- /dev/null +++ b/gb.xml/README @@ -0,0 +1,7 @@ +TODO : +GLrasterisation : +glBitmap() +glPolygonStipple() +glGetPolygonStipple() + +Waiting for texture implementation under components (needed to test !) diff --git a/gb.xml/TODO b/gb.xml/TODO new file mode 100644 index 00000000..1e9c72e1 --- /dev/null +++ b/gb.xml/TODO @@ -0,0 +1,8 @@ + +- Support "linked-nodes" (not urgent) + +- Make MANY tests with valgrind/gdb/whatyouwant , running many different projects, so that there isn't ANY bug or memory leak. + +- Check for any possible performance improvement + +- Improve my English. :/ \ No newline at end of file diff --git a/gb.xml/acinclude.m4 b/gb.xml/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/gb.xml/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/gb.xml/component.am b/gb.xml/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/gb.xml/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/gb.xml/configure.ac b/gb.xml/configure.ac new file mode 100755 index 00000000..66cf8fb6 --- /dev/null +++ b/gb.xml/configure.ac @@ -0,0 +1,37 @@ +dnl ---- configure.ac for gb.xml + +m4_include([../version.m4]) +AC_INIT(gambas3-gb-xml, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(gb.xml) +AC_PROG_LIBTOOL + +GB_COMPONENT( + xml, XML, gb.xml, [src], + [], + [], + [] +) + +GB_COMPONENT( + xmlhtml, XMLHTML, gb.xml.html, [html], + [], + [], + [] +) + +GB_COMPONENT_PKG_CONFIG( + xmlxslt, XMLXSLT, gb.xml.xslt, [xslt], + libxml-2.0 libxslt +) + +AC_OUTPUT( \ +Makefile \ +src/Makefile \ +src/html/Makefile \ +src/xslt/Makefile \ +src/rpc/Makefile \ +) + +GB_PRINT_MESSAGES \ No newline at end of file diff --git a/gb.xml/gambas.h b/gb.xml/gambas.h new file mode 120000 index 00000000..03677ecd --- /dev/null +++ b/gb.xml/gambas.h @@ -0,0 +1 @@ +../main/share/gambas.h \ No newline at end of file diff --git a/gb.xml/gb_common.h b/gb.xml/gb_common.h new file mode 120000 index 00000000..707d79da --- /dev/null +++ b/gb.xml/gb_common.h @@ -0,0 +1 @@ +../main/share/gb_common.h \ No newline at end of file diff --git a/gb.xml/m4 b/gb.xml/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/gb.xml/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/gb.xml/reconf b/gb.xml/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/gb.xml/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/gb.xml/src/.directory b/gb.xml/src/.directory new file mode 100755 index 00000000..22f35d7f --- /dev/null +++ b/gb.xml/src/.directory @@ -0,0 +1,6 @@ +[Dolphin] +Timestamp=2012,2,8,18,41,28 +Version=2 + +[Settings] +ShowDotFiles=true diff --git a/gb.xml/src/CDocument.cpp b/gb.xml/src/CDocument.cpp new file mode 100644 index 00000000..1a8e3477 --- /dev/null +++ b/gb.xml/src/CDocument.cpp @@ -0,0 +1,238 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CDocument.h" +#include "CNode.h" + +#include "node.h" +#include "document.h" +#include "element.h" +#include "utils.h" +#include "serializer.h" + +#define GBTHIS (static_cast(_object)) +#define THIS ((Document*)(static_cast(_object)->node)) + +BEGIN_METHOD(CDocument_new, GB_STRING fileName) + +if(XMLNode_NoInstanciate()) return; + +bool isHtml = false; + +if(GB.Is(_object, GB.FindClass("HtmlDocument")))//Called as inherited HtmlDocument constructor +{ + if(CheckHtmlInterface()) + { + isHtml = true; + } +} + +try +{ + if(!MISSING(fileName)) + { + if(isHtml) + { + GBTHIS->node = (Node*)XMLDocument_NewFromFile(STRING(fileName), LENGTH(fileName), HTMLDocumentType); + } + else + { + GBTHIS->node = (Node*)XMLDocument_NewFromFile(STRING(fileName), LENGTH(fileName)); + } + } + else + { + if(isHtml) + { + GBTHIS->node = (Node*)HTML.HtmlDocument_New(); + } + else + { + GBTHIS->node = (Node*)XMLDocument_New(); + } + } + + THIS->GBObject = (CDocument*)(_object); + +} +catch(XMLParseException &e) +{ + GB.Error(e.errorWhat); + GB.ReturnNull(); + XMLParseException_Cleanup(&e); +} + +END_METHOD + +BEGIN_METHOD_VOID(CDocument_free) + +XMLNode_Free(GBTHIS->node); + +END_METHOD + +BEGIN_METHOD(CDocument_fromString, GB_STRING content) + +try +{ + XMLDocument_SetContent(THIS, STRING(content), LENGTH(content)); +} +catch(XMLParseException &e) +{ + GB.Error(e.errorWhat); + XMLParseException_Cleanup(&e); +} + +END_METHOD + +BEGIN_METHOD(CDocument_tostring, GB_BOOLEAN indent) + + char *str = 0; + size_t len = 0; + GBserializeNode((Node*)THIS, str, len, VARG(indent) == -1 ? 0 : -1); + + GB.ReturnString(str); + +END_METHOD + +BEGIN_PROPERTY(CDocument_content) + +if(READ_PROPERTY) +{ + char *str = 0; + size_t len = 0; + + GBserializeNode((Node*)THIS, str, len); + + GB.ReturnString(str); +} +else +{ + try + { + XMLDocument_SetContent(THIS, PSTRING(), PLENGTH()); + } + catch(XMLParseException &e) + { + GB.Error(e.errorWhat); + XMLParseException_Cleanup(&e); + } +} + +END_PROPERTY + +BEGIN_PROPERTY(CDocument_root) + +if(READ_PROPERTY) +{ + XML_ReturnNode((Node*)(THIS->root)); +} +else +{ + XMLDocument_SetRoot(THIS, ((Element*)(VPROPOBJ(CNode)->node))); +} + +END_PROPERTY + +BEGIN_METHOD(CDocument_open, GB_STRING fileName) + +try +{ + XMLDocument_Open(THIS, STRING(fileName), LENGTH(fileName)); +} +catch(XMLParseException &e) +{ + GB.Error(e.errorWhat); + XMLParseException_Cleanup(&e); +} + +END_METHOD + +BEGIN_METHOD(CDocument_save, GB_STRING fileName; GB_BOOLEAN indent) + +XMLDocument_Save(THIS, GB.ToZeroString(ARG(fileName)), VARG(indent)); + +END_METHOD + +BEGIN_METHOD(CDocument_getElementsByTagName, GB_STRING tagName; GB_INTEGER mode; GB_INTEGER depth;) + +GB_ARRAY array; + +XMLNode_getGBChildrenByTagName(THIS, STRING(tagName), LENGTH(tagName), &array, VARGOPT(mode, GB_STRCOMP_BINARY), VARGOPT(depth, -1)); + +GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(CDocument_getElementsByNamespace, GB_STRING name; GB_INTEGER mode; GB_INTEGER depth;) + +GB_ARRAY array; + +XMLNode_getGBChildrenByTagName(THIS, STRING(name), LENGTH(name), &array, VARGOPT(mode, GB_STRCOMP_BINARY), VARGOPT(depth, -1)); + +GB.ReturnObject(array); + +END_METHOD + +BEGIN_PROPERTY(CDocument_getAll) + +GB_ARRAY array; + +XMLNode_getGBAllChildren(THIS, &array); + +GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(CDocument_createElement, GB_STRING tagName) + +Element *elmt = XMLElement_New(STRING(tagName), LENGTH(tagName)); + +XML_ReturnNode(elmt); + +END_METHOD + +GB_DESC CDocumentDesc[] = +{ + GB_DECLARE("XmlDocument", sizeof(CDocument)), + + GB_METHOD("_new", NULL, CDocument_new, "[(FileName)s]"), + GB_METHOD("_free", NULL, CDocument_free, NULL), + + GB_METHOD("CreateElement", "XmlElement", CDocument_createElement, "(TagName)s"), + GB_PROPERTY("Root", "XmlElement", CDocument_root), + GB_PROPERTY_READ("All", "XmlNode[]", CDocument_getAll), + GB_METHOD("GetElementsByTagName", "XmlElement[]", CDocument_getElementsByTagName, "(TagName)s[(Mode)i(Depth)i]"), + GB_METHOD("GetElementsByNamespace", "XmlElement[]", CDocument_getElementsByNamespace, "(Namespace)s[(Mode)i(Depth)i]"), + + + + GB_PROPERTY("Content", "s", CDocument_content), + GB_METHOD("FromString", NULL, CDocument_fromString, "(Data)s"), + GB_METHOD("HtmlFromString", NULL, CDocument_fromString, "(Data)s"), + GB_METHOD("ToString", "s", CDocument_tostring, "[(Indent)b]"), + + GB_METHOD("Open", NULL, CDocument_open, "(FileName)s"), + GB_METHOD("Save", NULL, CDocument_save, "(FileName)s[(Indent)b]"), + GB_METHOD("Write", NULL, CDocument_save, "(FileName)s[(Indent)b]"), + + + GB_END_DECLARE +}; diff --git a/gb.xml/src/CDocument.h b/gb.xml/src/CDocument.h new file mode 100644 index 00000000..10f0bc5c --- /dev/null +++ b/gb.xml/src/CDocument.h @@ -0,0 +1,29 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef CDOCUMENT_H +#define CDOCUMENT_H + +#include "gbinterface.h" + +extern GB_DESC CDocumentDesc[]; + +#endif // CDOCUMENT_H diff --git a/gb.xml/src/CElement.cpp b/gb.xml/src/CElement.cpp new file mode 100644 index 00000000..8dc3a248 --- /dev/null +++ b/gb.xml/src/CElement.cpp @@ -0,0 +1,411 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CElement.h" + +#include "node.h" +#include "element.h" +#include "textnode.h" +#include "parser.h" + + +#include + +#define THIS ((Element*)(static_cast(_object)->node)) +#define THISNODE (static_cast(_object)->node) + +BEGIN_METHOD(CElement_new, GB_STRING tagName) + +if(XMLNode_NoInstanciate()) return; +if(!MISSING(tagName)) +{ + THISNODE = XMLElement_New(STRING(tagName), LENGTH(tagName)); +} +else +{ +THISNODE = XMLElement_New(); +} + THIS->GBObject = static_cast(_object); + +END_METHOD + +BEGIN_PROPERTY(CElement_tagName) + +if(READ_PROPERTY) +{ + if(!THIS->tagName || !THIS->lenTagName) + { + GB.ReturnNull(); + return; + } + else + { + GB.ReturnNewString(THIS->tagName, THIS->lenTagName); + } +} +else +{ + XMLElement_SetTagName(THIS, PSTRING(), PLENGTH()); +} + +END_PROPERTY + +BEGIN_PROPERTY(CElement_prefix) + +if(READ_PROPERTY) +{ + if(!THIS->prefix || !THIS->lenPrefix) + { + GB.ReturnNull(); + return; + } + GB.ReturnNewString(THIS->prefix, THIS->lenPrefix); +} +else +{ + XMLElement_SetPrefix(THIS, PSTRING(), PLENGTH()); +} + +END_PROPERTY + +BEGIN_METHOD(CElement_appendChild, GB_OBJECT newChild) + + if(!VARGOBJ(CNode, newChild)) + { + GB.Error("Null object"); + return; + } + XMLNode_appendChild(THIS, VARGOBJ(CNode, newChild)->node); + +END_METHOD + +BEGIN_METHOD(CElement_getAttribute, GB_STRING attrName; GB_INTEGER mode) + +Attribute *attr = XMLElement_GetAttribute(THIS, STRING(attrName), LENGTH(attrName), VARG(mode)); + +if(attr && attr->attrValue && attr->lenAttrValue) +{ + GB.ReturnNewString(attr->attrValue, attr->lenAttrValue); +} +else +{ + GB.ReturnNull(); +} + +END_METHOD + +BEGIN_METHOD(CElement_removeAttribute, GB_STRING attrName) + + XMLElement_RemoveAttribute(THIS, STRING(attrName), LENGTH(attrName)); + +END_METHOD + +BEGIN_METHOD(CElement_setAttribute, GB_STRING attrName; GB_STRING attrValue) + + XMLElement_SetAttribute(THIS, STRING(attrName), LENGTH(attrName), + STRING(attrValue), LENGTH(attrValue)); + +END_METHOD + +BEGIN_METHOD(CElement_appendFromText, GB_STRING data; GB_VALUE param[0]) + +try +{ + if(GB.NParam() > 0) + { + XMLNode_substAppendFromText(THIS, STRING(data), LENGTH(data), ARG(param[0]), GB.NParam()); + } + else + { + XMLNode_appendFromText(THIS, STRING(data), LENGTH(data)); + } +} +catch(XMLParseException &e) +{ + GB.Error(e.errorWhat); + XMLParseException_Cleanup(&e); +} +END_METHOD + +BEGIN_METHOD(CElement_appendChildren, GB_OBJECT children) + +GB_ARRAY array = reinterpret_cast(VARG(children)); + +for(int i = 0; i < GB.Array.Count(array); i++) +{ + XMLNode_appendChild(THIS, (*(reinterpret_cast(GB.Array.Get(array, i))))->node); +} + +END_METHOD + +BEGIN_METHOD(CElement_prependChild, GB_OBJECT newChild) + +XMLNode_prependChild(THIS, VARGOBJ(CNode, newChild)->node); + +END_METHOD + +BEGIN_METHOD(CElement_removeChild, GB_OBJECT oldChild) + +XMLNode_removeChild(THIS, VARGOBJ(CNode, oldChild)->node); + +END_METHOD + +BEGIN_METHOD(CElement_replaceChild, GB_OBJECT oldChild; GB_OBJECT newChild) + +XMLNode_replaceChild(THIS, VARGOBJ(CNode, oldChild)->node, + VARGOBJ(CNode, newChild)->node); + +END_METHOD + +BEGIN_METHOD(CElement_insertAfter, GB_OBJECT child; GB_OBJECT newChild) + +XMLNode_insertAfter(THIS, VARGOBJ(CNode, child)->node, VARGOBJ(CNode, newChild)->node); + +END_METHOD + +BEGIN_METHOD(CElement_insertBefore, GB_OBJECT child; GB_OBJECT newChild) + +XMLNode_insertBefore(THIS, VARGOBJ(CNode, child)->node, VARGOBJ(CNode, newChild)->node); + +END_METHOD + +BEGIN_METHOD(CElement_appendText, GB_STRING data) + +XMLNode_appendText(THIS, STRING(data), LENGTH(data)); + +END_METHOD + +BEGIN_METHOD_VOID(CElement_clearChildren) + +XMLNode_clearChildren(THIS); + +END_METHOD + +BEGIN_PROPERTY(CElement_previousElement) + +XML_ReturnNode(XMLNode_previousElement(THIS)); + +END_PROPERTY + +BEGIN_PROPERTY(CElement_nextElement) + +XML_ReturnNode(XMLNode_nextElement(THIS)); + +END_PROPERTY + +BEGIN_METHOD(CElement_isAttributeSet, GB_STRING name) + +GB.ReturnBoolean((bool)(XMLElement_GetAttribute(THIS, STRING(name), LENGTH(name)))); + +END_METHOD + +BEGIN_PROPERTY(CElement_allChildNodes) + +GB_ARRAY array; +XMLNode_getGBAllChildren(THIS, &array); + +GB.ReturnObject(array); + +END_PROPERTY + +BEGIN_PROPERTY(CElement_firstChildElement) + +XML_ReturnNode(XMLNode_firstChildElement(THIS)); + +END_PROPERTY + +BEGIN_PROPERTY(CElement_lastChildElement) + +XML_ReturnNode(XMLNode_lastChildElement(THIS)); + +END_PROPERTY + +BEGIN_PROPERTY(CElement_childElements) + +GB_ARRAY array; +XMLNode_getGBChildElements(THIS, &array); +GB.ReturnObject(array); + +END_PROPERTY + +BEGIN_METHOD(CElement_fromText, GB_STRING data) + +GB_ARRAY array; +try +{ + GBparseXML(STRING(data), LENGTH(data), &array); +} +catch(XMLParseException &e) +{ + GB.Error(e.errorWhat); + XMLParseException_Cleanup(&e); +} +GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(CElement_getChildrenByTagName, GB_STRING tagName; GB_INTEGER mode ; GB_INTEGER depth;) + +GB_ARRAY array; +XMLNode_getGBChildrenByTagName(THIS, STRING(tagName), LENGTH(tagName), &array, VARGOPT(mode, GB_STRCOMP_BINARY), VARGOPT(depth, -1)); +GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(CElement_getChildrenByNamespace, GB_STRING name; GB_INTEGER mode ; GB_INTEGER depth;) + +GB_ARRAY array; +XMLNode_getGBChildrenByNamespace(THIS, STRING(name), LENGTH(name), &array, VARGOPT(mode, GB_STRCOMP_BINARY), VARGOPT(depth, -1)); +GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(CElement_getChildrenByAttributeValue, GB_STRING name; GB_STRING value; GB_INTEGER mode ; GB_INTEGER depth;) + +GB_ARRAY array; +XMLNode_getGBChildrenByAttributeValue(THIS, STRING(name), LENGTH(name), + STRING(value), LENGTH(value), + &array, VARGOPT(mode, GB_STRCOMP_BINARY), VARGOPT(depth, -1)); +GB.ReturnObject(array); + +END_METHOD + +BEGIN_PROPERTY(CElement_firstChild) + +XML_ReturnNode(THIS->firstChild); + +END_METHOD + +BEGIN_PROPERTY(CElement_lastChild) + +XML_ReturnNode(THIS->lastChild); + +END_PROPERTY + +BEGIN_METHOD(CElement_normalizeAttributeContent, GB_STRING data) + +if(!LENGTH(data)) +{ + GB.ReturnNull(); + return; +} + +char *escapedData; size_t lenEscapedData; + +XMLText_escapeAttributeContent(STRING(data), LENGTH(data), escapedData, lenEscapedData); + +GB.ReturnNewString(escapedData, lenEscapedData); + +if(escapedData != STRING(data)) free(escapedData); + +END_METHOD + + + +GB_DESC CElementDesc[] = +{ + GB_DECLARE("XmlElement", sizeof(CNode)), GB_INHERITS("XmlNode"), + + GB_METHOD("_new", NULL, CElement_new, "[(TagName)s]"), + + GB_METHOD("AppendChild", NULL, CElement_appendChild, "(NewChild)XmlNode"), + GB_METHOD("AppendChildren", NULL, CElement_appendChildren, "(NewChildren)XmlNode[]"), + GB_METHOD("PrependChild", NULL, CElement_prependChild, "(NewChild)XmlNode"), + GB_METHOD("InsertAfter", NULL, CElement_insertAfter, "(Child)XmlNode;(NewChild)XmlNode"), + GB_METHOD("InsertBefore", NULL, CElement_insertBefore, "(Child)XmlNode;(NewChild)XmlNode"), + GB_METHOD("RemoveChild", NULL, CElement_removeChild, "(OldChild)XmlNode"), + GB_METHOD("ReplaceChild", NULL, CElement_replaceChild, "(OldChild)XmlNode;(NewChild)XmlNode"), + GB_METHOD("ClearChildren", NULL, CElement_clearChildren, ""), + + GB_METHOD("AppendText", NULL, CElement_appendText, "(Data)s"), + GB_METHOD("AppendFromText", NULL, CElement_appendFromText, "(Data)s(Arguments)."), + + GB_METHOD("GetAttribute", "s", CElement_getAttribute, "(Name)s[(Mode)i]"), + GB_METHOD("RemoveAttribute", NULL, CElement_removeAttribute, "(Name)s"), + GB_METHOD("SetAttribute", NULL, CElement_setAttribute, "(Name)s(Value)s"), + + GB_METHOD("IsAttributeSet", "b", CElement_isAttributeSet, "(Name)s"), + + GB_PROPERTY_READ("ChildElements", "XmlElement[]", CElement_childElements), + GB_PROPERTY_READ("AllChildNodes", "XmlNode[]", CElement_allChildNodes), + GB_PROPERTY_READ("FirstChild", "XmlNode", CElement_firstChild), + GB_PROPERTY_READ("LastChild", "XmlNode", CElement_lastChild), + GB_PROPERTY_READ("FirstChildElement", "XmlElement", CElement_firstChildElement), + GB_PROPERTY_READ("LastChildElement", "XmlElement", CElement_lastChildElement), + + GB_PROPERTY("TagName", "s", CElement_tagName), + GB_PROPERTY("Prefix", "s", CElement_prefix), + GB_PROPERTY("PreviousElement", "XmlElement", CElement_previousElement), + GB_PROPERTY("NextElement", "XmlElement", CElement_nextElement), + + GB_METHOD("GetChildrenByNamespace", "XmlElement[]", CElement_getChildrenByNamespace, "(Namespace)s[(Mode)i(Depth)i]"), + GB_METHOD("GetChildrenByTagName", "XmlElement[]", CElement_getChildrenByTagName, "(TagName)s[(Mode)i(Depth)i]"), + GB_METHOD("GetChildrenByAttributeValue", "XmlElement[]", CElement_getChildrenByAttributeValue, "(Attribute)s(Value)s[(Mode)i(Depth)i]"), + + GB_STATIC_METHOD("FromText", "XmlNode[]", CElement_fromText, "(Data)s"), + GB_STATIC_METHOD("NormalizeAttributeContent", "s", CElement_normalizeAttributeContent, "(Data)s"), + + + GB_END_DECLARE +}; + +/* +GB_DESC CElementDesc[] = +{ + GB_DECLARE("XmlElement", sizeof(CElement)), GB_INHERITS("XmlNode"), + GB_METHOD("_new", NULL, CElement_new, "[(TagName)s]"), + GB_METHOD("_free", NULL, CElement_free, ""), + GB_METHOD("AppendChild", NULL, CElement_AppendChild, "(NewChild)XmlNode"), + GB_METHOD("AppendChildren", NULL, CElement_AppendChildren, "(NewChildren)XmlNode[]"), + GB_METHOD("PrependChild", NULL, CElement_prependChild, "(NewChild)XmlNode"), + GB_METHOD("InsertAfter", NULL, CElement_insertAfter, "(Child)XmlNode;(NewChild)XmlNode"), + GB_METHOD("InsertBefore", NULL, CElement_insertBefore, "(Child)XmlNode;(NewChild)XmlNode"), + GB_METHOD("AppendText", NULL, CElement_AppendText, "(NewText)s"), + GB_METHOD("AppendFromText", NULL, CElement_appendFromText, "(Data)s"), + GB_METHOD("RemoveChild", NULL, CElement_removeChild, "(OldChild)XmlNode"), + GB_METHOD("ReplaceChild", NULL, CElement_replaceChild, "(OldChild)XmlNode;(NewChild)XmlNode"), + GB_METHOD("NewElement", NULL, CElement_newElement, "(Name)s[(Value)s]"), + + GB_PROPERTY("TagName", "s", CElement_tagName), + + GB_PROPERTY_READ("PreviousSibling", "XmlElement", CElement_previousSibling), + GB_PROPERTY_READ("NextSibling", "XmlElement", CElement_nextSibling), + + GB_METHOD("GetAttribute", "s", CElement_getAttribute, "(Name)s"), + GB_METHOD("SetAttribute", NULL, CElement_setAttribute, "(Name)s(Value)s"), + GB_METHOD("NewAttribute", NULL, CElement_setAttribute, "(Name)s(Value)s"), + GB_METHOD("IsAttributeSet", "b", CElement_isAttributeSet, "(Name)s"), + + GB_PROPERTY_READ("ChildNodes", "XmlNode[]", CElement_childNodes), + GB_PROPERTY_READ("Children", "XmlNode[]", CElement_childNodes), + GB_PROPERTY_READ("ChildElements", "XmlElement[]", CElement_childElements), + GB_PROPERTY_READ("AllChildNodes", "XmlNode[]", CElement_allChildNodes), + GB_PROPERTY_READ("FirstChildElement", "XmlElement", CElement_firstChildElement), + GB_PROPERTY_READ("LastChildElement", "XmlElement", CElement_lastChildElement), + + GB_METHOD("MatchXPathFilter", "b", CElement_matchXPathFilter ,"(Filter)s"), + GB_METHOD("GetChildrenByTagName", "XmlElement[]", CElement_getChildrenByTagName, "(TagName)s[(Depth)i]"), + GB_METHOD("GetChildrenByAttributeValue", "XmlElement[]", CElement_getChildrenByAttributeValue, "(Attribute)s(Value)s[(Depth)i]"), + + GB_STATIC_METHOD("FromText", "XmlNode[]", CElement_fromText, "(Data)s"), + + GB_END_DECLARE +};*/ diff --git a/gb.xml/src/CElement.h b/gb.xml/src/CElement.h new file mode 100644 index 00000000..958980e6 --- /dev/null +++ b/gb.xml/src/CElement.h @@ -0,0 +1,29 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef CELEMENT_H +#define CELEMENT_H + +#include "gbinterface.h" + +extern GB_DESC CElementDesc[]; + +#endif // CELEMENT_H diff --git a/gb.xml/src/CExplorer.cpp b/gb.xml/src/CExplorer.cpp new file mode 100644 index 00000000..dc1da9ba --- /dev/null +++ b/gb.xml/src/CExplorer.cpp @@ -0,0 +1,154 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CExplorer.h" +#include "explorer.h" + +#include "node.h" +#include "document.h" +#include "utils.h" + +#undef THIS +#define THIS (static_cast(_object)->explorer) + +BEGIN_METHOD(CExplorerReadFlags_get, GB_INTEGER flag) + +int flag = VARG(flag); +if(flag > FLAGS_COUNT || flag < 0) +{ + GB.ReturnBoolean(false); + return; +} +GB.ReturnBoolean(THIS->flags[flag]); + +END_METHOD + +BEGIN_METHOD(CExplorerReadFlags_put, GB_BOOLEAN value; GB_INTEGER flag) + +int flag = VARG(flag); +if(flag > FLAGS_COUNT || flag < 0 || flag == READ_ERR_EOF) return; +THIS->flags[flag] = VARG(value); + +END_METHOD + +BEGIN_PROPERTY(CExplorer_Node) + +XML_ReturnNode(THIS->curNode); + +END_PROPERTY + +BEGIN_METHOD_VOID(CExplorer_Read) + +GB.ReturnInteger(THIS->Read()); + +END_METHOD + +BEGIN_METHOD(CExplorer_new, GB_OBJECT doc) + +THIS = new Explorer; + +if(!MISSING(doc)) +{ + THIS->Load((Document*)(VARGOBJ(CDocument, doc)->node)); +} + +END_METHOD + +BEGIN_METHOD_VOID(CExplorer_free) + +delete THIS; + +END_METHOD + +BEGIN_METHOD(CExplorer_open, GB_STRING path) + +try +{ + Document *doc = XMLDocument_NewFromFile(STRING(path), LENGTH(path)); + THIS->Load(doc); +} +catch(XMLParseException &e) +{ + GB.Error(e.errorWhat); + XMLParseException_Cleanup(&e); +} + + +END_METHOD + +BEGIN_PROPERTY(CExplorer_eof) + +GB.ReturnBoolean(THIS->eof); + +END_PROPERTY + +BEGIN_PROPERTY(CExplorer_state) + +GB.ReturnInteger(THIS->state); + +END_PROPERTY + +BEGIN_METHOD(CExplorer_load, GB_OBJECT doc) + +THIS->Load((Document*)(VARGOBJ(CDocument, doc)->node)); + +END_METHOD + +BEGIN_PROPERTY(CExplorer_document) + +if(READ_PROPERTY) +{ + XML_ReturnNode(THIS->loadedDocument); +} +else +{ + THIS->Load((Document*)(VPROPOBJ(CDocument)->node)); +} + +END_PROPERTY + +GB_DESC CExplorerReadFlagsDesc[] = +{ + GB_DECLARE(".XmlExplorerReadFlags", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", "b", CExplorerReadFlags_get, "(Flag)i"), + GB_METHOD("_put", "b", CExplorerReadFlags_put, "(Value)b(Flag)i"), + + GB_END_DECLARE +}; + +GB_DESC CExplorerDesc[] = +{ + GB_DECLARE("XmlExplorer", sizeof(CExplorer)), + + GB_METHOD("_new", NULL, CExplorer_new, "[(Document)XmlDocument]"), + GB_METHOD("_free", NULL, CExplorer_free, ""), + GB_METHOD("Load", NULL, CExplorer_load, "(Document)XmlDocument"), + GB_PROPERTY("Document", "XMLDocument", CExplorer_document), + GB_PROPERTY_SELF("ReadFlags", ".XmlExplorerReadFlags"), + GB_PROPERTY_READ("Node", "XmlNode", CExplorer_Node), + GB_PROPERTY_READ("Eof", "b", CExplorer_eof), + GB_PROPERTY_READ("State", "i", CExplorer_state), + GB_METHOD("Read", "i", CExplorer_Read, ""), + GB_METHOD("Open", NULL, CExplorer_open, "(Path)s"), + + GB_END_DECLARE +}; diff --git a/gb.xml/src/CExplorer.h b/gb.xml/src/CExplorer.h new file mode 100644 index 00000000..308f306f --- /dev/null +++ b/gb.xml/src/CExplorer.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef CEXPLORER_H +#define CEXPLORER_H + +#include "gbinterface.h" + +class Explorer; + +struct CExplorer +{ + GB_BASE ob; + Explorer *explorer; +}; + +extern GB_DESC CExplorerDesc[]; + +#endif // CEXPLORER_H diff --git a/gb.xml/src/CNode.cpp b/gb.xml/src/CNode.cpp new file mode 100644 index 00000000..d9730ca8 --- /dev/null +++ b/gb.xml/src/CNode.cpp @@ -0,0 +1,438 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CNode.h" +#include "node.h" +#include "document.h" +#include "element.h" +#include "textnode.h" +#include "serializer.h" +#include + +#define THISOBJ ((CNode*)_object) +#define THIS (static_cast(_object)->node) + +#define NODE_BASE 0 +#define NODE_ELEMENT 1 +#define NODE_TEXT 2 +#define NODE_COMMENT 3 +#define NODE_CDATA 4 +#define NODE_ATTRIBUTE 5 +#define NODE_DOCUMENT 6 + +BEGIN_METHOD_VOID(CNode_new) + +if(XMLNode_NoInstanciate()) return; + + +END_METHOD + +BEGIN_METHOD_VOID(CNode_free) + +XMLNode_DestroyGBObject(THIS); + +END_METHOD + +BEGIN_METHOD(CNode_tostring, GB_BOOLEAN indent) + + char *str = 0; + size_t len = 0; + + GBserializeNode(THIS, str, len, VARG(indent) ? 0 : -1); + + GB.ReturnString(str); + +END_METHOD + +BEGIN_PROPERTY(CNode_type) + +switch(THIS->type) +{ + case Node::ElementNode: + GB.ReturnInteger(NODE_ELEMENT);break; + case Node::Comment: + GB.ReturnInteger(NODE_COMMENT);break; + case Node::NodeText: + GB.ReturnInteger(NODE_TEXT);break; + case Node::CDATA: + GB.ReturnInteger(NODE_CDATA);break; + default: + GB.ReturnInteger(NODE_BASE); +} + +END_PROPERTY + +BEGIN_PROPERTY(CNode_isElement) + +GB.ReturnBoolean(THIS->type == Node::ElementNode); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_isText) + +GB.ReturnBoolean(THIS->type == Node::NodeText); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_isComment) + +GB.ReturnBoolean(THIS->type == Node::Comment); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_isCDATA) + +GB.ReturnBoolean(THIS->type == Node::CDATA); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_element) + +XML_ReturnNode(THIS); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_ownerDocument) + +XML_ReturnNode((Node*)XMLNode_GetOwnerDocument((Node*)THIS)); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_parent) + +XML_ReturnNode((Node*)(THIS->parent)); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_previous) + +XML_ReturnNode((Node*)(THIS->previousNode)); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_next) + +XML_ReturnNode((Node*)(THIS->nextNode)); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_textContent) + + +if(READ_PROPERTY) +{ + char *data; size_t len; + GBGetXMLTextContent(THIS, data, len); + GB.ReturnString(data); +} +else +{ + XMLNode_setTextContent(THIS, PSTRING(), PLENGTH()); +} + +END_PROPERTY + +BEGIN_PROPERTY(CNode_name) + +if(!READ_PROPERTY) +{ + if(THIS->type == Node::ElementNode) + { + XMLElement_SetTagName((Element*)THIS, PSTRING(), PLENGTH()); + } + return; +} + +switch (THIS->type) +{ + case Node::ElementNode: + GB.ReturnNewString(((Element*)THIS)->tagName, ((Element*)THIS)->lenTagName);break; + case Node::NodeText: + GB.ReturnNewZeroString("#text");break; + case Node::Comment: + GB.ReturnNewZeroString("#comment");break; + case Node::CDATA: + GB.ReturnNewZeroString("#cdata");break; + case Node::AttributeNode: + GB.ReturnNewString(((Attribute*)THIS)->attrName, ((Attribute*)THIS)->lenAttrName);break; + default: + GB.ReturnNewZeroString(""); +} + +END_PROPERTY + + +BEGIN_METHOD(CNode_newElement, GB_STRING name; GB_STRING value) + +if(!SUPPORT_CHILDREN(THIS)) return; +Element *elmt = XMLElement_New(STRING(name), LENGTH(name)); +if(!MISSING(value)) XMLElement_SetTextContent(elmt, STRING(value), LENGTH(value)); +XMLNode_appendChild(THIS, elmt); + +END_METHOD + +BEGIN_METHOD(CNode_setAttribute, GB_STRING attr; GB_STRING val) + +if(THIS->type != Node::ElementNode) return; + +XMLElement_SetAttribute(((Element*)THIS), STRING(attr), LENGTH(attr), + STRING(val), LENGTH(val)); + +END_METHOD + + +BEGIN_METHOD(CElementAttributes_get, GB_STRING name) + +if(THIS->type != Node::ElementNode) return; + +Attribute *attr = XMLElement_GetAttribute((Element*)THIS, STRING(name), LENGTH(name)); + +if(attr && attr->attrValue && attr->lenAttrValue) +{ + GB.ReturnNewString(attr->attrValue, attr->lenAttrValue); +} +else +{ + GB.ReturnNull(); +} + +END_METHOD + +BEGIN_METHOD(CElementAttributes_put, GB_STRING value; GB_STRING name) + +if(THIS->type != Node::ElementNode) return; + +XMLElement_SetAttribute((Element*)THIS, STRING(name), LENGTH(name), + STRING(value), LENGTH(value)); + +END_METHOD + +BEGIN_PROPERTY(CElementAttributes_count) + +if(THIS->type != Node::ElementNode) +{ + GB.ReturnInteger(0); + return; +} + +if(READ_PROPERTY) +{ + GB.ReturnInteger(((Element*)THIS)->attributeCount); +} + +END_PROPERTY + +BEGIN_METHOD_VOID(CElementAttributes_next) + +if(THIS->type != Node::ElementNode) {GB.StopEnum(); return;} + +Attribute *attr = *reinterpret_cast((GB.GetEnum())); +if(attr == 0) +{ + attr = ((Element*)THIS)->firstAttribute; + *reinterpret_cast(GB.GetEnum()) = attr; +} +else +{ + attr = (Attribute*)attr->nextNode; + *reinterpret_cast(GB.GetEnum()) = attr; +} + +THISOBJ->curAttrEnum = attr; + +if(attr == 0) {GB.StopEnum(); return;} + + +XML_ReturnNode(attr); + + +END_METHOD + + +BEGIN_PROPERTY(CElementAttributes_name) + +if(!THISOBJ->curAttrEnum) +{ + GB.Error("No enumerated attribute available"); + GB.ReturnNull(); + return; +} + +if(!THISOBJ->curAttrEnum->attrName || !THISOBJ->curAttrEnum->lenAttrName) +{ + GB.ReturnNull(); + return; +} + +GB.ReturnNewString(THISOBJ->curAttrEnum->attrName, THISOBJ->curAttrEnum->lenAttrName); + +END_PROPERTY + +BEGIN_PROPERTY(CElementAttributes_value) + +if(!THISOBJ->curAttrEnum) +{ + GB.Error("No enumerated attribute available"); + GB.ReturnNull(); + return; +} + +if(!THISOBJ->curAttrEnum->attrValue || !THISOBJ->curAttrEnum->lenAttrValue) +{ + GB.ReturnNull(); + return; +} + +GB.ReturnNewString(THISOBJ->curAttrEnum->attrValue, THISOBJ->curAttrEnum->lenAttrValue); + +END_PROPERTY + +BEGIN_PROPERTY(CNode_childNodes) + +GB_ARRAY array; +XMLNode_getGBChildren(THIS, &array); + +GB.ReturnObject(array); + +END_PROPERTY + +BEGIN_METHOD(CNode_getUserData, GB_STRING key) + +GB_VARIANT *value = XMLNode_getUserData(THIS, STRING(key), LENGTH(key)); + +if(value) +{ + GB.ReturnVariant(&(value->value)); + delete value; +} +else +{ + GB.ReturnNull(); +} + +END_METHOD + +BEGIN_METHOD(CNode_setUserData, GB_STRING key; GB_VARIANT value) + +XMLNode_addUserData(THIS, STRING(key), LENGTH(key), ARG(value)); + +END_METHOD + +BEGIN_METHOD(CNode_escapeContent, GB_STRING data) + +if(!LENGTH(data)) +{ + GB.ReturnNull(); + return; +} + +char *escapedData; size_t lenEscapedData; + +XMLText_escapeContent(STRING(data), LENGTH(data), escapedData, lenEscapedData); + +GB.ReturnNewString(escapedData, lenEscapedData); + +if(escapedData != STRING(data)) free(escapedData); + +END_METHOD + +BEGIN_METHOD(CNode_unEscapeContent, GB_STRING data) + +if(!LENGTH(data)) +{ + GB.ReturnNull(); + return; +} + +char *unescapedData; size_t lenUnEscapedData; + +XMLText_unEscapeContent(STRING(data), LENGTH(data), unescapedData, lenUnEscapedData); + +GB.ReturnNewString(unescapedData, lenUnEscapedData); + +if(unescapedData != STRING(data)) free(unescapedData); + +END_METHOD + +GB_DESC CElementAttributeNodeDesc[] = +{ + GB_DECLARE("_XmlAttrNode", sizeof(CNode)), GB_INHERITS("XmlNode"), + + GB_END_DECLARE +}; + +GB_DESC CElementAttributesDesc[] = +{ + GB_DECLARE(".XmlElementAttributes", 0), GB_VIRTUAL_CLASS(), + GB_METHOD("_get", "s", CElementAttributes_get, "(Name)s"), + GB_METHOD("_put", "s", CElementAttributes_put, "(Value)s(Name)s"), + GB_METHOD("_next", "XmlNode", CElementAttributes_next, ""), + GB_PROPERTY_READ("Count", "i", CElementAttributes_count), + GB_PROPERTY_READ("Name", "s", CElementAttributes_name), + GB_PROPERTY_READ("Value", "s", CElementAttributes_value), + GB_END_DECLARE +}; + +GB_DESC CNodeDesc[] = +{ + GB_DECLARE("XmlNode", sizeof(CNode)), GB_NOT_CREATABLE(), + + GB_METHOD("_new", NULL, CNode_new, ""), + GB_METHOD("_free", NULL, CNode_free, ""), + + GB_CONSTANT("ElementNode", "i", NODE_ELEMENT), + GB_CONSTANT("TextNode", "i", NODE_TEXT), + GB_CONSTANT("CommentNode", "i", NODE_COMMENT), + GB_CONSTANT("CDATASectionNode", "i", NODE_CDATA), + GB_CONSTANT("AttributeNode", "i", NODE_ATTRIBUTE), + + GB_PROPERTY_READ("Type", "i", CNode_type), + GB_PROPERTY_READ("IsElement", "b", CNode_isElement), + GB_PROPERTY_READ("IsText", "b", CNode_isText), + GB_PROPERTY_READ("IsComment", "b", CNode_isComment), + GB_PROPERTY_READ("IsCDATA", "b", CNode_isCDATA), + + GB_PROPERTY_READ("Element", "XmlElement", CNode_element), + GB_PROPERTY_READ("OwnerDocument", "XmlDocument", CNode_ownerDocument), + GB_PROPERTY_READ("ChildNodes", "XmlNode[]", CNode_childNodes), + GB_PROPERTY_READ("Children", "XmlNode[]", CNode_childNodes), + GB_PROPERTY_READ("Parent", "XmlElement", CNode_parent), + GB_PROPERTY_READ("Previous", "XmlNode", CNode_previous), + GB_PROPERTY_READ("Next", "XmlNode", CNode_next), + GB_PROPERTY_READ("PreviousSibling", "XmlNode", CNode_previous), + GB_PROPERTY_READ("NextSibling", "XmlNode", CNode_next), + + GB_METHOD("ToString", "s", CNode_tostring, "[(Indent)b]"), + GB_METHOD("GetUserData", "v", CNode_getUserData, "(Key)s"), + GB_METHOD("SetUserData", NULL, CNode_setUserData, "(Key)s(Value)v"), + GB_PROPERTY("TextContent", "s", CNode_textContent), + GB_PROPERTY("Value", "s", CNode_textContent), + GB_PROPERTY("Name", "s", CNode_name), + + GB_STATIC_METHOD("Serialize", "s", CNode_escapeContent, "(Data)s"), + GB_STATIC_METHOD("Deserialize", "s", CNode_unEscapeContent, "(Data)s"), + + GB_METHOD("NewElement", NULL, CNode_newElement, "(Name)s[(Value)s]"), + GB_METHOD("NewAttribute", NULL, CNode_setAttribute, "(Name)s(Value)s"), + GB_PROPERTY_SELF("Attributes", ".XmlElementAttributes"), + + GB_END_DECLARE +}; diff --git a/gb.xml/src/CNode.h b/gb.xml/src/CNode.h new file mode 100644 index 00000000..ac08e51c --- /dev/null +++ b/gb.xml/src/CNode.h @@ -0,0 +1,31 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef CNODE_H +#define CNODE_H + +#include "gbinterface.h" + +extern GB_DESC CNodeDesc[]; +extern GB_DESC CElementAttributesDesc[]; +extern GB_DESC CElementAttributeNodeDesc[]; + +#endif // CNODE_H diff --git a/gb.xml/src/CReader.cpp b/gb.xml/src/CReader.cpp new file mode 100644 index 00000000..222b83f3 --- /dev/null +++ b/gb.xml/src/CReader.cpp @@ -0,0 +1,435 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CReader.h" +#include "reader.h" +#include "element.h" +#include "serializer.h" +#include "utils.h" + +#undef THIS +#define THIS (static_cast(_object)->reader) + +BEGIN_METHOD_VOID(CReader_new) + +THIS = new Reader; + +END_METHOD + +BEGIN_METHOD_VOID(CReader_free) + +delete THIS; + +END_METHOD + +BEGIN_METHOD(CReader_ReadChar, GB_STRING car) + +if(!LENGTH(car)) return; +try +{ + GB.ReturnInteger(THIS->ReadChar(STRING(car)[0])); +} +catch(XMLParseException &e) +{ + GB.Error(e.errorWhat); + XMLParseException_Cleanup(&e); +} + +END_METHOD + +BEGIN_METHOD_VOID(CReader_Close) + +THIS->ClearReader(); + +END_METHOD + +BEGIN_PROPERTY(CReader_keepData) + +if(READ_PROPERTY) +{ + GB.ReturnBoolean(THIS->keepMemory); +} +else +{ + THIS->keepMemory = VPROP(GB_BOOLEAN); + if(THIS->keepMemory && THIS->foundNode) + { + //TODO : + //THIS->storedElements->push_back(THIS->foundNode); + //GB.Ref(THIS->foundNode); + } +} + +END_PROPERTY + +BEGIN_PROPERTY(CReader_pos) + +if(!READ_PROPERTY) return; + +GB.ReturnInteger(THIS->pos); + +END_PROPERTY + +BEGIN_METHOD_VOID(CReaderNodeAttr_next) + +if(!THIS->foundNode || THIS->state == READ_END_CUR_ELEMENT) +{ + GB.StopEnum(); return; +} + +if(THIS->foundNode->type != Node::ElementNode) +{ + GB.StopEnum(); return; +} + +Attribute *attr = *reinterpret_cast((GB.GetEnum())); +if(attr == 0) +{ + attr = ((Element*)(THIS->foundNode))->firstAttribute; + *reinterpret_cast(GB.GetEnum()) = attr; + (THIS->depth)++; +} +else +{ + attr = (Attribute*)attr->nextNode; + *reinterpret_cast(GB.GetEnum()) = attr; +} + +if(attr == 0) {GB.StopEnum(); THIS->curAttrEnum = 0; (THIS->depth)--; return;} + +THIS->curAttrEnum = attr; + +if((attr->attrValue && attr->lenAttrValue)) +{ + GB.ReturnNewString(attr->attrValue, attr->lenAttrValue); +} +else +{ + GB.ReturnNewZeroString(0); +} + +END_METHOD + +BEGIN_METHOD(CReaderNodeAttr_get, GB_STRING name) + +if(!THIS->foundNode || THIS->state == READ_END_CUR_ELEMENT) +{ + return; +} + +if(!THIS->foundNode->type == Node::ElementNode) return; + +Attribute *attr = XMLElement_GetAttribute((Element*)(THIS->foundNode), STRING(name), LENGTH(name)); + +if (!attr) +{ + GB.Error("No such attribute"); + return; +} + +GB.ReturnNewString(attr->attrValue, attr->lenAttrValue); + +END_METHOD + +BEGIN_METHOD(CReaderNodeAttr_Exist, GB_STRING name) + +if(!THIS->foundNode || THIS->state == READ_END_CUR_ELEMENT) +{ + return; +} + +if(!THIS->foundNode->type == Node::ElementNode) return; + +Attribute *attr = XMLElement_GetAttribute((Element*)(THIS->foundNode), STRING(name), LENGTH(name)); + +GB.ReturnBoolean(!!attr); + +END_METHOD + +BEGIN_PROPERTY(CReaderNodeAttr_count) + +if(!THIS->foundNode || THIS->state == READ_END_CUR_ELEMENT) +{ + GB.ReturnInteger(0); + return; +} + +if(THIS->foundNode->type == Node::ElementNode) +{ + GB.ReturnInteger(((Element*)(THIS->foundNode))->attributeCount); +} +else +{ +GB.ReturnInteger(0); +} + +END_PROPERTY + +BEGIN_PROPERTY(CReaderNodeAttr_name) + +if(!THIS->curAttrEnum) +{ + GB.Error("No enumerated attribute available"); + GB.ReturnNull(); + return; +} + + +if(!THIS->curAttrEnum->attrName || !THIS->curAttrEnum->lenAttrName) +{ + GB.ReturnNull(); + return; +} + +GB.ReturnNewString(THIS->curAttrEnum->attrName, THIS->curAttrEnum->lenAttrName); + +END_PROPERTY + +BEGIN_PROPERTY(CReaderNodeAttr_value) + +if(!THIS->curAttrEnum) +{ + GB.Error("No enumerated attribute available"); + GB.ReturnNull(); + return; +} + +if(!THIS->curAttrEnum->attrValue || !THIS->curAttrEnum->lenAttrValue) +{ + GB.ReturnNull(); + return; +} + +GB.ReturnNewString(THIS->curAttrEnum->attrValue, THIS->curAttrEnum->lenAttrValue); + +END_PROPERTY + +BEGIN_METHOD(CReaderReadFlags_get, GB_INTEGER flag) + +int flag = VARG(flag); +if(flag > FLAGS_COUNT || flag < 0) return; +GB.ReturnBoolean(THIS->flags[flag]); + +END_METHOD + +BEGIN_METHOD(CReaderReadFlags_put, GB_BOOLEAN value; GB_INTEGER flag) + +int flag = VARG(flag); +if(flag > FLAGS_COUNT || flag < 0 || flag == READ_ERR_EOF) return; +THIS->flags[flag] = VARG(value); + +END_METHOD + +BEGIN_PROPERTY(CReaderNode_type) + +GB.ReturnInteger(THIS->state); +END_PROPERTY + +BEGIN_PROPERTY(CReaderNode_Value) + +if(!THIS->foundNode || THIS->state == READ_END_CUR_ELEMENT) +{ + GB.ReturnNull(); + return; +} + + +if(THIS->curAttrEnum) +{ + if((THIS->curAttrEnum->attrValue && THIS->curAttrEnum->lenAttrValue)) + { + GB.ReturnNewString(THIS->curAttrEnum->attrValue, THIS->curAttrEnum->lenAttrValue); + } + else + { + GB.ReturnNewZeroString(0); + } + return; +} + +char *data; size_t len; +GBGetXMLTextContent(THIS->foundNode, data, len); +GB.ReturnString(data); + +END_PROPERTY + +BEGIN_PROPERTY(CReaderNode_Name) + +if(!THIS->foundNode || THIS->state == READ_END_CUR_ELEMENT) +{ + GB.ReturnNull(); + return; +} + +if(THIS->curAttrEnum) +{ + GB.ReturnNewString(THIS->curAttrEnum->attrName, THIS->curAttrEnum->lenAttrName); + return; +} + +switch (THIS->foundNode->type) +{ + case Node::ElementNode: + GB.ReturnNewString(((Element*)(THIS->foundNode))->tagName, + ((Element*)(THIS->foundNode))->lenTagName); break; + case Node::NodeText: + GB.ReturnNewZeroString("#text");break; + case Node::Comment: + GB.ReturnNewZeroString("#comment");break; + case Node::CDATA: + GB.ReturnNewZeroString("#cdata");break; + default: + GB.ReturnNull(); +} + + +END_PROPERTY + +BEGIN_PROPERTY(CReader_Depth) + +if(THIS->depth < 0) +{ + GB.ReturnInteger(0); + return; +} +GB.ReturnInteger(THIS->depth); + +END_PROPERTY + +BEGIN_PROPERTY(CReader_storedNodes) + +if(!READ_PROPERTY) return; + +GB.ReturnObject(0); + +END_PROPERTY + +BEGIN_PROPERTY(CReaderNode_IsEmptyElement) + +if(!THIS->foundNode) +{ + GB.ReturnBoolean(false); + return; +} + +if(!THIS->foundNode->type != Node::ElementNode) +{ + GB.ReturnBoolean(false); + return; +} + +GB.ReturnBoolean(THIS->waitClosingElmt); + +END_PROPERTY + + + +GB_DESC CReaderNodeTypeDesc[] = +{ + GB_DECLARE("XmlReaderNodeType", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("None", "i", 0), + GB_CONSTANT("Element", "i", NODE_ELEMENT), + GB_CONSTANT("Attribute", "i", READ_ATTRIBUTE), + GB_CONSTANT("Text", "i", NODE_TEXT), + GB_CONSTANT("CDATA", "i", NODE_CDATA), + GB_CONSTANT("EntityReference", "i", 0), + GB_CONSTANT("Entity", "i", 0), + GB_CONSTANT("ProcessingInstruction", "i", 0), + GB_CONSTANT("Comment", "i", NODE_COMMENT), + GB_CONSTANT("Document", "i", 0), + GB_CONSTANT("DocumentType", "i", 0), + GB_CONSTANT("DocumentFragment", "i", 0), + GB_CONSTANT("Notation", "i", 0), + GB_CONSTANT("Whitespace", "i",0), + GB_CONSTANT("SignificantWhitespace", "i",0), + GB_CONSTANT("EndElement", "i", READ_END_CUR_ELEMENT), + GB_CONSTANT("EndStream", "i", READ_ERR_EOF), + GB_CONSTANT("EndEntity", "i", 0), + GB_CONSTANT("XmlDeclaration", "i",0), + + GB_END_DECLARE +}; + +GB_DESC CReaderReadFlagsDesc[] = +{ + GB_DECLARE(".XmlReaderReadFlags", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", "b", CReaderReadFlags_get, "(Flag)i"), + GB_METHOD("_put", "b", CReaderReadFlags_put, "(Value)b(Flag)i"), + + GB_END_DECLARE +}; + +GB_DESC CReaderNodeAttributesDesc[] = +{ + GB_DECLARE(".XmlReader.Node.Attributes", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("_get", "s", CReaderNodeAttr_get, "(Name)s"), + GB_METHOD("_next", "s", CReaderNodeAttr_next, ""), + GB_METHOD("Exist", "b", CReaderNodeAttr_Exist, "(Name)s"), + GB_PROPERTY_READ("Count", "i", CReaderNodeAttr_count), + GB_PROPERTY_READ("Name", "i", CReaderNodeAttr_name), + GB_PROPERTY_READ("Value", "i", CReaderNodeAttr_value), + + GB_END_DECLARE +}; + +GB_DESC CReaderNodeDesc[] = +{ + GB_DECLARE(".XmlReader.Node", 0), GB_VIRTUAL_CLASS(), + + GB_PROPERTY_SELF("Attributes", ".XmlReader.Node.Attributes"), + //GB_PROPERTY_READ("BaseUri","s",CRNODE_BaseUri), + GB_PROPERTY_READ("Depth","i",CReader_Depth), + //GB_PROPERTY_READ("IsDefault","b",CRNODE_IsDefault), + GB_PROPERTY_READ("IsEmptyElement","b",CReaderNode_IsEmptyElement), + //GB_PROPERTY_READ("LocalName","s",CRNODE_LocalName), + GB_PROPERTY_READ("Name", "s", CReaderNode_Name), + //GB_PROPERTY_READ("NamespaceUri", "s", CRNODE_NamespaceUri), + //GB_PROPERTY_READ("Prefix", "s", CRNODE_Prefix), + //GB_PROPERTY_READ("QuoteChar", "s", CRNODE_QuoteChar), + GB_PROPERTY_READ("Type","i",CReaderNode_type), + GB_PROPERTY_READ("Value", "s", CReaderNode_Value), + //GB_PROPERTY_READ("XmlLang", "s", CRNODE_XmlLang), + + GB_END_DECLARE +}; + +GB_DESC CReaderDesc[] = +{ + GB_DECLARE("_XmlReader", sizeof(CReader)), + + GB_METHOD("_new", NULL, CReader_new, ""), + GB_METHOD("_free", NULL, CReader_free, ""), + GB_METHOD("_ReadChar", "i", CReader_ReadChar, "(Char)s"), + GB_METHOD("_Close", "i", CReader_Close, ""), + + GB_PROPERTY("KeepData", "b", CReader_keepData), + GB_PROPERTY_READ("Pos", "i", CReader_pos), + GB_PROPERTY_SELF("Node", ".XmlReader.Node"), + GB_PROPERTY_READ("StoredNodes", "XmlNode[]", CReader_storedNodes), + GB_PROPERTY_READ("Depth","i",CReader_Depth), + + GB_PROPERTY_SELF("ReadFlags", ".XmlReaderReadFlags"), + + GB_END_DECLARE +}; diff --git a/gb.xml/src/CReader.h b/gb.xml/src/CReader.h new file mode 100644 index 00000000..f92bb71f --- /dev/null +++ b/gb.xml/src/CReader.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef CXMLREADER_H +#define CXMLREADER_H + +#include "gbinterface.h" + +class Reader; + +typedef struct CReader +{ + GB_BASE ob; + Reader *reader; +} CReader; + +extern GB_DESC CReaderDesc[]; +extern GB_DESC CReaderNodeDesc[]; +extern GB_DESC CReaderNodeTypeDesc[]; +extern GB_DESC CReaderNodeAttributesDesc[]; +extern GB_DESC CReaderReadFlagsDesc[]; + + +#endif // CXMLREADER_H diff --git a/gb.xml/src/CTextNode.cpp b/gb.xml/src/CTextNode.cpp new file mode 100644 index 00000000..9c94d9f6 --- /dev/null +++ b/gb.xml/src/CTextNode.cpp @@ -0,0 +1,94 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CTextNode.h" +#include "textnode.h" + +#include "node.h" + +#define THISNODE (static_cast(_object)->node) + +BEGIN_METHOD(CTextNode_new, GB_STRING content) + + +if(XMLNode_NoInstanciate()) return; +if(GB.Is(_object, GB.FindClass("XmlCommentNode")))//Called as inherited Comment constructor +{ + if(!MISSING(content)) + { + THISNODE = XMLComment_New(STRING(content), LENGTH(content)); + } + else + { + THISNODE = XMLComment_New(); + } +} +else if(GB.Is(_object, GB.FindClass("XmlCDATANode")))//Called as inherited CDATA constructor +{ + if(!MISSING(content)) + { + THISNODE = XMLCDATA_New(STRING(content), LENGTH(content)); + } + else + { + THISNODE = XMLCDATA_New(); + } +} +else +{ + if(!MISSING(content)) + { + THISNODE = XMLTextNode_New(STRING(content), LENGTH(content)); + } + else + { + THISNODE = XMLTextNode_New(); + } +} + + THISNODE->GBObject = static_cast(_object); + +END_METHOD + + +GB_DESC CTextNodeDesc[] = +{ + GB_DECLARE("XmlTextNode", sizeof(CNode)), GB_INHERITS("XmlNode"), + + GB_METHOD("_new", NULL, CTextNode_new, "[(Content)s]"), + + GB_END_DECLARE +}; + +GB_DESC CCommentNodeDesc[] = +{ + GB_DECLARE("XmlCommentNode", sizeof(CNode)), GB_INHERITS("XmlTextNode"), + + GB_END_DECLARE +}; + +GB_DESC CCDATANodeDesc[] = +{ + GB_DECLARE("XmlCDATANode", sizeof(CNode)), GB_INHERITS("XmlTextNode"), + + GB_END_DECLARE +}; + diff --git a/gb.xml/src/CTextNode.h b/gb.xml/src/CTextNode.h new file mode 100644 index 00000000..1bdf2b24 --- /dev/null +++ b/gb.xml/src/CTextNode.h @@ -0,0 +1,31 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef CTEXTNODE_H +#define CTEXTNODE_H + +#include "gbinterface.h" + +extern GB_DESC CTextNodeDesc[]; +extern GB_DESC CCommentNodeDesc[]; +extern GB_DESC CCDATANodeDesc[]; + +#endif // CTEXTNODE_H diff --git a/gb.xml/src/Makefile.am b/gb.xml/src/Makefile.am new file mode 100644 index 00000000..5ab447e7 --- /dev/null +++ b/gb.xml/src/Makefile.am @@ -0,0 +1,18 @@ +COMPONENT = gb.xml +include $(top_srcdir)/component.am + +SUBDIRS = . @XMLXSLT_DIR@ rpc @XMLHTML_DIR@ +gblib_LTLIBRARIES = gb.xml.la + +gb_xml_la_LIBADD = @XML_LIB@ +gb_xml_la_LDFLAGS = -module @LD_FLAGS@ @XML_LDFLAGS@ +gb_xml_la_CPPFLAGS = @XML_INC@ +gb_xml_la_CXXFLAGS = $(AM_CXXFLAGS) -fexceptions -fvisibility=default + +gb_xml_la_SOURCES = main.cpp main.h utils.cpp utils.h\ +serializer.h serializer.cpp parser.h parser.cpp\ +document.cpp document.h node.cpp node.h textnode.cpp textnode.h element.cpp element.h \ +CDocument.cpp CDocument.h CNode.cpp CNode.h CElement.h CElement.cpp CTextNode.h CTextNode.cpp \ +CReader.h CReader.cpp reader.cpp reader.h \ +CExplorer.h CExplorer.cpp explorer.cpp explorer.h \ +gb.xml.h gbinterface.h diff --git a/gb.xml/src/document.cpp b/gb.xml/src/document.cpp new file mode 100644 index 00000000..53789a24 --- /dev/null +++ b/gb.xml/src/document.cpp @@ -0,0 +1,215 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "document.h" + +#include "node.h" +#include "element.h" +#include "utils.h" +#include "CDocument.h" +#include "parser.h" +#include "serializer.h" + +#include +#include + +Document* XMLDocument_New() +{ + Document *newDoc = (Document*)malloc(sizeof(Document)); + + XMLNode_Init((Node*)newDoc, Node::DocumentNode); + + newDoc->root = XMLElement_New("xml", 3); + newDoc->root->parentDocument = newDoc; + newDoc->parentDocument = newDoc; + newDoc->docType = XMLDocumentType; + XMLNode_appendChild((Node*)newDoc, newDoc->root); + + return newDoc; + +} + +Document* XMLDocument_NewFromFile(const char *fileName, const size_t lenFileName, const DocumentType docType) +{ + Document *newDoc = (Document*)malloc(sizeof(Document)); + + XMLNode_Init((Node*)newDoc, Node::DocumentNode); + + newDoc->root = 0; + newDoc->parentDocument = newDoc; + newDoc->docType = docType; + + try + { + XMLDocument_Open(newDoc, fileName, lenFileName); + } + catch(XMLParseException ex) + { + XMLDocument_Release(newDoc); + throw ex; + } + + return newDoc; +} + +void XMLDocument_Release(Document *doc) +{ + XMLNode_clearChildren((Node*)doc); + free(doc); +} + +/***** Node tree *****/ +void XMLDocument_SetRoot(Document *doc, Element *newRoot) +{ + if(!doc->root) + { + XMLNode_appendChild(doc, newRoot); + } + else + { + XMLNode_replaceChild(doc, doc->root, newRoot); + } + doc->root = newRoot; +} + +/***** Document loading *****/ +void XMLDocument_Open(Document *doc, const char *fileName, const size_t lenFileName) +{ + char *content; int len; + + if(GB.LoadFile(fileName, lenFileName, &content, &len)) + { + GB.Error("Error loading file."); + GB.Propagate(); + return; + } + + + XMLDocument_SetContent(doc, content, len); + +} + +void XMLDocument_SetContent(Document *doc, const char *content, const size_t len) +{ + char *posStart = 0, *posEnd = 0; + + if(doc->docType == XMLDocumentType) + { + //On cherche le début du prologue XML + posStart = (char*)memchrs(content, len, "", 2); + posEnd += 2; + } + } + else + { + //On cherche le début du prologue XML + posStart = (char*)memchrs(content, len, "', len - (posStart - content)); + + if(posEnd) + { + posEnd += 1; + //HTML5 ? () + doc->docType = (posEnd - posStart == 98) ? XHTMLDocumentType : HTMLDocumentType; + if(doc->docType == HTMLDocumentType) doc->docType = (!memcmp(posStart, "html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"", 98)) ? XHTMLDocumentType : HTMLDocumentType; + } + + } + + } + + Node** elements = 0; + size_t elementCount = 0; + + if(posEnd) + { + elements = parse(posEnd, len - (posEnd - content), &elementCount, doc->docType); + } + else + { + elements = parse(content, len, &elementCount, doc->docType); + } + + Node *newRoot = 0; + Node *node = 0; + + XMLNode_clearChildren((Node*)doc); + doc->root = 0; + for(size_t i = 0; i < elementCount; i++) + { + node = elements[i]; + if(node->type == Node::ElementNode) + { + if(!newRoot) + { + newRoot = node; + } + else + { + if(doc->docType == XMLDocumentType)//Strict document + { + throw XMLParseException_New("Extra root element", 0, 0, 0); + } + } + + } + XMLNode_appendChild((Node*)doc, node); + + } + + + free(elements); + if(newRoot) doc->root = (Element*)newRoot; +} + + + +void XMLDocument_Save(Document *doc, const char *fileName, bool indent) +{ + FILE *newFile = fopen(fileName, "w"); + + if(!newFile) + { + GB.Error("Cannot open file"); + GB.Propagate(); + return; + } + + char *data = 0; + size_t lenData = 0; + serializeNode(doc, data, lenData, indent ? 0 : -1); + data = (char*)realloc(data, lenData + 1); + data[lenData] = 0; + + fputs(data, newFile); + fclose(newFile); + free(data); + +} diff --git a/gb.xml/src/document.h b/gb.xml/src/document.h new file mode 100644 index 00000000..a81e03b9 --- /dev/null +++ b/gb.xml/src/document.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef DOCUMENT_H +#define DOCUMENT_H + +#include "main.h" +#include "utils.h" + +Document* XMLDocument_New(); +Document* XMLDocument_NewFromFile(const char *fileName, const size_t lenFileName, const DocumentType docType = XMLDocumentType); +void XMLDocument_Release(Document *doc); + +void XMLDocument_Open(Document *doc, const char *fileName, const size_t lenFileName); +void XMLDocument_SetContent(Document *doc, const char *content, const size_t len); +void XMLDocument_Save(Document *doc, const char *fileName, bool indent = false); + +void XMLDocument_SetRoot(Document *doc, Element *newRoot); + +#endif // DOCUMENT_H diff --git a/gb.xml/src/element.cpp b/gb.xml/src/element.cpp new file mode 100644 index 00000000..41057095 --- /dev/null +++ b/gb.xml/src/element.cpp @@ -0,0 +1,335 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "element.h" + +#include "node.h" +#include "utils.h" +#include "textnode.h" + +#include +#include + +/*************************************** Element ***************************************/ + +Element* XMLElement_New() +{ + Element *newElement = (Element*)malloc(sizeof(Element)); + memset(newElement, 0, sizeof(Element)); + XMLNode_Init(newElement, Node::ElementNode); + return newElement; +} + +Element* XMLElement_New(const char *ntagName, size_t nlenTagName) +{ + Element *newElement = XMLElement_New(); + XMLElement_SetTagName(newElement, ntagName, nlenTagName); + return newElement; +} + +void XMLElement_Free(Element *elmt) +{ + //Releasing tag name + if(elmt->tagName) free(elmt->tagName); + free(elmt->prefix); + free(elmt->localName); + + //Releasing children + XMLNode_clearChildren(elmt); + + //Releasing attributes + if(elmt->firstAttribute) + { + for(Attribute *attr = (Attribute*)(elmt->firstAttribute->nextNode); attr != 0; attr = (Attribute*)(attr->nextNode)) + { + XMLAttribute_Free((Attribute*)(attr->previousNode)); + } + XMLAttribute_Free(elmt->lastAttribute); + } + free(elmt); +} + + + +/***** TagName *****/ + +void XMLElement_SetTagName(Element *elmt, const char *ntagName, size_t nlenTagName) +{ + elmt->lenTagName = nlenTagName; + elmt->tagName = (char*)realloc(elmt->tagName, sizeof(char) * elmt->lenTagName); + memcpy(elmt->tagName, ntagName, nlenTagName); + XMLElement_RefreshPrefix(elmt); + +} + +void XMLElement_SetPrefix(Element *elmt, const char *nprefix, size_t nlenPrefix) +{ + if(nlenPrefix) + { + elmt->tagName = (char*)realloc(elmt->tagName, nlenPrefix + elmt->lenLocalName + 1); + memcpy(elmt->tagName, nprefix, nlenPrefix); + *(elmt->tagName + nlenPrefix) = ':'; + memcpy(elmt->tagName + nlenPrefix + 1, elmt->localName, elmt->lenLocalName); + } + else if(elmt->lenPrefix) + { + elmt->tagName = (char*)realloc(elmt->tagName, elmt->lenLocalName); + memcpy(elmt->tagName, elmt->localName, elmt->lenLocalName); + } + + + elmt->lenPrefix = nlenPrefix; + elmt->prefix = (char*)realloc(elmt->prefix, nlenPrefix); + if(nlenPrefix) memcpy(elmt->prefix, nprefix, nlenPrefix); +} + +void XMLElement_RefreshPrefix(Element *elmt) +{ + if(!elmt->lenTagName) + { + free(elmt->localName); + elmt->localName = 0; + elmt->lenLocalName = 0; + free(elmt->prefix); + elmt->prefix = 0; + elmt->lenPrefix = 0; + return; + } + register char* pos = (char*)memrchr(elmt->tagName, ':', elmt->lenTagName);//Prefix + if(pos) + { + elmt->lenLocalName = (elmt->tagName + elmt->lenTagName) - (pos + 1); + elmt->lenPrefix = pos - elmt->tagName; + elmt->localName = (char*)realloc(elmt->localName, elmt->lenLocalName); + elmt->prefix = (char*)realloc(elmt->prefix, elmt->lenPrefix); + memcpy(elmt->prefix, elmt->tagName, elmt->lenPrefix); + memcpy(elmt->localName, pos + 1, elmt->lenLocalName); + } + else + { + elmt->lenLocalName = elmt->lenTagName; + elmt->localName = (char*)realloc(elmt->localName, sizeof(char) * elmt->lenTagName); + memcpy(elmt->localName, elmt->tagName, elmt->lenTagName); + free(elmt->prefix); + elmt->prefix = 0; + elmt->lenPrefix = 0; + } +} + +/***** Attributes *****/ + +Attribute* XMLElement_AddAttribute(Element *elmt, const char *nattrName, const size_t nlenAttrName) +{ + elmt->attributeCount++; + Attribute *newAttribute = XMLAttribute_New(nattrName, nlenAttrName); + newAttribute->parent = elmt; + if(!elmt->lastAttribute)//No attribute + { + elmt->firstAttribute = newAttribute; + elmt->lastAttribute = elmt->firstAttribute; + elmt->lastAttribute->previousNode = 0; + elmt->lastAttribute->nextNode = 0; + return newAttribute; + } + + newAttribute->previousNode = elmt->lastAttribute; + elmt->lastAttribute->nextNode = newAttribute; + elmt->lastAttribute = newAttribute; + elmt->lastAttribute->nextNode = 0; + return newAttribute; +} + +Attribute* XMLElement_AddAttribute(Element *elmt, const char *nattrName, const size_t nlenAttrName, + const char *nattrVal, const size_t nlenAttrVal) +{ + elmt->attributeCount++; + Attribute *newAttribute = XMLAttribute_New(nattrName, nlenAttrName, + nattrVal, nlenAttrVal); + newAttribute->parent = elmt; + if(!elmt->lastAttribute)//No attribute + { + elmt->firstAttribute = newAttribute; + elmt->lastAttribute = elmt->firstAttribute; + elmt->lastAttribute->previousNode = 0; + elmt->lastAttribute->nextNode = 0; + return newAttribute; + } + + newAttribute->previousNode = elmt->lastAttribute; + elmt->lastAttribute->nextNode = newAttribute; + elmt->lastAttribute = newAttribute; + elmt->lastAttribute->nextNode = 0; + return newAttribute; +} + +Attribute* XMLElement_GetAttribute(const Element *elmt, const char *nattrName, const size_t nlenAttrName, const int mode) +{ + for(Attribute *attr = elmt->firstAttribute; attr != 0; attr = (Attribute*)(attr->nextNode)) + { + if(GB_MatchString(attr->attrName, attr->lenAttrName, nattrName, nlenAttrName, mode)) + return attr; + } + return 0; +} + +void XMLElement_SetAttribute(Element *elmt, const char *nattrName, const size_t nlenAttrName, + const char *nattrVal, const size_t nlenAttrVal) +{ + Attribute *attr = XMLElement_GetAttribute(elmt, nattrName, nlenAttrName); + if(!attr) + { + XMLElement_AddAttribute(elmt, nattrName, nlenAttrName, nattrVal, nlenAttrVal); + } + else + { + XMLAttribute_SetValue(attr, nattrVal, nlenAttrVal); + } +} + + + +bool XMLElement_AttributeContains(const Element *elmt, const char *attrName, size_t lenAttrName, const char *value, size_t lenValue) +{ + Attribute *attr = XMLElement_GetAttribute(elmt, attrName, lenAttrName); + if(!attr) return false; + char *pos = attr->attrValue; + size_t left = attr->lenAttrValue; + + while (1) { + if (!memcmp(value, pos, lenValue)) return true; + pos = (char*)memchr(pos, ' ', left); + if (!pos) break; + pos++; + left = attr->lenAttrValue - (pos - attr->attrValue); + } + + return false; +} + +void XMLElement_RemoveAttribute(Element *elmt, const char *attrName, size_t lenAttrName) +{ + XMLElement_RemoveAttribute(elmt, XMLElement_GetAttribute(elmt, attrName,lenAttrName)); +} + +void XMLElement_RemoveAttribute(Element *elmt, Attribute *attr) +{ + if(!attr) return; + if(attr->parent != elmt) return; + if(attr == elmt->firstAttribute) elmt->firstAttribute = (Attribute*)(attr->nextNode); + if(attr == elmt->lastAttribute) elmt->lastAttribute = (Attribute*)(attr->previousNode); + if(attr->nextNode) attr->nextNode->previousNode = attr->previousNode; + if(attr->previousNode) attr->previousNode->nextNode = attr->nextNode; + elmt->attributeCount--; + XMLAttribute_Free(attr); +} + +void XMLElement_SetTextContent(Element *elmt, const char *content, size_t lenContent) +{ + if(!lenContent) return; + + XMLNode_clearChildren(elmt); + + TextNode *newChild = XMLTextNode_New(content, lenContent); + + XMLNode_appendChild(elmt, newChild); + +} + +/*************************************** Attribute ***************************************/ + +Attribute* XMLAttribute_New() +{ + Attribute *newAttr = (Attribute*)malloc(sizeof(Attribute)); + XMLNode_Init(newAttr, Node::AttributeNode); + newAttr->attrName = 0; + newAttr->attrValue = 0; + newAttr->lenAttrName = 0; + newAttr->lenAttrValue = 0; + return newAttr; +} + +Attribute* XMLAttribute_New(const char *nattrName, const size_t nlenAttrName) +{ + Attribute *newAttr = (Attribute*)malloc(sizeof(Attribute)); + XMLNode_Init(newAttr, Node::AttributeNode); + newAttr->attrValue = 0; + newAttr->lenAttrValue = 0; + + newAttr->lenAttrName = nlenAttrName; + newAttr->attrName = (char*)malloc(sizeof(char)*nlenAttrName); + memcpy(newAttr->attrName, nattrName, nlenAttrName); + return newAttr; +} + +Attribute* XMLAttribute_New(const char *nattrName, const size_t nlenAttrName, + const char *nattrVal, const size_t nlenAttrVal) +{ + Attribute *newAttr = (Attribute*)malloc(sizeof(Attribute)); + XMLNode_Init(newAttr, Node::AttributeNode); + + newAttr->lenAttrName = nlenAttrName; + newAttr->lenAttrValue = nlenAttrVal; + + newAttr->attrName = (char*)malloc(sizeof(char)*(nlenAttrName)); + memcpy(newAttr->attrName, nattrName, nlenAttrName); + + if(nattrVal && nlenAttrVal) + { + newAttr->attrValue = (char*)malloc(nlenAttrVal); + memcpy(newAttr->attrValue, nattrVal, nlenAttrVal); + } + else + { + newAttr->attrValue = 0; + newAttr->lenAttrValue = 0; + } + + return newAttr; +} + +void XMLAttribute_Free(Attribute *attr) +{ + if(attr->attrName) free(attr->attrName); + if(attr->attrValue) free(attr->attrValue); + + free(attr); + attr = 0; +} + +void XMLAttribute_SetName(Attribute *attr, const char *nattrName, const size_t nlenAttrName) +{ + attr->lenAttrName = nlenAttrName; + attr->attrName = (char*)realloc(attr->attrName, sizeof(char) * attr->lenAttrName); + memcpy(attr->attrName, nattrName, attr->lenAttrName); +} + +void XMLAttribute_SetValue(Attribute *attr, const char *nattrVal, const size_t nlenAttrVal) +{ + attr->lenAttrValue = nlenAttrVal; + if((!nlenAttrVal) && attr->attrValue) + { + free(attr->attrValue); + attr->attrValue = 0; + return; + } + attr->attrValue = (char*)realloc(attr->attrValue, sizeof(char) * attr->lenAttrValue); + memcpy(attr->attrValue, nattrVal, attr->lenAttrValue); +} diff --git a/gb.xml/src/element.h b/gb.xml/src/element.h new file mode 100644 index 00000000..f017158c --- /dev/null +++ b/gb.xml/src/element.h @@ -0,0 +1,63 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef ELEMENT_H +#define ELEMENT_H + +#include "gb.xml.h" + + +Attribute* XMLAttribute_New(); +Attribute* XMLAttribute_New(const char *nattrName, const size_t nlenAttrName); +Attribute* XMLAttribute_New(const char *nattrName, const size_t nlenAttrName, + const char *nattrVal, const size_t nlenAttrVal); + +void XMLAttribute_Free(Attribute *attr); + +void XMLAttribute_SetName(Attribute *attr, const char *nattrName, const size_t nlenAttrName); +void XMLAttribute_SetValue(Attribute *attr, const char *nattrVal, const size_t nlenAttrVal); + +Element* XMLElement_New(); +Element* XMLElement_New(const char *ntagName, size_t nlenTagName); +void XMLElement_Free(Element *elmt); + +void XMLElement_SetTagName(Element *elmt, const char *ntagName, size_t nlenTagName); +void XMLElement_SetPrefix(Element *elmt, const char *nprefix, size_t nlenPrefix); +void XMLElement_RefreshPrefix(Element *elmt); + +Attribute* XMLElement_AddAttribute(Element *elmt, const char *nattrName, const size_t nlenAttrName); +Attribute* XMLElement_AddAttribute(Element *elmt, const char *nattrName, const size_t nlenAttrName, + const char *nattrVal, const size_t nlenAttrVal); + +Attribute* XMLElement_GetAttribute(const Element *elmt, const char *nattrName, const size_t nlenAttrName, const int mode = GB_STRCOMP_BINARY); + +void XMLElement_SetAttribute(Element *elmt, const char *nattrName, const size_t nlenAttrName, + const char *nattrVal, const size_t nlenAttrVal); + +bool XMLElement_AttributeContains(const Element *elmt, const char *attrName, size_t lenAttrName, const char *value, size_t lenValue); + +void XMLElement_RemoveAttribute(Element *elmt, const char *attrName, size_t lenAttrName); +void XMLElement_RemoveAttribute(Element *elmt, Attribute *attr); + +void XMLElement_SetTextContent(Element *elmt, const char *content, size_t lenContent); + + +#endif // ELEMENT_H diff --git a/gb.xml/src/explorer.cpp b/gb.xml/src/explorer.cpp new file mode 100644 index 00000000..5774b64a --- /dev/null +++ b/gb.xml/src/explorer.cpp @@ -0,0 +1,138 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "explorer.h" + +#include "node.h" +#include "document.h" +#include "element.h" +#include "gbinterface.h" +#include + + + +Explorer::Explorer() : loadedDocument(0) +{ + Init(); +} + +Explorer::~Explorer() +{ + Clear(); + delete[] flags; +} + +void Explorer::Init() +{ + this->flags = new bool[FLAGS_COUNT]; + memset(this->flags, false, FLAGS_COUNT); + this->flags[NODE_ELEMENT] = true; + this->flags[NODE_TEXT] = true; + this->flags[NODE_COMMENT] = true; + this->flags[NODE_CDATA] = true; + this->flags[READ_END_CUR_ELEMENT] = true; + this->flags[READ_ERR_EOF] = true; + Clear(); +} + +void Explorer::Load(Document *doc) +{ + Clear(); + loadedDocument = doc; + CNode *obj = XMLNode_GetGBObject(loadedDocument); + GB.Ref(obj); + //GB.Ref(doc); + + Read(); + +} + +void Explorer::Clear() +{ + if(loadedDocument) + { + CNode *obj = XMLNode_GetGBObject(loadedDocument); + GB.Unref(POINTER(&obj)); + } + loadedDocument = 0; + curNode = 0; + this->eof = false; + this->endElement = false; +} + +int Explorer::MoveNext() +{ + if(eof) return READ_ERR_EOF; + if(!loadedDocument) + { + GB.Error("No document loaded"); + GB.Propagate(); + return READ_ERR_EOF; + } + if(!curNode)//Début du document + { + curNode = (Node*)(loadedDocument->root); + return NODE_ELEMENT; + } + //Premier enfant + else if(curNode->type == Node::ElementNode && ((Element*)curNode)->childCount > 0 && !endElement) + { + curNode = (((Element*)curNode)->firstChild); + return curNode->type; + } + //Si plus d'enfants, frère suivant + else + { + Node *nextNode = curNode->nextNode; + endElement = false; + if(nextNode) + { + curNode = nextNode; + return nextNode->type; + } + //si plus d'enfants ni de frère, on remonte + else if(curNode->parent && curNode != loadedDocument->root && curNode->parent != loadedDocument) + { + curNode = curNode->parent; + endElement = true; + return READ_END_CUR_ELEMENT; + } + else//Plus de parent = fin du document + { + this->eof = true; + return READ_ERR_EOF; + } + + } + + return 0;//Hautement probablement impossible + +} + +unsigned char Explorer::Read() +{ + do + { + state = MoveNext(); + } while(!this->flags[state]); + + return state; +} diff --git a/gb.xml/src/explorer.h b/gb.xml/src/explorer.h new file mode 100644 index 00000000..5adc1c65 --- /dev/null +++ b/gb.xml/src/explorer.h @@ -0,0 +1,65 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef EXPLORER_H +#define EXPLORER_H + +/* ===== Constantes de retour de Read() ===== */ + +#define NODE_ELEMENT 1//Retourné au début d'un élément +#define NODE_TEXT 2//Fin d'un nœud texte +#define NODE_COMMENT 3//Fin d'un commentaire +#define NODE_CDATA 4//Fin d'un CDATA +#define NODE_ATTRIBUTE 5 +#define READ_END_CUR_ELEMENT 6//Fin d'un élément +#define READ_ERR_EOF 7//Fin du document +#define READ_ATTRIBUTE 8//Lecture d'un attribut +#define FLAGS_COUNT 9 + +class Document; +class Node; + + +/* Explorer : explore un document déjà chargé en mémoire.*/ +class Explorer +{ +public: + Explorer(); + ~Explorer(); + + void Load(Document *doc); + + + void Init();//Intitialise le lecteur + void Clear();//Réinitialise le lecteur + int MoveNext();//Va au nœud suivant + unsigned char Read();//Continue la lecture du document + bool *flags;//Flags de lecture + bool endElement;//Si on vient de finir la lecture de l'élément courant + bool eof; + Document *loadedDocument;//Document actuellement chargé + Node *curNode; + + unsigned char state; +}; + + +#endif // EXPLORER_H diff --git a/gb.xml/src/gb.xml.component b/gb.xml/src/gb.xml.component new file mode 100755 index 00000000..eb2afb8b --- /dev/null +++ b/gb.xml/src/gb.xml.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.xml +Author=Adrien Prokopowicz +State=Stable +Implements=XML \ No newline at end of file diff --git a/gb.xml/src/gb.xml.h b/gb.xml/src/gb.xml.h new file mode 100644 index 00000000..8f8566af --- /dev/null +++ b/gb.xml/src/gb.xml.h @@ -0,0 +1,204 @@ +#ifndef GB_XML_H +#define GB_XML_H + +#include + +#define GB_STRCOMP_BINARY 0 +#define GB_STRCOMP_NOCASE 1 +#define GB_STRCOMP_LANG 2 +#define GB_STRCOMP_LIKE 4 +#define GB_STRCOMP_NATURAL 8 + +struct Document; +struct CNode; + +typedef void* GB_ARRAY; + +#if 0 +#include + +using namespace std; + +#define DEBUG std::cerr << "XMLDBG (" << __FILE__ << ":" <<__LINE__ << ") :" +#define DEBUGH DEBUG << endl + +#endif + +typedef struct Node +{ + enum Type {ElementNode, NodeText, Comment, CDATA, AttributeNode, DocumentNode, HTMLDocumentNode}; + + //Node children + Node *firstChild; + Node *lastChild; + size_t childCount; + + //Node tree + Document *parentDocument; + Node *parent; + Node *nextNode; + Node *previousNode; + + //Node Type + Type type; + + //Gambas object + CNode *GBObject; + + //User data + void *userData; //GB_COLLECTION = void* (cf. gambas.h) +}Node; + +typedef struct Attribute : public Node +{ + char *attrName; + char *attrValue; + + size_t lenAttrName; + size_t lenAttrValue; + +}Attribute; + +typedef struct Element : public Node +{ + //Tag Name + char *tagName; + size_t lenTagName; + + char *prefix; + size_t lenPrefix; + + char* localName; + size_t lenLocalName; + + Attribute *firstAttribute; + Attribute *lastAttribute; + size_t attributeCount; + +}Element; + +typedef enum {XMLDocumentType, HTMLDocumentType, XHTMLDocumentType} DocumentType ; + +typedef struct Document : public Node +{ + Element *root; + DocumentType docType; +}Document; + +typedef struct TextNode : public Node +{ + char *content; + size_t lenContent; + + char *escapedContent; + size_t lenEscapedContent; + +}TextNode; + +typedef TextNode CommentNode; + +typedef TextNode CDATANode; + +typedef struct XMLParseException +{ + char *near; + size_t lenNear; + + size_t line; + size_t column; + + char *errorWhat; +}XMLParseException; + +//Gambas XML component interface + +#define XML_INTERFACE_VERSION 1 + +typedef struct +{ + void* version; + //Converts the node to its string representation + void (*SerializeXMLNode)(Node *node, char *&output, size_t &len, int indent); + void (*GBSerializeXMLNode)(Node *node, char *&output, size_t &len, int indent); + Node** (*ParseXML)(char const *data, const size_t lendata, size_t *nodeCount); + void (*GBGetXMLTextContent)(Node *node, char *&output, size_t &len); + + //Content escaping/normalization + void (*XMLText_escapeContent)(const char *src, const size_t lenSrc, char *&dst, size_t &lenDst); + void (*XMLText_unEscapeContent)(const char *src, const size_t lenSrc, char *&dst, size_t &lenDst); + void (*XMLText_escapeAttributeContent)(const char *src, const size_t lenSrc, char *&dst, size_t &lenDst); + + //XMLNode Interface + CNode* (*XMLNode_GetGBObject)(Node *node); + Element* (*XMLNode_getFirstChildByAttributeValue)(Node *node, const char *attrName, const size_t lenAttrName, + const char *attrValue, const size_t lenAttrValue, const int mode, const int depth); + void (*XMLNode_getGBChildrenByAttributeValue)(Node *node, const char *attrName, const size_t lenAttrName, + const char *attrValue, const size_t lenAttrValue, + GB_ARRAY *array, const int mode, const int depth); + + Element* (*XMLNode_firstChildElement)(Node *node); + Element* (*XMLNode_lastChildElement)(Node *node); + Element* (*XMLNode_previousElement)(const Node *node); + void (*XMLNode_appendChild)(Node *node, Node *newChild); + Element** (*XMLNode_getChildrenByTagName)(Node *node, const char *ctagName, const size_t clenTagName, size_t &lenArray, const int depth); + Element* (*XMLNode_getFirstChildByTagName)(const Node *node, const char *ctagName, const size_t clenTagName, const int depth); + + void (*XMLNode_setTextContent)(Node *node, const char *content, const size_t lenContent); + + + //XMLTextNode Interface + TextNode* (*XMLTextNode_New)(const char *ncontent, const size_t nlen); + void (*XMLTextNode_setEscapedTextContent)(TextNode *node, const char *ncontent, const size_t nlen); + + void (*XMLTextNode_checkEscapedContent)(TextNode *node); + + //XMLDocument Interface + Document* (*XMLDocument_New)(); + Document* (*XMLDocument_NewFromFile)(const char *fileName, const size_t lenFileName, const DocumentType docType); + void (*XMLDocument_SetContent)(Document *doc, const char *content, size_t len); + + //XMLElement Interface + Element* (*XMLElement_New)(const char *ntagName, size_t nlenTagName); + void (*XMLElement_SetTagName)(Element *elmt, const char *ntagName, size_t nlenTagName); + bool (*XMLElement_AttributeContains)(const Element *elmt, const char *attrName, size_t lenAttrName, const char *value, size_t lenValue); + + Attribute* (*XMLElement_AddAttribute)(Element *elmt, const char *ntagName, size_t nlenTagName, + const char *nattrVal, const size_t nlenAttrVal); + Attribute* (*XMLElement_GetAttribute)(const Element *elmt, const char *nattrName, const size_t nlenAttrName, const int mode); + void (*XMLElement_SetAttribute)(Element *elmt, const char *nattrName, const size_t nlenAttrName, + const char *nattrVal, const size_t nlenAttrVal); + + void (*XMLElement_RemoveAttribute)(Element *elmt, Attribute *attr); + //XMLComment Interface + CommentNode* (*XMLComment_New)(const char *ncontent, const size_t nlen); + + //XMLCDATA Interface + CDATANode* (*XMLCDATA_New)(const char *ncontent, const size_t nlen); + + + + void (*ReturnNode)(Node *node); + + + + //Various utils + void (*Trim)(const char* &str, size_t &len); + bool (*isNameStartChar)(const wchar_t car); + bool (*isNameChar)(const wchar_t car); + const void* (*memchrs)(const char *source, size_t lensource, const char *comp, size_t lencomp); + bool (*GB_MatchString)(const char *str, size_t lenStr, const char *pattern, size_t lenPattern, int mode); + wchar_t (*nextUTF8Char)(const char *&data, size_t len); + bool (*isWhiteSpace)(const wchar_t s); + + void (*ThrowXMLParseException)(const char* nerror, const char *text, const size_t lenText, const char *posFailed); + +#if defined(OS_MACOSX) || defined(__APPLE__) + void* (*memrchr)(const char *s, int c, size_t n); +#endif + + void *_null; + +}XML_INTERFACE; + + +#endif // GB_XML_H diff --git a/gb.xml/src/gb.xml/.component b/gb.xml/src/gb.xml/.component new file mode 100644 index 00000000..f887c95c --- /dev/null +++ b/gb.xml/src/gb.xml/.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.xml +Version=3.6.90 +State=1 diff --git a/gb.xml/src/gb.xml/.directory b/gb.xml/src/gb.xml/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/gb.xml/src/gb.xml/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/gb.xml/src/gb.xml/.icon.png b/gb.xml/src/gb.xml/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a9254f949f86671b58a744a6a93623576acc5217 GIT binary patch literal 10569 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 0 And Me.ReadFlags[$state]) Or $posBuffer = $lenBuffer + + If $posBuffer = $lenBuffer Then Return XmlReaderNodeType.EndStream + Return $state + + Endif + + If Not $stream Then Return 0 + If Eof($stream) Then Return XmlReaderNodeType.EndStream + + Do + sBuf = Read #$stream, 1 + $state = Me._ReadChar(sBuf) + Loop Until ($state <> 0 And Me.ReadFlags[$state]) Or Eof($stream) + + If Eof($stream) And $state = 0 Then Return XmlReaderNodeType.EndStream + Return $state + +End + +Public Sub Open(path As String) + + Dim fichier As File + If Left(Path) <> "/" Then Path = ".." &/ Path + fichier = Open path For Input + Me.InputStream = fichier + +End + +Public Sub Close() + + If $stream Then + Close #$stream + Endif + + $posBuffer = 0 + Me._Close() + +End + + + +Private Function Eof_Read() As Boolean + + If $DataBuffer Then Return $posBuffer = $lenBuffer + If Not $stream Then Return False + Return Eof($stream) + +End + +Private Function State_Read() As Integer + + Return $state + +End diff --git a/gb.xml/src/gb.xml/.src/XmlWriter.class b/gb.xml/src/gb.xml/.src/XmlWriter.class new file mode 100644 index 00000000..fa586135 --- /dev/null +++ b/gb.xml/src/gb.xml/.src/XmlWriter.class @@ -0,0 +1,240 @@ +' Gambas class file + +Export + +Property Read Data As String +Property Read DTD As _XmlWriterDTD +Property OutputStream As Stream + +Private $stream As Stream ''The stream the XmlWriter is currently writing into +Private $indent As Boolean ''If the XmlWriter must indent its output +Private $sData As String ''The data of the recently closed stream buffer + +'Internal state +Private $lastWasBlock As Boolean = False '' If the last instruction was a block, then the current must write a line break before +Private $bTagOpen As Boolean = False ''If a tag is currently open +Private $bStringStream As Boolean ''If we are working on our own internal string stream +Private $aElementsPile As New String[] ''The pile of all the currently open elements + +Public Sub Open(Optional fileName As String, Optional Indent As Boolean = $indent, Optional Encoding As String = "UTF-8") + + If fileName Then + If Left(fileName) <> "/" Then fileName = ".." &/ fileName + $stream = Open fileName For Output Create + Else If Not $stream + $stream = Open String For Write + $bStringStream = True + Endif + + $indent = Indent + + Print #$stream, "" + +End + +Public Sub Flush() + + CheckStream() + Flush #$stream + +End + +Public Sub Element(TagName As String, Optional Value As String, Optional Prefix As String, Optional URI As String) + + PrintIndent() + + If Prefix Then TagName = Prefix & ":" & TagName + Print #$stream, "<"; TagName; + + If URI Then PrintXmlNsAttribute(Prefix, URI) + + If Value Then + Print #$stream, ">"; XmlNode.Serialize(Value); " "; + Else + Print #$stream, " />"; + Endif + +End + +Public Sub StartElement(TagName As String, Optional Attributes As String[], Optional Prefix As String, Optional URI As String) + + Dim i As Integer = 0 + + PrintIndent() + + If Prefix Then TagName = Prefix & ":" & TagName + + Print #$stream, "<" & TagName; + + If URI Then PrintXmlNsAttribute(Prefix, URI) + + If Attributes + If (Attributes.Count Mod 2) Then Attributes.Push("") + For i = 0 To Attributes.Max Step 2 + Print #$stream, " "; Attributes[i]; "=\""; Attributes[i + 1]; "\""; + Next + Endif + + $bTagOpen = True + $lastWasBlock = True + $aElementsPile.Push(TagName) + +End + +Public Sub EndElement() + + Dim tag As String + + If Not $aElementsPile.Count Then Return + + tag = $aElementsPile.Pop() + + If $bTagOpen Then 'On ferme le tag précédent + Print #$stream, " />"; + $bTagOpen = False + Else + If $lastWasBlock Then PrintIndent() + Print #$stream, ""; + Endif + + $lastWasBlock = True + +End + +Public Sub Attribute(Name As String, Value As String, Optional Prefix As String, Optional URI As String) + + Dim sData As String + If Not $bTagOpen Then Error.Raise("Writing attribute with no open tag") + If Prefix Then + Name = Prefix & ":" & Name + Endif + + sData = " " & Name & "=\"" & XmlElement.NormalizeAttributeContent(Value) & "\"" + + If URI Then PrintXmlNsAttribute(Prefix, URI) + + Print #$stream, sData; + +End + +Public Sub Text(sText As String) + + CloseTags() + Print #$stream, XmlNode.Serialize(sText); + + $lastWasBlock = False + +End + +Public Sub Comment(sComment As String) + + PrintIndent() + Print #$stream, ""; + + $lastWasBlock = True + +End + +Public Sub CDATA(data As String) + + PrintIndent() + Print #$stream, ""; + + $lastWasBlock = True +End + +Public Sub PI(Target As String, Content As String) + + PrintIndent() + Print #$stream, ""; + + $lastWasBlock = True +End + +Public Function EndDocument() As String + + CheckStream() + + While $aElementsPile.Count + Me.EndElement() + Wend + + Try Flush #$stream + + If $bStringStream Then + $sData = Null + Try $sData = Close $stream + Return $sData + Endif + +End + +Public Sub Close() As String + + EndDocument() + If Not $bStringStream Then Close $stream + Return $sData + +End + +'Utils + +Private Sub CheckStream() + + If Not $stream Then Error.Raise("No output stream") + +End + +Private Sub CloseTags() + + CheckStream() + If $bTagOpen Then + Print #$stream, ">"; + $bTagOpen = False + Endif + +End + +Private Sub PrintIndent() + + CloseTags() + If $indent Then + If $lastWasBlock Then Print #$stream + Print #$stream, String$($aElementsPile.Count, " "); + Endif + +End + +Private Sub PrintXmlNsAttribute(Prefix As String, URI As String) + + Print #$stream, " xmlns"; If(Prefix, ":" & Prefix, ""); "=\""; URI; "\""; + +End + +'Properties + +Private Function Data_Read() As String + + Return $sData + +End + +Private Function OutputStream_Read() As Stream + + Return $stream + +End + +Private Sub OutputStream_Write(Value As Stream) + + $bStringStream = $bStringStream And $stream = Value + $stream = Value + +End + +Private Function DTD_Read() As _XmlWriterDTD + + _XmlWriterDTD._$writer = Me + Return _XmlWriterDTD + +End diff --git a/gb.xml/src/gb.xml/.src/_XmlWriterDTD.class b/gb.xml/src/gb.xml/.src/_XmlWriterDTD.class new file mode 100644 index 00000000..ff1bc3c5 --- /dev/null +++ b/gb.xml/src/gb.xml/.src/_XmlWriterDTD.class @@ -0,0 +1,8 @@ +' Gambas class file + +Export +Create Static + +Public _$writer As XmlWriter + + diff --git a/gb.xml/src/gb.xml/text.xml b/gb.xml/src/gb.xml/text.xml new file mode 100644 index 00000000..df9b1695 --- /dev/null +++ b/gb.xml/src/gb.xml/text.xml @@ -0,0 +1,2 @@ + +HelloBouh diff --git a/gb.xml/src/gbinterface.h b/gb.xml/src/gbinterface.h new file mode 100644 index 00000000..cfec0b15 --- /dev/null +++ b/gb.xml/src/gbinterface.h @@ -0,0 +1,27 @@ +#ifndef GBINTERFACE_H +#define GBINTERFACE_H + +#include "gambas.h" + +extern "C" GB_INTERFACE GB; + +#define VARGOBJ(_type, _ob) ((_type*)VARG(_ob)) +#define VPROPOBJ(_type) ((_type*)VPROP(GB_OBJECT)) +#define STRINGOPT(_str, _repl, _lenrepl) MISSING(_str) ? _repl : STRING(_str),\ + MISSING(_str) ? _lenrepl : LENGTH(_str) + + + +struct Node; +struct Attribute; + +typedef struct CNode +{ + GB_BASE ob; + Node *node; + Attribute *curAttrEnum; +}CNode; + +typedef CNode CDocument; + +#endif // GBINTERFACE_H diff --git a/gb.xml/src/html/CHTMLDocument.cpp b/gb.xml/src/html/CHTMLDocument.cpp new file mode 100644 index 00000000..c4b0d8fb --- /dev/null +++ b/gb.xml/src/html/CHTMLDocument.cpp @@ -0,0 +1,236 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CHTMLDocument.h" +#include "htmldocument.h" +#include "htmlelement.h" + +/*========== Document */ + +#define THIS ((Document*)(static_cast(_object)->node)) + +BEGIN_METHOD_VOID(CDocument_new) + +END_METHOD + +BEGIN_METHOD_VOID(CDocument_free) + + + +END_METHOD + +BEGIN_PROPERTY(CDocument_Html5) + +if(READ_PROPERTY) +{ + GB.ReturnBoolean(THIS->docType == HTMLDocumentType); +} +else +{ + HtmlDocument_SetHTML(THIS, VPROP(GB_BOOLEAN)); +} + +END_PROPERTY + +BEGIN_PROPERTY(CDocument_Title) + +if(READ_PROPERTY) +{ + char *title; size_t lenTitle; + XML.GBGetXMLTextContent(HtmlDocument_GetTitle(THIS), title, lenTitle); + GB.ReturnString(title); +} +else +{ + if(PLENGTH() <= 0) return; + XML.XMLNode_setTextContent(HtmlDocument_GetTitle(THIS), PSTRING(), PLENGTH()); +} + +END_PROPERTY + +BEGIN_PROPERTY(CDocument_favicon) + +if(READ_PROPERTY) +{ + char *favicon; size_t lenFavicon; + XML.GBGetXMLTextContent(HtmlDocument_GetFavicon(THIS), favicon, lenFavicon); + GB.ReturnString(favicon); +} +else +{ + if(PLENGTH() <= 0) return; + XML.XMLNode_setTextContent(HtmlDocument_GetFavicon(THIS), PSTRING(), PLENGTH()); +} + +END_PROPERTY + +BEGIN_PROPERTY(CDocument_lang) + +if(READ_PROPERTY) +{ + char *lang; size_t lenLang; + XML.GBGetXMLTextContent(HtmlDocument_GetLang(THIS), lang, lenLang); + GB.ReturnString(lang); +} +else +{ + if(PLENGTH() <= 0) return; + XML.XMLNode_setTextContent(HtmlDocument_GetLang(THIS), PSTRING(), PLENGTH()); +} + +END_PROPERTY + +BEGIN_PROPERTY(CDocument_base) + +if(READ_PROPERTY) +{ + char *base; size_t lenBase; + XML.GBGetXMLTextContent(HtmlDocument_GetBase(THIS), base, lenBase); + GB.ReturnString(base); +} +else +{ + if(PLENGTH() <= 0) return; + XML.XMLNode_setTextContent(HtmlDocument_GetBase(THIS), PSTRING(), PLENGTH()); +} + +END_PROPERTY + +BEGIN_PROPERTY(CDocument_root) + +XML.ReturnNode(THIS->root); + +END_PROPERTY + +BEGIN_PROPERTY(CDocument_head) + +XML.ReturnNode(HtmlDocument_GetHead(THIS)); + +END_PROPERTY + +BEGIN_PROPERTY(CDocument_body) + +XML.ReturnNode(HtmlDocument_GetBody(THIS)); + +END_PROPERTY + +BEGIN_METHOD(CDocument_getElementById, GB_STRING id; GB_INTEGER depth) + +XML.ReturnNode(HtmlDocument_GetElementById(THIS, STRING(id), LENGTH(id), VARGOPT(depth, -1))); + +END_METHOD + +BEGIN_METHOD(CDocument_getElementsByClassName, GB_STRING className; GB_INTEGER depth) + +if(LENGTH(className) <= 0) return; +GB_ARRAY array; +HtmlDocument_GetElementsByClassName(THIS, STRING(className), LENGTH(className), &array, VARGOPT(depth, -1)); +GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(CDocumentStyleSheets_add, GB_STRING path; GB_STRING media) + +HtmlDocument_AddStyleSheet(THIS, STRING(path), LENGTH(path), STRINGOPT(media, "screen", 6)); + +END_METHOD + +BEGIN_METHOD(CDocumentStyleSheets_addIfNotIE, GB_STRING path; GB_STRING media) + +HtmlDocument_AddStyleSheetIfNotIE(THIS, STRING(path), LENGTH(path), STRINGOPT(media, "screen", 6)); + +END_METHOD + +BEGIN_METHOD(CDocumentStyleSheets_addIfIE, GB_STRING path; GB_STRING cond; GB_STRING media) + +HtmlDocument_AddStyleSheetIfIE(THIS, STRING(path), LENGTH(path), + STRINGOPT(cond, "IE", 2), STRINGOPT(media, "screen", 6)); + +END_METHOD + +BEGIN_METHOD(CDocumentScripts_add, GB_STRING path) + +HtmlDocument_AddScript(THIS, STRING(path), LENGTH(path)); + +END_METHOD + +BEGIN_METHOD(CDocumentScripts_addIfNotIE, GB_STRING path) + +HtmlDocument_AddScriptIfNotIE(THIS, STRING(path), LENGTH(path)); + +END_METHOD + +BEGIN_METHOD(CDocumentScripts_addIfIE, GB_STRING path; GB_STRING cond) + +HtmlDocument_AddScriptIfIE(THIS, STRING(path), LENGTH(path), STRINGOPT(cond, "IE", 2)); + +END_METHOD + +GB_DESC CDocumentStyleSheetsDesc[] = +{ + GB_DECLARE(".HtmlDocumentStyleSheets", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("Add", NULL, CDocumentStyleSheets_add, "(Source)s[(Media)s]"), + GB_METHOD("AddIfIE", NULL, CDocumentStyleSheets_addIfIE, "(Source)s[(Condition)s(Media)s]"), + GB_METHOD("AddIfNotIE", NULL, CDocumentStyleSheets_addIfNotIE, "(Source)s[(Media)s]"), + + GB_END_DECLARE +}; + +GB_DESC CDocumentScriptsDesc[] = +{ + GB_DECLARE(".HtmlDocumentScripts", 0), GB_VIRTUAL_CLASS(), + + GB_METHOD("Add", NULL, CDocumentScripts_add, "(Source)s"), + GB_METHOD("AddIfIE", NULL, CDocumentScripts_addIfIE, "(Source)s[(Condition)s]"), + GB_METHOD("AddIfNotIE", NULL, CDocumentScripts_addIfNotIE, "(Source)s"), + + GB_END_DECLARE +}; + +GB_DESC CDocumentDesc[] = +{ + GB_DECLARE("HtmlDocument", sizeof(CDocument)), GB_INHERITS("XmlDocument"), + + GB_METHOD("_new", NULL, CDocument_new, ""), + GB_METHOD("_free", NULL, CDocument_free, ""), + + GB_PROPERTY("Html5", "b", CDocument_Html5), + + GB_PROPERTY("Title", "s", CDocument_Title), + GB_PROPERTY("Favicon", "s", CDocument_favicon), + GB_PROPERTY("Lang", "s", CDocument_lang), + GB_PROPERTY("Base", "s", CDocument_base), + GB_PROPERTY_READ("Head", "XmlElement", CDocument_head), + GB_PROPERTY_READ("Body", "XmlElement", CDocument_body), + + //GB_METHOD("FromString", NULL, CDocument_fromString, "(Data)s"), + //GB_METHOD("HtmlFromString", NULL, CDocument_fromString, "(Data)s"), + + GB_PROPERTY_SELF("StyleSheets", ".HtmlDocumentStyleSheets"), + GB_PROPERTY_SELF("Scripts", ".HtmlDocumentScripts"), + + GB_METHOD("GetElementById", "XmlElement", CDocument_getElementById, "(Id)s[(Depth)i]"), + GB_METHOD("GetElementsByClassName", "XmlElement[]", CDocument_getElementsByClassName, "(ClassName)s[(Depth)i]"), + + + GB_END_DECLARE +}; diff --git a/gb.xml/src/html/CHTMLDocument.h b/gb.xml/src/html/CHTMLDocument.h new file mode 100644 index 00000000..956a9e6b --- /dev/null +++ b/gb.xml/src/html/CHTMLDocument.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef HCDOCUMENT_H +#define HCDOCUMENT_H + +#include "htmlmain.h" +#include "../gbinterface.h" + +#ifndef CLASSES_CPP +extern GB_DESC CDocumentDesc[]; +extern GB_DESC CDocumentStyleSheetsDesc[]; +extern GB_DESC CDocumentScriptsDesc[]; +#endif + +#endif diff --git a/gb.xml/src/html/CHTMLElement.cpp b/gb.xml/src/html/CHTMLElement.cpp new file mode 100644 index 00000000..c89288a7 --- /dev/null +++ b/gb.xml/src/html/CHTMLElement.cpp @@ -0,0 +1,121 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "htmlelement.h" +#include "cssfilter.h" +#include "../gbinterface.h" + +/*========== Element */ + +#define THIS ((Element*)(static_cast(_object)->node)) +#define THISNODE (static_cast(_object)->node) + +BEGIN_PROPERTY(CElement_id) + +if(READ_PROPERTY) +{ + Attribute *id = HTMLElement_GetId(THIS); + if(id) + { + GB.ReturnNewString(id->attrValue, id->lenAttrValue); + } + else + { + GB.ReturnNull(); + } +} +else +{ + HTMLElement_SetId(THIS, PSTRING(), PLENGTH()); +} + +END_PROPERTY + +BEGIN_PROPERTY(CElement_className) + +if(READ_PROPERTY) +{ + Attribute *className = HTMLElement_GetClassName(THIS); + if(className) + { + GB.ReturnNewString(className->attrValue, className->lenAttrValue); + } + else + { + GB.ReturnNull(); + } +} +else +{ + HTMLElement_SetClassName(THIS, PSTRING(), PLENGTH()); +} + +END_PROPERTY + +BEGIN_METHOD(CElement_matchFilter, GB_STRING filter) + +GB.ReturnBoolean(HTMLElement_MatchFilter(THIS, STRING(filter), LENGTH(filter))); + +END_METHOD + +BEGIN_METHOD(CElement_getChildrenByFilter, GB_STRING filter; GB_INTEGER depth) + +GB_ARRAY array; + +HTMLElement_GetGBChildrenByFilter(THIS, STRING(filter), LENGTH(filter), &array, VARGOPT(depth, -1)); + +GB.ReturnObject(array); + +END_METHOD + +BEGIN_METHOD(CElement_getChildById, GB_STRING id; GB_INTEGER depth) + +XML.ReturnNode(HTMLElement_GetChildById(THIS, STRING(id), LENGTH(id), VARGOPT(depth, -1))); + +END_METHOD + +BEGIN_METHOD(CElement_getChildrenByClassName, GB_STRING className; GB_INTEGER depth) + + GB_ARRAY array; + + HTMLElement_GetGBChildrenByClassName(THIS, STRING(className), LENGTH(className), &array, VARGOPT(depth, -1)); + + GB.ReturnObject(array); + +END_METHOD + +GB_DESC CElementDesc[] = +{ + GB_DECLARE("XmlElement", sizeof(CNode)), + + GB_PROPERTY("Id", "s", CElement_id), + GB_PROPERTY("ClassName", "s", CElement_className), + + GB_METHOD("MatchFilter", "b", CElement_matchFilter, "(Filter)s"), + GB_METHOD("GetChildrenByFilter", "XmlElement[]", CElement_getChildrenByFilter, "(Filter)s[(Depth)i]"), + + GB_METHOD("GetChildById", "XmlElement", CElement_getChildById, "(Id)s[(Depth)i]"), + GB_METHOD("GetChildrenByClassName", "XmlElement[]", CElement_getChildrenByClassName, "(ClassName)s[(Depth)i]"), + + + + GB_END_DECLARE +}; diff --git a/gb.xml/src/html/CHTMLElement.h b/gb.xml/src/html/CHTMLElement.h new file mode 100644 index 00000000..1a9eab5c --- /dev/null +++ b/gb.xml/src/html/CHTMLElement.h @@ -0,0 +1,32 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef HCELEMENT_H +#define HCELEMENT_H + +#include "htmlmain.h" +#include "../gbinterface.h" + +#ifndef __HELEMENT_CPP +extern GB_DESC CElementDesc[]; +#endif + +#endif diff --git a/gb.xml/src/html/Makefile.am b/gb.xml/src/html/Makefile.am new file mode 100644 index 00000000..f571657c --- /dev/null +++ b/gb.xml/src/html/Makefile.am @@ -0,0 +1,19 @@ +COMPONENT = gb.xml.html +include $(top_srcdir)/component.am +gblib_LTLIBRARIES = gb.xml.html.la + +##gb_xml_html_la_LIBADD = @XMLHTML_LIB@ @XML_LIB@ -l:gb.xml.so +##gb_xml_html_la_LDFLAGS = -module @LD_FLAGS@ @XMLHTML_LDFLAGS@ -L$(srcdir)/../.libs +gb_xml_html_la_LIBADD = @XMLHTML_LIB@ @XML_LIB@ +gb_xml_html_la_LDFLAGS = -module @LD_FLAGS@ @XMLHTML_LDFLAGS@ +gb_xml_html_la_CPPFLAGS = @XMLHTML_INC@ +gb_xml_html_la_CXXFLAGS = $(AM_CXXFLAGS) -fexceptions + +gb_xml_html_la_SOURCES = *.h \ +htmlmain.cpp \ +htmldocument.cpp htmlelement.cpp \ +CHTMLDocument.cpp CHTMLElement.cpp \ +htmlserializer.cpp \ +cssfilter.cpp \ +htmlparser.cpp \ +gb.xml.html.h diff --git a/gb.xml/src/html/cssfilter.cpp b/gb.xml/src/html/cssfilter.cpp new file mode 100644 index 00000000..2d9a0415 --- /dev/null +++ b/gb.xml/src/html/cssfilter.cpp @@ -0,0 +1,195 @@ +#include "cssfilter.h" +#include "htmlmain.h" +#include "htmlelement.h" +#include + +bool HTMLElement_MatchSubFilter(const Element *elmt, const char *filter, size_t lenFilter); + +bool HTMLElement_MatchFilter(const Element *elmt, const char *filter, size_t lenFilter) +{ + if(!lenFilter) return true; + XML.Trim(filter, lenFilter); + char *pos; + + pos = (char*)memrchr(filter, ',', lenFilter); + if(pos) + { + return HTMLElement_MatchFilter(elmt, filter, (pos - filter)) || + HTMLElement_MatchFilter(elmt, pos, lenFilter - (pos + 1 - filter)); + } + + pos = (char*)memrchr(filter, '>', lenFilter); + if(pos) + { + Element *parent = (Element*)(elmt->parent); + if(!parent) return false; + return HTMLElement_MatchFilter(parent, filter, (pos - filter)) && + HTMLElement_MatchFilter(elmt, pos, lenFilter - (pos + 1 - filter)); + } + + pos = (char*)memrchr(filter, '+', lenFilter); + if(pos) + { + Element *previous = XML.XMLNode_previousElement(elmt); + if(!previous) return false; + return HTMLElement_MatchFilter(previous, filter, (pos - filter)) && + HTMLElement_MatchFilter(previous, pos, lenFilter - (pos + 1 - filter)); + } + + pos = (char*)memrchr(filter, ' ', lenFilter); + if(pos) + { + if(!HTMLElement_MatchFilter(elmt, pos, lenFilter - (pos + 1 - filter))) return false; + for(Node *parent = elmt->parent; parent != 0; parent = parent->parent)//TODO: does not support non-element parents + { + if(parent->type == Node::ElementNode) + { + if(HTMLElement_MatchFilter((Element*)parent, filter, (pos - filter))) return true; + } + } + + return false; + } + + return HTMLElement_MatchSubFilter(elmt, filter, lenFilter); +} + +bool HTMLElement_MatchSubFilter(const Element *elmt, const char *filter, size_t lenFilter) +{ + if(!lenFilter) return true; + XML.Trim(filter, lenFilter); + if(!lenFilter) return true; + + char s = 0; + char const *pos = 0; + + for(pos = filter + 1; pos < filter + lenFilter; ++pos) + { + if(!XML.isNameChar(*pos))//Something else that a name + { + break; + } + } + + bool cond = (pos != filter + lenFilter);//If there is something else to check + + s = *filter; + if(s == '*')//Universal selector + { + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + } + if(s == ':')//Pseudo-class + { + register size_t lenSubStr = pos - filter; + + if(lenSubStr == 11 && !memcmp(filter, "first-child", 11)) + { + if(!elmt->parent) return false; + if(XML.XMLNode_firstChildElement(elmt->parent) != elmt) return false; + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + } + else if(lenSubStr == 10 && !memcmp(filter, "last-child", 10)) + { + if(!elmt->parent) return false; + if(XML.XMLNode_lastChildElement(elmt->parent) != elmt) return false; + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + } + return false; + } + if(XML.isNameStartChar(s))//Tag Name + { + if(!(elmt->lenTagName + filter == pos)) return false;//lenTagName == pos - filter + if(memcmp(elmt->tagName, filter, elmt->lenTagName)) return false; + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + } + else if(s == '#')//ID + { + Attribute *id = HTMLElement_GetId(elmt); + if(!id) return false; + + if(!(id->lenAttrValue + filter + 1 == pos)) return false; + if(memcmp(filter + 1, id->attrValue, pos - (filter + 1))) return false; + if(cond) return (HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter))); + return true; + } + else if(s == '.')//ClassName + { + if(!HTMLElement_HasClassName(elmt, filter + 1, pos - (filter + 1))) return false; + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - (filter + 1))); + return true; + } + else if(s == '[')//Attribut + { + //Syntax : [foo="bar"] + char const *endPos = (char*)memchr(filter, ']', lenFilter);//On cherche le crochet fermant + + endPos = endPos ? endPos : filter + lenFilter - 1; + pos = (endPos + 1); + cond = (pos < filter + lenFilter); + + char *equalPos = (char*)memchr(filter, '=', lenFilter);//On cherche le signe égal + + if(equalPos)//Si trouvé + { + s = *(equalPos - 1);//Le signe avant le signe égal + char const *attrName = filter + 1; size_t lenAttrName = (equalPos - filter - 1); + char const *attrValue = equalPos + 2; size_t lenAttrValue = (endPos - equalPos - 3); + if(s == '~')// ~= Comparison + { + if(!XML.XMLElement_AttributeContains(elmt, attrName, lenAttrName - 1, attrValue, lenAttrValue)) return false; + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + } + if(s == '^')// ^= Comparison + { + Attribute *attr = XML.XMLElement_GetAttribute(elmt, attrName, lenAttrName - 1, 0); + if(!attr) return false; + if(attr->lenAttrValue < lenAttrValue) return false; + if(memcmp(attr->attrValue, attrValue, lenAttrValue)) return false; + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + } + if(s == '$')// $= Comparison + { + Attribute *attr = XML.XMLElement_GetAttribute(elmt, attrName, lenAttrName - 1, 0); + if(!attr) return false; + if(attr->lenAttrValue < lenAttrValue) return false; + if(memcmp(attr->attrValue + attr->lenAttrValue - lenAttrValue, attrValue, lenAttrValue)) return false; + if(cond) HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + } + if(s == '*')// *= Comparison + { + Attribute *attr = XML.XMLElement_GetAttribute(elmt, attrName, lenAttrName - 1, 0); + if(!attr) return false; + if(attr->lenAttrValue < lenAttrValue) return false; + if(!XML.memchrs(attr->attrValue, attr->lenAttrValue, attrValue, lenAttrValue)) return false; + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + } + + Attribute *attr = XML.XMLElement_GetAttribute(elmt, attrName, lenAttrName, 0); + if(!attr) return false; + if(attr->lenAttrValue != lenAttrValue) return false; + if(memcmp(attr->attrValue, attrValue, lenAttrValue)) return false; + + //Valeur de l'attribut + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + } + + Attribute *attr = XML.XMLElement_GetAttribute(elmt, filter + 1, endPos - filter - 1, 0); + if(!attr) return false; + //Si l'attribut est défini + if(cond) return HTMLElement_MatchSubFilter(elmt, pos, lenFilter - (pos - filter)); + return true; + + } + + return false; + +} diff --git a/gb.xml/src/html/cssfilter.h b/gb.xml/src/html/cssfilter.h new file mode 100644 index 00000000..0afbd29f --- /dev/null +++ b/gb.xml/src/html/cssfilter.h @@ -0,0 +1,8 @@ +#ifndef CSSFILTER_H +#define CSSFILTER_H + +#include "../gb.xml.h" + +bool HTMLElement_MatchFilter(const Element *elmt, const char *filter, size_t lenFilter); + +#endif // CSSFILTER_H diff --git a/gb.xml/src/html/gb.xml.html.component b/gb.xml/src/html/gb.xml.html.component new file mode 100755 index 00000000..c236ecc4 --- /dev/null +++ b/gb.xml/src/html/gb.xml.html.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.xml.html +Author=Adrien Prokopowicz +State=Stable +Require=gb.xml \ No newline at end of file diff --git a/gb.xml/src/html/gb.xml.html.h b/gb.xml/src/html/gb.xml.html.h new file mode 100644 index 00000000..225ff7a6 --- /dev/null +++ b/gb.xml/src/html/gb.xml.html.h @@ -0,0 +1,26 @@ +#ifndef GB_XML_HTML_H +#define GB_XML_HTML_H + +#include "../gb.xml.h" + +#define XML_HTML_INTERFACE_VERSION 1 + +typedef struct +{ + int version; + //Converts the node to its string representation + void (*serializeHTMLNode)(Node *node, char *&output, size_t &len, int indent); + void (*GBserializeHTMLNode)(Node *node, char *&output, size_t &len, int indent); + + //Parser + Node** (*parseHTML)(char const *data, const size_t lendata, size_t *nodeCount); + void (*GBparseHTML)(char const *data, const size_t lendata, GB_ARRAY *array); + + //HtmlDocument Interface + Document* (*HtmlDocument_New)(); + Document* (*HtmlDocument_NewFromFile)(const char *fileName, const size_t lenFileName); + void *_null; + +}XML_HTML_INTERFACE; + +#endif // GB_XML_HTML_H diff --git a/gb.xml/src/html/htmldocument.cpp b/gb.xml/src/html/htmldocument.cpp new file mode 100644 index 00000000..8b70876f --- /dev/null +++ b/gb.xml/src/html/htmldocument.cpp @@ -0,0 +1,317 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + + +#include "htmlelement.h" +#include "htmldocument.h" +#include +#include + +Element* GetElement(Node *parent, const char *tagName, const size_t lenTagName); +Attribute* GetAttribute(Element *elmt, const char *nattrName, const size_t nlenAttrName); + +void UpdateMetaCharset(Document *doc, bool html5); + +Document *HtmlDocument_New() +{ + Document *newDoc = XML.XMLDocument_New(); + newDoc->docType = XHTMLDocumentType; + newDoc->root->parentDocument = newDoc; + + XML.XMLElement_SetTagName(newDoc->root, "html", 4); + + Element *head = XML.XMLElement_New("head", 4); + XML.XMLNode_appendChild(newDoc->root, head); + + Element *body = XML.XMLElement_New("body", 4); + XML.XMLNode_appendChild(newDoc->root, body); + + //Meta utf-8 + Element *meta = XML.XMLElement_New("meta", 4); + + XML.XMLElement_AddAttribute(meta, "http-equiv", 10, "Content-Type", 12); + XML.XMLElement_AddAttribute(meta, "content", 7,"text/html; charset=utf-8", 24); + XML.XMLNode_appendChild(head, meta); + + //Title + Element *title = XML.XMLElement_New("title", 5); + XML.XMLNode_appendChild(head, title); + + return newDoc; +} + +Document* HtmlDocument_NewFromFile(const char *fileName, const size_t lenFileName) +{ + return XML.XMLDocument_NewFromFile(fileName, lenFileName, XHTMLDocumentType); +} + +void HtmlDocument_SetHTML(Document *doc, const bool isHtml) +{ + UpdateMetaCharset(doc, isHtml); + if(isHtml) + { + doc->docType = HTMLDocumentType; + } + else + { + doc->docType = XHTMLDocumentType; + } +} + +Element* HtmlDocument_GetHead(Document *doc) +{ + return GetElement(doc->root, "head", 4); +} + +Element* HtmlDocument_GetBody(Document *doc) +{ + return GetElement(doc->root, "body", 4); +} + +Element* HtmlDocument_GetTitle(Document *doc) +{ + return GetElement(HtmlDocument_GetHead(doc), "title", 5); +} + +Attribute* HtmlDocument_GetFavicon(Document *doc) +{ + Element *head = HtmlDocument_GetHead(doc); + Element **elmts; size_t lenElmts; + elmts = XML.XMLNode_getChildrenByTagName(head, "link", 4, lenElmts, 2); + Element *elmt; + + Attribute *attr; + + for(unsigned int i = 0; i < lenElmts; i++) + { + attr = XML.XMLElement_GetAttribute(elmts[i], "rel", 3, 0); + if(attr->lenAttrValue == 4) + { + if(!memcmp(attr->attrValue, "icon", 4)) + { + elmt = elmts[i]; + free(elmts); + return XML.XMLElement_GetAttribute(elmt, "href", 4, 0); + } + } + } + + free(elmts); + elmt = XML.XMLElement_New("link", 4); + XML.XMLElement_AddAttribute(elmt, "rel", 3, "icon", 4); + XML.XMLNode_appendChild(head, elmt); + return GetAttribute(elmt, "href", 4); +} + + +Attribute* HtmlDocument_GetBase(Document *doc) +{ + return GetAttribute(GetElement(HtmlDocument_GetHead(doc), "base", 4), "href", 4); +} + +Attribute* HtmlDocument_GetLang(Document *doc) +{ + return GetAttribute(doc->root, "lang", 4); +} + +void HtmlDocument_AddStyleSheet(Document *doc, const char *src, size_t lenSrc, + const char *media, size_t lenMedia) +{ + Element *elmt = XML.XMLElement_New("link", 4); + XML.XMLElement_AddAttribute(elmt, "rel", 3, "stylesheet", 10); + XML.XMLElement_AddAttribute(elmt, "href", 4, src, lenSrc); + XML.XMLElement_AddAttribute(elmt, "type", 4, "text/css", 8); + XML.XMLElement_AddAttribute(elmt, "media", 5, media, lenMedia); + XML.XMLNode_appendChild(HtmlDocument_GetHead(doc),elmt); +} + +void HtmlDocument_AddStyleSheetIfIE(Document *doc, const char *src, size_t lenSrc, + const char *cond, size_t lenCond, + const char *media, size_t lenMedia) +{ +//[if +cond+]><", 10); + XML.XMLNode_appendChild(head,comment); + HtmlDocument_AddStyleSheet(doc, src, lenSrc, media, lenMedia); + comment = XML.XMLComment_New("><", 10); + XML.XMLNode_appendChild(head,comment); + + HtmlDocument_AddScript(doc, src, lenSrc); + + comment = XML.XMLComment_New(">docType == (html5 ? HTMLDocumentType : XHTMLDocumentType)) return; + + //Looking for the meta charset element + size_t lenMetas; + Element **metas = XML.XMLNode_getChildrenByTagName(HtmlDocument_GetHead(doc), "meta", 4, lenMetas, 2); + Element *meta = 0; + Element *tmeta = 0; + Attribute *attr; + + for(size_t i = 0; i < lenMetas; i++) + { + tmeta = metas[i]; + if(doc->docType == XHTMLDocumentType) + { + attr = XML.XMLElement_GetAttribute(tmeta, "http-equiv", 10, 0); + if(!attr) continue; + if(!XML.GB_MatchString(attr->attrValue, attr->lenAttrValue, "Content-Type", 12, 0)) continue; + XML.XMLElement_RemoveAttribute(tmeta, attr); + attr = XML.XMLElement_GetAttribute(tmeta, "content", 7, 0); + if(!attr) continue; + if(!XML.GB_MatchString(attr->attrValue, attr->lenAttrValue, "text/html; charset=utf-8", 24, 0)) continue; + XML.XMLElement_RemoveAttribute(tmeta, attr); + meta = tmeta; + break; + } + else + { + attr = XML.XMLElement_GetAttribute(metas[i], "charset", 7, 0); + if(!attr) continue; + if(!XML.GB_MatchString(attr->attrValue, attr->lenAttrValue, "utf-8", 5, 0)) continue; + XML.XMLElement_RemoveAttribute(metas[i], attr); + + meta = metas[i]; + break; + } + } + free(metas); + + if(!meta) + { + meta = XML.XMLElement_New("meta", 4); + XML.XMLNode_appendChild(HtmlDocument_GetHead(doc), meta); + } + + if(html5) + { + XML.XMLElement_AddAttribute(meta, "charset", 7, "utf-8", 5); + } + else + { + XML.XMLElement_AddAttribute(meta, "http-equiv", 11, "Content-Type", 12); + XML.XMLElement_AddAttribute(meta, "content", 7, "text/html; charset=utf-8", 25); + } + +} + diff --git a/gb.xml/src/html/htmldocument.h b/gb.xml/src/html/htmldocument.h new file mode 100644 index 00000000..839f12be --- /dev/null +++ b/gb.xml/src/html/htmldocument.h @@ -0,0 +1,61 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef HDOCUMENT_H +#define HDOCUMENT_H + +#include "htmlmain.h" + +Document* HtmlDocument_New(); +Document* HtmlDocument_NewFromFile(const char *fileName, const size_t lenFileName); +Element* HtmlDocument_GetBody(Document *doc); +Element* HtmlDocument_GetHead(Document *doc); + +Element* HtmlDocument_GetElementById(Document *doc, const char *id, const size_t lenId, int depth = -1); +void HtmlDocument_GetElementsByClassName(Document *doc, const char *className, const size_t lenClassName, GB_ARRAY *array, int depth = -1); + +Element* HtmlDocument_GetTitle(Document *doc); +Attribute* HtmlDocument_GetFavicon(Document *doc); +Attribute* HtmlDocument_GetBase(Document *doc); +Attribute* HtmlDocument_GetLang(Document *doc); + +void HtmlDocument_SetHTML(Document *doc, const bool isHtml); + +void HtmlDocument_AddStyleSheet(Document *doc, const char *src, size_t lenSrc, + const char *media, size_t lenMedia); + +void HtmlDocument_AddStyleSheetIfIE(Document *doc, const char *src, size_t lenSrc, + const char *cond, size_t lenCond, + const char *media, size_t lenMedia); + +void HtmlDocument_AddStyleSheetIfNotIE(Document *doc, const char *src, size_t lenSrc, + const char *media, size_t lenMedia); + +void HtmlDocument_AddScript(Document *doc, const char *src, size_t lenSrc); + + +void HtmlDocument_AddScriptIfIE(Document *doc, const char *src, size_t lenSrc, + const char *cond, size_t lenCond); + +void HtmlDocument_AddScriptIfNotIE(Document *doc, const char *src, size_t lenSrc); + + +#endif diff --git a/gb.xml/src/html/htmlelement.cpp b/gb.xml/src/html/htmlelement.cpp new file mode 100644 index 00000000..095f1444 --- /dev/null +++ b/gb.xml/src/html/htmlelement.cpp @@ -0,0 +1,104 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "htmlelement.h" +#include "cssfilter.h" +#include "../gbinterface.h" + +void HTMLElement_AddGBChildrenByFilter(Element *elmt, char *filter, size_t lenFilter, GB_ARRAY *array, int depth = -1); + +bool HTMLElement_HasClassName(const Element *elmt, const char *className, size_t lenClassName) +{ + return XML.XMLElement_AttributeContains(elmt, "class", 5, className, lenClassName); +} + +Element* HTMLElement_GetChildById(Element *elmt, char *id, size_t lenId, int depth) +{ + return XML.XMLNode_getFirstChildByAttributeValue(elmt, "id", 2, id, lenId, 0, depth); +} + +void HTMLElement_GetGBChildrenByClassName(Element *elmt, char* className, size_t lenClassName, GB_ARRAY *array, int depth) +{ + XML.XMLNode_getGBChildrenByAttributeValue(elmt, "class", 5, className, lenClassName, array, 0, depth); +} + +void HTMLElement_SetId(Element *elmt, const char *value, size_t len) +{ + XML.XMLElement_SetAttribute(elmt, "id", 2, value, len); +} + +void HTMLElement_SetClassName(Element *elmt, const char *value, size_t len) +{ + XML.XMLElement_SetAttribute(elmt, "class", 5, value, len); +} + +Attribute* HTMLElement_GetClassName(const Element *elmt) +{ + return XML.XMLElement_GetAttribute(elmt, "class", 5, 0); +} + +Attribute* HTMLElement_GetId(const Element *elmt) +{ + return XML.XMLElement_GetAttribute(elmt, "id", 2, 0); +} + +void HTMLElement_GetGBChildrenByFilter(Element *elmt, char *filter, size_t lenFilter, GB_ARRAY *array, int depth) +{ + GB.Array.New(array, GB.FindClass("XmlElement"), 0); + HTMLElement_AddGBChildrenByFilter(elmt, filter, lenFilter, array, depth); +} + +void HTMLElement_AddGBChildrenByFilter(Element *elmt, char *filter, size_t lenFilter, GB_ARRAY *array, int depth) +{ + if(depth == 0) return; + for(Node *node = elmt->firstChild; node != 0; node = node->nextNode) + { + if(node->type == Node::ElementNode) + { + if(HTMLElement_MatchFilter((Element*)node, filter, lenFilter)) + { + *(reinterpret_cast((GB.Array.Add(*array)))) = XML.XMLNode_GetGBObject(node); + GB.Ref(node->GBObject); + } + HTMLElement_AddGBChildrenByFilter((Element*)(node), filter, lenFilter, array, depth - 1); + } + } +} + +const size_t lenSingleElements[] = {2 , 3 ,4 ,5 ,4 ,4 ,2 ,7 ,5 ,2 ,5 ,4 ,2 ,6 ,5 ,3}; +const char* singleElements[] = {"br", "img", "meta", "input", "area", "base", "co", "command", "embed", "hr", "keygen", "link", "param", "source", "track", "wbr"}; +#define COUNT_SINGLEELEMENTS 16 + + +bool HTMLElement_IsSingle(Element *elmt) +{ + for(int i = 0; i < COUNT_SINGLEELEMENTS; i++) + { + if(elmt->lenTagName == lenSingleElements[i]) + { + if(!strncasecmp(singleElements[i], elmt->tagName, elmt->lenTagName)) + { + return true; + } + } + } + return false; +} diff --git a/gb.xml/src/html/htmlelement.h b/gb.xml/src/html/htmlelement.h new file mode 100644 index 00000000..5e201c90 --- /dev/null +++ b/gb.xml/src/html/htmlelement.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef HELEMENT_H +#define HELEMENT_H + +#include "htmlmain.h" + +Attribute* HTMLElement_GetClassName(const Element *elmt); +bool HTMLElement_HasClassName(const Element *elmt, const char *className, const size_t lenClassName); +void HTMLElement_SetClassName(Element *elmt, const char *className, const size_t lenClassName); + +Attribute* HTMLElement_GetId(const Element *elmt); +void HTMLElement_SetId(Element *elmt, const char* value, size_t len); + +void HTMLElement_GetGBChildrenByFilter(Element *elmt, char *filter, size_t lenFilter, GB_ARRAY *array, int depth = -1); + +Element* HTMLElement_GetChildById(Element *elmt, char *id, size_t lenId, int depth = -1); +void HTMLElement_GetGBChildrenByClassName(Element *elmt, char* className, size_t lenClassName, GB_ARRAY *array, int depth = -1); + +bool HTMLElement_IsSingle(Element *elmt); + + +#endif diff --git a/gb.xml/src/html/htmlmain.cpp b/gb.xml/src/html/htmlmain.cpp new file mode 100644 index 00000000..c892c84d --- /dev/null +++ b/gb.xml/src/html/htmlmain.cpp @@ -0,0 +1,63 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __HMAIN_CPP +#include "htmlmain.h" +#include "CHTMLElement.h" +#include "CHTMLDocument.h" +#include "gb.xml.html.h" +#include "htmlserializer.h" +#include "htmlparser.h" +#include "htmldocument.h" + +GB_INTERFACE GB EXPORT; +XML_INTERFACE XML EXPORT; + +extern "C"{ +GB_DESC *GB_CLASSES[] EXPORT = +{ + CDocumentDesc, CDocumentStyleSheetsDesc, CDocumentScriptsDesc, CElementDesc, 0 +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.xml", XML_INTERFACE_VERSION, &XML); + return -1; +} + +void EXPORT GB_EXIT() +{ + +} + +void *GB_XML_HTML_1[] EXPORT = +{ + (void*)XML_HTML_INTERFACE_VERSION, + (void *)serializeHTMLNode, + (void *)GBserializeHTMLNode, + (void *)parseHTML, + (void *)GBparseHTML, + (void *)HtmlDocument_New, + (void *)HtmlDocument_NewFromFile, + NULL +}; + +} diff --git a/gb.xml/src/html/htmlmain.h b/gb.xml/src/html/htmlmain.h new file mode 100644 index 00000000..47f68c01 --- /dev/null +++ b/gb.xml/src/html/htmlmain.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef HMAIN_H +#define HMAIN_H + +#include "../gb.xml.h" + +#ifndef __HMAIN_CPP +extern "C" XML_INTERFACE XML; +#endif + +#if defined(OS_MACOSX) || defined(__APPLE__) +#define memrchr(s, c, n) XML.memrchr(s, c, n) +#endif + +#endif diff --git a/gb.xml/src/html/htmlparser.cpp b/gb.xml/src/html/htmlparser.cpp new file mode 100644 index 00000000..6b9f925a --- /dev/null +++ b/gb.xml/src/html/htmlparser.cpp @@ -0,0 +1,342 @@ +#include "htmlparser.h" +#include "htmlelement.h" +#include "../gbinterface.h" +#include +#include "../main.h" + +/***** Parser *****/ + +void GBparseHTML(const char *data, const size_t lendata, GB_ARRAY *array) +{ + size_t nodeCount; + size_t i = 0; + Node **nodes = parseHTML(data, lendata, &nodeCount); + GB.Array.New(array, GB.FindClass("XmlNode"), nodeCount); + + for(i = 0; i < nodeCount; ++i) + { + *(reinterpret_cast((GB.Array.Get(*array, i)))) = XML.XMLNode_GetGBObject(nodes[i]); + GB.Ref(nodes[i]->GBObject); + } + + free(nodes); +} + +//Ajoute 'elmt' à la liste +#define APPEND(_elmt) if(curElement == 0)\ +{\ + (*nodeCount)++;\ + elements = (Node**)realloc(elements, sizeof(Node*) * (*nodeCount));\ + elements[(*nodeCount) - 1] = _elmt;\ +}\ +else \ +{\ + XML.XMLNode_appendChild(curElement, _elmt); \ +} + +Node** parseHTML(char const *data, const size_t lendata, size_t *nodeCount)// XML.ThrowXMLParseException) +{ + *nodeCount = 0; + if(!lendata || !data) return 0; //Empty ? + + const char *endData = data + lendata; + + Node **elements = 0;//Elements to return + Element *curElement = 0;//Current element + + + register char s = 0;//Current byte (value) + register char const *pos = data;//Current byte (position) + register wchar_t ws = 0;//Current character (value) + + char *tag = 0;//First '<' character found + + while(pos < endData)//Start + { + tag = (char*)memchr(pos, '<', endData - pos);//On cherche un début de tag + + if(tag && (tag - pos) != 0)//On ajoute le texte, s'il existe + { + //Checking length + char const *textpos = pos; + size_t textlen = tag - pos; + //XML.Trim(textpos, textlen); + if(textlen != 0) + { + TextNode *text = XML.XMLTextNode_New("", 0); + XML.XMLTextNode_setEscapedTextContent(text, textpos, textlen); + APPEND(text); + } + } + + if(!tag) + { + if(pos < endData)//Il reste du texte + { + //Checking length + char const *textpos = pos; + size_t textlen = endData - pos; + //XML.Trim(textpos, textlen); + if(textlen != 0) + { + TextNode *text = XML.XMLTextNode_New("", 0); + XML.XMLTextNode_setEscapedTextContent(text, textpos, textlen); + APPEND(text); + } + } + break; + } + + tag++; + pos = tag;//On avance au caractère trouvé + + //On analyse le contenu du tag + ws = XML.nextUTF8Char(pos, endData - pos);//On prend le premier caractère + + if(!XML.isNameStartChar(ws))//Ce n'est pas un tagName, il y a quelque chose ... + { + if(ws == '/')//C'est un élément de fin + { + Element *oldCurElement = curElement; + while(curElement) + { + if((endData) >= pos + curElement->lenTagName) + { + if(!(strncasecmp(pos, curElement->tagName, curElement->lenTagName))) + { + break; + } + } + curElement = (Element*)(curElement->parent); + } + + if(!curElement) + { + curElement = oldCurElement; + } + else + { + pos += curElement->lenTagName; + curElement = (Element*)(curElement->parent); + } + + tag = (char*)memchr(pos, '>', endData - pos);//On cherche la fin du tag + if(tag) pos = tag + 1;//On avance à la fin du tag + + continue; + + } + else if(ws == '!')//Ce serait un commentaire ou un CDATA + { + if(memcmp(pos, "--", 2) == 0)//C'est bien un commentaire + { + pos += 2;//On va au début du contenu du commentaire + tag = (char*)XML.memchrs(pos, endData - pos, "-->", 3); + if(!tag)//Commentaire sans fin + { + //ERREUR : NEVER-ENDING COMMENT + XML.ThrowXMLParseException("Never-ending comment", + data, lendata, pos - 1); + } + + CommentNode *comment = XML.XMLComment_New("", 0); + XML.XMLTextNode_setEscapedTextContent(comment, pos, tag - pos); + APPEND(comment); + pos = tag + 3; + continue; + } + else if(memcmp(pos, "[CDATA[", 7) == 0)//C'est un CDATA + { + pos += 7;//On va au début du contenu du cdata + tag = (char*)XML.memchrs(pos, endData - pos, "]]>", 3); + if(!tag)//Cdata sans fin + { + //ERREUR : UNENDED CDATA + XML.ThrowXMLParseException("Never-ending CDATA", + data, lendata, pos - 1); + } + + CDATANode *cdata = XML.XMLCDATA_New("", 0); + XML.XMLTextNode_setEscapedTextContent(cdata, pos, tag - pos); + APPEND(cdata); + pos = tag + 3; + continue; + } + else if(memcmp(pos, "DOCTYPE", 7) == 0)//Doctypes are silently ignored for the moment + { + pos += 7; + tag = (char*)memchr(pos, '>', endData - pos); + if(!tag)//Doctype sans fin + { + XML.ThrowXMLParseException("Never-ending DOCTYPE", + data, lendata, pos - 1); + } + + pos = tag + 1; + continue; + } + else// ... ? + { + //ERREUR : INVALID TAG + XML.ThrowXMLParseException("Invalid Tag", + data, lendata, pos - 1); + } + } + else if(ws == '?')//Processing Instruction //TODO : add the PI API + { + tag = (char*)XML.memchrs(pos, endData - pos, "?>", 2);//Looking for the end of the PI + if(!tag)//Endless PI + { + XML.ThrowXMLParseException("Never-ending Processing instruction", + data, lendata, pos - 1); + } + + pos = tag + 2; + continue; + + } + else// ... ? + { + //ERREUR : INVALID TAG + XML.ThrowXMLParseException("Invalid Tag", + data, lendata, pos - 1); + } + }//Si tout va bien, on a un nouvel élément + else + { + while(XML.isNameChar(XML.nextUTF8Char(pos, endData - pos)))//On cherche le tagName + { + if(pos > endData) + { + //ERREUR : NEVER-ENDING TAG + XML.ThrowXMLParseException("Never-ending tag", + data, lendata, pos - 1); + } + } + pos--; + + Element *elmt = XML.XMLElement_New(tag, pos - tag); + APPEND(elmt); + curElement = elmt; + s = *pos; + + while(pos < endData)//On gère le contenu de l'élément (attributs) + { + if(s == '>') + { + if(HTMLElement_IsSingle(curElement)) + { + curElement = (Element*)(curElement->parent);//Pas d'enfants, on remonte + } + else if(XML.GB_MatchString(curElement->tagName, curElement->lenTagName, "script", 6, 1)) + { + pos ++;//On va au début du contenu du script + + char *scriptTag = (char*)malloc(sizeof(char) * (curElement->lenTagName) + 3); + scriptTag[0] = '<'; scriptTag[1] = '/'; scriptTag[curElement->lenTagName + 2] = '>'; + memcpy(scriptTag + 2, curElement->tagName, curElement->lenTagName); + + tag = (char*)XML.memchrs(pos, endData - pos, scriptTag, curElement->lenTagName + 3); + + free(scriptTag); + + if(!tag)//Script sans fin + { + XML.ThrowXMLParseException("Never-ending Script", + data, lendata, pos - 1); + } + + TextNode *scriptcontent = XML.XMLTextNode_New("", 0); + XML.XMLTextNode_setEscapedTextContent(scriptcontent, pos, tag - pos); + APPEND(scriptcontent); + pos = tag + curElement->lenTagName + 2; + curElement = (Element*)(curElement->parent);//Pas d'autres enfants, on remonte + } + break;//Fin de l'élément + } + if(s == '/') //Élément auto-fermant + { + pos++; + + curElement = (Element*)(curElement->parent);//Pas d'enfants, on remonte + break; + } + + if(XML.isNameStartChar(s))//Début d'attribut + { + const char *attrNamestart = pos; + while(XML.isNameChar(XML.nextUTF8Char(pos, endData - pos)) && pos < endData){}//On parcourt le nom d'attribut + pos--; + const char *attrNameEnd = pos; + s = *pos; + while(XML.isWhiteSpace(s) && pos < endData){pos++; s = *pos;}//On ignore les espaces blancs + + if(s != '=') + { + XML.XMLElement_AddAttribute(elmt, attrNamestart, attrNameEnd - attrNamestart, "", 0); + if(s == '>') break;//Fin de l'élément + else if (s == '/')//Élément auto-fermant + { + pos++; + curElement = (Element*)(curElement->parent);//Pas d'enfants, on remonte + break; + } + else + { + /*//ERREUR : INVALID TAG + XML.ThrowXMLParseException("Invalid tag", + data, lendata, pos - 1);*/ + //Tag not ended correctly ? ... + pos = (char*)memchr(pos, '>', endData - pos); + break; + } + } + + pos++; s = *pos; + + while(XML.isWhiteSpace(s) && pos < endData){pos++; s = *pos;}//On ignore les espaces blancs + + char delimiter = s; + if(delimiter != '"' && delimiter != '\'') + { + /*//ERREUR : EXPECTED ATTRIBUTE DELIMITER + XML.ThrowXMLParseException("Expected attribute delimiter", + data, lendata, pos - 1);*/ + //Seeking to the next white space or tag-end + while(!(XML.isWhiteSpace(s)) && pos < endData && s != '>'){pos++; s = *pos;} + delimiter = s; + const char* delimiterPos = (char*)memchr(pos, delimiter, endData - pos); + + XML.XMLElement_AddAttribute(elmt, attrNamestart, attrNameEnd - attrNamestart, + pos, delimiterPos - pos); + pos = delimiterPos; + if(delimiter == '>') pos = delimiterPos - 1; + } + else + { + pos++; + const char* delimiterPos = (char*)memchr(pos, delimiter, endData - pos); + if(!delimiterPos) delimiterPos = endData; + + XML.XMLElement_AddAttribute(elmt, attrNamestart, attrNameEnd - attrNamestart, + pos, delimiterPos - pos); + pos = delimiterPos; + } + + + + + + } + + pos++; s = *pos; + } + + } + pos++; + + } + + return elements; + +} diff --git a/gb.xml/src/html/htmlparser.h b/gb.xml/src/html/htmlparser.h new file mode 100644 index 00000000..0e8065fe --- /dev/null +++ b/gb.xml/src/html/htmlparser.h @@ -0,0 +1,9 @@ +#ifndef HTMLPARSER_H +#define HTMLPARSER_H + +#include "htmlmain.h" + +void GBparseHTML(char const *data, const size_t lendata, GB_ARRAY *array); +Node** parseHTML(char const *data, const size_t lendata, size_t *nodeCount);// throw(XMLParseException); + +#endif // HTMLPARSER_H diff --git a/gb.xml/src/html/htmlserializer.cpp b/gb.xml/src/html/htmlserializer.cpp new file mode 100644 index 00000000..77a95854 --- /dev/null +++ b/gb.xml/src/html/htmlserializer.cpp @@ -0,0 +1,239 @@ +#include "htmlserializer.h" +#include "htmlelement.h" +#include "../gbinterface.h" +#include + +void addStringLen(Node *node, size_t &len, int indent = -1);//Calculates the node's string representation length, and adds it to len (recursive) +void addString(Node *node, char *&data, int indent = -1);//Puts the string represenetation into data, and increments it (recursive) + + +void serializeHTMLNode(Node *node, char *&output, size_t &len, int indent) +{ + len = 0; + addStringLen(node, len, indent); + output = (char*)malloc(sizeof(char) * (len)); + addString(node, output, indent); + output -= len; + +} + +void GBserializeHTMLNode(Node *node, char *&output, size_t &len, int indent) +{ + len = 0; + addStringLen(node, len, indent); + output = GB.TempString(0, len); + addString(node, output, indent); + output -= len; +} + +void addStringLen(Node *node, size_t &len, int indent) +{ + switch (node->type) + { + case Node::DocumentNode: + if(((Document*)node)->docType == HTMLDocumentType) + { + len += 15 + (indent >= 0 ? 1 : 0); + } + else + { + len += 109 + (indent >= 0 ? 1 : 0); + } + //Content + for(register Node *child = node->firstChild; child != 0; child = child->nextNode) + { + addStringLen(child, len, indent >= 0 ? indent : -1); + } + break; + case Node::ElementNode: + // (indent) '<' + prefix:tag + (' ' + attrName + '=' + '"' + attrValue + '"') + '>' \n + // + children + (indent) '" \n + // Or, singlElement : + // (indent) '<' + prefix:tag + (' ' + attrName + '=' + '"' + attrValue + '"') + ' />' \n + if(HTMLElement_IsSingle((Element*)node)) + { + len += (4 + ((Element*)node)->lenTagName); + if(indent >= 0) len += indent + 1; + } + else + { + len += (5 + ((((Element*)node)->lenTagName) * 2)); + if(indent >= 0) len += indent * 2 + 2; + for(Node *child = node->firstChild; child != 0; child = child->nextNode) + { + addStringLen(child, len, indent >= 0 ? indent + 1 : -1); + } + } + + for(Attribute *attr = (Attribute*)(((Element*)node)->firstAttribute); attr != 0; attr = (Attribute*)(attr->nextNode)) + { + len += 4 + attr->lenAttrName + attr->lenAttrValue; + } + break; + case Node::NodeText: + + XML.XMLTextNode_checkEscapedContent((TextNode*)node); + len += ((TextNode*)node)->lenEscapedContent; + if(indent >= 0) len += indent + 1; + break; + + case Node::Comment: + + XML.XMLTextNode_checkEscapedContent((TextNode*)node); + // + len += ((TextNode*)node)->lenEscapedContent + 7; + if(indent >= 0) len += indent + 1; + break; + case Node::CDATA: + + XML.XMLTextNode_checkEscapedContent((TextNode*)node); + // + len += ((TextNode*)node)->lenContent + 12; + if(indent) len += indent + 1; + break; + + default: + break; + } +} + + +#define ADD(_car) *data = _car; data++; +void addString(Node *node, char *&data, int indent) +{ + bool single; + switch (node->type) + { + case Node::DocumentNode: + if(((Document*)node)->docType == HTMLDocumentType) + { + memcpy(data, "", 15); + data += 15; + } + else + { + memcpy(data, "", 109); + data += 109; + } + if(indent >= 0) + { + ADD('\n') + } + //Content + for(register Node *child = node->firstChild; child != 0; child = child->nextNode) + { + addString(child, data, indent >= 0 ? indent : -1); + } + break; + case Node::ElementNode: + //register char *content = data; + single = HTMLElement_IsSingle((Element*)node); + + //Opening tag + if(indent > 0) + { + memset(data, ' ', indent); + data += indent; + } + ADD('<'); + memcpy(data, ((Element*)node)->tagName, ((Element*)node)->lenTagName); data += ((Element*)node)->lenTagName; + + //Attributes + for(register Attribute *attr = (Attribute*)((Element*)node)->firstAttribute; attr != 0; attr = (Attribute*)(attr->nextNode)) + { + ADD(' '); + memcpy(data, attr->attrName, attr->lenAttrName); data += attr->lenAttrName; + + ADD('='); + ADD('"'); + memcpy(data, attr->attrValue, attr->lenAttrValue); data += attr->lenAttrValue; + ADD('"'); + } + + if(single) + { + ADD(' '); + ADD('/'); + } + ADD('>'); + if(indent >= 0) { ADD('\n'); } + + if(!single) + { + + //Content + for(register Node *child = ((Element*)node)->firstChild; child != 0; child = child->nextNode) + { + addString(child, data, indent >= 0 ? indent + 1 : -1); + } + + if(indent > 0) + { + memset(data, ' ', indent); + data += indent; + } + + //Ending Tag + ADD('<'); + ADD('/'); + memcpy(data, ((Element*)node)->tagName, ((Element*)node)->lenTagName); data += ((Element*)node)->lenTagName; + ADD('>'); + if(indent >= 0) { ADD('\n'); } + + } + break; + case Node::NodeText: + XML.XMLTextNode_checkEscapedContent((TextNode*)node); + if(indent >= 0) + { + memset(data, ' ', indent); + data += indent; + } + + memcpy(data, ((TextNode*)node)->escapedContent, ((TextNode*)node)->lenEscapedContent); + data += ((TextNode*)node)->lenEscapedContent; + if(indent >= 0) + { + ADD('\n'); + } + break; + case Node::Comment: + XML.XMLTextNode_checkEscapedContent((TextNode*)node); + if(indent >= 0) + { + memset(data, ' ', indent); + data += indent; + } + memcpy(data, "", 3); + data += 3; + if(indent >= 0) + { + ADD('\n'); + } + break; + case Node::CDATA: + XML.XMLTextNode_checkEscapedContent((TextNode*)node); + if(indent >= 0) + { + memset(data, ' ', indent); + data += indent; + } + memcpy(data, "content, ((CDATANode*)node)->lenContent); + data += ((CDATANode*)node)->lenContent; + memcpy(data, "]]>", 3); + data += 3; + if(indent >= 0) + { + ADD('\n'); + } + break; + default: + break; + } +} diff --git a/gb.xml/src/html/htmlserializer.h b/gb.xml/src/html/htmlserializer.h new file mode 100644 index 00000000..ffe8f250 --- /dev/null +++ b/gb.xml/src/html/htmlserializer.h @@ -0,0 +1,10 @@ +#ifndef SERIALIZER_H +#define SERIALIZER_H + +#include "htmlmain.h" + +void serializeHTMLNode(Node *node, char *&output, size_t &len, int indent = -1);//Converts the node to its string representation +void GBserializeHTMLNode(Node *node, char *&output, size_t &len, int indent = -1);//Same as above, but returns a Gambas String directly + + +#endif // SERIALIZER_H diff --git a/gb.xml/src/main.cpp b/gb.xml/src/main.cpp new file mode 100644 index 00000000..56f8fbfa --- /dev/null +++ b/gb.xml/src/main.cpp @@ -0,0 +1,139 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CNode.h" +#include "CElement.h" +#include "CTextNode.h" +#include "CDocument.h" +#include "CExplorer.h" +#include "CReader.h" + +#include "node.h" +#include "document.h" +#include "element.h" +#include "textnode.h" +#include "serializer.h" +#include "parser.h" + +#include "gambas.h" +#include "html/gb.xml.html.h" + +GB_INTERFACE GB EXPORT; +XML_HTML_INTERFACE HTML EXPORT; + +extern "C" +{ + + GB_DESC *GB_CLASSES[] EXPORT = + { + CNodeDesc, CElementDesc, CTextNodeDesc, CCommentNodeDesc, CCDATANodeDesc, CElementAttributesDesc, CElementAttributeNodeDesc, + CDocumentDesc, + CExplorerDesc, + CReaderDesc, CReaderNodeAttributesDesc, CReaderNodeDesc, CReaderNodeTypeDesc, CReaderReadFlagsDesc, 0 + }; + + void *GB_XML_1[] EXPORT = + { + (void *)XML_INTERFACE_VERSION, + (void *)serializeXMLNode, + (void *)GBserializeXMLNode, + (void *)parseXML, + (void *)GBGetXMLTextContent, + + (void *)XMLText_escapeContent, + (void *)XMLText_unEscapeContent, + (void *)XMLText_escapeAttributeContent, + + (void *)XMLNode_GetGBObject, + (void *)XMLNode_getFirstChildByAttributeValue, + (void *)XMLNode_getGBChildrenByAttributeValue, + (void *)XMLNode_firstChildElement, + (void *)XMLNode_lastChildElement, + (void *)XMLNode_previousElement, + (void *)XMLNode_appendChild, + (void *)XMLNode_getChildrenByTagName, + (void *)XMLNode_getFirstChildByTagName, + (void *)XMLNode_setTextContent, + + (void *)static_cast(XMLTextNode_New), + (void *)XMLTextNode_setEscapedTextContent, + (void *)XMLTextNode_checkEscapedContent, + + (void *) XMLDocument_New, + (void *) XMLDocument_NewFromFile, + (void *)XMLDocument_SetContent, + + (void *) static_cast(XMLElement_New), + (void *)XMLElement_SetTagName, + (void *)XMLElement_AttributeContains, + (void *) static_cast(XMLElement_AddAttribute), + (void *)XMLElement_GetAttribute, + (void *)XMLElement_SetAttribute, + (void *) static_cast(XMLElement_RemoveAttribute), + (void *) static_cast(XMLComment_New), + + (void *) static_cast(XMLCDATA_New), + + (void *)XML_ReturnNode, + + (void *)Trim, + (void *)isNameStartChar, + (void *)isNameChar, + (void *)memchrs, + (void *)GB_MatchString, + (void *)nextUTF8Char, + (void *) static_cast(isWhiteSpace), + + (void *)ThrowXMLParseException, + + #if defined(OS_MACOSX) || defined(__APPLE__) + (void*)memrchr, + #endif + + NULL + }; + + int EXPORT GB_INIT(void) + { + memset(&HTML, 0, sizeof(XML_HTML_INTERFACE)); + return -1; + } + + void EXPORT GB_EXIT() + { + + } +} + +bool CheckHtmlInterface() +{ + if((int)HTML.version == XML_HTML_INTERFACE_VERSION) + { + return true; + } + if(GB.ExistClass("HtmlDocument")) + { + GB.GetInterface("gb.xml.html", XML_HTML_INTERFACE_VERSION, &HTML); + return true; + } + return false; +} + diff --git a/gb.xml/src/main.h b/gb.xml/src/main.h new file mode 100644 index 00000000..c1825fa4 --- /dev/null +++ b/gb.xml/src/main.h @@ -0,0 +1,32 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef MAIN_H +#define MAIN_H + +#include "gb.xml.h" +#include "html/gb.xml.html.h" + +extern "C" XML_HTML_INTERFACE HTML; + +bool CheckHtmlInterface(); + +#endif // MAIN_H diff --git a/gb.xml/src/node.cpp b/gb.xml/src/node.cpp new file mode 100644 index 00000000..f53b82d2 --- /dev/null +++ b/gb.xml/src/node.cpp @@ -0,0 +1,635 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "node.h" +#include "element.h" +#include "textnode.h" +#include "parser.h" +#include "document.h" +#include "CNode.h" +#include "utils.h" + +#include +#include + +bool Node_NoInstanciate = false;//If true, newly-created Gambas objects won't instanciate a new node + +void XMLNode_Init(Node *node, Node::Type nodeType) +{ + memset(node, 0, sizeof(Node)); + node->type = nodeType; +} + +void XMLNode_Free(Node *&node)//TODO: Handle per-node type freeing +{ + if(!node) return; + if(node->userData) + { + GB.Unref(POINTER(&(node->userData))); + node->userData = 0; + } + switch(node->type) + { + case Node::ElementNode: + XMLElement_Free((Element*)node); + break; + case Node::DocumentNode: + XMLDocument_Release((Document*)node); + break; + case Node::NodeText: + case Node::CDATA: + case Node::Comment: + XMLTextNode_Free((TextNode*)node); + break; + default: + return; + break; + } + node = 0; +} + +CNode* XMLNode_GetGBObject(Node *node) +{ + if(!node->GBObject) + { + XMLNode_NewGBObject(node); + } + return node->GBObject; +} + +void XMLNode_NewGBObject(Node *node) +{ + Node_NoInstanciate = true; + switch(node->type) + { + case Node::ElementNode: + node->GBObject = (CNode*)GB.New(GB.FindClass("XmlElement"), 0, 0); + break; + case Node::CDATA: + node->GBObject = (CNode*)GB.New(GB.FindClass("XmlCDataNode"), 0, 0); + break; + case Node::Comment: + node->GBObject = (CNode*)GB.New(GB.FindClass("XmlCommentNode"), 0, 0); + break; + case Node::NodeText: + node->GBObject = (CNode*)GB.New(GB.FindClass("XmlTextNode"), 0, 0); + break; + case Node::DocumentNode: + node->GBObject = (CNode*)GB.New(GB.FindClass("XmlDocument"), 0, 0); + break; + case Node::AttributeNode: + node->GBObject = (CNode*)GB.New(GB.FindClass("XmlNode"), 0, 0); + break; + default: + fprintf(stderr, "FATAL : tried to create a Gambas object with invalid type."); + exit(EXIT_FAILURE); + break; + } + node->GBObject->node = node; + Node_NoInstanciate = false; +} + +void XMLNode_DestroyGBObject(Node *&node) +{ + if((!node->parent) && (!node->parentDocument)) + { + XMLNode_Free(node); + } + else + { + node->GBObject = 0; + } +} + +void XMLNode_DestroyParent(Node *node) +{ + if(!node->GBObject) + { + XMLNode_Free(node); + } + else + { + node->parent = 0; + node->parentDocument = 0; + } +} + +/***** Node tree *****/ + +Document* XMLNode_GetOwnerDocument(Node *node) +{ + if(node->type == Node::DocumentNode || node->type == Node::HTMLDocumentNode) { + return (Document*) node; + } + + while(node->parent && !node->parentDocument) + node = (Node*)(node->parent); + return node->parentDocument; +} + +void XMLNode_getGBChildren(Node *node, GB_ARRAY *array) +{ + GB.Array.New(array, GB.FindClass("XmlNode"), node->childCount); + if(!(SUPPORT_CHILDREN(node))) return; + int i = 0; + for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) + { + *(reinterpret_cast((GB.Array.Get(*array, i)))) = XMLNode_GetGBObject(tNode); + GB.Ref(tNode->GBObject); + ++i; + } +} + +/***** Node tree *****/ +void XMLNode_appendChild(Node *node, Node *newChild) +{ + (node->childCount)++; + if(!(node->lastChild))//No child + { + node->firstChild = newChild; + node->lastChild = newChild; + node->lastChild->previousNode = 0; + node->lastChild->nextNode = 0; + newChild->parent = node; + return; + } + + newChild->previousNode = node->lastChild; + node->lastChild->nextNode = newChild; + node->lastChild = newChild; + node->lastChild->nextNode = 0; + newChild->parent = node; + +} + +void XMLNode_prependChild(Node *node, Node *newChild) +{ + node->childCount++; + if(!node->lastChild)//No child + { + node->firstChild = newChild; + node->lastChild = node->firstChild; + node->lastChild->previousNode = 0; + node->lastChild->nextNode = 0; + newChild->parent = node; + return; + } + + newChild->nextNode = node->firstChild; + node->firstChild->previousNode = newChild; + node->firstChild = newChild; + node->firstChild->previousNode = 0; + newChild->parent = node; +} + +void XMLNode_removeKeepChild(Node *node, Node *child) +{ + if(child == node->firstChild) node->firstChild = child->nextNode; + if(child == node->lastChild) node->lastChild = child->previousNode; + if(child->nextNode) child->nextNode->previousNode = child->previousNode; + if(child->previousNode) child->previousNode->nextNode = child->nextNode; + node->childCount--; +} + +void XMLNode_removeChild(Node *node, Node *child) +{ + XMLNode_removeKeepChild(node, child); + XMLNode_DestroyParent(child); +} + +GB_VALUE *aft_args; +int aft_argsCount; + +void XMLNode_appendFromTextSubstCallback(int index, char* *str, int *len) +{ + if(index < 1 || index > aft_argsCount) return; + size_t nlen; + + XML_Format(&(aft_args[index - 1]), *str, nlen); + *len = (int)nlen; + +} + +void XMLNode_substAppendFromText(Node *node, const char *data, const size_t lenData, GB_VALUE *args, int argsCount) +{ + char *newData; + size_t lenNewData; + + aft_args = args; + aft_argsCount = argsCount; + + newData = GB.SubstString(data, lenData, XMLNode_appendFromTextSubstCallback); + lenNewData = GB.StringLength(newData); + + XMLNode_appendFromText(node, newData, lenNewData); +} + +void XMLNode_appendFromText(Node *node, const char *data, const size_t lenData) +{ + size_t nodeCount = 0; + Document *parentDoc = XMLNode_GetOwnerDocument(node); + + Node **nodes = parse(data, lenData, &nodeCount, parentDoc ? parentDoc->docType : XMLDocumentType); + for(size_t i = 0; i < nodeCount; i++) + { + XMLNode_appendChild(node, nodes[i]); + } + free(nodes); +} + +void XMLNode_addGBChildrenByTagName(Node *node, const char *compTagName, const size_t compLenTagName, GB_ARRAY *array, const int mode, const int depth) +{ + if(depth == 0 || depth == 1) return; + + for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) + { + if(tNode->type != Node::ElementNode) continue; + + if(GB_MatchString(((Element*)tNode)->tagName, ((Element*)tNode)->lenTagName, compTagName, compLenTagName, mode)) + { + *(reinterpret_cast((GB.Array.Add(*array)))) = XMLNode_GetGBObject(tNode); + GB.Ref(tNode->GBObject); + } + + XMLNode_addGBChildrenByTagName(tNode, compTagName, compLenTagName, array, mode, depth - 1); + } +} + +void XMLNode_addGBChildrenByNamespace(Node *node, const char *cnamespace, const size_t lenNamespace, GB_ARRAY *array, const int mode, const int depth) +{ + if(depth == 0 || depth == 1) return; + + for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) + { + if(tNode->type != Node::ElementNode) continue; + + if(GB_MatchString(((Element*)tNode)->tagName, ((Element*)tNode)->lenTagName, cnamespace, lenNamespace, mode)) + { + *(reinterpret_cast((GB.Array.Add(*array)))) = XMLNode_GetGBObject(tNode); + GB.Ref(tNode->GBObject); + } + + XMLNode_addGBChildrenByNamespace(tNode, cnamespace, lenNamespace, array, mode, depth - 1); + } +} + +void XMLNode_addGBAllChildren(Node *node, GB_ARRAY *array) +{ + if(SUPPORT_CHILDREN(node)) + { + for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) + { + *(reinterpret_cast((GB.Array.Add(*array)))) = XMLNode_GetGBObject(tNode); + GB.Ref(tNode->GBObject); + XMLNode_addGBAllChildren(tNode, array); + } + } + +} + +void XMLNode_getGBChildrenByTagName(Node *node, const char *ctagName, const size_t clenTagName, GB_ARRAY *array, const int mode, const int depth) +{ + GB.Array.New(array, GB.FindClass("XmlElement"), 0); + XMLNode_addGBChildrenByTagName(node, ctagName, clenTagName, array, mode, depth); +} + +void XMLNode_getGBChildrenByNamespace(Node *node, const char *cnamespace, const size_t lenNamespace, GB_ARRAY *array, const int mode, const int depth) +{ + GB.Array.New(array, GB.FindClass("XmlElement"), 0); + XMLNode_addGBChildrenByNamespace(node, cnamespace, lenNamespace, array, mode, depth); +} + +void XMLNode_getGBAllChildren(Node *node, GB_ARRAY *array) +{ + GB.Array.New(array, GB.FindClass("XmlNode"), 0); + XMLNode_addGBAllChildren(node, array); +} + +void XMLNode_getGBChildrenByAttributeValue(Node *node, const char *attrName, const size_t lenAttrName, + const char *attrValue, const size_t lenAttrValue, + GB_ARRAY *array, const int mode, const int depth) +{ + GB.Array.New(array, GB.FindClass("XmlElement"), 0); + XMLNode_addGBChildrenByAttributeValue(node, attrName, lenAttrName, attrValue, lenAttrValue, array, mode, depth); +} + +void XMLNode_addGBChildrenByAttributeValue(Node *node, const char *attrName, const size_t lenAttrName, + const char *attrValue, const size_t lenAttrValue, + GB_ARRAY *array, const int mode, const int depth) +{ + if(depth == 0 || depth == 1) return; + + for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) + { + if(tNode->type != Node::ElementNode) continue; + Attribute *attr = XMLElement_GetAttribute((Element*)tNode, attrName, lenAttrName); + if(attr) + { + if(GB_MatchString(attr->attrValue, attr->lenAttrValue, attrValue, lenAttrValue, mode)) + { + *(reinterpret_cast((GB.Array.Add(*array)))) = XMLNode_GetGBObject(tNode); + GB.Ref(tNode->GBObject); + } + } + + XMLNode_addGBChildrenByAttributeValue(tNode, attrName, lenAttrName, attrValue, lenAttrValue, array, mode, depth - 1); + } +} + +void XMLNode_getGBChildElements(Node *node, GB_ARRAY *array) +{ + GB.Array.New(array, GB.FindClass("XmlElement"), 0); + for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) + { + if(!SUPPORT_CHILDREN(tNode)) continue; + *(reinterpret_cast((GB.Array.Add(*array)))) = XMLNode_GetGBObject(tNode); + GB.Ref(tNode->GBObject); + } +} + +void XMLNode_addChildrenByTagName(Node *node, const char *compTagName, const size_t compLenTagName, Element** &array, size_t &lenArray, const int depth) +{ + if(depth == 0) return; + if(node->type == Node::ElementNode) + { + if(compLenTagName == ((Element*)node)->lenTagName) + { + if(memcmp(compTagName, ((Element*)node)->tagName, compLenTagName) == 0) + { + array = (Element**)realloc(array, sizeof(Element*) * (lenArray + 1)); + array[lenArray] = ((Element*)node); + ++lenArray; + } + } + } + if(depth == 1) return; + + if(SUPPORT_CHILDREN(node)) + { + for(Node *tNode = node->firstChild; tNode != 0; tNode = tNode->nextNode) + { + XMLNode_addChildrenByTagName(tNode, compTagName, compLenTagName, array, lenArray,depth - 1); + } + } +} + +Element** XMLNode_getChildrenByTagName(Node *node, const char *ctagName, const size_t clenTagName, size_t &lenArray, const int depth) +{ + lenArray = 0; + Element **array = 0; + XMLNode_addChildrenByTagName(node, ctagName, clenTagName, array, lenArray, depth); + return array; +} + +Element* XMLNode_getFirstChildByTagName(const Node *node, const char *ctagName, const size_t clenTagName, const int depth) +{ + if(depth == 0) return 0; + if(node->type == Node::ElementNode) + { + if(((Element*)node)->lenTagName == clenTagName) + { + if(!memcmp(((Element*)node)->tagName, ctagName, clenTagName)) return ((Element*)node); + } + } + if(depth == 1) return 0; + if(!SUPPORT_CHILDREN(node)) return 0; + Element *elmt = 0; + for(Node *it = node->firstChild; it != 0; it = it->nextNode) + { + if((it)->type == Node::ElementNode) + { + elmt = XMLNode_getFirstChildByTagName(it, ctagName, clenTagName, depth - 1); + if(elmt) return elmt; + } + } + return 0; +} + +Element* XMLNode_firstChildElement(Node *node) +{ + Node *child = node->firstChild; + while(child != 0) + { + if(child->type == Node::ElementNode) return (Element*)child; + child = child->nextNode; + } + + return 0; +} + +Element* XMLNode_lastChildElement(Node *node) +{ + Node *child = node->lastChild; + while(child != 0) + { + if(child->type == Node::ElementNode) return (Element*)child; + child = child->previousNode; + } + + return 0; +} + +Element* XMLNode_nextElement(Node *node) +{ + Node *child = node->nextNode; + while(child != 0) + { + if(child->type == Node::ElementNode) return (Element*)child; + child = child->nextNode; + } + + return 0; +} + +Element* XMLNode_previousElement(const Node *node) +{ + Node *child = node->previousNode; + while(child != 0) + { + if(child->type == Node::ElementNode) return (Element*)child; + child = child->previousNode; + } + + return 0; +} + +bool XMLNode_insertAfter(Node *node, Node *child, Node *newChild) +{ + if(child->parent != node) return false; + newChild->nextNode = child->nextNode; + newChild->previousNode = child; + if(child->nextNode) + { + child->nextNode->previousNode = newChild; + } + if(child == node->lastChild) + { + node->lastChild = newChild; + } + child->nextNode = newChild; + newChild->parent = node; + node->childCount++; + return true; +} + +bool XMLNode_insertBefore(Node *node, Node *child, Node *newChild) +{ + if(child->parent != node) return false; + newChild->nextNode = child; + newChild->previousNode = child->previousNode; + if(child->previousNode) + { + child->previousNode->nextNode = newChild; + } + if(child == node->firstChild) + { + node->firstChild = newChild; + } + child->previousNode = newChild; + newChild->parent = node; + node->childCount++; + return true; +} + +void XMLNode_replaceChild(Node *node, Node *oldChild, Node *newChild) +{ + if(XMLNode_insertBefore(node, oldChild, newChild)) + XMLNode_removeChild(node, oldChild); +} + + +void XMLNode_appendText(Node *node, const char *data, const size_t lenData) +{ + if(node->lastChild && node->lastChild->type == Node::NodeText) + { + TextNode *text = (TextNode*)node->lastChild; + text->content = (char*)realloc(text->content, lenData + text->lenContent); + memcpy(text->content + text->lenContent, data, lenData); + text->lenContent += lenData; + } + else + { + TextNode *text = XMLTextNode_New(data, lenData); + XMLNode_appendChild(node, text); + } +} + +void XMLNode_clearChildren(Node *node) +{ + if(node->childCount == 0) return; + register Node* prevChild = 0; + register Node* child = 0; + for(child = node->firstChild->nextNode; child != 0; child = child->nextNode) + { + prevChild = child->previousNode; + prevChild->nextNode = 0; + prevChild->previousNode = 0; + XMLNode_DestroyParent(prevChild); + } + node->lastChild->nextNode = 0; + node->lastChild->previousNode = 0; + XMLNode_DestroyParent(node->lastChild); + + node->childCount = 0; + node->lastChild = 0; + node->firstChild = 0; +} + +void XMLNode_setTextContent(Node *node, const char *content, const size_t lenContent) +{ + switch(node->type) + { + case Node::ElementNode: + XMLElement_SetTextContent((Element*)node, content, lenContent); + break; + case Node::AttributeNode: + XMLAttribute_SetValue((Attribute*)node, content, lenContent); + default: + return; + } +} + +GB_VARIANT* XMLNode_getUserData(Node *node, const char *key, const size_t lenkey) +{ + if(!node->userData) return 0; + GB_VARIANT *srcValue = new GB_VARIANT; + if (GB.Collection.Get(node->userData, key, lenkey, srcValue)) return 0; + return srcValue; +} + +void XMLNode_addUserData(Node *node, const char *key, const size_t lenkey, GB_VARIANT *value) +{ + if(!node->userData) + { + GB.Collection.New(POINTER(&(node->userData)), GB_COMP_BINARY); + } + + GB.Collection.Set(node->userData, key, lenkey, value); +} + + +bool XMLNode_NoInstanciate() +{ + return Node_NoInstanciate; +} + +void XML_ReturnNode(Node *node) +{ + if(!node) + { + GB.ReturnNull(); return; + } + if(!(node->GBObject)) + { + XMLNode_NewGBObject(node); + } + GB.ReturnObject(node->GBObject); +} + + +Element *XMLNode_getFirstChildByAttributeValue(Node *node, const char *attrName, const size_t lenAttrName, const char *attrValue, const size_t lenAttrValue, const int mode, const int depth) +{ + if(depth == 0) return 0; + + if(SUPPORT_CHILDREN(node)) + { + for(Node *child = node->firstChild; child != 0; child = child->nextNode) + { + if(child->type == Node::ElementNode) + { + Element *elmt; + Attribute *attr; + attr = XMLElement_GetAttribute((Element*)child, attrName, lenAttrName); + if(attr) + { + if(GB_MatchString(attr->attrValue, attr->lenAttrValue, attrValue, lenAttrValue, mode)) + { + return (Element*)child; + } + } + elmt = XMLNode_getFirstChildByAttributeValue(child, attrName, lenAttrName, attrValue, lenAttrValue, mode, depth - 1); + if(elmt) return elmt; + } + } + } + + return 0; +} diff --git a/gb.xml/src/node.h b/gb.xml/src/node.h new file mode 100644 index 00000000..0a0406bd --- /dev/null +++ b/gb.xml/src/node.h @@ -0,0 +1,98 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef NODE_H +#define NODE_H + +#include "gb.xml.h" + +#define SUPPORT_CHILDREN(__node) ((__node->type == Node::ElementNode) || (__node->type == Node::DocumentNode)) + +class Element; +class TextNode; +class Document; + +struct CNode; + +void XMLNode_Init(Node* node, Node::Type nodeType); +void XMLNode_Free(Node* &node); + +void XMLNode_DestroyGBObject(Node* &node); +void XMLNode_DestroyParent(Node *node); + +void XMLNode_NewGBObject(Node *node); +CNode* XMLNode_GetGBObject(Node *node); + +//Node tree +void XMLNode_getGBChildren(Node *node, GB_ARRAY *array); +Document* XMLNode_GetOwnerDocument(Node *node); +void XMLNode_appendChild(Node *node, Node *newChild); +void XMLNode_prependChild(Node *node, Node *newChild); +void XMLNode_removeChild(Node *node, Node *child); +void XMLNode_removeKeepChild(Node *node, Node *child); +void XMLNode_replaceChild(Node *node, Node *oldChild, Node *newChild); +bool XMLNode_insertAfter(Node *node, Node *child, Node *newChild); +bool XMLNode_insertBefore(Node *node, Node *child, Node *newChild); +void XMLNode_appendText(Node *node, const char *data, const size_t lenData); +void XMLNode_clearChildren(Node *node); +void XMLNode_appendFromText(Node *node, const char *data, const size_t lenData); + +//Searching elements +void XMLNode_getGBChildrenByNamespace(Node *node, const char *cnamespace, const size_t lenNamespace, GB_ARRAY *array, const int mode = GB_STRCOMP_BINARY, const int depth = -1); +void XMLNode_addGBChildrenByNamespace(Node *node, const char *cnamespace, const size_t lenNamespace, GB_ARRAY *array, const int mode = GB_STRCOMP_BINARY, const int depth = -1); +void XMLNode_getGBChildrenByTagName(Node *node, const char *ctagName, const size_t clenTagName, GB_ARRAY *array, const int mode = GB_STRCOMP_BINARY, const int depth = -1); +void XMLNode_addGBChildrenByTagName(Node *node, const char *compTagName, const size_t compLenTagName, GB_ARRAY *array, const int mode = GB_STRCOMP_BINARY, const int depth = -1); +void XMLNode_getGBChildrenByAttributeValue(Node *node, const char *attrName, const size_t lenAttrName, + const char *attrValue, const size_t lenAttrValue, + GB_ARRAY *array, const int mode = GB_STRCOMP_BINARY, const int depth = -1); +void XMLNode_addGBChildrenByAttributeValue(Node *node, const char *attrName, const size_t lenAttrName, + const char *attrValue, const size_t lenAttrValue, + GB_ARRAY *array, const int mode = GB_STRCOMP_BINARY, const int depth = -1); +Element* XMLNode_getFirstChildByAttributeValue(Node *node, const char *attrName, const size_t lenAttrName, + const char *attrValue, const size_t lenAttrValue, const int mode = GB_STRCOMP_BINARY, const int depth = -1); +Element** XMLNode_getChildrenByTagName(Node *node, const char *ctagName, const size_t clenTagName, size_t &lenArray, const int depth = -1); +Element* XMLNode_getFirstChildByTagName(const Node *node, const char *ctagName, const size_t clenTagName, const int depth = -1); +void XMLNode_addChildrenByTagName(Node *node, const char *compTagName, const size_t compLenTagName, Element** &array, size_t &lenArray, const int depth = -1); +void XMLNode_getGBAllChildren(Node *node, GB_ARRAY *array); +void XMLNode_addGBAllChildren(Node *node, GB_ARRAY *array); +void XMLNode_getGBChildElements(Node *node, GB_ARRAY *array); + +Element* XMLNode_firstChildElement(Node *node); +Element* XMLNode_lastChildElement(Node *node); +Element* XMLNode_previousElement(const Node *node); +Element* XMLNode_nextElement(Node *node); + +void XMLNode_setTextContent(Node *node, const char *content, const size_t lenContent);//Sets the plain text conent of a node + + + +bool XMLNode_NoInstanciate(); + +void XML_ReturnNode(Node *node); + +#endif // NODE_H + +#if !defined(NODE_GBINTERFACE) && defined(GBINTERFACE_H) +#define NODE_GBINTERFACE +void XMLNode_substAppendFromText(Node *node, const char *data, const size_t lenData, GB_VALUE *args, int argsCount); +GB_VARIANT *XMLNode_getUserData(Node *node, const char *key, const size_t lenkey); +void XMLNode_addUserData(Node *node, const char *key, const size_t lenkey, GB_VARIANT *value); +#endif diff --git a/gb.xml/src/parser.cpp b/gb.xml/src/parser.cpp new file mode 100644 index 00000000..a020adf8 --- /dev/null +++ b/gb.xml/src/parser.cpp @@ -0,0 +1,341 @@ +#include "parser.h" + +#include "node.h" +#include "element.h" +#include "textnode.h" +#include "gbinterface.h" +#include + + +/***** Parser *****/ + +void GBparse(const char *data, const size_t lendata, GB_ARRAY *array, DocumentType docType) +{ + if(docType == HTMLDocumentType || docType == XHTMLDocumentType) + { + if(CheckHtmlInterface()) + { + HTML.GBparseHTML(data, lendata, array); + return; + } + } + + GBparseXML(data, lendata, array); +} + +Node** parse(char const *data, const size_t lendata, size_t *nodeCount, DocumentType docType) +{ + if(docType == HTMLDocumentType || docType == XHTMLDocumentType) + { + if(CheckHtmlInterface()) + { + return HTML.parseHTML(data, lendata, nodeCount); + } + } + + return parseXML(data, lendata, nodeCount); +} + +void GBparseXML(const char *data, const size_t lendata, GB_ARRAY *array) +{ + size_t nodeCount; + size_t i = 0; + Node **nodes = parseXML(data, lendata, &nodeCount); + GB.Array.New(array, GB.FindClass("XmlNode"), nodeCount); + + for(i = 0; i < nodeCount; ++i) + { + *(reinterpret_cast((GB.Array.Get(*array, i)))) = XMLNode_GetGBObject(nodes[i]); + GB.Ref(nodes[i]->GBObject); + } + + free(nodes); +} + +//Ajoute 'elmt' à la liste +#define APPEND(_elmt) if(curElement == 0)\ +{\ + (*nodeCount)++;\ + elements = (Node**)realloc(elements, sizeof(Node*) * (*nodeCount));\ + elements[(*nodeCount) - 1] = _elmt;\ +}\ +else \ +{\ + XMLNode_appendChild(curElement, _elmt); \ +} + +void parser_cleanup(Node **elements, size_t *nodeCount) +{ + for(size_t i = *nodeCount; i --> 0;) + { + XMLNode_Free(elements[i]); + } + free(elements); +} + +#define THROW(_ex) parser_cleanup(elements, nodeCount); throw(_ex) + +Node** parseXML(char const *data, const size_t lendata, size_t *nodeCount) +{ + *nodeCount = 0; + if(!lendata || !data) return 0; //Empty ? + + const char *endData = data + lendata; + + Node **elements = 0;//Elements to return + Element *curElement = 0;//Current element + + + register char s = 0;//Current byte (value) + register char const *pos = data;//Current byte (position) + register wchar_t ws = 0;//Current character (value) + + char *tag = 0;//First '<' character found + + while(pos < endData)//Start + { + tag = (char*)memchr(pos, '<', endData - pos);//On cherche un début de tag + if(tag && (tag - pos) != 0)//On ajoute le texte, s'il existe + { + //Checking length + char const *textpos = pos; + size_t textlen = tag - pos; + //Trim(textpos, textlen); + if(textlen != 0) + { + TextNode *text = XMLTextNode_New(); + XMLTextNode_setEscapedTextContent(text, textpos, textlen); + APPEND(text); + } + } + + if(!tag) + { + if(pos < endData)//Il reste du texte + { + //Checking length + char const *textpos = pos; + size_t textlen = endData - pos; + //Trim(textpos, textlen); + if(textlen != 0) + { + TextNode *text = XMLTextNode_New(); + XMLTextNode_setEscapedTextContent(text, textpos, textlen); + APPEND(text); + } + } + break; + } + + tag++; + pos = tag;//On avance au caractère trouvé + + //On analyse le contenu du tag + ws = nextUTF8Char(pos, endData - pos);//On prend le premier caractère + + if(!isNameStartChar(ws))//Ce n'est pas un tagName, il y a quelque chose ... + { + if(ws == '/')//C'est un élément de fin + { + if(!curElement)//Pas d'élément courant + { + //ERREUR : CLOSING TAG WHEREAS NONE IS OPEN + THROW(XMLParseException_New("Closing tag whereas none is open", + data, lendata, pos - 1)); + + } + if((endData) < pos + curElement->lenTagName)//Impossible que les tags correspondent + { + //ERREUR : TAG MISMATCH + THROW(XMLParseException_New("Tag mismatch", + data, lendata, pos - 1)); + } + //Les tags ne correspondent pas + else if(memcmp(pos, curElement->tagName, curElement->lenTagName) != 0) + { + //ERREUR : TAG MISMATCH + THROW(XMLParseException_New("Tag mismatch", + data, lendata, pos - 1)); + } + else//Les tags correspondent, on remonte + { + pos += curElement->lenTagName; + curElement = (Element*)(curElement->parent); + tag = (char*)memchr(pos, '>', endData - pos);//On cherche la fin du tag + if (!tag) + { + THROW(XMLParseException_New("Never-ending tag", data, lendata, pos - 1)); + } + pos = tag + 1;//On avance à la fin du tag + + continue; + } + } + else if(ws == '!')//Ce serait un commentaire ou un CDATA + { + if(memcmp(pos, "--", 2) == 0)//C'est bien un commentaire + { + pos += 2;//On va au début du contenu du commentaire + tag = (char*)memchrs(pos, endData - pos, "-->", 3); + if(!tag)//Commentaire sans fin + { + //ERREUR : NEVER-ENDING COMMENT + THROW(XMLParseException_New("Never-ending comment", + data, lendata, pos - 1)); + } + + CommentNode *comment = XMLComment_New(); + XMLTextNode_setEscapedTextContent(comment, pos, tag - pos); + APPEND(comment); + pos = tag + 3; + continue; + } + else if(memcmp(pos, "[CDATA[", 7) == 0)//C'est un CDATA + { + pos += 7;//On va au début du contenu du cdata + tag = (char*)memchrs(pos, endData - pos, "]]>", 3); + if(!tag)//Cdata sans fin + { + //ERREUR : UNENDED CDATA + THROW(XMLParseException_New("Never-ending CDATA", + data, lendata, pos - 1)); + } + + CDATANode *cdata = XMLCDATA_New(); + XMLTextNode_setEscapedTextContent(cdata, pos, tag - pos); + APPEND(cdata); + pos = tag + 3; + continue; + } + else if(memcmp(pos, "DOCTYPE", 7) == 0)//Doctypes are silently ignored for the moment + { + pos += 7; + tag = (char*)memchr(pos, '>', endData - pos); + if(!tag)//Doctype sans fin + { + THROW(XMLParseException_New("Never-ending DOCTYPE", + data, lendata, pos - 1)); + } + + pos = tag + 1; + continue; + } + else// ... ? + { + //ERREUR : INVALID TAG + THROW(XMLParseException_New("Invalid Tag", + data, lendata, pos - 1)); + } + } + else if(ws == '?')//Processing Instruction //TODO : add the PI API + { + tag = (char*)memchrs(pos, endData - pos, "?>", 2);//Looking for the end of the PI + if(!tag)//Endless PI + { + THROW(XMLParseException_New("Never-ending Processing instruction", + data, lendata, pos - 1)); + } + + pos = tag + 2; + continue; + + } + else// ... ? + { + //ERREUR : INVALID TAG + THROW(XMLParseException_New("Invalid Tag", + data, lendata, pos - 1)); + } + }//Si tout va bien, on a un nouvel élément + else + { + while(isNameChar(nextUTF8Char(pos, endData - pos)))//On cherche le tagName + { + if(pos > endData) + { + //ERREUR : NEVER-ENDING TAG + THROW(XMLParseException_New("Never-ending tag", + data, lendata, pos - 1)); + } + } + pos--; + + Element *elmt = XMLElement_New(tag, pos - tag); + APPEND(elmt); + curElement = elmt; + s = *pos; + + while(pos < endData)//On gère le contenu de l'élément (attributs) + { + if(s == '>') break;//Fin de l'élément + if(s == '/') //Élément auto-fermant + { + pos++; + curElement = (Element*)(curElement->parent);//Pas d'enfants, on remonte + break; + } + + if(isNameStartChar(s))//Début d'attribut + { + const char *attrNamestart = pos; + while(isNameChar(nextUTF8Char(pos, endData - pos)) && pos < endData){}//On parcourt le nom d'attribut + pos--; + const char *attrNameEnd = pos; + s = *pos; + while(isWhiteSpace(s) && pos < endData){pos++; s = *pos;}//On ignore les espaces blancs + + if(s != '=') + { + XMLElement_AddAttribute(elmt, attrNamestart, attrNameEnd - attrNamestart); + if(s == '>') break;//Fin de l'élément + else if (s == '/')//Élément auto-fermant + { + pos++; + curElement = (Element*)(curElement->parent);//Pas d'enfants, on remonte + break; + } + else + { + //ERREUR : INVALID TAG + THROW(XMLParseException_New("Invalid tag", + data, lendata, pos - 1)); + } + } + + pos++; s = *pos; + + while(isWhiteSpace(s) && pos < endData){pos++; s = *pos;}//On ignore les espaces blancs + + char delimiter = s; + if(delimiter != '"' && delimiter != '\'') + { + //ERREUR : EXPECTED ATTRIBUTE DELIMITER + THROW(XMLParseException_New("Expected attribute delimiter", + data, lendata, pos - 1)); + } + pos++; + + char* delimiterPos = (char*)memchr(pos, delimiter, endData - pos); + + if(!delimiterPos) + { + THROW(XMLParseException_New("Never-ending attribute value", + data, lendata, pos - 1)); + } + + XMLElement_AddAttribute(elmt, attrNamestart, attrNameEnd - attrNamestart, + pos, delimiterPos - pos); + pos = delimiterPos; + + } + + pos++; s = *pos; + } + + } + pos++; + } + + return elements; + +} diff --git a/gb.xml/src/parser.h b/gb.xml/src/parser.h new file mode 100644 index 00000000..da95c25a --- /dev/null +++ b/gb.xml/src/parser.h @@ -0,0 +1,14 @@ +#ifndef PARSER_H +#define PARSER_H + +#include "main.h" +#include "utils.h" + +void GBparse(const char *data, const size_t lendata, GB_ARRAY *array, DocumentType docType); +Node** parse(char const *data, const size_t lendata, size_t *nodeCount, DocumentType docType); + +void GBparseXML(char const *data, const size_t lendata, GB_ARRAY *array); +Node** parseXML(char const *data, const size_t lendata, size_t *nodeCount); + + +#endif // PARSER_H diff --git a/gb.xml/src/reader.cpp b/gb.xml/src/reader.cpp new file mode 100644 index 00000000..87d2e006 --- /dev/null +++ b/gb.xml/src/reader.cpp @@ -0,0 +1,499 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "reader.h" + +#include "node.h" +#include "utils.h" +#include "element.h" +#include "document.h" +#include "textnode.h" + +#include +#include + +#define DELETE(_ob) if(_ob) {delete _ob; _ob = 0;} +#define FREE(_ob) if(_ob) {free(_ob); _ob = 0;} +#define UNREF(_ob) if(_ob) GB.Unref(POINTER(&(_ob))) +#define DESTROYPARENT(_ob) if(_ob) {XMLNode_DestroyParent(_ob); _ob = 0;} + +void Reader::ClearReader() +{ + //UNREF(foundNode); + //UNREF(curNode); + this->keepMemory = false; + this->pos = 0; + this->depth = -1; + this->inTag = false; + this->inTagName = false; + this->inAttr = false; + this->inAttrName = false; + this->inAttrVal = false; + this->inNewTag = false; + this->specialTagLevel = 0; + this->inEndTag = false; + this->inXMLProlog = false; + this->inCommentTag = false; + this->inCDATATag = false; + this->inComment = false; + this->inCDATA = false; + this->waitClosingElmt = false; + this->specialTagLevel = 0; + this->state = 0; + + if(curNode != foundNode) + { + DESTROYPARENT(curNode); + } + else + { + curNode = 0; + } + DESTROYPARENT(foundNode); + curElmt = 0; + storedDocument = 0; + FREE(attrName); + lenAttrName = 0; + FREE(attrVal); + lenAttrVal = 0; + FREE(content); + lenContent = 0; + + if(storedElements) + { + + /*for(vector::iterator it = storedElements->begin(); it != storedElements->end(); ++it) + { + GB.Unref(POINTER(&(*it))); + } + this->storedElements->clear();*/ + } + + curAttrEnum = 0; + +} + +void Reader::InitReader() +{ + attrName = 0; + attrVal = 0; + content = 0; + storedDocument = 0; + storedElements = 0; + curNode = 0; + foundNode = 0; + + ClearReader(); + + this->flags[NODE_ELEMENT] = true; + this->flags[NODE_TEXT] = true; + this->flags[NODE_COMMENT] = true; + this->flags[NODE_CDATA] = true; + this->flags[NODE_ATTRIBUTE] = false; + this->flags[READ_ATTRIBUTE] = false; + + this->flags[READ_END_CUR_ELEMENT] = true; + this->flags[READ_ERR_EOF] = true; + FREE(storedElements); + +} + +void Reader::DestroyReader() +{ + ClearReader(); +} + +int Reader::ReadChar(char car) +{ + #define APPEND(elmt) if(curElmt == 0){}\ + else {XMLNode_appendChild(curElmt, elmt);} + + ++(this->pos); + + if(waitClosingElmt) + { + if(car != '>') return 0; + waitClosingElmt = false; + depth--; +// this->state = READ_END_CUR_ELEMENT; + return 0; + } + + if(car == '<' && !inComment && !inCDATA)//Début de tag + { + if(inTag)//Si on est déjà dans un tag + { + throw XMLParseException_New("Invalid tag Name", pos); + } + inNewTag = true; + inTagName = true; + if(curNode && curNode->type == Node::NodeText) //Si il y avait du texte avant + { + DESTROYPARENT(foundNode); + foundNode = curNode; + if(keepMemory) + { + APPEND(foundNode); + } + //const char *trimmedText = curNode->toTextNode()->content; + //size_t lenTrimmedText = curNode->toTextNode()->lenContent; + + //Trim(trimmedText, lenTrimmedText); + + XMLTextNode_TrimContent((TextNode*)curNode); + + curNode = 0; + this->state = NODE_TEXT; + return NODE_TEXT; + } + } + else if(car == '>' && inTag && !inEndTag && !inComment && !inCDATA)//Fin de tag (de nouvel élément) + { + DESTROYPARENT(foundNode); + //UNREF(foundNode); + foundNode = curNode;//On a trouvé un élément complet + //curNode = 0; + //GB.Ref(foundNode); + inTag = false; + depth++; + if(keepMemory) + { + APPEND(foundNode); + curElmt = ((Element*)foundNode); + } + if(attrName && attrVal) + { + XMLElement_AddAttribute(((Element*)curNode), attrName, lenAttrName, + attrVal, lenAttrVal); + FREE(attrName); lenAttrName = 0; inAttrName = false; inAttr = false; + FREE(attrVal); lenAttrVal = 0; inAttrVal = false; + } + else if(attrName) + { + XMLElement_AddAttribute(((Element*)curNode), attrName, lenAttrName, "", 0); + FREE(attrName); lenAttrName = 0; inAttrName = false; inAttr = false; + } + this->state = NODE_ELEMENT; + return NODE_ELEMENT; + } + else if(isWhiteSpace(car) && inTag && inTagName && !inComment && !inCDATA)// Fin de tagName + { + inTagName = false; + XMLElement_RefreshPrefix((Element*)curNode); + } + else if(isNameStartChar(car) && inTag && !inTagName && !inEndTag && !inAttrVal && !inAttrName && !inComment && !inCDATA)//Début de nom d'attribut + { + if(attrName && attrVal) + { + XMLElement_AddAttribute(((Element*)curNode), attrName, lenAttrName, + attrVal, lenAttrVal); + FREE(attrName); lenAttrName = 0; inAttrName = false; inAttr = false; + FREE(attrVal); lenAttrVal = 0; inAttrVal = false; + } + else if(attrName) + { + XMLElement_AddAttribute(((Element*)curNode), attrName, lenAttrName, "", 0); + FREE(attrName); lenAttrName = 0; inAttrName = false; inAttr = false; + } + inAttr = true; + inAttrName = true; + attrName = (char*)malloc(1); + *attrName = car; + lenAttrName = 1; + } + else if(car == '=' && inAttrName && !inComment && !inCDATA)//Fin du nom d'attribut + { + inAttrName = false; + } + else if((car == '\'' || car == '"') && inAttr && !inAttrVal && !inComment && !inCDATA)//Début de valeur d'attribut + { + inAttrVal = true; + attrVal = 0; + } + else if((car == '\'' || car == '"') && inAttr && inAttrVal && !inComment && !inCDATA)//Fin de valeur d'attribut + { + XMLElement_AddAttribute(((Element*)curNode), attrName, lenAttrName, + attrVal, lenAttrVal); + FREE(attrName); lenAttrName = 0; + FREE(attrVal); lenAttrVal = 0; + inAttr = false; + inAttrVal = false; + this->state = READ_ATTRIBUTE; + return READ_ATTRIBUTE; + } + else if(car == '/' && inTag && !inAttrVal && !inComment && !inCDATA)//Self-closed element + { + inTag = false; + inTagName = false; + inEndTag = false; + if(curElmt) curElmt = (Element*)(curElmt->parent); + FREE(content); lenContent = 0; + //depth--; + waitClosingElmt = true; + DESTROYPARENT(foundNode); + foundNode = curNode; + XMLElement_RefreshPrefix((Element*)curNode); + this->state = NODE_ELEMENT; + depth++; + return NODE_ELEMENT; + } + else if(car == '/' && inNewTag && !inComment && !inCDATA)//C'est un tag de fin + { + inEndTag = true; + inNewTag = false; + inTag = true; + } + else if(car == '>' && inEndTag && !inComment && !inCDATA)//La fin d'un tag de fin + { + inTag = false; + inEndTag = false; + if(curElmt && lenContent == curElmt->lenTagName) + { + if(memcmp(curElmt->tagName, content, lenContent)) + curElmt = (Element*)(curElmt->parent); + } + FREE(content); lenContent = 0; + depth--; + this->state = READ_END_CUR_ELEMENT; + return READ_END_CUR_ELEMENT; + } + else if(inEndTag)//Tag de fin + { + if(!content) + { + content = (char*)malloc(1); + content[0] = car; + lenContent = 1; + } + else + { + content = (char*)realloc(content, lenContent + 1); + content[lenContent] = car; + ++lenContent; + } + + } + else if(inNewTag && car == '!' )//Premier caractère de commentaire + { + specialTagLevel = COMMENT_TAG_STARTCHAR_1; + inCommentTag = true; + inNewTag = false; + inTag = false; + } + //Caractère de début de CDATA + else if(inCommentTag && car == '[' && specialTagLevel == COMMENT_TAG_STARTCHAR_1) + { + specialTagLevel = CDATA_TAG_STARTCHAR_2; + inCommentTag = false; + inCDATATag = true; + } + //Caractère de CDATA + else if(inCDATATag && specialTagLevel >= CDATA_TAG_STARTCHAR_2 && specialTagLevel < CDATA_TAG_STARTCHAR_8 + && (car == '[' || car == 'C' || car == 'D' || car == 'A' || car == 'T')) + { + ++specialTagLevel; + if(specialTagLevel == CDATA_TAG_STARTCHAR_8) + { + inCDATATag = false; + inCDATA = true; + curNode = XMLCDATA_New(); + } + } + //Caractère "]" de fin de CDATA + else if(curNode && curNode->type == Node::CDATA && car == ']') + { + ++specialTagLevel; + if(specialTagLevel > CDATA_TAG_ENDCHAR_2)//On est allés un peu trop loin, il y a des ] en trop + { + --specialTagLevel; + char *&textContent = ((TextNode*)curNode)->content; + size_t &lenTextContent = ((TextNode*)curNode)->lenContent; + textContent = (char*)realloc(textContent, lenTextContent + 1); + textContent[lenTextContent] = car; + ++lenTextContent; + } + } + //Fin du CDATA + else if(curNode && curNode->type == Node::CDATA && car == '>' && specialTagLevel == CDATA_TAG_ENDCHAR_2) + { + specialTagLevel = 0; + inTag = false; + DESTROYPARENT(foundNode); + //UNREF(foundNode); + foundNode = curNode; + inCDATA = false; + if(keepMemory) + { + APPEND(foundNode); + } + curNode = 0; + this->state = NODE_CDATA; + return NODE_CDATA; + } + //Caractère "-" de début de commentaire + else if(inCommentTag && car == '-' && specialTagLevel >= COMMENT_TAG_STARTCHAR_1 && specialTagLevel < COMMENT_TAG_STARTCHAR_3 && !inComment && !inCDATA) + { + ++specialTagLevel; + if (specialTagLevel == COMMENT_TAG_STARTCHAR_3)//Le tag +#define COMMENT_TAG_STARTCHAR_1 1 //Caractère ! +#define COMMENT_TAG_STARTCHAR_2 2 //Caractère - +#define COMMENT_TAG_STARTCHAR_3 3 //Caractère - (2e) +#define COMMENT_TAG_ENDCHAR_1 4 //Caractère - +#define COMMENT_TAG_ENDCHAR_2 5 //Caractère - (2e) + + +//Prologue +#define PROLOG_TAG_ENDCHAR 6//Caractère ? + +//CDATA +#define CDATA_TAG_STARTCHAR_2 7 //Catactère [ +#define CDATA_TAG_STARTCHAR_3 8 //Catactère [ (2e) +#define CDATA_TAG_STARTCHAR_4 9 //Catactère C +#define CDATA_TAG_STARTCHAR_5 10 //Catactère D +#define CDATA_TAG_STARTCHAR_6 11 //Catactère A +#define CDATA_TAG_STARTCHAR_7 12 //Catactère T +#define CDATA_TAG_STARTCHAR_8 13 //Catactère A +#define CDATA_TAG_ENDCHAR_1 14 //Catactère ] +#define CDATA_TAG_ENDCHAR_2 15 //Catactère ] (2e) + +class Document; +class Node; +class Element; +class Attribute; + +class Reader +{ +public : + + Reader(){InitReader();} + ~Reader(){ClearReader();} + + Document *storedDocument;//Document stocké + Node *curNode;//Nœud en cours de lecture + Node *foundNode;//Noœud dont on vient de finir la lecture + Element *curElmt;//Nœud en cours de lecture + bool keepMemory;//Si on doit garder toute l'arborescence en mémoire + int pos;//Position dans le flux + bool inTag;//Si on est en train de lire un tag incomplet + bool inAttr;//Si on est en train de lire un attribut + bool inAttrName;//Si on est en train de lire un nom d'attribut + bool inAttrVal;//Si on est en train de lire une valeur d'attribut + bool inEndTag;//Si on est dans un tag de fin + bool inNewTag;//Si on est dans un nouveau tag + bool inTagName;//Si on est dans le nom d'un tag + bool waitClosingElmt;//Si on attend la fermeture d'un tag (auto-fermant) + bool inCommentTag;//Si on est dans un tag commentaire + bool inCDATATag;//Si on est dans un tag de CDATA + bool inXMLProlog;//Si on est dans un prologue xml + unsigned char specialTagLevel;//Niveau de lecture d'un tag spécial + bool inComment;//Si on est dans un commentaire + bool inCDATA;//Si on est dans un CDATA + int depth;//Profondeur du nœud courant + char *attrName;//Nom de l'attribut en cours de lecture + size_t lenAttrName; + char *attrVal;//Valeur de l'attribut en cours de lecture + size_t lenAttrVal; + char *content;//Contenu du nœud texte en cours de lecture + size_t lenContent; + + char state; + + Node* *storedElements; + size_t lenStoredElements; + + void InitReader();//Intitialise le lecteur + int ReadChar(char car);//Lit un caractère + void ClearReader();//Réinitialise le lecteur + void DestroyReader();//Détruit le lecteur + bool flags[FLAGS_COUNT];//Flags de lecture + + Attribute *curAttrEnum; +}; + +#endif diff --git a/gb.xml/src/rpc/Makefile.am b/gb.xml/src/rpc/Makefile.am new file mode 100644 index 00000000..466d8101 --- /dev/null +++ b/gb.xml/src/rpc/Makefile.am @@ -0,0 +1,2 @@ +COMPONENT = gb.xml.rpc +include $(top_srcdir)/component.am diff --git a/gb.xml/src/rpc/gb.xml.rpc.component b/gb.xml/src/rpc/gb.xml.rpc.component new file mode 120000 index 00000000..1331526c --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc.component @@ -0,0 +1 @@ +gb.xml.rpc/.component \ No newline at end of file diff --git a/gb.xml/src/rpc/gb.xml.rpc/.component b/gb.xml/src/rpc/gb.xml.rpc/.component new file mode 100644 index 00000000..4e44f5a8 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.xml.rpc +Version=3.10.90 +Needs=XML diff --git a/gb.xml/src/rpc/gb.xml.rpc/.directory b/gb.xml/src/rpc/gb.xml.rpc/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/gb.xml/src/rpc/gb.xml.rpc/.icon.png b/gb.xml/src/rpc/gb.xml.rpc/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a9254f949f86671b58a744a6a93623576acc5217 GIT binary patch literal 10569 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 7 Then + Error.Raise("Invalid data type") + Return + End If + + Values.Add(Null) + dTypes.Add(dataType) + Try Me[Values.Count - 1] = Value + If Error Then + sErr = Error.Text + Values.Pop() + dTypes.Pop() + Error.Raise(sErr) + End If + +End + +Public Function Pop() As Variant + + If Values.Count = 0 Then + Error.Raise("Bad argument") + Return Null + End If + + dTypes.Pop() + Return Values.Pop() + +End + +Public Sub Push(Value As Variant, dataType As Integer) + + Add(Value, dataType) + +End + + + +Public Sub Clear() + + Values.Clear() + dTypes.Clear() + +End + +Public Function Copy() As RpcArray + + Dim hRes As New RpcArray + Dim Bucle As Integer + + For Bucle = 0 To Values.Count - 1 + hRes.Add(Values[Bucle], dTypes[Bucle]) + Next + + Return hRes + +End + + + +Public Sub Reverse() + + Values.Reverse() + dTypes.Reverse() + +End + + +Public Sub Remove(Index As Integer) + + If Index < 0 Or Index >= Values.Count Then + Error.Raise("Bad index") + Return + End If + + Values.Remove(Index) + dTypes.Remove(Index) + +End + +Public Function Datatype(Index As Integer) As Integer + + If Index < 0 Or Index >= Values.Count Then + Error.Raise("Bad index") + Return "" + End If + + Return dTypes[Index] + +End + +Public Sub _New() + + Values = New Variant[] + dTypes = New Integer[] + +End + +Public Function _Get(Index As Integer) As Variant + + If Index < 0 Or Index >= Values.Count Then + Error.Raise("Bad index") + Return Null + End If + + Return Values[Index] + +End + +Public Sub _Put(Data As Variant, Index As Integer) + + Dim pSt As RpcStruct + Dim pAr As RpcArray + + If Index < 0 Or Index >= Values.Count Then + Error.Raise("Bad index") + Return + End If + + Select Case dTypes[Index] + + Case XmlRpc.xInteger + Try Values[Index] = CInt(Data) + If Error Then + Error.Raise("Bad integer value") + Return + End If + + Case XmlRpc.xBoolean + Try Values[Index] = CBool(Data) + If Error Then + Error.Raise("Bad boolean value") + Return + End If + + Case XmlRpc.xDouble + Try Values[Index] = CFloat(Data) + If Error Then + Error.Raise("Bad float value") + Return + End If + + Case XmlRpc.xString + Try Values[Index] = CStr(Data) + If Error Then + Error.Raise("Bad string value") + Return + End If + + Case XmlRpc.xBase64 + Try Values[Index] = CStr(Data) + If Error Then + Error.Raise("Bad string value") + Return + End If + + Case XmlRpc.xDate + Try Values[Index] = CDate(Data) + If Error Then + Error.Raise("Bad date value") + Return + End If + + Case XmlRpc.xStruct + If Data = Null Then + Error.Raise("Bad RpcStruct value") + Return + End If + Try pSt = Data + If Error Then + Error.Raise("Bad RpcStruct value") + Return + End If + Values[Index] = PSt + + Case XmlRpc.xArray + If Data = Null Then + Error.Raise("Bad RpcArray value") + Return + End If + Try pAr = Data + If Error Then + Error.Raise("Bad RpcArray value") + Return + End If + Values[Index] = pAr + + End Select + +End + + + + + + + diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/RpcAtom.class b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcAtom.class new file mode 100644 index 00000000..9e3be261 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcAtom.class @@ -0,0 +1,29 @@ +'*************************************************************************** +' +' RpcAtom.class +' +' (c)2005 - Daniel Campos Fernández +' +' XML-RPC Component +' +' Realizado para la Junta de Extremadura. +' Consejería de Educación Ciencia y Tecnología. +' Proyecto gnuLinEx +' +' This program Is free software; you can redistribute it And / Or modify +' it under the terms OF the GNU General PUBLIC License AS published by +' the Free Software Foundation; either version 1, Or (at your option) +' any later version. +' +' This program Is distributed IN the hope that it will be useful, +' but WITHOUT ANY WARRANTY; WITHOUT even the implied WARRANTY OF +' MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE.See the +' GNU General PUBLIC License FOR more details. +' +' You should have received a COPY OF the GNU General PUBLIC License +' along WITH this program; IF Not, WRITE TO the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** +PUBLIC Type AS Integer +PUBLIC Data AS Variant diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/RpcClient.class b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcClient.class new file mode 100644 index 00000000..6f09df2e --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcClient.class @@ -0,0 +1,372 @@ +' Gambas class file + +'*************************************************************************** +' +' RpcClient.class +' +' (c)2005 - Daniel Campos Fernández +' +' XML-RPC Component +' +' Realizado para la Junta de Extremadura. +' Consejería de Educación Ciencia y Tecnología. +' Proyecto gnuLinEx +' +' This program Is free software; you can redistribute it And / Or modify +' it under the terms OF the GNU General PUBLIC License AS published by +' the Free Software Foundation; either version 1, Or (at your option) +' any later version. +' +' This program Is distributed IN the hope that it will be useful, +' but WITHOUT ANY WARRANTY; WITHOUT even the implied WARRANTY OF +' MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE.See the +' GNU General PUBLIC License FOR more details. +' +' You should have received a COPY OF the GNU General PUBLIC License +' along WITH this program; IF Not, WRITE TO the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** +Export + +Private Method As RpcFunction +Private hHttp As HPost +Private hMode As Integer +Private sUrl As String +Private isFault As Boolean + +Public Const offLine As Integer = 0 +Public Const httpSync As Integer = 1 +Public Const httpAsync As Integer = 2 + +Property Mode As Integer +Property URL As String +Property Read RpcMethod As RpcFunction + +Event Reply(Data As Variant) +Event BadReply(Code As String) + + +Sub URL_Write(Vl As String) + + If hHttp Then + If hHttp.Http.Status > 0 Then + Error.Raise("Still active") + Return + End If + End If + + If hHttp Then hHttp = Null + sURL = Trim(Vl) + +End + +Function URL_Read() As String + + Return sURL + +End + + + +Sub Mode_Write(Vl As Integer) + + If hHttp Then + If hHttp.Http.Status > 0 Then + Error.Raise("Still active") + Return + End If + End If + + If vl >= 0 And vl < 3 Then + hMode = vl + End If + + If vl = 0 Then hHttp = Null + +End + +Function Mode_Read() As Integer + + Return hMode + +End + +Function RpcMethod_Read() As RpcFunction + + Return Method + +End + + +Public Sub _New(remoteFunction As RpcFunction) + + hMode = RpcClient.httpSync + If remoteFunction = Null Then Error.Raise("Invalid RpcFunction object") + Method = remoteFunction + +End + +Private Function testXML(sCad As String) As Integer + + Dim Xml As New XmlReader + + Try Xml.FromString(sCad) + If Error Then Return 1 + + Do While Not Xml.Eof + Try Xml.Read() + + If Error Then Return 1 + + + If Not Xml.Eof Then + If Xml.Node.Type = XmlReaderNodeType.Element Then + Select Case Xml.Node.Name + Case "methodResponse" + Case "fault" + Case "params" + Case "param" + Case "i4" + Case "int" + Case "boolean" + Case "string" + Case "double" + Case "dateTime.iso8601" + Case "base64" + Case "struct" + Case "array" + Case "data" + Case "member" + Case "value" + Case "name" + Default + Return 2 + End Select + End If + End If + + Loop + + Return 0 + +End + +Public Function EvalReply(sCad As String) As Variant + + Dim hAtom As RpcAtom + + If hHttp Then + If hHttp.Http.Status > 0 Then + Error.Raise("Still active") + Return + End If + End If + + Try hAtom = extractReply(sCad) + If Error Then + Error.Raise(Error.Text) + Return Null + End If + + If isFault Then + '//TODO ERROR + Error.Raise("server") + Return Null + End If + + If hAtom = Null Then + If Method.ReturnType = Null Then Return Null + Error.Raise("Invalid return type, wanted VOID, got " & XmlRpc.ToString(hAtom.Type)) + End If + + If Method.ReturnType <> hAtom.Type Then + Error.Raise("Invalid return type, wanted " & XmlRpc.ToString(Method.ReturnType) & ", got " & XmlRpc.ToString(hAtom.Type)) + Return + End If + + Return hAtom.Data + +End + + +Private Function extractReply(sCad As String) As RpcAtom + + Dim Xml As New XmlReader + Dim hAtom As RpcAtom + + isFault = False + Select Case testXML(sCad) + Case 1 + Error.Raise("Invalid XML data") + Return Null + Case 2 + Error.Raise("Invalid XML-RPC format") + Return Null + End Select + + Xml.FromString(sCad) + If Tools.Find(Xml, "methodResponse") = False Then + Error.Raise("Invalid XML-RPC format") + Return Null + End If + + Do While True + If Not Xml.Eof Then Xml.Read() + If Xml.Eof Then Break + If Xml.Node.Type = XmlReaderNodeType.Element Then + + Select Case Xml.Node.Name + Case "params" + Do While True + Xml.Read() + Select Case Xml.Node.Type + Case XmlReaderNodeType.EndElement + Return Null + Case XmlReaderNodeType.Element + If Xml.Node.Name = "param" Then + Xml.Read() + hAtom = Tools.GetParam(Xml) + If hAtom = Null Then + Error.Raise("Invalid XML-RPC format") + Return Null + End If + Return hAtom + Else + Error.Raise("Invalid XML-RPC format") + Return Null + End If + End Select + + Loop + + Case "fault" + isFault = True + Default + Break + End Select + + End If + Loop + + Error.Raise("Invalid XML-RPC format") + Return Null + +End + +Public Sub hHttp_GotData(Data As String) + + Dim Dt As Variant + + If Last = Null Then + Error.Raise("Invalid method call") + Return + End If + + If Me.Mode = RpcClient.httpAsync Then + Try Dt = EvalReply(Data) + If Error Then + Raise BadReply(Error.Text) + Return + End If + Raise Reply(Dt) + + End If + +End + +Public Sub hHttp_GotError() + + If Last = Null Then + Error.Raise("Invalid method call") + Return + End If + + If Me.Mode = RpcClient.httpAsync Then Raise BadReply("Unable to contact server, or bad reply from server") + +End + + +Public Function Call(Data As Variant[]) As Variant + + Dim Xml As New XmlWriter + Dim Bucle As Integer + Dim sCad As String + Dim sData As String + + If hHttp Then + If hHttp.Http.Status > 0 Then + Error.Raise("Still active") + Return + End If + End If + + If Method = Null Then + Error.Raise("Invalid RpcFunction object") + Return + End If + + If Data = Null Then + If Method.Count <> 0 Then + Error.Raise("Wrong parameters number") + Return + End If + Else + If Method.Count <> Data.Count Then + Error.Raise("Wrong parameters number") + Return + End If + End If + + Xml.Open("") + Xml.StartElement("methodCall") + Xml.Element("methodName", Method.MethodName) + Xml.StartElement("params") + For Bucle = 0 To Method.Count - 1 + + Xml.StartElement("param") + If Not Tools.AddValue(Xml, Data[Bucle], Method[Bucle]) Then + Xml.EndDocument() + Error.Raise("Invalid parameter " & Bucle + 1 & " Type : " & XmlRpc.ToString(Method[Bucle])) + Return + End If + Xml.EndElement() + + Next + Xml.EndElement() + + If hMode = RpcClient.offline Then Return Xml.EndDocument() + + If sURL = "" Then + Error.Raise("Invalid URL") + Return + End If + + If hHttp = Null Then hHttp = New HPost(sURL) As "hHttp" + + If hMode = RpcClient.httpSync Then + hHttp.Mode = False + Else + hHttp.Mode = True + End If + + sData = Xml.EndDocument() + + If Right(sData) <> "\n" Then sData &= "\n" + + sData = Replace(sData, "\n", "\r\n") + + sCad = hHttp.PostData(sData) + + If hMode = RpcClient.httpSync Then + If sCad = "" Then + Error.Raise("Unable to contact server, or bad reply from server") + Return Null + End If + Return EvalReply(sCad) + End If + + Return Null + +End + diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/RpcFunction.class b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcFunction.class new file mode 100644 index 00000000..49fc9eb4 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcFunction.class @@ -0,0 +1,125 @@ +' Gambas class file + +'*************************************************************************** +' +' RpcFunction.class +' +' (c)2005 - Daniel Campos Fernández +' +' XML-RPC Component +' +' Realizado para la Junta de Extremadura. +' Consejería de Educación Ciencia y Tecnología. +' Proyecto gnuLinEx +' +' This program Is free software; you can redistribute it And / Or modify +' it under the terms OF the GNU General PUBLIC License AS published by +' the Free Software Foundation; either version 1, Or (at your option) +' any later version. +' +' This program Is distributed IN the hope that it will be useful, +' but WITHOUT ANY WARRANTY; WITHOUT even the implied WARRANTY OF +' MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE.See the +' GNU General PUBLIC License FOR more details. +' +' You should have received a COPY OF the GNU General PUBLIC License +' along WITH this program; IF Not, WRITE TO the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** +Export + +Private dTypes As Integer[] +Private rType As Integer +Private mName As String +Private Hlp As String + +Property Read MethodName As String +Property Read Count As Integer +Property Read ReturnType As Integer +Property Help As String + +Function Help_read() As String + + Return Hlp + +End + +Sub Help_Write(sCad As String) + + Hlp = sCad + +End + + + +Function MethodName_Read() As String + + Return mName + +End + +Function Count_Read() As Integer + + Return dTypes.Count + +End + + +Public Function _get(Index As Integer) As Integer + + Return dTypes[Index] + +End + + + +Function ReturnType_Read() As Integer + + Return rType + +End + + + +Public Sub _New(Name As String, DataTypes As Integer[], Ret As Integer) + + Dim Ok As Boolean + Dim Bucle As Integer + + Ok = True + + If DataTypes <> Null Then + + For Bucle = 0 To Datatypes.Count - 1 + + If Datatypes[Bucle] < 0 Or Datatypes[Bucle] > 7 Then + Ok = False + Break + End If + + Next + + End If + + If Ret < 0 Or Ret > 8 Then Ok = False + + If Not Ok Then + + Error.Raise("Invalid data types") + + Else + + If DataTypes <> Null Then + dTypes = DataTypes.Copy() + Else + dTypes = New Integer[] + End If + + rType = Ret + mName = Name + + End If + + +End diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/RpcServer.class b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcServer.class new file mode 100644 index 00000000..e31fdc9e --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcServer.class @@ -0,0 +1,357 @@ +' Gambas class file + +'*************************************************************************** +' +' RpcServer.class +' +' (c)2005 - Daniel Campos Fernández +' +' XML-RPC Component +' +' Realizado para la Junta de Extremadura. +' Consejería de Educación Ciencia y Tecnología. +' Proyecto gnuLinEx +' +' This program Is free software; you can redistribute it And / Or modify +' it under the terms OF the GNU General PUBLIC License AS published by +' the Free Software Foundation; either version 1, Or (at your option) +' any later version. +' +' This program Is distributed IN the hope that it will be useful, +' but WITHOUT ANY WARRANTY; WITHOUT even the implied WARRANTY OF +' MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE.See the +' GNU General PUBLIC License FOR more details. +' +' You should have received a COPY OF the GNU General PUBLIC License +' along WITH this program; IF Not, WRITE TO the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** +Export + +Private Methods As Object[] +Private miniSrv As MiniServer +Private CurrIndex As Integer + +Property Read Count As Integer +Property Read Listening As Boolean + +Event RemoteCall(Method As String, Params As Variant[]) + +Function Count_Read() As Integer + + Return Methods.Count() + +End + +Function Listening_Read() As Boolean + + If miniSrv Then Return True + Return False + +End + + +Public Sub Unregister(methodName As String) + + Dim Bucle As Integer + + If CurrIndex <> - 1 Then + Error.Raise("Unable to Unregister a function in a RemoteCall event") + Return + End If + + For Bucle = 0 To Methods.Count - 1 + + If Methods[Bucle].MethodName = MethodName Then + + Methods[Bucle] = Null + Methods.Remove(Bucle) + Return + + End If + + Next + +End + + +Public Sub Register(remoteFunction As RpcFunction) + + Dim Bucle As Integer + + If remoteFunction.MethodName = "system.listMethods" Then + Error.Raise("system.listMethods is a reserved keyword") + Return + End If + + If remoteFunction.MethodName = "system.methodSignature" Then + Error.Raise("system.methodSignature is a reserved keyword") + Return + End If + + If remoteFunction.MethodName = "system.methodHelp" Then + Error.Raise("system.methodHelp is a reserved keyword") + Return + End If + + If remoteFunction = Null Then + Error.Raise("Null function") + Return + End If + + For Bucle = 0 To Methods.Count - 1 + + If Methods[Bucle] = remoteFunction Then + Error.Raise("Function already registered") + Return + End If + + If Methods[Bucle].methodName = remoteFunction.methodName Then + Error.Raise("Function already registered") + Return + End If + + Next + + Methods.Add(remoteFunction) + + + +End + +Public Sub Listen(Port As Integer, MaxConn As Integer) + + If Port < 1 Or Port > 65535 Then + Error.Raise("Invalid port number") + Return + End If + + If MaxConn < 0 Then + Error.Raise("Invalid maximum connections number") + Return + End If + + If miniSrv Then miniSrv = Null + miniSrv = New MiniServer As "miniSrv" + Try miniSrv.Listen(Port, MaxConn) + If Error Then + miniSrv = Null + Error.Raise("Unable to listen at port " & Port) + End If + +End + +Public Sub Stop() + + If miniSrv Then + miniSrv.Close() + miniSrv = Null + End If + +End + + +Public Sub SetReply(Data As Variant) + + Dim Xml As New XmlWriter + + If CurrIndex = - 1 Then + Error.Raise("No remote function available") + Return + End If + + Xml.Open("", True) + Xml.StartElement("methodResponse") + Xml.StartElement("params") + Xml.StartElement("param") + If Not Tools.AddValue(Xml, Data, Methods[CurrIndex].ReturnType) Then + miniSrv.SetReply(tools.FaultReply(8, "internal server error")) + Error.Raise("Invalid type conversion") + Return + End If + + miniSrv.SetReply(Xml.EndDocument()) + + + + +End + + +Public Sub miniSrv_ProcessData(Data As String) + + Dim Xml As New XmlReader + Dim hPar As New Variant[] + Dim hP As RpcAtom + Dim Counter As Integer + + Xml = New XmlReader + Xml.FromString(Data) + + tools.Find(Xml, "methodCall") + Try Xml.Read() + If Not tools.Find(Xml, "params") Then + miniSrv.SetReply(tools.FaultReply(4, "params field not found")) + CurrIndex = - 1 + Return + End If + + Try Xml.Read() + + Do While True + + 'If Xml.Node.Type = XmlReaderNodeType.EndElement Then Break + If Not Tools.Find(Xml, "param") Then Break + hP = Tools.GetParam(Xml) + If hP = Null Then + miniSrv.SetReply(tools.FaultReply(5, "malformed parameters")) + CurrIndex = - 1 + Return + End If + + hPar.Add(hP.Data) + If Methods[CurrIndex].Count < hPar.Count Then + miniSrv.SetReply(tools.FaultReply(6, "too many parameters")) + CurrIndex = - 1 + Return + End If + If hP.Type <> Methods[CurrIndex][Counter] Then + miniSrv.SetReply(tools.FaultReply(7, "wrong parameter type")) + CurrIndex = - 1 + Return + End If + + Counter = Counter + 1 + + Loop + + miniSrv.SetReply(tools.FaultReply(9, "Unknown error")) + + If methods[CurrIndex].MethodName = "system.listMethods" Then + System_ListMethods() + Else If methods[CurrIndex].MethodName = "system.methodHelp" Then + System_methodHelp(hPar[0]) + Else If methods[CurrIndex].MethodName = "system.methodSignature" Then + System_methodSignature(hPar[0]) + Else + Raise RemoteCall(Methods[CurrIndex].MethodName, hPar) + End If + + CurrIndex = - 1 +End + + +Public Sub miniSrv_GotData(Data As String) + + Dim Xml As New XmlReader + Dim Bucle As Integer + + Xml = New XmlReader + Xml.FromString(Data) + + If Not tools.Find(Xml, "methodCall") Then + Stop Event + Return + End If + Try Xml.Read() + If Not tools.Find(Xml, "methodName") Then + Stop Event + Return + End If + Do While True + Try Xml.Read() + If Xml.Node.Type = XmlReaderNodetype.Element Then + Stop Event + Return + End If + If Xml.Node.Type = XmlReaderNodeType.EndElement Then + Stop Event + Return + End If + If Xml.Node.Type = XmlReaderNodeType.Text Then + + For Bucle = 0 To Methods.Count - 1 + If Methods[Bucle].methodName = Xml.Node.Value Then + CurrIndex = bucle + Return + End If + Next + + Stop Event + Return + + End If + Loop + +End + +Public Sub _New() + + Dim Rpc As RpcFunction + + CurrIndex = - 1 + Methods = New Object[] + + Rpc = New RpcFunction("system.listMethods", Null, XmlRpc.xArray) + Methods.Add(Rpc) + Rpc = New RpcFunction("system.methodHelp", [XmlRpc.xString], XmlRpc.xString) + Methods.Add(Rpc) + Rpc = New RpcFunction("system.methodSignature", [XmlRpc.xString], XmlRpc.xArray) + Methods.Add(Rpc) + + +End + +Private Sub System_ListMethods() + + Dim Arr As New RpcArray + Dim Bucle As Integer + + For Bucle = 0 To Methods.Count - 1 + Arr.Add(Methods[Bucle].MethodName, XmlRpc.xString) + Next + + SetReply(Arr) + +End + +Private Sub System_methodHelp(Data As String) + + Dim Bucle As Integer + + Data = Trim(Data) + + For Bucle = 0 To Methods.Count - 1 + If Methods[Bucle].MethodName = Data Then + SetReply(Methods[Bucle].Help) + Return + End If + Next + + miniSrv.SetReply(tools.FaultReply("2", "Unknown method")) + +End + +Private Sub System_methodSignature(Data As String) + + Dim Bucle As Integer + Dim B2 As Integer + Dim xArr As New RpcArray + + Data = Trim(Data) + + For Bucle = 0 To Methods.Count - 1 + If Methods[Bucle].MethodName = Data Then + xArr.Add(XmlRpc.ToString(Methods[Bucle].ReturnType), XmlRpc.xString) + For B2 = 0 To Methods[Bucle].Count - 1 + xArr.Add(XmlRpc.ToString(Methods[Bucle][B2]), XmlRpc.xString) + Next + SetReply(xArr) + Return + End If + Next + + miniSrv.SetReply(tools.FaultReply("2", "Unknown method")) + +End diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/RpcStruct.class b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcStruct.class new file mode 100644 index 00000000..5d72d96c --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcStruct.class @@ -0,0 +1,151 @@ +' Gambas class file + +'*************************************************************************** +' +' RpcStruct.class +' +' (c)2005 - Daniel Campos Fernández +' +' XML-RPC Component +' +' Realizado para la Junta de Extremadura. +' Consejería de Educación Ciencia y Tecnología. +' Proyecto gnuLinEx +' +' This program Is free software; you can redistribute it And / Or modify +' it under the terms OF the GNU General PUBLIC License AS published by +' the Free Software Foundation; either version 1, Or (at your option) +' any later version. +' +' This program Is distributed IN the hope that it will be useful, +' but WITHOUT ANY WARRANTY; WITHOUT even the implied WARRANTY OF +' MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE.See the +' GNU General PUBLIC License FOR more details. +' +' You should have received a COPY OF the GNU General PUBLIC License +' along WITH this program; IF Not, WRITE TO the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** +Export + +Private Names As String[] +Private Values As Variant[] +Private dTypes As Integer[] + +Property Read Count As Integer + +Function Count_Read() As Integer + + Return Names.Count + +End + + +Public Sub _New() + + Names = New String[] + Values = New Variant[] + dTypes = New Integer[] + +End + +Public Sub Add(Name As String, Value As Variant, dataType As Integer) + + If datatype < 0 Or datatype > 7 Then + Error.Raise("Invalid data type") + Return + End If + + Names.Add(Name) + Values.Add(Value) + dTypes.Add(dataType) + +End + +Public Sub Clear() + + Names.Clear() + Values.Clear() + dTypes.Clear() + +End + +Public Function Copy() As RpcStruct + + Dim hRes As New RpcStruct + Dim Bucle As Integer + + For Bucle = 0 To Names.Count - 1 + + hRes.Add(Names[Bucle], Values[Bucle], dTypes[Bucle]) + + Next + + Return hRes + +End + +Public Function Key(Index As Integer) As String + + If Index < 0 Or Index >= Names.Count Then + Error.Raise("Bad index") + Return "" + End If + + Return Names[Index] + +End + + +Public Sub Reverse() + + Names.Reverse() + Values.Reverse() + dTypes.Reverse() + +End + + +Public Sub Remove(Index As String) + + Dim Ind As Integer + + Ind = Names.Find(Index) + If Ind = - 1 Then + Error.Raise("Invalid Index") + Return + End If + + Names.Remove(Ind) + Values.Remove(Ind) + dTypes.Remove(Ind) + +End + +Public Function Datatype(Index As Integer) As Integer + + If Index < 0 Or Index >= Names.Count Then + Error.Raise("Bad index") + Return "" + End If + + Return dTypes[Index] + +End + + +Public Function Value(Index As Integer) As Variant + + If Index < 0 Or Index >= Names.Count Then + Error.Raise("Bad index") + Return "" + End If + + Return Values[Index] + +End + + + + diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/RpcType.class b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcType.class new file mode 100644 index 00000000..addeaf59 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/RpcType.class @@ -0,0 +1,101 @@ +'*************************************************************************** +' +' RpcType.class +' +' (c)2005 - Daniel Campos Fernández +' +' XML-RPC Component +' +' Realizado para la Junta de Extremadura. +' Consejería de Educación Ciencia y Tecnología. +' Proyecto gnuLinEx +' +' This program Is free software; you can redistribute it And / Or modify +' it under the terms OF the GNU General PUBLIC License AS published by +' the Free Software Foundation; either version 1, Or (at your option) +' any later version. +' +' This program Is distributed IN the hope that it will be useful, +' but WITHOUT ANY WARRANTY; WITHOUT even the implied WARRANTY OF +' MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE.See the +' GNU General PUBLIC License FOR more details. +' +' You should have received a COPY OF the GNU General PUBLIC License +' along WITH this program; IF Not, WRITE TO the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** +EXPORT + +'*********************************************** +' Data types for XML-RPC calls +'*********************************************** +STATIC PUBLIC CONST xInteger AS Integer = 0 +STATIC PUBLIC CONST xBoolean AS Integer = 1 +STATIC PUBLIC CONST xDouble AS Integer = 2 +STATIC PUBLIC CONST xString AS Integer = 3 +STATIC PUBLIC CONST xBase64 AS Integer = 4 +STATIC PUBLIC CONST xDate AS Integer = 5 +STATIC PUBLIC CONST xStruct AS Integer = 6 +STATIC PUBLIC CONST xArray AS Integer = 7 +STATIC PUBLIC CONST xVoid AS Integer = 8 + +STATIC PUBLIC FUNCTION ToType(type AS String) AS Integer + + + SELECT CASE type + + CASE "int" + RETURN RpcType.xInteger + CASE "i4" + RETURN RpcType.xInteger + CASE "boolean" + RETURN RpcType.xBoolean + CASE "double" + RETURN RpcType.xDouble + CASE "string" + RETURN RpcType.xString + CASE "base64" + RETURN RpcType.xBase64 + CASE "dateTime.iso8601" + RETURN RpcType.xDate + CASE "struct" + RETURN RpcType.xStruct + CASE "array" + RETURN RpcType.xArray + + END SELECT + + RETURN -1 + +END + +STATIC PUBLIC FUNCTION ToString(type AS Integer) AS String + + SELECT CASE type + + CASE RpcType.xInteger + RETURN "int" + CASE RpcType.xBoolean + RETURN "boolean" + CASE RpcType.xDouble + RETURN "double" + CASE RpcType.xString + RETURN "string" + CASE RpcType.xBase64 + RETURN "base64" + CASE RpcType.xDate + RETURN "dateTime.iso8601" + CASE RpcType.xStruct + RETURN "struct" + CASE RpcType.xArray + RETURN "array" + + END SELECT + + RETURN "" + +END + + + diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/Test/CXMLRPC.class b/gb.xml/src/rpc/gb.xml.rpc/.src/Test/CXMLRPC.class new file mode 100644 index 00000000..ed19c296 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/Test/CXMLRPC.class @@ -0,0 +1,94 @@ +' Gambas class file + +' CXMLRPC.class +' Built-in XML-RPC server. + +Property HTTPPort As Integer +Property MaxConn As Integer + +Private iHTTPPort As Integer +Private iMaxConn As Integer +Private bXMLRPCDebug As Boolean + +Public hXMLRPC As RpcServer + +Public Function Connect() As Boolean + + Dim hRpcFunc As RpcFunction + + hXMLRPC = New RpcServer As "hXMLRPC" + + hRpcFunc = New RpcFunction("calendar.getplanning", Null, XmlRpc.xArray) + hRpcFunc.Help = ("set global variable param1 to param2.") + hXMLRPC.Register(hRpcFunc) + + Try hXMLRPC.Listen(9009, 10) + If Not hXMLRPC.Listening Then + Return False + Endif + Return True + +End + +' shutdown our xmlrpc server +Public Sub Disconnect() + + hXMLRPC.Stop() + +End + +Public Sub hXMLRPC_RemoteCall(sName As String, sData As Variant[]) + + Dim vValue As Variant + Dim aArray As New RpcArray + Dim sParam As String + Dim iParam As Integer + + 'Main.hXMLRPC.hXMLRPC.SetReply(aArray) + + Print "Get a RemoteCall for method " & sName + + Select Case sName + Case "calendar.getplanning" + hXMLRPC.SetReply(aArray) + Default + Return + End Select + +End + +Private Function HTTPPort_Read() As Integer + + Return iHTTPPort + +End + +Private Sub HTTPPort_Write(Value As Integer) + + iHTTPPort = Value + +End + +Private Function MaxConn_Read() As Integer + + Return iMaxConn + +End + +Private Sub MaxConn_Write(Value As Integer) + + iMaxConn = Value + +End + +Private Function XMLRPCDebug_Read() As Boolean + + Return bXMLRPCDebug + +End + +Private Sub XMLRPCDebug_Write(Value As Boolean) + + bXMLRPCDebug = Value + +End diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/Test/MMain.module b/gb.xml/src/rpc/gb.xml.rpc/.src/Test/MMain.module new file mode 100644 index 00000000..179d17f0 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/Test/MMain.module @@ -0,0 +1,63 @@ +' Gambas module file + +Private Srv As RpcServer + + + + +Private Sub GetStateName(Number As Integer) + + Srv.SetReply("Alabama") + +End + +Private Sub Ping() + + Srv.SetReply(Now()) + +End + + +Public Sub Srv_RemoteCall(Name As String, Data As Variant[]) + + Select Case Name + Case "pepe" + Print Data.Count + Case "examples.ping" + Ping() + Case "examples.getStateName" + GetStateName(Data[0]) + Default + Return + End Select + +End + + +' Public Sub Main() +' +' Dim Bucle As Integer +' Dim B2 As Integer +' Dim Rpc As RpcFunction +' Dim myFunc As RpcClient +' Dim mySig As RpcClient +' Dim Arr As New RpcArray +' Dim Arr2 As RpcArray +' Dim hVar As New Variant[] +' +' +' Rpc = New RpcFunction("queryTitle", [XmlRpc.xString], XmlRpc.xArray) +' mySig = New RpcClient(Rpc) +' mySig.URL = "http://127.0.0.1:9000" +' mySig.Mode = RpcClient.httpAsync +' +' End + +Public Sub Main() + + Dim hRpcServer As RpcServer + + hRpcServer = New RpcServer As "RpcServer" + hRpcServer.Listen(9009, 1) + +End diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/Test/MTest.module b/gb.xml/src/rpc/gb.xml.rpc/.src/Test/MTest.module new file mode 100644 index 00000000..8a2e7073 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/Test/MTest.module @@ -0,0 +1,61 @@ +' Gambas module file + +' Gambas module file + +Public hXMLRPC As CXMLRPC +Public tMainSleep As Timer + +Public Function GetPlanning() + + Dim fRpc As RpcFunction + Dim cRpc As RpcClient + Dim vVar As New Variant[] + Dim bOk As Boolean + + fRpc = New RpcFunction("calendar.getplanning", Null, XmlRpc.xArray) + cRpc = New RpcClient(fRpc) + cRpc.URL = "http://127.0.0.1:9009" + + Try bOk = cRpc.Call(Null) + If Error Then Print "Error posting" + If bOk Then + Print "Successful post" + Else + Print "Error: There was a problem setting" + Endif + +End + +'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +' gets triggered by sleep timer, triggers main loop +'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Public Sub tSleep_Timer() + + ' Check_for_Action() + Wait 0.01 +End + + +Public Sub Main() + + Dim X As Integer + + ' main loop sleep timer + tMainSleep = New Timer As "tSleep" + tMainSleep.Delay = 1000 + 'tMainSleep.Enabled = True + + hXMLRPC = New CXMLRPC + + ' start xmlrpc server + If hXMLRPC.Connect() Then + Print "XML-RPC listening on port 9009" + Else + Print "XML-RPC failed to start on port 9009" + End If + + For X = 1 To 100000 Step 1 + Print "XXX=" & X + GetPlanning() + Next +End diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/Tools.module b/gb.xml/src/rpc/gb.xml.rpc/.src/Tools.module new file mode 100644 index 00000000..ec16e7f7 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/Tools.module @@ -0,0 +1,338 @@ +' Gambas module file + +Public Sub ExitNode(Xml As XmlReader) + + Do While True + + If Xml.Eof Then Return + Xml.Read() + If Xml.Node.Type = XmlReaderNodeType.Element Then ExitNode(Xml) + If Xml.Node.Type = XmlReaderNodetype.EndElement Then Return + + + Loop + +End + + +Public Function Find(Xml As XmlReader, Node As String) As Boolean + + Do While True + + If Xml.Eof Then Return False + + If Xml.Node.Type = XmlReaderNodeType.Element Then + + If Xml.Node.Name = Node Then Return True + ExitNode(Xml) + + End If + + If Not Xml.Eof Then Xml.Read() + + Loop + +End + +Public Function FindText(Xml As XmlReader) As Boolean + + Do While True + + Xml.Read() + If Xml.Node.Type = XmlReaderNodeType.Text Then Return True + If Xml.Node.Type = XmlReaderNodeType.EndElement Then Return False + If Xml.Node.Type = XmlReaderNodeType.Element Then Return False + + Loop + +End + + +Public Function GetParam(Xml As XmlReader) As RpcAtom + + Dim hAtom As New RpcAtom + Dim Name As String + Dim hDate As Date + Dim sDate As String + Dim bAtom As RpcAtom + + If Not (Xml.Node.Type = XmlReaderNodetype.Element And Xml.Node.Name = "value") Then + Try Xml.Read() + End If + + If Not Find(Xml, "value") Then Return Null + + Do While True + Try Xml.Read() + If Xml.Node.Type = XmlReaderNodeType.EndElement Then + hAtom.Data = "" + hAtom.Type = XmlRpc.xString + Return hAtom + End If + If Xml.Node.Type = XmlReaderNodeType.Text Then + hAtom.Data = Xml.Node.Value + hAtom.Type = XmlRpc.xString + ExitNode(Xml) + Return hAtom + End If + If Xml.Node.Type = XmlReaderNodeType.Element Then Break + Loop + + Select Case Xml.Node.Name + + Case "i4" + hAtom.Type = XmlRpc.xInteger + If Not FindText(Xml) Then Return Null + Try hAtom.Data = CInt(Xml.Node.Value) + If Error Then Return Null + + Case "int" + hAtom.Type = XmlRpc.xInteger + If Not FindText(Xml) Then Return Null + Try hAtom.Data = CInt(Xml.Node.Value) + If Error Then Return Null + + Case "double" + hAtom.Type = XmlRpc.xDouble + If Not FindText(Xml) Then Return Null + Try hAtom.Data = CFloat(Xml.Node.Value) + If Error Then Return Null + + Case "boolean" + hAtom.Type = XmlRpc.xBoolean + If Not FindText(Xml) Then Return Null + If Trim(Xml.Node.Value) = "0" Then + hAtom.Data = False + Else If Trim(Xml.Node.Value) = "1" Then + hAtom.Data = True + Else + Return Null + End If + + Case "string" + hAtom.Type = XmlRpc.xString + If Not FindText(Xml) Then + hAtom.Data = "" + Else + hAtom.Data = Xml.Node.Value + End If + + Case "base64" + hAtom.Type = XmlRpc.xBase64 + If Not FindText(Xml) Then Return Null + Try hAtom.Data = XmlReader.Decode(Xml.Node.Value, "base64") + If Error Then Return Null + + Case "dateTime.iso8601" + hAtom.Type = XmlRpc.xDate + If Not FindText(Xml) Then Return Null + sDate = Trim(Xml.Node.Value) + If Len(sDate) = 8 Then + Try hDate = Date(Left(sDate, 4), Mid(sDate, 5, 2), Mid(sDate, 7, 2)) + If Error Then Return Null + hAtom.Data = hDate + Else If Len(sDate) = 17 Then + Try hDate = Date(Left(sDate, 4), Mid(sDate, 5, 2), Mid(sDate, 7, 2), Mid(sDate, 10, 2), Mid(sDate, 13, 2), Mid(sDate, 16, 2)) + If Error Then Return Null + hAtom.Data = hDate + Else + Return Null + End If + + Case "array" + hAtom.Type = XmlRpc.xArray + hAtom.Data = New RpcArray + Do While True + + Xml.Read() + If Xml.Node.Type = XmlReaderNodeType.Element Then + + If Xml.Node.Name <> "data" Then Return Null + Do While True + Xml.Read() + If Xml.Node.Type = XmlReaderNodeType.Element Then + If Xml.Node.Name <> "value" Then Return Null + bAtom = Tools.GetParam(Xml) + If bAtom = Null Then Return Null + hAtom.Data.Add(bAtom.Data, bAtom.Type) + End If + + If Xml.Node.Type = XmlReaderNodeType.EndElement Then + + 'If Xml.Node.Name = "data" Then + If Xml.Eof Then + ExitNode(Xml) + Xml.Read() + Return hAtom + End If + + End If + Loop + End If + + If Xml.Node.Type = XmlReaderNodeType.EndElement Then + ExitNode(Xml) + Xml.Read() + Return hAtom + End If + + Loop + + Case "struct" + hAtom.Type = XmlRpc.xStruct + hAtom.Data = New RpcStruct + Do While True + Xml.Read() + If Xml.Node.Type = XmlReaderNodeType.Element Then + + If Xml.Node.Name <> "member" Then Return Null + bAtom = Null + Name = "" + Do While True + Xml.Read() + If Xml.Node.Type = XmlReaderNodeType.EndElement Then + If bAtom = Null Or Name = "" Then Return Null + hAtom.Data.Add(Name, bAtom.Data, bAtom.Type) + Break + End If + If Xml.Node.Type = XmlReaderNodeType.Element Then + Select Case Xml.Node.Name + Case "name" + If Not FindText(Xml) Then Return Null + Name = Xml.Node.Value + ExitNode(Xml) + Case "value" + bAtom = GetParam(Xml) + If Not bAtom Then Return Null + Default + Return Null + End Select + End If + Loop + + End If + + If Xml.Node.Type = XmlReaderNodetype.EndElement Then + + If Xml.Node.Name = "struct" Then + ExitNode(Xml) + Xml.Read() + Return hAtom + End If + + End If + + Loop + + Default + Return Null + End Select + + ExitNode(Xml) + Return hAtom + +End + +Public Function AddValue(Xml As XmlWriter, Data As Variant, Type As Integer) As Boolean + + Dim xBool As Boolean + Dim xBase64 As String + Dim xDate As Date + Dim xArr As RpcArray + Dim xStr As RpcStruct + Dim Bucle As Integer + + Xml.StartElement("value") + Select Case Type + Case XmlRpc.xInteger + Try Xml.Element("int", CInt(Data)) + If Error Then Return False + + Case XmlRpc.xBoolean + Try xBool = CBool(Data) + If Error Then Return False + If xBool Then + Xml.Element("boolean", "1") + Else + Xml.Element("boolean", "0") + End If + + Case XmlRpc.xDouble + Try Xml.Element("double", CFloat(Data)) + If Error Then Return False + + Case Xmlrpc.xString + Try Xml.Element("string", CStr(Data)) + If Error Then Return False + + Case XmlRpc.xBase64 + Try xBase64 = CStr(Data) + If Error Then Return False + Xml.StartElement("base64") + Try Xml.Base64(xBase64) + If Error Then Return False + Xml.EndElement() + + Case XmlRpc.xDate + Try xDate = CDate(Data) + If Error Then Return False + Xml.Element("dateTime.iso8601", Format(xDate, "yyyymmdd") & "T" & Format(xDate, "hh:nn:ss")) + + Case XmlRpc.xArray + Try xArr = Data + If Error Then Return False + Xml.StartElement("array") + Xml.StartElement("data") + For Bucle = 0 To xArr.Count - 1 + If Not AddValue(Xml, xArr[Bucle], xArr.Datatype(Bucle)) Then Return False + Next + Xml.EndElement() + Xml.EndElement() + + Case XmlRpc.xStruct + Try xStr = Data + If Error Then Return False + Xml.StartElement("struct") + For Bucle = 0 To xStr.Count - 1 + Xml.StartElement("member") + Xml.Element("name", xStr.Key(Bucle)) + If Not AddValue(Xml, xStr.Value(Bucle), xStr.Datatype(Bucle)) Then Return False + Xml.EndElement() + Next + Xml.EndElement() + + End Select + Xml.EndElement() + Return True + +End + + + +Public Function FaultReply(Code As Integer, Reason As String) As String + + Dim Xml As New XmlWriter + + Xml.Open("", True) + Xml.StartElement("methodResponse") + Xml.StartElement("fault") + Xml.StartElement("value") + Xml.StartElement("struct") + + Xml.StartElement("member") + Xml.Element("name", "faultCode") + Xml.StartElement("value") + Xml.Element("int", Code) + Xml.EndElement() + Xml.EndElement() + + Xml.StartElement("member") + Xml.Element("name", "faultString") + Xml.StartElement("value") + Xml.Element("string", Reason) + + Return Xml.EndDocument() + + +End + diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/XmlRpc.class b/gb.xml/src/rpc/gb.xml.rpc/.src/XmlRpc.class new file mode 100644 index 00000000..8a1eb08f --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/XmlRpc.class @@ -0,0 +1,101 @@ +'*************************************************************************** +' +' XmlRpc.class +' +' (c)2005 - Daniel Campos Fernández +' +' XML-RPC Component +' +' Realizado para la Junta de Extremadura. +' Consejería de Educación Ciencia y Tecnología. +' Proyecto gnuLinEx +' +' This program Is free software; you can redistribute it And / Or modify +' it under the terms OF the GNU General PUBLIC License AS published by +' the Free Software Foundation; either version 1, Or (at your option) +' any later version. +' +' This program Is distributed IN the hope that it will be useful, +' but WITHOUT ANY WARRANTY; WITHOUT even the implied WARRANTY OF +' MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE.See the +' GNU General PUBLIC License FOR more details. +' +' You should have received a COPY OF the GNU General PUBLIC License +' along WITH this program; IF Not, WRITE TO the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** +EXPORT + +'*********************************************** +' Data types for XML-RPC calls +'*********************************************** +STATIC PUBLIC CONST xInteger AS Integer = 0 +STATIC PUBLIC CONST xBoolean AS Integer = 1 +STATIC PUBLIC CONST xDouble AS Integer = 2 +STATIC PUBLIC CONST xString AS Integer = 3 +STATIC PUBLIC CONST xBase64 AS Integer = 4 +STATIC PUBLIC CONST xDate AS Integer = 5 +STATIC PUBLIC CONST xStruct AS Integer = 6 +STATIC PUBLIC CONST xArray AS Integer = 7 +STATIC PUBLIC CONST xVoid AS Integer = 8 + +STATIC PUBLIC FUNCTION ToType(type AS String) AS Integer + + + SELECT CASE type + + CASE "int" + RETURN XmlRpc.xInteger + CASE "i4" + RETURN XmlRpc.xInteger + CASE "boolean" + RETURN XmlRpc.xBoolean + CASE "double" + RETURN XmlRpc.xDouble + CASE "string" + RETURN XmlRpc.xString + CASE "base64" + RETURN XmlRpc.xBase64 + CASE "dateTime.iso8601" + RETURN XmlRpc.xDate + CASE "struct" + RETURN XmlRpc.xStruct + CASE "array" + RETURN XmlRpc.xArray + + END SELECT + + RETURN -1 + +END + +STATIC PUBLIC FUNCTION ToString(type AS Integer) AS String + + SELECT CASE type + + CASE XmlRpc.xInteger + RETURN "int" + CASE XmlRpc.xBoolean + RETURN "boolean" + CASE XmlRpc.xDouble + RETURN "double" + CASE XmlRpc.xString + RETURN "string" + CASE XmlRpc.xBase64 + RETURN "base64" + CASE XmlRpc.xDate + RETURN "dateTime.iso8601" + CASE XmlRpc.xStruct + RETURN "struct" + CASE XmlRpc.xArray + RETURN "array" + + END SELECT + + RETURN "" + +END + + + diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/hPost.class b/gb.xml/src/rpc/gb.xml.rpc/.src/hPost.class new file mode 100644 index 00000000..01732d92 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/hPost.class @@ -0,0 +1,94 @@ +' Gambas class file + +Public Http As HttpClient +Private Buffer As String +Public Mode As Boolean + +Event GotData(Data As String) + +Event GotError() + +Public Sub Http_Read() + + Dim sCad As String + + If Mode = False Then Return + + If Lof(Http) Then + Try Read #Http, sCad, Lof(Http) + If Not Error Then Buffer = Buffer & sCad + End If + +End + +Public Sub Http_Finished() + + Dim sCad As String + + If Mode = False Then Return + + If Lof(Http) Then + Try Read #Http, sCad, Lof(Http) + If Not Error Then Buffer = Buffer & sCad + End If + + Raise GotData(Buffer) + + +End + +Public Sub Http_Error() + + If Mode = False Then Return + + Try Close #Http + + Raise GotError() + +End + + + +Public Function PostData(Data As String) As String + + Dim sCad As String + + Buffer = "" + sCad = "" + + Http.Async = Mode + Http.Post("text/xml", Data) + + If Mode = False Then + + Do While Http.Status > 0 + + 'Wait 0.001 + Wait 0.01 + + Loop + + If Http.Status < 0 Then Return "" + If Http.Code <> 200 Then Return "" + + If Lof(Http) Then + + Read #Http, sCad, Lof(Http) + + End If + + sCad = Trim(sCad) + Return sCad + + End If + +End + + +Public Sub _new(sUrl As String) + + Http = New HttpClient As "Http" + + Http.URL = sUrl + +End diff --git a/gb.xml/src/rpc/gb.xml.rpc/.src/miniServer.class b/gb.xml/src/rpc/gb.xml.rpc/.src/miniServer.class new file mode 100644 index 00000000..a31d7d98 --- /dev/null +++ b/gb.xml/src/rpc/gb.xml.rpc/.src/miniServer.class @@ -0,0 +1,465 @@ +' Gambas class file + +'*************************************************************************** +' +' miniServer.class +' +' (c)2005 - Daniel Campos Fernández +' +' XML-RPC Component +' +' Realizado para la Junta de Extremadura. +' Consejería de Educación Ciencia y Tecnología. +' Proyecto gnuLinEx +' +' This program Is free software; you can redistribute it And / Or modify +' it under the terms OF the GNU General PUBLIC License AS published by +' the Free Software Foundation; either version 1, Or (at your option) +' any later version. +' +' This program Is distributed IN the hope that it will be useful, +' but WITHOUT ANY WARRANTY; WITHOUT even the implied WARRANTY OF +' MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE.See the +' GNU General PUBLIC License FOR more details. +' +' You should have received a COPY OF the GNU General PUBLIC License +' along WITH this program; IF Not, WRITE TO the Free Software +' Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +' +'*************************************************************************** +Private Http As ServerSocket +Private SockColl As Collection + +Private hReply As String + +Event GotData(Data As String) +Event ProcessData(Data As String) + +Private Sub hError(hS As Socket, hErr As String) + + hS.Begin + hS.EndOfLine = gb.Windows + Print #hS, "HTTP/1.1 " & hErr + Print #hS, "Server: Gambas XML-RPC Server" + Print #hS, "Connection: close" + Print #hS, "Content-Type: text/html; charset=iso-8859-1" + Print #hS, "" + Print #hS, "" + Print #hS, "" + Print #hS, "" & hErr & "" + Print #hS, "" + Print #hS, "

    Bad Request

    " + Print #hS, hErr & ".

    " + Print #hS, "


    " + Print #hS, "
    Gambas XML-RPC Server
    " + Print #hS, "" + Print #hS, "" + Print #hS, "" + + Try hS.Send + +Catch + ' Catch any errors with a closed stream + +End + + + +Private Sub RemoveSocket(hS As Socket) + + Dim cCol As Collection + + For Each cCol In SockColl + If hS = cCol["socket"] Then + Try Close #hS + SockColl.Remove(cCol["id"]) + Break + End If + + Next + +End + +Private Function FirstTest(Xml As XmlReader) As Boolean + + Do While Not Xml.Eof + + Try Xml.Read() + If Error Then Return False + If Xml.Eof Then Return True + If Xml.Node.Type = XmlReaderNodeType.Element Then + + Select Case Xml.Node.Name + + Case "methodCall" + Case "methodName" + Case "params" + Case "param" + Case "i4" + Case "int" + Case "boolean" + Case "string" + Case "double" + Case "dateTime.iso8601" + Case "base64" + Case "struct" + Case "array" + Case "data" + Case "member" + Case "value" + Case "name" + Default + Return False + + End Select + + End If + + Loop + + Return True + +End + + +Public Sub SetReply(sCad As String) + + hReply = sCad + +End + + +Private Sub ProcessQuery(hS As Socket, Buf As String) + + Dim hBuf As String[] + Dim Bucle As Integer + Dim Point As Integer + Dim Resul As String + Dim sReply As String + Dim hBol As Boolean + Dim Xml As XmlReader + + Point = - 1 + hBuf = Split(Buf, Chr(13)) + + For Bucle = 0 To hBuf.Count - 1 + hBuf[Bucle] = Replace(hBuf[Bucle], Chr(10), "") + Next + + For Bucle = 0 To hBuf.Count - 1 + If hBuf[Bucle] = "" Then + Point = Bucle + Break + End If + Next + + If Point = - 1 Then + hError(hS, "400 Bad Request") + RemoveSocket(hS) + Return + End If + + For Bucle = Point + 1 To hBuf.Count - 1 + Resul = Resul & hBuf[Bucle] + If Bucle < (hBuf.Count - 1) Then Resul = Resul & "\n" + Next + + Xml = New XmlReader + Try Xml.FromString(Trim(Resul)) + If Error Then + Xml = Null + hError(hS, "400 Bad Request") + RemoveSocket(Hs) + Return + End If + + sReply = "OK" + + If Not FirstTest(Xml) Then + sReply = Tools.FaultReply("1", "Malformed XML-RPC document") + Else + hBol = Raise GotData(Resul) + If hBol Then + sReply = Tools.FaultReply("2", "Unknown method") + Else + sReply = Tools.FaultReply("3", "Can not perform desired request") + hReply = "" + Raise ProcessData(Resul) + If hReply <> "" Then sReply = hReply + End If + End If + + hS.Begin + hS.EndOfLine = gb.Windows + Print #hS, "HTTP/1.1 200 OK" + Print #hS, "Connection: close" + Print #hS, "Content-Length: " & Len(sReply) + Print #hS, "Content-Type: text/xml" + Print #hS, "Server: Gambas RPC Server" + Print #hS, "" + Write #hS, sReply, Len(sReply) + Try hS.Send + +Finally + + RemoveSocket(hS) + +Catch + ' Catch any errors with a closed stream + +End + + +Public Sub Socket_Read() + + Dim Buf As String + Dim sCad As String + Dim Bucle As Integer + Dim hS As Socket + Dim cCount As Integer + Dim cLen As Integer + Dim cWait As Integer = 1 + Dim cCol As Collection + + For Each cCol In SockColl + If cCol["socket"] = Last Then + hS = Last + Break + Endif + Next + + If hS = Null Then Return + + If cCol["buffer"] = "" Then + If Lof(hS) >= 5 Then + + Try Read #hS, Buf, Lof(hS) + cCol["buffer"] &= Buf + If Left(cCol["buffer"], 5) <> "POST " Then + hError(hS, "405 Method Not Allowed") + RemoveSocket(hS) + Return + End If + Else + ' We expect at LEAST something like "POST /" + RemoveSocket(hS) + Return + End If + Else + Try Read #hS, Buf, Lof(hS) + cCol["buffer"] &= Buf + + ' Don't continue, because this is the second (or more) iteration to read the data + ' The first iteration will handle the complete request + Return + End If + + If cCol["protocol"] = - 1 Then + If InStr(cCol["buffer"], Chr(13)) > 0 Then + Buf = Trim(Left(cCol["buffer"], InStr(cCol["buffer"], Chr(13)) - 1)) + Buf = Right(Buf, 8) + If Buf = "HTTP/1.1" Then + cCol["protocol"] = 1 + Else If Buf = "HTTP/1.0" Then + cCol["protocol"] = 0 + Else + hError(hS, "505 HTTP Version Not Supported") + RemoveSocket(hS) + Return + End If + + Else + If Len(cCol["buffer"]) > 4096 Then + hError(hS, "413 Request Entity Too Large") + RemoveSocket(hS) + Return + End If + End If + End If + + ' It is possible the HTTP headers are send in separate TCP packets, we will wait a maximum of 1000msec + For cCount = cWait To 199 Step 1 + If hS = Null Then Return + If InStr(cCol["buffer"], Chr(13) & Chr(10) & Chr(13) & Chr(10)) Then Break + Wait 0.005 + Next + + If InStr(cCol["buffer"], Chr(13) & Chr(10) & Chr(13) & Chr(10)) Then + If cCol["type"] = 0 Then + Buf = Left(cCol["buffer"], InStr(cCol["buffer"], Chr(13) & Chr(10) & Chr(13) & Chr(10))) + + If InStr(UCase(Buf), "CONTENT-TYPE:") > 0 Then + sCad = Mid(Buf, InStr(UCase(Buf), "CONTENT-TYPE:") + 13) + sCad = Trim(Left(sCad, InStr(sCad, Chr(13)))) + If LCase(sCad) <> "text/xml" Then + hError(hS, "415 Unsupported Media Type") + RemoveSocket(hS) + Return + Else + cCol["type"] = 1 + End If + Else + hError(hS, "415 Unsupported Media Type") + RemoveSocket(hS) + Return + End If + + If InStr(UCase(Buf), "CONTENT-LENGTH:") > 0 Then + sCad = Mid(Buf, InStr(UCase(Buf), "CONTENT-LENGTH:") + 15) + sCad = Trim(Left(sCad, InStr(sCad, Chr(13)))) + Try cCol["length"] = CInt(sCad) + If Error Then + hError(hS, "411 Length Required") + RemoveSocket(hS) + Return + End If + Else + hError(hS, "411 Length Required") + RemoveSocket(hS) + Return + End If + + ' Check for "Expect: 100-continue" option & HTTP/1.1 + If cCol["protocol"] = 1 And InStr(UCase(Buf), "EXPECT: 100-CONTINUE") > 0 Then + ' Do a special check if no body is received - only then reply + If InStr(cCol["buffer"], Chr(13) & Chr(10) & Chr(13) & Chr(10)) + 3 = Len(cCol["buffer"]) Then + hS.Begin + hS.EndOfLine = gb.Windows + Print #hS, "HTTP/1.1 100 Continue" + Print #hS, "" + Try hS.Send + End If + End If + + End If + End If + + If InStr(cCol["buffer"], Chr(13) & Chr(10) & Chr(13) & Chr(10)) Then + + If cCol["length"] = - 1 Or cCol["type"] <> 1 Then + hError(hS, "406 Not Acceptable") + RemoveSocket(hS) + Return + End If + + For cCount = cWait To 200 Step 1 + If hS = Null Then Return + cLen = Len(cCol["buffer"]) - InStr(cCol["buffer"], Chr(13) & Chr(10) & Chr(13) & Chr(10)) - 3 + If cLen >= cCol["length"] Then Break + Wait 0.005 + Next + + ' HTTP/1.0 = looseless checking, some older clients generation wrong length + ' HTTP/1.1 = strict checking + If (cCol["protocol"] = 1 And cCol["length"] = cLen) Or (cCol["protocol"] = 0 And cCol["length"] <= cLen) Then + ProcessQuery(hS, cCol["buffer"]) + Return + Else + + If Len(cCol["buffer"]) > 4096 Then + hError(hS, "413 Request Entity Too Large") + RemoveSocket(hS) + Return + Else + ' If size doesn't match - we give an internal error + hError(hS, "500 Internal Server Error") + RemoveSocket(hS) + Return + End If + + End If + + Else + If Len(cCol["buffer"]) > 4096 Then + hError(hS, "413 Request Entity Too Large") + RemoveSocket(hS) + Return + End If + End If + + ' Always give a response + ' We wait long enough for the information = 1000msec + hError(hS, "408 Request Timeout") + RemoveSocket(hS) + +End + + +Public Sub Http_Connection(RemoteHost As String) + + Dim SockCollEntry As New Collection + Dim iId As Integer + + Randomize + Do + iId = Int(Rnd(1, 32768)) + Loop Until Not SockColl.Exist(iId) + + SockCollEntry.Add("", "buffer") + SockCollEntry.Add(Http.Accept(), "socket") + SockCollEntry.Add(-1, "protocol") + SockCollEntry.Add(-1, "length") + SockCollEntry.Add(0, "type") + SockCollEntry.Add(CStr(iId), "id") + + SockColl.Add(SockCollEntry, CStr(iId)) + +End + + +Public Sub Close() + + Dim cCol As Collection + + For Each cCol In SockColl + RemoveSocket(cCol["socket"]) + Next + + Try Http.Close() + Http = Null + +End + + +Public Sub Listen(Port As Integer, MaxConn As Integer) + + If Not Http Then + Http = New ServerSocket As "Http" + Else + If Http.Status <> 0 Then Http.Close() + End If + + Try Http.Port = Port + If Error Then + Error.Raise("Invalid TCP port") + Return + End If + + If MaxConn > 0 Then + Try Http.Listen(MaxConn) + Else + Try Http.Listen() + End If + + If Error Then Error.Raise("Unable to listen at port " & Port) + If Http.Status < 0 Then Error.Raise("Unable to listen at port " & Port) + + +End + + +Public Sub _New() + + SockColl = New Collection + +End + + +Public Sub _Free() + + Me.Close() + +End + + + diff --git a/gb.xml/src/serializer.cpp b/gb.xml/src/serializer.cpp new file mode 100644 index 00000000..4916f5f3 --- /dev/null +++ b/gb.xml/src/serializer.cpp @@ -0,0 +1,351 @@ +#include "serializer.h" + +#include "utils.h" +#include "node.h" +#include "textnode.h" + +#include "gbinterface.h" + +#include + +void serializeNode(Node *node, char *&output, size_t &len, int indent) +{ + Document *parentDocument = XMLNode_GetOwnerDocument(node); + if(parentDocument) + { + if(parentDocument->docType == HTMLDocumentType || parentDocument->docType == XHTMLDocumentType) + { + if(CheckHtmlInterface()) + { + HTML.serializeHTMLNode(node, output, len, indent); + return; + } + else + { + //DEBUG << "WARNING : HTML Serializer not found" << endl; + } + } + } + + serializeXMLNode(node, output, len, indent); +} + +void GBserializeNode(Node *node, char *&output, size_t &len, int indent) +{ + Document *parentDocument = XMLNode_GetOwnerDocument(node); + if(parentDocument) + { + if(parentDocument->docType == HTMLDocumentType || parentDocument->docType == XHTMLDocumentType) + { + if(CheckHtmlInterface()) + { + HTML.GBserializeHTMLNode(node, output, len, indent); + return; + } + else + { + //DEBUG << "WARNING : HTML Serializer not found" << endl; + } + } + } + + GBserializeXMLNode(node, output, len, indent); +} + + +/***** String output *****/ + +void addStringLen(Node *node, size_t &len, int indent = -1);//Calculates the node's string representation length, and adds it to len (recursive) +void addString(Node *node, char *&data, int indent = -1);//Puts the string represenetation into data, and increments it (recursive) + +void serializeXMLNode(Node *node, char *&output, size_t &len, int indent) +{ + len = 0; + addStringLen(node, len, indent); + output = (char*)malloc(sizeof(char) * (len)); + addString(node, output, indent); + output -= len; + +} + +void GBserializeXMLNode(Node *node, char *&output, size_t &len, int indent) +{ + len = 0; + addStringLen(node, len, indent); + output = GB.TempString(0, len); + addString(node, output, indent); + output -= len; +} + +void addStringLen(Node *node, size_t &len, int indent) +{ + switch (node->type) + { + case Node::DocumentNode: + len += 38 + (indent >= 0 ? 1 : 0);// root->addStringLen(len, indent); + //Content + for(register Node *child = node->firstChild; child != 0; child = child->nextNode) + { + addStringLen(child, len, indent >= 0 ? indent : -1); + } + break; + case Node::ElementNode: + // (indent) '<' + prefix:tag + (' ' + attrName + '=' + '"' + attrValue + '"') + '>' \n + // + children + (indent) '" \n + // Or, singlElement : + // (indent) '<' + prefix:tag + (' ' + attrName + '=' + '"' + attrValue + '"') + ' />' \n + /*if(((Element*)node)->isSingle()) + { + len += (4 + ((Element*)node)->lenTagName); + if(indent >= 0) len += indent + 1; + } + else + {*/ + len += (5 + ((((Element*)node)->lenTagName) * 2)); + if(indent >= 0) len += indent * 2 + 2; + for(Node *child = node->firstChild; child != 0; child = child->nextNode) + { + addStringLen(child, len, indent >= 0 ? indent + 1 : -1); + } + //} + + for(Attribute *attr = (Attribute*)(((Element*)node)->firstAttribute); attr != 0; attr = (Attribute*)(attr->nextNode)) + { + len += 4 + attr->lenAttrName + attr->lenAttrValue; + } + break; + case Node::NodeText: + + XMLTextNode_checkEscapedContent((TextNode*)node); + len += ((TextNode*)node)->lenEscapedContent; + if(indent >= 0) len += indent + 1; + break; + + case Node::Comment: + + XMLTextNode_checkEscapedContent((TextNode*)node); + // + len += ((TextNode*)node)->lenEscapedContent + 7; + if(indent >= 0) len += indent + 1; + break; + case Node::CDATA: + + XMLTextNode_checkEscapedContent((TextNode*)node); + // + len += ((TextNode*)node)->lenContent + 12; + if(indent) len += indent + 1; + break; + + default: + break; + } +} + + +#define ADD(_car) *data = _car; data++; +void addString(Node *node, char *&data, int indent) +{ + //bool single; + switch (node->type) + { + case Node::DocumentNode: + memcpy(data, "", 38); + data += 38; + if(indent >= 0) + { + *data = '\n'; + ++data; + } + //Content + for(register Node *child = node->firstChild; child != 0; child = child->nextNode) + { + addString(child, data, indent >= 0 ? indent : -1); + } + break; + case Node::ElementNode: + //register char *content = data; + //single = ((Element*)node)->isSingle(); + + //Opening tag + if(indent > 0) + { + memset(data, ' ', indent); + data += indent; + } + ADD('<'); + memcpy(data, ((Element*)node)->tagName, ((Element*)node)->lenTagName); data += ((Element*)node)->lenTagName; + + //Attributes + for(register Attribute *attr = (Attribute*)((Element*)node)->firstAttribute; attr != 0; attr = (Attribute*)(attr->nextNode)) + { + ADD(' '); + memcpy(data, attr->attrName, attr->lenAttrName); data += attr->lenAttrName; + + ADD('='); + ADD('"'); + memcpy(data, attr->attrValue, attr->lenAttrValue); data += attr->lenAttrValue; + ADD('"'); + } + + /*if(single) + { + ADD(CHAR_SPACE); + ADD(CHAR_SLASH); + }*/ + ADD('>'); + if(indent >= 0) { ADD('\n'); } + + //if(!single) + { + + //Content + for(register Node *child = ((Element*)node)->firstChild; child != 0; child = child->nextNode) + { + addString(child, data, indent >= 0 ? indent + 1 : -1); + } + + if(indent > 0) + { + memset(data, ' ', indent); + data += indent; + } + + //Ending Tag + ADD('<'); + ADD('/'); + memcpy(data, ((Element*)node)->tagName, ((Element*)node)->lenTagName); data += ((Element*)node)->lenTagName; + ADD('>'); + if(indent >= 0) { ADD('\n'); } + + } + break; + case Node::NodeText: + XMLTextNode_checkEscapedContent((TextNode*)node); + if(indent >= 0) + { + memset(data, ' ', indent); + data += indent; + } + + memcpy(data, ((TextNode*)node)->escapedContent, ((TextNode*)node)->lenEscapedContent); + data += ((TextNode*)node)->lenEscapedContent; + if(indent >= 0) + { + ADD('\n'); + } + break; + case Node::Comment: + XMLTextNode_checkEscapedContent((TextNode*)node); + if(indent >= 0) + { + memset(data, ' ', indent); + data += indent; + } + memcpy(data, "", 3); + data += 3; + if(indent >= 0) + { + ADD('\n'); + } + break; + case Node::CDATA: + XMLTextNode_checkEscapedContent((TextNode*)node); + if(indent >= 0) + { + memset(data, ' ', indent); + data += indent; + } + memcpy(data, "content, ((CDATANode*)node)->lenContent); + data += ((CDATANode*)node)->lenContent; + memcpy(data, "]]>", 3); + data += 3; + if(indent >= 0) + { + ADD('\n'); + } + break; + default: + break; + } +} + +/***** Text Content *****/ +void addTextContentLen(Node *node, size_t &len); +void addTextContent(Node *node, char *&data); + +void GetXMLTextContent(Node *node, char *&output, size_t &len) +{ + len = 0; + addTextContentLen(node, len); + output = (char*)malloc(sizeof(char) * (len)); + addTextContent(node, output); + output -= len; +} + +void GBGetXMLTextContent(Node *node, char *&output, size_t &len) +{ + len = 0; + addTextContentLen(node, len); + output = GB.TempString(0, len); + addTextContent(node, output); + output -= len; +} + +void addTextContentLen(Node *node, size_t &len) +{ + if(!node) return; + switch(node->type) + { + case Node::DocumentNode: + case Node::ElementNode: + for(Node *child = node->firstChild; child != 0; child = child->nextNode) + { + addTextContentLen(child, len); + } + break; + case Node::NodeText: + case Node::Comment: + case Node::CDATA: + XMLTextNode_checkContent((TextNode*)node); + len += ((TextNode*)node)->lenContent; + break; + case Node::AttributeNode: + len += ((Attribute*)node)->lenAttrValue; + break; + default: + break; + } +} + +void addTextContent(Node *node, char *&data) +{ + if(!node) return; + switch(node->type) + { + case Node::DocumentNode: + case Node::ElementNode: + for(Node *child = node->firstChild; child != 0; child = child->nextNode) + { + addTextContent(child, data); + } + break; + case Node::NodeText: + case Node::Comment: + case Node::CDATA: + memcpy(data, ((TextNode*)node)->content, ((TextNode*)node)->lenContent); + data += ((TextNode*)node)->lenContent; + break; + case Node::AttributeNode: + memcpy(data, ((Attribute*)node)->attrValue, ((Attribute*)node)->lenAttrValue); + data += ((Attribute*)node)->lenAttrValue; + break; + default: + break; + } +} diff --git a/gb.xml/src/serializer.h b/gb.xml/src/serializer.h new file mode 100644 index 00000000..37a52e56 --- /dev/null +++ b/gb.xml/src/serializer.h @@ -0,0 +1,18 @@ +#ifndef SERIALIZER_H +#define SERIALIZER_H + +#include "gb.xml.h" + +//Switches if HTML enabled +void serializeNode(Node *node, char *&output, size_t &len, int indent = -1); +void GBserializeNode(Node *node, char *&output, size_t &len, int indent = -1); + +//String output +void serializeXMLNode(Node *node, char *&output, size_t &len, int indent = -1);//Converts the node to its string representation +void GBserializeXMLNode(Node *node, char *&output, size_t &len, int indent = -1);//Same as above, but returns a Gambas String directly + +//Text content +void GetXMLTextContent(Node *node, char *&output, size_t &len); +void GBGetXMLTextContent(Node *node, char *&output, size_t &len); + +#endif // SERIALIZER_H diff --git a/gb.xml/src/textnode.cpp b/gb.xml/src/textnode.cpp new file mode 100644 index 00000000..5d5fa344 --- /dev/null +++ b/gb.xml/src/textnode.cpp @@ -0,0 +1,336 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "textnode.h" +#include "utils.h" +#include "node.h" + +#include +#include + +/*************************************** TextNode ***************************************/ + +TextNode* XMLTextNode_New() +{ + TextNode *newTextNode = (TextNode*)malloc(sizeof(TextNode)); + XMLNode_Init(newTextNode, Node::NodeText); + newTextNode->content = 0; + newTextNode->lenContent = 0; + newTextNode->escapedContent = 0; + newTextNode->lenEscapedContent = 0; + + return newTextNode; +} + +TextNode* XMLTextNode_New(const char *ncontent, const size_t nlen) +{ + TextNode *newTextNode = (TextNode*)malloc(sizeof(TextNode)); + XMLNode_Init(newTextNode, Node::NodeText); + newTextNode->content = 0; + newTextNode->lenContent = 0; + newTextNode->escapedContent = 0; + newTextNode->lenEscapedContent = 0; + newTextNode->lenContent = nlen; + if(!newTextNode->lenContent) + { + newTextNode->content = 0; + return newTextNode; + } + newTextNode->content = (char*)malloc(sizeof(char) * nlen + 1); + memcpy(newTextNode->content, ncontent, nlen); + newTextNode->content[nlen] = 0; + + return newTextNode; +} + +void XMLTextNode_Free(TextNode *node) +{ + if(node->escapedContent && node->escapedContent != node->content) free(node->escapedContent); + if(node->content) free(node->content); + free(node); +} + +void XMLText_escapeContent(const char *src, const size_t lenSrc, char *&dst, size_t &lenDst) +{ + dst = (char*)src; + lenDst = lenSrc; + if(!lenSrc || !src) return; + char *posFound = strpbrk (dst, "<>&\""); + while (posFound != 0) + { + if(dst == src)//dst not allocated yet + { + dst = (char*)malloc(lenSrc + 1); + lenDst = lenSrc + 1; + dst[lenSrc] = 0; + memcpy(dst, src, lenSrc); + posFound = ((posFound - src) + dst); + } + switch(*posFound) + { + case '<'://< + *posFound = '&'; + ++posFound; + insertString(dst, lenDst , "lt;", 3, posFound); + posFound = strpbrk (posFound + 1,"<>&\""); + break; + + case '>': //> + *posFound = '&'; + ++posFound; + insertString(dst, lenDst, "gt;", 3, posFound); + posFound = strpbrk (posFound + 1,"<>&\""); + break; + + + case '&': //& + + *posFound = '&'; + ++posFound; + insertString(dst, lenDst, "amp;", 4, posFound); + posFound = strpbrk (posFound + 1,"<>&\""); + break; + + case '"': //" + + *posFound = '&'; + ++posFound; + insertString(dst, lenDst, "quot;", 5, posFound); + posFound = strpbrk (posFound + 1,"<>&\""); + break; + + default: + //posFound = strpbrk (posFound + 1,"<>&"); + break; + } + + + } + + if(dst != src) --lenDst; +} + +void XMLText_escapeAttributeContent(const char *src, const size_t lenSrc, char *&dst, size_t &lenDst) +{ + dst = (char*)src; + lenDst = lenSrc; + if(!lenSrc || !src) return; + char *posFound = strpbrk (dst, "<>&\"\n"); + while (posFound != 0) + { + if(dst == src)//dst not allocated yet + { + dst = (char*)malloc(lenSrc + 1); + lenDst = lenSrc + 1; + dst[lenSrc] = 0; + memcpy(dst, src, lenSrc); + posFound = ((posFound - src) + dst); + } + switch(*posFound) + { + case '<'://< + *posFound = '&'; + ++posFound; + insertString(dst, lenDst , "lt;", 3, posFound); + posFound = strpbrk (posFound + 1,"<>&\"\n"); + break; + + case '>': //> + *posFound = '&'; + ++posFound; + insertString(dst, lenDst, "gt;", 3, posFound); + posFound = strpbrk (posFound + 1,"<>&\"\n"); + break; + + + case '&': //& + + *posFound = '&'; + ++posFound; + insertString(dst, lenDst, "amp;", 4, posFound); + posFound = strpbrk (posFound + 1,"<>&\"\n"); + break; + + case '"': //" + + *posFound = '&'; + ++posFound; + insertString(dst, lenDst, "quot;", 5, posFound); + posFound = strpbrk (posFound + 1,"<>&\"\n"); + break; + + case '\n': + *posFound = '&'; + ++posFound; + insertString(dst, lenDst, "#10;", 4, posFound); + posFound = strpbrk (posFound + 1,"<>&\"\n"); + break; + + default: + //posFound = strpbrk (posFound + 1,"<>&"); + break; + } + + + } + + if(dst != src) --lenDst; +} + +void XMLText_unEscapeContent(const char *src, const size_t lenSrc, char *&dst, size_t &lenDst) +{ + dst = (char*)malloc(lenSrc); + lenDst = lenSrc; + memcpy(dst, src, lenSrc); + char *posFound = (char*)memchr(dst, '&', lenDst); + + while(((posFound != 0) && ((posFound + 3) < lenDst + dst)))//(posFound - dst) < lenDst - 3 + { + if(memcmp(posFound + 1, "lt;", 3) == 0)// < < + { + *posFound = '<'; + //.......dst=========posFound!===posFound+x|======dst+lenDst............ + //lenCut = (pos2 - pos1) = (dst + lenDst) - (posFound + x) + memmove(posFound + 1, posFound + 4, (dst + lenDst) - (posFound + 4)); + lenDst -= 3; + posFound -= 3; + } + else if(memcmp(posFound + 1, "gt;", 3) == 0)// > > + { + *posFound = '>'; + memmove(posFound + 1, posFound + 4, (dst + lenDst) - (posFound + 4)); + lenDst -= 3; + posFound -= 3; + } + else if(((posFound + 4) < lenDst + dst) && memcmp(posFound + 1, "amp;", 4) == 0)// & & + { + memmove(posFound + 1, posFound + 5, (dst + lenDst) - (posFound + 5)); + lenDst -= 4; + posFound -= 4; + } + else if(((posFound + 5) < lenDst + dst) && memcmp(posFound + 1, "quot;", 5) == 0)// "&" " + { + *posFound = '"'; + memmove(posFound + 1, posFound + 6, (dst + lenDst) - (posFound + 6)); + lenDst -= 5; + posFound -= 5; + } + if(posFound + 1 >= dst + lenDst) break; + posFound = (char*)memchr(posFound + 1, '&', lenDst - (posFound + 1 - dst)); + //cond = ((posFound != 0) && ((posFound + 3) < lenDst + dst)); + } +} + +void XMLTextNode_checkEscapedContent(TextNode *node) +{ + if(!(node->escapedContent) && node->content) + { + XMLText_escapeContent(node->content, node->lenContent, node->escapedContent, node->lenEscapedContent); + } +} + +void XMLTextNode_checkContent(TextNode *node) +{ + if(node->escapedContent && !(node->content)) + { + XMLText_unEscapeContent(node->escapedContent, node->lenEscapedContent, node->content, node->lenContent); + } +} + +void XMLTextNode_setEscapedTextContent(TextNode *node, const char *ncontent, const size_t nlen) +{ + node->escapedContent = (char*)realloc(node->escapedContent, sizeof(char)*nlen); + node->lenEscapedContent = nlen; + memcpy(node->escapedContent, ncontent, nlen); +} + +/***** Text Content *****/ + +void XMLTextNode_TrimContent(TextNode *node) +{ + const char *oldcontent = node->content; + Trim(oldcontent, node->lenContent); + memmove(node->content, oldcontent, node->lenContent); + node->content = (char*)realloc(node->content, sizeof(char)*node->lenContent); +} + +void XMLTextNode_setTextContent(TextNode *node, const char *ncontent, const size_t nlen) +{ + node->content = (char*)realloc(node->content, sizeof(char)*nlen + 1); + node->lenContent = nlen; + memcpy(node->content, ncontent, nlen); + node->content[node->lenContent] = 0; +} + +void XMLTextNode_appendTextContent(TextNode *node, const char *content, const size_t len) +{ + size_t newLen = node->lenContent + len; + node->content = (char*)realloc(node->content, newLen); + memcpy(node->content + node->lenContent, content, len); + node->lenContent = newLen; +} + +/*************************************** Comment ***************************************/ + +CommentNode* XMLComment_New() +{ + CommentNode *newComment = XMLTextNode_New(); + newComment->type = Node::Comment; + return newComment; +} + + +CommentNode* XMLComment_New(const char *ncontent, const size_t nlen) +{ + CommentNode *newComment = XMLTextNode_New(ncontent, nlen); + newComment->type = Node::Comment; + return newComment; +} + +/*************************************** CDATA ***************************************/ + +CDATANode* XMLCDATA_New() +{ + CommentNode *newComment = XMLTextNode_New(); + newComment->type = Node::CDATA; + return newComment; +} + +CDATANode* XMLCDATA_New(const char *ncontent, const size_t nlen) +{ + CommentNode *newComment = XMLTextNode_New(ncontent, nlen); + newComment->type = Node::CDATA; + return newComment; +} + + +bool XML_isTextNode(Node *node) +{ + switch(node->type) + { + case Node::NodeText: + case Node::Comment: + case Node::CDATA: + return true; + default: + return false; + } +} diff --git a/gb.xml/src/textnode.h b/gb.xml/src/textnode.h new file mode 100644 index 00000000..cc7c33dc --- /dev/null +++ b/gb.xml/src/textnode.h @@ -0,0 +1,51 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef TEXTNODE_H +#define TEXTNODE_H + +#include "main.h" + +void XMLText_unEscapeContent(const char *src, const size_t lenSrc, char *&dst, size_t &lenDst); +void XMLText_escapeContent(const char *src, const size_t lenSrc, char *&dst, size_t &lenDst); +void XMLText_escapeAttributeContent(const char *src, const size_t lenSrc, char *&dst, size_t &lenDst); + +TextNode* XMLTextNode_New(); +TextNode* XMLTextNode_New(const char *ncontent, const size_t nlen); +void XMLTextNode_Free(TextNode *node); + +void XMLTextNode_appendTextContent(TextNode *node, const char *content, const size_t len); +void XMLTextNode_checkEscapedContent(TextNode *node); +void XMLTextNode_checkContent(TextNode *node); +void XMLTextNode_setEscapedTextContent(TextNode *node, const char *ncontent, const size_t nlen); + +void XMLTextNode_TrimContent(TextNode *node); + +bool XML_isTextNode(Node *node); + + +CommentNode* XMLComment_New(); +CommentNode* XMLComment_New(const char *ncontent, const size_t nlen); + +CDATANode* XMLCDATA_New(); +CDATANode* XMLCDATA_New(const char *ncontent, const size_t nlen); + +#endif // TEXTNODE_H diff --git a/gb.xml/src/utils.cpp b/gb.xml/src/utils.cpp new file mode 100644 index 00000000..141789ef --- /dev/null +++ b/gb.xml/src/utils.cpp @@ -0,0 +1,372 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "utils.h" +#include "gbinterface.h" +#include "gb_common.h" +#include "textnode.h" + +#include +#include +#include + +#if defined(OS_MACOSX) +void *memrchr(const char *s, int c, size_t n) +{ + const char *start=s,*end=(s+n-1); + + while(end>=start) + { + if(*end==c) + return (void *)end; + else + end--; + } + + return NULL; +} +#endif + +wchar_t nextUTF8Char(const char *&data, size_t len) +{ + register unsigned char c = *data; + if (c <= 0x7f){//first byte + data++; + return (wchar_t)c; + } + else if (c <= 0xdf && c >= 0xbf && 1 < len){//2byte sequence start + register wchar_t c2 = (unsigned char)data[1]; + data += 2; + return (((c & 0x1f) << 6) | (c2 & 0x3f)); + } + else if (c <= 0xef && c >= 0xbf && 2 < len){//3byte sequence start + register wchar_t c2 = (unsigned char)data[1]; + register wchar_t c3 = (unsigned char)data[2]; + data += 3; + return (((((c & 0x1f) << 6) | (c2 & 0x3f)) << 6)| (c3 & 0x3f)); + //w = c & 0x0f; + } + else if (c <= 0xf7 && c >= 0xbf && 3 < len){//4byte sequence start + register wchar_t c2 = (unsigned char)data[1]; + register wchar_t c3 = (unsigned char)data[2]; + register wchar_t c4 = (unsigned char)data[3]; + data += 4; + return (((((((c & 0x1f) << 6) | (c2 & 0x3f)) << 6)| (c3 & 0x3f)) << 6) | (c4 & 0x3f)); + //w = c & 0x07; + } + else + { + return CHAR_ERROR; + } +} + + +/* http://www.w3.org/TR/REC-xml/#NT-NameStartChar + + NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | + [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | + [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF] + */ + +#define INTER(min, max) (car >= min && car <= max) +#define CAR(c) (car == c) + +bool isNameStartChar(const wchar_t car) +{ + if(INTER('a', 'z')) return true; + + return CAR(':') || INTER('A', 'Z') || CAR('_') || + INTER(0xC0, 0xD6) || INTER(0xD8, 0xF6) || INTER(0xF8, 0x2FF) || + INTER(0x370, 0x37D) || INTER(0x37F, 0x1FFF) || INTER(0x200C, 0x200D) || + INTER(0x2070, 0x218F) || INTER(0x2C00, 0x2FEF) || INTER(0x3001, 0xD7FF) || + INTER(0xF900, 0xFDCF) || INTER(0xFDF0, 0xFFFD) || INTER(0x10000, 0xEFFFF); + + +} + +/* http://www.w3.org/TR/REC-xml/#NT-NameChar + + NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040] + + */ + +bool isNameChar(const wchar_t car) +{ + if(INTER('a', 'z')) return true; + if(isNameStartChar(car)) return true; + return CAR('-') || CAR('.') || INTER('0', '9') || + (car == 0xB7) || INTER(0x0300, 0x036F) || INTER(0x203F, 0x2040); +} +/* http://www.w3.org/TR/REC-xml/#NT-S + + S ::= (#x20 | #x9 | #xD | #xA)+ + + */ +bool isWhiteSpace(const wchar_t s) +{ + register const wchar_t car = s; + + return (car == 0x20) || (car == 0x9) || (car == 0xD) || (car == 0xA); +} + +bool isWhiteSpace(const char s) +{ + register const char car = s; + + return (car == 0x20) || (car == 0x9) || (car == 0xD) || (car == 0xA); +} + +const void* memchrs(const char *source, size_t lensource, const char *comp, size_t lencomp) +{ + const char *pos = source - 1; + do + { + pos = (char*)(memchr((void*)(pos + 1), ((comp))[0], lensource - (pos - source) - 1)); + if(!pos) return 0; + if(pos + lencomp > source + lensource) return 0; + if(memcmp(pos, comp, lencomp) != 0) continue; + return pos; + + }while(1); +} + +const void* memrchrs(const char *source, size_t lensource, const char *comp, size_t lencomp) +{ + char *pos = (char*)source; + do + { + pos = (char*)(memrchr(pos, ((char*)(comp))[lencomp - 1], lensource - (pos - source))); + if(!pos) return 0; + if(pos - lencomp < source) return 0; + if(memcmp(pos - lencomp, comp, lencomp) != 0) continue; + return pos; + }while(1); +} + +void Trim(const char *&str, size_t &len) +{ + while(isWhiteSpace(*str) && len) + { + ++str; + --len; + } + + if(!len) return; + + while(isWhiteSpace(*(str + len - 1)) && len > 0) + --len; +} + +void insertString(char *&src, size_t &lenSrc, const char *insert, size_t lenInsert, char *&posInsert) +{ + size_t iPosInsert = posInsert - src; + lenSrc += lenInsert; + src = (char*)realloc(src, lenSrc); + posInsert = src + iPosInsert; + memmove(posInsert + lenInsert, posInsert, lenSrc - lenInsert - iPosInsert); + memcpy(posInsert, insert, lenInsert); +} + +bool GB_MatchString(const char *str, size_t lenStr, const char *pattern, size_t lenPattern, int mode) +{ + if(mode == GB_STRCOMP_NOCASE || mode == GB_STRCOMP_LANG + GB_STRCOMP_NOCASE) + { + if(lenStr != lenPattern) return false; + return strncasecmp(str, pattern, lenPattern) == 0; + } + else if(mode == GB_STRCOMP_LIKE) + { + return GB.MatchString(pattern, lenPattern, str, lenStr) == true; + } + else + { + if(lenStr != lenPattern) return false; + return memcmp(str, pattern, lenPattern) == 0; + } +} + + +void XML_Format(GB_VALUE *value, char* &dst, size_t &lenDst) +{ +#define RETURN(__str) dst = (char*)malloc(sizeof(char) * lenDst); memcpy(dst, __str, lenDst); return; +#define RETURNLEN(__str, __len) lenDst = __len; RETURN(__str); + static char buffer[32]; + if(value->type == GB_T_VARIANT) + GB.Conv(value, ((GB_VARIANT *)value)->value.type); + + if(value->type == GB_T_DATE) + GB.Conv(value, GB_T_STRING); + + switch(value->type) + { + case GB_T_BOOLEAN: + + if (VALUE((GB_BOOLEAN *)value)) + { + RETURNLEN("True", 4); + } + else + { + RETURNLEN("False", 5); + } + + case GB_T_BYTE: + case GB_T_SHORT: + case GB_T_INTEGER: + lenDst = sprintf(buffer, "%d", VALUE((GB_INTEGER *)value)); + RETURN(buffer); + break; + + case GB_T_LONG: + lenDst = sprintf(buffer, "%" PRId64, VALUE((GB_LONG *)value)); + break; + + case GB_T_STRING: + case GB_T_CSTRING: + + XMLText_escapeContent(VALUE((GB_STRING *)value).addr + VALUE((GB_STRING *)value).start, VALUE((GB_STRING *)value).len, dst, lenDst); + return; + + case GB_T_FLOAT: + int lendst; + GB.NumberToString(0, VALUE((GB_FLOAT *)value), NULL, &dst, &lendst); + lenDst = (size_t) lendst; + return; + case GB_T_NULL: + RETURNLEN("Null", 4) + break; + + default: + fprintf(stderr, "gb.xml: XML_Format: unsupported datatype: %d\n", (int)value->type); + dst = 0; + lenDst = 0; + return; + } +#undef RETURN +#undef RETURNLEN +} + +/************************************ Error Management ************************************/ + +void XMLParseException_AnalyzeText(XMLParseException *ex, const char *text, const size_t lenText, const char *posFailed) throw(); + +void ThrowXMLParseException(const char* nerror, const char *text, const size_t lenText, const char *posFailed) +{ + throw XMLParseException_New(nerror, text, lenText, posFailed); +} + +XMLParseException XMLParseException_New()//Void initializer +{ + XMLParseException exception; + memset(&exception, 0, sizeof(XMLParseException)); + exception.line = 1; + exception.column = 1; + return exception; +} + +XMLParseException XMLParseException_New(const char *nerror, size_t posFailed) +{ + XMLParseException exception = XMLParseException_New(); + size_t lenError; + char *error; + + lenError = strlen(nerror) + 1; + error = (char*) malloc(lenError); + memcpy(error, nerror, lenError); + + exception.errorWhat = (char*)malloc(37 + lenError); + sprintf(exception.errorWhat, "Parse error : %s !\n Position %zu", error, (size_t)posFailed); + exception.errorWhat[36 + lenError] = 0; + + free(error); + + return exception; +} + +XMLParseException XMLParseException_New(const char *nerror, const char *data, const size_t lenData, const char *posFailed) throw() +{ + XMLParseException exception = XMLParseException_New(); + size_t lenError; + + lenError = strlen(nerror) + 1; + + //Parse error : (errorText) !\n Line 123456789 , Column 123456789 : \n (near) + + if(posFailed == 0) + { + exception.errorWhat = (char*)malloc(17 + lenError); + sprintf(exception.errorWhat, "Parse error : %s !", nerror); + exception.errorWhat[16 + lenError] = 0; + return exception; + } + else if(!data || !lenData) + { + exception.errorWhat = (char*)malloc(37 + lenError); + sprintf(exception.errorWhat, "Parse error : %s !\n Position %zu", nerror, (size_t)posFailed); + exception.errorWhat[36 + lenError] = 0; + return exception; + } + + if(posFailed > data + lenData || posFailed < data) return exception; + XMLParseException_AnalyzeText(&exception, data, lenData, posFailed); + + + exception.errorWhat = (char*)malloc(61 + lenError + exception.lenNear); + memset(exception.errorWhat, 0, 61 + lenError + exception.lenNear); + sprintf(exception.errorWhat, "Parse error : %s !\n Line %zu , Column %zu : \n %s", nerror, exception.line, exception.column, exception.near); + exception.errorWhat[60 + lenError + exception.lenNear] = 0; + + return exception; +} + +void XMLParseException_Cleanup(XMLParseException *ex) +{ + if(ex->errorWhat) free(ex->errorWhat); + if(ex->near) free(ex->near); +} + +void XMLParseException_AnalyzeText(XMLParseException *ex, const char *text, const size_t lenText, const char *posFailed) throw() +{ + for(const char *pos = text; pos < posFailed; ++pos) + { + ++ex->column; + if(*pos == '\n') + { + ex->column = 1; + ++ex->line; + } + else if(*pos == '\r') + { + if(*(pos + 1) == '\n') ++pos; + ex->column = 1; + ++ex->line; + } + } + + ex->lenNear = text + lenText <= posFailed + 20 ? text + lenText - posFailed : 20; + if(ex->lenNear == 0) return; + ex->near = (char*)malloc(ex->lenNear + 1); + memcpy(ex->near, posFailed, ex->lenNear); + ex->near[ex->lenNear] = 0; +} + + + diff --git a/gb.xml/src/utils.h b/gb.xml/src/utils.h new file mode 100644 index 00000000..27709850 --- /dev/null +++ b/gb.xml/src/utils.h @@ -0,0 +1,62 @@ +/*************************************************************************** + + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef UTILS_H +#define UTILS_H + +#include "main.h" + +#define CHAR_ERROR 0xFFFD // � + +#if defined(OS_MACOSX) +#include +void *memrchr(const char *s, int c, size_t n); +#endif + +wchar_t nextUTF8Char(const char *&data, size_t len); +const void* memchrs(const char *source, size_t lensource, const char *comp, size_t lencomp); +const void* memrchrs(const char *source, size_t lensource, const char *comp, size_t lencomp); + + +bool isNameStartChar(const wchar_t car); +bool isNameChar(const wchar_t car); +bool isWhiteSpace(const wchar_t s); +bool isWhiteSpace(const char s); + +void Trim(const char* &str, size_t &len); +void insertString(char *&src, size_t &lenSrc, const char *insert, size_t lenInsert, char *&posInsert); + +bool GB_MatchString(const char *str, size_t lenStr, const char *pattern, size_t lenPattern, int mode = GB_STRCOMP_BINARY); + +XMLParseException XMLParseException_New(const char *nerror, size_t posFailed); +XMLParseException XMLParseException_New(const char *nerror, const char *data, const size_t lenData, const char *posFailed) throw(); +void XMLParseException_Cleanup(XMLParseException *ex); +void ThrowXMLParseException(const char* nerror, const char *text, const size_t lenText, const char *posFailed); + + +#endif // UTILS_H + +#if !defined(UTILS_GBINTERFACE) && defined(GBINTERFACE_H) +#define UTILS_GBINTERFACE + +void XML_Format(GB_VALUE *value, char* &dst, size_t &lenDst); + +#endif diff --git a/gb.xml/src/xslt/CXSLT.cpp b/gb.xml/src/xslt/CXSLT.cpp new file mode 100755 index 00000000..b9570f95 --- /dev/null +++ b/gb.xml/src/xslt/CXSLT.cpp @@ -0,0 +1,181 @@ +/*************************************************************************** + + CXSLT.c + + (c) 2004 Daniel Campos Fernández + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ +#define __CXSLT_C +#include "../gb.xml.h" +#include "CXSLT.h" + +extern GB_INTERFACE GB; +extern XML_INTERFACE XML; + +#include +#include +#include +#include +#include +#include +#include + +void XSLT_Transform(Document* doc, Document* stylesheet,char* &outDocument, size_t &outDocumentLen) +{ + if(!doc->childCount) throw XSLTException("Void document"); + if(!stylesheet->childCount) throw XSLTException("Void style sheet"); + + xsltStylesheetPtr sheet = 0; + + char *StyleSheetOutput = NULL; + size_t StyleSheetLen = 0; + + XML.SerializeXMLNode((Node*)stylesheet, StyleSheetOutput, StyleSheetLen, -1); + + StyleSheetOutput =(char*)realloc(StyleSheetOutput, StyleSheetLen + 1); + StyleSheetOutput[StyleSheetLen] = 0; + + + xmlDoc *xmlStyleSheet = xmlParseDoc((xmlChar*)(StyleSheetOutput)); + + free(StyleSheetOutput); + + if(!(sheet=xsltParseStylesheetDoc(xmlStyleSheet))) throw XSLTException("Invalid style sheet"); + + char *DocumentOutput; + size_t DocumentLen; + + + XML.SerializeXMLNode((Node*)doc, DocumentOutput, DocumentLen, -1); + + DocumentOutput =(char*)realloc(DocumentOutput, DocumentLen + 1); + DocumentOutput[DocumentLen] = 0; + + + xmlDoc *xmlInputDoc = xmlParseDoc((xmlChar*)(DocumentOutput)); + if(!xmlInputDoc) throw XSLTException("Unable to parse input document"); + + free(DocumentOutput); + + xmlDoc *xmlOutDoc; + + xmlOutDoc = xsltApplyStylesheet(sheet, xmlInputDoc, NULL); + if (!xmlOutDoc) throw XSLTException("Unable to apply style sheet"); + + int size; + xmlDocDumpFormatMemoryEnc(xmlOutDoc ,(xmlChar**)&outDocument, &size, "UTF-8", 1); + outDocumentLen = size; + + xsltFreeStylesheet(sheet); + xmlFreeDoc(xmlOutDoc); + + xmlFreeDoc(xmlInputDoc); +} + +BEGIN_METHOD(CXSLT_Transform,GB_OBJECT inputDoc;GB_OBJECT inputStyleSheet) + +if (GB.CheckObject(VARGOBJ(CDocument,inputDoc))) return; +if (GB.CheckObject(VARGOBJ(CDocument,inputStyleSheet))) return; + + Document *doc = (Document*)(VARGOBJ(CDocument,inputDoc)->node), + *stylesheet = (Document*)(VARGOBJ(CDocument,inputStyleSheet)->node); + + char *buffer = 0; + size_t size; + + try + { + XSLT_Transform(doc, stylesheet, buffer, size); + } + catch(XSLTException &ex) + { + GB.Error(ex.what()); + GB.ReturnNull(); + return; + } + + Document *outDoc = XML.XMLDocument_New(); + + try + { + XML.XMLDocument_SetContent(outDoc, (char*)(buffer),size); + } + catch(XMLParseException e) + { + std::cerr << "XSLT Warning : error when parsing output document : " << e.errorWhat << std::endl; + } + + + free(buffer); + XML.ReturnNode(outDoc); + + +END_METHOD + +BEGIN_METHOD(CXSLT_TransformToString,GB_OBJECT inputDoc;GB_OBJECT inputStyleSheet) + +if (GB.CheckObject(VARGOBJ(CDocument,inputDoc))) return; +if (GB.CheckObject(VARGOBJ(CDocument,inputStyleSheet))) return; + + Document *doc = (Document*)(VARGOBJ(CDocument,inputDoc)->node), + *stylesheet = (Document*)(VARGOBJ(CDocument,inputStyleSheet)->node); + + char *buffer = 0; + size_t size; + + try + { + XSLT_Transform(doc, stylesheet, buffer, size); + } + catch(XSLTException &ex) + { + GB.Error(ex.what()); + GB.ReturnNull(); + return; + } + + GB.ReturnNewString(buffer, size); + + free(buffer); + +END_METHOD + + + + +GB_DESC CXsltDesc[] = +{ + GB_DECLARE("Xslt", 0), GB_NOT_CREATABLE(), + + GB_STATIC_METHOD ("Transform","XmlDocument",CXSLT_Transform,"(Document)XmlDocument;(StyleSheet)XmlDocument;"), + GB_STATIC_METHOD ("TransformToString","s",CXSLT_TransformToString,"(Document)XmlDocument;(StyleSheet)XmlDocument;"), + + GB_END_DECLARE +}; + + +XSLTException::XSLTException(const char *error) throw() +{ + this->error = error; +} + +const char *XSLTException::what() +{ + return this->error; +} diff --git a/gb.xml/src/xslt/CXSLT.h b/gb.xml/src/xslt/CXSLT.h new file mode 100755 index 00000000..ae686d16 --- /dev/null +++ b/gb.xml/src/xslt/CXSLT.h @@ -0,0 +1,44 @@ +/*************************************************************************** + + CXSLT.h + + (c) 2004 Daniel Campos Fernández + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CXSLT_H +#define __CXSLT_H + +#include "../gbinterface.h" + +#ifndef __CXSLT_C +extern GB_DESC CXsltDesc[]; +#endif + +class XSLTException +{ +public: + XSLTException(const char *error) throw(); + const char* what(); +private: + const char *error; + +}; + +#endif diff --git a/gb.xml/src/xslt/Makefile.am b/gb.xml/src/xslt/Makefile.am new file mode 100755 index 00000000..98f0bedb --- /dev/null +++ b/gb.xml/src/xslt/Makefile.am @@ -0,0 +1,11 @@ +COMPONENT = gb.xml.xslt +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.xml.xslt.la + +gb_xml_xslt_la_LIBADD = @XMLXSLT_LIB@ @XML_LIB@ +gb_xml_xslt_la_LDFLAGS = -module @LD_FLAGS@ @XML_LDFLAGS@ @XMLXSLT_LDFLAGS@ +gb_xml_xslt_la_CPPFLAGS = @XMLXSLT_INC@ +gb_xml_xslt_la_CXXFLAGS = $(AM_CXXFLAGS) -fexceptions + +gb_xml_xslt_la_SOURCES = main.cpp CXSLT.h CXSLT.cpp diff --git a/gb.xml/src/xslt/gb.xml.xslt.component b/gb.xml/src/xslt/gb.xml.xslt.component new file mode 100755 index 00000000..8a40225b --- /dev/null +++ b/gb.xml/src/xslt/gb.xml.xslt.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.xml.xslt +Author=Daniel Campos Fernández +Require=gb.xml +State=Stable + diff --git a/gb.xml/src/xslt/main.cpp b/gb.xml/src/xslt/main.cpp new file mode 100755 index 00000000..d805d96e --- /dev/null +++ b/gb.xml/src/xslt/main.cpp @@ -0,0 +1,48 @@ +/*************************************************************************** + + main.c + + (c) 2004 Daniel Campos Fernández + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "CXSLT.h" +#include "../gb.xml.h" + +GB_INTERFACE GB EXPORT; +XML_INTERFACE XML EXPORT; + +extern "C" +{ + GB_DESC *GB_CLASSES[] EXPORT = + { + CXsltDesc ,0 + }; + + int EXPORT GB_INIT(void) + { + GB.GetInterface("gb.xml", XML_INTERFACE_VERSION, &XML); + return -1; + } + + void EXPORT GB_EXIT() + { + + } +} diff --git a/gb.xml/src/xslt/main.h b/gb.xml/src/xslt/main.h new file mode 100755 index 00000000..c8e619b5 --- /dev/null +++ b/gb.xml/src/xslt/main.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + main.h + + (c) 2004 Daniel Campos Fernández + (c) 2012 Adrien Prokopowicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __XSLTMAIN_H +#define __XSLTMAIN_H + +#include "../main.h" +#include +#include +#include +#include + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/gb.xml/src/xslt/xslt.pro b/gb.xml/src/xslt/xslt.pro new file mode 100644 index 00000000..61584fa6 --- /dev/null +++ b/gb.xml/src/xslt/xslt.pro @@ -0,0 +1,14 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Fri Apr 13 23:39:19 2012 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +HEADERS += CXSLT.h +SOURCES += \ + main.cpp \ + CXSLT.cpp diff --git a/logo/gambas-ide.svg b/logo/gambas-ide.svg new file mode 100644 index 00000000..9133c355 --- /dev/null +++ b/logo/gambas-ide.svg @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + 15/09/2008 + + + Fabien Bodard + + + + + Common Creative + + + + + Fabien Bodard + + + gbFBodard150908 + http://gambas.sf.net + + + gambas3 logo basic shrimp + + + A pretty shrimp, a malicious new gambas logo... we got the power ! + + + Fabien Bodard + + + + + + + + + + + + + + + + + + + + + + + diff --git a/logo/gambas.svg b/logo/gambas.svg new file mode 100644 index 00000000..523873c6 --- /dev/null +++ b/logo/gambas.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/m4/ax_compare_version.m4 b/m4/ax_compare_version.m4 new file mode 100644 index 00000000..74dc0fdd --- /dev/null +++ b/m4/ax_compare_version.m4 @@ -0,0 +1,177 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_compare_version.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_COMPARE_VERSION(VERSION_A, OP, VERSION_B, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +# +# DESCRIPTION +# +# This macro compares two version strings. Due to the various number of +# minor-version numbers that can exist, and the fact that string +# comparisons are not compatible with numeric comparisons, this is not +# necessarily trivial to do in a autoconf script. This macro makes doing +# these comparisons easy. +# +# The six basic comparisons are available, as well as checking equality +# limited to a certain number of minor-version levels. +# +# The operator OP determines what type of comparison to do, and can be one +# of: +# +# eq - equal (test A == B) +# ne - not equal (test A != B) +# le - less than or equal (test A <= B) +# ge - greater than or equal (test A >= B) +# lt - less than (test A < B) +# gt - greater than (test A > B) +# +# Additionally, the eq and ne operator can have a number after it to limit +# the test to that number of minor versions. +# +# eq0 - equal up to the length of the shorter version +# ne0 - not equal up to the length of the shorter version +# eqN - equal up to N sub-version levels +# neN - not equal up to N sub-version levels +# +# When the condition is true, shell commands ACTION-IF-TRUE are run, +# otherwise shell commands ACTION-IF-FALSE are run. The environment +# variable 'ax_compare_version' is always set to either 'true' or 'false' +# as well. +# +# Examples: +# +# AX_COMPARE_VERSION([3.15.7],[lt],[3.15.8]) +# AX_COMPARE_VERSION([3.15],[lt],[3.15.8]) +# +# would both be true. +# +# AX_COMPARE_VERSION([3.15.7],[eq],[3.15.8]) +# AX_COMPARE_VERSION([3.15],[gt],[3.15.8]) +# +# would both be false. +# +# AX_COMPARE_VERSION([3.15.7],[eq2],[3.15.8]) +# +# would be true because it is only comparing two minor versions. +# +# AX_COMPARE_VERSION([3.15.7],[eq0],[3.15]) +# +# would be true because it is only comparing the lesser number of minor +# versions of the two values. +# +# Note: The characters that separate the version numbers do not matter. An +# empty string is the same as version 0. OP is evaluated by autoconf, not +# configure, so must be a string, not a variable. +# +# The author would like to acknowledge Guido Draheim whose advice about +# the m4_case and m4_ifvaln functions make this macro only include the +# portions necessary to perform the specific comparison specified by the +# OP argument in the final configure script. +# +# LICENSE +# +# Copyright (c) 2008 Tim Toolan +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 11 + +dnl ######################################################################### +AC_DEFUN([AX_COMPARE_VERSION], [ + AC_REQUIRE([AC_PROG_AWK]) + + # Used to indicate true or false condition + ax_compare_version=false + + # Convert the two version strings to be compared into a format that + # allows a simple string comparison. The end result is that a version + # string of the form 1.12.5-r617 will be converted to the form + # 0001001200050617. In other words, each number is zero padded to four + # digits, and non digits are removed. + AS_VAR_PUSHDEF([A],[ax_compare_version_A]) + A=`echo "$1" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \ + -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/[[^0-9]]//g'` + + AS_VAR_PUSHDEF([B],[ax_compare_version_B]) + B=`echo "$3" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \ + -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/[[^0-9]]//g'` + + dnl # In the case of le, ge, lt, and gt, the strings are sorted as necessary + dnl # then the first line is used to determine if the condition is true. + dnl # The sed right after the echo is to remove any indented white space. + m4_case(m4_tolower($2), + [lt],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/false/;s/x${B}/true/;1q"` + ], + [gt],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort | sed "s/x${A}/false/;s/x${B}/true/;1q"` + ], + [le],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort | sed "s/x${A}/true/;s/x${B}/false/;1q"` + ], + [ge],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"` + ],[ + dnl Split the operator from the subversion count if present. + m4_bmatch(m4_substr($2,2), + [0],[ + # A count of zero means use the length of the shorter version. + # Determine the number of characters in A and B. + ax_compare_version_len_A=`echo "$A" | $AWK '{print(length)}'` + ax_compare_version_len_B=`echo "$B" | $AWK '{print(length)}'` + + # Set A to no more than B's length and B to no more than A's length. + A=`echo "$A" | sed "s/\(.\{$ax_compare_version_len_B\}\).*/\1/"` + B=`echo "$B" | sed "s/\(.\{$ax_compare_version_len_A\}\).*/\1/"` + ], + [[0-9]+],[ + # A count greater than zero means use only that many subversions + A=`echo "$A" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"` + B=`echo "$B" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"` + ], + [.+],[ + AC_WARNING( + [illegal OP numeric parameter: $2]) + ],[]) + + # Pad zeros at end of numbers to make same length. + ax_compare_version_tmp_A="$A`echo $B | sed 's/./0/g'`" + B="$B`echo $A | sed 's/./0/g'`" + A="$ax_compare_version_tmp_A" + + # Check for equality or inequality as necessary. + m4_case(m4_tolower(m4_substr($2,0,2)), + [eq],[ + test "x$A" = "x$B" && ax_compare_version=true + ], + [ne],[ + test "x$A" != "x$B" && ax_compare_version=true + ],[ + AC_WARNING([illegal OP parameter: $2]) + ]) + ]) + + AS_VAR_POPDEF([A])dnl + AS_VAR_POPDEF([B])dnl + + dnl # Execute ACTION-IF-TRUE / ACTION-IF-FALSE. + if test "$ax_compare_version" = "true" ; then + m4_ifvaln([$4],[$4],[:])dnl + m4_ifvaln([$5],[else $5])dnl + fi +]) dnl AX_COMPARE_VERSION diff --git a/m4/gb_cflags_gcc_option.m4 b/m4/gb_cflags_gcc_option.m4 new file mode 100644 index 00000000..6d431c36 --- /dev/null +++ b/m4/gb_cflags_gcc_option.m4 @@ -0,0 +1,226 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_cflags_gcc_option.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CFLAGS_GCC_OPTION (optionflag [,[shellvar][,[A][,[NA]]]) +# +# DESCRIPTION +# +# AX_CFLAGS_GCC_OPTION(-fomit-frame-pointer) would show a message like +# "checking CFLAGS for gcc -fomit-frame-pointer ... yes" and add the +# optionflag to CFLAGS if it is understood. You can override the +# shellvar-default of CFLAGS of course. The order of arguments stems from +# the explicit macros like AX_CFLAGS_WARN_ALL. +# +# The cousin AX_CXXFLAGS_GCC_OPTION would check for an option to add to +# CXXFLAGS - and it uses the autoconf setup for C++ instead of C (since it +# is possible to use different compilers for C and C++). +# +# The macro is a lot simpler than any special AX_CFLAGS_* macro (or +# ax_cxx_rtti.m4 macro) but allows to check for arbitrary options. +# However, if you use this macro in a few places, it would be great if you +# would make up a new function-macro and submit it to the ac-archive. +# +# - $1 option-to-check-for : required ("-option" as non-value) +# - $2 shell-variable-to-add-to : CFLAGS (or CXXFLAGS in the other case) +# - $3 action-if-found : add value to shellvariable +# - $4 action-if-not-found : nothing +# +# Note: in earlier versions, $1-$2 were swapped. We try to detect the +# situation and accept a $2=~/-/ as being the old option-to-check-for. +# +# There are other variants that emerged from the original macro variant +# which did just test an option to be possibly added. However, some +# compilers accept an option silently, or possibly for just another option +# that was not intended. Therefore, we have to do a generic test for a +# compiler family. For gcc we check "-pedantic" being accepted which is +# also understood by compilers who just want to be compatible with gcc +# even when not being made from gcc sources. +# +# See also: AX_CFLAGS_SUN_OPTION, AX_CFLAGS_HPUX_OPTION, +# AX_CFLAGS_AIX_OPTION, and AX_CFLAGS_IRIX_OPTION. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 12 + +AC_DEFUN([GB_CFLAGS_GCC_OPTION_OLD], [dnl +AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ax_cv_cflags_gcc_option_$2])dnl +AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for gcc m4_ifval($2,$2,-option)], +VAR,[AS_VAR_SET([VAR],["no, unknown"]) + AC_LANG_SAVE + AC_LANG_C + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic -Werror % m4_ifval($2,$2,-option)" dnl GCC + "-pedantic % m4_ifval($2,$2,-option) %% no, obsolete" dnl new GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_TRY_COMPILE([],[return 0;], + [AS_VAR_SET([VAR],[`echo $ac_arg | sed -e 's,.*% *,,'`]); break]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +m4_ifdef([AS_VAR_COPY],[AS_VAR_COPY([var],[VAR])],[var=AS_VAR_GET([VAR])]) +case ".$var" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $var " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $var]) + else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $var"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $var" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + + +dnl the only difference - the LANG selection... and the default FLAGS + +AC_DEFUN([GB_CXXFLAGS_GCC_OPTION_OLD], [dnl +AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ax_cv_cxxflags_gcc_option_$2])dnl +AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for gcc m4_ifval($2,$2,-option)], +VAR,[AS_VAR_SET([VAR],["no, unknown"]) + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic -Werror % m4_ifval($2,$2,-option)" dnl GCC + "-pedantic % m4_ifval($2,$2,-option) %% no, obsolete" dnl new GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_TRY_COMPILE([],[return 0;], + [AS_VAR_SET([VAR],[`echo $ac_arg | sed -e 's,.*% *,,'`]); break]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +m4_ifdef([AS_VAR_COPY],[AS_VAR_COPY([var],[VAR])],[var=AS_VAR_GET([VAR])]) +case ".$var" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $var " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $var]) + else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $var"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $var" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + +dnl ------------------------------------------------------------------------- + +AC_DEFUN([GB_CFLAGS_GCC_OPTION_NEW], [dnl +AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ax_cv_cflags_gcc_option_$1])dnl +AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)], +VAR,[AS_VAR_SET([VAR],["no, unknown"]) + AC_LANG_SAVE + AC_LANG_C + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic -Werror % m4_ifval($1,$1,-option)" dnl GCC + "-pedantic % m4_ifval($1,$1,-option) %% no, obsolete" dnl new GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_TRY_COMPILE([],[return 0;], + [AS_VAR_SET([VAR],[`echo $ac_arg | sed -e 's,.*% *,,'`]); break]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +m4_ifdef([AS_VAR_COPY],[AS_VAR_COPY([var],[VAR])],[var=AS_VAR_GET([VAR])]) +case ".$var" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $var " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $var]) + else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $var"]) + m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $var" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + + +dnl the only difference - the LANG selection... and the default FLAGS + +AC_DEFUN([GB_CXXFLAGS_GCC_OPTION_NEW], [dnl +AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ax_cv_cxxflags_gcc_option_$1])dnl +AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)], +VAR,[AS_VAR_SET([VAR],["no, unknown"]) + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic -Werror % m4_ifval($1,$1,-option)" dnl GCC + "-pedantic % m4_ifval($1,$1,-option) %% no, obsolete" dnl new GCC + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_TRY_COMPILE([],[return 0;], + [AS_VAR_SET([VAR],[`echo $ac_arg | sed -e 's,.*% *,,'`]); break]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +m4_ifdef([AS_VAR_COPY],[AS_VAR_COPY([var],[VAR])],[var=AS_VAR_GET([VAR])]) +case ".$var" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $var " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $var]) + else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $var"]) + m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $var" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + +AC_DEFUN([GB_CFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1, +[GB_CFLAGS_GCC_OPTION_NEW($@)],[GB_CFLAGS_GCC_OPTION_OLD($@)])]) + +AC_DEFUN([GB_CXXFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1, +[GB_CXXFLAGS_GCC_OPTION_NEW($@)],[GB_CXXFLAGS_GCC_OPTION_OLD($@)])]) + diff --git a/m4/gb_httpd.m4 b/m4/gb_httpd.m4 new file mode 100644 index 00000000..fea63a5a --- /dev/null +++ b/m4/gb_httpd.m4 @@ -0,0 +1,188 @@ +dnl +dnl Improved version of AC_CHECK_LIB +dnl +dnl Thanks to John Hawkinson (jhawk@mit.edu) +dnl +dnl usage: +dnl +dnl GB_AC_LBL_CHECK_LIB(LIBRARY, FUNCTION [, ACTION-IF-FOUND [, +dnl ACTION-IF-NOT-FOUND [, OTHER-LIBRARIES]]]) +dnl +dnl results: +dnl +dnl LIBS +dnl + +define(GB_AC_LBL_CHECK_LIB, +[AC_MSG_CHECKING([for $2 in -l$1]) +dnl Use a cache variable name containing both the library and function name, +dnl because the test really is for library $1 defining function $2, not +dnl just for library $1. Separate tests with the same $1 and different $2's +dnl may have different results. +ac_lib_var=`echo $1['_']$2['_']$5 | sed 'y%./+- %__p__%'` +AC_CACHE_VAL(ac_cv_lbl_lib_$ac_lib_var, +[ac_save_LIBS="$LIBS" +LIBS="-l$1 $5 $LIBS" +AC_TRY_LINK(dnl +ifelse([$2], [main], , dnl Avoid conflicting decl of main. +[/* Override any gcc2 internal prototype to avoid an error. */ +]ifelse(_AC_LANG_CURRENT, CPLUSPLUS, [#ifdef __cplusplus +extern "C" +#endif +])dnl +[/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $2(); +]), + [$2()], + eval "ac_cv_lbl_lib_$ac_lib_var=yes", + eval "ac_cv_lbl_lib_$ac_lib_var=no") +LIBS="$ac_save_LIBS" +])dnl +if eval "test \"`echo '$ac_cv_lbl_lib_'$ac_lib_var`\" = yes"; then + AC_MSG_RESULT(yes) + ifelse([$3], , +[changequote(, )dnl + ac_tr_lib=HAVE_LIB`echo $1 | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` +changequote([, ])dnl + AC_DEFINE_UNQUOTED($ac_tr_lib, [], []) + LIBS="-l$1 $LIBS" +], [$3]) +else + AC_MSG_RESULT(no) +ifelse([$4], , , [$4 +])dnl +fi +]) + +dnl +dnl GB_AC_LBL_LIBRARY_NET +dnl +dnl This test is for network applications that need socket() and +dnl gethostbyname() -ish functions. Under Solaris, those applications +dnl need to link with "-lsocket -lnsl". Under IRIX, they need to link +dnl with "-lnsl" but should *not* link with "-lsocket" because +dnl libsocket.a breaks a number of things (for instance: +dnl gethostbyname() under IRIX 5.2, and snoop sockets under most +dnl versions of IRIX). +dnl +dnl Unfortunately, many application developers are not aware of this, +dnl and mistakenly write tests that cause -lsocket to be used under +dnl IRIX. It is also easy to write tests that cause -lnsl to be used +dnl under operating systems where neither are necessary (or useful), +dnl such as SunOS 4.1.4, which uses -lnsl for TLI. +dnl +dnl This test exists so that every application developer does not test +dnl this in a different, and subtly broken fashion. + +dnl It has been argued that this test should be broken up into two +dnl seperate tests, one for the resolver libraries, and one for the +dnl libraries necessary for using Sockets API. Unfortunately, the two +dnl are carefully intertwined and allowing the autoconf user to use +dnl them independantly potentially results in unfortunate ordering +dnl dependancies -- as such, such component macros would have to +dnl carefully use indirection and be aware if the other components were +dnl executed. Since other autoconf macros do not go to this trouble, +dnl and almost no applications use sockets without the resolver, this +dnl complexity has not been implemented. +dnl +dnl The check for libresolv is in case you are attempting to link +dnl statically and happen to have a libresolv.a lying around (and no +dnl libnsl.a). +dnl +AC_DEFUN([GB_AC_LBL_LIBRARY_NET], [ + # Most operating systems have gethostbyname() in the default searched + # libraries (i.e. libc): + AC_CHECK_FUNC(gethostbyname, , + # Some OSes (eg. Solaris) place it in libnsl: + GB_AC_LBL_CHECK_LIB(nsl, gethostbyname, , + # Some strange OSes (SINIX) have it in libsocket: + GB_AC_LBL_CHECK_LIB(socket, gethostbyname, , + # Unfortunately libsocket sometimes depends on libnsl. + # AC_CHECK_LIB's API is essentially broken so the + # following ugliness is necessary: + GB_AC_LBL_CHECK_LIB(socket, gethostbyname, + LIBS="-lsocket -lnsl $LIBS", + AC_CHECK_LIB(resolv, gethostbyname), + -lnsl)))) + AC_CHECK_FUNC(socket, , AC_CHECK_LIB(socket, socket, , + GB_AC_LBL_CHECK_LIB(socket, socket, LIBS="-lsocket -lnsl $LIBS", , + -lnsl))) + # DLPI needs putmsg under HPUX so test for -lstr while we're at it + AC_CHECK_LIB(str, putmsg) + ]) + +dnl +dnl Checks to see if struct tm has the BSD tm_gmtoff member +dnl +dnl usage: +dnl +dnl GB_AC_ACME_TM_GMTOFF +dnl +dnl results: +dnl +dnl HAVE_TM_GMTOFF (defined) +dnl +AC_DEFUN([GB_AC_ACME_TM_GMTOFF], + [AC_MSG_CHECKING(if struct tm has tm_gmtoff member) + AC_CACHE_VAL(ac_cv_acme_tm_has_tm_gmtoff, + AC_TRY_COMPILE([ +# include +# include ], + [u_int i = sizeof(((struct tm *)0)->tm_gmtoff)], + ac_cv_acme_tm_has_tm_gmtoff=yes, + ac_cv_acme_tm_has_tm_gmtoff=no)) + AC_MSG_RESULT($ac_cv_acme_tm_has_tm_gmtoff) + if test $ac_cv_acme_tm_has_tm_gmtoff = yes ; then + AC_DEFINE([HAVE_TM_GMTOFF], [], [if struct tm has the BSD tm_gmtoff member]) + fi]) + +dnl +dnl Checks to see if int64_t exists +dnl +dnl usage: +dnl +dnl GB_AC_ACME_INT64T +dnl +dnl results: +dnl +dnl HAVE_INT64T (defined) +dnl +AC_DEFUN([GB_AC_ACME_INT64T], + [AC_MSG_CHECKING(if int64_t exists) + AC_CACHE_VAL(ac_cv_acme_int64_t, + AC_TRY_COMPILE([ +# include ], + [int64_t i64], + ac_cv_acme_int64_t=yes, + ac_cv_acme_int64_t=no)) + AC_MSG_RESULT($ac_cv_acme_int64_t) + if test $ac_cv_acme_int64_t = yes ; then + AC_DEFINE([HAVE_INT64T], [], [if int64_t exists]) + fi]) + +dnl +dnl Checks to see if socklen_t exists +dnl +dnl usage: +dnl +dnl GB_AC_ACME_SOCKLENT +dnl +dnl results: +dnl +dnl HAVE_SOCKLENT (defined) +dnl +AC_DEFUN([GB_AC_ACME_SOCKLENT], + [AC_MSG_CHECKING(if socklen_t exists) + AC_CACHE_VAL(ac_cv_acme_socklen_t, + AC_TRY_COMPILE([ +# include +# include ], + [socklen_t slen], + ac_cv_acme_socklen_t=yes, + ac_cv_acme_socklen_t=no)) + AC_MSG_RESULT($ac_cv_acme_socklen_t) + if test $ac_cv_acme_socklen_t = yes ; then + AC_DEFINE([HAVE_SOCKLENT], [], [if socklen_t exists]) + fi]) diff --git a/m4/gb_sdl.m4 b/m4/gb_sdl.m4 new file mode 100644 index 00000000..2d8834e0 --- /dev/null +++ b/m4/gb_sdl.m4 @@ -0,0 +1,185 @@ +# Configure paths for SDL +# Sam Lantinga 9/21/99 +# stolen from Manish Singh +# stolen back from Frank Belew +# stolen from Manish Singh +# Shamelessly stolen from Owen Taylor + +#serial 1 + +dnl GB_PATH_SDL is only a rename for AM_PATH_SDL, grab from sdl-1.2.13 release + +dnl GB_PATH_SDL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS +dnl +AC_DEFUN([GB_PATH_SDL], +[dnl +dnl Get the cflags and libraries from the sdl-config script +dnl +AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)], + sdl_prefix="$withval", sdl_prefix="") +AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)], + sdl_exec_prefix="$withval", sdl_exec_prefix="") +AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program], + , enable_sdltest=yes) + + if test x$sdl_exec_prefix != x ; then + sdl_config_args="$sdl_config_args --exec-prefix=$sdl_exec_prefix" + if test x${SDL_CONFIG+set} != xset ; then + SDL_CONFIG=$sdl_exec_prefix/bin/sdl-config + fi + fi + if test x$sdl_prefix != x ; then + sdl_config_args="$sdl_config_args --prefix=$sdl_prefix" + if test x${SDL_CONFIG+set} != xset ; then + SDL_CONFIG=$sdl_prefix/bin/sdl-config + fi + fi + + if test "x$prefix" != xNONE; then + PATH="$prefix/bin:$prefix/usr/bin:$PATH" + fi + AC_PATH_PROG(SDL_CONFIG, sdl-config, no, [$PATH]) + min_sdl_version=ifelse([$1], ,0.11.0,$1) + AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) + no_sdl="" + if test "$SDL_CONFIG" = "no" ; then + no_sdl=yes + else + SDL_CFLAGS=`$SDL_CONFIG $sdl_config_args --cflags` + SDL_LIBS=`$SDL_CONFIG $sdl_config_args --libs` + + sdl_major_version=`$SDL_CONFIG $sdl_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + sdl_minor_version=`$SDL_CONFIG $sdl_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + sdl_micro_version=`$SDL_CONFIG $sdl_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "x$enable_sdltest" = "xyes" ; then + ac_save_CFLAGS="$CFLAGS" + ac_save_CXXFLAGS="$CXXFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $SDL_CFLAGS" + CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" +dnl +dnl Now check if the installed SDL is sufficiently new. (Also sanity +dnl checks the results of sdl-config to some extent +dnl + rm -f conf.sdltest + AC_TRY_RUN([ +#include +#include +#include +#include "SDL.h" + +char* +my_strdup (char *str) +{ + char *new_str; + + if (str) + { + new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char)); + strcpy (new_str, str); + } + else + new_str = NULL; + + return new_str; +} + +int main (int argc, char *argv[]) +{ + int major, minor, micro; + char *tmp_version; + + /* This hangs on some systems (?) + system ("touch conf.sdltest"); + */ + { FILE *fp = fopen("conf.sdltest", "a"); if ( fp ) fclose(fp); } + + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = my_strdup("$min_sdl_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_sdl_version"); + exit(1); + } + + if (($sdl_major_version > major) || + (($sdl_major_version == major) && ($sdl_minor_version > minor)) || + (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** 'sdl-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version); + printf("*** of SDL required is %d.%d.%d. If sdl-config is correct, then it is\n", major, minor, micro); + printf("*** best to upgrade to the required version.\n"); + printf("*** If sdl-config was wrong, set the environment variable SDL_CONFIG\n"); + printf("*** to point to the correct copy of sdl-config, and remove the file\n"); + printf("*** config.cache before re-running configure\n"); + return 1; + } +} + +],, no_sdl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + CXXFLAGS="$ac_save_CXXFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + if test "x$no_sdl" = x ; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + if test "$SDL_CONFIG" = "no" ; then + echo "*** The sdl-config script installed by SDL could not be found" + echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the SDL_CONFIG environment variable to the" + echo "*** full path to sdl-config." + else + if test -f conf.sdltest ; then + : + else + echo "*** Could not run SDL test program, checking why..." + CFLAGS="$CFLAGS $SDL_CFLAGS" + CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" + AC_TRY_LINK([ +#include +#include "SDL.h" + +int main(int argc, char *argv[]) +{ return 0; } +#undef main +#define main K_and_R_C_main +], [ return 0; ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding SDL or finding the wrong" + echo "*** version of SDL. If it is not finding SDL, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means SDL was incorrectly installed" + echo "*** or that you have moved SDL since it was installed. In the latter case, you" + echo "*** may want to edit the sdl-config script: $SDL_CONFIG" ]) + CFLAGS="$ac_save_CFLAGS" + CXXFLAGS="$ac_save_CXXFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + SDL_CFLAGS="" + SDL_LIBS="" + ifelse([$3], , :, [$3]) + fi + AC_SUBST(SDL_CFLAGS) + AC_SUBST(SDL_LIBS) + rm -f conf.sdltest +]) diff --git a/main/AUTHORS b/main/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/main/COPYING b/main/COPYING new file mode 120000 index 00000000..012065c8 --- /dev/null +++ b/main/COPYING @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff --git a/main/ChangeLog b/main/ChangeLog new file mode 120000 index 00000000..22ec9b8a --- /dev/null +++ b/main/ChangeLog @@ -0,0 +1 @@ +../ChangeLog \ No newline at end of file diff --git a/main/INSTALL b/main/INSTALL new file mode 100644 index 00000000..64d33306 --- /dev/null +++ b/main/INSTALL @@ -0,0 +1,231 @@ + +REQUIREMENTS +============ + +Read the IMPORTANT NOTES in the README file. + + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + + +Gambas Options +============== + + --with-intl-includes where the internationalization headers are located. + --with-intl-libraries where the internationalization libraries are located. + --with-qt-includes where the QT component headers are located. + --with-qt-libraries where the QT component libraries are located. + --with-kde-includes where the KDE 3.x component headers are located. + --with-kde-libraries where the KDE 3.x component libraries are located. + --with-net-includes where the Networking component headers are located. + --with-net-libraries where the Networking component libraries are located. + --with-postgresql-includes where the PostgreSQL driver headers are located. + --with-postgresql-libraries where the PostgreSQL driver libraries are located. + --with-mysql-includes where the MySQL driver headers are located. + --with-mysql-libraries where the MySQL driver libraries are located. + --with-sdl-includes where the SDL component headers are located. + --with-sdl-libraries where the SDL component libraries are located. + +--disable-debug Remove debug information from binary files. + +--enable-optimization Enable optimization during compilation. + +--disable-preloading Disable the preloading of components. + +--disable-qt-component Do not compile the QT component. + + +Component options +================= + +XXX is a component or library name: + + intl internationalization library + kde KDE component + mysql MySQL driver + net Network component + postgresql PostgreSQL driver + qt QT component + sdl SDL component + +--with-XXX-libraries Where the libraries are located. + +--with-XXX-includes Where the headers are located. + +Use these options if the configure script cannot detect the +location of librairies and/or headers. + +The components or libraries that are not detected are automatically disables, +and then not compiled. + +That's all ! Good luck... diff --git a/main/Makefile.am b/main/Makefile.am new file mode 100644 index 00000000..c24d91e0 --- /dev/null +++ b/main/Makefile.am @@ -0,0 +1,89 @@ +ACLOCAL_AMFLAGS = -I m4 --install +SUBDIRS = gbc gbx . lib share +EXTRA_DIST = TODO reconf tools spec README mime gb.*.h +BUILT_SOURCES = trunk_version.h +CLEANFILES = trunk_version.h + +.PHONY: trunk_version.h +trunk_version.h: + @if test -d ../.svn ; then \ + if test ../.svn/wc.db -nt trunk_version.h || test ../.svn/entries -nt trunk_version.h; then \ + echo '#define TRUNK_VERSION "'`LC_ALL=C svn info 2>/dev/null | grep Revision | egrep -wo [0-9]+`'"' > trunk_version.h; \ + fi \ + elif test -d ../.git ; then \ + GIT_BRANCH=`git rev-parse --abbrev-ref HEAD`; \ + if test \! -e ../.git/refs/heads/$$GIT_BRANCH -o ../.git/refs/heads/$$GIT_BRANCH -nt trunk_version.h; then \ + if GIT_TAG=$$(git describe --exact-match $$GIT_BRANCH 2>/dev/null) ; then \ + GIT_BRANCH="$$GIT_TAG"; \ + fi; \ + GIT_HASH=`git rev-parse --short HEAD`; \ + echo '#define TRUNK_VERSION "'$$GIT_HASH' ('$$GIT_BRANCH')"' > trunk_version.h; \ + echo '#define TRUNK_VERSION_GIT 1' >> trunk_version.h; \ + fi; \ + else \ + touch trunk_version.h; \ + fi + +install-exec-local: + @rm -f $(srcdir)/../warnings.log + + @if test "x$(ROOT)" != "x"; then \ + echo "[Installing with ROOT=$(ROOT)]"; \ + fi + @if test "x$(DESTDIR)" != "x"; then \ + echo "[Installing with DESTDIR=$(DESTDIR)]"; \ + ROOT=$DESTDIR; \ + fi + + @echo "Making runtime symbolic link" + @$(LN_S) -f gbx$(GAMBAS_VERSION) $(DESTDIR)$(bindir)/gbr$(GAMBAS_VERSION) || true + + @if test x"$(XDG_UTILS)" != x; then \ + echo "Registering Gambas executable mimetype"; \ + $(INSTALL) -d $(DESTDIR)$(gbdatadir)/icons; \ + cp -f $(srcdir)/mime/application-x-gambas3.png $(DESTDIR)$(gbdatadir)/icons; \ + xdg-icon-resource install --context mimetypes --size 256 $(DESTDIR)$(gbdatadir)/icons/application-x-gambas3.png application-x-gambas3; \ + xdg-mime install $(srcdir)/mime/application-x-gambas3.xml; \ + fi + + @echo "Creating the information files for gb component..." + @$(INSTALL) -d $(DESTDIR)$(gbdatadir)/info + @$(DESTDIR)$(bindir)/gbi$(GAMBAS_VERSION) -r $(DESTDIR)$(prefix) gb + @rm -f $(DESTDIR)$(gblibdir)/lib.gb.la + @rm -f $(DESTDIR)$(gblibdir)/lib.gb.so* + @$(INSTALL) lib/gb.component $(DESTDIR)$(gblibdir) + + @echo "Installing the compiler tools..." + @(cd $(srcdir)/tools; d=`pwd`; \ + for p in gb*; do \ + echo "Compiling $$p..."; cd $$d/$$p; \ + $(DESTDIR)$(bindir)/gbc$(GAMBAS_VERSION) -ag -r $(DESTDIR)$(prefix); \ + if test $$? -eq 0; then \ + $(DESTDIR)$(bindir)/gba$(GAMBAS_VERSION); \ + rm -rf .gambas; \ + echo "Installing $$p..."; \ + $(INSTALL) $$p.gambas $(DESTDIR)$(bindir); \ + $(LN_S) -f $$p.gambas $(DESTDIR)$(bindir)/$$p || true; \ + else \ + echo "|| Unable to compile $$p" >> ../../../warnings.log; \ + fi \ + done) + +uninstall-local: + @rm -f $(DESTDIR)$(bindir)/gbr$(GAMBAS_VERSION) + @rm -rf $(DESTDIR)$(gblibdir)/info + @if test x"$(XDG_UTILS)" != x; then \ + xdg-mime uninstall $(srcdir)/mime/application-x-gambas3.xml; \ + xdg-icon-resource uninstall --context mimetypes --size 64 application-x-gambas3; \ + fi + @(cd $(srcdir)/tools; for p in gb*; do rm -f $(DESTDIR)$(bindir)/$$p.gambas $(DESTDIR)$(bindir)/$$p; done) + @rm -f $(DESTDIR)$(gblibdir)gb.component + @rm -rf $(DESTDIR)$(gbdatadir)/info/gb.info + @rm -rf $(DESTDIR)$(gbdatadir)/info/gb.list + +dist-hook: + @rm -f $(distdir)/trunk_version.h + @(cd $(distdir)/tools; \ + rm -rf `find . -name ".gambas" -o -name ".action" -o -name ".lock" -o -name ".xvpics" -o -name "*~" -o -name "*.out" -o -name "*.pot" -o -name "*.gambas" -o -name "core*" -o -name ".kdbg*" -o -name ".svn"`;) + + diff --git a/main/NEWS b/main/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/main/README b/main/README new file mode 100644 index 00000000..e69de29b diff --git a/main/TODO b/main/TODO new file mode 100644 index 00000000..e69de29b diff --git a/main/acinclude.m4 b/main/acinclude.m4 new file mode 120000 index 00000000..d84c32a3 --- /dev/null +++ b/main/acinclude.m4 @@ -0,0 +1 @@ +../acinclude.m4 \ No newline at end of file diff --git a/main/compile b/main/compile new file mode 100755 index 00000000..3d217032 --- /dev/null +++ b/main/compile @@ -0,0 +1,142 @@ +#! /bin/sh +# Wrapper for compilers which do not understand `-c -o'. + +scriptversion=2004-10-12.08 + +# Copyright (C) 1999, 2000, 2003, 2004 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +case $1 in + '') + echo "$0: No command. Try \`$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand `-c -o'. +Remove `-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file `INSTALL'. + +Report bugs to . +EOF + exit 0 + ;; + -v | --v*) + echo "compile $scriptversion" + exit 0 + ;; +esac + +ofile= +cfile= +eat= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as `compile cc -o foo foo.c'. + # So we strip `-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no `-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # `.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed -e 's|^.*/||' -e 's/\.c$/.o/'` + +# Create the lock directory. +# Note: use `[/.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/main/component.am b/main/component.am new file mode 120000 index 00000000..2f0eee34 --- /dev/null +++ b/main/component.am @@ -0,0 +1 @@ +../component.am \ No newline at end of file diff --git a/main/configure.ac b/main/configure.ac new file mode 100644 index 00000000..c69ca256 --- /dev/null +++ b/main/configure.ac @@ -0,0 +1,146 @@ +dnl ---- configure.ac for main programs + +m4_include([../version.m4]) +AC_INIT(gambas3-main, GB_VERSION, GB_MAIL, [], GB_URL) +AC_CONFIG_MACRO_DIR([m4]) + +GB_INIT(main) +GB_MATH_FUNC +LT_INIT +AM_PROG_CC_C_O + +if test "x${GAMBAS_CONFIG_FAILURE}" != "x"; then + SAVE_GAMBAS_CONFIG_FAILURE=${GAMBAS_CONFIG_FAILURE} + GAMBAS_CONFIG_FAILURE='' +fi + +dnl ---- Check for internationalization library + +GB_COMPONENT( + intl, INTL, libintl, [], + [GB_FIND(libintl.h, /opt/local /usr/local /usr, include)], + [GB_FIND(libintl.$SHLIBEXT, /opt/local /usr/local /usr /, lib)], + [-lintl], + [], + [Cannot find 'libintl' library. It may be located inside the system C library, so you can ignore that warning...]) + +dnl ---- Remove DISABLED file that could have been generated before + +rm -f FAILED DISABLED DISABLED.* + +dnl ---- Check for charset conversion library + +GB_COMPONENT( + conv, CONV, libiconv, [], + [GB_FIND(iconv.h, /opt/local /usr/local /usr, include)], + [GB_FIND(libiconv.$SHLIBEXT, /opt/local /usr/local /usr, lib)], + [-liconv], + [], + [Cannot find 'libiconv' library. It may be merged inside the system C library, so you can ignore that warning...]) + +dnl ---- Remove DISABLED file that could have been generated before + +rm -f FAILED DISABLED DISABLED.* + +dnl ---- Check for gettext library + +if test "x$GETTEXT_LIB" != x; then + +GB_COMPONENT( + gettext, GETTEXT, libgettextlib, [], + [], + [GB_FIND(libgettextlib.$SHLIBEXT, /opt/local /usr/local /usr, lib)], + [$GETTEXT_LIB], + [], + [Cannot find 'libgettextlib' library. It may be merged inside the system C library, so you can ignore that warning...]) + +fi + +dnl ---- Remove DISABLED file that could have been generated before + +rm -f FAILED DISABLED DISABLED.* + +dnl ---- Check for ffi library + +GB_COMPONENT_SEARCH( + ffi, FFI, libffi, [], + libffi, + [GB_FIND(ffi.h, /usr/local /usr/local/lib/64 /usr/local/lib /usr /usr/lib64 /usr/lib /usr/lib/gcc/*/*, include ffi/include)], + [GB_FIND(libffi.$SHLIBEXT, /usr/local /usr /usr/lib/gcc/*/*, lib .)], + [-lffi]) + +if test -z "$FFI_LIB"; then + GB_COMPONENT( + ffi, FFI, libffi, [], + [GB_FIND(ffi.h, /opt/local /usr/local /usr/local/lib /usr /usr/lib /usr/lib/gcc/*/*, include ffi/include)], + [GB_FIND(libffi.$SHLIBEXT, /usr/local /usr, lib)], + [-lffi]) +fi + +dnl ---- Remove DISABLED file that could have been generated before + +rm -f FAILED DISABLED DISABLED.* + +if test "x${SAVE_GAMBAS_CONFIG_FAILURE}" != "x"; then + GAMBAS_CONFIG_FAILURE=${SAVE_GAMBAS_CONFIG_FAILURE} + SAVE_GAMBAS_CONFIG_FAILURE='' +fi + +dnl ---- Check for the Linux inotify headers + +GB_COMPONENT( + inotify, INOTIFY, gb.inotify, [inotify], + [GB_FIND(sys/inotify.h, /usr/local/lib /usr/local /usr/lib /usr, include)], + [GB_FIND(, /usr/local /usr, lib)], + [$GB_INOTIFY_LIB]) + +dnl ---- We do not use libtool to load shared libraries anymore! + +AC_DEFINE(DONT_USE_LTDL, 1, [Do not use libtool to load shared libraries]) +if test "$SYSTEM" != "OPENBSD" && test "$SYSTEM" != "FREEBSD" && test "$SYSTEM" != "NETBSD"; then + DL_LIB="-ldl" +else + DL_LIB="" +fi + +AC_SUBST(DL_LIB) + +dnl ---- Check for Portland scripts + +AC_CHECK_PROGS(XDG_UTILS, [xdg-mime xdg-icon-resource], []) + +dnl ---- Create makefiles + +AC_CONFIG_FILES([\ +Makefile \ +share/Makefile \ +gbc/Makefile \ +gbx/Makefile \ +lib/Makefile \ +lib/debug/Makefile \ +lib/eval/Makefile \ +lib/db/Makefile \ +lib/vb/Makefile \ +lib/compress/Makefile \ +lib/option/Makefile \ +lib/geom/Makefile \ +lib/draw/Makefile \ +lib/gui/Makefile \ +lib/gui.opengl/Makefile \ +lib/gui.qt/Makefile \ +lib/gui.qt.webkit/Makefile \ +lib/gui.qt.opengl/Makefile \ +lib/gui.trayicon/Makefile \ +lib/image/Makefile \ +lib/image.effect/Makefile \ +lib/signal/Makefile \ +lib/term/Makefile \ +lib/complex/Makefile \ +lib/data/Makefile \ +lib/clipper/Makefile \ +lib/inotify/Makefile \ +lib/jit/Makefile \ +]) +AC_OUTPUT + +GB_PRINT_MESSAGES diff --git a/main/gb.pcre.h b/main/gb.pcre.h new file mode 120000 index 00000000..6d534635 --- /dev/null +++ b/main/gb.pcre.h @@ -0,0 +1 @@ +../gb.pcre/src/gb.pcre.h \ No newline at end of file diff --git a/main/gbc/Makefile.am b/main/gbc/Makefile.am new file mode 100644 index 00000000..7ad0cc10 --- /dev/null +++ b/main/gbc/Makefile.am @@ -0,0 +1,72 @@ +AM_CFLAGS += -I$(top_srcdir)/share +AM_CFLAGS_OPT += -I$(top_srcdir)/share + +bin_PROGRAMS = gbc3 gba3 gbi3 +noinst_PROGRAMS = gbcm3 +##noinst_LTLIBRARIES = libgbcopt.la + +##libgbcopt_la_CFLAGS= -DGAMBAS_PATH="\"$(bindir)\"" $(AM_CFLAGS_OPT) + +gbc3_LDADD = @C_LIB@ @MATH_LIB@ +gbc3_CFLAGS = -DGAMBAS_PATH="\"$(bindir)\"" $(AM_CFLAGS_OPT) + +gbi3_LDADD = @C_LIB@ @DL_LIB@ +gbi3_CFLAGS = -DGAMBAS_PATH="\"$(bindir)\"" $(AM_CFLAGS) + +gba3_LDADD = @C_LIB@ +gba3_CFLAGS = -DGAMBAS_PATH="\"$(bindir)\"" $(AM_CFLAGS) + +gbcm3_LDADD = @C_LIB@ + +gbc3_SOURCES = \ + gb_error.h gb_error.c \ + gb_alloc.c gb_array.c \ + gbc_class.h gbc_class.c \ + gbc_read.h gbc_read.c \ + gbc_preprocess.h gbc_preprocess.c \ + gbc_type.h gbc_type.c \ + gbc_compile.h gbc_compile.c \ + gbc_header.h gbc_header.c \ + gbc_help.h gbc_help.c \ + gbc_output.h gbc_output.c \ + gbc_trans.h gbc_trans_expr.c gbc_trans_tree.c gbc_trans_ctrl.c gbc_trans_subr.c \ + gbc_reserved.c \ + gb_buffer.c \ + gbc_dump.c gbc_code.c gbc_trans.c gbc_trans_code.c \ + gbc_pcode.c \ + gb_file.h gb_file.c \ + gbc_form.h gbc_form.c gbc_form_webpage.c \ + gb_str.h gb_str.c \ + gbc_chown.h gbc_chown.c \ + gb_common.c \ + gbc_arch.c \ + gb_table.c \ + gbc.c + +gba3_SOURCES = \ + gb_error.h gb_error.c \ + gb_alloc.c gb_array.c \ + gb_table.c \ + gb_str.h gb_str.c \ + gb_file.h gb_file.c \ + gbc_archive.h gbc_archive.c \ + gbc_chown.h gbc_chown.c \ + gb_common.c \ + gbc_arch.c \ + gba.c + +gbi3_SOURCES= \ + gb_error.h gb_error.c \ + gb_alloc.c gb_array.c \ + gb_str.h gb_str.c \ + gb_file.h gb_file.c \ + gb_table.c \ + gb_common.c \ + gbc_arch.c \ + gbi.c + +##libgbcopt_la_SOURCES = \ +## gb_table.c + +gbcm3_SOURCES = \ + gbc_reserved_make.c diff --git a/main/gbc/gb_alloc.c b/main/gbc/gb_alloc.c new file mode 100644 index 00000000..2615128f --- /dev/null +++ b/main/gbc/gb_alloc.c @@ -0,0 +1,24 @@ +/*************************************************************************** + + gb_alloc.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_alloc_temp.h" diff --git a/main/gbc/gb_array.c b/main/gbc/gb_array.c new file mode 100644 index 00000000..caaca8d0 --- /dev/null +++ b/main/gbc/gb_array.c @@ -0,0 +1,25 @@ +/*************************************************************************** + + gb_array.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_array_temp.h" + diff --git a/main/gbc/gb_buffer.c b/main/gbc/gb_buffer.c new file mode 100644 index 00000000..99f000e9 --- /dev/null +++ b/main/gbc/gb_buffer.c @@ -0,0 +1,25 @@ +/*************************************************************************** + + gb_buffer.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_buffer_temp.h" + diff --git a/main/gbc/gb_common.c b/main/gbc/gb_common.c new file mode 100644 index 00000000..7cc985e7 --- /dev/null +++ b/main/gbc/gb_common.c @@ -0,0 +1,32 @@ +/*************************************************************************** + + gb_common.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __COMMON_C + +#include "gb_common_case_temp.h" +#include "gb_common_buffer_temp.h" +#include "gb_common_swap_temp.h" + +void COMMON_init(void) +{ +} diff --git a/main/gbc/gb_error.c b/main/gbc/gb_error.c new file mode 100644 index 00000000..687779b7 --- /dev/null +++ b/main/gbc/gb_error.c @@ -0,0 +1,274 @@ +/*************************************************************************** + + gb_error.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_ERROR_C + +#include "gb_common.h" +#include + +#include "gb_buffer.h" +#include "gb_error.h" + + +ERROR_INFO ERROR_info; +bool ERROR_translate = FALSE; + +static const char *_message[] = +{ + /* 0 E_UNKNOWN */ "Unknown error", + /* 1 E_MEMORY */ "Out of memory", + /* 2 E_OPEN */ "Cannot open file: &1", + /* 3 E_READ */ "Cannot read file: &1", + /* 4 E_SYNTAX */ "Syntax error", + /* 5 E_UNEXPECTED */ "Unexpected &1", + /* 6 E_EXPECTED */ "&1 expected", + /* 7 E_MISSING */ "Missing &1", + /* 8 E_SYNTAX_MISSING */ "Syntax error. Missing &1", + /* 9 E_TOOLONG */ "File name is too long", + NULL +}; + +ERROR_CONTEXT *ERROR_current = NULL; + + +#if 0 +void ERROR_enter(ERROR_CONTEXT *err) +{ + CLEAR(err); + err->prev = ERROR_current; + ERROR_current = err; +} + + +void ERROR_leave(ERROR_CONTEXT *err) +{ + if (err->prev != ERROR_LEAVE_DONE) + { + /*ERROR_panic("ERROR_leave already done");*/ + + ERROR_current = err->prev; + err->prev = ERROR_LEAVE_DONE; + } +} +#endif + +char *ERROR_get(void) +{ + /* + if (code > 0 && code < 256) + return strerror(code); + else + return ERROR_Message[code - 256]; + */ + return strerror(errno); +} + + +void _add_char(uchar c, int *n) +{ + if (*n >= MAX_ERROR_MSG) + return; + + ERROR_info.msg[(*n)++] = c; +} + +void _add_string(const char *s, int *n) +{ + while (*s) + { + _add_char(*s, n); + s++; + } +} + +void ERROR_define(const char *pattern, const char *arg[]) +{ + int i, n; + uchar c; + bool subst; + + if ((intptr_t)pattern > 0 && (intptr_t)pattern < 256) + { + ERROR_info.code = (int)(intptr_t)pattern; + pattern = _message[(int)(intptr_t)pattern]; + } + else + ERROR_info.code = -1; + + n = 0; + + subst = FALSE; + + if (ERROR_translate) + { + int nsubst = 0; + + _add_string(pattern, &n); + + for (;;) + { + c = *pattern++; + if (c == 0) + break; + + if (subst) + { + if (c >= '1' && c <= '4') + { + c -= '0'; + if (c > nsubst) nsubst = c; + } + subst = FALSE; + } + else + { + if (c == '&') + subst = TRUE; + } + } + + for (i = 0; i < nsubst; i++) + { + _add_char('\t', &n); + _add_string(arg[i], &n); + } + } + else + { + for (;;) + { + c = *pattern++; + if (c == 0) + break; + + if (subst) + { + if (c >= '1' && c <= '4') + _add_string(arg[c - '1'], &n); + else + { + _add_char('&', &n); + _add_char(c, &n); + } + subst = FALSE; + } + else + { + if (c == '&') + subst = TRUE; + else + _add_char(c, &n); + } + } + } + + _add_char(0, &n); +} + +void PROPAGATE() +{ + ERROR_CONTEXT *err; + + /* + if (_must_free_index) + { + for (i = 0; i < _must_free_index; i++) + OBJECT_UNREF(&_must_free[i], "PROPAGATE"); + + _must_free_index = 0; + } + */ + + if (ERROR_current == NULL) + ERROR_panic("Cannot propagate error. No error handler."); + + err = ERROR_current; + ERROR_leave(ERROR_current); + longjmp(err->env, 1); +} + +void THROW(const char *code, ...) +{ + va_list args; + int i; + const char *arg[4]; + + va_start(args, code); + + for (i = 0; i < 4; i++) + arg[i] = va_arg(args, const char *); + + ERROR_define(code, arg); + PROPAGATE(); +} + +void ERROR_panic(const char *error, ...) +{ + va_list args; + + va_start(args, error); + + fflush(NULL); + + fprintf(stderr, "\n" + "** INTERNAL ERROR **\n" + ); + vfprintf(stderr, error, args); + fprintf(stderr, "\n"); + if (ERROR_info.code) + { + fprintf(stderr, "\n"); + ERROR_print(); + fprintf(stderr, "\n"); + } + fprintf(stderr, "** Program aborting... Sorry... :-(\n\n"); + abort(); +} + + +void ERROR_print_at(FILE *where) +{ + fprintf(where, "%s\n", ERROR_info.msg); +} + +void ERROR_print(void) +{ + ERROR_print_at(stderr); +} + +void TRACE_where(void) +{ +} + +void ERROR_warning(const char *warning, ...) +{ + va_list args; + + va_start(args, warning); + + fflush(NULL); + + fprintf(stderr, "gbc" GAMBAS_VERSION_STRING ": warning: "); + vfprintf(stderr, warning, args); + putc('\n', stderr); +} diff --git a/main/gbc/gb_error.h b/main/gbc/gb_error.h new file mode 100644 index 00000000..777c6ee6 --- /dev/null +++ b/main/gbc/gb_error.h @@ -0,0 +1,127 @@ +/*************************************************************************** + + gb_error.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_ERROR_H +#define __GB_ERROR_H + +#include +#include + +#include "gb_limit.h" + +#define E_UNKNOWN ((const char *)0) +#define E_MEMORY ((const char *)1) +#define E_OPEN ((const char *)2) +#define E_READ ((const char *)3) +#define E_SYNTAX ((const char *)4) +#define E_UNEXPECTED ((const char *)5) +#define E_EXPECTED ((const char *)6) +#define E_MISSING ((const char *)7) +#define E_SYNTAX_MISSING ((const char *)8) +#define E_TOOLONG ((const char *)9) + +typedef + struct { + int code; + char msg[MAX_ERROR_MSG + 1]; + } + ERROR_INFO; + +typedef + struct _ERROR { + struct _ERROR *prev; + int code; + jmp_buf env; + } + ERROR_CONTEXT; + +#ifndef __GB_ERROR_C +EXTERN ERROR_INFO ERROR_info; +EXTERN bool ERROR_translate; +EXTERN ERROR_CONTEXT *ERROR_current; +#endif + + +#define ERROR_LEAVE_DONE ((ERROR_CONTEXT *)-1) + +#define TRY \ + { \ + ERROR_CONTEXT __err_context; \ + { \ + ERROR_CONTEXT *__err = &__err_context; \ + ERROR_enter(__err); \ + __err->code = setjmp(__err->env); \ + if (__err->code == 0) + +#define FINALLY + +#define CATCH \ + if (__err->code != 0 && ((__err->code = 0), TRUE)) + +#define CATCH_GET(get_it) \ + if (__err->code != 0 && ((get_it = __err->code), TRUE) && ((__err->code = 0), TRUE)) + +#define END_TRY \ + if (__err->code == 0) \ + ERROR_leave(__err); \ + else \ + PROPAGATE(); \ + } \ + } + +#define ERROR_enter(_err) \ +do { \ + _err->prev = ERROR_current; \ + _err->code = 0; \ + ERROR_current = _err; \ +} while(0) + +#define ERROR_leave(_err) \ +do { \ + if (_err->prev != ERROR_LEAVE_DONE) \ + { \ + ERROR_CONTEXT *_prev = _err->prev; \ + _err->prev = ERROR_LEAVE_DONE; \ + ERROR_current = _prev; \ + } \ +} while(0) + +#define ERROR_clear() (ERROR_info.code = 0) + +char *ERROR_get(void); + +void ERROR_define(const char *pattern, const char *arg[]); + +void PROPAGATE() NORETURN; +void THROW(const char *code, ...) NORETURN; +void THROW_SYSTEM(int err, const char *path); + +void ERROR_panic(const char *error, ...) NORETURN; + +void ERROR_print(void); +void ERROR_print_at(FILE *where); +void ERROR_warning(const char *warning, ...); + +/*PUBLIC void ERROR_must_free(void *object);*/ + +#endif diff --git a/main/gbc/gb_file.c b/main/gbc/gb_file.c new file mode 100644 index 00000000..ce1232a5 --- /dev/null +++ b/main/gbc/gb_file.c @@ -0,0 +1,29 @@ +/*************************************************************************** + + gb_file.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_alloc.h" +#include "gb_str.h" + +#include "gb_file_temp.h" diff --git a/main/gbc/gb_file.h b/main/gbc/gb_file.h new file mode 100644 index 00000000..2d482a3c --- /dev/null +++ b/main/gbc/gb_file.h @@ -0,0 +1,24 @@ +/*************************************************************************** + + gb_file.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_file_share.h" diff --git a/main/gbc/gb_str.c b/main/gbc/gb_str.c new file mode 100644 index 00000000..71d0d85f --- /dev/null +++ b/main/gbc/gb_str.c @@ -0,0 +1,199 @@ +/*************************************************************************** + + gb_str.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_STR_C + +#include +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_limit.h" +#include "gb_alloc.h" + +#include "gb_str.h" + +static char *_free_later = NULL; +static char *_last_str = NULL; +static int _last_len = 0; + + +void STR_vadd(char **str, const char *fmt, va_list args) +{ + va_list copy; + int len, add; + char *new; + + va_copy(copy, args); + add = vsnprintf(NULL, 0, fmt, args); + + if (*str) + len = (*str == _last_str ? _last_len : strlen(*str)); + else + len = 0; + + ALLOC(&new, len + add + 1); + if (*str) strcpy(new, *str); + + vsprintf(&new[len], fmt, copy); + va_end(copy); + + *str = new; + + _last_str = new; + _last_len = len + add; +} + + +void STR_add(char **str, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + STR_vadd(str, fmt, args); + va_end(args); +} + + +char *STR_copy_len(const char *str, int len) +{ + char *cpy; + + ALLOC(&cpy, len + 1); + memcpy(cpy, str, len + 1); + return cpy; +} + + +char *STR_copy(const char *str) +{ + return STR_copy_len(str, strlen(str)); +} + + +static char *str_add(char *d, const char *s) +{ + for(;;) + { + if ((*d = *s) == 0) + break; + + d++; + s++; + } + + return d; +} + + +char *STR_cat(const char *str, ...) +{ + va_list args; + char *cpy; + char *p; + int len = 0; + + va_start(args, str); + + p = (char *)str; + while (p) + { + len += strlen(p); + p = va_arg(args, char *); + } + + va_end(args); + + ALLOC(&cpy, len + 1); + p = cpy; + + va_start(args, str); + + while (str) + { + p = str_add(p, str); + str = va_arg(args, char *); + } + + va_end(args); + + return cpy; +} + + +char *STR_upper(const char *str) +{ + char *s; + char *p; + + p = s = STR_copy(str); + while (*p) + { + *p = toupper(*p); + p++; + } + + return s; +} + + +char *STR_lower(const char *str) +{ + char *s; + char *p; + + p = s = STR_copy(str); + while (*p) + { + *p = tolower(*p); + p++; + } + + return s; +} + + +char *STR_free_later(char *str) +{ + if (_free_later) + STR_free(_free_later); + _free_later = str; + return str; +} + + +char *STR_print(const char *fmt, ...) +{ + va_list args; + char *str = NULL; + + va_start(args, fmt); + STR_vadd(&str, fmt, args); + va_end(args); + return str; +} + diff --git a/main/gbc/gb_str.h b/main/gbc/gb_str.h new file mode 100644 index 00000000..9c06b43b --- /dev/null +++ b/main/gbc/gb_str.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + gb_str.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_STR_H +#define __GB_STR_H + +#include "gb_alloc.h" + +char *STR_copy(const char *str); +char *STR_copy_len(const char *str, int len); +char *STR_cat(const char *str, ...); +char *STR_upper(const char *str); +char *STR_lower(const char *str); + +#define STR_free(_str) IFREE(_str) +char *STR_free_later(char *str); + +void STR_add(char **str, const char *fmt, ...); +char *STR_print(const char *fmt, ...); +void STR_vadd(char **str, const char *fmt, va_list args); + +#endif diff --git a/main/gbc/gb_table.c b/main/gbc/gb_table.c new file mode 100644 index 00000000..e4b89562 --- /dev/null +++ b/main/gbc/gb_table.c @@ -0,0 +1,26 @@ +/*************************************************************************** + + gb_table.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common_case.h" +#include "gb_table_temp.h" + diff --git a/main/gbc/gba.c b/main/gbc/gba.c new file mode 100644 index 00000000..269cf582 --- /dev/null +++ b/main/gbc/gba.c @@ -0,0 +1,415 @@ +/*************************************************************************** + + gba.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBA_C + +#include "config.h" +#include "trunk_version.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_str.h" +#include "gb_file.h" +#include "gb_array.h" +#include "gb_common_buffer.h" + +#include "gbc_archive.h" + +#if HAVE_GETOPT_LONG +static struct option Long_options[] = +{ + { "version", 0, NULL, 'V' }, + { "help", 0, NULL, 'h' }, + { "swap", 0, NULL, 's' }, + { "verbose", 0, NULL, 'v' }, + { "output", 1, NULL, 'o' }, + { "extract", 1, NULL, 'x' }, + { 0 } +}; +#endif + +static char **path_list; +static int path_current; + +static const char *allowed_hidden_files[] = { ".gambas", ".info", ".list", ".lang", ".action", ".connection", ".component", ".public", NULL }; +//static const char *remove_ext_root[] = { "module", "class", "form", "gambas", NULL }; +static const char *remove_ext_lang[] = { "pot", "po", NULL }; + +static bool _extract = FALSE; +static char *_extract_archive; +static char *_extract_file = NULL; + +static void get_arguments(int argc, char **argv) +{ + int opt; + #if HAVE_GETOPT_LONG + int index = 0; + #endif + + for(;;) + { + #if HAVE_GETOPT_LONG + opt = getopt_long(argc, argv, "vVLhso:x:", Long_options, &index); + #else + opt = getopt(argc, argv, "vVLhso:x:"); + #endif + + if (opt < 0) break; + + switch (opt) + { + case 'V': + #ifdef TRUNK_VERSION + #ifdef TRUNK_VERSION_GIT + printf(VERSION " " TRUNK_VERSION "\n"); + #else /* from svn */ + printf(VERSION " r" TRUNK_VERSION "\n"); + #endif + #else /* no TRUNK_VERSION */ + printf(VERSION "\n"); + #endif + exit(0); + + case 'v': + ARCH_verbose = TRUE; + break; + + case 's': + ARCH_swap = TRUE; + break; + + case 'o': + ARCH_define_output(optarg); + _extract = FALSE; + break; + + case 'x': + _extract_archive = optarg; + _extract = TRUE; + break; + + case 'L': + printf( + "\nGAMBAS Archiver version " VERSION "\n" + COPYRIGHT + ); + exit(0); + + case 'h': case '?': + printf( + "\nCreate a standalone one-file executable from a Gambas project.\n" + "Or extract a specific file from a Gambas executable (-x option).\n" + "\nUsage: gba" GAMBAS_VERSION_STRING " [options] []\n" + " gba" GAMBAS_VERSION_STRING " -x \n\n" + "Options:" + #if HAVE_GETOPT_LONG + "\n" + " -o --output=ARCHIVE archive path [/.gambas]\n" + " -x --extract=ARCHIVE archive path\n" + " -v --verbose verbose output\n" + " -s --swap swap endianness\n" + " -V --version display version\n" + " -L --license display license\n" + " -h --help display this help\n" + #else + " (no long options on this system)\n" + " -o=ARCHIVE archive path [/.gambas]\n" + " -x=ARCHIVE archive path\n" + " -v verbose output\n" + " -s swap endianness\n" + " -V display version\n" + " -L display license\n" + " -h display this help\n" + #endif + "\n" + ); + + exit(0); + + default: + exit(1); + + } + } + + if (optind < (argc - 1)) + { + fprintf(stderr, "gba: too many arguments.\n"); + exit(1); + } + + if (_extract) + { + if (optind == argc) + { + fprintf(stderr, "gba: not enough arguments.\n"); + exit(1); + } + _extract_file = argv[optind]; + } + else + { + if (optind == argc) + ARCH_define_project(NULL); + else + ARCH_define_project(argv[optind]); + + if (!FILE_exist(ARCH_project)) + { + fprintf(stderr, "gba: project file not found: %s\n", ARCH_project); + exit(1); + } + } +} + + +static void path_add(const char *path) +{ + *((char **)ARRAY_add(&path_list)) = STR_copy(path); +} + + +static void path_init(const char *first) +{ + ARRAY_create(&path_list); + + if (*first) + FILE_chdir(first); + + path_add(FILE_get_current_dir()); + + path_current = 0; +} + + +static void path_exit(void) +{ + ARRAY_delete(&path_list); +} + + +static int path_count(void) +{ + return ARRAY_count(path_list); +} + + +int main(int argc, char **argv) +{ + const char *path; + int nfile; + struct dirent **filelist; + struct dirent *dirent; + char *file_name; + const char *file; + struct stat info; + const char *ext; + int len; + const char **p; + int len_prefix; + const char **remove_ext; + ARCH *arch; + ARCH_FIND find; + int i; + + get_arguments(argc, argv); + COMMON_init(); + + TRY + { + if (_extract) // Extract a file from an archive + { + arch = ARCH_open(_extract_archive); + + if (ARCH_find(arch, _extract_file, 0, &find)) + fprintf(stderr, "gba: file not found in archive\n"); + else + fwrite(&arch->addr[find.pos], sizeof(char), find.len, stdout); + + ARCH_close(arch); + } + else // Create an archive + { + ARCH_init(); + + file = FILE_get_dir(ARCH_project); + len_prefix = strlen(file); + path_init(file); + + /* .startup and .project file always first ! */ + + path = FILE_cat(FILE_get_dir(ARCH_project), ".startup", NULL); + if (FILE_exist(path)) ARCH_add_file(path); + + path = FILE_cat(FILE_get_dir(ARCH_project), ".project", NULL); + if (FILE_exist(path)) ARCH_add_file(path); + + for(;;) + { + if (path_current >= path_count()) + break; + + path = path_list[path_current++]; + + if (chdir(path) != 0) + { + fprintf(stderr, "gba: warning: cannot change to directory: %s\n", path); + goto _NEXT_PATH; + } + + filelist = NULL; + nfile = scandir(path, &filelist, NULL, alphasort); + + if (nfile < 0) + { + fprintf(stderr, "gba: warning: cannot scan directory: %s\n", path); + goto _NEXT_PATH; + } + + for (i = 0; i < nfile; i++) + { + dirent = filelist[i]; + + file_name = dirent->d_name; + len = strlen(file_name); + + if (*file_name == '.') + { + for (p = allowed_hidden_files; *p; p++) + { + if (strcmp(file_name, *p) == 0) + break; + } + + if (*p == NULL) + continue; + } + + if (file_name[len - 1] == '~') + continue; + + //if (strcmp(file_name, ARCH_project_name) == 0) + // continue; + + if ((len == 4) && (strncmp(file_name, "core", 4) == 0)) + continue; + + if ((len > 5) && (strncmp(file_name, "core.", 5) == 0)) + continue; + + if ((len > 7) && (strncmp(file_name, "vgcore.", 5) == 0)) + continue; + + if ((len > 10) && (strncmp(file_name, "callgrind.", 5) == 0)) + continue; + + file = FILE_cat(path, file_name, NULL); + + // Do not put the archive file inside itself. + if (!strcmp(file, ARCH_output)) + continue; + + if (stat(file_name, &info)) + { + fprintf(stderr, "gba: warning: cannot stat file: %s\n", file); + continue; + } + + if (S_ISDIR(info.st_mode)) + { + if (strcmp(file_name, "CVS") == 0) + continue; + + path_add(file); + ARCH_add_file(file); + } + else + { + ext = FILE_get_ext(file_name); + + //printf("path = %s\n", &path[len_prefix]); + + //if (path[len_prefix] == 0) + // remove_ext = remove_ext_root; + if (strcmp(&path[len_prefix], "/.lang") == 0) + remove_ext = remove_ext_lang; + else + remove_ext = 0; + + if (remove_ext) + { + for (p = remove_ext; *p; p++) + { + if (strcasecmp(ext, *p) == 0) + break; + } + + if (*p != NULL) + continue; + } + + if (strcmp(ext, "gambas") == 0) + continue; + + ARCH_add_file(file); + } + + free(dirent); + } + + _NEXT_PATH: + if (filelist != NULL) + free(filelist); + + FREE((char **)&path); + } + + path_exit(); + + ARCH_exit(); + /*MEM_check();*/ + } + } + CATCH + { + fflush(NULL); + fprintf(stderr, "gba: ERROR: "); + ERROR_print(); + exit(1); + } + END_TRY + + return 0; +} + diff --git a/main/gbc/gbc.c b/main/gbc/gbc.c new file mode 100644 index 00000000..f175c605 --- /dev/null +++ b/main/gbc/gbc.c @@ -0,0 +1,520 @@ +/*************************************************************************** + + gbc.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_C + +#include "config.h" +#include "trunk_version.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_str.h" +#include "gb_file.h" +#include "gb_common_buffer.h" + +#include "gbc_compile.h" + +#include "gb_reserved.h" +#include "gbc_read.h" +#include "gbc_form.h" +#include "gbc_trans.h" +#include "gbc_header.h" +#include "gbc_output.h" + + +#if HAVE_GETOPT_LONG +static struct option Long_options[] = +{ + { "debug", 0, NULL, 'g' }, + { "version", 0, NULL, 'V' }, + { "help", 0, NULL, 'h' }, + { "license", 0, NULL, 'L' }, + { "verbose", 0, NULL, 'v' }, + { "translate", 0, NULL, 't' }, + { "public-control", 0, NULL, 'p' }, + { "public-module", 0, NULL, 'm' }, + { "swap", 0, NULL, 's' }, + { "class", 1, NULL, 'c' }, + /*{ "dump", 0, NULL, 'd' },*/ + { "root", 1, NULL, 'r' }, + { "all", 0, NULL, 'a' }, + { "translate-errors", 0, NULL, 'e' }, + { "no-old-read-write-syntax", 0, NULL, 1 }, + { 0 } +}; +#endif + +static bool main_debug = FALSE; +static bool main_exec = FALSE; +static bool main_verbose = FALSE; +static bool main_compile_all = FALSE; +static bool main_trans = FALSE; +static bool main_warnings = FALSE; +static bool main_public = FALSE; +static bool main_public_module = FALSE; +static bool main_swap = FALSE; +static bool main_no_old_read_syntax = FALSE; +//static char *main_class_file = NULL; + +static char **_files = NULL; + +static void get_arguments(int argc, char **argv) +{ + const char *dir; + int opt; + #if HAVE_GETOPT_LONG + int index = 0; + #endif + + for(;;) + { + #if HAVE_GETOPT_LONG + opt = getopt_long(argc, argv, "gxvaVhLwtpmser:", Long_options, &index); + #else + opt = getopt(argc, argv, "gxvaVhLwtpmser:"); + #endif + if (opt < 0) break; + + switch (opt) + { + case 'V': + #ifdef TRUNK_VERSION + #ifdef TRUNK_VERSION_GIT + printf(VERSION " " TRUNK_VERSION "\n"); + #else /* from svn */ + printf(VERSION " r" TRUNK_VERSION "\n"); + #endif + #else /* no TRUNK_VERSION */ + printf(VERSION "\n"); + #endif + exit(0); + + case 'g': + main_debug = TRUE; + break; + + case 'x': + main_exec = TRUE; + break; + + case 'v': + main_verbose = TRUE; + break; + + case 'a': + main_compile_all = TRUE; + break; + + case 't': + main_trans = TRUE; + break; + + case 'w': + main_warnings = TRUE; + break; + + case 'p': + main_public = TRUE; + break; + + case 'm': + main_public_module = TRUE; + break; + + case 's': + main_swap = TRUE; + break; + + //case 'c': + // main_class_file = optarg; + //: break; + + case 'r': + if (COMP_root) + { + fprintf(stderr, "gbc" GAMBAS_VERSION_STRING ": option '-r' already specified.\n"); + exit(1); + } + COMP_root = STR_copy(optarg); + break; + + case 'e': + ERROR_translate = TRUE; + break; + + case 1: + main_no_old_read_syntax = TRUE; + break; + + case 'L': + printf( + "\nGAMBAS Compiler version " VERSION "\n" + COPYRIGHT + ); + exit(0); + + case 'h': case '?': + printf( + "\nCompile Gambas projects into architecture-independent bytecode.\n" + "\nUsage: gbc" GAMBAS_VERSION_STRING " [options] []\n\n" + "Options:" + #if HAVE_GETOPT_LONG + "\n" + " -g --debug add debugging information\n" + " -v --verbose verbose output\n" + " -a --all compile all\n" + " -w --warnings display warnings\n" + " -t --translate output translation files\n" + " -p --public-control form controls are public\n" + " -m --public-module module symbols are public by default\n" + " -s --swap swap endianness\n" + + " -r --root gives the gambas installation directory\n" + " -e --translate-errors display translatable error messages\n" + " -x --exec executable mode (define the 'Exec' preprocessor constant and remove assertions)\n" + " -V --version display version\n" + " -L --license display license\n" + " -h --help display this help\n" + #else + " (no long options on this system)\n" + " -g add debugging information\n" + " -v verbose output\n" + " -a compile all\n" + " -w display warnings\n" + " -t output translation files\n" + " -p form controls are public\n" + " -m module symbols are public by default\n" + " -s swap endianness\n" + " -r gives the gambas installation directory\n" + " -e display translatable error messages\n" + " -x executable mode (define the 'Exec' preprocessor constant and remove assertions)\n" + " -V display version\n" + " -L display license\n" + " -h display this help\n" + #endif + "\n" + ); + + exit(0); + + default: + exit(1); + + } + } + + if (optind < (argc - 1)) + { + fprintf(stderr, "gbc" GAMBAS_VERSION_STRING ": too many arguments.\n"); + exit(1); + } + + /*COMP_project = STR_copy(FILE_cat(argv[optind], "Gambas", NULL));*/ + if (optind < argc) + FILE_chdir(argv[optind]); + + dir = FILE_get_current_dir(); + if (!dir) + { + fprintf(stderr, "gbc" GAMBAS_VERSION_STRING ": no current directory.\n"); + exit(1); + } + + COMP_project = STR_copy(FILE_cat(dir, ".project", NULL)); + + if (!FILE_exist(COMP_project)) + { + fprintf(stderr, "gbc" GAMBAS_VERSION_STRING ": project file not found: %s\n", COMP_project); + exit(1); + } +} + + +static void compile_file(const char *file) +{ + int i; + time_t time_src, time_form, time_pot, time_output; + char *source; + + COMPILE_begin(file, main_trans, main_debug); + + if (!main_compile_all) + { + if (FILE_exist(JOB->output)) + { + time_src = FILE_get_time(JOB->name); + time_output = FILE_get_time(JOB->output); + + if (JOB->form) + time_form = FILE_get_time(JOB->form); + else + time_form = time_src; + + if (main_trans) + time_pot = FILE_get_time(JOB->tname); + else + time_pot = time_src; + + if (time_src <= time_output && time_src <= time_pot && time_form <= time_output) + goto _FIN; + } + } + + JOB->all = main_compile_all; + JOB->exec = main_exec; + JOB->verbose = main_verbose; + JOB->warnings = main_warnings; + JOB->swap = main_swap; + JOB->public_module = main_public_module; + JOB->no_old_read_syntax = main_no_old_read_syntax; + //JOB->class_file = main_class_file; + + if (JOB->verbose) + { + putchar('\n'); + for (i = 1; i <= 9; i++) + printf("--------"); + printf("\nCompiling %s...\n", FILE_get_name(JOB->name)); + } + + JOB->first_line = 1; + + if (JOB->form) + { + JOB->first_line = FORM_FIRST_LINE; + BUFFER_add(&JOB->source, "#Line " FORM_FIRST_LINE_STRING "\n", -1); + + BUFFER_create(&source); + BUFFER_load_file(&source, JOB->form); + BUFFER_add(&source, "\n\0", 2); + + switch (JOB->family->type) + { + case FORM_WEBPAGE: + FORM_webpage(source); + break; + + case FORM_NORMAL: + default: + FORM_do(source, main_public); + break; + } + + BUFFER_delete(&source); + + BUFFER_add(&JOB->source, "#Line 1\n", -1); + } + + COMPILE_load(); + BUFFER_add(&JOB->source, "\n\0", 2); + + #if 0 + fprintf(stderr, "-----------------\n"); + fputs(JOB->source, stderr); + fprintf(stderr, "-----------------\n"); + #endif + + READ_do(); + + #ifdef DEBUG + TABLE_print(JOB->class->table, TRUE); + #endif + + HEADER_do(); + TRANS_code(); + + #ifdef DEBUG + TABLE_print(JOB->class->string, FALSE); + #endif + + OUTPUT_do(main_swap); + CLASS_export(); + +_FIN: + COMPILE_end(); +} + + +static int compare_path(char **a, char **b) +{ + return strcmp(*a, *b); +} + +static void fill_files(const char *root, bool recursive) +{ + DIR *dir; + char *path; + struct dirent *dirent; + char *file_name; + const char *file; + struct stat info; + const char *ext; + + path = STR_copy(root); + + dir = opendir(path); + if (!dir) + { + fprintf(stderr, "gbc" GAMBAS_VERSION_STRING ": cannot browse directory: %s\n", path); + exit(1); + } + + while ((dirent = readdir(dir)) != NULL) + { + file_name = dirent->d_name; + if (*file_name == '.') + continue; + + file = FILE_cat(path, file_name, NULL); + + if (stat(file, &info)) + { + fprintf(stderr, "gbc" GAMBAS_VERSION_STRING ": warning: cannot stat file: %s\n", file); + continue; + } + + if (S_ISDIR(info.st_mode)) + { + if (recursive) + fill_files(file, TRUE); + } + else + { + ext = FILE_get_ext(file); + + if ((strcmp(ext, "module") == 0) + || (strcmp(ext, "class") == 0)) + { + *((char **)ARRAY_add(&_files)) = STR_copy(file); + } + } + } + + closedir(dir); + STR_free(path); +} + +static void init_files(const char *first) +{ + bool recursive; + const char *name; + int i, n; + + ARRAY_create(&_files); + + if (*first) + FILE_chdir(first); + + recursive = chdir(".src") == 0; + fill_files(FILE_get_current_dir(), recursive); + if (recursive) FILE_chdir(".."); + + // Sort paths + n = ARRAY_count(_files); + qsort(_files, n, sizeof(*_files), (int (*)(const void *, const void *))compare_path); + + // Add the classes to the list of classes + for (i = 0; i < n; i++) + { + name = FILE_get_basename(_files[i]); + COMPILE_add_class(name, strlen(name)); + } + + // End the list of classes + COMPILE_end_class(); +} + + +static void exit_files(void) +{ + int i; + + for (i = 0; i < ARRAY_count(_files); i++) + STR_free(_files[i]); + + ARRAY_delete(&_files); +} + + +int main(int argc, char **argv) +{ + int i; + + MEMORY_init(); + COMMON_init(); + + TRY + { + get_arguments(argc, argv); + + COMPILE_init(); + + // Remove information files if we are compiling everything + + if (main_compile_all) + { + if (main_verbose) + puts("Removing .info and .list files"); + FILE_chdir(FILE_get_dir(COMP_project)); + FILE_unlink(".info"); + FILE_unlink(".list"); + } + + init_files(FILE_get_dir(COMP_project)); + + for (i = 0; i < ARRAY_count(_files); i++) + compile_file(_files[i]); + + exit_files(); + + COMPILE_exit(); + FILE_exit(); + + puts("OK"); + } + CATCH + { + fflush(NULL); + + COMPILE_print(MSG_ERROR, -1, NULL); + ERROR_print(); + exit(1); + } + END_TRY + + return 0; +} + diff --git a/main/gbc/gbc_arch.c b/main/gbc/gbc_arch.c new file mode 100644 index 00000000..61506078 --- /dev/null +++ b/main/gbc/gbc_arch.c @@ -0,0 +1,43 @@ +/*************************************************************************** + + gbc_arch.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_ARCH_C + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_str.h" +#include "gb_file.h" + +#include "gb_arch_temp.h" diff --git a/main/gbc/gbc_archive.c b/main/gbc/gbc_archive.c new file mode 100644 index 00000000..a86d111c --- /dev/null +++ b/main/gbc/gbc_archive.c @@ -0,0 +1,444 @@ +/*************************************************************************** + + gbc_archive.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_ARCHIVE_C + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_str.h" +#include "gb_file.h" +#include "gb_magic.h" +#include "gb_common_swap.h" +#include "gbc_chown.h" +#include "gbc_archive.h" + +/*#define DEBUG*/ + +#define TEMP_EXEC ".temp.gambas" + +char *ARCH_project; +char *ARCH_project_name; +char *ARCH_output = NULL; +bool ARCH_verbose = FALSE; +bool ARCH_swap = FALSE; + +static int arch_dir_pos; +static TABLE *arch_table; +static FILE *arch_file = NULL; + +#define ARCH_BUFFER_SIZE 4096 +static char *arch_buffer; + +static int pos_start; + +static void write_int(uint val) +{ + if (ARCH_swap) + SWAP_int((int *)&val); + if (fwrite(&val, sizeof(uint), 1, arch_file) < 1) + THROW("Write error"); +} + + +static void write_short(ushort val) +{ + if (ARCH_swap) + SWAP_short((short *)&val); + if (fwrite(&val, sizeof(ushort), 1, arch_file) < 1) + THROW("Write error"); +} + + +static long get_pos(void) +{ + long pos = ftell(arch_file); + if (pos < 0) + THROW("Unable to get file position"); + return (int)pos; // No archive file greater then 2 Go! +} + + +static void write_int_at(int pos, uint val) +{ + int old_pos = get_pos(); + + fseek(arch_file, pos, SEEK_SET); + write_int(val); + fseek(arch_file, old_pos, SEEK_SET); +} + + +static void write_string(const char *str, int len) +{ + if (fwrite(str, sizeof(char), len, arch_file) < len) + THROW("Write error"); +} + + +static void make_executable(void) +{ + const char *err; + struct stat info; + + FILE_chdir(FILE_get_dir(ARCH_output)); + + // If we cannot make the archive executable, just print a warning. gbs creates an executable cache + // inside /tmp, and /tmp may be mounted with the "noexec" flag. + + if (stat(TEMP_EXEC, &info) || chmod(TEMP_EXEC, info.st_mode | S_IXUSR | S_IXGRP | S_IXOTH)) + fprintf(stderr, "gba: warning: cannot change executable permissions\n"); + + FILE_set_owner(TEMP_EXEC, FILE_cat(FILE_get_dir(ARCH_project), ".project", NULL)); + + if (FILE_exist(ARCH_output) && unlink(ARCH_output)) + { + err = "Cannot remove previous executable"; + goto __ERROR; + } + + if (rename(TEMP_EXEC, ARCH_output)) + { + err = "Cannot create executable"; + goto __ERROR; + } + + return; + +__ERROR: + + THROW("Cannot make executable: &1: &2", err, strerror(errno)); +} + + +void ARCH_define_output(const char *path) +{ + STR_free(ARCH_output); + + if (path && *path != '/') + path = FILE_cat(FILE_get_current_dir(), path, NULL); + else if (!path) + path = ""; + + ARCH_output = STR_copy(path); +} + +static void print_separation() +{ + printf("---------------------------------------- ------------------------------ -------- -----\n"); +} + +void ARCH_define_project(const char *project) +{ + char *name; + char *dir; + const char *path; + + if (project == NULL) + project = FILE_get_current_dir(); + + FILE_chdir(project); + dir = STR_copy(FILE_get_current_dir()); + + arch_dir_pos = strlen(dir) + 1; + + path = FILE_cat(dir, ".startup", NULL); + if (FILE_exist(path)) + ARCH_project = STR_copy(path); + else + ARCH_project = STR_copy(FILE_cat(dir, ".project", NULL)); + + name = STR_copy(FILE_get_name(dir)); + + /*ARCH_project_name = STR_copy(FILE_set_ext(name, NULL));*/ + ARCH_project_name = STR_copy(name); + + if (!ARCH_output) + ARCH_define_output(strcat((char *)FILE_cat(dir, ARCH_project_name, NULL), ".gambas")); + + STR_free(name); + STR_free(dir); +} + + +void ARCH_init(void) +{ + TABLE_create(&arch_table, sizeof(ARCH_SYMBOL), TF_NORMAL); + + ALLOC(&arch_buffer, ARCH_BUFFER_SIZE); + + arch_file = fopen(FILE_cat(FILE_get_dir(ARCH_output), TEMP_EXEC, NULL), "w"); + if (arch_file == NULL) + THROW("Cannot create temporary archive file: &1", ARCH_output); + + fputs("#! /usr/bin/env gbr" GAMBAS_VERSION_STRING "\n", arch_file); + + while (get_pos() < 31) + fprintf(arch_file, " "); + fprintf(arch_file, "\n"); + + write_int(ARCH_MAGIC); + write_int(ARCH_VERSION); + + if (ARCH_verbose) + { + print_separation(); + printf("%-40.40s %-30.30s %8s %5s\n", "Path", "Abbrev. path","Size","Index"); + print_separation(); + } + + pos_start = get_pos(); + write_int(0); + write_int(0); + write_int(0); + write_int(0); + + write_int_at(pos_start, get_pos()); +} + + +#if ARCH_VERSION == 2 +static void compress_file_name(const char *src, int lsrc, char **dst, int *ldst) +{ + char *p; + static char tpath[PATH_MAX]; + char tpath2[PATH_MAX]; + int len; + int ind; + + strncpy(tpath, src, lsrc); + tpath[lsrc] = 0; + len = lsrc; + + if (ARCH_verbose) + printf("%-40.40s", tpath); + + for(;;) + { + p = index(tpath + 1, '/'); + if (!p) + break; + + if (!TABLE_find_symbol(arch_table, tpath, p - tpath, &ind)) + { + *p = 0; + THROW("&1: not in archive", tpath); + } + + len = snprintf(tpath2, sizeof(tpath2), "/%d:%s", ind, p + 1); + strcpy(tpath, tpath2); + } + + if (ARCH_verbose) + printf(" %-30.30s", tpath); + + *dst = tpath; + *ldst = len; +} +#endif + +void ARCH_exit(void) +{ + int i; + ARCH_SYMBOL *sym; + int pos_str; + + /* Write strings */ + + write_int_at(pos_start + sizeof(int), get_pos()); + + pos_str = 0; + + for (i = 0; i < TABLE_count(arch_table); i++) + { + sym = (ARCH_SYMBOL *)TABLE_get_symbol(arch_table, i); + write_string(sym->sym.name, sym->sym.len); + } + + /* Write file names */ + + write_int_at(pos_start + sizeof(int) * 2, get_pos()); + + write_int_at(pos_start + sizeof(int) * 3, TABLE_count(arch_table)); + + for (i = 0; i < TABLE_count(arch_table); i++) + { + sym = (ARCH_SYMBOL *)TABLE_get_symbol(arch_table, i); + //write_short((ushort)i); + write_int(pos_str); + write_int(sym->sym.len); + write_int(sym->pos); + write_int(sym->len); + + pos_str += sym->sym.len; + } + + for (i = 0; i < TABLE_count(arch_table); i++) + write_short(arch_table->sort[i]); + + /* Close file */ + + fclose(arch_file); + + make_executable(); + + if (ARCH_verbose) + { + print_separation(); + printf("Writing archive format version %d:\n%s\n", ARCH_VERSION,ARCH_output); + } + + /* Free everything */ + + for (i = 0; i < TABLE_count(arch_table); i++) + STR_free(TABLE_get_symbol(arch_table, i)->name); + + TABLE_delete(&arch_table); + + STR_free(ARCH_output); + STR_free(ARCH_project); + STR_free(ARCH_project_name); + + FREE(&arch_buffer); +} + + +int ARCH_add_file(const char *path) +{ + char *rel_path; + ARCH_SYMBOL *sym; + FILE *file; + struct stat info; + int len, len_read; + + int ind; + + #if ARCH_VERSION == 2 + compress_file_name(&path[arch_dir_pos], strlen(&path[arch_dir_pos]), &rel_path, &len); + rel_path = STR_copy(rel_path); + #else + rel_path = STR_copy(&path[arch_dir_pos]); + len = strlen(rel_path); + #endif + + TABLE_add_symbol(arch_table, rel_path, len, &ind); + sym = (ARCH_SYMBOL *)TABLE_get_symbol(arch_table, ind); + sym->pos = get_pos(); + + file = fopen(path, "r"); + if (file == NULL) + THROW("Cannot open file: &1", path); + + fstat(fileno(file), &info); + + if (S_ISDIR(info.st_mode)) + { + sym->pos = -1; + sym->len = 0; + fclose(file); + if (ARCH_verbose) + printf(" %8s", "-"); + } + else + { + sym->len = info.st_size; + + len = sym->len; + while (len > 0) + { + len_read = fread(arch_buffer, 1, ARCH_BUFFER_SIZE, file); + if (len_read > 0) + fwrite(arch_buffer, 1, len_read, arch_file); + + if (len_read < ARCH_BUFFER_SIZE) + { + if (ferror(file)) + THROW("Cannot read file: &1: &2", path, strerror(errno)); + else + break; + } + } + + fclose(file); + + if (ARCH_verbose) + printf(" %8d", sym->len); + } + + if (ARCH_verbose) + printf(" %5d\n", ind); + + return ind; +} + +#if 0 +void ARCH_browse(ARCH *a, void (*found)(const char *path, int64_t size)) +{ + int i; + ARCH_SYMBOL *asym; + SYMBOL *sym; + char *path; + char *temp; + int size; + int ip; + + for (i = 0; i < a->header.n_symbol; i++) + { + asym = &a->symbol[i]; + sym = &asym->sym; + + size = asym->len; + + path = STR_copy_len(sym->name, sym->len); + for(;;) + { + if (*path != '/') + break; + + ip = atoi(&path[1]); + sym = &a->symbol[ip].sym; + + temp = path; + path = STR_copy_len(sym->name, sym->len); + if (path[sym->len - 1] != '/') + STRING_add(&path, "/", 1); + STRING_add(&path, strchr(temp, ':') + 1, 0); + STRING_free(&temp); + } + + (*found)(path, size); + STR_free(path); + } +} +#endif \ No newline at end of file diff --git a/main/gbc/gbc_archive.h b/main/gbc/gbc_archive.h new file mode 100644 index 00000000..0242fd18 --- /dev/null +++ b/main/gbc/gbc_archive.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + gbc_archive.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_ARCHIVE_H +#define __GBC_ARCHIVE_H + +#include "gb_alloc.h" +#include "gb_limit.h" +#include "gb_table.h" +#include "gb_magic.h" +#include "gb_arch.h" + +#ifndef __GBC_ARCHIVE_C + +EXTERN char *ARCH_project; +EXTERN char *ARCH_project_name; +EXTERN char *ARCH_output; +EXTERN bool ARCH_verbose; +EXTERN bool ARCH_swap; + +#endif + +void ARCH_init(void); +void ARCH_exit(void); +void ARCH_define_project(const char *project); +void ARCH_define_output(const char *path); +int ARCH_add_file(const char *path); + +#endif diff --git a/main/gbc/gbc_chown.c b/main/gbc/gbc_chown.c new file mode 100644 index 00000000..873cc161 --- /dev/null +++ b/main/gbc/gbc_chown.c @@ -0,0 +1,52 @@ +/*************************************************************************** + + gbc_chown.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_CHOWN_C + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gbc_chown.h" + +void FILE_set_owner(const char *path, const char *template) +{ + struct stat info; + + if (geteuid()) + return; + + if (stat(template, &info)) + goto __ERROR; + + if (chown(path, info.st_uid, info.st_gid)) + goto __ERROR; + + return; + +__ERROR: + unlink(path); + THROW("Cannot set file owner: &1: &2", path, strerror(errno)); +} diff --git a/main/gbc/gbc_chown.h b/main/gbc/gbc_chown.h new file mode 100644 index 00000000..9c3a09e6 --- /dev/null +++ b/main/gbc/gbc_chown.h @@ -0,0 +1,29 @@ +/*************************************************************************** + + gbc_chown.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_CHOWN_H +#define __GBC_CHOWN_H + +void FILE_set_owner(const char *path, const char *template); + +#endif diff --git a/main/gbc/gbc_class.c b/main/gbc/gbc_class.c new file mode 100644 index 00000000..956c8166 --- /dev/null +++ b/main/gbc/gbc_class.c @@ -0,0 +1,1013 @@ +/*************************************************************************** + + gbc_class.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_CLASS_C + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_str.h" +#include "gb_file.h" + +#include "gb_table.h" +#include "gbc_compile.h" +#include "gb_code.h" + +static int _array_class[17]; + +void CLASS_create(CLASS **result) +{ + CLASS *class; + TRANS_FUNC func; + + ALLOC_ZERO(&class, sizeof(CLASS)); + + ARRAY_create_inc(&class->function, 256); + ARRAY_create(&class->event); + ARRAY_create(&class->prop); + ARRAY_create(&class->ext_func); + ARRAY_create_inc(&class->constant, 256); + ARRAY_create(&class->class); + ARRAY_create_inc(&class->unknown, 256); + ARRAY_create_inc(&class->stat, 256); + ARRAY_create_inc(&class->dyn, 256); + ARRAY_create(&class->array); + ARRAY_create(&class->structure); + ARRAY_create(&class->names); + + TABLE_create(&class->table, sizeof(CLASS_SYMBOL), TF_IGNORE_CASE); + TABLE_create(&class->string, sizeof(SYMBOL), TF_NORMAL); + + CLEAR(&func); + TYPE_clear(&func.type); + + func.index = CLASS_add_symbol(class, "@init"); + CLASS_add_function(class, &func); + + func.index = CLASS_add_symbol(class, "@new"); + CLASS_add_function(class, &func); + + class->name = STR_copy(FILE_set_ext(FILE_get_name(JOB->name), NULL)); + class->parent = NO_SYMBOL; + + memset(_array_class, 0, sizeof(_array_class)); + + *result = class; +} + + +static void delete_function(FUNCTION *func) +{ + ARRAY_delete(&func->local); + if (func->code) + FREE(&func->code); + + if (JOB->debug) + ARRAY_delete(&func->pos_line); + + if (func->param) + FREE(&func->param); +} + + +static void delete_event(EVENT *event) +{ + if (event->param) + FREE(&event->param); +} + + +static void delete_extfunc(EXTFUNC *extfunc) +{ + if (extfunc->param) + FREE(&extfunc->param); +} + + +static void delete_structure(CLASS_STRUCT *structure) +{ + if (structure->field) + ARRAY_delete(&structure->field); +} + + +void CLASS_delete(CLASS **class) +{ + int i; + + if (*class) + { + TABLE_delete(&((*class)->table)); + TABLE_delete(&((*class)->string)); + + for (i = 0; i < ARRAY_count((*class)->function); i++) + delete_function(&((*class)->function[i])); + + for (i = 0; i < ARRAY_count((*class)->event); i++) + delete_event(&((*class)->event[i])); + + for (i = 0; i < ARRAY_count((*class)->ext_func); i++) + delete_extfunc(&((*class)->ext_func[i])); + + for (i = 0; i < ARRAY_count((*class)->structure); i++) + delete_structure(&((*class)->structure[i])); + + for (i = 0; i < ARRAY_count((*class)->names); i++) + FREE(&((*class)->names[i])); + + ARRAY_delete(&((*class)->function)); + ARRAY_delete(&((*class)->event)); + ARRAY_delete(&((*class)->prop)); + ARRAY_delete(&((*class)->ext_func)); + ARRAY_delete(&((*class)->constant)); + ARRAY_delete(&((*class)->class)); + ARRAY_delete(&((*class)->unknown)); + ARRAY_delete(&((*class)->stat)); + ARRAY_delete(&((*class)->dyn)); + ARRAY_delete(&((*class)->array)); + ARRAY_delete(&((*class)->structure)); + ARRAY_delete(&((*class)->names)); + + if ((*class)->name != NULL) + STR_free((*class)->name); + + FREE(class); + } +} + + +CLASS_SYMBOL *CLASS_declare(CLASS *class, int index, int type, bool global) +{ + const char *name; + CLASS_SYMBOL *sym; + + sym = CLASS_get_symbol(class, index); + + if ((global && !TYPE_is_null(sym->global.type)) + || (!global && !TYPE_is_null(sym->local.type))) // || !TYPE_is_null(sym->global.type)))) + { + char name[sym->symbol.len + 1]; + memcpy(name, sym->symbol.name, sym->symbol.len); + name[sym->symbol.len] = 0; + THROW("'&1' already declared", name); + } + + if (type == TK_VARIABLE && sym->class && JOB->class->class[sym->class - 1].has_static) + { + name = SYMBOL_get_name(&sym->symbol); + if (global) + COMPILE_print(MSG_WARNING, -1, "class name hidden by global declaration: &1", name); + else + COMPILE_print(MSG_WARNING, -1, "class name hidden by local declaration: &1", name); + } + + if (!global && !TYPE_is_null(sym->global.type)) + { + name = SYMBOL_get_name(&sym->symbol); + + switch (TYPE_get_kind(sym->global.type)) + { + case TK_VARIABLE: + COMPILE_print(MSG_WARNING, -1, "global variable hidden by local declaration: &1", name); + break; + + case TK_FUNCTION: + COMPILE_print(MSG_WARNING, -1, "function hidden by local declaration: &1", name); + break; + + case TK_EXTERN: + COMPILE_print(MSG_WARNING, -1, "extern function hidden by local declaration: &1", name); + break; + + case TK_CONST: + COMPILE_print(MSG_WARNING, -1, "constant hidden by local declaration: &1", name); + break; + } + } + + if (global) + sym->global.line = JOB->line; + else + sym->local.line = JOB->line; + + return sym; +} + + +void CLASS_check_unused_global(CLASS *class) +{ + CLASS_SYMBOL *sym; + int i; + TYPE type; + + for (i = 0; i < TABLE_count(class->table); i++) + { + sym = CLASS_get_symbol(class, i); + type = sym->global.type; + + if (sym->global_used && sym->global_assigned) + continue; + + if (TYPE_is_null(type) || TYPE_is_public(type)) + continue; + + if (!sym->global_used) + { + if (TYPE_get_kind(type) == TK_VARIABLE) + COMPILE_print(MSG_WARNING, sym->global.line, "unused global variable: &1", SYMBOL_get_name(&sym->symbol)); + else if (TYPE_get_kind(type) == TK_FUNCTION) + COMPILE_print(MSG_WARNING, sym->global.line, "unused function: &1", SYMBOL_get_name(&sym->symbol)); + else if (TYPE_get_kind(type) == TK_EXTERN) + COMPILE_print(MSG_WARNING, sym->global.line, "unused extern function: &1", SYMBOL_get_name(&sym->symbol)); + } + else + { + if (TYPE_get_kind(type) == TK_VARIABLE) + COMPILE_print(MSG_WARNING, sym->global.line, "uninitialized global variable: &1", SYMBOL_get_name(&sym->symbol)); + } + } +} + + +void CLASS_add_function(CLASS *class, TRANS_FUNC *decl) +{ + FUNCTION *func; + int i; + CLASS_SYMBOL *sym; + PARAM *param; + int count; + + count = ARRAY_count(class->function); + if (count >= MAX_CLASS_FUNCTION) + THROW("Too many functions"); + + func = ARRAY_add_void(&class->function); + TYPE_clear(&func->type); + func->nparam = 0; + func->name = NO_SYMBOL; + + ARRAY_create(&func->local); + //ARRAY_create_inc(&func->code, 512); + func->code = NULL; + func->ncode = 0; + func->ncode_max = 0; + + if (JOB->debug) + ARRAY_create(&func->pos_line); + + if (!decl) return; + + sym = CLASS_declare(class, decl->index, TK_FUNCTION, TRUE); + /*CLASS_add_symbol(class, JOB->table, decl->index, &sym, NULL);*/ + + sym->global.type = decl->type; + sym->global.value = ARRAY_count(class->function) - 1; + + if (TYPE_is_static(decl->type)) + class->has_static = TRUE; + + func->nparam = decl->nparam; + + if (decl->nparam) + { + ALLOC(&func->param, decl->nparam * sizeof(PARAM)); + + for (i = 0; i < decl->nparam; i++) + func->param[i] = decl->param[i]; + } + + func->type = decl->type; + func->start = decl->start; + func->line = decl->line; + func->name = decl->index; + func->last_code = CODE_NO_POS; + func->last_code2 = CODE_NO_POS; + func->stack = 8; // Some stack may be needed for initialization functions + func->finally = 0; + func->catch = 0; + func->npmin = -1; + func->vararg = decl->vararg; + func->fast = decl->fast; + func->unsafe = decl->unsafe; + + // Function startup + + CODE_begin_function(func); + JOB->func = func; + + // Byref check at function startup + + if (decl->byref) + CODE_byref(decl->byref); + + // Optional parameters + + for (i = 0; i < func->nparam; i++) + { + param = &func->param[i]; + if (param->optional == NULL) + continue; + + if (func->npmin < 0) + func->npmin = i; + + TRANS_init_optional(param); + CODE_pop_optional(i - func->nparam); + } + + if (func->npmin < 0) + func->npmin = func->nparam; +} + + +void CLASS_add_event(CLASS *class, TRANS_EVENT *decl) +{ + EVENT *event; + int i; + CLASS_SYMBOL *sym; + int count; + + count = ARRAY_count(class->event); + if (count >= MAX_CLASS_EVENT) + THROW("Too many events"); + + event = ARRAY_add_void(&class->event); + TYPE_clear(&event->type); + event->nparam = 0; + event->name = NO_SYMBOL; + + if (!decl) return; + + sym = CLASS_declare(class, decl->index, TK_EVENT, TRUE); + /*CLASS_add_symbol(class, JOB->table, decl->index, &sym, NULL);*/ + + sym->global.type = decl->type; + sym->global.value = ARRAY_count(class->event) - 1; + + event->nparam = decl->nparam; + + if (event->nparam) + { + ALLOC(&event->param, decl->nparam * sizeof(PARAM)); + + for (i = 0; i < decl->nparam; i++) + { + event->param[i].type = decl->param[i].type; + event->param[i].index = decl->param[i].index; + } + } + + event->type = decl->type; + event->name = decl->index; +} + + +void CLASS_add_property(CLASS *class, TRANS_PROPERTY *decl) +{ + PROPERTY *prop; + CLASS_SYMBOL *sym; + int index; + int i; + + index = ARRAY_count(class->prop); + prop = ARRAY_add_void(&class->prop); + TYPE_clear(&prop->type); + prop->name = NO_SYMBOL; + prop->read = TRUE; + prop->write = decl->read == 0; + prop->synonymous = -1; + + sym = CLASS_declare(class, decl->index, TK_PROPERTY, TRUE); + /*CLASS_add_symbol(class, JOB->table, decl->index, &sym, NULL);*/ + + sym->global.type = decl->type; + sym->global.value = ARRAY_count(class->prop) - 1; + + if (TYPE_is_static(decl->type)) + class->has_static = TRUE; + + prop->type = decl->type; + prop->name = decl->index; + prop->line = decl->line; + prop->comment = decl->comment; + + for (i = 0; i < decl->nsynonymous; i++) + { + prop = ARRAY_add_void(&class->prop); + TYPE_clear(&prop->type); + prop->name = NO_SYMBOL; + prop->read = TRUE; + prop->write = decl->read == 0; + prop->synonymous = index; + + sym = CLASS_declare(class, decl->synonymous[i], TK_PROPERTY, TRUE); + /*CLASS_add_symbol(class, JOB->table, decl->index, &sym, NULL);*/ + + sym->global.type = decl->type; + sym->global.value = ARRAY_count(class->prop) - 1; + + prop->type = decl->type; + prop->name = decl->synonymous[i]; + prop->line = decl->line; + prop->comment = decl->comment; + } + +} + + +void CLASS_add_extern(CLASS *class, TRANS_EXTERN *decl) +{ + EXTFUNC *extfunc; + int i; + CLASS_SYMBOL *sym; + int count; + + count = ARRAY_count(class->ext_func); + if (count >= MAX_CLASS_EXTERN) + THROW("Too many external functions"); + + extfunc = ARRAY_add_void(&class->ext_func); + TYPE_clear(&extfunc->type); + extfunc->nparam = 0; + extfunc->vararg = FALSE; + extfunc->name = NO_SYMBOL; + + if (!decl) return; + + sym = CLASS_declare(class, decl->index, TK_EXTERN, TRUE); + /*CLASS_add_symbol(class, JOB->table, decl->index, &sym, NULL);*/ + + sym->global.type = decl->type; + sym->global.value = ARRAY_count(class->ext_func) - 1; + + extfunc->nparam = decl->nparam; + extfunc->vararg = decl->vararg; + + if (extfunc->nparam) + { + ALLOC(&extfunc->param, decl->nparam * sizeof(PARAM)); + + for (i = 0; i < decl->nparam; i++) + { + extfunc->param[i].type = decl->param[i].type; + extfunc->param[i].index = decl->param[i].index; + } + } + + extfunc->type = decl->type; + extfunc->name = decl->index; + extfunc->library = decl->library; + extfunc->alias = decl->alias; +} + + +int CLASS_add_constant(CLASS *class, TRANS_DECL *decl) +{ + CONSTANT *desc; + int num; + + num = ARRAY_count(class->constant); + if (num >= MAX_CLASS_CONST) + THROW("Too many constants"); + + desc = ARRAY_add(&class->constant); + desc->type = decl->type; + desc->index = decl->index; + + desc->value = decl->value; + if (TYPE_get_id(decl->type) == T_LONG) + desc->lvalue = decl->lvalue; + + desc->line = JOB->line; + + return num; +} + + +static int add_class(CLASS *class, int index, bool used, bool exported) +{ + int num; + CLASS_REF *desc; + CLASS_SYMBOL *sym = CLASS_get_symbol(class, index); + + num = sym->class - 1; + if (num < 0) + { + num = ARRAY_count(class->class); + if (num >= MAX_CLASS_CLASS) + THROW("Too many different classes used"); + + desc = ARRAY_add_void(&class->class); + desc->index = index; + + sym->class = num + 1; + + if (JOB->verbose) + printf("Adding class %.*s %s%s\n", sym->symbol.len, sym->symbol.name, used ? "" : "Unused ", exported ? "Exported" : ""); + + JOB->class->class[num].exported = exported; + } + + if (used != JOB->class->class[num].used) + { + if (JOB->verbose) + printf("Switching class %.*s to %s\n", sym->symbol.len, sym->symbol.name, used ? "Used" : "Unused"); + + JOB->class->class[num].used = used; + } + + return num; +} + +int CLASS_add_class(CLASS *class, int index) +{ + return add_class(class, index, TRUE, FALSE); +} + +int CLASS_add_class_unused(CLASS *class, int index) +{ + return add_class(class, index, FALSE, FALSE); +} + +int CLASS_add_class_exported(CLASS *class, int index) +{ + return add_class(class, index, TRUE, TRUE); +} + +int CLASS_add_class_exported_unused(CLASS *class, int index) +{ + return add_class(class, index, FALSE, TRUE); +} + + +bool CLASS_exist_class(CLASS *class, int index) +{ + return CLASS_get_symbol(class, index)->class > 0; +} + +static char *CLASS_add_name(CLASS *class, const char *name, int len) +{ + char *ret; + + ALLOC(&ret, len + 1); + memcpy(ret, name, len); + ret[len] = 0; + + *((char **)ARRAY_add(&class->names)) = ret; + return ret; +} + +int CLASS_get_array_class(CLASS *class, int type, int value) +{ + static char *names[] = { + NULL, "Boolean[]", "Byte[]", "Short[]", "Integer[]", "Long[]", "Single[]", "Float[]", + "Date[]", "String[]", "String[]", "Pointer[]", "Variant[]", NULL, NULL, NULL, "Object[]" + }; + + int index; + //CLASS_REF *cref; + + if (value < 0) + { + if (type <= T_VOID || type > T_OBJECT) + ERROR_panic("Bad native array class"); + + index = _array_class[type]; + + if (index == 0) + { + if (!TABLE_find_symbol(class->table, names[type], strlen(names[type]), &index)) + index = CLASS_add_symbol(class, names[type]); + index = CLASS_add_class_exported(class, index); + _array_class[type] = index; + } + } + else + { + CLASS_SYMBOL *sym = CLASS_get_symbol(class, class->class[value].index); + int len = sym->symbol.len; + char name[len + 2]; + + memcpy(name, sym->symbol.name, len); + memcpy(&name[len], "[]", 2); + + if (!TABLE_find_symbol(class->table, name, len + 2, &index)) + { + char *name_alloc = CLASS_add_name(class, name, len + 2); + TABLE_add_symbol(class->table, name_alloc, len + 2, &index); + } + + if (class->class[value].exported) + index = CLASS_add_class_exported(JOB->class, index); + else + index = CLASS_add_class(JOB->class, index); + } + + JOB->class->class[index].type = TYPE_make(type, value, 0); + + /*cref = &class->class[index]; + if (TYPE_is_null(cref->array)) + { + cref->array.t.id = type; + cref->array.t.value = value; + }*/ + + return index; +} + + +int CLASS_add_unknown(CLASS *class, int index) +{ + int num; + int *desc; + CLASS_SYMBOL *sym = CLASS_get_symbol(class, index); + + num = sym->unknown - 1; + if (num < 0) + { + num = ARRAY_count(class->unknown); + if (num >= MAX_CLASS_UNKNOWN) + THROW("Too many unknown symbols"); + + desc = ARRAY_add(&class->unknown); + *desc = index; + + sym->unknown = num + 1; + } + + return num; +} + +int CLASS_add_array(CLASS *class, TRANS_ARRAY *array) +{ + CLASS_ARRAY *desc; + int num; + int i; + + num = ARRAY_count(class->array); + if (num >= MAX_CLASS_ARRAY) + THROW("Too many array declarations"); + + desc = ARRAY_add(&class->array); + desc->type = array->type; + desc->ndim = array->ndim; + for (i = 0; i < desc->ndim; i++) + desc->dim[i] = array->dim[i]; + + return num; +} + + +void CLASS_begin_init_function(CLASS *class, int type) +{ + FUNCTION *func = &class->function[type]; + CODE_begin_function(func); + JOB->func = func; +} + + +void CLASS_add_declaration(CLASS *class, TRANS_DECL *decl) +{ + CLASS_SYMBOL *sym; + VARIABLE *var; + int count; + bool save_warnings = FALSE; + + if (decl->no_warning) + { + save_warnings = JOB->warnings; + JOB->warnings = FALSE; + } + + sym = CLASS_declare(class, decl->index, TYPE_get_kind(decl->type), TRUE); + + if (decl->no_warning) + { + JOB->warnings = save_warnings; + } + + sym->global.type = decl->type; + + if (TYPE_get_kind(decl->type) == TK_CONST) + { + sym->global.value = CLASS_add_constant(class, decl); + class->has_static = TRUE; + } + else if (TYPE_is_static(decl->type)) + { + count = ARRAY_count(class->stat); + if (count >= MAX_CLASS_SYMBOL) + THROW("Too many static variables"); + + sym->global.value = count; + var = ARRAY_add(&class->stat); + + var->type = decl->type; + var->index = decl->index; + //var->pos = class->size_stat; + //var->size = TYPE_sizeof(var->type); + + //class->size_stat += var->size; + + CLASS_begin_init_function(class, FUNC_INIT_STATIC); + + if (TRANS_has_init_var(decl)) + { + FUNCTION_add_all_pos_line(); + TRANS_init_var(decl); + CODE_pop_global(sym->global.value, TRUE); + sym->global_assigned = TRUE; + } + class->has_static = TRUE; + } + else + { + count = ARRAY_count(class->dyn); + if (count >= MAX_CLASS_SYMBOL) + THROW("Too many dynamic variables"); + + sym->global.value = count; + var = ARRAY_add(&class->dyn); + + var->type = decl->type; + var->index = decl->index; + //var->pos = class->size_dyn; + //var->size = TYPE_sizeof(var->type); + + //class->size_dyn += var->size; + + CLASS_begin_init_function(class, FUNC_INIT_DYNAMIC); + + if (TRANS_has_init_var(decl)) + { + FUNCTION_add_all_pos_line(); + TRANS_init_var(decl); + CODE_pop_global(sym->global.value, FALSE); + sym->global_assigned = TRUE; + } + } +} + +#if 0 +static void reorder_decl(CLASS *class, VARIABLE *tvar, const char *desc) +{ + int count; + int pos; + VARIABLE *var; + int i, j; + int n; + + /* variables statiques */ + + count = ARRAY_count(tvar); + if (count > 1) + { + if (JOB->verbose) + printf("Reordering %s variables:", desc); + + pos = 0; + for (i = 0; i < 3; i++) + { + for (j = 0; j < count; j++) + { + var = &tvar[j]; + n = var->size & 3; + + switch (i) + { + case 0: if (n != 0) continue; else break; + case 1: if (n != 2) continue; else break; + case 2: if (n != 1 && n != 3) continue; else break; + } + + var->pos = pos; + pos += var->size; + + if (JOB->verbose) + printf(" %s (%d) ", TABLE_get_symbol_name(class->table, var->index), var->pos); + } + } + + if (JOB->verbose) + printf("\n"); + } +} +#endif + +// Don't do that! The order of variables must be kept. + +void CLASS_sort_declaration(CLASS *class) +{ + //reorder_decl(class, class->stat, "static"); + //reorder_decl(class, class->dyn, "dynamic"); +} + + +int CLASS_add_symbol(CLASS *class, const char *name) +{ + int index; + + TABLE_add_symbol(class->table, name, strlen(name), &index); + return index; +} + + +void FUNCTION_add_last_pos_line(void) +{ + int current_pos; + + if (!JOB->debug || JOB->nobreak) + return; + + current_pos = CODE_get_current_pos(); + *ARRAY_add(&JOB->func->pos_line) = current_pos; +} + +void FUNCTION_add_all_pos_line(void) +{ + int line; + int current_pos; + + if (!JOB->debug || JOB->nobreak) + return; + + line = JOB->func->line + ARRAY_count(JOB->func->pos_line) - 1; + current_pos = CODE_get_current_pos(); + + //fprintf(stderr, "FUNCTION_add_all_pos_line: line = %d JOB->line = %d\n", line, JOB->line); + + while (line < JOB->line) + { + *ARRAY_add(&JOB->func->pos_line) = current_pos; + line++; + } +} + +char *FUNCTION_get_fake_name(int func) +{ + static char buf[6]; + + snprintf(buf, sizeof(buf), "$%d", func); + return buf; +} + + +static int check_one_property_func(CLASS *class, PROPERTY *prop, bool write) +{ + CLASS_SYMBOL *sym; + char *name; + bool is_static; + FUNCTION *func; + int index; + + JOB->line = prop->line; + + is_static = TYPE_is_static(prop->type); + + name = STR_copy(TABLE_get_symbol_name_suffix(class->table, prop->name, write ? "_Write" : "_Read")); + + if (!TABLE_find_symbol(class->table, name, strlen(name), &index)) + THROW("&1 is not declared", name); + + sym = (CLASS_SYMBOL *)TABLE_get_symbol(class->table, index); + + if (TYPE_get_kind(sym->global.type) != TK_FUNCTION) + THROW("&1 is declared but is not a function", name); + + func = &class->function[sym->global.value]; + JOB->line = func->line; + + if (TYPE_is_public(sym->global.type)) + THROW("A property implementation cannot be public"); + + if (is_static != TYPE_is_static(sym->global.type)) + { + if (is_static) + THROW("&1 must be static", name); + else + THROW("&1 cannot be static", name); + } + + if (write) + { + if (TYPE_get_id(func->type) != T_VOID) + goto _BAD_SIGNATURE; + + if (func->nparam != 1 || func->npmin != 1) + goto _BAD_SIGNATURE; + + if (!TYPE_compare(&func->param[0].type, &prop->type)) + goto _BAD_SIGNATURE; + } + else + { + if (!TYPE_compare(&func->type, &prop->type)) + goto _BAD_SIGNATURE; + + if (func->nparam != 0 || func->npmin != 0) + goto _BAD_SIGNATURE; + } + + STR_free(name); + sym->global_used = TRUE; + return sym->global.value; + +_BAD_SIGNATURE: + + THROW("&1 declaration does not match", name); + +} + + +void CLASS_check_properties(CLASS *class) +{ + int i; + PROPERTY *prop; + + for (i = 0; i < ARRAY_count(class->prop); i++) + { + prop = &class->prop[i]; + if (prop->synonymous >= 0) + { + prop->read = class->prop[prop->synonymous].read; + prop->write = class->prop[prop->synonymous].write; + } + else + { + prop->read = check_one_property_func(class, prop, FALSE); + if (prop->write) + prop->write = check_one_property_func(class, prop, TRUE); + else + prop->write = NO_SYMBOL; + } + } +} + + +CLASS_SYMBOL *CLASS_get_local_symbol(int local) +{ + PARAM *param; + + param = &JOB->func->local[local]; + return (CLASS_SYMBOL *)TABLE_get_symbol(JOB->class->table, param->index); +} + + +char *TYPE_get_desc(TYPE type) +{ + static char buf[256]; + + TYPE_ID id; + int value; + CLASS_SYMBOL *sym; + + id = TYPE_get_id(type); + value = TYPE_get_value(type); + + if (id == T_ARRAY) + { + strcpy(buf, TYPE_name[JOB->class->array[value].type.t.id]); + strcat(buf, "[]"); + } + else if (id == T_OBJECT) + { + if (value == -1) + strcpy(buf, "Object"); + else + { + sym = CLASS_get_symbol(JOB->class, JOB->class->class[value].index); + sprintf(buf, "%.*s", sym->symbol.len, sym->symbol.name); + } + } + else + { + strcpy(buf, TYPE_name[id]); + } + + return buf; +} + + diff --git a/main/gbc/gbc_class.h b/main/gbc/gbc_class.h new file mode 100644 index 00000000..379ccc85 --- /dev/null +++ b/main/gbc/gbc_class.h @@ -0,0 +1,275 @@ +/*************************************************************************** + + gbc_class.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_CLASS_H +#define __GBC_CLASS_H + +#include "gbc_type.h" +#include "gb_table.h" +#include "gbc_trans.h" + +#define FUNC_INIT_STATIC 0 +#define FUNC_INIT_DYNAMIC 1 + +typedef + struct { + TYPE type; + int value; + int line; + } + PACKED + CLASS_SYMBOL_INFO; + +typedef + struct { + SYMBOL symbol; + CLASS_SYMBOL_INFO global; + CLASS_SYMBOL_INFO local; + int class; + int unknown; + unsigned global_used : 1; + unsigned global_assigned : 1; + unsigned local_used : 1; + unsigned local_assigned : 1; + unsigned _reserved : 28; + } + PACKED + CLASS_SYMBOL; + +typedef + TRANS_PARAM PARAM; + +typedef + struct { + TYPE type; + int index; + int pos; + int size; + } + VARIABLE; + +typedef + struct { + int num; + int param; + } + VARIABLE_INIT; + +typedef + struct { + TYPE type; + int index; + int value; + int line; + int64_t lvalue; + } + CONSTANT; + +typedef + struct { + TYPE type; // Return value datatype + int name; // Function name index in class symbol table + + char nparam; // Maximum number of arguments + char npmin; // Minimum number of arguments + unsigned vararg : 1; // If this function accepts extra arguments + unsigned fast : 1; // If this function is jit compiled + unsigned unsafe : 1; // If this function is unsafe + unsigned use_is_missing : 1; // If this function uses IsMissing() + unsigned _reserved : 12; + short nlocal; // Local variable count + short nctrl; // Control structure variable count + + uint64_t byref; // Byref mask + PARAM *local; // Datatypes of local variables + PARAM *param; // Datatypes of arguments + + PATTERN *start; // Starts compilation from there + + int line; // ...which is this line + short stack; // Needed stack + short _reserved2; + + ushort *code; // Compile bytecode + + short *pos_line; // Bytecode position of each code line + + ushort ncode; // Number of instructions + ushort ncode_max; // Size of the bytecode allocation + + ushort last_code; // Last compiled bytecode position + ushort last_code2; // Last last compiled bytecode position + ushort finally; // FINALLY position + ushort catch; // CATCH position + } + PACKED + FUNCTION; + +typedef + struct { + TYPE type; // Return value datatype + int name; // Function name index in class symbol table + PARAM *param; // Argument list + short nparam; // Number of arguments + short _reserved; + } + PACKED + EVENT; + +typedef + struct { + TYPE type; // Return value datatype + int name; // Function name index in class symbol table + PARAM *param; // Argument list + short nparam; // Number of arguments + unsigned vararg : 1; // Variable number of arguments + unsigned _reserved : 15; + int library; // Library name index + int alias; // Real function name index + } + PACKED + EXTFUNC; + +typedef + struct { + TYPE type; // Property datatype + int name; // Property name index + int line; // The line where the property is declared + int comment; // Property string description, added to datatype + int synonymous; // Synonymous property index (-1 if not a synonymous) + short read; // Read function + short write; // Write function + } + PACKED + PROPERTY; + +typedef + struct { + TYPE type; // Array datatype + int ndim; // Number of dimensions + int dim[MAX_ARRAY_DIM]; // Dimensions bounds + } + CLASS_ARRAY; + +typedef + struct { + int index; // Structure name + int nfield; // Number of structure fields + VARIABLE *field; // Structure fields + } + CLASS_STRUCT; + +typedef + struct { + int index; // class name + unsigned used : 1; + unsigned exported : 1; + unsigned structure : 1; + unsigned has_static : 1; + unsigned is_collection : 1; // if the class is Collection (for JIT) + unsigned _reserved : 20; + TYPE type; // if the class is an array, the type of the array contents (for JIT) + } + CLASS_REF; + +typedef + struct { + TABLE *table; // symbol table + TABLE *string; // strings table + char *name; // class name + short parent; // parent class + unsigned exported : 1; // class is exported + unsigned autocreate : 1; // class is auto-creatable + unsigned optional : 1; // class is optional + unsigned nocreate : 1; // class cannot be instantiated + unsigned all_fast : 1; // all methods have the Fast option (JIT) + unsigned all_unsafe : 1; // all methods are unsafe + unsigned has_static : 1; // has static methods, properties or variables + unsigned has_fast : 1; // has at least one fast method + unsigned _reserved : 8; + VARIABLE *stat; // static variables + VARIABLE *dyn; // dynamic variables + CONSTANT *constant; // constants + CLASS_REF *class; // classes + int *unknown; // unknown symbols + FUNCTION *function; // functions + int size_stat; // static variables total size + int size_dyn; // dynamic variables total size + EVENT *event; // events + PROPERTY *prop; // properties + EXTFUNC *ext_func; // extern functions + CLASS_ARRAY *array; // array definitions + CLASS_STRUCT *structure; // structs definitions + char **names; // when some symbols must be created like object arrays + } + CLASS; + + +#define CLASS_get_symbol(class, ind) ((CLASS_SYMBOL *)TABLE_get_symbol((class)->table, ind)) + +#define FUNCTION_is_procedure(func) (TYPE_get_id((func)->type) == T_VOID) +#define FUNCTION_is_static(func) (TYPE_is_static((func)->type)) + + +void CLASS_create(CLASS **result); +void CLASS_delete(CLASS **class); + +CLASS_SYMBOL *CLASS_declare(CLASS *class, int index, int type, bool global); +void CLASS_check_unused_global(CLASS *class); +void CLASS_begin_init_function(CLASS *class, int type); + +void CLASS_add_function(CLASS *class, TRANS_FUNC *decl); +void CLASS_add_event(CLASS *class, TRANS_EVENT *decl); +void CLASS_add_property(CLASS *class, TRANS_PROPERTY *prop); +void CLASS_add_extern(CLASS *class, TRANS_EXTERN *decl); +void CLASS_add_declaration(CLASS *class, TRANS_DECL *decl); +int CLASS_add_constant(CLASS *class, TRANS_DECL *decl); +int CLASS_add_class(CLASS *class, int index); +int CLASS_add_class_unused(CLASS *class, int index); +int CLASS_add_class_exported(CLASS *class, int index); +int CLASS_add_class_exported_unused(CLASS *class, int index); +bool CLASS_exist_class(CLASS *class, int index); +int CLASS_add_unknown(CLASS *class, int index); +int CLASS_add_array(CLASS *class, TRANS_ARRAY *array); + +int CLASS_get_array_class(CLASS *class, int type, int value); + +void FUNCTION_add_last_pos_line(void); +void FUNCTION_add_all_pos_line(void); +char *FUNCTION_get_fake_name(int func); + +int CLASS_add_symbol(CLASS *class, const char *name); + +void CLASS_sort_declaration(CLASS *class); +void CLASS_check_properties(CLASS *class); + +CLASS_SYMBOL *CLASS_get_local_symbol(int local); + +char *TYPE_get_desc(TYPE type); + +// gbc_dump.c + +void CLASS_dump(void); +void CLASS_export(void); + +#endif diff --git a/main/gbc/gbc_code.c b/main/gbc/gbc_code.c new file mode 100644 index 00000000..48ac34fb --- /dev/null +++ b/main/gbc/gbc_code.c @@ -0,0 +1,36 @@ +/*************************************************************************** + + gbc_code.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_CODE_C + +#define PROJECT_COMP +#define CODE_DUMP + +#include "gb_common.h" +#include "gb_error.h" +#include "gbc_compile.h" +#include "gb_code.h" +#include "gb_limit.h" + +#include "gb_code_temp.h" + diff --git a/main/gbc/gbc_compile.c b/main/gbc/gbc_compile.c new file mode 100644 index 00000000..b8d229a3 --- /dev/null +++ b/main/gbc/gbc_compile.c @@ -0,0 +1,697 @@ +/*************************************************************************** + + gbc_compile.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_COMPILE_C + +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_str.h" +#include "gb_file.h" +#include "gb_component.h" + +#include "gbc_compile.h" +#include "gb_reserved.h" +#include "gbc_read.h" +#include "gbc_trans.h" +#include "gbc_header.h" +#include "gb_code.h" +#include "gbc_output.h" +#include "gbc_form.h" +#include "gbc_chown.h" +#include "gb_arch.h" + +/*#define DEBUG*/ + +char *COMP_root = NULL; +char *COMP_project; +char *COMP_project_name; +char *COMP_info_path; +char *COMP_lib_path; +static char *COMP_classes = NULL; +COMPILE COMP_current; +uint COMPILE_version = GAMBAS_PCODE_VERSION; + +#define STARTUP_MAX_LINE 256 + +const FORM_FAMILY COMP_form_families[] = +{ + { "form", FORM_NORMAL }, + { "report", FORM_NORMAL }, + { "webpage", FORM_WEBPAGE }, + { "webform", FORM_NORMAL }, + { "termform", FORM_NORMAL }, + { NULL } +}; + +static bool read_line(FILE *f, char *dir, int max) +{ + char *p; + int c; + + p = dir; + + for(;;) + { + max--; + + c = fgetc(f); + if (c == EOF) + return TRUE; + + if (c == '\n' || max == 0) + { + *p = 0; + return FALSE; + } + + *p++ = (char)c; + } +} + +static void add_memory_list(char *p, int size) +{ + char *pe = p + size; + char *p2; + int len; + + for(;;) + { + if (p >= pe) + break; + + p2 = p; + while (p2 < pe && *p2 != '\n') + p2++; + + if (p2 >= pe) + break; + + len = p2 - p; + /*if (len > 2 && p[len - 1] == '?') + len--;*/ + + COMPILE_add_class(p, len); + + p = p2 + 1; + } +} + +static void add_file_list(FILE *fi) +{ + char line[256]; + int len; + + for(;;) + { + if (read_line(fi, line, sizeof(line))) + break; + + len = strlen(line); + /*if (len > 2 && line[len - 1] == '?') + len--;*/ + + COMPILE_add_class(line, len); + } +} + +static void add_library_list_file(const char *path) +{ + ARCH *arch; + ARCH_FIND find; + const char *name; + char *rpath = NULL; + + if (*path == ':') + { + name = &path[1]; + + rpath = STR_cat(FILE_cat(COMP_lib_path, name, NULL), ".gambas", NULL); + if (!FILE_exist(rpath)) + { + STR_free(rpath); + rpath = STR_cat(FILE_cat(COMP_root, "lib/gambas" GAMBAS_VERSION_STRING, name, NULL), ".gambas", NULL); + } + + if (!FILE_exist(rpath)) + path = NULL; + else + path = rpath; + } + else + { + name = path; + if (!FILE_exist(path)) + path = NULL; + } + + if (path) + { + arch = ARCH_open(path); + + if (!ARCH_find(arch, ".list", 0, &find)) + add_memory_list(&arch->addr[find.pos], find.len); + + ARCH_close(arch); + } + else + ERROR_warning("cannot find library: %s", name); + + if (rpath) + STR_free(rpath); +} + + +static void add_component_list_file(char *name) +{ + char *path; + FILE *fi; + + path = (char *)FILE_cat(COMP_info_path, name, NULL); + strcat(path, ".list"); + fi = fopen(path, "r"); + + if (!fi) + { + // Do not raise an error if a component self-reference is not found + if (strcmp(name, COMP_project_name)) + //fprintf(stderr, "warning: cannot read component list file: %s.list\n", name); + THROW("Component not found: &1", name); + return; + } + + add_file_list(fi); + + fclose(fi); +} + + +static FILE *open_project_file() +{ + FILE *fp = fopen(COMP_project, "r"); + if (!fp) + THROW(E_OPEN, COMP_project); + return fp; +} + +static bool line_begins_with(const char *line, const char *key, int len) +{ + if (len < 0) + len = strlen(key); + + return strncmp(line, key, len) == 0; +} + +static void startup_print(FILE *fs, const char *key, const char *def) +{ + FILE *fp; + char line[256]; + int len = strlen(key); + bool print = FALSE; + + fp = open_project_file(); + + for(;;) + { + if (read_line(fp, line, sizeof(line))) + break; + + if (line_begins_with(line, key, len)) + { + fprintf(fs, "%s\n", &line[len]); + print = TRUE; + } + } + + fclose(fp); + + if (!print && def) + fprintf(fs, "%s\n", def); +} + +static char *find_version_in_file(void) +{ + char *dir, *pdir; + FILE *fv; + char line[256]; + const char *path; + int len; + + dir = STR_copy(COMP_project); + + for(;;) + { + pdir = STR_copy(FILE_get_dir(dir)); + STR_free(dir); + dir = pdir; + + if (dir[0] == '/' && dir[1] == 0) + { + STR_free(dir); + return NULL; + } + + path = FILE_cat(dir, "VERSION", NULL); + + if (FILE_exist(path)) + { + STR_free(dir); + break; + } + } + + fv = fopen(path, "r"); + if (!fv) + return NULL; + + len = fread(line, 1, sizeof(line) - 1, fv); + while (len > 0 && isspace(line[len - 1])) + len--; + line[len] = 0; + return STR_copy(line); +} + +static void startup_print_version(FILE *fs) +{ + FILE *fp; + char line[256]; + char *version = NULL; + + fp = open_project_file(); + + for(;;) + { + if (read_line(fp, line, sizeof(line))) + break; + + if (line_begins_with(line, "VersionFile=", 12)) + { + if (line[12] == '1') + { + version = find_version_in_file(); + break; + } + } + else if (line_begins_with(line, "Version=", 8)) + { + version = STR_copy(&line[8]); + } + } + + fclose(fp); + + if (version) + { + fputs(version, fs); + fputc('\n', fs); + STR_free(version); + } + else + fputs("0.0.0\n", fs); +} + +static void create_startup_file() +{ + const char *name; + FILE *fs; + + name = FILE_cat(FILE_get_dir(COMP_project), ".startup", NULL); + fs = fopen(name, "w"); + if (!fs) + THROW("Cannot create .startup file"); + + // Do that now, otherwise file buffer can be erased + FILE_set_owner(name, COMP_project); + + startup_print(fs, "Startup=", ""); + startup_print(fs, "Title=", ""); + startup_print(fs, "Stack=", "0"); + startup_print(fs, "StackTrace=", "0"); + + startup_print_version(fs); + + fputc('\n', fs); + startup_print(fs, "Component=", NULL); + startup_print(fs, "Library=", NULL); + fputc('\n', fs); + + if (fclose(fs)) + THROW("Cannot create .startup file"); +} + +#undef isdigit + +static int read_version_digits(const char **pstr) +{ + const char *p = *pstr; + int n; + int i; + + if (!isdigit(*p)) + return -1; + + n = 0; + + for (i = 0; i < 4; i++) + { + n = (n << 4) + *p++ - '0'; + if (!isdigit(*p)) + break; + } + + *pstr = p; + return n; +} + +static void init_version(void) +{ + const char *ver; + int n, v; + + ver = getenv("GB_PCODE_VERSION"); + if (ver && *ver) + { + v = 0; + n = read_version_digits(&ver); + if (n <= 0 || n > GAMBAS_VERSION) + return; + + v = n << 24; + + if (*ver++ != '.') + return; + + n = read_version_digits(&ver); + if (n < 0 || n > 0x99) + return; + + v |= n << 16; + + if (*ver++ == '.') + { + n = read_version_digits(&ver); + if (n > 0) + { + if (n > 0x9999) + return; + + v |= n; + } + } + + COMPILE_version = v; + } +} + +void COMPILE_init(void) +{ + FILE *fp; + char line[256]; + char *env; + + RESERVED_init(); + + if (!COMP_root) + COMP_root = STR_copy(FILE_get_dir(FILE_get_dir(FILE_find_gambas()))); + + // Component directory + + COMP_info_path = STR_copy(FILE_cat(COMP_root, "share/gambas" GAMBAS_VERSION_STRING "/info", NULL)); + + // Local libraries directory + + env = getenv("XDG_DATA_HOME"); + if (env && *env) + COMP_lib_path = STR_copy(FILE_cat(env, "gambas3/lib", NULL)); + else + COMP_lib_path = STR_copy(FILE_cat(FILE_get_home(), ".local/share/gambas3/lib", NULL)); + + // Project name + + COMP_project_name = STR_copy(FILE_get_name(FILE_get_dir(COMP_project))); + + // Bytecode version + + init_version(); + + // Project classes + + BUFFER_create(&COMP_classes); + + add_component_list_file("gb"); + + fp = open_project_file(); + + for(;;) + { + if (read_line(fp, line, sizeof(line))) + break; + + /*printf("%s\n", line);*/ + + if (strncmp(line, "Component=", 10) == 0) + add_component_list_file(&line[10]); + else if (strncmp(line, "Library=", 8) == 0) + add_library_list_file(&line[8]); + } + + fclose(fp); + + // Startup file + + create_startup_file(); + + // Adds a separator to make the difference between classes from components + // (they must be searched in the global symbol table) and classes from the + // project (they must be searched in the project symbol table) + + COMPILE_add_class("-", 1); + + /* + dir = opendir(FILE_get_dir(COMP_project)); + if (dir) + { + while ((dirent = readdir(dir)) != NULL) + { + name = dirent->d_name; + if (*name == '.') + continue; + + if ((strcasecmp(FILE_get_ext(name), "module") == 0) + || (strcasecmp(FILE_get_ext(name), "class") == 0)) + { + name = FILE_get_basename(name); + BUFFER_add(&COMP_classes, name, strlen(name)); + BUFFER_add(&COMP_classes, "\n", 1); + } + } + + closedir(dir); + } + + BUFFER_add(&COMP_classes, "\n", 1); + */ +} + + +void COMPILE_begin(const char *file, bool trans, bool debug) +{ + struct stat info; + off_t size; + + CLEAR(JOB); + + JOB->name = STR_copy(file); + JOB->debug = debug; + JOB->form = FORM_get_file_family(JOB->name, &JOB->family); + JOB->output = OUTPUT_get_file(JOB->name); + + if (trans) + { + JOB->trans = TRUE; + JOB->tname = OUTPUT_get_trans_file(JOB->name); + } + + BUFFER_create(&JOB->source); + CLASS_create(&JOB->class); + + JOB->default_library = NO_SYMBOL; + + size = 0; + + if (stat(JOB->name, &info)) + ERROR_warning("cannot stat file: %s", JOB->name); + else + size += info.st_size; + + if (JOB->form) + { + if (stat(JOB->form, &info)) + ERROR_warning("cannot stat file: %s", JOB->form); + else + size += info.st_size * 2; + } + + ALLOC(&JOB->pattern, sizeof(PATTERN) * (16 + size)); + JOB->pattern_count = 0; +} + + +void COMPILE_load(void) +{ + if (BUFFER_load_file(&JOB->source, JOB->name)) + THROW("Cannot load source file: &1", strerror(errno)); + + //BUFFER_add(&JOB->source, "\n", 1); +} + + +void COMPILE_end(void) +{ + CLASS_delete(&JOB->class); + BUFFER_delete(&JOB->source); + FREE(&JOB->pattern); + + if (JOB->help) + ARRAY_delete(&JOB->help); + + STR_free(JOB->name); + STR_free(JOB->form); + STR_free(JOB->output); + if (JOB->trans) + STR_free(JOB->tname); + if (JOB->help) + STR_free(JOB->hname); +} + + +void COMPILE_exit(void) +{ + RESERVED_exit(); + BUFFER_delete(&COMP_classes); + STR_free(COMP_project_name); + STR_free(COMP_project); + STR_free(COMP_info_path); + STR_free(COMP_root); +} + +void COMPILE_add_class(const char *name, int len) +{ + unsigned char clen = len; + + if (clen != len) + ERROR_panic("Class name is too long"); + + BUFFER_add(&COMP_classes, &clen, 1); + BUFFER_add(&COMP_classes, name, len); +} + +void COMPILE_end_class(void) +{ + unsigned char clen = 0; + BUFFER_add(&COMP_classes, &clen, 1); +} + +void COMPILE_enum_class(char **name, int *len) +{ + char *p = *name; + + if (!p) + p = COMP_classes; + else + p += p[-1]; + + *len = *p; + *name = p + 1; +} + +void COMPILE_print(int type, int line, const char *msg, ...) +{ + int i; + va_list args; + const char *arg[4]; + bool col; + + if (!JOB->warnings && type == MSG_WARNING) + return; + + va_start(args, msg); + + if (line < 0) + { + line = JOB->line; + col = JOB->column; + } + else + col = FALSE; + + if (JOB->name) + { + const char *name = FILE_get_name(JOB->name); + if (line) + { + if (line > JOB->max_line && JOB->form) + { + name = FILE_get_name(JOB->form); + fprintf(stderr, "%s:%d: ", name, line - FORM_FIRST_LINE + 1); + } + else + { + if (col) + fprintf(stderr, "%s:%d:%d: ", name, line, READ_get_column()); + else + fprintf(stderr, "%s:%d: ", name, line); + } + } + else + fprintf(stderr, "%s: ", name); + } + else + fprintf(stderr, "gbc" GAMBAS_VERSION_STRING ": "); + + fprintf(stderr, "%s: ", type ? "warning" : "error"); + + if (msg) + { + for (i = 0; i < 4; i++) + arg[i] = va_arg(args, const char *); + + ERROR_define(msg, arg); + fputs(ERROR_info.msg, stderr); + putc('\n', stderr); + } + + va_end(args); +} + diff --git a/main/gbc/gbc_compile.h b/main/gbc/gbc_compile.h new file mode 100644 index 00000000..85b3f623 --- /dev/null +++ b/main/gbc/gbc_compile.h @@ -0,0 +1,112 @@ +/*************************************************************************** + + gbc_compile.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef _COMPILE_H +#define _COMPILE_H + +#include + +#include "gb_array.h" +#include "gb_buffer.h" +#include "gb_table.h" +#include "gb_alloc.h" + +#include "gb_limit.h" +#include "gb_reserved.h" +#include "gbc_read.h" +#include "gbc_form.h" + +#include "gbc_class.h" + +enum { + MSG_ERROR = 0, + MSG_WARNING = 1 +}; + +typedef + struct { + char *name; // source file name + int line; // current line number + int first_line; // first line to compile + int max_line; // maximum line number + char *source; // source file contents + unsigned verbose : 1; // verbose compilation + unsigned debug : 1; // if debugging information must be generated + unsigned trans : 1; // if translation files must be generated + unsigned is_module : 1; // if the source file is a module + unsigned is_form : 1; // if the source is a class form + unsigned declared : 1; // ? + unsigned nobreak : 1; // no breakpoint + unsigned exported : 1; // there are some exported class + unsigned all : 1; // compile everything + unsigned swap : 1; // endianness must be swapped + unsigned public_module : 1; // modules symbols are public by default + unsigned trans_error : 1; // display error messages in a translatable form + unsigned no_old_read_syntax : 1; // do not compile the old read syntax + unsigned column : 1; // search column where there is an error + unsigned exec : 1; // we are compiling for an executable + unsigned warnings : 1; // if warnings must be printed + unsigned _reserved : 16; // reserved + char *output; // output file + PATTERN *pattern; // lexical analyze + int pattern_count; // number of patterns + PATTERN *current; // current pattern + PATTERN *end; // last pattern + FUNCTION *func; // current function being compiled + CLASS *class; // current class being compiled + char *form; // form file name + const FORM_FAMILY *family; // form file family + char *tname; // translation file name + int default_library; // default library name for extern declarations + const char **help; // help comments array + int help_first_line; // line of the first help comment + char *hname; // help file name + } + COMPILE; + +#ifndef __GBC_COMPILE_C + +EXTERN COMPILE COMP_current; +EXTERN char *COMP_root; +EXTERN char *COMP_project; +EXTERN char *COMP_project_name; +EXTERN char *COMP_info_path; +EXTERN FORM_FAMILY COMP_form_families[]; +EXTERN uint COMPILE_version; + +#endif + +#define JOB (&COMP_current) + +void COMPILE_init(void); +void COMPILE_load(void); +void COMPILE_exit(void); +void COMPILE_begin(const char *file, bool trans, bool debug); +void COMPILE_end(void); +void COMPILE_export_class(char *name); +void COMPILE_add_class(const char *name, int len); +void COMPILE_end_class(); +void COMPILE_enum_class(char **name, int *len); +void COMPILE_print(int type, int line, const char *msg, ...); + +#endif diff --git a/main/gbc/gbc_dump.c b/main/gbc/gbc_dump.c new file mode 100644 index 00000000..1434ef46 --- /dev/null +++ b/main/gbc/gbc_dump.c @@ -0,0 +1,845 @@ +/*************************************************************************** + + gbc_dump.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_DUMP_C + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_error.h" + +#include "gb_table.h" +#include "gb_str.h" +#include "gbc_compile.h" +#include "gb_code.h" +#include "gb_file.h" +#include "gbc_chown.h" +#include "gbc_help.h" +#include "gbc_output.h" + + +static FILE *_finfo; +static char *_buffer = NULL; +static int _buffer_ptr = 0; + +static const char *get_name(int index) +{ + return TABLE_get_symbol_name(JOB->class->table, index); +} + +static void get_string(int index, const char **str, int *len) +{ + SYMBOL *sym; + + if (index == VOID_STRING_INDEX) + { + *str = ""; + *len = 0; + } + else + { + sym = TABLE_get_symbol(JOB->class->string, index); + *str = sym->name; + *len = sym->len; + } +} + +static void print_quoted(FILE *file, const char *str, int len) +{ + unsigned char c; + + fputc('"', file); + while (len--) + { + c = *str++; + if (c == '\n') + fputs("\\n", file); + else if (c == '\t') + fputs("\\t", file); + else if (c == 0) + fputs("\\0", file); + else if (c == '"') + fputs("\\\"", file); + else + fputc(c, file); + } + fputc('"', file); +} + +static void dump_name(int index) +{ + printf("%s", get_name(index)); +} + + +static void dump_type(TYPE type, bool as) +{ + int value; + TYPE_ID id; + CLASS_ARRAY *array; + int i; + + id = TYPE_get_id(type); + value = TYPE_get_value(type); + + if (id == T_ARRAY) + { + array = &JOB->class->array[value]; + + printf("["); + for (i = 0; i < array->ndim; i++) + { + if (i > 0) + printf(","); + printf("%d", array->dim[i]); + } + printf("] As "); + dump_type(array->type, FALSE); + } + else if (id == T_OBJECT && value >= 0) + { + if (as) + printf(" As "); + dump_name(JOB->class->class[value].index); + } + else + { + if (as) + printf(" As "); + printf("%s", TYPE_get_desc(type)); + } +} + + +static void dump_function(FUNCTION *func) +{ + int i; + + //printf("<%lld> ", func->byref); + + printf("("); + + for (i = 0; i < func->nparam; i++) + { + if (i > 0) printf(", "); + + if (i >= func->npmin) + printf("Optional "); + + if (func->byref & (1LL << i)) + printf("ByRef "); + + dump_name(func->param[i].index); + dump_type(func->param[i].type, TRUE); + } + + if (func->vararg) + { + if (func->nparam > 0) + printf(", "); + printf("..."); + } + + printf(")"); +} + + + +void CLASS_dump(void) +{ + int i; + TYPE type; + CLASS_SYMBOL *sym; + CLASS *class = JOB->class; + + if (JOB->is_module) + printf("MODULE "); + else if (JOB->is_form) + printf("FORM "); + else + printf("CLASS "); + + printf("%s\n", class->name); + + if (class->parent != NO_SYMBOL) + { + printf("CLASS INHERITS "); + dump_name(class->class[class->parent].index); + putchar('\n'); + } + + if (class->exported) + printf("EXPORT\n"); + + if (class->autocreate) + printf("CREATE\n"); + + if (class->optional) + printf("OPTIONAL\n"); + + putchar('\n'); + + printf("Static size : %d octets\n", class->size_stat); + printf("Dynamic size : %d octets\n\n", class->size_dyn); + + for (i = 0; i < TABLE_count(class->table); i++) + { + sym = CLASS_get_symbol(class, i); + type = sym->global.type; + if (TYPE_is_null(type)) + continue; + + if (TYPE_is_static(type)) printf("Static "); + if (TYPE_is_public(type)) printf("Public "); else printf("Private "); + + switch(TYPE_get_kind(type)) + { + case TK_VARIABLE: + + dump_name(i); + dump_type(type, TRUE); + break; + + case TK_FUNCTION: + + if (TYPE_get_id(type) == T_VOID) + printf("Procedure "); + else + printf("Function "); + + dump_name(i); + dump_function(&class->function[sym->global.value]); + + break; + + case TK_CONST: + + printf("Const "); + dump_name(i); + dump_type(type, TRUE); + printf(" = "); + dump_name(class->constant[sym->global.value].index); + + break; + + case TK_PROPERTY: + + printf("Property "); + if (class->prop[sym->global.value].write == NO_SYMBOL) + printf("Read "); + dump_name(i); + dump_type(type, TRUE); + break; + + case TK_EVENT: + + printf("Event "); + dump_name(i); + break; + + case TK_UNKNOWN: printf("Unknown "); break; + case TK_EXTERN: printf("Extern "); break; + case TK_LABEL: printf("Label "); break; + } + + putchar('\n'); + + /* + if (TYPE_get_kind(type) == TK_FUNCTION) + { + func = &class->function[value]; + printf(" L:%ld", func->line); + } + else if (TYPE_get_kind(type) == TK_EVENT) + func = (FUNCTION *)&class->event[value]; + else if (TYPE_get_kind(type) == TK_EXTERN) + { + func = (FUNCTION *)&class->ext_func[value]; + printf(" in %s", TABLE_get_symbol_name(class->table, class->ext_func[value].library)); + } + */ + } + + putchar('\n'); +} + +static void export_newline(void) +{ + fputc('\n', _finfo); +} + +static void export_type(TYPE type, bool scomma) +{ + int value; + int index; + TYPE_ID id; + + id = TYPE_get_id(type); + value = TYPE_get_value(type); + + if (id == T_OBJECT && value >= 0) + { + fprintf(_finfo, "%s", get_name(JOB->class->class[value].index)); + if (scomma) + fputc(';', _finfo); + } + else if (id == T_ARRAY) + { + type = JOB->class->array[value].type; + index = CLASS_get_array_class(JOB->class, TYPE_get_id(type), TYPE_get_value(type)); + fprintf(_finfo, "%s", get_name(JOB->class->class[index].index)); + if (scomma) + fputc(';', _finfo); + } + // TODO: Manage T_STRUCT + else + fprintf(_finfo, "%s", TYPE_get_short_desc(type)); +} + + +static void export_signature(int nparam, int npmin, PARAM *param, bool vararg) +{ + int i; + + for (i = 0; i < nparam; i++) + { + if (i == npmin) + fprintf(_finfo, "["); + + fprintf(_finfo, "(%s)", get_name(param[i].index)); + export_type(param[i].type, TRUE); + } + + if (npmin < nparam) + fprintf(_finfo, "]"); + + if (vararg) + fprintf(_finfo, "."); + + export_newline(); +} + + +static void create_file(FILE **fw, const char *file) +{ + if (!*fw) + { + *fw = fopen(file, "w"); + if (!*fw) + THROW("Cannot create file: &1", FILE_cat(FILE_get_dir(COMP_project), file, NULL)); + } +} + +static void close_file_and_rename(FILE *f, const char *file, const char *dest) +{ + if (f) + { + fclose(f); + FILE_unlink(dest); + FILE_rename(file, dest); + FILE_set_owner(dest, COMP_project); + } + else + { + FILE_unlink(file); + FILE_unlink(dest); + } +} + +static bool exist_bytecode_file(char *name) +{ + char *output = OUTPUT_get_file(name); + bool exist = FILE_exist(output); + STR_free(output); + return exist; +} + +static void read_line(char **line, int *len) +{ + int l; + char c; + int lmax; + + *line = NULL; + *len = 0; + + if (!_buffer) + return; + + lmax = BUFFER_length(_buffer); + if (_buffer_ptr >= lmax) + return; + + *line = &_buffer[_buffer_ptr]; + + l = 0; + for(;;) + { + if (_buffer_ptr >= lmax) + break; + c = _buffer[_buffer_ptr++]; + if (c == '\n') + { + _buffer[_buffer_ptr - 1] = 0; + break; + } + l++; + } + + *len = l; +} + +static bool load_file(const char *name) +{ + _buffer_ptr = 0; + BUFFER_create(&_buffer); + if (BUFFER_load_file(&_buffer, name)) + { + BUFFER_delete(&_buffer); + return TRUE; + } + else + return FALSE; +} + +static void class_update_exported(CLASS *class) +{ + FILE *fw = NULL; + char *name; + int len; + bool inserted = FALSE; + bool optional; + bool has_static; + int cmp; + + if (load_file(".list") && !class->exported) + return; + + //if (!fr && !class->exported) + // return; + + for(;;) + { + read_line(&name, &len); + optional = FALSE; + has_static = FALSE; + + if (!name) + cmp = 1; + else + { + if (name[len - 1] == '?') + { + optional = TRUE; + name[len - 1] = 0; + len--; + } + if (name[len - 1] == '!') + { + has_static = TRUE; + name[len - 1] = 0; + len--; + } + cmp = strcmp(name, class->name); + } + + if (cmp == 0) + { + if (JOB->verbose) + printf("Remove '%s' from .list file\n", name); + continue; + } + else if ((cmp > 0) && class->exported && !inserted) + { + create_file(&fw, ".list#"); + if (JOB->verbose) + printf("Insert '%s%s' into .list file\n", class->name, class->optional ? "?" : ""); + fputs(class->name, fw); + if (class->has_static && COMPILE_version >= 0x03060090) + fputc('!', fw); + if (class->optional) + fputc('?', fw); + fputc('\n', fw); + inserted = TRUE; + } + + if (!name) + break; + + if (exist_bytecode_file(name)) + { + if (JOB->verbose) + printf("Copy '%s' in .list file\n", name); + + create_file(&fw, ".list#"); + fputs(name, fw); + if (has_static && COMPILE_version >= 0x03060090) + fputc('!', fw); + if (optional) + fputc('?', fw); + fputc('\n', fw); + } + else + { + if (JOB->verbose) + printf("Remove '%s' from .list file\n", name); + } + } + + if (_buffer) + BUFFER_delete(&_buffer); + + close_file_and_rename(fw, ".list#", ".list"); +} + +static void insert_class_info(CLASS *class, FILE *fw) +{ + int i; + TYPE type; + CLASS_SYMBOL *sym; + char kind; + int val; + FUNCTION *func; + EVENT *event; + EXTFUNC *extfunc; + CONSTANT *cst; + int line; + const char *str; + int len; + + if (JOB->verbose) + printf("Insert '%s' information into .info file\n", class->name); + + _finfo = fw; + + fprintf(_finfo, "#%s\n", class->name); + + if (class->parent != NO_SYMBOL) + fprintf(_finfo, "%s", get_name(JOB->class->class[class->parent].index)); + export_newline(); + + if (!class->nocreate) + fprintf(_finfo, "C"); + if (class->autocreate) + fprintf(_finfo, "A"); + if (class->optional) + fprintf(_finfo, "O"); + export_newline(); + + HELP_search_and_print_for_class(_finfo); + + for (i = 0; i < TABLE_count(class->table); i++) + { + sym = CLASS_get_symbol(class, i); + type = sym->global.type; + line = sym->global.line; + + if (TYPE_is_null(type)) + continue; + if (!TYPE_is_public(type)) + continue; + + switch(TYPE_get_kind(type)) + { + case TK_CONST: + + kind = 'C'; + break; + + case TK_FUNCTION: + + kind = 'm'; + break; + + case TK_PROPERTY: + + if (class->prop[sym->global.value].write == NO_SYMBOL) + kind = 'r'; + else + kind = 'p'; + break; + + case TK_VARIABLE: + + kind = 'v'; + break; + + case TK_EVENT: + + kind = ':'; + break; + + case TK_EXTERN: + + kind = 'X'; + break; + + default: + continue; + } + + fprintf(_finfo, "%s\n", get_name(i)); + fprintf(_finfo, "%c\n", TYPE_is_static(type) ? toupper(kind) : kind); + + export_type(type, FALSE); + + if (kind == 'r' || kind == 'p') + { + val = class->prop[sym->global.value].comment; + if (val != NO_SYMBOL) + { + get_string(val, &str, &len); + fprintf(_finfo, "%.*s", len, str); + } + } + + export_newline(); + + switch(kind) + { + case 'C': + cst = &class->constant[sym->global.value]; + + switch(TYPE_get_id(type)) + { + case T_BOOLEAN: + if (cst->value) + fputc('T', _finfo); + fputc('\n', _finfo); + break; + + case T_BYTE: + case T_SHORT: + case T_INTEGER: + fprintf(_finfo, "%d\n", cst->value); + break; + + case T_LONG: + fprintf(_finfo, "%" PRId64 "\n", cst->lvalue); + break; + + case T_SINGLE: + case T_FLOAT: + fprintf(_finfo, "%s\n", get_name(cst->value)); + break; + + case T_STRING: + get_string(cst->value, &str, &len); + print_quoted(_finfo, str, len); + export_newline(); + break; + + default: + export_newline(); + break; + } + break; + + case 'm': + func = &class->function[sym->global.value]; + export_signature(func->nparam, func->npmin, func->param, func->vararg); + line = func->line - 1; + break; + + case ':': + event = &class->event[sym->global.value]; + export_signature(event->nparam, event->nparam, event->param, FALSE); + break; + + case 'X': + extfunc = &class->ext_func[sym->global.value]; + export_signature(extfunc->nparam, extfunc->nparam, extfunc->param, FALSE); + break; + + default: + export_newline(); + } + + HELP_search_and_print(_finfo, line); + } +} + +#if 0 +static char *OUTPUT_get_help_file(const char *file) +{ + char *output; + char *p; + //char *dir; + char *name; + + //dir = STR_copy(FILE_get_dir(file)); + name = STR_copy(FILE_get_name(file)); + + for (p = name; *p; p++) + { + if (*p == '.') + { + *p = 0; + break; + } + + *p = toupper(*p); + } + + output = ".help"; + if (mkdir(output, 0777) == 0) + FILE_set_owner(output, COMP_project); + + output = STR_copy(FILE_cat(output, name, NULL)); + + //STR_free(dir); + STR_free(name); + + return output; +} + +static void output_help(void) +{ + FILE *file; + int i; + + JOB->hname = OUTPUT_get_help_file(JOB->name); + + if (!JOB->help) + { + FILE_unlink(JOB->hname); + return; + } + + file = fopen(JOB->hname, "w"); + + if (!file) + THROW("Cannot create file: &1", JOB->hname); + + for (i = 0; i < ARRAY_count(JOB->help); i++) + { + if (!JOB->help[i]) + continue; + + fprintf(stderr, "[%d] = %.*s\n", i + JOB->help_first_line, get_help_comment_length(JOB->help[i]), JOB->help[i]); + } + + fclose(file); + FILE_set_owner(JOB->hname, COMP_project); +} +#endif + + +void CLASS_export(void) +{ + FILE *fw = NULL; + char *line; + int len; + bool inserted = FALSE; + CLASS *class = JOB->class; + int cmp; + const char *msg; + + if (chdir(FILE_get_dir(COMP_project))) + { + msg = "Cannot change directory"; + goto __ERROR; + } + + class_update_exported(class); + + load_file(".info"); + + read_line(&line, &len); + + for(;;) + { + if (!line) + cmp = 1; + else + cmp = strcmp(&line[1], class->name); + + if (cmp == 0) + { + if (JOB->verbose) + printf("Remove '%s' information from .info file\n", class->name); + + for(;;) + { + read_line(&line, &len); + if (!line || *line == '#') + break; + } + + continue; + } + + if (cmp > 0 && class->exported && !inserted) + { + create_file(&fw, ".info#"); + insert_class_info(class, fw); + inserted = TRUE; + } + + if (!line) + break; + + // copying class information + + if (exist_bytecode_file(&line[1])) + { + if (JOB->verbose) + printf("Copy '%s' information in .info file\n", &line[1]); + for(;;) + { + create_file(&fw, ".info#"); + fputs(line, fw); + fputc('\n', fw); + read_line(&line, &len); + if (!line || *line == '#') + break; + } + } + else + { + if (JOB->verbose) + printf("Remove '%s' information from .info file\n", &line[1]); + for(;;) + { + read_line(&line, &len); + if (!line || *line == '#') + break; + } + } + } + + if (_buffer) + BUFFER_delete(&_buffer); + + close_file_and_rename(fw, ".info#", ".info"); + return; + +__ERROR: + + THROW("Cannot create class information: &1: &2", msg, strerror(errno)); +} + diff --git a/main/gbc/gbc_form.c b/main/gbc/gbc_form.c new file mode 100644 index 00000000..92c0371d --- /dev/null +++ b/main/gbc/gbc_form.c @@ -0,0 +1,531 @@ +/*************************************************************************** + + gbc_form.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_FORM_C + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_limit.h" +#include "gb_file.h" +#include "gb_str.h" +#include "gbc_compile.h" +#include "gbc_chown.h" +#include "gbc_form.h" + +/*#define DEBUG*/ + +static char *_source; +static const char *_current; + +static FORM_PARENT form_parent[MAX_FORM_PARENT]; +static int form_parent_level; + +static bool _no_trim = FALSE; + + +void FORM_print_len(const char *buffer, int len) +{ + if (JOB->verbose) + printf("%.*s", len, buffer); + + BUFFER_add(&JOB->source, buffer, len); +} + + +void FORM_print(const char *buffer) +{ + if (JOB->verbose) + printf("%s", buffer); + + BUFFER_add(&JOB->source, buffer, strlen(buffer)); +} + + +void FORM_print_char(char c) +{ + if (JOB->verbose) + putchar(c); + + BUFFER_add_char(&JOB->source, c); +} + + +static void print_fmt(const char *before, const char *word, int len, const char *after) +{ + FORM_print(before); + FORM_print_len(word, len); + FORM_print(after); +} + + +static void print_var(const char *before, const char *word, int len, const char *after) +{ + FORM_print(before); + if (!word) + FORM_print("Me"); + else + { + FORM_print("{"); + FORM_print_len(word, len); + FORM_print("}"); + } + FORM_print(after); +} + + +static bool read_line(const char **str, int *len) +{ + const char *start, *end; + unsigned char car; + int l; + + if (_no_trim) + { + car = *_current; + if (!car) + return TRUE; + } + else + { + for(;;) + { + car = *_current; + if (!car) + return TRUE; + if (car > ' ') + break; + + _current++; + } + } + + start = _current; + for(;;) + { + car = *_current++; + if (car == '\n') // || car =='\r' || !car) + break; + //if (car > ' ') + // nospace = _current; + } + + end = _current; + l = (int)(end - start); + + while (l > 0 && (uchar)end[-1] <= ' ') + { + end--; + l--; + } + + *str = start; + *len = l; + return FALSE; +} + + +static char *get_word(const char **str, int *len) +{ + char car; + const char *pos; + + *len = 0; + + for(;;) + { + car = **str; + if (car == '\n') + return NULL; + if (!isspace(car)) + break; + (*str)++; + } + + pos = *str; + + for(;;) + { + car = **str; + if (isspace(car)) + break; + (*str)++; + (*len)++; + } + + return (char *)pos; +} + + +static void parent_enter(const char *str, int len) +{ + if (form_parent_level >= MAX_FORM_PARENT) + THROW("&1: too many nested containers", JOB->form); + + form_parent[form_parent_level].name = (char *)str; + form_parent[form_parent_level].len = len; + form_parent_level++; +} + + +static void parent_leave(void) +{ + if (form_parent_level == 0) + THROW("&1: syntax error", JOB->form); + + form_parent_level--; +} + + +static void parent_get(char **str, int *len, int add) +{ + if (form_parent_level < add) + { + *str = NULL; + *len = 0; + } + else + { + FORM_PARENT *fp = &form_parent[form_parent_level - add]; + *str = fp->name; + *len = fp->len; + } +} + + +static void get_container(char **str, int *len) +{ + parent_get(str, len, 2); +} + +static void get_current(char **str, int *len) +{ + parent_get(str, len, 1); +} + +static void save_action(bool delete) +{ + FILE *file; + const char *path; + char *name; + const char *line; + int len; + + path = FILE_cat(FILE_get_dir(COMP_project), ".action", NULL); + mkdir(path, 0777); + FILE_set_owner(path, COMP_project); + + name = STR_copy(FILE_set_ext(FILE_get_name(JOB->form), "action")); + path = FILE_cat(FILE_get_dir(COMP_project), ".action", name, NULL); + + if (delete) + { + if (FILE_exist(path)) + { + if (JOB->verbose) + printf("Deleting action file %s\n", path); + + FILE_unlink(path); + } + } + else + { + if (JOB->verbose) + printf("Writing action file %s\n", path); + + file = fopen(path, "w"); + if (!file) + THROW("Cannot create action file: &1", path); + + fputs("# Gambas Action File 3.0\n", file); + + _no_trim = TRUE; + while (!read_line(&line, &len)) + { + fwrite(line, sizeof(char), len, file); + putc('\n', file); + } + _no_trim = FALSE; + + if (fclose(file)) + THROW("Cannot create action file: &1", path); + + FILE_set_owner(path, COMP_project); + } + + STR_free(name); +} + +char *FORM_get_file_family(const char *file, const FORM_FAMILY **family) +{ + char *form; + const FORM_FAMILY *p; + + if (strcmp(FILE_get_ext(file), "class")) + return NULL; + + p = COMP_form_families; + + while (p->ext) + { + form = STR_copy(FILE_set_ext(file, p->ext)); + if (FILE_exist(form)) + break; + STR_free(form); + form = NULL; + p++; + } + + if (form) *family= p; + return form; +} + + +void FORM_do(char *source, bool ctrl_public) +{ + const char *line; + char *word; + char *win = NULL; + char *twin = NULL; + + int len, len_twin; + const char *pos_rewind; + bool virtual; + bool public; + bool action; + + if (JOB->form == NULL) + return; + + _source = source; + _current = _source; + + /* version */ + + if (read_line(&line, &len)) + goto _ERROR; + + if (strncasecmp(line, "# Gambas Form File 3.0", len)) + THROW("Bad form file version"); + + pos_rewind = _current; + form_parent_level = 0; + + //FORM_print("@SECTION\n"); + + while (!read_line(&line, &len)) + { + if (len == 0 || *line == '#' || *line == '\'') + continue; + + if (*line == '{') + { + form_parent_level++; + line++; + + word = get_word(&line, &len); + if (word == NULL) + goto _ERROR; + + if (win != NULL) + { + if (word[0] == '!') + { + word++; + len--; + public = TRUE; + } + else + public = FALSE; + + if (ctrl_public || public) + FORM_print("Public"); + else + FORM_print("Private"); + + //print_fmt(" {%.*s} ", len, word); + print_fmt(" {", word, len, "} "); + } + + if (win == NULL) + { + win = word; + } + + word = get_word(&line, &len); + if (word == NULL) + goto _ERROR; + + if (twin != NULL) + { + if (*word == '#') + { + word++; + len--; + } + //print_fmt("AS %.*s\n", len, word); + print_fmt("As ", word, len, "\n"); + } + + if (twin == NULL) + { + twin = word; + len_twin = len; + + //print_fmt("INHERITS %.*s\n\n", len_twin, twin); + print_fmt("Inherits ", twin, len_twin, "\n\n"); + } + } + else if (*line == '}') + { + form_parent_level--; + if (form_parent_level <= 0) + break; + } + } + + FORM_print("\nPrivate Sub {@load}()\n\n"); + + _current = pos_rewind; + form_parent_level = 0; + + while (!read_line(&line, &len)) + { + if (len == 0 || *line == '#' || *line == '\'') + continue; + + if (*line == '{') + { + line++; + + word = get_word(&line, &len); + if (word == NULL) + goto _ERROR; + + if (form_parent_level == 0) + { + parent_enter(NULL, 0); + FORM_print(" With Me\n"); + } + else + { + //print_fmt(" {%.*s} = NEW ", len, word); + if (word[0] == '!') + { + word++; + len--; + } + + print_fmt(" {", word, len, "} = New "); + parent_enter(word, len); + + word = get_word(&line, &len); + if (word == NULL) + goto _ERROR; + + if (*word == '#') + { + virtual = TRUE; + word++; + len--; + } + else + virtual = FALSE; + + //print_fmt("%.*s", len, word); + FORM_print_len(word, len); + + if (!virtual) + { + get_container(&word, &len); + print_var("(", word, len, ")"); + } + + word = get_word(&line, &len); + if (word == NULL) + get_current(&word, &len); + + //print_fmt(" AS \"%.*s\"\n", len, word); + print_fmt(" As \"", word, len, "\"\n"); + + get_current(&word, &len); + print_var(" With ", word, len, "\n"); + } + } + else if (*line == '}') + { + FORM_print(" End With\n"); + parent_leave(); + if (form_parent_level == 0) + break; + } + else + { + /*get_current(&word, &len_word);*/ + /*FORM_print(" %.*s", len_word, word);*/ + FORM_print(" ."); + FORM_print_len(line, len); + FORM_print("\n"); + } + } + + if (form_parent_level > 0) + goto _ERROR; + + //FORM_print("\n Try Me._load()\n"); + FORM_print("\nEnd\n\n"); + + // Create or delete the action file if needed + + action = FALSE; + + while (!read_line(&line, &len)) + { + if (!strncasecmp(line, "# Gambas Action File 3.0", len)) + { + save_action(FALSE); + action = TRUE; + break; + } + } + + if (!action) + save_action(TRUE); + + return; + +_ERROR: + + THROW("&1: syntax error in form file", JOB->form); +} + diff --git a/main/gbc/gbc_form.h b/main/gbc/gbc_form.h new file mode 100644 index 00000000..fc340b95 --- /dev/null +++ b/main/gbc/gbc_form.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + gbc_form.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_FORM_H +#define __GBC_FORM_H + +enum { + FORM_NORMAL = 0, + FORM_WEBPAGE = 1 +}; + +typedef + struct { + char *ext; + int type; + } + FORM_FAMILY; + +typedef + struct { + char *name; + int len; + } + FORM_PARENT; + +#define FORM_FIRST_LINE 100000 +#define FORM_FIRST_LINE_STRING "100000" + +void FORM_do(char *source, bool ctrl_public); +char *FORM_get_file_family(const char *file, const FORM_FAMILY **family); +void FORM_print_len(const char *buffer, int len); +void FORM_print(const char *buffer); +void FORM_print_char(char c); + +void FORM_webpage(char *source); + +#endif diff --git a/main/gbc/gbc_form_webpage.c b/main/gbc/gbc_form_webpage.c new file mode 100644 index 00000000..d8790c4c --- /dev/null +++ b/main/gbc/gbc_form_webpage.c @@ -0,0 +1,526 @@ +/*************************************************************************** + + gbc_form_webpage.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_FORM_WEBPAGE_C + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_limit.h" +#include "gb_file.h" +#include "gb_str.h" +#include "gbc_compile.h" +#include "gbc_chown.h" +#include "gbc_form.h" + +/*#define DEBUG*/ + +enum { TYPE_CODE, TYPE_HTML, TYPE_COMMENT, TYPE_MARKUP, TYPE_ARG, TYPE_ROOT }; + +static const char *_start; + +static void print_quoted_string(const char *str, int len) +{ + int i; + char buf[8]; + uchar c; + + if (len == 0) + return; + + FORM_print_char('"'); + + for (i = 0; i < len; i++) + { + c = (uchar)str[i]; + //if (c >= ' ' && c <= 126 && c != '\\' && c != '"') + if (c >= ' ' && c != '\\' && c != '"') + FORM_print_char(c); + else + { + FORM_print_char('\\'); + if (c == '\n') + c = 'n'; + else if (c == '\r') + c = 'r'; + else if (c == '\t') + c = 't'; + else if (!(c == '"' || c == '\\')) + { + snprintf(buf, sizeof(buf), "x%02X", c); + FORM_print_len(buf, 3); + continue; + } + FORM_print_char(c); + } + } + + FORM_print_char('"'); +} + +static void print_lower_len(const char *str, int len) +{ + int i; + + for (i = 0; i < len; i++) + FORM_print_char(tolower(str[i])); +} + +static void flush_html(const char *end) +{ + if (_start == end) + return; + + FORM_print_len("Print ", 6); + if (end[-1] == '\n') + { + print_quoted_string(_start, end - _start - 1); + FORM_print_char('\n'); + } + else + { + print_quoted_string(_start, end - _start); + FORM_print_len(";\n", 2); + } + + _start = end; +} + +static bool check_contents(const char *str, int len) +{ + if (len < 4) + return FALSE; + + if (str[0] == '-' && str[1] == '-' && str[len - 1] == '-' && str[len - 2] == '-') + return TRUE; + else + return FALSE; +} + +static int print_code(const char *str, int len) +{ + char c; + const char *start = str; + + //fprintf(stderr, "'%.*s'\n", len, str); + + while (len > 0) + { + c = *str++; + len--; + + if (c == '"') + { + while (len > 0) + { + c = *str++; + len--; + if (c == '"') + break; + if (c == '\\' && *str) + { + str++; + len--; + } + } + } + else if (c == '%' && *str == '>') + { + len = str - start - 1; + FORM_print_len(start, len); + return len + 2; + } + } + + THROW("&1: syntax error in form file", JOB->form); +} + +static void print_markup(const char *str, int len) +{ + int l; + char c; + const char *p; + bool comma; + bool quote; + bool end; + + if (len <= 0) + return; + + p = str; + while (len > 0) + { + c = *str++; + len--; + if (c == ' ') + break; + } + + l = str - p - (c == ' '); + + if (*p == '/') + { + end = TRUE; + p++; + l--; + } + else + end = FALSE; + + if (l <= 0) + THROW(E_SYNTAX); + + FORM_print_len(p, l); + + if (end) + { + FORM_print(".__RenderEnd()\n"); + return; + } + + FORM_print(".__Render("); + + if (len > 0) + { + FORM_print_char('['); + + comma = FALSE; + + while (len > 0) + { + while (len > 0 && *str == ' ') + { + str++; + len--; + } + + if (len == 0) + break; + + p = str; + while (len > 0) + { + c = *str++; + len--; + if (c == '=' || c == ' ') + break; + } + + if (comma) + FORM_print(", "); + + FORM_print_char('"'); + l = str - p - (c == '='); + print_lower_len(p, l); + FORM_print("\": "); + + comma = TRUE; + + if (len <= 0 || c == ' ') + { + FORM_print("True"); + continue; + } + + p = str; + + // attr=<% ... %> + if (len >= 3 && *str == '<' && str[1] == '%' && str[2] == '=') + { + str += 3; + len -= 3; + FORM_print_char('('); + l = print_code(str, len); + FORM_print_char(')'); + str += l; + len -= l; + //fprintf(stderr, "-> '%.*s'\n", len, str); + continue; + } + else if (*str == '"') + { + str++; + len--; + quote = TRUE; + } + else + { + quote = FALSE; + } + + while (len > 0) + { + c = *str++; + len--; + if (quote) + { + if (len > 0 && c == '\\') + { + str++; + len--; + } + else if (c == '"') + break; + } + else + { + if (c == ' ') + break; + } + } + + if (!quote && str == p) + FORM_print("True"); + else + { + if (!quote) + FORM_print_char('"'); + FORM_print_len(p, str - p); + if (!quote) + FORM_print_char('"'); + } + } + + FORM_print_char(']'); + } + + FORM_print(")\n"); +} + +void FORM_webpage(char *source) +{ + char type = TYPE_CODE; + char c; + const char *p; + int line; + char buf[16]; + bool has_contents = FALSE; + + line = FORM_FIRST_LINE; + + FORM_print("Inherits WebPage\n\n"); + FORM_print("Public Sub _Render(Optional (_Arg) As Collection = New Collection)\n\n"); + + p = source; + +__PRINT: + + _start = p; + for(;;) + { + c = *p++; + if (!c) + { + flush_html(p - 1); + goto __END; + } + + if (c == '<') + { + if (*p == '%') + { + flush_html(p - 1); + + if (p[1] == '/' && p[2] == '%' && p[3] == '>') + { + p += 4; + type = TYPE_ROOT; + } + else + { + p++; + type = TYPE_CODE; + } + + goto __CODE; + } + else if (*p == '<') + { + flush_html(p - 1); + p++; + type = TYPE_MARKUP; + goto __CODE; + } + } + + if (c == '\n') + { + line++; + if ((p - _start) >= 256) + flush_html(p); + } + } + +__CODE: + + FORM_print("#Line "); + sprintf(buf, "%d", line); + FORM_print(buf); + FORM_print_char('\n'); + + if (type == TYPE_CODE) + { + if (*p == '=') + { + type = TYPE_HTML; + p++; + } + else if (*p == '!') + { + type = TYPE_ARG; + p++; + } + else if (*p == '-' && p[1] == '-') + { + type = TYPE_COMMENT; + p += 2; + } + else + type = TYPE_CODE; + } + else if (type == TYPE_ROOT) + { + FORM_print("Print Html$(Application.Root);\n"); + goto __PRINT; + } + + _start = p; + +__END_STRING: + + for(;;) + { + c = *p++; + if (!c) + goto __ERROR; + if (c == '"') + goto __STRING; + + if (c == '\n') + { + line++; + continue; + } + + if (c == '%' && *p == '>' && type != TYPE_MARKUP) + { + switch (type) + { + case TYPE_CODE: + FORM_print_len(_start, p - _start - 1); + FORM_print_char('\n'); + break; + + case TYPE_HTML: + + if ((p - _start) == 1) + { + FORM_print("Print Html$(Application.Root);\n"); + } + else + { + FORM_print("Print Html$("); + FORM_print_len(_start, p - _start - 1); + FORM_print(");\n"); + } + break; + + case TYPE_ARG: + FORM_print("Print _Arg!"); + print_lower_len(_start, p - _start - 1); + FORM_print(";\n"); + break; + + case TYPE_COMMENT: + default: + break; + } + + p++; + if (*p == '\n') + { + line++; + p++; + } + goto __PRINT; + } + else if (c == '>' && *p == '>' && type == TYPE_MARKUP) + { + if (check_contents(_start, p - _start - 1)) + { + if (has_contents) + THROW("Contents already declared"); + has_contents = TRUE; + + FORM_print("\nEnd\n\n"); + FORM_print("Public Sub _RenderEnd()\n\n"); + } + else + { + print_markup(_start, p - _start - 1); + FORM_print_char('\n'); + } + + p++; + if (*p == '\n') + { + line++; + p++; + } + goto __PRINT; + } + } + +__STRING: + + for(;;) + { + c = *p++; + if (!c) + goto __ERROR; + if (c == '"') + goto __END_STRING; + if (c == '\\' && *p) + p++; + } + +__END: + + FORM_print("\nEnd\n\n"); + return; + +__ERROR: + + THROW("&1: syntax error in form file", JOB->form); +} + diff --git a/main/gbc/gbc_header.c b/main/gbc/gbc_header.c new file mode 100644 index 00000000..1a2840bd --- /dev/null +++ b/main/gbc/gbc_header.c @@ -0,0 +1,1147 @@ +/*************************************************************************** + + gbc_header.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define _TRANS_HEADER_C + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_str.h" +#include "gb_file.h" +#include "gb_table.h" + +#include "gbc_compile.h" +#include "gbc_trans.h" +#include "gbc_header.h" +#include "gb_code.h" + +/*#define DEBUG*/ + + +static void check_public_private(PATTERN **look, bool *is_public) +{ + *is_public = JOB->is_module && JOB->public_module; + + if (PATTERN_is(**look, RS_PUBLIC)) + { + *is_public = TRUE; + (*look)++; + } + else if (PATTERN_is(**look, RS_PRIVATE)) + { + *is_public = FALSE; + (*look)++; + } +} + +static PATTERN *jump_expression(PATTERN *look) +{ + int niv = 0; + + for(;;) + { + if (PATTERN_is_newline(*look)) + break; + + if (PATTERN_is(*look, RS_LBRA) || PATTERN_is(*look, RS_LSQR)) + { + niv++; + } + else if (PATTERN_is(*look, RS_RBRA) || PATTERN_is(*look, RS_RSQR)) + { + if (niv > 0) + niv--; + else + break; + } + else if (niv == 0) + { + if (PATTERN_is(*look, RS_COMMA)) + break; + } + + look++; + } + + return look; +} + +static void analyze_function_desc(TRANS_FUNC *func, int flag) +{ + PATTERN *look = JOB->current; + TRANS_PARAM *param; + //bool is_output; + bool is_optional = FALSE; + TRANS_DECL ttyp; + uint64_t byref_mask = 1; + + if (!PATTERN_is_identifier(*look)) + THROW("Syntax error. Invalid identifier in function name"); + + func->index = PATTERN_index(*look); + TYPE_clear(&func->type); + look++; + + if (flag & HF_EVENT) + TABLE_copy_symbol_with_prefix(JOB->class->table, func->index, ':', &func->index); + + func->nparam = 0; + func->byref = 0; + func->vararg = FALSE; + + if ((flag & HF_VOID) && PATTERN_is_newline(*look)) + { + JOB->current = ++look; + return; + } + + if (!PATTERN_is(*look, RS_LBRA)) + THROW_UNEXPECTED(look); + look++; + + for(;;) + { + if (func->nparam >= MAX_PARAM_FUNC) + THROW("Too many arguments"); + + param = &func->param[func->nparam]; + CLEAR(param); + + if (PATTERN_is(*look, RS_RBRA)) + { + look++; + break; + } + + if (func->nparam > 0) + { + if (!PATTERN_is(*look, RS_COMMA)) + THROW(E_SYNTAX_MISSING, "',' or ')'"); + look++; + } + + if (PATTERN_is(*look, RS_3PTS) && !(flag & HF_NO_3PTS)) + { + look++; + if (!PATTERN_is(*look, RS_RBRA)) + THROW("Syntax error. '...' must be the last argument"); //, get_num_desc(func->nparam + 1)); + look++; + func->vararg = TRUE; + break; + } + + //is_output = FALSE; + + if (!(flag & HF_NO_OPT)) + { + if (PATTERN_is(*look, RS_OPTIONAL)) + { + look++; + is_optional = TRUE; + } + } + + if (PATTERN_is(*look, RS_AT) || PATTERN_is(*look, RS_BYREF)) + { + func->byref |= byref_mask; + look++; + } + + if (PATTERN_is(*look, RS_LBRA)) + { + param->ignore = TRUE; + look++; + } + + if (!PATTERN_is_identifier(*look)) + THROW("Syntax error. The &1 argument is not a valid identifier", TRANS_get_num_desc(func->nparam + 1)); + + param->index = PATTERN_index(*look); + look++; + + if (param->ignore) + { + if (!PATTERN_is(*look, RS_RBRA)) + THROW(E_MISSING, "')'"); + look++; + } + + JOB->current = look; + + if (!TRANS_type(TT_NOTHING, &ttyp)) + THROW("Syntax error. Invalid type description of &1 argument", TRANS_get_num_desc(func->nparam + 1)); + + param->type = ttyp.type; + + /* + if (is_output) + TYPE_set_flag(¶m->type, TF_OUTPUT); + */ + + look = JOB->current; + + if (is_optional) + { + param->optional = look; + look = jump_expression(look); + JOB->current = look; + } + + func->nparam++; + byref_mask <<= 1; + } + + JOB->current = look; +} + + +static void header_module_type(void) +{ + const char *ext; + const FORM_FAMILY *p; + + /*JOB->class->name = STR_copy(FILE_get_name(JOB->name));*/ + + ext = FILE_get_ext(JOB->name); + + if (strcasecmp(ext, "module") == 0) + { + JOB->is_module = TRUE; + JOB->is_form = FALSE; + } + else if (strcasecmp(ext, "class") == 0) + { + JOB->is_module = FALSE; + JOB->is_form = FALSE; + } + else + { + p = COMP_form_families; + while (p->ext) + { + if (strcasecmp(ext, p->ext) == 0) + { + JOB->is_module = FALSE; + JOB->is_form = TRUE; + break; + } + p++; + } + + if (!p->ext) + THROW("Unknown file extension"); + } + + JOB->declared = TRUE; +} + + +static bool header_event(TRANS_EVENT *event) +{ + PATTERN *look = JOB->current; + //TRANS_DECL ttyp; + + if (!PATTERN_is(*look, RS_EVENT)) + return FALSE; + + CLEAR(event); + + if (JOB->is_module) + THROW("A module cannot raise events"); + + JOB->current++; + analyze_function_desc((TRANS_FUNC *)event, HF_VOID + HF_NO_BYREF + HF_NO_3PTS + HF_NO_OPT); + + /*if (PATTERN_is(*JOB->current, RS_AS)) + { + if (!TRANS_type(TT_CAN_SQUARE, &ttyp)) + THROW("Syntax error in return type"); + event->type = ttyp.type; + }*/ + + TYPE_set_kind(&event->type, TK_EVENT); + TYPE_set_flag(&event->type, TF_PUBLIC); + + return TRUE; +} + + +static bool header_property(TRANS_PROPERTY *prop) +{ + TRANS_DECL ptype; + PATTERN *look = JOB->current; + bool is_static = FALSE; + bool is_public = TRUE; + + CLEAR(prop); + + /* static */ + + if (JOB->is_module) + { + is_static = TRUE; + } + else if (PATTERN_is(*look, RS_STATIC)) + { + is_static = TRUE; + look++; + } + + /* public */ + + if (PATTERN_is(*look, RS_PUBLIC) || PATTERN_is(*look, RS_PRIVATE)) + { + is_public = PATTERN_is(*look, RS_PUBLIC); + look++; + } + + if (!PATTERN_is(*look, RS_PROPERTY)) + return FALSE; + look++; + JOB->current = look; + + if (!is_public) + THROW("A property must be public"); + + /* read-only property */ + + if (PATTERN_is(*JOB->current, RS_READ)) + { + prop->read = TRUE; + JOB->current++; + } + else + prop->read = FALSE; + + /* property name */ + + if (!PATTERN_is_identifier(*JOB->current)) + THROW("Syntax error. Invalid identifier in property name"); + + prop->index = PATTERN_index(*JOB->current); + JOB->current++; + + prop->nsynonymous = 0; + + while (TRANS_is(RS_COMMA)) + { + if (prop->nsynonymous == 3) + THROW("Too many property synonymous"); + if (!PATTERN_is_identifier(*JOB->current)) + THROW("Syntax error. Invalid identifier in property name"); + prop->synonymous[prop->nsynonymous++] = PATTERN_index(*JOB->current); + JOB->current++; + } + + if (!TRANS_type(TT_NOTHING, &ptype)) + THROW("Syntax error. Bad property type"); + + prop->type = ptype.type; + prop->line = JOB->line; + + TYPE_set_kind(&prop->type, TK_PROPERTY); + if (is_static) + TYPE_set_flag(&prop->type, TF_STATIC); + TYPE_set_flag(&prop->type, TF_PUBLIC); + + if (PATTERN_is_string(*JOB->current)) + { + prop->comment = PATTERN_index(*JOB->current); + JOB->current++; + } + else + prop->comment = NO_SYMBOL; + + return TRUE; +} + +static bool header_extern(TRANS_EXTERN *trans) +{ + PATTERN *look = JOB->current; + TRANS_DECL ttyp; + int index; + bool is_public; + + check_public_private(&look, &is_public); + + if (!PATTERN_is(*look, RS_EXTERN)) + return FALSE; + look++; + + CLEAR(trans); + + JOB->current = look; + analyze_function_desc((TRANS_FUNC *)trans, HF_NO_BYREF); + + if (PATTERN_is(*JOB->current, RS_AS)) + { + if (!TRANS_type(TT_NOTHING, &ttyp)) + THROW("Syntax error in return type"); + trans->type = ttyp.type; + } + + if (TRANS_is(RS_IN)) + { + if (!PATTERN_is_string(*JOB->current)) + THROW("Library name must be a string"); + + index = PATTERN_index(*JOB->current); + JOB->current++; + } + else + { + if (JOB->default_library == NO_SYMBOL) + THROW(E_MISSING, "IN"); + + index = JOB->default_library; + } + + trans->library = index; + + if (TRANS_is(RS_EXEC)) + { + if (!PATTERN_is_string(*JOB->current)) + THROW("Alias name must be a string"); + + trans->alias = PATTERN_index(*JOB->current); + JOB->current++; + } + else + trans->alias = NO_SYMBOL; + + TYPE_set_kind(&trans->type, TK_EXTERN); + TYPE_set_flag(&trans->type, TF_STATIC); + if (is_public) TYPE_set_flag(&trans->type, TF_PUBLIC); + + return TRUE; +} + +static bool header_class(TRANS_DECL *decl) +{ + if (!TRANS_is(RS_CLASS)) + return FALSE; + + if (!PATTERN_is_identifier(*JOB->current)) + THROW("Syntax error. CLASS needs an identifier"); + + CLEAR(decl); + decl->index = PATTERN_index(*JOB->current); + JOB->current++; + + return TRUE; +} + +static bool header_declaration(TRANS_DECL *decl) +{ + PATTERN *look = JOB->current; + PATTERN *save; + bool is_static = FALSE; + bool is_public = FALSE; + bool is_const = FALSE; + bool no_warning = FALSE; + + /* static ! */ + + if (JOB->is_module) + { + is_static = TRUE; + } + else if (PATTERN_is(*look, RS_STATIC)) + { + is_static = TRUE; + //has_static = TRUE; + look++; + } + + check_public_private(&look, &is_public); + + /* const ? */ + + is_const = FALSE; + + if (PATTERN_is(*look, RS_CONST)) + { + //if (has_static) + // THROW("Unexpected &1", "STATIC"); + + is_const = TRUE; + look++; + } + + if (PATTERN_is(*look, RS_LBRA)) + { + no_warning = TRUE; + look++; + } + + if (!PATTERN_is_identifier(*look)) + return FALSE; + + CLEAR(decl); + + decl->index = PATTERN_index(*look); + look++; + + if (no_warning) + { + if (!PATTERN_is(*look, RS_RBRA)) + return FALSE; + look++; + decl->no_warning = TRUE; + } + + save = JOB->current; + JOB->current = look; + + if (!TRANS_type((!is_const ? TT_CAN_ARRAY | TT_CAN_EMBED : 0) | TT_CAN_NEW, decl)) + { + JOB->current = save; + return FALSE; + } + + if (is_static) TYPE_set_flag(&decl->type, TF_STATIC); + if (is_public) TYPE_set_flag(&decl->type, TF_PUBLIC); + if (is_const) + TYPE_set_kind(&decl->type, TK_CONST); + else + TYPE_set_kind(&decl->type, TK_VARIABLE); + + if (is_const) + { + if (!decl->init) + THROW(E_SYNTAX_MISSING, "'='"); + + JOB->current = decl->init; + JOB->current = TRANS_get_constant_value(decl, JOB->current); + } + + //JOB->current = look; + + return TRUE; +} + + +static bool header_enumeration(TRANS_DECL *decl) +{ + PATTERN *look = JOB->current; + bool is_public; + int value = 0; + + check_public_private(&look, &is_public); + + if (!PATTERN_is(*look, RS_ENUM)) + return FALSE; + look++; + + JOB->current = look; + + for(;;) + { + TRANS_newline(); + + if (!PATTERN_is_identifier(*JOB->current)) + THROW("Syntax error. Identifier expected"); + + CLEAR(decl); + + decl->index = PATTERN_index(*JOB->current); + JOB->current++; + + decl->type = TYPE_make_simple(T_INTEGER); + + if (is_public) TYPE_set_flag(&decl->type, TF_PUBLIC); + TYPE_set_kind(&decl->type, TK_CONST); + + if (TRANS_is(RS_EQUAL)) + { + JOB->current = TRANS_get_constant_value(decl, JOB->current); + value = decl->value + 1; + } + else + { + decl->value = value; + value++; + } + + CLASS_add_declaration(JOB->class, decl); + + if (TRANS_newline()) + break; + + if (!PATTERN_is(*JOB->current, RS_COMMA)) + THROW_UNEXPECTED(JOB->current); + JOB->current++; + } + + return TRUE; +} + + +static bool header_function(TRANS_FUNC *func) +{ + static HEADER_SPECIAL spec[] = + { + { "_init", HS_PUBLIC + HS_STATIC + HS_PROCEDURE + HS_NOPARAM }, + { "_exit", HS_PUBLIC + HS_STATIC + HS_PROCEDURE + HS_NOPARAM}, + { "_new", HS_PUBLIC + HS_DYNAMIC + HS_PROCEDURE }, + { "_free", HS_PUBLIC + HS_DYNAMIC + HS_PROCEDURE + HS_NOPARAM }, + { "_call", HS_PUBLIC }, + { "_get", HS_PUBLIC + HS_FUNCTION }, + { "_put", HS_PUBLIC + HS_PROCEDURE + HS_PUT }, + { "_next", HS_PUBLIC + HS_NOPARAM }, + { "_property", HS_PUBLIC + HS_NOPARAM + HS_FUNCTION + HS_PROPERTY }, + { "_unknown", HS_PUBLIC + HS_UNKNOWN }, + { "_compare", HS_PUBLIC + HS_DYNAMIC + HS_FUNCTION + HS_COMPARE }, + { "_attach", HS_PUBLIC + HS_DYNAMIC + HS_PROCEDURE + HS_ATTACH }, + { NULL, 0 } + }; + + PATTERN *look = JOB->current; + PATTERN pat; + TRANS_DECL ttyp; + SYMBOL *sym; + HEADER_SPECIAL *hsp; + + bool is_proc = FALSE; + bool is_static = FALSE; + bool is_public = FALSE; + bool is_fast = FALSE; + bool is_unsafe = FALSE; + + // fast ? + + if (PATTERN_is(*look, RS_FAST)) + { + is_fast = TRUE; + look++; + + if (PATTERN_is(*look, RS_UNSAFE)) + { + is_unsafe = TRUE; + look++; + } + } + + // static ? + + if (JOB->is_module) + { + is_static = TRUE; + } + else if (PATTERN_is(*look, RS_STATIC)) + { + is_static = TRUE; + look++; + } + + // public ou static ? + + is_public = JOB->is_module && JOB->public_module; + + if (PATTERN_is(*look, RS_PUBLIC)) + { + is_public = TRUE; + look++; + } + else if (PATTERN_is(*look, RS_PRIVATE)) + { + is_public = FALSE; + look++; + } + + if (PATTERN_is(*look, RS_PROCEDURE) || PATTERN_is(*look, RS_SUB)) + is_proc = TRUE; + else if (!PATTERN_is(*look, RS_FUNCTION)) + return FALSE; + look++; + + //CLEAR(func); + + JOB->current = look; + analyze_function_desc(func, HF_NORMAL); + + if (PATTERN_is(*JOB->current, RS_AS)) + { + if (!TRANS_type(TT_NOTHING, &ttyp)) + THROW("Syntax error. Invalid return type"); + func->type = ttyp.type; + is_proc = FALSE; + } + + TYPE_set_kind(&func->type, TK_FUNCTION); + if (is_static) TYPE_set_flag(&func->type, TF_STATIC); + if (is_public) TYPE_set_flag(&func->type, TF_PUBLIC); + + func->fast = is_fast || JOB->class->all_fast; + if (func->fast) + JOB->class->has_fast = TRUE; + + func->unsafe = is_unsafe || JOB->class->all_unsafe; + + // Check special methods + + sym = TABLE_get_symbol(JOB->class->table, func->index); + + if (*sym->name == '_') + { + for (hsp = spec; hsp->name; hsp++) + { + if (sym->len != strlen(hsp->name)) + continue; + if (strncmp(sym->name, hsp->name, sym->len)) + continue; + + if (hsp->flag == HS_ERROR) + THROW("The special method &1 cannot be implemented", hsp->name); + + if ((hsp->flag & HS_PUBLIC) && !is_public) + THROW("The special method &1 must be public", hsp->name); + + if ((hsp->flag & HS_STATIC) && !is_static) + THROW("The special method &1 must be static", hsp->name); + + if ((hsp->flag & HS_DYNAMIC) && is_static) + THROW("The special method &1 cannot be static", hsp->name); + + if ((hsp->flag & HS_PROCEDURE) && !is_proc) + THROW("The special method &1 cannot be a function", hsp->name); + + if ((hsp->flag & HS_FUNCTION) && is_proc) + THROW("The special method &1 must be a function", hsp->name); + + if ((hsp->flag & HS_NOPARAM) && func->nparam > 0) + THROW("The special method &1 takes no arguments", hsp->name); + + if (hsp->flag & HS_PUT) + { + if (func->nparam < 1) + THROW("The special method &1 must take at least one argument", hsp->name); + } + + if (hsp->flag & HS_UNKNOWN) + { + if (func->nparam > 0 || !func->vararg) + THROW("The special method &1 must take a variable number of arguments only", hsp->name); + } + + if (hsp->flag & HS_PROPERTY) + { + if (TYPE_get_id(func->type) != T_BOOLEAN) + THROW("The special method &1 must return a boolean", hsp->name); + } + + if (hsp->flag & HS_COMPARE) + { + if (func->type.t.id != T_INTEGER) + THROW("The special method must return an integer"); + if (func->nparam != 1) + THROW("The special method must take exactly one argument"); + } + + if (hsp->flag & HS_ATTACH) + { + if (func->nparam != 2) + THROW("The special method must take exactly two arguments"); + if (func->param[0].type.t.id != T_OBJECT || func->param[1].type.t.id != T_STRING) + THROW("The special method signature is incorrect"); + } + + break; + } + } + + + // We ignore function body + + if (!PATTERN_is_newline(*(JOB->current))) + THROW("Syntax error at function declaration"); + + func->line = PATTERN_index(*(JOB->current)) + 1; + func->start = JOB->current + 1; + + look = JOB->current; + for(;;) + { + pat = *look; + + if (PATTERN_is_newline(pat)) + { + JOB->line = PATTERN_index(pat) + 1; + pat = look[1]; + + if (PATTERN_is(pat, RS_END)) + { + if (PATTERN_is_newline(look[2])) + { + look += 2; + break; + } + if (TRANS_is_end_function(is_proc, &look[2])) + { + look += 3; + break; + } + else + { + if (is_proc && PATTERN_is(look[2], RS_FUNCTION)) + THROW(E_EXPECTED, "END SUB"); + else if (!is_proc && PATTERN_is(look[2], RS_SUB)) + THROW(E_EXPECTED, "END FUNCTION"); + } + } + else if (UNLIKELY(PATTERN_is_end(pat))) // || PATTERN_is_command(pat))) + THROW(E_MISSING, "END"); + } + + look++; + } + + JOB->current = look; + return TRUE; +} + + +static bool header_inherits(void) +{ + int index; + + if (!PATTERN_is(*JOB->current, RS_INHERITS)) + return FALSE; + + /*{ + if (!(PATTERN_is(JOB->current[0], RS_CLASS) + && PATTERN_is(JOB->current[1], RS_INHERITS))) + return FALSE; + JOB->current++; + }*/ + + JOB->current++; + + if (!PATTERN_is_class(*JOB->current)) + THROW("Syntax error. INHERITS needs a class name"); + + if (JOB->class->parent != NO_SYMBOL) + THROW("Cannot inherit twice"); + + index = PATTERN_index(*JOB->current); + + if (strcasecmp(TABLE_get_symbol_name(JOB->class->table, index), JOB->class->name) == 0) + THROW("Cannot inherit itself"); + + JOB->class->parent = CLASS_add_class(JOB->class, index); + /*printf("JOB->class->parent = %d\n", JOB->class->parent);*/ + + JOB->current++; + return TRUE; +} + + +static bool header_option(void) +{ + if (TRANS_is(RS_EXPORT)) + { + JOB->class->exported = TRUE; + + if (TRANS_is(RS_OPTIONAL)) + JOB->class->optional = TRUE; + + return TRUE; + } + + if (TRANS_is(RS_CREATE)) + { + if (PATTERN_is_newline(JOB->current[0]) || PATTERN_is(JOB->current[0], RS_STATIC)) + { + JOB->class->autocreate = TRUE; + TRANS_ignore(RS_STATIC); + return TRUE; + } + else if (TRANS_is(RS_PRIVATE)) + { + JOB->class->nocreate = TRUE; + return TRUE; + } + } + + if (TRANS_is(RS_FAST)) + { + JOB->class->all_fast = TRUE; + JOB->class->has_fast = TRUE; + + if (TRANS_is(RS_UNSAFE)) + JOB->class->all_unsafe = TRUE; + + return TRUE; + } + + return FALSE; +} + + +static bool header_library(void) +{ + if (!TRANS_is(RS_LIBRARY)) + return FALSE; + + if (!PATTERN_is_string(*JOB->current)) + THROW("Extern library name must be a string"); + + JOB->default_library = PATTERN_index(*JOB->current); + JOB->current++; + return TRUE; +} + + +static bool header_structure(void) +{ + PATTERN *look = JOB->current; + bool is_public; + CLASS_STRUCT *structure; + VARIABLE *field; + TRANS_DECL decl; + int nfield; + int index; + + check_public_private(&look, &is_public); + + if (!PATTERN_is(*look, RS_STRUCT)) + return FALSE; + look++; + JOB->current = look; + + if (!is_public) + THROW("Structures must be public"); + + if (!PATTERN_is_identifier(*JOB->current)) + THROW("Syntax error. STRUCT needs an identifier"); + + structure = ARRAY_add_void(&JOB->class->structure); + ARRAY_create(&structure->field); + nfield = 0; + + structure->index = PATTERN_index(*JOB->current); + index = CLASS_add_class_exported(JOB->class, structure->index); + JOB->class->class[index].structure = TRUE; + //fprintf(stderr, "Set structure flag to %s\n", TABLE_get_symbol_name(JOB->class->table, structure->index)); + +//TABLE_copy_symbol_with_prefix(JOB->class->table, structure->index, '.', NULL, &structure->index); + + JOB->current++; + TRANS_want_newline(); + + for(;;) + { + do + { + if (PATTERN_is_end(*JOB->current)) // || PATTERN_is_command(*JOB->current)) + THROW ("Missing END STRUCT"); + } + while (TRANS_newline()); + + if (PATTERN_is(*JOB->current, RS_END) && PATTERN_is(JOB->current[1], RS_STRUCT)) + { + if (nfield == 0) + THROW ("Syntax error. A structure must have one field at least."); + + JOB->current += 2; + + TRANS_want_newline(); + break; + } + + if (!PATTERN_is_identifier(*JOB->current)) + THROW("Syntax error. The &1 field is not a valid identifier", TRANS_get_num_desc(nfield + 1)); + + field = ARRAY_add(&structure->field); + field->index = PATTERN_index(*JOB->current); + + + JOB->current++; + + CLEAR(&decl); + if (!TRANS_type(TT_CAN_ARRAY | TT_CAN_EMBED, &decl)) + THROW("Syntax error. Invalid type description of &1 field", TRANS_get_num_desc(nfield + 1)); + + TRANS_want_newline(); + + field->type = decl.type; + + nfield++; + } + + structure->nfield = nfield; + + return TRUE; +} + +static bool header_use(void) +{ + if (!TRANS_is(RS_USE)) + return FALSE; + + CLASS_begin_init_function(JOB->class, FUNC_INIT_STATIC); + + for(;;) + { + if (!PATTERN_is_string(*JOB->current)) + THROW("Component name must be a string"); + + TRANS_string(*JOB->current); + TRANS_subr(TS_SUBR_USE, 1); + CODE_drop(); + + JOB->current++; + + if (!TRANS_is(RS_COMMA)) + break; + } + + if (!PATTERN_is_newline(*JOB->current)) + THROW(E_SYNTAX); + + return TRUE; +} + + +static void check_class_header() +{ + while (TRUE) //(JOB->current < JOB->end) + { + if (PATTERN_is_end(*JOB->current)) + break; + + if (TRANS_newline()) + continue; + + if (header_option()) + continue; + + if (header_inherits()) + continue; + + break; + } +} + +void HEADER_do(void) +{ + union { + TRANS_DECL decl; + TRANS_FUNC func; + TRANS_EVENT event; + TRANS_EXTERN ext; + TRANS_PROPERTY prop; + } trans; + + TRANS_reset(); + + header_module_type(); + + if (JOB->line == 1) + check_class_header(); + + while (TRUE) //JOB->current < JOB->end) + { + if (PATTERN_is_end(*JOB->current)) + break; + + if (TRANS_newline()) + { + if (JOB->line == 1) + check_class_header(); + + continue; + } + + if (header_function(&trans.func)) + { + CLASS_add_function(JOB->class, &trans.func); + continue; + } + + if (header_event(&trans.event)) + { + CLASS_add_event(JOB->class, &trans.event); + continue; + } + + if (header_property(&trans.prop)) + { + CLASS_add_property(JOB->class, &trans.prop); + continue; + } + + if (header_extern(&trans.ext)) + { + CLASS_add_extern(JOB->class, &trans.ext); + continue; + } + + if (header_enumeration(&trans.decl)) + continue; + + if (header_declaration(&trans.decl)) + { + CLASS_add_declaration(JOB->class, &trans.decl); + continue; + } + + if (header_structure()) + continue; + + if (header_class(&trans.decl)) + { + CLASS_add_class_exported(JOB->class, trans.decl.index); + continue; + } + + if (header_inherits()) + continue; + + if (header_library()) + continue; + + if (header_use()) + continue; + + /*if (PATTERN_is_command(*JOB->current)) + { + JOB->current++; + continue; + }*/ + + THROW_UNEXPECTED(JOB->current); + } + + // Sort class declaration to avoid alignment problems. + // This should be useless now, as it is done again by the interpreter + // when loading the class. + + CLASS_sort_declaration(JOB->class); + + if (JOB->verbose) + CLASS_dump(); +} diff --git a/main/gbc/gbc_header.h b/main/gbc/gbc_header.h new file mode 100644 index 00000000..a41bd1ac --- /dev/null +++ b/main/gbc/gbc_header.h @@ -0,0 +1,69 @@ +/*************************************************************************** + + gbc_header.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_HEADER_H +#define __GBC_HEADER_H + +#include "gbc_type.h" +#include "gb_reserved.h" +#include "gbc_read.h" +#include "gb_limit.h" +#include "gbc_trans.h" + + +enum { + HF_NORMAL = 0, + HF_NO_3PTS = 1, + HF_EVENT = 2, + HF_NO_OPT = 4, + HF_VOID = 8, + HF_NO_BYREF = 16 + }; + +enum { + HS_ERROR = 0, + HS_PUBLIC = 1 << 0, + HS_STATIC = 1 << 1, + HS_DYNAMIC = 1 << 2, + HS_PROCEDURE = 1 << 3, + HS_FUNCTION = 1 << 4, + HS_PUT = 1 << 5, + HS_UNKNOWN = 1 << 6, + HS_PROPERTY = 1 << 7, + HS_NOPARAM = 1 << 8, + HS_COMPARE = 1 << 9, + HS_ATTACH = 1 << 10 + }; + +typedef + struct { + char *name; + int flag; + } + HEADER_SPECIAL; + + +void HEADER_do(void); + +#endif + diff --git a/main/gbc/gbc_help.c b/main/gbc/gbc_help.c new file mode 100644 index 00000000..72faf124 --- /dev/null +++ b/main/gbc/gbc_help.c @@ -0,0 +1,179 @@ +/*************************************************************************** + + gbc_help.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_HELP_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_file.h" + +#include "gbc_compile.h" +#include "gbc_help.h" + +//#define DEBUG_ME 1 + +static bool get_help_at(int i, const char **help, int *len) +{ + const char *p; + int n = 0; + + if (i < 0 || i >= ARRAY_count(JOB->help)) + return TRUE; + + p = JOB->help[i]; + if (!p) + return TRUE; + + if (HELP_is_void_line(p)) + { + *help = p; + n = 0; + } + else + { + while (*p == '\'') + p++; + if (*p == ' ') + p++; + *help = p; + while (*p++ != '\n') + n++; + } + + *len = n; + return FALSE; +} + +void HELP_add_at_current_line(const char *help) +{ + int count = ARRAY_count(JOB->help); + int new_max; + + if (!count) + { + if (HELP_is_void_line(help)) + return; + + ARRAY_create_inc(&JOB->help, 256); + + JOB->help_first_line = JOB->line; + } + else + { + new_max = JOB->line - JOB->help_first_line; + + if (new_max < count) // Already added! + return; + + if (new_max > count) + ARRAY_add_many_void(&JOB->help, new_max - count); + } + + *ARRAY_add(&JOB->help) = help; + + #if DEBUG_ME + int len; + get_help_at(JOB->line - JOB->help_first_line, &help, &len); + fprintf(stderr, "add help comment: %d %p %d: %.*s\n", JOB->line, help, ARRAY_count(JOB->help), len, help); + #endif +} + +void HELP_search_and_print(FILE *file, int line) +{ + const char *help; + int len, i, ii; + + if (!JOB->help) + return; + + #if DEBUG_ME + fprintf(stderr, "HELP_search_and_print: %s: %d\n", FILE_get_name(JOB->name), line); + #endif + + i = line - JOB->help_first_line; + + if (i < 0) + return; + + if (JOB->help[i]) + { + if (!get_help_at(i, &help, &len)) + { + #if DEBUG_ME + fprintf(stderr, "[%d] '' %.*s\n", line, len, help); + #endif + fprintf(file, "'%.*s\n", len, help); + JOB->help[i] = NULL; + } + } + else + { + ii = i; + i--; + while (i >= 0) + { + if (!JOB->help[i] || HELP_is_for_class(JOB->help[i])) + break; + i--; + } + + for (i++; i < ii; i++) + { + if (HELP_is_void_line(JOB->help[i])) + continue; + + if (get_help_at(i, &help, &len)) + continue; + + #if DEBUG_ME + fprintf(stderr, "{%d} ''%.*s\n", line + i - ii, len, help); + #endif + fprintf(file, "'%.*s\n", len, help); + } + } +} + +void HELP_search_and_print_for_class(FILE *file) +{ + int i; + const char *help; + int len; + + if (!JOB->help) + return; + + for (i = 0; i < ARRAY_count(JOB->help); i++) + { + if (!JOB->help[i] || !HELP_is_for_class(JOB->help[i])) + break; + + if (get_help_at(i, &help, &len)) + break; + + #if DEBUG_ME + fprintf(stderr, "[%d] '''%.*s\n", JOB->help_first_line + i, len, help); + #endif + fprintf(file, "'%.*s\n", len, help); + } +} + diff --git a/main/gbc/gbc_help.h b/main/gbc/gbc_help.h new file mode 100644 index 00000000..657e1eed --- /dev/null +++ b/main/gbc/gbc_help.h @@ -0,0 +1,40 @@ +/*************************************************************************** + + gbc_help.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_HELP_H +#define __GBC_HELP_H + +#include + +#ifndef __GBC_HELP_C +#endif + +void HELP_add_at_current_line(const char *help); +void HELP_search_and_print(FILE *file, int line); +void HELP_search_and_print_for_class(FILE *file); + +#define HELP_is_help_comment(_ptr) ((_ptr)[0] == '\'' && ((_ptr)[1] == ' ' || ((_ptr)[1] == '\'' && (_ptr)[2] == ' '))) +#define HELP_is_for_class(_help) ((_help)[0] == '\'' && (_help)[1] == '\'') +#define HELP_is_void_line(_help) ((_help)[0] == '\n' && (_help)[1] == 0) + +#endif diff --git a/main/gbc/gbc_output.c b/main/gbc/gbc_output.c new file mode 100644 index 00000000..1ebd1611 --- /dev/null +++ b/main/gbc/gbc_output.c @@ -0,0 +1,1383 @@ +/*************************************************************************** + + gbc_output.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_OUTPUT_C + +#include "gb_common.h" + +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#include "gb_error.h" +#include "gb_alloc.h" +#include "gb_str.h" +#include "gb_file.h" +#include "gb_common_swap.h" +#include "gb_magic.h" +#include "gbc_chown.h" +#include "gbc_compile.h" +#include "gbc_form.h" +#include "gbc_output.h" + + +/*#define DEBUG*/ +/*#define DEBUG_MORE*/ + +static CLASS *Class; + +static int StringAddr; +static TABLE *StringTable; +static int NSection; +static int PosStartSection; +static int SizeSection; + +static bool _swap; + +static FILE *_file; + +static int _pos; +static char _buffer[OUTPUT_BUFFER_SIZE + 8]; +static const char * const _mbuffer = &_buffer[OUTPUT_BUFFER_SIZE]; +static char *_pbuffer; + +static OUTPUT_CHANGE *_change = NULL; + +static void output_init(void) +{ + TABLE_create(&StringTable, sizeof(OUTPUT_SYMBOL), TF_NORMAL); + StringAddr = 0; + NSection = 0; + _pos = 0; + _pbuffer = _buffer; + ARRAY_create(&_change); +} + + +static void output_exit(void) +{ + TABLE_delete(&StringTable); + ARRAY_delete(&_change); +} + + +static int get_string(const char *string, int len) +{ + OUTPUT_SYMBOL *sym; + int index; + bool new; + + if (len < 0) + len = strlen(string); + + new = !TABLE_add_symbol(StringTable, string, len, &index); + sym = (OUTPUT_SYMBOL *)TABLE_get_symbol(StringTable, index); + + if (new) + { + sym->value = StringAddr; + StringAddr += len + 1; + } + + #ifdef DEBUG + printf("'%.*s' -> %ld\n", len, string, sym->value); + #endif + + return sym->value; +} + +#define get_pos() (_pos) + +static void flush_buffer(void) +{ + size_t len = _pbuffer - _buffer; + + if (len <= 0) + return; + + if (fwrite(_buffer, sizeof(char), len, _file) != len) + THROW("Write error"); + + _pbuffer = _buffer; +} + + +static void write_byte(unsigned char val) +{ + #ifdef DEBUG_MORE + printf("%ld : b %u 0x%X\n", get_pos(), val, val); + #endif + + if (_pbuffer >= _mbuffer) + flush_buffer(); + + *_pbuffer++ = val; + _pos++; + + /*if (fwrite(&val, sizeof(char), 1, _file) != 1) + THROW("Write error");*/ +} + + +static void write_short(ushort val) +{ + #ifdef DEBUG_MORE + printf("%ld : i %u 0x%X\n", get_pos(), val, val); + #endif + + if (_swap) + SWAP_short((short *)&val); + + if (_pbuffer >= _mbuffer) + flush_buffer(); + + *((ushort *)_pbuffer) = val; + _pbuffer += sizeof(val); + _pos += sizeof(val); +} + + +static void write_int(uint val) +{ + #ifdef DEBUG_MORE + printf("%ld : l %lu 0x%lX\n", get_pos(), val, val); + #endif + + if (_swap) + SWAP_int((int *)&val); + + if (_pbuffer >= _mbuffer) + flush_buffer(); + + *((uint *)_pbuffer) = val; + _pbuffer += sizeof(val); + _pos += sizeof(val); +} + + +static void write_int64(uint64_t val) +{ + #ifdef DEBUG_MORE + printf("%ld : l %llu 0x%llX\n", get_pos(), val, val); + #endif + + if (_swap) + SWAP_int64((int64_t *)&val); + + if (_pbuffer >= _mbuffer) + flush_buffer(); + + *((uint64_t *)_pbuffer) = val; + _pbuffer += sizeof(val); + _pos += sizeof(val); +} + +static void write_string(const char *str, int len) +{ + #ifdef DEBUG_MORE + printf("%ld : s \"%.*s\"\n", get_pos(), len, str); + #endif + + if (&_pbuffer[len] > _mbuffer) + flush_buffer(); + + if (&_pbuffer[len] <= _mbuffer) + { + memcpy(_pbuffer, str, len); + _pbuffer += len; + *_pbuffer++ = 0; + _pos += len + 1; + return; + } + + if (fwrite(str, sizeof(char), len, _file) != len) + THROW("Write error"); + + _pos += len; + write_byte(0); +} + + +static void write_buffer(void *str, int len) +{ + #ifdef DEBUG_MORE + printf("%ld : buffer %ld octets\n", get_pos(), len); + #endif + + if (len == 0) + return; + + if (&_pbuffer[len] > _mbuffer) + flush_buffer(); + + if (&_pbuffer[len] <= _mbuffer) + { + memcpy(_pbuffer, str, len); + _pbuffer += len; + _pos += len; + return; + } + + flush_buffer(); + + if (fwrite(str, sizeof(char), len, _file) != len) + THROW("Write error"); + + _pos += len; +} + + +static void write_pad(void) +{ + while (get_pos() & 0x3) + write_byte(0); +} + + +static void write_type(TYPE type) +{ + write_byte(type.t.flag); + write_byte(type.t.id); + write_short(type.t.value); +} + + +static void add_change(off_t pos, uint val) +{ + int prev = get_pos(); + char *ppos; + OUTPUT_CHANGE *change; + + ppos = &_buffer[pos - (prev - (_pbuffer - _buffer))]; + if (ppos >= _buffer && ppos < (_pbuffer - sizeof(uint))) + { + *((uint *)ppos) = val; + return; + } + + change = ARRAY_add(&_change); + change->pos = pos; + change->val = val; +} + +static void begin_section(const char *name, int size) +{ + NSection++; + #ifdef DEBUG + printf("Section #%d : %s\n", NSection, name); + #endif + + if (size) + { + PosStartSection = get_pos(); + write_int(0); + } + else + PosStartSection = 0; + + SizeSection = size; +} + + +static void end_section(void) +{ + int len; + + if (PosStartSection) + { + write_pad(); + len = get_pos() - PosStartSection - sizeof(int); + add_change(PosStartSection, len); + + #ifdef DEBUG + printf("==> %ld %s\n", len / SizeSection, (SizeSection == 1) ? "bytes" : "elements"); + if (len % SizeSection) + printf("*** remain %ld bytes\n", len % SizeSection); + #endif + } +} + + +static void output_header(void) +{ + begin_section("Header", 0); + + /* magic */ + write_int(OUTPUT_MAGIC); + /* version */ + write_int(COMPILE_version); + /* endianness */ + write_int(OUTPUT_ENDIAN); + /* flag */ + if (JOB->debug) + write_int(1); + else + write_int(0); + + end_section(); +} + + +static void output_class(void) +{ + short flag; + begin_section("Class", 1); + + // Parent class + write_short(Class->parent); + + // Class flags + flag = 0; + if (Class->exported) flag |= 1; + if (Class->autocreate) flag |= 2; + if (Class->optional) flag |= 4; + if (Class->nocreate) flag |= 8; + if (Class->has_fast) flag |= 16; + write_short(flag); + + // Static size + write_int(Class->size_stat); + + // Dynamic size + write_int(Class->size_dyn); + + // Number of structures + write_short(ARRAY_count(Class->structure)); + + // reserved + write_short(0); + + end_section(); +} + + +static void output_desc(void) +{ + int i, n, nn = 0; + CLASS_SYMBOL *csym; + TYPE type; + short out_type; + + n = TABLE_count(Class->table); + + begin_section("Description", 6 * sizeof(int)); + + for (i = 0; i < n; i++) + { + csym = (CLASS_SYMBOL *)TABLE_get_symbol_sort(Class->table, i); + //csym = (CLASS_SYMBOL *)TABLE_get_symbol(Class->table, csym->symbol.sort); + + type = csym->global.type; + + if (TYPE_is_public(type)) + { + nn++; + /* name */ + write_int(get_string(csym->symbol.name, csym->symbol.len)); + /* datatype */ + write_type(csym->global.type); + + switch (TYPE_get_kind(type)) + { + case TK_VARIABLE: + + /* offset */ + write_int(csym->global.value); + /* read */ + write_int(0); + /* write */ + write_int(0); + + if (TYPE_is_static(type)) + out_type = CD_STATIC_VARIABLE_ID; + else + out_type = CD_VARIABLE_ID; + + break; + + case TK_PROPERTY: + + /* read */ + write_int(Class->prop[csym->global.value].read); + /* write */ + write_int(Class->prop[csym->global.value].write); + /* flag */ + write_int(0); + + if (TYPE_is_static(type)) + out_type = CD_STATIC_PROPERTY_ID; + else + out_type = CD_PROPERTY_ID; + + break; + + case TK_CONST: + + /* param */ + write_int(csym->global.value); + /* read */ + write_int(0); + /* write */ + write_int(0); + + out_type = CD_CONSTANT_ID; + + break; + + case TK_FUNCTION: + + /* exec */ + write_int(csym->global.value); + /* signature */ + write_int(0); + /* nparam */ + write_int(0); + + if (TYPE_is_static(type)) + out_type = CD_STATIC_METHOD_ID; + else + out_type = CD_METHOD_ID; + + break; + + case TK_EVENT: + + /* exec */ + write_int(csym->global.value); + /* signature */ + write_int(0); + /* nparam */ + write_int(0); + + out_type = CD_EVENT_ID; + + break; + + case TK_EXTERN: + + /* exec */ + write_int(csym->global.value); + /* signature */ + write_int(0); + /* nparam */ + write_int(0); + + out_type = CD_EXTERN_ID; + + break; + + default: + + ERROR_panic("output_desc: unknown symbol type"); + continue; + } + + /* type de symbole */ + write_int(out_type); + } + } + + end_section(); +} + + + +static void output_constant(void) +{ + int i, n; + CONSTANT *constant; + SYMBOL *sym; + //TABLE *table; + + n = ARRAY_count(Class->constant); + + begin_section("Constants", 3 * sizeof(int)); + + for (i = 0; i < n; i++) + { + constant = &Class->constant[i]; + + /* type */ + write_type(constant->type); + /* value */ + switch (TYPE_get_id(constant->type)) + { + case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: + + write_int(constant->value); + write_int(0); + break; + + case T_LONG: + + write_int64(constant->lvalue); + break; + + case T_SINGLE: case T_FLOAT: + + sym = TABLE_get_symbol(Class->table, constant->value); + write_int(get_string(sym->name, sym->len)); + write_int(sym->len); + break; + + case T_STRING: case T_CSTRING: + + if (constant->value == VOID_STRING_INDEX) + { + write_int(0); + write_int(0); + } + else + { + sym = TABLE_get_symbol(Class->string, constant->value); + write_int(get_string(sym->name, sym->len)); + write_int(sym->len); + } + break; + } + } + + end_section(); +} + + +static void output_class_ref(void) +{ + int i, n; + SYMBOL *sym; + CLASS_REF *ref; + + n = ARRAY_count(Class->class); + + begin_section("External classes", sizeof(int)); + + for (i = 0; i < n; i++) + { + ref = &Class->class[i]; + sym = TABLE_get_symbol(Class->table, ref->index); + if (ref->used) + { + if (ref->exported) + write_int(-get_string(sym->name, sym->len)); + else + write_int(get_string(sym->name, sym->len)); + } + else + { + if (JOB->verbose) + printf("Ignoring class %.*s\n", sym->len, sym->name); + write_int(-1); + } + } + + end_section(); +} + + +static void output_unknown_ref(void) +{ + int i, n; + SYMBOL *sym; + + n = ARRAY_count(Class->unknown); + + begin_section("External symbols", sizeof(int)); + + for (i = 0; i < n; i++) + { + sym = TABLE_get_symbol(Class->table, Class->unknown[i]); + write_int(get_string(sym->name, sym->len)); + } + + end_section(); +} + + +static void output_static(void) +{ + int i, n; + VARIABLE *var; + + n = ARRAY_count(Class->stat); + + begin_section("Static variables", 2 * sizeof(int)); + + for (i = 0; i < n; i++) + { + var = &Class->stat[i]; + + /* type */ + write_type(var->type); + /* addr */ + write_int(var->pos); + } + + end_section(); +} + + +static void output_dynamic(void) +{ + int i, n; + VARIABLE *var; + + n = ARRAY_count(Class->dyn); + + begin_section("Dynamic variables", 2 * sizeof(int)); + + for (i = 0; i < n; i++) + { + var = &Class->dyn[i]; + + /* type */ + write_type(var->type); + /* addr */ + write_int(var->pos); + } + + end_section(); +} + + +static void output_event(void) +{ + int i, n; + EVENT *event; + SYMBOL *sym; + + n = ARRAY_count(Class->event); + + begin_section("Events", 4 * sizeof(int)); + + for (i = 0; i < n; i++) + { + event = &Class->event[i]; + + /* type */ + write_type(event->type); + /* n_param */ + write_short(event->nparam); + /* reserved */ + write_short(0); + /* desc_param */ + write_int(0); + /* name */ + sym = TABLE_get_symbol(Class->table, event->name); + write_int(get_string(sym->name, sym->len)); + } + + end_section(); +} + + +static void output_extern(void) +{ + int i, n; + EXTFUNC *ext; + SYMBOL *sym; + + n = ARRAY_count(Class->ext_func); + + begin_section("Extern functions", 5 * sizeof(int)); + + for (i = 0; i < n; i++) + { + ext = &Class->ext_func[i]; + + /* type */ + write_type(ext->type); + /* n_param */ + write_short(ext->nparam); + /* vararg */ + write_byte(ext->vararg); + /* reserved */ + write_byte(0); + /* desc_param */ + write_int(0); + /* name */ + /*sym = TABLE_get_symbol(Class->table, ext->name); + write_int(get_string(sym->name, sym->len));*/ + /* alias name */ + if (ext->alias == NO_SYMBOL) + sym = TABLE_get_symbol(Class->table, ext->name); + else + sym = TABLE_get_symbol(Class->string, ext->alias); + write_int(get_string(sym->name, sym->len)); + /* library name */ + sym = TABLE_get_symbol(Class->string, ext->library); + write_int(get_string(sym->name, sym->len)); + } + + end_section(); +} + + +static void output_method(void) +{ + int i, n; + FUNCTION *func; + uchar flag; + /*SYMBOL *sym;*/ + + n = ARRAY_count(Class->function); + + begin_section("Methods", 8 * sizeof(int)); + + for (i = 0; i < n; i++) + { + func = &Class->function[i]; + + write_type(func->type); + write_byte(func->nparam); + write_byte(func->npmin); + write_byte(func->vararg); + + flag = func->fast; + if (func->use_is_missing) + flag += 2; + if (func->unsafe) + flag += 4; + + write_byte(flag); + + write_short(func->nlocal); + write_short(func->nctrl); + write_short(func->stack); + + /* gestion d'erreur */ + if (func->catch && func->finally) + write_short(Min(func->finally, func->catch)); + else if (func->catch) + write_short(func->catch); + else + write_short(func->finally); + + /* addr_code */ + write_int(func->ncode); + /* desc_param */ + write_int(0); + /* desc_local */ + write_int(0); + /* debug_info */ + write_int(0); + + } + + end_section(); +} + + +static void output_param_local(void) +{ + int i, j; + FUNCTION *func; + EVENT *event; + EXTFUNC *ext; + PARAM *param; + + begin_section("Parameters", sizeof(int)); + + for (i = 0; i < ARRAY_count(Class->function); i++) + { + func = &Class->function[i]; + + if (func->name != NO_SYMBOL) + { + /* Les param�res sont remis dans les variables locales ! + for (j = 0; j < func->nparam; j++) + { + param = &func->param[j]; + write_int(param->type); + } + */ + + for (j = 0; j < func->nlocal + func->nparam; j++) + { + param = &func->local[j]; + + /* type */ + write_type(param->type); + } + } + } + + for (i = 0; i < ARRAY_count(Class->event); i++) + { + event = &Class->event[i]; + + for (j = 0; j < event->nparam; j++) + { + param = &event->param[j]; + + /* type */ + write_type(param->type); + } + } + + for (i = 0; i < ARRAY_count(Class->ext_func); i++) + { + ext = &Class->ext_func[i]; + + for (j = 0; j < ext->nparam; j++) + { + param = &ext->param[j]; + + /* type */ + write_type(param->type); + } + } + + end_section(); +} + + +static void output_array(void) +{ + int i, j, p; + CLASS_ARRAY *array; + + begin_section("Arrays", sizeof(int)); + + p = ARRAY_count(Class->array) * sizeof(int); + + for (i = 0; i < ARRAY_count(Class->array); i++) + { + array = &Class->array[i]; + write_int(p); + p += sizeof(int) + array->ndim * sizeof(int); + } + + for (i = 0; i < ARRAY_count(Class->array); i++) + { + array = &Class->array[i]; + + write_type(array->type); + + for (j = 0; j < array->ndim; j++) + { + p = array->dim[j]; + if (j == (array->ndim - 1)) + p = (-p); + + write_int(p); + } + } + + end_section(); +} + + +static void output_structure(void) +{ + int i, j; + CLASS_STRUCT *structure; + VARIABLE *field; + SYMBOL *sym; + + for (i = 0; i < ARRAY_count(Class->structure); i++) + { + structure = &Class->structure[i]; + + begin_section("Structure", sizeof(int)); + + // Structure name + sym = TABLE_get_symbol(Class->table, structure->index); + write_int(get_string(sym->name, sym->len)); + + for (j = 0; j < structure->nfield; j++) + { + field = &structure->field[j]; + + // Field name + sym = TABLE_get_symbol(Class->table, field->index); + write_int(get_string(sym->name, sym->len)); + + // Field datatype + write_type(field->type); + } + + end_section(); + } +} + +static void output_code() +{ + int i, j; + int n; + FUNCTION *func; + + for (i = 0; i < ARRAY_count(Class->function); i++) + { + func = &Class->function[i]; + + n = func->ncode; + + begin_section("Code", sizeof(short)); + if (_swap) + { + for (j = 0; j < n; j++) + write_short(func->code[j]); + } + else + write_buffer(func->code, n * sizeof(short)); + end_section(); + } +} + + +static void output_debug_global() +{ + int i, nn = 0; + CLASS_SYMBOL *csym; + TYPE type; + + begin_section("Global symbol table", 4 * sizeof(int)); + + for (i = 0; i < TABLE_count(Class->table); i++) + { + csym = (CLASS_SYMBOL *)TABLE_get_symbol_sort(Class->table, i); + //csym = (CLASS_SYMBOL *)TABLE_get_symbol(Class->table, csym->symbol.sort); + + type = csym->global.type; + switch (TYPE_get_kind(type)) + { + case TK_VARIABLE: + case TK_FUNCTION: + case TK_PROPERTY: + case TK_EXTERN: + case TK_CONST: + + nn++; + /* name */ + write_int(get_string(csym->symbol.name, csym->symbol.len)); + /* len */ + write_int(csym->symbol.len); + /* type */ + write_type(csym->global.type); + /* value */ + write_int(csym->global.value); + + break; + + default: + /* ignore */ + break; + } + } + + end_section(); + + begin_section("Global symbol table sort", sizeof(ushort)); + + for (i = 0; i < nn; i++) + write_short(i); + + end_section(); +} + + +static void output_debug_method() +{ + int i, j, n; + SYMBOL *sym; + OUTPUT_SYMBOL *osym; + CLASS_SYMBOL *csym; + PARAM *param; + FUNCTION *func; + TABLE *table; + int index; + + begin_section("Debug method info", 5 * sizeof(int)); + + for (i = 0; i < ARRAY_count(Class->function); i++) + { + func = &Class->function[i]; + + if (func->pos_line != NULL && func->line < FORM_FIRST_LINE) + { + /* line */ + write_short(func->line); + write_short(ARRAY_count(func->pos_line)); + /* pos_line */ + write_int(0); + /* nom */ + sym = TABLE_get_symbol(Class->table, func->name); + write_int(get_string(sym->name, sym->len)); + /* local symbols */ + write_int(0); + /* n_local */ + write_short(0); + /* reserved */ + write_short(0); + } + else + { + write_short(0); + write_short(0); + write_int(0); + write_int(0); + write_int(0); + write_short(0); + write_short(0); + } + } + + end_section(); + + for (i = 0; i < ARRAY_count(Class->function); i++) + { + func = &Class->function[i]; + + n = func->pos_line ? ARRAY_count(func->pos_line) : 0; + + begin_section("Debug method lines", sizeof(short)); + + if (_swap) + { + for (j = 0; j < n; j++) + write_short(func->pos_line[j]); + } + else + write_buffer(func->pos_line, n * sizeof(short)); + + end_section(); + } + + for (i = 0; i < ARRAY_count(Class->function); i++) + { + func = &Class->function[i]; + + begin_section("Debug method local symbols", sizeof(int) * 3); + + if (func->name != NO_SYMBOL) + { + sym = (SYMBOL *)TABLE_get_symbol(Class->table, func->name); + /*printf("%.*s()\n", sym->len, sym->name);*/ + + TABLE_create(&table, sizeof(OUTPUT_SYMBOL), TF_IGNORE_CASE); + + for (j = 0; j < func->nlocal + func->nparam; j++) + { + param = &func->local[j]; + csym = (CLASS_SYMBOL *)TABLE_get_symbol(Class->table, param->index); + + TABLE_add_symbol(table, csym->symbol.name, csym->symbol.len, &index); + osym = (OUTPUT_SYMBOL *)TABLE_get_symbol(table, index); + osym->value = param->value;/*TYPE_long(param->type);*/ + } + + for (j = 0; j < TABLE_count(table); j++) + { + param = &func->local[j]; + osym = (OUTPUT_SYMBOL *)TABLE_get_symbol(table, j); + + /* name */ + write_int(get_string(osym->sym.name, osym->sym.len)); + /* len */ + write_int(osym->sym.len); + /* value */ + write_int(osym->value); + + /*printf("%.*s %ld\n", osym->sym.len, osym->sym.name, osym->value);*/ + } + + // We actually do not use the table sort, so do not output it! + + TABLE_delete(&table); + } + + end_section(); + } +} + + +static void output_debug_filename(void) +{ + char *path; + int n; + + begin_section("Debug file name", 1); + + path = (char *)FILE_get_name(JOB->name); + + n = strlen(path); + write_buffer(path, n); + /*write_pad();*/ + + end_section(); +} + + +static void output_string(void) +{ + int i; + SYMBOL *sym; + + begin_section("Strings", 1); + + for (i = 0; i < TABLE_count(StringTable); i++) + { + sym = TABLE_get_symbol(StringTable, i); + write_string(sym->name, sym->len); + } + + end_section(); +} + + +char *OUTPUT_get_file(const char *file) +{ + char *output; + char *p; + char *name; + + name = STR_copy(FILE_get_name(file)); + + for (p = name; *p; p++) + { + if (*p == '.') + { + *p = 0; + break; + } + + *p = toupper(*p); + } + + output = ".gambas"; + if (mkdir(output, 0777) == 0) + FILE_set_owner(output, COMP_project); + + output = STR_copy(FILE_cat(output, name, NULL)); + + //STR_free(dir); + STR_free(name); + + return output; +} + + +char *OUTPUT_get_trans_file(const char *file) +{ + char *output; + //char *dir; + char *name; + + //dir = STR_copy(FILE_get_dir(file)); + name = STR_copy(FILE_get_name(file)); + + output = ".lang"; //(char *)FILE_cat(dir, ".lang", NULL); + if (mkdir(output, 0777) == 0) + FILE_set_owner(output, COMP_project); + + output = (char *)FILE_cat(".lang", name, NULL); + output = STR_copy(FILE_set_ext(output, "pot")); + + //STR_free(dir); + STR_free(name); + + return output; +} + +static void output_translation(void) +{ + FILE *file; + int i, j, n; + CONSTANT *constant; + SYMBOL *sym; + unsigned char c; + time_t t; + struct tm *now; + + /*printf("Generating %s\n", JOB->tname);*/ + + if (!JOB->trans) + { + JOB->tname = OUTPUT_get_trans_file(JOB->name); + FILE_unlink(JOB->tname); + return; + } + + file = fopen(JOB->tname, "w"); + if (!file) + THROW("Cannot create file: &1", JOB->tname); + + fprintf(file, "# %s\n# Generated by the Gambas " GAMBAS_FULL_VERSION_STRING " compiler\n\n", JOB->name); + + fprintf(file, + "#, fuzzy\n" + "msgid \"\"\n" + "msgstr \"\"\n" + "\"Project-Id-Version: $(PACKAGE) $(VERSION)\\n\"\n"); + + t = time(NULL); + now = gmtime(&t); + + fprintf(file, "\"POT-Creation-Date: %04d-%02d-%02d %02d:%02d UTC\\n\"\n", + now->tm_year + 1900, now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min); + + // "\"PO-Revision-Date: $(DATE)\\n\"\n" // YEAR-MO-DA HO:MI+ZONE + fprintf(file, + "\"MIME-Version: 1.0\\n\"\n" + "\"Content-Type: text/plain; charset=UTF-8\\n\"\n" + "\"Content-Transfer-Encoding: 8bit\\n\"\n\n"); + + n = ARRAY_count(Class->constant); + + for (i = 0; i < n; i++) + { + constant = &Class->constant[i]; + if (TYPE_get_id(constant->type) != T_CSTRING) + continue; + + sym = TABLE_get_symbol(Class->string, constant->value); + if (sym->len == 0) + continue; + + for (j = 0; j < sym->len; j++) + { + c = sym->name[j]; + if (c > ' ') + break; + } + + if (j >= sym->len) + continue; + + if (constant->line < FORM_FIRST_LINE) + fprintf(file, "#: %s:%d\n", FILE_get_name(JOB->name), constant->line); + else + fprintf(file, "#: %s:%d\n", FILE_get_name(JOB->form), constant->line - FORM_FIRST_LINE); + + fprintf(file, "msgid \""); + + for (j = 0; j < sym->len; j++) + { + c = sym->name[j]; + if (c == '\n') + fprintf(file, "\\n"); + else if (c == '\t') + fprintf(file, "\\t"); + else if (c == '\r') + fprintf(file, "\\r"); + else if (c == '\\') + fprintf(file, "\\\\"); + else if (c == '"') + fprintf(file, "\\\""); + else + fputc(c, file); + } + + fprintf(file, "\"\n"); + fprintf(file, "msgstr \"\"\n\n"); + + sym->len = 0; /* Horrible hack for not writing the same string twice */ + } + + fclose(file); + FILE_set_owner(JOB->tname, COMP_project); +} + + +static void output_finalize(void) +{ + int i; + + flush_buffer(); + + for (i = 0; i < ARRAY_count(_change); i++) + { + fseek(_file, _change[i].pos, SEEK_SET); + if (fwrite(&_change[i].val, sizeof(int), 1, _file) != 1) + THROW("Write error"); + } +} + + +void OUTPUT_do(bool swap) +{ + const char *name; + + _swap = swap; + + output_init(); + + // The first string is always the class name + get_string(JOB->class->name, strlen(JOB->class->name)); + + name = JOB->output; + + #ifdef DEBUG + printf("Output to %s\n", name); + #endif + + _file = fopen(name, "w"); + if (!_file) + THROW("Cannot create file: &1", name); + + Class = JOB->class; + + #ifdef DEBUG + printf("pos = %lu\n", get_pos()); + #endif + + output_header(); + output_class(); + output_desc(); + output_constant(); + output_class_ref(); + output_unknown_ref(); + output_static(); + output_dynamic(); + output_event(); + output_extern(); + output_method(); + output_param_local(); + output_array(); + output_structure(); + output_code(); + + if (JOB->debug) + { + output_debug_global(); + output_debug_method(); + output_debug_filename(); + } + + output_string(); + + output_finalize(); + + fclose(_file); + FILE_set_owner(JOB->output, COMP_project); + + output_exit(); + + // Translations + output_translation(); +} diff --git a/main/gbc/gbc_output.h b/main/gbc/gbc_output.h new file mode 100644 index 00000000..fa60f4d4 --- /dev/null +++ b/main/gbc/gbc_output.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + gbc_output.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_OUTPUT_H +#define __GBC_OUTPUT_H + +#include "gb_table.h" +#include "gb_class_desc_common.h" + +typedef + struct { + SYMBOL sym; + int value; + } + OUTPUT_SYMBOL; + +typedef + struct { + off_t pos; + uint val; + } + OUTPUT_CHANGE; + +#define OUTPUT_BUFFER_SIZE 16384 + +void OUTPUT_do(bool swap); +char *OUTPUT_get_file(const char *file); +char *OUTPUT_get_trans_file(const char *file); + +#endif + + diff --git a/main/gbc/gbc_pcode.c b/main/gbc/gbc_pcode.c new file mode 100644 index 00000000..fea00e27 --- /dev/null +++ b/main/gbc/gbc_pcode.c @@ -0,0 +1,28 @@ +/*************************************************************************** + + gbc_pcode.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_PCODE_C + +#define PROJECT_COMP + +#include "gb_pcode_temp.h" diff --git a/main/gbc/gbc_preprocess.c b/main/gbc/gbc_preprocess.c new file mode 100644 index 00000000..7b019a9c --- /dev/null +++ b/main/gbc/gbc_preprocess.c @@ -0,0 +1,325 @@ +/*************************************************************************** + + gbc_preprocess.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_PREPROCESS_C + +#include "gb_common.h" +#include "gb_common_case.h" +#include "gb_error.h" +#include "gb_table.h" + +#include "gbc_compile.h" +#include "gbc_read.h" +#include "gbc_trans.h" +#include "gbc_preprocess.h" + +typedef + struct { + bool ignore; + int ignore_level; + } + PREP_STATE; + +#define MAX_LEVEL 16 + +int PREP_next_line; + +static PREP_STATE _stack[MAX_LEVEL]; + +static int _level; +static bool _ignore; +static int _ignore_level; +static PATTERN *_current; + + +static int get_expression(void); + +static bool is_current(int res) +{ + if (PATTERN_is(*_current, res)) + { + _current++; + return TRUE; + } + else + return FALSE; +} + +static bool compare_value(const char *value) +{ + PATTERN op; + SYMBOL *sym; + char version[8]; + int n, major, minor; + int diff; + + op = *_current++; + if (!PATTERN_is_reserved(op)) + THROW("Missing operator"); + + if (!PATTERN_is_string(*_current)) + THROW("String expected"); + + sym = TABLE_get_symbol(JOB->class->string, PATTERN_index(*_current)); + _current++; + + if (value) + { + if (strlen(value) != sym->len) + diff = 1; + else + diff = strncasecmp(value, sym->name, sym->len); + + switch (PATTERN_index(op)) + { + case RS_EQUAL: return diff == 0; + case RS_NE: return diff != 0; + default: THROW("Equality or inequality operator expected"); + } + } + else + { + if (sym->len < 1 || sym->len >= sizeof(version)) + THROW("Bad version string"); + + memcpy(version, sym->name, sym->len); + version[sym->len] = 0; + n = sscanf(version, "%d.%d", &major, &minor); + if (n == 0) + THROW("Bad version string"); + else if (n == 1) + minor = 0; + + diff = GAMBAS_VERSION - major; + if (!diff) + diff = GAMBAS_MINOR_VERSION - minor; + + switch (PATTERN_index(op)) + { + case RS_EQUAL: return diff == 0; + case RS_NE: return diff != 0; + case RS_GT: return diff > 0; + case RS_LT: return diff < 0; + case RS_GE: return diff >= 0; + case RS_LE: return diff <= 0; + default: THROW("Comparison operator expected"); + } + } +} + +#define compare_symbol(_symbol, _name, _len) ((strlen(_symbol) == _len) && !strncasecmp((_symbol), (_name), (_len))) + +static int get_symbol(const char *name, int len) +{ + if (compare_symbol("system", name, len)) + return compare_value(SYSTEM); + + if (compare_symbol("arch", name, len) || compare_symbol("architecture", name, len)) + return compare_value(ARCHITECTURE); + + if (compare_symbol("version", name, len) || compare_symbol("gambas", name, len)) + return compare_value(NULL); + + /*if (compare_symbol("debug", name, len)) + return JOB->debug;*/ + + /*if (compare_symbol("true", name, len)) + return TRUE; + + if (compare_symbol("false", name, len)) + return FALSE;*/ + + return FALSE; +} + +static int get_value(void) +{ + int value; + SYMBOL *sym; + + if (is_current(RS_LBRA)) + { + value = get_expression(); + if (!is_current(RS_RBRA)) + THROW("Missing right brace"); + return value; + } + + if (is_current(RS_NOT)) + return !get_value(); + + if (PATTERN_is_identifier(*_current)) + { + sym = TABLE_get_symbol(JOB->class->table, PATTERN_index(*_current)); + _current++; + value = get_symbol(sym->name, sym->len); + return value; + } + else if (is_current(RS_FALSE)) + return 0; + else if (is_current(RS_TRUE)) + return 1; + else if (is_current(RS_DEBUG)) + return JOB->debug; + else if (is_current(RS_EXEC)) + return JOB->exec; + + return 0; +} + +static int get_expression(void) +{ + int value; + + for(;;) + { + value = get_value(); + + if (is_current(RS_AND)) + { + if (!value) + return FALSE; + + continue; + } + + if (is_current(RS_OR)) + { + if (value) + return TRUE; + + continue; + } + + if (PATTERN_is_newline(*_current) || PATTERN_is(*_current, RS_RBRA)) + return value; + + THROW(E_SYNTAX); + } +} + +void PREP_init(void) +{ + _level = 0; + _ignore = FALSE; + _current = NULL; +} + +void PREP_exit(void) +{ + if (_level) + THROW("Missing #Endif"); +} + +int PREP_analyze(PATTERN *line) +{ + bool test; + + if (PATTERN_is(*line, RS_P_IF)) + { + if (_level >= MAX_LEVEL) + THROW("Too many imbricated #If...#Endif"); + + _stack[_level].ignore = _ignore; + _stack[_level].ignore_level = _ignore_level; + + line++; + _level++; + + if (!_ignore) + { + _current = line; + if (!get_expression()) + { + _ignore = TRUE; + _ignore_level = _level; + } + } + } + else if (PATTERN_is(*line, RS_P_ELSE)) + { + //bool else_if = FALSE; + + if (!_level) + THROW_UNEXPECTED(line); + + test = TRUE; + line++; + + if (!_ignore) + { + _ignore = TRUE; + _ignore_level = _level - 1; + } + else + { + if (_level == _ignore_level) + { + if (PATTERN_is(*line, RS_IF)) + { + line++; + + _current = line; + test = get_expression(); + + //else_if = TRUE; + } + } + + if (_level == _ignore_level) + _ignore = !test; + } + } + else if (PATTERN_is(*line, RS_P_ENDIF)) + { + if (!_level) + THROW_UNEXPECTED(line); + + _level--; + _ignore = _stack[_level].ignore; + _ignore_level = _stack[_level].ignore_level; + } + else if (PATTERN_is(*line, RS_P_LINE)) + { + TRANS_NUMBER result; + + line++; + if (!PATTERN_is_number(*line)) + THROW_UNEXPECTED(line); + + TRANS_get_number(PATTERN_index(*line), &result); + if (result.type != T_INTEGER) + THROW_UNEXPECTED(line); + + PREP_next_line = result.ival - 1; + return PREP_LINE; + } + else if (PATTERN_is(*line, RS_P_CONST)) + { + // TODO + } + else + THROW(E_SYNTAX); + + return _ignore ? PREP_IGNORE : PREP_CONTINUE; +} diff --git a/main/gbc/gbc_preprocess.h b/main/gbc/gbc_preprocess.h new file mode 100644 index 00000000..3734ac44 --- /dev/null +++ b/main/gbc/gbc_preprocess.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + gbc_preprocess.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_PREPROCESS_H +#define __GBC_PREPROCESS_H + +enum { PREP_CONTINUE, PREP_IGNORE, PREP_LINE }; + +#ifndef __GBC_PREPROCESS_C +extern int PREP_next_line; +#endif + +void PREP_init(void); +void PREP_exit(void); +int PREP_analyze(PATTERN *line); + +#endif diff --git a/main/gbc/gbc_read.c b/main/gbc/gbc_read.c new file mode 100644 index 00000000..1f3e7c12 --- /dev/null +++ b/main/gbc/gbc_read.c @@ -0,0 +1,1241 @@ +/*************************************************************************** + + gbc_read.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_READ_C + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_common_case.h" +#include "gb_error.h" +#include "gb_table.h" +#include "gb_file.h" + +#include "gbc_compile.h" +#include "gbc_class.h" +#include "gbc_preprocess.h" +#include "gbc_help.h" +#include "gbc_read.h" + +//#define DEBUG +//#define BIG_COMMENT 1 + +static bool is_init = FALSE; +static COMPILE *comp; +static const char *source_ptr; +static int source_length; +static bool _begin_line = FALSE; +static bool _no_quote = FALSE; + +static bool _prep = FALSE; +static int _prep_index; + +static char ident_car[256]; +static char first_car[256]; +char READ_digit_car[256]; +static char noop_car[256]; +static char canres_car[256]; + +enum +{ + GOTO_BREAK, + GOTO_SPACE, + GOTO_COMMENT, + GOTO_STRING, + GOTO_IDENT, + GOTO_QUOTED_IDENT, + GOTO_NUMBER, + GOTO_ERROR, + GOTO_SHARP, + GOTO_OTHER +}; + +static void READ_init(void) +{ + unsigned char i; + + JOB->line = 1; + JOB->max_line = FORM_FIRST_LINE - 1; + JOB->column = TRUE; + + if (!is_init) + { + for (i = 0; i < 255; i++) + { + ident_car[i] = (i != 0) && ((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z') || (i >= '0' && i <= '9') || strchr("$_?@", i)); + READ_digit_car[i] = (i >= '0' && i <= '9'); + noop_car[i] = ident_car[i] || READ_digit_car[i] || i <= ' '; + canres_car[i] = (i != ':') && (i != '.') && (i != '!') && (i != '('); + + if (i == 0) + first_car[i] = GOTO_BREAK; + else if (i <= ' ') + first_car[i] = GOTO_SPACE; + else if (i == '\'') + first_car[i] = GOTO_COMMENT; + else if (i == '"') + first_car[i] = GOTO_STRING; + else if (i == '#') + first_car[i] = GOTO_SHARP; + else if ((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z') || i == '$' || i == '_') + first_car[i] = GOTO_IDENT; + else if (i == '{') + first_car[i] = GOTO_QUOTED_IDENT; + else if (i >= '0' && i <= '9') + first_car[i] = GOTO_NUMBER; + else if (i >= 127) + first_car[i] = GOTO_ERROR; + else + first_car[i] = GOTO_OTHER; + } + + is_init = TRUE; + } +} + + +static void READ_exit(void) +{ + char *name = NULL; + int len; + int index; + bool local; + bool has_static; + char c; + + local = FALSE; + + for(;;) + { + COMPILE_enum_class(&name, &len); + if (!len) + break; + + if (len == 1 && *name == '-') + { + local = TRUE; + } + else if (*name != '.') + { + has_static = FALSE; + + for(;;) + { + c = name[len - 1]; + if (c == '!') + { + has_static = TRUE; + len--; + } + else if (c == '?') + len--; + else + break; + } + + if (TABLE_find_symbol(JOB->class->table, name, len, &index)) + { + if (local) + index = CLASS_add_class_unused(JOB->class, index); + else + index = CLASS_add_class_exported_unused(JOB->class, index); + + JOB->class->class[index].has_static = has_static; + } + } + } + + if (JOB->verbose) + printf("\n"); + + JOB->column = FALSE; +} + +static int get_utf8_length(const char *str, int len) +{ + int ulen = 0; + int i; + + for (i = 0; i < len; i++) + { + if ((str[i] & 0xC0) != 0x80) + ulen++; + } + + return ulen; +} + + +int READ_get_column() +{ + const char *p = source_ptr; + int col = 0; + + while (p > comp->source) + { + if (*p == '\n') + { + p++; + break; + } + p--; + col++; + } + + return get_utf8_length(p, col + 1); +} + + +char *READ_get_pattern(PATTERN *pattern) +{ + int type = PATTERN_type(*pattern); + int index = PATTERN_index(*pattern); + const char *str; + const char *before = _no_quote ? "" : "'"; + const char *after = _no_quote ? "" : "'"; + + switch(type) + { + case RT_RESERVED: + str = COMP_res_info[index].name; //TABLE_get_symbol_name(COMP_res_table, index); + if (ispunct(*str)) + snprintf(COMMON_buffer, COMMON_BUF_MAX, "%s%s%s", before, str, after); + else + strcpy(COMMON_buffer, str); + break; + + case RT_NUMBER: + case RT_IDENTIFIER: + case RT_CLASS: + snprintf(COMMON_buffer, COMMON_BUF_MAX, "%s%s%s", before, TABLE_get_symbol_name(JOB->class->table, index), after); + break; + + case RT_STRING: + case RT_TSTRING: + if (_no_quote) + snprintf(COMMON_buffer, COMMON_BUF_MAX, "\"%s\"", TABLE_get_symbol_name(JOB->class->string, index)); + else + strcpy(COMMON_buffer, "string"); + break; + + case RT_COMMAND: + snprintf(COMMON_buffer, COMMON_BUF_MAX, "#%d", index); + break; + + case RT_NEWLINE: + strcpy(COMMON_buffer, "end of line"); + break; + + case RT_END: + strcpy(COMMON_buffer, "end of file"); + break; + + case RT_SUBR: + //snprintf(COMMON_buffer, COMMON_BUF_MAX, "%s%s%s", bafore, COMP_subr_info[index].name, after); + strcpy(COMMON_buffer, COMP_subr_info[index].name); + break; + + default: + sprintf(COMMON_buffer, "%s?%02X.%02X.%d?%s", before, PATTERN_type(*pattern), PATTERN_flag(*pattern), (int)PATTERN_index(*pattern), after); + } + + return COMMON_buffer; +} + +void THROW_UNEXPECTED(PATTERN *pattern) +{ + switch (PATTERN_type(*pattern)) + { + case RT_NEWLINE: case RT_END: + THROW("Unexpected end of line"); + case RT_STRING: case RT_TSTRING: + THROW("Unexpected string"); + default: + THROW("Unexpected &1", READ_get_pattern(pattern)); + } +} + +void READ_dump_pattern(PATTERN *pattern) +{ + int type = PATTERN_type(*pattern); + int index = PATTERN_index(*pattern); + + /*pos = (int)(pattern - JOB->pattern); + if (pos < 0 || pos >= JOB->pattern_count) + return; + + printf("%d ", pos);*/ + + if (PATTERN_flag(*pattern) & RT_FIRST) + printf("!"); + else + printf(" "); + + if (PATTERN_flag(*pattern) & RT_POINT) + printf("."); + else + printf(" "); + + if (PATTERN_flag(*pattern) & RT_CLASS) + printf("C"); + else + printf(" "); + + printf(" "); + + _no_quote = TRUE; + + if (type == RT_RESERVED) + printf("RESERVED %s\n", READ_get_pattern(pattern)); + else if (type == RT_NUMBER) + printf("NUMBER %s\n", READ_get_pattern(pattern)); + else if (type == RT_IDENTIFIER) + printf("IDENTIFIER %s\n", READ_get_pattern(pattern)); + else if (type == RT_CLASS) + printf("CLASS %s\n", READ_get_pattern(pattern)); + else if (type == RT_STRING) + printf("STRING %s\n", READ_get_pattern(pattern)); + else if (type == RT_TSTRING) + printf("TSTRING %s\n", READ_get_pattern(pattern)); + else if (type == RT_NEWLINE) + printf("NEWLINE (%d)\n", index); + else if (type == RT_END) + printf("END\n"); + else if (type == RT_PARAM) + printf("PARAM %d\n", index); + else if (type == RT_SUBR) + printf("SUBR %s\n", READ_get_pattern(pattern)); + else if (type == RT_COMMAND) + printf("COMMAND %d\n", index); + else + printf("? %d\n", index); + + _no_quote = FALSE; +} + + +#if 0 +static inline unsigned char get_char_offset(int offset) +{ + offset += source_ptr; + + if (offset >= source_length || offset < 0) + return 0; + else + return (unsigned char)(comp->source[offset]); +} +#endif + +#if 0 +static unsigned char get_char(void) +{ + return get_char_offset(0); +} +#endif + +#define get_char_offset(_offset) ((unsigned char)source_ptr[(_offset)]) +#define get_char() ((unsigned char)(*source_ptr)) + + +static unsigned char next_char(void) +{ + source_ptr++; + return get_char(); +} + + +#ifdef DEBUG + +static void add_pattern(int type, int index) +{ + comp->pattern[comp->pattern_count++] = PATTERN_make(type, index); + READ_dump_pattern(&comp->pattern[comp->pattern_count - 1]); +} + +#define add_pattern_no_dump(_type, _index) comp->pattern[comp->pattern_count++] = PATTERN_make((_type), (_index)); + +#else + +#define add_pattern(_type, _index) comp->pattern[comp->pattern_count++] = PATTERN_make((_type), (_index)); +#define add_pattern_no_dump add_pattern + +#endif + +static PATTERN get_last_last_pattern() +{ + if (LIKELY(comp->pattern_count > 1)) + return comp->pattern[comp->pattern_count - 2]; + else + return NULL_PATTERN; +} + +#define get_last_pattern() (comp->pattern[comp->pattern_count - 1]) + +static void jump_to_next_prep(void) +{ + unsigned char car; + const char *line_start; + + for (;;) + { + line_start = source_ptr; + + for(;;) + { + car = get_char(); + if (!car) + return; + if (car == '\n' || !car || !isspace(car)) + break; + source_ptr++; + } + + if (car == '#') + { + source_ptr = line_start; + return; + } + + for(;;) + { + car = get_char(); + if (!car) + return; + source_ptr++; + if (car == '\n') + break; + } + + add_pattern(RT_NEWLINE, comp->line); + comp->line++; + } +} + +static void add_newline() +{ + int action = PREP_CONTINUE; + + if (_prep) + { + int line = comp->line; + + add_pattern_no_dump(RT_NEWLINE, comp->line); + action = PREP_analyze(&comp->pattern[_prep_index]); + _prep = FALSE; + + comp->pattern_count = _prep_index; + comp->line = line; + } + + if (action == PREP_LINE) + comp->line = PREP_next_line; + + // Void lines must act as void help comments + if (comp->line > 0 && PATTERN_is_newline(get_last_pattern())) + HELP_add_at_current_line("\n"); + + add_pattern(RT_NEWLINE, comp->line); + comp->line++; + + if (action == PREP_IGNORE) + jump_to_next_prep(); +} + +static void add_end() +{ + add_pattern(RT_END, 0); + comp->line++; +} + + +static bool add_number() +{ + unsigned char car; + const char *start; + int index; + char sign; + PATTERN last_pattern; + bool has_digit; + + start = source_ptr; + car = get_char(); + + if (car == '-' || car == '+') + { + sign = car; + car = next_char(); + + if (car == 'I' || car == 'i') + { + car = next_char(); + if (car == 'N' || car == 'n') + { + car = next_char(); + if (car == 'F' || car == 'f') + { + car = next_char(); + add_pattern(RT_RESERVED, RESERVED_find_word(start, 4)); + return FALSE; + } + } + + goto NOT_A_NUMBER; + } + } + else + sign = 0; + + if (car == '&') + { + car = toupper(next_char()); + + if (car == 'H') + goto READ_HEXA; + else if (car == 'X') + goto READ_BINARY; + else if (car == 'O') + goto READ_OCTAL; + else + { + source_ptr--; + goto READ_HEXA; + } + } + else if (car == '%') + goto READ_BINARY; + else if (isdigit(car)) + goto READ_NUMBER; + else + goto NOT_A_NUMBER; + +READ_BINARY: + + has_digit = FALSE; + for (;;) + { + car = next_char(); + if (car != '0' && car != '1') + break; + has_digit = TRUE; + } + + goto END_BINARY_HEXA; + +READ_OCTAL: + + has_digit = FALSE; + for (;;) + { + car = next_char(); + if (car < '0' || car > '7') + break; + has_digit = TRUE; + } + + goto END_BINARY_HEXA; + +READ_HEXA: + + has_digit = FALSE; + for (;;) + { + car = next_char(); + if (!isxdigit(car)) + break; + has_digit = TRUE; + } + +END_BINARY_HEXA: + + if (!has_digit) + goto NOT_A_NUMBER; + + if (car == '&') + car = next_char(); + else if (first_car[car] == GOTO_IDENT) + goto NOT_A_NUMBER; + + goto END; + +READ_NUMBER: + + while (isdigit(car)) + car = next_char(); + + if (car == '.') + { + do + { + car = next_char(); + } + while (isdigit(car)); + } + + if (toupper(car) == 'E') + { + car = next_char(); + if (car == '+' || car == '-') + car = next_char(); + + while (isdigit(car)) + car = next_char(); + } + else if (toupper(car) == 'I') + { + car = next_char(); + } + + goto END; + +END: + + last_pattern = get_last_pattern(); + + if (sign && !PATTERN_is_null(last_pattern) && (!PATTERN_is_reserved(last_pattern) || PATTERN_is(last_pattern, RS_RBRA) || PATTERN_is(last_pattern, RS_RSQR))) + { + add_pattern(RT_RESERVED, RESERVED_find_word(&sign, 1)); + TABLE_add_symbol(comp->class->table, start + 1, source_ptr - start - 1, &index); + add_pattern(RT_NUMBER, index); + } + else + { + TABLE_add_symbol(comp->class->table, start, source_ptr - start, &index); + add_pattern(RT_NUMBER, index); + } + + return FALSE; + +NOT_A_NUMBER: + + source_ptr = start; + return TRUE; +} + + + +static void add_identifier() +{ + unsigned char car; + const char *start; + int len; + int index; + int type; + int flag; + PATTERN last_pattern, last_last_pattern; + bool not_first; + bool can_be_reserved; + bool last_identifier, last_type, last_class, last_pub; + + last_pattern = get_last_pattern(); + + if (PATTERN_is_reserved(last_pattern)) + { + flag = RES_get_ident_flag(PATTERN_index(last_pattern)); + if (flag & RSF_PREV) + { + last_last_pattern = get_last_last_pattern(); + if (PATTERN_is_reserved(last_last_pattern)) + flag = RES_get_ident_flag(PATTERN_index(last_last_pattern)); + else + flag = 0; + } + } + else + flag = 0; + + type = RT_IDENTIFIER; + + start = source_ptr; + for(;;) + { + source_ptr++; + if (!ident_car[get_char()]) + break; + } + + len = source_ptr - start; + + last_class = (flag & RSF_CLASS) != 0; + last_type = (flag & RSF_AS) != 0; + + if (last_type) + { + source_ptr--; + + for(;;) + { + source_ptr++; + len++; + car = get_char(); + if (car == '[') + { + car = get_char_offset(1); + if (car == ']') + { + source_ptr++; + len++; + TABLE_add_symbol(comp->class->table, start, len - 2, &index); + continue; + } + } + + len--; + break; + } + } + + not_first = (flag & RSF_POINT) != 0; + + car = get_char(); + + //can_be_reserved = !not_first && TABLE_find_symbol(COMP_res_table, &comp->source[start], len, NULL, &index); + + can_be_reserved = !not_first && !last_class; + + if (can_be_reserved) + { + index = RESERVED_find_word(start, len); + can_be_reserved = (index >= 0); + } + + if (can_be_reserved) + { + static void *jump[] = { + &&__OTHERS, &&__ME_NEW_LAST_SUPER, &&__CLASS, &&__STRUCT, &&__SUB_PROCEDURE_FUNCTION, &&__CONST_EXTERN, &&__ENUM, &&__READ, &&__DATATYPE + }; + + last_identifier = (flag & RSF_IDENT) != 0; + last_pub = (flag & RSF_PUB) != 0; + + goto *jump[RES_get_read_switch(index)]; + + do + { + __ME_NEW_LAST_SUPER: + can_be_reserved = !last_identifier; + break; + + __CLASS: + can_be_reserved = canres_car[car] && _begin_line; + break; + + __STRUCT: + can_be_reserved = canres_car[car] && (_begin_line || last_pub || PATTERN_is(last_pattern, RS_AS) || PATTERN_is(last_pattern, RS_END) || PATTERN_is(last_pattern, RS_NEW)); + break; + + __SUB_PROCEDURE_FUNCTION: + can_be_reserved = canres_car[car] && (_begin_line || last_pub || PATTERN_is(last_pattern, RS_END)); + break; + + __CONST_EXTERN: + can_be_reserved = canres_car[car] && (_begin_line || last_pub); + break; + + __ENUM: + can_be_reserved = canres_car[car] && (_begin_line || last_pub); + break; + + __READ: + can_be_reserved = canres_car[car] && (!last_identifier || PATTERN_is(last_pattern, RS_PROPERTY)); + break; + + __DATATYPE: + if (car == '[' && get_char_offset(1) == ']') + { + len += 2; + source_ptr += 2; + can_be_reserved = FALSE; + } + else + { + if (last_type || PATTERN_is(last_pattern, RS_OPEN)) + can_be_reserved = TRUE; + else + can_be_reserved = FALSE; + } + break; + + __OTHERS: + if (last_type || last_identifier || (PATTERN_is(last_pattern, RS_LBRA) && car == ')' && PATTERN_is_reserved(get_last_last_pattern()))) + can_be_reserved = FALSE; + else + can_be_reserved = canres_car[car]; + break; + } + while (0); + } + + if (can_be_reserved) + { + type = RT_RESERVED; + goto __ADD_PATTERN; + } + + if ((flag == 0) && car != '.' && car != '!') + { + index = RESERVED_find_subr(start, len); + if (index >= 0) + { + if (COMP_subr_info[index].min_param == 0 || car == '(') + { + type = RT_SUBR; + goto __ADD_PATTERN; + } + } + } + + if (last_type) + type = RT_CLASS; + + if (flag & RSF_EVENT) + { + start--; + len++; + *((char *)start) = ':'; + } + + if (PATTERN_is(last_pattern, RS_EXCL)) + { + TABLE_add_symbol(comp->class->string, start, len, &index); + type = RT_STRING; + } + else + TABLE_add_symbol(comp->class->table, start, len, &index); + +__ADD_PATTERN: + + add_pattern(type, index); +} + + +static void add_quoted_identifier(void) +{ + unsigned char car; + const char *start; + int len; + int index; + int type; + PATTERN last_pattern; + + last_pattern = get_last_pattern(); + + type = RT_IDENTIFIER; + + start = source_ptr; + len = 0; + + for(;;) + { + car = next_char(); + if (!ident_car[car]) + break; + len++; + } + + if (car != '}') + THROW("Missing '}'"); + + if (len == 0) + THROW("Void identifier"); + + source_ptr++; + start++; + + if (PATTERN_is(last_pattern, RS_EVENT) || PATTERN_is(last_pattern, RS_RAISE)) + { + start--; + len++; + *((char *)start) = ':'; + } + + if (PATTERN_is(last_pattern, RS_EXCL)) + { + TABLE_add_symbol(comp->class->string, start, len, &index); + type = RT_STRING; + } + else + TABLE_add_symbol(comp->class->table, start, len, &index); + + add_pattern(type, index); +} + + +static void add_operator() +{ + unsigned char car; + const char *start; + const char *end; + int len; + int op = NO_SYMBOL; + int index; + + start = source_ptr; + end = start; + len = 1; + + for(;;) + { + source_ptr++; + + index = RESERVED_find_word(start, len); + if (index >= 0) + { + op = index; + end = source_ptr; + } + + car = get_char(); + //if (!isascii(car) || !ispunct(car)) + if (noop_car[car]) + break; + len++; + } + + source_ptr = end; + + if (op < 0) + THROW("Unknown operator"); + + add_pattern(RT_RESERVED, op); +} + + +static int xdigit_val(unsigned char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + else if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + else if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + else + return (-1); +} + + +static void add_string() +{ + unsigned char car; + const char *start; + int len; + int index; + ushort newline; + bool jump; + char *p; + const char *end; + int i; + + start = end = source_ptr; + len = 0; + newline = 0; + jump = FALSE; + p = (char *)source_ptr; + + for(;;) + { + source_ptr++; + car = get_char(); + + if (jump) + { + if (car == '\n') + newline++; + else if (car == '"') + jump = FALSE; + else if (!car || !isspace(car)) + break; + } + else + { + p++; + len++; + + if (car == '\n') + THROW("Non terminated string"); + + if (car == '\\') + { + source_ptr++; + car = get_char(); + + if (car == 'n') + *p = '\n'; + else if (car == 't') + *p = '\t'; + else if (car == 'r') + *p = '\r'; + else if (car == 'b') + *p = '\b'; + else if (car == 'v') + *p = '\v'; + else if (car == 'f') + *p = '\f'; + else if (car == 'e') + *p = '\x1B'; + else if (car == '0') + *p = 0; + else if (car == '\"' || car == '\'' || car == '\\') + *p = car; + else + { + if (car == 'x') + { + i = xdigit_val(get_char_offset(1)); + if (i >= 0) + { + car = i; + i = xdigit_val(get_char_offset(2)); + if (i >= 0) + { + car = (car << 4) | (uchar)i; + *p = car; + source_ptr += 2; + continue; + } + } + } + + THROW("Bad character constant in string"); + } + } + else if (car == '"') + { + p--; + len--; + end = source_ptr; + jump = TRUE; + comp->line += newline; + newline = 0; + } + else + *p = car; + } + } + + if (len > 0) + { + TABLE_add_symbol(comp->class->string, start + 1, len, &index); + add_pattern(RT_STRING, index); + } + else + add_pattern(RT_STRING, VOID_STRING_INDEX); + + source_ptr = end + 1; + //for (i = 0; i < newline; i++) + // add_newline(); +} + + +#if 0 +static void add_command() +{ + unsigned char car; + const char *start; + int len; + + start = source_ptr; + len = 0; + + for(;;) + { + source_ptr++; + car = get_char(); + if (car == '\n' || !car) + break; + len++; + } + + if (len) + { + //TABLE_add_symbol(comp->class->string, start + 1, len, NULL, &index); + if (len == 7 && !strncasecmp(start + 1, "SECTION", 7)) + add_pattern(RT_COMMAND, RC_SECTION); + } + + add_newline(); +} +#endif + +void READ_do(void) +{ + static void *jump_char[] = + { + &&__BREAK, + &&__SPACE, + &&__COMMENT, + &&__STRING, + &&__IDENT, + &&__QUOTED_IDENT, + &&__NUMBER, + &&__ERROR, + &&__SHARP, + &&__OTHER + }; + + unsigned char car; + + comp = JOB; + + READ_init(); + PREP_init(); + + //add_pattern(RT_NEWLINE, 0); + + source_ptr = comp->source; + source_length = BUFFER_length(comp->source); + _begin_line = TRUE; + _prep = FALSE; + + //while (source_ptr < source_length) + for(;;) + { + car = get_char(); + goto *jump_char[(int)first_car[car]]; + + __ERROR: + + THROW("Syntax error"); + + __SPACE: + + source_ptr++; + if (car == '\n') + { + add_newline(); + _begin_line = TRUE; + } + continue; + + __COMMENT: + + source_ptr++; + + if (HELP_is_help_comment(source_ptr)) + HELP_add_at_current_line(source_ptr); + + car = get_char(); + while (car != '\n') + { + source_ptr++; + car = get_char(); + } + + _begin_line = FALSE; + continue; + + __STRING: + + add_string(); + _begin_line = FALSE; + continue; + + __IDENT: + + add_identifier(); + _begin_line = FALSE; + continue; + + __QUOTED_IDENT: + + add_quoted_identifier(); + _begin_line = FALSE; + continue; + + __NUMBER: + + add_number(); + _begin_line = FALSE; + continue; + + __SHARP: + + if (_begin_line) + { + _prep = TRUE; + _prep_index = comp->pattern_count; + + add_identifier(); + _begin_line = FALSE; + continue; + } + else + goto __OTHER; + + __OTHER: + +#if BIG_COMMENT + if (car == '/' && get_char_offset(1) == '*') + { + for(;;) + { + source_ptr++; + car = get_char(); + if (car == 0) + break; + if (car == '*' && get_char_offset(1) == '/') + { + source_ptr += 2; + break; + } + if (car == '\n') + add_newline(); + } + + _begin_line = FALSE; + continue; + } +#endif + + if (add_number()) + add_operator(); + + _begin_line = FALSE; + } + +__BREAK: + + // We add end markers to simplify the compiler job, when it needs to look + // at many patterns in one shot. + + JOB->max_line = JOB->line - 1; + + add_newline(); + add_end(); + add_end(); + add_end(); + add_end(); + + PREP_exit(); + READ_exit(); +} + diff --git a/main/gbc/gbc_read.h b/main/gbc/gbc_read.h new file mode 100644 index 00000000..5c3514ef --- /dev/null +++ b/main/gbc/gbc_read.h @@ -0,0 +1,47 @@ +/*************************************************************************** + + gbc_read.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_READ_H +#define __GBC_READ_H + +#include "gbc_read_common.h" + +#include + +#undef isdigit +#define isdigit(_c) (READ_digit_car[(uchar)(_c)]) +#undef isspace +#define isspace(_c) (((uchar)_c) <= ' ') + +#ifndef __GBC_READ_C +extern char READ_digit_car[]; +#endif + +void READ_do(void); +void READ_dump_pattern(PATTERN *pattern); +char *READ_get_pattern(PATTERN *pattern); +int READ_get_column(); + +void THROW_UNEXPECTED(PATTERN *pattern) NORETURN; + +#endif diff --git a/main/gbc/gbc_reserved.c b/main/gbc/gbc_reserved.c new file mode 100644 index 00000000..61454634 --- /dev/null +++ b/main/gbc/gbc_reserved.c @@ -0,0 +1,27 @@ +/*************************************************************************** + + gbc_reserved.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_RESERVED_C + +#define PROJECT_COMP +#include "gb_reserved_temp.h" diff --git a/main/gbc/gbc_reserved_make.c b/main/gbc/gbc_reserved_make.c new file mode 100644 index 00000000..22c36bc9 --- /dev/null +++ b/main/gbc/gbc_reserved_make.c @@ -0,0 +1,469 @@ +/*************************************************************************** + + gbc_reserved_make.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_RESERVED_MAKE_C + +#define PROJECT_COMP + +#include +#include + +#include "gb_common.h" +#include "gb_reserved.h" +#include "gb_type_common.h" +#include "gb_pcode.h" +#include "gb_reserved_keyword.h" + +#define HASH_SIZE 109 + +/*uint hash(const char *key, int len) +{ + int i; + uint h = 0; + for (i = 0; i < len; i++) + h = (h << 4) + (h ^ (tolower(key[i]))); + + return h % HASH_SIZE; +}*/ + +char buffer[1024]; + +char *read_line(FILE *f) +{ + return fgets(buffer, sizeof(buffer), f); +} + +void read_write_until(FILE *in, FILE *out, char *until) +{ + char *line; + + for(;;) + { + line = read_line(in); + if (!line) + break; + if (out) fputs(line, out); + while (*line && ((unsigned char)*line <= ' ')) + line++; + if (!strncmp(line, until, strlen(until))) + break; + } +} + +#if 0 +int main(int argc, char **argv) +{ + COMP_INFO *info; + SUBR_INFO *subr; + int len; + int i, p, n; + uint h; + char c; + char last[16]; + char next[16]; + FILE *in, *out; + + in = fopen("../share/gb_reserved_temp.h", "r"); + if (!in) + { + fprintf(stderr, "unable to open '../share/gb_reserved_temp.h': %s\n", strerror(errno)); + exit(1); + } + + out = fopen("../share/gb_reserved_temp.h.tmp", "w"); + + read_write_until(in, out, "int RESERVED_find_word(const char *word, int len)"); + + read_write_until(in, out, "static void *jump[] = {"); + + for (h = 0; h < 12; h++) + { + if ((h % 8) == 0) + fprintf(out, "\t\t"); + fprintf(out, "&&__%02d, ", h); + if ((h % 8) == 7) + fprintf(out, "\n"); + } + fprintf(out, "\n\t};\n\n"); + + //printf(" goto *jump[h %% %d];\n\n", HASH_SIZE); + fprintf(out, "\tgoto *jump[len];\n\n"); + + fprintf(out, "__00:\n__01:\n\treturn -1;\n"); + + for (h = 2; h < 12; h++) + { + fprintf(out, "__%02d:\n", h); + + *last = 0; + + for(;;) + { + n = -1; + + *next = 0; + + for (info = &COMP_res_info[1], i = 1; info->name; info++, i++) + { + len = strlen(info->name); + if (len != h) + continue; + + if (strcmp(info->name, last) <= 0) + continue; + + if (*next == 0 || strcmp(info->name, next) <= 0) + { + strcpy(next, info->name); + n = i; + } + } + + if (n < 0) + break; + + info = &COMP_res_info[n]; + len = strlen(info->name); + + strcpy(last, info->name); + + fprintf(out, "\tif ("); + for (p = 0; p < len; p++) + { + if (p) + fprintf(out, " && "); + c = info->name[p]; + if (isalpha(c)) + fprintf(out, "tolower(word[%d]) == '%c'", p, tolower(info->name[p])); + else if (c == '\\') + fprintf(out, "word[%d] == '\\\\'", p); + else + fprintf(out, "word[%d] == '%c'", p, info->name[p]); + } + /*printf(" if (!strncmp(\""); + for (p = 0; p < len; p++) + { + c = info->name[p]; + if (c == '\\') + printf("\\\\"); + else + putchar(tolower(info->name[p])); + } + printf("\", word, %d)", len);*/ + fprintf(out, ") return %d;\n", n); + } + + fprintf(out, "\treturn -1;\n"); + } + + read_write_until(in, NULL, "}"); + read_write_until(in, NULL, "}"); + fprintf(out, "}\n"); + + read_write_until(in, out, "static void *jump[] = {"); + + for (h = 0; h < 12; h++) + { + if ((h % 8) == 0) + fprintf(out, "\t\t"); + fprintf(out, "&&__%02d, ", h); + if ((h % 8) == 7) + fprintf(out, "\n"); + } + fprintf(out, "\n\t};\n\n"); + + fprintf(out, "\tgoto *jump[len];\n\n"); + + fprintf(out, "__00:\n__01:\n\treturn -1;\n"); + + for (h = 2; h < 12; h++) + { + fprintf(out, "__%02d:\n", h); + + *last = 0; + + for(;;) + { + n = -1; + + *next = 0; + + for (subr = &COMP_subr_info[0], i = 0; subr->name; subr++, i++) + { + len = strlen(subr->name); + if (len != h) + continue; + + if (strcmp(subr->name, last) <= 0) + continue; + + if (*next == 0 || strcmp(subr->name, next) <= 0) + { + strcpy(next, subr->name); + n = i; + } + } + + if (n < 0) + break; + + subr = &COMP_subr_info[n]; + len = strlen(subr->name); + + strcpy(last, subr->name); + + fprintf(out, "\tif ("); //COMPARE_%d(\"%s\", word)) return %d;\n", len, len, info->name, i); + for (p = 0; p < len; p++) + { + if (p) + fprintf(out, " && "); + c = subr->name[p]; + if (isalpha(c)) + fprintf(out, "tolower(word[%d]) == '%c'", p, tolower(subr->name[p])); + else if (c == '\\') + fprintf(out, "word[%d] == '\\\\'", p); + else + fprintf(out, "word[%d] == '%c'", p, subr->name[p]); + } + fprintf(out, ") return %d;\n", n); + } + + fprintf(out, "\treturn -1;\n"); + } + + fprintf(out, "}\n"); + + fclose(in); + fclose(out); + + unlink("../share/gb_reserved_temp.h"); + rename("../share/gb_reserved_temp.h.tmp", "../share/gb_reserved_temp.h"); + + return 0; +} +#endif + +static int compare_name(const char *name1, const char* name2) +{ + int l1 = strlen(name1); + int l2 = strlen(name2); + + if (l1 < l2) + return -1; + if (l1 > l2) + return 1; + return strcasecmp(name1, name2); +} + +int main(int argc, char **argv) +{ + COMP_INFO *info; + SUBR_INFO *subr; + int len; + int i, p, n; + uint h; + char c; + char last[16]; + char next[16]; + FILE *in, *out; + + in = fopen("../share/gb_reserved_temp.h", "r"); + if (!in) + { + fprintf(stderr, "unable to open '../share/gb_reserved_temp.h': %s\n", strerror(errno)); + exit(1); + } + + out = fopen("../share/gb_reserved_temp.h.tmp", "w"); + + read_write_until(in, out, "int RESERVED_find_word(const char *word, int len)"); + + read_write_until(in, out, "static void *jump[] = {"); + + for (h = 32; h < 127; h++) + { + if ((h % 8) == 0) + fprintf(out, "\t\t"); + fprintf(out, "&&__%02X, ", h); + if ((h % 8) == 7) + fprintf(out, "\n"); + } + fprintf(out, "\n\t};\n\n"); + + //printf(" goto *jump[h %% %d];\n\n", HASH_SIZE); + fprintf(out, "\tgoto *jump[*word - 32];\n\n"); + + for (h = 32; h < 127; h++) + { + if (islower(h)) + continue; + + fprintf(out, "__%02X:\n", h); + if (isupper(h)) + fprintf(out, "__%02X:\n", tolower(h)); + + *last = 0; + + for(;;) + { + n = -1; + + *next = 0; + + for (info = &COMP_res_info[1], i = 1; info->name; info++, i++) + { + if (tolower(*info->name) != tolower(h) || !info->name[1]) + continue; + + if (compare_name(info->name, last) <= 0) + continue; + + if (*next == 0 || compare_name(info->name, next) <= 0) + { + strcpy(next, info->name); + n = i; + } + } + + if (n < 0) + break; + + info = &COMP_res_info[n]; + len = strlen(info->name); + + strcpy(last, info->name); + + fprintf(out, "\tif (len == %d", len); + for (p = 1; p < len; p++) + { + fprintf(out, " && "); + c = info->name[p]; + if (isalpha(c)) + fprintf(out, "tolower(word[%d]) == '%c'", p, tolower(info->name[p])); + else if (c == '\\') + fprintf(out, "word[%d] == '\\\\'", p); + else + fprintf(out, "word[%d] == '%c'", p, info->name[p]); + } + /*printf(" if (!strncmp(\""); + for (p = 0; p < len; p++) + { + c = info->name[p]; + if (c == '\\') + printf("\\\\"); + else + putchar(tolower(info->name[p])); + } + printf("\", word, %d)", len);*/ + fprintf(out, ") return %d;\n", n); + } + + fprintf(out, "\treturn -1;\n"); + } + + read_write_until(in, NULL, "}"); + read_write_until(in, NULL, "}"); + fprintf(out, "}\n"); + + read_write_until(in, out, "static void *jump[] = {"); + + for (h = 32; h < 127; h++) + { + if ((h % 8) == 0) + fprintf(out, "\t\t"); + fprintf(out, "&&__%02X, ", h); + if ((h % 8) == 7) + fprintf(out, "\n"); + } + fprintf(out, "\n\t};\n\n"); + + fprintf(out, "\tgoto *jump[*word - 32];\n\n"); + + for (h = 32; h < 127; h++) + { + if (islower(h)) + continue; + + fprintf(out, "__%02X:\n", h); + if (isupper(h)) + fprintf(out, "__%02X:\n", tolower(h)); + + *last = 0; + + for(;;) + { + n = -1; + + *next = 0; + + for (subr = &COMP_subr_info[0], i = 0; subr->name; subr++, i++) + { + if (tolower(*subr->name) != tolower(h) || !subr->name[1]) + continue; + + if (compare_name(subr->name, last) <= 0) + continue; + + if (*next == 0 || compare_name(subr->name, next) <= 0) + { + strcpy(next, subr->name); + n = i; + } + } + + if (n < 0) + break; + + subr = &COMP_subr_info[n]; + len = strlen(subr->name); + + strcpy(last, subr->name); + + fprintf(out, "\tif (len == %d", len); + for (p = 1; p < len; p++) + { + fprintf(out, " && "); + c = subr->name[p]; + if (isalpha(c)) + fprintf(out, "tolower(word[%d]) == '%c'", p, tolower(subr->name[p])); + else if (c == '\\') + fprintf(out, "word[%d] == '\\\\'", p); + else + fprintf(out, "word[%d] == '%c'", p, subr->name[p]); + } + fprintf(out, ") return %d;\n", n); + } + + fprintf(out, "\treturn -1;\n"); + } + + fprintf(out, "}\n"); + + fclose(in); + fclose(out); + + unlink("../share/gb_reserved_temp.h"); + rename("../share/gb_reserved_temp.h.tmp", "../share/gb_reserved_temp.h"); + + return 0; +} diff --git a/main/gbc/gbc_trans.c b/main/gbc/gbc_trans.c new file mode 100644 index 00000000..f437cff1 --- /dev/null +++ b/main/gbc/gbc_trans.c @@ -0,0 +1,851 @@ +/*************************************************************************** + + gbc_trans.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_TRANS_C + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" + +#include "gbc_compile.h" +#include "gbc_read.h" +#include "gbc_trans.h" +#include "gb_reserved.h" +#include "gb_code.h" + +#define IS_PURE_INTEGER(_int64_val) ((_int64_val) == ((int)(_int64_val))) + +short TRANS_in_assignment = 0; +short TRANS_in_left_value = 0; +bool TRANS_in_try = FALSE; + +void TRANS_reset(void) +{ + JOB->line = JOB->first_line; + JOB->current = JOB->pattern; + JOB->end = &(JOB->pattern[JOB->pattern_count]); +} + + +static bool read_integer(char *number, int base, bool minus, int64_t *result) +{ + uint64_t nbr2, nbr; + int d, n; + unsigned char c; + int nmax; + + n = 0; + nbr = 0; + + switch (base) + { + case 2: nmax = 64; break; + case 8: nmax = 21; break; + case 16: nmax = 16; break; + case 10: default: nmax = 19; break; + } + + if (base == 10) + { + c = *number++; + + for(;;) + { + if (isdigit(c)) + d = c - '0'; + else + break; + + n++; + if (n < nmax) + nbr = nbr * 10 + d; + else + { + nbr2 = nbr * 10 + d; + + if ((nbr2 / 10) != nbr || nbr2 > ((uint64_t)LLONG_MAX + minus)) + return TRUE; + + nbr = nbr2; + } + + c = *number++; + if (!c) + break; + } + } + else + { + c = *number++; + + for(;;) + { + if (isdigit(c)) + d = c - '0'; + else if (c >= 'A' && c <='Z') + d = c - 'A' + 10; + else if (c >= 'a' && c <='z') + d = c - 'a' + 10; + else + break; + + if (d >= base) + break; + + n++; + if (n > nmax) + return TRUE; + + nbr = nbr * base + d; + + c = *number++; + if (!c) + break; + } + + if ((c == '&' || c == 'u' || c == 'U') && base != 10) + c = *number++; + else + { + if ((base == 16 && n == 4) || (base == 2 && n == 16)) + { + if (nbr >= 0x8000L && nbr <= 0xFFFFL) + nbr |= INT64_C(0xFFFFFFFFFFFF0000); + } + else if ((base == 16 && n == 8) || (base == 2 && n == 32)) + { + if (nbr >= 0x80000000L && nbr <= 0xFFFFFFFFL) + nbr |= INT64_C(0xFFFFFFFF00000000); + } + } + } + + if (c) + return TRUE; + + if (n == 0) + return TRUE; + + *((int64_t *)result) = nbr; + return FALSE; +} + + +static bool read_float(char *number, double *result) +{ + unsigned char c; + double nint; + double nfrac, n; + int nexp; + bool nexp_minus; + + nint = 0.0; + nfrac = 0.0; + nexp = 0; + nexp_minus = FALSE; + + c = *number++; + + /* Integer part */ + + for(;;) + { + if (c == '.') + { + c = *number++; + break; + } + + if (!c || !isdigit(c)) + return TRUE; + + nint = nint * 10 + (c - '0'); + + c = *number++; + + if (c == 'e' || c == 'E') + break; + + if (!c || isspace(c)) + goto __END; + } + + /* Decimal part */ + + n = 0.1; + for(;;) + { + if (!c || !isdigit(c)) + break; + + nfrac += n * (c - '0'); + n /= 10; + + c = *number++; + } + + /* Exponent */ + + if (c == 'e' || c == 'E') + { + c = *number++; + + if (c == '+' || c == '-') + { + if (c == '-') + nexp_minus = TRUE; + + c = *number++; + } + + if (!c || !isdigit(c)) + return TRUE; + + for(;;) + { + nexp = nexp * 10 + (c - '0'); + if (nexp > DBL_MAX_10_EXP) + return TRUE; + + c = *number++; + if (!c || !isdigit(c)) + break; + } + } + + if (c) + return TRUE; + +__END: + + *result = (nint + nfrac) * pow(10, nexp_minus ? (-nexp) : nexp); + + return FALSE; +} + +bool TRANS_get_number(int index, TRANS_NUMBER *result) +{ + char buffer[68]; + SYMBOL *sym; + char *number; + unsigned char c; + int64_t val = 0; + double dval = 0.0; + int type; + int base = 10; + bool minus = FALSE; + bool complex = FALSE; + + sym = TABLE_get_symbol(JOB->class->table, index); + if (sym->len > 66) + return TRUE; + memcpy(buffer, sym->name, sym->len); + buffer[sym->len] = 0; + number = buffer; + + c = *number++; + + if (c == '+' || c == '-') + { + minus = (c == '-'); + c = *number++; + } + + if (c == '&') + { + c = *number++; + + if (c == 'H' || c == 'h') + { + base = 16; + c = *number++; + } + else if (c == 'X' || c == 'x') + { + base = 2; + c = *number++; + } + else if (c == 'O' || c == 'o') + { + base = 8; + c = *number++; + } + else + base = 16; + } + else if (c == '%') + { + base = 2; + c = *number++; + } + + if (!c) + return TRUE; + + if (c == '-' || c == '+') + return TRUE; + + errno = 0; + number--; + + if (base == 10 && tolower(buffer[sym->len - 1]) == 'i') + { + buffer[sym->len - 1] = 0; + complex = TRUE; + } + + if (!read_integer(number, base, minus, &val)) + { + if (minus) val = (-val); + + if (IS_PURE_INTEGER(val)) + { + type = T_INTEGER; + goto __END; + } + else + { + type = T_LONG; + goto __END; + } + } + + if (base == 10) + { + if (!read_float(number, &dval)) + { + if (minus) dval = (-dval); + type = T_FLOAT; + goto __END; + } + } + + return TRUE; + +__END: + + result->type = type; + result->complex = complex; + + if (type == T_INTEGER) + result->lval = result->ival = val; + else if (type == T_LONG) + result->lval = val; + else + result->dval = dval; + + return FALSE; +} + + +static PATTERN *trans_embedded_array(PATTERN *look, int mode, TRANS_DECL *result) +{ + TRANS_NUMBER tnum; + int i; + + if (!(mode & TT_CAN_EMBED)) + { + if (PATTERN_is(*look, RS_LSQR)) + THROW("Embedded arrays are forbidden here"); + return look; + } + + if (!PATTERN_is(*look, RS_LSQR)) + return look; + + look++; + + if (mode & TT_CAN_ARRAY) + { + for (i = 0;; i++) + { + if (i >= MAX_ARRAY_DIM) + THROW("Too many dimensions"); + + if (!PATTERN_is_number(*look)) + THROW(E_SYNTAX); + if (TRANS_get_number(PATTERN_index(*look), &tnum)) + THROW(E_SYNTAX); + if (tnum.type != T_INTEGER) + THROW(E_SYNTAX); + if (tnum.ival < 1 || tnum.ival > (2 << 22)) /* 4 Mo, ca devrait suffire... ;-) */ + THROW("Bad subscript range"); + + result->array.dim[i] = tnum.ival; + result->array.ndim++; + look++; + + if (PATTERN_is(*look, RS_RSQR)) + break; + + if (!PATTERN_is(*look, RS_COMMA)) + THROW(E_MISSING, "','"); + look++; + } + } + + if (!PATTERN_is(*look, RS_RSQR)) + THROW(E_MISSING, "']'"); + + result->is_embedded = TRUE; + + look++; + return look; +} + + +static int TRANS_get_class(PATTERN pattern, bool array) +{ + int index = PATTERN_index(pattern); + int index_array; + //CLASS_REF *cref; + + if (!CLASS_exist_class(JOB->class, index)) + { + if (array) + { + // Maybe a compound class? + + CLASS_SYMBOL *sym = CLASS_get_symbol(JOB->class, index); + int i; + char c; + + //fprintf(stderr, "TRANS_get_class: %.*s\n", sym->symbol.len, sym->symbol.name); + + for (i = sym->symbol.len - 1; i >= 0; i--) + { + c = sym->symbol.name[i]; + if (c == '[') + { + //fprintf(stderr, "TRANS_get_class: find %.*s\n", i, sym->symbol.name); + if (TABLE_find_symbol(JOB->class->table, sym->symbol.name, i, &index_array)) + { + index_array = TRANS_get_class(PATTERN_make(RT_CLASS, index_array), TRUE); + + if (JOB->class->class[index_array].exported) + index = CLASS_add_class_exported(JOB->class, index); + else + index = CLASS_add_class(JOB->class, index); + + JOB->class->class[index].type = TYPE_make(T_OBJECT, index_array, 0); + + /*cref = &JOB->class->class[index]; + if (TYPE_is_null(cref->array)) + { + cref->array.t.id = T_OBJECT; + cref->array.t.value = index_array; + }*/ + + return index; + } + } + } + } + + THROW("Unknown identifier: &1", TABLE_get_symbol_name(JOB->class->table, index)); + } + + return CLASS_add_class(JOB->class, index); +} + +static bool check_structure(int *cindex) +{ + SYMBOL *sym = TABLE_get_symbol(JOB->class->table, JOB->class->class[*cindex].index); + int len = sym->len; + char name[sym->len + 1]; + int index; + bool is_array; + + strncpy(name, sym->name, len); + while (name[len - 1] == ']') + len -= 2; + name[len] = 0; + + if (len < sym->len) + { + if (!TABLE_find_symbol(JOB->class->table, name, len, &index)) + goto __ERROR; + + index = CLASS_add_class(JOB->class, index); + is_array = TRUE; + } + else + { + index = *cindex; + is_array = FALSE; + } + + if (JOB->class->class[index].structure) + { + *cindex = index; + return is_array; + } + +__ERROR: + + THROW("&1 is not a structure", name); +} + + +bool TRANS_type(int mode, TRANS_DECL *result) +{ + PATTERN *look = JOB->current; + short id; + int value; + int flag = 0; + bool is_array; + + /* Do not fill the structure with zeros */ + + TYPE_clear(&result->type); + result->is_new = FALSE; + result->is_embedded = FALSE; + result->init = NULL; + result->array.ndim = 0; + + look = trans_embedded_array(look, mode, result); + + if (!PATTERN_is(*look, RS_AS)) + { + if (mode & TT_DO_NOT_CHECK_AS) + return FALSE; + else + THROW(E_MISSING, "AS"); + } + + look++; + + if (mode & TT_CAN_NEW) + { + if (PATTERN_is(*look, RS_NEW)) + { + if (result->is_embedded) //TYPE_get_id(result->type) == T_ARRAY) + THROW("Cannot mix NEW and embedded array"); + + result->is_new = TRUE; + look++; + result->init = look; + } + } + + if ((mode & TT_CAN_EMBED) && PATTERN_is(*look, RS_STRUCT)) + { + id = T_STRUCT; + look++; + + if (!PATTERN_is_class(*look)) + THROW_UNEXPECTED(look); + + value = TRANS_get_class(*look, TRUE); + is_array = check_structure(&value); + + if (!is_array) + { + if (result->is_new) + THROW("Cannot mix NEW and embedded structure"); + //if (result->array.ndim > 0) + // THROW("Cannot mix embedded array and embedded structure"); + } + else + THROW("Arrays of structure are not supported"); + + look++; + } + else + { + if (!PATTERN_is_type(*look) && !PATTERN_is_class(*look)) + THROW_UNEXPECTED(look); + + if (PATTERN_is_type(*look)) + { + id = RES_get_type(PATTERN_index(*look)); + value = -1; + } + else + { + id = T_OBJECT; + value = TRANS_get_class(*look, TRUE); + } + + if (PATTERN_is(look[1], RS_LSQR)) + { + value = CLASS_get_array_class(JOB->class, id, value); + id = T_OBJECT; + + if (!PATTERN_is(look[2], RS_RSQR)) + { + if ((mode & TT_CAN_NEW) && result->is_new) + { + //if (TYPE_get_id(result->type) == T_ARRAY) + // THROW("Cannot mix NEW and static array declaration"); + + //result->is_new = TRUE; + result->init = look; + } + else + THROW("Syntax error"); + } + + while (!PATTERN_is_newline(*look)) + look++; + } + else + { + //if (id == T_OBJECT) + // value = (-1); + look++; + } + } + + if (id == T_VOID) + return FALSE; + + /* + if (result->is_array && result->array.ndim == 0) + result->is_array = FALSE; + */ + + if (result->array.ndim > 0) + { + result->array.type = TYPE_make(id, value, flag); + result->type = TYPE_make(T_ARRAY, CLASS_add_array(JOB->class, &result->array), 0); + } + else if (id == T_STRUCT) + { + result->type = TYPE_make(id, value, flag); + } + else + { + result->type = TYPE_make(id, value, flag); + + if ((mode & TT_CAN_NEW) && !result->is_new && PATTERN_is(*look, RS_EQUAL)) + { + look++; + result->init = look; + while (!PATTERN_is_newline(*look)) + look++; + } + } + + JOB->current = look; + return TRUE; +} + +bool TRANS_check_declaration(void) +{ + PATTERN *look = JOB->current; + + if (!PATTERN_is_identifier(*look)) + return FALSE; + look++; + + if (PATTERN_is(*look, RS_LSQR)) + { + for(;;) + { + look++; + if (PATTERN_is(*look, RS_RSQR)) + break; + if (PATTERN_is_newline(*look)) + return FALSE; + } + look++; + } + + if (!PATTERN_is(*look, RS_AS)) + return FALSE; + + return TRUE; +} + + +PATTERN *TRANS_get_constant_value(TRANS_DECL *decl, PATTERN *current) +{ + int index; + TRANS_NUMBER number = {0}; + int type; + PATTERN value; + + type = TYPE_get_id(decl->type); + + value = *current++; + index = PATTERN_index(value); + + if (type == T_STRING) + { + if (PATTERN_is(value, RS_LBRA)) + { + value = *current++; + if (!PATTERN_is_string(value)) + THROW("Constant string expected"); + index = PATTERN_index(value); + value = *current++; + if (!PATTERN_is(value, RS_RBRA)) + THROW("Missing right brace"); + TYPE_set_id(&decl->type, T_CSTRING); + } + else + { + if (!PATTERN_is_string(value)) + THROW("Constant string expected"); + } + + decl->is_integer = FALSE; + decl->value = index; + } + else + { + if (PATTERN_is_string(value)) + THROW("Syntax error"); + + if (type != T_BOOLEAN && type <= T_FLOAT) + { + if (TRANS_get_number(index, &number)) + THROW("Type mismatch"); + } + + switch(type) + { + case T_BOOLEAN: + + decl->is_integer = TRUE; + + if (PATTERN_is(value, RS_TRUE)) + decl->value = -1L; + else if (PATTERN_is(value, RS_FALSE)) + decl->value = 0L; + else + THROW("Type mismatch"); + + break; + + case T_BYTE: case T_SHORT: case T_INTEGER: + + decl->is_integer = TRUE; + + if (number.type != T_INTEGER) + { + if (number.type == T_LONG) + THROW("Out of range"); + else + THROW("Type mismatch"); + } + + if (((type == T_BYTE) && (number.ival < 0 || number.ival > 255)) + || ((type == T_SHORT) && (number.ival < -32768L || number.ival > 32767L))) + THROW("Out of range"); + + decl->value = number.ival; + + //fprintf(stderr, "TRANS_get_constant_value: INT: %ld\n", decl->value); + break; + + case T_FLOAT: case T_SINGLE: + + if (type == T_SINGLE && !finite((float)number.dval)) + THROW("Out of range"); + + decl->is_integer = FALSE; + decl->value = index; + break; + + case T_LONG: + + if (number.type == T_FLOAT) + THROW("Type mismatch"); + + decl->is_integer = FALSE; + decl->value = index; + decl->lvalue = number.lval; + + //fprintf(stderr, "TRANS_get_constant_value: LONG: %lld\n", decl->lvalue); + break; + + default: + + THROW("Bad constant type"); + } + } + + return current; +} + + +void TRANS_want(int reserved, char *msg) +{ + if (!PATTERN_is(*JOB->current, reserved)) + THROW("Syntax error. &1 expected", msg ? msg : COMP_res_info[reserved].name); + JOB->current++; +} + +void TRANS_want_newline() +{ + if (!TRANS_newline()) + THROW_UNEXPECTED(JOB->current); +} + + +bool TRANS_is_end_function(bool is_proc, PATTERN *look) +{ + if (PATTERN_is_newline(*look)) + return TRUE; + + if (is_proc) + return PATTERN_is(*look, RS_PROCEDURE) || PATTERN_is(*look, RS_SUB); + else + return PATTERN_is(*look, RS_FUNCTION); +} + + +char *TRANS_get_num_desc(ushort num) +{ + static const char *num_desc[3] = { "first", "second", "third" }; + static char desc[8]; + + if (num < 1) + return NULL; + + if (ERROR_translate) + { + snprintf(desc, sizeof(desc), "#%d", num); + } + else + { + if (num < 4) + return (char *)num_desc[num - 1]; + + snprintf(desc, sizeof(desc), "%dth", num); + } + + return desc; +} + + diff --git a/main/gbc/gbc_trans.h b/main/gbc/gbc_trans.h new file mode 100644 index 00000000..132e6368 --- /dev/null +++ b/main/gbc/gbc_trans.h @@ -0,0 +1,257 @@ +/*************************************************************************** + + gbc_trans.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_TRANS_H +#define __GBC_TRANS_H + +#include "gbc_type.h" +#include "gb_reserved.h" +#include "gbc_read.h" +#include "gb_limit.h" + +#include "gbc_trans_common.h" + +enum { + TT_NOTHING = 0, + TT_DO_NOT_CHECK_AS = 1, + TT_CAN_EMBED = 2, + TT_CAN_ARRAY = 4, + TT_CAN_NEW = 8 + }; + +enum { + TS_MODE_READ = (1 << 0), + TS_MODE_WRITE = (1 << 1), + TS_MODE_APPEND = (1 << 2), + TS_MODE_CREATE = (1 << 3), + TS_MODE_DIRECT = (1 << 4), + TS_MODE_WATCH = (1 << 6), + TS_MODE_PIPE = (1 << 7), + TS_MODE_MEMORY = (1 << 8), + TS_MODE_STRING = (1 << 9), + }; + +enum { + TS_EXEC_NONE = 0, + TS_EXEC_READ = (1 << 0), + TS_EXEC_WRITE = (1 << 1), + TS_EXEC_TERM = (1 << 2), + TS_EXEC_STRING = (1 << 3), + TS_EXEC_WAIT = (1 << 4), + TS_EXEC_ERROR = (1 << 5) + }; + +enum { + TS_SUBR_PRINT, + TS_SUBR_INPUT, + TS_SUBR_WRITE, + TS_SUBR_WRITE_BYTES, + TS_SUBR_READ, + TS_SUBR_READ_BYTES, + TS_SUBR_OPEN, + TS_SUBR_CLOSE, + TS_SUBR_SEEK, + TS_SUBR_LINE_INPUT, + TS_SUBR_FLUSH, + TS_SUBR_EXEC, + TS_SUBR_SHELL, + TS_SUBR_WAIT, + TS_SUBR_KILL, + TS_SUBR_MOVE, + TS_SUBR_MKDIR, + TS_SUBR_RMDIR, + TS_SUBR_ARRAY, + TS_SUBR_COLLECTION, + TS_SUBR_COPY, + TS_SUBR_LINK, + TS_SUBR_ERROR, + TS_SUBR_LOCK, + TS_SUBR_UNLOCK, + TS_SUBR_LOCK_WAIT, + TS_SUBR_INPUT_FROM, + TS_SUBR_OUTPUT_TO, + TS_SUBR_DEBUG, + TS_SUBR_SLEEP, + TS_SUBR_RANDOMIZE, + TS_SUBR_ERROR_TO, + TS_SUBR_LEFT, + TS_SUBR_MID, + TS_SUBR_OPEN_MEMORY, + TS_SUBR_CHMOD, + TS_SUBR_CHOWN, + TS_SUBR_CHGRP, + TS_SUBR_USE, + TS_SUBR_CHECK_EXEC, + TS_SUBR_MOVE_KILL + }; + +enum { + TSO_SUBR_SCAN + }; + +enum { + TS_NONE = -1, + TS_STDIN = 0, + TS_STDOUT = 1, + TS_STDERR = 2 + }; + +#define TS_NO_SUBR ((void (*)())-1) + +#ifndef __GBC_TRANS_C +EXTERN short TRANS_in_assignment; +EXTERN short TRANS_in_left_value; +EXTERN bool TRANS_in_try; +EXTERN ushort *TRANS_labels; +#endif + +#define TRANS_newline() (PATTERN_is_newline(*JOB->current) ? JOB->line = PATTERN_index(*JOB->current) + 1, JOB->current++, TRUE : FALSE) + +void TRANS_reset(void); +/*PUBLIC bool TRANS_type(bool check_as, bool square, bool array, bool new, TRANS_DECL *result);*/ +bool TRANS_type(int flag, TRANS_DECL *result); +bool TRANS_get_number(int index, TRANS_NUMBER *result); +bool TRANS_check_declaration(void); +PATTERN *TRANS_get_constant_value(TRANS_DECL *decl, PATTERN *current); + +void TRANS_want(int reserved, char *msg); +void TRANS_want_newline(void); +//int TRANS_get_class(PATTERN pattern); +bool TRANS_is_end_function(bool is_proc, PATTERN *look); +char *TRANS_get_num_desc(ushort num); + +#define TRANS_is(_reserved) (PATTERN_is(*JOB->current, (_reserved)) ? JOB->current++, TRUE : FALSE) +#define TRANS_ignore(_reserved) (void)TRANS_is(_reserved) + +/* trans_code.c */ + +void TRANS_code(void); +#define TRANS_has_init_var(_decl) ((_decl)->is_new || (_decl)->init) +bool TRANS_init_var(TRANS_DECL *decl); +void TRANS_statement(void); +void TRANS_init_optional(TRANS_PARAM *param); +#define TRANS_add_label(_pos) (TRANS_labels ? *ARRAY_add(&TRANS_labels) = (_pos) : 0) +int TRANS_loop_local(bool allow_arg); + +/* trans_expr.c */ + +void TRANS_expression(bool check); +void TRANS_ignore_expression(void); +bool TRANS_popify_last(void); +void TRANS_reference(void); +bool TRANS_affectation(bool dup); +void TRANS_operation(short op, short nparam, bool output, PATTERN previous); +void TRANS_new(void); +TYPE TRANS_variable_get_type(void); +void TRANS_class(int index); +bool TRANS_string(PATTERN pattern); + +/* trans_tree.c */ + +#define RS_UNARY (-1) + +//TRANS_TREE *TRANS_tree(bool check_statement); +void TRANS_tree(bool check_statement, TRANS_TREE **result, int *count); + +/* trans_ctrl.c */ + +void TRANS_control_init(void); +void TRANS_control_exit(void); + +void TRANS_if(void); +void TRANS_else(void); +void TRANS_endif(void); +void TRANS_goto(void); +void TRANS_gosub(void); +void TRANS_on_goto_gosub(void); +void TRANS_do(int type); +void TRANS_loop(int type); +void TRANS_select(void); +void TRANS_case(void); +void TRANS_default(void); +void TRANS_end_select(void); +void TRANS_break(void); +void TRANS_continue(void); +void TRANS_return(void); +void TRANS_for(void); +void TRANS_for_each(void); +void TRANS_next(void); +void TRANS_try(void); +void TRANS_finally(void); +void TRANS_catch(void); +void TRANS_label(void); +void TRANS_with(void); +void TRANS_use_with(void); +void TRANS_end_with(void); +void TRANS_raise(void); +void TRANS_stop(void); + +/* trans_subr.c */ + +void TRANS_subr(int subr, int nparam); + +void TRANS_print(void); +void TRANS_input(void); +void TRANS_read(void); +void TRANS_read_old(void); +void TRANS_write(void); +void TRANS_open(void); +void TRANS_pipe(void); +void TRANS_memory(void); +void TRANS_open_string(void); +void TRANS_close(void); +void TRANS_lock(void); +void TRANS_unlock(void); +void TRANS_seek(void); +void TRANS_line_input(void); +void TRANS_flush(void); +void TRANS_quit(void); +void TRANS_exec(void); +void TRANS_shell(void); +void TRANS_wait(void); +void TRANS_sleep(void); +void TRANS_kill(void); +void TRANS_move(void); +void TRANS_chmod(void); +void TRANS_chown(void); +void TRANS_chgrp(void); +void TRANS_inc(void); +void TRANS_dec(void); +void TRANS_swap(void); +void TRANS_mkdir(void); +void TRANS_rmdir(void); +void TRANS_use(void); +void TRANS_copy(void); +void TRANS_link(void); +void TRANS_input_from(void); +void TRANS_output_to(void); +void TRANS_debug(void); +void TRANS_assert(void); +void TRANS_error(void); +void TRANS_scan(void); +void TRANS_randomize(void); +void TRANS_mid(void); +void TRANS_use(void); + +#endif + diff --git a/main/gbc/gbc_trans_code.c b/main/gbc/gbc_trans_code.c new file mode 100644 index 00000000..074232b4 --- /dev/null +++ b/main/gbc/gbc_trans_code.c @@ -0,0 +1,743 @@ +/*************************************************************************** + + gbc_trans_code.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define _TRANS_CODE_C + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gbc_compile.h" +#include "gbc_trans.h" +#include "gb_code.h" +#include "gb_limit.h" + +/*#define DEBUG*/ + +ushort *TRANS_labels = NULL; + +static FUNCTION *_func; + +static CLASS_SYMBOL *add_local(int sym_index, TYPE type, int value, bool used) +{ + CLASS_SYMBOL *sym; + PARAM *loc; + bool warnings = JOB->warnings; + + if (ARRAY_count(_func->local) >= MAX_LOCAL_SYMBOL) + THROW("Too many local variables"); + + loc = ARRAY_add(&_func->local); + loc->index = sym_index; + loc->type = type; + loc->value = value; + + if (used) + JOB->warnings = FALSE; + sym = CLASS_declare(JOB->class, sym_index, TK_VARIABLE, FALSE); + if (used) + JOB->warnings = warnings; + + sym->local.type = type; + sym->local.value = value; + sym->local_used = used; + + return sym; +} + + +static void create_local_from_param() +{ + int i; + + JOB->line--; // For line number of declaration + + for (i = 0; i < _func->nparam; i++) + { + if (TYPE_get_id(_func->param[i].type) != T_VOID) + add_local(_func->param[i].index, _func->param[i].type, (i - _func->nparam), _func->param[i].ignore); + } + + JOB->line++; +} + +static void remove_local() +{ + int i; + CLASS_SYMBOL *sym; + + for (i = 0; i < ARRAY_count(_func->local); i++) + { + sym = CLASS_get_symbol(JOB->class, _func->local[i].index); + + if (!sym->local_used) + { + if (sym->local.value < 0) + COMPILE_print(MSG_WARNING, sym->local.line, "unused argument: &1", SYMBOL_get_name(&sym->symbol)); + else + COMPILE_print(MSG_WARNING, sym->local.line, "unused variable: &1", SYMBOL_get_name(&sym->symbol)); + } + else if (!sym->local_assigned) + { + if (sym->local.value >= 0) + COMPILE_print(MSG_WARNING, sym->local.line, "uninitialized variable: &1", SYMBOL_get_name(&sym->symbol)); + } + + TYPE_clear(&sym->local.type); + } +} + + +static bool TRANS_local(void) +{ + int sym_index; + TRANS_DECL decl; + PATTERN *pattern; + CLASS_SYMBOL *sym; + int f; + bool no_warning; + bool save_warnings; + + if (!TRANS_is(RS_DIM)) + return FALSE; + /*JOB->current++; + else if (!TRANS_check_declaration()) + return FALSE;*/ + + for(;;) + { + //many = FALSE; + pattern = JOB->current; + + for(;;) + { + no_warning = TRANS_is(RS_LBRA); + + if (!PATTERN_is_identifier(*JOB->current)) + THROW(E_SYNTAX); + JOB->current++; + + if (no_warning) + { + if (!PATTERN_is(*JOB->current, RS_RBRA)) + THROW("Missing right brace"); + JOB->current++; + } + + if (!TRANS_is(RS_COMMA)) + break; + //many = TRUE; + } + + f = TT_DO_NOT_CHECK_AS | TT_CAN_ARRAY | TT_CAN_NEW; + //if (!many) + // f |= TT_CAN_SQUARE; + + if (!TRANS_type(f, &decl)) + THROW(E_SYNTAX); + + for(;;) + { + if (PATTERN_is(*pattern, RS_LBRA)) + { + pattern++; + no_warning = TRUE; + save_warnings = JOB->warnings; + JOB->warnings = FALSE; + } + else + no_warning = FALSE; + + sym_index = PATTERN_index(*pattern); + sym = add_local(sym_index, decl.type, _func->nlocal, FALSE); + pattern++; + + if (no_warning) + { + pattern++; + JOB->warnings = save_warnings; + } + + _func->nlocal++; + + if (TRANS_init_var(&decl)) + { + CODE_pop_local(_func->nlocal - 1); + sym->local_assigned = TRUE; + } + + if (JOB->verbose) + printf("LOCAL %s AS %s\n", TABLE_get_symbol_name(JOB->class->table, sym_index), TYPE_get_desc(decl.type)); + + if (!PATTERN_is(*pattern, RS_COMMA)) + break; + pattern++; + } + + /*if (!many) + { + if (TRANS_init_var(&decl)) + CODE_pop_local(_func->nlocal - 1); + }*/ + + if (!TRANS_is(RS_COMMA)) + break; + } + + return TRUE; +} + + +int TRANS_loop_local(bool allow_arg) +{ + int sym_index; + TRANS_DECL decl; + CLASS_SYMBOL *sym; + + if (!PATTERN_is_identifier(*JOB->current)) + THROW("Syntax error. Identifier expected"); + + sym_index = PATTERN_index(*JOB->current); + JOB->current++; + + sym = CLASS_get_symbol(JOB->class, sym_index); + + if (TRANS_type(TT_DO_NOT_CHECK_AS | TT_CAN_ARRAY, &decl)) + { + if (!TYPE_compare(&sym->local.type, &decl.type)) + { + add_local(sym_index, decl.type, _func->nlocal, FALSE); + _func->nlocal++; + } + } + + if (TYPE_is_null(sym->local.type)) + { + if (TYPE_is_null(sym->global.type)) + THROW("Unknown identifier: &1", TABLE_get_symbol_name(JOB->class->table, sym_index)); + else + THROW("Loop variable cannot be global"); + } + + if (!allow_arg && sym->local.value < 0) + THROW("Loop variable cannot be an argument"); + + sym->local_assigned = TRUE; + sym->local_used = TRUE; + + return sym->local.value; +} + + + +void TRANS_stop(void) +{ + if (TRANS_is(RS_EVENT)) + CODE_stop_event(); + else + CODE_stop(); +} + + +void TRANS_statement(void) +{ + static TRANS_STATEMENT statement[] = { + { RS_EXIT, TRANS_break }, + { RS_BREAK, TRANS_break }, + { RS_CONTINUE, TRANS_continue }, + { RS_GOTO, TRANS_goto }, + { RS_RETURN, TRANS_return }, + { RS_PRINT, TRANS_print }, + { RS_INPUT, TRANS_input }, + { RS_WRITE, TRANS_write }, + { RS_READ, TRANS_read_old }, + //{ RS_OPEN, TRANS_open }, + { RS_CLOSE, TRANS_close }, + { RS_SEEK, TRANS_seek }, + { RS_FLUSH, TRANS_flush }, + { RS_STOP, TRANS_stop }, + { RS_QUIT, TRANS_quit }, + { RS_EXEC, TRANS_exec }, + { RS_SHELL, TRANS_shell }, + { RS_WAIT, TRANS_wait }, + { RS_SLEEP, TRANS_sleep }, + { RS_KILL, TRANS_kill }, + { RS_MOVE, TRANS_move }, + { RS_INC, TRANS_inc }, + { RS_DEC, TRANS_dec }, + { RS_SWAP, TRANS_swap }, + { RS_MKDIR, TRANS_mkdir }, + { RS_RMDIR, TRANS_rmdir }, + { RS_COPY, TRANS_copy }, + { RS_RAISE, TRANS_raise }, + { RS_LINK, TRANS_link }, + { RS_LOCK, TRANS_lock }, + { RS_UNLOCK, TRANS_unlock }, + { RS_TRY, TRANS_try }, + { RS_LINE, TRANS_line_input }, + { RS_OUTPUT, TRANS_output_to }, + { RS_DEBUG, TRANS_debug }, + { RS_ASSERT, TRANS_assert }, + { RS_ERROR, TRANS_error }, + { RS_RANDOMIZE, TRANS_randomize }, + { RS_CHMOD, TRANS_chmod }, + { RS_CHOWN, TRANS_chown }, + { RS_CHGRP, TRANS_chgrp }, + { RS_GOSUB, TRANS_gosub }, + { RS_ON, TRANS_on_goto_gosub }, + + { RS_NONE, NULL } + }; + + PATTERN *look = JOB->current; + TRANS_STATEMENT *st; + COMP_INFO *info; + + if (PATTERN_is_reserved(look[0])) + { + info = &COMP_res_info[PATTERN_index(*look)]; + + if (!info->func) + { + for (st = statement; st->id; st++) + { + if (PATTERN_is(look[0], st->id)) + { + info->func = st->func; + break; + } + } + if (!info->func) + info->func = TS_NO_SUBR; + } + + if (info->func && info->func != TS_NO_SUBR) + { + JOB->current++; + (*info->func)(); + return; + } + } + else if (PATTERN_is_subr(look[0]) && (PATTERN_index(look[0]) == SUBR_Mid || PATTERN_index(look[0]) == SUBR_MidS)) + { + JOB->current++; + TRANS_mid(); + return; + } + + if (!TRANS_affectation(FALSE)) + TRANS_expression(TRUE); +} + + +static void translate_body() +{ + PATTERN *look; + bool is_proc = (TYPE_get_id(_func->type) == T_VOID); + bool test_newline; + //int line = JOB->line - 1; + bool just_got_select = FALSE; + + for(;;) + { + test_newline = TRUE; + CODE_allow_break(); + + FUNCTION_add_all_pos_line(); + + look = JOB->current; + + if (PATTERN_is(look[0], RS_END)) + if (TRANS_is_end_function(is_proc, &look[1])) + break; + + if (TRANS_newline()) + test_newline = FALSE; + else if (!TRANS_local()) + break; + + if (test_newline) + if (!PATTERN_is_newline(*JOB->current)) + THROW_UNEXPECTED(JOB->current); + } + + + TRANS_control_init(); + + for(;;) + { + test_newline = TRUE; + CODE_allow_break(); + + FUNCTION_add_all_pos_line(); + + look = JOB->current; + + if (PATTERN_is(look[0], RS_END)) + if (TRANS_is_end_function(is_proc, &look[1])) + break; + + /*if (PATTERN_is_newline(look[0])) + { + JOB->current++; + JOB->line++; + continue; + }*/ + if (TRANS_newline()) + continue; + + if (just_got_select) + { + if (!PATTERN_is(look[0], RS_CASE) && !PATTERN_is(look[0], RS_DEFAULT)) + THROW("Syntax error. CASE or DEFAULT expected after SELECT"); + just_got_select = FALSE; + } + + if (PATTERN_is(look[0], RS_DIM)) + { + TRANS_local(); + } + else if (PATTERN_is_identifier(look[0]) && PATTERN_is(look[1], RS_COLON)) + { + TRANS_label(); + } + else if (PATTERN_is(look[0], RS_IF)) + { + JOB->current++; + TRANS_if(); + } + else if (PATTERN_is(look[0], RS_ELSE)) + { + JOB->current++; + TRANS_else(); + } + else if ((PATTERN_is(look[0], RS_END) + && PATTERN_is(look[1], RS_IF)) + || PATTERN_is(look[0], RS_ENDIF)) + { + if (PATTERN_is(look[0], RS_END)) + JOB->current += 2; + else + JOB->current++; + + TRANS_endif(); + } + else if (PATTERN_is(look[0], RS_DO)) + { + JOB->current++; + TRANS_do(RS_DO); + } + else if (PATTERN_is(look[0], RS_WHILE)) + { + TRANS_do(RS_WHILE); + } + else if (PATTERN_is(*look, RS_REPEAT)) + { + JOB->current++; + TRANS_do(RS_REPEAT); + } + else if (PATTERN_is(look[0], RS_LOOP)) + { + JOB->current++; + TRANS_loop(RS_LOOP); + } + else if (PATTERN_is(look[0], RS_UNTIL)) + { + TRANS_loop(RS_UNTIL); + } + else if (PATTERN_is(look[0], RS_WEND)) + { + JOB->current++; + TRANS_loop(RS_WEND); + } + else if (PATTERN_is(look[0], RS_FOR)) + { + if (PATTERN_is(look[1], RS_EACH)) + { + JOB->current += 2; + TRANS_for_each(); + } + else + { + JOB->current++; + TRANS_for(); + } + } + else if (PATTERN_is(look[0], RS_NEXT)) + { + JOB->current++; + TRANS_next(); + } + else if (PATTERN_is(look[0], RS_SELECT)) + { + JOB->current++; + TRANS_select(); + just_got_select = TRUE; + } + else if (PATTERN_is(look[0], RS_CASE)) + { + JOB->current++; + if (PATTERN_is(look[1], RS_ELSE)) + { + JOB->current++; + TRANS_default(); + } + else + TRANS_case(); + } + else if (PATTERN_is(look[0], RS_DEFAULT)) + { + JOB->current++; + TRANS_default(); + } + else if (PATTERN_is(look[0], RS_END) + && PATTERN_is(look[1], RS_SELECT)) + { + JOB->current += 2; + TRANS_end_select(); + } + else if (PATTERN_is(look[0], RS_FINALLY)) + { + JOB->current++; + TRANS_finally(); + } + else if (PATTERN_is(look[0], RS_CATCH)) + { + JOB->current++; + TRANS_catch(); + } + else if (PATTERN_is(*look, RS_WITH)) + { + JOB->current++; + TRANS_with(); + } + else if (PATTERN_is(look[0], RS_END) + && PATTERN_is(look[1], RS_WITH)) + { + JOB->current += 2; + TRANS_end_with(); + } + else if (PATTERN_is(look[0], RS_LET)) + { + JOB->current++; + if (!TRANS_affectation(FALSE)) + THROW(E_SYNTAX); + } + else + TRANS_statement(); + + /* + if (next_newline) + { + for(;;) + { + if (PATTERN_is_NEWLINE(*JOB->current) + || PATTERN_is_END(*JOB->current)) + break; + JOB->current++; + } + } + */ + + if (test_newline) + if (!PATTERN_is_newline(*JOB->current)) + THROW_UNEXPECTED(JOB->current); + + } + + TRANS_control_exit(); +} + +static void trans_call(const char *name, int nparam) +{ + CLASS_SYMBOL *sym; + int index; + + if (!TABLE_find_symbol(JOB->class->table, name, strlen(name), &index)) + return; + + sym = (CLASS_SYMBOL *)TABLE_get_symbol(JOB->class->table, index); + + if (TYPE_get_kind(sym->global.type) != TK_FUNCTION) + return; + + sym->global_used = TRUE; + + CODE_push_global(sym->global.value, FALSE, TRUE); + CODE_call(nparam); + CODE_drop(); +} + + +void TRANS_code(void) +{ + int i; + + for (i = 0; i < ARRAY_count(JOB->class->function); i++) + { + _func = &JOB->class->function[i]; + + CODE_begin_function(_func); + + if (JOB->verbose) + { + printf("Compiling %s()...\n", TABLE_get_symbol_name(JOB->class->table, _func->name)); + if (_func->fast) + printf("Fast\n"); + } + + /* Do not debug implicit or generated functions */ + if (!_func->start || _func->name == NO_SYMBOL || TABLE_get_symbol_name(JOB->class->table, _func->name)[0] == '@') + JOB->nobreak = TRUE; + else + JOB->nobreak = FALSE; + + JOB->line = _func->line; + JOB->current = _func->start; + JOB->func = _func; + + /* fonction implicite ? */ + if (!_func->start) + { + if ((i == FUNC_INIT_DYNAMIC) && (JOB->form != NULL)) + { + //CODE_event(FALSE); + trans_call("@load", 0); + //CODE_event(TRUE); + } + + FUNCTION_add_last_pos_line(); + CODE_op(C_RETURN, 0, 0, TRUE); + if (JOB->verbose) + CODE_dump(_func->code, _func->ncode); + + continue; + } + + create_local_from_param(); + + translate_body(); + + CODE_return(2); // Return from function, ignore Gosub stack + + CODE_end_function(_func); + FUNCTION_add_last_pos_line(); + + _func->stack = _func->nlocal + _func->nctrl + CODE_stack_usage; + + if (JOB->verbose) + { + CODE_dump(_func->code, _func->ncode); + printf("%d local(s) %d control(s) ", _func->nlocal, _func->nctrl); + printf("%d stack\n", _func->stack); + printf("\n"); + } + + remove_local(); + } + + CLASS_check_properties(JOB->class); + CLASS_check_unused_global(JOB->class); + + JOB->func = NULL; +} + + + +bool TRANS_init_var(TRANS_DECL *decl) +{ + int i; + TRANS_ARRAY *array; + //PATTERN *save; + + if (decl->is_new) + { + if (TYPE_is_array(decl->type) && decl->array.ndim > 0) + { + array = &decl->array; + + if (TYPE_is_object(decl->type)) + CODE_push_class(TYPE_get_value(decl->type)); + else + CODE_push_number(TYPE_get_id(decl->type)); + + for (i = 0; i < array->ndim; i++) + CODE_push_number(array->dim[i]); + + CODE_new(array->ndim + 1, TRUE, FALSE); + } + else + { + JOB->current = decl->init; + TRANS_new(); + } + return TRUE; + } + else if (decl->init) + { + JOB->current = decl->init; + TRANS_expression(FALSE); + return TRUE; + } + else + return FALSE; +} + + +/* +void TRANS_init_object() +{ +} +*/ + +void TRANS_init_optional(TRANS_PARAM *param) +{ + PATTERN *look = param->optional; + PATTERN *save; + + if (look == NULL) + return; + + save = JOB->current; + + if (PATTERN_is(*look, RS_COMMA) || PATTERN_is(*look, RS_RBRA)) + { + CODE_push_void(); + } + else + { + if (!PATTERN_is(*look, RS_EQUAL)) + THROW("Syntax error. Invalid optional parameter"); + + look++; + JOB->current = look; + TRANS_expression(FALSE); + + if (!PATTERN_is(*JOB->current, RS_COMMA) && !PATTERN_is(*JOB->current, RS_RBRA)) + THROW("Syntax error. Invalid optional parameter"); + } + + JOB->current = save; +} diff --git a/main/gbc/gbc_trans_ctrl.c b/main/gbc/gbc_trans_ctrl.c new file mode 100644 index 00000000..a2b55232 --- /dev/null +++ b/main/gbc/gbc_trans_ctrl.c @@ -0,0 +1,1310 @@ +/*************************************************************************** + + gbc_trans_ctrl.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define _TRANS_CTRL_C + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gbc_compile.h" +#include "gbc_trans.h" +#include "gb_code.h" +#include "gb_limit.h" + +/*#define DEBUG*/ +/*#define DEBUG_GOTO*/ + +#define BEGIN_NO_BREAK \ +{ \ + bool nobreak = JOB->nobreak; \ + JOB->nobreak = TRUE; + +#define END_NO_BREAK \ + JOB->nobreak = nobreak; \ +} + + +static int ctrl_level; +static int ctrl_id; +static int ctrl_local; + +static TRANS_CTRL ctrl_data[MAX_CTRL_LEVEL]; + +static TRANS_CTRL *current_ctrl; + +static TRANS_GOTO *goto_info; +static TRANS_LABEL *label_info; +static short *ctrl_parent; + +static short *_relocation = NULL; + + +static void jump_length(ushort src, ushort dst) +{ + CODE_jump_length(src, dst); + TRANS_add_label(dst); +} + + +static void control_set_value(int value) +{ + if (ctrl_level <= 0) + return; + + current_ctrl->value = value; +} + + +static int control_get_value(void) +{ + if (ctrl_level <= 0) + return 0; + + return current_ctrl->value; +} + + +static void control_add_relocation() +{ + if (!_relocation) + ARRAY_create(&_relocation); + + *((ushort *)ARRAY_add(&_relocation)) = CODE_get_current_pos(); +} + +static void control_add_pos(ushort **tab_pos, ushort pos) +{ + if (!(*tab_pos)) + ARRAY_create(tab_pos); + + *((ushort *)ARRAY_add(tab_pos)) = pos; +} + + +static void control_add_current_pos(void) +{ + control_add_pos(¤t_ctrl->pos, CODE_get_current_pos()); +} + +static void control_add_current_pos_break() +{ + control_add_pos(¤t_ctrl->pos_break, CODE_get_current_pos()); +} + +static void control_add_current_pos_continue() +{ + control_add_pos(¤t_ctrl->pos_continue, CODE_get_current_pos()); +} + +static void control_add_this_pos(ushort pos) +{ + control_add_pos(¤t_ctrl->pos, pos); +} + +static void control_add_this_pos_continue(ushort pos) +{ + control_add_pos(¤t_ctrl->pos_continue, pos); +} + + +static void control_jump_each_pos_with(ushort *tab_pos) +{ + int i; + + if (!tab_pos) + return; + + for (i = 0; i < ARRAY_count(tab_pos); i++) + jump_length(tab_pos[i], CODE_get_current_pos()); +} + + +static void control_jump_each_pos(void) +{ + control_jump_each_pos_with(current_ctrl->pos); +} + + +static TRANS_CTRL *control_get_inner(void) +{ + int level = ctrl_level; + TRANS_CTRL *ctrl_look; + + for(;;) + { + if (!level) + return NULL; + + level--; + ctrl_look = &ctrl_data[level]; + + if ((ctrl_look->type == RS_DO) + || (ctrl_look->type == RS_WHILE) + || (ctrl_look->type == RS_REPEAT) + || (ctrl_look->type == RS_FOR) + || (ctrl_look->type == RS_EACH)) + return ctrl_look; + } +} + + +static TRANS_CTRL *control_get_inner_with(void) +{ + int level = ctrl_level; + TRANS_CTRL *ctrl_look; + + for(;;) + { + if (!level) + return NULL; + + level--; + ctrl_look = &ctrl_data[level]; + + if (ctrl_look->type == RS_WITH) + return ctrl_look; + } +} + + +static void add_goto(int index, int mode) +{ + TRANS_GOTO *info; + + if (goto_info == NULL) + ARRAY_create(&goto_info); + + info = ARRAY_add(&goto_info); + info->index = index; + info->pos = CODE_get_current_pos(); + info->ctrl_id = (ctrl_level == 0) ? 0 : current_ctrl->id; + info->line = JOB->line; + info->gosub = mode == RS_GOSUB; + info->on_goto = mode == RS_NONE; + + #ifdef DEBUG_GOTO + printf("add_goto: ctrl_id = %d (%ld)\n", info->ctrl_id, ARRAY_count(goto_info)); + #endif + + if (mode == RS_GOSUB) + { + //control_add_relocation(); + CODE_gosub(ctrl_local); + } + else if (mode == RS_GOTO) + CODE_jump(); + else + CODE_nop(); +} + + +static void control_enter(int type) +{ + short *parent; + + if (ctrl_level >= MAX_CTRL_LEVEL) + THROW("Too many nested control structures."); + + ctrl_id++; + + current_ctrl = &ctrl_data[ctrl_level]; + + current_ctrl->type = type; + current_ctrl->value = 0; + current_ctrl->pos = NULL; + current_ctrl->state = 0; + current_ctrl->local = ctrl_local; + current_ctrl->id = ctrl_id; + current_ctrl->loop_var = -1; + + parent = ARRAY_add(&ctrl_parent); + + if (ctrl_level == 0) + *parent = 0; + else + *parent = ctrl_data[ctrl_level - 1].id; + + switch (type) + { + case RS_FOR: + case RS_EACH: + ctrl_local += 2; + break; + + case RS_SELECT: + case RS_WITH: + ctrl_local += 1; + break; + } + + JOB->func->nctrl = Max(JOB->func->nctrl, ctrl_local); + + ctrl_level++; +} + + +static void control_leave(void) +{ + control_jump_each_pos_with(current_ctrl->pos_break); + + ARRAY_delete(¤t_ctrl->pos); + ARRAY_delete(¤t_ctrl->pos_break); + ARRAY_delete(¤t_ctrl->pos_continue); + + ctrl_local = current_ctrl->local; + + ctrl_level--; + + if (ctrl_level > 0) + current_ctrl = &ctrl_data[ctrl_level - 1]; + else + current_ctrl = NULL; +} + + +static void control_check(int type, const char *msg1, const char *msg2) +{ + if (ctrl_level <= 0) + THROW(msg1); + + if (current_ctrl->type != type) + THROW(E_UNEXPECTED, msg2); +} + +static void control_check_two(int type1, int type2, const char *msg1, const char *msg2) +{ + if (ctrl_level <= 0) + THROW(msg1); + + if (current_ctrl->type != type1 && current_ctrl->type != type2) + THROW(E_UNEXPECTED, msg2); +} + +static void control_check_loop_var(short var) +{ + int i; + + for (i = 0; i < (ctrl_level - 1); i++) + { + if (ctrl_data[i].loop_var == var) + THROW("Loop variable already in use"); + } + + current_ctrl->loop_var = var; +} + +static void check_try(const char *name) +{ + if (TRANS_in_try) + { + TRANS_in_try = FALSE; + if (name) + THROW("Cannot use TRY with &1", name); + else + THROW("Cannot use TRY twice"); + } +} + +void TRANS_control_init(void) +{ + ctrl_level = 0; + ctrl_id = 0; + current_ctrl = NULL; + + goto_info = NULL; + + label_info = NULL; + + ctrl_local = 0; //JOB->func->nlocal; + JOB->func->nctrl = 0; + + ARRAY_create(&ctrl_parent); +} + + +void TRANS_control_exit(void) +{ + int i; + CLASS_SYMBOL *sym; + int line; + TRANS_LABEL *label; + short id; + ushort *pcode; + + // Relocate locals + + if (_relocation) + { + for (i = 0; i < ARRAY_count(_relocation); i++) + { + pcode = &JOB->func->code[_relocation[i]]; + if (PCODE_is_breakpoint(*pcode)) + pcode++; + + *pcode += JOB->func->nlocal; + + /*fprintf(stderr, "%04d : %04X --> ", _relocation[i], pcode); + pcode = (pcode & 0xFF00) | ((pcode & 0xFF) + JOB->func->nlocal); + fprintf(stderr, "%04X\n", pcode); + JOB->func->code[_relocation[i]] = pcode;*/ + } + + ARRAY_delete(&_relocation); + } + + // Resolve GOTOs + + if (goto_info) + { + line = JOB->line; + + for (i = 0; i < ARRAY_count(goto_info); i++) + { + JOB->line = goto_info[i].line; + /*printf("%d\n", JOB->line);*/ + + sym = CLASS_get_symbol(JOB->class, goto_info[i].index); + + if (TYPE_get_kind(sym->local.type) != TK_LABEL) + THROW("Label '&1' not declared", TABLE_get_symbol_name(JOB->class->table, goto_info[i].index)); + + label = &label_info[sym->local.value]; + + id = goto_info[i].ctrl_id; + + if (goto_info[i].gosub) + { + if (label->ctrl_id) + THROW("Forbidden GOSUB"); + } + else + { + for(;;) + { + if (label->ctrl_id == id) + break; + + if (id == 0) + THROW("Forbidden GOTO"); + + #ifdef DEBUG_GOTO + printf("id = %d ctrl_parent[id - 1] = %d (%ld)\n", id, ctrl_parent[id - 1], ARRAY_count(ctrl_parent)); + #endif + id = ctrl_parent[id - 1]; + } + } + + jump_length(goto_info[i].pos, label->pos); + } + + JOB->line = line; + } + + /* Remove previously declared labels */ + + if (label_info) + { + for (i = 0; i < ARRAY_count(label_info); i++) + { + sym = CLASS_get_symbol(JOB->class, label_info[i].index); + TYPE_clear(&sym->local.type); + } + } + + ARRAY_delete(&goto_info); + ARRAY_delete(&ctrl_parent); + ARRAY_delete(&label_info); + + /* On ne doit pas laisser une structure de controle ouverte */ + + if (ctrl_level == 0) return; + + switch (ctrl_data[ctrl_level - 1].type) + { + case RS_IF: + THROW(E_MISSING, "ENDIF"); + case RS_FOR: + case RS_EACH: + THROW(E_MISSING, "NEXT"); + case RS_DO: + THROW(E_MISSING, "LOOP"); + case RS_REPEAT: + THROW(E_MISSING, "UNTIL"); + case RS_WHILE: + THROW(E_MISSING, "WEND"); + case RS_SELECT: + THROW(E_MISSING, "END SELECT"); + case RS_WITH: + THROW(E_MISSING, "END WITH"); + } +} + +static ushort trans_jump_if(bool if_true) +{ + ushort pos; + + if (CODE_check_jump_not()) + if_true = !if_true; + + pos = CODE_get_current_pos(); + + if (if_true) + CODE_jump_if_true(); + else + CODE_jump_if_false(); + + return pos; +} + +/*static void trans_endif(void) +{ + if (current_ctrl->state == 0) + jump_length(control_get_value(), CODE_get_current_pos()); + + control_jump_each_pos(); +}*/ + +static void trans_else(void) +{ + BEGIN_NO_BREAK + { + control_add_current_pos_break(); + CODE_jump(); + } + END_NO_BREAK + + control_jump_each_pos_with(current_ctrl->pos_continue); + ARRAY_delete(¤t_ctrl->pos_continue); + //jump_length(control_get_value(), CODE_get_current_pos()); + + current_ctrl->state = 1; +} + +static void trans_if(void) +{ + TRANS_expression(FALSE); + + if (PATTERN_is(*JOB->current, RS_AND)) + { + for(;;) + { + control_add_this_pos_continue(trans_jump_if(FALSE)); + + if (!PATTERN_is(*JOB->current, RS_AND)) + break; + + JOB->current++; + + TRANS_want(RS_IF, "AND IF"); + + TRANS_expression(FALSE); + } + } + else if (PATTERN_is(*JOB->current, RS_OR)) + { + for(;;) + { + control_add_this_pos(trans_jump_if(TRUE)); + + if (!PATTERN_is(*JOB->current, RS_OR)) + break; + + JOB->current++; + + TRANS_want(RS_IF, "OR IF"); + + TRANS_expression(FALSE); + } + + control_add_current_pos_continue(); + CODE_jump(); + } + else + { + control_add_this_pos_continue(trans_jump_if(FALSE)); + } + + if (PATTERN_is(*JOB->current, RS_THEN)) + JOB->current++; + else if (!PATTERN_is_newline(*JOB->current)) + THROW(E_EXPECTED, "THEN"); + + control_jump_each_pos(); + ARRAY_delete(¤t_ctrl->pos); +} + +static void trans_else_if(void) +{ + trans_else(); + current_ctrl->state = 0; + trans_if(); +} + + +void TRANS_if(void) +{ + PATTERN *look; + bool has_else; + + control_enter(RS_IF); + + trans_if(); + + if (PATTERN_is_newline(*JOB->current)) + return; + + has_else = FALSE; + look = JOB->current; + for(;;) + { + look++; + if (PATTERN_is_newline(*look)) + break; + if (PATTERN_is(*look, RS_ELSE)) + { + has_else = TRUE; + *look = PATTERN_make(RT_NEWLINE, 0); + break; + } + } + + TRANS_statement(); + + if (has_else) + { + JOB->current++; + trans_else(); + TRANS_statement(); + } + + TRANS_endif(); +} + + +void TRANS_else(void) +{ + control_check(RS_IF, "ELSE without IF", "ELSE"); + + if (current_ctrl->state) + THROW(E_UNEXPECTED, "ELSE"); + + if (TRANS_is(RS_IF)) + trans_else_if(); + else + trans_else(); +} + + +void TRANS_endif(void) +{ + control_check(RS_IF, "ENDIF without IF", "ENDIF"); + control_jump_each_pos_with(current_ctrl->pos_continue); + control_leave(); +} + + +void TRANS_goto(void) +{ + int index; + + check_try("GOTO"); + + if (!PATTERN_is_identifier(*JOB->current)) + THROW(E_SYNTAX); + + index = PATTERN_index(*JOB->current); + JOB->current++; + + add_goto(index, RS_GOTO); +} + +void TRANS_gosub(void) +{ + int index; + + check_try("GOSUB"); + + if (!PATTERN_is_identifier(*JOB->current)) + THROW(E_SYNTAX); + + index = PATTERN_index(*JOB->current); + JOB->current++; + + add_goto(index, RS_GOSUB); +} + + +void TRANS_on_goto_gosub(void) +{ + bool gosub; + int pos, n; + int index; + + TRANS_expression(FALSE); + + if (TRANS_is(RS_GOTO)) + gosub = FALSE; + else if (TRANS_is(RS_GOSUB)) + gosub = TRUE; + else + THROW(E_SYNTAX); + + check_try(gosub ? "ON GOSUB" : "ON GOTO"); + + pos = CODE_get_current_pos(); + CODE_nop(); + + for (n = 1;; n++) + { + if (!PATTERN_is_identifier(*JOB->current)) + THROW(E_SYNTAX); + + index = PATTERN_index(*JOB->current); + JOB->current++; + + add_goto(index, RS_NONE); + + if (!TRANS_is(RS_COMMA)) + break; + + if (n == 127) + THROW("Too many labels"); + } + + pos = CODE_set_current_pos(pos); + CODE_on(n); + CODE_set_current_pos(pos); + + if (gosub) + { + //control_add_relocation(); + CODE_gosub(ctrl_local); + } + else + CODE_jump(); +} + + +void TRANS_do(int type) +{ + bool is_until; + + control_enter(type); + control_set_value(CODE_get_current_pos()); + + if (type == RS_REPEAT) + return; + + is_until = PATTERN_is(*JOB->current, RS_UNTIL); + + if (PATTERN_is(*JOB->current, RS_WHILE) + || is_until) + { + + JOB->current++; + + TRANS_expression(FALSE); + + /*control_add_current_pos(); + + if (is_until) + CODE_jump_if_true(); + else + CODE_jump_if_false();*/ + + control_add_this_pos(trans_jump_if(is_until)); + + } +} + + +void TRANS_loop(int type) +{ + ushort pos; + + bool is_until; + + if (type == RS_LOOP) + control_check(RS_DO, "LOOP without DO", "LOOP"); + else if (type == RS_UNTIL) + control_check(RS_REPEAT, "UNTIL without REPEAT", "UNTIL"); + else if (type == RS_WEND) + control_check(RS_WHILE, "WEND without WHILE", "WEND"); + + control_jump_each_pos_with(current_ctrl->pos_continue); + + is_until = PATTERN_is(*JOB->current, RS_UNTIL); + + if ((type != RS_WEND) && (PATTERN_is(*JOB->current, RS_WHILE) || is_until)) + { + JOB->current++; + + TRANS_expression(FALSE); + + /*pos = CODE_get_current_pos(); + + if (is_until) + CODE_jump_if_false(); + else + CODE_jump_if_true();*/ + + pos = trans_jump_if(!is_until); + + jump_length(pos, control_get_value()); + } + else + { + pos = CODE_get_current_pos(); + CODE_jump(); + jump_length(pos, control_get_value()); + } + + control_jump_each_pos(); + control_leave(); +} + + +static void trans_select_break(bool do_not_add_pos) +{ + BEGIN_NO_BREAK + { + if (current_ctrl->value) + { + if (!do_not_add_pos) + { + control_add_current_pos(); + CODE_jump(); + } + + jump_length(current_ctrl->value, CODE_get_current_pos()); + } + } + END_NO_BREAK +} + + +void TRANS_select(void) +{ + control_enter(RS_SELECT); + + if (PATTERN_is(*JOB->current, RS_CASE)) + JOB->current++; + + TRANS_expression(FALSE); + + control_add_relocation(); + CODE_pop_ctrl(current_ctrl->local); +} + + +void TRANS_case(void) +{ + int i; + short pos; + short local; + bool like; + + control_check(RS_SELECT, "CASE without SELECT", "CASE"); + + if (current_ctrl->state) + THROW("Default case must be the last one"); + + trans_select_break(FALSE); + + local = current_ctrl->local; + + control_enter(RS_CASE); + + like = TRANS_is(RS_LIKE); + + for(i = 0; ; i++) + { + if (i > MAX_CASE_EXPR) + THROW("Too many expressions in CASE"); + + if (PATTERN_is_newline_end(*JOB->current)) + THROW("Unexpected end of line"); + + /*CODE_dup(); + TRANS_expression(FALSE); + CODE_op(C_EQ, 2);*/ + + if (like) + { + control_add_relocation(); + CODE_push_local(local); + TRANS_expression(FALSE); + CODE_op(C_LIKE, 0, 2, TRUE); + } + else if (TRANS_is(RS_TO)) + { + control_add_relocation(); + CODE_push_local(local); + TRANS_expression(FALSE); + CODE_op(C_LE, 0, 2, TRUE); + } + else + { + TRANS_expression(FALSE); + + if (TRANS_is(RS_TO)) + { + control_add_relocation(); + CODE_push_local(local); + CODE_op(C_LE, 0, 2, TRUE); + control_add_relocation(); + CODE_push_local(local); + TRANS_expression(FALSE); + CODE_op(C_LE, 0, 2, TRUE); + CODE_op(C_AND, 0, 2, TRUE); + } + else + { + control_add_relocation(); + CODE_push_local(local); + CODE_op(C_EQ, 0, 2, TRUE); + } + } + + if (!PATTERN_is(*JOB->current, RS_COMMA)) + { + pos = CODE_get_current_pos(); + CODE_jump_if_false(); + break; + } + + control_add_current_pos(); + CODE_jump_if_true(); + + JOB->current++; + TRANS_newline(); + } + + control_jump_each_pos(); + control_leave(); + + current_ctrl->value = pos; +} + + +void TRANS_default(void) +{ + control_check(RS_SELECT, "DEFAULT without SELECT", "DEFAULT"); + + if (current_ctrl->state) + THROW("Default case already defined"); + + trans_select_break(FALSE); + + current_ctrl->value = 0; /*CODE_get_current_pos();*/ + current_ctrl->state = 1; +} + + +void TRANS_end_select(void) +{ + control_check(RS_SELECT, "END SELECT without SELECT", "END SELECT"); + + /* + if (current_ctrl->value) + jump_length(current_ctrl->value, CODE_get_current_pos()); + */ + + trans_select_break(TRUE); + + control_jump_each_pos(); + + control_leave(); +} + + +void TRANS_break(void) +{ + TRANS_CTRL *ctrl_inner = control_get_inner(); + + check_try("BREAK"); + + if (!ctrl_inner) + THROW(E_UNEXPECTED, "BREAK"); + + control_add_pos(&ctrl_inner->pos_break, CODE_get_current_pos()); + CODE_jump(); +} + + +void TRANS_continue(void) +{ + TRANS_CTRL *ctrl_inner = control_get_inner(); + + check_try("CONTINUE"); + + if (!ctrl_inner) + THROW(E_UNEXPECTED, "CONTINUE"); + + control_add_pos(&ctrl_inner->pos_continue, CODE_get_current_pos()); + CODE_jump(); +} + + +void TRANS_return(void) +{ + if (FUNCTION_is_procedure(JOB->func)) + { + if (!(PATTERN_is_newline(*JOB->current) || PATTERN_is_end(*JOB->current))) + THROW("Return value datatype not specified in function declaration"); + } + + if (PATTERN_is_newline(*JOB->current) || PATTERN_is_end(*JOB->current)) + CODE_return(0); + else + { + TRANS_expression(FALSE); + CODE_return(1); + } +} + + +void TRANS_for(void) +{ + int local; + bool downto = FALSE; + + control_enter(RS_FOR); + + local = TRANS_loop_local(FALSE); + + TRANS_want(RS_EQUAL, "="); + + TRANS_expression(FALSE); + + CODE_pop_local(local); + + control_check_loop_var(local); + + if (TRANS_is(RS_DOWNTO)) + downto = TRUE; + else + TRANS_want(RS_TO, "TO"); + + TRANS_expression(FALSE); + + if (PATTERN_is(*JOB->current, RS_STEP)) + { + JOB->current++; + TRANS_expression(FALSE); + if (downto) + CODE_op(C_NEG, 0, 1, TRUE); + } + else + { + CODE_push_number(downto ? -1 : 1); + } + + /*CODE_pop_ctrl(current_ctrl->local + 1); + CODE_pop_ctrl(current_ctrl->local);*/ + + if (!PATTERN_is_newline(*JOB->current)) + THROW(E_UNEXPECTED, READ_get_pattern(JOB->current)); + + control_add_relocation(); + CODE_jump_first(current_ctrl->local); + + control_set_value(CODE_get_current_pos()); + + /* + current = JOB->current; + JOB->current = loop_var; + TRANS_expression(FALSE); + JOB->current = current; + */ + + control_add_current_pos(); + CODE_jump_next(); + + CODE_pop_local(local); +} + + +void TRANS_for_each(void) +{ + PATTERN *iterator = JOB->current; + PATTERN *save; + int local; + bool var = TRUE; + + while (!PATTERN_is(*JOB->current, RS_IN)) + { + if (PATTERN_is_newline_end(*JOB->current)) + { + JOB->current = iterator; + var = FALSE; + break; + } + JOB->current++; + } + + control_enter(RS_EACH); + + if (var) + JOB->current++; + + TRANS_expression(FALSE); + + /*CODE_pop_ctrl(current_ctrl->local);*/ + + control_add_relocation(); + CODE_first(current_ctrl->local); + + control_set_value(CODE_get_current_pos()); + control_add_current_pos(); + + if (var) + { + CODE_next(FALSE); + + save = JOB->current; + JOB->current = iterator; + + local = TRANS_loop_local(TRUE); + CODE_pop_local(local); + + //TRANS_reference(); + + TRANS_want(RS_IN, "IN"); + + JOB->current = save; + } + else + CODE_next(TRUE); + + return; +} + + +void TRANS_next(void) +{ + ushort pos; + + control_check_two(RS_FOR, RS_EACH, "NEXT without FOR", "NEXT"); + + /* + if (current_ctrl->type == RS_FOR) + { + control_jump_each_pos_with(current_ctrl->pos_continue); + + pos = CODE_get_current_pos(); + CODE_jump(); + jump_length(pos, control_get_value()); + + control_jump_each_pos(); + control_leave(); + } + else + { + */ + control_jump_each_pos_with(current_ctrl->pos_continue); + + pos = CODE_get_current_pos(); + CODE_jump(); + jump_length(pos, control_get_value()); + + control_jump_each_pos(); + control_leave(); +} + + +void TRANS_try(void) +{ + ushort pos; + + check_try(NULL); + + pos = CODE_get_current_pos(); + CODE_try(); + + TRANS_in_try = TRUE; + TRANS_statement(); + TRANS_in_try = FALSE; + + jump_length(pos, CODE_get_current_pos()); + CODE_end_try(); +} + + +void TRANS_finally(void) +{ + ushort pos = CODE_get_current_pos(); + + if ((JOB->func->finally != 0) + || (JOB->func->catch != 0) + || (pos == 0)) + THROW(E_UNEXPECTED, "FINALLY"); + + JOB->func->finally = pos; +} + + +void TRANS_catch(void) +{ + ushort pos = CODE_get_current_pos(); + + if ((JOB->func->catch != 0) + || (pos == 0)) + THROW(E_UNEXPECTED, "CATCH"); + + CODE_catch(); + + JOB->func->catch = CODE_get_current_pos(); +} + + +void TRANS_label(void) +{ + CLASS_SYMBOL *sym; + int sym_index; + TRANS_LABEL *label; + + sym_index = PATTERN_index(*JOB->current); + JOB->current++; + + sym = CLASS_declare(JOB->class, sym_index, TK_LABEL, FALSE); + + if (label_info == NULL) + ARRAY_create(&label_info); + + sym->local.type = TYPE_make(T_VOID, -1, TK_LABEL); + sym->local.value = ARRAY_count(label_info); + + label = ARRAY_add(&label_info); + + label->index = sym_index; + label->ctrl_id = (ctrl_level == 0) ? 0 : current_ctrl->id; + label->pos = CODE_get_current_pos(); + + #ifdef DEBUG_GOTO + printf("TRANS_label: %.*s ctrl_id = %d\n", sym->symbol.len, sym->symbol.name, label->ctrl_id); + #endif + + /* on saute le ':' */ + JOB->current++; + + /* + // A new set of control stack slots must be used for a GOSUB subroutine + + for (i = 0; i < ARRAY_count(goto_info); i++) + { + if (goto_info[i].gosub && goto_info[i].index == sym_index) + { + ctrl_local = JOB->func->nctrl + JOB->func->nlocal; + return; + } + } + */ +} + + +void TRANS_with(void) +{ + if (!TRANS_affectation(TRUE)) + TRANS_expression(FALSE); + + control_enter(RS_WITH); + + control_add_relocation(); + CODE_pop_ctrl(current_ctrl->local); +} + +void TRANS_use_with(void) +{ + TRANS_CTRL *ctrl_inner = control_get_inner_with(); + + if (ctrl_inner == NULL) + THROW("Syntax error. Point syntax used outside of WITH / END WITH"); + + control_add_relocation(); + CODE_push_local(ctrl_inner->local); +} + + +void TRANS_end_with(void) +{ + control_check(RS_WITH, "END WITH without WITH", "END WITH"); + + control_leave(); +} + + +void TRANS_raise(void) +{ + CLASS_SYMBOL *sym; + int index; + int np; + + if (!PATTERN_is_identifier(*JOB->current)) + THROW("Syntax error in event name"); + + index = PATTERN_index(*JOB->current); + sym = CLASS_get_symbol(JOB->class, index); + + JOB->current++; + + if (TYPE_get_kind(sym->global.type) == TK_EVENT) + { + if (TYPE_is_static(JOB->func->type)) + THROW("Cannot raise events in static function"); + + CODE_push_event(sym->global.value); + } + else + { + CODE_push_unknown_event(CLASS_add_unknown(JOB->class, index)); + } + + np = 0; + + if (TRANS_is(RS_LBRA)) + { + for (;;) + { + if (TRANS_is(RS_RBRA)) + break; + + if (np > 0) + TRANS_want(RS_COMMA, "comma"); + + if (np >= MAX_PARAM_FUNC) + THROW("Too many arguments"); + + TRANS_expression(FALSE); + np++; + } + } + + CODE_call(np); + + if (!TRANS_in_assignment) + CODE_drop(); +} diff --git a/main/gbc/gbc_trans_expr.c b/main/gbc/gbc_trans_expr.c new file mode 100644 index 00000000..c25029fe --- /dev/null +++ b/main/gbc/gbc_trans_expr.c @@ -0,0 +1,1115 @@ +/*************************************************************************** + + gbc_trans_expr.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define _TRANS_EXPR_C + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gbc_compile.h" + +#include "gbc_trans.h" +#include "gb_code.h" + +//#define DEBUG 1 + +//static bool _accept_statement = FALSE; + +static bool _must_drop_vargs = FALSE; + +static TYPE _type[MAX_EXPR_LEVEL]; +static int _type_level = 0; +static TYPE _last_type; + +static CLASS_SYMBOL *_last_symbol_used = NULL; +static bool _last_symbol_global = FALSE; + +static void trans_expression(bool check_statement); + +#if DEBUG + +static void _push_type(TYPE type) +{ + if (_type_level >= MAX_EXPR_LEVEL) // should have been detected by TRANS_tree() + THROW("Expression too complex"); + + fprintf(stderr, "level = %d / %d\n", _type_level, MAX_EXPR_LEVEL); + _type[_type_level++] = type; +} + +static void _drop_type(int n) +{ + _type_level -= n; + if (_type_level < 0) + ERROR_panic("Incorrect type analyze"); +} + +#define push_type(_type) fprintf(stderr, "push_type: %d in %s.%d\n", (_type).t.id, __func__, __LINE__), _push_type(_type) +#define push_type_id(_id) fprintf(stderr, "push_type_id: %d in %s.%d\n", (_id), __func__, __LINE__), _push_type(TYPE_make_simple(_id)) +#define pop_type() (fprintf(stderr, "pop_type: in %s.%d\n", __func__, __LINE__),(_type[--_type_level])) +#define drop_type(_n) fprintf(stderr, "drop_type: %d in %s.%d\n", (_n), __func__, __LINE__),_drop_type(_n) +#define get_type_id(_i, _nparam) (fprintf(stderr, "get_type(%d,%d): %d in %s.%d\n", (_i), (_nparam), (_type[_type_level + (_i) - (_nparam)].t.id), __func__, __LINE__),(_type[_type_level + (_i) - (_nparam)].t.id)) +#define dup_type() fprintf(stderr, "dup_type: in %s.%d\n", __func__, __LINE__),push_type(_type[_type_level - 1]) + +#else + +static void push_type(TYPE type) +{ + if (_type_level >= MAX_EXPR_LEVEL) // should have been detected by TRANS_tree() + THROW("Expression too complex"); + + _type[_type_level++] = type; +} + +static void drop_type(int n) +{ + _type_level -= n; + if (_type_level < 0) + ERROR_panic("Incorrect type analyze"); +} + +#define push_type_id(_id) push_type(TYPE_make_simple(_id)) +#define pop_type() (_type[--_type_level]) +#define get_type(_i, _nparam) (_type[_type_level + (_i) - (_nparam)]) +#define get_type_id(_i, _nparam) (get_type(_i, _nparam).t.id) +#define dup_type() push_type(_type[_type_level - 1]) + +#endif + +static short get_nparam(PATTERN *tree, int count, int *pindex, uint64_t *byref) +{ + PATTERN pattern; + int nparam = 0; + int index = *pindex; + + if (index < count) + { + pattern = tree[index + 1]; + if (PATTERN_is_param(pattern)) + { + index++; + nparam = PATTERN_index(pattern); + } + + if (byref) + { + int shift = 0; + *byref = 0; + while (index < count) + { + pattern = tree[index + 1]; + if (!PATTERN_is_param(pattern)) + break; + index++; + *byref |= (uint64_t)PATTERN_index(pattern) << shift; + shift += 16; + } + } + else + { + while (index < count) + { + pattern = tree[index + 1]; + if (!PATTERN_is_param(pattern)) + break; + index++; + } + } + + *pindex = index; + } + + /* + Gère le cas où on a codé un subr sans mettre de parenthèses + => nparam = 0 + */ + + return (short)nparam; +} + + +static void push_number(int index) +{ + TRANS_NUMBER number; + TRANS_DECL decl; + + if (TRANS_get_number(index, &number)) + THROW(E_SYNTAX); + + if (number.type == T_INTEGER) + { + CODE_push_number(number.ival); + } + else + { + CLEAR(&decl); + decl.type = TYPE_make_simple(number.type); + decl.index = NO_SYMBOL; + decl.value = index; + if (number.type == T_LONG) + decl.lvalue = number.lval; + CODE_push_const(CLASS_add_constant(JOB->class, &decl)); + } + + if (number.complex) + { + CODE_push_complex(); + push_type_id(T_OBJECT); + } + else + push_type_id(number.type); +} + + +static void push_string(int index, bool trans) +{ + TRANS_DECL decl; + SYMBOL *sym; + int len; + + if (index == VOID_STRING_INDEX) + len = 0; + else + { + sym = TABLE_get_symbol(JOB->class->string, index); + len = sym->len; + } + + if (len == 0) + { + CODE_push_void_string(); + } + else if (len == 1 && !trans) + { + CODE_push_char(*(sym->name)); + } + else + { + //CLEAR(&decl); + + if (trans) + decl.type = TYPE_make_simple(T_CSTRING); + else + decl.type = TYPE_make_simple(T_STRING); + decl.index = NO_SYMBOL; + decl.value = index; + + CODE_push_const(CLASS_add_constant(JOB->class, &decl)); + } + + push_type_id(T_STRING); +} + +bool TRANS_string(PATTERN pattern) +{ + if (!PATTERN_is_string(pattern)) + return TRUE; + push_string(PATTERN_index(pattern), FALSE); + return FALSE; +} + + +static void trans_class(int index) +{ + const char *name; + + if (!CLASS_exist_class(JOB->class, index)) + { + name = TABLE_get_symbol_name(JOB->class->table, index); + THROW("Unknown identifier: &1", name); + } + + CODE_push_class(CLASS_add_class(JOB->class, index)); + push_type_id(T_OBJECT); +} + +static void trans_identifier(int index, bool point, PATTERN next) +{ + CLASS_SYMBOL *sym = CLASS_get_symbol(JOB->class, index); + bool is_static; + bool is_func; + int type; + CONSTANT *constant; + +#if DEBUG + fprintf(stderr, "trans_identifier: %.*s\n", sym->symbol.len, sym->symbol.name); +#endif + + _last_symbol_used = NULL; + //fprintf(stderr, "_last_symbol_used = NULL\n"); + + if (!TYPE_is_null(sym->local.type) && !point) + { + CODE_push_local(sym->local.value); + push_type(sym->local.type); + sym->local_used = TRUE; + _last_symbol_used = sym; + _last_symbol_global = FALSE; + //fprintf(stderr, "_last_symbol_used = %.*s / local\n", sym->symbol.len, sym->symbol.name); + } + else if (!TYPE_is_null(sym->global.type) && !point) + { + type = TYPE_get_kind(sym->global.type); + if (!TYPE_is_public(sym->global.type)) + { + sym->global_used = TRUE; + _last_symbol_used = sym; + _last_symbol_global = TRUE; + //fprintf(stderr, "_last_symbol_used = %.*s / global\n", sym->symbol.len, sym->symbol.name); + } + + if (type == TK_CONST) + { + if (PATTERN_is_point(next)) + goto __CLASS; + + constant = &JOB->class->constant[sym->global.value]; + type = TYPE_get_id(constant->type); + if (type == T_BOOLEAN) + CODE_push_boolean(constant->value); + else if (type == T_INTEGER) + CODE_push_number(constant->value); + else + CODE_push_const(sym->global.value); + + push_type_id(type); + } + else if (type == TK_EXTERN) + { + if (PATTERN_is_point(next)) + goto __CLASS; + + CODE_push_extern(sym->global.value); + push_type(JOB->class->ext_func[sym->global.value].type); + } + /* That breaks some code if the property has the same name as a class! + else if (type == TK_PROPERTY) + { + CODE_push_me(FALSE); + CODE_push_unknown(CLASS_add_unknown(JOB->class, index)); + }*/ + else if (type == TK_EVENT || type == TK_LABEL || type == TK_PROPERTY) + { + goto __CLASS; + } + else /* TK_FUNCTION || TK_VARIABLE */ + { + is_static = TYPE_is_static(sym->global.type); + is_func = type == TK_FUNCTION; + + if (is_func && PATTERN_is_point(next)) + goto __CLASS; + + if (!is_static && TYPE_is_static(JOB->func->type)) + THROW("Dynamic symbols cannot be used in static function"); + + if (is_func && PATTERN_is_point(next)) + goto __CLASS; + else + CODE_push_global(sym->global.value, is_static, is_func); + + if (is_func) + push_type(JOB->class->function[sym->global.value].type); + else if (is_static) + push_type(JOB->class->stat[sym->global.value].type); + else + push_type(JOB->class->dyn[sym->global.value].type); + } + } + else + { + if (point) + { + CODE_push_unknown(CLASS_add_unknown(JOB->class, index)); + push_type_id(T_VOID); + } + else + goto __CLASS; + } + + return; + +__CLASS: + + trans_class(index); +} + + +static void trans_subr(int subr, short nparam) +{ + SUBR_INFO *info = &COMP_subr_info[subr]; + int type; + + if (nparam < info->min_param) + THROW("Not enough arguments to &1()", info->name); + else if (nparam > info->max_param) + THROW("Too many arguments to &1()", info->name); + + if (subr == SUBR_VarPtr) + { + if (CODE_check_varptr()) + THROW("VarPtr() argument must be a dynamic, a static or a local variable"); + } + else if (subr == SUBR_IsMissing) + { + JOB->func->use_is_missing = TRUE; + if (CODE_check_ismissing()) + THROW("IsMissing() requires a function argument"); + } + + CODE_subr(info->opcode, nparam, info->optype, info->max_param == info->min_param); + + type = info->type; + switch(type) + { + case RST_SAME: + type = get_type_id(0, nparam); + break; + + case RST_BCLR: + type = get_type_id(0, nparam); + break; + + case RST_MIN: + type = Max(get_type_id(0, nparam), get_type_id(1, nparam)); + if (type > T_DATE && type != T_VARIANT) + THROW("Number or Date expected"); + } + + if (nparam) + drop_type(nparam); + + push_type_id(type); +} + + +static void trans_operation(short op, short nparam, PATTERN previous) +{ + COMP_INFO *info = &COMP_res_info[op]; + int type = info->type; + int type1, type2; + TYPE ftype; + + switch (info->value) + { + case OP_PT: + if (nparam == 0) + { + TRANS_use_with(); + type = T_OBJECT; + } + else if (!PATTERN_is_identifier(previous)) + THROW(E_SYNTAX); + else + { + switch(get_type_id(0, nparam)) + { + case T_OBJECT: + case T_VARIANT: + case T_STRUCT: + case T_STRING: + break; + + default: + THROW("Not an object"); + } + } + + break; + + case OP_EXCL: + if (!PATTERN_is_identifier(previous)) + THROW(E_SYNTAX); + break; + + case OP_LSQR: + CODE_push_array(nparam); + break; + + case OP_RSQR: + TRANS_subr(TS_SUBR_ARRAY, nparam); + if (nparam > MAX_PARAM_OP) + nparam--; + break; + + case OP_COLON: + TRANS_subr(TS_SUBR_COLLECTION, nparam); + if (nparam > MAX_PARAM_OP) + nparam -= 2; + break; + + case OP_MINUS: + if (nparam == 1) + { + CODE_op(C_NEG, 0, nparam, TRUE); + type = RST_SAME; + } + else + CODE_op(info->code, info->subcode, nparam, TRUE); + break; + + default: + CODE_op(info->code, info->subcode, nparam, (info->flag != RSF_OPN)); + } + + switch(type) + { + case RST_SAME: + ftype = get_type(0, nparam); + break; + + case RST_ADD: + type = Max(get_type_id(0, nparam), get_type_id(1, nparam)); + if (type == T_DATE) + type = T_FLOAT; + ftype = TYPE_make_simple(type); + break; + + case RST_AND: + type1 = get_type_id(0, nparam); + type2 = get_type_id(1, nparam); + + if (type1 != T_VARIANT && type2 != T_VARIANT) + { + if ((type1 > T_BOOLEAN && type1 <= T_LONG) ^ (type2 > T_BOOLEAN && type2 <= T_LONG)) + COMPILE_print(MSG_WARNING, JOB->line, "integer and boolean mixed with `&1' operator", info->name); + } + + type = Max(type1, type2); + + if (type > T_LONG) + { + if (type != T_VARIANT) + type = T_BOOLEAN; + } + + ftype = TYPE_make_simple(type); + break; + + case RST_NOT: + type = get_type_id(0, nparam); + if (type == T_STRING || type == T_OBJECT || type == T_DATE) + type = T_BOOLEAN; + ftype = TYPE_make_simple(type); + break; + + case RST_MOD: + type1 = get_type_id(0, nparam); + type2 = get_type_id(1, nparam); + + if (type1 != T_VARIANT && type2 != T_VARIANT) + { + if (type1 <= T_BOOLEAN || type1 > T_LONG || type2 <= T_BOOLEAN || type2 > T_LONG) + THROW("Type mismatch"); + } + + ftype = TYPE_make_simple(Max(type1, type2)); + break; + + case RST_GET: + ftype = TYPE_make_simple(T_VARIANT); + break; + + /*case RST_GET: + ftype = get_type(0, nparam); + if (ftype.t.id == T_OBJECT) + { + ftype = JOB->class->class[ftype.t.value].array; + if (TYPE_is_null(ftype)) + ftype = TYPE_make_simple(T_VARIANT); + } + else + ftype = TYPE_make_simple(T_VARIANT); + + fprintf(stderr, "RST_GET: %s\n", TYPE_get_desc(ftype)); + break;*/ + + default: + ftype = TYPE_make_simple(type); + + } + + if (nparam) + drop_type(nparam); + + push_type(ftype); +} + + +static void trans_call(short nparam, uint64_t byref) +{ + if (!byref) + { + CODE_call(nparam); + } + else + { + CODE_call_byref(nparam, byref); + + if (_must_drop_vargs) + { + CODE_drop_vargs(); + _must_drop_vargs = FALSE; + } + } + + drop_type(nparam + 1); + + push_type_id(T_VARIANT); +} + +static void trans_expr_from_tree(TRANS_TREE *tree, int count) +{ + static void *jump[] = { + &&__CONTINUE, &&__CONTINUE, &&__RESERVED, &&__IDENTIFIER, &&__NUMBER, &&__STRING, &&__TSTRING, &&__CONTINUE, &&__SUBR, &&__CLASS, &&__CONTINUE, &&__CONTINUE + }; + + int i, op; + short nparam; + PATTERN pattern, next_pattern, prev_pattern; + uint64_t byref = 0; + + pattern = NULL_PATTERN; + + #if DEBUG + fprintf(stderr, "-------------------------------------------- %d\n", _type_level); + #endif + + i = 0; + + for(i = 0; i < count; i++) + { + prev_pattern = pattern; + pattern = tree[i]; + next_pattern = tree[i + 1]; + + goto *jump[PATTERN_type(pattern)]; + + __NUMBER: + + push_number(PATTERN_index(pattern)); + continue; + + __STRING: + + push_string(PATTERN_index(pattern), FALSE); + continue; + + __TSTRING: + + push_string(PATTERN_index(pattern), TRUE); + continue; + + __IDENTIFIER: + + trans_identifier(PATTERN_index(pattern), PATTERN_is_point(pattern), next_pattern); + continue; + + __CLASS: + + trans_class(PATTERN_index(pattern)); + continue; + + __SUBR: + + nparam = get_nparam(tree, count, &i, NULL); + trans_subr(PATTERN_index(pattern), nparam); + continue; + + __RESERVED: + + if (PATTERN_is(pattern, RS_TRUE)) + { + CODE_push_boolean(TRUE); + push_type_id(T_BOOLEAN); + } + else if (PATTERN_is(pattern, RS_FALSE)) + { + CODE_push_boolean(FALSE); + push_type_id(T_BOOLEAN); + } + else if (PATTERN_is(pattern, RS_NULL)) + { + CODE_push_null(); + push_type_id(T_OBJECT); + } + else if (PATTERN_is(pattern, RS_ME)) + { + /*if (FUNCTION_is_static(JOB->func)) + THROW("ME cannot be used in a static function");*/ + + CODE_push_me(FALSE); + push_type_id(T_OBJECT); + } + else if (PATTERN_is(pattern, RS_SUPER)) + { + /*if (FUNCTION_is_static(JOB->func)) + THROW("ME cannot be used in a static function");*/ + + CODE_push_super(FALSE); + push_type_id(T_OBJECT); + } + else if (PATTERN_is(pattern, RS_LAST)) + { + CODE_push_last(); + push_type_id(T_OBJECT); + } + else if (PATTERN_is(pattern, RS_AT)) + { + if (TRANS_popify_last()) + THROW("This expression cannot be passed by reference"); + } + else if (PATTERN_is(pattern, RS_COMMA)) + { + CODE_drop(); + drop_type(1); + } + else if (PATTERN_is(pattern, RS_ERROR)) + { + TRANS_subr(TS_SUBR_ERROR, 0); + push_type_id(T_BOOLEAN); + } + else if (PATTERN_is(pattern, RS_OPTIONAL)) + { + CODE_push_void(); + push_type_id(T_VOID); + } + else if (PATTERN_is(pattern, RS_PINF)) + { + CODE_push_inf(FALSE); + push_type_id(T_FLOAT); + } + else if (PATTERN_is(pattern, RS_MINF)) + { + CODE_push_inf(TRUE); + push_type_id(T_FLOAT); + } + else if (PATTERN_is(pattern, RS_3PTS)) + { + _must_drop_vargs = TRUE; + CODE_push_vargs(); + // push_type_id(T_FLOAT); we push no type + } + else + { + op = PATTERN_index(pattern); + if (op == RS_LBRA) + { + nparam = get_nparam(tree, count, &i, &byref); + trans_call(nparam, byref); + } + else + { + nparam = get_nparam(tree, count, &i, NULL); + trans_operation((short)op, nparam, prev_pattern); + } + } + + __CONTINUE: + ; + } + + _last_type = pop_type(); +} + + +void TRANS_new(void) +{ + int index; + int i, nparam; + bool array = FALSE; + bool event = FALSE; + //bool collection = FALSE; + bool check_param = FALSE; + + nparam = 0; + + if (PATTERN_is_class(*JOB->current)) + { + index = CLASS_add_class(JOB->class, PATTERN_index(*JOB->current)); + if (PATTERN_is(JOB->current[1], RS_LSQR)) + index = CLASS_get_array_class(JOB->class, T_OBJECT, index); + + CODE_push_class(index); + nparam = 1; + } + else if (PATTERN_is_type(*JOB->current)) + { + if (!PATTERN_is(JOB->current[1], RS_LSQR)) + THROW("Cannot instantiate native types"); + + //CODE_push_number(RES_get_type(PATTERN_index(*JOB->current))); + CODE_push_class(CLASS_get_array_class(JOB->class, RES_get_type(PATTERN_index(*JOB->current)), -1)); + nparam = 1; + } + else if (PATTERN_is(*JOB->current, RS_LBRA)) + { + /* NEW ("Class", ...) */ + nparam = 0; + JOB->current--; + check_param = TRUE; + } + else + THROW(E_SYNTAX); + + JOB->current++; + + if (TRANS_is(RS_LSQR)) + { + //if (collection) + // THROW("Array declaration is forbidden with typed collection"); + + if (!PATTERN_is(*JOB->current, RS_RSQR)) + { + for (i = 0;; i++) + { + if (i > MAX_ARRAY_DIM) + THROW("Too many dimensions"); + + TRANS_expression(FALSE); + nparam++; + + if (PATTERN_is(*JOB->current, RS_RSQR)) + break; + + if (!PATTERN_is(*JOB->current, RS_COMMA)) + THROW(E_MISSING, "']'"); + + JOB->current++; + } + } + + JOB->current++; + array = TRUE; + } + else + { + if (TRANS_is(RS_LBRA)) + { + for(;;) + { + if (nparam > MAX_PARAM_FUNC) + THROW("Too many arguments"); + + if (PATTERN_is(*JOB->current, RS_AT) || PATTERN_is(*JOB->current, RS_BYREF)) + THROW("NEW cannot have arguments passed by reference"); + + TRANS_expression(FALSE); + nparam++; + + if (PATTERN_is(*JOB->current, RS_RBRA)) + break; + + if (!PATTERN_is(*JOB->current, RS_COMMA)) + THROW(E_MISSING, "')'"); + + JOB->current++; + } + + JOB->current++; + + if (check_param && nparam == 0) + THROW("Not enough argument to New()"); + } + + if (TRANS_is(RS_AS)) + { + TRANS_expression(FALSE); + nparam++; + event = TRUE; + } + + /* + CODE_call(nparam, FALSE); + CODE_drop(); + */ + } + + //if (collection) + // CODE_new(nparam, TRUE, event); + //else + CODE_new(nparam, array, event); +} + + +static void trans_expression(bool check_statement) +{ + TRANS_TREE *tree; + int tree_length; + + if (!check_statement) + { + if (TRANS_is(RS_NEW)) + { + TRANS_new(); + return; + } + else if (TRANS_is(RS_READ)) + { + TRANS_read(); + return; + } + } + + TRANS_tree(check_statement, &tree, &tree_length); + + trans_expr_from_tree(tree, tree_length); + + FREE(&tree); + + if (check_statement) + { + /* + if (!CODE_check_statement_last()) + THROW("This expression cannot be a statement"); + */ + + CODE_drop(); + } +} + + +void TRANS_ignore_expression() +{ + TRANS_tree(FALSE, NULL, NULL); +} + + +TYPE TRANS_variable_get_type() +{ + TYPE type = TYPE_make_simple(T_VOID); + TRANS_TREE *tree; + int count; + int index; + CLASS_SYMBOL *sym; + + TRANS_tree(FALSE, &tree, &count); + + if (count == 1 && PATTERN_is_identifier(*tree)) + { + index = PATTERN_index(*tree); + sym = CLASS_get_symbol(JOB->class, index); + + if (!TYPE_is_null(sym->local.type)) + { + return sym->local.type; + } + else if (!TYPE_is_null(sym->global.type)) + { + int type = TYPE_get_kind(sym->global.type); + if (type == TK_VARIABLE) + return sym->global.type; + } + } + + FREE(&tree); + + return type; +} + + +bool TRANS_popify_last() +{ + if (!CODE_popify_last()) + return TRUE; + + if (_last_symbol_used) + { + if (_last_symbol_global) + _last_symbol_used->global_assigned = TRUE; + else + _last_symbol_used->local_assigned = TRUE; + } + + return FALSE; +} + + +void TRANS_reference(void) +{ + TRANS_expression(FALSE); + if (TRANS_popify_last()) + THROW("Invalid assignment"); +} + + +bool TRANS_affectation(bool dup) +{ + static TRANS_STATEMENT statement[] = { + //{ RS_NEW, TRANS_new }, + { RS_OPEN, TRANS_open }, + { RS_CLOSE, TRANS_close }, + { RS_SHELL, TRANS_shell }, + { RS_EXEC, TRANS_exec }, + { RS_RAISE, TRANS_raise }, + { RS_PIPE, TRANS_pipe }, + { RS_LOCK, TRANS_lock }, + { RS_MEMORY, TRANS_memory }, + //{ RS_READ, TRANS_read }, + { RS_NONE, NULL } + }; + + TRANS_STATEMENT *st; + PATTERN *look = JOB->current; + PATTERN *left, *expr, *after; + int niv = 0; + bool equal = FALSE; + bool stat = FALSE; + int op = RS_NONE; + int id = RS_NONE; + + for(;;) + { + if (PATTERN_is_newline(*look)) // || PATTERN_is_end(*look)) + break; + + if (PATTERN_is(*look, RS_LBRA) || PATTERN_is(*look, RS_LSQR)) + { + niv++; + } + else if (PATTERN_is(*look, RS_RBRA) || PATTERN_is(*look, RS_RSQR)) + { + if (niv > 0) + niv--; + } + else if (niv == 0) + { + if (PATTERN_is(*look, RS_EQUAL)) + { + equal = TRUE; + op = RS_NONE; + break; + } + else if (PATTERN_is_reserved(*look) && RES_is_assignment(PATTERN_index(*look))) + { + equal = TRUE; + op = RES_get_assignment_operator(PATTERN_index(*look)); + break; + } + } + + look++; + } + + if (!equal || look == JOB->current) + return FALSE; + + left = JOB->current; + *look++ = PATTERN_make(RT_NEWLINE, 0); + expr = look; + + JOB->current = expr; + + if (op == RS_NONE && (PATTERN_is_reserved(*JOB->current))) + { + if (TRANS_is(RS_NEW)) + { + TRANS_in_assignment++; + TRANS_new(); + TRANS_in_assignment--; + id = RS_NEW; + stat = TRUE; + } + else + { + for (st = statement; st->id; st++) + { + if (TRANS_is(st->id)) + { + id = st->id; + TRANS_in_assignment++; + (*st->func)(); + TRANS_in_assignment--; + stat = TRUE; + break; + } + } + } + } + + if (!stat) + { + TYPE type; + + if (op != RS_NONE) + { + JOB->current = left; + trans_expression(FALSE); + type = _last_type; + } + + JOB->current = expr; + trans_expression(FALSE); + after = JOB->current; + + if (op != RS_NONE) + { + push_type(_last_type); + push_type(type); + trans_operation(op, 2, NULL_PATTERN); + pop_type(); + } + } + + after = JOB->current; + + if (dup) + CODE_dup(); + + if (COMPILE_version >= 0x03070000) + { + if (id == RS_EXEC || id == RS_SHELL) + CODE_dup(); + } + + JOB->current = left; + TRANS_reference(); + + if (!PATTERN_is_newline(*JOB->current)) + THROW(E_SYNTAX); + + JOB->current = after; + + if (COMPILE_version >= 0x03070000) + { + if (id == RS_EXEC || id == RS_SHELL) + { + TRANS_subr(TS_SUBR_CHECK_EXEC, 1); + CODE_drop(); + } + } + + return TRUE; +} + + +void TRANS_expression(bool check_statement) +{ + _type_level = 0; + trans_expression(check_statement); +} + diff --git a/main/gbc/gbc_trans_subr.c b/main/gbc/gbc_trans_subr.c new file mode 100644 index 00000000..a5db385b --- /dev/null +++ b/main/gbc/gbc_trans_subr.c @@ -0,0 +1,1036 @@ +/*************************************************************************** + + gbc_trans_subr.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define _TRANS_SUBR_C + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gbc_compile.h" +#include "gbc_trans.h" +#include "gb_code.h" +#include "gb_limit.h" + +/*#define DEBUG*/ + + +typedef + struct { + char *name; + SUBR_INFO *info; + } + TRANS_SUBR_INFO; + + +static void trans_subr(int subr, int nparam) +{ + static TRANS_SUBR_INFO subr_info[] = + { + { ".Print" }, { ".Input" }, { ".Write" }, { ".WriteBytes" }, { ".Read" }, + { ".ReadBytes" }, { ".Open" }, { ".Close" }, { "Seek" }, { ".LineInput" }, + { ".Flush" }, { ".Exec" }, { ".Shell" }, { ".Wait" }, { ".Kill" }, + { ".Move" }, { ".Mkdir" }, { ".Rmdir" }, { ".Array" }, {".Collection" }, + { ".Copy" }, { ".Link" }, { ".Error" }, { ".Lock" }, { ".Unlock" }, + { ".LockWait" }, { ".InputFrom" }, { ".OutputTo" }, { ".Debug" }, { ".Sleep" }, + { ".Randomize" }, { ".ErrorTo" }, { "Left" }, { "Mid" }, { ".OpenMemory" }, + { ".Chmod" }, { ".Chown" }, { ".Chgrp" }, { ".Use" }, { ".CheckExec" }, + { ".MoveKill" } + }; + + TRANS_SUBR_INFO *tsi = &subr_info[subr]; + + if (tsi->info == NULL) + { + tsi->info = SUBR_get(tsi->name); + if (!tsi->info) + ERROR_panic("Unknown intern subroutine: %s", tsi->name); + } + + if (subr == TS_SUBR_ARRAY && nparam > MAX_PARAM_OP) + CODE_subr(tsi->info->opcode, MAX_PARAM_OP + 1, CODE_CALL_VARIANT + MAX_PARAM_OP, FALSE); + else if (subr == TS_SUBR_COLLECTION && nparam > MAX_PARAM_OP) + CODE_subr(tsi->info->opcode, MAX_PARAM_OP, CODE_CALL_VARIANT + MAX_PARAM_OP - 1, FALSE); + else + CODE_subr(tsi->info->opcode, nparam, tsi->info->optype, tsi->info->min_param == tsi->info->max_param); +} + + +static bool trans_stream_check(int default_stream, bool check) +{ + if (TRANS_is(RS_SHARP) || default_stream == TS_NONE) + { + TRANS_expression(FALSE); + + if (check) + { + if (PATTERN_is(*JOB->current, RS_COMMA)) + { + JOB->current++; + if (PATTERN_is_newline(*JOB->current)) + THROW(E_SYNTAX); + } + else + { + if (!PATTERN_is_newline(*JOB->current)) + THROW(E_SYNTAX); + } + } + + return FALSE; + } + else + { + //if (default_stream == TS_NONE) + // THROW("Syntax error. &1 expected", "'#'"); + + CODE_push_number(default_stream); + return TRUE; + } +} + +#define trans_stream(_default_stream) trans_stream_check(_default_stream, TRUE) +#define trans_stream_no_check(_default_stream) trans_stream_check(_default_stream, FALSE) + +static void trans_print_debug() +{ + int nparam = 1; + bool semicolon_or_comma = FALSE; + + for(;;) + { + if (PATTERN_is_newline(*JOB->current)) + break; + + TRANS_expression(FALSE); + nparam++; + semicolon_or_comma = FALSE; + + if (PATTERN_is_newline(*JOB->current)) + break; + + if (TRANS_is(RS_SCOLON)) + { + if (TRANS_is(RS_SCOLON)) + { + CODE_push_char(' '); + nparam++; + } + semicolon_or_comma = TRUE; + } + else if (TRANS_is(RS_COMMA)) + { + CODE_push_char('\t'); + nparam++; + semicolon_or_comma = TRUE; + } + else + THROW(E_SYNTAX); + } + + if (!semicolon_or_comma) + { + CODE_push_char('\n'); + nparam++; + } + + trans_subr(TS_SUBR_PRINT, nparam); + CODE_drop(); +} + + +void TRANS_print(void) +{ + trans_stream(TS_STDOUT); + trans_print_debug(); +} + + +void TRANS_debug(void) +{ + if (!JOB->debug) + CODE_disable(); + + trans_subr(TS_SUBR_DEBUG, 0); + trans_print_debug(); + + if (!JOB->debug) + CODE_enable(); +} + + +void TRANS_assert(void) +{ + if (!JOB->debug || JOB->exec) + CODE_disable(); + + TRANS_expression(FALSE); + trans_subr(TS_SUBR_DEBUG, 1); + CODE_drop(); + + if (!JOB->debug || JOB->exec) + CODE_enable(); +} + + +void TRANS_error(void) +{ + if (TRANS_is(RS_TO)) + { + if (TRANS_is(RS_DEFAULT)) + CODE_push_null(); + else + trans_stream(TS_NONE); + + trans_subr(TS_SUBR_ERROR_TO, 1); + + if (!TRANS_in_assignment) + CODE_drop(); + } + else + { + CODE_push_number(2); // stderr + trans_print_debug(); + } +} + +static int trans_binary_type(void) +{ + int index; + int nparam = 0; + + if (PATTERN_is_class(*JOB->current)) + { + index = CLASS_add_class(JOB->class, PATTERN_index(*JOB->current)); + if (PATTERN_is(JOB->current[1], RS_LSQR)) + index = CLASS_get_array_class(JOB->class, T_OBJECT, index); + + CODE_push_class(index); + } + else if (PATTERN_is_type(*JOB->current)) + { + if (PATTERN_is(JOB->current[1], RS_LSQR)) + { + index = CLASS_get_array_class(JOB->class, RES_get_type(PATTERN_index(*JOB->current)), -1); + CODE_push_class(index); + } + else + { + index = RES_get_type(PATTERN_index(*JOB->current)); + CODE_push_number(index); + } + } + else + THROW(E_SYNTAX); + + JOB->current++; + nparam++; + + #if 0 + if (TRANS_is(RS_LSQR)) + { + TRANS_expression(FALSE); + nparam++; + TRANS_want(RS_RSQR, NULL); + } + else if (TRANS_is(RS_STAR)) + { + if (!string) + THROW("Syntax error"); + TRANS_expression(FALSE); + nparam++; + } + #endif + + return nparam; +} + + +void TRANS_write(void) +{ + trans_stream(TS_STDOUT); + + TRANS_expression(FALSE); + + if (TRANS_is(RS_AS)) + { + trans_binary_type(); + trans_subr(TS_SUBR_WRITE, 3); + } + else + { + if (TRANS_is(RS_COMMA)) + TRANS_expression(FALSE); + else + { + if (JOB->no_old_read_syntax) + THROW("Syntax error. &1 expected", "AS"); + CODE_push_number(-1); + } + + trans_subr(TS_SUBR_WRITE_BYTES, 3); + } + + CODE_drop(); +} + + +void TRANS_output_to() +{ + TRANS_want(RS_TO, NULL); + + if (TRANS_is(RS_DEFAULT)) + CODE_push_null(); + else + trans_stream(TS_NONE); + + trans_subr(TS_SUBR_OUTPUT_TO, 1); + + if (!TRANS_in_assignment) + CODE_drop(); +} + +void TRANS_input_from() +{ + if (TRANS_is(RS_DEFAULT)) + CODE_push_null(); + else + trans_stream(TS_NONE); + + trans_subr(TS_SUBR_INPUT_FROM, 1); + + if (!TRANS_in_assignment) + CODE_drop(); +} + + +void TRANS_input(void) +{ + bool stream = TRUE; + + if (TRANS_is(RS_FROM)) + { + TRANS_input_from(); + return; + } + + trans_stream(TS_STDIN); + + for(;;) + { + trans_subr(TS_SUBR_INPUT, (stream ? 1 : 0)); + stream = FALSE; + + TRANS_reference(); + + if (PATTERN_is_newline(*JOB->current)) + break; + + if (!PATTERN_is(*JOB->current, RS_COMMA) + && !PATTERN_is(*JOB->current, RS_SCOLON)) + THROW(E_SYNTAX); + + JOB->current++; + } +} + +void TRANS_read_old(void) +{ + PATTERN *save_var; + PATTERN *save_current; + TYPE type; + + if (JOB->no_old_read_syntax) + THROW(E_UNEXPECTED, "READ"); + + trans_stream(TS_STDIN); + + save_var = JOB->current; + type = TRANS_variable_get_type(); + + if (TRANS_is(RS_COMMA)) + { + TRANS_expression(FALSE); + trans_subr(TS_SUBR_READ_BYTES, 2); + } + else + { + int id = TYPE_get_id(type); + + CODE_push_number(id); + trans_subr(TS_SUBR_READ, 2); + } + + save_current = JOB->current; + JOB->current = save_var; + TRANS_reference(); + JOB->current = save_current; +} + +void TRANS_read(void) +{ + bool def = trans_stream_no_check(TS_STDIN); + + if (TRANS_is(RS_AS)) + { + trans_binary_type(); + trans_subr(TS_SUBR_READ, 2); + } + else + { + if (!def) + TRANS_want(RS_COMMA, NULL); + + TRANS_expression(FALSE); + trans_subr(TS_SUBR_READ_BYTES, 2); + } +} + +void TRANS_open(void) +{ + int mode = TS_MODE_READ; + + if (TRANS_is(RS_PIPE)) + { + TRANS_pipe(); + return; + } + else if (TRANS_is(RS_MEMORY)) + { + TRANS_memory(); + return; + } + else if (TRANS_is(RS_STRING)) + { + TRANS_open_string(); + return; + } + + + /* Nom du fichier */ + + TRANS_expression(FALSE); + + /* mode d'ouverture */ + + if (TRANS_is(RS_FOR)) + { + if (TRANS_is(RS_READ)) + mode |= TS_MODE_READ | TS_MODE_DIRECT; + else if (TRANS_is(RS_INPUT)) + mode |= TS_MODE_READ; + + if (TRANS_is(RS_WRITE)) + mode |= TS_MODE_WRITE | TS_MODE_DIRECT; + else if (TRANS_is(RS_OUTPUT)) + mode |= TS_MODE_WRITE; + + if (TRANS_is(RS_CREATE)) + mode |= TS_MODE_CREATE; + else if (TRANS_is(RS_APPEND)) + mode |= TS_MODE_APPEND; + + //if (TRANS_is(RS_DIRECT)) + // mode |= TS_MODE_DIRECT; + + if (TRANS_is(RS_WATCH)) + mode |= TS_MODE_WATCH; + + /*if (TRANS_is(RS_BIG)) + mode |= TS_MODE_BIG; + else if (TRANS_is(RS_LITTLE)) + mode |= TS_MODE_LITTLE;*/ + + /*JOB->current--; + if (PATTERN_is(*JOB->current, RS_FOR)) + THROW("Syntax error in file open mode"); + JOB->current++;*/ + } + + CODE_push_number(mode); + + trans_subr(TS_SUBR_OPEN, 2); + + /*if (!TRANS_in_assignment) + { + //CODE_drop(); + + TRANS_want(RS_AS, NULL); + TRANS_ignore(RS_SHARP); + + TRANS_reference(); + }*/ +} + + +void TRANS_pipe(void) +{ + int mode = TS_MODE_READ; + + /* Nom du fichier */ + + TRANS_expression(FALSE); + + /* mode d'ouverture */ + + if (TRANS_is(RS_FOR)) + { + if (TRANS_is(RS_READ)) + mode |= TS_MODE_READ | TS_MODE_DIRECT; + //else if (TRANS_is(RS_INPUT)) + // mode |= TS_MODE_READ; + + if (TRANS_is(RS_WRITE)) + mode |= TS_MODE_WRITE | TS_MODE_DIRECT; + //else if (TRANS_is(RS_OUTPUT)) + // mode |= TS_MODE_WRITE; + + if (TRANS_is(RS_WATCH)) + mode |= TS_MODE_WATCH; + } + + CODE_push_number(mode | TS_MODE_PIPE); + + trans_subr(TS_SUBR_OPEN, 2); +} + + +void TRANS_memory(void) +{ + int mode = TS_MODE_READ; + + /* Memory address */ + + TRANS_expression(FALSE); + + /* Open mode */ + + if (TRANS_is(RS_FOR)) + { + if (TRANS_is(RS_READ)) + mode |= TS_MODE_READ | TS_MODE_DIRECT; + + if (TRANS_is(RS_WRITE)) + mode |= TS_MODE_WRITE | TS_MODE_DIRECT; + } + + CODE_push_number(mode); + + trans_subr(TS_SUBR_OPEN_MEMORY, 2); +} + + +void TRANS_open_string(void) +{ + int mode = TS_MODE_READ; + + /* Nom du fichier */ + + if (!PATTERN_is(*JOB->current, RS_FOR)) + TRANS_expression(FALSE); + else + CODE_push_null(); + + /* mode d'ouverture */ + + if (TRANS_is(RS_FOR)) + { + if (TRANS_is(RS_READ)) + mode |= TS_MODE_READ | TS_MODE_DIRECT; + + if (TRANS_is(RS_WRITE)) + mode |= TS_MODE_WRITE | TS_MODE_DIRECT; + } + + CODE_push_number(mode | TS_MODE_STRING); + + trans_subr(TS_SUBR_OPEN, 2); +} + + +void TRANS_close(void) +{ + if (PATTERN_is_newline(*JOB->current)) + THROW(E_SYNTAX); + + TRANS_ignore(RS_SHARP); + TRANS_expression(FALSE); + + trans_subr(TS_SUBR_CLOSE, 1); + + if (!TRANS_in_assignment) + CODE_drop(); +} + + +void TRANS_lock(void) +{ + if (PATTERN_is_newline(*JOB->current)) + THROW(E_SYNTAX); + + if (!TRANS_in_assignment) + THROW("Useless LOCK"); + + TRANS_expression(FALSE); + + if (TRANS_is(RS_WAIT)) + { + TRANS_expression(FALSE); + trans_subr(TS_SUBR_LOCK_WAIT, 2); + } + else + trans_subr(TS_SUBR_LOCK, 1); +} + + +void TRANS_unlock(void) +{ + if (PATTERN_is_newline(*JOB->current)) + THROW(E_SYNTAX); + + TRANS_ignore(RS_SHARP); + TRANS_expression(FALSE); + + trans_subr(TS_SUBR_UNLOCK, 1); + CODE_drop(); +} + + +void TRANS_seek(void) +{ + int nparam; + + trans_stream(TS_NONE); + + TRANS_expression(FALSE); + nparam = 2; + + /* + if (TRANS_is(RS_COMMA)) + { + TRANS_expression(FALSE); + nparam++; + } + */ + + trans_subr(TS_SUBR_SEEK, nparam); + CODE_drop(); +} + + +void TRANS_line_input(void) +{ + if (TRANS_is(RS_INPUT)) + { + trans_stream(TS_STDIN); + trans_subr(TS_SUBR_LINE_INPUT, 1); + TRANS_reference(); + } + else + THROW(E_SYNTAX); +} + + +void TRANS_flush(void) +{ + trans_stream(TS_STDOUT); + trans_subr(TS_SUBR_FLUSH, 1); + CODE_drop(); +} + + +void TRANS_quit(void) +{ + if (PATTERN_is_newline(*JOB->current)) + { + CODE_quit(FALSE); + } + else + { + TRANS_expression(FALSE); + CODE_quit(TRUE); + } + +} + +void TRANS_randomize(void) +{ + if (PATTERN_is_newline(*JOB->current)) + { + trans_subr(TS_SUBR_RANDOMIZE, 0); + } + else + { + TRANS_expression(FALSE); + trans_subr(TS_SUBR_RANDOMIZE, 1); + } + + CODE_drop(); +} + +static void trans_exec_shell(bool shell) +{ + int mode = TS_EXEC_NONE; + bool wait; + bool as = TRUE; + + TRANS_expression(FALSE); + + if (TRANS_is(RS_WITH)) + TRANS_expression(FALSE); + else + CODE_push_null(); + + wait = TRANS_is(RS_WAIT); + + if (TRANS_is(RS_FOR)) + { + if (TRANS_is(RS_READ)) + mode |= TS_EXEC_READ; + if (TRANS_is(RS_WRITE)) + mode |= TS_EXEC_WRITE; + + if (mode == 0) + { + mode |= TS_EXEC_TERM; + if (TRANS_is(RS_INPUT)) + mode |= TS_EXEC_READ; + if (TRANS_is(RS_OUTPUT)) + mode |= TS_EXEC_WRITE; + } + } + else + { + /*if (TRANS_is(RS_ERROR)) + { + mode = TS_EXEC_STRING + TS_EXEC_ERROR; + TRANS_want(RS_TO, "TO expected"); + } + else if (TRANS_is(RS_TO)) + { + mode = TS_EXEC_STRING; + } + + if (mode & TS_EXEC_STRING) + { + if (TRANS_in_assignment) + THROW("Syntax error. Cannot use this syntax in assignment"); + + wait = TRUE; + as = FALSE; + }*/ + + if (TRANS_is(RS_TO)) + { + if (TRANS_in_assignment) + THROW("Syntax error. Cannot use this syntax in assignment"); + + mode = TS_EXEC_STRING; + wait = TRUE; + as = FALSE; + } + } + + if (wait) mode |= TS_EXEC_WAIT; + CODE_push_number(mode); + + if (as && TRANS_is(RS_AS)) + TRANS_expression(FALSE); + else + CODE_push_null(); + + trans_subr(shell ? TS_SUBR_SHELL : TS_SUBR_EXEC, 4); + + if (mode & TS_EXEC_STRING) + TRANS_reference(); + else if (!TRANS_in_assignment) + CODE_drop(); +} + + +void TRANS_exec(void) +{ + trans_exec_shell(FALSE); +} + +void TRANS_shell(void) +{ + trans_exec_shell(TRUE); +} + + +void TRANS_wait(void) +{ + int nparam = 0; + + if (!PATTERN_is_newline(*JOB->current)) + { + TRANS_expression(FALSE); + nparam = 1; + } + + trans_subr(TS_SUBR_WAIT, nparam); + CODE_drop(); +} + + +void TRANS_sleep(void) +{ + TRANS_expression(FALSE); + trans_subr(TS_SUBR_SLEEP, 1); + CODE_drop(); +} + + +void TRANS_kill(void) +{ + TRANS_expression(FALSE); + trans_subr(TS_SUBR_KILL, 1); + CODE_drop(); +} + +void TRANS_move(void) +{ + int subr; + + TRANS_expression(FALSE); + if (TRANS_is(RS_TO)) + subr = TS_SUBR_MOVE; + else if (TRANS_is(RS_DOWNTO) || TRANS_is(RS_KILL)) + subr = TS_SUBR_MOVE_KILL; + else + THROW_UNEXPECTED(JOB->current); + + TRANS_expression(FALSE); + trans_subr(subr, 2); + CODE_drop(); +} + +void TRANS_copy(void) +{ + TRANS_expression(FALSE); + TRANS_want(RS_TO, NULL); + TRANS_expression(FALSE); + trans_subr(TS_SUBR_COPY, 2); + CODE_drop(); +} + +void TRANS_link(void) +{ + TRANS_expression(FALSE); + TRANS_want(RS_TO, NULL); + TRANS_expression(FALSE); + trans_subr(TS_SUBR_LINK, 2); + CODE_drop(); +} + +void TRANS_chmod(void) +{ + TRANS_expression(FALSE); + TRANS_want(RS_TO, NULL); + TRANS_expression(FALSE); + trans_subr(TS_SUBR_CHMOD, 2); + CODE_drop(); +} + +void TRANS_chown(void) +{ + TRANS_expression(FALSE); + TRANS_want(RS_TO, NULL); + TRANS_expression(FALSE); + trans_subr(TS_SUBR_CHOWN, 2); + CODE_drop(); +} + +void TRANS_chgrp(void) +{ + TRANS_expression(FALSE); + TRANS_want(RS_TO, NULL); + TRANS_expression(FALSE); + trans_subr(TS_SUBR_CHGRP, 2); + CODE_drop(); +} + +void TRANS_inc(void) +{ + PATTERN *save = JOB->current; + + TRANS_expression(FALSE); + CODE_push_number(1); + CODE_op(C_ADD, 0, 2, TRUE); + + JOB->current = save; + TRANS_reference(); +} + +void TRANS_dec(void) +{ + PATTERN *save = JOB->current; + + TRANS_expression(FALSE); + CODE_push_number(1); + CODE_op(C_SUB, 0, 2, TRUE); + + JOB->current = save; + TRANS_reference(); +} + + +void TRANS_swap(void) +{ + PATTERN *sa; + PATTERN *sb; + PATTERN *current; + + sa = JOB->current; + TRANS_expression(FALSE); + + TRANS_want(RS_COMMA, "Comma"); + + sb = JOB->current; + TRANS_expression(FALSE); + + current = JOB->current; + + JOB->current = sa; + TRANS_reference(); + + JOB->current = sb; + TRANS_reference(); + + JOB->current = current; +} + +void TRANS_mkdir(void) +{ + TRANS_expression(FALSE); + trans_subr(TS_SUBR_MKDIR, 1); + CODE_drop(); +} + +void TRANS_rmdir(void) +{ + TRANS_expression(FALSE); + trans_subr(TS_SUBR_RMDIR, 1); + CODE_drop(); +} + +void TRANS_mid() +{ + PATTERN *str; + PATTERN *pos; + PATTERN *len; + PATTERN *save; + + TRANS_want(RS_LBRA, "Left bracket"); + + str = JOB->current; + TRANS_expression(FALSE); + + TRANS_want(RS_COMMA, "Comma"); + + pos = JOB->current; + TRANS_expression(FALSE); + CODE_push_number(1); + CODE_op(C_SUB, 0, 2, TRUE); + TRANS_subr(TS_SUBR_LEFT, 2); + + if (TRANS_is(RS_COMMA)) + { + len = JOB->current; + TRANS_ignore_expression(); + } + else + { + len = NULL; + } + + TRANS_want(RS_RBRA, "Right bracket"); + TRANS_want(RS_EQUAL, "Equal"); + + TRANS_expression(FALSE); + + save = JOB->current; + + if (len) + { + JOB->current = str; + TRANS_expression(FALSE); + JOB->current = pos; + TRANS_expression(FALSE); + JOB->current = len; + TRANS_expression(FALSE); + CODE_op(C_ADD, 0, 2, TRUE); + TRANS_subr(TS_SUBR_MID, 2); + } + + CODE_op(C_CAT, 0, len ? 3 : 2, FALSE); + + JOB->current = str; + TRANS_reference(); + + JOB->current = save; +} + + + +#if 0 +void TRANS_scan(void) +{ + PATTERN *save; + int noutput = 0; + + TRANS_expression(FALSE); + TRANS_want(RS_WITH, NULL); + TRANS_expression(FALSE); + + TRANS_want(RS_TO, NULL); + + save = JOB->current; + for(;;) + { + TRANS_expression(FALSE); + noutput++; + if (!TRANS_is(RS_COMMA)) + break; + } + JOB->current = save; + + trans_subr_output(TSO_SUBR_SCAN, 2 + noutput, noutput); + + for(;;) + { + TRANS_reference(); + if (!TRANS_is(RS_COMMA)) + break; + } +} +#endif + +void TRANS_subr(int subr, int nparam) +{ + trans_subr(subr, nparam); +} + diff --git a/main/gbc/gbc_trans_tree.c b/main/gbc/gbc_trans_tree.c new file mode 100644 index 00000000..bcbedc20 --- /dev/null +++ b/main/gbc/gbc_trans_tree.c @@ -0,0 +1,825 @@ +/*************************************************************************** + + gbc_trans_tree.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_TRANS_TREE_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gbc_compile.h" + +#include "gbc_trans.h" +#include "gb_code.h" +#include "gbc_read.h" + +//#define DEBUG + +#define BYREF_TEST(_byref, _n) (_byref & ((uintptr_t)1 << _n)) +#define BYREF_SET(_byref, _n) _byref |= ((uintptr_t)1 << _n) + +static short level; +static PATTERN *current; +static TRANS_TREE tree[MAX_EXPR_PATTERN]; +static int tree_length = 0; + +static void analyze_expr(short priority, short op_main); +static void analyze_array(); + + +static void THROW_EXPR_TOO_COMPLEX() +{ + THROW("Expression too complex"); +} + +static void inc_level() +{ + level++; + if (level > MAX_EXPR_LEVEL) + THROW_EXPR_TOO_COMPLEX(); +} + + +static void dec_level() +{ + level--; +} + +#define add_pattern(_pattern) \ +do { \ + if (tree_length >= MAX_EXPR_PATTERN) \ + THROW_EXPR_TOO_COMPLEX(); \ + tree[tree_length++] = (_pattern); \ +} while (0) + + +static void remove_last_pattern() +{ + if (tree_length == 0) + return; + + tree_length--; +} + + +static PATTERN get_last_pattern(int dep) +{ + if (tree_length < dep) + return NULL_PATTERN; + else + return tree[tree_length - dep]; +} + + +static void change_last_pattern(int dep, PATTERN pattern) +{ + if (tree_length < dep) + return; + tree[tree_length - dep] = pattern; +} + + +static void check_last_first(int dep) +{ + if (PATTERN_is_identifier(get_last_pattern(dep))) + change_last_pattern(dep, PATTERN_set_flag(get_last_pattern(dep), RT_FIRST)); +} + + +static void add_reserved_pattern(int reserved) +{ + add_pattern(PATTERN_make(RT_RESERVED, reserved)); +} + + +static void add_operator_output(short op, short nparam, uint64_t byref) +{ + PATTERN pattern; + + /* + Why this test? + + add_operator() can be called without operator. See: + if (RES_priority(op) < prio) ... + */ + + if (op == RS_NONE || op == RS_UNARY) + return; + + if (op == RS_EXCL) + { + op = RS_LSQR; + nparam = 2; + + check_last_first(2); + } + + pattern = PATTERN_make(RT_RESERVED, op); + + //if (op == RS_LBRA && byref) + // pattern = PATTERN_set_flag(pattern, RT_OUTPUT); + + add_pattern(pattern); + + add_pattern(PATTERN_make(RT_PARAM, nparam)); + + if (op == RS_LBRA && byref) + { + while (byref) + { + add_pattern(PATTERN_make(RT_PARAM, byref & 0xFFFF)); + byref >>= 16; + } + } +} + +#define add_operator(_op, _nparam) add_operator_output(_op, _nparam, 0) + + +static void add_subr(PATTERN subr_pattern, short nparam) +{ + PATTERN pattern; + + //if (has_output) + // subr_pattern = PATTERN_set_flag(subr_pattern, RT_OUTPUT); + + add_pattern(subr_pattern); + + pattern = PATTERN_make(RT_PARAM, nparam); + add_pattern(pattern); +} + +static bool is_statement(void) +{ + PATTERN last; + int count; + + count = tree_length; + + if (count == 0) + return FALSE; + + count--; + last = tree[count]; + + while (PATTERN_is_param(last) && (count > 0)) + { + count--; + last = tree[count]; + } + + if (PATTERN_is(last, RS_PT)) + goto _ADD_BRACE; + + if (PATTERN_is_identifier(last)) + { + if (count == 0) + goto _ADD_BRACE; + + if (count >= 2) + { + last = tree[count - 1]; + if (PATTERN_is_param(last) && (PATTERN_index(last) == 0) + && PATTERN_is(tree[count - 2], RS_PT)) + goto _ADD_BRACE; + } + } + else if (PATTERN_is_subr(last) + || PATTERN_is(last, RS_LBRA) + || PATTERN_is(last, RS_RBRA) + || PATTERN_is(last, RS_AT) + || PATTERN_is(last, RS_COMMA)) + return TRUE; + + #ifdef DEBUG + printf("Last = "); + READ_dump_pattern(&last); + printf("%08X %08X %d\n", last, PATTERN_make(RT_RESERVED, RS_LBRA), PATTERN_is(last, RS_LBRA)); + #endif + + return FALSE; + +_ADD_BRACE: + + add_operator(RS_LBRA, 0); + #ifdef DEBUG + printf("Add ()\n"); + #endif + return TRUE; +} + +static void analyze_make_array() +{ + int n = 0; + bool checked = FALSE; + bool collection = FALSE; + + /*if (PATTERN_is(*current, RS_RSQR)) + { + current++; + add_pattern(PATTERN_make(RT_RESERVED, RS_NULL)); + return; + }*/ + + if (!PATTERN_is(*current, RS_RSQR)) + { + for(;;) + { + n++; + /*if (n > MAX_PARAM_OP) + THROW("Too many arguments");*/ + analyze_expr(0, RS_NONE); + + if (!checked) + { + collection = PATTERN_is(*current, RS_COLON); + checked = TRUE; + } + + if (collection) + { + if (!PATTERN_is(*current, RS_COLON)) + THROW(E_MISSING, "':'"); + current++; + n++; + /*if (n > MAX_PARAM_OP) + THROW("Too many arguments");*/ + analyze_expr(0, RS_NONE); + } + + if (!PATTERN_is(*current, RS_COMMA)) + break; + + current++; + + if (collection) + { + if (n == (MAX_PARAM_OP - 1)) + { + add_operator(RS_COLON, MAX_PARAM_OP + 1); + n = 0; + } + } + else + { + if (n == MAX_PARAM_OP) + { + add_operator(RS_RSQR, MAX_PARAM_OP + 1); + n = 0; + } + } + } + } + + while (PATTERN_is_newline(*current)) + { + add_pattern(PATTERN_make(RT_NEWLINE, 0)); + JOB->line++; + current++; + } + + if (!PATTERN_is(*current, RS_RSQR)) + THROW(E_MISSING, "']'"); + current++; + + add_operator(collection ? RS_COLON : RS_RSQR, n); +} + + +static void analyze_single(int op) +{ + PATTERN *pattern; + bool jump_newline; + + jump_newline = PATTERN_is_newline(*current); + if (jump_newline) + { + add_pattern(PATTERN_make(RT_NEWLINE, 0)); + JOB->line++; + current++; + } + + if (op == RS_PT && !PATTERN_is_identifier(*current)) + THROW("The '.' operator must be followed by an identifier"); + else if (op == RS_EXCL && !PATTERN_is_string(*current)) + THROW("The '!' operator must be followed by an identifier"); + + /* ( expr ) */ + + if (PATTERN_is(*current, RS_LBRA)) + { + int old_length = tree_length; + PATTERN last; + + current++; + analyze_expr(0, RS_NONE); + + if (!PATTERN_is(*current, RS_RBRA)) + THROW(E_MISSING, "')'"); + current++; + + if (tree_length == (old_length + 1)) + { + last = get_last_pattern(1); + if (PATTERN_is_string(last)) + change_last_pattern(1, PATTERN_make(RT_TSTRING, PATTERN_index(last))); + } + } + + /* [ expr, expr, ... ] */ + + else if (PATTERN_is(*current, RS_LSQR)) + { + current++; + analyze_make_array(); + } + + /* - expr | NOT expr */ + + else if (PATTERN_is(*current, RS_MINUS) || PATTERN_is(*current, RS_NOT)) + { + pattern = current; + current++; + + analyze_expr(RES_priority(RS_NOT), RS_UNARY); + add_operator(PATTERN_index(*pattern), 1); + } + + // . symbol + + else if (PATTERN_is(*current, RS_PT) && PATTERN_is_identifier(current[1])) + { + add_operator(PATTERN_index(current[0]), 0); + add_pattern(PATTERN_set_flag(current[1], RT_POINT)); + add_operator(PATTERN_index(current[0]), 2); + current += 2; + } + + // . [ ... ] + + else if (PATTERN_is(*current, RS_PT) && PATTERN_is(current[1], RS_LSQR)) + { + add_operator(PATTERN_index(current[0]), 0); + //add_pattern(PATTERN_set_flag(RS_RSQR, RT_POINT)); + current += 2; + analyze_array(); + } + + // ! symbol + + else if (PATTERN_is(*current, RS_EXCL) && PATTERN_is_string(current[1])) + { + add_operator(RS_PT, 0); + add_pattern(PATTERN_set_flag(current[1], RT_POINT)); + add_operator(RS_EXCL, 0); + current += 2; + } + + /* NULL, TRUE, FALSE, ME, PARENT, LAST, ERROR */ + /* number, string or symbol */ + + else if (PATTERN_is(*current, RS_NULL) + || PATTERN_is(*current, RS_ME) + || PATTERN_is(*current, RS_LAST) + || PATTERN_is(*current, RS_TRUE) + || PATTERN_is(*current, RS_FALSE) + || PATTERN_is(*current, RS_PINF) + || PATTERN_is(*current, RS_MINF) + || PATTERN_is(*current, RS_ERROR) + || (!PATTERN_is_reserved(*current) && !PATTERN_is_newline(*current) && !PATTERN_is_end(*current))) + { + add_pattern(*current); + + if (PATTERN_is_identifier(*current)) + { + /*if ((op == RS_NONE || op == RS_UNARY) && (PATTERN_is_identifier(*current))) + change_last_pattern(1, PATTERN_set_flag(get_last_pattern(1), RT_FIRST));*/ + if (op == RS_PT) + { + change_last_pattern(1, PATTERN_set_flag(get_last_pattern(1), RT_POINT)); + check_last_first(2); + } + } + + current++; + } + + else if (PATTERN_is(*current, RS_SUPER)) + { + add_pattern(*current); + current++; + if (!PATTERN_is(*current, RS_PT) + && !PATTERN_is(*current, RS_EXCL) + && !PATTERN_is(*current, RS_LBRA) + && !PATTERN_is(*current, RS_LSQR)) + THROW("SUPER cannot be used alone"); + } + + else + { + if (jump_newline) + { + current--; + JOB->line--; + } + + THROW_UNEXPECTED(current); + } +} + + +static void analyze_call() +{ + static PATTERN *byref_pattern[MAX_PARAM_FUNC]; + + int i, nparam_post = 0; + PATTERN subr_pattern = NULL_PATTERN; + PATTERN last_pattern = get_last_pattern(1); + SUBR_INFO *info; + bool optional = TRUE; + uint64_t byref = 0; + PATTERN *save; + + /* + get_pattern_subr(last_pattern, &subr); + */ + if (PATTERN_is_subr(last_pattern)) + { + subr_pattern = last_pattern; + remove_last_pattern(); + optional = FALSE; + } + else if (PATTERN_is_identifier(last_pattern)) + { + check_last_first(1); + } + else if (PATTERN_is_string(last_pattern) || PATTERN_is_number(last_pattern)) + THROW(E_SYNTAX); + + /* N.B. Le cas où last_pattern = "." n'a pas de test spécifique */ + + if (PATTERN_type(subr_pattern) == RT_SUBR && PATTERN_index(subr_pattern) == SUBR_VarPtr) + { + if (!PATTERN_is_identifier(current[0]) || !PATTERN_is(current[1], RS_RBRA)) + THROW("Syntax error. VarPtr() takes only one identifier"); + + add_pattern(*current); + current += 2; + add_subr(subr_pattern, 1); + } + else + { + for (;;) + { + if (PATTERN_is(*current, RS_RBRA)) + { + current++; + break; + } + + if (nparam_post > 0) + { + if (!PATTERN_is(*current, RS_COMMA)) + THROW(E_MISSING, "',' or ')'"); + current++; + } + + #if 0 + if (FALSE) /*(PATTERN_is(*current, RS_AMP))*/ + { + current++; + output[nparam_post] = current; + has_output = TRUE; + } + else + { + output[nparam_post] = NULL; + } + #endif + + if (optional && (PATTERN_is(*current, RS_COMMA) || PATTERN_is(*current, RS_RBRA))) + { + add_reserved_pattern(RS_OPTIONAL); + } + else if (optional && PATTERN_is(*current, RS_3PTS) && PATTERN_is(current[1], RS_RBRA)) + { + current++; + add_reserved_pattern(RS_3PTS); + nparam_post--; + } + else + { + if (PATTERN_is(*current, RS_AT) || PATTERN_is(*current, RS_BYREF)) + { + current++; + BYREF_SET(byref, nparam_post); + byref_pattern[nparam_post] = current; + } + + analyze_expr(0, RS_NONE); + } + + nparam_post++; + + if (nparam_post >= MAX_PARAM_FUNC) + THROW("Too many arguments"); + } + + last_pattern = get_last_pattern(1); + if (PATTERN_is(last_pattern, RS_OPTIONAL)) + THROW("Syntax error. Needless arguments"); + + /* + while (nparam_post > 0) + { + if (get_last_pattern(1) != PATTERN_make(RT_RESERVED, RS_OPTIONAL)) + break; + + remove_last_pattern(); + nparam_post--; + } + */ + + if (PATTERN_is_null(subr_pattern)) + { + add_operator_output(RS_LBRA, nparam_post, byref); + + save = current; + + for (i = nparam_post - 1; i >= 0; i--) + { + if (BYREF_TEST(byref, i)) + { + current = byref_pattern[i]; + analyze_expr(0, RS_NONE); + //if (!is_statement()) + // THROW("The &1 argument cannot be passed by reference", TRANS_get_num_desc(i + 1)); + add_pattern(PATTERN_make(RT_RESERVED, RS_AT)); + } + } + + current = save; + } + else + { + info = &COMP_subr_info[PATTERN_index(subr_pattern)]; + + if (nparam_post < info->min_param) + THROW("Not enough arguments to &1()", info->name); + else if (nparam_post > info->max_param) + THROW("Too many arguments to &1()", info->name); + else if (byref) + THROW("Subroutine arguments cannot be passed by reference"); + + add_subr(subr_pattern, nparam_post); + } + } + +} + + +static void analyze_array() +{ + int i; + + check_last_first(1); + + for(i = 0; i < MAX_ARRAY_DIM; i++) + { + analyze_expr(0, RS_NONE); + + if (!PATTERN_is(*current, RS_COMMA)) + break; + + current++; + } + + if (!PATTERN_is(*current, RS_RSQR)) + THROW(E_MISSING, "',' or ')'"); + current++; + + add_operator(RS_LSQR, i + 2); +} + + +static void analyze_expr(short priority, short op_main) +{ + short op, op_curr, op_not; + short prio; + short nparam; + + inc_level(); + + op_curr = op_main; + op_not = RS_NONE; + nparam = (op_main == RS_NONE || op_main == RS_UNARY) ? 0 : 1; + + if (PATTERN_is(*current, RS_NEW)) + THROW("Cannot use NEW operator there"); + +READ_OPERAND: + + //analyze_expr_check_first(op_curr); + + analyze_single(op_curr); + nparam++; + + if (nparam > MAX_PARAM_OP) + THROW("Expression too complex. Too many operands"); + +READ_OPERATOR: + + if (!PATTERN_is_reserved(*current)) + goto OPERATOR_END; + + op = PATTERN_index(*current); + + if (!RES_is_operator(op)) + goto OPERATOR_END; + + if (op == RS_AND || op == RS_OR) + if (PATTERN_is(current[1], RS_IF)) + goto OPERATOR_END; + + current++; + + if (op == RS_NOT && PATTERN_is_reserved(*current)) + { + op_not = PATTERN_index(*current); + if (RES_is_operator(op_not) && RES_can_have_not_before(op_not)) + { + op = op_not + 1; + current++; + } + } + + if (priority) + prio = priority; + else if (op_curr == RS_NONE) + prio = 0; + else + prio = RES_priority(op_curr); + + if (op_curr == RS_NONE) + { + if (RES_is_binary(op) || RES_is_n_ary(op)) + { + op_curr = op; + goto READ_OPERAND; + } + } + + if (op_curr == op) + { + if (!(RES_is_binary(op) && nparam == 2)) + goto READ_OPERAND; + } + + if (RES_priority(op) > prio) + { + if (op == RS_LSQR) + analyze_array(); + else if (op == RS_LBRA) + analyze_call(); + else + analyze_expr(RES_priority(op), op); + + goto READ_OPERATOR; + } + + if (RES_priority(op) == prio) + { + add_operator(op_curr, nparam); + + if (op == RS_LSQR) + { + analyze_array(); + goto READ_OPERATOR; + } + else if (op == RS_LBRA) + { + analyze_call(); + goto READ_OPERATOR; + } + else + { + if (RES_is_only(op_curr) || RES_is_only(op)) + THROW("Ambiguous expression. Please use brackets"); + + nparam = 1; + op_curr = op; + goto READ_OPERAND; + } + } + + if (RES_priority(op) < prio) + { + if ((op_main != RS_NONE) || (priority > 0)) + { + add_operator(op_curr, nparam); + current--; + if (op_not != RS_NONE) + current--; + goto END; + } + + add_operator(op_curr, nparam); + + if (op == RS_LSQR) + { + analyze_array(); + nparam = 1; + op_curr = op_main; + goto READ_OPERATOR; + } + else if (op == RS_LBRA) + { + analyze_call(); + nparam = 1; + op_curr = op_main; + goto READ_OPERATOR; + } + else + { + nparam = 1; + op_curr = op; + goto READ_OPERAND; + } + } + + dec_level(); + return; + +OPERATOR_END: + + add_operator(op_curr, nparam); + +END: + + dec_level(); + return; + +} + + +void TRANS_tree(bool check_statement, TRANS_TREE **result, int *count) +{ + #ifdef DEBUG + int i; + #endif + + tree_length = 0; + current = JOB->current; + level = 0; + + TRY + { + analyze_expr(0, RS_NONE); + JOB->current = current; + } + CATCH + { + JOB->current = current; + PROPAGATE(); + } + END_TRY + + #ifdef DEBUG + printf("\n"); + for (i = 0; i < tree_length; i++) + { + printf("[% 4d] ", i); + READ_dump_pattern(&tree[i]); + } + #endif + + if (check_statement && (!is_statement())) + THROW("This expression cannot be a statement"); + + if (result) + { + add_pattern(NULL_PATTERN); + ALLOC(result, sizeof(PATTERN) * tree_length); + memcpy(*result, tree, sizeof(PATTERN) * tree_length); + *count = tree_length - 1; + } +} + diff --git a/main/gbc/gbc_type.c b/main/gbc/gbc_type.c new file mode 100644 index 00000000..c071c2a9 --- /dev/null +++ b/main/gbc/gbc_type.c @@ -0,0 +1,126 @@ +/*************************************************************************** + + gbc_type.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_TYPE_C + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gbc_type.h" + +#include "gbc_class.h" +#include "gbc_compile.h" + + +const char *TYPE_name[] = +{ + "Void", "Boolean", "Byte", "Short", "Integer", "Long", "Single", "Float", "Date", + "String", "CString", "Variant", "Array", "Pointer", "Class", "Null", + "Object" +}; + + +size_t TYPE_sizeof(TYPE type) +{ + TYPE_ID id = TYPE_get_id(type); + + switch(id) + { + case T_BOOLEAN: + return 1; + + case T_BYTE: + return 1; + + case T_SHORT: + return 2; + + case T_INTEGER: + return 4; + + case T_LONG: + return 8; + + case T_SINGLE: + return 4; + + case T_FLOAT: + return 8; + + case T_DATE: + return 8; + + case T_STRING: + return 4; + + case T_VARIANT: + return 12; + + case T_OBJECT: + return 4; + + case T_POINTER: + return 4; + + case T_ARRAY: + { + int i; + size_t size; + CLASS_ARRAY *array = &JOB->class->array[TYPE_get_value(type)]; + + size = 1; + for (i = 0; i < array->ndim; i++) + size *= array->dim[i]; + + size *= TYPE_sizeof(array->type); + + return (size + 3) & ~3; + } + + default: + ERROR_panic("TYPE_sizeof: bad type id"); + } +} + + + +const char *TYPE_get_short_desc(TYPE type) +{ + static const char *name[] = { + "", "b", "i", "i", "i", "l", "g", "f", + "d", "s", "s", "p", "v", "?", "?", "?", + "o" + }; + + TYPE_ID id; + + id = TYPE_get_id(type); + + if (id == T_ARRAY) + return "?"; + else + return name[id]; +} diff --git a/main/gbc/gbc_type.h b/main/gbc/gbc_type.h new file mode 100644 index 00000000..44cc2002 --- /dev/null +++ b/main/gbc/gbc_type.h @@ -0,0 +1,109 @@ +/*************************************************************************** + + gbc_type.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_TYPE_H +#define __GBC_TYPE_H + +#define PROJECT_COMP +#include "gb_type_common.h" + +/*************************************************** + + Format d'un Type + + F K T T X X X X + + F : Flags (TF_*) + K : Kind (TK_*) + T : Type (T_*) + X : index, pour T_OBJECT, T_ARRAY, T_STRUCT + +***************************************************/ + +/* +typedef + ulong TYPE; +*/ + +typedef + unsigned char TYPE_ID; + +typedef + union { + struct { + unsigned char flag; + TYPE_ID id; + short value; + } t; + int l; + } + TYPE; + +typedef + struct { + int value; + TYPE type; + } + VALUE; + +#ifndef __GBC_TYPE_C +EXTERN char *TYPE_name[]; +#endif + +/*#define TYPE_is_const(type) (((type) >> 24) & TF_CONST)*/ +#define TYPE_is_static(type) ((type).t.flag & TF_STATIC) +#define TYPE_is_public(type) ((type).t.flag & TF_PUBLIC) + +#define TYPE_is_optional(type) ((type).t.flag & TF_OPTIONAL) +/*#define TYPE_is_output(type) (((type) >> 24) & TF_OUTPUT)*/ + +#define TYPE_is_array(type) (TYPE_get_id(type) == T_ARRAY) +#define TYPE_is_object(type) ((TYPE_get_id(type) == T_OBJECT) && (TYPE_get_value(type) >= 0)) + +#define TYPE_get_value(type) ((type).t.value) +#define TYPE_get_kind(type) ((type).t.flag & 0x7) +#define TYPE_get_id(type) ((type).t.id) +#define TYPE_is_null(type) ((type).l == 0) +#define TYPE_is_void(type) ((type).t.id == 0) + +#define TYPE_set_value(type, _value) ((type)->t.value = (_value)) +#define TYPE_set_id(type, _id) ((type)->t.id = (_id)) +#define TYPE_set_kind(type, _kind) ((type)->t.flag |= (_kind)) +#define TYPE_set_flag(type, _flag) ((type)->t.flag |= (_flag)) +#define TYPE_clear(type) ((type)->l = 0) + +#define TYPE_can_be_long(type) (TYPE_get_id(type) <= T_LONG) + +#define TYPE_compare(_t1, _t2) ((_t1)->t.id == (_t2)->t.id && (_t1)->t.value == (_t2)->t.value) + +#define TYPE_make_simple(_id) ({ TYPE _t; _t.t.flag = 0; _t.t.id = (_id); _t.t.value = -1; _t; }) +#define TYPE_make(_id, _value, _flag) ({ TYPE _t; _t.t.flag = (_flag); _t.t.id = (_id); _t.t.value = ((_id) == T_OBJECT || (_id) == T_ARRAY || (_id) == T_STRUCT) ? (_value) : -1; _t; }) + +/*PUBLIC long TYPE_get_class(TYPE type);*/ +//TYPE TYPE_make(TYPE_ID id, short value, int flag); +const char *TYPE_get_short_desc(TYPE type); +size_t TYPE_sizeof(TYPE type); + +#endif + + diff --git a/main/gbc/gbi.c b/main/gbc/gbi.c new file mode 100644 index 00000000..df685f05 --- /dev/null +++ b/main/gbc/gbi.c @@ -0,0 +1,923 @@ +/*************************************************************************** + + gbi.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBI_C + +#include "config.h" +#include "trunk_version.h" + +#include "gb_limit.h" +#include "gb_common.h" +#include "gb_alloc.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef DONT_USE_LTDL + #define lt_dlinit() (0) + #define lt_dlhandle void * + #define lt_dlopenext(_path) dlopen(_path, RTLD_LAZY) + #define lt_dlsym(_handle, _symbol) dlsym(_handle, _symbol) + #define lt_dlclose(_handle) dlclose(_handle) + #define lt_dlerror() dlerror() +#else + #include +#endif + +#include + +#include "gb_component.h" +#include "gb_file.h" +#include "gb_str.h" +#include "gb_arch.h" +#include "gb_common_swap.h" +#include "gb_array.h" +#include "gb_table.h" +#include "gambas.h" + +static char _root[PATH_MAX + 1] = { 0 }; +static char _lib_path[PATH_MAX + 1]; +static char _info_path[PATH_MAX + 1]; +static char _buffer[PATH_MAX + 16]; +static char _env[PATH_MAX + 32]; + +static FILE *out_info; +static FILE *out_list; +static bool _verbose = FALSE; +static bool _format = FALSE; +static bool _nopreload = FALSE; +static bool _root_set = FALSE; +static bool _analyze = FALSE; +static bool _no_include_warning = TRUE; + +static char **_components = NULL; + +static TABLE *_classes = NULL; + +static bool analyze(const char *comp, bool include); + +#if HAVE_GETOPT_LONG +static struct option LongOptions[] = +{ + { "version", 0, NULL, 'V' }, + { "verbose", 0, NULL, 'v' }, + { "help", 0, NULL, 'h' }, + { "license", 0, NULL, 'L' }, + { "root", 1, NULL, 'r' }, + { 0 } +}; +#endif + + +static int compare_components(char **a, char **b) +{ + return strcmp(*a, *b); +} + +static void print(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + vfprintf(out_info, fmt, args); + + va_end(args); +} + +static void warning(const char *fmt, ...) +{ + va_list args; + + fprintf(stderr, "gbi" GAMBAS_VERSION_STRING ": warning: "); + + va_start(args, fmt); + + vfprintf(stderr, fmt, args); + + putc('\n', stderr); + + va_end(args); +} + +static void error(bool must_exit, const char *fmt, ...) +{ + va_list args; + + fprintf(stderr, "gbi" GAMBAS_VERSION_STRING ": ERROR: "); + + va_start(args, fmt); + + vfprintf(stderr, fmt, args); + + putc('\n', stderr); + + va_end(args); + + if (must_exit) + exit(1); +} + +// static void error2(const char *msg, const char *msg2, bool must_exit) +// { +// fprintf(stderr, "gbi" GAMBAS_VERSION_STRING ": ERROR: %s: %s%s%s\n", msg, msg2, (errno != 0) ? ": " : "", (errno != 0) ? strerror(errno) : ""); +// if (must_exit) +// exit(1); +// } + + +static void init(void) +{ + const char *path; + char *env; + + /* chemin d'installation de Gambas */ + + if (!_root[0]) + { + const char *dir; + + path = FILE_find_gambas(); + dir = FILE_get_dir(FILE_get_dir(path)); + if (dir) + strncpy(_root, dir, PATH_MAX); + } + + #ifdef OS_64BITS + strcpy(_lib_path, FILE_cat(_root, GAMBAS_LIB64_PATH, NULL)); + if (access(FILE_cat(_lib_path, "gb.component", NULL), F_OK)) + #endif + strcpy(_lib_path, FILE_cat(_root, GAMBAS_LIB_PATH, NULL)); + + strcpy(_info_path, FILE_cat(_root, "share/gambas" GAMBAS_VERSION_STRING "/info", NULL)); + + if (lt_dlinit()) + error(TRUE, "Cannot initialize plug-in management: %s", lt_dlerror()); + + env = getenv("GBI_DEBUG"); + if (env && *env && atoi(env)) + _no_include_warning = FALSE; +} + + +static void newline() +{ + print("\n"); +} + +#if 0 +static bool print_type(const char *type) +{ + switch (*type) + { + case 'b': print("Boolean"); break; + case 'i': print("Integer"); break; + case 's': print("String"); break; + case 'd': print("Date"); break; + case 'f': print("Float"); break; + case 'v': print("Variant"); break; + case 'o': print("Object"); break; + + default: + + while (*type && *type != ';') + { + print("%c", *type); + type++; + } + return TRUE; + } + + return FALSE; +} +#endif + +static void dump_symbol(GB_DESC *desc) +{ + const char *name; + const char *p; + + name = &desc->name[1]; + + print("%s\n", name); + print("%c\n", *desc->name); + if (desc->val1) + print("%s", desc->val1); + newline(); + + if (*desc->name == 'C') + { + switch(*(char *)desc->val1) + { + case 'i': + print("%d", desc->val2); + break; + + case 's': + p = (const char *)desc->val2; + while (*p) + { + if (*p == '\n') + print("\\n"); + else if (*p == '\t') + print("\\t"); + else + print("%c", *p); + p++; + } + break; + + case 'b': + if (desc->val2) + print("T"); + break; + + case 'f': + print("%.15g", desc->val4); + break; + + default: + print("?"); + break; + } + } + else if (desc->val3) + print("%s", (char *)desc->val3); + + newline(); +} + + +static GB_DESC *_sort_symbol; + +static int sort_symbol(const int *a, const int *b) +{ + return strcmp(_sort_symbol[*a].name, _sort_symbol[*b].name); +} + +static void add_class(const char *name, bool has_static) +{ + int index; + + if (out_list) + { + if (!TABLE_add_symbol(_classes, name, strlen(name), &index)) + { + fputs(name, out_list); + if (has_static) + fputc('!', out_list); + fputc('\n', out_list); + } + } +} + +static void analyze_class(GB_DESC *desc) +{ + const char *name = desc->name; + char *parent = NULL; + bool autocreate = FALSE; + bool nocreate = FALSE; + uintptr_t hook; + int nsymbol; + int *sort; + GB_DESC *p; + int i; + bool has_static = FALSE; + + desc++; + + while (desc->name) + { + hook = (uintptr_t)desc->name; + + if (hook == (intptr_t)GB_INHERITS_ID) + parent = (char *)desc->val1; + else if (hook == (intptr_t)GB_NOT_CREATABLE_ID) + nocreate = TRUE; + else if (hook == (intptr_t)GB_AUTO_CREATABLE_ID) + autocreate = TRUE; + else if (hook > 16) + break; + + desc++; + } + + p = desc; + nsymbol = 0; + while (p->name) + { + nsymbol++; + p++; + } + + if (_format) + { + print("CLASS %s\n", name); + if (parent) print("INHERITS %s\n", parent); + if (!nocreate) print("CREATABLE\n"); + if (autocreate) print("AUTOCREATABLE\n"); + } + else + { + print("#%s\n", name); + if (parent) + print("%s", parent); + newline(); + if (!nocreate) + print("C"); + if (autocreate) + print("A"); + newline(); + } + + ALLOC(&sort, sizeof(int) * nsymbol); + for (i = 0; i < nsymbol; i++) + sort[i] = i; + + _sort_symbol = desc; + qsort(sort, nsymbol, sizeof(int), (int (*)(const void *, const void *))sort_symbol); + + for (i = 0; i < nsymbol; i++) + { + p = &desc[sort[i]]; + if (strchr("PCMR", *p->name) && p->name[1] != '_') + has_static = TRUE; + dump_symbol(p); + } + + FREE(&sort); + + if (_format) + newline(); + + add_class(name, has_static); +} + + +static GB_DESC **_sort_desc; + +static int sort_desc(const int *a, const int *b) +{ + return strcmp(_sort_desc[*a]->name, _sort_desc[*b]->name); +} + +static void analyze_include(char *include_list) +{ + char *includes[8]; + int nincludes; + char *include; + int i; + + include_list = STR_copy(include_list); + + if (_verbose) + fprintf(stderr, "Including %s\n", include_list); + + nincludes = 0; + include = strtok(include_list, ","); + while (include && nincludes < 8) + { + includes[nincludes++] = include; + include = strtok(NULL, ","); + } + + for (i = 0; i < nincludes; i++) + { + include = strtok(includes[i], "|"); + while (include) + { + if (!analyze(include, TRUE)) + break; + include = strtok(NULL, "|"); + } + } + + STR_free(include_list); +} + +static void analyze_classes(GB_DESC **desc) +{ + int nclass; + int *sort; + int i; + GB_DESC **p; + + nclass = 0; + + p = desc; + while (*p) + { + nclass++; + p++; + } + + ALLOC(&sort, sizeof(int) * nclass); + for (i = 0; i < nclass; i++) + sort[i] = i; + + _sort_desc = desc; + qsort(sort, nclass, sizeof(int), (int (*)(const void *, const void *))sort_desc); + + for (i = 0; i < nclass; i++) + analyze_class(desc[sort[i]]); + + FREE(&sort); +} + +static bool analyze_native_component(const char *path) +{ + lt_dlhandle lib; + GB_DESC **desc; + GB_DESC **desc_opt; + char **include; + bool ret = FALSE; + + if (_verbose) + fprintf(stderr, "Loading native component: %s\n", path); + + //lt_dlopen_flag = RTLD_LAZY; /* | RTLD_GLOBAL;*/ + + lib = lt_dlopenext(path); + if (!lib) + { + error(FALSE, "Cannot load shared library: %s", lt_dlerror()); + return TRUE; + } + + include = lt_dlsym(lib, LIB_INCLUDE); + if (include) + analyze_include(*include); + + desc = lt_dlsym(lib, LIB_CLASS); + desc_opt = lt_dlsym(lib, LIB_OPTIONAL); + + if (desc) + { + analyze_classes(desc); + if (desc_opt) + analyze_classes(desc_opt); + } + else + { + if (_verbose) + warning("cannot find '" LIB_CLASS "' symbol in shared library."); + } + + // Do not close shared libraries, except on openbsd that seems to feel better with + #ifdef OS_OPENBSD + lt_dlclose(lib); + #endif + + return ret; +} + + +static bool analyze_gambas_component(const char *path) +{ + ARCH *arch; + ARCH_FIND find; + bool ret = TRUE; + char *buffer; + char *line; + + if (_verbose) + fprintf(stderr, "Loading gambas component: %s\n", path); + + arch = ARCH_open(path); + + if (!ARCH_find(arch, ".component", 0, &find)) + { + ALLOC(&buffer, find.len + 1); + memcpy(buffer, &arch->addr[find.pos], find.len); + buffer[find.len] = 0; + + for (line = strtok(buffer, "\n"); line; line = strtok(NULL, "\n")) + { + if (strncmp(line, "Include=", 8)) + continue; + analyze_include(&line[8]); + break; + } + + FREE(&buffer); + } + + if (ARCH_find(arch, ".info", 0, &find)) + { + warning("'.info' file not found in component archive."); + goto __RETURN; + } + + fwrite(&arch->addr[find.pos], 1, find.len, out_info); + + if (ARCH_find(arch, ".list", 0, &find)) + { + warning("'.list' file not found in component archive."); + goto __RETURN; + } + + ALLOC(&buffer, find.len + 1); + memcpy(buffer, &arch->addr[find.pos], find.len); + buffer[find.len] = 0; + + for (line = strtok(buffer, "\n"); line; line = strtok(NULL, "\n")) + add_class(line, FALSE); + + FREE(&buffer); + + //fwrite(&arch->addr[find.pos], 1, find.len, out_list); + ret = FALSE; + +__RETURN: + + ARCH_close(arch); + return ret; +} + +#if 0 +static void preload(char **argv, char *lib) +{ +#if DO_PRELOADING + if (_nopreload || getenv("GB_PRELOAD") || !lib || !*lib) + return; + + snprintf(_env, sizeof(_env), "LD_PRELOAD=%s", lib); + putenv(_env); + putenv("GB_PRELOAD=1"); + + execvp(argv[0], argv); +#endif +} +#endif + +static bool find_native_component(const char *name) +{ + snprintf(_buffer, sizeof(_buffer), LIB_PATTERN, _lib_path, name); + return (access(_buffer, F_OK) == 0); +} + +static bool analyze(const char *comp, bool include) +{ + bool native, gambas; + char *name; + char *path_list = NULL; + char *path_info = NULL; + bool ok; + + name = STR_copy(comp); + + if (_verbose) + fprintf(stderr, "%s component %s\n", include ? "Including" : "Analyzing", name); + else if (!include) + puts(name); + + native = find_native_component(name); + + snprintf(_buffer, sizeof(_buffer), ARCH_PATTERN, _lib_path, name); + gambas = (access(_buffer, F_OK) == 0); + + if (!native && !gambas) + { + if (!include || !_no_include_warning) + warning("component %s not found", name); + STR_free(name); + return TRUE; + } + + if (!include) + { + path_info = STR_cat(FILE_cat(_info_path, name, NULL), ".info", NULL); + path_list = STR_cat(FILE_cat(_info_path, name, NULL), ".list", NULL); + + out_info = fopen(path_info, "w"); + if (!out_info) + { + error(FALSE, "Cannot write file: %s", path_info); + return TRUE; + } + + out_list = fopen(path_list, "w"); + if (!out_list) + { + error(FALSE, "Cannot write file: %s", path_list); + return TRUE; + } + + TABLE_create(&_classes, sizeof(SYMBOL), TF_IGNORE_CASE); + } + + fflush(stdout); + ok = TRUE; + + if (native) + { + snprintf(_buffer, sizeof(_buffer), LIB_PATTERN, _lib_path, name); + + if (analyze_native_component(_buffer)) + ok = FALSE; + } + + if (gambas) + { + snprintf(_buffer, sizeof(_buffer), ARCH_PATTERN, _lib_path, name); + + if (analyze_gambas_component(_buffer)) + if (!native) + ok = FALSE; + } + + if (!include) + { + TABLE_delete(&_classes); + + fclose(out_info); + fclose(out_list); + + if (!ok) + { + FILE_unlink(path_info); + FILE_unlink(path_list); + } + else if (_verbose) + { + fprintf(stderr, "Wrote %s\n", path_info); + fprintf(stderr, "Wrote %s\n", path_list); + } + + STR_free(path_info); + STR_free(path_list); + } + + STR_free(name); + + return FALSE; +} + +static void run_myself(const char *path, const char *name) +{ + const char *argv[10]; + int n = 0; + pid_t pid; + int status; + + if (_verbose) + fprintf(stderr, "Running myself for component %s\n", name); + + argv[n++] = path; + if (_verbose) + argv[n++] = "-v"; + if (_nopreload) + argv[n++] = "-p"; + if (_root_set) + { + argv[n++] = "-r"; + argv[n++] = _root; + } + argv[n++] = "-a"; + argv[n++] = name; + argv[n] = NULL; + + if (find_native_component(name)) + { + snprintf(_env, sizeof(_env), "LD_PRELOAD=%s", _buffer); + putenv(_env); + } + + pid = fork(); + switch (pid) + { + case 0: + execvp(path, (char **)argv); + error(FALSE, "Cannot run sub-process: %s", strerror(errno)); + exit(1); + case -1: + error(FALSE, "Cannot run sub-process: %s", strerror(errno)); + exit(1); + default: + waitpid(pid, &status, 0); + } +} + +static void make_component_list() +{ + DIR *dir; + struct dirent *dirent; + const char *name; + + dir = opendir(_lib_path); + if (dir == NULL) + error(TRUE, "Cannot read directory: %s", _lib_path); + + //save_fd = dup(STDOUT_FILENO); + + ARRAY_create(&_components); + + while ((dirent = readdir(dir)) != NULL) + { + name = dirent->d_name; + if (strcmp(FILE_get_ext(name), "component")) + continue; + name = FILE_get_basename(name); + *((char **)ARRAY_add(&_components)) = STR_copy(name); + } + + closedir(dir); + + qsort(_components, ARRAY_count(_components), sizeof(*_components), (int (*)(const void *, const void *))compare_components); +} + +int main(int argc, char **argv) +{ + int i; + char *name; + int opt; + int ind = 0; + + //dup(STDOUT_FILENO); + + //_verbose = TRUE; + + for(;;) + { + #if HAVE_GETOPT_LONG + opt = getopt_long(argc, argv, "vVhLpar:", LongOptions, &ind); + #else + opt = getopt(argc, argv, "vVhLpar:"); + #endif + if (opt < 0) break; + + switch (opt) + { + case 'V': + #ifdef TRUNK_VERSION + #ifdef TRUNK_VERSION_GIT + printf(VERSION " " TRUNK_VERSION "\n"); + #else /* from svn */ + printf(VERSION " r" TRUNK_VERSION "\n"); + #endif + #else /* no TRUNK_VERSION */ + printf(VERSION "\n"); + #endif + exit(0); + + case 'v': + _verbose = TRUE; + break; +#if DO_PRELOADING + case 'p': + _nopreload = TRUE; + break; +#endif + + case 'r': + strncpy(_root, optarg, PATH_MAX); + _root_set = TRUE; + break; + + case 'a': + _analyze = TRUE; + break; + + case 'L': + printf( + "\nGAMBAS Component Informer version " VERSION "\n" + COPYRIGHT + ); + exit(0); + + case 'h': + printf( + "\nGenerate component description files.\n" + "\nUsage: gbi" GAMBAS_VERSION_STRING " [options] [components]\n" + "\nOptions:" + #if HAVE_GETOPT_LONG + "\n" + #if DO_PRELOADING + " -p disable preloading\n" + #endif + " -r --root gives the gambas installation directory\n" + " -V --version display version\n" + " -L --license display license\n" + " -h --help display this help\n" + #else + " (no long options on this system)\n" + #if DO_PRELOADING + " -p disable preloading\n" + #endif + " -r gives the gambas installation directory\n" + " -V display version\n" + " -L display license\n" + " -h display this help\n" + #endif + "\n" + ); + + exit(0); + } + } + + init(); + + if (_analyze) + { + if (_verbose) + { + char *env = getenv("LD_PRELOAD"); + if (env) + fprintf(stderr, "LD_PRELOAD=%s\n", env); + } + + analyze(argv[optind], FALSE); + /*if (strcmp(argv[optind], "gb.qt4") == 0) + analyze("gb.gui", FALSE); + else if (strcmp(argv[optind], "gb.qt4.opengl") == 0) + analyze("gb.gui.opengl", FALSE);*/ + } + else + { + if (optind == argc + #ifdef OS_SOLARIS /* solaris bug ? */ + || optind == 0 + #endif + ) + { + /*preload(argv, + "libqt-mt.so.3 " + "libkdecore.so.4 " + );*/ + + if (_verbose) + { + fprintf(stderr, "component path: %s\n", _lib_path); + fprintf(stderr, "info path: %s\n", _info_path); + } + + make_component_list(); + + for (i = 0; i < ARRAY_count(_components); i++) + { + name = _components[i]; + run_myself(argv[0], name); + STR_free(name); + } + + ARRAY_delete(&_components); + } + else + { + if (!getenv("GB_PRELOAD")) + { + for (ind = optind; ind < argc; ind++) + { + name = argv[ind]; + /*if (strncmp(name, "gb.qt.kde", 9) == 0) + preload(argv, "libqt-mt.so.3 libkdecore.so.4"); + else if (strcmp(name, "gb.qt") == 0 || strncmp(name, "gb.qt.", 6) == 0) + preload(argv, "libqt-mt.so.3");*/ + } + } + + for (ind = optind; ind < argc; ind++) + { + name = argv[ind]; + //analyze(name, FALSE); + run_myself(argv[0], name); + } + } + } + + exit(0); +} + diff --git a/main/gbx/Makefile.am b/main/gbx/Makefile.am new file mode 100644 index 00000000..40c857e1 --- /dev/null +++ b/main/gbx/Makefile.am @@ -0,0 +1,98 @@ +AM_CFLAGS += $(GB_CFLAGS_LTO) -I$(top_srcdir)/share @INTL_INC@ @CONV_INC@ @GBX_THREAD_INC@ @FFI_INC@ +AM_CFLAGS_OPT += $(GB_CFLAGS_LTO) -I$(top_srcdir)/share + +bin_PROGRAMS = gbx3 +noinst_LIBRARIES = libgbx.a +gblib_LTLIBRARIES = gb.la + +libgbx_a_CFLAGS = -DGAMBAS_PATH="\"$(bindir)\"" $(AM_CFLAGS_OPT) + +gbx3_LDADD = @C_LIB@ @GBX_THREAD_LIB@ libgbx.a @MATH_LIB@ @INTL_LIB@ @CONV_LIB@ @GETTEXT_LIB@ @DL_LIB@ @FFI_LIB@ @RT_LIB@ +gbx3_LDFLAGS = @LD_FLAGS@ @GBX_THREAD_LDFLAGS@ @INTL_LDFLAGS@ @CONV_LDFLAGS@ @GETTEXT_LDFLAGS@ @FFI_LDFLAGS@ @RT_LDFLAGS@ +gbx3_CFLAGS = -DGAMBAS_PATH="\"$(bindir)\"" $(AM_CFLAGS) + +gb_la_LIBADD = @C_LIB@ @GBX_THREAD_LIB@ @MATH_LIB@ @INTL_LIB@ @CONV_LIB@ @GETTEXT_LIB@ @DL_LIB@ @FFI_LIB@ @RT_LIB@ +gb_la_LDFLAGS = -module @LD_FLAGS@ @INTL_LDFLAGS@ @CONV_LDFLAGS@ @GETTEXT_LDFLAGS@ @FFI_LDFLAGS@ @RT_LDFLAGS@ +gb_la_CFLAGS = -DGBX_INFO $(AM_CFLAGS) -O0 + +libgbx_a_SOURCES = \ + gb_error.h gb_error.c \ + gbx_split.h gbx_split.c \ + gbx_exec_loop.c \ + gb_hash.c \ + gb_common_check.h gb_common.c \ + gbx_exec.h gbx_exec.c gbx_exec_push.c gbx_exec_enum.c gbx_exec_pop.c gbx_exec_operator.c \ + gbx_string.h gbx_string.c \ + gbx_value.h gbx_value.c + +gbx3_SOURCES = \ + gbx_debug.h gbx_debug.c \ + gbx_jit.h gbx_jit.c gb.jit.h \ + gb_alloc.c gb_array.c \ + gbx_stack.h gbx_stack.c \ + gb_buffer.c gbx_replace.c \ + gb_table.c \ + gb_list.c \ + gbx_type.h gbx_type.c \ + gbx_class_desc.h gbx_class.h gbx_class_init.c gbx_class.c gbx_class_native.c \ + gbx_class_load.c gbx_class_load.h \ + gbx_event.h gbx_event.c \ + gb_file.h gb_file.c \ + gbx_stream.h gbx_stream.c gbx_stream_direct.c gbx_stream_lock.c gbx_stream_buffer.c gbx_stream_memory.c \ + gbx_stream_arch.c gbx_stream_process.c gbx_stream_pipe.c gbx_stream_string.c \ + gbx_project.h gbx_project.c \ + gbx_library.h gbx_library.c \ + gbx_subr.h gbx_subr.c \ + gbx_subr_file.c gbx_subr_string.c gbx_subr_conv.c gbx_subr_time.c gbx_subr_extern.c gbx_subr_misc.c \ + gbx_math.h gbx_math.c \ + gbx_subr_math_temp.h gbx_subr_math.c \ + gbx_subr_test_temp.h gbx_subr_test.c \ + gbx_api.h gbx_api.c \ + gbx_local.h gbx_local.c \ + gbx_regexp.h gbx_regexp.c \ + gbx_archive.h gbx_archive.c \ + gbx_watch.h gbx_watch.c \ + gbx_expression.h gbx_eval.h gbx_eval.c \ + gbx_compare.h gbx_compare.c \ + gbx.c \ + gbx_number.h gbx_number.c \ + gbx_object.h gbx_object.c \ + gbx_variant.h \ + gbx_date.h gbx_date.c \ + gbx_c_class.h gbx_c_class.c \ + gbx_c_collection.h gbx_c_collection.c \ + gbx_c_error.h gbx_c_error.c \ + gbx_c_gambas.h gbx_c_gambas.c \ + gbx_c_file.h gbx_c_file.c \ + gbx_c_application.h gbx_c_application.c \ + gbx_c_system.h gbx_c_system.c \ + gbx_c_array.h gbx_c_array.c \ + gbx_c_process.h gbx_c_process.c \ + gbx_c_string.h gbx_c_string.c \ + gbx_c_observer.h gbx_c_observer.c \ + gbx_c_task.h gbx_c_task.c \ + gbx_component.h gbx_component.c \ + gbx_extern.h gbx_extern.c \ + gbx_c_enum.h gbx_c_enum.c \ + gbx_c_timer.h gbx_c_timer.c \ + gbx_struct.h gbx_struct.c \ + gbx_signal.h gbx_signal.c + +gb_la_SOURCES = \ + gbx_info.h \ + gbx_local.h gbx_compare.h gbx_date.h \ + gbx_c_class.h gbx_c_class.c \ + gbx_c_collection.h gbx_c_collection.c \ + gbx_c_error.h gbx_c_error.c \ + gbx_c_gambas.h gbx_c_gambas.c \ + gbx_c_file.h gbx_c_file.c \ + gbx_c_application.h gbx_c_application.c \ + gbx_c_system.h gbx_c_system.c \ + gbx_c_array.h gbx_c_array.c \ + gbx_c_process.h gbx_c_process.c \ + gbx_c_string.h gbx_c_string.c \ + gbx_c_observer.h gbx_c_observer.c \ + gbx_c_enum.h gbx_c_enum.c \ + gbx_c_timer.h gbx_c_timer.c \ + gbx_c_task.h gbx_c_task.c \ + gbx_class_info.c diff --git a/main/gbx/gb.jit.h b/main/gbx/gb.jit.h new file mode 100644 index 00000000..065757b6 --- /dev/null +++ b/main/gbx/gb.jit.h @@ -0,0 +1,115 @@ +/*************************************************************************** + + gb.jit.h + + (c) 2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_JIT_H +#define __GB_JIT_H + +typedef + union { + int type; + struct { int type; double value; } PACKED _float; + struct { int type; float value; } PACKED _single; + struct { int type; int value; } PACKED _integer; + struct { int type; int64_t value; } PACKED _long; + struct { int type; char *addr; int len; } PACKED _string; + struct { int type; int val[2]; } PACKED _swap; + } + PACKED + JIT_CONSTANT; + +typedef + ushort JIT_PCODE; + +typedef + struct { + unsigned char flag; + unsigned char id; + short value; + } + JIT_CTYPE; + +typedef + struct { + void *next; + GB_VALUE *bp; + GB_VALUE *pp; + void *cp; + char *op; + GB_VALUE *ep; + void *fp; + JIT_PCODE *pc; + JIT_PCODE *ec; + JIT_PCODE *et; + GB_VALUE *gp; + } + JIT_CONTEXT; + +typedef + struct { + GB_VALUE **sp; + JIT_CONTEXT *exec; + GB_VALUE *ret; + bool *exec_debug; + GB_VALUE **exec_super; + void (*debug)(const char *fmt, ...); + JIT_PCODE *(*get_code)(void *func); + void (*throw)(int code, ...) NORETURN; + void (*throw_type)(GB_TYPE want, GB_TYPE got) NORETURN; + JIT_CONSTANT *(*get_constant)(int index); + void *(*get_class_ref)(int index); + void **subr_table; + const char *char_table; + void *(*unborrow)(GB_VALUE *val); + void (*new)(void); + void (*push_array)(ushort code); + void (*pop_array)(ushort code); + void (*conv)(GB_VALUE *value, GB_TYPE type); + void (*push_unknown)(void); + void (*call_unknown)(ushort *pc, GB_VALUE **psp); + void (*pop_unknown)(void); + void (*enum_first)(ushort code, GB_VALUE *local, GB_VALUE *penum); + bool (*enum_next)(ushort code, GB_VALUE *local, GB_VALUE *penum); + int (*find_symbol)(void *symbol, ushort *sort, int n_symbol, size_t s_symbol, int flag, const char *name, int len, const char *prefix); + void (*load_class)(void *class); + void (*load_class_without_init)(void *class); + void *error_current; + void *error_handler; + void (*error_reset)(void *); + void (*error_set_last)(bool); + bool *got_error; + void **event_last; + void (*push_complex)(void); + void (*push_vargs)(void); + void (*pop_vargs)(void); + void (*exec_quit)(ushort code); + void (*push_unknown_event)(bool); + void *(*get_extern)(void *ext); + const char *(*get_position)(void *cp, void *fp, ushort *pc); + void (*release_many)(GB_VALUE *, int); + void *(*static_struct)(void *ref, GB_CLASS type, char *addr); + void *(*static_array)(void *cp, void *ref, GB_CLASS type, char *addr); + void *(*get_array_class)(void *cp, JIT_CTYPE ctype); + } + JIT_INTERFACE; + +#endif diff --git a/main/gbx/gb_alloc.c b/main/gbx/gb_alloc.c new file mode 100644 index 00000000..11edd1c5 --- /dev/null +++ b/main/gbx/gb_alloc.c @@ -0,0 +1,27 @@ +/*************************************************************************** + + gb_alloc.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define PROJECT_EXEC + +#include "gb_alloc_temp.h" + diff --git a/main/gbx/gb_array.c b/main/gbx/gb_array.c new file mode 100644 index 00000000..caaca8d0 --- /dev/null +++ b/main/gbx/gb_array.c @@ -0,0 +1,25 @@ +/*************************************************************************** + + gb_array.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_array_temp.h" + diff --git a/main/gbx/gb_buffer.c b/main/gbx/gb_buffer.c new file mode 100644 index 00000000..99f000e9 --- /dev/null +++ b/main/gbx/gb_buffer.c @@ -0,0 +1,25 @@ +/*************************************************************************** + + gb_buffer.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_buffer_temp.h" + diff --git a/main/gbx/gb_common.c b/main/gbx/gb_common.c new file mode 100644 index 00000000..a9c7514c --- /dev/null +++ b/main/gbx/gb_common.c @@ -0,0 +1,111 @@ +/*************************************************************************** + + gb_common.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __COMMON_C +#define __COMMON_CHECK_C + +#include "gb_common.h" + +#include "gb_common_check.h" + +#include "gb_common_string_temp.h" +#include "gb_common_case_temp.h" +#include "gb_common_buffer_temp.h" +#include "gb_common_swap_temp.h" + +#include "gbx_signal.h" + +sigjmp_buf CHECK_jump; + +static SIGNAL_HANDLER _SIGSEGV_handler; +static SIGNAL_HANDLER _SIGBUS_handler; + +static int _dummy; +static volatile int _got_error; + + +void COMMON_init(void) +{ +} + + +static void signal_error(int sig, siginfo_t *info, void *context) +{ + SIGNAL_previous(sig == SIGSEGV ? &_SIGSEGV_handler : &_SIGBUS_handler, sig, info, context); + _got_error = TRUE; + siglongjmp(CHECK_jump, 1); +} + +void CHECK_enter(void) +{ + _got_error = FALSE; + SIGNAL_install(&_SIGSEGV_handler, SIGSEGV, signal_error); + SIGNAL_install(&_SIGBUS_handler, SIGBUS, signal_error); +} + +void CHECK_leave(void) +{ + SIGNAL_uninstall(&_SIGSEGV_handler, SIGSEGV); + SIGNAL_uninstall(&_SIGBUS_handler, SIGBUS); +} + +bool CHECK_got_error(void) +{ + return _got_error; +} + +bool CHECK_address(void *ptr, ssize_t len) +{ + offset_t i; + + if (len < 0) + return TRUE; + + CHECK_enter(); + if (sigsetjmp(CHECK_jump, TRUE) == 0) + { + for (i = 0; i < len; i += 1024) + _dummy = ((int *)ptr)[i]; + + _dummy = ((int *)ptr)[len >> 2]; + } + CHECK_leave(); + return _got_error; +} + +bool CHECK_strlen(char *ptr, ssize_t *len) +{ + size_t l = 0; + + CHECK_enter(); + if (sigsetjmp(CHECK_jump, TRUE) == 0) + { + l = strlen(ptr); + *len = l; + } + CHECK_leave(); + if (l != (size_t)(int)l) + _got_error = TRUE; + return _got_error; +} + diff --git a/main/gbx/gb_common_check.h b/main/gbx/gb_common_check.h new file mode 100644 index 00000000..e78488f6 --- /dev/null +++ b/main/gbx/gb_common_check.h @@ -0,0 +1,40 @@ +/*************************************************************************** + + gb_common_check.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_COMMON_CHECK_H +#define __GB_COMMON_CHECK_H + +#include +#include + +#ifndef __GB_COMMON_CHECK_C +EXTERN sigjmp_buf CHECK_jump; +#endif + +void CHECK_enter(void); +void CHECK_leave(void); +bool CHECK_got_error(void); +bool CHECK_address(void *ptr, ssize_t len); +bool CHECK_strlen(char *ptr, ssize_t *len); + +#endif diff --git a/main/gbx/gb_error.c b/main/gbx/gb_error.c new file mode 100644 index 00000000..4c073ccf --- /dev/null +++ b/main/gbx/gb_error.c @@ -0,0 +1,767 @@ +/*************************************************************************** + + gb_error.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_ERROR_C + +#include "gb_common.h" +#include +#include + +#include "gb_buffer.h" +#include "gbx_debug.h" +#include "gbx_exec.h" +#include "gbx_api.h" +#include "gbx_stack.h" +#include "gbx_project.h" +#include "gb_error.h" + +//#define DEBUG_ERROR 1 + +ERROR_CONTEXT *ERROR_current = NULL; +ERROR_INFO ERROR_last = { 0 }; +void *ERROR_backtrace = NULL; +ERROR_HANDLER *ERROR_handler = NULL; +#if DEBUG_ERROR +int ERROR_depth = 0; +#endif + +static int _lock = 0; +static char *_print_prefix = NULL; + +static const char *const _message[74] = +{ + /* 0 E_UNKNOWN */ "Unknown error", + /* 1 E_MEMORY */ "Out of memory", + /* 2 E_CLASS */ ".3Cannot load class '&1': &2&3", + /* 3 E_STACK */ "Stack overflow", + /* 4 E_NEPARAM */ "Not enough arguments", + /* 5 E_TMPARAM */ "Too many arguments", + /* 6 E_TYPE */ ".2Type mismatch: wanted &1, got &2 instead", + /* 7 E_OVERFLOW */ "Overflow", + /* 8 E_ILLEGAL */ "Illegal instruction", + /* 9 E_NFUNC */ "Not a function", + /* 10 E_CSTATIC */ ".1Class '&1' is not creatable", + /* 11 E_NSYMBOL */ ".2Unknown symbol '&2' in class '&1'", + /* 12 E_NOBJECT */ "Not an object", + /* 13 E_NULL */ "Null object", + /* 14 E_STATIC */ ".2'&1.&2' is static", + /* 15 E_NREAD */ ".2'&1.&2' is write only", + /* 16 E_NWRITE */ ".2'&1.&2' is read only", + /* 17 E_NPROPERTY */ ".2'&1.&2' is not a property", + /* 18 E_NRETURN */ "No return value", + /* 19 E_MATH */ "Mathematic error", + /* 20 E_ARG */ "Bad argument", + /* 21 E_BOUND */ "Out of bounds", + /* 22 E_NDIM */ "Bad number of dimensions", + /* 23 E_NARRAY */ "Not an array", + /* 24 E_MAIN */ "No startup method", + /* 25 E_NNEW */ "No instantiation method", + /* 26 E_ZERO */ "Division by zero", + /* 27 E_LIBRARY */ ".2Cannot load component '&1': &2", + /* 28 E_EVENT */ ".3Bad event handler in &1.&2(): &3", + /* 29 E_IOBJECT */ "Invalid object", + /* 30 E_ENUM */ "Not an enumeration", + /* 31 E_UCONV */ "Unsupported string conversion", + /* 32 E_CONV */ "Bad string conversion", + /* 33 E_DATE */ "Invalid date", + /* 34 E_BADPATH */ "Invalid path", + /* 35 E_OPEN */ ".2Cannot open file '&1': &2", + /* 36 E_PROJECT */ ".2Bad project file: line &1: &2", + /* 37 E_FULL */ "Device is full", + /* 38 E_EXIST */ "File already exists", /* &1 */ + /* 39 E_EOF */ "End of file", + /* 40 E_FORMAT */ "Bad format string", + /* 41 E_DYNAMIC */ ".2'&1.&2' is not static", + /* 42 E_SYSTEM */ ".2System error #&1: &2", + /* 43 E_ACCESS */ "Access forbidden", + /* 44 E_TOOLONG */ "File name is too long", + /* 45 E_NEXIST */ "File or directory does not exist", /* &1 */ + /* 46 E_DIR */ "File is a directory", /* &1 */ + /* 47 E_READ */ "Read error", + /* 48 E_WRITE */ "Write error", + /* 49 E_NDIR */ ".1Not a directory: &1", + /* 50 E_REGEXP */ ".1Bad regular expression: &1", + /* 51 E_ARCH */ ".2Bad archive: &1: &2", + /* 52 E_REGISTER */ ".1Cannot register class '&1'", + /* 53 E_CLOSED */ "Stream is closed", + /* 54 E_VIRTUAL */ "Bad use of virtual class", + /* 55 E_STOP */ "STOP instruction encountered", + /* 56 E_STRING */ "Too many simultaneous new strings", + /* 57 E_EVAL */ ".1Bad expression: &1", + /* 58 E_LOCK */ "File is locked", + /* 59 E_PARENT */ "No parent class", + /* 60 E_EXTLIB */ ".2Cannot find dynamic library '&1': &2", + /* 61 E_EXTSYM */ ".2Cannot find symbol '&2' in dynamic library '&1'", + /* 62 E_BYREF */ "Argument cannot be passed by reference", + /* 63 E_OVERRIDE */ ".3'&1.&2' is incorrectly overridden in class '&3'", + /* 64 E_NKEY */ "Void key", + /* 65 E_SARRAY */ "Embedded array", + /* 66 E_EXTCB */ ".1Cannot create callback: &1", + /* 67 E_SERIAL */ "Serialization error", + /* 68 E_CHILD */ ".2Cannot run child process: &1&2", + /* 69 E_USER */ "Unknown user or group", + /* 70 E_NEMPTY */ "Directory is not empty", + /* 71 E_UTYPE */ "Unsupported datatype", + /* 72 E_FREEREF */ "Free object referenced", + /* 73 E_ASSERT */ "Assertion failed" +}; + +#if DEBUG_ERROR +void ERROR_debug(const char *msg, ...) +{ + int i; + va_list args; + ERROR_CONTEXT *err; + + va_start(args, msg); + + for (i = 0; i < ERROR_depth; i++) + fprintf(stderr, "- "); + + vfprintf(stderr, msg, args); + + fprintf(stderr, "\t\t\t\t\t\t\t\t\t"); + DEBUG_where(); + err = ERROR_current; + while (err) + { + fprintf(stderr, "[%p] -> ", err); + err = err->prev; + } + fprintf(stderr, "NULL\n"); + + va_end(args); +} +#endif + +void ERROR_lock() +{ + _lock++; +} + + +void ERROR_unlock() +{ + _lock--; +} + +void ERROR_reset(ERROR_INFO *info) +{ + if (!info->code) + return; + + info->code = 0; + if (info->free) + { + STRING_unref(&info->msg); + info->free = FALSE; + } + info->msg = NULL; +} + +static void ERROR_clear() +{ + if (_lock) + { + #if DEBUG_ERROR + ERROR_debug("ERROR_clear: (%p) *LOCKED*\n", ERROR_current); + #endif + return; + } + + #if DEBUG_ERROR + fprintf(stderr, "ERROR_clear: (%p)\n", ERROR_current); + #endif + ERROR_reset(&ERROR_current->info); + STACK_free_backtrace(&ERROR_backtrace); +} + +#if 0 +void ERROR_enter(ERROR_CONTEXT *err) +{ + err->prev = ERROR_current; + err->info.code = 0; + //err->info.free = FALSE; + //err->info.msg = NULL; + //err->info.backtrace = NULL; + + ERROR_current = err; + + #if DEBUG_ERROR + fprintf(stderr, "ERROR_enter: (%p)\n", ERROR_current); + fprintf(stderr, ">> ERROR_enter"); + { + ERROR_CONTEXT *e = err; + while (e) + { + fprintf(stderr, " -> %p", e); + e = e->prev; + } + fprintf(stderr, "\n"); + } + #endif +} +#endif + +#if 0 +void ERROR_leave(ERROR_CONTEXT *err) +{ + if (err->prev == ERROR_LEAVE_DONE) + return; + + #if DEBUG_ERROR + fprintf(stderr, "<< ERROR_leave"); + { + ERROR_CONTEXT *e = err; + while (e) + { + fprintf(stderr, " -> %p", e); + e = e->prev; + } + fprintf(stderr, " : %d %s\n", err->info.code, err->info.msg); + } + #endif + + //if (!err->prev) + // BREAKPOINT(); + + ERROR_current = err->prev; + + if (ERROR_current) + { + #if DEBUG_ERROR + fprintf(stderr, "ERROR_leave: (%p)\n", ERROR_current); + #endif + if (err->info.code) + { + ERROR_reset(&ERROR_current->info); + ERROR_current->info = err->info; + } + } + else + ERROR_reset(&err->info); + + err->prev = ERROR_LEAVE_DONE; + //ERROR_reset(err); +} +#endif + +void ERROR_propagate() +{ + ERROR_HANDLER *ph, *prev; + #if DEBUG_ERROR + ERROR_debug("ERROR_propagate: %p %d %s (ret = %d)\n", ERROR_current, ERROR_current->info.code, ERROR_current->info.msg, ERROR_current->ret); + #endif + + //fprintf(stderr, "ERROR_propagate: %p\n", ERROR_handler); + + if (ERROR_in_catch(ERROR_current)) + ERROR_leave(ERROR_current); + + while (ERROR_handler) + { + ph = ERROR_handler; + if (ERROR_current && ERROR_current->handler == ph) + break; + + //fprintf(stderr, "ERROR_propagate: %p @ %p (%p)\n", ERROR_handler, ERROR_handler->context, ERROR_current); + prev = ph->prev; + (*ph->handler)(ph->arg1, ph->arg2); + ERROR_handler = prev; + } + + longjmp(ERROR_current->env, 1); +} + + + +const char *ERROR_get(void) +{ + /* + if (code > 0 && code < 256) + return strerror(code); + else + return ERROR_Message[code - 256]; + */ + return strerror(errno); +} + +static int get_message_length(const char *pattern, char *arg[], int narg) +{ + int len; + int i; + + len = strlen(pattern) + narg; + for (i = 0; i < narg; i++) + len += strlen(arg[i]); + + if (!EXEC_debug) + len -= narg * 3; + + return len; +} + +void ERROR_define(const char *pattern, char *arg[]) +{ + uchar c; + char *msg = NULL; + int len; + int narg = 0; + + ERROR_clear(); + + if ((intptr_t)pattern >= 0 && (intptr_t)pattern < 256) + { + ERROR_current->info.code = (int)(intptr_t)pattern; + pattern = _message[(int)(intptr_t)pattern]; + if (*pattern == '.') + { + narg = pattern[1] - '0'; + pattern += 2; + } + } + else if ((intptr_t)pattern == E_ABORT) + { + ERROR_current->info.code = E_ABORT; + pattern = ""; + } + else + { + ERROR_current->info.code = E_CUSTOM; + + if (arg) + { + msg = (char *)pattern; + for (;;) + { + c = *msg++; + if (c == 0) + break; + + if (c == '&') + { + c = *msg++; + if (c >= '1' && c <= '4') + { + c -= '0'; + if (c > narg) + narg = c; + } + } + } + } + } + + if (narg) + { + len = get_message_length(pattern, arg, narg); + if (len) + { + msg = STRING_new(NULL, len); + ERROR_current->info.msg = msg; + ERROR_current->info.free = TRUE; + + if (EXEC_debug) + { + int i; + strcpy(msg, pattern); + msg += strlen(pattern); + for (i = 0; i < narg; i++) + { + *msg++ = '|'; + if (arg[i]) + { + strcpy(msg, arg[i]); + msg += strlen(arg[i]); + } + } + } + else + { + for (;;) + { + c = *pattern++; + if (c == 0) + break; + + if (c == '&') + { + c = *pattern++; + if (c >= '1' && c <= '4') + { + c -= '1'; + if (arg[c]) + { + len = strlen(arg[c]); + memcpy(msg, arg[c], len); + msg += len; + } + } + } + else + *msg++ = c; + } + + *msg = 0; + } + + /*fprintf(stderr, "msg: %s\n", ERROR_current->info.msg); + if (strcmp(ERROR_current->info.msg, "Type mismatch: wanted WebView, got Function instead") == 0) + { + BREAKPOINT(); + STRING_watch = ERROR_current->info.msg; + }*/ + } + } + else if (ERROR_current->info.code == E_CUSTOM) + { + if (pattern && *pattern) + { + ERROR_current->info.msg = STRING_new_zero(pattern); + ERROR_current->info.free = TRUE; + } + else + { + ERROR_current->info.msg = (char *)_message[E_UNKNOWN]; + ERROR_current->info.free = FALSE; + } + } + else + { + ERROR_current->info.msg = (char *)pattern; + ERROR_current->info.free = FALSE; + } + + //fprintf(stderr, "ERROR_define: %p %d '%s'\n", ERROR_current, ERROR_current->info.code, ERROR_current->info.msg); + + //STRING_add_char(&ERROR_current->info.msg, 0); + + ERROR_current->info.cp = CP; + ERROR_current->info.fp = FP; + ERROR_current->info.pc = PC; + + #if DEBUG_ERROR + ERROR_debug("ERROR_define: %s\n", ERROR_current->info.msg); + #endif +} + +void THROW(int code, ...) +{ + va_list args; + int i; + char *arg[4]; + + va_start(args, code); + + for (i = 0; i < 4; i++) + arg[i] = va_arg(args, char *); + + ERROR_define((char *)(intptr_t)code, arg); + + va_end(args); + + PROPAGATE(); +} + +void THROW_NULL(void) +{ + THROW(E_NULL); +} + +void THROW_CLASS(void *class, char *arg1, char *arg2) +{ + THROW(E_CLASS, CLASS_get_name((CLASS *)class), arg1, arg2); +} + +void THROW_ILLEGAL(void) +{ + THROW(E_ILLEGAL); +} + +void THROW_STACK(void) +{ + #if DEBUG_STACK + fprintf(stderr, "THROW STACK!\n"); + #endif + THROW(E_STACK); +} + +void THROW_SYSTEM(int err, const char *path) +{ + char buf[6]; + + switch(err) + { + case ENOENT: + THROW(E_NEXIST, path); + + case EISDIR: + THROW(E_DIR, path); + + case ENOTDIR: + THROW(E_NDIR, path); + + case ENOMEM: + THROW(E_MEMORY); + + case EACCES: + THROW(E_ACCESS); + + case ENAMETOOLONG: + THROW(E_TOOLONG); + + case ENOSPC: + THROW(E_FULL); + + case EEXIST: + THROW(E_EXIST, path); + + default: + sprintf(buf, "%d", err); + THROW(E_SYSTEM, buf, strerror(err)); + } +} + + +void ERROR_fatal(const char *error, ...) +{ + va_list args; + + va_start(args, error); + fputs(EXEC_arch ? "gbr" GAMBAS_VERSION_STRING : "gbx" GAMBAS_VERSION_STRING, stderr); + fputs(": ", stderr); + vfprintf(stderr, error, args); + va_end(args); + putc('\n', stderr); + _exit(1); +} + +void ERROR_panic(const char *error, ...) +{ + va_list args; + + va_start(args, error); + + fflush(NULL); + + fprintf(stderr, "\n** \n** OOPS! INTERNAL ERROR. Program aborting, sorry! :-(\n** "); + vfprintf(stderr, error, args); + + va_end(args); + + fputc('\n', stderr); + + if (ERROR_current->info.code) + { + fprintf(stderr, "** \n"); + _print_prefix = "** "; + ERROR_print(); + _print_prefix = NULL; + } + fprintf(stderr, "** \n** Please send a bug report to the gambas bugtracker [1] or to the gambas mailing-list [2].\n** [1] http://gambaswiki.org/bugtracker\n** [2] https://lists.gambas-basic.org/listinfo/user\n** \n\n"); + _exit(1); +} + + +static void print_prefix(FILE *where) +{ + if (_print_prefix) fputs(_print_prefix, where); +} + +void ERROR_print_at(FILE *where, bool msgonly, bool newline) +{ + if (!ERROR_current->info.code) + return; + + print_prefix(where); + + if (!msgonly) + { + if (ERROR_current->info.cp && ERROR_current->info.fp && ERROR_current->info.pc) + fprintf(where, "%s: ", DEBUG_get_position(ERROR_current->info.cp, ERROR_current->info.fp, ERROR_current->info.pc)); + else + fprintf(where, "ERROR: "); + /*if (ERROR_current->info.code > 0 && ERROR_current->info.code < 256) + fprintf(where, "%ld:", ERROR_current->info.code);*/ + if (ERROR_current->info.code > 0) + fprintf(where, "#%d: ", ERROR_current->info.code); + if (ERROR_current->info.msg) + fprintf(where, "%s", ERROR_current->info.msg); + } + else + { + char *p = ERROR_current->info.msg; + unsigned char c; + + if (p) + { + while ((c = *p++)) + { + if (c < ' ') c = ' '; + fputc(c, where); + } + } + } + + if (newline) + fputc('\n', where); +} + +void ERROR_print(void) +{ + static bool lock = FALSE; + + if (EXEC_main_hook_done && !EXEC_debug && EXEC_Hook.error && !lock) + { + lock = TRUE; + GAMBAS_DoNotRaiseEvent = TRUE; + HOOK(error)(ERROR_current->info.code, ERROR_current->info.msg, DEBUG_get_position(ERROR_current->info.cp, ERROR_current->info.fp, ERROR_current->info.pc)); + lock = FALSE; + } + + ERROR_print_at(stderr, FALSE, TRUE); + + if (ERROR_backtrace) + { + print_prefix(stderr); + DEBUG_print_backtrace(ERROR_backtrace); + } +} + +static void ERROR_copy(ERROR_INFO *save, ERROR_INFO *last) +{ + ERROR_reset(save); + *save = ERROR_current->info; + + if (last) + { + ERROR_reset(last); + *last = ERROR_last; + } +} + +void ERROR_save(ERROR_INFO *save, ERROR_INFO *last) +{ + ERROR_copy(save, last); + + CLEAR(&ERROR_current->info); + if (last) + CLEAR(&ERROR_last); +} + +void ERROR_restore(ERROR_INFO *save, ERROR_INFO *last) +{ + ERROR_reset(&ERROR_current->info); + ERROR_current->info = *save; + CLEAR(save); + + if (last) + { + ERROR_reset(&ERROR_last); + ERROR_last = *last; + CLEAR(last); + } +} + +void ERROR_set_last(bool bt) +{ + ERROR_reset(&ERROR_last); + ERROR_last = ERROR_current->info; + if (ERROR_last.free) + STRING_ref(ERROR_last.msg); + + if (bt && !ERROR_backtrace) + ERROR_backtrace = STACK_get_backtrace(); +} + +void ERROR_define_last(void) +{ + ERROR_reset(&ERROR_current->info); + ERROR_current->info = ERROR_last; + if (ERROR_last.free) + STRING_ref(ERROR_last.msg); +} + +void ERROR_warning(const char *warning, ...) +{ + va_list args; + + va_start(args, warning); + + fflush(NULL); + + fprintf(stderr, "gbx" GAMBAS_VERSION_STRING ": warning: "); + vfprintf(stderr, warning, args); + + va_end(args); + + putc('\n', stderr); +} + +/*void ERROR_deprecated(const char *msg) +{ + ERROR_warning("%s: %s is deprecated.", DEBUG_get_current_position(), msg); +}*/ + +void ERROR_exit(void) +{ + ERROR_reset(&ERROR_last); + STACK_free_backtrace(&ERROR_backtrace); +} + +void ERROR_hook(void) +{ + static bool no_rec = FALSE; + + ERROR_INFO save = { 0 }; + ERROR_INFO last = { 0 }; + CLASS_DESC_METHOD *handle_error; + + if (no_rec) + return; + + if (PROJECT_class && CLASS_is_loaded(PROJECT_class)) + { + handle_error = (CLASS_DESC_METHOD *)CLASS_get_symbol_desc_kind(PROJECT_class, "Application_Error", CD_STATIC_METHOD, 0); + + if (handle_error) + { + no_rec = TRUE; + ERROR_save(&save, &last); + + TRY + { + EXEC_public_desc(PROJECT_class, NULL, handle_error, 0); + } + CATCH + { + ERROR_save(&save, &last); + } + END_TRY + + ERROR_restore(&save, &last); + no_rec = FALSE; + } + } +} + diff --git a/main/gbx/gb_error.h b/main/gbx/gb_error.h new file mode 100644 index 00000000..c4bc07d3 --- /dev/null +++ b/main/gbx/gb_error.h @@ -0,0 +1,186 @@ +/*************************************************************************** + + gb_error.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_ERROR_H +#define __GB_ERROR_H + +#include +#include + +#include "gb_common.h" +#include "gb_limit.h" + +//#define DEBUG_ERROR 1 + +#include "gb_error_common.h" + +enum { + E_ABORT = -2, + E_CUSTOM = -1, + E_UNKNOWN = 0, + E_MEMORY, + E_CLASS, + E_STACK, + E_NEPARAM, + E_TMPARAM, + E_TYPE, + E_OVERFLOW, + E_ILLEGAL, + E_NFUNC, + E_CSTATIC, + E_NSYMBOL, + E_NOBJECT, + E_NULL, + E_STATIC, + E_NREAD, + E_NWRITE, + E_NPROPERTY, + E_NRETURN, + E_MATH, + E_ARG, + E_BOUND, + E_NDIM, + E_NARRAY, + E_MAIN, + E_NNEW, + E_ZERO, + E_LIBRARY, + E_EVENT, + E_IOBJECT, + E_ENUM, + E_UCONV, + E_CONV, + E_DATE, + E_BADPATH, + E_OPEN, + E_PROJECT, + E_FULL, + E_EXIST, + E_EOF, + E_FORMAT, + E_DYNAMIC, + E_SYSTEM, + E_ACCESS, + E_TOOLONG, + E_NEXIST, + E_DIR, + E_READ, + E_WRITE, + E_NDIR, + E_REGEXP, + E_ARCH, + E_REGISTER, + E_CLOSED, + E_VIRTUAL, + E_STOP, + E_STRING, + E_EVAL, + E_LOCK, + E_PARENT, + E_EXTLIB, + E_EXTSYM, + E_BYREF, + E_OVERRIDE, + E_VKEY, + E_SARRAY, + E_EXTCB, + E_SERIAL, + E_CHILD, + E_USER, + E_NEMPTY, + E_UTYPE, + E_FREEREF, + E_ASSERT + }; + +#ifndef __GB_ERROR_C + +EXTERN ERROR_CONTEXT *ERROR_current; +EXTERN ERROR_INFO ERROR_last; +EXTERN ERROR_HANDLER *ERROR_handler; +EXTERN void *ERROR_backtrace; + +#if DEBUG_ERROR +EXTERN int ERROR_depth; + +void ERROR_debug(const char *msg, ...); +#endif + +#endif + +#define ON_ERROR(_handler) \ + { \ + ERROR_HANDLER __handler; \ + __handler.handler = (_handler); \ + __handler.prev = ERROR_handler; \ + __handler.context = ERROR_current; \ + ERROR_handler = &__handler; + //fprintf(stderr, "%s.%d: ERROR_handler -> %p @ %p\n", __FUNCTION__, __LINE__, ERROR_handler, ERROR_current); + +#define ON_ERROR_1(_handler, _arg1) \ + ON_ERROR(_handler) \ + __handler.arg1 = (intptr_t)(_arg1); + +#define ON_ERROR_2(_handler, _arg1, _arg2) \ + ON_ERROR_1(_handler, _arg1) \ + __handler.arg2 = (intptr_t)(_arg2); + +#define END_ERROR \ + ERROR_handler = __handler.prev; \ + } + //fprintf(stderr, "%s.%d: ERROR_handler <- %p @ %p (%p)\n", __FUNCTION__, __LINE__, ERROR_handler, ERROR_handler ? ERROR_handler->context : NULL, ERROR_current); + +const char *ERROR_get(void); + +void ERROR_define(const char *pattern, char *arg[]); + +void ERROR_propagate(void) NORETURN; + +void THROW(int code, ...) NORETURN; +void THROW_SYSTEM(int err, const char *path); +void THROW_NULL(void) NORETURN; +void THROW_ILLEGAL(void) NORETURN; +void THROW_STACK(void) NORETURN; +void THROW_CLASS(void *class, char *arg1, char *arg2) NORETURN; + +void ERROR_fatal(const char *error, ...) NORETURN; +void ERROR_panic(const char *error, ...) NORETURN; +void ERROR_warning(const char *warning, ...); + +void ERROR_print(void); +void ERROR_print_at(FILE *where, bool msgonly, bool newline); +void ERROR_hook(void); + +void ERROR_save(ERROR_INFO *save, ERROR_INFO *last); +void ERROR_restore(ERROR_INFO *save, ERROR_INFO *last); + +void ERROR_reset(ERROR_INFO *info); +void ERROR_lock(void); +void ERROR_unlock(void); +void ERROR_set_last(bool bt); +void ERROR_define_last(void); +//void ERROR_deprecated(const char *msg); + +void ERROR_exit(void); + +#endif diff --git a/main/gbx/gb_file.c b/main/gbx/gb_file.c new file mode 100644 index 00000000..ace4f98b --- /dev/null +++ b/main/gbx/gb_file.c @@ -0,0 +1,38 @@ +/*************************************************************************** + + gb_file.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" +#include "gb_error.h" + +#include "gbx_value.h" +#include "gbx_exec.h" +#include "gbx_project.h" +#include "gambas.h" +#include "gbx_regexp.h" +#include "gbx_string.h" +#include "gbx_archive.h" +#include "gbx_stream.h" + +#define PROJECT_EXEC + +#include "gb_file_temp.h" diff --git a/main/gbx/gb_file.h b/main/gbx/gb_file.h new file mode 100644 index 00000000..9307371f --- /dev/null +++ b/main/gbx/gb_file.h @@ -0,0 +1,26 @@ +/*************************************************************************** + + gb_file.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define PROJECT_EXEC + +#include "gb_file_share.h" diff --git a/main/gbx/gb_hash.c b/main/gbx/gb_hash.c new file mode 100644 index 00000000..05c491eb --- /dev/null +++ b/main/gbx/gb_hash.c @@ -0,0 +1,24 @@ +/*************************************************************************** + + gb_hash.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_hash_temp.h" diff --git a/main/gbx/gb_list.c b/main/gbx/gb_list.c new file mode 100644 index 00000000..34557b46 --- /dev/null +++ b/main/gbx/gb_list.c @@ -0,0 +1,24 @@ +/*************************************************************************** + + gb_list.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_list_temp.h" diff --git a/main/gbx/gb_table.c b/main/gbx/gb_table.c new file mode 100644 index 00000000..750592da --- /dev/null +++ b/main/gbx/gb_table.c @@ -0,0 +1,28 @@ +/*************************************************************************** + + gb_table.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define PROJECT_EXEC + +#include "gb_common_case.h" +#include "gb_table_temp.h" + diff --git a/main/gbx/gbx.c b/main/gbx/gbx.c new file mode 100644 index 00000000..1d8e6e6b --- /dev/null +++ b/main/gbx/gbx.c @@ -0,0 +1,485 @@ +/*************************************************************************** + + gbx.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C + +#include "config.h" +#include "../trunk_version.h" + +//#define USE_PROFILE 1 + +#include "gb_common.h" +#include "gb_alloc.h" +#include "gb_error.h" + +#include +#include +#include +#include + +#include "gbx_class.h" +#include "gbx_exec.h" +#include "gbx_stack.h" +#include "gbx_debug.h" +#include "gb_file.h" +#include "gbx_component.h" +#include "gbx_project.h" +#include "gbx_local.h" +#include "gbx_watch.h" +#include "gbx_event.h" +#include "gbx_extern.h" +#include "gbx_eval.h" +#include "gbx_subr.h" +#include "gbx_math.h" +#include "gb_common_buffer.h" +#include "gbx_api.h" +#include "gbx_signal.h" +#include "gbx_jit.h" + +#if USE_PROFILE +#include "gbx_profile.h" +#endif + +#include "gbx_c_file.h" +#include "gbx_c_application.h" + +extern void _exit(int) NORETURN; +FILE *log_file; + +static bool _welcome = FALSE; +static bool _quit_after_main = FALSE; +static bool _eval = FALSE; + +static void NORETURN my_exit(int ret) +{ + LOCAL_exit(); + COMPONENT_exit(); + EXTERN_exit(); + //fclose(log_file); + exit(ret); +} + +static void init(const char *file, int argc, char **argv) +{ + COMPONENT_init(); + FILE_init(); + + EXEC_init(); + CLASS_init(); + CFILE_init(); + WATCH_init(); + MATH_init(); + PROJECT_init(file); + DEBUG_init(); + + LOCAL_init(); + + if (file) + { + PROJECT_load(); + + if (PROJECT_run_httpd) + COMPONENT_exec("gb.httpd", argc, argv); + + PROJECT_load_finish(); + } + else + STACK_init(); + + if (EXEC_debug) + { + DEBUG.Welcome(); + DEBUG.Main(FALSE); + } + _welcome = TRUE; +} + + +static void main_exit(bool silent) +{ + silent |= EXEC_task; + + // If the stack has not been initialized because the project could not be started, do it now + if (!SP) + STACK_init(); + + TRY + { + SIGNAL_exit(); + EXTERN_release(); + STREAM_exit(); + OBJECT_exit(); + CFILE_exit(); + + CLASS_clean_up(silent); + + SUBR_exit(); + DEBUG_exit(); + WATCH_exit(); + #if USE_PROFILE + PROFILE_exit(); + #endif + CLASS_exit(); + COMPONENT_exit(); + EXTERN_exit(); + PROJECT_exit(); + LOCAL_exit(); + EVENT_exit(); + FILE_exit(); + STACK_exit(); + JIT_exit(); + ERROR_exit(); + } + CATCH + { + if (!silent) + ERROR_print_at(stderr, _eval, TRUE); + _exit(1); + } + END_TRY + + STRING_exit(); +} + +static bool is_option(const char *arg, char option) +{ + return (arg[0] == '-' && arg[1] == option && arg[2] == 0); +} + +static bool is_option_arg(char **argv, int argc, int *i, char option, const char **param) +{ + if (is_option(argv[*i], option)) + { + if (*i < (argc - 1) && *argv[*i + 1] && *argv[*i + 1] != '-') + { + *param = argv[*i + 1]; + (*i)++; + } + else + *param = NULL; + + return TRUE; + } + else + return FALSE; +} + + +static bool is_long_option(const char *arg, char option, char *long_option) +{ + if (is_option(arg, option)) + return TRUE; + else + return (arg[0] == '-' && arg[1] == '-' && !strcmp(&arg[2], long_option)); +} + + +int main(int argc, char *argv[]) +{ + CLASS_DESC_METHOD *startup = NULL; + int i, n; + char *file = NULL; + int ret = 0; + const char *redirect_stderr = NULL; + + //char log_path[256]; + //sprintf(log_path, "/tmp/gambas-%d.log", getuid()); + //log_file = freopen(log_path, "w+", stderr); + //fprintf(stderr, "Fichier log Gambas\n"); + + /*struct rlimit rl = { 64000000, 64000000 }; + if (setrlimit(RLIMIT_CORE, &rl)) + perror(strerror(errno));*/ + + MEMORY_init(); + COMMON_init(); + //STRING_init(); + + EXEC_arch = (strcmp(FILE_get_name(argv[0]), "gbr" GAMBAS_VERSION_STRING) == 0); + + if (argc == 2) + { + if (is_long_option(argv[1], 'h', "help")) + { + if (EXEC_arch) + { + printf( + "\nExecute a Gambas executable.\n" + "\nUsage: gbr" GAMBAS_VERSION_STRING " [options] []\n\n" + ); + } + else + { + printf( + "\nExecute a Gambas project or evaluate a Gambas expression (-e option).\n" + "\nUsage: gbx" GAMBAS_VERSION_STRING " [options] [] [-- ]\n" + " gbx" GAMBAS_VERSION_STRING " -e \n\n" + ); + } + printf( + "Options:\n" + " -g enter debugging mode\n" + " -s override startup class\n" + " -p activate profiling and debugging mode\n" + " -k do not unload shared libraries\n" + " -H --httpd run through an embedded http server\n" + " -j disable just-in-time compiler\n" + ); + + if (!EXEC_arch) + printf(" -e evaluate an expression\n"); + + printf( + " -V --version display version\n" + " -L --license display license\n" + " -h --help display this help\n" + "\n" + ); + + my_exit(0); + } + else if (is_long_option(argv[1], 'V', "version")) + { +#ifdef TRUNK_VERSION +#ifdef TRUNK_VERSION_GIT + printf(VERSION " " TRUNK_VERSION "\n"); +#else /* from svn */ + printf(VERSION " r" TRUNK_VERSION "\n"); +#endif +#else /* no TRUNK_VERSION */ + printf(VERSION "\n"); +#endif + my_exit(0); + } + else if (is_long_option(argv[1], 'L', "license")) + { + printf( + "\nGambas interpreter version " VERSION "\n" + COPYRIGHT + ); + my_exit(0); + } + } + + if (!EXEC_arch && argc >= 2 && is_option(argv[1], 'e')) + { + if (argc < 3) + ERROR_fatal("-e option needs an expression."); + + _eval = TRUE; + + TRY + { + init(NULL, argc, argv); + EVAL_string(argv[2]); + } + CATCH + { + if (ERROR_current->info.code && ERROR_current->info.code != E_ABORT) + ERROR_print_at(stderr, TRUE, TRUE); + main_exit(TRUE); + _exit(1); + } + END_TRY + + main_exit(FALSE); + _exit(0); + } + + for (i = 1; i < argc; i++) + { + if (is_option(argv[i], 'g')) + { + EXEC_debug = TRUE; + } + else if (is_option_arg(argv, argc, &i, 'p', &EXEC_profile_path)) + { + EXEC_debug = TRUE; + EXEC_profile = TRUE; + } + else if (is_option_arg(argv, argc, &i, 'f', &EXEC_fifo_name)) + { + EXEC_fifo = TRUE; + } + else if (is_option_arg(argv, argc, &i, 's', &PROJECT_startup)) + { + continue; + } + else if (is_option_arg(argv, argc, &i, 't', &redirect_stderr)) + { + int fd = open(redirect_stderr, O_WRONLY | O_CLOEXEC); + if (fd < 0) + ERROR_fatal("cannot redirect stderr."); + dup2(fd, STDERR_FILENO); + } + else if (is_option(argv[i], 'k')) + { + EXEC_keep_library = TRUE; + } + else if (is_option(argv[i], 'q')) + { + _quit_after_main = TRUE; + } + else if (is_long_option(argv[i], 'H', "httpd")) + { + PROJECT_run_httpd = TRUE; + } + else if (is_option(argv[i], 'j')) + { + JIT_disabled = TRUE; + } + else if (is_option(argv[i], '-')) + { + i++; + break; + } + else + { + if (file) + ERROR_fatal("too many %s.", EXEC_arch ? "executable files" : "project directories"); + + file = argv[i]; + + if (EXEC_arch) + { + i++; + break; + } + } + } + + n = i; + if (!file) + file = "."; + + if (EXEC_arch) + argv[0] = file; + + for (i = 1; i <= (argc - n); i++) + argv[i] = argv[i + n - 1]; + + argc -= n - 1; + + TRY + { + init(file, argc, argv); + + if (!EXEC_arch) + argv[0] = PROJECT_name; + + HOOK(main)(&argc, &argv); + EXEC_main_hook_done = TRUE; + + /* Startup class */ + CLASS_load(PROJECT_class); + startup = (CLASS_DESC_METHOD *)CLASS_get_symbol_desc_kind(PROJECT_class, "main", CD_STATIC_METHOD, 0); + if (startup == NULL) + THROW(E_MAIN); + + //CAPP_init(); /* needs startup class */ + CFILE_init_watch(); + + PROJECT_argc = argc; + PROJECT_argv = argv; + } + CATCH + { + ERROR_hook(); + + if (EXEC_debug && DEBUG_is_init()) + { + if (!_welcome) + DEBUG.Main(TRUE); + DEBUG.Main(TRUE); + main_exit(FALSE); + _exit(0); + } + else + { + if (ERROR->info.code && ERROR->info.code != E_ABORT) + ERROR_print(); + main_exit(TRUE); + _exit(1); + } + } + END_TRY + + TRY + { + EXEC_public_desc(PROJECT_class, NULL, startup, 0); + + if (TYPE_is_boolean(startup->type)) + ret = RP->_boolean.value ? 1 : 0; + else if (TYPE_is_integer(startup->type)) + ret = RP->_integer.value & 0xFF; + + EXEC_release_return_value(); + + if (_quit_after_main) + { + main_exit(TRUE); + _exit(0); + } + + if (!ret) + { + DEBUG_enter_event_loop(); + HOOK_DEFAULT(loop, WATCH_loop)(); + DEBUG_leave_event_loop(); + EVENT_check_post(); + } + } + CATCH + { + if (ERROR->info.code && ERROR->info.code != E_ABORT) + { + ERROR_hook(); + + if (EXEC_debug) + { + DEBUG.Main(TRUE); + main_exit(TRUE); + _exit(0); + } + else + { + ERROR_print(); + main_exit(TRUE); + _exit(1); + } + } + + ret = EXEC_quit_value; + } + END_TRY + + main_exit(FALSE); + + if (!EXEC_task) + MEMORY_exit(); + + fflush(NULL); + + return ret; +} + diff --git a/main/gbx/gbx_api.c b/main/gbx/gbx_api.c new file mode 100644 index 00000000..31ceb8bf --- /dev/null +++ b/main/gbx/gbx_api.c @@ -0,0 +1,2461 @@ +/*************************************************************************** + + gbx_api.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_API_C + +#include "gb_common.h" +#include "gb_common_case.h" +#include "gb_common_buffer.h" +#include "gb_error.h" +#include "gb_alloc.h" +#include "gb_list.h" + +#include + +#include "gbx_class.h" +#include "gbx_exec.h" +#include "gbx_event.h" +#include "gbx_stack.h" +#include "gbx_stream.h" +#include "gbx_library.h" +#include "gbx_watch.h" +#include "gbx_project.h" +#include "gbx_eval.h" +#include "gbx_local.h" +#include "gb_hash.h" +#include "gb_file.h" +#include "gbx_number.h" +#include "gbx_object.h" +#include "gbx_string.h" +#include "gbx_date.h" +#include "gbx_regexp.h" +#include "gbx_c_array.h" +#include "gbx_c_timer.h" +#include "gbx_component.h" +#include "gbx_c_gambas.h" +#include "gbx_c_observer.h" +#include "gbx_debug.h" +#include "gbx_c_file.h" +#include "gbx_extern.h" +#include "gbx_compare.h" +#include "gbx_subr.h" +#include "gbx_math.h" +#include "gbx_struct.h" +#include "gbx_signal.h" +#include "gbx_jit.h" + +#include "gambas.h" +#include "gbx_api.h" + +typedef + struct { + OBJECT *object; + CLASS_DESC_METHOD *desc; + } + GB_API_FUNCTION; + +const void *const GAMBAS_Api[] = +{ + (void *)GB_VERSION, + + (void *)GB_GetInterface, + + (void *)GB_Hook, + + (void *)GB_LoadComponent, + (void *)COMPONENT_can_load_library, + (void *)COMPONENT_exist, + (void *)COMPONENT_is_loaded, + (void *)GB_CurrentComponent, + (void *)COMPONENT_get_info, + (void *)COMPONENT_signal, + (void *)LIBRARY_declare_one, + + (void *)GB_Push, + (void *)GB_GetFunction, + (void *)GB_Call, + (void *)GB_GetClassInterface, + (void *)GB_GetProperty, + (void *)GB_SetProperty, + (void *)GB_Serialize, + (void *)GB_UnSerialize, + + (void *)WATCH_one_loop, + (void *)GB_Wait, + (void *)EVENT_post, + (void *)EVENT_post2, + (void *)CTIMER_every, + (void *)GB_Raise, + (void *)GB_RaiseBegin, + (void *)GB_RaiseEnd, + (void *)EVENT_post_event, + (void *)EVENT_check_post, + (void *)GB_CanRaise, + (void *)GB_GetEvent, + (void *)GB_GetLastEventName, + (void *)CTIMER_raise, + (void *)GB_Stopped, + + (void *)GB_NParam, + (void *)GB_Conv, + (void *)GB_GetUnknown, + + (void *)GB_Error, + (void *)ERROR_propagate, + (void *)GB_Deprecated, + (void *)GB_OnErrorBegin, + (void *)GB_OnErrorEnd, + + (void *)GB_GetClass, + (void *)GB_GetClassName, + (void *)GB_ExistClass, + (void *)CLASS_find_global, + (void *)GB_ExistClassLocal, + (void *)CLASS_find, + (void *)GB_GetArrayType, + (void *)CLASS_get_array_class, + (void *)CLASS_find_load_from, + (void *)GB_Is, + (void *)GB_Ref, + (void *)GB_Unref, + //(void *)GB_UnrefKeep, + (void *)GB_Detach, + (void *)GB_Attach, + (void *)OBJECT_parent, + (void *)GB_Create, + (void *)GB_New, + (void *)CLASS_auto_create, + (void *)GB_CheckObject, + + (void *)GB_GetEnum, + (void *)GB_StopEnum, + (void *)GB_BeginEnum, + (void *)GB_EndEnum, + (void *)GB_NextEnum, + (void *)GB_StopAllEnum, + + (void *)GB_GetReturnValue, + (void *)GB_Return, + (void *)GB_ReturnInteger, + (void *)GB_ReturnLong, + (void *)GB_ReturnPointer, + (void *)GB_ReturnBoolean, + (void *)GB_ReturnDate, + (void *)GB_ReturnObject, + (void *)GB_ReturnNull, + (void *)GB_ReturnSingle, + (void *)GB_ReturnFloat, + (void *)GB_ReturnVariant, + (void *)GB_ReturnConvVariant, + (void *)GB_ReturnBorrow, + (void *)GB_ReturnRelease, + (void *)GB_ReturnPtr, + (void *)GB_ReturnSelf, + + (void *)GB_ReturnString, + (void *)GB_ReturnVoidString, + (void *)GB_ReturnConstString, + (void *)GB_ReturnConstZeroString, + (void *)GB_ReturnNewString, + (void *)GB_ReturnNewZeroString, + + (void *)STRING_new, + (void *)GB_NewZeroString, + (void *)GB_TempString, + (void *)GB_RefString, + (void *)GB_FreeString, + (void *)STRING_free_later, + (void *)STRING_extend, + (void *)STRING_add, + (void *)STRING_add_char, + (void *)GB_StringLength, + (void *)GB_ToZeroString, + (void *)REGEXP_match, + (void *)NUMBER_from_string, + (void *)GB_NumberToString, + (void *)LOCAL_gettext, + + (void *)STRING_subst, + (void *)STRING_subst_add, + (void *)STRING_make, + (void *)GB_ConvString, + (void *)STRING_conv_file_name, + (void *)GB_RealFileName, + + (void *)GB_LoadFile, + (void *)STREAM_unmap, + (void *)GB_TempDir, + (void *)GB_TempFile, + (void *)GB_CopyFile, + (void *)GB_BrowseProject, + (void *)GB_BrowseDirectory, + (void *)GB_StatFile, + + (void *)GB_Store, + (void *)GB_StoreString, + (void *)GB_StoreObject, + (void *)GB_StoreVariant, + (void *)VALUE_read, + (void *)GB_BorrowValue, + (void *)GB_ReleaseValue, + (void *)COMPARE_variant, + + (void *)GB_SplitDate, + (void *)GB_MakeDate, + (void *)DATE_from_time, + (void *)DATE_timer, + + (void *)GB_Watch, + + (void *)GB_Eval, + + (void *)GB_Alloc, + (void *)GB_AllocZero, + (void *)GB_Free, + (void *)GB_Realloc, + + (void *)GB_NewArray, + (void *)ARRAY_delete, + (void *)GB_CountArray, + (void *)GB_Add, + (void *)ARRAY_insert_many, + (void *)ARRAY_remove_many, + + (void *)GB_tolower, + (void *)GB_toupper, + (void *)strcasecmp, + (void *)strncasecmp, + + (void *)GB_AppName, + (void *)GB_AppTitle, + (void *)GB_AppVersion, + (void *)GB_AppPath, + (void *)GB_AppStartupClass, + + (void *)GB_SystemCharset, + (void *)LOCAL_get_lang, + (void *)LOCAL_set_lang, + (void *)GB_SystemDomainName, + (void *)GB_IsRightToLeft, + (void *)GB_SystemPath, + (void *)GB_SystemHasForked, + (void *)GB_SystemDebug, + (void *)FILE_get_home, + (void *)DATE_get_timezone, + + (void *)GB_ArrayNew, + (void *)GB_ArrayCount, + (void *)GB_ArrayAdd, + (void *)GB_ArrayGet, + (void *)GB_ArrayType, + + (void *)GB_CollectionNew, + (void *)GB_CollectionCount, + (void *)GB_CollectionSet, + (void *)GB_CollectionGet, + (void *)GB_CollectionEnum, + + (void *)GB_HashTableNew, + (void *)HASH_TABLE_delete, + (void *)HASH_TABLE_size, + (void *)GB_HashTableAdd, + (void *)GB_HashTableRemove, + (void *)GB_HashTableGet, + (void *)GB_HashTableEnum, + (void *)GB_HashTableFirst, + + (void *)GB_StreamGet, + (void *)GB_StreamSetSwapping, + (void *)GB_StreamSetAvailableNow, + (void *)GB_StreamBlock, + (void *)GB_StreamRead, + (void *)GB_StreamWrite, + (void *)STREAM_get_readable, + (void *)STREAM_eof, + (void *)STREAM_handle, + + (void *)STRING_start_len, + (void *)STRING_end, + (void *)STRING_make, + + (void *)DEBUG_get_current_position, + (void *)DEBUG_enter_event_loop, + (void *)DEBUG_leave_event_loop, + + (void *)SIGNAL_register, + (void *)SIGNAL_unregister, + + (void *)LIST_insert, + (void *)LIST_remove, + + NULL +}; + +const void *const GAMBAS_DebugApi[] = +{ + (void *)GB_DebugGetExec, + (void *)STACK_get_frame, + (void *)ERROR_print_at, + (void *)ERROR_save, + (void *)ERROR_restore, + (void *)VALUE_to_string, + (void *)LOCAL_format_date, + (void *)LOCAL_format_number, + (void *)DEBUG_get_value, + (void *)DEBUG_set_value, + (void *)CARRAY_get_value, + (void *)DEBUG_enum_keys, + (void *)CLASS_get_next_sorted_symbol, + (void *)DEBUG_get_object_access_type, + (void *)DEBUG_find_class, + (void *)CARRAY_get_array_bounds, + (void *)GB_DebugBreakOnError, + (void *)DEBUG_enter_eval, + (void *)DEBUG_leave_eval, + NULL +}; + +const void *const GAMBAS_JitApi[] = +{ + (void *)&SP, + (void *)&EXEC_current, + (void *)&TEMP, + (void *)&EXEC_debug, + (void *)&EXEC_super, + (void *)JIT_debug, + (void *)JIT_get_code, + (void *)THROW, + (void *)THROW_TYPE, + (void *)JIT_get_constant, + (void *)JIT_get_class_ref, + (void *)EXEC_subr_table, + (void *)STRING_char_table, + (void *)UNBORROW, + (void *)EXEC_new, + (void *)EXEC_push_array, + (void *)EXEC_pop_array, + (void *)VALUE_convert, + (void *)EXEC_push_unknown, + (void *)JIT_call_unknown, + (void *)EXEC_pop_unknown, + (void *)EXEC_enum_first, + (void *)EXEC_enum_next, + (void *)SYMBOL_find, + (void *)JIT_load_class, + (void *)CLASS_load_without_init, + (void *)&ERROR_current, + (void *)&ERROR_handler, + (void *)ERROR_reset, + (void *)ERROR_set_last, + (void *)&EXEC_got_error, + (void *)&EVENT_Last, + (void *)EXEC_push_complex, + (void *)EXEC_push_vargs, + (void *)EXEC_drop_vargs, + (void *)EXEC_quit, + (void *)EXEC_push_unknown_event, + (void *)EXTERN_get_addr, + (void *)DEBUG_get_position, + (void *)RELEASE_many, + (void *)CSTRUCT_create_static, + (void *)CARRAY_create_static, + (void *)CARRAY_get_array_class, + NULL +}; + + +bool GAMBAS_DoNotRaiseEvent = FALSE; +bool GAMBAS_StopEvent = FALSE; + +static bool _event_stopped = FALSE; +static int _raise_event_level = 0; + +#define CATCH_ERROR \ + bool ret = FALSE; \ + TRY + +#define END_CATCH_ERROR \ + CATCH \ + { \ + ret = TRUE; \ + } \ + END_TRY \ + if (ret) EXEC_set_native_error(TRUE); \ + return ret; + +#define CATCH_ERROR_INT \ + int ret = 0; \ + TRY + +#define END_CATCH_ERROR_INT \ + CATCH \ + { \ + ret = -1; \ + } \ + END_TRY \ + if (ret < 0) EXEC_set_native_error(TRUE); \ + return ret; + +bool GB_GetInterface(const char *name, int version, void *iface) +{ + GB_LoadComponent(name); + + if (LIBRARY_get_interface_by_name(name, version, iface)) + ERROR_panic("Cannot find interface of library '%s'", name); + + return FALSE; +} + + +void *GB_Hook(int type, void *hook) +{ + void *old_hook; + void **phook = (void **)(void *)&EXEC_Hook; + + if ((type < 0) || (type > GB_HOOK_MAX)) + return NULL; + + type--; + old_hook = phook[type]; + if (hook) + phook[type] = hook; + + return old_hook; +} + + +bool GB_LoadComponent(const char *name) +{ + CATCH_ERROR + { + COMPONENT *comp = COMPONENT_create(name); + COMPONENT_load(comp); + } + END_CATCH_ERROR +} + + +static void push(int nval, va_list args) +{ + TYPE type; + + STACK_check(nval); + + while (nval) + { + type = va_arg(args, int); + SP->type = type; + + switch(type) + { + case T_INTEGER: + case T_BOOLEAN: + SP->_integer.value = va_arg(args, int); + break; + + case T_LONG: + SP->_long.value = va_arg(args, int64_t); + break; + + case T_STRING: + SP->type = T_CSTRING; + SP->_string.addr = va_arg(args, char *); + SP->_string.start = 0; + SP->_string.len = va_arg(args, int); + if (SP->_string.len <= 0 && SP->_string.addr) + SP->_string.len = strlen(SP->_string.addr); + break; + + case T_FLOAT: + SP->_float.value = va_arg(args, double); + break; + + case T_OBJECT: + SP->_object.object = va_arg(args, void *); + OBJECT_REF(SP->_object.object); + break; + + default: + ERROR_panic("GB.Push: unknown datatype"); + break; + } + + SP++; + nval--; + } +} + + +void GB_Push(int nval, ...) +{ + va_list args; + + va_start(args, nval); + push(nval, args); + va_end(args); +} + +static void error(int code, CLASS *class, const char *name) +{ + GB_Error((char *)(intptr_t)code, CLASS_get_name(class), name); +} + +static CLASS_DESC *get_desc(CLASS *class, const char *name) +{ + int index; + + index = CLASS_find_symbol(class, name); + + if (index == NO_SYMBOL) + { + error(E_NSYMBOL, class, name); + return NULL; + } + else + return class->table[index].desc; +} + +void *GB_GetProperty(void *object, const char *name) +{ + CLASS_DESC *desc; + CLASS *class; + char type; + + //if (GB_CheckObject(object)) + // return; + + if (OBJECT_is_class(object)) + { + class = (CLASS *)object; + object = NULL; + } + else + { + class = OBJECT_class(object); + } + + desc = get_desc(class, name); + + if (!desc) + return NULL; + + type = CLASS_DESC_get_type(desc); + + if (type == CD_PROPERTY || type == CD_PROPERTY_READ || type == CD_VARIABLE) + { + if (!object) + { + error(E_DYNAMIC, class, name); + return NULL; + } + } + else if (type == CD_STATIC_PROPERTY || type == CD_STATIC_PROPERTY_READ || type == CD_STATIC_VARIABLE || type == CD_CONSTANT) + { + if (object) + { + error(E_STATIC, class, name); + return NULL; + } + } + else + { + error(E_NPROPERTY, class, name); + return NULL; + } + + if (type == CD_VARIABLE) + VALUE_read(&TEMP, (char *)object + desc->variable.offset, desc->property.type); + else if (type == CD_STATIC_VARIABLE) + VALUE_read(&TEMP, (char *)class->stat + desc->variable.offset, desc->property.type); + else if (type == CD_CONSTANT) + VALUE_read(&TEMP, (void *)&desc->constant.value, desc->constant.type); + else + { + if (desc->property.native) + { + if (EXEC_call_native(desc->property.read, object, desc->property.type, 0)) + { + EXEC_set_native_error(TRUE); + return NULL; + } + } + else + { + EXEC.class = desc->property.class; + EXEC.object = object; + EXEC.nparam = 0; + EXEC.native = FALSE; + EXEC.index = (int)(intptr_t)desc->property.read; + //EXEC.func = &class->load->func[(long)desc->property.read]; + + EXEC_function_keep(); + + TEMP = *RP; + UNBORROW(RP); + RP->type = T_VOID; + } + } + + return &TEMP; +} + +bool GB_SetProperty(void *object, const char *name, GB_VALUE *val) +{ + VALUE *value = (VALUE *)val; + CLASS_DESC *desc; + CLASS *class; + char type; + + if (OBJECT_is_class(object)) + { + class = (CLASS *)object; + object = NULL; + } + else + { + class = OBJECT_class(object); + } + + desc = get_desc(class, name); + + if (!desc) + return TRUE; + + type = CLASS_DESC_get_type(desc); + + if (type == CD_PROPERTY || type == CD_VARIABLE) + { + if (!object) + { + if (!class->auto_create) + { + error(E_DYNAMIC, class, name); + return TRUE; + } + + object = EXEC_auto_create(class, TRUE); + } + } + else if (type == CD_STATIC_PROPERTY || type == CD_STATIC_VARIABLE) + { + if (object) + { + error(E_STATIC, class, name); + return TRUE; + } + } + else if (type == CD_PROPERTY_READ || type == CD_STATIC_PROPERTY_READ) + { + error(E_NWRITE, class, name); + return TRUE; + } + else + { + error(E_NPROPERTY, class, name); + return TRUE; + } + + if (type == CD_VARIABLE) + VALUE_write(value, (char *)object + desc->variable.offset, desc->variable.type); + else if (type == CD_STATIC_VARIABLE) + VALUE_write(value, (char *)class->stat + desc->variable.offset, desc->variable.type); + else + { + if (desc->property.native) + { + VALUE_conv(value, desc->property.type); + + if (EXEC_call_native(desc->property.write, object, 0, value)) + { + EXEC_set_native_error(TRUE); + return TRUE; + } + } + else + { + *SP = *value; + BORROW(SP); + SP++; + + EXEC.class = desc->property.class; + EXEC.object = object; + EXEC.nparam = 1; + EXEC.native = FALSE; + EXEC.index = (int)(intptr_t)desc->property.write; + //EXEC.func = &class->load->func[(long)desc->property.write]; + + EXEC_function(); + + /*VALUE_write(value, OBJECT_get_prop_addr(object, desc), desc->property.type);*/ + } + } + + return FALSE; +} + + +bool GB_CanRaise(void *object, int event_id) +{ + ushort *event_tab; + int func_id; + COBSERVER *obs; + + if (!object || !OBJECT_has_events(object)) + return FALSE; + + LIST_for_each(obs, OBJECT_event(object)->observer) + { + if (OBJECT_parent(obs) && obs->event && obs->event[event_id]) + return TRUE; + } + + if (!OBJECT_parent(object)) + return FALSE; + + event_tab = OBJECT_event(object)->event; + func_id = event_tab[event_id]; + + return (func_id != 0); +} + + +static int get_event_func_id(ushort *event_tab, int event_id) +{ + int func_id; + + if (!event_tab) + return 0; + + func_id = event_tab[event_id]; + if (!func_id) + return 0; + + return func_id; +} + +static bool raise_event(OBJECT *observer, void *object, int func_id, int nparam) +{ + bool stop_event; + CLASS *class; + CLASS_DESC_METHOD *desc; + void *old_last; + bool result; + + func_id--; + + if (OBJECT_is_class(observer)) + { + class = (CLASS *)observer; //OBJECT_class(object); + observer = NULL; + } + else + class = OBJECT_class(observer); + + desc = &class->table[func_id].desc->method; + + old_last = EVENT_Last; + EVENT_Last = object; + //OBJECT_REF(object, "raise_event"); + +// if (arg) +// { +// EXEC_dup(nparam); +// } +// else +// { +// va_start(args, nparam); +// push(nparam, args); +// va_end(args); +// } + + stop_event = GAMBAS_StopEvent; + GAMBAS_StopEvent = FALSE; + + EXEC_public_desc(class, observer, desc, nparam); + + if (RP->type == T_VOID) + result = FALSE; + else + result = RP->_boolean.value != 0; + + if (GAMBAS_StopEvent) + result = TRUE; + + GAMBAS_StopEvent = stop_event; + + //OBJECT_UNREF(object, "raise_event"); + EVENT_Last = old_last; + + EXEC_release_return_value(); + + return result; +} + +// If nparam < 0, the args are already on the stack + +static GB_RAISE_HANDLER *_GB_Raise_handler = NULL; + +static void error_GB_Raise(void *object, intptr_t nparam) +{ + RELEASE_MANY(SP, (int)nparam); + OBJECT_UNREF(object); + + _raise_event_level--; + + if (_GB_Raise_handler && _GB_Raise_handler->level == _raise_event_level) + { + (*_GB_Raise_handler->callback)(_GB_Raise_handler->data); + _GB_Raise_handler = _GB_Raise_handler->old; + } +} + +void GB_RaiseBegin(GB_RAISE_HANDLER *handler) +{ + handler->old = _GB_Raise_handler; + handler->level = _raise_event_level; + _GB_Raise_handler = handler; +} + +void GB_RaiseEnd(GB_RAISE_HANDLER *handler) +{ + _GB_Raise_handler = handler->old; +} + +bool GB_Raise(void *object, int event_id, int nparam, ...) +{ + OBJECT *parent; + int func_id; + int result; + va_list args; + bool arg; + COBSERVER *obs; + + if (GAMBAS_DoNotRaiseEvent) + return FALSE; + + if (!OBJECT_is_valid(object) || !OBJECT_has_events(object)) + return FALSE; + + OBJECT_REF(object); + + arg = nparam < 0; + nparam = abs(nparam); + + _raise_event_level++; + + ON_ERROR_2(error_GB_Raise, object, nparam) + { + if (!arg) + { + va_start(args, nparam); + push(nparam, args); + va_end(args); + } + + result = FALSE; + + // Observers before + + LIST_for_each(obs, OBJECT_event(object)->observer) + { + parent = OBJECT_active_parent(obs); + if (!parent) + continue; + if (obs->after) + continue; + //if (obs->object != object) + // continue; + + func_id = get_event_func_id(obs->event, event_id); + if (!func_id) + continue; + + if (!OBJECT_is_valid(parent)) + { + OBJECT_detach((OBJECT *)obs); + continue; + } + + EXEC_dup(nparam); + result = raise_event(parent, object, func_id, nparam); + + if (result) + goto __RETURN; + } + + // Parent + + parent = OBJECT_active_parent(object); + if (parent) + { + func_id = get_event_func_id(OBJECT_event(object)->event, event_id); + #if DEBUG_EVENT + CLASS *class = OBJECT_class(object); + fprintf(stderr, "GB_Raise(%p, %d, %s)\n", object, event_id, class->event[event_id].name); + fprintf(stderr, "func_id = %d parent = (%s %p)\n", func_id, parent->class->name, parent); + if (OBJECT_is_locked(parent)) + fprintf(stderr, "parent is locked!\n"); + #endif + if (func_id) + { + if (!OBJECT_is_valid(parent)) + OBJECT_detach(object); + else + { + EXEC_dup(nparam); + result = raise_event(parent, object, func_id, nparam); + if (result) + goto __RETURN; + } + } + } + + // Observers after + + LIST_for_each(obs, OBJECT_event(object)->observer) + { + parent = OBJECT_active_parent(obs); + if (!parent) + continue; + if (!obs->after) + continue; + //if (obs->object != object) + // continue; + + func_id = get_event_func_id(obs->event, event_id); + if (!func_id) + continue; + + if (!OBJECT_is_valid(parent)) + { + OBJECT_detach((OBJECT *)obs); + continue; + } + + EXEC_dup(nparam); + result = raise_event(parent, object, func_id, nparam); + if (result) + goto __RETURN; + } + +__RETURN: + + RELEASE_MANY(SP, nparam); + OBJECT_UNREF(object); + } + END_ERROR + + _raise_event_level--; + + return result; +} + + +bool GB_GetFunction(GB_FUNCTION *_func, void *object, const char *name, const char *sign, const char *type) +{ + GB_API_FUNCTION *func = (GB_API_FUNCTION *)_func; + char len_min, nparam, npvar; + TYPE *tsign; + TYPE tret; + int index; + CLASS *class; + int kind; + CLASS_DESC *desc; + bool error; + const char *err; + + if (OBJECT_is_class(object)) + { + class = (CLASS *)object; + kind = CD_STATIC_METHOD; + } + else + { + class = OBJECT_class(object); + kind = CD_METHOD; + } + + CLASS_load(class); + + index = CLASS_find_symbol(class, name); + if (index == NO_SYMBOL) + { + err = "Symbol not found"; + goto _NOT_FOUND; + } + + desc = class->table[index].desc; + if (CLASS_DESC_get_type(desc) != kind) + { + err = kind == CD_METHOD ? "Not a method" : "Not a static method"; + goto _NOT_FOUND; + } + + if (sign) + { + TYPE_signature_length(sign, &len_min, &nparam, &npvar); + tsign = NULL; + + if (nparam) + { + ALLOC(&tsign, nparam * sizeof(TYPE)); + tsign = TYPE_transform_signature(&tsign, sign, nparam); + } + + error = TYPE_compare_signature(desc->method.signature, desc->method.npmax, tsign, nparam, FALSE); + + if (nparam) + FREE(&tsign); + + if (error) + { + err = "Parameters do not match"; + goto _NOT_FOUND; + } + } + + if (type) + { + tret = TYPE_from_string(&type); + if (tret != desc->method.type) + { + if (tret == T_VOID) + err = "Must be a procedure"; + else if (desc->method.type == T_VOID) + err = "Must be a function"; + else + err = "Return type does not match"; + + goto _NOT_FOUND; + } + } + + func->object = kind == CD_STATIC_METHOD ? NULL : object; + func->desc = &desc->method; + + if (!func->desc) + abort(); + + return FALSE; + +_NOT_FOUND: + + GB_Error("Unable to find method &1 in class &2. &3", name, CLASS_get_name(class), err); + func->object = NULL; + func->desc = NULL; + return TRUE; +} + + +void *GB_GetClassInterface(void *_class, const char *_name) +{ + CLASS_DESC *desc; + int index; + CLASS *class = (CLASS *)_class; + int len = strlen(_name); + char name[len + 4]; + + CLASS_load(class); + + strcpy(name, "_@"); + strcat(name, _name); + + index = CLASS_find_symbol(class, name); + if (index == NO_SYMBOL) + goto __NOT_FOUND; + + desc = class->table[index].desc; + if (CLASS_DESC_get_type(desc) != CD_CONSTANT) + goto __NOT_FOUND; + + if (desc->constant.type != T_POINTER) + goto __NOT_FOUND; + + return desc->constant.value._pointer; + +__NOT_FOUND: + return NULL; +} + + +GB_VALUE *GB_Call(GB_FUNCTION *_func, int nparam, int release) +{ + GB_API_FUNCTION *func = (GB_API_FUNCTION *)_func; + bool stop_event; + + if (!func || !func->desc) + GB_Error("Unknown function call"); + //TEMP.type = GB_T_NULL; + else + { + stop_event = GAMBAS_StopEvent; + EXEC_public_desc(func->desc->class, func->object, func->desc, nparam); + _event_stopped = GAMBAS_StopEvent; + GAMBAS_StopEvent = stop_event; + + if (release) + EXEC_release_return_value(); + else + { + UNBORROW(RP); + TEMP = *RP; + RP->type = T_VOID; + } + } + + return (GB_VALUE *)&TEMP; +} + + +int GB_GetEvent(void *class, char *name) +{ + CLASS_DESC_EVENT *cd; + + cd = CLASS_get_event_desc((CLASS *)class, name); + if (!cd) + return (-1); + else + return cd->index; +} + + +char *GB_GetLastEventName() +{ + return EVENT_Name; +} + + +bool GB_Stopped(void) +{ + return _event_stopped; +} + + +int GB_NParam(void) +{ + return EXEC_unknown_nparam; +} + + +const char *GB_GetUnknown(void) +{ + return EXEC_unknown_name; +} + + +void GB_OnErrorBegin(GB_ERROR_HANDLER *handler) +{ + handler->prev = ERROR_handler; + handler->context = ERROR_current; + ERROR_handler = (ERROR_HANDLER *)handler; +} + +void GB_OnErrorEnd(GB_ERROR_HANDLER *handler) +{ + ERROR_handler = (ERROR_HANDLER *)handler->prev; +} + +void GB_Error(const char *error, ...) +{ + va_list args; + char *arg[4]; + int i; + + if (!error) + { + EXEC_set_native_error(FALSE); + return; + } + + va_start(args, error); + + for (i = 0; i < 4; i++) + arg[i] = va_arg(args, char *); + + va_end(args); + + ERROR_define(error, arg); + EXEC_set_native_error(TRUE); +} + +void GB_Deprecated(const char *msg, const char *func, const char *repl) +{ + if (EXEC_debug) + { + if (repl) + GB_Error("&1: &2 is deprecated. Use &3 instead", msg, func, repl); + else + GB_Error("&1: &2 is deprecated. Do not use it anymore", msg, func); + } + else + { + DEBUG_where(); + if (repl) + fprintf(stderr, "%s: %s is deprecated. Use %s instead\n", msg, func, repl); + else + fprintf(stderr, "%s: %s is deprecated. Do not use it anymore\n", msg, func); + } +} + +#if DEBUG_REF + +#include + +static void print_stack_backtrace() +{ + void *trace[16]; + char **messages = (char **)NULL; + int i, trace_size = 0; + + trace_size = backtrace(trace, 16); + messages = backtrace_symbols(trace, trace_size); + //printf("[bt] Execution path:\n"); + fprintf(stderr, "[ "); + for (i=0; istop = TRUE; +} + + +void *GB_GetEnum(void) +{ + return (void *)&EXEC_enum->data; +} + + +static void *_enum_object; + +void *GB_BeginEnum(void *enum_object) +{ + CENUM *old = EXEC_enum; + EXEC_enum = NULL; + _enum_object = enum_object; + return old; +} + +void GB_EndEnum(void *old_enum) +{ + EXEC_enum = old_enum; +} + + +bool GB_NextEnum(void) +{ + for(;;) + { + EXEC_enum = CENUM_get_next(EXEC_enum); + if (!EXEC_enum) + return TRUE; + if (EXEC_enum->enum_object == _enum_object && !EXEC_enum->stop) + return FALSE; + } +} + +void GB_StopAllEnum(void *enum_object) +{ + void *save = GB_BeginEnum(enum_object); + while (!GB_NextEnum()) + GB_StopEnum(); + GB_EndEnum(save); +} + +GB_VALUE *GB_GetReturnValue(void) +{ + return (GB_VALUE *)&TEMP; +} + +void GB_Return(GB_TYPE type, ...) +{ + static void *jump[16] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL + }; + + VALUE *ret = &TEMP; + va_list args; + + va_start(args, type); + + ret->type = type; + if (TYPE_is_object(type)) + goto __OBJECT; + else + goto *jump[type]; + +__BOOLEAN: + + ret->_integer.value = va_arg(args, int) ? (-1) : 0; + goto __CONV; + +__BYTE: + + ret->_integer.value = va_arg(args, int); + goto __CONV; + +__SHORT: + + ret->_integer.value = va_arg(args, int); + goto __CONV; + +__INTEGER: + + ret->_integer.value = va_arg(args, int); + goto __CONV; + +__LONG: + + ret->_long.value = va_arg(args, int64_t); + goto __CONV; + +__SINGLE: + + ret->_single.value = va_arg(args, double); + goto __CONV; + +__FLOAT: + + ret->_float.value = va_arg(args, double); + goto __CONV; + +__DATE: + + ret->_date.date = va_arg(args, int); + ret->_date.time = va_arg(args, int); + goto __CONV; + +__OBJECT: + + ret->_object.object = va_arg(args, void *); + goto __CONV; + +__POINTER: + + ret->_pointer.value = va_arg(args, void *); + goto __CONV; + +__CLASS: + ret->_class.class = va_arg(args, CLASS *); + goto __CONV; + +__CONV: +__STRING: +__VOID: +__VARIANT: +__FUNCTION: +__NULL: + + va_end(args); + return; +} + + +void GB_ReturnInteger(int val) +{ + TEMP.type = T_INTEGER; + TEMP._integer.value = val; +} + + +void GB_ReturnLong(int64_t val) +{ + TEMP.type = T_LONG; + TEMP._long.value = val; +} + + +void GB_ReturnPointer(void *val) +{ + TEMP.type = T_POINTER; + TEMP._pointer.value = val; +} + + +void GB_ReturnSingle(float val) +{ + TEMP.type = T_SINGLE; + TEMP._single.value = val; +} + +void GB_ReturnFloat(double val) +{ + TEMP.type = T_FLOAT; + TEMP._float.value = val; +} + + +void GB_ReturnDate(GB_DATE *date) +{ + TEMP.type = T_DATE; + if (date) + { + TEMP._date.date = date->value.date; + TEMP._date.time = date->value.time; + } + else + { + TEMP._date.date = 0; + TEMP._date.time = 0; + } +} + + +void GB_ReturnBoolean(int val) +{ + TEMP.type = T_BOOLEAN; + TEMP._boolean.value = val ? -1 : 0; +} + + +void GB_ReturnObject(void *val) +{ + if (val == NULL) + GB_ReturnNull(); + else + { + TEMP.type = T_OBJECT; //OBJECT_class(val); + TEMP._object.object = val; + } +} + + +void GB_ReturnVariant(GB_VARIANT_VALUE *val) +{ + TEMP._variant.type = T_VARIANT; + + if (!val) + { + TEMP._variant.vtype = T_NULL; + } + else + { + //VALUE_read(&TEMP, value, type); + TEMP._variant.vtype = val->type; + if (TEMP._variant.vtype == T_VOID) + TEMP._variant.vtype = T_NULL; + + VARIANT_copy_value(&TEMP._variant, (VARIANT *)val); + } +} + + +void GB_ReturnConvVariant(void) +{ + BORROW(&TEMP); + VALUE_conv(&TEMP, T_VARIANT); + UNBORROW(&TEMP); +} + +void GB_ReturnBorrow(void) +{ + BORROW(&TEMP); +} + +void GB_ReturnRelease(void) +{ + UNBORROW(&TEMP); +} + +void GB_ReturnPtr(GB_TYPE type, void *value) +{ + if (type == T_VOID) + return; + + if (!value) + VALUE_default(&TEMP, type); + else + VALUE_read(&TEMP, value, type); +} + + +void GB_ReturnSelf(void *object) +{ + if (object) + GB_ReturnObject(object); + else + { + TEMP.type = T_CLASS; + TEMP._class.class = NULL; + } +} + + +char *GB_ToZeroString(GB_STRING *src) +{ + char *str; + + str = STRING_new_temp(src->value.addr + src->value.start, src->value.len); + + if (str == NULL) + return ""; + else + return str; +} + + +void GB_ReturnString(char *str) +{ + TEMP.type = T_STRING; + TEMP._string.addr = str; + TEMP._string.start = 0; + TEMP._string.len = STRING_length(str); + + if (TEMP._string.len == 0) + TEMP._string.addr = 0; +} + + +void GB_ReturnConstString(const char *str, int len) +{ + TEMP.type = T_CSTRING; + TEMP._string.addr = (char *)str; + TEMP._string.start = 0; + TEMP._string.len = len; + + if (TEMP._string.len == 0) + TEMP._string.addr = 0; +} + + +void GB_ReturnConstZeroString(const char *str) +{ + int len; + + if (str) + len = strlen(str); + else + len = 0; + + GB_ReturnConstString(str, len); +} + + +void GB_ReturnNewString(const char *src, int len) +{ + if (len <= 0) + { + if (*src) + { + ERROR_warning("please use GB.ReturnNewZeroString() instead of GB.ReturnNewString()"); + len = strlen(src); + } + else + { + GB_ReturnVoidString(); + return; + } + } + + GB_ReturnString(STRING_new_temp(src, len)); +} + + +void GB_ReturnNewZeroString(const char *src) +{ + GB_ReturnString(STRING_new_temp_zero(src)); +} + + +void GB_ReturnNull(void) +{ + VALUE_null(&TEMP); +} + +void GB_ReturnVoidString(void) +{ + STRING_void_value(&TEMP); +} + + +void *GB_GetClass(void *object) +{ + if (object) + return OBJECT_class(object); + else + return EXEC.class; +} + + +char *GB_GetClassName(void *object) +{ + CLASS *class = GB_GetClass(object); + return class == CLASS_Class ? CLASS_get_name((CLASS *)object) : CLASS_get_name(class); +} + + +bool GB_Is(void *object, void *class) +{ + CLASS *ob_class; + + if (!object) + return FALSE; + + ob_class = OBJECT_class(object); + + return ((ob_class == class) || CLASS_inherits(ob_class, class)); +} + + +bool GB_LoadFile(const char *path, int lenp, char **addr, int *len) +{ + bool ret = FALSE; + + //fprintf(stderr, "GB_LoadFile: %.*s\n", lenp ? lenp : strlen(path), path); + + TRY + { + *addr = 0; + *len = 0; + + STREAM_map(STRING_conv_file_name(path, lenp), addr, len); + } + CATCH + { + if (*addr) + STREAM_unmap(*addr, *len); + + ret = TRUE; + } + END_TRY + + if (ret) + EXEC_set_native_error(TRUE); + + return ret; +} + + +bool GB_ExistFile(const char *path) +{ + return FILE_exist(path); +} + + +void GB_Store(GB_TYPE type, GB_VALUE *src, void *dst) +{ + if (src != NULL) + VALUE_write((VALUE *)src, dst, type); + else + VALUE_free(dst, type); +} + + +void GB_StoreString(GB_STRING *src, char **dst) +{ + STRING_unref(dst); + if (src) + { + *dst = STRING_new(src->value.addr + src->value.start, src->value.len); + } + else + *dst = NULL; +} + +void GB_StoreObject(GB_OBJECT *src, void **dst) +{ + void *object; + + if (src) + object = src->value; + else + object = NULL; + + if (object) + OBJECT_REF(object); + + OBJECT_UNREF(*dst); + *dst = object; +} + +void GB_StoreVariant(GB_VARIANT *src, void *dst) +{ + /*GB_Store(GB_T_VARIANT, (GB_VALUE *)src, dst);*/ + if (src) + { + VALUE_write((VALUE *)src, dst, T_VARIANT); + + /*VARIANT_keep((VARIANT *)&src->value); + VARIANT_free((VARIANT *)dst); + *((VARIANT *)dst) = *((VARIANT *)&src->value);*/ + } + else + { + VARIANT_free((VARIANT *)dst); + ((VARIANT *)dst)->type = T_NULL; + } +} + +void GB_BorrowValue(GB_VALUE *value) +{ + BORROW((VALUE *)value); +} + +void GB_ReleaseValue(GB_VALUE *value) +{ + RELEASE((VALUE *)value); +} + +void GB_Watch(int fd, int flag, void *callback, intptr_t param) +{ + HOOK_DEFAULT(watch, WATCH_watch)(fd, flag, callback, param); +} + + +void *GB_Create(void *class, const char *name, void *parent) +{ + void *object; + + object = OBJECT_new(class, name, parent); + OBJECT_UNREF_KEEP(object); + + return object; +} + +void *GB_New(void *class, const char *name, void *parent) +{ + if (name && !parent) + { + parent = OP; + if (!parent) + parent = CP; + } + + if (!((CLASS *)class)->no_create) + { + int nparam = 0; + if (!name && parent) + { + nparam = (int)(intptr_t)parent; + parent = NULL; + } + + return OBJECT_create(class, name, parent, nparam); + } + else + return GB_Create(class, name, parent); +} + +bool GB_CheckObject(void *object) +{ + CLASS *class; + + if (object == NULL) + { + GB_Error((char *)E_NULL); + return TRUE; + } + + class = OBJECT_class(object); + + if (class->check && (*(class->check))(object)) + { + GB_Error((char *)E_IOBJECT); + return TRUE; + } + + return FALSE; +} + + +const char *GB_AppName(void) +{ + return PROJECT_name; +} + +const char *GB_AppPath(void) +{ + return PROJECT_path; +} + +const char *GB_AppTitle(void) +{ + return LOCAL_gettext(PROJECT_title); +} + +const char *GB_AppVersion(void) +{ + return PROJECT_version; +} + +void *GB_AppStartupClass(void) +{ + return PROJECT_class; +} + + +void *GB_Eval(void *expr, void *func) +{ + bool err = EVAL_expression((EXPRESSION *)expr, (EVAL_FUNCTION)func); + + EXEC_set_native_error(err); + + if (err) + return NULL; + else + return &TEMP; +} + + +void GB_Alloc(void **addr, int len) +{ + ALLOC(addr, len); +} + +void GB_AllocZero(void **addr, int len) +{ + ALLOC_ZERO(addr, len); +} + +void GB_Free(void **addr) +{ + FREE(addr); +} + +void GB_Realloc(void **addr, int len) +{ + REALLOC(addr, len); +} + + +bool GB_Conv(GB_VALUE *arg, GB_TYPE type) +{ + CATCH_ERROR + { + VALUE_conv((VALUE *)arg, (GB_TYPE)type); + } + END_CATCH_ERROR +} + + +DATE_SERIAL *GB_SplitDate(GB_VALUE *arg) +{ + return DATE_split((VALUE *)arg); +} + +bool GB_MakeDate(DATE_SERIAL *date, GB_DATE *arg) +{ + return DATE_make(date, (VALUE *)arg); +} + + +int GB_StringLength(const char *str) +{ + return STRING_length(str); +} + + +bool GB_NumberToString(int local, double value, const char *format, char **str, int *len) +{ + return + LOCAL_format_number + ( + value, + format ? LF_USER : LF_GENERAL_NUMBER, + format, + format ? strlen(format) : 0, + str, len, local != 0 + ); +} + + +void GB_HashTableNew(GB_HASHTABLE *hash, int mode) +{ + HASH_TABLE_create((HASH_TABLE **)hash, sizeof(void *), mode); +} + +void GB_HashTableAdd(GB_HASHTABLE hash, const char *key, int len, void *data) +{ + if (len <= 0) + len = strlen(key); + + *((void **)HASH_TABLE_insert((HASH_TABLE *)hash, key, len)) = data; +} + +void GB_HashTableRemove(GB_HASHTABLE hash, const char *key, int len) +{ + if (len <= 0) + len = strlen(key); + + HASH_TABLE_remove((HASH_TABLE *)hash, key, len); +} + +bool GB_HashTableGet(GB_HASHTABLE hash, const char *key, int len, void **data) +{ + void **pdata; + + if (len <= 0) + len = strlen(key); + + pdata = (void **)HASH_TABLE_lookup((HASH_TABLE *)hash, key, len, FALSE); + if (pdata) + { + *data = *pdata; + return FALSE; + } + else + return TRUE; +} + +void GB_HashTableEnum(GB_HASHTABLE hash, GB_HASHTABLE_ENUM_FUNC func) +{ + HASH_ENUM iter; + void **pdata; + + CLEAR(&iter); + + for(;;) + { + pdata = (void **)HASH_TABLE_next((HASH_TABLE *)hash, &iter, FALSE); + if (!pdata) + break; + + (*func)(*pdata); + } +} + +bool GB_HashTableFirst(GB_HASHTABLE hash, void **data) +{ + HASH_ENUM iter; + void **pdata; + + CLEAR(&iter); + pdata = (void **)HASH_TABLE_next((HASH_TABLE *)hash, &iter, FALSE); + if (pdata) + { + *data = *pdata; + return FALSE; + } + else + return TRUE; +} + +void GB_NewArray(void *pdata, int size, int count) +{ + ARRAY_create_with_size(pdata, size, 16); + if (count) + ARRAY_add_data(pdata, count, TRUE); +} + + +int GB_CountArray(void *data) +{ + return ARRAY_count(data); +} + + +void *GB_Add(void *pdata) +{ + return ARRAY_add_void_size(pdata); +} + +char *GB_NewZeroString(char *src) +{ + return STRING_new_zero(src); +} + +char *GB_TempString(char *src, int len) +{ + return STRING_new_temp(src, len); +} + +char *GB_RefString(char *str) +{ + STRING_ref(str); + return str; +} + +void GB_FreeString(char **str) +{ + STRING_unref(str); + *str = NULL; +} + +bool GB_ConvString(char **result, const char *str, int len, const char *src, const char *dst) +{ + CATCH_ERROR + { + STRING_conv(result, str, len, src, dst, TRUE); + } + END_CATCH_ERROR +} + + +char *GB_SystemCharset(void) +{ + return LOCAL_is_UTF8 ? "UTF-8" : LOCAL_encoding; +} + +char *GB_SystemDomainName(void) +{ + char host[256], domain[256]; + + if (gethostname(host, sizeof(host) - 1)) + return ""; + + if (getdomainname(domain, sizeof(domain) - 1)) + return ""; + + if ((strlen(host) + strlen(domain)) > COMMON_BUF_MAX) + return ""; + + *COMMON_buffer = 0; + if (*domain && strcmp(domain, "(none)")) + { + strcpy(COMMON_buffer, domain); + strcat(COMMON_buffer, "."); + } + strcat(COMMON_buffer, host); + + return COMMON_buffer; +} + +bool GB_IsRightToLeft(void) +{ + return LOCAL_local.rtl; +} + +char *GB_SystemPath(void) +{ + return PROJECT_exec_path; +} + +/*void GB_StreamInit(GB_STREAM *stream, int fd) +{ + STREAM *s = (STREAM *)stream; + + s->type = &STREAM_direct; + s->direct.fd = fd; +}*/ + +GB_STREAM *GB_StreamGet(void *object) +{ + return (GB_STREAM *)CSTREAM_stream(object); +} + +void GB_StreamSetSwapping(GB_STREAM *stream, int v) +{ + ((STREAM *)stream)->common.swap = v; +} + +void GB_StreamSetAvailableNow(GB_STREAM *stream, int v) +{ + ((STREAM *)stream)->common.available_now = v; +} + +bool GB_StreamBlock(GB_STREAM *stream, int block) +{ + STREAM *st = (STREAM *)stream; + bool old = STREAM_is_blocking(st); + STREAM_blocking(st, block); + return old; +} + +int GB_StreamRead(GB_STREAM *stream, void *addr, int len) +{ + CATCH_ERROR_INT + { + if (len < 0) + ret = STREAM_read_max((STREAM *)stream, addr, -len); + else + ret = STREAM_read((STREAM *)stream, addr, len); + } + END_CATCH_ERROR_INT +} + +int GB_StreamWrite(GB_STREAM *stream, void *addr, int len) +{ + CATCH_ERROR_INT + { + STREAM_write((STREAM *)stream, addr, len); + ret = len; + } + END_CATCH_ERROR_INT +} + +int GB_tolower(int c) +{ + return tolower(c); +} + +int GB_toupper(int c) +{ + return toupper(c); +} + +char *GB_TempDir(void) +{ + return FILE_make_temp(NULL, NULL); +} + +char *GB_TempFile(const char *pattern) +{ + int len; + return FILE_make_temp(&len, pattern); +} + +bool GB_CopyFile(const char *src, const char *dst) +{ + CATCH_ERROR + { + FILE_copy(src, dst); + } + END_CATCH_ERROR +} + +#if 0 +int GB_FindFile(const char *dir, int recursive, int follow, void (*found)(const char *)) +{ + int ret = 0; + char *pattern; + int len_pattern; + char *str; + + TRY + { + if (recursive) + FILE_recursive_dir(dir, found, NULL, 0, follow); + else + { + FILE_dir_first(dir, NULL, 0); + while (!FILE_dir_next(&pattern, &len_pattern)) + { + if (!LOCAL_is_UTF8) + { + if (STRING_conv(&str, pattern, len_pattern, LOCAL_encoding, SC_UTF8, FALSE)) + STRING_new(&str, pattern, len_pattern); + else + STRING_ref(str); + } + else + STRING_new(&str, pattern, len_pattern); + + (*found)(str); + STRING_unref(&str); + } + } + } + CATCH + { + ret = 1; + GAMBAS_Error = TRUE; + } + END_TRY; + + return ret; +} +#endif + +bool GB_StatFile(const char *path, GB_FILE_STAT *info, bool follow) +{ + CATCH_ERROR + { + FILE_stat(path, (FILE_STAT *)info, follow); + } + END_CATCH_ERROR +} + +char *GB_RealFileName(const char *name, int len) +{ + char *path = STRING_conv_file_name(name, len); + char *real; + char *temp; + + if (!STREAM_in_archive(path)) + return path; + + temp = FILE_make_temp(NULL, NULL); + real = STRING_new_temp(NULL, strlen(temp) + strlen(path) + strlen("/data/")); + snprintf(real, strlen(temp) + strlen(path) + strlen("/data/") + 1, "%s/data/%s", temp, path); + + if (!FILE_exist(real)) + { + TRY + { + FILE_make_path_dir(real); + FILE_copy(path, real); + } + CATCH + { + real = path; + } + END_TRY + } + + return real; +} + +static GB_BROWSE_PROJECT_CALLBACK _browse_project_func; + +static void project_file_found(const char *path) +{ + FILE_STAT info; + + FILE_stat(path, &info, FALSE); + (*_browse_project_func)(path, info.size); +} + +void GB_BrowseProject(GB_BROWSE_PROJECT_CALLBACK func) +{ + ARCHIVE *arch = NULL; + + ARCHIVE_get_current(&arch); + if (!arch || (arch == ARCHIVE_main && !EXEC_arch)) + { + _browse_project_func = func; + FILE_recursive_dir(PROJECT_path, project_file_found, NULL, 0, FALSE); + } + else + ARCHIVE_browse(arch, func); +} + +void GB_BrowseDirectory(const char *dir, GB_BROWSE_CALLBACK before, GB_BROWSE_CALLBACK after) +{ + FILE_recursive_dir(dir, before, after, 0, FALSE); +} + + +const char *GB_CurrentComponent() +{ + ARCHIVE *arch; + + ARCHIVE_get_current(&arch); + return arch->name ? arch->name : ""; +} + + +void *GB_DebugGetExec(void) +{ + return &EXEC_current; +} + +void GB_DebugBreakOnError(bool b) +{ + EXEC_break_on_error = b; +} + +bool GB_SystemDebug(void) +{ + return EXEC_debug; +} + +void GB_SystemHasForked(void) +{ + MATH_init(); + FILE_init(); + LOCAL_init(); + + if (EXEC_profile) + DEBUG.Profile.Cancel(); + EXEC_profile = FALSE; + EXEC_profile_instr = FALSE; + + SIGNAL_has_forked(); +} + +bool GB_ExistClass(const char *name) +{ + return CLASS_look_global(name, strlen(name)) != NULL; +} + + +bool GB_ExistClassLocal(const char *name) +{ + return CLASS_look(name, strlen(name)) != NULL; +} + +TYPE GB_GetArrayType(void *klass) +{ + return ((CLASS *)klass)->array_type; +} + +void GB_Wait(int delay) +{ + struct timespec rem; + double wait; + double stop, time; + + DEBUG_enter_event_loop(); + + if (delay == 0) + { + HOOK_DEFAULT(wait, WATCH_wait)(0); + } + else + { + wait = delay / 1000.0; + + DATE_timer(&stop, FALSE); + stop += wait; + + for(;;) + { + HOOK_DEFAULT(wait, WATCH_wait)((int)(wait * 1000 + 0.5)); + + if (DATE_timer(&time, FALSE)) + break; + + wait = stop - time; + if (wait <= 0.0) + break; + + rem.tv_sec = 0; + rem.tv_nsec = 1000000; + nanosleep(&rem, &rem); + } + } + + DEBUG_leave_event_loop(); +} + +bool GB_Serialize(const char *path, GB_VALUE *value) +{ + STREAM stream; + + CATCH_ERROR + { + STREAM_open(&stream, path, STO_CREATE); + STREAM_write_type(&stream, T_VARIANT, (VALUE *)value); + STREAM_close(&stream); + } + END_CATCH_ERROR +} + +bool GB_UnSerialize(const char *path, GB_VALUE *value) +{ + STREAM stream; + + CATCH_ERROR + { + STREAM_open(&stream, path, STO_READ); + STREAM_read_type(&stream, T_VARIANT, (VALUE *)value); + STREAM_close(&stream); + } + END_CATCH_ERROR +} + diff --git a/main/gbx/gbx_api.h b/main/gbx/gbx_api.h new file mode 100644 index 00000000..6ebe91ab --- /dev/null +++ b/main/gbx/gbx_api.h @@ -0,0 +1,230 @@ +/*************************************************************************** + + gbx_api.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_API_H +#define __GBX_API_H + +#include "gambas.h" +#include "gbx_type.h" +#include "gbx_stream.h" +#include "gb_hash.h" +#include "gbx_date.h" + +bool GB_GetInterface(const char *library, int version, void *iface); +void *GB_Hook(int type, void *hook); + +bool GB_LoadComponent(const char *name); +const char *GB_CurrentComponent(void); + +void GB_Wait(int); +void GB_Push(int nval, ...); +bool GB_CanRaise(void *object, int event_id); +bool GB_Raise(void *object, int event_id, int nparam, ...); +void GB_RaiseBegin(GB_RAISE_HANDLER *handler); +void GB_RaiseEnd(GB_RAISE_HANDLER *handler); +int GB_GetEvent(void *class, char *name); +char *GB_GetLastEventName(void); +bool GB_Stopped(void); + +int GB_NParam(void); +const char *GB_GetUnknown(void); + +void *GB_GetProperty(void *object, const char *property); +bool GB_SetProperty(void *object, const char *name, GB_VALUE *value); + +bool GB_Serialize(const char *path, GB_VALUE *value); +bool GB_UnSerialize(const char *path, GB_VALUE *value); + +void GB_Error(const char *msg, ...); +void GB_Deprecated(const char *msg, const char *func, const char *repl); +void GB_OnErrorBegin(GB_ERROR_HANDLER *handler); +void GB_OnErrorEnd(GB_ERROR_HANDLER *handler); + +void GB_Ref(void *object); +void GB_Unref(void **object); +void GB_UnrefKeep(void **object, int); + +void GB_StopEnum(void); +void *GB_GetEnum(void); +void *GB_BeginEnum(void *); +void GB_EndEnum(void *); +bool GB_NextEnum(void); +void GB_StopAllEnum(void *); + +GB_VALUE *GB_GetReturnValue(void); +void GB_Return(GB_TYPE type, ...); +void GB_ReturnInteger(int val); +#define GB_ReturnInt GB_ReturnInteger +void GB_ReturnLong(int64_t val); +void GB_ReturnPointer(void *val); +void GB_ReturnBoolean(int val); +void GB_ReturnObject(void *val); +void GB_ReturnNull(void); +void GB_ReturnSingle(float val); +void GB_ReturnFloat(double val); +void GB_ReturnPtr(GB_TYPE type, void *value); +void GB_ReturnDate(GB_DATE *date); +void GB_ReturnSelf(void *object); +void GB_ReturnVariant(GB_VARIANT_VALUE *value); + +void GB_ReturnConvVariant(void); +void GB_ReturnBorrow(void); +void GB_ReturnRelease(void); + +void GB_ReturnString(char *str); +void GB_ReturnVoidString(void); +void GB_ReturnConstString(const char *str, int len); +void GB_ReturnConstZeroString(const char *str); +void GB_ReturnNewString(const char *src, int len); +void GB_ReturnNewZeroString(const char *src); + +void *GB_GetClass(void *object); +char *GB_GetClassName(void *object); +void *GB_FindClass(const char *name); +bool GB_ExistClass(const char *name); +bool GB_ExistClassLocal(const char *name); +TYPE GB_GetArrayType(void *klass); + +char *GB_ToZeroString(GB_STRING *src); + +bool GB_LoadFile(const char *path, int lenp, char **addr, int *len); +bool GB_ExistFile(const char *path); +//void GB_ReleaseFile(char **addr, int len); +#define GB_ReleaseFile STREAM_unmap +char *GB_RealFileName(const char *path, int len); +char *GB_TempDir(void); +char *GB_TempFile(const char *pattern); +bool GB_CopyFile(const char *src, const char *dst); +//int GB_FindFile(const char *dir, int recursive, int follow, void (*found)(const char *)); +bool GB_StatFile(const char *path, GB_FILE_STAT *info, bool follow); +void GB_BrowseProject(GB_BROWSE_PROJECT_CALLBACK func); +void GB_BrowseDirectory(const char *dir, GB_BROWSE_CALLBACK before, GB_BROWSE_CALLBACK after); + +int GB_IsMissing(int param); + +void GB_Attach(void *object, void *parent, const char *name); +void GB_Detach(void *object); + +void GB_Store(GB_TYPE type, GB_VALUE *src, void *dst); +void GB_StoreString(GB_STRING *src, char **dst); +void GB_StoreObject(GB_OBJECT *object, void **dst); +void GB_StoreVariant(GB_VARIANT *src, void *dst); +void GB_BorrowValue(GB_VALUE *value); +void GB_ReleaseValue(GB_VALUE *value); + +void GB_Watch(int fd, int flag, void *callback, intptr_t param); + +void *GB_Create(void *class_name, const char *name, void *parent); +void *GB_New(void *class_name, const char *name, void *parent); +bool GB_CheckObject(void *object); +bool GB_Is(void *object, void *class); + +bool GB_GetFunction(GB_FUNCTION *func, void *object, const char *name, const char *sign, const char *type); +GB_VALUE *GB_Call(GB_FUNCTION *func, int nparam, int release); +void *GB_GetClassInterface(void *class, const char *name); + +const char *GB_AppName(void); +const char *GB_AppTitle(void); +const char *GB_AppVersion(void); +const char *GB_AppPath(void); +void *GB_AppStartupClass(void); + +char *GB_SystemCharset(void); +char *GB_SystemDomainName(void); +bool GB_IsRightToLeft(void); +char *GB_SystemPath(void); +bool GB_SystemDebug(void); +void GB_SystemHasForked(void); + +void *GB_Eval(void *, void *); + +void GB_ArrayNew(GB_ARRAY *array, TYPE type, int size); +int GB_ArrayCount(GB_ARRAY array); +void *GB_ArrayAdd(GB_ARRAY array); +void *GB_ArrayGet(GB_ARRAY array, int index); +TYPE GB_ArrayType(GB_ARRAY array); + +void GB_CollectionNew(GB_COLLECTION *col, int mode); +int GB_CollectionCount(GB_COLLECTION col); +bool GB_CollectionSet(GB_COLLECTION col, const char *key, int len, GB_VARIANT *value); +bool GB_CollectionGet(GB_COLLECTION col, const char *key, int len, GB_VARIANT *value); +bool GB_CollectionEnum(GB_COLLECTION col, GB_COLLECTION_ITER *iter, GB_VARIANT *value, char **key, int *len); + +void GB_Alloc(void **addr, int len); +void GB_AllocZero(void **addr, int len); +void GB_Free(void **addr); +void GB_Realloc(void **addr, int len); + +char *GB_NewZeroString(char *src); +char *GB_TempString(char *src, int len); +char *GB_RefString(char *str); +void GB_FreeString(char **str); +int GB_StringLength(const char *str); +bool GB_ConvString(char **result, const char *str, int len, const char *src, const char *dst); + +bool GB_Conv(GB_VALUE *, GB_TYPE); + +DATE_SERIAL *GB_SplitDate(GB_VALUE *); +bool GB_MakeDate(DATE_SERIAL *, GB_DATE *); + +bool GB_NumberToString(int flag, double value, const char *format, char **str, int *len); + +void GB_HashTableNew(GB_HASHTABLE *hash, int mode); +void GB_HashTableAdd(GB_HASHTABLE hash, const char *key, int len, void *data); +void GB_HashTableRemove(GB_HASHTABLE hash, const char *key, int len); +bool GB_HashTableGet(GB_HASHTABLE hash, const char *key, int len, void **data); +void GB_HashTableEnum(GB_HASHTABLE hash, GB_HASHTABLE_ENUM_FUNC func); +bool GB_HashTableFirst(GB_HASHTABLE hash, void **data); + +void GB_NewArray(void *pdata, int size, int count); +int GB_CountArray(void *data); +void *GB_Add(void *pdata); + +GB_STREAM *GB_StreamGet(void *); +//void GB_StreamSetBytesRead(GB_STREAM *, int); +void GB_StreamSetSwapping(GB_STREAM *, int); +void GB_StreamSetAvailableNow(GB_STREAM *, int); +bool GB_StreamBlock(GB_STREAM *, int); +int GB_StreamRead(GB_STREAM *stream, void *addr, int len); +int GB_StreamWrite(GB_STREAM *stream, void *addr, int len); + +int GB_tolower(int c); +int GB_toupper(int c); + +void *GB_DebugGetClass(const char *name); +void *GB_DebugGetExec(void); +void GB_DebugBreakOnError(bool); + +#define GB_PrintString PRINT_string + +#ifndef __GBX_API_C +EXTERN void *GAMBAS_Api[]; +EXTERN void *GAMBAS_DebugApi[]; +EXTERN void *GAMBAS_JitApi[]; +EXTERN unsigned int GAMBAS_MissingParam; +EXTERN bool GAMBAS_DoNotRaiseEvent; +EXTERN bool GAMBAS_StopEvent; +#endif + +#endif + diff --git a/main/gbx/gbx_archive.c b/main/gbx/gbx_archive.c new file mode 100644 index 00000000..bb755c86 --- /dev/null +++ b/main/gbx/gbx_archive.c @@ -0,0 +1,783 @@ +/*************************************************************************** + + gbx_archive.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_ARCHIVE_C + +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_alloc.h" + +#include "gbx_string.h" +#include "gbx_stream.h" +#include "gbx_component.h" +#include "gbx_regexp.h" +#include "gbx_exec.h" +#include "gbx_class.h" +#include "gbx_project.h" + +#include "gbx_archive.h" + +#include "gb_arch_temp.h" + +//#define DEBUG_COMP 1 + +// main archive project (used only if gbx is run with -x flag) +ARCHIVE *ARCHIVE_main = NULL; + +// Additional library search directory used in debug mode, normally '~/.config/share/gambas3/lib' +char *ARCHIVE_path = NULL; + +static char *_local_path = NULL; +static ARCHIVE *_archive_list = NULL; +static char *arch_pattern = NULL; +static int arch_index = 0; +static ARCH *arch_dir = NULL; +static char *arch_prefix = NULL; + +ARCHIVE *ARCHIVE_create(const char *name, const char *path) +{ + ARCHIVE *arch; + + ALLOC_ZERO(&arch, sizeof(ARCHIVE)); + + arch->name = name; + arch->path = path; + + arch->domain = STRING_new_zero(name ? name : "gb"); + + arch->translation_loaded = FALSE; + + TABLE_create(&arch->classes, sizeof(CLASS_SYMBOL), TF_IGNORE_CASE); + + LIST_insert(&_archive_list, arch, &arch->list); + + return arch; +} + +static void error_ARCHIVE_load_exported_class(COMPONENT *current) +{ + COMPONENT_current = current; +} + +void ARCHIVE_load_exported_class(ARCHIVE *arch, int pass) +{ + char *buffer; + int len; + char *name; + CLASS *class; + int i; + COMPONENT *current; + bool optional; + char c; + + if (arch->exported_classes_loaded) + return; + + current = COMPONENT_current; + COMPONENT_current = (COMPONENT *)arch->current_component; + + ON_ERROR_1(error_ARCHIVE_load_exported_class, current) + { + /* COMPONENT_current is set => it will look in the archive */ + + #if DEBUG_COMP + fprintf(stderr, "load_exported_class: %s (component: %s)\n", arch->name, COMPONENT_current ? COMPONENT_current->name : "?"); + #endif + + if (FILE_exist(".list")) + { + if (pass & AR_FIND_ONLY) + { + #if DEBUG_COMP + fprintf(stderr, "\n"); + #endif + + STREAM_load(".list", &buffer, &len); + /* The file must end with a newline !*/ + buffer[len - 1] = 0; + + #if DEBUG_COMP + fprintf(stderr, "-----------\n%s\n----------\n\n", buffer); + #endif + + ARRAY_create(&arch->exported); + + name = strtok(buffer, "\n"); + while (name) + { + #if DEBUG_COMP + fprintf(stderr, "Check %s global\n", name); + #endif + + len = strlen(name); + optional = FALSE; + + for(;;) + { + c = name[len - 1]; + if (c == '?') + { + optional = TRUE; + len--; + } + else if (c == '!') + len--; + else + break; + } + + name[len] = 0; + + /* + class = CLASS_look_global(name, strlen(name)); + + if (class) + { + #if DEBUG_COMP + fprintf(stderr, "...override!\n"); + #endif + CLASS_load(class); + CLASS_check_global(class); + } + else + class = CLASS_find_global(name);*/ + + if (!optional || CLASS_look_global(name, len) == NULL) + { + class = CLASS_find_global(name); + CLASS_check_global(class); + + #if DEBUG_COMP + fprintf(stderr, "Add to load: %p %s\n", class, name); + #endif + class->component = COMPONENT_current; + *((CLASS **)ARRAY_add(&arch->exported)) = class; + } + + name = strtok(NULL, "\n"); + } + + FREE(&buffer); + } + + if (pass & AR_FIND_ONLY) // That way the 'pass' flag is always ignored. + { + #if DEBUG_COMP + fprintf(stderr, "\n"); + #endif + + for (i = 0; i < ARRAY_count(arch->exported); i++) + { + #if DEBUG_COMP + fprintf(stderr, "load %p %s\n", arch->exported[i], arch->exported[i]->name); + #endif + CLASS_load(arch->exported[i]); + } + + ARRAY_delete(&arch->exported); + arch->exported_classes_loaded = TRUE; + } + } + } + END_ERROR + + COMPONENT_current = current; +} + +// Archive path is an absolute path or :/: + +static char *exist_library(const char *dir, const char *name) +{ + char *path; + char *n; + char *p; + + n = STRING_new_zero(name); + + path = (char *)FILE_cat(dir, n, NULL); + if (FILE_exist(path)) + goto __RETURN_PATH; + + n = STRING_add(n, ".gambas", -1); + path = (char *)FILE_cat(dir, n, NULL); + if (FILE_exist(path)) + goto __RETURN_PATH; + + p = rindex(n, ':'); + if (p) + { + strcpy(p, ".gambas"); + path = (char *)FILE_cat(dir, n, NULL); + if (FILE_exist(path)) + goto __RETURN_PATH; + } + + path = NULL; + +__RETURN_PATH: + + STRING_free(&n); + return path; +} + +void ARCHIVE_load(ARCHIVE *arch, bool load_exp) +{ + char *path; + char *dir; + const char *name; + char *env; + + if (arch->path) + { + if (*arch->path == ':') + { + if (!_local_path) + { + env = getenv("XDG_DATA_HOME"); + if (env && *env) + _local_path = STRING_new_zero(FILE_cat(env, "gambas3/lib", NULL)); + else + _local_path = STRING_new_zero(FILE_cat(FILE_get_home(), ".local/share/gambas3/lib", NULL)); + } + + name = &arch->path[1]; + } + else + name = FILE_get_name(arch->path); + + // For backward-compatibility, library name is searched in the current folder too, without the vendor. + + if (!(path = exist_library(PROJECT_path, FILE_get_name(name)))) + { + if (!_local_path || !(path = exist_library(_local_path, name))) + { + if (!ARCHIVE_path || !(path = exist_library(ARCHIVE_path, name))) + { + if (!(path = exist_library(COMPONENT_path, name))) + { + dir = STRING_new_zero(FILE_cat(PROJECT_exec_path, "bin", NULL)); + path = exist_library(dir, name); + STRING_free(&dir); + } + } + } + } + + if (!path || !FILE_exist(path)) + THROW(E_LIBRARY, arch->name, "cannot find library"); + } + else + { + path = FILE_buffer(); + sprintf(path, ARCH_PATTERN, COMPONENT_path, arch->name); + } + + arch->arch = ARCH_open(path); + arch->current_component = COMPONENT_current; + + if (load_exp) + ARCHIVE_load_exported_class(arch, AR_FIND_AND_LOAD); //, dep); +} + + +void ARCHIVE_create_main(const char *path) +{ + ARCHIVE_main = ARCHIVE_create(NULL, NULL); + + if (path) + ARCHIVE_main->arch = ARCH_open(path); + else + ARCHIVE_main->arch = NULL; +} + + +void ARCHIVE_load_main() +{ + ARCHIVE_load_exported_class(ARCHIVE_main, AR_FIND_AND_LOAD); +} + + +void ARCHIVE_delete(ARCHIVE *arch) +{ + LIST_remove(&_archive_list, arch, &arch->list); + + if (arch->arch) + ARCH_close(arch->arch); + + TABLE_delete(&arch->classes); + STRING_free(&arch->domain); + STRING_free(&arch->version); + + FREE(&arch); +} + + +void ARCHIVE_init(void) +{ +} + + +void ARCHIVE_exit(void) +{ + if (ARCHIVE_main) + ARCHIVE_delete(ARCHIVE_main); + + STRING_free(&arch_pattern); + STRING_free(&arch_prefix); + STRING_free(&ARCHIVE_path); + STRING_free(&_local_path); +} + + +/* ### *parch must be initialized to NULL or a valid archive */ +/* Returns a true archive, never the main archive if we are not running an executable */ +bool ARCHIVE_find_from_path(ARCHIVE **parch, const char **ppath) +{ + int i; + CLASS *class; + const char *path = *ppath; + const char *p; + + if (*parch) + return FALSE; + + if (COMPONENT_current && COMPONENT_current->archive) + *parch = COMPONENT_current->archive; + else if (CP && CP->component && CP->component->archive) + *parch = CP->component->archive; + else + *parch = NULL; + + //fprintf(stderr, "ARCHIVE_find_from_path: %s (%s)\n", *ppath, *parch ? (*parch)->name : "NULL"); + + if (strncmp(path, "./", 2) == 0 && path[2]) + { + path += 2; + p = index(path, '/'); + if (p) + { + int len = p - path; + char name[len + 1]; + COMPONENT *comp; + + strncpy(name, path, len); + name[len] = 0; + + comp = COMPONENT_find(name); + if (comp && comp->archive) + { + *parch = comp->archive; + *ppath = p + 1; + return FALSE; + } + } + } + + if (strncmp(path, ".../", 4) == 0) + { + path += 4; + *parch = NULL; + } + else + { + i = 0; + while (strncmp(path, "../", 3) == 0) + { + path += 3; + if (*parch == NULL || *parch == ARCHIVE_main) + continue; + + while (i < STACK_frame_count) + { + class = STACK_frame[i].cp; + //fprintf(stderr, "[%d] %s / %s\n", i, class ? class->name : "NULL", class && class->component && class->component->archive ? class->component->archive->name : "NULL"); + if (class) + { + if (!class->component) + { + *parch = NULL; + break; + } + if (class->component && class->component->archive != *parch) + { + *parch = class->component->archive; + break; + } + } + + i++; + } + + if (i == STACK_frame_count) + *parch = NULL; + } + } + + if (*parch == NULL && EXEC_arch) + *parch = ARCHIVE_main; + + //fprintf(stderr, "--> '%s' / %s\n", *parch ? (*parch)->name : "(null)", *ppath); + + *ppath = path; + return *parch == NULL; +} + +/* Can return the main archive even if we are not running an executable */ +bool ARCHIVE_get_current(ARCHIVE **parch) +{ + if (COMPONENT_current && COMPONENT_current->archive) + *parch = COMPONENT_current->archive; + else if (CP && CP->component && CP->component->archive) + *parch = CP->component->archive; + else + *parch = ARCHIVE_main; + + return *parch == NULL; +} + + +bool ARCHIVE_get(ARCHIVE *arch, const char **ppath, ARCHIVE_FIND *find) +{ + ARCH_FIND f; + struct stat buf; + + if (!*ppath || **ppath == 0) + return TRUE; + + if (ARCHIVE_find_from_path(&arch, ppath)) + { + // no archive found, we try a lstat + if (!PROJECT_path) + return TRUE; + FILE_chdir(PROJECT_path); + if (stat(*ppath, &buf)) + return TRUE; + + find->pos = S_ISDIR(buf.st_mode) ? -1 : 0; + find->sym = NULL; + find->len = (int)buf.st_size; + find->index = -1; + find->arch = NULL; + return FALSE; + } + + if (ARCH_find(arch->arch, *ppath, 0, &f)) + return TRUE; + + find->sym = f.sym; + find->pos = f.pos; + find->len = f.len; + find->index = f.index; + find->arch = arch; + + return FALSE; +} + + +bool ARCHIVE_exist(ARCHIVE *arch, const char *path) +{ + ARCHIVE_FIND find; + + //if (get_current(&arch, &path)) + // return FALSE; + + return (!ARCHIVE_get(arch, &path, &find)); +} + + +bool ARCHIVE_is_dir(ARCHIVE *arch, const char *path) +{ + ARCHIVE_FIND find; + + if (ARCHIVE_get(arch, &path, &find)) + return FALSE; + + return (find.pos < 0); +} + + +void ARCHIVE_stat(ARCHIVE *arch, const char *path, FILE_STAT *info) +{ + ARCHIVE_FIND find = { 0 }; + struct stat buf; + + //if (get_current(&arch)) + // THROW_SYSTEM(ENOENT, path); + + if (ARCHIVE_get(arch, &path, &find)) + THROW_SYSTEM(ENOENT, path); + + if (find.arch) + fstat(find.arch->arch->fd, &buf); + else + stat(PROJECT_path, &buf); + + info->type = find.pos < 0 ? GB_STAT_DIRECTORY : GB_STAT_FILE; + info->mode = 0400; + + info->size = find.len; + info->atime = (int)buf.st_mtime; + info->mtime = (int)buf.st_mtime; + info->ctime = (int)buf.st_mtime; + info->hidden = (*FILE_get_name(path) == '.'); + info->uid = (int)buf.st_uid; + info->gid = (int)buf.st_gid; +} + + +void ARCHIVE_read(ARCHIVE *arch, int pos, void *buffer, int len) +{ + ARCH_read(arch->arch, pos, buffer, len); +} + + +void ARCHIVE_dir_first(ARCHIVE *arch, const char *path, const char *pattern, int attr) +{ + ARCHIVE_FIND find; + char abs_path[16]; + int abs_len; + + /*if (get_current(&arch, &path)) + { + arch_dir = NULL; + return; + }*/ + + if (ARCHIVE_get(arch, &path, &find)) + { + arch_dir = NULL; + return; + } + + // ?? "." means that we want to browse the archive. + if (!find.sym && !find.arch) // && !(path[0] == '.' && path[1] == 0)) + { + // By calling FILE_dir_first() again with an absolute path, we are sure that next calls to + // FILE_dir_next() will never call ARCHIVE_dir_next(). + FILE_dir_first(FILE_cat(PROJECT_path, path, NULL), pattern, attr); + return; + } + + if (pattern == NULL || !*pattern) + pattern = "*"; + + arch = find.arch; + arch_dir = arch->arch; + arch_index = 0; + + STRING_free(&arch_pattern); + arch_pattern = STRING_new_zero(pattern); + + //if (arch_dir->header.version == 2) + //{ + if (find.index >= 0) + abs_len = sprintf(abs_path, "/%d:", find.index); + else + abs_len = 0; + /*} + else + { + ARCH_get_absolute_path(path, strlen(path), abs_path, &abs_len); + + if (abs_len && abs_path[abs_len - 1] != '/') + { + abs_path[abs_len] = '/'; + abs_len++; + } + }*/ + + STRING_free(&arch_prefix); + if (abs_len) + arch_prefix = STRING_new(abs_path, abs_len); +} + + +bool ARCHIVE_dir_next(char **name, int *len, int attr) +{ + ARCH_SYMBOL *asym; + SYMBOL *sym; + char *s = NULL; + int l = 0; + int lenp; + + /*if (arch_fd < 0) + return FILE_dir_next(name, len);*/ + + if (!arch_dir) + return TRUE; + + lenp = STRING_length(arch_prefix); + + for(;;) + { + if (arch_index >= arch_dir->header.n_symbol) + return TRUE; + + //sym = &arch_dir->symbol[arch_index].sym; + asym = &arch_dir->symbol[arch_dir->sort[arch_index]]; + sym = &asym->sym; + + if (arch_pattern == NULL) + break; + + arch_index++; + + if (attr == GB_STAT_DIRECTORY && (asym->pos >= 0)) + continue; + + if (sym->len < lenp) + continue; + + if (lenp) + { + if (strncmp(sym->name, arch_prefix, lenp)) + continue; + } + else // special case: root directory when header.version == 2 + { + if (!strncmp(sym->name, "/", 1)) + continue; + } + + s = sym->name + lenp; + l = sym->len - lenp; + + if (l < 0) + continue; + + if (memchr(s, '/', l)) + continue; + + if (!REGEXP_match(arch_pattern, STRING_length(arch_pattern), s, l)) + continue; + + break; + } + + *name = s; + *len = l; + return FALSE; +} + + +bool ARCHIVE_check_addr(char *addr) +{ + ARCHIVE *arch; + ARCH *a; + + LIST_for_each(arch, _archive_list) + { + a = arch->arch; + if (a && addr >= a->addr && addr < &a->addr[a->length]) + return FALSE; + } + + return TRUE; +} + +void ARCHIVE_browse(ARCHIVE *arch, void (*found)(const char *path, int64_t size)) +{ + int i; + ARCH_SYMBOL *asym; + SYMBOL *sym; + ARCH *a = arch->arch; + char *path; + char *temp; + int size; + int ip; + + for (i = 0; i < a->header.n_symbol; i++) + { + asym = &a->symbol[i]; + sym = &asym->sym; + + size = asym->len; + + path = STRING_new(sym->name, sym->len); + for(;;) + { + if (*path != '/') + break; + + ip = atoi(&path[1]); + sym = &a->symbol[ip].sym; + + temp = path; + path = STRING_new(sym->name, sym->len); + if (path[sym->len - 1] != '/') + path = STRING_add(path, "/", 1); + path = STRING_add(path, strchr(temp, ':') + 1, 0); + STRING_free(&temp); + } + + (*found)(path, size); + STRING_free(&path); + } +} + +char *ARCHIVE_get_version(ARCHIVE *arch) +{ + COMPONENT *current; + char *buffer; + int len; + int n; + char *line; + + if (!arch->version_loaded) + { + current = COMPONENT_current; + COMPONENT_current = (COMPONENT *)arch->current_component; + + ON_ERROR_1(error_ARCHIVE_load_exported_class, current) + { + STREAM_load(".startup", &buffer, &len); + + n = 0; + line = strtok(buffer, "\n"); + while (line) + { + n++; + if (n == 5) + { + arch->version = STRING_new_zero(line); + break; + } + line = strtok(NULL, "\n"); + } + + FREE(&buffer); + } + END_ERROR + + COMPONENT_current = current; + arch->version_loaded = TRUE; + } + + return arch->version; +} diff --git a/main/gbx/gbx_archive.h b/main/gbx/gbx_archive.h new file mode 100644 index 00000000..e6f0655e --- /dev/null +++ b/main/gbx/gbx_archive.h @@ -0,0 +1,103 @@ +/*************************************************************************** + + gbx_archive.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_ARCHIVE_H +#define __GBX_ARCHIVE_H + +#define PROJECT_EXEC + +#include "gb_table.h" +#include "gb_file.h" +#include "gb_arch.h" +#include "gb_list.h" + +// ARCHIVE_load_exported_class() pass argument +#define AR_FIND_ONLY 1 +#define AR_LOAD_ONLY 2 +#define AR_FIND_AND_LOAD 3 + +typedef + struct { + LIST list; + ARCH *arch; + const char *name; + char *version; + char *domain; + TABLE *classes; + const char *path; + void *current_component; + struct _CLASS **exported; + void *jit_library; + unsigned translation_loaded : 1; + unsigned exported_classes_loaded : 1; + unsigned version_loaded : 1; + unsigned jit_compiling : 1; + } + ARCHIVE; + +typedef + struct { + ARCHIVE *arch; + ARCH_SYMBOL *sym; + int index; + int pos; + int len; + } + ARCHIVE_FIND; + +#ifndef __ARCHIVE_C +EXTERN ARCHIVE *ARCHIVE_main; +EXTERN char *ARCHIVE_path; +#endif + +void ARCHIVE_init(void); +void ARCHIVE_exit(void); + +void ARCHIVE_create_main(const char *path); +void ARCHIVE_load_main(void); + +ARCHIVE *ARCHIVE_create(const char *name, const char *path); +void ARCHIVE_delete(ARCHIVE *arch); +void ARCHIVE_load(ARCHIVE *arch, bool load_exp); +void ARCHIVE_load_exported_class(ARCHIVE *arch, int pass); +char *ARCHIVE_get_version(ARCHIVE *arch); + +bool ARCHIVE_get(ARCHIVE *arch, const char **ppath, ARCHIVE_FIND *find); + +void ARCHIVE_read(ARCHIVE *arch, int pos, void *buffer, int len); + +bool ARCHIVE_exist(ARCHIVE *arch, const char *path); +void ARCHIVE_stat(ARCHIVE *arch, const char *path, FILE_STAT *info); +bool ARCHIVE_is_dir(ARCHIVE *arch, const char *path); + +void ARCHIVE_dir_first(ARCHIVE *arch, const char *path, const char *pattern, int attr); +bool ARCHIVE_dir_next(char **name, int *len, int attr); + +bool ARCHIVE_find_from_path(ARCHIVE **parch, const char **ppath); +bool ARCHIVE_get_current(ARCHIVE **parch); + +bool ARCHIVE_check_addr(char *addr); + +void ARCHIVE_browse(ARCHIVE *arch, void (*found)(const char *path, int64_t size)); + +#endif diff --git a/main/gbx/gbx_c_application.c b/main/gbx/gbx_c_application.c new file mode 100644 index 00000000..73b2d0fe --- /dev/null +++ b/main/gbx/gbx_c_application.c @@ -0,0 +1,347 @@ +/*************************************************************************** + + gbx_c_application.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_APPLICATION_C + +#include "gambas.h" +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gbx_api.h" +#include "gbx_class.h" +#include "gbx_project.h" +#include "gbx_local.h" +#include "gbx_event.h" +#include "gbx_string.h" +#include "gbx_exec.h" +#include "gbx_extern.h" +#include "gbx_object.h" + +#include "gbx_c_application.h" + +extern char **environ; + +static bool _daemon = FALSE; + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Application_Path) + + GB_ReturnString(PROJECT_path); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Name) + + GB_ReturnConstZeroString(PROJECT_name); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Title) + + GB_ReturnConstZeroString(LOCAL_gettext(PROJECT_title)); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Id) + + GB_ReturnInt(getpid()); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Dir) + + GB_ReturnString(PROJECT_oldcwd); + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Version) + + GB_ReturnConstZeroString(PROJECT_version); + +END_PROPERTY + + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Application_Args_Count) + + GB_ReturnInt(PROJECT_argc); + +END_PROPERTY + +BEGIN_PROPERTY(Application_Args_Max) + + GB_ReturnInt(PROJECT_argc - 1); + +END_PROPERTY + + +BEGIN_METHOD(Application_Args_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0) + { + GB_Error((char *)E_ARG); + return; + } + + if (index >= PROJECT_argc) + GB_ReturnVoidString(); + else + GB_ReturnConstZeroString(PROJECT_argv[index]); + +END_METHOD + + +BEGIN_METHOD_VOID(Application_Args_next) + + int *index = (int*)GB_GetEnum(); + + if (*index >= PROJECT_argc) + GB_StopEnum(); + else + { + GB_ReturnConstZeroString(PROJECT_argv[*index]); + (*index)++; + } + +END_METHOD + + +BEGIN_PROPERTY(Application_Args_All) + + GB_ARRAY array; + int i; + char *arg; + + GB_ArrayNew(&array, GB_T_STRING, PROJECT_argc); + for (i = 0; i < PROJECT_argc; i++) + { + arg = PROJECT_argv[i]; + if (arg && *arg) + *(char **)GB_ArrayGet(array, i) = STRING_new_zero(arg); + } + + GB_ReturnObject(array); + +END_PROPERTY + +/*BEGIN_PROPERTY(Application_Args_Name) + + if (READ_PROPERTY) + GB_ReturnConstZeroString(PROJECT_argv[0]); + else + { + GB_StoreString(PROP(GB_STRING), &PROJECT_argname); + PROJECT_argv[0] = PROJECT_argname; + } + +END_PROPERTY*/ + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Application_Env_Count) + + int n = 0; + + while(environ[n]) + n++; + + GB_ReturnInt(n); + +END_PROPERTY + + +BEGIN_METHOD(Application_Env_get, GB_STRING key) + + GB_ReturnNewZeroString(getenv(GB_ToZeroString(ARG(key)))); + +END_METHOD + + +BEGIN_METHOD(Application_Env_put, GB_STRING value; GB_STRING key) + + setenv(GB_ToZeroString(ARG(key)), GB_ToZeroString(ARG(value)), 1); + +END_METHOD + + +BEGIN_METHOD_VOID(Application_Env_next) + + int *index = (int*)GB_GetEnum(); + char *pos; + char *key; + + if (environ[*index] == NULL) + GB_StopEnum(); + else + { + key = environ[*index]; + pos = strchr(key, '='); + if (!pos) + GB_ReturnVoidString(); + else + GB_ReturnConstString(key, pos - key); + (*index)++; + } + +END_METHOD + +//------------------------------------------------------------------------- + +static void init_again(int old_pid) +{ + char old[PATH_MAX]; + + FILE_remove_temp_file(); + snprintf(old, sizeof(old),FILE_TEMP_DIR, getuid(), old_pid); + rename(old, FILE_make_temp(NULL, NULL)); +} + +BEGIN_PROPERTY(Application_Daemon) + + int old_pid; + + if (READ_PROPERTY) + GB_ReturnBoolean(_daemon); + else + { + if (!_daemon && VPROP(GB_BOOLEAN)) + { + old_pid = getpid(); + if (daemon(FALSE, FALSE)) + THROW_SYSTEM(errno, NULL); + // Argh! daemon() changes the current process id... + _daemon = TRUE; + init_again(old_pid); + } + } + +END_PROPERTY + + +BEGIN_PROPERTY(Application_Startup) + + GB_ReturnObject(PROJECT_class); + +END_PROPERTY + +BEGIN_PROPERTY(Application_Priority) + + int pr; + + if (READ_PROPERTY) + { + errno = 0; + pr = getpriority(PRIO_PROCESS, 0); + if (pr == -1 && errno > 0) + THROW_SYSTEM(errno, NULL); + GB_ReturnInteger(pr); + } + else + { + pr = VPROP(GB_INTEGER); + + if (pr < -20) + pr = -20; + else if (pr > 19) + pr = 19; + + if (setpriority(PRIO_PROCESS, 0, VPROP(GB_INTEGER)) < 0) + THROW_SYSTEM(errno, NULL); + } + +END_PROPERTY + +#endif + +//------------------------------------------------------------------------- + +GB_DESC NATIVE_AppArgs[] = +{ + GB_DECLARE_STATIC("Args"), + + GB_STATIC_PROPERTY_READ("Count", "i", Application_Args_Count), + GB_STATIC_PROPERTY_READ("Max", "i", Application_Args_Max), + GB_STATIC_PROPERTY_READ("All", "String[]", Application_Args_All), + GB_STATIC_METHOD("_get", "s", Application_Args_get, "(Index)i"), + GB_STATIC_METHOD("_next", "s", Application_Args_next, NULL), + //GB_STATIC_PROPERTY("Name", "s", Application_Args_Name), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_AppEnv[] = +{ + GB_DECLARE_STATIC("Env"), + + GB_STATIC_PROPERTY_READ("Count", "i", Application_Env_Count), + GB_STATIC_METHOD("_get", "s", Application_Env_get, "(Key)s"), + GB_STATIC_METHOD("_put", NULL, Application_Env_put, "(Value)s(Key)s"), + GB_STATIC_METHOD("_next", "s", Application_Env_next, NULL), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_App[] = +{ + GB_DECLARE_STATIC("Application"), + + GB_STATIC_PROPERTY_SELF("Args", "Args"), + GB_STATIC_PROPERTY_SELF("Env", "Env"), + GB_STATIC_PROPERTY_READ("Path", "s", Application_Path), + GB_STATIC_PROPERTY_READ("Name", "s", Application_Name), + GB_STATIC_PROPERTY_READ("Title", "s", Application_Title), + GB_STATIC_PROPERTY_READ("Id", "i", Application_Id), + GB_STATIC_PROPERTY_READ("Handle", "i", Application_Id), + GB_STATIC_PROPERTY_READ("Version", "s", Application_Version), + GB_STATIC_PROPERTY_READ("Dir", "s", Application_Dir), + GB_STATIC_PROPERTY("Daemon", "b", Application_Daemon), + GB_STATIC_PROPERTY_READ("Startup", "Class", Application_Startup), + GB_STATIC_PROPERTY("Priority", "i", Application_Priority), + + GB_END_DECLARE +}; + diff --git a/main/gbx/gbx_c_application.h b/main/gbx/gbx_c_application.h new file mode 100644 index 00000000..448328fd --- /dev/null +++ b/main/gbx/gbx_c_application.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + gbx_c_application.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_APPLICATION_H +#define __GBX_C_APPLICATION_H + +#include "gambas.h" + +#ifndef __GBX_C_APPLICATION_C +extern GB_DESC NATIVE_AppEnv[]; +extern GB_DESC NATIVE_AppArgs[]; +extern GB_DESC NATIVE_App[]; +#endif + +#endif diff --git a/main/gbx/gbx_c_array.c b/main/gbx/gbx_c_array.c new file mode 100644 index 00000000..60a20167 --- /dev/null +++ b/main/gbx/gbx_c_array.c @@ -0,0 +1,2176 @@ +/*************************************************************************** + + gbx_c_array.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_ARRAY_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_array.h" +#include "gb_limit.h" +#include "gbx_class.h" + +#include "gbx_exec.h" +#include "gbx_date.h" +#include "gbx_variant.h" +#include "gbx_compare.h" +#include "gbx_class.h" +#include "gbx_object.h" +#include "gbx_api.h" +#include "gbx_c_file.h" +#include "gbx_struct.h" +#include "gbx_c_array.h" + +static bool _create_static_array; + + +static void THROW_static_array() +{ + GB_Error((char *)E_SARRAY); + return; +} + + +static bool check_not_multi(CARRAY *_object) +{ + if (UNLIKELY(THIS->ref != NULL)) + { + THROW_static_array(); + return TRUE; + } + else if (UNLIKELY(THIS->dim != NULL)) + { + GB_Error("Array is multi-dimensional"); + return TRUE; + } + else + return FALSE; +} + +static bool check_start_length(int count, int *start, int *length) +{ + if (*start < 0) + { + GB_Error((char *)E_BOUND); + return TRUE; + } + + if (*start >= count) + *length = 0; + + if (*length == -1) + *length = count - *start; + + if (*length < 0 || (*start + *length) > count) + { + GB_Error((char *)E_BOUND); + return TRUE; + } + + return FALSE; +} + +static int get_dim(CARRAY *_object) +{ + int d; + + if (THIS->dim == NULL) + return 1; + else + { + for(d = 0;; d++) + { + if (THIS->dim[d] < 0) + return (d + 1); + } + } +} + + +static int get_bound(CARRAY *_object, int d) +{ + if (THIS->dim == NULL) + return THIS->count; + else + { + d = THIS->dim[d]; + if (d < 0) + d = (-d); + + return d; + } +} + + +CLASS *CARRAY_get_array_class(CLASS *class, CTYPE ctype) +{ + if (ctype.id == T_NULL) + { + if (TYPE_is_pure_object((TYPE)class)) + return CLASS_get_array_class(class); + + ctype.id = (unsigned char)(TYPE)class; + ctype.value = -1; + } + + switch (ctype.id) + { + case T_BOOLEAN: class = CLASS_BooleanArray; break; + case T_BYTE: class = CLASS_ByteArray; break; + case T_SHORT: class = CLASS_ShortArray; break; + case T_INTEGER: class = CLASS_IntegerArray; break; + case T_LONG: class = CLASS_LongArray; break; + case T_SINGLE: class = CLASS_SingleArray; break; + case T_FLOAT: class = CLASS_FloatArray; break; + case T_STRING: class = CLASS_StringArray; break; + case T_DATE: class = CLASS_DateArray; break; + case T_POINTER: class = CLASS_PointerArray; break; + + case T_OBJECT: + if (ctype.value >= 0) + class = CLASS_get_array_class(class->load->class_ref[ctype.value]); + else + class = CLASS_ObjectArray; + break; + + case TC_STRUCT: + class = CLASS_get_array_of_struct_class(class->load->class_ref[ctype.value]); + break; + + default: + class = CLASS_VariantArray; + break; + } + + return class; +} + + +void *CARRAY_out_of_bounds() +{ + GB_Error((char *)E_BOUND); + return NULL; +} + + +#define get_data_multi CARRAY_get_data_multi +#define get_data CARRAY_get_data + +void *CARRAY_get_data_multi(CARRAY *_object, GB_INTEGER *arg, int nparam) +{ + int index; + + //fprintf(stderr, "get_data_multi: nparam = %d\n", nparam); + + if (UNLIKELY(THIS->dim != NULL)) + { + int max; + int i; + int d; + bool stop = FALSE; + + index = 0; + nparam--; + + for (i = 0;; i++) + { + if (i > nparam) + break; + + max = THIS->dim[i]; + if (max < 0) + { + max = (-max); + stop = TRUE; + } + + VALUE_conv_integer((VALUE *)&arg[i]); + d = arg[i].value; + + if (d < 0 || d >= max) + { + GB_Error((char *)E_BOUND); + return NULL; + } + + index *= max; + index += d; + + if (stop) + break; + } + + if (i != nparam) + { + GB_Error((char *)E_NDIM); + return NULL; + } + } + else + { + if (nparam != 1) + { + GB_Error((char *)E_NDIM); + return NULL; + } + + index = arg->value; + + if ((index < 0) || (index >= THIS->count)) + { + GB_Error((char *)E_BOUND); + return NULL; + } + } + + return (void *)((char *)(THIS->data) + index * THIS->size); +} + + +static int get_count(int *dim) +{ + int i, size; + + size = 1; + + for (i = 0;; i++) + { + size *= dim[i]; + if (size < 0) + { + size = (-size); + break; + } + } + + return size; +} + + +static void release_one(CARRAY *_object, int i) +{ + if (THIS->type == T_STRING) + STRING_unref(&(((char **)(THIS->data))[i])); + else if (THIS->type == T_VARIANT) + VARIANT_free(&(((VARIANT *)(THIS->data))[i])); + else if (TYPE_is_object(THIS->type)) + OBJECT_UNREF(((void **)(THIS->data))[i]); +} + +static void release_static(TYPE type, void *data, int start, int end) +{ + int i; + + if (type == T_STRING) + { + for (i = start; i < end; i++) + STRING_unref(&((char **)data)[i]); + } + else if (type == T_VARIANT) + { + for (i = start; i < end; i++) + VARIANT_free(&((VARIANT *)data)[i]); + } + else if (TYPE_is_object(type)) + { + for (i = start; i < end; i++) + OBJECT_UNREF(((void **)data)[i]); + } +} + + +static void release(CARRAY *_object, int start, int end) +{ + if (end < 0) + end = THIS->count; + + release_static(THIS->type, THIS->data, start, end); +} + +static void borrow(CARRAY *_object, int start, int end) +{ + int i; + + if (end < 0) + end = THIS->count; + + if (THIS->type == T_STRING) + { + for (i = start; i < end; i++) + STRING_ref(((char **)(THIS->data))[i]); + } + else if (THIS->type == T_VARIANT) + { + for (i = start; i < end; i++) + VARIANT_keep(&((VARIANT *)(THIS->data))[i]); + } + else if (TYPE_is_object(THIS->type)) + { + for (i = start; i < end; i++) + OBJECT_ref(((void **)(THIS->data))[i]); + } +} + + +static void clear(CARRAY *_object) +{ + if (UNLIKELY(THIS->ref != NULL)) + { + THROW_static_array(); + return; + } + + release(THIS, 0, -1); + if (UNLIKELY(THIS->dim != NULL)) + { + memset(THIS->data, 0, THIS->size * THIS->count); + } + else + { + ARRAY_delete(&THIS->data); + ARRAY_create_with_size(&THIS->data, THIS->size, 8); + THIS->count = 0; + } +} + + +static void *insert(CARRAY *_object, int index) +{ + THIS->count++; + return ARRAY_insert(&THIS->data, index); +} + + +size_t CARRAY_get_static_size(CLASS *class, CLASS_ARRAY *desc) +{ + return (size_t)get_count(desc->dim) * CLASS_sizeof_ctype(class, desc->ctype); +} + +int CARRAY_get_static_count(CLASS_ARRAY *desc) +{ + return get_count(desc->dim); +} + + +CARRAY *CARRAY_create_static(CLASS *class, void *ref, CLASS_ARRAY *desc, void *data) +{ + CARRAY *array; + TYPE type; + CLASS *aclass; + + type = CLASS_ctype_to_type(class, desc->ctype); + _create_static_array = TRUE; + aclass = CARRAY_get_array_class(class, desc->ctype); + array = OBJECT_create_native(aclass, NULL); + _create_static_array = FALSE; + + array->type = type; + array->data = data; + array->ref = ref; + array->count = get_count(desc->dim); + OBJECT_REF(ref); + array->dim = desc->dim; + array->size = CLASS_sizeof_ctype(class, desc->ctype); + + return array; +} + +void CARRAY_release_static(CLASS *class, CLASS_ARRAY *desc, void *data) +{ + int count = get_count(desc->dim); + + if (desc->ctype.id == TC_STRUCT) + { + CLASS *sclass = class->load->class_ref[desc->ctype.value]; + int i; + int size = CLASS_sizeof_ctype(class, desc->ctype); + char *p = (char *)data; + + for (i = 0; i < count; i++) + { + OBJECT_release_static(sclass, sclass->load->dyn, sclass->load->n_dyn, p); + p += size; + } + } + else + release_static(CLASS_ctype_to_type(class, desc->ctype), data, 0, count); +} + +void CARRAY_get_value(CARRAY *_object, int index, VALUE *value) +{ + VALUE_read(value, get_data(THIS, index), THIS->type); +} + +int *CARRAY_get_array_bounds(CARRAY *_object) +{ + return THIS->dim; +} + +static void check_size(CARRAY *_object, int size, int inc) +{ + if (inc > 0) + size = (size + inc - 1) / inc * inc; + + if (size > (INT_MAX / THIS->size)) + THROW(E_MEMORY); +} + +BEGIN_METHOD(Array_new, GB_INTEGER size) + + TYPE type; + CLASS *klass; + int inc; + GB_INTEGER *sizes = ARG(size); + int nsize = GB_NParam() + 1; + int i; + + if (_create_static_array) + return; + + klass = OBJECT_class(THIS); + + type = (TYPE)klass->array_type; + if (!type) + { + GB_Error("Bad array type"); + return; + } + + /*if (TYPE_is_object(type)) + THIS->mode = T_OBJECT; + else + THIS->mode = (int)type;*/ + + //printf("Array_new: type = %d nsize = %d\n", type, nsize); + + THIS->type = type; + THIS->size = TYPE_sizeof_memory(type); + + if (nsize <= 1) + { + int size = VARGOPT(size, 0); + + if (size < 0) + size = 0; + + inc = (size / 8) & ~7; + if (inc < 8) + inc = 8; + else if (inc > 256) + inc = 256; + + if (size) + check_size(THIS, size, inc); + + ARRAY_create_with_size(&THIS->data, THIS->size, inc); + if (size > 0) + ARRAY_add_many_void(&THIS->data, size); + + THIS->count = size; + } + else + { + if (nsize > MAX_ARRAY_DIM) + { + GB_Error("Too many dimensions"); + return; + } + + uint64_t size = 1; + for (i = 0; i < nsize; i++) + { + VALUE_conv_integer((VALUE *)&sizes[i]); + if (sizes[i].value < 1) + { + GB_Error("Bad dimension"); + return; + } + size *= sizes[i].value; + check_size(THIS, size, 0); + } + + ALLOC_ZERO(&THIS->dim, nsize * sizeof(int)); + + for (i = 0; i < nsize; i++) + THIS->dim[i] = sizes[i].value; + THIS->dim[nsize - 1] = (-THIS->dim[nsize - 1]); + + ARRAY_create_with_size(&THIS->data, THIS->size, 8); + ARRAY_add_many_void(&THIS->data, (int)size); + + THIS->count = size; + } + + +END_METHOD + + +BEGIN_PROPERTY(Array_Type) + + GB_ReturnInteger(THIS->type); + +END_PROPERTY + +BEGIN_METHOD_VOID(Array_free) + + if (UNLIKELY(THIS->ref != NULL)) + { + OBJECT_UNREF(THIS->ref); + return; + } + + release(THIS, 0, -1); + + ARRAY_delete(&THIS->data); + + FREE(&THIS->dim); + +END_METHOD + + +BEGIN_METHOD_VOID(Array_Clear) + + clear(THIS); + +END_METHOD + + +BEGIN_PROPERTY(Array_Data) + + GB_ReturnPointer(THIS->data); + +END_PROPERTY + + +BEGIN_PROPERTY(Array_Count) + + GB_ReturnInt(THIS->count); + +END_PROPERTY + + +BEGIN_PROPERTY(Array_Max) + + GB_ReturnInt(THIS->count - 1); + +END_PROPERTY + + +static bool copy_remove(CARRAY *_object, int start, int length, bool copy, bool remove) +{ + CARRAY *array; + int count = THIS->count; + void *data; + int i, nsize; + + if (check_not_multi(THIS) && (start != 0 || length != -1)) + return TRUE; + + if (check_start_length(count, &start, &length)) + return TRUE; + + if (copy) + { + GB_ArrayNew((GB_ARRAY *)POINTER(&array), THIS->type, 0); + + if (length > 0) + { + data = ARRAY_insert_many(&array->data, 0, length); + array->count += length; + memmove(data, get_data(THIS, start), length * THIS->size); + borrow(array, 0, -1); + } + + if (THIS->dim) + { + nsize = get_dim(THIS); + ALLOC_ZERO(&array->dim, nsize * sizeof(int)); + + for (i = 0; i < nsize; i++) + array->dim[i] = THIS->dim[i]; + } + } + + if (remove) + { + release(THIS, start, start + length); + ARRAY_remove_many(&THIS->data, start, length); + THIS->count -= length; + } + + if (copy) + GB_ReturnObject(array); + + return FALSE; +} + +BEGIN_METHOD(Array_Remove, GB_INTEGER index; GB_INTEGER length) + + copy_remove(THIS, VARG(index), VARGOPT(length, 1), FALSE, TRUE); + +END_METHOD + + +BEGIN_METHOD(Array_Copy, GB_INTEGER start; GB_INTEGER length) + + int start, length; + + if (MISSING(start) && MISSING(length)) + { + start = 0; + length = -1; + } + else + { + start = VARGOPT(start, 0); + length = VARGOPT(length, 1); + } + + copy_remove(THIS, start, length, TRUE, FALSE); + +END_METHOD + + +BEGIN_METHOD(Array_Extract, GB_INTEGER start; GB_INTEGER length) + + copy_remove(THIS, VARG(start), VARGOPT(length, 1), TRUE, TRUE); + +END_METHOD + + +void CARRAY_resize(CARRAY *_object, int size) +{ + int count; + + if (size < 0) + { + GB_Error((char *)E_ARG); + return; + } + + count = THIS->count; + if (size == count) + return; + + if (size > count) + { + check_size(THIS, size, DATA_TO_ARRAY(THIS->data)->inc); + ARRAY_add_many_void(&THIS->data, size - count); + } + else + { + release(THIS, size, -1); + ARRAY_remove_many(&THIS->data, size, count - size); + } + + THIS->count = size; +} + + +BEGIN_METHOD(Array_Resize, GB_INTEGER size) + + int size = VARG(size); + + if (check_not_multi(THIS)) + return; + + CARRAY_resize(THIS, size); + +END_METHOD + + +static void add(CARRAY *_object, GB_VALUE *value, int index) +{ + void *data; + + if (check_not_multi(THIS)) + return; + + data = insert(THIS, index); + /*GB_Conv(value, THIS->type);*/ + GB_Store(THIS->type, value, data); +} + + +#define IMPLEMENT_add(_type, _gtype) \ +BEGIN_METHOD(Array_##_type##_Add, GB_##_gtype value; GB_INTEGER index) \ +\ + add(THIS, (GB_VALUE *)(void *)ARG(value), VARGOPT(index, -1)); \ + \ +END_METHOD \ +\ +BEGIN_METHOD(Array_##_type##_Push, GB_##_gtype value) \ +\ + add(THIS, (GB_VALUE *)(void *)ARG(value), -1); \ + \ +END_METHOD + +IMPLEMENT_add(Integer, INTEGER) +IMPLEMENT_add(Long, LONG) +IMPLEMENT_add(Float, FLOAT) +IMPLEMENT_add(Single, SINGLE) +IMPLEMENT_add(Date, DATE) +IMPLEMENT_add(String, STRING) +IMPLEMENT_add(Object, OBJECT) +IMPLEMENT_add(Variant, VARIANT) + +BEGIN_METHOD(Array_Add, GB_VARIANT value; GB_INTEGER index) + + GB_VALUE *value = (GB_VALUE *)(void *)ARG(value); + GB_Conv(value, THIS->type); + add(THIS, value, VARGOPT(index, -1)); + +END_METHOD + +BEGIN_METHOD(Array_Push, GB_VARIANT value) + + GB_VALUE *value = (GB_VALUE *)(void *)ARG(value); + GB_Conv(value, THIS->type); + add(THIS, value, -1); + +END_METHOD + + +#define IMPLEMENT_put(_type, _gtype) \ +BEGIN_METHOD(Array_##_type##_put, GB_##_gtype value; GB_INTEGER index) \ + \ + void *data = get_data_multi(THIS, ARG(index), GB_NParam() + 1); \ + if (!data) return; \ + GB_Store(GB_T_##_gtype, (GB_VALUE *)(void *)ARG(value), data); \ + \ +END_METHOD + +#define IMPLEMENT_put2(_type, _gtype, _gstore) \ +BEGIN_METHOD(Array_##_type##_put, GB_##_gtype value; GB_INTEGER index) \ +\ + void *data = get_data_multi(THIS, ARG(index), GB_NParam() + 1); \ + if (!data) return; \ + GB_Store(GB_T_##_gstore, (GB_VALUE *)(void *)ARG(value), data); \ + \ +END_METHOD + +BEGIN_METHOD(Array_put, GB_VARIANT value; GB_INTEGER index) + + GB_VALUE *value; + void *data; + + value = (GB_VALUE *)(void *)ARG(value); + GB_Conv(value, THIS->type); + + data = get_data_multi(THIS, ARG(index), GB_NParam() + 1); + if (!data) return; + + GB_Store(THIS->type, value, data); + +END_METHOD + +IMPLEMENT_put(Integer, INTEGER) +IMPLEMENT_put2(Short, INTEGER, SHORT) +IMPLEMENT_put2(Byte, INTEGER, BYTE) +IMPLEMENT_put2(Boolean, INTEGER, BOOLEAN) +IMPLEMENT_put(Long, LONG) +IMPLEMENT_put(Float, FLOAT) +IMPLEMENT_put(Single, SINGLE) +IMPLEMENT_put(Date, DATE) +IMPLEMENT_put(String, STRING) +IMPLEMENT_put(Object, OBJECT) +IMPLEMENT_put(Variant, VARIANT) + + +BEGIN_METHOD(Array_Fill, GB_VALUE value; GB_INTEGER start; GB_INTEGER length) + + int count = THIS->count; + int start = VARGOPT(start, 0); + int length = VARGOPT(length, count); + int i; + void *data; + int size; + + if (check_start_length(count, &start, &length)) + return; + + VALUE_conv((VALUE *)ARG(value), THIS->type); + + data = get_data(THIS, start); + size = THIS->size; + + for (i = 0; i < length; i++) + { + GB_Store(THIS->type, (GB_VALUE *)ARG(value), data); + data += size; + } + +END_METHOD + + +BEGIN_METHOD(Array_Insert, GB_OBJECT array; GB_INTEGER pos) + + int pos = VARGOPT(pos, -1); + CARRAY *array = (CARRAY *)VARG(array); + void *data; + int count; + + if (GB_CheckObject(array)) + return; + + if (check_not_multi(THIS)) + { + GB_ReturnNull(); + return; + } + + count = array->count; + + if (count > 0) + { + if (pos < 0) + pos = THIS->count; + + data = ARRAY_insert_many(&THIS->data, pos, count); + THIS->count += count; + borrow(array, 0, -1); + memmove(data, array->data, count * THIS->size); + } + + GB_ReturnObject(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(Array_Pop) + + int index = THIS->count - 1; + + if (check_not_multi(THIS)) + return; + + if (index < 0) + { + GB_Error((char *)E_ARG); + return; + } + + GB_ReturnPtr(THIS->type, get_data(THIS, index)); + + BORROW(&TEMP); + release_one(THIS, index); + ARRAY_remove(&THIS->data, index); + THIS->count--; + UNBORROW(&TEMP); + +END_METHOD + + +BEGIN_METHOD(Array_get, GB_INTEGER index) + + void *data = get_data_multi(THIS, ARG(index), GB_NParam() + 1); + + if (data) + GB_ReturnPtr(THIS->type, data); + +END_METHOD + + +static void array_first_last(CARRAY *_object, void *_param, bool last) +{ + void *data; + int index = last ? THIS->count - 1 : 0; + + if (check_not_multi(THIS)) + return; + + data = get_data(THIS, index); + if (!data) return; + + if (READ_PROPERTY) + { + GB_ReturnPtr(THIS->type, data); + } + else + { + GB_VALUE *value; + + value = PROP(GB_VALUE); + GB_Conv(value, THIS->type); + + GB_Store(THIS->type, value, data); + } +} + + +BEGIN_PROPERTY(Array_First) + + array_first_last(_object, _param, FALSE); + +END_PROPERTY + + +BEGIN_PROPERTY(Array_Last) + + array_first_last(_object, _param, TRUE); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Array_next) + + int *index = (int *)GB_GetEnum(); + + if (*index >= THIS->count) + GB_StopEnum(); + else + { + GB_ReturnPtr(THIS->type, get_data(THIS, *index)); + (*index)++; + } + +END_METHOD + +BEGIN_METHOD(Array_Sort, GB_INTEGER mode) + + COMPARE_FUNC compare; + int mode = VARGOPT(mode, 0); + void *data = THIS->data; + + if (THIS->count > 1) + { + compare = COMPARE_get_func(THIS->type, mode); + qsort(data, THIS->count, THIS->size, compare); + } + + GB_ReturnObject(THIS); + +END_METHOD + + +static int find(CARRAY *_object, int mode, void *value, int start) +{ + COMPARE_FUNC compare = COMPARE_get_func(THIS->type, mode); + int i; + + if (start < 0) + start = 0; + else if (start >= THIS->count) + return (-1); + + for (i = start; i < THIS->count; i++) + { + if ((*compare)(get_data(THIS, i), value) == 0) + return i; + } + + return (-1); +} + +#define IMPLEMENT_find_fast(_type, _gtype, _ctype) \ +static int find_##_ctype(CARRAY *_object, int value, int start) \ +{ \ + int count = THIS->count; \ + _ctype *data; \ + int i; \ + \ + if (start < 0) \ + start = 0; \ + else if (start > count) \ + return (-1); \ + \ + data = (_ctype *)THIS->data; \ + \ + for (i = start; i < count; i++) \ + { \ + if (data[i] == value) \ + return i; \ + } \ + \ + return (-1); \ +} \ +\ +BEGIN_METHOD(Array_##_type##_Find, _gtype value; GB_INTEGER start) \ +\ + GB_ReturnInteger(find_##_ctype(THIS, VARG(value), VARGOPT(start, 0))); \ +\ +END_METHOD \ +BEGIN_METHOD(Array_##_type##_Exist, _gtype value) \ +\ + GB_ReturnBoolean(find_##_ctype(THIS, VARG(value), 0) >= 0); \ +\ +END_METHOD + +#define IMPLEMENT_find(_type, _gtype) \ +BEGIN_METHOD(Array_##_type##_Find, _gtype value; GB_INTEGER start) \ +\ + GB_ReturnInt(find(THIS, 0, &VARG(value), VARGOPT(start, 0))); \ +\ +END_METHOD \ +BEGIN_METHOD(Array_##_type##_Exist, _gtype value) \ +\ + GB_ReturnBoolean(find(THIS, 0, &VARG(value), 0) >= 0); \ +\ +END_METHOD + +IMPLEMENT_find_fast(Boolean, GB_BOOLEAN, bool) +IMPLEMENT_find_fast(Byte, GB_INTEGER, uchar) +IMPLEMENT_find_fast(Short, GB_INTEGER, short) +IMPLEMENT_find_fast(Integer, GB_INTEGER, int) +IMPLEMENT_find_fast(Long, GB_LONG, int64_t) +IMPLEMENT_find_fast(Float, GB_FLOAT, double) +IMPLEMENT_find_fast(Single, GB_SINGLE, float) +IMPLEMENT_find(Date, GB_DATE) +IMPLEMENT_find(Variant, GB_VARIANT) + +static int find_object(CARRAY *_object, void *value, int start, bool byref) +{ + int i; + void **data; + + if (start < 0) + start = 0; + else if (start >= THIS->count) + return (-1); + + data = get_data(THIS, 0); + + if (byref) + { + for (i = 0; i < THIS->count; i++) + { + if (data[i] == value) + return i; + } + } + else + { + for (i = 0; i < THIS->count; i++) + { + //if (*((void **)get_data(THIS, i)) == value) + if (!COMPARE_object(&data[i], &value)) + return i; + } + } + + return (-1); +} + +BEGIN_METHOD(Array_Object_Find, GB_OBJECT value; GB_INTEGER start) + + GB_ReturnInt(find_object(THIS, VARG(value), VARGOPT(start, 0), FALSE)); + +END_METHOD + +BEGIN_METHOD(Array_Object_FindByRef, GB_OBJECT value; GB_INTEGER start) + + GB_ReturnInt(find_object(THIS, VARG(value), VARGOPT(start, 0), TRUE)); + +END_METHOD + +BEGIN_METHOD(Array_Object_Exist, GB_OBJECT value) + + GB_ReturnBoolean(find_object(THIS, VARG(value), 0, FALSE) >= 0); + +END_METHOD + +BEGIN_METHOD(Array_Object_ExistByRef, GB_OBJECT value) + + GB_ReturnBoolean(find_object(THIS, VARG(value), 0, TRUE) >= 0); + +END_METHOD + + +static int find_string(CARRAY *_object, int mode, const char *value, int len_value, int start) +{ + char **data; + char *str; + int i; + int len; + + //fprintf(stderr, "find_string: %p %d: %.*s | %s\n", THIS, THIS->count, len_value, value, DEBUG_get_current_position()); + + if (start < 0) + start = 0; + else if (start >= THIS->count) + return (-1); + + data = ((char **)THIS->data); + + if (mode == GB_COMP_BINARY) + { + for (i = start; i < THIS->count; i++) + { + str = data[i]; + len = STRING_length(str); + if (STRING_equal(str, len, value, len_value)) + return i; + } + } + else if (mode == GB_COMP_NOCASE) + { + for (i = start; i < THIS->count; i++) + { + str = data[i]; + len = STRING_length(str); + if (STRING_equal_ignore_case(str, len, value, len_value)) + return i; + } + } + else + { + COMPARE_STRING_FUNC compare = COMPARE_get_string_func(mode); + bool nocase = mode & GB_COMP_NOCASE; + + for (i = start; i < THIS->count; i++) + { + if ((*compare)(data[i], STRING_length(data[i]), value, len_value, nocase, FALSE) == 0) + return i; + } + } + + return (-1); +} + +BEGIN_METHOD(Array_String_Find, GB_STRING value; GB_INTEGER mode; GB_INTEGER start) + + GB_ReturnInteger(find_string(THIS, VARGOPT(mode, GB_COMP_BINARY), STRING(value), LENGTH(value), VARGOPT(start, 0))); + +END_METHOD + +BEGIN_METHOD(Array_String_Exist, GB_STRING value; GB_INTEGER mode) + + //fprintf(stderr, "%s\n", DEBUG_get_current_position()); + GB_ReturnBoolean(find_string(THIS, VARGOPT(mode, GB_COMP_BINARY), STRING(value), LENGTH(value), 0) >= 0); + +END_METHOD + +BEGIN_METHOD(Array_String_join, GB_STRING sep; GB_STRING esc) + + char *sep = ","; + uint lsep = 1; + char *esc = ""; + uint lesc = 0; + char escl, NO_WARNING(escr); + int i; + char **data = (char **)THIS->data; + char *p, *p2; + int l, max; + + if (!MISSING(sep)) + { + sep = STRING(sep); + lsep = LENGTH(sep); + } + + if (!MISSING(esc)) + { + esc = STRING(esc); + lesc = LENGTH(esc); + + if (lesc == 1) + escl = escr = esc[0]; + else if (lesc >= 2) + { + escl = esc[0]; + escr = esc[1]; + } + } + + if (lesc == 0) + { + max = 0; + for (i = 0; i < THIS->count; i++) + max += STRING_length(data[i]) + lsep; + if (THIS->count) + max -= lsep; + + STRING_start_len(max); + + for (i = 0; i < THIS->count; i++) + { + p = data[i]; + l = STRING_length(data[i]); + + if (i) + STRING_make(sep, lsep); + if (l) + STRING_make(p, l); + } + } + else if (*sep && escr == *sep) + { + STRING_start(); + + for (i = 0; i < THIS->count; i++) + { + p = data[i]; + l = STRING_length(data[i]); + + if (i) + STRING_make(sep, lsep); + + if (l == 0) + continue; + + for(;;) + { + p2 = index(p, escr); + if (p2) + { + STRING_make(p, p2 - p); + STRING_make_char(escl); + STRING_make_char(escr); + p = p2 + 1; + } + else + { + STRING_make(p, l + data[i] - p); + break; + } + } + } + } + else + { + STRING_start(); + + for (i = 0; i < THIS->count; i++) + { + p = data[i]; + l = STRING_length(data[i]); + + if (i) + STRING_make(sep, lsep); + + if (l == 0) + continue; + + STRING_make_char(escl); + for(;;) + { + p2 = index(p, escr); + if (p2) + { + STRING_make(p, p2 - p + 1); + STRING_make_char(escr); + p = p2 + 1; + } + else + { + STRING_make(p, l + data[i] - p); + break; + } + } + STRING_make_char(escr); + } + } + + GB_ReturnString(STRING_end_temp()); + +END_METHOD + + +BEGIN_METHOD_VOID(CARRAY_reverse) + + size_t size; + int count; + char *buffer[16]; + char *pi, *pj; + + count = THIS->count; + if (count > 1) + { + size = THIS->size; + pi = get_data(THIS, 0); + pj = get_data(THIS, count - 1); + + do + { + memcpy(buffer, pi, size); + memcpy(pi, pj, size); + memcpy(pj, buffer, size); + pi += size; + pj -= size; + } + while (pi < pj); + } + + GB_ReturnObject(THIS); + +END_METHOD + + +BEGIN_METHOD(Array_Read, GB_OBJECT file; GB_INTEGER start; GB_INTEGER length) + + int count = THIS->count; + int start = VARGOPT(start, 0); + int length = VARGOPT(length, count); + + if (check_start_length(count, &start, &length)) + return; + + STREAM_read(CSTREAM_stream(VARG(file)), get_data(THIS, start), length * THIS->size); + +END_METHOD + + +BEGIN_METHOD(Array_Write, GB_OBJECT file; GB_INTEGER start; GB_INTEGER length) + + int count = THIS->count; + int start = VARGOPT(start, 0); + int length = VARGOPT(length, count); + + if (check_start_length(count, &start, &length)) + return; + + STREAM_write(CSTREAM_stream(VARG(file)), get_data(THIS, start), length * THIS->size); + +END_METHOD + + +BEGIN_PROPERTY(Array_Dim) + + GB_ReturnInteger(get_dim(THIS)); + +END_PROPERTY + + +BEGIN_METHOD(Array_Bounds_get, GB_INTEGER index) + + int dim = get_dim(THIS); + int index = VARG(index); + + if (index < 0 || index >= dim) + { + GB_Error((char *)E_BOUND); + return; + } + + GB_ReturnInteger(get_bound(THIS, index)); + +END_PROPERTY + +BEGIN_METHOD(Array_Byte_ToString, GB_INTEGER start; GB_INTEGER length) + + int start, length; + int count = THIS->count; + char *p; + + start = VARGOPT(start, 0); + + if (start < 0) + { + GB_Error((char *)E_BOUND); + return; + } + + if (start >= count) + { + GB_ReturnVoidString(); + return; + } + + length = VARGOPT(length, -1); + if (length < 0) + { + p = memchr((char *)THIS->data + start, 0, count - start); + if (!p) + length = count - start; + else + length = p - ((char *)THIS->data + start); + } + else + { + if ((start + length) > count) + length = count - start; + } + + GB_ReturnNewString((char *)THIS->data + start, length); + +END_METHOD + + +BEGIN_METHOD(Array_Byte_FromString, GB_STRING string) + + CARRAY *array; + char *string = STRING(string); + int length = LENGTH(string); + + GB_ArrayNew((GB_ARRAY *)POINTER(&array), T_BYTE, length); + memcpy(array->data, string, length); + GB_ReturnObject(array); + +END_METHOD + + +BEGIN_METHOD(ArrayOfStruct_get, GB_INTEGER index) + + void *data = get_data_multi(THIS, ARG(index), GB_NParam() + 1); + + if (data) + GB_ReturnObject(CSTRUCT_create_static(THIS, (CLASS *)THIS->type, data)); + +END_METHOD + +static void array_of_struct_put(CARRAY *_object, void *data, void *object) +{ + int i; + CLASS *class = (CLASS *)THIS->type; + CLASS_DESC *desc; + char *addr; + VALUE temp; + + for (i = 0; i < class->n_desc; i++) + { + desc = class->table[i].desc; + + if (((CSTRUCT *)object)->ref) + addr = (char *)((CSTATICSTRUCT *)object)->addr + desc->variable.offset; + else + addr = (char *)object + sizeof(CSTRUCT) + desc->variable.offset; + + VALUE_class_read(desc->variable.class, &temp, (void *)addr, desc->variable.ctype, object); + + addr = (char *)data + desc->variable.offset; + VALUE_write(&temp, (void *)addr, desc->variable.type); + } +} + +BEGIN_METHOD(ArrayOfStruct_put, GB_OBJECT value; GB_INTEGER index) + + void *object = VARG(value); + void *data; + + if (GB_CheckObject(object)) + return; + + data = get_data_multi(THIS, ARG(index), GB_NParam() + 1); + if (!data) + return; + + array_of_struct_put(THIS, data, object); + +END_METHOD + + +static void array_of_struct_first_last(void *_object, void *_param, bool last) +{ + int index = last ? THIS->count - 1 : 0; + + if (check_not_multi(THIS)) + return; + + void *data = get_data(THIS, index); + if (!data) return; + + if (READ_PROPERTY) + { + GB_ReturnObject(CSTRUCT_create_static(THIS, (CLASS *)THIS->type, data)); + } + else + { + array_of_struct_put(THIS, data, VPROP(GB_OBJECT)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(ArrayOfStruct_First) + + array_of_struct_first_last(_object, _param, FALSE); + +END_PROPERTY + + +BEGIN_PROPERTY(ArrayOfStruct_Last) + + array_of_struct_first_last(_object, _param, TRUE); + +END_PROPERTY + + +BEGIN_METHOD_VOID(ArrayOfStruct_next) + + int *index = (int *)GB_GetEnum(); + + if (*index >= THIS->count) + GB_StopEnum(); + else + { + GB_ReturnObject(CSTRUCT_create_static(THIS, (CLASS *)THIS->type, get_data(THIS, *index))); + (*index)++; + } + +END_METHOD + +static void error_convert(CARRAY *array) +{ + OBJECT_UNREF(array); +} + +static bool _convert(CARRAY *src, CLASS *class, VALUE *conv) +{ + CARRAY *array; + int i; + void *data; + VALUE temp; + int dim; + + if (!src || !TYPE_is_pure_object((TYPE)class)) + return TRUE; + + CLASS_load(class); // Force creation of array classes + + if (!class->is_array) + return TRUE; + + array = OBJECT_create(class, NULL, NULL, 0); + + if (src->count) + { + ARRAY_add_many_void(&array->data, src->count); + array->count = src->count; + + ON_ERROR_1(error_convert, array) + { + for (i = 0; i < src->count; i++) + { + data = CARRAY_get_data(src, i); + VALUE_read(&temp, data, src->type); + BORROW(&temp); + data = CARRAY_get_data(array, i); + VALUE_write(&temp, data, array->type); + RELEASE(&temp); + } + } + END_ERROR + } + + dim = get_dim(src); + if (dim > 1) + { + ALLOC(&array->dim, dim * sizeof(int)); + for (i = 0; i < dim; i++) + array->dim[i] = src->dim[i]; + } + + conv->_object.object = array; + return FALSE; +} + +#else + +#include "gbx_c_array.h" + +#define _convert NULL + +#endif /* #ifndef GBX_INFO */ + + +GB_DESC NATIVE_ArrayBounds[] = +{ + GB_DECLARE(".Array.Bounds", sizeof(CARRAY)), GB_NOT_CREATABLE(), + + GB_METHOD("_get", "i", Array_Bounds_get, "(Dimension)i"), + GB_PROPERTY_READ("Count", "i", Array_Dim), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_Array[] = +{ + GB_DECLARE("Array", sizeof(CARRAY)), GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, Array_free, NULL), + + GB_PROPERTY_READ("Type", "i", Array_Type), + GB_PROPERTY_READ("Count", "i", Array_Count), + GB_PROPERTY_READ("Max", "i", Array_Max), + GB_PROPERTY_READ("Length", "i", Array_Count), + GB_PROPERTY_READ("Dim", "i", Array_Dim), + GB_PROPERTY_READ("Data", "p", Array_Data), + GB_PROPERTY_SELF("Bounds", ".Array.Bounds"), + + GB_METHOD("Remove", NULL, Array_Remove, "(Index)i[(Length)i]"), + GB_METHOD("Clear", NULL, Array_Clear, NULL), + GB_METHOD("Resize", NULL, Array_Resize, "(Size)i"), + + //GB_METHOD("Add", NULL, Array_Add, "(Value)v[(Index)i]"), + //GB_METHOD("Push", NULL, Array_Push, "(Value)v"), + //GB_METHOD("_put", NULL, Array_put, "(Value)v(Index)i."), + + //GB_METHOD("Pop", "v", Array_Pop_variant, NULL), // Does not work + //GB_METHOD("_get", "v", Array_get_variant, "(Index)i."), + //GB_METHOD("_next", "v", Array_next_variant, NULL), + //GB_PROPERTY_READ("Last", "v", Array_Last), + + /*GB_METHOD("Copy", "Array", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Array", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Array", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)v[(Start)i(Length)i]"),*/ + + GB_INTERFACE("_convert", _convert), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_BooleanArray[] = +{ + GB_DECLARE("Boolean[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Integer_Add, "(Value)b[(Index)i]"), + GB_METHOD("Push", NULL, Array_Integer_Push, "(Value)b"), + GB_METHOD("_put", NULL, Array_Boolean_put, "(Value)b(Index)i."), + GB_METHOD("Find", "i", Array_Boolean_Find, "(Value)b[(Start)i]"), + GB_METHOD("Exist", "b", Array_Boolean_Exist, "(Value)b"), + + GB_METHOD("Pop", "b", Array_Pop, NULL), + GB_METHOD("_get", "b", Array_get, "(Index)i."), + GB_METHOD("_next", "b", Array_next, NULL), + + GB_PROPERTY("First", "b", Array_First), + GB_PROPERTY("Last", "b", Array_Last), + + GB_METHOD("Read", NULL, Array_Read, "(Stream)Stream;[(Start)i(Length)i]"), + GB_METHOD("Write", NULL, Array_Write, "(Stream)Stream;[(Start)i(Length)i]"), + + GB_METHOD("Insert", "Boolean[]", Array_Insert, "(Array)Boolean[];[(Pos)i]"), + GB_METHOD("Copy", "Boolean[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Boolean[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Boolean[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "Boolean[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("Reverse", "Boolean[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)b[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_ByteArray[] = +{ + GB_DECLARE("Byte[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Integer_Add, "(Value)c[(Index)i]"), + GB_METHOD("Push", NULL, Array_Integer_Push, "(Value)c"), + GB_METHOD("_put", NULL, Array_Byte_put, "(Value)c(Index)i."), + GB_METHOD("Find", "i", Array_Byte_Find, "(Value)c[(Start)i]"), + GB_METHOD("Exist", "b", Array_Byte_Exist, "(Value)c"), + + GB_METHOD("Pop", "c", Array_Pop, NULL), + GB_METHOD("_get", "c", Array_get, "(Index)i."), + GB_METHOD("_next", "c", Array_next, NULL), + + GB_PROPERTY("First", "c", Array_First), + GB_PROPERTY("Last", "c", Array_Last), + + GB_METHOD("Read", NULL, Array_Read, "(Stream)Stream;[(Start)i(Length)i]"), + GB_METHOD("Write", NULL, Array_Write, "(Stream)Stream;[(Start)i(Length)i]"), + + GB_METHOD("Insert", "Byte[]", Array_Insert, "(Array)Byte[];[(Pos)i]"), + GB_METHOD("Copy", "Byte[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Byte[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Byte[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "Byte[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("Reverse", "Byte[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)c[(Start)i(Length)i]"), + + GB_METHOD("ToString", "s", Array_Byte_ToString, "[(Start)i(Length)i]"), + GB_STATIC_METHOD("FromString", "Byte[]", Array_Byte_FromString, "(String)s"), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_ShortArray[] = +{ + GB_DECLARE("Short[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Integer_Add, "(Value)h[(Index)i]"), + GB_METHOD("Push", NULL, Array_Integer_Push, "(Value)h"), + GB_METHOD("_put", NULL, Array_Short_put, "(Value)h(Index)i."), + GB_METHOD("Find", "i", Array_Short_Find, "(Value)h[(Start)i]"), + GB_METHOD("Exist", "b", Array_Short_Exist, "(Value)h"), + + GB_METHOD("Pop", "h", Array_Pop, NULL), + GB_METHOD("_get", "h", Array_get, "(Index)i."), + GB_METHOD("_next", "h", Array_next, NULL), + + GB_PROPERTY("First", "h", Array_First), + GB_PROPERTY("Last", "h", Array_Last), + + GB_METHOD("Read", NULL, Array_Read, "(Stream)Stream;[(Start)i(Length)i]"), + GB_METHOD("Write", NULL, Array_Write, "(Stream)Stream;[(Start)i(Length)i]"), + + GB_METHOD("Insert", "Short[]", Array_Insert, "(Array)Short[];[(Pos)i]"), + GB_METHOD("Copy", "Short[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Short[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Short[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "Short[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("Reverse", "Short[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)h[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_IntegerArray[] = +{ + GB_DECLARE("Integer[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Integer_Add, "(Value)i[(Index)i]"), + GB_METHOD("Push", NULL, Array_Integer_Push, "(Value)i"), + GB_METHOD("_put", NULL, Array_Integer_put, "(Value)i(Index)i."), + GB_METHOD("Find", "i", Array_Integer_Find, "(Value)i[(Start)i]"), + GB_METHOD("Exist", "b", Array_Integer_Exist, "(Value)i"), + + GB_METHOD("Pop", "i", Array_Pop, NULL), + GB_METHOD("_get", "i", Array_get, "(Index)i."), + GB_METHOD("_next", "i", Array_next, NULL), + + GB_PROPERTY("First", "i", Array_First), + GB_PROPERTY("Last", "i", Array_Last), + + GB_METHOD("Read", NULL, Array_Read, "(Stream)Stream;[(Start)i(Length)i]"), + GB_METHOD("Write", NULL, Array_Write, "(Stream)Stream;[(Start)i(Length)i]"), + + GB_METHOD("Insert", "Integer[]", Array_Insert, "(Array)Integer[];[(Pos)i]"), + GB_METHOD("Copy", "Integer[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Integer[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Integer[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "Integer[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("Reverse", "Integer[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)i[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_LongArray[] = +{ + GB_DECLARE("Long[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Long_Add, "(Value)l[(Index)i]"), + GB_METHOD("Push", NULL, Array_Long_Push, "(Value)l"), + GB_METHOD("_put", NULL, Array_Long_put, "(Value)l(Index)i."), + GB_METHOD("Find", "i", Array_Long_Find, "(Value)l[(Start)i]"), + GB_METHOD("Exist", "b", Array_Long_Exist, "(Value)l"), + + GB_METHOD("Pop", "l", Array_Pop, NULL), + GB_METHOD("_get", "l", Array_get, "(Index)i."), + GB_METHOD("_next", "l", Array_next, NULL), + + GB_PROPERTY("First", "l", Array_First), + GB_PROPERTY("Last", "l", Array_Last), + + GB_METHOD("Read", NULL, Array_Read, "(Stream)Stream;[(Start)i(Length)i]"), + GB_METHOD("Write", NULL, Array_Write, "(Stream)Stream;[(Start)i(Length)i]"), + + GB_METHOD("Insert", "Long[]", Array_Insert, "(Array)Long[];[(Pos)i]"), + + GB_METHOD("Copy", "Long[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Long[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Long[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "Long[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("Reverse", "Long[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)l[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + +#ifdef OS_64BITS +#define Array_Pointer_Add Array_Long_Add +#define Array_Pointer_Push Array_Long_Push +#define Array_Pointer_put Array_Long_put +#define Array_Pointer_Find Array_Long_Find +#define Array_Pointer_Exist Array_Long_Exist +#else +#define Array_Pointer_Add Array_Integer_Add +#define Array_Pointer_Push Array_Integer_Push +#define Array_Pointer_put Array_Integer_put +#define Array_Pointer_Find Array_Integer_Find +#define Array_Pointer_Exist Array_Integer_Exist +#endif + +GB_DESC NATIVE_PointerArray[] = +{ + GB_DECLARE("Pointer[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Pointer_Add, "(Value)p[(Index)i]"), + GB_METHOD("Push", NULL, Array_Pointer_Push, "(Value)p"), + GB_METHOD("_put", NULL, Array_Pointer_put, "(Value)p(Index)i."), + GB_METHOD("Find", "i", Array_Pointer_Find, "(Value)p[(Start)i]"), + GB_METHOD("Exist", "b", Array_Pointer_Exist, "(Value)p"), + + GB_METHOD("Pop", "p", Array_Pop, NULL), + GB_METHOD("_get", "p", Array_get, "(Index)i."), + GB_METHOD("_next", "p", Array_next, NULL), + + GB_PROPERTY("First", "p", Array_First), + GB_PROPERTY("Last", "p", Array_Last), + + GB_METHOD("Read", NULL, Array_Read, "(Stream)Stream;[(Start)i(Length)i]"), + GB_METHOD("Write", NULL, Array_Write, "(Stream)Stream;[(Start)i(Length)i]"), + + GB_METHOD("Insert", "Pointer[]", Array_Insert, "(Array)Pointer[];[(Pos)i]"), + + GB_METHOD("Copy", "Pointer[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Pointer[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Pointer[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "Pointer[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("Reverse", "Pointer[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)p[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_StringArray[] = +{ + GB_DECLARE("String[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_String_Add, "(Value)s[(Index)i]"), + GB_METHOD("Push", NULL, Array_String_Push, "(Value)s"), + GB_METHOD("_put", NULL, Array_String_put, "(Value)s(Index)i."), + GB_METHOD("Find", "i", Array_String_Find, "(Value)s[(Mode)i(Start)i]"), + GB_METHOD("Exist", "b", Array_String_Exist, "(Value)s[(Mode)i]"), + + GB_METHOD("Pop", "s", Array_Pop, NULL), + GB_METHOD("_get", "s", Array_get, "(Index)i."), + GB_METHOD("_next", "s", Array_next, NULL), + + GB_PROPERTY("First", "s", Array_First), + GB_PROPERTY("Last", "s", Array_Last), + + GB_METHOD("Insert", "String[]", Array_Insert, "(Array)String[];[(Pos)i]"), + + GB_METHOD("Join", "s", Array_String_join, "[(Separator)s(Escape)s]"), + + GB_METHOD("Copy", "String[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "String[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "String[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "String[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("Reverse", "String[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)s[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_FloatArray[] = +{ + GB_DECLARE("Float[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Float_Add, "(Value)f[(Index)i]"), + GB_METHOD("Push", NULL, Array_Float_Push, "(Value)f"), + GB_METHOD("_put", NULL, Array_Float_put, "(Value)f(Index)i."), + GB_METHOD("Find", "i", Array_Float_Find, "(Value)f[(Start)i]"), + GB_METHOD("Exist", "b", Array_Float_Exist, "(Value)f"), + + GB_METHOD("Pop", "f", Array_Pop, NULL), + GB_METHOD("_get", "f", Array_get, "(Index)i."), + GB_METHOD("_next", "f", Array_next, NULL), + + GB_PROPERTY("First", "f", Array_First), + GB_PROPERTY("Last", "f", Array_Last), + + GB_METHOD("Read", NULL, Array_Read, "(Stream)Stream;[(Start)i(Length)i]"), + GB_METHOD("Write", NULL, Array_Write, "(Stream)Stream;[(Start)i(Length)i]"), + + GB_METHOD("Insert", "Float[]", Array_Insert, "(Array)Float[];[(Pos)i]"), + + GB_METHOD("Copy", "Float[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Float[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Float[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "Float[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("Reverse", "Float[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)f[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_SingleArray[] = +{ + GB_DECLARE("Single[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Single_Add, "(Value)g[(Index)i]"), + GB_METHOD("Push", NULL, Array_Single_Push, "(Value)g"), + GB_METHOD("_put", NULL, Array_Single_put, "(Value)g(Index)i."), + GB_METHOD("Find", "i", Array_Single_Find, "(Value)g[(Start)i]"), + GB_METHOD("Exist", "b", Array_Single_Exist, "(Value)g"), + + GB_METHOD("Pop", "g", Array_Pop, NULL), + GB_METHOD("_get", "g", Array_get, "(Index)i."), + GB_METHOD("_next", "g", Array_next, NULL), + + GB_PROPERTY("First", "g", Array_First), + GB_PROPERTY("Last", "g", Array_Last), + + GB_METHOD("Read", NULL, Array_Read, "(Stream)Stream;[(Start)i(Length)i]"), + GB_METHOD("Write", NULL, Array_Write, "(Stream)Stream;[(Start)i(Length)i]"), + + GB_METHOD("Insert", "Single[]", Array_Insert, "(Array)Single[];[(Pos)i]"), + + GB_METHOD("Copy", "Single[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Single[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Single[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "Single[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("Reverse", "Single[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)g[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_DateArray[] = +{ + GB_DECLARE("Date[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Date_Add, "(Value)d[(Index)i]"), + GB_METHOD("Push", NULL, Array_Date_Push, "(Value)d"), + GB_METHOD("_put", NULL, Array_Date_put, "(Value)d(Index)i."), + GB_METHOD("Find", "i", Array_Date_Find, "(Value)d[(Start)i]"), + GB_METHOD("Exist", "b", Array_Date_Exist, "(Value)d"), + + GB_METHOD("Pop", "d", Array_Pop, NULL), + GB_METHOD("_get", "d", Array_get, "(Index)i."), + GB_METHOD("_next", "d", Array_next, NULL), + + GB_PROPERTY("First", "d", Array_First), + GB_PROPERTY("Last", "d", Array_Last), + + GB_METHOD("Read", NULL, Array_Read, "(Stream)Stream;[(Start)i(Length)i]"), + GB_METHOD("Write", NULL, Array_Write, "(Stream)Stream;[(Start)i(Length)i]"), + + GB_METHOD("Insert", "Date[]", Array_Insert, "(Array)Date[];[(Pos)i]"), + + GB_METHOD("Copy", "Date[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Date[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Date[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Sort", "Date[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("Reverse", "Date[]", CARRAY_reverse, NULL), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)d[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + + +GB_DESC NATIVE_ObjectArray[] = +{ + GB_DECLARE("Object[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Object_Add, "(Value)o[(Index)i]"), + GB_METHOD("Push", NULL, Array_Object_Push, "(Value)o"), + GB_METHOD("_put", NULL, Array_Object_put, "(Value)o(Index)i."), + GB_METHOD("Find", "i", Array_Object_Find, "(Value)o[(Start)i]"), + GB_METHOD("FindByRef", "i", Array_Object_FindByRef, "(Value)o[(Start)i]"), + GB_METHOD("Exist", "b", Array_Object_Exist, "(Value)o"), + GB_METHOD("ExistByRef", "b", Array_Object_ExistByRef, "(Value)o"), + + GB_METHOD("Pop", "o", Array_Pop, NULL), + GB_METHOD("_get", "o", Array_get, "(Index)i."), + GB_METHOD("_next", "o", Array_next, NULL), + + GB_PROPERTY("First", "o", Array_First), + GB_PROPERTY("Last", "o", Array_Last), + + GB_METHOD("Insert", "Object[]", Array_Insert, "(Array)Object[];[(Pos)i]"), + + GB_METHOD("Copy", "Object[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Object[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Object[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)o[(Start)i(Length)i]"), + GB_METHOD("Reverse", "Object[]", CARRAY_reverse, NULL), + GB_METHOD("Sort", "Object[]", Array_Sort, "[(Mode)i]"), + + GB_END_DECLARE +}; + +GB_DESC NATIVE_VariantArray[] = +{ + GB_DECLARE("Variant[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Variant_Add, "(Value)v[(Index)i]"), + GB_METHOD("Push", NULL, Array_Variant_Push, "(Value)v"), + GB_METHOD("_put", NULL, Array_Variant_put, "(Value)v(Index)i."), + GB_METHOD("Find", "i", Array_Variant_Find, "(Value)v[(Start)i]"), + GB_METHOD("Exist", "b", Array_Variant_Exist, "(Value)v"), + + GB_METHOD("Pop", "v", Array_Pop, NULL), + GB_METHOD("_get", "v", Array_get, "(Index)i."), + GB_METHOD("_next", "v", Array_next, NULL), + + GB_PROPERTY("First", "v", Array_First), + GB_PROPERTY("Last", "v", Array_Last), + + GB_METHOD("Insert", "Variant[]", Array_Insert, "(Array)Variant[];[(Pos)i]"), + + GB_METHOD("Copy", "Variant[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "Variant[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "Variant[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)v[(Start)i(Length)i]"), + GB_METHOD("Reverse", "Variant[]", CARRAY_reverse, NULL), + + GB_END_DECLARE +}; + +// Beware: if this declaration is modified, the ARRAY_TEMPLATE_NDESC constant must be modified accordingly. + +GB_DESC NATIVE_TemplateArray[ARRAY_TEMPLATE_NDESC] = +{ + GB_DECLARE("*[]", sizeof(CARRAY)), GB_INHERITS("Array"), + + GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + + GB_METHOD("Add", NULL, Array_Object_Add, "(Value)*;[(Index)i]"), + GB_METHOD("Push", NULL, Array_Object_Push, "(Value)*;"), + GB_METHOD("_put", NULL, Array_Object_put, "(Value)*;(Index)i."), + GB_METHOD("Find", "i", Array_Object_Find, "(Value)*;[(Start)i]"), + GB_METHOD("FindByRef", "i", Array_Object_FindByRef, "(Value)*;[(Start)i]"), + GB_METHOD("Exist", "b", Array_Object_Exist, "(Value)*;"), + GB_METHOD("ExistByRef", "b", Array_Object_ExistByRef, "(Value)*;"), + + GB_METHOD("Pop", "*", Array_Pop, NULL), + GB_METHOD("_get", "*", Array_get, "(Index)i."), + GB_METHOD("_next", "*", Array_next, NULL), + + GB_PROPERTY("First", "*", Array_First), + GB_PROPERTY("Last", "*", Array_Last), + + GB_METHOD("Insert", "*", Array_Insert, "(Array)*[];[(Pos)i]"), + + GB_METHOD("Copy", "*[]", Array_Copy, "[(Start)i(Length)i]"), + GB_METHOD("Extract", "*[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Delete", "*[]", Array_Extract, "(Start)i[(Length)i]"), + GB_METHOD("Fill", NULL, Array_Fill, "(Value)*;[(Start)i(Length)i]"), + GB_METHOD("Sort", "*[]", Array_Sort, "[(Mode)i]"), + GB_METHOD("Reverse", "*[]", CARRAY_reverse, NULL), + + GB_END_DECLARE +}; + +// Beware: if this declaration is modified, the ARRAY_OF_STRUCT_TEMPLATE_NDESC constant must be modified accordingly. + +GB_DESC NATIVE_TemplateArrayOfStruct[ARRAY_OF_STRUCT_TEMPLATE_NDESC] = +{ + GB_DECLARE("$*[]", sizeof(CARRAY)), GB_NOT_CREATABLE(), + + //GB_METHOD("_new", NULL, Array_new, "[(Size)i.]"), + GB_METHOD("_free", NULL, Array_free, NULL), + + GB_PROPERTY_READ("Count", "i", Array_Count), + GB_PROPERTY_READ("Max", "i", Array_Max), + GB_PROPERTY_READ("Length", "i", Array_Count), + GB_PROPERTY_READ("Dim", "i", Array_Dim), + GB_PROPERTY_READ("Data", "p", Array_Data), + GB_PROPERTY_SELF("Bounds", ".Array.Bounds"), + + GB_METHOD("_get", "*", ArrayOfStruct_get, "(Index)i."), + GB_METHOD("_put", NULL, ArrayOfStruct_put, "(Value)*;(Index)i."), + GB_METHOD("_next", "*", ArrayOfStruct_next, NULL), + + GB_PROPERTY("First", "*", ArrayOfStruct_First), + GB_PROPERTY("Last", "*", ArrayOfStruct_Last), + + GB_END_DECLARE +}; + + +#ifndef GBX_INFO + +/* Gambas API */ + +void GB_ArrayNew(GB_ARRAY *array, TYPE type, int size) +{ + int np; + CTYPE ctype; + + if (size > 0) + { + GB_Push(1, GB_T_INTEGER, size); + np = 1; + } + else + { + np = 0; + } + + ctype.id = T_NULL; + *array = OBJECT_create(CARRAY_get_array_class((CLASS *)type, ctype), NULL, NULL, np); +} + +int GB_ArrayCount(GB_ARRAY array) +{ + return ((CARRAY *)array)->count; +} + +void *GB_ArrayAdd(GB_ARRAY array) +{ + return insert((CARRAY *)array, -1); +} + +void *GB_ArrayGet(GB_ARRAY array, int index) +{ + return get_data((CARRAY *)array, index); +} + +TYPE GB_ArrayType(GB_ARRAY array) +{ + return ((CARRAY *)array)->type; +} + +void GB_ArrayRemove(GB_ARRAY array, int index) +{ + copy_remove((CARRAY *)array, index, 1, FALSE, TRUE); +} + + + +#endif diff --git a/main/gbx/gbx_c_array.h b/main/gbx/gbx_c_array.h new file mode 100644 index 00000000..f832d5d3 --- /dev/null +++ b/main/gbx/gbx_c_array.h @@ -0,0 +1,115 @@ +/*************************************************************************** + + gbx_c_array.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_ARRAY_H +#define __GBX_C_ARRAY_H + +#ifndef GBX_INFO + +#include "gambas.h" + +#include "gbx_variant.h" +#include "gbx_object.h" +#include "gbx_type.h" +#include "gbx_class.h" + +// Do not forget to modify GB_ARRAY_BASE in gambas.h + +typedef + struct { + OBJECT object; + int size; + int count; + TYPE type; + void *data; + int *dim; + void *ref; + } + CARRAY; + +#ifndef __GBX_C_ARRAY_C +extern GB_DESC NATIVE_ArrayBounds[]; +extern GB_DESC NATIVE_Array[]; +extern GB_DESC NATIVE_BooleanArray[]; +extern GB_DESC NATIVE_ByteArray[]; +extern GB_DESC NATIVE_ShortArray[]; +extern GB_DESC NATIVE_IntegerArray[]; +extern GB_DESC NATIVE_LongArray[]; +extern GB_DESC NATIVE_PointerArray[]; +extern GB_DESC NATIVE_SingleArray[]; +extern GB_DESC NATIVE_FloatArray[]; +extern GB_DESC NATIVE_StringArray[]; +extern GB_DESC NATIVE_DateArray[]; +extern GB_DESC NATIVE_VariantArray[]; +extern GB_DESC NATIVE_ObjectArray[]; +extern GB_DESC NATIVE_TemplateArray[]; +extern GB_DESC NATIVE_TemplateArrayOfStruct[]; +#else + +#define THIS ((CARRAY *)_object) + +#endif + +void CARRAY_split(CARRAY *_object, const char *str, int lstr, const char *sep, const char *esc, bool no_void, bool keep_esc); +void CARRAY_reverse(void *_object, void *_param); +void CARRAY_get_value(CARRAY *_object, int index, VALUE *value); +#define CARRAY_invert(_array) CARRAY_reverse(_array, NULL) +void *CARRAY_get_data_multi(CARRAY *_object, GB_INTEGER *arg, int nparam); +void *CARRAY_out_of_bounds(); +CLASS *CARRAY_get_array_class(CLASS *class, CTYPE ctype); +int *CARRAY_get_array_bounds(CARRAY *_object); +void CARRAY_resize(CARRAY *_object, int size); + +CARRAY *CARRAY_create_static(CLASS *class, void *ref, CLASS_ARRAY *desc, void *data); +int CARRAY_get_static_count(CLASS_ARRAY *desc); +size_t CARRAY_get_static_size(CLASS *class, CLASS_ARRAY *desc); +void CARRAY_release_static(CLASS *class, CLASS_ARRAY *desc, void *data); + +#define CARRAY_get_data(_array, _index) \ +({ \ + int __index = (_index); \ + CARRAY *__array = (CARRAY *)(_array); \ + void *__data; \ + if ((__index < 0) || (__index >= __array->count)) \ + __data = CARRAY_out_of_bounds(); \ + else \ + __data = (void *)((char *)(__array->data) + __index * __array->size); \ + __data; \ +}) + +#define CARRAY_get_data_throw(_array, _index) \ +({ \ + int __index = (_index); \ + CARRAY *__array = (CARRAY *)(_array); \ + if ((__index < 0) || (__index >= __array->count)) \ + THROW(E_BOUND); \ + (void *)((char *)(__array->data) + __index * __array->size); \ +}) + + +#endif // #ifndef __GBX_CLASS_INFO_C + +#define ARRAY_TEMPLATE_NDESC 23 +#define ARRAY_OF_STRUCT_TEMPLATE_NDESC 15 + +#endif diff --git a/main/gbx/gbx_c_class.c b/main/gbx/gbx_c_class.c new file mode 100644 index 00000000..c99f91d0 --- /dev/null +++ b/main/gbx/gbx_c_class.c @@ -0,0 +1,990 @@ +/*************************************************************************** + + gbx_c_class.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_CLASS_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include + +#include "gb_common.h" +#include "gbx_api.h" +#include "gbx_component.h" +#include "gbx_project.h" +#include "gbx_class.h" +#include "gbx_class_desc.h" +#include "gbx_exec.h" +#include "gbx_event.h" +#include "gbx_object.h" +#include "gbx_c_array.h" +#include "gbx_c_observer.h" +#include "gbx_c_class.h" + +static CLASS_DESC_SYMBOL *_current_symbol = NULL; + +static void error(int code, CLASS *class, const char *name) +{ + GB_Error((char *)(intptr_t)code, CLASS_get_name(class), name); +} + +static bool check_null(void *object) +{ + if (!object) + { + GB_Error((char *)E_NULL); + return TRUE; + } + + return FALSE; +} + +//---- Components --------------------------------------------------------- + +BEGIN_METHOD(Components_get, GB_STRING name) + + const char *name = GB_ToZeroString(ARG(name)); + COMPONENT *comp; + + comp = COMPONENT_find(name); + GB_ReturnObject(comp); + +END_METHOD + +BEGIN_PROPERTY(Components_Count) + + GB_ReturnInt(COMPONENT_count); + +END_PROPERTY + +BEGIN_METHOD_VOID(Components_next) + + COMPONENT **plib = (COMPONENT **)GB_GetEnum(); + + *plib = COMPONENT_next(*plib); + if (*plib == NULL) + GB_StopEnum(); + else + GB_ReturnObject(*plib); + +END_METHOD + +BEGIN_PROPERTY(Component_Name) + + GB_ReturnConstZeroString(OBJECT(COMPONENT)->name); + +END_PROPERTY + +BEGIN_PROPERTY(Component_Version) + + if (OBJECT(COMPONENT)->user) + GB_ReturnString(ARCHIVE_get_version(OBJECT(COMPONENT)->archive)); + else + GB_ReturnConstZeroString(VERSION); + +END_PROPERTY + +BEGIN_PROPERTY(Component_Library) + + GB_ReturnBoolean(OBJECT(COMPONENT)->user); + +END_PROPERTY + +BEGIN_PROPERTY(Component_Path) + + GB_ReturnString(COMPONENT_path); + +END_PROPERTY + +BEGIN_METHOD(Component_Load, GB_STRING name) + + const char *name = GB_ToZeroString(ARG(name)); + COMPONENT *comp; + + comp = COMPONENT_find(name); + if (!comp) + comp = COMPONENT_create(name); + + COMPONENT_load(comp); + + GB_ReturnObject(comp); + +END_METHOD + +BEGIN_METHOD(Component_IsLoaded, GB_STRING name) + + GB_ReturnBoolean(COMPONENT_is_loaded(GB_ToZeroString(ARG(name)))); + +END_METHOD + +BEGIN_METHOD(Component_FindFromPath, GB_STRING path) + + ARCHIVE *arch = NULL; + const char *path = GB_ToZeroString(ARG(path)); + ARCHIVE_find_from_path(&arch, &path); + if (arch) + GB_ReturnNewZeroString(arch->name); + else + GB_ReturnVoidString(); + +END_METHOD + +//---- Classes ------------------------------------------------------------ + +BEGIN_METHOD(Classes_get, GB_STRING name) + + const char *name = GB_ToZeroString(ARG(name)); + CLASS *class = NULL; + + if (name != NULL) + class = CLASS_look(name, LENGTH(name)); + + if (class == NULL) + { + GB_Error("Unknown class '&1'", name); + return; + } + + if (!CLASS_is_loaded(class)) + { + GB_Error("Class is not loaded"); + return; + } + + GB_ReturnObject(class); + +END_METHOD + + +BEGIN_METHOD(Class_Load, GB_STRING name) + + CLASS *class; + + class = CLASS_get(GB_ToZeroString(ARG(name))); + GB_ReturnObject(class); + +END_METHOD + + +BEGIN_METHOD(Class_IsLoaded, GB_STRING name) + + const char *name = GB_ToZeroString(ARG(name)); + CLASS *class = NULL; + + if (name != NULL) + class = CLASS_look(name, LENGTH(name)); + + GB_ReturnBoolean(class && CLASS_is_loaded(class)); + +END_METHOD + + +BEGIN_METHOD_VOID(Classes_next) + + CLASS **pcurrent = (CLASS **)GB_GetEnum(); + CLASS *class; + + class = *pcurrent; + + for(;;) + { + if (!class) + class = CLASS_get_list(); + else + class = class->next; + + if (!class) + { + GB_StopEnum(); + break; + } + + if (CLASS_is_loaded(class)) + { + GB_ReturnObject(class); + break; + } + + } + + *pcurrent = class; + +END_METHOD + + +BEGIN_PROPERTY(Classes_count) + + GB_ReturnInt(CLASS_count()); + +END_PROPERTY + + +//---- Class -------------------------------------------------------------- + +BEGIN_PROPERTY(Class_Name) + + GB_ReturnConstZeroString(OBJECT(CLASS)->name); + +END_PROPERTY + + +BEGIN_PROPERTY(Class_Count) + + GB_ReturnInt(OBJECT(CLASS)->count); + +END_PROPERTY + + +BEGIN_PROPERTY(Class_Hidden) + + GB_ReturnBoolean(*(CLASS_get_name(OBJECT(CLASS))) == '.'); + +END_PROPERTY + + +BEGIN_PROPERTY(Class_Native) + + GB_ReturnBoolean(CLASS_is_native(OBJECT(CLASS))); + +END_PROPERTY + + +BEGIN_PROPERTY(Class_Component) + + GB_ReturnObject(OBJECT(CLASS)->component); + +END_PROPERTY + + +BEGIN_PROPERTY(Class_Parent) + + GB_ReturnObject(OBJECT(CLASS)->parent); + +END_PROPERTY + + +BEGIN_PROPERTY(Class_Symbols) + + CLASS *class = OBJECT(CLASS); + GB_ARRAY array; + CLASS_DESC_SYMBOL *cds; + int index = 0; + + GB_ArrayNew(&array, T_STRING, 0); + + for(;;) + { + cds = CLASS_get_next_sorted_symbol(class, &index); + if (!cds) + break; + *((char **)GB_ArrayAdd(array)) = STRING_new(cds->name, cds->len); + } + + GB_ReturnObject(array); + +END_PROPERTY + + +BEGIN_METHOD(Class_get, GB_STRING name) + + CLASS *class = OBJECT(CLASS); + CLASS_DESC_SYMBOL *cd = NULL; + const char *name = GB_ToZeroString(ARG(name)); + + if (name != NULL) + cd = CLASS_get_symbol(class, name); + + if (cd == NULL) + { + GB_Error("Unknown symbol '&1'", name); + return; + } + + _current_symbol = cd; + GB_ReturnObject(class); + +END_METHOD + + +BEGIN_METHOD(Class_Exist, GB_STRING name) + + CLASS *class = OBJECT(CLASS); + const char *name = GB_ToZeroString(ARG(name)); + + GB_ReturnBoolean(name != NULL && CLASS_get_symbol(class, name) != NULL); + +END_METHOD + + +BEGIN_PROPERTY(Class_Instance) + + GB_ReturnObject(OBJECT(CLASS)->instance); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Class_AutoCreate) + + CLASS *class = OBJECT(CLASS); + + if (!class->auto_create) + GB_ReturnNull(); + else + GB_ReturnObject(CLASS_auto_create(class, 0)); + +END_METHOD + + +BEGIN_METHOD(Class_New, GB_OBJECT params) + + CLASS *class = OBJECT(CLASS); + GB_ARRAY params = VARGOPT(params, NULL); + int i, np = 0; + void *object; + + if (params) + np = GB_ArrayCount(params); + + if (np) + { + STACK_check(np); + + for (i = 0; i < np; i++) + { + CARRAY_get_value(params, i, SP); + PUSH(); + } + } + + object = EXEC_create_object(class, np, NULL); + OBJECT_UNREF_KEEP(object); + GB_ReturnObject(object); + +END_METHOD + + +//---- Symbol ------------------------------------------------------------- + +BEGIN_PROPERTY(Symbol_Name) + + GB_ReturnConstString(_current_symbol->name, _current_symbol->len); + +END_PROPERTY + + +BEGIN_PROPERTY(Symbol_Kind) + + CLASS_DESC_SYMBOL *cds = _current_symbol; + + switch(CLASS_DESC_get_type(cds->desc)) + { + case CD_VARIABLE: + case CD_STATIC_VARIABLE: + GB_ReturnInt(1); + return; + + case CD_PROPERTY: + case CD_PROPERTY_READ: + case CD_STATIC_PROPERTY: + case CD_STATIC_PROPERTY_READ: + GB_ReturnInt(2); + return; + + case CD_METHOD: + case CD_STATIC_METHOD: + GB_ReturnInt(3); + return; + + case CD_EVENT: + GB_ReturnInt(4); + return; + + case CD_CONSTANT: + GB_ReturnInt(5); + return; + + default: + GB_ReturnInt(0); + return; + } + +END_PROPERTY + + +BEGIN_PROPERTY(Symbol_Static) + + CLASS_DESC_SYMBOL *cds = _current_symbol; + + GB_ReturnBoolean(index(CD_STATIC_LIST, CLASS_DESC_get_type(cds->desc)) != NULL); + +END_PROPERTY + + +BEGIN_PROPERTY(Symbol_Hidden) + + CLASS_DESC_SYMBOL *cds = _current_symbol; + + GB_ReturnBoolean(*cds->name == '_'); + +END_PROPERTY + + +BEGIN_PROPERTY(Symbol_ReadOnly) + + CLASS_DESC_SYMBOL *cds = _current_symbol; + + switch (CLASS_DESC_get_type(cds->desc)) + { + case CD_PROPERTY: + case CD_STATIC_PROPERTY: + GB_ReturnBoolean(FALSE); + break; + + default: + GB_ReturnBoolean(TRUE); + break; + } + +END_PROPERTY + + +BEGIN_PROPERTY(Symbol_Type) + + CLASS_DESC_SYMBOL *cds = _current_symbol; + + GB_ReturnConstZeroString(TYPE_to_string(cds->desc->property.type)); /* Valable pour tout symbole */ + +END_PROPERTY + + +BEGIN_PROPERTY(Symbol_Signature) + + CLASS_DESC_SYMBOL *cds = _current_symbol; + char *sign = CLASS_DESC_get_signature(cds->desc); + + if (sign) + { + STRING_free_later(sign); + GB_ReturnString(sign); + } + else + GB_ReturnVoidString(); + +END_METHOD + + +BEGIN_PROPERTY(Symbol_Value) + + CLASS_DESC *desc = _current_symbol->desc; + + if (CLASS_DESC_get_type(desc) != CD_CONSTANT) + { + GB_ReturnVariant(NULL); + return; + } + + if (desc->constant.type == T_STRING) + GB_ReturnConstZeroString(desc->constant.value._string); + else + GB_ReturnPtr(desc->constant.type, (void *)&desc->constant.value); + + GB_ReturnConvVariant(); + +END_PROPERTY + + +//---- Object ------------------------------------------------------------- + +BEGIN_METHOD(Object_GetProperty, GB_OBJECT object; GB_STRING property) + + const char *name; + void *object = VARG(object); + + if (GB_CheckObject(object)) + return; + + name = GB_ToZeroString(ARG(property)); + + GB_GetProperty(object, name); + GB_ReturnConvVariant(); + +END_METHOD + + +BEGIN_METHOD(Object_SetProperty, GB_OBJECT object; GB_STRING property; GB_VARIANT value) + + const char *name; + void *object = VARG(object); + + if (GB_CheckObject(object)) + return; + + name = GB_ToZeroString(ARG(property)); + + GB_SetProperty(object, name, (GB_VALUE *)ARG(value)); + +END_METHOD + + +BEGIN_METHOD(Object_Attach, GB_OBJECT object; GB_OBJECT parent; GB_STRING name) + + void *object = VARG(object); + void *parent = VARG(parent); + char *name = GB_ToZeroString(ARG(name)); + + if (GB_CheckObject(object)) + return; + + if (!parent || !name || !*name) + { + OBJECT_detach(object); + return; + } + + if (GB_CheckObject(parent)) + return; + + OBJECT_attach(object, parent, name); + + /*if (OBJECT_is(object, CLASS_Observer)) + COBSERVER_attach((COBSERVER *)object, parent, GB_ToZeroString(ARG(name)));*/ + +END_METHOD + + +BEGIN_METHOD(Object_Detach, GB_OBJECT object) + + void *object = VARG(object); + + if (GB_CheckObject(object)) + return; + + if (OBJECT_is(object, CLASS_Observer)) + COBSERVER_detach((COBSERVER *)object); + + OBJECT_detach(object); + +END_METHOD + + +BEGIN_METHOD(Object_Parent, GB_OBJECT object) + + void *object = VARG(object); + + if (GB_CheckObject(object)) + return; + + GB_ReturnObject(OBJECT_parent(object)); + +END_METHOD + + +BEGIN_METHOD(Object_Class, GB_OBJECT object) + + void *object = VARG(object); + + if (check_null(object)) + return; + + GB_ReturnObject(OBJECT_class(object)); + +END_METHOD + + +BEGIN_METHOD(Object_Type, GB_OBJECT object) + + void *object = VARG(object); + + if (check_null(object)) + return; + + GB_ReturnConstZeroString(OBJECT_class(object)->name); + +END_METHOD + + +BEGIN_METHOD(Object_Is, GB_OBJECT object; GB_STRING class) + + void *object = VARG(object); + CLASS *class = CLASS_look(STRING(class), LENGTH(class)); + + if (check_null(object)) + return; + + if (!class) + { + GB_ReturnBoolean(FALSE); + return; + } + + GB_ReturnBoolean(OBJECT_class(object) == class || CLASS_inherits(OBJECT_class(object), class)); + +END_METHOD + + +BEGIN_METHOD(Object_Call, GB_OBJECT object; GB_STRING method; GB_OBJECT params) + + int i; + int np = 0; + char *name = GB_ToZeroString(ARG(method)); + void *object = VARG(object); + GB_FUNCTION func; + GB_ARRAY params = VARGOPT(params, NULL); + + if (GB_CheckObject(object)) + return; + + if (GB_GetFunction(&func, object, name, NULL, NULL)) + { + error(E_NSYMBOL, OBJECT_class(object), name); + return; + } + + if (params) + np = GB_ArrayCount(params); + + if (np) + { + STACK_check(np); + + for (i = 0; i < np; i++) + { + CARRAY_get_value(params, i, SP); + PUSH(); + } + } + + GB_Call(&func, np, FALSE); + + if (TEMP.type != T_VOID) + GB_ReturnConvVariant(); + +END_METHOD + +BEGIN_METHOD(Object_Raise, GB_OBJECT object; GB_STRING event; GB_OBJECT params) + + CLASS *class; + CLASS_DESC *desc; + int index; + int i, np; + void *object = VARG(object); + GB_ARRAY params = VARGOPT(params, NULL); + int len = LENGTH(event); + char event[len + 2]; + + if (GB_CheckObject(object)) + return; + + class = OBJECT_class(object); + + event[0] = ':'; + memcpy(&event[1], STRING(event), len); + event[len + 1] = 0; + + index = CLASS_find_symbol(class, event); + if (index == NO_SYMBOL) + goto __UNKNOWN_EVENT; + + desc = class->table[index].desc; + if (CLASS_DESC_get_type(desc) != CD_EVENT) + goto __UNKNOWN_EVENT; + + if (params) + np = GB_ArrayCount(params); + else + np = 0; + + if (np) + { + STACK_check(np); + + for (i = 0; i < np; i++) + { + CARRAY_get_value(params, i, SP); + PUSH(); + } + } + + GB_ReturnBoolean(GB_Raise(object, desc->event.index, -np)); + return; + +__UNKNOWN_EVENT: + + GB_Error("Unknown event"); + +END_METHOD + + +BEGIN_METHOD(Object_IsValid, GB_OBJECT object) + + GB_ReturnBoolean(OBJECT_is_valid(VARG(object))); + +END_METHOD + + +BEGIN_METHOD(Object_Lock, GB_OBJECT object) + + void *object = VARG(object); + + if (GB_CheckObject(object)) + return; + + OBJECT_lock(object, TRUE); + +END_METHOD + + +BEGIN_METHOD(Object_Unlock, GB_OBJECT object) + + void *object = VARG(object); + + if (GB_CheckObject(object)) + return; + + OBJECT_lock(object, FALSE); + +END_METHOD + + +BEGIN_METHOD(Object_IsLocked, GB_OBJECT object) + + void *object = VARG(object); + + if (GB_CheckObject(object)) + return; + + GB_ReturnBoolean(OBJECT_is_locked(object)); + +END_METHOD + + +BEGIN_METHOD(Object_Count, GB_OBJECT object) + + void *object = VARG(object); + + if (GB_CheckObject(object)) + return; + + // We substract one because the object is referenced on the stack + GB_ReturnInteger(OBJECT_count(object) - 1); + +END_METHOD + + +BEGIN_METHOD(Object_SizeOf, GB_OBJECT object) + + void *object = VARG(object); + + if (check_null(object)) + return; + + GB_ReturnInteger(CLASS_sizeof(OBJECT_class(object))); + +END_METHOD + + +BEGIN_METHOD(Object_New, GB_STRING class; GB_OBJECT params) + + CLASS *class = CLASS_find(GB_ToZeroString(ARG(class))); + GB_ARRAY params = VARGOPT(params, NULL); + int i, np = 0; + void *object; + + if (!class) + { + GB_Error("Unknown class"); + return; + } + + if (params) + np = GB_ArrayCount(params); + + if (np) + { + STACK_check(np); + + for (i = 0; i < np; i++) + { + CARRAY_get_value(params, i, SP); + PUSH(); + } + } + + object = EXEC_create_object(class, np, NULL); + OBJECT_UNREF_KEEP(object); + GB_ReturnObject(object); + +END_METHOD + + +BEGIN_PROPERTY(Object_Address) + + GB_ReturnPointer(VPROP(GB_OBJECT)); + +END_PROPERTY + + +BEGIN_METHOD(Object_CanRaise, GB_OBJECT object; GB_STRING name) + + void *object = VARG(object); + CLASS *class; + char *name; + int index; + + if (!object) + { + GB_Error((char *)E_NULL); + return; + } + + class = OBJECT_class(object); + name = GB_ToZeroString(ARG(name)); + index = GB_GetEvent(class, name); + //fprintf(stderr, "Object.CanRaise: %s %d\n", name, index); + if (index < 0) + GB_ReturnBoolean(FALSE); + else + GB_ReturnBoolean(GB_CanRaise(object, index)); + +END_METHOD + +#endif + + +//------------------------------------------------------------------------- + +GB_DESC NATIVE_Symbol[] = +{ + GB_DECLARE_VIRTUAL(".Symbol"), + + GB_PROPERTY_READ("Name", "s", Symbol_Name), + GB_PROPERTY_READ("Kind", "i", Symbol_Kind), + GB_PROPERTY_READ("Type", "s", Symbol_Type), + GB_PROPERTY_READ("ReadOnly", "b", Symbol_ReadOnly), + GB_PROPERTY_READ("Hidden", "b", Symbol_Hidden), + GB_PROPERTY_READ("Signature", "s", Symbol_Signature), + GB_PROPERTY_READ("Static", "b", Symbol_Static), + GB_PROPERTY_READ("Value", "v", Symbol_Value), + + GB_END_DECLARE +}; + +GB_DESC NATIVE_Components[] = +{ + GB_DECLARE_STATIC("Components"), + + GB_STATIC_METHOD("_next", "Component", Components_next, NULL), + GB_STATIC_METHOD("_get", "Component", Components_get, "(Name)s"), + GB_STATIC_PROPERTY_READ("Count", "i", Components_Count), + + GB_END_DECLARE +}; + +GB_DESC NATIVE_Component[] = +{ + GB_DECLARE("Component", sizeof(COMPONENT)), + GB_NOT_CREATABLE(), + + //GB_STATIC_METHOD("_get", "Component", library_get, "(Name)s"), + GB_STATIC_METHOD("Load", "Component", Component_Load, "(Name)s"), + GB_STATIC_METHOD("IsLoaded", "b", Component_IsLoaded, "(Name)s"), + GB_STATIC_METHOD("FindFromPath", "s", Component_FindFromPath, "(Path)s"), + GB_STATIC_PROPERTY_READ("Path", "s", Component_Path), + + GB_PROPERTY_READ("Name", "s", Component_Name), + GB_PROPERTY_READ("Version", "s", Component_Version), + GB_PROPERTY_READ("Library", "b", Component_Library), + + GB_END_DECLARE +}; + +GB_DESC NATIVE_Classes[] = +{ + GB_DECLARE_STATIC("Classes"), + + GB_STATIC_METHOD("_next", "Class", Classes_next, NULL), + GB_STATIC_METHOD("_get", "Class", Classes_get, "(Name)s"), + GB_STATIC_PROPERTY_READ("Count", "i", Classes_count), + + GB_END_DECLARE +}; + +GB_DESC NATIVE_Class[] = +{ + GB_DECLARE("Class", sizeof(CLASS)), + GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("Load", "Class", Class_Load, "(Name)s"), + GB_STATIC_METHOD("IsLoaded", "b", Class_IsLoaded, "(Name)s"), + + GB_METHOD("_get", ".Symbol", Class_get, "(Symbol)s"), + + GB_PROPERTY_READ("Name", "s", Class_Name), + GB_PROPERTY_READ("Hidden", "b", Class_Hidden), + GB_PROPERTY_READ("Native", "b", Class_Native), + GB_PROPERTY_READ("Parent", "Class", Class_Parent), + GB_PROPERTY_READ("Component", "Component", Class_Component), + GB_PROPERTY_READ("Count", "i", Class_Count), + GB_PROPERTY_READ("Instance", "o", Class_Instance), + GB_PROPERTY_READ("Symbols", "String[]", Class_Symbols), + GB_METHOD("AutoCreate", "o", Class_AutoCreate, NULL), + GB_METHOD("New", "o", Class_New, "[(Arguments)Array;]"), + GB_METHOD("Exist", "b", Class_Exist, "(Symbol)s"), + + GB_CONSTANT("Variable", "i", 1), + GB_CONSTANT("Property", "i", 2), + GB_CONSTANT("Method", "i", 3), + GB_CONSTANT("Event", "i", 4), + GB_CONSTANT("Constant", "i", 5), + + GB_END_DECLARE +}; + +GB_DESC NATIVE_Object[] = +{ + GB_DECLARE_STATIC("Object"), + + GB_STATIC_METHOD("GetProperty", "v", Object_GetProperty, "(Object)o(Property)s"), + GB_STATIC_METHOD("SetProperty", NULL, Object_SetProperty, "(Object)o(Property)s(Value)v"), + GB_STATIC_METHOD("Attach", NULL, Object_Attach, "(Object)o(Parent)o(Name)s"), + GB_STATIC_METHOD("Detach", NULL, Object_Detach, "(Object)o"), + GB_STATIC_METHOD("Class", "Class", Object_Class, "(Object)o"), + GB_STATIC_METHOD("Type", "s", Object_Type, "(Object)o"), + GB_STATIC_METHOD("Is", "b", Object_Is, "(Object)o(Class)s"), + GB_STATIC_METHOD("Parent", "o", Object_Parent, "(Object)o"), + GB_STATIC_METHOD("Call", "v", Object_Call, "(Object)o(Method)s[(Arguments)Array;]"), + GB_STATIC_METHOD("New", "o", Object_New, "(Class)s[(Arguments)Array;]"), + GB_STATIC_METHOD("IsValid", "b", Object_IsValid, "(Object)o"), + GB_STATIC_METHOD("Lock", NULL, Object_Lock, "(Object)o"), + GB_STATIC_METHOD("Unlock", NULL, Object_Unlock, "(Object)o"), + GB_STATIC_METHOD("IsLocked", "b", Object_IsLocked, "(Object)o"), + GB_STATIC_METHOD("Count", "i", Object_Count, "(Object)o"), + GB_STATIC_METHOD("SizeOf", "i", Object_SizeOf, "(Object)o"), + GB_STATIC_METHOD("Address", "p", Object_Address, "(Object)o"), + GB_STATIC_METHOD("CanRaise", "b", Object_CanRaise, "(Object)o(Event)s"), + GB_STATIC_METHOD("Raise", "b", Object_Raise, "(Object)o(Event)s[(Arguments)Array;]"), + + GB_END_DECLARE +}; + diff --git a/main/gbx/gbx_c_class.h b/main/gbx/gbx_c_class.h new file mode 100644 index 00000000..054e6979 --- /dev/null +++ b/main/gbx/gbx_c_class.h @@ -0,0 +1,38 @@ +/*************************************************************************** + + gbx_c_class.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_CLASS_H +#define __GBX_C_CLASS_H + +#include "gambas.h" + +#ifndef __GBX_C_CLASS_C +extern GB_DESC NATIVE_Component[]; +extern GB_DESC NATIVE_Components[]; +extern GB_DESC NATIVE_Class[]; +extern GB_DESC NATIVE_Classes[]; +extern GB_DESC NATIVE_Object[]; +extern GB_DESC NATIVE_Symbol[]; +#endif + +#endif diff --git a/main/gbx/gbx_c_collection.c b/main/gbx/gbx_c_collection.c new file mode 100644 index 00000000..6a2eff3b --- /dev/null +++ b/main/gbx/gbx_c_collection.c @@ -0,0 +1,422 @@ +/*************************************************************************** + + gbx_c_collection.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_COLLECTION_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include "gb_common.h" +#include "gb_error.h" + +#include "gbx_variant.h" +#include "gambas.h" +#include "gbx_api.h" +#include "gbx_class.h" +#include "gbx_object.h" + +#include "gbx_c_collection.h" + + +static void clear(CCOLLECTION *col) +{ + VARIANT *value; + HASH_ENUM iter; + + CLEAR(&iter); + col->locked = TRUE; + + for(;;) + { + value = HASH_TABLE_next(col->hash_table, &iter, FALSE); + if (value == NULL) + break; + + VARIANT_free(value); + value->type = T_NULL; + } + + HASH_TABLE_delete(&col->hash_table); + col->locked = FALSE; +} + +#define get_key(_col, _key, _len, _set_last) HASH_TABLE_lookup((_col)->hash_table, (_key), (_len), _set_last) + +#define add_key(_col, _key, _len) ((_len) == 0 ? (GB_Error((char *)E_VKEY), NULL) : HASH_TABLE_insert(((CCOLLECTION *)(_col))->hash_table, (_key), (_len))) + +static void remove_key(CCOLLECTION *col, const char *key, int len) +{ + void *value; + HASH_NODE *last; + HASH_NODE *save; + void *save_enum; + + if (len == 0) + { + GB_Error((char *)E_VKEY); + return; + } + + save = col->hash_table->last; + + value = HASH_TABLE_lookup(col->hash_table, key, len, TRUE); + + last = col->hash_table->last; + col->hash_table->last = save; + + if (value == NULL) + return; + + if (last) + { + save_enum = GB_BeginEnum(col); + while (!GB_NextEnum()) + { + HASH_ENUM *iter = (HASH_ENUM *)GB_GetEnum(); + if (iter->next == last) + iter->next = iter->next->snext; + } + GB_EndEnum(save_enum); + } + + VARIANT_free((VARIANT *)value); + + if (!col->locked) + HASH_TABLE_remove(col->hash_table, key, len); + else // Prevent the freed variant to be freed twice if the collection is locked (i.e. being destroyed) + ((VARIANT *)value)->type = T_NULL; +} + + +BEGIN_METHOD(Collection_new, GB_INTEGER mode) + + int mode = MISSING(mode) ? 0 : VARG(mode); + + HASH_TABLE_create(&THIS->hash_table, TYPE_sizeof(T_VARIANT), mode); + THIS->last = NULL; + THIS->default_value.type = GB_T_NULL; + +END_METHOD + + +BEGIN_METHOD_VOID(Collection_free) + + clear(THIS); + GB_StoreVariant(NULL, POINTER(&THIS->default_value)); + +END_METHOD + + +BEGIN_PROPERTY(Collection_Count) + + GB_ReturnInt(CCOLLECTION_get_count(THIS)); + +END_PROPERTY + + +BEGIN_PROPERTY(Collection_Key) + + char *key; + int len; + + if (READ_PROPERTY) + { + if (HASH_TABLE_get_last_key(THIS->hash_table, &key, &len)) + GB_ReturnVoidString(); + else + GB_ReturnNewString(key, len); + } + else + HASH_TABLE_set_last_key(THIS->hash_table, PSTRING(), PLENGTH()); + +END_PROPERTY + + +BEGIN_METHOD(Collection_Add, GB_VARIANT value; GB_STRING key) + + void *data; + + data = add_key(THIS, STRING(key), LENGTH(key)); + if (!data) + return; + + GB_StoreVariant(ARG(value), data); + +END_METHOD + + +BEGIN_METHOD(Collection_Exist, GB_STRING key) + + GB_ReturnBoolean(get_key(THIS, STRING(key), LENGTH(key), FALSE) != NULL); + +END_METHOD + + +BEGIN_METHOD(Collection_Remove, GB_STRING key) + + remove_key(THIS, STRING(key), LENGTH(key)); + +END_METHOD + + +BEGIN_METHOD_VOID(Collection_Clear) + + int mode = THIS->hash_table->mode; + + /* Stops all iterators */ + GB_StopAllEnum(THIS); + + clear(THIS); + HASH_TABLE_create(&THIS->hash_table, TYPE_sizeof(T_VARIANT), mode); + +END_METHOD + + +BEGIN_METHOD_VOID(Collection_next) + + void *value; + HASH_TABLE *hash_table = OBJECT(CCOLLECTION)->hash_table; + HASH_ENUM *iter = (HASH_ENUM *)GB_GetEnum(); + + value = HASH_TABLE_next(hash_table, iter, TRUE); + + if (value == NULL) + GB_StopEnum(); + else + GB_ReturnVariant(value); + +END_METHOD + + +BEGIN_METHOD(Collection_get, GB_STRING key) + + void *value = get_key(THIS, STRING(key), LENGTH(key), TRUE); + if (!value) + { + if (THIS->has_default) + value = (void *)&THIS->default_value; + } + + GB_ReturnVariant(value); + +END_METHOD + + +BEGIN_METHOD(Collection_put, GB_VARIANT value; GB_STRING key) + + void *data; + + if (VARIANT_is_null((VARIANT *)&VARG(value))) + remove_key(THIS, STRING(key), LENGTH(key)); + else + { + data = add_key(THIS, STRING(key), LENGTH(key)); + if (!data) + return; + GB_StoreVariant(ARG(value), data); + } + +END_METHOD + + +BEGIN_METHOD_VOID(Collection_Copy) + + GB_COLLECTION col; + GB_COLLECTION_ITER iter;; + GB_VARIANT value; + char *key; + int len; + + GB_CollectionNew(&col, THIS->mode); + + CLEAR(&iter); + + for(;;) + { + if (GB_CollectionEnum(THIS, &iter, &value, &key, &len)) + break; + + GB_CollectionSet(col, key, len, &value); + } + + GB_ReturnObject(col); + +END_METHOD + + +BEGIN_PROPERTY(Collection_Default) + + if (READ_PROPERTY) + GB_ReturnVariant(&THIS->default_value); + else + { + GB_StoreVariant(PROP(GB_VARIANT), POINTER(&THIS->default_value)); + THIS->has_default = THIS->default_value.type != GB_T_NULL; + } + +END_PROPERTY + +static void return_node_key(HASH_TABLE *hash_table, HASH_NODE *node) +{ + char *key; + int len; + + HASH_TABLE_get_key(hash_table, node, &key, &len); + if (len) + GB_ReturnNewString(key, len); + else + GB_ReturnNull(); + +} + +BEGIN_PROPERTY(Collection_First) + + return_node_key(THIS->hash_table, THIS->hash_table->sfirst); + +END_PROPERTY + +BEGIN_PROPERTY(Collection_Last) + + return_node_key(THIS->hash_table, THIS->hash_table->slast); + +END_PROPERTY + +#endif + + +GB_DESC NATIVE_Collection[] = +{ + GB_DECLARE("Collection", sizeof(CCOLLECTION)), + + GB_METHOD("_new", NULL, Collection_new, "[(Mode)i]"), + GB_METHOD("_free", NULL, Collection_free, NULL), + + GB_PROPERTY_READ("Count", "i", Collection_Count), + GB_PROPERTY_READ("Length", "i", Collection_Count), + GB_PROPERTY_READ("First", "s", Collection_First), + GB_PROPERTY_READ("Last", "s", Collection_Last), + GB_PROPERTY("Key", "s", Collection_Key), + GB_PROPERTY("Default", "v", Collection_Default), + + GB_METHOD("Add", NULL, Collection_Add, "(Value)v(Key)s"), + GB_METHOD("Exist", "b", Collection_Exist, "(Key)s"), + GB_METHOD("Remove", NULL, Collection_Remove, "(Key)s"), + GB_METHOD("Clear", NULL, Collection_Clear, NULL), + /*GB_METHOD("_first", NULL, collection_first, NULL),*/ + GB_METHOD("_next", "v", Collection_next, NULL), + GB_METHOD("_get", "v", Collection_get, "(Key)s"), + GB_METHOD("_put", NULL, Collection_put, "(Value)v(Key)s"), + GB_METHOD("Copy", "Collection", Collection_Copy, NULL), + + GB_END_DECLARE +}; + +#ifndef GBX_INFO + +void GB_CollectionNew(GB_COLLECTION *col, int mode) +{ + VALUE param; + + param._integer.type = GB_T_INTEGER; + param._integer.value = mode; + + *col = OBJECT_create_native(CLASS_Collection, ¶m); +} + +int GB_CollectionCount(GB_COLLECTION col) +{ + return HASH_TABLE_size(((CCOLLECTION *)col)->hash_table); +} + +bool GB_CollectionSet(GB_COLLECTION col, const char *key, int len, GB_VARIANT *value) +{ + VARIANT *data; + + if (VARIANT_is_null((VARIANT *)&value->value)) + remove_key((CCOLLECTION *)col, key, len); + else + { + data = (VARIANT *)add_key((CCOLLECTION *)col, key, len); + if (!data) + return TRUE; + VALUE_write_variant((VALUE *)value, data); + } + return FALSE; +} + +bool GB_CollectionGet(GB_COLLECTION col, const char *key, int len, GB_VARIANT *value) +{ + CCOLLECTION *_object = (CCOLLECTION *)col; + VARIANT *var; + bool ret; + + value->type = T_VARIANT; + + var = (VARIANT *)get_key(THIS, key, len, TRUE); + + if (!var) + { + if (!THIS->has_default) + { + value->value.type = GB_T_NULL; + return TRUE; + } + + var = (VARIANT *)&THIS->default_value; + ret = TRUE; + } + else + { + ret = FALSE; + } + + value->value.type = var->type; + value->value.value.data = var->value.data; + return ret; +} + +bool GB_CollectionEnum(GB_COLLECTION col, GB_COLLECTION_ITER *iter, GB_VARIANT *value, char **key, int *len) +{ + VARIANT *val; + HASH_TABLE *hash_table = ((CCOLLECTION *)col)->hash_table; + + if (!value || !key) + { + CLEAR(iter); + return FALSE; + } + + val = HASH_TABLE_next(hash_table, (HASH_ENUM *)iter, TRUE); + if (!val) + return TRUE; + + value->type = T_VARIANT; + value->value.type = val->type; + value->value.value.data = val->value.data; + + HASH_TABLE_get_last_key(hash_table, key, len); + return FALSE; +} + +#endif diff --git a/main/gbx/gbx_c_collection.h b/main/gbx/gbx_c_collection.h new file mode 100644 index 00000000..3bcae972 --- /dev/null +++ b/main/gbx/gbx_c_collection.h @@ -0,0 +1,70 @@ +/*************************************************************************** + + gbx_c_collection.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_COLLECTION_H +#define __GBX_C_COLLECTION_H + +#include "gambas.h" + +#include "gb_hash.h" +#include "gbx_object.h" + +typedef + struct { + int sort; + void *data; + } + CCOL_DESC; + +typedef + struct { + OBJECT object; + HASH_TABLE *hash_table; + HASH_NODE *last; + GB_VARIANT_VALUE default_value; + short mode; + unsigned locked : 1; + unsigned has_default : 1; + } + CCOLLECTION; + +#ifndef __GBX_C_COLLECTION_C + +extern GB_DESC NATIVE_Collection[]; + +#else + +#define THIS ((CCOLLECTION *)_object) + +#endif + +#define CCOLLECTION_get_count(_col) HASH_TABLE_size((_col)->hash_table) + +/*PUBLIC void *CCOLLECTION_new(TYPE type);*/ +/* +void *CCOLLECTION_get_key(CCOLLECTION *col, char *key, int len); +void *CCOLLECTION_add_key(CCOLLECTION *col, char *key, int len); +void CCOLLECTION_remove_key(CCOLLECTION *col, char *key, int len); +*/ + +#endif diff --git a/main/gbx/gbx_c_enum.c b/main/gbx/gbx_c_enum.c new file mode 100644 index 00000000..f13318b3 --- /dev/null +++ b/main/gbx/gbx_c_enum.c @@ -0,0 +1,207 @@ +/*************************************************************************** + + gbx_c_enum.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_ENUM_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include "gb_common.h" +#include "gbx_class.h" + +#include "gambas.h" +#include "gbx_api.h" + +#include "gbx_exec.h" +#include "gbx_object.h" +#include "gbx_c_enum.h" + +//#define DEBUG_ME + +static CENUM *_enum_list = NULL; + + +CENUM *CENUM_create(void *enum_object) +{ + CENUM *_object; + + _object = OBJECT_new(CLASS_Enum, NULL, NULL); + OBJECT_UNREF_KEEP(_object); + + THIS->enum_object = enum_object; + OBJECT_REF(enum_object); + + LIST_insert(&_enum_list, THIS, &THIS->list); + + #ifdef DEBUG_ME + fprintf(stderr, "CENUM_create: %p <%p>\n", THIS, enum_object); + #endif + + return THIS; +} + +CENUM *CENUM_get_next(CENUM *cenum) +{ + if (!cenum) + return _enum_list; + else + return cenum->list.next; +} + + +BEGIN_METHOD_VOID(CENUM_free) + + #ifdef DEBUG_ME + fprintf(stderr, "CENUM_free: %p <%p>\n", THIS, THIS->enum_object); + #endif + + LIST_remove(&_enum_list, THIS, &THIS->list); + OBJECT_UNREF(THIS->enum_object); + + if (THIS->variant) + GB_StoreVariant(NULL, THIS->data); + +END_METHOD + +static bool check_enum() +{ + if (!EXEC_enum) + { + GB_Error("No current enumeration"); + return TRUE; + } + else + return FALSE; +} + +BEGIN_METHOD_VOID(CENUM_stop) + + #ifdef DEBUG_ME + fprintf(stderr, "CENUM_stop: %p <%p>\n", EXEC_enum, EXEC_enum->enum_object); + #endif + + if (check_enum()) + return; + + EXEC_enum->stop = TRUE; + +END_METHOD + + +BEGIN_PROPERTY(CENUM_index) + + if (check_enum()) + return; + + _object = EXEC_enum; + + if (READ_PROPERTY) + { + if (!THIS->variant) + { + #ifdef DEBUG_ME + fprintf(stderr, "CENUM_index: %p <%p>: -> NULL\n", THIS, THIS->enum_object); + #endif + GB_ReturnVariant(NULL); + } + else + { + #ifdef DEBUG_ME + fprintf(stderr, "CENUM_index: %p <%p>: -> value (%ld)\n", THIS, THIS->enum_object, ((VARIANT *)THIS->data)->type); + #endif + GB_ReturnVariant((GB_VARIANT_VALUE *)THIS->data); + } + } + else + { + #ifdef DEBUG_ME + fprintf(stderr, "CENUM_index: %p <%p>: set: variant = %d\n", THIS, THIS->enum_object, THIS->variant); + #endif + if (!THIS->variant) + ((VARIANT *)THIS->data)->type = T_NULL; + + GB_StoreVariant(PROP(GB_VARIANT), THIS->data); + THIS->variant = TRUE; + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(CENUM_next) + + void *enum_object = OP ? (void *)OP : (void *)CP; + CENUM **pcenum = (CENUM **)GB_GetEnum(); + CENUM *cenum; + + if (*pcenum == NULL) + cenum = _enum_list; + else + cenum = *pcenum; + + for(;;) + { + if (!cenum) + { + GB_StopEnum(); + return; + } + + if (cenum->enum_object == enum_object) + break; + + cenum = cenum->list.next; + } + + *pcenum = cenum->list.next; + EXEC_enum = cenum; + +END_METHOD + +BEGIN_PROPERTY(Enum_Stopped) + + if (check_enum()) + return; + + GB_ReturnBoolean(EXEC_enum->stop); + +END_PROPERTY + +#endif + +GB_DESC NATIVE_Enum[] = +{ + GB_DECLARE("Enum", sizeof(CENUM)), GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, CENUM_free, NULL), + + GB_STATIC_PROPERTY("Index", "v", CENUM_index), + GB_STATIC_PROPERTY_READ("Stopped", "b", Enum_Stopped), + GB_STATIC_METHOD("Stop", NULL, CENUM_stop, NULL), + + GB_STATIC_METHOD("_next", NULL, CENUM_next, NULL), + + GB_END_DECLARE +}; + + diff --git a/main/gbx/gbx_c_enum.h b/main/gbx/gbx_c_enum.h new file mode 100644 index 00000000..413aa1d2 --- /dev/null +++ b/main/gbx/gbx_c_enum.h @@ -0,0 +1,53 @@ +/*************************************************************************** + + gbx_c_enum.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_ENUM_H +#define __GBX_C_ENUM_H + +#include "gambas.h" +#include "gb_list.h" +#include "gbx_variant.h" + +typedef + struct CENUM { + GB_BASE object; + LIST list; + void *enum_object; + void *data[4]; + unsigned stop : 1; + unsigned variant : 1; + } + CENUM; + +#ifndef __GBX_C_ENUM_C +extern GB_DESC NATIVE_Enum[]; +#else + +#define THIS ((CENUM *)_object) + +#endif + +CENUM *CENUM_create(void *object); +CENUM *CENUM_get_next(CENUM *); + +#endif diff --git a/main/gbx/gbx_c_error.c b/main/gbx/gbx_c_error.c new file mode 100644 index 00000000..3b9ad112 --- /dev/null +++ b/main/gbx/gbx_c_error.c @@ -0,0 +1,168 @@ +/*************************************************************************** + + gbx_c_error.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_ERROR_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include "gb_common.h" +#include "gbx_class.h" +#include "gbx_debug.h" + +#include "gambas.h" +#include "gbx_api.h" + +#include "gbx_object.h" +#include "gbx_string.h" +#include "gbx_split.h" +#include "gbx_stack.h" +#include "gbx_exec.h" +#include "gbx_c_array.h" + +#include "gbx_c_error.h" + + +BEGIN_PROPERTY(Error_Code) + + GB_ReturnInt(ERROR_last.code); + +END_PROPERTY + +static char **_arg; + +static void get_subst(int np, char **str, int *len) +{ + *str = _arg[np]; + *len = STRING_length(_arg[np]); +} + + +BEGIN_PROPERTY(Error_Text) + + if (ERROR_last.code && ERROR_last.msg) + { + if (EXEC_debug) + { + GB_ARRAY array; + char *result; + + array = STRING_split(ERROR_last.msg, strlen(ERROR_last.msg), "|", 1, NULL, 0, FALSE, FALSE); + _arg = (char **)GB_ArrayGet(array, 0); + + result = STRING_subst(_arg[0], STRING_length(_arg[0]), get_subst); + + OBJECT_UNREF(array); + + GB_ReturnNewZeroString(result); + } + else + { + if (ERROR_last.free) + GB_ReturnString(ERROR_last.msg); + else + GB_ReturnConstZeroString(ERROR_last.msg); + } + } + else + GB_ReturnVoidString(); + +END_PROPERTY + + +BEGIN_PROPERTY(Error_Class) + + if (ERROR_last.code) + GB_ReturnObject(ERROR_last.cp); + else + GB_ReturnNull(); + +END_PROPERTY + + +BEGIN_PROPERTY(Error_Where) + + if (ERROR_last.code) + GB_ReturnNewZeroString(DEBUG_get_position(ERROR_last.cp, ERROR_last.fp, ERROR_last.pc)); + else + GB_ReturnVoidString(); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Error_Clear) + + ERROR_reset(&ERROR_last); + +END_METHOD + + +BEGIN_METHOD(Error_Raise, GB_STRING msg) + + ERROR_define(GB_ToZeroString(ARG(msg)), NULL); + EXEC_set_native_error(TRUE); + +END_METHOD + + +BEGIN_METHOD_VOID(Error_Propagate) + + if (ERROR_last.code) + { + ERROR_define_last(); + EXEC_set_native_error(TRUE); + } + +END_METHOD + + +BEGIN_PROPERTY(Error_Backtrace) + + if (ERROR_backtrace) + GB_ReturnObject(DEBUG_get_string_array_from_backtrace(ERROR_backtrace)); + else + GB_ReturnNull(); + +END_PROPERTY + +#endif + +GB_DESC NATIVE_Error[] = +{ + GB_DECLARE_STATIC("Error"), + + GB_STATIC_PROPERTY_READ("Code", "i", Error_Code), + GB_STATIC_PROPERTY_READ("Text", "s", Error_Text), + GB_STATIC_PROPERTY_READ("Class", "Class", Error_Class), + GB_STATIC_PROPERTY_READ("Where", "s", Error_Where), + GB_STATIC_PROPERTY_READ("Backtrace", "String[]", Error_Backtrace), + + GB_STATIC_METHOD("Clear", NULL, Error_Clear, NULL), + GB_STATIC_METHOD("Raise", NULL, Error_Raise, "(Message)s"), + GB_STATIC_METHOD("Propagate", NULL, Error_Propagate, NULL), + + GB_END_DECLARE +}; + + diff --git a/main/gbx/gbx_c_error.h b/main/gbx/gbx_c_error.h new file mode 100644 index 00000000..56dd6e6e --- /dev/null +++ b/main/gbx/gbx_c_error.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + gbx_c_error.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_ERROR_H +#define __GBX_C_ERROR_H + +#include "gambas.h" + +#ifndef __GBX_C_ERROR_C +extern GB_DESC NATIVE_Error[]; +#endif + +#endif diff --git a/main/gbx/gbx_c_file.c b/main/gbx/gbx_c_file.c new file mode 100644 index 00000000..9d404b4e --- /dev/null +++ b/main/gbx/gbx_c_file.c @@ -0,0 +1,1122 @@ +/*************************************************************************** + + gbx_c_file.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_FILE_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_list.h" +#include "gb_file.h" + +#include "gbx_api.h" +#include "gambas.h" +#include "gbx_class.h" +#include "gbx_stream.h" +#include "gbx_exec.h" +#include "gbx_project.h" +#include "gbx_string.h" +#include "gbx_date.h" +#include "gbx_watch.h" +#include "gbx_signal.h" + +#include "gbx_c_file.h" + +#define STREAM_FD STREAM_handle(CSTREAM_stream(THIS_STREAM)) + +CFILE *CFILE_in, *CFILE_out, *CFILE_err; + +DECLARE_EVENT(EVENT_Read); +DECLARE_EVENT(EVENT_Write); +DECLARE_EVENT(EVENT_Resize); + +static GB_FUNCTION _read_func; + +static char _buffer[16]; + +static bool _term_init = FALSE; +static ushort _term_width = 0; +static ushort _term_height = 0; +static SIGNAL_CALLBACK *_SIGWINCH_callback = NULL; +static GB_FUNCTION _term_resize_func; + + +static void callback_read(int fd, int type, CSTREAM *stream) +{ + if (!STREAM_read_ahead(CSTREAM_stream(stream))) + GB_Raise(stream, EVENT_Read, 0); + else + WATCH_little_sleep(); +} + +static void callback_write(int fd, int type, CSTREAM *stream) +{ + GB_Raise(stream, EVENT_Write, 0); +} + +static void cb_term_resize(int signum, intptr_t data) +{ + _term_width = _term_height = 0; + if (CFILE_in) + GB_Raise(CFILE_in, EVENT_Resize, 0); +} + +static void watch_stream(CSTREAM *_object, int mode, bool on) +{ + STREAM *stream = &THIS_STREAM->stream; + int fd = STREAM_handle(stream); + + if (mode & STO_READ) + GB_Watch(fd, GB_WATCH_READ, (void *)(on ? callback_read : NULL), (intptr_t)THIS); + + if (mode & STO_WRITE) + GB_Watch(fd, GB_WATCH_WRITE, (void *)(on ? callback_write : NULL), (intptr_t)THIS); +} + +CFILE *CFILE_create(STREAM *stream, int mode) +{ + CFILE *file = OBJECT_new(CLASS_File, NULL, NULL); + OBJECT_UNREF_KEEP(file); + + if (stream) + { + *CSTREAM_stream(file) = *stream; + //file->watch_fd = -1; + + if (mode & STO_WATCH) + { + watch_stream(&file->ob, mode, TRUE); + OBJECT_attach((OBJECT *)file, OP ? (OBJECT *)OP : (OBJECT *)CP, "File"); + } + } + + return file; +} + +static CFILE *create_default_stream(FILE *file, int mode) +{ + CFILE *ob; + STREAM stream; + bool tty = isatty(fileno(file)); + + CLEAR(&stream); + stream.type = &STREAM_buffer; + stream.common.available_now = !tty; + stream.common.no_read_ahead = tty; + stream.common.standard = TRUE; + stream.buffer.file = file; + STREAM_check_blocking(&stream); + ob = CFILE_create(&stream, mode); + OBJECT_REF(ob); + return ob; +} + +void CFILE_init(void) +{ + CFILE_in = create_default_stream(stdin, STO_READ); + CFILE_out = create_default_stream(stdout, STO_WRITE); + CFILE_err = create_default_stream(stderr, STO_WRITE); +} + +void CFILE_exit(void) +{ + OBJECT_UNREF(CFILE_in); + OBJECT_UNREF(CFILE_out); + OBJECT_UNREF(CFILE_err); + + if (_term_init) + SIGNAL_unregister(SIGWINCH, _SIGWINCH_callback); +} + +void CFILE_init_watch(void) +{ + bool has_term_func = GB_GetFunction(&_term_resize_func, PROJECT_class, "Application_Resize", "", "") == 0; + bool has_read_func = GB_GetFunction(&_read_func, PROJECT_class, "Application_Read", "", "") == 0; + + if (has_term_func || has_read_func) + OBJECT_attach((OBJECT *)CFILE_in, (OBJECT *)PROJECT_class, "Application"); + + if (has_read_func) + { + //fprintf(stderr, "watch stdin\n"); + //CFILE_in->watch_fd = STDIN_FILENO; + GB_Watch(STDIN_FILENO, GB_WATCH_READ, (void *)callback_read, (intptr_t)CFILE_in); + } +} + +BEGIN_METHOD_VOID(File_free) + + STREAM_close(&THIS->ob.stream); + +END_METHOD + + +BEGIN_PROPERTY(File_In) + + GB_ReturnObject(CFILE_in); + +END_PROPERTY + + +BEGIN_PROPERTY(File_Out) + + GB_ReturnObject(CFILE_out); + +END_PROPERTY + + +BEGIN_PROPERTY(File_Err) + + GB_ReturnObject(CFILE_err); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Stat_free) + + STRING_unref(&THIS_STAT->path); + +END_METHOD + + +BEGIN_PROPERTY(Stat_Type) + + GB_ReturnInt(THIS_STAT->info.type); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_Path) + + GB_ReturnString(THIS_STAT->path); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_Link) + + if (THIS_STAT->info.type == GB_STAT_LINK) + GB_ReturnNewZeroString(FILE_readlink(THIS_STAT->path)); + else + GB_ReturnVoidString(); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_Mode) + + GB_ReturnInt(THIS_STAT->info.mode); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_Hidden) + + GB_ReturnBoolean(THIS_STAT->info.hidden); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_Size) + + GB_ReturnLong(THIS_STAT->info.size); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_LastAccess) + + VALUE date; + + DATE_from_time(THIS_STAT->info.atime, 0, &date); + + GB_ReturnDate((GB_DATE *)&date); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_LastChange) + + VALUE date; + + DATE_from_time(THIS_STAT->info.ctime, 0, &date); + + GB_ReturnDate((GB_DATE *)&date); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_Time) + + VALUE date; + + DATE_from_time(THIS_STAT->info.mtime, 0, &date); + + GB_ReturnDate((GB_DATE *)&date); + +END_PROPERTY + + +static char *get_file_user(CFILE *_object) +{ + struct passwd *pwd; + uid_t uid = (uid_t)THIS_STAT->info.uid; + + if (uid == 0) + return "root"; + else + { + pwd = getpwuid(uid); + if (!pwd) + { + snprintf(_buffer, sizeof(_buffer), "%d", (int)uid); + return _buffer; + } + else + return pwd->pw_name; + } +} + +BEGIN_PROPERTY(Stat_User) + + GB_ReturnNewZeroString(get_file_user(THIS)); + +END_PROPERTY + + +static char *get_file_group(CFILE *_object) +{ + struct group *grp; + gid_t gid = (gid_t)THIS_STAT->info.gid; + + if (gid == 0) + return "root"; + else + { + grp = getgrgid(gid); + if (!grp) + { + snprintf(_buffer, sizeof(_buffer), "%d", (int)gid); + return _buffer; + } + else + return grp->gr_name; + } +} + +BEGIN_PROPERTY(Stat_Group) + + GB_ReturnNewZeroString(get_file_group(THIS)); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_SetUID) + + GB_ReturnBoolean(THIS_STAT->info.mode & S_ISUID); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_SetGID) + + GB_ReturnBoolean(THIS_STAT->info.mode & S_ISGID); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_Sticky) + + GB_ReturnBoolean(THIS_STAT->info.mode & S_ISVTX); + +END_PROPERTY + + +BEGIN_PROPERTY(Stat_Auth) + + char *auth = FILE_mode_to_string(THIS_STAT->info.mode); + + GB_ReturnNewString(auth, FILE_buffer_length()); + +END_PROPERTY + +#if 0 +BEGIN_METHOD(CFILE_access, GB_INTEGER who; GB_INTEGER access) + + bool ret; + int access = VARGOPT(access, GB_STAT_READ); + int who = VARG(who); + int mode = THIS_STAT->info.mode; + + if ((access & GB_STAT_READ) == 0) + mode &= ~(S_IRUSR | S_IRGRP | S_IROTH); + if ((access & GB_STAT_WRITE) == 0) + mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + if ((access & GB_STAT_EXEC) == 0) + mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); + + switch(who) + { + case GB_STAT_USER: ret = mode & S_IRWXU; break; + case GB_STAT_GROUP: ret = mode & S_IRWXG; break; + case GB_STAT_OTHER: ret = mode & S_IRWXO; break; + default: ret = FALSE; break; + } + + GB_ReturnBoolean(ret); + +END_METHOD +#endif + + +static void return_perm(CSTAT *_object, int rf, int wf, int xf) +{ + char perm[4]; + char *p; + int mode = THIS_STAT->info.mode; + + p = perm; + + if (mode & rf) *p++ = 'r'; + if (mode & wf) *p++ = 'w'; + if (mode & xf) *p++ = 'x'; + + *p = 0; + + GB_ReturnNewZeroString(perm); +} + + +BEGIN_PROPERTY(CFILE_perm_user) + + return_perm(THIS_STAT, S_IRUSR, S_IWUSR, S_IXUSR); + +END_PROPERTY + +BEGIN_PROPERTY(CFILE_perm_group) + + return_perm(THIS_STAT, S_IRGRP, S_IWGRP, S_IXGRP); + +END_PROPERTY + +BEGIN_PROPERTY(CFILE_perm_other) + + return_perm(THIS_STAT, S_IROTH, S_IWOTH, S_IXOTH); + +END_PROPERTY + +BEGIN_METHOD(CFILE_perm_get, GB_STRING user) + + char *who; + char *user = GB_ToZeroString(ARG(user)); + + who = get_file_user(THIS); + if (strcmp(user, who) == 0) + { + return_perm(THIS_STAT, S_IRUSR, S_IWUSR, S_IXUSR); + return; + } + + who = get_file_group(THIS); + if (strlen(user) > 2 && user[0] == '*' && user[1] == '.' && strcmp(&user[2], who) == 0) + { + return_perm(THIS_STAT, S_IRGRP, S_IWGRP, S_IXGRP); + return; + } + + return_perm(THIS_STAT, S_IROTH, S_IWOTH, S_IXOTH); + +END_METHOD + + +/*---- File path functions --------------------------------------------------*/ + +static char *_dir; +static char *_basename; +static char *_ext; + +static void split_path(char *path) +{ + char *p; + + p = rindex(path, '/'); + if (p) + { + if (p == path) + { + _dir = "/"; + _basename = path + 1; + } + else + { + *p = 0; + _dir = path; + _basename = p + 1; + } + } + else + { + _dir = ""; + _basename = path; + } + + p = rindex(_basename, '.'); + if (p) + { + *p = 0; + _ext = p + 1; + } + else + _ext = ""; +} + +static void return_path(void) +{ + char *tmp = NULL; + int len = strlen(_dir); + char *test; + + if (len) + { + tmp = STRING_add(tmp, _dir, len); + test = _basename ? _basename : _ext; + if (tmp[len - 1] != '/' && *test != '/') + tmp = STRING_add_char(tmp, '/'); + } + + if (_basename && *_basename) + tmp = STRING_add(tmp, _basename, 0); + + if (*_ext) + { + if (*_ext != '.') + tmp = STRING_add_char(tmp, '.'); + + tmp = STRING_add(tmp, _ext, 0); + } + + STRING_extend_end(tmp); + + GB_ReturnString(tmp); +} + +BEGIN_METHOD(File_Dir, GB_STRING path) + + split_path(GB_ToZeroString(ARG(path))); + GB_ReturnNewZeroString(_dir); + +END_METHOD + + +BEGIN_METHOD(File_SetDir, GB_STRING path; GB_STRING new_dir) + + split_path(GB_ToZeroString(ARG(path))); + _dir = GB_ToZeroString(ARG(new_dir)); + return_path(); + +END_METHOD + + +BEGIN_METHOD(File_Name, GB_STRING path) + + char *path; + + //if (LENGTH(path) && STRING(path)[LENGTH(path) - 1] == '/') + // LENGTH(path)--; + + path = GB_ToZeroString(ARG(path)); + GB_ReturnNewZeroString(FILE_get_name(path)); + +END_METHOD + + +BEGIN_METHOD(File_SetName, GB_STRING path; GB_STRING new_name) + + char *path; + + if (LENGTH(path) && STRING(path)[LENGTH(path) - 1] == '/') + LENGTH(path)--; + + path = GB_ToZeroString(ARG(path)); + + split_path(path); + _basename = GB_ToZeroString(ARG(new_name)); + _ext = ""; + return_path(); + +END_METHOD + + +BEGIN_METHOD(File_Ext, GB_STRING path) + + split_path(GB_ToZeroString(ARG(path))); + GB_ReturnNewZeroString(_ext); + +END_METHOD + + +BEGIN_METHOD(File_SetExt, GB_STRING path; GB_STRING new_ext) + + split_path(GB_ToZeroString(ARG(path))); + _ext = GB_ToZeroString(ARG(new_ext)); + return_path(); + +END_METHOD + + +BEGIN_METHOD(File_BaseName, GB_STRING path) + + split_path(GB_ToZeroString(ARG(path))); + GB_ReturnNewZeroString(_basename); + +END_METHOD + + +BEGIN_METHOD(File_SetBaseName, GB_STRING path; GB_STRING new_basename) + + split_path(GB_ToZeroString(ARG(path))); + _basename = GB_ToZeroString(ARG(new_basename)); + return_path(); + +END_METHOD + +static void error_CFILE_load_save(STREAM *stream) +{ + STREAM_close(stream); +} + +BEGIN_METHOD(File_Load, GB_STRING path) + + STREAM stream; + int64_t len; + int rlen; + char *str = NULL; + + STREAM_open(&stream, STRING_conv_file_name(STRING(path), LENGTH(path)), STO_READ); + + ON_ERROR_1(error_CFILE_load_save, &stream) + { + STREAM_lof(&stream, &len); + if (len >> 31) + THROW(E_MEMORY); + + if (len == 0) + { + char buffer[256]; + + str = NULL; + + for(;;) + { + len = STREAM_read_max(&stream, buffer, sizeof(buffer)); + if (len) str = STRING_add(str, buffer, len); + if (len < sizeof(buffer)) + break; + } + } + else + { + rlen = len; + + str = STRING_new(NULL, rlen); + rlen = STREAM_read_max(&stream, str, rlen); + str = STRING_extend(str, rlen); + } + + STREAM_close(&stream); + + STRING_free_later(str); + GB_ReturnString(str); + } + END_ERROR + +END_METHOD + +BEGIN_METHOD(File_Save, GB_STRING path; GB_STRING data) + + STREAM stream; + + STREAM_open(&stream, STRING_conv_file_name(STRING(path), LENGTH(path)), STO_CREATE); + + ON_ERROR_1(error_CFILE_load_save, &stream) + { + STREAM_write(&stream, STRING(data), LENGTH(data)); + STREAM_close(&stream); + } + END_ERROR + +END_METHOD + +BEGIN_METHOD(File_IsRelative, GB_STRING path) + + char *path = STRING(path); + int len = LENGTH(path); + + if (len <= 0) + { + GB_ReturnBoolean(FALSE); + return; + } + + GB_ReturnBoolean(FILE_is_relative(path)); + +END_METHOD + +BEGIN_METHOD(File_IsHidden, GB_STRING path) + + char *path = STRING(path); + int len = LENGTH(path); + int i; + char c; + + for (i = 0; i < len; i++) + { + if (path[i] != '.') + continue; + + if (i > 0 && path[i - 1] != '/') + continue; + + if (i == (len - 1)) + continue; + + c = path[i + 1]; + if (c == '/') + continue; + + if (c == '.') + { + if (i == (len - 2)) + continue; + + if (path[i + 2] == '/') + continue; + } + + GB_ReturnBoolean(TRUE); + return; + } + + GB_ReturnBoolean(FALSE); + +END_METHOD + +//--------------------------------------------------------------------------- + +BEGIN_PROPERTY(Stream_Handle) + + GB_ReturnInteger(STREAM_FD); + +END_PROPERTY + + +BEGIN_PROPERTY(Stream_ByteOrder) + + bool endian = EXEC_big_endian; + + if (READ_PROPERTY) + { + if (CSTREAM_stream(THIS_STREAM)->common.swap) + endian = !endian; + + GB_ReturnInteger(endian ? 1 : 0); + } + else + { + bool val = VPROP(GB_INTEGER); + CSTREAM_stream(THIS_STREAM)->common.swap = endian ^ val; + } + +END_PROPERTY + +BEGIN_PROPERTY(Stream_EndOfLine) + + if (READ_PROPERTY) + GB_ReturnInteger(CSTREAM_stream(THIS_STREAM)->common.eol); + else + { + int eol = VPROP(GB_INTEGER); + + if (eol >= 0 && eol <= 2) + CSTREAM_stream(THIS_STREAM)->common.eol = eol; + } + +END_PROPERTY + +BEGIN_METHOD_VOID(Stream_Close) + + STREAM_close(CSTREAM_stream(THIS_STREAM)); + +END_METHOD + +BEGIN_PROPERTY(Stream_EndOfFile) + + GB_ReturnBoolean(CSTREAM_stream(THIS_STREAM)->common.eof); + +END_PROPERTY + +BEGIN_PROPERTY(Stream_Blocking) + + if (READ_PROPERTY) + GB_ReturnBoolean(STREAM_is_blocking(CSTREAM_stream(THIS_STREAM))); + else + STREAM_blocking(CSTREAM_stream(THIS_STREAM), VPROP(GB_BOOLEAN)); + +END_PROPERTY + +BEGIN_PROPERTY(Stream_Tag) + + if (READ_PROPERTY) + GB_ReturnVariant(&THIS_STREAM->tag); + else + GB_StoreVariant(PROP(GB_VARIANT), POINTER(&(THIS_STREAM->tag))); + +END_METHOD + +BEGIN_METHOD_VOID(Stream_free) + + STREAM_close(CSTREAM_stream(THIS_STREAM)); + GB_StoreVariant(NULL, POINTER(&(THIS_STREAM->tag))); + +END_METHOD + +BEGIN_METHOD(Stream_ReadLine, GB_STRING escape) + + char *escape; + char *str; + + if (MISSING(escape)) + escape = NULL; + else + { + escape = GB_ToZeroString(ARG(escape)); + if (!*escape) + escape = NULL; + } + + str = STREAM_line_input(CSTREAM_stream(THIS_STREAM), escape); + STRING_free_later(str); + GB_ReturnString(str); + +END_METHOD + +BEGIN_METHOD_VOID(Stream_Begin) + + STREAM_begin(CSTREAM_stream(THIS_STREAM)); + +END_METHOD + +BEGIN_METHOD_VOID(Stream_End) + + STREAM_end(CSTREAM_stream(THIS_STREAM)); + +END_METHOD + +BEGIN_METHOD_VOID(Stream_Cancel) + + STREAM_cancel(CSTREAM_stream(THIS_STREAM)); + +END_METHOD + +BEGIN_PROPERTY(Stream_IsTerm) + + GB_ReturnBoolean(isatty(STREAM_FD)); + +END_PROPERTY + +BEGIN_METHOD(Stream_Watch, GB_INTEGER mode; GB_BOOLEAN on) + + int mode = VARG(mode); + + if (mode == R_OK) + mode = STO_READ; + else if (mode == W_OK) + mode = STO_WRITE; + else + { + GB_Error("Unknown watch"); + return; + } + + watch_stream(THIS_STREAM, mode, VARG(on)); + +END_METHOD + +BEGIN_METHOD_VOID(StreamLines_next) + + char *str; + + if (STREAM_eof(CSTREAM_stream(THIS_STREAM))) + GB_StopEnum(); + else + { + str = STREAM_line_input(CSTREAM_stream(THIS_STREAM), NULL); + STRING_free_later(str); + GB_ReturnString(str); + } + +END_METHOD + +//--------------------------------------------------------------------------- + +static void init_term_size(void *_object) +{ + struct winsize winSize; + + if (_term_width == 0 || _term_height == 0) + { + if (ioctl(STREAM_FD, TIOCGWINSZ, (char *)&winSize)) + THROW_SYSTEM(errno, NULL); + + _term_width = winSize.ws_col; + _term_height = winSize.ws_row; + } + + if (!_term_init) + { + _SIGWINCH_callback = SIGNAL_register(SIGWINCH, cb_term_resize, 0); + _term_init = TRUE; + } +} + +BEGIN_PROPERTY(StreamTerm_Name) + + GB_ReturnNewZeroString(ttyname(STREAM_FD)); + +END_PROPERTY + +BEGIN_METHOD(StreamTerm_Resize, GB_INTEGER width; GB_INTEGER height) + + struct winsize winSize = { 0 }; + + winSize.ws_row = (unsigned short)Max(1, Min(65535, VARG(height))); + winSize.ws_col = (unsigned short)Max(1, Min(65535, VARG(width))); + + if (ioctl(STREAM_FD, TIOCSWINSZ, (char *)&winSize)) + THROW_SYSTEM(errno, NULL); + +END_METHOD + +static void handle_term_property(void *_object, void *_param, bool iflag, int flag) +{ + struct termios ttmode; + + if (tcgetattr(STREAM_FD, &ttmode)) + THROW_SYSTEM(errno, ""); + + if (READ_PROPERTY) + GB_ReturnBoolean(((iflag ? ttmode.c_iflag : ttmode.c_lflag) & flag) == flag); + else + { + if (VPROP(GB_BOOLEAN)) + { + if (iflag) + ttmode.c_iflag |= flag; + else + ttmode.c_lflag |= flag; + } + else + { + if (iflag) + ttmode.c_iflag &= ~flag; + else + ttmode.c_lflag &= ~flag; + } + + if (tcsetattr(STREAM_FD, TCSANOW, &ttmode)) + THROW_SYSTEM(errno, ""); + } +} + +BEGIN_PROPERTY(StreamTerm_Echo) + + handle_term_property(_object, _param, FALSE, ECHO); + +END_PROPERTY + +BEGIN_PROPERTY(StreamTerm_FlowControl) + + handle_term_property(_object, _param, TRUE, IXON | IXOFF); + +END_PROPERTY + +BEGIN_PROPERTY(StreamTerm_Width) + + init_term_size(_object); + GB_ReturnInteger(_term_width); + +END_PROPERTY + +BEGIN_PROPERTY(StreamTerm_Height) + + init_term_size(_object); + GB_ReturnInteger(_term_height); + +END_PROPERTY + +#endif + +//--------------------------------------------------------------------------- + +GB_DESC StreamLinesDesc[] = +{ + GB_DECLARE_VIRTUAL(".Stream.Lines"), + + GB_METHOD("_next", "s", StreamLines_next, NULL), + + GB_END_DECLARE +}; + +GB_DESC StreamTermDesc[] = +{ + GB_DECLARE_VIRTUAL(".Stream.Term"), + + GB_PROPERTY_READ("Name", "s", StreamTerm_Name), + GB_PROPERTY("Echo", "b", StreamTerm_Echo), + GB_PROPERTY("FlowControl", "b", StreamTerm_FlowControl), + GB_PROPERTY_READ("Width", "i", StreamTerm_Width), + GB_PROPERTY_READ("W", "i", StreamTerm_Width), + GB_PROPERTY_READ("Height", "i", StreamTerm_Height), + GB_PROPERTY_READ("H", "i", StreamTerm_Height), + GB_METHOD("Resize", NULL, StreamTerm_Resize, "(Width)i(Height)i"), + + GB_END_DECLARE +}; + +GB_DESC StreamDesc[] = +{ + GB_DECLARE("Stream", sizeof(CSTREAM)), + GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, Stream_free, NULL), + + GB_PROPERTY("ByteOrder", "i", Stream_ByteOrder), + GB_PROPERTY_READ("Handle", "i", Stream_Handle), + GB_PROPERTY("EndOfLine", "i", Stream_EndOfLine), + GB_METHOD("Close", NULL, Stream_Close, NULL), + GB_PROPERTY_READ("EndOfFile", "b", Stream_EndOfFile), + GB_PROPERTY("Blocking", "b", Stream_Blocking), + GB_PROPERTY("Tag", "v", Stream_Tag), + GB_METHOD("ReadLine", "s", Stream_ReadLine, "[(Escape)s]"), + GB_PROPERTY_READ("IsTerm", "b", Stream_IsTerm), + + GB_PROPERTY_SELF("Lines", ".Stream.Lines"), + GB_PROPERTY_SELF("Term", ".Stream.Term"), + + GB_METHOD("Begin", NULL, Stream_Begin, NULL), + GB_METHOD("Send", NULL, Stream_End, NULL), + GB_METHOD("Drop", NULL, Stream_Cancel, NULL), + + GB_METHOD("Watch", NULL, Stream_Watch, "(Mode)i(Watch)b"), + + GB_END_DECLARE +}; + + +GB_DESC StatPermDesc[] = +{ + GB_DECLARE_VIRTUAL(".Stat.Perm"), + + GB_METHOD("_get", "s", CFILE_perm_get, "(UserOrGroup)s"), + GB_PROPERTY_READ("User", "s", CFILE_perm_user), + GB_PROPERTY_READ("Group", "s", CFILE_perm_group), + GB_PROPERTY_READ("Other", "s", CFILE_perm_other), + + GB_END_DECLARE +}; + + +GB_DESC StatDesc[] = +{ + GB_DECLARE("Stat", sizeof(CSTAT)), + GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, Stat_free, NULL), + + GB_PROPERTY_READ("Type", "i", Stat_Type), + GB_PROPERTY_READ("Mode", "i", Stat_Mode), + GB_PROPERTY_READ("Hidden", "b", Stat_Hidden), + GB_PROPERTY_READ("Size", "l", Stat_Size), + GB_PROPERTY_READ("Time", "d", Stat_Time), + GB_PROPERTY_READ("LastAccess", "d", Stat_LastAccess), + GB_PROPERTY_READ("LastModified", "d", Stat_Time), + GB_PROPERTY_READ("LastChange", "d", Stat_LastChange), + GB_PROPERTY_READ("User", "s", Stat_User), + GB_PROPERTY_READ("Group", "s", Stat_Group), + GB_PROPERTY_SELF("Perm", ".Stat.Perm"), + GB_PROPERTY_READ("SetGID", "b", Stat_SetGID), + GB_PROPERTY_READ("SetUID", "b", Stat_SetUID), + GB_PROPERTY_READ("Sticky", "b", Stat_Sticky), + GB_PROPERTY_READ("Path", "s", Stat_Path), + GB_PROPERTY_READ("Link", "s", Stat_Link), + GB_PROPERTY_READ("Auth", "s", Stat_Auth), + + GB_END_DECLARE +}; + + +GB_DESC FileDesc[] = +{ + GB_DECLARE("File", sizeof(CFILE)), + GB_INHERITS("Stream"), + GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, File_free, NULL), + +// GB_STATIC_PROPERTY_READ("Separator", "s", CFILE_separator), + + GB_STATIC_PROPERTY_READ("In", "File", File_In), + GB_STATIC_PROPERTY_READ("Out", "File", File_Out), + GB_STATIC_PROPERTY_READ("Err", "File", File_Err), + + GB_STATIC_METHOD("Dir", "s", File_Dir, "(Path)s"), + GB_STATIC_METHOD("Name", "s", File_Name, "(Path)s"), + GB_STATIC_METHOD("Ext", "s", File_Ext, "(Path)s"), + GB_STATIC_METHOD("BaseName", "s", File_BaseName, "(Path)s"), + + GB_STATIC_METHOD("SetDir", "s", File_SetDir, "(Path)s(NewDir)s"), + GB_STATIC_METHOD("SetName", "s", File_SetName, "(Path)s(NewName)s"), + GB_STATIC_METHOD("SetExt", "s", File_SetExt, "(Path)s(NewExt)s"), + GB_STATIC_METHOD("SetBaseName", "s", File_SetBaseName, "(Path)s(NewBaseName)s"), + + GB_STATIC_METHOD("IsRelative", "b", File_IsRelative, "(Path)s"), + GB_STATIC_METHOD("IsHidden", "b", File_IsHidden, "(Path)s"), + + GB_STATIC_METHOD("Load", "s", File_Load, "(FileName)s"), + GB_STATIC_METHOD("Save", NULL, File_Save, "(FileName)s(Data)s"), + + GB_EVENT("Read", NULL, NULL, &EVENT_Read), + GB_EVENT("Write", NULL, NULL, &EVENT_Write), + GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), + + GB_END_DECLARE +}; diff --git a/main/gbx/gbx_c_file.h b/main/gbx/gbx_c_file.h new file mode 100644 index 00000000..9baa3854 --- /dev/null +++ b/main/gbx/gbx_c_file.h @@ -0,0 +1,80 @@ +/*************************************************************************** + + gbx_c_file.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_FILE_H +#define __GBX_C_FILE_H + +#include "gambas.h" + +#include "gbx_value.h" +#include "gbx_stream.h" +#include "gbx_object.h" +#include "gb_file.h" + +typedef + struct { + OBJECT ob; + STREAM stream; + GB_VARIANT_VALUE tag; + } + CSTREAM; + +typedef + struct { + CSTREAM ob; + } + CFILE; + +typedef + struct { + OBJECT ob; + FILE_STAT info; + char *path; + } + CSTAT; + +#ifndef __GBX_C_FILE_C +extern GB_DESC StreamLinesDesc[]; +extern GB_DESC StreamTermDesc[]; +extern GB_DESC StreamDesc[]; +extern GB_DESC FileDesc[]; +extern GB_DESC StatDesc[]; +extern GB_DESC StatPermDesc[]; + +extern CFILE *CFILE_in; +extern CFILE *CFILE_out; +extern CFILE *CFILE_err; +#else +#define THIS ((CFILE *)_object) +#define THIS_STREAM ((CSTREAM *)_object) +#define THIS_STAT ((CSTAT *)_object) +#endif + +#define CSTREAM_stream(_cstream) (&((CSTREAM *)(void *)(_cstream))->stream) + +CFILE *CFILE_create(STREAM *stream, int mode); +void CFILE_init(void); +void CFILE_exit(void); +void CFILE_init_watch(void); + +#endif diff --git a/main/gbx/gbx_c_gambas.c b/main/gbx/gbx_c_gambas.c new file mode 100644 index 00000000..752c221c --- /dev/null +++ b/main/gbx/gbx_c_gambas.c @@ -0,0 +1,270 @@ +/*************************************************************************** + + gbx_c_gambas.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_GAMBAS_C + +#include + +#include "gbx_info.h" +#include "gbx_local.h" +#include "gbx_compare.h" +#include "gb_type_common.h" +#include "gb_file.h" +#include "gbx_date.h" +#include "gbx_exec.h" + +#ifndef GBX_INFO + +#include "gb_error.h" +#include "gbx_api.h" +#include "gbx_class.h" +#include "gbx_event.h" +#include "gbx_c_array.h" +#include "gbx_c_gambas.h" + + +static int get_arg_count(void) +{ + if (DEBUG_inside_eval && DEBUG_info) + { + if (DEBUG_info->fp && DEBUG_info->fp->vararg) + return DEBUG_info->bp - DEBUG_info->pp; + } + else + { + if (FP && FP->vararg) + return BP - PP; + } + return 0; +} + +static VALUE *get_arg(int i) +{ + if (DEBUG_inside_eval && DEBUG_info) + return &DEBUG_info->pp[i]; + else + return &PP[i]; +} + +BEGIN_PROPERTY(Param_Count) + + GB_ReturnInteger(get_arg_count()); + +END_PROPERTY + + +BEGIN_PROPERTY(Param_Max) + + GB_ReturnInteger(get_arg_count() - 1); + +END_PROPERTY + + +BEGIN_METHOD(Param_get, GB_INTEGER index) + + int index = VARG(index); + VALUE *arg; + + if (index < 0 || index >= get_arg_count()) + THROW(E_BOUND); + + arg = get_arg(index); + VALUE_conv(arg, T_VARIANT); + TEMP = *arg; + //VALUE_conv(&TEMP, T_VARIANT); + +END_METHOD + + +BEGIN_PROPERTY(Param_All) + + GB_ARRAY all; + int nparam = get_arg_count(); + int i; + VALUE *arg; + + GB_ArrayNew(POINTER(&all), T_VARIANT, nparam); + + for (i = 0; i < nparam; i++) + { + arg = get_arg(i); + VALUE_conv(arg, T_VARIANT); + GB_StoreVariant((GB_VARIANT *)arg, GB_ArrayGet(all, i)); + } + + GB_ReturnObject(all); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Param_next) + + int *index = (int *)GB_GetEnum(); + + if (*index >= get_arg_count()) + GB_StopEnum(); + else + { + VALUE *arg = get_arg(*index); + VALUE_conv(arg, T_VARIANT); + TEMP = *arg; + (*index)++; + } + +END_METHOD + + +BEGIN_PROPERTY(Param_Name) + + GB_ReturnConstZeroString(EXEC_unknown_name); + +END_PROPERTY + + +/*BEGIN_PROPERTY(Param_Property) + + GB_ReturnBoolean(EXEC_unknown_property); + +END_PROPERTY*/ + +BEGIN_PROPERTY(Param_EventName) + + GB_ReturnConstZeroString(EVENT_Name); + +END_PROPERTY + +#endif + +GB_DESC NATIVE_Param[] = +{ + GB_DECLARE_STATIC("Param"), + + GB_STATIC_PROPERTY_READ("Count", "i", Param_Count), + GB_STATIC_PROPERTY_READ("Max", "i", Param_Max), + GB_STATIC_PROPERTY_READ("All", "Variant[]", Param_All), + + GB_STATIC_PROPERTY_READ("Name", "s", Param_Name), + GB_STATIC_PROPERTY_READ("EventName", "s", Param_EventName), + //GB_STATIC_PROPERTY_READ("Property", "b", Param_Property), + + GB_STATIC_METHOD("_get", "v", Param_get, "(Index)i"), + GB_STATIC_METHOD("_next", "v", Param_next, NULL), + + //GB_STATIC_METHOD("Copy", "Variant[]", CPARAM_copy, "[(Start)i(Length)i]"), + + GB_END_DECLARE +}; + +GB_DESC NATIVE_Gambas[] = +{ + GB_DECLARE_STATIC("gb"), + + GB_CONSTANT("Binary", "i", GB_COMP_BINARY), + GB_CONSTANT("IgnoreCase", "i", GB_COMP_NOCASE), + GB_CONSTANT("Language", "i", GB_COMP_LANG), + GB_CONSTANT("Like", "i", GB_COMP_LIKE), + GB_CONSTANT("Match", "i", GB_COMP_MATCH), + GB_CONSTANT("Natural", "i", GB_COMP_NATURAL), + + GB_CONSTANT("Ascent", "i", GB_COMP_ASCENT), + GB_CONSTANT("Descent", "i", GB_COMP_DESCENT), + + /* BE CAREFUL ! These constants are used in the compiler */ + GB_CONSTANT("Null", "i", T_NULL), + GB_CONSTANT("Boolean", "i", T_BOOLEAN), + GB_CONSTANT("Byte", "i", T_BYTE), + GB_CONSTANT("Short", "i", T_SHORT), + GB_CONSTANT("Integer", "i", T_INTEGER), + GB_CONSTANT("Long", "i", T_LONG), + GB_CONSTANT("Float", "i", T_FLOAT), + GB_CONSTANT("Single", "i", T_SINGLE), + GB_CONSTANT("Date", "i", T_DATE), + GB_CONSTANT("String", "i" , T_STRING), + GB_CONSTANT("Pointer", "i" , T_POINTER), + GB_CONSTANT("Function", "i" , T_FUNCTION), + GB_CONSTANT("Variant", "i", T_VARIANT), + GB_CONSTANT("Class", "i" , T_CLASS), + GB_CONSTANT("Object", "i", T_OBJECT), + + GB_CONSTANT("File", "i", GB_STAT_FILE), + GB_CONSTANT("Directory", "i", GB_STAT_DIRECTORY), + GB_CONSTANT("Device", "i", GB_STAT_DEVICE), + GB_CONSTANT("Pipe", "i", GB_STAT_PIPE), + GB_CONSTANT("Socket", "i", GB_STAT_SOCKET), + GB_CONSTANT("Link", "i", GB_STAT_LINK), + + GB_CONSTANT("NewLine", "s", "\n"), + GB_CONSTANT("Tab", "s", "\t"), + GB_CONSTANT("Cr", "s", "\r"), + GB_CONSTANT("Lf", "s", "\n"), + GB_CONSTANT("CrLf", "s", "\r\n"), + + GB_CONSTANT("Standard", "i", LF_STANDARD), + GB_CONSTANT("GeneralNumber", "i", LF_GENERAL_NUMBER), + GB_CONSTANT("ShortNumber", "i", LF_SHORT_NUMBER), + GB_CONSTANT("Fixed", "i", LF_FIXED), + GB_CONSTANT("Percent", "i", LF_PERCENT), + GB_CONSTANT("Scientific", "i", LF_SCIENTIFIC), + GB_CONSTANT("Currency", "i", LF_CURRENCY), + GB_CONSTANT("International", "i", LF_INTERNATIONAL), + GB_CONSTANT("GeneralDate", "i", LF_GENERAL_DATE), + GB_CONSTANT("LongDate", "i", LF_LONG_DATE), + GB_CONSTANT("MediumDate", "i", LF_MEDIUM_DATE), + GB_CONSTANT("ShortDate", "i", LF_SHORT_DATE), + GB_CONSTANT("LongTime", "i", LF_LONG_TIME), + GB_CONSTANT("MediumTime", "i", LF_MEDIUM_TIME), + GB_CONSTANT("ShortTime", "i", LF_SHORT_TIME), + + GB_CONSTANT("Read", "i", R_OK), + GB_CONSTANT("Write", "i", W_OK), + GB_CONSTANT("Exec", "i", X_OK), + + GB_CONSTANT("Sunday", "i", 0), + GB_CONSTANT("Monday", "i", 1), + GB_CONSTANT("Tuesday", "i", 2), + GB_CONSTANT("Wednesday", "i", 3), + GB_CONSTANT("Thursday", "i", 4), + GB_CONSTANT("Friday", "i", 5), + GB_CONSTANT("Saturday", "i", 6), + + GB_CONSTANT("Millisecond", "i", DP_MILLISECOND), + GB_CONSTANT("Second", "i", DP_SECOND), + GB_CONSTANT("Minute", "i", DP_MINUTE), + GB_CONSTANT("Hour", "i", DP_HOUR), + GB_CONSTANT("Day", "i", DP_DAY), + GB_CONSTANT("WeekDay", "i", DP_WEEKDAY), + GB_CONSTANT("Week", "i", DP_WEEK), + GB_CONSTANT("Month", "i", DP_MONTH), + GB_CONSTANT("Quarter", "i", DP_QUARTER), + GB_CONSTANT("Year", "i", DP_YEAR), + + GB_CONSTANT("LittleEndian", "i", GB_LITTLE_ENDIAN), + GB_CONSTANT("BigEndian", "i", GB_BIG_ENDIAN), + + GB_CONSTANT("Unix", "i", GB_EOL_UNIX), + GB_CONSTANT("Windows", "i", GB_EOL_WINDOWS), + GB_CONSTANT("Mac", "i", GB_EOL_MAC), + + GB_END_DECLARE +}; + diff --git a/main/gbx/gbx_c_gambas.h b/main/gbx/gbx_c_gambas.h new file mode 100644 index 00000000..aaa13dc9 --- /dev/null +++ b/main/gbx/gbx_c_gambas.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + gbx_c_gambas.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_GAMBAS_H +#define __GBX_C_GAMBAS_H + +#include "gambas.h" +#include "gbx_object.h" + +#ifndef __GBX_C_GAMBAS_C +extern GB_DESC NATIVE_Gambas[]; +extern GB_DESC NATIVE_Param[]; +#endif + +#endif diff --git a/main/gbx/gbx_c_observer.c b/main/gbx/gbx_c_observer.c new file mode 100644 index 00000000..1b6350b3 --- /dev/null +++ b/main/gbx/gbx_c_observer.c @@ -0,0 +1,148 @@ +/*************************************************************************** + + gbx_c_observer.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_OBSERVER_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include "gbx_exec.h" +#include "gbx_api.h" +#include "gbx_event.h" +#include "gbx_c_observer.h" + +//#define DEBUG_ME 1 + +void COBSERVER_lock(COBSERVER *this, bool lock) +{ + if (lock) + this->locked++; + else + this->locked--; +} + +void COBSERVER_attach(COBSERVER *this, void *parent, const char *name) +{ + #if DEBUG_ME + fprintf(stderr, "COBSERVER_attach: %p: %s %p\n", this, parent ? OBJECT_class(parent)->name : "", parent); + #endif + if (this->object && this->event) + EVENT_search(OBJECT_class(this->object), this->event, name, parent); +} + +void COBSERVER_detach(COBSERVER *this) +{ + #if DEBUG_ME + fprintf(stderr, "COBSERVER_detach: %p\n", this); + #endif + + if (this->event) + FREE(&this->event); +} + +BEGIN_METHOD(Observer_new, GB_OBJECT object; GB_BOOLEAN after) + + OBJECT *object; + OBJECT_EVENT *ev; + char *name; + CLASS *class; + void *parent; + + object = (OBJECT *)VARG(object); + if (GB_CheckObject(object)) + return; + + parent = OBJECT_parent(THIS); + + if (!parent) + return; + + class = OBJECT_class(object); + if (class->n_event == 0) + return; + + name = EVENT_Name; + if (!name || !*name) + return; + + #if DEBUG_ME + fprintf(stderr, "Observer_new: %p %d %s (%s %p)\n", THIS, OBJECT_class(object)->n_event, name, GB_GetClassName(parent), parent); + #endif + + ev = OBJECT_event(object); + + THIS->after = VARGOPT(after, FALSE); + + ALLOC_ZERO(&THIS->event, sizeof(ushort) * class->n_event); + + THIS->object = object; + OBJECT_attach((OBJECT *)THIS, parent, name); + COBSERVER_attach(THIS, parent, name); + + LIST_insert((void **)&ev->observer, THIS, &THIS->list); + OBJECT_REF(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(Observer_free) + + #if DEBUG_ME + fprintf(stderr, "Observer_free: %p\n", THIS); + #endif + + GB_StoreVariant(NULL, &THIS->tag); + COBSERVER_detach(THIS); + +END_METHOD + +BEGIN_PROPERTY(Observer_Object) + + GB_ReturnObject(THIS->object); + +END_PROPERTY + +BEGIN_PROPERTY(Observer_Tag) + + if (READ_PROPERTY) + GB_ReturnVariant(&THIS->tag); + else + GB_StoreVariant(PROP(GB_VARIANT), &THIS->tag); + +END_PROPERTY + +#endif + +GB_DESC NATIVE_Observer[] = +{ + GB_DECLARE("Observer", sizeof(COBSERVER)), + + GB_METHOD("_new", NULL, Observer_new, "(Object)o[(After)b]"), + GB_METHOD("_free", NULL, Observer_free, NULL), + + GB_PROPERTY_READ("Object", "o", Observer_Object), + GB_PROPERTY("Tag", "v", Observer_Tag), + + GB_END_DECLARE +}; diff --git a/main/gbx/gbx_c_observer.h b/main/gbx/gbx_c_observer.h new file mode 100644 index 00000000..193ebd71 --- /dev/null +++ b/main/gbx/gbx_c_observer.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + gbx_c_observer.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_OBSERVER_H +#define __GBX_C_OBSERVER_H + +#include "gambas.h" +#include "gbx_object.h" +#include "gb_list.h" + +#ifndef __GBX_C_OBSERVER_C +extern GB_DESC NATIVE_Observer[]; +#else +#define THIS ((COBSERVER *)_object) +#endif + +// Note: the interpreter automatically allocates an extra OBJECT_EVENT structure. See CLASS_calc_info(). + +typedef + struct { + OBJECT ob; + LIST list; + ushort *event; + void *object; + GB_VARIANT_VALUE tag; + short locked; + unsigned after : 1; + } + COBSERVER; + +void COBSERVER_attach(COBSERVER *this, void *parent, const char *name); +void COBSERVER_detach(COBSERVER *this); +void COBSERVER_lock(COBSERVER *this, bool lock); + +#define COBSERVER_is_locked(_this) (((COBSERVER *)_this)->locked > 0) + +#endif diff --git a/main/gbx/gbx_c_process.c b/main/gbx/gbx_c_process.c new file mode 100644 index 00000000..d9294ad6 --- /dev/null +++ b/main/gbx/gbx_c_process.c @@ -0,0 +1,1312 @@ +/*************************************************************************** + + gbx_c_process.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_PROCESS_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include "gb_common.h" +#include "gb_common_buffer.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "gb_replace.h" +#include "gb_limit.h" +#include "gb_array.h" +#include "gbx_api.h" +#include "gambas.h" +#include "gbx_stream.h" +#include "gbx_exec.h" +#include "gbx_class.h" +#include "gbx_watch.h" +#include "gbx_project.h" +#include "gbx_c_array.h" +#include "gbx_local.h" +#include "gbx_signal.h" +#include "gbx_event.h" + +#include "gbx_c_process.h" + +//#define DEBUG_ME +//#define DEBUG_CHILD + +char *CPROCESS_shell = NULL; + +extern char **environ; + +DECLARE_EVENT(EVENT_Read); +DECLARE_EVENT(EVENT_Error); +DECLARE_EVENT(EVENT_Kill); + +static CPROCESS *_running_process_list = NULL; +static int _running_process = 0; +static int _ignore_process = 0; + +static SIGNAL_CALLBACK *_SIGCHLD_callback; +static bool _init = FALSE; + +static int _last_status = 0; + +static int _last_child_error = 0; +static int _last_child_error_errno = 0; + +static void init_child(void); +static void exit_child(void); +static bool wait_child(CPROCESS *process); + +enum { + CHILD_NO_ERROR, + CHILD_CANNOT_OPEN_TTY, + CHILD_CANNOT_INIT_TTY, + CHILD_CANNOT_PLUG_INPUT, + CHILD_CANNOT_PLUG_OUTPUT, + CHILD_CANNOT_EXEC, +}; + +static const char *const _child_error[] = { + NULL, + "cannot open slave pseudo-terminal: ", + "cannot initialize pseudo-terminal: ", + "cannot plug standard input: ", + "cannot plug standard output and standard error: ", + "cannot run executable: " +}; + +//------------------------------------------------------------------------- + +static void close_fd(int *pfd) +{ + int fd = *pfd; + + if (fd >= 0) + { + #ifdef DEBUG_ME + fprintf(stderr, "unwatch & close: %d\n", fd); + #endif + GB_Watch(fd, GB_WATCH_NONE, NULL, 0); + close(fd); + *pfd = -1; + } +} + +static void add_process_to_running_list(CPROCESS *process) +{ + if (_running_process_list) + _running_process_list->prev = process; + + process->next = _running_process_list; + process->prev = NULL; + + _running_process_list = process; + + process->running = TRUE; + _running_process++; +} + +static void remove_process_from_running_list(CPROCESS *process) +{ + if (process->prev) + process->prev->next = process->next; + + if (process->next) + process->next->prev = process->prev; + + if (process == _running_process_list) + _running_process_list = process->next; + + process->running = FALSE; + + _running_process--; + if (process->ignore) + _ignore_process--; +} + +static void callback_write(int fd, int type, CPROCESS *process) +{ + #ifdef DEBUG_ME + fprintf(stderr, "callback_write: %d %p\n", fd, process); + #endif + + if (process->to_string) + { + int n; + + for(;;) + { + n = read(fd, COMMON_buffer, 256); + if (n >= 0 || errno != EINTR) + break; + } + + if (n > 0) + { + process->result = STRING_add(process->result, COMMON_buffer, n); + return; + } + } + + if (GB_CanRaise(process, EVENT_Read)) + { + if (!STREAM_read_ahead(CSTREAM_stream(process))) + { + GB_Raise(process, EVENT_Read, 0); + return; + } + } + + close_fd(&process->out); +} + + +static void callback_error(int fd, int type, CPROCESS *process) +{ + char buffer[256]; + int n; + + #ifdef DEBUG_ME + fprintf(stderr, "callback_error: %d %p\n", fd, process); + #endif + + if (process->to_string && process->with_error) + { + int n; + + for(;;) + { + n = read(fd, COMMON_buffer, 256); + if (n >= 0 || errno != EINTR) + break; + } + + if (n > 0) + { + process->result = STRING_add(process->result, COMMON_buffer, n); + return; + } + } + + if (GB_CanRaise(process, EVENT_Error)) + { + n = read(fd, buffer, sizeof(buffer)); + if (n > 0) + { + GB_Raise(process, EVENT_Error, 1, GB_T_STRING, buffer, n); + return; + } + } + + close_fd(&process->err); +} + + +static void update_stream(CPROCESS *process) +{ + STREAM *stream = &process->ob.stream; + + stream->type = &STREAM_process; + (*stream->type->open)(stream, NULL, 0, process); +} + + +static void init_process(CPROCESS *process) +{ + //process->watch = GB_WATCH_NONE; + process->in = process->out = process->err = -1; + update_stream(process); +} + +static void exit_process(CPROCESS *_object) +{ + #ifdef DEBUG_ME + fprintf(stderr, "exit_process: %p\n", _object); + #endif + + if (THIS->in >= 0) + { + if (THIS->in != THIS->out) + close(THIS->in); + THIS->in = -1; + } + + close_fd(&THIS->out); + close_fd(&THIS->err); + + STREAM_close(&THIS->ob.stream); +} + +static void prepare_child_error(CPROCESS *_object) +{ + int fd; + char path[PATH_MAX]; + + snprintf(path, sizeof(path), FILE_TEMP_DIR "/%d.child", (int)getuid(), (int)getpid(), (int)THIS->pid); + + #ifdef DEBUG_ME + fprintf(stderr, "prepare_child_error: %p: %s\n", _object, path); + #endif + + if (_last_child_error == 0) + { + fd = open(path, O_RDONLY); + if (fd >= 0) + { + if (read(fd, &_last_child_error, sizeof(int)) != sizeof(int) + || read(fd, &_last_child_error_errno, sizeof(int)) != sizeof(int)) + { + _last_child_error = -1; + _last_child_error_errno = 0; + } + + close(fd); + } + + #ifdef DEBUG_ME + fprintf(stderr, "prepare_child_error: error = %d errno = %d\n", _last_child_error, _last_child_error_errno); + #endif + } + + unlink(path); +} + +static void throw_last_child_error() +{ + int child_error, child_errno; + + if (_last_child_error == 0) + return; + + child_error = _last_child_error; + child_errno = _last_child_error_errno; + + _last_child_error = 0; + + #ifdef DEBUG_ME + fprintf(stderr, "throw_last_child_error: %d %d\n", child_error, child_errno); + #endif + + if (child_error < 0) + THROW(E_CHILD, "unknown error", ""); + else + THROW(E_CHILD, _child_error[child_error], strerror(child_errno)); +} + +static void stop_process_after(CPROCESS *_object) +{ + STREAM *stream; + bool do_exit_process = THIS->in >= 0; + + #ifdef DEBUG_ME + fprintf(stderr, "stop_process_after: %p out = %d err = %d\n", _object, THIS->out, THIS->err); + #endif + + if (WIFEXITED(THIS->status) && WEXITSTATUS(THIS->status) == 255) + { + prepare_child_error(THIS); + exit_process(THIS); + //OBJECT_detach((OBJECT *)THIS); + } + + /* Vidage du tampon d'erreur */ + while (THIS->err >= 0) + { + callback_error(THIS->err, 0, THIS); + do_exit_process = TRUE; + } + + /* Vidage du tampon de sortie */ + if (THIS->out >= 0) + { + stream = CSTREAM_stream(THIS); + while (THIS->out >= 0 && !STREAM_is_closed(stream)) + callback_write(THIS->out, 0, THIS); + + do_exit_process = TRUE; + } + + if (do_exit_process) + exit_process(THIS); + + /*printf("** stop_process_after\n");*/ + //GB_Unref((void **)&_object); /* Ref du post */ +} + + +static void stop_process(CPROCESS *_object) +{ + if (!THIS->running) + return; + + #ifdef DEBUG_ME + fprintf(stderr, "stop_process: %p\n", THIS); + #endif + + /* Remove from running process list */ + + stop_process_after(THIS); + remove_process_from_running_list(THIS); + + #ifdef DEBUG_ME + fprintf(stderr, "Raising Kill event for %p: parent = %p can raise = %d\n", THIS, OBJECT_parent(THIS), GB_CanRaise(THIS, EVENT_Kill)); + #endif + GB_Raise(THIS, EVENT_Kill, 0); + + OBJECT_detach((OBJECT *)THIS); + OBJECT_UNREF(_object); + + //if (!_running_process_list) + if (_running_process <= _ignore_process) + exit_child(); +} + +static void abort_child(int error) +{ + int fd; + int save_errno; + char path[PATH_MAX]; + + fflush(stdout); + fflush(stderr); + + save_errno = errno; + + #ifdef DEBUG_ME + fprintf(stderr, "abort_child: %d %d\n", error, save_errno); + #endif + + snprintf(path, sizeof(path), FILE_TEMP_DIR "/%d.child", (int)getuid(), (int)getppid(), (int)getpid()); + + fd = open(path, O_CREAT | O_WRONLY, 0600); + if (fd >= 0) + { + write(fd, &error, sizeof(int)) == sizeof(int) + && write(fd, &save_errno, sizeof(int)) == sizeof(int); + close(fd); + } + + _exit(255); +} + +static void init_child_tty(int fd) +{ + struct termios terminal = { 0 }; + struct termios check; + + tcgetattr(fd, &terminal); + + terminal.c_iflag |= IXON | IXOFF | ICRNL; + #ifdef IUTF8 + if (LOCAL_is_UTF8) + terminal.c_iflag |= IUTF8; + #endif + + terminal.c_oflag |= OPOST; + //terminal.c_oflag &= ~ONLCR; + + terminal.c_lflag |= ISIG | ICANON | IEXTEN | ECHO; + ///terminal.c_lflag &= ~ECHO; + + #ifdef DEBUG_CHILD + fprintf(stderr, "init_child_tty: %s\n", isatty(fd) ? ttyname(fd) : "not a tty!"); + #endif + + if (tcsetattr(fd, TCSANOW, &terminal)) + { + #ifdef DEBUG_CHILD + int save_errno = errno; + fprintf(stderr, "init_child_tty: errno = %d\n", errno); + errno = save_errno; + #endif + abort_child(CHILD_CANNOT_INIT_TTY); + } + + tcgetattr(fd, &check); + if (check.c_iflag != terminal.c_iflag || check.c_oflag != terminal.c_oflag || check.c_lflag != terminal.c_lflag) + abort_child(CHILD_CANNOT_INIT_TTY); +} + +const char *CPROCESS_search_program_in_path(char *name) +{ + char *search; + char *p, *p2; + int len; + const char *path; + + if (!name) + return NULL; + + if (strchr(name, '/')) + { + if (access(name, X_OK) == 0) + return name; + else + return NULL; + } + + search = getenv("PATH"); + if (!search || !*search) + search = "/usr/bin:/bin"; + + search = STRING_new_zero(search); + + path = NULL; + + //fprintf(stderr, "search_program_in_path: '%s' in '%s'\n", name, search); + + p = search; + for(;;) + { + p2 = strchr(p, ':'); + if (p2) + len = p2 - p; + else + len = strlen(p); + + if (len > 0) + { + p[len] = 0; + //fprintf(stderr, "trying: %s\n", p); + path = FILE_cat(p, name, NULL); + if (access(path, X_OK) == 0) + break; + path = NULL; + } + + if (!p2) + break; + + p = p2 + 1; + } + + STRING_free(&search); + //fprintf(stderr, "--> %s\n", path); + return path; +} + +static void run_process(CPROCESS *process, int mode, void *cmd, CARRAY *env) +{ + static const char *shell[] = { NULL, "-c", NULL, NULL }; + + int fdin[2], fdout[2], fderr[2]; + pid_t pid; + char **argv; + CARRAY *array; + int i, n; + sigset_t sig, old; + int save_errno; + + // for virtual terminal + int fd_master = -1; + char *slave = NULL; + //struct termios termios_stdin; + //struct termios termios_check; + struct termios termios_master; + const char *exec; + + if (mode & PM_SHELL) + { + #ifdef DEBUG_ME + fprintf(stderr, "run_process %p: %s\n", process, (char *)cmd); + #endif + + argv = (char **)shell; + + if (CPROCESS_shell) + argv[0] = CPROCESS_shell; + else + argv[0] = "/bin/sh"; + + argv[2] = (char *)cmd; + + if (argv[2] == NULL || *argv[2] == 0) + return; + + exec = argv[0]; + + process->process_group = TRUE; + } + else + { + array = (CARRAY *)cmd; + n = ARRAY_count(array->data); + + if (n == 0) + return; + + ALLOC(&argv, sizeof(*argv) * (n + 1)); + memcpy(argv, array->data, sizeof(*argv) * n); + argv[n] = NULL; + + exec = CPROCESS_search_program_in_path(argv[0]); + if (!exec) + { + IFREE(argv); + THROW(E_NEXIST); + } + + for (i = 0; i < n; i++) + { + if (!argv[i]) + argv[i] = ""; + } + + #ifdef DEBUG_ME + { + int i; + + fprintf(stderr, "run_process %p: ", process); + for (i = 0; i < n; i++) + fprintf(stderr, "%s ", argv[i]); + fprintf(stderr, "\n"); + } + #endif + } + + if (mode & PM_STRING) + { + process->to_string = TRUE; + process->with_error = (mode & PM_ERROR) != 0; + process->result = NULL; + mode |= PM_READ; + } + + if (mode & PM_TERM) + { + fd_master = posix_openpt(O_RDWR | O_NOCTTY); + if (fd_master < 0) + goto __ABORT_ERRNO; + + grantpt(fd_master); + unlockpt(fd_master); + slave = ptsname(fd_master); + #ifdef DEBUG_ME + fprintf(stderr, "run_process: slave = %s\n", slave); + #endif + + if (mode & PM_TERM) + { + if (tcgetattr(fd_master, &termios_master)) + goto __ABORT_ERRNO; + + cfmakeraw(&termios_master); + //termios_master.c_lflag &= ~ECHO; + + if (tcsetattr(fd_master, TCSANOW, &termios_master)) + goto __ABORT_ERRNO; + } + } + else + { + /* Create pipes */ + + if ((mode & PM_WRITE) && pipe(fdin) != 0) + goto __ABORT_ERRNO; + + if ((mode & PM_READ) && (pipe(fdout) != 0 || pipe(fderr) != 0)) + goto __ABORT_ERRNO; + } + + // Adding to the running process list + + add_process_to_running_list(process); + OBJECT_REF(process); + + // Start the SIGCHLD callback + + init_child(); + + // Block SIGCHLD and fork + + sigemptyset(&sig); + + sigaddset(&sig, SIGCHLD); + sigprocmask(SIG_BLOCK, &sig, &old); + + if (mode & PM_SHELL || mode & PM_TERM || env) + pid = fork(); + else + pid = vfork(); + + if (pid == (-1)) + { + stop_process(process); + sigprocmask(SIG_SETMASK, &old, NULL); + goto __ABORT_ERRNO; + } + + // parent process + + if (pid) + { + process->pid = pid; + + #ifdef DEBUG_ME + fprintf(stderr, "fork: pid = %d\n", pid); + #endif + + if (mode & PM_WRITE) + { + if (mode & PM_TERM) + { + process->in = fd_master; + } + else + { + close(fdin[0]); + process->in = fdin[1]; + } + } + + if (mode & PM_READ) + { + if (mode & PM_TERM) + { + process->out = fd_master; + process->err = -1; + } + else + { + close(fdout[1]); + close(fderr[1]); + + process->out = fdout[0]; + process->err = fderr[0]; + } + + #ifdef DEBUG_ME + fprintf(stderr, "watch: out = %d err = %d\n", process->out, process->err); + #endif + GB_Watch(process->out, GB_WATCH_READ, (void *)callback_write, (intptr_t)process); + if (process->err >= 0) + { + fcntl(process->err, F_SETFL, fcntl(process->err, F_GETFL) | O_NONBLOCK); + GB_Watch(process->err, GB_WATCH_READ, (void *)callback_error, (intptr_t)process); + } + } + + if ((mode & PM_SHELL) == 0) + { + FREE(&argv); + } + + sigprocmask(SIG_SETMASK, &old, NULL); + } + else //------------ child process ---------------------------- + { + int fd_slave; + bool pwd; + int ch_i, ch_n; + + //bool stdin_isatty = isatty(STDIN_FILENO); + + sigprocmask(SIG_SETMASK, &old, NULL); + + if (mode & PM_SHELL) + setpgid(0, 0); + + if (mode & PM_TERM) + { + close(fd_master); + setsid(); + fd_slave = open(slave, O_RDWR); + if (fd_slave < 0) + abort_child(CHILD_CANNOT_OPEN_TTY); + + init_child_tty(fd_slave); + + /*#ifdef DEBUG_ME + fprintf(stderr, "run_process (child): slave = %s isatty = %d\n", slave, isatty(fd_slave)); + #endif*/ + + if (mode & PM_WRITE) + { + if (dup2(fd_slave, STDIN_FILENO) == -1) + abort_child(CHILD_CANNOT_PLUG_INPUT); + } + + if (mode & PM_READ) + { + if ((dup2(fd_slave, STDOUT_FILENO) == -1) + || (dup2(fd_slave, STDERR_FILENO) == -1)) + abort_child(CHILD_CANNOT_PLUG_OUTPUT); + } + + // Strange Linux behaviour ? + // Terminal initialization must be done on STDIN_FILENO after using dup2(). + // If it is done on fd_slave, before using dup2(), it sometimes fails with no error. + + /*if (mode & PM_WRITE) + init_child_tty(STDIN_FILENO); + else if (mode & PM_READ) + init_child_tty(STDOUT_FILENO);*/ + + /*puts("---------------------------------"); + if (stdin_isatty) puts("STDIN is a tty");*/ + /*tcgetattr(STDIN_FILENO, &termios_check); + puts(termios_check.c_lflag & ISIG ? "+ISIG" : "-ISIG"); + //tcsetattr(STDIN_FILENO, TCSADRAIN, &termios_check); + system("stty icanon"); + system("stty -a"); + puts("---------------------------------");*/ + } + else + { + if (mode & PM_WRITE) + { + close(fdin[1]); + + if (dup2(fdin[0], STDIN_FILENO) == -1) + abort_child(CHILD_CANNOT_PLUG_INPUT); + } + + if (mode & PM_READ) + { + close(fdout[0]); + close(fderr[0]); + + if ((dup2(fdout[1], STDOUT_FILENO) == -1) + || (dup2(fderr[1], STDERR_FILENO) == -1)) + abort_child(CHILD_CANNOT_PLUG_OUTPUT); + } + } + + pwd = FALSE; + + if (env) + { + char *str; + ch_n = ARRAY_count(env->data); + for (ch_i = 0; ch_i < ch_n; ch_i++) + { + str = ((char **)env->data)[ch_i]; + if (putenv(str)) + ERROR_warning("cannot set environment string: %s", str); + if (strncmp(str, "PWD=", 4) == 0 && chdir(&str[4]) == 0) + pwd = TRUE; + } + } + + // Return to the parent working directory if the PWD environment variable has not been set + if (!pwd) + FILE_chdir(PROJECT_oldcwd); + + execv(exec, (char **)argv); + abort_child(CHILD_CANNOT_EXEC); + } + + update_stream(process); + + #ifdef DEBUG_ME + fprintf(stderr, "run_process: child is OK\n"); + #endif + + return; + +__ABORT_ERRNO: + + save_errno = errno; + stop_process(process); + THROW_SYSTEM(save_errno, NULL); +} + +void CPROCESS_check(void *_object) +{ + #ifdef DEBUG_ME + fprintf(stderr, "CPROCESS_check: %p\n", THIS); + #endif + + usleep(100); + if (wait_child(THIS)) + { + #ifdef DEBUG_ME + fprintf(stderr, "CPROCESS_check: stop process later\n"); + #endif + stop_process_after(THIS); + EVENT_post(stop_process, (intptr_t)THIS); + throw_last_child_error(); + } + + //fprintf(stderr, "CPROCESS_check: %p END\n", THIS); +} + +static bool wait_child(CPROCESS *process) +{ + int status; + + #ifdef DEBUG_ME + fprintf(stderr, "wait_child: check process %d\n", process->pid); + #endif + + if (wait4(process->pid, &status, WNOHANG, NULL) == process->pid) + { + process->status = status; + process->wait = TRUE; + _last_status = status; + + #ifdef DEBUG_ME + fprintf(stderr, "wait_child: process %d has returned %d\n", process->pid, status); + #endif + + return TRUE; + } + else + return FALSE; +} + +static void callback_child(int signum, intptr_t data) +{ + CPROCESS *process, *next; + //int buffer; + +#if 0 + for(;;) + { + if (read(fd, (char *)&buffer, 1) == 1) + break; + if (errno != EINTR) + ERROR_panic("Cannot read from SIGCHLD pipe: %s", strerror(errno)); + } +#endif + + #ifdef DEBUG_ME + fprintf(stderr, ">> callback_child\n"); + #endif + + for (process = _running_process_list; process; ) + { + next = process->next; + if (wait_child(process)) + stop_process(process); + process = next; + } + + throw_last_child_error(); + + #ifdef DEBUG_ME + fprintf(stderr, "<< callback_child\n"); + #endif +} + +static void init_child(void) +{ + if (_init) + return; + + #ifdef DEBUG_ME + fprintf(stderr, "init_child()\n"); + #endif + + _SIGCHLD_callback = SIGNAL_register(SIGCHLD, callback_child, 0); + + _init = TRUE; +} + +static void exit_child(void) +{ + if (!_init) + return; + + #ifdef DEBUG_ME + fprintf(stderr, "exit_child()\n"); + #endif + + SIGNAL_unregister(SIGCHLD, _SIGCHLD_callback); + + _init = FALSE; +} + + +static void error_CPROCESS_create(CPROCESS *process) +{ + OBJECT_UNREF(process); +} + +CPROCESS *CPROCESS_create(int mode, void *cmd, char *name, CARRAY *env) +{ + CPROCESS *process; + + process = OBJECT_new(CLASS_Process, name, OP ? (OBJECT *)OP : (OBJECT *)CP); + //fprintf(stderr, "CPROCESS_create: %p\n", process); + + ON_ERROR_1(error_CPROCESS_create, process) + { + init_process(process); + run_process(process, mode, cmd, env); + } + END_ERROR + + OBJECT_UNREF_KEEP(process); + + if (!name || !*name) + STREAM_blocking(CSTREAM_stream(process), TRUE); + + return process; +} + +void CPROCESS_wait_for(CPROCESS *process, int timeout) +{ + int ret; + int sigfd; + + #ifdef DEBUG_ME + fprintf(stderr, "Waiting for %d\n", process->pid); + #endif + + // If CPROCESS_check() caught the process end, process->running is not set yet, because + // stop_process() will be raised at the next event loop. So no need to wait for it. + + if (process->wait) + return; + + OBJECT_REF(process); + + sigfd = SIGNAL_get_fd(); + + ON_ERROR_1(error_CPROCESS_create, process) + { + while (process->running) + { + #ifdef DEBUG_ME + fprintf(stderr, "Watch process %d\n", process->pid); + #endif + ret = WATCH_process(sigfd, process->out, process->err, timeout); + #ifdef DEBUG_ME + fprintf(stderr, "Watch process %d ->%s%s%s%s\n", process->pid, ret & WP_END ? " END" : "", ret & WP_OUTPUT ? " OUTPUT" : "", ret & WP_ERROR ? " ERROR" : "", ret & WP_TIMEOUT ? " TIMEOUT" : ""); + #endif + + if (ret & WP_OUTPUT) + callback_write(process->out, GB_WATCH_READ, process); + + if (ret & WP_ERROR) + callback_error(process->err, GB_WATCH_READ, process); + + if (ret & WP_END) + SIGNAL_raise_callbacks(sigfd, GB_WATCH_READ, 0); + + if (ret & WP_TIMEOUT) + break; + + if (ret == 0) + usleep(1000); + } + } + END_ERROR + + OBJECT_UNREF(process); + + #if 0 + { + sigsuspend(&old); + if (!process->running) + break; + + #ifdef DEBUG_ME + fprintf(stderr, "Waiting: %d\n", process->running); + #endif + + sleep(10); + } + #endif + + /*if (have_sigchld) + sigaddset(&old, SIGCHLD); + sigprocmask(SIG_SETMASK, &old, NULL);*/ + + #ifdef DEBUG_ME + fprintf(stderr, "Waiting for: got it !\n"); + #endif +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Process_exit) + + //fprintf(stderr, "Process_exit\n"); + + while (_running_process_list) + stop_process(_running_process_list); + + exit_child(); + + STRING_free(&CPROCESS_shell); + +END_METHOD + + +BEGIN_METHOD_VOID(Process_new) + + init_process(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(Process_free) + + #ifdef DEBUG_ME + fprintf(stderr, "Process_free %p\n", THIS); + #endif + + exit_process(THIS); + +END_METHOD + + +BEGIN_PROPERTY(Process_Id) + + GB_ReturnInt(THIS->pid); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Process_Kill) + + pid_t pid; + + if (!THIS->running) + return; + + if (THIS->process_group) + { + pid = getpgid(THIS->pid); + if (pid < 0) + return; + pid = - pid; + } + else + pid = THIS->pid; + + kill(pid, SIGKILL); + + //CPROCESS_wait_for(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Process_Signal) + + if (!THIS->running) + return; + + /* + printf("Send SIGUSR1 to process %d\n", THIS->pid); + fflush(NULL);*/ + kill(THIS->pid, SIGUSR1); + +END_METHOD + + +#if 0 +BEGIN_METHOD(CPROCESS_send, GB_STRING text) + + if (!THIS->running || THIS->in < 0) + return; + + STREAM_write(&THIS->ob.stream, STRING(text), LENGTH(text)); + +END_METHOD +#endif + +BEGIN_PROPERTY(Process_State) + + if (THIS->running) + GB_ReturnInteger(1); + else + { + if (WIFEXITED(THIS->status)) + GB_ReturnInteger(0); + else + GB_ReturnInteger(2); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Process_Value) + + int status; + + if (THIS->running) + { + GB_ReturnInteger(0); + return; + } + + status = THIS->status; + + if (WIFEXITED(status)) + GB_ReturnInteger(WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + GB_ReturnInteger(WTERMSIG(status)); + else + GB_ReturnInteger(-1); + +END_PROPERTY + +BEGIN_PROPERTY(Process_LastState) + + if (WIFEXITED(_last_status)) + GB_ReturnInteger(0); + else + GB_ReturnInteger(2); + +END_PROPERTY + + +BEGIN_PROPERTY(Process_LastValue) + + int status; + + status = _last_status; + + if (WIFEXITED(status)) + GB_ReturnInteger(WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + GB_ReturnInteger(WTERMSIG(status)); + else + GB_ReturnInteger(-1); + +END_PROPERTY + +BEGIN_METHOD(Process_Wait, GB_FLOAT timeout) + + CPROCESS_wait_for(THIS, (int)(VARGOPT(timeout, 0.0) * 1000)); + +END_METHOD + +BEGIN_PROPERTY(Process_Ignore) + + if (READ_PROPERTY) + GB_ReturnBoolean(THIS->ignore); + else + { + bool ignore = VPROP(GB_BOOLEAN); + if (THIS->ignore != ignore) + { + THIS->ignore = ignore; + if (ignore) + { + _ignore_process++; + if (_running_process <= _ignore_process) + exit_child(); + } + else + { + _ignore_process--; + if (_running_process > _ignore_process) + init_child(); + } + } + } + +END_METHOD + +BEGIN_METHOD_VOID(Process_CloseInput) + + close_fd(&THIS->in); + +END_METHOD + +/* +static int calc_mode(int arg) +{ + int mode = 0; + + if (arg & R_OK) mode += PM_READ; + if (arg & W_OK) mode += PM_WRITE; + if (arg & X_OK) mode += PM_SHELL; + + return mode; +} + +BEGIN_METHOD(Process_Exec, GB_OBJECT command; GB_INTEGER mode; GB_OBJECT env) + + CARRAY *command = (CARRAY *)VARG(command); + CARRAY *env = (CARRAY *)VARG(env); + + if (GB_CheckObject(command)) + return; + + init_process(THIS); + run_process(THIS, calc_mode(VARGOPT(mode, 0)), command, env); + +END_METHOD + +BEGIN_METHOD(Process_Shell, GB_STRING command; GB_INTEGER mode; GB_OBJECT env) + + char *command = GB_ToZeroString(ARG(command)); + CARRAY *env = (CARRAY *)VARG(env); + + init_process(THIS); + run_process(THIS, calc_mode(VARGOPT(mode, 0)) | PM_SHELL, command, env); + +END_METHOD +*/ + +#endif + +GB_DESC NATIVE_Process[] = +{ + GB_DECLARE("Process", sizeof(CPROCESS)), GB_NOT_CREATABLE(), + GB_INHERITS("Stream"), + + GB_CONSTANT("Stopped", "i", 0), + GB_CONSTANT("Running", "i", 1), + GB_CONSTANT("Crashed", "i", 2), + GB_CONSTANT("Signaled", "i", 2), + + GB_STATIC_PROPERTY_READ("LastState", "i", Process_LastState), + GB_STATIC_PROPERTY_READ("LastValue", "i", Process_LastValue), + + GB_PROPERTY_READ("Id", "i", Process_Id), + GB_PROPERTY_READ("Handle", "i", Process_Id), + GB_PROPERTY_READ("State", "i", Process_State), + GB_PROPERTY_READ("Value", "i", Process_Value), + + GB_STATIC_METHOD("_exit", NULL, Process_exit, NULL), + GB_METHOD("_new", NULL, Process_new, NULL), + GB_METHOD("_free", NULL, Process_free, NULL), + + GB_METHOD("Kill", NULL, Process_Kill, NULL), + GB_METHOD("Signal", NULL, Process_Signal, NULL), + GB_METHOD("Wait", NULL, Process_Wait, "[(Timeout)f]"), + + GB_METHOD("CloseInput", NULL, Process_CloseInput, NULL), + + GB_PROPERTY("Ignore", "b", Process_Ignore), + + /*GB_METHOD("Exec", NULL, Process_Exec, "(Command)String[];[(Mode)i(Environment)String[];]"), + GB_METHOD("Shell", NULL, Process_Shell, "(Command)s;[(Mode)i(Environment)String[];]"),*/ + + GB_EVENT("Read", NULL, NULL, &EVENT_Read), + GB_EVENT("Error", NULL, "(Error)s", &EVENT_Error), + GB_EVENT("Kill", NULL, NULL, &EVENT_Kill), + + GB_END_DECLARE +}; + diff --git a/main/gbx/gbx_c_process.h b/main/gbx/gbx_c_process.h new file mode 100644 index 00000000..e8e1610c --- /dev/null +++ b/main/gbx/gbx_c_process.h @@ -0,0 +1,82 @@ +/*************************************************************************** + + gbx_c_process.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_PROCESS_H +#define __GBX_C_PROCESS_H + +#include +#include + +#include "gambas.h" +#include "gbx_object.h" +#include "gbx_c_file.h" +#include "gbx_c_array.h" + +#ifndef __GBX_C_PROCESS_C +extern GB_DESC NATIVE_Process[]; +extern char *CPROCESS_shell; +#else + +#define THIS ((CPROCESS *)_object) + +#endif + +typedef + struct _CPROCESS { + CSTREAM ob; + struct _CPROCESS *prev; + struct _CPROCESS *next; + pid_t pid; + int in; + int out; + int err; + int status; + unsigned running : 1; + unsigned wait : 1; + unsigned to_string : 1; + unsigned with_error : 1; + unsigned process_group : 1; + unsigned ignore : 1; + unsigned error : 1; + char *result; + GB_VARIANT_VALUE tag; + } + CPROCESS; + +enum +{ + PM_READ = 1, + PM_WRITE = 2, + PM_TERM = 4, + PM_STRING = 8, + PM_WAIT = 16, + PM_ERROR = 32, + PM_SHELL = 128 +}; + +CPROCESS *CPROCESS_create(int mode, void *cmd, char *name, CARRAY *env); +void CPROCESS_wait_for(CPROCESS *process, int timeout); +void CPROCESS_check(void *_object); +const char *CPROCESS_search_program_in_path(char *name); + +#endif diff --git a/main/gbx/gbx_c_string.c b/main/gbx/gbx_c_string.c new file mode 100644 index 00000000..2c79c2b1 --- /dev/null +++ b/main/gbx/gbx_c_string.c @@ -0,0 +1,1046 @@ +/*************************************************************************** + + gbx_c_string.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_STRING_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include "gb_common.h" +#include "gb_common_case.h" +#include "gb_common_buffer.h" + +#include +#include +#include + +#include "gb_error.h" +#include "gb_table.h" +#include "gbx_string.h" +#include "gbx_api.h" +#include "gbx_exec.h" +#include "gbx_subr.h" +#include "gbx_compare.h" +#include "gambas.h" + +#include "gbx_c_string.h" + +//#define DEBUG_CACHE + +const uchar STRING_char_length[256] = +{ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 +}; + +//----------------------------------------------------------------------------- + +static int utf8_get_length(const char *sstr, int len) +{ + const uchar *str = (const uchar *)sstr; + int ulen; + int i; + + ulen = 0; + + for (i = 0; i < len; i++) + { + if ((str[i] & 0xC0) != 0x80) + ulen++; + } + + return ulen; +} + +uint STRING_utf8_to_unicode(const char *sstr, int len) +{ + const uchar *str = (const uchar *)sstr; + uint unicode; + + switch (len) + { + case 2: + unicode = (str[1] & 0x3F) + ((str[0] & 0x1F) << 6); + if (unicode < 0x80) + goto __INVALID; + break; + + case 3: + unicode = (str[2] & 0x3F) + ((str[1] & 0x3F) << 6) + ((str[0] & 0xF) << 12); + if (unicode < 0x800) + goto __INVALID; + break; + + case 4: + unicode = (str[3] & 0x3F) + ((str[2] & 0x3F) << 6) + ((str[1] & 0x3F) << 12) + ((str[0] & 0x7) << 18); + if (unicode < 0x10000) + goto __INVALID; + break; + + case 5: + unicode = (str[4] & 0x3F) + ((str[3] & 0x3F) << 6) + ((str[2] & 0x3F) << 12) + ((str[1] & 0x3F) << 18) + ((str[0] & 0x3) << 24); + if (unicode < 0x200000) + goto __INVALID; + break; + + case 6: + unicode = (str[5] & 0x3F) + ((str[4] & 0x3F) << 6) + ((str[3] & 0x3F) << 12) + ((str[2] & 0x3F) << 18) + ((str[1] & 0x3F) << 24) + ((str[0] & 0x1) << 30); + if (unicode < 0x4000000) + goto __INVALID; + break; + + default: + unicode = str[0]; + break; + } + + return unicode; + +__INVALID: + + return UNICODE_INVALID; +} + +void STRING_utf8_from_unicode(uint code, char *sstr) +{ + uchar *str = (uchar *)sstr; + + if (code < 0x80) + str[0] = code; + else if (code < 0x800) + { + str[0] = (code >> 6) | 0xC0; + str[1] = (code & 0x3F) | 0x80; + } + else if (code < 0x10000) + { + str[0] = (code >> 12) | 0xE0; + str[1] = ((code >> 6) & 0x3F) | 0x80; + str[2] = (code & 0x3F) | 0x80; + } + else if (code < 0x200000) + { + str[0] = (code >> 18) | 0xF0; + str[1] = ((code >> 12) & 0x3F) | 0x80; + str[2] = ((code >> 6) & 0x3F) | 0x80; + str[3] = (code & 0x3F) | 0x80; + } + else if (code < 0x4000000) + { + str[0] = (code >> 24) | 0xF8; + str[1] = ((code >> 18) & 0x3F) | 0x80; + str[2] = ((code >> 12) & 0x3F) | 0x80; + str[3] = ((code >> 6) & 0x3F) | 0x80; + str[4] = (code & 0x3F) | 0x80; + } + else if (code < 0x80000000) + { + str[0] = (code >> 31) | 0xFC; + str[1] = ((code >> 24) & 0x3F)| 0x80; + str[2] = ((code >> 18) & 0x3F) | 0x80; + str[3] = ((code >> 12) & 0x3F) | 0x80; + str[4] = ((code >> 6) & 0x3F) | 0x80; + str[5] = (code & 0x3F) | 0x80; + } + else + str[0] = 0; +} + +int COMMON_get_unicode_char() +{ + int lc; + uint uc; + + lc = STRING_utf8_get_char_length(*COMMON_get_current()); + if (COMMON_pos + lc > COMMON_len) + return -1; + + uc = STRING_utf8_to_unicode(COMMON_get_current(), lc); + COMMON_pos += lc; + return (int)uc; +} + +//----------------------------------------------------------------------------- + +char *STRING_utf8_current = NULL; +static const char *_utf8_current_start = NULL; +static int _utf8_current_len = 0; + +#define UTF8_MAX_COUNT 256 +#define UTF8_MAX_CACHE 64 + +struct { + ushort pos[UTF8_MAX_COUNT]; + ushort last_pos; + ushort cnext; + int lpos; + int lindex; + int cindex[UTF8_MAX_CACHE]; + int cpos[UTF8_MAX_CACHE]; + } +_utf8 = { { 0 } }; + +static int utf8_get_pos(const char *ref, const char *start, int len, int index) +{ + const uchar *str; + int i, j, pos; + int min_index, min_i; + +#ifdef DEBUG_CACHE + fprintf(stderr, "utf8_get_pos: [%p] %p %d %d\n", ref, start, len, index); +#endif + + if (index <= 0) + return 0; + + if (ref != STRING_utf8_current || start != _utf8_current_start || len != _utf8_current_len) + { + STRING_utf8_current = (char *)ref; + _utf8_current_start = start; + _utf8_current_len = len; +#ifdef DEBUG_CACHE + fprintf(stderr, "current -> %p / %ld\n", STRING_utf8_current, _utf8_current_start - STRING_utf8_current); +#endif + CLEAR(&_utf8); + } + + str = (const uchar *)start; + + if (index < UTF8_MAX_COUNT) + { + if (index <= _utf8.last_pos) + { +#ifdef DEBUG_CACHE + fprintf(stderr, "cached -> %d\n", _utf8.pos[index]); +#endif + pos = _utf8.pos[index]; + if (pos >= len) + pos = len; + return pos; + } + + pos = _utf8.pos[_utf8.last_pos]; + + for(;;) + { + if (pos >= len) + return len; + + pos += STRING_utf8_get_char_length(str[pos]); + _utf8.pos[++_utf8.last_pos] = pos; + + if (index == _utf8.last_pos) + { +#ifdef DEBUG_CACHE + fprintf(stderr, "search -> %d\n", pos); +#endif + return pos; + } + } + } + +#ifdef DEBUG_CACHE + fprintf(stderr, "index = %d\n", index); +#endif + + if (index == _utf8.lindex) + { + pos = _utf8.lpos; + if (pos >= len) + pos = len; + return pos; + } + + min_index = 0; + min_i = -1; + + for (j = 0; j < UTF8_MAX_CACHE; j++) + { + i = (_utf8.cnext + UTF8_MAX_CACHE - j - 1) % UTF8_MAX_CACHE; + + if (_utf8.cindex[i] == 0) + break; + + if ((index >= _utf8.cindex[i]) && (index < (_utf8.cindex[i] + 256))) + { +#ifdef DEBUG_CACHE + fprintf(stderr, "use cache %d (%d)\n", i, _utf8.cindex[i]); +#endif + pos = _utf8.cpos[i]; + j = _utf8.cindex[i]; + goto __CALC_POS; + } + else if (_utf8.cindex[i] > min_index && _utf8.cindex[i] < index) + { + min_index = _utf8.cindex[i]; + min_i = i; + } + } + + j = index & ~0xFF; + + if (min_i < 0) + { + pos = 0; + i = 0; + } + else + { + pos = _utf8.cpos[min_i]; + i = _utf8.cindex[min_i]; + } + +#ifdef DEBUG_CACHE + fprintf(stderr, "add cache %d: %d / %d\n", _utf8.cnext, j, index - i); +#endif + + for (; i < j; i++) + { + if (pos >= len) + { + pos = len; + break; + } + pos += STRING_utf8_get_char_length(str[pos]); + } + + _utf8.cindex[_utf8.cnext] = j; + _utf8.cpos[_utf8.cnext] = pos; + _utf8.cnext = (_utf8.cnext + 1) % UTF8_MAX_CACHE; + +__CALC_POS: + + for (i = j; i < index; i++) + { + if (pos >= len) + { + pos = len; + break; + } + pos += STRING_utf8_get_char_length(str[pos]); + } + + _utf8.lindex = index; + _utf8.lpos = pos; + + return pos; +} + +//----------------------------------------------------------------------------- + +static int byte_to_index(const char *str, int len, int byte) +{ + if (byte <= 0) + return 0; + + if (byte > len) + byte = len; + + return utf8_get_length(str, byte); +} + +static int index_to_byte(const char *ref, const char *str, int len, int index) +{ + if (index <= 0) + return 0; + + return utf8_get_pos(ref, str, len, index - 1) + 1; +} + + +BEGIN_METHOD(String_Pos, GB_STRING str; GB_INTEGER index) + + GB_ReturnInteger(index_to_byte(VARG(str).addr, STRING(str), LENGTH(str), VARG(index))); + +END_METHOD + + +BEGIN_METHOD(String_Len, GB_STRING str) + + GB_ReturnInteger(utf8_get_length(STRING(str), LENGTH(str))); + +END_METHOD + + +BEGIN_METHOD(String_Index, GB_STRING str; GB_INTEGER pos) + + GB_ReturnInteger(byte_to_index(STRING(str), LENGTH(str), VARG(pos))); + +END_METHOD + + +static void String_Mid(ushort code) +{ + char *str; + char *ref; + int start, length; + int len, ulen, upos; + bool null; + + SUBR_ENTER(); + + null = SUBR_check_string(PARAM); + + VALUE_conv_integer(&PARAM[1]); + start = PARAM[1]._integer.value - 1; + + if (start < 0) + THROW(E_ARG); + + if (null) + goto _SUBR_MID_FIN; + + ref = PARAM->_string.addr; + str = ref + PARAM->_string.start; + len = PARAM->_string.len; + + ulen = utf8_get_pos(ref, str, len, start); + if (ulen >= len) + { + VOID_STRING(PARAM); + goto _SUBR_MID_FIN; + } + + PARAM->_string.start += ulen; + //str += ulen; + //len -= ulen; + + if (NPARAM == 2) + { + ulen = len - ulen; + } + else + { + VALUE_conv_integer(&PARAM[2]); + length = PARAM[2]._integer.value; + + if (length < 0) + length += utf8_get_length(str, len) - start; + + if (length == 1) + { + ulen = STRING_utf8_get_char_length(str[ulen]); + } + else + { + upos = utf8_get_pos(ref, str, len, start + length); + if (upos > len) + upos = len; + ulen = upos - ulen; + } + } + + if (ulen <= 0) + { + VOID_STRING(PARAM); + } + else + PARAM->_string.len = ulen; + +_SUBR_MID_FIN: + + SP -= NPARAM; + SP++; +} + + +static void String_Left(ushort code) +{ + int val; + char *ref; + char *str; + int len, ulen; + + SUBR_ENTER(); + + if (!SUBR_check_string(PARAM)) + { + if (NPARAM == 1) + val = 1; + else + { + VALUE_conv_integer(&PARAM[1]); + val = PARAM[1]._integer.value; + } + + ref = PARAM->_string.addr; + str = ref + PARAM->_string.start; + len = PARAM->_string.len; + + if (val < 0) + val += utf8_get_length(str, len); + + ulen = utf8_get_pos(ref, str, len, val); + PARAM->_string.len = ulen; + } + + SP -= NPARAM; + SP++; +} + + +static void String_Right(ushort code) +{ + int val; + char *str; + char *ref; + int len, ulen; + + SUBR_ENTER(); + + if (!SUBR_check_string(PARAM)) + { + if (NPARAM == 1) + val = 1; + else + { + VALUE_conv_integer(&PARAM[1]); + val = PARAM[1]._integer.value; + } + + ref = PARAM->_string.addr; + str = ref + PARAM->_string.start; + len = PARAM->_string.len; + + if (val < 0) + val = (-val); + else + val = utf8_get_length(str, len) - val; + + ulen = utf8_get_pos(ref, str, len, val); + + PARAM->_string.start += ulen; + PARAM->_string.len -= ulen; + } + + SP -= NPARAM; + SP++; +} + + +bool STRING_convert_to_unicode(wchar_t **pwstr, int *pwlen, const char *str, int len) +{ + char *result; + int wlen = utf8_get_length(str, len); + int i, lc; + wchar_t *wstr; + + result = STRING_new_temp(NULL, (wlen + 1) * sizeof(wchar_t) - 1); + wstr = (wchar_t *)result; + + for (i = 0; i < wlen; i++) + { + lc = STRING_utf8_get_char_length(*str); + wstr[i] = (wchar_t)STRING_utf8_to_unicode(str, lc); + if (wstr[i] == UNICODE_INVALID) + return TRUE; + str += lc; + } + + wstr[wlen] = 0; + *pwstr = wstr; + *pwlen = wlen; + return FALSE; +} + +static bool convert_to_unicode(wchar_t **wstr, int *wlen, const char *str, int len, bool upper) +{ + wchar_t *wtemp; + int i, l; + + if (len == 0) + { + *wstr = NULL; + *wlen = 0; + return FALSE; + } + + if (STRING_convert_to_unicode(&wtemp, &l, str, len)) + return TRUE; + + if (upper) + { + for (i = 0; i < l; i++) + wtemp[i] = towupper(wtemp[i]); + } + else + { + for (i = 0; i < l; i++) + wtemp[i] = towlower(wtemp[i]); + } + + *wstr = wtemp; + *wlen = l; + return FALSE; +} + +static void convert_string(char *str, int len, bool upper) +{ + char *result; + char *p, *pe; + char c; + int lc; + wchar_t wc; + + if (len <= 0) + { + GB_ReturnVoidString(); + return; + } + + result = STRING_new_temp(str, len); + + p = result; + pe = &result[len]; + + if (upper) + { + while (p < pe) + { + c = *p; + lc = STRING_utf8_get_char_length(c); + + if (lc == 1) + { + *p = toupper(c); + p++; + } + else + { + wc = (wchar_t)STRING_utf8_to_unicode(p, lc); + wc = towupper(wc); + // We suppose that the conversion does not change the number of bytes! + STRING_utf8_from_unicode((uint)wc, p); + //if (STRING_utf8_get_char_length(*p) != lc) + // fprintf(stderr, "convert_string: not the same number of bytes!\n"); + p += lc; + } + } + } + else + { + while (p < pe) + { + c = *p; + lc = STRING_utf8_get_char_length(c); + + if (lc == 1) + { + *p = tolower(c); + p++; + } + else + { + wc = (wchar_t)STRING_utf8_to_unicode(p, lc); + wc = towlower(wc); + // We suppose that the conversion does not change the number of bytes! + STRING_utf8_from_unicode((uint)wc, p); + //if (STRING_utf8_get_char_length(*p) != lc) + // fprintf(stderr, "convert_string: not the same number of bytes!\n"); + p += lc; + } + } + } + + GB_ReturnString(result); +} + + +BEGIN_METHOD(String_Lower, GB_STRING str) + + convert_string(STRING(str), LENGTH(str), FALSE); + +END_METHOD + + +BEGIN_METHOD(String_Upper, GB_STRING str) + + convert_string(STRING(str), LENGTH(str), TRUE); + +END_METHOD + + +BEGIN_METHOD(String_UCaseFirst, GB_STRING str) + + char *str = STRING(str); + int len = LENGTH(str); + char *result; + int lc; + wchar_t wc; + + if (len <= 0) + { + GB_ReturnVoidString(); + return; + } + + result = STRING_new_temp(str, len); + + lc = STRING_utf8_get_char_length(result[0]); + + if (lc == 1) + { + result[0] = toupper(result[0]); + } + else + { + wc = (wchar_t)STRING_utf8_to_unicode(result, lc); + wc = towupper(wc); + // We suppose that the conversion does not change the number of bytes! + STRING_utf8_from_unicode((uint)wc, result); + } + + GB_ReturnString(result); + +END_METHOD + + +BEGIN_METHOD(String_Chr, GB_INTEGER code) + + char temp[8]; + + STRING_utf8_from_unicode(VARG(code), temp); + temp[STRING_utf8_get_char_length(temp[0])] = 0; + GB_ReturnNewZeroString(temp); + +END_METHOD + + +BEGIN_METHOD(String_Code, GB_STRING str; GB_INTEGER index) + + char *str; + int len, index, pos, lc; + + index = VARGOPT(index, 1); + if (index < 1) + { + GB_ReturnInteger(0); + return; + } + + str = STRING(str); + len = LENGTH(str); + + if (index > len) + { + GB_ReturnInteger(0); + return; + } + + pos = utf8_get_pos(VARG(str).addr, str, len, index - 1); + lc = STRING_utf8_get_char_length(str[pos]); + + GB_ReturnInteger(STRING_utf8_to_unicode(&str[pos], lc)); + +END_METHOD + +static void string_search(const char *str, const char *ref, int len, const char *pattern, int lenp, int start, bool right, bool nocase) +{ + int pos; + + if (lenp == 0) + goto __ERROR; + + if (start) + start = index_to_byte(ref, str, len, start); + + if (!nocase) + { + pos = STRING_search(str, len, pattern, lenp, start, right, FALSE); + pos = byte_to_index(str, len, pos); + } + else + { + wchar_t *wstr; + int lstr; + wchar_t *wpattern; + int lpattern; + + if (convert_to_unicode(&wstr, &lstr, str, len, TRUE)) + goto __ERROR; + + if (convert_to_unicode(&wpattern, &lpattern, pattern, lenp, TRUE)) + goto __ERROR; + + if (!start) + start = right ? lstr : 1; + + pos = STRING_search((char *)wstr, lstr * sizeof(wchar_t), (char *)wpattern, lpattern * sizeof(wchar_t), 1 + (start - 1) * sizeof(wchar_t), right, FALSE); + if (pos) + pos = (pos - 1) / sizeof(wchar_t) + 1; + } + + GB_ReturnInteger(pos); + return; + +__ERROR: + + GB_ReturnInteger(0); + return; +} + +BEGIN_METHOD(String_Instr, GB_STRING str; GB_STRING pattern; GB_INTEGER start; GB_INTEGER mode) + + string_search(STRING(str), VARG(str).addr, LENGTH(str), STRING(pattern), LENGTH(pattern), VARGOPT(start, 0), FALSE, VARGOPT(mode, GB_COMP_BINARY) == GB_COMP_NOCASE); + +END_METHOD + +BEGIN_METHOD(String_RInstr, GB_STRING str; GB_STRING pattern; GB_INTEGER start; GB_INTEGER mode) + + string_search(STRING(str), VARG(str).addr, LENGTH(str), STRING(pattern), LENGTH(pattern), VARGOPT(start, 0), TRUE, VARGOPT(mode, GB_COMP_BINARY) == GB_COMP_NOCASE); + +END_METHOD + +BEGIN_METHOD(String_Comp, GB_STRING str1; GB_STRING str2; GB_INTEGER mode) + + int mode = VARGOPT(mode, GB_COMP_BINARY) | GB_COMP_LANG; + bool nocase = (mode & GB_COMP_NOCASE) != 0; + + if (mode & GB_COMP_NATURAL) + GB_ReturnInteger(COMPARE_string_natural(STRING(str1), LENGTH(str1), STRING(str2), LENGTH(str2), nocase)); + else + GB_ReturnInteger(COMPARE_string_lang(STRING(str1), LENGTH(str1), STRING(str2), LENGTH(str2), nocase, TRUE)); + +END_METHOD + +#define IS_VALID(_char) \ + ((_char) < 0x110000 && \ + (((_char) & 0xFFFFF800) != 0xD800) && \ + ((_char) < 0xFDD0 || (_char) > 0xFDEF) && \ + ((_char) & 0xFFFE) != 0xFFFE) + +BEGIN_METHOD(String_IsValid, GB_STRING str) + + const uchar *str; + int len, lc; + uint unicode; + bool valid = FALSE; + int i; + + str = (const uchar *)STRING(str); + len = LENGTH(str); + + while (len) + { + lc = STRING_utf8_get_char_length(*str); + len -= lc; + if (len < 0) + goto _INVALID; + + //for (i = 0; i < len; i++) + // fprintf(stderr, "%02X ", str[i]); + + for (i = 1; i < lc; i++) + { + if ((str[i] & 0xC0) != 0x80) + goto _INVALID; + } + + unicode = STRING_utf8_to_unicode((char *)str, lc); + if (unicode == UNICODE_INVALID) + goto _INVALID; + if (!IS_VALID(unicode)) + goto _INVALID; + + str += lc; + } + + valid = TRUE; + +_INVALID: + + GB_ReturnBoolean(valid); + //fprintf(stderr, "\n"); + +END_METHOD + +//----------------------------------------------------------------------------- + +BEGIN_PROPERTY(BoxedString_Len) + + VALUE *val = &SP[-1]; + + VARIANT_undo(val); + int len = val->_string.len; + RELEASE_STRING(val); + GB_ReturnInteger(len); + +END_PROPERTY + +void BoxedString_get(ushort code) +{ + int start; + int len; + bool null; + + code++; + + SUBR_ENTER(); + + null = SUBR_check_string(PARAM); + + VALUE_conv_integer(&PARAM[1]); + start = PARAM[1]._integer.value; + + if (start < 0) + THROW(E_ARG); + + if (null) + goto _SUBR_MID_FIN; + + if (start >= PARAM->_string.len) + { + VOID_STRING(PARAM); + goto _SUBR_MID_FIN; + } + + if (NPARAM == 2) + len = 1; + else + { + VALUE_conv_integer(&PARAM[2]); + len = PARAM[2]._integer.value; + } + + if (len < 0) + len = Max(0, PARAM->_string.len - start + len); + + len = MinMax(len, 0, PARAM->_string.len - start); + + if (len == 0) + { + RELEASE_STRING(PARAM); + PARAM->_string.addr = NULL; + PARAM->_string.start = 0; + } + else + PARAM->_string.start += start; + + PARAM->_string.len = len; + +_SUBR_MID_FIN: + + SP -= NPARAM; + SP++; +} + +#endif // GBX_INFO + +//----------------------------------------------------------------------------- + +GB_DESC StringDesc[] = +{ + GB_DECLARE_STATIC("String"), + + GB_STATIC_METHOD("Len", "i", String_Len, "(String)s"), + + GB_STATIC_FAST_METHOD("Mid", "s", String_Mid, "(String)s(Start)i[(Length)i]"), + GB_STATIC_FAST_METHOD("Mid$", "s", String_Mid, "(String)s(Start)i[(Length)i]"), + GB_STATIC_FAST_METHOD("Left", "s", String_Left, "(String)s[(Length)i]"), + GB_STATIC_FAST_METHOD("Left$", "s", String_Left, "(String)s[(Length)i]"), + GB_STATIC_FAST_METHOD("Right", "s", String_Right, "(String)s[(Length)i]"), + GB_STATIC_FAST_METHOD("Right$", "s", String_Right, "(String)s[(Length)i]"), + + GB_STATIC_METHOD("Upper", "s", String_Upper, "(String)s"), + GB_STATIC_METHOD("Upper$", "s", String_Upper, "(String)s"), + GB_STATIC_METHOD("UCase", "s", String_Upper, "(String)s"), + GB_STATIC_METHOD("UCase$", "s", String_Upper, "(String)s"), + GB_STATIC_METHOD("UCaseFirst", "s", String_UCaseFirst, "(String)s"), + GB_STATIC_METHOD("UCaseFirst$", "s", String_UCaseFirst, "(String)s"), + GB_STATIC_METHOD("Lower", "s", String_Lower, "(String)s"), + GB_STATIC_METHOD("Lower$", "s", String_Lower, "(String)s"), + GB_STATIC_METHOD("LCase", "s", String_Lower, "(String)s"), + GB_STATIC_METHOD("LCase$", "s", String_Lower, "(String)s"), + + GB_STATIC_METHOD("InStr", "i", String_Instr, "(String)s(Pattern)s[(From)i(Mode)i]"), + GB_STATIC_METHOD("RInStr", "i", String_RInstr, "(String)s(Pattern)s[(From)i(Mode)i]"), + + GB_STATIC_METHOD("Comp", "i", String_Comp, "(String)s(String2)s[(Mode)i]"), + + GB_STATIC_METHOD("Byte", "i", String_Pos, "(String)s(Index)i"), + GB_STATIC_METHOD("Pos", "i", String_Pos, "(String)s(Index)i"), + GB_STATIC_METHOD("Index", "i", String_Index, "(String)s(Byte)i"), + + GB_STATIC_METHOD("Chr", "s", String_Chr, "(Unicode)i"), + GB_STATIC_METHOD("Chr$", "s", String_Chr, "(Unicode)i"), + GB_STATIC_METHOD("Code", "i", String_Code, "(String)s[(Index)i]"), + + GB_STATIC_METHOD("IsValid", "b", String_IsValid, "(String)s"), + + GB_END_DECLARE +}; + +//----------------------------------------------------------------------------- + +GB_DESC BoxedStringDesc[] = +{ + GB_DECLARE_STATIC("_BoxedString"), + + GB_STATIC_PROPERTY_READ("Len", "i", BoxedString_Len), + + GB_STATIC_FAST_METHOD("_get", "s", BoxedString_get, "(Start)i[(Length)i]"), + + //GB_STATIC_FAST_METHOD("_get", "s", _String_get, "(Start)i[(Length)i]"), + + /*GB_STATIC_FAST_METHOD("Mid", "s", _String_Mid, "(String)s(Start)i[(Length)i]"), + GB_STATIC_FAST_METHOD("Mid$", "s", _String_Mid, "(String)s(Start)i[(Length)i]"), + GB_STATIC_FAST_METHOD("Left", "s", _String_Left, "(String)s[(Length)i]"), + GB_STATIC_FAST_METHOD("Left$", "s", _String_Left, "(String)s[(Length)i]"), + GB_STATIC_FAST_METHOD("Right", "s", _String_Right, "(String)s[(Length)i]"), + GB_STATIC_FAST_METHOD("Right$", "s", _String_Right, "(String)s[(Length)i]"), + + GB_STATIC_METHOD("Upper", "s", String_Upper, "(String)s"), + GB_STATIC_METHOD("Upper$", "s", String_Upper, "(String)s"), + GB_STATIC_METHOD("UCase", "s", String_Upper, "(String)s"), + GB_STATIC_METHOD("UCase$", "s", String_Upper, "(String)s"), + GB_STATIC_METHOD("UCaseFirst", "s", String_UCaseFirst, "(String)s"), + GB_STATIC_METHOD("UCaseFirst$", "s", String_UCaseFirst, "(String)s"), + GB_STATIC_METHOD("Lower", "s", String_Lower, "(String)s"), + GB_STATIC_METHOD("Lower$", "s", String_Lower, "(String)s"), + GB_STATIC_METHOD("LCase", "s", String_Lower, "(String)s"), + GB_STATIC_METHOD("LCase$", "s", String_Lower, "(String)s"), + + GB_STATIC_METHOD("InStr", "i", String_Instr, "(String)s(Pattern)s[(From)i(Mode)i]"), + GB_STATIC_METHOD("RInStr", "i", String_RInstr, "(String)s(Pattern)s[(From)i(Mode)i]"), + + GB_STATIC_METHOD("Comp", "i", String_Comp, "(String)s(String2)s[(Mode)i]"), + + GB_STATIC_METHOD("Byte", "i", String_Pos, "(String)s(Index)i"), + GB_STATIC_METHOD("Pos", "i", String_Pos, "(String)s(Index)i"), + GB_STATIC_METHOD("Index", "i", String_Index, "(String)s(Byte)i"), + + GB_STATIC_METHOD("Chr", "s", String_Chr, "(Unicode)i"), + GB_STATIC_METHOD("Chr$", "s", String_Chr, "(Unicode)i"), + GB_STATIC_METHOD("Code", "i", String_Code, "(String)s[(Index)i]"), + + GB_STATIC_METHOD("IsValid", "b", String_IsValid, "(String)s"),*/ + + GB_END_DECLARE +}; diff --git a/main/gbx/gbx_c_string.h b/main/gbx/gbx_c_string.h new file mode 100644 index 00000000..1416af98 --- /dev/null +++ b/main/gbx/gbx_c_string.h @@ -0,0 +1,50 @@ +/*************************************************************************** + + gbx_c_string.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_STRING_H +#define __GBX_C_STRING_H + +#include "gambas.h" + +#ifndef __GBX_C_STRING_C + +EXTERN GB_DESC StringDesc[]; +EXTERN GB_DESC BoxedStringDesc[]; + +EXTERN const char STRING_char_length[]; + +#endif + +#define UNICODE_INVALID 0xFFFFFFFFU + +bool STRING_convert_to_unicode(wchar_t **pwstr, int *pwlen, const char *str, int len); +void STRING_utf8_from_unicode(uint code, char *sstr); +uint STRING_utf8_to_unicode(const char *sstr, int len); + +#define STRING_utf8_get_char_length(_c) ((int)STRING_char_length[(unsigned char)(_c)]) + +int COMMON_get_unicode_char(); + +void BoxedString_get(ushort code); + +#endif diff --git a/main/gbx/gbx_c_system.c b/main/gbx/gbx_c_system.c new file mode 100644 index 00000000..e31aa37d --- /dev/null +++ b/main/gbx/gbx_c_system.c @@ -0,0 +1,383 @@ +/*************************************************************************** + + gbx_c_system.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_SYSTEM_C + +#include "gambas.h" +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include "config.h" + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_common_case.h" +#include "gb_error.h" +#include "gbx_api.h" +#include "gbx_class.h" +#include "gbx_date.h" +#include "gbx_project.h" +#include "gbx_local.h" +#include "gbx_event.h" +#include "gbx_string.h" +#include "gbx_exec.h" +#include "gbx_extern.h" +#include "gbx_object.h" +#include "gbx_c_process.h" +#include "gbx_c_system.h" + +typedef + struct { + const char *prefix; + int prio; + } + SYSLOG_PREFIX; + +static const SYSLOG_PREFIX _syslog_prefix[] = +{ + { "[error]", LOG_ERR }, + { "[warning]", LOG_WARNING }, + { "[notice]", LOG_NOTICE }, + { "[info]", LOG_INFO }, + { "[debug]", LOG_DEBUG }, + { NULL, 0 } +}; + + +BEGIN_PROPERTY(User_Home) + + GB_ReturnString(FILE_get_home()); + +END_PROPERTY + + +BEGIN_PROPERTY(User_Name) + + struct passwd *info = getpwuid(getuid()); + + if (info) + GB_ReturnNewZeroString(info->pw_name); + else + GB_Error((char *)E_MEMORY); + +END_PROPERTY + + +BEGIN_PROPERTY(User_Id) + + GB_ReturnInteger(getuid()); + +END_PROPERTY + + +BEGIN_PROPERTY(User_Group) + + GB_ReturnInteger(getgid()); + +END_PROPERTY + + +BEGIN_PROPERTY(System_Language) + + if (READ_PROPERTY) + GB_ReturnNewZeroString(LOCAL_get_lang()); + else + LOCAL_set_lang(GB_ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + + +BEGIN_PROPERTY(System_FirstDayOfWeek) + + if (READ_PROPERTY) + GB_ReturnInteger(LOCAL_get_first_day_of_week()); + else + LOCAL_set_first_day_of_week(VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_PROPERTY(System_Charset) + + if (LOCAL_is_UTF8) + GB_ReturnConstZeroString("UTF-8"); + else + GB_ReturnString(LOCAL_encoding); + +END_PROPERTY + + +BEGIN_PROPERTY(System_RightToLeft) + + GB_ReturnBoolean(LOCAL_local.rtl); + +END_PROPERTY + + +BEGIN_PROPERTY(System_Path) + + GB_ReturnString(PROJECT_exec_path); + +END_PROPERTY + + +BEGIN_PROPERTY(System_Host) + + char buffer[256]; + + gethostname(buffer, 255); + GB_ReturnNewZeroString(buffer); + +END_PROPERTY + + +BEGIN_PROPERTY(System_Domain) + + char buffer[256]; + + if (getdomainname(buffer, 255)) + GB_Error("Unable to retrieve domain name: &1", strerror(errno)); + else + GB_ReturnNewZeroString(buffer); + +END_PROPERTY + + +BEGIN_PROPERTY(System_ByteOrder) + + GB_ReturnInteger(EXEC_big_endian); + +END_PROPERTY + + +BEGIN_PROPERTY(System_Backtrace) + + STACK_BACKTRACE *bt = STACK_get_backtrace(); + + GB_ReturnObject(DEBUG_get_string_array_from_backtrace(bt)); + STACK_free_backtrace(&bt); + +END_PROPERTY + + +BEGIN_PROPERTY(System_Error) + + GB_ReturnInteger(errno); + +END_PROPERTY + + +BEGIN_METHOD(System_GetExternSymbol, GB_STRING library; GB_STRING name) + + char *library = GB_ToZeroString(ARG(library)); + char *name = GB_ToZeroString(ARG(name)); + void *ptr = NULL; + + if (*library && *name) + ptr = EXTERN_get_symbol(library, name); + + GB_ReturnPointer(ptr); + +END_METHOD + + +BEGIN_PROPERTY(System_Shell) + + if (READ_PROPERTY) + { + if (!CPROCESS_shell) + GB_ReturnConstZeroString("/bin/sh"); + else + GB_ReturnString(CPROCESS_shell); + } + else + GB_StoreString(PROP(GB_STRING), &CPROCESS_shell); + +END_PROPERTY + +BEGIN_PROPERTY(System_Profile) + + if (READ_PROPERTY) + GB_ReturnBoolean(EXEC_profile_instr); + else + EXEC_profile_instr = EXEC_profile && VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_METHOD_VOID(System_Breakpoint) + + BREAKPOINT(); + +END_METHOD + +BEGIN_PROPERTY(System_TimeZone) + + GB_ReturnInteger(DATE_get_timezone()); + +END_PROPERTY + +BEGIN_PROPERTY(System_BreakOnError) + + if (READ_PROPERTY) + GB_ReturnBoolean(EXEC_break_on_error); + else if (EXEC_debug) + EXEC_break_on_error = VPROP(GB_BOOLEAN); + +END_METHOD + +BEGIN_METHOD(System_Log, GB_STRING message) + + static bool _opened = FALSE; + + const SYSLOG_PREFIX *p; + char *msg; + int len; + int lenp; + int prio; + + msg = STRING(message); + len = LENGTH(message); + prio = LOG_INFO; + + for (p = _syslog_prefix; p->prefix; p++) + { + lenp = strlen(p->prefix); + if (len >= (lenp + 2) && strncasecmp(msg, p->prefix, lenp) == 0) + { + msg += lenp + 2; + len -= lenp + 2; + prio = p->prio; + break; + } + } + + while (len > 0 && *msg == ' ') + msg++, len--; + + if (len <= 0) + return; + + if (!_opened) + { + _opened = TRUE; + openlog(PROJECT_name, LOG_PID, LOG_INFO); + } + + syslog(prio, "%.*s", len, msg); + +END_METHOD + +BEGIN_METHOD(System_Find, GB_STRING program) + + const char *path; + + path = CPROCESS_search_program_in_path(GB_ToZeroString(ARG(program))); + if (!path) + GB_ReturnNull(); + else + GB_ReturnNewZeroString(path); + +END_METHOD + +BEGIN_METHOD(System_Exist, GB_STRING program) + + GB_ReturnBoolean(CPROCESS_search_program_in_path(GB_ToZeroString(ARG(program))) != NULL); + +END_METHOD + +//------------------------------------------------------------------------- + +BEGIN_PROPERTY(Jit_Time) + + GB_ReturnFloat(0); + +END_PROPERTY + +//------------------------------------------------------------------------- + +#endif + +GB_DESC NATIVE_User[] = +{ + GB_DECLARE_STATIC("User"), + + GB_STATIC_PROPERTY_READ("Name", "s", User_Name), + GB_STATIC_PROPERTY_READ("Id", "i", User_Id), + GB_STATIC_PROPERTY_READ("Group", "i", User_Group), + GB_STATIC_PROPERTY_READ("Home", "s", User_Home), + + GB_END_DECLARE +}; + +GB_DESC NATIVE_System[] = +{ + GB_DECLARE_STATIC("System"), + + GB_STATIC_PROPERTY_READ("Path", "s", System_Path), + GB_CONSTANT("Version", "s", GAMBAS_VERSION_STRING), + GB_CONSTANT("FullVersion", "s", VERSION), + GB_STATIC_PROPERTY_READ("Backtrace", "String[]", System_Backtrace), + GB_STATIC_PROPERTY("BreakOnError", "b", System_BreakOnError), + + GB_STATIC_PROPERTY("Language", "s", System_Language), + GB_STATIC_PROPERTY("FirstDayOfWeek", "i", System_FirstDayOfWeek), + GB_STATIC_PROPERTY("Shell", "s", System_Shell), + GB_STATIC_PROPERTY("Profile", "b", System_Profile), + + GB_STATIC_PROPERTY_READ("RightToLeft", "b", System_RightToLeft), + GB_STATIC_PROPERTY_READ("Charset", "s", System_Charset), + GB_STATIC_PROPERTY_READ("Host", "s", System_Host), + GB_STATIC_PROPERTY_READ("Domain", "s", System_Domain), + GB_STATIC_PROPERTY_READ("ByteOrder", "i", System_ByteOrder), + GB_STATIC_PROPERTY_READ("Error", "i", System_Error), + GB_STATIC_PROPERTY_READ("TimeZone", "i", System_TimeZone), + + GB_CONSTANT("Family", "s", SYSTEM), + GB_CONSTANT("Architecture", "s", ARCHITECTURE), + + GB_STATIC_METHOD("GetExternSymbol", "p", System_GetExternSymbol, "(Library)s(Symbol)s"), + GB_STATIC_METHOD("_Breakpoint", NULL, System_Breakpoint, NULL), + + GB_STATIC_PROPERTY_SELF("User", "User"), + + GB_STATIC_METHOD("Log", NULL, System_Log, "(Message)s"), + + GB_STATIC_METHOD("Exist", "b", System_Exist, "(Program)s"), + GB_STATIC_METHOD("Find", "s", System_Find, "(Program)s"), + + GB_END_DECLARE +}; + +GB_DESC NATIVE_Jit[] = +{ + GB_DECLARE_STATIC("Jit"), + + GB_STATIC_PROPERTY_READ("Time", "f", Jit_Time), + + GB_END_DECLARE +}; diff --git a/main/gbx/gbx_c_system.h b/main/gbx/gbx_c_system.h new file mode 100644 index 00000000..db2cb9ca --- /dev/null +++ b/main/gbx/gbx_c_system.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + gbx_c_system.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_SYSTEM_H +#define __GBX_C_SYSTEM_H + +#include "gambas.h" + +#ifndef __GBX_C_SYSTEM_C +extern GB_DESC NATIVE_User[]; +extern GB_DESC NATIVE_System[]; +extern GB_DESC NATIVE_Jit[]; +#endif + +#endif diff --git a/main/gbx/gbx_c_task.c b/main/gbx/gbx_c_task.c new file mode 100644 index 00000000..333a0962 --- /dev/null +++ b/main/gbx/gbx_c_task.c @@ -0,0 +1,739 @@ +/*************************************************************************** + + gbx_c_task.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_TASK_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_list.h" +#include "gb_file.h" +#include "gb_error.h" +#include "gbx_api.h" +#include "gbx_exec.h" +#include "gbx_string.h" +#include "gbx_signal.h" +#include "gbx_event.h" + +#include "gbx_c_file.h" +#include "gbx_c_task.h" + +//#define DEBUG_ME 1 +#ifdef __CYGWIN__ +#define FIONREAD TIOCINQ +#endif + +DECLARE_EVENT(EVENT_Read); +DECLARE_EVENT(EVENT_Error); +DECLARE_EVENT(EVENT_Kill); + +enum { + CHILD_OK = 0, + CHILD_ERROR = 1, + CHILD_STDOUT = 2, + CHILD_STDERR = 3, + CHILD_RETURN = 4 +}; + +static SIGNAL_CALLBACK *_signal_handler = NULL; +static CTASK *_task_list = NULL; +static int _task_count = 0; + +//------------------------------------------------------------------------- + +static void cleanup_task(CTASK *_object); +static void stop_task(CTASK *_object); +static void close_fd(int *pfd); + +static void has_forked(void) +{ + CTASK *task; + pid_t pid = getpid(); + STREAM *stream; + + FILE_init(); + EXEC_debug = FALSE; + EXEC_task = TRUE; + if (EXEC_profile) + DEBUG.Profile.Cancel(); + EXEC_profile = FALSE; + EXEC_profile_instr = FALSE; + + EXEC_Hook.loop = NULL; + EXEC_Hook.wait = NULL; + EXEC_Hook.timer = NULL; + EXEC_Hook.watch = NULL; + EXEC_Hook.post = NULL; + //EXEC_Hook.quit = NULL; + + stream = CSTREAM_stream(CFILE_out); + + stream->common.eol = 0; + STREAM_blocking(stream, TRUE); + + SIGNAL_has_forked(); + + task = _task_list; + while (task) + { + if (task->pid != pid) + { + close_fd(&task->fd_out); + close_fd(&task->fd_err); + } + + task = task->list.next; + } +} + +static void callback_child(int signum, intptr_t data) +{ + CTASK *_object, *next; + int status; + + #if DEBUG_ME + fprintf(stderr, ">> callback_child\n"); + #endif + + _object = _task_list; + while (_object) + { + next = THIS->list.next; + if (wait4(THIS->pid, &status, WNOHANG, NULL) == THIS->pid) + { + THIS->status = status; + stop_task(THIS); + } + _object = next; + } + + #if DEBUG_ME + fprintf(stderr, "<< callback_child\n"); + #endif +} + +static int get_readable(int fd) +{ + int len; + + if (ioctl(fd, FIONREAD, &len) < 0 || len <= 0) + return 0; + else + return len; +} + +static bool callback_read(int fd, int type, CTASK *_object) +{ + int len; + char *data; + char *p; + int n; + + //fprintf(stderr, "callback_read: %d %p\n", fd, THIS); + + len = get_readable(fd); + if (len == 0) + return TRUE; + + data = STRING_new(NULL, len); + p = data; + + while (len > 0) + { + n = read(fd, p, len); + if (n < 0) + { + if (errno == EINTR) + continue; + else + break; + } + len -= n; + p += n; + } + + GB_Raise(THIS, EVENT_Read, 1, GB_T_STRING, data, STRING_length(data) - len); + + STRING_free(&data); + return FALSE; +} + + +static int callback_error(int fd, int type, CTASK *_object) +{ + char buffer[256]; + int n; + + //fprintf(stderr, "callback_error: %d %p\n", fd, THIS); + + n = read(fd, buffer, sizeof(buffer)); + + if (n <= 0) + return TRUE; + + GB_Raise(THIS, EVENT_Error, 1, GB_T_STRING, buffer, n); + return FALSE; +} + +static bool create_return_directory(void) +{ + static bool mkdir_done = FALSE; + + char buf[PATH_MAX]; + + if (mkdir_done) + return FALSE; + + sprintf(buf, RETURN_DIR_PATTERN, getuid(), getpid()); + + if (mkdir(buf, S_IRWXU) != 0) + { + GB_Error("Cannot create task return directory"); + return TRUE; + } + + mkdir_done = TRUE; + return FALSE; +} + +static void init_task(void) +{ + _task_count++; + + if (_task_count > 1) + return; + + _signal_handler = SIGNAL_register(SIGCHLD, callback_child, 0); +} + +static void exit_task(void) +{ + _task_count--; + + if (_task_count > 0) + return; + + SIGNAL_unregister(SIGCHLD, _signal_handler); + _signal_handler = NULL; +} + +static void prepare_task(CTASK *_object) +{ + THIS->fd_out = -1; + THIS->fd_err = -1; +} + +static void exit_child(int ret) +{ + FILE_exit(); + _exit(ret); +} + +static bool start_task(CTASK *_object) +{ + const char *err = NULL; + pid_t pid; + sigset_t sig, old; + GB_FUNCTION func; + int fd_out[2], fd_err[2]; + bool has_read, has_error; + GB_VALUE *ret; + char buf[PATH_MAX]; + FILE *f; + + if (EXEC_task) + return TRUE; + + if (THIS->stopped) + { + cleanup_task(THIS); + return TRUE; + } + + init_task(); + + LIST_insert(&_task_list, THIS, &THIS->list); + + // Create pipes + + has_read = GB_CanRaise(THIS, EVENT_Read); + has_error = GB_CanRaise(THIS, EVENT_Error); + + if (has_read && pipe(fd_out) != 0) + goto __ERROR; + + if (has_error && pipe(fd_err) != 0) + goto __ERROR; + + // Block SIGCHLD + + sigemptyset(&sig); + + sigaddset(&sig, SIGCHLD); + sigprocmask(SIG_BLOCK, &sig, &old); + + pid = fork(); + if (pid == (-1)) + { + stop_task(THIS); + sigprocmask(SIG_SETMASK, &old, NULL); + goto __ERROR; + } + + if (pid) + { + #if DEBUG_ME + fprintf(stderr, "start_task: %p %d\n", THIS, pid); + #endif + THIS->pid = pid; + + if (has_read) + { + close(fd_out[1]); + THIS->fd_out = fd_out[0]; + + GB_Watch(THIS->fd_out, GB_WATCH_READ, (void *)callback_read, (intptr_t)THIS); + } + + if (has_error) + { + close(fd_err[1]); + THIS->fd_err = fd_err[0]; + + fcntl(THIS->fd_err, F_SETFL, fcntl(THIS->fd_err, F_GETFL) | O_NONBLOCK); + GB_Watch(THIS->fd_err, GB_WATCH_READ, (void *)callback_error, (intptr_t)THIS); + } + + sigprocmask(SIG_SETMASK, &old, NULL); + } + else // child task + { + THIS->child = TRUE; + THIS->pid = getpid(); + + sigprocmask(SIG_SETMASK, &old, NULL); + + if (has_read) + { + close(fd_out[0]); + + if (dup2(fd_out[1], STDOUT_FILENO) == -1) + exit_child(CHILD_STDOUT); + + setlinebuf(stdout); + } + else + close(CHILD_STDOUT); + + if (has_error) + { + close(fd_err[0]); + + if (dup2(fd_err[1], STDERR_FILENO) == -1) + exit_child(CHILD_STDERR); + + setlinebuf(stderr); + } + else + close(CHILD_STDERR); + + has_forked(); // After the redirection + + GB_GetFunction(&func, THIS, "Main", "", NULL); + + TRY + { + ret = GB_Call(&func, 0, FALSE); + if (ret->type != GB_T_VOID) + { + sprintf(buf, RETURN_FILE_PATTERN, getuid(), getppid(), getpid()); + #if DEBUG_ME + fprintf(stderr, "serialize to: %s\n", buf); + #endif + GB_ReturnConvVariant(); + if (GB_Serialize(buf, ret)) + { + #if DEBUG_ME + fprintf(stderr, "gb.task: serialization has failed\n"); + #endif + exit_child(CHILD_RETURN); + } + } + } + CATCH + { + if (ERROR_current->info.code && ERROR_current->info.code != E_ABORT) + { + sprintf(buf, RETURN_FILE_PATTERN, getuid(), getppid(), getpid()); + f = fopen(buf, "w+"); + if (f) + { + ERROR_print_at(f, FALSE, FALSE); + fclose(f); + } + + exit_child(CHILD_ERROR); + } + } + END_TRY + + exit_child(CHILD_OK); + } + + return FALSE; + +__ERROR: + + // TODO: as the routine is posted, nobody will see the error! + if (!err) + err = strerror(errno); + fprintf(stderr, "gb.task: cannot run task: %s\n", err); + GB_Error("Cannot run task: &1", err); + + return TRUE; +} + +static void close_fd(int *pfd) +{ + int fd = *pfd; + + if (fd >= 0) + { + GB_Watch(fd, GB_WATCH_NONE, NULL, 0); + close(fd); + *pfd = -1; + } +} + +static bool get_return_value(CTASK *_object, bool cleanup) +{ + char path[PATH_MAX]; + GB_VALUE value; + bool fail = FALSE; + struct stat info; + char *err = NULL; + int fd; + int n; + + sprintf(path, RETURN_FILE_PATTERN, getuid(), getpid(), THIS->pid); + + if (!cleanup) + { + switch (THIS->status) + { + case CHILD_OK: + + #if DEBUG_ME + fprintf(stderr,"unserialize from: %s\n", path); + #endif + if (!THIS->got_value) + { + if (stat(path, &info)) + { + GB_Error((char *)E_NRETURN); + return TRUE; + } + + fail = GB_UnSerialize(path, &value); + if (!fail) + GB_StoreVariant(&value._variant, &THIS->ret); + THIS->got_value = TRUE; + } + break; + + case CHILD_ERROR: + + if (stat(path, &info)) + { + fail = TRUE; + } + else + { + err = STRING_new_temp(NULL, info.st_size); + + fd = open(path, O_RDONLY); + if (fd < 0) + { + fail = TRUE; + break; + } + else + { + for(;;) + { + n = read(fd, err, info.st_size); + if (n == info.st_size || errno != EINTR) + break; + } + close(fd); + + if (n == info.st_size) + GB_Error("Task has failed: &1", err); + else + fail = TRUE; + } + } + + if (fail) + GB_Error("Unable to get task error"); + + break; + } + } + + unlink(path); + + return fail; +} + +static void cleanup_task(CTASK *_object) +{ + //printf("cleanup task %p\n", THIS); fflush(stdout); + + OBJECT_UNREF(_object); +} + +static void stop_task(CTASK *_object) +{ + int len; + GB_RAISE_HANDLER handler; + + #if DEBUG_ME + fprintf(stderr, "stop_task: %p %d\n", THIS, THIS->pid); + #endif + + THIS->stopped = TRUE; + + // Remove task temporary files + FILE_remove_temp_file_pid(THIS->pid); + + // Flush standard error + if (THIS->fd_err >= 0) + while (callback_error(THIS->fd_err, 0, THIS) == 0); + + // Flush standard output + if (THIS->fd_out >= 0) + { + for(;;) + { + len = get_readable(THIS->fd_out); + if (len <= 0) + break; + + if (callback_read(THIS->fd_out, 0, THIS)) + break; + } + } + + close_fd(&THIS->fd_out); + close_fd(&THIS->fd_err); + + LIST_remove(&_task_list, THIS, &THIS->list); + exit_task(); + + //printf("Kill event...\n"); fflush(stdout); + + if (GB_CanRaise(THIS, EVENT_Kill)) + { + handler.callback = (GB_CALLBACK)cleanup_task; + handler.data = (intptr_t)THIS; + + GB_RaiseBegin(&handler); + GB_Raise(THIS, EVENT_Kill, 0); + GB_RaiseEnd(&handler); + } + + cleanup_task(THIS); +} + +//------------------------------------------------------------------------- + +BEGIN_METHOD_VOID(Task_new) + + GB_FUNCTION func; + + THIS->ret.type = GB_T_NULL; + + if (EXEC_task) + { + GB_Error("A task cannot create other tasks"); + return; + } + + if (create_return_directory()) + return; + + if (GB_GetFunction(&func, THIS, "Main", "", NULL)) + return; + + if (_task_count > MAX_TASK) + { + GB_Error("Too many tasks"); + return; + } + + prepare_task(THIS); + + OBJECT_REF(THIS); + EVENT_post((GB_CALLBACK)start_task, (intptr_t)THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Task_free) + + get_return_value(THIS, TRUE); + GB_StoreVariant(NULL, &THIS->ret); + +END_METHOD + +BEGIN_PROPERTY(Task_Handle) + + GB_ReturnInteger(THIS->pid); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Task_Stop) + + if (THIS->pid > 0) + kill(THIS->pid, SIGKILL); + else + THIS->stopped = TRUE; + +END_METHOD + +static void error_Task_Wait(CTASK *task) +{ + OBJECT_UNREF(task); +} + +BEGIN_METHOD_VOID(Task_Wait) + + OBJECT_REF(THIS); + + //printf("Task_Wait: %p\n", THIS); fflush(stdout); + + ON_ERROR_1(error_Task_Wait, THIS) + { + for(;;) + { + //printf("GB_Wait\n"); fflush(stdout); + GB_Wait(0); + //printf("stopped = %d\n", THIS->stopped); fflush(stdout); + if (THIS->stopped) + break; + //printf("sleep\n"); fflush(stdout); + sleep(10); + } + } + END_ERROR + + OBJECT_UNREF(_object); + +END_METHOD + +BEGIN_PROPERTY(Task_Value) + + if (!THIS->child && THIS->stopped) + { + if (WIFEXITED(THIS->status)) + { + switch (WEXITSTATUS(THIS->status)) + { + case CHILD_OK: + + if (get_return_value(THIS, FALSE)) + break; + GB_ReturnVariant(&THIS->ret); + return; + + case CHILD_STDOUT: + + GB_Error("Unable to redirect task standard output"); + return; + + case CHILD_STDERR: + + GB_Error("Unable to redirect task error output"); + return; + + case CHILD_RETURN: + + GB_Error("Unable to serialize task return value"); + return; + + case CHILD_ERROR: + + get_return_value(THIS, FALSE); + break; + } + } + else if (WIFSIGNALED(THIS->status)) + { + GB_Error("Task has aborted: &1", strsignal(WTERMSIG(THIS->status))); + return; + } + } + + GB_ReturnNull(); + GB_ReturnConvVariant(); + +END_PROPERTY + +BEGIN_PROPERTY(Task_Running) + + GB_ReturnBoolean(!THIS->stopped); + +END_PROPERTY + +//------------------------------------------------------------------------- + +#endif + +GB_DESC TaskDesc[] = +{ + GB_DECLARE("Task", sizeof(CTASK)), GB_NOT_CREATABLE(), + + GB_METHOD("_new", NULL, Task_new, NULL), + GB_METHOD("_free", NULL, Task_free, NULL), + + GB_PROPERTY_READ("Handle", "i", Task_Handle), + GB_PROPERTY_READ("Value", "v", Task_Value), + GB_PROPERTY_READ("Running", "b", Task_Running), + + GB_METHOD("Stop", NULL, Task_Stop, NULL), + GB_METHOD("Wait", NULL, Task_Wait, NULL), + + GB_EVENT("Read", NULL, "(Data)s", &EVENT_Read), + GB_EVENT("Error", NULL, "(Data)s", &EVENT_Error), + GB_EVENT("Kill", NULL, NULL, &EVENT_Kill), + + GB_END_DECLARE +}; diff --git a/main/gbx/gbx_c_task.h b/main/gbx/gbx_c_task.h new file mode 100644 index 00000000..8bc6e2c3 --- /dev/null +++ b/main/gbx/gbx_c_task.h @@ -0,0 +1,60 @@ +/*************************************************************************** + + gbx_c_task.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_TASK_H +#define __GBX_C_TASK_H + +#include +#include + +#include "gambas.h" +#include "gb_file.h" +#include "gb_list.h" + +#ifndef __GBX_C_TASK_C +extern GB_DESC TaskDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + LIST list; + GB_VARIANT_VALUE ret; + pid_t pid; + int fd_out; + int fd_err; + int status; + volatile sig_atomic_t stopped; + unsigned child : 1; + unsigned got_value : 1; + } + CTASK; + +#define THIS ((CTASK *)_object) + +#define RETURN_DIR_PATTERN FILE_TEMP_DIR "/task" +#define RETURN_FILE_PATTERN FILE_TEMP_DIR "/task/%d" + +#define MAX_TASK 256 + +#endif diff --git a/main/gbx/gbx_c_timer.c b/main/gbx/gbx_c_timer.c new file mode 100644 index 00000000..2b2c19b3 --- /dev/null +++ b/main/gbx/gbx_c_timer.c @@ -0,0 +1,206 @@ +/*************************************************************************** + + gbx_c_timer.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_C_TIMER_C + +#include "gbx_info.h" + +#ifndef GBX_INFO + +#include "gb_common.h" +#include "gbx_watch.h" +#include "gbx_api.h" +#include "gbx_exec.h" +#include "gbx_event.h" +#include "gbx_c_timer.h" + +DECLARE_EVENT(EVENT_Timer); + + +static void enable_timer(CTIMER *_object, bool on) +{ + if (on != (THIS->id != 0)) + HOOK_DEFAULT(timer, WATCH_timer)((GB_TIMER *)THIS, on); + if (on && (THIS->id == 0)) + GB_Error("Too many active timers"); +} + +CTIMER *CTIMER_every(int delay, GB_TIMER_CALLBACK callback, intptr_t param) +{ + CTIMER *timer; + + timer = OBJECT_create(CLASS_Timer, NULL, NULL, 0); + OBJECT_REF(timer); + timer->callback = callback; + timer->delay = delay; + timer->tag = param; + + enable_timer(timer, TRUE); + + return timer; +} + +void CTIMER_raise(void *_object) +{ + if (THIS->callback) + { + if (!(*(THIS->callback))(THIS->tag)) + return; + } + else + { + if (!GB_Raise(THIS, EVENT_Timer, 0)) + return; + } + + enable_timer(THIS, FALSE); +} + + +BEGIN_METHOD(Timer_new, GB_INTEGER delay) + + int delay; + THIS->id = 0; + + delay = VARGOPT(delay, -1); + if (delay < 0) + delay = 1000; + + THIS->delay = delay; + if (!MISSING(delay)) + enable_timer(THIS, TRUE); + +END_METHOD + + +BEGIN_METHOD_VOID(Timer_free) + + if (THIS->id) + HOOK_DEFAULT(timer, WATCH_timer)((GB_TIMER *)THIS, FALSE); + +END_METHOD + + +BEGIN_METHOD_VOID(Timer_Start) + + enable_timer(THIS, TRUE); + +END_METHOD + + +BEGIN_METHOD_VOID(Timer_Stop) + + enable_timer(THIS, FALSE); + +END_METHOD + + +BEGIN_METHOD_VOID(Timer_Restart) + + enable_timer(THIS, FALSE); + enable_timer(THIS, TRUE); + +END_METHOD + + +BEGIN_PROPERTY(Timer_Enabled) + + if (READ_PROPERTY) + GB_ReturnBoolean(THIS->id != 0); + else + enable_timer(THIS, VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Timer_Delay) + + if (READ_PROPERTY) + GB_ReturnInteger(THIS->delay); + else + { + int delay = VPROP(GB_INTEGER); + bool enabled = THIS->id != 0; + + if (delay > 0) + { + if (enabled) + HOOK_DEFAULT(timer, WATCH_timer)((GB_TIMER *)THIS, FALSE); + + THIS->delay = delay; + + if (enabled) + HOOK_DEFAULT(timer, WATCH_timer)((GB_TIMER *)THIS, TRUE); + } + } + +END_PROPERTY + +static void trigger_timer(void *_object) +{ + THIS->triggered = FALSE; + GB_Raise(THIS, EVENT_Timer, 0); + OBJECT_UNREF(_object); +} + +BEGIN_METHOD_VOID(Timer_Trigger) + + if (THIS->triggered) + return; + + THIS->triggered = TRUE; + OBJECT_REF(THIS); + EVENT_post(trigger_timer, (intptr_t)THIS); + +END_METHOD + +#endif + +GB_DESC NATIVE_Timer[] = +{ + GB_DECLARE("Timer", sizeof(CTIMER)), + + GB_METHOD("_new", NULL, Timer_new, "[(Delay)i]"), + GB_METHOD("_free", NULL, Timer_free, NULL), + + GB_PROPERTY("Enabled", "b", Timer_Enabled), + GB_PROPERTY("Delay", "i", Timer_Delay), + //GB_PROPERTY_READ("Timeout", "f", Timer_Timeout), + + GB_METHOD("Start", NULL, Timer_Start, NULL), + GB_METHOD("Stop", NULL, Timer_Stop, NULL), + GB_METHOD("Restart", NULL, Timer_Restart, NULL), + GB_METHOD("Trigger", NULL, Timer_Trigger, NULL), + + GB_CONSTANT("_IsControl", "b", TRUE), + GB_CONSTANT("_IsVirtual", "b", TRUE), + GB_CONSTANT("_Group", "s", "Special"), + GB_CONSTANT("_Properties", "s", "Enabled,Delay{Range:0;86400000;10;ms}=1000"), + GB_CONSTANT("_DefaultEvent", "s", "Timer"), + + GB_EVENT("Timer", NULL, NULL, &EVENT_Timer), + + GB_END_DECLARE +}; + + diff --git a/main/gbx/gbx_c_timer.h b/main/gbx/gbx_c_timer.h new file mode 100644 index 00000000..52ea4db1 --- /dev/null +++ b/main/gbx/gbx_c_timer.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + gbx_c_timer.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_C_TIMER_H +#define __GBX_C_TIMER_H + +#include "gambas.h" +#include "gbx_variant.h" +#include "gb_list.h" + +typedef + GB_TIMER CTIMER; + +#ifndef __GBX_C_TIMER_C +extern GB_DESC NATIVE_Timer[]; +#else + +#define THIS ((CTIMER *)_object) + +#endif + +void CTIMER_raise(void *_object); +CTIMER *CTIMER_every(int delay, GB_TIMER_CALLBACK callback, intptr_t param); + +#endif diff --git a/main/gbx/gbx_class.c b/main/gbx/gbx_class.c new file mode 100644 index 00000000..3258c60b --- /dev/null +++ b/main/gbx/gbx_class.c @@ -0,0 +1,1562 @@ +/*************************************************************************** + + gbx_class.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_CLASS_C + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_common_case.h" +#include "gb_alloc.h" +#include "gb_error.h" +#include "gb_limit.h" + +#include + +#include "gb_buffer.h" +#include "gb_file.h" +#include "gbx_type.h" +#include "gbx_exec.h" +#include "gbx_debug.h" +#include "gb_magic.h" +#include "gbx_stream.h" +#include "gbx_struct.h" +#include "gbx_string.h" +#include "gbx_object.h" +#include "gbx_variant.h" +#include "gbx_number.h" +#include "gbx_c_array.h" +#include "gbx_jit.h" + +#include "gambas.h" + +#include "gbx_class.h" + +//#define DEBUG_COMP 1 +//#define DEBUG_LOAD 1 +//#define DEBUG_DESC 1 + +// We are exiting... +bool CLASS_exiting = FALSE; + +/* Global class table */ +static TABLE _global_table; +/* List of all classes */ +static CLASS *_classes = NULL; +/* First created class (must be 'Class' class) */ +static CLASS *_first = NULL; +/* This flag forces CLASS_find() to only register in the global table */ +static bool _global = FALSE; + +#ifdef DEBUG +static CLASS *Class; +#endif + + +void CLASS_init(void) +{ + TABLE_create_static(&_global_table, sizeof(CLASS_SYMBOL), TF_IGNORE_CASE); + + CLASS_init_native(); +} + + +TABLE *CLASS_get_table(void) +{ + return &_global_table; +} + +CLASS *CLASS_get_list(void) +{ + return _classes; +} + +static void exit_class(CLASS *class, bool native) +{ + //CLASS_DESC *desc; + + /*if (!class->ready && !CLASS_is_virtual(class)) + printf("WARNING: class %s was never loaded.\n", class->name);*/ + + #if DEBUG_LOAD + fprintf(stderr, "Exiting class %s...\n", class->name); + #endif + + /* Est-ce que _exit marche pour les classes gambas ? */ + + if (CLASS_is_native(class) != native) + return; + + if (!CLASS_is_loaded(class)) + return; + + EXEC_public(class, NULL, "_exit", 0); +} + +static void unload_class(CLASS *class) +{ + #if DEBUG_LOAD + fprintf(stderr, "Unloading class %s...\n", class->name); + #endif + + if (class->free_name) + FREE(&class->name); + + if (class->is_struct) + { + FREE(&class->load->dyn); + if (class->debug) + FREE(&class->load->global); + FREE(&class->load); + FREE(&class->data); + } + else if (!CLASS_is_native(class)) + { + #ifdef OS_64BITS + + if (class->load) + { + FREE(&class->load->desc); + FREE(&class->load->cst); + FREE(&class->load->class_ref); + FREE(&class->load->unknown); + FREE(&class->load->event); + FREE(&class->load->ext); + FREE(&class->load->local); + FREE(&class->load->array); + + if (class->debug) + { + int i; + FUNCTION *func; + + for (i = 0; i < class->load->n_func; i++) + { + func = &class->load->func[i]; + FREE(&func->debug->local); + } + + FREE(&class->load->global); + FREE(&class->load->debug); + } + + FREE(&class->load->func); + } + + #endif + + if (class->load) + FREE(&class->load->prof); + + FREE(&class->load); + //if (!class->mmapped) + FREE(&class->data); + } + else + { + FREE(&class->signature); + FREE(&class->data); + } + + if (class->free_event) + FREE(&class->event); + + FREE(&class->stat); + + FREE(&class->table); + FREE(&class->sort); + + class->ready = FALSE; + class->loaded = FALSE; +} + +#define SWAP_FIELD(_v, _s, _d, _f) (_v = _s->_f, _s->_f = _d->_f, _d->_f = _v) + +static void class_replace_global(CLASS *class) +{ + CLASS_DESC_SYMBOL *cds; + CLASS *parent; + const char *name; + char *old_name; + CLASS *old_class; + int len; + CLASS swap; + char *swap_name; + bool swap_free_name; + int nprefix; + int i; + + if (!CLASS_is_loaded(class)) + return; + + name = class->name; + len = strlen(name); + + parent = class->parent; + if (parent) + { + for (nprefix = 0;; nprefix++) + { + if (parent->name[nprefix] != '^') + break; + } + nprefix++; + } + else + nprefix = 1; + + ALLOC(&old_name, len + nprefix + 1); + for (i = 0; i < nprefix; i++) + old_name[i] = '^'; + strcpy(&old_name[i], name); + + old_class = CLASS_find_global(old_name); + + FREE(&old_name); + + swap = *class; + *class = *old_class; + *old_class = swap; + + SWAP_FIELD(swap_name, class, old_class, name); + SWAP_FIELD(swap_free_name, class, old_class, free_name); + SWAP_FIELD(parent, class, old_class, next); + SWAP_FIELD(i, class, old_class, count); + SWAP_FIELD(i, class, old_class, ref); + + for (i = 0; i < old_class->n_desc; i++) + { + cds = &old_class->table[i]; + if (cds->desc && cds->desc->method.class == class) + cds->desc->method.class = old_class; + } + + CLASS_inheritance(class, old_class); +} + +static void release_class(CLASS *class) +{ + #if DEBUG_LOAD + fprintf(stderr, "release_class: %s\n", class->name); + #endif + OBJECT_release(class, NULL); + class->exit = TRUE; +} + +void CLASS_clean_up(bool silent) +{ + int n, nc, nb; + //CLASS_SYMBOL *csym; + CLASS *class; + + #if DEBUG_LOAD + fprintf(stderr, "\n------------------- CLASS_clean_up -------------------\n\n"); + #endif + + #if DEBUG_LOAD + fprintf(stderr, "Freeing auto-creatable objects...\n"); + #endif + + CLASS_exiting = TRUE; + + // Free automatic instances + + for (class = _classes; class; class = class->next) + { + if (class->instance) + { + #if DEBUG_LOAD + fprintf(stderr, "Freeing instance of %p %s\n", class, class->name); + #endif + OBJECT_UNREF(class->instance); + } + } + + // Count how many classes should be freed + + nc = 0; + + for (class = _classes; class; class = class->next) + { + if (!CLASS_is_native(class) && CLASS_is_loaded(class)) + { + #if DEBUG_LOAD + fprintf(stderr, "Must free: %p (%s) %s [%d]\n", class, class->component ? class->component->name : "-", class->name, class->count); + #endif + nc++; + } + } + + #if DEBUG_LOAD + fprintf(stderr, "Must free %d classes...\n", nc); + #endif + + #if DEBUG_LOAD + fprintf(stderr, "Calling _exit on loaded classes...\n"); + #endif + + for (class = _classes; class; class = class->next) + exit_class(class, FALSE); + + #if DEBUG_LOAD + fprintf(stderr, "Freeing classes with no instance...\n"); + #endif + + // Free classes having no instance + + n = 0; + for(;;) + { + nb = n; + + for (class = _classes; class; class = class->next) + { + if (class->count == 0 && !CLASS_is_native(class) && CLASS_is_loaded(class) && !class->exit) + { + release_class(class); + n++; + } + } + + if (n == nb) // nothing could be done + break; + } + + #if DEBUG_LOAD + fprintf(stderr, "Freeing other classes...\n"); + #endif + + // Remaining objects are circular references + // Everything is forced to be freed + + if (n < nc) + { + if (!silent) + ERROR_warning("circular references detected:"); + + for(;;) + { + nb = n; + + for (class = _classes; class; class = class->next) + { + if (!CLASS_is_native(class) && CLASS_is_loaded(class) && !class->exit && (!class->array_class || class->array_class->count == 0)) // && !class->astruct_class) + { + if (!silent) + fprintf(stderr, "gbx" GAMBAS_VERSION_STRING ": % 5d %s\n", class->count, class->name); + release_class(class); + n++; + } + } + + if (n == nb) // nothing could be done + break; + } + } + + + #if DEBUG_LOAD + fprintf(stderr, "Calling _exit on native classes...\n"); + #endif + + for (class = _classes; class; class = class->next) + exit_class(class, TRUE); +} + + +void CLASS_exit() +{ + CLASS *class, *next; + + #if DEBUG_LOAD + fprintf(stderr, "Unloading classes...\n"); + #endif + + for (class = _classes; class; class = class->next) + unload_class(class); + + #if DEBUG_LOAD + fprintf(stderr, "Destroying classes...\n"); + #endif + + class = _classes; + while (class) + { + next = class->next; + FREE(&class); + class = next; + } + + TABLE_delete_static(&_global_table); +} + + +CLASS *CLASS_look(const char *name, int len) +{ + CLASS_SYMBOL *csym; + ARCHIVE *arch = NULL; + int index; + + #if DEBUG_COMP + fprintf(stderr, "CLASS_look: %s in %s", name, _global ? "global" : "local"); + #endif + + //if (CP && CP->component && CP->component->archive) + if (!_global && !ARCHIVE_get_current(&arch)) + { + if (TABLE_find_symbol(arch->classes, name, len, &index)) + { + #if DEBUG_COMP + fprintf(stderr, " -> in %s\n", arch->name ? arch->name : "main"); + #endif + csym = (CLASS_SYMBOL *)TABLE_get_symbol(arch->classes, index); + return csym->class; + } + } + + if (TABLE_find_symbol(&_global_table, name, len, &index)) + { + #if DEBUG_COMP + fprintf(stderr, " -> %d in global\n", index); + #endif + csym = (CLASS_SYMBOL *)TABLE_get_symbol(&_global_table, index); + return csym->class; + } + else + { + #if DEBUG_COMP + fprintf(stderr, " -> ?\n"); + #endif + return NULL; + } +} + +CLASS *CLASS_find(const char *name) +{ + CLASS_SYMBOL *csym; + CLASS *class; + int index; + int len; + ARCHIVE *arch = NULL; + bool global; + + if (name == NULL) + name = COMMON_buffer; + + len = strlen(name); + +#if DEBUG_LOAD + fprintf(stderr, "CLASS_find: %s (%d)\n", name, _global); +#endif + + class = CLASS_look(name, len); + if (class) + return class; + + //if (CP && CP->component && CP->component->archive) + if (!_global && !ARCHIVE_get_current(&arch)) + { + global = FALSE; + TABLE_add_symbol(arch->classes, name, len, &index); + #if DEBUG_LOAD || DEBUG_COMP + fprintf(stderr, "Not found -> creating new one in %s\n", arch->name ? arch->name : "main"); + #endif + csym = (CLASS_SYMBOL *)TABLE_get_symbol(arch->classes, index); + } + else + { + global = TRUE; + TABLE_add_symbol(&_global_table, name, len, &index); + #if DEBUG_LOAD || DEBUG_COMP + fprintf(stderr, "Not found -> creating new one in global\n"); + #endif + csym = (CLASS_SYMBOL *)TABLE_get_symbol(&_global_table, index); + } + + ALLOC_ZERO(&class, sizeof(CLASS)); + csym->class = class; + class->ref = 1; + + class->next = _classes; + _classes = class; + + ALLOC(&class->name, len + 1); + strcpy((char *)class->name, name); + + csym->sym.name = class->name; + + class->free_name = TRUE; + + // The first class must be the Class class! + if (_first == NULL) + _first = class; + class->class = _first; + + class->global = global; + + if (arch) + class->component = arch->current_component; + + return class; +} + +int CLASS_count(void) +{ + return TABLE_count(&_global_table); +} + + +CLASS *CLASS_get(const char *name) +{ + CLASS *class = CLASS_find(name); + + if (!CLASS_is_loaded(class) && !class->in_load) + CLASS_load(class); + + return class; +} + + +CLASS *CLASS_look_global(const char *name, int len) +{ + CLASS *class; + + _global = TRUE; + class = CLASS_look(name, len); + _global = FALSE; + + return class; +} + + +CLASS *CLASS_find_global(const char *name) +{ + CLASS *class; + + _global = TRUE; + class = CLASS_find(name); + _global = FALSE; + + return class; +} + + +bool CLASS_inherits(CLASS *class, CLASS *parent) +{ + for(;;) + { + class = class->parent; + + if (class == NULL) + return FALSE; + + if (class == parent) + return TRUE; + } +} + + +int CLASS_find_symbol_with_prefix(CLASS *class, const char *name, const char *prefix) +{ + return SYMBOL_find(class->table, class->sort, class->n_desc, sizeof(CLASS_DESC_SYMBOL), TF_IGNORE_CASE, name, strlen(name), prefix); +} + + +CLASS_DESC_SYMBOL *CLASS_get_symbol(CLASS *class, const char *name) +{ + int index; + + index = CLASS_find_symbol(class, name); + if (index == NO_SYMBOL) + return NULL; + else + return &class->table[index]; +} + + +CLASS_DESC *CLASS_get_symbol_desc(CLASS *class, const char *name) +{ + CLASS_DESC_SYMBOL *cds = CLASS_get_symbol(class, name); + + if (cds == NULL) + return NULL; + else + return cds->desc; +} + + +short CLASS_get_symbol_index_kind(CLASS *class, const char *name, int kind, int kind2) +{ + CLASS_DESC *desc; + int index; + + index = CLASS_find_symbol(class, name); + if (index != NO_SYMBOL) + { + desc = CLASS_get_desc(class, index); + if (desc) + if ((CLASS_DESC_get_type(desc) == kind) || (CLASS_DESC_get_type(desc) == kind2)) + return (short)index; + } + + return (short)NO_SYMBOL; +} + + +CLASS_DESC *CLASS_get_symbol_desc_kind(CLASS *class, const char *name, int kind, int kind2) +{ + short index; + + index = CLASS_get_symbol_index_kind(class, name, kind, kind2); + if (index == NO_SYMBOL) + return NULL; + else + return CLASS_get_desc(class, index); +} + + +CLASS_DESC_EVENT *CLASS_get_event_desc(CLASS *class, const char *name) +{ + int index; + CLASS_DESC *cd; + + index = CLASS_find_symbol_with_prefix(class, name, ":"); + if (index == NO_SYMBOL) + return NULL; + + cd = class->table[index].desc; + + if (CLASS_DESC_get_type(cd) != CD_EVENT) + return NULL; + + return &cd->event; +} + + +char *CLASS_DESC_get_signature(CLASS_DESC *cd) +{ + char *res = NULL; + TYPE *sign; + int i, n; + TYPE type; + + switch (CLASS_DESC_get_type(cd)) + { + case CD_METHOD: + case CD_STATIC_METHOD: + + sign = cd->method.signature; + n = cd->method.npmax; + break; + + case CD_EVENT: + + sign = cd->event.signature; + n = cd->event.npmax; + break; + + case CD_EXTERN: + + sign = cd->ext.signature; + n = cd->ext.npmax; + break; + + default: + + return NULL; + } + + for (i = 0; i < n; i++) + { + type = sign[i]; + res = STRING_add(res, TYPE_to_string(type), 0); + if (TYPE_is_object(type)) + res = STRING_add_char(res, ';'); + } + + return res; +} + +// NOTE: The _free method can be called during a conversion, so we must save the EXEC structure + +static void error_CLASS_free(void *object, EXEC_GLOBAL *save) +{ + EXEC = *save; + ((OBJECT *)object)->ref = 0; + OBJECT_release(OBJECT_class(object), object); +} + +void CLASS_free(void *object) +{ + CLASS *class = OBJECT_class(object); + EXEC_GLOBAL save; + + if (!class->has_free) + { + OBJECT_release(class, object); + return; + } + + save = EXEC; + + ON_ERROR_2(error_CLASS_free, object, &save) + { + ((OBJECT *)object)->ref = 1; // Prevents anybody from freeing the object! + EXEC_special_inheritance(SPEC_FREE, class, object, 0, TRUE); + if (((OBJECT *)object)->ref != 1) + THROW(E_FREEREF); + error_CLASS_free(object, &save); + } + END_ERROR +} + +#if DEBUG_REF +void CLASS_ref(void *object) +{ + char *name; + + ((OBJECT *)object)->ref++; + + if (OBJECT_class(object) == FREE_MARK) + name = "*ALREADY FREED*"; + else + name = OBJECT_class(object)->name; + + #if DEBUG_MEMORY + fprintf(stderr, "%s: %s: ref(%s <%d>) -> %ld\n", OBJECT_ref_where, + DEBUG_get_current_position(), + name, GET_ALLOC_ID(object), ((OBJECT *)object)->ref); + #else + fprintf(stderr, "%s: %s: ref(%s %p) -> %ld\n", OBJECT_ref_where, + DEBUG_get_current_position(), + name, object, ((OBJECT *)object)->ref); + #endif + fflush(stdout); +} + +bool CLASS_unref(void *ob, bool can_free) +{ + char *name; + + OBJECT *object = (OBJECT *)ob; + + if (OBJECT_class(object) == FREE_MARK) + name = "*ALREADY FREED*"; + else + name = OBJECT_class(object)->name; + + #if DEBUG_MEMORY + if (object->ref <= 0) + fprintf(stderr, "*** <%d> REF = %ld !\n", GET_ALLOC_ID(object), object->ref); + + fprintf(stderr, "%s: %s: unref(%s <%d>) -> %ld\n", OBJECT_ref_where, + DEBUG_get_current_position(), + name, GET_ALLOC_ID(object), object->ref - 1); + #else + if (object->ref <= 0) + fprintf(stderr, "*** %p REF = %ld !\n", object, object->ref); + + fprintf(stderr, "%s, %s: unref(%s %p) -> %ld\n", OBJECT_ref_where, + DEBUG_get_current_position(), + name, object, object->ref - 1); + #endif + fflush(stdout); + + /*if (strcmp(OBJECT_class(object)->name, "Class") == 0) + fprintf(stderr, "Class ?\n");*/ + + if ((--(object->ref) <= 0) && can_free) + { + #if DEBUG_MEMORY + fprintf(stderr, "FREE <%d> !\n", GET_ALLOC_ID(object)); + #else + fprintf(stderr, "FREE %p !\n", object); + #endif + CLASS_free(object); + return TRUE; + } + + return FALSE; +} +#endif + +void CLASS_do_nothing() +{ +} + +int CLASS_return_zero() +{ + return 0; +} + +static CLASS *_sorted_class; + +static int partition(CLASS_DESC_SYMBOL *cds, ushort *sym, const int start, const int end) +{ + int pos = start; + short pivot = sym[start]; + int i; + ushort val; + int len; + const char *s1, *s2; + int result; + + for (i = start + 1; i <= end; i++) + { + len = cds[sym[i]].len; + result = len - cds[pivot].len; + + if (result > 0) + continue; + + if (result == 0) + { +#if TABLE_USE_KEY + result = cds[sym[i]].key - cds[pivot].key; + if (result > 0) + continue; + else if (result == 0) +#endif + { + s1 = cds[sym[i]].name; + s2 = cds[pivot].name; + + while (len) + { + result = tolower(*s1++) - tolower(*s2++); + if (result) + break; + len--; + } + + if (result == 0) + { + if (*cds[pivot].name != '.') + ERROR_panic("Symbol '%s' declared twice in class '%s'\n", cds[sym[i]].name, _sorted_class->name); + } + if (result >= 0) + continue; + } + } + + pos++; // incrémente compteur cad la place finale du pivot + //echanger(tableau, compteur, i); // élément positionné + val = sym[pos]; + sym[pos] = sym[i]; + sym[i] = val; + } + + //echanger(tableau, compteur, debut); // le pivot est placé + val = sym[pos]; + sym[pos] = sym[start]; + sym[start] = val; + + return pos; // et sa position est retournée +} + +static void my_qsort(CLASS_DESC_SYMBOL *cds, ushort *sym, const int start, const int end) +{ + if (start < end) // cas d'arrêt pour la récursivité + { + int pivot = partition(cds, sym, start, end); // division du tableau + my_qsort(cds, sym, start, pivot - 1); // trie partie1 + my_qsort(cds, sym, pivot + 1, end); // trie partie2 + } +} + +void CLASS_sort(CLASS *class) +{ + ushort *sym; + ushort i; + + if (!class->n_desc) + return; + + SYMBOL_compute_keys(class->table, class->n_desc, sizeof(CLASS_DESC_SYMBOL), TF_IGNORE_CASE); + + _sorted_class = class; + + ALLOC(&sym, sizeof(ushort) * class->n_desc); + + for (i = 0; i < class->n_desc; i++) + sym[i] = i; + + //qsort(sym, class->n_desc, sizeof(ushort), (int (*)(const void *, const void *))sort_desc); + my_qsort(class->table, sym, 0, class->n_desc - 1); + + class->sort = sym; + + #if DEBUG_DESC + { + SYMBOL *s; + + fprintf(stderr, "\nSORT %s\n", class->name); + + for (i = 0; i < class->n_desc; i++) + { + s = (SYMBOL *)&class->table[sym[i]]; + + fprintf(stderr, "[%d] %.*s (%d)\n", i, (int)s->len, s->name, sym[i]); + } + fputc('\n', stderr); + } + #endif +} + +void CLASS_inheritance(CLASS *class, CLASS *parent) +{ + if (class->parent != NULL) + THROW_CLASS(class, "Multiple inheritance", ""); + + class->parent = parent; + parent->has_child = TRUE; + + TRY + { + CLASS_load(class->parent); + } + CATCH + { + THROW_CLASS(class, "Cannot load parent class: ", STRING_new_temp_zero(ERROR_current->info.msg)); + } + END_TRY + + if (!class->check) + { + class->check = class->parent->check; + class->must_check = class->parent->must_check; + } + + if (!class->has_free) + class->has_free = class->parent->has_free; + + // CREATE STATIC is inherited, but not CREATE PRIVATE + + if (parent->auto_create) + class->auto_create = TRUE; + + if (!class->array_type) + class->array_type = parent->array_type; + + //fprintf(stderr, "CLASS_inheritance: %s %s\n", class->name, class->auto_create ? "AUTO CREATE" : ""); +} + + + +const char *CLASS_DESC_get_type_name(const CLASS_DESC *desc) +{ + switch (desc->gambas.val3._int[1]) + { + case CD_PROPERTY_ID: return desc->gambas.val2 < 0 ? "r" : "p"; + case CD_VARIABLE_ID: return "v"; + case CD_METHOD_ID: return "m"; + case CD_STATIC_PROPERTY_ID: return desc->gambas.val2 < 0 ? "R" : "P"; + case CD_STATIC_VARIABLE_ID: return "V"; + case CD_STATIC_METHOD_ID: return "M"; + case CD_CONSTANT_ID: return "C"; + case CD_EVENT_ID: return ":"; + case CD_EXTERN_ID: return "X"; + default: + //fprintf(stderr, "CLASS_DESC_get_type_name: %s: %ld\n", desc->gambas.name, desc->gambas.val4); + return "?"; + } +} + +static bool check_signature(char type, const CLASS_DESC *desc, const CLASS_DESC *pdesc) +{ + TYPE *sd, *sp; + int nsd, nsp; + + if (!TYPE_are_compatible(desc->property.type, pdesc->property.type)) + return TRUE; + + switch (type) + { + case CD_METHOD: + case CD_STATIC_METHOD: + + sd = desc->method.signature; + nsd = desc->method.npmax; + sp = pdesc->method.signature; + nsp = pdesc->method.npmax; + break; + + case CD_EVENT: + + sd = desc->event.signature; + nsd = desc->event.npmax; + sp = pdesc->event.signature; + nsp = pdesc->event.npmax; + break; + + case CD_EXTERN: + + sd = desc->ext.signature; + nsd = desc->ext.npmax; + sp = pdesc->ext.signature; + nsp = pdesc->ext.npmax; + break; + + case CD_VARIABLE: + case CD_STATIC_VARIABLE: + + return TRUE; + + default: + + return FALSE; + } + + return TYPE_compare_signature(sd, nsd, sp, nsp, TRUE); +} + +void CLASS_make_description(CLASS *class, const CLASS_DESC *desc, int n_desc, int *first) +{ + static const char *nonher[] = { "_new", "_free", "_init", "_exit", NULL }; + + int ind; + int i, j; + const char *name; + const char **pnonher; + CLASS *parent; + char type, parent_type; + CLASS_DESC_SYMBOL *cds; + bool check; + + #if DEBUG_DESC + fprintf(stderr, "\n---- %s\n", class->name); + #endif + + // Compute number of public descriptions + + class->n_desc = n_desc; + if (class->parent) + class->n_desc += class->parent->n_desc; + + // Make the description symbol table + + if (class->n_desc) + ALLOC(&class->table, sizeof(CLASS_DESC_SYMBOL) * class->n_desc); + + i = 0; + + if (class->parent && class->parent->n_desc) + { + for (j = 0; j < class->parent->n_desc; j++) + { + class->table[i] = class->parent->table[j]; + i++; + } + + for (j = 0; j < n_desc; j++) + { + if (CLASS_is_native(class)) + { + name = &(desc[j].gambas.name[1]); + type = CLASS_DESC_get_type(&desc[j]); + //ptype = (const char*)desc[j].gambas.type; + //fprintf(stderr, "%s -> ", ptype); + //desc[j].gambas.type = TYPE_from_string(&ptype); + //fprintf(stderr, "%p\n", (void *)desc[j].gambas.type); + } + else + { + name = desc[j].gambas.name; + type = *CLASS_DESC_get_type_name(&desc[j]); + } + + //fprintf(stderr, "%s.%s\n", class->name, name); + + // An inherited symbol has two or more entries in the table. Only the first one + // will be used, and so it must point at the new description, not the inherited one. + check = FALSE; + parent = class; + while ((parent = parent->parent)) + { + ind = CLASS_find_symbol(parent, name); + if (ind == NO_SYMBOL) + continue; + + cds = &parent->table[ind]; + + // The parent class public symbols of non-native classes were replaced by the symbol kind returned by CLASS_DESC_get_type_name() + // Only the first inheritance level is tested against signature compatibility + + if (cds->desc && !check) + { + parent_type = CLASS_DESC_get_type(cds->desc); + + if (parent_type != type) + { + #if DEBUG_DESC + fprintf(stderr, "type = '%c' parent_type = '%c'\n", type, parent_type); + #endif + THROW(E_OVERRIDE, CLASS_get_name(parent), cds->name, CLASS_get_name(class)); + } + + if (!CLASS_is_native(class) && strcasecmp(name, "_new")) + { + //fprintf(stderr, "check_signature: %s\n", name); + if (check_signature(type, &desc[j], cds->desc)) + THROW(E_OVERRIDE, CLASS_get_name(parent), cds->name, CLASS_get_name(class)); + } + + check = TRUE; + } + + cds = &class->table[ind]; + + #if DEBUG_DESC + fprintf(stderr, "%s: [%d] (%p %ld) := (%p %ld)\n", name, ind, cds->desc, cds->desc ? cds->desc->gambas.val1 : 0, &desc[j], desc[j].gambas.val1); + #endif + + cds->desc = (CLASS_DESC *)&desc[j]; + cds->name = "."; + cds->len = 1; + + /*if (!desc[j].gambas.val1 && index(CD_CALL_SOMETHING_LIST, type) != NULL) + { + //#if DEBUG_DESC + fprintf(stderr, "CLASS_make_description: '%s.%s' gambas.val1: %ld -> %ld\n", class->name, desc[j].gambas.name, desc[j].gambas.val1, class->parent->table[ind].desc->gambas.val1); + //#endif + ((CLASS_DESC *)desc)[j].gambas.val1 = class->parent->table[ind].desc->gambas.val1; + }*/ + } + } + + for (pnonher = nonher; *pnonher; pnonher++) + { + ind = CLASS_find_symbol(class->parent, *pnonher); + if (ind != NO_SYMBOL) + { + cds = &class->table[ind]; + cds->desc = NULL; + cds->name = "."; + cds->len = 1; + } + } + } + + *first = i; + + for (j = 0; j < n_desc; j++, i++) + { + class->table[i].desc = (CLASS_DESC *)&desc[j]; + name = desc[j].gambas.name; + + /* On saute le caractère de type de symbole */ + if (CLASS_is_native(class)) + { + name++; + if (*name == '!') + name++; + } + + class->table[i].name = (char *)name; + class->table[i].len = strlen(name); + } + + #if DEBUG_DESC + { + CLASS_DESC_SYMBOL *cds; + + for (i = 0; i < class->n_desc; i++) + { + cds = &class->table[i]; + fprintf(stderr, "%d: %.*s %p\n", i, cds->len, cds->name, cds->desc); + } + } + #endif +} + +/* 'all' means that size_dynamic is the entire size of the object, not just the size of the data */ +/* And check if the class is a stream */ + +void CLASS_calc_info(CLASS *class, int n_event, int size_dynamic, bool all, int size_static) +{ + // If the class is native and static, then size_dynamic == 0. But if we want to inherit + // the static class, and make the inherited class dynamic, then class->off_event must + // start after the object header. So we fix size_dynamic accordingly. + + if (all && size_dynamic == 0) + size_dynamic = sizeof(OBJECT); + + if (class->parent) + { + if (all) + class->off_event = size_dynamic; + else + class->off_event = class->parent->off_event + size_dynamic; + + class->n_event = class->parent->n_event + n_event; + } + else + { + if (all) + class->off_event = size_dynamic; + else + class->off_event = sizeof(OBJECT) + size_dynamic; + + class->n_event = n_event; + } + + if (class->n_event) + class->size = class->off_event + sizeof(OBJECT_EVENT) + class->n_event * sizeof(ushort); + else + class->size = class->off_event; + + class->size_stat = size_static; + if (size_static) + ALLOC_ZERO(&class->stat, class->size_stat); + else + class->stat = NULL; + + class->is_stream = (class == CLASS_Stream) || (class->parent && class->parent->is_stream); + + class->is_simple = class->parent == NULL && !class->is_virtual && !class->must_check; + if (class->parent) + class->parent->is_simple = FALSE; +} + + +void CLASS_make_event(CLASS *class, int *first) +{ + int i, ev; + + *first = 0; + + if (class->n_event == 0) + return; + + if (!CLASS_is_native(class)) + { + if (class->parent == NULL) + { + class->event = class->load->event; + class->free_event = FALSE; + return; + } + } + + ALLOC_ZERO(&class->event, sizeof(CLASS_EVENT) * class->n_event); + + if (class->parent != NULL) + { + ev = class->parent->n_event; + + for (i = 0; i < ev; i++) + class->event[i] = class->parent->event[i]; + } + else + ev = 0; + + class->free_event = TRUE; + + *first = ev; +} + + +int CLASS_get_inheritance(CLASS *class, CLASS **her) +{ + int nher = 0; + + while ((nher < MAX_INHERITANCE) && (class != NULL)) + { + *her++ = class; + nher++; + class = class->parent; + } + + return nher; +} + +void *CLASS_auto_create(CLASS *class, int nparam) +{ + void *ob = class->instance; + + //fprintf(stderr, ">>> CLASS_auto_create: %s (%p)\n", class->name, ob); + + // We automatically release invalid automatic instances + + if (ob) + { + if (OBJECT_is_valid(ob)) + { + RELEASE_MANY(SP, nparam); + //fprintf(stderr, "<<< CLASS_auto_create: %s (%p): valid=1\n", class->name, ob); + return ob; + } + + OBJECT_UNREF(class->instance); + class->instance = NULL; + } + + /*fprintf(stderr, "CLASS_auto_create: create %s\n", class->name);*/ + + OBJECT_create_and_set(&class->instance, class, NULL, NULL, nparam); + //ob = class->instance; + //OBJECT_REF(ob, "CLASS_auto_create"); + + //fprintf(stderr, "<<< CLASS_auto_create: %s (%p) valid=%d\n", class->name, ob, OBJECT_is_valid(ob)); + + return class->instance; +} + +//#define SET_OPTIONAL_OPERATOR(_class, _op, _func) if (!CLASS_has_operator(_class, _op)) (_class)->operators[_op] = (EXEC_no_operator##_func) + +void CLASS_search_special(CLASS *class) +{ + static int _operator_strength = 0; + int sym; + + class->special[SPEC_NEW] = CLASS_get_symbol_index_kind(class, "_new", CD_METHOD, 0); + class->special[SPEC_FREE] = CLASS_get_symbol_index_kind(class, "_free", CD_METHOD, 0); + class->special[SPEC_GET] = CLASS_get_symbol_index_kind(class, "_get", CD_METHOD, CD_STATIC_METHOD); + class->special[SPEC_PUT] = CLASS_get_symbol_index_kind(class, "_put", CD_METHOD, CD_STATIC_METHOD); + class->special[SPEC_FIRST] = CLASS_get_symbol_index_kind(class, "_first", CD_METHOD, CD_STATIC_METHOD); + class->special[SPEC_NEXT] = CLASS_get_symbol_index_kind(class, "_next", CD_METHOD, CD_STATIC_METHOD); + class->special[SPEC_CALL] = CLASS_get_symbol_index_kind(class, "_call", CD_METHOD, CD_STATIC_METHOD); + class->special[SPEC_UNKNOWN] = CLASS_get_symbol_index_kind(class, "_unknown", CD_METHOD, CD_STATIC_METHOD); + class->special[SPEC_PROPERTY] = CLASS_get_symbol_index_kind(class, "_property", CD_METHOD, CD_STATIC_METHOD); + class->special[SPEC_COMPARE] = CLASS_get_symbol_index_kind(class, "_compare", CD_METHOD, 0); + class->special[SPEC_ATTACH] = CLASS_get_symbol_index_kind(class, "_attach", CD_METHOD, 0); + class->special[SPEC_READY] = CLASS_get_symbol_index_kind(class, "_ready", CD_METHOD, 0); + + sym = CLASS_get_symbol_index_kind(class, "_@_convert", CD_CONSTANT, 0); + if (sym != NO_SYMBOL) + { + class->has_convert = TRUE; + class->convert = CLASS_get_desc(class, sym)->constant.value._pointer; + } + + sym = CLASS_get_symbol_index_kind(class, "_@_operator", CD_CONSTANT, 0); + if (sym != NO_SYMBOL) + { + class->has_operators = TRUE; + class->operators = CLASS_get_desc(class, sym)->constant.value._pointer; + _operator_strength++; + CLASS_set_operator_strength(class, _operator_strength); + } + + if (class->special[SPEC_NEXT] != NO_SYMBOL) + class->enum_static = CLASS_DESC_get_type(CLASS_get_desc(class, class->special[SPEC_NEXT])) == CD_STATIC_METHOD; + if (class->special[SPEC_UNKNOWN] != NO_SYMBOL) + class->unknown_static = CLASS_DESC_get_type(CLASS_get_desc(class, class->special[SPEC_UNKNOWN])) == CD_STATIC_METHOD; + if (class->special[SPEC_PROPERTY] != NO_SYMBOL) + class->property_static = CLASS_DESC_get_type(CLASS_get_desc(class, class->special[SPEC_PROPERTY])) == CD_STATIC_METHOD; + if (class->special[SPEC_FREE] != NO_SYMBOL) + class->has_free = TRUE; +} + + +CLASS *CLASS_check_global(CLASS *class) +{ + if (CLASS_is_loaded(class)) + { + if (COMPONENT_current && class->component == COMPONENT_current) + ERROR_panic("Class '%s' declared twice in the component '%s'.", CLASS_get_name(class), class->component->name); + + /*if (class->has_child) + THROW(E_CLASS, class->name, "Overriding an already inherited class is forbidden", "");*/ + + class_replace_global(class); + } + + return class; +} + + +CLASS_DESC_METHOD *CLASS_get_special_desc(CLASS *class, int spec) +{ + short index = class->special[spec]; + + if (index == NO_SYMBOL) + return NULL; + else + return &CLASS_get_desc(class, index)->method; +} + + +CLASS_DESC_SYMBOL *CLASS_get_next_sorted_symbol(CLASS *class, int *index) +{ + CLASS_DESC_SYMBOL *old = NULL; + CLASS_DESC_SYMBOL *cur; + + for(;;) + { + if (*index >= class->n_desc) + return NULL; + + cur = &class->table[class->sort[*index]]; + if (*index > 0) + old = &class->table[class->sort[*index - 1]]; + + (*index)++; + + if (!cur->desc) + continue; + + if (old && !TABLE_compare_ignore_case(cur->name, cur->len, old->name, old->len)) + continue; + + return cur; + } +} + + +void CLASS_create_array_class(CLASS *class) +{ + void *save = TYPE_joker; + char *name_joker; + GB_DESC *desc; + CLASS *array_type = (CLASS *)class->array_type; + + name_joker = STRING_new(class->name, strlen(class->name) - 2); + + if (!array_type) + { + array_type = class->global ? CLASS_find_global(name_joker) : CLASS_find(name_joker); + class->array_type = (TYPE)array_type; + } + + TYPE_joker = array_type; + array_type->array_class = class; + + ALLOC(&desc, sizeof(GB_DESC) * ARRAY_TEMPLATE_NDESC); + memcpy(desc, NATIVE_TemplateArray, sizeof(GB_DESC) * ARRAY_TEMPLATE_NDESC); + ((CLASS_DESC_GAMBAS *)desc)->name = class->name; + + CLASS_register_class(desc, class); + + class->is_array = TRUE; + class->quick_array = CQA_ARRAY; + class->data = (char *)desc; + + STRING_free(&name_joker); + TYPE_joker = save; +} + + +CLASS *CLASS_get_array_class(CLASS *class) +{ + if (!class->array_class) + { + char name[strlen(class->name) + 3]; + strcpy(name, class->name); + strcat(name, "[]"); + class->array_class = class->global ? CLASS_find_global(name) : CLASS_find(name); + CLASS_create_array_class(class->array_class); + } + + return class->array_class; +} + + +void CLASS_create_array_of_struct_class(CLASS *class) +{ + void *save = TYPE_joker; + char *name_joker; + GB_DESC *desc; + CLASS *array_type = (CLASS *)class->array_type; + + //fprintf(stderr, "CLASS_create_array_class: create %s\n", class->name); + + name_joker = STRING_new(&class->name[1], strlen(class->name) - 3); + + if (!array_type) + { + array_type = class->global ? CLASS_find_global(name_joker) : CLASS_find(name_joker); + class->array_type = (TYPE)array_type; + } + + TYPE_joker = array_type; + array_type->astruct_class = class; + + ALLOC(&desc, sizeof(GB_DESC) * ARRAY_OF_STRUCT_TEMPLATE_NDESC); + memcpy(desc, NATIVE_TemplateArrayOfStruct, sizeof(GB_DESC) * ARRAY_OF_STRUCT_TEMPLATE_NDESC); + ((CLASS_DESC_GAMBAS *)desc)->name = class->name; + + CLASS_register_class(desc, class); + + class->is_array = TRUE; + class->is_array_of_struct = TRUE; + class->data = (char *)desc; + + STRING_free(&name_joker); + TYPE_joker = save; +} + + +CLASS *CLASS_get_array_of_struct_class(CLASS *class) +{ + if (!class->astruct_class) + { + char name[strlen(class->name) + 4]; + sprintf(name, "$%s[]", class->name); + class->astruct_class = class->global ? CLASS_find_global(name) : CLASS_find(name); + CLASS_create_array_of_struct_class(class->astruct_class); + } + + return class->astruct_class; +} + +int CLASS_sizeof(CLASS *class) +{ + if (CLASS_is_struct(class)) + return class->size - sizeof(CSTRUCT); + else + return class->size - sizeof(OBJECT); +} + +char *CLASS_get_name(CLASS *class) +{ + char *name = class->name; + + while (*name == '^') + name++; + + return name; +} + + +CLASS *CLASS_find_load_from(const char *name, const char *from) +{ + COMPONENT *save; + CLASS *save_cp; + COMPONENT *comp = NULL; + CLASS *class; + + comp = COMPONENT_find(from); + + save = COMPONENT_current; + save_cp = CP; + + COMPONENT_current = comp; + CP = NULL; + class = CLASS_get(name); + + COMPONENT_current = save; + CP = save_cp; + + return class; +} + diff --git a/main/gbx/gbx_class.h b/main/gbx/gbx_class.h new file mode 100644 index 00000000..eaa93aa5 --- /dev/null +++ b/main/gbx/gbx_class.h @@ -0,0 +1,557 @@ +/*************************************************************************** + + gbx_class.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_CLASS_H +#define __GBX_CLASS_H + +#include "gb_error.h" +#include "gbx_type.h" +#include "gb_table.h" +#include "gbx_class_desc.h" +#include "gbx_component.h" + +#ifndef __GBX_CLASS_C +EXTERN bool CLASS_exiting; +#endif + +typedef + int CLASS_ID; + +typedef + struct { + CTYPE type; + int pos; + } + CLASS_VAR; + +typedef + struct _CLASS *CLASS_REF; + +typedef + char *CLASS_UNKNOWN; + +typedef + union { + int type; + struct { int type; double value; } PACKED _float; + struct { int type; float value; } PACKED _single; + struct { int type; int value; } PACKED _integer; + struct { int type; int64_t value; } PACKED _long; + struct { int type; char *addr; int len; } PACKED _string; + struct { int type; int val[2]; } PACKED _swap; + } + PACKED + CLASS_CONST; + +typedef + struct { + CTYPE type; + } + CLASS_LOCAL; + +typedef + struct { + TYPE type; + } + CLASS_PARAM; + +typedef + struct { + char *name; + int len; + } + PACKED + DEBUG_SYMBOL; + +typedef + struct { + DEBUG_SYMBOL sym; + int value; + } + LOCAL_SYMBOL; + +typedef + struct { + unsigned short line; + unsigned short nline; + unsigned short *pos; + char *name; + LOCAL_SYMBOL *local; + short n_local; + unsigned short index; // Function index in its class. Used by the profiler + } + PACKED + FUNC_DEBUG; + +typedef + struct { + TYPE type; + char n_param; + char npmin; + char vararg; + unsigned fast : 1; + unsigned unsafe : 1; + unsigned fast_linked : 1; + unsigned optional : 1; + unsigned use_is_missing : 1; + unsigned is_static : 1; + unsigned _reserved : 2; + short n_local; + short n_ctrl; + short stack_usage; + short error; + unsigned short *code; + CLASS_PARAM *param; + CLASS_LOCAL *local; + FUNC_DEBUG *debug; + } + PACKED + FUNCTION; + +typedef + struct { + TYPE type; + char n_param; + char npmin; + char vararg; + unsigned char flag; + } + PACKED + FUNCTION_FLAG; + +typedef + struct { + TYPE type; + short n_param; + short _reserved; + CLASS_PARAM *param; + char *name; + } + PACKED + CLASS_EVENT; + +typedef + struct { + TYPE type; + short n_param; + bool vararg; + unsigned loaded : 1; + unsigned _reserved : 7; + CLASS_PARAM *param; + char *alias; + char *library; + } + PACKED + CLASS_EXTERN; + +typedef + struct { + uint magic; + uint version; + uint endian; + uint flag; + } + PACKED + CLASS_HEADER; + +typedef + struct { + short parent; + short flag; + int s_static; + int s_dynamic; + short nstruct; + short _reserved; + } + PACKED + CLASS_INFO; + +typedef + struct { + CTYPE ctype; + int dim[0]; + } + CLASS_ARRAY; + +typedef + CLASS_ARRAY *CLASS_ARRAY_P; + +struct _CLASS; + +typedef + struct { + DEBUG_SYMBOL sym; + CTYPE ctype; + int value; + } + GLOBAL_SYMBOL; + +typedef + struct { + int nfield; + int *desc; + } + CLASS_STRUCT; + +typedef + struct { + short n_cst; + short n_stat; + short n_dyn; + short n_func; + + CLASS_CONST *cst; + CLASS_VAR *stat; + CLASS_VAR *dyn; + FUNCTION *func; + CLASS_EVENT *event; + CLASS_EXTERN *ext; + CLASS_ARRAY **array; + + CLASS_REF *class_ref; + char **unknown; + + GLOBAL_SYMBOL *global; + ushort *sort; + short n_global; + short n_ext; + uint *prof; // Shortcut indexes attributed during profiling. The first one is for the class, the other ones for the functions + #ifdef OS_64BITS + CLASS_DESC *desc; + CLASS_PARAM *local; + FUNC_DEBUG *debug; + #endif + } + PACKED + CLASS_LOAD; + +enum { + CO_EQUAL, CO_EQUALF, CO_EQUALO, + CO_COMP, CO_COMPF, CO_COMPO, + CO_ADD, CO_ADDF, CO_ADDO, + CO_SUB, CO_SUBF, CO_SUBO, + CO_MUL, CO_MULF, CO_MULO, + CO_DIV, CO_DIVF, CO_DIVO, + CO_POW, CO_POWF, CO_POWO, + CO_NEG, CO_ABS, CO_FABS, CO_SGN, + CO_STRENGTH +}; + +typedef + struct _CLASS { // 32b 64b + struct _CLASS *class; // 4 8 Points at the 'Class' class ! + int ref; // 8 12 Reference count + int count; // 12 16 Number of instanciated objects + struct _CLASS *parent; // 16 24 Inherited class + char *name; // 20 32 Class name + + unsigned loaded : 1; // Class is loaded + unsigned ready : 1; // Class is loaded and ready + unsigned debug : 1; // Debugging information ? + unsigned free_name : 1; // Must free the class name + unsigned free_event : 1; // Must free class->event + unsigned in_load : 1; // Class being loaded + unsigned exit : 1; // Marker used by CLASS_exit + unsigned auto_create : 1; // Automatically instanciated + + unsigned quick_array : 2; // Array accessor optimization type + unsigned no_create : 1; // Cannot instanciate this class + unsigned is_virtual : 1; // Virtual class (name beginning with a dot) + unsigned swap : 1; // Class endianness was swapped + unsigned enum_static : 1; // If class enumeration is static + unsigned is_stream : 1; // If the class inherits stream + unsigned global : 1; // If the class is in the global table + + unsigned is_native : 1; // If the class is native (i.e. written in C/C++) + unsigned error : 1; // Loading or registering the class has failed + unsigned is_observer : 1; // This is the Observer class + unsigned is_struct : 1; // This class is a structure + unsigned is_array : 1; // This class is an array + unsigned is_array_of_struct : 1; // This class is an array of struct + unsigned init_dynamic : 1; // If there is a special function to call at instanciation + unsigned must_check : 1; // The class has a check function + + unsigned has_child : 1; // The class has an inherited child class + unsigned unknown_static : 1; // If _unknown is static + unsigned property_static : 1; // If _property is static + unsigned has_convert : 1; // If the _convert interface is implemented + unsigned has_operators : 1; // If the _operators interface is implemented + unsigned is_simple : 1; // Class has no parent, no child, is not virtual, and has no 'check' function. + unsigned has_free : 1; // The class has a free function + unsigned _reserved : 1; // 24 36 + + short n_desc; // 26 38 number of descriptions + short n_event; // 28 40 number of events + + CLASS_DESC_SYMBOL *table; // 32 48 class description + ushort *sort; // 36 56 table sort + + CLASS_EVENT *event; // 40 64 event description + + int (*check)(); // 44 72 method for checking that an object is valid + + char *data; // 48 80 class file data for loaded class + // or generated description for native class + // or generated description for structures + + CLASS_LOAD *load; // 52 88 information about loaded class + + char *stat; // 56 96 static class data + TYPE *signature; // 60 104 signatures address + char *string; // 64 112 strings address + + uint size_stat; // 68 116 static class size + uint size; // 72 120 dynamic class size + uint off_event; // 76 124 offset of OBJECT_EVENT structure in the object + #ifdef OS_64BITS + uint _reserved2; // 128 + #endif + + short special[12]; // 100 152 special functions index (_new, _free, ...) + + TYPE array_type; // 104 160 datatype of the contents if this class is an array class of objects + struct _CLASS *array_class; // 108 168 array of class + struct _CLASS *astruct_class; // 112 176 array of struct class + + void *instance; // 116 184 automatically created instance + void **operators; // 120 192 arithmetic interface + bool (*convert)(); // 124 200 convert method + + COMPONENT *component; // 128 208 The component the class belongs to + + struct _CLASS *next; // 132 216 next class + } + CLASS; + +typedef + struct { + SYMBOL sym; + CLASS *class; + } + PACKED + CLASS_SYMBOL; + +typedef + enum { + SPEC_NEW, + SPEC_FREE, + SPEC_GET, + SPEC_PUT, + SPEC_FIRST, + SPEC_NEXT, + SPEC_CALL, + SPEC_UNKNOWN, + SPEC_PROPERTY, + SPEC_COMPARE, + SPEC_ATTACH, + SPEC_READY, + MAX_SPEC = 11 + } + CLASS_SPECIAL; + +typedef + enum { + CQA_NONE = 0, + CQA_ARRAY = 1, + CQA_COLLECTION = 2, + CQA_STRING = 3 + } + CLASS_QUICK_ARRAY; + +typedef + enum + { + CF_DEBUG = 1 + } + CLASS_FLAG; + +typedef + enum + { + CI_EXPORTED = 1, + CI_AUTOCREATE = 2, + CI_OPTIONAL = 4, + CI_NOCREATE = 8 + } + CLASS_INFO_FLAG; + +#ifndef __GBX_CLASS_INIT_C +EXTERN CLASS *CLASS_Class; +EXTERN CLASS *CLASS_Enum; +EXTERN CLASS *CLASS_Symbol; + +EXTERN CLASS *CLASS_Array; +EXTERN CLASS *CLASS_Collection; +EXTERN CLASS *CLASS_Stream; +EXTERN CLASS *CLASS_File; +EXTERN CLASS *CLASS_Stat; +EXTERN CLASS *CLASS_AppArgs; +EXTERN CLASS *CLASS_AppEnv; +EXTERN CLASS *CLASS_Application; +EXTERN CLASS *CLASS_Process; +EXTERN CLASS *CLASS_Component; +EXTERN CLASS *CLASS_Observer; +EXTERN CLASS *CLASS_Timer; +EXTERN CLASS *CLASS_BoxedString; + +EXTERN CLASS *CLASS_BooleanArray; +EXTERN CLASS *CLASS_ByteArray; +EXTERN CLASS *CLASS_ShortArray; +EXTERN CLASS *CLASS_IntegerArray; +EXTERN CLASS *CLASS_SingleArray; +EXTERN CLASS *CLASS_FloatArray; +EXTERN CLASS *CLASS_DateArray; +EXTERN CLASS *CLASS_StringArray; +EXTERN CLASS *CLASS_ObjectArray; +EXTERN CLASS *CLASS_VariantArray; +EXTERN CLASS *CLASS_LongArray; +EXTERN CLASS *CLASS_PointerArray; +#endif + + +#define DO_ERROR ((void (*)()) -1) + +#define CLASS_is_native(_class) ((_class)->is_native) +#define CLASS_is_struct(_class) ((_class)->is_struct) +#define CLASS_is_array(_class) ((_class)->is_array) + +#define FUNCTION_is_static(func) ((func)->type & TF_STATIC) +//#define FUNCTION_is_native(_desc) (((uintptr_t)(_desc)->exec >> 16) != 0) +#define FUNCTION_is_native(_desc) ((_desc)->native) + +#define FUNC_INIT_STATIC 0 +#define FUNC_INIT_DYNAMIC 1 + + +/* class.c */ + +void CLASS_init(void); +void CLASS_clean_up(bool silent); +void CLASS_exit(void); +CLASS *CLASS_get_list(void); + +CLASS *CLASS_get(const char *name); +int CLASS_count(void); + +char *CLASS_get_name(CLASS *class); + +CLASS_DESC_SYMBOL *CLASS_get_symbol(CLASS *class, const char *name); +CLASS_DESC *CLASS_get_symbol_desc(CLASS *class, const char *name); + +short CLASS_get_symbol_index_kind(CLASS *class, const char *name, int kind, int kind2); +CLASS_DESC *CLASS_get_symbol_desc_kind(CLASS *class, const char *name, int kind, int kind2); +CLASS_DESC_METHOD *CLASS_get_special_desc(CLASS *class, int spec); + +#define CLASS_get_desc(_class, _index) (((_class)->table[_index].desc)) + +CLASS_DESC_EVENT *CLASS_get_event_desc(CLASS *class, const char *name); + +#define CLASS_find_symbol(_class, _name) SYMBOL_find((_class)->table, (_class)->sort, (_class)->n_desc, sizeof(CLASS_DESC_SYMBOL), TF_IGNORE_CASE, (_name), strlen((_name)), NULL) + +int CLASS_find_symbol_with_prefix(CLASS *class, const char *name, const char *prefix); + +CLASS *CLASS_look(const char *name, int len); +CLASS *CLASS_find(const char *name); + +TABLE *CLASS_get_table(void); + +bool CLASS_inherits(CLASS *class, CLASS *parent); + +CLASS *CLASS_replace_global(const char *name); +CLASS *CLASS_look_global(const char *name, int len); +CLASS *CLASS_find_global(const char *name); +CLASS *CLASS_check_global(CLASS *class); + +void CLASS_ref(void *object); +bool CLASS_unref(void *object, bool can_free); +void CLASS_free(void *object); + +int CLASS_get_inheritance(CLASS *class, CLASS **her); + +void CLASS_do_nothing(); +int CLASS_return_zero(); + +void CLASS_sort(CLASS *class); + +void CLASS_inheritance(CLASS *class, CLASS *parent); +void CLASS_make_description(CLASS *class, const CLASS_DESC *desc, int n_desc, int *first); +void CLASS_make_event(CLASS *class, int *first); +void CLASS_calc_info(CLASS *class, int n_event, int size_dynamic, bool all, int size_static); + +void *CLASS_auto_create(CLASS *class, int nparam); + +void CLASS_search_special(CLASS *class); + +CLASS_DESC_SYMBOL *CLASS_get_next_sorted_symbol(CLASS *class, int *index); + +int CLASS_can_be_used_like_an_array(CLASS *class); + +void CLASS_create_array_class(CLASS *class); +CLASS *CLASS_get_array_class(CLASS *class); + +void CLASS_create_array_of_struct_class(CLASS *class); +CLASS *CLASS_get_array_of_struct_class(CLASS *class); + +int CLASS_sizeof(CLASS *class); + +CLASS *CLASS_find_load_from(const char *name, const char *from); + +/* class_init.c */ + +void CLASS_init_native(void); + +/* class_load.c */ + +TYPE CLASS_ctype_to_type(CLASS *class, CTYPE ctype); +int CLASS_sizeof_ctype(CLASS *class, CTYPE ctype); + +void CLASS_load_real(CLASS *class); +#define CLASS_load(_class) \ +({ \ + if (!((_class)->ready)) \ + CLASS_load_real(_class); \ +}) +void CLASS_run_inits(CLASS *class); +void CLASS_load_without_init(CLASS *class); + +/* class_native.c */ + +CLASS *CLASS_register_class(GB_DESC *desc, CLASS *class); +CLASS *CLASS_register(GB_DESC *desc); + +#define SET_IF_NULL(ptr, val) if ((ptr) == NULL) (ptr) = (val) +/* +#define DO_NOTHING(ptr) if ((ptr) == NULL) (ptr) = CLASS_do_nothing +#define RETURN_ZERO(ptr) if ((ptr) == NULL) (ptr) = CLASS_return_zero +*/ + +#define CLASS_is_virtual(class) (class->is_virtual) + +#define CLASS_has_operator(_class, _op) (((void **)(_class)->operators)[_op] != NULL) +#define CLASS_get_operator_strength(_class) (((intptr_t *)(_class)->operators)[CO_STRENGTH]) +#define CLASS_set_operator_strength(_class, _strength) (((intptr_t *)(_class)->operators)[CO_STRENGTH] = (_strength)) + +#define CLASS_is_loaded(_class) ((_class)->loaded) +#define CLASS_is_ready(_class) ((_class)->ready) + +#endif /* _CLASS_H */ diff --git a/main/gbx/gbx_class_desc.h b/main/gbx/gbx_class_desc.h new file mode 100644 index 00000000..8e6a9d05 --- /dev/null +++ b/main/gbx/gbx_class_desc.h @@ -0,0 +1,207 @@ +/*************************************************************************** + + gbx_class_desc.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_CLASS_DESC_H +#define __GBX_CLASS_DESC_H + +#include "gb_class_desc_common.h" +#include "gb_table.h" + +#define CD_PROPERTY 'p' +#define CD_PROPERTY_READ 'r' +#define CD_METHOD 'm' +#define CD_CONSTANT 'C' +#define CD_EVENT ':' +#define CD_STATIC_PROPERTY 'P' +#define CD_STATIC_PROPERTY_READ 'R' +#define CD_STATIC_METHOD 'M' +#define CD_VARIABLE 'v' +#define CD_STRUCT_FIELD 'f' +#define CD_STATIC_VARIABLE 'V' +#define CD_EXTERN 'X' + +#define CD_STATIC_LIST "PRMVX" +#define CD_CALL_SOMETHING_LIST "prmPRM" + + +typedef + struct { + char *name; + TYPE type; // property datatype + void (*read)(); // read property function + void (*write)(); // write property function + unsigned native : 1; // if the property is native + unsigned _reserved : 7; + char _reserved2[3]; + #ifdef OS_64BITS + int _reserved3; + #endif + struct _CLASS *class; + } + CLASS_DESC_PROPERTY; + +typedef + struct { + char *name; + TYPE type; // variable datatype + int offset; // variable offset in object memory + CTYPE ctype; // variable compilation datatype + intptr_t _reserved; + #ifdef OS_64BITS + intptr_t _reserved2; + #endif + struct _CLASS *class; + } + CLASS_DESC_VARIABLE; + +typedef + struct { + char *name; + TYPE type; // return value datatype + void (*exec)(); // method + TYPE *signature; // signature + char npmin; // minimum number of arguments + char npmax; // maximum number of arguments + char npvar; // if there is a variable number of arguments + unsigned native : 1; // if the method is native + unsigned subr : 1; // static method called like a subr + unsigned _reserved : 6; + #ifdef OS_64BITS + int _reserved2; + #endif + struct _CLASS *class; + } + CLASS_DESC_METHOD; + +typedef + struct { + char *name; + TYPE type; // return value datatype - N/A + intptr_t index; // event index + TYPE *signature; // event signature + char npmin; // minimum number of arguments + char npmax; // maximum number of arguments + char npvar; // if there is a variable number of arguments + char _reserved; + #ifdef OS_64BITS + int _reserved2; + #endif + struct _CLASS *class; + } + CLASS_DESC_EVENT; + +typedef + struct { + char *name; + TYPE type; // return value datatype + int exec; // extern function index + TYPE *signature; // signature + char npmin; // minimum number of arguments + char npmax; // maximum number of arguments + char npvar; // if there is a variable number of arguments + char _reserved; + #ifdef OS_64BITS + int _reserved2; + #endif + struct _CLASS *class; + } + CLASS_DESC_EXTERN; + +typedef + struct { + char *name; + TYPE type; // constant datatype + union { + int _integer; + double _float; + float _single; + char *_string; + int64_t _long; + void *_pointer; + } + value; + unsigned translate : 1; + unsigned _reserved : 31; + #ifdef OS_64BITS + int _reserved2; + #endif + struct _CLASS *class; + } + CLASS_DESC_CONSTANT; + +typedef + struct { + char *name; + void (*func)(); + } + CLASS_DESC_HOOK; + +typedef + struct { + char *name; + intptr_t type; + intptr_t val1; + intptr_t val2; + union { + double _double; + intptr_t _int[2]; + } val3; + } + CLASS_DESC_GAMBAS; + + +typedef + union { + CLASS_DESC_PROPERTY property; + CLASS_DESC_VARIABLE variable; + CLASS_DESC_METHOD method; + CLASS_DESC_CONSTANT constant; + CLASS_DESC_EVENT event; + CLASS_DESC_HOOK hook; + CLASS_DESC_GAMBAS gambas; + CLASS_DESC_EXTERN ext; + } + CLASS_DESC; + +typedef + struct { + char *name; + int len; +#if TABLE_USE_KEY + uint key; +#endif + CLASS_DESC *desc; + } + PACKED + CLASS_DESC_SYMBOL; + + +#define CLASS_DESC_get_type(d) (*(d)->gambas.name) +#define CLASS_DESC_is_static_method(d) (CLASS_DESC_get_type((CLASS_DESC *)d) == 'M') +#define CLASS_DESC_SELF (-1) + +char *CLASS_DESC_get_signature(CLASS_DESC *cd); +const char *CLASS_DESC_get_type_name(const CLASS_DESC *desc); + + +#endif diff --git a/main/gbx/gbx_class_info.c b/main/gbx/gbx_class_info.c new file mode 100644 index 00000000..5237867a --- /dev/null +++ b/main/gbx/gbx_class_info.c @@ -0,0 +1,448 @@ +/*************************************************************************** + + gbx_class_info.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_CLASS_INFO_C + +#include "gb_common.h" +#include "gb_alloc.h" +#include "gb_error.h" +#include "gb_limit.h" +#include "gbx_c_array.h" +#include "gambas.h" + +static GB_DESC NATIVE_GambasLanguage[] = +{ + GB_DECLARE(".", 0), + + GB_METHOD("Left$", "s", NULL, "(String)s[(Length)i]"), + GB_METHOD("Left", "s", NULL, "(String)s[(Length)i]"), + + GB_METHOD("Mid$", "s", NULL, "(String)s[(Pos)i(Length)i]"), + GB_METHOD("Mid", "s", NULL, "(String)s[(Pos)i(Length)i]"), + + GB_METHOD("Right$", "s", NULL, "(String)s[(Length)i]"), + GB_METHOD("Right", "s", NULL, "(String)s[(Length)i]"), + + GB_METHOD("Len", "i", NULL, "(String)s"), + + GB_METHOD("Space$", "s", NULL, "(Length)i"), + GB_METHOD("Space", "s", NULL, "(Length)i"), + + GB_METHOD("String$", "s", NULL, "(Length)i(Pattern)s"), + GB_METHOD("String", "s", NULL, "(Length)i(Pattern)s"), + + GB_METHOD("Trim$", "s", NULL, "(String)s"), + GB_METHOD("Trim", "s", NULL, "(String)s"), + + GB_METHOD("LTrim$", "s", NULL, "(String)s"), + GB_METHOD("LTrim", "s", NULL, "(String)s"), + + GB_METHOD("RTrim$", "s", NULL, "(String)s"), + GB_METHOD("RTrim", "s", NULL, "(String)s"), + + GB_METHOD("Upper$", "s", NULL, "(String)s"), + GB_METHOD("Upper", "s", NULL, "(String)s"), + GB_METHOD("UCase$", "s", NULL, "(String)s"), + GB_METHOD("UCase", "s", NULL, "(String)s"), + + GB_METHOD("Lower$", "s", NULL, "(String)s"), + GB_METHOD("Lower", "s", NULL, "(String)s"), + GB_METHOD("LCase$", "s", NULL, "(String)s"), + GB_METHOD("LCase", "s", NULL, "(String)s"), + + GB_METHOD("Chr$", "s", NULL, "(Code)i"), + GB_METHOD("Chr", "s", NULL, "(Code)i"), + + GB_METHOD("Asc", "i", NULL, "(String)s[(Pos)i]"), + + GB_METHOD("Instr", "i", NULL, "(String)s(Pattern)s[(From)i(Mode)i]"), + GB_METHOD("RInstr", "i", NULL, "(String)s(Pattern)s[(From)i(Mode)i]"), + + GB_METHOD("Subst$", "s", NULL, "(Pattern)s."), + GB_METHOD("Subst", "s", NULL, "(Pattern)s."), + + GB_METHOD("Replace$", "s", NULL, "(String)s(Find)s(Replace)s[(Mode)i]"), + GB_METHOD("Replace", "s", NULL, "(String)s(Find)s(Replace)s[(Mode)i]"), + + GB_METHOD("Split", "String[]", NULL, "(String)s[(Separators)s(Escape)s(IgnoreVoid)b(KeepEscape)b]"), + GB_METHOD("Scan", "String[]", NULL, "(String)s(Pattern)s"), + + GB_METHOD("Comp", "i", NULL, "(String1)s(String2)s[(Mode)i]"), + + GB_METHOD("Conv$", "s", NULL, "(String)s(From)s(To)s"), + GB_METHOD("Conv", "s", NULL, "(String)s(From)s(To)s"), + + GB_METHOD("SConv$", "s", NULL, "(String)s"), + GB_METHOD("SConv", "s", NULL, "(String)s"), + + GB_METHOD("DConv$", "s", NULL, "(String)s"), + GB_METHOD("DConv", "s", NULL, "(String)s"), + + GB_METHOD("Abs", "v", NULL, "(Value)v"), + GB_METHOD("Int", "v", NULL, "(Value)v"), + + GB_METHOD("Frac", "f", NULL, "(Value)f"), + GB_METHOD("Log", "f", NULL, "(Value)f"), + GB_METHOD("Exp", "f", NULL, "(Value)f"), + GB_METHOD("Sqr", "f", NULL, "(Value)f"), + GB_METHOD("Sin", "f", NULL, "(Value)f"), + GB_METHOD("Cos", "f", NULL, "(Value)f"), + GB_METHOD("Tan", "f", NULL, "(Value)f"), + GB_METHOD("Atn", "f", NULL, "(Value)f"), + GB_METHOD("ATan", "f", NULL, "(Value)f"), + GB_METHOD("Asn", "f", NULL, "(Value)f"), + GB_METHOD("ASin", "f", NULL, "(Value)f"), + GB_METHOD("Acs", "f", NULL, "(Value)f"), + GB_METHOD("ACos", "f", NULL, "(Value)f"), + GB_METHOD("Deg", "f", NULL, "(Radians)f"), + GB_METHOD("Rad", "f", NULL, "(Degrees)f"), + GB_METHOD("Log10", "f", NULL, "(Value)f"), + GB_METHOD("Sinh", "f", NULL, "(Value)f"), + GB_METHOD("Cosh", "f", NULL, "(Value)f"), + GB_METHOD("Tanh", "f", NULL, "(Value)f"), + GB_METHOD("Asnh", "f", NULL, "(Value)f"), + GB_METHOD("ASinh", "f", NULL, "(Value)f"), + GB_METHOD("Acsh", "f", NULL, "(Value)f"), + GB_METHOD("ACosh", "f", NULL, "(Value)f"), + GB_METHOD("Atnh", "f", NULL, "(Value)f"), + GB_METHOD("ATanh", "f", NULL, "(Value)f"), + GB_METHOD("Exp2", "f", NULL, "(Value)f"), + GB_METHOD("Exp10", "f", NULL, "(Value)f"), + GB_METHOD("Log2", "f", NULL, "(Value)f"), + GB_METHOD("Cbr", "f", NULL, "(Value)f"), + GB_METHOD("Expm", "f", NULL, "(Value)f"), + GB_METHOD("Logp", "f", NULL, "(Value)f"), + GB_METHOD("Ceil", "f", NULL, "(Value)f"), + GB_METHOD("Floor", "f", NULL, "(Value)f"), + + GB_METHOD("Atan2", "f", NULL, "(X)f(Y)f"), + GB_METHOD("Ang", "f", NULL, "(X)f(Y)f"), + GB_METHOD("Hyp", "f", NULL, "(X)f(Y)f"), + GB_METHOD("Mag", "f", NULL, "(X)f(Y)f"), + + GB_METHOD("Sgn", "i", NULL, "(Value)v"), + GB_METHOD("Fix", "v", NULL, "(Value)v"), + + GB_METHOD("Pi", "f", NULL, "[(Factor)f]"), + + GB_METHOD("Round", "f", NULL, "(Value)f[(Round)i]"), + + GB_METHOD("Rnd", "f", NULL, "[(From)f(To)f]"), + GB_METHOD("Rand", "i", NULL, "(From)i[(To)i]"), + + GB_METHOD("Min", "v", NULL, "(Value)v(Value2)v"), + GB_METHOD("Max", "v", NULL, "(Value)v(Value2)v"), + + GB_METHOD("If", "v", NULL, "(Test)b(True)v(False)v"), + GB_METHOD("IIf", "v", NULL, "(Test)b(True)v(False)v"), + + GB_METHOD("Choose", "v", NULL, "(Select)i[(Value)v.]"), + + GB_METHOD("BClr", "i", NULL, "(Value)i(Bit)i"), + GB_METHOD("BSet", "i", NULL, "(Value)i(Bit)i"), + GB_METHOD("BTst", "i", NULL, "(Value)i(Bit)i"), + GB_METHOD("BChg", "i", NULL, "(Value)i(Bit)i"), + + GB_METHOD("Shl", "i", NULL, "(Value)i(Shift)i"), + GB_METHOD("Shr", "i", NULL, "(Value)i(Shift)i"), + GB_METHOD("Rol", "i", NULL, "(Value)i(Shift)i"), + GB_METHOD("Ror", "i", NULL, "(Value)i(Shift)i"), + + GB_METHOD("IsBoolean", "b", NULL, "(Value)s"), + GB_METHOD("IsInteger", "b", NULL, "(Value)s"), + GB_METHOD("IsLong", "b", NULL, "(Value)s"), + GB_METHOD("IsFloat", "b", NULL, "(Value)s"), + GB_METHOD("IsDate", "b", NULL, "(Value)s"), + GB_METHOD("IsNumber", "b", NULL, "(Value)s"), + GB_METHOD("IsNull", "b", NULL, "(Value)v"), + + GB_METHOD("IsAscii", "b", NULL, "(String)s"), + GB_METHOD("IsLetter", "b", NULL, "(String)s"), + GB_METHOD("IsLower", "b", NULL, "(String)s"), + GB_METHOD("IsUpper", "b", NULL, "(String)s"), + GB_METHOD("IsLCase", "b", NULL, "(String)s"), + GB_METHOD("IsUCase", "b", NULL, "(String)s"), + GB_METHOD("IsDigit", "b", NULL, "(String)s"), + GB_METHOD("IsHexa", "b", NULL, "(String)s"), + GB_METHOD("IsSpace", "b", NULL, "(String)s"), + GB_METHOD("IsBlank", "b", NULL, "(String)s"), + GB_METHOD("IsPunct", "b", NULL, "(String)s"), + + GB_METHOD("TypeOf", "i", NULL, "(Value)v"), + GB_METHOD("SizeOf", "i", NULL, "(Type)i"), + + GB_METHOD("CBool", "b", NULL, "(Value)v"), + GB_METHOD("CBoolean", "b", NULL, "(Value)v"), + GB_METHOD("CByte", "c", NULL, "(Value)v"), + GB_METHOD("CShort", "h", NULL, "(Value)v"), + GB_METHOD("CInt", "i", NULL, "(Value)v"), + GB_METHOD("CInteger", "i", NULL, "(Value)v"), + GB_METHOD("CLong", "l", NULL, "(Value)v"), + GB_METHOD("CSingle", "g", NULL, "(Value)v"), + GB_METHOD("CFloat", "f", NULL, "(Value)v"), + GB_METHOD("CDate", "d", NULL, "(Value)v"), + GB_METHOD("CStr", "s", NULL, "(Value)v"), + GB_METHOD("CString", "s", NULL, "(Value)v"), + GB_METHOD("CPointer", "p", NULL, "(Value)v"), + GB_METHOD("CVariant", "v", NULL, "(Value)v"), + + GB_METHOD("Bin$", "s", NULL, "(Value)v[(Digits)i]"), + GB_METHOD("Bin", "s", NULL, "(Value)v[(Digits)i]"), + + GB_METHOD("Hex$", "s", NULL, "(Value)v[(Digits)i]"), + GB_METHOD("Hex", "s", NULL, "(Value)v[(Digits)i]"), + + GB_METHOD("Val", "v", NULL, "(String)s"), + + GB_METHOD("Str$", "s", NULL, "(Value)v"), + GB_METHOD("Str", "s", NULL, "(Value)v"), + + GB_METHOD("Format$", "s", NULL, "(Value)v[(Format)s]"), + GB_METHOD("Format", "s", NULL, "(Value)v[(Format)s]"), + + GB_METHOD("Timer", "f", NULL, NULL), + GB_METHOD("Now", "d", NULL, NULL), + + GB_METHOD("Year", "i", NULL, "(Date)d"), + GB_METHOD("Month", "i", NULL, "(Date)d"), + GB_METHOD("Day", "i", NULL, "(Date)d"), + GB_METHOD("Hour", "i", NULL, "(Date)d"), + GB_METHOD("Minute", "i", NULL, "(Date)d"), + GB_METHOD("Second", "i", NULL, "(Date)d"), + GB_METHOD("WeekDay", "i", NULL, "(Date)d"), + + GB_METHOD("Date", "d", NULL, "[(DateOrYear)v(Month)i(Day)i(Hour)i(Minute)i(Second)i(MilliSecond)i]"), + GB_METHOD("Time", "d", NULL, "[(DateOrHour)v(Minute)i(Second)i(MilliSecond)i]"), + GB_METHOD("Week", "i", NULL, "[(Date)d(Mode)i(Plain)b]"), + + GB_METHOD("DateAdd", "d", NULL, "(Date)d(Period)i(Count)i"), + GB_METHOD("DateDiff", "i", NULL, "(Date1)d(Date2)d(Period)i"), + + GB_METHOD("Eval", "v", NULL, "(Expression)s[(Context)Collection;]"), + + GB_METHOD("Eof", "b", NULL, "[(File)Stream;]"), + GB_METHOD("Lof", "l", NULL, "[(File)Stream;]"), + GB_METHOD("Seek", "l", NULL, "(File)Stream;"), + + GB_METHOD("Exist", "b", NULL, "(Path)s[(FollowLink)b]"), + GB_METHOD("Stat", "Stat", NULL, "(Path)s[(FollowLink)b]"), + + GB_METHOD("Temp$", "s", NULL, "[(Prefix)s]"), + GB_METHOD("Temp", "s", NULL, "[(Prefix)s]"), + + GB_METHOD("IsDir", "b", NULL, "(Path)s"), + + GB_METHOD("Access", "b", NULL, "(Path)s[(Mode)i]"), + + GB_METHOD("Dir", "String[]", NULL, "(Path)s[(Pattern)s(Filter)i]"), + GB_METHOD("RDir", "String[]", NULL, "(Path)s[(Pattern)s(Filter)i(FollowLink)b]"), + + GB_METHOD("DFree", "l", NULL, "(Path)s"), + + GB_METHOD("Alloc", "p", NULL, "(SizeOrString)v[(Count)i]"), + GB_METHOD("Free", NULL, NULL, "(Pointer)p"), + GB_METHOD("Realloc", "i", NULL, "(Pointer)p(Size)i[(Count)i]"), + GB_METHOD("Str@", "s", NULL, "(Pointer)p"), + GB_METHOD("String@", "s", NULL, "(Pointer)p"), + GB_METHOD("VarPtr", "p", NULL, "(Variable)v"), + + GB_METHOD("MkBool", "s", NULL, "(Value)b"), + GB_METHOD("MkBool$", "s", NULL, "(Value)b"), + GB_METHOD("MkBoolean", "s", NULL, "(Value)b"), + GB_METHOD("MkBoolean$", "s", NULL, "(Value)b"), + GB_METHOD("MkByte", "s", NULL, "(Value)c"), + GB_METHOD("MkByte$", "s", NULL, "(Value)c"), + GB_METHOD("MkShort", "s", NULL, "(Value)h"), + GB_METHOD("MkShort$", "s", NULL, "(Value)h"), + GB_METHOD("MkInt", "s", NULL, "(Value)i"), + GB_METHOD("MkInt$", "s", NULL, "(Value)i"), + GB_METHOD("MkInteger", "s", NULL, "(Value)i"), + GB_METHOD("MkInteger$", "s", NULL, "(Value)i"), + GB_METHOD("MkLong", "s", NULL, "(Value)l"), + GB_METHOD("MkLong$", "s", NULL, "(Value)l"), + GB_METHOD("MkSingle", "s", NULL, "(Value)g"), + GB_METHOD("MkSingle$", "s", NULL, "(Value)g"), + GB_METHOD("MkFloat", "s", NULL, "(Value)f"), + GB_METHOD("MkFloat$", "s", NULL, "(Value)f"), + GB_METHOD("MkDate", "s", NULL, "(Value)d"), + GB_METHOD("MkDate$", "s", NULL, "(Value)d"), + GB_METHOD("MkPointer", "s", NULL, "(Value)p"), + GB_METHOD("MkPointer$", "s", NULL, "(Value)p"), + + GB_METHOD("Swap", "s", NULL, "(String)s[(Endianness)i]"), + GB_METHOD("Swap$", "s", NULL, "(String)s[(Endianness)i]"), + + GB_METHOD("Bool@", "b", NULL, "(Pointer)p"), + GB_METHOD("Boolean@", "b", NULL, "(Pointer)p"), + GB_METHOD("Byte@", "c", NULL, "(Pointer)p"), + GB_METHOD("Short@", "h", NULL, "(Pointer)p"), + GB_METHOD("Int@", "i", NULL, "(Pointer)p"), + GB_METHOD("Integer@", "i", NULL, "(Pointer)p"), + GB_METHOD("Long@", "l", NULL, "(Pointer)p"), + GB_METHOD("Single@", "g", NULL, "(Pointer)p"), + GB_METHOD("Float@", "f", NULL, "(Pointer)p"), + GB_METHOD("Date@", "f", NULL, "(Pointer)p"), + GB_METHOD("Pointer@", "p", NULL, "(Pointer)p"), + + GB_METHOD("Tr", "s", NULL, "(String)s"), + GB_METHOD("Tr$", "s", NULL, "(String)s"), + + GB_METHOD("Quote", "s", NULL, "(String)s"), + GB_METHOD("Quote$", "s", NULL, "(String)s"), + + GB_METHOD("Shell", "s", NULL, "(String)s"), + GB_METHOD("Shell$", "s", NULL, "(String)s"), + + GB_METHOD("Html", "s", NULL, "(String)s"), + GB_METHOD("Html$", "s", NULL, "(String)s"), + + GB_METHOD("Base64", "s", NULL, "(String)s"), + GB_METHOD("Base64$", "s", NULL, "(String)s"), + + GB_METHOD("Url", "s", NULL, "(String)s"), + GB_METHOD("Url$", "s", NULL, "(String)s"), + + GB_METHOD("Unquote", "s", NULL, "(String)s"), + GB_METHOD("Unquote$", "s", NULL, "(String)s"), + + GB_METHOD("UnBase64", "s", NULL, "(String)s"), + GB_METHOD("UnBase64$", "s", NULL, "(String)s"), + + GB_METHOD("FromBase64", "s", NULL, "(String)s"), + GB_METHOD("FromBase64$", "s", NULL, "(String)s"), + + GB_METHOD("FromUrl", "s", NULL, "(String)s"), + GB_METHOD("FromUrl$", "s", NULL, "(String)s"), + + GB_METHOD("Odd", "b", NULL, "(Value)i"), + GB_METHOD("Even", "b", NULL, "(Value)i"), + + GB_METHOD("IsNan", "b", NULL, "(Value)f"), + GB_METHOD("IsInf", "i", NULL, "(Value)f"), + + GB_METHOD("IsMissing", "b", NULL, "(Argument)?"), + + GB_END_DECLARE +}; + + +extern GB_DESC NATIVE_GambasLanguage[]; +extern GB_DESC NATIVE_Gambas[]; +extern GB_DESC NATIVE_Param[]; +extern GB_DESC NATIVE_Enum[]; +extern GB_DESC NATIVE_Symbol[]; +extern GB_DESC NATIVE_Class[]; +extern GB_DESC NATIVE_Classes[]; +extern GB_DESC NATIVE_Component[]; +extern GB_DESC NATIVE_Components[]; +extern GB_DESC NATIVE_Object[]; +extern GB_DESC NATIVE_Collection[]; +extern GB_DESC NATIVE_Error[]; +extern GB_DESC StreamDesc[]; +extern GB_DESC StreamLinesDesc[]; +extern GB_DESC StreamTermDesc[]; +extern GB_DESC StatPermDesc[]; +extern GB_DESC StatDesc[]; +extern GB_DESC FileDesc[]; +extern GB_DESC NATIVE_AppEnv[]; +extern GB_DESC NATIVE_AppArgs[]; +extern GB_DESC NATIVE_App[]; +extern GB_DESC NATIVE_System[]; +extern GB_DESC NATIVE_Jit[]; +extern GB_DESC NATIVE_User[]; +extern GB_DESC NATIVE_ArrayBounds[]; +extern GB_DESC NATIVE_Array[]; +extern GB_DESC NATIVE_Process[]; +extern GB_DESC NATIVE_BooleanArray[]; +extern GB_DESC NATIVE_ByteArray[]; +extern GB_DESC NATIVE_ShortArray[]; +extern GB_DESC NATIVE_IntegerArray[]; +extern GB_DESC NATIVE_SingleArray[]; +extern GB_DESC NATIVE_FloatArray[]; +extern GB_DESC NATIVE_DateArray[]; +extern GB_DESC NATIVE_StringArray[]; +extern GB_DESC NATIVE_ObjectArray[]; +extern GB_DESC NATIVE_VariantArray[]; +extern GB_DESC NATIVE_TemplateArray[]; +extern GB_DESC NATIVE_TemplateArrayOfStruct[]; +extern GB_DESC NATIVE_LongArray[]; +extern GB_DESC NATIVE_PointerArray[]; +extern GB_DESC StringDesc[]; +extern GB_DESC BoxedStringDesc[]; +extern GB_DESC TaskDesc[]; +extern GB_DESC NATIVE_Timer[]; +extern GB_DESC NATIVE_Observer[]; +extern GB_DESC NATIVE_Proxy[]; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + NATIVE_GambasLanguage, + NATIVE_Gambas, + NATIVE_Param, + NATIVE_Enum, + NATIVE_Symbol, + NATIVE_Class, + NATIVE_Classes, + NATIVE_Component, + NATIVE_Components, + NATIVE_Object, + NATIVE_Collection, + NATIVE_Error, + StreamLinesDesc, + StreamTermDesc, + StreamDesc, + StatPermDesc, + StatDesc, + FileDesc, + NATIVE_AppEnv, + NATIVE_AppArgs, + NATIVE_App, + NATIVE_System, + NATIVE_Jit, + NATIVE_User, + NATIVE_ArrayBounds, + NATIVE_Array, + NATIVE_Process, + NATIVE_BooleanArray, + NATIVE_ByteArray, + NATIVE_ShortArray, + NATIVE_IntegerArray, + NATIVE_SingleArray, + NATIVE_FloatArray, + NATIVE_DateArray, + NATIVE_StringArray, + NATIVE_ObjectArray, + NATIVE_VariantArray, + NATIVE_TemplateArray, + NATIVE_TemplateArrayOfStruct, + NATIVE_LongArray, + NATIVE_PointerArray, + StringDesc, + BoxedStringDesc, + TaskDesc, + NATIVE_Timer, + NATIVE_Observer, + //NATIVE_Proxy, + NULL +}; + diff --git a/main/gbx/gbx_class_init.c b/main/gbx/gbx_class_init.c new file mode 100644 index 00000000..cb92f5d5 --- /dev/null +++ b/main/gbx/gbx_class_init.c @@ -0,0 +1,173 @@ +/*************************************************************************** + + gbx_class_init.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_CLASS_INIT_C + +#include "gb_common.h" +#include "gb_alloc.h" +#include "gb_error.h" +#include "gb_limit.h" + +#include "gbx_component.h" + +#include "gbx_c_gambas.h" +#include "gbx_c_observer.h" +#include "gbx_c_class.h" +#include "gbx_c_error.h" +#include "gbx_c_collection.h" +#include "gbx_c_file.h" +#include "gbx_c_system.h" +#include "gbx_c_application.h" +#include "gbx_c_array.h" +#include "gbx_c_process.h" +#include "gbx_c_string.h" +#include "gbx_c_enum.h" +#include "gbx_c_timer.h" +#include "gbx_c_task.h" + +#include "gbx_class.h" + +CLASS *CLASS_Class = NULL; +CLASS *CLASS_Collection = NULL; +CLASS *CLASS_Symbol = NULL; +CLASS *CLASS_File = NULL; +CLASS *CLASS_Stat = NULL; +CLASS *CLASS_Stream = NULL; +CLASS *CLASS_Application = NULL; +CLASS *CLASS_AppArgs = NULL; +CLASS *CLASS_AppEnv = NULL; +CLASS *CLASS_Process = NULL; +CLASS *CLASS_Component = NULL; +CLASS *CLASS_Observer = NULL; +CLASS *CLASS_Timer = NULL; +CLASS *CLASS_BoxedString = NULL; + +CLASS *CLASS_Array = NULL; +CLASS *CLASS_BooleanArray = NULL; +CLASS *CLASS_ByteArray = NULL; +CLASS *CLASS_ShortArray = NULL; +CLASS *CLASS_IntegerArray = NULL; +CLASS *CLASS_SingleArray = NULL; +CLASS *CLASS_FloatArray = NULL; +CLASS *CLASS_DateArray = NULL; +CLASS *CLASS_StringArray = NULL; +CLASS *CLASS_ObjectArray = NULL; +CLASS *CLASS_VariantArray = NULL; +CLASS *CLASS_LongArray = NULL; +CLASS *CLASS_PointerArray = NULL; + +CLASS *CLASS_String = NULL; +CLASS *CLASS_Enum = NULL; + +typedef + struct { + GB_DESC *desc; + CLASS **class; + int array; + TYPE array_type; + } + CLASS_INIT; + +static const CLASS_INIT init_list[] = +{ + { NATIVE_Gambas, NULL }, + { NATIVE_Param, NULL }, + { NATIVE_Enum, &CLASS_Enum }, + { NATIVE_Symbol, NULL }, + { NATIVE_Class, NULL }, + { NATIVE_Classes, NULL }, + { NATIVE_Component, NULL }, + { NATIVE_Components, NULL }, + { NATIVE_Object, NULL }, + { NATIVE_Collection, &CLASS_Collection, CQA_COLLECTION }, + { NATIVE_Error, NULL }, + { StreamLinesDesc, NULL }, + { StreamTermDesc, NULL }, + { StreamDesc, &CLASS_Stream }, + { StatPermDesc, NULL }, + { StatDesc, &CLASS_Stat }, + { FileDesc, &CLASS_File }, + { NATIVE_AppEnv, &CLASS_AppEnv }, + { NATIVE_AppArgs, &CLASS_AppArgs }, + { NATIVE_App, &CLASS_Application }, + { NATIVE_Process, &CLASS_Process }, + { NATIVE_System, NULL }, + { NATIVE_Jit, NULL }, + { NATIVE_User, NULL }, + { StringDesc, NULL }, + { BoxedStringDesc, &CLASS_BoxedString, CQA_STRING }, + { TaskDesc, NULL }, + { NATIVE_Timer, &CLASS_Timer }, + { NATIVE_Observer, &CLASS_Observer }, + //{ NATIVE_Proxy, &CLASS_Proxy }, + + { NATIVE_ArrayBounds, NULL }, + { NATIVE_Array, &CLASS_Array }, + { NATIVE_BooleanArray, &CLASS_BooleanArray, CQA_ARRAY, T_BOOLEAN }, + { NATIVE_ByteArray, &CLASS_ByteArray, CQA_ARRAY, T_BYTE }, + { NATIVE_ShortArray, &CLASS_ShortArray, CQA_ARRAY, T_SHORT }, + { NATIVE_IntegerArray, &CLASS_IntegerArray, CQA_ARRAY, T_INTEGER }, + { NATIVE_FloatArray, &CLASS_FloatArray, CQA_ARRAY, T_FLOAT }, + { NATIVE_SingleArray, &CLASS_SingleArray, CQA_ARRAY, T_SINGLE }, + { NATIVE_DateArray, &CLASS_DateArray, CQA_ARRAY, T_DATE }, + { NATIVE_StringArray, &CLASS_StringArray, CQA_ARRAY, T_STRING }, + { NATIVE_ObjectArray, &CLASS_ObjectArray, CQA_ARRAY, T_OBJECT }, + { NATIVE_VariantArray, &CLASS_VariantArray, CQA_ARRAY, T_VARIANT }, + { NATIVE_LongArray, &CLASS_LongArray, CQA_ARRAY, T_LONG }, + { NATIVE_PointerArray, &CLASS_PointerArray, CQA_ARRAY, T_POINTER }, + + { NULL } +}; + + +void CLASS_init_native(void) +{ + const CLASS_INIT *init; + CLASS *class; + + /* NOTE: The 'Class' class must be first in the global class table */ + CLASS_Class = CLASS_find("Class"); + CLASS_Symbol = CLASS_find("Symbol"); + CLASS_Component = CLASS_find("Component"); + CLASS_Stream = CLASS_find("Stream"); + + //LIBRARY_Current = LIBRARY_create(NULL); + + for (init = init_list; init->desc; init++) + { + class = CLASS_register(init->desc); + if (init->class != NULL) + *init->class = class; + if (init->array) + { + class->quick_array = init->array; + class->array_type = init->array_type; + class->is_array = init->array == CQA_ARRAY; + } + } + + CLASS_Observer->is_observer = TRUE; + CLASS_Observer->size += sizeof(OBJECT_EVENT); + //CLASS_Proxy->is_observer = TRUE; + //CLASS_Proxy->size += sizeof(OBJECT_EVENT); +} diff --git a/main/gbx/gbx_class_load.c b/main/gbx/gbx_class_load.c new file mode 100644 index 00000000..f4cf661d --- /dev/null +++ b/main/gbx/gbx_class_load.c @@ -0,0 +1,1360 @@ +/*************************************************************************** + + gbx_class_load.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_CLASS_LOAD_C + +#include "gb_common.h" +#include "gb_common_case.h" +#include "gb_common_buffer.h" +#include "gb_common_swap.h" +#include "gb_alloc.h" +#include "gb_error.h" +#include "gb_limit.h" + +#include + +#include "gb_buffer.h" +#include "gb_file.h" +#include "gbx_type.h" +#include "gbx_exec.h" +#include "gbx_debug.h" +#include "gb_magic.h" +#include "gbx_stream.h" +#include "gbx_c_array.h" +#include "gbx_string.h" +#include "gbx_object.h" +#include "gbx_variant.h" +#include "gbx_number.h" +#include "gbx_struct.h" +#include "gbx_jit.h" + +#include "gambas.h" + +#include "gbx_class.h" + +//#define DEBUG DEBUG +//#define DEBUG_LOAD 1 +//#define DEBUG_STRUCT 1 + +static bool _load_without_init = FALSE; +static const char *_class_name; +static bool _swap; +static bool _last_ctype_is_static; + +#define _b "\x1" +#define _s "\x2" +#define _i "\x3" +#define _p "\x4" +#define _c "\x5" +#define _t "\x6" + +#ifdef DEBUG +static CLASS *Class; +static int NSection; +#endif + +static void SWAP_type(CTYPE *p) +{ + SWAP_short(&p->value); +} + + +static int align_pos(int pos, int size) +{ + switch (size) + { + case 1: + return pos; + case 2: + return (pos + 1) & ~1; + #ifndef OS_64BITS + case 4: + default: + return (pos + 3) & ~3; + #else + case 4: + return (pos + 3) & ~3; + case 8: + default: + return (pos + 7) & ~7; + #endif + } +} + + +int CLASS_sizeof_ctype(CLASS *class, CTYPE ctype) +{ + size_t size; + + if (ctype.id == TC_ARRAY) + { + CLASS_ARRAY *array = class->load->array[ctype.value]; + size = CARRAY_get_static_size(class, array); + return (size + 3) & ~3; + } + else if (ctype.id == TC_STRUCT) + { + return CSTRUCT_get_size(class->load->class_ref[ctype.value]); + } + else + return TYPE_sizeof_memory(ctype.id); +} + +TYPE CLASS_ctype_to_type(CLASS *class, CTYPE ctype) +{ + if (ctype.id == T_OBJECT && ctype.value >= 0) + return (TYPE)(class->load->class_ref[ctype.value]); + else if (ctype.id == TC_ARRAY) + return (TYPE)CARRAY_get_array_class(class, class->load->array[ctype.value]->ctype); + else if (ctype.id == TC_STRUCT) + return (TYPE)(class->load->class_ref[ctype.value]); + else + return (TYPE)(ctype.id); +} + + +static void conv_ctype(CTYPE *ctype) +{ + //if (ctype->id == TC_POINTER) + // ctype->id = T_POINTER; +} + + +static void conv_type(CLASS *class, void *ptype) +{ + CTYPE ctype = *(CTYPE *)ptype; + + if (_swap) + { + SWAP_int((int *)&ctype); + SWAP_type(&ctype); + } + + _last_ctype_is_static = CTYPE_is_static(ctype); + *((TYPE *)ptype) = CLASS_ctype_to_type(class, ctype); +} + + +static void conv_type_simple(CLASS *class, int *ptype) +{ + CTYPE ctype = *(CTYPE *)ptype; + + if (_swap) + SWAP_int((int *)&ctype); + + *ptype = ctype.id; +} + + +static void check_version(CLASS *class, int loaded) +{ + if (loaded > GAMBAS_PCODE_VERSION) + THROW_CLASS(class, "Bytecode too recent. Please upgrade Gambas.", ""); + if (loaded < GAMBAS_PCODE_VERSION_MIN) + THROW_CLASS(class, "Bytecode too old. Please recompile the project.", ""); +} + + +static char *get_section(char *sec_name, char **section, short *pcount, const char *desc) +{ + static void *jump_swap[] = { &&__SWAP_END, &&__SWAP_BYTE, &&__SWAP_SHORT, &&__SWAP_INT, &&__SWAP_POINTER, &&__SWAP_CTYPE, &&__SWAP_TYPE }; + static size_t sizeof_32[] = { 0, 1, 2, 4, 4, 4, 4 }; + #ifdef OS_64BITS + static void *jump_trans[] = { &&__TRANS_END, &&__TRANS_BYTE, &&__TRANS_SHORT, &&__TRANS_INT, &&__TRANS_POINTER, &&__TRANS_CTYPE, &&__TRANS_TYPE }; + static size_t sizeof_64[] = { 0, 1, 2, 4, 8, 4, 8 }; + #endif + + char *current = *section + sizeof(int); + int section_size = *((int *)(*section)); + int i; + char *p; + const char *pdesc; + short size; + size_t size_one = 0; + #ifdef OS_64BITS + size_t size_one_64 = 0; + char *alloc = NULL; + char *pa = NULL; + #endif + + if (_swap) + SWAP_int(§ion_size); + + #ifdef DEBUG + NSection++; + fprintf(stderr, "Section #%d %s %08lX %d %d\n", NSection + 1, sec_name, (int)(current - (char *)Class->data), (int)size_one, (int)section_size); + #endif + + *section += section_size + sizeof(int); + + if (desc) + { + pdesc = desc; + while (*pdesc) + { + size_one += sizeof_32[(int)*pdesc]; + #ifdef OS_64BITS + size_one_64 += sizeof_64[(int)*pdesc]; + #endif + pdesc++; + } + + if (section_size % size_one) + THROW(E_CLASS, _class_name, "Bad format in section: ", sec_name); + + size = section_size / size_one; + if (pcount) *pcount = size; + if (!size) + return NULL; + + if (_swap) + { + for (i = 0; i < size; i++) + { + p = current + i * size_one; + pdesc = desc; + + __SWAP_NEXT: + goto *jump_swap[(int)(*pdesc++)]; + + __SWAP_BYTE: + p++; + goto __SWAP_NEXT; + + __SWAP_SHORT: + SWAP_short((short *)p); + p += sizeof(short); + goto __SWAP_NEXT; + + __SWAP_INT: + __SWAP_POINTER: + SWAP_int((int *)p); + p += sizeof(int); + goto __SWAP_NEXT; + + __SWAP_CTYPE: + __SWAP_TYPE: + SWAP_type((CTYPE *)p); + p += sizeof(CTYPE); + goto __SWAP_NEXT; + + __SWAP_END: + continue; + } + } + + #ifdef OS_64BITS + + if (size_one_64 != size_one) + { + ALLOC(&alloc, size_one_64 * size); + + for (i = 0; i < size; i++) + { + p = current + i * size_one; + pa = alloc + i * size_one_64; + pdesc = desc; + + __TRANS_NEXT: + goto *jump_trans[(int)(*pdesc++)]; + + __TRANS_BYTE: + *pa++ = *p++; + goto __TRANS_NEXT; + + __TRANS_SHORT: + *((short *)pa) = *((short *)p); + pa += sizeof(short); + p += sizeof(short); + goto __TRANS_NEXT; + + __TRANS_INT: + __TRANS_CTYPE: + *((int *)pa) = *((int *)p); + pa += sizeof(int); + p += sizeof(int); + goto __TRANS_NEXT; + + __TRANS_TYPE: + *((int *)pa) = *((int *)p); + pa += sizeof(int); + p += sizeof(int); + *((int *)pa) = 0; + pa += sizeof(int); + goto __TRANS_NEXT; + + __TRANS_POINTER: + *((int64_t *)pa) = *((int *)p); + pa += sizeof(int64_t); + p += sizeof(int); + goto __TRANS_NEXT; + + __TRANS_END: + continue; + } + + return alloc; + } + + #endif + + } + + return current; +} + +#define RELOCATE(_ptr) (_ptr = (char *)&class->string[(int)(intptr_t)(_ptr)]) + +static void load_structure(CLASS *class, int *structure, int nfield) +{ + char *name; + char *field; + CLASS *sclass; + int i, pos, size, len; + CTYPE ctype; + CLASS_DESC *desc; + CLASS_VAR *var; + GLOBAL_SYMBOL *global = NULL; + + name = (char *)(intptr_t)(*structure++); + RELOCATE(name); + #if DEBUG_STRUCT + fprintf(stderr, "Loading structure %s\n", name); + #endif + + if (class->global) + sclass = CLASS_find_global(name); + else + sclass = CLASS_find(name); + + if (CLASS_is_loaded(sclass)) + { + if (!sclass->is_struct) + THROW_CLASS(class, "Class already exists: ", name); + + // Check compatibility with previous declaration + + if (sclass->load->n_dyn != nfield) + goto __MISMATCH; + + desc = (CLASS_DESC *)sclass->data; + + for (i = 0; i < nfield; i++) + { + field = (char *)(intptr_t)(*structure++); + RELOCATE(field); + len = strlen(field); + ctype = *((CTYPE *)structure); + structure++; + + if (CLASS_ctype_to_type(class, ctype) != desc[i].variable.type) + goto __MISMATCH; + + if (TABLE_compare_ignore_case_len(field, strlen(field), sclass->table[i].name, sclass->table[i].len)) + goto __MISMATCH; + } + + // OK, they are the same! + return; + } + + sclass->swap = class->swap; + sclass->component = class->component; + sclass->debug = class->debug; + + ALLOC_ZERO(&sclass->load, sizeof(CLASS_LOAD)); + + ALLOC(&var, sizeof(CLASS_VAR) * nfield); + sclass->load->dyn = var; + sclass->load->n_dyn = nfield; + sclass->load->class_ref = class->load->class_ref; + sclass->load->array = class->load->array; + + if (sclass->debug) + { + ALLOC(&global, sizeof(GLOBAL_SYMBOL) * nfield); + sclass->load->global = global; + sclass->load->n_global = nfield; + } + + sclass->n_desc = nfield; + ALLOC(&sclass->table, sizeof(CLASS_DESC_SYMBOL) * nfield); + ALLOC(&desc, sizeof(CLASS_DESC) * nfield); + sclass->data = (char *)desc; + + pos = 0; //sizeof(CSTRUCT); + + for (i = 0; i < nfield; i++) + { + field = (char *)(intptr_t)(*structure++); + RELOCATE(field); + len = strlen(field); + ctype = *((CTYPE *)structure); + structure++; + + size = CLASS_sizeof_ctype(class, ctype); + pos = align_pos(pos, size); + + desc[i].variable.name = "f"; + desc[i].variable.type = CLASS_ctype_to_type(class, ctype); + desc[i].variable.ctype = ctype; + desc[i].variable.offset = pos; // This the position relative to the data, NOT the object! + desc[i].variable.class = sclass; + + var[i].type = ctype; + var[i].pos = pos; + + if (sclass->debug) + { + global[i].sym.name = field; + global[i].sym.len = len; + global[i].ctype = ctype; + global[i].value = i; + } + + #if DEBUG_STRUCT + fprintf(stderr, " %d: %s As %s (%d) pos = %d\n", i, field, TYPE_get_name(desc[i].variable.type), CLASS_sizeof_ctype(class, ctype), pos); + #endif + + sclass->table[i].desc = &desc[i]; + sclass->table[i].name = field; + sclass->table[i].len = len; + + pos += size; //sizeof_ctype(class, var->type); + } + + #ifdef OS_64BITS + size = align_pos(pos, 8); + #else + size = align_pos(pos, 4); + #endif + + size += sizeof(CSTRUCT); + + #if DEBUG_STRUCT + fprintf(stderr, " --> size = %d\n", size); + #endif + + CLASS_calc_info(sclass, 0, size, TRUE, 0); + + CLASS_sort(sclass); + + if (sclass->debug) + sclass->load->sort = sclass->sort; + + CLASS_search_special(sclass); + /*for (i = 0; i < MAX_SPEC; i++) + sclass->special[i] = NO_SYMBOL;*/ + + sclass->is_struct = TRUE; + + sclass->loaded = TRUE; + sclass->ready = TRUE; + return; + +__MISMATCH: + + THROW_CLASS(class, "Structure is declared elsewhere differently: ", CLASS_get_name(sclass)); +} + + +static void load_and_relocate(CLASS *class, int len_data, CLASS_DESC **pstart, int *pndesc) +{ + char *section; + CLASS_INFO *info; + CLASS_HEADER *header; + CLASS_DESC *start; + CLASS_PARAM *local; + CLASS_EVENT *event; + CLASS_EXTERN *ext; + CLASS_VAR *var; + FUNCTION *func; + FUNC_DEBUG *debug; + int i, j, pos; + int offset; + short n_desc, n_class_ref, n_unknown, n_array, n_struct; + CLASS_STRUCT *structure = NULL; + int size; + char *name; + int len; + uchar flag; + + ALLOC_ZERO(&class->load, sizeof(CLASS_LOAD)); + + /* header */ + + section = class->data; + + header = (CLASS_HEADER *)section; + section += sizeof(CLASS_HEADER); + + class->swap = header->endian != OUTPUT_ENDIAN; + _swap = class->swap; + if (_swap) + fprintf(stderr, "Swapping class %s\n", class->name); + + if (_swap) + { + SWAP_int((int *)&header->magic); + SWAP_int((int *)&header->version); + SWAP_int((int *)&header->flag); + } + + if (header->magic != OUTPUT_MAGIC) + { + int fd; + + fd = open("/tmp/gambas-bad-header.dump", O_CREAT | O_WRONLY, 0666); + if (fd >= 0) + { + if (write(fd, class->data, len_data) != len_data) + fprintf(stderr, "Cannot dump bad class file.\n"); + else + fprintf(stderr, "Bad class file dumped at /tmp/gambas-bad-header.dump\n"); + close(fd); + } + + THROW_CLASS(class, "Bad header", ""); + } + + check_version(class, header->version); + + class->debug = header->flag & CF_DEBUG; + + info = (CLASS_INFO *)get_section("info", §ion, NULL, _s _s _i _i _s _s ); + #ifdef OS_64BITS + class->load->desc = + #endif + start = (CLASS_DESC *)get_section("description", §ion, &n_desc, _p _t _p _p _p _p ); + class->load->cst = (CLASS_CONST *)get_section("constant", §ion, &class->load->n_cst, _i _p _i ); // A special process is needed later + class->load->class_ref = (CLASS **)get_section("reference", §ion, &n_class_ref, _p ); + class->load->unknown = (char **)get_section("unknown", §ion, &n_unknown, _p ); + class->load->stat = (CLASS_VAR *)get_section("static", §ion, &class->load->n_stat, _c _i ); + class->load->dyn = (CLASS_VAR *)get_section("dynamic", §ion, &class->load->n_dyn, _c _i ); + class->load->event = (CLASS_EVENT *)get_section("event", §ion, &class->n_event, _t _s _s _p _p ); + class->load->ext = (CLASS_EXTERN *)get_section("extern", §ion, &class->load->n_ext, _t _s _s _p _p _p ); + class->load->func = (FUNCTION *)get_section("function", §ion, &class->load->n_func, _t _b _b _b _b _s _s _s _s _p _p _p _p ); + #ifdef OS_64BITS + class->load->local = + #endif + local = (CLASS_PARAM *)get_section("local", §ion, NULL, _t); + class->load->array = (CLASS_ARRAY **)get_section("array", §ion, &n_array, _i); // A special process is needed later + + // Structure descriptions + + if (info->nstruct) + { + ALLOC(&structure, sizeof(CLASS_STRUCT) * info->nstruct); + for (i = 0; i < info->nstruct; i++) + { + structure[i].desc = (int *)get_section("structure", §ion, &n_struct, _i); + structure[i].nfield = (n_struct - 1) / 2; + } + } + + // Loading code + + for (i = 0; i < class->load->n_func; i++) + { + func = &class->load->func[i]; + func->code = (ushort *)get_section("code", §ion, NULL, _s); + + flag = ((FUNCTION_FLAG *)func)->flag; + + func->fast = (flag & 1) != 0; + func->optional = (func->npmin < func->n_param); + func->use_is_missing = (flag & 2) != 0; + func->unsafe = (flag & 4) != 0; + func->fast_linked = FALSE; + + if (func->use_is_missing) + { + func->stack_usage++; + func->n_ctrl++; + } + + func->_reserved = 0; + } + + /* Creation flags */ + + class->auto_create = (info->flag & CI_AUTOCREATE) != 0; + class->no_create = (info->flag & CI_NOCREATE) != 0; + //fprintf(stderr, "%s: info->flag = %d auto_create = %d no_create = %d\n", class->name, info->flag, class->auto_create, class->no_create); + + /* Debugging information */ + + if (class->debug) + { + class->load->global = (GLOBAL_SYMBOL *)get_section("debug global", §ion, &class->load->n_global, _p _i _c _i ); + class->load->sort = (ushort *)get_section("debug global sort", §ion, NULL, _s); + #ifdef OS_64BITS + class->load->debug = + #endif + debug = (FUNC_DEBUG *)get_section("debug method", §ion, NULL, _s _s _p _p _p _s _s ); + + for (i = 0; i < class->load->n_func; i++) + { + func = &class->load->func[i]; + func->debug = &debug[i]; + func->debug->index = i; + } + + for (i = 0; i < class->load->n_func; i++) + { + func = &class->load->func[i]; + func->debug->pos = (ushort *)get_section("debug line", §ion, NULL, _s ); + } + + for (i = 0; i < class->load->n_func; i++) + { + func = &class->load->func[i]; + func->debug->local = (LOCAL_SYMBOL *)get_section("debug local", §ion, &func->debug->n_local, _p _i _i ); + } + } + + // Profile information + + if (EXEC_profile) + ALLOC_ZERO(&class->load->prof, sizeof(uint) * (class->debug ? (1 + class->load->n_func) : 1)); + + /* Source file path, ignored now! */ + + if (class->debug) + get_section("debug file name", §ion, NULL, NULL); + + /* Strings */ + + class->string = (char *)get_section("string", §ion, NULL, NULL); + + /* Referenced classes */ + + for (i = 0; i < n_class_ref; i++) + { + offset = (int)(intptr_t)class->load->class_ref[i]; + + // The compiler does not know if an array class is global or not, we must check now. + + if (offset >= 0) + { + name = &class->string[offset]; + len = strlen(name); + + if (len >= 3 && name[len - 2] == '[') + { + do + { + len -= 2; + } + while (len >= 3 && name[len - 2] == '['); + + if (CLASS_look_global(name, len)) + offset = (- offset); + } + } + + { + CLASS *ref; + + if (offset >= 0) + { + ref = CLASS_find(&class->string[offset]); + //fprintf(stderr, "%s: %s -> %p (%s)\n", class->name, &class->string[offset], ref, ref->component ? ref->component->name : "NULL"); + } + else if (offset < -1) + { + ref = CLASS_find_global(&class->string[-offset]); + //fprintf(stderr, "%s: %s -> %p (%s)\n", class->name, &class->string[-offset], ref, ref->component ? ref->component->name : "NULL"); + } + else + ref = 0; //0x31415926; //CLASS_find(&class->string[-offset]); + + class->load->class_ref[i] = ref; + } + } + + /* Datatype conversion */ + + for (i = 0; i < class->load->n_func; i++) + { + func = &class->load->func[i]; + + conv_type(class, &func->type); + func->is_static = _last_ctype_is_static; + + if (func->n_param > 0) + { + func->param = (CLASS_PARAM *)local; + + for (j = 0; j < func->n_param; j++) + conv_type(class, &func->param[j].type); + + local += func->n_param; + } + + if (func->n_local > 0) + { + func->local = (CLASS_LOCAL *)local; + + #ifdef OS_64BITS + // The local variable descriptions are CTYPE that are 32 bits only. + // We must transform a 64 bits integer array into a 32 bits integer array. + for (j = 0; j < func->n_local; j++) + { + func->local[j] = func->local[j * 2]; + } + #endif + + // As the 'local' section is a mix of CLASS_PARAM and CLASS_LOCAL, + // we swap endianness there and not during get_section() + + if (_swap) + { + for (j = 0; j < func->n_local; j++) + { + SWAP_int((int *)&func->local[j].type); + SWAP_type(&func->local[j].type); + } + } + + for (j = 0; j < func->n_local; j++) + conv_ctype(&func->local[j].type); + + local += func->n_local; + } + } + + /* Events information */ + + for (i = 0; i < class->n_event; i++) + { + event = &class->load->event[i]; + + conv_type(class, &event->type); + + if (event->n_param > 0) + { + event->param = (CLASS_PARAM *)local; + + for (j = 0; j < event->n_param; j++) + conv_type(class, &event->param[j].type); + + local += event->n_param; + } + } + + /* Extern calls information */ + + for (i = 0; i < class->load->n_ext; i++) + { + ext = &class->load->ext[i]; + + conv_type(class, &ext->type); + + if (ext->n_param > 0) + { + ext->param = (CLASS_PARAM *)local; + + for (j = 0; j < ext->n_param; j++) + conv_type(class, &ext->param[j].type); + + local += ext->n_param; + } + } + + /* End of file reached ? */ + + if (section != &class->data[len_data]) + { + /*printf("%d\n", &class->load[BUFFER_length(class->load)] - section);*/ + THROW_CLASS(class, "Unknown section", ""); + } + + + /* Static array definition relocation */ + + if (n_array > 0) + { + #ifdef OS_64BITS + CLASS_ARRAY **array = class->load->array; + n_array = *((int *)array) / sizeof(int); + ALLOC(&class->load->array, sizeof(void *) * n_array); + #else + n_array = *((int *)class->load->array) / sizeof(int); + #endif + + for (i = 0; i < n_array; i++) + { + #ifdef OS_64BITS + class->load->array[i] = (CLASS_ARRAY *)((char *)array + ((int *)array)[i]); + #else + class->load->array[i] = (CLASS_ARRAY *)((char *)class->load->array + ((int *)class->load->array)[i]); + #endif + //conv_type(class, &class->load->array[i]->type); + } + } + + // Create structures (we may need the structure size to compute the variable sizes) + + if (info->nstruct) + { + for (i = 0; i < info->nstruct; i++) + load_structure(class, structure[i].desc, structure[i].nfield); + FREE(&structure); + } + + // Computes and align the position of each static and dynamic variables. + // Computes the total size needed accordingly. + + #ifdef DEBUG + fprintf(stderr, "Compute variable position for %s\n", class->name); + #endif + + pos = 0; + for (i = 0; i < class->load->n_stat; i++) + { + var = &class->load->stat[i]; + conv_ctype(&var->type); + size = CLASS_sizeof_ctype(class, var->type); + pos = align_pos(pos, size); + var->pos = pos; + #ifdef DEBUG + fprintf(stderr, "Static #%d: %d\n", i, var->pos); + #endif + pos += size; + } + #ifdef OS_64BITS + info->s_static = align_pos(pos, 8); + #else + info->s_static = align_pos(pos, 4); + #endif + + pos = 0; + for (i = 0; i < class->load->n_dyn; i++) + { + var = &class->load->dyn[i]; + conv_ctype(&var->type); + size = CLASS_sizeof_ctype(class, var->type); + pos = align_pos(pos, size); + var->pos = pos; + #ifdef DEBUG + fprintf(stderr, "Dynamic #%d: %d\n", i, var->pos); + #endif + pos += size; //sizeof_ctype(class, var->type); + } + #ifdef OS_64BITS + info->s_dynamic = align_pos(pos, 8); + #else + info->s_dynamic = align_pos(pos, 4); + #endif + + /* String relocation */ + + for (i = 0; i < n_desc; i++) + RELOCATE(start[i].gambas.name); + + for (i = 0; i < n_unknown; i++) + RELOCATE(class->load->unknown[i]); + + for (i = 0; i < class->n_event; i++) + RELOCATE(class->load->event[i].name); + + for (i = 0; i < class->load->n_ext; i++) + { + RELOCATE(class->load->ext[i].library); + RELOCATE(class->load->ext[i].alias); + } + + if (class->debug) + { + for (i = 0; i < class->load->n_global; i++) + { + RELOCATE(class->load->global[i].sym.name); + /*conv_type(class, &(class->load->global[i].type));*/ + } + + for (i = 0; i < class->load->n_func; i++) + { + func = &class->load->func[i]; + RELOCATE(func->debug->name); + + for (j = 0; j < func->debug->n_local; j++) + RELOCATE(func->debug->local[j].sym.name); + } + } + + /* Inheritance */ + + if (info->parent >= 0) + { + //printf("%s inherits %s\n", class->name, (class->load->class_ref[info->parent])->name); + CLASS_inheritance(class, class->load->class_ref[info->parent]); + } + + /* If there is no dynamic variable, then the class is not instanciable */ + //if (info->s_dynamic == 0) + // class->no_create = TRUE; + + /* Class size and offsets */ + + CLASS_calc_info(class, class->n_event, info->s_dynamic, FALSE, info->s_static); + + *pstart = start; + *pndesc = n_desc; +} + + +static void load_without_inits(CLASS *class) +{ + int i; + FUNCTION *func; + CLASS_DESC *desc; + CLASS_CONST *cc; + CLASS_VAR *var; + int len; + CLASS_EVENT *event; + CLASS_EXTERN *ext; + VALUE value; + int len_data; + int n_desc; + int offset; + int first; + int first_event; + COMPONENT *save; + CLASS_DESC *start; + char kind; + + //size_t alloc = MEMORY_size; + + if (CLASS_is_loaded(class)) + return; + + if (class->error) + THROW_CLASS(class, "Loading has already failed", ""); + + if (CLASS_exiting) + THROW_CLASS(class, "Program is exiting", ""); + + class->error = TRUE; + + #if DEBUG_LOAD + fprintf(stderr, "Loading class %s (%p)...\n", class->name, class); + #endif + + #ifdef DEBUG + Class = class; + NSection = 0; + #endif + + _class_name = class->name; + + if (class->in_load) + THROW_CLASS(class, "Circular reference", ""); + + if (!class->component) + { + if (CP) + { + class->component = CP->component; + #if DEBUG_COMP + fprintf(stderr, "Load class %s -> component %s from CP\n", class->name, class->component ? class->component->name : "NULL"); + #endif + } + else + { + class->component = COMPONENT_current; + #if DEBUG_COMP + fprintf(stderr, "Load class %s -> component %s from COMPONENT_current\n", class->name, class->component ? class->component->name : "NULL"); + #endif + } + } + + #if DEBUG_COMP + if (class->component) + fprintf(stderr, "Load class %s -> component %s\n", class->name, class->component->name); + else + fprintf(stderr, "Load class %s -> no component\n", class->name); + #endif + + save = COMPONENT_current; + COMPONENT_current = class->component; + #if DEBUG_LOAD + fprintf(stderr, "COMPONENT_current = %s\n", COMPONENT_current ? COMPONENT_current->name : "NULL"); + #endif + + len = strlen(class->name); + + { + char name[len + 9]; + char *p; + + strcpy(name, ".gambas/"); + p = &name[8]; + + for (i = 0; i < len; i++) + *p++ = toupper(class->name[i]); + *p = 0; + + TRY + { + //class->mmapped = !STREAM_map(name, &class->data, &len_data); + STREAM_load(name, &class->data, &len_data); + } + CATCH + { + COMPONENT_current = save; + THROW_CLASS(class, "Unable to load class file", ""); + } + END_TRY + + /*if (BUFFER_load_file(&class->data, FILE_get(name))) + THROW(E_CLASS, _class_name, "Unable to load class file", "");*/ + } + + COMPONENT_current = save; + #if DEBUG_LOAD + fprintf(stderr, "COMPONENT_current = %s\n", COMPONENT_current ? COMPONENT_current->name : "NULL"); + #endif + + class->in_load = TRUE; + + class->init_dynamic = TRUE; + + load_and_relocate(class, len_data, &start, &n_desc); + + // Information on static and dynamic variables + + if (class->parent) + offset = class->parent->off_event; + else + offset = sizeof(OBJECT); + + for (i = 0; i < class->load->n_dyn; i++) + { + var = &class->load->dyn[i]; + var->pos += offset; + } + + // Constant conversion & relocation + + for (i = 0; i < class->load->n_cst; i++) + { + cc = &class->load->cst[i]; + conv_type_simple(class, &(cc->type)); + + switch (cc->type) + { + case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: + #ifdef OS_64BITS + // Special process for integer constants + cc->_integer.value = (int)(intptr_t)cc->_string.addr; + #endif + break; + + case T_LONG: + #ifdef OS_64BITS + // Special process for long constants: the first 32 bits part of the LONG constant + // has been extended to 64 bits + //cc->_swap.val[0] = (int)(*((int64_t *)(void *)&cc->_string.addr)); + cc->_swap.val[0] = (int)(intptr_t)cc->_string.addr; + cc->_swap.val[1] = cc->_string.len; + #endif + /* + The two 32 bits parts of the LONG value have been already swapped independently. + So we just have to swap the two parts again. + */ + if (_swap) + { + int val; + + val = cc->_swap.val[0]; + cc->_swap.val[0] = cc->_swap.val[1]; + cc->_swap.val[1] = val; + } + break; + + case T_STRING: case T_CSTRING: + if (cc->_string.len) + cc->_string.addr += (intptr_t)class->string; + break; + + case T_FLOAT: case T_SINGLE: + cc->_string.addr += (intptr_t)class->string; + if (NUMBER_from_string(NB_READ_FLOAT, cc->_string.addr, strlen(cc->_string.addr), &value)) + THROW_CLASS(class, "Bad constant", ""); + if (cc->type == T_SINGLE) + cc->_single.value = (float)value._float.value; + else + cc->_float.value = value._float.value; + break; + } + } + + // Event description + + CLASS_make_event(class, &first_event); + + if (class->free_event && class->n_event > first_event) + memcpy(&class->event[first_event], class->load->event, (class->n_event - first_event) * sizeof(CLASS_EVENT)); + + // Class public description + + for (i = 0; i < n_desc; i++) + { + desc = &start[i]; //class->table[i].desc; + + //desc->gambas.name = (char *)CLASS_DESC_get_type_name(desc); + + conv_type(class, &desc->gambas.type); + + kind = *CLASS_DESC_get_type_name(desc); + + if (!desc->gambas.val1 && index(CD_CALL_SOMETHING_LIST, kind) != NULL) + fprintf(stderr, "load_without_inits: '%s.%s' gambas.val1 == 0\n", class->name, desc->gambas.name); + + switch (kind) + { + case CD_METHOD: + case CD_STATIC_METHOD: + + func = &class->load->func[desc->gambas.val1]; + desc->method.exec = (void (*)())desc->gambas.val1; + desc->method.npmin = func->npmin; + desc->method.npmax = func->n_param; + desc->method.npvar = func->vararg; + desc->method.signature = (TYPE *)func->param; + //desc->method.help = NULL; + desc->method.native = FALSE; + + break; + + case CD_PROPERTY: + case CD_STATIC_PROPERTY: + case CD_PROPERTY_READ: + case CD_STATIC_PROPERTY_READ: + + desc->property.read = (void (*)())desc->gambas.val1; + desc->property.write = (void (*)())desc->gambas.val2; + //if ((intptr_t)desc->property.write == -1L) + // desc->gambas.name = *desc->gambas.name == 'p' ? "r" : "R"; + desc->property.native = FALSE; + + break; + + case CD_VARIABLE: + case CD_STATIC_VARIABLE: + + if (kind == CD_STATIC_VARIABLE) + var = &class->load->stat[desc->gambas.val1]; + else + var = &class->load->dyn[desc->gambas.val1]; + + desc->variable.ctype = var->type; + desc->variable.offset = var->pos; + + break; + + case CD_CONSTANT: + + cc = &class->load->cst[desc->gambas.val1]; + + if (TYPE_is_integer(desc->constant.type)) + desc->constant.value._integer = cc->_integer.value; + else if (desc->constant.type == T_FLOAT) + desc->constant.value._float = cc->_float.value; + else if (desc->constant.type == T_LONG) + desc->constant.value._long = cc->_long.value; + else if (desc->constant.type == T_SINGLE) + desc->constant.value._single = cc->_single.value; + else + { + desc->constant.type = T_CSTRING; + desc->constant.value._string = cc->_string.addr; + desc->constant.translate = (cc->type == T_CSTRING); + } + + break; + + case CD_EVENT: + + //fprintf(stderr, "event %s.%s: %d %d\n", class->name, desc->event.name, first_event, (int)desc->event.index); + + event = &class->load->event[desc->event.index]; + if (class->parent) + desc->event.index += class->parent->n_event; + desc->event.npmin = event->n_param; + desc->event.npmax = event->n_param; + desc->event.signature = (TYPE *)event->param; + //desc->event.help = NULL; + //desc->event.index = first_event++; + break; + + case CD_EXTERN: + + ext = &class->load->ext[desc->gambas.val1]; + desc->ext.npmin = ext->n_param; + desc->ext.npmax = ext->n_param; + desc->ext.npmin = ext->n_param; + desc->ext.signature = (TYPE *)ext->param; + //desc->event.help = NULL; + break; + + default: + + THROW_CLASS(class, "Bad description", ""); + } + } + + // Inheritance + + CLASS_make_description(class, start, n_desc, &first); + + // Transfer symbol kind into symbol name (which is stored in the symbol table now), like native classes + // Define event index + + for (i = 0; i < n_desc; i++) + { + desc = &start[i]; + desc->gambas.name = (char *)CLASS_DESC_get_type_name(desc); + desc->method.class = class; + } + + // Sort the class description + + CLASS_sort(class); + + // Special methods + + CLASS_search_special(class); + + // JIT compilation + + for(i = 0; i < class->load->n_func; i++) + { + if (class->load->func[i].fast) + { + ARCHIVE *arch = class->component ? class->component->archive : NULL; + + if (JIT_can_compile(arch)) + { + if (JIT_compile(arch)) + { + for(i = 0; i < class->load->n_func; i++) + class->load->func[i].fast = FALSE; + } + } + + break; + } + } + + // Class is loaded... + + class->in_load = FALSE; + + // ...and usable ! + + class->loaded = TRUE; + class->error = FALSE; + + // Init breakpoints + + if (EXEC_debug) + DEBUG.InitBreakpoints(class); + + //total += MEMORY_size - alloc; + //printf("%s: %d TOTAL = %d\n", class->name, MEMORY_size - alloc, total); +} + +#if 0 +void CLASS_load_without_init(CLASS *class) +{ + load_without_inits(class, FALSE); + + /* Call the static initializer */ + + EXEC.native = FALSE; + EXEC.class = class; + EXEC.object = NULL; + EXEC.nparam = 0; + EXEC.index = FUNC_INIT_STATIC; + //EXEC.func = &class->load->func[FUNC_INIT_STATIC]; + + EXEC_function(); +} +#endif + +void CLASS_run_inits(CLASS *class) +{ + /* Call the static initializer */ + + EXEC.native = FALSE; + EXEC.class = class; + EXEC.object = NULL; + EXEC.nparam = 0; + EXEC.index = FUNC_INIT_STATIC; + //EXEC.func = &class->load->func[FUNC_INIT_STATIC]; + + EXEC_function(); + + /* _init */ + EXEC_public(class, NULL, "_init", 0); +} + + +void CLASS_load_real(CLASS *class) +{ + bool load_without_init = _load_without_init; + char *name = class->name; + int len = strlen(name); + + _load_without_init = FALSE; + + if (!CLASS_is_loaded(class)) + { + if (len >= 3 && name[len - 2] == '[' && name[len - 1] == ']' && !class->array_type) + { + CLASS_create_array_class(class); + return; + } + } + + load_without_inits(class); + class->loaded = TRUE; + class->ready = FALSE; + + if (load_without_init) + return; + + class->ready = TRUE; + CLASS_run_inits(class); +} + +void CLASS_load_without_init(CLASS *class) +{ + if (CLASS_is_loaded(class)) + return; + + _load_without_init = TRUE; + CLASS_load_real(class); +} diff --git a/main/gbx/gbx_class_load.h b/main/gbx/gbx_class_load.h new file mode 100644 index 00000000..ea120522 --- /dev/null +++ b/main/gbx/gbx_class_load.h @@ -0,0 +1,302 @@ +/*************************************************************************** + + gbx_class_load.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_CLASS_LOAD_H +#define __GBX_CLASS_LOAD_H + +#include "gb_common.h" + +#ifdef OS_64BITS + +typedef + uint32_t ptr32_t; + +typedef + struct { + ptr32_t name; + int len; + } + PACKED + SYMBOL_32; + +typedef + struct { + CTYPE type; + int pos; + } + PACKED + CLASS_VAR_32; + +typedef + uint32_t TYPE_32; + +typedef + union { + TYPE_32 type; + struct { TYPE_32 type; double value; } PACKED _float; + struct { TYPE_32 type; float value; } PACKED _single; + struct { TYPE_32 type; int value; } PACKED _integer; + struct { TYPE_32 type; int64_t value; } PACKED _long; + struct { TYPE_32 type; ptr32_t addr; int len; } PACKED _string; + struct { TYPE_32 type; int val[2]; } PACKED _swap; + } + PACKED + CLASS_CONST_32; + +typedef + ptr32_t CLASS_REF_32; + +typedef + ptr32_t CLASS_UNKNOWN_32; + +typedef + struct { + CTYPE type; + } + PACKED + CLASS_LOCAL_32; + +typedef + struct { + TYPE_32 type; + } + PACKED + CLASS_PARAM_32; + +typedef + struct { + SYMBOL_32 sym; + int value; + } + LOCAL_SYMBOL_32; + +typedef + struct { + unsigned short line; + unsigned short nline; + unsigned short *pos; + ptr32_t name; + ptr32_t local; // LOCAL_SYMBOL_32 + short n_local; + unsigned short _reserved; + } + PACKED + FUNC_DEBUG_32; + +typedef + struct { + TYPE type; + char n_param; + char npmin; + char vararg; + char flag; + short n_local; + short n_ctrl; + short stack_usage; + short error; + ptr32_t code; // unsigned short * + ptr32_t param; // CLASS_PARAM + ptr32_t local; // CLASS_LOCAL + ptr32_t debug; // FUNC_DEBUG + } + PACKED + FUNCTION_32; + +typedef + struct { + TYPE_32 type; + short n_param; + short _reserved; + ptr32_t param; // CLASS_PARAM + ptr32_t name; + } + PACKED + CLASS_EVENT_32; + +typedef + struct { + TYPE_32 type; + short n_param; + unsigned loaded : 1; + unsigned _reserved : 15; + ptr32_t param; // CLASS_PARAM + ptr32_t alias; + ptr32_t library; + } + PACKED + CLASS_EXTERN_32; + +typedef + struct { + TYPE_32 type; + int dim[0]; + } + CLASS_ARRAY_32; + +typedef + CLASS_ARRAY_32 *CLASS_ARRAY_P_32; + +struct _CLASS; + +typedef + struct { + SYMBOL_32 sym; + CTYPE ctype; + int value; + } + GLOBAL_SYMBOL_32; + +#if 0 +typedef + struct { + ptr32_t name; + TYPE_32 type; + ptr32_t read; + ptr32_t write; + char native; + char _reserved[3]; + ptr32_t class; + } + PACKED + CLASS_DESC_PROPERTY_32; + +typedef + struct { + ptr32_t name; + TYPE_32 type; + int offset; + int _reserved; + } + PACKED + CLASS_DESC_VARIABLE; + +typedef + struct { + ptr32_t name; + TYPE_32 type; + void (*exec)(); /* m�hode */ + TYPE *signature; /* signature */ + char npmin; /* nombre de param�res minimum */ + char npmax; /* nombre de param�res maximum dans la signature */ + char npvar; /* nombre d'arguments variables ? */ + char native; /* native method */ + struct _CLASS *class; + } + PACKED + CLASS_DESC_METHOD; + +typedef + struct { + char *name; + TYPE type; /* type de la valeur de retour */ + int *index; /* num�o de l'��ement */ + TYPE *signature; /* signature */ + char npmin; /* nombre de param�res minimum */ + char npmax; /* nombre de param�res maximum dans la signature */ + char npvar; /* nombre d'arguments variables ? */ + char _reserved; + struct _CLASS *class; + } + PACKED + CLASS_DESC_EVENT; + +typedef + struct { + char *name; + TYPE type; /* type de la valeur de retour */ + int exec; /* Index a ex�uter */ + TYPE *signature; /* signature */ + char npmin; /* nombre de param�res minimum */ + char npmax; /* nombre de param�res maximum dans la signature */ + char npvar; /* nombre d'arguments variables ? */ + char _reserved; + struct _CLASS *class; + } + PACKED + CLASS_DESC_EXTERN; + +typedef + struct { + char *name; + TYPE type; /* type de constante */ + union { + int _integer; + double _float; + char *_string; + int64_t _long; + void *_pointer; + } + value; + } + PACKED + CLASS_DESC_CONSTANT; + +typedef + struct { + char *name; + void (*func)(); + } + PACKED + CLASS_DESC_HOOK; +#endif + +typedef + union { + ptr32_t name; + TYPE_32 type; + ptr32_t val1; + ptr32_t val2; + ptr32_t val3; + ptr32_t val4; + } + PACKED + CLASS_DESC_GAMBAS_32; + +typedef + union { + CLASS_DESC_GAMBAS_32 gambas; + } + PACKED + CLASS_DESC_32; + + +typedef + struct { + CLASS_DESC_32 *desc; + CLASS_CONST_32 *cst; + CLASS_VAR_32 *stat; + CLASS_VAR_32 *dyn; + FUNCTION_32 *func; + CLASS_EVENT_32 *event; + CLASS_EXTERN_32 *ext; + CLASS_ARRAY_32 **array; + struct _CLASS **class_ref; + char **unknown; + GLOBAL_SYMBOL_32 *global; + } + PACKED + CLASS_LOAD_32; + +#endif + +#endif diff --git a/main/gbx/gbx_class_native.c b/main/gbx/gbx_class_native.c new file mode 100644 index 00000000..768980a9 --- /dev/null +++ b/main/gbx/gbx_class_native.c @@ -0,0 +1,356 @@ +/*************************************************************************** + + gbx_class_native.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_CLASS_NATIVE_C + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_alloc.h" +#include "gb_error.h" +#include "gb_limit.h" + +#include + +#include "gb_buffer.h" +#include "gb_file.h" +#include "gbx_type.h" +#include "gbx_exec.h" +#include "gbx_debug.h" +#include "gb_magic.h" +#include "gbx_stream.h" + +#include "gbx_string.h" +#include "gbx_object.h" +#include "gbx_variant.h" +#include "gbx_number.h" + +#include "gambas.h" +#include "gbx_api.h" + +#include "gbx_class.h" + +//extern long total; + +BEGIN_PROPERTY(class_self_property) + + GB_ReturnSelf(_object); + +END_PROPERTY + +CLASS *CLASS_register_class(GB_DESC *ptr, CLASS *class) +{ + int i, n_desc; + + CLASS_DESC *desc; + CLASS_DESC *start; + CLASS_DESC_GAMBAS *gambas = (CLASS_DESC_GAMBAS *)ptr; + + CLASS_EVENT *event; + const char *ptype; + //const char *type; + int first_event, nsign; + TYPE *sign; + int first; + int size_dynamic; + VALUE value; + + #if DEBUG_LOAD + fprintf(stderr, "Registering native class %s (%p)...\n", class->name, class); + #endif + + if (gambas->type != GB_VERSION) + return NULL; + + if (class->error) + THROW_CLASS(class, "Loading has already failed", ""); + + class->error = TRUE; + + class->is_native = TRUE; + class->load = NULL; + class->data = NULL; + class->component = COMPONENT_current; + + #if DEBUG_COMP + if (class->component) + fprintf(stderr, "class %s -> component %s\n", class->name, class->component->name); + else + fprintf(stderr, "class %s -> no component\n", class->name); + #endif + + class->is_virtual = *class->name == '.'; + + #ifdef OS_64BITS + size_dynamic = (gambas->val1 + 7) & ~7; + #else + size_dynamic = (gambas->val1 + 3) & ~3; + #endif + + class->n_desc = 0; + class->n_event = 0; + nsign = 0; + + /* Read the class global information at the beginning of the description */ + + desc = (CLASS_DESC *)&gambas[1]; + + for (start = NULL; start == NULL; desc++) + { + if (desc->gambas.name == NULL) + { + start = desc; + break; + } + + switch ((intptr_t)desc->gambas.name) + { + case (intptr_t)GB_INHERITS_ID: + CLASS_inheritance(class, CLASS_find((const char *)desc->gambas.type)); + break; + + case (intptr_t)GB_AUTO_CREATABLE_ID: + class->auto_create = TRUE; + break; + + case (intptr_t)GB_NOT_CREATABLE_ID: + class->no_create = TRUE; + break; + + case (intptr_t)GB_VIRTUAL_CLASS_ID: + class->no_create = TRUE; + class->is_virtual = TRUE; + break; + + case (intptr_t)GB_HOOK_CHECK_ID: + class->check = (int (*)())(desc->hook.func); + class->must_check = TRUE; + break; + + default: + start = desc; + break; + } + } + + /* If there is a parent class, and if the size is zero, then inherits the size */ + + if (class->parent && size_dynamic == 0) + size_dynamic = class->parent->size; + + /* Compute the number of symbol description */ + + for(desc = start, n_desc = 0; desc->gambas.name != NULL; desc++, n_desc++); + + /* Description analysis */ + + for (i = 0; i < n_desc; i++) + { + desc = &start[i]; + + ptype = (char *)desc->gambas.type; + desc->gambas.type = TYPE_from_string(&ptype); + + switch (CLASS_DESC_get_type(desc)) + { + case CD_CONSTANT: + + if (desc->constant.type == T_INTEGER) + { + desc->constant.value._integer = (int)desc->constant.value._long; + } + else if (desc->constant.type == T_STRING) + desc->constant.type = T_CSTRING; + else if (desc->constant.type == T_FLOAT || desc->constant.type == T_SINGLE) + { + if (desc->gambas.val1 == 0) + { + value._float.value = desc->gambas.val3._double; + } + else + { + if (NUMBER_from_string(NB_READ_FLOAT, desc->constant.value._string, strlen(desc->constant.value._string), &value)) + THROW_CLASS(class, "Bad constant", ""); + } + + if (desc->constant.type == T_SINGLE) + desc->constant.value._single = (float)value._float.value; + else + desc->constant.value._float = value._float.value; + } + break; + + case CD_PROPERTY: + case CD_STATIC_PROPERTY: + case CD_PROPERTY_READ: + case CD_STATIC_PROPERTY_READ: + + if ((intptr_t)desc->property.read == CLASS_DESC_SELF) + desc->property.read = (void (*)())class_self_property; + + desc->property.write = desc->property.read; + + desc->property.native = TRUE; + //desc->property.help = (char *)type; + break; + + case CD_METHOD: + case CD_STATIC_METHOD: + + //desc->method.help = (char *)desc->method.signature; + desc->method.native = TRUE; + desc->method.subr = desc->gambas.name[1] == '!'; + + if (desc->method.signature) + { + TYPE_signature_length((char *)desc->method.signature, &desc->method.npmin, &desc->method.npmax, &desc->method.npvar); + nsign += desc->method.npmax; + } + + break; + + case CD_EVENT: + + class->n_event++; + + //desc->event.help = (char *)desc->event.signature; + + if (desc->event.signature) + { + TYPE_signature_length((char *)desc->event.signature, &desc->event.npmin, &desc->event.npmax, &desc->method.npvar); + desc->event.npmin = desc->event.npmax; + nsign += desc->event.npmax; + } + + break; + } + + desc->method.class = class; + } + + CLASS_calc_info(class, class->n_event, size_dynamic, TRUE, 0); + + // Inheritance + + CLASS_make_description(class, start, n_desc, &first); + + CLASS_make_event(class, &first_event); + + // Transfer events and signatures + + if (nsign) + { + ALLOC(&class->signature, sizeof(TYPE) * nsign); + sign = class->signature; + + for (i = first; i < class->n_desc; i++) + { + desc = class->table[i].desc; + + //fprintf(stderr, "[%.*s]\n", class->table[i].len, class->table[i].name); + + switch (CLASS_DESC_get_type(desc)) + { + case CD_METHOD: + case CD_STATIC_METHOD: + + if (desc->method.npmax) + { + desc->method.signature = + TYPE_transform_signature(&sign, (char *)desc->method.signature, desc->method.npmax); + } + break; + + case CD_EVENT: + + if (desc->event.npmax) + { + desc->event.signature = + TYPE_transform_signature(&sign, (char *)desc->event.signature, desc->event.npmax); + } + + break; + } + } + } + + if (class->n_event && (!class->parent || class->n_event > class->parent->n_event)) + { + for (i = first; i < class->n_desc; i++) + { + desc = class->table[i].desc; + + switch (CLASS_DESC_get_type(desc)) + { + case CD_EVENT: + + event = &class->event[first_event]; + event->name = class->table[i].name; + if (desc->event.index) + *((int *)desc->event.index) = first_event; + desc->event.index = first_event; + + event->type = desc->event.type; + event->param = (CLASS_PARAM *)desc->event.signature; + event->n_param = desc->event.npmax; + + first_event++; + + break; + } + } + } + + /* Sort the class description */ + + CLASS_sort(class); + + /* Search for special methods */ + + CLASS_search_special(class); + + /* Class is loaded */ + + class->loaded = TRUE; + class->error = FALSE; + + /* Run the static initializer */ + + EXEC_public(class, NULL, "_init", 0); + + /* Class is ready */ + + class->ready = TRUE; + + //total += MEMORY_size - alloc; + //printf("%s: %d TOTAL = %d\n", class->name, MEMORY_size - alloc, total); + + return class; +} + + +CLASS *CLASS_register(GB_DESC *ptr) +{ + const char *name = ((CLASS_DESC_GAMBAS *)ptr)->name; + return CLASS_register_class(ptr, CLASS_check_global(CLASS_find_global(name))); +} + diff --git a/main/gbx/gbx_compare.c b/main/gbx/gbx_compare.c new file mode 100644 index 00000000..df0deb73 --- /dev/null +++ b/main/gbx/gbx_compare.c @@ -0,0 +1,692 @@ +/*************************************************************************** + + gbx_compare.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_COMPARE_C + +#include "gb_common.h" +#include "gb_common_case.h" + +#include +#include +#include + +#include "gbx_type.h" +#include "gbx_compare.h" +#include "gbx_date.h" +#include "gbx_object.h" +#include "gbx_class.h" +#include "gbx_exec.h" +#include "gbx_regexp.h" +#include "gbx_c_string.h" + + +static bool _descent = FALSE; + + +int compare_nothing(void *a, void *b) +{ + return 0; +} + +int compare_integer(int *a, int *b) +{ + bool comp; + + if (*a < *b) + comp = -1; + else if (*a > *b) + comp = 1; + else + return 0; + + if (_descent) + comp = -comp; + + return comp; +} + +int compare_short(short *a, short *b) +{ + bool comp; + + if (*a < *b) + comp = -1; + else if (*a > *b) + comp = 1; + else + return 0; + + return _descent ? (-comp) : comp; +} + + +int compare_byte(unsigned char *a, unsigned char *b) +{ + bool comp; + + if (*a < *b) + comp = -1; + else if (*a > *b) + comp = 1; + else + return 0; + + return _descent ? (-comp) : comp; +} + + +int compare_long(int64_t *a, int64_t *b) +{ + bool comp; + + if (*a < *b) + comp = -1; + else if (*a > *b) + comp = 1; + else + return 0; + + return _descent ? (-comp) : comp; +} + + +int compare_float(double *a, double *b) +{ + bool comp; + + if (*a < *b) + comp = -1; + else if (*a > *b) + comp = 1; + else + return 0; + + return _descent ? (-comp) : comp; +} + + +int compare_single(float *a, float *b) +{ + bool comp; + + if (*a < *b) + comp = -1; + else if (*a > *b) + comp = 1; + else + return 0; + + return _descent ? (-comp) : comp; +} + + +int compare_date(DATE *a, DATE *b) +{ + bool comp; + + comp = DATE_comp(a, b); + + return _descent ? (-comp) : comp; +} + +int COMPARE_string_lang(const char *s1, int l1, const char *s2, int l2, bool nocase, bool throw) +{ + wchar_t *t1 = NULL; + wchar_t *t2 = NULL; + int i, cmp; + int lt1, lt2; + + if (l1 < 0) + l1 = s1 ? strlen(s1) : 0; + + if (l2 < 0) + l2 = s2 ? strlen(s2) : 0; + + if (l1 == 0) + { + if (l2 == 0) + return 0; + else + return (-1); + } + else if (l2 == 0) + return 1; + + if (STRING_convert_to_unicode(&t1, <1, s1, l1) + || STRING_convert_to_unicode(&t2, <2, s2, l2)) + { + if (throw) + THROW(E_CONV); + else + goto __FAILED; + } + + if (nocase) + { + for (i = 0; i < lt1; i++) + t1[i] = towlower(t1[i]); + for (i = 0; i < lt2; i++) + t2[i] = towlower(t2[i]); + } + + errno = 0; + cmp = wcscoll(t1, t2); + if (!errno) + return (cmp < 0) ? - 1 : (cmp > 0) ? 1 : 0; + +__FAILED: + + return nocase ? TABLE_compare_ignore_case(s1, l1, s2, l2) : TABLE_compare(s1, l1, s2, l2); +} + +/* + Natural sort order. + Based on the algorithm made by Martin Pol (http://sourcefrog.net/projects/natsort/) + + This software is copyright by Martin Pool, and made available under the same + licence as zlib: + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the use + of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be appreciated but + is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. +*/ + +static int strnatcmp_compare_right(const char *a, int la, const char *b, int lb) +{ + int bias = 0; + unsigned char ca, cb; + + /* The longest run of digits wins. That aside, the greatest + value wins, but we can't know that it will until we've scanned + both numbers to know that they have the same magnitude, so we + remember it in BIAS. */ + + for (;; a++, b++, la--, lb--) + { + ca = (la > 0) ? *a : 0; + cb = (lb > 0) ? *b : 0; + + if (!isdigit(ca) && !isdigit(cb)) + return bias; + else if (!isdigit(ca)) + return -1; + else if (!isdigit(cb)) + return +1; + else if (ca < cb) + { + if (!bias) + bias = -1; + } + else if (ca > cb) + { + if (!bias) + bias = +1; + } + else if (!ca) // && !cb) + return bias; + } + + return 0; +} + + +static int strnatcmp_compare_left(const char *a, int la, const char *b, int lb) +{ + unsigned char ca, cb; + + /* Compare two left-aligned numbers: the first to have a + different value wins. */ + for (;; a++, b++, la--, lb--) + { + ca = (la > 0) ? *a : 0; + cb = (lb > 0) ? *b : 0; + + if (!isdigit(ca) && !isdigit(cb)) + return 0; + else if (!isdigit(ca)) + return -1; + else if (!isdigit(cb)) + return +1; + else if (ca < cb) + return -1; + else if (ca > cb) + return +1; + } + + return 0; +} + +int COMPARE_string_natural(const char *a, int la, const char *b, int lb, bool nocase) +{ + int ai, bi, lca, lcb; + unsigned char ca, cb; + int fractional, result; + + ai = bi = 0; + + for(;;) + { + for(;;) + { + if (ai >= la) + { + ca = 0; + break; + } + ca = a[ai]; + if (ca > ' ') + break; + ai++; + } + + for(;;) + { + if (bi >= lb) + { + cb = 0; + break; + } + cb = b[bi]; + if (cb > ' ') + break; + bi++; + } + + /* process run of digits */ + if (ca >= '0' && ca <= '9' && cb >= '0' && cb <= '9') + { + fractional = (ca == '0' || cb == '0'); + + if (fractional) + { + if ((result = strnatcmp_compare_left(a+ai, la-ai, b+bi, lb-bi)) != 0) + return result; + } + else + { + if ((result = strnatcmp_compare_right(a+ai, la-ai, b+bi, lb-bi)) != 0) + return result; + } + } + + if (!ca) + { + if (!cb) + return 0; + else + return -1; + } + else if (!cb) + return 1; + + lca = STRING_utf8_get_char_length(ca); + lcb = STRING_utf8_get_char_length(cb); + if (lca > 1 || lcb > 1) + { + if ((result = COMPARE_string_lang(&a[ai], lca, &b[bi], lcb, nocase, FALSE))) + return result; + ai += lca; + bi += lcb; + } + else + { + if (nocase) + { + ca = tolower(ca); + cb = tolower(cb); + } + + if (ca < cb) + return -1; + else if (ca > cb) + return +1; + ++ai; ++bi; + } + } +} + + +/*#define IMPLEMENT_COMPARE_STRING(_name, _func) \ +int compare_string_##_name(char **pa, char **pb) \ +{ \ + char *a; \ + char *b; \ + int comp; \ + \ + a = *pa; \ + if (!a) \ + a = ""; \ + \ + b = *pb; \ + if (!b) \ + b = ""; \ + \ + comp = _func(a, b); \ + if (_descent) \ + comp = -comp; \ + return comp; \ +} + +IMPLEMENT_COMPARE_STRING(binary, strcmp) +IMPLEMENT_COMPARE_STRING(case, strcasecmp)*/ + +static int compare_string_binary(char **pa, char **pb) +{ + int comp = TABLE_compare(*pa, STRING_length(*pa), *pb, STRING_length(*pb)); + return _descent ? -comp : comp; +} + +static int compare_string_case(char **pa, char **pb) +{ + int comp = TABLE_compare_ignore_case(*pa, STRING_length(*pa), *pb, STRING_length(*pb)); + return _descent ? -comp : comp; +} + +static int compare_string_lang(char **pa, char **pb) +{ + int comp = COMPARE_string_lang(*pa, STRING_length(*pa), *pb, STRING_length(*pb), FALSE, TRUE); + return _descent ? (-comp) : comp; +} + +static int compare_string_lang_case(char **pa, char **pb) +{ + int comp = COMPARE_string_lang(*pa, STRING_length(*pa), *pb, STRING_length(*pb), TRUE, TRUE); + return _descent ? (-comp) : comp; +} + +int COMPARE_string_like(const char *s1, int l1, const char *s2, int l2, bool nocase) +{ + int result; + + if (nocase) + { + if (REGEXP_match_pcre(s2, l2, s1, l1)) + return 0; + } + else + { + if (REGEXP_match(s2, l2, s1, l1)) + return 0; + } + result = TABLE_compare_ignore_case(s1, l1, s2, l2); + return (result < 0) ? -1 : (result > 0) ? 1 : 0; +} + +static int compare_string_like(char **pa, char **pb) +{ + int comp = COMPARE_string_like(*pa, STRING_length(*pa), *pb, STRING_length(*pb), FALSE); + return _descent ? (-comp) : comp; +} + +static int compare_string_match(char **pa, char **pb) +{ + int comp = COMPARE_string_like(*pa, STRING_length(*pa), *pb, STRING_length(*pb), TRUE); + return _descent ? (-comp) : comp; +} + +/*#define IMPLEMENT_COMPARE_STRING_CASE(_name, _nocase), _func \ +static int compare_string_##_name(char **pa, char **pb) \ +{ \ + int la = *pa ? strlen(*pa) : 0; \ + int lb = *pb ? strlen(*pb) : 0; \ + int diff = _func(*pa, la, *pb, lb, _nocase); \ + if (_descent) \ + return (-diff); \ + else \ + return diff; \ +} + +IMPLEMENT_COMPARE_STRING_CASE(like, FALSE, COMPARE_string_like) +IMPLEMENT_COMPARE_STRING_CASE(match, TRUE, COMPARE_string_like) +IMPLEMENT_COMPARE_STRING_CASE(natural, FALSE, COMPARE_string_natural) +IMPLEMENT_COMPARE_STRING_CASE(natural_case, TRUE, COMPARE_string_natural)*/ + +static int compare_string_natural(char **pa, char **pb) +{ + int comp = COMPARE_string_natural(*pa, STRING_length(*pa), *pb, STRING_length(*pb), FALSE); + return _descent ? (-comp) : comp; +} + +static int compare_string_natural_case(char **pa, char **pb) +{ + int comp = COMPARE_string_natural(*pa, STRING_length(*pa), *pb, STRING_length(*pb), TRUE); + return _descent ? (-comp) : comp; +} + + +int COMPARE_object(void **a, void **b) +{ + bool comp; + bool desc = _descent; + CLASS *ca, *cb; + + /*{ + STACK_BACKTRACE *bt = STACK_get_backtrace(); + fprintf(stderr, "COMPARE_object\n"); + DEBUG_print_backtrace(bt); + STACK_free_backtrace(&bt); + }*/ + + ca = OBJECT_class_null(*a); + cb = OBJECT_class_null(*b); + + if (ca && cb) + { + if (ca->has_operators && CLASS_has_operator(ca, CO_COMP) && ca == cb) + { + void *func = ca->operators[CO_COMP]; + comp = (*(int (*)(void *, void *))func)(*a, *b); + goto __RETURN; + } + + if (ca->special[SPEC_COMPARE] != NO_SYMBOL) + { + STACK_check(1); + SP->_object.class = cb; + SP->_object.object = *b; + OBJECT_REF(*b); + SP++; + EXEC_special(SPEC_COMPARE, ca, *a, 1, FALSE); + VALUE_conv_integer(&SP[-1]); + SP--; + comp = SP->_integer.value; + goto __RETURN; + } + + if (cb->special[SPEC_COMPARE] != NO_SYMBOL) + { + STACK_check(1); + SP->_object.class = ca; + SP->_object.object = *a; + OBJECT_REF(*a); + SP++; + EXEC_special(SPEC_COMPARE, cb, *b, 1, FALSE); + VALUE_conv_integer(&SP[-1]); + SP--; + comp = (- SP->_integer.value); + goto __RETURN; + } + + _descent = desc; + } + + comp = (*a == *b) ? 0 : (*a > *b) ? 1 : -1; + +__RETURN: + + return desc ? (-comp) : comp; +} + +int COMPARE_variant(VARIANT *a, VARIANT *b) +{ + TYPE type; + VALUE value; + int comp; + + if (a->type == T_NULL) + return b->type == T_NULL ? 0 : -1; + else if (b->type == T_NULL) + return 1; + + if (TYPE_is_object(a->type)) + { + if (TYPE_is_object(b->type)) + return COMPARE_object(&a->value._object, &b->value._object); + else + return 1; + } + else if (TYPE_is_object(b->type)) + return -1; + + if (a->type == b->type) + return (*COMPARE_get_func(a->type, 0))(&a->value, &b->value); + + type = Max(a->type, b->type); + + if (b->type == type) + { + VARIANT *c; + + c = a; + a = b; + b = c; + _descent = !_descent; + } + + value.type = T_VARIANT; + value._variant.vtype = b->type; + value._variant.value.data = b->value.data; + + BORROW(&value); + VALUE_conv(&value, type); + VALUE_conv_variant(&value); + comp = (*COMPARE_get_func(type, 0))(&a->value, &value._variant.value); + RELEASE(&value); + + return comp; +} + +static COMPARE_FUNC _string_func[] = { + /* 0 */ compare_string_binary, + /* 1 */ compare_string_case, + /* 2 */ compare_string_lang, + /* 3 */ compare_string_lang_case, + /* 4 */ compare_string_like, + /* 5 */ compare_string_match, + /* 6 */ compare_string_like, + /* 7 */ compare_string_match, + /* 8 */ compare_string_natural, + /* 9 */ compare_string_natural_case, + /* 10 */ compare_string_natural, + /* 11 */ compare_string_natural_case, + /* 12 */ compare_string_natural, + /* 13 */ compare_string_natural_case, + /* 14 */ compare_string_natural, + /* 15 */ compare_string_natural_case, +}; + + +COMPARE_FUNC COMPARE_get_func(TYPE type, int mode) +{ + _descent = (mode & GB_COMP_DESCENT) != 0; + mode &= GB_COMP_TYPE_MASK; + + if (type >= T_OBJECT) + return (COMPARE_FUNC)COMPARE_object; + + switch(type) + { + case T_INTEGER: + return (COMPARE_FUNC)compare_integer; + + case T_SHORT: + return (COMPARE_FUNC)compare_short; + + case T_BYTE: + case T_BOOLEAN: + return (COMPARE_FUNC)compare_byte; + + case T_LONG: + return (COMPARE_FUNC)compare_long; + + case T_FLOAT: + return (COMPARE_FUNC)compare_float; + + case T_SINGLE: + return (COMPARE_FUNC)compare_single; + + case T_DATE: + return (COMPARE_FUNC)compare_date; + + case T_STRING: + return _string_func[mode]; + + case T_POINTER: + #ifdef OS_64BITS + return (COMPARE_FUNC)compare_long; + #else + return (COMPARE_FUNC)compare_integer; + #endif + + case T_VARIANT: + return (COMPARE_FUNC)COMPARE_variant; + + default: + return (COMPARE_FUNC)compare_nothing; + } +} + +COMPARE_STRING_FUNC COMPARE_get_string_func(int mode) +{ + mode &= GB_COMP_TYPE_MASK; + + if (mode == GB_COMP_BINARY) + return (COMPARE_STRING_FUNC)STRING_compare; + else if (mode == GB_COMP_NOCASE) + return (COMPARE_STRING_FUNC)STRING_compare_ignore_case; + else + { + if (mode & GB_COMP_NATURAL) + return (COMPARE_STRING_FUNC)COMPARE_string_natural; + else if (mode & GB_COMP_LIKE) + return (COMPARE_STRING_FUNC)COMPARE_string_like; + else if (mode & GB_COMP_LANG) + return (COMPARE_STRING_FUNC)COMPARE_string_lang; + else + THROW(E_ARG); + } +} diff --git a/main/gbx/gbx_compare.h b/main/gbx/gbx_compare.h new file mode 100644 index 00000000..f4e8f838 --- /dev/null +++ b/main/gbx/gbx_compare.h @@ -0,0 +1,63 @@ +/*************************************************************************** + + gbx_compare.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_COMPARE_H +#define __GBX_COMPARE_H + +#ifndef GBX_INFO +#include "gbx_type.h" +#include "gbx_variant.h" +#endif + +#define GB_COMP_BINARY 0 +#define GB_COMP_NOCASE 1 +#define GB_COMP_LANG 2 +#define GB_COMP_LIKE 4 +#define GB_COMP_MATCH 5 +#define GB_COMP_NATURAL 8 + +#define GB_COMP_TYPE_MASK 15 + +#define GB_COMP_ASCENT 0 +#define GB_COMP_DESCENT 16 + +#ifndef GBX_INFO + +typedef + int (*COMPARE_FUNC)(); + +typedef + int (*COMPARE_STRING_FUNC)(); + +COMPARE_FUNC COMPARE_get_func(TYPE type, int mode); +COMPARE_STRING_FUNC COMPARE_get_string_func(int mode); + +int COMPARE_object(void **a, void **b); +int COMPARE_string_lang(const char *s1, int l1, const char *s2, int l2, bool nocase, bool throw); +int COMPARE_string_like(const char *s1, int l1, const char *s2, int l2, bool nocase); +int COMPARE_string_natural(const char *a, int la, const char *b, int lb, bool nocase); +int COMPARE_variant(VARIANT *a, VARIANT *b); + +#endif + +#endif diff --git a/main/gbx/gbx_component.c b/main/gbx/gbx_component.c new file mode 100644 index 00000000..d37d4304 --- /dev/null +++ b/main/gbx/gbx_component.c @@ -0,0 +1,430 @@ +/*************************************************************************** + + gbx_component.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_COMPONENT_C + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_common_case.h" +#include "gb_error.h" +#include "gb_alloc.h" +#include "gb_replace.h" +#include "gb_file.h" +#include "gbx_debug.h" + +#include +#include +#include +#include +#include + +#include "gb_error.h" + +#include "gbx_class.h" +#include "gbx_exec.h" + +#include "gbx_local.h" +#include "gbx_archive.h" +#include "gbx_library.h" +#include "gbx_project.h" + +#include "gbx_component.h" + + +//#define DEBUG_COMP 1 +//#define DEBUG_PRELOAD + +COMPONENT *COMPONENT_current = NULL; +COMPONENT *COMPONENT_main; +int COMPONENT_count = 0; +char *COMPONENT_path; + +static COMPONENT *_component_list = NULL; +static COMPONENT *_component_load = NULL; + +static bool _load_all = FALSE; + +void COMPONENT_init(void) +{ + LIBRARY_init(); + ARCHIVE_init(); +} + + +void COMPONENT_exit(void) +{ + COMPONENT *comp; + int order; + int max_order = 0; + + _component_load = NULL; + + LIST_for_each(comp, _component_list) + { + if (comp->order > max_order) + max_order = comp->order; + } + + // if order < 0, the component is not unloaded + + for (order = 0; order <= max_order; order++) + { + LIST_for_each(comp, _component_list) + { + if (comp->order == order) + COMPONENT_unload(comp); + } + } + + /*LIST_for_each(comp, _component_list) + { + if (comp->loaded) + COMPONENT_unload(comp); + }*/ + + while (_component_list) + COMPONENT_delete(_component_list); + + LIBRARY_exit(); + ARCHIVE_exit(); + + STRING_free(&COMPONENT_path); +} + + + +void COMPONENT_load_all(void) +{ + COMPONENT *comp; + + if (EXEC_debug) + { + COMPONENT_create("gb.eval"); + COMPONENT_create("gb.debug"); + } + + _load_all = TRUE; + + LIST_for_each(comp, _component_list) + { + COMPONENT_load(comp); + } + + _load_all = FALSE; +} + + +void COMPONENT_load_all_finish(void) +{ + COMPONENT *comp; + + LIST_for_each_name(comp, _component_load, load) + { + ARCHIVE_load_exported_class(comp->archive, AR_FIND_ONLY); + } + LIST_for_each_name(comp, _component_load, load) + { + ARCHIVE_load_exported_class(comp->archive, AR_LOAD_ONLY); + } + + LIST_for_each(comp, _component_list) + { + if (comp->library) + LIBRARY_after_init(comp->library); + } +} + + +COMPONENT *COMPONENT_find(const char *name) +{ + COMPONENT *comp; + + /* A null name is the main archive */ + if (!name) + return NULL; + + LIST_for_each(comp, _component_list) + { + if (strcmp(comp->name, name) == 0) + return comp; + } + + return NULL; +} + +bool COMPONENT_exist(const char *name) +{ + return COMPONENT_find(name) != NULL; +} + +bool COMPONENT_can_load_library(const char *name) +{ + char *path; + + path = FILE_buffer(); + sprintf(path, LIB_PATTERN, COMPONENT_path, name); + return FILE_exist(path); +} + +COMPONENT *COMPONENT_create(const char *name) +{ + COMPONENT *comp; + char *path = NULL; + bool can_archive; + bool user_library = FALSE; + bool same_name_as_project = FALSE; + char *p = NULL; + + if (*name == '/' || *name == ':') // user library + { + user_library = TRUE; + path = (char *)name; + if (*path == ':') + { + name++; + p = rindex(name, ':'); + if (p) + *p = 0; + } + else + name = FILE_get_basename(name); + } + + comp = COMPONENT_find(name); + if (comp) + return comp; + + ALLOC_ZERO(&comp, sizeof(COMPONENT)); + + comp->class = CLASS_Component; + comp->ref = 1; + + comp->name = STRING_new_zero(name); + + if (p) + *p = ':'; + + if (user_library) + { + comp->archive = ARCHIVE_create(comp->name, path); + comp->user = TRUE; + } + else + { + // Don't load the archive if it has the same name as the project + + if (PROJECT_name) + same_name_as_project = strcmp(comp->name, PROJECT_name) == 0; + + can_archive = !same_name_as_project; + + // System wide component + + path = FILE_buffer(); + sprintf(path, LIB_PATTERN, COMPONENT_path, name); + //fprintf(stderr, "COMPONENT_create: %s\n", path); + + if (FILE_exist(path)) + comp->library = LIBRARY_create(comp->name); + + if (can_archive) + { + path = FILE_buffer(); + sprintf(path, ARCH_PATTERN, COMPONENT_path, name); + + if (FILE_exist(path)) + comp->archive = ARCHIVE_create(comp->name, NULL); + } + } + + //fprintf(stderr, "insert %s\n", comp->name); + LIST_insert(&_component_list, comp, &comp->list); + COMPONENT_count++; + + if (!comp->library && !comp->archive && !same_name_as_project) + { + COMPONENT_delete(comp); + THROW(E_LIBRARY, name, "cannot find component"); + } + + return comp; +} + + +void COMPONENT_delete(COMPONENT *comp) +{ + COMPONENT_unload(comp); + LIST_remove(&_component_list, comp, &comp->list); + COMPONENT_count--; + + if (comp->library) + LIBRARY_delete(comp->library); + + if (comp->archive) + ARCHIVE_delete(comp->archive); + + STRING_free(&comp->name); + + FREE(&comp); +} + +static void error_COMPONENT_load(COMPONENT *current) +{ + COMPONENT_current = current; +} + +void COMPONENT_load(COMPONENT *comp) +{ + COMPONENT *current; + + if (comp->loaded || comp->loading) + return; + + #if DEBUG_COMP + fprintf(stderr, "Loading component %s\n", comp->name); + #endif + + comp->loading = TRUE; + + current = COMPONENT_current; + COMPONENT_current = comp; + + ON_ERROR_1(error_COMPONENT_load, current) + { + if (comp->library) + { + comp->order = LIBRARY_load(comp->library); + comp->library->persistent = _load_all; + } + + if (comp->archive) + { + if (_load_all) + { + //fprintf(stderr, "load later: %s\n", comp->name); + LIST_insert(&_component_load, comp, &comp->load); + } + + ARCHIVE_load(comp->archive, !_load_all); + } + } + END_ERROR + + comp->loading = FALSE; + comp->loaded = TRUE; + COMPONENT_current = current; +} + + +void COMPONENT_unload(COMPONENT *comp) +{ + if (!comp->loaded) + return; + + #if DEBUG_COMP + fprintf(stderr, "Unloading component %s [%d]\n", comp->name, comp->order); + #endif + + if (comp->library) + LIBRARY_unload(comp->library); + + /* Do not exist yet */ + //if (comp->archive) + // ARCHIVE_unload(comp->archive); + + comp->loaded = FALSE; +} + + +COMPONENT *COMPONENT_next(COMPONENT *comp) +{ + if (comp) + return (COMPONENT *)(comp->list.next); + else + return _component_list; +} + + +void COMPONENT_translation_must_be_reloaded(void) +{ + COMPONENT *comp; + + LIST_for_each(comp, _component_list) + { + if (comp->archive) + comp->archive->translation_loaded = FALSE; + } + + if (ARCHIVE_main) + ARCHIVE_main->translation_loaded = FALSE; +} + + +void COMPONENT_signal(int signal, void *param) +{ + COMPONENT *comp; + + LIST_for_each(comp, _component_list) + { + if (comp->library && comp->library->signal) + (*comp->library->signal)(signal, param); + } +} + +bool COMPONENT_get_info(const char *key, void **value) +{ + COMPONENT *comp; + + LIST_for_each(comp, _component_list) + { + if (comp->library && comp->library->info) + if ((*comp->library->info)(key, value)) + return FALSE; + } + + return TRUE; +} + +void COMPONENT_exec(const char *name, int argc, char **argv) +{ + COMPONENT *comp; + + comp = COMPONENT_create(name); + + COMPONENT_load(comp); + + if (comp->library) + LIBRARY_exec(comp->library, argc, argv); +} + +bool COMPONENT_is_loaded(const char *name) +{ + COMPONENT *comp; + + comp = COMPONENT_find(name); + return comp && comp->loaded; +} diff --git a/main/gbx/gbx_component.h b/main/gbx/gbx_component.h new file mode 100644 index 00000000..9ba557d5 --- /dev/null +++ b/main/gbx/gbx_component.h @@ -0,0 +1,89 @@ +/*************************************************************************** + + gbx_component.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_COMPONENT_H +#define __GBX_COMPONENT_H + +#include "gambas.h" +#include "gb_list.h" +#include "gb_component.h" + +#include "gbx_library.h" +#include "gbx_archive.h" + +typedef + struct _COMPONENT { + void *class; + intptr_t ref; + LIST list; + LIST load; + char *name; + LIBRARY *library; + ARCHIVE *archive; + unsigned order : 8; + unsigned loaded : 1; + unsigned user : 1; // user library + unsigned warning : 1; // Set when the bytecode warning was displayed by the class loader for this component + unsigned loading : 1; // component is being loaded + unsigned _reserved : 20; + } + COMPONENT; + +#ifndef __GBX_COMPONENT_C +EXTERN char *COMPONENT_path; +EXTERN COMPONENT *COMPONENT_current; +EXTERN int COMPONENT_count; +EXTERN COMPONENT *COMPONENT_main; +#endif + +void COMPONENT_init(void); +void COMPONENT_exit(void); + +COMPONENT *COMPONENT_create(const char *name); +void COMPONENT_delete(COMPONENT *comp); + +COMPONENT *COMPONENT_find(const char *name); +bool COMPONENT_exist(const char *name); +bool COMPONENT_can_load_library(const char *name); + +void COMPONENT_load(COMPONENT *comp); +void COMPONENT_unload(COMPONENT *comp); + +void COMPONENT_load_all(void); +void COMPONENT_load_all_finish(void); + +bool COMPONENT_is_loaded(const char *name); + +COMPONENT *COMPONENT_next(COMPONENT *comp); + +void COMPONENT_translation_must_be_reloaded(void); + +void COMPONENT_signal(int signal, void *param); + +#define COMPONENT_is_library(comp) ((comp)->library != NULL) + +bool COMPONENT_get_info(const char *key, void **value); + +void COMPONENT_exec(const char *name, int argc, char **argv); + +#endif diff --git a/main/gbx/gbx_date.c b/main/gbx/gbx_date.c new file mode 100644 index 00000000..f08a8ef0 --- /dev/null +++ b/main/gbx/gbx_date.c @@ -0,0 +1,913 @@ +/*************************************************************************** + + gbx_date.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __DATE_C + +#include "gb_common.h" +#include "gb_common_buffer.h" + +#include +#include +#include +#include + +#include "gb_error.h" +#include "gbx_value.h" +#include "gbx_local.h" +#include "gbx_number.h" +#include "gbx_c_string.h" +#include "gbx_math.h" + +#include "gbx_date.h" + +//#define DEBUG_DATE + +#define buffer_init COMMON_buffer_init +#define get_char COMMON_get_char +#define last_char COMMON_last_char +#define look_char COMMON_look_char +#define put_char COMMON_put_char +#define jump_space COMMON_jump_space +#define get_current COMMON_get_current +#define buffer_pos COMMON_pos +#define get_size_left COMMON_get_size_left + +static const char days_in_months[2][13] = +{ /* error, jan feb mar apr may jun jul aug sep oct nov dec */ + { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } /* leap year */ +}; + +static const short days_in_year[2][14] = +{ /* 0, jan feb mar apr may jun jul aug sep oct nov dec */ + { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } /* leap year */ +}; + +static double _start_time; + + +// Returns 1 for a leap year, 0 else + +static int date_is_leap_year(short year) +{ + if (year < 0) + year += 8001; + + if ((((year % 4) == 0) && ((year % 100) != 0)) || (year % 400) == 0) + return 1; + else + return 0; +} + + +static bool date_is_valid(DATE_SERIAL *date) +{ + return ((date->year == 0 + || ((date->month >= 1) && (date->month <= 12) & + (date->year >= DATE_YEAR_MIN) && (date->year <= DATE_YEAR_MAX) && (date->year != 0) && + (date->day >= 1) && (date->day <= days_in_months[date_is_leap_year(date->year)][(short)date->month]))) + && (date->hour >= 0) && (date->hour <= 23) && (date->min >= 0) && (date->min <= 59) + && (date->sec >= 0) && (date->sec <= 59)); +} + + +static short date_to_julian_year(short year) +{ + if (year < 0) + return year - DATE_YEAR_MIN; + else + return year - DATE_YEAR_MIN - 1; +} + + +static short date_from_julian_year(short year) +{ + if (year < (-DATE_YEAR_MIN)) + return year + DATE_YEAR_MIN; + else + return year + DATE_YEAR_MIN + 1; +} + +static double get_monotonic_timer(void) +{ +#ifdef HAVE_MONOTONIC_CLOCK + struct timespec tv; + #if OS_LINUX && defined(CLOCK_MONOTONIC_RAW) + if (clock_gettime(CLOCK_MONOTONIC_RAW, &tv) == 0) + #else + if (clock_gettime(CLOCK_MONOTONIC, &tv) == 0) + #endif + return (double)tv.tv_sec + (double)tv.tv_nsec / 1E9; +#else + struct timeval tv; + if (gettimeofday(&tv, NULL) == 0) + return (double)tv.tv_sec + (double)tv.tv_usec / 1E6; +#endif + return 0.0; +} + +void DATE_init(void) +{ + _start_time = get_monotonic_timer(); +} + + +void DATE_init_local(void) +{ + // Prevent glibc for calling stat("/etc/localtime") again and again... + if (!getenv("TZ")) + putenv("TZ=:/etc/localtime"); + + tzset(); +} + + +DATE_SERIAL *DATE_split_local(VALUE *value, bool local) +{ + static int last_nday, last_nmsec; + static DATE_SERIAL last_date = { 0 }; + + int nday, nmsec; + int A, B, C, D, E, M; + + nday = value->_date.date; + nmsec = value->_date.time; + + if (local && nday > 0) + nmsec -= DATE_get_timezone() * 1000; + + if (nmsec < 0) + { + nday--; + nmsec += 86400000; + } + else if (nmsec >= 86400000) + { + nday++; + nmsec -= 86400000; + } + + if (last_nmsec != nmsec) + { + last_nmsec = nmsec; + + last_date.msec = nmsec % 1000; + nmsec /= 1000; + last_date.sec = nmsec % 60; + nmsec /= 60; + last_date.min = nmsec % 60; + nmsec /= 60; + last_date.hour = nmsec; + } + + if (last_nday != nday) + { + last_nday = nday; + + /*nday += DATE_NDAY_BC;*/ + if (nday <= 0) + { + last_date.month = 0; + last_date.day = 0; + last_date.year = 0; + last_date.weekday = 0; + } + else + { + A = nday - 58 - 1; + B = (4 * (A + 36524))/ 146097 - 1; + C = A - (146097 * B)/4; + D = (4 * (C + 365)) / 1461 - 1; + E = C - ((1461 * D) / 4); + M = (5 * (E - 1) + 2) / 153; + + last_date.month = M + 3 - (12 * (M / 10)); + last_date.day = E - (153 * M + 2)/5; + last_date.year = 100 * B + D + (M / 10) /*- 4800*/ ; + last_date.year = date_from_julian_year(last_date.year); + last_date.weekday = (nday - 1) % 7; + } + } + + //fprintf(stderr, "DATE_split: %d %d %d / %d %d %d . %d\n", last_date.year, last_date.month, last_date.day, last_date.hour, last_date.min, last_date.sec, last_date.msec); + + return &last_date; +} + + +bool DATE_make_local(DATE_SERIAL *date, VALUE *val, bool local) +{ + short year; + int nday; + bool timezone; + + if (!date_is_valid(date)) + return TRUE; + + if (date->year == 0) + { + nday = 0; /*(-DATE_NDAY_BC - 1);*/ + timezone = FALSE; + } + else + { + year = date_to_julian_year(date->year); + + nday = year * 365; + if (year > 1) + { + year--; + nday += (year >>= 2); + nday -= (year /= 25); + nday += year >> 2; + } + + nday += days_in_year[date_is_leap_year(date->year)][(short)date->month] + date->day; + + /*nday -= DATE_NDAY_BC;*/ + + timezone = local; + } + + val->_date.date = nday; + val->_date.time = ((date->hour * 60) + date->min) * 60 + date->sec; + if (timezone) + val->_date.time += DATE_get_timezone(); + + if (val->_date.time < 0) + { + val->_date.date--; + val->_date.time += 86400; + } + else if (val->_date.time >= 86400) + { + val->_date.date++; + val->_date.time -= 86400; + } + + val->_date.time = val->_date.time * 1000 + date->msec; + val->type = T_DATE; + + return FALSE; +} + + +static int get_current_year(void) +{ + struct tm *tm; + struct timeval tv; + + if (gettimeofday(&tv, NULL) != 0) + THROW(E_DATE); + + tm = localtime((time_t *)&tv.tv_sec); + return tm->tm_year + 1900; +} + + +void DATE_from_time(time_t time, int usec, VALUE *val) +{ + static struct tm tm; + static time_t last_time = (time_t)-1; + + DATE_SERIAL date; + + if (time != last_time) + { + localtime_r(&time, &tm); + last_time = time; + } + + date.year = tm.tm_year + 1900; + date.month = tm.tm_mon + 1; + date.day = tm.tm_mday; + date.hour = tm.tm_hour; + date.min = tm.tm_min; + date.sec = tm.tm_sec; + date.msec = usec / 1000; + + if (DATE_make(&date, val)) + VALUE_default(val, T_DATE); +} + + +void DATE_now(VALUE *val) +{ + struct timeval tv; + + if (gettimeofday(&tv, NULL)) + VALUE_default(val, T_DATE); + else + { + //fprintf(stderr, "DATE_now: %d %d\n", tv.tv_sec, tv.tv_usec); + DATE_from_time((time_t)tv.tv_sec, tv.tv_usec, val); + } +} + + +int DATE_to_string(char *buffer, VALUE *value) +{ + DATE_SERIAL *date; + int len; + + if (value->_date.date == 0 && value->_date.time == 0) + return 0; + + date = DATE_split_local(value, FALSE); + + if (value->_date.date == 0) + len = sprintf(buffer,"%02d:%02d:%02d", date->hour, date->min, date->sec); + else if ((date->hour | date->min | date->sec | date->msec) == 0) + len = sprintf(buffer,"%02d/%02d/%04d", date->month, date->day, date->year); + else + len = sprintf(buffer,"%02d/%02d/%04d %02d:%02d:%02d", date->month, date->day, date->year, date->hour, date->min, date->sec); + + if (date->msec) + { + len += sprintf(&buffer[len], ".%03d", date->msec); + while (buffer[len - 1] == '0') + len--; + buffer[len] = 0; + } + + return len; +} + + +static bool read_integer(int *number, bool *zero) +{ + int nbr = 0; + int nbr2; + int c; + bool minus = FALSE; + + c = get_char(); + + if (c == '-') + { + minus = TRUE; + c = get_char(); + } + else if (c == '+') + c = get_char(); + + if ((c < 0) || !isdigit(c)) + return TRUE; + + if (zero) + *zero = (c == '0'); + + for(;;) + { + nbr2 = nbr * 10 + (c - '0'); + if (nbr2 < nbr) + return TRUE; + nbr = nbr2; + + c = look_char(); + if ((c < 0) || !isdigit(c)) + break; + + buffer_pos++; + } + + if (minus) + nbr = (-nbr); + + *number = nbr; + return FALSE; +} + + +static bool read_msec(int *number) +{ + int nbr = 0; + int nbr2; + int c; + int i; + + c = get_char(); + + if ((c < 0) || !isdigit(c)) + return TRUE; + + i = 0; + for(;;) + { + i++; + nbr2 = nbr * 10 + (c - '0'); + if (nbr2 < nbr) + return TRUE; + nbr = nbr2; + + if (i == 3) + break; + + c = look_char(); + if ((c < 0) || !isdigit(c)) + break; + + buffer_pos++; + } + + for (; i < 3; i++) + nbr *= 10; + + *number = nbr; + return FALSE; +} + + +static void set_date(DATE_SERIAL *date, int which, int value, bool zero) +{ + if (which == LO_YEAR) + { + if (!zero && value >= 0 && value <= 99) + { + if (value > 30) + value += 1900; + else + value += 2000; + } + date->year = value; + } + else if (which == LO_MONTH) + date->month = value; + else if (which == LO_DAY) + date->day = value; +} + + +static void set_time(DATE_SERIAL *date, int which, int value) +{ + if (which == LO_HOUR) + date->hour = value; + else if (which == LO_MINUTE) + date->min = value; + else if (which == LO_SECOND) + date->sec = value; +} + + +bool DATE_from_string(const char *str, int len, VALUE *val, bool local) +{ + DATE_SERIAL date; + LOCAL_INFO *info = LOCAL_get(local); + int nbr, nbr2; + int c, i; + bool has_date = FALSE; + bool zero, zero2; + //bool has_time = FALSE; + + if (!len) + { + DATE_void_value(val); + return FALSE; + } + + CLEAR(&date); + + buffer_init(str, len); + jump_space(); + + if (read_integer(&nbr, &zero)) + return TRUE; + + c = COMMON_get_unicode_char(); + + if (c == info->date_sep) + { + has_date = TRUE; + + if (read_integer(&nbr2, &zero2)) + return TRUE; + + c = COMMON_get_unicode_char(); + + if (c == info->date_sep) + { + set_date(&date, info->date_order[0], nbr, zero); + set_date(&date, info->date_order[1], nbr2, zero2); + + if (read_integer(&nbr, &zero)) + return TRUE; + + set_date(&date, info->date_order[2], nbr, zero); + } + else if ((c < 0) || isspace(c)) + { + i = 0; + + set_date(&date, LO_YEAR, get_current_year(), TRUE); + + if (info->date_order[i] == LO_YEAR) i++; + set_date(&date, info->date_order[i], nbr, zero); i++; + + if (info->date_order[i] == LO_YEAR) i++; + set_date(&date, info->date_order[i], nbr2, zero2); + } + + jump_space(); + + c = look_char(); + if (c < 0) + goto _OK; + + if (read_integer(&nbr, NULL)) + return TRUE; + + c = COMMON_get_unicode_char(); + } + + if (c == info->time_sep) + { + //has_time = TRUE; + + if (read_integer(&nbr2, NULL)) + return TRUE; + + c = COMMON_get_unicode_char(); + + if (c == info->time_sep) + { + set_time(&date, info->time_order[0], nbr); + set_time(&date, info->time_order[1], nbr2); + + if (read_integer(&nbr, NULL)) + return TRUE; + + set_time(&date, info->time_order[2], nbr); + + c = get_char(); + if (c == '.') // msec separator + { + if (read_msec(&nbr)) + return TRUE; + date.msec = nbr; + } + } + else if ((c < 0) || isspace(c)) + { + i = 0; + + if (info->time_order[i] == LO_SECOND) i++; + set_time(&date, info->time_order[i], nbr); i++; + + if (info->time_order[i] == LO_SECOND) i++; + set_time(&date, info->time_order[i], nbr2); + } + + c = get_char(); + if ((c < 0) || isspace(c)) + goto _OK; + } + + return TRUE; + +_OK: + + if (DATE_make_local(&date, val, local)) + return TRUE; + + if (!has_date) + val->_date.date = 0; + + return FALSE; +} + + +int DATE_comp(DATE *date1, DATE *date2) +{ + if (date1->date < date2->date) + return (-1); + + if (date1->date > date2->date) + return 1; + + if (date1->time < date2->time) + return (-1); + + if (date1->time > date2->time) + return 1; + + return 0; +} + + +int DATE_comp_value(VALUE *date1, VALUE *date2) +{ + return DATE_comp((DATE *)&date1->_date.date, (DATE *)&date2->_date.date); +} + + +double DATE_to_double(struct timeval *time, int from_start) +{ + double result = (double)time->tv_sec + (double)time->tv_usec / 1E6; + + if (from_start) + result -= _start_time; + + return result; +} + + +bool DATE_timer(double *result, int from_start) +{ + *result = get_monotonic_timer(); + + if (*result == 0.0) + return TRUE; + + if (from_start) + *result -= _start_time; + + return FALSE; +} + + +void DATE_add(VALUE *date, int period, int val) +{ + int64_t add_time = 0; + int64_t add_date = 0; + int64_t fix_date; + int64_t new_time; + int64_t new_date; + DATE_SERIAL ds; + int y, m, d; + + switch(period) + { + case DP_MILLISECOND: + add_date = val / 86400000; + add_time = val % 86400000 ; + goto __ADD_DATE_TIME; + + case DP_SECOND: + add_date = val / 86400; + add_time = (val % 86400) * 1000; + goto __ADD_DATE_TIME; + + case DP_MINUTE: + add_date = val / 1440; + add_time = (val % 1440) * 60000; + goto __ADD_DATE_TIME; + + case DP_HOUR: + add_date = val / 24; + add_time = (val % 24) * 3600000; + goto __ADD_DATE_TIME; + + case DP_DAY: + add_date = val; + goto __ADD_DATE_TIME; + + case DP_WEEK: + add_date = val * 7; + goto __ADD_DATE_TIME; + + default: + break; + } + + ds = *DATE_split(date); + + switch(period) + { + case DP_WEEKDAY: + add_date = (val / 5) * 7; + ds.weekday += val % 5; + if (ds.weekday > 5) + add_date += 2; + else if (ds.weekday < 1) + add_date -= 2; + add_date += val % 5; + goto __ADD_DATE_TIME; + + case DP_QUARTER: + val *= 3; + /* continue; */ + + case DP_MONTH: + y = ((ds.year * 12) + (ds.month - 1) + val) / 12; + m = ((ds.month - 1) + val) % 12; + if (m < 0) m += 12; + m++; + d = days_in_months[date_is_leap_year(y)][m]; + d = ds.day > d ? d : ds.day; + ds.day = d; + ds.month = m; + ds.year = y; + goto __MAKE_DATE; + + case DP_YEAR: + ds.year += val; + if (ds.month == 2 && ds.day == 29) + { + d = days_in_months[date_is_leap_year(ds.year)][ds.month]; + if (ds.day > d) + ds.day = d; + } + goto __MAKE_DATE; + + default: + THROW(E_ARG); + } + +__ADD_DATE_TIME: + + new_time = date->_date.time + add_time; + new_date = date->_date.date + add_date; + + if (new_time >= 86400000) + { + fix_date = new_time / 86400000; + new_time -= fix_date * 86400000; + new_date += fix_date; + } + else if (new_time < 0) + { + fix_date = (-new_time + 86400000 - 1) / 86400000; + new_time += fix_date * 86400000; + new_date -= fix_date; + } + + if (new_time < INT32_MIN || new_time > INT32_MAX || new_date < 0 || new_date > INT32_MAX) + THROW(E_OVERFLOW); + + date->_date.date = (int)new_date; + date->_date.time = (int)new_time; + return; + +__MAKE_DATE: + + if (DATE_make(&ds, date)) + THROW(E_DATE); + return; +} + + +int DATE_diff(VALUE *date1, VALUE *date2, int period) +{ + int64_t diff = 0; + int sdiff; + DATE_SERIAL ds1 = {0}; + DATE_SERIAL ds2 = {0}; + bool neg; + + switch (period) + { + case DP_DAY: + case DP_WEEK: + diff = date1->_date.date - date2->_date.date; + if (diff) + { + sdiff = lsgn(date1->_date.time - date2->_date.time); + if (sdiff != lsgn(diff)) + diff += sdiff; + } + break; + + case DP_MILLISECOND: + case DP_SECOND: + case DP_MINUTE: + case DP_HOUR: + diff = date1->_date.date - date2->_date.date; + diff = diff * 86400000 + (date1->_date.time - date2->_date.time); + break; + + case DP_MONTH: + case DP_QUARTER: + case DP_YEAR: + ds1 = *DATE_split(date1); + ds2 = *DATE_split(date2); + break; + + case DP_WEEKDAY: + diff = date1->_date.date - date2->_date.date; + if (diff) + { + sdiff = lsgn(date1->_date.time - date2->_date.time); + if (sdiff != lsgn(diff)) + diff += sdiff; + } + ds1 = *DATE_split(date1); + ds2 = *DATE_split(date2); + break; + + default: + THROW(E_ARG); + } + + switch (period) + { + case DP_DAY: + break; + + case DP_WEEK: + diff /= 7; + break; + + case DP_SECOND: + diff /= 1000; + break; + + case DP_MINUTE: + diff /= 60000; + break; + + case DP_HOUR: + diff /= 3600000; + break; + + case DP_WEEKDAY: + + neg = (diff < 0); + if (neg) + { + int swap; + diff = (-diff); + swap = ds1.weekday; + ds1.weekday = ds2.weekday; + ds2.weekday = swap; + } + + diff = diff / 7 * 5; + + /* last day is not included ! */ + while (ds2.weekday != ds1.weekday) + { + if (ds2.weekday > 0 && ds2.weekday < 6) + diff++; + ds2.weekday++; + if (ds2.weekday == 7) + ds2.weekday = 0; + } + + if (neg) + diff = (-diff); + + break; + + case DP_MONTH: + diff = (ds1.year - ds2.year) * 12 + ds1.month - ds2.month; + break; + + case DP_QUARTER: + diff = (ds1.year - ds2.year) * 4 + (ds1.month - ds2.month) / 3; + break; + + case DP_YEAR: + diff = ds1.year - ds2.year; + break; + + case DP_MILLISECOND: + break; + } + + if (diff < INT32_MIN || diff > INT32_MAX) + THROW(E_OVERFLOW); + + return diff; +} + + +// Beware: System.TimeZone is what must be added to go to UTC +// So if the shell command `date` displays "UTC+2", then that +// function must return -7200! + +int DATE_get_timezone(void) +{ + static time_t last = (time_t)0; + static int tz = 0; + + time_t t = time(NULL); + if ((t - last) >= 600) + { + struct tm *tm = localtime(&t); + tz = -tm->tm_gmtoff; + last = t; + } + + return tz; +} diff --git a/main/gbx/gbx_date.h b/main/gbx/gbx_date.h new file mode 100644 index 00000000..1a896ed1 --- /dev/null +++ b/main/gbx/gbx_date.h @@ -0,0 +1,109 @@ +/*************************************************************************** + + gbx_date.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_DATE_H +#define __GBX_DATE_H + +#ifndef GBX_INFO + +#include + +#include "gbx_value.h" + +typedef + struct { + int year; + int month; + int day; + int hour; + int min; + int sec; + int weekday; + int msec; + } + DATE_SERIAL; + +#ifndef __DATE_DECLARED +#define __DATE_DECLARED +typedef + struct { + int date; + int time; + } + DATE; +#endif + +#endif + +enum { + DP_MILLISECOND = 1, + DP_SECOND = 2, + DP_MINUTE = 3, + DP_HOUR = 4, + DP_DAY = 5, + DP_WEEK = 6, + DP_WEEKDAY = 7, + DP_MONTH = 8, + DP_QUARTER = 9, + DP_YEAR = 10, + }; + +#ifndef GBX_INFO + +#define DATE_YEAR_MIN -4801 +#define DATE_YEAR_MAX 9999 + +#define DATE_NDAY_BC 1753350 + +#define DATE_SERIAL_has_no_date(_date) ((_date)->year == 0) +#define DATE_SERIAL_has_no_time(_date) ((_date)->hour == 0 && (_date)->min == 0 && (_date)->sec == 0 && (_date)->msec == 0) + +#define DATE_void_value(_value) ((_value)->type = T_DATE, (_value)->_date.time = (_value)->_date.date = 0) + +void DATE_init(void); +void DATE_init_local(void); +int DATE_get_timezone(void); + +DATE_SERIAL *DATE_split_local(VALUE *value, bool local); +#define DATE_split(_value) DATE_split_local(_value, TRUE) + +bool DATE_make_local(DATE_SERIAL *date, VALUE *val, bool local); +#define DATE_make(_date, _value) DATE_make_local(_date, _value, TRUE) + +void DATE_from_time(time_t time, int usec, VALUE *val); +void DATE_now(VALUE *val); + +int DATE_to_string(char *buffer, VALUE *value); +bool DATE_from_string(const char *str, int len, VALUE *val, bool local); +int DATE_comp_value(VALUE *date1, VALUE *date2); +int DATE_comp(DATE *date1, DATE *date2); + +double DATE_to_double(struct timeval *time, int from_start); +bool DATE_timer(double *result, int from_start); + +void DATE_add(VALUE *date, int period, int val); +int DATE_diff(VALUE *date1, VALUE *date2, int period); + +#endif + +#endif diff --git a/main/gbx/gbx_debug.c b/main/gbx/gbx_debug.c new file mode 100644 index 00000000..fe6c1c1a --- /dev/null +++ b/main/gbx/gbx_debug.c @@ -0,0 +1,623 @@ +/*************************************************************************** + + gbx_debug.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_DEBUG_C + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_common_case.h" +#include + +#include "gb_buffer.h" +#include "gb_array.h" +#include "gb_error.h" +#include "gbx_exec.h" +#include "gbx_api.h" +#include "gbx_class.h" +#include "gbx_c_array.h" +#include "gbx_project.h" +#include "gbx_stack.h" +#include "gbx_subr.h" +#include "gbx_jit.h" + +#include "gbx_debug.h" + +DEBUG_INTERFACE DEBUG; +DEBUG_INFO *DEBUG_info = NULL; +int DEBUG_inside_eval = 0; + +static bool calc_line_from_position(CLASS *class, FUNCTION *func, PCODE *addr, ushort *line) +{ + int i; + ushort pos = addr - JIT_get_code(func); + ushort *post; + + if (func->debug) + { + post = func->debug->pos; + for (i = 0; i < (func->debug->nline - 1); i++) + { + if (pos >= post[i] && pos < post[i + 1]) + { + *line = i + func->debug->line; + return FALSE; + } + } + + /*printf("pos = %d addr=%p func->code=%p\n", pos, addr, func->code);*/ + } + + return TRUE; +} + +const char *DEBUG_get_position(CLASS *cp, FUNCTION *fp, PCODE *pc) +{ +#if DEBUG_MEMORY + static char buffer[256]; +#endif + ushort line = 0; + + if (!cp || !pc) + return "?"; + + if (fp != NULL && fp->debug) + calc_line_from_position(cp, fp, pc, &line); + +#if DEBUG_MEMORY + snprintf(buffer, sizeof(buffer), "%s.%s.%d", + cp ? cp->name : "?", + (fp && fp->debug) ? fp->debug->name : "?", + line); + + return buffer; +#else + snprintf(COMMON_buffer, COMMON_BUF_MAX, "%.64s.%.64s.%d", + cp->name, + (fp && fp->debug) ? fp->debug->name : "?", + line); + + return COMMON_buffer; +#endif +} + + +const char *DEBUG_get_current_position(void) +{ + return DEBUG_get_position(CP, FP, PC); +} + + +void DEBUG_init(void) +{ + if (!EXEC_debug) + return; + + COMPONENT_load(COMPONENT_create("gb.debug")); + LIBRARY_get_interface_by_name("gb.debug", DEBUG_INTERFACE_VERSION, &DEBUG); + + DEBUG_info = DEBUG.Init((GB_DEBUG_INTERFACE *)(void *)GAMBAS_DebugApi, EXEC_fifo, EXEC_fifo_name); + + if (!DEBUG_info) + ERROR_panic("Cannot initialize debug mode"); + + if (EXEC_profile) + { + EXEC_profile_instr = TRUE; + DEBUG.Profile.Init(EXEC_profile_path); + } +} + + +void DEBUG_exit(void) +{ + if (!EXEC_debug || !DEBUG_is_init()) + return; + + DEBUG.Exit(); + if (EXEC_profile) + DEBUG.Profile.Exit(); +} + +void DEBUG_enter_event_loop(void) +{ + if (EXEC_profile) + DEBUG.Profile.Begin(NULL, NULL); +} + +void DEBUG_leave_event_loop(void) +{ + if (EXEC_profile) + DEBUG.Profile.End(NULL, NULL); +} + +void DEBUG_where(void) +{ + //static bool breakpoint = FALSE; + const char *where = DEBUG_get_current_position(); + /*if (!breakpoint && !strcmp(where, "FForm._new.97")) + { + breakpoint = TRUE; + BREAKPOINT(); + }*/ + fprintf(stderr, "%s: ", where); +} + + +bool DEBUG_get_value(const char *sym, int len, GB_VARIANT *ret) +{ + int i; + VALUE value; + LOCAL_SYMBOL *lp; + GLOBAL_SYMBOL *gp; + CLASS_VAR *var; + char *addr; + CLASS *class; + void *ref; + + if (DEBUG_info->fp) + { + for (i = 0; i < DEBUG_info->fp->debug->n_local; i++) + { + lp = &DEBUG_info->fp->debug->local[i]; + if (len == lp->sym.len && strncasecmp(sym, lp->sym.name, len) == 0) + { + if (lp->value >= 0) + value = DEBUG_info->bp[lp->value]; + else + value = DEBUG_info->pp[lp->value]; + goto __FOUND; + } + } + } + + if (DEBUG_info->cp) + { + for (i = 0; i < DEBUG_info->cp->load->n_global; i++) + { + gp = &DEBUG_info->cp->load->global[i]; + if (len != gp->sym.len || strncasecmp(sym, gp->sym.name, len) != 0) + continue; + + if (CTYPE_get_kind(gp->ctype) == TK_VARIABLE) + { + if (!CTYPE_is_static(gp->ctype)) + { + if (!DEBUG_info->op) + continue; + var = &DEBUG_info->cp->load->dyn[gp->value]; + addr = (char *)DEBUG_info->op + var->pos; + ref = DEBUG_info->op; + } + else + { + var = &DEBUG_info->cp->load->stat[gp->value]; + addr = (char *)DEBUG_info->cp->stat + var->pos; + ref = DEBUG_info->cp; + } + + VALUE_class_read(DEBUG_info->cp, &value, addr, var->type, ref); + goto __FOUND; + } + else if (CTYPE_get_kind(gp->ctype) == TK_CONST) + { + VALUE_class_constant(DEBUG_info->cp, &value, gp->value); + goto __FOUND; + } + } + } + + //class = CLASS_look_global(sym, len); + class = CLASS_look(sym, len); + if (class) + { + if (class->auto_create && class->instance) + { + value._object.class = class; + value._object.object = class->instance; + value._object.super = NULL; + } + else + { + value.type = T_CLASS; + value._class.class = class; + value._class.super = NULL; + } + goto __FOUND; + } + + return TRUE; + +__FOUND: + + BORROW(&value); + VALUE_conv_variant(&value); + UNBORROW(&value); + + *((VALUE *)ret) = value; + return FALSE; +} + +int DEBUG_set_value(const char *sym, int len, VALUE *value) +{ + int i; + LOCAL_SYMBOL *lp; + GLOBAL_SYMBOL *gp; + CLASS_VAR *var; + char *addr; + VALUE *where; + bool ret = GB_DEBUG_SET_OK; + + TRY + { + if (DEBUG_info->fp) + { + for (i = 0; i < DEBUG_info->fp->debug->n_local; i++) + { + lp = &DEBUG_info->fp->debug->local[i]; + if (len == lp->sym.len && strncasecmp(sym, lp->sym.name, len) == 0) + { + if (lp->value >= 0) + where = &DEBUG_info->bp[lp->value]; + else + where = &DEBUG_info->pp[lp->value]; + VALUE_conv(value, where->type); + RELEASE(where); + *where = *value; + BORROW(where); + goto __FOUND; + } + } + } + + if (DEBUG_info->cp) + { + for (i = 0; i < DEBUG_info->cp->load->n_global; i++) + { + gp = &DEBUG_info->cp->load->global[i]; + if (len != gp->sym.len || strncasecmp(sym, gp->sym.name, len) != 0) + continue; + + if (CTYPE_get_kind(gp->ctype) == TK_VARIABLE) + { + if (!CTYPE_is_static(gp->ctype) && DEBUG_info->op) + { + var = &DEBUG_info->cp->load->dyn[gp->value]; + addr = (char *)DEBUG_info->op + var->pos; + } + else + { + var = &DEBUG_info->cp->load->stat[gp->value]; + addr = (char *)DEBUG_info->cp->stat + var->pos; + } + + VALUE_class_write(DEBUG_info->cp, value, addr, var->type); + goto __FOUND; + } + } + } + + ret = GB_DEBUG_SET_READ_ONLY; + +__FOUND: + + 0; + } + CATCH + { + ret = GB_DEBUG_SET_ERROR; + } + END_TRY + + if (ret == GB_DEBUG_SET_ERROR) + EXEC_set_native_error(TRUE); + + return ret; +} + +int DEBUG_get_object_access_type(void *object, CLASS *class, int *count) +{ + CLASS_DESC *desc; + CLASS_DESC_METHOD *dm; + char type; + int access = GB_DEBUG_ACCESS_NORMAL; + + //fprintf(stderr, "DEBUG_can_be_used_like_an_array: %p %s ?\n", object, class->name); + + if (!object) + goto __NORMAL; + + if (class == CLASS_Class || OBJECT_is_class(object)) + { + class = (CLASS *)object; + object = NULL; + CLASS_load(class); + } + + dm = CLASS_get_special_desc(class, SPEC_GET); + if (!dm) + { + //fprintf(stderr, "No _get method\n"); + goto __NORMAL; + } + + if (dm->npmin != 1 || dm->npmax != 1) + { + //fprintf(stderr, "No _get(Arg AS Integer) method\n"); + goto __NORMAL; + } + + if (*dm->signature == T_INTEGER) + access = GB_DEBUG_ACCESS_ARRAY; + else if (*dm->signature == T_STRING) + access = GB_DEBUG_ACCESS_COLLECTION; + else + goto __NORMAL; + + desc = CLASS_get_symbol_desc(class, "Count"); + if (!desc) + goto __NORMAL; + + type = CLASS_DESC_get_type(desc); + + // The two only possible cases: + // A static read-only property, and object == NULL + // or a dynamic read-only property, and object != NULL + + if (!((type == CD_PROPERTY_READ && object) || (type == CD_STATIC_PROPERTY_READ && !object))) + goto __NORMAL; + + TRY + { + if (desc->property.native) + { + if (EXEC_call_native(desc->property.read, object, desc->property.type, 0)) + access = GB_DEBUG_ACCESS_NORMAL; + } + else + { + EXEC.class = desc->property.class; + EXEC.object = object; + EXEC.nparam = 0; + EXEC.native = FALSE; + EXEC.index = (int)(intptr_t)desc->property.read; + //EXEC.func = &class->load->func[(int)desc->property.read]; + + EXEC_function_keep(); + + TEMP = *RP; + UNBORROW(RP); + RP->type = T_VOID; + } + + if (access != GB_DEBUG_ACCESS_NORMAL) + { + VALUE_conv_integer(&TEMP); + *count = TEMP._integer.value; + } + } + CATCH + { + access = GB_DEBUG_ACCESS_NORMAL; + } + END_TRY + + + // For collection-like objects, check _next and Key property + + if (access == GB_DEBUG_ACCESS_COLLECTION) + { + dm = CLASS_get_special_desc(class, SPEC_NEXT); + if (!dm) + goto __NORMAL; + + desc = CLASS_get_symbol_desc(class, "Key"); + if (!desc) + goto __NORMAL; + + type = CLASS_DESC_get_type(desc); + if (type != CD_PROPERTY_READ && type != CD_PROPERTY && type != CD_STATIC_PROPERTY_READ && type != CD_STATIC_PROPERTY && type != CD_VARIABLE && type != CD_STATIC_VARIABLE) + goto __NORMAL; + } + + return access; + +__NORMAL: + return GB_DEBUG_ACCESS_NORMAL; +} + +void DEBUG_print_backtrace(STACK_BACKTRACE *bt) +{ + int i, n; + bool stop; + STACK_BACKTRACE *NO_WARNING(end); + //STACK_CONTEXT *sc = (STACK_CONTEXT *)(STACK_base + STACK_size) - err->bt_count; + + //fprintf(stderr, "0: %s\n", DEBUG_get_position(err->cp, err->fp, err->pc)); + for (i = 0, n = 0, stop = FALSE; !stop; i++) + { + //fprintf(stderr, "%d: %s\n", i, DEBUG_get_position(bt[i].cp, bt[i].fp, bt[i].pc)); + if (STACK_backtrace_is_end(&bt[i])) + { + stop = TRUE; + end = &bt[i]; + STACK_backtrace_clear_end(end); + } + if (bt[i].pc) + { + n++; + fprintf(stderr, "%s ", DEBUG_get_position(bt[i].cp, bt[i].fp, bt[i].pc)); + } + } + fputc('\n', stderr); + + STACK_backtrace_set_end(end); +} + + +void DEBUG_print_current_backtrace(void) +{ + STACK_BACKTRACE *bt = STACK_get_backtrace(); + if (bt) + { + DEBUG_print_backtrace(bt); + STACK_free_backtrace(&bt); + } +} + + +GB_ARRAY DEBUG_get_string_array_from_backtrace(STACK_BACKTRACE *bt) +{ + GB_ARRAY array; + int i, n, size; + STACK_BACKTRACE *end; + + for (i = 0, size = 0;; i++) + { + if (bt[i].pc) + size++; + if (STACK_backtrace_is_end(&bt[i])) + { + end = &bt[i]; + STACK_backtrace_clear_end(end); + break; + } + } + + GB_ArrayNew(&array, GB_T_STRING, size); + //*((char **)GB_ArrayGet(array, 0)) = STRING_new_zero(DEBUG_get_position(err->cp, err->fp, err->pc)); + + for (i = 0, n = 0; n < size; i++) + { + if (!bt[i].pc) + continue; + *((char **)GB_ArrayGet(array, n)) = STRING_new_zero(DEBUG_get_position(bt[i].cp, bt[i].fp, bt[i].pc)); + n++; + } + + STACK_backtrace_set_end(end); + return array; +} + +GB_CLASS DEBUG_find_class(const char *name) +{ + CLASS *class; + CLASS *save = CP; + + // As the startup class is automatically exported, this is the only way for the debugger to find it. + if (PROJECT_class && !strcmp(name, PROJECT_class->name)) + return (GB_CLASS)PROJECT_class; + + CP = NULL; + class = CLASS_find(name); + CP = save; + + return (GB_CLASS)class; +} + +void DEBUG_enum_keys(void *object, GB_DEBUG_ENUM_CB cb) +{ + CENUM *old; + CENUM *cenum = NULL; + CLASS *class; + bool err = FALSE; + char *str; + int len; + char *save_key; + int save_lkey; + GB_VALUE value; + void *prop_ob; + + old = EXEC_enum; + + class = OBJECT_class(object); + if (class == CLASS_Class) + { + class = (CLASS *)object; + object = NULL; + } + + prop_ob = object ? object : class; + + TRY + { + GB_GetProperty(prop_ob, "Key"); + SUBR_get_string_len(&TEMP, &save_key, &save_lkey); + (*cb)(save_key, save_lkey); + + cenum = CENUM_create(prop_ob); + + EXEC_enum = cenum; + EXEC_special(SPEC_FIRST, class, object, 0, TRUE); + EXEC_enum = old; + + if (cenum->stop) + { + err = TRUE; + goto __END; + } + + for(;;) + { + EXEC_enum = cenum; + err = EXEC_special(SPEC_NEXT, class, object, 0, TRUE); + EXEC_enum = old; + + if (err || cenum->stop) + { + err = TRUE; + break; + } + + GB_GetProperty(prop_ob, "Key"); + + SUBR_get_string_len(&TEMP, &str, &len); + (*cb)(str, len); + } + + __END: + + value.type = GB_T_CSTRING; + value._string.value.addr = save_key; + value._string.value.start = 0; + value._string.value.len = save_lkey; + GB_SetProperty(prop_ob, "Key", &value); + + if (err) + OBJECT_UNREF(cenum); + } + CATCH + { + OBJECT_UNREF(cenum); + } + END_TRY +} + +void DEBUG_enter_eval() +{ + DEBUG_inside_eval++; +} + +void DEBUG_leave_eval() +{ + DEBUG_inside_eval--; +} diff --git a/main/gbx/gbx_debug.h b/main/gbx/gbx_debug.h new file mode 100644 index 00000000..1d2a3a38 --- /dev/null +++ b/main/gbx/gbx_debug.h @@ -0,0 +1,88 @@ +/*************************************************************************** + + gbx_debug.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_DEBUG_H +#define __GBX_DEBUG_H + +#include "gbx_class.h" +#include "gbx_stack.h" +#include "gb_pcode.h" +#include "../lib/debug/gb.debug.h" + +/* Object referencement debugging */ +#define DEBUG_REF 0 +/* String referencement debugging */ +#define DEBUG_STRING 0 +/* Bytecode debugging */ +#define DEBUG_PCODE 0 +/* Class loading debugging */ +#define DEBUG_LOAD 0 +/* Event debugging */ +#define DEBUG_EVENT 0 +/* Component management debugging */ +#define DEBUG_COMP 0 +/* Stream opening debugging */ +#define DEBUG_STREAM 0 + +#ifndef __GBX_DEBUG_C + +EXTERN DEBUG_INTERFACE DEBUG; +EXTERN DEBUG_INFO *DEBUG_info; +EXTERN int DEBUG_inside_eval; + +#endif + +#define DEBUG_is_init() (DEBUG_info != NULL) + +void DEBUG_init(void); +void DEBUG_exit(void); + +void DEBUG_enter_event_loop(void); +void DEBUG_leave_event_loop(void); + +const char *DEBUG_get_position(CLASS *cp, FUNCTION *fp, PCODE *pc); +const char *DEBUG_get_current_position(void); +void DEBUG_where(void); + +bool DEBUG_get_value(const char *sym, int len, GB_VARIANT *ret); +int DEBUG_set_value(const char *sym, int len, VALUE *value); +int DEBUG_get_object_access_type(void *object, CLASS *class, int *count); +GB_CLASS DEBUG_find_class(const char *name); +void DEBUG_enum_keys(void *object, GB_DEBUG_ENUM_CB cb); + +void DEBUG_print_backtrace(STACK_BACKTRACE *bt); +void DEBUG_print_current_backtrace(void); +GB_ARRAY DEBUG_get_string_array_from_backtrace(STACK_BACKTRACE *bt); + +void DEBUG_enter_eval(void); +void DEBUG_leave_eval(void); + +#define PROFILE_ENTER_FUNCTION() \ + if (EXEC_profile && CP && CP->component == COMPONENT_main) \ + DEBUG.Profile.Begin(CP, FP); \ + +#define PROFILE_LEAVE_FUNCTION() \ + if (EXEC_profile && CP && CP->component == COMPONENT_main) \ + DEBUG.Profile.End(CP, FP); \ + +#endif diff --git a/main/gbx/gbx_eval.c b/main/gbx/gbx_eval.c new file mode 100644 index 00000000..261bee25 --- /dev/null +++ b/main/gbx/gbx_eval.c @@ -0,0 +1,163 @@ +/*************************************************************************** + + gbx_eval.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_EVAL_C + +#include "gb_common.h" + +#include "gb_array.h" +#include "gbx_string.h" +#include "gbx_class.h" +#include "gbx_exec.h" +#include "gbx_debug.h" +#include "gbx_eval.h" + +#include "gbx_c_collection.h" +#include "gbx_api.h" + +bool EVAL_debug = FALSE; + +static EXPRESSION *EVAL; + +static void EVAL_enter() +{ + STACK_push_frame(&EXEC_current, EVAL->func.stack_usage); + + BP = SP; + PP = SP; + FP = &EVAL->func; + PC = EVAL->func.code; + + OP = NULL; + CP = &EVAL->exec_class; + //AP = ARCH_from_class(CP); + + EP = NULL; + EC = NULL; + GP = NULL; + + RP->type = T_VOID; + + PROFILE_ENTER_FUNCTION(); +} + +static void error_EVAL_exec(void) +{ + PROFILE_LEAVE_FUNCTION(); + STACK_pop_frame(&EXEC_current); +} + +static void EVAL_exec() +{ + // We need to push a void frame, because EXEC_leave looks at *PC to know if a return value is expected + STACK_push_frame(&EXEC_current, 0); + + PC = NULL; + GP = NULL; + + ON_ERROR(error_EVAL_exec) + { + EVAL_enter(); + } + END_ERROR + + EXEC_function_loop(); + + TEMP = *RP; + UNBORROW(&TEMP); +} + +bool EVAL_expression(EXPRESSION *expr, EVAL_FUNCTION func) +{ + int i; + EVAL_SYMBOL *sym; + bool debug; + bool error; + int nvar; + EXPRESSION *save_eval; + /*HASH_TABLE *hash_table; + char *name; + CCOL_ENUM enum_state;*/ + + save_eval = EVAL; + EVAL = expr; + + #ifdef DEBUG + fprintf(stderr, "EVAL: %s\n", EVAL->source); + #endif + + STACK_enable_for_eval(); + debug = EXEC_debug; + EXEC_debug = FALSE; + error = FALSE; + + nvar = EVAL->nvar; + + STACK_check(nvar); + + if (expr->custom) + nvar--; + + for (i = 0; i < nvar; i++) + { + + SP->type = T_VARIANT; + SP->_variant.vtype = T_NULL; + SP++; + + sym = (EVAL_SYMBOL *)TABLE_get_symbol(EVAL->table, EVAL->var[EVAL->nvar - i - 1]); + if ((*func)(sym->sym.name, sym->sym.len, (GB_VARIANT *)&SP[-1])) + { + GB_Error("Unknown symbol"); + error = TRUE; + goto __LEAVE; + } + + BORROW(SP - 1); + } + + if (expr->custom) + { + SP->type = T_OBJECT; + SP->_object.object = expr->parent; + OBJECT_REF(expr->parent); + SP++; + } + + TRY + { + EVAL_exec(); + } + CATCH + { + error = TRUE; + } + END_TRY + +__LEAVE: + + STACK_disable_for_eval(); + EXEC_debug = debug; + EVAL = save_eval; + return error; +} diff --git a/main/gbx/gbx_eval.h b/main/gbx/gbx_eval.h new file mode 100644 index 00000000..7fbd0bb7 --- /dev/null +++ b/main/gbx/gbx_eval.h @@ -0,0 +1,41 @@ +/*************************************************************************** + + gbx_eval.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_EVAL_H +#define __GBX_EVAL_H + +#include "gb_table.h" +#include "gbx_class.h" +#include "gb_reserved.h" +#include "gbx_c_collection.h" + +#include "../lib/eval/gb.eval.h" +#include "gbx_expression.h" + +#ifndef __GBX_EVAL_C +EXTERN bool EVAL_debug; +#endif + +bool EVAL_expression(EXPRESSION *expr, EVAL_FUNCTION func); + +#endif diff --git a/main/gbx/gbx_event.c b/main/gbx/gbx_event.c new file mode 100644 index 00000000..ba07f756 --- /dev/null +++ b/main/gbx/gbx_event.c @@ -0,0 +1,271 @@ +/*************************************************************************** + + gbx_event.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_EVENT_C + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gbx_exec.h" +#include "gbx_api.h" + +#include "gbx_event.h" + +//#define DEBUG_ME 1 + +void *EVENT_Last = NULL; + +char *EVENT_PreviousName = NULL; +char *EVENT_Name = NULL; + +static EVENT_POST *_post_list = NULL; + + +static void check_event_method(CLASS *class, const char *name, CLASS_DESC_METHOD *desc, CLASS_EVENT *event) +{ + int n = event->n_param; + + if (n < desc->npmin) + { + /*printf("event->n_param = %d desc->npmin = %d desc->npmax = %d\n", event->n_param, desc->npmin, desc->npmax);*/ + THROW(E_EVENT, CLASS_get_name(class), name, "Too many arguments"); + } + + if (n > desc->npmax) + { + if (!desc->npvar) + THROW(E_EVENT, CLASS_get_name(class), name, "Not enough arguments"); + + n = desc->npmax; + } + + if (TYPE_compare_signature(desc->signature, n, (TYPE *)event->param, n, FALSE)) + THROW(E_EVENT, CLASS_get_name(class), name, "Type mismatch"); + + if (desc->type != T_VOID) + THROW(E_EVENT, CLASS_get_name(class), name, "Not a procedure"); +} + + +void EVENT_search(CLASS *class, ushort *event, const char *name, OBJECT *parent) +{ + //ushort *event; + //ushort *self; + CLASS *class_parent; + int i, index; + const char *event_name; + CLASS_DESC *desc; + int kind; + int len_name, len_event_name; + char *func_name = COMMON_buffer; + char *pevent_name; + + #if DEBUG_ME + fprintf(stderr, "EVENT_search: class = %s name = %s parent = %p\n", class->name, name, parent); + #endif + + //event = OBJECT_event(object)->event; + //self = OBJECT_event(object)->self; + + if (class->n_event <= 0) + return; + + len_name = name ? strlen(name) : 0; + + if (len_name == 0 || len_name >= (COMMON_BUF_MAX - 4)) + { + memset(event, 0, class->n_event * sizeof(ushort)); + return; + } + + kind = parent->class == CLASS_Class ? CD_STATIC_METHOD : CD_METHOD; + + if (kind == CD_STATIC_METHOD) + class_parent = (CLASS *)parent; + else + class_parent = parent->class; + + strcpy(func_name, name); + pevent_name = &func_name[len_name]; + *pevent_name++ = '_'; + + for (i = 0; i < class->n_event; i++) + { + event[i] = 0; + + event_name = class->event[i].name; + if (*event_name == ':') + event_name++; + + len_event_name = strlen(event_name); + if ((len_name + len_event_name + 1) >= COMMON_BUF_MAX) + continue; + + strcpy(pevent_name, event_name); + index = CLASS_find_symbol(class_parent, func_name); + + if (index != NO_SYMBOL) + { + desc = class_parent->table[index].desc; + if (CLASS_DESC_get_type(desc) == kind) + { + #if DEBUG_ME + fprintf(stderr, "EVENT_search: FOUND [%d] %s.%s index = %d parent = %s.%p\n", i, class_parent->name, func_name, index, parent->class->name, parent); + #endif + check_event_method(class_parent, func_name, &desc->method, &class->event[i]); + event[i] = index + 1; + } + } + + #if 0 + snprintf(func_name, COMMON_BUF_MAX, "ME_%s", event_name); + index = CLASS_find_symbol(class, func_name); + + if (index != NO_SYMBOL) + { + desc = class->table[index].desc; + if (CLASS_DESC_get_type(desc) == CD_METHOD) + { + #if DEBUG_ME + printf("EVENT_search: FOUND [%d] %s.%s index = %d parent = %s.%p\n", i, class->name, func_name, index, parent->class->name, parent); + #endif + check_event_method(class, func_name, &desc->method, &class->event[i]); + if (!self) + { + ALLOC_ZERO(&self, sizeof(ushort) * class->n_event, "EVENT_search"); + OBJECT_event(object)->self = self; + } + self[i] = index + 1; + } + } + #endif + } +} + + + +void EVENT_exit() +{ + EVENT_POST *ep; + + while (_post_list) + { + ep = _post_list; + _post_list = _post_list->list.next; + FREE(&ep); + } + + STRING_free(&EVENT_Name); +} + + +static void post(void (*func)(), int nparam, intptr_t param, intptr_t param2) +{ + EVENT_POST *ep; + + /*printf("EVENT_post\n"); + fflush(NULL);*/ + + ALLOC(&ep, sizeof(EVENT_POST)); + + ep->func = func; + ep->nparam = nparam; + ep->param = param; + ep->param2 = param2; + + LIST_insert(&_post_list, ep, &ep->list); + + HOOK(post)(); +} + + +void EVENT_post(void (*func)(), intptr_t param) +{ + post(func, 1, param, 0); +} + +void EVENT_post2(void (*func)(), intptr_t param, intptr_t param2) +{ + post(func, 2, param, param2); +} + +static void post_event(void *object, int event) +{ + GB_Raise(object, event, 0); + OBJECT_UNREF(object); +} + +void EVENT_post_event(void *object, int event) +{ + OBJECT_REF(object); + post((void (*)())post_event, 2, (intptr_t)object, (intptr_t)event); +} + + +bool EVENT_check_post(void) +{ + EVENT_POST *ep; + EVENT_POST save; + bool ret = FALSE; + + #ifdef DEBUG_POST + fprintf(stderr, "EVENT_check_post: START\n"); + #endif + + while (_post_list) + { + ret = TRUE; + ep = _post_list; + save = *ep; + LIST_remove(&_post_list, _post_list, &_post_list->list); + + FREE(&ep); + + if (save.nparam == 1) + (*save.func)(save.param); + else + (*save.func)(save.param, save.param2); + } + + /*norec = FALSE;*/ + + #ifdef DEBUG_POST + fprintf(stderr, "EVENT_check_post: END\n"); + #endif + + return ret; +} + + +char *EVENT_enter_name(const char *name) +{ + char *save = EVENT_PreviousName; + EVENT_PreviousName = EVENT_Name; + EVENT_Name = (char *)name; + return save; +} + +void EVENT_leave_name(const char *save) +{ + EVENT_Name = EVENT_PreviousName; + EVENT_PreviousName = (char *)save; +} diff --git a/main/gbx/gbx_event.h b/main/gbx/gbx_event.h new file mode 100644 index 00000000..0e41e702 --- /dev/null +++ b/main/gbx/gbx_event.h @@ -0,0 +1,59 @@ +/*************************************************************************** + + gbx_event.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_EVENT_H +#define __GBX_EVENT_H + +#include "gbx_class.h" +#include "gbx_object.h" +#include "gb_list.h" + + +#ifndef __GBX_EVENT_C +extern void *EVENT_Last; +extern char *EVENT_PreviousName; +extern char *EVENT_Name; +#endif + +typedef + struct _EVENT_POST { + LIST list; + void (*func)(); + int nparam; + intptr_t param; + intptr_t param2; + } + EVENT_POST; + + +void EVENT_search(CLASS *class, ushort *event, const char *name, OBJECT *parent); +void EVENT_post(void (*func)(), intptr_t param); +void EVENT_post2(void (*func)(), intptr_t param, intptr_t param2); +bool EVENT_check_post(void); +void EVENT_init(void); +void EVENT_exit(void); +void EVENT_post_event(void *object, int event); +char *EVENT_enter_name(const char *name); +void EVENT_leave_name(const char *save); + +#endif diff --git a/main/gbx/gbx_exec.c b/main/gbx/gbx_exec.c new file mode 100644 index 00000000..c5b6990e --- /dev/null +++ b/main/gbx/gbx_exec.c @@ -0,0 +1,2042 @@ +/*************************************************************************** + + gbx_exec.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_EXEC_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gbx_type.h" + +#include +#include + +#include "gb_limit.h" +#include "gbx_subr.h" +#include "gbx_stack.h" +#include "gbx_debug.h" +#include "gbx_event.h" +#include "gbx_string.h" +#include "gbx_date.h" + +#include "gbx_c_collection.h" + +#include "gbx_api.h" +#include "gbx_jit.h" +#include "gbx_exec.h" + +//#define DEBUG_STACK 1 +//#define SHOW_FUNCTION 1 + +/* Current virtual machine state */ +STACK_CONTEXT EXEC_current = { 0 }; +/* Stack pointer */ +VALUE *SP = NULL; +/* Temporary storage or return value of a native function */ +VALUE TEMP; +/* Return value of a gambas function */ +VALUE RET; +/* SUPER was used for this stack pointer */ +VALUE *EXEC_super = NULL; +/* CPU endianness */ +bool EXEC_big_endian; +/* Current iterator */ +CENUM *EXEC_enum; + +const char *EXEC_profile_path = NULL; // profile file path +const char *EXEC_fifo_name = NULL; // fifo name +EXEC_HOOK EXEC_Hook = { NULL }; +EXEC_GLOBAL EXEC; +uint64_t EXEC_byref = 0; +uchar EXEC_quit_value = 0; + +bool EXEC_debug = FALSE; // debugging mode +bool EXEC_task = FALSE; // I am a background task +bool EXEC_profile = FALSE; // profiling mode +bool EXEC_profile_instr = FALSE; // profiling mode at instruction level +bool EXEC_arch = FALSE; // executing an archive +bool EXEC_fifo = FALSE; // debugging through a fifo +bool EXEC_keep_library = FALSE; // do not unload libraries +bool EXEC_string_add = FALSE; // next '&' operator is done for a '&=' +bool EXEC_main_hook_done = FALSE; +bool EXEC_got_error = FALSE; +bool EXEC_break_on_error = FALSE; // if we must break into the debugger as soon as there is an error. + +const char EXEC_should_borrow[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, 0, 0, 1 }; + +const char *EXEC_unknown_name; +bool EXEC_unknown_property; +char EXEC_unknown_nparam; + +void EXEC_init(void) +{ + union { + char _string[4]; + uint _int; + } + test; + + PC = NULL; + BP = NULL; + OP = NULL; + CP = NULL; + RP->type = T_VOID; + + test._string[0] = 0xAA; + test._string[1] = 0xBB; + test._string[2] = 0xCC; + test._string[3] = 0xDD; + + EXEC_big_endian = test._int == 0xAABBCCDDL; + + if (EXEC_big_endian) + ERROR_warning("CPU is big endian"); + + DATE_init(); +} + + +void EXEC_borrow(TYPE type, VALUE *value) +{ + static const void *jump[16] = { + &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, + &&__STRING, &&__NONE, &&__NONE, &&__VARIANT, &&__FUNCTION, &&__NONE, &&__NONE + }; + + goto *jump[type]; + +__VARIANT: + if (value->_variant.vtype == T_STRING) + STRING_ref(value->_variant.value._string); + else if (TYPE_is_object(value->_variant.vtype)) + OBJECT_REF(value->_variant.value._object); + return; + +__FUNCTION: + OBJECT_REF(value->_function.object); + return; + +__STRING: + STRING_ref(value->_string.addr); + +__NONE: + return; +} + + +void UNBORROW(VALUE *value) +{ + static const void *jump[16] = { + &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, + &&__STRING, &&__NONE, &&__NONE, &&__VARIANT, &&__FUNCTION, &&__NONE, &&__NONE + }; + + TYPE type = value->type; + + if (UNLIKELY(TYPE_is_object(type))) + { + OBJECT_UNREF_KEEP(value->_object.object); + return; + } + + goto *jump[type]; + +__VARIANT: + if (value->_variant.vtype == T_STRING) + STRING_unref_keep(&value->_variant.value._string); + else if (TYPE_is_object(value->_variant.vtype)) + OBJECT_UNREF_KEEP(value->_variant.value._object); + return; + +__FUNCTION: + OBJECT_UNREF_KEEP(value->_function.object); + return; + +__STRING: + STRING_unref_keep(&value->_string.addr); + +__NONE: + return; +} + + +void EXEC_release(TYPE type, VALUE *value) +{ + static const void *jump[16] = { + &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, + &&__STRING, &&__NONE, &&__NONE, &&__VARIANT, &&__FUNCTION, &&__NONE, &&__NONE + }; + + goto *jump[type]; + +__VARIANT: + if (value->_variant.vtype == T_STRING) + STRING_unref(&value->_variant.value._string); + else if (TYPE_is_object(value->_variant.vtype)) + OBJECT_UNREF(value->_variant.value._object); + return; + +__FUNCTION: + OBJECT_UNREF(value->_function.object); + return; + +__STRING: + STRING_unref(&value->_string.addr); + +__NONE: + return; +} + +void RELEASE_many(VALUE *value, int n) +{ + static const void *jump[16] = { + &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, + &&__STRING, &&__NONE, &&__NONE, &&__VARIANT, &&__FUNCTION, &&__NONE, &&__NONE + }; + + TYPE type; + + while (n) + { + n--; + value--; + + type = value->type; + + if (UNLIKELY(TYPE_is_object(type))) + { + OBJECT_UNREF(value->_object.object); + continue; + } + + goto *jump[type]; + + __VARIANT: + if (value->_variant.vtype == T_STRING) + STRING_unref(&value->_variant.value._string); + else if (TYPE_is_object(value->_variant.vtype)) + OBJECT_UNREF(value->_variant.value._object); + continue; + + __FUNCTION: + OBJECT_UNREF(value->_function.object); + continue; + + __STRING: + STRING_unref(&value->_string.addr); + + __NONE: + continue; + } +} + +#if 0 +void DUMP(VALUE *value) +{ + static void *jump[16] = { + &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, + &&__STRING, &&__NONE, &&__VARIANT, &&__ARRAY, &&__NONE, &&__FUNCTION, &&__NONE, &&__NONE + }; + + TYPE type = value->type; + + printf("type = %p / ", (void *)type); + + if (TYPE_is_object(type)) + goto __OBJECT; + else + goto *jump[type]; + +__STRING: + printf("STRING %p\n", value->_string.addr); + return; + +__OBJECT: + if (value->_object.object) + { + printf("OBJECT (%p)\n", value->_object.object); + printf("-> %s\n", OBJECT_class(value->_object.object)->name); + } + else + printf("OBJECT (NULL)\n"); + return; + +__VARIANT: + if (value->_variant.vtype == T_STRING) + printf("STRING %p\n", *((char **)value->_variant.value)); + else if (TYPE_is_object(value->_variant.vtype)) + printf("OBJECT (%s %p)\n", OBJECT_class(*((void **)value->_variant.value))->name, *((void **)value->_variant.value)); + return; + +__FUNCTION: + printf("FUNCTION %s (%s %p)\n", value->_function.class->name, OBJECT_class(value->_function.object)->name, value->_function.object); + return; + +__ARRAY: + printf("ARRAY\n"); + return; + +__NONE: + printf("\n"); + return; +} +#endif + +void EXEC_release_return_value(void) +{ + RELEASE(RP); + RP->type = T_VOID; +} + + +#define print_register() \ + fprintf(stderr, "| SP = %d BP = %d FP = %p PC = %p EC = %p\n", (int)(SP - (VALUE *)STACK_base), (int)(BP - (VALUE *)STACK_base), FP, PC, EC) + +static ushort exec_enter_can_quick(void) +{ + int i; + FUNCTION *func; + int nparam = EXEC.nparam; + + func = &EXEC.class->load->func[EXEC.index]; + + /* check number of arguments */ + + if (func->npmin < func->n_param || nparam != func->n_param || func->vararg) + return C_CALL_SLOW; + + /* check arguments type */ + + for (i = 0; i < nparam; i++) + { + if (SP[i - nparam].type != func->param[i].type) + return C_CALL_SLOW; + } + + return C_CALL_QUICK; +} + +static void init_local_var(CLASS *class, FUNCTION *func) +{ + static const void *jump[] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, + &&__DATE, &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL, + &&__OBJECT + }; + + CLASS_LOCAL *local; + int n; + CTYPE ctype; + VALUE *value; + + local = func->local; + value = SP; + + for (n = func->n_local; n; n--, value++, local++) + { + ctype = local->type; + + value->type = ctype.id; + goto *jump[ctype.id]; + + __BOOLEAN: + __BYTE: + __SHORT: + __INTEGER: + + value->_integer.value = 0; + continue; + + __LONG: + + value->_long.value = 0; + continue; + + __SINGLE: + value->_single.value = 0; + continue; + + __FLOAT: + value->_float.value = 0; + continue; + + __STRING: + value->_string.addr = NULL; + value->_string.start = 0; + value->_string.len = 0; + continue; + + __VARIANT: + value->_variant.vtype = T_NULL; + continue; + + __POINTER: + value->_pointer.value = NULL; + continue; + + __DATE: + value->_date.date = 0; + value->_date.time = 0; + continue; + + __VOID: + continue; + + __OBJECT: + if (ctype.value >= 0) + value->type = (TYPE)class->load->class_ref[ctype.value]; + value->_object.object = NULL; + continue; + + __FUNCTION: + __CLASS: + __NULL: + ERROR_panic("VALUE_default: Unknown default type"); + } + + SP = value; +} + +// EXEC.nparam must be set to the amount of stack that must be freed if an exception is raised during EXEC_enter() + +void EXEC_enter(void) +{ + int i; + FUNCTION *func; // = EXEC.func; + bool optional; + int nparam = EXEC.nparam; + void *object = EXEC.object; + CLASS *class = EXEC.class; + int64_t optargs; + + #if DEBUG_STACK + fprintf(stderr, "\n| >> EXEC_enter(%s, %d, %d)\n", EXEC.class->name, EXEC.index, nparam); + print_register(); + #endif + + func = &class->load->func[EXEC.index]; + #if DEBUG_STACK + if (func->debug) + fprintf(stderr, " | >> %s\n", func->debug->name); + #endif + + #if SHOW_FUNCTION + if (func->debug) + fprintf(stderr, "%s.%s\n", EXEC.class->name, func->debug->name); + #endif + + optional = func->optional; + + // Check number of arguments + + if (UNLIKELY(nparam < func->npmin)) + THROW(E_NEPARAM); + else if (UNLIKELY(nparam > func->n_param && !func->vararg)) + THROW(E_TMPARAM); + + // Mandatory arguments + + for (i = 0; i < func->npmin; i++) + VALUE_conv(SP - nparam + i, func->param[i].type); + + if (optional) + { + optargs = 0; + + // Optional arguments + + for (i = func->npmin; i < Min(func->n_param, nparam); i++) + { + if (SP[- nparam + i].type == T_VOID) + { + optargs |= (1 << i); + SP[- nparam + i]._void.ptype = func->param[i].type; + } + else + VALUE_conv(SP - nparam + i, func->param[i].type); + } + + // Missing optional arguments + + if (nparam < func->n_param) + { + STACK_check(func->n_param - nparam); + + for (i = nparam; i < func->n_param; i++) + { + optargs |= (1 << i); + SP->type = T_VOID; + SP->_void.ptype = func->param[i].type; + SP++; + } + + //EXEC.nparam = func->n_param; + nparam = func->n_param; + } + } + + // Save context & check stack + + STACK_push_frame(&EXEC_current, func->stack_usage); + + // Enter function + + BP = SP; + if (func->vararg) + PP = SP - (nparam - func->n_param); + else + PP = SP; + FP = func; + PC = func->code; + OP = object; + CP = class; + EP = NULL; + GP = NULL; + + PROFILE_ENTER_FUNCTION(); + + if (func->error) + { + #if DEBUG_ERROR + printf("EXEC_enter: EC = PC + %d\n", func->error); + #endif + EC = PC + func->error; + } + else + EC = NULL; + + // Reference the object so that it is not destroyed during the function call + OBJECT_REF(OP); + + // Local variables initialization + + if (func->n_local > 0) + init_local_var(class, func); + + // Control variables initialization + + if (func->n_ctrl > 0) + { + for (i = 0; i < func->n_ctrl; i++) + { + SP->type = T_VOID; + SP++; + } + + // Optional argument map + if (optional && func->use_is_missing) + { + SP--; + SP->type = T_LONG; + SP->_long.value = optargs; + SP++; + } + } + + RP->type = T_VOID; + + #if DEBUG_STACK + fprintf(stderr, "| << EXEC_enter()\n"); + print_register(); + #endif +} + + +void EXEC_enter_check(bool defined) +{ + ushort mode = defined ? exec_enter_can_quick() : C_CALL_SLOW; + + *PC = (*PC & 0xFF) | mode; + + switch(mode) + { + case C_CALL_QUICK: EXEC_enter_quick(); break; + //case C_CALL_EASY: EXEC_enter_easy(); break; + default: EXEC_enter(); break; + } +} + + +void EXEC_enter_quick(void) +{ + int i; + FUNCTION *func;; + void *object = EXEC.object; + CLASS *class = EXEC.class; + + #if DEBUG_STACK + fprintf(stderr, "\n| >> EXEC_enter_quick(%s, %d, %d)\n", EXEC.class->name, EXEC.index, EXEC.nparam); + print_register(); + #endif + + func = &class->load->func[EXEC.index]; + #if DEBUG_STACK + if (func->debug) + fprintf(stderr, " | >> %s\n", func->debug->name); + #endif + + #if SHOW_FUNCTION + if (func->debug) + fprintf(stderr, "%s.%s\n", EXEC.class->name, func->debug->name); + #endif + + /* save context & check stack */ + + STACK_push_frame(&EXEC_current, func->stack_usage); + + /* enter function */ + + BP = SP; + PP = SP; + FP = func; + PC = func->code; + OP = object; + CP = class; + EP = NULL; + GP = NULL; + + if (func->error) + EC = PC + func->error; + else + EC = NULL; + + PROFILE_ENTER_FUNCTION(); + + /* reference the object so that it is not destroyed during the function call */ + OBJECT_REF(OP); + + /* local variables initialization */ + + if (LIKELY(func->n_local != 0)) + init_local_var(class, func); + + /* control variables initialization */ + + if (LIKELY(func->n_ctrl != 0)) + { + for (i = 0; i < func->n_ctrl; i++) + { + SP->type = T_VOID; + SP++; + } + } + + RP->type = T_VOID; + + #if DEBUG_STACK + fprintf(stderr, "| << EXEC_enter()\n"); + print_register(); + #endif +} + +static int exec_leave_byref(ushort *pc, int nparam) +{ + int nbyref; + ushort *pc_func; + int nbyref_func; + VALUE *xp, *pp; + int i, n, bit; + int nb; + + pc++; + nbyref = 1 + (*pc & 0xF); + pc_func = FP->code; + + if (!PCODE_is(*pc_func, C_BYREF)) + return 0; + + nbyref_func = 1 + (*pc_func & 0xF); + if (nbyref_func < nbyref) + return 0; + + for (i = 1; i <= nbyref; i++) + { + if (pc[i] & ~pc_func[i]) + return 0; + } + + xp = PP - nparam; + pp = xp; + + for (i = 0, n = 0, nb = 0; i < nparam; i++) + { + bit = i & 15; + if (bit == 0) + n++; + + if (n <= nbyref && (pc[n] & (1 << bit))) + { + xp[nb] = *pp; + nb++; + } + else + { + RELEASE(pp); + } + pp++; + } + + pc--; + + n = SP - PP; + RELEASE_MANY(SP, n); + SP -= nparam; + + SP += nb; + OBJECT_UNREF(OP); + SP -= nb; + + PROFILE_LEAVE_FUNCTION(); + STACK_pop_frame(&EXEC_current); + + PC += nbyref + 1; + return nb; +} + +#if 0 +#define EXEC_LEAVE_BYREF() \ + pc++; \ + nbyref = 1 + (*pc & 0xF); \ + pc_func = FP->code; \ + \ + if (LIKELY(!PCODE_is(*pc_func, C_BYREF))) \ + goto __LEAVE_NORMAL; \ + \ + nbyref_func = 1 + (*pc_func & 0xF); \ + if (nbyref_func < nbyref) \ + goto __LEAVE_NORMAL; \ + \ + for (i = 1; i <= nbyref; i++) \ + { \ + if (pc[i] & ~pc_func[i]) \ + goto __LEAVE_NORMAL; \ + } \ + \ + xp = PP - nparam; \ + pp = xp; \ + \ + for (i = 0, n = 0; i < nparam; i++) \ + { \ + bit = i & 15; \ + if (bit == 0) \ + n++; \ + \ + if (n <= nbyref && (pc[n] & (1 << bit))) \ + { \ + xp[nb] = *pp; \ + nb++; \ + } \ + else \ + { \ + RELEASE(pp); \ + } \ + pp++; \ + } \ + \ + pc--; \ + n = SP - PP; \ + RELEASE_MANY(SP, n); \ + SP -= nparam; \ + SP += nb; \ + OBJECT_UNREF(OP); \ + SP -= nb; \ + PROFILE_LEAVE_FUNCTION(); \ + STACK_pop_frame(&EXEC_current); \ + PC += nbyref + 1; \ + goto __RETURN_VALUE; +#endif + +void EXEC_leave_drop() +{ + int nparam; + //VALUE ret; + ushort *pc; + int n, nb; + +#if DEBUG_STACK + fprintf(stderr, "| >> EXEC_leave\n"); + print_register(); +#endif + + /* Save the return value. It can be erased by OBJECT_UNREF() */ + + //ret = *RP; + EXEC_release_return_value(); + + //VALUE_copy(&ret, RP); + + pc = STACK_get_previous_pc(); + nparam = FP->n_param; + + /* ByRef arguments management */ + + nb = (pc && PCODE_is(pc[1], C_BYREF)) ? exec_leave_byref(pc, nparam) : 0; + if (nb == 0) + { + n = nparam + (SP - PP); + RELEASE_MANY(SP, n); + + OBJECT_UNREF(OP); + PROFILE_LEAVE_FUNCTION(); + STACK_pop_frame(&EXEC_current); + } + + SP += nb; + +#if DEBUG_STACK + fprintf(stderr, "| << EXEC_leave()\n"); + print_register(); + fprintf(stderr, "\n"); +#endif + return; +} + +void EXEC_leave_keep() +{ + VALUE ret; + int nparam; + ushort *pc; + int n, nb; + +#if DEBUG_STACK + fprintf(stderr, "| >> EXEC_leave\n"); + print_register(); +#endif + + // RP may be indirectly freed by OBJECT_UNREF() + VALUE_copy(&ret, RP); + + pc = STACK_get_previous_pc(); + nparam = FP->n_param; + + // ByRef arguments management + + nb = (pc && PCODE_is(pc[1], C_BYREF)) ? exec_leave_byref(pc, nparam) : 0; + if (nb == 0) + { + n = nparam + (SP - PP); + RELEASE_MANY(SP, n); + + OBJECT_UNREF(OP); + PROFILE_LEAVE_FUNCTION(); + STACK_pop_frame(&EXEC_current); + } + + if (pc) + { + if (SP[-1].type == T_FUNCTION) + { + SP--; + OBJECT_UNREF(SP->_function.object); + } + + COPY_VALUE(SP, &ret); + RP->type = T_VOID; + if (PCODE_is_variant(*PC) && SP->type != T_VOID) + VALUE_conv_variant(SP); + SP++; + } + else + { + VALUE_copy(RP, &ret); + } + + SP += nb; + +#if DEBUG_STACK + fprintf(stderr, "| << EXEC_leave()\n"); + print_register(); + fprintf(stderr, "\n"); +#endif + return; +} + +static void error_EXEC_function_real(void) +{ + RELEASE_MANY(SP, EXEC.nparam); + STACK_pop_frame(&EXEC_current); +} + +void EXEC_function_real() +{ + EXEC.func = &EXEC.class->load->func[EXEC.index]; + if (EXEC.func->fast) + { + JIT_exec(FALSE); + return; + } + + // We need to push a void frame, because EXEC_leave looks at *PC to know if a return value is expected + STACK_push_frame(&EXEC_current, 0); + + PC = NULL; + GP = NULL; + + ON_ERROR(error_EXEC_function_real) + { + EXEC_enter(); + } + END_ERROR + + EXEC_function_loop(); +} + +void EXEC_function_loop() +{ + bool retry = FALSE; + + if (LIKELY(PC != NULL)) + { + do + { + TRY + { + EXEC_loop(); + retry = FALSE; + } + CATCH + { + // QUIT was called + if (ERROR->info.code == E_ABORT) + { + #if DEBUG_ERROR + fprintf(stderr, "#0 QUIT\n"); + #endif + ERROR_lock(); + while (PC != NULL) + EXEC_leave_drop(); + ERROR_unlock(); + + //STACK_pop_frame(&EXEC_current); + PROPAGATE(); + } + + if (EXEC_break_on_error) + DEBUG.Main(TRUE); + + if (ERROR->info.code == E_ASSERT) + { + EP = NULL; + EC = NULL; + goto __IGNORE_TRY_CATCH; + } + + // Are we in a TRY? + if (EP && EC) + { + #if DEBUG_ERROR + fprintf(stderr, "#1 EP = %d SP = %d\n", EP - (VALUE *)STACK_base, SP - (VALUE *)STACK_base); + fprintf(stderr, "TRY\n"); + #endif + ERROR_set_last(FALSE); + + // No need to unwind the Gosub stack until the TRY stack position, because TRY GOSUB is forbidden + /*while (GP > EP) + { + ... + }*/ + + // The stack is popped until reaching the stack position before the TRY + while (SP > EP) + POP(); + + // We go directly to the END TRY + PC = EC; + EP = NULL; + retry = TRUE; + goto __CONTINUE; + } + + // Is there a CATCH in the function? + if (EC != NULL) + { + #if DEBUG_ERROR + fprintf(stderr, "#2 EC = %p\n", EC); + fprintf(stderr, "CATCH\n"); + #endif + + ERROR_set_last(TRUE); + + // The stack is popped until reaching the stack position at the function start + while (SP > (BP + FP->n_local + FP->n_ctrl)) + POP(); + + // Reset the Gosub stack pointer as all Gosub control variables saved have been released + GP = NULL; + + PC = EC; + EC = NULL; + retry = TRUE; + goto __CONTINUE; + } + + __IGNORE_TRY_CATCH: + + // There is no error handler in the function + + #if DEBUG_ERROR + fprintf(stderr, "#3\n"); + fprintf(stderr, "NOTHING\n"); + #endif + //ERROR_INFO save = { 0 }; + //ERROR_save(&save); + + ERROR_set_last(TRUE); + + if (EXEC_debug && !FP->fast && !STACK_has_error_handler()) + { + ERROR_hook(); + + for(;;) + DEBUG.Main(TRUE); + } + else + { + // We leave stack frames until we find either: + // - A stack frame that has an error handler. + // - A void stack frame created by EXEC_enter_real() + + // First we leave stack frames for JIT functions + // on top of the stack. We have just propagated + // past these some lines below. + + /*ERROR_lock(); + while (PC != NULL && EC == NULL && FP->fast) + EXEC_leave_drop(); + ERROR_unlock();*/ + + // We can only leave stack frames for non-JIT functions. + ERROR_lock(); + while (PC != NULL && EC == NULL && !FP->fast) + EXEC_leave_drop(); + ERROR_unlock(); + + if (FP && FP->fast) + PROPAGATE(); + + // If we got the void stack frame, then we remove it and raise the error again + if (PC == NULL) + { + STACK_pop_frame(&EXEC_current); + PROPAGATE(); + } + + + // We have a TRY too, handle it. + if (EP != NULL) + { + #if DEBUG_ERROR + fprintf(stderr, "#4 EP = %d SP = %d\n", EP - (VALUE *)STACK_base, SP - (VALUE *)STACK_base); + #endif + + ERROR_lock(); + while (SP > EP) + POP(); + ERROR_unlock(); + + EP = NULL; + /* On va directement sur le END TRY */ + } + + // Now we can handle the CATCH + + // The stack is popped until reaching the stack position at the function start + ERROR_lock(); + #if DEBUG_ERROR + DEBUG_where(); + fprintf(stderr, "#5 BP + local + ctrl = %d SP = %d\n", BP + FP->n_local + FP->n_ctrl - (VALUE *)STACK_base, SP - (VALUE *)STACK_base); + #endif + while (SP > (BP + FP->n_local + FP->n_ctrl)) + POP(); + ERROR_unlock(); + + PC = EC; + EC = NULL; + + retry = TRUE; + } + + //ERROR_restore(&save); + //ERROR_set_last(); + + __CONTINUE: + + while (SP < EXEC_super) + EXEC_super = ((VALUE *)EXEC_super)->_object.super; + } + END_TRY + + #if DEBUG_ERROR + if (retry) + fprintf(stderr, "RETRY %p\n", PC); + #endif + } + while (retry); + } + + if (PC == NULL) + STACK_pop_frame(&EXEC_current); +} + + +static ushort exec_native_can_quick(void) +{ + CLASS_DESC_METHOD *desc = EXEC.desc; + int i; + int nparam = EXEC.nparam; + + /* check number of arguments */ + + //fprintf(stderr, "exec_native_can_quick: %s.%s(%d) npmin:%d npmax:%d npvar:%d\n", desc->class->name, desc->name, nparam, desc->npmin, desc->npmax, desc->npvar); + + if (desc->npmin < desc->npmax || nparam != desc->npmin || desc->npvar) + return C_CALL_SLOW; + + /* check arguments type */ + + for (i = 0; i < nparam; i++) + { + if (SP[i - nparam].type != desc->signature[i]) + return C_CALL_SLOW; + } + + return C_CALL_QUICK; +} + +void EXEC_native_check(bool defined) +{ + ushort mode = defined ? exec_native_can_quick() : C_CALL_SLOW; + + *PC = (*PC & 0xFF) | mode; + + switch(mode) + { + case C_CALL_QUICK: EXEC_native_quick(); break; + //case C_CALL_EASY: EXEC_native_easy(); break; + default: EXEC_native(); break; + } +} + +#define EXEC_call_native_inline(_exec, _object, _type, _param) \ +({ \ + EXEC_set_native_error(FALSE); \ + (*(_exec))((_object), (void *)(_param)); \ + \ + if (EXEC_has_native_error()) \ + { \ + EXEC_set_native_error(FALSE); \ + error = TRUE; \ + } \ + else \ + { \ + TYPE __type = (_type); \ + if (TYPE_is_pure_object(__type) && TEMP.type != T_NULL && TEMP.type != T_VOID) \ + { \ + if (TEMP.type == T_CLASS) \ + TEMP._class.class = (CLASS *)__type; \ + else \ + TEMP.type = __type; \ + \ + error = FALSE; \ + } \ + else \ + error = FALSE; \ + } \ +}) + +bool EXEC_call_native(void (*exec)(), void *object, TYPE type, VALUE *param) +{ + bool error; + + EXEC_call_native_inline(exec, object, type, param); + return error; +} + +void EXEC_native_quick(void) +{ + CLASS_DESC_METHOD *desc = EXEC.desc; + int nparam = EXEC.nparam; + + bool error; + //void *free_later; + VALUE ret; + + EXEC_call_native_inline(desc->exec, EXEC.object, desc->type, &SP[-nparam]); + COPY_VALUE(&ret, &TEMP); + + if (UNLIKELY(error)) + { + RELEASE_MANY(SP, nparam); + POP(); + PROPAGATE(); + } + + if (desc->type == T_VOID) + { + RELEASE_MANY(SP, nparam); + + SP--; + OBJECT_UNREF(SP->_function.object); + + SP->type = T_VOID; + SP->_void.ptype = T_NULL; + SP++; + } + else + { + BORROW(&ret); + RELEASE_MANY(SP, nparam); + + SP--; + OBJECT_UNREF(SP->_function.object); + COPY_VALUE(SP, &ret); + SP++; + } + + + #if DEBUG_STACK + fprintf(stderr, "| << EXEC_native: %s (%p)\n", desc->name, &desc); + #endif +} + + +static void error_EXEC_native(intptr_t nparam) +{ + RELEASE_MANY(SP, (int)nparam); +} + +void EXEC_native(void) +{ + CLASS_DESC_METHOD *desc = EXEC.desc; + int nparam = EXEC.nparam; + void *object = EXEC.object; + bool use_stack = EXEC.use_stack; + + int i, n, nm; + VALUE *value; + TYPE *sign; + bool error; + VALUE ret; + + #if DEBUG_STACK + fprintf(stderr, "| >> EXEC_native: %s.%s (%p)\n", EXEC.class->name, desc->name, &desc); + #endif + + ON_ERROR_1(error_EXEC_native, nparam) + { + n = desc->npmin; + nm = desc->npmax; + + if (UNLIKELY(nparam < n)) + THROW(E_NEPARAM); + + if (LIKELY(!desc->npvar)) + { + if (UNLIKELY(nparam > nm)) + THROW(E_TMPARAM); + + value = &SP[-nparam]; + sign = desc->signature; + + for (i = 0; i < n; i++, value++, sign++) + VALUE_conv(value, *sign); + + if (UNLIKELY(n < nm)) + { + for (; i < nparam; i++, value++, sign++) + { + if (value->type != T_VOID) + VALUE_conv(value, *sign); + } + + n = nm - nparam; + + /*if (UNLIKELY(STACK_check(n))) + { + STACK_RELOCATE(value); + }*/ + + SP += n; + nparam = nm; + + for (; i < nparam; i++, value++) + value->type = T_VOID; + } + } + else + { + value = &SP[-nparam]; + sign = desc->signature; + + for (i = 0; i < n; i++, value++, sign++) + VALUE_conv(value, *sign); + + nm = desc->npmax; + if (UNLIKELY(n < nm)) + { + if (UNLIKELY(nparam < nm)) + { + for (; i < nparam; i++, value++, sign++) + { + if (value->type != T_VOID) + VALUE_conv(value, *sign); + } + + n = nm - nparam; + + /*if (UNLIKELY(STACK_check(n))) + { + STACK_RELOCATE(value); + }*/ + + SP += n; + nparam = nm; + + for (; i < nparam; i++, value++) + value->type = T_VOID; + } + else + { + n = desc->npmax; + for (; i < n; i++, value++, sign++) + { + if (value->type != T_VOID) + VALUE_conv(value, *sign); + } + } + } + + EXEC_unknown_nparam = nparam <= nm ? 0 : nparam - nm; + + for (; i < nparam; i++, value++) + VARIANT_undo(value); + + //printf("EXEC_native: nparvar = %d\n", EXEC.nparvar); + } + } + END_ERROR + + EXEC_call_native_inline(desc->exec, object, desc->type, &SP[-nparam]); + COPY_VALUE(&ret, &TEMP); + + if (UNLIKELY(error)) + { + RELEASE_MANY(SP, nparam); + + if (use_stack) + { + SP--; + OBJECT_UNREF(SP->_function.object); + } + + PROPAGATE(); + } + + // If the function description is on the stack + + if (desc->type == T_VOID) + { + RELEASE_MANY(SP, nparam); + + if (use_stack) + { + SP--; + OBJECT_UNREF(SP->_function.object); + } + + SP->type = T_VOID; + SP->_void.ptype = T_NULL; + SP++; + //UNBORROW(&ret); + } + else + { + BORROW(&ret); + RELEASE_MANY(SP, nparam); + + if (use_stack) + { + SP--; + OBJECT_UNREF(SP->_function.object); + + if (PCODE_is_variant(*PC) && ret.type != T_VOID) + VALUE_conv_variant(&ret); + } + + COPY_VALUE(SP, &ret); + SP++; + } + + + #if DEBUG_STACK + fprintf(stderr, "| << EXEC_native: %s (%p)\n", desc->name, &desc); + #endif +} + + +CLASS *EXEC_object_real(VALUE *val) +{ + CLASS *class; + OBJECT *object; + + object = val->_object.object; + class = val->_object.class; + + if (!object) + { + /* A null object and a virtual class means that we want to pass a static class */ + if (!class->is_virtual) + THROW_NULL(); + CLASS_load(class); + goto __RETURN; + } + + if (val == EXEC_super) + EXEC_super = val->_object.super; + else if (!class->is_virtual) + class = object->class; + + //CLASS_load(class); If we have an object, the class is necessarily loaded. + + if (UNLIKELY(class->must_check && (*(class->check))(object))) + THROW(E_IOBJECT); + +__RETURN: + + return class; +} + +CLASS *EXEC_object_variant(VALUE *val, OBJECT **pobject) +{ + CLASS *class; + OBJECT *object; + + if (TYPE_is_pure_object(val->_variant.vtype)) + { + object = val->_variant.value._object; + if (!object) + goto __NULL; + class = (CLASS *)val->_variant.vtype; + if (!class->is_virtual) + class = object->class; /* Virtual dispatching */ + goto __CHECK; + } + else if (val->_variant.vtype == T_OBJECT) + { + object = val->_variant.value._object; + if (!object) + goto __NULL; + class = object->class; + goto __CHECK; + } + else if (val->_variant.vtype == T_STRING || val->_variant.vtype == T_CSTRING) + { + *pobject = NULL; + return CLASS_BoxedString; + } + else + goto __ERROR; + +__ERROR: + + THROW(E_NOBJECT); + +__NULL: + + THROW_NULL(); + +__CHECK: + + //CLASS_load(class); //If we have an object, the class is not necessarily loaded? + + if (UNLIKELY(class->must_check && (*(class->check))(object))) + THROW(E_IOBJECT); + + *pobject = object; + + return class; +} + +bool EXEC_object_other(VALUE *val, CLASS **pclass, OBJECT **pobject) +{ + static const void *jump[] = { + &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, + &&__STRING, &&__CSTRING, &&__ERROR, &&__ERROR, &&__FUNCTION, &&__CLASS, &&__NULL, + &&__OBJECT, + }; + + CLASS *class; + OBJECT *object; + bool defined; + + goto *jump[val->type]; + +__FUNCTION: + + if (LIKELY(val->_function.kind == FUNCTION_UNKNOWN)) + { + EXEC_unknown_property = TRUE; + EXEC_unknown_name = CP->load->unknown[val->_function.index]; + + EXEC_special(SPEC_UNKNOWN, val->_function.class, val->_function.object, 0, FALSE); + + object = val->_function.object; + OBJECT_UNREF(object); + + SP--; + //*val = *SP; + COPY_VALUE(val, SP); + EXEC_object(val, pclass, pobject); + return FALSE; // Could be TRUE, but always returning FALSE allows optimizations in quick array management + } + else + goto __ERROR; + +__CLASS: + + class = val->_class.class; + object = NULL; + defined = TRUE; + + if (val == EXEC_super) + { + EXEC_super = val->_class.super; + //*class = (*class)->parent; + if (UNLIKELY(class == NULL)) + THROW(E_PARENT); + } + + CLASS_load(class); + goto __RETURN; + +__OBJECT: + + object = val->_object.object; + if (!object) + goto __NULL; + class = object->class; + defined = FALSE; + + goto __CHECK; + +__STRING: +__CSTRING: + + *pclass = CLASS_BoxedString; + *pobject = NULL; + return TRUE; + +__ERROR: + + THROW(E_NOBJECT); + +__NULL: + + THROW_NULL(); + +__CHECK: + + //CLASS_load(class); If we have an object, the class is necessarily loaded. + + if (UNLIKELY(class->must_check && (*(class->check))(object))) + THROW(E_IOBJECT); + +__RETURN: + + *pclass = class; + *pobject = object; + + return defined; +} + + +void EXEC_public_desc(CLASS *class, void *object, CLASS_DESC_METHOD *desc, int nparam) +{ + EXEC.object = object; + EXEC.nparam = nparam; /*desc->npmin;*/ + + if (FUNCTION_is_native(desc)) + { + EXEC.class = class; // EXEC_native() does not need the real class, except the GB.GetClass(NULL) API used by Form.Main. + EXEC.native = TRUE; + EXEC.use_stack = FALSE; + EXEC.desc = desc; + EXEC_native(); + SP--; + *RP = *SP; + SP->type = T_VOID; + } + else + { + EXEC.class = desc->class; // EXEC_function_real() needs the effective class, because the method can be an inherited one! + EXEC.native = FALSE; + EXEC.index = (int)(intptr_t)desc->exec; + EXEC_function_keep(); + } +} + +void EXEC_public(CLASS *class, void *object, const char *name, int nparam) +{ + CLASS_DESC *desc; + + desc = CLASS_get_symbol_desc_kind(class, name, (object != NULL) ? CD_METHOD : CD_STATIC_METHOD, 0); + + if (UNLIKELY(desc == NULL)) + return; + + EXEC_public_desc(class, object, &desc->method, nparam); + EXEC_release_return_value(); +} + + + +bool EXEC_special(int special, CLASS *class, void *object, int nparam, bool drop) +{ + CLASS_DESC *desc; + short index = class->special[special]; + + if (index == NO_SYMBOL) + return TRUE; + + desc = CLASS_get_desc(class, index); + + if (CLASS_DESC_get_type(desc) == CD_STATIC_METHOD) + { + if (object != NULL) + return TRUE; + } + else + { + if (object == NULL) + { + if (class->auto_create) + object = EXEC_auto_create(class, FALSE); + + if (object == NULL) + THROW(E_NOBJECT); + } + } + + EXEC.class = desc->method.class; + EXEC.object = object; + EXEC.nparam = nparam; + + /*printf("<< EXEC_spec: SP = %d\n", SP - (VALUE *)STACK_base); + save_SP = SP;*/ + + if (FUNCTION_is_native(&desc->method)) + { + if (desc->method.subr) + { + ((EXEC_FUNC_CODE)(EXEC.class->table[index].desc->method.exec))(nparam); + } + else + { + EXEC.desc = &desc->method; + EXEC.use_stack = FALSE; + EXEC.native = TRUE; + EXEC_native(); + } + + if (drop) + POP(); + } + else + { + //EXEC.func = &class->load->func[(long)desc->method.exec] + EXEC.index = (int)(intptr_t)desc->method.exec; + EXEC.native = FALSE; + if (drop) + EXEC_function(); + else + { + EXEC_function_keep(); + //*SP++ = *RP; + COPY_VALUE(SP, RP); + SP++; + RP->type = T_VOID; + } + } + + /*printf(">> EXEC_spec: SP = %d\n", SP - (VALUE *)STACK_base); + if (SP != save_SP) + printf("**** SP should be %d\n", save_SP - (VALUE *)STACK_base);*/ + + return FALSE; +} + + +/* + The highest parent method is called first, but get only the parameters + not consumed by the child methods. +*/ + +void EXEC_special_inheritance(int special, CLASS *class, OBJECT *object, int nparam, bool drop) +{ + CLASS *her[MAX_INHERITANCE]; + int nher; + int i, np, npopt, nparam_opt, save_nparam; + CLASS_DESC *desc; + short index; + int arg, opt; + VALUE *base; + + if (!class->parent) + { + if (special == SPEC_NEW && class->init_dynamic) + { + EXEC.class = class; + EXEC.object = object; + EXEC.index = FUNC_INIT_DYNAMIC; + EXEC.native = FALSE; + EXEC.nparam = 0; + EXEC_function(); + } + + if (EXEC_special(special, class, object, nparam, drop)) + { + if (nparam) + THROW(E_TMPARAM); + } + + return; + } + + nher = CLASS_get_inheritance(class, her); + + for(i = 0, np = 0; i < nher; i++) + { + class = her[i]; + + index = class->special[special]; + if (index == NO_SYMBOL) + continue; + desc = CLASS_get_desc(class, index); //class->special[special]; + np += desc->method.npmin; + } + + if (UNLIKELY(np > nparam)) + THROW(E_NEPARAM); + + save_nparam = nparam; + arg = - nparam; + opt = arg + np; + nparam_opt = nparam - np; + nparam = np; + + // nparam is now the number of mandatory arguments + // naram_opt is the number of optional arguments + + for(;;) + { + nher--; + if (nher < 0) + break; + class = her[nher]; + + if (special == SPEC_NEW) + { + if (class->init_dynamic) + { + EXEC.class = class; + EXEC.object = object; + EXEC.index = FUNC_INIT_DYNAMIC; + //EXEC.func = &class->load->func[FUNC_INIT_DYNAMIC]; + EXEC.native = FALSE; + EXEC.nparam = 0; + + EXEC_function(); + } + } + + index = class->special[special]; + if (index == NO_SYMBOL) + continue; + + desc = CLASS_get_desc(class, index); // class->special[special]; + + if (nher) + { + np = desc->method.npmin; + if (np > nparam) np = nparam; + nparam -= np; + + npopt = desc->method.npmax - desc->method.npmin; + if (npopt > nparam_opt) npopt = nparam_opt; + nparam_opt -= npopt; + } + else + { + np = nparam; + npopt = nparam_opt; + } + + if (np > 0 || npopt > 0) + { + STACK_check(np + npopt); + + base = SP; + + for (i = 0; i < np; i++) + { + *SP++ = base[arg]; + base[arg].type = T_NULL; + arg++; + } + + for (i = 0; i < npopt; i++) + { + *SP++ = base[opt]; + base[opt].type = T_NULL; + opt++; + } + } + + EXEC_special(special, class, object, np + npopt, drop); + } + + SP -= save_nparam; +} + +void *EXEC_create_object(CLASS *class, int np, char *event) +{ + void *object; + + CLASS_load(class); + + if (UNLIKELY(class->no_create)) + THROW(E_CSTATIC, CLASS_get_name(class)); + + object = OBJECT_new(class, event, ((OP == NULL) ? (OBJECT *)CP : (OBJECT *)OP)); + + TRY + { + OBJECT_lock(object, TRUE); + EXEC_special_inheritance(SPEC_NEW, class, object, np, TRUE); + OBJECT_lock(object, FALSE); + EXEC_special(SPEC_READY, class, object, 0, TRUE); + } + CATCH + { + // _free() methods should not be called, but we must + OBJECT_UNREF(object); + PROPAGATE(); + } + END_TRY + + return object; +} + + +void EXEC_new(ushort code) +{ + CLASS *class; + int np; + bool event; + void *object; + char *name = NULL; + char *cname = NULL; + char *save; + + np = code & 0xFF; + event = np & CODE_NEW_EVENT; + np &= 0x3F; + + /* Instanciation */ + + SP -= np; + + if (SP->type == T_CLASS) + { + class = SP->_class.class; + //if (UNLIKELY(class->override != NULL)) + // class = class->override; + } + else if (TYPE_is_string(SP->type)) + { + cname = STRING_copy_from_value_temp(SP); + class = CLASS_find(cname); + RELEASE_STRING(SP); + SP->type = T_NULL; + } + else + THROW_TYPE(T_STRING, SP->type); + + SP += np; + + //printf("**** NEW %s\n", class->name); + CLASS_load(class); + + if (UNLIKELY(class->no_create)) + THROW(E_CSTATIC, CLASS_get_name(class)); + + if (event) + { + SP--; + + if (!TYPE_is_string(SP->type)) + THROW_TYPE(T_STRING, SP->type); + + name = STRING_copy_from_value_temp(SP); + //printf("**** name %s\n", class->name); + + STRING_ref(name); + SP++; + object = OBJECT_new(class, name, ((OP == NULL) ? (OBJECT *)CP : (OBJECT *)OP)); + SP--; + STRING_unref(&name); + + RELEASE_STRING(SP); + + np -= 2; + } + else + { + object = OBJECT_new(class, name, ((OP == NULL) ? (OBJECT *)CP : (OBJECT *)OP)); + np--; + } + + save = EVENT_enter_name(name); + + TRY + { + OBJECT_lock(object, TRUE); + EXEC_special_inheritance(SPEC_NEW, class, object, np, TRUE); + OBJECT_lock(object, FALSE); + EVENT_leave_name(save); + + SP--; /* class */ + EXEC_special(SPEC_READY, class, object, 0, TRUE); + + SP->_object.class = class; + SP->_object.object = object; + SP++; + } + CATCH + { + EVENT_leave_name(save); + // _free() methods should not be called, but we must + OBJECT_UNREF(object); + //(*class->free)(class, object); + SP--; /* class */ + SP->type = T_NULL; + SP++; + PROPAGATE(); + } + END_TRY +} + + +void EXEC_do_quit(void) +{ + GAMBAS_DoNotRaiseEvent = TRUE; + + HOOK(quit)(); + + THROW(E_ABORT); +} + +void *EXEC_auto_create(CLASS *class, bool ref) +{ + void *object; + + object = CLASS_auto_create(class, 0); /* object is checked by CLASS_auto_create */ + + /*if (UNLIKELY(class->must_check && (*(class->check))(object))) + THROW(E_IOBJECT);*/ + + if (ref) + OBJECT_REF(object); + + return object; +} + +void EXEC_dup(int n) +{ + VALUE *src; + + STACK_check(n); + + src = SP - n; + while (n > 0) + { + BORROW(src); + *SP++ = *src++; + n--; + } +} + +void EXEC_push_complex(void) +{ + static void *(*func)(double) = NULL; + void *ob; + + SP--; + if (SP->type < T_INTEGER || SP->type > T_FLOAT) + THROW_ILLEGAL(); + SP++; + + if (!func) + { + if (COMPONENT_get_info("PUSH_COMPLEX", POINTER(&func))) + { + COMPONENT_load(COMPONENT_create("gb.complex")); + if (COMPONENT_get_info("PUSH_COMPLEX", POINTER(&func))) + THROW(E_MATH); + } + } + + SP--; + VALUE_conv_float(SP); + ob = (*func)(SP->_float.value); + SP->_object.object = ob; + SP->_object.class = OBJECT_class(ob); + OBJECT_REF(ob); + SP++; +} + +void EXEC_push_vargs(void) +{ + int i; + int nargs = (FP && FP->vararg) ? BP - PP : 0; + + if (nargs == 0) + return; + + STACK_check(nargs); + + for (i = 0; i < nargs; i++) + { + *SP = PP[i]; + PUSH(); + } +} + +void EXEC_drop_vargs(void) +{ + int nargs = (FP && FP->vararg) ? BP - PP : 0; + + if (nargs == 0) + return; + + RELEASE_MANY(SP, nargs); +} diff --git a/main/gbx/gbx_exec.h b/main/gbx/gbx_exec.h new file mode 100644 index 00000000..71a7abb7 --- /dev/null +++ b/main/gbx/gbx_exec.h @@ -0,0 +1,353 @@ +/*************************************************************************** + + gbx_exec.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_EXEC_H +#define __GBX_EXEC_H + +#include "gb_alloc.h" +#include "gb_error.h" +#include "gbx_class.h" +#include "gbx_value.h" +#include "gb_pcode.h" +#include "gbx_stack.h" + +#include "gbx_string.h" +#include "gbx_object.h" +#include "gbx_variant.h" + +#include "gbx_c_enum.h" + +enum { GB_LITTLE_ENDIAN, GB_BIG_ENDIAN }; + +typedef + void (*EXEC_FUNC)(); + +typedef + void (*EXEC_FUNC_CODE)(ushort); + +typedef + struct { + CLASS *class; + OBJECT *object; + CLASS_DESC_METHOD *desc; + FUNCTION *func; + int index; + char nparam; + bool native; + bool use_stack; + } + EXEC_GLOBAL; + +typedef + struct { + void (*main)(); + void (*loop)(); + void (*wait)(); + void (*timer)(); + void (*lang)(); + void (*watch)(); + void (*post)(); + void (*quit)(); + void (*error)(); + double (*timeout)(); + } + EXEC_HOOK; + +enum { + OP_NOTHING = 0, + OP_OBJECT_FLOAT, + OP_FLOAT_OBJECT, + OP_OBJECT_OTHER, + OP_OTHER_OBJECT, + OP_OBJECT_OBJECT + }; + + +#ifndef __GBX_EXEC_C + +extern STACK_CONTEXT EXEC_current; +extern VALUE *SP; +extern VALUE TEMP; +extern VALUE RET; + +extern VALUE *EXEC_super; + +extern bool EXEC_debug; +extern bool EXEC_task; +extern bool EXEC_profile; +extern const char *EXEC_profile_path; +extern bool EXEC_profile_instr; +extern bool EXEC_arch; +extern bool EXEC_fifo; +extern const char *EXEC_fifo_name; +extern bool EXEC_keep_library; +extern bool EXEC_string_add; +extern bool EXEC_break_on_error; + +extern EXEC_HOOK EXEC_Hook; + +extern CENUM *EXEC_enum; + +extern bool EXEC_big_endian; +extern bool EXEC_main_hook_done; +extern bool EXEC_got_error; + +extern const char EXEC_should_borrow[]; + +extern const char *EXEC_unknown_name; +extern char EXEC_unknown_nparam; +extern uchar EXEC_quit_value; + +extern EXEC_GLOBAL EXEC; + +extern const void *EXEC_subr_table[]; + +#endif + +// Local variables base pointer +#define BP EXEC_current.bp +// Arguments base pointer +#define PP EXEC_current.pp +// Current class +#define CP EXEC_current.cp +// Current object +#define OP EXEC_current.op +// Save stack pointer for a TRY +#define EP EXEC_current.ep +// Current function +#define FP EXEC_current.fp +// Program counter +#define PC EXEC_current.pc +// Where to go if there is an error +#define EC EXEC_current.ec +// Save register for TRY +#define ET EXEC_current.et +// GoSub stack +#define GP EXEC_current.gp + +// Function return value pointer +#define RP (&RET) + +#define HOOK(func) (!EXEC_Hook.func) ? 0 : (*EXEC_Hook.func) +#define HOOK_DEFAULT(func, def) (*((!EXEC_Hook.func) ? def : EXEC_Hook.func)) + +#define GET_NPARAM(var) short var = code & 0x3F +#define GET_PARAM(var, nparam) VALUE *var = &SP[-nparam] + +void EXEC_init(void); + +void EXEC_enter_check(bool defined); +void EXEC_enter(void); +void EXEC_enter_quick(void); +void EXEC_leave_keep(); +void EXEC_leave_drop(); +void EXEC_loop(void); + +#define EXEC_object_2(_val, _pclass, _pobject) \ +({ \ + VALUE *v = (_val); \ + \ + *(_pobject) = v->_object.object; \ + \ + if (v->_object.class->is_simple) \ + { \ + if (!*(_pobject)) \ + THROW_NULL(); \ + \ + *(_pclass) = OBJECT_class(v->_object.object); \ + } \ + else \ + *(_pclass) = EXEC_object_real(_val); \ +}) + +#define EXEC_object(_val, _pclass, _pobject) \ + ((LIKELY(TYPE_is_pure_object((_val)->type))) ? EXEC_object_2(_val, _pclass, _pobject), TRUE : \ + TYPE_is_variant((_val)->type) ? (*(_pclass) = EXEC_object_variant(_val, _pobject)), FALSE : \ + EXEC_object_other(_val, _pclass, _pobject)) + +#define EXEC_object_fast(_val, _pclass, _pobject) \ +({ \ + if ((_val)->type != T_CLASS) \ + EXEC_object_real(_val, _pclass, _pobject); \ + else \ + EXEC_object_other(_val, _pclass, _pobject); \ +}) + +#define EXEC_object_array(_val, _vclass, _vobject) \ + _vobject = (_val)->_object.object; \ + if (!(_vobject)) \ + THROW_NULL(); \ + _vclass = (_vobject)->class; \ + if ((_val) == EXEC_super) \ + EXEC_super = (_val)->_object.super; + +CLASS *EXEC_object_real(VALUE *val); +CLASS *EXEC_object_variant(VALUE *val, OBJECT **pobject); +bool EXEC_object_other(VALUE *val, CLASS **pclass, OBJECT **pobject); + +void *EXEC_auto_create(CLASS *class, bool ref); + +bool EXEC_call_native(void (*exec)(), void *object, TYPE type, VALUE *param); +void EXEC_native_check(bool defined); +void EXEC_native_quick(void); +void EXEC_native(void); +void EXEC_function_real(void); +void EXEC_function_loop(void); + +#define EXEC_function() EXEC_function_real(), EXEC_release_return_value() +#define EXEC_function_keep() EXEC_function_real() + +void EXEC_public(CLASS *class, void *object, const char *name, int nparam); +void EXEC_public_desc(CLASS *class, void *object, CLASS_DESC_METHOD *desc, int nparam); + +bool EXEC_special(int special, CLASS *class, void *object, int nparam, bool drop); + +void EXEC_special_inheritance(int special, CLASS *class, OBJECT *object, int nparam, bool drop); + +void EXEC_nop(void); + +void EXEC_push_unknown(void); +int EXEC_push_unknown_event(bool unknown); +//void EXEC_push_special(void); + +void EXEC_pop_unknown(void); + +void EXEC_enum_first(PCODE code, VALUE *local, VALUE *penum); +bool EXEC_enum_next(PCODE code, VALUE *local, VALUE *penum); + +void *EXEC_create_object(CLASS *class, int np, char *event); +void EXEC_new(ushort code); + +void EXEC_release_return_value(void); +void EXEC_do_quit(void); + +void EXEC_dup(int n); + +void EXEC_borrow(TYPE type, VALUE *val); +void UNBORROW(VALUE *val); +void EXEC_release(TYPE type, VALUE *val); +void RELEASE_many(VALUE *val, int n); + +void EXEC_push_complex(void); + +void EXEC_push_vargs(void); +void EXEC_drop_vargs(void); + +#define BORROW(_value) \ +do { \ + VALUE *_v = (_value); \ + TYPE type = _v->type; \ + if (TYPE_is_object(type)) \ + { \ + OBJECT_REF(_v->_object.object); \ + } \ + else if (EXEC_should_borrow[type]) \ + { \ + if (type == T_STRING) \ + STRING_ref(_v->_string.addr); \ + else \ + EXEC_borrow(type, _v); \ + } \ +} while (0) + +#define RELEASE(_value) \ +do { \ + VALUE *_v = (_value); \ + TYPE type = _v->type; \ + if (TYPE_is_object(type)) \ + { \ + OBJECT_UNREF(_v->_object.object); \ + } \ + else if (EXEC_should_borrow[type]) \ + { \ + if (type == T_STRING) \ + STRING_unref(&_v->_string.addr); \ + else \ + EXEC_release(type, _v); \ + } \ +} while (0) + +#define RELEASE_STRING(_value) \ +do { \ + if ((_value)->type == T_STRING) STRING_unref(&(_value)->_string.addr); \ +} while(0) + +#define RELEASE_OBJECT(_value) \ +do { \ + VALUE *_v = (_value); \ + OBJECT_UNREF(_v->_object.object); \ +} while (0) + +#define RELEASE_MANY(_val, _n) \ +do { \ +if (_n) \ +{ \ + if ((_n) == 1) \ + { \ + _val--; \ + RELEASE((_val)); \ + } \ + else \ + { \ + RELEASE_many((_val), (_n)); \ + _val -= (_n); \ + } \ +} \ +} while (0) + +#define PUSH() \ +do { \ + BORROW(SP); \ + SP++; \ +} while (0) + +#define POP() \ +do { \ + SP--; \ + RELEASE(SP); \ +} while (0) + +#define COPY_VALUE(_dst, _src) VALUE_copy(_dst, _src) + +#define EXEC_set_native_error(_err) (ERROR_current->info.native = (_err)) +#define EXEC_has_native_error() (ERROR_current->info.native) + +bool EXEC_check_operator_single(VALUE *P1, uchar op); +int EXEC_check_operator(VALUE *P1, VALUE *P2, uchar op); +void EXEC_operator(uchar what, uchar op, VALUE *P1, VALUE *P2); +void EXEC_operator_object_add_quick(VALUE *P1, double val); +int EXEC_comparator(uchar what, uchar op, VALUE *P1, VALUE *P2); +void EXEC_operator_object_sgn(VALUE *P1); +void EXEC_operator_object_fabs(VALUE *P1); +void EXEC_operator_object_single(uchar op, VALUE *P1); + +void SUBR_left(ushort code); +void SUBR_mid(ushort code); +void SUBR_right(ushort code); + +void EXEC_quit(ushort code); + +void EXEC_push_array(ushort code); +void EXEC_pop_array(ushort code); + +#endif /* */ diff --git a/main/gbx/gbx_exec_enum.c b/main/gbx/gbx_exec_enum.c new file mode 100644 index 00000000..c28f8220 --- /dev/null +++ b/main/gbx/gbx_exec_enum.c @@ -0,0 +1,97 @@ +/*************************************************************************** + + gbx_exec_enum.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" +#include "gb_limit.h" +#include "gbx_c_enum.h" +#include "gbx_exec.h" + + +/* EXEC_object() ne doit pas faire d'auto-create, car sinon + il renvoie un objet référencé */ + +void EXEC_enum_first(PCODE code, VALUE *local, VALUE *penum) +{ + OBJECT *object; + CLASS *class; + CENUM *old = EXEC_enum; + CENUM *cenum; + + EXEC_object(local, &class, &object); + + if (!object && class->auto_create && !class->enum_static) + object = EXEC_auto_create(class, FALSE); + + cenum = CENUM_create(object ? (void *)object : (void *)class); + + RELEASE(penum); + penum->_object.class = OBJECT_class(cenum); + penum->_object.object = cenum; + OBJECT_REF(cenum); + + EXEC_enum = cenum; + EXEC_special(SPEC_FIRST, class, object, 0, TRUE); + EXEC_enum = old; +} + + +bool EXEC_enum_next(PCODE code, VALUE *local, VALUE *penum) +{ + OBJECT *object; + CLASS *class; + bool defined; + bool drop = (code & 1); + bool err; + CENUM *old = EXEC_enum; + CENUM *cenum; + + defined = EXEC_object(local, &class, &object); + cenum = (CENUM *)penum->_object.object; + if (!cenum) + return TRUE; + + if (cenum->stop) + goto __STOP; + + EXEC_enum = cenum; + err = EXEC_special(SPEC_NEXT, class, object, 0, FALSE); + EXEC_enum = old; + + if (err) + THROW(E_ENUM); + + if (!defined && !drop && !cenum->stop) + VALUE_conv_variant(&SP[-1]); + + if (drop || cenum->stop) + POP(); + + if (!cenum->stop) + return FALSE; + +__STOP: + + OBJECT_UNREF(cenum); + penum->_object.object = NULL; + return TRUE; +} diff --git a/main/gbx/gbx_exec_loop.c b/main/gbx/gbx_exec_loop.c new file mode 100644 index 00000000..7189740d --- /dev/null +++ b/main/gbx/gbx_exec_loop.c @@ -0,0 +1,4162 @@ +/*************************************************************************** + + gbx_exec_loop.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_EXEC_LOOP_C + +#include "gb_common.h" +#include "gb_common_check.h" +#include "gb_error.h" +#include "gbx_type.h" +#include "gbx_debug.h" + +#include "gbx_subr.h" +#include "gb_pcode.h" +#include "gbx_stack.h" +#include "gbx_event.h" +#include "gbx_value.h" +#include "gbx_local.h" +#include "gbx_string.h" +#include "gbx_api.h" +#include "gbx_archive.h" +#include "gbx_extern.h" +#include "gbx_exec.h" +#include "gbx_subr.h" +#include "gbx_math.h" +#include "gbx_c_array.h" +#include "gbx_c_string.h" +#include "gbx_struct.h" +#include "gbx_variant.h" +#include "gbx_jit.h" + +//#define DEBUG_PCODE 1 + +#if DEBUG_PCODE +#define PROJECT_EXEC +#include "gb_pcode_temp.h" +#endif + +#define SUBR_beep EXEC_ILLEGAL + +#define GET_XXX() (((signed short)(code << 4)) >> 4) +#define GET_UXX() (code & 0xFFF) +#define GET_7XX() (code & 0x7FF) +#define GET_XX() ((signed char)code) +#define GET_UX() ((unsigned char)code) +#define GET_3X() (code & 0x3F) +#define GET_0X() (code & 0xF) +#define TEST_XX() (code & 1) + +static void my_VALUE_class_read(CLASS *class, VALUE *value, char *addr, CTYPE ctype, void *ref); +static void my_VALUE_class_constant(CLASS *class, VALUE *value, int ind); +static void _break(ushort code); + +//static void _SUBR_comp(ushort code); +static void _SUBR_compe(ushort code); +//static void _SUBR_compi(ushort code); +static void _SUBR_add(ushort code); +static void _SUBR_sub(ushort code); +static void _SUBR_mul(ushort code); +static void _SUBR_div(ushort code); + +//---- Subroutine dispatch table -------------------------------------------- + +const void *EXEC_subr_table[] = +{ + /* 00 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 10 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 20 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + /* 28 */ _SUBR_compe, _SUBR_compe, NULL, NULL, + /* 2C */ NULL, NULL, SUBR_near, SUBR_case, + /* 30 */ _SUBR_add, _SUBR_sub, _SUBR_mul, _SUBR_div, + /* 34 */ SUBR_neg, SUBR_quo, SUBR_rem, SUBR_pow, + /* 38 */ SUBR_and_, SUBR_and_, SUBR_and_, SUBR_not, + /* 3C */ SUBR_cat, SUBR_like, SUBR_file, SUBR_is, + + SUBR_left, /* 00 40 */ + SUBR_mid, /* 01 41 */ + SUBR_right, /* 02 42 */ + NULL, /* 03 43 */ + SUBR_space, /* 04 44 */ + SUBR_string, /* 05 45 */ + SUBR_trim, /* 06 46 */ + SUBR_upper, /* 07 47 */ + SUBR_hex_bin, /* 08 48 */ + SUBR_chr, /* 09 49 */ + SUBR_asc, /* 10 4A */ + SUBR_instr, /* 11 4B */ + SUBR_instr, /* 12 4C */ + SUBR_subst, /* 13 4D */ + SUBR_replace, /* 14 4E */ + SUBR_split, /* 15 4F */ + SUBR_scan, /* 16 50 */ + SUBR_strcomp, /* 17 51 */ + SUBR_iconv, /* 18 52 */ + SUBR_sconv, /* 19 53 */ + SUBR_abs, /* 20 54 */ + SUBR_int, /* 21 55 */ + SUBR_fix, /* 22 56 */ + SUBR_sgn, /* 23 57 */ + SUBR_math, /* 24 58 */ + SUBR_pi, /* 25 59 */ + SUBR_round, /* 26 5A */ + SUBR_randomize, /* 27 5B */ + SUBR_rnd, /* 28 5C */ + SUBR_min_max, /* 29 5D */ + SUBR_min_max, /* 30 5E */ + SUBR_if, /* 31 5F */ + SUBR_choose, /* 32 60 */ + SUBR_array, /* 33 61 */ + SUBR_math2, /* 34 62 */ + SUBR_is_chr, /* 35 63 */ + SUBR_bit, /* 36 64 */ + SUBR_is_type, /* 37 65 */ + SUBR_type, /* 38 66 */ + NULL, /* 39 67 */ + SUBR_hex_bin, /* 40 68 */ + SUBR_hex_bin, /* 41 69 */ + SUBR_val, /* 42 6A */ + SUBR_str, /* 43 6B */ + SUBR_format, /* 44 6C */ + SUBR_timer, /* 45 6D */ + SUBR_now, /* 46 6E */ + SUBR_year, /* 47 6F */ + SUBR_week, /* 48 70 */ + SUBR_date, /* 49 71 */ + SUBR_time, /* 50 72 */ + SUBR_date_op, /* 51 73 */ + SUBR_eval, /* 52 74 */ + SUBR_error, /* 53 75 */ + SUBR_debug, /* 54 76 */ + SUBR_wait, /* 55 77 */ + SUBR_open, /* 56 78 */ + SUBR_close, /* 57 79 */ + SUBR_input, /* 58 7A */ + SUBR_linput, /* 59 7B */ + SUBR_print, /* 60 7C */ + SUBR_read, /* 61 7D */ + SUBR_write, /* 62 7E */ + SUBR_flush, /* 63 7F */ + SUBR_lock, /* 64 80 */ + SUBR_inp_out, /* 65 81 */ + SUBR_eof, /* 66 82 */ + SUBR_lof, /* 67 83 */ + SUBR_seek, /* 68 84 */ + SUBR_kill, /* 69 85 */ + SUBR_mkdir, /* 70 86 */ // deprecated -> Even() & Odd() + SUBR_rmdir, /* 71 87 */ // deprecated + SUBR_move, /* 72 88 */ + SUBR_swap, /* 73 89 */ // support for Copy() + SUBR_link, /* 74 8A */ // deprecated -> IsNan() & IsInf() + SUBR_exist, /* 75 8B */ + SUBR_access, /* 76 8C */ + SUBR_stat, /* 77 8D */ + SUBR_dfree, /* 78 8E */ + SUBR_temp, /* 79 8F */ + SUBR_isdir, /* 80 90 */ + SUBR_dir, /* 81 91 */ + SUBR_rdir, /* 82 92 */ + SUBR_exec, /* 83 93 */ + SUBR_alloc, /* 84 94 */ + SUBR_free, /* 85 95 */ + SUBR_realloc, /* 86 96 */ + SUBR_strptr, /* 87 97 */ + SUBR_sleep, /* 88 98 */ + SUBR_varptr, /* 89 99 */ + SUBR_collection, /* 90 9A */ + SUBR_tr, /* 91 9B */ + SUBR_quote, /* 92 9C */ + SUBR_unquote, /* 93 9D */ + SUBR_make, /* 94 9E */ + SUBR_ptr, /* 95 9F */ +}; + +//---- Main interpreter loop ------------------------------------------------ + +static void _pop_ctrl(int ind) +{ + VALUE *val = &BP[ind]; + RELEASE(val); + SP--; + *val = *SP; +} + +void EXEC_loop(void) +{ + static const void *jump_table[256] = + { + /* 00 NOP */ &&_NEXT, + /* 01 PUSH LOCAL */ &&_PUSH_LOCAL, + /* 02 PUSH PARAM */ &&_PUSH_PARAM, + /* 03 PUSH ARRAY */ &&_PUSH_ARRAY, + /* 04 PUSH UNKNOWN */ &&_PUSH_UNKNOWN, + /* 05 PUSH EXTERN */ &&_PUSH_EXTERN, + /* 06 BYREF */ &&_BYREF, + /* 07 PUSH EVENT */ &&_PUSH_EVENT, + /* 08 QUIT */ &&_QUIT, + /* 09 POP LOCAL */ &&_POP_LOCAL, + /* 0A POP PARAM */ &&_POP_PARAM, + /* 0B POP ARRAY */ &&_POP_ARRAY, + /* 0C POP UNKNOWN */ &&_POP_UNKNOWN, + /* 0D POP OPTIONAL */ &&_POP_OPTIONAL, + /* 0E POP CTRL */ &&_POP_CTRL, + /* 0F BREAK */ &&_BREAK, + /* 10 RETURN */ &&_RETURN, + /* 11 PUSH SHORT */ &&_PUSH_SHORT, + /* 12 PUSH INTEGER */ &&_PUSH_INTEGER, + /* 13 PUSH CHAR */ &&_PUSH_CHAR, + /* 14 PUSH MISC */ &&_PUSH_MISC, + /* 15 PUSH ME */ &&_PUSH_ME, + /* 16 TRY */ &&_TRY, + /* 17 END TRY */ &&_END_TRY, + /* 18 CATCH */ &&_CATCH, + /* 19 DUP */ &&_DUP, + /* 1A DROP */ &&_DROP, + /* 1B NEW */ &&_NEW, + /* 1C CALL */ &&_CALL, + /* 1D CALL QUICK */ &&_CALL_QUICK, + /* 1E CALL EASY */ &&_CALL_SLOW, + /* 1F ON */ &&_ON_GOTO_GOSUB, + /* 20 JUMP */ &&_JUMP, + /* 21 JUMP IF TRUE */ &&_JUMP_IF_TRUE, + /* 22 JUMP IF FALSE */ &&_JUMP_IF_FALSE, + /* 23 GOSUB */ &&_GOSUB, + /* 24 JUMP FIRST */ &&_JUMP_FIRST, + /* 25 JUMP NEXT */ &&_JUMP_NEXT, + /* 26 FIRST */ &&_ENUM_FIRST, + /* 27 NEXT */ &&_ENUM_NEXT, + /* 28 = */ &&_SUBR_COMPE, + /* 29 <> */ &&_SUBR_COMPN, + /* 2A > */ &&_SUBR_COMPGT, + /* 2B <= */ &&_SUBR_COMPLE, + /* 2C < */ &&_SUBR_COMPLT, + /* 2D >= */ &&_SUBR_COMPGE, + /* 2E == */ &&_SUBR, + /* 2F CASE */ &&_SUBR_CODE, + /* 30 + */ &&_SUBR_ADD, + /* 31 - */ &&_SUBR_SUB, + /* 32 * */ &&_SUBR_MUL, + /* 33 / */ &&_SUBR_DIV, + /* 34 NEG */ &&_SUBR_CODE, + /* 35 \ */ &&_SUBR_CODE, + /* 36 MOD */ &&_SUBR_CODE, + /* 37 ^ */ &&_SUBR_CODE, + /* 38 AND */ &&_SUBR_CODE, + /* 39 OR */ &&_SUBR_CODE, + /* 3A XOR */ &&_SUBR_CODE, + /* 3B NOT */ &&_SUBR_CODE, + /* 3C & */ &&_SUBR_CODE, + /* 3D LIKE */ &&_SUBR_CODE, + /* 3E &/ */ &&_SUBR_CODE, + /* 3F Is */ &&_SUBR_CODE, + /* 40 Left$ */ &&_SUBR_LEFT, + /* 41 Mid$ */ &&_SUBR_MID, + /* 42 Right$ */ &&_SUBR_RIGHT, + /* 43 Len */ &&_SUBR_LEN, + /* 44 Space$ */ &&_SUBR, + /* 45 String$ */ &&_SUBR, + /* 46 Trim$ */ &&_SUBR_CODE, + /* 47 UCase$ */ &&_SUBR_CODE, + /* 48 Oct$ */ &&_SUBR_CODE, + /* 49 Chr$ */ &&_SUBR, + /* 4A Asc */ &&_SUBR_CODE, + /* 4B InStr */ &&_SUBR_CODE, + /* 4C RInStr */ &&_SUBR_CODE, + /* 4D Subst$ */ &&_SUBR_CODE, + /* 4E Replace$ */ &&_SUBR_CODE, + /* 4F Split */ &&_SUBR_CODE, + /* 50 Scan */ &&_SUBR, + /* 51 Comp */ &&_SUBR_CODE, + /* 52 Conv */ &&_SUBR, + /* 53 DConv */ &&_SUBR_CODE, + /* 54 Abs */ &&_SUBR_CODE, + /* 55 Int */ &&_SUBR_CODE, + /* 56 Fix */ &&_SUBR_CODE, + /* 57 Sgn */ &&_SUBR_CODE, + /* 58 Frac... */ &&_SUBR_CODE, + /* 59 Pi */ &&_SUBR_CODE, + /* 5A Round */ &&_SUBR_CODE, + /* 5B Randomize */ &&_SUBR_CODE, + /* 5C Rnd */ &&_SUBR_CODE, + /* 5D Min */ &&_SUBR_CODE, + /* 5E Max */ &&_SUBR_CODE, + /* 5F IIf */ &&_SUBR_CODE, + /* 60 Choose */ &&_SUBR_CODE, + /* 61 Array */ &&_SUBR_CODE, + /* 62 ATan2... */ &&_SUBR_CODE, + /* 63 IsAscii... */ &&_SUBR_CODE, + /* 64 BClr... */ &&_SUBR_CODE, + /* 65 IsBoolean... */ &&_SUBR_CODE, + /* 66 TypeOf */ &&_SUBR_CODE, + /* 67 CBool... */ &&_SUBR_CONV, + /* 68 Bin$ */ &&_SUBR_CODE, + /* 69 Hex$ */ &&_SUBR_CODE, + /* 6A Val */ &&_SUBR, + /* 6B Str */ &&_SUBR, + /* 6C Format */ &&_SUBR_CODE, + /* 6D Timer */ &&_SUBR, + /* 6E Now */ &&_SUBR, + /* 6F Year... */ &&_SUBR_CODE, + /* 70 Week */ &&_SUBR_CODE, + /* 71 Date */ &&_SUBR_CODE, + /* 72 Time... */ &&_SUBR_CODE, + /* 73 DateAdd... */ &&_SUBR_CODE, + /* 74 Eval */ &&_SUBR_CODE, + /* 75 Error */ &&_SUBR, + /* 76 Debug */ &&_SUBR_CODE, + /* 77 Wait */ &&_SUBR_CODE, + /* 78 Open */ &&_SUBR_CODE, + /* 79 Close */ &&_SUBR, + /* 7A Input */ &&_SUBR_CODE, + /* 7B LineInput */ &&_SUBR, + /* 7C Print */ &&_SUBR_CODE, + /* 7D Read */ &&_SUBR_CODE, + /* 7E Write */ &&_SUBR_CODE, + /* 7F Flush */ &&_SUBR, + /* 80 Lock... */ &&_SUBR_CODE, + /* 81 InputFrom... */ &&_SUBR_CODE, + /* 82 Eof */ &&_SUBR_CODE, + /* 83 Lof */ &&_SUBR_CODE, + /* 84 Seek */ &&_SUBR_CODE, + /* 85 Kill */ &&_SUBR_CODE, + /* 86 Mkdir */ &&_SUBR_CODE, + /* 87 Rmdir */ &&_SUBR_CODE, + /* 88 Move */ &&_SUBR_CODE, + /* 89 Copy */ &&_SUBR_CODE, + /* 8A Link */ &&_SUBR_CODE, + /* 8B Exist */ &&_SUBR_CODE, + /* 8C Access */ &&_SUBR_CODE, + /* 8D Stat */ &&_SUBR_CODE, + /* 8E Dfree */ &&_SUBR, + /* 8F Temp$ */ &&_SUBR_CODE, + /* 90 IsDir */ &&_SUBR, + /* 91 Dir */ &&_SUBR_CODE, + /* 92 RDir */ &&_SUBR_CODE, + /* 93 Exec... */ &&_SUBR_CODE, + /* 94 Alloc */ &&_SUBR_CODE, + /* 95 Free */ &&_SUBR, + /* 96 Realloc */ &&_SUBR_CODE, + /* 97 StrPtr */ &&_SUBR_CODE, + /* 98 Sleep... */ &&_SUBR_CODE, + /* 99 VarPtr */ &&_SUBR_CODE, + /* 9A Collection */ &&_SUBR_CODE, + /* 9B Tr$ */ &&_SUBR, + /* 9C Quote$... */ &&_SUBR_CODE, + /* 9D Unquote$... */ &&_SUBR_CODE, + /* 9E MkInt$... */ &&_SUBR_CODE, + /* 9F Byte@... */ &&_SUBR_CODE, + /* A0 ADD QUICK */ &&_ADD_QUICK, + /* A1 ADD QUICK */ &&_ADD_QUICK, + /* A2 ADD QUICK */ &&_ADD_QUICK, + /* A3 ADD QUICK */ &&_ADD_QUICK, + /* A4 ADD QUICK */ &&_ADD_QUICK, + /* A5 ADD QUICK */ &&_ADD_QUICK, + /* A6 ADD QUICK */ &&_ADD_QUICK, + /* A7 ADD QUICK */ &&_ADD_QUICK, + /* A8 ADD QUICK */ &&_ADD_QUICK, + /* A9 ADD QUICK */ &&_ADD_QUICK, + /* AA ADD QUICK */ &&_ADD_QUICK, + /* AB ADD QUICK */ &&_ADD_QUICK, + /* AC ADD QUICK */ &&_ADD_QUICK, + /* AD ADD QUICK */ &&_ADD_QUICK, + /* AE ADD QUICK */ &&_ADD_QUICK, + /* AF ADD QUICK */ &&_ADD_QUICK, + /* B0 PUSH CLASS */ &&_PUSH_CLASS, + /* B1 PUSH CLASS */ &&_PUSH_CLASS, + /* B2 PUSH CLASS */ &&_PUSH_CLASS, + /* B3 PUSH CLASS */ &&_PUSH_CLASS, + /* B4 PUSH CLASS */ &&_PUSH_CLASS, + /* B5 PUSH CLASS */ &&_PUSH_CLASS, + /* B6 PUSH CLASS */ &&_PUSH_CLASS, + /* B7 PUSH CLASS */ &&_PUSH_CLASS, + /* B8 PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* B9 PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BA PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BB PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BC PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BD PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BE PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BF PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* C0 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C1 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C2 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C3 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C4 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C5 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C6 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C7 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C8 PUSH STATIC */ &&_PUSH_STATIC, + /* C9 PUSH STATIC */ &&_PUSH_STATIC, + /* CA PUSH STATIC */ &&_PUSH_STATIC, + /* CB PUSH STATIC */ &&_PUSH_STATIC, + /* CC PUSH STATIC */ &&_PUSH_STATIC, + /* CD PUSH STATIC */ &&_PUSH_STATIC, + /* CE PUSH STATIC */ &&_PUSH_STATIC, + /* CF PUSH STATIC */ &&_PUSH_STATIC, + /* D0 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D1 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D2 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D3 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D4 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D5 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D6 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D7 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D8 POP STATIC */ &&_POP_STATIC, + /* D9 POP STATIC */ &&_POP_STATIC, + /* DA POP STATIC */ &&_POP_STATIC, + /* DB POP STATIC */ &&_POP_STATIC, + /* DC POP STATIC */ &&_POP_STATIC, + /* DD POP STATIC */ &&_POP_STATIC, + /* DE POP STATIC */ &&_POP_STATIC, + /* DF POP STATIC */ &&_POP_STATIC, + /* E0 PUSH CONST */ &&_PUSH_CONST, + /* E1 PUSH CONST */ &&_PUSH_CONST, + /* E2 PUSH CONST */ &&_PUSH_CONST, + /* E3 PUSH CONST */ &&_PUSH_CONST, + /* E4 PUSH CONST */ &&_PUSH_CONST, + /* E5 PUSH CONST */ &&_PUSH_CONST, + /* E6 PUSH CONST */ &&_PUSH_CONST, + /* E7 PUSH CONST */ &&_PUSH_CONST, + /* E8 PUSH CONST */ &&_PUSH_CONST, + /* E9 PUSH CONST */ &&_PUSH_CONST, + /* EA PUSH CONST */ &&_PUSH_CONST, + /* EB PUSH CONST */ &&_PUSH_CONST, + /* EC PUSH CONST */ &&_PUSH_CONST, + /* ED PUSH CONST */ &&_PUSH_CONST, + /* EE PUSH CONST */ &&_PUSH_CONST, + /* EF PUSH CONST */ &&_PUSH_CONST_EX, + /* F0 PUSH QUICK */ &&_PUSH_QUICK, + /* F1 PUSH QUICK */ &&_PUSH_QUICK, + /* F2 PUSH QUICK */ &&_PUSH_QUICK, + /* F3 PUSH QUICK */ &&_PUSH_QUICK, + /* F4 PUSH QUICK */ &&_PUSH_QUICK, + /* F5 PUSH QUICK */ &&_PUSH_QUICK, + /* F6 PUSH QUICK */ &&_PUSH_QUICK, + /* F7 PUSH QUICK */ &&_PUSH_QUICK, + /* F8 PUSH QUICK */ &&_PUSH_QUICK, + /* F9 PUSH QUICK */ &&_PUSH_QUICK, + /* FA PUSH QUICK */ &&_PUSH_QUICK, + /* FB PUSH QUICK */ &&_PUSH_QUICK, + /* FC PUSH QUICK */ &&_PUSH_QUICK, + /* FD PUSH QUICK */ &&_PUSH_QUICK, + /* FE PUSH QUICK */ &&_PUSH_QUICK, + /* FF PUSH QUICK */ &&_PUSH_QUICK + }; + + int NO_WARNING(ind); + ushort code; + + goto _MAIN; + +/*-----------------------------------------------*/ + +_MAIN: + +#if 0 + { + FILE *f; + + f = fopen("/var/log/thttpd/pcode.log", "a"); + if (f) + { + fprintf(f, "%s: ", DEBUG_get_current_position()); + if (*PC >> 8) + PCODE_dump(f, PC - FP->code, PC); + else + fprintf(f, "\n"); + fclose(f); + } + } +#endif + +#if DEBUG_PCODE + DEBUG_where(); + fprintf(stderr, "[%4d] ", (int)(intptr_t)(SP - (VALUE *)STACK_base)); + if (*PC >> 8) + PCODE_dump(stderr, PC - FP->code, PC); + else + fprintf(stderr, "\n"); + fflush(stderr); +#endif + + code = *PC; + goto *jump_table[code >> 8]; + +/*-----------------------------------------------*/ + +_SUBR_CODE: + + //fprintf(stderr, "gbx3: %02X: %s\n", (code >> 8), DEBUG_get_current_position()); + (*(EXEC_FUNC_CODE)EXEC_subr_table[code >> 8])(code); + + //if (PCODE_is_void(code)) + // POP(); + +/*-----------------------------------------------*/ + +_NEXT: + + code = *(++PC); + +#if 0 + { + FILE *f; + + f = fopen("/var/log/thttpd/pcode.log", "a"); + if (f) + { + fprintf(f, "%s: ", DEBUG_get_current_position()); + if (*PC >> 8) + PCODE_dump(f, PC - FP->code, PC); + else + fprintf(f, "\n"); + fclose(f); + } + } +#endif + +#if DEBUG_PCODE + DEBUG_where(); + fprintf(stderr, "[%4d] ", (int)(intptr_t)(SP - (VALUE *)STACK_base)); + if (*PC >> 8) + PCODE_dump(stderr, PC - FP->code, PC); + else + fprintf(stderr, "\n"); + fflush(stderr); +#endif + + goto *jump_table[code >> 8]; + +/*-----------------------------------------------*/ + +_SUBR: + + (*(EXEC_FUNC)EXEC_subr_table[code >> 8])(); + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_LOCAL: + + *SP = BP[GET_XX()]; + PUSH(); + + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_PARAM: + + *SP = PP[GET_XX()]; + PUSH(); + + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_ARRAY: + + EXEC_push_array(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_UNKNOWN: + + EXEC_push_unknown(); + goto _NEXT; + +/*-----------------------------------------------*/ + +/*_PUSH_SPECIAL: + + EXEC_push_special(); + goto _NEXT;*/ + +/*-----------------------------------------------*/ + +_PUSH_EVENT: + + /* + The function called by raising an event is different at each call, + but the signature remains the same, so optimizing the next CALL + instruction with CALL QUICK is safe. + + The only problem is when pushing a 'NULL' function, i.e. a function + that does nothing, because there is no handler for this event. + Then CALL QUICK must know how to handle these functions. + */ + + ind = GET_UX(); + + if (ind >= 0xFE) + ind = EXEC_push_unknown_event(ind & 1); + else if (CP->parent) + ind += CP->parent->n_event; + + SP->type = T_FUNCTION; + SP->_function.kind = FUNCTION_EVENT; + SP->_function.index = ind; + SP->_function.defined = FALSE; + SP->_function.class = NULL; + SP->_function.object = NULL; + SP++; + + goto _NEXT; + +/*-----------------------------------------------*/ + +_POP_LOCAL: + + { + VALUE *val = &BP[GET_XX()]; + + VALUE_conv(&SP[-1], val->type); + + RELEASE(val); + SP--; + *val = *SP; + } + + goto _NEXT; + +/*-----------------------------------------------*/ + +_POP_PARAM: + + { + VALUE *val = &PP[GET_XX()]; + + VALUE_conv(&SP[-1], val->type); + + RELEASE(val); + SP--; + *val = *SP; + } + + goto _NEXT; + +/*-----------------------------------------------*/ + +_POP_CTRL: + + _pop_ctrl(GET_XX()); + goto _NEXT; + +/*-----------------------------------------------*/ + +_POP_ARRAY: + + EXEC_pop_array(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_POP_UNKNOWN: + + EXEC_pop_unknown(); + goto _NEXT; + +/*-----------------------------------------------*/ + +_POP_OPTIONAL: + + { + VALUE *val = &PP[GET_XX()]; + + if (LIKELY(val->type == T_VOID)) + { + if (SP[-1].type == T_VOID) + VALUE_default(&SP[-1], val->_void.ptype); + else + VALUE_conv(&SP[-1], val->_void.ptype); + + SP--; + *val = *SP; + } + else + POP(); + } + + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_SHORT: + + SP->type = T_INTEGER; + PC++; + SP->_integer.value = *((short *)PC); + SP++; + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_INTEGER: + + SP->type = T_INTEGER; + PC++; + SP->_integer.value = PC[0] | ((uint)PC[1] << 16); + SP++; + PC += 2; + goto _MAIN; + +/*-----------------------------------------------*/ + +_PUSH_CHAR: + + STRING_char_value(SP, GET_UX()); + SP++; + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_ME: + + if (GET_UX() & 1) + { + if (DEBUG_info) + { + if (DEBUG_info->op) + { + SP->_object.class = DEBUG_info->cp; + SP->_object.object = DEBUG_info->op; + } + else if (DEBUG_info->cp) + { + SP->type = T_CLASS; + SP->_class.class = DEBUG_info->cp; + } + } + else + VALUE_null(SP); + } + else + { + if (LIKELY(OP != NULL)) + { + SP->_object.class = CP; + SP->_object.object = OP; + } + else + { + SP->type = T_CLASS; + SP->_class.class = CP; + } + } + + if (GET_UX() & 2) + { + // The used class must be in the stack, because it is tested by exec_push && exec_pop + if (LIKELY(OP != NULL)) + { + SP->_object.class = SP->_object.class->parent; + SP->_object.super = EXEC_super; + } + else + { + SP->_class.class = SP->_class.class->parent; + SP->_class.super = EXEC_super; + } + + EXEC_super = SP; + + //fprintf(stderr, "%s\n", DEBUG_get_current_position()); + //BREAKPOINT(); + } + + PUSH(); + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_MISC: + + { + static const void *_jump[] = + { &&__PUSH_NULL, &&__PUSH_VOID, &&__PUSH_FALSE, &&__PUSH_TRUE, &&__PUSH_LAST, &&__PUSH_STRING, &&__PUSH_PINF, &&__PUSH_MINF, &&__PUSH_COMPLEX, + &&__PUSH_VARGS, &&__PUSH_DROP_VARGS, &&__JIT_RETURN }; + //, &&__POP_LAST }; + + goto *_jump[GET_UX()]; + + __PUSH_NULL: + + VALUE_null(SP); + SP++; + goto _NEXT; + + __PUSH_VOID: + + SP->type = T_VOID; + SP++; + goto _NEXT; + + __PUSH_FALSE: + + SP->type = T_BOOLEAN; + SP->_integer.value = 0; + SP++; + goto _NEXT; + + __PUSH_TRUE: + + SP->type = T_BOOLEAN; + SP->_integer.value = -1; + SP++; + goto _NEXT; + + __PUSH_LAST: + + SP->type = T_OBJECT; + SP->_object.object = EVENT_Last; + OBJECT_REF(EVENT_Last); + SP++; + goto _NEXT; + + __PUSH_STRING: + + SP->type = T_CSTRING; + SP->_string.addr = ""; // NULL + SP->_string.start = SP->_string.len = 0; + SP++; + goto _NEXT; + + __PUSH_PINF: + + SP->type = T_FLOAT; + SP->_float.value = INFINITY; + SP++; + goto _NEXT; + + __PUSH_MINF: + + SP->type = T_FLOAT; + SP->_float.value = -INFINITY; + SP++; + goto _NEXT; + + __PUSH_COMPLEX: + + EXEC_push_complex(); + goto _NEXT; + + __PUSH_VARGS: + + EXEC_push_vargs(); + goto _NEXT; + + __PUSH_DROP_VARGS: + + EXEC_drop_vargs(); + goto _NEXT; + + __JIT_RETURN: + return; + + /*__POP_LAST: + + VALUE_conv(&SP[-1], T_OBJECT); + OBJECT_UNREF(EVENT_Last); + SP--; + EVENT_Last = SP->_object.object; + goto _NEXT;*/ + } + +/*-----------------------------------------------*/ + +/* +_PUSH_RETURN: + + *SP++ = *RP; + goto _NEXT; +*/ + +/*-----------------------------------------------*/ + +_DUP: + + *SP = SP[-1]; + PUSH(); + goto _NEXT; + +/*-----------------------------------------------*/ + +_DROP: + + POP(); + goto _NEXT; + +/*-----------------------------------------------*/ + +_NEW: + + EXEC_new(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_ON_GOTO_GOSUB: + + { + int n, m; + + m = GET_XX(); + SP--; + VALUE_conv_integer(SP); + n = SP->_integer.value; + if (n < 0 || n >= m) + PC += m + 3; + else + { + PC[m + 2] = PC[n + 1] - (m - n) - 2; + PC += m + 1; + } + goto _MAIN; + } + +/*-----------------------------------------------*/ + +_GOSUB: + + { + VALUE *ctrl; + int i; + + STACK_check(1 + FP->stack_usage - FP->n_local); + + SP->type = T_VOID; + SP->_void.value[0] = (intptr_t)PC; + SP->_void.value[1] = (intptr_t)GP; + + GP = SP; + + SP++; + + ctrl = &BP[FP->n_local]; + for (i = 0; i < FP->n_ctrl; i++) + { + *SP++ = ctrl[i]; + ctrl[i].type = T_NULL; + } + } + +/*-----------------------------------------------*/ + +_JUMP: + + PC += (signed short)PC[1] + 2; + goto _MAIN; + + +/*-----------------------------------------------*/ + +_JUMP_IF_TRUE: + + VALUE_conv_boolean(&SP[-1]); + SP--; + if (SP->_boolean.value & 1) + PC += (signed short)PC[1]; + + PC += 2; + goto _MAIN; + +/*-----------------------------------------------*/ + +_JUMP_IF_FALSE: + + VALUE_conv_boolean(&SP[-1]); + SP--; + if ((SP->_boolean.value & 1) == 0) + PC += (signed short)PC[1]; + + PC += 2; + goto _MAIN; + +/*-----------------------------------------------*/ + +_RETURN: + + { + static const void *return_jump[] = { &&__RETURN_GOSUB, &&__RETURN_VALUE, &&__RETURN_VOID }; + + TYPE type; + VALUE *ctrl; + int i; + + goto *return_jump[GET_UX()]; + + __RETURN_GOSUB: + + if (!GP) + goto __RETURN_VOID; + + ctrl = &BP[FP->n_local]; + GP++; + + for (i = 0; i < FP->n_ctrl; i++) + { + RELEASE(&ctrl[i]); + ctrl[i] = GP[i]; + } + + GP--; + + SP = GP; + PC = (PCODE *)GP->_void.value[0] + 2; + GP = (VALUE *)GP->_void.value[1]; + + goto _MAIN; + + __RETURN_VALUE: + + type = FP->type; + //if (TYPE_is_pure_object(type) && ((CLASS *)type)->override) + // type = (TYPE)(((CLASS *)type)->override); + + VALUE_conv(&SP[-1], type); + SP--; + *RP = *SP; + + goto __RETURN_LEAVE; + + __RETURN_VOID: + + VALUE_default(RP, FP->type); + + __RETURN_LEAVE: + + EXEC_leave_keep(); + + if (PC == NULL) + return; + + goto _NEXT; + } + +/*-----------------------------------------------*/ + +_CALL: + + { + VALUE * NO_WARNING(val); + + { + static const void *call_jump[] = + { + &&__CALL_NULL, &&__CALL_NATIVE, &&__CALL_PRIVATE, &&__CALL_PUBLIC, + &&__CALL_EVENT, &&__CALL_EXTERN, &&__CALL_UNKNOWN, &&__CALL_CALL, + &&__CALL_SUBR + }; + + ind = GET_3X(); + val = &SP[-(ind + 1)]; + + if (!TYPE_is_function(val->type)) + { + bool defined = EXEC_object(val, &EXEC.class, (OBJECT **)&EXEC.object); + + val->type = T_FUNCTION; + val->_function.kind = FUNCTION_CALL; + val->_function.defined = defined; + val->_function.class = EXEC.class; + val->_function.object = EXEC.object; + //goto _CALL; + } + else + { + EXEC.class = val->_function.class; + EXEC.object = val->_function.object; + } + + EXEC.nparam = ind; + EXEC.use_stack = TRUE; + + if (!val->_function.defined) + *PC |= CODE_CALL_VARIANT; + + goto *call_jump[(int)val->_function.kind]; + + __CALL_NULL: + + while (ind > 0) + { + POP(); + ind--; + } + + POP(); + + //if (!PCODE_is_void(code)) + { + /*VALUE_default(SP, (TYPE)(val->_function.function));*/ + VALUE_null(SP); + SP++; + } + + goto _NEXT; + + __CALL_NATIVE: + + EXEC.native = TRUE; + EXEC.index = val->_function.index; + EXEC.desc = &EXEC.class->table[EXEC.index].desc->method; + //EXEC.use_stack = TRUE; + + goto __EXEC_NATIVE; + + __CALL_PRIVATE: + + EXEC.native = FALSE; + EXEC.index = val->_function.index; + EXEC.func = &EXEC.class->load->func[EXEC.index]; + + goto __EXEC_ENTER; + + __CALL_PUBLIC: + + EXEC.native = FALSE; + EXEC.desc = &EXEC.class->table[val->_function.index].desc->method; + EXEC.index = (int)(intptr_t)(EXEC.desc->exec); + EXEC.class = EXEC.desc->class; + EXEC.func = &EXEC.class->load->func[EXEC.index]; + + goto __EXEC_ENTER; + + __EXEC_ENTER: + + if (EXEC.func->fast) + { + JIT_exec(TRUE); + goto _NEXT; + } + else + { + EXEC_enter_check(val->_function.defined); + goto _MAIN; + } + + __EXEC_NATIVE: + + EXEC_native_check(val->_function.defined); + goto _NEXT; + + __CALL_EVENT: + + //if (OP && !strcmp(OBJECT_class(OP)->name, "Workspace")) + // BREAKPOINT(); + ind = GB_Raise(OP, val->_function.index, (-EXEC.nparam)); + + POP(); // function + + //if (!PCODE_is_void(code)) + { + SP->type = T_BOOLEAN; + SP->_boolean.value = ind ? -1 : 0; + SP++; + } + + //EVENT_Last = old_last; + + goto _NEXT; + + __CALL_UNKNOWN: + + EXEC_unknown_name = CP->load->unknown[val->_function.index]; + EXEC.desc = CLASS_get_special_desc(EXEC.class, SPEC_UNKNOWN); + //EXEC.use_stack = TRUE; + goto __CALL_SPEC; + + __CALL_CALL: + + EXEC.desc = CLASS_get_special_desc(EXEC.class, SPEC_CALL); + + if (EXEC.desc) + { + if (!CLASS_DESC_is_static_method(EXEC.desc) && !EXEC.object) + { + if (!EXEC.class->auto_create) + THROW(E_DYNAMIC, CLASS_get_name(EXEC.class), $("_call")); + + EXEC.object = EXEC_auto_create(EXEC.class, FALSE); + EXEC.nparam = ind; + } + + goto __CALL_SPEC; + } + + if (!EXEC.object && EXEC.nparam == 1 && !EXEC.class->is_virtual) + { + SP[-2] = SP[-1]; + SP--; + VALUE_conv_object(SP - 1, (TYPE)EXEC.class); + goto _NEXT; + } + + __CALL_SPEC: + + if (!EXEC.desc) + THROW(E_NFUNC); + + EXEC.native = FUNCTION_is_native(EXEC.desc); + + if (EXEC.native) + { + EXEC_native(); + goto _NEXT; + } + else + { + EXEC.index = (int)(intptr_t)(EXEC.desc->exec); + EXEC.class = EXEC.desc->class; + EXEC.func = &EXEC.class->load->func[EXEC.index]; + + if (EXEC.func->fast) + { + JIT_exec(TRUE); + goto _NEXT; + } + else + { + EXEC_enter(); + goto _MAIN; + } + } + + __CALL_EXTERN: + + EXEC.index = val->_function.index; + EXTERN_call(); + goto _NEXT; + + __CALL_SUBR: + + ((EXEC_FUNC_CODE)(EXEC.class->table[val->_function.index].desc->method.exec))(code); + SP[-2] = SP[-1]; + SP--; + goto _NEXT; + } + +/*-----------------------------------------------*/ + +_CALL_QUICK: + + { + static const void *call_jump[] = + { + &&__CALL_NULL, &&__CALL_NATIVE_Q, &&__CALL_PRIVATE_Q, &&__CALL_PUBLIC_Q, + &&__CALL_EVENT, &&__CALL_EXTERN, &&__CALL_UNKNOWN, &&__CALL_CALL, + &&__CALL_SUBR + }; + + ind = GET_3X(); + val = &SP[-(ind + 1)]; + + EXEC.class = val->_function.class; + EXEC.object = val->_function.object; + EXEC.nparam = ind; + + if (!val->_function.defined) + *PC |= CODE_CALL_VARIANT; + + //if (call_jump[(int)val->_function.kind] == 0) + // fprintf(stderr, "val->_function.kind = %d ?\n", val->_function.kind); + + goto *call_jump[(int)val->_function.kind]; + + __CALL_PRIVATE_Q: + + EXEC.native = FALSE; + EXEC.index = val->_function.index; + + goto __EXEC_ENTER_Q; + + __CALL_PUBLIC_Q: + + EXEC.native = FALSE; + EXEC.desc = &EXEC.class->table[val->_function.index].desc->method; + EXEC.index = (int)(intptr_t)(EXEC.desc->exec); + EXEC.class = EXEC.desc->class; + + __EXEC_ENTER_Q: + + EXEC_enter_quick(); + goto _MAIN; + + __CALL_NATIVE_Q: + + EXEC.native = TRUE; + EXEC.index = val->_function.index; + EXEC.desc = &EXEC.class->table[EXEC.index].desc->method; + + EXEC_native_quick(); + goto _NEXT; + } + +/*-----------------------------------------------*/ + +#if 0 +_CALL_EASY: + { + static const void *call_jump[] = + { &&__CALL_NULL, &&__CALL_NATIVE_E, &&__CALL_PRIVATE_E, &&__CALL_PUBLIC_E }; + + VALUE * NO_WARNING(val); + + ind = GET_3X(); + val = &SP[-(ind + 1)]; + + EXEC.class = val->_function.class; + EXEC.object = val->_function.object; + EXEC.nparam = ind; + + if (!val->_function.defined) + *PC |= CODE_CALL_VARIANT; + + //if (call_jump[(int)val->_function.kind] == 0) + // fprintf(stderr, "val->_function.kind = %d ?\n", val->_function.kind); + + goto *call_jump[(int)val->_function.kind]; + + __CALL_PRIVATE_E: + + EXEC.native = FALSE; + EXEC.index = val->_function.index; + + goto __EXEC_ENTER_E; + + __CALL_PUBLIC_E: + + EXEC.native = FALSE; + EXEC.desc = &EXEC.class->table[val->_function.index].desc->method; + EXEC.index = (int)(intptr_t)(EXEC.desc->exec); + EXEC.class = EXEC.desc->class; + + __EXEC_ENTER_E: + + EXEC_enter_easy(); + goto _MAIN; + + __CALL_NATIVE_E: + + EXEC.native = TRUE; + EXEC.index = val->_function.index; + EXEC.desc = &EXEC.class->table[EXEC.index].desc->method; + + EXEC_native_easy(); + goto _NEXT; + } +#endif + +/*-----------------------------------------------*/ + +_CALL_SLOW: + + { + static const void *call_jump[] = + { + &&__CALL_NULL, &&__CALL_NATIVE_S, &&__CALL_PRIVATE_S, &&__CALL_PUBLIC_S, + &&__CALL_EVENT, &&__CALL_EXTERN, &&__CALL_UNKNOWN, &&__CALL_CALL, + &&__CALL_SUBR + }; + + ind = GET_3X(); + val = &SP[-(ind + 1)]; + + EXEC.class = val->_function.class; + EXEC.object = val->_function.object; + EXEC.nparam = ind; + EXEC.use_stack = TRUE; + + if (!val->_function.defined) + *PC |= CODE_CALL_VARIANT; + + goto *call_jump[(int)val->_function.kind]; + + __CALL_PRIVATE_S: + + EXEC.native = FALSE; + EXEC.index = val->_function.index; + + goto __EXEC_ENTER_S; + + __CALL_PUBLIC_S: + + EXEC.native = FALSE; + EXEC.desc = &EXEC.class->table[val->_function.index].desc->method; + EXEC.index = (int)(intptr_t)(EXEC.desc->exec); + EXEC.class = EXEC.desc->class; + + __EXEC_ENTER_S: + + EXEC.func = &EXEC.class->load->func[EXEC.index]; + + if (EXEC.func->fast) + { + JIT_exec(TRUE); + goto _NEXT; + } + else + { + EXEC_enter(); + goto _MAIN; + } + + __CALL_NATIVE_S: + + EXEC.native = TRUE; + EXEC.index = val->_function.index; + EXEC.desc = &EXEC.class->table[EXEC.index].desc->method; + + EXEC_native(); + goto _NEXT; + } + } + +/*-----------------------------------------------*/ + +_JUMP_FIRST: + + { + static const void *const jn_jump[] = + { + NULL, &&_JN_INTEGER_INC, &&_JN_BYTE, &&_JN_SHORT, &&_JN_INTEGER_DEC, &&_JN_LONG, &&_JN_SINGLE, &&_JN_FLOAT + }; + + TYPE type; + VALUE * NO_WARNING(inc); + VALUE * NO_WARNING(val); + VALUE * NO_WARNING(end); + + ind = GET_XX(); + + end = &BP[ind]; + inc = &BP[ind + 1]; + val = &BP[PC[3] & 0xFF]; + + type = val->type; + + if (type < T_BYTE || type > T_FLOAT) + THROW(E_TYPE, "Number", TYPE_get_name(type)); + + if (type > T_INTEGER) + VALUE_conv(&SP[-1], type); + else + VALUE_conv_integer(&SP[-1]); + + VALUE_conv(&SP[-2], type); + + _pop_ctrl(ind + 1); /* modifie val ! */ + _pop_ctrl(ind); + val = &BP[PC[3] & 0xFF]; + + // loop mode is stored in the inc type. It must be strictly lower than T_STRING + + if (type == T_INTEGER && inc->_integer.value > 0) + type = 1; + + inc->type = type; + + PC++; + + if (type <= T_INTEGER) + { + if (inc->_integer.value < 0) + goto _JN_INTEGER_TEST_DEC; + else + goto _JN_INTEGER_TEST_INC; + } + else if (type == T_LONG) + goto _JN_LONG_TEST; + else if (type == T_SINGLE) + goto _JN_SINGLE_TEST; + else //if (type == T_FLOAT) + goto _JN_FLOAT_TEST; + +/*-----------------------------------------------*/ + +_JUMP_NEXT: + + end = &BP[PC[-1] & 0xFF]; + inc = end + 1; + val = &BP[PC[2] & 0xFF]; + + goto *jn_jump[inc->type]; + + _JN_BYTE: + val->_integer.value = (unsigned char)(val->_integer.value + inc->_integer.value); + if (inc->_integer.value < 0) + goto _JN_INTEGER_TEST_DEC; + else + goto _JN_INTEGER_TEST_INC; + + _JN_SHORT: + val->_integer.value = (short)(val->_integer.value + inc->_integer.value); + if (inc->_integer.value < 0) + goto _JN_INTEGER_TEST_DEC; + else + goto _JN_INTEGER_TEST_INC; + + _JN_INTEGER_INC: + val->_integer.value += inc->_integer.value; + + _JN_INTEGER_TEST_INC: + if (val->_integer.value <= end->_integer.value) + { + PC += 3; + goto _MAIN; + } + else + goto _JN_END; + + _JN_INTEGER_DEC: + val->_integer.value += inc->_integer.value; + + _JN_INTEGER_TEST_DEC: + if (val->_integer.value >= end->_integer.value) + { + PC += 3; + goto _MAIN; + } + else + goto _JN_END; + + _JN_LONG: + val->_long.value += inc->_long.value; + + _JN_LONG_TEST: + if ((inc->_long.value > 0 && val->_long.value <= end->_long.value) + || (inc->_long.value < 0 && val->_long.value >= end->_long.value)) + { + PC += 3; + goto _MAIN; + } + else + goto _JN_END; + + _JN_SINGLE: + val->_single.value += inc->_single.value; + + _JN_SINGLE_TEST: + if ((inc->_single.value > 0 && val->_single.value <= end->_single.value) + || (inc->_single.value < 0 && val->_single.value >= end->_single.value)) + { + PC += 3; + goto _MAIN; + } + else + goto _JN_END; + + _JN_FLOAT: + val->_float.value += inc->_float.value; + + _JN_FLOAT_TEST: + if ((inc->_float.value > 0 && val->_float.value <= end->_float.value) + || (inc->_float.value < 0 && val->_float.value >= end->_float.value)) + { + PC += 3; + goto _MAIN; + } + else + goto _JN_END; + + _JN_END: + PC += (signed short)PC[1] + 2; + goto _MAIN; + } + +/*-----------------------------------------------*/ + +_ENUM_FIRST: + + ind = GET_XX(); + _pop_ctrl(ind); + EXEC_enum_first(code, &BP[ind], &BP[ind + 1]); + goto _NEXT; + +/*-----------------------------------------------*/ + +_ENUM_NEXT: + + ind = PC[-1] & 0xFF; + if (EXEC_enum_next(code, &BP[ind], &BP[ind + 1])) + goto _JUMP; + else + { + PC += 2; + goto _MAIN; + } + +/*-----------------------------------------------*/ + +_PUSH_CLASS: + + { + CLASS *class = CP->load->class_ref[GET_7XX()]; + + //CLASS_load(class); + //fprintf(stderr, "PUSH CLASS: %s %s\n", class->name, class->auto_create ? "AUTO CREATE" : ""); + + SP->type = T_CLASS; + SP->_class.class = class; + SP++; + + //fprintf(stderr, "PUSH CLASS: %s in %s\n", SP->_class.class->name, SP->_class.class->component ? SP->_class.class->component->name : NULL); + } + + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_FUNCTION: + + /*ind = GET_7XX();*/ + + SP->type = T_FUNCTION; + SP->_function.class = CP; + SP->_function.object = OP; + SP->_function.kind = FUNCTION_PRIVATE; + SP->_function.index = GET_7XX(); + SP->_function.defined = TRUE; + + OBJECT_REF(OP); + SP++; + + goto _NEXT; + +/*-----------------------------------------------*/ + +_PUSH_EXTERN: + + /*ind = GET_7XX();*/ + + SP->type = T_FUNCTION; + SP->_function.class = CP; + SP->_function.object = NULL; + SP->_function.kind = FUNCTION_EXTERN; + SP->_function.index = GET_UX(); + SP->_function.defined = TRUE; + + //OBJECT_REF(OP, "exec_loop._PUSH_FUNCTION (FUNCTION)"); + SP++; + + goto _NEXT; + +/*-----------------------------------------------*/ + + { + CLASS_VAR *var; + char *addr; + void *ref; + +_PUSH_DYNAMIC: + + var = &CP->load->dyn[GET_7XX()]; + + //if (OP == NULL) + // THROW_ILLEGAL(); + + ref = OP; + addr = &OP[var->pos]; + goto __READ; + +_PUSH_STATIC: + + var = &CP->load->stat[GET_7XX()]; + addr = (char *)CP->stat + var->pos; + ref = CP; + goto __READ; + +__READ: + + my_VALUE_class_read(CP, SP, addr, var->type, ref); + + PUSH(); + goto _NEXT; + + +_POP_DYNAMIC: + + var = &CP->load->dyn[GET_7XX()]; + + //if (OP == NULL) + // THROW_ILLEGAL(); + + addr = &OP[var->pos]; + goto __WRITE; + +_POP_STATIC: + + var = &CP->load->stat[GET_7XX()]; + addr = (char *)CP->stat + var->pos; + goto __WRITE; + +__WRITE: + + VALUE_class_write(CP, &SP[-1], addr, var->type); + POP(); + + goto _NEXT; + + } + +/*-----------------------------------------------*/ + +_PUSH_CONST: + + ind = GET_UXX(); + my_VALUE_class_constant(CP, SP, ind); + SP++; + goto _NEXT; + + +/*-----------------------------------------------*/ + +_PUSH_CONST_EX: + + PC++; + my_VALUE_class_constant(CP, SP, *PC); + SP++; + goto _NEXT; + + +/*-----------------------------------------------*/ + +_PUSH_QUICK: + + SP->type = T_INTEGER; + SP->_integer.value = GET_XXX(); + SP++; + goto _NEXT; + +/*-----------------------------------------------*/ + +_ADD_QUICK: + + { + static void *_aq_jump[] = { + &&__AQ_VOID, &&__AQ_BOOLEAN, &&__AQ_BYTE, &&__AQ_SHORT, &&__AQ_INTEGER, &&__AQ_LONG, &&__AQ_SINGLE, &&__AQ_FLOAT, + &&__AQ_DATE, &&__AQ_STRING, &&__AQ_STRING, &&__AQ_POINTER, &&__AQ_VARIANT, &&__AQ_BOOLEAN, &&__AQ_BOOLEAN, &&__AQ_BOOLEAN + }; + + TYPE NO_WARNING(type); + int NO_WARNING(value); + VALUE * NO_WARNING(P1); + void * NO_WARNING(jump_end); + + P1 = SP - 1; + + jump_end = &&__AQ_END; + type = P1->type; + value = GET_XXX(); + + __AQ_JUMP: + + if (TYPE_is_object(type)) + goto __AQ_OBJECT; + else + goto *_aq_jump[type]; + + __AQ_VOID: + + THROW(E_NRETURN); + + __AQ_BYTE: + + P1->_integer.value = (unsigned char)(P1->_integer.value + value); + goto *jump_end; + + __AQ_SHORT: + + P1->_integer.value = (short)(P1->_integer.value + value); + goto *jump_end; + + __AQ_INTEGER: + + P1->_integer.value += value; + goto *jump_end; + + __AQ_LONG: + + P1->_long.value += (int64_t)value; + goto *jump_end; + + __AQ_SINGLE: + + P1->_single.value += (float)value; + goto *jump_end; + + __AQ_DATE: + __AQ_STRING: + + VALUE_conv_float(P1); + + __AQ_FLOAT: + + P1->_float.value += (double)value; + goto *jump_end; + + __AQ_POINTER: + + P1->_pointer.value += value; + goto *jump_end; + + __AQ_VARIANT: + + jump_end = &&__AQ_VARIANT_END; + VARIANT_undo(P1); + type = P1->type; + goto __AQ_JUMP; + + __AQ_OBJECT: + + if (EXEC_check_operator_single(P1, CO_ADDF)) + { + EXEC_operator_object_add_quick(P1, value); + goto *jump_end; + } + + __AQ_BOOLEAN: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); + + __AQ_VARIANT_END: + + VALUE_conv_variant(P1); + + __AQ_END: + goto _NEXT; + } + +/*-----------------------------------------------*/ + +_TRY: + + EP = SP; + ET = EC; + EC = PC + (signed short)PC[1] + 2; + + #if DEBUG_ERROR + fprintf(stderr, "exec TRY %p\n", EC); + #endif + + PC += 2; + goto _MAIN; + +/*-----------------------------------------------*/ + +_END_TRY: + + #if DEBUG_ERROR + fprintf(stderr, "exec END TRY %p\n", PC); + #endif + + // If EP was reset to null, then an error occurred + EXEC_got_error = (EP == NULL); + EP = NULL; + EC = ET; + ET = NULL; + goto _NEXT; + +/*-----------------------------------------------*/ + +_CATCH: + + if (EC == NULL) + goto _NEXT; + else + goto __RETURN_VOID; + +/*-----------------------------------------------*/ + +_BREAK: + + _break(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_QUIT: + + EXEC_quit(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_BYREF: + + if (LIKELY(PC == FP->code)) + { + PC += GET_UX() + 2; + goto _MAIN; + } + + THROW(E_BYREF); + +/*-----------------------------------------------*/ + +_SUBR_COMPN: + + _SUBR_compe(code); + goto _NEXT; + +_SUBR_COMPE: + + _SUBR_compe(code); + goto _NEXT; + +#if 0 + { + static void *jump[] = { + &&__SC_VARIANT, &&__SC_BOOLEAN, &&__SC_BYTE, &&__SC_SHORT, &&__SC_INTEGER, &&__SC_LONG, &&__SC_SINGLE, &&__SC_FLOAT, + &&__SC_DATE, &&__SC_STRING, &&__SC_STRING, &&__SC_POINTER, &&__SC_ERROR, &&__SC_ERROR, &&__SC_ERROR, &&__SC_NULL, + &&__SC_OBJECT, &&__SC_OBJECT_FLOAT, &&__SC_FLOAT_OBJECT, &&__SC_OBJECT_OTHER, &&__SC_OTHER_OBJECT, &&__SC_OBJECT_OBJECT, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + NULL, &&__SC_BOOLEAN, &&__SC_BYTE, &&__SC_SHORT, &&__SC_INTEGER, &&__SC_LONG_NC, &&__SC_SINGLE_NC, &&__SC_FLOAT_NC, + &&__SC_DATE_NC, &&__SC_STRING_NC, &&__SC_STRING_NC, &&__SC_POINTER_NC, &&__SC_ERROR, &&__SC_ERROR, &&__SC_ERROR, &&__SC_NULL, + &&__SC_OBJECT + }; + + char NO_WARNING(result); + VALUE *NO_WARNING(P1); + VALUE *NO_WARNING(P2); + +_SUBR_COMPN: + + result = 1; + goto _SUBR_COMP; + +_SUBR_COMPE: + + result = 0; + +_SUBR_COMP: + + P1 = SP - 2; + P2 = SP - 1; + + goto *jump[code & 0x3F]; + + __SC_BOOLEAN: + __SC_BYTE: + __SC_SHORT: + __SC_INTEGER: + + result ^= P1->_integer.value == P2->_integer.value; + goto __SC_END; + + __SC_LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + __SC_LONG_NC: + + result ^= P1->_long.value == P2->_long.value; + goto __SC_END; + + __SC_DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + __SC_DATE_NC: + + result ^= DATE_comp_value(P1, P2) == 0; + goto __SC_END; + + __SC_NULL: + + if (P2->type == T_NULL) + { + result ^= VALUE_is_null(P1); + goto __SC_END_RELEASE; + } + else if (P1->type == T_NULL) + { + result ^= VALUE_is_null(P2); + goto __SC_END_RELEASE; + } + + __SC_STRING: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + + __SC_STRING_NC: + + if (P1->_string.len == P2->_string.len) + result ^= STRING_equal_same(P1->_string.addr + P1->_string.start, P2->_string.addr + P2->_string.start, P1->_string.len); + + RELEASE_STRING(P1); + RELEASE_STRING(P2); + goto __SC_END; + + __SC_SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + __SC_SINGLE_NC: + + result ^= P1->_single.value == P2->_single.value; + goto __SC_END; + + __SC_FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + __SC_FLOAT_NC: + + result ^= P1->_float.value == P2->_float.value; + goto __SC_END; + + __SC_POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + + __SC_POINTER_NC: + + result ^= P1->_pointer.value == P2->_pointer.value; + goto __SC_END; + + __SC_OBJECT: + + result ^= OBJECT_comp_value(P1, P2) == 0; + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __SC_END_RELEASE; + + __SC_OBJECT_FLOAT: + + result ^= EXEC_comparator(OP_OBJECT_FLOAT, CO_EQUALF, P1, P2); + goto __SC_END; + + __SC_FLOAT_OBJECT: + + result ^= EXEC_comparator(OP_FLOAT_OBJECT, CO_EQUALF, P1, P2); + goto __SC_END; + + __SC_OBJECT_OTHER: + + result ^= EXEC_comparator(OP_OBJECT_OTHER, CO_EQUALO, P1, P2); + goto __SC_END; + + __SC_OTHER_OBJECT: + + result ^= EXEC_comparator(OP_OTHER_OBJECT, CO_EQUALO, P1, P2); + goto __SC_END; + + __SC_OBJECT_OBJECT: + + result ^= EXEC_comparator(OP_OBJECT_OBJECT, CO_EQUAL, P1, P2); + goto __SC_END; + + __SC_VARIANT: + + { + bool variant = FALSE; + TYPE type; + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + code = EXEC_check_operator(P1, P2, CO_EQUAL); + if (code) + { + code += T_OBJECT; + if (!(variant || P1->type == T_OBJECT || P2->type == T_OBJECT)) + *PC |= code; + goto *jump[code]; + } + + type = Max(P1->type, P2->type); + + if (TYPE_is_object_null(P1->type) && TYPE_is_object_null(P2->type)) + type = T_OBJECT; + else if (TYPE_is_object(type)) + THROW(E_TYPE, "Object", TYPE_get_name(Min(P1->type, P2->type))); + else if (TYPE_is_void(type)) + THROW(E_NRETURN); + + if (!variant) + { + if (P1->type == P2->type) + *PC |= 0x20; + *PC |= type; + } + + goto *jump[type]; + } + + __SC_ERROR: + + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(code & 0x1F)); + + __SC_END_RELEASE: + + RELEASE(P1); + RELEASE(P2); + + __SC_END: + + P1->type = T_BOOLEAN; + P1->_boolean.value = -result; + SP--; + goto _NEXT; + } +#endif + +/*-----------------------------------------------*/ + + { + char NO_WARNING(result); + VALUE *NO_WARNING(P1); + VALUE *NO_WARNING(P2); + + static void *jump[] = { + &&__SCI_VARIANT, &&__SCI_BOOLEAN, &&__SCI_BYTE, &&__SCI_SHORT, &&__SCI_INTEGER, &&__SCI_LONG, &&__SCI_SINGLE, &&__SCI_FLOAT, + &&__SCI_DATE, &&__SCI_STRING, &&__SCI_STRING, &&__SCI_POINTER, &&__SCI_ERROR, &&__SCI_ERROR, &&__SCI_ERROR, &&__SCI_NULL, + &&__SCI_OBJECT, &&__SCI_OBJECT_FLOAT, &&__SCI_FLOAT_OBJECT, &&__SCI_OBJECT_OTHER, &&__SCI_OTHER_OBJECT, &&__SCI_OBJECT_OBJECT, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + NULL, &&__SCI_BOOLEAN, &&__SCI_BYTE, &&__SCI_SHORT, &&__SCI_INTEGER, &&__SCI_LONG_NC, &&__SCI_SINGLE_NC, &&__SCI_FLOAT_NC, + &&__SCI_DATE_NC, &&__SCI_STRING_NC, &&__SCI_STRING_NC, &&__SCI_POINTER_NC, &&__SCI_ERROR, &&__SCI_ERROR, &&__SCI_ERROR, &&__SCI_NULL, + &&__SCI_OBJECT + }; + +_SUBR_COMPGT: + + P1 = SP - 2; + P2 = SP - 1; + result = 0; + goto _SUBR_COMPI; + +_SUBR_COMPLT: + + P1 = SP - 1; + P2 = SP - 2; + result = 0; + goto _SUBR_COMPI; + +_SUBR_COMPLE: + + P1 = SP - 2; + P2 = SP - 1; + result = 1; + goto _SUBR_COMPI; + +_SUBR_COMPGE: + + P1 = SP - 1; + P2 = SP - 2; + result = 1; + goto _SUBR_COMPI; + +_SUBR_COMPI: + + goto *jump[code & 0x3F]; + + __SCI_BOOLEAN: + __SCI_BYTE: + __SCI_SHORT: + __SCI_INTEGER: + + result ^= P1->_integer.value > P2->_integer.value; + goto __SCI_END; + + __SCI_LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + __SCI_LONG_NC: + + result ^= P1->_long.value > P2->_long.value; + goto __SCI_END; + + __SCI_DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + __SCI_DATE_NC: + + result ^= DATE_comp_value(P1, P2) > 0; + goto __SCI_END; + + __SCI_NULL: + __SCI_STRING: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + + __SCI_STRING_NC: + + result ^= STRING_compare(P1->_string.addr + P1->_string.start, P1->_string.len, P2->_string.addr + P2->_string.start, P2->_string.len) > 0; + + RELEASE_STRING(P1); + RELEASE_STRING(P2); + goto __SCI_END; + + __SCI_SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + __SCI_SINGLE_NC: + + result ^= P1->_single.value > P2->_single.value; + goto __SCI_END; + + __SCI_FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + __SCI_FLOAT_NC: + + result ^= P1->_float.value > P2->_float.value; + goto __SCI_END; + + __SCI_POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + + __SCI_POINTER_NC: + + result ^= P1->_pointer.value > P2->_pointer.value; + goto __SCI_END; + + __SCI_OBJECT: + + result ^= OBJECT_comp_value(P1, P2) > 0; + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __SCI_END_RELEASE; + + __SCI_OBJECT_FLOAT: + + result ^= EXEC_comparator(OP_OBJECT_FLOAT, CO_COMPF, P1, P2) > 0; + goto __SCI_END; + + __SCI_FLOAT_OBJECT: + + result ^= EXEC_comparator(OP_FLOAT_OBJECT, CO_COMPF, P1, P2) > 0; + goto __SCI_END; + + __SCI_OBJECT_OTHER: + + result ^= EXEC_comparator(OP_OBJECT_OTHER, CO_COMPO, P1, P2) > 0; + goto __SCI_END; + + __SCI_OTHER_OBJECT: + + result ^= EXEC_comparator(OP_OTHER_OBJECT, CO_COMPO, P1, P2) > 0; + goto __SCI_END; + + __SCI_OBJECT_OBJECT: + + result ^= EXEC_comparator(OP_OBJECT_OBJECT, CO_COMP, P1, P2) > 0; + goto __SCI_END; + + __SCI_VARIANT: + + { + bool variant = FALSE; + int op; + TYPE type; + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + op = EXEC_check_operator(P1, P2, CO_COMP); + if (op) + { + op += T_OBJECT; + if (!(variant || P1->type == T_OBJECT || P2->type == T_OBJECT)) + *PC |= op; + goto *jump[op]; + } + + type = Max(P1->type, P2->type); + + if (type == T_NULL || TYPE_is_string(type)) + { + TYPE typem = Min(P1->type, P2->type); + if (!TYPE_is_string(typem)) + THROW_TYPE(typem, type); + } + else if (TYPE_is_object(type)) + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(type)); + else if (TYPE_is_void(type)) + THROW(E_NRETURN); + + if (!variant) + { + if (P1->type == P2->type) + *PC |= 0x20; + *PC |= type; + } + + goto *jump[type]; + } + + __SCI_ERROR: + + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(code & 0x1F)); + + __SCI_END_RELEASE: + + RELEASE(P1); + RELEASE(P2); + + __SCI_END: + + SP -= 2; + SP->type = T_BOOLEAN; + SP->_boolean.value = -result; + SP++; + goto _NEXT; + } + + +/*-----------------------------------------------*/ + +#define EXEC_code code + +_SUBR_CONV: + + VALUE_conv(SP - 1, code & 0x3F); + goto _NEXT; + +/*-----------------------------------------------*/ + +_SUBR_LEFT: + + SUBR_left(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_SUBR_RIGHT: + + SUBR_right(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_SUBR_MID: + + SUBR_mid(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_SUBR_LEN: + + { + int len; + + SUBR_GET_PARAM(1); + + if (SUBR_check_string(PARAM)) + len = 0; + else + len = PARAM->_string.len; + + RELEASE_STRING(PARAM); + + PARAM->type = T_INTEGER; + PARAM->_integer.value = len; + } + goto _NEXT; + +/*-----------------------------------------------*/ + +_SUBR_ADD: + + _SUBR_add(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_SUBR_SUB: + + _SUBR_sub(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_SUBR_MUL: + + _SUBR_mul(code); + goto _NEXT; + +/*-----------------------------------------------*/ + +_SUBR_DIV: + + _SUBR_div(code); + goto _NEXT; + +} + + +static void my_VALUE_class_read(CLASS *class, VALUE *value, char *addr, CTYPE ctype, void *ref) +{ + VALUE_class_read_inline(class, value, addr, ctype, ref); +} + + +#if 0 +static void _SUBR_compn(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__ERROR, &&__ERROR, &&__ERROR, &&__NULL, &&__OBJECT, + &&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_OTHER, &&__OTHER_OBJECT, &&__OBJECT_OBJECT + }; + + //static void *test[] = { &&__EQ, &&__NE, &&__GT, &&__LE, &&__LT, &&__GE }; + + char NO_WARNING(result); + VALUE *P1; + VALUE *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + goto *jump[code & 0x1F]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + result = P1->_integer.value == P2->_integer.value; + goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + result = P1->_long.value == P2->_long.value; + goto __END; + +__DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + result = DATE_comp_value(P1, P2) == 0; + goto __END; + +__NULL: + + if (P2->type == T_NULL) + { + result = VALUE_is_null(P1); + goto __END_RELEASE; + } + else if (P1->type == T_NULL) + { + result = VALUE_is_null(P2); + goto __END_RELEASE; + } + +__STRING: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + + if (P1->_string.len != P2->_string.len) + result = 0; + else + result = STRING_equal_same(P1->_string.addr + P1->_string.start, P2->_string.addr + P2->_string.start, P1->_string.len); + + RELEASE_STRING(P1); + RELEASE_STRING(P2); + goto __END; + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + result = P1->_single.value == P2->_single.value; + goto __END; + +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + result = P1->_float.value == P2->_float.value; + goto __END; + +__POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + + result = P1->_pointer.value == P2->_pointer.value; + goto __END; + +__OBJECT: + + result = OBJECT_comp_value(P1, P2) == 0; + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __END_RELEASE; + +__OBJECT_FLOAT: + + result = EXEC_comparator(OP_OBJECT_FLOAT, CO_EQUALF, P1, P2); + goto __END; + +__FLOAT_OBJECT: + + result = EXEC_comparator(OP_FLOAT_OBJECT, CO_EQUALF, P1, P2); + goto __END; + +__OBJECT_OTHER: + + result = EXEC_comparator(OP_OBJECT_OTHER, CO_EQUALO, P1, P2); + goto __END; + +__OTHER_OBJECT: + + result = EXEC_comparator(OP_OTHER_OBJECT, CO_EQUALO, P1, P2); + goto __END; + +__OBJECT_OBJECT: + + result = EXEC_comparator(OP_OBJECT_OBJECT, CO_EQUAL, P1, P2); + goto __END; + +__VARIANT: + + { + bool variant = FALSE; + TYPE type; + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + code = EXEC_check_operator(P1, P2, CO_EQUAL); + if (code) + { + code += T_OBJECT; + if (!(variant || P1->type == T_OBJECT || P2->type == T_OBJECT)) + *PC |= code; + goto *jump[code]; + } + + type = Max(P1->type, P2->type); + + if (TYPE_is_object_null(P1->type) && TYPE_is_object_null(P2->type)) + type = T_OBJECT; + else if (TYPE_is_object(type)) + THROW(E_TYPE, "Object", TYPE_get_name(Min(P1->type, P2->type))); + else if (TYPE_is_void(type)) + THROW(E_NRETURN); + + if (!variant) + *PC |= type; + + goto *jump[type]; + } + +__ERROR: + + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(code & 0x1F)); + +__END_RELEASE: + + RELEASE(P1); + RELEASE(P2); + +__END: + + P1->type = T_BOOLEAN; + SP--; + + P1->_boolean.value = result - 1; // ? 0 : -1; +} +#endif + +/*#define sgn(_x) \ +({ \ + int x = _x; \ + int minusOne = x >> 31; \ + unsigned int negateX = (unsigned int) -x; \ + int plusOne = (int)(negateX >> 31); \ + int result = minusOne | plusOne; \ + result; \ +})*/ + +static void my_VALUE_class_constant(CLASS *class, VALUE *value, int ind) +{ + VALUE_class_constant_inline(class, value, ind); +} + +#define MANAGE_VARIANT_OBJECT(_func, _op) \ +({ \ + type = Max(P1->type, P2->type); \ + if (TYPE_is_void(P1->type) || TYPE_is_void(P2->type)) \ + THROW(E_NRETURN); \ + \ + if (TYPE_is_number_date(type)) \ + { \ + *PC |= type; \ + if (P1->type == P2->type) \ + *PC |= 0x10; \ + goto *jump[type]; \ + } \ + \ + code = EXEC_check_operator(P1, P2, _op); \ + if (code) \ + { \ + code += T_DATE; \ + if (!(P1->type == T_OBJECT || P2->type == T_OBJECT)) \ + *PC |= code; \ + goto *jump[code]; \ + } \ + \ + VARIANT_undo(P1); \ + VARIANT_undo(P2); \ + \ + if (TYPE_is_string(P1->type)) \ + VALUE_conv_float(P1); \ + \ + if (TYPE_is_string(P2->type)) \ + VALUE_conv_float(P2); \ + \ + if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) \ + type = T_NULL; \ + else \ + type = Max(P1->type, P2->type); \ + \ + if (TYPE_is_number_date(type)) \ + { \ + (_func)(code | type); \ + VALUE_conv_variant(P1); \ + return; \ + } \ + \ + code = EXEC_check_operator(P1, P2, _op); \ + if (code) \ + { \ + (_func)(code + T_DATE); \ + VALUE_conv_variant(P1); \ + return; \ + } \ +}) + +#define MANAGE_VARIANT_POINTER_OBJECT(_func, _op) \ +({ \ + type = Max(P1->type, P2->type); \ + if (TYPE_is_void(P1->type) || TYPE_is_void(P2->type)) \ + THROW(E_NRETURN); \ + \ + if (TYPE_is_number_date(type) || TYPE_is_pointer(type)) \ + { \ + *PC |= type; \ + if (P1->type == P2->type) \ + *PC |= 0x10; \ + goto *jump[type]; \ + } \ + \ + code = EXEC_check_operator(P1, P2, _op); \ + if (code) \ + { \ + code += T_POINTER; \ + if (P1->type != T_OBJECT && P2->type != T_OBJECT) \ + *PC |= code; \ + goto *jump[code]; \ + } \ + \ + VARIANT_undo(P1); \ + VARIANT_undo(P2); \ + \ + if (TYPE_is_string(P1->type)) \ + VALUE_conv_float(P1); \ + \ + if (TYPE_is_string(P2->type)) \ + VALUE_conv_float(P2); \ + \ + if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) \ + type = T_NULL; \ + else \ + type = Max(P1->type, P2->type); \ + \ + if (TYPE_is_number_date(type) || TYPE_is_pointer(type)) \ + { \ + (_func)(code | type); \ + VALUE_conv_variant(P1); \ + return; \ + } \ + \ + code = EXEC_check_operator(P1, P2, _op); \ + if (code) \ + { \ + (_func)(code + T_POINTER); \ + VALUE_conv_variant(P1); \ + return; \ + } \ +}) + + +static void _SUBR_add(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, + &&__DATE, NULL, NULL, &&__POINTER, &&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_OTHER, &&__OTHER_OBJECT, + &&__OBJECT, && __BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG_NC, &&__SINGLE_NC, &&__FLOAT_NC, + &&__DATE, NULL, NULL, &&__POINTER_NC + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x1F; + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_BOOLEAN; + P1->_integer.value = P1->_integer.value | P2->_integer.value; SP--; return; + +__BYTE: + + P1->type = T_BYTE; + P1->_integer.value = (unsigned char)(P1->_integer.value + P2->_integer.value); SP--; return; + +__SHORT: + + P1->type = T_SHORT; + P1->_integer.value = (short)(P1->_integer.value + P2->_integer.value); SP--; return; + +__INTEGER: + + P1->type = T_INTEGER; + P1->_integer.value += P2->_integer.value; SP--; return; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + +__LONG_NC: + + P1->_long.value += P2->_long.value; SP--; return; + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + +__SINGLE_NC: + + P1->_single.value += P2->_single.value; SP--; return; + +__DATE: +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + +__FLOAT_NC: + + P1->_float.value += P2->_float.value; SP--; return; + +__POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + +__POINTER_NC: + + P1->_pointer.value += (intptr_t)P2->_pointer.value; SP--; return; + +__OBJECT_FLOAT: + + EXEC_operator(OP_OBJECT_FLOAT, CO_ADDF, P1, P2); SP--; return; + +__FLOAT_OBJECT: + + EXEC_operator(OP_FLOAT_OBJECT, CO_ADDF, P1, P2); SP--; return; + +__OBJECT_OTHER: + + EXEC_operator(OP_OBJECT_OTHER, CO_ADDO, P1, P2); SP--; return; + +__OTHER_OBJECT: + + EXEC_operator(OP_OTHER_OBJECT, CO_ADDO, P1, P2); SP--; return; + +__OBJECT: + + EXEC_operator(OP_OBJECT_OBJECT, CO_ADD, P1, P2); SP--; return; + +__VARIANT: + + MANAGE_VARIANT_POINTER_OBJECT(_SUBR_add, CO_ADD); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); +} + +static void _SUBR_sub(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, + &&__DATE, NULL, NULL, &&__POINTER, &&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_OTHER, &&__OTHER_OBJECT, + &&__OBJECT, && __BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG_NC, &&__SINGLE_NC, &&__FLOAT_NC, + &&__DATE, NULL, NULL, &&__POINTER_NC + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x1F; + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_BOOLEAN; + P1->_integer.value = P1->_integer.value ^ P2->_integer.value; SP--; return; + +__BYTE: + + P1->type = T_BYTE; + P1->_integer.value = (unsigned char)(P1->_integer.value - P2->_integer.value); SP--; return; + +__SHORT: + + P1->type = T_SHORT; + P1->_integer.value = (short)(P1->_integer.value - P2->_integer.value); SP--; return; + +__INTEGER: + + P1->type = T_INTEGER; + P1->_integer.value -= P2->_integer.value; SP--; return; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + +__LONG_NC: + + P1->_long.value -= P2->_long.value; SP--; return; + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + +__SINGLE_NC: + + P1->_single.value -= P2->_single.value; SP--; return; + +__DATE: +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + +__FLOAT_NC: + + P1->_float.value -= P2->_float.value; SP--; return; + +__POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + +__POINTER_NC: + + P1->_pointer.value -= (intptr_t)P2->_pointer.value; SP--; return; + +__OBJECT_FLOAT: + + EXEC_operator(OP_OBJECT_FLOAT, CO_SUBF, P1, P2); SP--; return; + +__FLOAT_OBJECT: + + EXEC_operator(OP_FLOAT_OBJECT, CO_SUBF, P1, P2); SP--; return; + +__OBJECT_OTHER: + + EXEC_operator(OP_OBJECT_OTHER, CO_SUBO, P1, P2); SP--; return; + +__OTHER_OBJECT: + + EXEC_operator(OP_OTHER_OBJECT, CO_SUBO, P1, P2); SP--; return; + +__OBJECT: + + EXEC_operator(OP_OBJECT_OBJECT, CO_SUB, P1, P2); SP--; return; + +__VARIANT: + + MANAGE_VARIANT_POINTER_OBJECT(_SUBR_sub, CO_SUB); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); +} + +static void _SUBR_mul(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, + &&__ERROR, &&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_OTHER, &&__OTHER_OBJECT, &&__OBJECT, NULL, NULL, + NULL, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG_NC, &&__SINGLE_NC, &&__FLOAT_NC, &&__ERROR, + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x1F; + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_BOOLEAN; + P1->_integer.value = P1->_integer.value & P2->_integer.value; SP--; return; + +__BYTE: + + P1->type = T_BYTE; + P1->_integer.value = (unsigned char)(P1->_integer.value * P2->_integer.value); SP--; return; + +__SHORT: + + P1->type = T_SHORT; + P1->_integer.value = (short)(P1->_integer.value * P2->_integer.value); SP--; return; + +__INTEGER: + + P1->type = T_INTEGER; + P1->_integer.value *= P2->_integer.value; SP--; return; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + +__LONG_NC: + + P1->_long.value *= P2->_long.value; SP--; return; + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + +__SINGLE_NC: + + P1->_single.value *= P2->_single.value; SP--; return; + +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + +__FLOAT_NC: + + P1->_float.value *= P2->_float.value; SP--; return; + +__OBJECT_FLOAT: + + EXEC_operator(OP_OBJECT_FLOAT, CO_MULF, P1, P2); SP--; return; + +__FLOAT_OBJECT: + + EXEC_operator(OP_FLOAT_OBJECT, CO_MULF, P1, P2); SP--; return; + +__OBJECT_OTHER: + + EXEC_operator(OP_OBJECT_OTHER, CO_MULO, P1, P2); SP--; return; + +__OTHER_OBJECT: + + EXEC_operator(OP_OTHER_OBJECT, CO_MULO, P1, P2); SP--; return; + +__OBJECT: + + EXEC_operator(OP_OBJECT_OBJECT, CO_MUL, P1, P2); SP--; return; + +__VARIANT: + + MANAGE_VARIANT_OBJECT(_SUBR_mul, CO_MUL); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); +} + +static void _SUBR_div(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, + &&__ERROR, &&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_OTHER, &&__OTHER_OBJECT, &&__OBJECT, NULL, NULL, + NULL, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE_NC, &&__FLOAT_NC, &&__ERROR, + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x1F; + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: +__LONG: +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + +__FLOAT_NC: + + P1->_float.value /= P2->_float.value; + if (isfinite(P1->_float.value)) + { + SP--; + return; + } + + THROW(E_ZERO); + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + +__SINGLE_NC: + + P1->_single.value /= P2->_single.value; + if (isfinite(P1->_single.value)) + { + SP--; + return; + } + + THROW(E_ZERO); + +__OBJECT_FLOAT: + + EXEC_operator(OP_OBJECT_FLOAT, CO_DIVF, P1, P2); + goto __CHECK_OBJECT; + +__FLOAT_OBJECT: + + EXEC_operator(OP_FLOAT_OBJECT, CO_DIVF, P1, P2); + goto __CHECK_OBJECT; + +__OBJECT_OTHER: + + EXEC_operator(OP_OBJECT_OTHER, CO_DIVO, P1, P2); + goto __CHECK_OBJECT; + +__OTHER_OBJECT: + + EXEC_operator(OP_OTHER_OBJECT, CO_DIVO, P1, P2); + goto __CHECK_OBJECT; + +__OBJECT: + + EXEC_operator(OP_OBJECT_OBJECT, CO_DIV, P1, P2); + goto __CHECK_OBJECT; + +__VARIANT: + + MANAGE_VARIANT_OBJECT(_SUBR_div, CO_DIV); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); + +__CHECK_OBJECT: + + if (P1->_object.object == NULL) + THROW(E_ZERO); + SP--; +} + +static void _SUBR_compe(ushort code) +{ + static void *jump[] = { + &&__SC_VARIANT, &&__SC_BOOLEAN, &&__SC_BYTE, &&__SC_SHORT, &&__SC_INTEGER, &&__SC_LONG, &&__SC_SINGLE, &&__SC_FLOAT, + &&__SC_DATE, &&__SC_STRING, &&__SC_STRING, &&__SC_POINTER, &&__SC_ERROR, &&__SC_ERROR, &&__SC_ERROR, &&__SC_NULL, + &&__SC_OBJECT, &&__SC_OBJECT_FLOAT, &&__SC_FLOAT_OBJECT, &&__SC_OBJECT_OTHER, &&__SC_OTHER_OBJECT, &&__SC_OBJECT_OBJECT, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + NULL, &&__SC_BOOLEAN, &&__SC_BYTE, &&__SC_SHORT, &&__SC_INTEGER, &&__SC_LONG_NC, &&__SC_SINGLE_NC, &&__SC_FLOAT_NC, + &&__SC_DATE_NC, &&__SC_STRING_NC, &&__SC_STRING_NC, &&__SC_POINTER_NC, &&__SC_ERROR, &&__SC_ERROR, &&__SC_ERROR, &&__SC_NULL, + &&__SC_OBJECT + }; + + char result = code >= C_NE; + VALUE *P1; + VALUE *P2; + + P1 = SP - 2; + P2 = SP - 1; + + goto *jump[code & 0x3F]; + +__SC_BOOLEAN: +__SC_BYTE: +__SC_SHORT: +__SC_INTEGER: + + result ^= P1->_integer.value == P2->_integer.value; + goto __SC_END; + +__SC_LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + +__SC_LONG_NC: + + result ^= P1->_long.value == P2->_long.value; + goto __SC_END; + +__SC_DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + +__SC_DATE_NC: + + result ^= DATE_comp_value(P1, P2) == 0; + goto __SC_END; + +__SC_NULL: + + if (P2->type == T_NULL) + { + result ^= VALUE_is_null(P1); + goto __SC_END_RELEASE; + } + else if (P1->type == T_NULL) + { + result ^= VALUE_is_null(P2); + goto __SC_END_RELEASE; + } + +__SC_STRING: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + +__SC_STRING_NC: + + if (P1->_string.len == P2->_string.len) + result ^= STRING_equal_same(P1->_string.addr + P1->_string.start, P2->_string.addr + P2->_string.start, P1->_string.len); + + RELEASE_STRING(P1); + RELEASE_STRING(P2); + goto __SC_END; + +__SC_SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + +__SC_SINGLE_NC: + + result ^= P1->_single.value == P2->_single.value; + goto __SC_END; + +__SC_FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + +__SC_FLOAT_NC: + + result ^= P1->_float.value == P2->_float.value; + goto __SC_END; + +__SC_POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + +__SC_POINTER_NC: + + result ^= P1->_pointer.value == P2->_pointer.value; + goto __SC_END; + +__SC_OBJECT: + + result ^= OBJECT_comp_value(P1, P2) == 0; + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __SC_END_RELEASE; + +__SC_OBJECT_FLOAT: + + result ^= EXEC_comparator(OP_OBJECT_FLOAT, CO_EQUALF, P1, P2); + goto __SC_END; + +__SC_FLOAT_OBJECT: + + result ^= EXEC_comparator(OP_FLOAT_OBJECT, CO_EQUALF, P1, P2); + goto __SC_END; + +__SC_OBJECT_OTHER: + + result ^= EXEC_comparator(OP_OBJECT_OTHER, CO_EQUALO, P1, P2); + goto __SC_END; + +__SC_OTHER_OBJECT: + + result ^= EXEC_comparator(OP_OTHER_OBJECT, CO_EQUALO, P1, P2); + goto __SC_END; + +__SC_OBJECT_OBJECT: + + result ^= EXEC_comparator(OP_OBJECT_OBJECT, CO_EQUAL, P1, P2); + goto __SC_END; + +__SC_VARIANT: + + { + bool variant = FALSE; + TYPE type; + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + code = EXEC_check_operator(P1, P2, CO_EQUAL); + if (code) + { + code += T_OBJECT; + if (!(variant || P1->type == T_OBJECT || P2->type == T_OBJECT)) + *PC |= code; + goto *jump[code]; + } + + type = Max(P1->type, P2->type); + + if (TYPE_is_object_null(P1->type) && TYPE_is_object_null(P2->type)) + type = T_OBJECT; + else if (TYPE_is_object(type)) + THROW_TYPE(T_OBJECT, Min(P1->type, P2->type)); + else if (TYPE_is_void(type)) + THROW(E_NRETURN); + + if (!variant) + { + if (P1->type == P2->type) + *PC |= 0x20; + *PC |= type; + } + + goto *jump[type]; + } + +__SC_ERROR: + + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(code & 0x1F)); + +__SC_END_RELEASE: + + RELEASE(P1); + RELEASE(P2); + +__SC_END: + + P1->type = T_BOOLEAN; + P1->_boolean.value = -result; + SP--; +} + +#if 0 +static void _SUBR_compi(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__ERROR, &&__ERROR, &&__ERROR, &&__NULL, &&__OBJECT, + &&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_OTHER, &&__OTHER_OBJECT, &&__OBJECT_OBJECT + }; + + static void *test[] = { &&__GT, &&__LE, &&__LT, &&__GE }; + + char NO_WARNING(result); + VALUE *P1; + VALUE *P2; + TYPE type; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x1F; + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + result = P1->_integer.value > P2->_integer.value ? 1 : P1->_integer.value < P2->_integer.value ? -1 : 0; + goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + result = P1->_long.value > P2->_long.value ? 1 : P1->_long.value < P2->_long.value ? -1 : 0; + goto __END; + +__DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + result = DATE_comp_value(P1, P2); + goto __END; + +__NULL: +__STRING: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + + result = STRING_compare(P1->_string.addr + P1->_string.start, P1->_string.len, P2->_string.addr + P2->_string.start, P2->_string.len); + + RELEASE_STRING(P1); + RELEASE_STRING(P2); + goto __END; + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + result = P1->_single.value > P2->_single.value ? 1 : P1->_single.value < P2->_single.value ? -1 : 0; + goto __END; + +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + result = P1->_float.value > P2->_float.value ? 1 : P1->_float.value < P2->_float.value ? -1 : 0; + goto __END; + +__POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + + result = P1->_pointer.value > P2->_pointer.value ? 1 : P1->_pointer.value < P2->_pointer.value ? -1 : 0; + goto __END; + +__OBJECT: + + result = OBJECT_comp_value(P1, P2); + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __END_RELEASE; + +__OBJECT_FLOAT: + + result = EXEC_comparator(OP_OBJECT_FLOAT, CO_COMPF, P1, P2); + goto __END; + +__FLOAT_OBJECT: + + result = EXEC_comparator(OP_FLOAT_OBJECT, CO_COMPF, P1, P2); + goto __END; + +__OBJECT_OTHER: + + result = EXEC_comparator(OP_OBJECT_OTHER, CO_COMPO, P1, P2); + goto __END; + +__OTHER_OBJECT: + + result = EXEC_comparator(OP_OTHER_OBJECT, CO_COMPO, P1, P2); + goto __END; + +__OBJECT_OBJECT: + + result = EXEC_comparator(OP_OBJECT_OBJECT, CO_COMP, P1, P2); + goto __END; + +__VARIANT: + + { + bool variant = FALSE; + int op; + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + op = EXEC_check_operator(P1, P2, CO_COMP); + if (op) + { + op += T_OBJECT; + if (!(variant || P1->type == T_OBJECT || P2->type == T_OBJECT)) + *PC |= op; + goto *jump[op]; + } + + type = Max(P1->type, P2->type); + + if (type == T_NULL || TYPE_is_string(type)) + { + TYPE typem = Min(P1->type, P2->type); + if (!TYPE_is_string(typem)) + THROW(E_TYPE, TYPE_get_name(typem), TYPE_get_name(type)); + } + else if (TYPE_is_object(type)) + goto __ERROR; + else if (TYPE_is_void(type)) + THROW(E_NRETURN); + + if (!variant) + *PC |= type; + + goto *jump[type]; + } + +__ERROR: + + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(type)); + +__END_RELEASE: + + RELEASE(P1); + RELEASE(P2); + +__END: + + P1->type = T_BOOLEAN; + SP--; + + goto *test[(code >> 8) - (C_GT >> 8)]; + +__GT: + P1->_boolean.value = result > 0 ? -1 : 0; + return; + +__GE: + P1->_boolean.value = result >= 0 ? -1 : 0; + return; + +__LT: + P1->_boolean.value = result < 0 ? -1 : 0; + return; + +__LE: + P1->_boolean.value = result <= 0 ? -1 : 0; + return; +} +#endif + +void EXEC_push_array(ushort code) +{ + static const void *jump[] = { + &&__PUSH_GENERIC, &&__PUSH_GENERIC, &&__PUSH_GENERIC, &&__PUSH_GENERIC, + &&__PUSH_ARRAY, &&__PUSH_ARRAY, &&__PUSH_ARRAY, &&__PUSH_ARRAY, + &&__PUSH_NATIVE_ARRAY, NULL, &&__PUSH_NATIVE_ARRAY_SIMPLE, &&__PUSH_NATIVE_ARRAY_SIMPLE, + &&__PUSH_NATIVE_COLLECTION, &&__PUSH_NATIVE_ARRAY_INTEGER, &&__PUSH_NATIVE_ARRAY_FLOAT, &&__PUSH_NATIVE_STRING + }; + + CLASS *class; + OBJECT *object; + ushort np; + int i; + void *NO_WARNING(data); + bool defined; + VALUE *NO_WARNING(val); + uint fast; + CARRAY *array; + + goto *jump[(code & 0xF0) >> 4]; + +__PUSH_GENERIC: + + np = GET_3X(); + val = &SP[-np]; + np--; + + defined = EXEC_object(val, &class, &object); + + if (class->quick_array == CQA_STRING) + { + if (np < 1) + THROW(E_NEPARAM); + else if (np > 2) + THROW(E_TMPARAM); + + if (defined) + *PC = (*PC & 0xFF00) | (0xF1 + np); + + goto __PUSH_NATIVE_STRING; + } + + fast = 0x41 + np; + + if (defined) + { + if (class->quick_array == CQA_ARRAY) + { + if (np == 1) + { + array = (CARRAY *)object; + if (array->type == GB_T_INTEGER) + fast = 0xD0; + else if (array->type == GB_T_FLOAT) + fast = 0xE0; + else if (TYPE_is_object(array->type)) + fast = 0xB0; + else + fast = 0xA0 + array->type; + } + else + { + if (np > MAX_ARRAY_DIM) + THROW(E_TMPARAM); + + fast = 0x81 + np; + } + } + else if (class->quick_array == CQA_COLLECTION) + { + if (np < 1) + THROW(E_NEPARAM); + else if (np > 1) + THROW(E_TMPARAM); + + fast = 0xC0; + } + else + { + // Check the symbol existance, but *not virtually* + if (object && !VALUE_is_super(val)) + { + CLASS *nvclass = val->_object.class; + + if (nvclass->special[SPEC_GET] == NO_SYMBOL) + THROW(E_NARRAY, CLASS_get_name(nvclass)); + } + } + } + + *PC = (*PC & 0xFF00) | fast; + + goto __PUSH_ARRAY_2; + +__PUSH_NATIVE_ARRAY: + + np = GET_3X(); + val = &SP[-np]; + np--; + EXEC_object_array(val, class, object); + + for (i = 1; i <= np; i++) + VALUE_conv_integer(&val[i]); + + data = CARRAY_get_data_multi((CARRAY *)object, (GB_INTEGER *)&val[1], np); + if (!data) + PROPAGATE(); + + VALUE_read(val, data, ((CARRAY *)object)->type); + goto __PUSH_NATIVE_END; + +__PUSH_NATIVE_COLLECTION: + + val = &SP[-2]; + EXEC_object_array(val, class, object); + + VALUE_conv_string(&val[1]); + //fprintf(stderr, "GB_CollectionGet: %p '%.*s'\n", val[1]._string.addr, val[1]._string.len, val[1]._string.addr + val[1]._string.start); + GB_CollectionGet((GB_COLLECTION)object, val[1]._string.addr + val[1]._string.start, val[1]._string.len, (GB_VARIANT *)val); + + RELEASE_STRING(&val[1]); + goto __PUSH_NATIVE_END; + +__PUSH_NATIVE_STRING: + + BoxedString_get(GET_0X() - 1); + return; + +__PUSH_NATIVE_ARRAY_SIMPLE: + + val = &SP[-2]; + np = code & 0x1F; + + EXEC_object_array(val, class, object); + array = (CARRAY *)object; + + VALUE_conv_integer(&val[1]); + + data = CARRAY_get_data_throw(array, val[1]._integer.value); + + VALUE_read_inline_type(val, data, np, array->type, __PUSH_NATIVE_END_NOREF, __PUSH_NATIVE_END); + +__PUSH_NATIVE_ARRAY_INTEGER: + + val = &SP[-2]; + EXEC_object_array(val, class, object); + array = (CARRAY *)object; + + VALUE_conv_integer(&val[1]); + i = val[1]._integer.value; + + if (i < 0 || i >= array->count) + THROW(E_BOUND); + + val->_integer.value = ((int *)(array->data))[i]; + val->type = GB_T_INTEGER; + goto __PUSH_NATIVE_END_NOREF; + +__PUSH_NATIVE_ARRAY_FLOAT: + + val = &SP[-2]; + EXEC_object_array(val, class, object); + array = (CARRAY *)object; + + VALUE_conv_integer(&val[1]); + i = val[1]._integer.value; + + if (i < 0 || i >= array->count) + THROW(E_BOUND); + + val->_float.value = ((double *)(array->data))[i]; + val->type = GB_T_FLOAT; + goto __PUSH_NATIVE_END_NOREF; + +__PUSH_NATIVE_END_NOREF: + + SP = val + 1; + OBJECT_UNREF(object); + return; + +__PUSH_NATIVE_END: + + SP = val; + PUSH(); + OBJECT_UNREF(object); + return; + +__PUSH_ARRAY: + + np = GET_3X(); + val = &SP[-np]; + np--; + defined = EXEC_object(val, &class, &object); + +__PUSH_ARRAY_2: + + if (EXEC_special(SPEC_GET, class, object, np, FALSE)) + THROW(E_NARRAY, CLASS_get_name(class)); + + OBJECT_UNREF(object); + SP--; + //SP[-1] = SP[0]; + VALUE_copy(&SP[-1], &SP[0]); + + if (!defined) + VALUE_conv_variant(&SP[-1]); +} + +void EXEC_pop_array(ushort code) +{ + static const void *jump[] = { + &&__POP_GENERIC, &&__POP_GENERIC, &&__POP_GENERIC, &&__POP_GENERIC, + &&__POP_ARRAY, &&__POP_ARRAY, &&__POP_ARRAY, &&__POP_ARRAY, + &&__POP_NATIVE_ARRAY, NULL, &&__POP_NATIVE_ARRAY_SIMPLE, &&__POP_NATIVE_ARRAY_SIMPLE, + &&__POP_NATIVE_COLLECTION, &&__POP_NATIVE_ARRAY_INTEGER, &&__POP_NATIVE_ARRAY_FLOAT, NULL + }; + + + CLASS *class; + OBJECT *object; + ushort np; + int i; + void *data; + bool defined; + VALUE *NO_WARNING(val); + VALUE swap; + int fast; + CARRAY *array; + + goto *jump[(code & 0xF0) >> 4]; + +__POP_GENERIC: + + np = GET_3X(); + val = &SP[-np]; + + defined = EXEC_object(val, &class, &object); + + fast = 0x40 + np; + + if (defined) + { + if (class->quick_array == CQA_ARRAY) + { + if (np == 2) + { + array = (CARRAY *)object; + if (array->type == GB_T_INTEGER) + fast = 0xD0; + else if (array->type == GB_T_FLOAT) + fast = 0xE0; + else if (TYPE_is_object(array->type)) + fast = 0xB0; + else + fast = 0xA0 + array->type; + } + else + { + if (np > (MAX_ARRAY_DIM + 1)) + THROW(E_TMPARAM); + + fast = 0x80 + np; + } + } + else if (class->quick_array == CQA_COLLECTION) + { + if (np < 2) + THROW(E_NEPARAM); + else if (np > 2) + THROW(E_TMPARAM); + + fast = 0xC0; + } + else + { + // Check the symbol existance, but *not virtually* + if (object && !VALUE_is_super(val)) + { + CLASS *nvclass = val->_object.class; + + if (nvclass->special[SPEC_PUT] == NO_SYMBOL) + THROW(E_NARRAY, CLASS_get_name(nvclass)); + } + } + } + + *PC = (*PC & 0xFF00) | fast; + + goto __POP_ARRAY_2; + +__POP_NATIVE_ARRAY: + + np = GET_3X(); + val = &SP[-np]; + + EXEC_object_array(val, class, object); + array = (CARRAY *)object; + + /*VALUE_copy(&swap, &val[0]); + VALUE_copy(&val[0], &val[-1]); + VALUE_copy(&val[-1], &swap);*/ + + //VALUE_conv(&val[0], type); + for (i = 1; i < np; i++) + VALUE_conv_integer(&val[i]); + + data = CARRAY_get_data_multi((CARRAY *)object, (GB_INTEGER *)&val[1], np - 1); + if (data == NULL) + PROPAGATE(); + + VALUE_write(&val[-1], data, array->type); + goto __POP_NATIVE_END; + +__POP_NATIVE_COLLECTION: + + val = &SP[-2]; + EXEC_object_array(val, class, object); + + VALUE_conv_variant(&val[-1]); + VALUE_conv_string(&val[1]); + + if (GB_CollectionSet((GB_COLLECTION)object, val[1]._string.addr + val[1]._string.start, val[1]._string.len, (GB_VARIANT *)&val[-1])) + PROPAGATE(); + + RELEASE_MANY(SP, 3); + return; + +__POP_NATIVE_ARRAY_SIMPLE: + + val = &SP[-2]; + EXEC_object_array(val, class, object); + array = (CARRAY *)object; + + /*VALUE_copy(&swap, &val[0]); + VALUE_copy(&val[0], &val[-1]); + VALUE_copy(&val[-1], &swap);*/ + + //VALUE_conv(&val[0], type); + VALUE_conv_integer(&val[1]); + + data = CARRAY_get_data(array, val[1]._integer.value); + if (data == NULL) + PROPAGATE(); + + VALUE_write(&val[-1], data, array->type); + goto __POP_NATIVE_END; + +__POP_NATIVE_ARRAY_INTEGER: + + val = &SP[-2]; + EXEC_object_array(val, class, object); + array = (CARRAY *)object; + + /*VALUE_copy(&swap, &val[0]); + VALUE_copy(&val[0], &val[-1]); + VALUE_copy(&val[-1], &swap);*/ + + VALUE_conv_integer(&val[-1]); + VALUE_conv_integer(&val[1]); + + i = val[1]._integer.value; + if (i < 0 || i >= array->count) + THROW(E_BOUND); + + ((int *)(array->data))[i] = val[-1]._integer.value; + goto __POP_NATIVE_FAST_END; + +__POP_NATIVE_ARRAY_FLOAT: + + val = &SP[-2]; + EXEC_object_array(val, class, object); + array = (CARRAY *)object; + + /*VALUE_copy(&swap, &val[0]); + VALUE_copy(&val[0], &val[-1]); + VALUE_copy(&val[-1], &swap);*/ + + VALUE_conv_float(&val[-1]); + VALUE_conv_integer(&val[1]); + + i = val[1]._integer.value; + if (i < 0 || i >= array->count) + THROW(E_BOUND); + + ((double *)(array->data))[i] = val[-1]._float.value; + +__POP_NATIVE_FAST_END: + + SP = val + 1; + OBJECT_UNREF(object); + SP -= 2; + return; + +__POP_NATIVE_END: + + SP = val + 1; + OBJECT_UNREF(object); + RELEASE(SP - 2); + SP -= 2; + //OBJECT_UNREF(object); + return; + +__POP_ARRAY: + + np = GET_3X(); + val = &SP[-np]; + + defined = EXEC_object(val, &class, &object); + +__POP_ARRAY_2: + + /* swap object and value to be inserted */ + VALUE_copy(&swap, &val[0]); + VALUE_copy(&val[0], &val[-1]); + VALUE_copy(&val[-1], &swap); + + if (EXEC_special(SPEC_PUT, class, object, np, TRUE)) + THROW(E_NARRAY, CLASS_get_name(class)); + + POP(); /* free the object */ +} + +void EXEC_quit(ushort code) +{ + switch(code & 3) + { + case 0: + EXEC_do_quit(); + break; + + case 1: + if (EXEC_debug && CP) // && CP->component == COMPONENT_main) + DEBUG.Breakpoint(0); + break; + + case 2: + GAMBAS_StopEvent = TRUE; + break; + + case 3: + VALUE_conv(&SP[-1], T_BYTE); + SP--; + EXEC_quit_value = (uchar)SP->_integer.value; + EXEC_do_quit(); + break; + } +} + +static void _break(ushort code) +{ + if (EXEC_debug) + { + /*TC = PC + 1; + TP = SP;*/ + + //fprintf(stderr, "%s\n", DEBUG_get_current_position()); + + if (CP && CP->component == COMPONENT_main) + { + if (EXEC_profile_instr) + DEBUG.Profile.Add(CP, FP, PC); + + code = (uchar)code; + + if (code == 0) + { + if (!DEBUG_info->stop) + return; + + // Return from (void stack) + if (DEBUG_info->leave) + { + if (STACK_get_current()->pc) + return; + if (FP == DEBUG_info->fp) + return; + if (BP > DEBUG_info->bp) + return; + } + // Forward or Return From + else if (DEBUG_info->fp != NULL) + { + if (BP > DEBUG_info->bp) + return; + } + // otherwise, Next + } + + DEBUG.Breakpoint(code); + } + } + else + *PC = C_NOP; +} + +void SUBR_left(ushort code) +{ + int val; + + SUBR_ENTER(); + + if (LIKELY(!SUBR_check_string(PARAM))) + { + if (NPARAM == 1) + val = 1; + else + { + VALUE_conv_integer(&PARAM[1]); + val = PARAM[1]._integer.value; + } + + if (val < 0) + val += PARAM->_string.len; + + PARAM->_string.len = MinMax(val, 0, PARAM->_string.len); + } + + SP -= NPARAM; + SP++; +} + +void SUBR_mid(ushort code) +{ + int start; + int len; + bool null; + + SUBR_ENTER(); + + null = SUBR_check_string(PARAM); + + VALUE_conv_integer(&PARAM[1]); + start = PARAM[1]._integer.value - 1; + + if (start < 0) + THROW(E_ARG); + + if (null) + goto _SUBR_MID_FIN; + + if (start >= PARAM->_string.len) + { + VOID_STRING(PARAM); + goto _SUBR_MID_FIN; + } + + if (NPARAM == 2) + len = PARAM->_string.len; + else + { + VALUE_conv_integer(&PARAM[2]); + len = PARAM[2]._integer.value; + } + + if (len < 0) + len = Max(0, PARAM->_string.len - start + len); + + len = MinMax(len, 0, PARAM->_string.len - start); + + if (len == 0) + { + RELEASE_STRING(PARAM); + PARAM->_string.addr = NULL; + PARAM->_string.start = 0; + } + else + PARAM->_string.start += start; + + PARAM->_string.len = len; + +_SUBR_MID_FIN: + + SP -= NPARAM; + SP++; +} + +void SUBR_right(ushort code) +{ + int val; + int new_len; + + SUBR_ENTER(); + + if (LIKELY(!SUBR_check_string(PARAM))) + { + if (NPARAM == 1) + val = 1; + else + { + VALUE_conv_integer(&PARAM[1]); + val = PARAM[1]._integer.value; + } + + if (val < 0) + val += PARAM->_string.len; + + new_len = MinMax(val, 0, PARAM->_string.len); + + PARAM->_string.start += PARAM->_string.len - new_len; + PARAM->_string.len = new_len; + } + + SP -= NPARAM; + SP++; +} + diff --git a/main/gbx/gbx_exec_operator.c b/main/gbx/gbx_exec_operator.c new file mode 100644 index 00000000..e02cc975 --- /dev/null +++ b/main/gbx/gbx_exec_operator.c @@ -0,0 +1,368 @@ +/*************************************************************************** + + gbx_exec_operator.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_EXEC_OPERATOR_C + +#include "gb_common.h" +#include "gbx_type.h" +#include "gbx_api.h" +#include "gbx_exec.h" + +typedef + void *(*FUNC_O_OF)(void *, double, bool); + +typedef + void *(*FUNC_O_OO)(void *, void *, bool); + +typedef + int (*FUNC_I_OF)(void *, double, bool); + +typedef + int (*FUNC_I_OO)(void *, void *, bool); + +typedef + void *(*FUNC_O_O)(void *); + +typedef + int (*FUNC_I_O)(void *); + +typedef + double (*FUNC_F_O)(void *); + +static void raise_error(void *o1, void *o2) +{ + if (o2 && OBJECT_class(o2) == OBJECT_class(o1)) + GB_Error((char *)E_TYPE, "Number", TYPE_get_name((TYPE)OBJECT_class(o1))); + else + GB_Error((char *)E_TYPE, TYPE_get_name((TYPE)OBJECT_class(o1)), o2 ? TYPE_get_name((TYPE)OBJECT_class(o2)) : "Number"); +} + +bool EXEC_check_operator_single(VALUE *P1, uchar op) +{ + return (TYPE_is_object(P1->type) && P1->_object.object && OBJECT_class(P1->_object.object)->has_operators + && CLASS_has_operator(OBJECT_class(P1->_object.object), op)); +} + +int EXEC_check_operator(VALUE *P1, VALUE *P2, uchar op) +{ + CLASS *class1, *class2; + + if (TYPE_is_number(P1->type) && TYPE_is_object(P2->type)) + { + if (P2->_object.object && OBJECT_class(P2->_object.object)->has_operators && CLASS_has_operator(OBJECT_class(P2->_object.object), op + 1)) + { + //*dynamic = P2->type == T_OBJECT; + return OP_FLOAT_OBJECT; + } + } + else if (TYPE_is_number(P2->type) && TYPE_is_object(P1->type)) + { + if (P1->_object.object && OBJECT_class(P1->_object.object)->has_operators && CLASS_has_operator(OBJECT_class(P1->_object.object), op + 1)) + { + //*dynamic = P1->type == T_OBJECT; + return OP_OBJECT_FLOAT; + } + } + else if (TYPE_are_objects(P1->type, P2->type) && OBJECT_are_not_null(P1->_object.object, P2->_object.object)) + { + class1 = OBJECT_class(P1->_object.object); + class2 = OBJECT_class(P2->_object.object); + + //*dynamic = P1->type == T_OBJECT || P2->type = T_OBJECT; + + if (class1->has_operators) + { + if (class1 == class2 && CLASS_has_operator(class1, op)) + return OP_OBJECT_OBJECT; + + if (class2->has_operators) + { + if (CLASS_get_operator_strength(class1) > CLASS_get_operator_strength(class2) && CLASS_has_operator(class1, op + 2)) + return OP_OBJECT_OTHER; + else if (CLASS_has_operator(class2, op + 2)) + return OP_OTHER_OBJECT; + } + else if (CLASS_has_operator(class1, op)) + return OP_OBJECT_OTHER; + } + else if (class2->has_operators && CLASS_has_operator(class2, op + 2)) + return OP_OTHER_OBJECT; + } + + return OP_NOTHING; +} + +void EXEC_operator(uchar what, uchar op, VALUE *P1, VALUE *P2) +{ + static void *jump[] = { NULL, &&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_OTHER, &&__OTHER_OBJECT, &&__OBJECT_OBJECT }; + + void *func; + void *result; + bool invert; + void *o1, *o2; + + goto *jump[what]; + +__OBJECT_FLOAT: + + o1 = P1->_object.object; + if (!o1) + THROW_NULL(); + + func = OBJECT_class(o1)->operators[op]; + VALUE_conv_float(P2); + result = (*(FUNC_O_OF)func)(o1, P2->_float.value, FALSE); + OBJECT_REF(result); + OBJECT_UNREF(o1); + + if (!result) + { + if (op != CO_DIVF) + raise_error(o1, NULL); + } + + goto __END; + +__FLOAT_OBJECT: + + o1 = P2->_object.object; + if (!o1) + THROW_NULL(); + + func = OBJECT_class(o1)->operators[op]; + VALUE_conv_float(P1); + result = (*(FUNC_O_OF)func)(o1, P1->_float.value, TRUE); + OBJECT_REF(result); + P1->_object.class = P2->_object.class; + OBJECT_UNREF(o1); + + if (!result && !EXEC_has_native_error()) + raise_error(o1, NULL); + + goto __END; + +__OTHER_OBJECT: + + o2 = P1->_object.object; + o1 = P2->_object.object; + + invert = TRUE; + goto __OTHER; + +__OBJECT_OTHER: +__OBJECT_OBJECT: + + o1 = P1->_object.object; + o2 = P2->_object.object; + + invert = FALSE; + goto __OTHER; + +__OTHER: + + if (!OBJECT_are_not_null(o1, o2)) + THROW_NULL(); + + func = OBJECT_class(o1)->operators[op]; + result = (*(FUNC_O_OO)func)(o1, o2, invert); + OBJECT_REF(result); + OBJECT_UNREF(o1); + OBJECT_UNREF(o2); + + if (!result && !EXEC_has_native_error()) + raise_error(o1, o2); + +__END: + + P1->_object.object = result; + + if (EXEC_has_native_error()) + { + EXEC_set_native_error(FALSE); + PROPAGATE(); + } +} + +void EXEC_operator_object_add_quick(VALUE *P1, double val) +{ + if (P1->_object.object) + { + void *func = OBJECT_class(P1->_object.object)->operators[CO_ADDF]; + void *result = (*(FUNC_O_OF)func)(P1->_object.object, val, FALSE); + OBJECT_REF(result); + OBJECT_UNREF(P1->_object.object); + P1->_object.object = result; + } + else + THROW_NULL(); + + if (EXEC_has_native_error()) + { + EXEC_set_native_error(FALSE); + PROPAGATE(); + } +} + + +int EXEC_comparator(uchar what, uchar op, VALUE *P1, VALUE *P2) +{ + static void *jump[] = { NULL, &&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_OTHER, &&__OTHER_OBJECT, &&__OBJECT_OBJECT }; + + void *func; + int result; + bool invert; + void *o1, *o2; + + goto *jump[what]; + +__OBJECT_FLOAT: + + o1 = P1->_object.object; + if (!o1) + THROW_NULL(); + + func = OBJECT_class(o1)->operators[op]; + VALUE_conv_float(P2); + result = (*(FUNC_I_OF)func)(o1, P2->_float.value, FALSE); + OBJECT_UNREF(o1); + + if (result < (-1)) + raise_error(o1, NULL); + + goto __END; + +__FLOAT_OBJECT: + + o2 = P2->_object.object; + if (!o2) + THROW_NULL(); + + func = OBJECT_class(o2)->operators[op]; + VALUE_conv_float(P1); + result = (*(FUNC_I_OF)func)(o2, P1->_float.value, TRUE); + OBJECT_UNREF(o2); + + if (result < (-1)) + raise_error(o2, NULL); + + goto __END; + +__OTHER_OBJECT: + + o2 = P1->_object.object; + o1 = P2->_object.object; + invert = TRUE; + goto __OTHER; + +__OBJECT_OTHER: +__OBJECT_OBJECT: + + o1 = P1->_object.object; + o2 = P2->_object.object; + invert = FALSE; + goto __OTHER; + +__OTHER: + + if (!OBJECT_are_not_null(o1, o2)) + THROW_NULL(); + + func = OBJECT_class(o1)->operators[op]; + result = (*(FUNC_I_OO)func)(o1, o2, invert); + OBJECT_UNREF(o1); + OBJECT_UNREF(o2); + //result = !!result; // result != 0; + + if (result < (-1)) + raise_error(o1, o2); + +__END: + + if (EXEC_has_native_error()) + { + EXEC_set_native_error(FALSE); + PROPAGATE(); + } + + return result; +} + +void EXEC_operator_object_sgn(VALUE *P1) +{ + if (P1->_object.object) + { + void *func = OBJECT_class(P1->_object.object)->operators[CO_SGN]; + int result = (*(FUNC_I_O)func)(P1->_object.object); + OBJECT_UNREF(P1->_object.object); + P1->type = T_INTEGER; + P1->_integer.value = result; + } + else + THROW_NULL(); + + if (EXEC_has_native_error()) + { + EXEC_set_native_error(FALSE); + PROPAGATE(); + } +} + +void EXEC_operator_object_fabs(VALUE *P1) +{ + if (P1->_object.object) + { + void *func = OBJECT_class(P1->_object.object)->operators[CO_FABS]; + double result = (*(FUNC_F_O)func)(P1->_object.object); + OBJECT_UNREF(P1->_object.object); + P1->type = T_FLOAT; + P1->_float.value = result; + } + else + THROW_NULL(); + + if (EXEC_has_native_error()) + { + EXEC_set_native_error(FALSE); + PROPAGATE(); + } +} + +void EXEC_operator_object_single(uchar op, VALUE *P1) +{ + if (P1->_object.object) + { + void *func = OBJECT_class(P1->_object.object)->operators[op]; + void *result = (*(FUNC_O_O)func)(P1->_object.object); + OBJECT_REF(result); + OBJECT_UNREF(P1->_object.object); + P1->_object.object = result; + } + else + THROW_NULL(); + + if (EXEC_has_native_error()) + { + EXEC_set_native_error(FALSE); + PROPAGATE(); + } +} diff --git a/main/gbx/gbx_exec_pop.c b/main/gbx/gbx_exec_pop.c new file mode 100644 index 00000000..2191f915 --- /dev/null +++ b/main/gbx/gbx_exec_pop.c @@ -0,0 +1,319 @@ +/*************************************************************************** + + gbx_exec_pop.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" +#include "gb_limit.h" +#include "gbx_exec.h" + +#include "gbx_string.h" +#include "gbx_object.h" +#include "gbx_c_array.h" +#include "gbx_c_collection.h" +#include "gbx_api.h" +#include "gbx_struct.h" + + +void EXEC_pop_unknown(void) +{ + static void *jump[] = { + /* 0 */ &&_POP_GENERIC, + /* 1 */ &&_POP_VARIABLE, + /* 2 */ &&_POP_STATIC_VARIABLE, + /* 3 */ &&_POP_PROPERTY, + /* 4 */ &&_POP_VARIABLE_AUTO, + /* 5 */ &&_POP_PROPERTY_AUTO, + /* 6 */ &&_POP_STRUCT_FIELD + }; + + const char *name; + int index; + CLASS_DESC *desc; + CLASS *class; + OBJECT *object; + char *addr; + bool defined; + VALUE *val; + + defined = EXEC_object(&SP[-1], &class, &object); + + /*printf("> exec_pop_unknown: SP = %p -> %p\n", SP, SP->_string.addr);*/ + + goto *jump[*PC & 0xF]; + + +_POP_GENERIC: + + name = CP->load->unknown[PC[1]]; + + // The first time we access a symbol, we must not be virtual to find it + val = &SP[-1]; + if (defined && object && !VALUE_is_super(val)) + index = CLASS_find_symbol(val->_object.class, name); + else + index = CLASS_find_symbol(class, name); + + if (index == NO_SYMBOL) + { + if (class->special[SPEC_UNKNOWN] == NO_SYMBOL) + { + if (defined && object && !VALUE_is_super(val)) + class = val->_object.class; + THROW(E_NSYMBOL, CLASS_get_name(class), name); + } + goto _POP_UNKNOWN_PROPERTY; + } + + desc = class->table[index].desc; + + switch (CLASS_DESC_get_type(desc)) + { + case CD_CONSTANT: + + THROW(E_NPROPERTY, CLASS_get_name(class), name); + + case CD_VARIABLE: + + if (object == NULL) + { + if (!class->auto_create) + THROW(E_DYNAMIC, CLASS_get_name(class), name); + object = EXEC_auto_create(class, TRUE); + *PC |= 4; + } + else + { + if (defined) *PC |= 1; + } + + if (defined) + PC[1] = index; + + goto _POP_VARIABLE_2; + + case CD_STRUCT_FIELD: + + if (object == NULL) + THROW(E_DYNAMIC, CLASS_get_name(class), name); + + if (defined) + { + *PC |= 6; + PC[1] = index; + } + + if (desc->variable.ctype.id == TC_STRUCT || desc->variable.ctype.id == TC_ARRAY) + THROW(E_NWRITE, CLASS_get_name(class), name); + + goto _POP_STRUCT_FIELD_2; + + case CD_STATIC_VARIABLE: + + if (object) + THROW(E_STATIC, CLASS_get_name(class), name); + + if (defined) *PC |= 2; + + if (defined) + PC[1] = index; + + goto _POP_STATIC_VARIABLE_2; + + case CD_PROPERTY: + + if (object == NULL) + { + if (!class->auto_create) + THROW(E_DYNAMIC, CLASS_get_name(class), name); + object = EXEC_auto_create(class, TRUE); + *PC |= 5; + } + else + { + if (defined) *PC |= 3; + } + + if (defined) + PC[1] = index; + + goto _POP_PROPERTY_2; + + case CD_STATIC_PROPERTY: + + if (object) + THROW(E_STATIC, CLASS_get_name(class), name); + + if (defined) *PC |= 3; + + if (defined) + PC[1] = index; + + goto _POP_PROPERTY_2; + + case CD_PROPERTY_READ: + case CD_STATIC_PROPERTY_READ: + + THROW(E_NWRITE, CLASS_get_name(class), name); + + case CD_METHOD: + case CD_STATIC_METHOD: + + THROW(E_NPROPERTY, CLASS_get_name(class), name); + + default: + + THROW(E_NSYMBOL, CLASS_get_name(class), name); + } + + +_POP_VARIABLE_AUTO: + + object = EXEC_auto_create(class, TRUE); + +_POP_VARIABLE: + + desc = class->table[PC[1]].desc; + +_POP_VARIABLE_2: + + addr = (char *)object + desc->variable.offset; + VALUE_write(&SP[-2], (void *)addr, desc->variable.type); + goto _FIN; + + +_POP_STATIC_VARIABLE: + + desc = class->table[PC[1]].desc; + +_POP_STATIC_VARIABLE_2: + + addr = (char *)desc->variable.class->stat + desc->variable.offset; + VALUE_write(&SP[-2], (void *)addr, desc->variable.type); + goto _FIN; + + +_POP_STRUCT_FIELD: + + desc = class->table[PC[1]].desc; + +_POP_STRUCT_FIELD_2: + + if (((CSTRUCT *)object)->ref) + addr = (char *)((CSTATICSTRUCT *)object)->addr + desc->variable.offset; + else + addr = (char *)object + sizeof(CSTRUCT) + desc->variable.offset; + + VALUE_write(&SP[-2], (void *)addr, desc->variable.type); + goto _FIN; + + +_POP_PROPERTY_AUTO: + + object = EXEC_auto_create(class, TRUE); + +_POP_PROPERTY: + + desc = class->table[PC[1]].desc; + +_POP_PROPERTY_2: + + VALUE_conv(&SP[-2], desc->property.type); + + if (desc->property.native) + { + if (EXEC_call_native(desc->property.write, object, 0, &SP[-2])) + PROPAGATE(); + } + else + { + *SP = SP[-2]; + PUSH(); + + EXEC.class = desc->property.class; + EXEC.object = object; + EXEC.nparam = 1; + EXEC.native = FALSE; + EXEC.index = (int)(intptr_t)desc->property.write; + + EXEC_function(); + } + + goto _FIN; + +_POP_UNKNOWN_PROPERTY: + + if (class->special[SPEC_PROPERTY] == NO_SYMBOL) + goto _NOT_A_PROPERTY; + + EXEC_unknown_name = name; + if (EXEC_special(SPEC_PROPERTY, class, class->property_static ? NULL : object, 0, FALSE)) + goto _NOT_A_PROPERTY; + + VALUE_conv_boolean(&SP[-1]); + SP--; + if (!SP->_boolean.value) + goto _NOT_A_PROPERTY; + + EXEC_unknown_name = name; + + *SP = SP[-2]; + PUSH(); + + EXEC_special(SPEC_UNKNOWN, class, class->unknown_static ? NULL : object, 1, TRUE); + goto _FIN; + +_NOT_A_PROPERTY: + + THROW(E_NPROPERTY, CLASS_get_name(class), name); + +_FIN: + + RELEASE(&SP[-2]); + OBJECT_UNREF(object); + SP -= 2; + PC++; +} + +#if 0 +void EXEC_pop_array(ushort code) +{ + CLASS *class; + OBJECT *object; + GET_NPARAM(np); + VALUE *val; + VALUE swap; + + val = &SP[-np]; + + EXEC_object(val, &class, &object); + + /* swap object and value to be inserted */ + VALUE_copy(&swap, &val[0]); + VALUE_copy(&val[0], &val[-1]); + VALUE_copy(&val[-1], &swap); + + if (EXEC_special(SPEC_PUT, class, object, np, TRUE)) + THROW(E_NARRAY, CLASS_get_name(class)); + + POP(); /* free the object */ +} +#endif diff --git a/main/gbx/gbx_exec_push.c b/main/gbx/gbx_exec_push.c new file mode 100644 index 00000000..b26cb589 --- /dev/null +++ b/main/gbx/gbx_exec_push.c @@ -0,0 +1,743 @@ +/*************************************************************************** + + gbx_exec_push.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" +#include "gb_limit.h" +#include "gbx_exec.h" +#include "gb_pcode.h" +#include "gbx_api.h" + +#include "gbx_string.h" +#include "gbx_c_array.h" +#include "gbx_c_collection.h" +#include "gbx_api.h" +#include "gbx_struct.h" +#include "gbx_local.h" + +void EXEC_push_unknown(void) +{ + static void *jump[] = { + &&_PUSH_GENERIC, // 0 + &&_PUSH_CONSTANT, // 1 + &&_PUSH_VARIABLE, // 2 + &&_PUSH_STATIC_VARIABLE, // 3 + &&_PUSH_PROPERTY, // 4 + &&_PUSH_METHOD, // 5 + &&_PUSH_STATIC_METHOD, // 6 + &&_PUSH_VARIABLE_AUTO, // 7 + &&_PUSH_PROPERTY_AUTO, // 8 + &&_PUSH_METHOD_AUTO, // 9 + &&_PUSH_EXTERN, // 10 + &&_PUSH_STRUCT_FIELD, // 11 + &&_PUSH_CONST_STRING, // 12 + }; + + const char *name; + int index; + CLASS_DESC *desc; + CLASS *class; + OBJECT *object; + void *ref; + char *addr; + bool defined; + VALUE *val; + + // EXEC_object can change *PC by calling EXEC_push_unknown() recursively + // So don't store *PC anywhere + + defined = EXEC_object(&SP[-1], &class, &object); + + goto *jump[*PC & 0xF]; + + +_PUSH_GENERIC: + + name = CP->load->unknown[PC[1]]; + + // The first time we access a symbol, we must not be virtual to find it + val = &SP[-1]; + if (defined && object && !VALUE_is_super(val)) + index = CLASS_find_symbol(val->_object.class, name); + else + index = CLASS_find_symbol(class, name); + + if (index == NO_SYMBOL) + { + //index = CLASS_find_symbol(class, name); + + if (class->special[SPEC_UNKNOWN] == NO_SYMBOL) + { + if (defined && object && !VALUE_is_super(val)) + class = val->_object.class; + THROW(E_NSYMBOL, CLASS_get_name(class), name); + } + + if (class->unknown_static && object) + THROW(E_STATIC, CLASS_get_name(class), name); + else if (!class->unknown_static && object == NULL) + { + if (!class->auto_create) + THROW(E_DYNAMIC, CLASS_get_name(class), name); + object = EXEC_auto_create(class, TRUE); + } + + index = CLASS_find_symbol(class, name); + if (index != NO_SYMBOL) + THROW(E_NSYMBOL, CLASS_get_name(val->_object.class), name); + + if (class->special[SPEC_PROPERTY] != NO_SYMBOL) + { + EXEC_unknown_name = name; + if (!EXEC_special(SPEC_PROPERTY, class, class->property_static ? NULL : object, 0, FALSE)) + { + VALUE_conv_boolean(&SP[-1]); + SP--; + if (SP->_boolean.value) + { + //SP--; Keep the object on the stack so that it is automatically freed if an error is raised + EXEC_special(SPEC_UNKNOWN, class, class->unknown_static ? NULL : object, 0, FALSE); + VALUE_conv_variant(&SP[-1]); + SP--; + SP[-1] = *SP; + OBJECT_UNREF(object); + goto _FIN; + } + } + } + + goto _PUSH_UNKNOWN_METHOD; + } + + desc = class->table[index].desc; + + switch (CLASS_DESC_get_type(desc)) + { + case CD_CONSTANT: + + if (TYPE_is_string(desc->constant.type)) + { + if (defined) + { + *PC |= 12; + PC[1] = index; + } + + goto _PUSH_CONST_STRING_2; + } + else + { + if (defined) + { + if ((PC[-1] & 0xF800) == C_PUSH_CLASS) + { + if (desc->constant.type == T_BOOLEAN) + { + PC[-1] = C_PUSH_MISC | (desc->constant.value._integer ? CPM_TRUE : CPM_FALSE); + PC[0] = C_NOP; + PC[1] = C_NOP; + goto _PUSH_CONSTANT_2; + } + else if (desc->constant.type == T_BYTE || desc->constant.type == T_SHORT) + { + PC[-1] = C_PUSH_INTEGER; + PC[0] = desc->constant.value._integer; + PC[1] = (CODE_CONV << 8) | desc->constant.type; + goto _PUSH_CONSTANT_2; + } + else if (desc->constant.type == T_INTEGER) + { + PC[-1] = C_PUSH_LONG; + PC[0] = desc->constant.value._integer & 0xFFFF; + PC[1] = desc->constant.value._integer >> 16; + goto _PUSH_CONSTANT_2; + } + } + + *PC |= 1; + + PC[1] = index; + } + + goto _PUSH_CONSTANT_2; + } + + case CD_VARIABLE: + + if (object == NULL) + { + if (!class->auto_create) + THROW(E_DYNAMIC, CLASS_get_name(class), name); + object = EXEC_auto_create(class, TRUE); + *PC |= 7; + } + else + { + if (defined) *PC |= 2; + } + + if (defined) + PC[1] = index; + + goto _PUSH_VARIABLE_2; + + case CD_STRUCT_FIELD: + + if (object == NULL) + THROW(E_DYNAMIC, CLASS_get_name(class), name); + + if (defined) + { + *PC |= 11; + PC[1] = index; + } + + goto _PUSH_STRUCT_FIELD_2; + + case CD_STATIC_VARIABLE: + + if (object) + THROW(E_STATIC, CLASS_get_name(class), name); + + if (defined) *PC |= 3; + + if (defined) + PC[1] = index; + + goto _PUSH_STATIC_VARIABLE_2; + + case CD_PROPERTY: + case CD_PROPERTY_READ: + + if (object == NULL) + { + if (!class->auto_create) + THROW(E_DYNAMIC, CLASS_get_name(class), name); + object = EXEC_auto_create(class, TRUE); + *PC |= 8; + } + else + { + if (defined) *PC |= 4; + } + + if (defined) + PC[1] = index; + + goto _PUSH_PROPERTY_2; + + case CD_STATIC_PROPERTY: + case CD_STATIC_PROPERTY_READ: + + if (object) + THROW(E_STATIC, CLASS_get_name(class), name); + + if (defined) *PC |= 4; + + if (defined) + PC[1] = index; + + goto _PUSH_PROPERTY_2; + + case CD_METHOD: + + if (object == NULL) + { + if (!class->auto_create) + THROW(E_DYNAMIC, CLASS_get_name(class), name); + object = EXEC_auto_create(class, TRUE); + *PC |= 9; + } + else + { + if (defined) *PC |= 5; + } + + if (defined) + PC[1] = index; + + goto _PUSH_METHOD_2; + + case CD_STATIC_METHOD: + + if (object) + { + OBJECT_UNREF(object); + object = NULL; + } + + if (defined) *PC |= 6; + + if (defined) + PC[1] = index; + + goto _PUSH_METHOD_2; + + case CD_EXTERN: + + if (object) + { + OBJECT_UNREF(object); + object = NULL; + } + + if (defined) *PC |= 10; + + if (defined) + PC[1] = index; + + goto _PUSH_EXTERN_2; + + default: + + THROW(E_NSYMBOL, CLASS_get_name(class), name); + } + + +_PUSH_CONSTANT: + + desc = class->table[PC[1]].desc; + +_PUSH_CONSTANT_2: + + VALUE_read(&SP[-1], (void *)&desc->constant.value, desc->constant.type); + goto _FIN_DEFINED; + + +_PUSH_CONST_STRING: + + desc = class->table[PC[1]].desc; + +_PUSH_CONST_STRING_2: + + SP--; + SP->type = T_CSTRING; + + if (!desc->constant.value._string) + { + SP->_string.addr = NULL; + SP->_string.len = 0; + } + else + { + if (desc->constant.translate) + SP->_string.addr = (char *)LOCAL_gettext(desc->constant.value._string); + else + SP->_string.addr = (char *)desc->constant.value._string; + + SP->_string.len = strlen(SP->_string.addr); + } + + SP->_string.start = 0; + SP++; + goto _FIN_DEFINED_NO_BORROW; + + +_PUSH_VARIABLE_AUTO: + + object = EXEC_auto_create(class, TRUE); + +_PUSH_VARIABLE: + + desc = class->table[PC[1]].desc; + +_PUSH_VARIABLE_2: + + addr = (char *)object + desc->variable.offset; + ref = object; + goto _READ_VARIABLE; + + +_PUSH_STATIC_VARIABLE: + + desc = class->table[PC[1]].desc; + +_PUSH_STATIC_VARIABLE_2: + + addr = (char *)desc->variable.class->stat + desc->variable.offset; + ref = desc->variable.class; + goto _READ_VARIABLE; + + +_PUSH_STRUCT_FIELD: + + desc = class->table[PC[1]].desc; + +_PUSH_STRUCT_FIELD_2: + + if (((CSTRUCT *)object)->ref) + addr = (char *)((CSTATICSTRUCT *)object)->addr + desc->variable.offset; + else + addr = (char *)object + sizeof(CSTRUCT) + desc->variable.offset; + + ref = object; + goto _READ_VARIABLE; + + +_READ_VARIABLE: + + VALUE_class_read(desc->variable.class, &SP[-1], (void *)addr, desc->variable.ctype, ref); + goto _FIN_DEFINED; + + +_PUSH_PROPERTY_AUTO: + + object = EXEC_auto_create(class, TRUE); + +_PUSH_PROPERTY: + + desc = class->table[PC[1]].desc; + +_PUSH_PROPERTY_2: + + if (desc->property.native) + { + if (EXEC_call_native(desc->property.read, object, desc->property.type, NULL)) + PROPAGATE(); + + //SP[-1] = TEMP; + VALUE_copy(&SP[-1], &TEMP); + goto _FIN_DEFINED; + } + else + { + EXEC.class = desc->property.class; + EXEC.object = object; + EXEC.nparam = 0; + EXEC.native = FALSE; + EXEC.index = (int)(intptr_t)desc->property.read; + + EXEC_function_keep(); + + //SP[-1] = *RP; + VALUE_copy(&SP[-1], RP); + RP->type = T_VOID; + goto _FIN_DEFINED_NO_BORROW; + } + + +_PUSH_STATIC_METHOD: + + if (object) + { + OBJECT_UNREF(object); + object = NULL; + } + + goto _PUSH_METHOD; + +_PUSH_METHOD_AUTO: + + object = EXEC_auto_create(class, TRUE); + +_PUSH_METHOD: + + index = PC[1]; + desc = class->table[index].desc; + +_PUSH_METHOD_2: + + //printf("PUSH_METHOD: %d %s\n", index, desc->method.name); + + SP--; + SP->type = T_FUNCTION; + SP->_function.class = class; + SP->_function.object = object; + /*SP->_function.function = (int)&desc->method;*/ + + if (FUNCTION_is_native(&desc->method)) + { + if (desc->method.subr) + SP->_function.kind = FUNCTION_SUBR; + else + SP->_function.kind = FUNCTION_NATIVE; + } + else + SP->_function.kind = FUNCTION_PUBLIC; + + SP->_function.index = index; + + SP->_function.defined = defined; + + SP++; + + goto _FIN; + + +_PUSH_EXTERN: + + if (object) + { + OBJECT_UNREF(object); + object = NULL; + } + + index = PC[1]; + desc = class->table[index].desc; + +_PUSH_EXTERN_2: + + //printf("PUSH_METHOD: %d %s\n", index, desc->method.name); + + SP--; + SP->type = T_FUNCTION; + SP->_function.class = class; + SP->_function.object = object; + SP->_function.kind = FUNCTION_EXTERN; + SP->_function.index = (int)desc->ext.exec; + SP->_function.defined = defined; + SP++; + + goto _FIN; + +_PUSH_UNKNOWN_METHOD: + + SP--; + SP->type = T_FUNCTION; + SP->_function.class = class; + SP->_function.object = object; + SP->_function.kind = FUNCTION_UNKNOWN; + SP->_function.index = PC[1]; + SP->_function.defined = FALSE; + SP++; + + //OBJECT_REF(&object); + + goto _FIN; + +_FIN_DEFINED: + + BORROW(&SP[-1]); + +_FIN_DEFINED_NO_BORROW: + + if (!defined) + VALUE_conv_variant(&SP[-1]); + + // SP[-1] was the object and it has been erased. So we must unref it manually, + // unless we are calling a static method (see above) + OBJECT_UNREF(object); + +_FIN: + + PC++; +} + +#if 0 +void EXEC_push_array(ushort code) +{ + static const void *jump[] = { &&__PUSH_GENERIC, &&__PUSH_QUICK_ARRAY, &&__PUSH_QUICK_COLLECTION, &&__PUSH_ARRAY }; + + CLASS *class; + OBJECT *object; + GET_NPARAM(np); + //int dim[MAX_ARRAY_DIM]; + int i; + void *data; + bool defined; + VALUE *val; + int fast; + //ARRAY_DESC *desc; + + val = &SP[-np]; + np--; + + goto *jump[((unsigned char)code) >> 6]; + +__PUSH_GENERIC: + + defined = EXEC_object(val, &class, &object); + + fast = 3; + + if (defined) + { + if (class->quick_array == CQA_ARRAY) + fast = 1; + else if (class->quick_array == CQA_COLLECTION) + fast = 2; + else + { + // Check the symbol existance, but *not virtually* + if (object && !VALUE_is_super(val)) + { + CLASS *nvclass = val->_object.class; + + if (nvclass->special[SPEC_GET] == NO_SYMBOL) + THROW(E_NARRAY, CLASS_get_name(nvclass)); + } + } + } + + *PC |= fast << 6; + + goto __PUSH_ARRAY_2; + +/*__PUSH_STATIC_ARRAY: + + for (i = 1; i <= np; i++) + { + VALUE_conv_integer(&val[i]; + dim[i - 1] = val[i]._integer.value; + } + + SP = val; + + desc = (ARRAY_DESC *)SP->_array.class->load->array[SP->_array.index]; + data = ARRAY_get_address(desc, SP->_array.addr, np, dim); + + VALUE_read(SP, data, CLASS_ctype_to_type(SP->_array.class, desc->type)); + + PUSH(); + return;*/ + +__PUSH_QUICK_ARRAY: + + // Optimization test + + //EXEC_object_fast(val, &class, &object); + EXEC_object_array(val, class, object); + + VALUE_conv_integer(&val[1]); + + if (np == 1) + { + data = CARRAY_get_data((CARRAY *)object, val[1]._integer.value); + } + else + { + for (i = 2; i <= np; i++) + VALUE_conv_integer(&val[i]); + + data = CARRAY_get_data_multi((CARRAY *)object, (GB_INTEGER *)&val[1], np); + } + + if (!data) + PROPAGATE(); + + //VALUE_read(val, data, ((CARRAY *)object)->type); + if (TYPE_is_object(((CARRAY *)object)->type)) + fast = T_OBJECT; + else + fast = ((CARRAY *)object)->type; + + VALUE_read_inline_type(val, data, fast); + + goto __PUSH_QUICK_END; + +__PUSH_QUICK_COLLECTION: + + EXEC_object_fast(val, &class, &object); + + VALUE_conv_string(&val[1]); + //fprintf(stderr, "GB_CollectionGet: %p '%.*s'\n", val[1]._string.addr, val[1]._string.len, val[1]._string.addr + val[1]._string.start); + GB_CollectionGet((GB_COLLECTION)object, val[1]._string.addr + val[1]._string.start, val[1]._string.len, (GB_VARIANT *)val); + + RELEASE_STRING(&val[1]); + +__PUSH_QUICK_END: + + SP = val; + PUSH(); + OBJECT_UNREF(object); + return; + +__PUSH_ARRAY: + + defined = EXEC_object(val, &class, &object); + +__PUSH_ARRAY_2: + + if (EXEC_special(SPEC_GET, class, object, np, FALSE)) + THROW(E_NARRAY, CLASS_get_name(class)); + + OBJECT_UNREF(object); + SP--; + //SP[-1] = SP[0]; + VALUE_copy(&SP[-1], &SP[0]); + + if (!defined) + VALUE_conv_variant(&SP[-1]); +} +#endif + +#if 0 +// JIT needs it +void EXEC_push_array(ushort code) +{ + CLASS *class; + OBJECT *object; + GET_NPARAM(np); + bool defined; + VALUE *val; + + val = &SP[-np]; + np--; + defined = EXEC_object(val, &class, &object); + + if (EXEC_special(SPEC_GET, class, object, np, FALSE)) + THROW(E_NARRAY, CLASS_get_name(class)); + + OBJECT_UNREF(object); + SP--; + //SP[-1] = SP[0]; + VALUE_copy(&SP[-1], &SP[0]); + + if (!defined) + VALUE_conv_variant(&SP[-1]); +} +#endif + +int EXEC_push_unknown_event(bool unknown) +{ + int index; + CLASS_DESC *desc; + const char *name; + + if (unknown) + { + name = CP->load->unknown[PC[1]]; + // The ':' is already in the name, thanks to the compiler. + index = CLASS_find_symbol(CP, name); + if (index == NO_SYMBOL) + THROW(E_DYNAMIC, CLASS_get_name(CP), name); + + desc = CP->table[index].desc; + if (CLASS_DESC_get_type(desc) != CD_EVENT) + THROW(E_DYNAMIC, CLASS_get_name(CP), name); + + PC[1] = index; + PC[0] &= ~1; + } + else + { + desc = CP->table[PC[1]].desc; + } + + index = desc->event.index; + + //if (desc->event.class->parent) + // index += desc->event.class->parent->n_event; + + return index; +} + diff --git a/main/gbx/gbx_expression.h b/main/gbx/gbx_expression.h new file mode 100644 index 00000000..e7420c61 --- /dev/null +++ b/main/gbx/gbx_expression.h @@ -0,0 +1,75 @@ +/*************************************************************************** + + gbx_expression.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_EXPRESSION_H +#define __GBX_EXPRESSION_H + +#include "gb_table.h" +#include "gbx_class.h" + +typedef + struct { + void *parent; + char *source; + int len; + PATTERN *pattern; + int pattern_count; + PATTERN *current; + PATTERN *tree; + CLASS exec_class; + CLASS_LOAD class_load; + FUNCTION func; + CLASS_CONST *cst; + ushort *code; + ushort ncode; + ushort ncode_max; + TABLE *table; + TABLE *string; + /*TABLE *variable;*/ + CLASS **class; + char **unknown; + int *var; + short nvar; + short last_code; + short last_code2; + short assign_code; + int stack_usage; + void *op; + char *error; + unsigned analyze : 1; + unsigned rewrite : 1; + unsigned comment : 1; + unsigned custom : 1; + //unsigned _reserved : 12; + } + //PACKED + EXPRESSION; + +typedef + struct { + SYMBOL sym; + int local; + } + EVAL_SYMBOL; + +#endif diff --git a/main/gbx/gbx_extern.c b/main/gbx/gbx_extern.c new file mode 100644 index 00000000..5776c46d --- /dev/null +++ b/main/gbx/gbx_extern.c @@ -0,0 +1,788 @@ +/*************************************************************************** + + gbx_extern.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_EXTERN_C + +#include "config.h" +#include "gb_common.h" + +#ifdef HAVE_FFI_COMPONENT + +#include + +#include "gb_common_buffer.h" +#include "gb_table.h" +#include "gb_hash.h" +#include "gbx_type.h" +#include "gbx_value.h" +#include "gbx_class_desc.h" +#include "gbx_class.h" +#include "gbx_exec.h" +#include "gbx_api.h" +#include "gbx_c_array.h" +#include "gbx_struct.h" +#include "gbx_extern.h" + +typedef + struct { + SYMBOL sym; + lt_dlhandle handle; + } + EXTERN_SYMBOL; + +typedef + struct { + ffi_cif cif; + ffi_type **types; + ffi_type *rtype; + } + EXTERN_CIF; + +typedef + struct EXTERN_CALLBACK { + struct EXTERN_CALLBACK *next; + EXEC_GLOBAL exec; + void *closure; + void *code; + int nparam; + TYPE *sign; + TYPE ret; + EXTERN_CIF info; + } + EXTERN_CALLBACK; + +typedef + struct EXTERN_FUNC { + struct EXTERN_FUNC *next; + char *alias; + void *call; + EXTERN_CIF info; + } + EXTERN_FUNC; + +static TABLE *_table = NULL; +//static EXTERN_CALLBACK *_callbacks = NULL; +static HASH_TABLE *_callbacks = NULL; +static EXTERN_FUNC *_functions = NULL; + +static ffi_type *_to_ffi_type[17] = { + &ffi_type_void, &ffi_type_sint32, &ffi_type_sint32, &ffi_type_sint32, + &ffi_type_sint32, &ffi_type_sint64, &ffi_type_float, &ffi_type_double, + &ffi_type_void, &ffi_type_pointer, &ffi_type_pointer, &ffi_type_pointer, + &ffi_type_void, &ffi_type_void, &ffi_type_void, &ffi_type_pointer, + &ffi_type_pointer + }; + + +static void prepare_cif(EXTERN_CIF *info, int nsign, TYPE *sign, TYPE ret, int nparam, VALUE *value) +{ + int i; + TYPE t; + + info->types = NULL; + + if (nparam > 0) + { + ALLOC(&info->types, sizeof(ffi_type *) * nparam); + + for (i = 0; i < nparam; i++) + { + if (i < nsign) + t = sign[i]; + else + t = value[i].type; + + if (TYPE_is_object(t)) + t = T_OBJECT; + + info->types[i] = _to_ffi_type[t]; + } + } + + if (TYPE_is_object(ret)) + t = T_OBJECT; + else + t = ret; + + info->rtype = _to_ffi_type[t]; + + if (ffi_prep_cif(&info->cif, FFI_DEFAULT_ABI, nparam, info->rtype, info->types) != FFI_OK) + THROW(E_EXTCB, "Unable to prepare function description"); +} + + +static lt_dlhandle get_library(const char *name) +{ + EXTERN_SYMBOL *esym; + char *p; + int index; + + if (!_table) + TABLE_create(&_table, sizeof(EXTERN_SYMBOL), TF_NORMAL); + + TABLE_add_symbol(_table, name, strlen(name), &index); + esym = (EXTERN_SYMBOL *)TABLE_get_symbol(_table, index); + if (!esym->handle) + { + /* !!! Must add the suffix !!! */ + + p = strrchr(name, ':'); + if (!p) + sprintf(COMMON_buffer, "%s." SHARED_LIBRARY_EXT, name); + else + sprintf(COMMON_buffer, "%.*s." SHARED_LIBRARY_EXT ".%s", (int)(p - name), name, p + 1); + + name = COMMON_buffer; + + #ifndef DONT_USE_LTDL + /* no more available in libltld ? + lt_dlopen_flag = RTLD_LAZY; + */ + esym->handle = lt_dlopenext(name); + #else + esym->handle = dlopen(name, RTLD_LAZY); + #endif + + if (esym->handle == NULL) + { + name = GB_RealFileName(name, strlen(name)); + #ifndef DONT_USE_LTDL + esym->handle = lt_dlopenext(name); + #else + esym->handle = dlopen(name, RTLD_LAZY); + #endif + } + + if (esym->handle == NULL) + THROW(E_EXTLIB, name, lt_dlerror()); + + //fprintf(stderr, "%s loaded.\n", name); + } + + return esym->handle; +} + +void *EXTERN_get_symbol(const char *library, const char *symbol) +{ + lt_dlhandle handle = get_library(library); + return lt_dlsym(handle, symbol); +} + +static EXTERN_FUNC *get_function(CLASS_EXTERN *ext) +{ + EXTERN_FUNC *func; + void *call; + lt_dlhandle handle; + + if (ext->loaded) + return (EXTERN_FUNC *)ext->alias; + + handle = get_library(ext->library); + call = lt_dlsym(handle, ext->alias); + + if (call == NULL) + THROW(E_EXTSYM, ext->library, ext->alias); + + ALLOC_ZERO(&func, sizeof(EXTERN_FUNC)); + func->next = _functions; + func->alias = ext->alias; + _functions = func; + + func->call = call; + + if (!ext->vararg) + prepare_cif(&func->info, ext->n_param, (TYPE *)ext->param, ext->type, ext->n_param, NULL); + + //ext->library = (char *)handle; + ext->alias = (char *)func; + ext->loaded = TRUE; + + return func; +} + +void *EXTERN_get_addr(CLASS_EXTERN *ext) +{ + EXTERN_FUNC *func = get_function(ext); + return func->call; +} + + +/* + EXEC.class : the class + EXEC.index : the extern function index + EXEC.nparam : the number of parameters to the call + EXEC.drop : if the return value should be dropped. +*/ + +void EXTERN_call(void) +{ + static const void *jump[17] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL, &&__OBJECT + }; + static const int use_temp[17] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, sizeof(char *), sizeof(char *), 0, 0, 0, 0, 0, sizeof(void *) }; + static char temp[16 * sizeof(void *)]; + static void *null = 0; + + CLASS_EXTERN *ext = &EXEC.class->load->ext[EXEC.index]; + EXTERN_FUNC *func; + EXTERN_CIF cif; + bool vararg; + int nparam = EXEC.nparam; + void *args[nparam]; + TYPE *sign; + VALUE *value; + char *tmp = NULL; + char *next_tmp; + int i, t; + union { + int _integer; + float _single; + double _float; + char *_string; + int64_t _long; + void *_pointer; + } + rvalue; + + if (!ext->loaded) + { + if (nparam < ext->n_param) + THROW(E_NEPARAM); + if (!ext->vararg && nparam > ext->n_param) + THROW(E_TMPARAM); + } + + func = get_function(ext); + sign = (TYPE *)ext->param; + vararg = ext->vararg; + value = &SP[-nparam]; + next_tmp = temp; + + for (i = 0; i < nparam; i++) + { + if (i < ext->n_param) + VALUE_conv(&value[i], sign[i]); + else + VARIANT_undo(&value[i]); + } + + if (vararg) + prepare_cif(&cif, ext->n_param, (TYPE *)ext->param, ext->type, nparam, value); + + for (i = 0; i < nparam; i++, value++, sign++) + { + if (TYPE_is_object(value->type)) + t = T_OBJECT; + else + t = (int)value->type; + + if (use_temp[t]) + { + tmp = next_tmp; + if ((tmp + use_temp[t]) > &temp[sizeof(temp)]) + THROW(E_TMPARAM); + args[i] = tmp; + next_tmp = tmp + use_temp[t]; + } + goto *jump[t]; + + __BOOLEAN: + __BYTE: + __SHORT: + __INTEGER: + args[i] = &value->_integer.value; + continue; + + __LONG: + args[i] = &value->_long.value; + continue; + + __SINGLE: + args[i] = &value->_single.value; + continue; + + __FLOAT: + args[i] = &value->_float.value; + continue; + + __STRING: + *((char **)tmp) = (char *)(value->_string.addr + value->_string.start); + continue; + + __OBJECT: + { + void *ob = value->_object.object; + void *addr; + CLASS *class; + + if (!ob) + goto __NULL; + + class = OBJECT_class(ob); + + if (class == CLASS_Class && !CLASS_is_native((CLASS *)ob)) + addr = ((CLASS *)ob)->stat; + else if (CLASS_is_array(class)) + addr = ((CARRAY *)ob)->data; + else if (CLASS_is_struct(class)) + { + if (((CSTRUCT *)ob)->ref) + addr = (char *)((CSTATICSTRUCT *)ob)->addr; + else + addr = (char *)ob + sizeof(CSTRUCT); + } + else + addr = (char *)ob + sizeof(OBJECT); + + *((void **)tmp) = addr; + } + continue; + + __POINTER: + args[i] = &value->_pointer.value; + continue; + + __NULL: + args[i] = &null; + continue; + + __DATE: + __VARIANT: + __VOID: + __CLASS: + __FUNCTION: + + THROW(E_UTYPE); + } + + if (vararg) + { + ffi_call(&cif.cif, func->call, &rvalue, args); + FREE(&cif.types); + } + else + ffi_call(&func->info.cif, func->call, &rvalue, args); + + + switch (ext->type) + { + case T_BOOLEAN: + case T_BYTE: + case T_SHORT: + case T_INTEGER: + GB_ReturnInteger(rvalue._integer); + break; + + case T_LONG: + GB_ReturnLong(rvalue._long); + break; + + case T_SINGLE: + GB_ReturnSingle(rvalue._single); + break; + + case T_FLOAT: + GB_ReturnFloat(rvalue._float); + break; + + case T_STRING: + GB_ReturnConstZeroString(rvalue._string); + break; + + case T_POINTER: + GB_ReturnPointer(rvalue._pointer); + break; + + case T_VOID: + default: + + if (TYPE_is_pure_object(ext->type)) + { + CLASS *class = (CLASS *)(ext->type); + if (CLASS_is_struct(class)) + { + GB_ReturnObject(CSTRUCT_create_static(STRUCT_CONST, class, rvalue._pointer)); + break; + } + } + + TEMP.type = T_VOID; + break; + } + + while (nparam) + { + nparam--; + POP(); + } + + POP(); /* extern function */ + + /* from EXEC_native() */ + + BORROW(&TEMP); + VALUE_conv(&TEMP, ext->type); + *SP = TEMP; + SP++; +} + +void EXTERN_release(void) +{ + EXTERN_CALLBACK *cb; + HASH_ENUM iter; + + if (!_callbacks) + return; + + CLEAR(&iter); + + for(;;) + { + cb = HASH_TABLE_next(_callbacks, &iter, FALSE); + if (!cb) + break; + if (cb->exec.object) + { + OBJECT_UNREF(cb->exec.object); + cb->exec.object = NULL; + } + } +} + +void EXTERN_exit(void) +{ + int i; + EXTERN_SYMBOL *esym; + EXTERN_CALLBACK *cb; + EXTERN_FUNC *func; + HASH_ENUM iter; + + if (_table) + { + for (i = 0; i < TABLE_count(_table); i++) + { + esym = (EXTERN_SYMBOL *)TABLE_get_symbol(_table, i); + if (esym->handle) + lt_dlclose(esym->handle); + } + + TABLE_delete(&_table); + } + + if (_callbacks) + { + CLEAR(&iter); + + for(;;) + { + cb = HASH_TABLE_next(_callbacks, &iter, FALSE); + if (!cb) + break; + if (cb->exec.object) + OBJECT_UNREF(cb->exec.object); + FREE(&cb->info.types); + ffi_closure_free(cb->closure); + } + + HASH_TABLE_delete(&_callbacks); + } + + while (_functions) + { + func = _functions; + _functions = func->next; + + FREE(&func->info.types); + FREE(&func); + } +} + +static void callback(ffi_cif *cif, void *result, void **args, void *user_data) +{ + static const void *jump[17] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL, &&__OBJECT + }; + + EXTERN_CALLBACK *cb = (EXTERN_CALLBACK *)user_data; + //VALUE_FUNCTION *value = &cb->func; + int i; + VALUE *arg; + + STACK_check(cb->nparam); + + for (i = 0; i < cb->nparam; i++) + { + arg = SP++; + arg->type = cb->sign[i]; + + if (TYPE_is_object(cb->sign[i])) + goto __OBJECT; + else + goto *jump[cb->sign[i]]; + + __BOOLEAN: + arg->_integer.value = *((char *)args[i]) ? -1 : 0; + continue; + + __BYTE: + arg->_integer.value = *((char *)args[i]); + continue; + + __SHORT: + arg->_integer.value = *((short *)args[i]); + continue; + + __INTEGER: + arg->_integer.value = *((int *)args[i]); + continue; + + __LONG: + arg->_long.value = *((int64_t *)args[i]); + continue; + + __SINGLE: + arg->_single.value = *((float *)args[i]); + continue; + + __FLOAT: + arg->_float.value = *((double *)args[i]); + continue; + + __STRING: + arg->type = T_CSTRING; + arg->_string.addr = *((char **)args[i]); + arg->_string.start = 0; + arg->_string.len = arg->_string.addr ? strlen(arg->_string.addr) : 0; + continue; + + __OBJECT: + arg->_object.object = *((void **)args[i]); + continue; + + __POINTER: + arg->_pointer.value = *((void **)args[i]); + continue; + + __NULL: + __DATE: + __VARIANT: + __VOID: + __CLASS: + __FUNCTION: + VALUE_null(arg); + } + + EXEC = cb->exec; + + if (!EXEC.native) + { + EXEC_function_keep(); + + // Do that later, within a TRY/CATCH: VALUE_conv(RP, cb->ret); + + switch (cb->ret) + { + case T_BOOLEAN: + case T_BYTE: + case T_SHORT: + case T_INTEGER: + *((ffi_arg *)result) = RP->_integer.value; + break; + + case T_LONG: + *((int64_t *)result) = RP->_long.value; + break; + + case T_SINGLE: + *((float *)result) = RP->_single.value; + break; + + case T_FLOAT: + *((double *)result) = RP->_float.value; + break; + + case T_STRING: + if (!RP->_string.len) + *((char **)result) = NULL; + else + *((char **)result) = RP->_string.addr + RP->_string.start; + break; + + case T_OBJECT: + *((void **)result) = RP->_object.object; + break; + + case T_POINTER: + *((void **)result) = RP->_pointer.value; + break; + + default: + break; + } + + EXEC_release_return_value(); + } +} + + +static void prepare_cif_from_gambas(EXTERN_CALLBACK *cb, FUNCTION *func) +{ + if (func->npmin != func->n_param || func->vararg) + THROW(E_EXTCB, "The function must take a fixed number of arguments"); + + cb->nparam = func->npmin; + cb->sign = (TYPE *)func->param; + cb->ret = func->type; +} + +/*static void prepare_cif_from_native(EXTERN_CALLBACK *cb, CLASS_DESC_METHOD *desc) +{ + THROW(E_EXTCB, "Not implemented yet"); +}*/ + +void *EXTERN_make_callback(VALUE_FUNCTION *value) +{ + EXEC_GLOBAL exec = { 0 }; + FUNCTION *func; + EXTERN_CALLBACK *cb; + union { + char key[sizeof(void *)]; + void *addr; + } + cb_key; + + if (value->kind == FUNCTION_EXTERN) + { + CLASS_EXTERN *ext = &value->class->load->ext[value->index]; + return get_function(ext)->call; + } + + if (!_callbacks) + HASH_TABLE_create(&_callbacks, sizeof(EXTERN_CALLBACK), HF_NORMAL); + + //ALLOC(&cb, sizeof(EXTERN_CALLBACK), "EXTERN_make_callback"); + + // See gbx_exec_loop.c, at the _CALL label, to understand the following. + + if (value->kind == FUNCTION_PRIVATE) + { + exec.object = value->object; + exec.class = value->class; + exec.native = FALSE; + exec.index = value->index; + } + else if (value->kind == FUNCTION_PUBLIC) + { + exec.object = value->object; + exec.native = FALSE; + exec.desc = &value->class->table[value->index].desc->method; + exec.index = (int)(intptr_t)(exec.desc->exec); + exec.class = exec.desc->class; + } + /*else if (value->kind == FUNCTION_NATIVE) + { + cb->exec.object = value->object; + cb->exec.class = value->class; + cb->exec.native = TRUE; + cb->exec.index = value->index; + cb->exec.desc = &value->class->table[value->index].desc->method; + //cb->desc = &value->class->table[value->index].desc->method; + + prepare_cif_from_native(cb, cb->exec.desc); + }*/ + else + THROW(E_EXTCB, "Not supported"); + + func = &exec.class->load->func[exec.index]; + cb_key.addr = func; + + cb = (EXTERN_CALLBACK *)HASH_TABLE_insert(_callbacks, cb_key.key, sizeof(void *)); + if (cb->code) + { + OBJECT_UNREF(value->object); + return cb->code; + } + + // Do not reference value->_function.object, as it has been already referenced + // when put on the stack in exec_loop.c + + /*if (value->object) + { + fprintf(stderr, "EXTERN_make_callback: ref: %p\n", value->object); + OBJECT_REF(value->object); + }*/ + + cb->exec = exec; + prepare_cif_from_gambas(cb, func); + + cb->exec.nparam = cb->nparam; + + prepare_cif(&cb->info, cb->nparam, cb->sign, cb->ret, cb->nparam, NULL); + + cb->closure = ffi_closure_alloc(sizeof(ffi_closure), &cb->code); + + if (ffi_prep_closure_loc(cb->closure, &cb->info.cif, callback, cb, cb->code) != FFI_OK) + THROW(E_EXTCB, "Unable to create closure"); + + return cb->code; +} + +#else /* HAVE_FFI_COMPONENT */ + +#include "gbx_value.h" +#include "gbx_extern.h" + +void EXTERN_call(void) +{ + THROW_ILLEGAL(); +} + +void EXTERN_release(void) +{ +} + +void EXTERN_exit(void) +{ +} + +void *EXTERN_make_callback(VALUE_FUNCTION *value) +{ + return NULL; +} + +void *EXTERN_get_symbol(const char *library, const char *symbol) +{ + return NULL; +} + + +EXTERN_FUNC_INFO EXTERN_get_function_info(CLASS_EXTERN *ext) +{ + EXTERN_FUNC_INFO func_info = { NULL, NULL }; + + return func_info; +} +#endif + diff --git a/main/gbx/gbx_extern.h b/main/gbx/gbx_extern.h new file mode 100644 index 00000000..8a2b1fc4 --- /dev/null +++ b/main/gbx/gbx_extern.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + gbx_extern.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_EXTERN_H +#define __GBX_EXTERN_H + +#include "config.h" + +#include + +#ifndef DONT_USE_LTDL + #include +#else + #define lt_dlsym dlsym + #define lt_dlclose dlclose + #define lt_dlerror dlerror + #define lt_dlhandle void * +#endif + +#include "gbx_value.h" + +void EXTERN_release(void); +void EXTERN_exit(void); +void EXTERN_call(void); +void *EXTERN_make_callback(VALUE_FUNCTION *value); +void *EXTERN_get_symbol(const char *library, const char *symbol); +void *EXTERN_get_addr(CLASS_EXTERN *ext); + +#endif diff --git a/main/gbx/gbx_info.h b/main/gbx/gbx_info.h new file mode 100644 index 00000000..10998329 --- /dev/null +++ b/main/gbx/gbx_info.h @@ -0,0 +1,70 @@ +/*************************************************************************** + + gbx_info.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_INFO_H +#define __GBX_INFO_H + +#include "gb_common.h" +#include "gambas.h" + +#ifdef GBX_INFO + +#undef GB_DECLARE +#define GB_DECLARE(name, size) { name, (intptr_t)GB_VERSION, 0 } + +#undef GB_HOOK_NEW +#define GB_HOOK_NEW(hook) { GB_HOOK_NEW_ID, 0 } + +#undef GB_HOOK_FREE +#define GB_HOOK_FREE(hook) { GB_HOOK_FREE_ID, 0 } + +#undef GB_HOOK_CHECK +#define GB_HOOK_CHECK(hook) { GB_HOOK_CHECK_ID, 0 } + +#undef GB_PROPERTY +#define GB_PROPERTY(symbol, type, proc) { "p" symbol, (intptr_t)type, 0 } + +#undef GB_PROPERTY_READ +#define GB_PROPERTY_READ(symbol, type, proc) { "r" symbol, (intptr_t)type, 0 } + +#undef GB_METHOD +#define GB_METHOD(symbol, type, exec, signature) { "m" symbol, (intptr_t)type, 0, (intptr_t)signature } + +#undef GB_EVENT +#define GB_EVENT(symbol, type, signature, id) { "::" symbol, (intptr_t)type, 0, (intptr_t)signature } + +#undef GB_STATIC_PROPERTY +#define GB_STATIC_PROPERTY(symbol, type, proc) { "P" symbol, (intptr_t)type, 0 } + +#undef GB_STATIC_PROPERTY_READ +#define GB_STATIC_PROPERTY_READ(symbol, type, proc) { "R" symbol, (intptr_t)type, 0 } + +#undef GB_STATIC_METHOD +#define GB_STATIC_METHOD(symbol, type, exec, signature) { "M" symbol, (intptr_t)type, 0, (intptr_t)signature } + +#undef GB_STATIC_FAST_METHOD +#define GB_STATIC_FAST_METHOD(symbol, type, exec, signature) { "M" symbol, (intptr_t)type, 0, (intptr_t)signature } + +#endif + +#endif diff --git a/main/gbx/gbx_jit.c b/main/gbx/gbx_jit.c new file mode 100644 index 00000000..0ad63672 --- /dev/null +++ b/main/gbx/gbx_jit.c @@ -0,0 +1,345 @@ +/*************************************************************************** + + gbx_jit.c + + (c) 2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_JIT_C + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_common_case.h" +#include "gbx_component.h" +#include "gbx_exec.h" +#include "gbx_object.h" +#include "gbx_api.h" +#include "gbx_jit.h" + +typedef + struct { + JIT_FUNC addr; + PCODE *code; + } + JIT_FUNCTION; + +bool JIT_disabled = FALSE; + +static bool _component_loaded = FALSE; +static GB_FUNCTION _jit_compile_func; + +static bool _jit_compiling = FALSE; +static void *_jit_library = NULL; + +static JIT_FUNCTION *_jit_func = NULL; + +static bool _debug = FALSE; + +void JIT_exit(void) +{ + ARRAY_delete(&_jit_func); +} + +bool JIT_can_compile(ARCHIVE *arch) +{ + return arch ? !arch->jit_compiling : !_jit_compiling; +} + +bool JIT_compile(ARCHIVE *arch) +{ + GB_VALUE *ret; + char *path; + void *lib; + void **iface; + COMPONENT *current; + + if (JIT_disabled) + return TRUE; + + if (arch) + { + if (arch->jit_library) + return FALSE; + } + else + { + if (_jit_library) + return FALSE; + } + + if (!_component_loaded) + { + char *var; + + _component_loaded = TRUE; + + var = getenv("GB_NO_JIT"); + if (var && var[0] && !(var[0] == '0' && var[1] == 0)) + { + JIT_disabled = TRUE; + return TRUE; + } + + var = getenv("GB_JIT_DEBUG"); + if (var && var[0] && !(var[0] == '0' && var[1] == 0)) + _debug = TRUE; + + if (_debug) + fprintf(stderr, "gbx3: loading gb.jit component\n"); + + COMPONENT_load(COMPONENT_create("gb.jit")); + if (GB_GetFunction(&_jit_compile_func, CLASS_find_global("Jit"), "_Compile", "s", "s")) + ERROR_panic("Unable to find JIT compilation method"); + } + + arch ? (arch->jit_compiling = TRUE) : (_jit_compiling = TRUE); + + current = COMPONENT_current; + COMPONENT_current = NULL; + + GB_Push(1, T_STRING, arch ? arch->name : "", -1); + ret = GB_Call(&_jit_compile_func, 1, FALSE); + path = GB_ToZeroString((GB_STRING *)ret); + + COMPONENT_current = current; + + if (!*path) + ERROR_panic("Unable to compile JIT source file"); + + arch ? (arch->jit_compiling = FALSE) : (_jit_compiling = FALSE); + + //fprintf(stderr, "gbx3: shared jit library is: %s\n", path); + + lib = dlopen(path, RTLD_NOW); + if (!lib) + ERROR_panic("Unable to load JIT library: %s", dlerror()); + + if (arch) + arch->jit_library = lib; + else + _jit_library = lib; + + iface = dlsym(lib, "GB_PTR"); + if (iface) *((void **)iface) = &GAMBAS_Api; + + iface = dlsym(lib, "JIT_PTR"); + if (iface) *((void **)iface) = &GAMBAS_JitApi; + + return FALSE; +} + +static bool create_function(CLASS *class, int index) +{ + ARCHIVE *arch; + FUNCTION *func; + JIT_FUNCTION *jit; + void *lib; + void *addr; + int i; + int len; + char *name; + + arch = class->component ? class->component->archive : NULL; + + func = &class->load->func[index]; + func->fast_linked = TRUE; + + if (!arch) + lib = _jit_library; + else + lib = arch->jit_library; + + name = class->name; + while (*name == '^') + name++; + + len = sprintf(COMMON_buffer, "jit_%s_%d", name, index); + + for (i = 0; i < len; i++) + COMMON_buffer[i] = tolower(COMMON_buffer[i]); + + addr = dlsym(lib, COMMON_buffer); + if (!addr) + { + func->fast = FALSE; + return TRUE; + } + + if (_debug && func->debug) + fprintf(stderr, "gbx3: loading jit function: %s.%s\n", class->name, func->debug->name); + + if (!_jit_func) + ARRAY_create(&_jit_func); + + jit = (JIT_FUNCTION *)ARRAY_add(&_jit_func); + + jit->addr = addr; + jit->code = func->code; + + func->code = (PCODE *)jit; + + return FALSE; +} + + +void JIT_exec(bool ret_on_stack) +{ + VALUE *sp = SP; + JIT_FUNCTION *jit; + CLASS *class = EXEC.class; + char nparam = EXEC.nparam; + VALUE ret; + FUNCTION *func = EXEC.func; + + if (UNLIKELY(nparam < func->npmin)) + THROW(E_NEPARAM); + else if (UNLIKELY(nparam > func->n_param && !func->vararg)) + THROW(E_TMPARAM); + + if (!func->fast_linked) + { + if (create_function(class, EXEC.index)) + return; + } + + STACK_push_frame(&EXEC_current, func->stack_usage); + + CP = class; + OP = (void *)EXEC.object; + FP = func; + EC = NULL; + + jit = (JIT_FUNCTION *)(func->code); + + PROFILE_ENTER_FUNCTION(); + + TRY + { + (*(jit->addr))(nparam); + } + CATCH + { + PROFILE_LEAVE_FUNCTION(); + if (SP != sp) + ERROR_panic("Stack mismatch in JIT function (SP %+ld)\n", SP - sp); + RELEASE_MANY(SP, nparam); + STACK_pop_frame(&EXEC_current); + PROPAGATE(); + } + END_TRY + + PROFILE_LEAVE_FUNCTION(); + + if (SP != sp) + ERROR_panic("Stack mismatch in JIT function (SP %+ld)\n", SP - sp); + + if (func->type != T_VOID) + { + ret = TEMP; + BORROW(&ret); + } + else + ret.type = T_VOID; + + RELEASE_MANY(SP, nparam); + + RET = ret; + + STACK_pop_frame(&EXEC_current); + + if (ret_on_stack) + { + if (SP[-1].type == T_FUNCTION) + { + SP--; + OBJECT_UNREF(SP->_function.object); + } + + *SP++ = ret; + ret.type = T_VOID; + } +} + +PCODE *JIT_get_code(FUNCTION *func) +{ + if (func->fast_linked) + return ((JIT_FUNCTION *)(func->code))->code; + else + return func->code; +} + +void *JIT_get_class_ref(int index) +{ + return CP->load->class_ref[index]; +} + +CLASS_CONST *JIT_get_constant(int index) +{ + return &CP->load->cst[index]; +} + +void JIT_debug(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +typedef + struct { + PCODE *pc; + VALUE **psp; + PCODE code; + } + JIT_call_unknown_ERROR; + +static void error_JIT_call_unknown(JIT_call_unknown_ERROR *save) +{ + save->pc[1] = (PCODE)save->code; + *save->psp = SP; +} + +void JIT_call_unknown(PCODE *pc, VALUE **psp) +{ + JIT_call_unknown_ERROR save; + + PC = pc; + SP = *psp; + + save.pc = pc; + save.psp = psp; + save.code = pc[1]; + + pc[1] = 0x140B; + + ON_ERROR_1(error_JIT_call_unknown, &save) + { + EXEC_function_loop(); + } + END_ERROR + + error_JIT_call_unknown(&save); +} + +void JIT_load_class(CLASS *class) +{ + CLASS_load(class); +} diff --git a/main/gbx/gbx_jit.h b/main/gbx/gbx_jit.h new file mode 100644 index 00000000..60f895a8 --- /dev/null +++ b/main/gbx/gbx_jit.h @@ -0,0 +1,55 @@ +/*************************************************************************** + + gbx_jit.h + + (c) 2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_JIT_H +#define __GBX_JIT_H + +#include "gbx_class.h" +#include "gbx_type.h" +#include "gbx_value.h" +#include "gbx_stack.h" +#include "gbx_object.h" +#include "gbx_exec.h" + +typedef + void (*JIT_FUNC)(uchar nparam); + +#ifndef __GBX_JIT_C +extern bool JIT_disabled; +#endif + +bool JIT_compile(ARCHIVE *arch); +void JIT_debug(const char *fmt, ...); +void JIT_exec(bool ret_on_stack); +PCODE *JIT_get_code(FUNCTION *func); +CLASS_CONST *JIT_get_constant(int index); +void *JIT_get_class_ref(int index); +void JIT_call_unknown(PCODE *pc, VALUE **psp); + +void JIT_exit(void); + +bool JIT_can_compile(ARCHIVE *arch); + +void JIT_load_class(CLASS *class); + +#endif diff --git a/main/gbx/gbx_library.c b/main/gbx/gbx_library.c new file mode 100644 index 00000000..4d7447a4 --- /dev/null +++ b/main/gbx/gbx_library.c @@ -0,0 +1,365 @@ +/*************************************************************************** + + gbx_library.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_LIBRARY_C + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_common_case.h" +#include "gb_error.h" +#include "gb_alloc.h" +#include "gb_replace.h" +#include "gb_magic.h" + +#include +#include +#include +#include +#include + +#include "gb_error.h" + +#include "gbx_class.h" +#include "gbx_exec.h" +#include "gbx_event.h" +#include "gbx_stack.h" +#include "gb_file.h" +#include "gbx_archive.h" +#include "gbx_project.h" +#include "gbx_api.h" +#include "gbx_string.h" +#include "gbx_object.h" +#include "gbx_component.h" +#include "gb.jit.h" + +#include "gbx_library.h" + +//#define DEBUG +//#define DEBUG_PRELOAD + +// Maximum size of a project or startup file +// This avoids reading too much in the archive file! + +#define MAX_SIZE 2048 + +int _bytes_read = 0; + +#if 0 +static lt_ptr library_malloc(size_t size) +{ + lt_ptr ptr; + ALLOC(&ptr, size, "library_malloc"); + printf("library_malloc: %d -> %p\n", size, ptr); + return ptr; +} + + +static void library_free(lt_ptr ptr) +{ + printf("library_free -> %p\n", ptr); + FREE(&ptr, "library_free"); +} +#endif + +static void *get_symbol(LIBRARY *lib, const char *symbol, bool err) +{ + void *sym; + + sym = lt_dlsym(lib->handle, symbol); + if (sym == NULL && err) + { + strcpy(COMMON_buffer, lt_dlerror()); + lt_dlclose(lib->handle); + lib->handle = NULL; + THROW(E_LIBRARY, lib->name, COMMON_buffer); + } + + return sym; +} + + +static void copy_interface(void **src, void **dst) +{ + for(;;) + { + if (!*src) + return; + + *dst++ = *src++; + } +} + + +void LIBRARY_init(void) +{ + /*if (putenv("LD_BIND_NOW=true")) + ERROR_panic("Cannot set LD_BIND_NOW: &1", strerror(errno)); + + if (putenv("KDE_MALLOC=0")) + ERROR_panic("Cannot set KDE_MALLOC: &1", strerror(errno));*/ + + /*lt_dlmalloc = library_malloc; + lt_dlfree = library_free;*/ + + #ifndef DONT_USE_LTDL + if (lt_dlinit()) + ERROR_panic("Cannot initialize plug-in management: %s", lt_dlerror()); + #endif +} + + +void LIBRARY_exit(void) +{ + #ifndef DONT_USE_LTDL + lt_dlexit(); + #endif +} + + +void LIBRARY_get_interface(LIBRARY *lib, int version, void *iface) +{ + char symbol[32]; + int i, len; + char c; + + len = strlen(lib->name); + for (i = 0; i < len; i++) + { + c = toupper(lib->name[i]); + if (!isalnum((unsigned char)c)) + c = '_'; + + symbol[i] = c; + } + + sprintf(&symbol[len], "_%d", version); + + copy_interface((void **)get_symbol(lib, symbol, TRUE), iface); +} + + +bool LIBRARY_get_interface_by_name(const char *name, int version, void *iface) +{ + COMPONENT *comp; + + comp = COMPONENT_find(name); + if (!comp || !comp->library) + return TRUE; + + LIBRARY_get_interface(comp->library, version, iface); + return FALSE; +} + + + +LIBRARY *LIBRARY_create(const char *name) +{ + LIBRARY *lib; + + ALLOC_ZERO(&lib, sizeof(LIBRARY)); + + lib->handle = NULL; + lib->name = name; + + /*if (name) + { + lib->persistent = FALSE; + lib->preload = FALSE; + } + else + { + lib->persistent = TRUE; + lib->preload = TRUE; + }*/ + + return lib; +} + + +void LIBRARY_delete(LIBRARY *lib) +{ + LIBRARY_unload(lib); + FREE(&lib); +} + + +static void init_interface(LIBRARY *lib, const char *sym, const char *sym_ptr, void **api) +{ + void **iface; + + iface = get_symbol(lib, sym_ptr, FALSE); + if (iface) + { + *((void **)iface) = api; + return; + } + + iface = get_symbol(lib, sym, FALSE); + if (iface) + copy_interface(api, iface); +} + + +int LIBRARY_load(LIBRARY *lib) +{ + int (*func)(); + GB_DESC **desc; + char *path; + int order = 0; + + if (lib->handle) + return 0; + +#ifdef DEBUG + clock_t t = clock(); + fprintf(stderr, "Loading library %s\n", lib->name); +#endif + + path = FILE_buffer(); + sprintf(path, LIB_PATTERN, COMPONENT_path, lib->name); + + //if (!FILE_exist(path)) + // sprintf(path, LIB_PATTERN, COMPONENT_user_path, lib->name); + + #ifndef DONT_USE_LTDL + /* no more available in libltld ? + lt_dlopen_flag = RTLD_LAZY; + */ + lib->handle = lt_dlopenext(path); + #else + lib->handle = dlopen(path, RTLD_LAZY); + #endif + + if (lib->handle == NULL) + THROW(E_LIBRARY, lib->name, lt_dlerror()); + + func = get_symbol(lib, LIB_INIT, TRUE); + + /* Interface de Gambas */ + + init_interface(lib, LIB_GAMBAS, LIB_GAMBAS "_PTR", GAMBAS_Api); + init_interface(lib, LIB_JIT, LIB_JIT "_PTR", GAMBAS_JitApi); + + /* Signal function */ + lib->signal = (void(*)())get_symbol(lib, LIB_SIGNAL, FALSE); + lib->info = (int(*)())get_symbol(lib, LIB_INFO, FALSE); + + /* Initialisation */ + order = (*func)(); + + /* Déclaration des classes */ + desc = get_symbol(lib, LIB_CLASS, FALSE); + if (desc) + LIBRARY_declare(desc); + +#ifdef DEBUG + fprintf(stderr, "Library %s loaded ", lib->name); + fprintf(stderr, "in %g s\n", ((double)(clock() - t) / CLOCKS_PER_SEC)); +#endif + + return order; +} + +void LIBRARY_after_init(LIBRARY *lib) +{ + void (*func)(); + + func = get_symbol(lib, LIB_AFTER_INIT, FALSE); + if (func) (*func)(); +} + +void LIBRARY_exec(LIBRARY *lib, int argc, char **argv) +{ + void (*func)(); + + func = get_symbol(lib, LIB_MAIN, FALSE); + if (func) + (*func)(argc, argv); +} + +void LIBRARY_declare_one(GB_DESC *desc) +{ + CLASS_find_global(desc->name); + if (CLASS_register(desc) == NULL) + THROW(E_REGISTER, desc->name); +} + +void LIBRARY_declare(GB_DESC **desc) +{ + GB_DESC **p; + + p = desc; + while (*p != NULL) + { + CLASS_find_global((*p)->name); + p++; + } + + p = desc; + while (*p != NULL) + { + if (CLASS_register(*p) == NULL) + THROW(E_REGISTER, (*p)->name); + + p++; + } +} + + +void LIBRARY_unload(LIBRARY *lib) +{ + void (*gambas_exit)(); + + if (lib->handle == NULL) + return; + + /* Pas de lib�ation des classes pr�harg� ! */ + + /* V�ification qu'aucune classe de la librairie n'est instanci� ! */ + + gambas_exit = lt_dlsym(lib->handle, LIB_EXIT); + if (gambas_exit != NULL) + (*gambas_exit)(); + + if (lib->persistent) + { + gambas_exit = lt_dlsym(lib->handle, "_fini"); + if (gambas_exit != NULL) + (*gambas_exit)(); + } + else + { + lt_dlclose(lib->handle); + } + + lib->handle = NULL; + +#ifdef DEBUG + printf("Unloading library %s\n", lib->name); +#endif +} + diff --git a/main/gbx/gbx_library.h b/main/gbx/gbx_library.h new file mode 100644 index 00000000..2ea37930 --- /dev/null +++ b/main/gbx/gbx_library.h @@ -0,0 +1,77 @@ +/*************************************************************************** + + gbx_library.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_LIBRARY_H +#define __GBX_LIBRARY_H + +#include "gambas.h" +#include "gb_list.h" +#include "gb_component.h" + +#include + +#ifndef DONT_USE_LTDL + #include +#else + #define lt_dlsym dlsym + #define lt_dlclose dlclose + #define lt_dlerror dlerror +#endif + +typedef + struct _LIBRARY { +#ifdef USE_LTDL + lt_dlhandle handle; +#else + void *handle; +#endif + const char *name; + void (*signal)(); + int (*info)(); + unsigned persistent : 1; + unsigned _reserved: 13; + } + LIBRARY; + +#ifndef __GBX_LIBRARY_C +#endif + +void LIBRARY_init(void); +void LIBRARY_exit(void); + +LIBRARY *LIBRARY_create(const char *path); +void LIBRARY_delete(LIBRARY *lib); + +int LIBRARY_load(LIBRARY *lib); +void LIBRARY_unload(LIBRARY *lib); + +void LIBRARY_declare(GB_DESC **desc); +void LIBRARY_declare_one(GB_DESC *desc); + +bool LIBRARY_get_interface_by_name(const char *name, int version, void *iface); +void LIBRARY_get_interface(LIBRARY *lib, int version, void *iface); + +void LIBRARY_exec(LIBRARY *lib, int argc, char **argv); +void LIBRARY_after_init(LIBRARY *lib); + +#endif diff --git a/main/gbx/gbx_local.c b/main/gbx/gbx_local.c new file mode 100644 index 00000000..18a97872 --- /dev/null +++ b/main/gbx/gbx_local.c @@ -0,0 +1,1766 @@ +/*************************************************************************** + + gbx_local.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_LOCAL_C + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_common_case.h" + +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "gbx_math.h" +#include "gbx_date.h" +#include "gbx_string.h" +#include "gbx_c_string.h" +#include "gbx_api.h" +#include "gb_file.h" +#include "gbx_component.h" +#include "gbx_exec.h" +#include "gbx_archive.h" + +#include "gbx_local.h" + +//#define DEBUG_LANG + +#define buffer_init COMMON_buffer_init +#define get_char COMMON_get_char +#define last_char COMMON_last_char +#define look_char COMMON_look_char +#define put_char COMMON_put_char +#define jump_space COMMON_jump_space +#define get_current COMMON_get_current +#define buffer_pos COMMON_pos +#define get_size_left COMMON_get_size_left + +static void add_string(const char *src, int len, int *before); + +/* System encoding*/ +char *LOCAL_encoding = NULL; + +/* If system encoding is UTF-8 */ +bool LOCAL_is_UTF8; + +/* Default 'C' localization */ +LOCAL_INFO LOCAL_default = { + '.', '.', + NULL, 0, NULL, 0, + 3, 3, + 0, + '/', ':', + "", + "", + { LO_MONTH, LO_DAY, LO_YEAR }, + { LO_HOUR, LO_MINUTE, LO_SECOND }, + "dddd mmmm d yyyy", + "mmm d yyyy", + "mm/dd/yyyy", + "hh:nn:ss", + "hh:nn AM/PM", + "hh:nn", + "mm/dd/yyyy hh:nn:ss", + "(#,##0.##)", + "(#,##0.##)", + "True", 4, + "False", 5, + 0 + }; + +/* User language localization */ +LOCAL_INFO LOCAL_local = { 0 }; + +// First day of weekday +char LOCAL_first_day_of_week = -1; + +static char *_rtl_lang[] = { "ar", "fa", "he", NULL }; + +static bool _translation_loaded = FALSE; + +static LOCAL_INFO *local_current; + +static char *env_LC_ALL = NULL; +static char *env_LANGUAGE = NULL; +static char *env_LANG = NULL; + +static bool _currency; + +static char *_lang = NULL; + +extern char **environ; +static char **_environ; + +#define add_currency_flag(_flag) (LOCAL_local.currency_flag <<= 1, LOCAL_local.currency_flag |= (!!(_flag))) +#define test_currency_flag(_negative, _space, _before, _intl) (!!(LOCAL_local.currency_flag & (1 << ((!!_negative) + ((!!_before) << 1) + ((!!_intl) << 2))))) + +static void init_currency_flag(struct lconv *info) +{ +#ifndef OS_BSD + add_currency_flag(info->int_n_cs_precedes); // 7 + add_currency_flag(info->int_p_cs_precedes); // 6 +#else + add_currency_flag(info->n_cs_precedes); // 7 + add_currency_flag(info->p_cs_precedes); // 6 +#endif + add_currency_flag(1); // 5 + add_currency_flag(1); // 4 + add_currency_flag(info->n_cs_precedes); // 3 + add_currency_flag(info->p_cs_precedes); // 2 + add_currency_flag(info->n_sep_by_space); // 1 + add_currency_flag(info->p_sep_by_space); // 0 +} + +static bool is_currency_before(bool negative, bool intl) +{ + return test_currency_flag(negative, FALSE, TRUE, intl); +} + +static bool is_currency_space(bool negative, bool intl) +{ + //fprintf(stderr, "%02X & %02X\n", LOCAL_local.currency_flag, (1 << ((!!negative) + ((!!0) << 1) + ((!!intl) << 2)))); + return test_currency_flag(negative, TRUE, FALSE, intl); +} + +static int my_setenv(const char *name, const char *value, char **ptr) +{ + char *str = NULL; + + str = STRING_add(str, name, 0); + str = STRING_add_char(str, '='); + str = STRING_add(str, value, 0); + + if (putenv(str)) + { + STRING_free(&str); + return 1; + } + else + { + STRING_free(ptr); + *ptr = str; + return 0; + } +} + +static void begin(void) +{ + buffer_init(COMMON_buffer, COMMON_BUF_MAX - 4); +} + +static void end(char **str, int *len) +{ + *(get_current()) = 0; + *str = COMMON_buffer; + *len = buffer_pos; +} + + +static void stradd_sep(char *dst, const char *src, const char *sep) +{ + if (*dst) + strcat(dst, sep); + + strcat(dst, src); +} + + +static void add_thousand_sep(int *before) +{ + int group; + const char *thsep; + int lthsep; + + if (before == NULL) + return; + + thsep = _currency ? local_current->thousand_sep : local_current->currency_thousand_sep; + lthsep = _currency ? local_current->len_thousand_sep : local_current->len_currency_thousand_sep; + + if (thsep && thsep) + { + group = _currency ? local_current->currency_group_size : local_current->group_size; + + if (group > 0 && (*before > 1) && ((*before - 1) == (((*before - 1) / group) * group))) + { + if (buffer_pos > 0 && (get_current()[-1] == ' ')) + put_char(' '); + else + add_string(thsep, lthsep, NULL); + } + } + + (*before)--; +} + + +static void add_string(const char *src, int len, int *before) +{ + if (len <= 0) + len = strlen(src); + + while (len > 0) + { + put_char(*src++); + len--; + + if (before) + add_thousand_sep(before); + } +} + +static void add_unicode(uint unicode) +{ + char str[8]; + STRING_utf8_from_unicode(unicode, str); + add_string(str, STRING_utf8_get_char_length(*str), NULL); +} + +static void add_currency(const char *sym) +{ + const char *p = sym; + char c; + + for(;;) + { + c = *p++; + if (c == 0) + return; + if (c != ' ') + put_char(c); + } +} + + +static void add_char(char c, int count, int *before) +{ + while (count > 0) + { + put_char(c); + count--; + + add_thousand_sep(before); + } +} + +static void add_zero(int count, int *before) +{ + add_char('0', count, before); +} + + +static int search(const char *src, int len, const char *list, int start, bool not) +{ + int i; + char c; + + for (i = start; i < len; i++) + { + c = src[i]; + + if (c == '\\') + { + i++; + if (i >= len) + break; + c = src[i]; + continue; + } + + if ((index(list, c) != NULL) ^ not) + return i; + } + + return len; +} + + +static void add_sign(char mode, int sign, bool after) +{ + if (after && mode != '(') + return; + + if (sign < 0) + { + if (mode == '(') + put_char(after ? ')' : '('); + else + put_char('-'); + } + else if (mode != 0 && mode != '(') + { + if (sign > 0) + put_char(mode); + else + put_char(' '); + } +} + + +static char *get_languages(void) +{ + char *lang; + char *lang_list = NULL; + char *src; + + lang = STRING_new_temp_zero(LOCAL_get_lang()); + + lang_list = STRING_add(lang_list, lang, 0); + lang_list = STRING_add_char(lang_list, ':'); + + src = index(lang, '_'); + if (src) + { + *src = 0; + lang_list = STRING_add(lang_list, lang, 0); + lang_list = STRING_add_char(lang_list, ':'); + } + + #ifdef DEBUG_LANG + fprintf(stderr, "Languages = %s\n", lang_list); + #endif + + return lang_list; +} + + +static void free_local_info(void) +{ + STRING_free(&LOCAL_local.true_str); + STRING_free(&LOCAL_local.false_str); + CLEAR(&LOCAL_local); +} + +static const char *fix_separator(const char *str) +{ + if (!str || !*str) + return " "; + + if ((uchar)str[0] == 0xC2 && (uchar)str[1] == 0xA0 && str[2] == 0) + return " "; + + if ((uchar)str[0] == 0xE2 && (uchar)str[1] == 0x80 && (uchar)str[2] == 0xAF && str[3] == 0) + return " "; + + return str[1] ? "_" : str; +} + +static void fill_local_info(void) +{ + struct lconv *info; + char *p; + char c; + char *dp; + char *tp; + char *codeset; + const char *lang; + char *am_pm; + bool got_second; + + free_local_info(); + + /* local encoding */ + + if (!LOCAL_is_UTF8) + STRING_free(&LOCAL_encoding); + + codeset = nl_langinfo(CODESET); + if (!codeset || !*codeset) + codeset = "US-ASCII"; + + LOCAL_is_UTF8 = (strcasecmp(codeset, "UTF-8") == 0); + if (LOCAL_is_UTF8) + LOCAL_encoding = SC_UTF8; + else + LOCAL_encoding = STRING_new_zero(codeset); + + #ifdef DEBUG_LANG + fprintf(stderr, "LOCAL_encoding = %s\n", LOCAL_encoding == SC_UTF8 ? "UTF-8" : LOCAL_encoding); + #endif + + /* Numeric information */ + + info = localeconv(); + + //fprintf(stderr, "'%s' '%s' %d %d\n", info->thousands_sep, info->mon_thousands_sep, *info->thousands_sep, *info->grouping); + //fprintf(stderr, "'%s' '%s'\n", nl_langinfo(THOUSANDS_SEP), nl_langinfo(MON_THOUSANDS_SEP)); + + LOCAL_local.decimal_point = *(info->decimal_point); + LOCAL_local.thousand_sep = fix_separator(info->thousands_sep); + LOCAL_local.len_thousand_sep = strlen(LOCAL_local.thousand_sep); + + LOCAL_local.group_size = *(info->grouping); + if (LOCAL_local.group_size == 0) + LOCAL_local.group_size = 3; + + /*LOCAL_local.currency_symbol = STRING_conv_to_UTF8(info->currency_symbol, 0); + STRING_ref(LOCAL_local.currency_symbol); + LOCAL_local.intl_currency_symbol = STRING_conv_to_UTF8(info->int_curr_symbol, 0); + STRING_ref(LOCAL_local.intl_currency_symbol);*/ + + // Date format + + p = nl_langinfo(D_FMT); + //fprintf(stderr, "date format: %s\n", p); + dp = LOCAL_local.date_order; + + if (strcmp(p, "%D") == 0) + p = "%m/%d/%y"; + else if (strcmp(p, "%F") == 0) + p = "%Y-%m-%d"; + + for (;;) + { + c = *p++; + if (!c) + break; + + if (c == '%') + { + c = *p++; + if (c != '%') + { + if (c == 'E' || c == 'O') + c = *p++; + + switch (c) + { + case 'y': case 'Y': + *dp++ = LO_YEAR; + stradd_sep(LOCAL_local.long_date, "yyyy", " "); + stradd_sep(LOCAL_local.medium_date, "yyyy", " "); + stradd_sep(LOCAL_local.short_date, "yyyy", "/"); + stradd_sep(LOCAL_local.general_date, "yyyy", "/"); + break; + + case 'b': case 'B': case 'h': case 'm': + *dp++ = LO_MONTH; + stradd_sep(LOCAL_local.long_date, "mmmm", " "); + stradd_sep(LOCAL_local.medium_date, "mmm", " "); + stradd_sep(LOCAL_local.short_date, "mm", "/"); + stradd_sep(LOCAL_local.general_date, "mm", "/"); + break; + + case 'd': case 'e': + *dp++ = LO_DAY; + stradd_sep(LOCAL_local.long_date, "dddd d", " "); + stradd_sep(LOCAL_local.medium_date, "dd", " "); + stradd_sep(LOCAL_local.short_date, "dd", "/"); + stradd_sep(LOCAL_local.general_date, "dd", "/"); + break; + } + continue; + } + } + + if (dp != LOCAL_local.date_order && LOCAL_local.date_sep == 0) + LOCAL_local.date_sep = STRING_utf8_to_unicode(p - 1, STRING_utf8_get_char_length(c)); + } + + // Time format + + p = nl_langinfo(T_FMT); + //fprintf(stderr, "time format: %s\n", p); + tp = LOCAL_local.time_order; + + if (strcmp(p, "%T") == 0 || strcmp(p, "%R") == 0 || strcmp(p, "%r") == 0) + p = "%H:%M:%S"; + + got_second = FALSE; + + for (;;) + { + c = *p++; + if (!c) + break; + + if (c == '%') + { + c = *p++; + if (c != '%') + { + if (c == 'E' || c == 'O') + c = *p++; + + switch(c) + { + case 'H': case 'I': case 'k': case 'l': + *tp++ = LO_HOUR; + stradd_sep(LOCAL_local.long_time, "hh", ":"); + stradd_sep(LOCAL_local.medium_time, "hh", ":"); + stradd_sep(LOCAL_local.short_time, "hh", ":"); + break; + + case 'M': + *tp++ = LO_MINUTE; + stradd_sep(LOCAL_local.long_time, "nn", ":"); + stradd_sep(LOCAL_local.medium_time, "nn", ":"); + stradd_sep(LOCAL_local.short_time, "nn", ":"); + break; + + case 'S': + *tp++ = LO_SECOND; + stradd_sep(LOCAL_local.long_time, "ss", ":"); + got_second = TRUE; + break; + } + continue; + } + } + + if (tp != LOCAL_local.time_order && LOCAL_local.time_sep == 0) + LOCAL_local.time_sep = STRING_utf8_to_unicode(p - 1, STRING_utf8_get_char_length(c)); + } + + // Fix missing seconds + + if (!got_second) + { + *tp++ = LO_SECOND; + stradd_sep(LOCAL_local.long_time, "ss", ":"); + } + + // Fix the french date separator + + lang = LOCAL_get_lang(); + if (strcmp(lang, "fr") == 0 || strncmp(lang, "fr_", 3) == 0) + LOCAL_local.date_sep = '/'; + + stradd_sep(LOCAL_local.general_date, LOCAL_local.long_time, " "); + am_pm = nl_langinfo(AM_STR); + if (am_pm && *am_pm) + { + am_pm = nl_langinfo(PM_STR); + if (am_pm && *am_pm) + { + stradd_sep(LOCAL_local.medium_time, "AM/PM", " "); + } + } + + // Currency format + + LOCAL_local.currency_thousand_sep = fix_separator(info->mon_thousands_sep); + LOCAL_local.len_currency_thousand_sep = strlen(LOCAL_local.currency_thousand_sep); + + LOCAL_local.currency_group_size = *(info->mon_grouping); + if (LOCAL_local.currency_group_size == 0) + LOCAL_local.currency_group_size = 3; + + LOCAL_local.currency_decimal_point = *(info->mon_decimal_point); + LOCAL_local.currency_symbol = info->currency_symbol; + LOCAL_local.intl_currency_symbol = info->int_curr_symbol; + + strcpy(LOCAL_local.general_currency, "($#,##0."); + strncat(LOCAL_local.general_currency, "########", Min(8, info->frac_digits)); + strcat(LOCAL_local.general_currency, ")"); + + strcpy(LOCAL_local.intl_currency, "($$#,##0."); + strncat(LOCAL_local.intl_currency, "########", Min(8, info->int_frac_digits)); + strcat(LOCAL_local.intl_currency, ")"); + + init_currency_flag(info); + + LOCAL_local.true_str = STRING_new_zero(LOCAL_gettext(LOCAL_default.true_str)); + LOCAL_local.len_true_str = STRING_length(LOCAL_local.true_str); + LOCAL_local.false_str = STRING_new_zero(LOCAL_gettext(LOCAL_default.false_str)); + LOCAL_local.len_false_str = STRING_length(LOCAL_local.false_str); +} + + +void LOCAL_init(void) +{ + _environ = environ; + LOCAL_set_lang(NULL); +} + +void LOCAL_exit(void) +{ + if (env_LANG) + { + if (environ == _environ) + unsetenv("LANG"); + STRING_free(&env_LANG); + } + if (env_LC_ALL) + { + if (environ == _environ) + unsetenv("LC_ALL"); + STRING_free(&env_LC_ALL); + } + if (env_LANGUAGE) + { + if (environ == _environ) + unsetenv("LANGUAGE"); + STRING_free(&env_LANGUAGE); + } + + if (!LOCAL_is_UTF8) + STRING_free(&LOCAL_encoding); + + STRING_free(&_lang); + free_local_info(); +} + + +const char *LOCAL_get_lang(void) +{ + char *lang; + + if (!_lang) + { + lang = getenv("LC_ALL"); + if (!lang) + lang = getenv("LANG"); + if (!lang || !*lang) + lang = "en_US"; + _lang = STRING_new_zero(lang); + } + + return _lang; +} + +void LOCAL_set_lang(const char *lang) +{ + char **l; + int rtl; + char *var; + char *err; + + #ifdef DEBUG_LANG + fprintf(stderr, "******** LOCAL_set_lang: %s ********\n", lang); + #endif + + if (lang && *lang) + { + my_setenv("LANG", lang, &env_LANG); + my_setenv("LC_ALL", lang, &env_LC_ALL); + } + + STRING_free(&_lang); + lang = LOCAL_get_lang(); + + if (getenv("LANGUAGE")) + my_setenv("LANGUAGE", lang, &env_LANGUAGE); + + if (setlocale(LC_ALL, "")) + { + _translation_loaded = FALSE; + COMPONENT_translation_must_be_reloaded(); + } + else + { + err = strerror(errno); + ERROR_warning("cannot switch to language '%s': %s. Did you install the corresponding locale packages?", lang ? lang : LOCAL_get_lang(), err); + setlocale(LC_ALL, "C"); + } + + DATE_init_local(); + fill_local_info(); + + /* If language is right to left written */ + + rtl = FALSE; + for (l = _rtl_lang; *l; l++) + { + if (strncmp(*l, lang, 2) == 0) + { + rtl = TRUE; + break; + } + } + + var = getenv("GB_REVERSE"); + if (var && !(var[0] == '0' && var[1] == 0)) + rtl = !rtl; + + HOOK(lang)(lang, rtl); + LOCAL_local.rtl = rtl; +} + +bool LOCAL_format_number(double number, int fmt_type, const char *fmt, int len_fmt, char **str, int *len_str, bool local) +{ + char c; + int n; + + char buf[32]; + char *buf_start; + + int pos; + //int pos2; + int thousand; + int *thousand_ptr; + + char sign; + bool comma; + bool point; + int before, before_zero; + int after, after_zero; + char exposant; + //char exp_sign; + int exp_zero; + + int number_sign; + uint64_t mantisse; + uint64_t power; + double number_mant; + int number_exp; + int number_real_exp; + int ndigit; + int pos_first_digit; + + bool intl_currency; + + if (local) + local_current = &LOCAL_local; + else + local_current = &LOCAL_default; + + switch(fmt_type) + { + case LF_USER: + break; + + case LF_STANDARD: + case LF_GENERAL_NUMBER: + if ((number != 0.0) && ((fabs(number) < 1E-4) || (fabs(number) >= 1E10))) + fmt = "0.##############E+#"; + else + fmt = "0.##############"; + break; + + case LF_SHORT_NUMBER: + if ((number != 0.0) && ((fabs(number) < 1E-4) || (fabs(number) >= 1E10))) + fmt = "0.#######E+#"; + else + fmt = "0.#######"; + break; + + case LF_FIXED: + fmt = "0.00"; + break; + + case LF_PERCENT: + fmt = "###%"; + break; + + case LF_SCIENTIFIC: + fmt = "0.################E+#"; + break; + + case LF_CURRENCY: + fmt = local_current->general_currency; + break; + + case LF_INTERNATIONAL: + fmt = local_current->intl_currency; + break; + + default: + return TRUE; + } + + if (len_fmt == 0) + len_fmt = strlen(fmt); + + if (len_fmt >= COMMON_BUF_MAX) + return TRUE; + + // Remove, not documented! + +#if 0 + /* looking for negative and null numbers formats */ + + pos = search(fmt, len_fmt, ";", 0, FALSE); + + if (number <= 0.0) + { + if (pos < len_fmt) + { + if (number < 0.0) + { + if ((pos < (len_fmt - 1)) && fmt[pos + 1] != ';') + { + fmt = &fmt[pos + 1]; + len_fmt -= pos + 1; + } + else + len_fmt = pos; + } + else + { + pos2 = search(fmt, len_fmt, ";", pos + 1, FALSE); + if (pos2 < len_fmt) + { + if ((pos2 < (len_fmt - 1)) && fmt[pos2 + 1] != ';') + { + fmt = &fmt[pos2 + 1]; + len_fmt -= pos2 + 1; + } + else + len_fmt = pos; + } + } + } + } + else if (pos < len_fmt) + len_fmt = pos; +#endif + + /* translate Gambas format to sprintf format */ + + sign = 0; + comma = FALSE; + before = 0; + before_zero = 0; + point = FALSE; + after = 0; + after_zero = 0; + exposant = 0; + //exp_sign = 0; + exp_zero = 0; + _currency = FALSE; + intl_currency = FALSE; + + begin(); + + /* Looking for '%' */ + + pos = search(fmt, len_fmt, "%", 0, FALSE); + if (pos < len_fmt) + number *= 100; + + /* format prefix */ + + pos = search(fmt, len_fmt, "-+#0.,($", 0, FALSE); + + if (pos >= len_fmt) + return TRUE; + + if (pos > 0) + add_string(fmt, pos, NULL); + + /* specify the sign */ + + if (fmt[pos] == '-') + { + sign = ' '; + pos++; + } + else if (fmt[pos] == '+') + { + sign = '+'; + pos++; + } + else if (fmt[pos] == '(') + { + sign = '('; + pos++; + } + + if (pos >= len_fmt) + return TRUE; + + /* currency */ + + if (fmt[pos] == '$') + { + _currency = TRUE; + pos++; + + if (fmt[pos] == '$') + { + intl_currency = TRUE; + pos++; + } + } + + /* decimal digits */ + + for(; pos < len_fmt; pos++) + { + c = fmt[pos]; + + if (c == ',') + { + comma = TRUE; + continue; + } + + if (c == '#' || c == '0') + { + before++; + if (c == '0' || before_zero > 0) + before_zero++; + continue; + } + + break; + } + + if (pos >= len_fmt) + goto _FORMAT; + + /* the point */ + + if (fmt[pos] != '.') + goto _FORMAT; + + pos++; + point = TRUE; + + if (pos >= len_fmt) + goto _FORMAT; + + /* digits after point */ + + for(; pos < len_fmt; pos++) + { + c = fmt[pos]; + + if (c == '#' || c == '0') + { + after++; + if (c == '0') + after_zero = after; + continue; + } + + break; + } + + if (pos >= len_fmt) + goto _FORMAT; + + /* exponent */ + + if (fmt[pos] == 'e' || fmt[pos] == 'E') + { + exposant = fmt[pos]; + //exp_sign = ' '; + + pos++; + if (pos >= len_fmt) + return TRUE; + + if (fmt[pos] == '-') + { + pos++; + } + else if (fmt[pos] == '+') + { + //exp_sign = '+'; + pos++; + } + + if (pos >= len_fmt) + return TRUE; + + for(; pos < len_fmt; pos++) + { + c = fmt[pos]; + + if (c == '#' || c == '0') + { + if (c == '0' || exp_zero > 0) + exp_zero++; + continue; + } + + break; + } + } + +_FORMAT: + + if (before == 0 && after == 0) + return TRUE; + + /* sign */ + + number_sign = fsgn(number); + + add_sign(sign, number_sign, FALSE); + + /* currency (before) */ + + if (_currency && is_currency_before(number_sign < 0, intl_currency)) + { + add_currency(intl_currency ? local_current->intl_currency_symbol : local_current->currency_symbol); + if (is_currency_space(number_sign < 0, intl_currency)) + put_char(' '); + } + + /* We note where the first digit will be printed */ + + pos_first_digit = buffer_pos; + + /* the number */ + + if (isfinite(number)) + { + number_mant = frexp10(fabs(number), &number_exp); + ndigit = after; + + if (!exposant) + ndigit += number_exp; + else + ndigit++; + + ndigit = MinMax(ndigit, 0, MAX_FLOAT_DIGIT); + //fprintf(stderr, "number_mant = %.24g number_exp = %d ndigit = %d\n", number_mant, number_exp, ndigit); + + power = pow10_uint64_p(ndigit + 1); + + mantisse = number_mant * power; + if ((mantisse % 10) >= 5) + mantisse += 10; + + //fprintf(stderr, "-> power = %" PRId64 " mantisse = %" PRId64 "\n", power, mantisse); + + if (mantisse >= power) + { + ndigit = sprintf(buf, ".%" PRId64, mantisse); + buf[0] = buf[1]; + buf[1] = '.'; + } + else + { + ndigit = sprintf(buf, "0.%" PRId64, mantisse); + } + + ndigit--; + buf[ndigit] = 0; + + /* 0.0 <= number_mant < 1.0 */ + + //number_exp++; /* simplifie les choses */ + + number_real_exp = number_exp; + if (exposant) + number_exp = number != 0.0; + + // should return "0[.]...", or "1[.]..." if the number is rounded up. + + buf_start = buf; + + if (buf_start[0] == '1') // the number has been rounded up. + { + if (exposant) + number_real_exp++; + else + number_exp++; + } + + if (ndigit > 1) // so there is a point + { + if (buf_start[0] == '0') + { + buf_start += 2; + ndigit -= 2; + } + else + { + buf_start[1] = buf_start[0]; + ndigit--; + buf_start++; + } + + while (ndigit > 0 && buf_start[ndigit - 1] == '0') + ndigit--; + } + + /* digits before point */ + + thousand = Max(before, Max(before_zero, number_exp)); + thousand_ptr = comma ? &thousand : NULL; + + if (number_exp > 0) + { + add_char(' ', before - Max(before_zero, number_exp), thousand_ptr); + add_zero(before_zero - number_exp, thousand_ptr); + + add_string(buf_start, Min(number_exp, ndigit), thousand_ptr); + + if (number_exp > ndigit) + add_zero(number_exp - ndigit, thousand_ptr); + } + else + { + add_char(' ', before - before_zero, thousand_ptr); + add_zero(before_zero, thousand_ptr); + } + + /* decimal point */ + + if (point) + put_char(local_current->decimal_point); + + /* digits after the decimal point */ + + if ((ndigit - number_exp) > 0) + { + if (number_exp < 0) + { + n = Min(after, (- number_exp)); + if (n == after) + { + add_zero(after_zero, NULL); + goto _EXPOSANT; + } + else + { + add_zero(n, NULL); + after -= n; + after_zero -= n; + } + } + + if (number_exp > 0) + { + buf_start += number_exp; + ndigit -= number_exp; + } + + n = Min(ndigit, after); + if (n > 0) + { + add_string(buf_start, n, NULL); + after -= n; + after_zero -= n; + } + + if (after_zero > 0) + add_zero(after_zero, NULL); + } + else + add_zero(after_zero, NULL); + + _EXPOSANT: + + /* The decimal point is removed if it is located at the end */ + + buffer_pos--; + if (look_char() != local_current->decimal_point) + buffer_pos++; + + /* exponant */ + + if (exposant != 0) // && number != 0.0) + { + put_char(exposant); + n = snprintf(buf, sizeof(buf), "%+.*d", exp_zero, number_real_exp - 1); + add_string(buf, n, NULL); + } + } + else // isfinite + { + if (isnan(number)) + add_string("NaN", 3, NULL); + else if (isinf(number)) + add_string("Inf", 3, NULL); + } + + /* currency (after) */ + + if (_currency && !is_currency_before(number_sign < 0, intl_currency)) + { + if (is_currency_space(number_sign < 0, intl_currency)) + put_char(' '); + add_currency(intl_currency ? local_current->intl_currency_symbol : local_current->currency_symbol); + } + + /* The last format brace is ignored */ + + if (sign == '(' && fmt[pos] == ')') + pos++; + + /* The sign after */ + + add_sign(sign, number_sign, TRUE); + + /* print at least a zero */ + + if (buffer_pos == pos_first_digit) + put_char('0'); + + /* suffixe de formatage */ + + if (pos < len_fmt) + add_string(&fmt[pos], len_fmt - pos, NULL); + + /* return the result */ + + end(str, len_str); + return FALSE; +} + +static void add_strftime(const char *format, struct tm *tm) +{ + int n; + + n = strftime(get_current(), get_size_left(), format, tm); + buffer_pos += n; +} + + +static void add_number(int value, int pad) +{ + static char temp[8] = { 0 }; + int i, n; + bool minus = FALSE; + + if (value < 0) + { + value = (-value); + minus = TRUE; + } + + n = 0; + for (i = 7; i >= 0; i--) + { + n++; + if (value < 10) + { + temp[i] = value + '0'; + value = 0; + if (n >= pad) + break; + } + else + { + temp[i] = (value % 10) + '0'; + value /= 10; + } + } + + if (minus) + { + i--; + temp[i] = '-'; + n++; + } + + add_string(&temp[i], n, NULL); +} + +static bool add_date_token(DATE_SERIAL *date, char *token, int count) +{ + struct tm tm = {0}; + char buf[8]; + int n; + bool date_token; + + if (*token == 0) + return FALSE; + + date_token = *token == 'd' || *token == 'm' || *token == 'y'; + + if ((date_token && DATE_SERIAL_has_no_date(date))) // || (!date_token && DATE_SERIAL_has_no_time(date))) + { + *token = 0; + return TRUE; + } + + switch (*token) + { + case 'd': + + if (count <= 2) + { + add_number(date->day, (count == 1 ? 0 : 2)); + } + else if (count >= 3) + { + tm.tm_wday = date->weekday; + add_strftime(count == 3 ? "%a" : "%A", &tm); + } + + break; + + case 'm': + + if (count <= 2) + { + add_number(date->month, (count == 1 ? 0 : 2)); + } + else if (count >= 3) + { + tm.tm_mon = date->month - 1; + add_strftime(count == 3 ? "%b" : "%B", &tm); + } + + break; + + case 'y': + + if (count <= 2 && date->year >= 1939 && date->year <= 2038) + add_number(date->year - (date->year >= 2000 ? 2000 : 1900), 2); + else + add_number(date->year, (count == 1 ? 0 : count)); + + break; + + case 'h': + case 'n': + case 's': + + add_number((*token == 'h') ? date->hour : ((*token == 'n') ? date->min : date->sec), (count == 1 ? 0 : 2)); + break; + + case 'u': + + if (date->msec || count == 2) + { + if (count >= 2) + add_number(date->msec, 3); + else + { + n = snprintf(buf, sizeof(buf), "%03d", date->msec); + while (buf[n - 1] == '0') + n--; + buf[n] = 0; + add_string(buf, n, NULL); + } + } + + break; + + case 't': + + if (count <= 2) + { + time_t t = time(NULL); + localtime_r(&t, &tm); + add_strftime(count == 2 ? "%z" : "%Z", &tm); + } + break; + } + + *token = 0; + return FALSE; +} + + +bool LOCAL_format_date(const DATE_SERIAL *date, int fmt_type, const char *fmt, int len_fmt, char **str, int *len_str) +{ + DATE_SERIAL vdate; + char c; + int pos; + int pos_ampm = -1; + struct tm date_tm; + + char token; + int token_count; + + local_current = &LOCAL_local; + vdate = *date; + + switch(fmt_type) + { + case LF_USER: + break; + + case LF_STANDARD: + case LF_GENERAL_DATE: + if (date->year == 0) + { + if (date->hour == 0 && date->min == 0 && date->sec == 0) + { + *str = NULL; + *len_str = 0; + return FALSE; + } + fmt = local_current->long_time; + } + else + fmt = local_current->general_date; + break; + + case LF_LONG_DATE: + fmt = local_current->long_date; + break; + + case LF_MEDIUM_DATE: + fmt = local_current->medium_date; + break; + + case LF_SHORT_DATE: + fmt = local_current->short_date; + break; + + case LF_LONG_TIME: + fmt = local_current->long_time; + break; + + case LF_MEDIUM_TIME: + fmt = local_current->medium_time; + break; + + case LF_SHORT_TIME: + fmt = local_current->short_time; + break; + + default: + return TRUE; + } + + if (len_fmt == 0) + len_fmt = strlen(fmt); + + if (len_fmt >= COMMON_BUF_MAX) + return TRUE; + + /* looking for AM/PM */ + + for (pos = 0; pos < len_fmt - 4; pos++) + { + if (fmt[pos] == '\\') + { + pos++; + continue; + } + + if (strncasecmp(&fmt[pos], "am/pm", 5) == 0) + { + pos_ampm = pos; + if (vdate.hour > 12) + vdate.hour -= 12; + else if (vdate.hour == 0) + vdate.hour = 12; + break; + } + } + + /* Formatting */ + + begin(); + + token = 0; + token_count = 0; + + for (pos = 0; pos < len_fmt; pos++) + { + c = fmt[pos]; + if (c == '\\') + { + pos++; + if (pos >= len_fmt) + break; + add_date_token(&vdate, &token, token_count); + put_char(fmt[pos]); + continue; + } + + if (pos == pos_ampm) + { + add_date_token(&vdate, &token, token_count); + + /* convert to struct tm */ + + date_tm.tm_sec = date->sec; + date_tm.tm_min = date->min; + date_tm.tm_hour = date->hour; + date_tm.tm_mday = 1; + date_tm.tm_mon = 0; + date_tm.tm_year = 0; + + add_strftime((c == 'a' ? "%P" : "%p"), &date_tm); + + pos += 4; + continue; + } + + if (c == 'd' || c == 'm' || c == 'y' || c == 'h' || c == 'n' || c == 's' || c == 'u' || c == 't') + { + if (c != token) + { + add_date_token(&vdate, &token, token_count); + + token = c; + token_count = 0; + } + + token_count++; + } + else + { + if (!add_date_token(&vdate, &token, token_count)) + { + if (c == '/') + add_unicode(local_current->date_sep); + else if (c == ':') + add_unicode(local_current->time_sep); + else + put_char(c); + } + } + } + + add_date_token(&vdate, &token, token_count); + + /* return the result */ + + end(str, len_str); + return FALSE; +} + + +static void LOCAL_load_translation(ARCHIVE *arch) +{ + char *domain = NULL; + char *lang_list; + char *lang; + char *src; + char *test; + char c; + const char *dst = NULL; + FILE *file; + char *addr; + int len; + COMPONENT *save = COMPONENT_current; + + /* We must force GB_LoadFile() to look in our archive, because all translation + files of one language have the same path! + */ + + if (arch) + { + domain = arch->domain; + COMPONENT_current = COMPONENT_find(arch->name); + } + + if (!domain) + domain = "gb"; + + #ifdef DEBUG_LANG + fprintf(stderr, "LOCAL_load_translation: %s / domain = %s\n", arch ? arch->name : "NULL", domain); + #endif + + lang_list = get_languages(); + + lang = strtok(lang_list, ":"); + + for(;;) + { + if (!lang) + break; + + if (*lang) + { + test = STRING_new_temp_zero(lang); + src = test; + + for(;;) + { + c = *src; + if (c == 0 || c == '_') + break; + *src = tolower(c); + src++; + } + + if (!arch) + dst = FILE_cat("...", ".lang", test, NULL); + else + dst = FILE_cat(".lang", test, NULL); + + dst = FILE_set_ext(dst, "mo"); + + #ifdef DEBUG_LANG + fprintf(stderr, "trying %s\n", dst); + #endif + + if (FILE_exist(dst)) + break; + } + + lang = strtok(NULL, ":"); + } + + if (!lang) + { + #ifdef DEBUG_LANG + fprintf(stderr, "No translation\n"); + #endif + goto __NOTRANS; + } + + #ifdef DEBUG_LANG + fprintf(stderr, "Loading %s\n", dst); + #endif + + if (GB_LoadFile(dst, 0, &addr, &len)) + { + #ifdef DEBUG_LANG + fprintf(stderr, "Cannot load %s\n", dst); + #endif + goto __ERROR; + } + + // These temporary files have predictable names because they + // are *.mo files read by the gettext system. + + dst = FILE_cat(FILE_make_temp(NULL, NULL), "tr", NULL); + mkdir(dst, S_IRWXU); + + dst = FILE_cat(FILE_make_temp(NULL, NULL), "tr", lang, NULL); + mkdir(dst, S_IRWXU); + + dst = FILE_cat(dst, "LC_MESSAGES", NULL); + mkdir(dst, S_IRWXU); + + dst = FILE_cat(dst, domain, NULL); + strcat((char *)dst, ".mo"); + + unlink(dst); + + #ifdef DEBUG_LANG + fprintf(stderr, "Writing to %s (%d bytes)\n", dst, len); + #endif + + // No need to test previous system calls as the failure will be detected now + + file = fopen(dst, "w"); + if (file) + { + fwrite(addr, len, 1, file); + fclose(file); + } + + GB_ReleaseFile(addr, len); + +__ERROR: + + // If the *.mo was not copied, then the following functions will failed + + #ifdef DEBUG_LANG + + fprintf(stderr, "bindtextdomain: %s\n", bindtextdomain(domain, FILE_cat(FILE_make_temp(NULL, NULL), "tr", NULL))); + fprintf(stderr, "bind_textdomain_codeset: %s\n", bind_textdomain_codeset(domain, "UTF-8")); + if (!arch) + fprintf(stderr, "textdomain: %s\n", textdomain(domain)); + + #else + + bindtextdomain(domain, FILE_cat(FILE_make_temp(NULL, NULL), "tr", NULL)); + #ifdef OS_SOLARIS + fprintf(stderr, "Warning: bind_textdomain_codeset() unavailable.\n"); + #else + bind_textdomain_codeset(domain, "UTF-8"); + #endif + if (!arch) + textdomain(domain); /* default domain */ + + #endif + +__NOTRANS: + + STRING_free(&lang_list); + + if (arch) + arch->translation_loaded = TRUE; + else + _translation_loaded = TRUE; + + COMPONENT_current = save; +} + + +const char *LOCAL_gettext(const char *msgid) +{ + const char *tr = msgid; + ARCHIVE *arch = NULL; + + /* + If LOCAL_gettext() is called, then we are in the context of + the archive, so the translation loaded will be the good one. + */ + + if (!msgid) + return ""; + + if (!ARCHIVE_get_current(&arch)) + { + #ifdef DEBUG_LANG + fprintf(stderr, "dgettext(\"%s\", \"%s\")\n", arch->domain, msgid); + #endif + if (!arch->translation_loaded) + LOCAL_load_translation(arch); + tr = dgettext(arch->domain, msgid); + } + + if (tr == msgid) + { + #ifdef DEBUG_LANG + fprintf(stderr, "dgettext(\"%s\", \"%s\")\n", "gb", msgid); + #endif + if (!_translation_loaded) + LOCAL_load_translation(NULL); + tr = dgettext("gb", msgid); + //tr = gettext(msgid); + } + + /*printf("tr: %s -> %s\n", msgid, tr);*/ + + if (!tr || tr[0] == 0 || (tr[0] == '-' && (tr[1] == 0 || (tr[1] == '\n' && tr[2] == 0)))) + tr = msgid; + + #ifdef DEBUG_LANG + fprintf(stderr, "--> \"%s\"\n", tr); + #endif + + return tr; +} + +int LOCAL_get_first_day_of_week() +{ + const char *lang; + + if (LOCAL_first_day_of_week >= 0) + return LOCAL_first_day_of_week; + + lang = LOCAL_get_lang(); + + if (strcmp(lang, "en") == 0 || strncmp(lang, "en_", 3) == 0 || strcmp(lang, "C") == 0) + return 0; + else + return 1; +} + +void LOCAL_set_first_day_of_week(char day) +{ + if (day >= -1 && day <= 6) + LOCAL_first_day_of_week = day; +} + diff --git a/main/gbx/gbx_local.h b/main/gbx/gbx_local.h new file mode 100755 index 00000000..0e913bc6 --- /dev/null +++ b/main/gbx/gbx_local.h @@ -0,0 +1,123 @@ +/*************************************************************************** + + gbx_local.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_LOCAL_H +#define __GBX_LOCAL_H + +#ifndef GBX_INFO + +#include +#include "gbx_date.h" +#include "gbx_archive.h" + +#endif + +enum { + LF_USER, + LF_STANDARD, + LF_GENERAL_NUMBER, + LF_SHORT_NUMBER, + LF_FIXED, + LF_PERCENT, + LF_SCIENTIFIC, + LF_CURRENCY, + LF_INTERNATIONAL, + LF_GENERAL_DATE, + LF_LONG_DATE, + LF_MEDIUM_DATE, + LF_SHORT_DATE, + LF_LONG_TIME, + LF_MEDIUM_TIME, + LF_SHORT_TIME, + LF_MAX + }; + +#ifndef GBX_INFO + +enum { + LO_HOUR = 0, + LO_MINUTE = 1, + LO_SECOND = 2, + LO_YEAR = 0, + LO_MONTH = 1, + LO_DAY = 2 + }; + +typedef + struct { + char decimal_point; + char currency_decimal_point; + const char *thousand_sep; + int len_thousand_sep; + const char *currency_thousand_sep; + int len_currency_thousand_sep; + char group_size; + char currency_group_size; + unsigned char currency_flag; + uint date_sep; + uint time_sep; + const char *currency_symbol; + const char *intl_currency_symbol; + char date_order[4]; + char time_order[4]; + char long_date[20]; + char medium_date[12]; + char short_date[12]; + char long_time[12]; + char medium_time[12]; + char short_time[8]; + char general_date[20]; + char general_currency[20]; + char intl_currency[20]; + char *true_str; + int len_true_str; + char *false_str; + int len_false_str; + bool rtl; + } + LOCAL_INFO; + +#ifndef __GBX_LOCAL_C +EXTERN LOCAL_INFO LOCAL_default, LOCAL_local; +EXTERN char *LOCAL_encoding; +EXTERN bool LOCAL_is_UTF8; +EXTERN char LOCAL_first_day_of_week; +#endif + + +void LOCAL_init(void); +void LOCAL_exit(void); +bool LOCAL_format_number(double number, int fmt_type, const char *fmt, int len_fmt, char **str, int *len_str, bool local); +bool LOCAL_format_date(const DATE_SERIAL *date, int fmt_type, const char *fmt, int len_fmt, char **str, int *len_str); +const char *LOCAL_get_lang(void); +void LOCAL_set_lang(const char *lang); +const char *LOCAL_gettext(const char *msgid); +//void LOCAL_load_translation(ARCHIVE *arch); +int LOCAL_get_first_day_of_week(); +void LOCAL_set_first_day_of_week(char day); + +#define LOCAL_get(_local) ((_local) ? &LOCAL_local : &LOCAL_default) + +#endif + +#endif diff --git a/main/gbx/gbx_math.c b/main/gbx/gbx_math.c new file mode 100644 index 00000000..58b98024 --- /dev/null +++ b/main/gbx/gbx_math.c @@ -0,0 +1,242 @@ +/*************************************************************************** + + gbx_math.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_MATH_C + +#include "gb_common.h" + +#include +#include +#include + +#include "gb_hash.h" +#include "gbx_math.h" + +const double MATH_pow10_double[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; +static const uint _pow10_uint[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; + +/* This is a twisted generalized feedback shift register + that generates pseudo-random numbers. + + Source code from the paper of Yann Guidon + in March edition of GNU/Linux Magazine France +*/ + +#define CRC32_STD 0x04C11DB7 +#define GFSR_SIZE 15 + +static uint GFSR_table[GFSR_SIZE]; +static uint GFSR_temp; +static uint GFSR_index; + +static void GFSR_init(uint seed) +{ + int i = 0, j; + uint t = seed; + + if (t == 0) + t = (uint)-1; + + do + { + t ^= (t >> 5) ^ (t << 1); + j = t >> 31; + t <<= 1; + if (j) + t ^= CRC32_STD; + GFSR_table[i++] = t; + } + while (i < GFSR_SIZE); + + GFSR_temp = GFSR_table[GFSR_SIZE - 1]; + GFSR_index = 0; +} + +static uint GFSR_random(void) +{ + int t; + + GFSR_temp ^= GFSR_table[GFSR_index]; + + t = GFSR_temp; + GFSR_temp <<= 1; + if (t < 0) + GFSR_temp ^= CRC32_STD; + + GFSR_table[GFSR_index] = GFSR_temp; + + if (++GFSR_index >= GFSR_SIZE) + GFSR_index = 0; + + return GFSR_temp; +} + +double frac(double x) +{ + x = fabs(x); + return x - floor(x); +} + +int lsgn(int x) +{ + return ((x > 0) ? 1 : ((x < 0) ? (-1) : 0)); +} + +int llsgn(int64_t x) +{ + return ((x > 0) ? 1 : ((x < 0) ? (-1) : 0)); +} + +/*int64_t llabs(int64_t x) +{ + return ((x < 0) ? (-x) : x); +}*/ + +int fsgn(double x) +{ + return ((x > 0) ? 1 : ((x < 0) ? (-1) : 0)); +} + +float fixf(float x) +{ + if (x >= 0) + return floorf(x); + else + return -floorf(fabsf(x)); +} + +double fix(double x) +{ + if (x >= 0) + return floor(x); + else + return -floor(fabs(x)); +} + +double frexp10(double x, int *exp) +{ + int p; + + if (x == 0.0) + { + *exp = 0; + return x; + } + + p = (int)log10(x); + x /= pow10(p); + + if (x >= 1) + { + x /= 10; + p++; + } + + *exp = p; + return x; +} + + +void randomize(bool set, uint seed) +{ + struct timeval tv; + + if (!set && gettimeofday(&tv, NULL) == 0) + seed = 0xD1C2B3A4 + (tv.tv_sec << 20) + tv.tv_usec; + + GFSR_init(seed); +} + + +double rnd(void) +{ + /*seed = 16807L * (seed % 127773L) - 2836L * (seed / 127773L); + if (seed <= 0) seed += 2147483647; + + return (double)seed / 2147483648.0;*/ + + uint64_t val; + + val = GFSR_random(); + val <<= 32; + val |= GFSR_random(); + + return (double)val / 18446744073709551616.0; //0xFFFFFFFFFFFFFFFFULL; +} + +#ifndef HAVE_EXP10 +double exp10(double x) +{ + return pow(10, x); +} +#endif + +#ifndef HAVE_LOG2 +double log2(double x) +{ + return log(x) / M_LN2; +} +#endif + +#ifndef HAVE_EXP2 +double exp2(double x) +{ + return pow(2, x); +} +#endif + +static int nbits(uint n) +{ + uint c; + for (c = 0; n; c++) + n &= n - 1; + return c; +} + +void MATH_init(void) +{ + uint seed; + + randomize(FALSE, 0); + + // Internet condom + do + seed = GFSR_random(); + while ((seed & 1) == 0 || nbits(seed) < 16); + + HASH_seed = seed; +} + +uint64_t pow10_uint64_p(int n) +{ + uint64_t v = 1; + + while (n > 8) + { + v *= 100000000; + n -= 8; + } + v *= _pow10_uint[n]; + return v; +} + diff --git a/main/gbx/gbx_math.h b/main/gbx/gbx_math.h new file mode 100644 index 00000000..a0111485 --- /dev/null +++ b/main/gbx/gbx_math.h @@ -0,0 +1,72 @@ +/*************************************************************************** + + gbx_math.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_MATH_H +#define __GBX_MATH_H + +#include "config.h" + +#ifdef OS_BSD +#undef HAVE_EXP10 +#endif + +#ifndef __GBX_MATH_C +extern const double MATH_pow10_double[]; +#endif + +void MATH_init(void); + +int lsgn(int x); +int llsgn(int64_t x); +//int64_t llabs(int64_t x); + +double frac(double x); +int fsgn(double x); +float fixf(float x); +double fix(double x); +double frexp10(double x, int *exp); + +#define pow10(_n) (((_n) >= 0 && (_n) <= 9) ? MATH_pow10_double[_n] : (((_n) < 0 && (_n) >= -9) ? (1.0 / MATH_pow10_double[-(_n)]) : exp10(_n))) +//#define mulpow10(_v, _n) (((_n) >= 0 && (_n) <= 9) ? ((_v) * MATH_pow10_double[_n]) : (((_n) < 0 && (_n) >= -9) ? ((_v) / MATH_pow10[-(_n)]) : ((_v) * exp10(_n)))) + +uint64_t pow10_uint64_p(int n); + +void randomize(bool set, uint seed); +double rnd(void); + +#define deg(_x) ((_x) * 180 / M_PI) +#define rad(_x) ((_x) * M_PI / 180) + +#ifndef HAVE_EXP10 +double exp10(double x); +#endif + +#ifndef HAVE_LOG2 +double log2(double x); +#endif + +#ifndef HAVE_EXP2 +double exp2(double x); +#endif + +#endif diff --git a/main/gbx/gbx_number.c b/main/gbx/gbx_number.c new file mode 100644 index 00000000..79d44cf2 --- /dev/null +++ b/main/gbx/gbx_number.c @@ -0,0 +1,543 @@ +/*************************************************************************** + + gbx_number.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_NUMBER_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_limit.h" + +#include +#include +#include + +#include "gbx_type.h" +#include "gb_common_buffer.h" +#include "gbx_local.h" +#include "gbx_math.h" +#include "gbx_string.h" +#include "gbx_number.h" + +#define buffer_init COMMON_buffer_init +#define get_char COMMON_get_char +#define last_char COMMON_last_char +#define look_char COMMON_look_char +#define put_char COMMON_put_char +#define jump_space COMMON_jump_space +#define get_current COMMON_get_current +#define buffer_pos COMMON_pos +#define get_size_left COMMON_get_size_left +#define has_string COMMON_has_string + +#define IS_PURE_INTEGER(_int64_val) ((_int64_val) == ((int)(_int64_val))) + +static bool read_integer(int base, bool minus, int64_t *result, bool local) +{ + uint64_t nbr2, nbr; + int d, n, c, nmax; + const char *thsep; + int lthsep; + int ndigit_thsep; + bool first_thsep; + + thsep = LOCAL_get(local)->thousand_sep; + lthsep = LOCAL_get(local)->len_thousand_sep; + ndigit_thsep = 0; + first_thsep = FALSE; + + n = 0; + nbr = 0; + + switch (base) + { + case 2: nmax = 64; break; + case 8: nmax = 21; break; + case 16: nmax = 16; break; + case 10: default: nmax = 19; break; + } + + c = last_char(); + + if (base == 10) + { + for(;;) + { + if (local) + { + COMMON_pos--; + + if (has_string(thsep, lthsep) && (ndigit_thsep == 3 || (!first_thsep && ndigit_thsep >= 1 && ndigit_thsep <= 3))) + { + COMMON_pos += lthsep; + c = get_char(); + first_thsep = TRUE; + ndigit_thsep = 0; + } + else + COMMON_pos++; + } + + if (c >= '0' && c <= '9') + { + d = c - '0'; + if (local) + ndigit_thsep++; + } + else + break; + + n++; + if (n < nmax) + { + nbr = nbr * 10 + d; + } + else + { + nbr2 = nbr * 10 + d; + + if ((nbr2 / base) != nbr || nbr2 > ((uint64_t)LLONG_MAX + minus)) + return TRUE; + + nbr = nbr2; + } + + c = get_char(); + if (c < 0) + break; + } + + c = last_char(); + + if (local && first_thsep && ndigit_thsep != 3) + return TRUE; + } + else + { + for(;;) + { + if (c >= '0' && c <= '9') + d = c - '0'; + else if (c >= 'A' && c <='Z') + d = c - 'A' + 10; + else if (c >= 'a' && c <='z') + d = c - 'a' + 10; + else + break; + + if (d >= base) + break; + + n++; + if (n > nmax) + return TRUE; + + nbr = nbr * base + d; + + c = get_char(); + if (c < 0) + break; + } + + c = last_char(); + + if ((c == '&' || c == 'u' || c == 'U') && base != 10) + c = get_char(); + else + { + if ((base == 16 && n == 4) || (base == 2 && n == 16)) + { + if (nbr >= 0x8000L && nbr <= 0xFFFFL) + nbr |= INT64_C(0xFFFFFFFFFFFF0000); + } + else if ((base == 16 && n == 8) || (base == 2 && n == 32)) + { + if (nbr >= 0x80000000L && nbr <= 0xFFFFFFFFL) + nbr |= INT64_C(0xFFFFFFFF00000000); + } + } + } + + if (c > 0 && !isspace(c)) + return TRUE; + + if (n == 0) + return TRUE; + + *((int64_t *)result) = nbr; + return FALSE; +} + + +static bool read_float(double *result, bool local) +{ + LOCAL_INFO *local_info; + char point; + const char *thsep; + int lthsep; + int ndigit_thsep; + bool first_thsep; + int c, n; + + uint64_t mantisse, mantisse_int; + int ndigit_frac; + bool frac; + bool frac_null; + bool nozero; + + int nexp; + bool nexp_minus; + + local_info = LOCAL_get(local); + point = local_info->decimal_point; + thsep = local_info->thousand_sep; + lthsep = local_info->len_thousand_sep; + ndigit_thsep = 0; + first_thsep = FALSE; + + c = last_char(); + + /* Integer and decimal part */ + + n = 0; + mantisse = 0; + mantisse_int = 0; + frac = FALSE; + frac_null = TRUE; + ndigit_frac = 0; + nexp = 0; + nexp_minus = FALSE; + nozero = FALSE; + + for(;;) + { + if (c == point) + { + if (frac) + break; + c = get_char(); + frac = TRUE; + mantisse_int = mantisse; + } + + if (local && !frac) + { + COMMON_pos--; + + if (has_string(thsep, lthsep) && (ndigit_thsep == 3 || (!first_thsep && ndigit_thsep >= 1 && ndigit_thsep <= 3))) + { + COMMON_pos += lthsep; + first_thsep = TRUE; + ndigit_thsep = 0; + c = get_char(); + } + else + COMMON_pos++; + } + + if (!isdigit(c) || (c < 0)) + break; + + if (c != '0') + nozero = TRUE; + + if (nozero) + n++; + + if (n > MAX_FLOAT_DIGIT) + { + if (n == (MAX_FLOAT_DIGIT + 1) && (c >= '5')) + mantisse++; + if (!frac) + ndigit_frac--; + c = get_char(); + continue; + } + + if (c == '0') + mantisse *= 10; + else + { + if (frac) + frac_null = FALSE; + mantisse = mantisse * 10 + (c - '0'); + } + + if (frac) + ndigit_frac++; + else if (local) + ndigit_thsep++; + + c = get_char(); + + if (c == 'e' || c == 'E') + break; + + if (c < 0) + goto __END; + } + + /* Exponent */ + + if (c == 'e' || c == 'E') + { + c = get_char(); + + if (c == '+' || c == '-') + { + if (c == '-') + nexp_minus = TRUE; + + c = get_char(); + } + + if (!isdigit(c) || (c < 0)) + return TRUE; + + for(;;) + { + nexp = nexp * 10 + (c - '0'); + if (nexp > DBL_MAX_10_EXP) + return TRUE; + + c = get_char(); + if (!isdigit(c) || (c < 0)) + break; + } + + if (nexp_minus) + nexp = (-nexp); + } + + if (c >= 0 && !isspace(c)) + return TRUE; + +__END: + + if (local && first_thsep && ndigit_thsep != 3) + return TRUE; + + if (frac && frac_null) + mantisse = mantisse_int; + else + nexp -= ndigit_frac; + + //fprintf(stderr, "%.24g %d\n", (double)mantisse, nexp); + //*result = mulpow10((double)mantisse, nexp); + *result = (double)mantisse * pow10(nexp); + + return FALSE; +} + + +bool NUMBER_from_string(int option, const char *str, int len, VALUE *value) +{ + int c; + int64_t val = 0; + double dval = 0.0; + TYPE type; + int base = 10; + bool minus = FALSE; + int pos; + + buffer_init(str, len); + + jump_space(); + + c = get_char(); + + if (c == '+' || c == '-') + { + minus = (c == '-'); + c = get_char(); + } + + if (option & NB_READ_INT_LONG) + { + if (option & NB_READ_HEX_BIN) + { + if (c == '&') + { + c = get_char(); + + if (c == 'H' || c == 'h') + { + base = 16; + c = get_char(); + } + else if (c == 'O' || c == 'o') + { + base = 8; + c = get_char(); + } + else if (c == 'X' || c == 'x') + { + base = 2; + c = get_char(); + } + else + base = 16; + } + else if (c == '%') + { + base = 2; + c = get_char(); + } + } + } + + if (c < 0) + return TRUE; + + if (c == '-' || c == '+') + return TRUE; + + errno = 0; + pos = COMMON_pos - 1; + + if (!read_integer(base, minus, &val, (option & NB_LOCAL) != 0)) + { + if (minus) val = (-val); + + if ((option & NB_READ_INTEGER) && IS_PURE_INTEGER(val)) + { + type = T_INTEGER; + goto __END; + } + else if ((option & NB_READ_LONG)) + { + type = T_LONG; + goto __END; + } + else if ((option & NB_READ_FLOAT) && base == 10) + { + type = T_FLOAT; + dval = (double)val; + goto __END; + } + } + + if ((option & NB_READ_FLOAT) && base == 10) + { + COMMON_pos = pos; + get_char(); + if (!read_float(&dval, (option & NB_LOCAL) != 0)) + { + if (minus) dval = (-dval); + type = T_FLOAT; + goto __END; + } + } + + return TRUE; + +__END: + + if (last_char() >= 0) //(c >= 0 && !isspace(c)) + return TRUE; + + value->type = type; + + if (type == T_INTEGER) + value->_integer.value = val; + else if (type == T_LONG) + value->_long.value = val; + else + value->_float.value = dval; + + //fprintf(stderr, "return FALSE\n"); + return FALSE; +} + + +void NUMBER_int_to_string(uint64_t nbr, int prec, int base, VALUE *value) +{ + char *ptr; + char *src; + int digit, len; + bool neg; + + //if (prec < 0) + // ERROR_panic("NUMBER_int_to_string: prec < 0"); + + len = 0; + ptr = &COMMON_buffer[COMMON_BUF_MAX]; + + if (nbr == 0 && prec == 0) + { + STRING_char_value(value, '0'); + return; + } + + neg = (nbr & (1LL << 63)) != 0; + + if (base == 10 && neg) + nbr = 1 + ~nbr; + + while (nbr > 0) + { + digit = nbr % base; + nbr /= base; + + ptr--; + len++; + + if (digit < 10) + *ptr = '0' + digit; + else + *ptr = 'A' + digit - 10; + } + + if (neg) + { + if (prec) + { + ptr += len - prec; + len = prec; + } + + if (base == 10) + { + len++; + ptr--; + *ptr = '-'; + } + + STRING_new_temp_value(value, NULL, len); + src = value->_string.addr; + + memcpy(src, ptr, len); + } + else + { + STRING_new_temp_value(value, NULL, Max(len, prec)); + src = value->_string.addr; + + while (prec > len) + { + *src++ = '0'; + prec--; + } + + memcpy(src, ptr, len); + } +} diff --git a/main/gbx/gbx_number.h b/main/gbx/gbx_number.h new file mode 100644 index 00000000..94424576 --- /dev/null +++ b/main/gbx/gbx_number.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + gbx_number.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_NUMBER_H +#define __GBX_NUMBER_H + +#include "gbx_type.h" +#include "gbx_value.h" + +enum { + NB_READ_NOTHING = 0, + NB_READ_INTEGER = 1, + NB_READ_LONG = 2, + NB_READ_INT_LONG = 3, + NB_READ_FLOAT = 4, + NB_READ_ALL = 7, + NB_READ_HEX_BIN = 8, + NB_LOCAL = 16 + }; + + +bool NUMBER_from_string(int option, const char *str, int len, VALUE *value); +void NUMBER_int_to_string(uint64_t nbr, int prec, int base, VALUE *value); + +#endif /* */ diff --git a/main/gbx/gbx_object.c b/main/gbx/gbx_object.c new file mode 100644 index 00000000..1c6afddf --- /dev/null +++ b/main/gbx/gbx_object.c @@ -0,0 +1,517 @@ +/*************************************************************************** + + gbx_object.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __OBJECT_C + +#include "gb_common.h" +#include "gb_alloc.h" +#include "gb_list.h" +#include "gbx_class.h" +#include "gbx_event.h" +#include "gbx_exec.h" +#include "gbx_compare.h" +#include "gbx_c_observer.h" +#include "gbx_c_array.h" +#include "gbx_struct.h" +#include "gbx_object.h" + +#if DEBUG_REF +const char *OBJECT_ref_where = 0; +#endif + +void **OBJECT_set_pointer = NULL; + +static OBJECT *_event_object_list = NULL; + +#if DEBUG_REF +char *OBJECT_where_am_i(const char *file, int line, const char *func) +{ + static char buffer[256]; + + snprintf(buffer, sizeof(buffer), "[%s] %s:%d", file, func, line); + return buffer; +} +#endif + +void *OBJECT_new(CLASS *class, const char *name, OBJECT *parent) +{ + OBJECT *object; + + ALLOC_ZERO(&object, class->size); + + object->class = class; + #if DEBUG_REF + object->ref = 0; + OBJECT_REF(object); + #else + object->ref = 1; + #endif + + class->count++; + + OBJECT_attach(object, parent, name); + + return object; +} + + +#if 0 +static void dump_attach(char *title) +{ + void *ob; + + fprintf(stderr, ">>>> %s: ", title); + for (ob = _event_object_list; ob; ob = OBJECT_event(ob)->next) + fprintf(stderr, "%p -> ", ob); + fprintf(stderr, "(nil)\n"); + +} +#endif + +static void call_attach_special_method(CLASS *class, void *ob, void *parent, const char *name) +{ + STACK_check(2); + + SP->_object.class = OBJECT_class(parent); + SP->_object.object = parent; + PUSH(); + + if (name) + { + SP->type = T_CSTRING; + SP->_string.addr = (char *)name; + SP->_string.start = 0; + SP->_string.len = strlen(name); + } + else + VALUE_null(SP); + + SP++; + + EXEC_special(SPEC_ATTACH, class, ob, 2, TRUE); +} + +static void insert_object(OBJECT *ob, OBJECT_EVENT *ev) +{ + ev->next = _event_object_list; + ev->prev = NULL; + + if (_event_object_list) + OBJECT_event(_event_object_list)->prev = ob; + + _event_object_list = ob; +} + +static void remove_object(OBJECT *ob, OBJECT_EVENT *ev) +{ + if (ev->prev) + OBJECT_event(ev->prev)->next = ev->next; + + if (ev->next) + OBJECT_event(ev->next)->prev = ev->prev; + + if (ob == _event_object_list) + _event_object_list = ev->next; + + ev->prev = NULL; + ev->next = NULL; +} + +void OBJECT_detach(OBJECT *ob) +{ + CLASS *class = OBJECT_class(ob); + OBJECT *parent; + OBJECT_EVENT *ev; + + if (!class->is_observer && class->n_event == 0) + return; + + ev = (OBJECT_EVENT *)((char *)ob + class->off_event); + + //if (!ev->parent) + // return; + + // Do not free the observers there + + remove_object(ob, ev); + + //dump_attach("OBJECT_detach"); + + /* Avoids an infinite recursion, if freeing the parent implies freeing the object */ + parent = ev->parent; + + if (parent) + { + ev->parent = NULL; + + if (class->special[SPEC_ATTACH] != NO_SYMBOL) + call_attach_special_method(class, ob, parent, NULL); + + #if DEBUG_EVENT || DEBUG_REF + fprintf(stderr, "OBJECT_detach : Detach (%s %p) from (%s %p)\n", + ob->class->name, ob, parent->class->name, parent); + #endif + OBJECT_UNREF(parent); + } +} + +static void remove_observers(OBJECT *ob) +{ + CLASS *class = OBJECT_class(ob); + OBJECT_EVENT *ev; + COBSERVER *obs, *next; + + //fprintf(stderr, "Remove observers: %s %p\n", class->name, ob); + + if (!class->is_observer && class->n_event == 0) + return; + + ev = (OBJECT_EVENT *)((char *)ob + class->off_event); + obs = ev->observer; + ev->observer = NULL; + + while (obs) + { + next = obs->list.next; + #if DEBUG_EVENT + fprintf(stderr, "Remove observer %p %d: %p: %p\n", obs, (int)obs->ob.ref, ob, obs->object); + #endif + obs->object = NULL; + OBJECT_UNREF(obs); + obs = next; + } + + //ev->observer = NULL; +} + +void OBJECT_attach(OBJECT *ob, OBJECT *parent, const char *name) +{ + CLASS *class = OBJECT_class(ob); + OBJECT_EVENT *ev; + + if (!name) + return; + + if (!class->is_observer && class->n_event == 0) + return; + + OBJECT_detach(ob); + + ev = (OBJECT_EVENT *)((char *)ob + class->off_event); + ev->parent = parent; + + #if DEBUG_EVENT || DEBUG_REF + fprintf(stderr, "OBJECT_attach : Attach (%s %p) to (%s %p) as %s\n", + ob->class->name, ob, parent->class->name, parent, name); + #endif + + OBJECT_REF(parent); + + EVENT_search(class, ev->event, name, parent); + + insert_object(ob, ev); + + if (class->special[SPEC_ATTACH] != NO_SYMBOL) + call_attach_special_method(class, ob, parent, name); + + //dump_attach("OBJECT_attach"); +} + + +bool OBJECT_comp_value(VALUE *ob1, VALUE *ob2) +{ + if (ob1->type == T_NULL && ob2->type == T_NULL) + return FALSE; + else if (ob1->type == T_NULL) + return ob2->_object.object != NULL; + else if (ob2->type == T_NULL) + return ob1->_object.object != NULL; + else + return COMPARE_object(&ob1->_object.object, &ob2->_object.object); +} + +void OBJECT_release_static(CLASS *class, CLASS_VAR *var, int nelt, char *data) +{ + static void *jump[17] = { + &&__NEXT, &&__NEXT, &&__NEXT, &&__NEXT, &&__NEXT, &&__NEXT, &&__NEXT, &&__NEXT, &&__NEXT, + &&__STRING, &&__NEXT, &&__NEXT, &&__VARIANT, &&__ARRAY, &&__STRUCT, &&__NEXT, &&__OBJECT + }; + + CTYPE type; + + while (nelt--) + { +#if TRACE_MEMORY + if (var->type.id == T_STRING || var->type.id == T_OBJECT) + fprintf(stderr, "release_static: %s [%d] trying %p\n", class->name, i, (*(void **)&data[var->pos])); +#endif + + type = var->type; + goto *jump[type.id]; + + __STRING: + STRING_unref((char **)&data[var->pos]); + goto __NEXT; + + __OBJECT: + OBJECT_UNREF(*((void **)&data[var->pos])); + goto __NEXT; + + __VARIANT: + VARIANT_free((VARIANT *)&data[var->pos]); + goto __NEXT; + + __ARRAY: + CARRAY_release_static(class, class->load->array[type.value], &data[var->pos]); + goto __NEXT; + + __STRUCT: + { + CLASS *sclass = class->load->class_ref[type.value]; + OBJECT_release_static(sclass, sclass->load->dyn, sclass->load->n_dyn, &data[var->pos]); + } + + __NEXT: + var++; + } +} + +static void release(CLASS *class, OBJECT *ob) +{ + CLASS_VAR *var; + int nelt; + char *data; + + if (class->parent != NULL && ob) + release(class->parent, ob); + + if (CLASS_is_native(class)) + return; + + if (ob == NULL) + { + var = class->load->stat; + nelt = class->load->n_stat; + data = class->stat; + } + else + { + if (CLASS_is_struct(class)) + { + if (((CSTRUCT *)ob)->ref) + { + CSTRUCT_release((CSTRUCT *)ob); + return; + } + data = (char *)ob + sizeof(CSTRUCT); + } + else + data = (char *)ob; + + var = class->load->dyn; + nelt = class->load->n_dyn; + } + + OBJECT_release_static(class, var, nelt, data); +} + + +void OBJECT_release(CLASS *class, OBJECT *ob) +{ +#if TRACE_MEMORY + printf("> OBJECT_release %s %p\n", class->name, ob); +#endif + + if (ob) + { + ob->ref = 1; // Prevents anybody from freeing the object! + OBJECT_detach(ob); + remove_observers(ob); + ob->ref = 0; + } + + release(class, ob); + + if (ob) + { + class->count--; + + #if DEBUG_REF + ob->class = FREE_MARK; + #endif + + IFREE(ob); + } + +#if TRACE_MEMORY + printf("< OBJECT_release %s %p\n", class->name, ob); +#endif +} + + +void OBJECT_exit(void) +{ + #if DEBUG_LOAD + fprintf(stderr, "------------ OBJECT_exit - BEGIN---------\n"); + #endif + while (_event_object_list) + OBJECT_detach(_event_object_list); + #if DEBUG_LOAD + fprintf(stderr, "------------ OBJECT_exit - END ----------\n"); + #endif +} + +static void error_OBJECT_create(const char *name, void *object) +{ + OBJECT_UNREF_KEEP(object); + EVENT_leave_name(name); +} + +void *OBJECT_create(CLASS *class, const char *name, void *parent, int nparam) +{ + void *object; + const char *save; + + // The "no create" flag only concerns users of NEW + //if (class->no_create) + // THROW(E_CSTATIC, CLASS_get_name(class)); + + ON_ERROR_2(error_OBJECT_create, save = EVENT_enter_name(name), object = OBJECT_new(class, name, parent)) + { + if (OBJECT_set_pointer) + { + *OBJECT_set_pointer = object; + OBJECT_ref(object); + OBJECT_set_pointer = NULL; + } + + OBJECT_lock(object, TRUE); + EXEC_special_inheritance(SPEC_NEW, class, object, nparam, TRUE); + OBJECT_lock(object, FALSE); + + EXEC_special(SPEC_READY, class, object, 0, TRUE); + + error_OBJECT_create(save, object); + } + END_ERROR + + return object; +} + + +/* FIXME: The _new methods are called differently from EXEC_special_inheritance */ + +void *OBJECT_create_native(CLASS *class, VALUE *param) +{ + CLASS_DESC *desc; + short index; + void *object; + + object = OBJECT_new(class, NULL, NULL); + + for(;;) + { + index = class->special[SPEC_NEW]; + if (index != NO_SYMBOL) + { + desc = CLASS_get_desc(class, index); + EXEC_call_native(desc->method.exec, object, desc->method.type, param); + } + class = class->parent; + if (!class) + break; + } + + EXEC_special(SPEC_READY, OBJECT_class(object), object, 0, TRUE); + OBJECT_UNREF_KEEP(object); + return object; +} + +void OBJECT_lock(OBJECT *object, bool lock) +{ + CLASS *class; + OBJECT_EVENT *ev; + + if (!object) + return; + + class = object->class; + + if (class->is_observer) + { + COBSERVER_lock((COBSERVER *)object, lock); + return; + } + + if (class->n_event == 0) + return; + + ev = (OBJECT_EVENT *)((char *)object + class->off_event); + + if (lock) + ev->locked++; + else + ev->locked--; +} + + +bool OBJECT_is_locked(OBJECT *object) +{ + CLASS *class; + + if (!object) + return FALSE; + + class = object->class; + + if (class->is_observer) + return COBSERVER_is_locked((COBSERVER *)object); + + if (class->n_event == 0) + return FALSE; + + return OBJECT_event(object)->locked > 0; +} + + +OBJECT *OBJECT_parent(void *object) +{ + CLASS *class = OBJECT_class(object); + + if (!class->is_observer && class->n_event == 0) + return NULL; + + //return ((OBJECT *)((intptr_t)OBJECT_event(object)->parent & ~1)); + return OBJECT_event(object)->parent; +} + + +OBJECT *OBJECT_active_parent(void *object) +{ + OBJECT *parent = OBJECT_parent(object); + + if (!parent || OBJECT_is_locked((OBJECT *)object) || OBJECT_is_locked(parent)) + return NULL; + + return parent; +} diff --git a/main/gbx/gbx_object.h b/main/gbx/gbx_object.h new file mode 100644 index 00000000..0911987d --- /dev/null +++ b/main/gbx/gbx_object.h @@ -0,0 +1,234 @@ +/*************************************************************************** + + gbx_object.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __OBJECT_H +#define __OBJECT_H + +#include "gbx_debug.h" +#include "gbx_value.h" +#include "gbx_class.h" + +#ifndef __OBJECT_C +EXTERN void **OBJECT_set_pointer; +#endif + +typedef + struct { + CLASS *class; + intptr_t ref; + } + OBJECT; + +typedef + struct { + OBJECT *parent; + OBJECT *next; + OBJECT *prev; + void *observer; + short locked; + ushort event[0]; + } + PACKED + OBJECT_EVENT; + +#define OBJECT_event(_object) ((OBJECT_EVENT *)((intptr_t *)_object + ((OBJECT *)(_object))->class->off_event / sizeof(intptr_t))) +#define OBJECT_is(_object, _class) (OBJECT_class(_object) == _class) +#define OBJECT_is_class(_object) OBJECT_is(_object, CLASS_Class) +#define OBJECT_class(_object) (((OBJECT *)_object)->class) +#define OBJECT_class_null(_object) ((_object) ? ((OBJECT *)_object)->class : NULL) +#define OBJECT_count(_object) (((OBJECT *)_object)->ref) + +#define OBJECT_are_null(_o1, _o2) (((intptr_t)(_o1) | (intptr_t)(_o2)) == 0) +#define OBJECT_are_not_null(_o1, _o2) ((_o1) && (_o2)) + +void *OBJECT_new(CLASS *class, const char *name, OBJECT *parent); +void OBJECT_attach(OBJECT *ob, OBJECT *parent, const char *name); +void OBJECT_detach(OBJECT *ob); +void OBJECT_release(CLASS *class, OBJECT *ob); +void OBJECT_release_static(CLASS *class, CLASS_VAR *var, int nelt, char *data); +//void OBJECT_free(CLASS *class, OBJECT *ob); +void OBJECT_lock(OBJECT *ob, bool block); +bool OBJECT_is_locked(OBJECT *ob); + +//void *OBJECT_alloc(CLASS *class, size_t size); +bool OBJECT_comp_value(VALUE *ob1, VALUE *ob2); + +void OBJECT_exit(void); +void *OBJECT_create(CLASS *class, const char *name, void *parent, int nparam); +void *OBJECT_create_native(CLASS *class, VALUE *param); + +#define OBJECT_create_and_set(_ptr, _class, _name, _parent, _nparam) \ + ((OBJECT_set_pointer = (_ptr)), \ + OBJECT_create((_class), (_name), (_parent), (_nparam))) + +#define OBJECT_is_valid(_object) ((_object) && !(((OBJECT *)_object)->class->check && (*((OBJECT *)_object)->class->check)(_object))) +#define OBJECT_has_events(_object) (((OBJECT *)_object)->class->n_event != 0) + +OBJECT *OBJECT_parent(void *object); +OBJECT *OBJECT_active_parent(void *object); + + +/* +static INLINE CLASS *OBJECT_class(void *object) +{ + if (object) + return ((OBJECT *)object)->class; + else + return object; +} +*/ + +/*#define DEBUG_REF 1*/ + +#if DEBUG_REF + +#if OS_64BITS + #define FREE_MARK ((CLASS *)0x2323232323232323LL) +#else + #define FREE_MARK ((CLASS *)0x23232323) +#endif + +EXTERN const char *OBJECT_ref_where; + +char *OBJECT_where_am_i(const char *file, int line, const char *func); + +#define OBJECT_ref(_object) \ +{ \ + if (_object) \ + { \ + if (OBJECT_class(_object) == FREE_MARK) \ + { \ + fprintf(stderr, "%s: **** ALREADY FREED **** %p\n", OBJECT_ref_where, (_object)); \ + fflush(NULL); \ + BREAKPOINT(); \ + } \ + CLASS_ref(_object); \ + } \ +} + + +#define OBJECT_unref(_object) \ +{ \ + if (_object) \ + { \ + if (OBJECT_class(_object) == FREE_MARK) \ + { \ + fprintf(stderr, "%s: **** ALREADY FREED **** %p\n", OBJECT_ref_where, (_object)); \ + fflush(NULL); \ + BREAKPOINT(); \ + } \ + if (CLASS_unref(_object, TRUE)) \ + _object = NULL; \ + } \ +} + +#define OBJECT_just_unref(_object) \ +{ \ + if (OBJECT_class(_object) == FREE_MARK) \ + { \ + fprintf(stderr, "%s: **** ALREADY FREED **** %p\n", OBJECT_ref_where, (_object)); \ + fflush(NULL); \ + BREAKPOINT(); \ + } \ + CLASS_unref(_object, TRUE); \ +} + + +#define OBJECT_unref_keep(_object) \ +{ \ + if (_object) \ + { \ + if (OBJECT_class(_object) == FREE_MARK) \ + { \ + fprintf(stderr, "%s: **** ALREADY FREED **** %p\n", OBJECT_ref_where, (_object)); \ + fflush(NULL); \ + } \ + CLASS_unref(_object, FALSE); \ + } \ +} + +#define OBJECT_REF(_ob) { OBJECT_ref_where = OBJECT_where_am_i(__FILE__, __LINE__, __func__); OBJECT_ref(_ob); } +#define OBJECT_UNREF(_ob) { OBJECT_ref_where = OBJECT_where_am_i(__FILE__, __LINE__, __func__); OBJECT_unref(_ob); } +#define OBJECT_UNREF_KEEP(_ob) { OBJECT_ref_where = OBJECT_where_am_i(__FILE__, __LINE__, __func__); OBJECT_unref_keep(_ob); } + +#else /* DEBUG_REF */ + +#define OBJECT_ref(_object) \ +{ \ + if (_object) \ + ((OBJECT *)(_object))->ref++; \ +} + +#define OBJECT_unref(_object) \ +{ \ + if (_object) \ + { \ + if ((--((OBJECT *)(_object))->ref) <= 0) \ + { \ + void *temp = (void *)(_object); \ + _object = NULL; \ + CLASS_free(temp); \ + } \ + } \ +} + +#define OBJECT_just_unref(_object) \ +{ \ + if ((--((OBJECT *)(_object))->ref) == 0) \ + { \ + void *temp = (void *)(_object); \ + _object = NULL; \ + CLASS_free(temp); \ + } \ +} + +#define OBJECT_unref_keep(_object) \ +{ \ + if (_object) \ + --((OBJECT *)(_object))->ref; \ +} + + +#define OBJECT_REF(_ob) OBJECT_ref(_ob) +#define OBJECT_UNREF(_ob) OBJECT_unref(_ob) +#define OBJECT_UNREF_KEEP(_ob) OBJECT_unref_keep(_ob) + +#endif /* DEBUG_REF */ + +#define OBJECT_get_var_addr(_object, _desc) ((void *)((char *)(_object) + (_desc)->variable.offset)) + + +static INLINE void OBJECT_null(VALUE *value, CLASS *class) +{ + value->_object.class = class; + value->_object.object = NULL; +} + + +static INLINE void OBJECT_put(VALUE *value, void *object) +{ + value->_object.class = OBJECT_class(object); + value->_object.object = object; +} + +#endif diff --git a/main/gbx/gbx_project.c b/main/gbx/gbx_project.c new file mode 100644 index 00000000..0f6544a9 --- /dev/null +++ b/main/gbx/gbx_project.c @@ -0,0 +1,406 @@ +/*************************************************************************** + + gbx_project.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_PROJECT_C + +#include "config.h" + +#include "gb_common.h" +#include "gb_common_case.h" +#include "gb_alloc.h" +#include "gb_error.h" + +#include + +#include "gb_limit.h" +#include "gb_buffer.h" +#include "gb_file.h" +#include "gbx_stream.h" +#include "gbx_archive.h" +#include "gbx_exec.h" +#include "gbx_stack.h" +#include "gb_component.h" +#include "gbx_component.h" + +#include "gbx_project.h" + +char *PROJECT_path = NULL; +char *PROJECT_exec_path = NULL; + +char *PROJECT_name = NULL; +char *PROJECT_title = NULL; +const char *PROJECT_startup = NULL; +char *PROJECT_version = NULL; +CLASS *PROJECT_class = NULL; + +int PROJECT_argc = 0; +char **PROJECT_argv = NULL; +//char *PROJECT_argname = NULL; + +char *PROJECT_oldcwd = NULL; + +bool PROJECT_run_httpd = FALSE; + +static char *project_buffer; + +//static char *project_ptr; +static int project_line; + +static const char *_last_component = NULL; + +static void raise_error(const char *msg) +{ + char line[16]; + + snprintf(line, sizeof(line), "%d", project_line); + THROW(E_PROJECT, line, msg); +} + + +static void project_title(char *name, int len) +{ + name[len] = 0; + PROJECT_title = name; +} + + +static void project_version(char *name, int len) +{ + name[len] = 0; + PROJECT_version = name; +} + + +static void project_component(char *name, int len) +{ + _last_component = name; + + name[len] = 0; + + COMPONENT_create(name); + + // If 'gb.httpd' is set explicitely, then always run through it. + + if (strcmp(name, "gb.httpd") == 0) + PROJECT_run_httpd = TRUE; + + _last_component = NULL; +} + + +static void project_startup(char *name, int len) +{ + if (PROJECT_startup) + return; + + if (len == 0) + raise_error("Project startup class name is void"); + + name[len] = 0; + PROJECT_startup = name; +} + + +static void project_stack(char *name, int len) +{ + int size; + + name[len] = 0; + size = atoi(name); + + if (size >= 1 && size <= 64) + STACK_size = size * 1024L * sizeof(VALUE); +} + +static void project_stacktrace(char *name, int len) +{ + //ERROR_backtrace = !(len == 1 && *name == '0'); + // Backtrace is always printed now. +} + +static void project_library_path(char *name, int len) +{ + if (!EXEC_debug) + { + ARCHIVE_path = STRING_new_zero(STRING_conv_file_name(name, len)); + if (*name != '/') + { + name = STRING_new_zero(FILE_cat(PROJECT_path, ARCHIVE_path, NULL)); + STRING_free(&ARCHIVE_path); + ARCHIVE_path = name; + } + } +} + +static void check_after_analyze() +{ + if (!PROJECT_name || PROJECT_name[0] == 0) + raise_error("No project name"); + + if (!PROJECT_startup || PROJECT_startup[0] == 0) + raise_error("No startup class"); + + if (!PROJECT_title || PROJECT_title[0] == 0) + PROJECT_title = PROJECT_name; +} + +static bool get_line(char **addr, const char *end, char **start, int *len) +{ + char *p = *addr; + + if (p >= end) + return FALSE; + + while (p < end && *p && *p != '\n') + p++; + + *start = *addr; + *len = p - *start; + *addr = p + 1; + + return (*len > 0); +} + +void PROJECT_analyze_startup(char *addr, int len, PROJECT_COMPONENT_CALLBACK cb) +{ + char *end = &addr[len]; + char *p; + int l, i; + + if (!cb) + { + if (get_line(&addr, end, &p, &l)) + project_startup(p, l); + if (get_line(&addr, end, &p, &l)) + project_title(p, l); + if (get_line(&addr, end, &p, &l)) + project_stack(p, l); + if (get_line(&addr, end, &p, &l)) + project_stacktrace(p, l); + if (get_line(&addr, end, &p, &l)) + project_version(p, l); + } + else + { + for (i = 1; i <= 5; i++) + get_line(&addr, end, &p, &l); + } + + if (get_line(&addr, end, &p, &l)) + { + project_library_path(p, l); + while (get_line(&addr, end, &p, &l)); + } + + if (!cb) + { + while (get_line(&addr, end, &p, &l)) + project_component(p, l); + + check_after_analyze(); + } + else + { + while (get_line(&addr, end, &p, &l)) + { + p[l] = 0; + (*cb)(p); + } + } +} + +void PROJECT_init(const char *file) +{ + int len; + const char *path; + + /* Save the working directory */ + + PROJECT_oldcwd = STRING_new_zero(FILE_getcwd(NULL)); + + /* Gambas installation path */ + + path = FILE_find_gambas(); + + PROJECT_exec_path = STRING_new_zero(FILE_get_dir(FILE_get_dir(path))); + + /* Component paths */ + + #ifdef OS_64BITS + COMPONENT_path = STRING_new_zero(FILE_cat(PROJECT_exec_path, GAMBAS_LIB64_PATH, NULL)); + if (access(COMPONENT_path, F_OK)) + { + STRING_free(&COMPONENT_path); + COMPONENT_path = STRING_new_zero(FILE_cat(PROJECT_exec_path, GAMBAS_LIB_PATH, NULL)); + } + #else + COMPONENT_path = STRING_new_zero(FILE_cat(PROJECT_exec_path, GAMBAS_LIB_PATH, NULL)); + #endif + + //STRING_new(&COMPONENT_user_path, FILE_cat(PROJECT_get_home(), ".local", GAMBAS_LIB_PATH, NULL), 0); + + /* Project path & name*/ + + if (!file) + { + // "gbx3 -e" case + PROJECT_path = STRING_new("", 0); + PROJECT_name = STRING_new("", 0); + return; + } + + if (*file == '.' && file[1] == '/') + file += 2; + + if (EXEC_arch) + { + if (FILE_is_relative(file)) + { + path = FILE_getcwd(file); + if (path == NULL) + goto _PANIC; + } + else + path = file; + + path = FILE_get_dir(path); + FILE_chdir(path); + } + else + { + if (FILE_is_absolute(file)) + { + path = file; + } + else + { + path = FILE_getcwd(file); + if (path == NULL) + goto _PANIC; + + if (!chdir(path)) + { + path = FILE_getcwd(NULL); + if (path == NULL) + goto _PANIC; + } + } + } + + len = strlen(path); + + while (len > 1) + { + if (path[len - 1] != '/') + break; + + len--; + /*path[len] = 0;*/ + } + + PROJECT_path = STRING_new(path, len); + + FILE_chdir(PROJECT_path); + + /* Project name */ + + if (EXEC_arch) + PROJECT_name = STRING_new_zero(FILE_get_basename(file)); + else + PROJECT_name = STRING_new_zero(FILE_get_name(PROJECT_path)); + + /* Main archive creation */ + + ARCHIVE_create_main(EXEC_arch ? FILE_get_name(file) : NULL); + + return; + +_PANIC: + ERROR_panic("Cannot initialize project: %s", strerror(errno)); +} + + +void PROJECT_load() +{ + const char *file; + int len; + + /* Project file analyze */ + + STACK_init(); + + if (EXEC_arch) + file = ".startup"; + else + file = FILE_cat(PROJECT_path, ".startup", NULL); + + TRY + { + STREAM_load(file, &project_buffer, &len); + } + CATCH + { + ERROR_fatal("unable to find startup file"); + } + END_TRY + + TRY + { + PROJECT_analyze_startup(project_buffer, len, NULL); + } + CATCH + { + if (_last_component) + ERROR_fatal("unable to load component: %s", _last_component); + else + ERROR_fatal("unable to analyze startup file"); + } + END_TRY + + // Loads all component + COMPONENT_load_all(); +} + +void PROJECT_load_finish(void) +{ + // Load exported class of components written in Gambas + COMPONENT_load_all_finish(); + + // Loads main archive + ARCHIVE_load_main(); + + // Startup class + PROJECT_class = CLASS_find(PROJECT_startup); +} + +void PROJECT_exit(void) +{ + if (project_buffer) + FREE(&project_buffer); + + //STRING_free(&PROJECT_argname); + STRING_free(&PROJECT_name); + STRING_free(&PROJECT_path); + STRING_free(&PROJECT_oldcwd); + STRING_free(&PROJECT_exec_path); +} diff --git a/main/gbx/gbx_project.h b/main/gbx/gbx_project.h new file mode 100644 index 00000000..74ac9a93 --- /dev/null +++ b/main/gbx/gbx_project.h @@ -0,0 +1,61 @@ +/*************************************************************************** + + gbx_project.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_PROJECT_H +#define __GBX_PROJECT_H + +#include "gbx_class.h" + +typedef + struct { + char *command; + void (*func)(); + } + PROJECT_COMMAND; + +typedef + void (*PROJECT_COMPONENT_CALLBACK)(char *); + +#ifndef __GBX_PROJECT_C +EXTERN char *PROJECT_path; +EXTERN char *PROJECT_exec_path; +EXTERN char *PROJECT_name; +EXTERN char *PROJECT_title; +EXTERN char *PROJECT_version; +EXTERN const char *PROJECT_startup; +EXTERN CLASS *PROJECT_class; +EXTERN int PROJECT_argc; +EXTERN char **PROJECT_argv; +//EXTERN char *PROJECT_argname; +EXTERN char *PROJECT_oldcwd; +EXTERN char *PROJECT_user_home; +EXTERN bool PROJECT_run_httpd; +#endif + +void PROJECT_init(const char *file); +void PROJECT_load(void); +void PROJECT_load_finish(void); +void PROJECT_exit(void); +void PROJECT_analyze_startup(char *addr, int len, PROJECT_COMPONENT_CALLBACK cb); + +#endif diff --git a/main/gbx/gbx_regexp.c b/main/gbx/gbx_regexp.c new file mode 100644 index 00000000..7693f31a --- /dev/null +++ b/main/gbx/gbx_regexp.c @@ -0,0 +1,304 @@ +/*************************************************************************** + + gbx_regexp.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_REGEXP_C + +#include "gb_common.h" +#include "gb_common_case.h" + +#include + +#include "gb_alloc.h" +#include "gb_array.h" +#include "gb_error.h" +#include "gbx_c_array.h" +#include "gbx_api.h" +#include "gb.pcre.h" + +#include "gbx_regexp.h" + + +static REGEXP_SCAN_FUNC _scan_cb = NULL; +static CARRAY *_scan_array; + +static PCRE_INTERFACE PCRE; + +static void init_pcre() +{ + static bool init = FALSE; + + if (init) + return; + + COMPONENT_load(COMPONENT_create("gb.pcre")); + LIBRARY_get_interface_by_name("gb.pcre", PCRE_INTERFACE_VERSION, &PCRE); + init = TRUE; +} + +bool REGEXP_match(const char *pattern, int len_pattern, const char *string, int len_string) +{ + unsigned char cp; + unsigned char cs; + + #define _next_pattern() (cp = *pattern++, len_pattern--) + #define _next_string(void) (cs = *string++, len_string--) + + /*if (len_pattern == 0 || len_string == 0) + return FALSE;*/ + + for(;;) + { + if (len_pattern == 0) + return (len_string == 0); + + _next_pattern(); + + if (cp == '*') + { + const char *p; + + if (len_pattern == 0) + { + if (_scan_cb) + (*_scan_cb)(string, len_string); + return TRUE; + } + + p = string; + + for(;;) + { + if (REGEXP_match(pattern, len_pattern, string, len_string)) + { + if (_scan_cb) + (*_scan_cb)(p, string - p); + return TRUE; + } + if (len_string == 0) + return FALSE; + _next_string(); + } + return FALSE; + } + + if (len_string == 0) + return FALSE; /*end || (len_pattern == 0);*/ + + _next_string(); + + if (cp == '?') + continue; + + if (cp == '[' && len_pattern > 0) + { + bool not = FALSE; + bool in = FALSE; + unsigned char cb = 0; + + _next_pattern(); + if (cp == '^') + { + not = TRUE; + _next_pattern(); + } + + if (cp == cs) + { + in = TRUE; + _next_pattern(); + } + else + { + for(;;) + { + if (cp == '-' && len_pattern > 1 && cb && cb != '-') + { + _next_pattern(); + if (cb <= cs && cs <= cp) + { + in = TRUE; + break; + } + cb = 0; + } + else if (cp == cs) + { + in = TRUE; + break; + } + else + cb = cp; + + _next_pattern(); + if (cp == ']') + break; + } + } + + for(;;) + { + if (cp == ']') + break; + if (len_pattern == 0) + THROW(E_REGEXP, "Missing ']'"); + _next_pattern(); + } + + if (in ^ not) + continue; + + return FALSE; + } + + if (cp == ' ') + { + if (cs > ' ') + return FALSE; + + while (len_string && cs <= ' ') + _next_string(); + + if (cs > ' ') + { + string--; + len_string++; + } + + while (len_pattern && cp == ' ') + _next_pattern(); + + if (cp != ' ') + { + pattern--; + len_pattern++; + } + + continue; + } + + if (cp == '{') + { + const char *save_string; + int save_len_string; + const char *save_pattern; + int save_len_pattern; + + string--; len_string++; + save_string = string; + save_len_string = len_string; + + NEXT_SUB_PATTERN: + + for(;;) + { + if (len_pattern == 0) + goto MISSING_BRACE; + _next_pattern(); + if (cp == ',' || cp == '}') + break; + _next_string(); + if (tolower(cp) != tolower(cs)) + break; + } + + if (cp == ',' || cp == '}') + { + save_pattern = pattern - 1; + save_len_pattern = len_pattern + 1; + + while (cp != '}') + { + if (len_pattern == 0) + goto MISSING_BRACE; + _next_pattern(); + } + + if (REGEXP_match(pattern, len_pattern, string, len_string)) + return TRUE; + + pattern = save_pattern; + len_pattern = save_len_pattern; + _next_pattern(); + } + + while (cp != ',') + { + if (cp == '}') + return FALSE; + + if (len_pattern == 0) + goto MISSING_BRACE; + _next_pattern(); + } + + string = save_string; + len_string = save_len_string; + + goto NEXT_SUB_PATTERN; + } + + if (cp == '\\') + { + if (len_pattern == 0) + THROW(E_REGEXP, "Trailing backslash"); + _next_pattern(); + } + + if (tolower(cp) != tolower(cs)) + return FALSE; + } + +MISSING_BRACE: + + THROW(E_REGEXP, "Missing '}'"); +} + + +static void add_string(const char *str, int len) +{ + char **p = (char **)GB_ArrayAdd((GB_ARRAY)_scan_array); + if (len) + *p = STRING_new(str, len); +} + + +bool REGEXP_scan(CARRAY *array, const char *pattern, int len_pattern, const char *string, int len_string) +{ + bool match; + + _scan_cb = add_string; + _scan_array = array; + match = REGEXP_match(pattern, len_pattern, string, len_string); + CARRAY_reverse(array, NULL); + _scan_cb = NULL; + _scan_array = NULL; + + return match; +} + +bool REGEXP_match_pcre(const char *pattern, int len_pattern, const char *string, int len_string) +{ + init_pcre(); + return PCRE.Match(string, len_string, pattern, len_pattern, 0, 0); +} + diff --git a/main/gbx/gbx_regexp.h b/main/gbx/gbx_regexp.h new file mode 100644 index 00000000..2340a5ac --- /dev/null +++ b/main/gbx/gbx_regexp.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + gbx_regexp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_REGEXP_H +#define __GBX_REGEXP_H + +#include "gbx_c_array.h" + +typedef + void (*REGEXP_SCAN_FUNC)(const char *, int); + +bool REGEXP_match(const char *pattern, int len_pattern, const char *string, int len_string); +bool REGEXP_match_pcre(const char *pattern, int len_pattern, const char *string, int len_string); +bool REGEXP_scan(CARRAY *array, const char *pattern, int len_pattern, const char *string, int len_string); + +#endif + diff --git a/main/gbx/gbx_replace.c b/main/gbx/gbx_replace.c new file mode 100644 index 00000000..36f906c6 --- /dev/null +++ b/main/gbx/gbx_replace.c @@ -0,0 +1,25 @@ +/*************************************************************************** + + gbx_replace.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_replace_temp.h" + diff --git a/main/gbx/gbx_signal.c b/main/gbx/gbx_signal.c new file mode 100644 index 00000000..5e69d22a --- /dev/null +++ b/main/gbx/gbx_signal.c @@ -0,0 +1,403 @@ +/*************************************************************************** + + gbx_signal.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_SIGNAL_C + +#include "gb_alloc.h" +#include "gb_error.h" +#include "gb_array.h" +#include "gbx_api.h" +#include "gbx_signal.h" + +//#define DEBUG_ME 1 + +static SIGNAL_HANDLER *_handlers = NULL; +static int _pipe[2] = { -1, -1 }; +static volatile int _count = 0; +static int _raising_callback = 0; + +void SIGNAL_install(SIGNAL_HANDLER *handler, int signum, void (*callback)(int, siginfo_t *, void *)) +{ + struct sigaction action; + + #if DEBUG_ME + fprintf(stderr, "SIGNAL_install: %d %p\n", signum, callback); + #endif + + handler->signum = signum; + + action.sa_flags = SA_SIGINFO; + sigemptyset(&action.sa_mask); + action.sa_sigaction = callback; + + if (sigaction(signum, NULL, &handler->old_action) != 0 || sigaction(signum, &action, NULL) != 0) + ERROR_panic("Cannot install signal handler: %s", strerror(errno)); +} + +void SIGNAL_uninstall(SIGNAL_HANDLER *handler, int signum) +{ + #if DEBUG_ME + fprintf(stderr, "SIGNAL_uninstall: %d\n", signum); + #endif + + if (sigaction(signum, &handler->old_action, NULL) != 0) + ERROR_panic("Cannot uninstall signal handler"); + + while (handler->callbacks) + SIGNAL_unregister(handler->signum, handler->callbacks); +} + +void SIGNAL_previous(SIGNAL_HANDLER *handler, int signum, siginfo_t *info, void *context) +{ + if (handler->old_action.sa_handler != SIG_DFL && handler->old_action.sa_handler != SIG_IGN) + { + if (handler->old_action.sa_flags & SA_SIGINFO) + { + //fprintf(stderr, "Calling old action %p\n", _old_SIGCHLD_action.sa_sigaction); + (*handler->old_action.sa_sigaction)(signum, info, context); + } + else + { + //fprintf(stderr, "Calling old handler %p\n", _old_SIGCHLD_action.sa_handler); + (*handler->old_action.sa_handler)(signum); + } + } +} + +static SIGNAL_HANDLER *find_handler(int signum) +{ + int i; + + for (i = 0; i < ARRAY_count(_handlers); i++) + { + if (_handlers[i].signum == signum) + return &_handlers[i]; + } + + return NULL; +} + +static SIGNAL_HANDLER *add_handler(void) +{ + if (!_handlers) + ARRAY_create_inc(&_handlers, 1); + + return ARRAY_add_void(&_handlers); +} + +static void handle_signal(int signum, siginfo_t *info, void *context) +{ + char buffer; + int save_errno; + + save_errno = errno; + + if (_count) + { + buffer = signum; + for(;;) + { + if (write(_pipe[1], &buffer, 1) == 1) + break; + + if (errno != EINTR) + { + ERROR_warning("cannot write signal #%d into signal pipe: %s", signum, strerror(errno)); + break; + } + } + } + + SIGNAL_previous(find_handler(signum), signum, info, context); + + errno = save_errno; +} + +static bool _must_purge_callbacks = FALSE; +static int _purge_signum; +static SIGNAL_HANDLER *_purge_handler; + +static void purge_callbacks(void) +{ + SIGNAL_CALLBACK *cb, *next_cb; + + _raising_callback--; + if (_raising_callback) + return; + + #if DEBUG_ME + fprintf(stderr, ">> purge_callbacks\n"); + #endif + + while (_must_purge_callbacks) + { + _must_purge_callbacks = FALSE; + + cb = _purge_handler->callbacks; + while (cb) + { + #if DEBUG_ME + fprintf(stderr, "purge_callbacks: cb = %p\n", cb); + #endif + next_cb = cb->next; + + if (!cb->callback) + SIGNAL_unregister(_purge_signum, cb); + + cb = next_cb; + } + } + + #if DEBUG_ME + fprintf(stderr, "<< purge_callbacks\n"); + #endif +} + +void SIGNAL_raise_callbacks(int fd, int type, void *data) +{ + SIGNAL_HANDLER *handler; + SIGNAL_CALLBACK *cb; + char signum; + int ret; + + /*old = signal(SIGCHLD, signal_child);*/ + #if DEBUG_ME + fprintf(stderr, "SIGNAL_raise_callbacks: fd = %d blocking = %d\n", fd, (fcntl(fd, F_GETFL) & O_NONBLOCK) == 0); + #endif + + for(;;) + { + ret = read(fd, &signum, 1); + if (ret != 1) + { + #if DEBUG_ME + fprintf(stderr, "SIGNAL_raise_callbacks: read -> %d / errno = %d\n", ret, errno); + #endif + return; + } + + handler = find_handler(signum); + if (!handler) + { + #if DEBUG_ME + fprintf(stderr, "SIGNAL_raise_callbacks: no handler\n"); + #endif + return; + } + + #if DEBUG_ME + fprintf(stderr, ">> SIGNAL_raise_callbacks (%d)\n", _raising_callback); + #endif + + _raising_callback++; + _purge_signum = signum; + _purge_handler = handler; + + ON_ERROR(purge_callbacks) + { + cb = handler->callbacks; + while (cb) + { + #if DEBUG_ME + fprintf(stderr, "SIGNAL_raise_callbacks: cb = %p cb->callback = %p\n", cb, cb->callback); + #endif + if (cb->callback) + (*cb->callback)((int)signum, cb->data); + + cb = cb->next; + } + } + END_ERROR + + #if DEBUG_ME + fprintf(stderr, "SIGNAL_raise_callbacks: purge_callbacks\n"); + #endif + purge_callbacks(); + + #if DEBUG_ME + fprintf(stderr, "<< SIGNAL_raise_callbacks (%d)\n", _raising_callback); + #endif + } +} + +static void create_pipe(void) +{ + if (pipe(_pipe) != 0) + ERROR_panic("Cannot create signal handler pipes: %s", strerror(errno)); + + if (_pipe[0] == 0) + BREAKPOINT(); + + fcntl(_pipe[0], F_SETFD, FD_CLOEXEC); + fcntl(_pipe[1], F_SETFD, FD_CLOEXEC); + // Allows to read the signal pipe without blocking + fcntl(_pipe[0], F_SETFL, fcntl(_pipe[0], F_GETFL) | O_NONBLOCK); + + GB_Watch(_pipe[0], GB_WATCH_READ, (void *)SIGNAL_raise_callbacks, 0); + + #if DEBUG_ME + fprintf(stderr, "create_pipe: fd = %d\n", _pipe[0]); + #endif +} + +static void delete_pipe(void) +{ + #if DEBUG_ME + fprintf(stderr, "delete_pipe: fd = %d\n", _pipe[0]); + #endif + GB_Watch(_pipe[0], GB_WATCH_NONE, NULL, 0); + close(_pipe[0]); + close(_pipe[1]); + _pipe[0] = -1; + _pipe[1] = -1; +} + +SIGNAL_CALLBACK *SIGNAL_register(int signum, void (*callback)(int, intptr_t), intptr_t data) +{ + SIGNAL_HANDLER *handler; + SIGNAL_CALLBACK *cb; + + if (!_count) + create_pipe(); + + _count++; + + handler = find_handler(signum); + if (!handler) + { + handler = add_handler(); + SIGNAL_install(handler, signum, handle_signal); + } + + ALLOC(&cb, sizeof(SIGNAL_CALLBACK)); + + cb->prev = NULL; + cb->next = handler->callbacks; + cb->callback = callback; + cb->data = data; + + if (cb->next) + cb->next->prev = cb; + handler->callbacks = cb; + + #if DEBUG_ME + fprintf(stderr, "SIGNAL_register: %d -> %p (%p)\n", signum, cb, cb->callback); + #endif + + #if DEBUG_ME + fprintf(stderr, "handler->callbacks %p:", handler); + SIGNAL_CALLBACK *save = cb; + cb = handler->callbacks; + while (cb) + { + fprintf(stderr, " -> %p (%p)", cb, cb->callback); + cb = cb->next; + } + fprintf(stderr, "\n"); + cb = save; + #endif + + return cb; +} + +void SIGNAL_unregister(int signum, SIGNAL_CALLBACK *cb) +{ + SIGNAL_HANDLER *handler = find_handler(signum); + + if (!handler) + return; + + if (_raising_callback) + { + #if DEBUG_ME + fprintf(stderr, "SIGNAL_unregister: disable %d %p (%p)\n", signum, cb, cb->callback); + #endif + cb->callback = NULL; + _must_purge_callbacks = TRUE; + return; + } + + #if DEBUG_ME + fprintf(stderr, "SIGNAL_unregister: remove %d %p (%p)\n", signum, cb, cb->callback); + #endif + + if (cb->prev) + cb->prev->next = cb->next; + + if (cb->next) + cb->next->prev = cb->prev; + + if (cb == handler->callbacks) + handler->callbacks = cb->next; + + IFREE(cb); + + _count--; + + if (_count == 0) + delete_pipe(); + + #if DEBUG_ME + fprintf(stderr, "handler->callbacks %p:", handler); + cb = handler->callbacks; + while (cb) + { + fprintf(stderr, " -> %p (%p)", cb, cb->callback); + cb = cb->next; + } + fprintf(stderr, "\n"); + #endif +} + +void SIGNAL_exit(void) +{ + int i; + SIGNAL_HANDLER *handler; + + if (_handlers) + { + for (i = 0; i < ARRAY_count(_handlers); i++) + { + handler = &_handlers[i]; + SIGNAL_uninstall(handler, handler->signum); + } + + ARRAY_delete(&_handlers); + } +} + +int SIGNAL_get_fd(void) +{ + return _pipe[0]; +} + +void SIGNAL_has_forked(void) +{ + if (!_count) + return; + + GB_Watch(_pipe[0], GB_WATCH_NONE, NULL, 0); + close(_pipe[0]); + close(_pipe[1]); + create_pipe(); +} diff --git a/main/gbx/gbx_signal.h b/main/gbx/gbx_signal.h new file mode 100644 index 00000000..64d65070 --- /dev/null +++ b/main/gbx/gbx_signal.h @@ -0,0 +1,61 @@ +/*************************************************************************** + + gbx_signal.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_SIGNAL_H +#define __GBX_SIGNAL_H + +#include +#include + +typedef + struct SIGNAL_CALLBACK { + struct SIGNAL_CALLBACK *prev; + struct SIGNAL_CALLBACK *next; + void (*callback)(int, intptr_t); + intptr_t data; + } + SIGNAL_CALLBACK; + +typedef + struct { + int signum; + struct sigaction old_action; + SIGNAL_CALLBACK *callbacks; + } + SIGNAL_HANDLER; + +void SIGNAL_install(SIGNAL_HANDLER *handler, int signum, void (*callback)(int, siginfo_t *, void *)); +void SIGNAL_uninstall(SIGNAL_HANDLER *handler, int signum); +void SIGNAL_previous(SIGNAL_HANDLER *handler, int signum, siginfo_t *info, void *context); + +SIGNAL_CALLBACK *SIGNAL_register(int signum, void (*callback)(int, intptr_t), intptr_t data); +void SIGNAL_unregister(int signum, SIGNAL_CALLBACK *cb); + +int SIGNAL_get_fd(void); +void SIGNAL_raise_callbacks(int fd, int type, void *data); +void SIGNAL_exit(void); +void SIGNAL_has_forked(void); + +#endif + + diff --git a/main/gbx/gbx_split.c b/main/gbx/gbx_split.c new file mode 100644 index 00000000..e5961d0e --- /dev/null +++ b/main/gbx/gbx_split.c @@ -0,0 +1,243 @@ +/*************************************************************************** + + gbx_split.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_SPLIT_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_array.h" +#include "gbx_c_array.h" + +static CARRAY *_array; +static bool _novoid; +static char *_entry; +static const char *_ptr; +static int _lptr; + +static void add_char_real(const char *p) +{ + if (_lptr) + { + int old_len = STRING_length(_entry); + + _entry = STRING_extend(_entry, old_len + _lptr); + memcpy(&_entry[old_len], _ptr, _lptr); + _entry[old_len + _lptr] = 0; + } + + _ptr = p; + _lptr = p ? 1 : 0; +} + +#define add_char(_p) \ +({ \ + if ((_p) && (_p) == (_ptr + _lptr)) \ + _lptr++; \ + else \ + add_char_real(_p); \ +}) + +static void add_entry() +{ + add_char_real(NULL); + + if (!_entry) + { + if (!_novoid) + ARRAY_add_void((char ***)&_array->data); + } + else + { + *((char **)ARRAY_add((char ***)&_array->data)) = _entry; + _entry = NULL; + } + + //fprintf(stderr, "** add_entry\n"); +} + +static void split_fast(CARRAY *array, const char *str, int lstr, const char *sep, int lsep, bool no_void) +{ + const char *ptr = NULL; + int lptr = 0; + + #define add_entry_fast() \ + ({ \ + if (lptr) \ + { \ + *((char **)ARRAY_add((char ***)&array->data)) = STRING_new(ptr, lptr); \ + lptr = 0; \ + } \ + else if (!no_void) \ + { \ + ARRAY_add_void((char ***)&array->data); \ + } \ + }) + + if (lsep == 1) + { + char csep = sep[0]; + + while (lstr--) + { + if (*str == csep) + { + add_entry_fast(); + } + else + { + if (!lptr) ptr = str; + lptr++; + } + + str++; + } + } + else + { + while (lstr--) + { + if (memchr(sep, *str, lsep)) + { + add_entry_fast(); + } + else + { + if (!lptr) ptr = str; + lptr++; + } + + str++; + } + } + + add_entry_fast(); +} + +CARRAY *STRING_split(const char *str, int lstr, const char *sep, int lsep, const char *esc, int lesc, bool no_void, bool keep_esc) +{ + CARRAY *array; + int i; + char c; + bool escape; + char escl, escr; + + array = OBJECT_create(CLASS_StringArray, NULL, NULL, 0); + if (lstr == 0) + return array; + + if (sep == NULL || lsep == 0) + { + sep = ","; + lsep = 1; + } + + if (esc == NULL || lesc == 0) + { + split_fast(array, str, lstr, sep, lsep, no_void); + } + else + { + _array = array; + _entry = NULL; + _novoid = no_void; + _ptr = NULL; + _lptr = 0; + + escl = esc[0]; + if (lesc >= 2) + escr = esc[1]; + else + escr = escl; + + if (escr == *sep) + { + for (i = 0; i < lstr; i++) + { + c = *str; + + if (c == escl) + { + i++; + str++; + if (i < lstr) + add_char(str); + } + else if (c == *sep || (lsep > 1 && memchr(&sep[1], c, lsep - 1))) + { + add_entry(); + } + else + add_char(str); + + str++; + } + } + else + { + escape = FALSE; + + for (i = 0; i < lstr; i++) + { + c = *str; + + if (escape) + { + if (c != escr) + add_char(str); + else if ((i < (lstr - 1)) && str[1] == escr) + { + add_char(str); + str++; + i++; + } + else + { + escape = FALSE; + if (keep_esc) + add_char(str); + } + } + else if (c == escl) + { + escape = TRUE; + if (keep_esc) + add_char(str); + } + else if (c == *sep || (lsep > 1 && memchr(&sep[1], c, lsep - 1))) + { + add_entry(); + } + else + add_char(str); + + str++; + } + } + + add_entry(); + } + + array->count = ARRAY_count(array->data); + + return array; +} diff --git a/main/gbx/gbx_split.h b/main/gbx/gbx_split.h new file mode 100644 index 00000000..12e37ace --- /dev/null +++ b/main/gbx/gbx_split.h @@ -0,0 +1,31 @@ +/*************************************************************************** + + gbx_split.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_SPLIT_H +#define __GBX_SPLIT_H + +#include "gbx_c_array.h" + +CARRAY *STRING_split(const char *str, int lstr, const char *sep, int lsep, const char *esc, int lesc, bool no_void, bool keep_esc); + +#endif \ No newline at end of file diff --git a/main/gbx/gbx_stack.c b/main/gbx/gbx_stack.c new file mode 100644 index 00000000..f6dde6a1 --- /dev/null +++ b/main/gbx/gbx_stack.c @@ -0,0 +1,161 @@ +/*************************************************************************** + + gbx_stack.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_STACK_C + +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_alloc.h" +#include "gbx_exec.h" +#include "gb_error.h" +#include "gbx_string.h" +#include "gbx_stack.h" + +char *STACK_base = NULL; +size_t STACK_size; +char *STACK_limit = NULL; +STACK_CONTEXT *STACK_frame; +int STACK_frame_count; + +uintptr_t STACK_process_stack_limit; + +void STACK_init(void) +{ + int stack; + struct rlimit limit; + uintptr_t max; + + // Get the maximum stack size allowed + if (getrlimit(RLIMIT_STACK, &limit)) + ERROR_panic("Cannot get stack size limit"); + + if (limit.rlim_cur == RLIM_INFINITY) + max = 64 << 20; // 64 Mb if there is no limit. + else + max = (uintptr_t)limit.rlim_cur; + + STACK_size = max - sizeof(VALUE) * 256; // some security + #if DEBUG_STACK + fprintf(stderr, "STACK_size = %ld\n", STACK_size); + #endif + + STACK_base = mmap(NULL, STACK_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); + + //fprintf(stderr, "Stack = %p %ld\n", STACK_base, STACK_size); + + STACK_process_stack_limit = (uintptr_t)&stack - max + 65536; + + STACK_limit = STACK_base + STACK_size; + STACK_frame = (STACK_CONTEXT *)STACK_limit; + STACK_frame_count = 0; + STACK_limit -= STACK_FOR_EVAL * sizeof(VALUE); + + SP = (VALUE *)STACK_base; +} + + +void STACK_exit(void) +{ + if (STACK_base) + { + munmap(STACK_base, STACK_size); + STACK_base = NULL; + } +} + +#if DEBUG_STACK +bool STACK_check(int need) +{ + static VALUE *old = NULL; + + fprintf(stderr, "STACK_check: SP = %d need = %d limit = %d\n", (int)(((char *)SP - STACK_base) / sizeof(VALUE)), need, (int)((STACK_limit - STACK_base) / sizeof(VALUE))); + + if (SP > old) + { + fprintf(stderr, "**** STACK_check: -> %ld bytes\n", ((char *)SP - STACK_base)); + old = SP; + } + + if (((char *)(SP + need) + sizeof(STACK_CONTEXT)) >= STACK_limit) + { + THROW_STACK(); + return TRUE; + } + else + return FALSE; +} +#endif + +bool STACK_has_error_handler(void) +{ + int i; + STACK_CONTEXT *sc; + + for (i = 0; i < STACK_frame_count; i++) + { + sc = &STACK_frame[i]; + if (sc->ec || sc->ep) + return TRUE; + } + + return FALSE; +} + +STACK_CONTEXT *STACK_get_frame(int frame) +{ + if (frame >= 0 && frame < STACK_frame_count) + return &STACK_frame[frame]; + else + return NULL; +} + +STACK_BACKTRACE *STACK_get_backtrace(void) +{ + STACK_BACKTRACE *bt, *pbt; + int i; + + if (STACK_frame_count == 0) + return NULL; + + ALLOC(&bt, sizeof(STACK_BACKTRACE) * (1 + STACK_frame_count)); + + bt->cp = CP; + bt->fp = FP; + bt->pc = PC; + + for (i = 0, pbt = &bt[1]; i < STACK_frame_count; i++, pbt++) + { + pbt->cp = STACK_frame[i].cp; + pbt->fp = STACK_frame[i].fp; + pbt->pc = STACK_frame[i].pc; + } + + // Mark the end of the backtrace + pbt--; + STACK_backtrace_set_end(pbt); + + return bt; +} diff --git a/main/gbx/gbx_stack.h b/main/gbx/gbx_stack.h new file mode 100644 index 00000000..4357d203 --- /dev/null +++ b/main/gbx/gbx_stack.h @@ -0,0 +1,132 @@ +/*************************************************************************** + + gbx_stack.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_STACK_H +#define __GBX_STACK_H + +#include "gbx_value.h" +#include "gb_pcode.h" + +//#define DEBUG_STACK 1 + +typedef + struct { + void *cp; + void *fp; + void *pc; + } + STACK_BACKTRACE; + +typedef + struct _stack_context { + struct _stack_context *next; + VALUE *bp; // local variables + VALUE *pp; // local parameters + CLASS *cp; // current class + char *op; // current object + VALUE *ep; // error pointer + FUNCTION *fp; // current function + PCODE *pc; // instruction + PCODE *ec; // instruction if error + PCODE *et; // TRY save + VALUE *gp; // GOSUB stack pointer + } + STACK_CONTEXT; + +#ifndef __GBX_STACK_C + +EXTERN char *STACK_base; +EXTERN size_t STACK_size; +EXTERN char *STACK_limit; +//EXTERN size_t STACK_relocate; + +EXTERN int STACK_frame_count; +EXTERN STACK_CONTEXT *STACK_frame; + +EXTERN uintptr_t STACK_process_stack_limit; + +#endif + +#define STACK_FOR_EVAL 16 + +void STACK_init(void); +void STACK_exit(void); + +#if DEBUG_STACK +bool STACK_check(int need); +#else + +#define STACK_check(_need) \ +do { \ + if (((char *)(SP + (_need)) + sizeof(STACK_CONTEXT)) >= STACK_limit) \ + THROW_STACK(); \ + } \ +while (0); + +#endif + +bool STACK_has_error_handler(void); + +STACK_BACKTRACE *STACK_get_backtrace(void); +#define STACK_free_backtrace(_backtrace) FREE(_backtrace) +#define STACK_backtrace_is_end(_bt) ((((intptr_t)((_bt)->cp)) & 1) != 0) +#define STACK_backtrace_set_end(_bt) ((_bt)->cp = (void *)(((intptr_t)((_bt)->cp)) | 1)) +#define STACK_backtrace_clear_end(_bt) ((_bt)->cp = (void *)(((intptr_t)((_bt)->cp)) & ~1)) + + +STACK_CONTEXT *STACK_get_frame(int frame); + +#define STACK_get_previous_pc() ((STACK_frame_count <= 0) ? NULL : STACK_frame->pc) + +#define STACK_get_current() ((STACK_frame_count > 0) ? STACK_frame : NULL) + +#define STACK_copy(_dst, _src) *(_dst) = *(_src) + +#define STACK_push_frame(_context, _need) \ +({ \ + int stack; \ + if ((uintptr_t)&stack < STACK_process_stack_limit) \ + THROW_STACK(); \ + \ + STACK_check(_need); \ + \ + STACK_frame--; \ + \ + STACK_copy(STACK_frame, _context); \ + \ + STACK_frame_count++; \ + STACK_limit -= sizeof(STACK_CONTEXT); \ +}) + +#define STACK_pop_frame(_context) \ +({ \ + STACK_copy(_context, STACK_frame); \ + STACK_frame++; \ + STACK_frame_count--; \ + STACK_limit += sizeof(STACK_CONTEXT); \ +}) + +#define STACK_enable_for_eval() STACK_limit += STACK_FOR_EVAL * sizeof(VALUE) +#define STACK_disable_for_eval() STACK_limit -= STACK_FOR_EVAL * sizeof(VALUE) + +#endif diff --git a/main/gbx/gbx_stream.c b/main/gbx/gbx_stream.c new file mode 100644 index 00000000..fc86cabf --- /dev/null +++ b/main/gbx/gbx_stream.c @@ -0,0 +1,1826 @@ +/*************************************************************************** + + gbx_stream.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_STREAM_C + +#include "gb_common.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gb_common_buffer.h" +#include "gb_common_swap.h" +#include "gb_common_check.h" +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" +#include "gbx_exec.h" +#include "gbx_project.h" +#include "gb_file.h" +#include "gambas.h" +#include "gbx_regexp.h" +#include "gbx_api.h" +#include "gbx_string.h" +#include "gbx_watch.h" +#include "gbx_c_array.h" +#include "gbx_c_collection.h" +#include "gbx_struct.h" +#include "gbx_stream.h" + +static STREAM _temp_stream = { 0 }; +static STREAM *_temp_save = NULL; +static int _temp_level; + +#if DEBUG_STREAM +static unsigned char _tag = 0; +static int _nopen = 0; +#endif + +void STREAM_exit(void) +{ +#if DEBUG_STREAM + if (_nopen) + ERROR_warning("%d streams yet opened", _nopen); +#endif + STREAM_close(&_temp_stream); +} + +static void wait_for_fd_ready_to_read(int fd) +{ + if (fd >= 0) + WATCH_process(fd, -1, -1, 0); +} + +bool STREAM_in_archive(const char *path) +{ + ARCHIVE *arch; + + if (FILE_is_relative(path)) + { + ARCHIVE_get_current(&arch); + if (arch->name || EXEC_arch) // || !FILE_exist_real(path)) - Why that test ? + return TRUE; + } + + return FALSE; +} + +int STREAM_get_readable(STREAM *stream, int *len) +{ + int fd; + off_t off; + off_t end; + + fd = STREAM_handle(stream); + if (fd < 0) + return TRUE; + +//_IOCTL: + + #ifdef FIONREAD + + if (!stream->common.no_fionread) + { + if (ioctl(fd, FIONREAD, len) >= 0) + return 0; + + stream->common.no_fionread = TRUE; + } + + #endif + +//_LSEEK: + + if (!stream->common.no_lseek) + { + off = lseek(fd, 0, SEEK_CUR); + if (off >= 0) + { + end = lseek(fd, 0, SEEK_END); + if (end >= 0) + { + *len = (int)(end - off); + + off = lseek(fd, off, SEEK_SET); + if (off >= 0) + return 0; + } + } + + stream->common.no_lseek = TRUE; + } + + //fprintf(stderr, "STREAM_get_readable: lseek: %d\n", *len); + return (-1); +} + +bool STREAM_default_eof(STREAM *stream) +{ + int fd; + int ilen; + + fd = STREAM_handle(stream); + if (fd < 0) + return TRUE; + + if (STREAM_is_blocking(stream) && !stream->common.available_now) + wait_for_fd_ready_to_read(STREAM_handle(stream)); + + if (STREAM_get_readable(stream, &ilen)) + return TRUE; + + return (ilen == 0); +} + +// STREAM_open *MUST* initialize completely the stream structure + +void STREAM_open(STREAM *stream, const char *path, int mode) +{ + STREAM_CLASS *sclass; + int fd; + + stream->type = NULL; + + if (mode & STO_PIPE) + sclass = &STREAM_pipe; + else if (mode & STO_MEMORY) + sclass = &STREAM_memory; + else if (mode & STO_STRING) + sclass = &STREAM_string; + else if (mode & STO_LOCK) + sclass = &STREAM_lock; + else + { + // ".99" is used for opening a file descriptor in direct mode + + if (FILE_is_relative(path) && !((mode & STO_DIRECT) && path[0] == '.' && isdigit(path[1]))) + { + ARCHIVE *arch = NULL; + const char *tpath = path; + + /*ARCHIVE *arch = NULL; + + if (strncmp(path, "../", 3)) + ARCHIVE_get_current(&arch); + else if (!EXEC_arch) + path += 3; + + if ((arch && arch->name) || EXEC_arch) // || !FILE_exist_real(path)) - Why that test ? + { + sclass = &STREAM_arch; + goto _OPEN; + }*/ + + if ((mode & STO_ACCESS) != STO_READ || mode & STO_PIPE) + THROW(E_ACCESS); + + if (!ARCHIVE_find_from_path(&arch, &tpath)) + { + sclass = &STREAM_arch; + goto __OPEN; + } + + path = tpath; + } + + if (mode & STO_DIRECT) + sclass = &STREAM_direct; + else + sclass = &STREAM_buffer; + } + +__OPEN: + + stream->common.mode = mode; + stream->common.swap = FALSE; + stream->common.eol = 0; + stream->common.eof = FALSE; + stream->common.buffer = NULL; + stream->common.buffer_pos = 0; + stream->common.buffer_len = 0; + stream->common.no_fionread = FALSE; + stream->common.no_lseek = FALSE; + stream->common.standard = FALSE; + stream->common.blocking = TRUE; + stream->common.available_now = FALSE; + stream->common.redirected = FALSE; + stream->common.redirect = NULL; + stream->common.no_read_ahead = FALSE; + + if ((*(sclass->open))(stream, path, mode, NULL)) + THROW_SYSTEM(errno, path); + + stream->type = sclass; + + fd = STREAM_handle(stream); + if (fd >= 0) + fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | O_CLOEXEC); + + #if DEBUG_STREAM + _tag++; + stream->common.tag = _tag; + _nopen++; + fprintf(stderr, "Open %p [%d] (%d)\n", stream, _tag, _nopen); + #endif +} + +static void release_buffer(STREAM *stream) +{ + if (stream->common.buffer) + { + #if DEBUG_STREAM + fprintf(stderr, "Stream %p [%d]: Free buffer\n", stream, stream->common.tag); + #endif + FREE(&stream->common.buffer); + stream->common.buffer_pos = 0; + stream->common.buffer_len = 0; + } +} + +void STREAM_release(STREAM *stream) +{ + release_buffer(stream); + STREAM_cancel(stream); +} + +static void stop_watching(STREAM *stream, int mode) +{ + int fd = STREAM_handle(stream); + if (fd >= 0) + GB_Watch(fd, mode, NULL, 0); +} + +void STREAM_close(STREAM *stream) +{ + STREAM_release(stream); + + if (!stream->type) + return; + + stop_watching(stream, GB_WATCH_NONE); + + if (!stream->common.standard) + { + if ((*(stream->type->close))(stream)) + { + if (errno != EBADF && errno != EINPROGRESS && errno != EAGAIN) + THROW_SYSTEM(errno, ""); + } + } + + stream->type = NULL; + + #if DEBUG_STREAM + _nopen--; + fprintf(stderr, "Close %p [%d] (%d)\n", stream, stream->common.tag, _nopen); + #endif +} + + +void STREAM_flush(STREAM *stream) +{ + STREAM_end(stream); + + if (!stream->type) + THROW(E_CLOSED); + + (*(stream->type->flush))(stream); +} + +static int read_buffer(STREAM *stream, void *addr, int len) +{ + int l = stream->common.buffer_len - stream->common.buffer_pos; + if (l > len) + l = len; + if (l > 0) + { + memcpy(addr, stream->common.buffer + stream->common.buffer_pos, l); + stream->common.buffer_pos += l; + return l; + } + else + return 0; +} + +int STREAM_read(STREAM *stream, void *addr, int len) +{ + int eff = 0; + int n; + + if (STREAM_is_closed(stream)) + THROW(E_CLOSED); + + if (len <= 0) + return 0; + + if (stream->common.buffer) + { + eff = read_buffer(stream, addr, len); + addr += eff; + len -= eff; + } + + while (len > 0) + { + n = (*(stream->type->read))(stream, addr, len); + + if ((n <= 0) && errno != EINTR) + { + stop_watching(stream, GB_WATCH_READ); + + if (n == 0) + THROW(E_EOF); + + switch(errno) + { + case 0: + case EAGAIN: + THROW(E_EOF); + case EIO: + THROW(E_READ); + default: + THROW_SYSTEM(errno, NULL); + } + } + + eff += n; + addr += n; + len -= n; + } + + return eff; +} + +#if 0 +char STREAM_getchar(STREAM *stream) +{ + char c = 0; + bool ret; + + if (!stream->type) + THROW(E_CLOSED); + + if (stream->common.buffer && stream->common.buffer_pos < stream->common.buffer_len) + return stream->common.buffer[stream->common.buffer_pos++]; + + for(;;) + { + if (stream->type->getchar) + ret = (*(stream->type->getchar))(stream, &c); + else + ret = (*(stream->type->read))(stream, &c, 1); + + if (ret == 1) + break; + + if (errno == EINTR) + continue; + + stop_watching(stream, GB_WATCH_READ); + + switch(errno) + { + case 0: + case EAGAIN: + THROW(E_EOF); + case EIO: + THROW(E_READ); + default: + THROW_SYSTEM(errno, NULL); + } + } + + return c; +} +#endif + + +int STREAM_read_max(STREAM *stream, void *addr, int len) +{ + int eff = 0; + int n; + int flags, handle; + int save_errno; + + if (!stream->type) + THROW(E_CLOSED); + + if (len <= 0) + return 0; + + if (stream->common.buffer) + { + eff += read_buffer(stream, addr, len); + addr += eff; + len -= eff; + } + + while (len > 0) + { + if (stream->common.available_now) + { + n = (*(stream->type->read))(stream, addr, len); + } + else + { + handle = STREAM_handle(stream); + flags = fcntl(handle, F_GETFL); + if ((flags & O_NONBLOCK) == 0) + { + wait_for_fd_ready_to_read(handle); + fcntl(handle, F_SETFL, flags | O_NONBLOCK); + } + + errno = 0; + n = (*(stream->type->read))(stream, addr, len); + save_errno = errno; + + if ((flags & O_NONBLOCK) == 0) + fcntl(handle, F_SETFL, flags); + + errno = save_errno; + } + + if (n > 0) + { + eff += n; + break; + } + + if (n <= 0 && errno != EINTR) + { + if (n == 0 || errno == 0) + { + stop_watching(stream, GB_WATCH_READ); + return eff; + } + + switch(errno) + { + case EAGAIN: + case EIO: + return eff; + default: + THROW_SYSTEM(errno, NULL); + } + } + + addr += n; + len -= n; + } + + return eff; +} + + +void STREAM_write(STREAM *stream, void *addr, int len) +{ + int n; + + if (STREAM_is_closed_for_writing(stream)) + THROW(E_CLOSED); + + if (len <= 0) + return; + + if (stream->common.redirected) + stream = stream->common.redirect; + + do + { + n = ((*(stream->type->write))(stream, addr, len)); + + if (n <= 0 && errno != EINTR) + { + switch(errno) + { + case 0: + case EIO: + THROW(E_WRITE); + default: + THROW_SYSTEM(errno, NULL); + } + } + + addr += n; + len -= n; + + if (STREAM_is_closed_for_writing(stream)) + THROW(E_CLOSED); + } + while (len > 0); + +} + +void STREAM_write_zeros(STREAM *stream, int len) +{ + static const char buffer[32] = { 0 }; + int lenw; + + while (len > 0) + { + lenw = Min(len, sizeof(buffer)); + STREAM_write(stream, (void *)buffer, lenw); + len -= lenw; + } +} + +void STREAM_write_eol(STREAM *stream) +{ + if (STREAM_is_closed_for_writing(stream)) + THROW(E_CLOSED); + + switch(stream->common.eol) + { + case ST_EOL_UNIX: STREAM_write(stream, "\n", 1); break; + case ST_EOL_WINDOWS: STREAM_write(stream, "\r\n", 2); break; + case ST_EOL_MAC: STREAM_write(stream, "\r", 1); break; + } +} + + +int64_t STREAM_tell(STREAM *stream) +{ + int64_t pos; + + if (STREAM_is_closed(stream)) + THROW(E_CLOSED); + + if (stream->type->tell(stream, &pos)) + /*THROW(E_SEEK, ERROR_get());*/ + THROW_SYSTEM(errno, NULL); + + return pos; +} + + +void STREAM_seek(STREAM *stream, int64_t pos, int whence) +{ + if (STREAM_is_closed(stream)) + THROW(E_CLOSED); + + if (stream->type->seek(stream, pos, whence)) + { + switch(errno) + { + case EINVAL: + THROW(E_ARG); + default: + THROW_SYSTEM(errno, NULL); + } + } + + release_buffer(stream); +} + +static int fill_buffer(STREAM *stream, char *addr, bool do_not_wait_ready) +{ + int n; + int flags, fd; + int len; + int eff = 0; + + fd = STREAM_handle(stream); + len = STREAM_BUFFER_SIZE; + + while (len > 0) + { + if (stream->common.available_now) + n = (*(stream->type->read))(stream, addr, len); + else + { + if (!do_not_wait_ready) + { + wait_for_fd_ready_to_read(fd); + do_not_wait_ready = TRUE; + } + + flags = fcntl(fd, F_GETFL); + if ((flags & O_NONBLOCK) == 0) + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + + n = (*(stream->type->read))(stream, addr, len); + + if ((flags & O_NONBLOCK) == 0) + { + int save_errno = errno; + fcntl(fd, F_SETFL, flags); + errno = save_errno; + } + } + + if (n == 0) + return eff; + + if (n > 0) + eff += n; + + if (n < 0 && errno != EINTR) + { + switch(errno) + { + case EAGAIN: + case EINPROGRESS: + case EIO: + return eff; + default: + THROW_SYSTEM(errno, NULL); + } + } + + addr += n; + len -= n; + } + + return eff; +} + +bool STREAM_read_ahead(STREAM *stream) +{ + int eff; + + if (stream->common.no_read_ahead) + return FALSE; + + if (stream->common.buffer && stream->common.buffer_pos < stream->common.buffer_len) + return FALSE; + + if (!stream->common.buffer) + ALLOC(&stream->common.buffer, STREAM_BUFFER_SIZE); + + eff = fill_buffer(stream, stream->common.buffer, TRUE); + + stream->common.buffer_pos = 0; + stream->common.buffer_len = eff; + + if (eff == 0) + { + stream->common.eof = TRUE; + return TRUE; + } + else + { + stream->common.eof = FALSE; + return FALSE; + } +} + + +static char *input(STREAM *stream, bool line, char *escape) +{ + unsigned char mode; + int len = 0; + int start; + unsigned char c = 0, lc = 0; + void *test; + char *buffer; + int buffer_len; + int buffer_pos; + char *addr; + char ec; + bool inside_escape = FALSE; + + addr = NULL; + + mode = 2; + ec = escape ? *escape : 0; + + if (!line) + test = &&__TEST_INPUT; + else + { + switch(stream->common.eol) + { + case GB_EOL_WINDOWS: test = &&__TEST_WINDOWS; break; + case GB_EOL_MAC: test = &&__TEST_MAC; break; + default: test = NULL; mode = ec ? 1 : 0; break; + } + } + + stream->common.eof = FALSE; + + if (!STREAM_is_blocking(stream) && STREAM_eof(stream)) + THROW(E_EOF); + + buffer = stream->common.buffer; + buffer_len = stream->common.buffer_len; + buffer_pos = stream->common.buffer_pos; + + if (!buffer) + { + #if DEBUG_STREAM + fprintf(stderr, "Stream %p [%d]: Alloc buffer\n", stream, stream->common.tag); + #endif + ALLOC(&buffer, STREAM_BUFFER_SIZE); + buffer_pos = 0; + buffer_len = 0; + } + + start = buffer_pos; + + for(;;) + { + if (mode == 0) + { + while (buffer_pos < buffer_len) + { + c = buffer[buffer_pos++]; //STREAM_getchar(stream); + + if (c == '\n') + { + len = buffer_pos - start - 1; + if (buffer_pos) + lc = buffer[buffer_pos - 1]; + if (lc == '\r') + len--; + goto __FINISH; + } + } + + len = buffer_len - start; + } + else if (mode == 1) + { + while (buffer_pos < buffer_len) + { + c = buffer[buffer_pos++]; //STREAM_getchar(stream); + + if (c == ec) + inside_escape = !inside_escape; + else if (!inside_escape) + { + if (c == '\n') + { + len = buffer_pos - start - 1; + if (buffer_pos) + lc = buffer[buffer_pos - 1]; + if (lc == '\r') + len--; + goto __FINISH; + } + } + } + + len = buffer_pos - start; + } + else + { + while (buffer_pos < buffer_len) + { + c = buffer[buffer_pos++]; //STREAM_getchar(stream); + + if (ec && c == ec) + { + inside_escape = !inside_escape; + continue; + } + + if (inside_escape) + continue; + + goto *test; + + __TEST_INPUT: + + if (c <= ' ') + { + len = buffer_pos - start - 1; + goto __FINISH; + } + else + continue; + + __TEST_MAC: + + if (c == '\r') + { + len = buffer_pos - start - 1; + goto __FINISH; + } + else + continue; + + __TEST_WINDOWS: + + if ((lc == '\r') && (c == '\n')) + { + len = buffer_pos - start - 2; + goto __FINISH; + } + else + { + lc = c; + continue; + } + } + + len = buffer_pos - start; + } + + lc = c; + + if (len) + { + if (!addr) + addr = STRING_new(buffer + start, len); + else + addr = STRING_add(addr, buffer + start, len); + len = 0; + } + + stream->common.buffer = buffer; + stream->common.buffer_pos = buffer_pos; + stream->common.buffer_len = buffer_len; + + buffer_pos = 0; + buffer_len = fill_buffer(stream, buffer, FALSE); + + if (!buffer_len) + { + stream->common.eof = TRUE; + break; + } + + start = 0; + } + +__FINISH: + + if (len > 0) + { + if (!addr) + addr = STRING_new(buffer + start, len); + else + addr = STRING_add(addr, buffer + start, len); + } + else if (len < 0) + addr = STRING_extend(addr, STRING_length(addr) + len); + + stream->common.buffer = buffer; + stream->common.buffer_pos = buffer_pos; + stream->common.buffer_len = buffer_len; + + return addr; +} + + +char *STREAM_line_input(STREAM *stream, char *escape) +{ + return input(stream, TRUE, escape); +} + + +char *STREAM_input(STREAM *stream) +{ + return input(stream, FALSE, NULL); +} + + +static int read_length(STREAM *stream) +{ + union + { + unsigned char _data[4]; + short _short; + int _int; + } + buffer; + + int len = 0; + + STREAM_read(stream, buffer._data, 1); + + switch (buffer._data[0] >> 6) + { + case 0: + case 1: + len = buffer._data[0]; + break; + + case 2: + STREAM_read(stream, &buffer._data[1], 1); + buffer._data[0] &= 0x3F; + + if (!EXEC_big_endian) + SWAP_short(&buffer._short); + + len = buffer._short; + break; + + case 3: + STREAM_read(stream, &buffer._data[1], 3); + buffer._data[0] &= 0x3F; + + if (!EXEC_big_endian) + SWAP_int(&buffer._int); + + len = buffer._int; + break; + } + + return len; +} + +static STREAM *enter_temp_stream(STREAM *stream) +{ + if (stream != &_temp_stream) + { + _temp_save = stream; + _temp_level = 0; + if (_temp_stream.type) + STREAM_close(&_temp_stream); + STREAM_open(&_temp_stream, NULL, STO_STRING | STO_WRITE); + } + + _temp_level++; + + return &_temp_stream; +} + +static STREAM *leave_temp_stream(void) +{ + _temp_level--; + if (_temp_level > 0) + return &_temp_stream; + + STREAM_write(_temp_save, _temp_stream.string.buffer, STRING_length(_temp_stream.string.buffer)); + STREAM_close(&_temp_stream); + return _temp_save; +} + +static void read_structure(STREAM *stream, CLASS *class, char *base); + +static void read_value_ctype(STREAM *stream, CLASS *class, CTYPE ctype, void *addr) +{ + TYPE type; + VALUE temp; + + type = (TYPE)ctype.id; + if (type == T_OBJECT && ctype.value >= 0) + { + class = class->load->class_ref[ctype.value]; + if (CLASS_is_struct(class)) + { + CSTRUCT *structure = (CSTRUCT *)OBJECT_create(class, NULL, NULL, 0); + OBJECT_REF(structure); + read_structure(stream, class, (char *)structure + sizeof(CSTRUCT)); + *((void **)addr) = structure; + return; + } + } + else if (type == TC_STRUCT) + { + class = class->load->class_ref[ctype.value]; + read_structure(stream, class, addr); + return; + } + + STREAM_read_type(stream, type, &temp); + VALUE_class_write(class, &temp, addr, ctype); +} + +static void read_structure(STREAM *stream, CLASS *class, char *base) +{ + int i, n; + CLASS_DESC *desc; + char *addr; + CTYPE ctype; + + for (n = 0; n < class->n_desc; n++) + { + desc = class->table[n].desc; + ctype = desc->variable.ctype; + addr = base + desc->variable.offset; + + if (ctype.id == TC_STRUCT) + { + read_structure(stream, class->load->class_ref[ctype.value], addr); + } + else if (ctype.id == TC_ARRAY) + { + CLASS_ARRAY *adesc = class->load->array[ctype.value]; + int size = CLASS_sizeof_ctype(class, adesc->ctype); + + for (i = 0; i < CARRAY_get_static_count(adesc); i++) + { + read_value_ctype(stream, desc->variable.class, adesc->ctype, addr); + addr += size; + } + } + else + { + read_value_ctype(stream, desc->variable.class, ctype, addr); + } + } +} + +void STREAM_read_type(STREAM *stream, TYPE type, VALUE *value) +{ + bool variant; + int len; + + union + { + unsigned char _byte; + short _short; + int _int; + float _single; + unsigned char _data[4]; + } + buffer; + + variant = (type == T_VARIANT); + + if (variant || TYPE_is_object(type)) + { + if (TYPE_is_pure_object(type) && CLASS_is_struct((CLASS *)type)) + { + CLASS *class = (CLASS *)type; + CSTRUCT *structure = (CSTRUCT *)OBJECT_create(class, NULL, NULL, 0); + + read_structure(stream, class, (char *)structure + sizeof(CSTRUCT)); + + value->_object.class = class; + value->_object.object = structure; + + return; + } + + STREAM_read(stream, &buffer._byte, 1); + + if (buffer._byte == 0) + { + value->type = T_VARIANT; + value->_variant.vtype = T_NULL; + return; + } + + if (buffer._byte == 'A') + { + CARRAY *array; + int size, i; + VALUE temp; + void *data; + + STREAM_read(stream, &buffer._byte, 1); + type = (TYPE)buffer._byte; + if (type > T_OBJECT) + THROW(E_SERIAL); + + size = read_length(stream); + + GB_ArrayNew((GB_ARRAY *)&array, type, size); + for (i = 0; i < size; i++) + { + data = CARRAY_get_data(array, i); + STREAM_read_type(stream, type, &temp); + VALUE_write(&temp, data, type); + } + + value->type = T_VARIANT; + value->_variant.vtype = (TYPE)OBJECT_class(array); + value->_variant.value._object = array; + + return; + } + + if (buffer._byte == 'c' || buffer._byte == 'C') + { + GB_COLLECTION col; + int size, i; + VALUE temp; + char *key; + char tkey[32]; + int len; + + size = read_length(stream); + + GB_CollectionNew(&col, buffer._byte == 'c'); + for (i = 0; i < size; i++) + { + len = read_length(stream); + if (len < sizeof(tkey)) + key = tkey; + else + key = STRING_new(NULL, len); + STREAM_read(stream, key, len); + STREAM_read_type(stream, T_VARIANT, &temp); + GB_CollectionSet(col, key, len, (GB_VARIANT *)&temp); + if (len >= 32) + STRING_free_real(key); + } + + value->type = T_VARIANT; + value->_variant.vtype = (TYPE)OBJECT_class(col); + value->_variant.value._object = col; + + return; + } + + if (variant) + type = buffer._byte; + } + + value->type = type; + + switch (type) + { + case T_BOOLEAN: + + STREAM_read(stream, &buffer._byte, 1); + value->_integer.value = (buffer._byte != 0) ? (-1) : 0; + break; + + case T_BYTE: + + STREAM_read(stream, &buffer._byte, 1); + value->_integer.value = buffer._byte; + break; + + case T_SHORT: + + STREAM_read(stream, &buffer._short, sizeof(short)); + if (stream->common.swap) + SWAP_short(&buffer._short); + value->_integer.value = buffer._short; + break; + + case T_INTEGER: + + STREAM_read(stream, &value->_integer.value, sizeof(int)); + if (stream->common.swap) + SWAP_int(&value->_integer.value); + break; + + case T_LONG: + + STREAM_read(stream, &value->_long.value, sizeof(int64_t)); + if (stream->common.swap) + SWAP_int64(&value->_long.value); + break; + + case T_POINTER: + + STREAM_read(stream, &value->_pointer.value, sizeof(void *)); + if (stream->common.swap) + SWAP_pointer(&value->_pointer.value); + break; + + case T_SINGLE: + + STREAM_read(stream, &buffer._single, sizeof(float)); + if (stream->common.swap) + SWAP_float(&buffer._single); + value->_single.value = buffer._single; + break; + + case T_FLOAT: + + STREAM_read(stream, &value->_float.value, sizeof(double)); + if (stream->common.swap) + SWAP_double(&value->_float.value); + break; + + case T_DATE: + + STREAM_read(stream, &value->_date.date, sizeof(int)); + STREAM_read(stream, &value->_date.time, sizeof(int)); + if (stream->common.swap) + { + SWAP_int(&value->_date.date); + SWAP_int(&value->_date.time); + } + break; + + case T_CSTRING: + value->type = T_STRING; + // continue + + case T_STRING: + + if (stream->type == &STREAM_memory) + { + ssize_t slen; + if (CHECK_strlen(stream->memory.addr + stream->memory.pos, &slen)) + THROW(E_READ); + len = (int)slen; + } + else + len = read_length(stream); + + if (len > 0) + { + STRING_new_temp_value(value, NULL, labs(len)); + STREAM_read(stream, value->_string.addr, len); + } + else + { + STRING_void_value(value); + } + + break; + + default: + + THROW(E_TYPE, "Standard type", TYPE_get_name(type)); + } + + if (variant) + VALUE_convert_variant(value); +} + +static void write_length(STREAM *stream, int len) +{ + union + { + unsigned char _byte; + short _short; + int _int; + } + buffer; + + if (len < 0x80) + { + buffer._byte = (unsigned char)len; + STREAM_write(stream, &buffer._byte, 1); + } + else if (len < 0x4000) + { + buffer._short = (short)len | 0x8000; + if (!EXEC_big_endian) + SWAP_short(&buffer._short); + STREAM_write(stream, &buffer._short, sizeof(short)); + } + else + { + buffer._int = len | 0xC0000000; + if (!EXEC_big_endian) + SWAP_int(&buffer._int); + STREAM_write(stream, &buffer._int, sizeof(int)); + } +} + +void STREAM_write_type(STREAM *stream, TYPE type, VALUE *value) +{ + union + { + unsigned char _byte; + short _short; + int _int; + int64_t _long; + float _single; + double _float; + unsigned char _data[8]; + void *_pointer; + } + buffer; + + if (VALUE_is_null(value)) + { + buffer._byte = 0; + STREAM_write(stream, &buffer._byte, 1); + return; + } + + if (type == T_VARIANT) + { + VARIANT_undo(value); + type = value->type; + if (!TYPE_is_object(type)) + { + buffer._byte = (unsigned char)type; + STREAM_write(stream, &buffer._byte, 1); + } + } + + if (TYPE_is_object(type)) + type = T_OBJECT; + + switch (type) + { + case T_BOOLEAN: + + buffer._byte = value->_integer.value ? 0xFF : 0; + STREAM_write(stream, &buffer._byte, 1); + break; + + case T_BYTE: + + buffer._byte = (unsigned char)value->_integer.value; + STREAM_write(stream, &buffer._byte, 1); + break; + + case T_SHORT: + + buffer._short = (short)value->_integer.value; + if (stream->common.swap) + SWAP_short(&buffer._short); + STREAM_write(stream, &buffer._short, sizeof(short)); + break; + + case T_INTEGER: + + buffer._int = value->_integer.value; + if (stream->common.swap) + SWAP_int(&buffer._int); + STREAM_write(stream, &buffer._int, sizeof(int)); + break; + + case T_LONG: + + buffer._long = value->_long.value; + if (stream->common.swap) + SWAP_int64(&buffer._long); + STREAM_write(stream, &buffer._long, sizeof(int64_t)); + break; + + case T_POINTER: + + buffer._pointer = value->_pointer.value; + if (stream->common.swap) + SWAP_pointer(&buffer._pointer); + STREAM_write(stream, &buffer._pointer, sizeof(void *)); + break; + + case T_SINGLE: + + buffer._single = value->_single.value; + if (stream->common.swap) + SWAP_float(&buffer._single); + STREAM_write(stream, &buffer._single, sizeof(float)); + break; + + case T_FLOAT: + + buffer._float = value->_float.value; + if (stream->common.swap) + SWAP_double(&buffer._float); + STREAM_write(stream, &buffer._float, sizeof(double)); + break; + + case T_DATE: + + buffer._int = value->_date.date; + if (stream->common.swap) + SWAP_int(&buffer._int); + STREAM_write(stream, &buffer._int, sizeof(int)); + + buffer._int = value->_date.time; + if (stream->common.swap) + SWAP_int(&buffer._int); + STREAM_write(stream, &buffer._int, sizeof(int)); + + break; + + case T_STRING: + case T_CSTRING: + + if (stream->type == &STREAM_memory) + { + STREAM_write(stream, value->_string.addr + value->_string.start, value->_string.len); + buffer._byte = 0; + STREAM_write(stream, &buffer._byte, 1); + } + else + { + write_length(stream, value->_string.len); + STREAM_write(stream, value->_string.addr + value->_string.start, value->_string.len); + } + break; + + case T_OBJECT: + { + CLASS *class = OBJECT_class(value->_object.object); + void *structure; + + if (class->quick_array == CQA_ARRAY || class->is_array_of_struct) + { + CARRAY *array = (CARRAY *)value->_object.object; + VALUE temp; + void *data; + int i; + + if (OBJECT_is_locked((OBJECT *)array)) + THROW(E_SERIAL); + OBJECT_lock((OBJECT *)array, TRUE); + + if (!array->ref) + { + buffer._byte = 'A'; + STREAM_write(stream, &buffer._byte, 1); + + if (TYPE_is_object(array->type)) + buffer._byte = T_OBJECT; + else + buffer._byte = (unsigned char)array->type; + + STREAM_write(stream, &buffer._byte, 1); + + write_length(stream, array->count); + } + + if (class->is_array_of_struct) + { + for (i = 0; i < array->count; i++) + { + data = CARRAY_get_data(array, i); + structure = CSTRUCT_create_static(array, (CLASS *)array->type, data); + temp._object.class = OBJECT_class(structure); + temp._object.object = structure; + OBJECT_REF(structure); + STREAM_write_type(stream, T_OBJECT, &temp); + OBJECT_UNREF(structure); + } + } + else + { + for (i = 0; i < array->count; i++) + { + data = CARRAY_get_data(array, i); + VALUE_read(&temp, data, array->type); + STREAM_write_type(stream, array->type, &temp); + } + } + + OBJECT_lock((OBJECT *)array, FALSE); + break; + } + else if (class->quick_array == CQA_COLLECTION) + { + CCOLLECTION *col = (CCOLLECTION *)value->_object.object; + GB_COLLECTION_ITER iter; + char *key = NULL; + int len; + VALUE temp; + + if (OBJECT_is_locked((OBJECT *)col)) + THROW(E_SERIAL); + OBJECT_lock((OBJECT *)col, TRUE); + + buffer._byte = col->mode ? 'c' : 'C'; + STREAM_write(stream, &buffer._byte, 1); + + write_length(stream, CCOLLECTION_get_count(col)); + + GB_CollectionEnum(col, &iter, (GB_VARIANT *)&temp, NULL, NULL); + while (!GB_CollectionEnum(col, &iter, (GB_VARIANT *)&temp, &key, &len)) + { + write_length(stream, len); + STREAM_write(stream, key, len); + STREAM_write_type(stream, T_VARIANT, &temp); + } + + OBJECT_lock((OBJECT *)col, FALSE); + break; + } + else if (class->is_struct) + { + CSTRUCT *structure = (CSTRUCT *)value->_object.object; + int i; + CLASS_DESC *desc; + VALUE temp; + char *addr; + + stream = enter_temp_stream(stream); + + if (OBJECT_is_locked((OBJECT *)structure)) + THROW(E_SERIAL); + OBJECT_lock((OBJECT *)structure, TRUE); + + for (i = 0; i < class->n_desc; i++) + { + desc = class->table[i].desc; + + if (structure->ref) + addr = (char *)((CSTATICSTRUCT *)structure)->addr + desc->variable.offset; + else + addr = (char *)structure + sizeof(CSTRUCT) + desc->variable.offset; + + VALUE_class_read(desc->variable.class, &temp, (void *)addr, desc->variable.ctype, (void *)structure); + BORROW(&temp); + STREAM_write_type(stream, temp.type, &temp); + RELEASE(&temp); + } + + stream = leave_temp_stream(); + + OBJECT_lock((OBJECT *)structure, FALSE); + break; + } + // continue; + } + + default: + + THROW(E_TYPE, "Standard type", TYPE_get_name(type)); + } +} + + +void STREAM_load(const char *path, char **buffer, int *rlen) +{ + STREAM stream; + int64_t len; + + STREAM_open(&stream, path, STO_READ); + STREAM_lof(&stream, &len); + + if (len >> 31) + THROW(E_MEMORY); + + *rlen = len; + + ALLOC(buffer, *rlen); + + STREAM_read(&stream, *buffer, *rlen); + STREAM_close(&stream); +} + + +bool STREAM_map(const char *path, char **paddr, int *plen) +{ + STREAM stream; + int fd; + struct stat info; + void *addr; + size_t len; + bool ret = TRUE; + + STREAM_open(&stream, path, STO_READ + STO_DIRECT); + + if (stream.type == &STREAM_arch) + { + *paddr = (char *)stream.arch.arch->arch->addr + stream.arch.start; + *plen = stream.arch.size; + ret = FALSE; + goto __RETURN; + } + + fd = STREAM_handle(&stream); + if (fd < 0) + goto __RETURN; + + if (fstat(fd, &info) < 0) + goto __RETURN; + + len = info.st_size; + addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); + if (addr == MAP_FAILED) + goto __RETURN; + + *paddr = addr; + *plen = len; + ret = FALSE; + +__RETURN: + + STREAM_close(&stream); + return ret; +} + +void STREAM_unmap(char *addr, int len) +{ + if (addr && len > 0 && ARCHIVE_check_addr(addr)) + { + munmap(addr, len); + } +} + + +int STREAM_handle(STREAM *stream) +{ + if (STREAM_is_closed(stream)) + THROW(E_CLOSED); + + if (stream->type->handle) + return (*stream->type->handle)(stream); + else + return (-1); +} + + +bool STREAM_lock_all(STREAM *stream) +{ + int64_t pos; + int fd; + + if (STREAM_is_closed(stream)) + THROW(E_CLOSED); + + fd = STREAM_handle(stream); + if (fd < 0) + return TRUE; + + pos = lseek(fd, 0, SEEK_CUR); + if (pos < 0) + goto __ERROR; + + if (lseek(fd, 0, SEEK_SET) < 0) + goto __ERROR; + + #ifdef F_TLOCK + + if (lockf(fd, F_TLOCK, 0)) + { + if (errno == EAGAIN) + return TRUE; + else + goto __ERROR; + } + + #else + + ERROR_warning("locking is not implemented"); + + #endif + + if (lseek(fd, pos, SEEK_SET) < 0) + goto __ERROR; + + return FALSE; + +__ERROR: + THROW_SYSTEM(errno, NULL); + return TRUE; +} + + + +void STREAM_lof(STREAM *stream, int64_t *len) +{ + int fd; + int ilen; + + if (STREAM_is_closed(stream)) + THROW(E_CLOSED); + + *len = 0; + + if (stream->type->lof) + { + if (!(*(stream->type->lof))(stream, len)) + goto ADD_BUFFER; + } + + fd = STREAM_handle(stream); + if ((fd >= 0) && (STREAM_get_readable(stream, &ilen) == 0)) + *len = ilen; + +ADD_BUFFER: + + if (stream->common.buffer) + *len += stream->common.buffer_len - stream->common.buffer_pos; +} + +bool STREAM_eof(STREAM *stream) +{ + if (STREAM_is_closed(stream)) + THROW(E_CLOSED); + + if (stream->common.buffer && stream->common.buffer_pos < stream->common.buffer_len) + return FALSE; + + if (stream->type->eof) + return ((*(stream->type->eof))(stream)); + else + return STREAM_default_eof(stream); +} + + +#if 0 +int STREAM_read_direct(int fd, char *buffer, int len) +{ + ssize_t eff_read; + ssize_t len_read; + + while (len > 0) + { + len_read = Min(len, MAX_IO); + eff_read = read(fd, buffer, len_read); + + if (eff_read > 0) + { + STREAM_eff_read += eff_read; + len -= eff_read; + buffer += eff_read; + } + + if (eff_read < len_read) + { + if (eff_read == 0) + errno = 0; + if (eff_read <= 0 && errno != EINTR) + return TRUE; + } + } + + return FALSE; +} + +int STREAM_write_direct(int fd, char *buffer, int len) +{ + ssize_t eff_write; + ssize_t len_write; + + while (len > 0) + { + len_write = Min(len, MAX_IO); + eff_write = write(fd, buffer, len_write); + + if (eff_write < len_write) + { + if (eff_write <= 0 && errno != EINTR) + return TRUE; + } + + len -= eff_write; + buffer += eff_write; + } + + return FALSE; +} +#endif + +void STREAM_blocking(STREAM *stream, bool block) +{ + int fd = STREAM_handle(stream); + + if (fd < 0) + return; + + stream->common.blocking = block; + + if (block) + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK); + else + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); +} + +void STREAM_check_blocking(STREAM *stream) +{ + int fd = STREAM_handle(stream); + + stream->common.blocking = (fd < 0) ? TRUE : ((fcntl(fd, F_GETFL) & O_NONBLOCK) == 0); +} + +void STREAM_cancel(STREAM *stream) +{ + if (!stream->common.redirect) + return; + + STREAM_close(stream->common.redirect); + FREE(&stream->common.redirect); + stream->common.redirected = FALSE; +} + +void STREAM_begin(STREAM *stream) +{ + STREAM_cancel(stream); + + if (!stream->common.redirect) + { + ALLOC_ZERO(&stream->common.redirect, sizeof(STREAM)); + STREAM_open(stream->common.redirect, NULL, STO_STRING | STO_WRITE); + } + + stream->common.redirected = TRUE; +} + +void STREAM_end(STREAM *stream) +{ + if (!stream->common.redirect) + return; + + stream->common.redirected = FALSE; + STREAM_write(stream, stream->common.redirect->string.buffer, STRING_length(stream->common.redirect->string.buffer)); + stream->common.redirected = TRUE; + STREAM_cancel(stream); +} + diff --git a/main/gbx/gbx_stream.h b/main/gbx/gbx_stream.h new file mode 100644 index 00000000..691bc4a0 --- /dev/null +++ b/main/gbx/gbx_stream.h @@ -0,0 +1,253 @@ +/*************************************************************************** + + gbx_stream.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_STREAM_H +#define __GBX_STREAM_H + +#include "gbx_value.h" +#include "gbx_archive.h" + +union STREAM; + +typedef + struct STREAM_CLASS { + int (*open)(union STREAM *stream, const char *path, int mode, void *data); + int (*close)(union STREAM *stream); + int (*read)(union STREAM *stream, char *buffer, int len); + int (*write)(union STREAM *stream, char *buffer, int len); + int (*seek)(union STREAM *stream, int64_t pos, int whence); + int (*tell)(union STREAM *stream, int64_t *pos); + int (*flush)(union STREAM *stream); + int (*eof)(union STREAM *stream); + int (*lof)(union STREAM *stream, int64_t *len); + int (*handle)(union STREAM *stream); + } + STREAM_CLASS; + +typedef + struct { + STREAM_CLASS *type; + short mode; + unsigned swap : 1; + unsigned eol : 2; + unsigned eof : 1; + unsigned no_fionread : 1; + unsigned no_lseek : 1; + unsigned available_now : 1; + unsigned standard : 1; + unsigned blocking : 1; + unsigned redirected : 1; + unsigned no_read_ahead : 1; + #if DEBUG_STREAM + unsigned tag : 5; + #else + unsigned _reserved : 5; + #endif + short buffer_pos; + short buffer_len; + char *buffer; + union STREAM *redirect; + } + STREAM_COMMON; + +typedef + struct { + STREAM_COMMON common; + int _reserved[6]; + } + STREAM_RESERVED; + +typedef + struct { + STREAM_COMMON common; + int64_t size; + int fd; + unsigned watch : 1; + unsigned has_size : 1; + unsigned use_size : 1; + } + STREAM_DIRECT; + +typedef + struct { + STREAM_COMMON common; + FILE *file; + } + STREAM_BUFFER; + +typedef + struct { + STREAM_COMMON common; + void *addr; + intptr_t pos; + } + STREAM_MEMORY; + +typedef + struct { + STREAM_COMMON common; + ARCHIVE *arch; + int size; + int start; + int pos; + } + STREAM_ARCH; + +typedef + struct { + STREAM_COMMON common; + void *process; + } + STREAM_PROCESS; + +typedef + struct { + STREAM_COMMON common; + char *buffer; + int pos; + int size; + } + STREAM_STRING; + +typedef + union STREAM { + STREAM_CLASS *type; + STREAM_COMMON common; + STREAM_RESERVED _reserved; + STREAM_DIRECT direct; + STREAM_BUFFER buffer; + STREAM_DIRECT pipe; + STREAM_MEMORY memory; + STREAM_ARCH arch; + STREAM_PROCESS process; + STREAM_STRING string; + } + STREAM; + +enum { + STO_READ = (1 << 0), + STO_WRITE = (1 << 1), + STO_READ_WRITE = STO_READ + STO_WRITE, + STO_MODE = 0x3, + STO_APPEND = (1 << 2), + STO_CREATE = (1 << 3), + STO_ACCESS = 0xF, + STO_DIRECT = (1 << 4), + STO_LOCK = (1 << 5), + STO_WATCH = (1 << 6), + STO_PIPE = (1 << 7), + STO_MEMORY = (1 << 8), + STO_STRING = (1 << 9) + }; + +enum { + ST_EOL_UNIX = 0, + ST_EOL_WINDOWS = 1, + ST_EOL_MAC = 2 + }; + +//EXTERN int STREAM_eff_read; + +#ifndef __STREAM_IMPL_C + +EXTERN STREAM_CLASS STREAM_direct; +EXTERN STREAM_CLASS STREAM_lock; +EXTERN STREAM_CLASS STREAM_buffer; +EXTERN STREAM_CLASS STREAM_pipe; +EXTERN STREAM_CLASS STREAM_memory; +EXTERN STREAM_CLASS STREAM_arch; +EXTERN STREAM_CLASS STREAM_process; +EXTERN STREAM_CLASS STREAM_string; +/*EXTERN STREAM_CLASS STREAM_null;*/ + +#else + +#define DECLARE_STREAM(stream) \ +STREAM_CLASS stream = \ +{ \ + (void *)stream_open, \ + (void *)stream_close, \ + (void *)stream_read, \ + (void *)stream_write, \ + (void *)stream_seek, \ + (void *)stream_tell, \ + (void *)stream_flush, \ + (void *)stream_eof, \ + (void *)stream_lof, \ + (void *)stream_handle \ +} + +#endif + +#define STREAM_BUFFER_SIZE 1024 + +bool STREAM_in_archive(const char *path); +//int STREAM_get_readable(int fd, long *len); + +void STREAM_open(STREAM *stream, const char *path, int mode); + +void STREAM_release(STREAM *stream); +void STREAM_close(STREAM *stream); +void STREAM_write(STREAM *stream, void *addr, int len); +char *STREAM_line_input(STREAM *stream, char *escape); +char *STREAM_input(STREAM *stream); +int64_t STREAM_tell(STREAM *stream); +void STREAM_seek(STREAM *stream, int64_t pos, int whence); +int STREAM_read(STREAM *stream, void *addr, int len); +int STREAM_read_max(STREAM *stream, void *addr, int len); +bool STREAM_read_ahead(STREAM *stream); +//char STREAM_getchar(STREAM *stream); +void STREAM_read_type(STREAM *stream, TYPE type, VALUE *value); +void STREAM_write(STREAM *stream, void *addr, int len); +void STREAM_write_zeros(STREAM *stream, int len); +void STREAM_write_type(STREAM *stream, TYPE type, VALUE *value); +void STREAM_write_eol(STREAM *stream); +void STREAM_flush(STREAM *stream); +int STREAM_handle(STREAM *stream); +void STREAM_lof(STREAM *stream, int64_t *len); +bool STREAM_eof(STREAM *stream); +bool STREAM_default_eof(STREAM *stream); + +void STREAM_load(const char *path, char **buffer, int *len); + +bool STREAM_map(const char *path, char **paddr, int *plen); +void STREAM_unmap(char *addr, int len); + +bool STREAM_lock_all(STREAM *stream); + +#define STREAM_is_closed(_stream) ((_stream)->type == NULL) +#define STREAM_is_closed_for_writing(_stream) (STREAM_is_closed(_stream) && !(_stream)->common.redirected) + +void STREAM_blocking(STREAM *stream, bool block); +#define STREAM_is_blocking(_stream) ((_stream)->common.blocking) +void STREAM_check_blocking(STREAM *stream); + +int STREAM_get_readable(STREAM *stream, int *len); + +void STREAM_exit(void); + +void STREAM_begin(STREAM *stream); +void STREAM_cancel(STREAM *stream); +void STREAM_end(STREAM *stream); + +#endif diff --git a/main/gbx/gbx_stream_arch.c b/main/gbx/gbx_stream_arch.c new file mode 100644 index 00000000..296ed3bc --- /dev/null +++ b/main/gbx/gbx_stream_arch.c @@ -0,0 +1,165 @@ +/*************************************************************************** + + gbx_stream_arch.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STREAM_IMPL_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" + +#include +#include +#include +#include +#include +#include + +#include "gbx_archive.h" +#include "gbx_stream.h" + + +static int stream_open(STREAM *stream, const char *path, int mode) +{ + ARCHIVE_FIND find; + + if (ARCHIVE_get(NULL, &path, &find)) + { + errno = ENOENT; + return TRUE; + } + + if (find.pos < 0) + { + errno = EISDIR; + return TRUE; + } + + if ((mode & STO_ACCESS) != STO_READ) + { + errno = EACCES; + return TRUE; + } + + stream->common.available_now = TRUE; + stream->arch.arch = find.arch; + stream->arch.size = find.len; + stream->arch.start = find.pos; + stream->arch.pos = 0; + + return FALSE; +} + + +static int stream_close(STREAM *stream) +{ + return FALSE; +} + + +static int stream_read(STREAM *stream, char *buffer, int len) +{ + int max; + + max = stream->arch.size - stream->arch.pos; + + if (len > max) + { + len = max; + errno = 0; + } + + ARCHIVE_read(stream->arch.arch, stream->arch.start + stream->arch.pos, buffer, len); + stream->arch.pos += len; + return len; +} + + +static int stream_write(STREAM *stream, char *buffer, int len) +{ + return -1; +} + + +static int stream_seek(STREAM *stream, int64_t pos, int whence) +{ + int64_t new_pos; + + switch(whence) + { + case SEEK_SET: + new_pos = pos; + break; + + case SEEK_CUR: + new_pos = stream->arch.pos + pos; + break; + + case SEEK_END: + new_pos = stream->arch.size - pos; + break; + + default: + return TRUE; + } + + if (new_pos < 0 || new_pos > stream->arch.size) + return TRUE; + + stream->arch.pos = (int)new_pos; + return FALSE; +} + +static int stream_tell(STREAM *stream, int64_t *pos) +{ + *pos = stream->arch.pos; + return FALSE; +} + + +static int stream_flush(STREAM *stream) +{ + return FALSE; +} + + +static int stream_eof(STREAM *stream) +{ + return (stream->arch.pos >= stream->arch.size); +} + + +static int stream_lof(STREAM *stream, int64_t *len) +{ + *len = stream->arch.size; + return FALSE; +} + +static int stream_handle(STREAM *stream) +{ + return -1; +} + + +DECLARE_STREAM(STREAM_arch); + diff --git a/main/gbx/gbx_stream_buffer.c b/main/gbx/gbx_stream_buffer.c new file mode 100644 index 00000000..af5bbe1a --- /dev/null +++ b/main/gbx/gbx_stream_buffer.c @@ -0,0 +1,245 @@ +/*************************************************************************** + + gbx_stream_buffer.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STREAM_IMPL_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" + +#include +#include +#include +#include +#include +#include + +#include "gbx_exec.h" +#include "gbx_stream.h" + + +#define FD (stream->buffer.file) + + +static int stream_open(STREAM *stream, const char *path, int mode) +{ + FILE *file; + char *fmode; + struct stat info; + int fd; + + if (mode & STO_CREATE) + fmode = "w+"; + else if (mode & STO_APPEND) + fmode = "a+"; + else if (mode & STO_WRITE) + fmode = "r+"; + else + fmode = "r"; + + file = fopen(path, fmode); + if (file == NULL) + return TRUE; + + fd = fileno(file); + + if (fstat(fd, &info) < 0) + { + fclose(file); + return TRUE; + } + + if (S_ISDIR(info.st_mode)) + { + fclose(file); + errno = EISDIR; + return TRUE; + } + + //fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); + + stream->common.available_now = TRUE; + FD = file; + return FALSE; +} + + +static int stream_close(STREAM *stream) +{ + FILE *f = FD; + + if (!f) + return TRUE; + + FD = NULL; + + return fclose(f) < 0; +} + + +static int stream_read(STREAM *stream, char *buffer, int len) +{ + int eff; + + if (!FD) + return TRUE; + + eff = (int)fread(buffer, 1, len, FD); + if (eff < len) + { + if (ferror(FD) == 0) + errno = 0; + } + + return eff; + + /* + while (len > 0) + { + len_read = Min(len, MAX_IO); + eff_read = fread(buffer, 1, len_read, FD); + + if (eff_read > 0) + { + STREAM_eff_read += eff_read; + len -= eff_read; + buffer += eff_read; + } + + if (eff_read < len_read) + { + if (feof(FD)) + { + errno = 0; + return TRUE; + } + if (ferror(FD) && errno != EINTR) + return TRUE; + } + } + + return FALSE; + */ +} + + +static int stream_flush(STREAM *stream) +{ + if (!FD) + return TRUE; + + return (fflush(FD) != 0); +} + + +static int stream_write(STREAM *stream, char *buffer, int len) +{ + if (!FD) + return TRUE; + + return fwrite(buffer, 1, len, FD); + + /*while (len > 0) + { + len_write = Min(len, MAX_IO); + eff_write = fwrite(buffer, 1, len_write, FD); + + if (eff_write < len_write) + { + if (ferror(FD) && errno != EINTR) + return TRUE; + } + + len -= eff_write; + buffer += eff_write; + } + + if (EXEC_debug) + return stream_flush(stream); + else + return FALSE;*/ +} + + +static int stream_seek(STREAM *stream, int64_t pos, int whence) +{ + if (!FD) + return TRUE; + + return (fseek(FD, (off_t)pos, whence) != 0); +} + + +static int stream_tell(STREAM *stream, int64_t *pos) +{ + if (!FD) + return TRUE; + + *pos = (int64_t)ftell(FD); + return (*pos < 0); +} + + +static int stream_eof(STREAM *stream) +{ + int c; + + if (!FD) + return TRUE; + + c = fgetc(FD); + if (c == EOF) + return TRUE; + + ungetc(c, FD); + return FALSE; +} + + +static int stream_lof(STREAM *stream, int64_t *len) +{ + struct stat info; + + if (!stream->common.available_now) + return TRUE; + + if (!FD || fstat(fileno(FD), &info) < 0) + return TRUE; + + *len = info.st_size; + return FALSE; +} + + +static int stream_handle(STREAM *stream) +{ + if (FD) + return fileno(FD); + else + return -1; +} + + + +DECLARE_STREAM(STREAM_buffer); diff --git a/main/gbx/gbx_stream_direct.c b/main/gbx/gbx_stream_direct.c new file mode 100644 index 00000000..ea6705f6 --- /dev/null +++ b/main/gbx/gbx_stream_direct.c @@ -0,0 +1,223 @@ +/*************************************************************************** + + gbx_stream_direct.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STREAM_IMPL_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" +#include "gbx_number.h" + +#include +#include +#include +#include +#include +#include + +#include "gbx_stream.h" + + +#define FD (stream->direct.fd) + + +static int stream_open(STREAM *stream, const char *path, int mode) +{ + int fd; + struct stat info; + int fmode, omode; + VALUE val; + + if (mode & STO_CREATE) + fmode = O_CREAT | O_TRUNC; // | O_EXCL; + else if (mode & STO_APPEND) + fmode = O_APPEND | O_CREAT; + else + fmode = 0; + + switch (mode & STO_MODE) + { + case STO_READ: fmode |= O_RDONLY; break; + case STO_WRITE: fmode |= O_WRONLY; break; + case STO_READ_WRITE: fmode |= O_RDWR; break; + default: fmode |= O_RDONLY; + } + + if (path[0] == '.' && isdigit(path[1])) + { + if ((mode & STO_CREATE) || (mode & STO_APPEND)) + THROW(E_ACCESS); + + if (NUMBER_from_string(NB_READ_INTEGER, &path[1], strlen(path) - 1, &val) || val._integer.value < 0) + { + errno = ENOENT; + return TRUE; + } + + fd = val._integer.value; + omode = fcntl(fd, F_GETFL, NULL); + if (omode < 0) + return TRUE; + + if (((mode & STO_MODE) == STO_READ && (omode & O_ACCMODE) == O_WRONLY) + || ((mode & STO_MODE) == STO_WRITE && (omode & O_ACCMODE) == O_RDONLY) + || ((mode & STO_MODE) == STO_READ_WRITE && (omode & O_ACCMODE) != O_RDWR)) + THROW(E_ACCESS); + + stream->direct.watch = TRUE; + stream->common.no_read_ahead = TRUE; + } + else + { + stream->direct.watch = FALSE; + + fd = open(path, fmode, 0666); + if (fd < 0) + return TRUE; + + if (fstat(fd, &info) < 0) + { + close(fd); + return TRUE; + } + + if (S_ISDIR(info.st_mode)) + { + close(fd); + errno = EISDIR; + return TRUE; + } + + if (!S_ISREG(info.st_mode)) + { + stream->common.available_now = FALSE; + stream->common.no_read_ahead = TRUE; + fcntl(fd, F_SETFL, O_NONBLOCK); + } + else + stream->common.available_now = TRUE; + } + + stream->direct.has_size = FALSE; + + FD = fd; + return FALSE; +} + + +static int stream_close(STREAM *stream) +{ + if (!stream->direct.watch) + { + if (close(FD) < 0) + return TRUE; + } + + FD = -1; + return FALSE; +} + + +static int stream_read(STREAM *stream, char *buffer, int len) +{ + return read(FD, buffer, len); +} + +static int stream_write(STREAM *stream, char *buffer, int len) +{ + return write(FD, buffer, len); +} + + +static int stream_seek(STREAM *stream, int64_t pos, int whence) +{ + return (lseek(FD, pos, whence) < 0); +} + + +static int stream_tell(STREAM *stream, int64_t *pos) +{ + *pos = lseek(FD, 0, SEEK_CUR); + return (*pos < 0); +} + + +static int stream_flush(STREAM *stream) +{ + return FALSE; +} + + +static int stream_eof(STREAM *stream) +{ + struct stat info; + off_t pos; + + if (!stream->direct.has_size) + { + if (fstat(FD, &info) == 0) + { + stream->direct.use_size = TRUE; + stream->direct.size = info.st_size; + } + + stream->direct.has_size = TRUE; + } + + if (stream->direct.use_size && !stream->common.no_lseek) + { + pos = lseek(FD, 0, SEEK_CUR); + if (pos >= 0) + return pos >= stream->direct.size; + + stream->common.no_lseek = TRUE; + } + + return STREAM_default_eof(stream); +} + + +static int stream_lof(STREAM *stream, int64_t *len) +{ + struct stat info; + + if (!stream->common.available_now) + return TRUE; + + if (fstat(FD, &info) < 0) + return TRUE; + + *len = info.st_size; + return FALSE; +} + + +static int stream_handle(STREAM *stream) +{ + return FD; +} + + +DECLARE_STREAM(STREAM_direct); + diff --git a/main/gbx/gbx_stream_lock.c b/main/gbx/gbx_stream_lock.c new file mode 100644 index 00000000..4354f572 --- /dev/null +++ b/main/gbx/gbx_stream_lock.c @@ -0,0 +1,109 @@ +/*************************************************************************** + + gbx_stream_lock.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STREAM_IMPL_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" +#include "gbx_number.h" + +#include +#include +#include +#include +#include +#include + +#include "gbx_stream.h" + + +#define FD (stream->direct.fd) + + +static int stream_open(STREAM *stream, const char *path, int mode) +{ + int fd; + + stream->direct.watch = FALSE; + + fd = open(path, O_CREAT | O_WRONLY, 0666); + if (fd < 0) + return TRUE; + + FD = fd; + + return FALSE; +} + + +static int stream_close(STREAM *stream) +{ + if (close(FD) < 0) + return TRUE; + + FD = -1; + return FALSE; +} + + +static int stream_read(STREAM *stream, char *buffer, int len) +{ + return 0; +} + +static int stream_write(STREAM *stream, char *buffer, int len) +{ + return 0; +} + + +static int stream_seek(STREAM *stream, int64_t pos, int whence) +{ + return TRUE; +} + + +static int stream_tell(STREAM *stream, int64_t *pos) +{ + return TRUE; +} + + +static int stream_flush(STREAM *stream) +{ + return TRUE; +} + +#define stream_eof NULL +#define stream_lof NULL + +static int stream_handle(STREAM *stream) +{ + return FD; +} + + +DECLARE_STREAM(STREAM_lock); + diff --git a/main/gbx/gbx_stream_memory.c b/main/gbx/gbx_stream_memory.c new file mode 100644 index 00000000..362bf279 --- /dev/null +++ b/main/gbx/gbx_stream_memory.c @@ -0,0 +1,186 @@ +/*************************************************************************** + + gbx_stream_memory.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STREAM_IMPL_C + +#include "gb_common.h" +#include "gb_common_check.h" +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gbx_stream.h" + + +static int stream_open(STREAM *stream, const char *path, int mode) +{ + stream->memory.addr = (char *)path; + + if (stream->memory.addr == NULL) + { + stream->type = NULL; + THROW(E_ARG); + } + + stream->memory.pos = 0; + + stream->common.available_now = TRUE; + + return FALSE; +} + + +static int stream_close(STREAM *stream) +{ + /*if (munmap(stream->memory.addr, stream->memory.size) != 0) + return TRUE;*/ + + stream->memory.addr = NULL; + //stream->memory.size = 0; + return FALSE; +} + + +static int stream_read(STREAM *stream, char *buffer, int len) +{ + /*if ((stream->common.mode & STO_READ) == 0) + THROW(E_ACCESS);*/ + + CHECK_enter(); + + if (sigsetjmp(CHECK_jump, TRUE) == 0) + memmove(buffer, stream->memory.addr + stream->memory.pos, len); + + CHECK_leave(); + + if (CHECK_got_error()) + { + errno = EIO; + return -1; + } + else + { + stream->memory.pos += len; + return len; + } +} + +static int stream_write(STREAM *stream, char *buffer, int len) +{ + if ((stream->common.mode & STO_WRITE) == 0) + THROW(E_ACCESS); + + CHECK_enter(); + + if (sigsetjmp(CHECK_jump, TRUE) == 0) + memmove(stream->memory.addr + stream->memory.pos, buffer, len); + + CHECK_leave(); + + if (CHECK_got_error()) + { + errno = EIO; + return -1; + } + else + { + stream->memory.pos += len; + return len; + } +} + + +static int stream_seek(STREAM *stream, int64_t pos, int whence) +{ + int64_t new_pos; + + switch(whence) + { + case SEEK_SET: + new_pos = pos; + break; + + case SEEK_CUR: + new_pos = stream->memory.pos + pos; + break; + + case SEEK_END: + return TRUE; + + default: + return TRUE; + } + + if (new_pos < 0) + return TRUE; + + stream->memory.pos = new_pos; + return FALSE; +} + + +static int stream_tell(STREAM *stream, int64_t *pos) +{ + *pos = (int64_t)stream->memory.pos; + return FALSE; +} + + +static int stream_flush(STREAM *stream) +{ + return FALSE; +} + + +static int stream_eof(STREAM *stream) +{ + //return (stream->memory.pos >= stream->memory.size); + return FALSE; +} + + +static int stream_lof(STREAM *stream, int64_t *len) +{ + //*len = stream->memory.size; + //return FALSE; + *len = 0; + return TRUE; +} + + +static int stream_handle(STREAM *stream) +{ + return -1; +} + + +DECLARE_STREAM(STREAM_memory); diff --git a/main/gbx/gbx_stream_pipe.c b/main/gbx/gbx_stream_pipe.c new file mode 100644 index 00000000..e117426b --- /dev/null +++ b/main/gbx/gbx_stream_pipe.c @@ -0,0 +1,127 @@ +/*************************************************************************** + + gbx_stream_pipe.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STREAM_IMPL_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" + +#include +#include +#include +#include +#include +#include + +#include "gbx_stream.h" + + +#define FD (stream->direct.fd) + + +static int stream_open(STREAM *stream, const char *path, int mode) +{ + int fd; + int fmode; + + RESTART_SYSCALL(mkfifo(path, 0666)) + { + if (errno != EEXIST) + return TRUE; + } + + fmode = 0; + + switch (mode & STO_MODE) + { + case STO_READ: fmode |= O_RDONLY; break; + case STO_WRITE: fmode |= O_WRONLY; break; + case STO_READ_WRITE: fmode |= O_RDWR; break; + default: fmode |= O_RDONLY; + } + + RESTART_SYSCALL(fd = open(path, fmode)) + return TRUE; + + stream->direct.size = 0; + + FD = fd; + return FALSE; +} + + +static int stream_close(STREAM *stream) +{ + if (close(FD) < 0) + return TRUE; + + FD = -1; + return FALSE; +} + + +static int stream_read(STREAM *stream, char *buffer, int len) +{ + return read(FD, buffer, len); +} + + +static int stream_write(STREAM *stream, char *buffer, int len) +{ + return write(FD, buffer, len); +} + + +static int stream_seek(STREAM *stream, int64_t pos, int whence) +{ + return TRUE; +} + + +static int stream_tell(STREAM *stream, int64_t *pos) +{ + return TRUE; +} + + +static int stream_flush(STREAM *stream) +{ + return FALSE; +} + + +#define stream_eof NULL + +#define stream_lof NULL + + +static int stream_handle(STREAM *stream) +{ + return FD; +} + + +DECLARE_STREAM(STREAM_pipe); + diff --git a/main/gbx/gbx_stream_process.c b/main/gbx/gbx_stream_process.c new file mode 100644 index 00000000..ad55f9b0 --- /dev/null +++ b/main/gbx/gbx_stream_process.c @@ -0,0 +1,118 @@ +/*************************************************************************** + + gbx_stream_process.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STREAM_IMPL_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" + +#include +#include +#include +#include +#include +#include + +#include "gbx_c_process.h" + +#include "gbx_stream.h" + + +#define FDR ((CPROCESS *)stream->process.process)->out +#define FDW ((CPROCESS *)stream->process.process)->in + + +static int stream_open(STREAM *stream, const char *path, int mode, CPROCESS *process) +{ + stream->process.process = process; + STREAM_blocking(stream, FALSE); + return FALSE; +} + + +static int stream_close(STREAM *stream) +{ + if (FDW >= 0) + { + if (close(FDW) < 0) + return TRUE; + + FDW = -1; + } + + return FALSE; +} + + +static int stream_read(STREAM *stream, char *buffer, int len) +{ + return read(FDR, buffer, len); +} + +static int stream_write(STREAM *stream, char *buffer, int len) +{ + return write(FDW, buffer, len); +} + + +static int stream_seek(STREAM *stream, int64_t pos, int whence) +{ + if (FDR < 0) + return TRUE; + + return (lseek(FDR, pos, whence) < 0); +} + + +static int stream_tell(STREAM *stream, int64_t *pos) +{ + if (FDR < 0) + return TRUE; + + *pos = lseek(FDR, 0, SEEK_CUR); + return (*pos < 0); +} + + +static int stream_flush(STREAM *stream) +{ + return FALSE; +} + + +#define stream_eof NULL + + +#define stream_lof NULL + + +static int stream_handle(STREAM *stream) +{ + return FDR; +} + + +DECLARE_STREAM(STREAM_process); + diff --git a/main/gbx/gbx_stream_string.c b/main/gbx/gbx_stream_string.c new file mode 100644 index 00000000..20d58ad9 --- /dev/null +++ b/main/gbx/gbx_stream_string.c @@ -0,0 +1,140 @@ +/*************************************************************************** + + gbx_stream_string.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STREAM_IMPL_C + +#include "gb_common.h" +#include "gb_common_check.h" +#include "gb_error.h" +#include "gbx_value.h" +#include "gb_limit.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gbx_string.h" +#include "gbx_stream.h" + + +static int stream_open(STREAM *stream, const char *path, int mode) +{ + stream->string.buffer = NULL; + stream->common.available_now = TRUE; + stream->string.size = 0; + stream->string.pos = 0; + + return FALSE; +} + + +static int stream_close(STREAM *stream) +{ + STRING_unref(&stream->string.buffer); + return FALSE; +} + + +static int stream_read(STREAM *stream, char *buffer, int len) +{ + int max; + + max = stream->string.size - stream->string.pos; + + if (len > max) + { + len = max; + errno = 0; + } + + if (len > 0) + memcpy(buffer, stream->string.buffer + stream->string.pos, len); + + stream->string.pos += len; + return len; +} + +static int stream_write(STREAM *stream, char *buffer, int len) +{ + if ((stream->common.mode & STO_WRITE) == 0) + THROW(E_ACCESS); + + stream->string.buffer = STRING_add(stream->string.buffer, buffer, len); + + stream->string.size += len; + stream->string.pos = stream->string.size; + + return len; +} + + +static int stream_seek(STREAM *stream, int64_t pos, int whence) +{ + int ipos = (int)pos; + + if (pos != (int64_t)ipos || ipos < 0 || ipos > stream->string.size) + return TRUE; + + stream->string.pos = ipos; + return FALSE; +} + + +static int stream_tell(STREAM *stream, int64_t *pos) +{ + *pos = (int64_t)stream->string.pos; + return FALSE; +} + + +static int stream_flush(STREAM *stream) +{ + return FALSE; +} + + +static int stream_eof(STREAM *stream) +{ + return (stream->string.pos >= stream->string.size); +} + + +static int stream_lof(STREAM *stream, int64_t *len) +{ + *len = STRING_length(stream->string.buffer); + return FALSE; +} + + +static int stream_handle(STREAM *stream) +{ + return -1; +} + + +DECLARE_STREAM(STREAM_string); diff --git a/main/gbx/gbx_string.c b/main/gbx/gbx_string.c new file mode 100644 index 00000000..86dd2305 --- /dev/null +++ b/main/gbx/gbx_string.c @@ -0,0 +1,1324 @@ +/*************************************************************************** + + gbx_string.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __STRING_C + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_common_case.h" +#include "gb_common_string.h" + +#include "gb_error.h" +#include "gbx_value.h" +#include "gbx_debug.h" +#include "gbx_local.h" +#include "gbx_project.h" +#include "gbx_exec.h" + +#include +#include +#include +#include + +#include "gbx_string.h" + +//#define DEBUG_ME + +#if DEBUG_STRING +#define DEBUG_ME +#endif + +#if DEBUG_STRING +char *STRING_watch = NULL; +#endif + +/*#ifdef DEBUG_ME +extern FILE *MEMORY_log; +#undef stderr +#define stderr MEMORY_log +static void print_where() +{ + fprintf(MEMORY_log, "%s: ", DEBUG_get_current_position()); +} +#define DEBUG_where print_where +#endif*/ + +#if DEBUG_MEMORY + +static void *_my_malloc(int size) +{ + void *ptr; + ALLOC(&ptr, size); + return ptr; +} + +static void _my_free(void *ptr) +{ + IFREE(ptr); +} + +static void *_my_realloc(void *ptr, int size) +{ + REALLOC(&ptr, size); + return ptr; +} +#else + +#define _my_malloc my_malloc +#define _my_free my_free +#define _my_realloc my_realloc + +#endif + +#define STRING_last_count 32 +static char *STRING_last[STRING_last_count] = { 0 }; + +const char STRING_char_table[512] = +"\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0A\x00\x0B\x00\x0C\x00\x0D\x00\x0E\x00\x0F\x00" +"\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1A\x00\x1B\x00\x1C\x00\x1D\x00\x1E\x00\x1F\x00" +"\x20\x00\x21\x00\x22\x00\x23\x00\x24\x00\x25\x00\x26\x00\x27\x00\x28\x00\x29\x00\x2A\x00\x2B\x00\x2C\x00\x2D\x00\x2E\x00\x2F\x00" +"\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\x3A\x00\x3B\x00\x3C\x00\x3D\x00\x3E\x00\x3F\x00" +"\x40\x00\x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00\x47\x00\x48\x00\x49\x00\x4A\x00\x4B\x00\x4C\x00\x4D\x00\x4E\x00\x4F\x00" +"\x50\x00\x51\x00\x52\x00\x53\x00\x54\x00\x55\x00\x56\x00\x57\x00\x58\x00\x59\x00\x5A\x00\x5B\x00\x5C\x00\x5D\x00\x5E\x00\x5F\x00" +"\x60\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00\x69\x00\x6A\x00\x6B\x00\x6C\x00\x6D\x00\x6E\x00\x6F\x00" +"\x70\x00\x71\x00\x72\x00\x73\x00\x74\x00\x75\x00\x76\x00\x77\x00\x78\x00\x79\x00\x7A\x00\x7B\x00\x7C\x00\x7D\x00\x7E\x00\x7F\x00" +"\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8A\x00\x8B\x00\x8C\x00\x8D\x00\x8E\x00\x8F\x00" +"\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9A\x00\x9B\x00\x9C\x00\x9D\x00\x9E\x00\x9F\x00" +"\xA0\x00\xA1\x00\xA2\x00\xA3\x00\xA4\x00\xA5\x00\xA6\x00\xA7\x00\xA8\x00\xA9\x00\xAA\x00\xAB\x00\xAC\x00\xAD\x00\xAE\x00\xAF\x00" +"\xB0\x00\xB1\x00\xB2\x00\xB3\x00\xB4\x00\xB5\x00\xB6\x00\xB7\x00\xB8\x00\xB9\x00\xBA\x00\xBB\x00\xBC\x00\xBD\x00\xBE\x00\xBF\x00" +"\xC0\x00\xC1\x00\xC2\x00\xC3\x00\xC4\x00\xC5\x00\xC6\x00\xC7\x00\xC8\x00\xC9\x00\xCA\x00\xCB\x00\xCC\x00\xCD\x00\xCE\x00\xCF\x00" +"\xD0\x00\xD1\x00\xD2\x00\xD3\x00\xD4\x00\xD5\x00\xD6\x00\xD7\x00\xD8\x00\xD9\x00\xDA\x00\xDB\x00\xDC\x00\xDD\x00\xDE\x00\xDF\x00" +"\xE0\x00\xE1\x00\xE2\x00\xE3\x00\xE4\x00\xE5\x00\xE6\x00\xE7\x00\xE8\x00\xE9\x00\xEA\x00\xEB\x00\xEC\x00\xED\x00\xEE\x00\xEF\x00" +"\xF0\x00\xF1\x00\xF2\x00\xF3\x00\xF4\x00\xF5\x00\xF6\x00\xF7\x00\xF8\x00\xF9\x00\xFA\x00\xFB\x00\xFC\x00\xFD\x00\xFE\x00\xFF\x00"; + +static int _index = 0; + +STRING_MAKE STRING_make_buffer; +#define _make STRING_make_buffer + +static iconv_t _conv_unicode_utf8 = (iconv_t)-1; +static iconv_t _conv_utf8_unicode = (iconv_t)-1; + + +/**************************************************************************** + + String pool management + +****************************************************************************/ + +#define SIZE_INC 16 +#define SIZE_INC2 256 +#define REAL_SIZE(_len) ((_len) >= 256 ? (((_len) + (SIZE_INC2 - 1)) & ~(SIZE_INC2 - 1)) : (((_len) + (SIZE_INC - 1)) & ~(SIZE_INC - 1))) + +#define POOL_SIZE 16 +#define POOL_MAX 64 + +#define POOL_MAX_LEN (POOL_SIZE * SIZE_INC) + +static STRING *_pool[POOL_SIZE] = { 0 }; +static char _pool_count[POOL_SIZE] = { 0 }; + +#ifdef DEBUG_ME + +static STRING *alloc_string(int _len) \ +{ \ + STRING *str; \ + int size = REAL_SIZE((_len) + 1 + sizeof(STRING)); \ + int pool = (size / SIZE_INC) - 1; \ + \ + MEMORY_count++; \ + fprintf(stderr, "[%d]\n", MEMORY_count); \ + \ + if (pool < POOL_SIZE && (_pool_count[pool])) \ + { \ + str = _pool[pool]; \ + fprintf(stderr, "alloc_string: (%p) %d bytes from pool %d [%d]\n", str, size, pool, _pool_count[pool]); \ + _pool[pool] = *((STRING **)str); \ + _pool_count[pool]--; \ + } \ + else \ + { \ + str = _my_malloc(size); \ + if (!str) \ + THROW_MEMORY(); \ + } \ + str->len = (_len); \ + str->ref = 1; \ + return str; \ +} + +#else + +#define alloc_string(_len) \ +({ \ + STRING *str; \ + int pool = (((_len) + 1 + sizeof(STRING) + (SIZE_INC - 1)) / SIZE_INC) - 1; \ + \ + MEMORY_count++; \ + \ + if (pool < POOL_SIZE && (_pool_count[pool])) \ + { \ + str = _pool[pool]; \ + _pool[pool] = *((STRING **)str); \ + _pool_count[pool]--; \ + } \ + else \ + { \ + str = _my_malloc(REAL_SIZE((_len) + 1 + sizeof(STRING))); \ + if (!str) \ + THROW_MEMORY(); \ + } \ + str->len = (_len); \ + str->ref = 1; \ + str; \ +}) + +#endif + +extern char *STRING_utf8_current; + +void STRING_free_real(char *ptr) +{ + STRING *str = STRING_from_ptr(ptr); + int size = REAL_SIZE(str->len + 1 + sizeof(STRING)); + int pool = (size / SIZE_INC) - 1; + + if (STRING_utf8_current == ptr) + { + //fprintf(stderr, "free STRING_utf8_current (%p)\n", ptr); + STRING_utf8_current = NULL; + } + + MEMORY_count--; +#ifdef DEBUG_ME + fprintf(stderr, "[%d]\n", MEMORY_count); +#endif + + if (pool < POOL_SIZE) + { + if (_pool_count[pool] < POOL_MAX) + { + #ifdef DEBUG_ME + fprintf(stderr, "STRING_free_real: (%p / %p) %d bytes to pool %d\n", str, ptr, size, pool); + str->ref = 0x87654321; + str->len = 0x87654321; + #endif + *((STRING **)str) = _pool[pool]; + _pool[pool] = str; + _pool_count[pool]++; + return; + } + } + + _my_free(str); +} + +static STRING *realloc_string(STRING *str, int new_len) +{ + int size; + int new_size; + + if (new_len == str->len) + return str; + + size = REAL_SIZE(str->len + 1 + sizeof(STRING)); + new_size = REAL_SIZE(new_len + 1 + sizeof(STRING)); + + if (new_size != size) + { + if (new_len == 0) + { + STRING_free_real(str->data); + return NULL; + } + else if (size > POOL_MAX_LEN && new_size > POOL_MAX_LEN) + { + str = _my_realloc(str, new_size); + //REALLOC(&str, new_size, "realloc_string"); + } + else + { + STRING *nstr = alloc_string(new_len); + if (new_len < str->len) + memcpy(nstr->data, str->data, new_len); + else + memcpy(nstr->data, str->data, str->len); + if (str->ref == 1) + STRING_free_real(str->data); + else + str->ref--; + str = nstr; + } + } + + str->len = new_len; + return str; +} + +static void clear_pool(void) +{ + int i; + STRING *str, *next; + + for (i = 0; i < POOL_SIZE; i++) + { + #ifdef DEBUG_ME + fprintf(stderr, "clear_pool: clear pool #%d\n", i); + #endif + str = _pool[i]; + while (str) + { + next = *((STRING **)str); + #ifdef DEBUG_ME + fprintf(stderr, "%p\n", str); + #endif + _my_free(str); + str = next; + } + } +} + +/**************************************************************************** + + String routines + +****************************************************************************/ + +char *STRING_new(const char *src, int len) +{ + STRING *str; + char *data; + + if (len == 0) + return NULL; + + //ALLOC(&str, REAL_SIZE(len + 1 + sizeof(STRING)), "STRING_new"); + str = alloc_string(len); + data = str->data; + + if (src) + memcpy(data, src, len); + + data[len] = 0; + + #ifdef DEBUG_ME + DEBUG_where(); + fprintf(stderr, "STRING_new %p ( 0 ) \"%.*s\"\n", data, len, src); + fflush(stderr); + #endif + + return data; +} + +char *STRING_free_later(char *ptr) +{ + /*if (NLast >= MAX_LAST_STRING) + THROW(E_STRING);*/ + + //static int nfl = 0; + + if (ptr) + { + //nfl++; + //fprintf(stderr, "% 8d % 6d\n", nfl, STRING_length(ptr)); + + #ifdef DEBUG_ME + if (STRING_last[_index]) + { + DEBUG_where(); + fprintf(stderr, "STRING_free_later: release temp: %p '%s'\n", STRING_last[_index], STRING_last[_index]); + fflush(stderr); + } + #endif + + //if (STRING_last[_index] && STRING_length(STRING_last[_index]) >= 1024) + // fprintf(stderr, "STRING_free_later: free [%d] %d\n", _index, STRING_length(STRING_last[_index])); + + STRING_unref(&STRING_last[_index]); + + #ifdef DEBUG_ME + fprintf(stderr, "STRING_free_later: post temp: %p '%s'\n", ptr, ptr); + fflush(stderr); + #endif + + STRING_last[_index] = ptr; + //if (STRING_length(ptr) >= 1024) + // fprintf(stderr, "STRING_free_later: [%d] = %d\n", _index, STRING_length(ptr)); + + _index++; + + if (_index >= STRING_last_count) + _index = 0; + } + + return ptr; +} + + +int STRING_get_free_index(void) +{ + return _index; +} + + +void STRING_clear_cache(void) +{ + int i; + + for (i = 0; i < STRING_last_count; i++) + { + #ifdef DEBUG_ME + if (STRING_last[i]) + fprintf(stderr, "release temp %p '%s'\n", STRING_last[i], STRING_last[i]); + #endif + STRING_unref(&STRING_last[i]); + STRING_last[i] = NULL; + } + + _index = 0; + + clear_pool(); +} + + +void STRING_exit(void) +{ + STRING_clear_cache(); + + #ifdef DEBUG_ME + fprintf(stderr, "STRING_exit\n"); + #endif + + if (_conv_unicode_utf8 != ((iconv_t)-1)) + iconv_close(_conv_unicode_utf8); + if (_conv_utf8_unicode != ((iconv_t)-1)) + iconv_close(_conv_utf8_unicode); +} + + +char *STRING_extend(char *str, int new_len) +{ + STRING *sstr; + + if (!str) + { + sstr = alloc_string(new_len); + #ifdef DEBUG_ME + fprintf(stderr, "STRING_extend: NULL -> %p / %p\n", sstr, sstr->data); + #endif + } + else + { + sstr = realloc_string(STRING_from_ptr(str), new_len); + #ifdef DEBUG_ME + fprintf(stderr, "STRING_extend: %p / %p -> %p / %p\n", STRING_from_ptr(str), str, sstr, sstr->data); + #endif + } + + return sstr ? sstr->data : NULL; +} + + +bool STRING_extend_will_realloc(char *str, int new_len) +{ + STRING *sstr; + int size; + int new_size; + + if (!str) + return new_len != 0; + + sstr = STRING_from_ptr(str); + + if (new_len == sstr->len) + return FALSE; + + size = REAL_SIZE(sstr->len + 1 + sizeof(STRING)); + new_size = REAL_SIZE(new_len + 1 + sizeof(STRING)); + return size != new_size; +} + + +void STRING_new_temp_value(VALUE *value, const char *src, int len) +{ + value->_string.addr = STRING_new_temp(src, len); + value->_string.len = len; //STRING_length(value->_string.addr); + value->_string.start = 0; + value->type = T_STRING; +} + + +void STRING_new_constant_value(VALUE *value, const char *src, int len) +{ + value->_string.addr = (char *)src; + value->_string.len = ((len < 0) ? strlen(src) : len); + value->_string.start = 0; + value->type = T_CSTRING; +} + + +void STRING_void_value(VALUE *value) +{ + value->type = T_CSTRING; + value->_string.addr = NULL; + value->_string.start = 0; + value->_string.len = 0; +} + + +#if DEBUG_STRING + +void STRING_free(char **ptr) +{ + if (*ptr == NULL) + return; + + #ifdef DEBUG_ME + DEBUG_where(); + fprintf(stderr, "STRING_free: %p\n", *ptr); + fflush(stderr); + #endif + + STRING_free_real(*ptr); + *ptr = NULL; +} + +void STRING_ref_real(char *ptr) +{ + STRING *str; + + if (ptr == NULL) + return; + + if (ptr == STRING_watch) + BREAKPOINT(); + + str = STRING_from_ptr(ptr); + + #ifdef DEBUG_ME + DEBUG_where(); + fprintf(stderr, "STRING_ref: %p ( %d -> %d )\n", ptr, str->ref, str->ref + 1); + if (str->ref < 0 || str->ref > 10000) + fprintf(stderr, "*** BAD\n"); + fflush(stderr); + #endif + + str->ref++; +} + + +void STRING_unref_real(char **ptr) +{ + STRING *str; + + if (*ptr == NULL) + return; + + if (*ptr == STRING_watch) + BREAKPOINT(); + + str = STRING_from_ptr(*ptr); + + #ifdef DEBUG_ME + DEBUG_where(); + fprintf(stderr, "STRING_unref: %p ( %d -> %d )\n", *ptr, str->ref, str->ref - 1); + if (str->ref < 1 || str->ref > 10000) + { + fprintf(stderr, "*** BAD\n"); + BREAKPOINT(); + } + fflush(stderr); + #endif + + if ((--str->ref) <= 0) + STRING_free(ptr); +} + +#endif + +void STRING_unref_keep(char **ptr) +{ + STRING *str; + + if (*ptr == NULL) + return; + + str = STRING_from_ptr(*ptr); + if (str->ref > 1) + str->ref--; + else + STRING_free_later(*ptr); +} + +/* The get_param argument index starts at 1, not 0! */ + +#define INDEX_AT 0 +#define INDEX_IGNORE (-1) +#define INDEX_ERROR (-2) + +static int get_param_index(const char *str, int len, uint *pos, int *len_pattern) +{ + uint i; + int index; + bool err; + uchar d; + + i = *pos + 1; + d = str[i]; + if (d == '&') + { + index = INDEX_AT; + } + else if (d >= '1' && d <= '9') + { + index = d - '0'; + } + else if (d == '{') + { + err = FALSE; + index = 0; + for(;;) + { + i++; + if (i >= len) + break; + d = str[i]; + if (d == '}') + break; + if (d >= '0' && d <= '9') + index = index * 10 + d - '0'; + else + err = TRUE; + } + if (err || index < 1 || index >= 64) + index = INDEX_ERROR; + } + else + { + index = INDEX_IGNORE; + } + + if (len_pattern) + *len_pattern = i - *pos + 1; + + *pos = i; + return index; +} + +char *STRING_subst(const char *str, int len, SUBST_FUNC get_param) +{ + uint i; + uchar c; + int np, lenp; + int len_subst; + char *subst; + char *ps; + char *p[64]; + int lp[64]; + + if (!str) + return NULL; + + if (len <= 0) + len = strlen(str); + + // Comment to force a commit because svn unexpectedly lost my log + if (len == 0) + return NULL; + + // Calculate the length + + len_subst = len; + + for (i = 0; i < len; i++) + { + c = str[i]; + if (c == '&') + { + np = get_param_index(str, len, &i, &lenp); + len_subst -= lenp; + + switch (np) + { + case INDEX_AT: + len_subst++; + break; + case INDEX_IGNORE: + len_subst += lenp; + break; + case INDEX_ERROR: + break; + default: + np--; + (*get_param)(np + 1, &p[np], &lp[np]); + if (lp[np] < 0) + lp[np] = strlen(p[np]); + len_subst += lp[np]; + } + } + } + + if (!len_subst) + return NULL; + + subst = STRING_new(NULL, len_subst); + ps = subst; + + for (i = 0; i < len; i++) + { + c = str[i]; + if (c == '&') + { + np = get_param_index(str, len, &i, &lenp); + switch (np) + { + case INDEX_AT: + *ps++ = '&'; + break; + case INDEX_IGNORE: + *ps++ = '&'; + *ps++ = str[i]; + break; + case INDEX_ERROR: + break; + default: + np--; + memcpy(ps, p[np], lp[np]); + ps += lp[np]; + } + } + else + *ps++ = c; + } + + *ps = 0; + return STRING_free_later(subst); +} + +char *STRING_subst_add(const char *str, int len, SUBST_ADD_FUNC add_param) +{ + uint i; + char c; + int np; + + if (!str) + return NULL; + + if (len <= 0) + len = strlen(str); + + STRING_start_len(len); + + for (i = 0; i < len; i++) + { + c = str[i]; + if (c == '&') + { + np = get_param_index(str, len, &i, NULL); + switch (np) + { + case INDEX_AT: + STRING_make_char('&'); + break; + case INDEX_IGNORE: + STRING_make_char('&'); + STRING_make_char(str[i]); + break; + case INDEX_ERROR: + break; + default: + (*add_param)(np); + } + } + else + STRING_make_char(c); + } + + return STRING_end_temp(); +} + + +char *STRING_add(char *str, const char *src, int len) +{ + int old_len; + + if (len <= 0 && src != NULL) + len = strlen(src); + + if (len <= 0) + return str; + + if (!str) + return STRING_new(src, len); + + old_len = STRING_length(str); + + str = STRING_extend(str, old_len + len); + if (src) + { + memcpy(&str[old_len], src, len); + str[old_len + len] = 0; + } + + return str; +} + + +char *STRING_add_char(char *str, char c) +{ + int len = STRING_length(str); + char *p; + + str = STRING_extend(str, len + 1); + + p = str + len; + p[0] = c; + p[1] = 0; + + return str; +} + + +void STRING_start_len(int len) +{ + _make.inc = 32; + + if (len == 0) + len = 32; + + _make.buffer = STRING_new(NULL, len); + _make.max = len; + _make.len = 0; + _make.ptr = _make.buffer; + _make.ntemp = 0; +} + + +void STRING_make_dump() +{ + int n = _make.ntemp; + _make.ntemp = 0; + STRING_make(_make.temp, n); +} + + +// len == 0 est possible ! On peut vouloir ajouter une chaîne vide. + +void STRING_make(const char *src, int len) +{ + int pos; + + if (!src) + return; + + if (len < 0) + len = strlen(src); + + if (len <= 0) + return; + + if (_make.ntemp) + STRING_make_dump(); + + _make.len += len; + + if (_make.len >= _make.max) + { + pos = (_make.len - _make.max) * 4; + if (pos < _make.inc) + pos = _make.inc; + else + { + _make.inc = (pos + 31) & ~31; + if (_make.inc > 1024) + _make.inc = 1024; + } + + _make.max += pos; + + //fprintf(stderr, "STRING_extend: %d\n", _max - STRING_length(SUBST_buffer)); + pos = _make.ptr - _make.buffer; + _make.buffer = STRING_extend(_make.buffer, _make.max); + _make.ptr = _make.buffer + pos; + } + + memcpy(_make.ptr, src, len); + _make.ptr += len; +} + +char *STRING_end() +{ + if (_make.ntemp) + STRING_make_dump(); + + if (_make.len) + { + _make.buffer = STRING_extend(_make.buffer, _make.len); + _make.buffer[_make.len] = 0; + } + else + STRING_free(&_make.buffer); + + return _make.buffer; +} + +char *STRING_end_temp() +{ + STRING_end(); + + if (_make.buffer) + STRING_free_later(_make.buffer); + + return _make.buffer; +} + + +/**************************************************************************** + + Charset conversion routines + +****************************************************************************/ + +static iconv_t my_iconv_open(const char *dst, const char *src) +{ + const char *osrc, *odst; + iconv_t *cache; + iconv_t handle; + + osrc = src; + odst = dst; + + if (dst == SC_UNICODE) + dst = EXEC_big_endian ? "UCS-4BE" : "UCS-4LE"; + else if (dst == SC_UTF8) + dst = "UTF-8"; + else if (!dst || *dst == 0) + dst = "ASCII"; + + if (src == SC_UNICODE) + src = EXEC_big_endian ? "UCS-4BE" : "UCS-4LE"; + else if (src == SC_UTF8) + src = "UTF-8"; + else if (!src || *src == 0) + src = "ASCII"; + + if (osrc == SC_UNICODE && odst == SC_UTF8) + cache = &_conv_unicode_utf8; + else if (osrc == SC_UTF8 && odst == SC_UNICODE) + cache = &_conv_utf8_unicode; + else + cache = NULL; + + if (cache && *cache != ((iconv_t)-1)) + return *cache; + + //fprintf(stderr, "iconv_open: %s -> %s\n", src, dst); + handle = iconv_open(dst, src); + if (cache) + *cache = handle; + return handle; +} + + +static void my_iconv_close(iconv_t handle) +{ + if (handle == _conv_unicode_utf8 || handle == _conv_utf8_unicode) + return; + + iconv_close(handle); +} + + +int STRING_conv(char **result, const char *str, int len, const char *src, const char *dst, bool throw) +{ + iconv_t handle; + bool err; + const char *in; + char *out; + size_t in_len; + size_t out_len; + size_t ret; + int errcode = 0; + bool unicode; + + *result = NULL; + + in = str; + in_len = len; + + if (len == 0) + return errcode; + + unicode = (dst == SC_UNICODE); + + handle = my_iconv_open(dst, src); + if (handle == (iconv_t)(-1)) + { + if (errno == EINVAL) + errcode = E_UCONV; + else + errcode = E_CONV; + } + else + { + err = FALSE; + + for(;;) + { + out = COMMON_buffer; + out_len = COMMON_BUF_MAX; + + #if defined(OS_SOLARIS) || defined(OS_BSD) + ret = iconv(handle, &in, &in_len, &out, &out_len); + #else + ret = iconv(handle, (char **)&in, &in_len, &out, &out_len); + #endif + + if (ret != (size_t)(-1) || errno == E2BIG) + *result = STRING_add(*result, COMMON_buffer, COMMON_BUF_MAX - out_len); + + if (ret != (size_t)(-1)) + break; + + if (errno != E2BIG) + { + err = TRUE; + break; + } + } + + my_iconv_close(handle); + + if (unicode) + *result = STRING_add(*result, "\0\0\0", 3); + + STRING_extend_end(*result); + + if (err) + errcode = E_CONV; + } + + if (throw && errcode) + THROW(errcode); + + return errcode; +} + + +char *STRING_conv_to_UTF8(const char *name, int len) +{ + char *result = NULL; + + if (!name) + return ""; + + if (LOCAL_is_UTF8) + { + if (len <= 0) + result = (char *)name; + else + result = STRING_new_temp(name, len); + } + else + { + if (len <= 0) + len = strlen(name); + + STRING_conv(&result, name, len, LOCAL_encoding, SC_UTF8, TRUE); + } + + if (result) + return result; + else + return ""; +} + + +char *STRING_conv_file_name(const char *name, int len) +{ + char *result = NULL; + int pos; + struct passwd *info; + char *dir; + char *user; + int err; + + if (!name) + return ""; + + if (len <= 0) + len = strlen(name); + + if (len > 0 && *name == '~') + { + for (pos = 0; pos < len; pos++) + { + if (name[pos] == '/') + break; + } + + if (pos <= 1) + dir = FILE_get_home(); + else + { + user = STRING_new_temp(&name[1], pos - 1); + info = getpwnam(user); + if (info) + dir = info->pw_dir; + else + dir = NULL; + } + + if (dir) + { + user = STRING_new_zero(dir); + if (pos < len) + user = STRING_add(user, &name[pos], len - pos); + name = user; + len = STRING_length(name); + STRING_free_later(user); + } + } + + if (LOCAL_is_UTF8) + result = STRING_new_temp(name, len); + else + { + err = STRING_conv(&result, name, len, SC_UTF8, LOCAL_encoding, FALSE); + if (err) + result = STRING_new_temp(name, len); + } + //fprintf(stderr, "STRING_conv_file_name: %s\n", result); + + if (result) + return result; + else + return ""; +} + + +// Warning! Returns the index starting from 1, and 0 if no match is found. + +int STRING_search(const char *ps, int ls, const char *pp, int lp, int is, bool right, bool nocase) +{ + int pos, ip, apos; + char *p; + + if (lp > ls) + return 0; + + if (is < 0) + is += ls; + else if (is == 0) + is = right ? ls : 1; + + ls = ls - lp + 1; + + if (is > ls) + { + if (!right) + return 0; + else + is = ls; + } + else if (is < lp) + { + if (right) + return 0; + else if (is < 1) + is = 1; + } + + is--; + + if (lp == 1) + { + char cp = *pp; + + if (nocase && cp != tolower(cp)) + { + cp = tolower(cp); + + if (right) + { + for (; is >= 0; is--) + { + if (tolower(ps[is]) == cp) + return is + 1; + } + } + else + { + for (; is < ls; is++) + { + if (tolower(ps[is]) == cp) + return is + 1; + } + } + } + else + { + if (right) + { + p = memrchr(ps, (uchar)cp, is + 1); + return p ? p - ps + 1 : 0; + + /*for (; is >= 0; is--) + { + if (ps[is] == cp) + return is + 1; + }*/ + } + else + { + p = memchr(ps + is, (uchar)cp, ls - is); + return p ? p - ps + 1 : 0; + + /*for (; is < ls; is++) + { + if (ps[is] == cp) + return is + 1; + }*/ + } + } + + return 0; + } + + pos = 0; + apos = 0; + + if (!nocase) // || pp[0] == tolower(pp[0])) + { + p = memchr(right ? ps : ps + is, (uchar)pp[0], right ? ls : ls - is); + if (!p) + goto __FOUND; + ls -= (int)(p - ps); + is -= (int)(p - ps); + if (is < 0) is = 0; + apos = (int)(p - ps); + ps = p; + + p = memrchr(ps, (uchar)pp[0], right ? is + 1 : ls); + if (!p) + goto __FOUND; + ls = (int)(p - ps + 1); + if (is >= ls) is = ls - 1; + } + + ps += is; + + if (right) + { + if (nocase) + { + for (; is >= 0; is--, ps--) + { + if (tolower(ps[0]) != tolower(pp[0])) + goto __NEXT_RN; + + for (ip = 1; ip < lp; ip++) + { + if (tolower(ps[ip]) != tolower(pp[ip])) + goto __NEXT_RN; + } + + pos = apos + is + 1; + goto __FOUND; + + __NEXT_RN: + ; + } + } + else + { + for (; is >= 0; is--, ps--) + { + if (ps[0] != pp[0]) + goto __NEXT_R; + + for (ip = 1; ip < lp; ip++) + { + if (ps[ip] != pp[ip]) + goto __NEXT_R; + } + + pos = apos + is + 1; + goto __FOUND; + + __NEXT_R: + ; + } + } + } + else + { + if (nocase) + { + for (; is < ls; is++, ps++) + { + if (tolower(ps[0]) != tolower(pp[0])) + goto __NEXT_LN; + + for (ip = 1; ip < lp; ip++) + { + if (tolower(ps[ip]) != tolower(pp[ip])) + goto __NEXT_LN; + } + + pos = apos + is + 1; + goto __FOUND; + + __NEXT_LN: + ; + } + } + else + { + for (; is < ls; is++, ps++) + { + if (ps[0] != pp[0]) + goto __NEXT_L; + + if (memcmp(ps + 1, pp + 1, lp - 1)) + goto __NEXT_L; + + /*for (ip = 1; ip < lp; ip++) + { + if (ps[ip] != pp[ip]) + goto __NEXT_L; + }*/ + + pos = apos + is + 1; + goto __FOUND; + + __NEXT_L: + ; + } + } + } + +__FOUND: + + return pos; +} diff --git a/main/gbx/gbx_string.h b/main/gbx/gbx_string.h new file mode 100644 index 00000000..7d9675bb --- /dev/null +++ b/main/gbx/gbx_string.h @@ -0,0 +1,224 @@ +/*************************************************************************** + + gbx_string.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __STRING_H +#define __STRING_H + +#include "gbx_value.h" +#include "gbx_debug.h" +#include "gb_common_string.h" + +typedef + struct + { + int ref; + int len; + char data[0]; + } + STRING; + +typedef + void (*SUBST_FUNC)(int, char **, int *); + +typedef + void (*SUBST_ADD_FUNC)(int); + +#define STRING_MAKE_TEMP 32 + +typedef + struct + { + char *buffer; + char *ptr; + int inc; + int len; + int max; + char temp[STRING_MAKE_TEMP]; + int ntemp; + } + STRING_MAKE; + +// NOTE: Defined in gambas.h too +#define SC_UNICODE ((char *)-1) +#define SC_UTF8 ((char *)-2) + +#ifndef __STRING_C +extern STRING_MAKE STRING_make_buffer; +extern const char STRING_char_table[]; +#endif + +void STRING_init(void); +void STRING_exit(void); +void STRING_clear_cache(void); + +char *STRING_new(const char *src, int len); +#define STRING_new_zero(_src) \ +({ \ + const char *_s = (_src); \ + STRING_new(_s, _s ? strlen(_s) : 0); \ +}) + +void STRING_free_real(char *ptr); +char *STRING_free_later(char *ptr); +int STRING_get_free_index(void); + +#define STRING_new_temp(_src, _len) STRING_free_later(STRING_new(_src, _len)) +#define STRING_new_temp_zero(_src) STRING_free_later(STRING_new_zero(_src)) + +char *STRING_extend(char *str, int new_len); +bool STRING_extend_will_realloc(char *str, int new_len); + +//void STRING_extend_end(char *str); +char *STRING_add(char *str, const char *src, int len); +char *STRING_add_char(char *str, char c); + +#define STRING_extend_end(_str) \ +do { \ + if (_str) \ + { \ + (_str)[STRING_length((_str))] = 0; \ + STRING_free_later(_str); \ + } \ +} while(0) + + +#define STRING_copy_from_value(_value) \ +({ \ + char *ptr; \ + if ((_value)->_string.len == 0) \ + ptr = NULL; \ + else if ((_value)->type == T_STRING && (_value)->_string.start == 0 && (_value)->_string.len == STRING_length((_value)->_string.addr)) \ + ptr = (_value)->_string.addr; \ + else \ + ptr = STRING_new(&(_value)->_string.addr[(_value)->_string.start], (_value)->_string.len); \ + ptr; \ +}) + +#define STRING_copy_from_value_temp(_value) \ +({ \ + char *ptr; \ + if ((_value)->_string.len == 0) \ + ptr = NULL; \ + else if ((_value)->type == T_STRING && (_value)->_string.start == 0 && (_value)->_string.len == STRING_length((_value)->_string.addr)) \ + ptr = (_value)->_string.addr; \ + else \ + ptr = STRING_new_temp(&(_value)->_string.addr[(_value)->_string.start], (_value)->_string.len); \ + ptr; \ +}) + + +void STRING_new_temp_value(VALUE *value, const char *src, int len); +void STRING_new_constant_value(VALUE *value, const char *src, int len); + +#define STRING_char_value(_value, _car) \ +do { \ + _value->type = T_CSTRING; \ + _value->_string.addr = (char *)&STRING_char_table[(_car) * 2]; \ + _value->_string.start = 0; \ + _value->_string.len = 1; \ +} while(0) + +void STRING_void_value(VALUE *value); + +char *STRING_subst(const char *str, int len, SUBST_FUNC get_param); +char *STRING_subst_add(const char *str, int len, SUBST_ADD_FUNC add_param); +int STRING_conv(char **result, const char *str, int len, const char *src, const char *dst, bool throw); +char *STRING_conv_file_name(const char *name, int len); +char *STRING_conv_to_UTF8(const char *name, int len); + +#define STRING_from_ptr(_ptr) ((STRING *)((_ptr) - offsetof(STRING, data))) +#define STRING_length(_ptr) ((_ptr) == NULL ? 0 : STRING_from_ptr(_ptr)->len) + +#if DEBUG_STRING + +#ifndef __STRING_C +extern char *STRING_watch; +#endif + +void STRING_free(char **ptr); +void STRING_ref_real(char *ptr); +void STRING_unref_real(char **ptr); + +#define STRING_ref(_p) fprintf(stderr, "<< %s >> ", __func__), STRING_ref_real(_p) +#define STRING_unref(_p) fprintf(stderr, "<< %s >> ", __func__), STRING_unref_real(_p) + +#else + +#define STRING_free(_p) \ +({ \ + char **pptr = _p; \ + char *ptr = *pptr; \ + if (LIKELY(ptr != NULL)) \ + { \ + STRING_free_real(ptr); \ + *pptr = NULL; \ + } \ +}) + +#define STRING_ref(_p) \ +({ \ + char *ptr = _p; \ + if (LIKELY(ptr != NULL)) \ + { \ + STRING_from_ptr(ptr)->ref++; \ + } \ +}) + +#define STRING_unref(_p) \ +({ \ + char **pptr = _p; \ + char *ptr = *pptr; \ + STRING *str; \ + if (LIKELY(ptr != NULL)) \ + { \ + str = STRING_from_ptr(ptr); \ + if ((--str->ref) <= 0) \ + { \ + STRING_free_real(ptr); \ + *pptr = NULL; \ + } \ + } \ +}) + +#endif + +void STRING_unref_keep(char **ptr); + +int STRING_search(const char *ps, int ls, const char *pp, int lp, int is, bool right, bool nocase); +int STRING_search2(const char *ps, int ls, const char *pp, int lp, int is, bool right, bool nocase); + +void STRING_start_len(int len); +#define STRING_start() STRING_start_len(0) +char *STRING_end(); +char *STRING_end_temp(); +void STRING_make(const char *src, int len); +void STRING_make_dump(); + +#define STRING_make_char(_c) \ +({ \ + if (UNLIKELY(STRING_make_buffer.ntemp == STRING_MAKE_TEMP)) \ + STRING_make_dump(); \ + STRING_make_buffer.temp[STRING_make_buffer.ntemp++] = (_c); \ +}) + +#endif diff --git a/main/gbx/gbx_struct.c b/main/gbx/gbx_struct.c new file mode 100644 index 00000000..e4282c73 --- /dev/null +++ b/main/gbx/gbx_struct.c @@ -0,0 +1,102 @@ +/*************************************************************************** + + gbx_struct.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_STRUCT_C + +#include "gb_alloc.h" +#include "gbx_struct.h" + +void *CSTRUCT_create_static(void *ref, CLASS *class, char *addr) +{ + CSTATICSTRUCT *object; + + ALLOC(&object, sizeof(CSTATICSTRUCT)); + + object->ob.class = class; + object->ob.ref = 0; + object->ref = ref; + object->addr = addr; + + class->count++; + + if (ref != STRUCT_CONST) + OBJECT_REF(ref); + + //fprintf(stderr, "CSTRUCT_create_static: %s %p ref = %p addr = %p\n", class->name, object, ref, addr); + + return object; +} + +int CSTRUCT_get_size(CLASS *class) +{ + return class->size - sizeof(CSTRUCT); +} + +void CSTRUCT_release(CSTRUCT *ob) +{ + if (ob->ref != STRUCT_CONST) + OBJECT_UNREF(ob->ref); +} + + +#if 0 +BEGIN_PROPERTY(Struct_Size) + + GB_ReturnInteger(CLASS_sizeof(OBJECT_class(THIS))); + +END_PROPERTY + + +BEGIN_METHOD(Struct_Read, GB_OBJECT stream) + + void *stream = VARG(stream); + + if (GB.CheckObject(stream)) + return; + +END_METHOD + + +BEGIN_METHOD(Struct_write, GB_OBJECT stream) + + void *stream = VARG(stream); + VALUE temp; + + if (GB.CheckObject(stream)) + return; + + temp._object.class = OBJECT_class(THIS); + temp._object.class = THIS; + STREAM_write_type(CSTREAM_stream(stream), T_OBJECT, &temp, 0); + +END_METHOD + +// Beware: if this declaration is modified, the CSTRUCT_NDESC constant must be modified accordingly. + +GB_DESC CSTRUCT_desc[CSTRUCT_NDESC] = +{ + GB_PROPERTY_READ("Size", "i", Struct_Size), + GB_METHOD("Read", NULL, Struct_Read, "(Stream)Stream;"), + GB_METHOD("Write", NULL, Struct_Write, "(Stream)Stream;"), +}; +#endif diff --git a/main/gbx/gbx_struct.h b/main/gbx/gbx_struct.h new file mode 100644 index 00000000..b518b00e --- /dev/null +++ b/main/gbx/gbx_struct.h @@ -0,0 +1,51 @@ +/*************************************************************************** + + gbx_struct.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_STRUCT_H +#define __GBX_STRUCT_H + +#include "gbx_object.h" +#include "gbx_value.h" + +#define STRUCT_CONST ((void *)-1) + +typedef + struct { + OBJECT ob; + void *ref; + } + CSTRUCT; + +typedef + struct { + OBJECT ob; + void *ref; + char *addr; + } + CSTATICSTRUCT; + +void *CSTRUCT_create_static(void *ref, CLASS *class, char *addr); +int CSTRUCT_get_size(CLASS *class); +void CSTRUCT_release(CSTRUCT *ob); + +#endif diff --git a/main/gbx/gbx_subr.c b/main/gbx/gbx_subr.c new file mode 100644 index 00000000..17181568 --- /dev/null +++ b/main/gbx/gbx_subr.c @@ -0,0 +1,244 @@ +/*************************************************************************** + + gbx_subr.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_SUBR_C + +#include "gb_common.h" +#include "gbx_subr.h" +#include "gambas.h" +#include "gbx_api.h" + +/*int NPARAM;*/ + +void SUBR_leave_void(int nparam) +{ + RELEASE_MANY(SP, nparam); + + SP->type = T_VOID; + SP++; +} + +void SUBR_leave(int nparam) +{ + BORROW(RP); + + RELEASE_MANY(SP, nparam); + + //*SP++ = *RP; + COPY_VALUE(SP, RP); + SP++; + RP->type = T_VOID; +} + + +bool SUBR_check_string_real(VALUE *param) +{ +__RETRY: + + if (TYPE_is_string(param->type)) + return (param->_string.len == 0); + + if (TYPE_is_null(param->type)) + return TRUE; + + if (param->type == T_VARIANT) + { + VARIANT_undo(param); + goto __RETRY; + } + + THROW_TYPE(T_STRING, param->type); +} + + +void SUBR_check_integer(VALUE *param) +{ + if (param->type == T_VARIANT) + VARIANT_undo(param); + + if (TYPE_is_integer(param->type)) + return; + + THROW_TYPE(T_INTEGER, param->type); +} + + +void SUBR_check_float(VALUE *param) +{ + if (param->type == T_VARIANT) + VARIANT_undo(param); + + if (TYPE_is_number(param->type)) + { + VALUE_conv_float(param); + return; + } + + THROW_TYPE(T_INTEGER, param->type); +} + + +int SUBR_get_integer(VALUE *param) +{ + SUBR_check_integer(param); + return param->_integer.value; +} + + +void *SUBR_get_pointer(VALUE *param) +{ + if (param->type == T_VARIANT) + VARIANT_undo(param); + + if (param->type != T_POINTER) + THROW_TYPE(T_POINTER, param->type); + + return (void *)param->_pointer.value; +} + +void *SUBR_get_pointer_or_string(VALUE *param) +{ + if (param->type == T_VARIANT) + VARIANT_undo(param); + + if (TYPE_is_string(param->type)) + return (void *)(param->_string.addr + param->_string.start); + + if (param->type == T_POINTER) + return (void *)param->_pointer.value; + + THROW_TYPE(T_POINTER, param->type); +} + + +double SUBR_get_float(VALUE *param) +{ + SUBR_check_float(param); + return param->_float.value; +} + + +char *SUBR_get_string(VALUE *param) +{ + if (SUBR_check_string(param)) + return ""; + + return STRING_copy_from_value_temp(param); +} + + +bool SUBR_get_boolean(VALUE *param) +{ + VALUE_conv_boolean(param); + return param->_boolean.value; +} + +static TYPE conv_type(TYPE type) +{ + /*if (type <= T_BYTE) + type = T_BYTE;*/ + if (type == T_CSTRING) // || type == T_NULL) + type = T_STRING; + else if (type == T_CLASS || type == T_FUNCTION) + type = T_VARIANT; + + return type; +} + +TYPE SUBR_check_good_type(VALUE *param, int count) +{ + int i; + TYPE type, type2; + + if (count == 0) + goto __VARIANT; + + type = conv_type(param[0].type); + + if (type == T_VARIANT) + goto __VARIANT; + + if (TYPE_is_value(type)) + { + for (i = 1; i < count; i++) + { + type2 = conv_type(param[i].type); + + if (type2 == type) + continue; + + if (type == T_NULL) + { + if (type2 <= T_FLOAT) + goto __VARIANT; + + type = type2; + continue; + } + + if (type <= T_FLOAT && type2 <= T_FLOAT) + { + if (type2 > type) + type = type2; + continue; + } + + if (type2 == T_NULL) + { + if (type <= T_FLOAT) + goto __VARIANT; + else + continue; + } + + if (TYPE_is_object(type) && TYPE_is_object(type2)) + { + type = T_OBJECT; + continue; + } + + type = T_VARIANT; + break; + } + } + + if (type == T_VOID) + THROW(E_NRETURN); + else if (!TYPE_is_value(type)) + THROW_TYPE(T_VARIANT, type); + + return type; + +__VARIANT: + + return T_VARIANT; +} + +TYPE SUBR_get_type(VALUE *param) +{ + if (param->type == T_INTEGER) + return (TYPE)param->_integer.value; + if (param->type == T_CLASS) + return (TYPE)param->_class.class; + THROW_ILLEGAL(); +} diff --git a/main/gbx/gbx_subr.h b/main/gbx/gbx_subr.h new file mode 100644 index 00000000..9b03beec --- /dev/null +++ b/main/gbx/gbx_subr.h @@ -0,0 +1,262 @@ +/*************************************************************************** + + gbx_subr.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_SUBR_H +#define __GBX_SUBR_H + +#include "gb_error.h" +#include "gbx_exec.h" + + +typedef + void (*SUBR_FUNC)(VALUE *); + +typedef + double (*MATH_FUNC)(double); + +typedef + double (*MATH_FUNC_2)(double, double); + +/* +#ifndef __GBX_SUBR_C +EXTERN int NPARAM; +#endif +*/ + +#define SUBR_GET_PARAM(nparam) \ + VALUE *PARAM = (SP - nparam); + +#define SUBR_ENTER() \ + int NPARAM = code & 0x3F; \ + SUBR_GET_PARAM(NPARAM); + +#define SUBR_ENTER_PARAM(nparam) \ + const int NPARAM = nparam; \ + SUBR_GET_PARAM(NPARAM); + +#define RETURN RP + +#define SUBR_LEAVE() SUBR_leave(NPARAM) + +/* BORROW(RP); \ + SUBR_leave_void(NPARAM); \ + *SP++ = *RP; \ + RP->type = T_VOID;*/ + +#define SUBR_LEAVE_VOID() SUBR_leave_void(NPARAM); + /*SP->type = T_VOID; \ + SP++;*/ + + +/* Common routines */ + +void SUBR_leave(int nparam); +void SUBR_leave_void(int nparam); + +#define SUBR_check_string(_value) (TYPE_is_string((_value)->type) ? ((_value)->_string.len == 0) : SUBR_check_string_real(_value)) + +#define VOID_STRING(_value) \ +do { \ + RELEASE_STRING(_value); \ + STRING_void_value(_value); \ +} while(0); + +bool SUBR_check_string_real(VALUE *param); +void SUBR_check_integer(VALUE *param); +void SUBR_check_float(VALUE *param); + +int SUBR_get_integer(VALUE *param); +double SUBR_get_float(VALUE *param); +void *SUBR_get_pointer(VALUE *param); +void *SUBR_get_pointer_or_string(VALUE *param); + +char *SUBR_get_string(VALUE *param); + +#define SUBR_get_string_len(_param, _pstr, _plen) \ +do { \ + if (SUBR_check_string(_param)) \ + { \ + *(_pstr) = NULL; \ + *(_plen) = 0; \ + } \ + else \ + { \ + *(_pstr) = (_param)->_string.addr + (_param)->_string.start; \ + *(_plen) = (_param)->_string.len; \ + } \ +} while(0); + +bool SUBR_get_boolean(VALUE *param); + +TYPE SUBR_get_type(VALUE *param); +TYPE SUBR_check_good_type(VALUE *param, int count); + +/* subr_math.c */ + +//void SUBR_add(ushort code); +//void SUBR_sub(ushort code); +//void SUBR_mul(ushort code); +//void SUBR_div(ushort code); +void SUBR_quo(ushort code); +void SUBR_rem(ushort code); +void SUBR_pow(ushort code); + +void SUBR_and_(ushort code); +void SUBR_not(ushort code); + +void SUBR_neg(ushort code); +void SUBR_int(ushort code); +void SUBR_abs(ushort code); +void SUBR_fix(ushort code); +void SUBR_sgn(ushort code); +void SUBR_pi(ushort code); +void SUBR_math(ushort code); +void SUBR_math2(ushort code); + +void SUBR_randomize(ushort code); +void SUBR_rnd(ushort code); +void SUBR_round(ushort code); + +void SUBR_isnan(ushort code); + +/* subr_string.c */ + +void SUBR_cat(ushort code); +void SUBR_file(ushort code); +//void SUBR_left(void); +//void SUBR_right(void); +//void SUBR_mid(void); +//void SUBR_len(void); +void SUBR_trim(ushort code); +void SUBR_space(void); +void SUBR_string(void); +void SUBR_upper(ushort code); +void SUBR_lower(void); +void SUBR_chr(void); +void SUBR_asc(ushort code); +void SUBR_instr(ushort code); +void SUBR_like(ushort code); +void SUBR_scan(void); +void SUBR_subst(ushort code); +void SUBR_replace(ushort code); +void SUBR_split(ushort code); +void SUBR_iconv(void); +void SUBR_sconv(ushort code); +void SUBR_is_chr(ushort code); +void SUBR_tr(void); +void SUBR_quote(ushort code); +void SUBR_unquote(ushort code); +void SUBR_swap(ushort code); + +/* subr_test.c */ + +//void SUBR_comp(ushort code); +//void SUBR_compn(ushort code); +//void SUBR_compi(ushort code); +void SUBR_case(ushort code); +void SUBR_bit(ushort code); +void SUBR_min_max(ushort code); +void SUBR_if(ushort code); +void SUBR_choose(ushort code); +void SUBR_near(void); +void SUBR_strcomp(ushort code); +void SUBR_is(ushort code); + +/* subr_conv.c */ + +void SUBR_is_type(ushort code); +//void SUBR_conv(ushort code); +void SUBR_type(ushort code); +void SUBR_str(void); +void SUBR_val(void); +void SUBR_format(ushort code); +void SUBR_hex_bin(ushort code); + +/* subr_time.c */ + +void SUBR_timer(void); +void SUBR_now(void); +void SUBR_year(ushort code); +void SUBR_time(ushort code); +void SUBR_date(ushort code); +void SUBR_date_op(ushort code); +void SUBR_week(ushort code); + +/* subr_file.c */ + +void SUBR_open(ushort code); +void SUBR_close(void); +void SUBR_print(ushort code); +void SUBR_linput(void); +void SUBR_eof(ushort code); +void SUBR_lof(ushort code); +void SUBR_seek(ushort code); +void SUBR_input(ushort code); +void SUBR_read(ushort code); +void SUBR_write(ushort code); +void SUBR_flush(void); +void SUBR_lock(ushort code); +void SUBR_inp_out(ushort code); + +void SUBR_stat(ushort code); +void SUBR_exist(ushort code); +void SUBR_dir(ushort code); +void SUBR_kill(ushort code); +void SUBR_mkdir(ushort code); +void SUBR_rmdir(ushort code); +void SUBR_move(ushort code); +void SUBR_link(ushort code); +void SUBR_temp(ushort code); +void SUBR_isdir(void); +void SUBR_access(ushort code); +void SUBR_rdir(ushort code); +void SUBR_dfree(); + +void SUBR_exit_inp_out(void); +#define SUBR_exit SUBR_exit_inp_out + +/* subr_extern.c */ + +void SUBR_alloc(ushort code); +void SUBR_free(void); +void SUBR_realloc(ushort code); +void SUBR_strptr(ushort code); +void SUBR_varptr(ushort code); +void SUBR_ptr(ushort code); +void SUBR_make(ushort code); + +/* subr_misc.c */ + +void SUBR_error(void); +void SUBR_shell(void); +void SUBR_wait(ushort code); +void SUBR_sleep(ushort code); +void SUBR_exec(ushort code); +void SUBR_eval(ushort code); +void SUBR_array(ushort code); +void SUBR_collection(ushort code); +void SUBR_debug(ushort code); + +void EVAL_string(char *expr); + +#endif diff --git a/main/gbx/gbx_subr_conv.c b/main/gbx/gbx_subr_conv.c new file mode 100644 index 00000000..a50d5a68 --- /dev/null +++ b/main/gbx/gbx_subr_conv.c @@ -0,0 +1,286 @@ +/*************************************************************************** + + gbx_subr_conv.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" + +#include "gbx_value.h" +#include "gbx_subr.h" +#include "gbx_local.h" + +#include "gbx_string.h" +#include "gbx_date.h" +#include "gbx_number.h" + + +void SUBR_is_type(ushort code) +{ + static void *jump[] = { + &&__BAD, &&__BOOLEAN, &&__BAD, &&__BAD, &&__INTEGER, &&__LONG, &&__BAD, &&__FLOAT, + &&__DATE, &&__BAD, &&__BAD, &&__BAD, &&__BAD, &&__BAD, &&__NUMBER, &&__NULL, + &&__BAD, &&__BAD + }; + + bool test; + char *addr; + int len; + VALUE temp; + + SUBR_ENTER_PARAM(1); + + code &= 0x3F; + + if (code == T_NULL) + goto __NULL; + + //VALUE_conv_string(PARAM); + SUBR_get_string_len(PARAM, &addr, &len); + VALUE_from_string(&temp, addr, len); + + goto *jump[code]; + +__BOOLEAN: + + test = temp.type == T_BOOLEAN; + goto __END; + +__INTEGER: + + test = temp.type == T_INTEGER; + goto __END; + +__LONG: + + test = temp.type == T_LONG || temp.type == T_INTEGER; + goto __END; + +__FLOAT: +__NUMBER: + + test = temp.type == T_FLOAT || temp.type == T_LONG || temp.type == T_INTEGER; + goto __END; + +__DATE: + + test = temp.type == T_DATE; + goto __END; + +__NULL: + + test = VALUE_is_null(PARAM); + goto __END; + +__BAD: + + THROW_ILLEGAL(); + +__END: + + RELEASE(PARAM); + SP--; + SP->type = T_BOOLEAN; + SP->_integer.value = (-test); + SP++; +} + +/* +void SUBR_conv(ushort code) +{ + VALUE_convert(SP - 1, code & 0x3F); +} +*/ + +void SUBR_type(ushort code) +{ + TYPE type; + int val; + + SUBR_ENTER_PARAM(1); + + if (code & 0x3F) + { + val = TYPE_sizeof_memory(SUBR_get_integer(PARAM)); + } + else + { + type = PARAM->type; + if (type == T_VARIANT) + type = PARAM->_variant.vtype; + + if (type == T_CSTRING) + val = T_STRING; + else if (TYPE_is_object(type) && type != T_NULL) + val = T_OBJECT; + else + val = type; + } + + RETURN->_integer.value = val; + RETURN->type = T_INTEGER; + + SUBR_LEAVE(); +} + + +void SUBR_str(void) +{ + char *addr; + int len; + + SUBR_ENTER_PARAM(1); + + VALUE_to_string(PARAM, &addr, &len); + STRING_new_temp_value(RETURN, addr, len); + + SUBR_LEAVE(); +} + + +void SUBR_val(void) +{ + char *addr; + int len; + + SUBR_ENTER_PARAM(1); + + if (SUBR_check_string(PARAM)) + VALUE_null(RETURN); + else + { + VALUE_get_string(PARAM, &addr, &len); + VALUE_from_string(RETURN, addr, len); + VALUE_conv_variant(RETURN); + } + + SUBR_LEAVE(); +} + + +void SUBR_format(ushort code) +{ + int fmt_type; + char *format = NULL; + int len = 0; + DATE_SERIAL *date; + char *str; + int len_str; + + SUBR_ENTER(); + + if (NPARAM == 1) + fmt_type = LF_STANDARD; + else + { + if (PARAM[1].type == T_VARIANT) + VARIANT_undo(&PARAM[1]); + + if (TYPE_is_string(PARAM[1].type)) + { + fmt_type = LF_USER; + VALUE_get_string(&PARAM[1], &format, &len); + if (!len) + fmt_type = LF_STANDARD; + } + else if (TYPE_is_integer(PARAM[1].type)) + { + fmt_type = PARAM[1]._integer.value; + if (fmt_type <= LF_USER || fmt_type >= LF_MAX) + THROW(E_ARG); + } + else + THROW_TYPE(T_INTEGER, PARAM[1].type); + } + + if (PARAM->type == T_VARIANT) + VARIANT_undo(PARAM); + + if (PARAM->type == T_DATE) + { + date = DATE_split(PARAM); + if (LOCAL_format_date(date, fmt_type, format, len, &str, &len_str)) + THROW(E_FORMAT); + } + else + { + VALUE_conv_float(PARAM); + if (LOCAL_format_number(PARAM->_float.value, fmt_type, format, len, &str, &len_str, TRUE)) + THROW(E_FORMAT); + } + + /*if (NPARAM >= 2) + RELEASE_STRING(&PARAM[1]);*/ + + STRING_new_temp_value(RETURN, str, len_str); + + SUBR_LEAVE(); +} + + +void SUBR_hex_bin(ushort code) +{ + int prec = 0; + int base; + int max_prec; + + SUBR_ENTER(); + + VALUE_conv(PARAM, T_LONG); + + switch(code >> 8) + { + case CODE_BIN: + base = 2; + max_prec = 64; + break; + + case CODE_HEX: + base = 16; + max_prec = 16; + break; + + default: + if (NPARAM == 0) // Compatibility with 2010 bytecode + { + SUBR_upper(1); + return; + } + base = 8; + max_prec = 22; + break; + } + + if (NPARAM == 2) + { + VALUE_conv_integer(&PARAM[1]); + + prec = PARAM[1]._integer.value; + + if (prec < 1 || prec > max_prec) + THROW(E_ARG); + } + + NUMBER_int_to_string(PARAM->_long.value, prec, base, RETURN); + + SUBR_LEAVE(); +} + + diff --git a/main/gbx/gbx_subr_extern.c b/main/gbx/gbx_subr_extern.c new file mode 100644 index 00000000..060e59c9 --- /dev/null +++ b/main/gbx/gbx_subr_extern.c @@ -0,0 +1,340 @@ +/*************************************************************************** + + gbx_subr_extern.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include + +#include "gb_common.h" +#include "gb_alloc.h" +#include "gb_common_check.h" +#include "gb_pcode.h" + +#include "gbx_subr.h" + + +void SUBR_alloc(ushort code) +{ + int size; + int count; + void *ptr; + char *copy; + + SUBR_ENTER(); + + if (NPARAM == 2) + count = SUBR_get_integer(&PARAM[1]); + else + count = 1; + + if (TYPE_is_null(PARAM->type)) + { + size = 1; + copy = NULL; + } + if (TYPE_is_string(PARAM->type)) + { + size = PARAM->_string.len + 1; + copy = PARAM->_string.addr + PARAM->_string.start; + } + else + { + size = SUBR_get_integer(PARAM); + copy = NULL; + } + + if (count <= 0 || size <= 0) + THROW(E_ARG); + + ALLOC(&ptr, size * count); + + if (copy) + { + size--; + memcpy(ptr, copy, size); + ((char *)ptr)[size] = 0; + } + + RETURN->type = T_POINTER; + RETURN->_pointer.value = ptr; + + SUBR_LEAVE(); +} + + +void SUBR_free(void) +{ + void *ptr; + + SUBR_ENTER_PARAM(1); + + ptr = SUBR_get_pointer(PARAM); + + IFREE(ptr); + + SUBR_LEAVE_VOID(); +} + + +void SUBR_realloc(ushort code) +{ + int size; + int count; + void *ptr; + + SUBR_ENTER(); + + if (NPARAM == 3) + size = SUBR_get_integer(&PARAM[2]); + else + size = 1; + + count = SUBR_get_integer(&PARAM[1]); + + if (size <= 0 || count <= 0) + THROW(E_ARG); + + ptr = SUBR_get_pointer(&PARAM[0]); + + REALLOC(&ptr, size * count); + + RETURN->type = T_POINTER; + RETURN->_pointer.value = ptr; + + SUBR_LEAVE(); +} + + +void SUBR_strptr(ushort code) +{ + char *ptr; + ssize_t len = 0; + bool err; + + SUBR_ENTER(); + + ptr = (char *)SUBR_get_pointer(PARAM); + + if (NPARAM == 1) + { + err = CHECK_strlen(ptr, &len); + } + else + { + len = SUBR_get_integer(&PARAM[1]); + err = CHECK_address(ptr, len); + } + + if (err) + { + VALUE_null(RETURN); + } + else + { + RETURN->type = T_CSTRING; + RETURN->_string.addr = ptr; + RETURN->_string.start = 0; + RETURN->_string.len = (int)len; + } + + SUBR_LEAVE(); +} + +void SUBR_varptr(ushort code) +{ + ushort op; + void *ptr; + VALUE *val; + CLASS_VAR *var; + + SUBR_ENTER_PARAM(1); + + op = (ushort)SUBR_get_integer(PARAM); + + if ((code & 0xFF) == 1) + { + uint64_t optargs = BP[FP->n_local + FP->n_ctrl - 1]._long.value; + + RETURN->type = T_BOOLEAN; + RETURN->_boolean.value = (optargs & (1 << (FP->n_param + (op & 0xFF) - 256))) ? -1 : 0; + } + else + { + if ((op & 0xFF00) == C_PUSH_LOCAL || (op & 0xFF00) == C_PUSH_PARAM) + { + if ((op & 0xFF00) == C_PUSH_PARAM) + val = &PP[(signed char)(op & 0xFF)]; + else + val = &BP[op & 0xFF]; + + switch(val->type) + { + case T_BOOLEAN: + case T_BYTE: + case T_SHORT: + case T_INTEGER: + ptr = &val->_integer.value; + break; + + case T_LONG: + ptr = &val->_long.value; + break; + + case T_SINGLE: + ptr = &val->_single.value; + break; + + case T_FLOAT: + ptr = &val->_float.value; + break; + + case T_DATE: + ptr = &val->_date.date; + break; + + case T_STRING: + case T_CSTRING: + ptr = val->_string.addr + val->_string.start; + break; + + case T_POINTER: + ptr = &val->_pointer.value; + break; + + default: + THROW(E_TYPE, "Number", TYPE_get_name(val->type)); + } + } + else if ((op & 0xF800) == C_PUSH_DYNAMIC) + { + var = &CP->load->dyn[op & 0x7FF]; + + if (OP == NULL) + THROW_ILLEGAL(); + + ptr = &OP[var->pos]; + } + else if ((op & 0xF800) == C_PUSH_STATIC) + { + var = &CP->load->stat[op & 0x7FF]; + ptr = (char *)CP->stat + var->pos; + } + else + THROW_ILLEGAL(); + + RETURN->type = T_POINTER; + RETURN->_pointer.value = ptr; + } + + SUBR_LEAVE(); +} + + +void SUBR_ptr(ushort code) +{ + void *ptr; + + SUBR_ENTER_PARAM(1); + + ptr = SUBR_get_pointer_or_string(PARAM); + + CHECK_enter(); + if (sigsetjmp(CHECK_jump, TRUE) == 0) + VALUE_read(RETURN, ptr, code & 0xF); + CHECK_leave(); + + if (CHECK_got_error()) + THROW(E_ARG); + + SUBR_LEAVE(); +} + +void SUBR_make(ushort code) +{ + static void *jump[] = { + &&__ERROR, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, + &&__DATE, &&__ERROR, &&__ERROR, &&__POINTER, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR + }; + + TYPE type; + + SUBR_ENTER_PARAM(1); + + type = code & 0x3F; + VALUE_conv(PARAM, type); + STRING_new_temp_value(RETURN, NULL, TYPE_sizeof_memory(type)); + goto *jump[type]; + +__BOOLEAN: + + *(RETURN->_string.addr) = PARAM->_boolean.value != 0; + goto __END; + +__BYTE: + + *(RETURN->_string.addr) = PARAM->_byte.value; + goto __END; + +__SHORT: + + memcpy(RETURN->_string.addr, &PARAM->_short.value, sizeof(short)); + goto __END; + +__INTEGER: + + memcpy(RETURN->_string.addr, &PARAM->_integer.value, sizeof(int)); + goto __END; + +__LONG: + + memcpy(RETURN->_string.addr, &PARAM->_long.value, sizeof(int64_t)); + goto __END; + +__SINGLE: + + memcpy(RETURN->_string.addr, &PARAM->_single.value, sizeof(float)); + goto __END; + +__FLOAT: + + memcpy(RETURN->_string.addr, &PARAM->_float.value, sizeof(double)); + goto __END; + +__DATE: + + memcpy(RETURN->_string.addr, &PARAM->_date.date, sizeof(int) * 2); + goto __END; + +__POINTER: + + memcpy(RETURN->_string.addr, &PARAM->_pointer.value, sizeof(void *)); + goto __END; + +__ERROR: + + THROW_ILLEGAL(); + +__END: + + SUBR_LEAVE(); +} + + diff --git a/main/gbx/gbx_subr_file.c b/main/gbx/gbx_subr_file.c new file mode 100755 index 00000000..9fccf97f --- /dev/null +++ b/main/gbx/gbx_subr_file.c @@ -0,0 +1,1152 @@ +/*************************************************************************** + + gbx_subr_file.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" +#include "gb_common_buffer.h" + +#include "gbx_subr.h" +#include "gb_file.h" +#include "gb_list.h" +#include "gbx_stream.h" +#include "gbx_archive.h" +#include "gbx_api.h" +#include "gbx_local.h" +#include "gbx_regexp.h" +#include "gbx_string.h" +#include "gbx_c_file.h" +#include "gbx_math.h" + +#include + +typedef + struct _stream { + struct _stream *next; + CSTREAM *stream; + } + CSTREAM_NODE; + +static void *_default_in = NULL; +static void *_default_out = NULL; +static void *_default_err = NULL; + +static GB_ARRAY _result; +static char *_pattern; +static int _len_pattern; +static int _ignore; + +static STREAM *_stream; + + +static void push_stream(void **list, CSTREAM *stream) +{ + CSTREAM_NODE *slot; + + ALLOC(&slot, sizeof(CSTREAM_NODE)); + slot->stream = stream; + //OBJECT_REF(stream); + + slot->next = *list; + *list = slot; +} + + +static CSTREAM *pop_stream(void **list) +{ + CSTREAM *stream; + CSTREAM_NODE *slot; + + if (!*list) + return NULL; + + stream = ((CSTREAM_NODE *)*list)->stream; + slot = *list; + *list = slot->next; + FREE(&slot); + + return stream; +} + +static STREAM *get_default(intptr_t val) +{ + STREAM *stream = NULL; + + switch(val) + { + case 0: + if (_default_in) + stream = CSTREAM_stream(((CSTREAM_NODE *)_default_in)->stream); + else if (CFILE_in) + stream = CSTREAM_stream(CFILE_in); + break; + case 1: + if (_default_out) + stream = CSTREAM_stream(((CSTREAM_NODE *)_default_out)->stream); + else if (CFILE_out) + stream = CSTREAM_stream(CFILE_out); + break; + case 2: + if (_default_err) + stream = CSTREAM_stream(((CSTREAM_NODE *)_default_err)->stream); + else if (CFILE_err) + stream = CSTREAM_stream(CFILE_err); + break; + } + + if (!stream) + THROW(E_CLOSED); + + return stream; +} + +static inline STREAM *_get_stream(VALUE *value, bool can_default) +{ + STREAM *stream; + + VARIANT_undo(value); + + if ((can_default) && TYPE_is_integer(value->type) && value->_integer.value >= 0 && value->_integer.value <= 2) + stream = get_default((intptr_t)(value->_integer.value)); + else + { + if (TYPE_is_object(value->type) && value->_object.object && OBJECT_class(value->_object.object)->is_stream) + stream = CSTREAM_stream(value->_object.object); + else + { + if (VALUE_is_null(value)) + THROW(E_NULL); + + VALUE_conv_object(value, (TYPE)CLASS_Stream); + stream = NULL; + } + } + + return stream; +} + +/*#define _get_stream(_value, _can_default) \ + STREAM *stream; \ + \ + VARIANT_undo(_value); \ + \ + if ((_can_default) && TYPE_is_integer((_value)->type) && (_value)->_integer.value >= 0 && (_value)->_integer.value <= 2) \ + stream = get_default((intptr_t)(_value)->_integer.value); \ + else \ + { \ + if (TYPE_is_object((_value)->type) && (_value)->_object.object && OBJECT_class((_value)->_object.object)->is_stream) \ + stream = CSTREAM_stream((_value)->_object.object); \ + else \ + { \ + if (VALUE_is_null(_value)) \ + THROW(E_NULL); \ + \ + VALUE_conv_object((_value), (TYPE)CLASS_Stream); \ + stream = NULL; \ + } \ + }*/ + +#define get_stream(_value, _can_default) \ +({ \ + STREAM *_stream = _get_stream(_value, _can_default); \ + \ + if (STREAM_is_closed(_stream)) \ + THROW(E_CLOSED); \ + \ + _stream; \ +}) + +#define get_stream_for_writing(_value, _can_default) \ +({ \ + STREAM *_stream = _get_stream(_value, _can_default); \ + \ + if (STREAM_is_closed_for_writing(_stream)) \ + THROW(E_CLOSED); \ + \ + _stream; \ +}) + +static char *get_path(VALUE *param) +{ + char *name; + int len; + + SUBR_get_string_len(param, &name, &len); + + return STRING_conv_file_name(name, len); +} + +void SUBR_open(ushort code) +{ + CFILE *file; + STREAM stream; + int mode; + void *addr; + + SUBR_ENTER_PARAM(2); + + SUBR_check_integer(&PARAM[1]); + mode = PARAM[1]._integer.value; + + if (code & 0x3F) + { + if (TYPE_is_pointer(PARAM->type)) + addr = (void *)PARAM->_pointer.value; + else + THROW_TYPE(T_POINTER, PARAM->type); + + STREAM_open(&stream, (char *)addr, mode | STO_MEMORY); + } + else if (mode & STO_STRING) + { + char *str; + + STREAM_open(&stream, NULL, mode); + + if (!VALUE_is_null(PARAM)) + { + str = SUBR_get_string(PARAM); + + if (mode & STO_WRITE) + { + stream.string.buffer = STRING_new(str, STRING_length(str)); + } + else + { + stream.string.buffer = str; + STRING_ref(str); + } + stream.string.size = STRING_length(str); + } + } + else + { + STREAM_open(&stream, get_path(PARAM), mode); + } + + file = CFILE_create(&stream, mode); + + OBJECT_put(RETURN, file); + + SUBR_LEAVE(); +} + + +void SUBR_close(void) +{ + STREAM *stream; + + SUBR_ENTER_PARAM(1); + + stream = get_stream(PARAM, FALSE); + + if (stream->type == &STREAM_string) + { + char *buffer = stream->string.buffer; + + RETURN->type = T_STRING; + RETURN->_string.addr = buffer; + RETURN->_string.start = 0; + RETURN->_string.len = stream->string.size; + + STRING_ref(buffer); + STREAM_close(stream); + STRING_free_later(buffer); + + //fprintf(stderr, "buffer ref = %d\n", STRING_from_ptr(buffer)->ref); + + SUBR_LEAVE(); + } + else + { + STREAM_close(stream); + SUBR_LEAVE_VOID(); + } +} + + +void SUBR_flush(void) +{ + SUBR_ENTER_PARAM(1); + + STREAM_flush(get_stream(PARAM, TRUE)); + + SUBR_LEAVE_VOID(); +} + + +/*static void print_it(char *addr, long len) +{ + STREAM_write(_stream, addr, len); +}*/ + +void SUBR_print(ushort code) +{ + int i; + char *addr; + int len; + + SUBR_ENTER(); + + if (NPARAM < 1) + THROW(E_NEPARAM); + + _stream = get_stream_for_writing(PARAM, TRUE); + + //PRINT_init(print_it, FALSE); + + for (i = 1; i < NPARAM; i++) + { + PARAM++; + //PRINT_value(PARAM); + VALUE_to_string(PARAM, &addr, &len); + if (len == 1 && *addr == '\n') + STREAM_write_eol(_stream); + else + STREAM_write(_stream, addr, len); + } + + SUBR_LEAVE_VOID(); +} + + +void SUBR_linput(void) +{ + STREAM *stream; + char *addr; + + stream = get_stream(&SP[-1], TRUE); + + addr = STREAM_line_input(stream, NULL); + + SP--; + if (!TYPE_is_integer(SP->type)) + RELEASE_OBJECT(SP); + + SP->type = T_STRING; + SP->_string.addr = addr; + SP->_string.start = 0; + SP->_string.len = STRING_length(addr); + + SP++; +} + + +void SUBR_input(ushort code) +{ + static STREAM *stream = NULL; + char *addr; + + SUBR_ENTER(); + + if (NPARAM == 1) + stream = get_stream(PARAM, TRUE); + + if (stream) + addr = STREAM_input(stream); + else + addr = NULL; + + if (NPARAM == 1) + { + SP--; + if (!TYPE_is_integer(SP->type)) + RELEASE_OBJECT(SP); + } + + if (addr) + { + //VALUE_from_string(SP, addr, STRING_length(addr)); + SP->type = T_STRING; + SP->_string.addr = addr; + SP->_string.start = 0; + SP->_string.len = STRING_length(addr); + } + else + VALUE_null(SP); + + SP++; +} + + +void SUBR_eof(ushort code) +{ + STREAM *stream; + bool eof; + + SUBR_ENTER(); + + if (NPARAM == 1) + { + stream = get_stream(PARAM, FALSE); + eof = STREAM_eof(stream); + RELEASE_OBJECT(PARAM); + SP--; + } + else + { + stream = get_default(0); + eof = STREAM_eof(stream); + } + + SP->type = T_BOOLEAN; + SP->_boolean.value = (-eof); + SP++; +} + + +void SUBR_lof(ushort code) +{ + STREAM *stream; + + SUBR_ENTER(); + + if (NPARAM == 1) + stream = get_stream(PARAM, FALSE); + else + stream = get_default(0); + + RETURN->type = T_LONG; + STREAM_lof(stream, &(RETURN->_long.value)); + + SUBR_LEAVE(); +} + + +void SUBR_seek(ushort code) +{ + STREAM *stream; + int64_t pos; + int64_t len; + int whence = SEEK_SET; + + SUBR_ENTER(); + + stream = get_stream(PARAM, FALSE); + + if (NPARAM >= 2) + { + VALUE_conv(&PARAM[1], T_LONG); + pos = PARAM[1]._long.value; + + if (NPARAM == 3) + { + VALUE_conv_integer(&PARAM[2]); + whence = PARAM[2]._integer.value; + if (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) + THROW(E_ARG); + } + else + { + if (pos < 0) + { + STREAM_lof(stream, &len); + pos += len; + } + } + + STREAM_seek(stream, pos, (int)whence); + RETURN->type = T_VOID; + } + else + { + RETURN->type = T_LONG; + RETURN->_long.value = STREAM_tell(stream); + } + + SUBR_LEAVE(); +} + +void SUBR_read(ushort code) +{ + STREAM *stream; + char *data; + int len = 0; + int eff; + + SUBR_ENTER_PARAM(2); + + stream = get_stream(PARAM, TRUE); + + if (code & 0x3F) + { + VALUE_conv_integer(&PARAM[1]); + len = PARAM[1]._integer.value; + + if (len == 0) + { + VALUE_null(RETURN); + } + else if (len > 0) + { + data = STRING_new_temp(NULL, len); + + STREAM_read(stream, data, len); + + RETURN->type = T_STRING; + RETURN->_string.addr = data; + RETURN->_string.start = 0; + RETURN->_string.len = len; + } + else + { + len = (-len); + + data = STRING_new(NULL, len); + + eff = STREAM_read_max(stream, data, len); + + if (eff == 0) + { + VALUE_null(RETURN); + STRING_free(&data); + } + else + { + if (eff < len) + { + data = STRING_extend(data, eff); + len = eff; + } + + STRING_extend_end(data); + + RETURN->type = T_STRING; + RETURN->_string.addr = data; + RETURN->_string.start = 0; + RETURN->_string.len = len; + } + } + } + else + { + TYPE type = SUBR_get_type(&PARAM[1]); + STREAM_read_type(stream, type, RETURN); + } + + SUBR_LEAVE(); +} + + +void SUBR_write(ushort code) +{ + STREAM *stream; + + SUBR_ENTER_PARAM(3); + + stream = get_stream_for_writing(PARAM, TRUE); + + if (code & 0x3F) + { + char *str; + int len; + int lenw; + + VALUE_conv_integer(&PARAM[2]); + lenw = PARAM[2]._integer.value; + + if (TYPE_is_pointer(PARAM[1].type)) + { + if (lenw < 0) + lenw = 0; + len = lenw; + str = (char *)PARAM[1]._pointer.value; + fprintf(stderr, "%p\n", str); + } + else + { + SUBR_get_string_len(&PARAM[1], &str, &len); + if (lenw < 0) + lenw = len; + } + + if (lenw > 0) + { + STREAM_write(stream, str, Min(len, lenw)); + if (lenw > len) + STREAM_write_zeros(stream, lenw - len); + } + } + else + { + TYPE type; + type = SUBR_get_type(&PARAM[2]); + VALUE_conv(&PARAM[1], type); + STREAM_write_type(stream, type, &PARAM[1]); + } + + SUBR_LEAVE_VOID(); +} + + +void SUBR_stat(ushort code) +{ + const char *path; + CSTAT *cstat; + FILE_STAT info; + bool follow = FALSE; + + SUBR_ENTER(); + + path = get_path(PARAM); + + if (NPARAM == 2) + follow = SUBR_get_boolean(&PARAM[1]); + + FILE_stat(path, &info, follow); + + cstat = OBJECT_new(CLASS_Stat, NULL, NULL); + OBJECT_UNREF_KEEP(cstat); + cstat->info = info; + cstat->path = STRING_new_zero(path); + + RETURN->_object.class = CLASS_Stat; + RETURN->_object.object = cstat; + + SUBR_LEAVE(); +} + + +void SUBR_exist(ushort code) +{ + bool exist; + const char *path; + bool follow = FALSE; + + SUBR_ENTER(); + + if (!NPARAM) + { + NPARAM++; + PARAM--; + } + + path = get_path(PARAM); + + if (NPARAM == 2) + follow = SUBR_get_boolean(&PARAM[1]); + + exist = FILE_exist_follow(path, follow); + + RETURN->type = T_BOOLEAN; + RETURN->_integer.value = exist ? -1 : 0; + + SUBR_LEAVE(); +} + + +void SUBR_dir(ushort code) +{ + GB_ARRAY array; + const char *path; + char *pattern; + int len_pattern; + char *str; + int attr = 0; + + SUBR_ENTER(); + + path = get_path(PARAM); + + if (NPARAM >= 2) + { + pattern = SUBR_get_string(&PARAM[1]); + if (NPARAM == 3) + attr = SUBR_get_integer(&PARAM[2]); + } + else + pattern = NULL; + + FILE_dir_first(path, pattern, attr); + + GB_ArrayNew(&array, T_STRING, 0); + + while (!FILE_dir_next(&pattern, &len_pattern)) + { + if (!LOCAL_is_UTF8) + { + if (STRING_conv(&str, pattern, len_pattern, LOCAL_encoding, SC_UTF8, FALSE)) + str = STRING_new(pattern, len_pattern); + else + STRING_ref(str); + } + else + str = STRING_new(pattern, len_pattern); + + *((char **)GB_ArrayAdd(array)) = str; + } + + RETURN->_object.class = OBJECT_class(array); + RETURN->_object.object = array; + + SUBR_LEAVE(); +} + + +static void found_file(const char *path) +{ + char *str; + int len; + + path += _ignore; + len = strlen(path); + + if (_pattern && !REGEXP_match(_pattern, _len_pattern, path, len)) + return; + + if (!LOCAL_is_UTF8) + { + if (STRING_conv(&str, path, len, LOCAL_encoding, SC_UTF8, FALSE)) + str = STRING_new(path, len); + else + STRING_ref(str); + } + else + str = STRING_new(path, len); + + *((char **)GB_ArrayAdd(_result)) = str; +} + +void SUBR_rdir(ushort code) +{ + const char *path; + int attr = 0; + bool follow = FALSE; + + SUBR_ENTER(); + + path = get_path(PARAM); + + if (NPARAM >= 2) + { + SUBR_get_string_len(&PARAM[1], &_pattern, &_len_pattern); + if (NPARAM >= 3) + { + attr = SUBR_get_integer(&PARAM[2]); + if (NPARAM == 4) + follow = SUBR_get_boolean(&PARAM[3]); + } + } + else + _pattern = NULL; + + GB_ArrayNew(&_result, T_STRING, 0); + + if (!path || *path == 0) + path = "."; + _ignore = strlen(path); + if (_ignore > 0 && path[_ignore - 1] != '/') + _ignore++; + + FILE_recursive_dir(path, found_file, NULL, attr, follow); + + RETURN->_object.class = OBJECT_class(_result); + RETURN->_object.object = _result; + + SUBR_LEAVE(); +} + + +void SUBR_kill(ushort code) +{ + SUBR_ENTER_PARAM(1); + + switch(code & 0xFF) + { + case 0: + FILE_unlink(get_path(PARAM)); + break; + + case 1: + FILE_mkdir(get_path(PARAM)); + break; + + case 2: + FILE_rmdir(get_path(PARAM)); + break; + + default: + THROW_ILLEGAL(); + } + + SUBR_LEAVE_VOID(); +} + + +void SUBR_mkdir(ushort code) +{ + SUBR_ENTER_PARAM(1); + + switch (code & 0xFF) + { + case 0: // Deprecated Mkdir + SUBR_kill(1); + return; + + case 1: // Even + VALUE_conv(PARAM, T_LONG); + PARAM->type = GB_T_BOOLEAN; + PARAM->_boolean.value = (PARAM->_long.value & 1) == 0 ? -1 : 0; + break; + + case 2: // Odd + VALUE_conv(PARAM, T_LONG); + PARAM->type = GB_T_BOOLEAN; + PARAM->_boolean.value = (PARAM->_long.value & 1) != 0 ? -1 : 0; + break; + + default: + THROW_ILLEGAL(); + } +} + + +void SUBR_rmdir(ushort code) +{ + SUBR_ENTER(); + int min, max; + + switch (code & 0x3F) + { + case 0: // Deprecated RmDir + SUBR_kill(2); + return; + + default: // Rand + + if (NPARAM == 1) + { + VALUE_conv_integer(PARAM); + min = 0; + max = PARAM->_integer.value; + } + else + { + VALUE_conv_integer(PARAM); + VALUE_conv_integer(&PARAM[1]); + min = PARAM->_integer.value; + max = PARAM[1]._integer.value; + if (max < min) + { + int temp = max; + max = min; + min = temp; + } + } + + RETURN->type = T_INTEGER; + RETURN->_integer.value = min + (int)(rnd() * (max + 1 - min)); + break; + } + + SUBR_LEAVE(); +} + + +void SUBR_move(ushort code) +{ + char *path; + char *auth; + FILE_STAT info; + + SUBR_ENTER_PARAM(2); + + path = get_path(&PARAM[0]); + + switch (code & 0xFF) + { + case 0: // Move + + FILE_rename(path, get_path(&PARAM[1])); + break; + + case 1: // Copy + + FILE_copy(path, get_path(&PARAM[1])); + break; + + case 2: // Link + + /* Parameters are NOT inverted ANYMORE! */ + FILE_link(path, get_path(&PARAM[1])); + break; + + case 3: // Chmod + + auth = SUBR_get_string(&PARAM[1]); + FILE_stat(path, &info, TRUE); + FILE_chmod(path, FILE_mode_from_string(info.mode, auth)); + break; + + case 4: // Chown + + auth = SUBR_get_string(&PARAM[1]); + FILE_chown(path, auth); + break; + + case 5: // Chgrp + + auth = SUBR_get_string(&PARAM[1]); + FILE_chgrp(path, auth); + break; + + case 6: // Move DownTo + + FILE_rename_unlink(path, get_path(&PARAM[1])); + break; + + default: + THROW_ILLEGAL(); + } + + SUBR_LEAVE_VOID(); +} + + +void SUBR_link(ushort code) +{ + SUBR_ENTER_PARAM(1); + + switch (code & 0xFF) + { + case 0: // Deprecated Link + SUBR_move(2); + return; + + case 1: // IsNan + VALUE_conv(PARAM, T_FLOAT); + PARAM->type = GB_T_BOOLEAN; + PARAM->_boolean.value = isnan(PARAM->_float.value) ? -1 : 0; + break; + + case 2: // IsInf + VALUE_conv(PARAM, T_FLOAT); + PARAM->type = GB_T_INTEGER; + PARAM->_integer.value = isinf(PARAM->_float.value); + break; + + default: + THROW_ILLEGAL(); + } +} + + +void SUBR_temp(ushort code) +{ + char *temp; + int len; + + SUBR_ENTER(); + + if (NPARAM == 0) + temp = FILE_make_temp(&len, NULL); + else + temp = FILE_make_temp(&len, SUBR_get_string(PARAM)); + + STRING_new_temp_value(RETURN, temp, len); + + SUBR_LEAVE(); +} + + +void SUBR_isdir(void) +{ + bool isdir; + const char *path; + + SUBR_ENTER_PARAM(1); + + path = get_path(PARAM); + + isdir = FILE_is_dir(path); + + RETURN->type = T_BOOLEAN; + RETURN->_integer.value = isdir ? -1 : 0; + + SUBR_LEAVE(); +} + + +void SUBR_access(ushort code) +{ + int access; + + SUBR_ENTER(); + + if (NPARAM == 1) + access = R_OK; + else + { + VALUE_conv_integer(&PARAM[1]); + access = PARAM[1]._integer.value; + } + + RETURN->type = T_BOOLEAN; + RETURN->_integer.value = FILE_access(get_path(PARAM), access) ? -1 : 0; + + SUBR_LEAVE(); +} + + +void SUBR_lock(ushort code) +{ + code &= 0x1F; + + if (code == 1) + { + SUBR_ENTER_PARAM(1); + STREAM_close(get_stream(PARAM, FALSE)); + SUBR_LEAVE_VOID(); + } + else + { + SUBR_ENTER_PARAM((code == 2) ? 2 : 1); + STREAM stream; + CFILE *file; + const char *path = get_path(PARAM); + double wait, timer; + + if (FILE_is_relative(path)) + THROW(E_BADPATH); + + if (code == 2) + { + DATE_timer(&wait, FALSE); + wait += SUBR_get_float(&PARAM[1]); + } + + for(;;) + { + STREAM_open(&stream, path, STO_LOCK); + + if (!STREAM_lock_all(&stream) && FILE_exist(path)) + break; + + STREAM_close(&stream); + + if (code == 2) + { + DATE_timer(&timer, FALSE); + if (timer < wait) + { + usleep(10000); + continue; + } + } + + THROW(E_LOCK); + } + + file = CFILE_create(&stream, STO_LOCK); + OBJECT_put(RETURN, file); + SUBR_LEAVE(); + } +} + + +void SUBR_inp_out(ushort code) +{ + CSTREAM *stream; + void **where; + + SUBR_ENTER_PARAM(1); + + switch(code & 0x1F) + { + case 0: where = &_default_in; break; + case 1: where = &_default_out; break; + default: where = &_default_err; break; + } + + if (VALUE_is_null(PARAM)) + { + stream = pop_stream(where); + if (stream) + OBJECT_UNREF(stream); + return; + } + + VALUE_conv_object(PARAM, (TYPE)CLASS_Stream); + + stream = PARAM->_object.object; + OBJECT_REF(stream); + + push_stream(where, stream); + + SUBR_LEAVE_VOID(); +} + +static void free_list(void **list) +{ + CSTREAM *stream; + + for(;;) + { + stream = pop_stream(list); + if (!stream) + return; + OBJECT_UNREF(stream); + } +} + +void SUBR_exit_inp_out(void) +{ + free_list((void **)&_default_in); + free_list((void **)&_default_out); + free_list((void **)&_default_err); +} + + +void SUBR_dfree(void) +{ + SUBR_ENTER_PARAM(1); + + RETURN->type = T_LONG; + RETURN->_long.value = FILE_free(get_path(PARAM)); + + SUBR_LEAVE(); +} + +void SUBR_debug(ushort code) +{ + SUBR_ENTER(); + + if (NPARAM == 0) + { + STREAM *stream = get_default(2); + const char *s = DEBUG_get_current_position(); + + STREAM_write(stream, (void *)s, strlen(s)); + STREAM_write(stream, ": ", 2); + + RETURN->type = T_INTEGER; + RETURN->_integer.value = 2; + } + else if (NPARAM == 1) + { + VALUE_conv_boolean(PARAM); + if (PARAM->_boolean.value == 0) + THROW(E_ASSERT); + } + + SUBR_LEAVE(); +} + diff --git a/main/gbx/gbx_subr_math.c b/main/gbx/gbx_subr_math.c new file mode 100644 index 00000000..13869867 --- /dev/null +++ b/main/gbx/gbx_subr_math.c @@ -0,0 +1,942 @@ +/*************************************************************************** + + gbx_subr_math.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_SUBR_MATH_C + +#ifdef OS_SOLARIS +/* Make math.h define M_PI and a few other things */ +#define __EXTENSIONS__ +/* Get definition for finite() */ +#include +#endif + +#include "gb_common.h" +#include "gbx_value.h" +#include "gbx_subr.h" +#include "gbx_math.h" + + +#define ABS(x) ((x) < 0 ? (-x) : (x)) + +#define SMT_NAME SUBR_quo +#define SMT_TYPE 3 +#define SMT_OP / + +#include "gbx_subr_math_temp.h" + + +#define SMT_NAME SUBR_rem +#define SMT_TYPE 3 +#define SMT_OP % + +#include "gbx_subr_math_temp.h" + +void SUBR_pi(ushort code) +{ + SUBR_ENTER(); + + if (NPARAM == 0) + { + SP->type = T_FLOAT; + SP->_float.value = M_PI; + SP++; + } + else + { + VALUE_conv_float(PARAM); + PARAM->_float.value = M_PI * PARAM->_float.value; + } +} + + +void SUBR_randomize(ushort code) +{ + SUBR_ENTER(); + + if (NPARAM == 0) + randomize(FALSE, 0); + else + randomize(TRUE, (uint)SUBR_get_integer(PARAM)); + + RETURN->type = T_VOID; + + SUBR_LEAVE(); +} + + +void SUBR_rnd(ushort code) +{ + double min = 0.0, max = 1.0; + + SUBR_ENTER(); + + if (NPARAM >= 1) + { + VALUE_conv_float(&PARAM[0]); + max = PARAM->_float.value; + } + + if (NPARAM == 2) + { + min = max; + VALUE_conv_float(&PARAM[1]); + max = PARAM[1]._float.value; + } + + RETURN->type = T_FLOAT; + RETURN->_float.value = (rnd() * (max - min)) + min; + + SUBR_LEAVE(); +} + + +void SUBR_round(ushort code) +{ + int val = 0; + double power; + + SUBR_ENTER(); + + if (NPARAM == 2) + val = SUBR_get_integer(&PARAM[1]); + + power = pow(10, val); + + VALUE_conv_float(&PARAM[0]); + + RETURN->type = T_FLOAT; + /*RETURN->_float.value = rint(PARAM->_float.value / power) * power;*/ + RETURN->_float.value = floor(PARAM->_float.value / power + 0.5) * power; + + SUBR_LEAVE(); +} + + +void SUBR_math(ushort code) +{ + static void *jump[] = { + NULL, &&__FRAC, &&__LOG, &&__EXP, &&__SQRT, &&__SIN, &&__COS, &&__TAN, &&__ATAN, &&__ASIN, &&__ACOS, + &&__DEG, &&__RAD, &&__LOG10, &&__SINH, &&__COSH, &&__TANH, &&__ASINH, &&__ACOSH, &&__ATANH, + &&__EXP2, &&__EXP10, &&__LOG2, &&__CBRT, &&__EXPM1, &&__LOG1P, &&__FLOOR, &&__CEIL + }; + + SUBR_ENTER_PARAM(1); + + VALUE_conv_float(PARAM); + goto *jump[code & 0x1F]; + +__FRAC: PARAM->_float.value = frac(PARAM->_float.value); goto __END; +__LOG: PARAM->_float.value = __builtin_log(PARAM->_float.value); goto __END; +__EXP: PARAM->_float.value = __builtin_exp(PARAM->_float.value); goto __END; +__SQRT: PARAM->_float.value = __builtin_sqrt(PARAM->_float.value); goto __END; +__SIN: PARAM->_float.value = __builtin_sin(PARAM->_float.value); goto __END; +__COS: PARAM->_float.value = __builtin_cos(PARAM->_float.value); goto __END; +__TAN: PARAM->_float.value = __builtin_tan(PARAM->_float.value); goto __END; +__ATAN: PARAM->_float.value = __builtin_atan(PARAM->_float.value); goto __END; +__ASIN: PARAM->_float.value = __builtin_asin(PARAM->_float.value); goto __END; +__ACOS: PARAM->_float.value = __builtin_acos(PARAM->_float.value); goto __END; +__DEG: PARAM->_float.value = deg(PARAM->_float.value); goto __END; +__RAD: PARAM->_float.value = rad(PARAM->_float.value); goto __END; +__LOG10: PARAM->_float.value = log10(PARAM->_float.value); goto __END; +__SINH: PARAM->_float.value = __builtin_sinh(PARAM->_float.value); goto __END; +__COSH: PARAM->_float.value = __builtin_cosh(PARAM->_float.value); goto __END; +__TANH: PARAM->_float.value = __builtin_tanh(PARAM->_float.value); goto __END; +__ASINH: PARAM->_float.value = __builtin_asinh(PARAM->_float.value); goto __END; +__ACOSH: PARAM->_float.value = __builtin_acosh(PARAM->_float.value); goto __END; +__ATANH: PARAM->_float.value = __builtin_atanh(PARAM->_float.value); goto __END; +__EXP2: PARAM->_float.value = __builtin_exp2(PARAM->_float.value); goto __END; +#if defined(__clang__) + __EXP10: PARAM->_float.value = exp10(PARAM->_float.value); goto __END; +#else + __EXP10: PARAM->_float.value = __builtin_exp10(PARAM->_float.value); goto __END; +#endif +__LOG2: PARAM->_float.value = __builtin_log2(PARAM->_float.value); goto __END; +__CBRT: PARAM->_float.value = __builtin_cbrt(PARAM->_float.value); goto __END; +__EXPM1: PARAM->_float.value = __builtin_expm1(PARAM->_float.value); goto __END; +__LOG1P: PARAM->_float.value = __builtin_log1p(PARAM->_float.value); goto __END; +__FLOOR: PARAM->_float.value = __builtin_floor(PARAM->_float.value); goto __END; +__CEIL: PARAM->_float.value = __builtin_ceil(PARAM->_float.value); goto __END; + +__END: + + //fprintf(stderr, "m: %.24g\n", PARAM->_float.value); + if (!isfinite(PARAM->_float.value)) + THROW(E_MATH); +} + + +void SUBR_math2(ushort code) +{ + static void *jump[] = { NULL, &&__ATAN2, &&__ANG, &&__HYPOT }; + + SUBR_ENTER_PARAM(2); + + VALUE_conv_float(&PARAM[0]); + VALUE_conv_float(&PARAM[1]); + + goto *jump[code & 0x1F]; + +__ATAN2: PARAM->_float.value = __builtin_atan2(PARAM[0]._float.value, PARAM[1]._float.value); goto __END; +__ANG: PARAM->_float.value = __builtin_atan2(PARAM[1]._float.value, PARAM[0]._float.value); goto __END; +__HYPOT: PARAM->_float.value = sqrt(PARAM[0]._float.value * PARAM[0]._float.value + PARAM[1]._float.value * PARAM[1]._float.value); goto __END; + +__END: + + if (!finite(PARAM->_float.value)) + THROW(E_MATH); + + SP--; +} + + +void SUBR_pow(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__NUMBER_INTEGER, &&__NUMBER_FLOAT, &&__OBJECT_FLOAT, &&__OBJECT_OTHER, &&__OBJECT_OBJECT + }; + + VALUE *P1, *P2; + uchar type; + bool variant = FALSE; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x0F; + goto *jump[type]; + +__VARIANT: + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + if (TYPE_is_number(P1->type) && TYPE_is_number(P2->type)) + { + if (TYPE_is_integer(P2->type)) + type = 1; + else + type = 2; + } + else + { + type = EXEC_check_operator(P1, P2, CO_POW); + + if (type == OP_OBJECT_FLOAT) + type = 3; + else if (type == OP_OBJECT_OTHER) + type = 4; + else if (type == OP_OBJECT_OBJECT) + type = 5; + else + THROW(E_TYPE, "Number", TYPE_get_name(P2->type)); + } + + if (!variant) + { + if (P1->type != T_OBJECT && P2->type != T_OBJECT) + *PC |= type; + goto *jump[type]; + } + else + { + SUBR_pow(type); + VALUE_conv_variant(P1); + return; + } + +__NUMBER_INTEGER: + + { + static void *ni_jump[] = { &&__M4, &&__M3, &&__M2, &&__M1, &&__P0, &&__END, &&__P2, &&__P3, &&__P4 }; + int val = P2->_integer.value; + + VALUE_conv_float(P1); + + if (val >= -4 && val <= 4) + goto *ni_jump[val + 4]; + else + goto __NUMBER_FLOAT; + + __P0: P1->_float.value = 1.0; goto __END; + __P2: P1->_float.value *= P1->_float.value; goto __END_NUMBER; + __P3: P1->_float.value *= P1->_float.value * P1->_float.value; goto __END_NUMBER; + __P4: P1->_float.value = P1->_float.value * P1->_float.value * P1->_float.value * P1->_float.value; goto __END_NUMBER; + __M1: P1->_float.value = 1.0 / P1->_float.value; goto __END_NUMBER; + __M2: P1->_float.value = 1.0 / P1->_float.value / P1->_float.value; goto __END_NUMBER; + __M3: P1->_float.value = 1.0 / P1->_float.value / P1->_float.value / P1->_float.value; goto __END_NUMBER; + __M4: P1->_float.value = 1.0 / P1->_float.value / P1->_float.value / P1->_float.value / P1->_float.value; goto __END_NUMBER; + } + +__NUMBER_FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + P1->_float.value = pow(P1->_float.value, P2->_float.value); + goto __END; + +__OBJECT_FLOAT: + + EXEC_operator(OP_OBJECT_FLOAT, CO_POWF, P1, P2); + goto __END; + +__OBJECT_OTHER: + + EXEC_operator(OP_OBJECT_OTHER, CO_POWO, P1, P2); + goto __END; + +__OBJECT_OBJECT: + + EXEC_operator(OP_OBJECT_OBJECT, CO_POW, P1, P2); + goto __END; + +__END_NUMBER: + + if (!finite(P1->_float.value)) + THROW(E_MATH); + +__END: + + SP--; +} + + +void SUBR_not(ushort code) +{ + static void *jump[17] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__NULL, + &&__OBJECT + }; + + VALUE *P1; + void *jump_end; + TYPE type = code & 0x1F; + bool test; + + P1 = SP - 1; + jump_end = &&__END; + goto *jump[type]; + +__BOOLEAN: + + P1->_integer.value = P1->_integer.value ? 0 : (-1); + goto *jump_end; + +__BYTE: + + P1->_integer.value = (unsigned char)~P1->_integer.value; + goto *jump_end; + +__SHORT: + + P1->_integer.value = (short)~P1->_integer.value; + goto *jump_end; + +__INTEGER: + + P1->_integer.value = ~P1->_integer.value; + goto *jump_end; + +__LONG: + + P1->_long.value = ~P1->_long.value; + goto *jump_end; + +__SINGLE: +__FLOAT: + goto __ERROR; + +__DATE: +__STRING: +__OBJECT: +__NULL: + + test = VALUE_is_null(P1); + RELEASE(P1); + + P1->_integer.value = test ? (-1) : 0; + P1->type = T_BOOLEAN; + goto *jump_end; + +__VARIANT: + + type = P1->type; + + if (TYPE_is_variant(type)) + { + type = P1->_variant.vtype; + jump_end = &&__VARIANT_END; + VARIANT_undo(P1); + } + else if (TYPE_is_object(type)) + *PC |= T_OBJECT; + else if (type) + *PC |= type; + else + goto __ERROR; + + if (TYPE_is_object(type)) + goto __OBJECT; + else + goto *jump[type]; + +__ERROR: + + THROW(E_TYPE, "Number, String or Object", TYPE_get_name(type)); + +__VARIANT_END: + + VALUE_conv_variant(P1); + +__END: + return; +} + +void SUBR_and_(ushort code) +{ + static void *jump[] = { + &&__UNKNOWN, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__OTHER, &&__VARIANT, &&__ERROR + }; + + TYPE type; + VALUE *P1, *P2; + void *jump_end; + short op; + + P1 = SP - 2; + P2 = P1 + 1; + + jump_end = &&__END; + type = code & 0x0F; + op = (code >> 8) - (C_AND >> 8); + goto *jump[type]; + +__BYTE: + + P1->type = type; + + { + static void *exec[] = { &&__AND_C, && __OR_C, &&__XOR_C }; + goto *exec[op]; + + __AND_C: P1->_integer.value = (unsigned char)(P1->_integer.value & P2->_integer.value); goto *jump_end; + __OR_C: P1->_integer.value = (unsigned char)(P1->_integer.value | P2->_integer.value); goto *jump_end; + __XOR_C: P1->_integer.value = (unsigned char)(P1->_integer.value ^ P2->_integer.value); goto *jump_end; + } + + goto *jump_end; + +__SHORT: + + P1->type = type; + + { + static void *exec[] = { &&__AND_H, && __OR_H, &&__XOR_H }; + goto *exec[op]; + + __AND_H: P1->_integer.value = (short)(P1->_integer.value & P2->_integer.value); goto *jump_end; + __OR_H: P1->_integer.value = (short)(P1->_integer.value | P2->_integer.value); goto *jump_end; + __XOR_H: P1->_integer.value = (short)(P1->_integer.value ^ P2->_integer.value); goto *jump_end; + } + + goto *jump_end; + +__BOOLEAN: +__INTEGER: + + P1->type = type; + + { + static void *exec[] = { &&__AND_I, && __OR_I, &&__XOR_I }; + goto *exec[op]; + + __AND_I: P1->_integer.value &= P2->_integer.value; goto *jump_end; + __OR_I: P1->_integer.value |= P2->_integer.value; goto *jump_end; + __XOR_I: P1->_integer.value ^= P2->_integer.value; goto *jump_end; + } + + goto *jump_end; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + P1->type = type; + + { + static void *exec[] = { &&__AND_L, && __OR_L, &&__XOR_L }; + goto *exec[op]; + + __AND_L: P1->_long.value &= P2->_long.value; goto *jump_end; + __OR_L: P1->_long.value |= P2->_long.value; goto *jump_end; + __XOR_L: P1->_long.value ^= P2->_long.value; goto *jump_end; + } + + goto *jump_end; + +__UNKNOWN: + + type = Max(P1->type, P2->type); + + if (!TYPE_is_integer_long(type)) + { + if (P1->type != T_VARIANT && P2->type != T_VARIANT) + type = 6; + else + type = 7; + } + + *PC |= type; + goto *jump[type]; + +__OTHER: + + if (!TYPE_is_integer_long(P1->type)) + VALUE_convert_boolean(P1); + + if (!TYPE_is_integer_long(P2->type)) + VALUE_convert_boolean(P2); + + type = Max(P1->type, P2->type); + goto *jump[type]; + +__VARIANT: + + if (TYPE_is_variant(P1->type)) + VARIANT_undo(P1); + + if (TYPE_is_variant(P2->type)) + VARIANT_undo(P2); + + if (!TYPE_is_integer_long(P1->type)) + VALUE_convert_boolean(P1); + + if (!TYPE_is_integer_long(P2->type)) + VALUE_convert_boolean(P2); + + type = Max(P1->type, P2->type); + + if (TYPE_is_integer_long(type)) + { + jump_end = &&__VARIANT_END; + goto *jump[type]; + } + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); + +__VARIANT_END: + + VALUE_conv_variant(P1); + +__END: + + SP--; +} + +#define MANAGE_VARIANT(_func) \ +({ \ + type = P1->type; \ + \ + if (TYPE_is_number_date(type)) \ + { \ + *PC |= type; \ + goto *jump[type]; \ + } \ + \ + if (TYPE_is_variant(type)) \ + { \ + type = P1->_variant.vtype; \ + if (TYPE_is_number_date(type)) \ + { \ + VARIANT_undo(P1); \ + (_func)(code | type); \ + VALUE_conv_variant(P1); \ + return; \ + } \ + } \ +}) + +#define MANAGE_VARIANT_OBJECT(_func, _op) \ +({ \ + type = P1->type; \ + \ + if (TYPE_is_number_date(type)) \ + { \ + *PC |= type; \ + goto *jump[type]; \ + } \ + \ + if (EXEC_check_operator_single(P1, _op)) \ + { \ + if (P1->type != T_OBJECT) \ + *PC |= T_DATE + 1; \ + goto *jump[T_DATE + 1]; \ + } \ + \ + if (TYPE_is_variant(type)) \ + { \ + VARIANT_undo(P1); \ + type = P1->type; \ + if (TYPE_is_number_date(type)) \ + { \ + (_func)(code | type); \ + VALUE_conv_variant(P1); \ + return; \ + } \ + if (EXEC_check_operator_single(P1, _op)) \ + { \ + (_func)(T_DATE + 1); \ + VALUE_conv_variant(P1); \ + return; \ + } \ + } \ +}) + +#define MANAGE_VARIANT_OBJECT_2(_func, _op, _op2) \ +({ \ + type = P1->type; \ + \ + if (TYPE_is_number_date(type)) \ + { \ + *PC |= type; \ + goto *jump[type]; \ + } \ + \ + if (EXEC_check_operator_single(P1, _op)) \ + { \ + if (P1->type != T_OBJECT) \ + *PC |= T_DATE + 1; \ + goto *jump[T_DATE + 1]; \ + } \ + if (EXEC_check_operator_single(P1, _op2)) \ + { \ + if (P1->type != T_OBJECT) \ + *PC |= T_DATE + 2; \ + goto *jump[T_DATE + 2]; \ + } \ + \ + if (TYPE_is_variant(type)) \ + { \ + VARIANT_undo(P1); \ + type = P1->type; \ + if (TYPE_is_number_date(type)) \ + { \ + (_func)(code | type); \ + VALUE_conv_variant(P1); \ + return; \ + } \ + if (EXEC_check_operator_single(P1, _op)) \ + { \ + (_func)(T_DATE + 1); \ + VALUE_conv_variant(P1); \ + return; \ + } \ + if (EXEC_check_operator_single(P1, _op2)) \ + { \ + (_func)(T_DATE + 2); \ + VALUE_conv_variant(P1); \ + return; \ + } \ + } \ +}) + + +void SUBR_sgn(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__INTEGER, &&__INTEGER, &&__INTEGER, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR, &&__OBJECT + }; + + VALUE *P1; + TYPE type; + + P1 = SP - 1; + type = code & 0x0F; + goto *jump[type]; + +__INTEGER: P1->_integer.value = lsgn(P1->_integer.value); goto __END; + +__LONG: P1->_integer.value = llsgn(P1->_long.value); goto __END; + +__SINGLE: P1->_integer.value = (P1->_single.value > 0) ? 1 : ((P1->_single.value < 0) ? (-1) : 0); goto __END; + +__FLOAT: P1->_integer.value = fsgn(P1->_float.value); goto __END; + +__OBJECT: + + EXEC_operator_object_sgn(P1); + return; + +__VARIANT: + + type = P1->type; + + if (TYPE_is_number(type)) + { + *PC |= type; + goto *jump[type]; + } + + if (EXEC_check_operator_single(P1, CO_SGN)) + { + if (P1->type != T_OBJECT) + *PC |= T_DATE + 1; + goto *jump[T_DATE + 1]; + } + + if (TYPE_is_variant(type)) + { + type = P1->_variant.vtype; + if (TYPE_is_number(type)) + { + VARIANT_undo(P1); + goto *jump[type]; + } + } + + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); + +__END: + + P1->type = T_INTEGER; +} + + +void SUBR_neg(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR, &&__OBJECT + }; + + VALUE *P1; + TYPE type; + + P1 = SP - 1; + + type = code & 0x0F; + + goto *jump[type]; + +__BOOLEAN: + + return; + +__BYTE: + + P1->_integer.value = (unsigned char)(-P1->_integer.value); return; + +__SHORT: + + P1->_integer.value = (short)(-P1->_integer.value); return; + +__INTEGER: + + P1->_integer.value = (-P1->_integer.value); return; + +__LONG: + + P1->_long.value = (-P1->_long.value); return; + +__SINGLE: + + P1->_single.value = (-P1->_single.value); return; + +__FLOAT: + + P1->_float.value = (-P1->_float.value); return; + +__OBJECT: + + EXEC_operator_object_single(CO_NEG, P1); + return; + +__VARIANT: + + MANAGE_VARIANT_OBJECT(SUBR_neg, CO_NEG); + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); +} + + +void SUBR_abs(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR, &&__OBJECT, &&__OBJECT_FLOAT + }; + + VALUE *P1; + TYPE type; + + P1 = SP - 1; + + type = code & 0x0F; + + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_INTEGER; + goto __INTEGER; + +__BYTE: + + P1->_integer.value = (unsigned char)ABS(-P1->_integer.value); return; + +__SHORT: + + P1->_integer.value = (short)ABS(P1->_integer.value); return; + +__INTEGER: + + P1->_integer.value = ABS(P1->_integer.value); return; + +__LONG: + + P1->_long.value = ABS(P1->_long.value); return; + +__SINGLE: + + P1->_single.value = fabsf(P1->_single.value); return; + +__FLOAT: + + P1->_float.value = fabs(P1->_float.value); return; + +__OBJECT: + + EXEC_operator_object_single(CO_ABS, P1); + return; + +__OBJECT_FLOAT: + + EXEC_operator_object_fabs(P1); + return; + +__VARIANT: + + MANAGE_VARIANT_OBJECT_2(SUBR_abs, CO_ABS, CO_FABS); + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); +} + + +void SUBR_int(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR + }; + + VALUE *P1; + TYPE type; + + P1 = SP - 1; + + type = code & 0x0F; + + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: +__LONG: + + return; + +__SINGLE: + + P1->_single.value = floorf(P1->_single.value); return; + +__FLOAT: + + P1->_float.value = floor(P1->_float.value); return; + +__VARIANT: + + MANAGE_VARIANT(SUBR_int); + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); +} + + +void SUBR_fix(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR + }; + + VALUE *P1; + TYPE type; + + P1 = SP - 1; + + type = code & 0x0F; + + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: +__LONG: + + return; + +__SINGLE: + + P1->_single.value = fixf(P1->_single.value); return; + +__FLOAT: + + P1->_float.value = fix(P1->_float.value); return; + +__VARIANT: + + MANAGE_VARIANT(SUBR_fix); + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); +} diff --git a/main/gbx/gbx_subr_math_temp.h b/main/gbx/gbx_subr_math_temp.h new file mode 100644 index 00000000..10b2271c --- /dev/null +++ b/main/gbx/gbx_subr_math_temp.h @@ -0,0 +1,463 @@ +/*************************************************************************** + + gbx_subr_math_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#if SMT_TYPE == 1 + +void SMT_NAME(void) +{ + #ifdef SMT_FLOAT + + static void *jump[] = { + &&__VARIANT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__ERROR + }; + + #elif defined(SMT_INTEGER) + + static void *jump[] = { + &&__VARIANT, &&__INTEGER, &&__INTEGER, &&__INTEGER, &&__INTEGER, &&__LONG, &&__ERROR, &&__ERROR, &&__ERROR + }; + + #else + + static void *jump[] = { + &&__VARIANT, &&__INTEGER, &&__INTEGER, &&__INTEGER, &&__INTEGER, &&__LONG, &&__FLOAT, &&__FLOAT, &&__ERROR + }; + + #endif + + VALUE *P1; + void *jump_end; + TYPE type = EXEC_code & 0x0F; + + P1 = SP - 1; + jump_end = &&__END; + goto *jump[type]; + +__VARIANT: + + type = P1->type; + + if (TYPE_is_number_date(type)) + { + *PC |= type; + goto *jump[type]; + } + + if (TYPE_is_variant(type)) + { + type = P1->_variant.vtype; + if (TYPE_is_number_date(type)) + { + VARIANT_undo(P1); + jump_end = &&__VARIANT_END; + goto *jump[type]; + } + } + + goto __ERROR; + +#ifndef SMT_FLOAT + +__INTEGER: + + #ifdef SMT_OP + P1->_integer.value = SMT_OP ( P1->_integer.value ); + #elif defined(SMT_FUNC_INTEGER) + P1->_integer.value = SMT_FUNC_INTEGER ( P1->_integer.value ); + #elif defined(SMT_FUNC) + P1->_integer.value = SMT_FUNC ( P1->_integer.value ); + #endif + + P1->type = type; + goto *jump_end; + +__LONG: + + VALUE_conv(P1, T_LONG); + + #ifdef SMT_OP + P1->_long.value = SMT_OP ( P1->_long.value ); + #elif defined(SMT_FUNC_LONG) + P1->_long.value = SMT_FUNC_LONG ( P1->_long.value ); + #elif defined(SMT_FUNC) + P1->_long.value = SMT_FUNC ( P1->_long.value ); + #else + #error "LONG function not defined" + #endif + + P1->type = type; + goto *jump_end; + +#endif + +#ifndef SMT_INTEGER + +__FLOAT: + + VALUE_conv_float(P1); + + #ifdef SMT_OP + P1->_float.value = SMT_OP ( P1->_float.value ); + #elif defined(SMT_FUNC_FLOAT) + P1->_float.value = SMT_FUNC_FLOAT ( P1->_float.value ); + #elif defined(SMT_FUNC) + P1->_float.value = SMT_FUNC ( P1->_float.value ); + #endif + + if (!finite(P1->_float.value)) + THROW(E_MATH); + + goto *jump_end; + +#endif + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); + +__VARIANT_END: + + VALUE_conv_variant(P1); + +__END: + return; +/* SP--;*/ +/* if (PCODE_is_void(*PC)) SP--;*/ +} + +#endif + + +#if SMT_TYPE == 2 + +void SMT_NAME(void) +{ + + #ifdef SMT_FLOAT + + static void *jump[] = { + &&__VARIANT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__FLOAT, &&__DATE + }; + + #elif defined(SMT_INTEGER) + + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__ERROR, &&__ERROR, &&__DATE + }; + + #else + + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__FLOAT, &&__FLOAT, &&__DATE + }; + + #endif + + TYPE type; + VALUE *P1, *P2; + void *jump_end; + + P1 = SP - 2; + P2 = P1 + 1; + + jump_end = &&__END; + type = EXEC_code & 0x0F; + goto *jump[type]; + +#ifndef SMT_FLOAT + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + /* + VALUE_conv(P1, type); + VALUE_conv(P2, type); + */ + + #ifdef SMT_TEST_ZERO + if (P2->_integer.value == 0) + THROW(E_ZERO); + #endif + + P1->_integer.value SMT_OP P2->_integer.value; + P1->type = type; + goto *jump_end; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + #ifdef SMT_TEST_ZERO + if (P2->_long.value == 0) + THROW(E_ZERO); + #endif + + P1->_long.value SMT_OP P2->_long.value; + P1->type = type; + goto *jump_end; + +#endif + +__DATE: + +#ifndef SMT_DATE + + goto __ERROR; + +#endif + +#ifndef SMT_INTEGER + +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + #ifdef SMT_OP + P1->_float.value SMT_OP P2->_float.value; + #elif defined(SMT_FUNC) + P1->_float.value = SMT_FUNC ( P1->_float.value, P2->_float.value ); + #endif + + #ifdef SMT_TEST_ZERO + if (!finite(P1->_float.value)) + { + if (P2->_float.value == 0.0) + THROW(E_ZERO); + else + THROW(E_MATH); + } + #elif defined(SMT_TEST_RESULT) + if (!finite(P1->_float.value)) + THROW(E_MATH); + #endif + + goto *jump_end; + +#endif + +__VARIANT: + + type = Max(P1->type, P2->type); + + if (TYPE_is_number_date(type)) + { + *PC |= type; + goto *jump[type]; + } + + if (TYPE_is_variant(P1->type)) + VARIANT_undo(P1); + + if (TYPE_is_variant(P2->type)) + VARIANT_undo(P2); + + if (TYPE_is_string(P1->type)) + VALUE_conv_float(P1); + + if (TYPE_is_string(P2->type)) + VALUE_conv_float(P2); + + if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) + type = T_NULL; + else + type = Max(P1->type, P2->type); + + if (TYPE_is_number_date(type)) + { + jump_end = &&__VARIANT_END; + goto *jump[type]; + } + + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); + +__VARIANT_END: + + VALUE_conv_variant(P1); + +__END: + + SP--; + /*if (!PCODE_is_void(*PC)) SP++;*/ +} + +#endif + + +#if SMT_TYPE == 3 + +void SMT_NAME(ushort code) +{ + + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__ERROR, &&__ERROR, &&__ERROR + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x0F; + goto *jump[type]; + +__VARIANT: + + type = Max(P1->type, P2->type); + + if (TYPE_is_integer_long(type)) + { + *PC |= type; + goto *jump[type]; + } + + if (TYPE_is_variant(P1->type)) + VARIANT_undo(P1); + + if (TYPE_is_variant(P2->type)) + VARIANT_undo(P2); + + if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) + type = T_NULL; + else + type = Max(P1->type, P2->type); + + if (TYPE_is_integer_long(type)) + goto *jump[type]; + + goto __ERROR; + +__BOOLEAN: + + if (P2->_integer.value == 0) + THROW(E_ZERO); + + P1->type = T_BOOLEAN; + goto __END; + +__BYTE: + + if (P2->_integer.value == 0) + THROW(E_ZERO); + + P1->_integer.value = (unsigned char)(P1->_integer.value SMT_OP P2->_integer.value); + P1->type = T_BYTE; + goto __END; + +__SHORT: + + if (P2->_integer.value == 0) + THROW(E_ZERO); + + P1->_integer.value = (short)(P1->_integer.value SMT_OP P2->_integer.value); + P1->type = T_SHORT; + goto __END; + +__INTEGER: + + if (P2->_integer.value == 0) + THROW(E_ZERO); + + P1->_integer.value = P1->_integer.value SMT_OP P2->_integer.value; + P1->type = T_INTEGER; + goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + if (P2->_long.value == 0) + THROW(E_ZERO); + + P1->_long.value = P1->_long.value SMT_OP P2->_long.value; + P1->type = T_LONG; + goto __END; + +__ERROR: + + THROW(E_TYPE, "Integer", TYPE_get_name(type)); + +__END: + + SP--; + /*if (!PCODE_is_void(*PC)) SP++;*/ +} + +#endif + + +#undef SMT_TYPE +#undef SMT_NAME + +#ifdef SMT_OP +#undef SMT_OP +#endif + +#ifdef SMT_FUNC +#undef SMT_FUNC +#endif + +#ifdef SMT_FUNC_INTEGER +#undef SMT_FUNC_INTEGER +#endif + +#ifdef SMT_FUNC_LONG +#undef SMT_FUNC_LONG +#endif + +#ifdef SMT_FUNC_FLOAT +#undef SMT_FUNC_FLOAT +#endif + +#ifdef SMT_FLOAT +#undef SMT_FLOAT +#endif + +#ifdef SMT_DATE +#undef SMT_DATE +#endif + +#ifdef SMT_INTEGER +#undef SMT_INTEGER +#endif + +#ifdef SMT_TEST_ZERO +#undef SMT_TEST_ZERO +#endif + +#ifdef SMT_CHECK_FLOAT +#undef SMT_CHECK_FLOAT +#endif + +#ifdef SMT_RESULT +#undef SMT_RESULT +#endif diff --git a/main/gbx/gbx_subr_misc.c b/main/gbx/gbx_subr_misc.c new file mode 100644 index 00000000..d07058ec --- /dev/null +++ b/main/gbx/gbx_subr_misc.c @@ -0,0 +1,432 @@ +/*************************************************************************** + + gbx_subr_misc.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include +#include + +#include "gb_common.h" +#include "gbx_subr.h" +#include "gbx_class.h" +#include "gambas.h" +#include "gbx_eval.h" +#include "gbx_date.h" +#include "gbx_archive.h" + +#include "gbx_api.h" +#include "gbx_c_collection.h" +#include "gbx_c_process.h" +#include "gbx_debug.h" +#include "gbx_watch.h" +#include "gbx_math.h" + + +static EVAL_INTERFACE EVAL; +static CCOLLECTION *eval_env; + +static void init_eval() +{ + static bool init = FALSE; + + if (init) + return; + + COMPONENT_load(COMPONENT_create("gb.eval")); + LIBRARY_get_interface_by_name("gb.eval", EVAL_INTERFACE_VERSION, &EVAL); + init = TRUE; +} + +void SUBR_error(void) +{ + SP->type = T_BOOLEAN; + SP->_boolean.value = EXEC_got_error ? -1 : 0; + SP++; +} + + +void SUBR_wait(ushort code) +{ + SUBR_ENTER(); + + EXEC_set_native_error(FALSE); + + if (NPARAM == 0) + GB_Wait(0); + else + GB_Wait((int)(SUBR_get_float(PARAM) * 1000 + 0.5)); + + if (EXEC_has_native_error()) + { + EXEC_set_native_error(FALSE); + PROPAGATE(); + } + + SUBR_LEAVE_VOID(); +} + + +void SUBR_sleep(ushort code) +{ + SUBR_ENTER_PARAM(1); + + switch(code & 0x3F) + { + case 0: // Sleep + { + double wait; + struct timespec rem; + + wait = SUBR_get_float(PARAM); + + rem.tv_sec = (time_t)(int)wait; + rem.tv_nsec = (int)(frac(wait) * 1E9); + + while (nanosleep(&rem, &rem) < 0); + break; + } + case 1: // Use + { + char *name = SUBR_get_string(PARAM); + COMPONENT *comp = COMPONENT_find(name); + if (!comp) + comp = COMPONENT_create(name); + + COMPONENT_load(comp); + break; + } + case 2: // CheckExec + { + CPROCESS_check(PARAM->_object.object); + break; + } + } + + SUBR_LEAVE(); +} + +static void error_subr_exec(CPROCESS *process) +{ + OBJECT_UNREF(process); +} + +void SUBR_exec(ushort code) +{ + void *cmd; + bool wait; + int mode; + CPROCESS *process; + bool ret; + bool shell; + char *name; + CARRAY *env; + + SUBR_ENTER_PARAM(4); + + shell = (code & 0x1F) != 0; + + if (shell) + cmd = (void *)SUBR_get_string(PARAM); + else + { + VALUE_conv_object(PARAM, (TYPE)CLASS_StringArray); + cmd = (void *)(PARAM->_object.object); + } + + if (!cmd) + THROW(E_ARG); + + if (VALUE_is_null(&PARAM[1])) + env = NULL; + else + { + VALUE_conv_object(&PARAM[1], (TYPE)CLASS_StringArray); + env = (PARAM[1]._object.object); + } + + VALUE_conv_integer(&PARAM[2]); + mode = PARAM[2]._integer.value; + wait = mode & PM_WAIT; + + name = SUBR_get_string(&PARAM[3]); + + ret = TRUE; // !PCODE_is_void(code); + + if (shell) + mode |= PM_SHELL; + + //STRING_ref(name); ## This should not be needed + process = CPROCESS_create(mode, cmd, name, env); + //STRING_unref(&name); + + if (wait) + { + OBJECT_REF(process); + + ON_ERROR_1(error_subr_exec, process) + { + CPROCESS_wait_for(process, 0); + } + END_ERROR + + if (!ret) + { + OBJECT_UNREF(process); + } + else if (!process->to_string) + { + OBJECT_UNREF_KEEP(process); + } + } + + if (ret) + { + if (process->to_string) + { + char *result = process->result; + process->result = NULL; + + RELEASE_MANY(SP, NPARAM); + + SP->type = T_STRING; + SP->_string.addr = result; + SP->_string.start = 0; + SP->_string.len = STRING_length(result); + SP++; + + OBJECT_UNREF(process); + } + else + { + RETURN->_object.class = CLASS_Process; + RETURN->_object.object = process; + SUBR_LEAVE(); + } + } + else + { + SUBR_LEAVE_VOID(); + } +} + + +static bool get_value(const char *sym, int len, GB_VARIANT *value) +{ + if (eval_env) + if (!GB_CollectionGet(eval_env, sym, len, value)) + return FALSE; + + value->type = T_NULL; + return TRUE; +} + +void EVAL_string(char *expr) +{ + int len; + EXPRESSION *eval; + + init_eval(); + len = strlen(expr); + eval_env = NULL; + + EVAL.New((void **)(void *)&eval, expr, len); + + if (EVAL.Compile(eval, FALSE)) + { + GB_Error(eval->error); + goto _ERROR; + } + + if (!EVAL.Run(eval, get_value)) + goto _ERROR; + + goto _FREE; + +_ERROR: + EVAL.Free((void **)(void *)&eval); + PROPAGATE(); + +_FREE: + EVAL.Free((void **)(void *)&eval); + + VALUE_to_string(RETURN, &expr, &len); + STREAM_write(CSTREAM_stream(CFILE_out), expr, len); + STREAM_write_eol(CSTREAM_stream(CFILE_out)); + STREAM_flush(CSTREAM_stream(CFILE_out)); +} + +void SUBR_eval(ushort code) +{ + char *expr; + int len; + EXPRESSION *eval; + + SUBR_ENTER(); + + init_eval(); + SUBR_get_string_len(PARAM, &expr, &len); + + if (NPARAM == 2) + { + VALUE_conv_object(&PARAM[1], (TYPE)CLASS_Collection); + eval_env = (CCOLLECTION *)(PARAM[1]._object.object); + } + else + eval_env = NULL; + + EVAL.New((void **)(void *)&eval, expr, len); + + if (EVAL.Compile(eval, FALSE)) + { + GB_Error(eval->error); + goto _ERROR; + } + + if (!EVAL.Run(eval, get_value)) + goto _ERROR; + + goto _FREE; + +_ERROR: + + EVAL.Free((void **)(void *)&eval); + PROPAGATE(); + +_FREE: + + EVAL.Free((void **)(void *)&eval); + SUBR_LEAVE(); +} + +void SUBR_array(ushort code) +{ + static bool reuse = FALSE; + + TYPE type; + int i, j; + CARRAY *array; + bool next_reuse; + + SUBR_ENTER(); + + next_reuse = code & CODE_CALL_VARIANT; + + if (reuse) + { + array = (CARRAY *)(PARAM[-1]._object.object); + type = array->type; + } + else + { + type = SUBR_check_good_type(PARAM, NPARAM); + + if (type == T_NULL) + type = T_OBJECT; + } + + for (i = 0; i < NPARAM; i++) + VALUE_conv(&PARAM[i], type); + + if (reuse) + { + j = array->count; + CARRAY_resize(array, j + NPARAM); + } + else + { + j = 0; + GB_ArrayNew(POINTER(&array), type, NPARAM); + OBJECT_REF(array); + } + + for (i = 0; i < NPARAM; i++, j++) + { + GB_Store(type, (GB_VALUE *)&PARAM[i], GB_ArrayGet(array, j)); + RELEASE(&PARAM[i]); + } + + if (reuse) + { + SP = PARAM; + } + else + { + PARAM->_object.class = OBJECT_class(array); //CLASS_Array; + PARAM->_object.object = array; + SP = PARAM + 1; + } + + reuse = next_reuse; +} + +void SUBR_collection(ushort code) +{ + static bool reuse = FALSE; + + int i; + GB_COLLECTION col; + char *key; + int len; + VALUE *vkey, *vval; + bool next_reuse; + + SUBR_ENTER(); + + next_reuse = code & CODE_CALL_VARIANT; + + if (reuse) + col = (GB_COLLECTION)(PARAM[-1]._object.object); + else + { + GB_CollectionNew(&col, GB_COMP_BINARY); + OBJECT_REF(col); + } + + for (i = 0; i < NPARAM; i += 2) + { + vkey = &PARAM[i]; + vval = vkey + 1; + SUBR_get_string_len(vkey, &key, &len); + VALUE_conv_variant(vval); + if (GB_CollectionSet(col, key, len, (GB_VARIANT *)vval)) + { + OBJECT_UNREF(col); + THROW(E_VKEY); + } + RELEASE_STRING(&PARAM[i]); + RELEASE(&PARAM[i + 1]); + } + + if (reuse) + { + SP = PARAM; + } + else + { + PARAM->_object.class = OBJECT_class(col); //CLASS_Array; + PARAM->_object.object = col; + SP = PARAM + 1; + } + + reuse = next_reuse; +} + + diff --git a/main/gbx/gbx_subr_string.c b/main/gbx/gbx_subr_string.c new file mode 100644 index 00000000..e4c455c2 --- /dev/null +++ b/main/gbx/gbx_subr_string.c @@ -0,0 +1,1359 @@ +/*************************************************************************** + + gbx_subr_string.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_common_case.h" + +#include +#include + +#include "gb_pcode.h" +#include "gbx_value.h" +#include "gbx_subr.h" +#include "gbx_regexp.h" +#include "gbx_class.h" +#include "gbx_string.h" +#include "gbx_split.h" +#include "gbx_c_array.h" +#include "gbx_local.h" +#include "gbx_compare.h" + +//static int _count = 0; + +//--------------------------------------------------------------------------- + +void SUBR_cat(ushort code) +{ + SUBR_ENTER(); + + if (NPARAM == 2) + { + int len, len2; + char *str; + + VALUE_conv_string(&PARAM[0]); + len = PARAM[0]._string.len ; + VALUE_conv_string(&PARAM[1]); + len2 = PARAM[1]._string.len; + + #if 0 + if (EXEC_string_add) + { + EXEC_string_add = FALSE; + + str = PARAM[0]._string.addr; + + if (0 && PARAM[0].type == T_STRING && PARAM[0]._string.start == 0 && STRING_length(str) == len) + { + if (str && !STRING_extend_will_realloc(str, len + len2)) + { + //_count++; + //fprintf(stderr, "[%d] &= optimization: str = %p (%d) param2 = %p (%d)\n", _count, str, len, PARAM[1]._string.addr + PARAM[1]._string.start, len2); + str = STRING_add(str, PARAM[1]._string.addr + PARAM[1]._string.start, len2); + /*if (str != PARAM[0]._string.addr) + { + //fprintf(stderr, "--> %p !\n", str); + BREAKPOINT(); + }*/ + RELEASE_STRING(&PARAM[1]); + + SP -= 2; + //SP->type = T_STRING; + //SP->_string.addr = str; + //SP->_string.start = 0; + SP->_string.len += len2; + SP++; + return; + } + } + /*else + { + if (PARAM[0].type != T_STRING) + fprintf(stderr, "PARAM[0].type == %ld\n", PARAM[0].type); + else if (PARAM[0]._string.start) + fprintf(stderr, "PARAM[0]._string.start == %d\n", PARAM[0]._string.start); + else if (STRING_length(str) != len) + fprintf(stderr, "len == %d / %d\n", len, STRING_length(str)); + }*/ + } + #endif + + str = STRING_new(NULL, len + len2); + + //fprintf(stderr, "normal: str = %p p0 = %p p1 = %p\n", str, PARAM[0]._string.addr + PARAM[0]._string.start, PARAM[1]._string.addr + PARAM[1]._string.start); + + memcpy(str, PARAM[0]._string.addr + PARAM[0]._string.start, len); + memcpy(&str[len], PARAM[1]._string.addr + PARAM[1]._string.start, len2); + + RELEASE_STRING(&PARAM[0]); + RELEASE_STRING(&PARAM[1]); + + SP -= 2; + SP->type = T_STRING; + SP->_string.addr = str; + SP->_string.start = 0; + SP->_string.len = len + len2; + SP++; + } + else + { + int i; + int len, len_cat; + char *str, *ptr; + + len_cat = 0; + + for (i = 0; i < NPARAM; i++) + { + VALUE_conv_string(&PARAM[i]); + len_cat += PARAM[i]._string.len; + } + + str = STRING_new(NULL, len_cat); + ptr = str; + + i = NPARAM; + while (i--) + { + len = PARAM->_string.len; + + if (len) + { + memcpy(ptr, PARAM->_string.addr + PARAM->_string.start, len); + ptr += len; + } + + RELEASE_STRING(PARAM); + PARAM++; + } + + SP -= NPARAM; + SP->type = T_STRING; + SP->_string.addr = str; + SP->_string.start = 0; + SP->_string.len = len_cat; + SP++; + } +} + + +void SUBR_file(ushort code) +{ + int i; + int length; + char *addr; + int len; + char *str, *ptr; + bool slash; + + SUBR_ENTER(); + + length = 0; + slash = FALSE; + + for (i = 0; i < NPARAM; i++) + { + /*VALUE_conv(&PARAM[i], T_STRING);*/ + SUBR_get_string_len(&PARAM[i], &addr, &len); + + if (len > 0) + { + if (length > 0) + { + if (!slash && addr[0] != '/') + length++; + else if (slash && addr[0] == '/') + length--; + } + + slash = addr[len - 1] == '/'; + + length += len; + } + + } + + str = STRING_new(NULL, length); + ptr = str; + slash = FALSE; + + i = NPARAM; + while (i--) + { + if (PARAM->type != T_NULL) + { + VALUE_get_string(PARAM, &addr, &len); + + if (len > 0) + { + if (ptr > str) + { + if (!slash && *addr != '/') + *ptr++ = '/'; + else if (slash && *addr == '/') + ptr--; + } + + slash = addr[len - 1] == '/'; + + memcpy(ptr, addr, len); + ptr += len; + } + + RELEASE_STRING(PARAM); + } + + PARAM++; + } + + SP -= NPARAM; + SP->type = T_STRING; + SP->_string.addr = str; + SP->_string.start = 0; + SP->_string.len = length; + SP++; +} + + + +void SUBR_space(void) +{ + int len; + + SUBR_ENTER_PARAM(1); + + SUBR_check_integer(PARAM); + len = PARAM->_integer.value; + + if (len < 0) + THROW(E_ARG); + + if (len == 0) + { + STRING_void_value(&SP[-1]); + } + else + { + char *str = STRING_new(NULL, len); + memset(str, ' ', len); + SP--; + SP->type = T_STRING; + SP->_string.addr = str; + SP->_string.start = 0; + SP->_string.len = len; + SP++; + } + + //SUBR_LEAVE(); +} + + + +void SUBR_string(void) +{ + int i; + char *d; + char *s; + int ld, ls; + + SUBR_ENTER_PARAM(2); + + SUBR_check_integer(PARAM); + SUBR_get_string_len(&PARAM[1], &s, &ls); + + ld = PARAM->_integer.value * ls; + if (ld < 0) + THROW(E_ARG); + + if (ld == 0) + { + STRING_void_value(RETURN); + } + else + { + STRING_new_temp_value(RETURN, NULL, ld); + d = RETURN->_string.addr; + + for (i = 0; i < PARAM->_integer.value; i++) + { + memcpy(d, s, ls); + d += ls; + } + + *d = 0; + } + + SUBR_LEAVE(); +} + + +void SUBR_trim(ushort code) +{ + unsigned char *str; + bool left, right; + + SUBR_GET_PARAM(1); + + if (SUBR_check_string(PARAM)) + { + VOID_STRING(PARAM); + return; + } + + code &= 0x1F; + left = (code == 0 || code == 1); + right = (code == 0 || code == 2); + + if (PARAM->_string.len > 0) + { + str = (uchar *)&PARAM->_string.addr[PARAM->_string.start]; + + if (left) + { + while (PARAM->_string.len > 0 && *str <= ' ') + { + PARAM->_string.start++; + PARAM->_string.len--; + str++; + } + } + + if (right) + { + while (PARAM->_string.len > 0 && str[PARAM->_string.len - 1] <= ' ') + { + PARAM->_string.len--; + } + } + } +} + +void SUBR_upper(ushort code) +{ + char *str; + int len, i; + + SUBR_ENTER_PARAM(1); + + if (SUBR_check_string(PARAM)) + { + VOID_STRING(&SP[-1]); + } + else + { + len = PARAM->_string.len; + if (len > 0) + { + str = STRING_new(&PARAM->_string.addr[PARAM->_string.start], PARAM->_string.len); + + if (code & 0x3F) + { + for (i = 0; i < len; i++) + str[i] = tolower(str[i]); + } + else + { + for (i = 0; i < len; i++) + str[i] = toupper(str[i]); + } + + SP--; + RELEASE_STRING(SP); + SP->type = T_STRING; + SP->_string.addr = str; + SP->_string.start = 0; + SP->_string.len = len; + SP++; + } + } +} + +void SUBR_chr(void) +{ + int car; + + SUBR_GET_PARAM(1); + + VALUE_conv_integer(PARAM); + + car = PARAM->_integer.value; + if (car < 0 || car > 255) + THROW(E_ARG); + + STRING_char_value(PARAM, car); +} + +void SUBR_asc(ushort code) +{ + int pos = 0; + + SUBR_ENTER(); + + if (!SUBR_check_string(PARAM)) + { + pos = 1; + if (NPARAM == 2) + { + SUBR_check_integer(&PARAM[1]); + pos = PARAM[1]._integer.value; + } + + if (pos < 1 || pos > PARAM->_string.len) + pos = 0; + else + pos = (unsigned char)PARAM->_string.addr[PARAM->_string.start + pos - 1]; + } + + RETURN->type = T_INTEGER; + RETURN->_integer.value = pos; + + SUBR_LEAVE(); +} + +void SUBR_instr(ushort code) +{ + bool right, nocase = FALSE; + int is, pos; + //int pos2; + char *ps, *pp; + int ls, lp; + + SUBR_ENTER(); + + pos = 0; + + if (SUBR_check_string(PARAM)) + goto __FOUND; + + if (SUBR_check_string(&PARAM[1])) + goto __FOUND; + + lp = PARAM[1]._string.len; + ls = PARAM->_string.len; + + right = ((code >> 8) == CODE_RINSTR); + + if (lp > ls) goto __FOUND; + + is = 0; + + if (NPARAM >= 3) + is = SUBR_get_integer(&PARAM[2]); + + if (NPARAM == 4) + nocase = SUBR_get_integer(&PARAM[3]) == GB_COMP_NOCASE; + + ps = PARAM->_string.addr + PARAM->_string.start; + pp = PARAM[1]._string.addr + PARAM[1]._string.start; + + pos = STRING_search(ps, ls, pp, lp, is, right, nocase); + /*pos2 = STRING_search2(ps, ls, pp, lp, is, right, nocase); + + if (pos != pos2) + { + for(;;) + usleep(1000); + }*/ + +__FOUND: + + RELEASE_STRING(PARAM); + RELEASE_STRING(&PARAM[1]); + + SP -= NPARAM; + SP->type = T_INTEGER; + SP->_integer.value = pos; + SP++; +} + +void SUBR_like(ushort code) +{ + static const void *jump[] = { &&__LIKE, &&__BEGINS, &&__ENDS, &&__MATCH }; + char *pattern; + char *string; + int len_pattern, len_string; + bool ret = FALSE; + + SUBR_ENTER_PARAM(2); + + SUBR_get_string_len(&PARAM[0], &string, &len_string); + SUBR_get_string_len(&PARAM[1], &pattern, &len_pattern); + + goto *jump[code & 0x3]; + +__LIKE: + + ret = REGEXP_match(pattern, len_pattern, string, len_string); + goto __RETURN; + +__BEGINS: + + if (len_pattern == 0) + ret = TRUE; + else if (len_pattern <= len_string) + ret = STRING_equal_same(string, pattern, len_pattern); + goto __RETURN; + +__ENDS: + + if (len_pattern == 0) + ret = TRUE; + else if (len_pattern <= len_string) + ret = STRING_equal_same(string + len_string - len_pattern, pattern, len_pattern); + goto __RETURN; + +__MATCH: + + ret = REGEXP_match_pcre(pattern, len_pattern, string, len_string); + goto __RETURN; + +__RETURN: + + RETURN->type = T_BOOLEAN; + RETURN->_boolean.value = -(ret ^ !!(code & 0x4)); + + SUBR_LEAVE(); +} + +static int subst_nparam; +static VALUE *subst_param; + +static void get_subst(int np, char **str, int *len) +{ + if (np > 0 && np < subst_nparam) + VALUE_get_string(&subst_param[np], str, len); + else + { + *str = NULL; + *len = 0; + } +} + +void SUBR_subst(ushort code) +{ + char *string; + int len; + int np; + + SUBR_ENTER(); + + SUBR_get_string_len(&PARAM[0], &string, &len); + + for (np = 1; np < NPARAM; np++) + VALUE_conv_string(&PARAM[np]); + + subst_param = PARAM; + subst_nparam = NPARAM; + + string = STRING_subst(string, len, get_subst); + + /*for (np = 0; np < NPARAM; np++) + RELEASE_STRING(&PARAM[np]);*/ + + RETURN->type = T_STRING; + RETURN->_string.addr = (char *)string; + RETURN->_string.start = 0; + RETURN->_string.len = STRING_length(string); + + SUBR_LEAVE(); +} + +void SUBR_replace(ushort code) +{ + char *ps; + char *pp; + char *pr; + int ls, lp, lr; + int pos; + bool nocase = FALSE; + + SUBR_ENTER(); + + SUBR_get_string_len(&PARAM[0], &ps, &ls); + SUBR_get_string_len(&PARAM[1], &pp, &lp); + SUBR_get_string_len(&PARAM[2], &pr, &lr); + if (NPARAM == 4) + nocase = SUBR_get_integer(&PARAM[3]) == GB_COMP_NOCASE; + + if (lp == 0 || ls == 0) + { + RELEASE(&PARAM[1]); + RELEASE(&PARAM[2]); + SP -= NPARAM; + SP++; + return; + } + + if (lp == lr) + { + ps = STRING_new_temp(ps, ls); + RETURN->_string.addr = ps; + RETURN->_string.len = ls; + + if (lp == 1) + { + char cp = *pp; + char cr = *pr; + + if (nocase) + { + char cpl = tolower(cp); + cp = toupper(cp); + + for (pos = 0; pos < ls; pos++) + { + if (ps[pos] == cp || ps[pos] == cpl) + ps[pos] = cr; + } + } + else + { + for (pos = 0; pos < ls; pos++) + { + if (ps[pos] == cp) + ps[pos] = cr; + } + } + } + else + { + for(;;) + { + pos = STRING_search(ps, ls, pp, lp, 0, FALSE, nocase); + if (pos == 0) + break; + pos--; + memcpy(&ps[pos], pr, lp); + pos += lp; + ps += pos; + ls -= pos; + } + } + } + else + { + STRING_start_len(ls); + + for(;;) + { + pos = STRING_search(ps, ls, pp, lp, 1, FALSE, nocase); + if (pos == 0) + break; + + pos--; + + if (pos > 0) + STRING_make(ps, pos); + + STRING_make(pr, lr); + + pos += lp; + + ps += pos; + ls -= pos; + + if (ls <= 0) + break; + } + + STRING_make(ps, ls); + RETURN->_string.addr = STRING_end_temp(); + RETURN->_string.len = STRING_length(RETURN->_string.addr); + } + + RETURN->type = T_STRING; + RETURN->_string.start = 0; + + SUBR_LEAVE(); +} + +void SUBR_split(ushort code) +{ + CARRAY *array; + char *str; + int lstr; + char *sep = NULL; + int lsep = 0; + char *esc = NULL; + int lesc = 0; + bool no_void = FALSE; + bool keep_esc = FALSE; + + SUBR_ENTER(); + + VALUE_conv_string(PARAM); + VALUE_get_string(PARAM, &str, &lstr); + + if (NPARAM >= 2) + { + SUBR_get_string_len(&PARAM[1], &sep, &lsep); + if (NPARAM >= 3) + { + SUBR_get_string_len(&PARAM[2], &esc, &lesc); + if (NPARAM >= 4) + { + no_void = SUBR_get_boolean(&PARAM[3]); + if (NPARAM == 5) + keep_esc = SUBR_get_boolean(&PARAM[4]); + } + } + } + + array = STRING_split(str, lstr, sep, lsep, esc, lesc, no_void, keep_esc); + + RETURN->_object.class = CLASS_StringArray; + RETURN->_object.object = array; + + SUBR_LEAVE(); +} + +void SUBR_scan(void) +{ + CARRAY *array; + char *str; + int len_str; + char *pat; + int len_pat; + + SUBR_ENTER_PARAM(2); + + SUBR_get_string_len(&PARAM[0], &str, &len_str); + SUBR_get_string_len(&PARAM[1], &pat, &len_pat); + + array = OBJECT_create(CLASS_StringArray, NULL, NULL, 0); + + if (len_str && len_pat) + REGEXP_scan(array, pat, len_pat, str, len_str); + + RETURN->_object.class = CLASS_StringArray; + RETURN->_object.object = array; + + SUBR_LEAVE(); +} + +void SUBR_iconv(void) +{ + char *str; + const char *src; + const char *dst; + char *result; + int len; + + SUBR_ENTER_PARAM(3); + + str = SUBR_get_string(&PARAM[0]); + len = PARAM[0]._string.len; + + src = SUBR_get_string(&PARAM[1]); + dst = SUBR_get_string(&PARAM[2]); + + STRING_conv(&result, str, len, src, dst, TRUE); + + if (!result) + VALUE_null(RETURN); + else + { + RETURN->type = T_STRING; + RETURN->_string.addr = result; + RETURN->_string.start = 0; + RETURN->_string.len = STRING_length(result); + } + + SUBR_LEAVE(); +} + +void SUBR_sconv(ushort code) +{ + char *str; + const char *src; + const char *dst; + char *result; + int len; + + SUBR_ENTER_PARAM(1); + + if (LOCAL_is_UTF8) + return; + + str = SUBR_get_string(&PARAM[0]); + len = PARAM[0]._string.len; + + if (code & 0xF) + { + src = LOCAL_encoding; + dst = SC_UTF8; + } + else + { + src = SC_UTF8; + dst = LOCAL_encoding; + } + + STRING_conv(&result, str, len, src, dst, TRUE); + + if (!result) + VALUE_null(RETURN); + else + { + RETURN->type = T_STRING; + RETURN->_string.addr = result; + RETURN->_string.start = 0; + RETURN->_string.len = STRING_length(result); + } + + SUBR_LEAVE(); +} + +static int _is_ascii(int c) +{ + return (c & ~0x7F) == 0; +} + +static int _is_letter(int c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +static int _is_lower(int c) +{ + return (c >= 'a' && c <= 'z'); +} + +static int _is_upper(int c) +{ + return (c >= 'A' && c <= 'Z'); +} + +static int _is_digit(int c) +{ + return (c >= '0' && c <= '9'); +} + +static int _is_hexa(int c) +{ + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); +} + +static int _is_space(int c) +{ + return strchr(" \n\r\t\f\v", c) != NULL; +} + +static int _is_blank(int c) +{ + return (c == 32 || c == '\t'); +} + +static int _is_punct(int c) +{ + return ((c > 32) && (c < 128) && !(_is_letter(c) || _is_digit(c))); +} + +static int _is_alnum(int c) +{ + return _is_letter(c) || _is_digit(c); +} + +void SUBR_is_chr(ushort code) +{ + static void *jump[] = + { + NULL, _is_ascii, _is_letter, _is_lower, _is_upper, _is_digit, _is_hexa, _is_space, _is_blank, _is_punct, _is_alnum + }; + + char *addr; + int len; + int i; + int (*func)(int); + + SUBR_ENTER_PARAM(1); + + VALUE_conv_string(PARAM); + + //SUBR_get_string_len(PARAM, &addr, &len); + VALUE_get_string(PARAM, &addr, &len); + + func = jump[code & 0x3F]; + + i = len; + while(i) + { + if (!(*func)(*addr++)) + break; + i--; + } + + RELEASE_STRING(PARAM); + SP--; + SP->type = T_BOOLEAN; + SP->_boolean.value = (len > 0 && i == 0) ? -1 : 0; + SP++; +} + +void SUBR_tr(void) +{ + char *str; + + SUBR_ENTER_PARAM(1); + + VALUE_conv_string(&PARAM[0]); + + if (SUBR_check_string(PARAM)) + STRING_void_value(RETURN); + else + { + str = STRING_new_temp(&PARAM->_string.addr[PARAM->_string.start], PARAM->_string.len); + + RETURN->type = T_CSTRING; + RETURN->_string.addr = (char *)LOCAL_gettext(str); + RETURN->_string.start = 0; + RETURN->_string.len = strlen(RETURN->_string.addr); + } + + SUBR_LEAVE(); +} + +static void make_hex_char(uchar c) +{ + static const char hex_digit[] = "0213456789ABCDEF"; + + STRING_make_char(hex_digit[c >> 4]); + STRING_make_char(hex_digit[c & 7]); +} + +void SUBR_quote(ushort code) +{ + static void *jump[8] = { &&__QUOTE, &&__SHELL, &&__HTML, &&__BASE64, &&__URL , &&__ILLEGAL, &&__ILLEGAL, &&__ILLEGAL }; + char *str; + int lstr; + int i; + unsigned char c; + char buf[8]; + + SUBR_ENTER_PARAM(1); + + VALUE_conv_string(&PARAM[0]); + + str = PARAM->_string.addr + PARAM->_string.start; + lstr = PARAM->_string.len; + + STRING_start_len(lstr); + + goto *jump[code & 0x7]; + +__QUOTE: + + STRING_make_char('"'); + + for (i = 0; i < lstr; i++) + { + c = str[i]; + //if (c >= ' ' && c <= 126 && c != '\\' && c != '"') + if (c >= ' ' && c != '\\' && c != '"') + STRING_make_char(c); + else + { + STRING_make_char('\\'); + if (c == '\n') + c = 'n'; + else if (c == '\r') + c = 'r'; + else if (c == '\t') + c = 't'; + else if (!(c == '"' || c == '\\')) + { + snprintf(buf, sizeof(buf), "x%02X", c); + STRING_make(buf, 3); + continue; + } + STRING_make_char(c); + } + } + + STRING_make_char('"'); + goto __END; + +__SHELL: + + /*if (!LOCAL_is_UTF8) + { + char *conv; + STRING_conv(&conv, str, lstr, SC_UTF8, LOCAL_encoding, FALSE); + str = conv; + lstr = str ? strlen(str) : 0; + }*/ + + // TODO: The following works with bash, but not with dash! + + STRING_make_char('\''); + + for (i = 0; i < lstr; i++) + { + c = str[i]; + if (c == '\'') + { + STRING_make("'\\'", 3); + } + STRING_make_char(c); + /* + if (c == '\n') + STRING_make("$'\\n'", 5); + else if (c == '\r') + STRING_make("$'\\r'", 5); + else if (c == '\t') + STRING_make("$'\\t'", 5); + else if (c < ' ') //|| (c > 126 && !LOCAL_is_UTF8)) + { + snprintf(buf, sizeof(buf), "$'\\x%02X'", c); + STRING_make(buf, 7); + } + else if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || index(".-/_~", c) || c > 126) + STRING_make_char(c); + else + { + STRING_make_char('\\'); + STRING_make_char(c); + } + */ + } + + STRING_make_char('\''); + + goto __END; + +__HTML: + + for (i = 0; i < lstr; i++) + { + c = str[i]; + if (c == '&') + STRING_make("&", 5); + else if (c == '<') + STRING_make("<", 4); + else if (c == '>') + STRING_make(">", 4); + else if (c == '"') + STRING_make(""", 6); + else if (c == '\'') + STRING_make("'", 6); + else if (c == 0xC2 && (uchar)str[i + 1] == 0xA0) + { + STRING_make(" ", 6); + i++; + } + else + STRING_make_char(c); + } + + goto __END; + +__BASE64: + { + static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + uchar *in; + char *out = buf; + + for (i = 0; i < (lstr - 2); i += 3) + { + in = (uchar *)&str[i]; + out[0] = base64[in[0] >> 2]; + out[1] = base64[((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4)]; + out[2] = base64[((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6)]; + out[3] = base64[in[2] & 0x3F]; + STRING_make(out, 4); + } + + if (i < lstr) + { + in = (uchar *)&str[i]; + lstr -= i; + out[0] = base64[in[0] >> 2]; + out[1] = base64[((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4)]; + out[2] = (lstr > 1 ? base64[((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6) ] : '='); + out[3] = (lstr > 2 ? base64[in[2] & 0x3F] : '='); + STRING_make(out, 4); + } + } + + goto __END; + +__URL: + + // Warning! '/' is not encoded, so that the function is more pratical, by supposing that no file url will have '/' in its name. + + for (i = 0; i < lstr; i++) + { + c = str[i]; + if (c == ' ') + STRING_make_char('+'); + else if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || index("-._~,$!/", c)) + STRING_make_char(c); + else + { + STRING_make_char('%'); + make_hex_char(c); + } + } + + goto __END; + +#if 0 +__JAVASCRIPT: + { + STRING_make_char('\''); + + for (i = 0; i < lstr; i++) + { + c = str[i]; + if (c >= ' ' && c <= 126 && c != '\\' && c != '\'') + STRING_make_char(c); + else + { + STRING_make_char('\\'); + if (c == '\n') + c = 'n'; + else if (c == '\r') + c = 'r'; + else if (c == '\t') + c = 't'; + else if (!(c == '\'' || c == '\\')) + { + snprintf(buf, sizeof(buf), "x%02X", c); + STRING_make(buf, 3); + continue; + } + STRING_make_char(c); + } + } + + STRING_make_char('\''); + } + + goto __END; + +#endif + +__ILLEGAL: + + THROW_ILLEGAL(); + +__END: + + RETURN->type = T_STRING; + RETURN->_string.addr = STRING_end_temp(); + RETURN->_string.start = 0; + RETURN->_string.len = STRING_length(RETURN->_string.addr); + + SUBR_LEAVE(); +} + +static int read_hex_digit(unsigned char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + else if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + else if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + else + return 0; +} + +void SUBR_unquote(ushort code) +{ + static void *jump[4] = { &&__UNQUOTE, &&__FROM_BASE64, &&__FROM_URL, &&__ILLEGAL }; + + char *str; + int lstr; + int i; + unsigned char c; + + SUBR_ENTER_PARAM(1); + + VALUE_conv_string(&PARAM[0]); + + str = PARAM->_string.addr + PARAM->_string.start; + lstr = PARAM->_string.len; + + STRING_start_len(lstr); + + goto *jump[code & 0x3]; + +__UNQUOTE: + + if (lstr >= 2 && str[0] == '"' && str[lstr - 1] == '"') + { + str++; + lstr -= 2; + } + + for (i = 0; i < lstr; i++) + { + c = str[i]; + if (c == '\\') + { + i++; + if (i >= lstr) + break; + c = str[i]; + + if (c == 'n') + c = '\n'; + else if (c == 't') + c = '\t'; + else if (c == 'r') + c = '\r'; + else if (c == 'x') + { + if (i >= (lstr - 2)) + break; + + c = (read_hex_digit(str[i + 1]) << 4) + read_hex_digit(str[i + 2]); + i += 2; + } + } + + STRING_make_char(c); + } + + goto __END; + +__FROM_BASE64: + + { + char buf[4]; + unsigned char n = 0; + + for (i = 0; i < lstr; i++) + { + c = str[i]; + if (c >= 'A' && c <= 'Z') + c = c - 'A'; + else if (c >= 'a' && c <= 'z') + c = c - 'a' + 26; + else if (c >= '0' && c <= '9') + c = c - '0' + 52; + else if (c == '+') + c = 62; + else if (c == '/') + c = 63; + else if (c == '=') + break; + else + continue; + + switch (n & 3) + { + case 0: buf[0] = c << 2; break; + case 1: buf[0] |= c >> 4; buf[1] = c << 4; break; + case 2: buf[1] |= c >> 2; buf[2] = c << 6; break; + case 3: buf[2] |= c; STRING_make(buf, 3); break; + } + n++; + } + + if ((n & 3) > 1) + STRING_make(buf, (n & 3) - 1); + } + + goto __END; + +__FROM_URL: + + for (i = 0; i < lstr; i++) + { + c = str[i]; + if (c == '+') + c = ' '; + else if (c == '%') + { + if (i >= (lstr - 2)) + break; + + c = (read_hex_digit(str[i + 1]) << 4) + read_hex_digit(str[i + 2]); + i += 2; + } + + STRING_make_char(c); + } + + goto __END; + +__ILLEGAL: + + THROW_ILLEGAL(); + +__END: + + RETURN->type = T_STRING; + RETURN->_string.addr = STRING_end_temp(); + RETURN->_string.start = 0; + RETURN->_string.len = STRING_length(RETURN->_string.addr); + + SUBR_LEAVE(); +} + +void SUBR_swap(ushort code) +{ + char *src, *dst; + int len, i, j; + + if (!(code & 0xFF)) + { + SUBR_move(1); + return; + } + + SUBR_ENTER(); + + if (NPARAM == 2 && (SUBR_get_integer(&PARAM[1]) == GB_BIG_ENDIAN) == EXEC_big_endian) + { + SP--; + return; + } + + if (SUBR_check_string(PARAM)) + STRING_void_value(RETURN); + else + { + len = PARAM->_string.len; + if (len > 0) + { + src = PARAM->_string.addr + PARAM->_string.start; + dst = STRING_new_temp(NULL, PARAM->_string.len); + + for (i = 0, j = len - 1; i < len; i++,j--) + dst[i] = src[j]; + + RETURN->type = T_STRING; + RETURN->_string.addr = dst; + RETURN->_string.start = 0; + RETURN->_string.len = len; + } + } + + SUBR_LEAVE(); +} + + diff --git a/main/gbx/gbx_subr_test.c b/main/gbx/gbx_subr_test.c new file mode 100644 index 00000000..faca6440 --- /dev/null +++ b/main/gbx/gbx_subr_test.c @@ -0,0 +1,1010 @@ +/*************************************************************************** + + gbx_subr_test.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" +#include + +#include "gbx_value.h" +#include "gbx_subr.h" +#include "gbx_date.h" +#include "gbx_object.h" +#include "gbx_math.h" +#include "gbx_compare.h" + +#define STT_NAME SUBR_case +#define STT_TEST == +#define STT_CASE + +#include "gbx_subr_test_temp.h" + + +void SUBR_bit(ushort code) +{ + static void *jump[16] = { + &&__ERROR, &&__BCLR, &&__BSET, &&__BTST, &&__BCHG, &&__ASL, &&__ASR, &&__ROL, + &&__ROR, &&__LSL, &&__LSR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR + }; + + static int nbits[6] = { 0, 0, 8, 16, 32, 64 }; + + int64_t val; + int bit; + TYPE type; + int n; + bool variant; + + SUBR_ENTER_PARAM(2); + + type = PARAM->type; + + variant = TYPE_is_variant(type); + if (variant) + type = PARAM->_variant.vtype; + + if (type <= T_BOOLEAN || type > T_LONG) + THROW(E_TYPE, "Number", TYPE_get_name(type)); + + VALUE_conv(PARAM, T_LONG); + val = PARAM->_long.value; + + n = nbits[type]; + + VALUE_conv_integer(&PARAM[1]); + bit = PARAM[1]._integer.value; + + if ((bit < 0) || (bit >= n)) + THROW(E_ARG); + + RETURN->type = type; + + goto *jump[code & 0xF]; + +__BCLR: + + val &= ~(1ULL << bit); + goto __END; + +__BSET: + + val |= (1ULL << bit); + goto __END; + +__BTST: + + RETURN->type = T_BOOLEAN; + RETURN->_boolean.value = (val & (1ULL << bit)) ? (-1) : 0; + goto __LEAVE; + +__BCHG: + + val ^= (1ULL << bit); + goto __END; + +__ASL: + + { + static void *asl_jump[6] = { &&__ERROR, &&__ERROR, &&__ASL_BYTE, &&__ASL_SHORT, &&__ASL_INTEGER, &&__ASL_LONG }; + + goto *asl_jump[type]; + + __ASL_BYTE: + val = ((unsigned char)val << bit); + goto __END_BYTE; + + __ASL_SHORT: + val = (((short)val << bit) & 0x7FFF) | (((short)val) & 0x8000); + goto __END_SHORT; + + __ASL_INTEGER: + val = (((int)val << bit) & 0x7FFFFFFF) | (((int)val) & 0x80000000); + goto __END_INTEGER; + + __ASL_LONG: + val = ((val << bit) & 0x7FFFFFFFFFFFFFFFLL) | (val & 0x8000000000000000LL); + goto __END_LONG; + } + +__ASR: + + { + static void *asr_jump[6] = { &&__ERROR, &&__ERROR, &&__ASR_BYTE, &&__ASR_SHORT, &&__ASR_INTEGER, &&__ASR_LONG }; + + goto *asr_jump[type]; + + __ASR_BYTE: + val = ((unsigned char)val >> bit); + goto __END_BYTE; + + __ASR_SHORT: + val = (((short)val >> bit) & 0x7FFF) | (((short)val) & 0x8000); + goto __END_SHORT; + + __ASR_INTEGER: + val = (((int)val >> bit) & 0x7FFFFFFF) | (((int)val) & 0x80000000); + goto __END_INTEGER; + + __ASR_LONG: + val = ((val >> bit) & 0x7FFFFFFFFFFFFFFFLL) | (val & 0x8000000000000000LL); + goto __END_LONG; + } + +__ROL: + + { + static void *rol_jump[6] = { &&__ERROR, &&__ERROR, &&__ROL_BYTE, &&__ROL_SHORT, &&__ROL_INTEGER, &&__ROL_LONG }; + + goto *rol_jump[type]; + + __ROL_BYTE: + val = (val << bit) | (val >> (8 - bit)); + goto __END_BYTE; + + __ROL_SHORT: + val = ((ushort)val << bit) | ((ushort)val >> (16 - bit)); + goto __END_SHORT; + + __ROL_INTEGER: + val = ((uint)val << bit) | ((uint)val >> (32 - bit)); + goto __END_INTEGER; + + __ROL_LONG: + val = ((uint64_t)val << bit) | ((uint64_t)val >> (64 - bit)); + goto __END_LONG; + } + +__ROR: + + { + static void *ror_jump[6] = { &&__ERROR, &&__ERROR, &&__ROR_BYTE, &&__ROR_SHORT, &&__ROR_INTEGER, &&__ROR_LONG }; + + goto *ror_jump[type]; + + __ROR_BYTE: + val = (val >> bit) | (val << (8 - bit)); + goto __END_BYTE; + + __ROR_SHORT: + val = ((ushort)val >> bit) | ((ushort)val << (16 - bit)); + goto __END_SHORT; + + __ROR_INTEGER: + val = ((uint)val >> bit) | ((uint)val << (32 - bit)); + goto __END_INTEGER; + + __ROR_LONG: + val = ((uint64_t)val >> bit) | ((uint64_t)val << (64 - bit)); + goto __END_LONG; + } + +__LSL: + + { + static void *lsl_jump[6] = { &&__ERROR, &&__ERROR, &&__LSL_BYTE, &&__LSL_SHORT, &&__LSL_INTEGER, &&__LSL_LONG }; + + goto *lsl_jump[type]; + + __LSL_BYTE: + val = ((unsigned char)val << bit); + goto __END_BYTE; + + __LSL_SHORT: + val = ((unsigned short)val << bit); + goto __END_SHORT; + + __LSL_INTEGER: + val = ((unsigned int)val << bit); + goto __END_INTEGER; + + __LSL_LONG: + val = ((uint64_t)val << bit); + goto __END_LONG; + } + +__LSR: + + { + static void *lsr_jump[6] = { &&__ERROR, &&__ERROR, &&__LSR_BYTE, &&__LSR_SHORT, &&__LSR_INTEGER, &&__LSR_LONG }; + + goto *lsr_jump[type]; + + __LSR_BYTE: + val = ((unsigned char)val >> bit); + goto __END_BYTE; + + __LSR_SHORT: + val = ((unsigned short)val >> bit); + goto __END_SHORT; + + __LSR_INTEGER: + val = ((unsigned int)val >> bit); + goto __END_INTEGER; + + __LSR_LONG: + val = ((uint64_t)val >> bit); + goto __END_LONG; + } + +__ERROR: + + THROW_ILLEGAL(); + +__END: + + { + static void *end_jump[6] = { &&__ERROR, &&__ERROR, &&__END_BYTE, &&__END_SHORT, &&__END_INTEGER, &&__END_LONG }; + + goto *end_jump[type]; + + __END_BYTE: + RETURN->_integer.value = (unsigned int)val & 0xFF; + goto __END_VARIANT; + + __END_SHORT: + RETURN->_integer.value = (int)(short)val; + goto __END_VARIANT; + + __END_INTEGER: + RETURN->_integer.value = (int)val; + goto __END_VARIANT; + + __END_LONG: + RETURN->_long.value = val; + goto __END_VARIANT; + } + +__END_VARIANT: + + if (variant) + VALUE_conv_variant(RETURN); + +__LEAVE: + + SUBR_LEAVE(); +} + + +void SUBR_if(ushort code) +{ + int i; + unsigned char test; + TYPE type; + + SUBR_ENTER_PARAM(3); + + VALUE_conv_boolean(PARAM); + i = PARAM->_boolean.value ? 1 : 2; + + test = code & 0x1F; + + if (!test) + { + type = PARAM[1].type; + if (PARAM[2].type == type && type <= T_VARIANT) + { + *PC |= 0x1F; + } + else + { + type = SUBR_check_good_type(&PARAM[1], 2); + if (TYPE_is_object(type)) + type = T_OBJECT; + *PC |= (unsigned char)type; + + VALUE_conv(&PARAM[i], type); + } + } + else if (test != 0x1F) + { + VALUE_conv(&PARAM[i], (TYPE)test); + } + + *PARAM = PARAM[i]; + RELEASE(&PARAM[3 - i]); + SP -= 2; +} + + +void SUBR_choose(ushort code) +{ + int val; + + SUBR_ENTER(); + + VALUE_conv_integer(PARAM); + val = PARAM->_integer.value; + + if (val >= 1 && val < NPARAM) + { + VALUE_conv_variant(&PARAM[val]); + *RETURN = PARAM[val]; + } + else + { + RETURN->type = T_VARIANT; + RETURN->_variant.vtype = T_NULL; + } + + SUBR_LEAVE(); +} + + +void SUBR_near(void) +{ + int result; + + SUBR_ENTER_PARAM(2); + + VALUE_conv_string(&PARAM[0]); + VALUE_conv_string(&PARAM[1]); + + //result = STRING_comp_value_ignore_case(&PARAM[0], &PARAM[1]) ? -1 : 0; + result = STRING_equal_ignore_case(PARAM[0]._string.addr + PARAM[0]._string.start, PARAM[0]._string.len, PARAM[1]._string.addr + PARAM[1]._string.start, PARAM[1]._string.len) ? -1 : 0; + + RELEASE_STRING(&PARAM[0]); + RELEASE_STRING(&PARAM[1]); + + PARAM->type = T_BOOLEAN; + PARAM->_boolean.value = result; + + SP--; +} + +#if 0 +void SUBR_comp(ushort code) +{ + static void *jump[17] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__ERROR, &&__ERROR, &&__ERROR, &&__NULL, &&__OBJECT + }; + + //static void *test[] = { &&__EQ, &&__NE, &&__GT, &&__LE, &&__LT, &&__GE }; + + char NO_WARNING(result); + VALUE *P1; + VALUE *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + goto *jump[code & 0x1F]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + result = P1->_integer.value == P2->_integer.value; + goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + result = P1->_long.value == P2->_long.value; + goto __END; + +__DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + result = DATE_comp_value(P1, P2) == 0; + goto __END; + +__NULL: + + if (P2->type == T_NULL) + { + result = VALUE_is_null(P1); + goto __END_RELEASE; + } + else if (P1->type == T_NULL) + { + result = VALUE_is_null(P2); + goto __END_RELEASE; + } + +__STRING: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + + if (P1->_string.len != P2->_string.len) + result = 0; + else + result = STRING_equal_same(P1->_string.addr + P1->_string.start, P2->_string.addr + P2->_string.start, P1->_string.len); + + RELEASE_STRING(P1); + RELEASE_STRING(P2); + goto __END; + +__SINGLE: +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + result = P1->_float.value == P2->_float.value; + goto __END; + +__POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + + result = P1->_pointer.value == P2->_pointer.value; + goto __END; + +__OBJECT: + + result = OBJECT_comp_value(P1, P2) == 0; + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __END_RELEASE; + +__VARIANT: + + { + bool variant = FALSE; + TYPE type; + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + type = Max(P1->type, P2->type); + + if (TYPE_is_object_null(P1->type) && TYPE_is_object_null(P2->type)) + type = T_OBJECT; + else if (TYPE_is_object(type)) + THROW(E_TYPE, "Object", TYPE_get_name(Min(P1->type, P2->type))); + + if (!variant) + *PC |= type; + + goto *jump[type]; + } + +__ERROR: + + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(code & 0x1F)); + +__END_RELEASE: + + RELEASE(P1); + RELEASE(P2); + +__END: + + P1->type = T_BOOLEAN; + SP--; + P1->_boolean.value = -result; +} + +void SUBR_compn(ushort code) +{ + static void *jump[17] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__ERROR, &&__ERROR, &&__ERROR, &&__NULL, &&__OBJECT + }; + + //static void *test[] = { &&__EQ, &&__NE, &&__GT, &&__LE, &&__LT, &&__GE }; + + char NO_WARNING(result); + VALUE *P1; + VALUE *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + goto *jump[code & 0x1F]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + result = P1->_integer.value == P2->_integer.value; + goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + result = P1->_long.value == P2->_long.value; + goto __END; + +__DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + result = DATE_comp_value(P1, P2) == 0; + goto __END; + +__NULL: + + if (P2->type == T_NULL) + { + result = VALUE_is_null(P1); + goto __END_RELEASE; + } + else if (P1->type == T_NULL) + { + result = VALUE_is_null(P2); + goto __END_RELEASE; + } + +__STRING: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + + if (P1->_string.len != P2->_string.len) + result = 0; + else + result = STRING_equal_same(P1->_string.addr + P1->_string.start, P2->_string.addr + P2->_string.start, P1->_string.len); + + RELEASE_STRING(P1); + RELEASE_STRING(P2); + goto __END; + +__SINGLE: +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + result = P1->_float.value == P2->_float.value; + goto __END; + +__POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + + result = P1->_pointer.value == P2->_pointer.value; + goto __END; + +__OBJECT: + + result = OBJECT_comp_value(P1, P2) == 0; + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __END_RELEASE; + +__VARIANT: + + { + bool variant = FALSE; + TYPE type; + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + type = Max(P1->type, P2->type); + + if (TYPE_is_object_null(P1->type) && TYPE_is_object_null(P2->type)) + type = T_OBJECT; + else if (TYPE_is_object(type)) + THROW(E_TYPE, "Object", TYPE_get_name(Min(P1->type, P2->type))); + + if (!variant) + *PC |= type; + + goto *jump[type]; + } + +__ERROR: + + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(code & 0x1F)); + +__END_RELEASE: + + RELEASE(P1); + RELEASE(P2); + +__END: + + P1->type = T_BOOLEAN; + SP--; + + P1->_boolean.value = result - 1; // ? 0 : -1; +} + +#define sgn(_x) \ +({ \ + int x = _x; \ + int minusOne = x >> 31; \ + unsigned int negateX = (unsigned int) -x; \ + int plusOne = (int)(negateX >> 31); \ + int result = minusOne | plusOne; \ + result; \ +}) + +void SUBR_compi(ushort code) +{ + static void *jump[17] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__ERROR, &&__ERROR, &&__ERROR, &&__NULL, &&__OBJECT + }; + + static void *test[] = { &&__GT, &&__LE, &&__LT, &&__GE }; + + char NO_WARNING(result); + VALUE *P1; + VALUE *P2; + TYPE type; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x1F; + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + result = P1->_integer.value > P2->_integer.value ? 1 : P1->_integer.value < P2->_integer.value ? -1 : 0; + goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + result = P1->_long.value > P2->_long.value ? 1 : P1->_long.value < P2->_long.value ? -1 : 0; + goto __END; + +__DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + result = DATE_comp_value(P1, P2); + goto __END; + +__NULL: +__STRING: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + + result = STRING_compare(P1->_string.addr + P1->_string.start, P1->_string.len, P2->_string.addr + P2->_string.start, P2->_string.len); + + RELEASE_STRING(P1); + RELEASE_STRING(P2); + goto __END; + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + result = P1->_single.value > P2->_single.value ? 1 : P1->_single.value < P2->_single.value ? -1 : 0; + goto __END; + +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + result = P1->_float.value > P2->_float.value ? 1 : P1->_float.value < P2->_float.value ? -1 : 0; + goto __END; + +__POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + + result = P1->_pointer.value > P2->_pointer.value ? 1 : P1->_pointer.value < P2->_pointer.value ? -1 : 0; + goto __END; + +__OBJECT: + + result = OBJECT_comp_value(P1, P2); + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __END_RELEASE; + +__VARIANT: + + { + bool variant = FALSE; + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + type = Max(P1->type, P2->type); + + if (type == T_NULL || TYPE_is_string(type)) + { + TYPE typem = Min(P1->type, P2->type); + if (!TYPE_is_string(typem)) + THROW(E_TYPE, TYPE_get_name(typem), TYPE_get_name(type)); + } + else if (TYPE_is_object(type)) + goto __ERROR; + + if (!variant) + *PC |= type; + + goto *jump[type]; + } + +__ERROR: + + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(type)); + +__END_RELEASE: + + RELEASE(P1); + RELEASE(P2); + +__END: + + P1->type = T_BOOLEAN; + SP--; + + goto *test[(code >> 8) - (C_GT >> 8)]; + +__GT: + P1->_boolean.value = result > 0 ? -1 : 0; + return; + +__GE: + P1->_boolean.value = result >= 0 ? -1 : 0; + return; + +__LT: + P1->_boolean.value = result < 0 ? -1 : 0; + return; + +__LE: + P1->_boolean.value = result <= 0 ? -1 : 0; + return; +} +#endif + +void SUBR_strcomp(ushort code) +{ + int mode = GB_COMP_BINARY; + /*char *s1, *s2; + int l1, l2; + int ret;*/ + + SUBR_ENTER(); + + VALUE_conv_string(&PARAM[0]); + VALUE_conv_string(&PARAM[1]); + + if (NPARAM == 3) + mode = SUBR_get_integer(&PARAM[2]); + + /*mode &= GB_COMP_TYPE_MASK; + + if (mode == GB_COMP_BINARY) + ret = STRING_compare(PARAM[0]._string.addr + PARAM[0]._string.start, PARAM[0]._string.len, PARAM[1]._string.addr + PARAM[1]._string.start, PARAM[1]._string.len); + else if (mode == GB_COMP_NOCASE) + ret = STRING_compare_ignore_case(PARAM[0]._string.addr + PARAM[0]._string.start, PARAM[0]._string.len, PARAM[1]._string.addr + PARAM[1]._string.start, PARAM[1]._string.len); + else + { + SUBR_get_string_len(&PARAM[0], &s1, &l1); + SUBR_get_string_len(&PARAM[1], &s2, &l2); + + if (mode & GB_COMP_NATURAL) + ret = COMPARE_string_natural(s1, l1, s2, l2, mode & GB_COMP_NOCASE); + else if (mode & GB_COMP_LIKE) + ret = COMPARE_string_like(s1, l1, s2, l2, mode & GB_COMP_NOCASE); + else if (mode & GB_COMP_LANG) + ret = COMPARE_string_lang(s1, l1, s2, l2, mode & GB_COMP_NOCASE, FALSE); + else + THROW(E_ARG); + }*/ + + RETURN->_integer.type = T_INTEGER; + RETURN->_integer.value = (*COMPARE_get_string_func(mode))(PARAM[0]._string.addr + PARAM[0]._string.start, PARAM[0]._string.len, PARAM[1]._string.addr + PARAM[1]._string.start, PARAM[1]._string.len, mode & GB_COMP_NOCASE, FALSE); + + SUBR_LEAVE(); +} + + + +void SUBR_is(ushort code) +{ + VALUE *P1 = SP - 2; + VALUE *P2 = SP - 1; + void *object; + CLASS *klass; + bool res; + + VALUE_conv(P1, T_OBJECT); + object = P1->_object.object; + klass = P2->_class.class; + + if (!object) + res = FALSE; + else + res = (OBJECT_class(object) == klass || CLASS_inherits(OBJECT_class(object), klass)); + + OBJECT_UNREF(object); + + P1->type = T_BOOLEAN; + P1->_boolean.value = -(res ^ (code & 1)); + SP--; +} + + +void SUBR_min_max(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__FLOAT, &&__FLOAT, &&__DATE + }; + + TYPE type; + VALUE *P1, *P2; + void *jump_end; + bool is_max; + + P1 = SP - 2; + P2 = P1 + 1; + + jump_end = &&__END; + type = code & 0x0F; + is_max = ((code >> 8) == CODE_MAX); + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + P1->type = type; + + if (is_max) + { + if (P2->_integer.value > P1->_integer.value) + P1->_integer.value = P2->_integer.value; + } + else + { + if (P2->_integer.value < P1->_integer.value) + P1->_integer.value = P2->_integer.value; + } + + goto *jump_end; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + if (is_max) + { + if (P2->_long.value > P1->_long.value) + P1->_long.value = P2->_long.value; + } + else + { + if (P2->_long.value < P1->_long.value) + P1->_long.value = P2->_long.value; + } + + goto *jump_end; + +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + if (is_max) + { + if (P2->_float.value > P1->_float.value) + P1->_float.value = P2->_float.value; + } + else + { + if (P2->_float.value < P1->_float.value) + P1->_float.value = P2->_float.value; + } + + goto *jump_end; + +__DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + if (DATE_comp_value(P1, P2) == (is_max ? -1 : 1)) + *P1 = *P2; + + goto *jump_end; + +__VARIANT: + + type = Max(P1->type, P2->type); + + if (TYPE_is_number_date(type)) + { + *PC |= type; + goto *jump[type]; + } + + if (TYPE_is_variant(P1->type)) + VARIANT_undo(P1); + + if (TYPE_is_variant(P2->type)) + VARIANT_undo(P2); + + type = Max(P1->type, P2->type); + + if (TYPE_is_number_date(type)) + { + jump_end = &&__VARIANT_END; + goto *jump[type]; + } + + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number or date", TYPE_get_name(type)); + +__VARIANT_END: + + VALUE_conv_variant(P1); + +__END: + + SP--; +} diff --git a/main/gbx/gbx_subr_test_temp.h b/main/gbx/gbx_subr_test_temp.h new file mode 100644 index 00000000..a57dbbcb --- /dev/null +++ b/main/gbx/gbx_subr_test_temp.h @@ -0,0 +1,333 @@ +/*************************************************************************** + + gbx_subr_test_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef STT_INEQUALITY + +void STT_NAME(ushort code) +{ + static void *jump[17] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, + &&__NULL, + &&__OBJECT + }; + + TYPE type; + VALUE *P1, *P2; + bool result, variant; + + P1 = SP - 2; + P2 = P1 + 1; + + variant = FALSE; + type = code & 0x1F; + goto *jump[type]; + +__VARIANT: + + type = Max(P1->type, P2->type); + + if (TYPE_is_variant(P1->type)) + { + #ifdef STT_CASE + TEMP = *P1; + P1 = &TEMP; + #endif + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + if (variant) + type = Max(P1->type, P2->type); + + if (TYPE_is_object_null(P1->type) && TYPE_is_object_null(P2->type)) + type = T_OBJECT; + else if (TYPE_is_object(type)) + THROW(E_TYPE, "Object", TYPE_get_name(Min(P1->type, P2->type))); + + if (!variant) + *PC |= type; + + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + result = P1->_integer.value STT_TEST P2->_integer.value; + goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + result = P1->_long.value STT_TEST P2->_long.value; + goto __END; + +__DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + #ifdef STT_NEAR + result = P1->_date.date STT_TEST P2->_date.date; + #else + result = (DATE_comp_value(P1, P2) STT_TEST 0); + #endif + goto __END; + +__STRING: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + + if (P1->_string.len != P2->_string.len) + result = 0 STT_TEST 1; + else + #ifdef STT_NEAR + result = (STRING_equal_ignore_case(P1->_string.addr + P1->_string.start, P1->_string.len, P2->_string.addr + P2->_string.start, P2->_string.len) STT_TEST 1); + #else + result = (STRING_equal_same(P1->_string.addr + P1->_string.start, P2->_string.addr + P2->_string.start, P1->_string.len) STT_TEST 1); + #endif + + goto __END_RELEASE; + +#ifdef STT_NEAR + +__SINGLE: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + result = fabs(P1->_float.value - P2->_float.value) <= 1E-6 * fabs(P1->_float.value + P2->_float.value); + goto __END; + +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + result = fabs(P1->_float.value - P2->_float.value) <= 1E-12 * fabs(P1->_float.value + P2->_float.value); + goto __END; + +#else + +__SINGLE: +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + result = P1->_float.value STT_TEST P2->_float.value; + goto __END; + +#endif + +__OBJECT: + + result = OBJECT_comp_value(P1, P2) STT_TEST TRUE; + goto __END_RELEASE; + +__NULL: + + result = VALUE_is_null(P1->type == T_NULL ? P2 : P1) STT_TEST TRUE; + goto __END_RELEASE; + +__ERROR: + + THROW(E_TYPE, "Number or Date", TYPE_get_name(type)); + +__END_RELEASE: + +#ifdef STT_CASE + RELEASE(P2); +#else + RELEASE(P1); + RELEASE(P2); +#endif + +__END: + + #ifdef STT_CASE + + P2->type = T_BOOLEAN; + P2->_boolean.value = result ? -1 : 0; + + #else + + P1->type = T_BOOLEAN; + P1->_boolean.value = result ? -1 : 0; + + SP--; + + #endif + +} + +#else /* inequality tests */ + +void STT_NAME(ushort code) +{ + static void *jump[17] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, + &&__NULL, &&__ERROR + }; + + TYPE type, typem; + VALUE *P1, *P2; + bool result, variant; + + P1 = SP - 2; + P2 = P1 + 1; + + variant = FALSE; + type = code & 0x1F; + goto *jump[type]; + +__VARIANT: + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + type = Max(P1->type, P2->type); + + if (type == T_NULL || TYPE_is_string(type)) + { + typem = Min(P1->type, P2->type); + if (!TYPE_is_string(typem)) + THROW(E_TYPE, TYPE_get_name(typem), TYPE_get_name(type)); + } + else if (TYPE_is_object(type)) + goto __ERROR; + + if (!variant) + *PC |= type; + + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + result = P1->_integer.value STT_TEST P2->_integer.value; + goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + result = P1->_long.value STT_TEST P2->_long.value; + goto __END; + +__DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + result = (DATE_comp_value(P1, P2) STT_TEST 0); + goto __END; + +__NULL: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + +__STRING: + + result = (STRING_compare(P1->_string.addr + P1->_string.start, P1->_string.len, P2->_string.addr + P2->_string.start, P2->_string.len) STT_TEST 0); + + goto __END_RELEASE; + +__SINGLE: +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + result = P1->_float.value STT_TEST P2->_float.value; + goto __END; + +__ERROR: + + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(type)); + +__END_RELEASE: + + RELEASE(P1); + RELEASE(P2); + +__END: + + P1->type = T_BOOLEAN; + P1->_boolean.value = result ? -1 : 0; + + SP--; +} + +#endif + + +#ifdef STT_INEQUALITY +#undef STT_INEQUALITY +#endif + +#undef STT_NAME +#undef STT_TEST + +#ifdef STT_NO_OBJECT +#undef STT_NO_OBJECT +#endif + +#ifdef STT_CASE +#undef STT_CASE +#endif + +#ifdef STT_NULL +#undef STT_NULL +#endif + +#ifdef STT_NEAR +#undef STT_NEAR +#endif + + diff --git a/main/gbx/gbx_subr_time.c b/main/gbx/gbx_subr_time.c new file mode 100644 index 00000000..6dc5f6f2 --- /dev/null +++ b/main/gbx/gbx_subr_time.c @@ -0,0 +1,287 @@ +/*************************************************************************** + + gbx_subr_time.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" + +#include +#include + +#include "gb_error.h" +#include "gbx_value.h" +#include "gbx_subr.h" +#include "gbx_local.h" +#include "gbx_date.h" + + +void SUBR_timer(void) +{ + double result = 0.0; + + DATE_timer(&result, TRUE); + + SP->type = T_FLOAT; + SP->_float.value = result; + SP++; +} + + +void SUBR_now(void) +{ + DATE_now(SP); + SP++; +} + + +void SUBR_year(ushort code) +{ + DATE_SERIAL *date; + int val; + + SUBR_ENTER_PARAM(1); + + VALUE_conv(PARAM, T_DATE); + + date = DATE_split(PARAM); + + switch(code & 0xF) + { + case 1: val = date->year; break; + case 2: val = date->month; break; + case 3: val = date->day; break; + case 4: val = date->hour; break; + case 5: val = date->min; break; + case 6: val = date->sec; break; + case 7: val = date->weekday; break; + case 8: val = date->msec; break; + default: val = 0; + } + + PARAM->type = T_INTEGER; + PARAM->_integer.value = val; + + #if 0 + SUBR_LEAVE() /* Not necessary */ + #endif +} + + +void SUBR_date(ushort code) +{ + DATE_SERIAL date; + + SUBR_ENTER(); + + if (NPARAM <= 1) + { + if (NPARAM == 0) + DATE_now(PARAM); + else + VALUE_conv(PARAM, T_DATE); + + date = *DATE_split(PARAM); + date.hour = 0; + date.min = 0; + date.sec = 0; + date.msec = 0; + } + else if (NPARAM >= 3) + { + VALUE_conv_integer(PARAM); + VALUE_conv_integer(&PARAM[1]); + VALUE_conv_integer(&PARAM[2]); + + CLEAR(&date); + date.year = PARAM->_integer.value; + date.month = PARAM[1]._integer.value; + date.day = PARAM[2]._integer.value; + + if (NPARAM >= 4) + { + VALUE_conv_integer(&PARAM[3]); + date.hour = PARAM[3]._integer.value; + } + + if (NPARAM >= 5) + { + VALUE_conv_integer(&PARAM[4]); + date.min = PARAM[4]._integer.value; + } + + if (NPARAM >= 6) + { + VALUE_conv_integer(&PARAM[5]); + date.sec = PARAM[5]._integer.value; + } + + if (NPARAM >= 7) + { + VALUE_conv_integer(&PARAM[6]); + date.msec = PARAM[6]._integer.value; + } + } + else + THROW(E_NEPARAM); + + if (DATE_make(&date, RETURN)) + THROW(E_DATE); + + SUBR_LEAVE(); +} + + +void SUBR_time(ushort code) +{ + DATE_SERIAL date; + + SUBR_ENTER(); + + if (NPARAM <= 1) + { + if (NPARAM == 0) + DATE_now(PARAM); + else + VALUE_conv(PARAM, T_DATE); + + date = *DATE_split(PARAM); + date.year = 0; + } + else if (NPARAM >= 3) + { + VALUE_conv_integer(PARAM); + VALUE_conv_integer(&PARAM[1]); + VALUE_conv_integer(&PARAM[2]); + + CLEAR(&date); + date.hour = PARAM->_integer.value; + date.min = PARAM[1]._integer.value; + date.sec = PARAM[2]._integer.value; + if (NPARAM == 4) + { + VALUE_conv_integer(&PARAM[3]); + date.msec = PARAM[3]._integer.value; + } + } + else + THROW(E_NEPARAM); + + if (DATE_make(&date, RETURN)) + THROW(E_DATE); + + SUBR_LEAVE(); +} + + +void SUBR_date_op(ushort code) +{ + SUBR_ENTER_PARAM(3); + + switch (code & 0xF) + { + case 0: /* DateAdd */ + + VALUE_conv(PARAM, T_DATE); + *RETURN = *PARAM; + DATE_add(RETURN, SUBR_get_integer(&PARAM[1]), SUBR_get_integer(&PARAM[2])); + + break; + + case 1: /* DateDiff */ + + VALUE_conv(PARAM, T_DATE); + VALUE_conv(&PARAM[1], T_DATE); + + /* Dates are inverted! */ + RETURN->_integer.value = DATE_diff(&PARAM[1], PARAM, SUBR_get_integer(&PARAM[2])); + RETURN->type = T_INTEGER; + + break; + } + + SUBR_LEAVE(); +} + + +void SUBR_week(ushort code) +{ + bool plain = FALSE; + int start = LOCAL_get_first_day_of_week(); + DATE_SERIAL ds; + VALUE date, first; + int day, n; + + SUBR_ENTER(); + + if (NPARAM >= 1) + { + VALUE_conv(PARAM, T_DATE); + date = *PARAM; + + if (NPARAM >= 2) + { + start = SUBR_get_integer(&PARAM[1]); + if (start < 0 || start > 6) + THROW(E_ARG); + + if (NPARAM == 3) + plain = SUBR_get_boolean(&PARAM[2]); + } + } + else + DATE_now(&date); + + /* Split it */ + ds = *DATE_split(&date); + /* Set to 1 Jan of the current year */ + ds.month = 1; + ds.day = 1; + ds.hour = 0; + ds.min = 0; + ds.sec = 0; + /* Convert to date & time */ + DATE_make(&ds, &first); + + /* Get the weekday of this 1 Jan */ + day = DATE_split(&first)->weekday; + + /* number of beginning days to ignore */ + + n= 0; + while (day != start) + { + day++; + if (day > 6) + day = 0; + n++; + } + + if (!plain) + { + if (n >= 4) + n -= 7; + } + + RETURN->type = T_INTEGER; + RETURN->_integer.value = (DATE_diff(&date, &first, DP_DAY) - n + 7) / 7; + + SUBR_LEAVE(); +} diff --git a/main/gbx/gbx_type.c b/main/gbx/gbx_type.c new file mode 100644 index 00000000..11dc8a80 --- /dev/null +++ b/main/gbx/gbx_type.c @@ -0,0 +1,351 @@ +/*************************************************************************** + + gbx_type.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_TYPE_C + +#include + +#include "gb_common.h" +#include "gb_common_buffer.h" +#include "gb_limit.h" +#include "gbx_variant.h" +#include "gbx_class.h" +#include "gambas.h" + +#include "gbx_type.h" + + +void *TYPE_joker = NULL; + +const size_t TYPE_sizeof_memory_tab[16] = { 0, 1, 1, 2, 4, 8, 4, 8, 8, sizeof(void *), sizeof(void *), sizeof(void *), sizeof(VARIANT), 0, 0, 0 }; + + +// Needed size for storing a class global variable + +size_t TYPE_sizeof(TYPE type) +{ + static size_t size[16] = { 0, 4, 4, 4, 4, 8, 8, 8, 8, sizeof(void *), sizeof(void *), sizeof(void *), sizeof(VARIANT), 0, 0, 0 }; + + if (TYPE_is_object(type)) + return sizeof(void *); + else + return size[type]; +} + + +const char *TYPE_get_name(TYPE type) +{ + static const char *name[17] = + { + "Void", + "Boolean", + "Byte", + "Short", + "Integer", + "Long", + "Single", + "Float", + "Date", + "String", + "String", + "Pointer", + "Variant", + "Function", + "Class", + "Null", + "Object" + }; + + + if (TYPE_is_pure_object(type)) + return ((CLASS *)type)->name; + else + return name[type]; +} + + +const char *TYPE_to_string(TYPE type) +{ + switch (type) + { + case T_BOOLEAN: return "b"; + case T_BYTE: return "c"; + case T_SHORT: return "h"; + case T_INTEGER: return "i"; + case T_LONG: return "l"; + case T_SINGLE: return "g"; + case T_FLOAT: return "f"; + case T_DATE: return "d"; + case T_STRING: return "s"; + case T_POINTER: return "p"; + case T_VARIANT: return "v"; + case T_OBJECT: return "o"; + + default: + if (TYPE_is_pure_object(type)) + return ((CLASS *)type)->name; + else + return ""; + + } +} + + +void TYPE_signature_length(const char *sign, char *len_min, char *len_max, char *var) +{ + char c; + int len = 0; + bool brace = FALSE; + + *len_min = 0; + *len_max = 0; + *var = 0; + + if (sign == NULL) + return; + + for(;;) + { + c = *sign++; + if (c == 0) + break; + + if (c == '.') + { + *var = 1; + break; + } + + if (c == '[') + { + brace = TRUE; + continue; + } + + + if (c == '\'' || c == '(' || c == '<') + { + for(;;) + { + c = *sign; + if (c == 0) + break; + sign++; + if (c == '\'' || c == ')' || c == '>') + break; + } + continue; + } + + if (c == ']') + continue; + + if (!brace) + *len_min = len + 1; + + if (islower(c)) + { + len++; + continue; + } + + len++; + + for(;;) + { + c = *sign; + if (c == 0) + break; + sign++; + if (c == ';') + break; + } + } + + *len_max = len; +} + + +TYPE TYPE_from_string(const char **ptype) +{ + const char *start; + const char *type; + bool quote = FALSE; + + for(;;) + { + type = *ptype; + + if (type == NULL || *type == 0) + return T_VOID; + + (*ptype)++; + + if (*type == '\'') + { + quote = !quote; + continue; + } + + if (*type == ')' || *type == '>') + { + quote = FALSE; + continue; + } + + if (quote) + continue; + + if (*type == '(' || *type == '<') + { + quote = TRUE; + continue; + } + + //if (index("[]<>", *type) == NULL) + if (!(*type == '[' || *type == ']' || *type == '<' || *type == '>')) + break; + } + + switch(*type) + { + case 'b': return T_BOOLEAN; + case 'c': return T_BYTE; + case 'h': return T_SHORT; + case 'i': return T_INTEGER; + case 'l': return T_LONG; + case 'g': return T_SINGLE; + case 'f': return T_FLOAT; + case 'd': return T_DATE; + case 's': return T_STRING; + case 'v': return T_VARIANT; + case 'o': return T_OBJECT; + case 'p': return T_POINTER; + + default: + + start = type; + + while (*type != ';' && *type != 0 && *type != '(') + type++; + + if (*start == '*') + { + strcpy(COMMON_buffer, ((CLASS *)TYPE_joker)->name); + start++; + if (type > start) + strncat(COMMON_buffer, start, type - start); + } + else if (*start && start[1] == '*') + { + COMMON_buffer[0] = *start; + strcpy(&COMMON_buffer[1], ((CLASS *)TYPE_joker)->name); + start += 2; + if (type > start) + strncat(COMMON_buffer, start, type - start); + } + else + { + memcpy(COMMON_buffer, start, type - start); + COMMON_buffer[type - start] = 0; + } + + *ptype = (char *)type + 1; + + // Template classes search their symbols locally first + if (TYPE_joker) + return (TYPE)CLASS_find(NULL); + else + return (TYPE)CLASS_find_global(NULL); + } +} + + +TYPE *TYPE_transform_signature(TYPE **signature, const char *sign, int npmax) +{ + TYPE *tsign; + int i; + + //fprintf(stderr, "TYPE_transform_signature: %s\n", sign); + + tsign = *signature; + + for (i = 0; i < npmax; i++) + { + tsign[i] = TYPE_from_string(&sign); + //fprintf(stderr, "%p ", (void *)tsign[i]); + } + //fputc('\n', stderr); + + *signature += npmax; + + return tsign; +} + +bool TYPE_are_compatible(TYPE type, TYPE ptype) +{ + if (type == ptype) + return TRUE; + + if (!TYPE_are_objects(type, ptype)) + return FALSE; + + if (ptype == T_OBJECT) + return TRUE; + + if (type == T_OBJECT) + return FALSE; + + if (CLASS_inherits((CLASS *)type, (CLASS *)ptype)) + return TRUE; + + return FALSE; +} + +bool TYPE_compare_signature(TYPE *sign1, int np1, TYPE *sign2, int np2, bool check_compat) +{ + int i; + + if (np1 != np2) + return TRUE; + + if (check_compat) + { + for (i = 0; i < np1; i++) + { + if (!TYPE_are_compatible(sign1[i], sign2[i])) + return TRUE; + } + } + else + { + for (i = 0; i < np1; i++) + { + if (sign1[i] != sign2[i]) + return TRUE; + } + } + + return FALSE; +} + diff --git a/main/gbx/gbx_type.h b/main/gbx/gbx_type.h new file mode 100644 index 00000000..30d40e74 --- /dev/null +++ b/main/gbx/gbx_type.h @@ -0,0 +1,96 @@ +/*************************************************************************** + + gbx_type.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_TYPE_H +#define __GBX_TYPE_H + +#include "gb_type_common.h" + +/* Types du compilateur */ + +#define MAX_TYPE 16 + +typedef + struct { + unsigned char flag; + unsigned char id; + short value; + } + CTYPE; + +#define CTYPE_is_static(type) ((type).flag & TF_STATIC) +#define CTYPE_is_public(type) ((type).flag & TF_PUBLIC) +#define CTYPE_get_kind(type) ((type).flag & 0x7) + +// If type > T_OBJECT, then type is a pointer to the class + +typedef + uintptr_t TYPE; + +typedef + void (*TYPE_FUNC)(); + +typedef + TYPE_FUNC TYPE_JUMP[T_OBJECT]; + + +#ifndef __GBX_TYPE_C +EXTERN void *TYPE_joker; +EXTERN const size_t TYPE_sizeof_memory_tab[]; +#endif + +#define TYPE_is_void(type) ((type) == T_VOID) +#define TYPE_is_null(type) ((type) == T_NULL) +#define TYPE_is_object(type) ((type) >= T_OBJECT) +#define TYPE_is_object_null(type) ((type) >= T_NULL) +#define TYPE_is_pure_object(type) ((type) > T_OBJECT) +#define TYPE_is_boolean(type) ((type) == T_BOOLEAN) +#define TYPE_is_integer(type) ((type) >= T_BOOLEAN && (type) <= T_INTEGER) +#define TYPE_is_integer_long(type) ((type) >= T_BOOLEAN && (type) <= T_LONG) +#define TYPE_is_long(type) ((type) == T_LONG) +#define TYPE_is_single(type) ((type) == T_SINGLE) +#define TYPE_is_float(type) ((type) == T_FLOAT) +#define TYPE_is_variant(type) ((type) == T_VARIANT) +#define TYPE_is_number(type) ((type) >= T_BOOLEAN && (type) <= T_FLOAT) +#define TYPE_is_number_date(type) ((type) >= T_BOOLEAN && (type) <= T_DATE) +#define TYPE_is_string(type) ((type) == T_STRING || (type) == T_CSTRING) +#define TYPE_is_function(type) ((type) == T_FUNCTION) +#define TYPE_is_pointer(type) ((type) == T_POINTER) + +#define TYPE_are_objects(_t1, _t2) (TYPE_is_object(_t1) && TYPE_is_object(_t2)) +//#define TYPE_are_not_objects(_t1, _t2) (((_t1) | (_t2)) < T_OBJECT) + +size_t TYPE_sizeof(TYPE type); +#define TYPE_sizeof_memory(_type) (TYPE_is_object(_type) ? sizeof(void *) : TYPE_sizeof_memory_tab[_type]) +#define TYPE_is_value(_type) (TYPE_is_object(_type) || TYPE_is_null(_type) || TYPE_sizeof_memory_tab[_type] > 0) + +const char *TYPE_get_name(TYPE type); + +TYPE TYPE_from_string(const char **ptype); +const char *TYPE_to_string(TYPE type); +TYPE *TYPE_transform_signature(TYPE **signature, const char *sign, int nparam); +void TYPE_signature_length(const char *sign, char *len_min, char *len_max, char *var); +bool TYPE_are_compatible(TYPE type, TYPE ptype); +bool TYPE_compare_signature(TYPE *sign1, int np1, TYPE *sign2, int np2, bool check_compat); + +#endif diff --git a/main/gbx/gbx_value.c b/main/gbx/gbx_value.c new file mode 100644 index 00000000..c8174f6a --- /dev/null +++ b/main/gbx/gbx_value.c @@ -0,0 +1,2189 @@ +/*************************************************************************** + + gbx_value.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_VALUE_C + +#include "gb_common.h" +#include "gb_common_case.h" + +#include "gbx_math.h" +#include "gbx_type.h" + +#include "gbx_c_array.h" +#include "gbx_string.h" +#include "gbx_number.h" +#include "gbx_object.h" +#include "gbx_variant.h" +#include "gbx_date.h" +#include "gbx_struct.h" +#include "gbx_exec.h" +#include "gbx_local.h" +#include "gb_common_buffer.h" +#include "gbx_extern.h" + +#include "gbx_value.h" + +#if 0 +static bool unknown_function(VALUE *value) +{ + if (value->_function.kind == FUNCTION_UNKNOWN) + { + EXEC_unknown_property = TRUE; + EXEC_unknown_name = CP->load->unknown[value->_function.index]; + + EXEC_special(SPEC_UNKNOWN, value->_function.class, value->_function.object, 0, FALSE); + + //object = value->_function.object; + OBJECT_UNREF(value->_function.object); + + SP--; + //*val = *SP; + COPY_VALUE(value, SP); + return TRUE; + } + else + return FALSE; +} +#endif + +void THROW_TYPE(TYPE wanted, TYPE got) +{ + THROW(E_TYPE, TYPE_get_name(wanted), TYPE_get_name(got)); +} + +static void undo_variant(VALUE *value) +{ + static void *jump[16] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__CSTRING, &&__POINTER, &&__VOID, &&__FUNCTION, &&__CLASS, &&__NULL + }; + + TYPE type = value->_variant.vtype; + + //if (index != T_NULL) + // VALUE_read(value, &value->_variant.value, value->_variant.vtype); + + value->type = type; + + if (TYPE_is_object(type)) + goto __OBJECT; + else + goto *jump[type]; + +__BOOLEAN: + + value->_boolean.value = value->_variant.value._boolean ? -1 : 0; + return; + +__BYTE: + + value->_byte.value = value->_variant.value._byte; + return; + +__SHORT: + + value->_short.value = value->_variant.value._short; + return; + +__INTEGER: + + value->_integer.value = value->_variant.value._integer; + return; + +__LONG: + + value->_long.value = value->_variant.value._long; + return; + +__SINGLE: + + value->_single.value = value->_variant.value._single; + return; + +__FLOAT: + + value->_float.value = value->_variant.value._float; + return; + +__DATE: + + // It works, as the normal date field is before the variant date field! + value->_date.date = value->_variant.value._date.date; + value->_date.time = value->_variant.value._date.time; + return; + +__STRING: + + { + char *str = value->_variant.value._string; + + value->type = T_STRING; + value->_string.addr = str; + value->_string.start = 0; + value->_string.len = STRING_length(str); + + return; + } + +__CSTRING: + + { + char *str = value->_variant.value._string; + + value->type = T_CSTRING; + value->_string.addr = str; + value->_string.start = 0; + value->_string.len = strlen(str); + + return; + } + +__OBJECT: + + value->_object.object = value->_variant.value._object; + return; + +__POINTER: + + value->_pointer.value = value->_variant.value._pointer; + return; + +__CLASS: // Is it useful for variants ? + + value->_class.class = value->_variant.value._object; + value->_class.super = NULL; + return; + +__NULL: + return; + +__VOID: +__FUNCTION: + + ERROR_panic("Bad type (%d) for undo_variant", type); +} + + +static void VALUE_put(VALUE *value, void *addr, TYPE type) +{ + static void *jump[16] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL + }; + + VALUE_conv(value, type); + + if (TYPE_is_object(type)) + goto __OBJECT; + else + goto *jump[type]; + +__BOOLEAN: + + *((unsigned char *)addr) = (value->_boolean.value != 0 ? 255 : 0); + return; + +__BYTE: + + *((unsigned char *)addr) = (unsigned char)(value->_byte.value); + return; + +__SHORT: + + *((short *)addr) = (short)(value->_short.value); + return; + +__INTEGER: + + *((int *)addr) = value->_integer.value; + return; + +__LONG: + + *((int64_t *)addr) = value->_long.value; + return; + +__SINGLE: + + *((float *)addr) = value->_single.value; + return; + +__FLOAT: + + *((double *)addr) = value->_float.value; + return; + +__DATE: + + /* Inverted, if value ~= addr */ + + ((int *)addr)[1] = value->_date.time; + ((int *)addr)[0] = value->_date.date; + return; + +/*__STRING: + + ((int *)addr)[0] = (int)(value->_string.addr + value->_string.start); + ((int *)addr)[1] = value->_string.len; + return;*/ + +__POINTER: + + *((void **)addr) = value->_pointer.value; + return; + +__OBJECT: + + *((void **)addr) = value->_object.object; + return; + +__VARIANT: + + *((VARIANT *)addr) = *((VARIANT *)&value->_variant.vtype); + return; + +__CLASS: + + *((void **)addr) = value->_class.class; + return; + +__VOID: +__FUNCTION: +__NULL: +__STRING: + + ERROR_panic("Bad type (%d) for VALUE_put", type); +} + + +/* This function must keep the datatype, as it is used for initializing local variables */ + +void VALUE_default(VALUE *value, TYPE type) +{ + static void *jump[16] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL + }; + + value->type = type; + + if (TYPE_is_object(type)) + goto __OBJECT; + else + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + value->_integer.value = 0; + return; + +__LONG: + + value->_long.value = 0; + return; + +__SINGLE: + value->_single.value = 0; + return; + +__FLOAT: + value->_float.value = 0; + return; + +__STRING: + value->_string.addr = NULL; + value->_string.start = 0; + value->_string.len = 0; + return; + +__VARIANT: + value->_variant.vtype = T_NULL; + return; + +__POINTER: + value->_pointer.value = NULL; + return; + +__DATE: + value->_date.date = 0; + value->_date.time = 0; + return; + +__VOID: + return; + +__OBJECT: + value->_object.class = (CLASS *)type; + value->_object.object = NULL; + return; + +__FUNCTION: +__CLASS: +__NULL: + ERROR_panic("VALUE_default: Unknown default type"); +} + + +void VALUE_convert(VALUE *value, TYPE type) +{ + static const void *jump[16][16] = + { + /* ,------> void b c h i l g f d cs s p v func class n */ + // | + /* void */ { &&__OK, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, &&__NR, }, + /* b */ { &&__N, &&__OK, &&__b2c, &&__b2h, &&__TYPE, &&__b2l, &&__b2g, &&__b2f, &&__N, &&__b2s, &&__b2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* c */ { &&__N, &&__c2b, &&__OK, &&__c2h, &&__TYPE, &&__c2l, &&__c2g, &&__c2f, &&__c2d, &&__c2s, &&__c2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* h */ { &&__N, &&__h2b, &&__h2c, &&__OK, &&__TYPE, &&__h2l, &&__h2g, &&__h2f, &&__h2d, &&__h2s, &&__h2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* i */ { &&__N, &&__i2b, &&__i2c, &&__i2h, &&__OK, &&__i2l, &&__i2g, &&__i2f, &&__i2d, &&__i2s, &&__i2s, &&__i2p, &&__2v, &&__N, &&__N, &&__N, }, + /* l */ { &&__N, &&__l2b, &&__l2c, &&__l2h, &&__l2i, &&__OK, &&__l2g, &&__l2f, &&__l2d, &&__l2s, &&__l2s, &&__l2p, &&__2v, &&__N, &&__N, &&__N, }, + /* g */ { &&__N, &&__g2b, &&__g2c, &&__g2h, &&__g2i, &&__g2l, &&__OK, &&__g2f, &&__g2d, &&__g2s, &&__g2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* f */ { &&__N, &&__f2b, &&__f2c, &&__f2h, &&__f2i, &&__f2l, &&__f2g, &&__OK, &&__f2d, &&__f2s, &&__f2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* d */ { &&__N, &&__d2b, &&__d2c, &&__d2h, &&__d2i, &&__d2l, &&__d2g, &&__d2f, &&__OK, &&__d2s, &&__d2s, &&__N, &&__2v, &&__N, &&__N, &&__N, }, + /* cs */ { &&__N, &&__s2b, &&__s2c, &&__s2h, &&__s2i, &&__s2l, &&__s2g, &&__s2f, &&__s2d, &&__OK, &&__OK, &&__N, &&__s2v, &&__N, &&__N, &&__N, }, + /* s */ { &&__N, &&__s2b, &&__s2c, &&__s2h, &&__s2i, &&__s2l, &&__s2g, &&__s2f, &&__s2d, &&__OK, &&__OK, &&__N, &&__s2v, &&__N, &&__N, &&__N, }, + /* p */ { &&__N, &&__p2b, &&__N, &&__N, &&__p2i, &&__p2l, &&__N, &&__N, &&__N, &&__p2s, &&__p2s, &&__OK, &&__2v, &&__N, &&__N, &&__N, }, + /* v */ { &&__N, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__v2, &&__OK, &&__N, &&__v2, &&__v2, }, + /* func */ { &&__N, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__func, &&__F2p, &&__func, &&__OK, &&__N, &&__func, }, + /* class */ { &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__2v, &&__N, &&__OK, &&__N, }, + /* null */ { &&__N, &&__n2b, &&__N, &&__N, &&__N, &&__N, &&__N, &&__N, &&__n2d, &&__n2s, &&__n2s, &&__n2p, &&__2v, &&__N, &&__N, &&__OK, }, + }; + + int len; + char *addr; + CLASS *class; + bool test; + +__CONV: + + if ((type | value->type) >> 4) + goto __OBJECT; + else + goto *jump[value->type][type]; + +__c2b: +__h2b: +__i2b: + + value->_integer.value = -(value->_integer.value != 0); + value->type = T_BOOLEAN; + return; + +__l2b: + + value->_integer.value = -(value->_long.value != 0); + value->type = T_BOOLEAN; + return; + +__g2b: + + value->_integer.value = -(value->_single.value != 0); + value->type = T_BOOLEAN; + return; + +__f2b: + + value->_integer.value = -(value->_float.value != 0); + value->type = T_BOOLEAN; + return; + +__d2b: + value->_integer.value = -(value->_date.date != 0 || value->_date.time != 0); + value->type = T_BOOLEAN; + return; + +__p2b: + value->_integer.value = -(value->_pointer.value != NULL); + value->type = T_BOOLEAN; + return; + +__b2c: +__h2c: +__i2c: + + value->_integer.value = (unsigned char)value->_integer.value; + value->type = T_BYTE; + return; + +__l2c: + + value->_integer.value = (unsigned char)value->_long.value; + value->type = T_BYTE; + return; + +__g2c: + + value->_integer.value = (unsigned char)value->_single.value; + value->type = T_BYTE; + return; + +__f2c: + + value->_integer.value = (unsigned char)value->_float.value; + value->type = T_BYTE; + return; + +__b2h: +__c2h: +__i2h: + + value->_integer.value = (short)value->_integer.value; + value->type = T_SHORT; + return; + +__l2h: + + value->_integer.value = (short)value->_long.value; + value->type = T_SHORT; + return; + +__g2h: + + value->_integer.value = (short)value->_single.value; + value->type = T_SHORT; + return; + +__f2h: + + value->_integer.value = (short)value->_float.value; + value->type = T_SHORT; + return; + +__l2i: + + value->_integer.value = (int)value->_long.value; + value->type = T_INTEGER; + return; + +__g2i: + + value->_integer.value = (int)value->_single.value; + value->type = T_INTEGER; + return; + +__f2i: + + value->_integer.value = (int)value->_float.value; + value->type = T_INTEGER; + return; + +__p2i: + + value->_integer.value = (int)(intptr_t)value->_pointer.value; + value->type = T_INTEGER; + return; + +__b2l: +__c2l: +__h2l: +__i2l: + + value->_long.value = (int64_t)value->_integer.value; + value->type = T_LONG; + return; + +__g2l: + + value->_long.value = (int64_t)value->_single.value; + value->type = T_LONG; + return; + +__f2l: + + value->_long.value = (int64_t)value->_float.value; + value->type = T_LONG; + return; + +__p2l: + + value->_long.value = (int64_t)(intptr_t)value->_pointer.value; + value->type = T_LONG; + return; + +__b2g: +__c2g: +__h2g: +__i2g: + + value->_single.value = value->_integer.value; + value->type = T_SINGLE; + return; + +__l2g: + + value->_single.value = (float)value->_long.value; + if (!isfinite(value->_single.value)) + THROW(E_OVERFLOW); + value->type = T_SINGLE; + return; + +__f2g: + + value->_single.value = (float)value->_float.value; + if (!isfinite(value->_single.value)) + THROW(E_OVERFLOW); + value->type = T_SINGLE; + return; + +__b2f: +__c2f: +__h2f: +__i2f: + + value->_float.value = value->_integer.value; + value->type = T_FLOAT; + return; + +__l2f: + + value->_float.value = value->_long.value; + value->type = T_FLOAT; + return; + +__g2f: + + value->_float.value = value->_single.value; + value->type = T_FLOAT; + return; + +__c2d: +__h2d: +__i2d: + + value->_date.date = Max(0, value->_integer.value); + value->_date.time = 0; + value->type = T_DATE; + return; + +__l2d: + + if (value->_long.value < 0) + value->_date.date = 0; + else if (value->_long.value > INT_MAX) + value->_date.date = INT_MAX; + else + value->_date.date = (int)value->_long.value; + + value->_date.time = 0; + value->type = T_DATE; + return; + +__g2d: + { + float val = value->_single.value; + float ival = floorf(val); + value->_date.time = (int)((val - ival) * 86400000.0 + 0.5); + value->_date.date = (int)ival; + value->type = T_DATE; + return; + } + +__f2d: + { + double val = value->_float.value; + double ival = floor(val); + value->_date.time = (int)((val - ival) * 86400000.0 + 0.5); + value->_date.date = (int)ival; + value->type = T_DATE; + return; + } + +__d2c: +__d2h: +__d2i: + + value->_integer.value = value->_date.date; + value->type = T_INTEGER; + goto *jump[T_INTEGER][type]; + +__d2l: + + value->_long.value = value->_date.date; + value->type = T_LONG; + return; + +__d2g: + + value->_single.value = (float)value->_date.date + (float)value->_date.time / 86400000.0; + value->type = T_SINGLE; + return; + +__d2f: + + value->_float.value = (double)value->_date.date + (double)value->_date.time / 86400000.0; + value->type = T_FLOAT; + return; + +__b2s: + + if (value->_boolean.value) + STRING_char_value(value, 'T'); + else + STRING_void_value(value); + return; + +__c2s: +__h2s: +__i2s: + +/*len = sprintf(COMMON_buffer, "%d", value->_integer.value); + STRING_new_temp_value(value, COMMON_buffer, len);*/ + NUMBER_int_to_string(value->_integer.value, 0, 10, value); + BORROW(value); + return; + +__l2s: + +/*len = sprintf(COMMON_buffer, "%" PRId64, value->_long.value); + STRING_new_temp_value(value, COMMON_buffer, len);*/ + NUMBER_int_to_string(value->_long.value, 0, 10, value); + BORROW(value); + return; + +__g2s: + + LOCAL_format_number(value->_single.value, LF_GENERAL_NUMBER, NULL, 0, &addr, &len, FALSE); + STRING_new_temp_value(value, addr, len); + BORROW(value); + return; + +__f2s: + + LOCAL_format_number(value->_float.value, LF_GENERAL_NUMBER, NULL, 0, &addr, &len, FALSE); + STRING_new_temp_value(value, addr, len); + BORROW(value); + return; + +__p2s: + #if OS_64BITS + NUMBER_int_to_string((int64_t)(intptr_t)value->_pointer.value, 0, 16, value); + #else + NUMBER_int_to_string((int)(intptr_t)value->_pointer.value, 0, 16, value); + #endif + BORROW(value); + return; + +__d2s: + + len = DATE_to_string(COMMON_buffer, value); + STRING_new_temp_value(value, COMMON_buffer, len); + BORROW(value); + return; + +__s2b: + + addr = value->_string.addr; + value->_integer.value = -(addr != NULL && value->_string.len != 0); + if (value->type == T_STRING) + STRING_unref(&addr); + value->type = T_BOOLEAN; + return; + +__s2c: +__s2h: +__s2i: + + addr = value->type == T_STRING ? value->_string.addr : NULL; + + if (NUMBER_from_string(NB_READ_INTEGER, value->_string.addr + value->_string.start, value->_string.len, value)) + goto __N; + + STRING_unref(&addr); + goto *jump[T_INTEGER][type]; + +__s2l: + + addr = value->type == T_STRING ? value->_string.addr : NULL; + + if (NUMBER_from_string(NB_READ_LONG, value->_string.addr + value->_string.start, value->_string.len, value)) + goto __N; + + STRING_unref(&addr); + return; + +__s2g: + + addr = value->type == T_STRING ? value->_string.addr : NULL; + + if (NUMBER_from_string(NB_READ_FLOAT, value->_string.addr + value->_string.start, value->_string.len, value)) + goto __N; + + value->_single.value = value->_float.value; + + STRING_unref(&addr); + value->type = type; + return; + +__s2f: + + addr = value->type == T_STRING ? value->_string.addr : NULL; + + if (NUMBER_from_string(NB_READ_FLOAT, value->_string.addr + value->_string.start, value->_string.len, value)) + goto __N; + + STRING_unref(&addr); + value->type = type; + return; + +__s2d: + + addr = value->type == T_STRING ? value->_string.addr : NULL; + + if (DATE_from_string(value->_string.addr + value->_string.start, value->_string.len, value, FALSE)) + goto __N; + + STRING_unref(&addr); + return; + +__n2b: + + value->_integer.value = 0; + value->type = T_BOOLEAN; + return; + +__n2d: + + DATE_void_value(value); + return; + +__n2s: + + STRING_void_value(value); + return; + +__n2p: + + value->_pointer.value = 0; + value->type = T_POINTER; + return; + +__v2: + + undo_variant(value); + goto __CONV; + +__s2v: + + addr = STRING_copy_from_value_temp(value); + + if (addr != value->_string.addr) + { + STRING_ref(addr); + + if (value->type == T_STRING) + STRING_unref(&value->_string.addr); + } + + value->_variant.vtype = T_STRING; //value->type; + value->_variant.value._string = addr; + value->type = T_VARIANT; + return; + +__2v: + + /* VALUE_put ne fonctionne pas avec T_STRING ! */ + if (value->type != T_NULL) + VALUE_put(value, &value->_variant.value, value->type); + + value->_variant.vtype = value->type; + value->type = T_VARIANT; + return; + +__func: + + goto __N; + +__i2p: + value->_pointer.value = (void *)(intptr_t)value->_integer.value; + value->type = T_POINTER; + return; + +__l2p: + value->_pointer.value = (void *)(intptr_t)value->_long.value; + value->type = T_POINTER; + return; + +__F2p: + + value->_pointer.value = EXTERN_make_callback(&value->_function); + value->type = T_POINTER; + return; + +__OBJECT: + + if (!TYPE_is_object(type)) + { + if (type == T_BOOLEAN) + { + test = (value->_object.object != NULL); + OBJECT_UNREF(value->_object.object); + value->_boolean.value = -test; + value->type = T_BOOLEAN; + return; + } + + if (type == T_VARIANT) + goto __2v; + + if (!value->_object.object) + goto __N; + + if (value->type == T_OBJECT) + class = OBJECT_class(value->_object.object); + else + class = value->_object.class; + + if (class->has_convert) + { + void *unref = value->_object.object; + TYPE old_type = value->type; + + if (!((*class->convert)(value->_object.object, type, value))) + { + OBJECT_UNREF(unref); + + if (value->type == old_type) + goto __TYPE; + else + goto __OK; + } + } + + goto __N; + } + + if (!TYPE_is_object(value->type)) + { + if (value->type == T_NULL) + { + OBJECT_null(value, (CLASS *)type); // Also works if type == T_OBJECT + goto __TYPE; + } + + if (value->type == T_POINTER && type != T_OBJECT) + { + class = (CLASS *)type; + + if (CLASS_is_struct(class)) + { + value->_object.object = CSTRUCT_create_static(STRUCT_CONST, class, value->_pointer.value); + OBJECT_REF(value->_object.object); + goto __TYPE; + } + } + + if (value->type == T_VARIANT) + goto __v2; + + if (value->type == T_FUNCTION) + goto __func; + + if (value->type == T_CLASS) + { + class = value->_class.class; + + if (CLASS_is_virtual(class)) + THROW(E_VIRTUAL); + + CLASS_load(class); + + if (class->auto_create) + value->_object.object = CLASS_auto_create(class, 0); + else + value->_object.object = class; + + OBJECT_REF(value->_object.object); + value->type = T_OBJECT; + /* on continue... */ + } + else + { + if (TYPE_is_pure_object(type)) + { + class = (CLASS *)type; + + if (class->has_convert) + { + if (!((*class->convert)(NULL, value->type, value))) + { + OBJECT_REF(value->_object.object); + goto __TYPE; + } + } + } + + goto __N; + } + } + + if (value->_object.object == NULL) + goto __TYPE; + + if (value->type == T_OBJECT) + class = OBJECT_class(value->_object.object); + else + class = value->_object.class; + + if (CLASS_is_virtual(class)) + THROW(E_VIRTUAL); + + if (type == T_OBJECT) + goto __TYPE; + +__RETRY: + + if ((class == (CLASS *)type) || CLASS_inherits(class, (CLASS *)type)) + goto __TYPE; + + if (value->type != T_OBJECT && value->_object.object) + { + class = OBJECT_class(value->_object.object); + value->type = T_OBJECT; + goto __RETRY; + } + + if (class->has_convert) + { + void *unref = value->_object.object; + if (!((*class->convert)(value->_object.object, type, value))) + { + OBJECT_UNREF(unref); + OBJECT_REF(value->_object.object); + goto __TYPE; + } + } + + CLASS *class2 = (CLASS *)type; + if (class2->has_convert) + { + void *unref = value->_object.object; + if (!((*class2->convert)(NULL, OBJECT_class(unref), value))) + { + OBJECT_UNREF(unref); + OBJECT_REF(value->_object.object); + goto __TYPE; + } + } + + THROW_TYPE(type, (TYPE)class); + +__TYPE: + + value->type = type; + +__OK: + + return; + +__N: + + THROW_TYPE(type, value->type); + +__NR: + + THROW(E_NRETURN); +} + + +void VALUE_write_variant(VALUE *value, void *addr) +{ + static void *jump[16] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__CSTRING, &&__POINTER, &&__VOID, &&__VOID, &&__CLASS, &&__NULL + }; + + TYPE type = value->_variant.vtype; + + if (TYPE_is_object(type)) + goto __OBJECT; + else + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: +__LONG: +__SINGLE: +__FLOAT: +__DATE: +__POINTER: +__NULL: + + VARIANT_free((VARIANT *)addr); + ((VARIANT *)addr)->type = value->_variant.vtype; + ((VARIANT *)addr)->value.data = value->_variant.value.data; + return; + +__CSTRING: +__STRING: +{ + char *str = value->_variant.value._string; + STRING_ref(str); + VARIANT_free((VARIANT *)addr); + ((VARIANT *)addr)->type = GB_T_STRING; + ((VARIANT *)addr)->value._string = str; + return; +} + +__OBJECT: +__CLASS: +{ + void *object = value->_variant.value._object; + OBJECT_REF(object); + VARIANT_free((VARIANT *)addr); + ((VARIANT *)addr)->type = type; + ((VARIANT *)addr)->value._object = object; + return; +} + +__VOID: + + ERROR_panic("Bad type (%d) for VALUE_write_variant", type); +} + +void VALUE_write(VALUE *value, void *addr, TYPE type) +{ + static void *jump[16] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL + }; + + char *str; + +__CONV: + + if (TYPE_is_object(type)) + goto __OBJECT; + else + goto *jump[type]; + +__BOOLEAN: + + VALUE_conv_boolean(value); + *((unsigned char *)addr) = (value->_boolean.value != 0 ? 255 : 0); + return; + +__BYTE: + + VALUE_conv(value, T_BYTE); + *((unsigned char *)addr) = (unsigned char)(value->_byte.value); + return; + +__SHORT: + + VALUE_conv(value, T_SHORT); + *((short *)addr) = (short)(value->_short.value); + return; + +__INTEGER: + + VALUE_conv_integer(value); + *((int *)addr) = value->_integer.value; + return; + +__LONG: + + VALUE_conv(value, T_LONG); + *((int64_t *)addr) = value->_long.value; + return; + +__SINGLE: + + VALUE_conv(value, T_SINGLE); + *((float *)addr) = value->_single.value; + return; + +__FLOAT: + + VALUE_conv_float(value); + *((double *)addr) = value->_float.value; + return; + +__DATE: + + VALUE_conv(value, T_DATE); + ((int *)addr)[0] = value->_date.date; + ((int *)addr)[1] = value->_date.time; + return; + +__STRING: + + VALUE_conv_string(value); + + str = STRING_copy_from_value_temp(value); + STRING_ref(str); + STRING_unref((char **)addr); + *((char **)addr) = str; + return; + +__OBJECT: + + VALUE_conv(value, type); + + OBJECT_REF(value->_object.object); + OBJECT_UNREF(*((void **)addr)); + *((void **)addr) = value->_object.object; + return; + +__CLASS: + + VALUE_conv(value, type); + + OBJECT_REF(value->_class.class); + OBJECT_UNREF(*((void **)addr)); + *((void **)addr) = value->_class.class; + return; + +__POINTER: + + VALUE_conv(value, T_POINTER); + *((void **)addr) = value->_pointer.value; + return; + +__VARIANT: + + VARIANT_undo(value); + + type = value->type; + if (type == T_CSTRING) + type = T_STRING; + + VARIANT_clear((VARIANT *)addr); + ((VARIANT *)addr)->type = type; + + /* Et si type ne fait pas partie des types valides pour cette fonction ?? */ + if (type == T_NULL) + return; + + addr = &((VARIANT *)addr)->value.data; + /*goto *jump[Min(T_OBJECT, type)];*/ + goto __CONV; + +__VOID: + + THROW(E_NRETURN); + +__FUNCTION: + + THROW_TYPE(T_VARIANT, type); + +__NULL: + + ERROR_panic("Bad type (%d) for VALUE_write", type); +} + + + +void VALUE_read(VALUE *value, void *addr, TYPE type) +{ + static void *jump[16] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__CSTRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL + }; + + value->type = type; + + if (TYPE_is_object(type)) + goto __OBJECT; + else + goto *jump[type]; + +__BOOLEAN: + + value->_boolean.value = (*((unsigned char *)addr) != 0) ? (-1) : 0; + return; + +__BYTE: + + value->_byte.value = *((unsigned char *)addr); + return; + +__SHORT: + + value->_short.value = *((short *)addr); + return; + +__INTEGER: + + value->_integer.value = *((int *)addr); + return; + +__LONG: + + value->_long.value = *((int64_t *)addr); + return; + +__SINGLE: + + value->_single.value = *((float *)addr); + return; + +__FLOAT: + + value->_float.value = *((double *)addr); + return; + +__DATE: + + value->_date.date = ((int *)addr)[0]; + value->_date.time = ((int *)addr)[1]; + return; + +__STRING: + + { + char *str = *((char **)addr); + + value->type = T_STRING; + value->_string.addr = str; + value->_string.start = 0; + value->_string.len = STRING_length(str); + + return; + } + +__CSTRING: + + { + char *str = *((char **)addr); + + value->type = T_CSTRING; + value->_string.addr = str; + value->_string.start = 0; + value->_string.len = (str == NULL) ? 0 : strlen(str); + + return; + } + +__OBJECT: + + value->_object.object = *((void **)addr); + return; + +__POINTER: + + value->_pointer.value = *((void **)addr); + return; + +__VARIANT: + + value->_variant.type = T_VARIANT; + value->_variant.vtype = ((VARIANT *)addr)->type; + + if (value->_variant.vtype == T_VOID) + value->_variant.vtype = T_NULL; + + VARIANT_copy_value(&value->_variant, ((VARIANT *)addr)); + + return; + +__CLASS: + + value->_class.class = *((void **)addr); + value->_class.super = NULL; + return; + +__VOID: +__FUNCTION: +__NULL: + + ERROR_panic("Bad type (%d) for VALUE_read", type); +} + + +void VALUE_free(void *addr, TYPE type) +{ + if (type == T_STRING) + { + STRING_unref((char **)addr); + *((char **)addr) = NULL; + } + else if (TYPE_is_object(type)) + { + OBJECT_UNREF(*((void **)addr)); + *((void **)addr) = NULL; + } + else if (type == T_VARIANT) + { + VARIANT_free((VARIANT *)addr); + ((VARIANT *)addr)->type = T_NULL; + } +} + + + +void VALUE_to_string(VALUE *value, char **addr, int *len) +{ + static void *jump[16] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL + }; + +__CONV: + + if (TYPE_is_object(value->type)) + goto __OBJECT; + else + goto *jump[value->type]; + +__NULL: + + *addr = ""; // To be coherent with Print "", as Null == "" */ + *len = 0; + return; + +__BOOLEAN: + + if (value->_boolean.value) + { + *addr = (char *)LOCAL_gettext("True"); + *len = strlen(*addr); + } + else + { + *addr = (char *)LOCAL_gettext("False"); + *len = strlen(*addr); + //*addr = LOCAL_local.false_str; + //*len = LOCAL_local.len_false_str; + } + return; + +__BYTE: +__SHORT: +__INTEGER: + + *len = sprintf(COMMON_buffer, "%d", value->_integer.value); + *addr = COMMON_buffer; + + return; + +__LONG: + + *len = sprintf(COMMON_buffer, "%" PRId64, value->_long.value); + *addr = COMMON_buffer; + + return; + +__DATE: + + LOCAL_format_date(DATE_split(value), LF_STANDARD, NULL, 0, addr, len); + return; + +__SINGLE: + + LOCAL_format_number(value->_single.value, LF_SHORT_NUMBER, NULL, 0, addr, len, TRUE); + return; + +__FLOAT: + + LOCAL_format_number(value->_float.value, LF_STANDARD, NULL, 0, addr, len, TRUE); + return; + +__STRING: + + *len = value->_string.len; + *addr = value->_string.addr + value->_string.start; + return; + +__OBJECT: + + { + CLASS *class; + + if (VALUE_is_null(value)) + goto __NULL; + + class = OBJECT_class(value->_object.object); + if (class->has_convert) + { + VALUE temp; + if (!((*class->convert)(value->_object.object, T_CSTRING, &temp))) + { + *addr = temp._string.addr + temp._string.start; + *len = temp._string.len; + STRING_free_later(*addr); + return; + } + } + + *len = sprintf(COMMON_buffer, "(%s %p)", class->name, value->_object.object); + *addr = COMMON_buffer; + return; + } + +__POINTER: + + if (VALUE_is_null(value)) + goto __NULL; + + *len = sprintf(COMMON_buffer, "(Pointer %p)", value->_pointer.value); + *addr = COMMON_buffer; + return; + +__VARIANT: + + VARIANT_undo(value); + goto __CONV; + +__VOID: + + THROW(E_NRETURN); + +__CLASS: + + *len = sprintf(COMMON_buffer, "(Class %s)", value->_class.class->name); + *addr = COMMON_buffer; + return; + +__FUNCTION: + + //if (unknown_function(value)) + // goto __CONV; + + *len = sprintf(COMMON_buffer, "(Function %s:%d)", value->_function.class->name, value->_function.index); + *addr = COMMON_buffer; +} + + +void VALUE_from_string(VALUE *value, const char *addr, int len) +{ + while (len > 0 && isspace(*addr)) + addr++, len--; + + while (len > 0 && isspace(addr[len - 1])) + len--; + + if (len > 0) + { + if (!DATE_from_string(addr, len, value, TRUE)) + return; + + if (!NUMBER_from_string(NB_READ_ALL | NB_READ_HEX_BIN | NB_LOCAL, addr, len, value)) + return; + + if (len == LOCAL_local.len_true_str && strncasecmp(addr, LOCAL_local.true_str, len) == 0) + { + value->type = T_BOOLEAN; + value->_boolean.value = -1; + return; + } + + if (len == LOCAL_local.len_false_str && strncasecmp(addr, LOCAL_local.false_str, len) == 0) + { + value->type = T_BOOLEAN; + value->_boolean.value = 0; + return; + } + } + + VALUE_null(value); +} + + +void VALUE_class_read(CLASS *class, VALUE *value, char *addr, CTYPE ctype, void *ref) +{ + VALUE_class_read_inline(class, value, addr, ctype, ref); +} + + +void VALUE_class_write(CLASS *class, VALUE *value, char *addr, CTYPE ctype) +{ + if (ctype.id == T_OBJECT) + { + TYPE type = (ctype.value >= 0) ? (TYPE)class->load->class_ref[ctype.value] : T_OBJECT; + + VALUE_conv(value, type); + + OBJECT_REF(value->_object.object); + OBJECT_UNREF(*((void **)addr)); + *((void **)addr) = value->_object.object; + //VALUE_write(value, addr, (ctype.value >= 0) ? (TYPE)class->load->class_ref[ctype.value] : T_OBJECT); + } + else if (ctype.id == TC_STRUCT) + { + TYPE type = (TYPE)class->load->class_ref[ctype.value]; + VALUE_conv(value, type); + THROW_ILLEGAL(); + } + else if (ctype.id == TC_ARRAY) + { + THROW_ILLEGAL(); + } + else + { + VALUE_write(value, addr, (TYPE)ctype.id); + } +} + +void VALUE_class_constant(CLASS *class, VALUE *value, int ind) +{ + VALUE_class_constant_inline(class, value, ind); +} + + +bool VALUE_is_null(VALUE *val) +{ + static void *jump[16] = { + &&__FALSE, &&__FALSE, &&__FALSE, &&__FALSE, &&__FALSE, &&__FALSE, &&__FALSE, &&__FALSE, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FALSE, &&__FALSE, &&__NULL + }; + + TYPE type = val->type; + + if (TYPE_is_object(type)) + return val->_object.object == NULL; + else + goto *jump[type]; + +__NULL: + return TRUE; + +__STRING: + return val->_string.addr == 0 || val->_string.len == 0; + +__DATE: + return val->_date.date == 0 && val->_date.time == 0; + +__POINTER: + return val->_pointer.value == NULL; + +__VARIANT: + + if (val->_variant.vtype == T_NULL) + return TRUE; + + if (val->_variant.vtype == T_STRING) + return val->_variant.value._string == NULL; + + if (val->_variant.vtype == T_DATE) + return val->_variant.value.data == 0; + + if (val->_variant.vtype == T_POINTER) + return val->_variant.value._pointer == NULL; + + if (TYPE_is_object(val->_variant.vtype)) + return val->_variant.value._object == NULL; + +__FALSE: + return FALSE; +} + + +void VALUE_convert_boolean(VALUE *value) +{ + static const void *jump[16] = + { + &&__NR, &&__OK, &&__c2b, &&__h2b, &&__i2b, &&__l2b, &&__g2b, &&__f2b, + &&__d2b, &&__s2b, &&__s2b, &&__N, &&__v2, &&__func, &&__N, &&__n2b + }; + + char *addr; + TYPE type = value->type; + + if (TYPE_is_object(type)) + { + if (value->_object.object) + { + OBJECT_just_unref(value->_object.object); + value->_boolean.value = -1; + } + value->type = T_BOOLEAN; + return; + } + else + goto *jump[type]; + +__c2b: +__h2b: +__i2b: + + value->_integer.value = (value->_integer.value != 0) ? -1 : 0; + value->type = T_BOOLEAN; + return; + +__l2b: + + value->_integer.value = (value->_long.value != 0) ? -1 : 0; + value->type = T_BOOLEAN; + return; + +__g2b: + + value->_integer.value = (value->_single.value != 0) ? -1 : 0; + value->type = T_BOOLEAN; + return; + +__f2b: + + value->_integer.value = (value->_float.value != 0) ? -1 : 0; + value->type = T_BOOLEAN; + return; + +__d2b: + + value->_integer.value = (value->_date.date != 0 || value->_date.time != 0) ? -1 : 0; + value->type = T_BOOLEAN; + return; + +__s2b: + + addr = value->_string.addr; + value->_integer.value = ((addr != NULL) && (value->_string.len != 0)) ? -1 : 0; + if (value->type == T_STRING) + STRING_unref(&addr); + value->type = T_BOOLEAN; + return; + +__n2b: + + value->_integer.value = 0; + value->type = T_BOOLEAN; + return; + +__v2: +{ + static const void *jumpv[] = { &&__NR, &&__bv2b, &&__bv2b, &&__hv2b, &&__iv2b, &&__lv2b, &&__iv2b, &&__lv2b, &&__lv2b, &&__sv2b, &&__sv2b, &&__pv2b, &&__N, &&__N, &&__N, &&__nv2b }; + int test; + + type = value->_variant.vtype; + if (TYPE_is_object(type)) + goto __ov2b; + else + goto *jumpv[type]; + +__bv2b: + test = value->_variant.value._boolean != 0; + goto __VOK; + +__hv2b: + test = value->_variant.value._short != 0; + goto __VOK; + +__iv2b: + test = value->_variant.value._integer != 0; + goto __VOK; + +__lv2b: + test = value->_variant.value._long != 0; + goto __VOK; + +__pv2b: + test = value->_variant.value._pointer != 0; + goto __VOK; + +__ov2b: + test = value->_variant.value._object != 0; + OBJECT_UNREF(value->_variant.value._object); + goto __VOK; + +__sv2b: + test = value->_variant.value._string && *value->_variant.value._string; + STRING_unref(&value->_variant.value._string); + goto __VOK; + +__nv2b: + test = FALSE; + +__VOK: + value->type = T_BOOLEAN; + value->_integer.value = test ? -1 : 0; + return; +} + +__func: + + //if (unknown_function(value)) + // goto __CONV; + //else + goto __N; + +__N: + + THROW_TYPE(T_BOOLEAN, type); + +__NR: + + THROW(E_NRETURN); + +__OK: + return; +} + + +void VALUE_convert_integer(VALUE *value) +{ + static const void *jump[16] = + { + &&__NR, &&__TYPE, &&__TYPE, &&__TYPE, &&__OK, &&__l2i, &&__g2i, &&__f2i, + &&__d2i, &&__s2i, &&__s2i, &&__N, &&__v2, &&__func, &&__N, &&__N + }; + + char *addr; + +__CONV: + + goto *jump[value->type]; + +__l2i: + + value->_integer.value = (int)value->_long.value; + goto __TYPE; + +__g2i: + + value->_integer.value = (int)value->_single.value; + goto __TYPE; + +__f2i: + + value->_integer.value = (int)value->_float.value; + goto __TYPE; + +__d2i: + + value->_integer.value = value->_date.date; + goto __TYPE; + +__s2i: + + addr = value->type == T_STRING ? value->_string.addr : NULL; + + if (NUMBER_from_string(NB_READ_INTEGER, value->_string.addr + value->_string.start, value->_string.len, value)) + goto __N; + + STRING_unref(&addr); + goto __TYPE; + +__v2: + + undo_variant(value); + if (TYPE_is_object(value->type)) + goto __N; + else + goto __CONV; + +__func: + + //if (unknown_function(value)) + // goto __CONV; + //else + goto __N; + +__TYPE: + + value->type = T_INTEGER; + +__OK: + + return; + +__N: + + THROW_TYPE(T_INTEGER, value->type); + +__NR: + + THROW(E_NRETURN); +} + + +void VALUE_convert_float(VALUE *value) +{ + static const void *jump[16] = + { + &&__NR, &&__b2f, &&__c2f, &&__h2f, &&__i2f, &&__l2f, &&__g2f, &&__OK, + &&__d2f, &&__s2f, &&__s2f, &&__N, &&__v2, &&__func, &&__N, &&__N + }; + + char *addr; + +__CONV: + + if (TYPE_is_object(value->type)) + goto __N; + else + goto *jump[value->type]; + +__b2f: +__c2f: +__h2f: +__i2f: + + value->_float.value = value->_integer.value; + value->type = T_FLOAT; + return; + +__l2f: + + value->_float.value = value->_long.value; + value->type = T_FLOAT; + return; + +__g2f: + + value->_float.value = value->_single.value; + value->type = T_FLOAT; + return; + +__d2f: + + value->_float.value = (double)value->_date.date + (double)value->_date.time / 86400000.0; + value->type = T_FLOAT; + return; + +__s2f: + + addr = value->type == T_STRING ? value->_string.addr : NULL; + + if (NUMBER_from_string(NB_READ_FLOAT, value->_string.addr + value->_string.start, value->_string.len, value)) + goto __N; + + STRING_unref(&addr); + return; + +__v2: + + undo_variant(value); + if (TYPE_is_object(value->type)) + goto __N; + else + goto __CONV; + +__func: + + //if (unknown_function(value)) + // goto __CONV; + //else + goto __N; + +__N: + + THROW_TYPE(T_FLOAT, value->type); + +__NR: + + THROW(E_NRETURN); + +__OK: + + return; +} + + +void VALUE_convert_string(VALUE *value) +{ + static const void *jump[16] = + { + &&__NR, &&__b2s, &&__c2s, &&__h2s, &&__i2s, &&__l2s, &&__g2s, &&__f2s, + &&__d2s, &&__OK, &&__OK, &&__N, &&__v2, &&__func, &&__N, &&__n2s + }; + + int len; + char *addr; + +__CONV: + + goto *jump[value->type]; + +__b2s: + + if (value->_boolean.value) + STRING_char_value(value, 'T'); + else + STRING_void_value(value); + return; + +__c2s: +__h2s: +__i2s: + + NUMBER_int_to_string(value->_integer.value, 0, 10, value); + BORROW(value); + return; + +__l2s: + + NUMBER_int_to_string(value->_long.value, 0, 10, value); + BORROW(value); + return; + +__g2s: + + LOCAL_format_number(value->_single.value, LF_GENERAL_NUMBER, NULL, 0, &addr, &len, FALSE); + STRING_new_temp_value(value, addr, len); + BORROW(value); + return; + +__f2s: + + LOCAL_format_number(value->_float.value, LF_GENERAL_NUMBER, NULL, 0, &addr, &len, FALSE); + STRING_new_temp_value(value, addr, len); + BORROW(value); + return; + +__d2s: + + len = DATE_to_string(COMMON_buffer, value); + STRING_new_temp_value(value, COMMON_buffer, len); + BORROW(value); + return; + +__n2s: + + STRING_void_value(value); + return; + +__v2: + + undo_variant(value); + if (TYPE_is_object(value->type)) + goto __N; + else + goto __CONV; + +__func: + + //if (unknown_function(value)) + // goto __CONV; + //else + goto __N; + +__OK: + + return; + +__N: + + THROW_TYPE(T_STRING, value->type); + +__NR: + + THROW(E_NRETURN); +} + + +void VALUE_convert_variant(VALUE *value) +{ + static const void *jump[16] = + { + &&__NR, &&__2v, &&__2v, &&__2v, &&__2v, &&__2v, &&__2v, &&__2v, + &&__2v, &&__s2v, &&__s2v, &&__2v, &&__OK, &&__func, &&__2v, &&__2v + }; + + char *addr; + +//__CONV: + + if (TYPE_is_object(value->type)) + goto __2v; + else + goto *jump[value->type]; + +__s2v: + + addr = STRING_copy_from_value_temp(value); + + if (addr != value->_string.addr) + { + STRING_ref(addr); + + if (value->type == T_STRING) + STRING_unref(&value->_string.addr); + } + + value->_variant.value._string = addr; + value->_variant.vtype = T_STRING; + + goto __TYPE; + +__2v: + + /* VALUE_put ne fonctionne pas avec T_STRING ! */ + if (value->type != T_NULL) + VALUE_put(value, &value->_variant.value, value->type); + + value->_variant.vtype = value->type; + goto __TYPE; + +__func: + + //if (unknown_function(value)) + // goto __CONV; + //else + goto __N; + +__TYPE: + + value->type = T_VARIANT; + +__OK: + + return; + +__N: + + THROW_TYPE(T_VARIANT, value->type); + +__NR: + + THROW(E_NRETURN); +} + +#if 0 +void VALUE_convert_object(VALUE *value, TYPE type) +{ + CLASS *class; + +__CONV: + + #if 0 + if (!TYPE_is_object(type)) + { + if (type == T_BOOLEAN) + { + test = (value->_object.object != NULL); + OBJECT_UNREF(value->_object.object); + value->_boolean.value = test ? -1 : 0; + goto __TYPE; + } + + if (type == T_VARIANT) + goto __2v; + + goto __N; + } + #endif + + if (!TYPE_is_object(value->type)) + { + if (value->type == T_NULL) + { + OBJECT_null(value, (CLASS *)type); /* marche aussi pour type = T_OBJECT */ + goto __TYPE; + } + + if (value->type == T_VARIANT) + goto __v2; + + if (value->type == T_FUNCTION) + goto __func; + + if (value->type == T_CLASS) + { + class = value->_class.class; + + if (CLASS_is_virtual(class)) + THROW(E_VIRTUAL); + + CLASS_load(class); + + if (class->auto_create) + value->_object.object = CLASS_auto_create(class, 0); + else + value->_object.object = class; + + OBJECT_REF(value->_object.object); + value->type = T_OBJECT; + /* on continue... */ + } + else + goto __N; + + } + + if (value->_object.object == NULL) + goto __TYPE; + + if (value->type == T_OBJECT) + { + /*if (value->_object.object == NULL) + goto __TYPE;*/ + + class = OBJECT_class(value->_object.object); + /* on continue */ + } + else + class = value->_object.class; + + if (CLASS_is_virtual(class)) + THROW(E_VIRTUAL); + + if (type == T_OBJECT) + goto __TYPE; + +__RETRY: + + if ((class == (CLASS *)type) || CLASS_inherits(class, (CLASS *)type)) + goto __TYPE; + + if (value->type != T_OBJECT && value->_object.object) + { + class = OBJECT_class(value->_object.object); + value->type = T_OBJECT; + goto __RETRY; + } + + if (class->special[SPEC_CONVERT] != NO_SYMBOL) + { + void *conv = ((void *(*)())(CLASS_get_desc(class, class->special[SPEC_CONVERT])->constant.value._pointer))(value->_object.object, type); + if (conv) + { + OBJECT_REF(conv); + OBJECT_UNREF(value->_object.object); + value->_object.object = conv; + goto __TYPE; + } + } + + THROW(E_TYPE, TYPE_get_name(type), TYPE_get_name((TYPE)class)); + +__v2: + + undo_variant(value); + goto __CONV; + +__func: + + //if (unknown_function(value)) + // goto __CONV; + //else + goto __N; + +__TYPE: + + value->type = type; + return; + +__N: + + THROW(E_TYPE, TYPE_get_name(type), TYPE_get_name(value->type)); +} +#endif + +void VALUE_undo_variant(VALUE *value) +{ + undo_variant(value); +} diff --git a/main/gbx/gbx_value.h b/main/gbx/gbx_value.h new file mode 100644 index 00000000..ea055aec --- /dev/null +++ b/main/gbx/gbx_value.h @@ -0,0 +1,656 @@ +/*************************************************************************** + + gbx_value.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_VALUE_H +#define __GBX_VALUE_H + +#include + +#include "gbx_type.h" +#include "gbx_class.h" + +#ifndef __DATE_DECLARED +#define __DATE_DECLARED +typedef + struct { + int date; + int time; + } + DATE; +#endif + +typedef + struct { + TYPE type; + int value; + } + VALUE_BOOLEAN; + +typedef + struct { + TYPE type; + int value; + } + VALUE_BYTE; + +typedef + struct { + TYPE type; + int value; + } + VALUE_SHORT; + +typedef + struct { + TYPE type; + int value; + } + VALUE_INTEGER; + +typedef + struct { + TYPE type; + #ifndef OS_64BITS + int _padding; + #endif + int64_t value; + } + VALUE_LONG; + +typedef + struct { + TYPE type; + char *value; + } + VALUE_POINTER; + +typedef + struct { + TYPE type; + float value; + } + VALUE_SINGLE; + +typedef + struct { + TYPE type; + #ifndef OS_64BITS + int _padding; + #endif + double value; + } + VALUE_FLOAT; + +typedef + struct { + TYPE type; + int date; /* number of days */ + int time; /* number of milliseconds */ + } + VALUE_DATE; + +typedef + struct { + TYPE type; + char *addr; + int start; + int len; + } + VALUE_STRING; + +typedef + struct { + TYPE type; + CLASS *class; + void *object; + char kind; + char defined; + short index; + } + VALUE_FUNCTION; + +enum +{ + FUNCTION_NULL, + FUNCTION_NATIVE, + FUNCTION_PRIVATE, + FUNCTION_PUBLIC, + FUNCTION_EVENT, + FUNCTION_EXTERN, + FUNCTION_UNKNOWN, + FUNCTION_CALL, + FUNCTION_SUBR +}; + +typedef + struct { + TYPE type; + TYPE ptype; + intptr_t value[2]; + } + VALUE_VOID; + +typedef + struct { + TYPE type; + TYPE vtype; + union { + char _boolean; + unsigned char _byte; + short _short; + int _integer; + int64_t _long; + float _single; + double _float; + DATE _date; + char *_string; + void *_pointer; + void *_object; + int64_t data; + } + value; + } + VALUE_VARIANT; + +typedef + struct { + CLASS *class; + void *object; + void *super; + } + VALUE_OBJECT; + +typedef + struct { + TYPE type; + CLASS *class; + void *super; + } + VALUE_CLASS; + +typedef + union value { + TYPE type; + VALUE_BOOLEAN _boolean; + VALUE_BYTE _byte; + VALUE_SHORT _short; + VALUE_INTEGER _integer; + VALUE_LONG _long; + VALUE_SINGLE _single; + VALUE_FLOAT _float; + VALUE_DATE _date; + VALUE_STRING _string; + VALUE_POINTER _pointer; + VALUE_FUNCTION _function; + VALUE_VARIANT _variant; + VALUE_CLASS _class; + VALUE_OBJECT _object; + VALUE_VOID _void; + } + VALUE; + +typedef + void (*VALUE_CONVERT_FUNC)(VALUE *); + +#define VALUE_copy(_dst, _src) \ + ((_dst)->_void.type = (_src)->_void.type, \ + (_dst)->_void.ptype = (_src)->_void.ptype, \ + (_dst)->_void.value[0] = (_src)->_void.value[0], \ + (_dst)->_void.value[1] = (_src)->_void.value[1]) + +#define VALUE_is_equal(_v1, _v2) (*_v1 == *v2) + +#define VALUE_is_object(val) (TYPE_is_object((val)->type)) +#define VALUE_is_string(val) ((val)->type == T_STRING || (val)->type == T_CSTRING) +#define VALUE_is_number(val) ((val)->type >= T_BYTE && (val)->type <= T_FLOAT) + +void VALUE_default(VALUE *value, TYPE type); + +void VALUE_convert(VALUE *value, TYPE type); + +void VALUE_convert_boolean(VALUE *value); +void VALUE_convert_integer(VALUE *value); +void VALUE_convert_float(VALUE *value); +void VALUE_convert_string(VALUE *value); +void VALUE_convert_variant(VALUE *value); + +void VALUE_read(VALUE *value, void *addr, TYPE type); +void VALUE_write(VALUE *value, void *addr, TYPE type); + +void VALUE_undo_variant(VALUE *value); +void VALUE_write_variant(VALUE *value, void *addr); + +//void VALUE_put(VALUE *value, void *addr, TYPE type); + +void VALUE_free(void *addr, TYPE type); +void VALUE_to_string(VALUE *value, char **addr, int *len); +void VALUE_from_string(VALUE *value, const char *addr, int len); + +void VALUE_class_read(CLASS *class, VALUE *value, char *addr, CTYPE ctype, void *ref); +void VALUE_class_write(CLASS *class, VALUE *value, char *addr, CTYPE ctype); +void VALUE_class_constant(CLASS *class, VALUE *value, int ind); + +#define VALUE_null(_val) ({ (_val)->type = T_NULL; (_val)->_object.object = NULL; }) +bool VALUE_is_null(VALUE *val); + +//void VALUE_get_string(VALUE *val, char **text, int *length); +#define VALUE_get_string(_value, _ptext, _plen) \ +({ \ + *(_plen) = (_value)->_string.len; \ + if (*(_plen)) \ + *(_ptext) = (_value)->_string.start + (_value)->_string.addr; \ + else \ + *(_ptext) = NULL; \ +}) + +void THROW_TYPE(TYPE wanted, TYPE got) NORETURN; + +#define VALUE_conv(_value, _type) \ +({ \ + if ((_value)->type != (_type)) \ + VALUE_convert(_value, _type); \ +}) + +#if 0 + +#define VALUE_conv_boolean(_value) \ +({ \ + VALUE *v = _value; \ + if (UNLIKELY(v->type != T_BOOLEAN)) \ + { \ + VALUE_convert_boolean(v); \ + } \ +}) + +#define VALUE_conv_integer(_value) \ +({ \ + VALUE *v = _value; \ + if (UNLIKELY(v->type != T_INTEGER)) \ + { \ + if (TYPE_is_object(v->type)) \ + THROW_TYPE_INTEGER(v->type); \ + VALUE_convert_integer(v); \ + } \ +}) + +#define VALUE_conv_float(_value) \ +({ \ + VALUE *v = _value; \ + if (UNLIKELY(v->type != T_FLOAT)) \ + { \ + if (TYPE_is_object(v->type)) \ + THROW_TYPE_FLOAT(v->type); \ + VALUE_convert_float(v); \ + } \ +}) + +#define VALUE_conv_string(_value) \ +({ \ + VALUE *v = _value; \ + if (UNLIKELY(v->type != T_STRING && v->type != T_CSTRING)) \ + { \ + if (TYPE_is_object(v->type)) \ + THROW_TYPE_STRING(v->type); \ + VALUE_convert_string(v); \ + } \ +}) + +#define VALUE_conv_variant(_value) \ +({ \ + if (UNLIKELY((_value)->type != T_VARIANT)) \ + VALUE_convert_variant(_value); \ +}) + +#define VALUE_conv_object(_value, _type) \ +({ \ + if (UNLIKELY((_value)->type != (_type))) \ + VALUE_convert_object(_value, _type); \ +}) + +#else + +#define VALUE_conv_boolean(_value) \ +({ \ + if (UNLIKELY((_value)->type != T_BOOLEAN)) \ + VALUE_convert_boolean(_value); \ +}) + +#define VALUE_conv_float(_value) \ +({ \ + if (UNLIKELY((_value)->type != T_FLOAT)) \ + VALUE_convert_float(_value); \ +}) + +#define VALUE_conv_variant(_value) \ +({ \ + if (UNLIKELY((_value)->type != T_VARIANT)) \ + VALUE_convert_variant(_value); \ +}) + +//#define VALUE_conv_boolean(_value) VALUE_conv(_value, T_BOOLEAN) +#define VALUE_conv_integer(_value) VALUE_conv(_value, T_INTEGER) +//#define VALUE_conv_float(_value) VALUE_conv(_value, T_FLOAT) +//#define VALUE_conv_variant(_value) VALUE_conv(_value, T_VARIANT) +#define VALUE_conv_object(_value, _type) VALUE_conv(_value, _type) + +#define VALUE_conv_string(_value) \ +({ \ + if (UNLIKELY((_value)->type != T_STRING && (_value)->type != T_CSTRING)) \ + VALUE_conv(_value, T_STRING); \ +}) + +#endif + +#define VALUE_is_super(_value) (EXEC_super && EXEC_super == (_value)->_object.super) + +#define VALUE_class_read_inline(_class, _value, _addr, _ctype, _ref) \ +({ \ + static void *jump[17] = { \ + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, \ + &&__STRING, &&__CSTRING, &&__POINTER, &&__VARIANT, &&__ARRAY, &&__STRUCT, &&__NULL, &&__OBJECT \ + }; \ + \ + for(;;) \ + { \ + (_value)->type = (_ctype).id; \ + goto *jump[(_ctype).id]; \ + \ + __BOOLEAN: \ + (_value)->_boolean.value = -(*((unsigned char *)(_addr)) != 0); \ + break; \ + \ + __BYTE: \ + (_value)->_byte.value = *((unsigned char *)(_addr)); \ + break; \ + \ + __SHORT: \ + (_value)->_short.value = *((short *)(_addr)); \ + break; \ + \ + __INTEGER: \ + (_value)->_integer.value = *((int *)(_addr)); \ + break; \ + \ + __LONG: \ + (_value)->_long.value = *((int64_t *)(_addr)); \ + break; \ + \ + __SINGLE: \ + (_value)->_single.value = *((float *)(_addr)); \ + break; \ + \ + __FLOAT: \ + (_value)->_float.value = *((double *)(_addr)); \ + break; \ + \ + __DATE: \ + (_value)->_date.date = ((int *)(_addr))[0]; \ + (_value)->_date.time = ((int *)(_addr))[1]; \ + break; \ + \ + __STRING: \ + { \ + char *str = *((char **)(_addr)); \ + \ + (_value)->type = T_STRING; \ + (_value)->_string.addr = str; \ + (_value)->_string.start = 0; \ + (_value)->_string.len = STRING_length(str); \ + \ + break; \ + } \ + \ + __CSTRING: \ + { \ + char *str = *((char **)(_addr)); \ + \ + (_value)->type = T_CSTRING; \ + (_value)->_string.addr = str; \ + (_value)->_string.start = 0; \ + (_value)->_string.len = (str == NULL) ? 0 : strlen(str); \ + \ + break; \ + } \ + \ + __OBJECT: \ + (_value)->_object.object = *((void **)(_addr)); \ + (_value)->type = ((_ctype).value >= 0) ? (TYPE)(_class)->load->class_ref[(_ctype).value] : T_OBJECT; \ + break; \ + \ + __POINTER: \ + (_value)->_pointer.value = *((void **)(_addr)); \ + break; \ + \ + __VARIANT: \ + (_value)->_variant.type = T_VARIANT; \ + (_value)->_variant.vtype = ((VARIANT *)(_addr))->type; \ + if ((_value)->_variant.vtype == T_VOID) \ + (_value)->_variant.vtype = T_NULL; \ + VARIANT_copy_value(&(_value)->_variant, ((VARIANT *)(_addr))); \ + break; \ + \ + __ARRAY: \ + { \ + void *object = CARRAY_create_static((_class), (_ref), (_class)->load->array[(_ctype).value], (_addr)); \ + (_value)->_object.class = OBJECT_class(object); \ + (_value)->_object.object = object; \ + break; \ + } \ + \ + __STRUCT: \ + { \ + void *object = CSTRUCT_create_static((_ref), (_class)->load->class_ref[(_ctype).value], (_addr)); \ + (_value)->_object.class = OBJECT_class(object); \ + (_value)->_object.object = object; \ + break; \ + } \ + \ + __VOID: \ + __NULL: \ + THROW_ILLEGAL(); \ + } \ +}) + +#define VALUE_class_constant_inline(_class, _value, _ind) \ +({ \ + static void *jump[] = \ + { \ + &&__ILLEGAL, &&__INTEGER, &&__INTEGER, &&__INTEGER, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, \ + &&__ILLEGAL, &&__STRING, &&__CSTRING, &&__POINTER, &&__ILLEGAL, &&__ILLEGAL, &&__ILLEGAL, &&__ILLEGAL \ + }; \ + \ + CLASS_CONST *cc; \ + \ + for(;;) \ + { \ + cc = &(_class)->load->cst[_ind]; \ + goto *jump[cc->type]; \ + \ + __INTEGER: \ + \ + (_value)->type = T_INTEGER; \ + (_value)->_integer.value = cc->_integer.value; \ + break; \ + \ + __LONG: \ + \ + (_value)->type = T_LONG; \ + (_value)->_long.value = cc->_long.value; \ + break; \ + \ + __SINGLE: \ + \ + (_value)->type = T_SINGLE; \ + (_value)->_single.value = cc->_single.value; \ + break; \ + \ + __FLOAT: \ + \ + (_value)->type = T_FLOAT; \ + (_value)->_float.value = cc->_float.value; \ + break; \ + \ + __STRING: \ + \ + (_value)->type = T_CSTRING; \ + (_value)->_string.addr = (char *)cc->_string.addr; \ + (_value)->_string.start = 0; \ + (_value)->_string.len = cc->_string.len; \ + break; \ + \ + __CSTRING: \ + \ + (_value)->type = T_CSTRING; \ + (_value)->_string.addr = (char *)LOCAL_gettext(cc->_string.addr); \ + (_value)->_string.start = 0; \ + (_value)->_string.len = strlen((_value)->_string.addr); \ + break; \ + \ + __POINTER: \ + \ + (_value)->type = T_POINTER; \ + (_value)->_pointer.value = NULL; \ + break; \ + \ + __ILLEGAL: \ + \ + THROW_ILLEGAL(); \ + } \ +}) + +#define VALUE_read_inline_type(_value, _addr, _ctype, _type, _label_noref, _label_ref) \ +({ \ + static void *jump[17] = { \ + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, \ + &&__STRING, &&__CSTRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL, &&__OBJECT \ + }; \ + \ + for(;;) \ + { \ + (_value)->type = (_type); \ + goto *jump[_ctype]; \ + \ + __BOOLEAN: \ + \ + (_value)->_boolean.value = (*((unsigned char *)(_addr)) != 0) ? (-1) : 0; \ + goto _label_noref; \ + \ + __BYTE: \ + \ + (_value)->_byte.value = *((unsigned char *)(_addr)); \ + goto _label_noref; \ + \ + __SHORT: \ + \ + (_value)->_short.value = *((short *)(_addr)); \ + goto _label_noref; \ + \ + __INTEGER: \ + \ + (_value)->_integer.value = *((int *)(_addr)); \ + goto _label_noref; \ + \ + __LONG: \ + \ + (_value)->_long.value = *((int64_t *)(_addr)); \ + goto _label_noref; \ + \ + __SINGLE: \ + \ + (_value)->_single.value = *((float *)(_addr)); \ + goto _label_noref; \ + \ + __FLOAT: \ + \ + (_value)->_float.value = *((double *)(_addr)); \ + goto _label_noref; \ + \ + __DATE: \ + \ + (_value)->_date.date = ((int *)(_addr))[0]; \ + (_value)->_date.time = ((int *)(_addr))[1]; \ + goto _label_noref; \ + \ + __STRING: \ + \ + { \ + char *str = *((char **)(_addr)); \ + \ + (_value)->type = T_STRING; \ + (_value)->_string.addr = str; \ + (_value)->_string.start = 0; \ + (_value)->_string.len = STRING_length(str); \ + \ + goto _label_ref; \ + } \ + \ + __CSTRING: \ + \ + { \ + char *str = *((char **)(_addr)); \ + \ + (_value)->type = T_CSTRING; \ + (_value)->_string.addr = str; \ + (_value)->_string.start = 0; \ + (_value)->_string.len = (str == NULL) ? 0 : strlen(str); \ + \ + goto _label_noref; \ + } \ + \ + __OBJECT: \ + \ + (_value)->_object.object = *((void **)(_addr)); \ + goto _label_ref; \ + \ + __POINTER: \ + \ + (_value)->_pointer.value = *((void **)(_addr)); \ + goto _label_noref; \ + break; \ + \ + __VARIANT: \ + \ + (_value)->_variant.type = T_VARIANT; \ + (_value)->_variant.vtype = ((VARIANT *)(_addr))->type; \ + \ + if ((_value)->_variant.vtype == T_VOID) \ + (_value)->_variant.vtype = T_NULL; \ + \ + VARIANT_copy_value(&(_value)->_variant, ((VARIANT *)(_addr))); \ + \ + goto _label_ref; \ + \ + __CLASS: \ + \ + (_value)->_class.class = *((void **)(_addr)); \ + (_value)->_class.super = NULL; \ + goto _label_noref; \ + \ + __VOID: \ + __FUNCTION: \ + __NULL: \ + THROW_ILLEGAL(); \ + } \ +}) + +#endif diff --git a/main/gbx/gbx_variant.h b/main/gbx/gbx_variant.h new file mode 100644 index 00000000..c79bf5b1 --- /dev/null +++ b/main/gbx/gbx_variant.h @@ -0,0 +1,99 @@ +/*************************************************************************** + + gbx_variant.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __VARIANT_H +#define __VARIANT_H + +#include "gbx_type.h" +#include "gbx_string.h" +#include "gbx_object.h" + +// On ARM 32 bits, sizeof(VARIANT) = 12, so it must be packed like GB_VARIANT + +typedef + struct + { + TYPE type; + union { + char _boolean; + unsigned char _byte; + short _short; + int _integer; + int64_t _long; + float _single; + double _float; + GB_DATE_VALUE _date; + char *_string; + void *_object; + void *_pointer; + int64_t data; + } + value; + } + PACKED + VARIANT; + +#define VARIANT_copy_value(_dst, _src) (_dst)->value.data = (_src)->value.data + +#define VARIANT_undo(_value) \ +({ \ + if ((_value)->type == T_VARIANT) \ + VALUE_undo_variant(_value); \ +}) + +#define VARIANT_free(_var) \ +({ \ + if ((_var)->type == T_STRING) \ + { \ + STRING_unref(&(_var)->value._string); \ + } \ + else if (TYPE_is_object((_var)->type)) \ + { \ + OBJECT_UNREF((_var)->value._object); \ + } \ +}) + +#define VARIANT_keep(_var) \ +({ \ + if ((_var)->type == T_STRING) \ + { \ + STRING_ref((_var)->value._string); \ + } \ + else if (TYPE_is_object((_var)->type)) \ + { \ + OBJECT_REF((_var)->value._object); \ + } \ +}) + +#define VARIANT_is_null(_var) \ + (((_var)->type == T_NULL) || ((_var)->type == T_STRING && !(_var)->value._string) || (TYPE_is_object((_var)->type) && !(_var)->value._object)) + +#define VARIANT_clear(_var) \ +({ \ + VARIANT_free(_var); \ + (_var)->type = 0; \ + (_var)->value.data = 0; \ +}) + +#endif + diff --git a/main/gbx/gbx_watch.c b/main/gbx/gbx_watch.c new file mode 100644 index 00000000..33d04240 --- /dev/null +++ b/main/gbx/gbx_watch.c @@ -0,0 +1,739 @@ +/*************************************************************************** + + gbx_watch.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_WATCH_C + +#include "gb_common.h" +#include "gb_error.h" + +#include +#include +#include + +#include "gb_array.h" +#include "gbx_exec.h" +#include "gbx_event.h" +#include "gbx_date.h" +#include "gbx_c_timer.h" +#include "gbx_watch.h" + +//#define DEBUG_TIMER 1 +//#define DEBUG_WATCH 1 + +static fd_set read_fd; +static fd_set write_fd; + +static WATCH_CALLBACK *watch_callback = NULL; +//static short *watch_index = NULL; + +static int max_fd = 0; + +static WATCH_TIMER *_timers = NULL; + +static int _do_not_really_delete_callback = 0; +static bool _must_delete_callback = FALSE; + +static struct timeval _last_time = { 0 }; + +#ifdef DEBUG_TIMER +double _debug_time; +static double time_to_double(const struct timeval *t) +{ + return (double)t->tv_sec + t->tv_usec / 1E6 - _debug_time; +} +#endif + +static void time_add(struct timeval *t1, const struct timeval *t2) +{ + t1->tv_sec += t2->tv_sec; + t1->tv_usec += t2->tv_usec; + if (t1->tv_usec > 1000000) + { + t1->tv_usec -= 1000000; + t1->tv_sec++; + } +} + +static void time_sub(struct timeval *t1, const struct timeval *t2) +{ + t1->tv_sec -= t2->tv_sec; + t1->tv_usec -= t2->tv_usec; + if (t1->tv_usec < 0) + { + t1->tv_usec += 1000000; + t1->tv_sec--; + } +} + +#define time_comp(t1, t2, op) ((t1)->tv_sec op (t2)->tv_sec || ((t1)->tv_sec == (t2)->tv_sec && (t1)->tv_usec op (t2)->tv_usec)) +#define time_lower_than(t1, t2) time_comp(t1, t2, <) + +static void time_from_ms(struct timeval *t, int ms) +{ + t->tv_sec = ms / 1000; + t->tv_usec = (ms % 1000) * 1000; +} + +static struct timeval *time_now(void) +{ + static struct timeval current; + + gettimeofday(¤t, NULL); + if (current.tv_usec < 0 || current.tv_usec >= 1000000) + fprintf(stderr, "gbx3: warning: gettimeofday: tv_usec = %ld!\n", current.tv_usec); + return ¤t; +} + + +static void add_timer(GB_TIMER *timer, const struct timeval *timeout) +{ + int i; + WATCH_TIMER *wt; + + for (i = 0; i < ARRAY_count(_timers); i++) + { + //if (_timers[i].timeout > timeout) + if (time_lower_than(timeout, &_timers[i].timeout)) + break; + } + + wt = ARRAY_insert(&_timers, i); + wt->timer = timer; + wt->timeout = *timeout; + + #ifdef DEBUG_TIMER + fprintf(stderr, "add_timer: %p at %i: %g\n", timer, i, time_to_double(timeout)); + #endif +} + +static int find_timer(GB_TIMER *timer) +{ + int i; + + for (i = 0; i < ARRAY_count(_timers); i++) + { + if (_timers[i].timer == timer) + return i; + } + + return (-1); +} + +static void remove_timer(GB_TIMER *timer) +{ + int i = find_timer(timer); + + if (i >= 0) + { + ARRAY_remove(&_timers, i) + #ifdef DEBUG_TIMER + fprintf(stderr, "remove_timer: %p at %i\n", timer, i); + #endif + } +} + +double WATCH_get_timeout(GB_TIMER *timer) +{ + int i = find_timer(timer); + + if (i < 0) + return 0.0; + else + return DATE_to_double(&_timers[i].timeout, TRUE); +} + +static void raise_timers() +{ + struct timeval timeout; + struct timeval delay; + struct timeval *now; + GB_TIMER *timer; + + now = time_now(); + + while (ARRAY_count(_timers)) + { + timeout = _timers[0].timeout; + if (time_lower_than(now, &timeout)) + return; + + timer = _timers[0].timer; + + #ifdef DEBUG_TIMER + fprintf(stderr, "raise_timers: %p has been triggered! now = %.7f timeout = %.7f\n", timer, time_to_double(now), time_to_double(&timeout)); + #endif + + time_from_ms(&delay, timer->delay); + time_add(&timeout, &delay); + + if (time_lower_than(&timeout, now)) + { + timeout = *now; + time_add(&timeout, &delay); + } + + remove_timer(timer); + add_timer(timer, &timeout); + + CTIMER_raise(timer); + } +} + + +static bool get_timeout(const struct timeval *wait, struct timeval *tv) +{ + struct timeval timeout; + struct timeval *now; + + if (ARRAY_count(_timers) == 0 && !wait) + return TRUE; + + now = time_now(); + + #ifdef DEBUG_TIMER + fprintf(stderr, "get_timeout: now = %.7g timeout = %.7g\n", time_to_double(now), time_to_double(&_timers[0].timeout)); + #endif + + if (ARRAY_count(_timers) == 0) + timeout = *wait; + else + { + timeout = _timers[0].timeout; + if (wait && time_lower_than(wait, &timeout)) + timeout = *wait; + } + + time_sub(&timeout, now); + + // HZ = 100 on Linux. If a timer must be triggered in less than 10 ms, then + // we do a busy wait instead of calling the system. + + if (timeout.tv_sec == 0 && timeout.tv_usec < (1000000 / 100)) + { + #ifdef DEBUG_TIMER + fprintf(stderr, "busy loop\n"); + #endif + // busy loop! + time_add(&timeout, now); + for(;;) + { + now = time_now(); + if (!time_lower_than(now, &timeout)) + break; + } + timeout.tv_sec = 0; + timeout.tv_usec = 0; + } + else if (timeout.tv_sec < 0) + { + #ifdef DEBUG_TIMER + fprintf(stderr, "timeout < 0: %ld %ld\n", timeout.tv_sec, timeout.tv_usec); + #endif + timeout.tv_sec = 0; + timeout.tv_usec = 0; + } + + *tv = timeout; + + #ifdef DEBUG_TIMER + fprintf(stderr, "timeout: %.7f %ld %ld\n", timeout.tv_sec + timeout.tv_usec / 1E6, tv->tv_sec, tv->tv_usec); + #endif + return FALSE; +} + + +void WATCH_init(void) +{ + FD_ZERO(&read_fd); + FD_ZERO(&write_fd); + + ARRAY_create(&watch_callback); + //ARRAY_create(&watch_index); + ARRAY_create(&_timers); + + #ifdef DEBUG_TIMER + DATE_timer(&_debug_time, FALSE); + #endif +} + + +void WATCH_exit(void) +{ + ARRAY_delete(&watch_callback); + //ARRAY_delete(&watch_index); + ARRAY_delete(&_timers); +} + + +static void watch_fd(int fd, int flag, bool watch) +{ + #if DEBUG_WATCH + fprintf(stderr, "watch_fd: %d for %s: %s\n", fd, flag == GB_WATCH_READ ? "read" : "write", watch ? "set" : "clear"); + #endif + + if (flag == GB_WATCH_READ) + { + if (watch) + FD_SET(fd, &read_fd); + else + FD_CLR(fd, &read_fd); + } + else if (flag == GB_WATCH_WRITE) + { + if (watch) + FD_SET(fd, &write_fd); + else + FD_CLR(fd, &write_fd); + } +} + + +static int watch_find_callback(int fd) +{ + int i; + + for (i = 0; i < ARRAY_count(watch_callback); i++) + { + if (watch_callback[i].fd == fd) + return i; + } + + return -1; +} + + +static int find_max_fd(void) +{ + int i; + int max = -1; + + for (i = 0; i < ARRAY_count(watch_callback); i++) + { + if (watch_callback[i].fd > max) + max = watch_callback[i].fd; + } + + return max; +} + +/*static void ensure_watch_index(int fd) +{ + int i; + int count = ARRAY_count(watch_index); + + //fprintf(stderr, "ensure_watch_index: %d (%d)\n", fd, count); + + if (fd < count) + return; + + ARRAY_add_many(&watch_index, fd - count + 1); + for (i = count; i <= fd; i++) + watch_index[i] = -1; +}*/ + +static WATCH_CALLBACK *watch_create_callback(int fd) +{ + int pos; + WATCH_CALLBACK *wcb; + + #if DEBUG_WATCH + fprintf(stderr, "watch_create_callback: %d\n", fd); + #endif + + pos = watch_find_callback(fd); + if (pos < 0) + { + pos = ARRAY_count(watch_callback); + wcb = ARRAY_add_void(&watch_callback); + wcb->fd = fd; + } + else + wcb = &watch_callback[pos]; + + if (fd > max_fd) + max_fd = fd; + + #if DEBUG_WATCH + fprintf(stderr, "watch_create_callback: %d -> %d read = %p (%p) write = %p (%p)\n", fd, pos, wcb->callback_read, (void *)wcb->param_read, wcb->callback_write, (void *)wcb->param_write); + #endif + + //ensure_watch_index(fd); + //watch_index[fd] = pos; + + return wcb; +} + + +static void watch_delete_callback(int fd) +{ + WATCH_CALLBACK *wcb; + int pos; + + #if DEBUG_WATCH + fprintf(stderr, "watch_delete_callback: %d (%d)\n", fd, _do_not_really_delete_callback); + #endif + + pos = watch_find_callback(fd); + if (pos < 0) + return; + + //watch_index[fd] = -1; + + wcb = &watch_callback[pos]; + wcb->fd = -1; + watch_fd(fd, GB_WATCH_READ, FALSE); + watch_fd(fd, GB_WATCH_WRITE, FALSE); + max_fd = find_max_fd(); + + if (_do_not_really_delete_callback) + { + #if DEBUG_WATCH + fprintf(stderr, "--> do not really delete\n"); + #endif + _must_delete_callback = TRUE; + return; + } + + ARRAY_remove(&watch_callback, pos); + + #if DEBUG_WATCH + fprintf(stderr, "--> deleted\n"); + #endif +} + + +void WATCH_watch(int fd, int type, void *callback, intptr_t param) +{ + WATCH_CALLBACK *wcb; + + if (fd < 0 || fd > FD_SETSIZE) + { + if (type != GB_WATCH_NONE) + ERROR_warning("trying to watch fd #%d", fd); + return; + } + + if (type == GB_WATCH_NONE) + watch_delete_callback(fd); + else + { + wcb = watch_create_callback(fd); + if (type == GB_WATCH_READ) + { + wcb->callback_read = callback; + wcb->param_read = param; + } + else + { + wcb->callback_write = callback; + wcb->param_write = param; + } + + #if DEBUG_WATCH + fprintf(stderr, "add watch: %d -> %d read = %p (%p) write = %p (%p)\n", fd, watch_find_callback(fd), wcb->callback_read, (void *)wcb->param_read, wcb->callback_write, (void *)wcb->param_write); + #endif + + if (!wcb->callback_read && !wcb->callback_write) + watch_delete_callback(fd); + else + { + watch_fd(fd, GB_WATCH_READ, wcb->callback_read != NULL); + watch_fd(fd, GB_WATCH_WRITE, wcb->callback_write != NULL); + } + } +} + + +static void raise_callback(fd_set *rfd, fd_set *wfd) +{ + int i; + WATCH_CALLBACK wcb; + + _do_not_really_delete_callback++; + + #if DEBUG_WATCH + fprintf(stderr, "raise_callback: max_fd = %d\n", max_fd); + #endif + for (i = 0; i < ARRAY_count(watch_callback); i++) + { + // We copy the callback structure, because the watch_callback array can change during the + // execution of the callbacks. + + if (watch_callback[i].fd < 0) + continue; + + wcb = watch_callback[i]; + + #if DEBUG_WATCH + fprintf(stderr, "raise_callback: [%d] fd = %d read = %p (%p) write = %p (%p)\n", i, wcb.fd, wcb.callback_read, (void *)wcb.param_read, wcb.callback_write, (void *)wcb.param_write); + #endif + + if (FD_ISSET(wcb.fd, rfd)) + { + FD_CLR(wcb.fd, rfd); + if (wcb.callback_read) + { + #ifdef DEBUG_WATCH + fprintf(stderr, "call read callback on fd %d\n", wcb.fd); + #endif + (*(wcb.callback_read))(wcb.fd, GB_WATCH_READ, wcb.param_read); + } + } + + if (watch_callback[i].fd < 0) + continue; + + if (FD_ISSET(wcb.fd, wfd)) + { + FD_CLR(wcb.fd, wfd); + if (wcb.callback_write) + { + #ifdef DEBUG_WATCH + fprintf(stderr, "call write callback on fd %d\n", wcb.fd); + #endif + (*(wcb.callback_write))(wcb.fd, GB_WATCH_WRITE, wcb.param_write); + } + } + } + + _do_not_really_delete_callback--; + + if (!_do_not_really_delete_callback && _must_delete_callback) + { + #if DEBUG_WATCH + fprintf(stderr, "do must delete callback\n"); + #endif + i = 0; + while (i < ARRAY_count(watch_callback)) + { + if (watch_callback[i].fd < 0) + { + ARRAY_remove(&watch_callback, i); + } + else + i++; + } + _must_delete_callback = FALSE; + } +} + + +static int do_select(fd_set *rfd, fd_set *wfd, struct timeval *timeout) +{ + int fd; + + for (fd = max_fd; fd >= 0; fd--) + { + if (FD_ISSET(fd, &read_fd) || FD_ISSET(fd, &write_fd)) + break; + } + + if (fd < 0 && !timeout) + return 0; + + max_fd = fd; + + *rfd = read_fd; + *wfd = write_fd; + + return select(max_fd + 1, rfd, wfd, NULL, timeout); +} + +static bool do_loop(struct timeval *wait) +{ + int ret; + struct timeval tv; + fd_set rfd, wfd; + bool something_done = FALSE; + + if (EVENT_check_post()) + something_done = TRUE; + + #ifdef DEBUG_TIMER + fprintf(stderr, "\ndo_loop: now = %.7g: select (%d)\n", time_to_double(time_now()), something_done); + #endif + + if (get_timeout(wait, &tv)) + ret = do_select(&rfd, &wfd, NULL); + else + { + ret = do_select(&rfd, &wfd, &tv); + something_done = ARRAY_count(_timers) > 0; + } + + if (ret < 0 && errno != EINTR) + THROW_SYSTEM(errno, NULL); + + #ifdef DEBUG_TIMER + fprintf(stderr, "do_loop: now = %.7g: timers (%d)\n", time_to_double(time_now()), something_done); + #endif + + raise_timers(); + + #ifdef DEBUG_TIMER + fprintf(stderr, "do_loop: now = %.7g: callbacks\n", time_to_double(time_now())); + #endif + + if (ret > 0) + { + raise_callback(&rfd, &wfd); + something_done = TRUE; + } + else if (ret < 0) + { + something_done = TRUE; + } + + //if (EVENT_check_post()) + // something_done = TRUE; + + return something_done; +} + + +bool WATCH_one_loop(int wait) +{ + struct timeval timeout; + + if (wait == 0) + return do_loop(NULL); + else + { + time_from_ms(&timeout, wait); + return do_loop(&timeout); + } +} + +void WATCH_loop(void) +{ + while (do_loop(NULL)); +} + + +void WATCH_wait(int wait) +{ + struct timeval *now; + struct timeval timeout; + + if (wait == 0) + { + timeout.tv_sec = 0; + timeout.tv_usec = 0; + do_loop(&timeout); + } + else + { + now = time_now(); + time_from_ms(&timeout, wait); + time_add(&timeout, now); + + for(;;) + { + now = time_now(); + if (time_lower_than(&timeout, now)) + break; + do_loop(&timeout); + } + } +} + +int WATCH_process(int fd_end, int fd_output, int fd_error, int timeout) +{ + fd_set rfd; + int ret, fd_max; + struct timeval tv; + + fd_max = fd_end > fd_output ? fd_end : fd_output; + fd_max = fd_error > fd_max ? fd_error : fd_max; + + for(;;) + { + FD_ZERO(&rfd); + FD_SET(fd_end, &rfd); + if (fd_output >= 0) + FD_SET(fd_output, &rfd); + if (fd_error >= 0) + FD_SET(fd_error, &rfd); + + if (timeout > 0) + { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + ret = select(fd_max + 1, &rfd, NULL, NULL, &tv); + if (ret == 0) + break; + } + else + { + ret = select(fd_max + 1, &rfd, NULL, NULL, NULL); + } + + //fprintf(stderr, "WATCH_process: select -> %d [%d] / fd_end -> %d\n", ret, errno, FD_ISSET(fd_end, &rfd)); + + if (ret > 0) + break; + if (errno != EINTR) + break; + } + + ret = timeout > 0 ? WP_TIMEOUT : WP_NOTHING; + + if (FD_ISSET(fd_end, &rfd)) ret += WP_END; + if (fd_output >= 0 && FD_ISSET(fd_output, &rfd)) ret += WP_OUTPUT; + if (fd_error >= 0 && FD_ISSET(fd_error, &rfd)) ret += WP_ERROR; + return ret; +} + + +void WATCH_timer(void *t, int on) +{ + GB_TIMER *timer = (GB_TIMER *)t; + struct timeval timeout; + + if (on) + { + time_from_ms(&timeout, timer->delay); + time_add(&timeout, time_now()); + add_timer(timer, &timeout); + timer->id = (intptr_t)timer; + } + else + { + remove_timer(timer); + timer->id = 0; + } +} + +void WATCH_little_sleep(void) +{ + struct timeval tv; + struct timeval *now; + + now = time_now(); + tv = *now; + time_sub(&tv, &_last_time); + if (tv.tv_sec == 0 && tv.tv_usec < 10000) + usleep(10000 - tv.tv_usec); + _last_time = *now; +} + diff --git a/main/gbx/gbx_watch.h b/main/gbx/gbx_watch.h new file mode 100644 index 00000000..f9e4e82f --- /dev/null +++ b/main/gbx/gbx_watch.h @@ -0,0 +1,73 @@ +/*************************************************************************** + + gbx_watch.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_WATCH_H +#define __GBX_WATCH_H + +#include "gambas.h" +#include +#include + +#ifdef OS_OPENBSD +#include +#endif + +enum { + WP_NOTHING = 0, + WP_END = 1, + WP_OUTPUT = 2, + WP_ERROR = 4, + WP_TIMEOUT = 8 +}; + +typedef + struct { + int fd; + void (*callback_read)(); + void (*callback_write)(); + intptr_t param_read; + intptr_t param_write; + } + WATCH_CALLBACK; + +typedef + struct { + GB_TIMER *timer; + struct timeval timeout; + } + WATCH_TIMER; + +void WATCH_init(void); +void WATCH_exit(void); + +void WATCH_watch(int fd, int flag, void *callback, intptr_t param); +bool WATCH_one_loop(int); +void WATCH_loop(void); +void WATCH_wait(int); +int WATCH_loop_signal(const sigset_t *sig); +int WATCH_process(int fd_end, int fd_output, int fd_error, int timeout); +void WATCH_timer(void *t, int on); +double WATCH_get_timeout(GB_TIMER *timer); +void WATCH_little_sleep(void); + +#endif /* */ diff --git a/main/lib/Makefile.am b/main/lib/Makefile.am new file mode 100644 index 00000000..7cca1d08 --- /dev/null +++ b/main/lib/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = debug eval db compress vb option geom draw image gui gui.opengl gui.qt gui.qt.webkit gui.qt.opengl image.effect signal term complex data clipper gui.trayicon @INOTIFY_DIR@ jit +EXTRA_DIST = gb.component diff --git a/main/lib/clipper/LICENSE b/main/lib/clipper/LICENSE new file mode 100644 index 00000000..3793cdd9 --- /dev/null +++ b/main/lib/clipper/LICENSE @@ -0,0 +1,26 @@ +The Clipper Library (including Delphi, C++ & C# source code, other accompanying +code, examples and documentation), hereafter called "the Software", has been +released under the following license, terms and conditions: + +Boost Software License - Version 1.0 - August 17th, 2003 +http://www.boost.org/LICENSE_1_0.txt + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the Software covered by this license to use, reproduce, +display, distribute, execute, and transmit the Software, and to prepare +derivative works of the Software, and to permit third-parties to whom the +Software is furnished to do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including the +above license grant, this restriction and the following disclaimer, must be +included in all copies of the Software, in whole or in part, and all derivative +works of the Software, unless such copies or derivative works are solely in the +form of machine-executable object code generated by a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL +THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY +DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/main/lib/clipper/Makefile.am b/main/lib/clipper/Makefile.am new file mode 100644 index 00000000..5a0b6e1c --- /dev/null +++ b/main/lib/clipper/Makefile.am @@ -0,0 +1,19 @@ +COMPONENT = gb.clipper +include $(top_srcdir)/component.am + +noinst_LTLIBRARIES = libclipper.la +gblib_LTLIBRARIES = gb.clipper.la + +libclipper_la_LIBADD = +libclipper_la_LDFLAGS = -module @LD_FLAGS@ +libclipper_la_CXXFLAGS = -I$(top_srcdir)/share $(AM_CXXFLAGS_OPT) -fexceptions + +libclipper_la_SOURCES = \ + clipper.hpp clipper.cpp + +gb_clipper_la_LIBADD = libclipper.la +gb_clipper_la_LDFLAGS = -module @LD_FLAGS@ +gb_clipper_la_CXXFLAGS = -I$(top_srcdir)/share $(AM_CXXFLAGS) + +gb_clipper_la_SOURCES = \ + main.h main.cpp gb.geom.h c_clipper.cpp c_clipper.h diff --git a/main/lib/clipper/c_clipper.cpp b/main/lib/clipper/c_clipper.cpp new file mode 100644 index 00000000..cea5cb93 --- /dev/null +++ b/main/lib/clipper/c_clipper.cpp @@ -0,0 +1,485 @@ +/*************************************************************************** + + c_clipper.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_CLIPPER_CPP + +#include "main.h" +#include "gb.geom.h" +#include "c_clipper.h" + +#define SCALE 1048576.0 + +static IntPoint to_point_xy(double x, double y) +{ + return IntPoint(x * SCALE + 0.5, y * SCALE + 0.5); +} + +static IntPoint to_point(GEOM_POINTF *point) +{ + return to_point_xy(point->x, point->y); +} + +static GEOM_POINTF *from_point(IntPoint p) +{ + return GEOM.CreatePointF((double)p.X / SCALE, (double)p.Y / SCALE); +} + +static bool is_polygon_closed(Path &p) +{ + int n = p.size() - 1; + + if (n <= 1) + return false; + + return p[0].X == p[n].X && p[0].Y == p[n].Y; +} + +static void set_polygon_closed(Path &p, bool closed) +{ + if (is_polygon_closed(p) == closed) + return; + + if (closed) + p.push_back(p[0]); + else + p.erase(p.begin() + p.size() - 1); +} + +static bool to_polygons(Paths &polygons, GB_ARRAY array) +{ + int count; + CPOLYGON *p; + int i; + + if (GB.CheckObject(array)) + return true; + + count = GB.Array.Count(array); + if (count == 0) + return false; + + polygons.clear(); + + for(i = 0; i < count; i++) + { + p = *(CPOLYGON **)GB.Array.Get(array, i); + if (!p) + continue; + + polygons.push_back(*(p->poly)); + } + + return false; +} + +static GB_ARRAY from_polygons(Paths &polygons, bool closed) +{ + GB_ARRAY a; + CPOLYGON *p; + uint i; + + GB.Array.New(&a, GB.FindClass("Polygon"), polygons.size()); + + for (i = 0; i < polygons.size(); i++) + { + if (polygons[i].size() == 0) + continue; + + set_polygon_closed(polygons[i], closed); + + p = (CPOLYGON *)GB.New(GB.FindClass("Polygon"), NULL, NULL); + *(p->poly) = polygons[i]; + + *(GB_ARRAY *)GB.Array.Get(a, i) = p; + GB.Ref(p); + } + + return a; +} + +//--------------------------------------------------------------------------- + +#define THIS ((CPOLYGON *)_object) +#define POLY THIS->poly + +static bool _convert_polygon(CPOLYGON *_object, GB_TYPE type, GB_VALUE *conv) +{ + if (type != GB.FindClass("PointF[]")) + return true; + + if (THIS) + { + // Polygon --> PointF[] + GB_ARRAY a; + int i; + GEOM_POINTF **data; + + GB.Array.New(&a, GB.FindClass("PointF"), POLY->size()); // + THIS->closed); + data = (GEOM_POINTF **)GB.Array.Get(a, 0); + for(i = 0; i < (int)POLY->size(); i++) + { + data[i] = from_point((*POLY)[i]); + GB.Ref(data[i]); + } + + /*if (closed) + { + data[i] = from_point((*POLY)[0]); + GB.Ref(data[i]); + }*/ + + conv->_object.value = a; + return false; + } + else + { + // PointF[] --> Polygon + CPOLYGON *p; + GB_ARRAY a = (GB_ARRAY)conv->_object.value; + int size = GB.Array.Count(a); + int i; + GEOM_POINTF **points; + + p = (CPOLYGON *)GB.New(GB.FindClass("Polygon"), NULL, NULL); + + points = (GEOM_POINTF **)GB.Array.Get(a, 0); + for (i = 0; i < size; i++) + { + if (!points[i]) + continue; + + p->poly->push_back(to_point(points[i])); + } + + conv->_object.value = p; + return false; + } +} + +BEGIN_METHOD(Polygon_new, GB_INTEGER size) + + POLY = new Path; + + if (!MISSING(size)) + POLY->resize(VARG(size)); + +END_METHOD + +BEGIN_METHOD_VOID(Polygon_free) + + delete POLY; + +END_METHOD + +BEGIN_METHOD(Polygon_get, GB_INTEGER index) + + int index = VARG(index); + + if (index < 0 || index >= (int)POLY->size()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + GB.ReturnObject(from_point((*POLY)[index])); + +END_METHOD + +BEGIN_METHOD(Polygon_put, GB_OBJECT point; GB_INTEGER index) + + int index = VARG(index); + GEOM_POINTF *point = (GEOM_POINTF *)VARG(point); + + if (GB.CheckObject(point)) + return; + + if (index < 0 || index >= (int)POLY->size()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + (*POLY)[index] = to_point(point); + +END_METHOD + +BEGIN_PROPERTY(Polygon_Count) + + GB.ReturnInteger(POLY->size()); + +END_PROPERTY + +BEGIN_PROPERTY(Polygon_Max) + + GB.ReturnInteger(POLY->size() - 1); + +END_PROPERTY + +BEGIN_PROPERTY(Polygon_Area) + + GB.ReturnFloat(Area(*POLY) / SCALE / SCALE); + +END_PROPERTY + +BEGIN_METHOD_VOID(Polygon_Reverse) + + ReversePath(*POLY); + +END_METHOD + +BEGIN_METHOD(Polygon_Simplify, GB_INTEGER fill) + + Paths result; + + SimplifyPolygon(*POLY, result, (PolyFillType)VARGOPT(fill, pftNonZero)); + + GB.ReturnObject(from_polygons(result, is_polygon_closed(*POLY))); + +END_METHOD + +BEGIN_METHOD(Polygon_Clean, GB_FLOAT distance) + + bool closed; + CPOLYGON *result = (CPOLYGON *)GB.New(GB.FindClass("Polygon"), NULL, 0); + + result->poly->resize(POLY->size()); + + closed = is_polygon_closed(*POLY); + + CleanPolygon(*POLY, *(result->poly), VARGOPT(distance, 1.415)); + + set_polygon_closed(*(result->poly), closed); + + GB.ReturnObject(result); + +END_METHOD + +BEGIN_METHOD(Polygon_Add, GB_FLOAT x; GB_FLOAT y) + + POLY->push_back(to_point_xy(VARG(x), VARG(y))); + +END_METHOD + +BEGIN_METHOD(Polygon_AddPoint, GB_OBJECT point) + + GEOM_POINTF *point = (GEOM_POINTF *)VARG(point); + + if (GB.CheckObject(point)) + return; + + POLY->push_back(to_point(point)); + +END_METHOD + +BEGIN_METHOD(Polygon_Remove, GB_INTEGER index; GB_INTEGER count) + + int index = VARG(index); + int count = VARGOPT(count, 1); + int index2; + + if (index < 0 || index >= (int)POLY->size()) + { + GB.Error(GB_ERR_BOUND); + return; + } + + if (count < 0) + count = (int)POLY->size() - index; + + index2 = index + count; + + if (index2 > (int)POLY->size()) + index2 = POLY->size(); + + if (count == 1) + POLY->erase(POLY->begin() + index); + else + POLY->erase(POLY->begin() + index, POLY->begin() + index2); + +END_METHOD + +BEGIN_PROPERTY(Polygon_Orientation) + + GB.ReturnBoolean(Orientation(*POLY)); + +END_PROPERTY + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Clipper_Offset, GB_OBJECT polygons; GB_FLOAT delta; GB_INTEGER join; GB_FLOAT limit; GB_BOOLEAN do_not_fix) + + Paths polygons; + Paths result; + + if (to_polygons(polygons, VARG(polygons))) + return; + + SimplifyPolygons(polygons, result, pftNonZero); + polygons = result; + + ClipperOffset co; + co.AddPaths(polygons, (JoinType)VARGOPT(join, jtSquare), etClosedPolygon); + co.MiterLimit = VARGOPT(limit, 0.0); + co.Execute(result, VARG(delta) * SCALE); + + //OffsetPaths(polygons, result, VARG(delta) * SCALE, (JoinType)VARGOPT(join, jtSquare), VARGOPT(limit, 0.0), !VARGOPT(do_not_fix, false)); + + GB.ReturnObject(from_polygons(result, true)); + +END_METHOD + + +BEGIN_METHOD(Clipper_Simplify, GB_OBJECT polygons; GB_INTEGER fill) + + Paths polygons; + Paths result; + + if (to_polygons(polygons, VARG(polygons))) + return; + + SimplifyPolygons(polygons, result, (PolyFillType)VARGOPT(fill, pftNonZero)); + + GB.ReturnObject(from_polygons(result, true)); + +END_METHOD + +BEGIN_METHOD(Clipper_Clean, GB_OBJECT polygons; GB_FLOAT distance) + + Paths polygons; + Paths result; + + if (to_polygons(polygons, VARG(polygons))) + return; + + result.resize(polygons.size()); + + CleanPolygons(polygons, result, VARGOPT(distance, 1.415)); + + GB.ReturnObject(from_polygons(result, true)); + +END_METHOD + +static void execute(ClipType action, PolyFillType fill, void *subject, void *clip) +{ + Clipper c; + Paths psubject, pclip, result; + PolyTree tree; + + if (to_polygons(psubject, subject)) + return; + + if (clip && to_polygons(pclip, clip)) + return; + + c.AddPaths(psubject, ptSubject, true); + if (clip) + c.AddPaths(pclip, ptClip, true); + + c.StrictlySimple(true); + c.Execute(action, tree, fill, fill); + ClosedPathsFromPolyTree(tree, result); + + GB.ReturnObject(from_polygons(result, true)); +} + +BEGIN_METHOD(Clipper_Union, GB_OBJECT subject; GB_OBJECT clip; GB_INTEGER fill) + + execute(ctUnion, (PolyFillType)VARGOPT(fill, pftNonZero), VARG(subject), VARGOPT(clip, NULL)); + +END_METHOD + +BEGIN_METHOD(Clipper_Intersection, GB_OBJECT subject; GB_OBJECT clip; GB_INTEGER fill) + + execute(ctIntersection, (PolyFillType)VARGOPT(fill, pftNonZero), VARG(subject), VARGOPT(clip, NULL)); + +END_METHOD + +BEGIN_METHOD(Clipper_Difference, GB_OBJECT subject; GB_OBJECT clip; GB_INTEGER fill) + + execute(ctDifference, (PolyFillType)VARGOPT(fill, pftNonZero), VARG(subject), VARGOPT(clip, NULL)); + +END_METHOD + +BEGIN_METHOD(Clipper_ExclusiveOr, GB_OBJECT subject; GB_OBJECT clip; GB_INTEGER fill) + + execute(ctXor, (PolyFillType)VARGOPT(fill, pftNonZero), VARG(subject), VARGOPT(clip, NULL)); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC PolygonDesc[] = +{ + GB_DECLARE("Polygon", sizeof(CPOLYGON)), + + GB_METHOD("_new", NULL, Polygon_new, "[(Size)i]"), + GB_METHOD("_free", NULL, Polygon_free, NULL), + + GB_METHOD("_get", "PointF", Polygon_get, "(Index)i"), + GB_METHOD("_put", NULL, Polygon_put, "(Point)PointF;(Index)i"), + + GB_PROPERTY_READ("Count", "i", Polygon_Count), + GB_PROPERTY_READ("Max", "i", Polygon_Max), + GB_PROPERTY_READ("Area", "f", Polygon_Area), + GB_PROPERTY_READ("Orientation", "b", Polygon_Orientation), + + GB_METHOD("Reverse", NULL, Polygon_Reverse, NULL), + GB_METHOD("Simplify", "Polygon[]", Polygon_Simplify, "[(Fill)i]"), + GB_METHOD("Clean", "Polygon", Polygon_Clean, "[(Distance)f]"), + + GB_METHOD("Add", NULL, Polygon_Add, "(X)f(Y)f"), + GB_METHOD("AddPoint", NULL, Polygon_AddPoint, "(Point)PointF;"), + GB_METHOD("Remove", NULL, Polygon_Remove, "(Index)i[(Count)i]"), + + GB_INTERFACE("_convert", &_convert_polygon), + + GB_END_DECLARE +}; + +GB_DESC ClipperDesc[] = +{ + GB_DECLARE_VIRTUAL("Clipper"), + + GB_CONSTANT("JoinMiter", "i", jtMiter), + GB_CONSTANT("JoinSquare", "i", jtSquare), + GB_CONSTANT("JoinRound", "i", jtRound), + + GB_CONSTANT("FillEvenOdd", "i", pftEvenOdd), + GB_CONSTANT("FillWinding", "i", pftNonZero), + GB_CONSTANT("FillNonZero", "i", pftNonZero), + GB_CONSTANT("FillPositive", "i", pftPositive), + GB_CONSTANT("FillNegative", "i", pftNegative), + + GB_STATIC_METHOD("Offset", "Polygon[]", Clipper_Offset, "(Polygons)Polygon[];(Delta)f[(Join)i(Limit)f(DoNotFix)b]"), + GB_STATIC_METHOD("Simplify", "Polygon[]", Clipper_Simplify, "(Polygons)Polygon[];[(Fill)i]"), + GB_STATIC_METHOD("Clean", "Polygon[]", Clipper_Clean, "(Polygons)Polygon[];[(Distance)f]"), + + GB_STATIC_METHOD("Union", "Polygon[]", Clipper_Union, "(Polygons)Polygon[];[(Clip)Polygon[];(Fill)i]"), + GB_STATIC_METHOD("Intersection", "Polygon[]", Clipper_Intersection, "(Polygons)Polygon[];[(Clip)Polygon[];(Fill)i]"), + GB_STATIC_METHOD("Difference", "Polygon[]", Clipper_Difference, "(Polygons)Polygon[];[(Clip)Polygon[];(Fill)i]"), + GB_STATIC_METHOD("ExclusiveOr", "Polygon[]", Clipper_ExclusiveOr, "(Polygons)Polygon[];[(Clip)Polygon[];(Fill)i]"), + + GB_END_DECLARE +}; + + diff --git a/main/lib/clipper/c_clipper.h b/main/lib/clipper/c_clipper.h new file mode 100644 index 00000000..da3d3a38 --- /dev/null +++ b/main/lib/clipper/c_clipper.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + c_clipper.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_CLIPPER_H +#define __C_CLIPPER_H + +#include "gambas.h" +#include "clipper.hpp" + +using namespace ClipperLib; + +#ifndef __C_CLIPPER_CPP + +extern GB_DESC PolygonDesc[]; +extern GB_DESC ClipperDesc[]; + +#endif + +typedef + struct { + GB_BASE ob; + Path *poly; + //bool closed; + } + CPOLYGON; + + +#endif diff --git a/main/lib/clipper/clipper.cpp b/main/lib/clipper/clipper.cpp new file mode 100644 index 00000000..ce1f3661 --- /dev/null +++ b/main/lib/clipper/clipper.cpp @@ -0,0 +1,4610 @@ +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 6.1.3a * +* Date : 22 January 2014 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2014 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +/******************************************************************************* +* * +* This is a translation of the Delphi Clipper library and the naming style * +* used has retained a Delphi flavour. * +* * +*******************************************************************************/ + +#include "clipper.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ClipperLib { + +#ifdef use_int32 + static cInt const loRange = 46340; + static cInt const hiRange = 46340; +#else + static cInt const loRange = 0x3FFFFFFF; + static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; + typedef unsigned long long ulong64; +#endif + +static double const pi = 3.141592653589793238; +static double const two_pi = pi *2; +static double const def_arc_tolerance = 0.25; + +enum Direction { dRightToLeft, dLeftToRight }; + +static int const Unassigned = -1; //edge not currently 'owning' a solution +static int const Skip = -2; //edge that would otherwise close a path + +#define HORIZONTAL (-1.0E+40) +#define TOLERANCE (1.0e-20) +#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) + +struct TEdge { + IntPoint Bot; + IntPoint Curr; + IntPoint Top; + IntPoint Delta; + double Dx; + PolyType PolyTyp; + EdgeSide Side; + int WindDelta; //1 or -1 depending on winding direction + int WindCnt; + int WindCnt2; //winding count of the opposite polytype + int OutIdx; + TEdge *Next; + TEdge *Prev; + TEdge *NextInLML; + TEdge *NextInAEL; + TEdge *PrevInAEL; + TEdge *NextInSEL; + TEdge *PrevInSEL; +}; + +struct IntersectNode { + TEdge *Edge1; + TEdge *Edge2; + IntPoint Pt; +}; + +struct LocalMinima { + cInt Y; + TEdge *LeftBound; + TEdge *RightBound; + LocalMinima *Next; +}; + +struct OutPt; + +struct OutRec { + int Idx; + bool IsHole; + bool IsOpen; + OutRec *FirstLeft; //see comments in clipper.pas + PolyNode *PolyNd; + OutPt *Pts; + OutPt *BottomPt; +}; + +struct OutPt { + int Idx; + IntPoint Pt; + OutPt *Next; + OutPt *Prev; +}; + +struct Join { + OutPt *OutPt1; + OutPt *OutPt2; + IntPoint OffPt; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +inline cInt Round(double val) +{ + if ((val < 0)) return static_cast(val - 0.5); + else return static_cast(val + 0.5); +} +//------------------------------------------------------------------------------ + +inline cInt Abs(cInt val) +{ + return val < 0 ? -val : val; +} + +//------------------------------------------------------------------------------ +// PolyTree methods ... +//------------------------------------------------------------------------------ + +void PolyTree::Clear() +{ + for (PolyNodes::size_type i = 0; i < AllNodes.size(); ++i) + delete AllNodes[i]; + AllNodes.resize(0); + Childs.resize(0); +} +//------------------------------------------------------------------------------ + +PolyNode* PolyTree::GetFirst() const +{ + if (!Childs.empty()) + return Childs[0]; + else + return 0; +} +//------------------------------------------------------------------------------ + +int PolyTree::Total() const +{ + return (int)AllNodes.size(); +} + +//------------------------------------------------------------------------------ +// PolyNode methods ... +//------------------------------------------------------------------------------ + +PolyNode::PolyNode(): Childs(), Parent(0), Index(0), m_IsOpen(false) +{ +} +//------------------------------------------------------------------------------ + +int PolyNode::ChildCount() const +{ + return (int)Childs.size(); +} +//------------------------------------------------------------------------------ + +void PolyNode::AddChild(PolyNode& child) +{ + unsigned cnt = (unsigned)Childs.size(); + Childs.push_back(&child); + child.Parent = this; + child.Index = cnt; +} +//------------------------------------------------------------------------------ + +PolyNode* PolyNode::GetNext() const +{ + if (!Childs.empty()) + return Childs[0]; + else + return GetNextSiblingUp(); +} +//------------------------------------------------------------------------------ + +PolyNode* PolyNode::GetNextSiblingUp() const +{ + if (!Parent) //protects against PolyTree.GetNextSiblingUp() + return 0; + else if (Index == Parent->Childs.size() - 1) + return Parent->GetNextSiblingUp(); + else + return Parent->Childs[Index + 1]; +} +//------------------------------------------------------------------------------ + +bool PolyNode::IsHole() const +{ + bool result = true; + PolyNode* node = Parent; + while (node) + { + result = !result; + node = node->Parent; + } + return result; +} +//------------------------------------------------------------------------------ + +bool PolyNode::IsOpen() const +{ + return m_IsOpen; +} +//------------------------------------------------------------------------------ + +#ifndef use_int32 + +//------------------------------------------------------------------------------ +// Int128 class (enables safe math on signed 64bit integers) +// eg Int128 val1((cInt)9223372036854775807); //ie 2^63 -1 +// Int128 val2((cInt)9223372036854775807); +// Int128 val3 = val1 * val2; +// val3.AsString => "85070591730234615847396907784232501249" (8.5e+37) +//------------------------------------------------------------------------------ + +class Int128 +{ + public: + + cUInt lo; + cInt hi; + + Int128(cInt _lo = 0) + { + lo = (cUInt)_lo; + if (_lo < 0) hi = -1; else hi = 0; + } + + + Int128(const Int128 &val): lo(val.lo), hi(val.hi){} + + Int128(const cInt& _hi, const ulong64& _lo): lo(_lo), hi(_hi){} + + Int128& operator = (const cInt &val) + { + lo = (ulong64)val; + if (val < 0) hi = -1; else hi = 0; + return *this; + } + + bool operator == (const Int128 &val) const + {return (hi == val.hi && lo == val.lo);} + + bool operator != (const Int128 &val) const + { return !(*this == val);} + + bool operator > (const Int128 &val) const + { + if (hi != val.hi) + return hi > val.hi; + else + return lo > val.lo; + } + + bool operator < (const Int128 &val) const + { + if (hi != val.hi) + return hi < val.hi; + else + return lo < val.lo; + } + + bool operator >= (const Int128 &val) const + { return !(*this < val);} + + bool operator <= (const Int128 &val) const + { return !(*this > val);} + + Int128& operator += (const Int128 &rhs) + { + hi += rhs.hi; + lo += rhs.lo; + if (lo < rhs.lo) hi++; + return *this; + } + + Int128 operator + (const Int128 &rhs) const + { + Int128 result(*this); + result+= rhs; + return result; + } + + Int128& operator -= (const Int128 &rhs) + { + *this += -rhs; + return *this; + } + + Int128 operator - (const Int128 &rhs) const + { + Int128 result(*this); + result -= rhs; + return result; + } + + Int128 operator-() const //unary negation + { + if (lo == 0) + return Int128(-hi,0); + else + return Int128(~hi,~lo +1); + } + + Int128 operator/ (const Int128 &rhs) const + { + if (rhs.lo == 0 && rhs.hi == 0) + throw "Int128 operator/: divide by zero"; + + bool negate = (rhs.hi < 0) != (hi < 0); + Int128 dividend = *this; + Int128 divisor = rhs; + if (dividend.hi < 0) dividend = -dividend; + if (divisor.hi < 0) divisor = -divisor; + + if (divisor < dividend) + { + Int128 result = Int128(0); + Int128 cntr = Int128(1); + while (divisor.hi >= 0 && !(divisor > dividend)) + { + divisor.hi <<= 1; + if ((cInt)divisor.lo < 0) divisor.hi++; + divisor.lo <<= 1; + + cntr.hi <<= 1; + if ((cInt)cntr.lo < 0) cntr.hi++; + cntr.lo <<= 1; + } + divisor.lo >>= 1; + if ((divisor.hi & 1) == 1) + divisor.lo |= 0x8000000000000000LL; + divisor.hi = (ulong64)divisor.hi >> 1; + + cntr.lo >>= 1; + if ((cntr.hi & 1) == 1) + cntr.lo |= 0x8000000000000000LL; + cntr.hi >>= 1; + + while (cntr.hi != 0 || cntr.lo != 0) + { + if (!(dividend < divisor)) + { + dividend -= divisor; + result.hi |= cntr.hi; + result.lo |= cntr.lo; + } + divisor.lo >>= 1; + if ((divisor.hi & 1) == 1) + divisor.lo |= 0x8000000000000000LL; + divisor.hi >>= 1; + + cntr.lo >>= 1; + if ((cntr.hi & 1) == 1) + cntr.lo |= 0x8000000000000000LL; + cntr.hi >>= 1; + } + if (negate) result = -result; + return result; + } + else if (rhs.hi == this->hi && rhs.lo == this->lo) + return Int128(negate ? -1: 1); + else + return Int128(0); + } + + double AsDouble() const + { + const double shift64 = 18446744073709551616.0; //2^64 + if (hi < 0) + { + cUInt lo_ = ~lo + 1; + if (lo_ == 0) return (double)hi * shift64; + else return -(double)(lo_ + ~hi * shift64); + } + else + return (double)(lo + hi * shift64); + } + +}; +//------------------------------------------------------------------------------ + +Int128 Int128Mul (cInt lhs, cInt rhs) +{ + bool negate = (lhs < 0) != (rhs < 0); + + if (lhs < 0) lhs = -lhs; + ulong64 int1Hi = ulong64(lhs) >> 32; + ulong64 int1Lo = ulong64(lhs & 0xFFFFFFFF); + + if (rhs < 0) rhs = -rhs; + ulong64 int2Hi = ulong64(rhs) >> 32; + ulong64 int2Lo = ulong64(rhs & 0xFFFFFFFF); + + //nb: see comments in clipper.pas + ulong64 a = int1Hi * int2Hi; + ulong64 b = int1Lo * int2Lo; + ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; + + Int128 tmp; + tmp.hi = cInt(a + (c >> 32)); + tmp.lo = cInt(c << 32); + tmp.lo += cInt(b); + if (tmp.lo < b) tmp.hi++; + if (negate) tmp = -tmp; + return tmp; +}; +#endif + +//------------------------------------------------------------------------------ +// Miscellaneous global functions +//------------------------------------------------------------------------------ + +bool Orientation(const Path &poly) +{ + return Area(poly) >= 0; +} +//------------------------------------------------------------------------------ + +double Area(const Path &poly) +{ + int size = (int)poly.size(); + if (size < 3) return 0; + + double a = 0; + for (int i = 0, j = size -1; i < size; ++i) + { + a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y); + j = i; + } + return -a * 0.5; +} +//------------------------------------------------------------------------------ + +double Area(const OutRec &outRec) +{ + OutPt *op = outRec.Pts; + if (!op) return 0; + double a = 0; + do { + a += (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y); + op = op->Next; + } while (op != outRec.Pts); + return a * 0.5; +} +//------------------------------------------------------------------------------ + +bool PointIsVertex(const IntPoint &Pt, OutPt *pp) +{ + OutPt *pp2 = pp; + do + { + if (pp2->Pt == Pt) return true; + pp2 = pp2->Next; + } + while (pp2 != pp); + return false; +} +//------------------------------------------------------------------------------ + +int PointInPolygon (const IntPoint &pt, const Path &path) +{ + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf + int result = 0; + size_t cnt = path.size(); + if (cnt < 3) return 0; + IntPoint ip = path[0]; + for(size_t i = 1; i <= cnt; ++i) + { + IntPoint ipNext = (i == cnt ? path[0] : path[i]); + if (ipNext.Y == pt.Y) + { + if ((ipNext.X == pt.X) || (ip.Y == pt.Y && + ((ipNext.X > pt.X) == (ip.X < pt.X)))) return -1; + } + if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y)) + { + if (ip.X >= pt.X) + { + if (ipNext.X > pt.X) result = 1 - result; + else + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - + (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } else + { + if (ipNext.X > pt.X) + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - + (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } + } + ip = ipNext; + } + return result; +} +//------------------------------------------------------------------------------ + +int PointInPolygon (const IntPoint &pt, OutPt *op) +{ + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf + int result = 0; + OutPt* startOp = op; + for(;;) + { + if (op->Next->Pt.Y == pt.Y) + { + if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y && + ((op->Next->Pt.X > pt.X) == (op->Pt.X < pt.X)))) return -1; + } + if ((op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y)) + { + if (op->Pt.X >= pt.X) + { + if (op->Next->Pt.X > pt.X) result = 1 - result; + else + { + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - + (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; + } + } else + { + if (op->Next->Pt.X > pt.X) + { + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - + (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; + } + } + } + op = op->Next; + if (startOp == op) break; + } + return result; +} +//------------------------------------------------------------------------------ + +bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) +{ + OutPt* op = OutPt1; + do + { + int res = PointInPolygon(op->Pt, OutPt2); + if (res >= 0) return res != 0; + op = op->Next; + } + while (op != OutPt1); + return true; +} +//---------------------------------------------------------------------- + +bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(e1.Delta.Y, e2.Delta.X) == Int128Mul(e1.Delta.X, e2.Delta.Y); + else +#endif + return e1.Delta.Y * e2.Delta.X == e1.Delta.X * e2.Delta.Y; +} +//------------------------------------------------------------------------------ + +bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, + const IntPoint pt3, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(pt1.Y-pt2.Y, pt2.X-pt3.X) == Int128Mul(pt1.X-pt2.X, pt2.Y-pt3.Y); + else +#endif + return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y); +} +//------------------------------------------------------------------------------ + +bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, + const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(pt1.Y-pt2.Y, pt3.X-pt4.X) == Int128Mul(pt1.X-pt2.X, pt3.Y-pt4.Y); + else +#endif + return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y); +} +//------------------------------------------------------------------------------ + +inline bool IsHorizontal(TEdge &e) +{ + return e.Delta.Y == 0; +} +//------------------------------------------------------------------------------ + +inline double GetDx(const IntPoint pt1, const IntPoint pt2) +{ + return (pt1.Y == pt2.Y) ? + HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y); +} +//--------------------------------------------------------------------------- + +inline void SetDx(TEdge &e) +{ + e.Delta.X = (e.Top.X - e.Bot.X); + e.Delta.Y = (e.Top.Y - e.Bot.Y); + + if (e.Delta.Y == 0) e.Dx = HORIZONTAL; + else e.Dx = (double)(e.Delta.X) / e.Delta.Y; +} +//--------------------------------------------------------------------------- + +inline void SwapSides(TEdge &Edge1, TEdge &Edge2) +{ + EdgeSide Side = Edge1.Side; + Edge1.Side = Edge2.Side; + Edge2.Side = Side; +} +//------------------------------------------------------------------------------ + +inline void SwapPolyIndexes(TEdge &Edge1, TEdge &Edge2) +{ + int OutIdx = Edge1.OutIdx; + Edge1.OutIdx = Edge2.OutIdx; + Edge2.OutIdx = OutIdx; +} +//------------------------------------------------------------------------------ + +inline cInt TopX(TEdge &edge, const cInt currentY) +{ + return ( currentY == edge.Top.Y ) ? + edge.Top.X : edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y)); +} +//------------------------------------------------------------------------------ + +bool IntersectPoint(TEdge &Edge1, TEdge &Edge2, + IntPoint &ip, bool UseFullInt64Range) +{ +#ifdef use_xyz + ip.Z = 0; +#endif + double b1, b2; + //nb: with very large coordinate values, it's possible for SlopesEqual() to + //return false but for the edge.Dx value be equal due to double precision rounding. + if (SlopesEqual(Edge1, Edge2, UseFullInt64Range) || Edge1.Dx == Edge2.Dx) + { + if (Edge2.Bot.Y > Edge1.Bot.Y) ip = Edge2.Bot; + else ip = Edge1.Bot; + return false; + } + else if (Edge1.Delta.X == 0) + { + ip.X = Edge1.Bot.X; + if (IsHorizontal(Edge2)) + ip.Y = Edge2.Bot.Y; + else + { + b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx); + ip.Y = Round(ip.X / Edge2.Dx + b2); + } + } + else if (Edge2.Delta.X == 0) + { + ip.X = Edge2.Bot.X; + if (IsHorizontal(Edge1)) + ip.Y = Edge1.Bot.Y; + else + { + b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx); + ip.Y = Round(ip.X / Edge1.Dx + b1); + } + } + else + { + b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx; + b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx; + double q = (b2-b1) / (Edge1.Dx - Edge2.Dx); + ip.Y = Round(q); + if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) + ip.X = Round(Edge1.Dx * q + b1); + else + ip.X = Round(Edge2.Dx * q + b2); + } + + if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y) + { + if (Edge1.Top.Y > Edge2.Top.Y) + ip.Y = Edge1.Top.Y; + else + ip.Y = Edge2.Top.Y; + if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) + ip.X = TopX(Edge1, ip.Y); + else + ip.X = TopX(Edge2, ip.Y); + } + return true; +} +//------------------------------------------------------------------------------ + +void ReversePolyPtLinks(OutPt *pp) +{ + if (!pp) return; + OutPt *pp1, *pp2; + pp1 = pp; + do { + pp2 = pp1->Next; + pp1->Next = pp1->Prev; + pp1->Prev = pp2; + pp1 = pp2; + } while( pp1 != pp ); +} +//------------------------------------------------------------------------------ + +void DisposeOutPts(OutPt*& pp) +{ + if (pp == 0) return; + pp->Prev->Next = 0; + while( pp ) + { + OutPt *tmpPp = pp; + pp = pp->Next; + delete tmpPp; + } +} +//------------------------------------------------------------------------------ + +inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt) +{ + std::memset(e, 0, sizeof(TEdge)); + e->Next = eNext; + e->Prev = ePrev; + e->Curr = Pt; + e->OutIdx = Unassigned; +} +//------------------------------------------------------------------------------ + +void InitEdge2(TEdge& e, PolyType Pt) +{ + if (e.Curr.Y >= e.Next->Curr.Y) + { + e.Bot = e.Curr; + e.Top = e.Next->Curr; + } else + { + e.Top = e.Curr; + e.Bot = e.Next->Curr; + } + SetDx(e); + e.PolyTyp = Pt; +} +//------------------------------------------------------------------------------ + +TEdge* RemoveEdge(TEdge* e) +{ + //removes e from double_linked_list (but without removing from memory) + e->Prev->Next = e->Next; + e->Next->Prev = e->Prev; + TEdge* result = e->Next; + e->Prev = 0; //flag as removed (see ClipperBase.Clear) + return result; +} +//------------------------------------------------------------------------------ + +inline void ReverseHorizontal(TEdge &e) +{ + //swap horizontal edges' Top and Bottom x's so they follow the natural + //progression of the bounds - ie so their xbots will align with the + //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] + cInt tmp = e.Top.X; + e.Top.X = e.Bot.X; + e.Bot.X = tmp; +#ifdef use_xyz + tmp = e.Top.Z; + e.Top.Z = e.Bot.Z; + e.Bot.Z = tmp; +#endif +} +//------------------------------------------------------------------------------ + +void SwapPoints(IntPoint &pt1, IntPoint &pt2) +{ + IntPoint tmp = pt1; + pt1 = pt2; + pt2 = tmp; +} +//------------------------------------------------------------------------------ + +bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, + IntPoint pt2b, IntPoint &pt1, IntPoint &pt2) +{ + //precondition: segments are Collinear. + if (Abs(pt1a.X - pt1b.X) > Abs(pt1a.Y - pt1b.Y)) + { + if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b); + if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b); + if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a; + if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b; + return pt1.X < pt2.X; + } else + { + if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b); + if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b); + if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a; + if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b; + return pt1.Y > pt2.Y; + } +} +//------------------------------------------------------------------------------ + +bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) +{ + OutPt *p = btmPt1->Prev; + while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Prev; + double dx1p = std::fabs(GetDx(btmPt1->Pt, p->Pt)); + p = btmPt1->Next; + while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Next; + double dx1n = std::fabs(GetDx(btmPt1->Pt, p->Pt)); + + p = btmPt2->Prev; + while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Prev; + double dx2p = std::fabs(GetDx(btmPt2->Pt, p->Pt)); + p = btmPt2->Next; + while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next; + double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt)); + return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); +} +//------------------------------------------------------------------------------ + +OutPt* GetBottomPt(OutPt *pp) +{ + OutPt* dups = 0; + OutPt* p = pp->Next; + while (p != pp) + { + if (p->Pt.Y > pp->Pt.Y) + { + pp = p; + dups = 0; + } + else if (p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X) + { + if (p->Pt.X < pp->Pt.X) + { + dups = 0; + pp = p; + } else + { + if (p->Next != pp && p->Prev != pp) dups = p; + } + } + p = p->Next; + } + if (dups) + { + //there appears to be at least 2 vertices at BottomPt so ... + while (dups != p) + { + if (!FirstIsBottomPt(p, dups)) pp = dups; + dups = dups->Next; + while (dups->Pt != pp->Pt) dups = dups->Next; + } + } + return pp; +} +//------------------------------------------------------------------------------ + +bool FindSegment(OutPt* &pp, bool UseFullInt64Range, + IntPoint &pt1, IntPoint &pt2) +{ + //OutPt1 & OutPt2 => the overlap segment (if the function returns true) + if (!pp) return false; + OutPt* pp2 = pp; + IntPoint pt1a = pt1, pt2a = pt2; + do + { + if (SlopesEqual(pt1a, pt2a, pp->Pt, pp->Prev->Pt, UseFullInt64Range) && + SlopesEqual(pt1a, pt2a, pp->Pt, UseFullInt64Range) && + GetOverlapSegment(pt1a, pt2a, pp->Pt, pp->Prev->Pt, pt1, pt2)) + return true; + pp = pp->Next; + } + while (pp != pp2); + return false; +} +//------------------------------------------------------------------------------ + +bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1, + const IntPoint pt2, const IntPoint pt3) +{ + if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) + return false; + else if (pt1.X != pt3.X) + return (pt2.X > pt1.X) == (pt2.X < pt3.X); + else + return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); +} +//------------------------------------------------------------------------------ + +OutPt* InsertPolyPtBetween(OutPt* p1, OutPt* p2, const IntPoint Pt) +{ + if (p1 == p2) throw "JoinError"; + OutPt* result = new OutPt; + result->Pt = Pt; + if (p2 == p1->Next) + { + p1->Next = result; + p2->Prev = result; + result->Next = p2; + result->Prev = p1; + } else + { + p2->Next = result; + p1->Prev = result; + result->Next = p1; + result->Prev = p2; + } + return result; +} +//------------------------------------------------------------------------------ + +bool HorzSegmentsOverlap(const IntPoint& pt1a, const IntPoint& pt1b, + const IntPoint& pt2a, const IntPoint& pt2b) +{ + //precondition: both segments are horizontal + if ((pt1a.X > pt2a.X) == (pt1a.X < pt2b.X)) return true; + else if ((pt1b.X > pt2a.X) == (pt1b.X < pt2b.X)) return true; + else if ((pt2a.X > pt1a.X) == (pt2a.X < pt1b.X)) return true; + else if ((pt2b.X > pt1a.X) == (pt2b.X < pt1b.X)) return true; + else if ((pt1a.X == pt2a.X) && (pt1b.X == pt2b.X)) return true; + else if ((pt1a.X == pt2b.X) && (pt1b.X == pt2a.X)) return true; + else return false; +} + + +//------------------------------------------------------------------------------ +// ClipperBase class methods ... +//------------------------------------------------------------------------------ + +ClipperBase::ClipperBase() //constructor +{ + m_MinimaList = 0; + m_CurrentLM = 0; + m_UseFullRange = false; +} +//------------------------------------------------------------------------------ + +ClipperBase::~ClipperBase() //destructor +{ + Clear(); +} +//------------------------------------------------------------------------------ + +void RangeTest(const IntPoint& Pt, bool& useFullRange) +{ + if (useFullRange) + { + if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) + throw "Coordinate outside allowed range"; + } + else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) + { + useFullRange = true; + RangeTest(Pt, useFullRange); + } +} +//------------------------------------------------------------------------------ + +TEdge* FindNextLocMin(TEdge* E) +{ + for (;;) + { + while (E->Bot != E->Prev->Bot || E->Curr == E->Top) E = E->Next; + if (!IsHorizontal(*E) && !IsHorizontal(*E->Prev)) break; + while (IsHorizontal(*E->Prev)) E = E->Prev; + TEdge* E2 = E; + while (IsHorizontal(*E)) E = E->Next; + if (E->Top.Y == E->Prev->Bot.Y) continue; //ie just an intermediate horz. + if (E2->Prev->Bot.X < E->Bot.X) E = E2; + break; + } + return E; +} +//------------------------------------------------------------------------------ + +TEdge* ClipperBase::ProcessBound(TEdge* E, bool IsClockwise) +{ + TEdge *EStart = E, *Result = E; + TEdge *Horz = 0; + cInt StartX; + if (IsHorizontal(*E)) + { + //it's possible for adjacent overlapping horz edges to start heading left + //before finishing right, so ... + if (IsClockwise) StartX = E->Prev->Bot.X; + else StartX = E->Next->Bot.X; + if (E->Bot.X != StartX) ReverseHorizontal(*E); + } + + if (Result->OutIdx != Skip) + { + if (IsClockwise) + { + while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip) + Result = Result->Next; + if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip) + { + //nb: at the top of a bound, horizontals are added to the bound + //only when the preceding edge attaches to the horizontal's left vertex + //unless a Skip edge is encountered when that becomes the top divide + Horz = Result; + while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; + if (Horz->Prev->Top.X == Result->Next->Top.X) + { + if (!IsClockwise) Result = Horz->Prev; + } + else if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; + } + while (E != Result) + { + E->NextInLML = E->Next; + if (IsHorizontal(*E) && E != EStart && + E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + E = E->Next; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) + ReverseHorizontal(*E); + Result = Result->Next; //move to the edge just beyond current bound + } else + { + while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip) + Result = Result->Prev; + if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip) + { + Horz = Result; + while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; + if (Horz->Next->Top.X == Result->Prev->Top.X) + { + if (!IsClockwise) Result = Horz->Next; + } + else if (Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; + } + + while (E != Result) + { + E->NextInLML = E->Prev; + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + E = E->Prev; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + Result = Result->Prev; //move to the edge just beyond current bound + } + } + + if (Result->OutIdx == Skip) + { + //if edges still remain in the current bound beyond the skip edge then + //create another LocMin and call ProcessBound once more + E = Result; + if (IsClockwise) + { + while (E->Top.Y == E->Next->Bot.Y) E = E->Next; + //don't include top horizontals when parsing a bound a second time, + //they will be contained in the opposite bound ... + while (E != Result && IsHorizontal(*E)) E = E->Prev; + } else + { + while (E->Top.Y == E->Prev->Bot.Y) E = E->Prev; + while (E != Result && IsHorizontal(*E)) E = E->Next; + } + if (E == Result) + { + if (IsClockwise) Result = E->Next; + else Result = E->Prev; + } else + { + //there are more edges in the bound beyond result starting with E + if (IsClockwise) + E = Result->Next; + else + E = Result->Prev; + LocalMinima* locMin = new LocalMinima; + locMin->Next = 0; + locMin->Y = E->Bot.Y; + locMin->LeftBound = 0; + locMin->RightBound = E; + locMin->RightBound->WindDelta = 0; + Result = ProcessBound(locMin->RightBound, IsClockwise); + InsertLocalMinima(locMin); + } + } + return Result; +} +//------------------------------------------------------------------------------ + +bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) +{ +#ifdef use_lines + if (!Closed && PolyTyp == ptClip) + throw clipperException("AddPath: Open paths must be subject."); +#else + if (!Closed) + throw clipperException("AddPath: Open paths have been disabled."); +#endif + + int highI = (int)pg.size() -1; + if (Closed) while (highI > 0 && (pg[highI] == pg[0])) --highI; + while (highI > 0 && (pg[highI] == pg[highI -1])) --highI; + if ((Closed && highI < 2) || (!Closed && highI < 1)) return false; + + //create a new edge array ... + TEdge *edges = new TEdge [highI +1]; + + bool IsFlat = true; + //1. Basic (first) edge initialization ... + try + { + edges[1].Curr = pg[1]; + RangeTest(pg[0], m_UseFullRange); + RangeTest(pg[highI], m_UseFullRange); + InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]); + InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]); + for (int i = highI - 1; i >= 1; --i) + { + RangeTest(pg[i], m_UseFullRange); + InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]); + } + } + catch(...) + { + delete [] edges; + throw; //range test fails + } + TEdge *eStart = &edges[0]; + + //2. Remove duplicate vertices, and (when closed) collinear edges ... + TEdge *E = eStart, *eLoopStop = eStart; + for (;;) + { + if ((E->Curr == E->Next->Curr)) + { + if (E == E->Next) break; + if (E == eStart) eStart = E->Next; + E = RemoveEdge(E); + eLoopStop = E; + continue; + } + if (E->Prev == E->Next) + break; //only two vertices + else if (Closed && + SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) && + (!m_PreserveCollinear || + !Pt2IsBetweenPt1AndPt3(E->Prev->Curr, E->Curr, E->Next->Curr))) + { + //Collinear edges are allowed for open paths but in closed paths + //the default is to merge adjacent collinear edges into a single edge. + //However, if the PreserveCollinear property is enabled, only overlapping + //collinear edges (ie spikes) will be removed from closed paths. + if (E == eStart) eStart = E->Next; + E = RemoveEdge(E); + E = E->Prev; + eLoopStop = E; + continue; + } + E = E->Next; + if (E == eLoopStop) break; + } + + if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next))) + { + delete [] edges; + return false; + } + + if (!Closed) + { + m_HasOpenPaths = true; + eStart->Prev->OutIdx = Skip; + } + + //3. Do second stage of edge initialization ... + E = eStart; + do + { + InitEdge2(*E, PolyTyp); + E = E->Next; + if (IsFlat && E->Curr.Y != eStart->Curr.Y) IsFlat = false; + } + while (E != eStart); + + //4. Finally, add edge bounds to LocalMinima list ... + + //Totally flat paths must be handled differently when adding them + //to LocalMinima list to avoid endless loops etc ... + if (IsFlat) + { + if (Closed) + { + delete [] edges; + return false; + } + E->Prev->OutIdx = Skip; + if (E->Prev->Bot.X < E->Prev->Top.X) ReverseHorizontal(*E->Prev); + LocalMinima* locMin = new LocalMinima(); + locMin->Next = 0; + locMin->Y = E->Bot.Y; + locMin->LeftBound = 0; + locMin->RightBound = E; + locMin->RightBound->Side = esRight; + locMin->RightBound->WindDelta = 0; + while (E->Next->OutIdx != Skip) + { + E->NextInLML = E->Next; + if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + E = E->Next; + } + InsertLocalMinima(locMin); + m_edges.push_back(edges); + return true; + } + + m_edges.push_back(edges); + bool clockwise; + TEdge* EMin = 0; + for (;;) + { + E = FindNextLocMin(E); + if (E == EMin) break; + else if (!EMin) EMin = E; + + //E and E.Prev now share a local minima (left aligned if horizontal). + //Compare their slopes to find which starts which bound ... + LocalMinima* locMin = new LocalMinima; + locMin->Next = 0; + locMin->Y = E->Bot.Y; + if (E->Dx < E->Prev->Dx) + { + locMin->LeftBound = E->Prev; + locMin->RightBound = E; + clockwise = false; //Q.nextInLML = Q.prev + } else + { + locMin->LeftBound = E; + locMin->RightBound = E->Prev; + clockwise = true; //Q.nextInLML = Q.next + } + locMin->LeftBound->Side = esLeft; + locMin->RightBound->Side = esRight; + + if (!Closed) locMin->LeftBound->WindDelta = 0; + else if (locMin->LeftBound->Next == locMin->RightBound) + locMin->LeftBound->WindDelta = -1; + else locMin->LeftBound->WindDelta = 1; + locMin->RightBound->WindDelta = -locMin->LeftBound->WindDelta; + + E = ProcessBound(locMin->LeftBound, clockwise); + TEdge* E2 = ProcessBound(locMin->RightBound, !clockwise); + + if (locMin->LeftBound->OutIdx == Skip) + locMin->LeftBound = 0; + else if (locMin->RightBound->OutIdx == Skip) + locMin->RightBound = 0; + InsertLocalMinima(locMin); + if (!clockwise) E = E2; + } + return true; +} +//------------------------------------------------------------------------------ + +bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) +{ + bool result = false; + for (Paths::size_type i = 0; i < ppg.size(); ++i) + if (AddPath(ppg[i], PolyTyp, Closed)) result = true; + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::InsertLocalMinima(LocalMinima *newLm) +{ + if( ! m_MinimaList ) + { + m_MinimaList = newLm; + } + else if( newLm->Y >= m_MinimaList->Y ) + { + newLm->Next = m_MinimaList; + m_MinimaList = newLm; + } else + { + LocalMinima* tmpLm = m_MinimaList; + while( tmpLm->Next && ( newLm->Y < tmpLm->Next->Y ) ) + tmpLm = tmpLm->Next; + newLm->Next = tmpLm->Next; + tmpLm->Next = newLm; + } +} +//------------------------------------------------------------------------------ + +void ClipperBase::Clear() +{ + DisposeLocalMinimaList(); + for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) + { + //for each edge array in turn, find the first used edge and + //check for and remove any hiddenPts in each edge in the array. + TEdge* edges = m_edges[i]; + delete [] edges; + } + m_edges.clear(); + m_UseFullRange = false; + m_HasOpenPaths = false; +} +//------------------------------------------------------------------------------ + +void ClipperBase::Reset() +{ + m_CurrentLM = m_MinimaList; + if( !m_CurrentLM ) return; //ie nothing to process + + //reset all edges ... + LocalMinima* lm = m_MinimaList; + while( lm ) + { + TEdge* e = lm->LeftBound; + if (e) + { + e->Curr = e->Bot; + e->Side = esLeft; + e->OutIdx = Unassigned; + } + + e = lm->RightBound; + if (e) + { + e->Curr = e->Bot; + e->Side = esRight; + e->OutIdx = Unassigned; + } + lm = lm->Next; + } +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeLocalMinimaList() +{ + while( m_MinimaList ) + { + LocalMinima* tmpLm = m_MinimaList->Next; + delete m_MinimaList; + m_MinimaList = tmpLm; + } + m_CurrentLM = 0; +} +//------------------------------------------------------------------------------ + +void ClipperBase::PopLocalMinima() +{ + if( ! m_CurrentLM ) return; + m_CurrentLM = m_CurrentLM->Next; +} +//------------------------------------------------------------------------------ + +IntRect ClipperBase::GetBounds() +{ + IntRect result; + LocalMinima* lm = m_MinimaList; + if (!lm) + { + result.left = result.top = result.right = result.bottom = 0; + return result; + } + result.left = lm->LeftBound->Bot.X; + result.top = lm->LeftBound->Bot.Y; + result.right = lm->LeftBound->Bot.X; + result.bottom = lm->LeftBound->Bot.Y; + while (lm) + { + if (lm->LeftBound->Bot.Y > result.bottom) + result.bottom = lm->LeftBound->Bot.Y; + TEdge* e = lm->LeftBound; + for (;;) { + TEdge* bottomE = e; + while (e->NextInLML) + { + if (e->Bot.X < result.left) result.left = e->Bot.X; + if (e->Bot.X > result.right) result.right = e->Bot.X; + e = e->NextInLML; + } + if (e->Bot.X < result.left) result.left = e->Bot.X; + if (e->Bot.X > result.right) result.right = e->Bot.X; + if (e->Top.X < result.left) result.left = e->Top.X; + if (e->Top.X > result.right) result.right = e->Top.X; + if (e->Top.Y < result.top) result.top = e->Top.Y; + + if (bottomE == lm->LeftBound) e = lm->RightBound; + else break; + } + lm = lm->Next; + } + return result; +} + +//------------------------------------------------------------------------------ +// TClipper methods ... +//------------------------------------------------------------------------------ + +Clipper::Clipper(int initOptions) : ClipperBase() //constructor +{ + m_ActiveEdges = 0; + m_SortedEdges = 0; + m_ExecuteLocked = false; + m_UseFullRange = false; + m_ReverseOutput = ((initOptions & ioReverseSolution) != 0); + m_StrictSimple = ((initOptions & ioStrictlySimple) != 0); + m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0); + m_HasOpenPaths = false; +#ifdef use_xyz + m_ZFill = 0; +#endif +} +//------------------------------------------------------------------------------ + +Clipper::~Clipper() //destructor +{ + Clear(); + m_Scanbeam.clear(); +} +//------------------------------------------------------------------------------ + +#ifdef use_xyz +void Clipper::ZFillFunction(TZFillCallback zFillFunc) +{ + m_ZFill = zFillFunc; +} +//------------------------------------------------------------------------------ +#endif + +void Clipper::Reset() +{ + ClipperBase::Reset(); + m_Scanbeam.clear(); + m_ActiveEdges = 0; + m_SortedEdges = 0; + LocalMinima* lm = m_MinimaList; + while (lm) + { + InsertScanbeam(lm->Y); + lm = lm->Next; + } +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, Paths &solution, + PolyFillType subjFillType, PolyFillType clipFillType) +{ + if( m_ExecuteLocked ) return false; + if (m_HasOpenPaths) + throw clipperException("Error: PolyTree struct is need for open path clipping."); + m_ExecuteLocked = true; + solution.resize(0); + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + m_UsingPolyTree = false; + bool succeeded = ExecuteInternal(); + if (succeeded) BuildResult(solution); + DisposeAllOutRecs(); + m_ExecuteLocked = false; + return succeeded; +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, PolyTree& polytree, + PolyFillType subjFillType, PolyFillType clipFillType) +{ + if( m_ExecuteLocked ) return false; + m_ExecuteLocked = true; + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + m_UsingPolyTree = true; + bool succeeded = ExecuteInternal(); + if (succeeded) BuildResult2(polytree); + DisposeAllOutRecs(); + m_ExecuteLocked = false; + return succeeded; +} +//------------------------------------------------------------------------------ + +void Clipper::FixHoleLinkage(OutRec &outrec) +{ + //skip OutRecs that (a) contain outermost polygons or + //(b) already have the correct owner/child linkage ... + if (!outrec.FirstLeft || + (outrec.IsHole != outrec.FirstLeft->IsHole && + outrec.FirstLeft->Pts)) return; + + OutRec* orfl = outrec.FirstLeft; + while (orfl && ((orfl->IsHole == outrec.IsHole) || !orfl->Pts)) + orfl = orfl->FirstLeft; + outrec.FirstLeft = orfl; +} +//------------------------------------------------------------------------------ + +bool Clipper::ExecuteInternal() +{ + bool succeeded = true; + try { + Reset(); + if (!m_CurrentLM) return false; + cInt botY = PopScanbeam(); + do { + InsertLocalMinimaIntoAEL(botY); + ClearGhostJoins(); + ProcessHorizontals(false); + if (m_Scanbeam.empty()) break; + cInt topY = PopScanbeam(); + succeeded = ProcessIntersections(botY, topY); + if (!succeeded) break; + ProcessEdgesAtTopOfScanbeam(topY); + botY = topY; + } while (!m_Scanbeam.empty() || m_CurrentLM); + } + catch(...) + { + succeeded = false; + } + + if (succeeded) + { + //fix orientations ... + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec *outRec = m_PolyOuts[i]; + if (!outRec->Pts || outRec->IsOpen) continue; + if ((outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) + ReversePolyPtLinks(outRec->Pts); + } + + if (!m_Joins.empty()) JoinCommonEdges(); + + //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec *outRec = m_PolyOuts[i]; + if (outRec->Pts && !outRec->IsOpen) + FixupOutPolygon(*outRec); + } + + if (m_StrictSimple) DoSimplePolygons(); + } + + ClearJoins(); + ClearGhostJoins(); + return succeeded; +} +//------------------------------------------------------------------------------ + +void Clipper::InsertScanbeam(const cInt Y) +{ + m_Scanbeam.insert(Y); +} +//------------------------------------------------------------------------------ + +cInt Clipper::PopScanbeam() +{ + cInt Y = *m_Scanbeam.begin(); + m_Scanbeam.erase(m_Scanbeam.begin()); + return Y; +} +//------------------------------------------------------------------------------ + +void Clipper::DisposeAllOutRecs(){ + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + DisposeOutRec(i); + m_PolyOuts.clear(); +} +//------------------------------------------------------------------------------ + +void Clipper::DisposeOutRec(PolyOutList::size_type index) +{ + OutRec *outRec = m_PolyOuts[index]; + if (outRec->Pts) DisposeOutPts(outRec->Pts); + delete outRec; + m_PolyOuts[index] = 0; +} +//------------------------------------------------------------------------------ + +void Clipper::SetWindingCount(TEdge &edge) +{ + TEdge *e = edge.PrevInAEL; + //find the edge of the same polytype that immediately preceeds 'edge' in AEL + while (e && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL; + if (!e) + { + edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); + edge.WindCnt2 = 0; + e = m_ActiveEdges; //ie get ready to calc WindCnt2 + } + else if (edge.WindDelta == 0 && m_ClipType != ctUnion) + { + edge.WindCnt = 1; + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + else if (IsEvenOddFillType(edge)) + { + //EvenOdd filling ... + if (edge.WindDelta == 0) + { + //are we inside a subj polygon ... + bool Inside = true; + TEdge *e2 = e->PrevInAEL; + while (e2) + { + if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0) + Inside = !Inside; + e2 = e2->PrevInAEL; + } + edge.WindCnt = (Inside ? 0 : 1); + } + else + { + edge.WindCnt = edge.WindDelta; + } + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + else + { + //nonZero, Positive or Negative filling ... + if (e->WindCnt * e->WindDelta < 0) + { + //prev edge is 'decreasing' WindCount (WC) toward zero + //so we're outside the previous polygon ... + if (Abs(e->WindCnt) > 1) + { + //outside prev poly but still inside another. + //when reversing direction of prev poly use the same WC + if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; + //otherwise continue to 'decrease' WC ... + else edge.WindCnt = e->WindCnt + edge.WindDelta; + } + else + //now outside all polys of same polytype so set own WC ... + edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); + } else + { + //prev edge is 'increasing' WindCount (WC) away from zero + //so we're inside the previous polygon ... + if (edge.WindDelta == 0) + edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1); + //if wind direction is reversing prev then use same WC + else if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; + //otherwise add to WC ... + else edge.WindCnt = e->WindCnt + edge.WindDelta; + } + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + + //update WindCnt2 ... + if (IsEvenOddAltFillType(edge)) + { + //EvenOdd filling ... + while (e != &edge) + { + if (e->WindDelta != 0) + edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0); + e = e->NextInAEL; + } + } else + { + //nonZero, Positive or Negative filling ... + while ( e != &edge ) + { + edge.WindCnt2 += e->WindDelta; + e = e->NextInAEL; + } + } +} +//------------------------------------------------------------------------------ + +bool Clipper::IsEvenOddFillType(const TEdge& edge) const +{ + if (edge.PolyTyp == ptSubject) + return m_SubjFillType == pftEvenOdd; else + return m_ClipFillType == pftEvenOdd; +} +//------------------------------------------------------------------------------ + +bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const +{ + if (edge.PolyTyp == ptSubject) + return m_ClipFillType == pftEvenOdd; else + return m_SubjFillType == pftEvenOdd; +} +//------------------------------------------------------------------------------ + +bool Clipper::IsContributing(const TEdge& edge) const +{ + PolyFillType pft, pft2; + if (edge.PolyTyp == ptSubject) + { + pft = m_SubjFillType; + pft2 = m_ClipFillType; + } else + { + pft = m_ClipFillType; + pft2 = m_SubjFillType; + } + + switch(pft) + { + case pftEvenOdd: + //return false if a subj line has been flagged as inside a subj polygon + if (edge.WindDelta == 0 && edge.WindCnt != 1) return false; + break; + case pftNonZero: + if (Abs(edge.WindCnt) != 1) return false; + break; + case pftPositive: + if (edge.WindCnt != 1) return false; + break; + default: //pftNegative + if (edge.WindCnt != -1) return false; + } + + switch(m_ClipType) + { + case ctIntersection: + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 != 0); + case pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); + } + break; + case ctUnion: + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + break; + case ctDifference: + if (edge.PolyTyp == ptSubject) + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 != 0); + case pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); + } + break; + case ctXor: + if (edge.WindDelta == 0) //XOr always contributing unless open + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + return true; + break; + default: + return true; + } +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) +{ + OutPt* result; + TEdge *e, *prevE; + if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx )) + { + result = AddOutPt(e1, Pt); + e2->OutIdx = e1->OutIdx; + e1->Side = esLeft; + e2->Side = esRight; + e = e1; + if (e->PrevInAEL == e2) + prevE = e2->PrevInAEL; + else + prevE = e->PrevInAEL; + } else + { + result = AddOutPt(e2, Pt); + e1->OutIdx = e2->OutIdx; + e1->Side = esRight; + e2->Side = esLeft; + e = e2; + if (e->PrevInAEL == e1) + prevE = e1->PrevInAEL; + else + prevE = e->PrevInAEL; + } + + if (prevE && prevE->OutIdx >= 0 && + (TopX(*prevE, Pt.Y) == TopX(*e, Pt.Y)) && + SlopesEqual(*e, *prevE, m_UseFullRange) && + (e->WindDelta != 0) && (prevE->WindDelta != 0)) + { + OutPt* outPt = AddOutPt(prevE, Pt); + AddJoin(result, outPt, e->Top); + } + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) +{ + AddOutPt( e1, Pt ); + if (e2->WindDelta == 0) AddOutPt(e2, Pt); + if( e1->OutIdx == e2->OutIdx ) + { + e1->OutIdx = Unassigned; + e2->OutIdx = Unassigned; + } + else if (e1->OutIdx < e2->OutIdx) + AppendPolygon(e1, e2); + else + AppendPolygon(e2, e1); +} +//------------------------------------------------------------------------------ + +void Clipper::AddEdgeToSEL(TEdge *edge) +{ + //SEL pointers in PEdge are reused to build a list of horizontal edges. + //However, we don't need to worry about order with horizontal edge processing. + if( !m_SortedEdges ) + { + m_SortedEdges = edge; + edge->PrevInSEL = 0; + edge->NextInSEL = 0; + } + else + { + edge->NextInSEL = m_SortedEdges; + edge->PrevInSEL = 0; + m_SortedEdges->PrevInSEL = edge; + m_SortedEdges = edge; + } +} +//------------------------------------------------------------------------------ + +void Clipper::CopyAELToSEL() +{ + TEdge* e = m_ActiveEdges; + m_SortedEdges = e; + while ( e ) + { + e->PrevInSEL = e->PrevInAEL; + e->NextInSEL = e->NextInAEL; + e = e->NextInAEL; + } +} +//------------------------------------------------------------------------------ + +void Clipper::AddJoin(OutPt *op1, OutPt *op2, const IntPoint OffPt) +{ + Join* j = new Join; + j->OutPt1 = op1; + j->OutPt2 = op2; + j->OffPt = OffPt; + m_Joins.push_back(j); +} +//------------------------------------------------------------------------------ + +void Clipper::ClearJoins() +{ + for (JoinList::size_type i = 0; i < m_Joins.size(); i++) + delete m_Joins[i]; + m_Joins.resize(0); +} +//------------------------------------------------------------------------------ + +void Clipper::ClearGhostJoins() +{ + for (JoinList::size_type i = 0; i < m_GhostJoins.size(); i++) + delete m_GhostJoins[i]; + m_GhostJoins.resize(0); +} +//------------------------------------------------------------------------------ + +void Clipper::AddGhostJoin(OutPt *op, const IntPoint OffPt) +{ + Join* j = new Join; + j->OutPt1 = op; + j->OutPt2 = 0; + j->OffPt = OffPt; + m_GhostJoins.push_back(j); +} +//------------------------------------------------------------------------------ + +void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) +{ + while( m_CurrentLM && ( m_CurrentLM->Y == botY ) ) + { + TEdge* lb = m_CurrentLM->LeftBound; + TEdge* rb = m_CurrentLM->RightBound; + PopLocalMinima(); + OutPt *Op1 = 0; + if (!lb) + { + //nb: don't insert LB into either AEL or SEL + InsertEdgeIntoAEL(rb, 0); + SetWindingCount(*rb); + if (IsContributing(*rb)) + Op1 = AddOutPt(rb, rb->Bot); + } + else if (!rb) + { + InsertEdgeIntoAEL(lb, 0); + SetWindingCount(*lb); + if (IsContributing(*lb)) + Op1 = AddOutPt(lb, lb->Bot); + InsertScanbeam(lb->Top.Y); + } + else + { + InsertEdgeIntoAEL(lb, 0); + InsertEdgeIntoAEL(rb, lb); + SetWindingCount( *lb ); + rb->WindCnt = lb->WindCnt; + rb->WindCnt2 = lb->WindCnt2; + if (IsContributing(*lb)) + Op1 = AddLocalMinPoly(lb, rb, lb->Bot); + InsertScanbeam(lb->Top.Y); + } + + if (rb) + { + if(IsHorizontal(*rb)) AddEdgeToSEL(rb); + else InsertScanbeam( rb->Top.Y ); + } + + if (!lb || !rb) continue; + + //if any output polygons share an edge, they'll need joining later ... + if (Op1 && IsHorizontal(*rb) && + m_GhostJoins.size() > 0 && (rb->WindDelta != 0)) + { + for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) + { + Join* jr = m_GhostJoins[i]; + //if the horizontal Rb and a 'ghost' horizontal overlap, then convert + //the 'ghost' join to a real join ready for later ... + if (HorzSegmentsOverlap(jr->OutPt1->Pt, jr->OffPt, rb->Bot, rb->Top)) + AddJoin(jr->OutPt1, Op1, jr->OffPt); + } + } + + if (lb->OutIdx >= 0 && lb->PrevInAEL && + lb->PrevInAEL->Curr.X == lb->Bot.X && + lb->PrevInAEL->OutIdx >= 0 && + SlopesEqual(*lb->PrevInAEL, *lb, m_UseFullRange) && + (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0)) + { + OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot); + AddJoin(Op1, Op2, lb->Top); + } + + if(lb->NextInAEL != rb) + { + + if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 && + SlopesEqual(*rb->PrevInAEL, *rb, m_UseFullRange) && + (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0)) + { + OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot); + AddJoin(Op1, Op2, rb->Top); + } + + TEdge* e = lb->NextInAEL; + if (e) + { + while( e != rb ) + { + //nb: For calculating winding counts etc, IntersectEdges() assumes + //that param1 will be to the Right of param2 ABOVE the intersection ... + IntersectEdges(rb , e , lb->Curr); //order important here + e = e->NextInAEL; + } + } + } + + } +} +//------------------------------------------------------------------------------ + +void Clipper::DeleteFromAEL(TEdge *e) +{ + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if( !AelPrev && !AelNext && (e != m_ActiveEdges) ) return; //already deleted + if( AelPrev ) AelPrev->NextInAEL = AelNext; + else m_ActiveEdges = AelNext; + if( AelNext ) AelNext->PrevInAEL = AelPrev; + e->NextInAEL = 0; + e->PrevInAEL = 0; +} +//------------------------------------------------------------------------------ + +void Clipper::DeleteFromSEL(TEdge *e) +{ + TEdge* SelPrev = e->PrevInSEL; + TEdge* SelNext = e->NextInSEL; + if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted + if( SelPrev ) SelPrev->NextInSEL = SelNext; + else m_SortedEdges = SelNext; + if( SelNext ) SelNext->PrevInSEL = SelPrev; + e->NextInSEL = 0; + e->PrevInSEL = 0; +} +//------------------------------------------------------------------------------ + +#ifdef use_xyz + +void Clipper::SetZ(IntPoint& pt, TEdge& e) +{ + pt.Z = 0; + if (m_ZFill) + { + //put the 'preferred' point as first parameter ... + if (e.OutIdx < 0) + (*m_ZFill)(e.Bot, e.Top, pt); //outside a path so presume entering + else + (*m_ZFill)(e.Top, e.Bot, pt); //inside a path so presume exiting + } +} +//------------------------------------------------------------------------------ +#endif + +void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, + const IntPoint &Pt, bool protect) +{ + //e1 will be to the Left of e2 BELOW the intersection. Therefore e1 is before + //e2 in AEL except when e1 is being inserted at the intersection point ... + bool e1stops = !protect && !e1->NextInLML && + e1->Top.X == Pt.X && e1->Top.Y == Pt.Y; + bool e2stops = !protect && !e2->NextInLML && + e2->Top.X == Pt.X && e2->Top.Y == Pt.Y; + bool e1Contributing = ( e1->OutIdx >= 0 ); + bool e2Contributing = ( e2->OutIdx >= 0 ); + +#ifdef use_lines + //if either edge is on an OPEN path ... + if (e1->WindDelta == 0 || e2->WindDelta == 0) + { + //ignore subject-subject open path intersections UNLESS they + //are both open paths, AND they are both 'contributing maximas' ... + if (e1->WindDelta == 0 && e2->WindDelta == 0) + { + if ((e1stops || e2stops) && e1Contributing && e2Contributing) + AddLocalMaxPoly(e1, e2, Pt); + } + + //if intersecting a subj line with a subj poly ... + else if (e1->PolyTyp == e2->PolyTyp && + e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion) + { + if (e1->WindDelta == 0) + { + if (e2Contributing) + { + AddOutPt(e1, Pt); + if (e1Contributing) e1->OutIdx = Unassigned; + } + } + else + { + if (e1Contributing) + { + AddOutPt(e2, Pt); + if (e2Contributing) e2->OutIdx = Unassigned; + } + } + } + else if (e1->PolyTyp != e2->PolyTyp) + { + //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ... + if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 && + (m_ClipType != ctUnion || e2->WindCnt2 == 0)) + { + AddOutPt(e1, Pt); + if (e1Contributing) e1->OutIdx = Unassigned; + } + else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) && + (m_ClipType != ctUnion || e1->WindCnt2 == 0)) + { + AddOutPt(e2, Pt); + if (e2Contributing) e2->OutIdx = Unassigned; + } + } + + if (e1stops) + if (e1->OutIdx < 0) DeleteFromAEL(e1); + else throw clipperException("Error intersecting polylines"); + if (e2stops) + if (e2->OutIdx < 0) DeleteFromAEL(e2); + else throw clipperException("Error intersecting polylines"); + return; + } +#endif + + //update winding counts... + //assumes that e1 will be to the Right of e2 ABOVE the intersection + if ( e1->PolyTyp == e2->PolyTyp ) + { + if ( IsEvenOddFillType( *e1) ) + { + int oldE1WindCnt = e1->WindCnt; + e1->WindCnt = e2->WindCnt; + e2->WindCnt = oldE1WindCnt; + } else + { + if (e1->WindCnt + e2->WindDelta == 0 ) e1->WindCnt = -e1->WindCnt; + else e1->WindCnt += e2->WindDelta; + if ( e2->WindCnt - e1->WindDelta == 0 ) e2->WindCnt = -e2->WindCnt; + else e2->WindCnt -= e1->WindDelta; + } + } else + { + if (!IsEvenOddFillType(*e2)) e1->WindCnt2 += e2->WindDelta; + else e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0; + if (!IsEvenOddFillType(*e1)) e2->WindCnt2 -= e1->WindDelta; + else e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0; + } + + PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; + if (e1->PolyTyp == ptSubject) + { + e1FillType = m_SubjFillType; + e1FillType2 = m_ClipFillType; + } else + { + e1FillType = m_ClipFillType; + e1FillType2 = m_SubjFillType; + } + if (e2->PolyTyp == ptSubject) + { + e2FillType = m_SubjFillType; + e2FillType2 = m_ClipFillType; + } else + { + e2FillType = m_ClipFillType; + e2FillType2 = m_SubjFillType; + } + + cInt e1Wc, e2Wc; + switch (e1FillType) + { + case pftPositive: e1Wc = e1->WindCnt; break; + case pftNegative: e1Wc = -e1->WindCnt; break; + default: e1Wc = Abs(e1->WindCnt); + } + switch(e2FillType) + { + case pftPositive: e2Wc = e2->WindCnt; break; + case pftNegative: e2Wc = -e2->WindCnt; break; + default: e2Wc = Abs(e2->WindCnt); + } + + if ( e1Contributing && e2Contributing ) + { + if ( e1stops || e2stops || + (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || + (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) ) + AddLocalMaxPoly(e1, e2, Pt); + else + { + AddOutPt(e1, Pt); + AddOutPt(e2, Pt); + SwapSides( *e1 , *e2 ); + SwapPolyIndexes( *e1 , *e2 ); + } + } + else if ( e1Contributing ) + { + if (e2Wc == 0 || e2Wc == 1) + { + AddOutPt(e1, Pt); + SwapSides(*e1, *e2); + SwapPolyIndexes(*e1, *e2); + } + } + else if ( e2Contributing ) + { + if (e1Wc == 0 || e1Wc == 1) + { + AddOutPt(e2, Pt); + SwapSides(*e1, *e2); + SwapPolyIndexes(*e1, *e2); + } + } + else if ( (e1Wc == 0 || e1Wc == 1) && + (e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops ) + { + //neither edge is currently contributing ... + + cInt e1Wc2, e2Wc2; + switch (e1FillType2) + { + case pftPositive: e1Wc2 = e1->WindCnt2; break; + case pftNegative : e1Wc2 = -e1->WindCnt2; break; + default: e1Wc2 = Abs(e1->WindCnt2); + } + switch (e2FillType2) + { + case pftPositive: e2Wc2 = e2->WindCnt2; break; + case pftNegative: e2Wc2 = -e2->WindCnt2; break; + default: e2Wc2 = Abs(e2->WindCnt2); + } + + if (e1->PolyTyp != e2->PolyTyp) + AddLocalMinPoly(e1, e2, Pt); + else if (e1Wc == 1 && e2Wc == 1) + switch( m_ClipType ) { + case ctIntersection: + if (e1Wc2 > 0 && e2Wc2 > 0) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctUnion: + if ( e1Wc2 <= 0 && e2Wc2 <= 0 ) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctDifference: + if (((e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || + ((e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctXor: + AddLocalMinPoly(e1, e2, Pt); + } + else + SwapSides( *e1, *e2 ); + } + + if( (e1stops != e2stops) && + ( (e1stops && (e1->OutIdx >= 0)) || (e2stops && (e2->OutIdx >= 0)) ) ) + { + SwapSides( *e1, *e2 ); + SwapPolyIndexes( *e1, *e2 ); + } + + //finally, delete any non-contributing maxima edges ... + if( e1stops ) DeleteFromAEL( e1 ); + if( e2stops ) DeleteFromAEL( e2 ); +} +//------------------------------------------------------------------------------ + +void Clipper::SetHoleState(TEdge *e, OutRec *outrec) +{ + bool IsHole = false; + TEdge *e2 = e->PrevInAEL; + while (e2) + { + if (e2->OutIdx >= 0 && e2->WindDelta != 0) + { + IsHole = !IsHole; + if (! outrec->FirstLeft) + outrec->FirstLeft = m_PolyOuts[e2->OutIdx]; + } + e2 = e2->PrevInAEL; + } + if (IsHole) outrec->IsHole = true; +} +//------------------------------------------------------------------------------ + +OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) +{ + //work out which polygon fragment has the correct hole state ... + if (!outRec1->BottomPt) + outRec1->BottomPt = GetBottomPt(outRec1->Pts); + if (!outRec2->BottomPt) + outRec2->BottomPt = GetBottomPt(outRec2->Pts); + OutPt *OutPt1 = outRec1->BottomPt; + OutPt *OutPt2 = outRec2->BottomPt; + if (OutPt1->Pt.Y > OutPt2->Pt.Y) return outRec1; + else if (OutPt1->Pt.Y < OutPt2->Pt.Y) return outRec2; + else if (OutPt1->Pt.X < OutPt2->Pt.X) return outRec1; + else if (OutPt1->Pt.X > OutPt2->Pt.X) return outRec2; + else if (OutPt1->Next == OutPt1) return outRec2; + else if (OutPt2->Next == OutPt2) return outRec1; + else if (FirstIsBottomPt(OutPt1, OutPt2)) return outRec1; + else return outRec2; +} +//------------------------------------------------------------------------------ + +bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2) +{ + do + { + outRec1 = outRec1->FirstLeft; + if (outRec1 == outRec2) return true; + } while (outRec1); + return false; +} +//------------------------------------------------------------------------------ + +OutRec* Clipper::GetOutRec(int Idx) +{ + OutRec* outrec = m_PolyOuts[Idx]; + while (outrec != m_PolyOuts[outrec->Idx]) + outrec = m_PolyOuts[outrec->Idx]; + return outrec; +} +//------------------------------------------------------------------------------ + +void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) +{ + //get the start and ends of both output polygons ... + OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; + OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; + + OutRec *holeStateRec; + if (Param1RightOfParam2(outRec1, outRec2)) + holeStateRec = outRec2; + else if (Param1RightOfParam2(outRec2, outRec1)) + holeStateRec = outRec1; + else + holeStateRec = GetLowermostRec(outRec1, outRec2); + + //get the start and ends of both output polygons and + //join e2 poly onto e1 poly and delete pointers to e2 ... + + OutPt* p1_lft = outRec1->Pts; + OutPt* p1_rt = p1_lft->Prev; + OutPt* p2_lft = outRec2->Pts; + OutPt* p2_rt = p2_lft->Prev; + + EdgeSide Side; + //join e2 poly onto e1 poly and delete pointers to e2 ... + if( e1->Side == esLeft ) + { + if( e2->Side == esLeft ) + { + //z y x a b c + ReversePolyPtLinks(p2_lft); + p2_lft->Next = p1_lft; + p1_lft->Prev = p2_lft; + p1_rt->Next = p2_rt; + p2_rt->Prev = p1_rt; + outRec1->Pts = p2_rt; + } else + { + //x y z a b c + p2_rt->Next = p1_lft; + p1_lft->Prev = p2_rt; + p2_lft->Prev = p1_rt; + p1_rt->Next = p2_lft; + outRec1->Pts = p2_lft; + } + Side = esLeft; + } else + { + if( e2->Side == esRight ) + { + //a b c z y x + ReversePolyPtLinks(p2_lft); + p1_rt->Next = p2_rt; + p2_rt->Prev = p1_rt; + p2_lft->Next = p1_lft; + p1_lft->Prev = p2_lft; + } else + { + //a b c x y z + p1_rt->Next = p2_lft; + p2_lft->Prev = p1_rt; + p1_lft->Prev = p2_rt; + p2_rt->Next = p1_lft; + } + Side = esRight; + } + + outRec1->BottomPt = 0; + if (holeStateRec == outRec2) + { + if (outRec2->FirstLeft != outRec1) + outRec1->FirstLeft = outRec2->FirstLeft; + outRec1->IsHole = outRec2->IsHole; + } + outRec2->Pts = 0; + outRec2->BottomPt = 0; + outRec2->FirstLeft = outRec1; + + int OKIdx = e1->OutIdx; + int ObsoleteIdx = e2->OutIdx; + + e1->OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly + e2->OutIdx = Unassigned; + + TEdge* e = m_ActiveEdges; + while( e ) + { + if( e->OutIdx == ObsoleteIdx ) + { + e->OutIdx = OKIdx; + e->Side = Side; + break; + } + e = e->NextInAEL; + } + + outRec2->Idx = outRec1->Idx; +} +//------------------------------------------------------------------------------ + +OutRec* Clipper::CreateOutRec() +{ + OutRec* result = new OutRec; + result->IsHole = false; + result->IsOpen = false; + result->FirstLeft = 0; + result->Pts = 0; + result->BottomPt = 0; + result->PolyNd = 0; + m_PolyOuts.push_back(result); + result->Idx = (int)m_PolyOuts.size()-1; + return result; +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) +{ + bool ToFront = (e->Side == esLeft); + if( e->OutIdx < 0 ) + { + OutRec *outRec = CreateOutRec(); + outRec->IsOpen = (e->WindDelta == 0); + OutPt* newOp = new OutPt; + outRec->Pts = newOp; + newOp->Idx = outRec->Idx; + newOp->Pt = pt; + newOp->Next = newOp; + newOp->Prev = newOp; + if (!outRec->IsOpen) + SetHoleState(e, outRec); +#ifdef use_xyz + if (pt == e->Bot) newOp->Pt = e->Bot; + else if (pt == e->Top) newOp->Pt = e->Top; + else SetZ(newOp->Pt, *e); +#endif + e->OutIdx = outRec->Idx; //nb: do this after SetZ ! + return newOp; + } else + { + OutRec *outRec = m_PolyOuts[e->OutIdx]; + //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' + OutPt* op = outRec->Pts; + + if (ToFront && (pt == op->Pt)) return op; + else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; + + OutPt* newOp = new OutPt; + newOp->Idx = outRec->Idx; + newOp->Pt = pt; + newOp->Next = op; + newOp->Prev = op->Prev; + newOp->Prev->Next = newOp; + op->Prev = newOp; + if (ToFront) outRec->Pts = newOp; +#ifdef use_xyz + if (pt == e->Bot) newOp->Pt = e->Bot; + else if (pt == e->Top) newOp->Pt = e->Top; + else SetZ(newOp->Pt, *e); +#endif + return newOp; + } +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessHorizontals(bool IsTopOfScanbeam) +{ + TEdge* horzEdge = m_SortedEdges; + while(horzEdge) + { + DeleteFromSEL(horzEdge); + ProcessHorizontal(horzEdge, IsTopOfScanbeam); + horzEdge = m_SortedEdges; + } +} +//------------------------------------------------------------------------------ + +inline bool IsMinima(TEdge *e) +{ + return e && (e->Prev->NextInLML != e) && (e->Next->NextInLML != e); +} +//------------------------------------------------------------------------------ + +inline bool IsMaxima(TEdge *e, const cInt Y) +{ + return e && e->Top.Y == Y && !e->NextInLML; +} +//------------------------------------------------------------------------------ + +inline bool IsIntermediate(TEdge *e, const cInt Y) +{ + return e->Top.Y == Y && e->NextInLML; +} +//------------------------------------------------------------------------------ + +TEdge *GetMaximaPair(TEdge *e) +{ + TEdge* result = 0; + if ((e->Next->Top == e->Top) && !e->Next->NextInLML) + result = e->Next; + else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML) + result = e->Prev; + + if (result && (result->OutIdx == Skip || + //result is false if both NextInAEL & PrevInAEL are nil & not horizontal ... + (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) + return 0; + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2) +{ + //check that one or other edge hasn't already been removed from AEL ... + if (Edge1->NextInAEL == Edge1->PrevInAEL || + Edge2->NextInAEL == Edge2->PrevInAEL) return; + + if( Edge1->NextInAEL == Edge2 ) + { + TEdge* Next = Edge2->NextInAEL; + if( Next ) Next->PrevInAEL = Edge1; + TEdge* Prev = Edge1->PrevInAEL; + if( Prev ) Prev->NextInAEL = Edge2; + Edge2->PrevInAEL = Prev; + Edge2->NextInAEL = Edge1; + Edge1->PrevInAEL = Edge2; + Edge1->NextInAEL = Next; + } + else if( Edge2->NextInAEL == Edge1 ) + { + TEdge* Next = Edge1->NextInAEL; + if( Next ) Next->PrevInAEL = Edge2; + TEdge* Prev = Edge2->PrevInAEL; + if( Prev ) Prev->NextInAEL = Edge1; + Edge1->PrevInAEL = Prev; + Edge1->NextInAEL = Edge2; + Edge2->PrevInAEL = Edge1; + Edge2->NextInAEL = Next; + } + else + { + TEdge* Next = Edge1->NextInAEL; + TEdge* Prev = Edge1->PrevInAEL; + Edge1->NextInAEL = Edge2->NextInAEL; + if( Edge1->NextInAEL ) Edge1->NextInAEL->PrevInAEL = Edge1; + Edge1->PrevInAEL = Edge2->PrevInAEL; + if( Edge1->PrevInAEL ) Edge1->PrevInAEL->NextInAEL = Edge1; + Edge2->NextInAEL = Next; + if( Edge2->NextInAEL ) Edge2->NextInAEL->PrevInAEL = Edge2; + Edge2->PrevInAEL = Prev; + if( Edge2->PrevInAEL ) Edge2->PrevInAEL->NextInAEL = Edge2; + } + + if( !Edge1->PrevInAEL ) m_ActiveEdges = Edge1; + else if( !Edge2->PrevInAEL ) m_ActiveEdges = Edge2; +} +//------------------------------------------------------------------------------ + +void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2) +{ + if( !( Edge1->NextInSEL ) && !( Edge1->PrevInSEL ) ) return; + if( !( Edge2->NextInSEL ) && !( Edge2->PrevInSEL ) ) return; + + if( Edge1->NextInSEL == Edge2 ) + { + TEdge* Next = Edge2->NextInSEL; + if( Next ) Next->PrevInSEL = Edge1; + TEdge* Prev = Edge1->PrevInSEL; + if( Prev ) Prev->NextInSEL = Edge2; + Edge2->PrevInSEL = Prev; + Edge2->NextInSEL = Edge1; + Edge1->PrevInSEL = Edge2; + Edge1->NextInSEL = Next; + } + else if( Edge2->NextInSEL == Edge1 ) + { + TEdge* Next = Edge1->NextInSEL; + if( Next ) Next->PrevInSEL = Edge2; + TEdge* Prev = Edge2->PrevInSEL; + if( Prev ) Prev->NextInSEL = Edge1; + Edge1->PrevInSEL = Prev; + Edge1->NextInSEL = Edge2; + Edge2->PrevInSEL = Edge1; + Edge2->NextInSEL = Next; + } + else + { + TEdge* Next = Edge1->NextInSEL; + TEdge* Prev = Edge1->PrevInSEL; + Edge1->NextInSEL = Edge2->NextInSEL; + if( Edge1->NextInSEL ) Edge1->NextInSEL->PrevInSEL = Edge1; + Edge1->PrevInSEL = Edge2->PrevInSEL; + if( Edge1->PrevInSEL ) Edge1->PrevInSEL->NextInSEL = Edge1; + Edge2->NextInSEL = Next; + if( Edge2->NextInSEL ) Edge2->NextInSEL->PrevInSEL = Edge2; + Edge2->PrevInSEL = Prev; + if( Edge2->PrevInSEL ) Edge2->PrevInSEL->NextInSEL = Edge2; + } + + if( !Edge1->PrevInSEL ) m_SortedEdges = Edge1; + else if( !Edge2->PrevInSEL ) m_SortedEdges = Edge2; +} +//------------------------------------------------------------------------------ + +TEdge* GetNextInAEL(TEdge *e, Direction dir) +{ + return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL; +} +//------------------------------------------------------------------------------ + +void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) +{ + if (HorzEdge.Bot.X < HorzEdge.Top.X) + { + Left = HorzEdge.Bot.X; + Right = HorzEdge.Top.X; + Dir = dLeftToRight; + } else + { + Left = HorzEdge.Top.X; + Right = HorzEdge.Bot.X; + Dir = dRightToLeft; + } +} +//------------------------------------------------------------------------ + +void Clipper::PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam) +{ + //get the last Op for this horizontal edge + //the point may be anywhere along the horizontal ... + OutPt* outPt = m_PolyOuts[horzEdge->OutIdx]->Pts; + if (horzEdge->Side != esLeft) outPt = outPt->Prev; + + //First, match up overlapping horizontal edges (eg when one polygon's + //intermediate horz edge overlaps an intermediate horz edge of another, or + //when one polygon sits on top of another) ... + //for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) + //{ + // Join* j = m_GhostJoins[i]; + // if (HorzSegmentsOverlap(j->OutPt1->Pt, j->OffPt, horzEdge->Bot, horzEdge->Top)) + // AddJoin(j->OutPt1, outPt, j->OffPt); + //} + + //Also, since horizontal edges at the top of one SB are often removed from + //the AEL before we process the horizontal edges at the bottom of the next, + //we need to create 'ghost' Join records of 'contrubuting' horizontals that + //we can compare with horizontals at the bottom of the next SB. + if (isTopOfScanbeam) + { + if (outPt->Pt == horzEdge->Top) + AddGhostJoin(outPt, horzEdge->Bot); + else + AddGhostJoin(outPt, horzEdge->Top); + } +} +//------------------------------------------------------------------------------ + +/******************************************************************************* +* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or * +* Bottom of a scanbeam) are processed as if layered. The order in which HEs * +* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#] * +* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs), * +* and with other non-horizontal edges [*]. Once these intersections are * +* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into * +* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. * +*******************************************************************************/ + +void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) +{ + Direction dir; + cInt horzLeft, horzRight; + + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + + TEdge* eLastHorz = horzEdge, *eMaxPair = 0; + while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML)) + eLastHorz = eLastHorz->NextInLML; + if (!eLastHorz->NextInLML) + eMaxPair = GetMaximaPair(eLastHorz); + + for (;;) + { + bool IsLastHorz = (horzEdge == eLastHorz); + TEdge* e = GetNextInAEL(horzEdge, dir); + while(e) + { + //Break if we've got to the end of an intermediate horizontal edge ... + //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. + if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && + e->Dx < horzEdge->NextInLML->Dx) break; + + TEdge* eNext = GetNextInAEL(e, dir); //saves eNext for later + + if ((dir == dLeftToRight && e->Curr.X <= horzRight) || + (dir == dRightToLeft && e->Curr.X >= horzLeft)) + { + if (horzEdge->OutIdx >= 0 && horzEdge->WindDelta != 0) + PrepareHorzJoins(horzEdge, isTopOfScanbeam); + //so far we're still in range of the horizontal Edge but make sure + //we're at the last of consec. horizontals when matching with eMaxPair + if(e == eMaxPair && IsLastHorz) + { + if (dir == dLeftToRight) + IntersectEdges(horzEdge, e, e->Top); + else + IntersectEdges(e, horzEdge, e->Top); + if (eMaxPair->OutIdx >= 0) throw clipperException("ProcessHorizontal error"); + return; + } + else if(dir == dLeftToRight) + { + IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); + IntersectEdges(horzEdge, e, Pt, true); + } + else + { + IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); + IntersectEdges( e, horzEdge, Pt, true); + } + SwapPositionsInAEL( horzEdge, e ); + } + else if( (dir == dLeftToRight && e->Curr.X >= horzRight) || + (dir == dRightToLeft && e->Curr.X <= horzLeft) ) break; + e = eNext; + } //end while + + if (horzEdge->OutIdx >= 0 && horzEdge->WindDelta != 0) + PrepareHorzJoins(horzEdge, isTopOfScanbeam); + + if (horzEdge->NextInLML && IsHorizontal(*horzEdge->NextInLML)) + { + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + } else + break; + } //end for (;;) + + if(horzEdge->NextInLML) + { + if(horzEdge->OutIdx >= 0) + { + OutPt* op1 = AddOutPt( horzEdge, horzEdge->Top); + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->WindDelta == 0) return; + //nb: HorzEdge is no longer horizontal here + TEdge* ePrev = horzEdge->PrevInAEL; + TEdge* eNext = horzEdge->NextInAEL; + if (ePrev && ePrev->Curr.X == horzEdge->Bot.X && + ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0 && + (ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && + SlopesEqual(*horzEdge, *ePrev, m_UseFullRange))) + { + OutPt* op2 = AddOutPt(ePrev, horzEdge->Bot); + AddJoin(op1, op2, horzEdge->Top); + } + else if (eNext && eNext->Curr.X == horzEdge->Bot.X && + eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0 && + eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && + SlopesEqual(*horzEdge, *eNext, m_UseFullRange)) + { + OutPt* op2 = AddOutPt(eNext, horzEdge->Bot); + AddJoin(op1, op2, horzEdge->Top); + } + } + else + UpdateEdgeIntoAEL(horzEdge); + } + else if (eMaxPair) + { + if (eMaxPair->OutIdx >= 0) + { + if (dir == dLeftToRight) + IntersectEdges(horzEdge, eMaxPair, horzEdge->Top); + else + IntersectEdges(eMaxPair, horzEdge, horzEdge->Top); + if (eMaxPair->OutIdx >= 0) + throw clipperException("ProcessHorizontal error"); + } else + { + DeleteFromAEL(horzEdge); + DeleteFromAEL(eMaxPair); + } + } else + { + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top); + DeleteFromAEL(horzEdge); + } +} +//------------------------------------------------------------------------------ + +void Clipper::UpdateEdgeIntoAEL(TEdge *&e) +{ + if( !e->NextInLML ) throw + clipperException("UpdateEdgeIntoAEL: invalid call"); + + e->NextInLML->OutIdx = e->OutIdx; + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if (AelPrev) AelPrev->NextInAEL = e->NextInLML; + else m_ActiveEdges = e->NextInLML; + if (AelNext) AelNext->PrevInAEL = e->NextInLML; + e->NextInLML->Side = e->Side; + e->NextInLML->WindDelta = e->WindDelta; + e->NextInLML->WindCnt = e->WindCnt; + e->NextInLML->WindCnt2 = e->WindCnt2; + e = e->NextInLML; + e->Curr = e->Bot; + e->PrevInAEL = AelPrev; + e->NextInAEL = AelNext; + if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y); +} +//------------------------------------------------------------------------------ + +bool Clipper::ProcessIntersections(const cInt botY, const cInt topY) +{ + if( !m_ActiveEdges ) return true; + try { + BuildIntersectList(botY, topY); + size_t IlSize = m_IntersectList.size(); + if (IlSize == 0) return true; + if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList(); + else return false; + } + catch(...) + { + m_SortedEdges = 0; + DisposeIntersectNodes(); + throw clipperException("ProcessIntersections error"); + } + m_SortedEdges = 0; + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::DisposeIntersectNodes() +{ + for (size_t i = 0; i < m_IntersectList.size(); ++i ) + delete m_IntersectList[i]; + m_IntersectList.clear(); +} +//------------------------------------------------------------------------------ + +void Clipper::BuildIntersectList(const cInt botY, const cInt topY) +{ + if ( !m_ActiveEdges ) return; + + //prepare for sorting ... + TEdge* e = m_ActiveEdges; + m_SortedEdges = e; + while( e ) + { + e->PrevInSEL = e->PrevInAEL; + e->NextInSEL = e->NextInAEL; + e->Curr.X = TopX( *e, topY ); + e = e->NextInAEL; + } + + //bubblesort ... + bool isModified; + do + { + isModified = false; + e = m_SortedEdges; + while( e->NextInSEL ) + { + TEdge *eNext = e->NextInSEL; + IntPoint Pt; + if(e->Curr.X > eNext->Curr.X) + { + if (!IntersectPoint(*e, *eNext, Pt, m_UseFullRange) && e->Curr.X > eNext->Curr.X +1) + throw clipperException("Intersection error"); + if (Pt.Y > botY) + { + Pt.Y = botY; + if (std::fabs(e->Dx) > std::fabs(eNext->Dx)) + Pt.X = TopX(*eNext, botY); else + Pt.X = TopX(*e, botY); + } + + IntersectNode * newNode = new IntersectNode; + newNode->Edge1 = e; + newNode->Edge2 = eNext; + newNode->Pt = Pt; + m_IntersectList.push_back(newNode); + + SwapPositionsInSEL(e, eNext); + isModified = true; + } + else + e = eNext; + } + if( e->PrevInSEL ) e->PrevInSEL->NextInSEL = 0; + else break; + } + while ( isModified ); + m_SortedEdges = 0; //important +} +//------------------------------------------------------------------------------ + + +void Clipper::ProcessIntersectList() +{ + for (size_t i = 0; i < m_IntersectList.size(); ++i) + { + IntersectNode* iNode = m_IntersectList[i]; + { + IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt, true); + SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 ); + } + delete iNode; + } + m_IntersectList.clear(); +} +//------------------------------------------------------------------------------ + +bool IntersectListSort(IntersectNode* node1, IntersectNode* node2) +{ + return node2->Pt.Y < node1->Pt.Y; +} +//------------------------------------------------------------------------------ + +inline bool EdgesAdjacent(const IntersectNode &inode) +{ + return (inode.Edge1->NextInSEL == inode.Edge2) || + (inode.Edge1->PrevInSEL == inode.Edge2); +} +//------------------------------------------------------------------------------ + +bool Clipper::FixupIntersectionOrder() +{ + //pre-condition: intersections are sorted Bottom-most first. + //Now it's crucial that intersections are made only between adjacent edges, + //so to ensure this the order of intersections may need adjusting ... + CopyAELToSEL(); + std::sort(m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort); + size_t cnt = m_IntersectList.size(); + for (size_t i = 0; i < cnt; ++i) + { + if (!EdgesAdjacent(*m_IntersectList[i])) + { + size_t j = i + 1; + while (j < cnt && !EdgesAdjacent(*m_IntersectList[j])) j++; + if (j == cnt) return false; + std::swap(m_IntersectList[i], m_IntersectList[j]); + } + SwapPositionsInSEL(m_IntersectList[i]->Edge1, m_IntersectList[i]->Edge2); + } + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::DoMaxima(TEdge *e) +{ + TEdge* eMaxPair = GetMaximaPair(e); + if (!eMaxPair) + { + if (e->OutIdx >= 0) + AddOutPt(e, e->Top); + DeleteFromAEL(e); + return; + } + + TEdge* eNext = e->NextInAEL; + while(eNext && eNext != eMaxPair) + { + IntersectEdges(e, eNext, e->Top, true); + SwapPositionsInAEL(e, eNext); + eNext = e->NextInAEL; + } + + if(e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned) + { + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); + } + else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 ) + { + IntersectEdges( e, eMaxPair, e->Top); + } +#ifdef use_lines + else if (e->WindDelta == 0) + { + if (e->OutIdx >= 0) + { + AddOutPt(e, e->Top); + e->OutIdx = Unassigned; + } + DeleteFromAEL(e); + + if (eMaxPair->OutIdx >= 0) + { + AddOutPt(eMaxPair, e->Top); + eMaxPair->OutIdx = Unassigned; + } + DeleteFromAEL(eMaxPair); + } +#endif + else throw clipperException("DoMaxima error"); +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) +{ + TEdge* e = m_ActiveEdges; + while( e ) + { + //1. process maxima, treating them as if they're 'bent' horizontal edges, + // but exclude maxima with horizontal edges. nb: e can't be a horizontal. + bool IsMaximaEdge = IsMaxima(e, topY); + + if(IsMaximaEdge) + { + TEdge* eMaxPair = GetMaximaPair(e); + IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair)); + } + + if(IsMaximaEdge) + { + TEdge* ePrev = e->PrevInAEL; + DoMaxima(e); + if( !ePrev ) e = m_ActiveEdges; + else e = ePrev->NextInAEL; + } + else + { + //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... + if (IsIntermediate(e, topY) && IsHorizontal(*e->NextInLML)) + { + UpdateEdgeIntoAEL(e); + if (e->OutIdx >= 0) + AddOutPt(e, e->Bot); + AddEdgeToSEL(e); + } + else + { + e->Curr.X = TopX( *e, topY ); + e->Curr.Y = topY; + } + + if (m_StrictSimple) + { + TEdge* ePrev = e->PrevInAEL; + if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) && + (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0)) + { + OutPt* op = AddOutPt(ePrev, e->Curr); + OutPt* op2 = AddOutPt(e, e->Curr); + AddJoin(op, op2, e->Curr); //StrictlySimple (type-3) join + } + } + + e = e->NextInAEL; + } + } + + //3. Process horizontals at the Top of the scanbeam ... + ProcessHorizontals(true); + + //4. Promote intermediate vertices ... + e = m_ActiveEdges; + while(e) + { + if(IsIntermediate(e, topY)) + { + OutPt* op = 0; + if( e->OutIdx >= 0 ) + op = AddOutPt(e, e->Top); + UpdateEdgeIntoAEL(e); + + //if output polygons share an edge, they'll need joining later ... + TEdge* ePrev = e->PrevInAEL; + TEdge* eNext = e->NextInAEL; + if (ePrev && ePrev->Curr.X == e->Bot.X && + ePrev->Curr.Y == e->Bot.Y && op && + ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && + SlopesEqual(*e, *ePrev, m_UseFullRange) && + (e->WindDelta != 0) && (ePrev->WindDelta != 0)) + { + OutPt* op2 = AddOutPt(ePrev, e->Bot); + AddJoin(op, op2, e->Top); + } + else if (eNext && eNext->Curr.X == e->Bot.X && + eNext->Curr.Y == e->Bot.Y && op && + eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && + SlopesEqual(*e, *eNext, m_UseFullRange) && + (e->WindDelta != 0) && (eNext->WindDelta != 0)) + { + OutPt* op2 = AddOutPt(eNext, e->Bot); + AddJoin(op, op2, e->Top); + } + } + e = e->NextInAEL; + } +} +//------------------------------------------------------------------------------ + +void Clipper::FixupOutPolygon(OutRec &outrec) +{ + //FixupOutPolygon() - removes duplicate points and simplifies consecutive + //parallel edges by removing the middle vertex. + OutPt *lastOK = 0; + outrec.BottomPt = 0; + OutPt *pp = outrec.Pts; + + for (;;) + { + if (pp->Prev == pp || pp->Prev == pp->Next ) + { + DisposeOutPts(pp); + outrec.Pts = 0; + return; + } + + //test for duplicate points and collinear edges ... + if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || + (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && + (!m_PreserveCollinear || + !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) + { + lastOK = 0; + OutPt *tmp = pp; + pp->Prev->Next = pp->Next; + pp->Next->Prev = pp->Prev; + pp = pp->Prev; + delete tmp; + } + else if (pp == lastOK) break; + else + { + if (!lastOK) lastOK = pp; + pp = pp->Next; + } + } + outrec.Pts = pp; +} +//------------------------------------------------------------------------------ + +int PointCount(OutPt *Pts) +{ + if (!Pts) return 0; + int result = 0; + OutPt* p = Pts; + do + { + result++; + p = p->Next; + } + while (p != Pts); + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResult(Paths &polys) +{ + polys.reserve(m_PolyOuts.size()); + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + if (!m_PolyOuts[i]->Pts) continue; + Path pg; + OutPt* p = m_PolyOuts[i]->Pts->Prev; + int cnt = PointCount(p); + if (cnt < 2) continue; + pg.reserve(cnt); + for (int i = 0; i < cnt; ++i) + { + pg.push_back(p->Pt); + p = p->Prev; + } + polys.push_back(pg); + } +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResult2(PolyTree& polytree) +{ + polytree.Clear(); + polytree.AllNodes.reserve(m_PolyOuts.size()); + //add each output polygon/contour to polytree ... + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) + { + OutRec* outRec = m_PolyOuts[i]; + int cnt = PointCount(outRec->Pts); + if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) continue; + FixHoleLinkage(*outRec); + PolyNode* pn = new PolyNode(); + //nb: polytree takes ownership of all the PolyNodes + polytree.AllNodes.push_back(pn); + outRec->PolyNd = pn; + pn->Parent = 0; + pn->Index = 0; + pn->Contour.reserve(cnt); + OutPt *op = outRec->Pts->Prev; + for (int j = 0; j < cnt; j++) + { + pn->Contour.push_back(op->Pt); + op = op->Prev; + } + } + + //fixup PolyNode links etc ... + polytree.Childs.reserve(m_PolyOuts.size()); + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) + { + OutRec* outRec = m_PolyOuts[i]; + if (!outRec->PolyNd) continue; + if (outRec->IsOpen) + { + outRec->PolyNd->m_IsOpen = true; + polytree.AddChild(*outRec->PolyNd); + } + else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) + outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); + else + polytree.AddChild(*outRec->PolyNd); + } +} +//------------------------------------------------------------------------------ + +void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2) +{ + //just swap the contents (because fIntersectNodes is a single-linked-list) + IntersectNode inode = int1; //gets a copy of Int1 + int1.Edge1 = int2.Edge1; + int1.Edge2 = int2.Edge2; + int1.Pt = int2.Pt; + int2.Edge1 = inode.Edge1; + int2.Edge2 = inode.Edge2; + int2.Pt = inode.Pt; +} +//------------------------------------------------------------------------------ + +inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) +{ + if (e2.Curr.X == e1.Curr.X) + { + if (e2.Top.Y > e1.Top.Y) + return e2.Top.X < TopX(e1, e2.Top.Y); + else return e1.Top.X > TopX(e2, e1.Top.Y); + } + else return e2.Curr.X < e1.Curr.X; +} +//------------------------------------------------------------------------------ + +bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2, + cInt& Left, cInt& Right) +{ + if (a1 < a2) + { + if (b1 < b2) {Left = std::max(a1,b1); Right = std::min(a2,b2);} + else {Left = std::max(a1,b2); Right = std::min(a2,b1);} + } + else + { + if (b1 < b2) {Left = std::max(a2,b1); Right = std::min(a1,b2);} + else {Left = std::max(a2,b2); Right = std::min(a1,b1);} + } + return Left < Right; +} +//------------------------------------------------------------------------------ + +inline void UpdateOutPtIdxs(OutRec& outrec) +{ + OutPt* op = outrec.Pts; + do + { + op->Idx = outrec.Idx; + op = op->Prev; + } + while(op != outrec.Pts); +} +//------------------------------------------------------------------------------ + +void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge) +{ + if(!m_ActiveEdges) + { + edge->PrevInAEL = 0; + edge->NextInAEL = 0; + m_ActiveEdges = edge; + } + else if(!startEdge && E2InsertsBeforeE1(*m_ActiveEdges, *edge)) + { + edge->PrevInAEL = 0; + edge->NextInAEL = m_ActiveEdges; + m_ActiveEdges->PrevInAEL = edge; + m_ActiveEdges = edge; + } + else + { + if(!startEdge) startEdge = m_ActiveEdges; + while(startEdge->NextInAEL && + !E2InsertsBeforeE1(*startEdge->NextInAEL , *edge)) + startEdge = startEdge->NextInAEL; + edge->NextInAEL = startEdge->NextInAEL; + if(startEdge->NextInAEL) startEdge->NextInAEL->PrevInAEL = edge; + edge->PrevInAEL = startEdge; + startEdge->NextInAEL = edge; + } +} +//---------------------------------------------------------------------- + +OutPt* DupOutPt(OutPt* outPt, bool InsertAfter) +{ + OutPt* result = new OutPt; + result->Pt = outPt->Pt; + result->Idx = outPt->Idx; + if (InsertAfter) + { + result->Next = outPt->Next; + result->Prev = outPt; + outPt->Next->Prev = result; + outPt->Next = result; + } + else + { + result->Prev = outPt->Prev; + result->Next = outPt; + outPt->Prev->Next = result; + outPt->Prev = result; + } + return result; +} +//------------------------------------------------------------------------------ + +bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, + const IntPoint Pt, bool DiscardLeft) +{ + Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight); + Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight); + if (Dir1 == Dir2) return false; + + //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we + //want Op1b to be on the Right. (And likewise with Op2 and Op2b.) + //So, to facilitate this while inserting Op1b and Op2b ... + //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, + //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) + if (Dir1 == dLeftToRight) + { + while (op1->Next->Pt.X <= Pt.X && + op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + op1 = op1->Next; + if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; + op1b = DupOutPt(op1, !DiscardLeft); + if (op1b->Pt != Pt) + { + op1 = op1b; + op1->Pt = Pt; + op1b = DupOutPt(op1, !DiscardLeft); + } + } + else + { + while (op1->Next->Pt.X >= Pt.X && + op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + op1 = op1->Next; + if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; + op1b = DupOutPt(op1, DiscardLeft); + if (op1b->Pt != Pt) + { + op1 = op1b; + op1->Pt = Pt; + op1b = DupOutPt(op1, DiscardLeft); + } + } + + if (Dir2 == dLeftToRight) + { + while (op2->Next->Pt.X <= Pt.X && + op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + op2 = op2->Next; + if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; + op2b = DupOutPt(op2, !DiscardLeft); + if (op2b->Pt != Pt) + { + op2 = op2b; + op2->Pt = Pt; + op2b = DupOutPt(op2, !DiscardLeft); + }; + } else + { + while (op2->Next->Pt.X >= Pt.X && + op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + op2 = op2->Next; + if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; + op2b = DupOutPt(op2, DiscardLeft); + if (op2b->Pt != Pt) + { + op2 = op2b; + op2->Pt = Pt; + op2b = DupOutPt(op2, DiscardLeft); + }; + }; + + if ((Dir1 == dLeftToRight) == DiscardLeft) + { + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + } + else + { + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + } + return true; +} +//------------------------------------------------------------------------------ + +bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) +{ + OutPt *op1 = j->OutPt1, *op1b; + OutPt *op2 = j->OutPt2, *op2b; + + //There are 3 kinds of joins for output polygons ... + //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are a vertices anywhere + //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). + //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same + //location at the Bottom of the overlapping segment (& Join.OffPt is above). + //3. StrictSimple joins where edges touch but are not collinear and where + //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. + bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y); + + if (isHorizontal && (j->OffPt == j->OutPt1->Pt) && + (j->OffPt == j->OutPt2->Pt)) + { + //Strictly Simple join ... + op1b = j->OutPt1->Next; + while (op1b != op1 && (op1b->Pt == j->OffPt)) + op1b = op1b->Next; + bool reverse1 = (op1b->Pt.Y > j->OffPt.Y); + op2b = j->OutPt2->Next; + while (op2b != op2 && (op2b->Pt == j->OffPt)) + op2b = op2b->Next; + bool reverse2 = (op2b->Pt.Y > j->OffPt.Y); + if (reverse1 == reverse2) return false; + if (reverse1) + { + op1b = DupOutPt(op1, false); + op2b = DupOutPt(op2, true); + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } else + { + op1b = DupOutPt(op1, true); + op2b = DupOutPt(op2, false); + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } + } + else if (isHorizontal) + { + //treat horizontal joins differently to non-horizontal joins since with + //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt + //may be anywhere along the horizontal edge. + op1b = op1; + while (op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2) + op1 = op1->Prev; + while (op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2) + op1b = op1b->Next; + if (op1b->Next == op1 || op1b->Next == op2) return false; //a flat 'polygon' + + op2b = op2; + while (op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b) + op2 = op2->Prev; + while (op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1) + op2b = op2b->Next; + if (op2b->Next == op2 || op2b->Next == op1) return false; //a flat 'polygon' + + cInt Left, Right; + //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges + if (!GetOverlap(op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right)) + return false; + + //DiscardLeftSide: when overlapping edges are joined, a spike will created + //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up + //on the discard Side as either may still be needed for other joins ... + IntPoint Pt; + bool DiscardLeftSide; + if (op1->Pt.X >= Left && op1->Pt.X <= Right) + { + Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X); + } + else if (op2->Pt.X >= Left&& op2->Pt.X <= Right) + { + Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X); + } + else if (op1b->Pt.X >= Left && op1b->Pt.X <= Right) + { + Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X; + } + else + { + Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X); + } + j->OutPt1 = op1; j->OutPt2 = op2; + return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide); + } else + { + //nb: For non-horizontal joins ... + // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y + // 2. Jr.OutPt1.Pt > Jr.OffPt.Y + + //make sure the polygons are correctly oriented ... + op1b = op1->Next; + while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Next; + bool Reverse1 = ((op1b->Pt.Y > op1->Pt.Y) || + !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)); + if (Reverse1) + { + op1b = op1->Prev; + while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Prev; + if ((op1b->Pt.Y > op1->Pt.Y) || + !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)) return false; + }; + op2b = op2->Next; + while ((op2b->Pt == op2->Pt) && (op2b != op2))op2b = op2b->Next; + bool Reverse2 = ((op2b->Pt.Y > op2->Pt.Y) || + !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)); + if (Reverse2) + { + op2b = op2->Prev; + while ((op2b->Pt == op2->Pt) && (op2b != op2)) op2b = op2b->Prev; + if ((op2b->Pt.Y > op2->Pt.Y) || + !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)) return false; + } + + if ((op1b == op1) || (op2b == op2) || (op1b == op2b) || + ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false; + + if (Reverse1) + { + op1b = DupOutPt(op1, false); + op2b = DupOutPt(op2, true); + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } else + { + op1b = DupOutPt(op1, true); + op2b = DupOutPt(op2, false); + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } + } +} +//---------------------------------------------------------------------- + +void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) +{ + + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + if (outRec->Pts && outRec->FirstLeft == OldOutRec) + { + if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) + outRec->FirstLeft = NewOutRec; + } + } +} +//---------------------------------------------------------------------- + +void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) +{ + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + if (outRec->FirstLeft == OldOutRec) outRec->FirstLeft = NewOutRec; + } +} +//---------------------------------------------------------------------- + +static OutRec* ParseFirstLeft(OutRec* FirstLeft) +{ + while (FirstLeft && !FirstLeft->Pts) + FirstLeft = FirstLeft->FirstLeft; + return FirstLeft; +} +//------------------------------------------------------------------------------ + +void Clipper::JoinCommonEdges() +{ + for (JoinList::size_type i = 0; i < m_Joins.size(); i++) + { + Join* join = m_Joins[i]; + + OutRec *outRec1 = GetOutRec(join->OutPt1->Idx); + OutRec *outRec2 = GetOutRec(join->OutPt2->Idx); + + if (!outRec1->Pts || !outRec2->Pts) continue; + + //get the polygon fragment with the correct hole state (FirstLeft) + //before calling JoinPoints() ... + OutRec *holeStateRec; + if (outRec1 == outRec2) holeStateRec = outRec1; + else if (Param1RightOfParam2(outRec1, outRec2)) holeStateRec = outRec2; + else if (Param1RightOfParam2(outRec2, outRec1)) holeStateRec = outRec1; + else holeStateRec = GetLowermostRec(outRec1, outRec2); + + if (!JoinPoints(join, outRec1, outRec2)) continue; + + if (outRec1 == outRec2) + { + //instead of joining two polygons, we've just created a new one by + //splitting one polygon into two. + outRec1->Pts = join->OutPt1; + outRec1->BottomPt = 0; + outRec2 = CreateOutRec(); + outRec2->Pts = join->OutPt2; + + //update all OutRec2.Pts Idx's ... + UpdateOutPtIdxs(*outRec2); + + //We now need to check every OutRec.FirstLeft pointer. If it points + //to OutRec1 it may need to point to OutRec2 instead ... + if (m_UsingPolyTree) + for (PolyOutList::size_type j = 0; j < m_PolyOuts.size() - 1; j++) + { + OutRec* oRec = m_PolyOuts[j]; + if (!oRec->Pts || ParseFirstLeft(oRec->FirstLeft) != outRec1 || + oRec->IsHole == outRec1->IsHole) continue; + if (Poly2ContainsPoly1(oRec->Pts, join->OutPt2)) + oRec->FirstLeft = outRec2; + } + + if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) + { + //outRec2 is contained by outRec1 ... + outRec2->IsHole = !outRec1->IsHole; + outRec2->FirstLeft = outRec1; + + //fixup FirstLeft pointers that may need reassigning to OutRec1 + if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); + + if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0)) + ReversePolyPtLinks(outRec2->Pts); + + } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts)) + { + //outRec1 is contained by outRec2 ... + outRec2->IsHole = outRec1->IsHole; + outRec1->IsHole = !outRec2->IsHole; + outRec2->FirstLeft = outRec1->FirstLeft; + outRec1->FirstLeft = outRec2; + + //fixup FirstLeft pointers that may need reassigning to OutRec1 + if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2); + + if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0)) + ReversePolyPtLinks(outRec1->Pts); + } + else + { + //the 2 polygons are completely separate ... + outRec2->IsHole = outRec1->IsHole; + outRec2->FirstLeft = outRec1->FirstLeft; + + //fixup FirstLeft pointers that may need reassigning to OutRec2 + if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2); + } + + } else + { + //joined 2 polygons together ... + + outRec2->Pts = 0; + outRec2->BottomPt = 0; + outRec2->Idx = outRec1->Idx; + + outRec1->IsHole = holeStateRec->IsHole; + if (holeStateRec == outRec2) + outRec1->FirstLeft = outRec2->FirstLeft; + outRec2->FirstLeft = outRec1; + + //fixup FirstLeft pointers that may need reassigning to OutRec1 + if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); + } + } +} + +//------------------------------------------------------------------------------ +// ClipperOffset support functions ... +//------------------------------------------------------------------------------ + +DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2) +{ + if(pt2.X == pt1.X && pt2.Y == pt1.Y) + return DoublePoint(0, 0); + + double Dx = (double)(pt2.X - pt1.X); + double dy = (double)(pt2.Y - pt1.Y); + double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy ); + Dx *= f; + dy *= f; + return DoublePoint(dy, -Dx); +} + +//------------------------------------------------------------------------------ +// ClipperOffset class +//------------------------------------------------------------------------------ + +ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance) +{ + this->MiterLimit = miterLimit; + this->ArcTolerance = arcTolerance; + m_lowest.X = -1; +} +//------------------------------------------------------------------------------ + +ClipperOffset::~ClipperOffset() +{ + Clear(); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Clear() +{ + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + delete m_polyNodes.Childs[i]; + m_polyNodes.Childs.clear(); + m_lowest.X = -1; +} +//------------------------------------------------------------------------------ + +void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType) +{ + int highI = (int)path.size() - 1; + if (highI < 0) return; + PolyNode* newNode = new PolyNode(); + newNode->m_jointype = joinType; + newNode->m_endtype = endType; + + //strip duplicate points from path and also get index to the lowest point ... + if (endType == etClosedLine || endType == etClosedPolygon) + while (highI > 0 && path[0] == path[highI]) highI--; + newNode->Contour.reserve(highI + 1); + newNode->Contour.push_back(path[0]); + int j = 0, k = 0; + for (int i = 1; i <= highI; i++) + if (newNode->Contour[j] != path[i]) + { + j++; + newNode->Contour.push_back(path[i]); + if (path[i].Y > newNode->Contour[k].Y || + (path[i].Y == newNode->Contour[k].Y && + path[i].X < newNode->Contour[k].X)) k = j; + } + if ((endType == etClosedPolygon && j < 2) || + (endType != etClosedPolygon && j < 0)) + { + delete newNode; + return; + } + m_polyNodes.AddChild(*newNode); + + //if this path's lowest pt is lower than all the others then update m_lowest + if (endType != etClosedPolygon) return; + if (m_lowest.X < 0) + m_lowest = IntPoint(0, k); + else + { + IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y]; + if (newNode->Contour[k].Y > ip.Y || + (newNode->Contour[k].Y == ip.Y && + newNode->Contour[k].X < ip.X)) + m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType) +{ + for (Paths::size_type i = 0; i < paths.size(); ++i) + AddPath(paths[i], joinType, endType); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::FixOrientations() +{ + //fixup orientations of all closed paths if the orientation of the + //closed path with the lowermost vertex is wrong ... + if (m_lowest.X >= 0 && + !Orientation(m_polyNodes.Childs[(int)m_lowest.X]->Contour)) + { + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedPolygon || + (node.m_endtype == etClosedLine && Orientation(node.Contour))) + ReversePath(node.Contour); + } + } else + { + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedLine && !Orientation(node.Contour)) + ReversePath(node.Contour); + } + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Execute(Paths& solution, double delta) +{ + solution.clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr; + clpr.AddPaths(m_destPolys, ptSubject, true); + if (delta > 0) + { + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + } + else + { + IntRect r = clpr.GetBounds(); + Path outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPath(outer, ptSubject, true); + clpr.ReverseSolution(true); + clpr.Execute(ctUnion, solution, pftNegative, pftNegative); + if (solution.size() > 0) solution.erase(solution.begin()); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Execute(PolyTree& solution, double delta) +{ + solution.Clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr; + clpr.AddPaths(m_destPolys, ptSubject, true); + if (delta > 0) + { + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + } + else + { + IntRect r = clpr.GetBounds(); + Path outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPath(outer, ptSubject, true); + clpr.ReverseSolution(true); + clpr.Execute(ctUnion, solution, pftNegative, pftNegative); + //remove the outer PolyNode rectangle ... + if (solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0) + { + PolyNode* outerNode = solution.Childs[0]; + solution.Childs.reserve(outerNode->ChildCount()); + solution.Childs[0] = outerNode->Childs[0]; + for (int i = 1; i < outerNode->ChildCount(); ++i) + solution.AddChild(*outerNode->Childs[i]); + } + else + solution.Clear(); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoOffset(double delta) +{ + m_destPolys.clear(); + m_delta = delta; + + //if Zero offset, just copy any CLOSED polygons to m_p and return ... + if (NEAR_ZERO(delta)) + { + m_destPolys.reserve(m_polyNodes.ChildCount()); + for (int i = 0; i < m_polyNodes.ChildCount(); i++) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedPolygon) + m_destPolys.push_back(node.Contour); + } + return; + } + + //see offset_triginometry3.svg in the documentation folder ... + if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit); + else m_miterLim = 0.5; + + double y; + if (ArcTolerance <= 0.0) y = def_arc_tolerance; + else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance) + y = std::fabs(delta) * def_arc_tolerance; + else y = ArcTolerance; + //see offset_triginometry2.svg in the documentation folder ... + double steps = pi / std::acos(1 - y / std::fabs(delta)); + if (steps > std::fabs(delta) * pi) + steps = std::fabs(delta) * pi; //ie excessive precision check + m_sin = std::sin(two_pi / steps); + m_cos = std::cos(two_pi / steps); + m_StepsPerRad = steps / two_pi; + if (delta < 0.0) m_sin = -m_sin; + + m_destPolys.reserve(m_polyNodes.ChildCount() * 2); + for (int i = 0; i < m_polyNodes.ChildCount(); i++) + { + PolyNode& node = *m_polyNodes.Childs[i]; + m_srcPoly = node.Contour; + + int len = (int)m_srcPoly.size(); + if (len == 0 || (delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon))) + continue; + + m_destPoly.clear(); + if (len == 1) + { + if (node.m_jointype == jtRound) + { + double X = 1.0, Y = 0.0; + for (cInt j = 1; j <= steps; j++) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + double X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + } + else + { + double X = -1.0, Y = -1.0; + for (int j = 0; j < 4; ++j) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + if (X < 0) X = 1; + else if (Y < 0) Y = 1; + else X = -1; + } + } + m_destPolys.push_back(m_destPoly); + continue; + } + //build m_normals ... + m_normals.clear(); + m_normals.reserve(len); + for (int j = 0; j < len - 1; ++j) + m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); + if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon) + m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); + else + m_normals.push_back(DoublePoint(m_normals[len - 2])); + + if (node.m_endtype == etClosedPolygon) + { + int k = len - 1; + for (int j = 0; j < len; ++j) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + } + else if (node.m_endtype == etClosedLine) + { + int k = len - 1; + for (int j = 0; j < len; ++j) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + m_destPoly.clear(); + //re-build m_normals ... + DoublePoint n = m_normals[len -1]; + for (int j = len - 1; j > 0; j--) + m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = DoublePoint(-n.X, -n.Y); + k = 0; + for (int j = len - 1; j >= 0; j--) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + } + else + { + int k = 0; + for (int j = 1; j < len - 1; ++j) + OffsetPoint(j, k, node.m_jointype); + + IntPoint pt1; + if (node.m_endtype == etOpenButt) + { + int j = len - 1; + pt1 = IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta)); + m_destPoly.push_back(pt1); + pt1 = IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta)); + m_destPoly.push_back(pt1); + } + else + { + int j = len - 1; + k = len - 2; + m_sinA = 0; + m_normals[j] = DoublePoint(-m_normals[j].X, -m_normals[j].Y); + if (node.m_endtype == etOpenSquare) + DoSquare(j, k); + else + DoRound(j, k); + } + + //re-build m_normals ... + for (int j = len - 1; j > 0; j--) + m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = DoublePoint(-m_normals[1].X, -m_normals[1].Y); + + k = len - 1; + for (int j = k - 1; j > 0; --j) OffsetPoint(j, k, node.m_jointype); + + if (node.m_endtype == etOpenButt) + { + pt1 = IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta)); + m_destPoly.push_back(pt1); + pt1 = IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta)); + m_destPoly.push_back(pt1); + } + else + { + k = 1; + m_sinA = 0; + if (node.m_endtype == etOpenSquare) + DoSquare(0, 1); + else + DoRound(0, 1); + } + m_destPolys.push_back(m_destPoly); + } + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) +{ + m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y); + if (m_sinA < 0.00005 && m_sinA > -0.00005) return; + else if (m_sinA > 1.0) m_sinA = 1.0; + else if (m_sinA < -1.0) m_sinA = -1.0; + + if (m_sinA * m_delta < 0) + { + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + m_destPoly.push_back(m_srcPoly[j]); + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); + } + else + switch (jointype) + { + case jtMiter: + { + double r = 1 + (m_normals[j].X * m_normals[k].X + + m_normals[j].Y * m_normals[k].Y); + if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k); + break; + } + case jtSquare: DoSquare(j, k); break; + case jtRound: DoRound(j, k); break; + } + k = j; +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoSquare(int j, int k) +{ + double dx = std::tan(std::atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4); + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx)))); + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx)))); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoMiter(int j, int k, double r) +{ + double q = m_delta / r; + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q), + Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q))); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoRound(int j, int k) +{ + double a = std::atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y); + int steps = (int)Round(m_StepsPerRad * std::fabs(a)); + + double X = m_normals[k].X, Y = m_normals[k].Y, X2; + for (int i = 0; i < steps; ++i) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + X * m_delta), + Round(m_srcPoly[j].Y + Y * m_delta))); + X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); +} + +//------------------------------------------------------------------------------ +// Miscellaneous public functions +//------------------------------------------------------------------------------ + +void Clipper::DoSimplePolygons() +{ + PolyOutList::size_type i = 0; + while (i < m_PolyOuts.size()) + { + OutRec* outrec = m_PolyOuts[i++]; + OutPt* op = outrec->Pts; + if (!op) continue; + do //for each Pt in Polygon until duplicate found do ... + { + OutPt* op2 = op->Next; + while (op2 != outrec->Pts) + { + if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) + { + //split the polygon into two ... + OutPt* op3 = op->Prev; + OutPt* op4 = op2->Prev; + op->Prev = op4; + op4->Next = op; + op2->Prev = op3; + op3->Next = op2; + + outrec->Pts = op; + OutRec* outrec2 = CreateOutRec(); + outrec2->Pts = op2; + UpdateOutPtIdxs(*outrec2); + if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts)) + { + //OutRec2 is contained by OutRec1 ... + outrec2->IsHole = !outrec->IsHole; + outrec2->FirstLeft = outrec; + } + else + if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) + { + //OutRec1 is contained by OutRec2 ... + outrec2->IsHole = outrec->IsHole; + outrec->IsHole = !outrec2->IsHole; + outrec2->FirstLeft = outrec->FirstLeft; + outrec->FirstLeft = outrec2; + } else + { + //the 2 polygons are separate ... + outrec2->IsHole = outrec->IsHole; + outrec2->FirstLeft = outrec->FirstLeft; + } + op2 = op; //ie get ready for the Next iteration + } + op2 = op2->Next; + } + op = op->Next; + } + while (op != outrec->Pts); + } +} +//------------------------------------------------------------------------------ + +void ReversePath(Path& p) +{ + std::reverse(p.begin(), p.end()); +} +//------------------------------------------------------------------------------ + +void ReversePaths(Paths& p) +{ + for (Paths::size_type i = 0; i < p.size(); ++i) + ReversePath(p[i]); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType) +{ + Clipper c; + c.StrictlySimple(true); + c.AddPath(in_poly, ptSubject, true); + c.Execute(ctUnion, out_polys, fillType, fillType); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType) +{ + Clipper c; + c.StrictlySimple(true); + c.AddPaths(in_polys, ptSubject, true); + c.Execute(ctUnion, out_polys, fillType, fillType); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(Paths &polys, PolyFillType fillType) +{ + SimplifyPolygons(polys, polys, fillType); +} +//------------------------------------------------------------------------------ + +inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2) +{ + double Dx = ((double)pt1.X - pt2.X); + double dy = ((double)pt1.Y - pt2.Y); + return (Dx*Dx + dy*dy); +} +//------------------------------------------------------------------------------ + +double DistanceFromLineSqrd( + const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2) +{ + //The equation of a line in general form (Ax + By + C = 0) + //given 2 points (x¹,y¹) & (x²,y²) is ... + //(y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0 + //A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹ + //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) + //see http://en.wikipedia.org/wiki/Perpendicular_distance + double A = double(ln1.Y - ln2.Y); + double B = double(ln2.X - ln1.X); + double C = A * ln1.X + B * ln1.Y; + C = A * pt.X + B * pt.Y - C; + return (C * C) / (A * A + B * B); +} +//--------------------------------------------------------------------------- + +bool SlopesNearCollinear(const IntPoint& pt1, + const IntPoint& pt2, const IntPoint& pt3, double distSqrd) +{ + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; +} +//------------------------------------------------------------------------------ + +bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd) +{ + double Dx = (double)pt1.X - pt2.X; + double dy = (double)pt1.Y - pt2.Y; + return ((Dx * Dx) + (dy * dy) <= distSqrd); +} +//------------------------------------------------------------------------------ + +OutPt* ExcludeOp(OutPt* op) +{ + OutPt* result = op->Prev; + result->Next = op->Next; + op->Next->Prev = result; + result->Idx = 0; + return result; +} +//------------------------------------------------------------------------------ + +void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) +{ + //distance = proximity in units/pixels below which vertices + //will be stripped. Default ~= sqrt(2). + + size_t size = in_poly.size(); + + if (size == 0) + { + out_poly.clear(); + return; + } + + OutPt* outPts = new OutPt[size]; + for (size_t i = 0; i < size; ++i) + { + outPts[i].Pt = in_poly[i]; + outPts[i].Next = &outPts[(i + 1) % size]; + outPts[i].Next->Prev = &outPts[i]; + outPts[i].Idx = 0; + } + + double distSqrd = distance * distance; + OutPt* op = &outPts[0]; + while (op->Idx == 0 && op->Next != op->Prev) + { + if (PointsAreClose(op->Pt, op->Prev->Pt, distSqrd)) + { + op = ExcludeOp(op); + size--; + } + else if (PointsAreClose(op->Prev->Pt, op->Next->Pt, distSqrd)) + { + ExcludeOp(op->Next); + op = ExcludeOp(op); + size -= 2; + } + else if (SlopesNearCollinear(op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd)) + { + op = ExcludeOp(op); + size--; + } + else + { + op->Idx = 1; + op = op->Next; + } + } + + if (size < 3) size = 0; + out_poly.resize(size); + for (size_t i = 0; i < size; ++i) + { + out_poly[i] = op->Pt; + op = op->Next; + } + delete [] outPts; +} +//------------------------------------------------------------------------------ + +void CleanPolygon(Path& poly, double distance) +{ + CleanPolygon(poly, poly, distance); +} +//------------------------------------------------------------------------------ + +void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance) +{ + for (Paths::size_type i = 0; i < in_polys.size(); ++i) + CleanPolygon(in_polys[i], out_polys[i], distance); +} +//------------------------------------------------------------------------------ + +void CleanPolygons(Paths& polys, double distance) +{ + CleanPolygons(polys, polys, distance); +} +//------------------------------------------------------------------------------ + +void Minkowski(const Path& poly, const Path& path, + Paths& solution, bool isSum, bool isClosed) +{ + int delta = (isClosed ? 1 : 0); + size_t polyCnt = poly.size(); + size_t pathCnt = path.size(); + Paths pp; + pp.reserve(pathCnt); + if (isSum) + for (size_t i = 0; i < pathCnt; ++i) + { + Path p; + p.reserve(polyCnt); + for (size_t j = 0; j < poly.size(); ++j) + p.push_back(IntPoint(path[i].X + poly[j].X, path[i].Y + poly[j].Y)); + pp.push_back(p); + } + else + for (size_t i = 0; i < pathCnt; ++i) + { + Path p; + p.reserve(polyCnt); + for (size_t j = 0; j < poly.size(); ++j) + p.push_back(IntPoint(path[i].X - poly[j].X, path[i].Y - poly[j].Y)); + pp.push_back(p); + } + + Paths quads; + quads.reserve((pathCnt + delta) * (polyCnt + 1)); + for (size_t i = 0; i < pathCnt - 1 + delta; ++i) + for (size_t j = 0; j < polyCnt; ++j) + { + Path quad; + quad.reserve(4); + quad.push_back(pp[i % pathCnt][j % polyCnt]); + quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]); + quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); + quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]); + if (!Orientation(quad)) ReversePath(quad); + quads.push_back(quad); + } + + Clipper c; + c.AddPaths(quads, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed) +{ + Minkowski(pattern, path, solution, true, pathIsClosed); +} +//------------------------------------------------------------------------------ + +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, + PolyFillType pathFillType, bool pathIsClosed) +{ + Clipper c; + for (size_t i = 0; i < paths.size(); ++i) + { + Paths tmp; + Minkowski(pattern, paths[i], tmp, true, pathIsClosed); + c.AddPaths(tmp, ptSubject, true); + } + if (pathIsClosed) c.AddPaths(paths, ptClip, true); + c.Execute(ctUnion, solution, pathFillType, pathFillType); +} +//------------------------------------------------------------------------------ + +void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution) +{ + Minkowski(poly1, poly2, solution, false, true); +} +//------------------------------------------------------------------------------ + +enum NodeType {ntAny, ntOpen, ntClosed}; + +void AddPolyNodeToPolygons(const PolyNode& polynode, NodeType nodetype, Paths& paths) +{ + bool match = true; + if (nodetype == ntClosed) match = !polynode.IsOpen(); + else if (nodetype == ntOpen) return; + + if (!polynode.Contour.empty() && match) + paths.push_back(polynode.Contour); + for (int i = 0; i < polynode.ChildCount(); ++i) + AddPolyNodeToPolygons(*polynode.Childs[i], nodetype, paths); +} +//------------------------------------------------------------------------------ + +void PolyTreeToPaths(const PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + AddPolyNodeToPolygons(polytree, ntAny, paths); +} +//------------------------------------------------------------------------------ + +void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + AddPolyNodeToPolygons(polytree, ntClosed, paths); +} +//------------------------------------------------------------------------------ + +void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + //Open paths are top level only, so ... + for (int i = 0; i < polytree.ChildCount(); ++i) + if (polytree.Childs[i]->IsOpen()) + paths.push_back(polytree.Childs[i]->Contour); +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const IntPoint &p) +{ + s << "(" << p.X << "," << p.Y << ")"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const Path &p) +{ + if (p.empty()) return s; + Path::size_type last = p.size() -1; + for (Path::size_type i = 0; i < last; i++) + s << "(" << p[i].X << "," << p[i].Y << "), "; + s << "(" << p[last].X << "," << p[last].Y << ")\n"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const Paths &p) +{ + for (Paths::size_type i = 0; i < p.size(); i++) + s << p[i]; + s << "\n"; + return s; +} +//------------------------------------------------------------------------------ + +#ifdef use_deprecated + +void OffsetPaths(const Paths &in_polys, Paths &out_polys, + double delta, JoinType jointype, EndType_ endtype, double limit) +{ + ClipperOffset co(limit, limit); + co.AddPaths(in_polys, jointype, (EndType)endtype); + co.Execute(out_polys, delta); +} +//------------------------------------------------------------------------------ + +#endif + + +} //ClipperLib namespace diff --git a/main/lib/clipper/clipper.hpp b/main/lib/clipper/clipper.hpp new file mode 100644 index 00000000..da680777 --- /dev/null +++ b/main/lib/clipper/clipper.hpp @@ -0,0 +1,398 @@ +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 6.1.3a * +* Date : 22 January 2014 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2014 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +#ifndef clipper_hpp +#define clipper_hpp + +#define CLIPPER_VERSION "6.1.3" + +//use_int32: When enabled 32bit ints are used instead of 64bit ints. This +//improve performance but coordinate values are limited to the range +/- 46340 +//#define use_int32 + +//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. +//#define use_xyz + +//use_lines: Enables line clipping. Adds a very minor cost to performance. +//#define use_lines + +//use_deprecated: Enables support for the obsolete OffsetPaths() function +//which has been replace with the ClipperOffset class. +#define use_deprecated + +#include +#include +#include +#include +#include +#include +#include + +namespace ClipperLib { + +enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; +enum PolyType { ptSubject, ptClip }; +//By far the most widely used winding rules for polygon filling are +//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) +//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) +//see http://glprogramming.com/red/chapter11.html +enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; + +#ifdef use_int32 +typedef int cInt; +typedef unsigned int cUInt; +#else +typedef signed long long cInt; +typedef unsigned long long cUInt; +#endif + +struct IntPoint { + cInt X; + cInt Y; +#ifdef use_xyz + cInt Z; + IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; +#else + IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; +#endif + + friend inline bool operator== (const IntPoint& a, const IntPoint& b) + { + return a.X == b.X && a.Y == b.Y; + } + friend inline bool operator!= (const IntPoint& a, const IntPoint& b) + { + return a.X != b.X || a.Y != b.Y; + } +}; +//------------------------------------------------------------------------------ + +typedef std::vector< IntPoint > Path; +typedef std::vector< Path > Paths; + +inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} +inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} + +std::ostream& operator <<(std::ostream &s, const IntPoint &p); +std::ostream& operator <<(std::ostream &s, const Path &p); +std::ostream& operator <<(std::ostream &s, const Paths &p); + +struct DoublePoint +{ + double X; + double Y; + DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} + DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} +}; +//------------------------------------------------------------------------------ + +#ifdef use_xyz +typedef void (*TZFillCallback)(IntPoint& z1, IntPoint& z2, IntPoint& pt); +#endif + +enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; +enum JoinType {jtSquare, jtRound, jtMiter}; +enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; +#ifdef use_deprecated + enum EndType_ {etClosed, etButt = 2, etSquare, etRound}; +#endif + +class PolyNode; +typedef std::vector< PolyNode* > PolyNodes; + +class PolyNode +{ +public: + PolyNode(); + Path Contour; + PolyNodes Childs; + PolyNode* Parent; + PolyNode* GetNext() const; + bool IsHole() const; + bool IsOpen() const; + int ChildCount() const; +private: + unsigned Index; //node index in Parent.Childs + bool m_IsOpen; + JoinType m_jointype; + EndType m_endtype; + PolyNode* GetNextSiblingUp() const; + void AddChild(PolyNode& child); + friend class Clipper; //to access Index + friend class ClipperOffset; +}; + +class PolyTree: public PolyNode +{ +public: + ~PolyTree(){Clear();}; + PolyNode* GetFirst() const; + void Clear(); + int Total() const; +private: + PolyNodes AllNodes; + friend class Clipper; //to access AllNodes +}; + +bool Orientation(const Path &poly); +double Area(const Path &poly); +int PointInPolygon(const IntPoint &pt, const Path &path); + +#ifdef use_deprecated + void OffsetPaths(const Paths &in_polys, Paths &out_polys, + double delta, JoinType jointype, EndType_ endtype, double limit = 0); +#endif + +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); + +void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); +void CleanPolygon(Path& poly, double distance = 1.415); +void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); +void CleanPolygons(Paths& polys, double distance = 1.415); + +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); +void MinkowskiSum(const Path& pattern, const Paths& paths, + Paths& solution, PolyFillType pathFillType, bool pathIsClosed); +void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); + +void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); +void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); +void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); + +void ReversePath(Path& p); +void ReversePaths(Paths& p); + +struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; + +//enums that are used internally ... +enum EdgeSide { esLeft = 1, esRight = 2}; + +//forward declarations (for stuff used internally) ... +struct TEdge; +struct IntersectNode; +struct LocalMinima; +struct Scanbeam; +struct OutPt; +struct OutRec; +struct Join; + +typedef std::vector < OutRec* > PolyOutList; +typedef std::vector < TEdge* > EdgeList; +typedef std::vector < Join* > JoinList; +typedef std::vector < IntersectNode* > IntersectList; + + +//------------------------------------------------------------------------------ + +//ClipperBase is the ancestor to the Clipper class. It should not be +//instantiated directly. This class simply abstracts the conversion of sets of +//polygon coordinates into edge objects that are stored in a LocalMinima list. +class ClipperBase +{ +public: + ClipperBase(); + virtual ~ClipperBase(); + bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); + bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); + virtual void Clear(); + IntRect GetBounds(); + bool PreserveCollinear() {return m_PreserveCollinear;}; + void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; +protected: + void DisposeLocalMinimaList(); + TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); + void PopLocalMinima(); + virtual void Reset(); + TEdge* ProcessBound(TEdge* E, bool IsClockwise); + void InsertLocalMinima(LocalMinima *newLm); + void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed); + TEdge* DescendToMin(TEdge *&E); + void AscendToMax(TEdge *&E, bool Appending, bool IsClosed); + LocalMinima *m_CurrentLM; + LocalMinima *m_MinimaList; + bool m_UseFullRange; + EdgeList m_edges; + bool m_PreserveCollinear; + bool m_HasOpenPaths; +}; +//------------------------------------------------------------------------------ + +class Clipper : public virtual ClipperBase +{ +public: + Clipper(int initOptions = 0); + ~Clipper(); + bool Execute(ClipType clipType, + Paths &solution, + PolyFillType subjFillType = pftEvenOdd, + PolyFillType clipFillType = pftEvenOdd); + bool Execute(ClipType clipType, + PolyTree &polytree, + PolyFillType subjFillType = pftEvenOdd, + PolyFillType clipFillType = pftEvenOdd); + bool ReverseSolution() {return m_ReverseOutput;}; + void ReverseSolution(bool value) {m_ReverseOutput = value;}; + bool StrictlySimple() {return m_StrictSimple;}; + void StrictlySimple(bool value) {m_StrictSimple = value;}; + //set the callback function for z value filling on intersections (otherwise Z is 0) +#ifdef use_xyz + void ZFillFunction(TZFillCallback zFillFunc); +#endif +protected: + void Reset(); + virtual bool ExecuteInternal(); +private: + PolyOutList m_PolyOuts; + JoinList m_Joins; + JoinList m_GhostJoins; + IntersectList m_IntersectList; + ClipType m_ClipType; + std::set< cInt, std::greater > m_Scanbeam; + TEdge *m_ActiveEdges; + TEdge *m_SortedEdges; + bool m_ExecuteLocked; + PolyFillType m_ClipFillType; + PolyFillType m_SubjFillType; + bool m_ReverseOutput; + bool m_UsingPolyTree; + bool m_StrictSimple; +#ifdef use_xyz + TZFillCallback m_ZFill; //custom callback +#endif + void SetWindingCount(TEdge& edge); + bool IsEvenOddFillType(const TEdge& edge) const; + bool IsEvenOddAltFillType(const TEdge& edge) const; + void InsertScanbeam(const cInt Y); + cInt PopScanbeam(); + void InsertLocalMinimaIntoAEL(const cInt botY); + void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); + void AddEdgeToSEL(TEdge *edge); + void CopyAELToSEL(); + void DeleteFromSEL(TEdge *e); + void DeleteFromAEL(TEdge *e); + void UpdateEdgeIntoAEL(TEdge *&e); + void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); + bool IsContributing(const TEdge& edge) const; + bool IsTopHorz(const cInt XPos); + void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); + void DoMaxima(TEdge *e); + void PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam); + void ProcessHorizontals(bool IsTopOfScanbeam); + void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam); + void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutRec* GetOutRec(int idx); + void AppendPolygon(TEdge *e1, TEdge *e2); + void IntersectEdges(TEdge *e1, TEdge *e2, + const IntPoint &pt, bool protect = false); + OutRec* CreateOutRec(); + OutPt* AddOutPt(TEdge *e, const IntPoint &pt); + void DisposeAllOutRecs(); + void DisposeOutRec(PolyOutList::size_type index); + bool ProcessIntersections(const cInt botY, const cInt topY); + void BuildIntersectList(const cInt botY, const cInt topY); + void ProcessIntersectList(); + void ProcessEdgesAtTopOfScanbeam(const cInt topY); + void BuildResult(Paths& polys); + void BuildResult2(PolyTree& polytree); + void SetHoleState(TEdge *e, OutRec *outrec); + void DisposeIntersectNodes(); + bool FixupIntersectionOrder(); + void FixupOutPolygon(OutRec &outrec); + bool IsHole(TEdge *e); + bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); + void FixHoleLinkage(OutRec &outrec); + void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); + void ClearJoins(); + void ClearGhostJoins(); + void AddGhostJoin(OutPt *op, const IntPoint offPt); + bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); + void JoinCommonEdges(); + void DoSimplePolygons(); + void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); + void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec); +#ifdef use_xyz + void SetZ(IntPoint& pt, TEdge& e); +#endif +}; +//------------------------------------------------------------------------------ + +class ClipperOffset +{ +public: + ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); + ~ClipperOffset(); + void AddPath(const Path& path, JoinType joinType, EndType endType); + void AddPaths(const Paths& paths, JoinType joinType, EndType endType); + void Execute(Paths& solution, double delta); + void Execute(PolyTree& solution, double delta); + void Clear(); + double MiterLimit; + double ArcTolerance; +private: + Paths m_destPolys; + Path m_srcPoly; + Path m_destPoly; + std::vector m_normals; + double m_delta, m_sinA, m_sin, m_cos; + double m_miterLim, m_StepsPerRad; + IntPoint m_lowest; + PolyNode m_polyNodes; + + void FixOrientations(); + void DoOffset(double delta); + void OffsetPoint(int j, int& k, JoinType jointype); + void DoSquare(int j, int k); + void DoMiter(int j, int k, double r); + void DoRound(int j, int k); +}; +//------------------------------------------------------------------------------ + +class clipperException : public std::exception +{ + public: + clipperException(const char* description): m_descr(description) {} + virtual ~clipperException() throw() {} + virtual const char* what() const throw() {return m_descr.c_str();} + private: + std::string m_descr; +}; +//------------------------------------------------------------------------------ + +} //ClipperLib namespace + +#endif //clipper_hpp + + diff --git a/main/lib/clipper/gb.clipper.component b/main/lib/clipper/gb.clipper.component new file mode 100644 index 00000000..7522f1a4 --- /dev/null +++ b/main/lib/clipper/gb.clipper.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.clipper +Author=Benoît Minisini +State=Stable diff --git a/main/lib/clipper/gb.geom.h b/main/lib/clipper/gb.geom.h new file mode 120000 index 00000000..2ae286cf --- /dev/null +++ b/main/lib/clipper/gb.geom.h @@ -0,0 +1 @@ +../geom/gb.geom.h \ No newline at end of file diff --git a/main/lib/clipper/main.cpp b/main/lib/clipper/main.cpp new file mode 100644 index 00000000..4e06895b --- /dev/null +++ b/main/lib/clipper/main.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** + + main.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "c_clipper.h" +#include "main.h" + +extern "C" { + +GB_INTERFACE GB EXPORT; +GEOM_INTERFACE GEOM EXPORT; + +GB_DESC *GB_CLASSES [] EXPORT = +{ + PolygonDesc, + ClipperDesc, + NULL +}; + +const char *GB_INCLUDE EXPORT = "gb.geom"; + +int EXPORT GB_INIT(void) +{ + GB.Component.Load("gb.geom"); + GB.GetInterface("gb.geom", GEOM_INTERFACE_VERSION, &GEOM); + return 0; +} + + +void EXPORT GB_EXIT() +{ +} + +} diff --git a/main/lib/clipper/main.h b/main/lib/clipper/main.h new file mode 100644 index 00000000..6cac7eff --- /dev/null +++ b/main/lib/clipper/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb.geom.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern "C" GB_INTERFACE GB; +extern "C" GEOM_INTERFACE GEOM; +#endif + +#endif /* __MAIN_H */ diff --git a/main/lib/complex/Makefile.am b/main/lib/complex/Makefile.am new file mode 100644 index 00000000..bb78945c --- /dev/null +++ b/main/lib/complex/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.complex +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.complex.la + +gb_complex_la_LIBADD = @MATH_LIB@ +gb_complex_la_LDFLAGS = -module @LD_FLAGS@ +gb_complex_la_CFLAGS = -I$(top_srcdir)/share $(AM_CFLAGS) + +gb_complex_la_SOURCES = \ + ccomplex.h ccomplex.c \ + main.h main.c + + diff --git a/main/lib/complex/ccomplex.c b/main/lib/complex/ccomplex.c new file mode 100644 index 00000000..21c87828 --- /dev/null +++ b/main/lib/complex/ccomplex.c @@ -0,0 +1,533 @@ +/*************************************************************************** + + ccomplex.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCOMPLEX_C + +#include "ccomplex.h" + +#define THIS ((CCOMPLEX *)_object) +#define RE(_c) ((_c)->v[0]) +#define IM(_c) ((_c)->v[1]) +#define ABS(_c) (hypot(RE(_c), IM(_c))) +#define ABS2(_c) (RE(_c) * RE(_c) + IM(_c) * IM(_c)) +#define ZERO(_c) (RE(_c) == 0.0 && IM(_c) == 0.0) + + +//---- Complex number creation ---------------------------------------------- + +CCOMPLEX *COMPLEX_create(double re, double im) +{ + static GB_CLASS CLASS_Complex = (GB_CLASS)NULL; + CCOMPLEX *c; + + if (!CLASS_Complex) + CLASS_Complex = GB.FindClass("Complex"); + + c = (CCOMPLEX *)GB.New(CLASS_Complex, NULL, NULL); + c->v[0] = re; + c->v[1] = im; + + return c; +} + +//#define COMPLEX_make(_a, _re, _im) (((_a)->ob.ref <= 1) ? ((_a)->v[0] = (_re), (_a)->v[1] = (_im), (_a)) : COMPLEX_create((_re), (_im))) + +static inline CCOMPLEX *COMPLEX_make(CCOMPLEX *a, const double re, const double im) +{ + if (a->ob.ref <= 1) + { + a->v[0] = re; + a->v[1] = im; + return a; + } + else + return COMPLEX_create(re, im); +} + +CCOMPLEX *COMPLEX_push_complex(double value) +{ + return COMPLEX_create(0, value); +} + +//---- Arithmetic operators ------------------------------------------------- + +static CCOMPLEX *_addf(CCOMPLEX *a, double f, bool invert) +{ + return COMPLEX_make(a, RE(a) + f, IM(a)); +} + +static CCOMPLEX *_add(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + return COMPLEX_make(a, RE(a) + RE(b), IM(a) + IM(b)); +} + +static CCOMPLEX *_subf(CCOMPLEX *a, double f, bool invert) +{ + if (invert) + return COMPLEX_make(a, f - RE(a), -IM(a)); + else + return COMPLEX_make(a, RE(a) - f, IM(a)); +} + +static CCOMPLEX *_sub(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + return COMPLEX_make(a, RE(a) - RE(b), IM(a) - IM(b)); +} + +static CCOMPLEX *_mulf(CCOMPLEX *a, double f, bool invert) +{ + return COMPLEX_make(a, RE(a) * f, IM(a) * f); +} + +static CCOMPLEX *_mul(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + return COMPLEX_make(a, RE(a) * RE(b) - IM(a) * IM(b), RE(a) * IM(b) + IM(a) * RE(b)); +} + +static CCOMPLEX *_divf(CCOMPLEX *a, double f, bool invert) +{ + if (invert) + { + if (ZERO(a)) + return NULL; + + double s = ABS2(a); + double re, im; + + re = RE(a) / s; + im = -IM(a) / s; + + return COMPLEX_make(a, re * f, im * f); + } + else + { + if (f == 0.0) + return NULL; + + return COMPLEX_make(a, RE(a) / f, IM(a) / f); + } +} + +static CCOMPLEX *_div(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + double ar = RE(a), ai = IM(a); + double br = RE(b), bi = IM(b); + + if (br == 0.0 && bi == 0.0) + return NULL; + + double s = 1.0 / ABS(b); + + double sbr = s * br; + double sbi = s * bi; + + double zr = (ar * sbr + ai * sbi) * s; + double zi = (ai * sbr - ar * sbi) * s; + + return COMPLEX_make(a, zr, zi); +} + +static int _equal(CCOMPLEX *a, CCOMPLEX *b, bool invert) +{ + return RE(a) == RE(b) && IM(a) == IM(b); +} + +static int _equalf(CCOMPLEX *a, double f, bool invert) +{ + return RE(a) == f && IM(a) == 0; +} + +static double _fabs(CCOMPLEX *a) +{ + return ABS(a); +} + +static CCOMPLEX *_neg(CCOMPLEX *a) +{ + return COMPLEX_make(a, -RE(a), -IM(a)); +} + +double _logabs(CCOMPLEX *a) +{ + double xabs = fabs(RE(a)); + double yabs = fabs(IM(a)); + double max, u; + + if (xabs >= yabs) + { + max = xabs; + u = yabs / xabs; + } + else + { + max = yabs; + u = xabs / yabs; + } + + /* Handle underflow when u is close to 0 */ + return log(max) + 0.5 * log1p (u * u); +} + +static double _arg(CCOMPLEX *a) +{ + if (ZERO(a)) + return 0.0; + else + return atan2(IM(a), RE(a)); +} + +static CCOMPLEX *_powi(CCOMPLEX *a, int i) +{ + CCOMPLEX *r; + bool inv; + + inv = i < 0; + i = abs(i); + + if (i == 2) + r = _mul(a, a, FALSE); + else if (i == 3) + { + r = COMPLEX_create(RE(a), IM(a)); + r = _mul(r, a, FALSE); + r = _mul(r, a, FALSE); + } + else if (i == 4) + { + a = _mul(a, a, FALSE); + r = _mul(a, a, FALSE); + } + else + r = COMPLEX_make(a, RE(a), IM(a)); + + if (inv) + return _divf(r, 1, TRUE); + else + return r; +} + +static CCOMPLEX *_pow(CCOMPLEX *a, CCOMPLEX *b) +{ + if (RE(a) == 0.0 && IM(a) == 0.0) + { + if (RE(b) == 0.0 && IM(b) == 0.0) + return COMPLEX_make(a, 1.0, 0.0); + else + return COMPLEX_make(a, 0.0, 0.0); + } + else if (IM(b) == 0.0) + { + if (RE(b) >= 4.0 && RE(b) <= -4.0 && RE(b) == (int)RE(b)) + return _powi(a, (int)RE(b)); + } + + double logr = _logabs (a); + double theta = _arg(a); + + double br = RE(b), bi = IM(b); + + double rho = exp(logr * br - bi * theta); + double beta = theta * br + bi * logr; + + return COMPLEX_make(a, rho * cos (beta), rho * sin (beta)); +} + +static CCOMPLEX *_powf(CCOMPLEX *a, double b) +{ + if (RE(a) == 0.0 && IM(a) == 0.0) + { + if (b == 0.0) + return COMPLEX_make(a, 1.0, 0.0); + else + return COMPLEX_make(a, 0.0, 0.0); + } + else if (b == 0.0) + return COMPLEX_make(a, 1.0, 0.0); + else if (b <= 4.0 && b >= -4.0 && b == (int)b) + return _powi(a, (int)b); + else + { + double logr = _logabs (a); + double theta = _arg (a); + double rho = exp (logr * b); + double beta = theta * b; + + return COMPLEX_make(a, rho * cos(beta), rho * sin(beta)); + } +} + +static GB_OPERATOR_DESC _operator = +{ + .equal = (void *)_equal, + .equalf = (void *)_equalf, + .add = (void *)_add, + .addf = (void *)_addf, + .sub = (void *)_sub, + .subf = (void *)_subf, + .mul = (void *)_mul, + .mulf = (void *)_mulf, + .div = (void *)_div, + .divf = (void *)_divf, + .pow = (void *)_pow, + .powf = (void *)_powf, + .fabs = (void *)_fabs, + .neg = (void *)_neg +}; + +//---- Conversions ---------------------------------------------------------- + +char *COMPLEX_to_string(double real, double imag, bool local) +{ + char buffer[64]; + char *p; + char *str; + int len; + + if (real == 0.0 && imag == 0.0) + return GB.NewString("0", 1); + + p = buffer; + + if (real != 0.0) + { + GB.NumberToString(local, real, NULL, &str, &len); + strncpy(p, str, len); + p += len; + } + + if (imag != 0.0) + { + if (imag < 0.0) + { + *p++ = '-'; + imag = (-imag); + } + else if (p != buffer) + *p++ = '+'; + + if (imag != 1.0) + { + GB.NumberToString(local, imag, NULL, &str, &len); + strncpy(p, str, len); + p += len; + } + *p++ = 'i'; + } + + return GB.NewString(buffer, p - buffer); +} + +static bool _convert(CCOMPLEX *a, GB_TYPE type, GB_VALUE *conv) +{ + if (a) + { + switch (type) + { + case GB_T_FLOAT: + if (IM(a)) + return TRUE; + conv->_float.value = RE(a); + return FALSE; + + case GB_T_SINGLE: + if (IM(a)) + return TRUE; + conv->_single.value = RE(a); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + if (IM(a)) + return TRUE; + conv->_integer.value = RE(a); + return FALSE; + + case GB_T_LONG: + if (IM(a)) + return TRUE; + conv->_long.value = RE(a); + return FALSE; + + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = COMPLEX_to_string(RE(a), IM(a), type == GB_T_CSTRING); + conv->_string.value.start = 0; + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); + return FALSE; + + default: + return TRUE; + } + } + else + { + switch(type) + { + case GB_T_FLOAT: + conv->_object.value = COMPLEX_create(conv->_float.value, 0); + return FALSE; + + case GB_T_SINGLE: + conv->_object.value = COMPLEX_create(conv->_single.value, 0); + return FALSE; + + case GB_T_LONG: + conv->_object.value = COMPLEX_create((double)conv->_long.value, 0); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + conv->_object.value = COMPLEX_create(conv->_integer.value, 0); + return FALSE; + + default: + return TRUE; + } + } +} + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Complex_new, GB_FLOAT real; GB_FLOAT imag) + + THIS->v[0] = VARGOPT(real, 0.0); + THIS->v[1] = VARGOPT(imag, 0.0); + +END_METHOD + + +BEGIN_METHOD(Complex_call, GB_FLOAT real; GB_FLOAT imag) + + GB.ReturnObject(COMPLEX_create(VARG(real), VARG(imag))); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_Copy) + + GB.ReturnObject(COMPLEX_create(RE(THIS), IM(THIS))); + +END_METHOD + + +BEGIN_METHOD(Complex_Polar, GB_FLOAT abs; GB_FLOAT arg) + + double mod = VARG(abs); + double arg = VARG(arg); + + GB.ReturnObject(COMPLEX_create(cos(arg) * mod, sin(arg) * mod)); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_Arg) + + GB.ReturnFloat(_arg(THIS)); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_Abs) + + GB.ReturnFloat(ABS(THIS)); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_Abs2) + + GB.ReturnFloat(ABS2(THIS)); + +END_METHOD + + +BEGIN_PROPERTY(Complex_Real) + + if (READ_PROPERTY) + GB.ReturnFloat(RE(THIS)); + else + THIS->v[0] = VPROP(GB_FLOAT); + +END_PROPERTY + + +BEGIN_PROPERTY(Complex_Imag) + + if (READ_PROPERTY) + GB.ReturnFloat(IM(THIS)); + else + THIS->v[1] = VPROP(GB_FLOAT); + +END_PROPERTY + + +BEGIN_METHOD_VOID(Complex_Inv) + + GB.ReturnObject(_divf(THIS, 1, TRUE)); + +END_METHOD + + +BEGIN_METHOD_VOID(Complex_Conj) + + GB.ReturnObject(COMPLEX_create(RE(THIS), -IM(THIS))); + +END_METHOD + + +BEGIN_METHOD(Complex_ToString, GB_BOOLEAN local) + + GB.ReturnString(GB.FreeStringLater(COMPLEX_to_string(RE(THIS), IM(THIS), VARGOPT(local, FALSE)))); + +END_METHOD + +//--------------------------------------------------------------------------- + +GB_DESC ComplexDesc[] = +{ + GB_DECLARE("Complex", sizeof(CCOMPLEX)), + + // Utility Methods + GB_METHOD("_new", NULL, Complex_new, "[(Real)f(Imag)f]"), + GB_STATIC_METHOD("_call", "Complex", Complex_call, "[(Real)f(Imag)f]"), + GB_STATIC_METHOD("Polar", "Complex", Complex_Polar, "[(Abs)f(Arg)f]"), + + GB_METHOD("Copy", "Complex", Complex_Copy, NULL), + GB_METHOD("ToString", "s", Complex_ToString, "[(Local)b]"), + + GB_PROPERTY("Real", "f", Complex_Real), + GB_PROPERTY("Imag", "f", Complex_Imag), + + GB_METHOD("Abs2", "f", Complex_Abs2, NULL), + GB_METHOD("Arg", "f", Complex_Arg, NULL), + + GB_METHOD("Conj", "Complex", Complex_Conj, NULL), + GB_METHOD("Inv", "Complex", Complex_Inv, NULL), + + GB_INTERFACE("_operator", &_operator), + GB_INTERFACE("_convert", &_convert), + + GB_END_DECLARE +}; diff --git a/main/lib/complex/ccomplex.h b/main/lib/complex/ccomplex.h new file mode 100644 index 00000000..21f56948 --- /dev/null +++ b/main/lib/complex/ccomplex.h @@ -0,0 +1,44 @@ +/*************************************************************************** + + ccomplex.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCOMPLEX_H +#define __CCOMPLEX_H + +#include "gambas.h" +#include "main.h" + +#ifndef __CDEBUG_C +extern GB_DESC ComplexDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + double v[2]; + } + CCOMPLEX; + +CCOMPLEX *COMPLEX_create(double re, double im); +CCOMPLEX *COMPLEX_push_complex(double value); + +#endif diff --git a/main/lib/complex/gb.complex.component b/main/lib/complex/gb.complex.component new file mode 100644 index 00000000..d362d409 --- /dev/null +++ b/main/lib/complex/gb.complex.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.component +Author=Benoît Minisini +Implements=Complex diff --git a/main/lib/complex/main.c b/main/lib/complex/main.c new file mode 100644 index 00000000..b9936121 --- /dev/null +++ b/main/lib/complex/main.c @@ -0,0 +1,58 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gambas.h" +#include "ccomplex.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + ComplexDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +int EXPORT GB_INFO(const char *key, void **value) +{ + if (!strcasecmp(key, "PUSH_COMPLEX")) + { + *value = (void *)COMPLEX_push_complex; + return TRUE; + } + else + return FALSE; +} + + diff --git a/main/lib/complex/main.h b/main/lib/complex/main.h new file mode 100644 index 00000000..d14ee22f --- /dev/null +++ b/main/lib/complex/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/main/lib/compress/CCompress.c b/main/lib/compress/CCompress.c new file mode 100644 index 00000000..c3abefc2 --- /dev/null +++ b/main/lib/compress/CCompress.c @@ -0,0 +1,187 @@ +/*************************************************************************** + + CCompress.c + + (c) 2003-2004 Daniel Campos Fern�dez + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCOMPRESS_C + + +#include "CCompress.h" +#include "main.h" +#include + +#define Check_Driver() if (!THIS->driver) { GB.Error("No driver specified"); return; } + +#define Check_Level() if (!MISSING(Level)) \ + level=VARG(Level); \ + else \ + level=THIS->driver->default_compression(); \ + \ + if ( (level < THIS->driver->min_compression()) || (level > THIS->driver->max_compression()) ) \ + if (level != THIS->driver->default_compression()) \ + { \ + GB.Error("Invalid compression level"); \ + return; \ + } + +//************************************************************************* +//############################### COMPRESS ################################ +//************************************************************************* + +/************************************************* +Gambas object "Constructor" +*************************************************/ +BEGIN_METHOD_VOID(CCOMPRESS_new) + + THIS->driver=NULL; + THIS->stream.desc=NULL; + +END_METHOD +/************************************************* +Gambas object "Destructor" +*************************************************/ +BEGIN_METHOD_VOID(CCOMPRESS_free) + + + +END_METHOD + +BEGIN_METHOD (CCOMPRESS_String,GB_STRING Source;GB_INTEGER Level;GB_BOOLEAN AllowGrow;) + + int level; + char *target=NULL; + unsigned int lent; + int allow=0; + + Check_Driver(); + Check_Level(); + lent=0; + if (!MISSING(AllowGrow)) + if ( VARG(AllowGrow) ) allow=1; + + THIS->driver->Compress.String(&target,&lent,STRING(Source),LENGTH(Source),level); + + if (!lent) { GB.ReturnVoidString(); return; } + if ( (!allow) && (LENGTH(Source)<=lent) ) + { + if (target) GB.Free(POINTER(&target)); + GB.ReturnNewString (STRING(Source),LENGTH(Source)); + return; + } + + GB.ReturnNewString (target,lent); + if (target) GB.Free(POINTER(&target)); + +END_METHOD + + +BEGIN_METHOD (CCOMPRESS_File,GB_STRING Source;GB_STRING Target;GB_INTEGER Level;) + + int level; + + Check_Driver(); + Check_Level(); + THIS->driver->Compress.File(STRING(Source),STRING(Target),level); + + + + +END_METHOD + +BEGIN_METHOD (CCOMPRESS_Open,GB_STRING Path;GB_INTEGER Level;) + + int level; + + Check_Driver(); + Check_Level(); + + if (THIS->stream.desc) + { + GB.Error ("File is already opened"); + return; + } + + THIS->driver->Compress.Open(STRING(Path),level,&THIS->stream); + +END_METHOD + +BEGIN_PROPERTY ( COMPRESS_Min ) + + if (!THIS->driver) { GB.ReturnInteger(0); return; } + GB.ReturnInteger(THIS->driver->min_compression()); + +END_PROPERTY + +BEGIN_PROPERTY ( COMPRESS_Max ) + + if (!THIS->driver) { GB.ReturnInteger(0); return; } + GB.ReturnInteger(THIS->driver->max_compression()); + +END_PROPERTY + +BEGIN_PROPERTY ( COMPRESS_Default ) + + if (!THIS->driver) { GB.ReturnInteger(0); return; } + GB.ReturnInteger(THIS->driver->default_compression()); + +END_PROPERTY + +BEGIN_PROPERTY ( COMPRESS_Type ) + + if (READ_PROPERTY) + { + if (!THIS->driver) { GB.ReturnVoidString(); return; } + GB.ReturnNewZeroString(THIS->driver->name); + return; + } + + if (THIS->stream.desc) { GB.Error("Type can not be changed while the stream is opened"); return; } + + if (!(THIS->driver = COMPRESS_GetDriver(GB.ToZeroString(PROP(GB_STRING))))) + GB.Error("Cannot find driver &1", GB.ToZeroString(PROP(GB_STRING))); + +END_PROPERTY + +/******************************************************************* +Interface declaration +*******************************************************************/ +GB_DESC CCompressDesc[] = +{ + + GB_DECLARE("Compress", sizeof(CCOMPRESS)), + + GB_INHERITS("Stream"), + + GB_PROPERTY_READ("Min","i",COMPRESS_Min), + GB_PROPERTY_READ("Max","i",COMPRESS_Max), + GB_PROPERTY_READ("Default","i",COMPRESS_Default), + GB_PROPERTY("Type","s",COMPRESS_Type), + + GB_METHOD("_new", NULL, CCOMPRESS_new,NULL), + GB_METHOD("_free", NULL, CCOMPRESS_free, NULL), + + GB_METHOD("String","s",CCOMPRESS_String,"(Source)s[(Level)i(AllowGrow)b]"), + GB_METHOD("File",NULL,CCOMPRESS_File,"(Source)s(Target)s[(Level)i]"), + GB_METHOD("Open",NULL,CCOMPRESS_Open,"(Path)s[(Level)i]"), + + GB_END_DECLARE +}; + diff --git a/main/lib/compress/CCompress.h b/main/lib/compress/CCompress.h new file mode 100644 index 00000000..7cdbd4b7 --- /dev/null +++ b/main/lib/compress/CCompress.h @@ -0,0 +1,51 @@ +/*************************************************************************** + + CCompress.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCOMPRESS_H +#define __CCOMPRESS_H + +#include "gambas.h" +#include "gb.compress.h" + +#ifndef __CCOMPRESS_C + + +extern GB_DESC CCompressDesc[]; +extern GB_STREAM_DESC CCompressStream; + +#else + +#define THIS ((CCOMPRESS *)_object) + +#endif +typedef struct +{ + GB_BASE ob; + GB_STREAM stream; + COMPRESS_DRIVER *driver; + + +} CCOMPRESS; + + +#endif diff --git a/main/lib/compress/CUncompress.c b/main/lib/compress/CUncompress.c new file mode 100644 index 00000000..042e6f8a --- /dev/null +++ b/main/lib/compress/CUncompress.c @@ -0,0 +1,149 @@ +/*************************************************************************** + + CUncompress.c + + (c) 2003-2004 Daniel Campos Fern�dez + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CUNCOMPRESS_C + +#include "CUncompress.h" +#include "main.h" +#include + +#define Check_Driver() if (!THIS->driver) { GB.Error("No driver specified"); return; } + + +//************************************************************************* +//#################### INITIALIZATION AND DESTRUCTION ##################### +//************************************************************************* +/************************************************* + Class "Constructor" + *************************************************/ +BEGIN_METHOD_VOID(CUNCOMPRESS_init) + + + +END_METHOD +/************************************************* + Class "Destructor" + *************************************************/ +BEGIN_METHOD_VOID(CUNCOMPRESS_exit) + + + +END_METHOD +/************************************************* + Gambas object "Constructor" + *************************************************/ +BEGIN_METHOD_VOID(CUNCOMPRESS_new) + + THIS->driver=NULL; + THIS->stream.desc=NULL; + +END_METHOD +/************************************************* + Gambas object "Destructor" + *************************************************/ +BEGIN_METHOD_VOID(CUNCOMPRESS_free) + + if (THIS->stream.desc && THIS->driver) THIS->driver->Compress.Close(&THIS->stream); + +END_METHOD + +BEGIN_METHOD (CUNCOMPRESS_File,GB_STRING Source;GB_STRING Target;) + + Check_Driver(); + THIS->driver->Uncompress.File(STRING(Source),STRING(Target)); + +END_METHOD + +BEGIN_METHOD (CUNCOMPRESS_String,GB_STRING Source;) + + char *target=NULL; + unsigned int lent=0; + + Check_Driver(); + if (!LENGTH(Source)) { GB.ReturnVoidString(); return; } + + THIS->driver->Uncompress.String(&target,&lent,STRING(Source),LENGTH(Source)); + + if (!lent) { GB.ReturnVoidString(); return; } + GB.ReturnNewString (target,lent); + GB.Free(POINTER(&target)); + +END_METHOD + +BEGIN_METHOD (CUNCOMPRESS_Open,GB_STRING Path;) + + + Check_Driver(); + + if (THIS->stream.desc) { GB.Error ("File is already opened"); return; } + + THIS->driver->Uncompress.Open(STRING(Path),&THIS->stream); + +END_METHOD + +BEGIN_PROPERTY (CUNCOMPRESS_Type) + + if (READ_PROPERTY) + { + if (!THIS->driver) + { + GB.ReturnNull(); + return; + } + GB.ReturnNewZeroString(THIS->driver->name); + return; + } + + if (THIS->stream.desc) { GB.Error("Type can not be changed while the stream is opened"); return; } + + if (!(THIS->driver = COMPRESS_GetDriver(GB.ToZeroString(PROP(GB_STRING))))) + { + GB.Error("Cannot find driver &1", GB.ToZeroString(PROP(GB_STRING))); + } + +END_PROPERTY + +/******************************************************************* + Interface declaration + *******************************************************************/ +GB_DESC CUncompressDesc[] = +{ + + GB_DECLARE("Uncompress", sizeof(CUNCOMPRESS)), + + GB_INHERITS("Stream"), + + GB_PROPERTY("Type","s",CUNCOMPRESS_Type), + + GB_STATIC_METHOD("_init", NULL, CUNCOMPRESS_init, NULL), + GB_STATIC_METHOD("_exit", NULL, CUNCOMPRESS_exit, NULL), + GB_METHOD("_new", NULL, CUNCOMPRESS_new, NULL), + GB_METHOD("_free", NULL, CUNCOMPRESS_free, NULL), + + GB_METHOD("String","s",CUNCOMPRESS_String,"(Source)s"), + GB_METHOD("File",NULL,CUNCOMPRESS_File,"(Source)s(Target)s"), + GB_METHOD("Open",NULL,CUNCOMPRESS_Open,"(Path)s"), + + GB_END_DECLARE +}; + diff --git a/main/lib/compress/CUncompress.h b/main/lib/compress/CUncompress.h new file mode 100644 index 00000000..e87b44b5 --- /dev/null +++ b/main/lib/compress/CUncompress.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + CUncompress.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CUNCOMPRESS_H +#define __CUNCOMPRESS_H + +#include "gambas.h" +#include "gb.compress.h" + +#ifndef __CUNCOMPRESS_C + + +extern GB_DESC CUncompressDesc[]; + +#else + +#define THIS ((CUNCOMPRESS *)_object) + +#endif +typedef struct +{ + GB_BASE ob; + GB_STREAM stream; + COMPRESS_DRIVER *driver; + +} CUNCOMPRESS; + + +#endif diff --git a/main/lib/compress/Makefile.am b/main/lib/compress/Makefile.am new file mode 100644 index 00000000..34b6dd99 --- /dev/null +++ b/main/lib/compress/Makefile.am @@ -0,0 +1,12 @@ +COMPONENT = gb.compress +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.compress.la + +gb_compress_la_LIBADD = +gb_compress_la_LDFLAGS = -module @LD_FLAGS@ +gb_compress_la_CFLAGS = -I$(top_srcdir)/share $(AM_CFLAGS) + +gb_compress_la_SOURCES = gb.compress.h main.h main.c CCompress.h CCompress.c CUncompress.h CUncompress.c + + diff --git a/main/lib/compress/gb.compress.component b/main/lib/compress/gb.compress.component new file mode 100644 index 00000000..e562a546 --- /dev/null +++ b/main/lib/compress/gb.compress.component @@ -0,0 +1,3 @@ +[Component] +Key=gb.compress +Author=Daniel Campos Fernández diff --git a/main/lib/compress/gb.compress.h b/main/lib/compress/gb.compress.h new file mode 100644 index 00000000..83fc1d29 --- /dev/null +++ b/main/lib/compress/gb.compress.h @@ -0,0 +1,68 @@ +/*************************************************************************** + + gb.compress.h + + (c) 2003-2004 Daniel Campos Fern�ndez + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_COMPRESS_H +#define __GB_COMPRESS_H + +#include "gambas.h" + +typedef + struct { + char *type; + } + COMPRESS_DESC; + + +typedef + struct { + char *name; + int (*max_compression)(void); + int (*min_compression)(void); + int (*default_compression)(void); + + struct { + int (*String) (char **target,unsigned int *lent,char *source,unsigned int len,int level); + int (*File) (char *source,char *target,int level); + void (*Open) (char *path,int level,GB_STREAM *stream); + int (*Close) (GB_STREAM *stream); + } Compress; + + struct { + int (*String) (char **target,unsigned int *lent,char *source,unsigned int len); + int (*File) (char *source,char *target); + void (*Open) (char *path,GB_STREAM *stream); + int (*Close) (GB_STREAM *stream); + } Uncompress; + } + COMPRESS_DRIVER; + +typedef + struct { + intptr_t version; + void (*Register)(COMPRESS_DRIVER *); + } + COMPRESS_INTERFACE; + +#define COMPRESS_INTERFACE_VERSION 1 + +#endif diff --git a/main/lib/compress/main.c b/main/lib/compress/main.c new file mode 100644 index 00000000..1ff69a49 --- /dev/null +++ b/main/lib/compress/main.c @@ -0,0 +1,111 @@ +/*************************************************************************** + + main.c + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define MAX_DRIVER 8 +#define __MAIN_C + +#include + +#include "CCompress.h" +#include "CUncompress.h" +#include "gb.compress.h" + +#include "gb_common.h" + +#include "main.h" + + + +GB_INTERFACE GB EXPORT; + +static COMPRESS_DRIVER *_drivers[MAX_DRIVER]; +static int _drivers_count = 0; + + +static void COMPRESS_Register(COMPRESS_DRIVER *driver) +{ + if (_drivers_count >= MAX_DRIVER) + return; + + _drivers[_drivers_count] = driver; + _drivers_count++; + +} + +COMPRESS_DRIVER *COMPRESS_GetDriver(char *type) +{ + int i; + char *comp; + + if (!type || !*type) + { + GB.Error("Driver name missing"); + return NULL; + } + + comp = alloca(strlen(type) + 14); + + strcpy(comp, "gb.compress."); + strcat(comp, type); + + if (GB.Component.Load(comp)) + { + GB.Error("Cannot find driver for : &1", type); + return NULL; + } + + for (i = 0; i < _drivers_count; i++) + { + if (strcasecmp(_drivers[i]->name, type) == 0) + return _drivers[i]; + } + + return NULL; +} + +void *GB_COMPRESS_1[] EXPORT = { + + (void *)1, + + (void *)COMPRESS_Register, + + NULL +}; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CCompressDesc, + CUncompressDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ + +} + diff --git a/main/lib/compress/main.h b/main/lib/compress/main.h new file mode 100644 index 00000000..16729d1d --- /dev/null +++ b/main/lib/compress/main.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + main.h + + (c) 2003-2004 Daniel Campos Fernández + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + + + +#include "gb_common.h" +#include "gambas.h" +#include "gb.compress.h" + + + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif +COMPRESS_DRIVER* COMPRESS_GetDriver(char *type); +#endif diff --git a/main/lib/data/Makefile.am b/main/lib/data/Makefile.am new file mode 100644 index 00000000..01600cf5 --- /dev/null +++ b/main/lib/data/Makefile.am @@ -0,0 +1,21 @@ +COMPONENT = gb.data +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.data.la + +gb_data_la_LDFLAGS = -module @LD_FLAGS@ +gb_data_la_CPPFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx +gb_data_la_CFLAGS = $(AM_CFLAGS) + +gb_data_la_SOURCES = \ + main.h main.c list.h lookup3.h string_compare.h \ + c_list.h c_list.c \ + c_deque.h c_deque.c \ + c_circular.h c_circular.c \ + c_avltree.h c_avltree.c \ + c_graph.h c_graph.c \ + c_graphmatrix.h c_graphmatrix.c \ + c_heap.h c_heap.c \ + trie.h trie.c \ + c_trie.h c_trie.c + diff --git a/main/lib/data/TODO b/main/lib/data/TODO new file mode 100644 index 00000000..1f3644c7 --- /dev/null +++ b/main/lib/data/TODO @@ -0,0 +1,55 @@ +NOTE that one should not compare elements in a list based on their indices. +If List2 is an exact copy of List1 and both Currents point to the same +element, it is not guaranteed that List1.Index = List2.Index because one of +them could be the negative equivalent of the other (non-negative) one. + +* OPT: Dynamically add up to 8 anchors to increase the chance of finding a + very good anchor for traversals. These are automatically updated on + append/prepend/take operations to be widespread across the List. +* OPT: Also save an anchor of the last element obtained via _get(). + Sequential traversal with indices should now be almost array-like in + speed. + +* BUG: Current can be used as an enumerator now without corrupting the list + and also if it is initially invalid. + +* OPT: Make the VAL_append()/prepend() algorithms more clever: They can use + any neighbour chunk now to borrow space for the inserted value. This + measure reduces list fragmentation and also enables to satisfy the (L) + postulate. + +* NEW: Push() and Pop() are new methods of the List class to save and + restore anchors from a Gambas program. These can be used to quickly go + back to a formerly-visited element. + +AvlTree: +* NEW: Add a Copy() method to return a deep copy of an AvlTree. +* NEW: Split() is a new method of AvlTree to return the left or right + subtree of a given node or both. +* NEW: Add Insert() to insert an AvlTree into another one. (See a thread on + stackoverflow about an O(log n) algorithm for that.) +* NEW: Add the Reverse virtual property to traver in reverse in-order. + +Graph: +* Write down the hidden methods and their signatures +* Set down the relationships between methods; which default implementation + calls what; what is minimum needed to implement a fully-functional Graph +* NEW: Add default implementations for some hidden functions + +RedBlackTree: +* NEW: RedBlackTree class. + +SearchTree: +* NEW: SearchTree is the base class of all search trees. + +PrioQueue: +* NEW: Re-implement PrioQueue using a RedBlackTree. + -> contain structures (key, time, value) where key is the key and time + is a logical timestamp (rbtree->clock++). If key1 == key2, then compare + the unique insertion timestamps. +* OPT: Don't have Integer priority parameters. Consistently use + GB.CompVariant() to compare elements. This is at least as mighty as + integer priorities, but better encapsulated. + +- Use libtree to provide some trees (if possible). +- General, navigatable tree class. diff --git a/main/lib/data/c_avltree.c b/main/lib/data/c_avltree.c new file mode 100644 index 00000000..3dd1f40a --- /dev/null +++ b/main/lib/data/c_avltree.c @@ -0,0 +1,777 @@ +/* + * c_avltree.c - AvlTree class + * + * Copyright (C) 2013 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_AVLTREE_C + +#include + +#include "gambas.h" +#include "gb_common.h" +#include "string_compare.h" + +#include "c_avltree.h" + +typedef struct node NODE; + +struct node { + char *key; /* Key string */ + size_t length; /* Key length */ + int balance; /* Balance value in {-1, 0, 1} */ + NODE *left, *right; /* Children or NULL */ + NODE *parent; /* Parent */ + GB_VARIANT_VALUE val; /* Payload */ +}; + +typedef struct { + GB_BASE ob; + NODE *root; /* The root of the tree */ + NODE *last; /* Last used node */ + size_t count; /* Element count */ + size_t height; /* Tree height */ +} CAVLTREE; + +static void CAVLTREE_init(CAVLTREE *tree) +{ + tree->root = tree->last = NULL; + tree->count = tree->height = 0; +} + +static NODE *NODE_new(NODE *parent, char *key, size_t length) +{ + NODE *node; + + GB.Alloc((void **) &node, sizeof(*node)); + node->key = GB.NewString(key, length); + node->length = length; + node->balance = 0; + node->left = node->right = NULL; + /* If 'parent' is NULL, this shall be its own parent */ + node->parent = parent ? : node; + node->val.type = GB_T_NULL; + return node; +} + +static void NODE_destroy(NODE *node) +{ + GB.FreeString(&node->key); + GB.StoreVariant(NULL, &node->val); + GB.Free((void **) &node); +} + +static NODE *CAVLTREE_first(CAVLTREE *tree) +{ + NODE *first = tree->root; + + if (!first) + return NULL; + while (first->left) + first = first->left; + return first; +} + +#define THIS ((CAVLTREE *) _object) + +/**G + * Create a new, empty AvlTree. + **/ +BEGIN_METHOD_VOID(AvlTree_new) + + CAVLTREE_init(THIS); + +END_METHOD + +struct enum_state { + int started; + NODE *next; +}; + +static void CAVLTREE_clear(CAVLTREE *tree) +{ + NODE *node = CAVLTREE_first(tree), *parent; + + /* We ought to traverse the tree from children to parents */ + while (node) { + while (node->left) + node = node->left; + while (node->right) + node = node->right; + parent = node->parent; + if (node == parent) { + parent = NULL; + } else { + if (parent->left == node) + parent->left = NULL; + else + parent->right = NULL; + } + NODE_destroy(node); + node = parent; + } + + /* Fix enumerators */ + void *ebuf; + struct enum_state *state; + + ebuf = GB.BeginEnum(tree); + while (!GB.NextEnum()) { + state = GB.GetEnum(); + state->next = NULL; + } + GB.EndEnum(ebuf); + + tree->root = tree->last = NULL; + tree->count = 0; + tree->height = 0; +} + +/**G + * Clears the tree automatically. + **/ +BEGIN_METHOD_VOID(AvlTree_free) + + CAVLTREE_clear(THIS); + +END_METHOD + +static NODE *CAVLTREE_find(CAVLTREE *tree, char *key, size_t length) +{ + NODE *node = tree->root; + int res; + + while (node) { + res = STRING_compare(key, length, node->key, node->length); + + if (!res) + return node; + else if (res < 0) + node = node->left; + else + node = node->right; + } + return NULL; +} + +/**G + * Return the value associated with the given key. If no node with this key + * was found, Null is returned. + **/ +BEGIN_METHOD(AvlTree_get, GB_STRING key) + + NODE *node; + + node = CAVLTREE_find(THIS, STRING(key), LENGTH(key)); + THIS->last = node; + if (!node) { + GB.ReturnNull(); + return; + } + GB.ReturnVariant(&node->val); + +END_METHOD + +/* In-order. */ +static NODE *CAVLTREE_next(CAVLTREE *tree, NODE *node) +{ + NODE *next; + + if ((next = node->right)) { + while (next->left) + next = next->left; + return next; + } + for (next = node->parent; node == next->right; next = next->parent) + node = next; + /* This condition is only met when climing from the right subtree to + * root in the above loop. We're done then. */ + if (node == next) + return NULL; + return next; +} + +#if 0 +/* Reverse in-order */ +static NODE *CAVLTREE_prev(CAVLTREE *tree, NODE *node) +{ + NODE *prev; + + if ((prev = node->left)) { + while (prev->right) + prev = prev->right; + return prev; + } + for (prev = node->parent; node == prev->left; prev = prev->parent) + node = prev; + if (node == prev) + return NULL; + return prev; +} +#endif + +static inline void rotate_left(CAVLTREE *tree, NODE *rot) +{ + NODE *right = rot->right, *parent = rot->parent; + + if (rot == tree->root) { + tree->root = right; + /* Don't forget to add the root signature */ + right->parent = right; + } else { + if (rot == parent->left) + parent->left = right; + else + parent->right = right; + right->parent = parent; + } + rot->parent = right; + + rot->right = right->left; + if (rot->right) + rot->right->parent = rot; + right->left = rot; +} + +static inline void rotate_right(CAVLTREE *tree, NODE *rot) +{ + NODE *left = rot->left, *parent = rot->parent; + + if (rot == tree->root) { + tree->root = left; + left->parent = left; + } else { + if (rot == parent->left) + parent->left = left; + else + parent->right = left; + left->parent = parent; + } + rot->parent = left; + + rot->left = left->right; + if (rot->left) + rot->left->parent = rot; + left->right = rot; +} + +#define sgn(x) \ +({ \ + typeof(x) __x = (x); \ + __x ? (__x < 0 ? -1 : 1) : 0; \ +}) + +static NODE *CAVLTREE_find_add(CAVLTREE *tree, char *key, size_t length) +{ + NODE *node, *parent, *reb, *rot, *new; + int res; + + /* Make GCC happy */ + parent = NULL; + res = 0; + + /* Slightly extended version of NODE_find() which gathers additional + * data for insertion. I'd like to have them separate. */ + reb = node = tree->root; + while (node) { + res = STRING_compare(key, length, node->key, node->length); + + if (!res) + return node; + if (node->balance) + reb = node; + parent = node; + if (res < 0) + node = node->left; + else + node = node->right; + } + res = sgn(res); + + new = node = NODE_new(parent, key, length); + tree->count++; + + /* Fix enumerations, pt. I: empty tree */ + void *ebuf; + struct enum_state *state; + + if (!tree->root) { + tree->root = node; + tree->height++; + ebuf = GB.BeginEnum(tree); + while (!GB.NextEnum()) { + state = GB.GetEnum(); + state->next = node; + } + GB.EndEnum(ebuf); + return node; + } + if (res == -1) + parent->left = node; + else + parent->right = node; + + /* Fix enumerators, pt. II: all other cases */ + ebuf = GB.BeginEnum(tree); + while (!GB.NextEnum()) { + state = GB.GetEnum(); + /* + * Nasty. If a new element is inserted before the current + * state->next OR state->next is NULL and a new last + * element is added, update the state. + */ + if (state->next == parent + || (!state->next && !CAVLTREE_next(tree, new))) + state->next = new; + } + GB.EndEnum(ebuf); + + /* Adjust balance factors */ + while (node != reb) { + if (node == parent->left) + parent->balance--; + else + parent->balance++; + node = parent; + parent = parent->parent; + } + + /* Rebalance the tree */ + switch (reb->balance) { + case 1: + case -1: + tree->height++; + break; + case 2: /* Right heavy */ + rot = reb->right; + if (rot->balance == 1) { /* Right-right */ + reb->balance = 0; + rot->balance = 0; + } else { /* Right-left */ + switch (rot->left->balance) { + case 1: + reb->balance = -1; + rot->balance = 0; + break; + case 0: + reb->balance = 0; + rot->balance = 0; + break; + case -1: + reb->balance = 0; + rot->balance = 1; + break; + } + rot->left->balance = 0; + rotate_right(tree, rot); + } + rotate_left(tree, reb); + break; + case -2: /* Left heavy */ + rot = reb->left; + if (rot->balance == -1) { /* Left-left */ + reb->balance = 0; + rot->balance = 0; + } else { /* Left-right */ + switch (rot->right->balance) { + case 1: + reb->balance = 0; + rot->balance = -1; + break; + case 0: + reb->balance = 0; + rot->balance = 0; + break; + case -1: + reb->balance = 1; + rot->balance = 0; + break; + } + rot->right->balance = 0; + rotate_left(tree, rot); + } + rotate_right(tree, reb); + break; + } + + return new; +} + +#ifdef DEBUG_ME +static void dump_node(NODE *node) +{ + fprintf(stderr, "%p \"%s\" (parent=%p \"%s\", left=%p \"%s\", " + "right=%p \"%s\") balance=%d\n", node, node->key, + node->parent, node->parent->key, node->left, + node->left ? node->left->key : "", node->right, + node->right ? node->right->key : "", node->balance); + if (node->left) + dump_node(node->left); + if (node->right) + dump_node(node->right); +} +#endif + +static void CAVLTREE_remove(CAVLTREE *tree, char *key, size_t length) +{ + NODE *node, *rep, *child, *reb; + int d; /* A balance delta */ + int process_root = 1; + + node = CAVLTREE_find(tree, key, length); + +#ifdef DEBUG_ME + fprintf(stderr, "Deletion of %p \"%s\"\n", node, key); + dump_node(tree->root); + fprintf(stderr, "-->\n"); +#endif + + if (!node) + return; + tree->count--; + if (node == tree->last) + tree->last = NULL; + + if (!node->left || !node->right) { + rep = node->left ? : node->right; + reb = node->parent; + d = node == reb->left ? 1: -1; + goto replace; + } + + rep = CAVLTREE_next(tree, node); + + /* Detach replacement node */ + reb = rep->parent; + d = LIKELY(rep == reb->left) ? 1 : -1; + if (reb == node) + goto replace; + child = rep->left ? : rep->right; + if (child) + child->parent = reb; + if (LIKELY(rep == reb->left)) + reb->left = child; + else + reb->right = child; + + /* Replace 'node' by 'rep'. At this point, 'rep' may be anything + * from an inner node to a half-leaf or leaf. */ +replace:; + /* Fix enumerations */ + void *ebuf; + struct enum_state *state; + + ebuf = GB.BeginEnum(tree); + while (!GB.NextEnum()) { + state = GB.GetEnum(); + if (state->next == node) + state->next = rep; + } + GB.EndEnum(ebuf); + + if (node == tree->root) { + tree->root = rep; + if (rep) { + rep->parent = rep; + } else { /* Tree gets empty */ + tree->count = 0; + tree->height = 0; + NODE_destroy(node); + return; + } + } else { + if (node == node->parent->left) + node->parent->left = rep; + else + node->parent->right = rep; + if (rep) + rep->parent = node->parent; + } + if (rep) { + rep->balance = node->balance; + if (rep != node->left) { + rep->left = node->left; + if (rep->left) + rep->left->parent = rep; + } else { + rep->balance++; + } + if (rep != node->right) { + rep->right = node->right; + if (rep->right) + rep->right->parent = rep; + } else { + rep->balance--; + } + } + + NODE_destroy(node); + + /* Rebalance */ + if (reb == tree->root) + process_root = 0; + do { + int old_balance = reb->balance; + NODE *rot; + + reb->balance += d; + switch (reb->balance) { + case 2: /* Right heavy */ + rot = reb->right; + if (rot->balance == 1) { /* Right-right */ + reb->balance = 0; + rot->balance = 0; + } else { /* Right-left */ + switch (rot->left->balance) { + case 1: + reb->balance = -1; + rot->balance = 0; + break; + case 0: + reb->balance = 0; + rot->balance = 0; + break; + case -1: + reb->balance = 0; + rot->balance = 1; + break; + } + rot->left->balance = 0; + rotate_right(tree, rot); + } + rotate_left(tree, reb); + break; + case -2: /* Left heavy */ + rot = reb->left; + if (rot->balance == -1) { /* Left-left */ + reb->balance = 0; + rot->balance = 0; + } else { /* Left-right */ + switch (rot->right->balance) { + case 1: + reb->balance = 0; + rot->balance = -1; + break; + case 0: + reb->balance = 0; + rot->balance = 0; + break; + case -1: + reb->balance = 1; + rot->balance = 0; + break; + } + rot->right->balance = 0; + rotate_left(tree, rot); + } + rotate_right(tree, reb); + break; + case -1: + case 1: + goto end; + } + d = reb->balance - old_balance; + if (reb == reb->parent->right) + d = -d; + reb = reb->parent; + } while (reb != tree->root || process_root--); + tree->height--; +end:; +#ifdef DEBUG_ME + dump_node(tree->root); + fprintf(stderr, "Deletion complete\n\n"); +#endif +} + +/**G + * Creates a new node with the given key and value or changes the value of + * an already existing key. If the value is Null, then the node is removed. + **/ +BEGIN_METHOD(AvlTree_put, GB_VARIANT value; GB_STRING key) + + NODE *node; + + if (VARG(value).type == GB_T_NULL) { + CAVLTREE_remove(THIS, STRING(key), LENGTH(key)); + return; + } + node = CAVLTREE_find_add(THIS, STRING(key), LENGTH(key)); + GB.StoreVariant(ARG(value), &node->val); + THIS->last = node; + +END_METHOD + +/**G + * Visit each element of the tree in-order, i.e. from the smallest key to + * the greatest. The Key property of the tree is set according to the value + * in the enumerator. + * + * {example + * Dim v As Variant + * + * Print "Key", "Value" + * For Each v In hTree + * Print hTree.Key, v + * Next + * } + **/ +BEGIN_METHOD_VOID(AvlTree_next) + + NODE *node; + struct enum_state *state = GB.GetEnum(); + + if (!state->started) { + state->started = 1; + node = CAVLTREE_first(THIS); + } else { + node = state->next; + } + if (!node) { + GB.StopEnum(); + return; + } + state->next = CAVLTREE_next(THIS, node); + THIS->last = node; + GB.ReturnVariant(&node->val); + +END_METHOD + +/**G + * Clear the tree, i.e. remove all elements. This is **way** faster than + * removing every element by assigning Null to it like this: + * + * For Each v In hTree + * hTree[hTree.Key] = Null + * Next + * + * Because removing an element may require to rebalance the tree at most + * hTree.Height times. For each element, this is a great overhead to have an + * empty tree. + * + * So use hTree.Clear() if the tree shall be emptied. + **/ +BEGIN_METHOD_VOID(AvlTree_Clear) + + CAVLTREE_clear(THIS); + +END_METHOD + +/**G + * Return whether an element with the given key exists. + **/ +BEGIN_METHOD(AvlTree_Exist, GB_STRING key) + + NODE *node; + + node = CAVLTREE_find(THIS, STRING(key), LENGTH(key)); + /* TODO: Use this as a cache for subsequent MoveTo() or anything */ + THIS->last = node; + GB.ReturnBoolean(!!node); + +END_METHOD + +/**G + * Return the balance factor of the AvlTree. It is either -1, 0 or 1. + **/ +BEGIN_PROPERTY(AvlTree_Balance) + + if (!THIS->root) { + GB.ReturnInteger(0); + return; + } + GB.ReturnInteger(THIS->root->balance); + +END_PROPERTY + +/**G + * Return the number of elements in the tree. + **/ +BEGIN_PROPERTY(AvlTree_Count) + + GB.ReturnInteger(THIS->count); + +END_PROPERTY + +/**G + * Return the height of the tree. + **/ +BEGIN_PROPERTY(AvlTree_Height) + + GB.ReturnInteger(THIS->height); + +END_PROPERTY + +/**G + * Return the last used key. This can be Null if the element was removed + * meanwhile. + * + * Be careful as this property changes with nearly every operation on the + * tree. + **/ +BEGIN_PROPERTY(AvlTree_Key) + + if (!THIS->last) + GB.ReturnNull(); + else + GB.ReturnString(THIS->last->key); + +END_PROPERTY + +GB_DESC CAvlTree[] = { + GB_DECLARE("AvlTree", sizeof(CAVLTREE)), + + GB_METHOD("_new", NULL, AvlTree_new, NULL), + GB_METHOD("_free", NULL, AvlTree_free, NULL), + GB_METHOD("_get", "v", AvlTree_get, "(Key)s"), + GB_METHOD("_put", NULL, AvlTree_put, "(Value)v(Key)s"), + GB_METHOD("_next", "v", AvlTree_next, NULL), + + GB_METHOD("Clear", NULL, AvlTree_Clear, NULL), + GB_METHOD("Exist", "b", AvlTree_Exist, "(Key)s"), + +#if 0 + /* Returns left and right subtree of root as array of new + * (deeply-copied) trees. This is trivial as any subtree in an + * AvlTree is also an AvlTree. */ + GB_METHOD("Split", ".AvlTree.Split", AvlTree_Split, "(Key)s"), + /* Merge into this tree. */ + GB_METHOD("Merge", NULL, AvlTree_Merge, "(OtherTree)AvlTree"), + /* Return a deep copy of the AvlTree */ + GB_METHOD("Copy", "AvlTree", AvlTree_Copy, NULL), +#endif + + GB_PROPERTY_READ("Balance", "i", AvlTree_Balance), + GB_PROPERTY_READ("Count", "i", AvlTree_Count), + GB_PROPERTY_READ("Height", "i", AvlTree_Height), + + GB_PROPERTY_READ("Key", "s", AvlTree_Key), + + GB_END_DECLARE +}; + +#if 0 +GB_DESC CAvlTreeSplit[] = { + GB_DECLARE_VIRTUAL(".AvlTree.Split"), + + GB_PROPERTY_READ("Left", "AvlTree", AvlTreeSplit_Left), + GB_PROPERTY_READ("Right", "AvlTree", AvlTreeSplit_Right), + GB_PROPERTY_READ("Both", "AvlTree[]", AvlTreeSplit_Both), + + GB_END_DECLARE +}; +#endif diff --git a/main/lib/data/c_avltree.h b/main/lib/data/c_avltree.h new file mode 100644 index 00000000..43cb2f8f --- /dev/null +++ b/main/lib/data/c_avltree.h @@ -0,0 +1,33 @@ +/* + * c_avltree.h + * + * Copyright (C) 2013 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_AVLTREE_H +#define __C_AVLTREE_H + +#include "gambas.h" + +extern GB_INTERFACE GB; + +#ifndef __C_AVLTREE_C +extern GB_DESC CAvlTree[]; +#endif + +#endif /* !__C_AVLTREE_H */ diff --git a/main/lib/data/c_circular.c b/main/lib/data/c_circular.c new file mode 100644 index 00000000..d6a9d77a --- /dev/null +++ b/main/lib/data/c_circular.c @@ -0,0 +1,307 @@ +/* + * c_circular.c - Circular/Ring buffer type + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_CIRCULAR_C + +#include "gambas.h" +#include "c_circular.h" + +typedef struct { + GB_BASE ob; + GB_VARIANT_VALUE *elements; + size_t size; /* Mirror of the array's size to speed things up */ + int reader; + int writer; + int overwrite; + int empty : 1; + int full : 1; +} CCIRCULAR; + +static int CCIRCULAR_size(CCIRCULAR *circ) +{ + return circ->size; +} + +static int CCIRCULAR_is_empty(CCIRCULAR *circ) +{ + return circ->empty; +} + +static int CCIRCULAR_is_full(CCIRCULAR *circ) +{ + return circ->full; +} + +/* + * All movements go forward. This means that moving the writer could never + * induce an 'empty' state and conversely moving the reader could never make + * the circular 'full'. + */ + +static void CCIRCULAR_move_index(CCIRCULAR *circ, int *idx, int pos) +{ + size_t size = CCIRCULAR_size(circ); + + if (!size) + pos = 0; + else if (pos >= size) + pos %= size; + *idx = pos; + /* Set empty/full flags */ + if (circ->reader == circ->writer) { + /* But only if we really operated on the given @circ */ + if (idx == &circ->reader) + circ->empty = 1; + else if (idx == &circ->writer) + circ->full = 1; + } else { + circ->empty = circ->full = 0; + } +} + +static void CCIRCULAR_inc_index(CCIRCULAR *circ, int *idx) +{ + CCIRCULAR_move_index(circ, idx, *idx + 1); +} + +static void CCIRCULAR_reset(CCIRCULAR *circ) +{ + circ->reader = circ->writer = 0; + circ->empty = 1; + if (!circ->size) + circ->full = 1; + else + circ->full = 0; +} + +static void CCIRCULAR_init(CCIRCULAR *circ, size_t size, int overwrite) +{ + circ->size = size; + GB.NewArray(&circ->elements, sizeof(GB_VARIANT_VALUE), circ->size); + CCIRCULAR_reset(circ); + circ->overwrite = overwrite; +} + +static void CCIRCULAR_destroy(CCIRCULAR *circ) +{ + GB.FreeArray((void *) &circ->elements); +} + +#define THIS ((CCIRCULAR *) _object) + +BEGIN_METHOD(Circular_new, GB_INTEGER size; GB_BOOLEAN overwrite) + + CCIRCULAR_init(THIS, VARG(size), VARGOPT(overwrite, 1)); + +END_METHOD + +static GB_VARIANT_VALUE *CCIRCULAR_read(CCIRCULAR *circ) +{ + GB_VARIANT_VALUE *var; + + if (CCIRCULAR_is_empty(circ)) + return NULL; + var = &circ->elements[circ->reader]; + CCIRCULAR_inc_index(circ, &circ->reader); + return var; +} + +static void CCIRCULAR_read_and_free_all(CCIRCULAR *circ) +{ + int i; + + for (i = 0; i < circ->size; i++) + GB.StoreVariant(NULL, &circ->elements[i]); + CCIRCULAR_reset(circ); +} + +BEGIN_METHOD_VOID(Circular_free) + + CCIRCULAR_read_and_free_all(THIS); + CCIRCULAR_destroy(THIS); + +END_METHOD + +static void CCIRCULAR_write(CCIRCULAR *circ, GB_VARIANT *variant) +{ + if (CCIRCULAR_is_full(circ)) { + if (circ->overwrite) /* Consume oldest value and continue */ + CCIRCULAR_read(circ); + else /* Do nothing */ + return; + } + GB.StoreVariant(variant, &circ->elements[circ->writer]); + CCIRCULAR_inc_index(circ, &circ->writer); +} + +BEGIN_METHOD(Circular_Write, GB_VARIANT value) + + CCIRCULAR_write(THIS, ARG(value)); + +END_METHOD + +BEGIN_METHOD_VOID(Circular_Read) + + if (CCIRCULAR_is_empty(THIS)) { + GB.ReturnNull(); + GB.ReturnConvVariant(); + return; + } + GB.ReturnVariant(CCIRCULAR_read(THIS)); + +END_METHOD + +BEGIN_METHOD_VOID(Circular_Peek) + + if (CCIRCULAR_is_empty(THIS)) { + GB.ReturnNull(); + GB.ReturnConvVariant(); + return; + } + GB.ReturnVariant(&THIS->elements[THIS->reader]); + +END_METHOD + +static void CCIRCULAR_resize(CCIRCULAR *circ, size_t new) +{ + size_t old = CCIRCULAR_size(circ); + + if (old == new) + return; + if (old < new) { + GB_VARIANT_VALUE *buf; + int i; + + buf = GB.Insert(&circ->elements, old, new - old); + for (i = 0; old < new; old++, i++) + buf[i].type = GB_T_NULL; + } else { + int i; + + for (i = new; i < old; i++) + GB.StoreVariant(NULL, &circ->elements[i]); + GB.Remove(&circ->elements, new, old - new); + /* Move the indices accordingly */ + if (circ->reader > new) + circ->reader = new; + if (circ->writer > new) + circ->writer = new; + /* 0-length circular? */ + if (!new) { + circ->empty = circ->full = 1; + } + } + circ->size = new; +} + +BEGIN_METHOD(Circular_Resize, GB_INTEGER size) + + CCIRCULAR_resize(THIS, VARG(size)); + +END_METHOD + +BEGIN_METHOD_VOID(Circular_Clear) + + CCIRCULAR_read_and_free_all(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Circular_Reset) + + CCIRCULAR_reset(THIS); + +END_METHOD + +BEGIN_PROPERTY(Circular_IsEmpty) + + GB.ReturnBoolean(CCIRCULAR_is_empty(THIS)); + +END_PROPERTY + +BEGIN_PROPERTY(Circular_IsFull) + + GB.ReturnBoolean(CCIRCULAR_is_full(THIS)); + +END_PROPERTY + +BEGIN_PROPERTY(Circular_Reader) + + if (READ_PROPERTY) { + GB.ReturnInteger(THIS->reader); + return; + } + CCIRCULAR_move_index(THIS, &THIS->reader, VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Circular_Writer) + + if (READ_PROPERTY) { + GB.ReturnInteger(THIS->writer); + return; + } + CCIRCULAR_move_index(THIS, &THIS->writer, VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Circular_Size) + + if (READ_PROPERTY) { + GB.ReturnInteger(CCIRCULAR_size(THIS)); + return; + } + CCIRCULAR_resize(THIS, VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(Circular_Overwrite) + + if (READ_PROPERTY) { + GB.ReturnBoolean(THIS->overwrite); + return; + } + THIS->overwrite = VPROP(GB_BOOLEAN); + +END_PROPERTY + +GB_DESC CCircular[] = { + GB_DECLARE("Circular", sizeof(CCIRCULAR)), + + GB_METHOD("_new", NULL, Circular_new, "(Size)i[(Overwrite)b]"), + GB_METHOD("_free", NULL, Circular_free, NULL), + + GB_METHOD("Write", NULL, Circular_Write, "(Value)v"), + GB_METHOD("Read", "v", Circular_Read, NULL), + GB_METHOD("Peek", "v", Circular_Peek, NULL), + + GB_METHOD("Resize", NULL, Circular_Resize, "(Size)i"), + GB_METHOD("Clear", NULL, Circular_Clear, NULL), + GB_METHOD("Reset", NULL, Circular_Reset, NULL), + + GB_PROPERTY_READ("IsEmpty", "b", Circular_IsEmpty), + GB_PROPERTY_READ("IsFull", "b", Circular_IsFull), + GB_PROPERTY("Reader", "i", Circular_Reader), + GB_PROPERTY("Writer", "i", Circular_Writer), + GB_PROPERTY("Size", "i", Circular_Size), + GB_PROPERTY("Overwrite", "b", Circular_Overwrite), + + GB_END_DECLARE +}; diff --git a/main/lib/data/c_circular.h b/main/lib/data/c_circular.h new file mode 100644 index 00000000..6d06e542 --- /dev/null +++ b/main/lib/data/c_circular.h @@ -0,0 +1,33 @@ +/* + * c_circular.h + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_CIRCULAR_H +#define __C_CIRCULAR_H + +#include "gambas.h" + +extern GB_INTERFACE GB; + +#ifndef __C_CIRCULAR_C +extern GB_DESC CCircular[]; +#endif + +#endif /* !__C_CIRCULAR_H */ diff --git a/main/lib/data/c_deque.c b/main/lib/data/c_deque.c new file mode 100644 index 00000000..79a681b7 --- /dev/null +++ b/main/lib/data/c_deque.c @@ -0,0 +1,391 @@ +/* + * c_deque.c - Deque, FILO and FIFO types + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_DEQUE_C + +#include "gambas.h" +#include "gb_common.h" /* EXTERN for gbx_type.h */ +#include "gbx_type.h" /* TYPE_is_object() */ + +#include "c_list.h" +#include "c_deque.h" + +typedef struct { + GB_VARIANT_VALUE var; + LIST list; + int prio; +} CDEQUE_ELEM; +#define get_elem(node) LIST_data(node, CDEQUE_ELEM, list) + +static CDEQUE_ELEM *CDEQUE_new_elem(GB_VARIANT *variant) +{ + CDEQUE_ELEM *new; + + GB.Alloc((void **) &new, sizeof(*new)); + new->var.type = GB_T_NULL; + LIST_init(&new->list); + GB.StoreVariant(variant, &new->var); + return new; +} + +static CDEQUE_ELEM *CDEQUE_copy_elem(CDEQUE_ELEM *elem) +{ + CDEQUE_ELEM *copy; + + GB.Alloc((void **) ©, sizeof(*copy)); + LIST_init(©->list); + copy->prio = elem->prio; + memcpy(©->var, &elem->var, sizeof(copy->var)); + if (TYPE_is_object(copy->var.type)) + GB.Ref(copy->var.value._object); + return copy; +} + +static void CDEQUE_destroy_elem(CDEQUE_ELEM *elem) +{ + LIST_unlink(&elem->list); + GB.StoreVariant(NULL, &elem->var); + GB.Free((void **) &elem); +} + +typedef struct { + GB_BASE ob; + LIST elements; +} CDEQUE; + +static int CDEQUE_is_empty(CDEQUE *dq) +{ + return LIST_is_empty(&dq->elements); +} + +static void CDEQUE_init(CDEQUE *dq) +{ + LIST_init(&dq->elements); +} + +#define THIS ((CDEQUE *) _object) + +BEGIN_METHOD_VOID(Deque_new) + + CDEQUE_init(THIS); + +END_METHOD + +static CDEQUE_ELEM *CDEQUE_pop_front(CDEQUE *dq) +{ + CDEQUE_ELEM *elem; + + if (CDEQUE_is_empty(dq)) + return NULL; + elem = get_elem(dq->elements.next); + LIST_unlink(&elem->list); + return elem; +} + +static void CDEQUE_pop_and_free_all(CDEQUE *dq) +{ + while (!CDEQUE_is_empty(dq)) + CDEQUE_destroy_elem(CDEQUE_pop_front(dq)); +} + +BEGIN_METHOD_VOID(Deque_free) + + CDEQUE_pop_and_free_all(THIS); + +END_METHOD + +static void CDEQUE_push_front(CDEQUE *dq, CDEQUE_ELEM *elem) +{ + LIST_append(&dq->elements, &elem->list); +} + +BEGIN_METHOD(Deque_PushFront, GB_VARIANT value) + + CDEQUE_ELEM *elem; + + elem = CDEQUE_new_elem(ARG(value)); + CDEQUE_push_front(THIS, elem); + +END_METHOD + +static void CDEQUE_push_back(CDEQUE *dq, CDEQUE_ELEM *elem) +{ + LIST_prepend(&dq->elements, &elem->list); +} + +BEGIN_METHOD(Deque_PushBack, GB_VARIANT value) + + CDEQUE_ELEM *elem; + + elem = CDEQUE_new_elem(ARG(value)); + CDEQUE_push_back(THIS, elem); + +END_METHOD + +BEGIN_METHOD_VOID(Deque_PopFront) + + CDEQUE_ELEM *elem; + + if (CDEQUE_is_empty(THIS)) { + GB.ReturnNull(); + GB.ReturnConvVariant(); + return; + } + elem = CDEQUE_pop_front(THIS); + GB.ReturnVariant(&elem->var); + GB.ReturnBorrow(); + GB.StoreVariant(NULL, &elem->var); + GB.ReturnRelease(); + CDEQUE_destroy_elem(elem); + +END_METHOD + +static CDEQUE_ELEM *CDEQUE_pop_back(CDEQUE *dq) +{ + CDEQUE_ELEM *elem; + + if (CDEQUE_is_empty(dq)) + return NULL; + elem = get_elem(dq->elements.prev); + LIST_unlink(&elem->list); + return elem; +} + +BEGIN_METHOD_VOID(Deque_PopBack) + + CDEQUE_ELEM *elem; + + if (CDEQUE_is_empty(THIS)) { + GB.ReturnNull(); + GB.ReturnConvVariant(); + return; + } + elem = CDEQUE_pop_back(THIS); + GB.ReturnVariant(&elem->var); + GB.ReturnBorrow(); + GB.StoreVariant(NULL, &elem->var); + GB.ReturnRelease(); + CDEQUE_destroy_elem(elem); + +END_METHOD + +BEGIN_METHOD_VOID(Deque_PeekFront) + + CDEQUE_ELEM *elem; + + if (CDEQUE_is_empty(THIS)) { + GB.ReturnNull(); + GB.ReturnConvVariant(); + } else { + elem = get_elem(THIS->elements.next); + GB.ReturnVariant(&elem->var); + } + +END_METHOD + +BEGIN_METHOD_VOID(Deque_PeekBack) + + CDEQUE_ELEM *elem; + + if (CDEQUE_is_empty(THIS)) { + GB.ReturnNull(); + GB.ReturnConvVariant(); + } else { + elem = get_elem(THIS->elements.prev); + GB.ReturnVariant(&elem->var); + } + +END_METHOD + +BEGIN_METHOD_VOID(Deque_Clear) + + CDEQUE_pop_and_free_all(THIS); + +END_METHOD + +static void CDEQUE_copy(CDEQUE *src, CDEQUE *dst) +{ + LIST *node; + + list_for_each(node, &src->elements) + CDEQUE_push_back(dst, CDEQUE_copy_elem(get_elem(node))); +} + +BEGIN_METHOD_VOID(Deque_Copy) + + CDEQUE *copy; + + /* This method is also used by Queue and Stack, so get the right + * class. */ + copy = GB.New(GB.GetClass(THIS), NULL, NULL); + CDEQUE_copy(THIS, copy); + GB.ReturnObject(copy); + +END_METHOD + +BEGIN_PROPERTY(Deque_IsEmpty) + + GB.ReturnBoolean(CDEQUE_is_empty(THIS)); + +END_PROPERTY + +BEGIN_PROPERTY(Deque_Size) + + size_t size; + LIST *node; + + size = 0; + list_for_each(node, &THIS->elements) + size++; + GB.ReturnInteger(size); + +END_PROPERTY + +/* + * Double-ended queue + */ + +GB_DESC CDeque[] = { + GB_DECLARE("Deque", sizeof(CDEQUE)), + + GB_METHOD("_new", NULL, Deque_new, NULL), + GB_METHOD("_free", NULL, Deque_free, NULL), + + GB_METHOD("PushFront", NULL, Deque_PushFront, "(Value)v"), + GB_METHOD("PushBack", NULL, Deque_PushBack, "(Value)v"), + GB_METHOD("PopFront", "v", Deque_PopFront, NULL), + GB_METHOD("PopBack", "v", Deque_PopBack, NULL), + GB_METHOD("PeekFront", "v", Deque_PeekFront, NULL), + GB_METHOD("PeekBack", "v", Deque_PeekBack, NULL), + + GB_METHOD("Clear", NULL, Deque_Clear, NULL), + GB_METHOD("Copy", "Deque", Deque_Copy, NULL), + + GB_PROPERTY_READ("IsEmpty", "b", Deque_IsEmpty), + GB_PROPERTY_READ("Size", "i", Deque_Size), + + GB_END_DECLARE +}; + +/* + * LIFO + */ + +GB_DESC CStack[] = { + GB_DECLARE("Stack", sizeof(CDEQUE)), + + GB_METHOD("_new", NULL, Deque_new, NULL), + GB_METHOD("_free", NULL, Deque_free, NULL), + + GB_METHOD("Push", NULL, Deque_PushBack, "(Value)v"), + GB_METHOD("Pop", "v", Deque_PopBack, NULL), + GB_METHOD("Peek", "v", Deque_PeekBack, NULL), + + GB_METHOD("Clear", NULL, Deque_Clear, NULL), + GB_METHOD("Copy", "Stack", Deque_Copy, NULL), + + GB_PROPERTY_READ("IsEmpty", "b", Deque_IsEmpty), + GB_PROPERTY_READ("Size", "i", Deque_Size), + + GB_END_DECLARE +}; + +/* + * FIFO + */ + +GB_DESC CQueue[] = { + GB_DECLARE("Queue", sizeof(CDEQUE)), + + GB_METHOD("_new", NULL, Deque_new, NULL), + GB_METHOD("_free", NULL, Deque_free, NULL), + + GB_METHOD("Enqueue", NULL, Deque_PushBack, "(Value)v"), + GB_METHOD("Enq", NULL, Deque_PushBack, "(Value)v"), + GB_METHOD("Dequeue", "v", Deque_PopFront, NULL), + GB_METHOD("Deq", "v", Deque_PopFront, NULL), + GB_METHOD("Peek", "v", Deque_PeekFront, NULL), + + GB_METHOD("Clear", NULL, Deque_Clear, NULL), + GB_METHOD("Copy", "Queue", Deque_Copy, NULL), + + GB_PROPERTY_READ("IsEmpty", "b", Deque_IsEmpty), + GB_PROPERTY_READ("Size", "i", Deque_Size), + + GB_END_DECLARE +}; + +/* + * We keep the Priority queue sorted by a special Enqueue() function. + * The higher the priority, the closer to the beginning the value will be + * enqueued. For equal priorities, the later added element will be enqueued + * at a higher index. + * + * TODO: This is an O(n) search over a linked list. We gotta change that! + */ + +static void CPRIOQ_enqueue(CDEQUE *dq, CDEQUE_ELEM *elem, int prio) +{ + LIST *next; + CDEQUE_ELEM *e; + + /* Just find the right node to prepend the new value to */ + list_for_each(next, &dq->elements) { + e = get_elem(next); + if (prio > e->prio) + break; + } + elem->prio = prio; + LIST_prepend(next, &elem->list); +} + +BEGIN_METHOD(PrioQueue_Enqueue, GB_VARIANT value; GB_INTEGER prio) + + CDEQUE_ELEM *elem; + + elem = CDEQUE_new_elem(ARG(value)); + CPRIOQ_enqueue(THIS, elem, VARG(prio)); + +END_METHOD + +/* + * Priority FIFO + */ + +GB_DESC CPrioQueue[] = { + GB_DECLARE("PrioQueue", sizeof(CDEQUE)), + + GB_METHOD("_new", NULL, Deque_new, NULL), + GB_METHOD("_free", NULL, Deque_free, NULL), + + GB_METHOD("Enqueue", NULL, PrioQueue_Enqueue, "(Value)v(Prio)i"), + GB_METHOD("Enq", NULL, PrioQueue_Enqueue, "(Value)v(Prio)i"), + GB_METHOD("Dequeue", "v", Deque_PopFront, NULL), + GB_METHOD("Deq", "v", Deque_PopFront, NULL), + GB_METHOD("Peek", "v", Deque_PeekFront, NULL), + + GB_METHOD("Clear", NULL, Deque_Clear, NULL), + + GB_PROPERTY_READ("IsEmpty", "b", Deque_IsEmpty), + GB_PROPERTY_READ("Size", "i", Deque_Size), + + GB_END_DECLARE +}; diff --git a/main/lib/data/c_deque.h b/main/lib/data/c_deque.h new file mode 100644 index 00000000..3ceb8bfc --- /dev/null +++ b/main/lib/data/c_deque.h @@ -0,0 +1,36 @@ +/* + * c_deque.h + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_DEQUE_H +#define __C_DEQUE_H + +#include "gambas.h" + +extern GB_INTERFACE GB; + +#ifndef __C_DEQUE_C +extern GB_DESC CDeque[]; +extern GB_DESC CStack[]; +extern GB_DESC CQueue[]; +extern GB_DESC CPrioQueue[]; +#endif + +#endif /* !__C_DEQUE_H */ diff --git a/main/lib/data/c_graph.c b/main/lib/data/c_graph.c new file mode 100644 index 00000000..4fc622f5 --- /dev/null +++ b/main/lib/data/c_graph.c @@ -0,0 +1,485 @@ +/* + * c_graph.c - (Un)Directed, (un)weighted Graph interface + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_GRAPH_C + +#include + +#include "gambas.h" +#include "c_graph.h" + +static GB_HASHTABLE interfaces; + +BEGIN_METHOD_VOID(Graph_init) + + GB.HashTable.New(&interfaces, GB_COMP_BINARY); + +END_METHOD + +static void enum_func(void *x) +{ + GB.Free(&x); +} + +BEGIN_METHOD_VOID(Graph_exit) + + GB.HashTable.Enum(interfaces, (GB_HASHTABLE_ENUM_FUNC) enum_func); + GB.HashTable.Free(&interfaces); + +END_METHOD + +BEGIN_METHOD_VOID(Graph_NoMethod) + + GB.Error("This method is not implemented."); + +END_METHOD + +BEGIN_PROPERTY(Graph_NoProperty) + + GB.Error("This property is not implemented."); + +END_PROPERTY + +BEGIN_METHOD(Graph_call, GB_BOOLEAN d; GB_BOOLEAN w) + + GB_CLASS GraphMatrixClass; + + GB.Push(2, GB_T_BOOLEAN, VARGOPT(d, 0), + GB_T_BOOLEAN, VARGOPT(w, 0)); + GraphMatrixClass = GB.FindClass("GraphMatrix"); + GB.ReturnObject(GB.New(GraphMatrixClass, NULL, (void *) (intptr_t) 2)); + +END_METHOD + +#define get_func(func, arg, ret, need) \ +do { \ + GB_FUNCTION f; \ + \ + err = #func; \ + if (GB.GetFunction(&f, _object, #func, arg, ret)) { \ + if (need) \ + goto error; \ + else /* Clear error */ \ + GB.Error(NULL); \ + desc->func = NULL; \ + } else { \ + desc->func = f.desc; \ + } \ +} while(0) + +GRAPH_DESC *get_desc(void *_object) +{ + GRAPH_DESC *desc; + const char *err; + + GB.Alloc((void **) &desc, sizeof(*desc)); + get_func(_getVertex, "s", NULL, 0); + get_func(_getEdge, "ss", NULL, 0); + get_func(_nextVertex, NULL, "s", 0); + get_func(_nextEdge, NULL, "String[]", 0); + get_func(_countVertices, NULL, "i", 0); + get_func(_countEdges, NULL, "i", 0); + get_func(_nextInEdge, NULL, "String[]", 0); + get_func(_nextOutEdge, NULL, "String[]", 0); + get_func(_nextAdjacent, NULL, "s", 0); + get_func(_vertexProperty, ".", "b", 0); + get_func(_edgeProperty, ".", "b", 0); + get_func(_vertexUnknown, ".", "v", 0); + get_func(_edgeUnknown, ".", "v", 0); + return desc; + +error: + GB.Error("Method \"&1\" not found", err); + GB.Free((void **) &desc); + return NULL; +} + +#define THIS ((CGRAPH *) _object) + +BEGIN_METHOD_VOID(Graph_new) + + GRAPH_DESC *desc; + char *name = GB.GetClassName(THIS); + size_t len = GB.StringLength(name); + + if (GB.HashTable.Get(interfaces, name, len, (void **) &desc)) { + desc = get_desc(THIS); + GB.HashTable.Add(interfaces, name, len, desc); + } + THIS->desc = desc; + THIS->vertex = NULL; + GB.Array.New(&THIS->edge, GB_T_STRING, 2); + THIS->tag.type = GB_T_NULL; + +END_METHOD + +BEGIN_METHOD_VOID(Graph_free) + + GB.StoreVariant(NULL, &THIS->tag); + GB.Unref(&THIS->edge); + GB.FreeString(&THIS->vertex); + +END_METHOD + +BEGIN_PROPERTY(Graph_Tag) + + if (READ_PROPERTY) { + GB.ReturnVariant(&THIS->tag); + return; + } + GB.StoreVariant(PROP(GB_VARIANT), &THIS->tag); + +END_PROPERTY + +#define SET_VERTEX() \ +do { \ + GB.FreeString(&THIS->vertex); \ + THIS->vertex = GB.NewString(STRING(vert), LENGTH(vert));\ +} while (0) + +#define SET_VERTEX_P() \ +do { \ + GB.FreeString(&THIS->vertex); \ + THIS->vertex = GB.NewString(PSTRING(), PLENGTH()); \ +} while (0) + +#define SET_EDGE() \ +do { \ + GB.StoreString(ARG(src), GB.Array.Get(THIS->edge, 0)); \ + GB.StoreString(ARG(dst), GB.Array.Get(THIS->edge, 1)); \ +} while (0) + +#define SET_EDGE_P() \ +do { \ + GB.StoreObject(VPROP(GB_OBJECT), &THIS->edge); \ +} while (0) + +BEGIN_PROPERTY(Graph_Vertex) + + if (READ_PROPERTY) { + GB.ReturnString(THIS->vertex); + return; + } + SET_VERTEX_P(); + +END_PROPERTY + +BEGIN_PROPERTY(Graph_Edge) + + if (READ_PROPERTY) { + GB.ReturnObject(THIS->edge); + return; + } + SET_EDGE_P(); + +END_PROPERTY + +GB_DESC CGraph[] = { + GB_DECLARE("Graph", sizeof(CGRAPH)), + GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("_init", NULL, Graph_init, NULL), + GB_STATIC_METHOD("_exit", NULL, Graph_exit, NULL), + + GB_STATIC_METHOD("_call", "Graph", Graph_call, "[(Directed)b(Weighted)b]"), + GB_METHOD("_new", NULL, Graph_new, NULL), + GB_METHOD("_free", NULL, Graph_free, NULL), + + /* Graph */ + GB_METHOD("_getVertex", ".Graph.Vertex", Graph_NoMethod, "(Vertex)s"), + GB_METHOD("_getEdge", ".Graph.Edge", Graph_NoMethod, "(Src)s(Dst)s"), + + GB_METHOD("_nextVertex", "s", Graph_NoMethod, NULL), + GB_METHOD("_nextEdge", "String[]", Graph_NoMethod, NULL), + + GB_METHOD("_countVertices", "i", Graph_NoMethod, NULL), + GB_METHOD("_countEdges", "i", Graph_NoMethod, NULL), + + /* Vertex */ + GB_METHOD("_nextInEdge", "String[]", Graph_NoMethod, NULL), + GB_METHOD("_nextOutEdge", "String[]", Graph_NoMethod, NULL), + + /* Public */ + GB_METHOD("Add", ".Graph.Vertex", Graph_NoMethod, "(Name)s"), + GB_METHOD("Remove", NULL, Graph_NoMethod, "(Vertex)s"), + GB_METHOD("Connect", ".Graph.Edge", Graph_NoMethod, "(Src)s(Dst)s"), + GB_METHOD("Disconnect", NULL, Graph_NoMethod, "(Src)s(Dst)s"), + + GB_PROPERTY("Tag", "v", Graph_Tag), + + GB_PROPERTY("_Vertex", "s", Graph_Vertex), + GB_PROPERTY("_Edge", "String[]", Graph_Edge), + + GB_PROPERTY_SELF("Vertices", ".Graph.Vertices"), + GB_PROPERTY_SELF("Edges", ".Graph.Edges"), + + GB_PROPERTY_SELF("InEdges", ".Graph.InEdges"), + GB_PROPERTY_SELF("OutEdges", ".Graph.OutEdges"), + GB_PROPERTY_SELF("Adjacent", ".Graph.Adjacent"), + + GB_END_DECLARE +}; + +/* + * Call another method/property of this class and fail if not found. + */ +#define CALL_GRAPH(func, narg) \ +do { \ + GB_FUNCTION f; \ + \ + f.desc = THIS->desc->func; \ + f.object = THIS; \ + GB.Call(&f, narg, 0); \ +} while (0) + +#define CALL_GRAPH_VAL(func, narg, arg, ret, retval) \ +do { \ + GB_FUNCTION f; \ + \ + if (GB.GetFunction(&f, _object, func, arg, ret)) { \ + GB.Error("Method " func " not found"); \ + return; \ + } \ + retval = GB.Call(&f, narg, 0); \ +} while (0) + +/* + * Try to call another method/property of this class. If found, return + * afterwards. If not, continue. + */ +#define TRY_CALL_GRAPH(func, narg) \ +do { \ + if (THIS->desc->func) { \ + CALL_GRAPH(func, narg); \ + return; \ + } \ +} while (0) + +BEGIN_METHOD_VOID(GraphVertices_next) + + CALL_GRAPH(_nextVertex, 0); + +END_METHOD + +BEGIN_METHOD(GraphVertices_get, GB_STRING vert) + + SET_VERTEX(); + GB.ReturnSelf(THIS); + +END_METHOD + +BEGIN_PROPERTY(GraphVertices_Count) + + CALL_GRAPH(_countVertices, 0); + /* Default: enumerate the vertices, counting. Then use + * TRY_CALL_GRAPH. */ + +END_PROPERTY + +GB_DESC CGraphVertices[] = { + GB_DECLARE_VIRTUAL(".Graph.Vertices"), + + GB_METHOD("_next", "s", GraphVertices_next, NULL), + GB_METHOD("_get", ".Graph.Vertex", GraphVertices_get, "(Vertex)s"), + + GB_PROPERTY_READ("Count", "i", GraphVertices_Count), + + GB_END_DECLARE +}; + +BEGIN_METHOD_VOID(GraphEdges_next) + + CALL_GRAPH(_nextEdge, 0); + +END_METHOD + +BEGIN_METHOD(GraphEdges_get, GB_STRING src; GB_STRING dst) + + SET_EDGE(); + GB.ReturnSelf(THIS); + +END_METHOD + +BEGIN_PROPERTY(GraphEdges_Count) + + CALL_GRAPH(_countEdges, 0); + /* Default: enumerate the edges, counting. Then use + * TRY_CALL_GRAPH. */ + +END_PROPERTY + +GB_DESC CGraphEdges[] = { + GB_DECLARE_VIRTUAL(".Graph.Edges"), + + GB_METHOD("_next", "String[]", GraphEdges_next, NULL), + GB_METHOD("_get", ".Graph.Edge", GraphEdges_get, "(Src)s(Dst)s"), + GB_METHOD("Exist", "b", Graph_NoMethod, "(Src)s(Dst)s"), + + GB_PROPERTY_READ("Count", "i", GraphEdges_Count), + + GB_END_DECLARE +}; + +BEGIN_METHOD(GraphInEdges_get, GB_STRING vert) + + SET_VERTEX(); + GB.ReturnSelf(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(GraphInEdges_next) + + CALL_GRAPH(_nextInEdge, 0); + +END_METHOD + +GB_DESC CGraphInEdges[] = { + GB_DECLARE_VIRTUAL(".Graph.InEdges"), + + GB_METHOD("_get", ".Graph.InEdges", GraphInEdges_get, "(Vertex)s"), + GB_METHOD("_next", "String[]", GraphInEdges_next, NULL), + + GB_END_DECLARE +}; + +BEGIN_METHOD(GraphOutEdges_get, GB_STRING vert) + + SET_VERTEX(); + GB.ReturnSelf(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(GraphOutEdges_next) + + CALL_GRAPH(_nextOutEdge, 0); + +END_METHOD + +GB_DESC CGraphOutEdges[] = { + GB_DECLARE_VIRTUAL(".Graph.OutEdges"), + + GB_METHOD("_get", ".Graph.OutEdges", GraphOutEdges_get, "(Vertex)s"), + GB_METHOD("_next", "String[]", GraphOutEdges_next, NULL), + + GB_END_DECLARE +}; + +BEGIN_METHOD(GraphAdjacent_get, GB_STRING vert) + + SET_VERTEX(); + GB.ReturnSelf(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(GraphAdjacent_next) + + CALL_GRAPH(_nextAdjacent, 0); + +END_METHOD + +GB_DESC CGraphAdjacent[] = { + GB_DECLARE_VIRTUAL(".Graph.Adjacent"), + + GB_METHOD("_get", ".Graph.Adjacent", GraphAdjacent_get, "(Vertex)s"), + GB_METHOD("_next", "s", GraphAdjacent_next, NULL), + + GB_END_DECLARE +}; + +BEGIN_PROPERTY(GraphVertex_InDegree) + + /* Call ??? */ + /* Default: enumerate */ + assert(0); + +END_PROPERTY + +BEGIN_PROPERTY(GraphVertex_OutDegree) + + /* Call ??? */ + /* Default: enumerate */ + assert(0); + +END_PROPERTY + +BEGIN_METHOD_VOID(GraphVertex_property) + + CALL_GRAPH(_vertexProperty, GB.NParam()); + +END_METHOD + +BEGIN_METHOD_VOID(GraphVertex_unknown) + + CALL_GRAPH(_vertexUnknown, GB.NParam()); + +END_METHOD + +GB_DESC CGraphVertex[] = { + GB_DECLARE_VIRTUAL(".Graph.Vertex"), + + GB_PROPERTY_READ("InDegree", "i", GraphVertex_InDegree), + GB_PROPERTY_READ("OutDegree", "i", GraphVertex_OutDegree), + + GB_METHOD("_property", "b", GraphVertex_property, "."), + GB_METHOD("_unknown", "v", GraphVertex_unknown, "."), + + GB_END_DECLARE +}; + +BEGIN_PROPERTY(GraphEdge_Src) + + GB.ReturnString(GB.Array.Get(THIS->edge, 0)); + +END_PROPERTY + +BEGIN_PROPERTY(GraphEdge_Dst) + + GB.ReturnString(GB.Array.Get(THIS->edge, 1)); + +END_PROPERTY + +BEGIN_METHOD_VOID(GraphEdge_property) + + CALL_GRAPH(_edgeProperty, GB.NParam()); + +END_METHOD + +BEGIN_METHOD_VOID(GraphEdge_unknown) + + CALL_GRAPH(_edgeUnknown, GB.NParam()); + +END_METHOD + +GB_DESC CGraphEdge[] = { + GB_DECLARE_VIRTUAL(".Graph.Edge"), + + GB_PROPERTY_READ("Src", "s", GraphEdge_Src), + GB_PROPERTY_READ("Dst", "s", GraphEdge_Dst), + + GB_PROPERTY_READ("Source", "s", GraphEdge_Src), + GB_PROPERTY_READ("Destination", "s", GraphEdge_Dst), + GB_PROPERTY("Weight", "f", Graph_NoProperty), + + GB_METHOD("_property", "b", GraphEdge_property, "."), + GB_METHOD("_unknown", "v", GraphEdge_unknown, "."), + + GB_END_DECLARE +}; diff --git a/main/lib/data/c_graph.h b/main/lib/data/c_graph.h new file mode 100644 index 00000000..c893e737 --- /dev/null +++ b/main/lib/data/c_graph.h @@ -0,0 +1,66 @@ +/* + * c_graph.h + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_GRAPH_H +#define __C_GRAPH_H + +#include "gambas.h" + +extern GB_INTERFACE GB; + +typedef struct { + void *_getVertex; + void *_getEdge; + + void *_nextVertex; + void *_nextEdge; + + void *_countVertices; + void *_countEdges; + + void *_nextInEdge; + void *_nextOutEdge; + void *_nextAdjacent; + + void *_vertexProperty; + void *_edgeProperty; + + void *_vertexUnknown; + void *_edgeUnknown; +} GRAPH_DESC; + +typedef struct { + GB_BASE ob; + GRAPH_DESC *desc; + char *vertex; + GB_ARRAY edge; + GB_VARIANT_VALUE tag; +} CGRAPH; + +extern GRAPH_DESC *get_desc(void *_object); + +#ifndef __C_GRAPH_C +extern GB_DESC CGraph[], CGraphVertices[], CGraphEdges[], CGraphInEdges[], + CGraphOutEdges[], CGraphAdjacent[], CGraphVertex[], + CGraphEdge[]; +#endif + +#endif /* __C_GRAPH_H */ diff --git a/main/lib/data/c_graphmatrix.c b/main/lib/data/c_graphmatrix.c new file mode 100644 index 00000000..7241e311 --- /dev/null +++ b/main/lib/data/c_graphmatrix.c @@ -0,0 +1,699 @@ +/* + * c_graphmatrix.c - Graph as adjacency matrix + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_GRAPHMATRIX_C + +#include + +#include "gambas.h" +#include "c_graph.h" +#include "c_graphmatrix.h" + +#define E_NOVERTEX "Vertex does not exist" +#define E_NOEDGE "Edge does not exist" +#define E_NOPUT "No suitable _put method in the Matrix class" + +typedef struct { + int set : 1; + double weight; +} EDGE; + +/* + * The VERT structure is a row in the adjacency matrix. + */ +typedef struct { + EDGE *edges; + GB_VARIANT_VALUE val; + char *name; +} VERT; + +/* Virtual object selector or part of enumeration state */ +union virt { + unsigned int vertex; + struct { + unsigned int src, dst; + }; +}; + +typedef struct { + CGRAPH base; + int directed : 1; + int weighted : 1; + GB_HASHTABLE names; + VERT *matrix; + /* + * NOTE: This field is used by the "true" virtual object portions of + * this class: GraphMatrix.Vertices[x] and GraphMatrix.Edges[x, y]. + * + * In the enumerators (InEdges, OutEdges, Adjacent), we must use the + * "current" vertex/edge which is stored within ->base. + */ + union virt v; + void *gsl_matrix; /* Cache gb.gsl Matrix object of this */ +} CMATRIX; + +#define THIS ((CMATRIX *) _object) + +BEGIN_METHOD(Matrix_new, GB_BOOLEAN d; GB_BOOLEAN w) + + THIS->directed = VARGOPT(d, 0); + THIS->weighted = VARGOPT(w, 0); + THIS->v.vertex = -1; + THIS->v.src = THIS->v.dst = -1; + GB.HashTable.New(&THIS->names, GB_COMP_NOCASE); + GB.NewArray(&THIS->matrix, sizeof(*THIS->matrix), 0); + THIS->gsl_matrix = NULL; + +END_METHOD + +BEGIN_METHOD_VOID(Matrix_free) + + unsigned int i, count = GB.Count(THIS->matrix); + + GB.HashTable.Free(&THIS->names); + for (i = 0; i < count; i++) { + VERT *cur = &THIS->matrix[i]; + + GB.FreeString(&cur->name); + GB.FreeArray(&cur->edges); + GB.StoreVariant(NULL, &cur->val); + } + GB.FreeArray(&THIS->matrix); + GB.Unref(&THIS->gsl_matrix); + +END_METHOD + +static unsigned int get_vertex(CMATRIX *mat, const char *str, size_t len) +{ + uintptr_t vert; /* Be wide enough to get a void *! */ + + if (GB.HashTable.Get(mat->names, str, len, (void **) &vert)) + return -1; + assert(vert >= 0 && vert < GB.Count(mat->matrix)); + return (unsigned int) vert; +} + +static unsigned int get_cur_vertex(CMATRIX *mat) +{ + return get_vertex(mat, mat->base.vertex, + GB.StringLength(mat->base.vertex)); +} + +BEGIN_METHOD(Matrix_getVertex, GB_STRING vert) + + unsigned int vert = get_vertex(THIS, STRING(vert), LENGTH(vert)); + + if (vert == -1) { + GB.Error(E_NOVERTEX); + return; + } + THIS->v.vertex = vert; + GB.ReturnSelf(THIS); + +END_METHOD + +BEGIN_METHOD(Matrix_getEdge, GB_STRING src; GB_STRING dst) + + unsigned int src = get_vertex(THIS, STRING(src), LENGTH(src)), + dst = get_vertex(THIS, STRING(dst), LENGTH(dst)); + + if (src == -1 || dst == -1) { + GB.Error(E_NOVERTEX); + return; + } + if (!THIS->matrix[src].edges[dst].set) { + GB.Error(E_NOEDGE); + return; + } + THIS->v.src = src; THIS->v.dst = dst; + GB.ReturnSelf(THIS); + +END_METHOD + +struct enum_state { + union virt v; + GB_ARRAY e; +}; + +BEGIN_METHOD_VOID(Matrix_nextVertex) + + struct enum_state *state = GB.GetEnum(); + + if (state->v.vertex == GB.Count(THIS->matrix)) { + GB.StopEnum(); + return; + } + GB.ReturnString(THIS->matrix[state->v.vertex++].name); + +END_METHOD + +/* Return non-zero if no next edge was found */ +static int next_edge(CMATRIX *mat, unsigned int *srcp, unsigned int *dstp) +{ + unsigned int src = *srcp, dst = *dstp; + unsigned int count = GB.Count(mat->matrix); + + do { + dst = (dst + 1) % count; + if (!dst) + src++; + if (src >= count) + return -1; + } while (!mat->matrix[src].edges[dst].set); + *srcp = src; + *dstp = dst; + return 0; +} + +/**G + * The same String[] object is used during the enumeration. If you want to + * save a snapshot of it, use String[].Copy(). + * + * Also, you should maybe not change the contents of this array. It may work + * now but I can't guarantee it will in the future. + */ +BEGIN_METHOD_VOID(Matrix_nextEdge) + + struct enum_state *state = GB.GetEnum(); + unsigned int src = state->v.src, dst = state->v.dst; + + if (!state->e) { + GB.Array.New(&state->e, GB_T_STRING, 2); + GB.Ref(state->e); + /* Try edge 0,0 which is a special case according to the + * logic below. */ + if (THIS->matrix[src].edges[dst].set) + goto found; + } + /* End of enumeration? */ + if (next_edge(THIS, &src, &dst)) { + GB.StopEnum(); + GB.Unref(&state->e); + return; + } + state->v.src = src; state->v.dst = dst; +found:; + GB_STRING str; + + str.type = GB_T_STRING; + + str.value.addr = THIS->matrix[src].name; + str.value.start = 0; + str.value.len = GB.StringLength(str.value.addr); + GB.StoreString(&str, GB.Array.Get(state->e, 0)); + + str.value.addr = THIS->matrix[dst].name; + str.value.len = GB.StringLength(str.value.addr); + GB.StoreString(&str, GB.Array.Get(state->e, 1)); + GB.ReturnObject(state->e); + +END_METHOD + +BEGIN_METHOD_VOID(Matrix_countVertices) + + GB.ReturnInteger(GB.Count(THIS->matrix)); + +END_METHOD + +BEGIN_METHOD_VOID(Matrix_countEdges) + + unsigned int i, j, count = GB.Count(THIS->matrix), edges = 0; + + for (i = 0; i < count; i++) + for (j = 0; j < count; j++) + if (THIS->matrix[i].edges[j].set) + edges++; + GB.ReturnInteger(edges); + +END_METHOD + +static int next_edge_vertical(CMATRIX *mat, unsigned int *srcp, + unsigned int *dstp) +{ + unsigned int src = *srcp, dst = *dstp; + unsigned int count = GB.Count(mat->matrix); + + do { + src = (src + 1) % count; + if (!src) + dst++; + if (dst >= count) + return -1; + } while (!mat->matrix[src].edges[dst].set); + *srcp = src; + *dstp = dst; + return 0; +} + +BEGIN_METHOD_VOID(Matrix_nextInEdge) + + struct enum_state *state = GB.GetEnum(); + unsigned int src = state->v.src, dst = state->v.dst; + + if (!state->e) { + dst = state->v.dst = get_cur_vertex(THIS); + GB.Array.New(&state->e, GB_T_STRING, 2); + GB.Ref(state->e); + if (THIS->matrix[src].edges[dst].set) + goto found; + } + if (next_edge_vertical(THIS, &src, &dst) || dst != state->v.dst) { + GB.StopEnum(); + GB.Unref(&state->e); + return; + } + state->v.src = src; +found:; + GB_STRING str; + + str.type = GB_T_STRING; + + str.value.addr = THIS->matrix[src].name; + str.value.start = 0; + str.value.len = GB.StringLength(str.value.addr); + GB.StoreString(&str, GB.Array.Get(state->e, 0)); + + str.value.addr = THIS->matrix[dst].name; + str.value.len = GB.StringLength(str.value.addr); + GB.StoreString(&str, GB.Array.Get(state->e, 1)); + GB.ReturnObject(state->e); + +END_METHOD + +BEGIN_METHOD_VOID(Matrix_nextOutEdge) + + struct enum_state *state = GB.GetEnum(); + unsigned int src = state->v.src, dst = state->v.dst; + + if (!state->e) { + src = state->v.src = get_cur_vertex(THIS); + GB.Array.New(&state->e, GB_T_STRING, 2); + GB.Ref(state->e); + if (THIS->matrix[src].edges[dst].set) + goto found; + } + if (next_edge(THIS, &src, &dst) || src != state->v.src) { + GB.StopEnum(); + GB.Unref(&state->e); + return; + } + state->v.dst = dst; +found:; + GB_STRING str; + + str.type = GB_T_STRING; + + str.value.addr = THIS->matrix[src].name; + str.value.start = 0; + str.value.len = GB.StringLength(str.value.addr); + GB.StoreString(&str, GB.Array.Get(state->e, 0)); + + str.value.addr = THIS->matrix[dst].name; + str.value.len = GB.StringLength(str.value.addr); + GB.StoreString(&str, GB.Array.Get(state->e, 1)); + GB.ReturnObject(state->e); + +END_METHOD + +BEGIN_METHOD_VOID(Matrix_nextAdjacent) + + struct enum_state *state = GB.GetEnum(); + unsigned int src = state->v.src, dst = state->v.dst; + + if (!state->e) { + src = state->v.src = get_cur_vertex(THIS); + state->e = (void *) 1; + if (THIS->matrix[src].edges[dst].set) + goto found; + } + if (next_edge(THIS, &src, &dst) || src != state->v.src) { + GB.StopEnum(); + return; + } + state->v.dst = dst; +found: + GB.ReturnString(THIS->matrix[dst].name); + +END_METHOD + +/* TODO: This is used in Add() and Remove() to completely delete the gb.gsl + * matrix. We could also enlarge/shrink it accordingly... */ +static void invalidate_gsl_matrix(CMATRIX *mat) +{ + GB.Unref(&mat->gsl_matrix); + mat->gsl_matrix = NULL; +} + +static void update_gsl_matrix(CMATRIX *mat, unsigned i, unsigned j) +{ + GB_FUNCTION put; + + if (!mat->gsl_matrix) + return; + if (GB.GetFunction(&put, mat->gsl_matrix, "_put", "vii", NULL)) { + GB.Error(E_NOPUT); + return; + } + GB.Push(3, GB_T_INTEGER, !!mat->matrix[i].edges[j].set, + GB_T_INTEGER, i, + GB_T_INTEGER, j); + GB.Call(&put, 3, 0); +} + +BEGIN_METHOD(Matrix_Add, GB_STRING name) + + unsigned int vert = get_vertex(THIS, STRING(name), LENGTH(name)), i; + VERT *new; + + if (vert != -1) + goto end; + + vert = GB.Count(THIS->matrix); + new = GB.Add(&THIS->matrix); + /* Add edge buffers to all other vertices */ + for (i = 0; i < vert; i++) { + EDGE *e = GB.Add(&THIS->matrix[i].edges); + + e->set = 0; + e->weight = 0; + } + GB.NewArray(&new->edges, sizeof(*new->edges), vert + 1); + /* No outgoing edges */ + memset(new->edges, 0, (vert + 1) * sizeof(*new->edges)); + + new->val.type = GB_T_NULL; + GB.StoreVariant(NULL, &new->val); + + new->name = GB.NewString(STRING(name), LENGTH(name)); + + GB.HashTable.Add(THIS->names, STRING(name), LENGTH(name), + (void *) (intptr_t) vert); + + invalidate_gsl_matrix(THIS); + +end: + THIS->v.vertex = vert; + GB.ReturnSelf(THIS); + +END_METHOD + +BEGIN_METHOD(Matrix_Remove, GB_STRING vert) + + unsigned int vert = get_vertex(THIS, STRING(vert), LENGTH(vert)); + unsigned int count = GB.Count(THIS->matrix), i; + + if (vert == -1) { + GB.Error(E_NOVERTEX); + return; + } + for (i = 0; i < count; i++) { + if (i == vert) + continue; + GB.Remove(&THIS->matrix[i].edges, i, 1); + } + GB.FreeArray(&THIS->matrix[vert].edges); + GB.StoreVariant(NULL, &THIS->matrix[vert].val); + GB.FreeString(&THIS->matrix[vert].name); + GB.Remove(&THIS->matrix, vert, 1); + + GB.HashTable.Remove(THIS->names, STRING(vert), LENGTH(vert)); + + invalidate_gsl_matrix(THIS); + +END_METHOD + +BEGIN_METHOD(Matrix_Connect, GB_STRING src; GB_STRING dst; GB_FLOAT w) + + unsigned int src = get_vertex(THIS, STRING(src), LENGTH(src)), + dst = get_vertex(THIS, STRING(dst), LENGTH(dst)); + float w = VARGOPT(w, 1); + + if (src == -1 || dst == -1) { + GB.Error(E_NOVERTEX); + return; + } + THIS->matrix[src].edges[dst].set = 1; + THIS->matrix[src].edges[dst].weight = w; + THIS->v.src = src; THIS->v.dst = dst; + update_gsl_matrix(THIS, src, dst); + /* Duplicate if the graph is undirected */ + if (!THIS->directed && src != dst) { + THIS->matrix[dst].edges[src].set = 1; + THIS->matrix[dst].edges[src].weight = w; + update_gsl_matrix(THIS, dst, src); + } + + GB.ReturnSelf(THIS); + +END_METHOD + +BEGIN_METHOD(Matrix_Disconnect, GB_STRING src; GB_STRING dst) + + unsigned int src = get_vertex(THIS, STRING(src), LENGTH(src)), + dst = get_vertex(THIS, STRING(dst), LENGTH(dst)); + + if (src == -1 || dst == -1) { + GB.Error(E_NOVERTEX); + return; + } + THIS->matrix[src].edges[dst].set = 0; + update_gsl_matrix(THIS, src, dst); + if (!THIS->directed && src != dst) { + THIS->matrix[dst].edges[src].set = 0; + update_gsl_matrix(THIS, dst, src); + } + +END_METHOD + +BEGIN_PROPERTY(Matrix_Matrix) + + unsigned int count = GB.Count(THIS->matrix), i, j; + void *obj; + GB_FUNCTION put; + + if (THIS->gsl_matrix) { + GB.ReturnObject(THIS->gsl_matrix); + return; + } + + if (GB.Component.Load("gb.gsl")) { + GB.Error("gb.gsl could not be found"); + return; + } + + GB.Push(3, GB_T_INTEGER, count, + GB_T_INTEGER, count, + GB_T_BOOLEAN, 0); + obj = GB.New(GB.FindClass("Matrix"), NULL, (void *) (intptr_t) 3); + if (GB.GetFunction(&put, obj, "_put", "vii", NULL)) { + GB.Error(E_NOPUT); + return; + } + /* TODO: Direct access possible? */ + for (i = 0; i < count; i++) { + for (j = 0; j < count; j++) { + GB.Push(3, GB_T_INTEGER, + !!THIS->matrix[i].edges[j].set, + GB_T_INTEGER, i, + GB_T_INTEGER, j); + GB.Call(&put, 3, 0); + } + } + THIS->gsl_matrix = obj; + GB.Ref(obj); + GB.ReturnObject(obj); + +END_PROPERTY + +GB_DESC CGraphMatrix[] = { + GB_DECLARE("GraphMatrix", sizeof(CMATRIX)), + GB_INHERITS("Graph"), + + GB_METHOD("_new", NULL, Matrix_new, "[(Directed)b(Weighted)b]"), + GB_METHOD("_free", NULL, Matrix_free, NULL), + + GB_METHOD("_getVertex", ".Matrix.Vertex", Matrix_getVertex, "(Vertex)s"), + GB_METHOD("_getEdge", ".Matrix.Edge", Matrix_getEdge, "(Src)s(Dst)s"), + + GB_METHOD("_nextVertex", "s", Matrix_nextVertex, NULL), + GB_METHOD("_nextEdge", "String[]", Matrix_nextEdge, NULL), + + GB_METHOD("_countVertices", "i", Matrix_countVertices, NULL), + GB_METHOD("_countEdges", "i", Matrix_countEdges, NULL), + + GB_METHOD("_nextInEdge", "String[]", Matrix_nextInEdge, NULL), + GB_METHOD("_nextOutEdge", "String[]", Matrix_nextOutEdge, NULL), + GB_METHOD("_nextAdjacent", "s", Matrix_nextAdjacent, NULL), + + GB_METHOD("Add", ".Matrix.Vertex", Matrix_Add, "(Name)s"), + GB_METHOD("Remove", NULL, Matrix_Remove, "(Vertex)s"), + GB_METHOD("Connect", ".Matrix.Edge", Matrix_Connect, "(Src)s(Dst)s[(Weight)f]"), + GB_METHOD("Disconnect", NULL, Matrix_Disconnect, "(Src)s(Dst)s"), + + GB_PROPERTY_SELF("Vertices", ".Matrix.Vertices"), + GB_PROPERTY_SELF("Edges", ".Matrix.Edges"), + + /* + * Require gb.gsl. + */ + //GB_STATIC_METHOD("FromMatrix", "GraphMatrix", Matrix_FromMatrix, "(Matrix)Matrix;"), + GB_PROPERTY_READ("Matrix", "Matrix", Matrix_Matrix), + + GB_END_DECLARE +}; + +BEGIN_METHOD(MatrixVertices_get, GB_STRING vert) + + THIS->v.vertex = get_vertex(THIS, STRING(vert), LENGTH(vert)); + GB.ReturnSelf(THIS); + +END_METHOD + +GB_DESC CMatrixVertices[] = { + GB_DECLARE_VIRTUAL(".Matrix.Vertices"), + GB_INHERITS(".Graph.Vertices"), + + GB_METHOD("_get", ".Matrix.Vertex", MatrixVertices_get, "(Vertex)s"), + + GB_END_DECLARE +}; + +BEGIN_METHOD(MatrixEdges_get, GB_STRING src; GB_STRING dst) + + THIS->v.src = get_vertex(THIS, STRING(src), LENGTH(src)); + THIS->v.dst = get_vertex(THIS, STRING(dst), LENGTH(dst)); + GB.ReturnSelf(THIS); + +END_METHOD + +GB_DESC CMatrixEdges[] = { + GB_DECLARE_VIRTUAL(".Matrix.Edges"), + GB_INHERITS(".Graph.Edges"), + + GB_METHOD("_get", ".Matrix.Edge", MatrixEdges_get, "(Src)s(Dst)s"), + + GB_END_DECLARE +}; + +BEGIN_PROPERTY(MatrixVertex_InDegree) + + unsigned int i, count = GB.Count(THIS->matrix), deg = 0; + + for (i = 0; i < count; i++) + if (THIS->matrix[i].edges[THIS->v.vertex].set) + deg++; + GB.ReturnInteger(deg); + +END_PROPERTY + +BEGIN_PROPERTY(MatrixVertex_OutDegree) + + unsigned int j, count = GB.Count(THIS->matrix), deg = 0; + + for (j = 0; j < count; j++) + if (THIS->matrix[THIS->v.vertex].edges[j].set) + deg++; + GB.ReturnInteger(deg); + +END_PROPERTY + +BEGIN_PROPERTY(MatrixVertex_Name) + + char *name = THIS->matrix[THIS->v.vertex].name; + + if (READ_PROPERTY) { + GB.ReturnString(name); + return; + } + GB.HashTable.Remove(THIS->names, name, GB.StringLength(name)); + GB.FreeString(&THIS->matrix[THIS->v.vertex].name); + THIS->matrix[THIS->v.vertex].name = + GB.NewString(PSTRING(), PLENGTH()); + GB.HashTable.Add(THIS->names, PSTRING(), PLENGTH(), + (void *) (intptr_t) THIS->v.vertex); + +END_PROPERTY + +BEGIN_PROPERTY(MatrixVertex_Value) + + if (READ_PROPERTY) { + GB.ReturnVariant(&THIS->matrix[THIS->v.vertex].val); + return; + } + GB.StoreVariant(PROP(GB_VARIANT), &THIS->matrix[THIS->v.vertex].val); + +END_METHOD + +GB_DESC CMatrixVertex[] = { + GB_DECLARE_VIRTUAL(".Matrix.Vertex"), + GB_INHERITS(".Graph.Vertex"), + + GB_PROPERTY_READ("InDegree", "i", MatrixVertex_InDegree), + GB_PROPERTY_READ("OutDegree", "i", MatrixVertex_OutDegree), + GB_PROPERTY("Name", "s", MatrixVertex_Name), + GB_PROPERTY("Value", "v", MatrixVertex_Value), + + GB_END_DECLARE +}; + +BEGIN_PROPERTY(MatrixEdge_Src) + + int src = THIS->v.src; + + GB.ReturnString(THIS->matrix[src].name); + +END_PROPERTY + +BEGIN_PROPERTY(MatrixEdge_Dst) + + int dst = THIS->v.dst; + + GB.ReturnString(THIS->matrix[dst].name); + +END_PROPERTY + +BEGIN_PROPERTY(MatrixEdge_Weight) + + int src = THIS->v.src, dst = THIS->v.dst; + + if (READ_PROPERTY) { + GB.ReturnFloat(THIS->matrix[src].edges[dst].weight); + return; + } + THIS->matrix[src].edges[dst].weight = VPROP(GB_FLOAT); + if (!THIS->directed && src != dst) + THIS->matrix[dst].edges[src].weight = VPROP(GB_FLOAT); + +END_PROPERTY + +GB_DESC CMatrixEdge[] = { + GB_DECLARE_VIRTUAL(".Matrix.Edge"), + GB_INHERITS(".Graph.Edge"), + + GB_PROPERTY_READ("Src", "s", MatrixEdge_Src), + GB_PROPERTY_READ("Dst", "s", MatrixEdge_Dst), + GB_PROPERTY("Weight", "f", MatrixEdge_Weight), + + GB_PROPERTY_READ("Source", "s", MatrixEdge_Src), + GB_PROPERTY_READ("Destination", "s", MatrixEdge_Dst), + + GB_END_DECLARE +}; diff --git a/main/lib/data/c_graphmatrix.h b/main/lib/data/c_graphmatrix.h new file mode 100644 index 00000000..fa1309e8 --- /dev/null +++ b/main/lib/data/c_graphmatrix.h @@ -0,0 +1,34 @@ +/* + * c_graphmatrix.h + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_GRAPHMATRIX_H +#define __C_GRAPHMATRIX_H + +#include "gambas.h" + +extern GB_INTERFACE GB; + +#ifndef __C_GRAPHMATRIX_C +extern GB_DESC CGraphMatrix[], CMatrixVertices[], CMatrixEdges[], + CMatrixVertex[], CMatrixEdge[]; +#endif + +#endif /* __C_GRAPHMATRIX_H */ diff --git a/main/lib/data/c_heap.c b/main/lib/data/c_heap.c new file mode 100644 index 00000000..bb4d8d79 --- /dev/null +++ b/main/lib/data/c_heap.c @@ -0,0 +1,406 @@ +/* + * c_heap.c - (Min-/Max-)Heap and PrioSet + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_HEAP_C + +#include "gambas.h" +#include "gb_common.h" /* EXTERN for gbx_compare.h */ +#include "gbx_compare.h" /* GB_COMP_{ASCENT,DESCENT} */ +#include "gbx_type.h" /* TYPE_is_object() */ + +#include "c_heap.h" + +typedef struct { + GB_BASE ob; + int mode; + int count; + GB_VARIANT_VALUE *h; +} CHEAP; /* "Hey! Don't talk like this about my data structures!" */ + +static inline int compare(CHEAP *heap, int i, int j) +{ + int res = GB.CompVariant(&heap->h[i], &heap->h[j]); + + return heap->mode == GB_COMP_ASCENT ? res : -res; +} + +static inline int compare1(CHEAP *heap, GB_VARIANT_VALUE *x, int j) +{ + int res = GB.CompVariant(x, &heap->h[j]); + + return heap->mode == GB_COMP_ASCENT ? res : -res; +} + +static inline int compare3(CHEAP *heap, GB_VARIANT_VALUE *x, + GB_VARIANT_VALUE *y) +{ + int res = GB.CompVariant(x, y); + + return heap->mode == GB_COMP_ASCENT ? res : -res; +} + +static inline void copy(CHEAP *heap, int src, int dst) +{ + memmove(&heap->h[dst], &heap->h[src], sizeof(GB_VARIANT_VALUE)); +} + +static inline void copy1(CHEAP *heap, int src, GB_VARIANT_VALUE *dst) +{ + memmove(dst, &heap->h[src], sizeof(GB_VARIANT_VALUE)); +} + +static inline void copy2(CHEAP *heap, GB_VARIANT_VALUE *src, int dst) +{ + memmove(&heap->h[dst], src, sizeof(GB_VARIANT_VALUE)); +} + +#define left(k) (2 * (k) + 1) +#define right(k) (left(k) + 1) +#define parent(k) (((k) - 1) / 2) + +static int upheap(CHEAP *heap, int k) +{ + GB_VARIANT_VALUE x; + int r = 0; + + copy1(heap, k, &x); + while (k && compare1(heap, &x, parent(k)) < 0) { + copy(heap, parent(k), k); + k = parent(k); + r++; + } + copy2(heap, &x, k); + return r; +} + +static int downheap(CHEAP *heap, int k) +{ + int count = GB.Count(heap->h), r = 0; + GB_VARIANT_VALUE x; + + copy1(heap, k, &x); + while (k <= parent(count - 1)) { + int j, l = j = left(k), r = right(k); + + if (r < count && compare(heap, l, r) > 0) + j = r; + if (compare1(heap, &x, j) <= 0) + break; + copy(heap, j, k); + k = j; + r++; + } + copy2(heap, &x, k); + return r; +} + +static void new_heap(CHEAP *heap, int count) +{ + GB.NewArray(&heap->h, sizeof(*heap->h), count); +} + +static void rebuild(CHEAP *heap) +{ + int i; + + for (i = parent(GB.Count(heap->h) - 1); i >= 0; i--) + downheap(heap, i); +} + +static void from_array(CHEAP *heap, GB_ARRAY array) +{ + int count = GB.Array.Count(array), i; + + new_heap(heap, count); + for (i = 0; i < count; i++) { + memcpy(&heap->h[i], GB.Array.Get(array, i), + sizeof(*heap->h)); + if (TYPE_is_object(heap->h[i].type)) + GB.Ref(heap->h[i].value._object); + } + rebuild(heap); +} + +#define THIS ((CHEAP *) _object) + +/**G + * Creates a new Heap. + * + * If 'Mode' is gb.Ascent, it's a MinHeap, i.e. the smallest element is at + * the beginning. If 'mode' is gb.Descent, it's a MaxHeap. + * + * If the 'Array' argument is given, a copy of that array is transformed + * into a Heap (by using a bottom-up algorithm which is O(n)). + */ +BEGIN_METHOD(Heap_new, GB_INTEGER mode; GB_OBJECT array) + + THIS->mode = VARG(mode); + if (THIS->mode != GB_COMP_ASCENT && THIS->mode != GB_COMP_DESCENT) { + GB.Error("Invalid mode"); + return; + } + + if (MISSING(array)) { + new_heap(THIS, 0); + } else { + GB_ARRAY array = (GB_ARRAY) VARG(array); + + if (GB.CheckObject(array)) + return; + from_array(THIS, array); + } + +END_METHOD + +/**G + * Free up storage of the Heap + */ +BEGIN_METHOD_VOID(Heap_free) + + int count = GB.Count(THIS->h), i; + + for (i = 0; i < count; i++) + GB.StoreVariant(NULL, &THIS->h[i]); + GB.FreeArray(&THIS->h); + +END_METHOD + +/**G + * Insert an element into the Heap. + */ +BEGIN_METHOD(Heap_Insert, GB_VARIANT data) + + GB.StoreVariant(ARG(data), GB.Add(&THIS->h)); + upheap(THIS, GB.Count(THIS->h) - 1); + +END_METHOD + +static void delete(CHEAP *heap, int i, GB_VARIANT_VALUE *x) +{ + int count = GB.Count(heap->h); + + copy1(heap, i, x); + copy(heap, count - 1, i); + GB.Remove(&heap->h, count - 1, 1); + downheap(heap, i); +} + +/**G + * Remove the first element. + */ +BEGIN_METHOD_VOID(Heap_Remove) + + int count = GB.Count(THIS->h); + GB_VARIANT_VALUE x; + + if (!count) { + GB.Error(GB_ERR_BOUND); + return; + } + + delete(THIS, 0, &x); + GB.ReturnVariant(&x); + GB.ReturnBorrow(); + GB.StoreVariant(NULL, &x); + GB.ReturnRelease(); + +END_METHOD + +/* + * Comparison by identity works as follows: + * - if both are objects and have _identity() methods which return + * variants, the return value is the comparison of those variants, + * - if they are objects without _identity(), they are compared based + * on their addresses in memory, + * - else they are not objects and are normally compared. + */ +static int compare_identity(CHEAP *heap, GB_VARIANT_VALUE *a, + GB_VARIANT_VALUE *b) +{ + static int has_identity; + static GB_FUNCTION aid; + static GB_VARIANT_VALUE *last = NULL; + static GB_VALUE x; + + GB_FUNCTION bid; + GB_VALUE *y; + + if (!TYPE_is_object(a->type) || !TYPE_is_object(b->type)) + return compare3(heap, a, b); + + if (last != a) { + if (!GB.GetFunction(&aid, a->value._object, "_identity", + NULL, "v")) { + has_identity = 1; + memcpy(&x, GB.Call(&aid, 0, 0), sizeof(x)); + } else { + has_identity = 0; + } + GB.Error(NULL); /* Clear any GetFunction error */ + last = a; + } + + /* No _identity()? */ + if (!has_identity || GB.GetFunction(&bid, b->value._object, + "_identity", NULL, "v")) { + GB.Error(NULL); + return a->value._object != b->value._object; + } + + y = GB.Call(&bid, 0, 0); + return compare3(heap, &x._variant.value, &y->_variant.value); +} + +/**G + * Find all occurences of `Old' and replace them by `New'. This is an O(n) + * operation. Additionally the heap has to be rebuilt as soon as there is + * more than one replacement made. + * + * If `New' is Null, then the entry will be deleted. + * + * The search for objects is done by identity and *not* by using the + * _compare() method. "By identity" means that if two objects are to be + * compared and both have a special _identity() method returning a Variant, + * the return values of these methods are compared. + * + * If one of the objects does not implement the _identity() method, the + * default is comparison by object addresses in memory. This strategy lets + * you save primitive data types (Integer, Boolean, String), etc. in the + * heap. But you can also *distinct* objects in equivalence classes. The + * _compare() method defines your equivalence relation, the identity + * comparison enables to discern different objects. + * + * If we are a heap of objects whose _compare() methods compare some sort of + * priority, and `Old' is the same object as `New', this can be used to + * propagate a priority change made to the object and will correct its + * position in the heap. + * + * This has an application in Dijkstra's algorithm (or any priority first + * search like Prim or A*) where you would update a node's priority. + */ +BEGIN_METHOD(Heap_Update, GB_VARIANT old; GB_VARIANT new) + + int count = GB.Count(THIS->h), i, found = 0, idx = -1; + GB_VARIANT_VALUE *old, *new; + + old = &VARG(old); new = &VARG(new); + for (i = 0; i < count; i++) { + if (compare_identity(THIS, old, &THIS->h[i])) + continue; + /* + * Make Null delete the entry. We don't count that as a + * finding because we can fix the heap up on the fly. + */ + if (new->type == GB_T_NULL) { + GB_VARIANT_VALUE buf; + + delete(THIS, i, &buf); + GB.StoreVariant(NULL, &buf); + count = GB.Count(THIS->h); + continue; + } + /* XXX: If `old' and `new' are the same and if they're + * objects, memory errors will occur if you store one over + * the other... Maybe StoreVariant() wasn't made to replace + * an object by itself (refcount goes to zero before it is + * incremented again, maybe?) */ + if (!TYPE_is_object(THIS->h[i].type) + || THIS->h[i].value._object != new->value._object) + GB.StoreVariant(ARG(new), &THIS->h[i]); + found++; + idx = i; + } + /* + * Most applications will have pairwise distinct elements in the + * heap, so that at most one element is changed. In this case, we + * can get away more quickly (O(log n) vs. O(n)) with an upheap() + * or downheap() call. + */ + if (found == 1) { + if (!upheap(THIS, idx)) + downheap(THIS, idx); + } else if (found) { + rebuild(THIS); + } + +END_METHOD + +/**G + * Return or set the first element of the Heap. + */ +BEGIN_PROPERTY(Heap_First) + + if (!GB.Count(THIS->h)) { + GB.Error(GB_ERR_BOUND); + return; + } + + if (READ_PROPERTY) { + GB.ReturnVariant(&THIS->h[0]); + return; + } + GB.StoreVariant(PROP(GB_VARIANT), &THIS->h[0]); + downheap(THIS, 0); + +END_PROPERTY + +/**G + * Return the number of elements in the Heap. + */ +BEGIN_PROPERTY(Heap_Count) + + GB.ReturnInteger(GB.Count(THIS->h)); + +END_PROPERTY + +/**G + * Return whether the Heap is empty. + */ +BEGIN_PROPERTY(Heap_IsEmpty) + + GB.ReturnBoolean(GB.Count(THIS->h) == 0); + +END_PROPERTY + +GB_DESC CHeap[] = { + /**G Heap + * This class implements a dynamic heap. It can be a MinHeap or a + * MaxHeap, depending on what mode you specify on construction. + * + * Being `dynamic' means that this class allows you to update the + * data or the position of elements which are already in the heap. + */ + GB_DECLARE("Heap", sizeof(CHEAP)), + + GB_METHOD("_new", NULL, Heap_new, "(Mode)i[(Array)Variant[];]"), + GB_METHOD("_free", NULL, Heap_free, NULL), + + GB_METHOD("Insert", NULL, Heap_Insert, "(Data)v"), + GB_METHOD("Remove", "v", Heap_Remove, NULL), + GB_METHOD("Update", NULL, Heap_Update, "(Old)v(New)v"), + + GB_PROPERTY("First", "v", Heap_First), + + GB_PROPERTY_READ("Count", "i", Heap_Count), + GB_PROPERTY_READ("IsEmpty", "b", Heap_IsEmpty), + + GB_END_DECLARE +}; diff --git a/main/lib/data/c_heap.h b/main/lib/data/c_heap.h new file mode 100644 index 00000000..6d1ae526 --- /dev/null +++ b/main/lib/data/c_heap.h @@ -0,0 +1,33 @@ +/* + * c_heap.h + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_HEAP_H +#define __C_HEAP_H + +#include "gambas.h" + +extern GB_INTERFACE GB; + +#ifndef __C_HEAP_C +extern GB_DESC CHeap[]; +#endif + +#endif /* __C_HEAP_H */ diff --git a/main/lib/data/c_list.c b/main/lib/data/c_list.c new file mode 100644 index 00000000..124dd34c --- /dev/null +++ b/main/lib/data/c_list.c @@ -0,0 +1,1363 @@ +/* + * c_list.c - Circular doubly-linked lists + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_LIST_C + +#include + +#include "gambas.h" +#include "gb_common.h" +#include "list.h" +#include "c_list.h" + +#define CHUNK_SIZE 16 + +/* + * List implementation properties: + * + Increase cache locality by saving CHUNK_SIZE values inside a single + * chunk. + * + A special algorithm to re-arrange the values in a chunk guarantees some + * properties we can use to speed up the critical paths, such as + * traversals. + * + Cached references ('anchors') in the list which can be used calculate + * the best starting point for a traversal to a given index. + * - Fragmentation in the middle chunks in a list because of the (O) + * postulate of the rearrangement algorithm. + */ + +typedef struct { + LIST list; + GB_VARIANT_VALUE var[CHUNK_SIZE]; + int first; /* First valid element in var */ + int last; /* Last valid element in var */ +} CHUNK; +#define get_chunk(node) LIST_data(node, CHUNK, list) + +typedef struct { + CHUNK *ck; + int idx; /* Absolute index into ->ck->var */ + int lgi; /* Absolute list global index [-Count; Count - 1] */ +} VAL; + +typedef struct { + GB_BASE ob; + LIST list; /* Beginning of linked CHUNKs */ + VAL current; /* Current element */ + size_t count; /* Do not iterate over all elements to get this */ + int autonorm; /* Automatically normalise indices */ +} CLIST; + +static void CHUNK_init(CHUNK *ck) +{ + int i; + + LIST_init(&ck->list); + for (i = 0; i < CHUNK_SIZE; i++) + ck->var[i].type = GB_T_NULL; + ck->first = -1; + ck->last = -1; +} + +static CHUNK *CHUNK_new(void) +{ + CHUNK *new; + + GB.Alloc((void **) &new, sizeof(*new)); + CHUNK_init(new); + return new; +} + +static inline int CHUNK_count(CHUNK *ck) +{ + return ck->last - ck->first + 1; +} + +static inline int CHUNK_is_first(CLIST *list, CHUNK *ck) +{ + return list->list.next == &ck->list; +} + +static inline int VAL_is_first(CLIST *list, VAL *val) +{ + CHUNK *ck = val->ck; + + return CHUNK_is_first(list, ck) && val->idx == ck->first; +} + +static inline int CHUNK_is_last(CLIST *list, CHUNK *ck) +{ + return list->list.prev == &ck->list; +} + +static inline int VAL_is_last(CLIST *list, VAL *val) +{ + CHUNK *ck = val->ck; + + return CHUNK_is_last(list, ck) && val->idx == ck->last; +} + +static void CHUNK_free_all(CHUNK *ck) +{ + int i; + + if (ck->first < 0 || ck->last < 0) + return; + for (i = ck->first; i <= ck->last; i++) + if (ck->var[i].type != GB_T_NULL) + GB.StoreVariant(NULL, &ck->var[i]); + ck->first = ck->last = -1; +} + +static void CHUNK_destroy(CHUNK *ck) +{ + /* The chunk *must* be unlinked */ + CHUNK_free_all(ck); + GB.Free((void **) &ck); +} + +#define THIS ((CLIST *) _object) + +BEGIN_METHOD_VOID(List_new) + + LIST_init(&THIS->list); + THIS->current.ck = NULL; + THIS->count = 0; + THIS->autonorm = 0; + +END_METHOD + +BEGIN_METHOD_VOID(List_free) + + LIST *node, *next; + CHUNK *ck; + + list_for_each_safe(node, &THIS->list, next) { + LIST_unlink(node); + ck = get_chunk(node); + CHUNK_destroy(ck); + } + THIS->current.ck = NULL; + THIS->count = 0; + +END_METHOD + +static inline GB_VARIANT_VALUE *VAL_value(VAL *val) +{ +#ifdef DEBUG_ME + if (val->idx < val->ck->first || val->idx > val->ck->last) + printf(": err: %d : %d,%d\n", val->idx, val->ck->first, + val->ck->last); +#endif + assert(val->idx >= val->ck->first && val->idx <= val->ck->last); + return &val->ck->var[val->idx]; +} + +static inline int VAL_is_equal(VAL *v1, VAL *v2) +{ + return v1->ck == v2->ck && v1->idx == v2->idx; +} + +/* + * A VAL carries an absolute index of the element it represents in the list. + * This must be updated by each of the functions that modified a VAL because + * they are trusted by traversing algorithms (which have access to Current + * and the enumerators) when looking for the shortest around through the + * list. + * + * The index can be positive or negative. We need some magic to update it + * correctly everytime and with every implementation of C (that's about the + * sign of the result from a % operation). + * + * (Be sure to compile this code with a high optimisation level.) + */ + +#ifndef sgn +# define sgn(x) \ +({ \ + int __x = (x); \ + \ + __x < 0 ? -1 : (__x ? 1 : 0); \ +}) +#endif + +/* One's complement abs() */ +#define onesabs(x) \ +({ \ + int __x = (x); \ + \ + __x < 0 ? ~__x : __x; \ +}) + +/* Get corresponding non-negative index over the list */ +#define abslgi(list, i) \ +({ \ + CLIST *__l = (list); \ + int __i = (i); \ + \ + __i < 0 ? __l->count + __i : __i; \ +}) + +#define update_lgi(list, val, i) \ +do { \ + CLIST *__l = (list); \ + VAL *__v = (val); \ + int __i = (i); \ + \ + if (!__l->count) { \ + __v->ck = NULL; \ + } else { \ + __v->lgi = (onesabs(__i) % __l->count); \ + if (__i < 0) \ + __v->lgi = ~__v->lgi; \ + } \ +} while (0) + +static void CLIST_first(CLIST *list, VAL *buf) +{ + if (!list->count) { + buf->ck = NULL; + /* + * The VAL is invalid but we want to clear this anyways. + */ + update_lgi(list, buf, 0); + return; + } + + buf->ck = get_chunk(list->list.next); + buf->idx = buf->ck->first; + update_lgi(list, buf, 0); +} + +static void CLIST_last(CLIST *list, VAL *buf) +{ + if (!list->count) { + buf->ck = NULL; + update_lgi(list, buf, 0); + return; + } + buf->ck = get_chunk(list->list.prev); + buf->idx = buf->ck->last; + update_lgi(list, buf, -1); +} + +/* + * Modify 'val' so that it points to the next/prev valid value. 'first' is + * used to detect the end of an enumeration, i.e. where no other elements + * should be looked for. In this case, NULL is written to val->ck. + * + * Since the LGI is said to be in bounds of [-Count; Count - 1], these + * functions must watch when the list head is traversed and reset the index + * accordingly. + */ + +static void CHUNK_next(CLIST *list, VAL *val) +{ + LIST *node; + + update_lgi(list, val, val->lgi + 1); + + /* Try to just update the index. */ + if (val->idx < val->ck->last) { + val->idx++; + return; + } + + /* Go to next chunk */ + if ((node = val->ck->list.next) == &list->list) + node = node->next; + val->ck = get_chunk(node); + val->idx = val->ck->first; +} + +static void CHUNK_next_enum(CLIST *list, VAL *first, VAL *val) +{ + CHUNK *ck = val->ck; + LIST *node; + + assert(first != val); + + update_lgi(list, val, val->lgi + 1); + + if (val->idx < ck->last) { + val->idx++; + if (VAL_is_equal(first, val)) + goto no_next; + return; + } + if ((node = ck->list.next) == &list->list) + node = node->next; + ck = get_chunk(node); + val->ck = ck; + val->idx = ck->first; + if (VAL_is_equal(first, val)) + goto no_next; + return; + +no_next: + val->ck = NULL; + update_lgi(list, val, 0); +} + +static void CHUNK_prev(CLIST *list, VAL *val) +{ + LIST *node; + + update_lgi(list, val, val->lgi - 1); + + if (val->idx > val->ck->first) { + val->idx--; + return; + } + if ((node = val->ck->list.prev) == &list->list) + node = node->prev; + val->ck = get_chunk(node); + val->idx = val->ck->last; +} + +static void CHUNK_prev_enum(CLIST *list, VAL *first, VAL *val) +{ + CHUNK *ck = val->ck; + LIST *node; + + assert(first != val); + + update_lgi(list, val, val->lgi - 1); + + if (val->idx > ck->first) { + val->idx--; + if (VAL_is_equal(first, val)) + goto no_prev; + return; + } + if ((node = ck->list.prev) == &list->list) + node = node->prev; + ck = get_chunk(node); + val->ck = ck; + val->idx = ck->last; + if (VAL_is_equal(first, val)) + goto no_prev; + return; + +no_prev: + val->ck = NULL; + update_lgi(list, val, 0); +} + +/* + * Random access function stuff (for CLIST_get()). + * Negative 'idx' makes go backwards. The 'val' is filled. Out of bounds is + * signalled by val->ck == NULL. + * + * We use all references (i.e. Current and the enumerators) to get a good + * anchor, i.e. a VAL and a direction from where we can get to the desired + * index the shortest way. + */ + +/* XXX: sizeof(VAL) may never exceed 3*sizeof(intptr_t)! */ +struct enum_state { + CHUNK *first; + VAL next; +}; + +#define begin_all_references(list) \ +do { \ + CLIST *__list = list; \ + VAL *__vp; \ + void *__ebuf; \ + struct enum_state *__es = NULL; \ + \ + __ebuf = GB.BeginEnum(__list); \ + if (!__list->current.ck) { \ + if (GB.NextEnum()) { \ + __vp = NULL; \ + } else { \ + __es = (struct enum_state *) GB.GetEnum(); \ + __vp = &__es->next; \ + } \ + } else { \ + __vp = &__list->current; \ + } \ + for (; __vp; __vp = GB.NextEnum() ? NULL : \ + (__es = (struct enum_state *) GB.GetEnum(), &__es->next)) { + +#define end_all_references \ + } \ + GB.EndEnum(__ebuf); \ +} while (0) + +struct anchor { + VAL start; + int direction; +}; + +/* 'idx' is required to be non-negative and in bounds at this point. */ +static inline void get_best_anchor(CLIST *list, int idx, struct anchor *buf) +{ + int d, tmp; + + /* Distance from head forwards/backwards/of all references */ + d = idx; + tmp = list->count - 1 - idx; + if (tmp < d) { + d = tmp; + CLIST_last(list, &buf->start); + } else { + CLIST_first(list, &buf->start); + } + begin_all_references(list) { + tmp = abs(abslgi(list, __vp->lgi) - idx); + if (tmp < d) { + d = tmp; + memcpy(&buf->start, __vp, sizeof(buf->start)); + } + } end_all_references; + + buf->direction = sgn(idx - abslgi(list, buf->start.lgi)); +} + +static inline void get_body_forward(CLIST *list, LIST *node, int i, + VAL *val) +{ + CHUNK *ck; + int count; + + while (1) { + ck = get_chunk(node); + count = CHUNK_count(ck); + + if (i < count) { + val->ck = ck; + val->idx = ck->first + i; + return; + } + i -= count; + do + node = node->next; + while (node == &list->list); + } +} + +static inline void get_body_backward(CLIST *list, LIST *node, int i, + VAL *val) +{ + CHUNK *ck; + int count; + + while (1) { + do + node = node->prev; + while (node == &list->list); + ck = get_chunk(node); + count = -CHUNK_count(ck); + + if (i >= count) { + val->ck = ck; + val->idx = ck->last + i + 1; + return; + } + i -= count; + } +} + +static void CLIST_get(CLIST *list, int idx, VAL *val) +{ + LIST *node; + int i, dir; + struct anchor anchor; + + /* Make a non-negative index */ + i = abslgi(list, idx); + /* We do _not_ allow indices to wrap around the end, like we could: + * i %= list->count. Don't use a loop just to detect that case. */ + if (i >= list->count) { + /* Not enough elements. */ + val->ck = NULL; + return; + } + + get_best_anchor(list, i, &anchor); + dir = anchor.direction; + + update_lgi(list, val, idx); + /* Got that index in a reference already? Just copy. */ + if (!dir) { + val->ck = anchor.start.ck; + val->idx = anchor.start.idx; + return; + } + + node = &anchor.start.ck->list; + /* + * Don't start exactly at the given anchor point (possibly in the + * middle of a chunk) but instead at the beginning of the chunk. + * Because of cache spatiality, this is not a great loss and the + * code is simplified much. + */ + i -= abslgi(list, anchor.start.lgi); + i += anchor.start.idx - anchor.start.ck->first; + + /* + * Prevent a if (i < 0) branch prediction catastrophe if we merged + * both algorithms into one loop. + */ + if (i < 0) + get_body_backward(list, node, i, val); + else + get_body_forward(list, node, i, val); +} + +BEGIN_METHOD_VOID(List_next) + + struct enum_state *state = GB.GetEnum(); + GB_VARIANT_VALUE *val; + /* XXX: Would like to cache that in the enum_state but no space left + * there... */ + VAL start; + + if (!state->first) { /* Beginning */ + CLIST_first(THIS, &state->next); + state->first = state->next.ck; + } + /* No elements left? */ + if (!state->next.ck) { + GB.StopEnum(); + return; + } + + val = VAL_value(&state->next); + start.ck = state->first; + start.idx = start.ck->first; + CHUNK_next_enum(THIS, &start, &state->next); + GB.ReturnVariant(val); + +END_METHOD + +/* + * The same as List_next but backwards. + */ + +BEGIN_METHOD_VOID(ListBackwards_next) + + struct enum_state *state = GB.GetEnum(); + GB_VARIANT_VALUE *val; + VAL start; + + if (!state->first) { /* Beginning */ + CLIST_last(THIS, &state->next); + state->first = state->next.ck; + } + /* No elements left? */ + if (!state->next.ck) { + state->first = NULL; + GB.StopEnum(); + return; + } + + val = VAL_value(&state->next); + start.ck = state->first; + start.idx = start.ck->last; + CHUNK_prev_enum(THIS, &start, &state->next); + GB.ReturnVariant(val); + +END_METHOD + +static inline int normalise_index(CLIST *list, int index) +{ + int i; + + i = onesabs(index) % list->count; + if (index < 0) + i = ~i; + return i; +} + +BEGIN_METHOD(List_get, GB_INTEGER index) + + int index = VARG(index); + VAL val; + + if (THIS->autonorm) + index = normalise_index(THIS, index); + CLIST_get(THIS, index, &val); + if (!val.ck) { + GB.Error(GB_ERR_BOUND); + return; + } + GB.ReturnVariant(VAL_value(&val)); + +END_METHOD + +BEGIN_METHOD(List_put, GB_VARIANT var; GB_INTEGER index) + + int index = VARG(index); + VAL val; + + if (THIS->autonorm) + index = normalise_index(THIS, index); + CLIST_get(THIS, index, &val); + if (!val.ck) { + GB.Error(GB_ERR_BOUND); + return; + } + GB.StoreVariant(ARG(var), VAL_value(&val)); + +END_METHOD + +/* + * The main problem when modifying the list structure is that there are + * references to VALs in Current and all the enumerators. + * + * The following postulates shall be met by the algorithms: + * + * (V) Value. If an element other than that a VAL refers to is removed or an + * element is added, the reference shall be modified according to the + * operation, i.e. it shall stay pointing to the particular value it has + * pointed to before. References are value-bound. + * + * (B) Beginning. If the element a reference points to is removed, the + * reference shall remain relative to the beginning of the list (as long + * as it doesn't get empty), i.e. it moves on to the next value. This + * will guarantee that the following code works as expected: + * + * Dim vEnum As Variant + * + * For Each vEnum In hList + * hList.Take(hList.FindFirst(vEnum)) + * Next + * + * If the list gets empty, the references are invalidated. + * + * The rearrangement algorithm shall assure the following: + * + * (C) Coherency. All values in a chunk must be contiguous. + * + * (A) Alignment. The first chunk has all elements aligned to its end; the + * last chunk has all elements aligned to its beginning. For the special + * case of only one chunk in the List (the 'sole chunk'), it is not + * specially aligned but its initial element is at CHUNK_SIZE/2-1. + * Should a chunk be allocated and linked into the middle of the list, + * its initial element shall also be at CHUNK_SIZE/2-1. + * + * (L) Least Copy. Rearrangement for any non-aligned chunk shall strive for + * the least copy operations. + * + * (O) Order. Values get never reordered with respect to the list. It is + * possible, however, to move elements to other chunks as long as the + * order persists. + * + * Note that if (O) would not apply, on the one hand, we could make more + * intelligent algorithms, but on the other hand, the first posulates must + * be improved so it remains in the first place. + */ + +static void CLIST_append(CLIST *list, GB_VARIANT *var) +{ + CHUNK *ck; + + ck = get_chunk(list->list.next); + /* (A) */ + if (UNLIKELY(!list->count)) { + ck = CHUNK_new(); + ck->first = ck->last = CHUNK_SIZE / 2 - 1; + LIST_append(&list->list, &ck->list); + } else if (UNLIKELY(ck->first == 0)) { + ck = CHUNK_new(); + ck->first = ck->last = CHUNK_SIZE - 1; + LIST_append(&list->list, &ck->list); + } else { + ck->first--; + } + /* (C), (O) */ + GB.StoreVariant(var, &ck->var[ck->first]); + list->count++; + + /* (V) */ + begin_all_references(list) { + if (__vp->lgi >= 0) + __vp->lgi++; + if (__vp->ck == ck) + __vp->idx++; + } end_all_references; +} + +static void CLIST_prepend(CLIST *list, GB_VARIANT *var) +{ + CHUNK *ck; + + ck = get_chunk(list->list.prev); + /* (A) */ + if (UNLIKELY(!list->count)) { + ck = CHUNK_new(); + ck->first = ck->last = CHUNK_SIZE / 2 - 1; + LIST_prepend(&list->list, &ck->list); + } else if (UNLIKELY(ck->last == CHUNK_SIZE - 1)) { + ck = CHUNK_new(); + ck->first = ck->last = 0; + LIST_prepend(&list->list, &ck->list); + } else { + ck->last++; + } + /* (C), (O) */ + GB.StoreVariant(var, &ck->var[ck->last]); + list->count++; + + /* (V) */ + begin_all_references(list) { + if (__vp->lgi < 0) + __vp->lgi--; + } end_all_references; +} + +/* + * With the VAL_append()/prepend() functions it is a little more difficult. + * We have to distinguish three cases (processed in this order): + * 1) There is space for the operation in this chunk: so shift elements and + * insert the new value; + * 1a) If the element is to be appended/prepended after/before the + * last/first element, do not shift; + * 2) There is space _immediately_ free in a neighbour chunk for the + * operation (for append it's the next chunk, for prepend the previous + * one), i.e. we don't need to shift the neighbour chunk, then shift + * values into this chunk and insert the new value. This may never cross + * the list head!; + * 2a) See 1a); + * 3) If none of the previous things worked, allocate a new chunk and shift + * in there; + * 3a) See 1a); + * + * Point 2) didn't shift values in the neighbour because this could go + * infinitely through the list so we just allocate a new chunk to do it fast + * and easily. + */ + +static void VAL_append(CLIST *list, VAL *val, GB_VARIANT *var) +{ + CHUNK *ck, *next = NULL; + int s, n, shifted_to_next = 0; + GB_VARIANT_VALUE *buf; + VAL back; + + ck = val->ck; + /* Currently not (L). We only shift towards the end. */ + if (ck->last < CHUNK_SIZE - 1) { /* 1) */ + ck->last++; + if (val->idx == ck->last - 1) { /* 1a) */ + buf = &ck->var[ck->last]; + } else { + shift: + s = val->idx + 1; + n = ck->last - s; + memmove(&ck->var[s + 1], &ck->var[s], + n * sizeof(ck->var[0])); + buf = &ck->var[s]; + } + } else { + LIST *node = ck->list.next; + + /* 2) */ + if (node == &list->list) + goto add_new_chunk; + + next = get_chunk(node); + if (next->first) { + next->first--; + if (val->idx == ck->last) { /* 2a) */ + buf = &next->var[next->first]; + goto have_buf; + } + shift_to_next: + shifted_to_next = 1; + memcpy(&next->var[next->first], &ck->var[ck->last], + sizeof(ck->var[0])); + goto shift; + } else { /* 3) */ + add_new_chunk: + next = CHUNK_new(); + next->first = next->last = CHUNK_SIZE / 2 - 1; + LIST_append(&ck->list, &next->list); + if (val->idx == ck->last) { /* 3a) */ + buf = &next->var[next->first]; + goto have_buf; + } + goto shift_to_next; + } + } + +have_buf: + bzero(buf, sizeof(*buf)); + buf->type = GB_T_NULL; + GB.StoreVariant(var, buf); + list->count++; + + int lgi, vlgi; + + /* + * Nasty: When appending to the last element iff it has a negative + * LGI, i.e. val->lgi == -1, then we must not produce a 'lgi' of + * value 0. For a positive LGI of the last element, we just + * incremented list->count so that's no problem. + */ + if (val->lgi == -1) + lgi = abslgi(list, -1); + else + lgi = normalise_index(list, abslgi(list, val->lgi + 1)); + memcpy(&back, val, sizeof(back)); + begin_all_references(list) { + vlgi = abslgi(list, __vp->lgi); + + if (vlgi <= lgi && __vp->lgi < 0) + __vp->lgi--; + else if (vlgi > lgi && __vp->lgi >= 0) + __vp->lgi++; + __vp->lgi = normalise_index(list, __vp->lgi); + + if (__vp->ck == back.ck) { + if (shifted_to_next && __vp->idx == back.ck->last) { + __vp->ck = next; + __vp->idx = next->first; + } else if (__vp->idx > back.idx) { + __vp->idx++; + } + } + } end_all_references; +} + +static void VAL_prepend(CLIST *list, VAL *val, GB_VARIANT *var) +{ + CHUNK *ck, *prev = NULL; + int s, n, shifted_to_prev = 0; + GB_VARIANT_VALUE *buf; + VAL back; + + ck = val->ck; + /* Not (L) */ + if (ck->first) { /* 1) */ + ck->first--; + if (val->idx == ck->first + 1) { /* 1a) */ + buf = &ck->var[ck->first]; + } else { + shift: + s = ck->first + 1; + n = val->idx - s; + memmove(&ck->var[s - 1], &ck->var[s], + n * sizeof(ck->var[0])); + buf = &ck->var[s + n - 1]; + } + } else { + LIST *node = ck->list.prev; + + /* 2) */ + if (node == &list->list) + goto add_new_chunk; + + prev = get_chunk(node); + if (prev->last < CHUNK_SIZE - 1) { + prev->last++; + if (val->idx == ck->first) { /* 2a) */ + buf = &prev->var[prev->last]; + goto have_buf; + } + shift_to_prev: + shifted_to_prev = 1; + memcpy(&prev->var[prev->last], &ck->var[ck->first], + sizeof(ck->var[0])); + goto shift; + } else { /* 3) */ + add_new_chunk: + prev = CHUNK_new(); + prev->first = prev->last = CHUNK_SIZE / 2 - 1; + LIST_prepend(&ck->list, &prev->list); + if (val->idx == ck->first) { /* 3a) */ + buf = &prev->var[prev->last]; + goto have_buf; + } + goto shift_to_prev; + } + } + +have_buf: + bzero(buf, sizeof(*buf)); + buf->type = GB_T_NULL; + GB.StoreVariant(var, buf); + list->count++; + + int lgi, vlgi; + + /* + * We have a similar case here as in VAL_append() but the other way + * around: if the element is the new first one, we must check if + * val->lgi is 0, it then may stay 0. If it was -list->count, + * everything is fine because of list->count++ above. + */ + if (!val->lgi) + lgi = 0; + else + lgi = normalise_index(list, abslgi(list, val->lgi - 1)); + memcpy(&back, val, sizeof(back)); + begin_all_references(list) { + vlgi = abslgi(list, __vp->lgi); + + if (vlgi <= lgi && __vp->lgi < 0) + __vp->lgi--; + else if (vlgi >= lgi && __vp->lgi >= 0) + __vp->lgi++; + __vp->lgi = normalise_index(list, __vp->lgi); + + if (__vp->ck == back.ck) { + if (shifted_to_prev && __vp->idx == back.ck->first) { + __vp->ck = prev; + __vp->idx = prev->last; + } else if (__vp->idx < back.idx) { + __vp->idx--; + } + } + } end_all_references; +} + +static void CLIST_take(CLIST *list, VAL *val, GB_VARIANT_VALUE *buf) +{ + GB_VARIANT_VALUE *v; + CHUNK *ck = val->ck; + VAL back; + int gets_empty, is_last; + int i, src, dst, phantom; + int n, m; + size_t size; + + i = val->idx; + v = &ck->var[i]; + /* Save that value */ + memcpy(buf, v, sizeof(*buf)); + /* No need to not (O) */ + + gets_empty = (CHUNK_count(ck) == 1); + if (gets_empty) { + phantom = i; + src = dst = 0; + goto no_move; + } + + n = i - ck->first; + m = ck->last - i; + is_last = CHUNK_is_last(list, ck); + /* (A) */ + if (CHUNK_is_first(list, ck)) { + if (is_last) /* Sole */ + goto normal; + goto first; /* Algorithms match */ + } else if (is_last) { + goto last; + } +normal: + /* (L) */ + if (n <= m) { + first: /* Move block before i upwards */ + src = ck->first; + dst = src + 1; + ck->first++; + phantom = src; + } else { + last: /* Move block after i downwards */ + src = i + 1; + dst = i; + n = m; + ck->last--; + phantom = i + m; + } + /* (C) */ + size = n * sizeof(ck->var[0]); + memmove(&ck->var[dst], &ck->var[src], size); + +no_move: + /* Don't forget to remove the phantom value (overriding the actual + * data with zeros to be sure from leakage) */ + bzero(&ck->var[phantom], sizeof(ck->var[0])); + ck->var[phantom].type = GB_T_NULL; + + list->count--; + + /* Don't accidentally erase information if 'val' is a reference + * itself because then once __vp == val and __vp will be changed. */ + memcpy(&back, val, sizeof(back)); + begin_all_references(list) { +#ifdef DEBUG_ME + printf(": in: %p -> %d (%d) %d %d\n", __vp, __vp->idx, + __vp->lgi, back.idx, src < dst); +#endif + + /* The LGI stuff is pretty much independent of the ->ck and + * ->idx code and conditions. We only care about negative + * LGIs basically because non-negative ones automagically + * stay correct according to (B). */ + int vlgi = abslgi(list, __vp->lgi); + int blgi = abslgi(list, back.lgi); + + if (vlgi >= blgi) { + /* If this reference is taken and it's the last + * element of the list, then set it to zero. Kind + * of a nasty condition to occur but this is the + * only difficulty here because of (B). */ + if (UNLIKELY(vlgi == list->count && vlgi == blgi)) + __vp->lgi = 0; + else if (__vp->lgi < 0) + __vp->lgi++; + } + + if (__vp->ck != back.ck) + continue; + + /* (B) */ + if (__vp->idx == back.idx) { + if (!list->count) { + __vp->ck = NULL; + continue; + } else if (gets_empty) { + goto next_chunk; + } else if (src < dst) { + /* According to ck->first++ above which + * happens only when src < dst. */ + __vp->idx++; + } + } + /* (V) */ + LIST *node; + + if (__vp->idx > __vp->ck->last) { + next_chunk: + if ((node = __vp->ck->list.next) == &list->list) + node = node->next; + __vp->ck = get_chunk(node); + __vp->idx = __vp->ck->first; + } + +#ifdef DEBUG_ME + printf(": out: %p -> %d (%d) (%p,%d,%d)\n", __vp, __vp->idx, + __vp->lgi, __vp->ck, __vp->ck ? + __vp->ck->first : 0, __vp->ck ? + __vp->ck->last : 0); +#endif + } end_all_references; + if (gets_empty) { + LIST_unlink(&ck->list); + CHUNK_destroy(ck); + } +} + +BEGIN_METHOD(List_Append, GB_VARIANT value) + + CLIST_append(THIS, ARG(value)); + +END_METHOD + +BEGIN_METHOD(List_Prepend, GB_VARIANT value) + + CLIST_prepend(THIS, ARG(value)); + +END_METHOD + +#define CHECK_CURRENT() (THIS->current.ck) +#define CHECK_RAISE_CURRENT() \ + if (!CHECK_CURRENT()) { \ + GB.Error("No current element"); \ + return; \ + } + +BEGIN_METHOD(List_Take, GB_INTEGER index) + + VAL val; + GB_VARIANT_VALUE buf; + int index; + + if (MISSING(index)) { + CHECK_RAISE_CURRENT(); + CLIST_take(THIS, &THIS->current, &buf); + } else { + index = VARG(index); + if (THIS->autonorm) + index = normalise_index(THIS, index); + CLIST_get(THIS, index, &val); + if (!val.ck) { + GB.Error(GB_ERR_BOUND); + return; + } + CLIST_take(THIS, &val, &buf); + } + GB.ReturnVariant(&buf); + GB.ReturnBorrow(); + GB.StoreVariant(NULL, &buf); + GB.ReturnRelease(); + +END_METHOD + +#define IMPLEMENT_Move(which, magic) \ +BEGIN_METHOD_VOID(List_Move ## which) \ + \ + if (!THIS->count) { \ + GB.Error("No elements"); \ + return; \ + } \ + magic; \ + \ +END_METHOD + +IMPLEMENT_Move(Next, if (!CHECK_CURRENT())CLIST_first(THIS, &THIS->current); + CHUNK_next(THIS, &THIS->current);) +IMPLEMENT_Move(Prev, if (!CHECK_CURRENT()) CLIST_last(THIS, &THIS->current); + CHUNK_prev(THIS, &THIS->current);) +IMPLEMENT_Move(First, CLIST_first(THIS, &THIS->current);) +IMPLEMENT_Move(Last, CLIST_last(THIS, &THIS->current);) + +BEGIN_METHOD(List_MoveTo, GB_INTEGER index) + + int index = VARG(index); + + if (THIS->autonorm) + index = normalise_index(THIS, index); + + CLIST_get(THIS, index, &THIS->current); + if (!THIS->current.ck) + GB.Error(GB_ERR_BOUND); + +END_METHOD + +/* + * Modify 'val' to point to the next/prev value equal to 'comp'. If nothing + * found - 'val' is allowed to cycle and point to itself again! - NULL is + * written to val->ck. + */ + +static void CLIST_find_forward(CLIST *list, VAL *val, GB_VARIANT *comp) +{ + CHUNK *last = NULL; + int cached_diff; + VAL start; + + memcpy(&start, val, sizeof(start)); + + cached_diff = 1; + do { + /* We actually enumerate but do the checking ourselves */ + CHUNK_next(list, val); + /* Note that comparing here allows 'val' to point to itself + * again. This is intentional for cyclic lists */ + if (!GB.CompVariant(VAL_value(val), &comp->value)) + return; + if (val->ck != last) + last = val->ck; + if (last == start.ck && val->idx == start.idx) + cached_diff = 0; + } while (cached_diff); + /* Invalidate */ + val->ck = NULL; /* This is most likely &list->current */ +} + +static void CLIST_find_backward(CLIST *list, VAL *val, GB_VARIANT *comp) +{ + CHUNK *last = NULL; + int cached_diff; + VAL start; + + memcpy(&start, val, sizeof(start)); + + cached_diff = 1; + do { + CHUNK_prev(list, val); + if (!GB.CompVariant(VAL_value(val), &comp->value)) + return; + if (val->ck != last) + last = val->ck; + if (last == start.ck && val->idx == start.idx) + cached_diff = 0; + } while (cached_diff); + val->ck = NULL; +} + +#define CHECK_RET_CURRENT() \ + if (!CHECK_CURRENT()) { \ + GB.ReturnNull(); \ + return; \ + } + +#define IMPLEMENT_Find(which, which2, magic) \ + \ +BEGIN_METHOD(List_Find ## which, GB_VARIANT value) \ + \ + if (!THIS->count) { \ + GB.Error("No elements"); \ + return; \ + } \ + magic; \ + CLIST_find_ ## which2 (THIS, &THIS->current, ARG(value)); \ + \ +END_METHOD + +IMPLEMENT_Find(Next, forward, + if (!CHECK_CURRENT()) CLIST_first(THIS, &THIS->current)) +IMPLEMENT_Find(Prev, backward, + if (!CHECK_CURRENT()) CLIST_last(THIS, &THIS->current)) +IMPLEMENT_Find(First, forward, CLIST_first(THIS, &THIS->current)) +IMPLEMENT_Find(Last, backward, CLIST_last(THIS, &THIS->current)) + +BEGIN_PROPERTY(List_AutoNormalize) + + if (READ_PROPERTY) { + GB.ReturnBoolean(THIS->autonorm); + return; + } + THIS->autonorm = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(ListItem_Value) + + GB_VARIANT_VALUE *val; + + CHECK_RET_CURRENT(); + val = VAL_value(&THIS->current); + if (READ_PROPERTY) { + GB.ReturnVariant(val); + return; + } + GB.StoreVariant(PROP(GB_VARIANT), val); + +END_PROPERTY + +BEGIN_PROPERTY(ListItem_Index) + + int index; + + if (READ_PROPERTY) { + GB.ReturnInteger(THIS->current.lgi); + return; + } + if (THIS->autonorm) + index = normalise_index(THIS, VPROP(GB_INTEGER)); + else + index = VPROP(GB_INTEGER); + CLIST_get(THIS, index, &THIS->current); + if (!THIS->current.ck) + GB.Error(GB_ERR_BOUND); + +END_PROPERTY + +BEGIN_PROPERTY(List_Count) + + GB.ReturnInteger(THIS->count); + +END_PROPERTY + +BEGIN_PROPERTY(List_Current) + + CHECK_RAISE_CURRENT(); + GB.ReturnSelf(THIS); + +END_PROPERTY + +GB_DESC CList[] = { + GB_DECLARE("List", sizeof(CLIST)), + + GB_METHOD("_new", NULL, List_new, NULL), + GB_METHOD("_free", NULL, List_free, NULL), + GB_METHOD("_next", "v", List_next, NULL), + GB_METHOD("_get", "v", List_get, "(Index)i"), + GB_METHOD("_put", NULL, List_put, "(Value)v(Index)i"), + + GB_METHOD("Clear", NULL, List_free, NULL), + + GB_METHOD("Append", NULL, List_Append, "(Value)v"), + GB_METHOD("Prepend", NULL, List_Prepend, "(Value)v"), + GB_METHOD("Take", "v", List_Take, "[(Index)i]"), + + GB_METHOD("MoveNext", NULL, List_MoveNext, NULL), + GB_METHOD("MovePrev", NULL, List_MovePrev, NULL), + GB_METHOD("MovePrevious", NULL, List_MovePrev, NULL), + GB_METHOD("MoveFirst", NULL, List_MoveFirst, NULL), + GB_METHOD("MoveLast", NULL, List_MoveLast, NULL), + GB_METHOD("MoveTo", NULL, List_MoveTo, "(Index)i"), + + GB_METHOD("FindNext", NULL, List_FindNext, "(Value)v"), + GB_METHOD("FindPrev", NULL, List_FindPrev, "(Value)v"), + GB_METHOD("FindPrevious", NULL, List_FindPrev, "(Value)v"), + GB_METHOD("FindFirst", NULL, List_FindFirst, "(Value)v"), + GB_METHOD("FindLast", NULL, List_FindLast, "(Value)v"), + + GB_PROPERTY("AutoNormalize", "b", List_AutoNormalize), + GB_PROPERTY("Current", ".List.Item", List_Current), + GB_PROPERTY("Value", "v", ListItem_Value), + GB_PROPERTY("Index", "i", ListItem_Index), + GB_PROPERTY_READ("Count", "i", List_Count), + GB_PROPERTY_SELF("Backwards", ".List.Backwards"), + + GB_END_DECLARE +}; + +GB_DESC CListBackwards[] = { + GB_DECLARE_VIRTUAL(".List.Backwards"), + + GB_METHOD("_next", "v", ListBackwards_next, NULL), + + GB_END_DECLARE +}; + +BEGIN_METHOD(ListItem_Append, GB_VARIANT value) + + VAL_append(THIS, &THIS->current, ARG(value)); + +END_METHOD + +BEGIN_METHOD(ListItem_Prepend, GB_VARIANT value) + + VAL_prepend(THIS, &THIS->current, ARG(value)); + +END_METHOD + +BEGIN_PROPERTY(ListItem_IsFirst) + + GB.ReturnBoolean(VAL_is_first(THIS, &THIS->current)); + +END_PROPERTY + +BEGIN_PROPERTY(ListItem_IsLast) + + GB.ReturnBoolean(VAL_is_last(THIS, &THIS->current)); + +END_PROPERTY + +BEGIN_PROPERTY(ListItem_IsValid) + + GB.ReturnBoolean(!!CHECK_CURRENT()); + +END_PROPERTY + +GB_DESC CListItem[] = { + GB_DECLARE_VIRTUAL(".List.Item"), + + GB_METHOD("Append", NULL, ListItem_Append, "(Value)v"), + GB_METHOD("Prepend", NULL, ListItem_Prepend, "(Value)v"), + + GB_PROPERTY_READ("IsFirst", "b", ListItem_IsFirst), + GB_PROPERTY_READ("IsLast", "b", ListItem_IsLast), + GB_PROPERTY_READ("IsValid", "b", ListItem_IsValid), + GB_PROPERTY_READ("Index", "i", ListItem_Index), + GB_PROPERTY("Value", "v", ListItem_Value), + + GB_END_DECLARE +}; diff --git a/main/lib/data/c_list.h b/main/lib/data/c_list.h new file mode 100644 index 00000000..7588695e --- /dev/null +++ b/main/lib/data/c_list.h @@ -0,0 +1,36 @@ +/* + * c_list.h + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_LIST_H +#define __C_LIST_H + +#include "gambas.h" +#include "list.h" + +extern GB_INTERFACE GB; + +#ifndef __C_LIST_C +extern GB_DESC CList[]; +extern GB_DESC CListBackwards[]; +extern GB_DESC CListItem[]; +#endif + +#endif /* !__C_LIST_H */ diff --git a/main/lib/data/c_trie.c b/main/lib/data/c_trie.c new file mode 100644 index 00000000..34d6e128 --- /dev/null +++ b/main/lib/data/c_trie.c @@ -0,0 +1,590 @@ +/* + * c_trie.c - (Patricia) Trie / Prefix tree + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_TRIE_C + +#include +#include +#include + +#include "gambas.h" +#include "c_trie.h" + +#include "trie.h" + +typedef struct { + GB_BASE ob; + struct trie *root; + char *key; + size_t count; + uint64_t time; +} CTRIE; + +#define ERR_OOM "Out of memory" + +#define THIS ((CTRIE *) _object) + +#define RESET_TIME() (THIS->time = 0) +#define UPDATE_TIME() (THIS->time++) + +/**G + * Create a new, empty Trie. + */ +BEGIN_METHOD_VOID(Trie_new) + + THIS->root = new_trie(); + THIS->key = NULL; + THIS->count = 0; + RESET_TIME(); + +END_METHOD + +static void value_dtor(void *val) +{ + GB.StoreVariant(NULL, (GB_VARIANT_VALUE *) val); + GB.Free(&val); +} + +BEGIN_METHOD_VOID(Trie_free) + + destroy_trie(THIS->root, value_dtor); + GB.FreeString(&THIS->key); + UPDATE_TIME(); + +END_METHOD + +/**G + * Return the value associated with a key. If the key was not found, return + * Null. + */ +BEGIN_METHOD(Trie_get, GB_STRING key) + + GB_VARIANT_VALUE *val; + + val = trie_value(THIS->root, STRING(key), LENGTH(key)); + if (!val) + GB.ReturnNull(); + else + GB.ReturnVariant(val); + +END_METHOD + +/**G + * Associate a value with a given key. If the value is Null, the key is + * removed. + */ +BEGIN_METHOD(Trie_put, GB_VARIANT value; GB_STRING key) + + GB_VARIANT_VALUE *val; + void *oldval; + + if (VARG(value).type == GB_T_NULL) { + trie_remove(THIS->root, STRING(key), LENGTH(key), + value_dtor); + UPDATE_TIME(); + return; + } + GB.Alloc((void **) &val, sizeof(*val)); + val->type = GB_T_NULL; + GB.StoreVariant(ARG(value), val); + oldval = trie_insert(THIS->root, STRING(key), LENGTH(key), val); + if (oldval) + value_dtor(oldval); + UPDATE_TIME(); + +END_METHOD + +/**G + * Remove the named element. This is equivalent to _put'ing Null into its + * key. + */ +BEGIN_METHOD(Trie_Remove, GB_STRING key) + + trie_remove(THIS->root, STRING(key), LENGTH(key), value_dtor); + UPDATE_TIME(); + +END_METHOD + +struct stack { + struct trie *node; + int idx, visited : 1; + struct stack *prev; +}; + +struct enum_state { + struct stack *top; + int start; +}; + +/**G + * Enumerates all values in the Trie in lexicographic key order. The Key + * property is set for each enumerated value. + * + * If you picture the Trie as a tree (and have all child nodes ordered + * lexicographically), the lexicographic traversal of the trie is the + * pre-order traversal (of nodes with a value). + */ +BEGIN_METHOD_VOID(Trie_next) + + struct enum_state *state = GB.GetEnum(); + struct stack *top; + struct trie *node = NULL; /* silence compiler with "goto visit" */ + + if (!state->start) { + state->start = 1; + GB.FreeString(&THIS->key); + THIS->key = GB.NewString("", 0); + GB.Alloc((void **) &state->top, sizeof(*state->top)); + state->top->node = THIS->root; + state->top->idx = 0; + state->top->visited = 0; + state->top->prev = NULL; + top = state->top; + goto visit; + } + + top = state->top; +next: + if (top->idx >= top->node->nchildren) { + struct stack *prev = top->prev; + size_t len = GB.StringLength(THIS->key) - top->node->len; + + THIS->key = GB.ExtendString(THIS->key, len); + GB.Free((void **) &top); + top = prev; + if (!top) { + GB.StopEnum(); + return; + } + top->idx++; + goto next; + } + + node = top->node->children[top->idx]; +visit: + if (!top->visited) { + /* AddString() will take the root node's len == 0 as a + * request to use strlen(). Make that a special case. */ + if (top->node->len) { + THIS->key = GB.AddString(THIS->key, + top->node->key, + top->node->len); + } + } else { + struct stack *old = top; + + if (old->node->nchildren) { + GB.Alloc((void **) &top, sizeof(*top)); + top->node = node; + top->idx = 0; + top->visited = 0; + top->prev = old; + goto visit; + } + } + + top->visited = 1; + state->top = top; + if (!top->node->value) + goto next; + GB.ReturnVariant(top->node->value); + +END_METHOD + +/**G + * Remove all elements from the Trie. + */ +BEGIN_METHOD_VOID(Trie_Clear) + + clear_trie(THIS->root, value_dtor); + UPDATE_TIME(); + +END_METHOD + +/**G + * Return whether the named key exists, i.e. if it has a value. + * + * This does not return if the given string is *part* of a path to another + * node, it will only give you exact matches. To test if a given prefix + * exists, use [../GetPrefix]. + */ +BEGIN_METHOD(Trie_Exist, GB_STRING key) + + struct trie *node; + + node = trie_find(THIS->root, STRING(key), LENGTH(key)); + GB.ReturnBoolean(!!node); + +END_METHOD + +typedef struct CPREFIX { + GB_BASE ob; + CTRIE *trie; + struct trie_prefix p; + char *key; + char *prefix; + uint64_t time; +} CPREFIX; + +/**G + * Return a TriePrefix object to search part of a trie. + * + * If the prefix is not found, Null is returned. + */ +BEGIN_METHOD(Trie_GetPrefix, GB_STRING prefix) + + static GB_CLASS TriePrefix; + struct trie_prefix p; + CPREFIX *obj; + + trie_reset_prefix(&p); + trie_constrain2(THIS->root, &p, STRING(prefix), LENGTH(prefix)); + if (!p.node) { + GB.ReturnNull(); + return; + } + + if (!TriePrefix) + TriePrefix = GB.FindClass("TriePrefix"); + obj = GB.New(TriePrefix, NULL, NULL); + obj->trie = THIS; + GB.Ref(THIS); + obj->p = p; + obj->key = NULL; + obj->prefix = GB.NewString(STRING(prefix), LENGTH(prefix)); + obj->time = THIS->time; + GB.ReturnObject(obj); + +END_METHOD + +/**G + * Return the completion of the given prefix, that is the longest + * unambiguous continuation of the prefix, like when you hit + * in the console, your shell might complete a command or file name + * for you. + * + * If the prefix is not found, Null is returned. + */ +BEGIN_METHOD(Trie_Complete, GB_STRING prefix) + + struct trie_prefix p; + char *s; + + trie_reset_prefix(&p); + trie_constrain2(THIS->root, &p, STRING(prefix), LENGTH(prefix)); + if (!p.node) { + GB.ReturnNull(); + return; + } + + s = GB.NewString(STRING(prefix), LENGTH(prefix)); + /* Again, we need to special-case p.node->len - p.i == 0. */ + if (p.node->len - p.i) + s = GB.AddString(s, p.node->key + p.i, p.node->len - p.i); + GB.ReturnString(s); + GB.ReturnBorrow(); + GB.FreeString(&s); + GB.ReturnRelease(); + +END_METHOD + +/**G + * Return the number of keys in the Trie. + */ +BEGIN_PROPERTY(Trie_Count) + + GB.ReturnInteger(THIS->count); + +END_PROPERTY + +/**G + * Return the key of the last enumerated element. + */ +BEGIN_PROPERTY(Trie_Key) + + GB.ReturnString(THIS->key); + +END_PROPERTY + +GB_DESC CTrie[] = { + /**G + * This class implements a Patricia Trie. You can learn about its + * semantics from [Wikipedia] (http://en.wikipedia.org/wiki/Radix_tree) + */ + GB_DECLARE("Trie", sizeof(CTRIE)), + + GB_METHOD("_new", NULL, Trie_new, NULL), + GB_METHOD("_free", NULL, Trie_free, NULL), + + GB_METHOD("_get", "v", Trie_get, "(Key)s"), + GB_METHOD("_put", NULL, Trie_put, "(Value)v(Key)s"), + GB_METHOD("_next", "v", Trie_next, NULL), + + /**G Trie Add + * Add an element to the trie. A synonym for _put. + */ + GB_METHOD("Add", NULL, Trie_put, "(Value)v(Key)s"), + GB_METHOD("Remove", NULL, Trie_Remove, "(Key)s"), + GB_METHOD("Clear", NULL, Trie_Clear, NULL), + GB_METHOD("Exist", "b", Trie_Exist, "(Key)s"), + GB_METHOD("GetPrefix", "TriePrefix", Trie_GetPrefix, "(Prefix)s"), + GB_METHOD("Complete", "s", Trie_Complete, "(Prefix)s"), + + GB_PROPERTY_READ("Count", "i", Trie_Count), + GB_PROPERTY_READ("Key", "s", Trie_Key), + + GB_END_DECLARE +}; + +#undef THIS +#define THIS ((CPREFIX *) _object) +#define TRIE (THIS->trie) +#define PREFIX (&THIS->p) + +/* A TriePrefix is a valid object if it's non-NULL, the prefix string was + * found and the trie was not modified since its creation. */ +static int check_prefix(CPREFIX *p) +{ + //printf("p=%p, state=%d, time=%lu (%lu)\n", p, p->p.state, p->time, p->trie->time); + return !p || p->p.state == TRIE_UNSET || p->time != p->trie->time; +} + +BEGIN_METHOD_VOID(TriePrefix_free) + + GB.Unref((void **) &TRIE); + GB.FreeString(&THIS->key); + GB.FreeString(&THIS->prefix); + +END_METHOD + +/**G + * This is the same as hTrie[hPrefix.Prefix & RelKey] where hTrie is the + * Trie from which hPrefix was created, except that it is faster. + */ +BEGIN_METHOD(TriePrefix_get, GB_STRING rel) + + GB_VARIANT_VALUE *val; + + val = trie_value2(TRIE->root, PREFIX, STRING(rel), LENGTH(rel)); + if (!val) + GB.ReturnNull(); + else + GB.ReturnVariant(val); + +END_METHOD + +/**G + * Iterate through all keys in the prefix range, in lexicographic order. + * + * See also + * [../../trie/_next] + */ +BEGIN_METHOD_VOID(TriePrefix_next) + + struct enum_state *state = GB.GetEnum(); + struct stack *top; + struct trie *node = NULL; /* silence compiler */ + + if (!state->start) { + state->start = 1; + GB.FreeString(&THIS->key); + THIS->key = GB.NewString("", 0); + GB.Alloc((void **) &state->top, sizeof(*state->top)); + state->top->node = PREFIX->node; + state->top->idx = 0; + state->top->visited = 0; + state->top->prev = NULL; + top = state->top; + goto visit; + } + + top = state->top; +next: + if (top->idx >= top->node->nchildren) { + struct stack *prev = top->prev; + size_t len = GB.StringLength(THIS->key) - top->node->len; + + THIS->key = GB.ExtendString(THIS->key, len); + GB.Free((void **) &top); + top = prev; + if (!top) { + GB.StopEnum(); + return; + } + top->idx++; + goto next; + } + + node = top->node->children[top->idx]; +visit: + if (!top->visited) { + int i = 0; + + /* Take the offset in the prefix' root into account */ + if (!top->prev) + i = PREFIX->i; + /* If top->node->len - i == 0, we want to add nothing, but + * GB.AddString() will take that as a request to use + * strlen() itself. So special-case that. */ + if (top->node->len - i) { + THIS->key = GB.AddString(THIS->key, + top->node->key + i, + top->node->len - i); + } + } else { + struct stack *old = top; + + if (old->node->nchildren) { + GB.Alloc((void **) &top, sizeof(*top)); + top->node = node; + top->idx = 0; + top->visited = 0; + top->prev = old; + goto visit; + } + } + + top->visited = 1; + state->top = top; + if (!top->node->value) + goto next; + GB.ReturnVariant(top->node->value); + +END_METHOD + +/**G + * Return if the given key exists relative to the prefix. This returns the + * same as hTrie.Exist(hPrefix.Prefix & RelKey). + * + * See also + * [../_get] + */ +BEGIN_METHOD(TriePrefix_Exist, GB_STRING rel) + + struct trie *node; + + node = trie_find2(TRIE->root, PREFIX, STRING(rel), LENGTH(rel)); + GB.ReturnBoolean(!!node); + +END_METHOD + +/**G + * Add bytes to the prefix. If the extended prefix does not exist within the + * Trie, an error is raised. + */ +BEGIN_METHOD(TriePrefix_Add, GB_STRING rel) + + char *s = THIS->prefix; + struct trie_prefix new = *PREFIX; + + trie_constrain2(TRIE->root, &new, STRING(rel), LENGTH(rel)); + if (!new.node) { + GB.Error("Prefix does not exist"); + return; + } + *PREFIX = new; + THIS->prefix = GB.AddString(s, STRING(rel), LENGTH(rel)); + +END_METHOD + +/**G + * Remove bytes from the end of the prefix. There is no way this function + * can fail since it removes Min(Len(hPrefix.Prefix), Length) bytes and + * if the TriePrefix was valid, the weaker prefix will also be valid. + */ +BEGIN_METHOD(TriePrefix_Remove, GB_INTEGER len) + + char *s = THIS->prefix; + size_t len = VARGOPT(len, 1), l; + + if (len < 0) + GB.Error("Invalid length"); + if (len <= 0) + return; + + l = GB.StringLength(s); + if (len > l) + len = l; + + /* + * Since the struct trie has no uplinks to parent nodes, we cannot + * go backwards beyond node boundaries. Thus, we do it the less + * elegant way: remove characters from the Prefix property and + * recreate the prefix from scratch. + * + * As noted in the help text above, this will always work. + */ + l -= len; + THIS->prefix = GB.ExtendString(s, l); + trie_reset_prefix(PREFIX); + trie_constrain2(TRIE->root, PREFIX, THIS->prefix, l); + +END_METHOD + +/**G + * Return the **relative key** of the last enumerated object. + * + * See also + * [../_get] + */ +BEGIN_PROPERTY(TriePrefix_Key) + + GB.ReturnString(THIS->key); + +END_PROPERTY + +/**G + * Return the prefix string of this object. + */ +BEGIN_PROPERTY(TriePrefix_Prefix) + + GB.ReturnString(THIS->prefix); + +END_PROPERTY + +GB_DESC CTriePrefix[] = { + /**G + * This class provides a read-only view of part of a Trie. It lets + * you examine keys with a common prefix. Searches begin in the + * middle of the Trie and are thus faster. + * + * TriePrefix objects are invalidated when you change the Trie, so + * be careful if you store them persistently. + */ + GB_DECLARE("TriePrefix", sizeof(CPREFIX)), + GB_NOT_CREATABLE(), + + GB_HOOK_CHECK(check_prefix), + + GB_METHOD("_free", NULL, TriePrefix_free, NULL), + + GB_METHOD("_get", "v", TriePrefix_get, "(RelKey)s"), + GB_METHOD("_next", "v", TriePrefix_next, NULL), + + GB_METHOD("Exist", "b", TriePrefix_Exist, "(RelKey)s"), + GB_METHOD("Add", NULL, TriePrefix_Add, "(RelKey)s"), + GB_METHOD("Remove", NULL, TriePrefix_Remove, "[(Length)i]"), + + GB_PROPERTY_READ("Key", "s", TriePrefix_Key), + GB_PROPERTY_READ("Prefix", "s", TriePrefix_Prefix), + + GB_END_DECLARE +}; diff --git a/main/lib/data/c_trie.h b/main/lib/data/c_trie.h new file mode 100644 index 00000000..b29010f0 --- /dev/null +++ b/main/lib/data/c_trie.h @@ -0,0 +1,33 @@ +/* + * c_trie.h + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_TRIE_H +#define __C_TRIE_H + +#include "gambas.h" + +extern GB_INTERFACE GB; + +#ifndef __C_TRIE_C +extern GB_DESC CTrie[], CTriePrefix[]; +#endif + +#endif /* __C_TRIE_H */ diff --git a/main/lib/data/gb.data.component b/main/lib/data/gb.data.component new file mode 100644 index 00000000..33a5dc85 --- /dev/null +++ b/main/lib/data/gb.data.component @@ -0,0 +1,3 @@ +[Component] +Author=Tobias Boege +State=Stable diff --git a/main/lib/data/gb.data/.component b/main/lib/data/gb.data/.component new file mode 100644 index 00000000..07b768ee --- /dev/null +++ b/main/lib/data/gb.data/.component @@ -0,0 +1,3 @@ +[Component] +Key=gb.data +Version=3.10.90 diff --git a/main/lib/data/gb.data/.directory b/main/lib/data/gb.data/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/main/lib/data/gb.data/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/main/lib/data/gb.data/.icon.png b/main/lib/data/gb.data/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a9254f949f86671b58a744a6a93623576acc5217 GIT binary patch literal 10569 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __LIST_H +#define __LIST_H + +#include "gambas.h" + +typedef struct list { + struct list *prev, *next; +} LIST; + +#define INIT_LIST(name) {&name, &name} +#define DECLARE_LIST(name) LIST name = INIT_LIST(name) + +#define LIST_data(list, type, member) \ + ((type *) ((char *) (list) - offsetof(type, member))) + +/* All but the entry node */ + +#define list_for_each(_node, _list) \ + for (_node = (_list)->next; _node != (_list); _node = _node->next) + +#define list_for_each_prev(_node, _list) \ + for (_node = (_list)->prev; _node != (_list); _node = _node->prev) + +/* All, including the entry node (at the beginning of the loop) */ + +#define list_for_each_first(_node, _list, _c) \ + for (_node = (_list), _c = 1; _node != (_list) || _c--; \ + _node = _node->next) + +#define list_for_each_prev_first(_node, _list, _c) \ + for (_node = (_list), _c = 1; _node != (_list) || _c--; \ + _node = _node->prev) + +/* All but entry node, may modify the list */ + +#define list_for_each_safe(_node, _list, _next) \ + for (_node = (_list)->next, _next = _node->next; _node != (_list);\ + _node = _next, _next = _node->next) + +static inline void LIST_init(LIST *list) +{ + list->prev = list->next = list; +} + +static inline int LIST_is_empty(LIST *list) +{ + return list->next == list; +} + +static inline void LIST_prepend(LIST *list, LIST *new) +{ + register LIST *new_end = new->prev; + + list->prev->next = new; + new->prev = list->prev; + new_end->next = list; + list->prev = new_end; +} + +static inline void LIST_append(LIST *list, LIST *new) +{ + register LIST *new_end = new->prev; + + new_end->next = list->next; + list->next->prev = new_end; + new->prev = list; + list->next = new; +} + +static inline LIST *LIST_unlink(LIST *node) +{ + node->prev->next = node->next; + node->next->prev = node->prev; + node->prev = node->next = node; + return node; +} + +#endif /* !__LIST_H */ diff --git a/main/lib/data/lookup3.h b/main/lib/data/lookup3.h new file mode 100644 index 00000000..861da47d --- /dev/null +++ b/main/lib/data/lookup3.h @@ -0,0 +1,142 @@ +/* + * lookup3.h + * + * These routines were originally written by Bob Jenkins. The original file + * http://burtleburtle.net/bob/c/lookup3.c (10th Aug 2013) contained these + * credits: + * + * --- + * lookup3.c, by Bob Jenkins, May 2006, Public Domain. + * + * These are functions for producing 32-bit hashes for hash table lookup. + * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() + * are externally useful functions. Routines to test the hash are included + * if SELF_TEST is defined. You can use this free for any purpose. It's in + * the public domain. It has no warranty. + * --- + * + * This file is a (rigorously) modified version for Gambas. + * + * Copyright (C) 2013 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __LOOKUP3_H +#define __LOOKUP3_H + +#include + +#define lookup3_size(n) ((uint32_t) 1 << (n)) +#define lookup3_mask(n) (lookup3_size(n) - 1) +#define lookup3_rot(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) + +/* lookup3_mix -- mix 3 32-bit values reversibly. */ +#define lookup3_mix(a, b, c) \ +{ \ + a -= c; a ^= lookup3_rot(c, 4); c += b; \ + b -= a; b ^= lookup3_rot(a, 6); a += c; \ + c -= b; c ^= lookup3_rot(b, 8); b += a; \ + a -= c; a ^= lookup3_rot(c, 16); c += b; \ + b -= a; b ^= lookup3_rot(a, 19); a += c; \ + c -= b; c ^= lookup3_rot(b, 4); b += a; \ +} + +/* lookup3_final -- final mixing of 3 32-bit values (a,b,c) into c */ +#define lookup3_final(a,b,c) \ +{ \ + c ^= b; c -= lookup3_rot(b, 14); \ + a ^= c; a -= lookup3_rot(c, 11); \ + b ^= a; b -= lookup3_rot(a, 25); \ + c ^= b; c -= lookup3_rot(b, 16); \ + a ^= c; a -= lookup3_rot(c, 4); \ + b ^= a; b -= lookup3_rot(a, 14); \ + c ^= b; c -= lookup3_rot(b, 24); \ +} + +/* + * __lookup3_64() -- Hashes an array of 32 bit values into a 64 bit value + * @k : the key, an array of uint32_t values + * @length: the length of the key, in uint32_ts + * @pc : seed #1 + * @pb : seed #2 + * + * If 'pb' is zero then the result will be valid a 32 bit value. + */ +static inline uint64_t __lookup3_64(const uint32_t *k, size_t length, + uint32_t pc, uint32_t pb) +{ + uint32_t a, b, c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t) (length << 2)) + pc; + c += pb; + + /* Handle most of the key */ + while (length > 3) { + a += k[0]; + b += k[1]; + c += k[2]; + lookup3_mix(a, b, c); + length -= 3; + k += 3; + } + + /* Handle the last 3 uint32_t's */ + switch(length) { /* All the case statements fall through */ + case 3: + c += k[2]; + case 2: + b += k[1]; + case 1: + a += k[0]; + lookup3_final(a, b, c); + case 0: /* case 0: nothing left to add */ + break; + } + /* Report the result. Mirror the 32 bit version if 'pb' was zero. */ + return c + pb ? ((uint64_t) b) << 32 : 0; +} + +/* + * Arbitrary string hash wrappers. They pad the string with '\0' to a 32 + * bits boundary. So two (key,length) pairs ("a",1) and ("a\0",2) would + * give identical hashes. Beware! + */ + +static inline uint32_t lookup3_32(const char *k, size_t length, + uint32_t initval) +{ + size_t len = (length + 31) / 32; + uint32_t key[len]; + + bzero(key, len * sizeof(key[0])); + memcpy(key, k, length); + return __lookup3_64(key, len, initval, 0); +} + +static inline uint64_t lookup3_64(const char *k, size_t length, + uint32_t pc, uint32_t pb) +{ + size_t len = (length + 31) / 32; + uint32_t key[len]; + + bzero(key, len * sizeof(key[0])); + memcpy(key, k, length); + return __lookup3_64(key, len, pc, pb); +} + +#endif /* __LOOKUP3_H */ diff --git a/main/lib/data/main.c b/main/lib/data/main.c new file mode 100644 index 00000000..c09da0bf --- /dev/null +++ b/main/lib/data/main.c @@ -0,0 +1,81 @@ +/* + * main.c - gb.data glue + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __MAIN_C + +#include "c_list.h" +#include "c_deque.h" +#include "c_circular.h" +#include "c_avltree.h" +#include "c_trie.h" +#include "c_graph.h" +#include "c_graphmatrix.h" +#include "c_heap.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = { + CList, + CListBackwards, + CListItem, + + CDeque, + CStack, + CQueue, + CPrioQueue, + + CCircular, + + CAvlTree, + + CTrie, + CTriePrefix, + + CGraph, + CGraphVertices, + CGraphEdges, + CGraphInEdges, + CGraphOutEdges, + CGraphAdjacent, + CGraphVertex, + CGraphEdge, + + CGraphMatrix, + CMatrixVertices, + CMatrixEdges, + CMatrixVertex, + CMatrixEdge, + + CHeap, + + NULL +}; + +int EXPORT GB_INIT() +{ + return 0; +} + + +void EXPORT GB_EXIT() +{ +} diff --git a/main/lib/data/main.h b/main/lib/data/main.h new file mode 100644 index 00000000..1b8d6481 --- /dev/null +++ b/main/lib/data/main.h @@ -0,0 +1,32 @@ +/* + * main.h + * + * Copyright (C) 2012/3 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif /* !__MAIN_H */ diff --git a/main/lib/data/string_compare.h b/main/lib/data/string_compare.h new file mode 100644 index 00000000..dcce4d07 --- /dev/null +++ b/main/lib/data/string_compare.h @@ -0,0 +1,43 @@ +/*************************************************************************** + + gb_common_string_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gambas.h" + +int STRING_compare(const char *str1, int len1, const char *str2, int len2) +{ + uint i; + int len = len1 < len2 ? len1 : len2; + int diff; + register unsigned char c1, c2; + + for (i = 0; i < len; i++) + { + c1 = str1[i]; + c2 = str2[i]; + if (LIKELY(c1 > c2)) return 1; + if (LIKELY(c1 < c2)) return -1; + } + + diff = len1 - len2; + return LIKELY(diff < 0) ? (-1) : LIKELY(diff > 0) ? 1 : 0; +} diff --git a/main/lib/data/trie.c b/main/lib/data/trie.c new file mode 100644 index 00000000..5c44cc0c --- /dev/null +++ b/main/lib/data/trie.c @@ -0,0 +1,756 @@ +/* + * trie.c + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include +#include + +#include "trie.h" + +#include "c_trie.h" + +/** + * __key_index() - Return a unique number for the character + * @c: char + */ +static inline int __key_index(char c) +{ + return (int) (unsigned char) c; +} + +static inline int popcnt(uint64_t word) +{ + int n; + + for (n = 0; word; n++) + word &= word - 1; + return n; +} + +#define MASK_SIZE \ + (__CHAR_BIT__ * sizeof(((struct trie *) 0)->mask[0])) +#define INDEX(i) (i / MASK_SIZE) +#define OFFSET(i) (i % MASK_SIZE) + +/** + * __key_to_array_index() - Return array index over a node's ->children + * corresponding to a key character + * @node: struct trie + * @c: the character + */ +static inline int __key_to_array_index(const struct trie *node, char c) +{ + int i = __key_index(c), j, n; + + for (j = n = 0; i >= MASK_SIZE; j++, i -= MASK_SIZE) + n += popcnt(node->mask[j]); + n += popcnt(node->mask[j] & ((1ULL << i) - 1)); + return n; +} + +static inline void __set_bit(uint64_t mask[4], int i) +{ + mask[INDEX(i)] |= 1ULL << (OFFSET(i)); +} + +static inline void set_bit(struct trie *node, int i) +{ + __set_bit(node->mask, i); +} + +static inline void clear_bit(struct trie *node, int i) +{ + node->mask[INDEX(i)] &= ~(1ULL << (OFFSET(i))); +} + +static inline int test_bit(const struct trie *node, int i) +{ + return !!(node->mask[INDEX(i)] & (1ULL << (OFFSET(i)))); +} + +/** + * get_continuation() - Return the node continuing the key of another node + * with a given character + * @node: struct trie + * @c: the character + * + * If you have a trie like + * + * 0 + * | + * te + * | + * +--+--+ + * | | + * st rm + * + * and search for the key "term", this function comes in handy at the node + * "te". You will call get_continuation(te_node, 'r') which yields rm_node. + * + * If no such continuation exists, NULL is returned. + */ +static inline struct trie *get_continuation(const struct trie *node, char c) +{ + int i = __key_index(c); + int j = __key_to_array_index(node, c); + + if (!test_bit(node, i)) + return NULL; + return node->children[j]; +} + +struct __trie_find_res { + struct trie *node, *parent; + int i, j; +}; + +/** + * __trie_find() - Get the node containing a key + * @trie: struct trie + * @key: the key + * @len: length + * + * This function returns the node in which `key' ends which may NOT be the + * node which has exactly the key `key'. It returns NULL, if no such node + * was found. + */ +static struct __trie_find_res __trie_find(const struct trie *trie, + const char *key, size_t len) +{ + struct trie *node = (struct trie *) trie, *parent = NULL; + int i = 0, j = 0; + + while (node) { + i = 0; + while (i < node->len && j < len && node->key[i] == key[j]) { + i++; + j++; + } + /* + * Four cases: + * 1) the `key' and `node' were entirely consumed: perfect + * match. Get out. + * 2) only `key' consumed: we're done as the key lies + * within the node. + * 3) only `node' was consumed: recurse to its children. + * 4) if neither of the above, node and key deverged here, + * so break the loop since this is as close as we can + * get. + */ + if (j == len) { + break; + } else if (i == node->len) { + parent = node; + node = get_continuation(node, key[j]); + } else { + break; + } + } + return (struct __trie_find_res) {node, parent, i, j}; +} + +/** + * __is_exact() - Return whether a found node matches a key exactly + * @res: struct __trie_find_res + * @len: length + */ +static inline int __is_exact(struct __trie_find_res *res, size_t len) +{ + return res->i == res->node->len && res->j == len; +} + +/** + * __trie_find_exact() - Get the node with ends in a key + * @trie: struct trie + * @key: the key + * @len: length + * + * Unlike __trie_find(), this function returns the node which has the key + * `key' or NULL if none. Note, however, that this is not a guarantee that + * the node contains a non-NULL ->value. + */ +static struct trie *__trie_find_exact(const struct trie *trie, + const char *key, size_t len) +{ + struct __trie_find_res res = __trie_find(trie, key, len); + + return (res.node && __is_exact(&res, len)) ? res.node : NULL; +} + +/** + * __new_node() - Allocate a trie node + * @key: part of the key + * @len: length of `key' + * @value: payload + * + * If `len' equals zero, the length is obtained from `key' via strlen(). For + * the single case where a zero length is correct, this doesn't do much + * harm - as opposed to the strangeness of comparing a size_t to -1 or some + * other clearly invalid value. + */ +static struct trie *new_node(const char *key, size_t len, void *value) +{ + struct trie *trie; + + /*if (!len) + len = strlen(key);*/ + + GB.Alloc((void **) &trie, sizeof(*trie) + len); + memset(trie->mask, 0, sizeof(trie->mask)); + trie->children = NULL; + trie->nchildren = 0; + trie->value = value; + trie->len = len; + memcpy(trie->key, key, len); + return trie; +} + +/** + * new_trie() - Allocate a new trie + */ +struct trie *new_trie(void) +{ + return new_node("", 0, NULL); +} + +/** + * destroy_node() - Deallocate a single node + * @node: struct trie + * @dtor: value destructor + */ +static void destroy_node(struct trie *node, void (*dtor)(void *)) +{ + GB.Free((void **) &node->children); + if (node->value && dtor) + dtor(node->value); + GB.Free((void **) &node); +} + +/** + * destroy_trie() - Deallocate an entire trie + * @trie: struct trie + * @dtor: value destructor + */ +void destroy_trie(struct trie *trie, void (*dtor)(void *)) +{ + int i; + + for (i = 0; i < trie->nchildren; i++) + destroy_trie(trie->children[i], dtor); + destroy_node(trie, dtor); +} + +/** + * clear_trie() - Remove all but the root + * @trie: struct trie + * @dtor: value destructor + */ +void clear_trie(struct trie *trie, void (*dtor)(void *)) +{ + int i; + + for (i = 0; i < trie->nchildren; i++) + destroy_trie(trie->children[i], dtor); + memset(trie->mask, 0, sizeof(trie->mask)); + GB.Free((void **) &trie->children); + trie->children = NULL; + trie->nchildren = 0; + if (trie->value) + dtor(trie->value); + trie->value = NULL; +} + +/** + * __sort_two_children() - Sort (at most) two children into a children array + * @array: array with enough space for the element(s) + * @mask: buffer + * @child1: struct trie + * @child2: struct trie, may be NULL + * + * This function writes the apropriate mask for the array into the `mask' + * argument. + * + * The `child2' can be NULL in which case it is ignored and not assigned to + * the array. + */ +static inline void __sort_two_children(const struct trie *array[2], + uint64_t mask[4], + const struct trie *child1, + const struct trie *child2) +{ + int i, j; + + i = __key_index(*child1->key); + j = child2 ? __key_index(*child2->key) : 0; /* just to initialise */ + if (!child2 || i < j) { + array[0] = child1; + if (child2) + array[1] = child2; + } else { + array[0] = child2; + array[1] = child1; + } + __set_bit(mask, i); + if (child2) + __set_bit(mask, j); +} + +/** + * __trie_insert_split() - Split a node to insert a new key + * @res: struct __trie_find_res + * @key: the key + * @len: length + * @value: the value + */ +static void __trie_insert_split(struct __trie_find_res *res, const char *key, + size_t len, void *value) +{ + struct trie *node = res->node, *bottom, *branch = NULL; + struct trie **topchildren; + /* + * If key[res->j] == '\0', the key lies within `node' and will be in + * the "top" node already, so we save the `branch'. + */ + int have_branch = !!key[res->j]; + + /* + * - `bottom' will contain the bottom part of the split node; + * - `branch' will be the new node associated with the wanted key + * (if it is not within the `node') + * - `topchildren' is the new ->children array of the "top" half of + * the split node - which will consist of `bottom' and `branch'. + */ + bottom = new_node(&node->key[res->i], node->len - res->i, + node->value); + + if (have_branch) { + branch = new_node(&key[res->j], len - res->j, value); + GB.Alloc((void **) &topchildren, 2 * sizeof(*topchildren)); + } else { + GB.Alloc((void **) &topchildren, sizeof(*topchildren)); + } + /* While doing the Alloc() stuff, we can already Realloc() the + * "top" node here... */ + GB.Realloc((void **) &node, sizeof(*node) + res->j); + /* Link the split node into the trie again */ + int i = __key_to_array_index(res->parent, *node->key); + + res->parent->children[i] = node; + + /* + * new_node() set `bottom' already up quite well. However, we need + * to tweak: ->mask, ->children and ->nchildren. + * + * After we have copied them from the "top" node, we can set the + * members there correctly to: ->mask, ->children, ->nchildren, + * ->value and ->len need tweaking while ->key was cut properly by + * Realloc(). + */ + memcpy(bottom->mask, node->mask, sizeof(bottom->mask)); + bottom->children = node->children; + bottom->nchildren = node->nchildren; + + /* + * __sort_two_children() is aware that `branch' may be NULL. + */ + memset(node->mask, 0, sizeof(node->mask)); + __sort_two_children((const struct trie **) topchildren, + node->mask, bottom, branch); + node->children = topchildren; + node->nchildren = have_branch ? 2 : 1; + node->value = NULL; + node->len = res->i; + + /* + * The new `branch' has everything right if it exists as it has no + * children which need extra care. If it wasn't created, we need to + * assign the `value' to the "top" node now. + */ + if (!have_branch) + node->value = value; +} + +/** + * __trie_insert_child() - Extend an already existing key to a new one + * @res: struct __trie_find_res + * @key: the key + * @len: length + * @value: the value + */ +static void __trie_insert_child(struct __trie_find_res *res, const char *key, + size_t len, void *value) +{ + struct trie *node = res->parent, *child, **children; + int i, j, k; + + /* + * Here, we CAN'T have the case that `node' has no children and no + * value so that we could concatenate the keys. This just cannot + * happen while adding nodes: then we would have added a leaf node + * without a value which doesn't happen. The case above can only + * occur when children are *removed* from an interior parent without + * a value and is thus handled in the trie_remove() function. + */ + + child = new_node(&key[res->j], len - res->j, value); + i = __key_index(*child->key); + j = __key_to_array_index(node, *child->key); + children = node->children; + GB.Realloc((void **) &children, (node->nchildren + 1) * + sizeof(*children)); + + for (k = node->nchildren; k > j; k--) + children[k] = children[k - 1]; + children[k] = child; + node->children = children; + node->nchildren++; + set_bit(node, i); +} + +/** + * trie_insert() - Associate a value with a key in the trie + * @trie: struct trie + * @key: the key + * @len: length + * @value: the value + * + * You can use the empty string as `key' to save data in the trie's root + * node. Note that the NULL pointer is an invalid `value' and is used to + * detect value-less nodes, so don't use it! + * + * If a value was replaced, the old value is returned. + */ +void *trie_insert(struct trie *trie, const char *key, size_t len, void *value) +{ + struct __trie_find_res res = __trie_find(trie, key, len); + + if (res.node) { + if (__is_exact(&res, len)) { + void *last = res.node->value; + + res.node->value = value; + return last; + } + __trie_insert_split(&res, key, len, value); + } else { + __trie_insert_child(&res, key, len, value); + } + return NULL; +} + +/** + * __trie_remove_leaf() - Remove a leaf node + * @res: struct __trie_find_res + * @dtor: value destructor + */ +static void __trie_remove_leaf(struct __trie_find_res *res, + void (*dtor)(void *)) +{ + struct trie *node = res->node, *parent = res->parent; + int i, j, k; + + /* + * Unlink the node which means + * a) delete it from its parent's mask and + * b) delete it from its parent's children array + * + * Then we can simply destroy it. + */ + + i = __key_index(*node->key); + j = __key_to_array_index(parent, *node->key); + + /* + * b) -- yes, we do b) before a) to not need to undo the mask + * changes if an allocation fails :-) and the code is different for + * each of the cases below, anyway: + * i) if the parent will have no children left, we don't bother + * doing reallocations and stuff. + * ii) if the value-less non-root parent will only have one child + * left, merge these two nodes to save space. + * iii) else, reallocate the children array normally. + * + * In i), it is impossible that the parent would have no value + * because of ii) in a former removal (just saying that every leaf + * node (except the root if it becomes a leaf) is guaranteed to have + * a value). + */ + if (parent->nchildren == 1) { /* i) */ + GB.Free((void **) &parent->children); + parent->children = NULL; + parent->nchildren = 0; + /* a) */ + clear_bit(parent, i); + parent->nchildren--; + /* !parent->len is equivalent to parent == trie_root */ + } else if (parent->nchildren == 2 && !parent->value + && !parent->len) { /* ii) */ + struct trie *other; + + if (parent->children[0] == node) + other = parent->children[1]; + else + other = parent->children[0]; + GB.Realloc((void **) &parent, sizeof(*parent) + parent->len + + other->len); + memcpy(parent->key + parent->len, other->key, other->len); + parent->len += other->len; + /* does a) */ + memcpy(parent->mask, other->mask, sizeof(parent->mask)); + GB.Free((void **) &parent->children); + parent->children = other->children; + parent->nchildren = other->nchildren; + parent->value = other->value; + /* Do NOT destroy_node() as we copied its ->children! */ + GB.Free((void **) &other); + } else { /* iii) */ + /* does a) */ + for (k = j + 1; k < parent->nchildren; k++) + parent->children[k - 1] = parent->children[k]; + parent->nchildren--; + GB.Realloc((void **) &parent->children, parent->nchildren * + sizeof(*parent->children)); + clear_bit(parent, i); + } + + destroy_node(node, dtor); +} + +/** + * __trie_remove_interior() - Remove an interior node + * @res: struct __trie_find_res + * @dtor: value destructor + */ +static void __trie_remove_interior(struct __trie_find_res *res, + void (*dtor)(void *)) +{ + /* + * Let's see: an interior node can only have 2 or more children, so + * we cannot possibly do any compression of the nodes. We just erase + * the value and leave the trie structure as-is. + */ + dtor(res->node->value); + res->node->value = NULL; +} + +/** + * trie_remove() - Remove a key from the trie + * @trie: struct trie + * @key: the key + * @len: length + * @dtor: value destructor + */ +void trie_remove(struct trie *trie, const char *key, size_t len, + void (*dtor)(void *)) +{ + struct __trie_find_res res; + struct trie *node; + + res = __trie_find(trie, key, len); + node = res.node; + /* + * We only want to work with valued, exactly-matching non-roots. + * Delete a value from the root anyways. + */ + if (!node || !__is_exact(&res, len) || !node->value) + return; + if (node == trie) { + dtor(node->value); + node->value = NULL; + return; + } + + if (!node->children) + __trie_remove_leaf(&res, dtor); + else + __trie_remove_interior(&res, dtor); +} + +/** + * trie_find() - Get a trie node from its key + * @trie: struct trie + * @key: the key + * @len: length + * + * Returns NULL if the key was not found. The empty string maps to the root + * node of the trie. + */ +struct trie *trie_find(const struct trie *trie, const char *key, size_t len) +{ + return __trie_find_exact(trie, key, len); +} + +/** + * trie_value() - Get value corresponding to a key + * @trie: struct trie + * @key: the key + * @len: length + * + * Return NULL if the key was not found. The empty string maps to the root + * node of the trie. + */ +void *trie_value(const struct trie *trie, const char *key, size_t len) +{ + struct trie *node = trie_find(trie, key, len); + + return node ? node->value : NULL; +} + +/** + * trie_constrain() - Constrain the trie paths + * @trie: struct trie + * @p: struct trie_prefix + * @c: character + * + * To `constrain' a trie means to limit keys to a given prefix. If you have + * a trie consisting of keys "test", "tesla" and "term" and you constrain it + * with the prefix "tes", only "test" and "tesla" will be reachable. + * + * The constraint is saved in the struct trie_prefix. Contraining a trie + * does not alter its structure so that you can constrain the same trie + * multiple times simultaneously. + * + * By calling this function multiple times, you can refine the prefix in + * `p'. To begin without a constraint, use a prefix filled by + * trie_reset_prefix(). + * + * If the prefix is not found, `p' is reset. + * + * WARNING + * Using any of the prefix-aware functions implies that the trie did not + * change between calls. If it did, the prefix may be invalid and the + * program may crash in consequence. + */ +void trie_constrain(const struct trie *trie, struct trie_prefix *p, char c) +{ + struct trie *node; + int i; + + node = p->node ? : (struct trie *) trie; + i = p->i; + + if (i == node->len) { + node = get_continuation(node, c); + if (!node) + goto reset; + p->node = node; + /* node->len is guaranteed to be positive here */ + p->i = 1; + } else { + if (node->key[i] != c) + goto reset; + p->i = ++i; + } + /* + * As a `logical' node counts only what has a value. If you insert + * "soup" and "sour", the resulting "sou" node would NOT exist + * logically. + * + * Additionally, the match must be exact to count as TRIE_EXACT. + */ + if (p->i == node->len && node->value) + p->state = TRIE_EXACT; + else + p->state = TRIE_EXIST; + return; + +reset: + trie_reset_prefix(p); +} + +/** + * trie_constrain2() - Constrain the trie multiple times + * @trie: struct trie + * @p: struct trie_prefix + * @str: string + * @len: length + * + * This function calls trie_constrain() in a loop - but with the + * difference that as soon as the prefix is not found, the function + * returns. In effect, the `str' is taken of consecutive constraints + * which should *ALL* be applied in row or none of them. + */ +void trie_constrain2(const struct trie *trie, struct trie_prefix *p, + const char *str, size_t len) +{ + int i; + + if (!len) { + p->state = trie->value ? TRIE_EXACT : TRIE_EXIST; + p->node = (struct trie *) trie; + p->i = 0; + return; + } + + for (i = 0; i < len; i++) { + trie_constrain(trie, p, str[i]); + if (p->state == TRIE_UNSET) + return; + } +} + +/** + * trie_find2() - Find a key from a trie constraint + * @trie: struct trie + * @p: struct trie_prefix + * @key: the key relative (!) to the prefix `p' + * @len: length + * + * This function is similar to trie_find(), except that keys are relative to + * the constraint in `p'. + */ +struct trie *trie_find2(const struct trie *trie, + const struct trie_prefix *p, + const char *key, size_t len) +{ + struct trie *node; + int i, j; + + node = p->node ? : (struct trie *) trie; + i = p->i; + /* + * First consume the rest of the prefix node. If none of the trivial + * cases occured, we can then use the normal traversal algorithm. + */ + for (j = 0; i < node->len && j < len; i++, j++) + if (node->key[i] != key[j]) + return NULL; + if (j == len) + return node; + node = get_continuation(node, key[j]); + if (!node) + return NULL; + return __trie_find_exact(node, key, len); +} + +/** + * trie_value2() - Analogon to trie_value() using trie_find2() + * @trie: struct trie + * @p: struct trie_prefix + * @key: the key + * @len: length + */ +void *trie_value2(const struct trie *trie, const struct trie_prefix *p, + const char *key, size_t len) +{ + struct trie *node = trie_find2(trie, p, key, len); + + return node ? node->value : NULL; +} diff --git a/main/lib/data/trie.h b/main/lib/data/trie.h new file mode 100644 index 00000000..f6fa35f2 --- /dev/null +++ b/main/lib/data/trie.h @@ -0,0 +1,82 @@ +/* + * trie.h + * + * Copyright (C) 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef TRIE_H +#define TRIE_H + +#include +#include + +struct trie { + uint64_t mask[4]; /* 256 Bits */ + struct trie **children; + unsigned int nchildren; + void *value; + size_t len; + char key[]; +}; + +enum trie_path { + TRIE_UNSET = 0, /* No chance to get a match from here */ + TRIE_EXIST, /* We are within a key */ + TRIE_EXACT, /* We got an exact match */ +}; + +struct trie_prefix { + enum trie_path state; + struct trie *node; + int i; +}; + +extern struct trie *new_trie(void); +extern void destroy_trie(struct trie *trie, void (*dtor)(void *)); +extern void clear_trie(struct trie *trie, void (*dtor)(void *)); + +extern void *trie_insert(struct trie *trie, const char *key, size_t len, + void *value); +extern void trie_remove(struct trie *trie, const char *key, size_t len, + void (*dtor)(void *)); + +extern struct trie *trie_find(const struct trie *trie, const char *key, + size_t len); +extern void *trie_value(const struct trie *trie, const char *key, + size_t len); + +static inline void trie_reset_prefix(struct trie_prefix *p) +{ + p->state = TRIE_UNSET; + p->node = NULL; + p->i = 0; +} + +extern void trie_constrain(const struct trie *trie, + struct trie_prefix *p, char c); +extern void trie_constrain2(const struct trie *trie, + struct trie_prefix *p, const char *str, + size_t len); +extern struct trie *trie_find2(const struct trie *trie, + const struct trie_prefix *p, + const char *key, size_t len); +extern void *trie_value2(const struct trie *trie, + const struct trie_prefix *p, + const char *key, size_t len); + +#endif /* TRIE_H */ diff --git a/main/lib/db/CConnection.c b/main/lib/db/CConnection.c new file mode 100644 index 00000000..544d844a --- /dev/null +++ b/main/lib/db/CConnection.c @@ -0,0 +1,883 @@ +/*************************************************************************** + + CConnection.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CCONNECTION_C + +#include "main.h" + +#include "CTable.h" +//#include "CView.h" +#include "CDatabase.h" +#include "CUser.h" +#include "CConnection.h" + + +/*************************************************************************** + + Connection + +***************************************************************************/ + +static CCONNECTION *_current = NULL; + +static SUBCOLLECTION_DESC _databases_desc = +{ + ".Connection.Databases", + (void *)CDATABASE_get, + (void *)CDATABASE_exist, + (void *)CDATABASE_list, + (void *)CDATABASE_release +}; + +static SUBCOLLECTION_DESC _users_desc = +{ + ".Connection.Users", + (void *)CUSER_get, + (void *)CUSER_exist, + (void *)CUSER_list, + (void *)CUSER_release +}; + +static SUBCOLLECTION_DESC _tables_desc = +{ + ".Connection.Tables", + (void *)CTABLE_get, + (void *)CTABLE_exist, + (void *)CTABLE_list, + (void *)CTABLE_release +}; + +/*static GB_SUBCOLLECTION_DESC _views_desc = +{ + ".ConnectionViews", + (void *)CVIEW_get, + (void *)CVIEW_exist, + (void *)CVIEW_list +};*/ + + + +static void open_connection(CCONNECTION *_object) +{ + if (DB_Open(&THIS->desc, &THIS->driver, &THIS->db)) + return; + + THIS->limit = 0; + THIS->trans = 0; + + THIS->db.flags.system = !THIS->desc.name || THIS->driver->Database.IsSystem(&THIS->db, THIS->desc.name); +} + +static bool check_opened(CCONNECTION *_object) +{ + DB_CurrentDatabase = &THIS->db; + + /*if (!THIS->db.handle) + open_connection(THIS);*/ + + if (!THIS->db.handle) + { + GB.Error("Connection is not opened"); + return TRUE; + } + else + return FALSE; +} + +#define CHECK_OPEN() \ + if (check_opened(THIS)) \ + return; + +static int get_current(CCONNECTION **current) +{ + if (*current == NULL) + { + if (_current == NULL) + { + GB.Error("No current connection"); + return TRUE; + } + *current = _current; + } + + return FALSE; +} + +#define CHECK_DB() \ + if (get_current((CCONNECTION **)(void *)&_object)) \ + return; + +static void close_connection(CCONNECTION *_object) +{ + if (!THIS->db.handle) + return; + + GB.Unref(POINTER(&THIS->databases)); + THIS->databases = NULL; + GB.Unref(POINTER(&THIS->users)); + THIS->users = NULL; + GB.Unref(POINTER(&THIS->tables)); + THIS->tables = NULL; + + THIS->driver->Close(&THIS->db); + GB.FreeString(&THIS->db.charset); + + THIS->db.handle = NULL; + THIS->driver = NULL; +} + + +BEGIN_METHOD(CCONNECTION_new, GB_STRING url) + + char *url, *name, *p; + + THIS->db.handle = NULL; + THIS->db.ignore_case = FALSE; // Now case is sensitive by default! + THIS->db.timeout = 20; // Connection timeout is 20 seconds by default + THIS->db.timezone = GB.System.TimeZone(); + + if (_current == NULL) + _current = THIS; + + if (MISSING(url)) + return; + + url = GB.ToZeroString(ARG(url)); + + p = index(url, ':'); + if (!p || p == url) goto __BAD_URL; + *p++ = 0; + if (p[0] != '/' || p[1] != '/') goto __BAD_URL; + p += 2; + + THIS->desc.type = GB.NewZeroString(url); + url = p; + + p = rindex(url, '/'); + if (!p || p == url) goto __BAD_URL; + *p++ = 0; + + name = p; + + p = index(url, '@'); + if (p) + { + if (p == url) + goto __BAD_URL; + *p = 0; + THIS->desc.user = GB.NewZeroString(url); + url = p + 1; + } + + p = index(url, ':'); + if (p) + { + *p = 0; + THIS->desc.port = GB.NewZeroString(p + 1); + } + + THIS->desc.host = GB.NewZeroString(url); + THIS->desc.name = GB.NewZeroString(name); + return; + +__BAD_URL: + + GB.Error("Malformed URL"); + +END_METHOD + + +BEGIN_METHOD_VOID(CCONNECTION_free) + + close_connection(THIS); + + if (_current == THIS) + _current = NULL; + + GB.StoreString(NULL, &THIS->desc.type); + GB.StoreString(NULL, &THIS->desc.host); + GB.StoreString(NULL, &THIS->desc.user); + GB.StoreString(NULL, &THIS->desc.password); + GB.StoreString(NULL, &THIS->desc.name); + GB.StoreString(NULL, &THIS->desc.port); + GB.StoreString(NULL, &THIS->db.charset); + +END_METHOD + + +#define IMPLEMENT(_prop) \ +BEGIN_PROPERTY(CCONNECTION_##_prop) \ +\ + if (READ_PROPERTY) \ + GB.ReturnString(THIS->desc._prop); \ + else \ + GB.StoreString(PROP(GB_STRING), &THIS->desc._prop); \ +\ +END_PROPERTY + +IMPLEMENT(type) +IMPLEMENT(host) +IMPLEMENT(user) +IMPLEMENT(password) +IMPLEMENT(name) +IMPLEMENT(port) + + +BEGIN_PROPERTY(CCONNECTION_version) + + CHECK_DB(); + CHECK_OPEN(); + + GB.ReturnInteger(THIS->db.version); + +END_PROPERTY + + +BEGIN_PROPERTY(Connection_Timeout) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->db.timeout); + else + THIS->db.timeout = VPROP(GB_INTEGER); + +END_PROPERTY + + +#if 0 +BEGIN_PROPERTY(Connection_Timezone) + + if (READ_PROPERTY) + GB.ReturnInteger(THIS->db.timezone); + else + THIS->db.timezone = VPROP(GB_INTEGER); + +END_PROPERTY +#endif + + +BEGIN_PROPERTY(CCONNECTION_opened) + + CHECK_DB(); + + GB.ReturnBoolean(THIS->db.handle != NULL); + +END_PROPERTY + + +BEGIN_PROPERTY(CCONNECTION_error) + + CHECK_DB(); + + GB.ReturnInteger(THIS->db.error); + +END_PROPERTY + + +/*BEGIN_PROPERTY(Connection_Transaction) + + CHECK_DB(); + + GB.ReturnInteger(THIS->trans); + +END_PROPERTY*/ + + +BEGIN_METHOD_VOID(CCONNECTION_open) + + CHECK_DB(); + + if (THIS->db.handle) + { + GB.Error("Connection already opened"); + return; + } + + open_connection(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(CCONNECTION_close) + + CHECK_DB(); + + close_connection(THIS); + +END_METHOD + +#if 0 +BEGIN_PROPERTY(CCONNECTION_ignore_case) + + CHECK_DB(); + CHECK_OPEN(); + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->db.ignore_case); + else + { + if (THIS->db.flags.no_case) + { + if (THIS->db.ignore_case) + GB.Error("This database driver cannot be case sensitive"); + else + GB.Error("This database driver is always case sensitive"); + return; + } + THIS->db.ignore_case = VPROP(GB_BOOLEAN); + } + +END_PROPERTY +#endif + +BEGIN_PROPERTY(CCONNECTION_ignore_charset) + + CHECK_DB(); + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->ignore_charset); + else + THIS->ignore_charset = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_PROPERTY(Connection_Collations) + + GB_ARRAY array; + + CHECK_DB(); + CHECK_OPEN(); + + array = THIS->driver->GetCollations(&THIS->db); + if (array) + GB.ReturnObject(array); + else + GB.Error("Collations are not supported"); + +END_PROPERTY + +BEGIN_METHOD_VOID(CCONNECTION_begin) + + CHECK_DB(); + CHECK_OPEN(); + + if (!THIS->db.flags.no_nest || THIS->trans == 0) + THIS->driver->Begin(&THIS->db); + THIS->trans++; + +END_METHOD + + +BEGIN_METHOD_VOID(CCONNECTION_commit) + + CHECK_DB(); + CHECK_OPEN(); + + if (THIS->trans == 0) + return; + + THIS->trans--; + if (!THIS->db.flags.no_nest || THIS->trans == 0) + THIS->driver->Commit(&THIS->db); + +END_METHOD + + +BEGIN_METHOD_VOID(CCONNECTION_rollback) + + CHECK_DB(); + CHECK_OPEN(); + + if (THIS->trans == 0) + return; + + THIS->trans--; + if (!THIS->db.flags.no_nest || THIS->trans == 0) + THIS->driver->Rollback(&THIS->db); + +END_METHOD + + +BEGIN_METHOD(CCONNECTION_limit, GB_INTEGER limit) + + CHECK_DB(); + CHECK_OPEN(); + + THIS->limit = VARG(limit); + GB.ReturnObject(THIS); + +END_PROPERTY + +static char *_make_query_buffer; +static char *_make_query_original; + +static void make_query_get_param(int index, char **str, int *len) +{ + if (index == 1) + *str = _make_query_buffer; + else if (index == 2) + *str = _make_query_original; + + *len = -1; +} + +static char *make_query(CCONNECTION *_object, char *pattern, int len, int narg, GB_VALUE *arg) +{ + char *query; + const char *keyword; + char buffer[32]; + + query = DB_MakeQuery(THIS->driver, pattern, len, narg, arg); + + if (query && THIS->limit > 0 && strncasecmp(query, "SELECT ", 7) == 0) + { + keyword = THIS->db.limit.keyword; + if (!keyword) + keyword = "LIMIT"; + + snprintf(buffer, sizeof(buffer), "%s %d", keyword, THIS->limit); + + _make_query_buffer = buffer; + _make_query_original = &query[7]; + + switch (THIS->db.limit.pos) + { + case DB_LIMIT_AT_BEGIN: + query = GB.SubstString("SELECT &1 &2", 0, make_query_get_param); + break; + + case DB_LIMIT_AT_END: + default: + query = GB.SubstString("SELECT &2 &1", 0, make_query_get_param); + break; + } + + THIS->limit = 0; + } + + return query; +} + +BEGIN_METHOD(CCONNECTION_exec, GB_STRING query; GB_VALUE param[0]) + + char *query; + CRESULT *result; + + CHECK_DB(); + CHECK_OPEN(); + + query = make_query(THIS, STRING(query), LENGTH(query), GB.NParam(), ARG(param[0])); + if (!query) + return; + + result = DB_MakeResult(THIS, RESULT_FIND, NULL, query); + + if (result) + GB.ReturnObject(result); + +END_METHOD + + +BEGIN_METHOD(CCONNECTION_create, GB_STRING table) + + CRESULT *result; + char *table = GB.ToZeroString(ARG(table)); + + CHECK_DB(); + CHECK_OPEN(); + + if (!table || !*table) + { + GB.Error("Void table name"); + return; + } + + result = DB_MakeResult(THIS, RESULT_CREATE, table, NULL); + + if (result) + GB.ReturnObject(result); + else + GB.ReturnNull(); + +END_METHOD + + +static char *get_query(char *prefix, CCONNECTION *_object, char *table, int len_table, char *query, int len_query, GB_VALUE *arg) +{ + if (!len_table) + { + GB.Error("Void table name"); + return NULL; + } + + q_init(); + + q_add(prefix); + q_add(" "); + q_add(DB_GetQuotedTable(THIS->driver, &THIS->db, table, len_table)); + + if (query && len_query > 0) + { + q_add(" "); + if (strncasecmp(query, "WHERE ", 6) && strncasecmp(query, "ORDER BY ", 9)) + q_add("WHERE "); + q_add_length(query, len_query); + } + + query = make_query(THIS, q_get(), q_length(), GB.NParam(), arg); + + return query; +} + + +BEGIN_METHOD(CCONNECTION_find, GB_STRING table; GB_STRING query; GB_VALUE param[0]) + + char *query; + CRESULT *result; + + CHECK_DB(); + CHECK_OPEN(); + + query = get_query("SELECT * FROM", THIS, STRING(table), LENGTH(table), + MISSING(query) ? NULL : STRING(query), + MISSING(query) ? 0 : LENGTH(query), + ARG(param[0])); + + if (!query) + return; + + result = DB_MakeResult(THIS, RESULT_FIND, NULL, query); + + if (result) + GB.ReturnObject(result); + +END_METHOD + + +BEGIN_METHOD(CCONNECTION_delete, GB_STRING table; GB_STRING query; GB_VALUE param[0]) + + char *query; + + CHECK_DB(); + CHECK_OPEN(); + + query = get_query("DELETE FROM", THIS, STRING(table), LENGTH(table), + MISSING(query) ? NULL : STRING(query), + MISSING(query) ? 0 : LENGTH(query), + ARG(param[0])); + + if (!query) + return; + + DB_MakeResult(THIS, RESULT_DELETE, NULL, query); + +END_METHOD + + +BEGIN_METHOD(CCONNECTION_edit, GB_STRING table; GB_STRING query; GB_VALUE param[0]) + + char *query; + CRESULT *result; + /*char *table = GB.ToZeroString(ARG(table));*/ + + CHECK_DB(); + CHECK_OPEN(); + + /*if (check_table(THIS, table, TRUE)) + return;*/ + + query = get_query("SELECT * FROM", THIS, STRING(table), LENGTH(table), + MISSING(query) ? NULL : STRING(query), + MISSING(query) ? 0 : LENGTH(query), + ARG(param[0])); + + if (!query) + return; + + result = DB_MakeResult(THIS, RESULT_EDIT, GB.ToZeroString(ARG(table)), query); + + if (result) + GB.ReturnObject(result); + +END_METHOD + + +BEGIN_METHOD(CCONNECTION_quote, GB_STRING name; GB_BOOLEAN is_table) + + char *name = STRING(name); + int len = LENGTH(name); + + CHECK_DB(); + CHECK_OPEN(); + + if (VARGOPT(is_table, FALSE)) // && THIS->db.flags.schema) + GB.ReturnNewZeroString(DB_GetQuotedTable(THIS->driver, &THIS->db, STRING(name), LENGTH(name))); + else + { + q_init(); + q_add(THIS->driver->GetQuote()); + q_add_length(name, len); + q_add(THIS->driver->GetQuote()); + GB.ReturnString(q_get()); + } + +END_METHOD + + +BEGIN_METHOD(CCONNECTION_format_blob, GB_STRING data) + + DB_BLOB blob; + + CHECK_DB(); + CHECK_OPEN(); + + blob.data = STRING(data); + blob.length = LENGTH(data); + + q_init(); + DB_CurrentDatabase = &THIS->db; + (*THIS->driver->FormatBlob)(&blob, q_add_length); + GB.ReturnString(q_get()); + +END_METHOD + + +BEGIN_METHOD(CCONNECTION_subst, GB_STRING query; GB_VALUE param[0]) + + char *query; + + CHECK_DB(); + CHECK_OPEN(); + + query = make_query(THIS, STRING(query), LENGTH(query), GB.NParam(), ARG(param[0])); + + if (!query) + return; + + GB.ReturnString(query); + +END_METHOD + + +BEGIN_PROPERTY(CCONNECTION_current) + + if (READ_PROPERTY) + GB.ReturnObject(_current); + else + _current = (CCONNECTION *)VPROP(GB_OBJECT); + +END_PROPERTY + + +BEGIN_PROPERTY(CCONNECTION_charset) + + CHECK_DB(); + CHECK_OPEN(); + + if (THIS->db.charset) + GB.ReturnString(THIS->db.charset); + else + GB.ReturnConstZeroString("ASCII"); + +END_PROPERTY + + +BEGIN_PROPERTY(CCONNECTION_databases) + + CHECK_DB(); + CHECK_OPEN(); + + GB_SubCollectionNew(&THIS->databases, &_databases_desc, THIS); + GB.ReturnObject(THIS->databases); + +END_PROPERTY + + +BEGIN_PROPERTY(CCONNECTION_users) + + CHECK_DB(); + CHECK_OPEN(); + + GB_SubCollectionNew(&THIS->users, &_users_desc, THIS); + GB.ReturnObject(THIS->users); + +END_PROPERTY + + +BEGIN_PROPERTY(CCONNECTION_tables) + + CHECK_DB(); + CHECK_OPEN(); + + GB_SubCollectionNew(&THIS->tables, &_tables_desc, THIS); + GB.ReturnObject(THIS->tables); + +END_PROPERTY + + +/*BEGIN_PROPERTY(CCONNECTION_views) + + CHECK_DB(); + CHECK_OPEN(); + + GB.SubCollection.New(&THIS->views, &_views_desc, THIS); + GB.ReturnObject(THIS->views); + +END_PROPERTY*/ + + +BEGIN_PROPERTY(CCONNECTION_debug) + + if (READ_PROPERTY) + GB.ReturnBoolean(DB_IsDebug()); + else + DB_SetDebug(VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(Connection_Handle) + + CHECK_DB(); + GB.ReturnPointer(THIS->db.handle); + +END_PROPERTY + + +BEGIN_PROPERTY(Connection_LastInsertId) + + CHECK_DB(); + CHECK_OPEN(); + + GB.ReturnLong((*THIS->driver->GetLastInsertId)(&THIS->db)); + +END_PROPERTY + +GB_DESC CConnectionDesc[] = +{ + GB_DECLARE("_Connection", sizeof(CCONNECTION)), + + GB_METHOD("_new", NULL, CCONNECTION_new, "[(DatabaseURL)s]"), + GB_METHOD("_free", NULL, CCONNECTION_free, NULL), + + GB_PROPERTY("Type", "s", CCONNECTION_type), + GB_PROPERTY("Host", "s", CCONNECTION_host), + GB_PROPERTY("Login", "s", CCONNECTION_user), + GB_PROPERTY("User", "s", CCONNECTION_user), + GB_PROPERTY("Password", "s", CCONNECTION_password), + GB_PROPERTY("Name", "s", CCONNECTION_name), + GB_PROPERTY("Port", "s", CCONNECTION_port), + GB_PROPERTY("Timeout", "i", Connection_Timeout), + //GB_PROPERTY("Timezone", "i", Connection_Timezone), + GB_PROPERTY_READ("Charset", "s", CCONNECTION_charset), + GB_PROPERTY_READ("Version", "i", CCONNECTION_version), + GB_PROPERTY_READ("Opened", "b", CCONNECTION_opened), + GB_PROPERTY_READ("Error", "i", CCONNECTION_error), + //GB_PROPERTY_READ("Transaction", "i", Connection_Transaction), + GB_PROPERTY("IgnoreCharset", "b", CCONNECTION_ignore_charset), + GB_PROPERTY_READ("Collations", "String[]", Connection_Collations), + GB_PROPERTY_READ("Handle", "p", Connection_Handle), + + GB_PROPERTY_READ("LastInsertId", "l", Connection_LastInsertId), + + GB_METHOD("Open", NULL, CCONNECTION_open, NULL), + GB_METHOD("Close", NULL, CCONNECTION_close, NULL), + + GB_METHOD("Limit", "Connection", CCONNECTION_limit, "(Limit)i"), + GB_METHOD("Exec", "Result", CCONNECTION_exec, "(Request)s(Arguments)."), + GB_METHOD("Create", "Result", CCONNECTION_create, "(Table)s"), + GB_METHOD("Find", "Result", CCONNECTION_find, "(Table)s[(Request)s(Arguments).]"), + GB_METHOD("Edit", "Result", CCONNECTION_edit, "(Table)s[(Request)s(Arguments).]"), + GB_METHOD("Delete", NULL, CCONNECTION_delete, "(Table)s[(Request)s(Arguments).]"), + GB_METHOD("Subst", "s", CCONNECTION_subst, "(Format)s(Arguments)."), + + GB_METHOD("Begin", NULL, CCONNECTION_begin, NULL), + GB_METHOD("Commit", NULL, CCONNECTION_commit, NULL), + GB_METHOD("Rollback", NULL, CCONNECTION_rollback, NULL), + + GB_METHOD("Quote", "s", CCONNECTION_quote, "(Name)s[(Table)b]"), + GB_METHOD("FormatBlob", "s", CCONNECTION_format_blob, "(Data)s"), + + GB_PROPERTY("Tables", ".Connection.Tables", CCONNECTION_tables), + GB_PROPERTY("Databases", ".Connection.Databases", CCONNECTION_databases), + GB_PROPERTY("Users", ".Connection.Users", CCONNECTION_users), + //GB_PROPERTY("Views", ".ConnectionViews", CCONNECTION_views), + + GB_CONSTANT("_Properties", "s", "Type,Host,Login,Password,Name,Port"), + + GB_END_DECLARE +}; + + +GB_DESC CDBDesc[] = +{ + GB_DECLARE("DB", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("Boolean", "i", GB_T_BOOLEAN), + GB_CONSTANT("Integer", "i", GB_T_INTEGER), + GB_CONSTANT("Long", "i", GB_T_LONG), + GB_CONSTANT("Float", "i", GB_T_FLOAT), + GB_CONSTANT("Date", "i", GB_T_DATE), + GB_CONSTANT("String", "i", GB_T_STRING), + GB_CONSTANT("Serial", "i", DB_T_SERIAL), + GB_CONSTANT("Blob", "i", DB_T_BLOB), + + GB_STATIC_PROPERTY("Current", "Connection", CCONNECTION_current), + + GB_STATIC_METHOD("Open", NULL, CCONNECTION_open, NULL), + GB_STATIC_METHOD("Close", NULL, CCONNECTION_close, NULL), + + GB_STATIC_PROPERTY_READ("Charset", "s", CCONNECTION_charset), + GB_STATIC_PROPERTY_READ("Version", "i", CCONNECTION_version), + GB_STATIC_PROPERTY_READ("Opened", "b", CCONNECTION_opened), + GB_STATIC_PROPERTY_READ("Error", "i", CCONNECTION_error), + //GB_STATIC_PROPERTY_READ("Transaction", "i", Connection_Transaction), + GB_STATIC_PROPERTY("IgnoreCharset", "b", CCONNECTION_ignore_charset), + GB_STATIC_PROPERTY_READ("Collations", "String[]", Connection_Collations), + GB_STATIC_PROPERTY_READ("Handle", "p", Connection_Handle), + + GB_STATIC_PROPERTY("Debug", "b", CCONNECTION_debug), + + GB_STATIC_METHOD("Limit", "Connection", CCONNECTION_limit, "(Limit)i"), + GB_STATIC_METHOD("Exec", "Result", CCONNECTION_exec, "(Request)s(Arguments)."), + GB_STATIC_METHOD("Create", "Result", CCONNECTION_create, "(Table)s"), + GB_STATIC_METHOD("Find", "Result", CCONNECTION_find, "(Table)s[(Request)s(Arguments).]"), + GB_STATIC_METHOD("Edit", "Result", CCONNECTION_edit, "(Table)s[(Request)s(Arguments).]"), + GB_STATIC_METHOD("Delete", NULL, CCONNECTION_delete, "(Table)s[(Request)s(Arguments).]"), + GB_STATIC_METHOD("Subst", "s", CCONNECTION_subst, "(Format)s(Arguments)."), + + GB_STATIC_METHOD("Begin", NULL, CCONNECTION_begin, NULL), + GB_STATIC_METHOD("Commit", NULL, CCONNECTION_commit, NULL), + GB_STATIC_METHOD("Rollback", NULL, CCONNECTION_rollback, NULL), + + GB_STATIC_METHOD("Quote", "s", CCONNECTION_quote, "(Name)s[(Table)b]"), + GB_STATIC_METHOD("FormatBlob", "s", CCONNECTION_format_blob, "(Data)s"), + + GB_STATIC_PROPERTY("Tables", ".Connection.Tables", CCONNECTION_tables), + //GB_STATIC_PROPERTY("Views", ".ConnectionViews", CCONNECTION_views), + GB_STATIC_PROPERTY("Databases", ".Connection.Databases", CCONNECTION_databases), + GB_STATIC_PROPERTY("Users", ".Connection.Users", CCONNECTION_users), + + GB_END_DECLARE +}; + + diff --git a/main/lib/db/CConnection.h b/main/lib/db/CConnection.h new file mode 100644 index 00000000..5310b551 --- /dev/null +++ b/main/lib/db/CConnection.h @@ -0,0 +1,56 @@ +/*************************************************************************** + + CConnection.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CCONNECTION_H +#define __CCONNECTION_H + +#include "gambas.h" +#include "gb.db.h" +#include "c_subcollection.h" + +#ifndef __CCONNECTION_C +extern GB_DESC CConnectionDesc[]; +extern GB_DESC CDBDesc[]; +#else + +#define THIS ((CCONNECTION *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + DB_DRIVER *driver; + DB_DATABASE db; + DB_DESC desc; + CSUBCOLLECTION *databases; + CSUBCOLLECTION *tables; + CSUBCOLLECTION *views; + CSUBCOLLECTION *users; + int limit; + int trans; + bool ignore_charset; + } + CCONNECTION; + +#endif diff --git a/main/lib/db/CDatabase.c b/main/lib/db/CDatabase.c new file mode 100644 index 00000000..4976826c --- /dev/null +++ b/main/lib/db/CDatabase.c @@ -0,0 +1,206 @@ +/*************************************************************************** + + CDatabase.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDATABASE_C + +#include "main.h" + +#include "CDatabase.h" + + +static int valid_database(CDATABASE *_object) +{ + return !THIS->conn || !THIS->conn->db.handle; +} + +static bool check_database(CCONNECTION *conn, const char *name, bool must_exist) +{ + bool exist = conn->driver->Database.Exist(&conn->db, (char *)name); + + if (must_exist) + { + if (!exist) + { + GB.Error("Unknown database: &1", name); + return TRUE; + } + } + else + { + if (exist) + { + GB.Error("Database already exists: &1", name); + return TRUE; + } + } + + return FALSE; +} + + +void *CDATABASE_get(CCONNECTION *conn, const char *name) +{ + CDATABASE *_object; + + if (check_database(conn, name, TRUE)) + return NULL; + + _object = GB.New(GB.FindClass("Database"), NULL, NULL); + THIS->conn = conn; + THIS->driver = conn->driver; + THIS->name = GB.NewZeroString(name); + return THIS; +} + +int CDATABASE_exist(CCONNECTION *conn, const char *name) +{ + return conn->driver->Database.Exist(&conn->db, (char *)name); +} + +void CDATABASE_list(CCONNECTION *conn, char ***list) +{ + conn->driver->Database.List(&conn->db, list); +} + +void CDATABASE_release(CCONNECTION *conn, void *_object) +{ + THIS->conn = NULL; +} + + +/*************************************************************************** + + Database + +***************************************************************************/ + +BEGIN_METHOD_VOID(CDATABASE_free) + + if (!valid_database(THIS)) + GB_SubCollectionRemove(THIS->conn->databases, THIS->name, 0); + GB.FreeString(&THIS->name); + +END_METHOD + + +BEGIN_PROPERTY(CDATABASE_name) + + GB.ReturnString(THIS->name); + +END_PROPERTY + + +BEGIN_PROPERTY(CDATABASE_system) + + GB.ReturnBoolean(THIS->driver->Database.IsSystem(&THIS->conn->db, THIS->name)); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CDATABASE_delete) + + THIS->conn->driver->Database.Delete(&THIS->conn->db, THIS->name); + +END_METHOD + + +BEGIN_PROPERTY(CDATABASE_connection) + + GB.ReturnObject(THIS->conn); + +END_PROPERTY + + + + +GB_DESC CDatabaseDesc[] = +{ + GB_DECLARE("Database", sizeof(CDATABASE)), + GB_NOT_CREATABLE(), + GB_HOOK_CHECK(valid_database), + + GB_METHOD("_free", NULL, CDATABASE_free, NULL), + + GB_METHOD("Delete", NULL, CDATABASE_delete, NULL), + + GB_PROPERTY_READ("Name", "s", CDATABASE_name), + GB_PROPERTY_READ("System", "b", CDATABASE_system), + GB_PROPERTY_READ("Connection", "Connection", CDATABASE_connection), + + GB_END_DECLARE +}; + + + +/*************************************************************************** + + .Connection.Databases + +***************************************************************************/ + +#undef THIS +#define THIS ((CSUBCOLLECTION *)_object) + +BEGIN_METHOD(CDATABASE_add, GB_STRING name) + + CCONNECTION *conn = GB_SubCollectionContainer(THIS); + char *name = GB.ToZeroString(ARG(name)); + + if (DB_CheckNameWith(name, "database", conn->db.db_name_char)) + return; + + if (check_database(conn, name, FALSE)) + return; + + conn->driver->Database.Create(&conn->db, name); + +END_METHOD + + +BEGIN_METHOD(CDATABASE_remove, GB_STRING name) + + CCONNECTION *conn = GB_SubCollectionContainer(THIS); + char *name = GB.ToZeroString(ARG(name)); + + GB_SubCollectionRemove(THIS, STRING(name), LENGTH(name)); + + if (check_database(conn, name, TRUE)) + return; + + conn->driver->Database.Delete(&conn->db, name); + +END_METHOD + + + +GB_DESC CConnectionDatabasesDesc[] = +{ + GB_DECLARE(".Connection.Databases", 0), GB_INHERITS(".SubCollection"), + + GB_METHOD("Add", NULL, CDATABASE_add, "(Name)s"), + GB_METHOD("Remove", NULL, CDATABASE_remove, "(Name)s"), + + GB_END_DECLARE +}; + + diff --git a/main/lib/db/CDatabase.h b/main/lib/db/CDatabase.h new file mode 100644 index 00000000..487c666d --- /dev/null +++ b/main/lib/db/CDatabase.h @@ -0,0 +1,55 @@ +/*************************************************************************** + + CDatabase.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDATABASE_H +#define __CDATABASE_H + +#include "gambas.h" +#include "gb.db.h" +#include "CConnection.h" + +#ifndef __CDATABASE_C +extern GB_DESC CConnectionDatabasesDesc[]; +extern GB_DESC CDatabaseDesc[]; +#else + +#define THIS ((CDATABASE *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + DB_DRIVER *driver; + CCONNECTION *conn; + char *name; + } + CDATABASE; + +void *CDATABASE_get(CCONNECTION *conn, const char *key); +int CDATABASE_exist(CCONNECTION *conn, const char *key); +void CDATABASE_list(CCONNECTION *conn, char ***list); +void CDATABASE_release(CCONNECTION *conn, void *_object); + +#endif + diff --git a/main/lib/db/CField.c b/main/lib/db/CField.c new file mode 100644 index 00000000..0aeb7080 --- /dev/null +++ b/main/lib/db/CField.c @@ -0,0 +1,329 @@ +/*************************************************************************** + + CField.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CFIELD_C + +#include "main.h" + +#include "CField.h" + + +static int valid_field(CFIELD *_object) +{ + return !THIS->table || !THIS->table->conn || !THIS->table->conn->db.handle; +} + +static bool exist_field(CTABLE *table, const char *name) +{ + DB_FIELD *fp; + + if (!name || !*name) + return FALSE; + + if (table->create) + { + for (fp = table->new_fields; fp; fp = fp->next) + { + if (!strcasecmp(fp->name, name)) + return TRUE; + } + + return FALSE; + } + else + return table->driver->Field.Exist(&table->conn->db, table->name, (char *)name); +} + +static bool check_field(CTABLE *table, char *name, bool must_exist) +{ + bool exist = exist_field(table, name); + + if (must_exist) + { + if (!exist) + { + GB.Error("Unknown field: &1.&2", table->name, name); + return TRUE; + } + } + else + { + if (exist) + { + GB.Error("Field already exists: &1.&2", table->name, name); + return TRUE; + } + } + + return FALSE; +} + + +static bool check_type(int type) +{ + if (type == GB_T_BOOLEAN + || type == GB_T_INTEGER + || type == GB_T_LONG + || type == GB_T_FLOAT + || type == GB_T_DATE + || type == GB_T_STRING + || type == DB_T_SERIAL + || type == DB_T_BLOB) + return FALSE; + + GB.Error("Bad field type"); + return TRUE; +} + + +static CFIELD *make_field(CTABLE *table, const char *name, bool must_exist) +{ + CFIELD *_object; + + if (check_field(table, (char *)name, must_exist)) + return NULL; + + _object = GB.New(GB.FindClass("Field"), NULL, NULL); + THIS->table = table; + THIS->driver = table->conn->driver; + THIS->name = GB.NewZeroString(name); + + return _object; +} + + +void CFIELD_free_info(DB_FIELD *info) +{ + GB.FreeString(&info->name); + GB.FreeString(&info->collation); + GB.StoreVariant(NULL, &info->def); + info->type = GB_T_NULL; + info->length = 0; +} + +static void add_new_field(CTABLE *table, DB_FIELD *field) +{ + DB_FIELD **fp; + + fp = &table->new_fields; + + for(;;) + { + if (!*fp) + { + *fp = field; + field->next = NULL; + return; + } + + fp = &((*fp)->next); + } +} + + +void *CFIELD_get(CTABLE *table, const char *name) +{ + CFIELD *field = make_field(table, name, TRUE); + table->driver->Field.Info(&table->conn->db, table->name, (char *)name, &field->info); + return field; +} + + +int CFIELD_exist(CTABLE *table, const char *name) +{ + return exist_field(table, name); +} + +void CFIELD_list(CTABLE *table, char ***list) +{ + table->driver->Field.List(&table->conn->db, table->name, list); +} + +void CFIELD_release(CTABLE *table, void *_object) +{ + THIS->table = NULL; +} + + +/*************************************************************************** + + Field + +***************************************************************************/ + +BEGIN_METHOD_VOID(Field_free) + + if (!valid_field(THIS)) + GB_SubCollectionRemove(THIS->table->fields, THIS->name, 0); + + GB.FreeString(&THIS->name); + + CFIELD_free_info(&THIS->info); + +END_METHOD + + + +/*BEGIN_METHOD_VOID(CFIELD_delete) + + THIS->table->conn->driver->User.Delete(THIS->conn->handle, THIS->name); + +END_METHOD*/ + + +BEGIN_PROPERTY(Field_Name) + + GB.ReturnString(THIS->name); + +END_PROPERTY + + +BEGIN_PROPERTY(Field_Type) + + GB.ReturnInteger(THIS->info.type); + +END_PROPERTY + + +BEGIN_PROPERTY(Field_Length) + + GB.ReturnInteger(THIS->info.length); + +END_PROPERTY + + +BEGIN_PROPERTY(Field_Default) + + GB.ReturnVariant(&THIS->info.def); + +END_PROPERTY + + +BEGIN_PROPERTY(Field_Table) + + GB.ReturnObject(THIS->table); + +END_PROPERTY + + +BEGIN_PROPERTY(Field_Collation) + + GB.ReturnString(THIS->info.collation); + +END_PROPERTY + + +GB_DESC CFieldDesc[] = +{ + GB_DECLARE("Field", sizeof(CFIELD)), + GB_NOT_CREATABLE(), + GB_HOOK_CHECK(valid_field), + + GB_METHOD("_free", NULL, Field_free, NULL), + + //GB_METHOD("Delete", NULL, CFIELD_delete, NULL), + + GB_PROPERTY_READ("Name", "s", Field_Name), + GB_PROPERTY_READ("Type", "i", Field_Type), + GB_PROPERTY_READ("Length", "i", Field_Length), + GB_PROPERTY_READ("Default", "v", Field_Default), + GB_PROPERTY_READ("Table", "Table", Field_Table), + GB_PROPERTY_READ("Collation", "s", Field_Collation), + + GB_END_DECLARE +}; + + +/*************************************************************************** + + .Table.Fields + +***************************************************************************/ + +#undef THIS +#define THIS ((CSUBCOLLECTION *)_object) + +BEGIN_METHOD(Field_Add, GB_STRING name; GB_INTEGER type; GB_INTEGER length; GB_VARIANT def; GB_STRING collation) + + CTABLE *table = GB_SubCollectionContainer(THIS); + char *name = GB.ToZeroString(ARG(name)); + DB_FIELD new_field, *info; + + if (!table->create) + { + GB.Error("Table already exists"); + return; + } + + if (DB_CheckName(name, "field")) + return; + + if (check_field(table, name, FALSE)) + return; + + new_field.next = NULL; + + new_field.type = VARG(type); + if (check_type(new_field.type)) + return; + + new_field.length = VARGOPT(length, 0); + if (new_field.length < 0) + new_field.length = 0; + else if (new_field.length > 65535) + new_field.length = 65535; + + GB.Alloc(POINTER(&info), sizeof(DB_FIELD)); + + info->next = NULL; + info->type = new_field.type; + info->length = new_field.length; + + info->def.type = GB_T_NULL; + if (!MISSING(def)) + GB.StoreVariant(ARG(def), &info->def); + + info->name = GB.NewString(STRING(name), LENGTH(name)); + //DB_LowerString(info->name); + + if (info->type == GB_T_STRING && !MISSING(collation) && LENGTH(collation) > 0) + info->collation = GB.NewString(STRING(collation), LENGTH(collation)); + else + info->collation = NULL; + + add_new_field(table, info); + +END_METHOD + + +GB_DESC CTableFieldsDesc[] = +{ + GB_DECLARE(".Table.Fields", 0), GB_INHERITS(".SubCollection"), + + GB_METHOD("Add", NULL, Field_Add, "(Name)s(Type)i[(Length)i(Default)v(Collation)s]"), + //GB_METHOD("Remove", NULL, CFIELD_remove, "(Name)s"), + + GB_END_DECLARE +}; + + diff --git a/main/lib/db/CField.h b/main/lib/db/CField.h new file mode 100644 index 00000000..5b112bac --- /dev/null +++ b/main/lib/db/CField.h @@ -0,0 +1,58 @@ +/*************************************************************************** + + CField.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CFIELD_H +#define __CFIELD_H + +#include "gambas.h" +#include "gb.db.h" +#include "CTable.h" + +#ifndef __CFIELD_C +extern GB_DESC CTableFieldsDesc[]; +extern GB_DESC CFieldDesc[]; +#else + +#define THIS ((CFIELD *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + DB_DRIVER *driver; + CTABLE *table; + char *name; + DB_FIELD info; + } + CFIELD; + +void *CFIELD_get(CTABLE *table, const char *name); +int CFIELD_exist(CTABLE *table, const char *name); +void CFIELD_list(CTABLE *table, char ***list); +void CFIELD_release(CTABLE *table, void *_object); + +void CFIELD_free_info(DB_FIELD *info); + +#endif + diff --git a/main/lib/db/CIndex.c b/main/lib/db/CIndex.c new file mode 100644 index 00000000..321ce1f4 --- /dev/null +++ b/main/lib/db/CIndex.c @@ -0,0 +1,274 @@ +/*************************************************************************** + + CIndex.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CINDEX_C + +#include "main.h" + +#include "CIndex.h" + + +static int valid_index(CINDEX *_object) +{ + return !THIS->table || !THIS->table->conn || !THIS->table->conn->db.handle; +} + + +static bool exist_index(CTABLE *table, const char *name) +{ + if (!name || !*name) + return FALSE; + + return table->driver->Index.Exist(&table->conn->db, table->name, (char *)name); +} + +static bool check_index(CTABLE *table, const char *name, bool must_exist) +{ + bool exist = exist_index(table, name); + + if (must_exist) + { + if (!exist) + { + GB.Error("Unknown index: &1.&2", table->name, name); + return TRUE; + } + } + else + { + if (exist) + { + GB.Error("Index already exists: &1.&2", table->name, name); + return TRUE; + } + } + + return FALSE; +} + + +static CINDEX *make_index(CTABLE *table, const char *name, bool must_exist) +{ + CINDEX *_object; + + if (check_index(table, name, must_exist)) + return NULL; + + _object = GB.New(GB.FindClass("Index"), NULL, NULL); + THIS->table = table; + THIS->driver = table->conn->driver; + THIS->name = GB.NewZeroString(name); + + return _object; +} + + +void *CINDEX_get(CTABLE *table, const char *name) +{ + CINDEX *index = make_index(table, name, TRUE); + table->driver->Index.Info(&table->conn->db, table->name, (char *)name, &index->info); + return index; +} + + +int CINDEX_exist(CTABLE *table, const char *name) +{ + return exist_index(table, name); +} + + +void CINDEX_list(CTABLE *table, char ***list) +{ + table->driver->Index.List(&table->conn->db, table->name, list); +} + +void CINDEX_release(CTABLE *table, void *_object) +{ + THIS->table = NULL; +} + + +/*************************************************************************** + + Index + +***************************************************************************/ + +BEGIN_METHOD_VOID(CINDEX_free) + + if (!valid_index(THIS)) + GB_SubCollectionRemove(THIS->table->indexes, THIS->name, 0); + + GB.FreeString(&THIS->name); + + GB.FreeString(&THIS->info.name); + GB.FreeString(&THIS->info.fields); + THIS->info.unique = FALSE; + +END_METHOD + + + +BEGIN_PROPERTY(CINDEX_name) + + GB.ReturnString(THIS->name); + +END_PROPERTY + + +BEGIN_PROPERTY(CINDEX_fields) + + GB_ARRAY array; + char *fields; + char *name; + + fields = GB.NewZeroString(THIS->info.fields); + GB.Array.New(&array, GB_T_STRING, 0); + + name = strtok(fields, ","); + while (name) + { + *((char **)GB.Array.Add(array)) = GB.NewZeroString(name); + name = strtok(NULL, ","); + } + + GB.FreeString(&fields); + GB.ReturnObject(array); + +END_PROPERTY + + +BEGIN_PROPERTY(CINDEX_unique) + + GB.ReturnBoolean(THIS->info.unique); + +END_PROPERTY + + +BEGIN_PROPERTY(CINDEX_primary) + + GB.ReturnBoolean(THIS->info.primary); + +END_PROPERTY + + +BEGIN_PROPERTY(CINDEX_table) + + GB.ReturnObject(THIS->table); + +END_PROPERTY + + +GB_DESC CIndexDesc[] = +{ + GB_DECLARE("Index", sizeof(CINDEX)), + GB_NOT_CREATABLE(), + GB_HOOK_CHECK(valid_index), + + GB_METHOD("_free", NULL, CINDEX_free, NULL), + + GB_PROPERTY_READ("Name", "s", CINDEX_name), + GB_PROPERTY_READ("Unique", "b", CINDEX_unique), + GB_PROPERTY_READ("Primary", "b", CINDEX_primary), + GB_PROPERTY_READ("Fields", "String[]", CINDEX_fields), + + GB_PROPERTY_READ("Table", "Table", CINDEX_table), + + GB_END_DECLARE +}; + + +/*************************************************************************** + + .Table.Indexes + +***************************************************************************/ + +#undef THIS +#define THIS ((CSUBCOLLECTION *)_object) + +BEGIN_METHOD(CINDEX_add, GB_STRING name; GB_OBJECT fields; GB_BOOLEAN unique) + + CTABLE *table = GB_SubCollectionContainer(THIS); + char *name = GB.ToZeroString(ARG(name)); + DB_INDEX info; + int i; + GB_ARRAY fields; + char *field; + + if (DB_CheckNameWith(name, "index", ".")) + return; + + if (check_index(table, name, FALSE)) + return; + + info.name = name; + + fields = (GB_ARRAY)VARG(fields); + q_init(); + for (i = 0; i < GB.Array.Count(fields); i++) + { + field = *(char **)GB.Array.Get(fields, i); + + if (i > 0) + q_add(","); + + q_add(table->driver->GetQuote()); + q_add(field); + q_add(table->driver->GetQuote()); + } + + info.fields = q_steal(); + info.unique = VARGOPT(unique, FALSE); + + table->driver->Index.Create(&table->conn->db, table->name, name, &info); + + GB.FreeString(&info.fields); + +END_METHOD + + +BEGIN_METHOD(CINDEX_remove, GB_STRING name) + + CTABLE *table = GB_SubCollectionContainer(THIS); + char *name = GB.ToZeroString(ARG(name)); + + if (check_index(table, name, TRUE)) + return; + + table->driver->Index.Delete(&table->conn->db, table->name, name); + +END_METHOD + + +GB_DESC CTableIndexesDesc[] = +{ + GB_DECLARE(".Table.Indexes", 0), GB_INHERITS(".SubCollection"), + + GB_METHOD("Add", NULL, CINDEX_add, "(Name)s(Fields)String[];[(Unique)b]"), + GB_METHOD("Remove", NULL, CINDEX_remove, "(Name)s"), + + GB_END_DECLARE +}; + + diff --git a/main/lib/db/CIndex.h b/main/lib/db/CIndex.h new file mode 100644 index 00000000..f1779846 --- /dev/null +++ b/main/lib/db/CIndex.h @@ -0,0 +1,56 @@ +/*************************************************************************** + + CIndex.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CINDEX_H +#define __CINDEX_H + +#include "gambas.h" +#include "gb.db.h" +#include "CTable.h" + +#ifndef __CINDEX_C +extern GB_DESC CTableIndexesDesc[]; +extern GB_DESC CIndexDesc[]; +#else + +#define THIS ((CINDEX *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + DB_DRIVER *driver; + CTABLE *table; + char *name; + DB_INDEX info; + } + CINDEX; + +void *CINDEX_get(CTABLE *table, const char *name); +int CINDEX_exist(CTABLE *table, const char *name); +void CINDEX_list(CTABLE *table, char ***list); +void CINDEX_release(CTABLE *table, void *_object); + +#endif + diff --git a/main/lib/db/CResult.c b/main/lib/db/CResult.c new file mode 100644 index 00000000..f027a541 --- /dev/null +++ b/main/lib/db/CResult.c @@ -0,0 +1,998 @@ +/*************************************************************************** + + CResult.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CRESULT_C + +#include "main.h" + +#include "deletemap.h" +#include "CField.h" +#include "CResultField.h" +#include "CResult.h" + +GB_CLASS CLASS_Blob; + +static CBLOB *make_blob(CRESULT *result, int field); +static void set_blob(CBLOB *_object, char *data, int length); + + +static SUBCOLLECTION_DESC _fields_desc = +{ + ".Result.Fields", + (void *)CRESULTFIELD_get, + (void *)CRESULTFIELD_exist, + (void *)NULL, + (void *)CRESULTFIELD_release +}; + + +static int check_result(CRESULT *_object) +{ + return (THIS->conn->db.handle == NULL); +} + +static bool check_available(CRESULT *_object) +{ + if (!THIS->available) + { + GB.Error("Result is not available"); + return TRUE; + } + else + return FALSE; +} + + +static void init_buffer(CRESULT *_object) +{ + int i; + + if (THIS->info.nfield == 0) + return; + + GB.Alloc(POINTER(&THIS->buffer), sizeof(GB_VARIANT_VALUE) * THIS->info.nfield); + BARRAY_create(&THIS->changed, THIS->info.nfield); + BARRAY_clear_all(THIS->changed, THIS->info.nfield); + + for (i = 0; i < THIS->info.nfield; i++) + THIS->buffer[i].type = GB_T_NULL; +} + +static void void_buffer(CRESULT *_object) +{ + int i; + + //fprintf(stderr, "void_buffer\n"); + + if (THIS->info.nfield == 0) + return; + + for (i = 0; i < THIS->info.nfield; i++) + GB.StoreVariant(NULL, &THIS->buffer[i]); + + BARRAY_clear_all(THIS->changed, THIS->info.nfield); +} + + +static void release_buffer(CRESULT *_object) +{ + if (THIS->buffer) + { + void_buffer(THIS); + GB.Free(POINTER(&THIS->buffer)); + BARRAY_delete(&THIS->changed); + } +} + + +static bool load_buffer(CRESULT *_object, int vpos) +{ + int i, ind; + int pos; + int result; + + if (vpos == THIS->pos) + return FALSE; + + DB_CurrentDatabase = &THIS->conn->db; + + if (THIS->count < 0) + { + if (vpos != (THIS->pos + 1)) + { + GB.Error("Result is forward only"); + return TRUE; + } + } + else + { + if (vpos < 0 || vpos >= THIS->count || THIS->info.nfield == 0) + { + THIS->pos = -1; + THIS->available = FALSE; + return TRUE; + } + } + + pos = DELETE_MAP_virtual_to_real(THIS->dmap, vpos); + + //fprintf(stderr, "Result %p: Loading real %ld\n", THIS, pos); + + void_buffer(THIS); + + if (THIS->handle) + { + result = THIS->driver->Result.Fill(&THIS->conn->db, THIS->handle, pos, THIS->buffer, (pos > 0) && (pos == (DELETE_MAP_virtual_to_real(THIS->dmap, THIS->pos) + 1))); + + if (result == DB_ERROR) + return TRUE; + else if (result == DB_NO_DATA) + { + THIS->pos = -1; + THIS->available = FALSE; + return TRUE; + } + + if (THIS->mode == RESULT_EDIT) + { + q_init(); + + for (i = 0; i < THIS->info.nindex; i++) + { + ind = THIS->info.index[i]; + if (i > 0) q_add(" AND "); + q_add(THIS->driver->GetQuote()); + q_add(THIS->info.field[ind].name); + q_add(THIS->driver->GetQuote()); + if (THIS->buffer[ind].type == GB_T_NULL) + q_add(" IS NULL"); + else + { + q_add(" = "); + DB_FormatVariant(THIS->driver, &THIS->buffer[ind], q_add_length); + } + } + + GB.FreeString(&THIS->edit); + THIS->edit = q_steal(); + } + } + + THIS->pos = vpos; + THIS->available = TRUE; + return FALSE; +} + + +static void reload_buffer(CRESULT *_object) +{ + int pos = THIS->pos; + THIS->pos = -1; + load_buffer(THIS, pos); +} + + +static void table_release(DB_INFO *info) +{ + int i; + + if (info->table) + GB.FreeString(&info->table); + + if (info->field) + { + for (i = 0; i < info->nfield; i++) + CFIELD_free_info(&info->field[i]); + + GB.Free(POINTER(&info->field)); + } + + if (info->index) + GB.Free(POINTER(&info->index)); +} + + +static GB_TYPE get_field_type(CRESULT *_object, int field) +{ + GB_TYPE type; + + if (THIS->info.field) + type = THIS->info.field[field].type; + else + type = THIS->driver->Result.Field.Type(THIS->handle, field); + + //fprintf(stderr, "get_field_type: %d -> %ld\n", field, type); + return type; +} + + +CRESULT *DB_MakeResult(CCONNECTION *conn, int mode, char *table_temp, char *query) +{ + CRESULT *_object; + DB_RESULT res; + char *duplicate; + char *token; + const char *error = NULL; + char *arg; + char *table; + + switch (mode) + { + case RESULT_FIND: + + if (conn->driver->Exec(&conn->db, query, &res, "Query failed: &1")) + return NULL; + + break; + + case RESULT_CREATE: + + res = NULL; + break; + + case RESULT_EDIT: + + if (conn->driver->Exec(&conn->db, query, &res, "Query failed: &1")) + return NULL; + + break; + + case RESULT_DELETE: + + conn->driver->Exec(&conn->db, query, NULL, "Query failed: &1"); + return NULL; + } + + _object = GB.New(GB.FindClass("Result"), NULL, NULL); + + THIS->conn = conn; + GB.Ref(conn); + + THIS->driver = conn->driver; + THIS->available = FALSE; + THIS->mode = mode; + THIS->handle = res; + THIS->pos = -1; + THIS->dmap = NULL; + + // table must be copied because it can be a temporary string! + table = GB.NewZeroString(table_temp); + + switch (mode) + { + case RESULT_FIND: + + THIS->driver->Result.Init(THIS->handle, &THIS->info, &THIS->count); + break; + + case RESULT_CREATE: + + if (THIS->driver->Table.Init(&conn->db, table, &THIS->info)) + goto ERROR; + + THIS->count = 1; + break; + + case RESULT_EDIT: + + THIS->driver->Result.Init(THIS->handle, &THIS->info, &THIS->count); + + if (THIS->driver->Table.Init(&conn->db, table, &THIS->info)) + goto ERROR; + + if (THIS->driver->Table.Index(&conn->db, table, &THIS->info)) + { + error = "Table '&1' has no primary key"; + arg = table; + goto ERROR; + } + + break; + } + + init_buffer(THIS); + load_buffer(THIS, 0); + + GB.FreeString(&table); + return THIS; + +ERROR: + + if (!error) + { + if (strchr(table, (int)',') == NULL) + { + arg = table; + if (!THIS->driver->Table.Exist(&conn->db, table)) + error = "Unknown table: &1"; + else + error = "Cannot read information about table &1"; + } + else + { + duplicate = GB.NewZeroString(table); + token = strtok(duplicate,","); + do { + arg = token; + if (!THIS->driver->Table.Exist(&conn->db, token)) + error = "Unknown table: &1"; + else + error = "Cannot read information about table '&1'"; + } + while ((token = strtok(NULL, ".")) != NULL); + GB.FreeString(&duplicate); + } + } + + GB.Free(POINTER(&_object)); + GB.Error(error, arg); + GB.FreeString(&table); + + return NULL; +} + + +BEGIN_METHOD_VOID(Result_free) + + release_buffer(THIS); + + if (THIS->mode != RESULT_CREATE) + THIS->driver->Result.Release(THIS->handle, &THIS->info); + + if (THIS->mode != RESULT_FIND) + table_release(&THIS->info); + + if (THIS->edit) + GB.FreeString(&THIS->edit); + + DELETE_MAP_free(&THIS->dmap); + + GB.Unref(POINTER(&THIS->conn)); + GB.Unref(POINTER(&THIS->fields)); + +END_METHOD + + +BEGIN_PROPERTY(Result_Count) + + GB.ReturnInteger(THIS->count); + +END_PROPERTY + + +BEGIN_PROPERTY(Result_Max) + + GB.ReturnInteger(THIS->count - 1); + +END_PROPERTY + + +BEGIN_PROPERTY(Result_Index) + + GB.ReturnInteger(THIS->pos); + +END_PROPERTY + + +BEGIN_PROPERTY(Result_Available) + + GB.ReturnBoolean(THIS->available); + +END_PROPERTY + + +static void check_blob(CRESULT *_object, int field) +{ + GB_VARIANT val; + + //fprintf(stderr, "check_blob: %d\n", field); + + if (THIS->buffer[field].type == GB_T_NULL) + { + val.type = GB_T_VARIANT; + val.value.type = (GB_TYPE)CLASS_Blob; + val.value.value._object = make_blob(THIS, field); + + GB.StoreVariant(&val, &THIS->buffer[field]); + } +} + +BEGIN_METHOD(Result_get, GB_STRING field) + + int index; + GB_TYPE type; + + if (check_available(THIS)) + return; + + index = CRESULTFIELD_find(THIS, GB.ToZeroString(ARG(field)), TRUE); + if (index < 0) + return; + + type = get_field_type(THIS, index); + + if (type == DB_T_BLOB) + check_blob(THIS, index); + + GB.ReturnVariant(&THIS->buffer[index]); + +END_METHOD + + +BEGIN_METHOD(Result_GetAll, GB_STRING field) + + int index; + int pos; + GB_TYPE type, atype; + GB_ARRAY result; + GB_VARIANT_VALUE *val; + + index = CRESULTFIELD_find(THIS, GB.ToZeroString(ARG(field)), TRUE); + if (index < 0) + return; + + atype = type = get_field_type(THIS, index); + if (atype == DB_T_SERIAL) + atype = GB_T_LONG; + else if (atype == DB_T_BLOB) + atype = GB_T_OBJECT; + + GB.Array.New(POINTER(&result), atype, 0); + + pos = THIS->pos; + load_buffer(THIS, 0); + + while (THIS->available) + { + if (type == DB_T_BLOB) + check_blob(THIS, index); + + val = &THIS->buffer[index]; + + switch (atype) + { + case GB_T_BOOLEAN: *(char *)GB.Array.Add(result) = val->value._boolean; break; + case GB_T_INTEGER: *(int *)GB.Array.Add(result) = val->value._integer; break; + case GB_T_LONG: *(int64_t *)GB.Array.Add(result) = val->value._long; break; + case GB_T_FLOAT: *(double *)GB.Array.Add(result) = val->value._float; break; + case GB_T_DATE: *(GB_DATE_VALUE *)GB.Array.Add(result) = val->value._date; break; + + case GB_T_STRING: + if (val->type == GB_T_CSTRING) + *(char **)GB.Array.Add(result) = GB.NewString(val->value._string, strlen(val->value._string)); + else + *(char **)GB.Array.Add(result) = GB.RefString(val->value._string); + break; + + case GB_T_OBJECT: *(void **)GB.Array.Add(result) = val->value._object; GB.Ref(val->value._object); break; + } + + load_buffer(THIS, THIS->pos + 1); + } + + if (THIS->count >= 0) + load_buffer(THIS, pos); + + GB.ReturnObject(result); + +END_METHOD + + +BEGIN_METHOD(Result_put, GB_VARIANT value; GB_STRING field) + + int index; + GB_TYPE type; + + if (check_available(THIS)) + return; + + if (THIS->mode == RESULT_FIND) + { + GB.Error("Result is read-only"); + return; + } + + index = CRESULTFIELD_find(THIS, GB.ToZeroString(ARG(field)), TRUE); + if (index < 0) + return; + + type = get_field_type(THIS, index); + + if (type == DB_T_SERIAL) + { + //GB.Error("Read-only field"); + return; + } + + if (type == DB_T_BLOB) + { + check_blob(THIS, index); + + if (VARG(value).type == (GB_TYPE)CLASS_Blob) + { + CBLOB *src = VARG(value).value._object; + set_blob((CBLOB *)THIS->buffer[index].value._object, src->data, src->length); + } + else + { + GB_STRING *str = (GB_STRING *)(void *)ARG(value); + + if (GB.Conv((GB_VALUE *)(void *)ARG(value), GB_T_STRING)) + return; + + set_blob((CBLOB *)THIS->buffer[index].value._object, str->value.addr + str->value.start, str->value.len); + } + + BARRAY_set(THIS->changed, index); + return; + } + + if (VARG(value).type != GB_T_NULL && VARG(value).type != type) + { + if (GB.Conv((GB_VALUE *)(void *)ARG(value), THIS->info.field[index].type)) + { + GB.Error("Type mismatch"); + return; + } + + GB.Conv((GB_VALUE *)(void *)ARG(value), GB_T_VARIANT); + } + + GB.StoreVariant(ARG(value), &THIS->buffer[index]); + BARRAY_set(THIS->changed, index); + +END_METHOD + +#if 0 +BEGIN_METHOD(CRESULT_copy, GB_OBJECT result) + + CRESULT *result = (CRESULT *)VARG(result); + int index; + + if (THIS->mode == RESULT_FIND) + { + GB.Error("Result is read-only"); + return; + } + + for (index = 0; index < + + index = find_field(THIS, GB.ToZeroString(ARG(field))); + if (index < 0) + return; + + if (VARG(value).type != GB_T_NULL && VARG(value).type != THIS->info.types[index]) + /*{ + GB.Error("Type mismatch"); + return; + }*/ + { + if (GB.Conv((GB_VALUE *)ARG(value), THIS->info.types[index])) + return; + + GB.Conv((GB_VALUE *)ARG(value), GB_T_VARIANT); + } + + GB.StoreVariant(ARG(value), &THIS->buffer[index]); + +END_METHOD +#endif + +BEGIN_METHOD_VOID(Result_MoveFirst) + + GB.ReturnBoolean(load_buffer(THIS, 0)); + +END_METHOD + + +BEGIN_METHOD_VOID(Result_MoveLast) + + if (THIS->count < 0) + GB.Error("Result is forward only"); + else + GB.ReturnBoolean(load_buffer(THIS, THIS->count - 1)); + +END_METHOD + + +BEGIN_METHOD_VOID(Result_MovePrevious) + + GB.ReturnBoolean(load_buffer(THIS, THIS->pos - 1)); + +END_METHOD + + +BEGIN_METHOD_VOID(Result_MoveNext) + + GB.ReturnBoolean(load_buffer(THIS, THIS->pos + 1)); + +END_METHOD + + +BEGIN_METHOD(Result_MoveTo, GB_INTEGER pos) + + GB.ReturnBoolean(load_buffer(THIS, VARG(pos))); + +END_METHOD + + +BEGIN_METHOD_VOID(Result_next) + + int *pos = (int *)GB.GetEnum(); + + if (!load_buffer(THIS, *pos)) + (*pos)++; + else + GB.StopEnum(); + +END_METHOD + +BEGIN_METHOD_VOID(Result_Update) + + int i; + bool comma; + DB_INFO *info = &THIS->info; + + if (check_available(THIS)) + return; + + DB_CurrentDatabase = &THIS->conn->db; + + q_init(); + + switch(THIS->mode) + { + case RESULT_CREATE: + + if (BARRAY_is_void(THIS->changed, THIS->info.nfield)) + break; + + q_add("INSERT INTO "); + q_add(DB_GetQuotedTable(THIS->driver, DB_CurrentDatabase, info->table, -1)); + q_add(" ( "); + + comma = FALSE; + for (i = 0; i < info->nfield; i++) + { + if (THIS->buffer[i].type == GB_T_NULL) + continue; + if (!BARRAY_test(THIS->changed, i)) + continue; + if (comma) q_add(", "); + q_add(THIS->driver->GetQuote()); + q_add(info->field[i].name); + q_add(THIS->driver->GetQuote()); + comma = TRUE; + } + + if (!comma) + { + q_add(THIS->driver->GetQuote()); + q_add(info->field[0].name); + q_add(THIS->driver->GetQuote()); + } + + q_add(" ) VALUES ( "); + + comma = FALSE; + for (i = 0; i < info->nfield; i++) + { + if (THIS->buffer[i].type == GB_T_NULL) + continue; + if (!BARRAY_test(THIS->changed, i)) + continue; + if (comma) q_add(", "); + DB_FormatVariant(THIS->driver, &THIS->buffer[i], q_add_length); + comma = TRUE; + } + + if (!comma) + { + DB_FormatVariant(THIS->driver, &THIS->buffer[0], q_add_length); + } + + q_add(" )"); + + if (!THIS->driver->Exec(&THIS->conn->db, q_get(), NULL, "Cannot create record: &1")) + void_buffer(THIS); + + break; + + case RESULT_EDIT: + + if (BARRAY_is_void(THIS->changed, THIS->info.nfield)) + break; + + q_add("UPDATE "); + q_add(DB_GetQuotedTable(THIS->driver, DB_CurrentDatabase, info->table, -1)); + q_add(" SET "); + + comma = FALSE; + for (i = 0; i < info->nfield; i++) + { + if (!BARRAY_test(THIS->changed, i)) + continue; + if (comma) q_add(", "); + q_add(THIS->driver->GetQuote()); + q_add(THIS->info.field[i].name); + q_add(THIS->driver->GetQuote()); + q_add(" = "); + DB_FormatVariant(THIS->driver, &THIS->buffer[i], q_add_length); + comma = TRUE; + } + + q_add(" WHERE "); + q_add(THIS->edit); + + THIS->driver->Exec(&THIS->conn->db, q_get(), NULL, "Cannot modify record: &1"); + + break; + + default: + + GB.Error("Result is read-only"); + break; + } + + BARRAY_clear_all(THIS->changed, THIS->info.nfield); + +END_METHOD + + +BEGIN_METHOD(Result_Delete, GB_BOOLEAN keep) + + DB_INFO *info = &THIS->info; + int *pos; + void *save_enum; + + if (check_available(THIS)) + return; + + q_init(); + + switch(THIS->mode) + { + case RESULT_CREATE: + + void_buffer(THIS); + break; + + case RESULT_EDIT: + + q_add("DELETE FROM "); + q_add(DB_GetQuotedTable(THIS->driver, DB_CurrentDatabase, info->table, -1)); + q_add(" WHERE "); + q_add(THIS->edit); + + THIS->driver->Exec(&THIS->conn->db, q_get(), NULL, "Cannot delete record: &1"); + + if (!VARGOPT(keep, FALSE)) + { + DELETE_MAP_add(&THIS->dmap, THIS->pos); + THIS->count--; + reload_buffer(THIS); + + save_enum = GB.BeginEnum(THIS); + while (!GB.NextEnum()) + { + pos = (int *)GB.GetEnum(); + if (*pos > THIS->pos) + (*pos)--; + } + GB.EndEnum(save_enum); + } + + break; + + default: + + GB.Error("Result is read-only"); + break; + } + +END_METHOD + + + + +BEGIN_PROPERTY(Result_Fields) + + GB_SubCollectionNew(&THIS->fields, &_fields_desc, THIS); + GB.ReturnObject(THIS->fields); + +END_PROPERTY + + +BEGIN_PROPERTY(Result_Connection) + + GB.ReturnObject(THIS->conn); + +END_PROPERTY + + +GB_DESC CResultDesc[] = +{ + GB_DECLARE("Result", sizeof(CRESULT)), GB_NOT_CREATABLE(), + + GB_HOOK_CHECK(check_result), + + GB_METHOD("_free", NULL, Result_free, NULL), + + GB_PROPERTY_READ("Count", "i", Result_Count), + GB_PROPERTY_READ("Length", "i", Result_Count), + GB_PROPERTY_READ("Available", "b", Result_Available), + GB_PROPERTY_READ("Index", "i", Result_Index), + GB_PROPERTY_READ("Max", "i", Result_Max), + + GB_METHOD("_get", "v", Result_get, "(Field)s"), + GB_METHOD("_put", NULL, Result_put, "(Value)v(Field)s"), + GB_METHOD("_next", NULL, Result_next, NULL), + + GB_METHOD("MoveFirst", "b", Result_MoveFirst, NULL), + GB_METHOD("MoveLast", "b", Result_MoveLast, NULL), + GB_METHOD("MovePrevious", "b", Result_MovePrevious, NULL), + GB_METHOD("MoveNext", "b", Result_MoveNext, NULL), + GB_METHOD("MoveTo", "b", Result_MoveTo, "(Index)i"), + + GB_METHOD("Update", NULL, Result_Update, NULL), + GB_METHOD("Delete", NULL, Result_Delete, "[(Keep)b]"), + + GB_METHOD("All", "Array", Result_GetAll, "(Field)s"), + + GB_PROPERTY_READ("Fields", ".Result.Fields", Result_Fields), + GB_PROPERTY_READ("Connection", "Connection", Result_Connection), + + GB_END_DECLARE +}; + +/** Blob *******************************************************************/ + +static bool _convert_blob(CBLOB *_object, GB_TYPE type, GB_VALUE *conv) +{ + if (BLOB) + { + switch (type) + { + case GB_T_STRING: + case GB_T_CSTRING: + conv->_string.value.addr = BLOB->data; + conv->_string.value.start = 0; + conv->_string.value.len = BLOB->length; + conv->type = GB_T_CSTRING; + return FALSE; + + default: + return TRUE; + } + } + else + return TRUE; +} + +/*static int check_blob(CBLOB *_object) +{ + return check_result(BLOB->result) || (BLOB->result->pos != BLOB->pos); +}*/ + +static CBLOB *make_blob(CRESULT *result, int field) +{ + CBLOB *_object; + + _object = GB.New(CLASS_Blob, NULL, NULL); + + //BLOB->result = result; + //GB.Ref(result); + + //BLOB->field = field; + //BLOB->pos = result->pos; + BLOB->data = NULL; + BLOB->length = 0; + BLOB->constant = TRUE; + + if (result->handle && result->pos >= 0) + { + BLOB->constant = FALSE; + result->driver->Result.Blob(result->handle, result->pos, field, BLOB); + if (BLOB->constant) + set_blob(BLOB, BLOB->data, BLOB->length); + } + + //fprintf(stderr, "make_blob: [%d] %d (%d) -> %p\n", result->pos, field, BLOB->length, BLOB); + + //GB.UnrefKeep(POINTER(&_object), FALSE); + return BLOB; +} + +static void set_blob(CBLOB *_object, char *data, int length) +{ + if (!BLOB->constant && BLOB->data) + GB.FreeString((char **)&BLOB->data); + + if (data && length) + { + BLOB->data = GB.NewString(data, length); + BLOB->constant = FALSE; + } + + BLOB->length = length; +} + +BEGIN_METHOD_VOID(Blob_init) + + CLASS_Blob = GB.FindClass("Blob"); + +END_METHOD + +BEGIN_METHOD_VOID(Blob_free) + + //GB.Unref(POINTER(&BLOB->result)); + set_blob(BLOB, NULL, 0); + +END_METHOD + +/*BEGIN_PROPERTY(CBLOB_result) + + GB.ReturnObject(BLOB->result); + +END_PROPERTY*/ + +BEGIN_PROPERTY(Blob_Data) + + if (READ_PROPERTY) + { + if (BLOB->length) + GB.ReturnConstString(BLOB->data, BLOB->length); + else + GB.ReturnVoidString(); + } + else + { + set_blob(BLOB, PSTRING(), PLENGTH()); + } + +END_PROPERTY + +BEGIN_PROPERTY(Blob_Length) + + GB.ReturnInteger(BLOB->length); + +END_PROPERTY + + +GB_DESC CBlobDesc[] = +{ + GB_DECLARE("Blob", sizeof(CBLOB)), GB_NOT_CREATABLE(), + + //GB_HOOK_CHECK(check_blob), + + GB_STATIC_METHOD("_init", NULL, Blob_init, NULL), + GB_METHOD("_free", NULL, Blob_free, NULL), + + //GB_PROPERTY_READ("Result", "Result", CBLOB_result), + GB_PROPERTY("Data", "s", Blob_Data), + GB_PROPERTY_READ("Length", "i", Blob_Length), + GB_INTERFACE("_convert", &_convert_blob), + //GB_METHOD("_unknown", "v", CBLOB_unknown, "v"), + + GB_END_DECLARE +}; diff --git a/main/lib/db/CResult.h b/main/lib/db/CResult.h new file mode 100644 index 00000000..5cfb9ff7 --- /dev/null +++ b/main/lib/db/CResult.h @@ -0,0 +1,78 @@ +/*************************************************************************** + + CResult.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CRESULT_H +#define __CRESULT_H + +#include "gambas.h" +#include "gb.db.h" +#include "deletemap.h" +#include "CDatabase.h" +#include "gb_barray.h" +#include "c_subcollection.h" + +#ifndef __CRESULT_C +extern GB_DESC CResultDesc[]; +extern GB_DESC CBlobDesc[]; +extern GB_CLASS CLASS_Blob; +#else + +#define THIS ((CRESULT *)_object) +#define BLOB ((CBLOB *)_object) + +#endif + +enum +{ + RESULT_FIND = 0, + RESULT_EDIT = 1, + RESULT_CREATE = 2, + RESULT_DELETE = 3 +}; + +typedef + struct { + GB_BASE ob; + DB_DRIVER *driver; + CCONNECTION *conn; + DB_RESULT handle; + GB_VARIANT_VALUE *buffer; + BARRAY changed; + char *edit; + DB_INFO info; + int pos; + int count; + int field; + CSUBCOLLECTION *fields; + DELETE_MAP *dmap; + unsigned available : 1; + unsigned no_seek : 1; + unsigned mode : 2; + } + CRESULT; + +#define CBLOB DB_BLOB + +CRESULT *DB_MakeResult(CCONNECTION *db, int mode, char *table, char *query); + +#endif diff --git a/main/lib/db/CResultField.c b/main/lib/db/CResultField.c new file mode 100644 index 00000000..5c1bea6f --- /dev/null +++ b/main/lib/db/CResultField.c @@ -0,0 +1,258 @@ +/*************************************************************************** + + CResultField.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CRESULTFIELD_C + +#include + +#include "main.h" + +#include "CResultField.h" + + +static int valid_result_field(CRESULTFIELD *_object) +{ + return !THIS->result || !THIS->result->conn || !THIS->result->conn->db.handle; +} + + +int CRESULTFIELD_find(CRESULT *result, const char *name, bool error) +{ + int index; + char *p; + + if (!name || !*name) + return -1; + + index = strtol(name, &p, 10); + if (*name && *p == 0) + { + if (index < 0 || index >= result->info.nfield) + { + if (error) + GB.Error("Bad field index"); + index = -1; + } + } + else + { + if (result->handle) + index = result->driver->Result.Field.Index(result->handle, (char *)name, &result->conn->db); + else + { + for (index = 0; index < result->info.nfield; index++) + { + if (strcasecmp(name, result->info.field[index].name) == 0) + break; + } + } + + if (index < 0 || index >= result->info.nfield) + { + if (error) + GB.Error("Unknown field: &1", name); + index = -1; + } + } + + return index; +} + + +static CRESULTFIELD *make_result_field(CRESULT *result, int index) +{ + CRESULTFIELD *_object; + + _object = GB.New(GB.FindClass("ResultField"), NULL, NULL); + THIS->result = result; + THIS->driver = result->conn->driver; + THIS->index = index; + + return _object; +} + + +void *CRESULTFIELD_get(CRESULT *result, const char *name) +{ + int index; + + if ((intptr_t)name >> 16) + index = CRESULTFIELD_find(result, name, TRUE); + else + index = (int)(intptr_t)name; + + if (index < 0) + return NULL; + else + return make_result_field(result, index); +} + + +int CRESULTFIELD_exist(CRESULT *result, const char *name) +{ + return CRESULTFIELD_find(result, name, FALSE) >= 0; +} + +char *CRESULTFIELD_key(CRESULT *result, int index) +{ + if (result->handle) + return result->driver->Result.Field.Name(result->handle, index); + else + return result->info.field[index].name; +} + +void CRESULTFIELD_release(CRESULT *result, void *_object) +{ + THIS->result = NULL; +} + + + + +/*************************************************************************** + + ResultField + +***************************************************************************/ + +BEGIN_METHOD_VOID(CRESULTFIELD_free) + + if (!valid_result_field(THIS)) + GB_SubCollectionRemove(THIS->result->fields, CRESULTFIELD_key(THIS->result, THIS->index), 0); + +END_METHOD + + +BEGIN_PROPERTY(CRESULTFIELD_name) + + GB.ReturnNewZeroString(CRESULTFIELD_key(THIS->result, THIS->index)); + +END_PROPERTY + + +BEGIN_PROPERTY(CRESULTFIELD_type) + + CRESULT *result = THIS->result; + + if (result->handle) + GB.ReturnInteger(result->driver->Result.Field.Type(result->handle, THIS->index)); + else + GB.ReturnInteger(result->info.field[THIS->index].type); + +END_PROPERTY + + +BEGIN_PROPERTY(CRESULTFIELD_length) + + CRESULT *result = THIS->result; + + if (result->handle) + GB.ReturnInteger(result->driver->Result.Field.Length(result->handle, THIS->index)); + else + GB.ReturnInteger(result->info.field[THIS->index].length); + +END_PROPERTY + + +/*BEGIN_PROPERTY(CRESULTFIELD_default) + + GB.Error("No default value"); + +END_PROPERTY*/ + +BEGIN_PROPERTY(CRESULTFIELD_result) + + GB.ReturnObject(THIS->result); + +END_PROPERTY + + + +GB_DESC CResultFieldDesc[] = +{ + GB_DECLARE("ResultField", sizeof(CRESULTFIELD)), + GB_NOT_CREATABLE(), + GB_HOOK_CHECK(valid_result_field), + + GB_METHOD("_free", NULL, CRESULTFIELD_free, NULL), + + GB_PROPERTY_READ("Name", "s", CRESULTFIELD_name), + GB_PROPERTY_READ("Type", "i", CRESULTFIELD_type), + GB_PROPERTY_READ("Length", "i", CRESULTFIELD_length), + + GB_PROPERTY_READ("Result", "Result", CRESULTFIELD_result), + + GB_END_DECLARE +}; + + + +/*************************************************************************** + + .Result.Fields + +***************************************************************************/ + +#undef THIS +#define THIS ((CSUBCOLLECTION *)_object) + +BEGIN_PROPERTY(CRESULTFIELD_count) + + CRESULT *result = GB_SubCollectionContainer(THIS); + GB.ReturnInteger(result->info.nfield); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CRESULTFIELD_next) + + CRESULT *result = GB_SubCollectionContainer(THIS); + int *index = (int *)GB.GetEnum(); + CRESULTFIELD *rf; + + if (*index >= result->info.nfield) + GB.StopEnum(); + else + { + rf = GB_SubCollectionGet(THIS, CRESULTFIELD_key(result, *index), 0); + (*index)++; + GB.ReturnObject(rf); + } + +END_METHOD + + + +GB_DESC CResultFieldsDesc[] = +{ + GB_DECLARE(".Result.Fields", 0), GB_INHERITS(".SubCollection"), + + GB_PROPERTY_READ("Count", "i", CRESULTFIELD_count), + //GB_PROPERTY_READ("Length", "i", CRESULTFIELD_count), + GB_METHOD("_next", "ResultField", CRESULTFIELD_next, NULL), + + GB_END_DECLARE +}; + + + diff --git a/main/lib/db/CResultField.h b/main/lib/db/CResultField.h new file mode 100644 index 00000000..251080a5 --- /dev/null +++ b/main/lib/db/CResultField.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + CResultField.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CRESULTFIELD_H +#define __CRESULTFIELD_H + +#include "gambas.h" +#include "gb.db.h" +#include "CResult.h" + +#ifndef __CRESULTFIELD_C +extern GB_DESC CResultFieldsDesc[]; +extern GB_DESC CResultFieldDesc[]; +#else + +#define THIS ((CRESULTFIELD *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + DB_DRIVER *driver; + CRESULT *result; + int index; + } + CRESULTFIELD; + +void *CRESULTFIELD_get(CRESULT *result, const char *name); +int CRESULTFIELD_exist(CRESULT *result, const char *name); +int CRESULTFIELD_find(CRESULT *result, const char *name, bool error); +void CRESULTFIELD_release(CRESULT *result, void *_object); + +char *CRESULTFIELD_key(CRESULT *result, int index); + +#endif + diff --git a/main/lib/db/CTable.c b/main/lib/db/CTable.c new file mode 100644 index 00000000..03b0bdc2 --- /dev/null +++ b/main/lib/db/CTable.c @@ -0,0 +1,443 @@ +/*************************************************************************** + + CTable.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTABLE_C + +#include + +#include "main.h" + +#include "CField.h" +#include "CIndex.h" +#include "CTable.h" + + +static SUBCOLLECTION_DESC _fields_desc = +{ + ".Table.Fields", + (void *)CFIELD_get, + (void *)CFIELD_exist, + (void *)CFIELD_list, + (void *)CFIELD_release +}; + +static SUBCOLLECTION_DESC _indexes_desc = +{ + ".Table.Indexes", + (void *)CINDEX_get, + (void *)CINDEX_exist, + (void *)CINDEX_list, + (void *)CINDEX_release +}; + + +static int valid_table(CTABLE *_object) +{ + return !THIS->conn || !THIS->conn->db.handle; +} + + +static bool check_table(CCONNECTION *conn, char *name, bool must_exist) +{ + bool exist = conn->driver->Table.Exist(&conn->db, name); + + if (must_exist) + { + if (!exist) + { + GB.Error("Unknown table: &1", name); + return TRUE; + } + } + else + { + if (exist) + { + GB.Error("Table already exists: &1", name); + return TRUE; + } + } + + return FALSE; +} + + +static CTABLE *make_table(CCONNECTION *conn, const char *name, bool must_exist) +{ + CTABLE *_object; + + if (check_table(conn, (char *)name, must_exist)) + return NULL; + + //fprintf(stderr, "make_table: '%s'\n", name); + + _object = GB.New(GB.FindClass("Table"), NULL, NULL); + THIS->conn = conn; + //GB.Ref(conn); + THIS->driver = conn->driver; + THIS->name = GB.NewZeroString(name); + + //fprintf(stderr, "make_table: -> %p '%s'\n", THIS, THIS->name); + + return _object; +} + + +void *CTABLE_get(CCONNECTION *conn, const char *name) +{ + return make_table(conn, name, TRUE); +} + + +int CTABLE_exist(CCONNECTION *conn, const char *name) +{ + return conn->driver->Table.Exist(&conn->db, (char *)name); +} + + +void CTABLE_list(CCONNECTION *conn, char ***list) +{ + conn->driver->Table.List(&conn->db, list); +} + +void CTABLE_release(CCONNECTION *conn, void *_object) +{ + THIS->conn = NULL; +} + + +/*************************************************************************** + + Table + +***************************************************************************/ + +static void free_new_fields(CTABLE *_object) +{ + DB_FIELD *fp; + DB_FIELD *next; + + for (fp = THIS->new_fields; fp; fp = next) + { + next = fp->next; + CFIELD_free_info(fp); + GB.Free(POINTER(&fp)); + } + + THIS->new_fields = NULL; +} + + +BEGIN_PROPERTY(CTABLE_primary_key) + + GB_ARRAY primary; + int i, n; + char *field; + + if (THIS->create) + { + if (READ_PROPERTY) + { + if (!THIS->primary) + { + GB.ReturnNull(); + return; + } + + GB.ReturnObject(DB_StringArrayToGambasArray(THIS->primary)); + } + else + { + primary = (GB_ARRAY)VPROP(GB_OBJECT); + if (primary) + n = GB.Array.Count(primary); + else + n = 0; + + for (i = 0; i < n; i++) + { + field = *((char **)GB.Array.Get(primary, i)); + if (!CFIELD_exist(THIS, field)) + { + GB.Error("Unknown field: &1", field); + return; + } + } + + DB_FreeStringArray(&THIS->primary); + if (n) + { + GB.NewArray(&THIS->primary, sizeof(char *), n); + for (i = 0; i < n; i++) + THIS->primary[i] = GB.NewZeroString(*((char **)GB.Array.Get(primary, i))); + } + } + } + else + { + if (READ_PROPERTY) + { + if (THIS->driver->Table.PrimaryKey(&THIS->conn->db, THIS->name, &THIS->primary)) + return; + + GB.ReturnObject(DB_StringArrayToGambasArray(THIS->primary)); + DB_FreeStringArray(&THIS->primary); + } + else + GB.Error("Read-only property"); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CTABLE_name) + + GB.ReturnString(THIS->name); + +END_PROPERTY + + +BEGIN_PROPERTY(CTABLE_system) + + GB.ReturnBoolean(THIS->driver->Table.IsSystem(&THIS->conn->db, THIS->name)); + +END_PROPERTY + + +BEGIN_PROPERTY(CTABLE_type) + + char *type; + + if (THIS->create) + { + if (READ_PROPERTY) + GB.ReturnString(THIS->type); + else + GB.StoreString(PROP(GB_STRING), &THIS->type); + } + else + { + if (READ_PROPERTY) + { + type = THIS->driver->Table.Type(&THIS->conn->db, THIS->name, NULL); + if (type) + GB.ReturnNewZeroString(type); + else + GB.ReturnVoidString(); + } + else + THIS->driver->Table.Type(&THIS->conn->db, THIS->name, GB.ToZeroString(PROP(GB_STRING))); + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(CTABLE_update) + + DB_FIELD *fp; + DB_FIELD *fp_serial = NULL; + + if (!THIS->new_fields) + { + GB.Error("No field"); + return; + } + + for (fp = THIS->new_fields; fp; fp = fp->next) + { + if (fp->type == DB_T_SERIAL) + { + if (THIS->conn->db.flags.no_serial) + { + GB.Error("Serial fields are not supported"); + return; + } + if (fp_serial) + { + GB.Error("Only one serial field is allowed"); + return; + } + fp_serial = fp; + } + else if (fp->type == DB_T_BLOB) + { + if (THIS->conn->db.flags.no_blob) + { + GB.Error("Blob fields are not supported"); + return; + } + } + } + + if (fp_serial) + { + if (!(THIS->primary && GB.Count(THIS->primary) == 1 && strcmp(THIS->primary[0], fp_serial->name) == 0)) + { + GB.Error("The serial field must be the primary key"); + return; + } + } + + /*if (!THIS->primary || GB.Count(THIS->primary) == 0) + { + GB.Error("No primary key"); + return; + }*/ + + if (THIS->driver->Table.Create(&THIS->conn->db, THIS->name, THIS->new_fields, THIS->primary, THIS->type)) + return; + + free_new_fields(THIS); + DB_FreeStringArray(&THIS->primary); + THIS->create = FALSE; + + //GB.Unref(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(CTABLE_free) + + //fprintf(stderr, "CTABLE_free: %p '%s'\n", THIS, THIS->name); + + if (!valid_table(THIS)) + GB_SubCollectionRemove(THIS->conn->tables, THIS->name, 0); + //GB.Unref(POINTER(&THIS->conn)); + + GB.FreeString(&THIS->name); + GB.FreeString(&THIS->type); + DB_FreeStringArray(&THIS->primary); + + GB.Unref(POINTER(&THIS->fields)); + GB.Unref(POINTER(&THIS->indexes)); + + free_new_fields(THIS); + +END_METHOD + + +BEGIN_PROPERTY(CTABLE_fields) + + GB_SubCollectionNew(&THIS->fields, &_fields_desc, THIS); + GB.ReturnObject(THIS->fields); + +END_PROPERTY + + +BEGIN_PROPERTY(CTABLE_indexes) + + GB_SubCollectionNew(&THIS->indexes, &_indexes_desc, THIS); + GB.ReturnObject(THIS->indexes); + +END_PROPERTY + + +BEGIN_PROPERTY(CTABLE_connection) + + GB.ReturnObject(THIS->conn); + +END_PROPERTY + + + +GB_DESC CTableDesc[] = +{ + GB_DECLARE("Table", sizeof(CTABLE)), + GB_NOT_CREATABLE(), + GB_HOOK_CHECK(valid_table), + + GB_METHOD("_free", NULL, CTABLE_free, NULL), + + //GB_METHOD("AddField", NULL, CTABLE_add_field, "(Name)s(Type)i[(Length)i(Default)v"]) + + GB_PROPERTY_READ("Name", "s", CTABLE_name), + GB_PROPERTY_READ("System", "b", CTABLE_system), + GB_PROPERTY("PrimaryKey", "String[]", CTABLE_primary_key), + GB_PROPERTY("Type", "s", CTABLE_type), + GB_PROPERTY_READ("Connection", "Connection", CTABLE_connection), + + GB_METHOD("Update", NULL, CTABLE_update, NULL), + + GB_PROPERTY_READ("Fields", ".Table.Fields", CTABLE_fields), + GB_PROPERTY_READ("Indexes", ".Table.Indexes", CTABLE_indexes), + + GB_END_DECLARE +}; + + +/*************************************************************************** + + .Connection.Tables + +***************************************************************************/ + +#undef THIS +#define THIS ((CSUBCOLLECTION *)_object) + +BEGIN_METHOD(CTABLE_add, GB_STRING name; GB_STRING type) + + CCONNECTION *conn = GB_SubCollectionContainer(THIS); + CTABLE *table; + char *name = GB.ToZeroString(ARG(name)); + + if (DB_CheckNameWith(name, "table", ".")) + return; + + table = make_table(conn, name, FALSE); + if (!table) + return; + + GB_SubCollectionAdd(THIS, STRING(name), LENGTH(name), table); + + if (!MISSING(type)) + GB.StoreString(ARG(type), &table->type); + + table->create = TRUE; + GB.ReturnObject(table); + +END_METHOD + + +BEGIN_METHOD(CTABLE_remove, GB_STRING name) + + CCONNECTION *conn = GB_SubCollectionContainer(THIS); + char *name = GB.ToZeroString(ARG(name)); + + GB_SubCollectionRemove(THIS, STRING(name), LENGTH(name)); + + if (check_table(conn, name, TRUE)) + return; + + conn->driver->Table.Delete(&conn->db, name); + +END_METHOD + +GB_DESC CConnectionTablesDesc[] = +{ + GB_DECLARE(".Connection.Tables", 0), GB_INHERITS(".SubCollection"), + + GB_METHOD("Add", "Table", CTABLE_add, "(Name)s[(Type)s]"), + GB_METHOD("Remove", NULL, CTABLE_remove, "(Name)s"), + + GB_END_DECLARE +}; diff --git a/main/lib/db/CTable.h b/main/lib/db/CTable.h new file mode 100644 index 00000000..967a86f3 --- /dev/null +++ b/main/lib/db/CTable.h @@ -0,0 +1,63 @@ +/*************************************************************************** + + CTable.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTABLE_H +#define __CTABLE_H + +#include "gambas.h" +#include "gb.db.h" +#include "CDatabase.h" +#include "c_subcollection.h" + +#ifndef __CTABLE_C +extern GB_DESC CTableDesc[]; +extern GB_DESC CConnectionTablesDesc[]; +//extern GB_DESC CTableFieldDesc[]; +//extern GB_DESC CTableIndexDesc[]; +#else + +#define THIS ((CTABLE *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + DB_DRIVER *driver; + CCONNECTION *conn; + char *name; + char *type; + CSUBCOLLECTION *fields; + CSUBCOLLECTION *indexes; + bool create; + DB_FIELD *new_fields; /* linked list of DB_FIELD structures located in the objects stored in fields */ + char **primary; + } + CTABLE; + +void *CTABLE_get(CCONNECTION *conn, const char *key); +int CTABLE_exist(CCONNECTION *conn, const char *key); +void CTABLE_list(CCONNECTION *conn, char ***list); +void CTABLE_release(CCONNECTION *conn, void *_object); + +#endif diff --git a/main/lib/db/CUser.c b/main/lib/db/CUser.c new file mode 100644 index 00000000..d73362b8 --- /dev/null +++ b/main/lib/db/CUser.c @@ -0,0 +1,229 @@ +/*************************************************************************** + + CUser.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CUSER_C + +#include "main.h" + +#include "CUser.h" + + +static int valid_user(CUSER *_object) +{ + return !THIS->conn || !THIS->conn->db.handle; +} + +static bool check_user(CCONNECTION *conn, const char *name, bool must_exist) +{ + bool exist = conn->driver->User.Exist(&conn->db, (char *)name); + + if (must_exist) + { + if (!exist) + { + GB.Error("Unknown user: &1", name); + return TRUE; + } + } + else + { + if (exist) + { + GB.Error("User already exists: &1", name); + return TRUE; + } + } + + return FALSE; +} + + +void *CUSER_get(CCONNECTION *conn, const char *name) +{ + CUSER *_object; + + if (check_user(conn, name, TRUE)) + return NULL; + + _object = GB.New(GB.FindClass("DatabaseUser"), NULL, NULL); + THIS->conn = conn; + THIS->driver = conn->driver; + THIS->name = GB.NewZeroString(name); + conn->driver->User.Info(&conn->db, THIS->name, &THIS->info); + return THIS; +} + + +int CUSER_exist(CCONNECTION *conn, const char *name) +{ + return conn->driver->User.Exist(&conn->db, (char *)name); +} + + +void CUSER_list(CCONNECTION *conn, char ***list) +{ + conn->driver->User.List(&conn->db, list); +} + + +void CUSER_release(CCONNECTION *conn, void *_object) +{ + THIS->conn = NULL; +} + + +/*************************************************************************** + + User + +***************************************************************************/ + +BEGIN_METHOD_VOID(CUSER_free) + + if (!valid_user(THIS)) + GB_SubCollectionRemove(THIS->conn->users, THIS->name, 0); + GB.FreeString(&THIS->name); + GB.FreeString(&THIS->info.password); + +END_METHOD + + +BEGIN_PROPERTY(CUSER_name) + + GB.ReturnString(THIS->name); + +END_PROPERTY + + +BEGIN_METHOD_VOID(CUSER_delete) + + THIS->conn->driver->User.Delete(&THIS->conn->db, THIS->name); + +END_METHOD + + +BEGIN_PROPERTY(CUSER_password) + + if (READ_PROPERTY) + GB.ReturnString(THIS->info.password); + else if (THIS->name) + { + GB.StoreString(PROP(GB_STRING), &THIS->info.password); + THIS->driver->User.SetPassword(&THIS->conn->db, THIS->name, THIS->info.password); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CUSER_administrator) + + GB.ReturnBoolean(THIS->info.admin); + +END_PROPERTY + + +BEGIN_PROPERTY(CUSER_connection) + + GB.ReturnObject(THIS->conn); + +END_PROPERTY + + +GB_DESC CUserDesc[] = +{ + GB_DECLARE("DatabaseUser", sizeof(CUSER)), + GB_NOT_CREATABLE(), + GB_HOOK_CHECK(valid_user), + + GB_METHOD("_free", NULL, CUSER_free, NULL), + + GB_METHOD("Delete", NULL, CUSER_delete, NULL), + + GB_PROPERTY_READ("Name", "s", CUSER_name), + GB_PROPERTY_READ("Administrator", "b", CUSER_administrator), + GB_PROPERTY("Password", "s", CUSER_password), + GB_PROPERTY_READ("Connection", "Connection", CUSER_connection), + + GB_END_DECLARE +}; + + + +/*************************************************************************** + + .Connection.Users + +***************************************************************************/ + +#undef THIS +#define THIS ((CSUBCOLLECTION *)_object) + +BEGIN_METHOD(CUSER_add, GB_STRING name; GB_STRING password; GB_BOOLEAN admin) + + CCONNECTION *conn = GB_SubCollectionContainer(THIS); + char *name = GB.ToZeroString(ARG(name)); + DB_USER info; + + CLEAR(&info); + + if (DB_CheckNameWith(name, "user", "@%")) + return; + + if (check_user(conn, name, FALSE)) + return; + + info.admin = VARGOPT(admin, FALSE); + if (!MISSING(password)) + info.password = GB.ToZeroString(ARG(password)); + + conn->driver->User.Create(&conn->db, name, &info); + +END_METHOD + + +BEGIN_METHOD(CUSER_remove, GB_STRING name) + + CCONNECTION *conn = GB_SubCollectionContainer(THIS); + char *name = GB.ToZeroString(ARG(name)); + + GB_SubCollectionRemove(THIS, STRING(name), LENGTH(name)); + + if (check_user(conn, name, TRUE)) + return; + + conn->driver->User.Delete(&conn->db, name); + +END_METHOD + + +GB_DESC CConnectionUsersDesc[] = +{ + GB_DECLARE(".Connection.Users", 0), GB_INHERITS(".SubCollection"), + + GB_METHOD("Add", NULL, CUSER_add, "(Name)s[(Password)s(Admin)b]"), + GB_METHOD("Remove", NULL, CUSER_remove, "(Name)s"), + + GB_END_DECLARE +}; + + diff --git a/main/lib/db/CUser.h b/main/lib/db/CUser.h new file mode 100644 index 00000000..c06993f1 --- /dev/null +++ b/main/lib/db/CUser.h @@ -0,0 +1,56 @@ +/*************************************************************************** + + CUser.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CUSER_H +#define __CUSER_H + +#include "gambas.h" +#include "gb.db.h" +#include "CConnection.h" + +#ifndef __CUSER_C +extern GB_DESC CConnectionUsersDesc[]; +extern GB_DESC CUserDesc[]; +#else + +#define THIS ((CUSER *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + DB_DRIVER *driver; + CCONNECTION *conn; + char *name; + DB_USER info; + } + CUSER; + +void *CUSER_get(CCONNECTION *conn, const char *key); +int CUSER_exist(CCONNECTION *conn, const char *key); +void CUSER_list(CCONNECTION *conn, char ***list); +void CUSER_release(CCONNECTION *conn, void *_object); + +#endif + diff --git a/main/lib/db/Makefile.am b/main/lib/db/Makefile.am new file mode 100644 index 00000000..93b50614 --- /dev/null +++ b/main/lib/db/Makefile.am @@ -0,0 +1,25 @@ +COMPONENT = gb.db +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.db.la + +gb_db_la_LIBADD = @C_LIB@ +gb_db_la_LDFLAGS = -module @LD_FLAGS@ +gb_db_la_CFLAGS = -I$(top_srcdir)/share $(AM_CFLAGS) + +gb_db_la_SOURCES = \ + gb.db.h gb.db.proto.h gb_barray.h \ + c_subcollection.h c_subcollection.c \ + main.h main.c \ + deletemap.h deletemap.c \ + CConnection.h CConnection.c \ + CDatabase.h CDatabase.c \ + CUser.h CUser.c \ + CTable.h CTable.c \ + CField.h CField.c \ + CIndex.h CIndex.c \ + CResult.h CResult.c \ + CResultField.h CResultField.c \ + sqlite.h sqlite.c + + diff --git a/main/lib/db/c_subcollection.c b/main/lib/db/c_subcollection.c new file mode 100644 index 00000000..bc392216 --- /dev/null +++ b/main/lib/db/c_subcollection.c @@ -0,0 +1,265 @@ +/*************************************************************************** + + c_subcollection.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_SUBCOLLECTION_C + +#include "main.h" +#include "c_subcollection.h" + + +static void free_string_array(char ***parray) +{ + int i; + char **array = *parray; + + if (!*parray) + return; + + for (i = 0; i < GB.Count(array); i++) + GB.FreeString(&array[i]); + + GB.FreeArray(parray); +} + +static void *get_from_key(CSUBCOLLECTION *_object, const char *key, int len) +{ + void *data; + char *tkey; + + if (!key || !*key) + return NULL; + + if (len <= 0) + len = strlen(key); + + //fprintf(stderr, "get_from_key: %.*s\n", len, key); + + if (GB.HashTable.Get(THIS->hash_table, key, len, &data)) + { + tkey = GB.TempString(key, len); + data = (*THIS->desc->get)(THIS->container, tkey); + if (data) + { + //fprintf(stderr, "get_from_key: insert %p '%.*s'\n", data, len, key); + GB.HashTable.Add(THIS->hash_table, key, len, data); + GB.Ref(data); + } + } + + return data; +} + +static CSUBCOLLECTION *_current = NULL; + +static void clear_one(void *data) +{ + CSUBCOLLECTION *save = _current; + + if (_current->desc->release) + (*_current->desc->release)(_current->container, data); + + //fprintf(stderr, "clear: %p\n", data); + GB.Unref(&data); + + _current = save; +} + +static void clear_subcollection(CSUBCOLLECTION *_object) +{ + _current = THIS; + GB.HashTable.Enum(THIS->hash_table, clear_one); + GB.HashTable.Free(&THIS->hash_table); +} + + +BEGIN_METHOD_VOID(CSUBCOLLECTION_free) + + //*THIS->store = NULL; + free_string_array(&THIS->list); + + clear_subcollection(THIS); + +END_METHOD + + +BEGIN_PROPERTY(CSUBCOLLECTION_count) + + /*free_string_array(&THIS->list);*/ + if (!THIS->list) + (*THIS->desc->list)(THIS->container, &THIS->list); + + if (THIS->list) + GB.ReturnInteger(GB.Count(THIS->list)); + else + GB.ReturnInteger(0); + +END_PROPERTY + + +BEGIN_METHOD(CSUBCOLLECTION_exist, GB_STRING key) + + char *key = GB.ToZeroString(ARG(key)); + + if (!key || !*key) + GB.ReturnBoolean(FALSE); + else + GB.ReturnBoolean((*THIS->desc->exist)(THIS->container, key)); + +END_METHOD + + +BEGIN_METHOD_VOID(CSUBCOLLECTION_next) + + int *pos = (int *)GB.GetEnum(); + + if (THIS->desc->list) + { + char *key = NULL; + int n; + + if (*pos == 0) + { + free_string_array(&THIS->list); + (*THIS->desc->list)(THIS->container, &THIS->list); + } + + if (THIS->list) + { + if (*pos < GB.Count(THIS->list)) + { + n = (*pos)++; + key = THIS->list[n]; + } + } + + if (!key || !*key) + GB.StopEnum(); + else + GB.ReturnObject(get_from_key(THIS, key, 0)); + } + else + { + /*void *elt; + + elt = get_from_key(THIS, NULL, *pos); + (*pos)++; + if (elt) + GB_ReturnObject(elt); + else*/ + GB.StopEnum(); + } + +END_METHOD + + +BEGIN_METHOD(CSUBCOLLECTION_get, GB_STRING key) + + GB.ReturnObject(get_from_key(THIS, STRING(key), LENGTH(key))); + +END_METHOD + +BEGIN_METHOD_VOID(CSUBCOLLECTION_refresh) + + clear_subcollection(THIS); + GB.HashTable.New(&THIS->hash_table, GB_COMP_BINARY); + +END_METHOD + +GB_DESC SubCollectionDesc[] = +{ + GB_DECLARE(".SubCollection", sizeof(CSUBCOLLECTION)), + + GB_METHOD("_free", NULL, CSUBCOLLECTION_free, NULL), + + GB_PROPERTY_READ("Count", "i", CSUBCOLLECTION_count), + //GB_PROPERTY_READ("Length", "i", CSUBCOLLECTION_count), + + GB_METHOD("Exist", "b", CSUBCOLLECTION_exist, "(Key)s"), + GB_METHOD("_next", "o", CSUBCOLLECTION_next, NULL), + GB_METHOD("_get", "o", CSUBCOLLECTION_get, "(Key)s"), + GB_METHOD("Refresh", NULL, CSUBCOLLECTION_refresh, NULL), + + GB_END_DECLARE +}; + + +void GB_SubCollectionNew(CSUBCOLLECTION **subcollection, SUBCOLLECTION_DESC *desc, void *container) +{ + CSUBCOLLECTION *ob; + + if (*subcollection) + return; + + ob = GB.New(GB.FindClass(desc->klass), NULL, NULL); + + ob->container = container; + //ob->store = subcollection; + //GB_Ref(container); + ob->desc = desc; + GB.HashTable.New(&ob->hash_table, GB_COMP_BINARY); + + *subcollection = ob; + GB.Ref(ob); +} + + +void *GB_SubCollectionContainer(void *_object) +{ + return THIS->container; +} + + +void GB_SubCollectionAdd(void *_object, const char *key, int len, void *value) +{ + if (len <= 0) + len = strlen(key); + + GB.Ref(value); + GB_SubCollectionRemove(THIS, key, len); + //fprintf(stderr, "GB_SubCollectionAdd: insert %p '%.*s'\n", value, len, key); + GB.HashTable.Add(THIS->hash_table, key, len, value); +} + +void GB_SubCollectionRemove(void *_object, const char *key, int len) +{ + void *old_value; + + if (!THIS) + return; + + if (len <= 0) + len = strlen(key); + + if (!GB.HashTable.Get(THIS->hash_table, key, len, &old_value)) + { + //fprintf(stderr, "GB_SubCollectionRemove: remove %p '%.*s'\n", old_value, len, key); + GB.HashTable.Remove(THIS->hash_table, key, len); + GB.Unref(&old_value); + } +} + + +void *GB_SubCollectionGet(void *_object, const char *key, int len) +{ + return get_from_key(THIS, key, len); +} diff --git a/main/lib/db/c_subcollection.h b/main/lib/db/c_subcollection.h new file mode 100644 index 00000000..17dce3f9 --- /dev/null +++ b/main/lib/db/c_subcollection.h @@ -0,0 +1,68 @@ +/*************************************************************************** + + c_subcollection.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_SUBCOLLECTION_H +#define __C_SUBCOLLECTION_H + +#include "gambas.h" + +/* SubCollection description */ + +typedef + struct { + char *klass; + void *(*get)(void *, const char *); + int (*exist)(void *, const char *); + void (*list)(void *, char ***); + void (*release)(void *, void *); + } + SUBCOLLECTION_DESC; + +typedef + struct { + GB_BASE object; + GB_HASHTABLE hash_table; + int mode; + void *container; + SUBCOLLECTION_DESC *desc; + char **list; + } + CSUBCOLLECTION; + +#ifndef __C_SUBCOLLECTION_C + +extern GB_DESC SubCollectionDesc[]; + +#else + +#define THIS ((CSUBCOLLECTION *)_object) + +#endif + +void GB_SubCollectionNew(CSUBCOLLECTION **subcollection, SUBCOLLECTION_DESC *desc, void *container); +void *GB_SubCollectionContainer(void *_object); +void GB_SubCollectionAdd(void *_object, const char *key, int len, void *value); +void GB_SubCollectionRemove(void *_object, const char *key, int len); +void *GB_SubCollectionGet(void *_object, const char *key, int len); + +#endif diff --git a/main/lib/db/deletemap.c b/main/lib/db/deletemap.c new file mode 100644 index 00000000..2cd400a9 --- /dev/null +++ b/main/lib/db/deletemap.c @@ -0,0 +1,183 @@ +/*************************************************************************** + + deletemap.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __DELETEMAP_C + +#include "gambas.h" +#include "main.h" +#include "deletemap.h" + +//#define DEBUG_ME + +typedef + struct _DELETE_SLOT { + struct _DELETE_SLOT *prev; + struct _DELETE_SLOT *next; + int start; + int length; + } + DELETE_SLOT; + +int DELETE_MAP_virtual_to_real(DELETE_MAP *dmap, int vpos) +{ + DELETE_SLOT *slot = (DELETE_SLOT *)dmap; + int rpos = vpos; + + while (slot) + { + if (rpos < slot->start) + break; + rpos += slot->length; + slot = slot->next; + } + + #ifdef DEBUG_ME + printf("DELETE_MAP_virtual_to_real: %ld => %ld\n", vpos, rpos); + #endif + + return rpos; +} + +int DELETE_MAP_real_to_virtual(DELETE_MAP *dmap, int rpos) +{ + DELETE_SLOT *slot = (DELETE_SLOT *)dmap; + int vpos = rpos; + + while (slot) + { + if (rpos < slot->start) + break; + if (rpos < (slot->start + slot->length)) + return (-1); + vpos -= slot->length; + slot = slot->next; + } + + #ifdef DEBUG_ME + printf("DELETE_MAP_real_to_virtual: %ld => %ld\n", rpos, vpos); + #endif + + return vpos; +} + +#ifdef DEBUG_ME +static void dump(DELETE_MAP *dmap) +{ + DELETE_SLOT *slot = (DELETE_SLOT *)dmap; + + printf("dumping map %p\n", dmap); + while (slot) + { + printf("[ %ld %ld ]\n", slot->start, slot->length); + slot = slot->next; + } + printf("\n"); +} +#endif + +static void create_slot(DELETE_SLOT **pslot, int pos, DELETE_SLOT *before, DELETE_SLOT *after) +{ + GB.Alloc(POINTER(pslot), sizeof(DELETE_SLOT)); + (*pslot)->prev = before; + (*pslot)->next = after; + (*pslot)->start = pos; + (*pslot)->length = 1; + + if (before) + before->next = *pslot; + if (after) + after->prev = *pslot; +} + +static DELETE_SLOT *delete_slot(DELETE_SLOT *slot) +{ + DELETE_SLOT *nslot; + + nslot = slot->next; + + if (slot->prev) + slot->prev->next = slot->next; + if (slot->next) + slot->next->prev = slot->prev; + + GB.Free(POINTER(&slot)); + + return nslot; +} + +void DELETE_MAP_add(DELETE_MAP **dmap, int vpos) +{ + DELETE_SLOT *slot; + DELETE_SLOT *nslot; + DELETE_SLOT *bslot = NULL; + int rpos; + + if (vpos < 0) + return; + + rpos = DELETE_MAP_virtual_to_real(*dmap, vpos); + + for (slot = (DELETE_SLOT *)*dmap; slot; slot = slot->next) + { + if (rpos < slot->start) + break; + bslot = slot; + } + + create_slot(&nslot, rpos, bslot, slot); + if ((DELETE_SLOT *)*dmap == slot) + *dmap = (DELETE_MAP *)nslot; + + slot = nslot; + if (slot->prev) + slot = slot->prev; + + while (slot->next) + { + if ((slot->start + slot->length) == (slot->next->start)) + { + nslot = slot->next; + slot->length += nslot->length; + delete_slot(nslot); + } + else + slot = slot->next; + } + + #ifdef DEBUG_ME + printf("DELETE_MAP_add: %ld\n", vpos); + dump(*dmap); + #endif +} + +void DELETE_MAP_free(DELETE_MAP **dmap) +{ + DELETE_SLOT *slot; + + slot = (DELETE_SLOT *)*dmap; + while (slot) + slot = delete_slot(slot); + + *dmap = NULL; +} + diff --git a/main/lib/db/deletemap.h b/main/lib/db/deletemap.h new file mode 100644 index 00000000..c8f5fb72 --- /dev/null +++ b/main/lib/db/deletemap.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + deletemap.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __DELETEMAP_H +#define __DELETEMAP_H + +typedef + void *DELETE_MAP; + +int DELETE_MAP_virtual_to_real(DELETE_MAP *dmap, int vpos); +int DELETE_MAP_real_to_virtual(DELETE_MAP *dmap, int rpos); +void DELETE_MAP_add(DELETE_MAP **dmap, int vpos); +void DELETE_MAP_free(DELETE_MAP **dmap); + +#endif diff --git a/main/lib/db/gb.db.component b/main/lib/db/gb.db.component new file mode 100644 index 00000000..dd66d23c --- /dev/null +++ b/main/lib/db/gb.db.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.db +Name=Database access component +Author=Benoît Minisini,Nigel Gerrard,Andrea Bortolan,Daniel Vostanikian diff --git a/main/lib/db/gb.db.h b/main/lib/db/gb.db.h new file mode 100644 index 00000000..cdad5cc2 --- /dev/null +++ b/main/lib/db/gb.db.h @@ -0,0 +1,268 @@ +/*************************************************************************** + + gb.db.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_DB_H +#define __GB_DB_H + +#include "gambas.h" + +#define DB_INTERFACE_VERSION 1 + +typedef + struct { + char *type; + char *host; + char *port; + char *name; + char *user; + char *password; + int options; + } + DB_DESC; + +/* LIMIT position */ + +#define DB_LIMIT_NONE 0 +#define DB_LIMIT_AT_BEGIN 1 +#define DB_LIMIT_AT_END 2 + +typedef + struct { + void *handle; /* Connection handle */ + int version; /* Version of the database system */ + char *charset; /* Charset used by the database */ + void *data; /* Can be used by the driver for storing its own private data */ + int error; /* Last SQL error code raise by a query */ + int timeout; /* Connection timeout */ + int timezone; /* Timezone of dates (default to local timezone) */ + unsigned ignore_case : 1; /* If table, field and index names are case sensitive */ + struct { + unsigned no_table_type : 1; /* Tables do not have types */ + unsigned no_serial : 1; /* Serial fields are not supported */ + unsigned no_blob : 1; /* Blob fields are not supported */ + unsigned no_seek : 1; /* Cannot seek anywhere in a Result */ + unsigned no_nest : 1; /* Cannot nest transactions */ + //unsigned no_case : 1; /* table, field and index names must be converted to lower case */ + unsigned schema : 1; /* If table names can be prefixed by a schema name and a dot */ + unsigned no_collation : 1; /* No collation support at field level */ + unsigned system : 1; /* system database */ + } + flags; + struct { + const char *keyword; /* keyword for limiting the result of a query */ + int pos; /* position of 'limit' keyword */ + } + limit; + const char *db_name_char; /* These characters are allowed in a database name */ + } + DB_DATABASE; + +typedef + void *DB_RESULT; + +typedef + struct _DB_FIELD { + struct _DB_FIELD *next; + char *name; + GB_TYPE type; /* gambas field type */ + int length; /* max length for text fields (0 = no limit) */ + GB_VARIANT_VALUE def; /* default value */ + char *collation; /* field collation */ + } + DB_FIELD; + + +typedef + struct { + char *table; + int nfield; + int nindex; + DB_FIELD *field; + int *index; + } + DB_INFO; + +typedef + struct { + char *name; + char *fields; /* list of index fields separated by commas */ + int unique; /* index is unique */ + int primary; /* primary index */ + } + DB_INDEX; + +typedef + struct { + char *name; + char *password; + int admin; /* user is a superuser */ + } + DB_USER; + +typedef + struct { + GB_BASE ob; + char *data; + int length; + int constant; + } + DB_BLOB; + +typedef + void (*DB_FORMAT_CALLBACK)(const char *, int); + +typedef + void (*DB_SUBST_CALLBACK)(int, char **, int *, char); + +typedef + struct { + const char *name; + + int (*Open)(DB_DESC *desc, DB_DATABASE *db); + void (*Close)(DB_DATABASE *db); + + int (*Format)(GB_VALUE *val, DB_FORMAT_CALLBACK add); + void (*FormatBlob)(DB_BLOB *blob, DB_FORMAT_CALLBACK add); + + int (*Exec)(DB_DATABASE *db, const char *, DB_RESULT *result, const char *err); + + int (*Begin)(DB_DATABASE *db); + int (*Commit)(DB_DATABASE *db); + int (*Rollback)(DB_DATABASE *db); + GB_ARRAY (*GetCollations)(DB_DATABASE *db); + const char *(*GetQuote)(void); + int64_t (*GetLastInsertId)(DB_DATABASE *db); + + struct { + void (*Init)(DB_RESULT result, DB_INFO *info, int *count); + int (*Fill)(DB_DATABASE *db, DB_RESULT result, int pos, GB_VARIANT_VALUE *buffer, int next); + void (*Blob)(DB_RESULT result, int pos, int field, DB_BLOB *blob); + void (*Release)(DB_RESULT result, DB_INFO *info); + struct { + GB_TYPE (*Type)(DB_RESULT result, int index); + char *(*Name)(DB_RESULT result, int index); + int (*Index)(DB_RESULT result, const char *name, DB_DATABASE *db); + int (*Length)(DB_RESULT result, int index); + } + Field; + } + Result; + + struct { + int (*Exist)(DB_DATABASE *db, const char *table, const char *field); + int (*List)(DB_DATABASE *db, const char *table, char ***fields); + int (*Info)(DB_DATABASE *db, const char *table, const char *field, DB_FIELD *info); + } + Field; + + struct { + int (*Init)(DB_DATABASE *db, const char *table, DB_INFO *info); + int (*Index)(DB_DATABASE *db, const char *table, DB_INFO *info); + void (*Release)(DB_DATABASE *db, DB_INFO *info); + int (*Exist)(DB_DATABASE *db, const char *table); + int (*List)(DB_DATABASE *db, char ***tables); + int (*PrimaryKey)(DB_DATABASE *db, const char *table, char ***primary); + int (*IsSystem)(DB_DATABASE *db, const char *table); + char *(*Type)(DB_DATABASE *db, const char *table, const char *type); + int (*Delete)(DB_DATABASE *db, const char *table); + int (*Create)(DB_DATABASE *db, const char *table, DB_FIELD *fields, char **primary, const char *tabletype); + } + Table; + + struct { + int (*Exist)(DB_DATABASE *db, const char *table, const char *index); + int (*List)(DB_DATABASE *db, const char *table, char ***indexes); + int (*Info)(DB_DATABASE *db, const char *table, const char *index, DB_INDEX *info); + int (*Delete)(DB_DATABASE *db, const char *table, const char *index); + int (*Create)(DB_DATABASE *db, const char *table, const char *index, DB_INDEX *info); + } + Index; + + struct { + int (*Exist)(DB_DATABASE *db, const char *name); + int (*List)(DB_DATABASE *db, char ***names); + int (*IsSystem)(DB_DATABASE *db, const char *name); + int (*Delete)(DB_DATABASE *db, const char *name); + int (*Create)(DB_DATABASE *db, const char *name); + } + Database; + + struct { + int (*Exist)(DB_DATABASE *db, const char *user); + int (*List)(DB_DATABASE *db, char ***users); + int (*Info)(DB_DATABASE *db, const char *user, DB_USER *info); + int (*Delete)(DB_DATABASE *db, const char *user); + int (*Create)(DB_DATABASE *db, const char *user, DB_USER *info); + int (*SetPassword)(DB_DATABASE *db, const char *user, const char *password); + } + User; + } + DB_DRIVER; + +typedef + struct { + intptr_t version; + void (*Register)(DB_DRIVER *); + void (*Format)(DB_DRIVER *, GB_VALUE *, DB_FORMAT_CALLBACK); + void (*FormatVariant)(DB_DRIVER *, GB_VARIANT_VALUE *, DB_FORMAT_CALLBACK); + int (*IsDebug)(void); + void (*TryAnother)(const char *); + char *(*SubstString)(const char *, int, DB_SUBST_CALLBACK); + char *(*QuoteString)(const char *, int, char); + char *(*UnquoteString)(const char *, int, char); + DB_DATABASE *(*GetCurrentDatabase)(); + + struct { + void (*Init)(void); + void (*Add)(const char *); + void (*AddLower)(const char *); + void (*AddLength)(const char *, int); + char *(*Get)(void); + char *(*GetNew)(void); + int (*Length)(void); + } + Query; + + struct { + int (*Find)(char **, const char *); + } + StringArray; + } + DB_INTERFACE; + +/* Field datatypes */ + +#define DB_T_SERIAL ((GB_TYPE)-1) +#define DB_T_BLOB ((GB_TYPE)-2) + +// Result.Fill() return values + +#define DB_OK 0 +#define DB_ERROR 1 +#define DB_NO_DATA 2 + +/* Field Separator Character e.g. Table.field = Table.field */ + +#define FLD_SEP '.' + +#endif /* __MAIN_H */ diff --git a/main/lib/db/gb.db.proto.h b/main/lib/db/gb.db.proto.h new file mode 100644 index 00000000..a270a198 --- /dev/null +++ b/main/lib/db/gb.db.proto.h @@ -0,0 +1,148 @@ +/*************************************************************************** + + gb.db.proto.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_DB_PROTO_H +#define __GB_DB_PROTO_H + +#include "gb.db.h" + +static const char *get_quote(void); +static int open_database(DB_DESC *desc, DB_DATABASE *db); +static void close_database(DB_DATABASE *db); +static GB_ARRAY get_collations(DB_DATABASE *db); +static int64_t get_last_insert_id(DB_DATABASE *db); +static int format_value(GB_VALUE *arg, DB_FORMAT_CALLBACK add); +static void format_blob(DB_BLOB *blob, DB_FORMAT_CALLBACK add); +static int exec_query(DB_DATABASE *db, const char *query, DB_RESULT *result, const char *err); +static void query_init(DB_RESULT result, DB_INFO *info, int *count); +static void query_release(DB_RESULT result, DB_INFO *info); +static int query_fill(DB_DATABASE *db, DB_RESULT result, int pos, GB_VARIANT_VALUE *buffer, int next); +static void blob_read(DB_RESULT result, int pos, int field, DB_BLOB *blob); +static char *field_name(DB_RESULT result, int field); +static int field_index(DB_RESULT result, const char *name, DB_DATABASE *db); +static GB_TYPE field_type(DB_RESULT result, int field); +static int field_length(DB_RESULT result, int field); +static int begin_transaction(DB_DATABASE *db); +static int commit_transaction(DB_DATABASE *db); +static int rollback_transaction(DB_DATABASE *db); +static int field_info(DB_DATABASE *db, const char *table, const char *field, DB_FIELD *info); +static int table_init(DB_DATABASE *db, const char *table, DB_INFO *info); +static int table_index(DB_DATABASE *db, const char *table, DB_INFO *info); +static void table_release(DB_DATABASE *db, DB_INFO *info); +static int table_exist(DB_DATABASE *db, const char *table); +static int table_list(DB_DATABASE *db, char ***tables); +static int table_primary_key(DB_DATABASE *db, const char *table, char ***primary); +static int table_is_system(DB_DATABASE *db, const char *table); +static char *table_type(DB_DATABASE *db, const char *table, const char *type); +static int table_delete(DB_DATABASE *db, const char *table); +static int table_create(DB_DATABASE *db, const char *table, DB_FIELD *fields, char **primary, const char *type); +static int field_exist(DB_DATABASE *db, const char *table, const char *field); +static int field_list(DB_DATABASE *db, const char *table, char ***fields); +static int field_info(DB_DATABASE *db, const char *table, const char *field, DB_FIELD *info); +static int index_exist(DB_DATABASE *db, const char *table, const char *index); +static int index_list(DB_DATABASE *db, const char *table, char ***indexes); +static int index_info(DB_DATABASE *db, const char *table, const char *index, DB_INDEX *info); +static int index_delete(DB_DATABASE *db, const char *table, const char *index); +static int index_create(DB_DATABASE *db, const char *table, const char *index, DB_INDEX *info); +static int database_exist(DB_DATABASE *db, const char *name); +static int database_list(DB_DATABASE *db, char ***databases); +static int database_is_system(DB_DATABASE *db, const char *name); +static int database_delete(DB_DATABASE *db, const char *name); +static int database_create(DB_DATABASE *db, const char *name); +static int user_exist(DB_DATABASE *db, const char *name); +static int user_list(DB_DATABASE *db, char ***users); +static int user_info(DB_DATABASE *db, const char *name, DB_USER *info ); +static int user_delete(DB_DATABASE *db, const char *name); +static int user_create(DB_DATABASE *db, const char *name, DB_USER *info); +static int user_set_password(DB_DATABASE *db, const char *name, const char *password); + +#define DECLARE_DRIVER(_driver, _name) \ +static DB_DRIVER _driver = \ +{ \ + _name, \ + open_database, \ + close_database, \ + format_value, \ + format_blob, \ + exec_query, \ + begin_transaction, \ + commit_transaction, \ + rollback_transaction, \ + get_collations, \ + get_quote, \ + get_last_insert_id, \ + { \ + query_init, \ + query_fill, \ + blob_read, \ + query_release, \ + { \ + field_type, \ + field_name, \ + field_index, \ + field_length, \ + }, \ + }, \ + { \ + field_exist, \ + field_list, \ + field_info, \ + }, \ + { \ + table_init, \ + table_index, \ + table_release, \ + table_exist, \ + table_list, \ + table_primary_key, \ + table_is_system, \ + table_type, \ + table_delete, \ + table_create, \ + }, \ + { \ + index_exist, \ + index_list, \ + index_info, \ + index_delete, \ + index_create, \ + }, \ + { \ + database_exist, \ + database_list, \ + database_is_system, \ + database_delete, \ + database_create, \ + }, \ + { \ + user_exist, \ + user_list, \ + user_info, \ + user_delete, \ + user_create, \ + user_set_password \ + } \ +}; + + +#endif diff --git a/main/lib/db/gb.db/.component b/main/lib/db/gb.db/.component new file mode 100644 index 00000000..a9a67ca4 --- /dev/null +++ b/main/lib/db/gb.db/.component @@ -0,0 +1,3 @@ +[Component] +Key=gb.db +Version=3.12.0 diff --git a/main/lib/db/gb.db/.directory b/main/lib/db/gb.db/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/main/lib/db/gb.db/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/main/lib/db/gb.db/.icon.png b/main/lib/db/gb.db/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a9254f949f86671b58a744a6a93623576acc5217 GIT binary patch literal 10569 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 "# Gambas Database Template File 3.0" Then Error.Raise("Bad database template format") + iLine = 1 + + For Each sLine In hFile.Lines + + Inc iLine + sLine = Trim(sLine) + If Not sLine Then Continue + If sLine Begins "#" Then Continue + + If sLine = "}" Then + If cIndex Then + cTable["Indexes"].Add(cIndex) + cIndex = Null + cCurrent = cTable + Else If cField Then + cTable["Fields"].Add(cField) + cField = Null + cCurrent = cTable + Else If cTable Then + GoSub CREATE_TABLE + cTable = Null + cIndex = Null + cField = Null + cCurrent = Null + Else + sErr = "Unexpected '}'" + Goto SYNTAX_ERROR + Endif + Continue + Endif + + If cIndex Then + Else If cField Then + Else If cTable Then + If sLine = "{ Index" Then + cIndex = New Collection + cCurrent = cIndex + Continue + Else If sLine = "{ Field" Then + cField = New Collection + cCurrent = cField + Continue + Endif + Else + If sLine <> "{ Table" Then + sErr = "`{ Table` expected" + Goto SYNTAX_ERROR + Endif + cTable = New Collection + cTable["Fields"] = New Collection[] + cTable["Indexes"] = New Collection[] + cCurrent = cTable + Continue + Endif + + iPos = InStr(sLine, "=") + If iPos <= 1 Then Goto SYNTAX_ERROR + ' FIXME: Calling Eval() is not very secure! + Try cCurrent[Trim(Left(sLine, iPos - 1))] = Eval(Trim(Mid$(sLine, iPos + 1))) + If Error Then Goto SYNTAX_ERROR + + Next + + If cCurrent Then Goto SYNTAX_ERROR + + Close #hFile + Return + +SYNTAX_ERROR: + + If Not sErr Then sErr = "`" & sLine & "`" + Error.Raise("Syntax error in database template at line " & CStr(iLine) & ": " & sErr) + +CREATE_TABLE: + + sTable = cTable["Name"] + If Not sTable Then Return + If Me.Tables.Exist(sTable) Then Return + + 'Print "create table: "; sTable;; cTable["Type"];; cTable["Fields"].Count + hTable = Me.Tables.Add(sTable, cTable["Type"]) + For Each cField In cTable["Fields"] + iLength = 0 + Try iLength = cField["Length"] + 'Print "create field: "; cField["Name"];; cField["Type"];; iLength;; cField["Default"];; cField["Collation"] + sColl = cField["Collation"] + If sColl = "default" Then sColl = "" + hTable.Fields.Add(cField["Name"], cField["Type"], iLength, cField["Default"], sColl) + Next + + 'Print "primary key: "; cTable["PrimaryKey"].Join(",") + hTable.PrimaryKey = cTable["PrimaryKey"] + hTable.Update + + For Each cIndex In cTable["Indexes"] + 'Print "create index: "; cIndex["Name"];; cIndex["Fields"].Join(",");; cIndex["Unique"] + hTable.Indexes.Add(cIndex["Name"], cIndex["Fields"], cIndex["Unique"]) + Next + + Return + +End + +Public Sub GetTemplate() As String + + Dim hFile As File + Dim aTable As String[] + Dim hTable As Table + Dim sTable As String + Dim hField As Field + Dim hIndex As Index + Dim sTemplate As String + + hFile = Open String For Write + + aTable = New String[] + For Each hTable In Me.Tables + If hTable.System Then Continue + 'If sTable And If hTable.Name <> sTable Then Continue + aTable.Add(hTable.Name) + Next + + Print #hFile, TEMPLATE_MAGIC + + For Each sTable In aTable + + hTable = Me.Tables[sTable] + + Print #hFile, "{ Table" + Print #hFile, " Name="; Quote(hTable.Name) + If hTable.Type Then Print #hFile, " Type="; Quote(hTable.Type) + Print #hFile, " PrimaryKey=[\""; hTable.PrimaryKey.Join("\",\""); "\"]" + + For Each hField In hTable.Fields + + Print #hFile, " { Field" + Print #hFile, " Name="; Quote(hField.Name) + Print #hFile, " Type="; + + Select hField.Type + Case db.Blob + Print #hFile, "db.Blob" + Case db.Boolean + Print #hFile, "db.Boolean" + Case db.Date + Print #hFile, "db.Date" + Case db.Float + Print #hFile, "db.Float" + Case db.Integer + Print #hFile, "db.Integer" + Case db.Long + Print #hFile, "db.Long" + Case db.Serial + Print #hFile, "db.Serial" + Case db.String + Print #hFile, "db.String" + If hField.Length Then Print #hFile, " Length="; hField.Length + Case Else + Error.Raise("Unknown database field type") + End Select + + If Not IsNull(hField.Default) Then + Print #hFile, " Default="; + If hField.Type = db.String Then + Print #hFile, Quote(hField.Default) + Else If hField.Type = db.Boolean Then + Print #hFile, If(hField.Default, "True", "False") + Else If hField.Type = db.Date Then + Print #hFile, "CDate(\""; CStr(hField.Default); "\")" + Else + Print #hFile, CStr(hField.Default) + Endif + Endif + + If hField.Collation Then + Print #hFile, " Collation="; Quote(hField.Collation) + Endif + + Print #hFile, " }" + + Next + + For Each hIndex In hTable.Indexes + If hIndex.Primary Then Continue + Print #hFile, " { Index " + Print #hFile, " Name="; Quote(hIndex.Name) + Print #hFile, " Unique="; If(hIndex.Unique, "True", "False") + Print #hFile, " Fields=[\""; hIndex.Fields.Join("\",\""); "\"]" + Print #hFile, " }" + Next + + Print #hFile, "}" + + Next + + sTemplate = Close #hFile + Return sTemplate + +End + +Public Sub Copy() As Connection + + Dim hConn As Connection + + hConn = New Connection + hConn.Host = Me.Host + hConn.IgnoreCharset = Me.IgnoreCharset + hConn.Name = Me.Name + hConn.Password = Me.Password + hConn.Port = Me.Port + hConn.Timeout = Me.Timeout + hConn.Type = Me.Type + hConn.User = Me.User + + Return hConn + +End + + + +Private Function SQL_Read() As SQLRequest + + Return New SQLRequest(Me) + +End diff --git a/main/lib/db/gb.db/.src/Connections.class b/main/lib/db/gb.db/.src/Connections.class new file mode 100644 index 00000000..01ddf6dd --- /dev/null +++ b/main/lib/db/gb.db/.src/Connections.class @@ -0,0 +1,215 @@ +' Gambas class file + +Export + +Class Desktop + +Static Property Read Count As Integer +Static Property Read Key As String + +Static Private $cConn As New Collection +Static Private $aConn As String[] +Static Private $sKey As String + +Static Public Sub Exist(Name As String) As Boolean + + If $cConn.Exist(Name) Or If Exist(".../.connection" &/ Name & ".connection") Then Return True + +End + +Static Private Sub Init() + + Dim sFile As String + + If $aConn Then Return + $aConn = New String[] + + For Each sFile In Dir(".../.connection", "*.connection") + $aConn.Add(File.BaseName(sFile)) + Next + +End + +Static Private Sub ReadConnectionFile(sName As String) As Collection + + Dim sPath As String + Dim cData As New Collection + Dim hFile As File + Dim sLine As String + Dim bInConnection As Boolean + Dim aLine As String[] + Dim sType As String + + sPath = ".../.connection" &/ sName & ".connection" + If Not Exist(sPath) Then Return + + hFile = Open sPath + + While Not Eof(hFile) + + Line Input #hFile, sLine + If Left(sLine) = "#" Then Continue + If Left(sLine) = "[" Then + bInConnection = sLine = "[Connection]" + Continue + Endif + + If bInConnection Then + + aLine = Scan(sLine, "*=*") + If aLine.Count < 2 Then Continue + + Select Case LCase(aLine[0]) + + Case "type" + sType = LCase(UnQuote(aLine[1])) + cData["type"] = sType + + Case "host" + If sType Not Begins "sqlite" Then cData["host"] = UnQuote(aLine[1]) + + Case "path" + If sType Begins "sqlite" Then cData["host"] = UnQuote(aLine[1]) + + Case "port" + cData["port"] = UnQuote(aLine[1]) + + Case "database" + cData["name"] = UnQuote(aLine[1]) + + Case "ignorecharset" + cData["ignorecharset"] = LCase(aLine[1]) = "true" + + Case "user" + cData["user"] = UnQuote(aLine[1]) + + Case "rememberpassword" + cData["rememberpassword"] = LCase(aLine[1]) = "true" + + End Select + + Endif + + Wend + + Close #hFile + + Return cData + +End + +Static Private Sub GetConnectionFrom(sName As String, cData As Collection) As Connection + + Dim hConn As Connection + Dim bPassword As Boolean + + hConn = New Connection + hConn.Type = cData["type"] + hConn.Host = cData["host"] + hConn.Port = cData["port"] + hConn.Name = cData["name"] + hConn.IgnoreCharset = cData["ignorecharset"] + hConn.User = cData["user"] + + bPassword = cData["rememberpassword"] + + If bPassword Then + If Component.IsLoaded("gb.desktop") Then + Try hConn.Password = Desktop.Passwords[Application.Name &/ sName] + If Error Then + Error "gb.db: warning: "; Application.Name &/ sName; ": unable to retrieve connection password: "; Error.Text + Endif + Endif + Endif + + Return hConn + +End + + + +Static Public Sub _get(Name As String) As Connection + + Dim hConn As Connection + Dim cData As Collection + + Init() + + If $cConn.Exist(Name) Then Return $cConn[Name] + + cData = ReadConnectionFile(Name) + If Not cData Then Return + + hConn = GetConnectionFrom(Name, cData) + $cConn[Name] = hConn + Return hConn + +End + +Static Public Sub _next() As Connection + + Init() + + If Not Enum.Index Then Enum.Index = 0 + + If Enum.Index >= $aConn.Count Then + $sKey = "" + Enum.Stop + Else + Inc Enum.Index + $sKey = $aConn[Enum.Index - 1] + Return _get($sKey) + Endif + +End + +Static Private Function Key_Read() As String + + Return $sKey + +End + +Static Public Sub Create(Name As String) As Connection + + Dim cData As Collection + Dim hConn As Connection + Dim hConnDB As Connection + Dim sPath As String + + Init() + + cData = ReadConnectionFile(Name) + If Not cData Then Return + + hConn = GetConnectionFrom(Name, cData) + + sPath = ".../.connection" &/ Name & ".template" + If Exist(sPath) Then + + hConnDB = New Connection + hConnDB.Type = hConn.Type + hConnDB.Host = hConn.Host + hConnDB.Port = hConn.Port + hConnDB.Open + If Not hConnDB.Databases.Exist(hConn.Name) Then + hConnDB.Databases.Add(hConn.Name) + Endif + hConnDB.Close + + hConn.Open + hConn.ApplyTemplate(File.Load(sPath)) + hConn.Close + + Endif + + Return hConn + +End + + +Static Private Function Count_Read() As Integer + + Init() + Return $aConn.Count + +End diff --git a/main/lib/db/gb.db/.src/Main.module b/main/lib/db/gb.db/.src/Main.module new file mode 100644 index 00000000..ef5ca5da --- /dev/null +++ b/main/lib/db/gb.db/.src/Main.module @@ -0,0 +1,16 @@ +' Gambas module file + +Public Sub Main() + + Dim hConn As New Connection + + hConn.Type = "sqlite" + 'hConn.Host = "/home/benoit" + 'hConn.Name = "test.db" + hConn.Open + + 'Print hConn.SQL().Select("id", "color").From("test").Where("color like &1", "red").OrderBy("color")() + Print hConn.SQL().Select("COUNT(*) AS nRecord").From("test").Where("color like &1", "red").OrderBy("color")() + hConn.Close + +End diff --git a/main/lib/db/gb.db/.src/SQLRequest.class b/main/lib/db/gb.db/.src/SQLRequest.class new file mode 100644 index 00000000..c827c910 --- /dev/null +++ b/main/lib/db/gb.db/.src/SQLRequest.class @@ -0,0 +1,174 @@ +' Gambas class file + +Export + +Private $sTable As String +Private $sWhere As String +Private $sType As String +Private $aField As String[] +Private $aOrderBy As String[] +Private $hConn As Connection +Private $sOp As String + +Public Sub _new(Connection As Connection) + + $hConn = Connection + +End + +Public Sub Select(Optional Fields As Variant, ...) As SQLRequest + + Dim aField As String[] + + $sType = "SELECT" + + If Fields Then + Try aField = Fields + If Error Then + aField = [CStr(Fields)] + aField.Insert(Param.All) + Else + aField = aField.Copy() + Endif + Endif + + $aField = aField + Return Me + +End + +Public Sub Delete() As SQLRequest + + $sType = "DELETE" + Return Me + +End + + +Public Function From(Table As String) As SQLRequest + + $sTable = Table + Return Me + +End + + +Public Function Where((Where) As String, ...) As SQLRequest + + Dim aArg As New Variant[] + + Where = Trim(Where) + If Not Where Then Return Me + + If $sOp Then + If $sWhere Then + $sWhere &= " " & $sOp + Endif + $sOp = "" + Endif + + aArg.Add(Where) + aArg.Insert(Param.All) + $sWhere &= " (" & Object.Call($hConn, "Subst", aArg) & ")" + $sOp = "AND" + Return Me + +End + +Public Function OrderBy((OrderBy) As Variant, ...) As SQLRequest + + Dim aOrderBy As String[] + + If IsNull(OrderBy) Then Return Me + + Try aOrderBy = OrderBy + If Error Then + aOrderBy = [CStr(OrderBy)] + aOrderBy.Insert(Param.All) + Endif + + $aOrderBy = aOrderBy + + Return Me + +End + +Public Function Or() As SQLRequest + + $sOp = "OR" + Return Me + +End + +Public Function And() As SQLRequest + + $sOp = "AND" + Return Me + +End + +Public Sub Get() As String + + Return _call() + +End + + +Public Function _call() As String + + Dim sReq As String + Dim I As Integer + Dim aScan As String[] + Dim sField As String + Dim bDesc As Boolean + + If Not $sTable Then Error.Raise("No table specified") + + sReq = $sType + + If $aField Then + + sReq &= " " + + For I = 0 To $aField.Max + If I Then sReq &= "," + aScan = Scan($aField[i], "* AS *") + If aScan.Count = 2 And If InStr(aScan[1], " ") = 0 Then + sReq &= $aField[I] + Else + sReq &= $hConn.Quote($aField[I]) + Endif + Next + + Else If $sType = "SELECT" Then + + sReq &= " *" + + Endif + + sReq &= " FROM " & $hConn.Quote($sTable, True) + + If $sWhere Then sReq &= " WHERE" & $sWhere + + If $aOrderBy Then + + sReq &= " ORDER BY " + + For I = 0 To $aOrderBy.Max + If I Then sReq &= "," + sField = $aOrderBy[I] + If sField Ends " DESC" Then + sField = Left(sField, -5) + bDesc = True + Else + bDesc = False + Endif + sReq &= $hConn.Quote(sField) + If bDesc Then sReq &= " DESC" + Next + + Endif + + Return sReq + +End diff --git a/main/lib/db/gb_barray.h b/main/lib/db/gb_barray.h new file mode 100644 index 00000000..bd2b1be4 --- /dev/null +++ b/main/lib/db/gb_barray.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + gb_barray.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_BARRAY_H +#define __GB_BARRAY_H + +#include +#include "gb_alloc.h" + +typedef + int *BARRAY; + +#define BARRAY_SIZE sizeof(int) +#define BARRAY_NBITS (BARRAY_SIZE << 3) + +#define BARRAY_clear_all(_data, _size) memset((_data), 0, ((_size) + BARRAY_NBITS - 1) / BARRAY_NBITS * BARRAY_SIZE) + +#define BARRAY_create(_pdata, _size) GB.Alloc(POINTER((_pdata)), ((_size) + BARRAY_NBITS - 1) / BARRAY_NBITS * BARRAY_SIZE) + +#define BARRAY_delete(_pdata) GB.Free(POINTER((_pdata))) + +#define BARRAY_set(_data, _bit) (_data[(_bit) / BARRAY_NBITS] |= (1 << ((_bit) & (BARRAY_NBITS - 1)))) +#define BARRAY_clear(_data, _bit) ((_data)[(_bit) / BARRAY_NBITS] &= ~(1 << ((_bit) & (BARRAY_NBITS - 1)))) +#define BARRAY_invert(_data, _bit) ((_data)[(_bit) / BARRAY_NBITS] ^= (1 << ((_bit) & (BARRAY_NBITS - 1)))) + +#define BARRAY_test(_data, _bit) (((_data)[(_bit) / BARRAY_NBITS] & (1 << ((_bit) & (BARRAY_NBITS - 1)))) != 0) + +#define BARRAY_is_void(_data, _size) \ +({ \ + int i, v = 0; \ + int size = ((_size) + BARRAY_NBITS - 1) / BARRAY_NBITS; \ + for (i = 0; !v && i < size; i++) \ + v |= (_data)[i]; \ + v == 0; \ +}) + +#endif diff --git a/main/lib/db/main.c b/main/lib/db/main.c new file mode 100644 index 00000000..1d071b57 --- /dev/null +++ b/main/lib/db/main.c @@ -0,0 +1,699 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include +#include + +#include "gb_common.h" + +#include "c_subcollection.h" +#include "CConnection.h" +#include "CDatabase.h" +#include "CUser.h" +#include "CTable.h" +#include "CField.h" +#include "CIndex.h" +#include "CResult.h" +#include "CResultField.h" + +#include "sqlite.h" +#include "main.h" + + +GB_INTERFACE GB EXPORT; + + +static DB_DRIVER *_drivers[MAX_DRIVER]; +static int _drivers_count = 0; +static char *_query = NULL; + +#define TEMP_MAX 64 +static char _temp[TEMP_MAX]; +static int _temp_len; + +static bool _debug = FALSE; +static const char *_try_another = NULL; + +DB_DATABASE *DB_CurrentDatabase = NULL; + +int DB_CheckNameWith(const char *name, const char *msg, const char *more) +{ + unsigned char c; + const char *p = name; + + if (!name || !*name) + { + GB.Error("Void &1 name", msg); + return TRUE; + } + + while ((c = *p++)) + { + if ((c >= 'A' && c <='Z') || (c >= 'a' && c <='z') || (c >= '0' && c <= '9') || c == '_') + continue; + + if (more && index(more, c)) + continue; + + GB.Error("Bad &1 name: &2", msg, name); + return TRUE; + } + + return FALSE; +} + + +// void DB_LowerString(char *s) +// { +// register char c; +// +// for(;;) +// { +// c = *s; +// if (!c) +// return; +// *s++ = tolower(c); +// } +// } + +void DB_FreeStringArray(char ***parray) +{ + int i; + char **array = *parray; + + if (!*parray) + return; + + for (i = 0; i < GB.Count(array); i++) + GB.FreeString(&array[i]); + + GB.FreeArray(parray); +} + +GB_ARRAY DB_StringArrayToGambasArray(char **array) +{ + GB_ARRAY garray; + int i, n; + char *str; + + n = GB.Count(array); + + GB.Array.New(&garray, GB_T_STRING, n); + + for (i = 0; i < n; i++) + { + str = GB.NewZeroString(array[i]); + *((char **)GB.Array.Get(garray, i)) = str; + } + + return garray; +} + +int DB_FindStringArray(char **array, const char *elt) +{ + int i; + + for (i = 0; i < GB.Count(array); i++) + { + if (!strcasecmp(elt, array[i])) + return i; + } + + return -1; +} + + +static void DB_Register(DB_DRIVER *driver) +{ + if (_drivers_count >= MAX_DRIVER) + return; + + _drivers[_drivers_count] = driver; + _drivers_count++; +} + + +void DB_TryAnother(const char *driver) +{ + _try_another = driver; +} + +static DB_DRIVER *DB_GetDriver(const char *type) +{ + int i; + char comp[type ? strlen(type) + 8 : 1]; + + if (!type) + { + GB.Error("Driver name missing"); + return NULL; + } + + strcpy(comp, "gb.db."); + strcat(comp, type); + + GB.Component.Load(comp); + GB.Error(NULL); // reset the error flag; + + for (i = 0; i < _drivers_count; i++) + { + if (strcasecmp(_drivers[i]->name, type) == 0) + return _drivers[i]; + } + + GB.Error("Cannot find driver for database: &1", type); + return NULL; +} + + +bool DB_Open(DB_DESC *desc, DB_DRIVER **driver, DB_DATABASE *db) +{ + DB_DRIVER *d; + int res; + int timeout; + const char *type = desc->type; + + timeout = db->timeout; + CLEAR(db); + db->timeout = timeout; + + for(;;) + { + d = DB_GetDriver(type); + if (!d) + return TRUE; + + *driver = d; + + _try_another = NULL; + res = (*d->Open)(desc, db); + if (!res) + return FALSE; + + if (!_try_another) + return TRUE; + + type = _try_another; + } +} + +void DB_Format(DB_DRIVER *driver, GB_VALUE *arg, DB_FORMAT_CALLBACK add) +{ + static char buffer[32]; + + char *s; + int l; + int i; + + if (arg->type == GB_T_VARIANT) + GB.Conv(arg, ((GB_VARIANT *)arg)->value.type); + + if (arg->type == (GB_TYPE)CLASS_Blob) + { + (*driver->FormatBlob)((DB_BLOB *)(((GB_OBJECT *)arg)->value), add); + return; + } + + if ((arg->type == GB_T_DATE && arg->_date.value.date == 0 && arg->_date.value.time == 0) + || (arg->type == GB_T_STRING && arg->_string.value.len == 0) + || (arg->type == GB_T_NULL)) + { + add("NULL", 4); + return; + } + + if (!(*driver->Format)(arg, add)) + { + switch (arg->type) + { + case GB_T_BOOLEAN: + + if (VALUE((GB_BOOLEAN *)arg)) + add("TRUE", 4); + else + add("FALSE", 5); + + return; + + case GB_T_BYTE: + case GB_T_SHORT: + case GB_T_INTEGER: + + l = sprintf(buffer, "%d", VALUE((GB_INTEGER *)arg)); + add(buffer, l); + return; + + case GB_T_LONG: + + l = sprintf(buffer, "%" PRId64, VALUE((GB_LONG *)arg)); + add(buffer, l); + return; + + case GB_T_FLOAT: + + GB.NumberToString(FALSE, VALUE((GB_FLOAT *)arg), NULL, &s, &l); + add(s, l); + + return; + + case GB_T_STRING: + case GB_T_CSTRING: + + s = VALUE((GB_STRING *)arg).addr + VALUE((GB_STRING *)arg).start; + l = VALUE((GB_STRING *)arg).len; + + add("'", 1); + + for (i = 0; i < l; i++, s++) + { + add(s, 1); + if (*s == '\'' || *s == '\\') + add(s, 1); + } + + add("'", 1); + return; + + default: + fprintf(stderr, "gb.db: DB_Format: unsupported datatype: %d\n", (int)arg->type); + return; + } + } +} + + +void DB_FormatVariant(DB_DRIVER *driver, GB_VARIANT_VALUE *arg, DB_FORMAT_CALLBACK add) +{ + GB_VALUE value; + + value.type = arg->type; + + switch(arg->type) + { + case GB_T_NULL: + break; + + case GB_T_STRING: + case GB_T_CSTRING: + { + GB_STRING *val = (GB_STRING *)(void *)&value; + val->value.addr = arg->value._string; + val->value.start = 0; + if (arg->type == GB_T_STRING) + val->value.len = GB.StringLength(arg->value._string); + else + val->value.len = strlen(arg->value._string); + } + break; + + default: + value.type = GB_T_VARIANT; + value._variant.value = *arg; + GB.Conv(&value, arg->type); + break; + } + + DB_Format(driver, &value, add); +} + + +static int query_narg; +static GB_VALUE *query_arg; +static DB_DRIVER *query_driver; + +static void mq_add_param(int index) +{ + if (index < 1 || index > query_narg) + return; + + DB_Format(query_driver, &query_arg[index - 1], (DB_FORMAT_CALLBACK)GB.SubstAddCallback); +} + +char *DB_MakeQuery(DB_DRIVER *driver, const char *pattern, int len, int narg, GB_VALUE *arg) +{ + char *query; + + query_narg = narg; + query_arg = arg; + query_driver = driver; + + if (narg == 0) + query = GB.TempString(pattern, len); + else + query = GB.SubstStringAdd(pattern, len, mq_add_param); + + if (!query || *query == 0) + { + GB.Error("Void query"); + return NULL; + } + else + return query; +} + + +void q_init(void) +{ + GB.FreeString(&_query); + _query = NULL; + _temp_len = 0; +} + +static void q_dump_temp(void) +{ + if (!_temp_len) + return; + + _query = GB.AddString(_query, _temp, _temp_len); + _temp_len = 0; +} + +void q_add_length(const char *str, int len) +{ + if (!str) + return; + + if ((_temp_len + len) > TEMP_MAX) + q_dump_temp(); + + if (len > TEMP_MAX) + _query = GB.AddString(_query, str, len); + else + { + memcpy(&_temp[_temp_len], str, len); + _temp_len += len; + } +} + +void q_add(const char *str) +{ + if (str) + q_add_length(str, strlen(str)); +} + +void q_add_lower(const char *str) +{ + int i, len; + char *lstr; + + if (!str) + return; + + len = strlen(str); + + if (len <= 0) + return; + + lstr = GB.TempString(str, len); + for (i = 0; i < len; i++) + lstr[i] = GB.ToLower(lstr[i]); + + q_add_length(lstr, len); +} + +char *q_get(void) +{ + q_dump_temp(); + return _query; +} + +char *q_steal(void) +{ + char *s; + q_dump_temp(); + s = _query; + _query = NULL; + return s; +} + +int q_length(void) +{ + return GB.StringLength(_query) + _temp_len; +} + +void DB_SetDebug(int debug) +{ + _debug = debug; +} + +int DB_IsDebug(void) +{ + return _debug; +} + +static char *_quote; +DB_SUBST_CALLBACK _quote_cb; + +static void ss_get_param(int index, char **str, int *len) +{ + (*_quote_cb)(index, str, len, _quote[index]); +} + +char *DB_SubstString(const char *pattern, int len_pattern, DB_SUBST_CALLBACK add) +{ + int i; + unsigned char last_c, c; + int n; + char quote[20] = {0}; + + c = 0; + + len_pattern--; + for (i = 0; i < len_pattern; i++) + { + last_c = c; + c = pattern[i]; + + if (c == '&') + { + c = pattern[++i]; + if (c == '&') + continue; + if (isdigit(c)) + { + n = c - '0'; + c = pattern[++i]; + if (isdigit(c)) + { + n = n * 10 + c - '0'; + i++; + } + quote[n] = last_c; + } + } + } + + _quote_cb = add; + _quote = quote; + + return GB.SubstString(pattern, len_pattern, ss_get_param); +} + +char *DB_QuoteString(const char *str, int len, char quote) +{ + char *res, *p, c; + int len_res; + int i; + + len_res = len; + for (i = 0; i < len; i++) + { + if (str[i] == quote) + len_res++; + } + + res = GB.TempString(NULL, len_res); + + p = res; + for (i = 0; i < len; i++) + { + c = str[i]; + *p++ = c; + if (c == quote || c == '\\') + *p++ = c; + } + *p = 0; + + return res; +} + +char *DB_UnquoteString(const char *str, int len, char quote) +{ + char *res, *p, c; + int len_res; + int i; + + if (len >= 2 && str[0] == quote && str[len - 1] == quote) + { + str++; + len -= 2; + } + + if (!len) + return ""; + + len_res = len; + for (i = 0; i < (len - 1); i++) + { + if ((str[i] == quote && str[i + 1] == quote) || str[i] == '\\') + { + len_res--; + i++; + } + } + + res = GB.TempString(NULL, len_res); + + p = res; + for (i = 0; i < len; i++) + { + c = str[i]; + if (c == quote && (i + 1) < len && str[i + 1] == quote) + i++; + else if (c == '\\' && (i + 1) < len) + { + c = str[i + 1]; + i++; + } + + *p++ = c; + } + *p = 0; + + return res; +} + +DB_DATABASE *DB_GetCurrent() +{ + return DB_CurrentDatabase; +} + +char *DB_GetQuotedTable(DB_DRIVER *driver, DB_DATABASE *db, const char *table, int len) +{ + char *point = NULL; + char *res; + const char *quote; + + if (!table) + return ""; + + if (len < 0) + len = strlen(table); + + if (len == 0) + return ""; + + if (db->flags.schema) + point = index(table, '.'); + + quote = (*driver->GetQuote)(); + + + if (!point) + { + res = GB.TempString(NULL, len + 2); + sprintf(res, "%s%.*s%s", quote, len, table, quote); + } + else + { + int len_schema = (int)(point - table); + res = GB.TempString(NULL, len + 4); + sprintf(res, "%s%.*s%s.%s%.*s%s", quote, len_schema, table, quote, quote, len - len_schema - 1, point + 1, quote); + } + + return res; +} + + +GB_DESC *GB_CLASSES [] EXPORT = +{ + SubCollectionDesc, + CIndexDesc, + CFieldDesc, + CTableFieldsDesc, + CTableIndexesDesc, + CTableDesc, + CUserDesc, + CDatabaseDesc, + CConnectionUsersDesc, + CConnectionDatabasesDesc, + CConnectionTablesDesc, + CConnectionDesc, + CDBDesc, + CBlobDesc, + CResultFieldDesc, + CResultFieldsDesc, + CResultDesc, + NULL +}; + +void *GB_DB_1[] EXPORT = { + + (void *)1, + + (void *)DB_Register, + (void *)DB_Format, + (void *)DB_FormatVariant, + (void *)DB_IsDebug, + (void *)DB_TryAnother, + (void *)DB_SubstString, + (void *)DB_QuoteString, + (void *)DB_UnquoteString, + (void *)DB_GetCurrent, + + (void *)q_init, + (void *)q_add, + (void *)q_add_lower, + (void *)q_add_length, + (void *)q_get, + (void *)q_steal, + (void *)q_length, + + (void *)DB_FindStringArray, + + NULL +}; + + +int EXPORT GB_INIT(void) +{ + char *env = getenv("GB_DB_DEBUG"); + + if (env && strcmp(env, "0")) + DB_SetDebug(TRUE); + + DB_Register(&DB_sqlite_pseudo_driver); + return 0; +} + + +void EXPORT GB_EXIT() +{ + GB.FreeString(&_query); +} + diff --git a/main/lib/db/main.h b/main/lib/db/main.h new file mode 100644 index 00000000..3acbdefa --- /dev/null +++ b/main/lib/db/main.h @@ -0,0 +1,64 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" +#include "gb.db.h" +#include "c_subcollection.h" +#include "CResult.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern DB_DATABASE *DB_CurrentDatabase; +#endif + +#define MAX_DRIVER 8 + +bool DB_Open(DB_DESC *desc, DB_DRIVER **driver, DB_DATABASE *db); +char *DB_MakeQuery(DB_DRIVER *driver, const char *pattern, int len, int narg, GB_VALUE *arg); +void DB_Format(DB_DRIVER *driver, GB_VALUE *arg, DB_FORMAT_CALLBACK func); +void DB_FormatVariant(DB_DRIVER *driver, GB_VARIANT_VALUE *arg, DB_FORMAT_CALLBACK func); +char *DB_GetQuotedTable(DB_DRIVER *driver, DB_DATABASE *db, const char *table, int len_table); + +void DB_LowerString(char *s); +int DB_CheckNameWith(const char *name, const char *msg, const char *more); +#define DB_CheckName(_name, _msg) DB_CheckNameWith(_name, _msg, NULL) +void DB_FreeStringArray(char ***parray); +GB_ARRAY DB_StringArrayToGambasArray(char **array); +int DB_FindStringArray(char **array, const char *elt); +void DB_SetDebug(int debug); +int DB_IsDebug(void); +void DB_TryAnother(const char *); + +void q_init(void); +void q_add(const char *str); +void q_add_lower(const char *str); +void q_add_length(const char *str, int len); +char *q_get(void); +char *q_steal(void); +int q_length(void); + +#endif /* __MAIN_H */ diff --git a/main/lib/db/sqlite.c b/main/lib/db/sqlite.c new file mode 100644 index 00000000..b218f9fb --- /dev/null +++ b/main/lib/db/sqlite.c @@ -0,0 +1,216 @@ +/*************************************************************************** + + sqlite.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __SQLITE_C + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + + +/* Internal function to check whether a file is a sqlite database file */ + +static bool is_sqlite2_database(char *filename) +{ + FILE* fp; + bool res; + char magic_text[48]; + + fp = fopen(filename, "r"); + if (!fp) + return FALSE; + + res = fread(magic_text, 1, 47, fp) == 47; + fclose(fp); + + if (!res) + return FALSE; + + magic_text[47] = '\0'; + + if (strcmp(magic_text, "** This file contains an SQLite 2.1 database **")) + return FALSE; + + return TRUE; +} + +static bool is_sqlite3_database(char *filename) +{ + FILE *fp; + bool res; + char magic_text[16]; + + fp = fopen(filename, "r"); + if (!fp) + return FALSE; + + res = fread(magic_text, 1, 15, fp) == 15; + fclose(fp); + + if (!res) + return FALSE; + + magic_text[15] = '\0'; + + if (strcmp(magic_text, "SQLite format 3")) + return FALSE; + + return TRUE; +} + +static bool IsDatabaseFile(char *filename) +{ + return is_sqlite3_database(filename) || is_sqlite2_database(filename); +} + +static char *FindDatabase(char *name, char *hostName) +{ + char *dbhome = NULL; + char *fullpath = NULL; + + /* Does Name includes fullpath */ + if (strcmp(basename(name), name)) + { + if (IsDatabaseFile(name)) + fullpath = GB.NewZeroString(name); + + return fullpath; + } + + /* Hostname contains home area */ + fullpath = GB.NewZeroString(hostName); + fullpath = GB.AddChar(fullpath, '/'); + fullpath = GB.AddString(fullpath, name, 0); + if (IsDatabaseFile(fullpath)) + { + return fullpath; + } + GB.FreeString(&fullpath); + + /* Check the GAMBAS_SQLITE_DBHOME setting */ + dbhome = getenv("GAMBAS_SQLITE_DBHOME"); + + if (dbhome != NULL) + { + fullpath = GB.NewZeroString(dbhome); + fullpath = GB.AddChar(fullpath, '/'); + fullpath = GB.AddString(fullpath, name, 0); + + if (IsDatabaseFile(fullpath)) + { + return fullpath; + } + } + + fullpath = GB.NewZeroString(GB.TempDir()); + fullpath = GB.AddString(fullpath, "/sqlite/", 0); + fullpath = GB.AddString(fullpath, name, 0); + + if (IsDatabaseFile(fullpath)) + { + return fullpath; + } + + GB.FreeString(&fullpath); + return NULL; +} + +//------------------------------------------------------------------------- + +static int open_database(DB_DESC *desc, DB_DATABASE * db) +{ + char *host; + char *db_fullpath = NULL; + bool ver2 = FALSE; + + if (!desc->name) // memory database + goto __SQLITE; + + host = desc->host; + if (!host) + host = ""; + + db_fullpath = FindDatabase(desc->name, host); + if (!db_fullpath) + { + GB.Error("Unable to locate database `&1` in `&2`", desc->name, host); + return TRUE; + } + + ver2 = is_sqlite2_database(db_fullpath); + + GB.FreeString(&db_fullpath); + + if (ver2) + goto __SQLITE2; + else + goto __SQLITE3; + +__SQLITE: + + GB.Component.Load("gb.db.sqlite3"); + GB.Error(NULL); + + if (GB.Component.Exist("gb.db.sqlite3")) + goto __SQLITE3; + else + goto __SQLITE2; + +__SQLITE2: + DB_TryAnother("sqlite2"); + return TRUE; + +__SQLITE3: + DB_TryAnother("sqlite3"); + return TRUE; +} + + +//------------------------------------------------------------------------- + +static int database_is_system(DB_DATABASE * db, const char *name) +{ + return FALSE; +} + +/***************************************************************************** + + The driver interface + +*****************************************************************************/ + +DB_DRIVER DB_sqlite_pseudo_driver = +{ + .name = "sqlite", + .Open = open_database, + .Database.IsSystem = database_is_system +}; + diff --git a/main/lib/db/sqlite.h b/main/lib/db/sqlite.h new file mode 100644 index 00000000..2122b335 --- /dev/null +++ b/main/lib/db/sqlite.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + sqlite.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __SQLITE_H +#define __SQLITE_H + +#include "gambas.h" +#include "gb_common.h" +#include "gb.db.h" + +#ifndef __SQLITE_C +extern DB_DRIVER DB_sqlite_pseudo_driver; +#endif + +#endif diff --git a/main/lib/debug/CDebug.c b/main/lib/debug/CDebug.c new file mode 100644 index 00000000..7154f03d --- /dev/null +++ b/main/lib/debug/CDebug.c @@ -0,0 +1,294 @@ +/*************************************************************************** + + CDebug.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDEBUG_C + +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "gb_limit.h" +#include "gb.debug.h" +#include "CDebug.h" + +/*#define DEBUG*/ + + +DECLARE_EVENT(EVENT_Read); + +static int _started = FALSE; +static int _fdr = -1; +static int _fdw = -1; +static CDEBUG *_debug_object = NULL; + +#define BUFFER_SIZE DEBUG_OUTPUT_MAX_SIZE +static char *_buffer = NULL; +static int _buffer_left; + +static void callback_read(int fd, int type, intptr_t param) +{ + int n, i, p; + + for(;;) + { + fcntl(_fdr, F_SETFL, fcntl(_fdr, F_GETFL) | O_NONBLOCK); + + if (_buffer_left) + { + n = read(_fdr, &_buffer[_buffer_left], BUFFER_SIZE - _buffer_left); + if (n < 0) + n = 0; + + n += _buffer_left; + _buffer_left = 0; + } + else + n = read(_fdr, _buffer, BUFFER_SIZE); + + if (n <= 0) + { + //usleep(10000); // the callback is called again and again even if there is nothing to read, why? + break; + } + + p = 0; + + for (i = 0; i < n; i++) + { + if (_buffer[i] == '\n') + { + /*fprintf(stderr, "CDEBUG_read: <<< %.*s >>>\n", i - p, &_buffer[p]);*/ + GB.Raise(_debug_object, EVENT_Read, 1, GB_T_STRING, i <= p ? NULL : &_buffer[p], i - p); + if (!_buffer) + break; + p = i + 1; + } + } + + if (!_buffer) + break; + + if (p == 0 && n >= BUFFER_SIZE) + { + GB.Raise(_debug_object, EVENT_Read, 1, GB_T_STRING, _buffer, BUFFER_SIZE); + if (!_buffer) + break; + _buffer_left = 0; + } + else + { + _buffer_left = n - p; + if (p && n > p) + memmove(_buffer, &_buffer[p], _buffer_left); + } + } + + fcntl(_fdr, F_SETFL, fcntl(_fdr, F_GETFL) & ~O_NONBLOCK); +} + +// static void callback_read(int fd, int type, intptr_t param) +// { +// char *buffer = NULL; +// char tmp[256]; +// int n; +// +// for(;;) +// { +// n = read(_fdr, tmp, 256); +// if (n <= 0) +// break; +// GB.AddString(&buffer, tmp, n); +// } +// +// GB.Raise(_debug_object, EVENT_Read, 1, GB_T_STRING, buffer, GB.StringLength(buffer)); +// } + +static char *input_fifo(char *path) +{ + sprintf(path, "/tmp/gambas.%d/%d.in", getuid(), getpid()); + return path; +} + +static char *output_fifo(char *path) +{ + sprintf(path, "/tmp/gambas.%d/%d.out", getuid(), getpid()); + return path; +} + +BEGIN_METHOD_VOID(CDEBUG_begin) + + char path[PATH_MAX]; + char name[16]; + + signal(SIGPIPE, SIG_IGN); + + input_fifo(path); + unlink(path); + if (mkfifo(path, 0600)) + { + GB.Error("Cannot create input fifo in /tmp: &1", strerror(errno)); + return; + } + + output_fifo(path); + unlink(path); + if (mkfifo(path, 0600)) + { + GB.Error("Cannot create output fifo in /tmp: &1", strerror(errno)); + return; + } + + sprintf(name, "%d", getpid()); + GB.ReturnNewZeroString(name); + +END_METHOD + + +BEGIN_METHOD_VOID(CDEBUG_start) + + char path[DEBUG_FIFO_PATH_MAX]; + int i; + + if (_started) + return; + + for (i = 0; i < 25; i++) + { + _fdw = open(output_fifo(path), O_WRONLY | O_NONBLOCK); + if (_fdw >= 0) + break; + usleep(20000); + } + + if (_fdw < 0) + { + GB.Error("Unable to open fifo"); + return; + } + + _fdr = open(input_fifo(path), O_RDONLY | O_NONBLOCK); + fcntl(_fdr, F_SETFL, fcntl(_fdr, F_GETFL) & ~O_NONBLOCK); + + _debug_object = GB.New(GB.FindClass("Debug"), "Debug", NULL); + GB.Ref(_debug_object); + + GB.Alloc(POINTER(&_buffer), BUFFER_SIZE); + _buffer_left = 0; + + GB.Watch(_fdr, GB_WATCH_READ, (void *)callback_read, 0); + + _started = TRUE; + +END_METHOD + + +BEGIN_METHOD_VOID(CDEBUG_stop) + + if (!_started) + return; + + GB.Watch(_fdr, GB_WATCH_NONE, (void *)callback_read, 0); + GB.Free(POINTER(&_buffer)); + + GB.Unref(POINTER(&_debug_object)); + + close(_fdw); + close(_fdr); + + _fdw = _fdr = -1; + _started = FALSE; + +END_METHOD + + +BEGIN_METHOD_VOID(CDEBUG_end) + + char path[DEBUG_FIFO_PATH_MAX]; + + CALL_METHOD_VOID(CDEBUG_stop); + + unlink(input_fifo(path)); + unlink(output_fifo(path)); + + signal(SIGPIPE, SIG_DFL); + +END_METHOD + + +BEGIN_METHOD(CDEBUG_write, GB_STRING data) + + const char *data = STRING(data); + int len = LENGTH(data); + + if (_fdw < 0) + return; + + if (data && len > 0) + { + if (write(_fdw, data, len) != len) + goto __ERROR; + } + if (write(_fdw, "\n", 1) != 1) + goto __ERROR; + + return; + +__ERROR: + + fprintf(stderr, "gb.debug: warning: unable to send data to the debugger: %s\n", strerror(errno)); + +END_METHOD + + +BEGIN_METHOD(Debug_GetSignal, GB_INTEGER signal) + + GB.ReturnNewZeroString(strsignal(VARG(signal))); + +END_METHOD + + +GB_DESC CDebugDesc[] = +{ + GB_DECLARE("Debug", sizeof(CDEBUG)), + GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("_exit", NULL, CDEBUG_end, NULL), + + GB_STATIC_METHOD("Begin", "s", CDEBUG_begin, NULL), + GB_STATIC_METHOD("End", NULL, CDEBUG_end, NULL), + GB_STATIC_METHOD("Start", NULL, CDEBUG_start, NULL), + GB_STATIC_METHOD("Stop", NULL, CDEBUG_stop, NULL), + + GB_STATIC_METHOD("GetSignal", "s", Debug_GetSignal, "(Signal)i"), + + GB_STATIC_METHOD("Write", NULL, CDEBUG_write, "(Data)s"), + + GB_EVENT("Read", NULL, "(Data)s", &EVENT_Read), + + GB_END_DECLARE +}; + diff --git a/main/lib/debug/CDebug.h b/main/lib/debug/CDebug.h new file mode 100644 index 00000000..ae393f21 --- /dev/null +++ b/main/lib/debug/CDebug.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + CDebug.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDEBUG_H +#define __CDEBUG_H + +#include "gambas.h" + +typedef + struct { + GB_BASE ob; + } + CDEBUG; + +#ifndef __CDEBUG_C +extern GB_DESC CDebugDesc[]; +#endif + +#endif diff --git a/main/lib/debug/Makefile.am b/main/lib/debug/Makefile.am new file mode 100644 index 00000000..d0c201af --- /dev/null +++ b/main/lib/debug/Makefile.am @@ -0,0 +1,18 @@ +COMPONENT = gb.debug +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.debug.la + +gb_debug_la_LIBADD = @GBX_THREAD_LIB@ +gb_debug_la_LDFLAGS = -module @LD_FLAGS@ +gb_debug_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx @GBX_THREAD_INC@ @INCLTDL@ $(AM_CFLAGS) + +gb_debug_la_SOURCES = \ + CDebug.h CDebug.c \ + print.h print.c \ + debug.h debug.c \ + profile.h profile.c \ + main.h main.c \ + gb.debug.h + + diff --git a/main/lib/debug/debug.c b/main/lib/debug/debug.c new file mode 100644 index 00000000..cb81e2d9 --- /dev/null +++ b/main/lib/debug/debug.c @@ -0,0 +1,1136 @@ +/*************************************************************************** + + debug.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __DEBUG_C + +// Do not include gbx_debug.h +#define __GBX_DEBUG_H + +#include "gb_common.h" +#include "gambas.h" + +#include +#include +#include + +#include "gb_error.h" +#include "gbx_type.h" +#include "gb_limit.h" +#include "gbx_stack.h" +#include "gbx_class.h" +#include "gbx_exec.h" +#include "gbx_local.h" +#include "gbx_object.h" + +#include "gbx_eval.h" + +#include "print.h" + +#include "debug.h" + +//#define DEBUG_ME + +DEBUG_INFO DEBUG_info = { 0 }; +GB_DEBUG_INTERFACE *DEBUG_interface; +char DEBUG_buffer[DEBUG_BUFFER_MAX + 1]; + +static DEBUG_BREAK *Breakpoint; +static bool Error; + +static EVAL_INTERFACE EVAL; + +static int _fdr; +static int _fdw; +static FILE *_out; +static FILE *_in; +static bool _fifo; + +#define EXEC_current (*(STACK_CONTEXT *)GB_DEBUG.GetExec()) + +#ifdef DEBUG_ME +#define WARNING(_msg, ...) fprintf(stderr, "W\t" _msg "\n", ##__VA_ARGS__) +#define INFO(_msg, ...) fprintf(stderr, "I\t" _msg "\n", ##__VA_ARGS__) +#else +#define WARNING(_msg, ...) fprintf(_out, "W\t" _msg "\n", ##__VA_ARGS__) +#define INFO(_msg, ...) fprintf(_out, "I\t" _msg "\n", ##__VA_ARGS__) +#endif + +static void init_eval_interface() +{ + static bool init = FALSE; + + if (!init) + { + GB.GetInterface("gb.eval", EVAL_INTERFACE_VERSION, &EVAL); + init = TRUE; + } +} + +void DEBUG_break_on_next_line(void) +{ + DEBUG_info.stop = TRUE; + DEBUG_info.leave = FALSE; + DEBUG_info.fp = NULL; + DEBUG_info.bp = NULL; + DEBUG_info.pp = NULL; +} + + +static void signal_user(int sig) +{ + signal(SIGUSR1, signal_user); + + #ifdef DEBUG_ME + fprintf(stderr, "Got SIGUSR1\n"); + #endif + + /*CAPP_got_signal();*/ + DEBUG_break_on_next_line(); +} + + +bool DEBUG_calc_line_from_position(CLASS *class, FUNCTION *func, PCODE *addr, ushort *line) +{ + int lo, hi; + int mid; + ushort pos = addr - func->code; + ushort *post; + + if (func->debug) + { + post = func->debug->pos; + + lo = 0; + hi = func->debug->nline - 1; + + while (lo < hi) + { + mid = (lo + hi) >> 1; + if (pos < post[mid]) + { + hi = mid; + } + else if (pos >= post[mid + 1]) + { + lo = mid + 1; + } + else + { + *line = mid + func->debug->line; + return FALSE; + } + } + } + + return TRUE; +} + + +static bool calc_position_from_line(CLASS *class, ushort line, FUNCTION **function, PCODE **addr) +{ + int i; + ushort pos, pos_after; + FUNCTION *func = NULL; + FUNC_DEBUG *debug = NULL; + + // Look at the first functions last, because global variables can be declared and + // initialized everywhere in the file, and initialization function is the first one. + + for(i = class->load->n_func - 1; i >= 0; i--) + { + func = &class->load->func[i]; + debug = func->debug; + //fprintf(stderr, "calc_position_from_line: %s (%d -> %d) / %d\n", debug->name, debug->line, debug->line + debug->nline - 1, line); + if (debug && line >= debug->line && line < (debug->line + debug->nline)) + break; + } + + if (i < 0) + return TRUE; + + //fprintf(stderr, "calc_position_from_line: %s OK\n", debug->name); + + line -= debug->line; + + for(;;) + { + pos = debug->pos[line]; + pos_after = debug->pos[line + 1]; + if (pos != pos_after) + break; + + line++; + if (line >= debug->nline) + return TRUE; + } + + *function = func; + *addr = &func->code[pos]; + + /*printf("%s.%d -> %04X\n", class->name, line + debug->line, **addr);*/ + + return FALSE; +} + + +DEBUG_INFO *DEBUG_init(GB_DEBUG_INTERFACE *debug, bool fifo, const char *fifo_name) +{ + char path[DEBUG_FIFO_PATH_MAX]; + char name[16]; + //int i; + + //if (!EXEC_debug) + // return; + + DEBUG_interface = debug; + _fifo = fifo; + + if (_fifo) + { + if (!fifo_name) + { + sprintf(name, "%d", getppid()); + fifo_name = name; + } + + snprintf(path, sizeof(path), "/tmp/gambas.%d/%s.out", getuid(), fifo_name); + + /*for (i = 0; i < 20; i++) + { + _fdr = open(path, O_RDONLY | O_NONBLOCK); + if (_fdr >= 0) + break; + usleep(10000); + } + if (_fdr < 0) + return NULL;*/ + + _fdr = open(path, O_RDONLY | O_CLOEXEC); + if (_fdr < 0) + { + fprintf(stderr, "gb.debug: %s: %s\n", strerror(errno), path); + return NULL; + } + + snprintf(path, sizeof(path), "/tmp/gambas.%d/%s.in", getuid(), fifo_name); + + _fdw = open(path, O_WRONLY | O_CLOEXEC); + if (_fdw < 0) + { + fprintf(stderr, "gb.debug: %s: %s\n", strerror(errno), path); + return NULL; + } + + _in = fdopen(_fdr, "r"); + _out = fdopen(_fdw, "w"); + + if (!_in || !_out) + { + fprintf(stderr, "gb.debug: %s: %s\n", strerror(errno), path); + return NULL; + } + //ERROR_panic("Cannot open fifos"); + + setlinebuf(_in); + //setvbuf(_in, NULL, _IONBF, 0); + setlinebuf(_out); + //setvbuf(_out, NULL, _IONBF, 0); + } + else + { + _in = stdin; + _out = stdout; + } + + //ARRAY_create(&Breakpoint); + GB.NewArray(&Breakpoint, sizeof(DEBUG_BREAK), 16); + signal(SIGUSR1, signal_user); + signal(SIGPIPE, SIG_IGN); + + setlinebuf(_out); + + return &DEBUG_info; +} + +void DEBUG_exit(void) +{ + GB.FreeArray(&Breakpoint); + + /* Don't do it, it blocks! + + if (EXEC_fifo) + { + fclose(_in); + fclose(_out); + } + + */ +} + + +static int find_free_breakpoint(void) +{ + int i; + char used[MAX_BREAKPOINT]; + + memset(used, FALSE, MAX_BREAKPOINT); + + for (i = 0; i < ARRAY_count(Breakpoint); i++) + used[Breakpoint[i].id - 1] = TRUE; + + for (i = 0; i < MAX_BREAKPOINT; i++) + if (!used[i]) + return (i + 1); + + return 0; +} + +static bool init_breakpoint(DEBUG_BREAK *brk) +{ + PCODE *addr = NULL; + FUNCTION *func; + + //fprintf(stderr, "init_breakpoint: id = %d\n", brk->id); + + if (brk->addr || !CLASS_is_loaded(brk->class)) + { + WARNING("Breakpoint is pending"); + return TRUE; + } + + if (CLASS_is_native(brk->class) || !brk->class->debug) + { + WARNING("Cannot set breakpoint: no debugging information"); + return TRUE; + } + + if (calc_position_from_line(brk->class, brk->line, &func, &addr)) + { + WARNING("Cannot set breakpoint: cannot calculate position"); + //fprintf(_out, "Cannot calc position from line number\n"); + return TRUE; + } + + if (!PCODE_is_breakpoint(*addr)) + { + //WARNING("Cannot set breakpoint: Not a line beginning: %04d: %04X", addr - func->code, *addr); + WARNING("Cannot set breakpoint: Not a line beginning"); + //fprintf(_out, "Not a line beginning ?\n"); + return TRUE; + } + + if (*addr & 0xFF) + { + WARNING("Breakpoint already set"); + //fprintf(_out, "Breakpoint already set\n"); + return FALSE; + } + + brk->addr = addr; + *addr = PCODE_BREAKPOINT(brk->id); + + //fprintf(stderr, "init_breakpoint: OK\n"); + + #ifdef DEBUG_ME + fprintf(stderr, "init_breakpoint: %s.%d\n", brk->class->name, brk->line); + #endif + + INFO("Breakpoint set: %s.%d", brk->class->name, brk->line); + return FALSE; +} + + +static bool set_breakpoint(CLASS *class, ushort line) +{ + DEBUG_BREAK *brk; + int id; + + if (GB.Count(Breakpoint) >= MAX_BREAKPOINT) + { + WARNING("Too many breakpoints"); + return TRUE; + } + + id = find_free_breakpoint(); + if (id == 0) + { + WARNING("Cannot create breakpoint"); + return TRUE; + } + + brk = (DEBUG_BREAK *)GB.Add(&Breakpoint); + + brk->id = id; + brk->addr = NULL; + brk->class = class; + brk->line = line; + + #ifdef DEBUG_ME + fprintf(stderr, "set_breakpoint: %s.%d\n", class->name, line); + #endif + + init_breakpoint(brk); + + return FALSE; +} + + +static bool unset_breakpoint(CLASS *class, ushort line) +{ + int i; + DEBUG_BREAK *brk; + + for (i = 0; i < GB.Count(Breakpoint); i++) + { + brk = &Breakpoint[i]; + if (brk->class == class && brk->line == line) + { + if (brk->addr) + *(brk->addr) = PCODE_BREAKPOINT(0); + GB.Remove(&Breakpoint, i, 1); + + #ifdef DEBUG_ME + fprintf(stderr, "unset_breakpoint: %s.%d\n", class->name, line); + #endif + + INFO("Breakpoint removed"); + return FALSE; + } + } + + WARNING("Unknown breakpoint"); + return TRUE; +} + + +void DEBUG_init_breakpoints(CLASS *class) +{ + int i; + DEBUG_BREAK *brk; + + #ifdef DEBUG_ME + fprintf(stderr, "DEBUG_init_breakpoints: %p %s\n", class, class->name); + #endif + + for (i = 0; i < GB.Count(Breakpoint); i++) + { + brk = &Breakpoint[i]; + if (brk->class == class) + { + //fprintf(stderr, "DEBUG_init_breakpoints: %s\n", class->name); + init_breakpoint(brk); + } + } +} + +static void print_local() +{ + int i; + FUNCTION *fp; + LOCAL_SYMBOL *lp; + + fp = DEBUG_info.fp; + + if (!fp || !fp->debug) + return; + + for (i = 0; i < fp->debug->n_local; i++) + { + lp = &fp->debug->local[i]; + fprintf(_out, "%.*s ", lp->sym.len, lp->sym.name); + } + + /*else + { + cmd++; + + for (i = 0; i < FP->debug->n_local; i++) + { + lp = &FP->debug->local[i]; + if (lp->sym.len == strlen(cmd) && strncasecmp(lp->sym.name, cmd, lp->sym.len) == 0) + { + fprintf(_out, "="); + //fprintf(_out, "TUT\n"); + PRINT_value(_out, &BP[lp->value], TRUE); + nl = FALSE; + break; + } + } + }*/ +} + + +static void print_symbol(GLOBAL_SYMBOL *gp, bool is_static, bool is_public) +{ + if (CTYPE_get_kind(gp->ctype) != TK_VARIABLE && CTYPE_get_kind(gp->ctype) != TK_CONST) + return; + + if (CTYPE_is_static(gp->ctype) && !is_static) + return; + + if (!CTYPE_is_static(gp->ctype) && is_static) + return; + + if (CTYPE_is_public(gp->ctype) && !is_public) + return; + + if (!CTYPE_is_public(gp->ctype) && is_public) + return; + + fprintf(_out, "%.*s ", gp->sym.len, gp->sym.name); +} + + +static void print_object() +{ + int i; + GLOBAL_SYMBOL *gp; + CLASS *cp = DEBUG_info.cp; + void *op = DEBUG_info.op; + + if (!cp || !cp->load) + return; + + fprintf(_out, "S: "); + + for (i = 0; i < cp->load->n_global; i++) + { + gp = &cp->load->global[i]; + print_symbol(gp, TRUE, TRUE); + } + + fprintf(_out, "s: "); + + for (i = 0; i < cp->load->n_global; i++) + { + gp = &cp->load->global[i]; + print_symbol(gp, TRUE, FALSE); + } + + if (op) + { + fprintf(_out, "D: "); + + for (i = 0; i < cp->load->n_global; i++) + { + gp = &cp->load->global[i]; + print_symbol(gp, FALSE, TRUE); + } + + fprintf(_out, "d: "); + + for (i = 0; i < cp->load->n_global; i++) + { + gp = &cp->load->global[i]; + print_symbol(gp, FALSE, FALSE); + } + } +} + +static void command_quit(const char *cmd) +{ + exit(1); +} + +static void command_go(const char *cmd) +{ + GB.Component.Signal(GB_SIGNAL_DEBUG_CONTINUE, 0); + + DEBUG_info.stop = FALSE; + DEBUG_info.leave = FALSE; + DEBUG_info.fp = NULL; + DEBUG_info.bp = NULL; + DEBUG_info.pp = NULL; +} + +static void command_step(const char *cmd) +{ + GB.Component.Signal(GB_SIGNAL_DEBUG_FORWARD, 0); + DEBUG_break_on_next_line(); +} + +static void command_next(const char *cmd) +{ + GB.Component.Signal(GB_SIGNAL_DEBUG_FORWARD, 0); + DEBUG_info.stop = TRUE; + DEBUG_info.leave = FALSE; + DEBUG_info.fp = FP; + DEBUG_info.bp = BP; + DEBUG_info.pp = PP; +} + +static void command_from(const char *cmd) +{ + STACK_CONTEXT *sc = GB_DEBUG.GetStack(0); //STACK_get_current(); + + if (sc && sc->pc) + { + GB.Component.Signal(GB_SIGNAL_DEBUG_FORWARD, 0); + DEBUG_info.stop = TRUE; + DEBUG_info.leave = FALSE; + DEBUG_info.fp = sc->fp; + DEBUG_info.bp = sc->bp; + DEBUG_info.pp = sc->pp; + } + else + { + GB.Component.Signal(GB_SIGNAL_DEBUG_FORWARD, 0); + DEBUG_info.stop = TRUE; + DEBUG_info.leave = TRUE; + DEBUG_info.fp = FP; + DEBUG_info.bp = BP; + DEBUG_info.pp = PP; + } +} + + +static void command_set_breakpoint(const char *cmd) +{ + char class_name[64]; + ushort line; + //CLASS *class; + + if (sscanf(cmd, "+%64[^.].%hu", class_name, &line) != 2) + WARNING("Cannot set breakpoint: syntax error"); + else + { + //class = (CLASS *)GB.FindClassLocal(class_name); + //CLASS_load_without_init(class); + //fprintf(stderr, "command_set_breakpoint: %s %s\n", class->name, class->component ? class->component->name : "?"); + set_breakpoint((CLASS *)GB_DEBUG.FindClass(class_name), line); + } +} + + +static void command_unset_breakpoint(const char *cmd) +{ + char class_name[64]; + ushort line; + + if (sscanf(cmd, "-%64[^.].%hu", class_name, &line) != 2) + WARNING("Cannot remove breakpoint: Syntax error"); + else + { + //class = CLASS_find(class_name); + //CLASS_load_without_init(class); + unset_breakpoint((CLASS *)GB_DEBUG.FindClass(class_name), line); + } +} + + +void DEBUG_backtrace(FILE *out) +{ + int i, n; + STACK_CONTEXT *context; + ushort line; + + if (CP) + fprintf(out, "%s", DEBUG_get_current_position()); + else + fprintf(out, "?"); + + //for (i = 0; i < (STACK_frame_count - 1); i++) + n = 0; + for (i = 0;; i++) + { + context = GB_DEBUG.GetStack(i); //&STACK_frame[i]; + if (!context) + break; + + if (context->pc) + { + line = 0; + if (DEBUG_calc_line_from_position(context->cp, context->fp, context->pc, &line)) + n += fprintf(out, " %s.?.?", context->cp->name); + else + n += fprintf(out, " %s.%s.%d", context->cp->name, context->fp->debug->name, line); + } + else if (context->cp) + n += fprintf(out, " ?"); + + if (n >= (DEBUG_OUTPUT_MAX_SIZE / 2)) + { + fprintf(out, " ..."); + break; + } + } +} + +static void debug_info() +{ + fprintf(_out, "*[%d]\t", getpid()); + + if (Error) + GB_DEBUG.PrintError(_out, TRUE, FALSE); + + fprintf(_out, "\t"); + + DEBUG_backtrace(_out); + fprintf(_out, "\t"); + + print_local(); + fprintf(_out, "\t"); + + print_object(); + fprintf(_out, "\n"); +} + +static void command_frame(const char *cmd) +{ + int i; + int frame; + STACK_CONTEXT *context = NULL; + + if (cmd) + { + frame = atoi(&cmd[1]); + //fprintf(_out, "switching to frame %d\n", frame); + + if (frame > 0) + { + for (i = 0;; i++) + { + context = GB_DEBUG.GetStack(i); + if (!context) + break; + if (!context->pc && !context->cp) + continue; + + frame--; + if (!frame) + { + DEBUG_info.bp = context->bp; + DEBUG_info.pp = context->pp; + DEBUG_info.fp = context->fp; + DEBUG_info.op = context->op; + DEBUG_info.cp = context->cp; + break; + } + } + } + } + + if (!context) + { + DEBUG_info.bp = BP; + DEBUG_info.pp = PP; + DEBUG_info.fp = FP; + DEBUG_info.op = OP; + DEBUG_info.cp = CP; + } + + debug_info(); +} + +static void command_eval(const char *cmd) +{ + EXPRESSION *expr; + ERROR_INFO save_error = { 0 }; + ERROR_INFO save_last = { 0 }; + DEBUG_INFO save_debug; + VALUE *val; + int start, len; + FILE *out; + const char *name; + int ret; + + init_eval_interface(); + + out = *cmd == '!' ? stdout : _out; + + len = strlen(cmd); + for (start = 0; start < len; start++) + { + if (cmd[start] == '\t') + break; + if (*cmd != '!') + fputc(cmd[start], _out); + } + + if (start >= len) + return; + + if (*cmd != '!') + fprintf(_out, "\t"); + + GB_DEBUG.SaveError(&save_error, &save_last); + save_debug = DEBUG_info; + + start++; + EVAL.New(POINTER(&expr), &cmd[start], len - start); + + if (EVAL.Compile(expr, *cmd == '=')) + { + if (*cmd != '!') + fprintf(_out, "!"); + fputs(expr->error, out); + goto __END; + } + + GB_DEBUG.EnterEval(); + val = (VALUE *)EVAL.Run(expr, GB_DEBUG.GetValue); + GB_DEBUG.LeaveEval(); + if (!val) + goto __ERROR; + + switch(*cmd) + { + case '?': + PRINT_value(out, val, TRUE); + break; + + case '!': + PRINT_value(out, val, FALSE); + break; + + case '#': + PRINT_object(out, val); + break; + + case '=': + if (!EVAL.GetAssignmentSymbol(expr, &name, &len)) + { + ret = GB_DEBUG.SetValue(name, len, val); + if (ret == GB_DEBUG_SET_ERROR) + goto __ERROR; + else if (ret == GB_DEBUG_SET_READ_ONLY) + { + fprintf(out, "!%.*s is read-only", len, name); + goto __END; + } + } + fprintf(out, "OK"); + break; + } + + goto __END; + +__ERROR: + + if (*cmd != '!') + fprintf(out, "!"); + GB_DEBUG.PrintError(out, TRUE, FALSE); + +__END: + + EVAL.Free(POINTER(&expr)); + DEBUG_info = save_debug; //.cp = NULL; + GB_DEBUG.RestoreError(&save_error, &save_last); + + fprintf(out, "\n"); + fflush(out); +} + + +static void command_symbol(const char *cmd) +{ + int start, len; + DEBUG_INFO save_debug = DEBUG_info; + + len = strlen(cmd); + for (start = 0; start < len; start++) + { + if (cmd[start] == '\t') + break; + fputc(cmd[start], _out); + } + + if (start >= len) + return; + + fprintf(_out, "\t"); + + /*DEBUG_info.bp = BP; + DEBUG_info.fp = FP; + DEBUG_info.op = OP; + DEBUG_info.cp = CP;*/ + + start++; + PRINT_symbol(_out, &cmd[start], len - start); + + fprintf(_out, "\n"); + fflush(_out); + + DEBUG_info = save_debug; +} + + +static void command_break_on_error(const char *cmd) +{ + GB_DEBUG.BreakOnError(cmd[1] == '+'); +} + +void DEBUG_main(bool error) +{ + static DEBUG_TYPE last_command = TC_NONE; + + static DEBUG_COMMAND Command[] = + { + { "q", TC_NONE, command_quit, FALSE }, + { "n", TC_NEXT, command_next, FALSE }, + { "s", TC_STEP, command_step, FALSE }, + { "f", TC_FROM, command_from, FALSE }, + { "g", TC_GO, command_go, FALSE }, + { "+", TC_NONE, command_set_breakpoint, TRUE }, + { "-", TC_NONE, command_unset_breakpoint, TRUE }, + { "&", TC_NONE, command_symbol, TRUE }, + { "?", TC_NONE, command_eval, TRUE }, + { "!", TC_NONE, command_eval, TRUE }, + { "#", TC_NONE, command_eval, TRUE }, + { "=", TC_NONE, command_eval, TRUE }, + { "@", TC_NONE, command_frame, TRUE }, + { "b", TC_NONE, command_break_on_error, TRUE }, + + { NULL } + }; + + static bool first = TRUE; + char *cmd = NULL; + char cmdbuf[64]; + int len; + DEBUG_COMMAND *tc = NULL; + /*static int cpt = 0;*/ + + Error = error; + + fflush(_out); + + #ifdef DEBUG_ME + fprintf(stderr, "DEBUG_main {\n"); + #endif + + if (_fifo) + { + fprintf(_out, first ? "!!\n" : "!\n"); + first = FALSE; + } + + command_frame(NULL); + + do + { + /*if (CP == NULL) + printf("[]:"); + else + printf("[%s%s]:", DEBUG_get_current_position(), Error ? "*" : "");*/ + + GB.Component.Signal(GB_SIGNAL_DEBUG_BREAK, 0); + + if (!_fifo) + { + fprintf(_out, "> "); + fflush(_out); + } + + GB.FreeString(&cmd); + + for(;;) + { + *cmdbuf = 0; + errno = 0; + if (fgets(cmdbuf, sizeof(cmdbuf), _in) == NULL && errno != EINTR) + break; + if (!*cmdbuf) + continue; + cmd = GB.AddString(cmd, cmdbuf, 0); + if (cmd[GB.StringLength(cmd) - 1] == '\n') + break; + } + + len = GB.StringLength(cmd); + + // A null string command means an I/O error + if (len == 0) + { + fprintf(stderr, "warning: debugger I/O error: %s\n", strerror(errno)); + exit(1); + } + + if (len > 0 && cmd[len - 1] == '\n') + { + len--; + cmd[len] = 0; + } + + #ifdef DEBUG_ME + fprintf(stderr, "--> %s\n", cmd); + #endif + + if (len == 0) + { + if (last_command == TC_NONE) + continue; + + for (tc = Command; tc->pattern; tc++) + { + if (tc->type == last_command) + { + (*tc->func)(cmd); + break; + } + } + } + else + { + for (tc = Command; tc->pattern; tc++) + { + if (strncasecmp(tc->pattern, cmd, strlen(tc->pattern)) == 0) + { + if (tc->type != TC_NONE) + last_command = tc->type; + (*tc->func)(cmd); + break; + } + } + } + + if (tc->pattern == NULL) + WARNING("Unknown command: %s", cmd); + + fflush(_out); + } + while (last_command == TC_NONE || tc->pattern == NULL || tc->loop); + + GB.FreeString(&cmd); + + #ifdef DEBUG_ME + fprintf(stderr, "} DEBUG_main\n"); + #endif +} + + + +void DEBUG_breakpoint(int id) +{ + DEBUG_main(FALSE); +} + + +const char *DEBUG_get_position(CLASS *cp, FUNCTION *fp, PCODE *pc) +{ + if (pc) + { + ushort line = 0; + + if (fp != NULL && fp->debug) + DEBUG_calc_line_from_position(cp, fp, pc, &line); + + snprintf(DEBUG_buffer, sizeof(DEBUG_buffer), "%.64s.%.64s.%d", + cp ? cp->name : "?", + (fp && fp->debug) ? fp->debug->name : "?", + line); + } + else + { + snprintf(DEBUG_buffer, sizeof(DEBUG_buffer), "%.64s.%.64s", + cp ? cp->name : "?", + (fp && fp->debug) ? fp->debug->name : "?"); + } + + return DEBUG_buffer; +} + +const char *DEBUG_get_profile_position(CLASS *cp, FUNCTION *fp, PCODE *pc) +{ + static uint prof_index = 0; + const char *name; + const char *func; + char buffer[16], buffer2[16]; + + func = "?"; + + if (cp) + { + if (cp->load && cp->load->prof) + { + if (cp->load->prof[0] == 0) + { + prof_index++; + cp->load->prof[0] = prof_index; + name = cp->name; + } + else + { + sprintf(buffer, "%u", cp->load->prof[0]); + name = buffer; + } + + if (fp && fp->debug) + { + int i = fp->debug->index + 1; + if (cp->load->prof[i] == 0) + { + prof_index++; + cp->load->prof[i] = prof_index; + func = fp->debug->name; + } + else + { + sprintf(buffer2, "%u", cp->load->prof[i]); + func = buffer2; + } + } + else + func = "?"; + } + else + name = cp->name; + } + else + name = "?"; + + if (pc) + { + ushort line = 0; + + if (fp != NULL && fp->debug) + DEBUG_calc_line_from_position(cp, fp, pc, &line); + + snprintf(DEBUG_buffer, sizeof(DEBUG_buffer), "%.64s.%.64s.%d", name, func, line); + } + else + { + snprintf(DEBUG_buffer, sizeof(DEBUG_buffer), "%.64s.%.64s", name, func); + } + + return DEBUG_buffer; +} + + +const char *DEBUG_get_current_position(void) +{ + return DEBUG_get_position(CP, FP, PC); +} + + +void DEBUG_where(void) +{ + fprintf(_out ? _out : stderr, "%s: ", DEBUG_get_current_position()); +} + + +void DEBUG_welcome(void) +{ + if (!_fifo) + fprintf(_out, DEBUG_WELCOME); +} diff --git a/main/lib/debug/debug.h b/main/lib/debug/debug.h new file mode 100644 index 00000000..97fe3c16 --- /dev/null +++ b/main/lib/debug/debug.h @@ -0,0 +1,94 @@ +/*************************************************************************** + + debug.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __DEBUG_H +#define __DEBUG_H + +#include "gambas.h" +#include "main.h" +#include "gb.debug.h" +#include "gb_pcode.h" +#include "gbx_value.h" + +typedef + struct { + int id; + FUNCTION *func; + PCODE *addr; + CLASS *class; + ushort line; + VALUE *bp; + FUNCTION *fp; + } + DEBUG_BREAK; + +typedef + enum { + TC_NONE = 0, + TC_STEP = 1, + TC_NEXT = 2, + TC_GO = 3, + TC_FROM = 4 + } + DEBUG_TYPE; + +typedef + struct { + char *pattern; + DEBUG_TYPE type; + void (*func)(const char *); + bool loop; + } + DEBUG_COMMAND; + + +#ifndef __GBX_DEBUG_C +EXTERN DEBUG_INFO DEBUG_info; +EXTERN GB_DEBUG_INTERFACE *DEBUG_interface; +EXTERN char DEBUG_buffer[]; +#endif + +#define DEBUG_BUFFER_MAX 255 + +#define GB_DEBUG (*DEBUG_interface) + +#define DEBUG_WELCOME "Welcome to the Gambas debugger.\n" + +void DEBUG_breakpoint(int id); +void DEBUG_main(bool error); + +bool DEBUG_calc_line_from_position(CLASS *class, FUNCTION *func, PCODE *addr, ushort *line); +const char *DEBUG_get_position(CLASS *cp, FUNCTION *fp, PCODE *pc); +const char *DEBUG_get_profile_position(CLASS *cp, FUNCTION *fp, PCODE *pc); +const char *DEBUG_get_current_position(void); +void DEBUG_init_breakpoints(CLASS *class); + +void DEBUG_break_on_next_line(void); + +DEBUG_INFO *DEBUG_init(GB_DEBUG_INTERFACE *debug, bool fifo, const char*fifo_name); +void DEBUG_exit(void); +void DEBUG_welcome(void); +void DEBUG_where(void); +void DEBUG_backtrace(FILE *out); + +#endif diff --git a/main/lib/debug/gb.debug.component b/main/lib/debug/gb.debug.component new file mode 100644 index 00000000..e1c80a2c --- /dev/null +++ b/main/lib/debug/gb.debug.component @@ -0,0 +1,2 @@ +[Component] +Author=Benoît Minisini diff --git a/main/lib/debug/gb.debug.h b/main/lib/debug/gb.debug.h new file mode 100644 index 00000000..5da87926 --- /dev/null +++ b/main/lib/debug/gb.debug.h @@ -0,0 +1,120 @@ +/*************************************************************************** + + gb.debug.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_DEBUG_H +#define __GB_DEBUG_H + +#include +#include "gambas.h" +#include "gbx_class.h" +#include "gbx_value.h" +#include "gb_pcode.h" + +#define DEBUG_INTERFACE_VERSION 1 + +typedef + struct { + unsigned stop : 1; + unsigned leave : 1; + FUNCTION *fp; + VALUE *bp; + VALUE *pp; + void *op; + CLASS *cp; + PCODE *ec; + VALUE *ep; + } + DEBUG_INFO; + +enum +{ + GB_DEBUG_ACCESS_NORMAL = 0, + GB_DEBUG_ACCESS_ARRAY = 1, + GB_DEBUG_ACCESS_COLLECTION = 2 +}; + +enum +{ + GB_DEBUG_SET_OK = 0, + GB_DEBUG_SET_ERROR = 1, + GB_DEBUG_SET_READ_ONLY = 2 +}; + +typedef + void (*GB_DEBUG_ENUM_CB)(char *key, int len); + +typedef + struct { + void *(*GetExec)(void); + void *(*GetStack)(int frame); + void (*PrintError)(FILE *where, bool msgonly, bool newline); + void (*SaveError)(void *, void *); + void (*RestoreError)(void *, void *); + void (*ToString)(GB_VALUE *value, char **addr, int *len); + int (*FormatDate)(GB_DATE_SERIAL *date, int fmt_type, const char *fmt, int len_fmt, char **str, int *len_str); + int (*FormatNumber)(double number, int fmt_type, const char *fmt, int len_fmt, char **str, int *len_str, bool local); + bool (*GetValue)(const char *sym, int len, GB_VARIANT *ret); + int (*SetValue)(const char *sym, int len, VALUE *value); + void (*GetArrayValue)(GB_ARRAY array, int index, GB_VALUE *value); + void (*EnumKeys)(void *collection, GB_DEBUG_ENUM_CB cb); + void *(*GetNextSortedSymbol)(void *klass, int *index); + int (*GetObjectAccessType)(void *object, CLASS *klass, int *count); + GB_CLASS (*FindClass)(const char *name); + int *(*GetArrayBounds)(void *array); + void (*BreakOnError)(bool); + void (*EnterEval)(void); + void (*LeaveEval)(void); + } + GB_DEBUG_INTERFACE; + +typedef + struct { + intptr_t version; + DEBUG_INFO *(*Init)(GB_DEBUG_INTERFACE *debug, int fifo, const char *fifo_name); + void (*Exit)(void); + void (*Welcome)(void); + void (*Main)(int error); + void (*Where)(void); + void (*Backtrace)(FILE *out); + void (*Breakpoint)(int id); + void (*BreakOnNextLine)(void); + const char *(*GetPosition)(void *klass, void *func, void *pcode); + const char *(*GetCurrentPosition)(void); + void (*InitBreakpoints)(void *klass); + struct { + void (*Init)(const char *path); + void (*Add)(void *cp, void *fp, void *pc); + void (*Exit)(void); + void (*Begin)(void *cp, void *fp); + void (*End)(void *cp, void *fp); + void (*Cancel)(void); + } + Profile; + void *_null; + } + DEBUG_INTERFACE; + +#define DEBUG_OUTPUT_MAX_SIZE 65536 +#define DEBUG_FIFO_PATH_MAX 64 + +#endif diff --git a/main/lib/debug/main.c b/main/lib/debug/main.c new file mode 100644 index 00000000..935e6b4c --- /dev/null +++ b/main/lib/debug/main.c @@ -0,0 +1,72 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gambas.h" +#include "debug.h" +#include "profile.h" +#include "CDebug.h" +#include "main.h" + +const GB_INTERFACE *GB_PTR EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CDebugDesc, + NULL +}; + +void *GB_DEBUG_1[] EXPORT = +{ + (void *)1, + (void *)DEBUG_init, + (void *)DEBUG_exit, + (void *)DEBUG_welcome, + (void *)DEBUG_main, + (void *)DEBUG_where, + (void *)DEBUG_backtrace, + (void *)DEBUG_breakpoint, + (void *)DEBUG_break_on_next_line, + (void *)DEBUG_get_position, + (void *)DEBUG_get_current_position, + (void *)DEBUG_init_breakpoints, + (void *)PROFILE_init, + (void *)PROFILE_add, + (void *)PROFILE_exit, + (void *)PROFILE_begin, + (void *)PROFILE_end, + (void *)PROFILE_cancel, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} + + diff --git a/main/lib/debug/main.h b/main/lib/debug/main.h new file mode 100644 index 00000000..23f4e73b --- /dev/null +++ b/main/lib/debug/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern const GB_INTERFACE *GB_PTR; +#endif + +#define GB (*GB_PTR) + +#endif diff --git a/main/lib/debug/print.c b/main/lib/debug/print.c new file mode 100644 index 00000000..c5cdcef0 --- /dev/null +++ b/main/lib/debug/print.c @@ -0,0 +1,486 @@ +/*************************************************************************** + + print.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __PRINT_C + +// Do not include gbx_debug.h +#define __GBX_DEBUG_H + +#include "gb_common.h" + +#include +#include +#include + +#include "gb_error.h" +#include "gbx_type.h" +#include "gb_limit.h" +#include "gbx_class.h" +#include "gbx_object.h" +#include "gbx_local.h" +#include "gbx_variant.h" + +#include "gambas.h" + +#include "print.h" + + +static FILE *_where; +static int _level; + +static void print_value(VALUE *value); + +static void print_string(const char *s, int len) +{ + int i; + uchar c; + + fputc('"', _where); + + for (i = 0; i < len; i++) + { + if (i > (DEBUG_OUTPUT_MAX_SIZE - 8)) + { + fprintf(_where, "..."); + break; + } + + c = s[i]; + + if (c < 32) + { + if (c == 10) + fprintf(_where, "\\n"); + else if (c == 13) + fprintf(_where, "\\r"); + else if (c == 9) + fprintf(_where, "\\t"); + else + fprintf(_where, "\\x%02X", c); + } + else if (c == '\"') + { + fprintf(_where, "\\\""); + } + else + { + fputc(c, _where); + } + } + + fputc('"', _where); +} + +#if 0 +static void new_line(void) +{ + int i; + + fputc('\t', _where); + for (i = 1; i < _level; i++) + fprintf(_where, " "); +} +#endif + +static void print_object(void *object) +{ + //int i; + //char *key; + //VALUE value; + //long len; + + fprintf(_where, "(%s %p)", OBJECT_class(object)->name, object); + +/* + if (GB.Is(object, GB.FindClass("Collection"))) + { + //if (_level > 1) + // new_line(); + + //fprintf(_where, "%s", OBJECT_class(object)->name); + GB_DEBUG.EnumCollection(object, NULL, NULL, NULL); + for (i = 0; i < 8; i++) + { + if (GB_DEBUG.EnumCollection(object, (GB_VARIANT *)&value, &key, &len)) + break; + new_line(); + fprintf(_where, "["); + print_string(key, len); + fprintf(_where, "] = "); + print_value(&value); + } + if (GB.Collection.Count(object) > 8) + { + new_line(); + fprintf(_where, "... #%ld", GB.Collection.Count(object)); + } + } + else if (GB.Is(object, GB.FindClass("Array"))) + { + //if (_level > 1) + // new_line(); + + //fprintf(_where, "%s", OBJECT_class(object)->name); + for (i = 0; i < GB.Array.Count(object); i++) + { + if (i >= 8 && i < (GB.Array.Count(object) - 1)) + { + new_line(); + fprintf(_where, "..."); + i = GB.Array.Count(object) - 1; + } + new_line(); + GB_DEBUG.GetArrayValue(object, i, (GB_VALUE *)&value); + fprintf(_where, "[%d] = ", i); + print_value(&value); + } + } +*/ +} + +static void print_value(VALUE *value) +{ + static void *jump[16] = { + &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL + }; + + VALUE conv; + char *addr; + int len; + + //*more = FALSE; + + if (_level >= 4) + { + fprintf(_where, "..."); + return; + } + + _level++; + +__CONV: + + if (TYPE_is_object(value->type)) + goto __OBJECT; + else + goto *jump[value->type]; + +__NULL: + + fprintf(_where, "NULL"); + goto __RETURN; + +__BOOLEAN: + + fprintf(_where, value->_boolean.value ? "TRUE" : "FALSE"); + goto __RETURN; + +__BYTE: +__SHORT: +__INTEGER: + + fprintf(_where, "%d", value->_integer.value); + goto __RETURN; + +__LONG: + + fprintf(_where, "%" PRId64, value->_long.value); + goto __RETURN; + +__DATE: + + GB_DEBUG.FormatDate(GB.SplitDate((GB_DATE *)value), LF_STANDARD, NULL, 0, &addr, &len); + goto __PRINT; + +__SINGLE: + + GB_DEBUG.FormatNumber(value->_single.value, LF_SHORT_NUMBER, NULL, 0, &addr, &len, TRUE); + goto __PRINT; + +__FLOAT: + + GB_DEBUG.FormatNumber(value->_float.value, LF_STANDARD, NULL, 0, &addr, &len, TRUE); + +__PRINT: + + fprintf(_where, "%.*s", (int)len, addr); + goto __RETURN; + +__STRING: + + print_string(value->_string.addr + value->_string.start, value->_string.len); + goto __RETURN; + +__OBJECT: + + if (!value->_object.object) + goto __NULL; + + //*more = !CLASS_is_native(OBJECT_class(value->_object.object)); + + print_object(value->_object.object); + goto __RETURN; + +__VARIANT: + + conv = *value; + value = &conv; + GB.Conv((GB_VALUE *)value, (GB_TYPE)value->_variant.vtype); + //VARIANT_undo(value); + goto __CONV; + +__VOID: + + fprintf(_where, "VOID"); + goto __RETURN; + +__CLASS: + + { + CLASS *class = value->_class.class; + //*more = (!CLASS_is_native(class) && class->load->n_stat > 0); + + fprintf(_where, "(%s)", class->name); + goto __RETURN; + } + +__POINTER: + + fprintf(_where, "(%p)", value->_pointer.value); + goto __RETURN; + +__FUNCTION: + + fprintf(_where, "FUNCTION"); + goto __RETURN; + +__RETURN: + _level--; +} + + +void PRINT_value(FILE *where, VALUE *value, bool format) +{ + char *pval; + int lpval; + + if (format) + { + _where = where; + _level = 0; + print_value(value); + //fputc('\n', _where); + } + else + { + GB_DEBUG.ToString((GB_VALUE *)value, &pval, &lpval); + fwrite(pval, sizeof(char), lpval, where); + } +} + +void PRINT_symbol(FILE *where, const char *sym, int len) +{ + GB_VALUE value; + + _where = where; + + if (GB_DEBUG.GetValue(sym, len, (GB_VARIANT *)(void *)&value)) + { + fprintf(_where, "Unknown symbol"); + return; + } + + print_value((VALUE *)&value); +} + +static void print_key(char *key, int len) +{ + fprintf(_where, " "); + print_string(key, len); +} + +void PRINT_object(FILE *where, VALUE *value) +{ + VALUE conv; + void *object; + int index; + int count = 0; + CLASS *class, *real_class; + CLASS_DESC_SYMBOL *cd; + char *key; + int len; + bool static_class; + int access; + int *dim; + + _where = where; + + if (value->type == T_VARIANT) + { + conv = *value; + value = &conv; + GB.Conv((GB_VALUE *)value, (GB_TYPE)value->_variant.vtype); + } + + if (value->type < T_OBJECT && value->type != T_CLASS) + { + //fprintf(_where, "\n"); + return; + } + + real_class = NULL; + + if (value->type == T_CLASS) + { + object = value->_class.class; + class = (CLASS *)object; + static_class = TRUE; + real_class = NULL; + } + else + { + object = value->_object.object; + real_class = (CLASS *)GB.GetClass(object); + if (value->type == T_OBJECT) + class = real_class; + else + class = value->_object.class; + + if (real_class == class) + real_class = NULL; + + /*if (value->type != T_OBJECT && value->_object.class->is_virtual) + { + } + else + class = (CLASS *)GB.GetClass(object);*/ + //fprintf(stderr, "PRINT_object: %s %s\n", class->name, value->_object.class->name); + static_class = FALSE; + } + + if (!object) + return; + + fprintf(_where, "%s ", class->name); + if (real_class) fprintf(_where, "%s", real_class->name); + fputc(' ', _where); + + access = GB_DEBUG.GetObjectAccessType(object, class, &count); + + if (access == GB_DEBUG_ACCESS_COLLECTION) + { + fprintf(_where, "C: [%d]", count); + + GB_DEBUG.EnumKeys(object, print_key); + fprintf(_where, " "); + } + else if (GB.Is(object, GB.FindClass("Array"))) + { + dim = GB_DEBUG.GetArrayBounds(object); + if (!dim) + fprintf(_where, "A: [%d] ", count); + else + { + fprintf(_where, "A: ["); + for(;;) + { + len = *dim++; + if (len > 0) + fprintf(_where, "%d,", len); + else + { + fprintf(_where, "%d", -len); + break; + } + } + fprintf(_where, "] "); + } + } + else if (access == GB_DEBUG_ACCESS_ARRAY) + { + fprintf(_where, "A: [%d] ", count); + } + + if (!class->is_virtual && real_class) + class = real_class; + + fprintf(_where, "S:"); + + index = 0; + + for(;;) + { + cd = (CLASS_DESC_SYMBOL *)GB_DEBUG.GetNextSortedSymbol(class, &index); + if (!cd) + break; + key = cd->name; + if (cd->len == 0 || (cd->len == 1 && key[0] == '.')) + continue; + + switch(CLASS_DESC_get_type(cd->desc)) + { + case CD_STATIC_VARIABLE: + case CD_STATIC_PROPERTY: + case CD_STATIC_PROPERTY_READ: + case CD_CONSTANT: + fprintf(_where, " %.*s", cd->len, key); + break; + } + } + + if (count > 0 && static_class) + fprintf(_where, " [%d]", count); + + fprintf(_where, " D:"); + + if (!static_class) + { + index = 0; + + for(;;) + { + cd = (CLASS_DESC_SYMBOL *)GB_DEBUG.GetNextSortedSymbol(class, &index); + if (!cd) + break; + key = cd->name; + if (cd->len == 0 || (cd->len == 1 && key[0] == '.')) + continue; + + switch(CLASS_DESC_get_type(cd->desc)) + { + case CD_VARIABLE: + case CD_PROPERTY: + case CD_PROPERTY_READ: + case CD_STRUCT_FIELD: + fprintf(_where, " %.*s", cd->len, key); + break; + } + } + + if (count > 0) + fprintf(_where, " [%d]", count); + } + + //fprintf(_where, "\n"); +} diff --git a/main/lib/debug/print.h b/main/lib/debug/print.h new file mode 100644 index 00000000..f6997ba8 --- /dev/null +++ b/main/lib/debug/print.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + print.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __PRINT_H +#define __PRINT_H + +#include + +#include "gambas.h" +#include "main.h" +#include "gb.debug.h" +#include "gbx_value.h" +#include "debug.h" + +PUBLIC void PRINT_value(FILE *where, VALUE *value, bool format); +PUBLIC void PRINT_object(FILE *where, VALUE *value); +PUBLIC void PRINT_symbol(FILE *where, const char *sym, int len); + +#endif diff --git a/main/lib/debug/profile.c b/main/lib/debug/profile.c new file mode 100644 index 00000000..f89ba0bd --- /dev/null +++ b/main/lib/debug/profile.c @@ -0,0 +1,245 @@ +/*************************************************************************** + + profile.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __PROFILE_C + +#include +#include + +#include "gambas.h" +#include "debug.h" +#include "main.h" + +// Maximum profile file is 512 Mb by default + +#define MAX_PROFILE_SIZE (512 << 20) + +static bool _init = FALSE; +static FILE *_file; +static int _last_line = 0; +static bool _new_line = TRUE; +static int _count = 0; +static size_t _max_profile_size = MAX_PROFILE_SIZE; +//static long _ticks_per_sec; + +static uint64_t get_time(void) +{ + static uint64_t last = 0; + + struct timeval time; + //struct tms time; + uint64_t t; + + gettimeofday(&time, NULL); + t = (uint64_t)time.tv_sec * 1000000 + (uint64_t)time.tv_usec - last; + + //times(&time); + //t = (uint64_t)(time.tms_utime + time.tms_stime) * 1000000 / _ticks_per_sec - last; + + last += t; + + return t; +} + +void PROFILE_init(const char *path) +{ + char *env; + size_t max; + char buffer[PATH_MAX + 1]; + + if (_init) + return; + + if (!path) + { + sprintf(buffer, ".%d.prof", getpid()); + path = buffer; + } + + //fprintf(stderr, "gb.profile: start profiling: %s\n", path); + + _file = fopen(path, "w"); + if (!_file) + { + fprintf(stderr, "gb.debug: cannot create profile file '%s': %s\n", path, strerror(errno)); + abort(); + } + + fprintf(_file, "[1]\n"); + + //_ticks_per_sec = sysconf(_SC_CLK_TCK); + //fprintf(stderr, "_ticks_per_sec = %ld\n", _ticks_per_sec); + + env = getenv("GB_PROFILE_MAX"); + if (env) + { + max = atoi(env); + if (max > 0) + { + if (max < 128) + max = 128; + else if (max > 4096) + max = 4096; + + _max_profile_size = max << 20; + } + } + + _init = TRUE; + get_time(); +} + +void PROFILE_cancel(void) +{ + if (_init) + { + close(fileno(_file)); + _init = FALSE; + } +} + +void PROFILE_exit(void) +{ + if (!_init) + return; + + //fprintf(stderr, "gb.profile: stop profiling\n"); + + if (!_new_line) + fputc('\n', _file); + + fclose(_file); +} + +static void check_size() +{ + _count = 0; + if (ftell(_file) > _max_profile_size) + { + fprintf(stderr, "gb.debug: maximum profile size reached\n"); + PROFILE_exit(); + abort(); + } +} + + +#define CODE(n) (n + '9' + 1) + +static void add_line(ushort line, uint64_t time) +{ + int n; + char buf[32], num[32]; + char *p; + + n = line - _last_line; + p = buf; + + if (n >= -9 && n <= 9) + *p++ = CODE(0 + n + 9); + else if (n >= -99 && n <= 99) + { + *p++ = n > 0 ? CODE(19) : CODE(20); + *p++ = CODE(0) + abs(n) - 10; + } + else + { + *p++ = n > 0 ? CODE(21) : CODE(22); + n = sprintf(num, "%d", abs(n)); + *p++ = CODE(n); + strcpy(p, num); + p += n; + } + + if (time <= 9) + *p++ = CODE(time); + else + { + n = sprintf(num, "%" PRIu64, time); + *p++ = CODE(10 + n - 2); + strcpy(p, num); + p += n; + } + + *p = 0; + + fputs(buf, _file); + + _last_line = line; + _new_line = FALSE; + _count++; + if ((_count & 0xFFFFF) == 0) + check_size(); +} + +void PROFILE_add(void *cp, void *fp, void *pc) +{ + uint64_t time = get_time(); + ushort line; + + line = 0; + DEBUG_calc_line_from_position(cp, fp, pc, &line); + + add_line(line, time); +} + +void PROFILE_begin(void *cp, void *fp) +{ + uint64_t time = get_time(); + const char *where = cp ? DEBUG_get_profile_position(cp, fp, NULL) : "0"; + + if (!_new_line) + fputc('\n', _file); + fprintf(_file, "(%s %" PRId64 "\n", where, time); + _last_line = 0; + _new_line = TRUE; + + _count++; + if ((_count & 0xFFFFF) == 0) + check_size(); + /*if (cp && fp) + { + FUNCTION *ffp = (FUNCTION *)fp; + add_line(ffp->debug->line, time); + }*/ +} + +void PROFILE_end(void *cp, void *fp) +{ + uint64_t time = get_time(); + //const char *where; + + if (cp && fp) + { + FUNCTION *ffp = (FUNCTION *)fp; + if (ffp->debug) + add_line(ffp->debug->line + ffp->debug->nline, time); + } + + //where = cp ? DEBUG_get_position(cp, fp, NULL) : ".System.EventLoop"; + + if (!_new_line) + fputc('\n', _file); + fprintf(_file, ")\n"); + _last_line = 0; + _new_line = TRUE; +} diff --git a/main/lib/debug/profile.h b/main/lib/debug/profile.h new file mode 100644 index 00000000..ca06f978 --- /dev/null +++ b/main/lib/debug/profile.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + profile.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __PROFILE_H +#define __PROFILE_H + +void PROFILE_init(void); +void PROFILE_exit(void); +void PROFILE_add(void *cp, void *fp, void *pc); +void PROFILE_begin(void *cp, void *fp); +void PROFILE_end(void *cp, void *fp); +void PROFILE_cancel(void); + +#endif diff --git a/main/lib/draw/Makefile.am b/main/lib/draw/Makefile.am new file mode 100644 index 00000000..ff1fe8fb --- /dev/null +++ b/main/lib/draw/Makefile.am @@ -0,0 +1,15 @@ +EXTRA_DIST = gb.image.h gb.geom.h + +gblib_LTLIBRARIES = gb.draw.la + +gb_draw_la_LIBADD = @MATH_LIB@ +gb_draw_la_LDFLAGS = -module @LD_FLAGS@ +gb_draw_la_CFLAGS = -I$(top_srcdir)/share @INCLTDL@ $(AM_CFLAGS) + +gb_draw_la_SOURCES = \ + gb.draw.h gb.paint.h \ + gb_list.c \ + cpaint.h cpaint.c \ + main.h main.c + + diff --git a/main/lib/draw/cdraw.c b/main/lib/draw/cdraw.c new file mode 100644 index 00000000..a268e39a --- /dev/null +++ b/main/lib/draw/cdraw.c @@ -0,0 +1,1309 @@ +/*************************************************************************** + + cdraw.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CDRAW_C + +#include "gb.image.h" +#include "matrix.h" +#include "main.h" +#include "crect.h" +#include "cpaint.h" +#include "cdraw.h" + +static GB_DRAW *_current = NULL; +#define THIS _current +#define DRAW _current->desc +#define THIS_MATRIX ((MATRIX *)&(THIS->matrix)) +#define CHECK_DEVICE() if (check_device()) return + +static bool check_device() +{ + if (!_current || !_current->opened) + { + GB.Error("No current device"); + return TRUE; + } + else + return FALSE; +} + +GB_DRAW *DRAW_from_device(void *device) +{ + GB_DRAW *d; + + for (d = _current; d; d = d->previous) + { + if (d->device == device && d->opened) + break; + } + + return d; +} + +PUBLIC GB_DRAW *DRAW_get_current() +{ + check_device(); + return _current; +} + +bool DRAW_open(GB_DRAW *draw) +{ + if (draw->opened) + return FALSE; + + //fprintf(stderr, "DRAW_open: %p\n", draw); + GB.Alloc(POINTER(&draw->extra), draw->desc->size); + memset(draw->extra, 0, draw->desc->size); + + draw->opened = !draw->desc->Begin(draw); + return !draw->opened; +} + +void DRAW_close(GB_DRAW *draw) +{ + if (draw->opened) + { + //fprintf(stderr, "DRAW_close: %p\n", draw); + draw->desc->End(draw); + GB.Free(POINTER(&draw->extra)); + draw->opened = FALSE; + } +} + +bool DRAW_begin(void *device) +{ + GB_DRAW_DESC *desc; + GB_DRAW *draw; + GB_CLASS klass; + GB_DRAW *other; + + klass = GB.GetClass(device); + if (klass == GB.FindClass("Class")) + { + klass = (GB_CLASS)device; + desc = (GB_DRAW_DESC *)GB.GetClassInterface(klass, "StaticDraw"); + } + else + { + desc = (GB_DRAW_DESC *)GB.GetClassInterface(klass, "Draw"); + } + + if (!desc) + { + GB.Error("Not a drawable object"); + return TRUE; + } + + GB.Alloc(POINTER(&draw), sizeof(GB_DRAW)); + + other = DRAW_from_device(device); + + draw->desc = desc; + GB.Ref(device); + draw->device = device; + MATRIX_init(&draw->matrix); + draw->save = NULL; + draw->opened = FALSE; + draw->xform = FALSE; + + draw->previous = _current; + _current = draw; + + draw->paint = PAINT_from_device(device); + if (draw->paint) + PAINT_close(draw->paint); + + if (other) + { + draw->extra = other->extra; + } + else + { + if (DRAW_open(draw)) + return TRUE; + + DRAW->SetBackground(draw, GB_DRAW_COLOR_DEFAULT); + DRAW->SetForeground(draw, GB_DRAW_COLOR_DEFAULT); + DRAW->Fill.SetColor(draw, GB_DRAW_COLOR_DEFAULT); + } + + return FALSE; +} + + +void DRAW_end() +{ + GB_DRAW *draw; + + if (!_current) + return; + + draw = _current; + _current = _current->previous; + + DRAW_close(draw); + + if (draw->paint) + PAINT_open(draw->paint); + + GB.Unref(POINTER(&draw->device)); + GB.Free(POINTER(&draw)); +} + + +BEGIN_METHOD(CDRAW_begin, GB_OBJECT device) + + void *device = VARG(device); + + if (GB.CheckObject(device)) + return; + + DRAW_begin(device); + +END_METHOD + + +BEGIN_METHOD_VOID(CDRAW_end) + + DRAW_end(); + +END_METHOD + + +BEGIN_METHOD_VOID(CDRAW_exit) + + while (_current) + DRAW_end(); + +END_METHOD + + +BEGIN_METHOD_VOID(CDRAW_save) + + CHECK_DEVICE(); + DRAW->Save(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(CDRAW_restore) + + CHECK_DEVICE(); + DRAW->Restore(THIS); + +END_METHOD + +BEGIN_PROPERTY(CDRAW_device) + + if (THIS) + GB.ReturnObject(THIS->device); + else + GB.ReturnNull(); + +END_PROPERTY + +BEGIN_PROPERTY(CDRAW_width) + + CHECK_DEVICE(); + GB.ReturnInteger(THIS->width); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_height) + + CHECK_DEVICE(); + GB.ReturnInteger(THIS->height); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_resolution) + + CHECK_DEVICE(); + GB.ReturnInteger(THIS->resolution); + +END_PROPERTY + +static void handle_int_property(void *_param, int (*get)(GB_DRAW *), void (*set)(GB_DRAW *, int)) +{ + if (READ_PROPERTY) + GB.ReturnInteger((*get)(THIS)); + else + (*set)(THIS, VPROP(GB_INTEGER)); +} + +BEGIN_PROPERTY(CDRAW_background) + + CHECK_DEVICE(); + handle_int_property(_param, DRAW->GetBackground, DRAW->SetBackground); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_foreground) + + CHECK_DEVICE(); + handle_int_property(_param, DRAW->GetForeground, DRAW->SetForeground); + +END_PROPERTY + +BEGIN_PROPERTY(Draw_ClipRect) + + CRECT *rect; + + CHECK_DEVICE(); + + if (READ_PROPERTY) + { + if (!DRAW->Clip.IsEnabled(THIS)) + { + GB.ReturnNull(); + return; + } + + rect = CRECT_create(); + DRAW->Clip.Get(THIS, &rect->x, &rect->y, &rect->w, &rect->h); + GB.ReturnObject(rect); + } + else + { + rect = (CRECT *)VPROP(GB_OBJECT); + DRAW->Clip.SetEnabled(THIS, FALSE); + if (rect) + DRAW->Clip.Set(THIS, rect->x, rect->y, rect->w, rect->h); + } + +END_PROPERTY + +BEGIN_PROPERTY(CDRAW_clip_x) + + int val; + + CHECK_DEVICE(); + DRAW->Clip.Get(THIS, &val, NULL, NULL, NULL); + GB.ReturnInteger(val); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_clip_y) + + int val; + + CHECK_DEVICE(); + DRAW->Clip.Get(THIS, NULL, &val, NULL, NULL); + GB.ReturnInteger(val); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_clip_w) + + int val; + + CHECK_DEVICE(); + DRAW->Clip.Get(THIS, NULL, NULL, &val, NULL); + GB.ReturnInteger(val); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_clip_h) + + int val; + + CHECK_DEVICE(); + DRAW->Clip.Get(THIS, NULL, NULL, NULL, &val); + GB.ReturnInteger(val); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_clip_enabled) + + CHECK_DEVICE(); + + if (READ_PROPERTY) + GB.ReturnBoolean(DRAW->Clip.IsEnabled(THIS)); + else + DRAW->Clip.SetEnabled(THIS, VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_METHOD(CDRAW_clip, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + CHECK_DEVICE(); + DRAW->Clip.Set(THIS, VARG(x), VARG(y), VARG(w), VARG(h)); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_invert) + + CHECK_DEVICE(); + + if (READ_PROPERTY) + GB.ReturnBoolean(DRAW->IsInverted(THIS)); + else + DRAW->SetInverted(THIS, VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_transparent) + + CHECK_DEVICE(); + + if (READ_PROPERTY) + GB.ReturnBoolean(DRAW->IsTransparent(THIS)); + else + DRAW->SetTransparent(THIS, VPROP(GB_BOOLEAN)); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_font) + + CHECK_DEVICE(); + + if (READ_PROPERTY) + GB.ReturnObject(DRAW->GetFont(THIS)); + else + { + if (GB.CheckObject(VPROP(GB_OBJECT))) + return; + DRAW->SetFont(THIS, VPROP(GB_OBJECT)); + } + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_line_width) + + CHECK_DEVICE(); + handle_int_property(_param, DRAW->Line.GetWidth, DRAW->Line.SetWidth); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_line_style) + + CHECK_DEVICE(); + handle_int_property(_param, DRAW->Line.GetStyle, DRAW->Line.SetStyle); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_fill_color) + + CHECK_DEVICE(); + handle_int_property(_param, DRAW->Fill.GetColor, DRAW->Fill.SetColor); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_fill_style) + + CHECK_DEVICE(); + handle_int_property(_param, DRAW->Fill.GetStyle, DRAW->Fill.SetStyle); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_fill_x) + + int x, y; + + CHECK_DEVICE(); + + DRAW->Fill.GetOrigin(THIS, &x, &y); + + if (READ_PROPERTY) + GB.ReturnInteger(x); + else + DRAW->Fill.SetOrigin(THIS, VPROP(GB_INTEGER), y); + +END_PROPERTY + + +BEGIN_PROPERTY(CDRAW_fill_y) + + int x, y; + + CHECK_DEVICE(); + + DRAW->Fill.GetOrigin(THIS, &x, &y); + + if (READ_PROPERTY) + GB.ReturnInteger(y); + else + DRAW->Fill.SetOrigin(THIS, x, VPROP(GB_INTEGER)); + +END_PROPERTY + + +BEGIN_METHOD(CDRAW_point, GB_INTEGER x; GB_INTEGER y) + + int x, y; + + CHECK_DEVICE(); + + x = VARG(x); + y = VARG(y); + + if (THIS->xform) + MATRIX_map_point(THIS_MATRIX, &x, &y); + + DRAW->Draw.Point(THIS, x, y); + +END_METHOD + + +BEGIN_METHOD(CDRAW_line, GB_INTEGER x1; GB_INTEGER y1; GB_INTEGER x2; GB_INTEGER y2) + + int x1, y1, x2, y2; + + CHECK_DEVICE(); + + x1 = VARG(x1); + y1 = VARG(y1); + x2 = VARG(x2); + y2 = VARG(y2); + + if (THIS->xform) + { + MATRIX_map_point(THIS_MATRIX, &x1, &y1); + MATRIX_map_point(THIS_MATRIX, &x2, &y2); + } + + DRAW->Draw.Line(THIS, x1, y1, x2, y2); + +END_METHOD + + +BEGIN_METHOD(CDRAW_rect, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + int x, y, w, h; + + CHECK_DEVICE(); + + x = VARG(x); + y = VARG(y); + w = VARG(w); + h = VARG(h); + + if (w < 0) + x += w, w = (-w); + if (h < 0) + y += h, h = (-h); + + if (THIS->xform) + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + + if (w < 1 || h < 1) + return; + + DRAW->Draw.Rect(THIS, x, y, w, h); + +END_METHOD + +BEGIN_METHOD(CDRAW_fill_rect, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER color) + + int x, y, w, h; + int fill_style, line_style, fill_color; + + CHECK_DEVICE(); + + x = VARG(x); + y = VARG(y); + w = VARG(w); + h = VARG(h); + + if (w < 0) + x += w, w = (-w); + if (h < 0) + y += h, h = (-h); + + if (THIS->xform) + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + + if (w < 1 || h < 1) + return; + + line_style = DRAW->Line.GetStyle(THIS); + DRAW->Line.SetStyle(THIS, 0); + fill_style = DRAW->Fill.GetStyle(THIS); + DRAW->Fill.SetStyle(THIS, 1); + fill_color = DRAW->Fill.GetColor(THIS); + if (!MISSING(color)) + DRAW->Fill.SetColor(THIS, VARG(color)); + + DRAW->Draw.Rect(THIS, x, y, w, h); + + DRAW->Line.SetStyle(THIS, line_style); + DRAW->Fill.SetStyle(THIS, fill_style); + DRAW->Fill.SetColor(THIS, fill_color); + +END_METHOD + + +BEGIN_METHOD_VOID(CDRAW_clear) + + int fill_style, line_style, fill_color, bg_color; + + CHECK_DEVICE(); + + line_style = DRAW->Line.GetStyle(THIS); + DRAW->Line.SetStyle(THIS, 0); + fill_style = DRAW->Fill.GetStyle(THIS); + DRAW->Fill.SetStyle(THIS, 1); + bg_color = DRAW->GetBackground(THIS); + DRAW->SetBackground(THIS, GB_DRAW_COLOR_DEFAULT); + fill_color = DRAW->Fill.GetColor(THIS); + DRAW->Fill.SetColor(THIS, DRAW->GetBackground(THIS)); + + DRAW->Draw.Rect(THIS, 0, 0, THIS->width, THIS->height); + + DRAW->Line.SetStyle(THIS, line_style); + DRAW->Fill.SetStyle(THIS, fill_style); + DRAW->SetBackground(THIS, bg_color); + DRAW->Fill.SetColor(THIS, fill_color); + +END_METHOD + +// BEGIN_METHOD(CDRAW_round_rect, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_FLOAT round) +// +// CHECK_DEVICE(); +// DRAW->Draw.RoundRect(THIS, VARG(x), VARG(y), VARG(w), VARG(h), VARGOPT(round, 0.25)); +// +// END_METHOD + + +BEGIN_METHOD(CDRAW_ellipse, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_FLOAT start; GB_FLOAT end) + + int x, y, w, h; + + CHECK_DEVICE(); + + x = VARG(x); + y = VARG(y); + w = VARG(w); + h = VARG(h); + + if (THIS->xform) + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + + if (w < 1 || h < 1) + return; + + DRAW->Draw.Ellipse(THIS, x, y, w, h, VARGOPT(start, 0.0), VARGOPT(end, 0.0)); + +END_METHOD + + +BEGIN_METHOD(CDRAW_arc, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_FLOAT start; GB_FLOAT end) + + int x, y, w, h; + + CHECK_DEVICE(); + + x = VARG(x); + y = VARG(y); + w = VARG(w); + h = VARG(h); + + if (THIS->xform) + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + + if (w < 1 || h < 1) + return; + + DRAW->Draw.Arc(THIS, x, y, w, h, VARGOPT(start, 0.0), VARGOPT(end, 0.0)); + +END_METHOD + + +BEGIN_METHOD(CDRAW_circle, GB_INTEGER x; GB_INTEGER y; GB_INTEGER radius; GB_FLOAT start; GB_FLOAT end) + + int x, y, w, h; + int radius; + + CHECK_DEVICE(); + + radius = VARG(radius); + if (radius <= 0) + return; + + x = VARG(x) - radius + 1; + y = VARG(y) - radius + 1; + w = radius * 2 - 1; + h = radius * 2 - 1; + + if (THIS->xform) + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + + DRAW->Draw.Ellipse(THIS, x, y, w, h, VARGOPT(start, 0.0), VARGOPT(end, 0.0)); + +END_METHOD + + +BEGIN_METHOD(CDRAW_text, GB_STRING text; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER align) + + int x, y, w, h; + + CHECK_DEVICE(); + + x = VARG(x); + y = VARG(y); + w = VARGOPT(w, -1); + h = VARGOPT(h, -1); + + if (THIS->xform) + { + if (w < 0 || h < 0) + MATRIX_map_point(THIS_MATRIX, &x, &y); + else + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + } + + DRAW->Draw.Text(THIS, STRING(text), LENGTH(text), x, y, w, h, VARGOPT(align, GB_DRAW_ALIGN_DEFAULT)); + +END_METHOD + + +BEGIN_METHOD(CDRAW_text_width, GB_STRING text) + + int w; + CHECK_DEVICE(); + DRAW->Draw.TextSize(THIS, STRING(text), LENGTH(text), &w, NULL); + GB.ReturnInteger(w); + +END_METHOD + + +BEGIN_METHOD(CDRAW_text_height, GB_STRING text) + + int h; + CHECK_DEVICE(); + DRAW->Draw.TextSize(THIS, STRING(text), LENGTH(text), NULL, &h); + GB.ReturnInteger(h); + +END_METHOD + + +BEGIN_METHOD(CDRAW_rich_text, GB_STRING text; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER align) + + int x, y, w, h; + + CHECK_DEVICE(); + + x = VARG(x); + y = VARG(y); + w = VARGOPT(w, -1); + h = VARGOPT(h, -1); + + if (THIS->xform) + { + if (w < 0 || h < 0) + MATRIX_map_point(THIS_MATRIX, &x, &y); + else + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + } + + DRAW->Draw.RichText(THIS, STRING(text), LENGTH(text), x, y, w, h, VARGOPT(align, GB_DRAW_ALIGN_DEFAULT)); + +END_METHOD + + +BEGIN_METHOD(CDRAW_rich_text_width, GB_STRING text) + + int w; + CHECK_DEVICE(); + DRAW->Draw.RichTextSize(THIS, STRING(text), LENGTH(text), -1, &w, NULL); + GB.ReturnInteger(w); + +END_METHOD + + +BEGIN_METHOD(CDRAW_rich_text_height, GB_STRING text; GB_INTEGER width) + + int w = VARGOPT(width, -1); + int h; + CHECK_DEVICE(); + DRAW->Draw.RichTextSize(THIS, STRING(text), LENGTH(text), w, NULL, &h); + GB.ReturnInteger(h); + +END_METHOD + + +BEGIN_METHOD(CDRAW_polyline, GB_OBJECT points) + + uint n; + GB_ARRAY points = VARG(points); + int *coord; + + CHECK_DEVICE(); + + n = GB.Array.Count(points) / 2; + if (n == 0) + return; + + coord = (int *)GB.Array.Get(points, 0); + + if (THIS->xform) + coord = MATRIX_map_array(THIS_MATRIX, coord, n); + + DRAW->Draw.Polyline(THIS, n, coord); + + if (THIS->xform) + MATRIX_free_array(&coord); + +END_METHOD + + +BEGIN_METHOD(CDRAW_polygon, GB_OBJECT points) + + uint n; + GB_ARRAY points = VARG(points); + int *coord; + + CHECK_DEVICE(); + + n = GB.Array.Count(points) / 2; + if (n == 0) + return; + + coord = (int *)GB.Array.Get(points, 0); + + if (THIS->xform) + coord = MATRIX_map_array(THIS_MATRIX, coord, n); + + DRAW->Draw.Polygon(THIS, n, coord); + + if (THIS->xform) + MATRIX_free_array(&coord); + +END_METHOD + + +BEGIN_METHOD(CDRAW_picture, GB_OBJECT picture; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER sx; GB_INTEGER sy; GB_INTEGER sw; GB_INTEGER sh) + + GB_PICTURE picture = VARG(picture); + GB_PICTURE_INFO info; + int x, y, w, h; + + CHECK_DEVICE(); + + if (GB.CheckObject(picture)) + return; + + DRAW->GetPictureInfo(THIS, picture, &info); + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + w = VARGOPT(w, info.width); + h = VARGOPT(h, info.height); + + if (THIS->xform) + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + + DRAW->Draw.Picture(THIS, picture, + x, y, w, h, + VARGOPT(sx, 0), VARGOPT(sy, 0), VARGOPT(sw, -1), VARGOPT(sh, -1)); + +END_METHOD + + +BEGIN_METHOD(CDRAW_image, GB_OBJECT image; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER sx; GB_INTEGER sy; GB_INTEGER sw; GB_INTEGER sh) + + GB_IMAGE image = VARG(image); + int x, y, w, h; + GB_IMG *info = (GB_IMG *)image; + + CHECK_DEVICE(); + + if (GB.CheckObject(image)) + return; + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + w = VARGOPT(w, info->width); + h = VARGOPT(h, info->height); + + if (THIS->xform) + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + + DRAW->Draw.Image(THIS, image, + x, y, w, h, + VARGOPT(sx, 0), VARGOPT(sy, 0), VARGOPT(sw, -1), VARGOPT(sh, -1)); + +END_METHOD + +static uint blend_color(uint col, uint gray, uint alpha) +{ + gray *= (0xFF - alpha); + + return + ((col & 0xFF) * alpha + gray) >> 8 + | ((((col >> 8) & 0xFF) * alpha + gray) >> 8) << 8 + | ((((col >> 16) & 0xFF) * alpha + gray) >> 8) << 16; +} + +BEGIN_METHOD(CDRAW_zoom, GB_OBJECT image; GB_INTEGER zoom; GB_INTEGER x; GB_INTEGER y; GB_INTEGER sx; GB_INTEGER sy; GB_INTEGER sw; GB_INTEGER sh) + + GB_IMAGE image = VARG(image); + GB_IMG *info = (GB_IMG *)image; + int zoom, size, size2; + int x, y, sx, sy, sw, sh; + int i, j, xr, yr; + uint col, col1, col2, last_col; + bool border; + int fill_style, fill_color; + uint a; + bool opaque; + + CHECK_DEVICE(); + + if (GB.CheckObject(image)) + return; + + zoom = VARG(zoom); + if (zoom < 1) + { + GB.Error("Bad zoom factor"); + return; + } + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + + sx = VARGOPT(sx, 0); + sy = VARGOPT(sy, 0); + sw = VARGOPT(sw, info->width); + sh = VARGOPT(sh, info->height); + + DRAW_NORMALIZE(x, y, sw, sh, sx, sy, sw, sh, info->width, info->height); + + //DRAW->Fill.GetOrigin(THIS, &ox, &oy); + + border = DRAW->Line.GetStyle(THIS); + + if (zoom < 6 && !border) + { + DRAW->Fill.SetStyle(THIS, 1); + DRAW->Fill.SetColor(THIS, 0x979797); + DRAW->Draw.Rect(THIS, x, y, sw * zoom, sh * zoom); + DRAW->Draw.Image(THIS, image, x, y, sw * zoom, sh * zoom, sx, sy, sw, sh); + } + else + { + //IMAGE.Convert(info, GB_IMAGE_BGRA); + // May have to convert the image data + //if (info->format != GB_IMAGE_BGRA && info->format != GB_IMAGE_BGRX) + // GB.Alloc(POINTER(&conv), sw * sizeof(int)); + + size = zoom; + size2 = size / 2; + //if (border) size++; + + fill_style = DRAW->Fill.GetStyle(THIS); + fill_color = DRAW->Fill.GetColor(THIS); + + //last_style = -1; + //style = border; + last_col = 0; + col1 = 0; + col2 = 0; + a = 0xFF; + opaque = TRUE; + + DRAW->Fill.SetStyle(THIS, 1); // FILL_SOLID + DRAW->Fill.SetColor(THIS, 0); + + for (j = sy, yr = y; j < (sy + sh); j++, yr += zoom) + { + for (i = sx, xr = x; i < (sx + sw); i++, xr += zoom) + { + col = IMAGE.GetPixel(info, i, j); + + if (col != last_col) + { + last_col = col; + + a = (col >> 24) ^ 0xFF; + col &= 0xFFFFFF; + + if (a < 0xFF) + { + if (a == 0) + { + col1 = 0x808080; + col2 = 0xC0C0C0; + } + else + { + col1 = blend_color(col, 0x80, a); + col2 = blend_color(col, 0xC0, a); + } + opaque = FALSE; + } + else + { + opaque = TRUE; + DRAW->Fill.SetColor(THIS, col); + } + } + + if (opaque) + DRAW->Draw.Rect(THIS, xr, yr, size + 1, size + 1); + else + { + DRAW->Fill.SetColor(THIS, col1); + DRAW->Line.SetStyle(THIS, 0); + DRAW->Draw.Rect(THIS, xr, yr, size, size); + DRAW->Fill.SetColor(THIS, col2); + DRAW->Draw.Rect(THIS, xr + size2, yr, size - size2, size2); + DRAW->Draw.Rect(THIS, xr, yr + size2, size2, size - size2); + DRAW->Line.SetStyle(THIS, border); + if (border && a >= 16) + { + DRAW->Fill.SetStyle(THIS, 0); + DRAW->Draw.Rect(THIS, xr, yr, size + 1, size + 1); + DRAW->Fill.SetStyle(THIS, 1); + } + } + + } + } + + DRAW->Fill.SetStyle(THIS, fill_style); + DRAW->Fill.SetColor(THIS, fill_color); + } + +END_METHOD + + +BEGIN_METHOD(CDRAW_tile, GB_OBJECT picture; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + int x, y, w, h; + + GB_PICTURE picture = VARG(picture); + + CHECK_DEVICE(); + + if (GB.CheckObject(picture)) + return; + + x = VARG(x); + y = VARG(y); + w = VARG(w); + h = VARG(h); + + if (THIS->xform) + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); + + DRAW->Draw.TiledPicture(THIS, picture, x, y, w, h); + +END_METHOD + + +BEGIN_METHOD_VOID(CDRAW_reset) + + CHECK_DEVICE(); + MATRIX_reset(THIS_MATRIX); + THIS->xform = FALSE; + +END_METHOD + + +BEGIN_METHOD_VOID(CDRAW_push) + + MATRIX *save; + + CHECK_DEVICE(); + + GB.Alloc(POINTER(&save), sizeof(MATRIX)); + *save = *THIS_MATRIX; + save->next = THIS->save; + THIS->save = save; + +END_METHOD + + +BEGIN_METHOD_VOID(CDRAW_pop) + + MATRIX *save; + + CHECK_DEVICE(); + + if (!THIS->save) + { + MATRIX_reset(THIS_MATRIX); + return; + } + + save = THIS->save; + THIS->save = save->next; + *THIS_MATRIX = *save; + + GB.Free(POINTER(&save)); + +END_METHOD + + +BEGIN_METHOD(CDRAW_translate, GB_FLOAT dx; GB_FLOAT dy) + + double dx = VARG(dx); + double dy = VARG(dy); + + CHECK_DEVICE(); + MATRIX_translate(THIS_MATRIX, dx, dy); + THIS->xform = !THIS->matrix.identity; + +END_METHOD + + +BEGIN_METHOD(CDRAW_scale, GB_FLOAT dx; GB_FLOAT dy) + + double dx = VARG(dx); + double dy = VARG(dy); + + CHECK_DEVICE(); + MATRIX_scale(THIS_MATRIX, dx, dy); + THIS->xform = !THIS->matrix.identity; + +END_METHOD + + +// BEGIN_METHOD(CDRAW_rotate, GB_FLOAT a) +// +// double a = VARG(a); +// +// DP->rotate(a); +// if (DPM) +// DPM->rotate(a); +// +// END_METHOD + +/**************************************************************************** + + Style API + +****************************************************************************/ + +#define GET_COORD() \ + int x, y, w, h; \ +\ + CHECK_DEVICE(); \ +\ + x = VARG(x); \ + y = VARG(y); \ + w = VARG(w); \ + h = VARG(h); \ +\ + if (THIS->xform) \ + MATRIX_map_rect(THIS_MATRIX, &x, &y, &w, &h); \ +\ + if (w < 1 || h < 1) \ + return; + + +BEGIN_METHOD(CDRAW_style_arrow, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER type; GB_INTEGER state) + + GET_COORD(); + DRAW->Style.Arrow(THIS, x, y, w, h, VARG(type), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(CDRAW_style_check, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER value; GB_INTEGER state) + + GET_COORD(); + DRAW->Style.Check(THIS, x, y, w, h, VARG(value), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(CDRAW_style_option, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN value; GB_INTEGER state) + + GET_COORD(); + DRAW->Style.Option(THIS, x, y, w, h, VARG(value), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(CDRAW_style_separator, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN vertical; GB_INTEGER state) + + GET_COORD(); + DRAW->Style.Separator(THIS, x, y, w, h, VARGOPT(vertical, FALSE), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(CDRAW_style_button, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN value; GB_INTEGER state; GB_BOOLEAN flat) + + GET_COORD(); + DRAW->Style.Button(THIS, x, y, w, h, VARG(value), VARGOPT(state, GB_DRAW_STATE_NORMAL), VARGOPT(flat, FALSE)); + +END_METHOD + +BEGIN_METHOD(CDRAW_style_panel, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER border; GB_INTEGER state) + + GET_COORD(); + DRAW->Style.Panel(THIS, x, y, w, h, VARG(border), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(CDRAW_style_handle, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_BOOLEAN vertical; GB_INTEGER state) + + GET_COORD(); + DRAW->Style.Handle(THIS, x, y, w, h, VARGOPT(vertical, FALSE), VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +BEGIN_METHOD(DrawStyle_Box, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER state) + + GET_COORD(); + DRAW->Style.Box(THIS, x, y, w, h, VARGOPT(state, GB_DRAW_STATE_NORMAL)); + +END_METHOD + +GB_DESC CDrawClipDesc[] = +{ + GB_DECLARE(".Draw.Clip", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_PROPERTY_READ("X", "i", CDRAW_clip_x), + GB_STATIC_PROPERTY_READ("Y", "i", CDRAW_clip_y), + GB_STATIC_PROPERTY_READ("W", "i", CDRAW_clip_w), + GB_STATIC_PROPERTY_READ("H", "i", CDRAW_clip_h), + GB_STATIC_PROPERTY_READ("Width", "i", CDRAW_clip_w), + GB_STATIC_PROPERTY_READ("Height", "i", CDRAW_clip_h), + + GB_STATIC_PROPERTY("Enabled", "b", CDRAW_clip_enabled), + GB_STATIC_METHOD("_call", NULL, CDRAW_clip, "(X)i(Y)i(Width)i(Height)i"), + + GB_END_DECLARE +}; + +GB_DESC CDrawStyleDesc[] = +{ + GB_DECLARE(".Draw.Style", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("Arrow", NULL, CDRAW_style_arrow, "(X)i(Y)i(Width)i(Height)i(Type)i[(Flag)i]"), + GB_STATIC_METHOD("Check", NULL, CDRAW_style_check, "(X)i(Y)i(Width)i(Height)i(Value)i[(Flag)i]"), + GB_STATIC_METHOD("Option", NULL, CDRAW_style_option, "(X)i(Y)i(Width)i(Height)i(Value)b[(Flag)i]"), + GB_STATIC_METHOD("Separator", NULL, CDRAW_style_separator, "(X)i(Y)i(Width)i(Height)i[(Vertical)b(Flag)i]"), + GB_STATIC_METHOD("Button", NULL, CDRAW_style_button, "(X)i(Y)i(Width)i(Height)i(Value)b[(Flag)i(Flat)b]"), + GB_STATIC_METHOD("Panel", NULL, CDRAW_style_panel, "(X)i(Y)i(Width)i(Height)i(Border)i[(Flag)i]"), + GB_STATIC_METHOD("Handle", NULL, CDRAW_style_handle, "(X)i(Y)i(Width)i(Height)i[(Vertical)b(Flag)i]"), + GB_STATIC_METHOD("Box", NULL, DrawStyle_Box, "(X)i(Y)i(Width)i(Height)i[(Flag)i]"), + + GB_END_DECLARE +}; + +GB_DESC CDrawDesc[] = +{ + GB_DECLARE("Draw", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_exit", NULL, CDRAW_exit, NULL), + + GB_STATIC_METHOD("Begin", NULL, CDRAW_begin, "(Device)o"), + GB_STATIC_METHOD("End", NULL, CDRAW_end, NULL), + + GB_STATIC_METHOD("Save", NULL, CDRAW_save, NULL), + GB_STATIC_METHOD("Restore", NULL, CDRAW_restore, NULL), + + GB_STATIC_PROPERTY_READ("Device", "o", CDRAW_device), + + GB_STATIC_PROPERTY_READ("W", "i", CDRAW_width), + GB_STATIC_PROPERTY_READ("H", "i", CDRAW_height), + GB_STATIC_PROPERTY_READ("Width", "i", CDRAW_width), + GB_STATIC_PROPERTY_READ("Height", "i", CDRAW_height), + GB_STATIC_PROPERTY_READ("Resolution", "i", CDRAW_resolution), + + GB_STATIC_PROPERTY("ClipRect", "Rect", Draw_ClipRect), + GB_STATIC_PROPERTY_SELF("Clip", ".Draw.Clip"), + GB_STATIC_PROPERTY_SELF("Style", ".Draw.Style"), + + GB_STATIC_PROPERTY("Background", "i", CDRAW_background), + GB_STATIC_PROPERTY("Foreground", "i", CDRAW_foreground), + //GB_STATIC_PROPERTY("BackColor", "i", CDRAW_background), + //GB_STATIC_PROPERTY("ForeColor", "i", CDRAW_foreground), + + GB_STATIC_PROPERTY("Invert", "b", CDRAW_invert), + GB_STATIC_PROPERTY("Transparent", "b", CDRAW_transparent), + + GB_STATIC_PROPERTY("Font", "Font", CDRAW_font), + + GB_STATIC_PROPERTY("LineWidth", "i", CDRAW_line_width), + GB_STATIC_PROPERTY("LineStyle", "i", CDRAW_line_style), + + GB_STATIC_PROPERTY("FillColor", "i", CDRAW_fill_color), + GB_STATIC_PROPERTY("FillStyle", "i", CDRAW_fill_style), + GB_STATIC_PROPERTY("FillX", "i", CDRAW_fill_x), + GB_STATIC_PROPERTY("FillY", "i", CDRAW_fill_y), + + GB_STATIC_METHOD("Clear", NULL, CDRAW_clear, NULL), + GB_STATIC_METHOD("Point", NULL, CDRAW_point, "(X)i(Y)i"), + GB_STATIC_METHOD("Line", NULL, CDRAW_line, "(X1)i(Y1)i(X2)i(Y2)i"), + GB_STATIC_METHOD("Rect", NULL, CDRAW_rect, "(X)i(Y)i(Width)i(Height)i"), + GB_STATIC_METHOD("FillRect", NULL, CDRAW_fill_rect, "(X)i(Y)i(Width)i(Height)i[(Color)i]"), + //GB_STATIC_METHOD("RoundRect", NULL, CDRAW_round_rect, "(X)i(Y)i(Width)i(Height)i[(Round)f]"), + GB_STATIC_METHOD("Circle", NULL, CDRAW_circle, "(X)i(Y)i(Radius)i[(Start)f(End)f]"), + GB_STATIC_METHOD("Arc", NULL, CDRAW_arc, "(X)i(Y)i(Width)i(Height)i[(Start)f(End)f]"), + GB_STATIC_METHOD("Ellipse", NULL, CDRAW_ellipse, "(X)i(Y)i(Width)i(Height)i[(Start)f(End)f]"), + GB_STATIC_METHOD("Text", NULL, CDRAW_text, "(Text)s(X)i(Y)i[(Width)i(Height)i(Alignment)i)]"), + GB_STATIC_METHOD("TextWidth", "i", CDRAW_text_width, "(Text)s"), + GB_STATIC_METHOD("TextHeight", "i", CDRAW_text_height, "(Text)s"), + GB_STATIC_METHOD("RichText", NULL, CDRAW_rich_text, "(Text)s(X)i(Y)i[(Width)i(Height)i(Alignment)i)]"), + GB_STATIC_METHOD("RichTextWidth", "i", CDRAW_rich_text_width, "(Text)s"), + GB_STATIC_METHOD("RichTextHeight", "i", CDRAW_rich_text_height, "(Text)s[(Width)i]"), + GB_STATIC_METHOD("Polyline", NULL, CDRAW_polyline, "(Points)Integer[]"), + GB_STATIC_METHOD("Polygon", NULL, CDRAW_polygon, "(Points)Integer[]"), + + GB_STATIC_METHOD("Picture", NULL, CDRAW_picture, "(Picture)Picture;(X)i(Y)i[(Width)i(Height)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + //GB_STATIC_METHOD("AlignedPicture", NULL, CDRAW_picture, "(Picture)Picture;(X)i(Y)i(Width)i(Height)i(Alignment)i"), + GB_STATIC_METHOD("Tile", NULL, CDRAW_tile, "(Picture)Picture;(X)i(Y)i(Width)i(Height)i"), + GB_STATIC_METHOD("Image", NULL, CDRAW_image, "(Image)Image;(X)i(Y)i[(Width)i(Height)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + //GB_STATIC_METHOD("AlignedImage", NULL, CDRAW_picture, "(Image)Image;(X)i(Y)i(Width)i(Height)i(Alignment)i"), + GB_STATIC_METHOD("Zoom", NULL, CDRAW_zoom, "(Image)Image;(Zoom)i(X)i(Y)i[(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + + GB_STATIC_METHOD("Reset", NULL, CDRAW_reset, NULL), + GB_STATIC_METHOD("Push", NULL, CDRAW_push, NULL), + GB_STATIC_METHOD("Pop", NULL, CDRAW_pop, NULL), + GB_STATIC_METHOD("Translate", NULL, CDRAW_translate, "(DX)f(DY)f"), + GB_STATIC_METHOD("Scale", NULL, CDRAW_scale, "(SX)f(SY)f"), + + GB_CONSTANT("Normal", "i", GB_DRAW_STATE_NORMAL), + GB_CONSTANT("Disabled", "i", GB_DRAW_STATE_DISABLED), + GB_CONSTANT("Focus", "i", GB_DRAW_STATE_FOCUS), + GB_CONSTANT("Hover", "i", GB_DRAW_STATE_HOVER), + //GB_CONSTANT("ToolButton", "i", GB_DRAW_STATE_TOOL_BUTTON), + #if 0 + GB_STATIC_METHOD("Drawing", NULL, CDRAW_drawing, "(Drawing)Drawing;(X)i(Y)i[(Width)i(Height)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + + GB_STATIC_METHOD("Rotate", NULL, CDRAW_rotate, "(Angle)f"), + #endif + + GB_END_DECLARE +}; + + diff --git a/main/lib/draw/cdraw.h b/main/lib/draw/cdraw.h new file mode 100644 index 00000000..ed8e1c18 --- /dev/null +++ b/main/lib/draw/cdraw.h @@ -0,0 +1,45 @@ +/*************************************************************************** + + cdraw.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CDRAW_H +#define __CDRAW_H + +#include "gambas.h" +#include "gb.draw.h" + +#ifndef __CDRAW_C + +extern GB_DESC CDrawDesc[]; +extern GB_DESC CDrawClipDesc[]; +extern GB_DESC CDrawStyleDesc[]; + +#endif + +GB_DRAW *DRAW_get_current(); +GB_DRAW *DRAW_from_device(void *device); +bool DRAW_begin(void *device); +void DRAW_end(); +bool DRAW_open(GB_DRAW *draw); +void DRAW_close(GB_DRAW *draw); + +#endif diff --git a/main/lib/draw/cpaint.c b/main/lib/draw/cpaint.c new file mode 100644 index 00000000..1196be01 --- /dev/null +++ b/main/lib/draw/cpaint.c @@ -0,0 +1,1801 @@ +/*************************************************************************** + + cpaint.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPAINT_C + +#include "gb.image.h" +#include "main.h" +#include "cpaint.h" + +static GB_PAINT *_current = NULL; +#define THIS _current +#define PAINT _current->desc + +#define XTHIS ((PAINT_EXTENTS *)_object) + +#define MTHIS ((PAINT_MATRIX *)_object) +#define MPAINT (_matrix_desc) + +#define BTHIS ((PAINT_BRUSH *)_object) +#define BPAINT (BTHIS->desc) + +#define CHECK_DEVICE() if (check_device()) return +#define CHECK_PATH() if (check_path()) return + +#define RAD(_deg) ((_deg) * M_PI / 180) + +static GB_PAINT_MATRIX_DESC *_matrix_desc = NULL; + +static void load_matrix_interface() +{ + if (!_matrix_desc) + { + _matrix_desc = (GB_PAINT_MATRIX_DESC *)GB.GetClassInterface(GB.FindClass("Image"), "PaintMatrix"); + if (!_matrix_desc) + { + fprintf(stderr, "gb.draw: error: unable to find PaintMatrix interface\n"); + abort(); + } + } +} + +static bool check_device() +{ + if (!_current || !_current->extra) + { + GB.Error("No current device"); + return TRUE; + } + else + return FALSE; +} + +static bool check_path() +{ + if (THIS->has_path) + { + GB.Error("Pending path"); + return TRUE; + } + else + return FALSE; +} + +GB_PAINT *PAINT_get_current() +{ + if (check_device()) + return NULL; + else + return _current; +} + +GB_PAINT *PAINT_from_device(void *device) +{ + GB_PAINT *d; + + for (d = _current; d; d = d->previous) + { + if (d->device == device && d->opened) + break; + } + + return d; +} + +bool PAINT_is_painted(void *device) +{ + return PAINT_from_device(device) != NULL; +} + + +bool PAINT_open(GB_PAINT *paint) +{ + if (paint->opened) + return FALSE; + + //fprintf(stderr, "PAINT_open: %p\n", paint); + GB.Alloc(POINTER(&paint->extra), paint->desc->size); + memset(paint->extra, 0, paint->desc->size); + paint->opened = !paint->desc->Begin(paint); + + if (!paint->opened) + GB.Free(POINTER(&paint->extra)); + + return !paint->opened; +} + +void PAINT_close(GB_PAINT *paint) +{ + if (!paint->opened) + return; + + if (!paint->other) + { + //fprintf(stderr, "PAINT_close: %p\n", paint); + paint->desc->End(paint); + GB.Free(POINTER(&paint->extra)); + } + else + PAINT->Restore(THIS); + + paint->opened = FALSE; +} + +bool PAINT_begin(void *device) +{ + GB_PAINT_DESC *desc; + GB_PAINT *paint, *other; + GB_CLASS klass; + + klass = GB.GetClass(device); + desc = (GB_PAINT_DESC *)GB.GetClassInterface(klass, "Paint"); + load_matrix_interface(); + + if (!desc) + { + GB.Error("Not a paintable object"); + return TRUE; + } + + GB.Alloc(POINTER(&paint), sizeof(GB_PAINT)); + + other = PAINT_from_device(device); + + paint->desc = desc; + GB.Ref(device); + paint->device = device; + paint->brush = NULL; + paint->opened = FALSE; + paint->other = FALSE; + paint->has_path = FALSE; + paint->tag = NULL; + paint->area.x = paint->area.y = 0; + + paint->previous = _current; + _current = paint; + + //paint->draw = DRAW_from_device(device); + //if (paint->draw) + // DRAW_close(paint->draw); + + if (other) + { + paint->extra = other->extra; + paint->opened = TRUE; + paint->other = TRUE; + paint->area = other->area; + paint->resolutionX = other->resolutionX; + paint->resolutionY = other->resolutionY; + paint->brush = other->brush; + if (paint->brush) + GB.Ref(paint->brush); + PAINT->Save(THIS); + } + else + { + if (PAINT_open(paint)) + return TRUE; + } + + return FALSE; +} + +void PAINT_end() +{ + GB_PAINT *paint; + + if (!_current) + return; + + paint = _current; + _current = _current->previous; + + PAINT_close(paint); + + //if (paint->draw) + // DRAW_open(paint->draw); + + if (paint->brush) + GB.Unref(POINTER(&paint->brush)); + GB.Unref(POINTER(&paint->device)); + GB.StoreObject(NULL, POINTER(&(paint->tag))); + GB.Free(POINTER(&paint)); +} + +void PAINT_translate(double tx, double ty) +{ + GB_TRANSFORM transform; + + if (tx == 0.0 && ty == 0.0) + return; + + MPAINT->Create(&transform); + PAINT->Matrix(THIS, FALSE, transform); + MPAINT->Translate(transform, (float)tx, (float)ty); + PAINT->Matrix(THIS, TRUE, transform); + MPAINT->Delete(&transform); +} + +void PAINT_set_area(GEOM_RECTF *area) +{ + THIS->area.x += area->x; + THIS->area.y += area->y; + + THIS->area.width = area->w; //MIN(area->w, THIS->area.width - THIS->area.x); + THIS->area.height = area->h; //MIN(area->h, THIS->area.height - THIS->area.y); + + if (THIS->area.width <= 0 || THIS->area.height <= 0) + THIS->area.width = THIS->area.height = 0; + + PAINT_translate(area->x, area->y); +} + +//---- PaintExtents --------------------------------------------------------- + +#define IMPLEMENT_EXTENTS_PROPERTY(_method, _field) \ +BEGIN_PROPERTY(_method) \ + GB.ReturnFloat(XTHIS->ext._field); \ +END_PROPERTY + +IMPLEMENT_EXTENTS_PROPERTY(PaintExtents_X, x1) +IMPLEMENT_EXTENTS_PROPERTY(PaintExtents_Y, y1) +IMPLEMENT_EXTENTS_PROPERTY(PaintExtents_X2, x2) +IMPLEMENT_EXTENTS_PROPERTY(PaintExtents_Y2, y2) + +BEGIN_PROPERTY(PaintExtents_Width) + + GB.ReturnFloat(XTHIS->ext.x2 - XTHIS->ext.x1); + +END_PROPERTY + +BEGIN_PROPERTY(PaintExtents_Height) + + GB.ReturnFloat(XTHIS->ext.y2 - XTHIS->ext.y1); + +END_PROPERTY + +BEGIN_METHOD(PaintExtents_Merge, GB_OBJECT extents) + + PAINT_EXTENTS *extents = VARG(extents); + + if (GB.CheckObject(extents)) + return; + + if (extents->ext.x1 < XTHIS->ext.x1) XTHIS->ext.x1 = extents->ext.x1; + if (extents->ext.y1 < XTHIS->ext.y1) XTHIS->ext.y1 = extents->ext.y1; + if (extents->ext.x2 > XTHIS->ext.x2) XTHIS->ext.x2 = extents->ext.x2; + if (extents->ext.y2 > XTHIS->ext.y2) XTHIS->ext.y2 = extents->ext.y2; + + GB.ReturnObject(XTHIS); + +END_METHOD + +GB_DESC PaintExtentsDesc[] = +{ + GB_DECLARE("PaintExtents", sizeof(PAINT_EXTENTS)), + + GB_PROPERTY_READ("X", "f", PaintExtents_X), + GB_PROPERTY_READ("Y", "f", PaintExtents_Y), + GB_PROPERTY_READ("X2", "f", PaintExtents_X2), + GB_PROPERTY_READ("Y2", "f", PaintExtents_Y2), + GB_PROPERTY_READ("Width", "f", PaintExtents_Width), + GB_PROPERTY_READ("Height", "f", PaintExtents_Height), + + GB_METHOD("Merge", "PaintExtents", PaintExtents_Merge, "(Extents)PaintExtents;"), + + GB_END_DECLARE +}; + + +//---- PaintMatrix ---------------------------------------------------------- + +static bool _do_not_init = FALSE; + +static PAINT_MATRIX *create_matrix(GB_TRANSFORM transform) +{ + PAINT_MATRIX *matrix; + _do_not_init = TRUE; + matrix = GB.New(GB.FindClass("PaintMatrix"), NULL, NULL); + _do_not_init = FALSE; + matrix->transform = transform; + return matrix; +} + +BEGIN_METHOD(PaintMatrix_new, GB_FLOAT xx; GB_FLOAT xy; GB_FLOAT yx; GB_FLOAT yy; GB_FLOAT x0; GB_FLOAT y0) + + load_matrix_interface(); + + if (_do_not_init) + return; + + MPAINT->Create(&MTHIS->transform); + MPAINT->Init(MTHIS->transform, VARGOPT(xx, 1.0), VARGOPT(xy, 0.0), VARGOPT(yx, 0.0), VARGOPT(yy, 1.0), VARGOPT(x0, 0.0), VARGOPT(y0, 0.0)); + +END_METHOD + +BEGIN_METHOD(PaintMatrix_call, GB_FLOAT xx; GB_FLOAT xy; GB_FLOAT yx; GB_FLOAT yy; GB_FLOAT x0; GB_FLOAT y0) + + GB_TRANSFORM transform; + + MPAINT->Create(&transform); + MPAINT->Init(transform, VARGOPT(xx, 1.0), VARGOPT(xy, 0.0), VARGOPT(yx, 0.0), VARGOPT(yy, 1.0), VARGOPT(x0, 0.0), VARGOPT(y0, 0.0)); + GB.ReturnObject(create_matrix(transform)); + +END_METHOD + +BEGIN_METHOD_VOID(PaintMatrix_free) + + if (MTHIS->transform) + MPAINT->Delete(&MTHIS->transform); + +END_METHOD + +BEGIN_METHOD_VOID(PaintMatrix_Copy) + + GB_TRANSFORM transform; + + MPAINT->Copy(&transform, MTHIS->transform); + GB.ReturnObject(create_matrix(transform)); + +END_METHOD + +BEGIN_METHOD_VOID(PaintMatrix_Reset) + + MPAINT->Init(MTHIS->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); + RETURN_SELF(); + +END_METHOD + +BEGIN_METHOD(PaintMatrix_Translate, GB_FLOAT tx; GB_FLOAT ty) + + MPAINT->Translate(MTHIS->transform, (float)VARG(tx), (float)VARG(ty)); + RETURN_SELF(); + +END_METHOD + +BEGIN_METHOD(PaintMatrix_Scale, GB_FLOAT sx; GB_FLOAT sy) + + MPAINT->Scale(MTHIS->transform, (float)VARG(sx), (float)VARG(sy)); + RETURN_SELF(); + +END_METHOD + +BEGIN_METHOD(PaintMatrix_Rotate, GB_FLOAT angle) + + MPAINT->Rotate(MTHIS->transform, (float)VARG(angle)); + RETURN_SELF(); + +END_METHOD + +BEGIN_METHOD_VOID(PaintMatrix_Invert) + + if (MPAINT->Invert(MTHIS->transform)) + GB.ReturnNull(); + else + RETURN_SELF(); + +END_METHOD + +BEGIN_METHOD(PaintMatrix_Multiply, GB_OBJECT matrix) + + PAINT_MATRIX *matrix = (PAINT_MATRIX *)VARG(matrix); + + if (GB.CheckObject(matrix)) + return; + + MPAINT->Multiply(MTHIS->transform, matrix->transform); + RETURN_SELF(); + +END_METHOD + +BEGIN_METHOD(PaintMatrix_Map, GB_OBJECT point) + + GEOM_POINTF *point = VARG(point); + double x, y; + + if (GB.CheckObject(point)) + return; + + x = point->x; + y = point->y; + + MPAINT->Map(MTHIS->transform, &x, &y); + + GB.ReturnObject(GEOM.CreatePointF(x, y)); + +END_METHOD + +GB_DESC PaintMatrixDesc[] = +{ + GB_DECLARE("PaintMatrix", sizeof(PAINT_MATRIX)), + + GB_METHOD("_new", NULL, PaintMatrix_new, "[(XX)f(XY)f(YX)f(YY)f(X0)f(Y0)f]"), + GB_STATIC_METHOD("_call", "PaintMatrix", PaintMatrix_call, "[(XX)f(XY)f(YX)f(YY)f(X0)f(Y0)f]"), + GB_METHOD("_free", NULL, PaintMatrix_free, NULL), + + GB_METHOD("Copy", "PaintMatrix", PaintMatrix_Copy, NULL), + GB_METHOD("Reset", "PaintMatrix", PaintMatrix_Reset, NULL), + GB_METHOD("Translate", "PaintMatrix", PaintMatrix_Translate, "(TX)f(TY)f"), + GB_METHOD("Scale", "PaintMatrix", PaintMatrix_Scale, "(SX)f(SY)f"), + GB_METHOD("Rotate", "PaintMatrix", PaintMatrix_Rotate, "(Angle)f"), + GB_METHOD("Invert", "PaintMatrix", PaintMatrix_Invert, NULL), + GB_METHOD("Multiply", "PaintMatrix", PaintMatrix_Multiply, "(Matrix)PaintMatrix;"), + GB_METHOD("Map", "PointF", PaintMatrix_Map, "(Point)PointF;"), + + GB_END_DECLARE +}; + + +//---- PaintBrush ----------------------------------------------------------- + +BEGIN_METHOD_VOID(PaintBrush_free) + + BPAINT->Brush.Free(BTHIS->brush); + +END_METHOD + +BEGIN_PROPERTY(PaintBrush_Matrix) + + GB_TRANSFORM transform; + PAINT_MATRIX *matrix; + + if (READ_PROPERTY) + { + MPAINT->Create(&transform); + BPAINT->Brush.Matrix(BTHIS->brush, FALSE, transform); + GB.ReturnObject(create_matrix(transform)); + } + else + { + matrix = (PAINT_MATRIX *)VPROP(GB_OBJECT); + if (!matrix) + BPAINT->Brush.Matrix(BTHIS->brush, TRUE, NULL); + else + BPAINT->Brush.Matrix(BTHIS->brush, TRUE, matrix->transform); + } + +END_PROPERTY + +BEGIN_METHOD_VOID(PaintBrush_Reset) + + BPAINT->Brush.Matrix(BTHIS->brush, TRUE, NULL); + +END_METHOD + +BEGIN_METHOD(PaintBrush_Translate, GB_FLOAT tx; GB_FLOAT ty) + + GB_TRANSFORM transform; + + MPAINT->Create(&transform); + BPAINT->Brush.Matrix(BTHIS->brush, FALSE, transform); + MPAINT->Translate(transform, (float)VARG(tx), (float)VARG(ty)); + BPAINT->Brush.Matrix(BTHIS->brush, TRUE, transform); + MPAINT->Delete(&transform); + +END_METHOD + +BEGIN_METHOD(PaintBrush_Scale, GB_FLOAT sx; GB_FLOAT sy) + + GB_TRANSFORM transform; + + MPAINT->Create(&transform); + BPAINT->Brush.Matrix(BTHIS->brush, FALSE, transform); + MPAINT->Scale(transform, (float)VARG(sx), (float)VARG(sy)); + BPAINT->Brush.Matrix(BTHIS->brush, TRUE, transform); + MPAINT->Delete(&transform); + +END_METHOD + +BEGIN_METHOD(PaintBrush_Rotate, GB_FLOAT angle) + + GB_TRANSFORM transform; + + MPAINT->Create(&transform); + BPAINT->Brush.Matrix(BTHIS->brush, FALSE, transform); + MPAINT->Rotate(transform, (float)VARG(angle)); + BPAINT->Brush.Matrix(BTHIS->brush, TRUE, transform); + MPAINT->Delete(&transform); + +END_METHOD + + +GB_DESC PaintBrushDesc[] = +{ + GB_DECLARE("PaintBrush", sizeof(PAINT_BRUSH)), GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, PaintBrush_free, NULL), + + GB_PROPERTY("Matrix", "PaintMatrix", PaintBrush_Matrix), + GB_METHOD("Reset", NULL, PaintBrush_Reset, NULL), + GB_METHOD("Translate", NULL, PaintBrush_Translate, "(TX)f(TY)f"), + GB_METHOD("Scale", NULL, PaintBrush_Scale, "(SX)f(SY)f"), + GB_METHOD("Rotate", NULL, PaintBrush_Rotate, "(Angle)f"), + + GB_END_DECLARE +}; + + +//---- Paint ---------------------------------------------------------------- + + +BEGIN_METHOD(Paint_Begin, GB_OBJECT device; GB_OBJECT area) + + void *device = VARG(device); + GEOM_RECTF *area = VARGOPT(area, NULL); + + if (GB.CheckObject(device)) + return; + + PAINT_begin(device); + if (area) + PAINT_set_area(area); + +END_METHOD + + +BEGIN_METHOD_VOID(Paint_End) + + PAINT_end(); + +END_METHOD + + +BEGIN_METHOD_VOID(Paint_exit) + + while (_current) + PAINT_end(); + +END_METHOD + + +BEGIN_PROPERTY(Paint_Device) + + if (THIS) + GB.ReturnObject(THIS->device); + else + GB.ReturnNull(); + +END_PROPERTY + + +BEGIN_PROPERTY(Paint_Width) + + CHECK_DEVICE(); + GB.ReturnFloat(THIS->area.width); + +END_PROPERTY + + +BEGIN_PROPERTY(Paint_Height) + + CHECK_DEVICE(); + GB.ReturnFloat(THIS->area.height); + +END_PROPERTY + + +BEGIN_PROPERTY(Paint_ResolutionX) + + CHECK_DEVICE(); + GB.ReturnInteger(THIS->resolutionX); + +END_PROPERTY + + +BEGIN_PROPERTY(Paint_ResolutionY) + + CHECK_DEVICE(); + GB.ReturnInteger(THIS->resolutionY); + +END_PROPERTY + +#define IMPLEMENT_METHOD(_method, _api) \ +BEGIN_METHOD_VOID(_method) \ + CHECK_DEVICE(); \ + PAINT->_api(THIS); \ +END_METHOD + + +#define IMPLEMENT_METHOD_PRESERVE(_method, _api) \ +BEGIN_METHOD(_method, GB_BOOLEAN preserve) \ + bool preserve = VARGOPT(preserve, FALSE); \ + CHECK_DEVICE(); \ + PAINT->_api(THIS, preserve); \ + if (!preserve) \ + THIS->has_path = FALSE; \ +END_METHOD + +#define IMPLEMENT_PROPERTY_EXTENTS(_property, _api) \ +BEGIN_PROPERTY(_property) \ + PAINT_EXTENTS *extents; \ + CHECK_DEVICE(); \ + extents = GB.New(GB.FindClass("PaintExtents"), NULL, NULL); \ + PAINT->_api(THIS, &extents->ext); \ + GB.ReturnObject(extents); \ +END_METHOD + +#define IMPLEMENT_PROPERTY(_property, _api, _type, _gtype, _return) \ +BEGIN_PROPERTY(_property) \ + _type value; \ + CHECK_DEVICE(); \ + if (READ_PROPERTY) \ + { \ + PAINT->_api(THIS, FALSE, &value); \ + _return(value); \ + } \ + else \ + { \ + value = (_type)VPROP(_gtype); \ + PAINT->_api(THIS, TRUE, &value); \ + } \ +END_METHOD + +#define IMPLEMENT_PROPERTY_BOOLEAN(_property, _api) \ + IMPLEMENT_PROPERTY(_property, _api, int, GB_BOOLEAN, GB.ReturnBoolean) + +#define IMPLEMENT_PROPERTY_INTEGER(_property, _api) \ + IMPLEMENT_PROPERTY(_property, _api, int, GB_INTEGER, GB.ReturnInteger) + +#define IMPLEMENT_PROPERTY_UNSIGNED_INTEGER(_property, _api) \ + IMPLEMENT_PROPERTY(_property, _api, uint, GB_INTEGER, GB.ReturnInteger) + +#define IMPLEMENT_PROPERTY_FLOAT(_property, _api) \ + IMPLEMENT_PROPERTY(_property, _api, float, GB_FLOAT, GB.ReturnFloat) + +IMPLEMENT_PROPERTY_BOOLEAN(Paint_Invert, Invert) +IMPLEMENT_PROPERTY_INTEGER(Paint_FillStyle, FillStyle) + +BEGIN_PROPERTY(Paint_Background) + + uint value; + + CHECK_DEVICE(); + + if (READ_PROPERTY) + { + PAINT->Background(THIS, FALSE, &value); + GB.ReturnInteger(value); + } + else + { + value = (uint)VPROP(GB_INTEGER); + PAINT->Background(THIS, TRUE, &value); + GB.StoreObject(NULL, POINTER(&THIS->brush)); + } + +END_METHOD + +IMPLEMENT_PROPERTY_BOOLEAN(Paint_Antialias, Antialias) +IMPLEMENT_METHOD(Paint_Save, Save) +IMPLEMENT_METHOD(Paint_Restore, Restore) +IMPLEMENT_METHOD_PRESERVE(Paint_Clip, Clip) +IMPLEMENT_METHOD(Paint_ResetClip, ResetClip) +IMPLEMENT_PROPERTY_EXTENTS(Paint_ClipExtents, ClipExtents) +IMPLEMENT_METHOD_PRESERVE(Paint_Fill, Fill) +IMPLEMENT_METHOD_PRESERVE(Paint_Stroke, Stroke) +IMPLEMENT_PROPERTY_EXTENTS(Paint_PathExtents, PathExtents) + + +static GB_ARRAY _outline; +static GB_ARRAY _outline_p; + +static void make_path_outline(int cmd, float x, float y) +{ + if (!_outline) + GB.Array.New(&_outline, GB.FindClass("PointF[]"), 0); + + if (cmd == GB_PAINT_PATH_MOVE) + { + GB.Array.New(&_outline_p, GB.FindClass("PointF"), 0); + GB.Ref(_outline_p); + *((void **)GB.Array.Add(_outline)) = _outline_p; + } + + GEOM_POINTF *p = GEOM.CreatePointF(x, y); + *((void **)GB.Array.Add(_outline_p)) = p; + GB.Ref(p); +} + +BEGIN_PROPERTY(Paint_PathOutline) + + CHECK_DEVICE(); + + _outline = NULL; + PAINT->PathOutline(THIS, make_path_outline); + GB.ReturnObject(_outline); + +END_PROPERTY + + +BEGIN_PROPERTY(Paint_ClipRect) + + GB_EXTENTS ext; + GEOM_RECT *rect; + int w, h; + + CHECK_DEVICE(); + + if (READ_PROPERTY) + { + (*PAINT->ClipExtents)(THIS, &ext); + + w = floorf(ext.x2) - ceilf(ext.x1); + h = floorf(ext.y2) - ceilf(ext.y1); + + if (w <= 0 || h <= 0) + { + GB.ReturnNull(); + return; + } + + rect = GEOM.CreateRect(); + rect->x = ceilf(ext.x1); + rect->y = ceilf(ext.y1); + rect->w = w; + rect->h = h; + + GB.ReturnObject(rect); + } + else + { + rect = (GEOM_RECT *)VPROP(GB_OBJECT); + if (rect) + PAINT->ClipRect(THIS, rect->x, rect->y, rect->w, rect->h); + else + PAINT->ResetClip(THIS); + } + +END_PROPERTY + + +BEGIN_METHOD(Paint_PathContains, GB_FLOAT x; GB_FLOAT y) + + CHECK_DEVICE(); + GB.ReturnBoolean(PAINT->PathContains(THIS, (float)VARG(x), (float)VARG(y))); + +END_METHOD + +IMPLEMENT_PROPERTY_INTEGER(Paint_FillRule, FillRule) +IMPLEMENT_PROPERTY_INTEGER(Paint_LineCap, LineCap) +IMPLEMENT_PROPERTY_INTEGER(Paint_LineJoin, LineJoin) +IMPLEMENT_PROPERTY_INTEGER(Paint_Operator, Operator) +IMPLEMENT_PROPERTY_FLOAT(Paint_LineWidth, LineWidth) +IMPLEMENT_PROPERTY_FLOAT(Paint_MiterLimit, MiterLimit) + +BEGIN_PROPERTY(Paint_Brush) + + CHECK_DEVICE(); + + if (READ_PROPERTY) + GB.ReturnObject(THIS->brush); + else + { + PAINT_BRUSH *old_brush = THIS->brush; + PAINT_BRUSH *new_brush = (PAINT_BRUSH *)VPROP(GB_OBJECT); + if (new_brush) + { + GB.Ref(new_brush); + PAINT->SetBrush(THIS, new_brush->brush); + } + GB.Unref(POINTER(&old_brush)); + THIS->brush = new_brush; + } + +END_PROPERTY + + +BEGIN_PROPERTY(Paint_BrushOrigin) + + float x, y; + + CHECK_DEVICE(); + + if (READ_PROPERTY) + { + PAINT->BrushOrigin(THIS, FALSE, &x, &y); + GB.ReturnObject(GEOM.CreatePointF(x, y)); + } + else + { + GEOM_POINT *p = VPROP(GB_OBJECT); + if (!p) + x = y = 0.0; + else + { + x = p->x; + y = p->y; + } + + PAINT->BrushOrigin(THIS, TRUE, &x, &y); + } + +END_PROPERTY + + +BEGIN_PROPERTY(Paint_Dash) + + GB_ARRAY array; + float *dashes; + int count, i; + + CHECK_DEVICE(); + + if (READ_PROPERTY) + { + PAINT->Dash(THIS, FALSE, &dashes, &count); + if (!count) + GB.ReturnNull(); + else + { + GB.Array.New(POINTER(&array), GB_T_FLOAT, count); + for (i = 0; i < count; i++) + *((double *)GB.Array.Get(array, i)) = (double)dashes[i]; + GB.ReturnObject(array); + } + GB.Free(POINTER(&dashes)); + } + else + { + array = (GB_ARRAY)VPROP(GB_OBJECT); + if (!array) + count = 0; + else + count = GB.Array.Count(array); + + if (!count) + { + PAINT->Dash(THIS, TRUE, NULL, &count); + //THIS->lineStyle = GB_PAINT_LINE_STYLE_SOLID; + } + else + { + GB.Alloc(POINTER(&dashes), sizeof(float) * count); + for (i = 0; i < count; i++) + dashes[i] = (float)*((double *)GB.Array.Get(array, i)); + PAINT->Dash(THIS, TRUE, &dashes, &count); + GB.Free(POINTER(&dashes)); + //THIS->lineStyle = GB_PAINT_LINE_STYLE_CUSTOM; + } + } + +END_PROPERTY + + +IMPLEMENT_PROPERTY_FLOAT(Paint_DashOffset, DashOffset) + +BEGIN_METHOD_VOID(Paint_NewPath) + + CHECK_DEVICE(); + + PAINT->NewPath(THIS); + THIS->has_path = FALSE; + +END_METHOD + +IMPLEMENT_METHOD(Paint_ClosePath, ClosePath) + + +BEGIN_PROPERTY(Paint_X) + + float x, y; + CHECK_DEVICE(); + PAINT->GetCurrentPoint(THIS, &x, &y); + GB.ReturnFloat((double)x); + +END_PROPERTY + + +BEGIN_PROPERTY(Paint_Y) + + float x, y; + CHECK_DEVICE(); + PAINT->GetCurrentPoint(THIS, &x, &y); + GB.ReturnFloat((double)y); + +END_PROPERTY + + +BEGIN_METHOD(Paint_Arc, GB_FLOAT xc; GB_FLOAT yc; GB_FLOAT radius; GB_FLOAT angle; GB_FLOAT length; GB_BOOLEAN pie) + + CHECK_DEVICE(); + + bool pie = VARGOPT(pie, FALSE); + float angle = VARGOPT(angle, 0.0); + float length = VARGOPT(length, MISSING(angle) ? M_PI * 2 : 0.0); + + if (MISSING(length) || length == 0.0) + pie = FALSE; + + PAINT->Arc(THIS, VARG(xc), VARG(yc), VARG(radius), angle, length, pie); + THIS->has_path = TRUE; + +END_METHOD + + +BEGIN_METHOD(Paint_Ellipse, GB_FLOAT x; GB_FLOAT y; GB_FLOAT width; GB_FLOAT height; GB_FLOAT angle; GB_FLOAT length; GB_BOOLEAN pie) + + CHECK_DEVICE(); + + bool pie = VARGOPT(pie, FALSE); + float angle = VARGOPT(angle, 0.0); + float length = VARGOPT(length, MISSING(angle) ? M_PI * 2 : 0.0); + + if (MISSING(length) || length == 0.0) + pie = FALSE; + + PAINT->Ellipse(THIS, VARG(x), VARG(y), VARG(width), VARG(height), angle, length, pie); + THIS->has_path = TRUE; + +END_METHOD + + +BEGIN_METHOD(Paint_Polygon, GB_OBJECT points) + + GB_ARRAY points = VARG(points); + int i, n; + double *p; + + CHECK_DEVICE(); + + if (!points) + return; + + n = GB.Array.Count(points); + if (n < 4) + return; + + CHECK_DEVICE(); + + p = (double *)GB.Array.Get(points, 0); + + PAINT->MoveTo(THIS, p[0], p[1]); + for (i = 2; i < n; i+= 2) + PAINT->LineTo(THIS, p[i], p[i + 1]); + PAINT->LineTo(THIS, p[0], p[1]); + THIS->has_path = TRUE; + +END_METHOD + + +BEGIN_METHOD(Paint_CurveTo, GB_FLOAT x1; GB_FLOAT y1; GB_FLOAT x2; GB_FLOAT y2; GB_FLOAT x3; GB_FLOAT y3) + + CHECK_DEVICE(); + PAINT->CurveTo(THIS, VARG(x1), VARG(y1), VARG(x2), VARG(y2), VARG(x3), VARG(y3)); + THIS->has_path = TRUE; + +END_METHOD + + +BEGIN_METHOD(Paint_RelCurveTo, GB_FLOAT x1; GB_FLOAT y1; GB_FLOAT x2; GB_FLOAT y2; GB_FLOAT x3; GB_FLOAT y3) + + float x, y; + CHECK_DEVICE(); + PAINT->GetCurrentPoint(THIS, &x, &y); + PAINT->CurveTo(THIS, x + VARG(x1), y + VARG(y1), x + VARG(x2), y + VARG(y2), x + VARG(x3), y + VARG(y3)); + THIS->has_path = TRUE; + +END_METHOD + + +BEGIN_METHOD(Paint_LineTo, GB_FLOAT x; GB_FLOAT y) + + CHECK_DEVICE(); + PAINT->LineTo(THIS, VARG(x), VARG(y)); + THIS->has_path = TRUE; + +END_METHOD + + +BEGIN_METHOD(Paint_RelLineTo, GB_FLOAT x; GB_FLOAT y) + + float fx, fy; + CHECK_DEVICE(); + PAINT->GetCurrentPoint(THIS, &fx, &fy); + PAINT->LineTo(THIS, fx + VARG(x), fy + VARG(y)); + THIS->has_path = TRUE; + +END_METHOD + + +BEGIN_METHOD(Paint_MoveTo, GB_FLOAT x; GB_FLOAT y) + + CHECK_DEVICE(); + PAINT->MoveTo(THIS, VARG(x), VARG(y)); + +END_METHOD + + +BEGIN_METHOD(Paint_RelMoveTo, GB_FLOAT x; GB_FLOAT y) + + float fx, fy; + CHECK_DEVICE(); + PAINT->GetCurrentPoint(THIS, &fx, &fy); + PAINT->MoveTo(THIS, fx + VARG(x), fy + VARG(y)); + +END_METHOD + + +BEGIN_METHOD(Paint_Rectangle, GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h; GB_FLOAT radius) + + CHECK_DEVICE(); + + float x = VARG(x); + float y = VARG(y); + float w = VARG(w); + float h = VARG(h); + float r = VARGOPT(radius, 0.0); + + if (r <= 0.0) + PAINT->Rectangle(THIS, x, y, w, h); + else + { + r = Min(r, Min(w, h) / 2); + float r2 = r * (1-0.55228475); + + //PAINT->NewPath(THIS); + + PAINT->MoveTo(THIS, x + r, y); + PAINT->LineTo(THIS, x + w - r, y); + PAINT->CurveTo(THIS, x + w - r2, y, x + w, y + r2, x + w, y + r); + PAINT->LineTo(THIS, x + w, y + h - r); + PAINT->CurveTo(THIS, x + w, y + h - r2, x + w - r2, y + h, x + w - r, y + h); + PAINT->LineTo(THIS, x + r, y + h); + PAINT->CurveTo(THIS, x + r2, y + h, x, y + h - r2, x, y + h - r); + PAINT->LineTo(THIS, x, y + r); + PAINT->CurveTo(THIS, x, y + r2, x + r2, y, x + r, y); + } + + THIS->has_path = TRUE; + +END_METHOD + + +IMPLEMENT_PROPERTY(Paint_Font, Font, GB_FONT, GB_OBJECT, GB.ReturnObject) + + +BEGIN_METHOD(Paint_Text, GB_STRING text; GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h; GB_INTEGER align) + + CHECK_DEVICE(); + + if (!MISSING(x) && !MISSING(y)) + PAINT->MoveTo(THIS, (float)VARG(x), (float)VARG(y)); + + PAINT->Text(THIS, STRING(text), LENGTH(text), VARGOPT(w, -1), VARGOPT(h, -1), VARGOPT(align, GB_DRAW_ALIGN_DEFAULT), FALSE); + THIS->has_path = TRUE; + +END_METHOD + + +BEGIN_METHOD(Paint_RichText, GB_STRING text; GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h; GB_INTEGER align) + + CHECK_DEVICE(); + + if (!MISSING(x) && !MISSING(y)) + PAINT->MoveTo(THIS, (float)VARG(x), (float)VARG(y)); + + PAINT->RichText(THIS, STRING(text), LENGTH(text), VARGOPT(w, -1), VARGOPT(h, -1), VARGOPT(align, GB_DRAW_ALIGN_DEFAULT), FALSE); + THIS->has_path = TRUE; + +END_METHOD + + +BEGIN_METHOD(Paint_DrawText, GB_STRING text; GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h; GB_INTEGER align) + + CHECK_DEVICE(); + CHECK_PATH(); + + if (!MISSING(x) && !MISSING(y)) + PAINT->MoveTo(THIS, (float)VARG(x), (float)VARG(y)); + + PAINT->Text(THIS, STRING(text), LENGTH(text), VARGOPT(w, -1), VARGOPT(h, -1), VARGOPT(align, GB_DRAW_ALIGN_DEFAULT), TRUE); + +END_METHOD + + +BEGIN_METHOD(Paint_DrawRichText, GB_STRING text; GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h; GB_INTEGER align) + + CHECK_DEVICE(); + CHECK_PATH(); + + if (!MISSING(x) && !MISSING(y)) + PAINT->MoveTo(THIS, (float)VARG(x), (float)VARG(y)); + + PAINT->RichText(THIS, STRING(text), LENGTH(text), VARGOPT(w, -1), VARGOPT(h, -1), VARGOPT(align, GB_DRAW_ALIGN_DEFAULT), TRUE); + +END_METHOD + + +BEGIN_METHOD(Paint_TextExtents, GB_STRING text) + + PAINT_EXTENTS *extents; + + CHECK_DEVICE(); + + extents = GB.New(GB.FindClass("PaintExtents"), NULL, NULL); + + if (!LENGTH(text)) + { + PAINT->GetCurrentPoint(THIS, &extents->ext.x1, &extents->ext.y1); + extents->ext.x2 = extents->ext.x1; + extents->ext.y2 = extents->ext.y1; + } + else + PAINT->TextExtents(THIS, STRING(text), LENGTH(text), &extents->ext); + + GB.ReturnObject(extents); + +END_METHOD + + +BEGIN_METHOD(Paint_RichTextExtents, GB_STRING text; GB_FLOAT width) + + PAINT_EXTENTS *extents; + + CHECK_DEVICE(); + + extents = GB.New(GB.FindClass("PaintExtents"), NULL, NULL); + PAINT->RichTextExtents(THIS, STRING(text), LENGTH(text), &extents->ext, VARGOPT(width, -1)); + + GB.ReturnObject(extents); + +END_METHOD + + +BEGIN_METHOD(Paint_TextSize, GB_STRING text) + + float w, h; + GEOM_RECTF *size; + + CHECK_DEVICE(); + PAINT->TextSize(THIS, STRING(text), LENGTH(text), &w, &h); + size = GEOM.CreateRectF(); + size->w = w; + size->h = h; + GB.ReturnObject(size); + +END_METHOD + + +BEGIN_METHOD(Paint_RichTextSize, GB_STRING text; GB_FLOAT width) + + float w, h; + GEOM_RECTF *size; + + w = VARGOPT(width, -1); + + CHECK_DEVICE(); + PAINT->RichTextSize(THIS, STRING(text), LENGTH(text), w, &w, &h); + size = GEOM.CreateRectF(); + size->w = w; + size->h = h; + GB.ReturnObject(size); + +END_METHOD + + +static PAINT_BRUSH *make_brush(GB_PAINT *d, GB_BRUSH brush) +{ + PAINT_BRUSH *that; + that = GB.New(GB.FindClass("PaintBrush"), NULL, NULL); + that->desc = d->desc; + that->brush = brush; + GB.ReturnObject(that); + return that; +} + + +BEGIN_METHOD(Paint_Color, GB_INTEGER color) + + GB_BRUSH brush; + + CHECK_DEVICE(); + + PAINT->Brush.Color(&brush, VARG(color)); + make_brush(THIS, brush); + +END_METHOD + + +BEGIN_METHOD(Paint_Image, GB_OBJECT image; GB_FLOAT x; GB_FLOAT y) + + GB_BRUSH brush; + + CHECK_DEVICE(); + + if (GB.CheckObject(VARG(image))) + return; + + PAINT->Brush.Image(&brush, (GB_IMAGE)VARG(image)); + make_brush(THIS, brush); + + if (!MISSING(x) || !MISSING(y)) + { + GB_TRANSFORM transform; + MPAINT->Create(&transform); + MPAINT->Translate(transform, VARGOPT(x, 0.0), VARGOPT(y, 0.0)); + PAINT->Brush.Matrix(brush, TRUE, transform); + MPAINT->Delete(&transform); + } + +END_METHOD + + +BEGIN_METHOD(Paint_LinearGradient, GB_FLOAT x0; GB_FLOAT y0; GB_FLOAT x1; GB_FLOAT y1; GB_OBJECT colors; GB_OBJECT positions; GB_INTEGER extend) + + GB_BRUSH brush; + GB_ARRAY positions, colors; + int nstop; + + CHECK_DEVICE(); + + positions = (GB_ARRAY)VARG(positions); + if (GB.CheckObject(positions)) + return; + colors = (GB_ARRAY)VARG(colors); + if (GB.CheckObject(colors)) + return; + + nstop = Min(GB.Array.Count(positions), GB.Array.Count(colors)); + + PAINT->Brush.LinearGradient(&brush, (float)VARG(x0), (float)VARG(y0), (float)VARG(x1), (float)VARG(y1), + nstop, (double *)GB.Array.Get(positions, 0), (GB_COLOR *)GB.Array.Get(colors, 0), VARGOPT(extend, GB_PAINT_EXTEND_PAD)); + + make_brush(THIS, brush); + +END_METHOD + + +BEGIN_METHOD(Paint_RadialGradient, GB_FLOAT cx; GB_FLOAT cy; GB_FLOAT radius; GB_FLOAT fx; GB_FLOAT fy; GB_OBJECT colors; GB_OBJECT positions; GB_INTEGER extend) + + GB_BRUSH brush; + GB_ARRAY positions, colors; + int nstop; + + CHECK_DEVICE(); + + positions = (GB_ARRAY)VARG(positions); + if (GB.CheckObject(positions)) + return; + colors = (GB_ARRAY)VARG(colors); + if (GB.CheckObject(colors)) + return; + + nstop = Min(GB.Array.Count(positions), GB.Array.Count(colors)); + + PAINT->Brush.RadialGradient(&brush, (float)VARG(cx), (float)VARG(cy), (float)VARG(radius), (float)VARG(fx), (float)VARG(fy), + nstop, (double *)GB.Array.Get(positions, 0), (GB_COLOR *)GB.Array.Get(colors, 0), VARGOPT(extend, GB_PAINT_EXTEND_PAD)); + + make_brush(THIS, brush); + +END_METHOD + + +BEGIN_PROPERTY(Paint_Matrix) + + GB_TRANSFORM transform; + PAINT_MATRIX *matrix; + + CHECK_DEVICE(); + + if (READ_PROPERTY) + { + MPAINT->Create(&transform); + PAINT->Matrix(THIS, FALSE, transform); + GB.ReturnObject(create_matrix(transform)); + } + else + { + matrix = (PAINT_MATRIX *)VPROP(GB_OBJECT); + if (!matrix) + PAINT->Matrix(THIS, TRUE, NULL); + else + PAINT->Matrix(THIS, TRUE, matrix->transform); + } + +END_PROPERTY + + +BEGIN_METHOD_VOID(Paint_Reset) + + CHECK_DEVICE(); + PAINT->Matrix(THIS, TRUE, NULL); + PAINT_translate(THIS->area.x, THIS->area.y); + +END_METHOD + + +BEGIN_METHOD(Paint_Translate, GB_FLOAT tx; GB_FLOAT ty) + + CHECK_DEVICE(); + PAINT_translate(VARG(tx), VARG(ty)); + +END_METHOD + + +BEGIN_METHOD(Paint_Scale, GB_FLOAT sx; GB_FLOAT sy) + + GB_TRANSFORM transform; + + CHECK_DEVICE(); + MPAINT->Create(&transform); + PAINT->Matrix(THIS, FALSE, transform); + MPAINT->Scale(transform, (float)VARG(sx), (float)VARG(sy)); + PAINT->Matrix(THIS, TRUE, transform); + MPAINT->Delete(&transform); + +END_METHOD + + +BEGIN_METHOD(Paint_Rotate, GB_FLOAT angle) + + GB_TRANSFORM transform; + + CHECK_DEVICE(); + MPAINT->Create(&transform); + PAINT->Matrix(THIS, FALSE, transform); + MPAINT->Rotate(transform, (float)VARG(angle)); + PAINT->Matrix(THIS, TRUE, transform); + MPAINT->Delete(&transform); + +END_METHOD + + +BEGIN_METHOD(Paint_DrawImage, GB_OBJECT image; GB_FLOAT x; GB_FLOAT y; GB_FLOAT width; GB_FLOAT height; GB_FLOAT opacity; GB_OBJECT source) + + GB_IMG *image; + float x, y, w, h; + float opacity = VARGOPT(opacity, 1.0); + GEOM_RECT *source = (GEOM_RECT *)VARGOPT(source, NULL); + + CHECK_DEVICE(); + CHECK_PATH(); + + image = (GB_IMG *)VARG(image); + + if (GB.CheckObject(image)) + return; + + x = VARG(x); + y = VARG(y); + w = VARGOPT(width, -1); + h = VARGOPT(height, -1); + + if (GB.CheckObject(VARG(image))) + return; + + if (w < 0) w = image->width; + if (h < 0) h = image->height; + + if (w <= 0.0 || h <= 0.0) + return; + + if (image->width <= 0 || image->height <= 0) + return; + + PAINT->DrawImage(THIS, VARG(image), x, y, w, h, opacity, source ? (GB_RECT *)&source->x : NULL); + +END_METHOD + + +BEGIN_METHOD(Paint_DrawPicture, GB_OBJECT picture; GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h; GB_OBJECT source) + + GB_PICTURE picture = VARG(picture); + GB_PICTURE_INFO info; + GEOM_RECT *source = (GEOM_RECT *)VARGOPT(source, NULL); + float x, y, w, h; + + CHECK_DEVICE(); + CHECK_PATH(); + + if (GB.CheckObject(picture)) + return; + + PAINT->GetPictureInfo(THIS, picture, &info); + + x = VARG(x); + y = VARG(y); + w = VARGOPT(w, -1); + h = VARGOPT(h, -1); + + if (w < 0) w = info.width; + if (h < 0) h = info.height; + + if (info.width <= 0 || info.height <= 0) + return; + + PAINT->DrawPicture(THIS, picture, x, y, w, h, source ? (GB_RECT *)&source->x : NULL); + +END_METHOD + + +BEGIN_METHOD(Paint_ZoomImage, GB_OBJECT image; GB_INTEGER zoom; GB_INTEGER x; GB_INTEGER y; GB_INTEGER grid; GB_OBJECT source) + + GB_IMAGE image = VARG(image); + GB_IMG *info = (GB_IMG *)image; + GEOM_RECT *source = (GEOM_RECT *)VARGOPT(source, NULL); + int zoom; + int x, y, sx, sy, sw, sh; + int i, j, xr, yr; + bool border; + GB_COLOR borderColor; + int antialias = FALSE; + GB_RECT rect; + float opacity = 1.0; //VARGOPT(opacity, 1.0); + + CHECK_DEVICE(); + CHECK_PATH(); + + if (GB.CheckObject(image)) + return; + + zoom = VARG(zoom); + if (zoom < 1) + { + GB.Error("Bad zoom factor"); + return; + } + + x = VARGOPT(x, 0); + y = VARGOPT(y, 0); + + if (source) + { + sx = source->x; + sy = source->y; + sw = source->w; + sh = source->h; + } + else + { + sx = sy = 0; + sw = info->width; + sh = info->height; + } + + DRAW_NORMALIZE(x, y, sw, sh, sx, sy, sw, sh, info->width, info->height); + + //DRAW->Fill.GetOrigin(THIS, &ox, &oy); + + PAINT->Save(THIS); + PAINT->Antialias(THIS, TRUE, &antialias); + + borderColor = VARGOPT(grid, GB_COLOR_DEFAULT); + border = borderColor != GB_COLOR_DEFAULT; + + rect.x = sx; + rect.y = sy; + rect.w = sw; + rect.h = sh; + + PAINT->DrawImage(THIS, image, x, y, sw * zoom, sh * zoom, opacity, &rect); + + if (border && zoom >= 3) + { + float dashes[2] = { 1.0, 1.0 }; + int count; + float *pdashes = dashes; + + for (i = sx + 1, xr = x + zoom; i < (sx + sw); i++, xr += zoom) + { + //PAINT->FillRect(THIS, xr, y, 1, sh * zoom, borderColor); + PAINT->MoveTo(THIS, xr, y); + PAINT->LineTo(THIS, xr, y + sh * zoom); + } + + for (j = sy + 1, yr = y + zoom; j < (sy + sh); j++, yr += zoom) + { + //PAINT->FillRect(THIS, x, yr, sw * zoom, 1, borderColor); + PAINT->MoveTo(THIS, x, yr); + PAINT->LineTo(THIS, x + sw * zoom, yr); + } + + count = 0; + PAINT->Dash(THIS, TRUE, NULL, &count); + PAINT->Background(THIS, TRUE, &borderColor); + PAINT->Stroke(THIS, TRUE); + + borderColor ^= 0xFFFFFF; + count = 2; + PAINT->Dash(THIS, TRUE, &pdashes, &count); + PAINT->Background(THIS, TRUE, &borderColor); + PAINT->Stroke(THIS, FALSE); + + THIS->has_path = FALSE; + } + + PAINT->Restore(THIS); + +END_METHOD + + +#if 0 +BEGIN_PROPERTY(Paint_LineStyle) + + int v; + int count; + float dashes[6]; + + CHECK_DEVICE(); + + if (READ_PROPERTY) + { + GB.ReturnInteger(THIS->lineStyle); + return; + } + + v = VPROP(GB_INTEGER); + + switch (v) + { + case GB_PAINT_LINE_STYLE_NONE: + break; + + case GB_PAINT_LINE_STYLE_SOLID: + PAINT->Dash(THIS, TRUE, NULL, &count); + break; + + case GB_PAINT_LINE_STYLE_DASH: + dashes[0] = 3; dashes[1] = 3; count = 2; + PAINT->Dash(THIS, TRUE, dashes, &count); + break; + + case GB_PAINT_LINE_STYLE_DOT: + dashes[0] = 1; dashes[1] = 3; count = 2; + PAINT->Dash(THIS, TRUE, dashes, &count); + break; + + case GB_PAINT_LINE_STYLE_DASH_DOT: + dashes[0] = 3; dashes[1] = 3; dashes[2] = 3; dashes[3] = 1; dacount = 4; + PAINT->Dash(THIS, TRUE, dashes, &count); + break; + + case GB_PAINT_LINE_STYLE_DASH_DOT_DOT: + dashes[0] = 3; dashes[1] = 3; dashes[2] = 3; dashes[3] = 1; dashes[4] = 3; dashes[5] = 1; dacount = 6; + PAINT->Dash(THIS, TRUE, dashes, &count); + break; + + default: + return; + } + + THIS->lineStyle = v; + +END_PROPERTY +#endif + +BEGIN_METHOD(Paint_FillRect, GB_FLOAT x; GB_FLOAT y; GB_FLOAT w; GB_FLOAT h; GB_INTEGER color) + + CHECK_DEVICE(); + CHECK_PATH(); + PAINT->FillRect(THIS, VARG(x), VARG(y), VARG(w), VARG(h), VARG(color)); + +END_METHOD + + +BEGIN_PROPERTY(Paint_Tag) + + CHECK_DEVICE(); + + if (READ_PROPERTY) + GB.ReturnObject(THIS->tag); + else + GB.StoreObject(PROP(GB_OBJECT), POINTER(&(THIS->tag))); + +END_PROPERTY + +#if 0 +Static Public Sub DrawEllipsizedText(Text As String, X As Integer, Y As Integer, W As Integer, H As Integer, Optional Alignment As Integer = Align.TopLeft) + + Dim aElt As String[] = Split(Text, " \n") + Dim sElt As String + Dim I As Integer + Dim HL As Integer = Paint.Font.Height + Dim YT, WT As Integer + Dim sText As String + + YT = 0 + + For I = 0 To aElt.Max + sElt = aElt[I] + + If (YT + HL * 2) > H Then + WT = Paint.Font.TextWidth(LTrim(sText & " " & sElt & "…")) + If WT > W Then + Paint.DrawText(sText & "…", X, Y + YT, W, H, Align.TopLeft) + Return + Endif + Else + WT = Paint.Font.TextWidth(LTrim(sText & " " & sElt)) + If WT > W Then + Paint.DrawText(sText, X, Y + YT, W, H, Align.TopLeft) + sText = "" + YT += HL + Endif + Endif + + sText = RTrim(sText & " " & sElt) + + Next + + Paint.DrawText(sText, X, Y + YT, W, H, Align.TopLeft) + +End +#endif + +//-- Style API -------------------------------------------------------------- + +#define GET_COORD() \ + int x, y, w, h; \ +\ + CHECK_DEVICE(); \ +\ + x = VARG(x); \ + y = VARG(y); \ + w = VARG(w); \ + h = VARG(h); \ +\ + if (w < 1 || h < 1) \ + return; + + +GB_DESC PaintDesc[] = +{ + GB_DECLARE("Paint", 0), GB_VIRTUAL_CLASS(), + + GB_STATIC_METHOD("_exit", NULL, Paint_exit, NULL), + + GB_CONSTANT("ExtendPad", "i", GB_PAINT_EXTEND_PAD), + GB_CONSTANT("ExtendRepeat", "i", GB_PAINT_EXTEND_REPEAT), + GB_CONSTANT("ExtendReflect", "i", GB_PAINT_EXTEND_REFLECT), + + GB_CONSTANT("FillRuleWinding", "i", GB_PAINT_FILL_RULE_WINDING), + GB_CONSTANT("FillRuleEvenOdd", "i", GB_PAINT_FILL_RULE_EVEN_ODD), + + GB_CONSTANT("LineCapButt", "i", GB_PAINT_LINE_CAP_BUTT), + GB_CONSTANT("LineCapRound", "i", GB_PAINT_LINE_CAP_ROUND), + GB_CONSTANT("LineCapSquare", "i", GB_PAINT_LINE_CAP_SQUARE), + + GB_CONSTANT("LineJoinMiter", "i", GB_PAINT_LINE_JOIN_MITER), + GB_CONSTANT("LineJoinRound", "i", GB_PAINT_LINE_JOIN_ROUND), + GB_CONSTANT("LineJoinBevel", "i", GB_PAINT_LINE_JOIN_BEVEL), + + GB_CONSTANT("OperatorClear", "i", GB_PAINT_OPERATOR_CLEAR), + GB_CONSTANT("OperatorSource", "i", GB_PAINT_OPERATOR_SOURCE), + GB_CONSTANT("OperatorOver", "i", GB_PAINT_OPERATOR_OVER), + GB_CONSTANT("OperatorIn", "i", GB_PAINT_OPERATOR_IN), + GB_CONSTANT("OperatorOut", "i", GB_PAINT_OPERATOR_OUT), + GB_CONSTANT("OperatorATop", "i", GB_PAINT_OPERATOR_ATOP), + GB_CONSTANT("OperatorDest", "i", GB_PAINT_OPERATOR_DEST), + GB_CONSTANT("OperatorDestOver", "i", GB_PAINT_OPERATOR_DEST_OVER), + GB_CONSTANT("OperatorDestIn", "i", GB_PAINT_OPERATOR_DEST_IN), + GB_CONSTANT("OperatorDestOut", "i", GB_PAINT_OPERATOR_DEST_OUT), + GB_CONSTANT("OperatorDestATop", "i", GB_PAINT_OPERATOR_DEST_ATOP), + GB_CONSTANT("OperatorXor", "i", GB_PAINT_OPERATOR_XOR), + GB_CONSTANT("OperatorAdd", "i", GB_PAINT_OPERATOR_ADD), + GB_CONSTANT("OperatorSaturate", "i", GB_PAINT_OPERATOR_SATURATE), + + GB_STATIC_METHOD("Begin", NULL, Paint_Begin, "(Device)o[(Area)RectF;]"), + GB_STATIC_METHOD("End", NULL, Paint_End, NULL), + + GB_STATIC_PROPERTY_READ("Device", "o", Paint_Device), + GB_STATIC_PROPERTY_READ("W", "f", Paint_Width), + GB_STATIC_PROPERTY_READ("H", "f", Paint_Height), + GB_STATIC_PROPERTY_READ("Width", "f", Paint_Width), + GB_STATIC_PROPERTY_READ("Height", "f", Paint_Height), + GB_STATIC_PROPERTY_READ("ResolutionX", "i", Paint_ResolutionX), + GB_STATIC_PROPERTY_READ("ResolutionY", "i", Paint_ResolutionY), + GB_STATIC_PROPERTY("AntiAlias", "b", Paint_Antialias), + + GB_STATIC_PROPERTY("_Invert", "b", Paint_Invert), + GB_STATIC_PROPERTY("_FillStyle", "i", Paint_FillStyle), + GB_STATIC_PROPERTY("Background", "i", Paint_Background), + GB_STATIC_PROPERTY("_Tag", "o", Paint_Tag), + + GB_STATIC_METHOD("Save", NULL, Paint_Save, NULL), + GB_STATIC_METHOD("Restore", NULL, Paint_Restore, NULL), + + GB_STATIC_METHOD("Clip", NULL, Paint_Clip, "[(Preserve)b]"), + GB_STATIC_METHOD("ResetClip", NULL, Paint_ResetClip, NULL), + GB_STATIC_PROPERTY_READ("ClipExtents", "PaintExtents", Paint_ClipExtents), + GB_STATIC_PROPERTY("ClipRect", "Rect", Paint_ClipRect), + + GB_STATIC_METHOD("Fill", NULL, Paint_Fill, "[(Preserve)b]"), + //GB_STATIC_PROPERTY_READ("FillExtents", "PaintExtents", Paint_FillExtents), + //GB_STATIC_METHOD("InFill", "b", Paint_InFill, "(X)f(Y)f"), + + //GB_STATIC_METHOD("Mask", NULL, CAIRO_mask, "(Pattern)CairoPattern;"), + + //GB_STATIC_METHOD("Paint", NULL, CAIRO_paint, "[(Alpha)f]"), + + GB_STATIC_METHOD("Stroke", NULL, Paint_Stroke, "[(Preserve)b]"), + //GB_STATIC_PROPERTY_READ("StrokeExtents", "PaintExtents", Paint_StrokeExtents), + //GB_STATIC_METHOD("InStroke", "b", Paint_InStroke, "(X)f(Y)f"), + + GB_STATIC_PROPERTY_READ("PathExtents", "PaintExtents", Paint_PathExtents), + GB_STATIC_METHOD("PathContains", "b", Paint_PathContains, "(X)f(Y)f"), + GB_STATIC_PROPERTY_READ("PathOutline", "PointF[][]", Paint_PathOutline), + + GB_STATIC_PROPERTY("Brush", "PaintBrush", Paint_Brush), + GB_STATIC_PROPERTY("BrushOrigin", "PointF", Paint_BrushOrigin), + GB_STATIC_PROPERTY("Dash", "Float[]", Paint_Dash), + GB_STATIC_PROPERTY("DashOffset", "f", Paint_DashOffset), + GB_STATIC_PROPERTY("FillRule", "i", Paint_FillRule), + GB_STATIC_PROPERTY("LineCap", "i", Paint_LineCap), + GB_STATIC_PROPERTY("LineJoin", "i", Paint_LineJoin), + GB_STATIC_PROPERTY("LineWidth", "f", Paint_LineWidth), + //GB_STATIC_PROPERTY("LineStyle", "i", Paint_LineStyle), + GB_STATIC_PROPERTY("MiterLimit", "f", Paint_MiterLimit), + GB_STATIC_PROPERTY("Operator", "i", Paint_Operator), + //GB_STATIC_PROPERTY("Tolerance", "f", CAIRO_tolerance), + + GB_STATIC_METHOD("NewPath", NULL, Paint_NewPath, NULL), + GB_STATIC_METHOD("ClosePath", NULL, Paint_ClosePath, NULL), + + GB_STATIC_PROPERTY_READ("X", "f", Paint_X), + GB_STATIC_PROPERTY_READ("Y", "f", Paint_Y), + + GB_STATIC_METHOD("Rectangle", NULL, Paint_Rectangle, "(X)f(Y)f(Width)f(Height)f[(Radius)f]"), + GB_STATIC_METHOD("FillRect", NULL, Paint_FillRect, "(X)f(Y)f(Width)f(Height)f(Color)i"), + GB_STATIC_METHOD("Arc", NULL, Paint_Arc, "(XC)f(YC)f(Radius)f[(Angle)f(Length)f(Pie)b]"), + GB_STATIC_METHOD("Ellipse", NULL, Paint_Ellipse, "(X)f(Y)f(Width)f(Height)f[(Angle)f(Length)f(Pie)b]"), + GB_STATIC_METHOD("Polygon", NULL, Paint_Polygon, "(Points)Float[];"), + + GB_STATIC_METHOD("CurveTo", NULL, Paint_CurveTo, "(X1)f(Y1)f(X2)f(Y2)f(X3)f(Y3)f"), + GB_STATIC_METHOD("RelCurveTo", NULL, Paint_RelCurveTo, "(X1)f(Y1)f(X2)f(Y2)f(X3)f(Y3)f"), + GB_STATIC_METHOD("LineTo", NULL, Paint_LineTo, "(X)f(Y)f"), + GB_STATIC_METHOD("RelLineTo", NULL, Paint_RelLineTo, "(X)f(Y)f"), + GB_STATIC_METHOD("MoveTo", NULL, Paint_MoveTo, "(X)f(Y)f"), + GB_STATIC_METHOD("RelMoveTo", NULL, Paint_RelMoveTo, "(X)f(Y)f"), + GB_STATIC_PROPERTY("Font", "Font", Paint_Font), + GB_STATIC_METHOD("Text", NULL, Paint_Text, "(Text)s[(X)f(Y)f(Width)f(Height)f(Alignment)i]"), + GB_STATIC_METHOD("TextSize", "RectF", Paint_TextSize, "(Text)s"), + GB_STATIC_METHOD("RichText", NULL, Paint_RichText, "(Text)s[(X)f(Y)f(Width)f(Height)f(Alignment)i]"), + GB_STATIC_METHOD("RichTextSize", "RectF", Paint_RichTextSize, "(Text)s[(Width)f]"), + GB_STATIC_METHOD("DrawText", NULL, Paint_DrawText, "(Text)s[(X)f(Y)f(Width)f(Height)f(Alignment)i]"), + GB_STATIC_METHOD("DrawRichText", NULL, Paint_DrawRichText, "(Text)s[(X)f(Y)f(Width)f(Height)f(Alignment)i]"), + GB_STATIC_METHOD("TextExtents", "PaintExtents", Paint_TextExtents, "(Text)s"), + GB_STATIC_METHOD("RichTextExtents", "PaintExtents", Paint_RichTextExtents, "(Text)s[(Width)f]"), + + GB_STATIC_METHOD("Color", "PaintBrush", Paint_Color, "(Color)i"), + GB_STATIC_METHOD("Image", "PaintBrush", Paint_Image, "(Image)Image;[(X)f(Y)f]"), + GB_STATIC_METHOD("LinearGradient", "PaintBrush", Paint_LinearGradient, "(X0)f(Y0)f(X1)f(Y1)f(Colors)Integer[];(Positions)Float[];[(Extend)i]"), + GB_STATIC_METHOD("RadialGradient", "PaintBrush", Paint_RadialGradient, "(CX)f(CY)f(Radius)f(FX)f(FY)f(Colors)Integer[];(Positions)Float[];[(Extend)i]"), + + GB_STATIC_PROPERTY("Matrix", "PaintMatrix", Paint_Matrix), + + GB_STATIC_METHOD("Reset", NULL, Paint_Reset, NULL), + GB_STATIC_METHOD("Translate", NULL, Paint_Translate, "(TX)f(TY)f"), + GB_STATIC_METHOD("Scale", NULL, Paint_Scale, "(SX)f(SY)f"), + GB_STATIC_METHOD("Rotate", NULL, Paint_Rotate, "(Angle)f"), + + //GB_STATIC_METHOD("Clear", NULL, Paint_Clear, NULL), + + GB_STATIC_METHOD("DrawImage", NULL, Paint_DrawImage, "(Image)Image;(X)f(Y)f[(Width)f(Height)f(Opacity)f(Source)Rect;]"), + GB_STATIC_METHOD("DrawPicture", NULL, Paint_DrawPicture, "(Picture)Picture;(X)f(Y)f[(Width)f(Height)f(Source)Rect;]"), + GB_STATIC_METHOD("ZoomImage", NULL, Paint_ZoomImage, "(Image)Image;(Zoom)i(X)i(Y)i[(Grid)i(Source)Rect;]"), + + + GB_END_DECLARE +}; + + diff --git a/main/lib/draw/cpaint.h b/main/lib/draw/cpaint.h new file mode 100644 index 00000000..6334ef7f --- /dev/null +++ b/main/lib/draw/cpaint.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + cpaint.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPAINT_H +#define __CPAINT_H + +#include "gambas.h" +#include "gb.paint.h" + +#ifndef __CPAINT_C + +extern GB_DESC PaintExtentsDesc[]; +extern GB_DESC PaintMatrixDesc[]; +extern GB_DESC PaintBrushDesc[]; +extern GB_DESC PaintDesc[]; + +#endif + +GB_PAINT *PAINT_get_current(); +void *PAINT_get_current_device(); +GB_PAINT *PAINT_from_device(void *device); +bool PAINT_is_painted(void *device); + +bool PAINT_begin(void *device); +void PAINT_end(); +bool PAINT_open(GB_PAINT *paint); +void PAINT_close(GB_PAINT *paint); + +#endif diff --git a/main/lib/draw/gb.draw.h b/main/lib/draw/gb.draw.h new file mode 100644 index 00000000..efa47069 --- /dev/null +++ b/main/lib/draw/gb.draw.h @@ -0,0 +1,98 @@ +/*************************************************************************** + + gb.draw.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_DRAW_H +#define __GB_DRAW_H + +#include "gb_common.h" +#include "gambas.h" + +#define GB_DRAW_ALIGN_DEFAULT (-1) +#define GB_DRAW_COLOR_DEFAULT (-1) + +enum { + GB_DRAW_STATE_NORMAL = 0, + GB_DRAW_STATE_DISABLED = 1, + GB_DRAW_STATE_FOCUS = 2, + GB_DRAW_STATE_HOVER = 4, + GB_DRAW_STATE_ACTIVE = 8 + }; + +typedef + void *GB_PICTURE; + +typedef + struct { + int width; + int height; + } + GB_PICTURE_INFO; + +#ifndef __GB_IMAGE_DEFINED +#define __GB_IMAGE_DEFINED +typedef + void *GB_IMAGE; +#endif + +#ifndef __GB_COLOR_DEFINED +#define __GB_COLOR_DEFINED +typedef + unsigned int GB_COLOR; +#endif + +typedef + void *GB_FONT; + +#define DRAW_INTERFACE_VERSION 1 + +typedef + struct { + int version; + struct { + void *(*GetCurrent)(); + void (*Begin)(void *); + void (*End)(); + bool (*IsPainted)(void *); + } + Paint; + } + DRAW_INTERFACE; + +#define DRAW_NORMALIZE(x, y, w, h, sx, sy, sw, sh, width, height) \ + if (sw < 0) sw = width; \ + if (sh < 0) sh = height; \ + if (w < 0) w = sw; \ + if (h < 0) h = sh; \ + if (sx < 0) sw += sx, sx = 0; \ + if (sy < 0) sh += sy, sy = 0; \ + if (sw > ((width) - sx)) \ + sw = ((width) - sx); \ + if (sh > ((height) - sy)) \ + sh = ((height) - sy); \ + if (sx >= (width) || sy >= (height) || sw <= 0 || sh <= 0) \ + return; + +#endif + + + diff --git a/main/lib/draw/gb.geom.h b/main/lib/draw/gb.geom.h new file mode 120000 index 00000000..2ae286cf --- /dev/null +++ b/main/lib/draw/gb.geom.h @@ -0,0 +1 @@ +../geom/gb.geom.h \ No newline at end of file diff --git a/main/lib/draw/gb.image.h b/main/lib/draw/gb.image.h new file mode 120000 index 00000000..d53c98b6 --- /dev/null +++ b/main/lib/draw/gb.image.h @@ -0,0 +1 @@ +../image/gb.image.h \ No newline at end of file diff --git a/main/lib/draw/gb.paint.h b/main/lib/draw/gb.paint.h new file mode 100644 index 00000000..aa5f89aa --- /dev/null +++ b/main/lib/draw/gb.paint.h @@ -0,0 +1,243 @@ +/*************************************************************************** + + gb.paint.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_PAINT_H +#define __GB_PAINT_H + +#include "gb_common.h" +#include "gambas.h" +#include "gb.draw.h" + +enum { + GB_PAINT_EXTEND_PAD, + GB_PAINT_EXTEND_REPEAT, + GB_PAINT_EXTEND_REFLECT +}; + +enum { + GB_PAINT_FILL_RULE_WINDING, + GB_PAINT_FILL_RULE_EVEN_ODD +}; + +enum { + GB_PAINT_LINE_CAP_BUTT, + GB_PAINT_LINE_CAP_ROUND, + GB_PAINT_LINE_CAP_SQUARE +}; + +enum { + GB_PAINT_LINE_JOIN_MITER, + GB_PAINT_LINE_JOIN_ROUND, + GB_PAINT_LINE_JOIN_BEVEL +}; + +enum { + GB_PAINT_OPERATOR_CLEAR, + GB_PAINT_OPERATOR_SOURCE, + GB_PAINT_OPERATOR_OVER, + GB_PAINT_OPERATOR_IN, + GB_PAINT_OPERATOR_OUT, + GB_PAINT_OPERATOR_ATOP, + GB_PAINT_OPERATOR_DEST, + GB_PAINT_OPERATOR_DEST_OVER, + GB_PAINT_OPERATOR_DEST_IN, + GB_PAINT_OPERATOR_DEST_OUT, + GB_PAINT_OPERATOR_DEST_ATOP, + GB_PAINT_OPERATOR_XOR, + GB_PAINT_OPERATOR_ADD, + GB_PAINT_OPERATOR_SATURATE +}; + +enum { + GB_PAINT_PATH_MOVE, + GB_PAINT_PATH_LINE +}; + +struct GB_PAINT_DESC; + +typedef + struct { + int x, y, w, h; + } + GB_RECT; + +typedef + struct { + float x1, y1, x2, y2; + } + GB_EXTENTS; + +typedef + void *GB_BRUSH; + +typedef + void *GB_TRANSFORM; + +typedef + struct { + GB_BASE ob; + GB_EXTENTS ext; + } + PAINT_EXTENTS; + +typedef + struct { + GB_BASE ob; + GB_TRANSFORM transform; + } + PAINT_MATRIX; + +typedef + struct { + GB_BASE ob; + struct GB_PAINT_DESC *desc; // drawing driver + GB_BRUSH brush; // brush + } + PAINT_BRUSH; + +typedef + struct GB_PAINT { + struct GB_PAINT_DESC *desc; // drawing driver + struct GB_PAINT *previous; // previous drawing context + void *device; // drawing object + struct { + double x; + double y; + double width; + double height; + } area; // drawing area + int resolutionX; // device horizontal resolution in DPI + int resolutionY; // device vertical resolution in DPI + PAINT_BRUSH *brush; // current brush + void *extra; // driver-specific state + unsigned opened : 1; // if the painting has been opened + unsigned other : 1; // if painting are imbricated on that device + unsigned has_path : 1; // if there is a current path + void *tag; // needed to support the old Draw class + } + GB_PAINT; + +typedef + void (*GB_PAINT_OUTLINE_CB)(int, float, float); + +typedef + struct GB_PAINT_DESC { + // Size of the GB_PAINT structure extra data + int size; + // Begins and terminates the drawing + int (*Begin)(GB_PAINT *d); + void (*End)(GB_PAINT *d); + + void (*Save)(GB_PAINT *d); + void (*Restore)(GB_PAINT *d); + + void (*Antialias)(GB_PAINT *d, int set, int *antialias); + + void (*Font)(GB_PAINT *d, int set, GB_FONT *font); + + void (*Background)(GB_PAINT *d, int set, GB_COLOR *color); + void (*Invert)(GB_PAINT *d, int set, int *invert); + + void (*Clip)(GB_PAINT *d, int preserve); + void (*ResetClip)(GB_PAINT *d); + void (*ClipExtents)(GB_PAINT *d, GB_EXTENTS *ext); + void (*ClipRect)(GB_PAINT *d, int x, int y, int w, int h); + + void (*Fill)(GB_PAINT *d, int preserve); + void (*Stroke)(GB_PAINT *d, int preserve); + + void (*PathExtents)(GB_PAINT *d, GB_EXTENTS *ext); + int (*PathContains)(GB_PAINT *d, float x, float y); + void (*PathOutline)(GB_PAINT *d, GB_PAINT_OUTLINE_CB cb); + + void (*Dash)(GB_PAINT *d, int set, float **dash, int *count); + void (*DashOffset)(GB_PAINT *d, int set, float *offset); + + void (*FillRule)(GB_PAINT *d, int set, int *value); + void (*FillStyle)(GB_PAINT *d, int set, int *value); + void (*LineCap)(GB_PAINT *d, int set, int *value); + void (*LineJoin)(GB_PAINT *d, int set, int *value); + void (*LineWidth)(GB_PAINT *d, int set, float *value); + void (*MiterLimit)(GB_PAINT *d, int set, float *value); + + void (*Operator)(GB_PAINT *d, int set, int *value); + + void (*NewPath)(GB_PAINT *d); + void (*ClosePath)(GB_PAINT *d); + + void (*Arc)(GB_PAINT *d, float xc, float yc, float radius, float angle, float length, bool pie); + void (*Ellipse)(GB_PAINT *d, float x, float y, float width, float height, float angle, float length, bool pie); + void (*Rectangle)(GB_PAINT *d, float x, float y, float width, float height); + void (*GetCurrentPoint)(GB_PAINT *d, float *x, float *y); + void (*MoveTo)(GB_PAINT *d, float x, float y); + void (*LineTo)(GB_PAINT *d, float x, float y); + void (*CurveTo)(GB_PAINT *d, float x1, float y1, float x2, float y2, float x3, float y3); + + void (*Text)(GB_PAINT *d, const char *text, int len, float w, float h, int align, bool draw); + void (*TextExtents)(GB_PAINT *d, const char *text, int len, GB_EXTENTS *ext); + void (*TextSize)(GB_PAINT *d, const char *text, int len, float *w, float *h); + void (*RichText)(GB_PAINT *d, const char *text, int len, float w, float h, int align, bool draw); + void (*RichTextExtents)(GB_PAINT *d, const char *text, int len, GB_EXTENTS *ext, float width); + void (*RichTextSize)(GB_PAINT *d, const char *text, int len, float width, float *w, float *h); + + void (*Matrix)(GB_PAINT *d, int set, GB_TRANSFORM matrix); + + void (*SetBrush)(GB_PAINT *d, GB_BRUSH brush); + void (*BrushOrigin)(GB_PAINT *d, int set, float *x, float *y); + + void (*DrawImage)(GB_PAINT *d, GB_IMAGE image, float x, float y, float w, float h, float opacity, GB_RECT *source); + void (*DrawPicture)(GB_PAINT *d, GB_PICTURE picture, float x, float y, float w, float h, GB_RECT *source); + void (*GetPictureInfo)(GB_PAINT *d, GB_PICTURE picture, GB_PICTURE_INFO *info); + void (*FillRect)(GB_PAINT *d, float x, float y, float width, float height, GB_COLOR color); + + struct { + void (*Free)(GB_BRUSH brush); + void (*Color)(GB_BRUSH *brush, GB_COLOR color); + void (*Image)(GB_BRUSH *brush, GB_IMAGE image); + void (*LinearGradient)(GB_BRUSH *brush, float x0, float y0, float x1, float y1, int nstop, double *positions, GB_COLOR *colors, int extend); + void (*RadialGradient)(GB_BRUSH *brush, float cx, float cy, float r, float fx, float fy, int nstop, double *positions, GB_COLOR *colors, int extend); + void (*Matrix)(GB_BRUSH brush, int set, GB_TRANSFORM matrix); + } + Brush; + } + GB_PAINT_DESC; + +typedef + struct GB_PAINT_MATRIX_DESC { + void (*Create)(GB_TRANSFORM *matrix); + void (*Copy)(GB_TRANSFORM *matrix, GB_TRANSFORM copy); + void (*Delete)(GB_TRANSFORM *matrix); + void (*Init)(GB_TRANSFORM matrix, float xx, float yx, float xy, float yy, float x0, float y0); + void (*Translate)(GB_TRANSFORM matrix, float tx, float ty); + void (*Scale)(GB_TRANSFORM matrix, float sx, float sy); + void (*Rotate)(GB_TRANSFORM matrix, float angle); + int (*Invert)(GB_TRANSFORM matrix); + void (*Multiply)(GB_TRANSFORM matrix, GB_TRANSFORM matrix2); + void (*Map)(GB_TRANSFORM matrix, double *x, double *y); + } + GB_PAINT_MATRIX_DESC; + +#endif + + + diff --git a/main/lib/draw/gb_list.c b/main/lib/draw/gb_list.c new file mode 100644 index 00000000..34557b46 --- /dev/null +++ b/main/lib/draw/gb_list.c @@ -0,0 +1,24 @@ +/*************************************************************************** + + gb_list.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_list_temp.h" diff --git a/main/lib/draw/main.c b/main/lib/draw/main.c new file mode 100644 index 00000000..7713f631 --- /dev/null +++ b/main/lib/draw/main.c @@ -0,0 +1,75 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "cpaint.h" +#include "main.h" + + +GB_INTERFACE GB EXPORT; +GEOM_INTERFACE GEOM EXPORT; +IMAGE_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES [] EXPORT = +{ + PaintExtentsDesc, + PaintMatrixDesc, + PaintBrushDesc, + PaintDesc, + NULL +}; + +void *GB_DRAW_1[] EXPORT = +{ + (void *)1, + (void *)PAINT_get_current, + (void *)PAINT_begin, + (void *)PAINT_end, + (void *)PAINT_is_painted, + NULL +}; + +const char *GB_INCLUDE EXPORT = "gb.geom"; + +int EXPORT GB_INIT(void) +{ + GB.Component.Load("gb.geom"); + GB.GetInterface("gb.geom", GEOM_INTERFACE_VERSION, &GEOM); + GB.Component.Load("gb.image"); + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + return 0; +} + + +void EXPORT GB_EXIT() +{ +} + diff --git a/main/lib/draw/main.h b/main/lib/draw/main.h new file mode 100644 index 00000000..7b00469a --- /dev/null +++ b/main/lib/draw/main.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb.image.h" +#include "gb.geom.h" +#include "gb.draw.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern IMAGE_INTERFACE IMAGE; +extern GEOM_INTERFACE GEOM; +#endif + +#endif /* __MAIN_H */ diff --git a/main/lib/draw/matrix.c b/main/lib/draw/matrix.c new file mode 100644 index 00000000..7a5f01b9 --- /dev/null +++ b/main/lib/draw/matrix.c @@ -0,0 +1,221 @@ +/*************************************************************************** + + matrix.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MATRIX_C + +#include "gb_common.h" +#include "matrix.h" +#include "main.h" + +static INLINE int DROUND(double d) +{ + return d >= 0.0 ? (int)(d + 0.5) : (int)( d - ((int)d-1) + 0.5 ) + ((int)d-1); +} + +static INLINE double DMIN(double a, double b) +{ + return (a > b ? b : a); +} + +static INLINE double DMAX(double a, double b) +{ + return (a > b ? a : b); +} + +void MATRIX_reset(MATRIX *matrix) +{ + matrix->m11 = matrix->m22 = 1; + matrix->m12 = matrix->m21 = 0; + matrix->dx = matrix->dy = 0; + matrix->identity = TRUE; + matrix->rotation = FALSE; +} + +void MATRIX_init(MATRIX *matrix) +{ + matrix->next = NULL; + MATRIX_reset(matrix); +} + +#define MAPDOUBLE(mat, x, y, nx, ny ) \ +{ \ + double fx = x; \ + double fy = y; \ + nx = mat->m11*fx + mat->m21*fy + mat->dx; \ + ny = mat->m12*fx + mat->m22*fy + mat->dy; \ +} + +void MATRIX_map_point(MATRIX *matrix, int *x, int *y) +{ + double nx; + double ny; + + MAPDOUBLE(matrix, *x, *y, nx, ny); + + *x = DROUND(nx); + *y = DROUND(ny); +} + +void MATRIX_map_rect(MATRIX *matrix, int *x, int *y, int *w, int *h) +{ + int rx, ry, rw, rh; + + if (matrix->m12 == 0.0F && matrix->m21 == 0.0F) + { + rx = DROUND(matrix->m11 * *x + matrix->dx); + ry = DROUND(matrix->m22 * *y + matrix->dy); + rw = DROUND(matrix->m11 * *w); + rh = DROUND(matrix->m22 * *h); + + if (rw < 0) + { + rw = -rw; + rx -= rw - 1; + } + + if (rh < 0) + { + rh = -rh; + ry -= rh-1; + } + } + else + { + int left = *x; + int top = *y; + int right = *x + *w; + int bottom = *y + *h; + + double x0, y0; + double x, y; + MAPDOUBLE(matrix, left, top, x0, y0 ); + double xmin = x0; + double ymin = y0; + double xmax = x0; + double ymax = y0; + MAPDOUBLE(matrix, right, top, x, y ); + xmin = DMIN( xmin, x ); + ymin = DMIN( ymin, y ); + xmax = DMAX( xmax, x ); + ymax = DMAX( ymax, y ); + MAPDOUBLE(matrix, right, bottom, x, y ); + xmin = DMIN( xmin, x ); + ymin = DMIN( ymin, y ); + xmax = DMAX( xmax, x ); + ymax = DMAX( ymax, y ); + MAPDOUBLE(matrix, left, bottom, x, y ); + xmin = DMIN( xmin, x ); + ymin = DMIN( ymin, y ); + xmax = DMAX( xmax, x ); + ymax = DMAX( ymax, y ); + double ww = xmax - xmin; + double hh = ymax - ymin; + xmin -= ( xmin - x0 ) / ww; + ymin -= ( ymin - y0 ) / hh; + xmax -= ( xmax - x0 ) / ww; + ymax -= ( ymax - y0 ) / hh; + + rx = DROUND(xmin); + ry = DROUND(ymin); + rw = DROUND(xmax) - DROUND(xmin) + 1; + rh = DROUND(ymax) - DROUND(ymin) + 1; + } + + *x = rx; + *y = ry; + *w = rw; + *h = rh; +} + +int *MATRIX_map_array(MATRIX *matrix, int *coord, int npoint) +{ + int *map_coord, *map; + int i; + + GB.Alloc(POINTER(&map_coord), sizeof(int) * npoint * 2); + + map = map_coord; + for (i = 0; i < npoint; i++) + { + map[0] = coord[0]; + map[1] = coord[1]; + MATRIX_map_point(matrix, &map[0], &map[1]); + coord += 2; + map += 2; + } + + return map_coord; +} + +void MATRIX_free_array(int **coord) +{ + GB.Free(POINTER(coord)); +} + + +static void update_flag(MATRIX *matrix) +{ + matrix->identity = + matrix->m11 == 1.0 && matrix->m22 == 1.0 + && matrix->m12 == 0.0 && matrix->m21 == 0.0 + && matrix->dx == 0.0 && matrix->dy == 0.0; + + matrix->rotation = matrix->m12 != 0.0 || matrix->m21 != 0.0; +} + +void MATRIX_translate(MATRIX *matrix, double dx, double dy) +{ + matrix->dx += dx * matrix->m11 + dy * matrix->m21; + matrix->dy += dy * matrix->m22 + dx * matrix->m12; + + update_flag(matrix); +} + +void MATRIX_scale(MATRIX *matrix, double sx, double sy) +{ + matrix->m11 *= sx; + matrix->m12 *= sx; + matrix->m21 *= sy; + matrix->m22 *= sy; + + update_flag(matrix); +} + +void MATRIX_rotate(MATRIX *matrix, double angle) +{ + double sina = sin(angle); + double cosa = cos(angle); + + double m11 = cosa * matrix->m11 + sina * matrix->m21; + double m12 = cosa * matrix->m12 + sina * matrix->m22; + double m21 = sina * matrix->m11 + cosa * matrix->m21; + double m22 = sina * matrix->m12 + cosa * matrix->m22; + + matrix->m11 = m11; + matrix->m12 = m12; + matrix->m21 = m21; + matrix->m22 = m22; + + update_flag(matrix); +} + diff --git a/main/lib/draw/matrix.h b/main/lib/draw/matrix.h new file mode 100644 index 00000000..70ead6cf --- /dev/null +++ b/main/lib/draw/matrix.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + matrix.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MATRIX_H +#define __MATRIX_H + +#include "gb_common.h" +#include "gb.draw.h" + +typedef + GB_MATRIX + MATRIX; + +void MATRIX_init(MATRIX *matrix); +void MATRIX_reset(MATRIX *matrix); + +void MATRIX_translate(MATRIX *matrix, double dx, double dy); +void MATRIX_scale(MATRIX *matrix, double sx, double sy); +void MATRIX_rotate(MATRIX *matrix, double angle); + +void MATRIX_map_point(MATRIX *matrix, int *x, int *y); +void MATRIX_map_rect(MATRIX *matrix, int *x, int *y, int *w, int *h); + +int *MATRIX_map_array(MATRIX *matrix, int *coord, int npoint); +void MATRIX_free_array(int **coord); + +#define MATRIX_is_identity(_matrix) ((_matrix)->identity) + +#endif diff --git a/main/lib/eval/Makefile.am b/main/lib/eval/Makefile.am new file mode 100644 index 00000000..c7cd311a --- /dev/null +++ b/main/lib/eval/Makefile.am @@ -0,0 +1,29 @@ +COMPONENT = gb.eval +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.eval.la + +gb_eval_la_LIBADD = +gb_eval_la_LDFLAGS = -module @LD_FLAGS@ +gb_eval_la_CFLAGS = -I$(top_srcdir)/share @INCLTDL@ $(AM_CFLAGS) + +gb_eval_la_SOURCES = \ + gb_alloc_override.h \ + gb_error.h gb_error.c \ + gb_array.c \ + gb_table.c \ + eval_code.h eval_code.c \ + eval_read.h eval_read.c \ + eval_reserved.c \ + eval_trans.h eval_trans.c \ + eval_trans_expr.c \ + eval_trans_tree.c \ + eval.h eval.c \ + eval_analyze.h eval_analyze.c \ + c_expression.h c_expression.c \ + c_system.h c_system.c \ + c_highlight.h c_highlight.c \ + gb.eval.h \ + main.h main.c + + diff --git a/main/lib/eval/c_expression.c b/main/lib/eval/c_expression.c new file mode 100644 index 00000000..267d3f3e --- /dev/null +++ b/main/lib/eval/c_expression.c @@ -0,0 +1,196 @@ +/*************************************************************************** + + c_expression.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_EXPRESSION_C + +#include "gb_common.h" +#include "gambas.h" + +#include "eval.h" +#include "main.h" + +#include "c_expression.h" + +/*#define DEBUG*/ + +static CEXPRESSION *_current; + + +BEGIN_METHOD_VOID(Expression_new) + + THIS->compiled = FALSE; + CLEAR(&THIS->expr); + THIS->expr.parent = THIS; + THIS->expr.custom = GB.GetClass(THIS) != CLASS_Expression; + +END_METHOD + + +BEGIN_METHOD_VOID(Expression_free) + + EVAL_clear(&THIS->expr, FALSE); + GB.FreeString(&THIS->text); + GB.FreeString(&THIS->expr.source); + GB.Unref((void **)&THIS->env); + +END_METHOD + + +BEGIN_PROPERTY(Expression_Text) + + if (READ_PROPERTY) + GB.ReturnString(THIS->text); + else + { + GB.StoreString(PROP(GB_STRING), &THIS->text); + GB.FreeString(&THIS->expr.source); + THIS->expr.source = GB.NewString(THIS->text, VPROP(GB_STRING).len); + THIS->expr.len = VPROP(GB_STRING).len; + THIS->compiled = FALSE; + } + +END_PROPERTY + + +BEGIN_PROPERTY(Expression_Environment) + + if (READ_PROPERTY) + GB.ReturnObject(THIS->env); + else + GB.StoreObject(PROP(GB_OBJECT), &THIS->env); + +END_PROPERTY + +static void prepare(CEXPRESSION *_object) +{ + if (!THIS->compiled && (THIS->expr.len > 0)) + { + if (!EVAL_compile(&THIS->expr, FALSE)) + THIS->compiled = TRUE; + else + GB.Error(THIS->expr.error); + } +} + +BEGIN_METHOD_VOID(Expression_Prepare) + + prepare(THIS); + +END_METHOD + + +static bool get_variable(const char *sym, int len, GB_VARIANT *value) +{ + if (_current->env) + if (!GB.Collection.Get(_current->env, sym, len, value)) + return FALSE; + + if (_current->expr.custom) + { + GB_FUNCTION func; + GB_VALUE *ret; + + if (!GB.GetFunction(&func, (void *)_current, "GetValue", NULL, NULL)) + { + GB.Push(1, GB_T_STRING, sym, len); + ret = GB.Call(&func, 1, FALSE); + *value = ret->_variant; + return FALSE; //value->type == GB_T_NULL; + } + } + + value->type = GB_T_NULL; + return TRUE; +} + +static void execute(CEXPRESSION *_object) +{ + CEXPRESSION *save_current; + + if (!THIS->compiled) + prepare(THIS); + + if (!THIS->compiled) + { + GB.ReturnVariant(NULL); + return; + } + + save_current = _current; + _current = THIS; + EVAL_expression(&THIS->expr, (EVAL_FUNCTION)get_variable); + GB.ReturnConvVariant(); + _current = save_current; +} + +BEGIN_PROPERTY(Expression_Value) + + execute(THIS); + +END_PROPERTY + +BEGIN_METHOD(Expression_IsSubr, GB_STRING name) + + (_p); + GB.ReturnBoolean(TRUE); + +END_METHOD + +BEGIN_METHOD(Expression_IsIdentifier, GB_STRING name) + + (_p); + GB.ReturnBoolean(TRUE); + +END_METHOD + +BEGIN_METHOD(Expression_GetValue, GB_STRING name) + + (_p); + GB.ReturnVariant(NULL); + +END_METHOD + +GB_DESC CExpressionDesc[] = +{ + GB_DECLARE("Expression", sizeof(CEXPRESSION)), + + //GB_STATIC_METHOD("_init", NULL, Expression_init, NULL), + //GB_STATIC_METHOD("_exit", NULL, Expression_exit, NULL), + + GB_METHOD("_new", NULL, Expression_new, NULL), + GB_METHOD("_free", NULL, Expression_free, NULL), + + GB_PROPERTY("Text", "s", Expression_Text), + GB_PROPERTY("Environment", "Collection;", Expression_Environment), + + GB_METHOD("Compile", NULL, Expression_Prepare, NULL), + + GB_PROPERTY_READ("Value", "v", Expression_Value), + + GB_STATIC_METHOD("IsSubr", "b", Expression_IsSubr, "(Name)s"), + GB_STATIC_METHOD("IsIdentifier", "b", Expression_IsIdentifier, "(Name)s"), + GB_METHOD("GetValue", "v", Expression_GetValue, "(Name)s"), + + GB_END_DECLARE +}; + diff --git a/main/lib/eval/c_expression.h b/main/lib/eval/c_expression.h new file mode 100644 index 00000000..6ed2e64e --- /dev/null +++ b/main/lib/eval/c_expression.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + c_expression.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_EXPRESSION_H +#define __C_EXPRESSION_H + +#include "gambas.h" +#include "eval.h" + +#ifndef __C_EXPRESSION_C +extern GB_DESC CExpressionDesc[]; +#else + +#define THIS OBJECT(CEXPRESSION) + +#endif + +typedef + struct { + GB_BASE ob; + char *text; + void *env; + EXPRESSION expr; + int pos; + bool compiled; + } + CEXPRESSION; + +#endif diff --git a/main/lib/eval/c_highlight.c b/main/lib/eval/c_highlight.c new file mode 100644 index 00000000..bad85de6 --- /dev/null +++ b/main/lib/eval/c_highlight.c @@ -0,0 +1,316 @@ +/*************************************************************************** + + c_highlight.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_HIGHLIGHT_CPP + +#include "gb_common.h" +#include "main.h" +#include "gb.eval.h" +#include "eval_analyze.h" + +#include "c_highlight.h" + +static void *_analyze_symbol = 0; +static void *_analyze_type = 0; +static void *_analyze_pos = 0; +static char *_analyze_text = 0; + +static char *_purged_line = NULL; + +static int get_char_length(unsigned char c) +{ + int n = 1; + + if (c & 0x80) + { + for (;;) + { + c <<= 1; + if (!(c & 0x80)) + break; + n++; + } + } + + return n; +} + +static char *purge(const char *s, int len_s, bool comment, bool string) +{ + char c; + uint i; + int lc, ls; + bool in_comment = FALSE; + char wait = 0; + char *r = NULL; + + for (i = 0; i < len_s; i += ls) + { + c = s[i]; + ls = lc = get_char_length((unsigned char)c); + + switch(wait) + { + case 0: + + if (in_comment) + { + if (!comment) + c = ' ', lc = 1; + } + else if (c == '"') + wait = '"'; + else if (c == '\'') + in_comment = TRUE; + + break; + + case '"': + if (c == '"') + wait = 0; + else if (c == '\\') + { + if (string) + { + if (i < len_s) + r = GB.AddChar(r, c); + //r += c; + i++; + c = s[i]; + lc = get_char_length((unsigned char)c); + } + else + { + i++; + if (i < len_s) + r = GB.AddChar(r, ' '); + //r += ' '; + c = ' ', lc = 1; + } + } + else + { + if (!string) + c = ' ', lc = 1; + } + break; + } + + if (lc == 1) + r = GB.AddChar(r, c); + else + r = GB.AddString(r, &s[i], lc); + } + + GB.FreeString(&_purged_line); + _purged_line = r; + + return r; +} + + +static int convState(int state) +{ + switch(state) + { + case RT_END: return HIGHLIGHT_NORMAL; + case RT_RESERVED: return HIGHLIGHT_KEYWORD; + case RT_IDENTIFIER: return HIGHLIGHT_SYMBOL; + case RT_CLASS: return HIGHLIGHT_DATATYPE; + case RT_NUMBER: return HIGHLIGHT_NUMBER; + case RT_STRING: return HIGHLIGHT_STRING; + case RT_SUBR: return HIGHLIGHT_SUBR; + case RT_COMMENT: return HIGHLIGHT_COMMENT; + case RT_OPERATOR: return HIGHLIGHT_OPERATOR; + case RT_DATATYPE: return HIGHLIGHT_DATATYPE; + case RT_ERROR: return HIGHLIGHT_ERROR; + case RT_HELP: return HIGHLIGHT_HELP; + case RT_PREPROCESSOR: return HIGHLIGHT_PREPROCESSOR; + case RT_ESCAPE: return HIGHLIGHT_ESCAPE; + case RT_CONSTANT: return HIGHLIGHT_CONSTANT; + case RT_LABEL: return HIGHLIGHT_LABEL; + default: return HIGHLIGHT_NORMAL; + } +} + +static void analyze(const char *src, int len_src, bool rewrite, int state) +{ + GB_ARRAY garray, tarray, parray; + int i, n, pos, len, p, upos, ulen, l; + char *str; + EVAL_ANALYZE result; + + EVAL_analyze(src, len_src, state == HIGHLIGHT_COMMENT ? RT_COMMENT : RT_END, &result, rewrite); + + n = 0; + for (i = 0; i < result.len; i++) + { + if (result.color[i].state != RT_END) + n++; + } + + GB.Array.New(&garray, GB_T_STRING, n); + GB.Array.New(&tarray, GB_T_INTEGER, n); + GB.Array.New(&parray, GB_T_INTEGER, n); + + pos = 0; + upos = 0; + i = 0; + for (p = 0; p < result.len; p++) + { + len = result.color[p].len; + + ulen = 0; + for (l = 0; l < len; l++) + ulen += get_char_length(result.str[upos + ulen]); + + if (result.color[p].state != RT_END) + { + str = GB.NewString(&result.str[upos], ulen); + *((char **)GB.Array.Get(garray, i)) = str; + *((int *)GB.Array.Get(tarray, i)) = convState(result.color[p].state); + *((int *)GB.Array.Get(parray, i)) = pos; + i++; + } + + pos += len; + upos += ulen; + } + + GB.Unref(&_analyze_symbol); + _analyze_symbol = garray; + GB.Ref(garray); + + GB.Unref(&_analyze_type); + _analyze_type = tarray; + GB.Ref(tarray); + + GB.Unref(&_analyze_pos); + _analyze_pos = parray; + GB.Ref(parray); + + GB.FreeString(&_analyze_text); + _analyze_text = result.str; +} + +BEGIN_METHOD_VOID(Highlight_exit) + + GB.Unref(&_analyze_symbol); + GB.Unref(&_analyze_type); + GB.Unref(&_analyze_pos); + GB.FreeString(&_analyze_text); + GB.FreeString(&_purged_line); + +END_METHOD + + + +BEGIN_METHOD(Highlight_Purge, GB_STRING text; GB_BOOLEAN comment; GB_BOOLEAN string) + + bool comment = VARGOPT(comment, FALSE); + bool string = VARGOPT(string, FALSE); + + if (comment && string) + GB.ReturnNewString(STRING(text), LENGTH(text)); + else + GB.ReturnString(purge(STRING(text), LENGTH(text), comment, string)); + +END_METHOD + +BEGIN_METHOD(Highlight_Analyze, GB_STRING text; GB_BOOLEAN rewrite; GB_INTEGER state) + + analyze(STRING(text), LENGTH(text), VARGOPT(rewrite, FALSE), VARGOPT(state, HIGHLIGHT_NORMAL)); + GB.ReturnObject(_analyze_symbol); + +END_METHOD + +BEGIN_PROPERTY(Highlight_Symbols) + + GB.ReturnObject(_analyze_symbol); + +END_PROPERTY + +BEGIN_PROPERTY(Highlight_Types) + + GB.ReturnObject(_analyze_type); + +END_PROPERTY + +BEGIN_PROPERTY(Highlight_Positions) + + GB.ReturnObject(_analyze_pos); + +END_PROPERTY + +BEGIN_PROPERTY(Highlight_TextAfter) + + GB.ReturnString(_analyze_text); + +END_PROPERTY + +BEGIN_PROPERTY(Highlight_Alternate) + + GB.Deprecated("gb.eval", "Highlight.Alternate", NULL); + GB.ReturnInteger(-1); + +END_PROPERTY + +GB_DESC CHighlightDesc[] = +{ + GB_DECLARE("Highlight", 0), GB_VIRTUAL_CLASS(), + + GB_CONSTANT("Background", "i", HIGHLIGHT_BACKGROUND), + GB_CONSTANT("Normal", "i", HIGHLIGHT_NORMAL), + GB_CONSTANT("Keyword", "i", HIGHLIGHT_KEYWORD), + GB_CONSTANT("Function", "i", HIGHLIGHT_SUBR), + GB_CONSTANT("Operator", "i", HIGHLIGHT_OPERATOR), + GB_CONSTANT("Symbol", "i", HIGHLIGHT_SYMBOL), + GB_CONSTANT("Number", "i", HIGHLIGHT_NUMBER), + GB_CONSTANT("String", "i", HIGHLIGHT_STRING), + GB_CONSTANT("Comment", "i", HIGHLIGHT_COMMENT), + GB_CONSTANT("Breakpoint", "i", HIGHLIGHT_BREAKPOINT), + GB_CONSTANT("Current", "i", HIGHLIGHT_CURRENT), + GB_CONSTANT("DataType", "i", HIGHLIGHT_DATATYPE), + GB_CONSTANT("Selection", "i", HIGHLIGHT_SELECTION), + GB_CONSTANT("Highlight", "i", HIGHLIGHT_HIGHLIGHT), + GB_CONSTANT("CurrentLine", "i", HIGHLIGHT_LINE), + GB_CONSTANT("Error", "i", HIGHLIGHT_ERROR), + GB_CONSTANT("Help", "i", HIGHLIGHT_HELP), + GB_CONSTANT("Preprocessor", "i", HIGHLIGHT_PREPROCESSOR), + GB_CONSTANT("Escape", "i", HIGHLIGHT_ESCAPE), + GB_CONSTANT("Label", "i", HIGHLIGHT_LABEL), + GB_CONSTANT("Constant", "i", HIGHLIGHT_CONSTANT), + GB_CONSTANT("Custom", "i", HIGHLIGHT_NUM_COLOR), + + GB_STATIC_PROPERTY_READ("Alternate", "i", Highlight_Alternate), + + GB_STATIC_METHOD("_exit", NULL, Highlight_exit, NULL), + GB_STATIC_METHOD("Analyze", "String[]", Highlight_Analyze, "(Code)s[(Rewrite)b(State)i]"), + GB_STATIC_PROPERTY_READ("Symbols", "String[]", Highlight_Symbols), + GB_STATIC_PROPERTY_READ("Types", "Integer[]", Highlight_Types), + GB_STATIC_PROPERTY_READ("Positions", "Integer[]", Highlight_Positions), + GB_STATIC_PROPERTY_READ("TextAfter", "s", Highlight_TextAfter), + GB_STATIC_METHOD("Purge", "s", Highlight_Purge, "(Code)s[(Comment)b(String)b]"), + + GB_END_DECLARE +}; diff --git a/main/lib/eval/c_highlight.h b/main/lib/eval/c_highlight.h new file mode 100644 index 00000000..6c7259e5 --- /dev/null +++ b/main/lib/eval/c_highlight.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + c_highlight.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_HIGHLIGHT_H +#define __C_HIGHLIGHT_H + +#include "gambas.h" +#include "gb.eval.h" + +#ifndef __C_HIGHLIGHT_C + +extern GB_DESC CHighlightDesc[]; + +#endif + +#endif diff --git a/main/lib/eval/c_system.c b/main/lib/eval/c_system.c new file mode 100644 index 00000000..9a38072d --- /dev/null +++ b/main/lib/eval/c_system.c @@ -0,0 +1,110 @@ +/*************************************************************************** + + c_system.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_SYSTEM_C + +#include "gambas.h" +#include "gb_common.h" +#include "gb_reserved.h" +#include "c_system.h" + +static GB_ARRAY _keywords = 0; +static GB_ARRAY _datatypes = 0; + +BEGIN_PROPERTY(CSYSTEM_keywords) + + COMP_INFO *info; + SUBR_INFO *subr; + char *str; + + if (!_keywords) + { + GB.Array.New(&_keywords, GB_T_STRING, 0); + + for (info = &COMP_res_info[1]; info->name; info++) + { + if (*info->name >= 'A' && *info->name <= 'Z') + { + str = GB.NewZeroString(info->name); + *((char **)GB.Array.Add(_keywords)) = str; + } + } + + for (subr = &COMP_subr_info[0]; subr->name; subr++) + { + str = GB.NewZeroString(subr->name); + *((char **)GB.Array.Add(_keywords)) = str; + } + + GB.Ref(_keywords); + } + + GB.ReturnObject(_keywords); + +END_PROPERTY + +BEGIN_PROPERTY(CSYSTEM_datatypes) + + COMP_INFO *info; + char *str; + + if (!_datatypes) + { + GB.Array.New(&_datatypes, GB_T_STRING, 0); + + for (info = &COMP_res_info[1]; info->name; info++) + { + if (info->flag & RSF_TYPE) + { + str = GB.NewZeroString(info->name); + *((char **)GB.Array.Add(_datatypes)) = str; + } + } + + GB.Ref(_datatypes); + } + + GB.ReturnObject(_datatypes); + +END_PROPERTY + +BEGIN_METHOD_VOID(CSYSTEM_exit) + + GB.Unref((void **)&_keywords); + GB.Unref((void **)&_datatypes); + +END_METHOD + + +GB_DESC CSystemDesc[] = +{ + GB_DECLARE("System", 0), + + GB_STATIC_METHOD("_exit", NULL, CSYSTEM_exit, NULL), + + GB_STATIC_PROPERTY_READ("Keywords", "String[]", CSYSTEM_keywords), + GB_STATIC_PROPERTY_READ("Datatypes", "String[]", CSYSTEM_datatypes), + + GB_END_DECLARE +}; + diff --git a/main/lib/eval/c_system.h b/main/lib/eval/c_system.h new file mode 100644 index 00000000..82c05b82 --- /dev/null +++ b/main/lib/eval/c_system.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + c_system.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_SYSTEM_H +#define __C_SYSTEM_H + +#include "gambas.h" +#include "gb_common.h" +#include "eval.h" + +#ifndef __C_SYSTEM_C +extern GB_DESC CSystemDesc[]; +#endif + +#endif diff --git a/main/lib/eval/eval.c b/main/lib/eval/eval.c new file mode 100644 index 00000000..a48b8c97 --- /dev/null +++ b/main/lib/eval/eval.c @@ -0,0 +1,291 @@ +/*************************************************************************** + + eval.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __EVAL_C + +#include "gb_alloc_override.h" +#include "gb_common.h" +#include "gb_error.h" +#include "gb_array.h" + +#include "eval_analyze.h" +#include "eval_trans.h" +#include "gb_code.h" +#include "eval.h" + +#include "gb_common_case_temp.h" + +/*#define DEBUG*/ + +EXPRESSION *EVAL; +EXPRESSION EVAL_read_expr; + + +void EVAL_init(void) +{ + RESERVED_init(); + CLEAR(&EVAL_read_expr); +} + + +void EVAL_exit(void) +{ + EVAL_clear(&EVAL_read_expr, FALSE); + RESERVED_exit(); + EVAL_analyze_exit(); +} + +GB_VALUE *EVAL_expression(EXPRESSION *expr, EVAL_FUNCTION get_value) +{ + EVAL = expr; + + /* Creates a class and a function for the evaluation context */ + + CLEAR(&EVAL->func); + EVAL->func.type = T_VARIANT; + EVAL->func.n_param = EVAL->nvar; + EVAL->func.npmin = EVAL->nvar; + EVAL->func.stack_usage = EVAL->stack_usage; + EVAL->func.code = EVAL->code; + + CLEAR(&EVAL->class_load); + EVAL->class_load.cst = EVAL->cst; + EVAL->class_load.func = &EVAL->func; + EVAL->class_load.class_ref = EVAL->class; + EVAL->class_load.unknown = EVAL->unknown; + + CLEAR(&EVAL->exec_class); + /*_class.class = CLASS_class;*/ + EVAL->exec_class.ref = 1; + EVAL->exec_class.count = 1; + EVAL->exec_class.name = ".Eval"; + EVAL->exec_class.loaded = TRUE; + EVAL->exec_class.ready = TRUE; + EVAL->exec_class.load = &EVAL->class_load; + + return GB.Eval(EVAL, get_value); +} + + +void EVAL_clear(EXPRESSION *expr, bool keep_error) +{ + ARRAY_delete(&expr->tree); + + ARRAY_delete(&expr->var); + ARRAY_delete(&expr->unknown); + ARRAY_delete(&expr->class); + ARRAY_delete(&expr->cst); + + TABLE_delete(&expr->string); + TABLE_delete(&expr->table); + + if (expr->pattern) + FREE(&expr->pattern); + if (expr->code) + FREE(&expr->code); + + if (!keep_error) + GB.FreeString(&expr->error); +} + + +void EVAL_start(EXPRESSION *expr) +{ + int index; + + ALLOC(&expr->pattern, sizeof(PATTERN) * (16 + expr->len)); + expr->pattern_count = 0; + + TABLE_create(&expr->table, sizeof(EVAL_SYMBOL), EVAL->analyze ? TF_NORMAL : TF_IGNORE_CASE); + TABLE_create(&expr->string, sizeof(SYMBOL), TF_NORMAL); + + ARRAY_create(&expr->cst); + ARRAY_create(&expr->class); + ARRAY_create(&expr->unknown); + expr->code = NULL; + expr->ncode = 0; + expr->ncode_max = 0; + ARRAY_create(&expr->var); + expr->nvar = 0; + + if (EVAL->custom) + { + // _ is the first symbol, it returns the Expression object + TABLE_add_symbol(expr->table, "_", 1, &index); + EVAL_add_variable(index); + } +} + +bool EVAL_compile(EXPRESSION *expr, bool assign) +{ + bool error = FALSE; + + EVAL = expr; + + EVAL_clear(EVAL, FALSE); + + if (expr->len == 0) + return TRUE; + + EVAL_start(EVAL); + + TRY + { + EVAL_read(); + + EVAL->current = EVAL->pattern; + + if (PATTERN_is(*EVAL->current, RS_LET)) + { + EVAL->current++; + assign = TRUE; + } + + if (assign) + { + if (!TRANS_affectation()) + THROW(E_SYNTAX); + } + else + TRANS_expression(); + + if (!PATTERN_is_end(*EVAL->current)) + THROW(E_SYNTAX); + + CODE_return(1); + + EVAL->stack_usage = CODE_stack_usage; + } + CATCH + { + EVAL_clear(EVAL, TRUE); + error = TRUE; + } + END_TRY + + #ifdef DEBUG + CODE_dump(EVAL->code); + printf("Stack usage = %d\n", CODE_stack_usage); + #endif + + return error; +} + +bool EVAL_get_assignment_symbol(EXPRESSION *expr, const char **name, int *len) +{ + ushort code = EVAL->assign_code; + int index; + EVAL_SYMBOL *sym; + + if ((code & 0xFF00) == C_POP_PARAM) + { + index = -((signed char)(code & 0xFF)) - 1; + sym = (EVAL_SYMBOL *)TABLE_get_symbol(EVAL->table, EVAL->var[index]); + *name = sym->sym.name; + *len = sym->sym.len; + return FALSE; + } + else + return TRUE; +} + +void EVAL_new(EXPRESSION **expr, char *src, int len) +{ + GB.Alloc((void **)expr, sizeof(EXPRESSION)); + CLEAR(*expr); + (*expr)->source = GB.NewString(src, len); + (*expr)->source = GB.AddString((*expr)->source, "\n\0", 2); + (*expr)->len = len + 2; + /*(*expr)->option = option;*/ +} + + +void EVAL_free(EXPRESSION **pexpr) +{ + EVAL_clear(*pexpr, FALSE); + GB.FreeString(&(*pexpr)->source); + GB.Free((void **)pexpr); +} + + +int EVAL_add_constant(CLASS_CONST *cst) +{ + int num; + CLASS_CONST *desc; + + num = ARRAY_count(EVAL->cst); + + desc = ARRAY_add(&EVAL->cst); + *desc = *cst; + + return num; +} + + +int EVAL_add_class(char *name) +{ + int num; + CLASS **desc; + + num = ARRAY_count(EVAL->class); + + desc = ARRAY_add(&EVAL->class); + *desc = (CLASS *)GB.FindClassLocal(name); + + /*sym->class = num + 1;*/ + + return num; +} + + +int EVAL_add_unknown(char *name) +{ + int num; + char **desc; + + num = ARRAY_count(EVAL->unknown); + + desc = ARRAY_add(&EVAL->unknown); + *desc = name; + + return num; +} + + +int EVAL_add_variable(int index) +{ + EVAL_SYMBOL *sym; + + sym = (EVAL_SYMBOL *)TABLE_get_symbol(EVAL->table, index); + + if (sym->local == 0) + { + EVAL->nvar++; + sym->local = EVAL->nvar; + + *((int *)ARRAY_add(&EVAL->var)) = index; + } + + return (-sym->local); +} + diff --git a/main/lib/eval/eval.h b/main/lib/eval/eval.h new file mode 100644 index 00000000..bd8e73c1 --- /dev/null +++ b/main/lib/eval/eval.h @@ -0,0 +1,62 @@ +/*************************************************************************** + + eval.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __EVAL_H +#define __EVAL_H + +#include "gb_table.h" +#include "gb_reserved.h" +#include "gb_error.h" +#include "eval_read.h" +/*#include "CCollection.h"*/ +#include "main.h" + +#include "../../gbx/gbx_expression.h" +#include "gb.eval.h" + +#ifndef __EVAL_C +EXTERN EXPRESSION *EVAL; +EXTERN EXPRESSION EVAL_read_expr; +#endif + + +void EVAL_init(void); +void EVAL_exit(void); + +void EVAL_new(EXPRESSION **expr, char *src, int len); +void EVAL_free(EXPRESSION **expr); +bool EVAL_compile(EXPRESSION *expr, bool assign); + +GB_VALUE *EVAL_expression(EXPRESSION *expr, EVAL_FUNCTION get_value); +void EVAL_clear(EXPRESSION *expr, bool keep_error); + +int EVAL_add_constant(CLASS_CONST *cst); +int EVAL_add_class(char *name); +int EVAL_add_unknown(char *name); +int EVAL_add_variable(int index); + +void EVAL_start(EXPRESSION *expr); + +bool EVAL_get_assignment_symbol(EXPRESSION *expr, const char **name, int *len); + +#endif diff --git a/main/lib/eval/eval_analyze.c b/main/lib/eval/eval_analyze.c new file mode 100644 index 00000000..1136b23b --- /dev/null +++ b/main/lib/eval/eval_analyze.c @@ -0,0 +1,746 @@ +/*************************************************************************** + + eval_analyze.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __EVAL_ANALYZE_C + +#include "gambas.h" +#include "gb_common.h" +#include "gb_array.h" +#include "eval_analyze.h" + +#include "c_system.h" +/*#define DEBUG*/ + +static const uchar _utf8_char_length[256] = +{ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 +}; + +static char _analyze_buffer[256]; +static int _analyze_buffer_pos; + +#define COLOR_BUFFER_SIZE 256 +static EVAL_COLOR _colors[COLOR_BUFFER_SIZE]; +static int _colors_len = 0; +static EVAL_COLOR *_color_buffer = NULL; + +#define NEXT_UTF8_CHAR(_p) (_p += _utf8_char_length[(uchar)*(_p)]) + +static int get_type(PATTERN *pattern) +{ + int type = PATTERN_type(*pattern); + int index = PATTERN_index(*pattern); + + if (type == RT_RESERVED) + { + if (index >= RS_COLON) + { + if (!((index == RS_AND || index == RS_OR) && PATTERN_is(pattern[1], RS_IF))) + type = RT_OPERATOR; + } + else if (RES_is_type(index)) + type = RT_DATATYPE; + else if (index == RS_WITH && pattern > EVAL->pattern) + { + index = PATTERN_index(pattern[-1]); + if (index == RS_BEGINS || index == RS_ENDS) + type = RT_OPERATOR; + } + } + + return type; +} + +static int is_me_last_kind(PATTERN pattern) +{ + return PATTERN_is(pattern, RS_ME) + || PATTERN_is(pattern, RS_SUPER) + || PATTERN_is(pattern, RS_LAST) + || PATTERN_is(pattern, RS_TRUE) + || PATTERN_is(pattern, RS_FALSE) + || PATTERN_is(pattern, RS_PINF) + || PATTERN_is(pattern, RS_MINF) + || PATTERN_is(pattern, RS_NULL); +} + +static int is_optional_kind(PATTERN pattern) +{ + return PATTERN_is(pattern, RS_OPTIONAL) + || PATTERN_is(pattern, RS_BYREF); +} + +static void get_symbol(PATTERN pattern, const char **symbol, int *len) +{ + static char keyword[16]; + int i; + SYMBOL *sym; + int type = PATTERN_type(pattern); + int index = PATTERN_index(pattern); + + switch(type) + { + case RT_RESERVED: + *symbol = COMP_res_info[index].name; + *len = strlen(*symbol); + if (!EVAL->rewrite) + { + memcpy(keyword, *symbol, *len); + for (i = 0; i < *len; i++) + keyword[i] = toupper(keyword[i]); + *symbol = keyword; + } + return; + + case RT_NUMBER: + case RT_IDENTIFIER: + sym = TABLE_get_symbol(EVAL->table, index); + break; + + case RT_CLASS: + sym = TABLE_get_symbol(EVAL->table, index); + break; + + case RT_STRING: + case RT_TSTRING: + case RT_COMMENT: + case RT_ERROR: + sym = TABLE_get_symbol(EVAL->string, index); + break; + + case RT_SUBR: + *symbol = COMP_subr_info[index].name; + *len = strlen(*symbol); + return; + + default: + *symbol = NULL; + *len = 0; + return; + } + + *symbol = sym->name; + *len = sym->len; + if (*len > EVAL_COLOR_MAX_LEN) + *len = EVAL_COLOR_MAX_LEN; +} + + +static void add_data(int state, int len) +{ + EVAL_COLOR *color; + + if (len == 0) + return; + + if (_colors_len >= COLOR_BUFFER_SIZE) + { + if (!_color_buffer) + ARRAY_create_inc(&_color_buffer, COLOR_BUFFER_SIZE); + + color = ARRAY_add_many(&_color_buffer, COLOR_BUFFER_SIZE); + memcpy(color, _colors, sizeof(EVAL_COLOR) * COLOR_BUFFER_SIZE); + _colors_len = 0; + } + else + { + color = &_colors[_colors_len]; + color->state = state; + color->len = len; + color->alternate = FALSE; + _colors_len++; + } +} + +static void add_data_merge(int state, int len) +{ + if (_colors_len > 0 && _colors[_colors_len - 1].state == state && (_colors[_colors_len - 1].len + len) < EVAL_COLOR_MAX_LEN) + _colors[_colors_len - 1].len += len; + else + add_data(state, len); +} + +static void flush_colors(EVAL_ANALYZE *result) +{ + EVAL_COLOR *color; + + if (_color_buffer) + { + if (_colors_len) + { + color = ARRAY_add_many(&_color_buffer, _colors_len); + memcpy(color, _colors, sizeof(EVAL_COLOR) * _colors_len); + } + + result->color = _color_buffer; + result->len = ARRAY_count(_color_buffer); + } + else + { + result->color = _colors; + result->len = _colors_len; + } +} + +static int is_proc(void) +{ + PATTERN pattern; + int i; + + if (!EVAL->pattern) + return FALSE; + + for (i = 0;; i++) + { + pattern = EVAL->pattern[i]; + if (PATTERN_is_end(pattern)) + return FALSE; + + if (PATTERN_is(pattern, RS_PRIVATE) || PATTERN_is(pattern, RS_PUBLIC) || PATTERN_is(pattern, RS_STATIC) || PATTERN_is(pattern, RS_FAST)) + continue; + + return (PATTERN_is(pattern, RS_SUB) || PATTERN_is(pattern, RS_PROCEDURE) || PATTERN_is(pattern, RS_FUNCTION)); + } +} + +static int get_indent(bool *empty) +{ + int i; + unsigned char c; + + *empty = TRUE; + + for (i = 0; i < (int)EVAL->len; i++) + { + c = EVAL->source[i]; + if (c > ' ') + { + *empty = FALSE; + break; + } + } + + return i; +} + +static int get_symbol_indent(const char *symbol, int len) +{ + int i; + + for (i = 0; i < len; i++) + { + if (!(symbol[i] > 0 && symbol[i] < 33)) + return i; + } + + return len; +} + +static bool symbol_starts_with(const char *symbol, int len, int from, const char *comp) +{ + int l = strlen(comp); + return (from < (len - l) && strncmp(&symbol[from], comp, l) == 0); +} + + +static int get_utf8_length(const char *s, int l) +{ + int len; + int i; + + for (i = 0, len = 0; i < l; i++) + { + if ((s[i] & 0xC0) != 0x80) + len++; + } + + return len; +} + +static void init_result() +{ + _analyze_buffer_pos = 0; +} + +static void flush_result(EVAL_ANALYZE *result) +{ + if (_analyze_buffer_pos > 0) + { + result->str = GB.AddString(result->str, _analyze_buffer, _analyze_buffer_pos); + _analyze_buffer_pos = 0; + } +} + +static void add_result(EVAL_ANALYZE *result, const char *str, int len) +{ + if ((_analyze_buffer_pos + len) > sizeof(_analyze_buffer)) + flush_result(result); + + if (len > sizeof(_analyze_buffer)) + result->str = GB.AddString(result->str, str, len); + else + { + memcpy(&_analyze_buffer[_analyze_buffer_pos], str, len); + _analyze_buffer_pos += len; + } +} + +static void add_result_char(EVAL_ANALYZE *result, char c) +{ + if ((_analyze_buffer_pos + 1) > sizeof(_analyze_buffer)) + flush_result(result); + + _analyze_buffer[_analyze_buffer_pos++] = c; +} + +static void analyze(EVAL_ANALYZE *result) +{ + PATTERN *pattern; + int nspace; + bool empty = FALSE; + int type, old_type, next_type; + const char *symbol; + const char *p; + bool space_before, space_after; + int len, i, l; + bool preprocessor; + + _colors_len = 0; + EVAL_analyze_exit(); + + pattern = EVAL->pattern; + nspace = 0; + preprocessor = FALSE; + + if (EVAL->len <= 0) + return; + + if (!EVAL->comment) + { + nspace = get_indent(&empty); + add_data(RT_END, nspace); + } + + if (empty) + return; + + if (!pattern) + return; + + init_result(); + + if (nspace) + add_result(result, EVAL->source, nspace); + + type = EVAL->comment ? RT_COMMENT : RT_END; + next_type = RT_END; + old_type = RT_END; + space_after = FALSE; + + for(;;) + { + old_type = next_type; + type = get_type(pattern); + next_type = type; + get_symbol(*pattern, &symbol, &len); + + space_before = space_after; + space_after = FALSE; + + if (type == RT_END) + break; + + //if (in_quote && (type == RT_RESERVED || type == RT_DATATYPE || type == RT_SUBR)) + // type = RT_IDENTIFIER; + + switch(type) + { + case RT_RESERVED: + //state = Keyword; + //if (old_type != RT_OPERATOR) + //me = is_me_last(*pattern); + + if (is_me_last_kind(*pattern)) + { + if (old_type != RT_OPERATOR) + space_before = TRUE; + next_type = RT_IDENTIFIER; + } + else if (is_optional_kind(*pattern)) + { + if (old_type != RT_OPERATOR) + space_before = TRUE; + } + else + space_before = TRUE; + + /*if (!is_me_last_kind(*pattern)) + space_before = TRUE; + else + { + if (*pattern != RS_OPTIONAL) + if (old_type != RT_OPERATOR) + space_before = TRUE; + }*/ + + if (preprocessor && PATTERN_is(pattern[-1], RS_SHARP)) + space_before = FALSE; + + /*if (PATTERN_index(*pattern) >= RS_COLON) + { + int i; + + for (i = 0; i < len; i++) + usym[i] = GB.toupper(symbol[i]); + usym[len] = 0; + + symbol = usym; + }*/ + + break; + + case RT_DATATYPE: + //state = Datatype; + if (PATTERN_is(pattern[-1], RS_OPEN)) + type = RT_RESERVED; + + if (old_type != RT_OPERATOR) + space_before = TRUE; + + break; + + case RT_IDENTIFIER: + case RT_CLASS: + //state = Symbol; + if (old_type != RT_OPERATOR) + space_before = TRUE; + break; + + case RT_NUMBER: + //state = Number; + if (old_type != RT_OPERATOR) + space_before = TRUE; + break; + + case RT_STRING: + //state = String; + if (old_type != RT_OPERATOR) + space_before = TRUE; + break; + + case RT_SUBR: + //state = Subr; + if (old_type != RT_OPERATOR) + space_before = TRUE; + break; + + case RT_COMMENT: + //state = Commentary; + space_before = *symbol != ' '; + i = get_symbol_indent(symbol, len); + if (i <= (len - 2) && symbol[i + 1] == '\'') + type = RT_HELP; + else + { + while (i < len && (uchar)symbol[i] == '\'') + i++; + + while (i < len && (uchar)symbol[i] <= ' ') + i++; + + if (i < len) + { + if (symbol_starts_with(symbol, len, i, "NOTE:") + || symbol_starts_with(symbol, len, i, "TODO:") + || symbol_starts_with(symbol, len, i, "FIXME:")) + type = RT_HELP; + } + } + break; + + case RT_OPERATOR: + + if (index("([)]@", *symbol)) + { + space_after = FALSE; + } + else if (index(":;,", *symbol)) + { + space_before = FALSE; + space_after = TRUE; + } + else if (index("#{", *symbol)) + { + if (old_type != RT_OPERATOR) + space_before = TRUE; + space_after = FALSE; + //in_quote = *symbol == '{'; + + if (!preprocessor && *symbol == '#' && old_type == RT_END) + preprocessor = TRUE; + } + else if (index("}", *symbol)) + { + space_before = FALSE; + space_after = FALSE; + //in_quote = FALSE; + } + else if (index(".!", *symbol)) //symbol[0] == '.' && symbol[1] == 0) + { + //space_before = FALSE; + space_after = FALSE; + } + else if (PATTERN_is(*pattern, RS_NOT) || *symbol == '-') + { + if (old_type == RT_OPERATOR && (PATTERN_is(pattern[-1], RS_LBRA) || PATTERN_is(pattern[-1],RS_LSQR))) + space_before = FALSE; + else + space_before = TRUE; + + space_after = TRUE; + } + else + { + space_before = TRUE; + space_after = TRUE; + } + + if (old_type == RT_RESERVED) + space_before = TRUE; + + break; + + case RT_ERROR: + space_before = TRUE; + break; + } + + if (space_before && old_type != RT_END) + { + add_result_char(result, ' '); + add_data(preprocessor ? RT_PREPROCESSOR : RT_END, 1); + } + + if (type == RT_STRING) + add_result_char(result, '"'); + + if (len) + { + if (EVAL->rewrite && type == RT_CLASS) + { + add_result_char(result, toupper(symbol[0])); + if (len > 1) add_result(result, &symbol[1], len - 1); + } + else + add_result(result, symbol, len); + //printf("add: %.*s\n", len, symbol); + len = get_utf8_length(symbol, len); + } + + if (type == RT_STRING) + { + add_result_char(result, '"'); + len += 2; + } + + if (EVAL->rewrite) + { + if (type == RT_STRING) + { + add_data(RT_STRING, 1); + len -= 2; + for (i = 0, p = symbol; i < len; i++) + { + if (*p == '\\') + { + i++; + NEXT_UTF8_CHAR(p); + + add_data_merge(RT_ESCAPE, 1); + if (i < len) + { + if (*p == 'x' && i < (len - 2) && isxdigit(p[1]) && isxdigit(p[2])) + { + l = 3; + i += 2; + } + else + l = 1; + add_data_merge(RT_ESCAPE, l); + + while (l--) + NEXT_UTF8_CHAR(p); + } + } + else + { + NEXT_UTF8_CHAR(p); + add_data_merge(RT_STRING, 1); + } + } + add_data_merge(RT_STRING, 1); + goto __NEXT_PATTERN; + } + else if (type == RT_IDENTIFIER) + { + if (PATTERN_is(pattern[1], RS_COLON)) + { + add_result_char(result, ':'); + add_data(RT_LABEL, len + 1); + space_after = TRUE; + pattern ++; + goto __NEXT_PATTERN; + } + else if (old_type == RT_RESERVED && (PATTERN_is(pattern[-1], RS_GOTO) || PATTERN_is(pattern[-1], RS_GOSUB))) + { + type = RT_LABEL; + } + } + else if (type == RT_RESERVED) + { + if (PATTERN_is(*pattern, RS_NULL) + || PATTERN_is(*pattern, RS_TRUE) + || PATTERN_is(*pattern, RS_FALSE) + || PATTERN_is(*pattern, RS_PINF) + || PATTERN_is(*pattern, RS_MINF)) + { + type = RT_CONSTANT; + } + } + } + + if (preprocessor && type != RT_COMMENT && type != RT_HELP) + add_data(RT_PREPROCESSOR, len); + else + add_data(type, len); + //printf("add_data: %.d (%d)\n", type, len); + + __NEXT_PATTERN: + pattern++; + } + + flush_result(result); + flush_colors(result); + + //fprintf(stderr, "analyze: %d %s\n", strlen(result->str), result->str); +} + + +#define add_pattern(_type, _index) EVAL->pattern[EVAL->pattern_count++] = PATTERN_make((_type), (_index)); + +static void add_end_pattern(void) +{ + int index; + int len; + + len = EVAL->len - (READ_source_ptr - EVAL->source); + if (len > 0) + { + TABLE_add_symbol(EVAL->string, READ_source_ptr, len, &index); + add_pattern(RT_ERROR, index); + } + + add_pattern(RT_END, 0); + //get_symbol(PATTERN_make(RT_ERROR, index), &sym, &len); +} + + +PUBLIC void EVAL_analyze(const char *src, int len, int state, EVAL_ANALYZE *result, bool rewrite) +{ + int nspace = 0; + + #ifdef DEBUG + printf("EVAL: %*.s\n", expr->len, expr->source); + #endif + + CLEAR(result); + + while (len > 0 && src[len - 1] == ' ') + { + len--; + nspace++; + } + + result->len = 0; + result->str = NULL; + + if (len > 0) + { + EVAL = &EVAL_read_expr; + + EVAL_clear(EVAL, FALSE); + + EVAL->source = GB.NewString(src, len); + EVAL->source = GB.AddString(EVAL->source, "\0\0", 2); + EVAL->len = len; + + EVAL->analyze = TRUE; + EVAL->rewrite = rewrite; + EVAL->comment = state == RT_COMMENT; + + //fprintf(stderr, "EVAL_analyze: [%d] %.*s\n", EVAL->comment, len, src); + + EVAL_start(EVAL); + + TRY + { + EVAL_read(); + } + CATCH + { + add_end_pattern(); + } + END_TRY + + analyze(result); + result->proc = is_proc(); + result->state = EVAL->comment ? RT_COMMENT : RT_END; + + //fprintf(stderr, "--> [%d]\n", EVAL->comment); + + GB.FreeString(&EVAL->source); + } + else + { + result->proc = FALSE; + } + + while (nspace > 0) + { + result->str = GB.AddString(result->str, " ", nspace > 8 ? 8 : nspace); + nspace -= 8; + } +} + + +void EVAL_analyze_exit(void) +{ + if (_color_buffer) + ARRAY_delete(&_color_buffer); +} diff --git a/main/lib/eval/eval_analyze.h b/main/lib/eval/eval_analyze.h new file mode 100644 index 00000000..1fe8af8f --- /dev/null +++ b/main/lib/eval/eval_analyze.h @@ -0,0 +1,32 @@ +/*************************************************************************** + + eval_analyze.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __EVAL_ANALYZE_H +#define __EVAL_ANALYZE_H + +#include "eval.h" + +void EVAL_analyze(const char *src, int len, int state, EVAL_ANALYZE *result, bool rewrite); +void EVAL_analyze_exit(void); + +#endif diff --git a/main/lib/eval/eval_code.c b/main/lib/eval/eval_code.c new file mode 100644 index 00000000..8f7b6dd5 --- /dev/null +++ b/main/lib/eval/eval_code.c @@ -0,0 +1,37 @@ +/*************************************************************************** + + eval_code.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __EVAL_CODE_C + +#define PROJECT_EXEC + +#include "gb_alloc_override.h" + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_reserved.h" +#include "gb_code.h" +#include "gb_limit.h" +#include "eval.h" + +#include "gb_code_temp.h" diff --git a/main/lib/eval/eval_code.h b/main/lib/eval/eval_code.h new file mode 100644 index 00000000..d8371318 --- /dev/null +++ b/main/lib/eval/eval_code.h @@ -0,0 +1,110 @@ +/*************************************************************************** + + eval_code.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CODE_H +#define __CODE_H + +#include "gb_pcode.h" + +#ifndef __CODE_C +EXTERN short CODE_stack_usage; +#endif + + +PUBLIC void CODE_begin_function(FUNCTION *func); +PUBLIC void CODE_end_function(FUNCTION *func); + +PUBLIC boolean CODE_popify_last(void); +PUBLIC boolean CODE_check_statement_last(void); +PUBLIC boolean CODE_check_pop_local_last(short *local); + +PUBLIC int CODE_get_current_pos(); +PUBLIC void CODE_dump(PCODE *code); + +PUBLIC void CODE_push_number(int value); +PUBLIC void CODE_push_const(short value); + +PUBLIC void CODE_push_local(short num); +PUBLIC void CODE_pop_local(short num); + +PUBLIC void CODE_push_array(short nparam); + +PUBLIC void CODE_push_global(short global, boolean is_static, boolean is_function); +PUBLIC void CODE_pop_global(short global, boolean is_static); + +PUBLIC void CODE_push_symbol(short symbol); +PUBLIC void CODE_pop_symbol(short symbol); + +PUBLIC void CODE_push_unknown(short symbol); +PUBLIC void CODE_pop_unknown(short symbol); + +PUBLIC void CODE_push_class(short class); +PUBLIC void CODE_push_special(short spec); +PUBLIC void CODE_push_event(short event); + +PUBLIC void CODE_pop_optional(short num); + +PUBLIC void CODE_jump(void); +PUBLIC void CODE_jump_first(short local); +PUBLIC void CODE_jump_next(void); +PUBLIC void CODE_jump_if_true(void); +PUBLIC void CODE_jump_if_false(void); +PUBLIC void CODE_jump_length(short src, short dst); +PUBLIC void CODE_first(short local); +PUBLIC void CODE_next(void); + +PUBLIC void CODE_op(short op, short nparam, boolean fixed); + +PUBLIC void CODE_new(ushort nparam, boolean array, boolean event); + +PUBLIC void CODE_push_me(void); +PUBLIC void CODE_push_last(void); +PUBLIC void CODE_push_null(void); +PUBLIC void CODE_push_boolean(boolean value); + +PUBLIC void CODE_dup(void); + +PUBLIC void CODE_return(boolean return_value); + +PUBLIC void CODE_quit(void); +PUBLIC void CODE_stop(void); + +PUBLIC void CODE_push_char(char car); +PUBLIC void CODE_push_void(void); + +PUBLIC void CODE_event(boolean on); + +PUBLIC void CODE_subr(short subr, short nparam, short optype, boolean output, boolean fixed); +PUBLIC void CODE_call(short nparam, boolean output); +PUBLIC void CODE_drop(void); +PUBLIC void CODE_push_return(void); + +PUBLIC void CODE_try(void); +PUBLIC void CODE_end_try(void); +PUBLIC void CODE_catch(void); + +PUBLIC void CODE_pop_ctrl(short num); + +PUBLIC void CODE_break(void); + +#endif diff --git a/main/lib/eval/eval_read.c b/main/lib/eval/eval_read.c new file mode 100644 index 00000000..350599c4 --- /dev/null +++ b/main/lib/eval/eval_read.c @@ -0,0 +1,1409 @@ +/*************************************************************************** + + eval_read.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __EVAL_READ_C + +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_table.h" + +#include "eval.h" +#include "eval_read.h" + + +//#define DEBUG 1 +//#define ENABLE_BIG_COMMENT + +PUBLIC const char *READ_source_ptr; +#define source_ptr READ_source_ptr + +static bool is_init = FALSE; +static bool _begin_line = FALSE; + +static char ident_car[256]; +static char first_car[256]; +static char digit_car[256]; +static char noop_car[256]; +static char canres_car[256]; + +#undef isspace +#define isspace(_c) (((uchar)_c) <= ' ') +#undef isdigit +#define isdigit(_c) (digit_car[_c]) + +enum +{ + GOTO_BREAK, + GOTO_SPACE, + GOTO_COMMENT, + GOTO_STRING, + GOTO_IDENT, + GOTO_QUOTED_IDENT, + GOTO_NUMBER, + GOTO_ERROR, + GOTO_SHARP, + GOTO_OTHER +}; + +static void READ_init(void) +{ + unsigned char i; + + if (!is_init) + { + for (i = 0; i < 255; i++) + { + ident_car[i] = (i != 0) && ((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z') || (i >= '0' && i <= '9') || strchr("$_?@", i)); + digit_car[i] = (i >= '0' && i <= '9'); + noop_car[i] = ident_car[i] || digit_car[i] || i <= ' '; + canres_car[i] = (i != ':') && (i != '.') && (i != '!') && (i != '('); + + if (i == 0) + first_car[i] = GOTO_BREAK; + else if (i <= ' ') + first_car[i] = GOTO_SPACE; + else if (i == '\'') + first_car[i] = GOTO_COMMENT; + else if (i == '"') + first_car[i] = GOTO_STRING; + else if (i == '#') + first_car[i] = GOTO_SHARP; + else if ((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z') || i == '$' || i == '_') + first_car[i] = GOTO_IDENT; + else if (i == '{') + first_car[i] = GOTO_QUOTED_IDENT; + else if (i >= '0' && i <= '9') + first_car[i] = GOTO_NUMBER; + else if (i >= 127) + first_car[i] = GOTO_ERROR; + else + first_car[i] = GOTO_OTHER; + } + + is_init = TRUE; + } +} + +static bool _no_quote = FALSE; +#define BUF_MAX 255 +static char _buffer[BUF_MAX + 1]; + +PUBLIC char *READ_get_pattern(PATTERN *pattern) +{ + int type = PATTERN_type(*pattern); + int index = PATTERN_index(*pattern); + const char *str; + const char *before = _no_quote ? "" : "'"; + const char *after = _no_quote ? "" : "'"; + + switch(type) + { + case RT_RESERVED: + //snprintf(COMMON_buffer, COMMON_BUF_MAX, "%s%s%s", before, TABLE_get_symbol_name(COMP_res_table, index), after); + str = COMP_res_info[index].name; + if (ispunct((unsigned char)*str)) + snprintf(_buffer, BUF_MAX, "%s%s%s", before, str, after); + else + strcpy(_buffer, str); + break; + + case RT_NUMBER: + case RT_IDENTIFIER: + case RT_CLASS: + snprintf(_buffer, BUF_MAX, "%s%s%s", before, TABLE_get_symbol_name(EVAL->table, index), after); + break; + + case RT_STRING: + case RT_TSTRING: + if (_no_quote) + snprintf(_buffer, BUF_MAX, "\"%s\"", TABLE_get_symbol_name(EVAL->string, index)); + else + strcpy(_buffer, "string"); + break; + + case RT_NEWLINE: + case RT_END: + strcpy(_buffer, "end of expression"); + break; + + case RT_SUBR: + //snprintf(COMMON_buffer, COMMON_BUF_MAX, "%s%s%s", bafore, COMP_subr_info[index].name, after); + strcpy(_buffer, COMP_subr_info[index].name); + break; + + case RT_COMMENT: + strncpy(_buffer, TABLE_get_symbol_name(EVAL->string, index), BUF_MAX); + _buffer[BUF_MAX] = 0; + break; + + default: + sprintf(_buffer, "%s?%08X?%s", before, *pattern, after); + } + + return _buffer; +} + +#if DEBUG +PUBLIC void READ_dump_pattern(PATTERN *pattern) +{ + int type = PATTERN_type(*pattern); + int index = PATTERN_index(*pattern); + int pos; + + pos = (int)(pattern - EVAL->pattern); + if (pos < 0 || pos >= EVAL->pattern_count) + return; + + printf("%d ", pos); + + if (PATTERN_flag(*pattern) & RT_FIRST) + printf("!"); + else + printf(" "); + + if (PATTERN_flag(*pattern) & RT_POINT) + printf("."); + else + printf(" "); + + if (PATTERN_flag(*pattern) & RT_CLASS) + printf("C"); + else + printf(" "); + + printf(" "); + + _no_quote = TRUE; + + if (type == RT_RESERVED) + printf("RESERVED %s\n", READ_get_pattern(pattern)); + else if (type == RT_NUMBER) + printf("NUMBER %s\n", READ_get_pattern(pattern)); + else if (type == RT_IDENTIFIER) + printf("IDENTIFIER %s\n", READ_get_pattern(pattern)); + else if (type == RT_CLASS) + printf("CLASS %s\n", READ_get_pattern(pattern)); + else if (type == RT_STRING) + printf("STRING %s\n", READ_get_pattern(pattern)); + else if (type == RT_TSTRING) + printf("TSTRING %s\n", READ_get_pattern(pattern)); + else if (type == RT_NEWLINE) + printf("NEWLINE (%d)\n", index); + else if (type == RT_END) + printf("END\n"); + else if (type == RT_PARAM) + printf("PARAM %d\n", index); + else if (type == RT_SUBR) + printf("SUBR %s\n", READ_get_pattern(pattern)); + else if (type == RT_COMMENT) + printf("COMMENT %s\n", READ_get_pattern(pattern)); + else + printf("? %d\n", index); + + _no_quote = FALSE; +} +#endif + +#define get_char_offset(_offset) ((unsigned char)source_ptr[(_offset)]) +#define get_char() ((unsigned char)(*source_ptr)) + +static unsigned char next_char(void) +{ + source_ptr++; + return get_char(); +} + +#ifdef DEBUG + +static void add_pattern(int type, int index) +{ + EVAL->pattern[EVAL->pattern_count++] = PATTERN_make(type, index); + READ_dump_pattern(&EVAL->pattern[EVAL->pattern_count - 1]); +} + +#else + +#define add_pattern(_type, _index) EVAL->pattern[EVAL->pattern_count++] = PATTERN_make((_type), (_index)); + +#endif + +static PATTERN get_last_last_pattern() +{ + if (EVAL->pattern_count > 1) + return EVAL->pattern[EVAL->pattern_count - 2]; + else + return NULL_PATTERN; +} + +static PATTERN get_last_pattern() +{ + if (EVAL->pattern_count > 0) + return EVAL->pattern[EVAL->pattern_count - 1]; + else + return NULL_PATTERN; +} + +static void add_newline() +{ + add_pattern(RT_NEWLINE, 0); + source_ptr++; +} + + +static void add_end() +{ + add_pattern(RT_END, 0); + source_ptr++; +} + + +static bool add_number() +{ + unsigned char car; + const char *start; + int index; + char sign; + PATTERN last_pattern; + bool has_digit; + + start = source_ptr; + car = get_char(); + + if (car == '-' || car == '+') + { + sign = car; + car = next_char(); + + if (car == 'I' || car == 'i') + { + car = next_char(); + if (car == 'N' || car == 'n') + { + car = next_char(); + if (car == 'F' || car == 'f') + { + car = next_char(); + add_pattern(RT_RESERVED, RESERVED_find_word(start, 4)); + return FALSE; + } + } + + goto NOT_A_NUMBER; + } + } + else + sign = 0; + + if (car == '&') + { + car = toupper(next_char()); + + if (car == 'H') + goto READ_HEXA; + else if (car == 'X') + goto READ_BINARY; + else if (car == 'O') + goto READ_OCTAL; + else + { + source_ptr--; + goto READ_HEXA; + } + } + else if (car == '%') + goto READ_BINARY; + else if (isdigit(car)) + goto READ_NUMBER; + else + goto NOT_A_NUMBER; + +READ_BINARY: + + has_digit = FALSE; + for (;;) + { + car = next_char(); + if (car != '0' && car != '1') + break; + has_digit = TRUE; + } + + goto END_BINARY_HEXA; + +READ_OCTAL: + + has_digit = FALSE; + for (;;) + { + car = next_char(); + if (car < '0' || car > '7') + break; + has_digit = TRUE; + } + + goto END_BINARY_HEXA; + +READ_HEXA: + + has_digit = FALSE; + for (;;) + { + car = next_char(); + if (!isxdigit(car)) + break; + has_digit = TRUE; + } + +END_BINARY_HEXA: + + if (!has_digit) + goto NOT_A_NUMBER; + + if (car == '&') + car = next_char(); + else if (first_car[car] == GOTO_IDENT) + goto NOT_A_NUMBER; + + goto END; + +READ_NUMBER: + + while (isdigit(car)) + car = next_char(); + + if (car == '.') + { + do + { + car = next_char(); + } + while (isdigit(car)); + } + + if (toupper(car) == 'E') + { + car = next_char(); + if (car == '+' || car == '-') + car = next_char(); + + while (isdigit(car)) + car = next_char(); + } + else if (toupper(car) == 'I') + { + car = next_char(); + } + + goto END; + +END: + + last_pattern = get_last_pattern(); + + if (sign && !PATTERN_is_null(last_pattern) && (!PATTERN_is_reserved(last_pattern) || PATTERN_is(last_pattern, RS_RBRA) || PATTERN_is(last_pattern, RS_RSQR))) + { + add_pattern(RT_RESERVED, RESERVED_find_word(&sign, 1)); + TABLE_add_symbol(EVAL->table, start + 1, source_ptr - start - 1, &index); + add_pattern(RT_NUMBER, index); + } + else + { + TABLE_add_symbol(EVAL->table, start, source_ptr - start, &index); + add_pattern(RT_NUMBER, index); + } + + return FALSE; + +NOT_A_NUMBER: + + source_ptr = start; + return TRUE; +} + +#if 0 +static void add_identifier(bool no_res) +{ + unsigned char car; + const char *start; + int len; + int index; + int type; + int flag; + PATTERN last_pattern; + bool not_first; + bool can_be_reserved; + bool can_be_subr; + bool is_type; + bool last_func, last_declare, last_type, last_class; + + last_pattern = get_last_pattern(); + + if (PATTERN_is_reserved(last_pattern)) + { + flag = RES_get_ident_flag(PATTERN_index(last_pattern)); + not_first = (flag & RSF_INF) != 0; + last_func = (flag & RSF_ILF) != 0; + last_declare = (flag & RSF_ILD) != 0; + last_class = (flag & RSF_ILC) != 0; + last_type = last_class || (flag & RSF_ILT) != 0; + if (flag & RSF_ILDD) + { + PATTERN last_last_pattern = get_last_last_pattern(); + + if (PATTERN_is_reserved(last_last_pattern) && RES_get_ident_flag(PATTERN_index(last_last_pattern)) & RSF_ILD) + last_declare = TRUE; + flag &= ~RSF_ILDD; // flag == 0 means we can read a subroutine! + } + } + else + { + flag = 0; + not_first = last_func = last_declare = last_type = last_class = FALSE; + } + + type = RT_IDENTIFIER; + + start = source_ptr; + len = 1; + + if (last_type) + { + for(;;) + { + source_ptr++; + len++; + car = get_char(); + if (ident_car[car]) + continue; + if (car == '[') + { + car = get_char_offset(1); + if (car == ']') + { + source_ptr++; + len++; + TABLE_add_symbol(EVAL->table, start, len - 2, NULL, NULL); + continue; + } + } + + len--; + break; + } + } + else + { + for(;;) + { + source_ptr++; + car = get_char(); + if (!ident_car[car]) + break; + len++; + } + } + + if (no_res) + { + if (!EVAL->analyze) + { + if (get_char() == '}') + source_ptr++; + } + goto IDENTIFIER; + } + + car = get_char(); + + /*if (car == '}') + can_be_reserved = FALSE; + else + can_be_reserved = !not_first && TABLE_find_symbol(COMP_res_table, &EVAL->source[start], len, NULL, &index);*/ + + can_be_reserved = car != '}' && !not_first && !last_class; + + if (can_be_reserved) + { + index = RESERVED_find_word(start, len); + can_be_reserved = (index >= 0); + } + + if (can_be_reserved) + { + if (index == RS_ME || index == RS_NEW || index == RS_LAST || index == RS_SUPER) + { + can_be_reserved = !last_declare; + } + else if (index == RS_CLASS) + { + can_be_reserved = _begin_line && isspace(car); + } + else + { + is_type = (PATTERN_is_type(PATTERN_make(RT_RESERVED, index)) || index == RS_NEW); + + /*if (last_type) + can_be_reserved = is_type; + else if (last_func) + can_be_reserved = FALSE; + else + can_be_reserved = !is_type && (car != ':') && (car != '.') && (car != '!') && (car != '(');*/ + + if (is_type && (car == '[') && (get_char_offset(1) == ']')) + { + len += 2; + source_ptr += 2; + is_type = FALSE; + can_be_reserved = FALSE; + } + else + { + if (index == RS_NEW) + is_type = TRUE; + + if (last_type) + can_be_reserved = is_type; + else if (last_func) + can_be_reserved = FALSE; + else + can_be_reserved = !is_type && (car != ':') && (car != '.') && (car != '!') && (car != '('); + } + } + } + + can_be_subr = flag == 0 && car != '.' && car != '!'; + + if (can_be_reserved) + { + type = RT_RESERVED; + } + else if (can_be_subr && TABLE_find_symbol(COMP_subr_table, start, len, NULL, &index)) + { + type = RT_SUBR; + } + else + { + if (last_type) + type = RT_CLASS; + goto IDENTIFIER; + } + + add_pattern(type, index); + return; + +IDENTIFIER: + + if (!EVAL->analyze && PATTERN_is(last_pattern, RS_EXCL)) + { + TABLE_add_symbol(EVAL->string, start, len, NULL, &index); + type = RT_STRING; + } + else + TABLE_add_symbol(EVAL->table, start, len, NULL, &index); + + add_pattern(type, index); +} +#endif + +static void add_identifier() +{ + unsigned char car; + const char *start; + int len; + int index; + int type; + int flag; + PATTERN last_pattern, last_last_pattern; + bool not_first; + bool can_be_reserved; + bool last_identifier, last_type, last_class, last_pub; + bool exist = TRUE; + + last_pattern = get_last_pattern(); + + if (PATTERN_is_reserved(last_pattern)) + { + flag = RES_get_ident_flag(PATTERN_index(last_pattern)); + if (flag & RSF_PREV) + { + last_last_pattern = get_last_last_pattern(); + if (PATTERN_is_reserved(last_last_pattern)) + flag = RES_get_ident_flag(PATTERN_index(last_last_pattern)); + else + flag = 0; + } + } + else + flag = 0; + + type = RT_IDENTIFIER; + + start = source_ptr; + for(;;) + { + source_ptr++; + if (!ident_car[get_char()]) + break; + } + + len = source_ptr - start; + + last_class = (flag & RSF_CLASS) != 0; + last_type = (flag & RSF_AS) != 0; + + if (last_type) + { + source_ptr--; + + for(;;) + { + source_ptr++; + len++; + car = get_char(); + if (car == '[') + { + car = get_char_offset(1); + if (car == ']') + { + source_ptr++; + len++; + TABLE_add_symbol(EVAL->table, start, len - 2, &index); + continue; + } + } + + len--; + break; + } + } + + not_first = (flag & RSF_POINT) != 0; + + car = get_char(); + + //can_be_reserved = !not_first && TABLE_find_symbol(COMP_res_table, &comp->source[start], len, NULL, &index); + + can_be_reserved = !not_first && !last_class; + + if (can_be_reserved) + { + index = RESERVED_find_word(start, len); + can_be_reserved = (index >= 0); + } + + if (can_be_reserved) + { + static void *jump[] = { + &&__OTHERS, &&__ME_NEW_LAST_SUPER, &&__CLASS, &&__STRUCT, &&__SUB_PROCEDURE_FUNCTION, &&__CONST_EXTERN, &&__ENUM, &&__READ, &&__DATATYPE + }; + + last_identifier = (flag & RSF_IDENT) != 0; + last_pub = (flag & RSF_PUB) != 0; + + goto *jump[RES_get_read_switch(index)]; + + do + { + __ME_NEW_LAST_SUPER: + can_be_reserved = !last_identifier; + break; + + __CLASS: + can_be_reserved = canres_car[car] && _begin_line; + break; + + __STRUCT: + can_be_reserved = canres_car[car] && (_begin_line || last_pub || PATTERN_is(last_pattern, RS_AS) || PATTERN_is(last_pattern, RS_END) || PATTERN_is(last_pattern, RS_NEW)); + break; + + __SUB_PROCEDURE_FUNCTION: + can_be_reserved = canres_car[car] && (_begin_line || last_pub || PATTERN_is(last_pattern, RS_END)); + break; + + __CONST_EXTERN: + can_be_reserved = canres_car[car] && (_begin_line || last_pub); + break; + + __ENUM: + can_be_reserved = canres_car[car] && (_begin_line || last_pub); + break; + + __READ: + can_be_reserved = canres_car[car] && (!last_identifier || PATTERN_is(last_pattern, RS_PROPERTY)); + break; + + __DATATYPE: + if (car == '[' && get_char_offset(1) == ']') + { + len += 2; + source_ptr += 2; + can_be_reserved = FALSE; + } + else + { + if (last_type || PATTERN_is(last_pattern, RS_OPEN)) + can_be_reserved = TRUE; + else + can_be_reserved = FALSE; + } + break; + + __OTHERS: + if (last_type || last_identifier || (PATTERN_is(last_pattern, RS_LBRA) && car == ')' && PATTERN_is_reserved(get_last_last_pattern()))) + can_be_reserved = FALSE; + else + can_be_reserved = canres_car[car]; + break; + } + while (0); + } + + if (can_be_reserved) + { + type = RT_RESERVED; + goto __ADD_PATTERN; + } + + if ((flag == 0) && car != '.' && car != '!') + { + index = RESERVED_find_subr(start, len); + if (index >= 0) + { + if (COMP_subr_info[index].min_param == 0 || car == '(') + { + type = RT_SUBR; + + if (EVAL->custom) + { + GB_FUNCTION func; + GB_VALUE *ret; + + if (!GB.GetFunction(&func, (void *)GB.GetClass(EVAL->parent), "IsSubr", NULL, NULL)) + { + GB.Push(1, GB_T_STRING, start, len); + ret = GB.Call(&func, 1, FALSE); + if (!ret->_boolean.value) + { + exist = TABLE_add_symbol(EVAL->table, start, len, &index); + type = RT_IDENTIFIER; + } + } + } + + goto __ADD_PATTERN; + } + } + } + + if (last_type) + type = RT_CLASS; + + /*if (flag & RSF_EVENT) + { + start--; + len++; + *((char *)start) = ':'; + }*/ + + if (!EVAL->analyze && PATTERN_is(last_pattern, RS_EXCL)) + { + TABLE_add_symbol(EVAL->string, start, len, &index); + type = RT_STRING; + } + else + exist = TABLE_add_symbol(EVAL->table, start, len, &index); + +__ADD_PATTERN: + + if (EVAL->custom && !exist) + { + GB_FUNCTION func; + GB_VALUE *ret; + + if (!GB.GetFunction(&func, (void *)GB.GetClass(EVAL->parent), "IsIdentifier", NULL, NULL)) + { + GB.Push(1, GB_T_STRING, start, len); + ret = GB.Call(&func, 1, FALSE); + if (!ret->_boolean.value) + THROW("Unknown symbol"); + } + } + + add_pattern(type, index); +} + + +static void add_quoted_identifier(void) +{ + unsigned char car; + const char *start; + int len; + int index; + int type; + PATTERN last_pattern; + + last_pattern = get_last_pattern(); + + type = RT_IDENTIFIER; + + start = source_ptr; + len = 1; + + for(;;) + { + source_ptr++; + car = get_char(); + len++; + if (!ident_car[car]) + break; + } + + source_ptr++; + + if (!EVAL->analyze) + { + if (car != '}') + THROW("Missing '}'"); + + if (len == 2) + THROW("Void identifier"); + } + else + { + if (!car) + len--; + } + + if (!EVAL->analyze && PATTERN_is(last_pattern, RS_EXCL)) + { + TABLE_add_symbol(EVAL->string, start + 1, len - 2, &index); + type = RT_STRING; + } + else + { + if (!EVAL->rewrite) + { + start++; + len -= 2; + } + TABLE_add_symbol(EVAL->table, start, len, &index); + } + + add_pattern(type, index); +} + + + +static void add_operator() +{ + unsigned char car; + const char *start; + const char *end; + int len; + int op = NO_SYMBOL; + int index; + + start = source_ptr; + end = start; + len = 1; + + for(;;) + { + source_ptr++; + + index = RESERVED_find_word(start, len); + if (index >= 0) + { + op = index; + end = source_ptr; + } + + car = get_char(); + //if (!isascii(car) || !ispunct(car)) + if (noop_car[car]) + break; + len++; + } + + source_ptr = end; + + if (EVAL->analyze && op == RS_QUES) + op = RS_PRINT; + + if (op < 0) + THROW("Unknown operator"); + + add_pattern(RT_RESERVED, op); +} + + +static int xdigit_val(unsigned char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + else if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + else if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + else + return (-1); +} + + +static void add_string() +{ + unsigned char car; + const char *start; + int len; + int index; + ushort newline; + bool jump; + char *p; + int i; + + start = source_ptr; + len = 0; + newline = 0; + jump = FALSE; + p = (char *)source_ptr; + + //fprintf(stderr, "EVAL_read: add_string: [%d] %s\n", source_ptr - EVAL->source, source_ptr); + + for(;;) + { + source_ptr++; + car = get_char(); + + //fprintf(stderr, "EVAL_read: car = %d jump = %d\n", car, jump); + + if (jump) + { + if (car == '\n') + newline++; + else if (car == '"') + jump = FALSE; + else if (!car || !isspace(car)) + break; + } + else + { + p++; + len++; + + if (!car || car == '\n') + THROW("Non terminated string"); + + if (car == '\\') + { + source_ptr++; + car = get_char(); + + if (car == 'n') + *p = '\n'; + else if (car == 't') + *p = '\t'; + else if (car == 'r') + *p = '\r'; + else if (car == 'b') + *p = '\b'; + else if (car == 'v') + *p = '\v'; + else if (car == 'f') + *p = '\f'; + else if (car == 'e') + *p = '\x1B'; + else if (car == '0') + *p = 0; + else if (car == '\"' || car == '\'' || car == '\\') + *p = car; + else + { + if (car == 'x') + { + i = xdigit_val(get_char_offset(1)); + if (i >= 0) + { + car = i; + i = xdigit_val(get_char_offset(2)); + if (i >= 0) + { + car = (car << 4) | (uchar)i; + *p = car; + source_ptr += 2; + continue; + } + } + } + + THROW("Bad character constant in string"); + } + } + else if (car == '"') + { + p--; + len--; + jump = TRUE; + } + else + *p = car; + } + } + + p[1] = 0; + + //fprintf(stderr, "EVAL_read: add_string (end): [%d] %s\n", source_ptr - EVAL->source, source_ptr); + if (len > 0) + { + TABLE_add_symbol(EVAL->string, start + 1, len, &index); + add_pattern(RT_STRING, index); + } + else + add_pattern(RT_STRING, VOID_STRING_INDEX); + + for (i = 0; i < newline; i++) + add_newline(); + + source_ptr -= newline; +} + +#if ENABLE_BIG_COMMENT +static void add_big_comment() +{ + unsigned char car; + const char *start; + int len; + int index; + int type; + //bool space = FALSE; + + start = source_ptr; + len = 0; + EVAL->comment = TRUE; + + /*for(;;) + { + if (start == EVAL->source) + break; + + start--; + car = *start; + if (car == '\n') + break; + + if (car > ' ') + { + start++; + space = TRUE; + break; + } + len++; + } + + if (!space) + { + start = source_ptr; + len = 1; + }*/ + + for(;;) + { + car = get_char(); + if (car == 0) + break; + + /*if (car == '\n') + { + TABLE_add_symbol(EVAL->string, start, len, NULL, &index); + type = RT_COMMENT; + add_pattern(type, index); + add_newline(); + len = 1; + start = source_ptr + 1; + continue; + }*/ + + if (car == '*' && get_char_offset(1) == '/') + { + source_ptr += 2; + len += 2; + EVAL->comment = FALSE; + break; + } + len++; + source_ptr++; + } + + TABLE_add_symbol(EVAL->string, start, len, &index); + type = RT_COMMENT; + add_pattern(type, index); +} +#endif + +static void add_comment() +{ + unsigned char car; + const char *start; + int len; + int index; + int type; + bool space = FALSE; + + start = source_ptr; + len = 1; + + for(;;) + { + if (start == EVAL->source) + break; + + start--; + car = *start; + if (car == '\n') + break; + + if (car > ' ') + { + start++; + space = TRUE; + break; + } + len++; + } + + if (!space) + { + start = source_ptr; + len = 1; + } + + for(;;) + { + source_ptr++; + car = get_char(); + if (car == 0 || car == '\n') + break; + len++; + } + + TABLE_add_symbol(EVAL->string, start, len, &index); + type = RT_COMMENT; + + add_pattern(type, index); +} + + +static void add_string_for_analyze() +{ + unsigned char car; + const char *start; + int len; + int index; + int type; + + start = source_ptr; + len = 0; + + for(;;) + { + source_ptr++; + car = get_char(); + if (car == '\\') + { + source_ptr++; + car = get_char(); + len++; + if (car == 0) + break; + } + else if (car == 0 || car == '\n' || car == '"') + break; + len++; + } + + if (car == '"') + source_ptr++; + + TABLE_add_symbol(EVAL->string, start + 1, len, &index); + type = RT_STRING; + + add_pattern(type, index); + //fprintf(stderr, "add_string_for_analyze: %s\n", TABLE_get_symbol_name(EVAL->string, index)); +} + + +PUBLIC void EVAL_read(void) +{ + static void *jump_char[10] = + { + &&__BREAK, + &&__SPACE, + &&__COMMENT, + &&__STRING, + &&__IDENT, + &&__QUOTED_IDENT, + &&__NUMBER, + &&__ERROR, + &&__SHARP, + &&__OTHER + }; + + unsigned char car; + + READ_init(); + + source_ptr = EVAL->source; + _begin_line = TRUE; + +#if ENABLE_BIG_COMMENT + if (EVAL->comment) + goto __BIG_COMMENT; +#endif + + for(;;) + { + car = get_char(); + goto *jump_char[(int)first_car[car]]; + + __ERROR: + + THROW(E_SYNTAX); + + __SPACE: + + source_ptr++; + if (car == '\n') + { + add_newline(); + _begin_line = TRUE; + } + continue; + + __COMMENT: + + if (EVAL->analyze) + { + add_comment(); + } + else + { + do + { + source_ptr++; + car = get_char(); + } + while (car != '\n' && car != 0); + } + + _begin_line = FALSE; + continue; + + __STRING: + + if (EVAL->analyze) + add_string_for_analyze(); + else + add_string(); + _begin_line = FALSE; + continue; + + __IDENT: + + add_identifier(); + _begin_line = FALSE; + continue; + + __QUOTED_IDENT: + + add_quoted_identifier(); + _begin_line = FALSE; + continue; + + __NUMBER: + + add_number(); + _begin_line = FALSE; + continue; + + __SHARP: + + if (_begin_line) + { + _begin_line = FALSE; + if (get_char_offset(1) == '!' && EVAL->analyze) + { + add_comment(); + continue; + } + } + + __OTHER: + +#if ENABLE_BIG_COMMENT + if (car == '/' && get_char_offset(1) == '*') + goto __BIG_COMMENT; +#endif + + if (add_number()) + add_operator(); + + _begin_line = FALSE; + continue; + +#if ENABLE_BIG_COMMENT + __BIG_COMMENT: + + if (EVAL->analyze) + add_big_comment(); + else + { + for(;;) + { + car = get_char(); + if (car == 0) + break; + if (car == '*' && get_char_offset(1) == '/') + { + source_ptr += 2; + break; + } + if (car == '\n') + add_newline(); + source_ptr++; + } + } + + _begin_line = FALSE; + continue; +#endif + } + +__BREAK: + + add_end(); + add_end(); + add_end(); + add_end(); +} + diff --git a/main/lib/eval/eval_read.h b/main/lib/eval/eval_read.h new file mode 100644 index 00000000..842e4689 --- /dev/null +++ b/main/lib/eval/eval_read.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + eval_read.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __EVAL_READ_H +#define __EVAL_READ_H + +#include "gbc_read_common.h" + +#ifndef __EVAL_READ_C +EXTERN const char *READ_source_ptr; +#endif + +PUBLIC void EVAL_read(void); +PUBLIC char *READ_get_pattern(PATTERN *pattern); + +#endif diff --git a/main/lib/eval/eval_reserved.c b/main/lib/eval/eval_reserved.c new file mode 100644 index 00000000..ae676fbd --- /dev/null +++ b/main/lib/eval/eval_reserved.c @@ -0,0 +1,26 @@ +/*************************************************************************** + + eval_reserved.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +//#define __EVAL_RESERVED_C + +#include "gb_reserved_temp.h" diff --git a/main/lib/eval/eval_trans.c b/main/lib/eval/eval_trans.c new file mode 100644 index 00000000..ec8a9fe6 --- /dev/null +++ b/main/lib/eval/eval_trans.c @@ -0,0 +1,458 @@ +/*************************************************************************** + + eval_trans.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define _TRANS_C + +#include +#include + +#include "gb_common.h" +#include "gb_error.h" + +#include "gb_reserved.h" +#include "eval_read.h" +#include "eval_trans.h" +#include "eval.h" + +/* +PUBLIC void TRANS_reset(void) +{ + JOB->line = 1; + JOB->current = JOB->pattern; + JOB->end = &(JOB->pattern[ARRAY_count(JOB->pattern)]); +} + + +PUBLIC boolean TRANS_newline(void) +{ + if (PATTERN_IS_NEWLINE(*JOB->current)) + { + JOB->line = PATTERN_INDEX(*JOB->current) + 1; + JOB->current++; + return TRUE; + } + + return FALSE; +} +*/ + +#if 0 +PUBLIC boolean TRANS_get_number(int index, TRANS_NUMBER *result) +{ + char car; + int val; + double dval; + char *end; + int pos; + + int base = 0; + char *number = TABLE_get_symbol_name(EVAL->table, index); + boolean minus = FALSE; + boolean is_unsigned = FALSE; + + car = *number; + + if (car == '+' || car == '-') + { + minus = (car == '-'); + car = *(++number); + } + + if (car == '&') + { + car = *(++number); + car = toupper(car); + + if (car == 'H') + { + base = 16; + car = *(++number); + } + else if (car == 'X') + { + base = 2; + car = *(++number); + } + else + base = 16; + } + else if (car == '%') + { + base = 2; + car = *(++number); + } + + if (!car) + return TRUE; + + if (car == '-' || car == '+') + return TRUE; + + if (car == '0' && toupper(number[1]) == 'X') + return TRUE; + + pos = strlen(number) - 1; + if (number[pos] == '&') + { + number[pos] = 0; + is_unsigned = TRUE; + } + + errno = 0; + + if (base) + { + val = strtol(number, &end, base); + + if (!is_unsigned && val >= 0x8000L && val <= 0xFFFFL) + val |= 0xFFFF0000; + } + else + { + base = 10; + val = strtol(number, &end, base); + if (errno || *end) + { + if (is_unsigned) + return TRUE; + + errno = 0; + base = 0; + dval = strtod(number, &end); + } + } + + if (*end || errno) + return TRUE; + + if (!base) + { + result->type = T_FLOAT; + result->dval = minus ? (-dval) : dval; + } + else + { + result->type = T_INTEGER; + result->ival = minus ? (-val) : val; + } + + return FALSE; +} +#endif + +bool TRANS_get_number(int index, TRANS_NUMBER *result) +{ + GB_VALUE value; + SYMBOL *sym = TABLE_get_symbol(EVAL->table, index); + int len = sym->len; + + if (len > 0 && tolower(sym->name[len - 1]) == 'i') + { + len--; + result->complex = TRUE; + } + else + result->complex = FALSE; + + if (GB.NumberFromString(GB_NB_READ_ALL | GB_NB_READ_HEX_BIN, sym->name, len, &value)) + return TRUE; + + if (value.type == T_INTEGER) + { + result->type = T_INTEGER; + result->ival = ((GB_INTEGER *)(void *)&value)->value; + } + else if (value.type == T_LONG) + { + result->type = T_LONG; + result->lval = ((GB_LONG *)(void *)&value)->value; + } + else + { + result->type = T_FLOAT; + result->dval = ((GB_FLOAT *)(void *)&value)->value; + } + + return FALSE; +} + +#if 0 +static PATTERN *trans_square(PATTERN *look, int mode, TRANS_DECL *result) +{ + TRANS_NUMBER tnum; + int i; + + if (!(mode & TT_CAN_SQUARE)) + { + if (PATTERN_is(*look, RS_LSQR)) + THROW("Arrays are forbidden here"); + return look; + } + + if (!PATTERN_IS(*look, RS_LSQR)) + return look; + + /* + if (result->is_array) + THROW("Syntax error. Duplicated array declaration"); + */ + + look++; + + /* + if (PATTERN_IS(*look, RS_RSQR)) + { + look++; + return look; + } + */ + + if (mode && TT_CAN_ARRAY) + { + for (i = 0;; i++) + { + if (i > MAX_ARRAY_DIM) + THROW("Too many dimensions"); + + if (TRANS_get_number(PATTERN_INDEX(*look), &tnum)) + THROW(E_SYNTAX); + if (tnum.type != T_INTEGER) + THROW(E_SYNTAX); + if (tnum.ival < 1 || tnum.ival > (2 << 22)) /* 4 Mo, ca devrait suffire... ;-) */ + THROW("Bad subscript range"); + + result->array.dim[i] = tnum.ival; + result->array.ndim++; + look++; + + if (PATTERN_is(*look, RS_RSQR)) + break; + + if (!PATTERN_is(*look, RS_COMMA)) + THROW("Missing comma"); + look++; + } + } + + if (!PATTERN_IS(*look, RS_RSQR)) + THROW("Missing ']'"); + + look++; + return look; +} +#endif + +#if 0 +PUBLIC boolean TRANS_type(int mode, TRANS_DECL *result) +{ + PATTERN *look = JOB->current; + short id = 0; + int value = -1L; + int flag = 0; + + /* Ne pas remplir la structure de z�os */ + + /* Attention ! Probl�e du tableau d'objet ! */ + + TYPE_clear(&result->type); + result->is_new = FALSE; + result->array.ndim = 0; + + look = trans_square(look, mode, result); + + if (!PATTERN_IS(*look, RS_AS)) + { + if (mode & TT_DO_NOT_CHECK_AS) + return FALSE; + else + THROW("Missing AS"); + } + + look++; + + if (mode & TT_CAN_NEW) + { + if (PATTERN_IS(*look, RS_NEW)) + { + if (TYPE_get_id(result->type) == T_ARRAY) + THROW("Cannot mix NEW and array declaration"); + + result->is_new = TRUE; + look++; + } + } + + if (PATTERN_IS_TYPE(*look)) + { + id = RES_get_type(PATTERN_index(*look)); + if (id == T_OBJECT) + value = (-1); + look++; + } + else if (PATTERN_IS_IDENTIFIER(*look)) + { + id = T_OBJECT; + value = CLASS_add_class(JOB->class, PATTERN_INDEX(*look)); + look++; + } + else + THROW(E_SYNTAX); + + /*look = trans_square(look, mode, result);*/ + + if (id == T_VOID) + return FALSE; + + /* + if (result->is_array && result->array.ndim == 0) + result->is_array = FALSE; + */ + + if (result->array.ndim > 0) + { + result->array.type = TYPE_make(id, value, flag); + result->type = TYPE_make(T_ARRAY, CLASS_add_array(JOB->class, &result->array), 0); + } + else + result->type = TYPE_make(id, value, flag); + + JOB->current = look; + return TRUE; +} +#endif + +#if 0 +PUBLIC boolean TRANS_check_declaration(void) +{ + PATTERN *look = JOB->current; + + if (!PATTERN_is_identifier(*look)) + return FALSE; + look++; + + if (PATTERN_is(*look, RS_LSQR)) + { + for(;;) + { + look++; + if (PATTERN_is(*look, RS_RSQR)) + break; + if (PATTERN_is_newline(*look)) + return FALSE; + } + look++; + } + + if (!PATTERN_is(*look, RS_AS)) + return FALSE; + + return TRUE; +} +#endif + +#if 0 +PUBLIC void TRANS_get_constant_value(TRANS_DECL *decl, PATTERN value) +{ + int index; + TRANS_NUMBER number; + int type; + + index = PATTERN_index(value); + + /* V�ification de la constante */ + + type = TYPE_get_id(decl->type); + + switch(type) + { + case T_BOOLEAN: + + decl->is_integer = TRUE; + + if (PATTERN_is(value, RS_TRUE)) + decl->value = -1L; + else if (PATTERN_is(value, RS_FALSE)) + decl->value = 0L; + else + THROW("Type mismatch"); + + break; + + case T_BYTE: case T_SHORT: case T_INTEGER: + + decl->is_integer = TRUE; + + if (TRANS_get_number(index, &number)) + THROW("Type mismatch"); + + if (number.type != T_INTEGER) + THROW("Type mismatch"); + + if (((type == T_BYTE) && (number.ival < 0 || number.ival > 255)) + || ((type == T_SHORT) && (number.ival < -32768L || number.ival > 32767L))) + THROW("Out of range"); + + decl->value = number.ival; + break; + + case T_STRING: case T_FLOAT: + + decl->is_integer = FALSE; + decl->value = index; + break; + + default: + + THROW("Bad constant type"); + } +} +#endif + +#if 0 +PUBLIC void TRANS_want(int reserved) +{ + if (!PATTERN_is(*JOB->current, reserved)) + THROW("Syntax error. %s expected", COMP_res_info[reserved].name); + JOB->current++; +} + + +PUBLIC boolean TRANS_is(int reserved) +{ + if (PATTERN_is(*JOB->current, reserved)) + { + JOB->current++; + return TRUE; + } + else + return FALSE; +} + +PUBLIC void TRANS_ignore(int reserved) +{ + if (PATTERN_is(*JOB->current, reserved)) + JOB->current++; +} + +#endif + diff --git a/main/lib/eval/eval_trans.h b/main/lib/eval/eval_trans.h new file mode 100644 index 00000000..0246922c --- /dev/null +++ b/main/lib/eval/eval_trans.h @@ -0,0 +1,73 @@ +/*************************************************************************** + + eval_trans.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __EVAL_TRANS_H +#define __EVAL_TRANS_H + +#include "gb_type_common.h" +#include "gb_reserved.h" +#include "eval_read.h" +#include "gb_limit.h" +#include "../../gbx/gbx_class.h" + +#include "gbc_trans_common.h" + +/* +enum { + TT_NOTHING = 0, + TT_DO_NOT_CHECK_AS = 1, + TT_CAN_SQUARE = 2, + TT_CAN_ARRAY = 4, + TT_CAN_NEW = 8 + }; +*/ + +/* +void TRANS_reset(void); +boolean TRANS_newline(void); +boolean TRANS_type(int flag, TRANS_DECL *result); +boolean TRANS_check_declaration(void); +void TRANS_get_constant_value(TRANS_DECL *decl, PATTERN value); + +void TRANS_want(int reserved); +boolean TRANS_is(int reserved); +void TRANS_ignore(int reserved); +*/ + +bool TRANS_get_number(int index, TRANS_NUMBER *result); + +/* eval_trans_expr.c */ + +void TRANS_expression(void); +bool TRANS_affectation(void); +void TRANS_operation(short op, short nparam, PATTERN previous); + +/* eval_trans_tree.c */ + +#define RS_UNARY (-1) + +void TRANS_tree(void); +/*boolean TRANS_is_statement(TRANS_TREE *tree);*/ + +#endif + diff --git a/main/lib/eval/eval_trans_expr.c b/main/lib/eval/eval_trans_expr.c new file mode 100644 index 00000000..d4025428 --- /dev/null +++ b/main/lib/eval/eval_trans_expr.c @@ -0,0 +1,618 @@ +/*************************************************************************** + + eval_trans_expr.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __EVAL_TRANS_EXPR_C + +#define PROJECT_EXEC + +#include "gb_common.h" +#include + +#include "gb_error.h" +#include "gb_reserved.h" + +#include "gb_code.h" +#include "eval_trans.h" +#include "eval.h" + +/*#define DEBUG*/ + +static int subr_array_index = -1; +static int subr_collection_index = -1; + +static void find_subr(int *index, const char *name) +{ + if (*index < 0) + *index = RESERVED_find_subr(name, strlen(name)); +} + +static short get_nparam(PATTERN *tree, int *index) +{ + PATTERN pattern; + + if (*index < (ARRAY_count(tree) - 1)) + { + pattern = tree[*index + 1]; + if (PATTERN_is_param(pattern)) + { + (*index)++; + return (short)PATTERN_index(pattern); + } + } + + /* + G�e le cas o on a cod�un subr sans mettre de parenth�es + => nparam = 0 + */ + + return 0; +} + + +static void push_number(int index) +{ + TRANS_NUMBER number; + CLASS_CONST cst; + //SYMBOL *sym; + + if (TRANS_get_number(index, &number)) + THROW(E_SYNTAX); + + if (number.type == T_INTEGER) + { + CODE_push_number(number.ival); + } + else + { + cst.type = number.type; + if (cst.type == T_FLOAT) + cst._float.value = number.dval; + else if (cst.type == T_LONG) + cst._long.value = number.lval; + + CODE_push_const(EVAL_add_constant(&cst)); + } + + if (number.complex) + CODE_push_complex(); +} + + +static void push_string(int index, bool trans) +{ + CLASS_CONST cst; + SYMBOL *sym; + int len; + + if (index == VOID_STRING_INDEX) + len = 0; + else + { + sym = TABLE_get_symbol(EVAL->string, index); + len = sym->len; + } + + if (len == 0) + { + CODE_push_void_string(); + } + else if (len == 1) + { + CODE_push_char(*(sym->name)); + } + else + { + cst.type = trans ? T_CSTRING : T_STRING; + cst._string.addr = sym->name; + cst._string.len = len; + + CODE_push_const(EVAL_add_constant(&cst)); + } +} + + +/* +static void push_class(int index) +{ + TRANS_DECL decl; + + decl.type = TYPE_make(T_STRING, 0, 0); + decl.index = NO_SYMBOL; + decl.value = index; + CODE_push_class(CLASS_add_constant(EVAL->class, &decl)); +} +*/ + + +static void trans_class(int index) +{ + SYMBOL *sym = TABLE_get_symbol(EVAL->table, index); + + if (GB.ExistClassLocal(sym->name)) + CODE_push_class(EVAL_add_class(sym->name)); + else + { + THROW("Unknown class"); + } +} + + +static void trans_identifier(int index, bool first, bool point) +{ + SYMBOL *sym = TABLE_get_symbol(EVAL->table, index); + + if (sym->name[sym->len]) + sym->name[sym->len] = 0; + + if (point) + { + CODE_push_unknown(EVAL_add_unknown(sym->name)); + } + else if (first && GB.ExistClassLocal(sym->name)) + { + //printf("%.*s %s\n", sym->symbol.len, sym->symbol.name, isupper(*sym->symbol.name) ? "U" : "l"); + CODE_push_class(EVAL_add_class(sym->name)); + } + else + { + CODE_push_local(EVAL_add_variable(index)); + } +} + + +static void trans_subr(int subr, short nparam) +{ + SUBR_INFO *info = &COMP_subr_info[subr]; + + //fprintf(stderr, "trans_subr: %d: %s: %d %d\n", subr, info->name, info->min_param, info->max_param); + + if (nparam < info->min_param) + THROW2("Not enough arguments to &1()", info->name); + else if (nparam > info->max_param) + THROW2("Too many arguments to &1()", info->name); + + CODE_subr(info->opcode, nparam, info->optype, info->max_param == info->min_param); +} + + +void TRANS_operation(short op, short nparam, PATTERN previous) +{ + COMP_INFO *info = &COMP_res_info[op]; + + switch (info->value) + { + case OP_PT: + + /*if (nparam == 0) + TRANS_use_with();*/ + if (!PATTERN_is_identifier(previous)) + THROW(E_SYNTAX); + break; + + case OP_EXCL: + if (!PATTERN_is_identifier(previous)) + THROW(E_SYNTAX); + break; + + case OP_LSQR: + CODE_push_array(nparam); + break; + + case OP_RSQR: + find_subr(&subr_array_index, ".Array"); + if (nparam > MAX_PARAM_OP) + CODE_subr(COMP_subr_info[subr_array_index].opcode, MAX_PARAM_OP + 1, CODE_CALL_VARIANT + MAX_PARAM_OP, FALSE); + else + trans_subr(subr_array_index, nparam); + break; + + case OP_COLON: + find_subr(&subr_collection_index, ".Collection"); + if (nparam > MAX_PARAM_OP) + CODE_subr(COMP_subr_info[subr_collection_index].opcode, MAX_PARAM_OP, CODE_CALL_VARIANT + MAX_PARAM_OP - 1, FALSE); + else + trans_subr(subr_collection_index, nparam); + break; + + case OP_LBRA: + CODE_call(nparam); + break; + + case OP_MINUS: + if (nparam == 1) + CODE_op(C_NEG, 0, nparam, TRUE); + else + CODE_op(info->code, info->subcode, nparam, TRUE); + break; + + default: + + CODE_op(info->code, info->subcode, nparam, (info->flag != RSF_OPN)); + } +} + + +static void trans_expr_from_tree(PATTERN *tree) +{ + int i; + short nparam; + int count; + PATTERN pattern, prev_pattern; //, next_pattern + + count = ARRAY_count(tree) - 1; + pattern = NULL_PATTERN; + + for (i = 0; i <= count; i++) + { + prev_pattern = pattern; + pattern = tree[i]; + /*if (i < count) + next_pattern = tree[i + 1]; + else + next_pattern = NULL_PATTERN;*/ + + if (PATTERN_is_number(pattern)) + push_number(PATTERN_index(pattern)); + + else if (PATTERN_is_string(pattern)) + push_string(PATTERN_index(pattern), FALSE); + + else if (PATTERN_is_tstring(pattern)) + push_string(PATTERN_index(pattern), TRUE); + + else if (PATTERN_is_identifier(pattern)) + trans_identifier(PATTERN_index(pattern), PATTERN_is_first(pattern), PATTERN_is_point(pattern)); + + else if (PATTERN_is_class(pattern)) + trans_class(PATTERN_index(pattern)); + + else if (PATTERN_is_subr(pattern)) + { + nparam = get_nparam(tree, &i); + trans_subr(PATTERN_index(pattern), nparam); + } + + else if (PATTERN_is_reserved(pattern)) + { + if (PATTERN_is(pattern, RS_TRUE)) + { + CODE_push_boolean(TRUE); + } + else if (PATTERN_is(pattern, RS_FALSE)) + { + CODE_push_boolean(FALSE); + } + else if (PATTERN_is(pattern, RS_NULL)) + { + CODE_push_null(); + } + else if (PATTERN_is(pattern, RS_ME)) + { + /*if (FUNCTION_is_static(EVAL->func)) + THROW("ME cannot be used in a static function");*/ + + CODE_push_me(TRUE); + } + else if (PATTERN_is(pattern, RS_SUPER)) + { + /*if (FUNCTION_is_static(EVAL->func)) + THROW("ME cannot be used in a static function");*/ + + CODE_push_super(TRUE); + } + else if (PATTERN_is(pattern, RS_LAST)) + { + CODE_push_last(); + } + /* + else if (PATTERN_is(pattern, RS_AT)) + { + if (!CODE_popify_last()) + THROW("Invalid output parameter"); + } + */ + else if (PATTERN_is(pattern, RS_COMMA)) + { + CODE_drop(); + } + /*else if (PATTERN_is(pattern, RS_ERROR)) + { + TRANS_subr(TS_SUBR_ERROR, 0); + }*/ + else if (PATTERN_is(pattern, RS_OPTIONAL)) + { + CODE_push_void(); + } + else if (PATTERN_is(pattern, RS_PINF)) + { + CODE_push_inf(FALSE); + } + else if (PATTERN_is(pattern, RS_MINF)) + { + CODE_push_inf(TRUE); + } + else + { + nparam = get_nparam(tree, &i); + TRANS_operation((short)PATTERN_index(pattern), nparam, prev_pattern); + } + } + } +} + +#if 0 +static void trans_new(void) +{ + int index; + int i, nparam; + bool array = FALSE; + bool event = FALSE; + bool collection = FALSE; + + if (PATTERN_is_identifier(*EVAL->current)) + { + index = PATTERN_index(*EVAL->current); + CODE_push_class(CLASS_add_class(EVAL->class, index)); + nparam = 1; + } + else if (PATTERN_is_type(*EVAL->current)) + { + if (PATTERN_is(EVAL->current[1], RS_LSQR)) + { + CODE_push_number(RES_get_type(PATTERN_index(*EVAL->current))); + nparam = 1; + } + else + THROW("Cannot instanciate native types"); + } + + EVAL->current++; + + if (PATTERN_is(*EVAL->current, RS_LSQR)) + { + if (collection) + THROW("Array declaration is forbidden with typed collection"); + + EVAL->current++; + + for (i = 0;; i++) + { + if (i > MAX_ARRAY_DIM) + THROW("Too many dimensions"); + + TRANS_expression(FALSE); + nparam++; + + if (PATTERN_is(*EVAL->current, RS_RSQR)) + break; + + if (!PATTERN_is(*EVAL->current, RS_COMMA)) + THROW("Comma missing"); + + EVAL->current++; + } + + EVAL->current++; + array = TRUE; + } + else + { + if (PATTERN_is(*EVAL->current, RS_LBRA)) + { + EVAL->current++; + + for(;;) + { + if (nparam > MAX_PARAM_FUNC) + THROW("Too many arguments"); + + if (PATTERN_is(*EVAL->current, RS_AT)) + THROW("NEW cannot have output parameters"); + + TRANS_expression(FALSE); + nparam++; + + if (PATTERN_is(*EVAL->current, RS_RBRA)) + break; + + if (!PATTERN_is(*EVAL->current, RS_COMMA)) + THROW("Comma missing"); + + EVAL->current++; + } + + EVAL->current++; + } + + if (PATTERN_is(*EVAL->current, RS_AS)) + { + EVAL->current++; + TRANS_expression(FALSE); + nparam++; + event = TRUE; + } + + /* + CODE_call(nparam, FALSE); + CODE_drop(); + */ + } + + if (collection) + CODE_new(nparam, TRUE, event); + else + CODE_new(nparam, array, event); +} +#endif + +void TRANS_expression() +{ + TRANS_tree(); + + trans_expr_from_tree(EVAL->tree); + + ARRAY_delete(&EVAL->tree); +} + + +void TRANS_reference(void) +{ + TRANS_expression(); + + if (!CODE_popify_last()) + THROW("Invalid assignment"); + + EVAL->assign_code = EVAL->code[EVAL->ncode - 1]; +} + +static void trans_operation(short op, short nparam, PATTERN previous) +{ + COMP_INFO *info = &COMP_res_info[op]; + CODE_op(info->code, info->subcode, nparam, (info->flag != RSF_OPN)); +} + +bool TRANS_affectation(void) +{ + /*static TRANS_STATEMENT statement[] = { + //{ RS_NEW, TRANS_new }, + { RS_OPEN, TRANS_open }, + { RS_SHELL, TRANS_shell }, + { RS_EXEC, TRANS_exec }, + { RS_RAISE, TRANS_raise }, + { RS_PIPE, TRANS_pipe }, + { RS_LOCK, TRANS_lock }, + { RS_NONE, NULL } + };*/ + + //TRANS_STATEMENT *st; + PATTERN *look = EVAL->current; + PATTERN *left, *expr, *after; + int niv = 0; + bool equal = FALSE; + //bool stat = FALSE; + int op; + + for(;;) + { + if (PATTERN_is_newline(*look) || PATTERN_is_end(*look)) + break; + + if (PATTERN_is(*look, RS_LBRA) || PATTERN_is(*look, RS_LSQR)) + { + niv++; + } + else if (PATTERN_is(*look, RS_RBRA) || PATTERN_is(*look, RS_RSQR)) + { + if (niv > 0) + niv--; + } + else if (niv == 0) + { + if (PATTERN_is(*look, RS_EQUAL)) + { + equal = TRUE; + op = RS_NONE; + break; + } + else if (PATTERN_is_reserved(*look) && RES_is_assignment(PATTERN_index(*look))) + { + equal = TRUE; + op = RES_get_assignment_operator(PATTERN_index(*look)); + break; + } + } + + look++; + } + + if (!equal) + return FALSE; + + left = EVAL->current; + *look++ = PATTERN_make(RT_NEWLINE, 0); + expr = look; + + EVAL->current = expr; + + /*if (op == RS_NONE && (PATTERN_is_reserved(*EVAL->current))) + { + if (PATTERN_is(*EVAL->current, RS_NEW)) + { + EVAL->current++; + TRANS_in_affectation++; + TRANS_new(); + TRANS_in_affectation--; + stat = TRUE; + } + else + { + for (st = statement; st->id; st++) + { + if (PATTERN_is(*EVAL->current, st->id)) + { + EVAL->current++; + TRANS_in_affectation++; + (*st->func)(); + TRANS_in_affectation--; + stat = TRUE; + } + } + } + }*/ + + //if (!stat) + //{ + if (op != RS_NONE) + { + EVAL->current = left; + TRANS_expression(); + } + + EVAL->current = expr; + TRANS_expression(); + after = EVAL->current; + + if (op != RS_NONE) + { + /*if (op == RS_AMP) + CODE_string_add();*/ + trans_operation(op, 2, NULL_PATTERN); + } + //} + + after = EVAL->current; + + /*if (dup) + CODE_dup();*/ + + CODE_dup(); // So that Assign() returns the assigned value + + EVAL->current = left; + TRANS_reference(); + + EVAL->current = after; + + return TRUE; +} + + diff --git a/main/lib/eval/eval_trans_tree.c b/main/lib/eval/eval_trans_tree.c new file mode 100644 index 00000000..c92caa3f --- /dev/null +++ b/main/lib/eval/eval_trans_tree.c @@ -0,0 +1,713 @@ +/*************************************************************************** + + eval_trans_tree.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __TRANS_TREE_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_reserved.h" + +#include "eval_trans.h" +#include "eval_read.h" +#include "eval.h" + +//#define DEBUG + +static short level; +static PATTERN *current; +/*PRIVATE PATTERN last_pattern;*/ + +static void analyze_expr(short priority, short op_main); + + +static void inc_level() +{ + level++; + if (level > MAX_EXPR_LEVEL) + THROW("Expression too complex"); +} + + +static void dec_level() +{ + level--; +} + + +static void add_pattern(PATTERN pattern) +{ + PATTERN *node; + short index; + + index = (short)ARRAY_count(EVAL->tree); + + if (index >= MAX_EXPR_PATTERN) + THROW("Expression too complex"); + + node = ARRAY_add(&EVAL->tree); + *node = pattern; + /*last_pattern = pattern;*/ + + + /*#ifdef DEBUG + READ_dump_pattern(&pattern); + #endif*/ +} + + +static void remove_last_pattern() +{ + if (!ARRAY_count(EVAL->tree)) + return; + + ARRAY_remove_last(&EVAL->tree); +} + + +static PATTERN get_last_pattern(int dep) +{ + short index; + + index = (short)ARRAY_count(EVAL->tree); + if (index < dep) + return NULL_PATTERN; + + return EVAL->tree[index - dep]; +} + + +static void change_last_pattern(int dep, PATTERN pattern) +{ + short index; + + index = (short)ARRAY_count(EVAL->tree); + if (index < dep) + return; + + EVAL->tree[index - dep] = pattern; +} + + +static void check_last_first(int dep) +{ + if (PATTERN_is_identifier(get_last_pattern(dep))) + change_last_pattern(dep, PATTERN_set_flag(get_last_pattern(dep), RT_FIRST)); +} + + +static void add_reserved_pattern(int reserved) +{ + add_pattern(PATTERN_make(RT_RESERVED, reserved)); +} + + + +static void add_operator_output(short op, short nparam) +{ + PATTERN pattern; + + /* + Why this test? + + add_operator() can be called without operator. See: + if (RES_priority(op) < prio) ... + */ + + if (op == RS_NONE || op == RS_UNARY) + return; + + if (op == RS_EXCL) + { + op = RS_LSQR; + nparam = 2; + + check_last_first(2); + } + + pattern = PATTERN_make(RT_RESERVED, op); + + /*if (op == RS_LBRA && has_output) + pattern = PATTERN_set_flag(pattern, RT_OUTPUT);*/ + + add_pattern(pattern); + + pattern = PATTERN_make(RT_PARAM, nparam); + add_pattern(pattern); +} + + +static void add_operator(short op, short nparam) +{ + add_operator_output(op, nparam); +} + + + +static void add_subr(PATTERN subr_pattern, short nparam) +{ + PATTERN pattern; + + /*if (has_output) + subr_pattern = PATTERN_set_flag(subr_pattern, RT_OUTPUT);*/ + + add_pattern(subr_pattern); + + pattern = PATTERN_make(RT_PARAM, nparam); + add_pattern(pattern); +} + + +static void analyze_make_array() +{ + int n = 0; + bool checked = FALSE; + bool collection = FALSE; + + if (!PATTERN_is(*current, RS_RSQR)) + { + for(;;) + { + n++; + /*if (n > MAX_PARAM_OP) + THROW("Too many arguments");*/ + analyze_expr(0, RS_NONE); + + if (!checked) + { + collection = PATTERN_is(*current, RS_COLON); + checked = TRUE; + } + + if (collection) + { + if (!PATTERN_is(*current, RS_COLON)) + THROW("Missing ':'"); + current++; + n++; + /*if (n > MAX_PARAM_OP) + THROW("Too many arguments");*/ + analyze_expr(0, RS_NONE); + } + + if (!PATTERN_is(*current, RS_COMMA)) + break; + current++; + + if (collection) + { + if (n == (MAX_PARAM_OP - 1)) + { + add_operator(RS_COLON, MAX_PARAM_OP + 1); + n = 0; + } + } + else + { + if (n == MAX_PARAM_OP) + { + add_operator(RS_RSQR, MAX_PARAM_OP + 1); + n = 0; + } + } + } + } + + if (!PATTERN_is(*current, RS_RSQR)) + THROW("Missing ']'"); + current++; + + add_operator(collection ? RS_COLON : RS_RSQR, n); +} + + + +static void analyze_single(int op) +{ + PATTERN *pattern; + + if (PATTERN_is_newline(*current)) + current++; + + if (op == RS_PT && !PATTERN_is_identifier(*current)) + THROW("The '.' operator must be followed by an identifier"); + else if (op == RS_EXCL && !PATTERN_is_string(*current)) + THROW("The '!' operator must be followed by an identifier"); + + /* ( expr ) */ + + if (PATTERN_is(*current, RS_LBRA)) + { + int index = ARRAY_count(EVAL->tree); + PATTERN last; + + current++; + analyze_expr(0, RS_NONE); + + if (!PATTERN_is(*current, RS_RBRA)) + THROW("Missing ')'"); + current++; + + if (ARRAY_count(EVAL->tree) == (index + 1)) + { + last = get_last_pattern(1); + if (PATTERN_is_string(last)) + change_last_pattern(1, PATTERN_make(RT_TSTRING, PATTERN_index(last))); + } + } + + /* [ expr, expr, ... ] */ + + else if (PATTERN_is(*current, RS_LSQR)) + { + current++; + analyze_make_array(); + } + + /* - expr | NOT expr */ + + else if (PATTERN_is(*current, RS_MINUS) || PATTERN_is(*current, RS_NOT)) + { + pattern = current; + current++; + + analyze_expr(RES_priority(RS_NOT), RS_UNARY); + add_operator(PATTERN_index(*pattern), 1); + } + + /* . symbol + + else if (PATTERN_is(*current, RS_PT) && PATTERN_is_identifier(current[1])) + { + add_operator(PATTERN_index(current[0]), 0); + add_pattern(PATTERN_set_flag(current[1], RT_POINT)); + current += 2; + }*/ + + /* NULL, TRUE, FALSE, ME, LAST */ + /* nombre, chaine ou symbole */ + + else if (PATTERN_is(*current, RS_NULL) + || PATTERN_is(*current, RS_ME) + || PATTERN_is(*current, RS_LAST) + || PATTERN_is(*current, RS_TRUE) + || PATTERN_is(*current, RS_FALSE) + || PATTERN_is(*current, RS_PINF) + || PATTERN_is(*current, RS_MINF) + //|| PATTERN_is(*current, RS_ERROR) + || (!PATTERN_is_reserved(*current) && !PATTERN_is_newline(*current) && !PATTERN_is_end(*current))) + { + add_pattern(*current); + + if (PATTERN_is_identifier(*current)) + { + /*if ((op == RS_NONE || op == RS_UNARY) && (PATTERN_is_identifier(*current))) + change_last_pattern(1, PATTERN_set_flag(get_last_pattern(1), RT_FIRST));*/ + if (op == RS_PT) + { + change_last_pattern(1, PATTERN_set_flag(get_last_pattern(1), RT_POINT)); + check_last_first(2); + } + } + + current++; + } + + else + THROW2("Unexpected &1", READ_get_pattern(current)); +} + + +static void analyze_call() +{ + int nparam_post = 0; + PATTERN subr_pattern = NULL_PATTERN; + PATTERN last_pattern = get_last_pattern(1); + SUBR_INFO *info; + bool optional = TRUE; + + if (PATTERN_is_subr(last_pattern)) + { + subr_pattern = last_pattern; + remove_last_pattern(); + optional = FALSE; + } + else if (PATTERN_is_identifier(last_pattern)) + { + if (EVAL->custom) + { + change_last_pattern(1, PATTERN_make(RT_IDENTIFIER, EVAL->var[0])); + add_reserved_pattern(RS_PT); + add_pattern(PATTERN_set_flag(last_pattern, RT_POINT)); + } + + check_last_first(1); + } + else if (PATTERN_is_string(last_pattern) || PATTERN_is_number(last_pattern)) + THROW(E_SYNTAX); + + /* N.B. Le cas où last_pattern = "." n'a pas de test spécifique */ + + if (subr_pattern && subr_pattern == PATTERN_make(RT_SUBR, SUBR_VarPtr)) + THROW("VarPtr() cannot be used with Eval()"); + + for (;;) + { + if (PATTERN_is(*current, RS_RBRA)) + { + current++; + break; + } + + if (nparam_post > 0) + { + if (!PATTERN_is(*current, RS_COMMA)) + THROW("Missing ')'"); + current++; + } + + if (optional && (PATTERN_is(*current, RS_COMMA) || PATTERN_is(*current, RS_RBRA))) + { + add_reserved_pattern(RS_OPTIONAL); + } + else + { + analyze_expr(0, RS_NONE); + } + + nparam_post++; + + if (nparam_post > MAX_PARAM_FUNC) + THROW("Too many arguments"); + } + + if (get_last_pattern(1) == PATTERN_make(RT_RESERVED, RS_OPTIONAL)) + THROW("Syntax error. Needless arguments"); + + /* + while (nparam_post > 0) + { + if (get_last_pattern(1) != PATTERN_make(RT_RESERVED, RS_OPTIONAL)) + break; + + remove_last_pattern(); + nparam_post--; + } + */ + + if (subr_pattern == NULL_PATTERN) + add_operator_output(RS_LBRA, nparam_post); + else + { + info = &COMP_subr_info[PATTERN_index(subr_pattern)]; + + if (nparam_post < info->min_param) + THROW2("Not enough arguments to &1", info->name); + else if (nparam_post > info->max_param) + THROW2("Too many arguments to &1", info->name); + + add_subr(subr_pattern, nparam_post); + } +} + + +static void analyze_array() +{ + int i; + + check_last_first(1); + + for(i = 0; i < MAX_ARRAY_DIM; i++) + { + analyze_expr(0, RS_NONE); + + if (!PATTERN_is(*current, RS_COMMA)) + break; + + current++; + } + + if (!PATTERN_is(*current, RS_RSQR)) + THROW("Missing ']'"); + current++; + + add_operator(RS_LSQR, i + 2); +} + +#if 0 +static void analyze_expr_check_first(int op_curr) +{ + /* On laisse le marqueur RT_FIRST que si on a affaire �l'op�ateur '.' */ + /* + last = get_last_pattern(); + if (PATTERN_is_first(last)) + { + if (op_curr != RS_PT) + change_last_pattern(PATTERN_unset_flag(last, RT_FIRST)); + } + */ +} +#endif + +static void analyze_expr(short priority, short op_main) +{ + short op, op_curr, op_not; + short prio; + short nparam; + + inc_level(); + + op_curr = op_main; + op_not = RS_NONE; + nparam = (op_main == RS_NONE || op_main == RS_UNARY) ? 0 : 1; + + /* Special NEW case */ + /* It should never be used, analysis is done elsewhere */ + + if (PATTERN_is(*current, RS_NEW)) + THROW("Cannot use NEW operator there"); + + // Operand analysis + +READ_OPERAND: + + //analyze_expr_check_first(op_curr); + + analyze_single(op_curr); + nparam++; + + if (nparam > MAX_PARAM_OP) + THROW("Expression too complex. Too many operands"); + + // operator + +READ_OPERATOR: + + if (!PATTERN_is_reserved(*current)) + goto OPERATOR_END; + + op = PATTERN_index(*current); + + if (!RES_is_operator(op)) + goto OPERATOR_END; + + current++; + + if (op == RS_NOT && PATTERN_is_reserved(*current)) + { + op_not = PATTERN_index(*current); + if (RES_is_operator(op_not) && RES_can_have_not_before(op_not)) + { + op = op_not + 1; + current++; + } + } + + /*if ((op == RS_BEGINS || op == RS_ENDS) && PATTERN_is(*current, RS_WITH)) + current++;*/ + + if (priority) + prio = priority; + else if (op_curr == RS_NONE) + prio = 0; + else + prio = RES_priority(op_curr); + + if (op_curr == RS_NONE) + { + if (RES_is_binary(op) || RES_is_n_ary(op)) + { + op_curr = op; + goto READ_OPERAND; + } + } + + if (op_curr == op) + { + if (!(RES_is_binary(op) && nparam == 2)) + goto READ_OPERAND; + } + + if (RES_priority(op) > prio) + { + if (op == RS_LSQR) + analyze_array(); + else if (op == RS_LBRA) + analyze_call(); + else + analyze_expr(RES_priority(op), op); + + goto READ_OPERATOR; + } + + if (RES_priority(op) == prio) + { + add_operator(op_curr, nparam); + + if (op == RS_LSQR) + { + analyze_array(); + goto READ_OPERATOR; + } + else if (op == RS_LBRA) + { + analyze_call(); + goto READ_OPERATOR; + } + else + { + if (RES_is_only(op_curr) || RES_is_only(op)) + THROW("Ambiguous expression. Please use brackets"); + + nparam = 1; + op_curr = op; + goto READ_OPERAND; + } + } + + if (RES_priority(op) < prio) + { + if ((op_main != RS_NONE) || (priority > 0)) + { + add_operator(op_curr, nparam); + current--; + if (op_not != RS_NONE) + current--; + goto END; + } + + add_operator(op_curr, nparam); + + if (op == RS_LSQR) + { + analyze_array(); + nparam = 1; + op_curr = op_main; + goto READ_OPERATOR; + } + else if (op == RS_LBRA) + { + analyze_call(); + nparam = 1; + op_curr = op_main; + goto READ_OPERATOR; + } + else + { + nparam = 1; + op_curr = op; + goto READ_OPERAND; + } + } + + dec_level(); + return; + +OPERATOR_END: + + //analyze_expr_check_first(op_curr); + + add_operator(op_curr, nparam); + +END: + + dec_level(); + return; + +} + + +PUBLIC void TRANS_tree() +{ + #ifdef DEBUG + int i; + #endif + + /*last_pattern = NULL_PATTERN;*/ + + ARRAY_create(&EVAL->tree); + /*ARRAY_add(&tree);*/ + + current = EVAL->current; //EVAL->pattern; + level = 0; + + if (PATTERN_is_newline(*current) || PATTERN_is_end(*current)) + THROW(E_SYNTAX); + + analyze_expr(0, RS_NONE); + + while (PATTERN_is_newline(*current)) + current++; + + EVAL->current = current; + + #ifdef DEBUG + printf("\n"); + for (i = 0; i < ARRAY_count(EVAL->tree); i++) + READ_dump_pattern(&EVAL->tree[i]); + #endif +} + + +#if 0 +PUBLIC bool TRANS_is_statement(TRANS_TREE *tree) +{ + PATTERN last; + int count; + + count = ARRAY_count(tree); + + if (count == 0) + return FALSE; + + count--; + last = tree[count]; + + if (PATTERN_is_param(last) && (count > 0)) + { + count--; + last = tree[count]; + } + + if (PATTERN_is(last, RS_PT) || (PATTERN_is_identifier(last) && count == 0)) + { + add_operator(RS_LBRA, 0); + return TRUE; + } + + if (PATTERN_is_subr(last) + || PATTERN_is(last, RS_LBRA) + || PATTERN_is(last, RS_RBRA) + || PATTERN_is(last, RS_AT) + || PATTERN_is(last, RS_COMMA)) + return TRUE; + + #ifdef DEBUG + printf("Last = "); + READ_dump_pattern(&last); + #endif + + return FALSE; + +} +#endif + diff --git a/main/lib/eval/gb.eval.component b/main/lib/eval/gb.eval.component new file mode 100644 index 00000000..427ea0af --- /dev/null +++ b/main/lib/eval/gb.eval.component @@ -0,0 +1,20 @@ +[Component] +Key=gb.eval +Name=Gambas expression evaluator +Name[fr]=Evaluateur d'expression de Gambas +Name[pl]=Kompilator wyrażeń Gambasa +Name[es]=Evaluador de expresiones de Gambas +Name[tr]=Gambas ifade değerlendirici +Author=Benoît Minisini + +;-------------------------------------------------- + +[Component] +Key=gb.eval +Name=Gambas expression evaluator +Name[fr]=Evaluateur d'expression de Gambas +Name[pl]=Kompilator wyrażeń Gambasa +Name[es]=Evaluador de expresiones de Gambas +Author=Benoît Minisini + + diff --git a/main/lib/eval/gb.eval.h b/main/lib/eval/gb.eval.h new file mode 100644 index 00000000..2ddd602b --- /dev/null +++ b/main/lib/eval/gb.eval.h @@ -0,0 +1,118 @@ +/*************************************************************************** + + gb.eval.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_EVAL_H +#define __GB_EVAL_H + +#include "gambas.h" +#include "gbc_read_common.h" + +/*#define DEBUG*/ + +typedef + bool (*EVAL_FUNCTION)(const char *, int, GB_VARIANT *); + +typedef + void *EVAL_EXPRESSION; + +#define EVAL_COLOR_MAX_LEN (1 << 10) + +typedef + struct { + unsigned state : 5; + unsigned alternate : 1; + unsigned len : 10; + } + EVAL_COLOR; + +typedef + EVAL_COLOR *EVAL_COLOR_ARRAY; + +typedef + struct + { + char *str; + EVAL_COLOR *color; + int len; + int proc; + int state; + } + EVAL_ANALYZE; + +#define EVAL_NORMAL 0 +#define EVAL_USE_CONTEXT 1 + +enum { + RT_DATATYPE = 13, + RT_ERROR = 14, + RT_HELP = 15, + RT_PREPROCESSOR = 16, + RT_ESCAPE = 17, + RT_LABEL = 18, + RT_CONSTANT = 19 + }; + +typedef + enum + { + HIGHLIGHT_BACKGROUND, + HIGHLIGHT_NORMAL, + HIGHLIGHT_KEYWORD, + HIGHLIGHT_SUBR, + HIGHLIGHT_OPERATOR, + HIGHLIGHT_SYMBOL, + HIGHLIGHT_NUMBER, + HIGHLIGHT_STRING, + HIGHLIGHT_COMMENT, + HIGHLIGHT_BREAKPOINT, + HIGHLIGHT_CURRENT, + HIGHLIGHT_DATATYPE, + HIGHLIGHT_SELECTION, + HIGHLIGHT_HIGHLIGHT, + HIGHLIGHT_LINE, + HIGHLIGHT_ERROR, + HIGHLIGHT_HELP, + HIGHLIGHT_PREPROCESSOR, + HIGHLIGHT_ESCAPE, + HIGHLIGHT_LABEL, + HIGHLIGHT_CONSTANT, + HIGHLIGHT_NUM_COLOR + } + HIGHLIGHT_COLOR; + +typedef + struct { + int version; + void (*Analyze)(const char *src, int len, int state, EVAL_ANALYZE *result, bool rewrite); + void (*New)(EVAL_EXPRESSION *expr, const char *src, int len); + bool (*Compile)(EVAL_EXPRESSION expr, bool assign); + GB_VALUE *(*Run)(EVAL_EXPRESSION expr, EVAL_FUNCTION func); + void (*Free)(EVAL_EXPRESSION *expr); + bool (*GetAssignmentSymbol)(EVAL_EXPRESSION expr, const char **sym, int *len); + } + EVAL_INTERFACE; + +#define EVAL_INTERFACE_VERSION 2 + +#endif + diff --git a/main/lib/eval/gb_alloc_override.h b/main/lib/eval/gb_alloc_override.h new file mode 100644 index 00000000..40d577d7 --- /dev/null +++ b/main/lib/eval/gb_alloc_override.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + gb_alloc_override.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_ALLOC_H +#define __GB_ALLOC_H + +#include "main.h" + +#define ALLOC(_ptr, _len) GB.Alloc((void **)(void *)_ptr, _len) +#define FREE(_ptr) GB.Free((void **)(void *)_ptr) +#define REALLOC(_ptr, _len) GB.Realloc((void **)(void *)_ptr, _len) + +#endif + diff --git a/main/lib/eval/gb_array.c b/main/lib/eval/gb_array.c new file mode 100644 index 00000000..37362e99 --- /dev/null +++ b/main/lib/eval/gb_array.c @@ -0,0 +1,26 @@ +/*************************************************************************** + + gb_array.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_alloc_override.h" +#include "gb_array_temp.h" + diff --git a/main/lib/eval/gb_error.c b/main/lib/eval/gb_error.c new file mode 100644 index 00000000..f833540c --- /dev/null +++ b/main/lib/eval/gb_error.c @@ -0,0 +1,116 @@ +/*************************************************************************** + + gb_error.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_ERROR_C + +#include "main.h" +#include "gb_common.h" +#include +#include "eval.h" +#include "gb_error.h" + +ERROR_CONTEXT *ERROR_current = NULL; + +void ERROR_clear(void) +{ + errno = 0; +} + +void ERROR_reset(ERROR_INFO *info) +{ + if (!info->code) + return; + + info->code = 0; + if (info->free) + { + GB.FreeString(&info->msg); + info->free = FALSE; + } + + info->msg = NULL; +} + + +void ERROR_propagate() +{ + if (ERROR_in_catch(ERROR_current)) + ERROR_leave(ERROR_current); + longjmp(ERROR_current->env, 1); +} + + + +PUBLIC char *ERROR_get(void) +{ + /* + if (code > 0 && code < 256) + return strerror(code); + else + return ERROR_Message[code - 256]; + */ + return strerror(errno); +} + +PUBLIC void THROW(const char *msg) +{ + GB.FreeString(&EVAL->error); + EVAL->error = GB.NewZeroString(msg); + ERROR_propagate(); +} + +static const char *_error_arg; + +static void get_error_arg(int index, char **str, int *len) +{ + *str = (char *)_error_arg; + *len = strlen(_error_arg); +} + +PUBLIC void THROW2(const char *pattern, const char *msg) +{ + GB.FreeString(&EVAL->error); + _error_arg = msg; + EVAL->error = GB.NewZeroString(GB.SubstString(pattern, strlen(pattern), get_error_arg)); + ERROR_propagate(); +} + +void ERROR_panic(const char *error, ...) +{ + va_list args; + + va_start(args, error); + + fflush(NULL); + + fprintf(stderr, "\n** INTERNAL ERROR **\n**"); + vfprintf(stderr, error, args); + putc('\n', stderr); + /*if (ERROR_current->info.code) + { + ERROR_print(); + }*/ + fprintf(stderr, "** Program aborting. Sorry! :-(\n"); + /*abort();*/ + _exit(1); +} diff --git a/main/lib/eval/gb_error.h b/main/lib/eval/gb_error.h new file mode 100644 index 00000000..071175b1 --- /dev/null +++ b/main/lib/eval/gb_error.h @@ -0,0 +1,59 @@ +/*************************************************************************** + + gb_error.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_ERROR_H +#define __GB_ERROR_H + +#include +#include + +#include "gb_limit.h" + +#define NO_ERROR_HANDLER + +#include "gb_error_common.h" + +#define E_MEMORY "Out of memory" +#define E_SYNTAX "Syntax error" + +#ifndef __GB_ERROR_C +EXTERN ERROR_CONTEXT *ERROR_current; +#endif + +void ERROR_clear(void); +char *ERROR_get(void); +void ERROR_reset(ERROR_INFO *info); + +//void ERROR_enter(ERROR_CONTEXT *err); +//void ERROR_leave(ERROR_CONTEXT *err); + +void PROPAGATE() NORETURN; +void THROW(const char *msg) NORETURN; +void THROW2(const char *pattern, const char *msg) NORETURN; + +void ERROR_panic(const char *error, ...) NORETURN; + +void ERROR_print(void); +void ERROR_print_at(FILE *where); + +#endif diff --git a/main/lib/eval/gb_table.c b/main/lib/eval/gb_table.c new file mode 100644 index 00000000..4053f07e --- /dev/null +++ b/main/lib/eval/gb_table.c @@ -0,0 +1,28 @@ +/*************************************************************************** + + gb_table.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gambas.h" +#include "gb_alloc_override.h" + +#include "gb_table_temp.h" + diff --git a/main/lib/eval/main.c b/main/lib/eval/main.c new file mode 100644 index 00000000..90dc10d4 --- /dev/null +++ b/main/lib/eval/main.c @@ -0,0 +1,78 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gb_common.h" +#include "gambas.h" + +#include "eval.h" +#include "eval_analyze.h" +#include "main.h" + +#include "c_expression.h" +#include "c_system.h" +#include "c_highlight.h" + +GB_INTERFACE GB EXPORT; + +GB_CLASS CLASS_Expression; + +void *GB_EVAL_2[] EXPORT = { + + (void *)EVAL_INTERFACE_VERSION, + (void *)EVAL_analyze, + (void *)EVAL_new, + (void *)EVAL_compile, + (void *)EVAL_expression, + (void *)EVAL_free, + (void *)EVAL_get_assignment_symbol, + + NULL + }; + + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CExpressionDesc, + CSystemDesc, + CHighlightDesc, + NULL +}; + + +int EXPORT GB_INIT(void) +{ + CLASS_Expression = GB.FindClass("Expression"); + + EVAL_init(); + + return 0; +} + +void EXPORT GB_EXIT() +{ + EVAL_exit(); +} + + diff --git a/main/lib/eval/main.h b/main/lib/eval/main.h new file mode 100644 index 00000000..a52a8f39 --- /dev/null +++ b/main/lib/eval/main.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; + +extern GB_CLASS CLASS_Expression; +#endif + +#endif diff --git a/main/lib/gb.component b/main/lib/gb.component new file mode 100644 index 00000000..bc25172e --- /dev/null +++ b/main/lib/gb.component @@ -0,0 +1,26 @@ +[Component] +Key=gb +Name=Gambas internal native classes +Name[fr]=Classes internes de Gambas +Name[pl]=Macierzyste klasy Gambasa +Name[es]=Clases internas de Gambas +Name[tr]=Gambas iç sınıfları +Author=Benoît Minisini + +[Special] +Controls=Timer +Virtual=Timer + +;---------------------------------------- + +[Component] +Key=gb +Name=Gambas internal native classes +Name[fr]=Classes internes de Gambas +Name[pl]=Macierzyste klasy Gambasa +Name[es]=Clases internas de Gambas +Author=Benoît Minisini + +[Special] +Controls=Timer +Virtual=Timer \ No newline at end of file diff --git a/main/lib/geom/Makefile.am b/main/lib/geom/Makefile.am new file mode 100644 index 00000000..36f7a717 --- /dev/null +++ b/main/lib/geom/Makefile.am @@ -0,0 +1,10 @@ +gblib_LTLIBRARIES = gb.geom.la + +gb_geom_la_LIBADD = @MATH_LIB@ +gb_geom_la_LDFLAGS = -module @LD_FLAGS@ +gb_geom_la_CFLAGS = -I$(top_srcdir)/share @INCLTDL@ $(AM_CFLAGS) + +gb_geom_la_SOURCES = \ + crect.h crect.c crect_temp.h \ + cpoint.h cpoint.c cpoint_temp.h \ + gb.geom.h main.h main.c diff --git a/main/lib/geom/cpoint.c b/main/lib/geom/cpoint.c new file mode 100644 index 00000000..5a40cd1c --- /dev/null +++ b/main/lib/geom/cpoint.c @@ -0,0 +1,33 @@ +/*************************************************************************** + + cpoint.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CPOINT_C + +#include "gb_common.h" +#include "cpoint.h" +#include "crect.h" +#include "cpoint_temp.h" + +IMPLEMENT_POINT_CLASS(CPOINT, Point, GB_INTEGER, int, "i", GB.ReturnInteger, ((CPOINT *)_object), CRECT, Rect) +IMPLEMENT_POINT_CLASS(CPOINTF, PointF, GB_FLOAT, double, "f", GB.ReturnFloat, ((CPOINTF *)_object), CRECTF, RectF) + diff --git a/main/lib/geom/cpoint.h b/main/lib/geom/cpoint.h new file mode 100644 index 00000000..6fc26ef4 --- /dev/null +++ b/main/lib/geom/cpoint.h @@ -0,0 +1,54 @@ +/*************************************************************************** + + cpoint.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CPOINT_H +#define __CPOINT_H + +#include "gambas.h" +#include "main.h" + +#ifndef __CRECT_C +extern GB_DESC PointDesc[]; +extern GB_DESC PointFDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + int x; + int y; + } + CPOINT; + +typedef + struct { + GB_BASE ob; + double x; + double y; + } + CPOINTF; + +#endif + +CPOINT * CPOINT_create(int x, int y); +CPOINTF * CPOINTF_create(double x, double y); diff --git a/main/lib/geom/cpoint_temp.h b/main/lib/geom/cpoint_temp.h new file mode 100644 index 00000000..cfd356f6 --- /dev/null +++ b/main/lib/geom/cpoint_temp.h @@ -0,0 +1,258 @@ +/*************************************************************************** + + cpoint_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define IMPLEMENT_POINT_CLASS(__struct, __name, __gtype, __ctype, __sign, __return, __this, __rstruct, __rname) \ + \ +__struct * __struct##_create(__ctype x, __ctype y) \ +{ \ + __struct *p = GB.New(GB.FindClass(#__name), NULL, NULL); \ + p->x = x; \ + p->y = y; \ + return p; \ +} \ + \ +static inline __struct *__struct##_make(__struct *a, const __ctype x, const __ctype y) \ +{ \ + if (a->ob.ref <= 1) \ + { \ + a->x = x; \ + a->y = y; \ + return a; \ + } \ + else \ + return __struct##_create(x, y); \ +} \ + \ +static __struct *_add_##__name(__struct *a, __struct *b, bool invert) \ +{ \ + return __struct##_make(a, a->x + b->x, a->y + b->y); \ +} \ + \ +static __struct *_sub_##__name(__struct *a, __struct *b, bool invert) \ +{ \ + return __struct##_make(a, a->x - b->x, a->y - b->y); \ +} \ + \ +static __struct *_mulf_##__name(__struct *a, double f, bool invert) \ +{ \ + return __struct##_make(a, a->x * f, a->y * f); \ +} \ + \ +static __struct *_mulo_##__name(__struct *a, void *b, bool invert) \ +{ \ + return NULL; \ +} \ + \ +static __struct *_divf_##__name(__struct *a, double f, bool invert) \ +{ \ + if (invert) \ + return NULL; \ + if (f == 0.0) \ + return NULL; \ + \ + return __struct##_make(a, a->x / f, a->y / f); \ +} \ + \ +static __struct *_divo_##__name(__struct *a, void *b, bool invert) \ +{ \ + return NULL; \ +} \ + \ +static int _equal_##__name(__struct *a, __struct *b, bool invert) \ +{ \ + return a->x == b->x && a->y == b->y; \ +} \ + \ +static double _fabs_##__name(__struct *a) \ +{ \ + return hypot(a->x, a->y); \ +} \ + \ +static __struct *_neg_##__name(__struct *a) \ +{ \ + return __struct##_make(a, -a->x, -a->y); \ +} \ + \ +static GB_OPERATOR_DESC _operator_##__name = \ +{ \ + .equal = (void *)_equal_##__name, \ + .add = (void *)_add_##__name, \ + .sub = (void *)_sub_##__name, \ + .mulf = (void *)_mulf_##__name, \ + .mulo = (void *)_mulo_##__name, \ + .divf = (void *)_divf_##__name, \ + .divo = (void *)_divo_##__name, \ + .fabs = (void *)_fabs_##__name, \ + .neg = (void *)_neg_##__name, \ +}; \ + \ +char *__struct##_to_string(void *a, bool local) \ +{ \ + char *result = NULL; \ + char *str; \ + int len; \ + \ + __ctype x = ((__struct *)a)->x; \ + __ctype y = ((__struct *)a)->y; \ + \ + result = GB.AddChar(result, '['); \ + \ + GB.NumberToString(local, x, NULL, &str, &len); \ + result = GB.AddString(result, str, len); \ + \ + result = GB.AddChar(result, local ? ' ' : ','); \ + \ + GB.NumberToString(local, y, NULL, &str, &len); \ + result = GB.AddString(result, str, len); \ + \ + result = GB.AddChar(result, ']'); \ + \ + return result; \ +} \ + \ +static bool _convert_##__name(void *a, GB_TYPE type, GB_VALUE *conv) \ +{ \ + if (a) \ + { \ + double norm = _fabs_##__name(a); \ + \ + switch (type) \ + { \ + case GB_T_FLOAT: \ + conv->_float.value = norm; \ + return FALSE; \ + \ + case GB_T_SINGLE: \ + conv->_single.value = norm; \ + return FALSE; \ + \ + case GB_T_INTEGER: \ + case GB_T_SHORT: \ + case GB_T_BYTE: \ + conv->_integer.value = norm; \ + return FALSE; \ + \ + case GB_T_LONG: \ + conv->_long.value = norm; \ + return FALSE; \ + \ + case GB_T_STRING: \ + case GB_T_CSTRING: \ + conv->_string.value.addr = __struct##_to_string(a, type == GB_T_CSTRING); \ + conv->_string.value.start = 0; \ + conv->_string.value.len = GB.StringLength(conv->_string.value.addr); \ + return FALSE; \ + \ + default: \ + if (type == GB.FindClass("Point")) \ + { \ + conv->_object.value = CPOINT_create(((__struct *)a)->x, ((__struct *)a)->y); \ + return FALSE; \ + } \ + if (type == GB.FindClass("PointF")) \ + { \ + conv->_object.value = CPOINTF_create(((__struct *)a)->x, ((__struct *)a)->y); \ + return FALSE; \ + } \ + else \ + return TRUE; \ + } \ + } \ + else \ + return TRUE; \ +} \ + \ + \ +BEGIN_METHOD(__name##_new, __gtype x; __gtype y; __gtype w; __gtype h) \ + \ + if (!MISSING(x) && !MISSING(y)) \ + { \ + __this->x = VARG(x); \ + __this->y = VARG(y); \ + } \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_call, __gtype x; __gtype y) \ + \ + GB.ReturnObject(__struct##_create(VARGOPT(x, 0), VARGOPT(y, 0))); \ + \ +END_METHOD \ + \ +BEGIN_PROPERTY(__name##_X) \ + \ + if (READ_PROPERTY) \ + __return(__this->x); \ + else \ + __this->x = VPROP(__gtype); \ + \ +END_PROPERTY \ + \ +BEGIN_PROPERTY(__name##_Y) \ + \ + if (READ_PROPERTY) \ + __return(__this->y); \ + else \ + __this->y = VPROP(__gtype); \ + \ +END_PROPERTY \ + \ +BEGIN_METHOD_VOID(__name##_Copy) \ + \ + GB.ReturnObject(__struct##_create(__this->x, __this->y)); \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_InRect, GB_OBJECT rect) \ + \ + __rstruct *rect = VARG(rect); \ + \ + if (GB.CheckObject(rect)) \ + return; \ + \ + GB.ReturnBoolean( \ + (__this->x >= rect->x) && (__this->x < (rect->x + rect->w)) \ + && (__this->y >= rect->y) && (__this->y < (rect->y + rect->h)) \ + ); \ + \ +END_METHOD \ + \ +GB_DESC __name##Desc[] = \ +{ \ + GB_DECLARE(#__name, sizeof(__struct)), \ + \ + GB_METHOD("_new", NULL, __name##_new, "[(X)" __sign "(Y)" __sign), \ + GB_STATIC_METHOD("_call", #__name, __name##_call, "[(X)" __sign "(Y)" __sign), \ + \ + GB_PROPERTY("X", __sign, __name##_X), \ + GB_PROPERTY("Y", __sign, __name##_Y), \ + \ + GB_METHOD("Copy", #__name, __name##_Copy, NULL), \ + GB_METHOD("InRect", "b", __name##_InRect, "(Rectangle)" #__rname ";"), \ + \ + GB_INTERFACE("_operator", &_operator_##__name), \ + GB_INTERFACE("_convert", &_convert_##__name), \ + \ + GB_END_DECLARE \ +}; + diff --git a/main/lib/geom/crect.c b/main/lib/geom/crect.c new file mode 100644 index 00000000..e1be2931 --- /dev/null +++ b/main/lib/geom/crect.c @@ -0,0 +1,32 @@ +/*************************************************************************** + + crect.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CRECT_C + +#include "gb_common.h" +#include "cpoint.h" +#include "crect.h" +#include "crect_temp.h" + +IMPLEMENT_RECT_CLASS(CRECT, Rect, GB_INTEGER, int, "i", GB.ReturnInteger, ((CRECT *)_object), CPOINT, Point) +IMPLEMENT_RECT_CLASS(CRECTF, RectF, GB_FLOAT, double, "f", GB.ReturnFloat, ((CRECTF *)_object), CPOINTF, PointF) diff --git a/main/lib/geom/crect.h b/main/lib/geom/crect.h new file mode 100644 index 00000000..801fef4f --- /dev/null +++ b/main/lib/geom/crect.h @@ -0,0 +1,60 @@ +/*************************************************************************** + + crect.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CRECT_H +#define __CRECT_H + +#include "gambas.h" +#include "main.h" + +#ifndef __CRECT_C +extern GB_DESC RectDesc[]; +extern GB_DESC RectFDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + int x; + int y; + int w; + int h; + } + PACKED + CRECT; + +typedef + struct { + GB_BASE ob; + double x; + double y; + double w; + double h; + } + PACKED + CRECTF; + +CRECT *CRECT_create(void); +CRECTF *CRECTF_create(void); + +#endif diff --git a/main/lib/geom/crect_temp.h b/main/lib/geom/crect_temp.h new file mode 100644 index 00000000..2b91fd0a --- /dev/null +++ b/main/lib/geom/crect_temp.h @@ -0,0 +1,404 @@ +/*************************************************************************** + + crect_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define IMPLEMENT_RECT_CLASS(__struct, __name, __gtype, __ctype, __sign, __return, __this, __pstruct, __pname) \ + \ +static void __struct##_normalize(__struct *_object) \ +{ \ + if (__this->w < 0) \ + { \ + __this->w = (- __this->w); \ + __this->x -= __this->w; \ + } \ + \ + if (__this->h < 0) \ + { \ + __this->h = (- __this->h); \ + __this->y -= __this->h; \ + } \ +} \ + \ +__struct * __struct##_create(void) \ +{ \ + return GB.New(GB.FindClass(#__name), NULL, NULL); \ +} \ + \ + \ +BEGIN_METHOD(__name##_new, __gtype x; __gtype y; __gtype w; __gtype h) \ + \ + if (!MISSING(x) && !MISSING(y) && !MISSING(w) && !MISSING(h)) \ + { \ + __this->x = VARG(x); \ + __this->y = VARG(y); \ + __this->w = VARG(w); \ + __this->h = VARG(h); \ + __struct##_normalize(__this); \ + } \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_call, __gtype x; __gtype y; __gtype w; __gtype h) \ + \ + __struct *rect = __struct##_create(); \ + \ + rect->x = VARG(x); \ + rect->y = VARG(y); \ + rect->w = VARG(w); \ + rect->h = VARG(h); \ + __struct##_normalize(rect); \ + \ + GB.ReturnObject(rect); \ + \ +END_METHOD \ + \ +BEGIN_PROPERTY(__name##_X) \ + \ + if (READ_PROPERTY) \ + __return(__this->x); \ + else \ + __this->x = VPROP(__gtype); \ + \ +END_PROPERTY \ + \ +BEGIN_PROPERTY(__name##_Y) \ + \ + if (READ_PROPERTY) \ + __return(__this->y); \ + else \ + __this->y = VPROP(__gtype); \ + \ +END_PROPERTY \ + \ +BEGIN_PROPERTY(__name##_Width) \ + \ + if (READ_PROPERTY) \ + __return(__this->w); \ + else \ + { \ + __this->w = VPROP(__gtype); \ + __struct##_normalize(__this); \ + } \ + \ +END_PROPERTY \ + \ +BEGIN_PROPERTY(__name##_Height) \ + \ + if (READ_PROPERTY) \ + __return(__this->h); \ + else \ + { \ + __this->h = VPROP(__gtype); \ + __struct##_normalize(__this); \ + } \ + \ +END_PROPERTY \ + \ +BEGIN_PROPERTY(__name##_Left) \ + \ + if (READ_PROPERTY) \ + __return(__this->x); \ + else \ + { \ + __ctype dx = VPROP(__gtype) - __this->x; \ + if (dx > __this->w) \ + dx = __this->w; \ + \ + __this->x += dx; \ + __this->w -= dx; \ + } \ + \ +END_PROPERTY \ + \ +BEGIN_PROPERTY(__name##_Top) \ + \ + if (READ_PROPERTY) \ + __return(__this->y); \ + else \ + { \ + __ctype dy = VPROP(__gtype) - __this->y; \ + if (dy > __this->h) \ + dy = __this->h; \ + \ + __this->y += dy; \ + __this->h -= dy; \ + } \ + \ +END_PROPERTY \ + \ +BEGIN_PROPERTY(__name##_Right) \ + \ + if (READ_PROPERTY) \ + __return(__this->x + __this->w); \ + else \ + { \ + __ctype x2 = VPROP(__gtype); \ + if (x2 < __this->x) \ + x2 = __this->x; \ + \ + __this->w = x2 - __this->x; \ + } \ + \ +END_PROPERTY \ + \ +BEGIN_PROPERTY(__name##_Bottom) \ + \ + if (READ_PROPERTY) \ + __return(__this->y + __this->h); \ + else \ + { \ + __ctype y2 = VPROP(__gtype); \ + if (y2 < __this->y) \ + y2 = __this->y; \ + \ + __this->h = y2 - __this->y; \ + } \ + \ +END_PROPERTY \ + \ +BEGIN_METHOD_VOID(__name##_Clear) \ + \ + __this->x = __this->y = __this->w = __this->h = 0; \ + \ +END_METHOD \ + \ +BEGIN_METHOD_VOID(__name##_IsVoid) \ + \ + GB.ReturnBoolean(__this->w <= 0 || __this->h <= 0); \ + \ +END_METHOD \ + \ +BEGIN_METHOD_VOID(__name##_Copy) \ + \ + __struct *copy = __struct##_create(); \ + \ + copy->x = __this->x; \ + copy->y = __this->y; \ + copy->w = __this->w; \ + copy->h = __this->h; \ + \ + GB.ReturnObject(copy); \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_Move, __gtype x; __gtype y; __gtype w; __gtype h) \ + \ + __this->x = VARG(x); \ + __this->y = VARG(y); \ + if (!MISSING(w) && !MISSING(h)) \ + { \ + __this->w = VARG(w); \ + __this->h = VARG(h); \ + __struct##_normalize(__this); \ + } \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_Resize, __gtype w; __gtype h) \ + \ + __this->w = VARG(w); \ + __this->h = VARG(h); \ + __struct##_normalize(__this); \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_Translate, __gtype dx; __gtype dy) \ + \ + __this->x += VARG(dx); \ + __this->y += VARG(dy); \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_Union, GB_OBJECT rect) \ + \ + __struct *dest; \ + __ctype x, y, w, h; \ + __struct *rect = (__struct *)VARG(rect); \ + \ + if (GB.CheckObject(rect)) \ + return; \ + \ + dest = __struct##_create(); \ + \ + x = Min(__this->x, rect->x); \ + y = Min(__this->y, rect->y); \ + w = Max(__this->x + __this->w, rect->x + rect->w) - x; \ + h = Max(__this->y + __this->h, rect->y + rect->h) - y; \ + \ + dest->x = x; \ + dest->y = y; \ + dest->w = w; \ + dest->h = h; \ + \ + GB.ReturnObject(dest); \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_Intersection, GB_OBJECT rect) \ + \ + __struct *dest; \ + __ctype x, y, x2, y2; \ + __struct *rect = (__struct *)VARG(rect); \ + \ + if (GB.CheckObject(rect)) \ + return; \ + \ + x = Max(__this->x, rect->x); \ + y = Max(__this->y, rect->y); \ + x2 = Min(__this->x + __this->w, rect->x + rect->w); \ + y2 = Min(__this->y + __this->h, rect->y + rect->h); \ + \ + if (x2 > x && y2 > y) \ + { \ + dest = __struct##_create(); \ + \ + dest->x = x; \ + dest->y = y; \ + dest->w = x2 - x; \ + dest->h = y2 - y; \ + \ + GB.ReturnObject(dest); \ + } \ + else \ + { \ + GB.ReturnNull(); \ + } \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_Contains, __gtype x; __gtype y) \ + \ + __ctype x = VARG(x); \ + __ctype y = VARG(y); \ + \ + GB.ReturnBoolean((x >= __this->x) && (x < (__this->x + __this->w)) && (y >= __this->y) && (y < (__this->y + __this->h))); \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_Adjust, __gtype left; __gtype top; __gtype right; __gtype bottom) \ + \ + __ctype left = VARG(left); \ + __ctype top = VARGOPT(top, left); \ + __ctype right = VARGOPT(right, left); \ + __ctype bottom = VARGOPT(bottom, top); \ + \ + __this->x += left; \ + __this->w -= (left + right); \ + __this->y += top; \ + __this->h -= (top + bottom); \ + \ + if (__this->w < 1 || __this->h < 1) \ + __this->w = __this->h = 0; \ + \ +END_METHOD \ + \ +BEGIN_METHOD_VOID(__name##_Center) \ + \ + __pstruct *point = GB.New(GB.FindClass(#__pname), NULL, NULL); \ + point->x = __this->x + __this->w / 2; \ + point->y = __this->y + __this->h / 2; \ + GB.ReturnObject(point); \ + \ +END_METHOD \ + \ +BEGIN_METHOD(__name##_Stretch, __gtype width; __gtype height; GB_OBJECT frame; GB_INTEGER align) \ + \ + __struct *frame = (__struct *)VARG(frame); \ + int align = VARGOPT(align, ALIGN_CENTER); \ + __ctype w = VARG(width); \ + __ctype h = VARG(height); \ + \ + __struct *result; \ + double scalew, scaleh; \ + \ + if (GB.CheckObject(frame)) \ + return; \ + \ + result = __struct##_create(); \ + if (w <= 0 || h <= 0 || frame->w <= 0 || frame->h <= 0) \ + { \ + GB.ReturnObject(result); \ + return; \ + } \ + \ + scalew = (double)frame->w / w; \ + scaleh = (double)frame->h / h; \ + if (scalew > scaleh) \ + scalew = scaleh; \ + else \ + scaleh = scalew; \ + \ + result->w = w * scalew; \ + result->h = h * scaleh; \ + \ + if (ALIGN_IS_LEFT(align)) \ + result->x = frame->x; \ + else if (ALIGN_IS_CENTER(align)) \ + result->x = frame->x + (frame->w - result->w) / 2; \ + else if (ALIGN_IS_RIGHT(align)) \ + result->x = frame->x + frame->w - result->w; \ + \ + if (ALIGN_IS_TOP(align)) \ + result->y = frame->y; \ + else if (ALIGN_IS_MIDDLE(align)) \ + result->y = frame->y + (frame->h - result->h) / 2; \ + else if (ALIGN_IS_BOTTOM(align)) \ + result->y = frame->y + frame->h - result->h; \ + \ + GB.ReturnObject(result); \ + \ +END_METHOD \ + \ +GB_DESC __name##Desc[] = \ +{ \ + GB_DECLARE(#__name, sizeof(__struct)), \ + \ + GB_METHOD("_new", NULL, __name##_new, "[(X)" __sign "(Y)" __sign "(Width)" __sign "(Height)" __sign "]"), \ + GB_STATIC_METHOD("_call", #__name, __name##_call, "(X)" __sign "(Y)" __sign "(Width)" __sign "(Height)" __sign), \ + \ + GB_PROPERTY("X", __sign, __name##_X), \ + GB_PROPERTY("Y", __sign, __name##_Y), \ + GB_PROPERTY("W", __sign, __name##_Width), \ + GB_PROPERTY("H", __sign, __name##_Height), \ + GB_PROPERTY("Width", __sign, __name##_Width), \ + GB_PROPERTY("Height", __sign, __name##_Height), \ + GB_PROPERTY("Left", __sign, __name##_Left), \ + GB_PROPERTY("Top", __sign, __name##_Top), \ + GB_PROPERTY("Right", __sign, __name##_Right), \ + GB_PROPERTY("Bottom", __sign, __name##_Bottom), \ + \ + GB_METHOD("Clear", NULL, __name##_Clear, NULL), \ + GB_METHOD("IsVoid", "b", __name##_IsVoid, NULL), \ + GB_METHOD("Copy", #__name, __name##_Copy, NULL), \ + GB_METHOD("Move", NULL, __name##_Move, "(X)" __sign "(Y)" __sign "[(Width)" __sign "(Height)" __sign "]"), \ + GB_METHOD("Resize", NULL, __name##_Resize, "(Width)" __sign "(Height)" __sign ""), \ + GB_METHOD("Translate", NULL, __name##_Translate, "(DX)" __sign "(DY)" __sign ""), \ + GB_METHOD("Union", #__name, __name##_Union, "(Rect)" #__name ";"), \ + GB_METHOD("Intersection", #__name, __name##_Intersection, "(Rect)" #__name ";"), \ + GB_METHOD("Contains", "b", __name##_Contains, "(X)" __sign "(Y)" __sign ""), \ + GB_METHOD("Adjust", NULL, __name##_Adjust, "(Left)" __sign "[(Top)" __sign "(Right)" __sign "(Bottom)" __sign "]"), \ + GB_METHOD("Center", #__pname, __name##_Center, NULL), \ + \ + GB_STATIC_METHOD("Stretch", #__name, __name##_Stretch, "(Width)" __sign "(Height)" __sign "(Frame)" #__name ";[(Alignment)i]"), \ + \ + GB_END_DECLARE \ +}; diff --git a/main/lib/geom/gb.geom.h b/main/lib/geom/gb.geom.h new file mode 100644 index 00000000..51fec36e --- /dev/null +++ b/main/lib/geom/gb.geom.h @@ -0,0 +1,105 @@ +/*************************************************************************** + + gb.geom.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_GEOM_H +#define __GB_GEOM_H + +#include "gambas.h" + +enum +{ + ALIGN_NORMAL = 0x00, + ALIGN_LEFT = 0x01, + ALIGN_RIGHT = 0x02, + ALIGN_CENTER = 0x03, + ALIGN_TOP_NORMAL = 0x10, + ALIGN_TOP_LEFT = 0x11, + ALIGN_TOP_RIGHT = 0x12, + ALIGN_TOP = 0x13, + ALIGN_BOTTOM_NORMAL = 0x20, + ALIGN_BOTTOM_LEFT = 0x21, + ALIGN_BOTTOM_RIGHT = 0x22, + ALIGN_BOTTOM = 0x23, + ALIGN_JUSTIFY = 0x04, +}; + +#define ALIGN_IS_TOP(_align) (((_align) & 0xF0) == 0x10) +#define ALIGN_IS_BOTTOM(_align) (((_align) & 0xF0) == 0x20) +#define ALIGN_IS_MIDDLE(_align) (((_align) & 0xF0) == 0x00) +#define ALIGN_IS_LEFT(_align) (((_align) & 0xF) == 0x1 || (((_align) & 0xF) == 0x0 && !GB.System.IsRightToLeft())) +#define ALIGN_IS_RIGHT(_align) (((_align) & 0xF) == 0x2 || (((_align) & 0xF) == 0x0 && GB.System.IsRightToLeft())) +#define ALIGN_IS_CENTER(_align) (((_align) & 0xF) == 0x3) + +typedef + struct { + GB_BASE ob; + int x; + int y; + } + GEOM_POINT; + +typedef + struct { + GB_BASE ob; + double x; + double y; + } + GEOM_POINTF; + +typedef + struct { + GB_BASE ob; + int x; + int y; + int w; + int h; + } + PACKED + GEOM_RECT; + +typedef + struct { + GB_BASE ob; + double x; + double y; + double w; + double h; + } + PACKED + GEOM_RECTF; + +#define GEOM_INTERFACE_VERSION 1 + +typedef + struct { + int version; + GEOM_POINT *(*CreatePoint)(int x, int y); + GEOM_POINTF *(*CreatePointF)(double x, double y); + GEOM_RECT *(*CreateRect)(void); + GEOM_RECTF *(*CreateRectF)(void); + } + GEOM_INTERFACE; + +#endif + + diff --git a/main/lib/geom/main.c b/main/lib/geom/main.c new file mode 100644 index 00000000..3f2a566e --- /dev/null +++ b/main/lib/geom/main.c @@ -0,0 +1,67 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "cpoint.h" +#include "crect.h" +#include "main.h" + +const GB_INTERFACE *GB_PTR EXPORT; + +void *GB_GEOM_1[] EXPORT = { + + (void *)1, + (void *)CPOINT_create, + (void *)CPOINTF_create, + (void *)CRECT_create, + (void *)CRECTF_create, + NULL + }; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + PointDesc, + PointFDesc, + RectDesc, + RectFDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + + +void EXPORT GB_EXIT() +{ +} + diff --git a/main/lib/geom/main.h b/main/lib/geom/main.h new file mode 100644 index 00000000..cb0ff30c --- /dev/null +++ b/main/lib/geom/main.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "gb.geom.h" + +#ifndef __MAIN_C +extern const GB_INTERFACE *GB_PTR; +#endif + +#define GB (*GB_PTR) + +#endif /* __MAIN_H */ diff --git a/main/lib/gui.opengl/Makefile.am b/main/lib/gui.opengl/Makefile.am new file mode 100644 index 00000000..ce29f239 --- /dev/null +++ b/main/lib/gui.opengl/Makefile.am @@ -0,0 +1,11 @@ +COMPONENT = gb.gui.opengl +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gui.opengl.la + +gb_gui_opengl_la_LIBADD = +gb_gui_opengl_la_LDFLAGS = -module @LD_FLAGS@ +gb_gui_opengl_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx @INCLTDL@ $(AM_CFLAGS) + +gb_gui_opengl_la_SOURCES = main.h main.c + diff --git a/main/lib/gui.opengl/gb.gui.opengl.component b/main/lib/gui.opengl/gb.gui.opengl.component new file mode 100644 index 00000000..65350c19 --- /dev/null +++ b/main/lib/gui.opengl/gb.gui.opengl.component @@ -0,0 +1,6 @@ +[Component] +Author=Benoît Minisini +Require=gb.gui,gb.opengl +Type=Form +Implement=OpenGLViewer +State=Finished diff --git a/main/lib/gui.opengl/main.c b/main/lib/gui.opengl/main.c new file mode 100644 index 00000000..ca66be98 --- /dev/null +++ b/main/lib/gui.opengl/main.c @@ -0,0 +1,73 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + NULL +}; + +char *GB_INCLUDE EXPORT = "gb.qt4.opengl|gb.qt5.opengl|gb.gtk.opengl"; + +int EXPORT GB_INIT(void) +{ + const char *comp = NULL; + char *env; + + env = getenv("GB_GUI"); + if (env) + { + if (strcmp(env, "gb.qt4") == 0) + comp = "gb.qt4.opengl"; + else if (strcmp(env, "gb.qt5") == 0) + comp = "gb.qt5.opengl"; + else if (strcmp(env, "gb.gtk") == 0) + comp = "gb.gtk.opengl"; + } + + if (!comp) + { + // GB_GUI should be set by gb.gui + if (!env) + fprintf(stderr, "gb.gui.opengl: error: no component specified in GB_GUI environment variable"); + else + fprintf(stderr, "gb.gui.opengl: error: unsupported component specified in GB_GUI environment variable"); + exit(1); + } + + if (GB.Component.Load(comp)) + fprintf(stderr, "gb.gui.opengl: unable to load '%s' component\n", comp); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + + diff --git a/main/lib/gui.opengl/main.h b/main/lib/gui.opengl/main.h new file mode 100644 index 00000000..d14ee22f --- /dev/null +++ b/main/lib/gui.opengl/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/main/lib/gui.qt.opengl/Makefile.am b/main/lib/gui.qt.opengl/Makefile.am new file mode 100644 index 00000000..ead52ad2 --- /dev/null +++ b/main/lib/gui.qt.opengl/Makefile.am @@ -0,0 +1,12 @@ +COMPONENT = gb.gui.qt.opengl +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gui.qt.opengl.la + +gb_gui_qt_opengl_la_LIBADD = +gb_gui_qt_opengl_la_LDFLAGS = -module @LD_FLAGS@ +gb_gui_qt_opengl_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx @INCLTDL@ $(AM_CFLAGS) + +gb_gui_qt_opengl_la_SOURCES = \ + main.h main.c + diff --git a/main/lib/gui.qt.opengl/gb.gui.qt.opengl.component b/main/lib/gui.qt.opengl/gb.gui.qt.opengl.component new file mode 100644 index 00000000..5f12979e --- /dev/null +++ b/main/lib/gui.qt.opengl/gb.gui.qt.opengl.component @@ -0,0 +1,4 @@ +[Component] +Author=Benoît Minisini +Requires=gb.gui.qt +State=Finished diff --git a/main/lib/gui.qt.opengl/main.c b/main/lib/gui.qt.opengl/main.c new file mode 100644 index 00000000..8c87701b --- /dev/null +++ b/main/lib/gui.qt.opengl/main.c @@ -0,0 +1,71 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + NULL +}; + +char *GB_INCLUDE EXPORT = "gb.qt4.opengl|gb.qt5.opengl"; + +int EXPORT GB_INIT(void) +{ + const char *comp = NULL; + char *env; + + env = getenv("GB_GUI"); + if (env) + { + if (strcmp(env, "gb.qt4") == 0) + comp = "gb.qt4.opengl"; + else if (strcmp(env, "gb.qt5") == 0) + comp = "gb.qt5.opengl"; + } + + if (!comp) + { + // GB_GUI should be set by gb.gui + if (!env) + fprintf(stderr, "gb.gui.qt.opengl: error: no component specified in GB_GUI environment variable"); + else + fprintf(stderr, "gb.gui.qt.opengl: error: unsupported component specified in GB_GUI environment variable"); + exit(1); + } + + if (GB.Component.Load(comp)) + fprintf(stderr, "gb.gui.qt.opengl: unable to load '%s' component\n", comp); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + + diff --git a/main/lib/gui.qt.opengl/main.h b/main/lib/gui.qt.opengl/main.h new file mode 100644 index 00000000..d14ee22f --- /dev/null +++ b/main/lib/gui.qt.opengl/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/main/lib/gui.qt.webkit/Makefile.am b/main/lib/gui.qt.webkit/Makefile.am new file mode 100644 index 00000000..cfe0189c --- /dev/null +++ b/main/lib/gui.qt.webkit/Makefile.am @@ -0,0 +1,12 @@ +COMPONENT = gb.gui.qt.webkit +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gui.qt.webkit.la + +gb_gui_qt_webkit_la_LIBADD = +gb_gui_qt_webkit_la_LDFLAGS = -module @LD_FLAGS@ +gb_gui_qt_webkit_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx @INCLTDL@ $(AM_CFLAGS) + +gb_gui_qt_webkit_la_SOURCES = \ + main.h main.c + diff --git a/main/lib/gui.qt.webkit/gb.gui.qt.webkit.component b/main/lib/gui.qt.webkit/gb.gui.qt.webkit.component new file mode 100644 index 00000000..5f12979e --- /dev/null +++ b/main/lib/gui.qt.webkit/gb.gui.qt.webkit.component @@ -0,0 +1,4 @@ +[Component] +Author=Benoît Minisini +Requires=gb.gui.qt +State=Finished diff --git a/main/lib/gui.qt.webkit/main.c b/main/lib/gui.qt.webkit/main.c new file mode 100644 index 00000000..b6261b41 --- /dev/null +++ b/main/lib/gui.qt.webkit/main.c @@ -0,0 +1,71 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + NULL +}; + +char *GB_INCLUDE EXPORT = "gb.qt4.webkit|gb.qt5.webkit"; + +int EXPORT GB_INIT(void) +{ + const char *comp = NULL; + char *env; + + env = getenv("GB_GUI"); + if (env) + { + if (strcmp(env, "gb.qt4") == 0) + comp = "gb.qt4.webkit"; + else if (strcmp(env, "gb.qt5") == 0) + comp = "gb.qt5.webkit"; + } + + if (!comp) + { + // GB_GUI should be set by gb.gui + if (!env) + fprintf(stderr, "gb.gui.qt.webkit: error: no component specified in GB_GUI environment variable"); + else + fprintf(stderr, "gb.gui.qt.webkit: error: unsupported component specified in GB_GUI environment variable"); + exit(1); + } + + if (GB.Component.Load(comp)) + fprintf(stderr, "gb.gui.qt.webkit: unable to load '%s' component\n", comp); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + + diff --git a/main/lib/gui.qt.webkit/main.h b/main/lib/gui.qt.webkit/main.h new file mode 100644 index 00000000..d14ee22f --- /dev/null +++ b/main/lib/gui.qt.webkit/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/main/lib/gui.qt/Makefile.am b/main/lib/gui.qt/Makefile.am new file mode 100644 index 00000000..60ffed3d --- /dev/null +++ b/main/lib/gui.qt/Makefile.am @@ -0,0 +1,12 @@ +COMPONENT = gb.gui.qt +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gui.qt.la + +gb_gui_qt_la_LIBADD = +gb_gui_qt_la_LDFLAGS = -module @LD_FLAGS@ +gb_gui_qt_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx @INCLTDL@ $(AM_CFLAGS) + +gb_gui_qt_la_SOURCES = \ + main.h main.c + diff --git a/main/lib/gui.qt/gb.gui.qt.component b/main/lib/gui.qt/gb.gui.qt.component new file mode 100644 index 00000000..fdcc7ade --- /dev/null +++ b/main/lib/gui.qt/gb.gui.qt.component @@ -0,0 +1,6 @@ +[Component] +Author=Benoît Minisini +Implements=Form,EventLoop,ImageIO +Requires=gb.image +Type=Form +State=Finished \ No newline at end of file diff --git a/main/lib/gui.qt/main.c b/main/lib/gui.qt/main.c new file mode 100644 index 00000000..f1ad721e --- /dev/null +++ b/main/lib/gui.qt/main.c @@ -0,0 +1,158 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +enum { USE_NOTHING, USE_GB_QT4, USE_GB_QT5 }; + +const GB_INTERFACE *GB_PTR EXPORT; + +// Prevents gbi3 from complaining + +GB_DESC *GB_CLASSES[] EXPORT = +{ + NULL +}; + +char *GB_INCLUDE EXPORT = "gb.qt4|gb.qt5"; + + +const char *get_name(int use) +{ + switch (use) + { + case USE_GB_QT4: return "gb.qt4"; + default: return "gb.qt5"; + } +} + + +static bool can_use(int use) +{ + static const char *ext[] = { "ext", "webkit", "opengl", NULL }; + char test[32]; + char *suffix; + const char **pext; + const char *name; + + name = get_name(use); + + if (!GB.Component.CanLoadLibrary(name)) + return FALSE; + + strcpy(test, name); + suffix = test + strlen(name); + *suffix++ = '.'; + + for (pext = ext; *pext; pext++) + { + strcpy(suffix, *pext); + if (GB.Component.Exist(test) && !GB.Component.CanLoadLibrary(test)) + return FALSE; + } + + return TRUE; +} + + +int EXPORT GB_INIT(void) +{ + int use = USE_NOTHING; + int use_other = USE_NOTHING; + char *env; + const char *comp; + + env = getenv("GB_GUI"); + if (env && *env) + { + if (strcmp(env, "gb.qt4") == 0) + use = USE_GB_QT4; + else if (strcmp(env, "gb.qt5") == 0) + use = USE_GB_QT5; + else + fprintf(stderr, "gb.gui.qt: warning: '%s' component not supported\n", env); + } + + if (use == USE_NOTHING) + { + use = USE_GB_QT5; + + env = getenv("KDE_FULL_SESSION"); + + if (env && !strcmp(env, "true")) + { + env = getenv("KDE_SESSION_VERSION"); + if (env) + { + if (strcmp(env, "4") == 0) + use = USE_GB_QT4; + else if (strcmp(env, "5") == 0) + use = USE_GB_QT5; + } + } + } + + if (!can_use(use)) + { + if (use == USE_GB_QT4) + use_other = USE_GB_QT5; + else + use_other = USE_GB_QT4; + + if (can_use(use_other)) + { + fprintf(stderr, "gb.gui.qt: warning: '%s' component not found, using '%s' instead\n", get_name(use), get_name(use_other)); + use = use_other; + } + else + { + fprintf(stderr, "gb.gui.qt: error: unable to find any QT component\n"); + exit(1); + } + } + + comp = get_name(use); + + if (GB.Component.Load(comp)) + { + fprintf(stderr, "gb.gui.qt: error: cannot load component '%s'\n", comp); + exit(1); + } + else + { + env = getenv("GB_GUI_DEBUG"); + if (env && !strcmp(env, "0")) + fprintf(stderr, "gb.gui.qt: loading '%s'\n", comp); + } + + setenv("GB_GUI", comp, TRUE); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + diff --git a/main/lib/gui.qt/main.h b/main/lib/gui.qt/main.h new file mode 100644 index 00000000..23f4e73b --- /dev/null +++ b/main/lib/gui.qt/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern const GB_INTERFACE *GB_PTR; +#endif + +#define GB (*GB_PTR) + +#endif diff --git a/main/lib/gui.trayicon/Makefile.am b/main/lib/gui.trayicon/Makefile.am new file mode 100644 index 00000000..e4e49490 --- /dev/null +++ b/main/lib/gui.trayicon/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.gui.trayicon +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gui.trayicon.la + +gb_gui_trayicon_la_LIBADD = +gb_gui_trayicon_la_LDFLAGS = -module @LD_FLAGS@ +gb_gui_trayicon_la_CFLAGS = -I$(top_srcdir)/share @INCLTDL@ $(AM_CFLAGS) + +gb_gui_trayicon_la_SOURCES = \ + cfaketrayicon.h cfaketrayicon.c \ + main.h main.c + diff --git a/main/lib/gui.trayicon/cfaketrayicon.c b/main/lib/gui.trayicon/cfaketrayicon.c new file mode 100644 index 00000000..d2f9ad0c --- /dev/null +++ b/main/lib/gui.trayicon/cfaketrayicon.c @@ -0,0 +1,83 @@ +/*************************************************************************** + + cfaketrayicon.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CFAKETRAYICON_CPP + +#include "gambas.h" +#include "main.h" + +#include "cfaketrayicon.h" + + +GB_DESC FakeTrayIconsDesc[] = +{ + GB_DECLARE_STATIC("TrayIcons"), + + GB_STATIC_METHOD("_next", "TrayIcon", NULL, NULL), + GB_STATIC_METHOD("_get", "TrayIcon", NULL, "(Index)i"), + GB_STATIC_PROPERTY_READ("Count", "i", NULL), + GB_STATIC_METHOD("DeleteAll", NULL, NULL, NULL), + + GB_END_DECLARE +}; + + +GB_DESC FakeTrayIconDesc[] = +{ + GB_DECLARE("TrayIcon", 0), + + //GB_STATIC_METHOD("_exit", NULL, NULL, NULL), + + GB_CONSTANT("Horizontal", "i", 0), + GB_CONSTANT("Vertical", "i", 1), + + GB_METHOD("_new", NULL, NULL, NULL), + GB_METHOD("_free", NULL, NULL, NULL), + + GB_METHOD("Show", NULL, NULL, NULL), + GB_METHOD("Hide", NULL, NULL, NULL), + GB_METHOD("Delete", NULL, NULL, NULL), + + GB_PROPERTY("Picture", "Picture", NULL), + GB_PROPERTY("Icon", "Picture", NULL), + GB_PROPERTY("Visible", "b", NULL), + + GB_PROPERTY("Text", "s", NULL), + GB_PROPERTY("PopupMenu", "s", NULL), + GB_PROPERTY("Tooltip", "s", NULL), + GB_PROPERTY("Tag", "v", NULL), + + GB_EVENT("Click", NULL, NULL, NULL), + GB_EVENT("MiddleClick", NULL, NULL, NULL), + GB_EVENT("Scroll", NULL, "(Delta)f(Orientation)i", NULL), + + GB_CONSTANT("_IsControl", "b", TRUE), + GB_CONSTANT("_Family", "s", "*"), + GB_CONSTANT("_IsVirtual", "b", TRUE), + GB_CONSTANT("_Group", "s", "Special"), + GB_CONSTANT("_DefaultEvent", "s", "Click"), + GB_CONSTANT("_Properties", "s", "Visible=False,Tag,Tooltip,Picture,PopupMenu{Menu}"), + + GB_END_DECLARE +}; + diff --git a/main/lib/gui.trayicon/cfaketrayicon.h b/main/lib/gui.trayicon/cfaketrayicon.h new file mode 100644 index 00000000..55b733aa --- /dev/null +++ b/main/lib/gui.trayicon/cfaketrayicon.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + ctrayicon.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CFAKETRAYICON_H +#define __CFAKETRAYICON_H + +#include "gambas.h" +#include "main.h" + +#ifndef __CFAKETRAYICON_CPP +extern GB_DESC FakeTrayIconsDesc[]; +extern GB_DESC FakeTrayIconDesc[]; +#endif + +#endif diff --git a/main/lib/gui.trayicon/gb.gui.trayicon.component b/main/lib/gui.trayicon/gb.gui.trayicon.component new file mode 100644 index 00000000..6b4c404f --- /dev/null +++ b/main/lib/gui.trayicon/gb.gui.trayicon.component @@ -0,0 +1,5 @@ +[Component] +Author=Benoît Minisini +Requires=gb.dbus +Needs=Form +State=Finished \ No newline at end of file diff --git a/main/lib/gui.trayicon/main.c b/main/lib/gui.trayicon/main.c new file mode 100644 index 00000000..fdb5f052 --- /dev/null +++ b/main/lib/gui.trayicon/main.c @@ -0,0 +1,73 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "cfaketrayicon.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; + +// Prevents gbi3 from complaining + +GB_DESC *GB_CLASSES[] EXPORT = +{ + NULL +}; + +GB_DESC *GB_OPTIONAL_CLASSES[] EXPORT = +{ + FakeTrayIconDesc, FakeTrayIconsDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_AFTER_INIT(void) +{ + GB_FUNCTION func; + bool has_dbus_systemtray = FALSE; + void (*declare_tray_icon)(); + + GB.Component.Load("gb.dbus"); + + if (!GB.GetFunction(&func, (void *)GB.FindClass("DBus"), "_HasSystemTray", NULL, NULL)) + has_dbus_systemtray = GB.Call(&func, 0, FALSE)->_boolean.value; + + if (has_dbus_systemtray) + GB.Component.Load("gb.dbus.trayicon"); + else + { + GB.Component.GetInfo("DECLARE_TRAYICON", POINTER(&declare_tray_icon)); + (*declare_tray_icon)(); + } +} + +void EXPORT GB_EXIT() +{ +} + + diff --git a/main/lib/gui.trayicon/main.h b/main/lib/gui.trayicon/main.h new file mode 100644 index 00000000..d14ee22f --- /dev/null +++ b/main/lib/gui.trayicon/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/main/lib/gui/Makefile.am b/main/lib/gui/Makefile.am new file mode 100644 index 00000000..54e63ec3 --- /dev/null +++ b/main/lib/gui/Makefile.am @@ -0,0 +1,12 @@ +COMPONENT = gb.gui +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.gui.la + +gb_gui_la_LIBADD = +gb_gui_la_LDFLAGS = -module @LD_FLAGS@ +gb_gui_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx @INCLTDL@ $(AM_CFLAGS) + +gb_gui_la_SOURCES = \ + main.h main.c + diff --git a/main/lib/gui/gb.gui.component b/main/lib/gui/gb.gui.component new file mode 100644 index 00000000..45c1ae57 --- /dev/null +++ b/main/lib/gui/gb.gui.component @@ -0,0 +1,6 @@ +[Component] +Author=Benoît Minisini +Implements=Form,EventLoop,ImageIO +Requires=gb.image +Type=Form +State=Finished diff --git a/main/lib/gui/main.c b/main/lib/gui/main.c new file mode 100644 index 00000000..1218db8e --- /dev/null +++ b/main/lib/gui/main.c @@ -0,0 +1,176 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "main.h" + +enum { USE_NOTHING, USE_GB_QT4, USE_GB_QT5, USE_GB_GTK, USE_GB_GTK3 }; + +static char use_list[4][3] = { + { USE_GB_QT5, USE_GB_GTK, USE_GB_GTK3 }, + { USE_GB_QT4, USE_GB_GTK3, USE_GB_GTK }, + { USE_GB_GTK3, USE_GB_QT4, USE_GB_QT5 }, + { USE_GB_GTK, USE_GB_QT5, USE_GB_QT4 }, +}; + +GB_INTERFACE GB EXPORT; + +// Prevents gbi3 from complaining + +GB_DESC *GB_CLASSES[] EXPORT = +{ + NULL +}; + +char *GB_INCLUDE EXPORT = "gb.qt4|gb.qt5|gb.gtk|gb.gtk3"; + + +const char *get_name(int use) +{ + switch (use) + { + case USE_GB_QT4: return "gb.qt4"; + case USE_GB_QT5: return "gb.qt5"; + case USE_GB_GTK3: return "gb.gtk3"; + default: return "gb.gtk"; + } +} + + +static bool can_use(int use) +{ + static const char *ext[] = { "ext", "webkit", "opengl", NULL }; + char test[32]; + char *suffix; + const char **pext; + const char *name; + + name = get_name(use); + + if (!GB.Component.CanLoadLibrary(name)) + return FALSE; + + strcpy(test, name); + suffix = test + strlen(name); + *suffix++ = '.'; + + for (pext = ext; *pext; pext++) + { + strcpy(suffix, *pext); + if (GB.Component.Exist(test) && !GB.Component.CanLoadLibrary(test)) + return FALSE; + } + + return TRUE; +} + + +int EXPORT GB_INIT(void) +{ + int use = USE_NOTHING; + int use_other = USE_NOTHING; + char *env; + const char *comp; + int i; + + env = getenv("GB_GUI"); + if (env) + { + if (strcmp(env, "gb.qt4") == 0) + use = USE_GB_QT4; + else if (strcmp(env, "gb.qt5") == 0) + use = USE_GB_QT5; + else if (strcmp(env, "gb.gtk") == 0) + use = USE_GB_GTK; + else if (strcmp(env, "gb.gtk3") == 0) + use = USE_GB_GTK3; + } + + if (use == USE_NOTHING) + { + use = USE_GB_GTK; + + env = getenv("KDE_FULL_SESSION"); + + if (env && !strcmp(env, "true")) + { + env = getenv("KDE_SESSION_VERSION"); + if (env) + { + if (strcmp(env, "4") == 0) + use = USE_GB_QT4; + else if (strcmp(env, "5") == 0) + use = USE_GB_QT5; + } + } + } + + if (!can_use(use)) + { + use_other = USE_NOTHING; + for (i = 0; i <= 2; i++) + { + if (can_use(use_list[use - 1][i])) + { + use_other = use_list[use - 1][i]; + break; + } + } + + if (use_other) + { + fprintf(stderr, "gb.gui: warning: '%s' component not found, using '%s' instead\n", get_name(use), get_name(use_other)); + use = use_other; + } + else + { + fprintf(stderr, "gb.gui: error: unable to find any GUI component\n"); + exit(1); + } + } + + comp = get_name(use); + + if (GB.Component.Load(comp)) + { + fprintf(stderr, "gb.gui: error: cannot load component '%s'\n", comp); + exit(1); + } + else + { + env = getenv("GB_GUI_DEBUG"); + if (env && !strcmp(env, "0")) + fprintf(stderr, "gb.gui: loading '%s'\n", comp); + } + + setenv("GB_GUI", comp, TRUE); + + return 0; +} + +void EXPORT GB_EXIT() +{ +} + + diff --git a/main/lib/gui/main.h b/main/lib/gui/main.h new file mode 100644 index 00000000..d14ee22f --- /dev/null +++ b/main/lib/gui/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/main/lib/image.effect/CImage.cpp b/main/lib/image.effect/CImage.cpp new file mode 100644 index 00000000..687cbf0f --- /dev/null +++ b/main/lib/image.effect/CImage.cpp @@ -0,0 +1,473 @@ +/*************************************************************************** + + CImage.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CIMAGE_CPP + +#include + +#include "gambas.h" +#include "kimageeffect.h" +#include "effect.h" +#include "main.h" + +#include "CImage.h" + + +static void return_copy(GB_IMAGE img) +{ + GB_IMG *info = (GB_IMG *)img; + GB_IMG *copy; + + copy = IMAGE.Create(info->width, info->height, info->format, info->data); + GB.ReturnObject(copy); +} + +BEGIN_METHOD(CIMAGE_gradient, GB_INTEGER w; GB_INTEGER h; GB_INTEGER cs; GB_INTEGER cd; GB_INTEGER orient; GB_FLOAT x; GB_FLOAT y) + + QSize size(VARG(w), VARG(h)); + QColor cs(VARG(cs)); + QColor cd(VARG(cd)); + + if (MISSING(x) || MISSING(y)) + { + QImage img = KImageEffect::gradient(size, cs, cd, (KImageEffect::GradientType)VARG(orient)); + GB.ReturnObject(img.object()); + } + else + { + QImage img = KImageEffect::unbalancedGradient(size, cs, cd, (KImageEffect::GradientType)VARG(orient), (int)(VARG(x) * 200), (int)(VARG(y) * 200)); + GB.ReturnObject(img.object()); + } + +END_METHOD + + +BEGIN_METHOD(CIMAGE_intensity, GB_FLOAT val; GB_INTEGER channel) + + KImageEffect::RGBComponent channel = KImageEffect::All; + QImage img(THIS); + + if (!MISSING(channel)) + channel = (KImageEffect::RGBComponent)VARG(channel); + + if (channel == KImageEffect::All) + KImageEffect::intensity(img, VARG(val)); + else + KImageEffect::channelIntensity(img, VARG(val), channel); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_flatten, GB_INTEGER c1; GB_INTEGER c2) + + QImage img(THIS); + + KImageEffect::flatten(img, VARG(c1), VARG(c2)); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_fade, GB_INTEGER col; GB_FLOAT val) + + QImage img(THIS); + + KImageEffect::fade(img, VARG(val), VARG(col)); + +END_METHOD + + +/*BEGIN_METHOD_VOID(CIMAGE_gray) + + QImage img(THIS); + + KImageEffect::toGray(img, false); + +END_METHOD*/ + + +/*BEGIN_METHOD(CIMAGE_desaturate, GB_FLOAT val) + + QImage img(THIS); + + KImageEffect::desaturate(img, VARGOPT(val, 0.3)); + +END_METHOD*/ + + +BEGIN_METHOD(CIMAGE_threshold, GB_FLOAT val) + + QImage img(THIS); + + KImageEffect::threshold(img, (uint)(VARG(val) * 255)); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_solarize, GB_FLOAT val) + + QImage img(THIS); + + KImageEffect::solarize(img, VARG(val) * 100); + +END_METHOD + + +BEGIN_METHOD_VOID(CIMAGE_normalize) + + QImage img(THIS); + + KImageEffect::normalize(img); + +END_METHOD + + +BEGIN_METHOD_VOID(CIMAGE_equalize) + + QImage img(THIS); + + KImageEffect::equalize(img); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_balance, GB_FLOAT brightness; GB_FLOAT contrast; GB_FLOAT gamma; GB_INTEGER channel) + + Effect::balance(THIS, VARGOPT(channel, Effect::All), (int)(VARG(brightness) * 50), (int)(VARG(contrast) * 50), (int)(VARG(gamma) * 50)); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_invert, GB_INTEGER channel) + + Effect::invert(THIS, VARGOPT(channel, Effect::All)); + +END_METHOD + + +BEGIN_METHOD_VOID(CIMAGE_emboss) + + QImage src(THIS); + QImage dest = KImageEffect::emboss(src, 0, 1); + GB.ReturnObject(dest.object()); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_edge, GB_FLOAT radius) + + double radius = VARGOPT(radius, -1); + + if (radius != 0) + { + if (radius < 0) + radius = 0; + QImage src(THIS); + QImage dest = KImageEffect::edge(src, radius); + GB.ReturnObject(dest.object()); + } + else + return_copy(THIS); + +END_METHOD + + +BEGIN_METHOD_VOID(CIMAGE_despeckle) + + QImage src(THIS); + QImage dest = KImageEffect::despeckle(src); + GB.ReturnObject(dest.object()); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_blur, GB_FLOAT val) + + double val = VARGOPT(val, 0.2); + double sigma = 0.5 + val * (4.0 - 0.5); + double radius = 8; + + QImage src(THIS); + QImage dest = KImageEffect::blur(src, radius, sigma); + GB.ReturnObject(dest.object()); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_sharpen, GB_FLOAT val) + + double val = VARGOPT(val, 0.2); + double radius = 0.1 + val * (2.5 - 0.1); + double sigma = radius < 1 ? radius : sqrt(radius); + + QImage src(THIS); + QImage dest = KImageEffect::sharpen(src, radius, sigma); + GB.ReturnObject(dest.object()); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_charcoal, GB_FLOAT radius) + + double radius = VARGOPT(radius, -1); + + if (radius != 0) + { + if (radius < 0) + radius = 0; + QImage src(THIS); + QImage dest = KImageEffect::charcoal(src, radius, 0.5); + GB.ReturnObject(dest.object()); + } + else + return_copy(THIS); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_oil_paint, GB_FLOAT radius) + + double radius = VARGOPT(radius, -1); + + if (radius != 0) + { + if (radius < 0) + radius = 0; + QImage src(THIS); + QImage dest = KImageEffect::oilPaintConvolve(src, radius); + GB.ReturnObject(dest.object()); + } + else + return_copy(THIS); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_spread, GB_INTEGER amount) + + int amount = VARGOPT(amount, 0); + + if (amount > 0) + { + QImage src(THIS); + QImage dest = KImageEffect::spread(src, VARGOPT(amount, 3)); + GB.ReturnObject(dest.object()); + } + else + return_copy(THIS); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_shade, GB_FLOAT azimuth; GB_FLOAT elevation) + + QImage src(THIS); + QImage dest = KImageEffect::shade(src, true, VARGOPT(azimuth, M_PI/6), VARGOPT(elevation, M_PI/6)); + GB.ReturnObject(dest.object()); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_swirl, GB_FLOAT angle; GB_INTEGER background) + + QImage src(THIS); + QImage dest = KImageEffect::swirl(src, VARGOPT(angle, M_PI/3), VARGOPT(background, 0xFFFFFF) ^ 0xFF000000); + GB.ReturnObject(dest.object()); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_wave, GB_FLOAT amp; GB_FLOAT freq; GB_INTEGER background) + + QImage src(THIS); + QImage dest = KImageEffect::wave(src, VARGOPT(amp, 25.0), VARGOPT(freq, 150.0), VARGOPT(background, 0xFFFFFF) ^ 0xFF000000); + GB.ReturnObject(dest.object()); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_noise, GB_INTEGER type) + + QImage src(THIS); + QImage dest = KImageEffect::addNoise(src, (KImageEffect::NoiseType)VARG(type)); + GB.ReturnObject(dest.object()); + +END_METHOD + + +BEGIN_METHOD(CIMAGE_implode, GB_FLOAT factor; GB_INTEGER background) + + QImage src(THIS); + QImage dest = KImageEffect::implode(src, VARGOPT(factor, 1.0) * 100.0, VARGOPT(background, 0xFFFFFF) ^ 0xFF000000); + GB.ReturnObject(dest.object()); + +END_METHOD + + +BEGIN_METHOD_VOID(Image_Histogram) + + CIMAGEHISTOGRAM *hist; + int *histogram; + QImage image(THIS); + unsigned int *p, *pm; + + hist = (CIMAGEHISTOGRAM *)GB.New(GB.FindClass("ImageHistogram"), NULL, NULL); + + GB.Alloc(POINTER(&histogram), sizeof(int) * 256 * 4); + + memset(histogram, 0, 256 * 4 * sizeof(int)); + + p = (unsigned int *)image.bits(); + pm = &p[image.width() * image.height()]; + + if (image.inverted()) + { + while (p < pm) + { + histogram[qRed(*p)]++; + histogram[qGreen(*p) + 256]++; + histogram[qBlue(*p) + 256 * 2]++; + histogram[qAlpha(*p) + 256 * 3]++; + p++; + } + } + else + { + while (p < pm) + { + histogram[qBlue(*p)]++; + histogram[qGreen(*p) + 256]++; + histogram[qRed(*p) + 256 * 2]++; + histogram[qAlpha(*p) + 256 * 3]++; + p++; + } + } + + hist->histogram = histogram; + GB.ReturnObject(hist); + +END_METHOD + + +BEGIN_METHOD_VOID(ImageHistogram_free) + + GB.Free(POINTER(&THIS_HISTOGRAM->histogram)); + +END_METHOD + + +BEGIN_METHOD(ImageHistogram_get, GB_INTEGER channel; GB_INTEGER value) + + int channel; + int value; + + switch(VARG(channel)) + { + case KImageEffect::Blue: channel = 0; break; + case KImageEffect::Green: channel = 1; break; + case KImageEffect::Red: channel = 2; break; + case KImageEffect::Alpha: channel = 3; break; + default: GB.Error("Bad channel"); return; + } + + value = VARG(value); + if (value < 0 || value > 255) + { + GB.Error("Out of bounds"); + return; + } + + GB.ReturnInteger(THIS_HISTOGRAM->histogram[channel * 256 + value]); + +END_METHOD + + +GB_DESC ImageHistogramDesc[] = +{ + GB_DECLARE("ImageHistogram", sizeof(CIMAGEHISTOGRAM)), + GB_NOT_CREATABLE(), + + GB_METHOD("_free", NULL, ImageHistogram_free, NULL), + GB_METHOD("_get", "i", ImageHistogram_get, "(Channel)i(Value)i"), + + GB_END_DECLARE +}; + +GB_DESC CImageDesc[] = +{ + GB_DECLARE("Image", 0), + + GB_CONSTANT("Vertical", "i", KImageEffect::VerticalGradient), + GB_CONSTANT("Horizontal", "i", KImageEffect::HorizontalGradient), + GB_CONSTANT("Diagonal", "i", KImageEffect::DiagonalGradient), + GB_CONSTANT("Cross", "i", KImageEffect::CrossDiagonalGradient), + GB_CONSTANT("Pyramid", "i", KImageEffect::PyramidGradient), + GB_CONSTANT("Rectangle", "i", KImageEffect::RectangleGradient), + GB_CONSTANT("PipeCross", "i", KImageEffect::PipeCrossGradient), + GB_CONSTANT("Elliptic", "i", KImageEffect::EllipticGradient), + + GB_CONSTANT("All", "i", KImageEffect::All), + GB_CONSTANT("Red", "i", KImageEffect::Red), + GB_CONSTANT("Green", "i", KImageEffect::Green), + GB_CONSTANT("Blue", "i", KImageEffect::Blue), + GB_CONSTANT("Alpha", "i", KImageEffect::Alpha), + + GB_CONSTANT("Uniform", "i", KImageEffect::UniformNoise), + GB_CONSTANT("Gaussian", "i", KImageEffect::GaussianNoise), + GB_CONSTANT("Multiplicative", "i", KImageEffect::MultiplicativeGaussianNoise), + GB_CONSTANT("Impulse", "i", KImageEffect::ImpulseNoise), + GB_CONSTANT("Laplacian", "i", KImageEffect::LaplacianNoise), + GB_CONSTANT("Poisson", "i", KImageEffect::PoissonNoise), + + GB_STATIC_METHOD("Gradient", "Image", CIMAGE_gradient, "(Width)i(Height)i(SrcColor)i(DstColor)i(Orientation)i[(XDecay)f(YDecay)f]"), + + GB_METHOD("Intensity", NULL, CIMAGE_intensity, "(Value)f[(Channel)i]"), + GB_METHOD("Flatten", NULL, CIMAGE_flatten, "(DarkColor)i(BrightColor)i"), + GB_METHOD("Fade", NULL, CIMAGE_fade, "(Color)i(Value)f"), + //GB_METHOD("Gray", NULL, CIMAGE_gray, NULL), + //GB_METHOD("Desaturate", NULL, CIMAGE_desaturate, "[(Value)f]"), + GB_METHOD("Threshold", NULL, CIMAGE_threshold, "(Value)f"), + GB_METHOD("Solarize", NULL, CIMAGE_solarize, "(Value)f"), + GB_METHOD("Normalize", NULL, CIMAGE_normalize, NULL), + GB_METHOD("Equalize", NULL, CIMAGE_equalize, NULL), + GB_METHOD("Balance", NULL, CIMAGE_balance, "(Brightness)f(Contrast)f(Gamma)f[(Channel)i]"), + GB_METHOD("Invert", NULL, CIMAGE_invert, "[(Channel)i]"), + + GB_METHOD("Emboss", "Image", CIMAGE_emboss, NULL), + GB_METHOD("Edge", "Image", CIMAGE_edge, "[(Radius)f]"), + GB_METHOD("Despeckle", "Image", CIMAGE_despeckle, NULL), + GB_METHOD("Charcoal", "Image", CIMAGE_charcoal, "[(Radius)f]"), + GB_METHOD("OilPaint", "Image", CIMAGE_oil_paint, "[(Radius)f]"), + GB_METHOD("Blur", "Image", CIMAGE_blur, "[(Value)f]"), + GB_METHOD("Sharpen", "Image", CIMAGE_sharpen, "[(Value)f]"), + GB_METHOD("Spread", "Image", CIMAGE_spread, "[(Amount)i]"), + GB_METHOD("Shade", "Image", CIMAGE_shade, "[(Azimuth)f(Elevation)f]"), + GB_METHOD("Swirl", "Image", CIMAGE_swirl, "[(Angle)f(Background)i]"), + GB_METHOD("Wave", "Image", CIMAGE_wave, "[(Amplitude)f(WaveLength)f(Background)i]"), + GB_METHOD("Noise", "Image", CIMAGE_noise, "(Noise)i"), + GB_METHOD("Implode", "Image", CIMAGE_implode, "[(Factor)f(Background)i]"), + + GB_METHOD("Histogram", "ImageHistogram", Image_Histogram, NULL), + + GB_END_DECLARE +}; + diff --git a/main/lib/image.effect/CImage.h b/main/lib/image.effect/CImage.h new file mode 100644 index 00000000..282ad0ab --- /dev/null +++ b/main/lib/image.effect/CImage.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + CImage.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CIMAGE_H +#define __CIMAGE_H + +#include "gambas.h" + +#ifndef __CIMAGE_CPP + +extern GB_DESC CImageDesc[]; +extern GB_DESC ImageHistogramDesc[]; + +#else + +#define THIS ((GB_IMAGE)_object) +#define THIS_HISTOGRAM ((CIMAGEHISTOGRAM *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + int *histogram; + } + CIMAGEHISTOGRAM; + +#endif diff --git a/main/lib/image.effect/Makefile.am b/main/lib/image.effect/Makefile.am new file mode 100644 index 00000000..9798c1f9 --- /dev/null +++ b/main/lib/image.effect/Makefile.am @@ -0,0 +1,29 @@ +COMPONENT = gb.image.effect +include $(top_srcdir)/component.am + +noinst_LTLIBRARIES = libimageeffect.la +gblib_LTLIBRARIES = gb.image.effect.la + +libimageeffect_la_LIBADD = +libimageeffect_la_LDFLAGS = -module @LD_FLAGS@ +libimageeffect_la_CXXFLAGS = -I$(top_srcdir)/share $(AM_CXXFLAGS_OPT) + +libimageeffect_la_SOURCES = \ + kimageeffect.h kimageeffect.cpp \ + effect.h effect.cpp + +gb_image_effect_la_LIBADD = libimageeffect.la +gb_image_effect_la_LDFLAGS = -module @LD_FLAGS@ +gb_image_effect_la_CXXFLAGS = -I$(top_srcdir)/share $(AM_CXXFLAGS) + +gb_image_effect_la_SOURCES = \ +main.cpp main.h qt.h \ +kcpuinfo.h kcpuinfo.cpp \ +qcolor.h qcolor.cpp \ +qpoint.h qpoint.cpp \ +qsize.h qsize.cpp \ +qrect.h qrect.cpp \ +qimage.h qimage.cpp \ +CImage.h CImage.cpp + + diff --git a/main/lib/image.effect/effect.cpp b/main/lib/image.effect/effect.cpp new file mode 100644 index 00000000..54158758 --- /dev/null +++ b/main/lib/image.effect/effect.cpp @@ -0,0 +1,153 @@ +/*************************************************************************** + + effect.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __EFFECT_CPP + +#include + +#include "effect.h" +#include "main.h" + +static void get_info(GB_IMAGE img, uint **data, uint *width, uint *height, uint *npixels, bool *inv) +{ + GB_IMG *info = (GB_IMG *)img; + + SYNCHRONIZE_IMAGE(info); + + *data = (uint *)info->data; + if (width) *width = info->width; + if (height) *height = info->height; + if (npixels) *npixels = info->width * info->height; + if (inv) *inv = GB_IMAGE_FMT_IS_SWAPPED(info->format); //(info->format == GB_IMAGE_RGBA || info->format == GB_IMAGE_RGBX); +} + +static inline int between0And255 (int val) +{ + if (val < 0) + return 0; + else if (val > 255) + return 255; + else + return val; +} + +static inline uint RGBA(int r, int g, int b, int a) +{ + return ((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); +} + +static inline int R(uint rgb ) { return (int)((rgb >> 16) & 0xff); } +static inline int G(uint rgb ) { return (int)((rgb >> 8) & 0xff); } +static inline int B(uint rgb ) { return (int)(rgb & 0xff); } +static inline int A(uint rgb ) { return (int)((rgb >> 24) & 0xff); } + +static inline uint invert(uint col) +{ + return ((col & 0xFF)) << 16 | ((col & 0xFF0000)) >> 16 | (col & 0xFF00FF00); +} + +static inline int myRound(double d) +{ + return d >= 0.0 ? int(d + 0.5) : int( d - ((int)d-1) + 0.5 ) + ((int)d-1); +} + +static inline int brightness (int base, int strength) +{ + return between0And255(base + strength * 255 / 50); +} + +static inline int contrast (int base, int strength) +{ + return between0And255 ((base - 127) * (strength + 50) / 50 + 127); +} + +static inline int gamma (int base, int strength) +{ + return between0And255 (myRound(255.0 * pow (base / 255.0, 1.0 / pow (10, strength / 50.0)))); +} + +static inline int brightnessContrastGamma(int base, int newBrightness, int newContrast, int newGamma) +{ + return gamma(contrast(brightness(base, newBrightness), newContrast), newGamma); +} + +void Effect::balance(GB_IMAGE img, int channels, int brightness, int contrast, int gamma) +{ + uchar transformRed[256], transformGreen[256], transformBlue[256]; + uint i; + uint np; + uint *p; + bool inv; + uint col; + + get_info(img, &p, NULL, NULL, &np, &inv); + + if (!inv) + { + for (i = 0; i < 256; i++) + { + uchar applied = brightnessContrastGamma(i, brightness, contrast, gamma); + + transformRed[i] = (channels & Red) ? applied : i; + transformGreen[i] = (channels & Green) ? applied : i; + transformBlue[i] = (channels & Blue) ? applied : i; + } + } + else + { + for (i = 0; i < 256; i++) + { + uchar applied = brightnessContrastGamma(i, brightness, contrast, gamma); + + transformBlue[i] = (channels & Red) ? applied : i; + transformGreen[i] = (channels & Green) ? applied : i; + transformRed[i] = (channels & Blue) ? applied : i; + } + } + + for (i = 0; i < np; i++) + { + col = p[i]; + p[i] = RGBA(transformRed[R(col)], transformGreen[G(col)], transformBlue[B(col)], A(col)); + } +} + +void Effect::invert(GB_IMAGE img, int channels) +{ + uint np; + uint *p; + bool inv; + uint mask; + uint i; + + get_info(img, &p, NULL, NULL, &np, &inv); + + if (!inv) + mask = RGBA((channels & Red) ? 0xFF : 0, (channels & Green) ? 0xFF : 0, (channels & Blue) ? 0xFF : 0, 0); + else + mask = RGBA((channels & Blue) ? 0xFF : 0, (channels & Green) ? 0xFF : 0, (channels & Red) ? 0xFF : 0, 0); + + for (i = 0; i < np; i++) + p[i] ^= mask; +} + diff --git a/main/lib/image.effect/effect.h b/main/lib/image.effect/effect.h new file mode 100644 index 00000000..f82190a1 --- /dev/null +++ b/main/lib/image.effect/effect.h @@ -0,0 +1,44 @@ +/*************************************************************************** + + effect.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __EFFECT_H +#define __EFFECT_H + +#include "main.h" + +class Effect +{ +public: + + enum { + Red = 1, + Green = 2, + Blue = 4, + All = 7 + }; + + static void balance(GB_IMAGE img, int channels, int brightness, int contrast, int gamma); + static void invert(GB_IMAGE img, int channels); +}; + +#endif diff --git a/main/lib/image.effect/gb.image.effect.component b/main/lib/image.effect/gb.image.effect.component new file mode 100644 index 00000000..f0c721ea --- /dev/null +++ b/main/lib/image.effect/gb.image.effect.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.image.component +Name=Image processing component +Author=Benoît Minisini +Requires=gb.image diff --git a/main/lib/image.effect/kcpuinfo.cpp b/main/lib/image.effect/kcpuinfo.cpp new file mode 100644 index 00000000..44726a2e --- /dev/null +++ b/main/lib/image.effect/kcpuinfo.cpp @@ -0,0 +1,212 @@ +/*************************************************************************** + + kcpuinfo.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* + * This file is part of the KDE libraries + * Copyright (C) 2003 Fredrik H�glund + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include "kcpuinfo.h" + + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) +# define HAVE_GNU_INLINE_ASM +#endif + + +/* +// Copied from kdecore/kglobal.h +#if __GNUC__ - 0 > 3 || (__GNUC__ - 0 == 3 && __GNUC_MINOR__ - 0 > 2) +# define KDE_NO_EXPORT __attribute__ ((visibility("hidden"))) +#else +# define KDE_NO_EXPORT +#endif +*/ +#define KDE_NO_EXPORT + +typedef void (*kde_sighandler_t) (int); + +/* +#ifdef __i386__ +static jmp_buf KDE_NO_EXPORT env; + +// Sighandler for the SSE OS support check +static void KDE_NO_EXPORT sighandler( int ) +{ + std::longjmp( env, 1 ); +} +#endif + +#ifdef __PPC__ +static sigjmp_buf KDE_NO_EXPORT jmpbuf; +static sig_atomic_t KDE_NO_EXPORT canjump = 0; + +static void KDE_NO_EXPORT sigill_handler( int sig ) +{ + if ( !canjump ) { + signal( sig, SIG_DFL ); + raise( sig ); + } + canjump = 0; + siglongjmp( jmpbuf, 1 ); +} +#endif +*/ + +static int KDE_NO_EXPORT getCpuFeatures() +{ + int features = 0; + +#if defined( HAVE_GNU_INLINE_ASM ) +#if defined( __i386__ ) + bool haveCPUID = false; + bool have3DNOW = false; + int result = 0; + + // First check if the CPU supports the CPUID instruction + __asm__ __volatile__( + // Try to toggle the CPUID bit in the EFLAGS register + "pushf \n\t" // Push the EFLAGS register onto the stack + "popl %%ecx \n\t" // Pop the value into ECX + "movl %%ecx, %%edx \n\t" // Copy ECX to EDX + "xorl $0x00200000, %%ecx \n\t" // Toggle bit 21 (CPUID) in ECX + "pushl %%ecx \n\t" // Push the modified value onto the stack + "popf \n\t" // Pop it back into EFLAGS + + // Check if the CPUID bit was successfully toggled + "pushf \n\t" // Push EFLAGS back onto the stack + "popl %%ecx \n\t" // Pop the value into ECX + "xorl %%eax, %%eax \n\t" // Zero out the EAX register + "cmpl %%ecx, %%edx \n\t" // Compare ECX with EDX + "je .Lno_cpuid_support%= \n\t" // Jump if they're identical + "movl $1, %%eax \n\t" // Set EAX to true + ".Lno_cpuid_support%=: \n\t" + : "=a"(haveCPUID) : : "%ecx", "%edx" ); + + // If we don't have CPUID we won't have the other extensions either + if ( ! haveCPUID ) + return 0L; + + // Execute CPUID with the feature request bit set + __asm__ __volatile__( + "pushl %%ebx \n\t" // Save EBX + "movl $1, %%eax \n\t" // Set EAX to 1 (features request) + "cpuid \n\t" // Call CPUID + "popl %%ebx \n\t" // Restore EBX + : "=d"(result) : : "%eax", "%ecx" ); + + // Test bit 23 (MMX support) + if ( result & 0x00800000 ) + features |= KCPUInfo::IntelMMX; + + __asm__ __volatile__( + "pushl %%ebx \n\t" + "movl $0x80000000, %%eax \n\t" + "cpuid \n\t" + "cmpl $0x80000000, %%eax \n\t" + "jbe .Lno_extended%= \n\t" + "movl $0x80000001, %%eax \n\t" + "cpuid \n\t" + "test $0x80000000, %%edx \n\t" + "jz .Lno_extended%= \n\t" + "movl $1, %%eax \n\t" // // Set EAX to true + ".Lno_extended%=: \n\t" + "popl %%ebx \n\t" // Restore EBX + : "=a"(have3DNOW) : ); + + if ( have3DNOW ) + features |= KCPUInfo::AMD3DNOW; + +#ifdef HAVE_X86_SSE + // Test bit 25 (SSE support) + if ( result & 0x00200000 ) { + features |= KCPUInfo::IntelSSE; + + // OS support test for SSE. + // Install our own sighandler for SIGILL. + kde_sighandler_t oldhandler = std::signal( SIGILL, sighandler ); + + // Try executing an SSE insn to see if we get a SIGILL + if ( setjmp( env ) ) + features ^= KCPUInfo::IntelSSE; // The OS support test failed + else + __asm__ __volatile__("xorps %xmm0, %xmm0"); + + // Restore the default sighandler + std::signal( SIGILL, oldhandler ); + + // Test bit 26 (SSE2 support) + if ( (result & 0x00400000) && (features & KCPUInfo::IntelSSE) ) + features |= KCPUInfo::IntelSSE2; + + // Note: The OS requirements for SSE2 are the same as for SSE + // so we don't have to do any additional tests for that. + } +#endif // HAVE_X86_SSE +#elif defined __PPC__ && defined HAVE_PPC_ALTIVEC + signal( SIGILL, sigill_handler ); + if ( sigsetjmp( jmpbuf, 1 ) ) { + signal( SIGILL, SIG_DFL ); + } else { + canjump = 1; + __asm__ __volatile__( "mtspr 256, %0\n\t" + "vand %%v0, %%v0, %%v0" + : /* none */ + : "r" (-1) ); + signal( SIGILL, SIG_DFL ); + features |= KCPUInfo::AltiVec; + } +#endif // __i386__ +#endif //HAVE_GNU_INLINE_ASM + + return features; +} + +unsigned int KCPUInfo::s_features = getCpuFeatures(); + + diff --git a/main/lib/image.effect/kcpuinfo.h b/main/lib/image.effect/kcpuinfo.h new file mode 100644 index 00000000..89c43a5a --- /dev/null +++ b/main/lib/image.effect/kcpuinfo.h @@ -0,0 +1,92 @@ +/*************************************************************************** + + kcpuinfo.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* + * This file is part of the KDE libraries + * Copyright (C) 2003 Fredrik Höglund + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __KCPUINFO_H +#define __KCPUINFO_H + + +/** + * This class provides a means for applications to obtain information at + * runtime about processor support for certain architecture extensions, + * such as MMX, SSE, 3DNow and AltiVec. + * + * @since 3.2 + */ +class KCPUInfo +{ + public: + /** + * This enum contains the list of architecture extensions you + * can query. + */ + enum Extensions { + IntelMMX = 1 << 0, //!< Intel's MMX instructions. + IntelSSE = 1 << 1, //!< Intel's SSE instructions. + IntelSSE2 = 1 << 2, //!< Intel's SSE2 instructions. + AMD3DNOW = 1 << 3, //!< AMD 3DNOW instructions + AltiVec = 1 << 4 //!< Motorola AltiVec instructions + }; + + /** + * Returns true if the processor supports @p extension, + * and false otherwise. + * + * @param extension the feature to query. + * @return If true, the processor supports @p extension. + * @see Extensions + */ + static bool haveExtension( unsigned int extension ) + { return s_features & extension; } + + private: + static unsigned int s_features; +}; + +#endif + diff --git a/main/lib/image.effect/kimageeffect.cpp b/main/lib/image.effect/kimageeffect.cpp new file mode 100644 index 00000000..8dfc16b2 --- /dev/null +++ b/main/lib/image.effect/kimageeffect.cpp @@ -0,0 +1,5043 @@ +/*************************************************************************** + + kimageeffect.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* This file is part of the KDE libraries + Copyright (C) 1998, 1999, 2001, 2002 Daniel M. Duley + (C) 1998, 1999 Christian Tibirna + (C) 1998, 1999 Dirk A. Mueller + (C) 1999 Geert Jansen + (C) 2000 Josef Weidendorfer + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +// $Id: kimageeffect.cpp,v 1.50.2.1 2004/01/23 19:04:06 orlovich Exp $ + +#include +#include + +#include +#include + +#include "qcolor.h" +#include "qimage.h" +#include "kimageeffect.h" +#include "kcpuinfo.h" + +#include + +#if 0 +#if defined(__i386__) && ( defined(__GNUC__) || defined(__INTEL_COMPILER) ) +# if defined( HAVE_X86_MMX ) +# define USE_MMX_INLINE_ASM +# endif +# if defined( HAVE_X86_SSE2 ) +# define USE_SSE2_INLINE_ASM +# endif +#endif +#endif + +//====================================================================== +// +// Utility stuff for effects ported from ImageMagick to QImage +// +//====================================================================== +#define MaxRGB 255L +#define DegreesToRadians(x) ((x)*M_PI/180.0) +#define MagickSQ2PI 2.50662827463100024161235523934010416269302368164062 +#define MagickEpsilon 1.0e-12 +#define MagickPI 3.14159265358979323846264338327950288419716939937510 + +static inline unsigned int intensityValue(unsigned int color) +{ + return((unsigned int)((0.299*qRed(color) + + 0.587*qGreen(color) + + 0.1140000000000001*qBlue(color)))); +} + +/*static inline void liberateMemory(void **memory) +{ + assert(memory != (void **)NULL); + if(*memory == (void *)NULL) return; + free(*memory); + *memory=(void *) NULL; +}*/ +#define liberateMemory(_pmemory) \ +({ \ + free(*(_pmemory)); \ + *(_pmemory) = NULL; \ +}) + +struct double_packet +{ + double red; + double green; + double blue; + double alpha; +}; + +struct short_packet +{ + unsigned short int red; + unsigned short int green; + unsigned short int blue; + unsigned short int alpha; +}; + + +// Functions for managing BGRA images + +static inline uint invert(uint col) +{ + return ((col & 0xFF)) << 16 | ((col & 0xFF0000)) >> 16 | (col & 0xFF00FF00); +} + +#define INVERT(_exp) \ + if (image.inverted()) \ + { \ + _exp = invert(_exp); \ + } + +//====================================================================== +// +// Gradient effects +// +//====================================================================== + +QImage KImageEffect::gradient(const QSize &size, const QColor &ca, + const QColor &cb, GradientType eff, int ncols) +{ + int rDiff, gDiff, bDiff; + int rca, gca, bca, rcb, gcb, bcb; + + QImage image(size, false); + + if (size.width() == 0 || size.height() == 0) { +#ifndef NDEBUG + std::cerr << "WARNING: KImageEffect::gradient: invalid image" << std::endl; +#endif + return image; + } + + register int x, y; + + rDiff = (rcb = cb.red()) - (rca = ca.red()); + gDiff = (gcb = cb.green()) - (gca = ca.green()); + bDiff = (bcb = cb.blue()) - (bca = ca.blue()); + + if( eff == VerticalGradient || eff == HorizontalGradient ){ + + uint *p; + uint rgb; + + register int rl = rca << 16; + register int gl = gca << 16; + register int bl = bca << 16; + + if( eff == VerticalGradient ) { + + int rcdelta = ((1<<16) / size.height()) * rDiff; + int gcdelta = ((1<<16) / size.height()) * gDiff; + int bcdelta = ((1<<16) / size.height()) * bDiff; + + for ( y = 0; y < size.height(); y++ ) { + p = (uint *) image.scanLine(y); + + rl += rcdelta; + gl += gcdelta; + bl += bcdelta; + + rgb = qRgb( (rl>>16), (gl>>16), (bl>>16) ); + INVERT(rgb); + + for( x = 0; x < size.width(); x++ ) { + *p = rgb; + p++; + } + } + + } + else { // must be HorizontalGradient + + unsigned int *o_src = (unsigned int *)image.scanLine(0); + unsigned int *src = o_src; + uint rgb; + + int rcdelta = ((1<<16) / size.width()) * rDiff; + int gcdelta = ((1<<16) / size.width()) * gDiff; + int bcdelta = ((1<<16) / size.width()) * bDiff; + + for( x = 0; x < size.width(); x++) { + + rl += rcdelta; + gl += gcdelta; + bl += bcdelta; + + rgb = qRgb( (rl>>16), (gl>>16), (bl>>16)); + INVERT(rgb); + + *src++ = rgb; + } + + src = o_src; + + // Believe it or not, manually copying in a for loop is faster + // than calling memcpy for each scanline (on the order of ms...). + // I think this is due to the function call overhead (mosfet). + + for (y = 1; y < size.height(); ++y) { + + p = (unsigned int *)image.scanLine(y); + src = o_src; + for(x=0; x < size.width(); ++x) + *p++ = *src++; + } + } + } + + else { + + float rfd, gfd, bfd; + float rd = rca, gd = gca, bd = bca; + + unsigned char *xtable[3]; + unsigned char *ytable[3]; + + unsigned int w = size.width(), h = size.height(); + xtable[0] = new unsigned char[w]; + xtable[1] = new unsigned char[w]; + xtable[2] = new unsigned char[w]; + ytable[0] = new unsigned char[h]; + ytable[1] = new unsigned char[h]; + ytable[2] = new unsigned char[h]; + w*=2, h*=2; + + if ( eff == DiagonalGradient || eff == CrossDiagonalGradient) { + // Diagonal dgradient code inspired by BlackBox (mosfet) + // BlackBox dgradient is (C) Brad Hughes, and + // Mike Cole . + + rfd = (float)rDiff/w; + gfd = (float)gDiff/w; + bfd = (float)bDiff/w; + + int dir; + for (x = 0; x < size.width(); x++, rd+=rfd, gd+=gfd, bd+=bfd) { + dir = eff == DiagonalGradient? x : size.width() - x - 1; + xtable[0][dir] = (unsigned char) rd; + xtable[1][dir] = (unsigned char) gd; + xtable[2][dir] = (unsigned char) bd; + } + rfd = (float)rDiff/h; + gfd = (float)gDiff/h; + bfd = (float)bDiff/h; + rd = gd = bd = 0; + for (y = 0; y < size.height(); y++, rd+=rfd, gd+=gfd, bd+=bfd) { + ytable[0][y] = (unsigned char) rd; + ytable[1][y] = (unsigned char) gd; + ytable[2][y] = (unsigned char) bd; + } + + for (y = 0; y < size.height(); y++) { + unsigned int *scanline = (unsigned int *)image.scanLine(y); + for (x = 0; x < size.width(); x++) { + scanline[x] = qRgb(xtable[0][x] + ytable[0][y], + xtable[1][x] + ytable[1][y], + xtable[2][x] + ytable[2][y]); + INVERT(scanline[x]); + } + } + } + + else if (eff == RectangleGradient || + eff == PyramidGradient || + eff == PipeCrossGradient || + eff == EllipticGradient) + { + int rSign = rDiff>0? 1: -1; + int gSign = gDiff>0? 1: -1; + int bSign = bDiff>0? 1: -1; + + rfd = (float)rDiff / size.width(); + gfd = (float)gDiff / size.width(); + bfd = (float)bDiff / size.width(); + + rd = (float)rDiff/2; + gd = (float)gDiff/2; + bd = (float)bDiff/2; + + for (x = 0; x < size.width(); x++, rd-=rfd, gd-=gfd, bd-=bfd) + { + xtable[0][x] = (unsigned char) abs((int)rd); + xtable[1][x] = (unsigned char) abs((int)gd); + xtable[2][x] = (unsigned char) abs((int)bd); + } + + rfd = (float)rDiff/size.height(); + gfd = (float)gDiff/size.height(); + bfd = (float)bDiff/size.height(); + + rd = (float)rDiff/2; + gd = (float)gDiff/2; + bd = (float)bDiff/2; + + for (y = 0; y < size.height(); y++, rd-=rfd, gd-=gfd, bd-=bfd) + { + ytable[0][y] = (unsigned char) abs((int)rd); + ytable[1][y] = (unsigned char) abs((int)gd); + ytable[2][y] = (unsigned char) abs((int)bd); + } + unsigned int rgb; + int h = (size.height()+1)>>1; + for (y = 0; y < h; y++) { + unsigned int *sl1 = (unsigned int *)image.scanLine(y); + unsigned int *sl2 = (unsigned int *)image.scanLine(QMAX(size.height()-y-1, y)); + + int w = (size.width()+1)>>1; + int x2 = size.width()-1; + + for (x = 0; x < w; x++, x2--) { + rgb = 0; + if (eff == PyramidGradient) { + rgb = qRgb(rcb-rSign*(xtable[0][x]+ytable[0][y]), + gcb-gSign*(xtable[1][x]+ytable[1][y]), + bcb-bSign*(xtable[2][x]+ytable[2][y])); + } + else if (eff == RectangleGradient) { + rgb = qRgb(rcb - rSign * + QMAX(xtable[0][x], ytable[0][y]) * 2, + gcb - gSign * + QMAX(xtable[1][x], ytable[1][y]) * 2, + bcb - bSign * + QMAX(xtable[2][x], ytable[2][y]) * 2); + } + else if (eff == PipeCrossGradient) { + rgb = qRgb(rcb - rSign * + QMIN(xtable[0][x], ytable[0][y]) * 2, + gcb - gSign * + QMIN(xtable[1][x], ytable[1][y]) * 2, + bcb - bSign * + QMIN(xtable[2][x], ytable[2][y]) * 2); + } + else if (eff == EllipticGradient) { + rgb = qRgb(rcb - rSign * + (int)sqrt((xtable[0][x]*xtable[0][x] + + ytable[0][y]*ytable[0][y])*2.0), + gcb - gSign * + (int)sqrt((xtable[1][x]*xtable[1][x] + + ytable[1][y]*ytable[1][y])*2.0), + bcb - bSign * + (int)sqrt((xtable[2][x]*xtable[2][x] + + ytable[2][y]*ytable[2][y])*2.0)); + } + + //INVERT(rgb); + sl1[x] = sl2[x] = rgb; + sl1[x2] = sl2[x2] = rgb; + } + } + + if (image.inverted()) + image.invert(); + } + + delete [] xtable[0]; + delete [] xtable[1]; + delete [] xtable[2]; + delete [] ytable[0]; + delete [] ytable[1]; + delete [] ytable[2]; + } + +#if 0 + // dither if necessary + if (ncols && (QPixmap::defaultDepth() < 15 )) { + if ( ncols < 2 || ncols > 256 ) + ncols = 3; + QColor *dPal = new QColor[ncols]; + for (int i=0; i 200 ) xfactor = 200; + if (yfactor > 200 ) yfactor = 200; + + + // float xbal = xfactor/5000.; + // float ybal = yfactor/5000.; + float xbal = xfactor/30./size.width(); + float ybal = yfactor/30./size.height(); + float rat; + + int rDiff, gDiff, bDiff; + int rca, gca, bca, rcb, gcb, bcb; + + QImage image(size, false); + + if (size.width() == 0 || size.height() == 0) { +#ifndef NDEBUG + std::cerr << "WARNING: KImageEffect::unbalancedGradient : invalid image\n"; +#endif + return image; + } + + register int x, y; + unsigned int *scanline; + + rDiff = (rcb = cb.red()) - (rca = ca.red()); + gDiff = (gcb = cb.green()) - (gca = ca.green()); + bDiff = (bcb = cb.blue()) - (bca = ca.blue()); + + if( eff == VerticalGradient || eff == HorizontalGradient){ + QColor cRow; + + uint *p; + uint rgbRow; + + if( eff == VerticalGradient) { + for ( y = 0; y < size.height(); y++ ) { + dir = _yanti ? y : size.height() - 1 - y; + p = (uint *) image.scanLine(dir); + rat = 1 - exp( - (float)y * ybal ); + + cRow.setRgb( rcb - (int) ( rDiff * rat ), + gcb - (int) ( gDiff * rat ), + bcb - (int) ( bDiff * rat ) ); + + rgbRow = cRow.rgb(); + INVERT(rgbRow); + + for( x = 0; x < size.width(); x++ ) { + *p = rgbRow; + p++; + } + } + } + else { + + unsigned int *src = (unsigned int *)image.scanLine(0); + for(x = 0; x < size.width(); x++ ) + { + dir = _xanti ? x : size.width() - 1 - x; + rat = 1 - exp( - (float)x * xbal ); + + src[dir] = qRgb(rcb - (int) ( rDiff * rat ), + gcb - (int) ( gDiff * rat ), + bcb - (int) ( bDiff * rat )); + INVERT(src[dir]); + } + + // Believe it or not, manually copying in a for loop is faster + // than calling memcpy for each scanline (on the order of ms...). + // I think this is due to the function call overhead (mosfet). + + for(y = 1; y < size.height(); ++y) + { + scanline = (unsigned int *)image.scanLine(y); + for(x=0; x < size.width(); ++x) + scanline[x] = src[x]; + } + } + } + + else { + int w=size.width(), h=size.height(); + + unsigned char *xtable[3]; + unsigned char *ytable[3]; + xtable[0] = new unsigned char[w]; + xtable[1] = new unsigned char[w]; + xtable[2] = new unsigned char[w]; + ytable[0] = new unsigned char[h]; + ytable[1] = new unsigned char[h]; + ytable[2] = new unsigned char[h]; + + if ( eff == DiagonalGradient || eff == CrossDiagonalGradient) + { + for (x = 0; x < w; x++) { + dir = _xanti ? x : w - 1 - x; + rat = 1 - exp( - (float)x * xbal ); + + xtable[0][dir] = (unsigned char) ( rDiff/2 * rat ); + xtable[1][dir] = (unsigned char) ( gDiff/2 * rat ); + xtable[2][dir] = (unsigned char) ( bDiff/2 * rat ); + } + + for (y = 0; y < h; y++) { + dir = _yanti ? y : h - 1 - y; + rat = 1 - exp( - (float)y * ybal ); + + ytable[0][dir] = (unsigned char) ( rDiff/2 * rat ); + ytable[1][dir] = (unsigned char) ( gDiff/2 * rat ); + ytable[2][dir] = (unsigned char) ( bDiff/2 * rat ); + } + + for (y = 0; y < h; y++) { + unsigned int *scanline = (unsigned int *)image.scanLine(y); + for (x = 0; x < w; x++) { + scanline[x] = qRgb(rcb - (xtable[0][x] + ytable[0][y]), + gcb - (xtable[1][x] + ytable[1][y]), + bcb - (xtable[2][x] + ytable[2][y])); + INVERT(scanline[x]); + } + } + } + + else if (eff == RectangleGradient || + eff == PyramidGradient || + eff == PipeCrossGradient || + eff == EllipticGradient) + { + int rSign = rDiff>0? 1: -1; + int gSign = gDiff>0? 1: -1; + int bSign = bDiff>0? 1: -1; + + for (x = 0; x < w; x++) + { + dir = _xanti ? x : w - 1 - x; + rat = 1 - exp( - (float)x * xbal ); + + xtable[0][dir] = (unsigned char) abs((int)(rDiff*(0.5-rat))); + xtable[1][dir] = (unsigned char) abs((int)(gDiff*(0.5-rat))); + xtable[2][dir] = (unsigned char) abs((int)(bDiff*(0.5-rat))); + } + + for (y = 0; y < h; y++) + { + dir = _yanti ? y : h - 1 - y; + + rat = 1 - exp( - (float)y * ybal ); + + ytable[0][dir] = (unsigned char) abs((int)(rDiff*(0.5-rat))); + ytable[1][dir] = (unsigned char) abs((int)(gDiff*(0.5-rat))); + ytable[2][dir] = (unsigned char) abs((int)(bDiff*(0.5-rat))); + } + + for (y = 0; y < h; y++) { + unsigned int *scanline = (unsigned int *)image.scanLine(y); + for (x = 0; x < w; x++) { + if (eff == PyramidGradient) + { + scanline[x] = qRgb(rcb-rSign*(xtable[0][x]+ytable[0][y]), + gcb-gSign*(xtable[1][x]+ytable[1][y]), + bcb-bSign*(xtable[2][x]+ytable[2][y])); + } + if (eff == RectangleGradient) + { + scanline[x] = qRgb(rcb - rSign * + QMAX(xtable[0][x], ytable[0][y]) * 2, + gcb - gSign * + QMAX(xtable[1][x], ytable[1][y]) * 2, + bcb - bSign * + QMAX(xtable[2][x], ytable[2][y]) * 2); + } + if (eff == PipeCrossGradient) + { + scanline[x] = qRgb(rcb - rSign * + QMIN(xtable[0][x], ytable[0][y]) * 2, + gcb - gSign * + QMIN(xtable[1][x], ytable[1][y]) * 2, + bcb - bSign * + QMIN(xtable[2][x], ytable[2][y]) * 2); + } + if (eff == EllipticGradient) + { + scanline[x] = qRgb(rcb - rSign * + (int)sqrt((xtable[0][x]*xtable[0][x] + + ytable[0][y]*ytable[0][y])*2.0), + gcb - gSign * + (int)sqrt((xtable[1][x]*xtable[1][x] + + ytable[1][y]*ytable[1][y])*2.0), + bcb - bSign * + (int)sqrt((xtable[2][x]*xtable[2][x] + + ytable[2][y]*ytable[2][y])*2.0)); + } + INVERT(scanline[x]); + } + } + } + + #if 0 + if (ncols && (QPixmap::defaultDepth() < 15 )) { + if ( ncols < 2 || ncols > 256 ) + ncols = 3; + QColor *dPal = new QColor[ncols]; + for (int i=0; i 8 ? 256 : image.numColors(); + int pixels = image.depth() > 8 ? image.width()*image.height() : + image.numColors(); + unsigned int *data = image.depth() > 8 ? (unsigned int *)image.bits() : + (unsigned int *)image.colorTable(); + + bool brighten = (percent >= 0); + if(percent < 0) + percent = -percent; + + fprintf(stderr, "image: %d x %d = %d\n", image.width(), image.height(), pixels); + +#ifdef USE_MMX_INLINE_ASM + bool haveMMX = KCPUInfo::haveExtension( KCPUInfo::IntelMMX ); + + if(haveMMX) + { + Q_UINT16 p = Q_UINT16(256.0f*(percent)); + KIE4Pack mult = {{p,p,p,0}}; + + __asm__ __volatile__( + "pxor %%mm7, %%mm7\n\t" // zero mm7 for unpacking + "movq (%0), %%mm6\n\t" // copy intensity change to mm6 + : : "r"(&mult), "m"(mult)); + + unsigned int rem = pixels % 4; + pixels -= rem; + Q_UINT32 *end = ( data + pixels ); + + if (brighten) + { + while ( data != end ) { + __asm__ __volatile__( + "movq (%0), %%mm0\n\t" + "movq 8(%0), %%mm4\n\t" // copy 4 pixels of data to mm0 and mm4 + "movq %%mm0, %%mm1\n\t" + "movq %%mm0, %%mm3\n\t" + "movq %%mm4, %%mm5\n\t" // copy to registers for unpacking + "punpcklbw %%mm7, %%mm0\n\t" + "punpckhbw %%mm7, %%mm1\n\t" // unpack the two pixels from mm0 + "pmullw %%mm6, %%mm0\n\t" + "punpcklbw %%mm7, %%mm4\n\t" + "pmullw %%mm6, %%mm1\n\t" // multiply by intensity*256 + "psrlw $8, %%mm0\n\t" // divide by 256 + "pmullw %%mm6, %%mm4\n\t" + "psrlw $8, %%mm1\n\t" + "psrlw $8, %%mm4\n\t" + "packuswb %%mm1, %%mm0\n\t" // pack solution into mm0. saturates at 255 + "movq %%mm5, %%mm1\n\t" + + "punpckhbw %%mm7, %%mm1\n\t" // unpack 4th pixel in mm1 + + "pmullw %%mm6, %%mm1\n\t" + "paddusb %%mm3, %%mm0\n\t" // add intesity result to original of mm0 + "psrlw $8, %%mm1\n\t" + "packuswb %%mm1, %%mm4\n\t" // pack upper two pixels into mm4 + + "movq %%mm0, (%0)\n\t" // rewrite to memory lower two pixels + "paddusb %%mm5, %%mm4\n\t" + "movq %%mm4, 8(%0)\n\t" // rewrite upper two pixels + : : "r"(data) ); + data += 4; + } + + end += rem; + while ( data != end ) { + __asm__ __volatile__( + "movd (%0), %%mm0\n\t" // repeat above but for + "punpcklbw %%mm7, %%mm0\n\t" // one pixel at a time + "movq %%mm0, %%mm3\n\t" + "pmullw %%mm6, %%mm0\n\t" + "psrlw $8, %%mm0\n\t" + "paddw %%mm3, %%mm0\n\t" + "packuswb %%mm0, %%mm0\n\t" + "movd %%mm0, (%0)\n\t" + : : "r"(data) ); + data++; + } + } + else + { + while ( data != end ) { + __asm__ __volatile__( + "movq (%0), %%mm0\n\t" + "movq 8(%0), %%mm4\n\t" + "movq %%mm0, %%mm1\n\t" + "movq %%mm0, %%mm3\n\t" + + "movq %%mm4, %%mm5\n\t" + + "punpcklbw %%mm7, %%mm0\n\t" + "punpckhbw %%mm7, %%mm1\n\t" + "pmullw %%mm6, %%mm0\n\t" + "punpcklbw %%mm7, %%mm4\n\t" + "pmullw %%mm6, %%mm1\n\t" + "psrlw $8, %%mm0\n\t" + "pmullw %%mm6, %%mm4\n\t" + "psrlw $8, %%mm1\n\t" + "psrlw $8, %%mm4\n\t" + "packuswb %%mm1, %%mm0\n\t" + "movq %%mm5, %%mm1\n\t" + + "punpckhbw %%mm7, %%mm1\n\t" + + "pmullw %%mm6, %%mm1\n\t" + "psubusb %%mm0, %%mm3\n\t" // subtract darkening amount + "psrlw $8, %%mm1\n\t" + "packuswb %%mm1, %%mm4\n\t" + + "movq %%mm3, (%0)\n\t" + "psubusb %%mm4, %%mm5\n\t" // only change for this version is + "movq %%mm5, 8(%0)\n\t" // subtraction here as we are darkening image + : : "r"(data) ); + data += 4; + } + + end += rem; + while ( data != end ) { + __asm__ __volatile__( + "movd (%0), %%mm0\n\t" + "punpcklbw %%mm7, %%mm0\n\t" + "movq %%mm0, %%mm3\n\t" + "pmullw %%mm6, %%mm0\n\t" + "psrlw $8, %%mm0\n\t" + "psubusw %%mm0, %%mm3\n\t" + "packuswb %%mm3, %%mm3\n\t" + "movd %%mm3, (%0)\n\t" + : : "r"(data) ); + data++; + } + } + __asm__ __volatile__("emms"); // clear mmx state + } + else +#endif // USE_MMX_INLINE_ASM + { + unsigned char *segTbl = new unsigned char[segColors]; + int tmp; + if(brighten){ // keep overflow check out of loops + for(int i=0; i < segColors; ++i){ + tmp = (int)(i*percent); + if(tmp > 255) + tmp = 255; + segTbl[i] = tmp; + } + } + else{ + for(int i=0; i < segColors; ++i){ + tmp = (int)(i*percent); + if(tmp < 0) + tmp = 0; + segTbl[i] = tmp; + } + } + + if(brighten){ // same here + for(int i=0; i < pixels; ++i){ + int r = qRed(data[i]); + int g = qGreen(data[i]); + int b = qBlue(data[i]); + int a = qAlpha(data[i]); + r = r + segTbl[r] > 255 ? 255 : r + segTbl[r]; + g = g + segTbl[g] > 255 ? 255 : g + segTbl[g]; + b = b + segTbl[b] > 255 ? 255 : b + segTbl[b]; + data[i] = qRgba(r, g, b,a); + INVERT(data[i]); + } + } + else{ + for(int i=0; i < pixels; ++i){ + int r = qRed(data[i]); + int g = qGreen(data[i]); + int b = qBlue(data[i]); + int a = qAlpha(data[i]); + r = r - segTbl[r] < 0 ? 0 : r - segTbl[r]; + g = g - segTbl[g] < 0 ? 0 : g - segTbl[g]; + b = b - segTbl[b] < 0 ? 0 : b - segTbl[b]; + data[i] = qRgba(r, g, b, a); + INVERT(data[i]); + } + } + delete [] segTbl; + } + + return image; +} + +QImage& KImageEffect::channelIntensity(QImage &image, float percent, + RGBComponent channel) +{ + if (image.width() == 0 || image.height() == 0) { +#ifndef NDEBUG + std::cerr << "WARNING: KImageEffect::channelIntensity : invalid image\n"; +#endif + return image; + } + + int segColors = image.depth() > 8 ? 256 : image.numColors(); + unsigned char *segTbl = new unsigned char[segColors]; + int pixels = image.depth() > 8 ? image.width()*image.height() : + image.numColors(); + unsigned int *data = image.depth() > 8 ? (unsigned int *)image.bits() : + (unsigned int *)image.colorTable(); + bool brighten = (percent >= 0); + if(percent < 0) + percent = -percent; + + if(brighten){ // keep overflow check out of loops + for(int i=0; i < segColors; ++i){ + int tmp = (int)(i*percent); + if(tmp > 255) + tmp = 255; + segTbl[i] = tmp; + } + } + else{ + for(int i=0; i < segColors; ++i){ + int tmp = (int)(i*percent); + if(tmp < 0) + tmp = 0; + segTbl[i] = tmp; + } + } + + if(brighten){ // same here + if(channel == Red){ // and here ;-) + for(int i=0; i < pixels; ++i){ + int c = qRed(data[i]); + c = c + segTbl[c] > 255 ? 255 : c + segTbl[c]; + data[i] = qRgba(c, qGreen(data[i]), qBlue(data[i]), qAlpha(data[i])); + INVERT(data[i]); + } + } + else if(channel == Green){ + for(int i=0; i < pixels; ++i){ + int c = qGreen(data[i]); + c = c + segTbl[c] > 255 ? 255 : c + segTbl[c]; + data[i] = qRgba(qRed(data[i]), c, qBlue(data[i]), qAlpha(data[i])); + INVERT(data[i]); + } + } + else{ + for(int i=0; i < pixels; ++i){ + int c = qBlue(data[i]); + c = c + segTbl[c] > 255 ? 255 : c + segTbl[c]; + data[i] = qRgba(qRed(data[i]), qGreen(data[i]), c, qAlpha(data[i])); + INVERT(data[i]); + } + } + + } + else{ + if(channel == Red){ + for(int i=0; i < pixels; ++i){ + int c = qRed(data[i]); + c = c - segTbl[c] < 0 ? 0 : c - segTbl[c]; + data[i] = qRgba(c, qGreen(data[i]), qBlue(data[i]), qAlpha(data[i])); + INVERT(data[i]); + } + } + else if(channel == Green){ + for(int i=0; i < pixels; ++i){ + int c = qGreen(data[i]); + c = c - segTbl[c] < 0 ? 0 : c - segTbl[c]; + data[i] = qRgba(qRed(data[i]), c, qBlue(data[i]), qAlpha(data[i])); + INVERT(data[i]); + } + } + else{ + for(int i=0; i < pixels; ++i){ + int c = qBlue(data[i]); + c = c - segTbl[c] < 0 ? 0 : c - segTbl[c]; + data[i] = qRgba(qRed(data[i]), qGreen(data[i]), c, qAlpha(data[i])); + INVERT(data[i]); + } + } + } + delete [] segTbl; + + return image; +} + + +#if 0 +// Modulate an image with an RBG channel of another image +// +QImage& KImageEffect::modulate(QImage &image, QImage &modImage, bool reverse, + ModulationType type, int factor, RGBComponent channel) +{ + if (image.width() == 0 || image.height() == 0 || + modImage.width() == 0 || modImage.height() == 0) { +#ifndef NDEBUG + std::cerr << "WARNING: KImageEffect::modulate : invalid image\n"; +#endif + return image; + } + + int r, g, b, h, s, v, a; + QColor clr; + int mod=0; + unsigned int x1, x2, y1, y2; + register int x, y; + + // for image, we handle only depth 32 + if (image.depth()<32) image = image.convertDepth(32); + + // for modImage, we handle depth 8 and 32 + if (modImage.depth()<8) modImage = modImage.convertDepth(8); + + unsigned int *colorTable2 = (modImage.depth()==8) ? + modImage.colorTable():0; + unsigned int *data1, *data2; + unsigned char *data2b; + unsigned int color1, color2; + + x1 = image.width(); y1 = image.height(); + x2 = modImage.width(); y2 = modImage.height(); + + for (y = 0; y < (int)y1; y++) { + data1 = (unsigned int *) image.scanLine(y); + data2 = (unsigned int *) modImage.scanLine( y%y2 ); + data2b = (unsigned char *) modImage.scanLine( y%y2 ); + + x=0; + while(x < (int)x1) { + color2 = (colorTable2) ? colorTable2[*data2b] : *data2; + if (reverse) { + color1 = color2; + color2 = *data1; + } + else + color1 = *data1; + + if (type == Intensity || type == Contrast) { + r = qRed(color1); + g = qGreen(color1); + b = qBlue(color1); + if (channel != All) { + mod = (channel == Red) ? qRed(color2) : + (channel == Green) ? qGreen(color2) : + (channel == Blue) ? qBlue(color2) : + (channel == Gray) ? qGray(color2) : 0; + mod = mod*factor/50; + } + + if (type == Intensity) { + if (channel == All) { + r += r * factor/50 * qRed(color2)/256; + g += g * factor/50 * qGreen(color2)/256; + b += b * factor/50 * qBlue(color2)/256; + } + else { + r += r * mod/256; + g += g * mod/256; + b += b * mod/256; + } + } + else { // Contrast + if (channel == All) { + r += (r-128) * factor/50 * qRed(color2)/128; + g += (g-128) * factor/50 * qGreen(color2)/128; + b += (b-128) * factor/50 * qBlue(color2)/128; + } + else { + r += (r-128) * mod/128; + g += (g-128) * mod/128; + b += (b-128) * mod/128; + } + } + + if (r<0) r=0; if (r>255) r=255; + if (g<0) g=0; if (g>255) g=255; + if (b<0) b=0; if (b>255) b=255; + a = qAlpha(*data1); + *data1 = qRgba(r, g, b, a); + } + else if (type == Saturation || type == HueShift) { + clr.setRgb(color1); + clr.hsv(&h, &s, &v); + mod = (channel == Red) ? qRed(color2) : + (channel == Green) ? qGreen(color2) : + (channel == Blue) ? qBlue(color2) : + (channel == Gray) ? qGray(color2) : 0; + mod = mod*factor/50; + + if (type == Saturation) { + s -= s * mod/256; + if (s<0) s=0; if (s>255) s=255; + } + else { // HueShift + h += mod; + while(h<0) h+=360; + h %= 360; + } + + clr.setHsv(h, s, v); + a = qAlpha(*data1); + *data1 = clr.rgb() | ((uint)(a & 0xff) << 24); + } + data1++; data2++; data2b++; x++; + if ( (x%x2) ==0) { data2 -= x2; data2b -= x2; } + } + } + return image; +} + + + +//====================================================================== +// +// Blend effects +// +//====================================================================== + + +// Nice and fast direct pixel manipulation +QImage& KImageEffect::blend(const QColor& clr, QImage& dst, float opacity) +{ + if (dst.width() <= 0 || dst.height() <= 0) + return dst; + + if (opacity < 0.0 || opacity > 1.0) { +#ifndef NDEBUG + std::cerr << "WARNING: KImageEffect::blend : invalid opacity. Range [0, 1]\n"; +#endif + return dst; + } + + int depth = dst.depth(); + if (depth != 32) + dst = dst.convertDepth(32); + + int pixels = dst.width() * dst.height(); + +#ifdef USE_SSE2_INLINE_ASM + if ( KCPUInfo::haveExtension( KCPUInfo::IntelSSE2 ) && pixels > 16 ) { + Q_UINT16 alpha = Q_UINT16( ( 1.0 - opacity ) * 256.0 ); + + KIE8Pack packedalpha = { { alpha, alpha, alpha, 256, + alpha, alpha, alpha, 256 } }; + + Q_UINT16 red = Q_UINT16( clr.red() * 256 * opacity ); + Q_UINT16 green = Q_UINT16( clr.green() * 256 * opacity ); + Q_UINT16 blue = Q_UINT16( clr.blue() * 256 * opacity ); + + KIE8Pack packedcolor = { { blue, green, red, 0, + blue, green, red, 0 } }; + + // Prepare the XMM5, XMM6 and XMM7 registers for unpacking and blending + __asm__ __volatile__( + "pxor %%xmm7, %%xmm7\n\t" // Zero out XMM7 for unpacking + "movdqu (%0), %%xmm6\n\t" // Set up (1 - alpha) * 256 in XMM6 + "movdqu (%1), %%xmm5\n\t" // Set up color * alpha * 256 in XMM5 + : : "r"(&packedalpha), "r"(&packedcolor), "m"(packedcolor), "m"(packedalpha) ); + + Q_UINT32 *data = reinterpret_cast( dst.bits() ); + + // Check how many pixels we need to process to achieve 16 byte alignment + int offset = (16 - (Q_UINT32( data ) & 0x0f)) / 4; + + // The main loop processes 8 pixels / iteration + int remainder = (pixels - offset) % 8; + pixels -= remainder; + + // Alignment loop + for ( int i = 0; i < offset; i++ ) { + __asm__ __volatile__( + "movd (%0,%1,4), %%xmm0\n\t" // Load one pixel to XMM1 + "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel + "pmullw %%xmm6, %%xmm0\n\t" // Multiply the pixel with (1 - alpha) * 256 + "paddw %%xmm5, %%xmm0\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%xmm0\n\t" // Divide by 256 + "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword + "movd %%xmm0, (%0,%1,4)\n\t" // Write the pixel to the image + : : "r"(data), "r"(i) ); + } + + // Main loop + for ( int i = offset; i < pixels; i += 8 ) { + __asm__ __volatile( + // Load 8 pixels to XMM registers 1 - 4 + "movq (%0,%1,4), %%xmm0\n\t" // Load pixels 1 and 2 to XMM1 + "movq 8(%0,%1,4), %%xmm1\n\t" // Load pixels 3 and 4 to XMM2 + "movq 16(%0,%1,4), %%xmm2\n\t" // Load pixels 5 and 6 to XMM3 + "movq 24(%0,%1,4), %%xmm3\n\t" // Load pixels 7 and 8 to XMM4 + + // Prefetch the pixels for next iteration + "prefetchnta 32(%0,%1,4) \n\t" + + // Blend pixels 1 and 2 + "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixels + "pmullw %%xmm6, %%xmm0\n\t" // Multiply the pixels with (1 - alpha) * 256 + "paddw %%xmm5, %%xmm0\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%xmm0\n\t" // Divide by 256 + + // Blend pixels 3 and 4 + "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the pixels + "pmullw %%xmm6, %%xmm1\n\t" // Multiply the pixels with (1 - alpha) * 256 + "paddw %%xmm5, %%xmm1\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%xmm1\n\t" // Divide by 256 + + // Blend pixels 5 and 6 + "punpcklbw %%xmm7, %%xmm2\n\t" // Unpack the pixels + "pmullw %%xmm6, %%xmm2\n\t" // Multiply the pixels with (1 - alpha) * 256 + "paddw %%xmm5, %%xmm2\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%xmm2\n\t" // Divide by 256 + + // Blend pixels 7 and 8 + "punpcklbw %%xmm7, %%xmm3\n\t" // Unpack the pixels + "pmullw %%xmm6, %%xmm3\n\t" // Multiply the pixels with (1 - alpha) * 256 + "paddw %%xmm5, %%xmm3\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%xmm3\n\t" // Divide by 256 + + // Pack the pixels into 2 double quadwords + "packuswb %%xmm1, %%xmm0\n\t" // Pack pixels 1 - 4 to a double qword + "packuswb %%xmm3, %%xmm2\n\t" // Pack pixles 5 - 8 to a double qword + + // Write the pixels back to the image + "movdqa %%xmm0, (%0,%1,4)\n\t" // Store pixels 1 - 4 + "movdqa %%xmm2, 16(%0,%1,4)\n\t" // Store pixels 5 - 8 + : : "r"(data), "r"(i) ); + } + + // Cleanup loop + for ( int i = pixels; i < pixels + remainder; i++ ) { + __asm__ __volatile__( + "movd (%0,%1,4), %%xmm0\n\t" // Load one pixel to XMM1 + "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel + "pmullw %%xmm6, %%xmm0\n\t" // Multiply the pixel with (1 - alpha) * 256 + "paddw %%xmm5, %%xmm0\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%xmm0\n\t" // Divide by 256 + "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword + "movd %%xmm0, (%0,%1,4)\n\t" // Write the pixel to the image + : : "r"(data), "r"(i) ); + } + } else +#endif + +#ifdef USE_MMX_INLINE_ASM + if ( KCPUInfo::haveExtension( KCPUInfo::IntelMMX ) && pixels > 1 ) { + Q_UINT16 alpha = Q_UINT16( ( 1.0 - opacity ) * 256.0 ); + KIE4Pack packedalpha = { { alpha, alpha, alpha, 256 } }; + + Q_UINT16 red = Q_UINT16( clr.red() * 256 * opacity ); + Q_UINT16 green = Q_UINT16( clr.green() * 256 * opacity ); + Q_UINT16 blue = Q_UINT16( clr.blue() * 256 * opacity ); + + KIE4Pack packedcolor = { { blue, green, red, 0 } }; + + __asm__ __volatile__( + "pxor %%mm7, %%mm7\n\t" // Zero out MM7 for unpacking + "movq (%0), %%mm6\n\t" // Set up (1 - alpha) * 256 in MM6 + "movq (%1), %%mm5\n\t" // Set up color * alpha * 256 in MM5 + : : "r"(&packedalpha), "r"(&packedcolor), "m"(packedcolor), "m"(packedalpha) ); + + Q_UINT32 *data = reinterpret_cast( dst.bits() ); + + // The main loop processes 4 pixels / iteration + int remainder = pixels % 4; + pixels -= remainder; + + // Main loop + for ( int i = 0; i < pixels; i += 4 ) { + __asm__ __volatile__( + // Load 4 pixels to MM registers 1 - 4 + "movd (%0,%1,4), %%mm0\n\t" // Load the 1st pixel to MM0 + "movd 4(%0,%1,4), %%mm1\n\t" // Load the 2nd pixel to MM1 + "movd 8(%0,%1,4), %%mm2\n\t" // Load the 3rd pixel to MM2 + "movd 12(%0,%1,4), %%mm3\n\t" // Load the 4th pixel to MM3 + + // Blend the first pixel + "punpcklbw %%mm7, %%mm0\n\t" // Unpack the pixel + "pmullw %%mm6, %%mm0\n\t" // Multiply the pixel with (1 - alpha) * 256 + "paddw %%mm5, %%mm0\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%mm0\n\t" // Divide by 256 + + // Blend the second pixel + "punpcklbw %%mm7, %%mm1\n\t" // Unpack the pixel + "pmullw %%mm6, %%mm1\n\t" // Multiply the pixel with (1 - alpha) * 256 + "paddw %%mm5, %%mm1\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%mm1\n\t" // Divide by 256 + + // Blend the third pixel + "punpcklbw %%mm7, %%mm2\n\t" // Unpack the pixel + "pmullw %%mm6, %%mm2\n\t" // Multiply the pixel with (1 - alpha) * 256 + "paddw %%mm5, %%mm2\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%mm2\n\t" // Divide by 256 + + // Blend the fourth pixel + "punpcklbw %%mm7, %%mm3\n\t" // Unpack the pixel + "pmullw %%mm6, %%mm3\n\t" // Multiply the pixel with (1 - alpha) * 256 + "paddw %%mm5, %%mm3\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%mm3\n\t" // Divide by 256 + + // Pack the pixels into 2 quadwords + "packuswb %%mm1, %%mm0\n\t" // Pack pixels 1 and 2 to a qword + "packuswb %%mm3, %%mm2\n\t" // Pack pixels 3 and 4 to a qword + + // Write the pixels back to the image + "movq %%mm0, (%0,%1,4)\n\t" // Store pixels 1 and 2 + "movq %%mm2, 8(%0,%1,4)\n\t" // Store pixels 3 and 4 + : : "r"(data), "r"(i) ); + } + + // Cleanup loop + for ( int i = pixels; i < pixels + remainder; i++ ) { + __asm__ __volatile__( + "movd (%0,%1,4), %%mm0\n\t" // Load one pixel to MM1 + "punpcklbw %%mm7, %%mm0\n\t" // Unpack the pixel + "pmullw %%mm6, %%mm0\n\t" // Multiply the pixel with 1 - alpha * 256 + "paddw %%mm5, %%mm0\n\t" // Add color * alpha * 256 to the result + "psrlw $8, %%mm0\n\t" // Divide by 256 + "packuswb %%mm0, %%mm0\n\t" // Pack the pixel to a dword + "movd %%mm0, (%0,%1,4)\n\t" // Write the pixel to the image + : : "r"(data), "r"(i) ); + } + + // Empty the MMX state + __asm__ __volatile__("emms"); + } else +#endif // USE_MMX_INLINE_ASM + + { + int rcol, gcol, bcol; + clr.rgb(&rcol, &gcol, &bcol); + +#ifdef WORDS_BIGENDIAN // ARGB (skip alpha) + register unsigned char *data = (unsigned char *)dst.bits() + 1; +#else // BGRA + register unsigned char *data = (unsigned char *)dst.bits(); +#endif + + for (register int i=0; i 1.0) { +#ifndef NDEBUG + std::cerr << "WARNING: KImageEffect::blend : invalid opacity. Range [0, 1]\n"; +#endif + return dst; + } + + if (src.depth() != 32) src = src.convertDepth(32); + if (dst.depth() != 32) dst = dst.convertDepth(32); + + int pixels = src.width() * src.height(); + +#ifdef USE_SSE2_INLINE_ASM + if ( KCPUInfo::haveExtension( KCPUInfo::IntelSSE2 ) && pixels > 16 ) { + Q_UINT16 alpha = Q_UINT16( opacity * 256.0 ); + KIE8Pack packedalpha = { { alpha, alpha, alpha, 0, + alpha, alpha, alpha, 0 } }; + + // Prepare the XMM6 and XMM7 registers for unpacking and blending + __asm__ __volatile__( + "pxor %%xmm7, %%xmm7\n\t" // Zero out XMM7 for unpacking + "movdqu (%0), %%xmm6\n\t" // Set up alpha * 256 in XMM6 + : : "r"(&packedalpha), "m"(packedalpha) ); + + Q_UINT32 *data1 = reinterpret_cast( src.bits() ); + Q_UINT32 *data2 = reinterpret_cast( dst.bits() ); + + // Check how many pixels we need to process to achieve 16 byte alignment + int offset = (16 - (Q_UINT32( data2 ) & 0x0f)) / 4; + + // The main loop processes 4 pixels / iteration + int remainder = (pixels - offset) % 4; + pixels -= remainder; + + // Alignment loop + for ( int i = 0; i < offset; i++ ) { + __asm__ __volatile__( + "movd (%1,%2,4), %%xmm1\n\t" // Load one dst pixel to XMM1 + "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the pixel + "movd (%0,%2,4), %%xmm0\n\t" // Load one src pixel to XMM0 + "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel + "psubw %%xmm1, %%xmm0\n\t" // Subtract dst from src + "pmullw %%xmm6, %%xmm0\n\t" // Multiply the result with alpha * 256 + "psllw $8, %%xmm1\n\t" // Multiply dst with 256 + "paddw %%xmm1, %%xmm0\n\t" // Add dst to result + "psrlw $8, %%xmm0\n\t" // Divide by 256 + "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword + "movd %%xmm0, (%1,%2,4)\n\t" // Write the pixel to the image + : : "r"(data1), "r"(data2), "r"(i) ); + } + + // Main loop + for ( int i = offset; i < pixels; i += 4 ) { + __asm__ __volatile__( + // Load 4 src pixels to XMM0 and XMM2 and 4 dst pixels to XMM1 and XMM3 + "movq (%0,%2,4), %%xmm0\n\t" // Load two src pixels to XMM0 + "movq (%1,%2,4), %%xmm1\n\t" // Load two dst pixels to XMM1 + "movq 8(%0,%2,4), %%xmm2\n\t" // Load two src pixels to XMM2 + "movq 8(%1,%2,4), %%xmm3\n\t" // Load two dst pixels to XMM3 + + // Prefetch the pixels for the iteration after the next one + "prefetchnta 32(%0,%2,4) \n\t" + "prefetchnta 32(%1,%2,4) \n\t" + + // Blend the first two pixels + "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the dst pixels + "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the src pixels + "psubw %%xmm1, %%xmm0\n\t" // Subtract dst from src + "pmullw %%xmm6, %%xmm0\n\t" // Multiply the result with alpha * 256 + "psllw $8, %%xmm1\n\t" // Multiply dst with 256 + "paddw %%xmm1, %%xmm0\n\t" // Add dst to the result + "psrlw $8, %%xmm0\n\t" // Divide by 256 + + // Blend the next two pixels + "punpcklbw %%xmm7, %%xmm3\n\t" // Unpack the dst pixels + "punpcklbw %%xmm7, %%xmm2\n\t" // Unpack the src pixels + "psubw %%xmm3, %%xmm2\n\t" // Subtract dst from src + "pmullw %%xmm6, %%xmm2\n\t" // Multiply the result with alpha * 256 + "psllw $8, %%xmm3\n\t" // Multiply dst with 256 + "paddw %%xmm3, %%xmm2\n\t" // Add dst to the result + "psrlw $8, %%xmm2\n\t" // Divide by 256 + + // Write the pixels back to the image + "packuswb %%xmm2, %%xmm0\n\t" // Pack the pixels to a double qword + "movdqa %%xmm0, (%1,%2,4)\n\t" // Store the pixels + : : "r"(data1), "r"(data2), "r"(i) ); + } + + // Cleanup loop + for ( int i = pixels; i < pixels + remainder; i++ ) { + __asm__ __volatile__( + "movd (%1,%2,4), %%xmm1\n\t" // Load one dst pixel to XMM1 + "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the pixel + "movd (%0,%2,4), %%xmm0\n\t" // Load one src pixel to XMM0 + "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel + "psubw %%xmm1, %%xmm0\n\t" // Subtract dst from src + "pmullw %%xmm6, %%xmm0\n\t" // Multiply the result with alpha * 256 + "psllw $8, %%xmm1\n\t" // Multiply dst with 256 + "paddw %%xmm1, %%xmm0\n\t" // Add dst to result + "psrlw $8, %%xmm0\n\t" // Divide by 256 + "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword + "movd %%xmm0, (%1,%2,4)\n\t" // Write the pixel to the image + : : "r"(data1), "r"(data2), "r"(i) ); + } + } else +#endif // USE_SSE2_INLINE_ASM + +#ifdef USE_MMX_INLINE_ASM + if ( KCPUInfo::haveExtension( KCPUInfo::IntelMMX ) && pixels > 1 ) { + Q_UINT16 alpha = Q_UINT16( opacity * 256.0 ); + KIE4Pack packedalpha = { { alpha, alpha, alpha, 0 } }; + + // Prepare the MM6 and MM7 registers for blending and unpacking + __asm__ __volatile__( + "pxor %%mm7, %%mm7\n\t" // Zero out MM7 for unpacking + "movq (%0), %%mm6\n\t" // Set up alpha * 256 in MM6 + : : "r"(&packedalpha), "m"(packedalpha) ); + + Q_UINT32 *data1 = reinterpret_cast( src.bits() ); + Q_UINT32 *data2 = reinterpret_cast( dst.bits() ); + + // The main loop processes 2 pixels / iteration + int remainder = pixels % 2; + pixels -= remainder; + + // Main loop + for ( int i = 0; i < pixels; i += 2 ) { + __asm__ __volatile__( + // Load 2 src pixels to MM0 and MM2 and 2 dst pixels to MM1 and MM3 + "movd (%0,%2,4), %%mm0\n\t" // Load the 1st src pixel to MM0 + "movd (%1,%2,4), %%mm1\n\t" // Load the 1st dst pixel to MM1 + "movd 4(%0,%2,4), %%mm2\n\t" // Load the 2nd src pixel to MM2 + "movd 4(%1,%2,4), %%mm3\n\t" // Load the 2nd dst pixel to MM3 + + // Blend the first pixel + "punpcklbw %%mm7, %%mm0\n\t" // Unpack the src pixel + "punpcklbw %%mm7, %%mm1\n\t" // Unpack the dst pixel + "psubw %%mm1, %%mm0\n\t" // Subtract dst from src + "pmullw %%mm6, %%mm0\n\t" // Multiply the result with alpha * 256 + "psllw $8, %%mm1\n\t" // Multiply dst with 256 + "paddw %%mm1, %%mm0\n\t" // Add dst to the result + "psrlw $8, %%mm0\n\t" // Divide by 256 + + // Blend the second pixel + "punpcklbw %%mm7, %%mm2\n\t" // Unpack the src pixel + "punpcklbw %%mm7, %%mm3\n\t" // Unpack the dst pixel + "psubw %%mm3, %%mm2\n\t" // Subtract dst from src + "pmullw %%mm6, %%mm2\n\t" // Multiply the result with alpha * 256 + "psllw $8, %%mm3\n\t" // Multiply dst with 256 + "paddw %%mm3, %%mm2\n\t" // Add dst to the result + "psrlw $8, %%mm2\n\t" // Divide by 256 + + // Write the pixels back to the image + "packuswb %%mm2, %%mm0\n\t" // Pack the pixels to a qword + "movq %%mm0, (%1,%2,4)\n\t" // Store the pixels + : : "r"(data1), "r"(data2), "r"(i) ); + } + + // Blend the remaining pixel (if there is one) + if ( remainder ) { + __asm__ __volatile__( + "movd (%0), %%mm0\n\t" // Load one src pixel to MM0 + "punpcklbw %%mm7, %%mm0\n\t" // Unpack the src pixel + "movd (%1), %%mm1\n\t" // Load one dst pixel to MM1 + "punpcklbw %%mm7, %%mm1\n\t" // Unpack the dst pixel + "psubw %%mm1, %%mm0\n\t" // Subtract dst from src + "pmullw %%mm6, %%mm0\n\t" // Multiply the result with alpha * 256 + "psllw $8, %%mm1\n\t" // Multiply dst with 256 + "paddw %%mm1, %%mm0\n\t" // Add dst to result + "psrlw $8, %%mm0\n\t" // Divide by 256 + "packuswb %%mm0, %%mm0\n\t" // Pack the pixel to a dword + "movd %%mm0, (%1)\n\t" // Write the pixel to the image + : : "r"(data1 + pixels), "r"(data2 + pixels) ); + } + + // Empty the MMX state + __asm__ __volatile__("emms"); + } else +#endif // USE_MMX_INLINE_ASM + + { +#ifdef WORDS_BIGENDIAN // ARGB (skip alpha) + register unsigned char *data1 = (unsigned char *)dst.bits() + 1; + register unsigned char *data2 = (unsigned char *)src.bits() + 1; +#else // BGRA + register unsigned char *data1 = (unsigned char *)dst.bits(); + register unsigned char *data2 = (unsigned char *)src.bits(); +#endif + + for (register int i=0; i 1) initial_intensity = 1; + if (initial_intensity < -1) initial_intensity = -1; + if (initial_intensity < 0) { + unaffected = 1. + initial_intensity; + initial_intensity = 0; + } + + + float intensity = initial_intensity; + float var = 1. - initial_intensity; + + if (anti_dir) { + initial_intensity = intensity = 1.; + var = -var; + } + + register int x, y; + + unsigned int *data = (unsigned int *)image.bits(); + + int image_width = image.width(); //Those can't change + int image_height = image.height(); + + + if( eff == VerticalGradient || eff == HorizontalGradient ) { + + // set the image domain to apply the effect to + xi = 0, xf = image_width; + yi = 0, yf = image_height; + if (eff == VerticalGradient) { + if (anti_dir) yf = (int)(image_height * unaffected); + else yi = (int)(image_height * (1 - unaffected)); + } + else { + if (anti_dir) xf = (int)(image_width * unaffected); + else xi = (int)(image_height * (1 - unaffected)); + } + + var /= (eff == VerticalGradient?yf-yi:xf-xi); + + int ind_base; + for (y = yi; y < (int)yf; y++) { + intensity = eff == VerticalGradient? intensity + var : + initial_intensity; + ind_base = image_width * y ; + for (x = xi; x < (int)xf ; x++) { + if (eff == HorizontalGradient) intensity += var; + ind = x + ind_base; + r = qRed (data[ind]) + (int)(intensity * + (r_bgnd - qRed (data[ind]))); + g = qGreen(data[ind]) + (int)(intensity * + (g_bgnd - qGreen(data[ind]))); + b = qBlue (data[ind]) + (int)(intensity * + (b_bgnd - qBlue (data[ind]))); + if (r > 255) r = 255; if (r < 0 ) r = 0; + if (g > 255) g = 255; if (g < 0 ) g = 0; + if (b > 255) b = 255; if (b < 0 ) b = 0; + a = qAlpha(data[ind]); + data[ind] = qRgba(r, g, b, a); + } + } + } + else if (eff == DiagonalGradient || eff == CrossDiagonalGradient) { + float xvar = var / 2 / image_width; // / unaffected; + float yvar = var / 2 / image_height; // / unaffected; + float tmp; + + for (x = 0; x < image_width ; x++) { + tmp = xvar * (eff == DiagonalGradient? x : image.width()-x-1); + ind = x; + for (y = 0; y < image_height ; y++) { + intensity = initial_intensity + tmp + yvar * y; + + r = qRed (data[ind]) + (int)(intensity * + (r_bgnd - qRed (data[ind]))); + g = qGreen(data[ind]) + (int)(intensity * + (g_bgnd - qGreen(data[ind]))); + b = qBlue (data[ind]) + (int)(intensity * + (b_bgnd - qBlue (data[ind]))); + if (r > 255) r = 255; if (r < 0 ) r = 0; + if (g > 255) g = 255; if (g < 0 ) g = 0; + if (b > 255) b = 255; if (b < 0 ) b = 0; + a = qAlpha(data[ind]); + data[ind] = qRgba(r, g, b, a); + + ind += image_width; + } + } + } + + else if (eff == RectangleGradient || eff == EllipticGradient) { + float xvar; + float yvar; + + for (x = 0; x < image_width / 2 + image_width % 2; x++) { + xvar = var / image_width * (image_width - x*2/unaffected-1); + for (y = 0; y < image_height / 2 + image_height % 2; y++) { + yvar = var / image_height * (image_height - y*2/unaffected -1); + + if (eff == RectangleGradient) + intensity = initial_intensity + QMAX(xvar, yvar); + else + intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar); + if (intensity > 1) intensity = 1; + if (intensity < 0) intensity = 0; + + //NW + ind = x + image_width * y ; + r = qRed (data[ind]) + (int)(intensity * + (r_bgnd - qRed (data[ind]))); + g = qGreen(data[ind]) + (int)(intensity * + (g_bgnd - qGreen(data[ind]))); + b = qBlue (data[ind]) + (int)(intensity * + (b_bgnd - qBlue (data[ind]))); + if (r > 255) r = 255; if (r < 0 ) r = 0; + if (g > 255) g = 255; if (g < 0 ) g = 0; + if (b > 255) b = 255; if (b < 0 ) b = 0; + a = qAlpha(data[ind]); + data[ind] = qRgba(r, g, b, a); + + //NE + ind = image_width - x - 1 + image_width * y ; + r = qRed (data[ind]) + (int)(intensity * + (r_bgnd - qRed (data[ind]))); + g = qGreen(data[ind]) + (int)(intensity * + (g_bgnd - qGreen(data[ind]))); + b = qBlue (data[ind]) + (int)(intensity * + (b_bgnd - qBlue (data[ind]))); + if (r > 255) r = 255; if (r < 0 ) r = 0; + if (g > 255) g = 255; if (g < 0 ) g = 0; + if (b > 255) b = 255; if (b < 0 ) b = 0; + a = qAlpha(data[ind]); + data[ind] = qRgba(r, g, b, a); + } + } + + //CT loop is doubled because of stupid central row/column issue. + // other solution? + for (x = 0; x < image_width / 2; x++) { + xvar = var / image_width * (image_width - x*2/unaffected-1); + for (y = 0; y < image_height / 2; y++) { + yvar = var / image_height * (image_height - y*2/unaffected -1); + + if (eff == RectangleGradient) + intensity = initial_intensity + QMAX(xvar, yvar); + else + intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar); + if (intensity > 1) intensity = 1; + if (intensity < 0) intensity = 0; + + //SW + ind = x + image_width * (image_height - y -1) ; + r = qRed (data[ind]) + (int)(intensity * + (r_bgnd - qRed (data[ind]))); + g = qGreen(data[ind]) + (int)(intensity * + (g_bgnd - qGreen(data[ind]))); + b = qBlue (data[ind]) + (int)(intensity * + (b_bgnd - qBlue (data[ind]))); + if (r > 255) r = 255; if (r < 0 ) r = 0; + if (g > 255) g = 255; if (g < 0 ) g = 0; + if (b > 255) b = 255; if (b < 0 ) b = 0; + a = qAlpha(data[ind]); + data[ind] = qRgba(r, g, b, a); + + //SE + ind = image_width-x-1 + image_width * (image_height - y - 1) ; + r = qRed (data[ind]) + (int)(intensity * + (r_bgnd - qRed (data[ind]))); + g = qGreen(data[ind]) + (int)(intensity * + (g_bgnd - qGreen(data[ind]))); + b = qBlue (data[ind]) + (int)(intensity * + (b_bgnd - qBlue (data[ind]))); + if (r > 255) r = 255; if (r < 0 ) r = 0; + if (g > 255) g = 255; if (g < 0 ) g = 0; + if (b > 255) b = 255; if (b < 0 ) b = 0; + a = qAlpha(data[ind]); + data[ind] = qRgba(r, g, b, a); + } + } + } +#ifndef NDEBUG + else std::cerr << "KImageEffect::blend effect not implemented" << std::endl; +#endif + return image; +} + +// Not very efficient as we create a third big image... +// +QImage& KImageEffect::blend(QImage &image1, QImage &image2, + GradientType gt, int xf, int yf) +{ + if (image1.width() == 0 || image1.height() == 0 || + image2.width() == 0 || image2.height() == 0) + return image1; + + QImage image3; + + image3 = KImageEffect::unbalancedGradient(image1.size(), + QColor(0,0,0), QColor(255,255,255), + gt, xf, yf, 0); + + return blend(image1,image2,image3, Red); // Channel to use is arbitrary +} + +// Blend image2 into image1, using an RBG channel of blendImage +// +QImage& KImageEffect::blend(QImage &image1, QImage &image2, + QImage &blendImage, RGBComponent channel) +{ + if (image1.width() == 0 || image1.height() == 0 || + image2.width() == 0 || image2.height() == 0 || + blendImage.width() == 0 || blendImage.height() == 0) { +#ifndef NDEBUG + std::cerr << "KImageEffect::blend effect invalid image" << std::endl; +#endif + return image1; + } + + int r, g, b; + int ind1, ind2, ind3; + + unsigned int x1, x2, x3, y1, y2, y3; + unsigned int a; + + register int x, y; + + // for image1 and image2, we only handle depth 32 + if (image1.depth()<32) image1 = image1.convertDepth(32); + if (image2.depth()<32) image2 = image2.convertDepth(32); + + // for blendImage, we handle depth 8 and 32 + if (blendImage.depth()<8) blendImage = blendImage.convertDepth(8); + + unsigned int *colorTable3 = (blendImage.depth()==8) ? + blendImage.colorTable():0; + + unsigned int *data1 = (unsigned int *)image1.bits(); + unsigned int *data2 = (unsigned int *)image2.bits(); + unsigned int *data3 = (unsigned int *)blendImage.bits(); + unsigned char *data3b = (unsigned char *)blendImage.bits(); + unsigned int color3; + + x1 = image1.width(); y1 = image1.height(); + x2 = image2.width(); y2 = image2.height(); + x3 = blendImage.width(); y3 = blendImage.height(); + + for (y = 0; y < (int)y1; y++) { + ind1 = x1*y; + ind2 = x2*(y%y2); + ind3 = x3*(y%y3); + + x=0; + while(x < (int)x1) { + color3 = (colorTable3) ? colorTable3[data3b[ind3]] : data3[ind3]; + + a = (channel == Red) ? qRed(color3) : + (channel == Green) ? qGreen(color3) : + (channel == Blue) ? qBlue(color3) : qGray(color3); + + r = (a*qRed(data1[ind1]) + (256-a)*qRed(data2[ind2]))/256; + g = (a*qGreen(data1[ind1]) + (256-a)*qGreen(data2[ind2]))/256; + b = (a*qBlue(data1[ind1]) + (256-a)*qBlue(data2[ind2]))/256; + + a = qAlpha(data1[ind1]); + data1[ind1] = qRgba(r, g, b, a); + + ind1++; ind2++; ind3++; x++; + if ( (x%x2) ==0) ind2 -= x2; + if ( (x%x3) ==0) ind3 -= x3; + } + } + return image1; +} + + +//====================================================================== +// +// Hash effects +// +//====================================================================== + +unsigned int KImageEffect::lHash(unsigned int c) +{ + unsigned char r = qRed(c), g = qGreen(c), b = qBlue(c), a = qAlpha(c); + unsigned char nr, ng, nb; + nr =(r >> 1) + (r >> 2); nr = nr > r ? 0 : nr; + ng =(g >> 1) + (g >> 2); ng = ng > g ? 0 : ng; + nb =(b >> 1) + (b >> 2); nb = nb > b ? 0 : nb; + + return qRgba(nr, ng, nb, a); +} + + +// ----------------------------------------------------------------------------- + +unsigned int KImageEffect::uHash(unsigned int c) +{ + unsigned char r = qRed(c), g = qGreen(c), b = qBlue(c), a = qAlpha(c); + unsigned char nr, ng, nb; + nr = r + (r >> 3); nr = nr < r ? ~0 : nr; + ng = g + (g >> 3); ng = ng < g ? ~0 : ng; + nb = b + (b >> 3); nb = nb < b ? ~0 : nb; + + return qRgba(nr, ng, nb, a); +} + + +// ----------------------------------------------------------------------------- + +QImage& KImageEffect::hash(QImage &image, Lighting lite, unsigned int spacing) +{ + if (image.width() == 0 || image.height() == 0) { +#ifndef NDEBUG + std::cerr << "KImageEffect::hash effect invalid image" << std::endl; +#endif + return image; + } + + register int x, y; + unsigned int *data = (unsigned int *)image.bits(); + unsigned int ind; + + //CT no need to do it if not enough space + if ((lite == NorthLite || + lite == SouthLite)&& + (unsigned)image.height() < 2+spacing) return image; + if ((lite == EastLite || + lite == WestLite)&& + (unsigned)image.height() < 2+spacing) return image; + + if (lite == NorthLite || lite == SouthLite) { + for (y = 0 ; y < image.height(); y = y + 2 + spacing) { + for (x = 0; x < image.width(); x++) { + ind = x + image.width() * y; + data[ind] = lite==NorthLite?uHash(data[ind]):lHash(data[ind]); + + ind = ind + image.width(); + data[ind] = lite==NorthLite?lHash(data[ind]):uHash(data[ind]); + } + } + } + + else if (lite == EastLite || lite == WestLite) { + for (y = 0 ; y < image.height(); y++) { + for (x = 0; x < image.width(); x = x + 2 + spacing) { + ind = x + image.width() * y; + data[ind] = lite==EastLite?uHash(data[ind]):lHash(data[ind]); + + ind++; + data[ind] = lite==EastLite?lHash(data[ind]):uHash(data[ind]); + } + } + } + + else if (lite == NWLite || lite == SELite) { + for (y = 0 ; y < image.height(); y++) { + for (x = 0; + x < (int)(image.width() - ((y & 1)? 1 : 0) * spacing); + x = x + 2 + spacing) { + ind = x + image.width() * y + ((y & 1)? 1 : 0); + data[ind] = lite==NWLite?uHash(data[ind]):lHash(data[ind]); + + ind++; + data[ind] = lite==NWLite?lHash(data[ind]):uHash(data[ind]); + } + } + } + + else if (lite == SWLite || lite == NELite) { + for (y = 0 ; y < image.height(); y++) { + for (x = 0 + ((y & 1)? 1 : 0); x < image.width(); x = x + 2 + spacing) { + ind = x + image.width() * y - ((y & 1)? 1 : 0); + data[ind] = lite==SWLite?uHash(data[ind]):lHash(data[ind]); + + ind++; + data[ind] = lite==SWLite?lHash(data[ind]):uHash(data[ind]); + } + } + } + + return image; +} +#endif + +//====================================================================== +// +// Flatten effects +// +//====================================================================== + +QImage& KImageEffect::flatten(QImage &image, const QColor &ca, + const QColor &cb, int ncols) +{ + if (image.width() == 0 || image.height() == 0) + return image; + + // a bitmap is easy... + /*if (image.depth() == 1) { + image.setColor(0, ca.rgb()); + image.setColor(1, cb.rgb()); + return image; + }*/ + + int r1, r2, g1, g2, b1, b2; + int min = 0, max = 255; + + if (image.inverted()) + { + b1 = ca.red(); b2 = cb.red(); + g1 = ca.green(); g2 = cb.green(); + r1 = ca.blue(); r2 = cb.blue(); + } + else + { + r1 = ca.red(); r2 = cb.red(); + g1 = ca.green(); g2 = cb.green(); + b1 = ca.blue(); b2 = cb.blue(); + } + + QRgb col; + + // Get minimum and maximum greylevel. +/* if (image.numColors()) { + // pseudocolor + for (int i = 0; i < image.numColors(); i++) { + col = image.color(i); + int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3; + min = QMIN(min, mean); + max = QMAX(max, mean); + } + } else*/ { + // truecolor + for (int y=0; y < image.height(); y++) + for (int x=0; x < image.width(); x++) { + col = image.pixel(x, y); + int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3; + min = QMIN(min, mean); + max = QMAX(max, mean); + } + } + + // Conversion factors + float sr = ((float) r2 - r1) / (max - min); + float sg = ((float) g2 - g1) / (max - min); + float sb = ((float) b2 - b1) / (max - min); + + + // Repaint the image +/* if (image.numColors()) { + for (int i=0; i < image.numColors(); i++) { + col = image.color(i); + int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3; + int r = (int) (sr * (mean - min) + r1 + 0.5); + int g = (int) (sg * (mean - min) + g1 + 0.5); + int b = (int) (sb * (mean - min) + b1 + 0.5); + image.setColor(i, qRgba(r, g, b, qAlpha(col))); + } + } else*/ { + for (int y=0; y < image.height(); y++) + for (int x=0; x < image.width(); x++) { + col = image.pixel(x, y); + //INVERT(col); + int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3; + int r = (int) (sr * (mean - min) + r1 + 0.5); + int g = (int) (sg * (mean - min) + g1 + 0.5); + int b = (int) (sb * (mean - min) + b1 + 0.5); + col = qRgba(r, g, b, qAlpha(col)); + //INVERT(col); + image.setPixel(x, y, col); + } + } + + +/* // Dither if necessary + if ( (ncols <= 0) || ((image.numColors() != 0) && (image.numColors() <= ncols))) + return image; + + if (ncols == 1) ncols++; + if (ncols > 256) ncols = 256; + + QColor *pal = new QColor[ncols]; + sr = ((float) r2 - r1) / (ncols - 1); + sg = ((float) g2 - g1) / (ncols - 1); + sb = ((float) b2 - b1) / (ncols - 1); + + for (int i=0; i red) + r = cr - tbl[cr - red]; + else + r = cr + tbl[red - cr]; + if (cg > green) + g = cg - tbl[cg - green]; + else + g = cg + tbl[green - cg]; + if (cb > blue) + b = cb - tbl[cb - blue]; + else + b = cb + tbl[blue - cb]; + image.setColor(i, qRgba(r, g, b, qAlpha(col))); + } + + } else*/ { + // truecolor + for (int y=0; y red) + r = cr - tbl[cr - red]; + else + r = cr + tbl[red - cr]; + if (cg > green) + g = cg - tbl[cg - green]; + else + g = cg + tbl[green - cg]; + if (cb > blue) + b = cb - tbl[cb - blue]; + else + b = cb + tbl[blue - cb]; + *data++ = qRgba(r, g, b, qAlpha(col)); + } + } + } + + return image; +} + +//====================================================================== +// +// Color effects +// +//====================================================================== + +// This code is adapted from code (C) Rik Hemsley +// +// The formula used (r + b + g) /3 is different from the qGray formula +// used by Qt. This is because our formula is much much faster. If, +// however, it turns out that this is producing sub-optimal images, +// then it will have to change (kurt) +// +// It does produce lower quality grayscale ;-) Use fast == true for the fast +// algorithm, false for the higher quality one (mosfet). +QImage& KImageEffect::toGray(QImage &img, bool fast) +{ + if (img.width() == 0 || img.height() == 0) + return img; + +/* + if(fast){ + if (img.depth() == 32) { + register uchar * r(img.bits()); + register uchar * g(img.bits() + 1); + register uchar * b(img.bits() + 2); + + uchar * end(img.bits() + img.numBytes()); + + while (r != end) { + + *r = *g = *b = (((*r + *g) >> 1) + *b) >> 1; // (r + b + g) / 3 + + r += 4; + g += 4; + b += 4; + } + } + else + { + for (int i = 0; i < img.numColors(); i++) + { + register uint r = qRed(img.color(i)); + register uint g = qGreen(img.color(i)); + register uint b = qBlue(img.color(i)); + + register uint gray = (((r + g) >> 1) + b) >> 1; + img.setColor(i, qRgba(gray, gray, gray, qAlpha(img.color(i)))); + } + } + } + else*/{ + int pixels = img.depth() > 8 ? img.width()*img.height() : + img.numColors(); + unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() : + (unsigned int *)img.colorTable(); + int val, i; + for(i=0; i < pixels; ++i){ + val = qGray(data[i]); + data[i] = qRgba(val, val, val, qAlpha(data[i])); + } + } + return img; +} + +// CT 29Jan2000 - desaturation algorithms +QImage& KImageEffect::desaturate(QImage &image, float desat) +{ + if (image.width() == 0 || image.height() == 0) + return image; + + if (desat < 0) desat = 0.; + if (desat > 1) desat = 1.; + int pixels = image.depth() > 8 ? image.width()*image.height() : image.numColors(); + unsigned int *data = image.depth() > 8 ? (unsigned int *)image.bits() : (unsigned int *)image.colorTable(); + int h, s, v, i; + uint col; + QColor clr; // keep constructor out of loop (mosfet) + for (i = 0; i < pixels; ++i) + { + col = data[i]; + INVERT(col); + clr.setRgb(col); + clr.hsv(&h, &s, &v); + clr.setHsv(h, (int)(s * (1. - desat)), v); + data[i] = clr.rgb(); + INVERT(data[i]); + } + return image; +} + +#if 0 +// Contrast stuff (mosfet) +QImage& KImageEffect::contrast(QImage &img, int c) +{ + if (img.width() == 0 || img.height() == 0) + return img; + + if(c > 255) + c = 255; + if(c < -255) + c = -255; + int pixels = img.depth() > 8 ? img.width()*img.height() : + img.numColors(); + unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() : + (unsigned int *)img.colorTable(); + int i, r, g, b; + for(i=0; i < pixels; ++i){ + r = qRed(data[i]); + g = qGreen(data[i]); + b = qBlue(data[i]); + if(qGray(data[i]) <= 127){ + if(r - c <= 255) + r -= c; + if(g - c <= 255) + g -= c; + if(b - c <= 255) + b -= c; + } + else{ + if(r + c <= 255) + r += c; + if(g + c <= 255) + g += c; + if(b + c <= 255) + b += c; + } + data[i] = qRgba(r, g, b, qAlpha(data[i])); + } + return(img); +} +#endif + +#if 0 +//====================================================================== +// +// Dithering effects +// +//====================================================================== + +// adapted from kFSDither (C) 1997 Martin Jones (mjones@kde.org) +// +// Floyd-Steinberg dithering +// Ref: Bitmapped Graphics Programming in C++ +// Marv Luse, Addison-Wesley Publishing, 1993. +QImage& KImageEffect::dither(QImage &img, const QColor *palette, int size) +{ + if (img.width() == 0 || img.height() == 0 || + palette == 0 || img.depth() <= 8) + return img; + + QImage dImage( img.width(), img.height(), 8, size ); + int i; + + dImage.setNumColors( size ); + for ( i = 0; i < size; i++ ) + dImage.setColor( i, palette[ i ].rgb() ); + + int *rerr1 = new int [ img.width() * 2 ]; + int *gerr1 = new int [ img.width() * 2 ]; + int *berr1 = new int [ img.width() * 2 ]; + + memset( rerr1, 0, sizeof( int ) * img.width() * 2 ); + memset( gerr1, 0, sizeof( int ) * img.width() * 2 ); + memset( berr1, 0, sizeof( int ) * img.width() * 2 ); + + int *rerr2 = rerr1 + img.width(); + int *gerr2 = gerr1 + img.width(); + int *berr2 = berr1 + img.width(); + + for ( int j = 0; j < img.height(); j++ ) + { + uint *ip = (uint * )img.scanLine( j ); + uchar *dp = dImage.scanLine( j ); + + for ( i = 0; i < img.width(); i++ ) + { + rerr1[i] = rerr2[i] + qRed( *ip ); + rerr2[i] = 0; + gerr1[i] = gerr2[i] + qGreen( *ip ); + gerr2[i] = 0; + berr1[i] = berr2[i] + qBlue( *ip ); + berr2[i] = 0; + ip++; + } + + *dp++ = nearestColor( rerr1[0], gerr1[0], berr1[0], palette, size ); + + for ( i = 1; i < img.width()-1; i++ ) + { + int indx = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size ); + *dp = indx; + + int rerr = rerr1[i]; + rerr -= palette[indx].red(); + int gerr = gerr1[i]; + gerr -= palette[indx].green(); + int berr = berr1[i]; + berr -= palette[indx].blue(); + + // diffuse red error + rerr1[ i+1 ] += ( rerr * 7 ) >> 4; + rerr2[ i-1 ] += ( rerr * 3 ) >> 4; + rerr2[ i ] += ( rerr * 5 ) >> 4; + rerr2[ i+1 ] += ( rerr ) >> 4; + + // diffuse green error + gerr1[ i+1 ] += ( gerr * 7 ) >> 4; + gerr2[ i-1 ] += ( gerr * 3 ) >> 4; + gerr2[ i ] += ( gerr * 5 ) >> 4; + gerr2[ i+1 ] += ( gerr ) >> 4; + + // diffuse red error + berr1[ i+1 ] += ( berr * 7 ) >> 4; + berr2[ i-1 ] += ( berr * 3 ) >> 4; + berr2[ i ] += ( berr * 5 ) >> 4; + berr2[ i+1 ] += ( berr ) >> 4; + + dp++; + } + + *dp = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size ); + } + + delete [] rerr1; + delete [] gerr1; + delete [] berr1; + + img = dImage; + return img; +} + +int KImageEffect::nearestColor( int r, int g, int b, const QColor *palette, int size ) +{ + if (palette == 0) + return 0; + + int dr = palette[0].red() - r; + int dg = palette[0].green() - g; + int db = palette[0].blue() - b; + + int minDist = dr*dr + dg*dg + db*db; + int nearest = 0; + + for (int i = 1; i < size; i++ ) + { + dr = palette[i].red() - r; + dg = palette[i].green() - g; + db = palette[i].blue() - b; + + int dist = dr*dr + dg*dg + db*db; + + if ( dist < minDist ) + { + minDist = dist; + nearest = i; + } + } + + return nearest; +} + +bool KImageEffect::blend( + const QImage & upper, + const QImage & lower, + QImage & output +) +{ + if ( + upper.width() > lower.width() || + upper.height() > lower.height() || + upper.depth() != 32 || + lower.depth() != 32 + ) + { +#ifndef NDEBUG + std::cerr << "KImageEffect::blend : Sizes not correct\n" ; +#endif + return false; + } + + output = lower.copy(); + + register uchar *i, *o; + register int a; + register int col; + register int w = upper.width(); + int row(upper.height() - 1); + + do { + + i = upper.scanLine(row); + o = output.scanLine(row); + + col = w << 2; + --col; + + do { + + while (!(a = i[col]) && (col != 3)) { + --col; --col; --col; --col; + } + + --col; + o[col] += ((i[col] - o[col]) * a) >> 8; + + --col; + o[col] += ((i[col] - o[col]) * a) >> 8; + + --col; + o[col] += ((i[col] - o[col]) * a) >> 8; + + } while (col--); + + } while (row--); + + return true; +} + +#if 0 +// Not yet... +bool KImageEffect::blend( + const QImage & upper, + const QImage & lower, + QImage & output, + const QRect & destRect +) +{ + output = lower.copy(); + return output; +} + +#endif + +bool KImageEffect::blend( + int &x, int &y, + const QImage & upper, + const QImage & lower, + QImage & output +) +{ + int cx=0, cy=0, cw=upper.width(), ch=upper.height(); + + if ( upper.width() + x > lower.width() || + upper.height() + y > lower.height() || + x < 0 || y < 0 || + upper.depth() != 32 || lower.depth() != 32 ) + { + if ( x > lower.width() || y > lower.height() ) return false; + if ( upper.width()<=0 || upper.height() <= 0 ) return false; + if ( lower.width()<=0 || lower.height() <= 0 ) return false; + + if (x<0) {cx=-x; cw+=x; x=0; }; + if (cw + x > lower.width()) { cw=lower.width()-x; }; + if (y<0) {cy=-y; ch+=y; y=0; }; + if (ch + y > lower.height()) { ch=lower.height()-y; }; + + if ( cx >= upper.width() || cy >= upper.height() ) return true; + if ( cw <= 0 || ch <= 0 ) return true; + } + + output.create(cw,ch,32); +// output.setAlphaBuffer(true); // I should do some benchmarks to see if + // this is worth the effort + + register QRgb *i, *o, *b; + + register int a; + register int j,k; + for (j=0; j(&lower.scanLine(y+j) [ (x+cw) << 2 ]); + i=reinterpret_cast(&upper.scanLine(cy+j)[ (cx+cw) << 2 ]); + o=reinterpret_cast(&output.scanLine(j) [ cw << 2 ]); + + k=cw-1; + --b; --i; --o; + do + { + while ( !(a=qAlpha(*i)) && k>0 ) + { + i--; +// *o=0; + *o=*b; + --o; --b; + k--; + }; +// *o=0xFF; + *o = qRgb(qRed(*b) + (((qRed(*i) - qRed(*b)) * a) >> 8), + qGreen(*b) + (((qGreen(*i) - qGreen(*b)) * a) >> 8), + qBlue(*b) + (((qBlue(*i) - qBlue(*b)) * a) >> 8)); + --i; --o; --b; + } while (k--); + } + + return true; +} + +bool KImageEffect::blendOnLower( + int x, int y, + const QImage & upper, + const QImage & lower +) +{ + int cx=0, cy=0, cw=upper.width(), ch=upper.height(); + + if ( upper.depth() != 32 || lower.depth() != 32 ) return false; + if ( x + cw > lower.width() || + y + ch > lower.height() || + x < 0 || y < 0 ) + { + if ( x > lower.width() || y > lower.height() ) return true; + if ( upper.width()<=0 || upper.height() <= 0 ) return true; + if ( lower.width()<=0 || lower.height() <= 0 ) return true; + + if (x<0) {cx=-x; cw+=x; x=0; }; + if (cw + x > lower.width()) { cw=lower.width()-x; }; + if (y<0) {cy=-y; ch+=y; y=0; }; + if (ch + y > lower.height()) { ch=lower.height()-y; }; + + if ( cx >= upper.width() || cy >= upper.height() ) return true; + if ( cw <= 0 || ch <= 0 ) return true; + } + + register uchar *i, *b; + register int a; + register int k; + + for (int j=0; j0 ) +#else + while ( !(a=*(i-3)) && k>0 ) +#endif + { + i-=4; b-=4; k--; + }; + +#ifndef WORDS_BIGENDIAN + --i; --b; + *b += ( ((*i - *b) * a) >> 8 ); + --i; --b; + *b += ( ((*i - *b) * a) >> 8 ); + --i; --b; + *b += ( ((*i - *b) * a) >> 8 ); + --i; --b; +#else + *b += ( ((*i - *b) * a) >> 8 ); + --i; --b; + *b += ( ((*i - *b) * a) >> 8 ); + --i; --b; + *b += ( ((*i - *b) * a) >> 8 ); + i -= 2; b -= 2; +#endif + } while (k--); + } + + return true; +} + +void KImageEffect::blendOnLower(const QImage &upper, const QPoint &upperOffset, + QImage &lower, const QRect &lowerRect) +{ + // clip rect + QRect lr = lowerRect & lower.rect(); + lr.setWidth( QMIN(lr.width(), upper.width()-upperOffset.x()) ); + lr.setHeight( QMIN(lr.height(), upper.height()-upperOffset.y()) ); + if ( !lr.isValid() ) return; + + // blend + for (int y = 0; y < lr.height(); y++) { + for (int x = 0; x < lr.width(); x++) { + QRgb *b = reinterpret_cast(lower.scanLine(lr.y() + y)+ (lr.x() + x) * sizeof(QRgb)); + QRgb *d = reinterpret_cast(upper.scanLine(upperOffset.y() + y) + (upperOffset.x() + x) * sizeof(QRgb)); + int a = qAlpha(*d); + *b = qRgb(qRed(*b) - (((qRed(*b) - qRed(*d)) * a) >> 8), + qGreen(*b) - (((qGreen(*b) - qGreen(*d)) * a) >> 8), + qBlue(*b) - (((qBlue(*b) - qBlue(*d)) * a) >> 8)); + } + } +} + +void KImageEffect::blendOnLower(const QImage &upper, const QPoint &upperOffset, + QImage &lower, const QRect &lowerRect, float opacity) +{ + // clip rect + QRect lr = lowerRect & lower.rect(); + lr.setWidth( QMIN(lr.width(), upper.width()-upperOffset.x()) ); + lr.setHeight( QMIN(lr.height(), upper.height()-upperOffset.y()) ); + if ( !lr.isValid() ) return; + + // blend + for (int y = 0; y < lr.height(); y++) { + for (int x = 0; x < lr.width(); x++) { + QRgb *b = reinterpret_cast(lower.scanLine(lr.y() + y)+ (lr.x() + x) * sizeof(QRgb)); + QRgb *d = reinterpret_cast(upper.scanLine(upperOffset.y() + y) + (upperOffset.x() + x) * sizeof(QRgb)); + int a = qRound(opacity * qAlpha(*d)); + *b = qRgb(qRed(*b) - (((qRed(*b) - qRed(*d)) * a) >> 8), + qGreen(*b) - (((qGreen(*b) - qGreen(*d)) * a) >> 8), + qBlue(*b) - (((qBlue(*b) - qBlue(*d)) * a) >> 8)); + } + } +} + +QRect KImageEffect::computeDestinationRect(const QSize &lowerSize, + Disposition disposition, QImage &upper) +{ + int w = lowerSize.width(); + int h = lowerSize.height(); + int ww = upper.width(); + int wh = upper.height(); + QRect d; + + switch (disposition) { + case NoImage: + break; + case Centered: + d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh); + break; + case Tiled: + d.setRect(0, 0, w, h); + break; + case CenterTiled: + d.setCoords(-ww + ((w - ww) / 2) % ww, -wh + ((h - wh) / 2) % wh, + w-1, h-1); + break; + case Scaled: + upper = upper.smoothScale(w, h); + d.setRect(0, 0, w, h); + break; + case CenteredAutoFit: + if( ww <= w && wh <= h ) { + d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh); // like Centered + break; + } + // fall through + case CenteredMaxpect: { + double sx = (double) w / ww; + double sy = (double) h / wh; + if (sx > sy) { + ww = (int)(sy * ww); + wh = h; + } else { + wh = (int)(sx * wh); + ww = w; + } + upper = upper.smoothScale(ww, wh); + d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh); + break; + } + case TiledMaxpect: { + double sx = (double) w / ww; + double sy = (double) h / wh; + if (sx > sy) { + ww = (int)(sy * ww); + wh = h; + } else { + wh = (int)(sx * wh); + ww = w; + } + upper = upper.smoothScale(ww, wh); + d.setRect(0, 0, w, h); + break; + } + } + + return d; +} + +void KImageEffect::blendOnLower(QImage &upper, QImage &lower, + Disposition disposition, float opacity) +{ + QRect r = computeDestinationRect(lower.size(), disposition, upper); + for (int y = r.top(); y 8){ // DirectClass source image + unsigned int *srcData, *destData; + unsigned int *pixels; + pixels = (unsigned int *)malloc(src.width()*sizeof(unsigned int)); + if(!pixels){ + //qWarning("KImageEffect::sample(): Unable to allocate pixels buffer"); + free(pixels); + free(x_offset); + free(y_offset); + return(src); + } + j = (-1); + for(y=0; y < h; ++y){ + destData = (unsigned int *)dest.scanLine(y); + if(j != y_offset[y]){ + // read a scan line + j = (int)(y_offset[y]); + srcData = (unsigned int *)src.scanLine(j); + (void)memcpy(pixels, srcData, src.width()*sizeof(unsigned int)); + } + // sample each column + for(x=0; x < w; ++x){ + k = (int)(x_offset[x]); + destData[x] = pixels[k]; + } + } + free(pixels); + } + /* + else{ // PsudeoClass source image + unsigned char *srcData, *destData; + unsigned char *pixels; + pixels = (unsigned char *)malloc(src.width()*sizeof(unsigned char)); + if(!pixels){ + qWarning("KImageEffect::sample(): Unable to allocate pixels buffer"); + free(pixels); + free(x_offset); + free(y_offset); + return(src); + } + // copy colortable + dest.setNumColors(src.numColors()); + (void)memcpy(dest.colorTable(), src.colorTable(), + src.numColors()*sizeof(unsigned int)); + + // sample image + j = (-1); + for(y=0; y < h; ++y){ + destData = (unsigned char *)dest.scanLine(y); + if(j != y_offset[y]){ + // read a scan line + j = (int)(y_offset[y]); + srcData = (unsigned char *)src.scanLine(j); + (void)memcpy(pixels, srcData, src.width()*sizeof(unsigned char)); + } + // sample each column + for(x=0; x < w; ++x){ + k = (int)(x_offset[x]); + destData[x] = pixels[k]; + } + } + free(pixels); + }*/ + free(x_offset); + free(y_offset); + return(dest); +} +#endif + +void KImageEffect::threshold(QImage &img, unsigned int threshold) +{ + int i, count; + unsigned int *data; + if(img.depth() > 8){ // DirectClass + count = img.width()*img.height(); + data = (unsigned int *)img.bits(); + } + /*else{ // PsudeoClass + count = img.numColors(); + data = (unsigned int *)img.colorTable(); + }*/ + if (img.inverted()) + { + for(i=0; i < count; ++i) + data[i] = intensityValue(invert(data[i])) < threshold ? 0xFF000000 : 0xFFFFFFFF; + } + else + { + for(i=0; i < count; ++i) + data[i] = intensityValue(data[i]) < threshold ? 0xFF000000 : 0xFFFFFFFF; + } +} + +void KImageEffect::hull(const int x_offset, const int y_offset, + const int polarity, const int columns, + const int rows, + unsigned int *f, unsigned int *g) +{ + int x, y; + + unsigned int *p, *q, *r, *s; + unsigned int v; + if(f == NULL || g == NULL) + return; + p=f+(columns+2); + q=g+(columns+2); + r=p+(y_offset*(columns+2)+x_offset); + for (y=0; y < rows; y++){ + p++; + q++; + r++; + if(polarity > 0) + for (x=0; x < columns; x++){ + v=(*p); + if (*r > v) + v++; + *q=v; + p++; + q++; + r++; + } + else + for(x=0; x < columns; x++){ + v=(*p); + if (v > (unsigned int) (*r+1)) + v--; + *q=v; + p++; + q++; + r++; + } + p++; + q++; + r++; + } + p=f+(columns+2); + q=g+(columns+2); + r=q+(y_offset*(columns+2)+x_offset); + s=q-(y_offset*(columns+2)+x_offset); + for(y=0; y < rows; y++){ + p++; + q++; + r++; + s++; + if(polarity > 0) + for(x=0; x < (int) columns; x++){ + v=(*q); + if (((unsigned int) (*s+1) > v) && (*r > v)) + v++; + *p=v; + p++; + q++; + r++; + s++; + } + else + for (x=0; x < columns; x++){ + v=(*q); + if (((unsigned int) (*s+1) < v) && (*r < v)) + v--; + *p=v; + p++; + q++; + r++; + s++; + } + p++; + q++; + r++; + s++; + } +} + +QImage KImageEffect::despeckle(QImage &src) +{ + int i, j, x, y; + unsigned int *blue_channel, *red_channel, *green_channel, *buffer, + *alpha_channel; + int packets; + static const int + X[4]= {0, 1, 1,-1}, + Y[4]= {1, 0, 1, 1}; + + unsigned int *destData; + QImage dest(src.width(), src.height(), src.transparent()); + + packets = (src.width()+2)*(src.height()+2); + red_channel = (unsigned int *)calloc(packets, sizeof(unsigned int)); + green_channel = (unsigned int *)calloc(packets, sizeof(unsigned int)); + blue_channel = (unsigned int *)calloc(packets, sizeof(unsigned int)); + alpha_channel = (unsigned int *)calloc(packets, sizeof(unsigned int)); + buffer = (unsigned int *)calloc(packets, sizeof(unsigned int)); + if(!red_channel || ! green_channel || ! blue_channel || ! alpha_channel || + !buffer){ + free(red_channel); + free(green_channel); + free(blue_channel); + free(alpha_channel); + free(buffer); + return(src); + } + + // copy image pixels to color component buffers + j = src.width()+2; + if(src.depth() > 8){ // DirectClass source image + unsigned int *srcData; + for(y=0; y < src.height(); ++y){ + srcData = (unsigned int *)src.scanLine(y); + ++j; + for(x=0; x < src.width(); ++x){ + red_channel[j] = qRed(srcData[x]); + green_channel[j] = qGreen(srcData[x]); + blue_channel[j] = qBlue(srcData[x]); + alpha_channel[j] = qAlpha(srcData[x]); + ++j; + } + ++j; + } + } + /* + else { // PsudeoClass source image + unsigned char *srcData; + unsigned int *cTable = src.colorTable(); + unsigned int pixel; + for(y=0; y < src.height(); ++y){ + srcData = (unsigned char *)src.scanLine(y); + ++j; + for(x=0; x < src.width(); ++x){ + pixel = *(cTable+srcData[x]); + red_channel[j] = qRed(pixel); + green_channel[j] = qGreen(pixel); + blue_channel[j] = qBlue(pixel); + alpha_channel[j] = qAlpha(pixel); + ++j; + } + ++j; + } + }*/ + + // reduce speckle in red channel + for(i=0; i < 4; i++){ + hull(X[i],Y[i],1,src.width(),src.height(),red_channel,buffer); + hull(-X[i],-Y[i],1,src.width(),src.height(),red_channel,buffer); + hull(-X[i],-Y[i],-1,src.width(),src.height(),red_channel,buffer); + hull(X[i],Y[i],-1,src.width(),src.height(),red_channel,buffer); + } + // reduce speckle in green channel + for (i=0; i < packets; i++) + buffer[i]=0; + for (i=0; i < 4; i++){ + hull(X[i],Y[i],1,src.width(),src.height(),green_channel,buffer); + hull(-X[i],-Y[i],1,src.width(),src.height(),green_channel,buffer); + hull(-X[i],-Y[i],-1,src.width(),src.height(),green_channel,buffer); + hull(X[i],Y[i],-1,src.width(),src.height(),green_channel,buffer); + } + // reduce speckle in blue channel + for (i=0; i < packets; i++) + buffer[i]=0; + for (i=0; i < 4; i++){ + hull(X[i],Y[i],1,src.width(),src.height(),blue_channel,buffer); + hull(-X[i],-Y[i],1,src.width(),src.height(),blue_channel,buffer); + hull(-X[i],-Y[i],-1,src.width(),src.height(),blue_channel,buffer); + hull(X[i],Y[i],-1,src.width(),src.height(),blue_channel,buffer); + } + // copy color component buffers to despeckled image + j = dest.width()+2; + for(y=0; y < dest.height(); ++y) + { + destData = (unsigned int *)dest.scanLine(y); + ++j; + for (x=0; x < dest.width(); ++x) + { + destData[x] = qRgba(red_channel[j], green_channel[j], + blue_channel[j], alpha_channel[j]); + ++j; + } + ++j; + } + free(buffer); + free(red_channel); + free(green_channel); + free(blue_channel); + free(alpha_channel); + return(dest); +} + +unsigned int KImageEffect::generateNoise(unsigned int pixel, + NoiseType noise_type) +{ +#define NoiseEpsilon 1.0e-5 +#define NoiseMask 0x7fff +#define SigmaUniform 4.0 +#define SigmaGaussian 4.0 +#define SigmaImpulse 0.10 +#define SigmaLaplacian 10.0 +#define SigmaMultiplicativeGaussian 0.5 +#define SigmaPoisson 0.05 +#define TauGaussian 20.0 + + double alpha, beta, sigma, value; + alpha=(double) (rand() & NoiseMask)/NoiseMask; + if (alpha == 0.0) + alpha=1.0; + switch(noise_type){ + case UniformNoise: + default: + { + value=(double) pixel+SigmaUniform*(alpha-0.5); + break; + } + case GaussianNoise: + { + double tau; + + beta=(double) (rand() & NoiseMask)/NoiseMask; + sigma=sqrt(-2.0*log(alpha))*cos(2.0*M_PI*beta); + tau=sqrt(-2.0*log(alpha))*sin(2.0*M_PI*beta); + value=(double) pixel+ + (sqrt((double) pixel)*SigmaGaussian*sigma)+(TauGaussian*tau); + break; + } + case MultiplicativeGaussianNoise: + { + if (alpha <= NoiseEpsilon) + sigma=MaxRGB; + else + sigma=sqrt(-2.0*log(alpha)); + beta=(rand() & NoiseMask)/NoiseMask; + value=(double) pixel+ + pixel*SigmaMultiplicativeGaussian*sigma*cos(2.0*M_PI*beta); + break; + } + case ImpulseNoise: + { + if (alpha < (SigmaImpulse/2.0)) + value=0; + else + if (alpha >= (1.0-(SigmaImpulse/2.0))) + value=MaxRGB; + else + value=pixel; + break; + } + case LaplacianNoise: + { + if (alpha <= 0.5) + { + if (alpha <= NoiseEpsilon) + value=(double) pixel-MaxRGB; + else + value=(double) pixel+SigmaLaplacian*log(2.0*alpha); + break; + } + beta=1.0-alpha; + if (beta <= (0.5*NoiseEpsilon)) + value=(double) pixel+MaxRGB; + else + value=(double) pixel-SigmaLaplacian*log(2.0*beta); + break; + } + case PoissonNoise: + { + register int + i; + + for (i=0; alpha > exp(-SigmaPoisson*pixel); i++) + { + beta=(double) (rand() & NoiseMask)/NoiseMask; + alpha=alpha*beta; + } + value=i/SigmaPoisson; + break; + } + } + if(value < 0.0) + return(0); + if(value > MaxRGB) + return(MaxRGB); + return((unsigned int) (value+0.5)); +} + +QImage KImageEffect::addNoise(QImage &src, NoiseType noise_type) +{ + int x, y; + QImage dest(src.width(), src.height(), 32); + unsigned int *destData; + + if(src.depth() > 8){ // DirectClass source image + unsigned int *srcData; + for(y=0; y < src.height(); ++y){ + srcData = (unsigned int *)src.scanLine(y); + destData = (unsigned int *)dest.scanLine(y); + for(x=0; x < src.width(); ++x){ + destData[x] = qRgba(generateNoise(qRed(srcData[x]), noise_type), + generateNoise(qGreen(srcData[x]), noise_type), + generateNoise(qBlue(srcData[x]), noise_type), + qAlpha(srcData[x])); + } + } + } + /*else{ // PsudeoClass source image + unsigned char *srcData; + unsigned int *cTable = src.colorTable(); + unsigned int pixel; + for(y=0; y < src.height(); ++y){ + srcData = (unsigned char *)src.scanLine(y); + destData = (unsigned int *)dest.scanLine(y); + for(x=0; x < src.width(); ++x){ + pixel = *(cTable+srcData[x]); + destData[x] = qRgba(generateNoise(qRed(pixel), noise_type), + generateNoise(qGreen(pixel), noise_type), + generateNoise(qBlue(pixel), noise_type), + qAlpha(pixel)); + } + } + + }*/ + return(dest); +} + +unsigned int KImageEffect::interpolateColor(QImage *image, double x_offset, + double y_offset, + unsigned int background) +{ + double alpha, beta; + unsigned int p, q, r, s; + int x, y; + + x = (int)x_offset; + y = (int)y_offset; + if((x < -1) || (x >= image->width()) || (y < -1) || (y >= image->height())) + return(background); + if(image->depth() > 8){ + if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1))) { + unsigned int *t = (unsigned int *)image->scanLine(y); + p = t[x]; + q = t[x+1]; + r = t[x+image->width()]; + s = t[x+image->width()+1]; + } + else{ + unsigned int *t = (unsigned int *)image->scanLine(y); + p = background; + if((x >= 0) && (y >= 0)){ + p = t[x]; + } + q = background; + if(((x+1) < image->width()) && (y >= 0)){ + q = t[x+1]; + } + r = background; + if((x >= 0) && ((y+1) < image->height())){ + t = (unsigned int *)image->scanLine(y+1); + r = t[x+image->width()]; + } + s = background; + if(((x+1) < image->width()) && ((y+1) < image->height())){ + t = (unsigned int *)image->scanLine(y+1); + s = t[x+image->width()+1]; + } + + } + } + /*else{ + unsigned int *colorTable = (unsigned int *)image->colorTable(); + if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1))) { + unsigned char *t; + t = (unsigned char *)image->scanLine(y); + p = *(colorTable+t[x]); + q = *(colorTable+t[x+1]); + t = (unsigned char *)image->scanLine(y+1); + r = *(colorTable+t[x]); + s = *(colorTable+t[x+1]); + } + else{ + unsigned char *t; + p = background; + if((x >= 0) && (y >= 0)){ + t = (unsigned char *)image->scanLine(y); + p = *(colorTable+t[x]); + } + q = background; + if(((x+1) < image->width()) && (y >= 0)){ + t = (unsigned char *)image->scanLine(y); + q = *(colorTable+t[x+1]); + } + r = background; + if((x >= 0) && ((y+1) < image->height())){ + t = (unsigned char *)image->scanLine(y+1); + r = *(colorTable+t[x]); + } + s = background; + if(((x+1) < image->width()) && ((y+1) < image->height())){ + t = (unsigned char *)image->scanLine(y+1); + s = *(colorTable+t[x+1]); + } + + } + + }*/ + x_offset -= floor(x_offset); + y_offset -= floor(y_offset); + alpha = 1.0-x_offset; + beta = 1.0-y_offset; + + return(qRgba((unsigned char)(beta*(alpha*qRed(p)+x_offset*qRed(q))+y_offset*(alpha*qRed(r)+x_offset*qRed(s))), + (unsigned char)(beta*(alpha*qGreen(p)+x_offset*qGreen(q))+y_offset*(alpha*qGreen(r)+x_offset*qGreen(s))), + (unsigned char)(beta*(alpha*qBlue(p)+x_offset*qBlue(q))+y_offset*(alpha*qBlue(r)+x_offset*qBlue(s))), + (unsigned char)(beta*(alpha*qAlpha(p)+x_offset*qAlpha(q))+y_offset*(alpha*qAlpha(r)+x_offset*qAlpha(s))))); +} + +QImage KImageEffect::implode(QImage &src, double factor, + unsigned int background) +{ + double amount, distance, radius; + double x_center, x_distance, x_scale; + double y_center, y_distance, y_scale; + unsigned int *destData; + int x, y; + + QImage dest(src.width(), src.height(), src.transparent()); + + // compute scaling factor + x_scale = 1.0; + y_scale = 1.0; + x_center = (double)0.5*src.width(); + y_center = (double)0.5*src.height(); + radius=x_center; + if(src.width() > src.height()) + y_scale = (double)src.width()/src.height(); + else if(src.width() < src.height()){ + x_scale = (double) src.height()/src.width(); + radius = y_center; + } + amount=factor/10.0; + if(amount >= 0) + amount/=10.0; + if(src.depth() > 8){ // DirectClass source image + unsigned int *srcData; + for(y=0; y < src.height(); ++y){ + srcData = (unsigned int *)src.scanLine(y); + destData = (unsigned int *)dest.scanLine(y); + y_distance=y_scale*(y-y_center); + for(x=0; x < src.width(); ++x){ + destData[x] = srcData[x]; + x_distance = x_scale*(x-x_center); + distance= x_distance*x_distance+y_distance*y_distance; + if(distance < (radius*radius)){ + double factor; + // Implode the pixel. + factor=1.0; + if(distance > 0.0) + factor= + pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount); + destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center, + factor*y_distance/y_scale+y_center, + background); + } + } + } + } + /*else{ // PsudeoClass source image + unsigned char *srcData; + unsigned char idx; + unsigned int *cTable = src.colorTable(); + for(y=0; y < src.height(); ++y){ + srcData = (unsigned char *)src.scanLine(y); + destData = (unsigned int *)dest.scanLine(y); + y_distance=y_scale*(y-y_center); + for(x=0; x < src.width(); ++x){ + idx = srcData[x]; + destData[x] = cTable[idx]; + x_distance = x_scale*(x-x_center); + distance= x_distance*x_distance+y_distance*y_distance; + if(distance < (radius*radius)){ + double factor; + // Implode the pixel. + factor=1.0; + if(distance > 0.0) + factor= + pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount); + destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center, + factor*y_distance/y_scale+y_center, + background); + } + } + } + + }*/ + return(dest); +} + +#if 0 +QImage KImageEffect::rotate(QImage &img, RotateDirection r) +{ + QImage dest; + int x, y; + if(img.depth() > 8){ + unsigned int *srcData, *destData; + switch(r){ + case Rotate90: + dest.create(img.height(), img.width(), img.depth()); + for(y=0; y < img.height(); ++y){ + srcData = (unsigned int *)img.scanLine(y); + for(x=0; x < img.width(); ++x){ + destData = (unsigned int *)dest.scanLine(x); + destData[img.height()-y-1] = srcData[x]; + } + } + break; + case Rotate180: + dest.create(img.width(), img.height(), img.depth()); + for(y=0; y < img.height(); ++y){ + srcData = (unsigned int *)img.scanLine(y); + destData = (unsigned int *)dest.scanLine(img.height()-y-1); + for(x=0; x < img.width(); ++x) + destData[img.width()-x-1] = srcData[x]; + } + break; + case Rotate270: + dest.create(img.height(), img.width(), img.depth()); + for(y=0; y < img.height(); ++y){ + srcData = (unsigned int *)img.scanLine(y); + for(x=0; x < img.width(); ++x){ + destData = (unsigned int *)dest.scanLine(img.width()-x-1); + destData[y] = srcData[x]; + } + } + break; + default: + dest = img; + break; + } + } + else{ + unsigned char *srcData, *destData; + unsigned int *srcTable, *destTable; + switch(r){ + case Rotate90: + dest.create(img.height(), img.width(), img.depth()); + dest.setNumColors(img.numColors()); + srcTable = (unsigned int *)img.colorTable(); + destTable = (unsigned int *)dest.colorTable(); + for(x=0; x < img.numColors(); ++x) + destTable[x] = srcTable[x]; + for(y=0; y < img.height(); ++y){ + srcData = (unsigned char *)img.scanLine(y); + for(x=0; x < img.width(); ++x){ + destData = (unsigned char *)dest.scanLine(x); + destData[img.height()-y-1] = srcData[x]; + } + } + break; + case Rotate180: + dest.create(img.width(), img.height(), img.depth()); + dest.setNumColors(img.numColors()); + srcTable = (unsigned int *)img.colorTable(); + destTable = (unsigned int *)dest.colorTable(); + for(x=0; x < img.numColors(); ++x) + destTable[x] = srcTable[x]; + for(y=0; y < img.height(); ++y){ + srcData = (unsigned char *)img.scanLine(y); + destData = (unsigned char *)dest.scanLine(img.height()-y-1); + for(x=0; x < img.width(); ++x) + destData[img.width()-x-1] = srcData[x]; + } + break; + case Rotate270: + dest.create(img.height(), img.width(), img.depth()); + dest.setNumColors(img.numColors()); + srcTable = (unsigned int *)img.colorTable(); + destTable = (unsigned int *)dest.colorTable(); + for(x=0; x < img.numColors(); ++x) + destTable[x] = srcTable[x]; + for(y=0; y < img.height(); ++y){ + srcData = (unsigned char *)img.scanLine(y); + for(x=0; x < img.width(); ++x){ + destData = (unsigned char *)dest.scanLine(img.width()-x-1); + destData[y] = srcData[x]; + } + } + break; + default: + dest = img; + break; + } + + } + return(dest); +} +#endif + +void KImageEffect::solarize(QImage &img, double factor) +{ + int i, count; + int threshold; + unsigned int *data; + + threshold = (int)(factor*(MaxRGB+1)/100.0); + /*if(img.depth() < 32){ + data = (unsigned int *)img.colorTable(); + count = img.numColors(); + } + else*/{ + data = (unsigned int *)img.bits(); + count = img.width()*img.height(); + } + + for(i=0; i < count; ++i){ + data[i] = qRgba(qRed(data[i]) > threshold ? MaxRGB-qRed(data[i]) : qRed(data[i]), + qGreen(data[i]) > threshold ? MaxRGB-qGreen(data[i]) : qGreen(data[i]), + qBlue(data[i]) > threshold ? MaxRGB-qBlue(data[i]) : qBlue(data[i]), + qAlpha(data[i])); + } +} + +QImage KImageEffect::spread(QImage &src, unsigned int amount) +{ + int quantum, x, y; + int x_distance, y_distance; + if(src.width() < 3 || src.height() < 3) + return(src); + QImage dest(src.width(), src.height(), src.transparent()); + quantum=(amount+1) >> 1; + if(src.depth() > 8){ // DirectClass source image + unsigned int *p, *q; + for(y=0; y < src.height(); y++){ + q = (unsigned int *)dest.scanLine(y); + for(x=0; x < src.width(); x++){ + x_distance = x + ((rand() % (amount+1))-quantum); + y_distance = y + ((rand() % (amount+1))-quantum); + x_distance = QMIN(x_distance, src.width()-1); + y_distance = QMIN(y_distance, src.height()-1); + if(x_distance < 0) + x_distance = 0; + if(y_distance < 0) + y_distance = 0; + p = (unsigned int *)src.scanLine(y_distance); + p += x_distance; + *q++=(*p); + } + } + } + /*else { // PsudeoClass source image + // just do colortable values + unsigned char *p, *q; + for(y=0; y < src.height(); y++){ + q = (unsigned char *)dest.scanLine(y); + for(x=0; x < src.width(); x++){ + x_distance = x + ((rand() & (amount+1))-quantum); + y_distance = y + ((rand() & (amount+1))-quantum); + x_distance = QMIN(x_distance, src.width()-1); + y_distance = QMIN(y_distance, src.height()-1); + if(x_distance < 0) + x_distance = 0; + if(y_distance < 0) + y_distance = 0; + p = (unsigned char *)src.scanLine(y_distance); + p += x_distance; + *q++=(*p); + } + } + }*/ + return(dest); +} + +QImage KImageEffect::swirl(QImage &src, double degrees, + unsigned int background) +{ + double cosine, distance, factor, radius, sine, x_center, x_distance, + x_scale, y_center, y_distance, y_scale; + int x, y; + unsigned int *q; + QImage dest(src.width(), src.height(), src.transparent()); + + // compute scaling factor + x_center = src.width()/2.0; + y_center = src.height()/2.0; + radius = QMAX(x_center,y_center); + x_scale=1.0; + y_scale=1.0; + if(src.width() > src.height()) + y_scale=(double)src.width()/src.height(); + else if(src.width() < src.height()) + x_scale=(double)src.height()/src.width(); + //degrees=DegreesToRadians(degrees); + // swirl each row + if(src.depth() > 8){ // DirectClass source image + unsigned int *p; + for(y=0; y < src.height(); y++){ + p = (unsigned int *)src.scanLine(y); + q = (unsigned int *)dest.scanLine(y); + y_distance = y_scale*(y-y_center); + for(x=0; x < src.width(); x++){ + // determine if the pixel is within an ellipse + *q=(*p); + x_distance = x_scale*(x-x_center); + distance = x_distance*x_distance+y_distance*y_distance; + if (distance < (radius*radius)){ + // swirl + factor = 1.0-sqrt(distance)/radius; + sine = sin(degrees*factor*factor); + cosine = cos(degrees*factor*factor); + *q = interpolateColor(&src, + (cosine*x_distance-sine*y_distance)/x_scale+x_center, + (sine*x_distance+cosine*y_distance)/y_scale+y_center, + background); + } + p++; + q++; + } + } + } + /*else{ // PsudeoClass source image + unsigned char *p; + unsigned int *cTable = (unsigned int *)src.colorTable(); + for(y=0; y < src.height(); y++){ + p = (unsigned char *)src.scanLine(y); + q = (unsigned int *)dest.scanLine(y); + y_distance = y_scale*(y-y_center); + for(x=0; x < src.width(); x++){ + // determine if the pixel is within an ellipse + *q = *(cTable+(*p)); + x_distance = x_scale*(x-x_center); + distance = x_distance*x_distance+y_distance*y_distance; + if (distance < (radius*radius)){ + // swirl + factor = 1.0-sqrt(distance)/radius; + sine = sin(degrees*factor*factor); + cosine = cos(degrees*factor*factor); + *q = interpolateColor(&src, + (cosine*x_distance-sine*y_distance)/x_scale+x_center, + (sine*x_distance+cosine*y_distance)/y_scale+y_center, + background); + } + p++; + q++; + } + } + + }*/ + return(dest); +} + +QImage KImageEffect::wave(QImage &src, double amplitude, double wavelength, + unsigned int background) +{ + double *sine_map; + int x, y; + unsigned int *q; + + QImage dest(src.width(), src.height() + (int)(2*fabs(amplitude)), src.transparent()); + // allocate sine map + sine_map = (double *)malloc(dest.width()*sizeof(double)); + if(!sine_map) + return(src); + for(x=0; x < dest.width(); ++x) + sine_map[x]=fabs(amplitude)+amplitude*sin((2*M_PI*x)/wavelength); + // wave image + for(y=0; y < dest.height(); ++y){ + q = (unsigned int *)dest.scanLine(y); + for (x=0; x < dest.width(); x++){ + *q=interpolateColor(&src, x, (int)(y-sine_map[x]), background); + ++q; + } + } + free(sine_map); + return(dest); +} + +// +// The following methods work by computing a value from neighboring pixels +// (mosfet 05/26/03) +// + +// New algorithms based on ImageMagick 5.5.6 (05/26/03) + +#if 0 +QImage KImageEffect::oilPaint(QImage &src, int /*radius*/) +{ + /* binary compat method - remove me when possible! */ + return(oilPaintConvolve(src, 0)); +} +#endif + +QImage KImageEffect::oilPaintConvolve(QImage &src, double radius) +{ + unsigned long count /*,*histogram*/; + unsigned long histogram[256]; + unsigned int k; + int width; + int x, y, mx, my, sx, sy; + int mcx, mcy; + unsigned int *s=0, *q; + + //if(src.depth() < 32) + // src.convertDepth(32); + QImage dest(src.width(), src.height(), src.transparent()); + + width = getOptimalKernelWidth(radius, 0.5); + if(src.width() < width || width <= 0) { + //qWarning("KImageEffect::oilPaintConvolve(): Image is smaller than radius!"); + return(dest); + } + /* + histogram = (unsigned long *)malloc(256*sizeof(unsigned long)); + if(!histogram){ + qWarning("KImageEffect::oilPaintColvolve(): Unable to allocate memory!"); + return(dest); + } + */ + unsigned int **jumpTable = (unsigned int **)src.jumpTable(); + for(y=0; y < dest.height(); ++y){ + sy = y-(width/2); + q = (unsigned int *)dest.scanLine(y); + for(x=0; x < dest.width(); ++x){ + count = 0; + memset(histogram, 0, 256*sizeof(unsigned long)); + //memset(histogram, 0, 256); + sy = y-(width/2); + for(mcy=0; mcy < width; ++mcy, ++sy){ + my = sy < 0 ? 0 : sy > src.height()-1 ? + src.height()-1 : sy; + sx = x+(-width/2); + for(mcx=0; mcx < width; ++mcx, ++sx){ + mx = sx < 0 ? 0 : sx > src.width()-1 ? + src.width()-1 : sx; + + k = intensityValue(jumpTable[my][mx]); + if(k > 255){ + //qWarning("KImageEffect::oilPaintConvolve(): k is %d", + // k); + k = 255; + } + histogram[k]++; + if(histogram[k] > count){ + count = histogram[k]; + s = jumpTable[my]+mx; + } + } + } + *q++ = (*s); + } + } + /* liberateMemory(histogram); */ + return(dest); +} + +#if 0 +QImage KImageEffect::charcoal(QImage &src, double /*factor*/) +{ + /* binary compat method - remove me when possible! */ + return(charcoal(src, 0, 1)); +} +#endif + +QImage KImageEffect::charcoal(QImage &src, double radius, double sigma) +{ + QImage img0 = edge(src, radius); + QImage img = blur(img0, radius, sigma); + img0.release(); + normalize(img); + img.invertPixels(); + KImageEffect::toGray(img); + return(img); +} + +void KImageEffect::normalize(QImage &image) +{ + struct double_packet high, low, intensity, *histogram; + struct short_packet *normalize_map; + long long number_pixels; + int x, y; + unsigned int *p, *q; + register long i; + unsigned long threshold_intensity; + unsigned char r, g, b, a; + + //if(image.depth() < 32) // result will always be 32bpp + // image = image.convertDepth(32); + + histogram = (struct double_packet *) + malloc(256*sizeof(struct double_packet)); + normalize_map = (struct short_packet *) + malloc(256*sizeof(struct short_packet)); + + if(!histogram || !normalize_map){ + if(histogram) + liberateMemory( &histogram); + if(normalize_map) + liberateMemory( &normalize_map); + //qWarning("KImageEffect::normalize(): Unable to allocate memory!"); + return; + } + + /* + Form histogram. + */ + memset(histogram, 0, 256*sizeof(struct double_packet)); + for(y=0; y < image.height(); ++y){ + p = (unsigned int *)image.scanLine(y); + for(x=0; x < image.width(); ++x){ + histogram[(unsigned char)(qRed(*p))].red++; + histogram[(unsigned char)(qGreen(*p))].green++; + histogram[(unsigned char)(qBlue(*p))].blue++; + histogram[(unsigned char)(qAlpha(*p))].alpha++; + p++; + } + } + + /* + Find the histogram boundaries by locating the 0.1 percent levels. + */ + number_pixels = (long long)image.width()*image.height(); + threshold_intensity = number_pixels/1000; + memset(&high, 0, sizeof(struct double_packet)); + memset(&low, 0, sizeof(struct double_packet)); + + /* red */ + memset(&intensity, 0, sizeof(struct double_packet)); + for(high.red=255; high.red != 0; high.red--){ + intensity.red+=histogram[(unsigned char)high.red].red; + if(intensity.red > threshold_intensity) + break; + } + if(low.red == high.red){ + threshold_intensity = 0; + memset(&intensity, 0, sizeof(struct double_packet)); + for(low.red=0; low.red < 255; low.red++){ + intensity.red+=histogram[(unsigned char)low.red].red; + if(intensity.red > threshold_intensity) + break; + } + memset(&intensity, 0, sizeof(struct double_packet)); + for(high.red=255; high.red != 0; high.red--){ + intensity.red+=histogram[(unsigned char)high.red].red; + if(intensity.red > threshold_intensity) + break; + } + } + + /* green */ + memset(&intensity, 0, sizeof(struct double_packet)); + for(high.green=255; high.green != 0; high.green--){ + intensity.green+=histogram[(unsigned char)high.green].green; + if(intensity.green > threshold_intensity) + break; + } + if(low.green == high.green){ + threshold_intensity = 0; + memset(&intensity, 0, sizeof(struct double_packet)); + for(low.green=0; low.green < 255; low.green++){ + intensity.green+=histogram[(unsigned char)low.green].green; + if(intensity.green > threshold_intensity) + break; + } + memset(&intensity,0,sizeof(struct double_packet)); + for(high.green=255; high.green != 0; high.green--){ + intensity.green+=histogram[(unsigned char)high.green].green; + if(intensity.green > threshold_intensity) + break; + } + } + + /* blue */ + memset(&intensity, 0, sizeof(struct double_packet)); + for(high.blue=255; high.blue != 0; high.blue--){ + intensity.blue+=histogram[(unsigned char)high.blue].blue; + if(intensity.blue > threshold_intensity) + break; + } + if(low.blue == high.blue){ + threshold_intensity = 0; + memset(&intensity, 0, sizeof(struct double_packet)); + for(low.blue=0; low.blue < 255; low.blue++){ + intensity.blue+=histogram[(unsigned char)low.blue].blue; + if(intensity.blue > threshold_intensity) + break; + } + memset(&intensity,0,sizeof(struct double_packet)); + for(high.blue=255; high.blue != 0; high.blue--){ + intensity.blue+=histogram[(unsigned char)high.blue].blue; + if(intensity.blue > threshold_intensity) + break; + } + } + + /* alpha */ + memset(&intensity, 0, sizeof(struct double_packet)); + for(high.alpha=255; high.alpha != 0; high.alpha--){ + intensity.alpha+=histogram[(unsigned char)high.alpha].alpha; + if(intensity.alpha > threshold_intensity) + break; + } + if(low.alpha == high.alpha){ + threshold_intensity = 0; + memset(&intensity, 0, sizeof(struct double_packet)); + for(low.alpha=0; low.alpha < 255; low.alpha++){ + intensity.alpha+=histogram[(unsigned char)low.alpha].alpha; + if(intensity.alpha > threshold_intensity) + break; + } + memset(&intensity,0,sizeof(struct double_packet)); + for(high.alpha=255; high.alpha != 0; high.alpha--){ + intensity.alpha+=histogram[(unsigned char)high.alpha].alpha; + if(intensity.alpha > threshold_intensity) + break; + } + } + liberateMemory( &histogram); + + /* + Stretch the histogram to create the normalized image mapping. + */ + + // should the maxes be 65535? + memset(normalize_map, 0 ,256*sizeof(struct short_packet)); + for(i=0; i <= (long) 255; i++){ + if(i < (long) low.red) + normalize_map[i].red=0; + else if (i > (long) high.red) + normalize_map[i].red=65535; + else if (low.red != high.red) + normalize_map[i].red = + (unsigned short)((65535*(i-low.red))/(high.red-low.red)); + + if(i < (long) low.green) + normalize_map[i].green=0; + else if (i > (long) high.green) + normalize_map[i].green=65535; + else if (low.green != high.green) + normalize_map[i].green = + (unsigned short)((65535*(i-low.green))/(high.green-low.green)); + + if(i < (long) low.blue) + normalize_map[i].blue=0; + else if (i > (long) high.blue) + normalize_map[i].blue=65535; + else if (low.blue != high.blue) + normalize_map[i].blue = + (unsigned short)((65535*(i-low.blue))/(high.blue-low.blue)); + + if(i < (long) low.alpha) + normalize_map[i].alpha=0; + else if (i > (long) high.alpha) + normalize_map[i].alpha=65535; + else if (low.alpha != high.alpha) + normalize_map[i].alpha = + (unsigned short)((65535*(i-low.alpha))/(high.alpha-low.alpha)); + + } + + for(y=0; y < image.height(); ++y){ + q = (unsigned int *)image.scanLine(y); + for(x=0; x < image.width(); ++x){ + if(low.red != high.red) + r = (normalize_map[(unsigned short)(qRed(q[x]))].red)/257; + else + r = qRed(q[x]); + if(low.green != high.green) + g = (normalize_map[(unsigned short)(qGreen(q[x]))].green)/257; + else + g = qGreen(q[x]); + if(low.blue != high.blue) + b = (normalize_map[(unsigned short)(qBlue(q[x]))].blue)/257; + else + b = qBlue(q[x]); + if(low.alpha != high.alpha) + a = (normalize_map[(unsigned short)(qAlpha(q[x]))].alpha)/257; + else + a = qAlpha(q[x]); + q[x] = qRgba(r, g, b, a); + } + } + liberateMemory( &normalize_map); +} + +void KImageEffect::equalize(QImage &image) +{ + struct double_packet high, low, intensity, *map, *histogram; + struct short_packet *equalize_map; + int x, y; + unsigned int *p, *q; + long i; + unsigned char r, g, b, a; + + //if(image.depth() < 32) // result will always be 32bpp + // image = image.convertDepth(32); + + histogram=(struct double_packet *) malloc(256*sizeof(struct double_packet)); + map=(struct double_packet *) malloc(256*sizeof(struct double_packet)); + equalize_map=(struct short_packet *)malloc(256*sizeof(struct short_packet)); + if(!histogram || !map || !equalize_map){ + if(histogram) + liberateMemory( &histogram); + if(map) + liberateMemory( &map); + if(equalize_map) + liberateMemory( &equalize_map); + //qWarning("KImageEffect::equalize(): Unable to allocate memory!"); + return; + } + + /* + Form histogram. + */ + memset(histogram, 0, 256*sizeof(struct double_packet)); + for(y=0; y < image.height(); ++y){ + p = (unsigned int *)image.scanLine(y); + for(x=0; x < image.width(); ++x){ + histogram[(unsigned char)(qRed(*p))].red++; + histogram[(unsigned char)(qGreen(*p))].green++; + histogram[(unsigned char)(qBlue(*p))].blue++; + histogram[(unsigned char)(qAlpha(*p))].alpha++; + p++; + } + } + /* + Integrate the histogram to get the equalization map. + */ + memset(&intensity, 0 ,sizeof(struct double_packet)); + for(i=0; i <= 255; ++i){ + intensity.red += histogram[i].red; + intensity.green += histogram[i].green; + intensity.blue += histogram[i].blue; + intensity.alpha += histogram[i].alpha; + map[i]=intensity; + } + low=map[0]; + high=map[255]; + memset(equalize_map, 0, 256*sizeof(short_packet)); + for(i=0; i <= 255; ++i){ + if(high.red != low.red) + equalize_map[i].red=(unsigned short) + ((65535*(map[i].red-low.red))/(high.red-low.red)); + if(high.green != low.green) + equalize_map[i].green=(unsigned short) + ((65535*(map[i].green-low.green))/(high.green-low.green)); + if(high.blue != low.blue) + equalize_map[i].blue=(unsigned short) + ((65535*(map[i].blue-low.blue))/(high.blue-low.blue)); + if(high.alpha != low.alpha) + equalize_map[i].alpha=(unsigned short) + ((65535*(map[i].alpha-low.alpha))/(high.alpha-low.alpha)); + } + liberateMemory( &histogram); + liberateMemory( &map); + + /* + Stretch the histogram. + */ + for(y=0; y < image.height(); ++y){ + q = (unsigned int *)image.scanLine(y); + for(x=0; x < image.width(); ++x){ + if(low.red != high.red) + r = (equalize_map[(unsigned short)(qRed(q[x]))].red/257); + else + r = qRed(q[x]); + if(low.green != high.green) + g = (equalize_map[(unsigned short)(qGreen(q[x]))].green/257); + else + g = qGreen(q[x]); + if(low.blue != high.blue) + b = (equalize_map[(unsigned short)(qBlue(q[x]))].blue/257); + else + b = qBlue(q[x]); + if(low.alpha != high.alpha) + a = (equalize_map[(unsigned short)(qAlpha(q[x]))].alpha/257); + else + a = qAlpha(q[x]); + q[x] = qRgba(r, g, b, a); + } + } + liberateMemory( &equalize_map); + +} + +QImage KImageEffect::edge(QImage &image, double radius) +{ + double *kernel; + int width; + register long i; + QImage dest; + + width = getOptimalKernelWidth(radius, 0.5); + if(image.width() < width || image.height() < width){ + //qWarning("KImageEffect::edge(): Image is smaller than radius!"); + return(dest); + } + kernel= (double *)malloc(width*width*sizeof(double)); + if(!kernel){ + //qWarning("KImageEffect::edge(): Unable to allocate memory!"); + return(dest); + } + for(i=0; i < (width*width); i++) + kernel[i]=(-1.0); + kernel[i/2]=width*width-1.0; + convolveImage(&image, &dest, width, kernel); + liberateMemory(&kernel); + return(dest); +} + +#if 0 +QImage KImageEffect::emboss(QImage &src) +{ + /* binary compat method - remove me when possible! */ + return(emboss(src, 0, 1)); +} +#endif + +QImage KImageEffect::emboss(QImage &image, double radius, double sigma) +{ + double alpha, *kernel; + int j, width; + register long i, u, v; + QImage dest; + + if(sigma == 0.0){ + //qWarning("KImageEffect::emboss(): Zero sigma is not permitted!"); + return(dest); + } + + width = getOptimalKernelWidth(radius, sigma); + if(image.width() < width || image.height() < width){ + //qWarning("KImageEffect::emboss(): Image is smaller than radius!"); + return(dest); + } + kernel= (double *)malloc(width*width*sizeof(double)); + if(!kernel){ + //qWarning("KImageEffect::emboss(): Unable to allocate memory!"); + return(dest); + } + + //if(image.depth() < 32) + // image = image.convertDepth(32); + + i=0; + j=width/2; + for(v=(-width/2); v <= (width/2); v++){ + for(u=(-width/2); u <= (width/2); u++){ + alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma)); + kernel[i]=((u < 0) || (v < 0) ? -8.0 : 8.0)*alpha/ + (2.0*MagickPI*sigma*sigma); + if (u == j) + kernel[i]=0.0; + i++; + } + j--; + } + convolveImage(&image, &dest, width, kernel); + liberateMemory(&kernel); + + equalize(dest); + return(dest); +} + +void KImageEffect::blurScanLine(double *kernel, int width, + unsigned int *src, unsigned int *dest, + int columns) +{ + register double *p; + unsigned int *q; + register int x; + register long i; + double red, green, blue, alpha; + double scale = 0.0; + + if(width > columns){ + for(x=0; x < columns; ++x){ + scale = 0.0; + red = blue = green = alpha = 0.0; + p = kernel; + q = src; + for(i=0; i < columns; ++i){ + if((i >= (x-width/2)) && (i <= (x+width/2))){ + red += (*p)*(qRed(*q)*257); + green += (*p)*(qGreen(*q)*257); + blue += (*p)*(qBlue(*q)*257); + alpha += (*p)*(qAlpha(*q)*257); + } + if(((i+width/2-x) >= 0) && ((i+width/2-x) < width)) + scale+=kernel[i+width/2-x]; + p++; + q++; + } + scale = 1.0/scale; + red = scale*(red+0.5); + green = scale*(green+0.5); + blue = scale*(blue+0.5); + alpha = scale*(alpha+0.5); + + red = red < 0 ? 0 : red > 65535 ? 65535 : red; + green = green < 0 ? 0 : green > 65535 ? 65535 : green; + blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue; + alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha; + + dest[x] = qRgba((unsigned char)(red/257UL), + (unsigned char)(green/257UL), + (unsigned char)(blue/257UL), + (unsigned char)(alpha/257UL)); + } + return; + } + + for(x=0; x < width/2; ++x){ + scale = 0.0; + red = blue = green = alpha = 0.0; + p = kernel+width/2-x; + q = src; + for(i=width/2-x; i < width; ++i){ + red += (*p)*(qRed(*q)*257); + green += (*p)*(qGreen(*q)*257); + blue += (*p)*(qBlue(*q)*257); + alpha += (*p)*(qAlpha(*q)*257); + scale += (*p); + p++; + q++; + } + scale=1.0/scale; + + red = scale*(red+0.5); + green = scale*(green+0.5); + blue = scale*(blue+0.5); + alpha = scale*(alpha+0.5); + + red = red < 0 ? 0 : red > 65535 ? 65535 : red; + green = green < 0 ? 0 : green > 65535 ? 65535 : green; + blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue; + alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha; + + dest[x] = qRgba((unsigned char)(red/257UL), + (unsigned char)(green/257UL), + (unsigned char)(blue/257UL), + (unsigned char)(alpha/257UL)); + } + + for(; x < columns-width/2; ++x){ + red = blue = green = alpha = 0.0; + p = kernel; + q = src+(x-width/2); + for (i=0; i < (long) width; ++i){ + red += (*p)*(qRed(*q)*257); + green += (*p)*(qGreen(*q)*257); + blue += (*p)*(qBlue(*q)*257); + alpha += (*p)*(qAlpha(*q)*257); + p++; + q++; + } + red = scale*(red+0.5); + green = scale*(green+0.5); + blue = scale*(blue+0.5); + alpha = scale*(alpha+0.5); + + red = red < 0 ? 0 : red > 65535 ? 65535 : red; + green = green < 0 ? 0 : green > 65535 ? 65535 : green; + blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue; + alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha; + + dest[x] = qRgba((unsigned char)(red/257UL), + (unsigned char)(green/257UL), + (unsigned char)(blue/257UL), + (unsigned char)(alpha/257UL)); + } + + for(; x < columns; ++x){ + red = blue = green = alpha = 0.0; + scale=0; + p = kernel; + q = src+(x-width/2); + for(i=0; i < columns-x+width/2; ++i){ + red += (*p)*(qRed(*q)*257); + green += (*p)*(qGreen(*q)*257); + blue += (*p)*(qBlue(*q)*257); + alpha += (*p)*(qAlpha(*q)*257); + scale += (*p); + p++; + q++; + } + scale=1.0/scale; + red = scale*(red+0.5); + green = scale*(green+0.5); + blue = scale*(blue+0.5); + alpha = scale*(alpha+0.5); + + red = red < 0 ? 0 : red > 65535 ? 65535 : red; + green = green < 0 ? 0 : green > 65535 ? 65535 : green; + blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue; + alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha; + + dest[x] = qRgba((unsigned char)(red/257UL), + (unsigned char)(green/257UL), + (unsigned char)(blue/257UL), + (unsigned char)(alpha/257UL)); + } +} + +int KImageEffect::getBlurKernel(int width, double sigma, double **kernel) +{ +#define KernelRank 3 + double alpha, normalize; + register long i; + int bias; + + assert(sigma != 0.0); + if(width == 0) + width = 3; + *kernel=(double *)malloc(width*sizeof(double)); + if(*kernel == (double *)NULL) + return(0); + memset(*kernel, 0, width*sizeof(double)); + bias = KernelRank*width/2; + for(i=(-bias); i <= bias; i++){ + alpha=exp(-((double) i*i)/(2.0*KernelRank*KernelRank*sigma*sigma)); + (*kernel)[(i+bias)/KernelRank]+=alpha/(MagickSQ2PI*sigma); + } + normalize=0; + for(i=0; i < width; i++) + normalize+=(*kernel)[i]; + for(i=0; i < width; i++) + (*kernel)[i]/=normalize; + + return(width); +} + +#if 0 +QImage KImageEffect::blur(QImage &src, double /*factor*/) +{ + /* binary compat method - remove me when possible! */ + return(blur(src, 0, 1)); +} +#endif + +QImage KImageEffect::blur(QImage &src, double radius, double sigma) +{ + double *kernel; + QImage dest; + int width; + int x, y; + unsigned int *scanline, *temp; + unsigned int *p, *q; + + if(sigma == 0.0){ + //qWarning("KImageEffect::blur(): Zero sigma is not permitted!"); + return(dest); + } + //if(src.depth() < 32) + // src = src.convertDepth(32); + + kernel=(double *) NULL; + if(radius > 0) + width=getBlurKernel((int) (2*ceil(radius)+1),sigma,&kernel); + else{ + double *last_kernel; + last_kernel=(double *) NULL; + width=getBlurKernel(3,sigma,&kernel); + + while ((long) (MaxRGB*kernel[0]) > 0){ + if(last_kernel != (double *)NULL){ + liberateMemory( &last_kernel); + } + last_kernel=kernel; + kernel = (double *)NULL; + width = getBlurKernel(width+2, sigma, &kernel); + } + if(last_kernel != (double *) NULL){ + liberateMemory( &kernel); + width-=2; + kernel = last_kernel; + } + } + + if(width < 3){ + //qWarning("KImageEffect::blur(): Kernel radius is too small!"); + liberateMemory( &kernel); + return(dest); + } + + dest.create(src.width(), src.height(), src.transparent()); + + scanline = (unsigned int *)malloc(sizeof(unsigned int)*src.height()); + temp = (unsigned int *)malloc(sizeof(unsigned int)*src.height()); + for(y=0; y < src.height(); ++y){ + p = (unsigned int *)src.scanLine(y); + q = (unsigned int *)dest.scanLine(y); + blurScanLine(kernel, width, p, q, src.width()); + } + + unsigned int **srcTable = (unsigned int **)src.jumpTable(); + unsigned int **destTable = (unsigned int **)dest.jumpTable(); + for(x=0; x < src.width(); ++x){ + for(y=0; y < src.height(); ++y){ + scanline[y] = srcTable[y][x]; + } + blurScanLine(kernel, width, scanline, temp, src.height()); + for(y=0; y < src.height(); ++y){ + destTable[y][x] = temp[y]; + } + } + liberateMemory( &scanline); + liberateMemory( &temp); + liberateMemory( &kernel); + return(dest); +} + +bool KImageEffect::convolveImage(QImage *image, QImage *dest, + const unsigned int order, + const double *kernel) +{ + long width; + double red, green, blue, alpha; + double normalize, *normal_kernel; + register const double *k; + register unsigned int *q; + int x, y, mx, my, sx, sy; + long i; + int mcx, mcy; + + width = order; + if((width % 2) == 0){ + //qWarning("KImageEffect: Kernel width must be an odd number!"); + return(false); + } + normal_kernel = (double *)malloc(width*width*sizeof(double)); + if(!normal_kernel){ + //qWarning("KImageEffect: Unable to allocate memory!"); + return(false); + } + dest->reset(); + dest->create(image->width(), image->height(), image->transparent()); + //if(image->depth() < 32) + // *image = image->convertDepth(32); + + normalize=0.0; + for(i=0; i < (width*width); i++) + normalize += kernel[i]; + if(fabs(normalize) <= MagickEpsilon) + normalize=1.0; + normalize=1.0/normalize; + for(i=0; i < (width*width); i++) + normal_kernel[i] = normalize*kernel[i]; + + unsigned int **jumpTable = (unsigned int **)image->jumpTable(); + for(y=0; y < dest->height(); ++y){ + sy = y-(width/2); + q = (unsigned int *)dest->scanLine(y); + for(x=0; x < dest->width(); ++x){ + k = normal_kernel; + red = green = blue = alpha = 0; + sy = y-(width/2); + for(mcy=0; mcy < width; ++mcy, ++sy){ + my = sy < 0 ? 0 : sy > image->height()-1 ? + image->height()-1 : sy; + sx = x+(-width/2); + for(mcx=0; mcx < width; ++mcx, ++sx){ + mx = sx < 0 ? 0 : sx > image->width()-1 ? + image->width()-1 : sx; + red += (*k)*(qRed(jumpTable[my][mx])*257); + green += (*k)*(qGreen(jumpTable[my][mx])*257); + blue += (*k)*(qBlue(jumpTable[my][mx])*257); + alpha += (*k)*(qAlpha(jumpTable[my][mx])*257); + ++k; + } + } + + red = red < 0 ? 0 : red > 65535 ? 65535 : red+0.5; + green = green < 0 ? 0 : green > 65535 ? 65535 : green+0.5; + blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue+0.5; + alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha+0.5; + + *q++ = qRgba((unsigned char)(red/257UL), + (unsigned char)(green/257UL), + (unsigned char)(blue/257UL), + (unsigned char)(alpha/257UL)); + } + } + free(normal_kernel); + return(true); + +} + +int KImageEffect::getOptimalKernelWidth(double radius, double sigma) +{ + double normalize, value; + long width; + register long u; + + assert(sigma != 0.0); + if(radius > 0.0) + return((int)(2.0*ceil(radius)+1.0)); + for(width=5; ;){ + normalize=0.0; + for(u=(-width/2); u <= (width/2); u++) + normalize+=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma); + u=width/2; + value=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma)/normalize; + if((long)(65535*value) <= 0) + break; + width+=2; + } + return((int)width-2); +} + +#if 0 +QImage KImageEffect::sharpen(QImage &src, double /*factor*/) +{ + /* binary compat method - remove me when possible! */ + return(sharpen(src, 0, 1)); +} +#endif + +QImage KImageEffect::sharpen(QImage &image, double radius, double sigma) +{ + double alpha, normalize, *kernel; + int width; + register long i, u, v; + QImage dest; + + if(sigma == 0.0){ + //qWarning("KImageEffect::sharpen(): Zero sigma is not permitted!"); + return(dest); + } + width = getOptimalKernelWidth(radius, sigma); + if(image.width() < width){ + //qWarning("KImageEffect::sharpen(): Image is smaller than radius!"); + return(dest); + } + kernel = (double *)malloc(width*width*sizeof(double)); + if(!kernel){ + //qWarning("KImageEffect::sharpen(): Unable to allocate memory!"); + return(dest); + } + + i = 0; + normalize=0.0; + for(v=(-width/2); v <= (width/2); v++){ + for(u=(-width/2); u <= (width/2); u++){ + alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma)); + kernel[i]=alpha/(2.0*MagickPI*sigma*sigma); + normalize+=kernel[i]; + i++; + } + } + kernel[i/2]=(-2.0)*normalize; + convolveImage(&image, &dest, width, kernel); + liberateMemory( &kernel); + return(dest); +} + +// End of new algorithms + +QImage KImageEffect::shade(QImage &src, bool color_shading, double azimuth, + double elevation) +{ + struct PointInfo{ + double x, y, z; + }; + + double distance, normal_distance, shade; + int x, y; + + struct PointInfo light, normal; + + unsigned int *q; + + QImage dest(src.width(), src.height(), src.transparent()); + + //azimuth = DegreesToRadians(azimuth); + //elevation = DegreesToRadians(elevation); + light.x = MaxRGB*cos(azimuth)*cos(elevation); + light.y = MaxRGB*sin(azimuth)*cos(elevation); + light.z = MaxRGB*sin(elevation); + normal.z= 2*MaxRGB; // constant Z of surface normal + + if(src.depth() > 8){ // DirectClass source image + unsigned int *p, *s0, *s1, *s2; + for(y=0; y < src.height(); ++y){ + p = (unsigned int *)src.scanLine(QMIN(QMAX(y-1,0),src.height()-3)); + q = (unsigned int *)dest.scanLine(y); + // shade this row of pixels. + *q++=(*(p+src.width())); + p++; + s0 = p; + s1 = p + src.width(); + s2 = p + 2*src.width(); + for(x=1; x < src.width()-1; ++x){ + // determine the surface normal and compute shading. + normal.x=intensityValue(*(s0-1))+intensityValue(*(s1-1))+intensityValue(*(s2-1))- + (double) intensityValue(*(s0+1))-(double) intensityValue(*(s1+1))- + (double) intensityValue(*(s2+1)); + normal.y=intensityValue(*(s2-1))+intensityValue(*s2)+intensityValue(*(s2+1))- + (double) intensityValue(*(s0-1))-(double) intensityValue(*s0)- + (double) intensityValue(*(s0+1)); + if((normal.x == 0) && (normal.y == 0)) + shade=light.z; + else{ + shade=0.0; + distance=normal.x*light.x+normal.y*light.y+normal.z*light.z; + if (distance > 0.0){ + normal_distance= + normal.x*normal.x+normal.y*normal.y+normal.z*normal.z; + if(fabs(normal_distance) > 0.0000001) + shade=distance/sqrt(normal_distance); + } + } + if(!color_shading){ + *q = qRgba((unsigned char)(shade), + (unsigned char)(shade), + (unsigned char)(shade), + qAlpha(*s1)); + } + else{ + *q = qRgba((unsigned char)((shade*qRed(*s1))/(MaxRGB+1)), + (unsigned char)((shade*qGreen(*s1))/(MaxRGB+1)), + (unsigned char)((shade*qBlue(*s1))/(MaxRGB+1)), + qAlpha(*s1)); + } + ++s0; + ++s1; + ++s2; + q++; + } + *q++=(*s1); + } + }/* + else{ // PsudeoClass source image + unsigned char *p, *s0, *s1, *s2; + int scanLineIdx; + unsigned int *cTable = (unsigned int *)src.colorTable(); + for(y=0; y < src.height(); ++y){ + scanLineIdx = QMIN(QMAX(y-1,0),src.height()-3); + p = (unsigned char *)src.scanLine(scanLineIdx); + q = (unsigned int *)dest.scanLine(y); + // shade this row of pixels. + s0 = p; + s1 = (unsigned char *) src.scanLine(scanLineIdx+1); + s2 = (unsigned char *) src.scanLine(scanLineIdx+2); + *q++=(*(cTable+(*s1))); + ++p; + ++s0; + ++s1; + ++s2; + for(x=1; x < src.width()-1; ++x){ + // determine the surface normal and compute shading. + normal.x=intensityValue(*(cTable+(*(s0-1))))+intensityValue(*(cTable+(*(s1-1))))+intensityValue(*(cTable+(*(s2-1))))- + (double) intensityValue(*(cTable+(*(s0+1))))-(double) intensityValue(*(cTable+(*(s1+1))))- + (double) intensityValue(*(cTable+(*(s2+1)))); + normal.y=intensityValue(*(cTable+(*(s2-1))))+intensityValue(*(cTable+(*s2)))+intensityValue(*(cTable+(*(s2+1))))- + (double) intensityValue(*(cTable+(*(s0-1))))-(double) intensityValue(*(cTable+(*s0)))- + (double) intensityValue(*(cTable+(*(s0+1)))); + if((normal.x == 0) && (normal.y == 0)) + shade=light.z; + else{ + shade=0.0; + distance=normal.x*light.x+normal.y*light.y+normal.z*light.z; + if (distance > 0.0){ + normal_distance= + normal.x*normal.x+normal.y*normal.y+normal.z*normal.z; + if(fabs(normal_distance) > 0.0000001) + shade=distance/sqrt(normal_distance); + } + } + if(!color_shading){ + *q = qRgba((unsigned char)(shade), + (unsigned char)(shade), + (unsigned char)(shade), + qAlpha(*(cTable+(*s1)))); + } + else{ + *q = qRgba((unsigned char)((shade*qRed(*(cTable+(*s1))))/(MaxRGB+1)), + (unsigned char)((shade*qGreen(*(cTable+(*s1))))/(MaxRGB+1)), + (unsigned char)((shade*qBlue(*(cTable+(*s1))))/(MaxRGB+1)), + qAlpha(*s1)); + } + ++s0; + ++s1; + ++s2; + q++; + } + *q++=(*(cTable+(*s1))); + } + }*/ + return(dest); +} + +#if 0 +// High quality, expensive HSV contrast. You can do a faster one by just +// taking a grayscale threshold (ie: 128) and incrementing RGB color +// channels above it and decrementing those below it, but this gives much +// better results. (mosfet 12/28/01) +void KImageEffect::contrastHSV(QImage &image, bool sharpen) +{ + int i, sign; + unsigned int *data; + int count; + double brightness, scale, theta; + QColor c; + int h, s, v; + uint col; + + sign = sharpen ? 1 : -1; + scale=0.5000000000000001; + if(image.depth() > 8){ + count = image.width()*image.height(); + data = (unsigned int *)image.bits(); + } + else{ + count = image.numColors(); + data = (unsigned int *)image.colorTable(); + } + for(i=0; i < count; ++i){ + col = data[i]; + INVERT(col); + c.setRgb(data[i]); + c.hsv(&h, &s, &v); + brightness = v/255.0; + theta=(brightness-0.5)*M_PI; + brightness+=scale*(((scale*((sin(theta)+1.0)))-brightness)*sign); + if (brightness > 1.0) + brightness=1.0; + else + if (brightness < 0) + brightness=0.0; + v = (int)(brightness*255); + c.setHsv(h, s, v); + data[i] = qRgba(c.red(), c.green(), c.blue(), qAlpha(data[i])); + INVERT(data[i]); + } +} + + +struct BumpmapParams { + BumpmapParams( double bm_azimuth, double bm_elevation, + int bm_depth, KImageEffect::BumpmapType bm_type, + bool invert ) { + /* Convert to radians */ + double azimuth = DegreesToRadians( bm_azimuth ); + double elevation = DegreesToRadians( bm_elevation ); + + /* Calculate the light vector */ + lx = (int)( cos(azimuth) * cos(elevation) * 255.0 ); + ly = (int)( sin(azimuth) * cos(elevation) * 255.0 ); + int lz = (int)( sin(elevation) * 255.0 ); + + /* Calculate constant Z component of surface normal */ + int nz = (6 * 255) / bm_depth; + nz2 = nz * nz; + nzlz = nz * lz; + + /* Optimize for vertical normals */ + background = lz; + + /* Calculate darkness compensation factor */ + compensation = sin(elevation); + + /* Create look-up table for map type */ + for (int i = 0; i < 256; i++) + { + double n = 0; + switch (bm_type) + { + case KImageEffect::Spherical: + n = i / 255.0 - 1.0; + lut[i] = (int) (255.0 * sqrt(1.0 - n * n) + 0.5); + break; + + case KImageEffect::Sinuosidal: + n = i / 255.0; + lut[i] = (int) (255.0 * (sin((-M_PI / 2.0) + M_PI * n) + 1.0) / + 2.0 + 0.5); + break; + + case KImageEffect::Linear: + default: + lut[i] = i; + } + + if (invert) + lut[i] = 255 - lut[i]; + } + } + int lx, ly; + int nz2, nzlz; + int background; + double compensation; + uchar lut[256]; +}; + + +static void bumpmap_convert_row( uint *row, + int width, + int bpp, + int has_alpha, + uchar *lut, + int waterlevel ) +{ + uint *p; + + p = row; + + has_alpha = has_alpha ? 1 : 0; + + if (bpp >= 3) + for (; width; width--) + { + if (has_alpha) { + unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5); + *p++ = lut[(unsigned int) ( waterlevel + + ( ( idx - + waterlevel) * qBlue( *row )) / 255.0 )]; + } else { + unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5); + *p++ = lut[idx]; + } + + ++row; + } +} + +static void bumpmap_row( uint *src, + uint *dest, + int width, + int bpp, + int has_alpha, + uint *bm_row1, + uint *bm_row2, + uint *bm_row3, + int bm_width, + int bm_xofs, + bool tiled, + bool row_in_bumpmap, + int ambient, + bool compensate, + BumpmapParams *params ) +{ + int xofs1, xofs2, xofs3; + int shade; + int ndotl; + int nx, ny; + int x; + int tmp; + + tmp = bm_xofs; + xofs2 = MOD(tmp, bm_width); + + for (x = 0; x < width; x++) + { + /* Calculate surface normal from bump map */ + + if (tiled || (row_in_bumpmap && + x >= - tmp && x < - tmp + bm_width)) { + if (tiled) { + xofs1 = MOD(xofs2 - 1, bm_width); + xofs3 = MOD(xofs2 + 1, bm_width); + } else { + xofs1 = FXCLAMP(xofs2 - 1, 0, bm_width - 1); + xofs3 = FXCLAMP(xofs2 + 1, 0, bm_width - 1); + } + nx = (bm_row1[xofs1] + bm_row2[xofs1] + bm_row3[xofs1] - + bm_row1[xofs3] - bm_row2[xofs3] - bm_row3[xofs3]); + ny = (bm_row3[xofs1] + bm_row3[xofs2] + bm_row3[xofs3] - + bm_row1[xofs1] - bm_row1[xofs2] - bm_row1[xofs3]); + } else { + nx = ny = 0; + } + + /* Shade */ + + if ((nx == 0) && (ny == 0)) + shade = params->background; + else { + ndotl = nx * params->lx + ny * params->ly + params->nzlz; + + if (ndotl < 0) + shade = (int)( params->compensation * ambient ); + else { + shade = (int)( ndotl / sqrt(double(nx * nx + ny * ny + params->nz2)) ); + + shade = (int)( shade + QMAX(0.0, (255 * params->compensation - shade)) * + ambient / 255 ); + } + } + + /* Paint */ + + /** + * NOTE: if we want to work with non-32bit images the alpha handling would + * also change + */ + if (compensate) { + int red = (int)((qRed( *src ) * shade) / (params->compensation * 255)); + int green = (int)((qGreen( *src ) * shade) / (params->compensation * 255)); + int blue = (int)((qBlue( *src ) * shade) / (params->compensation * 255)); + int alpha = (int)((qAlpha( *src ) * shade) / (params->compensation * 255)); + ++src; + *dest++ = qRgba( red, green, blue, alpha ); + } else { + int red = qRed( *src ) * shade / 255; + int green = qGreen( *src ) * shade / 255; + int blue = qBlue( *src ) * shade / 255; + int alpha = qAlpha( *src ) * shade / 255; + ++src; + *dest++ = qRgba( red, green, blue, alpha ); + } + + /* Next pixel */ + + if (++xofs2 == bm_width) + xofs2 = 0; + } +} + +/** + * A bumpmapping algorithm. + * + * @param img the image you want bumpmap + * @param map the map used + * @param azimuth azimuth + * @param elevation elevation + * @param depth depth (not the depth of the image, but of the map) + * @param xofs X offset + * @param yofs Y offset + * @param waterlevel level that full transparency should represent + * @param ambient ambient lighting factor + * @param compensate compensate for darkening + * @param invert invert bumpmap + * @param type type of the bumpmap + * + * @return The destination image (dst) containing the result. + * @author Zack Rusin + */ +QImage KImageEffect::bumpmap(QImage &img, QImage &map, double azimuth, double elevation, + int depth, int xofs, int yofs, int waterlevel, + int ambient, bool compensate, bool invert, + BumpmapType type, bool tiled) +{ + QImage dst; + + if ( img.depth() != 32 || img.depth() != 32 ) { + qWarning( "Bump-mapping effect works only with 32 bit images"); + return dst; + } + + dst.create( img.width(), img.height(), img.depth() ); + int bm_width = map.width(); + int bm_height = map.height(); + int bm_bpp = map.depth(); + int bm_has_alpha = map.hasAlphaBuffer(); + + int yofs1, yofs2, yofs3; + + if ( tiled ) { + yofs2 = MOD( yofs, bm_height ); + yofs1 = MOD( yofs2 - 1, bm_height); + yofs3 = MOD( yofs2 + 1, bm_height); + } else { + yofs1 = 0; + yofs2 = 0; + yofs3 = FXCLAMP( yofs2+1, 0, bm_height - 1 ); + } + + BumpmapParams params( azimuth, elevation, depth, type, invert ); + + uint* bm_row1 = (unsigned int*)map.scanLine( yofs1 ); + uint* bm_row2 = (unsigned int*)map.scanLine( yofs2 ); + uint* bm_row3 = (unsigned int*)map.scanLine( yofs3 ); + + bumpmap_convert_row( bm_row1, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel ); + bumpmap_convert_row( bm_row2, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel ); + bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel ); + + for (int y = 0; y < img.height(); ++y) + { + int row_in_bumpmap = (y >= - yofs && y < - yofs + bm_height); + + uint* src_row = (unsigned int*)img.scanLine( y ); + uint* dest_row = (unsigned int*)dst.scanLine( y ); + + bumpmap_row( src_row, dest_row, img.width(), img.depth(), img.hasAlphaBuffer(), + bm_row1, bm_row2, bm_row3, bm_width, xofs, + tiled, + row_in_bumpmap, ambient, compensate, + ¶ms ); + + /* Next line */ + + if (tiled || row_in_bumpmap) + { + uint* bm_tmprow = bm_row1; + bm_row1 = bm_row2; + bm_row2 = bm_row3; + bm_row3 = bm_tmprow; + + if (++yofs2 == bm_height) + yofs2 = 0; + + if (tiled) + yofs3 = MOD(yofs2 + 1, bm_height); + else + yofs3 = FXCLAMP(yofs2 + 1, 0, bm_height - 1); + + bm_row3 = (unsigned int*)map.scanLine( yofs3 ); + bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha, + params.lut, waterlevel ); + } + } + return dst; +} +#endif diff --git a/main/lib/image.effect/kimageeffect.h b/main/lib/image.effect/kimageeffect.h new file mode 100644 index 00000000..16126fbc --- /dev/null +++ b/main/lib/image.effect/kimageeffect.h @@ -0,0 +1,811 @@ +/*************************************************************************** + + kimageeffect.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/* This file is part of the KDE libraries + Copyright (C) 1998, 1999, 2001, 2002 Daniel M. Duley + (C) 1998, 1999 Christian Tibirna + (C) 1998, 1999 Dirk A. Mueller + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +// $Id: kimageeffect.h,v 1.37 2003/10/07 22:40:42 mueller Exp $ + +#ifndef __KIMAGE_EFFECT_H +#define __KIMAGE_EFFECT_H + +#include "qcolor.h" +#include "qsize.h" +#include "qpoint.h" +#include "qrect.h" +#include "qimage.h" + +/** + * This class includes various QImage based graphical effects. + * + * Everything is + * static, so there is no need to create an instance of this class. You can + * just call the static methods. They are encapsulated here merely to provide + * a common namespace. + */ +class KImageEffect +{ +public: + /** + * This enum provides a gradient type specification + * @see KImageEffect::blend(), KImageEffect::gradient(), + * KImageEffect::unbalancedGradient() + */ + enum GradientType { VerticalGradient, + HorizontalGradient, + DiagonalGradient, + CrossDiagonalGradient, + PyramidGradient, + RectangleGradient, + PipeCrossGradient, + EllipticGradient + }; + + /** + * This enum provides a RGB channel specification + * @see KImageEffect::blend(), KImageEffect::channelIntensity(), + * KImageEffect::modulate() + */ + enum RGBComponent { Red = 1, //!< Red channel + Green = 2, //!< Green channel + Blue = 4, //!< Blue channel + All = 7, //!< All channels + Alpha = 8 + //Gray = 15 //!< Grey channel + }; + + /** + * This enum provides a lighting direction specification + * @see KImageEffect::hash() + */ + enum Lighting {NorthLite, //!< Lighting from the top of the image + NWLite, //!< Lighting from the top left of the image + WestLite, //!< Lighting from the left of the image + SWLite, //!< Lighting from the bottom left of the image + SouthLite, //!< Lighting from the bottom of the image + SELite, //!< Lighting from the bottom right of the image + EastLite, //!< Lighting from the right of the image + NELite //!< Lighting from the top right of the image + }; + + /** + * This enum provides a modulation type specification + * @see KImageEffect::modulate() + */ + enum ModulationType { Intensity, //!< Modulate image intensity + Saturation, //!< Modulate image saturation + HueShift, //!< Modulate image hue + Contrast //!< Modulate image contrast + }; + + /** + * This enum provides a noise type specification + * @see KImageEffect::addNoise() + */ + enum NoiseType { UniformNoise=0, //!< Uniform distribution + GaussianNoise, //!< Gaussian distribution + MultiplicativeGaussianNoise, //!< Multiplicative Gaussian distribution + ImpulseNoise, //!< Impulse distribution + LaplacianNoise, //!< Laplacian distribution + PoissonNoise //!< Poisson distribution + }; + + /** + * This enum provides a rotation specification. + * @see KImageEffect::rotate() + */ + enum RotateDirection{ Rotate90, //!< Rotate 90 degrees to the right. + Rotate180, //!< Rotate 180 degrees. + Rotate270 //!< Rotate 90 degrees to the left. + }; + + /** + * Create a gradient from color a to color b of the specified type. + * + * @param size The desired size of the gradient. + * @param ca Color a + * @param cb Color b + * @param type The type of gradient. + * @param ncols The number of colors to use when not running on a + * truecolor display. The gradient will be dithered to this number of + * colors. Pass 0 to prevent dithering. + */ + static QImage gradient(const QSize &size, const QColor &ca, + const QColor &cb, GradientType type, int ncols=3); + + /** + * Create an unbalanced gradient. + * + * An unbalanced gradient is a gradient where the transition from + * color a to color b is not linear, but in this case, exponential. + * + * @param size The desired size of the gradient. + * @param ca Color a + * @param cb Color b + * @param type The type of gradient. + * @param xfactor The x decay length. Use a value between -200 and 200. + * @param yfactor The y decay length. + * @param ncols The number of colors. See KImageEffect:gradient. + */ + static QImage unbalancedGradient(const QSize &size, const QColor &ca, + const QColor &cb, GradientType type, int xfactor = 100, + int yfactor = 100, int ncols = 3); + +#if 0 + /** + * Blends a color into the destination image, using an opacity + * value for blending one into another. Very fast direct pixel + * manipulation is used. + * + * This function uses MMX and SSE2 instructions to blend the + * image on processors that support it. + * + * @param clr source color to be blended into the destination image. + * @param dst destination image in which the source will be blended into. + * @param opacity opacity (between 0.0 and 1.0) which determines how much + * the source color will be blended into the destination image. + * @return The destination image (dst) containing the result. + * @author Karol Szwed (gallium@kde.org) + * @author Fredrik Höglund (fredrik@kde.org) + */ + static QImage& blend(const QColor& clr, QImage& dst, float opacity); + + /** + * Blend the src image into the destination image, using an opacity + * value for blending one into another. Very fast direct pixel + * manipulation is used. + * + * This function uses MMX and SSE2 instructions to blend the + * images on processors that support it. + * + * @param src source image to be blended into the destination image. + * @param dst destination image in which the source will be blended into. + * @param opacity opacity (between 0.0 and 1.0) which determines how much + * the source image will be blended into the destination image. + * @return The destination image (dst) containing the result. + * @author Karol Szwed (gallium@kde.org) + * @author Fredrik Höglund (fredrik@kde.org) + */ + static QImage& blend(QImage& src, QImage& dst, float opacity); + + /** + * Blend the provided image into a background of the indicated color. + * + * @param initial_intensity this parameter takes values from -1 to 1: + * a) if positive: how much to fade the image in its + * less affected spot + * b) if negative: roughly indicates how much of the image + * remains unaffected + * @param bgnd indicates the color of the background to blend in + * @param eff lets you choose what kind of blending you like + * @param anti_dir blend in the opposite direction (makes no much sense + * with concentric blending effects) + * @param image must be 32bpp + */ + static QImage& blend(QImage &image, float initial_intensity, + const QColor &bgnd, GradientType eff, + bool anti_dir=false); + + /** + * Blend an image into another one, using a gradient type + * for blending from one to another. + * + * @param image1 source1 and result of blending + * @param image2 source2 of blending + * @param gt gradient type for blending between source1 and source2 + * @param xf x decay length for unbalanced gradient tpye + * @param yf y decay length for unbalanced gradient tpye + */ + static QImage& blend(QImage &image1,QImage &image2, + GradientType gt, int xf=100, int yf=100); + + /** + * Blend an image into another one, using a color channel of a + * third image for the decision of blending from one to another. + * + * @param image1 Source 1 and result of blending + * @param image2 Source 2 of blending + * @param blendImage If the gray value of of pixel is 0, the result + * for this pixel is that of image1; for a gray value + * of 1, the pixel of image2 is used; for a value + * in between, a corresponding blending is used. + * @param channel The RBG channel to use for the blending decision. + */ + static QImage& blend(QImage &image1, QImage &image2, + QImage &blendImage, RGBComponent channel); + + /** + * Blend an image into another one, using alpha in the expected way. + * @param upper the "upper" image + * @param lower the "lower" image + * @param output the target image + * @author Rik Hemsley (rikkus) + */ + static bool blend(const QImage & upper, const QImage & lower, QImage & output); +// Not yet... static bool blend(const QImage & image1, const QImage & image2, QImage & output, const QRect & destRect); + + /** + * Blend an image into another one, using alpha in the expected way and + * over coordinates @p x and @p y with respect to the lower image. + * The output is a QImage which is the @p upper image already blended + * with the @p lower one, so its size will be (in general) the same than + * @p upper instead of the same size than @p lower like the method above. + * In fact, the size of @p output is like upper's one only when it can be + * painted on lower, if there has to be some clipping, output's size will + * be the clipped area and x and y will be set to the correct up-left corner + * where the clipped rectangle begins. + * @param x x-coordinate of lower image + * @param y y-coordinate of lower image + * @param upper the "upper" image + * @param lower the "lower" image + * @param output the target image + */ + static bool blend(int &x, int &y, const QImage & upper, const QImage & lower, QImage & output); + + /** + * Blend an image into another one, using alpha in the expected way and + * over coordinates @p x and @p y with respect to the lower image. + * The output is painted in the own @p lower image. This is an optimization + * of the blend method above provided by convenience. + * @param x x-coordinate of lower image + * @param y y-coordinate of lower image + * @param upper the "upper" image + * @param lower the "lower" image, which becomes the output image + */ + static bool blendOnLower(int x, int y, const QImage & upper, const QImage & lower); + + /** + * Blend part of an image into part of another, using the alpha channel in + * the expected way. + * Note that the destination rectangle will be correctly clipped. + * + * @param upper the "upper" image + * @param upperOffset Offset for the part of the upper image to be used. + * @param lower the "lower" image + * @param lowerRect Rectangle for the part of the lower image where the + * blending will occur. + * @since 3.2 + */ + static void blendOnLower(const QImage &upper, const QPoint &upperOffset, + QImage &lower, const QRect &lowerRect); + + /** + * Blend part of an image into part of another, using the opacity value + * and the alpha channel in the expected way. + * Note that the destination rectangle will be correctly clipped. + * + * @param upper the "upper" image + * @param upperOffset Offset for the part of the upper image to be used. + * @param lower the "lower" image + * @param lowerRect Rectangle for the part of the lower image where the + * blending will occur. + * @param opacity Opacity (between 0.0 and 1.0) which determines how much + * the source image will be blended into the destination image. + * @since 3.2 + */ + static void blendOnLower(const QImage &upper, const QPoint &upperOffset, + QImage &lower, const QRect &lowerRect, float opacity); + + /** + * Disposition of a source image on top of a destination image. + * @see KImageEffect::computeDestinationRect, KImageEffect::blendOnLower + * @since 3.2 + */ + enum Disposition { NoImage = 0, //!< Don't overlay + Centered, //!< Center top image on botton image + Tiled, //!< Tile top image on bottom image + CenterTiled, //!< Center and tile top image on bottom image + CenteredMaxpect, //!< Center and scale aspect + TiledMaxpect, //!< Tile and scale aspect + Scaled, //!< Scale + CenteredAutoFit //!< Center and scale or scale aspect + }; + + /** + * Compute the destination rectangle where to draw the upper image on top + * of another image using the given disposition. For tiled + * disposition, the rectangle should be duplicated on the whole area to + * obtained the wanted effect. + * + * @param lowerSize The size of the destination image. + * @param disposition The wanted disposition. + * @param upper The upper image. Note that this image may be scaled to + * adjust to the requested disposition. + * + * @return the computed rectangle. Its size may exceed @e lowerSize. + * @since 3.2 + */ + static QRect computeDestinationRect(const QSize &lowerSize, + Disposition disposition, QImage &upper); + + /** + * Blend an image on top of another using a given disposition and a given + * opacity. The alpha channel of the upper image is used in the expected + * way. Beware the upper image may be modified. + * @since 3.2 + */ + static void blendOnLower(QImage &upper, QImage &lower, + Disposition disposition, float opacity); +#endif + + /** + * Fade an image to a certain background color. + * + * The number of colors will not be changed. + * + * @param image The QImage to process. + * @param val The strength of the effect. 0 <= val <= 1. + * @param color The background color. + * @return Returns the image(), provided for convenience. + */ + static QImage& fade(QImage &image, float val, const QColor &color); + + /** + * This recolors a pixmap. The most dark color will become color a, + * the most bright one color b, and in between. + * + * @param image A QImage to process. + * @param ca Color a + * @param cb Color b + * @param ncols The number of colors to dither the image to. + * Pass 0 to prevent dithering. + */ + static QImage& flatten(QImage &image, const QColor &ca, + const QColor &cb, int ncols=0); + +#if 0 + /** + * Build a hash on any given QImage + * + * @param image The QImage to process + * @param lite The hash faces the indicated lighting (cardinal poles). + * @param spacing How many unmodified pixels in between hashes. + * @return Returns the image(), provided for convenience. + */ + static QImage& hash(QImage &image, Lighting lite=NorthLite, + unsigned int spacing=0); + +#endif + + /** + * Either brighten or dim the image by a specified percent. + * For example, .50 will modify the colors by 50%. + * + * This function uses MMX instructions to process the image + * on processors that support it. + * + * @param image The QImage to process. + * @param percent The percent value. Use a negative value to dim. + * @return Returns The image(), provided for convenience. + * @author Daniel M. Duley (mosfet) + * @author Benjamin Roe (ben@benroe.com) + */ + static QImage& intensity(QImage &image, float percent); + + /** + * Modifies the intensity of a pixmap's RGB channel component. + * + * @param image The QImage to process. + * @param percent Percent value. Use a negative value to dim. + * @param channel Which channel(s) should be modified + * @return The @p image, provided for convenience. + * @author Daniel M. Duley (mosfet) + */ + static QImage& channelIntensity(QImage &image, float percent, + RGBComponent channel); + +#if 0 + /** + * Modulate the image with a color channel of another image. + * + * @param image The QImage to modulate and result. + * @param modImage The QImage to use for modulation. + * @param reverse Invert the meaning of image/modImage; result is image! + * @param type The modulation Type to use. + * @param factor The modulation amplitude; with 0 no effect [-200;200]. + * @param channel The RBG channel of image2 to use for modulation. + * @return Returns the image(), provided for convenience. + */ + static QImage& modulate(QImage &image, QImage &modImage, bool reverse, + ModulationType type, int factor, RGBComponent channel); +#endif + + /** + * Convert an image to grayscale. + * + * @param image The QImage to process. + * @param fast Set to @p true in order to use a faster but non-photographic + * quality algorithm. Appropriate for things such as toolbar icons. + * @return Returns the image(), provided for convenience. + * @author Daniel M. Duley (mosfet) + */ + static QImage& toGray(QImage &image, bool fast = false); + + /** + * Desaturate an image evenly. + * + * @param image The QImage to process. + * @param desat A value between 0 and 1 setting the degree of desaturation + * @return Returns the image(), provided for convenience. + */ + static QImage& desaturate(QImage &image, float desat = 0.3); + +#if 0 + /** + * Fast, but low quality contrast of an image. Also see contrastHSV. + * + * @param image The QImage to process. + * @param c A contrast value between -255 to 255. + * @return The image(), provided for convenience. + * @author Daniel M. Duley (mosfet) + */ + static QImage& contrast(QImage &image, int c); + + /** + * Dither an image using Floyd-Steinberg dithering for low-color + * situations. + * + * @param image The QImage to process. + * @param palette The color palette to use + * @param size The size of the palette + * @return Returns the image(), provided for convenience. + */ + static QImage& dither(QImage &image, const QColor *palette, int size); + + /** + * Calculate the image for a selected image, for instance a selected icon + * on the desktop. + * @param img the QImage to select + * @param col the selected color, usually from QColorGroup::highlight(). + */ + static QImage& selectedImage( QImage &img, const QColor &col ); + + /** + * High quality, expensive HSV contrast. You can do a faster one by just + * taking a intensity threshold (ie: 128) and incrementing RGB color + * channels above it and decrementing those below it, but this gives much + * better results. + * + * @param img The QImage to process. + * @param sharpen If true sharpness is increase, (spiffed). Otherwise + * it is decreased, (dulled). + * @author Daniel M. Duley (mosfet) + */ + static void contrastHSV(QImage &img, bool sharpen=true); + #endif + + /** + * Normalises the pixel values to span the full range of color values. + * This is a contrast enhancement technique. + * @param img the image that is normalised + * @author Daniel M. Duley (mosfet) + */ + static void normalize(QImage &img); + + /** + * Performs histogram equalisation on the reference + * image. + * @param img the image that is equalised + * @author Daniel M. Duley (mosfet) + */ + static void equalize(QImage &img); + + /** + * Thresholds the reference image. You can also threshold images by using + * ThresholdDither in the various QPixmap/QImage convert methods, but this + * lets you specify a threshold value. + * + * @param img The QImage to process. + * @param value The threshold value. + * @author Daniel M. Duley (mosfet) + */ + static void threshold(QImage &img, unsigned int value=128); + + /** + * Produces a 'solarization' effect seen when exposing a photographic + * film to light during the development process. + * + * @param img The QImage to process. + * @param factor The extent of the solarization (0-99.9) + * @author Daniel M. Duley (mosfet) + */ + static void solarize(QImage &img, double factor=50.0); + + /** + * Embosses the source image. This involves highlighting the edges + * and applying various other enhancements in order to get a metal + * effect. + * + * @param src The QImage to process. + * @param radius The radius of the gaussian not counting the + * center pixel. Use 0 and a suitable radius will be automatically used. + * @param sigma The standard deviation of the gaussian. Use 1 if you're not + * sure. + * @return The embossed image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage emboss(QImage &src, double radius, double sigma); + + #if 0 + /** + * Convenience method. + */ + static QImage emboss(QImage &src); + #endif + + /** + * Minimizes speckle noise in the source image using the 8 hull + * algorithm. + * + * @param src The QImage to process. + * @return The despeckled image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage despeckle(QImage &src); + + /** + * Produces a neat little "charcoal" effect. + * + * @param src The QImage to process. + * @param radius The radius of the gaussian not counting the + * center pixel. Use 0 and a suitable radius will be automatically used. + * @param sigma The standard deviation of the gaussian. Use 1 if you're not + * sure. + * @return The charcoal image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage charcoal(QImage &src, double radius, double sigma); + + #if 0 + /** + * This is provided for binary compatability only! Use the above method + * with a radius and sigma instead! + */ + static QImage charcoal(QImage &src, double factor=50.0); + + /** + * Rotates the image by the specified amount + * + * @param src The QImage to process. + * @param r The rotate direction. + * @return The rotated image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage rotate(QImage &src, RotateDirection r); + + /** + * Scales an image using simple pixel sampling. This does not produce + * nearly as nice a result as QImage::smoothScale(), but has the + * advantage of being much faster - only a few milliseconds. + * + * @param src The QImage to process. + * @param w The new width. + * @param h The new height. + * @return The scaled image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage sample(QImage &src, int w, int h); + #endif + + /** + * Adds noise to an image. + * + * @param src The QImage to process. + * @param type The algorithm used to generate the noise. + * @return The image with noise added. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage addNoise(QImage &src, NoiseType type = GaussianNoise); + + /** + * Blurs an image by convolving pixel neighborhoods. + * + * @param src The QImage to process. + * @param radius The radius of the gaussian not counting the + * center pixel. Use 0 and a suitable radius will be automatically used. + * @param sigma The standard deviation of the gaussian. Use 1 if you're not + * sure. + * @return The blurred image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage blur(QImage &src, double radius, double sigma); + + #if 0 + /** + * This is provided for binary compatability only! Use the above method + * with a radius and sigma instead! + */ + static QImage blur(QImage &src, double factor=50.0); + #endif + + /** + * Detects edges in an image using pixel neighborhoods and an edge + * detection mask. + * + * @param src The QImage to process. + * @param radius The radius of the gaussian not counting the + * center pixel. Use 0 and a suitable radius will be automatically used. + * @return The image with edges detected. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage edge(QImage &src, double radius); + + /** + * Implodes an image by a specified percent. + * + * @param src The QImage to process. + * @param factor The extent of the implosion. + * @param background An RGBA value to use for the background. After the + * effect some pixels may be "empty". This value is used for those pixels. + * @return The imploded image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage implode(QImage &src, double factor=30.0, + unsigned int background = 0xFFFFFFFF); + + /** + * Produces an oil painting effect. + * + * @param src The QImage to process. + * @param radius The radius of the gaussian not counting the + * center pixel. Use 0 and a suitable radius will be automatically used. + * @return The new image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage oilPaintConvolve(QImage &src, double radius); + + #if 0 + /** + * This is provided for binary compatability only! Use the above method + * instead! + */ + static QImage oilPaint(QImage &src, int radius=3); + #endif + + /** + * Sharpens the pixels in the image using pixel neighborhoods. + * + * @param src The QImage to process. + * @param radius The radius of the gaussian not counting the + * center pixel. Use 0 and a suitable radius will be automatically used. + * @param sigma The standard deviation of the gaussian. Use 1 if you're not + * sure. + * @return The sharpened image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage sharpen(QImage &src, double radius, double sigma); + + #if 0 + /** + * This is provided for binary compatability only! Use the above method + * instead! + */ + static QImage sharpen(QImage &src, double factor=30.0); + #endif + + /** + * Randomly displaces pixels. + * + * @param src The QImage to process. + * @param amount The vicinity for choosing a random pixel to swap. + * @return The image with pixels displaced. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage spread(QImage &src, unsigned int amount=3); + + /** + * Shades the image using a distance light source. + * + * @param src The QImage to process. + * @param color_shading If true do color shading, otherwise do grayscale. + * @param azimuth Determines the light source and direction. + * @param elevation Determines the light source and direction. + * @return The shaded image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage shade(QImage &src, bool color_shading, double azimuth, + double elevation); + /** + * Swirls the image by a specified amount + * + * @param src The QImage to process. + * @param degrees The tightness of the swirl. + * @param background An RGBA value to use for the background. After the + * effect some pixels may be "empty". This value is used for those pixels. + * @return The swirled image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage swirl(QImage &src, double angle, unsigned int background = + 0xFFFFFFFF); + + /** + * Modifies the pixels along a sine wave. + * + * @param src The QImage to process. + * @param amplitude The amplitude of the sine wave. + * @param frequency The frequency of the sine wave. + * @param background An RGBA value to use for the background. After the + * effect some pixels may be "empty". This value is used for those pixels. + * @return The new image. The original is not changed. + * @author Daniel M. Duley (mosfet) + */ + static QImage wave(QImage &src, double amplitude=25.0, double frequency=150.0, + unsigned int background = 0xFFFFFFFF); +private: + +#if 0 + /** + * Helper function to fast calc some altered (lighten, shaded) colors + * + */ + static unsigned int lHash(unsigned int c); + static unsigned int uHash(unsigned int c); + + /** + * Helper function to find the nearest color to the RBG triplet + */ + static int nearestColor( int r, int g, int b, const QColor *pal, int size ); +#endif + static void hull(const int x_offset, const int y_offset, const int polarity, + const int width, const int height, + unsigned int *f, unsigned int *g); + static unsigned int generateNoise(unsigned int pixel, NoiseType type); + static unsigned int interpolateColor(QImage *image, double x, double y, + unsigned int background); + /* Various convolve routines */ + static int getOptimalKernelWidth(double radius, double sigma); + static bool convolveImage(QImage *image, QImage *dest, + const unsigned int order, + const double *kernel); + static void blurScanLine(double *kernel, int width, + unsigned int *src, unsigned int *dest, + int columns); + static int getBlurKernel(int width, double sigma, double **kernel); +}; + +#endif diff --git a/main/lib/image.effect/main.cpp b/main/lib/image.effect/main.cpp new file mode 100644 index 00000000..a2b24584 --- /dev/null +++ b/main/lib/image.effect/main.cpp @@ -0,0 +1,51 @@ +/*************************************************************************** + + main.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_CPP + +#include "CImage.h" +#include "main.h" + +extern "C" { + +GB_INTERFACE GB EXPORT; +GB_INTERFACE IMAGE EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + ImageHistogramDesc, + CImageDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + GB.GetInterface("gb.image", IMAGE_INTERFACE_VERSION, &IMAGE); + return 0; +} + +void EXPORT GB_EXIT() +{ +} + +} diff --git a/main/lib/image.effect/main.h b/main/lib/image.effect/main.h new file mode 100644 index 00000000..9ca45fb9 --- /dev/null +++ b/main/lib/image.effect/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "../image/gb.image.h" + +#ifndef __MAIN_CPP +extern "C" GB_INTERFACE GB; +extern "C" IMAGE_INTERFACE IMAGE; +#endif + +#endif /* __MAIN_H */ diff --git a/main/lib/image.effect/qcolor.cpp b/main/lib/image.effect/qcolor.cpp new file mode 100644 index 00000000..b45d5984 --- /dev/null +++ b/main/lib/image.effect/qcolor.cpp @@ -0,0 +1,1021 @@ +/*************************************************************************** + + qcolor.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/**************************************************************************** +** $Id: qt/qcolor.cpp 3.3.4 edited Dec 22 17:04 $ +** +** Implementation of QColor class +** +** Created : 940112 +** +** Copyright (C) 1992-2002 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include "qcolor.h" +//#include "qnamespace.h" +//#include "qdatastream.h" + +#include + + +/*! + \class QColor qcolor.h + \brief The QColor class provides colors based on RGB or HSV values. + + \ingroup images + \ingroup graphics + \ingroup appearance + + A color is normally specified in terms of RGB (red, green and blue) + components, but it is also possible to specify HSV (hue, saturation + and value) or set a color name (the names are copied from from the + X11 color database). + + In addition to the RGB value, a QColor also has a pixel value and a + validity. The pixel value is used by the underlying window system + to refer to a color. It can be thought of as an index into the + display hardware's color table. + + The validity (isValid()) indicates whether the color is legal at + all. For example, a RGB color with RGB values out of range is + illegal. For performance reasons, QColor mostly disregards illegal + colors. The result of using an invalid color is unspecified and + will usually be surprising. + + There are 19 predefined QColor objects: \c white, \c black, \c + red, \c darkRed, \c green, \c darkGreen, \c blue, \c darkBlue, \c + cyan, \c darkCyan, \c magenta, \c darkMagenta, \c yellow, \c + darkYellow, \c gray, \c darkGray, \c lightGray, \c color0 and \c + color1, accessible as members of the Qt namespace (ie. \c Qt::red). + + \img qt-colors.png Qt Colors + + The colors \c color0 (zero pixel value) and \c color1 (non-zero + pixel value) are special colors for drawing in \link QBitmap + bitmaps\endlink. Painting with \c color0 sets the bitmap bits to 0 + (transparent, i.e. background), and painting with \c color1 sets the + bits to 1 (opaque, i.e. foreground). + + The QColor class has an efficient, dynamic color allocation + strategy. A color is normally allocated the first time it is used + (lazy allocation), that is, whenever the pixel() function is called. + The following steps are taken to allocate a color. If, at any point, + a suitable color is found then the appropriate pixel value is + returned and the subsequent steps are not taken: + + \list 1 + \i Is the pixel value valid? If it is, just return it; otherwise, + allocate a pixel value. + \i Check an internal hash table to see if we allocated an equal RGB + value earlier. If we did, set the corresponding pixel value for the + color and return it. + \i Try to allocate the RGB value. If we succeed, we get a pixel value + that we save in the internal table with the RGB value. + Return the pixel value. + \i The color could not be allocated. Find the closest matching + color, save it in the internal table, and return it. + \endlist + + A color can be set by passing setNamedColor() an RGB string like + "#112233", or a color name, e.g. "blue". The names are taken from + X11's rgb.txt database but can also be used under Windows. To get + a lighter or darker color use light() and dark() respectively. + Colors can also be set using setRgb() and setHsv(). The color + components can be accessed in one go with rgb() and hsv(), or + individually with red(), green() and blue(). + + Use maxColors() and numBitPlanes() to determine the maximum number + of colors and the number of bit planes supported by the underlying + window system, + + If you need to allocate many colors temporarily, for example in an + image viewer application, enterAllocContext(), leaveAllocContext() and + destroyAllocContext() will prove useful. + + \section1 HSV Colors + + Because many people don't know the HSV color model very well, we'll + cover it briefly here. + + The RGB model is hardware-oriented. Its representation is close to + what most monitors show. In contrast, HSV represents color in a way + more suited to the human perception of color. For example, the + relationships "stronger than", "darker than" and "the opposite of" + are easily expressed in HSV but are much harder to express in RGB. + + HSV, like RGB, has three components: + + \list + + \i H, for hue, is either 0-359 if the color is chromatic (not + gray), or meaningless if it is gray. It represents degrees on the + color wheel familiar to most people. Red is 0 (degrees), green is + 120 and blue is 240. + + \i S, for saturation, is 0-255, and the bigger it is, the + stronger the color is. Grayish colors have saturation near 0; very + strong colors have saturation near 255. + + \i V, for value, is 0-255 and represents lightness or brightness + of the color. 0 is black; 255 is as far from black as possible. + + \endlist + + Here are some examples: Pure red is H=0, S=255, V=255. A dark red, + moving slightly towards the magenta, could be H=350 (equivalent to + -10), S=255, V=180. A grayish light red could have H about 0 (say + 350-359 or 0-10), S about 50-100, and S=255. + + Qt returns a hue value of -1 for achromatic colors. If you pass a + too-big hue value, Qt forces it into range. Hue 360 or 720 is + treated as 0; hue 540 is treated as 180. + + \sa QPalette, QColorGroup, QApplication::setColorSpec(), + \link http://www.inforamp.net/~poynton/Poynton-color.html Color FAQ\endlink +*/ + +/***************************************************************************** + Global colors + *****************************************************************************/ + +#if defined(Q_WS_WIN) +#define COLOR0_PIX 0x00ffffff +#define COLOR1_PIX 0 +#else +#define COLOR0_PIX 0 +#define COLOR1_PIX 1 +#endif + +#if 0 +static QColor stdcol[19]; + +QT_STATIC_CONST_IMPL QColor & Qt::color0 = stdcol[0]; +QT_STATIC_CONST_IMPL QColor & Qt::color1 = stdcol[1]; +QT_STATIC_CONST_IMPL QColor & Qt::black = stdcol[2]; +QT_STATIC_CONST_IMPL QColor & Qt::white = stdcol[3]; +QT_STATIC_CONST_IMPL QColor & Qt::darkGray = stdcol[4]; +QT_STATIC_CONST_IMPL QColor & Qt::gray = stdcol[5]; +QT_STATIC_CONST_IMPL QColor & Qt::lightGray = stdcol[6]; +QT_STATIC_CONST_IMPL QColor & Qt::red = stdcol[7]; +QT_STATIC_CONST_IMPL QColor & Qt::green = stdcol[8]; +QT_STATIC_CONST_IMPL QColor & Qt::blue = stdcol[9]; +QT_STATIC_CONST_IMPL QColor & Qt::cyan = stdcol[10]; +QT_STATIC_CONST_IMPL QColor & Qt::magenta = stdcol[11]; +QT_STATIC_CONST_IMPL QColor & Qt::yellow = stdcol[12]; +QT_STATIC_CONST_IMPL QColor & Qt::darkRed = stdcol[13]; +QT_STATIC_CONST_IMPL QColor & Qt::darkGreen = stdcol[14]; +QT_STATIC_CONST_IMPL QColor & Qt::darkBlue = stdcol[15]; +QT_STATIC_CONST_IMPL QColor & Qt::darkCyan = stdcol[16]; +QT_STATIC_CONST_IMPL QColor & Qt::darkMagenta = stdcol[17]; +QT_STATIC_CONST_IMPL QColor & Qt::darkYellow = stdcol[18]; +#endif + +/***************************************************************************** + QColor member functions + *****************************************************************************/ + +bool QColor::color_init = FALSE; // color system not initialized +bool QColor::globals_init = FALSE; // global color not initialized +QColor::ColorModel QColor::colormodel = d32; + +#if 0 +QColor* QColor::globalColors() +{ + return stdcol; +} +#endif + +/*! + Initializes the global colors. This function is called if a global + color variable is initialized before the constructors for our + global color objects are executed. Without this mechanism, + assigning a color might assign an uninitialized value. + + Example: + \code + QColor myColor = red; // will initialize red etc. + + int main( int argc, char **argc ) + { + } + \endcode +*/ + +#if 0 +void QColor::initGlobalColors() +{ + globals_init = TRUE; + +#if 0 + #ifdef Q_WS_X11 + // HACK: we need a way to recognize color0 and color1 uniquely, so + // that we can use color0 and color1 with fixed pixel values on + // all screens + stdcol[ 0].d.argb = qRgba(255, 255, 255, 1); + stdcol[ 1].d.argb = qRgba( 0, 0, 0, 1); + #else + stdcol[ 0].d.argb = qRgb(255,255,255); + stdcol[ 1].d.argb = 0; + #endif // Q_WS_X11 + stdcol[ 0].setPixel( COLOR0_PIX ); + stdcol[ 1].setPixel( COLOR1_PIX ); + + // From the "The Palette Manager: How and Why" by Ron Gery, March 23, + // 1992, archived on MSDN: + // The Windows system palette is broken up into two sections, + // one with fixed colors and one with colors that can be changed + // by applications. The system palette predefines 20 entries; + // these colors are known as the static or reserved colors and + // consist of the 16 colors found in the Windows version 3.0 VGA + // driver and 4 additional colors chosen for their visual appeal. + // The DEFAULT_PALETTE stock object is, as the name implies, the + // default palette selected into a device context (DC) and consists + // of these static colors. Applications can set the remaining 236 + // colors using the Palette Manager. + // The 20 reserved entries have indices in [0,9] and [246,255]. We + // reuse 17 of them. + stdcol[ 2].setRgb( 0, 0, 0 ); // index 0 black + stdcol[ 3].setRgb( 255, 255, 255 ); // index 255 white + stdcol[ 4].setRgb( 128, 128, 128 ); // index 248 medium gray + stdcol[ 5].setRgb( 160, 160, 164 ); // index 247 light gray + stdcol[ 6].setRgb( 192, 192, 192 ); // index 7 light gray + stdcol[ 7].setRgb( 255, 0, 0 ); // index 249 red + stdcol[ 8].setRgb( 0, 255, 0 ); // index 250 green + stdcol[ 9].setRgb( 0, 0, 255 ); // index 252 blue + stdcol[10].setRgb( 0, 255, 255 ); // index 254 cyan + stdcol[11].setRgb( 255, 0, 255 ); // index 253 magenta + stdcol[12].setRgb( 255, 255, 0 ); // index 251 yellow + stdcol[13].setRgb( 128, 0, 0 ); // index 1 dark red + stdcol[14].setRgb( 0, 128, 0 ); // index 2 dark green + stdcol[15].setRgb( 0, 0, 128 ); // index 4 dark blue + stdcol[16].setRgb( 0, 128, 128 ); // index 6 dark cyan + stdcol[17].setRgb( 128, 0, 128 ); // index 5 dark magenta + stdcol[18].setRgb( 128, 128, 0 ); // index 3 dark yellow +#endif +} +#endif + +/*! + \enum QColor::Spec + + The type of color specified, either RGB or HSV, e.g. in the + \c{QColor::QColor( x, y, z, colorSpec)} constructor. + + \value Rgb + \value Hsv +*/ + + +/*! + \fn QColor::QColor() + + Constructs an invalid color with the RGB value (0, 0, 0). An + invalid color is a color that is not properly set up for the + underlying window system. + + The alpha value of an invalid color is unspecified. + + \sa isValid() +*/ + + +/*! + \fn QColor::QColor( int r, int g, int b ) + + Constructs a color with the RGB value \a r, \a g, \a b, in the + same way as setRgb(). + + The color is left invalid if any or the arguments are illegal. + + \sa setRgb() +*/ + + +/*! + Constructs a color with the RGB value \a rgb and a custom pixel + value \a pixel. + + If \a pixel == 0xffffffff (the default), then the color uses the + RGB value in a standard way. If \a pixel is something else, then + the pixel value is set directly to \a pixel, skipping the normal + allocation procedure. +*/ + +QColor::QColor( QRgb rgb, uint pixel ) +{ + if ( pixel == 0xffffffff ) { + setRgb( rgb ); + } else { + d.argb = rgb; + setPixel( pixel ); + } +} + +void QColor::setPixel( uint pixel ) +{ + switch ( colormodel ) { + case d8: + d.d8.direct = TRUE; + d.d8.invalid = FALSE; + d.d8.dirty = FALSE; + d.d8.pix = pixel; + break; + case d32: + d.d32.pix = pixel; + break; + } +} + + +/*! + Constructs a color with the RGB or HSV value \a x, \a y, \a z. + + The arguments are an RGB value if \a colorSpec is QColor::Rgb. \a + x (red), \a y (green), and \a z (blue). All of them must be in the + range 0-255. + + The arguments are an HSV value if \a colorSpec is QColor::Hsv. \a + x (hue) must be -1 for achromatic colors and 0-359 for chromatic + colors; \a y (saturation) and \a z (value) must both be in the + range 0-255. + + \sa setRgb(), setHsv() +*/ + +QColor::QColor( int x, int y, int z, Spec colorSpec ) +{ + d.d32.argb = Invalid; + d.d32.pix = Dirt; + if ( colorSpec == Hsv ) + setHsv( x, y, z ); + else + setRgb( x, y, z ); +} + + +/*! + Constructs a named color in the same way as setNamedColor() using + name \a name. + + The color is left invalid if \a name cannot be parsed. + + \sa setNamedColor() +*/ + +#if 0 +QColor::QColor( const QString& name ) +{ + setNamedColor( name ); +} +#endif + +/*! + Constructs a named color in the same way as setNamedColor() using + name \a name. + + The color is left invalid if \a name cannot be parsed. + + \sa setNamedColor() +*/ + +#if 0 +QColor::QColor( const char *name ) +{ + setNamedColor( QString(name) ); +} +#endif + + +/*! + Constructs a color that is a copy of \a c. +*/ + +QColor::QColor( const QColor &c ) +{ + //if ( !globals_init ) + //initGlobalColors(); + d.argb = c.d.argb; + d.d32.pix = c.d.d32.pix; +} + + +/*! + Assigns a copy of the color \a c and returns a reference to this + color. +*/ + +QColor &QColor::operator=( const QColor &c ) +{ + //if ( !globals_init ) + //initGlobalColors(); + d.argb = c.d.argb; + d.d32.pix = c.d.d32.pix; + return *this; +} + + +/*! + \fn bool QColor::isValid() const + + Returns FALSE if the color is invalid, i.e. it was constructed using the + default constructor; otherwise returns TRUE. +*/ + +/*! + \internal +*/ +bool QColor::isDirty() const +{ + if ( colormodel == d8 ) { + return d.d8.dirty; + } else { + return d.d32.probablyDirty(); + } +} + +/*! + Returns the name of the color in the format "#RRGGBB", i.e. a "#" + character followed by three two-digit hexadecimal numbers. + + \sa setNamedColor() +*/ + +#if 0 +QString QColor::name() const +{ +#ifndef QT_NO_SPRINTF + QString s; + s.sprintf( "#%02x%02x%02x", red(), green(), blue() ); + return s; +#else + char s[20]; + sprintf( s, "#%02x%02x%02x", red(), green(), blue() ); + return QString(s); +#endif +} + +static int hex2int( QChar hexchar ) +{ + int v; + if ( hexchar.isDigit() ) + v = hexchar.digitValue(); + else if ( hexchar >= 'A' && hexchar <= 'F' ) + v = hexchar.cell() - 'A' + 10; + else if ( hexchar >= 'a' && hexchar <= 'f' ) + v = hexchar.cell() - 'a' + 10; + else + v = -1; + return v; +} +#endif + + +/*! + Sets the RGB value to \a name, which may be in one of these + formats: + \list + \i #RGB (each of R, G and B is a single hex digit) + \i #RRGGBB + \i #RRRGGGBBB + \i #RRRRGGGGBBBB + \i A name from the X color database (rgb.txt) (e.g. + "steelblue" or "gainsboro"). These color names also work + under Windows. + \endlist + + The color is invalid if \a name cannot be parsed. +*/ + +#if 0 +void QColor::setNamedColor( const QString &name ) +{ + if ( name.isEmpty() ) { + d.argb = 0; + if ( colormodel == d8 ) { + d.d8.invalid = TRUE; + } else { + d.d32.argb = Invalid; + } + } else if ( name[0] == '#' ) { + const QChar *p = name.unicode()+1; + int len = name.length()-1; + int r, g, b; + if ( len == 12 ) { + r = (hex2int(p[0]) << 4) + hex2int(p[1]); + g = (hex2int(p[4]) << 4) + hex2int(p[5]); + b = (hex2int(p[8]) << 4) + hex2int(p[9]); + } else if ( len == 9 ) { + r = (hex2int(p[0]) << 4) + hex2int(p[1]); + g = (hex2int(p[3]) << 4) + hex2int(p[4]); + b = (hex2int(p[6]) << 4) + hex2int(p[7]); + } else if ( len == 6 ) { + r = (hex2int(p[0]) << 4) + hex2int(p[1]); + g = (hex2int(p[2]) << 4) + hex2int(p[3]); + b = (hex2int(p[4]) << 4) + hex2int(p[5]); + } else if ( len == 3 ) { + r = (hex2int(p[0]) << 4) + hex2int(p[0]); + g = (hex2int(p[1]) << 4) + hex2int(p[1]); + b = (hex2int(p[2]) << 4) + hex2int(p[2]); + } else { + r = g = b = -1; + } + if ( (uint)r > 255 || (uint)g > 255 || (uint)b > 255 ) { + d.d32.argb = Invalid; + d.d32.pix = Dirt; +#if defined(QT_CHECK_RANGE) + qWarning( "QColor::setNamedColor: could not parse color '%s'", + name.local8Bit().data() ); +#endif + } else { + setRgb( r, g, b ); + } + } else { + setSystemNamedColor( name ); + } +} +#endif + +#undef max +#undef min + +/*! + \fn void QColor::getHsv( int &h, int &s, int &v ) const + \obsolete +*/ + +/*! \fn void QColor::getHsv( int *h, int *s, int *v ) const + + Returns the current RGB value as HSV. The contents of the \a h, \a + s and \a v pointers are set to the HSV values. If any of the three + pointers are null, the function does nothing. + + The hue (which \a h points to) is set to -1 if the color is + achromatic. + + \warning Colors are stored internally as RGB values, so getHSv() + may return slightly different values to those set by setHsv(). + + \sa setHsv(), rgb() +*/ + +/*! \obsolete Use getHsv() instead. + */ +void QColor::hsv( int *h, int *s, int *v) const +{ + if ( !h || !s || !v ) + return; + int r = qRed(d.argb); + int g = qGreen(d.argb); + int b = qBlue(d.argb); + uint max = r; // maximum RGB component + int whatmax = 0; // r=>0, g=>1, b=>2 + if ( (uint)g > max ) { + max = g; + whatmax = 1; + } + if ( (uint)b > max ) { + max = b; + whatmax = 2; + } + uint min = r; // find minimum value + if ( (uint)g < min ) min = g; + if ( (uint)b < min ) min = b; + int delta = max-min; + *v = max; // calc value + *s = max ? (510*delta+max)/(2*max) : 0; + if ( *s == 0 ) { + *h = -1; // undefined hue + } else { + switch ( whatmax ) { + case 0: // red is max component + if ( g >= b ) + *h = (120*(g-b)+delta)/(2*delta); + else + *h = (120*(g-b+delta)+delta)/(2*delta) + 300; + break; + case 1: // green is max component + if ( b > r ) + *h = 120 + (120*(b-r)+delta)/(2*delta); + else + *h = 60 + (120*(b-r+delta)+delta)/(2*delta); + break; + case 2: // blue is max component + if ( r > g ) + *h = 240 + (120*(r-g)+delta)/(2*delta); + else + *h = 180 + (120*(r-g+delta)+delta)/(2*delta); + break; + } + } +} + + +/*! + Sets a HSV color value. \a h is the hue, \a s is the saturation + and \a v is the value of the HSV color. + + If \a s or \a v are not in the range 0-255, or \a h is < -1, the + color is not changed. + + \warning Colors are stored internally as RGB values, so getHSv() + may return slightly different values to those set by setHsv(). + + \sa hsv(), setRgb() +*/ + +void QColor::setHsv( int h, int s, int v ) +{ + if ( h < -1 || (uint)s > 255 || (uint)v > 255 ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QColor::setHsv: HSV parameters out of range" ); +#endif + return; + } + int r=v, g=v, b=v; + if ( s == 0 || h == -1 ) { // achromatic case + // Ignore + } else { // chromatic case + if ( (uint)h >= 360 ) + h %= 360; + uint f = h%60; + h /= 60; + uint p = (uint)(2*v*(255-s)+255)/510; + uint q, t; + if ( h&1 ) { + q = (uint)(2*v*(15300-s*f)+15300)/30600; + switch( h ) { + case 1: r=(int)q; g=(int)v, b=(int)p; break; + case 3: r=(int)p; g=(int)q, b=(int)v; break; + case 5: r=(int)v; g=(int)p, b=(int)q; break; + } + } else { + t = (uint)(2*v*(15300-(s*(60-f)))+15300)/30600; + switch( h ) { + case 0: r=(int)v; g=(int)t, b=(int)p; break; + case 2: r=(int)p; g=(int)v, b=(int)t; break; + case 4: r=(int)t; g=(int)p, b=(int)v; break; + } + } + } + setRgb( r, g, b ); +} + + +/*! + \fn QRgb QColor::rgb() const + + Returns the RGB value. + + The return type \e QRgb is equivalent to \c unsigned \c int. + + For an invalid color, the alpha value of the returned color is + unspecified. + + \sa setRgb(), hsv(), qRed(), qBlue(), qGreen(), isValid() +*/ + +/*! \fn void QColor::getRgb( int *r, int *g, int *b ) const + + Sets the contents pointed to by \a r, \a g and \a b to the red, + green and blue components of the RGB value respectively. The value + range for a component is 0..255. + + \sa rgb(), setRgb(), getHsv() +*/ + +/*! \obsolete Use getRgb() instead */ +void QColor::rgb( int *r, int *g, int *b) const +{ + *r = qRed(d.argb); + *g = qGreen(d.argb); + *b = qBlue(d.argb); +} + + +/*! + Sets the RGB value to \a r, \a g, \a b. The arguments, \a r, \a g + and \a b must all be in the range 0..255. If any of them are + outside the legal range, the color is not changed. + + \sa rgb(), setHsv() +*/ + +void QColor::setRgb( int r, int g, int b) +{ +/* if ( (uint)r > 255 || (uint)g > 255 || (uint)b > 255 ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QColor::setRgb: RGB parameter(s) out of range" ); +#endif + return; + }*/ + d.argb = qRgba( r, g, b, alpha() ); + if ( colormodel == d8 ) { + d.d8.invalid = FALSE; + d.d8.direct = FALSE; + d.d8.dirty = TRUE; + } else { + d.d32.pix = Dirt; + } +} + + +/*! + \overload + Sets the RGB value to \a rgb. + + The type \e QRgb is equivalent to \c unsigned \c int. + + \sa rgb(), setHsv() +*/ + +void QColor::setRgb( QRgb rgb ) +{ + d.argb = rgb; + if ( colormodel == d8 ) { + d.d8.invalid = FALSE; + d.d8.direct = FALSE; + d.d8.dirty = TRUE; + } else { + d.d32.pix = Dirt; + } +} + +/*! + \fn int QColor::red() const + + Returns the R (red) component of the RGB value. +*/ + + +/*! + \fn int QColor::green() const + + Returns the G (green) component of the RGB value. +*/ + +/*! + \fn int QColor::blue() const + + Returns the B (blue) component of the RGB value. +*/ + + +/*! + Returns a lighter (or darker) color, but does not change this + object. + + Returns a lighter color if \a factor is greater than 100. Setting + \a factor to 150 returns a color that is 50% brighter. + + Returns a darker color if \a factor is less than 100. We recommend + using dark() for this purpose. If \a factor is 0 or negative, the + return value is unspecified. + + (This function converts the current RGB color to HSV, multiplies V + by \a factor, and converts the result back to RGB.) + + \sa dark() +*/ + +QColor QColor::light( int factor ) const +{ + if ( factor <= 0 ) // invalid lightness factor + return *this; + else if ( factor < 100 ) // makes color darker + return dark( 10000/factor ); + + int h, s, v; + hsv( &h, &s, &v ); + v = (factor*v)/100; + if ( v > 255 ) { // overflow + s -= v-255; // adjust saturation + if ( s < 0 ) + s = 0; + v = 255; + } + QColor c; + c.setHsv( h, s, v ); + return c; +} + + +/*! + Returns a darker (or lighter) color, but does not change this + object. + + Returns a darker color if \a factor is greater than 100. Setting + \a factor to 300 returns a color that has one-third the + brightness. + + Returns a lighter color if \a factor is less than 100. We + recommend using lighter() for this purpose. If \a factor is 0 or + negative, the return value is unspecified. + + (This function converts the current RGB color to HSV, divides V by + \a factor and converts back to RGB.) + + \sa light() +*/ + +QColor QColor::dark( int factor ) const +{ + if ( factor <= 0 ) // invalid darkness factor + return *this; + else if ( factor < 100 ) // makes color lighter + return light( 10000/factor ); + int h, s, v; + hsv( &h, &s, &v ); + v = (v*100)/factor; + QColor c; + c.setHsv( h, s, v ); + return c; +} + + +/*! + \fn bool QColor::operator==( const QColor &c ) const + + Returns TRUE if this color has the same RGB value as \a c; + otherwise returns FALSE. +*/ + +/*! + \fn bool QColor::operator!=( const QColor &c ) const + Returns TRUE if this color has a different RGB value from \a c; + otherwise returns FALSE. +*/ + +/*! + Returns the pixel value. + + This value is used by the underlying window system to refer to a + color. It can be thought of as an index into the display + hardware's color table, but the value is an arbitrary 32-bit + value. + + \sa alloc() +*/ + +/*! + \fn QStringList QColor::colorNames() + Returns a QStringList containing the color names Qt knows about. +*/ + +/***************************************************************************** + QColor stream functions + *****************************************************************************/ +#ifndef QT_NO_DATASTREAM +/*! + \relates QColor + Writes a color object, \a c to the stream, \a s. + + \sa \link datastreamformat.html Format of the QDataStream operators \endlink +*/ + +QDataStream &operator<<( QDataStream &s, const QColor &c ) +{ + Q_UINT32 p = (Q_UINT32)c.rgb(); + if ( s.version() == 1 ) // Swap red and blue + p = ((p << 16) & 0xff0000) | ((p >> 16) & 0xff) | (p & 0xff00ff00); + return s << p; +} + +/*! + \relates QColor + Reads a color object, \a c, from the stream, \a s. + + \sa \link datastreamformat.html Format of the QDataStream operators \endlink +*/ + +QDataStream &operator>>( QDataStream &s, QColor &c ) +{ + Q_UINT32 p; + s >> p; + if ( s.version() == 1 ) // Swap red and blue + p = ((p << 16) & 0xff0000) | ((p >> 16) & 0xff) | (p & 0xff00ff00); + c.setRgb( p ); + return s; +} +#endif + +/***************************************************************************** + QColor global functions (documentation only) + *****************************************************************************/ + +/*! + \fn int qRed( QRgb rgb ) + \relates QColor + + Returns the red component of the RGB triplet \a rgb. + \sa qRgb(), QColor::red() +*/ + +/*! + \fn int qGreen( QRgb rgb ) + \relates QColor + + Returns the green component of the RGB triplet \a rgb. + \sa qRgb(), QColor::green() +*/ + +/*! + \fn int qBlue( QRgb rgb ) + \relates QColor + + Returns the blue component of the RGB triplet \a rgb. + \sa qRgb(), QColor::blue() +*/ + +/*! + \fn int qAlpha( QRgb rgba ) + \relates QColor + + Returns the alpha component of the RGBA quadruplet \a rgba. + */ + +/*! + \fn QRgb qRgb( int r, int g, int b ) + \relates QColor + + Returns the RGB triplet \a (r,g,b). + + The return type QRgb is equivalent to \c unsigned \c int. + + \sa qRgba(), qRed(), qGreen(), qBlue() +*/ + +/*! + \fn QRgb qRgba( int r, int g, int b, int a ) + \relates QColor + + Returns the RGBA quadruplet \a (r,g,b,a). + + The return type QRgba is equivalent to \c unsigned \c int. + + \sa qRgb(), qRed(), qGreen(), qBlue() +*/ + +/*! + \fn int qGray( int r, int g, int b ) + \relates QColor + + Returns a gray value 0..255 from the (\a r, \a g, \a b) triplet. + + The gray value is calculated using the formula (r*11 + g*16 + + b*5)/32. +*/ + +/*! + \overload int qGray( qRgb rgb ) + \relates QColor + + Returns a gray value 0..255 from the given \a rgb colour. +*/ + diff --git a/main/lib/image.effect/qcolor.h b/main/lib/image.effect/qcolor.h new file mode 100644 index 00000000..c118315f --- /dev/null +++ b/main/lib/image.effect/qcolor.h @@ -0,0 +1,255 @@ +/*************************************************************************** + + qcolor.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/**************************************************************************** +** $Id: qt/qcolor.h 3.3.4 edited Nov 24 2003 $ +** +** Definition of QColor class +** +** Created : 940112 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QCOLOR_H +#define QCOLOR_H + +#include "qt.h" + +//#ifndef QT_H +//#include "qwindowdefs.h" +//#include "qstringlist.h" +//#endif // QT_H + +const QRgb RGB_MASK = 0x00ffffff; // masks RGB values + +Q_EXPORT inline int qRed( QRgb rgb ) // get red part of RGB +{ return (int)((rgb >> 16) & 0xff); } + +Q_EXPORT inline int qGreen( QRgb rgb ) // get green part of RGB +{ return (int)((rgb >> 8) & 0xff); } + +Q_EXPORT inline int qBlue( QRgb rgb ) // get blue part of RGB +{ return (int)(rgb & 0xff); } + +Q_EXPORT inline int qAlpha( QRgb rgb ) // get alpha part of RGBA +{ return (int)((rgb >> 24) & 0xff); } + +Q_EXPORT inline QRgb qRgb( int r, int g, int b )// set RGB value +{ return (0xff << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); } + +Q_EXPORT inline QRgb qRgba( int r, int g, int b, int a )// set RGBA value +{ return ((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); } + +Q_EXPORT inline int qGray( int r, int g, int b )// convert R,G,B to gray 0..255 +{ return (r*11+g*16+b*5)/32; } + +Q_EXPORT inline int qGray( QRgb rgb ) // convert RGB to gray 0..255 +{ return qGray( qRed(rgb), qGreen(rgb), qBlue(rgb) ); } + + +class Q_EXPORT QColor +{ +public: + enum Spec { Rgb, Hsv }; + + QColor(); + QColor( int r, int g, int b ); + QColor( int x, int y, int z, Spec ); + QColor( QRgb rgb, uint pixel=0xffffffff); + //QColor( const QString& name ); + QColor( const char *name ); + QColor( const QColor & ); + QColor &operator=( const QColor & ); + + bool isValid() const; + bool isDirty() const; + //QString name() const; + //void setNamedColor( const QString& name ); + + QRgb rgb() const; + void setRgb( int r, int g, int b ); + void setRgb( QRgb rgb ); + void getRgb( int *r, int *g, int *b) const { rgb( r, g, b); } + void rgb( int *r, int *g, int *b) const; // obsolete + + int red() const; + int green() const; + int blue() const; + int alpha() const; + + void setHsv( int h, int s, int v ); + void getHsv( int *h, int *s, int *v ) const { hsv( h, s, v ); } + void hsv( int *h, int *s, int *v ) const; // obsolete + void getHsv( int &h, int &s, int &v ) const { hsv( &h, &s, &v ); } // obsolete + + QColor light( int f = 150 ) const; + QColor dark( int f = 200 ) const; + + bool operator==( const QColor &c ) const; + bool operator!=( const QColor &c ) const; + + //uint alloc(); + //uint pixel() const; + +#if defined(Q_WS_X11) + // ### in 4.0, make this take a default argument of -1 for default screen? + uint alloc( int screen ); + uint pixel( int screen ) const; +#endif + + //static int maxColors(); + //static int numBitPlanes(); + + //static int enterAllocContext(); + //static void leaveAllocContext(); + //static int currentAllocContext(); + //static void destroyAllocContext( int ); + +#if defined(Q_WS_WIN) + static const QRgb* palette( int* numEntries = 0 ); + static int setPaletteEntries( const QRgb* entries, int numEntries, + int base = -1 ); + static HPALETTE hPal() { return hpal; } + static uint realizePal( QWidget * ); +#endif + + static void initialize(); + static void cleanup(); +#ifndef QT_NO_STRINGLIST + static QStringList colorNames(); +#endif + enum { Dirt = 0x44495254, Invalid = 0x49000000 }; + +private: + //void setSystemNamedColor( const QString& name ); + void setPixel( uint pixel ); + //static void initGlobalColors(); + static uint argbToPix32(QRgb); + //static QColor* globalColors(); + static bool color_init; + static bool globals_init; +#if defined(Q_WS_WIN) + static HPALETTE hpal; +#endif + static enum ColorModel { d8, d32 } colormodel; + union { + QRgb argb; + struct D8 { + QRgb argb; + uchar pix; + uchar invalid; + uchar dirty; + uchar direct; + } d8; + struct D32 { + QRgb argb; + uint pix; + bool invalid() const { return argb == QColor::Invalid && pix == QColor::Dirt; } + bool probablyDirty() const { return pix == QColor::Dirt; } + } d32; + } d; +}; + + +inline QColor::QColor() +{ d.d32.argb = Invalid; d.d32.pix = Dirt; } + +inline QColor::QColor( int r, int g, int b ) +{ + d.d32.argb = Invalid; + d.d32.pix = Dirt; + setRgb( r, g, b ); +} + +inline QRgb QColor::rgb() const +{ return d.argb; } + +inline int QColor::red() const +{ return qRed(d.argb); } + +inline int QColor::green() const +{ return qGreen(d.argb); } + +inline int QColor::blue() const +{ return qBlue(d.argb); } + +inline int QColor::alpha() const +{ return qAlpha(d.argb); } + +inline bool QColor::isValid() const +{ + if ( colormodel == d8 ) + return !d.d8.invalid; + else + return !d.d32.invalid(); +} + +inline bool QColor::operator==( const QColor &c ) const +{ + return d.argb == c.d.argb && isValid() == c.isValid(); +} + +inline bool QColor::operator!=( const QColor &c ) const +{ + return !operator==(c); +} + + +/***************************************************************************** + QColor stream functions + *****************************************************************************/ + +#ifndef QT_NO_DATASTREAM +Q_EXPORT QDataStream &operator<<( QDataStream &, const QColor & ); +Q_EXPORT QDataStream &operator>>( QDataStream &, QColor & ); +#endif + +#endif // QCOLOR_H diff --git a/main/lib/image.effect/qimage.cpp b/main/lib/image.effect/qimage.cpp new file mode 100644 index 00000000..b507fbbc --- /dev/null +++ b/main/lib/image.effect/qimage.cpp @@ -0,0 +1,147 @@ +/*************************************************************************** + + qimage.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "qimage.h" + +void QImage::init() +{ + img = NULL; + bpl = 0; + jt = NULL; + inv = FALSE; +} + +void QImage::check() +{ + if (!img) + return; + if (GB_IMAGE_FMT_IS_SWAPPED(img->format)) + fprintf(stderr, "gb.image.effect: warning: unsupported image format: %s\n", IMAGE.FormatToString(img->format)); + inv = GB_IMAGE_FMT_IS_RGBA(img->format); +} + +void QImage::create(int w, int h, bool t) +{ + img = IMAGE.Create(w, h, t ? GB_IMAGE_BGRA : GB_IMAGE_BGRX, NULL); + //GB.Ref(img); + check(); + jumpTable(); +} + +QImage::QImage() +{ + init(); +} + +QImage::QImage(const QSize &size, bool t) +{ + init(); + create(size.width(), size.height(), t); +} + +QImage::QImage(int w, int h, bool t) +{ + init(); + create(w, h, t); +} + +QImage::QImage(GB_IMAGE image) +{ + init(); + img = (GB_IMG *)image; + //GB.Ref(img); + SYNCHRONIZE_IMAGE(img); + check(); + jumpTable(); +} + +QImage::QImage(const QImage ©) +{ + init(); + img = (GB_IMG *)copy.object(); + //GB.Ref(img); + SYNCHRONIZE_IMAGE(img); + check(); +} + +QImage::~QImage() +{ + if (jt) + { + free(jt); + jt = NULL; + } +} + +void QImage::release() +{ + GB.Unref(POINTER(&img)); + img = NULL; +} + +/*static inline uint invert(uint col) +{ + return (col >> 24) | (col << 24) | ((col & 0x00FF0000) >> 8) | ((col & 0x0000FF00) << 8); +}*/ + +void QImage::invert() +{ + uint i, n; + uchar *p, t; + + n = width() * height(); + p = (uchar *)bits(); + + for (i = 0; i < n; i++, p += 4) + { + t = p[0]; + p[0] = p[2]; + p[2] = t; + } +} + + +uchar **QImage::jumpTable() +{ + if (!jt && img->data) + { + int bpl = 4 * width(); + jt = (uchar**)malloc(height() * sizeof(uchar*)); + for (int i = 0; i < height(); i++) + jt[i] = (uchar *)img->data + i * bpl; + } + + return jt; +} + +void QImage::invertPixels() +{ + uint i, n; + uint *p; + + n = width() * height(); + p = (uint *)bits(); + + for (i = 0; i < n; i++) + p[i] ^= 0xFFFFFF; +} diff --git a/main/lib/image.effect/qimage.h b/main/lib/image.effect/qimage.h new file mode 100644 index 00000000..15699230 --- /dev/null +++ b/main/lib/image.effect/qimage.h @@ -0,0 +1,493 @@ +/*************************************************************************** + + qimage.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/**************************************************************************** +** $Id: qt/qimage.h 3.3.4 edited May 27 2003 $ +** +** Definition of QImage and QImageIO classes +** +** Created : 950207 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QIMAGE_H +#define QIMAGE_H + +#include "qsize.h" +#include "main.h" + +class QImage +{ +public: + + QImage(); + QImage(int w, int h, bool t); + QImage(const QSize &size, bool t); + QImage(GB_IMAGE img); + ~QImage(); + QImage(const QImage ©); + void reset() { }; + void release(); + void create(int w, int h, bool t); + GB_IMAGE object() const { return (GB_IMAGE)img; } + int depth() const { return 32; } + int width() const { return img->width; } + int height() const { return img->height; } + int numColors() const { return 0; } + QRgb *colorTable() const { return NULL; } + QRgb pixel(int x, int y) const { return ((uint *)img->data)[y * width() + x]; } + void setPixel(int x, int y, QRgb c) { ((uint *)img->data)[y * width() + x] = c; } + bool inverted() const { return inv; } + void invert(); + void invertPixels(); + bool transparent() { return GB_IMAGE_FMT_IS_TRANSPARENT(img->format); } + + uchar *scanLine( int ) const; + uchar *bits() const { return (uchar *)img->data; } + uchar **jumpTable(); + + +private: + + void init(); + void check(); + + GB_IMG *img; + int bpl; + bool inv; + uchar **jt; +}; + +inline uchar *QImage::scanLine( int i ) const +{ + return jt[i]; +} + + + +#if 0 + +class QImageDataMisc; // internal +#ifndef QT_NO_IMAGE_TEXT +class Q_EXPORT QImageTextKeyLang { +public: + QImageTextKeyLang(const char* k, const char* l) : key(k), lang(l) { } + QImageTextKeyLang() { } + + QCString key; + QCString lang; + + bool operator< (const QImageTextKeyLang& other) const + { return key < other.key || key==other.key && lang < other.lang; } + bool operator== (const QImageTextKeyLang& other) const + { return key==other.key && lang==other.lang; } +}; +#endif //QT_NO_IMAGE_TEXT + + +class Q_EXPORT QImage +{ +public: + enum Endian { IgnoreEndian, BigEndian, LittleEndian }; + + QImage(); + QImage( int width, int height, int depth, int numColors=0, + Endian bitOrder=IgnoreEndian ); + QImage( const QSize&, int depth, int numColors=0, + Endian bitOrder=IgnoreEndian ); +#ifndef QT_NO_IMAGEIO + QImage( const QString &fileName, const char* format=0 ); + QImage( const char * const xpm[] ); + QImage( const QByteArray &data ); +#endif + QImage( uchar* data, int w, int h, int depth, + QRgb* colortable, int numColors, + Endian bitOrder ); +#ifdef Q_WS_QWS + QImage( uchar* data, int w, int h, int depth, int pbl, + QRgb* colortable, int numColors, + Endian bitOrder ); +#endif + QImage( const QImage & ); + ~QImage(); + + QImage &operator=( const QImage & ); + QImage &operator=( const QPixmap & ); + bool operator==( const QImage & ) const; + bool operator!=( const QImage & ) const; + void detach(); + QImage copy() const; + QImage copy(int x, int y, int w, int h, int conversion_flags=0) const; + QImage copy(const QRect&) const; +#ifndef QT_NO_MIME + static QImage fromMimeSource( const QString& abs_name ); +#endif + bool isNull() const { return data->bits == 0; } + + int width() const { return data->w; } + int height() const { return data->h; } + QSize size() const { return QSize(data->w,data->h); } + QRect rect() const { return QRect(0,0,data->w,data->h); } + int depth() const { return data->d; } + int numColors() const { return data->ncols; } + Endian bitOrder() const { return (Endian) data->bitordr; } + + QRgb color( int i ) const; + void setColor( int i, QRgb c ); + void setNumColors( int ); + + bool hasAlphaBuffer() const; + void setAlphaBuffer( bool ); + + bool allGray() const; + bool isGrayscale() const; + + uchar *bits() const; + uchar *scanLine( int ) const; + uchar **jumpTable() const; + QRgb *colorTable() const; + int numBytes() const; + int bytesPerLine() const; + +#ifdef Q_WS_QWS + QGfx * graphicsContext(); +#endif + + bool create( int width, int height, int depth, int numColors=0, + Endian bitOrder=IgnoreEndian ); + bool create( const QSize&, int depth, int numColors=0, + Endian bitOrder=IgnoreEndian ); + void reset(); + + void fill( uint pixel ); + void invertPixels( bool invertAlpha = TRUE ); + + QImage convertDepth( int ) const; +#ifndef QT_NO_IMAGE_TRUECOLOR + QImage convertDepthWithPalette( int, QRgb* p, int pc, int cf=0 ) const; +#endif + QImage convertDepth( int, int conversion_flags ) const; + QImage convertBitOrder( Endian ) const; + + enum ScaleMode { + ScaleFree, + ScaleMin, + ScaleMax + }; +#ifndef QT_NO_IMAGE_SMOOTHSCALE + QImage smoothScale( int w, int h, ScaleMode mode=ScaleFree ) const; + QImage smoothScale( const QSize& s, ScaleMode mode=ScaleFree ) const; +#endif +#ifndef QT_NO_IMAGE_TRANSFORMATION + QImage scale( int w, int h, ScaleMode mode=ScaleFree ) const; + QImage scale( const QSize& s, ScaleMode mode=ScaleFree ) const; + QImage scaleWidth( int w ) const; + QImage scaleHeight( int h ) const; + QImage xForm( const QWMatrix &matrix ) const; +#endif + +#ifndef QT_NO_IMAGE_DITHER_TO_1 + QImage createAlphaMask( int conversion_flags=0 ) const; +#endif +#ifndef QT_NO_IMAGE_HEURISTIC_MASK + QImage createHeuristicMask( bool clipTight=TRUE ) const; +#endif +#ifndef QT_NO_IMAGE_MIRROR + QImage mirror() const; + QImage mirror(bool horizontally, bool vertically) const; +#endif + QImage swapRGB() const; + + static Endian systemBitOrder(); + static Endian systemByteOrder(); + +#ifndef QT_NO_IMAGEIO + static const char* imageFormat( const QString &fileName ); + static QStrList inputFormats(); + static QStrList outputFormats(); +#ifndef QT_NO_STRINGLIST + static QStringList inputFormatList(); + static QStringList outputFormatList(); +#endif + bool load( const QString &fileName, const char* format=0 ); + bool loadFromData( const uchar *buf, uint len, + const char *format=0 ); + bool loadFromData( QByteArray data, const char* format=0 ); + bool save( const QString &fileName, const char* format, + int quality=-1 ) const; + bool save( QIODevice * device, const char* format, + int quality=-1 ) const; +#endif //QT_NO_IMAGEIO + + bool valid( int x, int y ) const; + int pixelIndex( int x, int y ) const; + QRgb pixel( int x, int y ) const; + void setPixel( int x, int y, uint index_or_rgb ); + + // Auxiliary data + int dotsPerMeterX() const; + int dotsPerMeterY() const; + void setDotsPerMeterX(int); + void setDotsPerMeterY(int); + QPoint offset() const; + void setOffset(const QPoint&); +#ifndef QT_NO_IMAGE_TEXT + QValueList textList() const; + QStringList textLanguages() const; + QStringList textKeys() const; + QString text(const char* key, const char* lang=0) const; + QString text(const QImageTextKeyLang&) const; + void setText(const char* key, const char* lang, const QString&); +#endif +private: + void init(); + void reinit(); + void freeBits(); + static void warningIndexRange( const char *, int ); + + struct QImageData : public QShared { // internal image data + int w; // image width + int h; // image height + int d; // image depth + int ncols; // number of colors + int nbytes; // number of bytes data + int bitordr; // bit order (1 bit depth) + QRgb *ctbl; // color table + uchar **bits; // image data + bool alpha; // alpha buffer + int dpmx; // dots per meter X (or 0) + int dpmy; // dots per meter Y (or 0) + QPoint offset; // offset in pixels +#ifndef QT_NO_IMAGE_TEXT + QImageDataMisc* misc; // less common stuff +#endif + bool ctbl_mine; // this allocated ctbl + } *data; +#ifndef QT_NO_IMAGE_TEXT + QImageDataMisc& misc() const; +#endif +#ifndef QT_NO_IMAGEIO + bool doImageIO( QImageIO* io, int quality ) const; +#endif + friend Q_EXPORT void bitBlt( QImage* dst, int dx, int dy, + const QImage* src, int sx, int sy, + int sw, int sh, int conversion_flags ); +}; + + +// QImage stream functions + +#if !defined(QT_NO_DATASTREAM) && !defined(QT_NO_IMAGEIO) +Q_EXPORT QDataStream &operator<<( QDataStream &, const QImage & ); +Q_EXPORT QDataStream &operator>>( QDataStream &, QImage & ); +#endif + +#ifndef QT_NO_IMAGEIO +class QIODevice; +typedef void (*image_io_handler)( QImageIO * ); // image IO handler + + +struct QImageIOData; + + +class Q_EXPORT QImageIO +{ +public: + QImageIO(); + QImageIO( QIODevice *ioDevice, const char *format ); + QImageIO( const QString &fileName, const char* format ); + ~QImageIO(); + + + const QImage &image() const { return im; } + int status() const { return iostat; } + const char *format() const { return frmt; } + QIODevice *ioDevice() const { return iodev; } + QString fileName() const { return fname; } + int quality() const; + QString description() const { return descr; } + const char *parameters() const; + float gamma() const; + + void setImage( const QImage & ); + void setStatus( int ); + void setFormat( const char * ); + void setIODevice( QIODevice * ); + void setFileName( const QString & ); + void setQuality( int ); + void setDescription( const QString & ); + void setParameters( const char * ); + void setGamma( float ); + + bool read(); + bool write(); + + static const char* imageFormat( const QString &fileName ); + static const char *imageFormat( QIODevice * ); + static QStrList inputFormats(); + static QStrList outputFormats(); + + static void defineIOHandler( const char *format, + const char *header, + const char *flags, + image_io_handler read_image, + image_io_handler write_image ); + +private: + void init(); + + QImage im; // image + int iostat; // IO status + QCString frmt; // image format + QIODevice *iodev; // IO device + QString fname; // file name + char *params; // image parameters //### change to QImageIOData *d in 3.0 + QString descr; // image description + QImageIOData *d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QImageIO( const QImageIO & ); + QImageIO &operator=( const QImageIO & ); +#endif +}; + +#endif //QT_NO_IMAGEIO + +Q_EXPORT void bitBlt( QImage* dst, int dx, int dy, const QImage* src, + int sx=0, int sy=0, int sw=-1, int sh=-1, + int conversion_flags=0 ); + + +/***************************************************************************** + QImage member functions + *****************************************************************************/ + +inline bool QImage::hasAlphaBuffer() const +{ + return data->alpha; +} + +inline uchar *QImage::bits() const +{ + return data->bits ? data->bits[0] : 0; +} + +inline uchar **QImage::jumpTable() const +{ + return data->bits; +} + +inline QRgb *QImage::colorTable() const +{ + return data->ctbl; +} + +inline int QImage::numBytes() const +{ + return data->nbytes; +} + +inline int QImage::bytesPerLine() const +{ + return data->h ? data->nbytes/data->h : 0; +} + +inline QImage QImage::copy(const QRect& r) const +{ + return copy(r.x(), r.y(), r.width(), r.height()); +} + +inline QRgb QImage::color( int i ) const +{ +#if defined(QT_CHECK_RANGE) + if ( i >= data->ncols ) + warningIndexRange( "color", i ); +#endif + return data->ctbl ? data->ctbl[i] : (QRgb)-1; +} + +inline void QImage::setColor( int i, QRgb c ) +{ +#if defined(QT_CHECK_RANGE) + if ( i >= data->ncols ) + warningIndexRange( "setColor", i ); +#endif + if ( data->ctbl ) + data->ctbl[i] = c; +} + +inline uchar *QImage::scanLine( int i ) const +{ +#if defined(QT_CHECK_RANGE) + if ( i >= data->h ) + warningIndexRange( "scanLine", i ); +#endif + return data->bits ? data->bits[i] : 0; +} + +inline int QImage::dotsPerMeterX() const +{ + return data->dpmx; +} + +inline int QImage::dotsPerMeterY() const +{ + return data->dpmy; +} + +inline QPoint QImage::offset() const +{ + return data->offset; +} +#endif + +#endif // QIMAGE_H diff --git a/main/lib/image.effect/qpoint.cpp b/main/lib/image.effect/qpoint.cpp new file mode 100644 index 00000000..1c6c8a02 --- /dev/null +++ b/main/lib/image.effect/qpoint.cpp @@ -0,0 +1,463 @@ +/*************************************************************************** + + qpoint.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/**************************************************************************** +** $Id: qt/qpoint.cpp 3.3.4 edited May 27 2003 $ +** +** Implementation of QPoint class +** +** Created : 931028 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include "qpoint.h" +//#include "qdatastream.h" + + +/*! + \class QPoint qpoint.h + \brief The QPoint class defines a point in the plane. + + \ingroup images + \ingroup graphics + \mainclass + + A point is specified by an x coordinate and a y coordinate. + + The coordinate type is \c QCOORD (a 32-bit integer). The minimum + value of \c QCOORD is \c QCOORD_MIN (-2147483648) and the maximum + value is \c QCOORD_MAX (2147483647). + + The coordinates are accessed by the functions x() and y(); they + can be set by setX() and setY() or by the reference functions rx() + and ry(). + + Given a point \e p, the following statements are all equivalent: + \code + p.setX( p.x() + 1 ); + p += QPoint( 1, 0 ); + p.rx()++; + \endcode + + A QPoint can also be used as a vector. Addition and subtraction + of QPoints are defined as for vectors (each component is added + separately). You can divide or multiply a QPoint by an \c int or a + \c double. The function manhattanLength() gives an inexpensive + approximation of the length of the QPoint interpreted as a vector. + + Example: + \code + //QPoint oldPos is defined somewhere else + MyWidget::mouseMoveEvent( QMouseEvent *e ) + { + QPoint vector = e->pos() - oldPos; + if ( vector.manhattanLength() > 3 ) + ... //mouse has moved more than 3 pixels since oldPos + } + \endcode + + QPoints can be compared for equality or inequality, and they can + be written to and read from a QStream. + + \sa QPointArray QSize, QRect +*/ + + +/***************************************************************************** + QPoint member functions + *****************************************************************************/ + +/*! + \fn QPoint::QPoint() + + Constructs a point with coordinates (0, 0) (isNull() returns TRUE). +*/ + +/*! + \fn QPoint::QPoint( int xpos, int ypos ) + + Constructs a point with x value \a xpos and y value \a ypos. +*/ + +/*! + \fn bool QPoint::isNull() const + + Returns TRUE if both the x value and the y value are 0; otherwise + returns FALSE. +*/ + +/*! + \fn int QPoint::x() const + + Returns the x coordinate of the point. + + \sa setX() y() +*/ + +/*! + \fn int QPoint::y() const + + Returns the y coordinate of the point. + + \sa setY() x() +*/ + +/*! + \fn void QPoint::setX( int x ) + + Sets the x coordinate of the point to \a x. + + \sa x() setY() +*/ + +/*! + \fn void QPoint::setY( int y ) + + Sets the y coordinate of the point to \a y. + + \sa y() setX() +*/ + + +/*! + \fn QCOORD &QPoint::rx() + + Returns a reference to the x coordinate of the point. + + Using a reference makes it possible to directly manipulate x. + + Example: + \code + QPoint p( 1, 2 ); + p.rx()--; // p becomes (0, 2) + \endcode + + \sa ry() +*/ + +/*! + \fn QCOORD &QPoint::ry() + + Returns a reference to the y coordinate of the point. + + Using a reference makes it possible to directly manipulate y. + + Example: + \code + QPoint p( 1, 2 ); + p.ry()++; // p becomes (1, 3) + \endcode + + \sa rx() +*/ + + +/*! + \fn QPoint &QPoint::operator+=( const QPoint &p ) + + Adds point \a p to this point and returns a reference to this + point. + + Example: + \code + QPoint p( 3, 7 ); + QPoint q( -1, 4 ); + p += q; // p becomes (2,11) + \endcode +*/ + +/*! + \fn QPoint &QPoint::operator-=( const QPoint &p ) + + Subtracts point \a p from this point and returns a reference to + this point. + + Example: + \code + QPoint p( 3, 7 ); + QPoint q( -1, 4 ); + p -= q; // p becomes (4,3) + \endcode +*/ + +/*! + \fn QPoint &QPoint::operator*=( int c ) + + Multiplies this point's x and y by \a c, and returns a reference + to this point. + + Example: + \code + QPoint p( -1, 4 ); + p *= 2; // p becomes (-2,8) + \endcode +*/ + +/*! + \overload QPoint &QPoint::operator*=( double c ) + + Multiplies this point's x and y by \a c, and returns a reference + to this point. + + Example: + \code + QPoint p( -1, 4 ); + p *= 2.5; // p becomes (-3,10) + \endcode + + Note that the result is truncated because points are held as + integers. +*/ + + +/*! + \fn bool operator==( const QPoint &p1, const QPoint &p2 ) + + \relates QPoint + + Returns TRUE if \a p1 and \a p2 are equal; otherwise returns FALSE. +*/ + +/*! + \fn bool operator!=( const QPoint &p1, const QPoint &p2 ) + + \relates QPoint + + Returns TRUE if \a p1 and \a p2 are not equal; otherwise returns FALSE. +*/ + +/*! + \fn const QPoint operator+( const QPoint &p1, const QPoint &p2 ) + + \relates QPoint + + Returns the sum of \a p1 and \a p2; each component is added separately. +*/ + +/*! + \fn const QPoint operator-( const QPoint &p1, const QPoint &p2 ) + + \relates QPoint + + Returns \a p2 subtracted from \a p1; each component is subtracted + separately. +*/ + +/*! + \fn const QPoint operator*( const QPoint &p, int c ) + + \relates QPoint + + Returns the QPoint formed by multiplying both components of \a p + by \a c. +*/ + +/*! + \overload const QPoint operator*( int c, const QPoint &p ) + + \relates QPoint + + Returns the QPoint formed by multiplying both components of \a p + by \a c. +*/ + +/*! + \overload const QPoint operator*( const QPoint &p, double c ) + + \relates QPoint + + Returns the QPoint formed by multiplying both components of \a p + by \a c. + + Note that the result is truncated because points are held as + integers. +*/ + +/*! + \overload const QPoint operator*( double c, const QPoint &p ) + + \relates QPoint + + Returns the QPoint formed by multiplying both components of \a p + by \a c. + + Note that the result is truncated because points are held as + integers. +*/ + +/*! + \overload const QPoint operator-( const QPoint &p ) + + \relates QPoint + + Returns the QPoint formed by changing the sign of both components + of \a p, equivalent to \c{QPoint(0,0) - p}. +*/ + +/*! + \fn QPoint &QPoint::operator/=( int c ) + + Divides both x and y by \a c, and returns a reference to this + point. + + Example: + \code + QPoint p( -2, 8 ); + p /= 2; // p becomes (-1,4) + \endcode +*/ + +/*! + \overload QPoint &QPoint::operator/=( double c ) + + Divides both x and y by \a c, and returns a reference to this + point. + + Example: + \code + QPoint p( -3, 10 ); + p /= 2.5; // p becomes (-1,4) + \endcode + + Note that the result is truncated because points are held as + integers. +*/ + +/*! + \fn const QPoint operator/( const QPoint &p, int c ) + + \relates QPoint + + Returns the QPoint formed by dividing both components of \a p by + \a c. +*/ + +/*! + \overload const QPoint operator/( const QPoint &p, double c ) + + \relates QPoint + + Returns the QPoint formed by dividing both components of \a p + by \a c. + + Note that the result is truncated because points are held as + integers. +*/ + + +void QPoint::warningDivByZero() +{ +#if defined(QT_CHECK_MATH) + qWarning( "QPoint: Division by zero error" ); +#endif +} + + +/***************************************************************************** + QPoint stream functions + *****************************************************************************/ +#ifndef QT_NO_DATASTREAM +/*! + \relates QPoint + + Writes point \a p to the stream \a s and returns a reference to + the stream. + + \sa \link datastreamformat.html Format of the QDataStream operators \endlink +*/ + +QDataStream &operator<<( QDataStream &s, const QPoint &p ) +{ + if ( s.version() == 1 ) + s << (Q_INT16)p.x() << (Q_INT16)p.y(); + else + s << (Q_INT32)p.x() << (Q_INT32)p.y(); + return s; +} + +/*! + \relates QPoint + + Reads a QPoint from the stream \a s into point \a p and returns a + reference to the stream. + + \sa \link datastreamformat.html Format of the QDataStream operators \endlink +*/ + +QDataStream &operator>>( QDataStream &s, QPoint &p ) +{ + if ( s.version() == 1 ) { + Q_INT16 x, y; + s >> x; p.rx() = x; + s >> y; p.ry() = y; + } + else { + Q_INT32 x, y; + s >> x; p.rx() = x; + s >> y; p.ry() = y; + } + return s; +} +#endif // QT_NO_DATASTREAM +/*! + Returns the sum of the absolute values of x() and y(), + traditionally known as the "Manhattan length" of the vector from + the origin to the point. The tradition arises because such + distances apply to travelers who can only travel on a rectangular + grid, like the streets of Manhattan. + + This is a useful, and quick to calculate, approximation to the + true length: sqrt(pow(x(),2)+pow(y(),2)). +*/ +int QPoint::manhattanLength() const +{ + return QABS(x())+QABS(y()); +} diff --git a/main/lib/image.effect/qpoint.h b/main/lib/image.effect/qpoint.h new file mode 100644 index 00000000..7a5d1a00 --- /dev/null +++ b/main/lib/image.effect/qpoint.h @@ -0,0 +1,235 @@ +/*************************************************************************** + + qpoint.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/**************************************************************************** +** $Id: qt/qpoint.h 3.3.4 edited May 27 2003 $ +** +** Definition of QPoint class +** +** Created : 931028 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QPOINT_H +#define QPOINT_H + +#include "qt.h" + +class Q_EXPORT QPoint +{ +public: + QPoint(); + QPoint( int xpos, int ypos ); + + bool isNull() const; + + int x() const; + int y() const; + void setX( int x ); + void setY( int y ); + + int manhattanLength() const; + + QCOORD &rx(); + QCOORD &ry(); + + QPoint &operator+=( const QPoint &p ); + QPoint &operator-=( const QPoint &p ); + QPoint &operator*=( int c ); + QPoint &operator*=( double c ); + QPoint &operator/=( int c ); + QPoint &operator/=( double c ); + + friend inline bool operator==( const QPoint &, const QPoint & ); + friend inline bool operator!=( const QPoint &, const QPoint & ); + friend inline const QPoint operator+( const QPoint &, const QPoint & ); + friend inline const QPoint operator-( const QPoint &, const QPoint & ); + friend inline const QPoint operator*( const QPoint &, int ); + friend inline const QPoint operator*( int, const QPoint & ); + friend inline const QPoint operator*( const QPoint &, double ); + friend inline const QPoint operator*( double, const QPoint & ); + friend inline const QPoint operator-( const QPoint & ); + friend inline const QPoint operator/( const QPoint &, int ); + friend inline const QPoint operator/( const QPoint &, double ); + +private: + static void warningDivByZero(); + +#if defined(Q_OS_MAC) + QCOORD yp; + QCOORD xp; +#else + QCOORD xp; + QCOORD yp; +#endif +}; + + +/***************************************************************************** + QPoint stream functions + *****************************************************************************/ +#ifndef QT_NO_DATASTREAM +Q_EXPORT QDataStream &operator<<( QDataStream &, const QPoint & ); +Q_EXPORT QDataStream &operator>>( QDataStream &, QPoint & ); +#endif + +/***************************************************************************** + QPoint inline functions + *****************************************************************************/ + +inline QPoint::QPoint() +{ xp=0; yp=0; } + +inline QPoint::QPoint( int xpos, int ypos ) +{ xp=(QCOORD)xpos; yp=(QCOORD)ypos; } + +inline bool QPoint::isNull() const +{ return xp == 0 && yp == 0; } + +inline int QPoint::x() const +{ return xp; } + +inline int QPoint::y() const +{ return yp; } + +inline void QPoint::setX( int x ) +{ xp = (QCOORD)x; } + +inline void QPoint::setY( int y ) +{ yp = (QCOORD)y; } + +inline QCOORD &QPoint::rx() +{ return xp; } + +inline QCOORD &QPoint::ry() +{ return yp; } + +inline QPoint &QPoint::operator+=( const QPoint &p ) +{ xp+=p.xp; yp+=p.yp; return *this; } + +inline QPoint &QPoint::operator-=( const QPoint &p ) +{ xp-=p.xp; yp-=p.yp; return *this; } + +inline QPoint &QPoint::operator*=( int c ) +{ xp*=(QCOORD)c; yp*=(QCOORD)c; return *this; } + +inline QPoint &QPoint::operator*=( double c ) +{ xp=(QCOORD)(xp*c); yp=(QCOORD)(yp*c); return *this; } + +inline bool operator==( const QPoint &p1, const QPoint &p2 ) +{ return p1.xp == p2.xp && p1.yp == p2.yp; } + +inline bool operator!=( const QPoint &p1, const QPoint &p2 ) +{ return p1.xp != p2.xp || p1.yp != p2.yp; } + +inline const QPoint operator+( const QPoint &p1, const QPoint &p2 ) +{ return QPoint(p1.xp+p2.xp, p1.yp+p2.yp); } + +inline const QPoint operator-( const QPoint &p1, const QPoint &p2 ) +{ return QPoint(p1.xp-p2.xp, p1.yp-p2.yp); } + +inline const QPoint operator*( const QPoint &p, int c ) +{ return QPoint(p.xp*c, p.yp*c); } + +inline const QPoint operator*( int c, const QPoint &p ) +{ return QPoint(p.xp*c, p.yp*c); } + +inline const QPoint operator*( const QPoint &p, double c ) +{ return QPoint((QCOORD)(p.xp*c), (QCOORD)(p.yp*c)); } + +inline const QPoint operator*( double c, const QPoint &p ) +{ return QPoint((QCOORD)(p.xp*c), (QCOORD)(p.yp*c)); } + +inline const QPoint operator-( const QPoint &p ) +{ return QPoint(-p.xp, -p.yp); } + +inline QPoint &QPoint::operator/=( int c ) +{ +#if defined(QT_CHECK_MATH) + if ( c == 0 ) + warningDivByZero(); +#endif + xp/=(QCOORD)c; + yp/=(QCOORD)c; + return *this; +} + +inline QPoint &QPoint::operator/=( double c ) +{ +#if defined(QT_CHECK_MATH) + if ( c == 0.0 ) + warningDivByZero(); +#endif + xp=(QCOORD)(xp/c); + yp=(QCOORD)(yp/c); + return *this; +} + +inline const QPoint operator/( const QPoint &p, int c ) +{ +#if defined(QT_CHECK_MATH) + if ( c == 0 ) + QPoint::warningDivByZero(); +#endif + return QPoint(p.xp/c, p.yp/c); +} + +inline const QPoint operator/( const QPoint &p, double c ) +{ +#if defined(QT_CHECK_MATH) + if ( c == 0.0 ) + QPoint::warningDivByZero(); +#endif + return QPoint((QCOORD)(p.xp/c), (QCOORD)(p.yp/c)); +} + +#define Q_DEFINED_QPOINT +#endif // QPOINT_H diff --git a/main/lib/image.effect/qrect.cpp b/main/lib/image.effect/qrect.cpp new file mode 100644 index 00000000..4c3c9417 --- /dev/null +++ b/main/lib/image.effect/qrect.cpp @@ -0,0 +1,981 @@ +/*************************************************************************** + + qrect.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/**************************************************************************** +** $Id: qt/qrect.cpp 3.3.4 edited May 27 2003 $ +** +** Implementation of QRect class +** +** Created : 931028 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#define QRECT_C + +#include "qrect.h" +//#include "qdatastream.h" + +/*! + \class QRect + \brief The QRect class defines a rectangle in the plane. + + \ingroup images + \ingroup graphics + \mainclass + + A rectangle is internally represented as an upper-left corner and + a bottom-right corner, but it is normally expressed as an + upper-left corner and a size. + + The coordinate type is QCOORD (defined in \c qwindowdefs.h as \c + int). The minimum value of QCOORD is QCOORD_MIN (-2147483648) and + the maximum value is QCOORD_MAX (2147483647). + + Note that the size (width and height) of a rectangle might be + different from what you are used to. If the top-left corner and + the bottom-right corner are the same, the height and the width of + the rectangle will both be 1. + + Generally, \e{width = right - left + 1} and \e{height = bottom - + top + 1}. We designed it this way to make it correspond to + rectangular spaces used by drawing functions in which the width + and height denote a number of pixels. For example, drawing a + rectangle with width and height 1 draws a single pixel. + + The default coordinate system has origin (0, 0) in the top-left + corner. The positive direction of the y axis is down, and the + positive x axis is from left to right. + + A QRect can be constructed with a set of left, top, width and + height integers, from two QPoints or from a QPoint and a QSize. + After creation the dimensions can be changed, e.g. with setLeft(), + setRight(), setTop() and setBottom(), or by setting sizes, e.g. + setWidth(), setHeight() and setSize(). The dimensions can also be + changed with the move functions, e.g. moveBy(), moveCenter(), + moveBottomRight(), etc. You can also add coordinates to a + rectangle with addCoords(). + + You can test to see if a QRect contains a specific point with + contains(). You can also test to see if two QRects intersect with + intersects() (see also intersect()). To get the bounding rectangle + of two QRects use unite(). + + \sa QPoint, QSize +*/ + + +/***************************************************************************** + QRect member functions + *****************************************************************************/ + +/*! + \fn QRect::QRect() + + Constructs an invalid rectangle. +*/ + +/*! + Constructs a rectangle with \a topLeft as the top-left corner and + \a bottomRight as the bottom-right corner. +*/ + +QRect::QRect( const QPoint &topLeft, const QPoint &bottomRight ) +{ + x1 = (QCOORD)topLeft.x(); + y1 = (QCOORD)topLeft.y(); + x2 = (QCOORD)bottomRight.x(); + y2 = (QCOORD)bottomRight.y(); +} + +/*! + Constructs a rectangle with \a topLeft as the top-left corner and + \a size as the rectangle size. +*/ + +QRect::QRect( const QPoint &topLeft, const QSize &size ) +{ + x1 = (QCOORD)topLeft.x(); + y1 = (QCOORD)topLeft.y(); + x2 = (QCOORD)(x1+size.width()-1); + y2 = (QCOORD)(y1+size.height()-1); +} + +/*! + \fn QRect::QRect( int left, int top, int width, int height ) + + Constructs a rectangle with the \a top, \a left corner and \a + width and \a height. + + Example (creates three identical rectangles): + \code + QRect r1( QPoint(100,200), QPoint(110,215) ); + QRect r2( QPoint(100,200), QSize(11,16) ); + QRect r3( 100, 200, 11, 16 ); + \endcode +*/ + + +/*! + \fn bool QRect::isNull() const + + Returns TRUE if the rectangle is a null rectangle; otherwise + returns FALSE. + + A null rectangle has both the width and the height set to 0, that + is right() == left() - 1 and bottom() == top() - 1. + + Note that if right() == left() and bottom() == top(), then the + rectangle has width 1 and height 1. + + A null rectangle is also empty. + + A null rectangle is not valid. + + \sa isEmpty(), isValid() +*/ + +/*! + \fn bool QRect::isEmpty() const + + Returns TRUE if the rectangle is empty; otherwise returns FALSE. + + An empty rectangle has a left() \> right() or top() \> bottom(). + + An empty rectangle is not valid. \c{isEmpty() == !isValid()} + + \sa isNull(), isValid(), normalize() +*/ + +/*! + \fn bool QRect::isValid() const + + Returns TRUE if the rectangle is valid; otherwise returns FALSE. + + A valid rectangle has a left() \<= right() and top() \<= bottom(). + + Note that non-trivial operations like intersections are not defined + for invalid rectangles. + + \c{isValid() == !isEmpty()} + + \sa isNull(), isEmpty(), normalize() +*/ + + +/*! + Returns a normalized rectangle, i.e. a rectangle that has a + non-negative width and height. + + It swaps left and right if left() \> right(), and swaps top and + bottom if top() \> bottom(). + + \sa isValid() +*/ + +QRect QRect::normalize() const +{ + QRect r; + if ( x2 < x1 ) { // swap bad x values + r.x1 = x2; + r.x2 = x1; + } else { + r.x1 = x1; + r.x2 = x2; + } + if ( y2 < y1 ) { // swap bad y values + r.y1 = y2; + r.y2 = y1; + } else { + r.y1 = y1; + r.y2 = y2; + } + return r; +} + + +/*! + \fn int QRect::left() const + + Returns the left coordinate of the rectangle. Identical to x(). + + \sa setLeft(), right(), topLeft(), bottomLeft() +*/ + +/*! + \fn int QRect::top() const + + Returns the top coordinate of the rectangle. Identical to y(). + + \sa setTop(), bottom(), topLeft(), topRight() +*/ + +/*! + \fn int QRect::right() const + + Returns the right coordinate of the rectangle. + + \sa setRight(), left(), topRight(), bottomRight() +*/ + +/*! + \fn int QRect::bottom() const + + Returns the bottom coordinate of the rectangle. + + \sa setBottom(), top(), bottomLeft(), bottomRight() +*/ + +/*! + \fn QCOORD &QRect::rLeft() + + Returns a reference to the left coordinate of the rectangle. + + \sa rTop(), rRight(), rBottom() +*/ + +/*! + \fn QCOORD &QRect::rTop() + + Returns a reference to the top coordinate of the rectangle. + + \sa rLeft(), rRight(), rBottom() +*/ + +/*! + \fn QCOORD &QRect::rRight() + + Returns a reference to the right coordinate of the rectangle. + + \sa rLeft(), rTop(), rBottom() +*/ + +/*! + \fn QCOORD &QRect::rBottom() + + Returns a reference to the bottom coordinate of the rectangle. + + \sa rLeft(), rTop(), rRight() +*/ + +/*! + \fn int QRect::x() const + + Returns the left coordinate of the rectangle. Identical to left(). + + \sa left(), y(), setX() +*/ + +/*! + \fn int QRect::y() const + + Returns the top coordinate of the rectangle. Identical to top(). + + \sa top(), x(), setY() +*/ + +/*! + \fn void QRect::setLeft( int pos ) + + Sets the left edge of the rectangle to \a pos. May change the + width, but will never change the right edge of the rectangle. + + Identical to setX(). + + \sa left(), setTop(), setWidth() +*/ + +/*! + \fn void QRect::setTop( int pos ) + + Sets the top edge of the rectangle to \a pos. May change the + height, but will never change the bottom edge of the rectangle. + + Identical to setY(). + + \sa top(), setBottom(), setHeight() +*/ + +/*! + \fn void QRect::setRight( int pos ) + + Sets the right edge of the rectangle to \a pos. May change the + width, but will never change the left edge of the rectangle. + + \sa right(), setLeft(), setWidth() +*/ + +/*! + \fn void QRect::setBottom( int pos ) + + Sets the bottom edge of the rectangle to \a pos. May change the + height, but will never change the top edge of the rectangle. + + \sa bottom(), setTop(), setHeight() +*/ + +/*! + \fn void QRect::setX( int x ) + + Sets the x position of the rectangle (its left end) to \a x. May + change the width, but will never change the right edge of the + rectangle. + + Identical to setLeft(). + + \sa x(), setY() +*/ + +/*! + \fn void QRect::setY( int y ) + + Sets the y position of the rectangle (its top) to \a y. May change + the height, but will never change the bottom edge of the + rectangle. + + Identical to setTop(). + + \sa y(), setX() +*/ + +/*! + Set the top-left corner of the rectangle to \a p. May change + the size, but will the never change the bottom-right corner of + the rectangle. + + \sa topLeft(), moveTopLeft(), setBottomRight(), setTopRight(), setBottomLeft() +*/ +void QRect::setTopLeft( const QPoint &p ) +{ + setLeft( p.x() ); + setTop( p.y() ); +} + +/*! + Set the bottom-right corner of the rectangle to \a p. May change + the size, but will the never change the top-left corner of + the rectangle. + + \sa bottomRight(), moveBottomRight(), setTopLeft(), setTopRight(), setBottomLeft() +*/ +void QRect::setBottomRight( const QPoint &p ) +{ + setRight( p.x() ); + setBottom( p.y() ); +} + +/*! + Set the top-right corner of the rectangle to \a p. May change + the size, but will the never change the bottom-left corner of + the rectangle. + + \sa topRight(), moveTopRight(), setTopLeft(), setBottomRight(), setBottomLeft() +*/ +void QRect::setTopRight( const QPoint &p ) +{ + setRight( p.x() ); + setTop( p.y() ); +} + +/*! + Set the bottom-left corner of the rectangle to \a p. May change + the size, but will the never change the top-right corner of + the rectangle. + + \sa bottomLeft(), moveBottomLeft(), setTopLeft(), setBottomRight(), setTopRight() +*/ +void QRect::setBottomLeft( const QPoint &p ) +{ + setLeft( p.x() ); + setBottom( p.y() ); +} + +/*! + \fn QPoint QRect::topLeft() const + + Returns the top-left position of the rectangle. + + \sa setTopLeft(), moveTopLeft(), bottomRight(), left(), top() +*/ + +/*! + \fn QPoint QRect::bottomRight() const + + Returns the bottom-right position of the rectangle. + + \sa setBottomRight(), moveBottomRight(), topLeft(), right(), bottom() +*/ + +/*! + \fn QPoint QRect::topRight() const + + Returns the top-right position of the rectangle. + + \sa setTopRight(), moveTopRight(), bottomLeft(), top(), right() +*/ + +/*! + \fn QPoint QRect::bottomLeft() const + + Returns the bottom-left position of the rectangle. + + \sa setBottomLeft(), moveBottomLeft(), topRight(), bottom(), left() +*/ + +/*! + \fn QPoint QRect::center() const + + Returns the center point of the rectangle. + + \sa moveCenter(), topLeft(), bottomRight(), topRight(), bottomLeft() +*/ + + +/*! + Extracts the rectangle parameters as the position \a *x, \a *y and + width \a *w and height \a *h. + + \sa setRect(), coords() +*/ + +void QRect::rect( int *x, int *y, int *w, int *h ) const +{ + *x = x1; + *y = y1; + *w = x2-x1+1; + *h = y2-y1+1; +} + +/*! + Extracts the rectangle parameters as the top-left point \a *xp1, + \a *yp1 and the bottom-right point \a *xp2, \a *yp2. + + \sa setCoords(), rect() +*/ + +void QRect::coords( int *xp1, int *yp1, int *xp2, int *yp2 ) const +{ + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + + +/*! + Sets the left position of the rectangle to \a pos, leaving the + size unchanged. + + \sa left(), setLeft(), moveTop(), moveRight(), moveBottom() +*/ +void QRect::moveLeft( int pos ) +{ + x2 += (QCOORD)(pos - x1); + x1 = (QCOORD)pos; +} + +/*! + Sets the top position of the rectangle to \a pos, leaving the + size unchanged. + + \sa top(), setTop(), moveLeft(), moveRight(), moveBottom() +*/ + +void QRect::moveTop( int pos ) +{ + y2 += (QCOORD)(pos - y1); + y1 = (QCOORD)pos; +} + +/*! + Sets the right position of the rectangle to \a pos, leaving the + size unchanged. + + \sa right(), setRight(), moveLeft(), moveTop(), moveBottom() +*/ + +void QRect::moveRight( int pos ) +{ + x1 += (QCOORD)(pos - x2); + x2 = (QCOORD)pos; +} + +/*! + Sets the bottom position of the rectangle to \a pos, leaving the + size unchanged. + + \sa bottom(), setBottom(), moveLeft(), moveTop(), moveRight() +*/ + +void QRect::moveBottom( int pos ) +{ + y1 += (QCOORD)(pos - y2); + y2 = (QCOORD)pos; +} + +/*! + Sets the top-left position of the rectangle to \a p, leaving the + size unchanged. + + \sa topLeft(), setTopLeft(), moveBottomRight(), moveTopRight(), moveBottomLeft() +*/ + +void QRect::moveTopLeft( const QPoint &p ) +{ + moveLeft( p.x() ); + moveTop( p.y() ); +} + +/*! + Sets the bottom-right position of the rectangle to \a p, leaving + the size unchanged. + + \sa bottomRight(), setBottomRight(), moveTopLeft(), moveTopRight(), moveBottomLeft() +*/ + +void QRect::moveBottomRight( const QPoint &p ) +{ + moveRight( p.x() ); + moveBottom( p.y() ); +} + +/*! + Sets the top-right position of the rectangle to \a p, leaving the + size unchanged. + + \sa topRight(), setTopRight(), moveTopLeft(), moveBottomRight(), moveBottomLeft() +*/ + +void QRect::moveTopRight( const QPoint &p ) +{ + moveRight( p.x() ); + moveTop( p.y() ); +} + +/*! + Sets the bottom-left position of the rectangle to \a p, leaving + the size unchanged. + + \sa bottomLeft(), setBottomLeft(), moveTopLeft(), moveBottomRight(), moveTopRight() +*/ + +void QRect::moveBottomLeft( const QPoint &p ) +{ + moveLeft( p.x() ); + moveBottom( p.y() ); +} + + +/*! + Sets the center point of the rectangle to \a p, leaving the size + unchanged. + + \sa center(), moveTopLeft(), moveBottomRight(), moveTopRight(), moveBottomLeft() +*/ + +void QRect::moveCenter( const QPoint &p ) +{ + QCOORD w = x2 - x1; + QCOORD h = y2 - y1; + x1 = (QCOORD)(p.x() - w/2); + y1 = (QCOORD)(p.y() - h/2); + x2 = x1 + w; + y2 = y1 + h; +} + + +/*! + Moves the rectangle \a dx along the x axis and \a dy along the y + axis, relative to the current position. Positive values move the + rectangle to the right and down. + + \sa moveTopLeft() +*/ + +void QRect::moveBy( int dx, int dy ) +{ + x1 += (QCOORD)dx; + y1 += (QCOORD)dy; + x2 += (QCOORD)dx; + y2 += (QCOORD)dy; +} + +/*! + Sets the coordinates of the rectangle's top-left corner to \a (x, + y), and its size to \a (w, h). + + \sa rect(), setCoords() +*/ + +void QRect::setRect( int x, int y, int w, int h ) +{ + x1 = (QCOORD)x; + y1 = (QCOORD)y; + x2 = (QCOORD)(x+w-1); + y2 = (QCOORD)(y+h-1); +} + +/*! + Sets the coordinates of the rectangle's top-left corner to \a + (xp1, yp1), and the coordinates of its bottom-right corner to \a + (xp2, yp2). + + \sa coords(), setRect() +*/ + +void QRect::setCoords( int xp1, int yp1, int xp2, int yp2 ) +{ + x1 = (QCOORD)xp1; + y1 = (QCOORD)yp1; + x2 = (QCOORD)xp2; + y2 = (QCOORD)yp2; +} + +/*! + Adds \a xp1, \a yp1, \a xp2 and \a yp2 respectively to the + existing coordinates of the rectangle. +*/ + +void QRect::addCoords( int xp1, int yp1, int xp2, int yp2 ) +{ + x1 += (QCOORD)xp1; + y1 += (QCOORD)yp1; + x2 += (QCOORD)xp2; + y2 += (QCOORD)yp2; +} + +/*! + \fn QSize QRect::size() const + + Returns the size of the rectangle. + + \sa width(), height() +*/ + +/*! + \fn int QRect::width() const + + Returns the width of the rectangle. The width includes both the + left and right edges, i.e. width = right - left + 1. + + \sa height(), size(), setHeight() +*/ + +/*! + \fn int QRect::height() const + + Returns the height of the rectangle. The height includes both the + top and bottom edges, i.e. height = bottom - top + 1. + + \sa width(), size(), setHeight() +*/ + +/*! + Sets the width of the rectangle to \a w. The right edge is + changed, but not the left edge. + + \sa width(), setLeft(), setRight(), setSize() +*/ + +void QRect::setWidth( int w ) +{ + x2 = (QCOORD)(x1 + w - 1); +} + +/*! + Sets the height of the rectangle to \a h. The top edge is not + moved, but the bottom edge may be moved. + + \sa height(), setTop(), setBottom(), setSize() +*/ + +void QRect::setHeight( int h ) +{ + y2 = (QCOORD)(y1 + h - 1); +} + +/*! + Sets the size of the rectangle to \a s. The top-left corner is not + moved. + + \sa size(), setWidth(), setHeight() +*/ + +void QRect::setSize( const QSize &s ) +{ + x2 = (QCOORD)(s.width() +x1-1); + y2 = (QCOORD)(s.height()+y1-1); +} + +/*! + Returns TRUE if the point \a p is inside or on the edge of the + rectangle; otherwise returns FALSE. + + If \a proper is TRUE, this function returns TRUE only if \a p is + inside (not on the edge). +*/ + +bool QRect::contains( const QPoint &p, bool proper ) const +{ + if ( proper ) + return p.x() > x1 && p.x() < x2 && + p.y() > y1 && p.y() < y2; + else + return p.x() >= x1 && p.x() <= x2 && + p.y() >= y1 && p.y() <= y2; +} + +/*! + \overload bool QRect::contains( int x, int y, bool proper ) const + + Returns TRUE if the point \a x, \a y is inside this rectangle; + otherwise returns FALSE. + + If \a proper is TRUE, this function returns TRUE only if the point + is entirely inside (not on the edge). +*/ + +/*! + \overload bool QRect::contains( int x, int y ) const + + Returns TRUE if the point \a x, \a y is inside this rectangle; + otherwise returns FALSE. +*/ + +/*! + \overload + + Returns TRUE if the rectangle \a r is inside this rectangle; + otherwise returns FALSE. + + If \a proper is TRUE, this function returns TRUE only if \a r is + entirely inside (not on the edge). + + \sa unite(), intersect(), intersects() +*/ + +bool QRect::contains( const QRect &r, bool proper ) const +{ + if ( proper ) + return r.x1 > x1 && r.x2 < x2 && r.y1 > y1 && r.y2 < y2; + else + return r.x1 >= x1 && r.x2 <= x2 && r.y1 >= y1 && r.y2 <= y2; +} + +/*! + Unites this rectangle with rectangle \a r. +*/ +QRect& QRect::operator|=(const QRect &r) +{ + *this = *this | r; + return *this; +} + +/*! + Intersects this rectangle with rectangle \a r. +*/ +QRect& QRect::operator&=(const QRect &r) +{ + *this = *this & r; + return *this; +} + + +/*! + Returns the bounding rectangle of this rectangle and rectangle \a + r. + + The bounding rectangle of a nonempty rectangle and an empty or + invalid rectangle is defined to be the nonempty rectangle. + + \sa operator|=(), operator&(), intersects(), contains() +*/ + +QRect QRect::operator|(const QRect &r) const +{ + if ( isValid() ) { + if ( r.isValid() ) { + QRect tmp; + tmp.setLeft( QMIN( x1, r.x1 ) ); + tmp.setRight( QMAX( x2, r.x2 ) ); + tmp.setTop( QMIN( y1, r.y1 ) ); + tmp.setBottom( QMAX( y2, r.y2 ) ); + return tmp; + } else { + return *this; + } + } else { + return r; + } +} + +/*! + Returns the bounding rectangle of this rectangle and rectangle \a + r. \c{r.unite(s)} is equivalent to \c{r|s}. +*/ +QRect QRect::unite( const QRect &r ) const +{ + return *this | r; +} + + +/*! + Returns the intersection of this rectangle and rectangle \a r. + + Returns an empty rectangle if there is no intersection. + + \sa operator&=(), operator|(), isEmpty(), intersects(), contains() +*/ + +QRect QRect::operator&( const QRect &r ) const +{ + QRect tmp; + tmp.x1 = QMAX( x1, r.x1 ); + tmp.x2 = QMIN( x2, r.x2 ); + tmp.y1 = QMAX( y1, r.y1 ); + tmp.y2 = QMIN( y2, r.y2 ); + return tmp; +} + +/*! + Returns the intersection of this rectangle and rectangle \a r. + \c{r.intersect(s)} is equivalent to \c{r&s}. +*/ +QRect QRect::intersect( const QRect &r ) const +{ + return *this & r; +} + +/*! + Returns TRUE if this rectangle intersects with rectangle \a r + (there is at least one pixel that is within both rectangles); + otherwise returns FALSE. + + \sa intersect(), contains() +*/ + +bool QRect::intersects( const QRect &r ) const +{ + return ( QMAX( x1, r.x1 ) <= QMIN( x2, r.x2 ) && + QMAX( y1, r.y1 ) <= QMIN( y2, r.y2 ) ); +} + + +/*! + \relates QRect + + Returns TRUE if \a r1 and \a r2 are equal; otherwise returns FALSE. +*/ + +bool operator==( const QRect &r1, const QRect &r2 ) +{ + return r1.x1==r2.x1 && r1.x2==r2.x2 && r1.y1==r2.y1 && r1.y2==r2.y2; +} + +/*! + \relates QRect + + Returns TRUE if \a r1 and \a r2 are different; otherwise returns FALSE. +*/ + +bool operator!=( const QRect &r1, const QRect &r2 ) +{ + return r1.x1!=r2.x1 || r1.x2!=r2.x2 || r1.y1!=r2.y1 || r1.y2!=r2.y2; +} + + +/***************************************************************************** + QRect stream functions + *****************************************************************************/ +#ifndef QT_NO_DATASTREAM +/*! + \relates QRect + + Writes the QRect, \a r, to the stream \a s, and returns a + reference to the stream. + + \sa \link datastreamformat.html Format of the QDataStream operators \endlink +*/ + +QDataStream &operator<<( QDataStream &s, const QRect &r ) +{ + if ( s.version() == 1 ) + s << (Q_INT16)r.left() << (Q_INT16)r.top() + << (Q_INT16)r.right() << (Q_INT16)r.bottom(); + else + s << (Q_INT32)r.left() << (Q_INT32)r.top() + << (Q_INT32)r.right() << (Q_INT32)r.bottom(); + return s; +} + +/*! + \relates QRect + + Reads a QRect from the stream \a s into rect \a r and returns a + reference to the stream. + + \sa \link datastreamformat.html Format of the QDataStream operators \endlink +*/ + +QDataStream &operator>>( QDataStream &s, QRect &r ) +{ + if ( s.version() == 1 ) { + Q_INT16 x1, y1, x2, y2; + s >> x1; s >> y1; s >> x2; s >> y2; + r.setCoords( x1, y1, x2, y2 ); + } + else { + Q_INT32 x1, y1, x2, y2; + s >> x1; s >> y1; s >> x2; s >> y2; + r.setCoords( x1, y1, x2, y2 ); + } + return s; +} +#endif // QT_NO_DATASTREAM diff --git a/main/lib/image.effect/qrect.h b/main/lib/image.effect/qrect.h new file mode 100644 index 00000000..43628532 --- /dev/null +++ b/main/lib/image.effect/qrect.h @@ -0,0 +1,294 @@ +/*************************************************************************** + + qrect.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/**************************************************************************** +** $Id: qt/qrect.h 3.3.4 edited May 27 2003 $ +** +** Definition of QRect class +** +** Created : 931028 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QRECT_H +#define QRECT_H + +#include "qt.h" +#include "qsize.h" + +#if defined(topLeft) +#error "Macro definition of topLeft conflicts with QRect" +// don't just silently undo people's defines: #undef topLeft +#endif + +class Q_EXPORT QRect // rectangle class +{ +public: + QRect() { x1 = y1 = 0; x2 = y2 = -1; } + QRect( const QPoint &topleft, const QPoint &bottomright ); + QRect( const QPoint &topleft, const QSize &size ); + QRect( int left, int top, int width, int height ); + + bool isNull() const; + bool isEmpty() const; + bool isValid() const; + QRect normalize() const; + + int left() const; + int top() const; + int right() const; + int bottom() const; + + QCOORD &rLeft(); + QCOORD &rTop(); + QCOORD &rRight(); + QCOORD &rBottom(); + + int x() const; + int y() const; + void setLeft( int pos ); + void setTop( int pos ); + void setRight( int pos ); + void setBottom( int pos ); + void setX( int x ); + void setY( int y ); + + void setTopLeft( const QPoint &p ); + void setBottomRight( const QPoint &p ); + void setTopRight( const QPoint &p ); + void setBottomLeft( const QPoint &p ); + + QPoint topLeft() const; + QPoint bottomRight() const; + QPoint topRight() const; + QPoint bottomLeft() const; + QPoint center() const; + + void rect( int *x, int *y, int *w, int *h ) const; + void coords( int *x1, int *y1, int *x2, int *y2 ) const; + + void moveLeft( int pos ); + void moveTop( int pos ); + void moveRight( int pos ); + void moveBottom( int pos ); + void moveTopLeft( const QPoint &p ); + void moveBottomRight( const QPoint &p ); + void moveTopRight( const QPoint &p ); + void moveBottomLeft( const QPoint &p ); + void moveCenter( const QPoint &p ); + void moveBy( int dx, int dy ); + + void setRect( int x, int y, int w, int h ); + void setCoords( int x1, int y1, int x2, int y2 ); + void addCoords( int x1, int y1, int x2, int y2 ); + + QSize size() const; + int width() const; + int height() const; + void setWidth( int w ); + void setHeight( int h ); + void setSize( const QSize &s ); + + QRect operator|(const QRect &r) const; + QRect operator&(const QRect &r) const; + QRect& operator|=(const QRect &r); + QRect& operator&=(const QRect &r); + + bool contains( const QPoint &p, bool proper=FALSE ) const; + bool contains( int x, int y ) const; // inline methods, _don't_ merge these + bool contains( int x, int y, bool proper ) const; + bool contains( const QRect &r, bool proper=FALSE ) const; + QRect unite( const QRect &r ) const; + QRect intersect( const QRect &r ) const; + bool intersects( const QRect &r ) const; + + friend Q_EXPORT bool operator==( const QRect &, const QRect & ); + friend Q_EXPORT bool operator!=( const QRect &, const QRect & ); + +private: +#if defined(Q_WS_X11) || defined(Q_OS_TEMP) + friend void qt_setCoords( QRect *r, int xp1, int yp1, int xp2, int yp2 ); +#endif +#if defined(Q_OS_MAC) + QCOORD y1; + QCOORD x1; + QCOORD y2; + QCOORD x2; +#else + QCOORD x1; + QCOORD y1; + QCOORD x2; + QCOORD y2; +#endif +}; + +Q_EXPORT bool operator==( const QRect &, const QRect & ); +Q_EXPORT bool operator!=( const QRect &, const QRect & ); + + +/***************************************************************************** + QRect stream functions + *****************************************************************************/ +#ifndef QT_NO_DATASTREAM +Q_EXPORT QDataStream &operator<<( QDataStream &, const QRect & ); +Q_EXPORT QDataStream &operator>>( QDataStream &, QRect & ); +#endif + +/***************************************************************************** + QRect inline member functions + *****************************************************************************/ + +inline QRect::QRect( int left, int top, int width, int height ) +{ + x1 = (QCOORD)left; + y1 = (QCOORD)top; + x2 = (QCOORD)(left+width-1); + y2 = (QCOORD)(top+height-1); +} + +inline bool QRect::isNull() const +{ return x2 == x1-1 && y2 == y1-1; } + +inline bool QRect::isEmpty() const +{ return x1 > x2 || y1 > y2; } + +inline bool QRect::isValid() const +{ return x1 <= x2 && y1 <= y2; } + +inline int QRect::left() const +{ return x1; } + +inline int QRect::top() const +{ return y1; } + +inline int QRect::right() const +{ return x2; } + +inline int QRect::bottom() const +{ return y2; } + +inline QCOORD &QRect::rLeft() +{ return x1; } + +inline QCOORD & QRect::rTop() +{ return y1; } + +inline QCOORD & QRect::rRight() +{ return x2; } + +inline QCOORD & QRect::rBottom() +{ return y2; } + +inline int QRect::x() const +{ return x1; } + +inline int QRect::y() const +{ return y1; } + +inline void QRect::setLeft( int pos ) +{ x1 = (QCOORD)pos; } + +inline void QRect::setTop( int pos ) +{ y1 = (QCOORD)pos; } + +inline void QRect::setRight( int pos ) +{ x2 = (QCOORD)pos; } + +inline void QRect::setBottom( int pos ) +{ y2 = (QCOORD)pos; } + +inline void QRect::setX( int x ) +{ x1 = (QCOORD)x; } + +inline void QRect::setY( int y ) +{ y1 = (QCOORD)y; } + +inline QPoint QRect::topLeft() const +{ return QPoint(x1, y1); } + +inline QPoint QRect::bottomRight() const +{ return QPoint(x2, y2); } + +inline QPoint QRect::topRight() const +{ return QPoint(x2, y1); } + +inline QPoint QRect::bottomLeft() const +{ return QPoint(x1, y2); } + +inline QPoint QRect::center() const +{ return QPoint((x1+x2)/2, (y1+y2)/2); } + +inline int QRect::width() const +{ return x2 - x1 + 1; } + +inline int QRect::height() const +{ return y2 - y1 + 1; } + +inline QSize QRect::size() const +{ return QSize(x2-x1+1, y2-y1+1); } + +inline bool QRect::contains( int x, int y, bool proper ) const +{ + if ( proper ) + return x > x1 && x < x2 && + y > y1 && y < y2; + else + return x >= x1 && x <= x2 && + y >= y1 && y <= y2; +} + +inline bool QRect::contains( int x, int y ) const +{ + return x >= x1 && x <= x2 && + y >= y1 && y <= y2; +} +#define Q_DEFINED_QRECT +#endif // QRECT_H diff --git a/main/lib/image.effect/qsize.cpp b/main/lib/image.effect/qsize.cpp new file mode 100644 index 00000000..05d447ff --- /dev/null +++ b/main/lib/image.effect/qsize.cpp @@ -0,0 +1,452 @@ +/*************************************************************************** + + qsize.cpp + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/**************************************************************************** +** $Id: qt/qsize.cpp 3.3.4 edited Oct 23 2003 $ +** +** Implementation of QSize class +** +** Created : 931028 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include "qsize.h" +//#include "qdatastream.h" + + +/*! + \class QSize + \brief The QSize class defines the size of a two-dimensional object. + + \ingroup images + \ingroup graphics + + A size is specified by a width and a height. + + The coordinate type is QCOORD (defined in \c as \c int). + The minimum value of QCOORD is QCOORD_MIN (-2147483648) and the maximum + value is QCOORD_MAX (2147483647). + + The size can be set in the constructor and changed with setWidth() + and setHeight(), or using operator+=(), operator-=(), operator*=() + and operator/=(), etc. You can swap the width and height with + transpose(). You can get a size which holds the maximum height and + width of two sizes using expandedTo(), and the minimum height and + width of two sizes using boundedTo(). + + + \sa QPoint, QRect +*/ + + +/***************************************************************************** + QSize member functions + *****************************************************************************/ + +/*! + \fn QSize::QSize() + Constructs a size with invalid (negative) width and height. +*/ + +/*! + \fn QSize::QSize( int w, int h ) + Constructs a size with width \a w and height \a h. +*/ + +/*! + \fn bool QSize::isNull() const + Returns TRUE if the width is 0 and the height is 0; otherwise + returns FALSE. +*/ + +/*! + \fn bool QSize::isEmpty() const + Returns TRUE if the width is less than or equal to 0, or the height is + less than or equal to 0; otherwise returns FALSE. +*/ + +/*! + \fn bool QSize::isValid() const + Returns TRUE if the width is equal to or greater than 0 and the height is + equal to or greater than 0; otherwise returns FALSE. +*/ + +/*! + \fn int QSize::width() const + Returns the width. + \sa height() +*/ + +/*! + \fn int QSize::height() const + Returns the height. + \sa width() +*/ + +/*! + \fn void QSize::setWidth( int w ) + Sets the width to \a w. + \sa width(), setHeight() +*/ + +/*! + \fn void QSize::setHeight( int h ) + Sets the height to \a h. + \sa height(), setWidth() +*/ + +/*! + Swaps the values of width and height. +*/ + +void QSize::transpose() +{ + QCOORD tmp = wd; + wd = ht; + ht = tmp; +} + +/*! \enum QSize::ScaleMode + + This enum type defines the different ways of scaling a size. + + \img scaling.png + + \value ScaleFree The size is scaled freely. The ratio is not preserved. + \value ScaleMin The size is scaled to a rectangle as large as possible + inside a given rectangle, preserving the aspect ratio. + \value ScaleMax The size is scaled to a rectangle as small as possible + outside a given rectangle, preserving the aspect ratio. + + \sa QSize::scale(), QImage::scale(), QImage::smoothScale() +*/ + +/*! + Scales the size to a rectangle of width \a w and height \a h according + to the ScaleMode \a mode. + + \list + \i If \a mode is \c ScaleFree, the size is set to (\a w, \a h). + \i If \a mode is \c ScaleMin, the current size is scaled to a rectangle + as large as possible inside (\a w, \a h), preserving the aspect ratio. + \i If \a mode is \c ScaleMax, the current size is scaled to a rectangle + as small as possible outside (\a w, \a h), preserving the aspect ratio. + \endlist + + Example: + \code + QSize t1( 10, 12 ); + t1.scale( 60, 60, QSize::ScaleFree ); + // t1 is (60, 60) + + QSize t2( 10, 12 ); + t2.scale( 60, 60, QSize::ScaleMin ); + // t2 is (50, 60) + + QSize t3( 10, 12 ); + t3.scale( 60, 60, QSize::ScaleMax ); + // t3 is (60, 72) + \endcode +*/ +void QSize::scale( int w, int h, ScaleMode mode ) +{ + if ( mode == ScaleFree ) { + wd = (QCOORD)w; + ht = (QCOORD)h; + } else { + bool useHeight = TRUE; + int w0 = width(); + int h0 = height(); + int rw = h * w0 / h0; + + if ( mode == ScaleMin ) { + useHeight = ( rw <= w ); + } else { // mode == ScaleMax + useHeight = ( rw >= w ); + } + + if ( useHeight ) { + wd = (QCOORD)rw; + ht = (QCOORD)h; + } else { + wd = (QCOORD)w; + ht = (QCOORD)( w * h0 / w0 ); + } + } +} + +/*! + \overload + + Equivalent to scale(\a{s}.width(), \a{s}.height(), \a mode). +*/ +void QSize::scale( const QSize &s, ScaleMode mode ) +{ + scale( s.width(), s.height(), mode ); +} + +/*! + \fn QCOORD &QSize::rwidth() + Returns a reference to the width. + + Using a reference makes it possible to directly manipulate the width. + + Example: + \code + QSize s( 100, 10 ); + s.rwidth() += 20; // s becomes (120,10) + \endcode + + \sa rheight() +*/ + +/*! + \fn QCOORD &QSize::rheight() + Returns a reference to the height. + + Using a reference makes it possible to directly manipulate the height. + + Example: + \code + QSize s( 100, 10 ); + s.rheight() += 5; // s becomes (100,15) + \endcode + + \sa rwidth() +*/ + +/*! + \fn QSize &QSize::operator+=( const QSize &s ) + + Adds \a s to the size and returns a reference to this size. + + Example: + \code + QSize s( 3, 7 ); + QSize r( -1, 4 ); + s += r; // s becomes (2,11) +\endcode +*/ + +/*! + \fn QSize &QSize::operator-=( const QSize &s ) + + Subtracts \a s from the size and returns a reference to this size. + + Example: + \code + QSize s( 3, 7 ); + QSize r( -1, 4 ); + s -= r; // s becomes (4,3) + \endcode +*/ + +/*! + \fn QSize &QSize::operator*=( int c ) + Multiplies both the width and height by \a c and returns a reference to + the size. +*/ + +/*! + \overload QSize &QSize::operator*=( double c ) + + Multiplies both the width and height by \a c and returns a reference to + the size. + + Note that the result is truncated. +*/ + +/*! + \fn bool operator==( const QSize &s1, const QSize &s2 ) + \relates QSize + Returns TRUE if \a s1 and \a s2 are equal; otherwise returns FALSE. +*/ + +/*! + \fn bool operator!=( const QSize &s1, const QSize &s2 ) + \relates QSize + Returns TRUE if \a s1 and \a s2 are different; otherwise returns FALSE. +*/ + +/*! + \fn const QSize operator+( const QSize &s1, const QSize &s2 ) + \relates QSize + Returns the sum of \a s1 and \a s2; each component is added separately. +*/ + +/*! + \fn const QSize operator-( const QSize &s1, const QSize &s2 ) + \relates QSize + Returns \a s2 subtracted from \a s1; each component is + subtracted separately. +*/ + +/*! + \fn const QSize operator*( const QSize &s, int c ) + \relates QSize + Multiplies \a s by \a c and returns the result. +*/ + +/*! + \overload const QSize operator*( int c, const QSize &s ) + \relates QSize + Multiplies \a s by \a c and returns the result. +*/ + +/*! + \overload const QSize operator*( const QSize &s, double c ) + \relates QSize + Multiplies \a s by \a c and returns the result. +*/ + +/*! + \overload const QSize operator*( double c, const QSize &s ) + \relates QSize + Multiplies \a s by \a c and returns the result. +*/ + +/*! + \fn QSize &QSize::operator/=( int c ) + Divides both the width and height by \a c and returns a reference to the + size. +*/ + +/*! + \fn QSize &QSize::operator/=( double c ) + \overload + Divides both the width and height by \a c and returns a reference to the + size. + + Note that the result is truncated. +*/ + +/*! + \fn const QSize operator/( const QSize &s, int c ) + \relates QSize + Divides \a s by \a c and returns the result. +*/ + +/*! + \fn const QSize operator/( const QSize &s, double c ) + \relates QSize + \overload + Divides \a s by \a c and returns the result. + + Note that the result is truncated. +*/ + +/*! + \fn QSize QSize::expandedTo( const QSize & otherSize ) const + + Returns a size with the maximum width and height of this size and + \a otherSize. +*/ + +/*! + \fn QSize QSize::boundedTo( const QSize & otherSize ) const + + Returns a size with the minimum width and height of this size and + \a otherSize. +*/ + + +void QSize::warningDivByZero() +{ +#if defined(QT_CHECK_MATH) + qWarning( "QSize: Division by zero error" ); +#endif +} + + +/***************************************************************************** + QSize stream functions + *****************************************************************************/ +#ifndef QT_NO_DATASTREAM +/*! + \relates QSize + Writes the size \a sz to the stream \a s and returns a reference to + the stream. + + \sa \link datastreamformat.html Format of the QDataStream operators \endlink +*/ + +QDataStream &operator<<( QDataStream &s, const QSize &sz ) +{ + if ( s.version() == 1 ) + s << (Q_INT16)sz.width() << (Q_INT16)sz.height(); + else + s << (Q_INT32)sz.width() << (Q_INT32)sz.height(); + return s; +} + +/*! + \relates QSize + Reads the size from the stream \a s into size \a sz and returns a + reference to the stream. + + \sa \link datastreamformat.html Format of the QDataStream operators \endlink +*/ + +QDataStream &operator>>( QDataStream &s, QSize &sz ) +{ + if ( s.version() == 1 ) { + Q_INT16 w, h; + s >> w; sz.rwidth() = w; + s >> h; sz.rheight() = h; + } + else { + Q_INT32 w, h; + s >> w; sz.rwidth() = w; + s >> h; sz.rheight() = h; + } + return s; +} +#endif // QT_NO_DATASTREAM diff --git a/main/lib/image.effect/qsize.h b/main/lib/image.effect/qsize.h new file mode 100644 index 00000000..7142e12c --- /dev/null +++ b/main/lib/image.effect/qsize.h @@ -0,0 +1,256 @@ +/*************************************************************************** + + qsize.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +/**************************************************************************** +** $Id: qt/qsize.h 3.3.4 edited May 27 2003 $ +** +** Definition of QSize class +** +** Created : 931028 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QSIZE_H +#define QSIZE_H + +#include "qt.h" +#include "qpoint.h" + +class Q_EXPORT QSize +// ### Make QSize inherit Qt in Qt 4.0 +{ +public: + // ### Move this enum to qnamespace.h in Qt 4.0 + enum ScaleMode { + ScaleFree, + ScaleMin, + ScaleMax + }; + + QSize(); + QSize( int w, int h ); + + bool isNull() const; + bool isEmpty() const; + bool isValid() const; + + int width() const; + int height() const; + void setWidth( int w ); + void setHeight( int h ); + void transpose(); + + void scale( int w, int h, ScaleMode mode ); + void scale( const QSize &s, ScaleMode mode ); + + QSize expandedTo( const QSize & ) const; + QSize boundedTo( const QSize & ) const; + + QCOORD &rwidth(); + QCOORD &rheight(); + + QSize &operator+=( const QSize & ); + QSize &operator-=( const QSize & ); + QSize &operator*=( int c ); + QSize &operator*=( double c ); + QSize &operator/=( int c ); + QSize &operator/=( double c ); + + friend inline bool operator==( const QSize &, const QSize & ); + friend inline bool operator!=( const QSize &, const QSize & ); + friend inline const QSize operator+( const QSize &, const QSize & ); + friend inline const QSize operator-( const QSize &, const QSize & ); + friend inline const QSize operator*( const QSize &, int ); + friend inline const QSize operator*( int, const QSize & ); + friend inline const QSize operator*( const QSize &, double ); + friend inline const QSize operator*( double, const QSize & ); + friend inline const QSize operator/( const QSize &, int ); + friend inline const QSize operator/( const QSize &, double ); + +private: + static void warningDivByZero(); + + QCOORD wd; + QCOORD ht; +}; + + +/***************************************************************************** + QSize stream functions + *****************************************************************************/ +#ifndef QT_NO_DATASTREAM +Q_EXPORT QDataStream &operator<<( QDataStream &, const QSize & ); +Q_EXPORT QDataStream &operator>>( QDataStream &, QSize & ); +#endif + +/***************************************************************************** + QSize inline functions + *****************************************************************************/ + +inline QSize::QSize() +{ wd = ht = -1; } + +inline QSize::QSize( int w, int h ) +{ wd=(QCOORD)w; ht=(QCOORD)h; } + +inline bool QSize::isNull() const +{ return wd==0 && ht==0; } + +inline bool QSize::isEmpty() const +{ return wd<1 || ht<1; } + +inline bool QSize::isValid() const +{ return wd>=0 && ht>=0; } + +inline int QSize::width() const +{ return wd; } + +inline int QSize::height() const +{ return ht; } + +inline void QSize::setWidth( int w ) +{ wd=(QCOORD)w; } + +inline void QSize::setHeight( int h ) +{ ht=(QCOORD)h; } + +inline QCOORD &QSize::rwidth() +{ return wd; } + +inline QCOORD &QSize::rheight() +{ return ht; } + +inline QSize &QSize::operator+=( const QSize &s ) +{ wd+=s.wd; ht+=s.ht; return *this; } + +inline QSize &QSize::operator-=( const QSize &s ) +{ wd-=s.wd; ht-=s.ht; return *this; } + +inline QSize &QSize::operator*=( int c ) +{ wd*=(QCOORD)c; ht*=(QCOORD)c; return *this; } + +inline QSize &QSize::operator*=( double c ) +{ wd=(QCOORD)(wd*c); ht=(QCOORD)(ht*c); return *this; } + +inline bool operator==( const QSize &s1, const QSize &s2 ) +{ return s1.wd == s2.wd && s1.ht == s2.ht; } + +inline bool operator!=( const QSize &s1, const QSize &s2 ) +{ return s1.wd != s2.wd || s1.ht != s2.ht; } + +inline const QSize operator+( const QSize & s1, const QSize & s2 ) +{ return QSize(s1.wd+s2.wd, s1.ht+s2.ht); } + +inline const QSize operator-( const QSize &s1, const QSize &s2 ) +{ return QSize(s1.wd-s2.wd, s1.ht-s2.ht); } + +inline const QSize operator*( const QSize &s, int c ) +{ return QSize(s.wd*c, s.ht*c); } + +inline const QSize operator*( int c, const QSize &s ) +{ return QSize(s.wd*c, s.ht*c); } + +inline const QSize operator*( const QSize &s, double c ) +{ return QSize((QCOORD)(s.wd*c), (QCOORD)(s.ht*c)); } + +inline const QSize operator*( double c, const QSize &s ) +{ return QSize((QCOORD)(s.wd*c), (QCOORD)(s.ht*c)); } + +inline QSize &QSize::operator/=( int c ) +{ +#if defined(QT_CHECK_MATH) + if ( c == 0 ) + warningDivByZero(); +#endif + wd/=(QCOORD)c; ht/=(QCOORD)c; + return *this; +} + +inline QSize &QSize::operator/=( double c ) +{ +#if defined(QT_CHECK_MATH) + if ( c == 0.0 ) + warningDivByZero(); +#endif + wd=(QCOORD)(wd/c); ht=(QCOORD)(ht/c); + return *this; +} + +inline const QSize operator/( const QSize &s, int c ) +{ +#if defined(QT_CHECK_MATH) + if ( c == 0 ) + QSize::warningDivByZero(); +#endif + return QSize(s.wd/c, s.ht/c); +} + +inline const QSize operator/( const QSize &s, double c ) +{ +#if defined(QT_CHECK_MATH) + if ( c == 0.0 ) + QSize::warningDivByZero(); +#endif + return QSize((QCOORD)(s.wd/c), (QCOORD)(s.ht/c)); +} + +inline QSize QSize::expandedTo( const QSize & otherSize ) const +{ + return QSize( QMAX(wd,otherSize.wd), QMAX(ht,otherSize.ht) ); +} + +inline QSize QSize::boundedTo( const QSize & otherSize ) const +{ + return QSize( QMIN(wd,otherSize.wd), QMIN(ht,otherSize.ht) ); +} + + +#endif // QSIZE_H diff --git a/main/lib/image.effect/qt.h b/main/lib/image.effect/qt.h new file mode 100644 index 00000000..ade1f7e0 --- /dev/null +++ b/main/lib/image.effect/qt.h @@ -0,0 +1,57 @@ +/*************************************************************************** + + qt.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __QT_H +#define __QT_H + +#include "gb_common.h" + +#define Q_EXPORT +#define QT_NO_DATASTREAM +#define QT_NO_STRINGLIST +#define Q_WS_X11 + +#define QT_STATIC_CONST static const +#define QT_STATIC_CONST_IMPL const + +#define QMAX(a, b) ((b) < (a) ? (a) : (b)) +#define QMIN(a, b) ((a) < (b) ? (a) : (b)) +#define QABS(a) ((a) >= 0 ? (a) : -(a)) + +typedef + unsigned short Q_UINT16; + +typedef + int QCOORD; + +typedef + unsigned int QRgb; + +#define QImage MyQImage +#define QPoint MyQPoint +#define QSize MyQSize +#define QRect MyQRect +#define KImageEffect MyKImageEffect +#define QColor MyQColor + +#endif diff --git a/main/lib/image/CImage.c b/main/lib/image/CImage.c new file mode 100644 index 00000000..7c47c01f --- /dev/null +++ b/main/lib/image/CImage.c @@ -0,0 +1,598 @@ +/*************************************************************************** + + CImage.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CIMAGE_C + +#include "main.h" +#include "image.h" +#include "CImage.h" + +static int _balance = 0; + +static double _brightness = 0; +static double _contrast = 0; +static double _gamma = 0; +static double _hue = 0; +static double _saturation = 0; +static double _lightness = 0; + +BEGIN_METHOD(Image_new, GB_INTEGER w; GB_INTEGER h; GB_INTEGER col; GB_INTEGER format) + + int format = IMAGE_get_default_format(); + + if (VARGOPT(format, 0) == 1) + format = GB_IMAGE_FMT_SET_PREMULTIPLIED(format); + + IMAGE_create(THIS_IMAGE, VARGOPT(w, 0), VARGOPT(h, 0), format); + + if (!MISSING(col)) + IMAGE_fill(THIS_IMAGE, VARG(col)); + +END_METHOD + +BEGIN_METHOD_VOID(Image_free) + + IMAGE_delete(THIS_IMAGE); + +END_METHOD + +BEGIN_PROPERTY(Image_Width) + + GB.ReturnInteger(THIS_IMAGE->width); + +END_PROPERTY + +BEGIN_PROPERTY(Image_Height) + + GB.ReturnInteger(THIS_IMAGE->height); + +END_PROPERTY + +BEGIN_PROPERTY(Image_Depth) + + GB.ReturnInteger(32); + +END_PROPERTY + +BEGIN_METHOD(Image_Fill, GB_INTEGER col) + + IMAGE_fill(THIS_IMAGE, VARG(col)); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_FillRect, GB_INTEGER x; GB_INTEGER y; GB_INTEGER width; GB_INTEGER height; GB_INTEGER col) + + IMAGE_fill_rect(THIS_IMAGE, VARG(x), VARG(y), VARG(width), VARG(height), VARG(col), TRUE); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_PaintRect, GB_INTEGER x; GB_INTEGER y; GB_INTEGER width; GB_INTEGER height; GB_INTEGER col) + + IMAGE_fill_rect(THIS_IMAGE, VARG(x), VARG(y), VARG(width), VARG(height), VARG(col), FALSE); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_get, GB_INTEGER x; GB_INTEGER y) + + GB.ReturnInteger(IMAGE_get_pixel(THIS_IMAGE, VARG(x), VARG(y))); + +END_METHOD + +BEGIN_METHOD(Image_put, GB_INTEGER col; GB_INTEGER x; GB_INTEGER y) + + IMAGE_set_pixel(THIS_IMAGE, VARG(x), VARG(y), VARG(col)); + +END_METHOD + +BEGIN_PROPERTY(Image_Data) + + GB.ReturnPointer((void *)THIS_IMAGE->data); + +END_PROPERTY + +BEGIN_METHOD_VOID(Image_Desaturate) + + IMAGE_make_gray(THIS_IMAGE); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Image_Gray) + + fprintf(stderr, "warning: Image.Gray is deprecated, use Image.Desaturate instead.\n"); + Image_Desaturate(_object, _param); + +END_METHOD + +BEGIN_METHOD_VOID(Image_Clear) + + //IMAGE_fill(THIS_IMAGE, 0XFF000000); + IMAGE_delete(THIS_IMAGE); + +END_METHOD + +BEGIN_METHOD(Image_Replace, GB_INTEGER src; GB_INTEGER dst; GB_BOOLEAN noteq) + + IMAGE_replace(THIS_IMAGE, VARG(src), VARG(dst), VARGOPT(noteq, FALSE)); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Erase, GB_INTEGER color) + + IMAGE_make_transparent(THIS_IMAGE, VARGOPT(color, 0xFFFFFF)); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Image_Transparent) + + fprintf(stderr, "warning: Image.Transparent is deprecated, use Image.Erase instead.\n"); + Image_Erase(_object, _param); + +END_METHOD + +BEGIN_METHOD(Image_Colorize, GB_INTEGER color) + + IMAGE_colorize(THIS_IMAGE, VARG(color)); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Mask, GB_INTEGER color) + + IMAGE_mask(THIS_IMAGE, VARG(color)); + GB.ReturnObject(THIS); + +END_METHOD + +// Parameter correction +#define CHECK_PARAMETERS(dst, dx, dy, dw, dh, src, sx, sy, sw, sh) \ + if ( sw < 0 ) sw = src->width; \ + if ( sh < 0 ) sh = src->height; \ + if (dw < 0) dw = sw; \ + if (dh < 0) dh = sh; \ + if (dw != sw || dh != sh) \ + { \ + GB.Error("Stretching images is not implemented in gb.image"); \ + return; \ + } \ + if ( sx < 0 ) { dx -= sx; dw += sx; sw += sx; sx = 0; } \ + if ( sy < 0 ) { dy -= sy; dh += sy; sh += sy; sy = 0; } \ + if ( dx < 0 ) { sx -= dx; sw += dx; dx = 0; } \ + if ( dy < 0 ) { sy -= dy; sh += dy; dy = 0; } \ + if ( (sx + sw) > src->width ) sw = src->width - sx; \ + if ( (sy + sh) > src->height ) sh = src->height - sy; \ + if ( (dx + sw) > dst->width ) sw = dst->width - dx; \ + if ( (dy + sh) > dst->height ) sh = dst->height - dy; \ + if (sw <= 0 || sh <= 0) \ + return; + +BEGIN_METHOD(Image_Copy, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) + + CIMAGE *image; + int x = VARGOPT(x, 0); + int y = VARGOPT(y, 0); + int w = VARGOPT(w, THIS_IMAGE->width); + int h = VARGOPT(h, THIS_IMAGE->height); + + if (x < 0) { w += x; x = 0; } + if (y < 0) { h += y; y = 0; } + if ((x + w) > THIS_IMAGE->width) { w = THIS_IMAGE->width - x; } + if ((y + h) > THIS_IMAGE->height) { h = THIS_IMAGE->height - y; } + + image = GB.New(GB.FindClass("Image"), NULL, NULL); + + IMAGE_create(&image->image, w, h, THIS_IMAGE->format); + if (w > 0 && h > 0) + IMAGE_bitblt(&image->image, 0, 0, -1, -1, THIS_IMAGE, x, y, w, h); + + GB.ReturnObject(image); + +END_METHOD + +BEGIN_METHOD(Image_Resize, GB_INTEGER width; GB_INTEGER height) + + GB_IMG tmp; + int w = VARG(width); + int h = VARG(height); + + if (w < 0) w = THIS_IMAGE->width; + if (h < 0) h = THIS_IMAGE->height; + + //IMAGE_convert(THIS_IMAGE, IMAGE_get_default_format()); + //fprintf(stderr, "format = %d\n", THIS_IMAGE->format); + tmp.ob = THIS_IMAGE->ob; + IMAGE_create(&tmp, w, h, THIS_IMAGE->format); + IMAGE_bitblt(&tmp, 0, 0, -1, -1, THIS_IMAGE, 0, 0, w, h); + + IMAGE_delete(THIS_IMAGE); + *THIS_IMAGE = tmp; + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Mirror, GB_BOOLEAN horz; GB_BOOLEAN vert) + + GB_IMG tmp; + + tmp.ob = THIS_IMAGE->ob; + IMAGE_create(&tmp, THIS_IMAGE->width, THIS_IMAGE->height, THIS_IMAGE->format); + IMAGE_mirror(THIS_IMAGE, &tmp, VARG(horz), VARG(vert)); + IMAGE_delete(THIS_IMAGE); + *THIS_IMAGE = tmp; + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Image_RotateLeft) + + GB_IMG tmp; + + tmp.ob = THIS_IMAGE->ob; + IMAGE_create(&tmp, THIS_IMAGE->height, THIS_IMAGE->width, THIS_IMAGE->format); + IMAGE_rotate(THIS_IMAGE, &tmp, TRUE); + IMAGE_delete(THIS_IMAGE); + *THIS_IMAGE = tmp; + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Image_RotateRight) + + GB_IMG tmp; + + tmp.ob = THIS_IMAGE->ob; + IMAGE_create(&tmp, THIS_IMAGE->height, THIS_IMAGE->width, THIS_IMAGE->format); + IMAGE_rotate(THIS_IMAGE, &tmp, FALSE); + IMAGE_delete(THIS_IMAGE); + *THIS_IMAGE = tmp; + GB.ReturnObject(THIS); + +END_METHOD + +#if 0 +BEGIN_METHOD(CIMAGE_transform, GB_FLOAT sx; GB_FLOAT sy; GB_FLOAT dx; GB_FLOAT dy) + + CIMAGE *image; + int w, h; + double dx = VARG(dx); + double dy = VARG(dy); + double f, s; + + GB.New(POINTER(&image), GB.FindClass("Image"), NULL, NULL); + GB.ReturnObject(image); + + f = fabs(dx); + if (f > fabs(dy)) + f = fabs(dy); + + s = 1 / sqrt(dx * dx + dy * dy); + + w = (int)(THIS_IMAGE->width * (1.0 + f) * s + 0.5); + h = (int)(THIS_IMAGE->height * (1.0 + f) * s + 0.5); + + if (!w || !h) + return; + + IMAGE_create(&image->image, w, h, THIS_IMAGE->format); + IMAGE_transform(&image->image, THIS_IMAGE, VARG(sx), VARG(sy), VARG(dx), VARG(dy)); + +END_METHOD +#endif + +BEGIN_METHOD(Image_DrawAlpha, GB_OBJECT image; GB_INTEGER x; GB_INTEGER y; GB_INTEGER srcx; GB_INTEGER srcy; GB_INTEGER srcw; GB_INTEGER srch) + + CIMAGE *image = VARG(image); + + if (GB.CheckObject(image)) + return; + + IMAGE_draw_alpha(THIS_IMAGE, VARGOPT(x, 0), VARGOPT(y, 0), &image->image, VARGOPT(srcx, 0), VARGOPT(srcy, 0), VARGOPT(srcw, -1), VARGOPT(srch, -1)); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_DrawImage, GB_OBJECT image; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER srcx; GB_INTEGER srcy; GB_INTEGER srcw; GB_INTEGER srch) + + CIMAGE *image = VARG(image); + + if (GB.CheckObject(image)) + return; + + IMAGE_bitblt(THIS_IMAGE, VARGOPT(x, 0), VARGOPT(y, 0), VARGOPT(w, -1), VARGOPT(h, -1), &image->image, VARGOPT(srcx, 0), VARGOPT(srcy, 0), VARGOPT(srcw, -1), VARGOPT(srch, -1)); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_PaintImage, GB_OBJECT image; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h; GB_INTEGER srcx; GB_INTEGER srcy; GB_INTEGER srcw; GB_INTEGER srch) + + CIMAGE *image = VARG(image); + + if (GB.CheckObject(image)) + return; + + IMAGE_compose(THIS_IMAGE, VARGOPT(x, 0), VARGOPT(y, 0), VARGOPT(w, -1), VARGOPT(h, -1), &image->image, VARGOPT(srcx, 0), VARGOPT(srcy, 0), VARGOPT(srcw, -1), VARGOPT(srch, -1)); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_PROPERTY(Image_Format) + + int fmt; + + if (READ_PROPERTY) + { + GB.ReturnConstZeroString(IMAGE_format_to_string(THIS_IMAGE->format)); + } + else + { + fmt = IMAGE_format_from_string(GB.ToZeroString(PROP(GB_STRING))); + + if (fmt < 0) + { + GB.Error("Unknown format"); + return; + } + + IMAGE_convert(THIS_IMAGE, fmt); + } + +END_PROPERTY + +BEGIN_PROPERTY(Image_Debug) + + if (READ_PROPERTY) + GB.ReturnBoolean(IMAGE_debug); + else + IMAGE_debug = VPROP(GB_BOOLEAN); + +END_PROPERTY + +BEGIN_METHOD(Image_Opacity, GB_FLOAT opacity) + + int alpha = VARG(opacity) * 255; + + if (alpha < 0) + alpha = 0; + else if (alpha > 255) + alpha = 255; + + IMAGE_set_opacity(THIS_IMAGE, (uchar)alpha); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Invert, GB_BOOLEAN keep_color) + + IMAGE_invert(THIS_IMAGE, VARGOPT(keep_color, FALSE)); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Blur, GB_INTEGER radius) + + IMAGE_blur(THIS_IMAGE, VARGOPT(radius, 8)); + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_PROPERTY(Image_Pixels) + + GB_ARRAY array; + int size; + + if (!GB_IMAGE_FMT_IS_32_BITS(THIS_IMAGE->format)) + { + GB.Error("Image format must be 32 bits"); + return; + } + + size = THIS_IMAGE->width * THIS_IMAGE->height; + + if (READ_PROPERTY) + { + GB.Array.New(&array, GB_T_INTEGER, size); + IMAGE_get_pixels(THIS_IMAGE, GB.Array.Get(array, 0)); + GB.ReturnObject(array); + } + else + { + array = VPROP(GB_OBJECT); + + if (GB.CheckObject(array)) + return; + + if (GB.Array.Count(array) < size) + { + GB.Error("Not enough pixels"); + return; + } + + IMAGE_set_pixels(THIS_IMAGE, GB.Array.Get(array, 0)); + } + +END_PROPERTY + +BEGIN_METHOD_VOID(Image_BeginBalance) + + _balance++; + GB.ReturnObject(THIS); + + +END_METHOD + +BEGIN_METHOD_VOID(Image_EndBalance) + + if (_balance <= 0) + { + GB.Error("Missing call to BeginBalance"); + return; + } + + _balance--; + + if (_balance == 0) + IMAGE_balance(THIS_IMAGE, _brightness * 255, _contrast * 255, _gamma * 255, _hue * 180, _saturation * 255, _lightness * 255); + + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Brightness, GB_FLOAT value) + + if (_balance) + _brightness = VARG(value); + else + IMAGE_balance(THIS_IMAGE, VARG(value) * 255, 0, 0, 0, 0, 0); + + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Contrast, GB_FLOAT value) + + if (_balance) + _contrast = VARG(value); + else + IMAGE_balance(THIS_IMAGE, 0, VARG(value) * 255, 0, 0, 0, 0); + + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Gamma, GB_FLOAT value) + + if (_balance) + _gamma = VARG(value); + else + IMAGE_balance(THIS_IMAGE, 0, 0, VARG(value) * 255, 0, 0, 0); + + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Hue, GB_FLOAT value) + + if (_balance) + _hue = VARG(value); + else + IMAGE_balance(THIS_IMAGE, 0, 0, 0, VARG(value) * 180, 0, 0); + + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Saturation, GB_FLOAT value) + + if (_balance) + _saturation = VARG(value); + else + IMAGE_balance(THIS_IMAGE, 0, 0, 0, 0, VARG(value) * 255, 0); + + GB.ReturnObject(THIS); + +END_METHOD + +BEGIN_METHOD(Image_Lightness, GB_FLOAT value) + + if (_balance) + _lightness = VARG(value); + else + IMAGE_balance(THIS_IMAGE, 0, 0, 0, 0, 0, VARG(value) * 255); + + GB.ReturnObject(THIS); + +END_METHOD + + + +//--------------------------------------------------------------------------- + +GB_DESC CImageDesc[] = +{ + GB_DECLARE("Image", sizeof(CIMAGE)), + + GB_CONSTANT("Standard", "i", 0), + GB_CONSTANT("Premultiplied", "i", 1), + + GB_STATIC_PROPERTY("Debug", "b", Image_Debug), + + GB_METHOD("_new", NULL, Image_new, "[(Width)i(Height)i(Color)i(Format)i]"), + GB_METHOD("_free", NULL, Image_free, NULL), + + GB_METHOD("_get", "i", Image_get, "(X)i(Y)i"), + GB_METHOD("_put", NULL, Image_put, "(Color)i(X)i(Y)i"), + + GB_PROPERTY_READ("Width", "i", Image_Width), + GB_PROPERTY_READ("Height", "i", Image_Height), + GB_PROPERTY_READ("W", "i", Image_Width), + GB_PROPERTY_READ("H", "i", Image_Height), + GB_PROPERTY_READ("Depth", "i", Image_Depth), + GB_PROPERTY_READ("Data", "p", Image_Data), + GB_PROPERTY("Format", "s", Image_Format), + GB_PROPERTY("Pixels", "Integer[]", Image_Pixels), + + GB_METHOD("Clear", NULL, Image_Clear, NULL), + GB_METHOD("Fill", "Image", Image_Fill, "(Color)i"), + GB_METHOD("Gray", "Image", Image_Gray, NULL), + GB_METHOD("Transparent", "Image", Image_Transparent, "[(Color)i]"), + GB_METHOD("Desaturate", "Image", Image_Desaturate, NULL), + GB_METHOD("Erase", "Image", Image_Erase, "[(Color)i]"), + GB_METHOD("Replace", "Image", Image_Replace, "(OldColor)i(NewColor)i[(NotEqual)b]"), + GB_METHOD("Colorize", "Image", Image_Colorize, "(Color)i"), + GB_METHOD("Mask", "Image", Image_Mask, "(Color)i"), + GB_METHOD("Opacity", "Image", Image_Opacity, "(Opacity)f"), + GB_METHOD("Invert", "Image", Image_Invert, "[(KeepColor)b]"), + + GB_METHOD("Copy", "Image", Image_Copy, "[(X)i(Y)i(Width)i(Height)i]"), + GB_METHOD("Resize", "Image", Image_Resize, "(Width)i(Height)i"), + + GB_METHOD("Mirror", "Image", Image_Mirror, "(Horizontal)b(Vertical)b"), + GB_METHOD("RotateLeft", "Image", Image_RotateLeft, NULL), + GB_METHOD("RotateRight", "Image", Image_RotateRight, NULL), + + GB_METHOD("FillRect", "Image", Image_FillRect, "(X)i(Y)i(Width)i(Height)i(Color)i"), + GB_METHOD("PaintRect", "Image", Image_PaintRect, "(X)i(Y)i(Width)i(Height)i(Color)i"), + GB_METHOD("DrawAlpha", "Image", Image_DrawAlpha, "(Image)Image;[(X)i(Y)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + + GB_METHOD("DrawImage", "Image", Image_DrawImage, "(Image)Image;[(X)i(Y)i(Width)i(Height)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + GB_METHOD("PaintImage", "Image", Image_PaintImage, "(Image)Image;[(X)i(Y)i(Width)i(Height)i(SrcX)i(SrcY)i(SrcWidth)i(SrcHeight)i]"), + + GB_METHOD("Fuzzy", "Image", Image_Blur, "[(Radius)i]"), + + GB_METHOD("BeginBalance", "Image", Image_BeginBalance, NULL), + GB_METHOD("Brightness", "Image", Image_Brightness, "(Brightness)f"), + GB_METHOD("Contrast", "Image", Image_Contrast, "(Contrast)f"), + GB_METHOD("Gamma", "Image", Image_Gamma, "(Gamma)f"), + GB_METHOD("Hue", "Image", Image_Hue, "(Hue)f"), + GB_METHOD("Saturation", "Image", Image_Saturation, "(Saturation)f"), + GB_METHOD("Lightness", "Image", Image_Lightness, "(Lightness)f"), + GB_METHOD("EndBalance", "Image", Image_EndBalance, NULL), + + GB_END_DECLARE +}; + diff --git a/main/lib/image/CImage.h b/main/lib/image/CImage.h new file mode 100644 index 00000000..6ee3d1cb --- /dev/null +++ b/main/lib/image/CImage.h @@ -0,0 +1,48 @@ +/*************************************************************************** + + CImage.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CIMAGE_H +#define __CIMAGE_H + +#include "gambas.h" +#include "gb.image.h" + +#ifndef __CIMAGE_C + +extern GB_DESC CImageDesc[]; + +#else + +#define THIS ((CIMAGE *)_object) +#define THIS_IMAGE (&THIS->image) + +#endif + +typedef + struct { + GB_IMG image; + } + CIMAGE; + +#endif + diff --git a/main/lib/image/CImageStat.c b/main/lib/image/CImageStat.c new file mode 100644 index 00000000..53fb9d9c --- /dev/null +++ b/main/lib/image/CImageStat.c @@ -0,0 +1,140 @@ +/*************************************************************************** + + CImageStat.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CIMAGESTAT_C + +#include +#include "main.h" +#include "image_stat.h" +#include "CImageStat.h" + +// static int my_mmap(const char *path, char **paddr, int *plen) +// { +// struct stat info; +// int fd; +// void *addr; +// size_t len; +// +// fd = open(path, O_RDONLY); +// if (fd < 0) +// return -1; +// +// if (fstat(fd, &info) < 0) +// return -1; +// +// len = info.st_size; +// addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); +// if (addr == MAP_FAILED) +// return -1; +// +// *paddr = addr; +// *plen = len; +// return fd; +// } + +BEGIN_METHOD(CIMAGESTAT_call, GB_STRING path) + + char *path = GB.FileName(STRING(path), LENGTH(path)); + IMAGE_STREAM stream; + IMAGE_INFO info = {0}; + CIMAGESTAT *stat; + + if (GB.LoadFile(path, strlen(path), &stream.addr, &stream.len)) + return; + + stream.pos = 0; + + if (IMAGE_get_info(&stream, &info)) + { + GB.Error("Unable to stat image: &1", IMAGE_error); + stat = NULL; + } + else + { + stat = GB.New(GB.FindClass("ImageStat"), NULL, NULL); + stat->path = GB.NewZeroString(path); + stat->type = info.type; + stat->width = info.width; + stat->height = info.height; + stat->depth = info.depth; + } + + GB.ReleaseFile(stream.addr, stream.len); + + GB.ReturnObject(stat); + +END_METHOD + +BEGIN_METHOD_VOID(CIMAGESTAT_free) + + GB.FreeString(&THIS->path); + +END_METHOD + +BEGIN_PROPERTY(CIMAGESTAT_path) + + GB.ReturnString(THIS->path); + +END_PROPERTY + +BEGIN_PROPERTY(CIMAGESTAT_type) + + GB.ReturnConstZeroString(THIS->type); + +END_PROPERTY + +BEGIN_PROPERTY(CIMAGESTAT_width) + + GB.ReturnInteger(THIS->width); + +END_PROPERTY + +BEGIN_PROPERTY(CIMAGESTAT_height) + + GB.ReturnInteger(THIS->height); + +END_PROPERTY + +BEGIN_PROPERTY(CIMAGESTAT_depth) + + GB.ReturnInteger(THIS->depth); + +END_PROPERTY + +GB_DESC CImageStatDesc[] = +{ + GB_DECLARE("ImageStat", sizeof(CIMAGESTAT)), + GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("_call", "ImageStat", CIMAGESTAT_call, "(Path)s"), + GB_METHOD("_free", NULL, CIMAGESTAT_free, NULL), + + GB_PROPERTY_READ("Path", "s", CIMAGESTAT_path), + GB_PROPERTY_READ("Type", "s", CIMAGESTAT_type), + GB_PROPERTY_READ("Width", "i", CIMAGESTAT_width), + GB_PROPERTY_READ("Height", "i", CIMAGESTAT_height), + GB_PROPERTY_READ("Depth", "i", CIMAGESTAT_depth), + + GB_END_DECLARE +}; + diff --git a/main/lib/image/CImageStat.h b/main/lib/image/CImageStat.h new file mode 100644 index 00000000..39e0bb4e --- /dev/null +++ b/main/lib/image/CImageStat.h @@ -0,0 +1,51 @@ +/*************************************************************************** + + CImageStat.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CIMAGESTAT_H +#define __CIMAGESTAT_H + +#include "gambas.h" + +#ifndef __CIMAGESTAT_C + +extern GB_DESC CImageStatDesc[]; + +#else + +#define THIS ((CIMAGESTAT *)_object) + +#endif + +typedef + struct { + GB_BASE ob; + char *path; + char *type; + int width; + int height; + int depth; + } + CIMAGESTAT; + +#endif + diff --git a/main/lib/image/Makefile.am b/main/lib/image/Makefile.am new file mode 100644 index 00000000..abc4c7e5 --- /dev/null +++ b/main/lib/image/Makefile.am @@ -0,0 +1,24 @@ +COMPONENT = gb.image +include $(top_srcdir)/component.am + +noinst_LTLIBRARIES = libimage.la +gblib_LTLIBRARIES = gb.image.la + +libimage_la_LIBADD = +libimage_la_LDFLAGS = -module @LD_FLAGS@ +libimage_la_CFLAGS = -I$(top_srcdir)/share $(AM_CFLAGS_OPT) + +libimage_la_SOURCES = \ + image.h image.c + +gb_image_la_LIBADD = libimage.la @MATH_LIB@ +gb_image_la_LDFLAGS = -module @LD_FLAGS@ +gb_image_la_CFLAGS = -I$(top_srcdir)/share $(AM_CFLAGS) + +gb_image_la_SOURCES = \ + image_stat.h image_stat.c \ + CImage.h CImage.c \ + CImageStat.h CImageStat.c \ + c_color.h c_color.c \ + gb.image.h \ + main.h main.c diff --git a/main/lib/image/c_color.c b/main/lib/image/c_color.c new file mode 100644 index 00000000..8b6d3c10 --- /dev/null +++ b/main/lib/image/c_color.c @@ -0,0 +1,677 @@ +/*************************************************************************** + + c_color.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __C_COLOR_C + +#include +#include "c_color.h" + +void gt_color_to_rgba(uint color, int *r, int *g, int *b, int *a) +{ + *b = color & 0xFF; + *g = (color >> 8) & 0xFF; + *r = (color >> 16) & 0xFF; + *a = (color >> 24) & 0xFF; +} + +static uint gt_rgba_to_color(int r, int g, int b, int a) +{ + return (uint)((uchar)b | ((uchar)g << 8) | ((uchar)r << 16) | ((uchar)a << 24)); +} + +void COLOR_rgb_to_hsv(int r, int g, int b, int *H, int *S, int *V) +{ + int v, x, f; + int i; + + x = r; + if (g < x) x = g; + if (b < x) x = b; + + v = r; + if (g > v) v = g; + if (b > v) v = b; + + if (v == x) + { + *H = -1; + *S = 0; + *V = v; + } + else + { + f = (r == x) ? g - b : ((g == x) ? b - r : r - g); + i = (r == x) ? 3 : ((g == x) ? 5 : 1); + *H = (int)((i - (double)f / (v - x)) * 60); + *S = ((v - x) * 255) / v; + *V = v; + if (*H == 360) *H = 0; + } +} + +static void gt_rgb_to_hsv_cached(int r, int g, int b, int *h, int *s, int *v) +{ + static int old_r = 0, old_g = 0, old_b = 0, old_h = -1, old_s = 0, old_v = 0; + + if (r == old_r && g == old_g && b == old_b) + { + *h = old_h; + *s = old_s; + *v = old_v; + return; + } + + COLOR_rgb_to_hsv(r, g, b, h, s, v); + + old_r = r; + old_g = g; + old_b = b; + old_h = *h; + old_s = *s; + old_v = *v; +} + +void COLOR_hsv_to_rgb(int h, int s, int v, int *R, int *G, int *B) +{ + double var_h; + int var_i; + int var_1, var_2, var_3; + int tmp_r, tmp_g, tmp_b; + + if (h < 0) + h = 360 - ((-h) % 360); + else + h = h % 360; + + /*H = ((double)h) / 360; + S = ((double)s) / 255; + V = ((double)v) / 255;*/ + + if (s == 0) + { + *R = v; + *G = v; + *B = v; + } + else + { + var_i = h / 60; + var_h = h % 60; //((double)h / 60) - var_i; + + //var_1 = V * ( 1 - S ); + var_1 = v * (255 - s) / 255; + + //var_2 = V * ( 1 - S * ( var_h - var_i ) ); + var_2 = v * (255 - s * var_h / 60) / 255; + + //var_3 = V * ( 1 - S * ( 1 - ( var_h - var_i ) ) ); + var_3 = v * (255 - s * (60 - var_h) / 60) / 255; + + switch (var_i) + { + case 0: + tmp_r = v; + tmp_g = var_3; + tmp_b = var_1; + break; + + case 1: + tmp_r = var_2; + tmp_g = v; + tmp_b = var_1; + break; + + case 2: + tmp_r = var_1; + tmp_g = v; + tmp_b = var_3; + break; + + case 3: + tmp_r = var_1; + tmp_g = var_2; + tmp_b = v; + break; + + case 4: + tmp_r = var_3; + tmp_g = var_1; + tmp_b = v; + break; + + default: + tmp_r = v; + tmp_g = var_1; + tmp_b = var_2; + break; + } + + *R = tmp_r; + *G = tmp_g; + *B = tmp_b; + + } +} + +static int get_luminance(CCOLOR *_object) +{ + return (int)(0.299 * THIS->r + 0.587 * THIS->g + 0.114 * THIS->b + 0.5); +} + + +int COLOR_get_luminance(GB_COLOR col) +{ + CCOLOR info; + gt_color_to_rgba(col, &info.r, &info.g, &info.b, &info.a); + return get_luminance(&info); +} + + +static void set_luminance(CCOLOR *_object, int l) +{ + int c; + + if (l <= 0) + { + THIS->r = 0; + THIS->g = 0; + THIS->b = 0; + return; + } + else if (l >= 255) + { + THIS->r = 255; + THIS->g = 255; + THIS->b = 255; + return; + } + + for(;;) + { + c = get_luminance(THIS); + if (c == l) + return; + if (c == (l + 1) || c == (l - 1)) + break; + THIS->r = MinMax(THIS->r + l - c, 0, 255); + THIS->g = MinMax(THIS->g + l - c, 0, 255); + THIS->b = MinMax(THIS->b + l - c, 0, 255); + } + + THIS->g = MinMax(THIS->g + ((l > c) ? 1 : -1), 0, 255); + c = get_luminance(THIS); + if (c == l) + return; + + THIS->r = MinMax(THIS->r + ((l > c) ? 1 : -1), 0, 255); + c = get_luminance(THIS); + if (c == l) + return; + + THIS->b = MinMax(THIS->b + ((l > c) ? 1 : -1), 0, 255); +} + + +GB_COLOR COLOR_set_luminance(GB_COLOR col, int l) +{ + CCOLOR info; + gt_color_to_rgba(col, &info.r, &info.g, &info.b, &info.a); + set_luminance(&info, l); + return gt_rgba_to_color(info.r, info.g, info.b, info.a); +} + + +GB_COLOR COLOR_merge(GB_COLOR col1, GB_COLOR col2, double weight) +{ + int r, g, b; + int h1, s1, v1, a1; + int h2, s2, v2, a2; + + if (weight == 0.0) + return col1; + else if (weight == 1.0) + return col2; + else + { + gt_color_to_rgba(col1, &r, &g, &b, &a1); + COLOR_rgb_to_hsv(r, g, b, &h1, &s1, &v1); + gt_color_to_rgba(col2, &r, &g, &b, &a2); + COLOR_rgb_to_hsv(r, g, b, &h2, &s2, &v2); + + #define MIX(_val1, _val2) ((int)((_val1) * (1 - weight) + (_val2) * weight + 0.5)) + + if (h1 < 0) + h1 = h2; + else if (h2 < 0) + h2 = h1; + else + h1 = MIX(h1, h2); + + COLOR_hsv_to_rgb(h1, MIX(s1, s2), MIX(v1, v2), &r, &g, &b); + + return gt_rgba_to_color(r, g, b, MIX(a1, a2)); + } +} + +GB_COLOR COLOR_gradient(GB_COLOR col1, GB_COLOR col2, double weight) +{ + int r1, g1, b1, a1; + int r2, g2, b2, a2; + + if (weight == 0.0) + return col1; + else if (weight == 1.0) + return col2; + else + { + gt_color_to_rgba(col1, &r1, &g1, &b1, &a1); + gt_color_to_rgba(col2, &r2, &g2, &b2, &a2); + + #define MIX(_val1, _val2) ((int)((_val1) * (1 - weight) + (_val2) * weight + 0.5)) + + return gt_rgba_to_color(MIX(r1, r2), MIX(g1, g2), MIX(b1, b2), MIX(a1, a2)); + } +} + +GB_COLOR COLOR_lighter(GB_COLOR color) +{ + int h, s, v; + int r, g, b, a; + + gt_color_to_rgba(color, &r, &g, &b, &a); + COLOR_rgb_to_hsv(r, g, b, &h, &s, &v); + COLOR_hsv_to_rgb(h, s / 2, 255 - (255 - v) / 2, &r, &g, &b); + + return gt_rgba_to_color(r, g, b, a); +} + +GB_COLOR COLOR_darker(GB_COLOR color) +{ + int h, s, v; + int r, g, b, a; + + gt_color_to_rgba(color, &r, &g, &b, &a); + COLOR_rgb_to_hsv(r, g, b, &h, &s, &v); + COLOR_hsv_to_rgb(h, s ? 255 - (255 - s) / 2 : 0, v / 2, &r, &g, &b); + + v = gt_rgba_to_color(r, g, b, a); + + return v; +} + +BEGIN_METHOD(Color_RGB, GB_INTEGER r; GB_INTEGER g; GB_INTEGER b; GB_INTEGER a) + + GB.ReturnInteger(gt_rgba_to_color(VARG(r), VARG(g), VARG(b), VARGOPT(a, 0))); + +END_METHOD + +BEGIN_METHOD(Color_SetRGB, GB_INTEGER color; GB_INTEGER red; GB_INTEGER green; GB_INTEGER blue; GB_INTEGER alpha) + + int r, g, b, a; + gt_color_to_rgba(VARG(color), &r, &g, &b, &a); + GB.ReturnInteger(gt_rgba_to_color(VARGOPT(red, r), VARGOPT(green, g), VARGOPT(blue, b), VARGOPT(alpha, a))); + +END_METHOD + +BEGIN_METHOD(Color_HSV, GB_INTEGER h; GB_INTEGER s; GB_INTEGER v; GB_INTEGER a) + + int r, g, b; + COLOR_hsv_to_rgb(VARG(h), VARG(s), VARG(v), &r, &g, &b); + GB.ReturnInteger(gt_rgba_to_color(r, g, b, VARGOPT(a, 0))); + +END_METHOD + +BEGIN_METHOD(Color_SetHSV, GB_INTEGER color; GB_INTEGER hue; GB_INTEGER saturation; GB_INTEGER value; GB_INTEGER alpha) + + int r, g, b, a, h, s, v; + gt_color_to_rgba(VARG(color), &r, &g, &b, &a); + gt_rgb_to_hsv_cached(r, g, b, &h, &s, &v); + COLOR_hsv_to_rgb(VARGOPT(hue, h), VARGOPT(saturation, s), VARGOPT(value, v), &r, &g, &b); + GB.ReturnInteger(gt_rgba_to_color(r, g, b, VARGOPT(alpha, a))); + +END_METHOD + +BEGIN_METHOD(Color_get, GB_INTEGER color) + + static GB_CLASS klass = 0; + CCOLOR *info; + + if (!klass) klass = GB.FindClass("ColorInfo"); + info = GB.New(klass, NULL, NULL); + + gt_color_to_rgba(VARG(color), &info->r, &info->g, &info->b, &info->a); + GB.ReturnObject(info); + +END_METHOD + +static void handle_rgba_property(CCOLOR *_object, void *_param, int prop) +{ + if (READ_PROPERTY) + { + switch(prop) + { + case CC_R: GB.ReturnInteger(THIS->r); break; + case CC_G: GB.ReturnInteger(THIS->g); break; + case CC_B: GB.ReturnInteger(THIS->b); break; + case CC_A: GB.ReturnInteger(THIS->a); break; + } + } + else + { + int v = VPROP(GB_INTEGER); + if (v < 0) v = 0; else if (v > 255) v = 255; + switch(prop) + { + case CC_R: THIS->r = v; break; + case CC_G: THIS->g = v; break; + case CC_B: THIS->b = v; break; + case CC_A: THIS->a = v; break; + } + } +} + +BEGIN_PROPERTY(ColorInfo_Alpha) + + handle_rgba_property(_object, _param, CC_A); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Red) + + handle_rgba_property(_object, _param, CC_R); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Green) + + handle_rgba_property(_object, _param, CC_G); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Blue) + + handle_rgba_property(_object, _param, CC_B); + +END_PROPERTY + +static void handle_hsv_property(CCOLOR *_object, void *_param, int prop) +{ + int h, s, v; + gt_rgb_to_hsv_cached(THIS->r, THIS->g, THIS->b, &h, &s, &v); + + if (READ_PROPERTY) + { + switch(prop) + { + case CC_H: GB.ReturnInteger(h); break; + case CC_V: GB.ReturnInteger(v); break; + case CC_S: GB.ReturnInteger(s); break; + } + } + else + { + switch(prop) + { + case CC_H: h = VPROP(GB_INTEGER) % 360; break; + case CC_V: v = VPROP(GB_INTEGER); if (v < 0) v = 0; else if (v > 255) v = 255; break; + case CC_S: s = VPROP(GB_INTEGER); if (s < 0) s = 0; else if (s > 255) s = 255; break; + } + COLOR_hsv_to_rgb(h, s, v, &THIS->r, &THIS->g, &THIS->b); + } +} + +BEGIN_PROPERTY(ColorInfo_Hue) + + handle_hsv_property(THIS, _param, CC_H); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Saturation) + + handle_hsv_property(THIS, _param, CC_S); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Value) + + handle_hsv_property(THIS, _param, CC_V); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Color) + + CCOLOR *info = THIS; + + if (READ_PROPERTY) + GB.ReturnInteger(gt_rgba_to_color(info->r, info->g, info->b, info->a)); + else + gt_color_to_rgba(VPROP(GB_INTEGER), &info->r, &info->g, &info->b, &info->a); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Luminance) + + if (READ_PROPERTY) + GB.ReturnInteger(get_luminance(THIS)); + else + set_luminance(THIS, VPROP(GB_INTEGER)); + +END_PROPERTY + +//--------------------------------------------------------------------------- + +BEGIN_METHOD(Color_Lighter, GB_INTEGER color) + + GB.ReturnInteger(COLOR_lighter(VARG(color))); + +END_METHOD + +BEGIN_METHOD(Color_Darker, GB_INTEGER color) + + GB.ReturnInteger(COLOR_darker(VARG(color))); + +END_METHOD + +BEGIN_METHOD(Color_Merge, GB_INTEGER color1; GB_INTEGER color2; GB_FLOAT weight) + + GB.ReturnInteger(COLOR_merge(VARG(color1), VARG(color2), VARGOPT(weight, 0.5))); + +END_METHOD + +BEGIN_METHOD(Color_Gradient, GB_INTEGER color1; GB_INTEGER color2; GB_FLOAT weight) + + GB.ReturnInteger(COLOR_gradient(VARG(color1), VARG(color2), VARGOPT(weight, 0.5))); + +END_METHOD + +BEGIN_METHOD(Color_Desaturate, GB_INTEGER color) + + int r, g, b, a, gray; + + gt_color_to_rgba(VARG(color), &r, &g, &b, &a); + gray = (r * 11 + g * 16 + b * 5) / 32; + GB.ReturnInteger(gt_rgba_to_color(gray, gray, gray, a)); + +END_METHOD + +BEGIN_METHOD(Color_Blend, GB_INTEGER src; GB_INTEGER dst) + + uint src = VARG(src); + uint dst = VARG(dst); + uchar rs, gs, bs; + uchar rd, gd, bd; + uchar as = src >> 24; + uchar ad = dst >> 24; + + if (as == 0xFF) + { + GB.ReturnInteger(dst); + return; + } + else if (as == 0) + { + GB.ReturnInteger(src); + return; + } + + ad ^= 0xFF; + as ^= 0xFF; + + bs = src & 0xFF; + gs = (src >> 8) & 0xFF; + rs = (src >> 16) & 0xFF; + + bd = dst & 0xFF; + gd = (dst >> 8) & 0xFF; + rd = (dst >> 16) & 0xFF; + + // D = S * alpha(S) + D * (1 - alpha(S)) + + bd = (((bs - bd) * as) >> 8) + bd; + rd = (((rs - rd) * as) >> 8) + rd; + gd = (((gs - gd) * as) >> 8) + gd; + if (ad < as) ad = as; + ad ^= 0xFF; + + GB.ReturnInteger(bd | (gd << 8) | (rd << 16) | (ad << 24)); + +END_METHOD + +BEGIN_METHOD(Color_GetAlpha, GB_INTEGER color) + + int r, g, b, a; + + gt_color_to_rgba(VARG(color), &r, &g, &b, &a); + GB.ReturnInteger(a); + +END_METHOD + +BEGIN_METHOD(Color_SetAlpha, GB_INTEGER color; GB_INTEGER alpha) + + int r, g, b, a; + + gt_color_to_rgba(VARG(color), &r, &g, &b, &a); + a = VARG(alpha); + GB.ReturnInteger(gt_rgba_to_color(r, g, b, a)); + +END_METHOD + +BEGIN_METHOD(Color_Distance, GB_INTEGER col1; GB_INTEGER col2) + + int r1, g1, b1, a1; + int r2, g2, b2, a2; + + gt_color_to_rgba(VARG(col1), &r1, &g1, &b1, &a1); + gt_color_to_rgba(VARG(col2), &r2, &g2, &b2, &a2); + + r1 -= r2; + g1 -= g2; + b1 -= b2; + a1 -= a2; + r1 *= r1; + g1 *= g1; + b1 *= b1; + a1 *= a1; + + GB.ReturnFloat(sqrt(r1 + b1 + g1 + a1) / 510.0); + +END_METHOD + +GB_DESC CColorInfoDesc[] = +{ + GB_DECLARE("ColorInfo", sizeof(CCOLOR)), GB_NOT_CREATABLE(), + + GB_PROPERTY("Alpha", "i", ColorInfo_Alpha), + GB_PROPERTY("Red", "i", ColorInfo_Red), + GB_PROPERTY("Green", "i", ColorInfo_Green), + GB_PROPERTY("Blue", "i", ColorInfo_Blue), + GB_PROPERTY("Hue", "i", ColorInfo_Hue), + GB_PROPERTY("Saturation", "i", ColorInfo_Saturation), + GB_PROPERTY("Value", "i", ColorInfo_Value), + GB_PROPERTY("Color", "i", ColorInfo_Color), + GB_PROPERTY("Luminance", "i", ColorInfo_Luminance), + + GB_END_DECLARE +}; + +GB_DESC CColorDesc[] = +{ + GB_DECLARE_STATIC("Color"), + + GB_CONSTANT("Default", "i", COLOR_DEFAULT), + + GB_CONSTANT("Black", "i", 0x000000), + GB_CONSTANT("White", "i", 0xFFFFFF), + + GB_CONSTANT("LightGray", "i", 0xC0C0C0), + GB_CONSTANT("Gray", "i", 0x808080), + GB_CONSTANT("DarkGray", "i", 0x404040), + + GB_CONSTANT("Blue", "i", 0x0000FF), + GB_CONSTANT("DarkBlue", "i", 0x000080), + + GB_CONSTANT("Green", "i", 0x00FF00), + GB_CONSTANT("DarkGreen", "i", 0x008000), + + GB_CONSTANT("Red", "i", 0xFF0000), + GB_CONSTANT("DarkRed", "i", 0x800000), + + GB_CONSTANT("Cyan", "i", 0x00FFFF), + GB_CONSTANT("DarkCyan", "i", 0x008080), + + GB_CONSTANT("Magenta", "i", 0x00FF00FF), + GB_CONSTANT("DarkMagenta", "i", 0x00800080), + + GB_CONSTANT("Yellow", "i", 0xFFFF00), + GB_CONSTANT("DarkYellow", "i", 0x808000), + + GB_CONSTANT("Orange", "i", 0xFF8000), + GB_CONSTANT("Violet", "i", 0x8000FF), + GB_CONSTANT("Pink", "i", 0xFF80FF), + + GB_CONSTANT("Transparent", "i", 0xFF000000), + + GB_STATIC_METHOD("RGB", "i", Color_RGB, "(Red)i(Green)i(Blue)i[(Alpha)i]"), + GB_STATIC_METHOD("HSV", "i", Color_HSV, "(Hue)i(Saturation)i(Value)i[(Alpha)i]"), + + GB_STATIC_METHOD("Lighter", "i", Color_Lighter, "(Color)i"), + GB_STATIC_METHOD("Darker", "i", Color_Darker, "(Color)i"), + GB_STATIC_METHOD("Merge", "i", Color_Merge, "(Color1)i(Color2)i[(Weight)f]"), + GB_STATIC_METHOD("Gradient", "i", Color_Gradient, "(Color1)i(Color2)i[(Weight)f]"), + GB_STATIC_METHOD("Blend", "i", Color_Blend, "(Source)i(Destination)i"), + GB_STATIC_METHOD("Desaturate", "i", Color_Desaturate, "(Color)i"), + + GB_STATIC_METHOD("SetAlpha", "i", Color_SetAlpha, "(Color)i(Alpha)i"), + GB_STATIC_METHOD("SetRGB", "i", Color_SetRGB, "(Color)i[(Red)i(Green)i(Blue)i(Alpha)i]"), + GB_STATIC_METHOD("SetHSV", "i", Color_SetHSV, "(Color)i[(Hue)i(Saturation)i(Value)i(Alpha)i]"), + GB_STATIC_METHOD("GetAlpha", "i", Color_GetAlpha, "(Color)i"), + + GB_STATIC_METHOD("Distance", "f", Color_Distance, "(Color1)i(Color2)i"), + + GB_STATIC_METHOD("_get", "ColorInfo", Color_get, "(Color)i"), + //GB_STATIC_METHOD("_call", "ColorInfo", Color_get, "(Color)i"), + + GB_END_DECLARE +}; + + diff --git a/main/lib/image/c_color.h b/main/lib/image/c_color.h new file mode 100644 index 00000000..c5750a55 --- /dev/null +++ b/main/lib/image/c_color.h @@ -0,0 +1,52 @@ +/*************************************************************************** + + c_color.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __C_COLOR_H +#define __C_COLOR_H + +#include "main.h" + +#ifndef __C_COLOR_C +extern GB_DESC CColorDesc[]; +extern GB_DESC CColorInfoDesc[]; +#else +#define THIS ((CCOLOR *)_object) +enum { CC_R, CC_G, CC_B, CC_A, CC_H, CC_S, CC_V }; +#endif + +typedef + struct { + GB_BASE ob; + int r, g, b, a; + } + CCOLOR; + +void COLOR_rgb_to_hsv(int r, int g, int b, int *H, int *S, int *V); +void COLOR_hsv_to_rgb(int h, int s, int v, int *R, int *G, int *B); +GB_COLOR COLOR_merge(GB_COLOR col1, GB_COLOR col2, double weight); +GB_COLOR COLOR_lighter(GB_COLOR color); +GB_COLOR COLOR_darker(GB_COLOR color); +int COLOR_get_luminance(GB_COLOR color); +GB_COLOR COLOR_set_luminance(GB_COLOR color, int l); + +#endif diff --git a/main/lib/image/gb.image.component b/main/lib/image/gb.image.component new file mode 100644 index 00000000..01ec1410 --- /dev/null +++ b/main/lib/image/gb.image.component @@ -0,0 +1,5 @@ +[Component] +Key=gb.image +Name=Image buffer support +Author=Benoît Minisini +State=Stable diff --git a/main/lib/image/gb.image.h b/main/lib/image/gb.image.h new file mode 100644 index 00000000..d4aaf676 --- /dev/null +++ b/main/lib/image/gb.image.h @@ -0,0 +1,171 @@ +/*************************************************************************** + + gb.image.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_IMAGE_H +#define __GB_IMAGE_H + +#include "gambas.h" + +// Constants used by image data format + +#define GB_IMAGE_BGRX 0 // 00000 +#define GB_IMAGE_XRGB 1 // 00001 +#define GB_IMAGE_RGBX 2 // 00010 +#define GB_IMAGE_XBGR 3 // 00011 +#define GB_IMAGE_BGR 4 // 00100 +#define GB_IMAGE_RGB 5 // 00101 + +#define GB_IMAGE_BGRA 8 // 01000 +#define GB_IMAGE_ARGB 9 // 01001 +#define GB_IMAGE_RGBA 10 // 01010 +#define GB_IMAGE_ABGR 11 // 01011 + +#define GB_IMAGE_BGRP 24 // 11000 +#define GB_IMAGE_PRGB 25 // 11001 +#define GB_IMAGE_RGBP 26 // 11010 +#define GB_IMAGE_PBGR 27 // 11011 + +// Format test functions + +#define GB_IMAGE_FMT_IS_24_BITS(_format) ((_format) & 4) +#define GB_IMAGE_FMT_IS_32_BITS(_format) (((_format) & 4) == 0) + +#define GB_IMAGE_FMT_IS_RGBA(_format) ((_format) & 2) + +#define GB_IMAGE_FMT_IS_SWAPPED(_format) ((_format) & 1) + +#define GB_IMAGE_FMT_IS_TRANSPARENT(_format) ((_format) & 8) + +#define GB_IMAGE_FMT_IS_PREMULTIPLIED(_format) ((_format) & 16) +#define GB_IMAGE_FMT_SET_PREMULTIPLIED(_format) ((_format) | 16) +#define GB_IMAGE_FMT_CLEAR_PREMULTIPLIED(_format) ((_format) & ~16) + +// Image owner information + +struct GB_IMG; + +typedef + struct { + const char *name; // owner name (this is the name of the component) + int format; // preferred format + void (*free)(struct GB_IMG *img, void *handle); // free owner handle + void (*release)(struct GB_IMG *img, void *handle); // free temporary handle + void *(*temp)(struct GB_IMG *img); // create a temporary handle for an image and returns it + void (*sync)(struct GB_IMG *img); // synchronize the data. Called only if the GB_IMG.sync flag is set + } + GB_IMG_OWNER; + +// Gambas image + +typedef + struct GB_IMG { + GB_BASE ob; + unsigned char *data; // points at the image data + int width; // image width in pixels + int height; // image height in pixels + int format; // image format (RGB, BGR, RGBA...) + GB_IMG_OWNER *owner; // owner of the data, NULL means gb.image + void *owner_handle; // handle for the owner + GB_IMG_OWNER *temp_owner; // owner of the temporary handle that does not own the data + void *temp_handle; // temporary handle + unsigned modified : 1; // data has been modified by gb.image + unsigned sync : 1; // data must be synchronized by calling GB_IMG_OWNER.sync() + unsigned is_void : 1; // void image (no data) + } + GB_IMG; + +#ifndef __GB_IMAGE_DEFINED +#define __GB_IMAGE_DEFINED +typedef + void *GB_IMAGE; +#endif + +// Pixel color: the color is not premultiplied, and the alpha component is inverted (0 = solid / 255 = transparent) + +#ifndef __GB_COLOR_DEFINED +#define __GB_COLOR_DEFINED +typedef + unsigned int GB_COLOR; +#endif + +// Split a color into its component. Uninvert the alpha component + +#define GB_COLOR_SPLIT(_color, _r, _g, _b, _a) \ +({ \ + uint _c = (uint)(_color); \ + _b = _c & 0xFF; \ + _g = (_c >> 8) & 0xFF; \ + _r = (_c >> 16) & 0xFF; \ + _a = (_c >> 24) ^ 0xFF; \ +}) + +// Create a GB_COLOR from rgba components + +#define GB_COLOR_MAKE(_r, _g, _b, _a) (((_b) & 0xFF) | (((_g) & 0xFF) << 8) | (((_r) & 0xFF) << 16) | ((((_a) & 0xFF) ^ 0xFF) << 24)) + +// Gambas image component interface + +#define IMAGE_INTERFACE_VERSION 1 + +typedef + struct { + intptr_t version; + // Create an image + GB_IMG *(*Create)(int width, int height, int format, unsigned char *data); + // Take image ownership by giving the image handle and information + void (*Take)(GB_IMG *img, GB_IMG_OWNER *owner, void *owner_handle, int width, int height, unsigned char *data); + // Create a temporary handle on the image without becoming the owner. + void *(*Check)(GB_IMG *img, GB_IMG_OWNER *temp_owner); + // Synchronize the image data if needed + void (*Synchronize)(GB_IMG *img); + // Return the size of the image data in bytes + int (*Size)(GB_IMG *img); + // Set the default format used when creating images + void (*SetDefaultFormat)(int format); + // Get the default format used when creating images + int (*GetDefaultFormat)(void); + // Get the color of a pixel + GB_COLOR (*GetPixel)(GB_IMG *img, int x, int y); + // Converts an image to one of the following formats: BGRA, RGBA, BGRP, RGBP + void (*Convert)(GB_IMG *img, int format); + // Merge two colors + GB_COLOR (*MergeColor)(GB_COLOR col1, GB_COLOR col2, double weight); + // Make a color lighter + GB_COLOR (*LighterColor)(GB_COLOR col); + // Make a color darker + GB_COLOR (*DarkerColor)(GB_COLOR col); + // Return the image format as a string + const char *(*FormatToString)(int format); + } + IMAGE_INTERFACE; + +#define GB_IMG_HANDLE(_image) ((_image)->temp_handle) + +#define SYNCHRONIZE_IMAGE(_image) (IMAGE.Synchronize(_image)) +#define MODIFY_IMAGE(_image) ((_image)->modified = 1) + +#define COLOR_DEFAULT ((GB_COLOR)-1) +#define GB_COLOR_DEFAULT ((GB_COLOR)-1) + +#endif + diff --git a/main/lib/image/image.c b/main/lib/image/image.c new file mode 100644 index 00000000..e26cac65 --- /dev/null +++ b/main/lib/image/image.c @@ -0,0 +1,2438 @@ +/*************************************************************************** + + image.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __IMAGE_C + +#include "c_color.h" +#include "image.h" + +bool IMAGE_debug = FALSE; + +typedef + struct { unsigned char d[3]; } PACKED uint24; + +typedef + struct { + int format; + const char *name; + } + FORMAT; + +//#define DEBUG_CONVERT +//#define DEBUG_ME 1 + +static int _default_format = GB_IMAGE_RGBA; + +static FORMAT _formats[] = +{ + { GB_IMAGE_BGRX, "BGRX" }, + { GB_IMAGE_XRGB, "XRGB" }, + { GB_IMAGE_RGBX, "RGBX" }, + { GB_IMAGE_XBGR, "XBGR" }, + { GB_IMAGE_BGR , "BGR" }, + { GB_IMAGE_RGB , "RGB" }, + + { GB_IMAGE_BGRA, "BGRA" }, + { GB_IMAGE_ARGB, "ARGB" }, + { GB_IMAGE_RGBA, "RGBA" }, + { GB_IMAGE_ABGR, "ABGR" }, + + { GB_IMAGE_BGRP, "BGRP" }, + { GB_IMAGE_PRGB, "PRGB" }, + { GB_IMAGE_RGBP, "RGBP" }, + { GB_IMAGE_PBGR, "PBGR" }, + { 0, NULL } +}; + +/*static inline unsigned char *GET_END_POINTER(GB_IMG *image) +{ + return &image->data[IMAGE_size(image)]; +}*/ + +#define GET_END_POINTER(_img) (&(_img)->data[IMAGE_size(_img)]) + +#define PREMUL(_x) \ +({ \ + uint x = (_x); \ + uint a = x >> 24; \ + \ + if (a == 0) \ + x = 0; \ + else if (a != 0xFF) \ + { \ + uint t = (x & 0xFF00FF) * a; \ + t = (t + ((t >> 8) & 0xFF00FF) + 0x800080) >> 8; \ + t &= 0xff00ff; \ + \ + x = ((x >> 8) & 0xff) * a; \ + x = (x + ((x >> 8) & 0xFF) + 0x80); \ + x &= 0xFF00; \ + x |= t | (a << 24); \ + } \ + x; \ +}) + +#define INV_PREMUL(__p) \ +({ \ + uint _p = (__p); \ + if (ALPHA(_p) == 0) \ + _p = 0; \ + else if (ALPHA(_p) != 0xFF) \ + _p = ((ALPHA(_p) << 24) \ + | (((255*RED(_p))/ ALPHA(_p)) << 16) \ + | (((255*GREEN(_p)) / ALPHA(_p)) << 8) \ + | ((255*BLUE(_p)) / ALPHA(_p))); \ + _p; \ +}) + +#define INV_SPREMUL(__p) \ +({ \ + uint _p = (__p); \ + if (SALPHA(_p) == 0) \ + _p = 0; \ + else if (SALPHA(_p) != 0xFF) \ + _p = ((SALPHA(_p) << 24) \ + | (((255*SRED(_p))/ SALPHA(_p)) << 16) \ + | (((255*SGREEN(_p)) / SALPHA(_p)) << 8) \ + | ((255*SBLUE(_p)) / SALPHA(_p))); \ + _p; \ +}) + +#define SWAP(__p) \ +({ \ + uint _p = (__p); \ + RGBA(ALPHA(_p), BLUE(_p), GREEN(_p), RED(_p)); \ +}) + +#define SWAP_RED_BLUE(__p) \ +({ \ + uint _p = (__p); \ + RGBA(BLUE(_p), GREEN(_p), RED(_p), ALPHA(_p)); \ +}) + +// Convert from GB_COLOR to a specific format + +static uint GB_COLOR_to_format(GB_COLOR col, int format) +{ + col ^= 0xFF000000; + if (GB_IMAGE_FMT_IS_PREMULTIPLIED(format)) + col = PREMUL(col); + if (GB_IMAGE_FMT_IS_SWAPPED(format)) + col = SWAP(col); + if (GB_IMAGE_FMT_IS_RGBA(format)) + col = SWAP_RED_BLUE(col); + return col; +} + +// Convert from a specific format to GB_COLOR + +static GB_COLOR GB_COLOR_from_format(uint col, int format) +{ + if (GB_IMAGE_FMT_IS_RGBA(format)) + col = SWAP_RED_BLUE(col); + if (GB_IMAGE_FMT_IS_SWAPPED(format)) + col = SWAP(col); + if (GB_IMAGE_FMT_IS_PREMULTIPLIED(format)) + col = INV_PREMUL(col); + return col ^ 0xFF000000; +} + +// Convert from BGRA to a specific format + +static inline uint BGRA_to_format(uint col, int format) +{ + if (GB_IMAGE_FMT_IS_PREMULTIPLIED(format)) + col = PREMUL(col); + if (GB_IMAGE_FMT_IS_SWAPPED(format)) + col = SWAP(col); + if (GB_IMAGE_FMT_IS_RGBA(format)) + col = SWAP_RED_BLUE(col); + return col; +} + +// Convert from a specific format to BGRA + +static inline uint BGRA_from_format(uint col, int format) +{ + if (GB_IMAGE_FMT_IS_RGBA(format)) + col = SWAP_RED_BLUE(col); + if (GB_IMAGE_FMT_IS_SWAPPED(format)) + col = SWAP(col); + if (GB_IMAGE_FMT_IS_PREMULTIPLIED(format)) + col = INV_PREMUL(col); + return col; +} + +// Convert from GB_COLOR to BGRA + +static inline uint GB_COLOR_to_BGRA(GB_COLOR col) +{ + return col ^ 0xFF000000; +} + +// Compose two BGRA colors + +static inline uint BGRA_compose(uint dst, uint src) +{ + unsigned char a = ALPHA(src); + if (a == 255) + return src; + else if (a == 0) + return dst; + else + { + unsigned char r = ((RED(src) - RED(dst)) * a) / 256 + RED(dst); + unsigned char g = ((GREEN(src) - GREEN(dst)) * a) / 256 + GREEN(dst); + unsigned char b = ((BLUE(src) - BLUE(dst)) * a) / 256 + BLUE(dst); + if (ALPHA(dst) > a) + a = ALPHA(dst); + return RGBA(r, g, b, a); + } +} + +static inline bool is_valid(GB_IMG *img, int x, int y) +{ + return !(x >= img->width || y >= img->height || x < 0 || y < 0); +} + +static void free_image(GB_IMG *img, void *image) +{ + //fprintf(stderr, "free_image: %p %p : %d\n", img, img->data, IMAGE_size(img)); + GB.Free(POINTER(&img->data)); +} + +static GB_IMG_OWNER _image_owner = { + "gb.image", + 0, + free_image, + free_image, + NULL, + NULL + }; + + +// Only converts to the following formats: +// - GB_IMAGE_BGRA +// - GB_IMAGE_BGRX +// - GB_IMAGE_RGBA +// - GB_IMAGE_RGBX + +static void convert_image(uchar *dst, int dst_format, uchar *src, int src_format, int w, int h) +{ + unsigned char *s = src; + unsigned char *d = dst; + int len; + unsigned char *dm; + uint *p, *pm; + bool psrc, pdst; + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: src_format = %d dst_format = %d\n", src_format, dst_format); + #endif + + len = w * h * sizeof(uint); + dm = &d[len]; + + psrc = GB_IMAGE_FMT_IS_PREMULTIPLIED(src_format); + pdst = GB_IMAGE_FMT_IS_PREMULTIPLIED(dst_format); + + if (psrc != pdst && GB_IMAGE_FMT_IS_SWAPPED(dst_format)) + { + p = (uint *)dst; + pm = (uint *)dm; + + if (psrc) + { + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: premultiplied -> normal\n"); + #endif + // convert premultiplied to normal + while (p != pm) + { + *p = INV_PREMUL(*p); + p++; + } + } + else + { + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: normal -> premultiplied\n"); + #endif + // convert normal to premultiplied + while (p != pm) + { + *p = PREMUL(*p); + p++; + } + } + } + + src_format = GB_IMAGE_FMT_CLEAR_PREMULTIPLIED(src_format); + dst_format = GB_IMAGE_FMT_CLEAR_PREMULTIPLIED(dst_format); + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: after: src_format = %d dst_format = %d\n", src_format, dst_format); + #endif + + if (dst_format == GB_IMAGE_BGRA || dst_format == GB_IMAGE_BGRX) + { + switch (src_format) + { + case GB_IMAGE_BGRA: case GB_IMAGE_BGRX: + goto __0123; + + case GB_IMAGE_ARGB: case GB_IMAGE_XRGB: + goto __3210; + + case GB_IMAGE_RGBA: case GB_IMAGE_RGBX: + goto __2103; + + case GB_IMAGE_ABGR: case GB_IMAGE_XBGR: + goto __1230; + + case GB_IMAGE_BGR: + goto __012X; + + case GB_IMAGE_RGB: + goto __210X; + } + } + else if (dst_format == GB_IMAGE_RGBA || dst_format == GB_IMAGE_RGBX) + { + switch (src_format) + { + case GB_IMAGE_RGBA: case GB_IMAGE_RGBX: + goto __0123; + + case GB_IMAGE_ABGR: case GB_IMAGE_XBGR: + goto __3210; + + case GB_IMAGE_BGRA: case GB_IMAGE_BGRX: + goto __2103; + + case GB_IMAGE_ARGB: case GB_IMAGE_XRGB: + goto __1230; + + case GB_IMAGE_RGB: + goto __012X; + + case GB_IMAGE_BGR: + goto __210X; + } + } + else if (dst_format == GB_IMAGE_ARGB || dst_format == GB_IMAGE_XRGB) + { + switch (src_format) + { + case GB_IMAGE_ARGB: case GB_IMAGE_XRGB: + goto __0123; + + case GB_IMAGE_BGRA: case GB_IMAGE_BGRX: + goto __3210; + + case GB_IMAGE_ABGR: case GB_IMAGE_XBGR: + goto __0321; + + case GB_IMAGE_RGBA: case GB_IMAGE_RGBX: + goto __3012; + + case GB_IMAGE_RGB: + goto __012X; + + case GB_IMAGE_BGR: + goto __210X; + } + } + else if (dst_format == GB_IMAGE_ABGR || dst_format == GB_IMAGE_XBGR) + { + switch (src_format) + { + case GB_IMAGE_ABGR: case GB_IMAGE_XBGR: + goto __0123; + + case GB_IMAGE_RGBA: case GB_IMAGE_RGBX: + goto __3210; + + case GB_IMAGE_ARGB: case GB_IMAGE_XRGB: + goto __0321; + + case GB_IMAGE_BGRA: case GB_IMAGE_BGRX: + goto __3012; + + case GB_IMAGE_BGR: + goto __012X; + + case GB_IMAGE_RGB: + goto __210X; + } + } + +__0123: + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: 0123\n"); + #endif + memcpy(dst, src, len); + goto __PREMULTIPLIED; + +__3210: + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: 3210\n"); + #endif + while (d != dm) + { + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; + s += 4; + d += 4; + } + goto __PREMULTIPLIED; + +__0321: + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: 0321\n"); + #endif + while (d != dm) + { + d[0] = s[0]; + d[1] = s[3]; + d[2] = s[2]; + d[3] = s[1]; + s += 4; + d += 4; + } + goto __PREMULTIPLIED; + +__2103: + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: 2103\n"); + #endif + + while (d != dm) + { + d[0] = s[2]; + d[1] = s[1]; + d[2] = s[0]; + d[3] = s[3]; + s += 4; + d += 4; + } + goto __PREMULTIPLIED; + +__3012: + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: 3012\n"); + #endif + + while (d != dm) + { + d[0] = s[3]; + d[1] = s[0]; + d[2] = s[1]; + d[3] = s[2]; + s += 4; + d += 4; + } + goto __PREMULTIPLIED; + +__1230: + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: 1230\n"); + #endif + + while (d != dm) + { + d[0] = s[1]; + d[1] = s[2]; + d[2] = s[3]; + d[3] = s[0]; + s += 4; + d += 4; + } + goto __PREMULTIPLIED; + +__012X: + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: 012X\n"); + #endif + + while (d != dm) + { + d[0] = s[0]; + d[1] = s[1]; + d[2] = s[2]; + d[3] = 0xFF; + s += 3; + d += 4; + } + return; + +__210X: + + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: 210X\n"); + #endif + + while (d != dm) + { + d[0] = s[2]; + d[1] = s[1]; + d[2] = s[0]; + d[3] = 0xFF; + s += 3; + d += 4; + } + return; + +__PREMULTIPLIED: + + if (psrc != pdst && !GB_IMAGE_FMT_IS_SWAPPED(dst_format)) + { + p = (uint *)dst; + pm = (uint *)dm; + + if (psrc) + { + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: premultiplied -> normal\n"); + #endif + // convert premultiplied to normal + while (p != pm) + { + *p = INV_PREMUL(*p); + p++; + } + } + else + { + #ifdef DEBUG_CONVERT + fprintf(stderr, "convert_image: normal -> premultiplied\n"); + #endif + // convert normal to premultiplied + while (p != pm) + { + *p = PREMUL(*p); + p++; + } + } + } +} + +#define SYNCHRONIZE(_img) ({ if ((_img)->sync && (_img)->temp_owner) (*(_img)->temp_owner->sync)(_img); }) +#define MODIFY(_img) ((_img)->modified = TRUE) + +int IMAGE_size(GB_IMG *img) +{ + return img->width * img->height * (GB_IMAGE_FMT_IS_24_BITS(img->format) ? 3 : 4); +} + +void IMAGE_create(GB_IMG *img, int width, int height, int format) +{ + GB_BASE save = img->ob; + CLEAR(img); + img->ob = save; + img->owner = &_image_owner; + + if (width <= 0 || height <= 0) + { + img->is_void = TRUE; + return; + } + + img->width = width; + img->height = height; + img->format = format; + GB.Alloc(POINTER(&img->data), IMAGE_size(img)); + img->owner_handle = img->data; + + #ifdef DEBUG_ME + fprintf(stderr, "IMAGE_create: %p\n", img); + #endif +} + + +void IMAGE_create_with_data(GB_IMG *img, int width, int height, int format, unsigned char *data) +{ + IMAGE_create(img, width, height, format); + if (data && !IMAGE_is_void(img)) + memcpy(img->data, data, IMAGE_size(img)); +} + +const char *IMAGE_format_to_string(int fmt) +{ + FORMAT *pf; + + for (pf = _formats; pf->name; pf++) + { + if (fmt == pf->format) + return pf->name; + } + + return NULL; +} + +int IMAGE_format_from_string(char *fmt) +{ + FORMAT *pf; + + for (pf = _formats; pf->name; pf++) + { + if (strcmp(fmt, pf->name) == 0) + return pf->format; + } + + return -1; +} + +void IMAGE_convert(GB_IMG *img, int dst_format) +{ + uchar *data; + int src_format = img->format; + + if (src_format == dst_format) + return; + + //IMAGE_create(&tmp, img->width, img->height, format); + img->format = dst_format; + if (IMAGE_is_void(img)) + return; + + if (IMAGE_debug) + fprintf(stderr, "gb.image: convert: %s -> %s\n", IMAGE_format_to_string(src_format), IMAGE_format_to_string(dst_format)); + + GB.Alloc(POINTER(&data), IMAGE_size(img)); + convert_image(data, dst_format, img->data, src_format, img->width, img->height); + //GB.Free(POINTER(&img->data)); + IMAGE_take(img, &_image_owner, data, img->width, img->height, data); +} + +// Check if a temporary handle is needed, and create it if needed by calling the owner "temp" function + +void *IMAGE_check(GB_IMG *img, GB_IMG_OWNER *temp_owner) +{ + if (!img) + return NULL; + + // If we already have the temporary handle, then do nothing + if (img->temp_owner == temp_owner) + return img->temp_handle; + + #ifdef DEBUG_ME + fprintf(stderr, "IMAGE_check: %p: %s (%p) / %s (%p) -> %s\n", + img, + img->owner->name, img->owner_handle, + img->temp_owner ? img->temp_owner->name : "NULL", img->temp_handle, + temp_owner ? temp_owner->name : "NULL"); + #endif + + // If somebody else has a temporary handle + if (img->temp_owner) + { + // release it only if it is not the owner + if (img->temp_owner != img->owner && img->temp_owner->release) + (*img->temp_owner->release)(img, img->temp_handle); + img->temp_handle = 0; + img->temp_owner = NULL; + } + + // Get the temporary handle + if (temp_owner) + { + // If we are the owner, we must use our owner handle as temporary handle + if (img->owner == temp_owner) + img->temp_handle = img->owner_handle; + // If we are not the owner, then we will have to create the temporary handle ourself + else + { + // Synchronize the image if needed + SYNCHRONIZE(img); + // Conversion can make gb.image the new owner and temporary owner + IMAGE_convert(img, temp_owner->format); + img->temp_handle = (*temp_owner->temp)(img); + } + } + + // Become the temporary owner + img->temp_owner = temp_owner; + + #ifdef DEBUG_ME + fprintf(stderr, "==========>: %p: %s (%p) / %s (%p)\n", + img, + img->owner->name, img->owner_handle, + img->temp_owner ? img->temp_owner->name : "NULL", img->temp_handle); + #endif + + return img->temp_handle; +} + +// Take ownership of the image +void IMAGE_take(GB_IMG *img, GB_IMG_OWNER *owner, void *owner_handle, int width, int height, unsigned char *data) +{ + if (!img) + return; + + // If we are already the owner with the same handle, then do nothing + if (img->owner == owner && img->owner_handle == owner_handle) + return; + + // Release the old owner + //fprintf(stderr, "releasing image %p owned by %s\n", img, img->owner->name); + (*img->owner->free)(img, img->owner_handle); + + // If we have the temporary handle too, then clean it as it is necessarily the same + if (img->temp_owner == img->owner) + { + img->temp_owner = NULL; + img->temp_handle = 0; + } + + // Become the owner + img->owner = owner; + img->owner_handle = owner_handle; + + // As we are now the owner, then we must have the temporary handle too + IMAGE_check(img, NULL); + img->temp_owner = owner; + img->temp_handle = owner_handle; + + // Initialize the data + img->width = width; + img->height = height; + img->data = data; + if (owner && owner->format) + img->format = owner->format; + img->is_void = width <= 0 || height <= 0; +} + +void IMAGE_delete(GB_IMG *img) +{ + #ifdef DEBUG_ME + fprintf(stderr, "IMAGE_delete: %p\n", img); + #endif + //IMAGE_take(img, &_image_owner, NULL, 0, 0, NULL); + + // Release the temporary handle before the owner, because the temporary owner may write to the data before freeing! + + if (img->temp_owner && img->temp_owner != img->owner && img->temp_handle) + (*img->temp_owner->release)(img, img->temp_handle); + + if (!IMAGE_is_void(img)) + (*img->owner->free)(img, img->owner_handle); + + img->width = img->height = 0; + img->format = 0; + img->temp_owner = NULL; + img->temp_handle = NULL; + img->owner = &_image_owner; + img->owner_handle = NULL; + img->is_void = TRUE; +} + +void IMAGE_synchronize(GB_IMG *img) +{ + SYNCHRONIZE(img); +} + +#define GET_POINTER(_img, _p, _pm) \ + uint *_p = (uint *)(_img)->data; \ + uint *_pm = (uint *)GET_END_POINTER(_img); \ + if ((_img)->is_void) return; + +void IMAGE_fill(GB_IMG *img, GB_COLOR col) +{ + GET_POINTER(img, p, pm); + + //SYNCHRONIZE(img); unneeded, as the entire image will be replaced + + col = GB_COLOR_to_format(col, img->format); + //fprintf(stderr, "fill with %08X\n", col); + while (p != pm) + *p++ = col; + + MODIFY(img); +} + +// GB_IMAGE_RGB[APX] only +void IMAGE_make_gray(GB_IMG *img) +{ + GET_POINTER(img, p, pm); + uint col; + uchar g; + int format = img->format; + + SYNCHRONIZE(img); + + while (p != pm) + { + col = BGRA_from_format(*p, format); + g = GRAY(col); + + *p++ = BGRA_to_format(RGBA(g, g, g, ALPHA(col)), format); + } + + MODIFY(img); +} + +GB_COLOR IMAGE_get_pixel(GB_IMG *img, int x, int y) +{ + uint col; + + if (!is_valid(img, x, y)) + return (-1); + + SYNCHRONIZE(img); + col = ((uint *)img->data)[y * img->width + x]; + return GB_COLOR_from_format(col, img->format); +} + +void IMAGE_get_pixels(GB_IMG *img, int *data) +{ + SYNCHRONIZE(img); + + memcpy(data, img->data, img->width * img->height * sizeof(int)); +} + +void IMAGE_set_pixel(GB_IMG *img, int x, int y, GB_COLOR col) +{ + if (!is_valid(img, x, y)) + return; + + SYNCHRONIZE(img); + ((uint *)img->data)[y * img->width + x] = GB_COLOR_to_format(col, img->format); + MODIFY(img); +} + +void IMAGE_set_pixels(GB_IMG *img, int *data) +{ + SYNCHRONIZE(img); + memcpy(img->data, data, img->width * img->height * sizeof(int)); + MODIFY(img); +} + +void IMAGE_fill_rect(GB_IMG *img, int x, int y, int w, int h, GB_COLOR col, bool opaque) +{ + uint *p; + int i; + uint c; + int format = img->format; + + if (x >= img->width || y >= img->height) return; + + if (x < 0) { w += x; x = 0; } + if (y < 0) { h += y; y = 0; } + if ((x + w) > img->width) { w = img->width - x; } + if ((y + h) > img->height) { h = img->height - y; } + + if (w <= 0 || h <= 0) return; + + SYNCHRONIZE(img); + + p = &((uint *)img->data)[y * img->width + x]; + + c = GB_COLOR_to_BGRA(col); + + if (opaque || ALPHA(c) == 255) + { + c = BGRA_to_format(c, format); + while (h) + { + for(i = w; i; i--) + *p++ = c; + h--; + p += img->width - w; + } + } + else + { + while (h) + { + for(i = w; i; i--, p++) + *p = BGRA_to_format(BGRA_compose(BGRA_from_format(*p, format), c), format); + h--; + p += img->width - w; + } + } + + MODIFY(img); +} + +void IMAGE_replace(GB_IMG *img, GB_COLOR src, GB_COLOR dst, bool noteq) +{ + GET_POINTER(img, p, pm); + + src = GB_COLOR_to_format(src, img->format); + dst = GB_COLOR_to_format(dst, img->format); + + SYNCHRONIZE(img); + + if (noteq) + { + while (p != pm) + { + if (*p != src) + *p = dst; + p++; + } + } + else + { + while (p != pm) + { + if (*p == src) + *p = dst; + p++; + } + } + + MODIFY(img); +} + +// Comes from the GIMP + +typedef + struct { + float r; + float b; + float g; + float a; + } + FLOAT_RGB; + +static void color_to_alpha(FLOAT_RGB *src, const FLOAT_RGB *color) +{ + FLOAT_RGB alpha; + + alpha.a = src->a; + + if (color->r < 0.0001) + alpha.r = src->r; + else if (src->r > color->r) + alpha.r = (src->r - color->r) / (1.0 - color->r); + else if (src->r < color->r) + alpha.r = (color->r - src->r) / color->r; + else alpha.r = 0.0; + + if (color->g < 0.0001) + alpha.g = src->g; + else if (src->g > color->g) + alpha.g = (src->g - color->g) / (1.0 - color->g); + else if (src->g < color->g) + alpha.g = (color->g - src->g) / (color->g); + else alpha.g = 0.0; + + if (color->b < 0.0001) + alpha.b = src->b; + else if (src->b > color->b) + alpha.b = (src->b - color->b) / (1.0 - color->b); + else if (src->b < color->b) + alpha.b = (color->b - src->b) / (color->b); + else alpha.b = 0.0; + + if (alpha.r > alpha.g) + { + if (alpha.r > alpha.b) + src->a = alpha.r; + else + src->a = alpha.b; + } + else if (alpha.g > alpha.b) + { + src->a = alpha.g; + } + else + { + src->a = alpha.b; + } + + if (src->a < 0.0001) + return; + + src->r = (src->r - color->r) / src->a + color->r; + src->g = (src->g - color->g) / src->a + color->g; + src->b = (src->b - color->b) / src->a + color->b; + + src->a *= alpha.a; +} + +void IMAGE_make_transparent(GB_IMG *img, GB_COLOR col) +{ + uint color; + FLOAT_RGB rgb_color; + FLOAT_RGB rgb_src; + int format = img->format; + GET_POINTER(img, p, pm); + //uint *p = (uint *)img->data; + //uint *pm = (uint *)(img->data + IMAGE_size(img)); + + //fprintf(stderr, "IMAGE_make_transparent: %d x %d / %d\n", img->width, img->height, img->format); + + SYNCHRONIZE(img); + + color = GB_COLOR_to_BGRA(col); + rgb_color.b = BLUE(color) / 255.0; + rgb_color.g = GREEN(color) / 255.0; + rgb_color.r = RED(color) / 255.0; + rgb_color.a = 1.0; + + while (p != pm) + { + color = BGRA_from_format(*p, format); + rgb_src.b = BLUE(color) / 255.0; + rgb_src.g = GREEN(color) / 255.0; + rgb_src.r = RED(color) / 255.0; + rgb_src.a = ALPHA(color) / 255.0; + + color_to_alpha(&rgb_src, &rgb_color); + + color = RGBA( + (unsigned char)(255.0 * rgb_src.r + 0.5), + (unsigned char)(255.0 * rgb_src.g + 0.5), + (unsigned char)(255.0 * rgb_src.b + 0.5), + (unsigned char)(255.0 * rgb_src.a + 0.5) + ); + + *p = BGRA_to_format(color, format); + //fprintf(stderr, "[%d] %08X\n", p - (uint *)img->data, *p); + p++; + } + + MODIFY(img); + //fprintf(stderr, "IMAGE_make_transparent: ** DONE **\n"); +} + + +void IMAGE_set_default_format(int format) +{ + _default_format = format; //GB_IMAGE_FMT_CLEAR_PREMULTIPLIED(format); +} + +int IMAGE_get_default_format() +{ + return _default_format; +} + +// Parameter correction +#define CHECK_PARAMETERS(dst, dx, dy, dw, dh, src, sx, sy, sw, sh) \ + if ( sw < 0 ) sw = src->width; \ + if ( sh < 0 ) sh = src->height; \ + if (dw < 0) dw = sw; \ + if (dh < 0) dh = sh; \ + if (dw != sw || dh != sh) \ + { \ + GB.Error("Stretching images is not implemented in gb.image"); \ + return; \ + } \ + if ( sx < 0 ) { dx -= sx; dw += sx; sw += sx; sx = 0; } \ + if ( sy < 0 ) { dy -= sy; dh += sy; sh += sy; sy = 0; } \ + if ( dx < 0 ) { sx -= dx; sw += dx; dx = 0; } \ + if ( dy < 0 ) { sy -= dy; sh += dy; dy = 0; } \ + if ( (sx + sw) > src->width ) sw = src->width - sx; \ + if ( (sy + sh) > src->height ) sh = src->height - sy; \ + if ( (dx + sw) > dst->width ) sw = dst->width - dx; \ + if ( (dy + sh) > dst->height ) sh = dst->height - dy; \ + if (sw <= 0 || sh <= 0) \ + return; + +void IMAGE_bitblt(GB_IMG *dst, int dx, int dy, int dw, int dh, GB_IMG *src, int sx, int sy, int sw, int sh) +{ + int sfmt = src->format; + int dfmt = dst->format; + + CHECK_PARAMETERS(dst, dx, dy, dw, dh, src, sx, sy, sw, sh); + + SYNCHRONIZE(src); + SYNCHRONIZE(dst); + + uint *d = (uint *)dst->data + dy * dst->width + dx; + uint *s = (uint *)src->data + sy * src->width + sx; + + if (GB_IMAGE_FMT_IS_32_BITS(sfmt) && GB_IMAGE_FMT_IS_32_BITS(dfmt)) + { + if (sfmt != dfmt) + { + const int dd = dst->width - sw; + const int ds = src->width - sw; + int t; + while (sh--) + { + for (t = sw; t--;) + { + *d = BGRA_to_format(BGRA_from_format(*s, sfmt), dfmt); + d++; + s++; + } + + d += dd; + s += ds; + } + } + else if (sw < 64) + { + const int dd = dst->width - sw; + const int ds = src->width - sw; + int t; + while (sh--) + { + for (t = sw; t--;) + *d++ = *s++; + d += dd; + s += ds; + } + } + else + { + // Trust libc + const int dd = dst->width; + const int ds = src->width; + const int b = sw * sizeof(uint); + while (sh--) + { + memcpy(d, s, b); + d += dd; + s += ds; + } + } + } + else if (GB_IMAGE_FMT_IS_24_BITS(sfmt) && GB_IMAGE_FMT_IS_24_BITS(sfmt)) + { + char *d = (char *)dst->data + (dy * dst->width + dx) * 3; + char *s = (char *)src->data + (sy * src->width + sx) * 3; + const int dd = dst->width * 3; + const int ds = src->width * 3; + const int b = sw * 3; + + while (sh--) + { + memcpy(d, s, b); + d += dd; + s += ds; + } + } + else + { + GB.Error("The pixel size of both images must be the same"); + } + + MODIFY(dst); +} + +void IMAGE_set_opacity(GB_IMG *dst, uchar opacity) +{ + if (!GB_IMAGE_FMT_IS_32_BITS(dst->format)) + { + GB.Error("The image must have an alpha channel"); + return; + } + + if (opacity == 255) + return; + + SYNCHRONIZE(dst); + + GET_POINTER(dst, p, pm); + + if (GB_IMAGE_FMT_IS_PREMULTIPLIED(dst->format)) + { + uint *pp = p; + while (pp != pm) + { + *pp = INV_PREMUL(*pp); + pp++; + } + } + + uchar *d = (uchar *)p; + uchar *dm = (uchar *)pm; + + if (!GB_IMAGE_FMT_IS_SWAPPED(dst->format)) + { + d += 3; + dm += 3; + } + + if (opacity == 0) + { + while (d != dm) + { + *d = 0; + d += 4; + } + } + else + { + uchar da[256]; + int i; + + for (i = 0; i < 256; i++) + da[i] = i * opacity >> 8; + + while (d != dm) + { + *d = da[*d]; + d += 4; + } + } + + if (GB_IMAGE_FMT_IS_PREMULTIPLIED(dst->format)) + { + uint *pp = p; + while (pp != pm) + { + *pp = PREMUL(*pp); + pp++; + } + } + + MODIFY(dst); +} + +void IMAGE_draw_alpha(GB_IMG *dst, int dx, int dy, GB_IMG *src, int sx, int sy, int sw, int sh) +{ + if (!GB_IMAGE_FMT_IS_32_BITS(src->format) || !GB_IMAGE_FMT_IS_32_BITS(dst->format)) + { + GB.Error("The images must have an alpha channel"); + return; + } + + CHECK_PARAMETERS(dst, dx, dy, sw, sh, src, sx, sy, sw, sh); + + SYNCHRONIZE(src); + SYNCHRONIZE(dst); + + uchar *d = (uchar *)((uint *)dst->data + dy * dst->width + dx); + uchar *s = (uchar *)((uint *)src->data + sy * src->width + sx); + + const int dd = (dst->width - sw) * 4; + const int ds = (src->width - sw) * 4; + //uint cs, cd; + int sformat = src->format; + int dformat = dst->format; + int t; + + if (!GB_IMAGE_FMT_IS_SWAPPED(sformat)) + s += 3; + if (!GB_IMAGE_FMT_IS_SWAPPED(dformat)) + d += 3; + + while (sh--) + { + for (t = sw; t--; d += 4,s += 4) + { + if (*s < *d) + *d = *s; + } + + d += dd; + s += ds; + } + + MODIFY(dst); +} + +void IMAGE_compose(GB_IMG *dst, int dx, int dy, int dw, int dh, GB_IMG *src, int sx, int sy, int sw, int sh) +{ + if (dst->format != src->format) + { + GB.Error("The images must have the same format"); + return; + } + + CHECK_PARAMETERS(dst, dx, dy, dw, dh, src, sx, sy, sw, sh); + + SYNCHRONIZE(src); + SYNCHRONIZE(dst); + + switch(src->format) + { + case GB_IMAGE_RGBA: case GB_IMAGE_BGRA: + { + #if 0 + uint *d = (uint *)dst->data + dy * dst->width + dx; + uint *s = (uint *)src->data + sy * src->width + sx; + + const int dd = dst->width - sw; + const int ds = src->width - sw; + int t; + while (sh--) + { + for (t = sw; t--;) + { + unsigned char a = ALPHA(*s); + if (a == 255) + *d = *s; + else if (a) + { + unsigned char r = ((RED(*s)-RED(*d)) * a) / 256 + RED(*d); + unsigned char g = ((GREEN(*s)-GREEN(*d)) * a) / 256 + GREEN(*d); + unsigned char b = ((BLUE(*s)-BLUE(*d)) * a) / 256 + BLUE(*d); + if (ALPHA(*d) > a) + a = ALPHA(*d); + *d = RGBA(r,g,b,a); + } + d++; + s++; + } + d += dd; + s += ds; + } + #else + uchar *d = (uchar *)((uint *)dst->data + dy * dst->width + dx); + uchar *s = (uchar *)((uint *)src->data + sy * src->width + sx); + + const int dd = (dst->width - sw) * 4; + const int ds = (src->width - sw) * 4; + int t; + while (sh--) + { + for (t = sw; t--;) + { + unsigned char a = s[3]; + if (a == 255) + *(uint *)d = *(uint *)s; + else if (a) + { + d[0] = ((s[0] - d[0]) * a) / 256 + d[0]; + d[1] = ((s[1] - d[1]) * a) / 256 + d[1]; + d[2] = ((s[2] - d[2]) * a) / 256 + d[2]; + if (d[3] > a) + d[3] = a; + } + d += 4; + s += 4; + } + d += dd; + s += ds; + } + #endif + + break; + } + + default: + GB.Error("Unsupported image format"); + return; + } + + MODIFY(dst); +} + +void IMAGE_colorize(GB_IMG *img, GB_COLOR color) +{ + GET_POINTER(img, p, pm); + uint col; + int h, s, v; + int r, g, b; + int hcol, scol, vcol; + int format = img->format; + uchar vcolmul[256]; + int i; + + SYNCHRONIZE(img); + + col = GB_COLOR_to_BGRA(color); + COLOR_rgb_to_hsv(RED(col), GREEN(col), BLUE(col), &hcol, &scol, &vcol); + + for (i = 0; i < 256; i++) + vcolmul[i] = vcol * i / 255; + + while (p != pm) + { + col = BGRA_from_format(*p, format); + COLOR_rgb_to_hsv(RED(col), GREEN(col), BLUE(col), &h, &s, &v); + COLOR_hsv_to_rgb(hcol, scol, vcolmul[v], &r, &g, &b); + *p++ = BGRA_to_format(RGBA(r, g, b, ALPHA(col)), img->format); + } + + MODIFY(img); +} + +void IMAGE_mask(GB_IMG *img, GB_COLOR color) +{ + GET_POINTER(img, p, pm); + uint col; + unsigned char red[256], blue[256], green[256], alpha[256]; + int i, r, g, b, a; + int format = img->format; + + SYNCHRONIZE(img); + + col = GB_COLOR_to_format(color, img->format); + r = RED(col); + g = GREEN(col); + b = BLUE(col); + a = ALPHA(col); + + for (i = 0; i < 256; i++) + { + red[i] = i * r / 255; + green[i] = i * g / 255; + blue[i] = i * b / 255; + alpha[i] = i * a / 255; + } + + while (p != pm) + { + col = BGRA_from_format(*p, format); + *p++ = BGRA_to_format(RGBA(red[RED(col)], green[GREEN(col)], blue[BLUE(col)], alpha[ALPHA(col)]), format); + } + + MODIFY(img); +} + +void IMAGE_mirror(GB_IMG *src, GB_IMG *dst, bool horizontal, bool vertical) +{ + if (dst->width != src->width || dst->height != src->height || dst->format != src->format || IMAGE_is_void(src)) + return; + + int w = src->width; + int h = src->height; + + int dxi = horizontal ? -1 : 1; + int dxs = horizontal ? (w - 1) : 0; + int dyi = vertical ? -1 : 1; + int dy = vertical ? (h - 1) : 0; + int sx, sy; + + SYNCHRONIZE(src); + + if (GB_IMAGE_FMT_IS_24_BITS(src->format)) + { + for (sy = 0; sy < h; sy++, dy += dyi) + { + uint24 *ssl = (uint24 *)(src->data + sy * src->width * 3); + uint24 *dsl = (uint24 *)(dst->data + dy * dst->width * 3); + int dx = dxs; + for (sx = 0; sx < w; sx++, dx += dxi) + dsl[dx] = ssl[sx]; + } + } + else + { + for (sy = 0; sy < h; sy++, dy += dyi) + { + uint *ssl = (uint *)(src->data + sy * src->width * 4); + uint *dsl = (uint *)(dst->data + dy * dst->width * 4); + int dx = dxs; + for (sx = 0; sx < w; sx++, dx += dxi) + dsl[dx] = ssl[sx]; + } + } + + MODIFY(dst); +} + +void IMAGE_rotate(GB_IMG *src, GB_IMG *dst, bool left) +{ + if (dst->width != src->height || dst->width != src->height || dst->format != src->format || IMAGE_is_void(src)) + return; + + int x, y; + int w = dst->width; + int h = dst->height; + + SYNCHRONIZE(src); + + if (GB_IMAGE_FMT_IS_24_BITS(src->format)) + { + uint24 *pd = (uint24 *)dst->data; + + if (left) + { + + for (y = 0; y < h; y++) + { + uint24 *ps = (uint24 *)(src->data + (h - 1 - y) * 3); + + for (x = 0; x < w; x++) + { + *pd++ = *ps; + ps += h; + } + } + } + else + { + for (y = 0; y < h; y++) + { + uint24 *ps = (uint24 *)(src->data + ((w - 1) * h + y) * 3); + + for (x = 0; x < w; x++) + { + *pd++ = *ps; + ps -= h; + } + } + } + } + else + { + uint *pd = (uint *)dst->data; + + if (left) + { + for (y = 0; y < h; y++) + { + uint *ps = (uint *)(src->data + (h - 1 - y) * 4); + + for (x = 0; x < w; x++) + { + *pd++ = *ps; + ps += h; + } + } + } + else + { + for (y = 0; y < h; y++) + { + uint *ps = (uint *)(src->data + ((w - 1) * h + y) * 4); + + for (x = 0; x < w; x++) + { + *pd++ = *ps; + ps -= h; + } + } + } + } + + MODIFY(dst); +} + +#if 0 +#define GET_RGBA(_col, _r, _g, _b, _a) { _r = RED(_col); _g = GREEN(_col); _b = BLUE(_col); _a = ALPHA(_col); } + +void IMAGE_transform(GB_IMG *dst, GB_IMG *src, double sx, double sy, double sdx, double sdy) +{ + int x, y; + double ssx; + double ssy; + uint col, cdp; + int ix, iy; + uchar r, g, b, a; + uchar rc, gc, bc, ac; + int wx, wy; + + uint *dp = (uint *)dst->data; + memset(dp, 0, IMAGE_size(dst)); + + dp += dst->width; + + for (y = 1; y < (dst->height - 1); y++) + { + ssx = sx; + ssy = sy; + + dp++; + for (x = 1; x < (dst->width - 1); x++) + { + ix = sx; + iy = sy; + + if (is_valid(src, ix, iy)) + { + col = BGRA_from_format(*((uint *)src->data + iy * src->width + ix), src->format); + + //dp[1] = RGBA(RED(col) * wx / 256, GREEN(col) * wx / 256, BLUE(col) * wx / 256, ALPHA(col) * wx / 256); + + wx = 128; + + cdp = BGRA_from_format(*dp, dst->format); + dp[0] = RGBA(RED(cdp) + RED(col) * wx / 256, GREEN(cdp) + GREEN(col) * wx / 256, BLUE(cdp) + BLUE(col) * wx / 256, ALPHA(col)); + + wx = 32; + + cdp = BGRA_from_format(dp[-1], dst->format); + dp[-1] = RGBA(RED(cdp) + RED(col) * wx / 256, GREEN(cdp) + GREEN(col) * wx / 256, BLUE(cdp) + BLUE(col) * wx / 256, ALPHA(col)); + + cdp = BGRA_from_format(dp[-dst->width], dst->format); + dp[-dst->width] = RGBA(RED(cdp) + RED(col) * wx / 256, GREEN(cdp) + GREEN(col) * wx / 256, BLUE(cdp) + BLUE(col) * wx / 256, ALPHA(col)); + + dp[1] = RGBA(RED(col) * wx / 256, GREEN(col) * wx / 256, BLUE(col) * wx / 256, 0); + + dp[dst->width] = dp[1]; //RGBA(RED(col) * wx / 256, GREEN(col) * wx / 256, BLUE(col) * wx / 256, ALPHA(col) * wx / 256); + } + + sx += sdx; + sy += sdy; + dp++; + } + dp++; + sx = ssx - sdy; + sy = ssy + sdx; + } +} +#endif + +/* +StackBlur - a fast almost Gaussian Blur For Canvas + +Version: 0.5 +Author: Mario Klingemann +Contact: mario@quasimondo.com +Website: http://www.quasimondo.com/StackBlurForCanvas +Twitter: @quasimondo + +In case you find this class useful - especially in commercial projects - +I am not totally unhappy for a small donation to my PayPal account +mario@quasimondo.de + +Or support me on flattr: +https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript + +Copyright (c) 2010 Mario Klingemann + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +*/ + +static const short mul_table[] = { + 512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512, + 454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512, + 482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456, + 437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512, + 497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328, + 320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456, + 446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335, + 329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512, + 505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405, + 399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328, + 324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271, + 268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456, + 451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388, + 385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335, + 332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292, + 289,287,285,282,280,278,275,273,271,269,267,265,263,261,259 +}; + + +static const char shg_table[] = { + 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, + 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 +}; + +typedef + struct _BLUR_STACK { + uchar r, g, b, a; + struct _BLUR_STACK *next; + } + BLUR_STACK; + +void IMAGE_blur(GB_IMG *img, int radius) //top_x, top_y, width, height, radius ) +{ + if (radius < 1) + return; + + if (radius >= 255) + radius = 254; + + if (img->is_void) + return; + + SYNCHRONIZE(img); + + uchar *pixels = (uchar *)img->data; + int width = img->width; + int height = img->height; + + uint x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum, + r_out_sum, g_out_sum, b_out_sum, a_out_sum, + r_in_sum, g_in_sum, b_in_sum, a_in_sum, rbs; + + uchar pr, pg, pb, pa; + + uint div = radius + radius + 1; + uint widthMinus1 = width - 1; + uint heightMinus1 = height - 1; + uint radiusPlus1 = radius + 1; + uint sumFactor = radiusPlus1 * ( radiusPlus1 + 1 ) / 2; + + BLUR_STACK *stackStart, *stackEnd, *stack; + + stackStart = alloca(sizeof(BLUR_STACK) * div); + + stack = stackStart; + stackEnd = stackStart + radiusPlus1; + + for (i = 1; i < div; i++) + { + stack->next = stack + 1; + stack++; + } + + stack->next = stackStart; + + BLUR_STACK *stackIn = NULL; + BLUR_STACK *stackOut = NULL; + + yw = yi = 0; + + uint mul_sum = mul_table[radius]; + uint shg_sum = shg_table[radius]; + + //fprintf(stderr, "blur: format = %d / %02X%02X%02X%02X\n", img->format, pixels[0], pixels[1], pixels[2], pixels[3]); + + if (GB_IMAGE_FMT_IS_32_BITS(img->format)) + { + for (y = 0; y < height; y++) + { + r_in_sum = g_in_sum = b_in_sum = a_in_sum = r_sum = g_sum = b_sum = a_sum = 0; + + pr = pixels[yi]; + pg = pixels[yi + 1]; + pb = pixels[yi + 2]; + pa = pixels[yi + 3]; + + r_out_sum = radiusPlus1 * pr; + g_out_sum = radiusPlus1 * pg; + b_out_sum = radiusPlus1 * pb; + a_out_sum = radiusPlus1 * pa; + + r_sum += sumFactor * pr; + g_sum += sumFactor * pg; + b_sum += sumFactor * pb; + a_sum += sumFactor * pa; + + stack = stackStart; + + for (i = 0; i < radiusPlus1; i++) + { + stack->r = pr; + stack->g = pg; + stack->b = pb; + stack->a = pa; + stack = stack->next; + } + + for (i = 1; i < radiusPlus1; i++) + { + p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 ); + rbs = radiusPlus1 - i; + + pr = pixels[p]; + pg = pixels[p + 1]; + pb = pixels[p + 2]; + pa = pixels[p + 3]; + + r_sum += pr * rbs; + g_sum += pg * rbs; + b_sum += pb * rbs; + a_sum += pa * rbs; + + r_in_sum += pr; + g_in_sum += pg; + b_in_sum += pb; + a_in_sum += pa; + + stack->r = pr; + stack->g = pg; + stack->b = pb; + stack->a = pa; + + stack = stack->next; + } + + + stackIn = stackStart; + stackOut = stackEnd; + for (x = 0; x < width; x++) + { + pixels[yi] = (r_sum * mul_sum) >> shg_sum; // * 255 / pa; + pixels[yi + 1] = (g_sum * mul_sum) >> shg_sum; // * 255 / pa; + pixels[yi + 2] = (b_sum * mul_sum) >> shg_sum; // * 255 / pa; + pixels[yi + 3] = (a_sum * mul_sum) >> shg_sum; + + r_sum -= r_out_sum; + g_sum -= g_out_sum; + b_sum -= b_out_sum; + a_sum -= a_out_sum; + + r_out_sum -= stackIn->r; + g_out_sum -= stackIn->g; + b_out_sum -= stackIn->b; + a_out_sum -= stackIn->a; + + p = x + radius + 1; + p = ( yw + ( p < widthMinus1 ? p : widthMinus1 ) ) << 2; + + r_in_sum += pixels[p]; + g_in_sum += pixels[p + 1]; + b_in_sum += pixels[p + 2]; + a_in_sum += pixels[p + 3]; + + r_sum += r_in_sum; + g_sum += g_in_sum; + b_sum += b_in_sum; + a_sum += a_in_sum; + + stackIn->r = pixels[p]; + stackIn->g = pixels[p + 1]; + stackIn->b = pixels[p + 2]; + stackIn->a = pixels[p + 3]; + stackIn = stackIn->next; + + r_out_sum += ( pr = stackOut->r ); + g_out_sum += ( pg = stackOut->g ); + b_out_sum += ( pb = stackOut->b ); + a_out_sum += ( pa = stackOut->a ); + + r_in_sum -= pr; + g_in_sum -= pg; + b_in_sum -= pb; + a_in_sum -= pa; + + stackOut = stackOut->next; + + yi += 4; + } + yw += width; + } + + for (x = 0; x < width; x++) + { + g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_sum = r_sum = 0; + + yi = x << 2; + + pr = pixels[yi]; + pg = pixels[yi + 1]; + pb = pixels[yi + 2]; + pa = pixels[yi + 3]; + + r_out_sum = radiusPlus1 * pr; + g_out_sum = radiusPlus1 * pg; + b_out_sum = radiusPlus1 * pb; + a_out_sum = radiusPlus1 * pa; + + r_sum += sumFactor * pr; + g_sum += sumFactor * pg; + b_sum += sumFactor * pb; + a_sum += sumFactor * pa; + + stack = stackStart; + + for (i = 0; i < radiusPlus1; i++) + { + stack->r = pr; + stack->g = pg; + stack->b = pb; + stack->a = pa; + stack = stack->next; + } + + yp = width; + + for (i = 1; i <= radius; i++) + { + yi = ( yp + x ) << 2; + + rbs = radiusPlus1 - i; + + pr = pixels[yi]; + pg = pixels[yi + 1]; + pb = pixels[yi + 2]; + pa = pixels[yi + 3]; + + r_sum += pr * rbs; + g_sum += pg * rbs; + b_sum += pb * rbs; + a_sum += pa * rbs; + + r_in_sum += pr; + g_in_sum += pg; + b_in_sum += pb; + a_in_sum += pa; + + stack->r = pr; + stack->g = pg; + stack->b = pb; + stack->a = pa; + stack = stack->next; + + if (i < heightMinus1) + yp += width; + } + + yi = x; + stackIn = stackStart; + stackOut = stackEnd; + for (y = 0; y < height; y++) + { + p = yi << 2; + + pixels[p] = (r_sum * mul_sum) >> shg_sum; + pixels[p + 1] = (g_sum * mul_sum) >> shg_sum; + pixels[p + 2] = (b_sum * mul_sum) >> shg_sum; + pixels[p + 3] = (a_sum * mul_sum) >> shg_sum; + + r_sum -= r_out_sum; + g_sum -= g_out_sum; + b_sum -= b_out_sum; + a_sum -= a_out_sum; + + r_out_sum -= stackIn->r; + g_out_sum -= stackIn->g; + b_out_sum -= stackIn->b; + a_out_sum -= stackIn->a; + + p = y + radiusPlus1; + p = ( x + (( p < heightMinus1 ? p : heightMinus1 ) * width )) << 2; + + r_sum += ( r_in_sum += pixels[p]); + g_sum += ( g_in_sum += pixels[p+1]); + b_sum += ( b_in_sum += pixels[p+2]); + a_sum += ( a_in_sum += pixels[p+3]); + + stackIn->r = pixels[p]; + stackIn->g = pixels[p+1]; + stackIn->b = pixels[p+2]; + stackIn->a = pixels[p+3]; + stackIn = stackIn->next; + + r_out_sum += ( pr = stackOut->r ); + g_out_sum += ( pg = stackOut->g ); + b_out_sum += ( pb = stackOut->b ); + a_out_sum += ( pa = stackOut->a ); + + r_in_sum -= pr; + g_in_sum -= pg; + b_in_sum -= pb; + a_in_sum -= pa; + + stackOut = stackOut->next; + + yi += width; + } + } + } + else // 24 bits ================================= + { + for (y = 0; y < height; y++) + { + r_in_sum = g_in_sum = b_in_sum = r_sum = g_sum = b_sum = 0; + + pr = pixels[yi]; + pg = pixels[yi + 1]; + pb = pixels[yi + 2]; + + r_out_sum = radiusPlus1 * pr; + g_out_sum = radiusPlus1 * pg; + b_out_sum = radiusPlus1 * pb; + + r_sum += sumFactor * pr; + g_sum += sumFactor * pg; + b_sum += sumFactor * pb; + + stack = stackStart; + + for (i = 0; i < radiusPlus1; i++) + { + stack->r = pr; + stack->g = pg; + stack->b = pb; + stack = stack->next; + } + + for (i = 1; i < radiusPlus1; i++) + { + p = yi + (( widthMinus1 < i ? widthMinus1 : i ) * 3 ); + rbs = radiusPlus1 - i; + + pr = pixels[p]; + pg = pixels[p + 1]; + pb = pixels[p + 2]; + + r_sum += pr * rbs; + g_sum += pg * rbs; + b_sum += pb * rbs; + + r_in_sum += pr; + g_in_sum += pg; + b_in_sum += pb; + + stack->r = pr; + stack->g = pg; + stack->b = pb; + + stack = stack->next; + } + + + stackIn = stackStart; + stackOut = stackEnd; + for (x = 0; x < width; x++) + { + pixels[yi] = (r_sum * mul_sum) >> shg_sum; // * 255 / pa; + pixels[yi + 1] = (g_sum * mul_sum) >> shg_sum; // * 255 / pa; + pixels[yi + 2] = (b_sum * mul_sum) >> shg_sum; // * 255 / pa; + + r_sum -= r_out_sum; + g_sum -= g_out_sum; + b_sum -= b_out_sum; + + r_out_sum -= stackIn->r; + g_out_sum -= stackIn->g; + b_out_sum -= stackIn->b; + + p = x + radius + 1; + p = ( yw + ( p < widthMinus1 ? p : widthMinus1 ) ) * 3; + + r_in_sum += pixels[p]; + g_in_sum += pixels[p + 1]; + b_in_sum += pixels[p + 2]; + + r_sum += r_in_sum; + g_sum += g_in_sum; + b_sum += b_in_sum; + + stackIn->r = pixels[p]; + stackIn->g = pixels[p + 1]; + stackIn->b = pixels[p + 2]; + stackIn = stackIn->next; + + r_out_sum += ( pr = stackOut->r ); + g_out_sum += ( pg = stackOut->g ); + b_out_sum += ( pb = stackOut->b ); + + r_in_sum -= pr; + g_in_sum -= pg; + b_in_sum -= pb; + + stackOut = stackOut->next; + + yi += 3; + } + yw += width; + } + + for (x = 0; x < width; x++) + { + g_in_sum = b_in_sum = r_in_sum = g_sum = b_sum = r_sum = 0; + + yi = x << 2; + + pr = pixels[yi]; + pg = pixels[yi + 1]; + pb = pixels[yi + 2]; + + r_out_sum = radiusPlus1 * pr; + g_out_sum = radiusPlus1 * pg; + b_out_sum = radiusPlus1 * pb; + + r_sum += sumFactor * pr; + g_sum += sumFactor * pg; + b_sum += sumFactor * pb; + + stack = stackStart; + + for (i = 0; i < radiusPlus1; i++) + { + stack->r = pr; + stack->g = pg; + stack->b = pb; + stack = stack->next; + } + + yp = width; + + for (i = 1; i <= radius; i++) + { + yi = ( yp + x ) << 2; + + rbs = radiusPlus1 - i; + + pr = pixels[yi]; + pg = pixels[yi + 1]; + pb = pixels[yi + 2]; + + r_sum += pr * rbs; + g_sum += pg * rbs; + b_sum += pb * rbs; + + r_in_sum += pr; + g_in_sum += pg; + b_in_sum += pb; + + stack->r = pr; + stack->g = pg; + stack->b = pb; + stack = stack->next; + + if (i < heightMinus1) + yp += width; + } + + yi = x; + stackIn = stackStart; + stackOut = stackEnd; + for (y = 0; y < height; y++) + { + p = yi << 2; + + pixels[p] = (r_sum * mul_sum) >> shg_sum; + pixels[p + 1] = (g_sum * mul_sum) >> shg_sum; + pixels[p + 2] = (b_sum * mul_sum) >> shg_sum; + + r_sum -= r_out_sum; + g_sum -= g_out_sum; + b_sum -= b_out_sum; + + r_out_sum -= stackIn->r; + g_out_sum -= stackIn->g; + b_out_sum -= stackIn->b; + + p = y + radiusPlus1; + p = ( x + (( p < heightMinus1 ? p : heightMinus1 ) * width )) * 3; + + r_sum += ( r_in_sum += pixels[p]); + g_sum += ( g_in_sum += pixels[p+1]); + b_sum += ( b_in_sum += pixels[p+2]); + + stackIn->r = pixels[p]; + stackIn->g = pixels[p + 1]; + stackIn->b = pixels[p + 2]; + stackIn = stackIn->next; + + r_out_sum += ( pr = stackOut->r ); + g_out_sum += ( pg = stackOut->g ); + b_out_sum += ( pb = stackOut->b ); + + r_in_sum -= pr; + g_in_sum -= pg; + b_in_sum -= pb; + + stackOut = stackOut->next; + + yi += width; + } + } + } + + MODIFY(img); +} + + +//--------------------------------------------------------------------------- + +static inline int between0And255 (int val) +{ + if (val < 0) + return 0; + else if (val > 255) + return 255; + else + return val; +} + +static inline int get_brightness(int base, int strength) +{ + if (strength == 0) return base; + return between0And255(base + strength); +} + +static inline int get_contrast(int base, int strength) +{ + if (strength == 0) return base; + return between0And255 ((base - 127) * (strength + 255) / 255 + 127); +} + +static inline int myRound(double d) +{ + return d >= 0.0 ? (int)(d + 0.5) : (int)( d - ((int)d-1) + 0.5 ) + ((int)d-1); +} + +static inline int get_gamma(int base, int strength) +{ + if (strength == 0) return base; + return between0And255(myRound(255.0 * pow(base / 255.0, 1.0 / pow(10, strength / 255.0)))); +} + + +void IMAGE_balance(GB_IMG *img, int brightness, int contrast, int gamma, int hue, int saturation, int lightness) +{ + GET_POINTER(img, p, pm); + uint col; + //int h, s, v; + //int r, g, b; + //int hcol, scol, vcol; + int format = img->format; + int i; + uchar *pp; + + SYNCHRONIZE(img); + + if (brightness || contrast || gamma) + { + uchar trgb[256]; + + for (i = 0; i < 256; i++) + trgb[i] = get_gamma(get_contrast(get_brightness(i, brightness), contrast), gamma); + + if (img->format == GB_IMAGE_BGRA || img->format == GB_IMAGE_RGBA) + { + while (p != pm) + { + pp = (uchar *)p; + pp[0] = trgb[pp[0]]; + pp[1] = trgb[pp[1]]; + pp[2] = trgb[pp[2]]; + p++; + } + } + else + { + while (p != pm) + { + col = BGRA_from_format(*p, format); + *p++ = BGRA_to_format(RGBA(trgb[RED(col)], trgb[GREEN(col)], trgb[BLUE(col)], ALPHA(col)), format); + } + } + } + + if (hue || saturation) + { + double sm; + uchar r, g, b; + + if (saturation < 0) + sm = 1 + saturation / 255.0; + else + sm = 1 + saturation / 255.0 * 2; + + double hue6 = (double)hue / 360 * 6; + + double l, h, s, v, m, f; + + p = (uint *)img->data; + while (p != pm) + { + col = BGRA_from_format(*p, format); + r = RED(col); g = GREEN(col); b = BLUE(col); + + uchar vs = r; + if (g > vs) vs = g; + if (b > vs) vs = b; + + uchar ms = r; + if (g < ms) ms = g; + if (b < ms) ms = b; + + uchar vm = (vs - ms); + + l = (double)(ms + vs) / 510; + + if (vs && vm) + { + if ((ms + vs) <= 255) + { + s = (double)vm / (vs + ms) * sm; + if (s > 1) s = 1; + v = l * (1 + s); + } + else + { + s = (double)vm / (510 - (vs + ms)) * sm; + if (s > 1) s = 1; + v = (l + s - l * s); + } + + if (r == vs) + { + if (g == ms) + h = 5 + (((double)vs-b)/vm) + hue6; + else + h = 1 - ((double)(vs-g)/vm) + hue6; + } + else if (g == vs) + { + if (b == ms) + h = 1 + ((double)(vs-r)/vm) + hue6; + else + h = 3 - ((double)(vs-b)/vm) + hue6; + } + else + { + if (r == ms) + h = 3 + ((double)(vs-g)/vm) + hue6; + else + h = 5 - ((double)(vs-r)/vm) + hue6; + } + + if (h < 0) h += 6; + if (h >= 6) h -= 6; + + m = l + l - v; + f = h - (int)h; + + switch((int)h) + { + case 0: + r = v*255; g = (m+((v-m)*f))*255; b = m*255; break; + case 1: + r = (v-((v-m)*f))*255; g = v*255; b = m*255; break; + case 2: + r = m*255; g = v*255; b = (m+((v-m)*f))*255; break; + case 3: + r = m*255; g = (v-((v-m)*f))*255; b = v*255; break; + case 4: + r = (m+((v-m)*f))*255; g = m*255; b = v*255; break; + case 5: + r = v*255; g = m*255; b = (v-((v-m)*f))*255; break; + } + } + + *p++ = BGRA_to_format(RGBA(between0And255(r), between0And255(g), between0And255(b), ALPHA(col)), format); + } + } + + if (lightness) + { + uchar trgb[256]; + double lp = 1 + lightness / 255.0; + double lm = 1 - lightness / 255.0; + + for (i = 0; i < 256; i++) + { + if (lightness < 0) + trgb[i] = between0And255(i * lp); + else + trgb[i] = between0And255(i * lm + lightness); + } + + p = (uint *)img->data; + + if (img->format == GB_IMAGE_BGRA || img->format == GB_IMAGE_RGBA) + { + while (p != pm) + { + pp = (uchar *)p; + pp[0] = trgb[pp[0]]; + pp[1] = trgb[pp[1]]; + pp[2] = trgb[pp[2]]; + p++; + } + } + else + { + while (p != pm) + { + col = BGRA_from_format(*p, format); + *p++ = BGRA_to_format(RGBA(trgb[RED(col)], trgb[GREEN(col)], trgb[BLUE(col)], ALPHA(col)), format); + } + } + } + + MODIFY(img); +} + + +//--------------------------------------------------------------------------- + +void IMAGE_invert(GB_IMG *img, bool keep_hue) // GB_COLOR bg, GB_COLOR fg) +{ + GET_POINTER(img, p, pm); + uint col; + int format = img->format; + int h, s, v; + int r, g, b; + + SYNCHRONIZE(img); + + if (!keep_hue) //bg == COLOR_DEFAULT || fg == COLOR_DEFAULT) + { + while (p != pm) + { + col = BGRA_from_format(*p, format); + *p++ = BGRA_to_format(RGBA(255 - RED(col), 255 - GREEN(col), 255 - BLUE(col), ALPHA(col)), format); + } + } + else + { + while (p != pm) + { + col = BGRA_from_format(*p, format); + COLOR_rgb_to_hsv(RED(col), GREEN(col), BLUE(col), &h, &s, &v); + v = (int)sqrt(255 * 255 - v * v); + //if (h >= 0) s = (int)sqrt(255 * 255 - s * s);; + COLOR_hsv_to_rgb(h, s, v, &r, &g, &b); + *p++ = BGRA_to_format(RGBA(r, g, b, ALPHA(col)), format); + } + } + /*else + { + bl = COLOR_get_luminance(bg); + fl = COLOR_get_luminance(fg); + + for (i = 0; i < 256; i++) + lum[i] = (int)sqrt(fl * fl - (i * i - bl * bl)); + + while (p != pm) + { + col = BGRA_from_format(*p, format); + col = COLOR_set_luminance(col, lum[COLOR_get_luminance(col)]); + *p++ = BGRA_to_format(col, format); + } + }*/ + + MODIFY(img); +} diff --git a/main/lib/image/image.h b/main/lib/image/image.h new file mode 100644 index 00000000..2c9c7402 --- /dev/null +++ b/main/lib/image/image.h @@ -0,0 +1,89 @@ +/*************************************************************************** + + image.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __IMAGE_H +#define __IMAGE_H + +#include "main.h" + +#ifndef __IMAGE_C +extern bool IMAGE_debug; +#endif + +static inline int RED(uint rgba) { return ((rgba >> 16) & 0xff); } +static inline int GREEN(uint rgba) { return ((rgba >> 8) & 0xff); } +static inline int BLUE(uint rgba) { return (rgba & 0xff); } +static inline int ALPHA(uint rgba) { return ((rgba >> 24) & 0xff); } + +static inline uint RGB(int r, int g, int b) { return (0xff << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); } +static inline uint RGBA(int r, int g, int b, int a) { return ((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); } +static inline int GRAY(uint rgba) { return (RED(rgba) * 11 + GREEN(rgba) * 16 + BLUE(rgba) * 5) / 32; } + +int IMAGE_size(GB_IMG *img); + +void IMAGE_create(GB_IMG *img, int width, int height, int format); +void IMAGE_create_with_data(GB_IMG *img, int width, int height, int format, unsigned char *data); + +void IMAGE_take(GB_IMG *img, GB_IMG_OWNER *owner, void *owner_handle, int width, int height, unsigned char *data); +void *IMAGE_check(GB_IMG *img, GB_IMG_OWNER *temp_owner); +void IMAGE_synchronize(GB_IMG *img); + +void IMAGE_delete(GB_IMG *img); + +void IMAGE_convert(GB_IMG *img, int format); + +void IMAGE_fill(GB_IMG *img, GB_COLOR col); +void IMAGE_fill_rect(GB_IMG *img, int x, int y, int w, int h, GB_COLOR col, bool opaque); + +void IMAGE_make_gray(GB_IMG *img); +void IMAGE_make_transparent(GB_IMG *img, GB_COLOR color); + +GB_COLOR IMAGE_get_pixel(GB_IMG *img, int x, int y); +void IMAGE_set_pixel(GB_IMG *img, int x, int y, GB_COLOR col); + +void IMAGE_get_pixels(GB_IMG *img, int *data); +void IMAGE_set_pixels(GB_IMG *img, int *data); + +void IMAGE_replace(GB_IMG *img, GB_COLOR src, GB_COLOR dst, bool noteq); + +void IMAGE_set_default_format(int format); +int IMAGE_get_default_format(); +const char *IMAGE_format_to_string(int fmt); +int IMAGE_format_from_string(char *fmt); + +void IMAGE_bitblt(GB_IMG *dst, int dx, int dy, int dw, int dh, GB_IMG *src, int sx, int sy, int sw, int sh); +void IMAGE_draw_alpha(GB_IMG *dst, int dx, int dy, GB_IMG *src, int sx, int sy, int sw, int sh); +void IMAGE_compose(GB_IMG *dst, int dx, int dy, int dw, int dh, GB_IMG *src, int sx, int sy, int sw, int sh); +void IMAGE_colorize(GB_IMG *img, GB_COLOR color); +void IMAGE_mask(GB_IMG *img, GB_COLOR color); +void IMAGE_mirror(GB_IMG *src, GB_IMG *dst, bool horizontal, bool vertical); +void IMAGE_rotate(GB_IMG *src, GB_IMG *dst, bool left); +void IMAGE_transform(GB_IMG *dst, GB_IMG *src, double sx, double sy, double sdx, double sdy); +void IMAGE_set_opacity(GB_IMG *dst, uchar opacity); +void IMAGE_blur(GB_IMG *img, int radius); +void IMAGE_balance(GB_IMG *img, int brightness, int contrast, int gamma, int hue, int saturation, int lightness); +void IMAGE_invert(GB_IMG *img, bool keep_hue); + +#define IMAGE_is_void(_image) ((_image)->is_void) + +#endif diff --git a/main/lib/image/image_stat.c b/main/lib/image/image_stat.c new file mode 100644 index 00000000..dbfd95f2 --- /dev/null +++ b/main/lib/image/image_stat.c @@ -0,0 +1,624 @@ +/*************************************************************************** + + image_stat.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __IMAGE_STAT_C + +#include +#include + +#include "image_stat.h" + +const char *IMAGE_error = NULL; + +static const char _sign_gif[3] = { 'G', 'I', 'F' }; +static const char _sign_bmp[2] = { 'B', 'M' }; +static const char _sign_jpg[3] = { 0xff, 0xd8, 0xff }; +static const char _sign_png[8] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a }; +static const char _sign_tif_ii[4] = { 'I', 'I', 0x2A, 0x00 }; +static const char _sign_tif_mm[4] = { 'M', 'M', 0x00, 0x2A }; + +static int stream_seek(IMAGE_STREAM *stream, int pos, int whence) +{ + switch (whence) + { + case SEEK_CUR: + if ((stream->pos + pos) >= stream->len) + return 1; + if ((stream->pos + pos) < 0) + return 1; + stream->pos += pos; + return 0; + + case SEEK_SET: + if (pos < 0 || pos >= stream->len) + return 1; + stream->pos = pos; + return 0; + + default: + return 1; + } +} + +static int stream_read(IMAGE_STREAM *stream, void *addr, int len) +{ + int lmax = stream->len - stream->pos; + + if (len > lmax) + len = lmax; + + memcpy(addr, stream->addr + stream->pos, len); + stream->pos += len; + return len; +} + + +static int stream_getc(IMAGE_STREAM *stream) +{ + if (stream->pos >= stream->len) + return EOF; + + return (uchar)stream->addr[stream->pos++]; +} + + +static ushort read_ushort(IMAGE_STREAM * stream) +{ + uchar a[2]; + + /* returns 0 if we hit the end-of-file */ + if((stream_read(stream, a, 2)) < 2) + return 0; + + //fprintf(stderr, "read_ushort -> %d\n", (int)(((ushort)a[0]) << 8) + ((ushort)a[1])); + return (((ushort)a[0]) << 8) + ((ushort)a[1]); +} + + +static int get_ushort_at(void *pshort, int big_endian) +{ + if (big_endian) + return (((uchar *)pshort)[0] << 8) | ((uchar *)pshort)[1]; + else + return (((uchar *)pshort)[1] << 8) | ((uchar *)pshort)[0]; +} + +static signed short get_short_at(void *pshort, int big_endian) +{ + return (signed short)get_ushort_at(pshort, big_endian); +} + +static int get_int_at(void *pint, int big_endian) +{ + if (big_endian) + return (((char*)pint)[0] << 24) | (((uchar *)pint)[1] << 16) | (((uchar *)pint)[2] << 8 ) | (((uchar *)pint)[3] << 0); + else + return (((char*)pint)[3] << 24) | (((uchar *)pint)[2] << 16) | (((uchar *)pint)[1] << 8 ) | (((uchar *)pint)[0] << 0); +} + +static unsigned get_uint_at(void *pint, int big_endian) +{ + return (uint)get_int_at(pint, big_endian) & 0xffffffff; +} + + +//------------------------------------------------------------------------- + +static bool handle_gif (IMAGE_STREAM * stream, IMAGE_INFO *result) +{ + uchar dim[5]; + + if (stream_seek(stream, 3, SEEK_CUR)) + return TRUE; + + if (stream_read(stream, dim, sizeof(dim)) != sizeof(dim)) + return TRUE; + + result->width = (uint)dim[0] | (((uint)dim[1]) << 8); + result->height = (uint)dim[2] | (((uint)dim[3]) << 8); + result->depth = (dim[4] & 0x80) ? ((((uint)dim[4]) & 0x07) + 1) : 0; + return FALSE; +} + +//------------------------------------------------------------------------- + +static bool handle_bmp (IMAGE_STREAM * stream, IMAGE_INFO *result) +{ + uchar dim[16]; + int size; + + if (stream_seek(stream, 11, SEEK_CUR)) + return TRUE; + + if (stream_read(stream, dim, sizeof(dim)) != sizeof(dim)) + return TRUE; + + size = (((uint)dim[3]) << 24) + (((uint)dim[2]) << 16) + (((uint)dim[1]) << 8) + ((uint) dim[0]); + if (size == 12) + { + result->width = (((uint)dim[5]) << 8) + ((uint)dim[4]); + result->height = (((uint)dim[7]) << 8) + ((uint) dim[6]); + result->depth = ((uint)dim[11]); + return FALSE; + } + else if (size > 12 && (size <= 64 || size == 108)) + { + result->width = (((uint)dim[7]) << 24) + (((uint)dim[6]) << 16) + (((uint)dim[5]) << 8) + ((uint) dim[4]); + result->height = (((uint)dim[11]) << 24) + (((uint)dim[10]) << 16) + (((uint)dim[9]) << 8) + ((uint) dim[8]); + result->depth = (((uint)dim[15]) << 8) + ((uint)dim[14]); + return FALSE; + } + + return TRUE; +} + +//------------------------------------------------------------------------- + +static bool handle_png (IMAGE_STREAM * stream, IMAGE_INFO *result) +{ + uchar dim[13]; + + if (stream_seek(stream, 8, SEEK_CUR)) + return TRUE; + + if((stream_read(stream, dim, sizeof(dim))) < sizeof(dim)) + return TRUE; + + result->width = (((uint)dim[0]) << 24) + (((uint)dim[1]) << 16) + (((uint)dim[2]) << 8) + ((uint)dim[3]); + result->height = (((uint)dim[4]) << 24) + (((uint)dim[5]) << 16) + (((uint)dim[6]) << 8) + ((uint)dim[7]); + + switch (dim[9]) + { + case 0: result->depth = 8; break; + case 2: result->depth = 24; break; + case 3: result->depth = 8; break; + case 4: result->depth = 32; break; + case 6: result->depth = 32; break; + default: result->depth = 32; break; + } + + return FALSE; +} + +//------------------------------------------------------------------------- + +/* some defines for the different JPEG block types */ +#define M_SOF0 0xC0 /* Start Of Frame N */ +#define M_SOF1 0xC1 /* N indicates which compression process */ +#define M_SOF2 0xC2 /* Only SOF0-SOF2 are now in common use */ +#define M_SOF3 0xC3 +#define M_SOF5 0xC5 /* NB: codes C4 and CC are NOT SOF markers */ +#define M_SOF6 0xC6 +#define M_SOF7 0xC7 +#define M_SOF9 0xC9 +#define M_SOF10 0xCA +#define M_SOF11 0xCB +#define M_SOF13 0xCD +#define M_SOF14 0xCE +#define M_SOF15 0xCF +#define M_SOI 0xD8 +#define M_EOI 0xD9 /* End Of Image (end of datastream) */ +#define M_SOS 0xDA /* Start Of Scan (begins compressed data) */ +#define M_APP0 0xE0 +#define M_APP1 0xE1 +#define M_APP2 0xE2 +#define M_APP3 0xE3 +#define M_APP4 0xE4 +#define M_APP5 0xE5 +#define M_APP6 0xE6 +#define M_APP7 0xE7 +#define M_APP8 0xE8 +#define M_APP9 0xE9 +#define M_APP10 0xEA +#define M_APP11 0xEB +#define M_APP12 0xEC +#define M_APP13 0xED +#define M_APP14 0xEE +#define M_APP15 0xEF +#define M_COM 0xFE /* COMment */ + +#define M_PSEUDO 0xFFD8 /* pseudo marker for start of image(byte 0) */ + +static uint next_marker(IMAGE_STREAM * stream, int last_marker, int comment_correction, int ff_read) +{ + int a=0, marker; + + /* get marker byte, swallowing possible padding */ + if (last_marker == M_COM && comment_correction) + { + /* some software does not count the length bytes of COM section */ + /* one company doing so is very much envolved in JPEG... so we accept too */ + /* by the way: some of those companies changed their code now... */ + comment_correction = 2; + } + else + { + last_marker = 0; + comment_correction = 0; + } + + if (ff_read) + a = 1; /* already read 0xff in filetype detection */ + + do + { + if ((marker = stream_getc(stream)) == EOF) + return M_EOI;/* we hit EOF */ + + if (last_marker == M_COM && comment_correction > 0) + { + if (marker != 0xFF) + { + marker = 0xff; + comment_correction--; + } + else + { + last_marker = M_PSEUDO; /* stop skipping non 0xff for M_COM */ + } + } + + if (++a > 25) + { + /* who knows the maxim amount of 0xff? though 7 */ + /* but found other implementations */ + return M_EOI; + } + } + while (marker == 0xff); + + if (a < 2) + return M_EOI; /* at least one 0xff is needed before marker code */ + + if (last_marker == M_COM && comment_correction) + return M_EOI; /* ah illegal: char after COM section not 0xFF */ + + return (uint)marker; +} + +/* skip over a variable-length block; assumes proper length marker */ +static bool skip_variable(IMAGE_STREAM * stream) +{ + int length = read_ushort(stream); + + if (length < 2) + return 0; + + length -= 2; + + return stream_seek(stream, length, SEEK_CUR) == 0; +} + + +static bool handle_jpeg(IMAGE_STREAM * stream, IMAGE_INFO *result) +{ + uint marker = M_PSEUDO; + ushort ff_read = 1; + bool ret = TRUE; + + for(;;) + { + marker = next_marker(stream, marker, 1, ff_read); + ff_read = 0; + + //fprintf(stderr, "marker = 0x%02X\n", marker); + + switch (marker) + { + case M_SOF0: + case M_SOF1: + case M_SOF2: + case M_SOF3: + case M_SOF5: + case M_SOF6: + case M_SOF7: + case M_SOF9: + case M_SOF10: + case M_SOF11: + case M_SOF13: + case M_SOF14: + case M_SOF15: + + read_ushort(stream); + stream_getc(stream); + result->height = read_ushort(stream); + result->width = read_ushort(stream); + stream_getc(stream); + result->depth = 24; + return FALSE; + + case M_APP0: + case M_APP1: + case M_APP2: + case M_APP3: + case M_APP4: + case M_APP5: + case M_APP6: + case M_APP7: + case M_APP8: + case M_APP9: + case M_APP10: + case M_APP11: + case M_APP12: + case M_APP13: + case M_APP14: + case M_APP15: + + if (!skip_variable(stream)) + return ret; + + break; + + case M_SOS: + case M_EOI: + + return ret; /* we're about to hit image data, or are at EOF. stop processing. */ + + default: + + if (!skip_variable(stream)) /* anything else isn't interesting */ + return ret; + + break; + } + } + + return ret; /* perhaps image broken -> no info but size */ +} + +//------------------------------------------------------------------------- + +//static const int _tiff_bytes_per_format[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8}; + +/* uncompressed only */ +#define TAG_IMAGEWIDTH 0x0100 +#define TAG_IMAGEHEIGHT 0x0101 +/* compressed images only */ +#define TAG_COMP_IMAGEWIDTH 0xA002 +#define TAG_COMP_IMAGEHEIGHT 0xA003 + +#define TAG_FMT_BYTE 1 +#define TAG_FMT_STRING 2 +#define TAG_FMT_USHORT 3 +#define TAG_FMT_ULONG 4 +#define TAG_FMT_URATIONAL 5 +#define TAG_FMT_SBYTE 6 +#define TAG_FMT_UNDEFINED 7 +#define TAG_FMT_SSHORT 8 +#define TAG_FMT_SLONG 9 +#define TAG_FMT_SRATIONAL 10 +#define TAG_FMT_SINGLE 11 +#define TAG_FMT_DOUBLE 12 + +static bool handle_tiff(IMAGE_STREAM * stream, IMAGE_INFO *result, bool big_endian) +{ + int i, num_entries; + uchar *dir_entry; + size_t ifd_size, dir_size, entry_value, ifd_addr; + int entry_tag , entry_type; + char *ifd_data, ifd_ptr[4]; + int width = 0, height = 0; + + if (stream_read(stream, ifd_ptr, 4) != 4) + return TRUE; + + ifd_addr = get_uint_at(ifd_ptr, big_endian); + if (stream_seek(stream, ifd_addr - 8, SEEK_CUR)) + return TRUE; + + ifd_size = 2; + GB.Alloc(POINTER(&ifd_data), ifd_size); + + if (stream_read(stream, ifd_data, 2) != 2) + { + GB.Free(POINTER(&ifd_data)); + return TRUE; + } + + num_entries = get_ushort_at(ifd_data, big_endian); + + // dir_size = + * + + dir_size = 2 + 12 * num_entries + 4; + + ifd_size = dir_size; + GB.Realloc(POINTER(&ifd_data), ifd_size); + + if (stream_read(stream, ifd_data + 2, dir_size - 2) != dir_size - 2) + { + GB.Free(POINTER(&ifd_data)); + return TRUE; + } + + /* now we have the directory we can look how long it should be */ + ifd_size = dir_size; + for(i = 0; i < num_entries; i++) + { + dir_entry = (uchar *)(ifd_data + 2 + i * 12); + entry_tag = get_ushort_at(dir_entry + 0, big_endian); + entry_type = get_ushort_at(dir_entry + 2, big_endian); + + switch(entry_type) + { + case TAG_FMT_BYTE: + case TAG_FMT_SBYTE: + entry_value = (size_t)(dir_entry[8]); + break; + case TAG_FMT_USHORT: + entry_value = get_ushort_at(dir_entry+8, big_endian); + break; + case TAG_FMT_SSHORT: + entry_value = get_short_at(dir_entry+8, big_endian); + break; + case TAG_FMT_ULONG: + entry_value = get_uint_at(dir_entry+8, big_endian); + break; + case TAG_FMT_SLONG: + entry_value = get_int_at(dir_entry+8, big_endian); + break; + default: + continue; + } + + switch(entry_tag) + { + case TAG_IMAGEWIDTH: + case TAG_COMP_IMAGEWIDTH: + width = entry_value; + break; + + case TAG_IMAGEHEIGHT: + case TAG_COMP_IMAGEHEIGHT: + height = entry_value; + break; + } + } + + GB.Free(POINTER(&ifd_data)); + + if (width > 0 && height > 0) + { + result->height = height; + result->width = width; + result->depth = 24; + return FALSE; + } + + return TRUE; +} + +//------------------------------------------------------------------------- + +static char *image_type_to_mime_type(int type) +{ + switch(type) + { + case IMAGE_TYPE_GIF: + return "image/gif"; + case IMAGE_TYPE_JPEG: + return "image/jpeg"; + case IMAGE_TYPE_PNG: + return "image/png"; + case IMAGE_TYPE_BMP: + return "image/bmp"; + case IMAGE_TYPE_TIFF_INTEL: + case IMAGE_TYPE_TIFF_MOTOROLA: + return "image/tiff"; + default: + case IMAGE_TYPE_UNKNOWN: + return "application/octet-stream"; + } +} + +static int get_image_type(IMAGE_STREAM * stream) +{ + char buffer[12]; + + if((stream_read(stream, buffer, 3)) != 3) + { + IMAGE_error = "Read error"; + return IMAGE_TYPE_ERROR; + } + + if (memcmp(buffer, _sign_gif, 3) == 0) + return IMAGE_TYPE_GIF; + + if (memcmp(buffer, _sign_jpg, 3) == 0) + return IMAGE_TYPE_JPEG; + + if (memcmp(buffer, _sign_png, 3) == 0) + { + if (stream_read(stream, buffer + 3, 5) != 5) + { + IMAGE_error = "Read error"; + return IMAGE_TYPE_ERROR; + } + + if (memcmp(buffer, _sign_png, 8) == 0) + return IMAGE_TYPE_PNG; + + IMAGE_error = "PNG file is corrupted"; + return IMAGE_TYPE_ERROR; + } + + if (memcmp(buffer, _sign_bmp, 2) == 0) + return IMAGE_TYPE_BMP; + + if (stream_read(stream, buffer + 3, 1) != 1) + { + IMAGE_error = "Read error"; + return IMAGE_TYPE_ERROR; + } + + if (memcmp(buffer, _sign_tif_ii, 4) == 0) + return IMAGE_TYPE_TIFF_INTEL; + + if (memcmp(buffer, _sign_tif_mm, 4) == 0) + return IMAGE_TYPE_TIFF_MOTOROLA; + + return IMAGE_TYPE_UNKNOWN; +} + + +bool IMAGE_get_info(IMAGE_STREAM *stream, IMAGE_INFO *result) +{ + int type; + bool err; + + type = get_image_type(stream); + if (type == IMAGE_TYPE_ERROR) + return TRUE; + + result->type = image_type_to_mime_type(type); + + switch(type) + { + case IMAGE_TYPE_GIF: + err = handle_gif(stream, result); + break; + case IMAGE_TYPE_JPEG: + err = handle_jpeg(stream, result); + break; + case IMAGE_TYPE_PNG: + err = handle_png(stream, result); + break; + case IMAGE_TYPE_BMP: + err = handle_bmp(stream, result); + break; + case IMAGE_TYPE_TIFF_INTEL: + err = handle_tiff(stream, result, FALSE); + break; + case IMAGE_TYPE_TIFF_MOTOROLA: + err = handle_tiff(stream, result, TRUE); + break; + default: + case IMAGE_TYPE_UNKNOWN: + err = FALSE; + break; + } + + if (err) + IMAGE_error = "Cannot read file"; + + return err; +} + diff --git a/main/lib/image/image_stat.h b/main/lib/image/image_stat.h new file mode 100644 index 00000000..49275e09 --- /dev/null +++ b/main/lib/image/image_stat.h @@ -0,0 +1,66 @@ +/*************************************************************************** + + image_stat.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __IMAGE_STAT_H +#define __IMAGE_STAT_H + +#include "main.h" + +typedef + struct { + char *addr; + int len; + int pos; + } + IMAGE_STREAM; + +typedef + enum + { + IMAGE_TYPE_ERROR = -1, + IMAGE_TYPE_UNKNOWN = 0, + IMAGE_TYPE_GIF = 1, + IMAGE_TYPE_JPEG, + IMAGE_TYPE_PNG, + IMAGE_TYPE_BMP, + IMAGE_TYPE_TIFF_INTEL, + IMAGE_TYPE_TIFF_MOTOROLA + } + IMAGE_FILETYPE; + +typedef + struct { + char *type; + unsigned int width; + unsigned int height; + unsigned int depth; + } + IMAGE_INFO; + +#ifndef __IMAGE_STAT_C +extern const char *IMAGE_error; +#endif + +bool IMAGE_get_info(IMAGE_STREAM *stream, IMAGE_INFO *info); + +#endif \ No newline at end of file diff --git a/main/lib/image/main.c b/main/lib/image/main.c new file mode 100644 index 00000000..9b12edbb --- /dev/null +++ b/main/lib/image/main.c @@ -0,0 +1,82 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include + +#include "CImage.h" +#include "CImageStat.h" +#include "c_color.h" +#include "image.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; + + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CColorInfoDesc, + CColorDesc, + CImageDesc, + CImageStatDesc, + NULL +}; + +static GB_IMG *create_image(int width, int height, int format, unsigned char *data) +{ + CIMAGE *image; + + image = GB.New(GB.FindClass("Image"), NULL, NULL); + IMAGE_create_with_data(&image->image, width, height, format, data); + return (GB_IMG *)image; +} + +void *GB_IMAGE_1[] EXPORT = +{ + (void *)IMAGE_INTERFACE_VERSION, + (void *)create_image, + (void *)IMAGE_take, + (void *)IMAGE_check, + (void *)IMAGE_synchronize, + (void *)IMAGE_size, + (void *)IMAGE_set_default_format, + (void *)IMAGE_get_default_format, + (void *)IMAGE_get_pixel, + (void *)IMAGE_convert, + (void *)COLOR_merge, + (void *)COLOR_lighter, + (void *)COLOR_darker, + (void *)IMAGE_format_to_string, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/main/lib/image/main.h b/main/lib/image/main.h new file mode 100644 index 00000000..16984e48 --- /dev/null +++ b/main/lib/image/main.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" +#include "gb.image.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif /* __MAIN_H */ diff --git a/main/lib/inotify/Makefile.am b/main/lib/inotify/Makefile.am new file mode 100644 index 00000000..771f0ba9 --- /dev/null +++ b/main/lib/inotify/Makefile.am @@ -0,0 +1,13 @@ +COMPONENT = gb.inotify +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.inotify.la + +gb_inotify_la_LIBADD = @INOTIFY_LIB@ +gb_inotify_la_LDFLAGS = -module @LD_FLAGS@ @INOTIFY_LDFLAGS@ +gb_inotify_la_CPPFLAGS = @INOTIFY_INC@ -I$(top_srcdir)/share + +gb_inotify_la_SOURCES = \ + main.h main.c \ + gb_list.c \ + c_watch.h c_watch.c diff --git a/main/lib/inotify/TODO b/main/lib/inotify/TODO new file mode 100644 index 00000000..39c32a66 --- /dev/null +++ b/main/lib/inotify/TODO @@ -0,0 +1,5 @@ + - Work on portability to non-Linux filesystem monitoring systems. + I've to look at: pnotify[0] or rather libkqueue[1] + +[0] https://code.google.com/p/pnotify +[1] http://sourceforge.net/projects/libkqueue diff --git a/main/lib/inotify/c_watch.c b/main/lib/inotify/c_watch.c new file mode 100644 index 00000000..aa01afd6 --- /dev/null +++ b/main/lib/inotify/c_watch.c @@ -0,0 +1,697 @@ +/* + * c_watch.c - Watch and .Watch.Events classes + * + * Copyright (C) 2013, 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __C_WATCH_C + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "c_watch.h" + +//#define DEBUG_ME 1 + +#define GB_ErrorErrno() GB.Error(strerror(errno)) + +enum { EV_READ, EV_STAT, EV_CLOSE, EV_CREATE, EV_DELETE, EV_WRITE, EV_MOVE, EV_MOVED_FROM, EV_MOVED_TO, EV_OPEN, NUM_EV }; + +typedef + struct cinfo CINFO; + +struct cinfo { + struct inotify_event *iev; + CINFO *prev; +}; + +typedef + struct { + int fd; + GB_HASHTABLE watches; + CINFO *top; + } + CINOTIFY; + +typedef + struct { + CWATCH *root; + char *path; + int wd; + int events[NUM_EV]; + } + WATCH_LIST; + +typedef + struct { + int *eventp; + uint32_t mask; + } + EVENT_TABLE; + +/* XXX: We currently only support one inotify instance */ +static CINOTIFY _ino = {-1, NULL, NULL}; + +DECLARE_EVENT(EVENT_Read); +DECLARE_EVENT(EVENT_Stat); +DECLARE_EVENT(EVENT_Close); +DECLARE_EVENT(EVENT_Create); +DECLARE_EVENT(EVENT_Delete); +DECLARE_EVENT(EVENT_Write); +DECLARE_EVENT(EVENT_Move); +DECLARE_EVENT(EVENT_MoveFrom); +DECLARE_EVENT(EVENT_MoveTo); +DECLARE_EVENT(EVENT_Open); + +static EVENT_TABLE _event_table[] = +{ + {&EVENT_Read, IN_ACCESS}, + {&EVENT_Stat, IN_ATTRIB}, + {&EVENT_Close, IN_CLOSE_WRITE | IN_CLOSE_NOWRITE}, + {&EVENT_Create, IN_CREATE}, + {&EVENT_Delete, IN_DELETE | IN_DELETE_SELF}, + {&EVENT_Write, IN_MODIFY}, + {&EVENT_Move, IN_MOVE_SELF}, + {&EVENT_MoveFrom, IN_MOVED_FROM}, + {&EVENT_MoveTo, IN_MOVED_TO}, + {&EVENT_Open, IN_OPEN} +}; + +static bool destroy_watch(CWATCH *watch); +static void callback(int fd, int flags, CINOTIFY *ino); + +static void init_inotify(void) +{ + if (_ino.fd >= 0) + return; + +#if DEBUG_ME + fprintf(stderr, "init_inotify\n"); +#endif + + _ino.fd = inotify_init(); + if (_ino.fd == -1) { + GB_ErrorErrno(); + return; + } + GB.HashTable.New(&_ino.watches, GB_COMP_BINARY); + GB.Watch(_ino.fd, GB_WATCH_READ, callback, (intptr_t) &_ino); + _ino.top = NULL; +} + +static void destroy_watch_list(WATCH_LIST *list) +{ + while (!destroy_watch(list->root)); +} + +static void exit_inotify(void) +{ + WATCH_LIST *list; + int fd = _ino.fd; // prevent a recursion + + if (_ino.fd < 0) + return; + + _ino.fd = -1; + +#if DEBUG_ME + fprintf(stderr, "exit_inotify\n"); +#endif + + while (!GB.HashTable.First(_ino.watches, (void **)&list)) + destroy_watch_list(list); + + GB.Watch(fd, GB_WATCH_NONE, NULL, 0); + close(fd); + GB.HashTable.Free(&_ino.watches); +} + +static WATCH_LIST *find_watch_list(CINOTIFY *ino, int wd) +{ + WATCH_LIST *list = NULL; + GB.HashTable.Get(ino->watches, (char *)&wd, sizeof(wd), (void **)&list); + return list; +} + +static WATCH_LIST *find_watch_list_from_path(CINOTIFY *ino, const char *path, int len, bool create) +{ + WATCH_LIST *list = NULL; + GB.HashTable.Get(ino->watches, path, len, (void **)&list); + + if (!list && create) + { +#if DEBUG_ME + fprintf(stderr, "find_watch_list_from_path: create watch list for %.*s\n", len, path); +#endif + GB.AllocZero(POINTER(&list), sizeof(WATCH_LIST)); + list->wd = -1; + list->path = GB.NewString(path, len); + GB.HashTable.Add(ino->watches, path, len, list); + } + + return list; +} + +static void update_watch_list(WATCH_LIST *list) +{ + int i; + uint32_t mask = 0; + + for (i = 0; i < NUM_EV; i++) + { + if (list->events[i]) + mask |= _event_table[i].mask; + } + + if (mask == 0) + { + if (list->wd >= 0) + { +#if DEBUG_ME + fprintf(stderr, "update_watch_list: remove watch %d\n", list->wd); +#endif + GB.HashTable.Remove(_ino.watches, (char *)&list->wd, sizeof(list->wd)); + inotify_rm_watch(_ino.fd, list->wd); + list->wd = -1; + } + } + else + { + int wd = inotify_add_watch(_ino.fd, list->path, mask); + + if (wd >= 0 && list->wd != wd) + { +#if DEBUG_ME + fprintf(stderr, "update_watch_list: add watch %d %08X\n", wd, mask); +#endif + list->wd = wd; + GB.HashTable.Add(_ino.watches, (char *)&wd, sizeof(wd), list); + } + } +} + +static void create_watch(CWATCH *watch, const char *path, int len) +{ + int i; + WATCH_LIST *list = find_watch_list_from_path(&_ino, path, len, TRUE); + + watch->root = list; + +#if DEBUG_ME + fprintf(stderr, "create_watch: %p %.*s\n", watch, len, path); +#endif + + LIST_insert(&list->root, watch, &watch->list); + + for (i = 0; i < NUM_EV; i++) + { + if (watch->events & (1 << i)) + list->events[i]++; + } + + update_watch_list(list); +} + +static bool destroy_watch(CWATCH *watch) +{ + int i; + WATCH_LIST *list = (WATCH_LIST *)watch->root; + + if (!list) + return FALSE; + + watch->root = NULL; + +#if DEBUG_ME + fprintf(stderr, "destroy_watch: %p %s\n", watch, list->path); +#endif + + GB.StoreVariant(NULL, &watch->tag); + LIST_remove(&list->root, watch, &watch->list); + + for (i = 0; i < NUM_EV; i++) + { + if (watch->events & (1 << i)) + list->events[i]--; + } + + update_watch_list(list); + + if (list->root == NULL) + { + GB.HashTable.Remove(_ino.watches, list->path, GB.StringLength(list->path)); + +#if DEBUG_ME + fprintf(stderr, "destroy_watch: remove watch list: %s -> %d\n", list->path, GB.HashTable.Count(_ino.watches)); +#endif + + GB.FreeString(&list->path); + GB.Free(POINTER(&list)); + + if (GB.HashTable.Count(_ino.watches) == 0) + exit_inotify(); + + return TRUE; + } + else + return FALSE; + +} + +static void pause_watch(CWATCH *watch) +{ + int i; + WATCH_LIST *list = (WATCH_LIST *)watch->root; + + if (watch->paused) + return; + + watch->paused = TRUE; + watch->save_events = watch->events; + + for (i = 0; i < NUM_EV; i++) + { + if (watch->events & (1 << i)) + list->events[i]--; + } + + watch->events = 0; + + update_watch_list(list); +} + +static void resume_watch(CWATCH *watch) +{ + int i; + WATCH_LIST *list = (WATCH_LIST *)watch->root; + + if (!watch->paused) + return; + + watch->paused = FALSE; + watch->events = watch->save_events; + + for (i = 0; i < NUM_EV; i++) + { + if (watch->events & (1 << i)) + list->events[i]++; + } + + watch->save_events = 0; + + update_watch_list(list); +} + +static void callback(int fd, int flags, CINOTIFY *ino) +{ + struct inotify_event *iev; + int i, j, length; + int event; + CINFO info; + WATCH_LIST *list; + CWATCH *watch; + uint32_t mask; + char buf[sizeof(*iev) + NAME_MAX + 1] __attribute__((aligned(sizeof(int)))); + + for(;;) + { + if ((length = read(fd, buf, sizeof(buf))) <= 0) + { + if (errno == EINTR) + continue; + + GB_ErrorErrno(); + return; + } + + break; + } + + for (i = 0; i < length; i += sizeof(*iev) + iev->len) + { + + iev = (struct inotify_event *) &buf[i]; + + list = find_watch_list(ino, iev->wd); + + if (!list && !(iev->mask & IN_Q_OVERFLOW)) { + if (getenv("GB_INOTIFY_DEBUG")) + fprintf(stderr, "gb.inotify: descriptor %d not known. Name was `%s'\n", iev->wd, iev->name); + continue; + } + + mask = iev->mask & ~(IN_IGNORED | IN_ISDIR | IN_Q_OVERFLOW | IN_UNMOUNT); + + LIST_for_each(watch, list->root) + { + if (watch->paused) + continue; + + for (j = 0; j < NUM_EV; j++) + { + event = *_event_table[j].eventp; + if (_event_table[j].mask & mask && GB.CanRaise(watch, event)) + { + /* + * In case, the event loop kicks in at GB.Raise() + * and preempts this callback with itself. + * + * The problem is then that the event info structure + * may get replaced by another, thus we lose data. + * That's why we save it here. However, it is + * sufficient to keep the CINFO on the stack! + */ + info.iev = iev; + info.prev = _ino.top; + _ino.top = &info; + + GB.Raise(watch, event, 0); + + _ino.top = info.prev; + } + } + } + + /* Also remove the watch if the kernel did. */ + /*if (iev->mask & IN_IGNORED) + destroy_watch(watch);*/ + } +} + +/**G + * Tidy up. + **/ +BEGIN_METHOD_VOID(Watch_exit) + + /* Free the remaining objects. destroy_watch() will then also take + * care of calling INOTIFY_exit() to free bookkeeping data. */ + exit_inotify(); + +END_METHOD + +#if 0 +/**G + * Return a Watch from its path. If the path is not watched, Null is + * returned. + **/ +BEGIN_METHOD(Watch_get, GB_STRING path) + + CWATCH *watch; + + watch = find_watch_path(&_ino, GB.ToZeroString(ARG(path))); + if (!watch) { + GB.ReturnNull(); + return; + } + GB.ReturnObject(watch); + +END_METHOD +#endif + +/**G + * The name of the file or directory which is subject to the event, relative + * to the Watch object which triggered the event. If Null, the directory + * itself is subject. + **/ +BEGIN_PROPERTY(Watch_Name) + + struct inotify_event *iev = _ino.top->iev; + + GB.ReturnNewZeroString(iev->len ? iev->name : ""); + +END_PROPERTY + +/**G + * Return whether the event subject is/was a directory. + **/ +BEGIN_PROPERTY(Watch_IsDir) + + GB.ReturnBoolean(!!(_ino.top->iev->mask & IN_ISDIR)); + +END_PROPERTY + +/**G + * Return whether the filesystem on which the watched path resided, was + * unmounted. In this case, the Watch object is invalidated just after the + * event. + **/ +BEGIN_PROPERTY(Watch_Unmount) + + GB.ReturnBoolean(!!(_ino.top->iev->mask & IN_UNMOUNT)); + +END_PROPERTY + +/**G + * A cookie used to associate events. This is currently only used to connect + * MoveFrom and MoveTo events of the same file. + **/ +BEGIN_PROPERTY(Watch_Cookie) + + GB.ReturnInteger(_ino.top->iev->cookie); + +END_PROPERTY + +#define THIS ((CWATCH *) _object) + +/**G + * Create a new watch for the given path. + * + * If the NoFollowLink parameter is set and true, symlinks are not followed. + * + * Events is a bitmask specifying which events are to be reported at all. + * The default mask is determined by the event handlers defined for this + * object. + * + * TODO: Maybe add support for IN_EXCL_UNLINK, IN_ONESHOT and IN_ONLYDIR. + **/ +BEGIN_METHOD(Watch_new, GB_STRING path; GB_BOOLEAN nofollow; GB_INTEGER events) + + int i; + ushort events = VARGOPT(events, 0); + + if (LENGTH(path) == 0) + { + GB.Error("Null path"); + return; + } + + //fprintf(stderr, "Watch_new: %p\n", THIS); + + /* If this is the first watch, we need an inotify instance first. + * We don't use the component's init function to set up _ino because + * the inotify instance needs a GB.Watch() which would keep the + * program open even if no watches were registered. So this is done + * ad-hoc if there are watches. */ + init_inotify(); + + /* Get the mask */ + if (!events) + { + for (i = 0; i < NUM_EV; i++) + { + if (GB.CanRaise(THIS, *_event_table[i].eventp)) + events |= (1 << i); //_event_table[i].mask; + } + /* But we need at least to watch one event. IN_DELETE_SELF + * seems a good one as it doesn't trigger too often :-) */ + //mask |= IN_DELETE_SELF; + } + + THIS->events = events; + THIS->nofollow = VARGOPT(nofollow, FALSE); + THIS->tag.type = GB_T_NULL; + + create_watch(THIS, STRING(path), LENGTH(path)); + +END_METHOD + +/**G + * Deallocate the watch. + **/ +BEGIN_METHOD_VOID(Watch_free) + + //fprintf(stderr, "Watch_free: %p\n", THIS); + + destroy_watch(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Watch_Resume) + + resume_watch(THIS); + +END_METHOD + +BEGIN_METHOD_VOID(Watch_Pause) + + pause_watch(THIS); + +END_METHOD + +/**G + * Return the watched path. + **/ +BEGIN_PROPERTY(Watch_Path) + + GB.ReturnString(((WATCH_LIST *)THIS->root)->path); + +END_PROPERTY + +/**G + * Return or set if the watch is paused. + * + * {seealso + * Pause(), Resume() + * } + **/ +BEGIN_PROPERTY(Watch_IsPaused) + + if (READ_PROPERTY) + GB.ReturnBoolean(THIS->paused); + else if (THIS->paused != VPROP(GB_BOOLEAN)) + { + if (VPROP(GB_BOOLEAN)) + pause_watch(THIS); + else + resume_watch(THIS); + } + +END_PROPERTY + +/**G + * This Variant is free for use by the Gambas programmer. + **/ +BEGIN_PROPERTY(Watch_Tag) + + if (READ_PROPERTY) { + GB.ReturnVariant(&THIS->tag); + return; + } + GB.StoreVariant(PROP(GB_VARIANT), &THIS->tag); + +END_PROPERTY + +GB_DESC CWatch[] = +{ + GB_DECLARE("Watch", sizeof(CWATCH)), + + GB_CONSTANT("Read", "i", 1 << EV_READ), + GB_CONSTANT("Stat", "i", 1 << EV_STAT), + GB_CONSTANT("Close", "i", 1 << EV_CLOSE), + GB_CONSTANT("Create", "i", 1 << EV_CREATE), + GB_CONSTANT("Delete", "i", 1 << EV_DELETE), + GB_CONSTANT("Write", "i", 1 << EV_WRITE), + GB_CONSTANT("Move", "i", 1 << EV_MOVE), + GB_CONSTANT("MoveFrom", "i", 1 << EV_MOVED_FROM), + GB_CONSTANT("MoveTo", "i", 1 << EV_MOVED_TO), + GB_CONSTANT("Open", "i", 1 << EV_OPEN), + GB_CONSTANT("All", "i", -1), + + GB_EVENT("Read", NULL, NULL, &EVENT_Read), + GB_EVENT("Stat", NULL, NULL, &EVENT_Stat), + GB_EVENT("Close", NULL, NULL, &EVENT_Close), + GB_EVENT("Create", NULL, NULL, &EVENT_Create), + GB_EVENT("Delete", NULL, NULL, &EVENT_Delete), + GB_EVENT("Write", NULL, NULL, &EVENT_Write), + GB_EVENT("Move", NULL, NULL, &EVENT_Move), + GB_EVENT("MoveFrom", NULL, NULL, &EVENT_MoveFrom), + GB_EVENT("MoveTo", NULL, NULL, &EVENT_MoveTo), + GB_EVENT("Open", NULL, NULL, &EVENT_Open), + + GB_STATIC_METHOD("_exit", NULL, Watch_exit, NULL), + //GB_STATIC_METHOD("_get", "Watch", Watch_get, "(Path)s"), + + GB_METHOD("Resume", NULL, Watch_Resume, NULL), + GB_METHOD("Pause", NULL, Watch_Pause, NULL), + + GB_STATIC_PROPERTY_READ("Name", "s", Watch_Name), + GB_STATIC_PROPERTY_READ("IsDir", "b", Watch_IsDir), + GB_STATIC_PROPERTY_READ("Unmount", "b", Watch_Unmount), + GB_STATIC_PROPERTY_READ("Cookie", "i", Watch_Cookie), + + GB_METHOD("_new", NULL, Watch_new, "(Path)s[(NoFollowLink)b(Events)i]"), + GB_METHOD("_free", NULL, Watch_free, NULL), + + GB_PROPERTY_SELF("Events", ".Watch.Events"), + GB_PROPERTY_READ("Path", "s", Watch_Path), + GB_PROPERTY("IsPaused", "b", Watch_IsPaused), + GB_PROPERTY("Tag", "v", Watch_Tag), + + GB_END_DECLARE +}; + +/**G + * Return if the given flag is set. You can also combine multiple flags to + * check if at least one of them is set. + **/ +BEGIN_METHOD(WatchEvents_get, GB_INTEGER events) + + GB.ReturnBoolean(!!((THIS->paused ? THIS->save_events : THIS->events) & VARG(events))); + +END_METHOD + +/**G + * Set or clear a flag. You can combine multiple flags to set or clear them + * en masse. + **/ +BEGIN_METHOD(WatchEvents_put, GB_BOOLEAN value; GB_INTEGER events) + + WATCH_LIST *list = (WATCH_LIST *)THIS->root; + int i; + int events = VARG(events); + bool old, new; + + if (!THIS->paused) + { + for (i = 0; i < NUM_EV; i++) + { + new = (events & (1 << i)) != 0; + old = (THIS->events & (1 << i)) != 0; + if (new != old) + { + if (new) + list->events[i]++; + else + list->events[i]--; + } + } + + THIS->events = events; + update_watch_list(list); + } + else + THIS->save_events = events; + +END_METHOD + +GB_DESC CWatchEvents[] = +{ + GB_DECLARE_VIRTUAL(".Watch.Events"), + + GB_METHOD("_get", "b", WatchEvents_get, "(Events)i"), + GB_METHOD("_put", NULL, WatchEvents_put, "(Value)b(Events)i"), + + GB_END_DECLARE +}; diff --git a/main/lib/inotify/c_watch.h b/main/lib/inotify/c_watch.h new file mode 100644 index 00000000..d619d54f --- /dev/null +++ b/main/lib/inotify/c_watch.h @@ -0,0 +1,46 @@ +/* + * c_watch.h + * + * Copyright (C) 2013, 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __C_WATCH_H +#define __C_WATCH_H + +#include "main.h" +#include "gb_list.h" + +#ifndef __C_WATCH_C +extern GB_DESC CWatch[]; +extern GB_DESC CWatchEvents[]; +#endif + +typedef + struct { + GB_BASE ob; + LIST list; + void *root; + GB_VARIANT_VALUE tag; + ushort events; + ushort save_events; + bool nofollow; + bool paused; + } + CWATCH; + +#endif /* __C_WATCH_H */ diff --git a/main/lib/inotify/gb.inotify.component b/main/lib/inotify/gb.inotify.component new file mode 100644 index 00000000..33a5dc85 --- /dev/null +++ b/main/lib/inotify/gb.inotify.component @@ -0,0 +1,3 @@ +[Component] +Author=Tobias Boege +State=Stable diff --git a/main/lib/inotify/gb_list.c b/main/lib/inotify/gb_list.c new file mode 100644 index 00000000..34557b46 --- /dev/null +++ b/main/lib/inotify/gb_list.c @@ -0,0 +1,24 @@ +/*************************************************************************** + + gb_list.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_list_temp.h" diff --git a/main/lib/inotify/main.c b/main/lib/inotify/main.c new file mode 100644 index 00000000..ef1cf205 --- /dev/null +++ b/main/lib/inotify/main.c @@ -0,0 +1,43 @@ +/* + * main.c - gb.inotify glue + * + * Copyright (C) 2013, 2014 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#define __MAIN_C + +#include "c_watch.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = { + CWatch, + CWatchEvents, + + NULL +}; + +int EXPORT GB_INIT() +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} diff --git a/main/lib/inotify/main.h b/main/lib/inotify/main.h new file mode 100644 index 00000000..706234d4 --- /dev/null +++ b/main/lib/inotify/main.h @@ -0,0 +1,32 @@ +/* + * main.h + * + * Copyright (C) 2013 Tobias Boege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif /* __MAIN_H */ diff --git a/main/lib/jit/Makefile.am b/main/lib/jit/Makefile.am new file mode 100644 index 00000000..6cb8a4bc --- /dev/null +++ b/main/lib/jit/Makefile.am @@ -0,0 +1,16 @@ +COMPONENT = gb.jit +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.jit.la + +gb_jit_la_LIBADD = @C_LIB@ +gb_jit_la_LDFLAGS = -module @LD_FLAGS@ +gb_jit_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx $(AM_CFLAGS) + +gb_jit_la_SOURCES = \ + main.h main.c \ + gb_str.h gb_str.c \ + gbc_reserved.c \ + jit.h jit.c jit_body.c + + diff --git a/main/lib/jit/gb.jit.component b/main/lib/jit/gb.jit.component new file mode 100644 index 00000000..844988ff --- /dev/null +++ b/main/lib/jit/gb.jit.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.jit +Version=3.10.90 +State=2 +Hidden=True +Authors=Benoît Minisini diff --git a/main/lib/jit/gb.jit/.component b/main/lib/jit/gb.jit/.component new file mode 100644 index 00000000..b481a6fc --- /dev/null +++ b/main/lib/jit/gb.jit/.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.jit +Version=3.12.90 +Authors=Benoît Minisini diff --git a/main/lib/jit/gb.jit/.directory b/main/lib/jit/gb.jit/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/main/lib/jit/gb.jit/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/main/lib/jit/gb.jit/.icon.png b/main/lib/jit/gb.jit/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a9254f949f86671b58a744a6a93623576acc5217 GIT binary patch literal 10569 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" To sResult + + Return $sResult + +End + +Public Sub Compiler_Read() + + Dim sData As String + + sData = Read #$hProcess, -1024 + $sResult &= sData + +End + +Public Sub Compiler_Error(({Error}) As String) + + $sResult &= {Error} + +End + +Public Sub _Compile(sArch As String) As String + + Dim sFile As String + Dim sDir As String + Dim sName As String + Dim sPath As String + Dim hFile As File + Dim sResult As String + Dim sPathO As String + Dim sPathSO As String + Dim fTime As Float + + fTime = Timer + + Try $bDebug = CInt(Env["GB_JIT_DEBUG"]) + + If $bDebug Then Error "gb.jit: translating "; If(sArch, sArch, "project") + + If Not $sCompiler Then Init + + + sName = sArch + If Not sName Then sName = "gb" + + sDir = File.Dir(Temp$()) &/ "jit" + Try Mkdir sDir + + sPath = sDir &/ "jit.h" + + If Not Exist(sPath) Then + + If $bDebug Then Error "gb.jit: generating header" + + hFile = Open sPath For Output Create + + Print #hFile, "#define NO_CONFIG_H" + + Print #hFile, File.Load("gambas.h"); + Print #hFile, File.Load("jit.h"); + Print #hFile, File.Load("gb.jit.h"); + + Print #hFile, "GB_INTERFACE * GB_PTR;" + Print #hFile, "#define GB (*GB_PTR)" + Print #hFile, "JIT_INTERFACE * JIT_PTR;" + Print #hFile, "#define JIT (*JIT_PTR)" + + Print #hFile, File.Load("gb_error_common.h"); + + Close #hFile + + If $bDebug Then + Try Kill "/tmp/jit.h" + Copy sDir &/ "jit.h" To "/tmp/jit.h" + Endif + + ' 'Shell $sCompiler & " -fPIC " & Shell(sPath) To sResult + ' sResult = RunCompiler(sPath,, "-fPIC") + ' If Not Exist(sPath & ".gch") Then + ' Error "gb.jit: error: unable to generate precompiled header" + ' Error sResult + ' Return + ' Endif + ' + ' If $bDebug Then + ' Try Kill "/tmp/" & File.Name(sPath) + ' Copy sPath To "/tmp/" & File.Name(sPath) + ' Try Kill "/tmp/" & File.Name(sPath & ".gch") + ' Copy sPath & ".gch" To "/tmp/" & File.Name(sPath & ".gch") + ' Endif + + Endif + + sPath = sDir &/ sName & ".c" + + If $bDebug Then Error "gb.jit: generating "; sPath + + hFile = Open sPath For Output Create + + Print #hFile, "#include \"jit.h\"" + Print #hFile + + If sArch Then + sDir = "." &/ sArch + Else + sDir = "..." + Endif + + For Each sFile In Dir(sDir &/ ".gambas") + _ClassStat.Stat(sDir, sFile) + If Not _ClassStat.HasFast Then Continue + sFile = _ClassStat.Name + If $bDebug Then Error "gb.jit: translating class "; sFile + Print #hFile, __Jit.Translate(sFile, sArch) + Next + + Close #hFile + + If $bDebug Then + Try Kill "/tmp/" & File.Name(sPath) + Copy sPath To "/tmp/" & File.Name(sPath) + Endif + + sPathO = File.SetExt(sPath, "o") + sPathSO = File.SetExt(sPath, "so") + + If $bDebug Then Error "gb.jit: compiling to "; sPathO + + 'gcc -c -fPIC -o foo.o foo.c + 'Exec [$sCompiler, "-c", "-fPIC", "-o", File.SetExt(sPath, "o"), sPath] To sResult + 'Shell $sCompiler & " -c -fPIC " & sFlag & " -o " & Shell(sPathO) & " " & Shell(sPath) & " 2>&1" To sResult + sResult = RunCompiler(sPath, sPathSO, "-w -fPIC -shared -lm") + If Not Exist(sPathSO) Then + Error "gb.jit: error: unable to compile JIT code:" + Error sResult + Return + Endif + + ' If $bDebug Then Error "gb.jit: linking to "; sPathSO + ' + ' 'gcc -shared -o libfoo.so foo.o + ' 'Exec [$sCompiler, "-shared", "-o", File.SetExt(sPath, "so"), File.SetExt(sPath, "o")] To sResult + ' 'Shell $sCompiler & " -shared " & sFlag & " -lm -o " & Shell(sPathSO) & " " & Shell(sPathO) & " 2>&1" To sResult + ' RunCompiler(sPathO, sPathSO, "-shared -lm") + ' If Not Exist(sPathSO) Then + ' Error "gb.jit: warning: unable to link JIT code:" + ' Error sResult + ' Return + ' Endif + + fTime = Timer - fTime + $fTime += fTime + + If $bDebug Then Error "gb.jit: done in "; Format(fTime, "0.000"); "s" + + ' Shell "objdump -S " & Shell(sPathSO) To sResult + ' Error sResult + + Return sPathSO + +End + +Private Function Time_Read() As Float + + Return $fTime + +End diff --git a/main/lib/jit/gb.jit/.src/Main.module b/main/lib/jit/gb.jit/.src/Main.module new file mode 100644 index 00000000..96188747 --- /dev/null +++ b/main/lib/jit/gb.jit/.src/Main.module @@ -0,0 +1,7 @@ +' Gambas module file + +Public Sub Main() + + + +End diff --git a/main/lib/jit/gb.jit/.src/_ClassStat.class b/main/lib/jit/gb.jit/.src/_ClassStat.class new file mode 100644 index 00000000..6daf8744 --- /dev/null +++ b/main/lib/jit/gb.jit/.src/_ClassStat.class @@ -0,0 +1,102 @@ +' Gambas class file + +Static Public Name As String +Static Public Parent As String +Static Public HasFast As Boolean + +Static Private $iSectionSize As Integer +Static Private $iSectionPos As Integer + +Static Private Sub GotoNextSection(hFile As File) + + $iSectionPos += $iSectionSize + Seek #hFile, $iSectionPos + $iSectionSize = Read #hFile As Integer + $iSectionPos += 4 + +End + +Static Private Sub ReadZeroString(hFile As File) As String + + Dim sStr As String + Dim iPos As Integer + + Do + sStr &= Read #hFile, Min(16, Lof(hFile) - Seek(hFile)) + iPos = InStr(sStr, Chr$(0)) + If iPos Then Return Left(sStr, iPos - 1) + Loop + +End + + + +Static Public Sub Stat(sDir As String, sName As String) + + Dim sPath As String + Dim hFile As File + Dim iVal As Integer + Dim iParent As Integer + Dim iFlag As Short + Dim bDebug As Boolean + Dim iStringPos As Integer + + sPath = sDir &/ ".gambas" &/ UCase(sName) + If Not Exist(sPath) Then Error.Raise("Class not found: " & sPath) + + $iSectionPos = 0 + $iSectionSize = 0 + + hFile = Open sPath + + Seek #hFile, 8 + iVal = Read #hFile As Integer + If iVal <> &H12345678 Then hFile.ByteOrder = 1 - hFile.ByteOrder + Seek #hFile, 12 + iVal = Read #hFile As Integer + bDebug = iVal And 1 + + $iSectionSize = 16 + + GotoNextSection(hFile) ' info + Seek #hFile, $iSectionPos + iParent = Read #hFile As Short + iFlag = Read #hFile As Short + + 'hStat.Exported = BTst(iFlag, 0) + 'hStat.AutoCreate = BTst(iFlag, 1) + 'hStat.Optional = BTst(iFlag, 2) + 'hStat.NoCreate = BTst(iFlag, 3) + HasFast = BTst(iFlag, 4) + + GotoNextSection(hFile) ' description + GotoNextSection(hFile) ' constant + GotoNextSection(hFile) ' reference + + If iParent <> -1 Then + + Seek #hFile, $iSectionPos + iParent * 4 + iParent = Read #hFile As Integer + iParent = Abs(iParent) + + Endif + + Do + iStringPos = $iSectionPos + Try GotoNextSection(hFile) + If Error Then Break + Loop + + Seek #hFile, iStringPos + Name = ReadZeroString(hFile) + + If iParent > 0 Then + + Seek #hFile, iStringPos + iParent + Parent = ReadZeroString(hFile) + + Endif + + Close hFile + +End diff --git a/main/lib/jit/gb.jit/gambas.h b/main/lib/jit/gb.jit/gambas.h new file mode 120000 index 00000000..9f0c378e --- /dev/null +++ b/main/lib/jit/gb.jit/gambas.h @@ -0,0 +1 @@ +../../../share/gambas.h \ No newline at end of file diff --git a/main/lib/jit/gb.jit/gb.jit.h b/main/lib/jit/gb.jit/gb.jit.h new file mode 120000 index 00000000..2b756893 --- /dev/null +++ b/main/lib/jit/gb.jit/gb.jit.h @@ -0,0 +1 @@ +../../../gbx/gb.jit.h \ No newline at end of file diff --git a/main/lib/jit/gb.jit/gb_error_common.h b/main/lib/jit/gb.jit/gb_error_common.h new file mode 120000 index 00000000..07c2de99 --- /dev/null +++ b/main/lib/jit/gb.jit/gb_error_common.h @@ -0,0 +1 @@ +../../../share/gb_error_common.h \ No newline at end of file diff --git a/main/lib/jit/gb.jit/jit.h b/main/lib/jit/gb.jit/jit.h new file mode 100644 index 00000000..1ceb7074 --- /dev/null +++ b/main/lib/jit/gb.jit/jit.h @@ -0,0 +1,570 @@ +#define GB_JIT 1 + +#include +#include +//#include + +#define TRUE 1 +#define FALSE 0 + +// __attribute__((noreturn)) makes gcc dizzy and slow as hell +#define NORETURN + +#define E_NEPARAM 4 +#define E_TMPARAM 5 +#define E_TYPE 6 +#define E_OVERFLOW 7 +#define E_NOBJECT 12 +#define E_NULL 13 +#define E_NRETURN 18 +#define E_MATH 19 +#define E_ARG 20 +#define E_BOUND 21 +#define E_ZERO 26 +#define E_IOBJECT 29 + +static inline double frac(double x) +{ + x = fabs(x); + return x - floor(x); +} + +#ifdef __clang__ +#define __builtin_exp10 exp10 +static inline double exp10(double x) +{ + return pow(10, x); +} +#endif + +#ifdef __TINYC__ +#define __builtin_log log +#define __builtin_exp exp +#define __builtin_sqrt sqrt +#define __builtin_sin sin +#define __builtin_cos cos +#define __builtin_tan tan +#define __builtin_atan atan +#define __builtin_asin asin +#define __builtin_acos acos +#define __builtin_sinh sinh +#define __builtin_cosh cosh +#define __builtin_tanh tanh +#define __builtin_asinh asinh +#define __builtin_acosh acosh +#define __builtin_atanh atanh +#define __builtin_exp2(_expr) pow(2, (_expr)) +#define __builtin_exp10(_expr) pow(10, (_expr)) +#define __builtin_log2(_expr) (log(_expr) / M_LN2) +#define __builtin_cbrt cbrt +#define __builtin_expm1 expm1 +#define __builtin_log1p log1p +#define __builtin_floor floor +#define __builtin_ceil ceil +#endif + +typedef + unsigned char uchar; + +typedef + GB_VALUE VALUE; + +typedef + ushort PCODE; + +typedef + void (*EXEC_FUNC)(); + +typedef + void (*EXEC_FUNC_CODE)(ushort); + +typedef + struct { + GB_TYPE type; + GB_CLASS class; + void *super; + } + GB_VALUE_CLASS; + +typedef + struct { + GB_TYPE type; + void *object; + void *super; + } + GB_VALUE_OBJECT; + +typedef + struct { + GB_TYPE type; + void *class; + void *object; + char kind; + char defined; + short index; + } + GB_VALUE_FUNCTION; + +typedef + struct { + GB_TYPE type; + void *addr; + void *gp; + } + GB_VALUE_GOSUB; + +typedef + struct { + GB_BASE object; + int size; + int count; + GB_TYPE type; + void *data; + int *dim; + void *ref; + } + GB_ARRAY_IMPL; + +#define SP (*psp) +#define PC (JIT.exec->pc) +#define CP (JIT.exec->cp) +#define OP (JIT.exec->op) +#define FP (JIT.exec->fp) +#define PP (JIT.exec->pp) +#define BP (JIT.exec->bp) +#define EP (JIT.exec->ep) + +#define THROW(_err) ({ SP = sp; JIT.throw(_err); }) +#define THROW_TYPE(_t1, _t2) ({ SP = sp; JIT.throw_type(_t1, _t2); }) + +#define CHECK_FINITE(_val) ({ if (!isfinite(_val)) THROW(E_OVERFLOW); (_val); }) + +#define PARAM_b(_p) (JIT.conv(&sp[-n+(_p)], GB_T_BOOLEAN), sp[-n+(_p)]._boolean.value) +#define PARAM_c(_p) (JIT.conv(&sp[-n+(_p)], GB_T_BYTE), (uchar)(sp[-n+(_p)]._integer.value)) +#define PARAM_h(_p) (JIT.conv(&sp[-n+(_p)], GB_T_SHORT), (short)(sp[-n+(_p)]._integer.value)) +#define PARAM_i(_p) (JIT.conv(&sp[-n+(_p)], GB_T_INTEGER), sp[-n+(_p)]._integer.value) +#define PARAM_l(_p) (JIT.conv(&sp[-n+(_p)], GB_T_LONG), sp[-n+(_p)]._long.value) +#define PARAM_g(_p) (JIT.conv(&sp[-n+(_p)], GB_T_SINGLE), sp[-n+(_p)]._single.value) +#define PARAM_f(_p) (JIT.conv(&sp[-n+(_p)], GB_T_FLOAT), sp[-n+(_p)]._float.value) +#define PARAM_p(_p) (JIT.conv(&sp[-n+(_p)], GB_T_POINTER), sp[-n+(_p)]._pointer.value) +#define PARAM_d(_p) (JIT.conv(&sp[-n+(_p)], GB_T_DATE), *(GB_DATE *)&sp[-n+(_p)]) +#define PARAM_s(_p) (JIT.conv(&sp[-n+(_p)], GB_T_STRING), *(GB_STRING *)&sp[-n+(_p)]) +#define PARAM_o(_p) (JIT.conv(&sp[-n+(_p)], GB_T_OBJECT), *(GB_OBJECT *)&sp[-n+(_p)]) +#define PARAM_v(_p) (JIT.conv(&sp[-n+(_p)], GB_T_VARIANT), *(GB_VARIANT *)&sp[-n+(_p)]) +#define PARAM_O(_p, _type) (JIT.conv(&sp[-n+(_p)], (GB_TYPE)(_type)), *(GB_OBJECT *)&sp[-n+(_p)]) + +#define PARAM_OPT(_p, _type, _default) (((_p) >= n || (sp[-n+(_p)].type == GB_T_VOID)) ? (_default) : PARAM_##_type(_p)) +#define PARAM_OPT_b(_p) PARAM_OPT(_p, b, 0) +#define PARAM_OPT_c(_p) PARAM_OPT(_p, c, 0) +#define PARAM_OPT_h(_p) PARAM_OPT(_p, h, 0) +#define PARAM_OPT_i(_p) PARAM_OPT(_p, i, 0) +#define PARAM_OPT_l(_p) PARAM_OPT(_p, l, 0) +#define PARAM_OPT_g(_p) PARAM_OPT(_p, g, 0) +#define PARAM_OPT_f(_p) PARAM_OPT(_p, f, 0) +#define PARAM_OPT_p(_p) PARAM_OPT(_p, p, 0) +#define PARAM_OPT_d(_p) PARAM_OPT(_p, d, ({ GB_DATE _v; _v.type = GB_T_DATE; _v.value.time = _v.value.date = 0; _v; })) +#define PARAM_OPT_s(_p) PARAM_OPT(_p, s, GET_CSTRING("", 0, 0)) +#define PARAM_OPT_o(_p) PARAM_OPT(_p, o, GET_OBJECT(0, GB_T_OBJECT)) +#define PARAM_OPT_v(_p) PARAM_OPT(_p, v, ({ GB_VARIANT _v; _v.type = GB_T_VARIANT; _v.value.type = GB_T_NULL; _v; })) +#define PARAM_OPT_O(_p, _type) (((_p) >= n || (sp[-n+(_p)].type == GB_T_VOID)) ? GET_OBJECT(GB_T_OBJECT, 0) : PARAM_O(_p, _type)) + +#define OPT(_p, _n) ({ \ + uchar _opt = 0; \ + int _i; \ + GB_VALUE *_param = &sp[-n+(_p)]; \ + for (_i = 0; _i < (_n); _i++) \ + { \ + if (((_i + (_p)) >= n) || _param->type == GB_T_VOID) \ + _opt |= (1 << _i); \ + _param++; \ + } \ + _opt; \ +}) + +#define RETURN_b(_val) (GB.ReturnBoolean((int)_val)) +#define RETURN_c(_val) (GB.Return(GB_T_BYTE, (int)(_val))) +#define RETURN_h(_val) (GB.Return(GB_T_SHORT, (int)(_val))) +#define RETURN_i(_val) (GB.ReturnInteger(_val)) +#define RETURN_l(_val) (GB.ReturnLong(_val)) +#define RETURN_g(_val) (GB.ReturnSingle(_val)) +#define RETURN_f(_val) (GB.ReturnFloat(_val)) +#define RETURN_d(_val) ({ GB_DATE _v = (_val); GB.ReturnDate(&_v); }) +#define RETURN_p(_val) (GB.ReturnPointer(_val)) +#define RETURN_o(_val) ({ GB_OBJECT _v = (_val); GB.Return(_v.type, _v.value); }) +#define RETURN_v(_val) ({ GB_VARIANT _v = (_val); GB.ReturnVariant(&_v.value); }) +#define RETURN_s(_val) (*(GB_STRING *)GB.GetReturnValue() = (_val)) + +#define PUSH_b(_val) ({ char _v = -(_val); sp->_boolean.value = _v; sp->type = GB_T_BOOLEAN; sp++; }) +#define PUSH_c(_val) ({ uchar _v = (_val); sp->_integer.value = _v; sp->type = GB_T_BYTE; sp++; }) +#define PUSH_h(_val) ({ short _v = (_val); sp->_integer.value = _v; sp->type = GB_T_SHORT; sp++; }) +#define PUSH_i(_val) ({ sp->_integer.value = (_val); sp->type = GB_T_INTEGER; sp++; }) +#define PUSH_l(_val) ({ int64_t _v = (_val); sp->_long.value = _v; sp->type = GB_T_LONG; sp++; }) +#define PUSH_g(_val) ({ float _v = (_val); sp->_single.value = _v; sp->type = GB_T_SINGLE; sp++; }) +#define PUSH_f(_val) ({ double _v = (_val); sp->_float.value = _v; sp->type = GB_T_FLOAT; sp++; }) +#define PUSH_p(_val) ({ intptr_t _v = (_val); sp->_pointer.value = _v; sp->type = GB_T_POINTER; sp++; }) +#define PUSH_d(_val) ({ *((GB_DATE *)sp) = (_val); sp++; }) +#define PUSH_t(_val) ({ *((GB_STRING *)sp) = (_val); sp++; }) +#define PUSH_s(_val) ({ *((GB_STRING *)sp) = (_val); if (sp->type == GB_T_STRING) GB.RefString(sp->_string.value.addr); sp++; }) +#define PUSH_o(_val) ({ *((GB_OBJECT *)sp) = (_val); GB.Ref(sp->_object.value); sp++; }) +#define PUSH_v(_val) ({ *((GB_VARIANT *)sp) = (_val); GB.BorrowValue(sp); sp++; }) +#define PUSH_C(_val) ({ GB_VALUE_CLASS _v; _v.type = GB_T_CLASS; _v.class = (_val); *((GB_VALUE_CLASS *)sp) = _v; sp++; }) +#define PUSH_u(_val) ({ *sp = (_val); GB.BorrowValue(sp); sp++; }) +#define PUSH_V() ({ sp->type = GB_T_VOID; sp++; }) +#define PUSH_n(_val) ({ sp->type = GB_T_NULL; sp++; }) +#define PUSH_F(_val) ({ sp->type = GB_T_FUNCTION; ((GB_VALUE_FUNCTION *)sp)->class = ((GB_VALUE_FUNCTION *)sp)->object = NULL; sp++; }) + +enum +{ + FUNCTION_NULL, + FUNCTION_NATIVE, + FUNCTION_PRIVATE, + FUNCTION_PUBLIC, + FUNCTION_EVENT, + FUNCTION_EXTERN, + FUNCTION_UNKNOWN, + FUNCTION_CALL, + FUNCTION_SUBR +}; + +#define PUSH_PRIVATE_FUNCTION(_index) ({ \ + GB_VALUE_FUNCTION *f = (GB_VALUE_FUNCTION *)sp; \ + f->type = GB_T_FUNCTION; \ + f->class = CP; \ + f->object = OP; \ + f->kind = FUNCTION_PRIVATE; \ + f->index = (_index); \ + f->defined = 1; \ + GB.Ref(OP); \ + sp++; \ +}) + +#define POP_b() ({ sp--; sp->_boolean.value; }) +#define POP_c() ({ sp--; (uchar)sp->_integer.value; }) +#define POP_h() ({ sp--; (short)sp->_integer.value; }) +#define POP_i() ({ sp--; sp->_integer.value; }) +#define POP_l() ({ sp--; sp->_long.value; }) +#define POP_g() ({ sp--; sp->_single.value; }) +#define POP_f() ({ sp--; sp->_float.value; }) +#define POP_p() ({ sp--; sp->_pointer.value; }) +#define POP_d() ({ sp--; *((GB_DATE*)sp); }) +#define POP_s() ({ sp--; JIT.unborrow(sp); *((GB_STRING*)sp); }) +#define POP_t() ({ sp--; *((GB_STRING*)sp); }) +#define POP_BORROW_s() ({ sp--; *((GB_STRING*)sp); }) +#define POP_o() ({ sp--; JIT.unborrow(sp); *((GB_OBJECT*)sp); }) +#define POP_BORROW_o() ({ sp--; *((GB_OBJECT*)sp); }) +#define POP_v() ({ sp--; JIT.unborrow(sp); *((GB_VARIANT*)sp); }) +#define POP_BORROW_v() ({ sp--; *((GB_VARIANT*)sp); }) +#define POP_u() ({ sp--; JIT.unborrow(sp); *sp; }) +#define POP_BORROW_u() ({ sp--; *sp }) +#define POP_V() (sp--) + +#define BORROW_s(_val) ({ GB_STRING _v = (_val); if ((_v).type == GB_T_STRING) GB.RefString(_v.value.addr); _v; }) +#define BORROW_o(_val) ({ GB_OBJECT _v = (_val); GB.Ref(_v.value); _v; }) +#define BORROW_v(_val) ({ GB_VARIANT _v = (_val); GB.BorrowValue((GB_VALUE *)&_v); _v; }) + +#define RELEASE_s(_val) ({ GB_STRING _v = (_val); if ((_v).type == GB_T_STRING) GB.FreeString(&_v.value.addr); _v; }) +#define RELEASE_o(_val) ({ GB_OBJECT _v = (_val); GB.Unref(&_v.value); _v; }) +#define RELEASE_v(_val) ({ GB_VARIANT _v = (_val); GB.ReleaseValue((GB_VALUE *)&_v); _v; }) + +#define RELEASE_FAST_s(_val) ({ if ((_val).type == GB_T_STRING) GB.FreeString(&(_val).value.addr); }) +#define RELEASE_FAST_o(_val) GB.Unref(&((_val).value)) +#define RELEASE_FAST_v(_val) GB.ReleaseValue((GB_VALUE *)&(_val)) + +#define CLASS(_class) ({ JIT.load_class((void *)_class); (GB_CLASS)(_class); }) + +#define CONSTANT_s(_addr, _len) GET_CSTRING((char *)_addr, 0, _len) +#define CONSTANT_t(_addr, _len) GET_CSTRING(GB.Translate((const char *)_addr), 0, strlen(temp.value.addr)) + +#define GET_CHAR(_char) GET_CSTRING(&JIT.char_table[(_char) * 2], 0, 1) + +#define GET_STRING(_addr, _start, _len) ({ \ + GB_STRING temp; \ + temp.type = GB_T_STRING; \ + temp.value.addr = (char *)(_addr); \ + temp.value.start = (_start); \ + temp.value.len = (_len); \ + temp; }) + +#define GET_CSTRING(_addr, _start, _len) ({ \ + GB_STRING temp; \ + temp.type = GB_T_CSTRING; \ + temp.value.addr = (char *)(_addr); \ + temp.value.start = (_start); \ + temp.value.len = (_len); \ + temp; }) + +#define GET_OBJECT(_addr, _type) ({ \ + GB_OBJECT temp; \ + temp.type = (GB_TYPE)(_type); \ + temp.value = (_addr); \ + temp; }) + +#define GET_DATE(_addr) ({ \ + GB_DATE temp; \ + int *_val = (int *)(_addr); \ + temp.type = GB_T_DATE; \ + temp.value.date = _val[0]; \ + temp.value.time = _val[1]; \ + temp; \ +}) + +#define GET_VARIANT(_val) ({ \ + GB_VARIANT temp; \ + temp.type = GB_T_VARIANT; \ + temp.value = (_val); \ + if (temp.value.type == GB_T_VOID) temp.value.type = GB_T_NULL; \ + temp; }) + +#define GET_FUNCTION(_pc) ({ CALL_UNKNOWN(_pc); POP_u(); }) + +// TODO: automatic class +#define ADDR(_val) ({ \ + char *_object = (_val).value; \ + if (!_object) THROW(E_NULL); \ + _object; \ +}) + +#define ADDR_CHECK(_check, _val) ({ \ + char *_object = (_val).value; \ + if (!_object) THROW(E_NULL); \ + if (((int (*)())_check)(_object)) THROW(E_IOBJECT); \ + _object; \ +}) + +#define ADDR_UNSAFE(_val) ((char *)((_val).value)) + +#define GET_b(_addr) (*(bool *)(_addr)) +#define GET_c(_addr) (*(uchar *)(_addr)) +#define GET_h(_addr) (*(short *)(_addr)) +#define GET_i(_addr) (*(int *)(_addr)) +#define GET_l(_addr) (*(int64_t *)(_addr)) +#define GET_g(_addr) (*(float *)(_addr)) +#define GET_f(_addr) (*(double *)(_addr)) +#define GET_d(_addr) GET_DATE(_addr) +#define GET_p(_addr) (*(intptr_t *)(_addr)) +#define GET_s(_addr) GET_STRING((*(char **)(_addr)), 0, GB.StringLength(temp.value.addr)) +#define GET_o(_addr, _type) GET_OBJECT((*(char **)(_addr)), _type) +#define GET_v(_addr) GET_VARIANT((*(GB_VARIANT_VALUE *)(_addr))) +#define GET_S(_ref, _addr, _type) GET_OBJECT(JIT.static_struct((_ref), (_type), (_addr)), _type) + +#define GET_A(_class, _ref, _addr, _type, _desc) ({ \ + SP = sp; \ + GB.Unref(&ra); \ + ra = JIT.static_array((_class), (_ref), (void *)(_desc), (_addr)); \ + GET_OBJECT(ra, _type); \ +}) + +#define SET_b(_addr, _val) (GET_b(_addr) = (_val)) +#define SET_c(_addr, _val) (GET_c(_addr) = (_val)) +#define SET_h(_addr, _val) (GET_h(_addr) = (_val)) +#define SET_i(_addr, _val) (GET_i(_addr) = (_val)) +#define SET_l(_addr, _val) (GET_l(_addr) = (_val)) +#define SET_g(_addr, _val) (GET_g(_addr) = (_val)) +#define SET_f(_addr, _val) (GET_f(_addr) = (_val)) +#define SET_d(_addr, _val) ({ GB_DATE_VALUE temp = (_val).value; int *_d = (int *)(_addr); _d[0] = temp.date; _d[1] = temp.time; }) +#define SET_p(_addr, _val) (GET_p(_addr) = (_val)) +#define SET_s(_addr, _val) ({ GB_VALUE temp = (GB_VALUE)(_val); GB.StoreString((GB_STRING *)&temp, (char **)(_addr)); }) +#define SET_o(_addr, _val) ({ GB_VALUE temp = (GB_VALUE)(_val); GB.StoreObject((GB_OBJECT *)&temp, (void **)(_addr)); }) +#define SET_v(_addr, _val) ({ GB_VALUE temp = (GB_VALUE)(_val); GB.StoreVariant((GB_VARIANT *)&temp, (GB_VARIANT_VALUE *)(_addr)); }) + +#define GET_ARRAY_UNSAFE(_type, _array, _index) ({ \ + GB_ARRAY_IMPL *_a = (_array).value; \ + int _i = (_index); \ + &((_type *)_a->data)[_i]; \ +}) + +#define GET_ARRAY(_type, _array, _index) ({ \ + GB_ARRAY_IMPL *_a = (_array).value; \ + int _i = (_index); \ + if (!_a) THROW(E_NOBJECT); \ + if (_i < 0 || _i >= _a->count) THROW(E_BOUND); \ + &((_type *)_a->data)[_i]; \ +}) + +#define PUSH_ARRAY(_type, _array, _index, _unsafe) *GET_ARRAY##_unsafe(_type, _array, _index) + +#define PUSH_ARRAY_b(_array, _index, _unsafe) PUSH_ARRAY(bool, _array, _index, _unsafe) +#define PUSH_ARRAY_c(_array, _index, _unsafe) PUSH_ARRAY(uchar, _array, _index, _unsafe) +#define PUSH_ARRAY_h(_array, _index, _unsafe) PUSH_ARRAY(short, _array, _index, _unsafe) +#define PUSH_ARRAY_i(_array, _index, _unsafe) PUSH_ARRAY(int, _array, _index, _unsafe) +#define PUSH_ARRAY_l(_array, _index, _unsafe) PUSH_ARRAY(int64_t, _array, _index, _unsafe) +#define PUSH_ARRAY_g(_array, _index, _unsafe) PUSH_ARRAY(float, _array, _index, _unsafe) +#define PUSH_ARRAY_f(_array, _index, _unsafe) PUSH_ARRAY(double, _array, _index, _unsafe) +#define PUSH_ARRAY_p(_array, _index, _unsafe) PUSH_ARRAY(intptr_t, _array, _index, _unsafe) +#define PUSH_ARRAY_d(_array, _index, _unsafe) GET_DATE(GET_ARRAY##_unsafe(int, _array, _index)) +#define PUSH_ARRAY_s(_array, _index, _unsafe) GET_STRING(PUSH_ARRAY(char *, _array, _index, _unsafe), 0, GB.StringLength(temp.value.addr)) +#define PUSH_ARRAY_o(_array, _index, _unsafe) GET_OBJECT(PUSH_ARRAY(void *, _array, _index, _unsafe), GB_T_OBJECT) +#define PUSH_ARRAY_O(_array, _index, _type, _unsafe) GET_OBJECT(PUSH_ARRAY(void *, _array, _index, _unsafe), _type) +#define PUSH_ARRAY_v(_array, _index, _unsafe) GET_VARIANT(PUSH_ARRAY(GB_VARIANT_VALUE, _array, _index, _unsafe)) + +#define POP_ARRAY(_type, _array, _index, _val, _unsafe) (*GET_ARRAY##_unsafe(_type, _array, _index) = (_val)) + +#define POP_ARRAY_b(_array, _index, _val, _unsafe) POP_ARRAY(bool, _array, _index, _val, _unsafe) +#define POP_ARRAY_c(_array, _index, _val, _unsafe) POP_ARRAY(uchar, _array, _index, _val, _unsafe) +#define POP_ARRAY_h(_array, _index, _val, _unsafe) POP_ARRAY(short, _array, _index, _val, _unsafe) +#define POP_ARRAY_i(_array, _index, _val, _unsafe) POP_ARRAY(int, _array, _index, _val, _unsafe) +#define POP_ARRAY_l(_array, _index, _val, _unsafe) POP_ARRAY(int64_t, _array, _index, _val, _unsafe) +#define POP_ARRAY_g(_array, _index, _val, _unsafe) POP_ARRAY(float, _array, _index, _val, _unsafe) +#define POP_ARRAY_f(_array, _index, _val, _unsafe) POP_ARRAY(double, _array, _index, _val, _unsafe) +#define POP_ARRAY_p(_array, _index, _val, _unsafe) POP_ARRAY(intptr_t, _array, _index, _val, _unsafe) +#define POP_ARRAY_d(_array, _index, _val, _unsafe) ({ GB_DATE temp = (GB_DATE)(_val); int *_p = (int *)GET_ARRAY##_unsafe(int, _array, _index)); _p[0] = temp.value.date; _p[1] = temp.value.time; }) +#define POP_ARRAY_s(_array, _index, _val, _unsafe) ({ GB_VALUE temp = (GB_VALUE)(_val); GB.StoreString((GB_STRING *)&temp, GET_ARRAY##_unsafe(char *, _array, _index)); }) +#define POP_ARRAY_o(_array, _index, _val, _unsafe) ({ GB_VALUE temp = (GB_VALUE)(_val); GB.StoreObject((GB_OBJECT *)&temp, GET_ARRAY##_unsafe(void *, _array, _index)); }) +#define POP_ARRAY_v(_array, _index, _val, _unsafe) ({ GB_VALUE temp = (GB_VALUE)(_val); GB.StoreVariant((GB_VARIANT *)&temp, GET_ARRAY##_unsafe(GB_VARIANT_VALUE, _array, _index)); }) + +#define CONV(_val, _src, _dest, _type) ({ PUSH_##_src(_val); SP = sp; JIT.conv(sp - 1, (GB_TYPE)(_type)); POP_##_dest(); }) + +#define CONV_d_b(_val) ({ GB_DATE _v = (_val); _v.value.date != 0 || _v.value.time != 0; }) +#define CONV_d_c(_val) ((uchar)((_val).value.date)) +#define CONV_d_h(_val) ((short)((_val).value.date)) +#define CONV_d_i(_val) ((_val).value.date) +#define CONV_d_l(_val) ((int64_t)((_val).value.date)) +#define CONV_d_g(_val) ({ GB_DATE _v = (_val); (float)((float)_v.value.date + (float)_v.value.time / 86400000.0); }) +#define CONV_d_f(_val) ({ GB_DATE _v = (_val); (double)((double)_v.value.date + (double)_v.value.time / 86400000.0); }) +#define CONV_d_p(_val) (THROW_TYPE(T_DATE, T_POINTER)) +#define CONV_d_s(_val) CONV(_val, d, s, GB_T_STRING) +#define CONV_d_o(_val) (THROW_TYPE(T_DATE, T_OBJECT)) + +#define CONV_o_O(_val, _class) CONV(_val, o, o, CLASS(_class)) + +#define GET_NULL_o() ({ GB_OBJECT temp; temp.type = GB_T_OBJECT; temp.value = NULL; temp; }) +#define GET_NULL_v() ({ GB_VARIANT temp; temp.type = GB_T_VARIANT; temp.value.type = GB_T_NULL; temp; }) +#define GET_NULL_s() GET_CSTRING("", 0, 0) +#define GET_NULL_d() ({ GB_DATE temp; temp.type = GB_T_DATE; temp.value.date = 0; temp.value.time = 0; temp; }) +#define GET_NULL_p() ((intptr_t)0) + +#define PUSH_GOSUB(_label) ({ \ + GB_VALUE_GOSUB *_p = (GB_VALUE_GOSUB *)sp; \ + _p->type = GB_T_VOID; \ + _p->addr = &&_label; \ + _p->gp = gp; \ + gp = _p; \ + sp++; \ +}) + +#define LEAVE_TRY() ERROR_leave(__err) + +#define RETURN_LEAVE_TRY() ({ \ + void *_addr = &&__L0; \ + if (!gp) { LEAVE_TRY(); goto __RETURN; } \ + _addr = gp->addr; \ + sp = (GB_VALUE *)gp; \ + gp = gp->gp; \ + goto *_addr; \ +}) + +#define RETURN() ({ \ + void *_addr; \ + if (!gp) goto __RETURN; \ + _addr = gp->addr; \ + sp = (GB_VALUE *)gp; \ + gp = gp->gp; \ + goto *_addr; \ +}) + +#define RELEASE_GOSUB() ({ \ + while(gp) \ + { \ + gp = gp->gp; \ + SP--; \ + } \ +}) + +#define CALL_SUBR(_pc, _func) ({ PC = &pc[_pc]; SP = sp; (*((EXEC_FUNC)_func))(); sp = SP; }) +#define CALL_SUBR_CODE(_pc, _func, _code) ({ PC = &pc[_pc]; SP = sp; (*((EXEC_FUNC_CODE)_func))(_code); sp = SP; }) +#define CALL_SUBR_UNKNOWN(_pc) ({ JIT.call_unknown(&pc[_pc], &sp); }) + +#define CALL_PUSH_ARRAY(_pc, _code) ({ PC = &pc[_pc]; SP = sp; (*(EXEC_FUNC)JIT.push_array)(_code); sp = SP; }) +#define CALL_POP_ARRAY(_pc, _code) ({ PC = &pc[_pc]; SP = sp; (*(EXEC_FUNC)JIT.pop_array)(_code); sp = SP; sp++; }) + +#define PUSH_UNKNOWN(_pc) ({ PC = &pc[_pc]; SP = sp; JIT.push_unknown(); sp = SP; }) +//#define PUSH_UNKNOWN(_pc) CALL_UNKNOWN(_pc) +#define POP_UNKNOWN(_pc) ({ PC = &pc[_pc]; SP = sp; JIT.pop_unknown(); sp = SP; }) + +#define PUSH_COMPLEX(_val) ({ PUSH_f(_val); SP = sp; JIT.push_complex(); POP_o(); }) + +#define GET_LAST() GET_OBJECT(*(JIT.event_last), GB_T_OBJECT) + +#define GET_ME(_type) ({ \ + GB_OBJECT _temp; \ + if (OP) \ + { \ + GB_VALUE_OBJECT *_p = (GB_VALUE_OBJECT *)&_temp; \ + _p->type = (GB_TYPE)(_type); \ + _p->object = OP; \ + } \ + else \ + { \ + GB_VALUE_CLASS *_p = (GB_VALUE_CLASS *)&_temp; \ + _p->type = GB_T_CLASS; \ + _p->class = (GB_TYPE)(_type); \ + } \ + _temp; }) + +#define GET_SUPER(_type) ({ \ + GB_OBJECT _temp; \ + if (OP) \ + { \ + GB_VALUE_OBJECT *_p = (GB_VALUE_OBJECT *)&_temp; \ + _p->type = (GB_TYPE)(_type); \ + _p->object = OP; \ + _p->super = *JIT.exec_super; \ + } \ + else \ + { \ + GB_VALUE_CLASS *_p = (GB_VALUE_CLASS *)&_temp; \ + _p->type = GB_T_CLASS; \ + _p->class = (GB_TYPE)(_type); \ + _p->super = *JIT.exec_super; \ + } \ + *JIT.exec_super = sp; \ + _temp; }) + +#define LEAVE_SUPER() { \ + while (sp < *JIT.exec_super) \ + *JIT.exec_super = ((GB_VALUE_OBJECT *)*JIT.exec_super)->super; \ +} + +#define CALL_UNKNOWN(_pc) ({ JIT.call_unknown(&pc[_pc], &sp); }) + +#define ENUM_FIRST(_code, _plocal, _penum) ({ GB.Unref(&(_penum).value); (_penum).type = 0; JIT.enum_first(_code, (GB_VALUE *)&_plocal, (GB_VALUE*)&_penum); }) + +#define ENUM_NEXT(_code, _plocal, _penum, _label) ({ \ + SP = sp; \ + bool _t = JIT.enum_next(_code, (GB_VALUE *)&_plocal, (GB_VALUE *)&_penum); \ + sp = SP; \ + if (_t) goto _label; \ +}) + +#define CALL_MATH(_func) ({ double _v = _func; if (!isfinite(_v)) THROW(E_MATH); _v; }) +#define CALL_MATH_UNSAFE(_func) (_func) + +#define ERROR_current (*(ERROR_CONTEXT **)(JIT.error_current)) +#define ERROR_handler (*(ERROR_HANDLER **)(JIT.error_handler)) +#define ERROR_reset JIT.error_reset + +#define QUIT(_code) (SP = sp, JIT.exec_quit(_code)) + +#define RAISE_EVENT(_event, _narg) ({ SP = sp; GB.Raise(OP, (_event), -(_narg)); sp = SP; }) +#define RAISE_UNKNOWN_EVENT(_pc) ({ PC = &pc[_pc]; SP = sp; JIT.push_unknown_event(0); sp = SP; }) + +#define MATH_ABS(_val) ({ \ + __typeof(_val) _v = (_val); \ + (_v < 0) ? (- _v) : _v; \ +}) + +#define MATH_SGN(_val) ({ \ + __typeof(_val) _v = (_val); \ + (_v < 0) ? -1 : ((_v > 0) ? 1 : 0); \ +}) + +#define MATH_FIX_g(_val) ({ \ + __typeof(_val) _v = (_val); \ + (_v >= 0) ? floorf(_v) : -floorf(fabsf(_v)); \ +}) + +#define MATH_FIX_f(_val) ({ \ + __typeof(_val) _v = (_val); \ + (_v >= 0) ? floor(_v) : -floor(fabs(_v)); \ +}) + diff --git a/main/lib/jit/gb_str.c b/main/lib/jit/gb_str.c new file mode 100644 index 00000000..4702b311 --- /dev/null +++ b/main/lib/jit/gb_str.c @@ -0,0 +1,227 @@ +/*************************************************************************** + + gb_str.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_STR_C + +#include "gb_common.h" + +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "gb_str.h" + +//#define DEBUG + +static char *_free_later = NULL; +static char *_last_str = NULL; +static int _last_len = 0; + +#ifdef DEBUG +static int _count = 0; +#endif + +void STR_free(char *str) +{ + if (!str) + return; + +#ifdef DEBUG + _count--; + fprintf(stderr, "free %p -> %d\n", str, _count); +#endif + GB.Free((void **)&(str)); +} + +void STR_vadd(char **str, const char *fmt, va_list args) +{ + va_list copy; + int len, add; + char *new; + + va_copy(copy, args); + add = vsnprintf(NULL, 0, fmt, args); + + if (*str) + len = (*str == _last_str ? _last_len : strlen(*str)); + else + len = 0; + + GB.Alloc((void **)&new, len + add + 1); + if (*str) strcpy(new, *str); + + vsprintf(&new[len], fmt, copy); + va_end(copy); + +#ifdef DEBUG + if (*str) { + _count--; + fprintf(stderr, "free %p -> %d\n", *str, _count); + } + _count++; + fprintf(stderr, "alloc %p -> %d\n", new, _count); +#endif + if (*str) GB.Free((void **)str); + *str = new; + + _last_str = new; + _last_len = len + add; +} + + +void STR_add(char **str, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + STR_vadd(str, fmt, args); + va_end(args); +} + + +char *STR_copy_len(const char *str, int len) +{ + char *cpy; + + GB.Alloc((void **)&cpy, len + 1); + memcpy(cpy, str, len + 1); +#ifdef DEBUG + _count++; + fprintf(stderr, "alloc %p -> %d\n", cpy, _count); +#endif + return cpy; +} + + +char *STR_copy(const char *str) +{ + return STR_copy_len(str, strlen(str)); +} + + +/*static char *str_add(char *d, const char *s) +{ + for(;;) + { + if ((*d = *s) == 0) + break; + + d++; + s++; + } + + return d; +} + + +char *STR_cat(const char *str, ...) +{ + va_list args; + char *cpy; + char *p; + int len = 0; + + va_start(args, str); + + p = (char *)str; + while (p) + { + len += strlen(p); + p = va_arg(args, char *); + } + + va_end(args); + + GB.Alloc((void **)&cpy, len + 1); + p = cpy; + + va_start(args, str); + + while (str) + { + p = str_add(p, str); + str = va_arg(args, char *); + } + + va_end(args); + + return cpy; +}*/ + + +char *STR_upper(const char *str) +{ + char *s; + char *p; + + p = s = STR_copy(str); + while (*p) + { + *p = toupper(*p); + p++; + } + + return s; +} + + +char *STR_lower(const char *str) +{ + char *s; + char *p; + + p = s = STR_copy(str); + while (*p) + { + *p = tolower(*p); + p++; + } + + return s; +} + + +char *STR_free_later(char *str) +{ + if (_free_later) + STR_free(_free_later); + _free_later = str; + return str; +} + + +char *STR_print(const char *fmt, ...) +{ + va_list args; + char *str = NULL; + + va_start(args, fmt); + STR_vadd(&str, fmt, args); + va_end(args); + return str; +} + diff --git a/main/lib/jit/gb_str.h b/main/lib/jit/gb_str.h new file mode 100644 index 00000000..600ef920 --- /dev/null +++ b/main/lib/jit/gb_str.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + gb_str.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_STR_H +#define __GB_STR_H + +#include + +char *STR_copy(const char *str); +char *STR_copy_len(const char *str, int len); +char *STR_cat(const char *str, ...); +char *STR_upper(const char *str); +char *STR_lower(const char *str); + +void STR_free(char *str); +char *STR_free_later(char *str); + +void STR_add(char **str, const char *fmt, ...); +char *STR_print(const char *fmt, ...); +void STR_vadd(char **str, const char *fmt, va_list args); + +#endif diff --git a/main/lib/jit/gbc_reserved.c b/main/lib/jit/gbc_reserved.c new file mode 100644 index 00000000..c4abf837 --- /dev/null +++ b/main/lib/jit/gbc_reserved.c @@ -0,0 +1,30 @@ +/*************************************************************************** + + gbc_reserved.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBC_RESERVED_C + +#define __GB_COMMON_CASE_H +#include "main.h" +#define PROJECT_COMP +#include "gb_reserved_temp.h" + diff --git a/main/lib/jit/jit.c b/main/lib/jit/jit.c new file mode 100644 index 00000000..72a0fc6b --- /dev/null +++ b/main/lib/jit/jit.c @@ -0,0 +1,517 @@ +/*************************************************************************** + + jit.c + + (c) 2000-2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __JIT_C + +#include +#include +#include +#include +#include + +#include "gb_str.h" +#include "jit.h" + +typedef + struct { + const char *name; + char type; + } + CLASS_TYPE; + +CLASS *JIT_class; +char *JIT_prefix; +bool JIT_last_print_is_label; + +static char *_buffer = NULL; +static char *_buffer_decl = NULL; +static char *_buffer_body = NULL; + +static bool _decl_null_variant = FALSE; + +/*static const CLASS_TYPE _class_type[] = { + { "Boolean[]", T_BOOLEAN }, + { "Byte[]", T_BYTE }, + { "Short[]", T_SHORT }, + { "Integer[]", T_INTEGER }, + { "Long[]", T_LONG }, + { "Single[]", T_SINGLE }, + { "Float[]", T_FLOAT }, + { "Date[]", T_DATE }, + { "String[]", T_STRING }, + { "Pointer[]", T_POINTER }, + { "Object[]", T_OBJECT }, + { "Variant[]", T_VARIANT }, + { NULL, 0 } +};*/ + +static const char *_type_name[] = +{ + "V" , "b", "c", "h", "i", "l", "g", "f", + "d", "s", "t", "p", "v", "F", "C", "n", + "o", "u" +}; + +static const char *_gtype_name[] = +{ + "GB_T_VOID" , "GB_T_BOOLEAN", "GB_T_BYTE", "GB_T_SHORT", "GB_T_INTEGER", "GB_T_LONG", "GB_T_SINGLE", "GB_T_FLOAT", + "GB_T_DATE", "GB_T_STRING", "GB_T_CSTRING", "GB_T_POINTER", "GB_T_VARIANT", "?", "GB_T_CLASS", "?", + "GB_T_OBJECT" +}; + +static const char *_ctype_name[] = +{ + "void" , "bool", "uchar", "short", "int", "int64_t", "float", "double", + "GB_DATE", "GB_STRING", "GB_STRING", "intptr_t", "GB_VARIANT", "?", "void *", "?", + "GB_OBJECT", "GB_VALUE", "?" +}; + +const char *JIT_get_type(TYPE type) +{ + return _type_name[TYPEID(type)]; +} + +const char *JIT_get_gtype(TYPE type) +{ + return _gtype_name[TYPEID(type)]; +} + +const char *JIT_get_ctype(TYPE type) +{ + return _ctype_name[TYPEID(type)]; +} + +TYPE JIT_ctype_to_type(CLASS *class, CTYPE ctype) +{ + if (ctype.id == T_OBJECT && ctype.value >= 0) + return (TYPE)(class->load->class_ref[ctype.value]); + else if (ctype.id == TC_ARRAY) + { + CLASS_ARRAY *desc = class->load->array[ctype.value]; + return (TYPE)JIT.get_array_class(class, *(JIT_CTYPE *)&desc->ctype); + } + else if (ctype.id == TC_STRUCT) + return (TYPE)(class->load->class_ref[ctype.value]); + else + return (TYPE)(ctype.id); +} + + +static void JIT_begin(void) +{ + JIT_prefix = STR_lower(JIT_class->name); + _buffer = NULL; + _buffer_decl = NULL; + JIT_print("\n//////// %s\n\n", JIT_class->name); +} + +static char *JIT_end(void) +{ + char *result = _buffer; + + STR_free(JIT_prefix); + _buffer = NULL; + + GB.FreeStringLater(result); + return result; +} + +static void declare_implementation(FUNCTION *func, int index) +{ + int i; + int nopt; + int opt; + const char *vol = func->error ? "volatile " : ""; + + JIT_print("static %s jit_%s_%d_(", JIT_get_ctype(func->type), JIT_prefix, index); + + for (i = 0; i < func->npmin; i++) + { + if (i) JIT_print(","); + JIT_print("%s%s p%d", vol, JIT_get_ctype(func->param[i].type), i); + } + + if (i < func->n_param) + { + opt = nopt = 0; + + for (; i < func->n_param; i++) + { + if (i) JIT_print(","); + + if (nopt == 0) + { + JIT_print("uchar o%d,", opt); + opt++; + } + + JIT_print("%s%s p%d", vol, JIT_get_ctype(func->param[i].type), i); + + nopt++; + if (nopt >= 8) + nopt = 0; + } + } + + if (func->vararg) + { + if (func->n_param) + JIT_print(","); + JIT_print("uchar nv,GB_VALUE *v"); + } + + JIT_print(")"); +} + + +void JIT_declare_func(FUNCTION *func, int index) +{ + JIT_print("void jit_%s_%d(uchar n);\n", JIT_prefix, index); + + declare_implementation(func, index); + JIT_print(";\n"); +} + + +const char *JIT_get_default_value(TYPE type) +{ + switch(TYPEID(type)) + { + case T_DATE: return "{GB_T_DATE}"; + case T_STRING: return "{GB_T_STRING}"; + case T_OBJECT: return "{GB_T_NULL}"; + case T_VARIANT: + if (!_decl_null_variant) + { + JIT_print_decl(" GB_VARIANT null_variant = {GB_T_VARIANT,{GB_T_NULL}};\n"); + _decl_null_variant = TRUE; + } + return "null_variant"; + default: return "0"; + } +} + + +static bool JIT_translate_func(FUNCTION *func, int index) +{ + int i; + TYPE type; + int nopt; + const char *def; + const char *vol = func->error ? "volatile " : ""; + + if (func->debug) + JIT_section(func->debug->name); + + JIT_print("void jit_%s_%d(uchar n)\n{\n", JIT_prefix, index); + + if (func->n_param || func->vararg) + JIT_print(" VALUE *sp = *((VALUE **)%p);\n", JIT.sp); + + JIT_print(" "); + + if (!TYPE_is_void(func->type)) + JIT_print("RETURN_%s(", JIT_get_type(func->type)); + + JIT_print("jit_%s_%d_(", JIT_prefix, index); + + for (i = 0; i < func->npmin; i++) + { + if (i) JIT_print(","); + type = func->param[i].type; + if (TYPE_is_pure_object(type)) + JIT_print("PARAM_O(%d, CLASS(%p))", i, type); + else + JIT_print("PARAM_%s(%d)", JIT_get_type(type), i); + } + + if (i < func->n_param) + { + nopt = 0; + + for (; i < func->n_param; i++) + { + if (i) JIT_print(","); + + if (nopt == 0) + JIT_print("OPT(%d,%d),", i, Min(func->n_param, i + 8) - i); + + type = func->param[i].type; + if (TYPE_is_pure_object(type)) + JIT_print("PARAM_OPT_O(%d, CLASS(%p))", i, type); + else + JIT_print("PARAM_OPT_%s(%d)", JIT_get_type(type), i); + + nopt++; + if (nopt >= 8) + nopt = 0; + } + } + + if (func->vararg) + { + if (func->n_param) + JIT_print(","); + JIT_print("n - %d,&sp[-n+%d]", i, i); + } + + if (!TYPE_is_void(func->type)) + JIT_print(")"); + + JIT_print(");\n"); + JIT_print("}\n\n"); + + declare_implementation(func, index); + JIT_print("\n{\n"); + + _buffer_decl = NULL; + _buffer_body = NULL; + _decl_null_variant = FALSE; + + for (i = -1; i < func->n_local; i++) + { + if (i < 0) + { + if (TYPE_is_void(func->type)) + continue; + type = func->type; + def = JIT_get_default_value(type); + JIT_print_decl(" %s r = ", JIT_get_ctype(type)); + } + else + { + type = JIT_ctype_to_type(JIT_class, func->local[i].type); + def = JIT_get_default_value(type); + JIT_print_decl(" %s%s l%d = ", vol, JIT_get_ctype(type), i); + } + + JIT_print_decl(def); + JIT_print_decl(";\n"); + } + + for (i = 0; i < func->n_param; i++) + { + type = func->param[i].type; + switch(TYPEID(type)) + { + case T_STRING: case T_OBJECT: case T_VARIANT: + JIT_print_body(" BORROW_%s(p%d);\n", JIT_get_type(type), i); + } + } + + if (JIT_translate_body(func, index)) + return TRUE; + + if (!TYPE_is_void(func->type)) + { + switch(TYPEID(func->type)) + { + case T_STRING: + case T_OBJECT: + case T_VARIANT: + JIT_print_body(" JIT.unborrow((GB_VALUE *)&r);\n"); + break; + } + + JIT_print_body(" return r;\n"); + } + else + JIT_print_body(" return;\n"); + + _buffer = GB.AddString(_buffer, _buffer_decl, GB.StringLength(_buffer_decl)); + JIT_print("\n"); + _buffer = GB.AddString(_buffer, _buffer_body, GB.StringLength(_buffer_body)); + + GB.FreeString(&_buffer_decl); + GB.FreeString(&_buffer_body); + + JIT_print("}\n"); + + return FALSE; +} + + +void JIT_vprint(char **buffer, const char *fmt, va_list args) +{ + int len, add; + va_list copy; + + va_copy(copy, args); + add = vsnprintf(NULL, 0, fmt, copy); + va_end(copy); + + len = GB.StringLength(*buffer); + + *buffer = GB.ExtendString(*buffer, len + add); + + vsprintf(*buffer + len, fmt, args); + + JIT_last_print_is_label = (strncmp(fmt, "__L", 3) == 0); +} + + +void JIT_print(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + JIT_vprint(&_buffer, fmt, args); + va_end(args); +} + + +void JIT_print_decl(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + JIT_vprint(&_buffer_decl, fmt, args); + va_end(args); +} + + +void JIT_print_body(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + JIT_vprint(&_buffer_body, fmt, args); + va_end(args); +} + + +void JIT_section(const char *str) +{ + JIT_print("\n// %s\n\n", str); +} + + +void JIT_declare(TYPE type, const char *fmt, ...) +{ + va_list args; + const char *def; + + def = JIT_get_default_value(type); + + JIT_print_decl(" %s ", JIT_get_ctype(type)); + + va_start(args, fmt); + JIT_vprint(&_buffer_decl, fmt, args); + va_end(args); + + switch (TYPEID(type)) + { + case T_STRING: + case T_OBJECT: + case T_VARIANT: + JIT_print_decl(" = %s", def); + break; + } + + JIT_print_decl(";\n"); +} + + + +char *JIT_translate(const char *name, const char *from) +{ + CLASS *class; + int i; + FUNCTION *func; + + JIT_class = class = (CLASS *)GB.LoadClassFrom(name, from); + + JIT_begin(); + + for (i = 0; i < class->load->n_func; i++) + { + func = &class->load->func[i]; + if (!func->fast) + continue; + JIT_declare_func(func, i); + } + + for (i = 0; i < class->load->n_func; i++) + { + func = &class->load->func[i]; + if (!func->fast) + continue; + + JIT_last_print_is_label = FALSE; + if (JIT_translate_func(func, i)) + return NULL; + } + + return JIT_end(); +} + +void JIT_panic(const char *fmt, ...) +{ + va_list args; + fprintf(stderr, "gb.jit: panic: "); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + va_end(args); + fputc('\n', stderr); + fputs(_buffer, stderr); + fputc('\n', stderr); + abort(); +} + +int JIT_get_code_size(FUNCTION *func) +{ + void *code = func->code; + int size = ((int *)code)[-1] / sizeof(ushort); + + if (func->code[size - 1] == 0) + size--; + + return size; +} + + +void JIT_load_class_without_init(CLASS *class) +{ + void *save_cp; + + if (class->loaded) + return; + + if (class->ready || class->in_load) + return; + + save_cp = JIT.exec->cp; + JIT.exec->cp = JIT_class; + + //fprintf(stderr, "gb.jit: load class: %s (%p)\n", class->name, class); + JIT.load_class_without_init(class); + + JIT.exec->cp = save_cp; +} + + +int JIT_find_symbol(CLASS *class, const char *name) +{ + JIT_load_class_without_init(class); + return JIT.find_symbol(class->table, class->sort, class->n_desc, sizeof(CLASS_DESC_SYMBOL), TF_IGNORE_CASE, name, strlen(name), NULL); +} + + diff --git a/main/lib/jit/jit.h b/main/lib/jit/jit.h new file mode 100644 index 00000000..a06bfa02 --- /dev/null +++ b/main/lib/jit/jit.h @@ -0,0 +1,94 @@ +/*************************************************************************** + + jit.h + + (c) 2000-2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __JIT_H +#define __JIT_H + +#define __GB_COMMON_CASE_H +#include "gb_common.h" +#include "gb_str.h" +#include "gb_pcode.h" +#include "gb_reserved.h" +#include "gbx_type.h" +#include "gbx_class.h" +#include "main.h" + +#define T_UNKNOWN 17 + +#undef TYPE_is_pure_object +#define TYPE_is_pure_object(_type) ((_type) > T_UNKNOWN) + +#undef TYPE_is_object +#define TYPE_is_object(_type) ((_type) == T_OBJECT || TYPE_is_pure_object(_type)) + +#define TYPEID(_type) (TYPE_is_pure_object(_type) ? T_OBJECT : (_type)) + +enum +{ + CALL_UNKNOWN, + CALL_PRIVATE, + CALL_EVENT, + CALL_EXTERN +}; + +#define PM_STRING 8 +#define PM_WAIT 16 + + +#ifndef __GBC_JIT_C +EXTERN char *JIT_prefix; +EXTERN CLASS *JIT_class; +EXTERN bool JIT_last_print_is_label; +#endif + + +// jit.c + +char *JIT_translate(const char *name, const char *from); + +void JIT_section(const char *str); + +void JIT_print(const char *fmt, ...); +void JIT_print_decl(const char *fmt, ...); +void JIT_print_body(const char *fmt, ...); +void JIT_declare(TYPE type, const char *fmt, ...); + +const char *JIT_get_type(TYPE type); +const char *JIT_get_gtype(TYPE type); +const char *JIT_get_ctype(TYPE type); +TYPE JIT_ctype_to_type(CLASS *class, CTYPE ctype); +const char *JIT_get_default_value(TYPE type); + +void JIT_panic(const char *fmt, ...) NORETURN; + +int JIT_get_code_size(FUNCTION *func); +int JIT_find_symbol(CLASS *class, const char *name); +void JIT_load_class_without_init(CLASS *class); + +// jit_body.c + +bool JIT_translate_body(FUNCTION *func, int index); + + +#endif + diff --git a/main/lib/jit/jit_body.c b/main/lib/jit/jit_body.c new file mode 100644 index 00000000..a2f197d4 --- /dev/null +++ b/main/lib/jit/jit_body.c @@ -0,0 +1,3359 @@ +/*************************************************************************** + + jit_body.c + + (c) 2000-2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __JIT_BODY_C + +#define _GNU_SOURCE + +#include "config.h" +#include +#include +#include +#include +#include + +#include "jit.h" + +#define JIT_print JIT_print_body + +#define MAX_STACK 256 + +typedef + struct { + TYPE type; + char *expr; + ushort func; + ushort pc; + int index; + TYPE call; + } + STACK_SLOT; + +typedef + struct { + TYPE type; + char *expr; + } + CTRL_INFO; + +enum { + CALL_SUBR = 0, + CALL_SUBR_CODE = 1, + CALL_SUBR_UNKNOWN = 2, + CALL_NEW = 3, + CALL_RETURN_UNKNWON = 128 +}; + +enum { + MATH_NEG, + MATH_ABS, + MATH_SGN, + MATH_INT, + MATH_FIX +}; + + +static FUNCTION *_func; + +static STACK_SLOT _stack[MAX_STACK]; +static int _stack_current = 0; + +static bool _decl_rs; +static bool _decl_ro; +static bool _decl_rv; +static bool _decl_tp; +static bool _decl_ra; + +static ushort _pc; + +static bool _no_release = FALSE; +static bool _no_release_but_borrow = FALSE; + +static int _loop_count; + +static TYPE *_dup_type; + +enum { LOOP_UNKNOWN, LOOP_UP, LOOP_DOWN }; + +static int _loop_type; + +static int *_ctrl_index; +static CTRL_INFO *_ctrl_info; + +static bool _has_gosub; +static bool _has_finally; +static bool _has_catch; +static bool _try_finished; + +static bool _has_just_dup; + +static bool _unsafe; + + +static void enter_function(FUNCTION *func, int index) +{ + _func = func; + + _decl_rs = FALSE; + _decl_ro = FALSE; + _decl_rv = FALSE; + _decl_tp = FALSE; + _decl_ra = FALSE; + + _has_gosub = FALSE; + _loop_count = 0; + _has_just_dup = FALSE; + + _has_catch = FALSE; + _has_finally = func->error && (func->code[func->error - 1] != C_CATCH); + + _unsafe = func->unsafe; + + GB.NewArray((void **)&_dup_type, sizeof(TYPE), 0); + GB.NewArray((void **)&_ctrl_info, sizeof(CTRL_INFO), 0); + + if (func->n_ctrl) + GB.AllocZero((void **)&_ctrl_index, sizeof(int) * func->n_ctrl); + else + _ctrl_index = NULL; + + JIT_print_decl(" VALUE **psp = (VALUE **)%p;\n", JIT.sp); + JIT_print_decl(" VALUE *sp = SP;\n"); + //JIT_print_decl(" VALUE *ep = sp;\n"); + //JIT_print(" VALUE *sp = SP; fprintf(stderr, \"> %d: sp = %%p\\n\", sp);\n", index); + JIT_print_decl(" ushort *pc = (ushort *)%p;\n", JIT.get_code(func)); + JIT_print_decl(" GB_VALUE_GOSUB *gp = 0;\n"); + JIT_print_decl(" bool error = FALSE;\n"); + + if (func->vararg) + { + JIT_print(" VALUE *fp = FP, *pp = PP, *bp = BP;\n"); + JIT_print(" FP = %p; PP = v; BP = sp;\n", func); + } + + JIT_print(" VALUE *ssp = sp;\n"); // fprintf(stderr, \"bp = %%p\\n\", bp);\n"); + JIT_print(" TRY {\n\n"); + + _try_finished = FALSE; +} + + +static void print_catch(void) +{ + JIT_print("\n } CATCH {\n\n"); + JIT_print(" CP = (void *)%p;\n", JIT_class); + JIT_print(" FP = (void *)%p;\n", _func); + if (_has_catch || _has_finally) + JIT_print(" JIT.error_set_last(FALSE); \n"); + //JIT_print(" fprintf(stderr, \"EP = %%p SP = %%p sp = %%p\\n\", EP, SP, sp);\n"); + JIT_print(" if (SP > sp) sp = SP; else SP = sp;\n"); + JIT_print(" LEAVE_SUPER();\n"); + //JIT_print(" if (sp > bp) { fprintf(stderr, \"sp = %%p bp = %%p release %%d\\n\", sp, bp, sp - bp); JIT.release_many(sp, sp - bp); SP = sp = bp; }\n"); + JIT_print(" if (sp > ssp) { JIT.release_many(sp, sp - ssp); SP = sp = ssp; }\n"); + //JIT_print(" PP = SP;\n"); + JIT_print(" error = TRUE;\n"); + JIT_print("\n } END_TRY\n\n"); + JIT_print("__FINALLY:;\n"); + _try_finished = TRUE; +} + +#define RELEASE_FAST(_expr, _type, _index) ({ \ + TYPE _t = (_type); \ + switch(TYPEID(_t)) \ + { \ + case T_STRING: case T_OBJECT: case T_VARIANT: \ + JIT_print((_expr), JIT_get_type(_t), (_index)); \ + } \ +}) + +static bool leave_function(FUNCTION *func, int index) +{ + int i; + + STR_free_later(NULL); + JIT_print("\n__RETURN:;\n"); + //JIT_print("__RETURN: fprintf(stderr, \"< %d: sp = %%p\\n\", sp);\n", ind); + + if (_stack_current) + JIT_panic("Stack mismatch: stack is not void"); + + if (!_has_catch && !_has_finally) + print_catch(); + + JIT_print("__RELEASE:;\n"); + if (func->vararg) + JIT_print(" FP = fp; BP = bp; PP = pp;\n"); + JIT_print(" SP = sp;\n"); + JIT_print(" RELEASE_GOSUB();\n"); + + for (i = 0; i < GB.Count(_ctrl_info); i++) + { + RELEASE_FAST(" RELEASE_FAST_%s(c%d);\n", _ctrl_info[i].type, i); + if (_ctrl_info[i].expr) + STR_free(_ctrl_info[i].expr); + } + + for (i = 0; i < GB.Count(_dup_type); i++) + RELEASE_FAST(" RELEASE_FAST_%s(d%d);\n", _dup_type[i], i); + + for (i = 0; i < func->n_local; i++) + RELEASE_FAST(" RELEASE_FAST_%s(l%d);\n", JIT_ctype_to_type(JIT_class, func->local[i].type), i); + + for (i = 0; i < func->n_param; i++) + RELEASE_FAST(" RELEASE_FAST_%s(p%d);\n", func->param[i].type, i); + + if (_decl_ra) + JIT_print(" GB.Unref(&ra);\n"); + + if (!_has_catch && !_has_finally) + { + JIT_print(" if (error) { "); + /*if (func->n_param) + JIT_print("SP -= %d; ", func->n_param);*/ + JIT_print("GB.Propagate(); }\n"); + } + + GB.Free((void **)&_ctrl_index); + GB.FreeArray((void **)&_ctrl_info); + GB.FreeArray((void **)&_dup_type); + + _func = NULL; + + return FALSE; +} + + +static TYPE get_local_type(FUNCTION *func, int index) +{ + TYPE type; + + if (index < func->n_local) + type = JIT_ctype_to_type(JIT_class, func->local[index].type); + else + type = _ctrl_info[_ctrl_index[index - func->n_local]].type; + + return type; +} + + +static void free_stack(int n) +{ + if (n < 0) n += _stack_current; + STR_free(_stack[n].expr); + _stack[n].expr = NULL; +} + + +static void check_stack(int n) +{ + if (_stack_current < n) + JIT_panic("Stack mismatch: stack is void"); +} + + +static void pop_stack(int n) +{ + int i; + + for (i = 1; i <= n; i++) + free_stack(-i); + + _stack_current -= n; +} + + +static void declare(bool *flag, const char *expr) +{ + if (*flag) + return; + + JIT_print_decl(" %s;\n", expr); + *flag = TRUE; +} + + +static void print_label(FUNCTION *func, ushort pc) +{ + //JIT_print("__L%d:; fprintf(stderr, \"[%s]\\n\");\n", pc, JIT.get_position(JIT_class, func, &func->code[pc])); + JIT_print("__L%d:; // %s\n", pc, JIT.get_position(JIT_class, func, &func->code[pc])); +} + + +static void push_one(TYPE type, const char *fmt, va_list args) +{ + if (_stack_current > MAX_STACK) + JIT_panic("Expression too complex"); + + CLEAR(&_stack[_stack_current]); + + if (fmt) + STR_vadd(&_stack[_stack_current].expr, fmt, args); + + _stack[_stack_current].type = type; + _stack[_stack_current].call = T_UNKNOWN; + _stack_current++; +} + + +static void push(TYPE type, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + push_one(type, fmt, args); + + va_end(args); +} + +static STACK_SLOT *get_stack(int n) +{ + if (n < 0) n += _stack_current; + return &_stack[n]; +} + +static TYPE get_type(int n) +{ + TYPE type; + + type = get_stack(n)->type; + + if (TYPE_is_pure_object(type)) + JIT_load_class_without_init((CLASS *)type); + + return type; +} + + +static char *get_expr(int n) +{ + return get_stack(n)->expr; +} + + +static void set_expr(int n, char *expr) +{ + if (n < 0) n += _stack_current; + _stack[n].expr = expr; +} + + +static CLASS *get_class(int n) +{ + TYPE type = get_type(n); + + if (type == T_CLASS) + { + sscanf(get_expr(n), "CLASS(%p)", (void **)&type); + if (type) + JIT_load_class_without_init((CLASS *)type); + } + else if (!TYPE_is_pure_object(type)) + type = 0; + + return (CLASS *)type; +} + + +static char *borrow_expr(char *expr, TYPE type) +{ + const char *type_name = JIT_get_type(type); + int len; + char *new_expr; + + len = strlen(expr); + if ((strncmp(&expr[len - 5], "();})", 5) == 0) && (strncmp(&expr[len - 10], "POP_", 4) == 0) && (expr[len - 6] == *type_name)) + new_expr = STR_print("%.*sPOP_BORROW_%s();})", len - 10, expr, type_name); + else + new_expr = STR_print("BORROW_%s(%s)", type_name, expr); + + STR_free(expr); + return new_expr; +} + + +static const char *get_conv_format(TYPE src, TYPE dest) +{ + static char buffer[64]; + + if (src == T_VOID) + { + sprintf(buffer, "(THROW(E_NRETURN),%s)", JIT_get_default_value(TYPEID(dest))); + return buffer; + } + + switch(dest) + { + case T_VOID: + + return "((void)%s)"; + + case T_BOOLEAN: + + switch(TYPEID(src)) + { + case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: case T_SINGLE: case T_FLOAT: case T_POINTER: + return "((%s)!=0)"; + case T_OBJECT: + return "({ void *_addr = (%s).value; if (_addr) { GB.Ref(_addr); GB.Unref(&_addr); } (_addr) != 0; })"; + } + break; + + case T_BYTE: + + switch(src) + { + case T_BOOLEAN: + return "((uchar)(%s)?255:0)"; + case T_SHORT: case T_INTEGER: case T_LONG: case T_SINGLE: case T_FLOAT: + return "((uchar)(%s))"; + } + break; + + case T_SHORT: + + switch(src) + { + case T_BOOLEAN: + return "((short)(%s)?-1:0)"; + case T_BYTE: case T_INTEGER: case T_LONG: case T_SINGLE: case T_FLOAT: + return "((short)(%s))"; + } + break; + + case T_INTEGER: + + switch(src) + { + case T_BOOLEAN: + return "((int)(%s)?-1:0)"; + case T_BYTE: case T_SHORT: case T_LONG: case T_SINGLE: case T_FLOAT: case T_POINTER: + return "((int)(%s))"; + } + break; + + case T_LONG: + + switch(src) + { + case T_BOOLEAN: + return "((int64_t)(%s)?-1:0)"; + case T_BYTE: case T_SHORT: case T_INTEGER: case T_SINGLE: case T_FLOAT: case T_POINTER: + return "((int64_t)(%s))"; + } + break; + + case T_SINGLE: + + switch(src) + { + case T_BOOLEAN: + return "((float)(%s)?-1:0)"; + case T_BYTE: case T_SHORT: case T_INTEGER: + return "((float)(%s))"; + case T_LONG: case T_FLOAT: + if (_unsafe) + return "((float)(%s))"; + else + return "(CHECK_FINITE((float)(%s)))"; + } + break; + + case T_FLOAT: + + switch(src) + { + case T_BOOLEAN: + return "((double)(%s)?-1:0)"; + case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: case T_SINGLE: + return "((double)(%s))"; + } + break; + + case T_STRING: + + switch(src) + { + case T_CSTRING: return "%s"; + case T_NULL: return "GET_NULL_s()"; + } + break; + + case T_CSTRING: + + switch(src) + { + case T_STRING: return "%s"; + case T_NULL: return "GET_NULL_s()"; + } + break; + + default: + + if (src == T_NULL) + { + switch(dest) + { + case T_DATE: + case T_POINTER: + case T_VARIANT: + case T_OBJECT: + sprintf(buffer,"GET_NULL_%s()", JIT_get_type(dest)); + return buffer; + default: + sprintf(buffer, "GET_OBJECT(NULL, CLASS(%p))", (CLASS *)dest); + return buffer; + } + } + + if (TYPE_is_object(dest) && TYPE_is_object(src)) + { + if (TYPE_is_pure_object(dest)) + { + sprintf(buffer, "CONV_o_O(%%s, %p)", (CLASS *)dest); + return buffer; + } + else + return "%s"; + } + break; + } + + if (TYPE_is_pure_object(dest)) + sprintf(buffer, "CONV(%%s, %s, %s, CLASS(%p))", JIT_get_type(src), JIT_get_type(dest), (CLASS *)dest); + else if (src == T_FUNCTION) + sprintf(buffer, "CONV(NULL, F, %s, %s)", JIT_get_type(dest), JIT_get_gtype(dest)); + else + sprintf(buffer, "CONV(%%s, %s, %s, %s)", JIT_get_type(src), JIT_get_type(dest), JIT_get_gtype(dest)); + + return buffer; +} + +static char *get_conv(TYPE src, TYPE dest, char *expr) +{ + char *new_expr; + char *borrow; + + if (dest == T_VOID) + { + switch (TYPEID(src)) + { + case T_OBJECT: + case T_STRING: + case T_VARIANT: + borrow = borrow_expr(expr, src); + new_expr = STR_print("RELEASE_%s(%s)", JIT_get_type(src), borrow); + STR_free(borrow); + return new_expr; + } + } + + if (src == T_VOID && !expr) // void method arguments + new_expr = STR_copy(JIT_get_default_value(TYPEID(dest))); + else + new_expr = STR_print(get_conv_format(src, dest), expr); + + STR_free(expr); + return new_expr; +} + +static char *peek(int n, TYPE conv) +{ + STACK_SLOT *ss; + char *expr; + TYPE type; + + ss = get_stack(n); + + expr = ss->expr; + type = ss->type; + + if (type == T_FUNCTION && !expr) + expr = ss->expr = STR_print("GET_FUNCTION(%d)", ss->pc); + + if (type != conv) + ss->expr = expr = get_conv(type, conv, expr); + + return expr; +} + + +static char *peek_pop(int n, TYPE conv, const char *fmt, va_list args) +{ + char *dest = NULL; + char *expr; + TYPE type; + char *op; + + if (n < 0) n += _stack_current; + + expr = _stack[n].expr; + type = _stack[n].type; + + if (fmt) + { + STR_vadd(&dest, fmt, args); + + if (!_no_release) + { + switch (TYPEID(conv)) + { + case T_STRING: + declare(&_decl_rs, "char *rs"); + JIT_print(" if ((%s).type == GB_T_STRING) rs = (%s).value.addr; else rs = NULL;\n", dest, dest); + break; + + case T_OBJECT: + declare(&_decl_ro, "void *ro"); + JIT_print(" ro = (%s).value;\n", dest); + break; + + case T_VARIANT: + declare(&_decl_rv, "GB_VARIANT rv"); + JIT_print(" rv = (%s);\n", dest); + break; + } + } + } + + if (type != conv) + _stack[n].expr = expr = get_conv(type, conv, expr); + + if (fmt) + { + if (!_no_release || _no_release_but_borrow) + { + switch (TYPEID(conv)) + { + case T_STRING: + case T_OBJECT: + case T_VARIANT: + _stack[n].expr = expr = borrow_expr(expr, conv); + break; + } + } + + if (_no_release) + { + JIT_print(" "); + JIT_print(dest, expr); + JIT_print(";\n"); + } + else + { + if (dest[strlen(dest) - 1] != '=') + op = " ="; + else + op = ""; + + JIT_print(" %s%s %s;\n", dest, op, expr); + + switch (TYPEID(conv)) + { + case T_STRING: JIT_print(" GB.FreeString(&rs);\n"); break; + case T_OBJECT: JIT_print(" GB.Unref(&ro);\n"); break; + case T_VARIANT: JIT_print(" GB.ReleaseValue((GB_VALUE *)&rv);\n"); break; + } + } + + STR_free(dest); + } + + return expr; +} + + +static char *push_expr(int n, TYPE type) +{ + const char *type_name; + char *expr; + char *new_expr; + int len; + + type_name = JIT_get_type(type); + + expr = peek(n, type); + + if (type == T_VOID) + return "PUSH_V()"; + + if (type == T_FUNCTION) + { + new_expr = STR_print("CALL_UNKNOWN(%d)", get_stack(n)->pc); + } + else + { + len = strlen(expr); + if ((strncmp(&expr[len - 5], "();})", 5) == 0) && (strncmp(&expr[len - 10], "POP_", 4) == 0) && (expr[len - 6] == *type_name)) + new_expr = STR_print("%.*s})", len - 10, expr); + else + new_expr = STR_print("PUSH_%s(%s)", type_name, expr); + } + + STR_free(expr); + set_expr(n, new_expr); + + //fprintf(stderr, "push_expr %s ===> %s\n", expr, new_expr); + + return new_expr; +} + + +static void pop(TYPE type, const char *fmt, ...) +{ + va_list args; + char *expr; + + check_stack(1); + + _stack_current--; + + va_start(args, fmt); + expr = peek_pop(_stack_current, type, fmt, args); + va_end(args); + + if (!fmt) + JIT_print(" %s;\n", expr); + + free_stack(_stack_current); +} + + +static bool check_swap(TYPE type, const char *fmt, ...) +{ + va_list args; + char *expr = NULL; + char *swap = NULL; + + if (_has_just_dup) + { + _has_just_dup = FALSE; + return TRUE; + } + + if (_stack_current < 2) + return TRUE; + + STR_add(&expr, "({ %s _t = %s; ", JIT_get_ctype(type), peek(-2, type)); + + va_start(args, fmt); + STR_vadd(&swap, fmt, args); + va_end(args); + STR_add(&expr, swap, peek(-1, type)); + STR_add(&expr, "; _t; })"); + + pop_stack(2); + + push(type, "%s", expr); + + STR_free(swap); + STR_free(expr); + + return FALSE; +} + + +static int add_ctrl(int index, TYPE type, const char *expr) +{ + int index_ctrl; + CTRL_INFO *info; + + index_ctrl = GB.Count(_ctrl_info); + + info = (CTRL_INFO *)GB.Add(&_ctrl_info); + + info->type = type; + if (expr) + info->expr = STR_copy(expr); + else + info->expr = NULL; + + if (index >= 0) + _ctrl_index[index] = index_ctrl; + + //JIT_print_decl(" %s c%d;\n", JIT_get_ctype(type), index_ctrl); + JIT_declare(type, "c%d", index_ctrl); + + return index_ctrl; +} + + +static void pop_ctrl(int index, TYPE type) +{ + int index_ctrl; + char *expr; + + if (type == T_VOID) + type = get_type(-1); + + if (type == T_CLASS) + expr = get_expr(-1); + else + expr = NULL; + + index_ctrl = add_ctrl(index, type, expr); + + //_no_release = TRUE; + if (expr) + pop_stack(1); + else + pop(type, "c%d", index_ctrl); + //_no_release = FALSE; +} + + +static void push_constant(CLASS *class, int index) +{ + CLASS_CONST *cc = &class->load->cst[index]; + + switch(cc->type) + { + case T_BOOLEAN: push(T_BOOLEAN, "(bool)%d", cc->_integer.value); break; + case T_BYTE: push(T_BYTE, "(uchar)%d", cc->_integer.value); break; + case T_SHORT: push(T_SHORT, "(short)%d", cc->_integer.value); break; + case T_INTEGER: push(T_INTEGER, "(int)%d", cc->_integer.value); break; + case T_LONG: push(T_LONG, "(int64_t)%" PRId64, cc->_long.value); break; + case T_SINGLE: push(T_SINGLE, "(*(float *)%p)", &cc->_single.value); break; + case T_FLOAT: push(T_FLOAT, "(*(double *)%p)", &cc->_float.value); break; + case T_STRING: push(T_CSTRING, "CONSTANT_s(%p, %d)", cc->_string.addr, cc->_string.len); break; + case T_CSTRING: push(T_CSTRING, "CONSTANT_t(%p, %d)", cc->_string.addr, 0); break; + case T_POINTER: push(T_POINTER, "(intptr_t)0"); break; + default: JIT_panic("unknown constant type"); + } +} + + +static void push_function(int func, int index) +{ + push(T_FUNCTION, NULL); + _stack[_stack_current - 1].func = func; + _stack[_stack_current - 1].index = index; + _stack[_stack_current - 1].pc = _pc; +} + + +static void push_unknown(int index) +{ + TYPE type = T_UNKNOWN; + TYPE call_type = T_UNKNOWN; + char *expr; + CLASS *class; + + check_stack(1); + + // TODO: Check object + // if (!class->is_simple && ... + // if (UNLIKELY(class->must_check && (*(class->check))(object))) + // THROW(E_IOBJECT); + + class = get_class(-1); + + if (class) + { + CLASS_DESC *desc; + void *addr; + int pos; + TYPE utype; + char *sym; + char *get_addr; + bool static_class; + + static_class = get_type(-1) == T_CLASS; + + sym = JIT_class->load->unknown[index]; + + if (class == (CLASS *)GB.FindClass("Param")) + { + if (!strcasecmp(sym, "Count")) + { + pop_stack(1); + push(T_INTEGER, _func->vararg ? "nv" : "0"); + return; + } + else if (!strcasecmp(sym, "Max")) + { + pop_stack(1); + push(T_INTEGER, _func->vararg ? "(nv - 1)" : "-1"); + return; + } + } + + index = JIT_find_symbol(class, sym); + + if (index != NO_SYMBOL) + { + desc = class->table[index].desc; + class = desc->method.class; + utype = JIT_ctype_to_type(class, desc->variable.ctype); + + switch (CLASS_DESC_get_type(desc)) + { + case CD_STATIC_VARIABLE: + + pop_stack(1); + + addr = (char *)desc->variable.class->stat + desc->variable.offset; + + if (TYPE_is_object(utype)) + { + if (TYPE_is_pure_object(utype)) + push(utype, "({ JIT.load_class(%p); GET_o(%p, CLASS(%p)); })", class, addr, (CLASS *)utype); + else + push(utype, "({ JIT.load_class(%p); GET_o(%p, GB_T_OBJECT); })", class, addr); + } + else + push(utype, "({ JIT.load_class(%p); GET_%s(%p); })", class, JIT_get_type(utype), addr); + + return; + + case CD_VARIABLE: + + // TODO: automatic class + + expr = peek(-1, (TYPE)class); + + pos = desc->variable.offset; + + if (_unsafe) + get_addr = STR_print("ADDR_UNSAFE(%s)", expr); + else if (class->must_check) + get_addr = STR_print("ADDR_CHECK(%p, %s)", class->check, expr); + else + get_addr = STR_print("ADDR(%s)", expr); + + pop_stack(1); + + if (TYPE_is_object(utype)) + { + if (TYPE_is_pure_object(utype)) + push(utype, "GET_o(%s + %d, CLASS(%p))", get_addr, pos, (CLASS *)utype); + else + push(utype, "GET_o(%s + %d, GB_T_OBJECT)", get_addr, pos); + } + else + push(utype, "GET_%s(%s + %d)", JIT_get_type(utype), get_addr, pos); + + STR_free(get_addr); + + return; + + case CD_CONSTANT: + + if (!static_class) + { + type = desc->constant.type; + break; + } + + pop_stack(1); + + switch(desc->constant.type) + { + case T_BOOLEAN: push(T_BOOLEAN, "(bool)%d", desc->constant.value._integer); break; + case T_BYTE: push(T_BYTE, "(uchar)%d", desc->constant.value._integer); break; + case T_SHORT: push(T_SHORT, "(short)%d", desc->constant.value._integer); break; + case T_INTEGER: push(T_INTEGER, "(int)%d", desc->constant.value._integer); break; + case T_LONG: push(T_LONG, "(int64_t)%" PRId64, desc->constant.value._long); break; + case T_SINGLE: push(T_SINGLE, "(*(float *)%p)", &desc->constant.value._single); break; + case T_FLOAT: push(T_FLOAT, "(*(double *)%p)", &desc->constant.value._float); break; + case T_POINTER: push(T_POINTER, "(intptr_t)%p", desc->constant.value._pointer); break; + case T_STRING: case T_CSTRING: + if (desc->constant.translate) + push(T_CSTRING, "CONSTANT_t(%p, %d)", desc->constant.value._string, strlen(desc->constant.value._string)); + else + push(T_CSTRING, "CONSTANT_s(%p, %d)", desc->constant.value._string, strlen(desc->constant.value._string)); + break; + default: JIT_panic("unknown constant type"); + } + + return; + + case CD_PROPERTY: + case CD_STATIC_PROPERTY: + case CD_PROPERTY_READ: + case CD_STATIC_PROPERTY_READ: + + type = desc->property.type; + break; + + case CD_METHOD: + case CD_STATIC_METHOD: + + call_type = desc->method.type; + break; + } + } + else + JIT_print(" // %s.%s ?\n", class->name, sym); + } + + expr = STR_copy(push_expr(-1, get_type(-1))); + pop_stack(1); + push(type, "({%s;PUSH_UNKNOWN(%d);POP_%s();})", expr, _pc, JIT_get_type(type)); + + _stack[_stack_current - 1].call = call_type; + + STR_free(expr); +} + + +static void pop_unknown(int index) +{ + CLASS *class; + char *expr = NULL; + char *arg; + + check_stack(2); + + class = get_class(-1); + + if (class) + { + CLASS_DESC *desc; + void *addr; + int pos; + TYPE utype; + const char *sym; + char *get_addr; + + sym = JIT_class->load->unknown[index]; + index = JIT_find_symbol(class, sym); + if (index != NO_SYMBOL) + { + desc = class->table[index].desc; + utype = JIT_ctype_to_type(class, desc->variable.ctype); + + switch (CLASS_DESC_get_type(desc)) + { + case CD_STATIC_VARIABLE: + + pop_stack(1); + + addr = (char *)desc->variable.class->stat + desc->variable.offset; + + _no_release = TRUE; + if (check_swap(utype, "SET_%s(%p, %%s)", JIT_get_type(utype), addr)) + pop(utype, "SET_%s(%p, %%s)", JIT_get_type(utype), addr); + _no_release = FALSE; + + return; + + case CD_VARIABLE: + + // TODO: automatic class + + expr = peek(-1, (TYPE)class); + + if (_unsafe) + get_addr = STR_print("ADDR_UNSAFE(%s)", expr); + else if (class->must_check) + get_addr = STR_print("ADDR_CHECK(%p, %s)", class->check, expr); + else + get_addr = STR_print("ADDR(%s)", expr); + + pop_stack(1); + + pos = desc->variable.offset; + + _no_release = TRUE; + if (check_swap(utype, "SET_%s(%s + %d, %%s)", JIT_get_type(utype), get_addr, pos)) + pop(utype, "SET_%s(%s + %d, %%s)", JIT_get_type(utype), get_addr, pos); + _no_release = FALSE; + + STR_free(get_addr); + + return; + } + } + else + JIT_print(" // %s.%s ?\n", class->name, sym); + } + + arg = push_expr(-2, get_type(-2)); + STR_add(&expr,"%s;", arg); + + arg = push_expr(-1, get_type(-1)); + STR_add(&expr, "%s;POP_UNKNOWN(%d);", arg, _pc); + + pop_stack(2); + + push(T_VOID, "({%s})", expr); + + if (check_swap(T_UNKNOWN, "({%s})", expr)) + pop(T_VOID, NULL); + + STR_free(expr); +} + + +static void push_array(ushort code) +{ + TYPE type; + int i, narg; + char *expr = NULL; + char *expr1, *expr2; + const char *unsafe = _unsafe ? "_UNSAFE" : ""; + + narg = code & 0x3F; + check_stack(narg); + + type = get_type(-narg); + + if (TYPE_is_pure_object(type)) + { + CLASS *class = (CLASS *)type; + + //JIT_print(" // %s %d\n", class->name, class->is_array); + + if (class->is_array) + { + type = class->array_type; + + if (narg == 2) + { + expr1 = peek(-2, get_type(-2)); + expr2 = peek(-1, T_INTEGER); + + if (TYPE_is_pure_object(type)) + expr = STR_print("PUSH_ARRAY_O(%s,%s,CLASS(%p),%s)", expr1, expr2, (CLASS *)type, unsafe); + else + expr = STR_print("PUSH_ARRAY_%s(%s,%s,%s)", JIT_get_type(type), expr1, expr2, unsafe); + + pop_stack(2); + + push(type, "(%s)", expr); + + STR_free(expr); + + return; + } + } + else + type = T_UNKNOWN; + } + else + type = T_UNKNOWN; + + //declare_sp(); + + for (i = _stack_current - narg; i < _stack_current; i++) + { + STR_add(&expr, "%s;", push_expr(i, get_type(i))); + free_stack(i); + } + + _stack_current -= narg; + + STR_add(&expr, "CALL_PUSH_ARRAY(%d, 0x%04X);POP_%s();", _pc, code, JIT_get_type(type)); + + push(type, "({%s})", expr); + + STR_free(expr); +} + + +static void pop_array(ushort code) +{ + TYPE type; + int i, narg; + char *expr = NULL; + char *expr1, *expr2; + const char *unsafe = _unsafe ? "_UNSAFE" : ""; + + narg = code & 0x3F; + check_stack(narg + 1); + + type = get_type(-narg); + + if (TYPE_is_pure_object(type)) + { + CLASS *class = (CLASS *)type; + + if (class->is_array) + { + type = class->array_type; + + if (narg == 2) + { + expr1 = peek(-2, get_type(-2)); + expr2 = peek(-1, T_INTEGER); + + STR_add(&expr, "POP_ARRAY_%s(%s,%s,%s,%s);", JIT_get_type(type), expr1, expr2, peek(-3, type), unsafe); + + pop_stack(3); + + goto CHECK_SWAP; + } + } + + } + else + type = T_UNKNOWN; + + //declare_sp(); + + narg++; + + for (i = _stack_current - narg; i < _stack_current; i++) + { + STR_add(&expr, "%s;", push_expr(i, get_type(i))); + free_stack(i); + } + + _stack_current -= narg; + + STR_add(&expr, "CALL_POP_ARRAY(%d, 0x%04X);sp--;", _pc, code); + +CHECK_SWAP: + + push(T_VOID, "({%s})", expr); + + if (check_swap(type, "({%s})", expr)) + pop(T_VOID, NULL); + + STR_free(expr); +} + + +static void push_subr(char mode, ushort code) +{ + const char *call; + TYPE type; + int i, narg; + char *expr = NULL; + ushort op; + bool rst = FALSE; + char type_id = 0; + void *addr; + + //declare_sp(); + + //JIT_print(" static ushort s%d = 0x%04X;\n", _subr_count, code); + + op = code >> 8; + + switch(mode & 7) + { + case CALL_SUBR_CODE: + call = "CALL_SUBR_CODE(%d, %p, 0x%04X)"; + addr = JIT.subr_table[op]; + break; + case CALL_SUBR: + call = "CALL_SUBR(%d, %p)"; + addr = JIT.subr_table[op]; + break; + case CALL_SUBR_UNKNOWN: + call = "CALL_SUBR_UNKNOWN(%d)"; + addr = NULL; + break; + case CALL_NEW: + call = "CALL_SUBR_CODE(%d, %p, 0x%04X)"; + addr = JIT.new; + break; + default: + return; + } + + if (op == (C_NEW >> 8)) + { + narg = code & 0x3F; + type = get_type(-narg); + } + else if (op < CODE_FIRST_SUBR) + { + int index = RESERVED_get_from_opcode(code); + + if (index < 0) + JIT_panic("Unknown operator"); + + if (RES_is_unary(index)) + narg = 1; + else if (RES_is_binary(index)) + narg = 2; + else + narg = code & 0x3F; + + type_id = COMP_res_info[index].type; + rst = TRUE; + } + else + { + SUBR_INFO *info = SUBR_get_from_opcode(op - CODE_FIRST_SUBR, code & 0x3F); + if (!info) + JIT_panic("unknown subroutine"); + if (info->min_param <= info->max_param) + narg = code & 0x3F; + else + narg = info->min_param; + + type_id = info->type; + rst = TRUE; + } + + check_stack(narg); + + if (rst) + { + switch(type_id) + { + case RST_SAME: + case RST_BCLR: + type = get_type(-narg); + break; + + case RST_MIN: + type = Max(get_type(-1), get_type(-2)); + if (type > T_DATE && type != T_VARIANT) + type = T_UNKNOWN; + break; + + case RST_COLLECTION: + type = GB.FindClass("Collection"); + break; + + case RST_EXEC: + i = atoi(get_expr(-2)); + if ((i & PM_WAIT) && (i & PM_STRING)) + type = T_STRING; + else + type = GB.FindClass("Process"); + break; + + case RST_READ: + type = get_type(-1); + if (type == T_INTEGER) + type = atoi(get_expr(-1)); + else if (type == T_CLASS) + type = (TYPE)get_class(-1); + break; + + default: + type = (type_id >= T_UNKNOWN) ? T_UNKNOWN : type_id; + } + } + + if (op == (C_NEW >> 8)) + { + if (type == T_CLASS) + type = (TYPE)get_class(-narg); + else + type = T_OBJECT; + } + else if (op == CODE_DEBUG) + { + STR_add(&expr, "FP=(void *)%p;PC = &pc[%d];", _func, _pc); + type = narg == 0 ? T_INTEGER : T_BOOLEAN; + } + + if (narg > 0) + { + for (i = _stack_current - narg; i < _stack_current; i++) + { + STR_add(&expr, "%s;", push_expr(i, get_type(i))); + free_stack(i); + } + + _stack_current -= narg; + } + + STR_add(&expr, call, _pc, addr, code); + + if (mode & CALL_RETURN_UNKNWON) + type = T_UNKNOWN; + + STR_add(&expr, ";POP_%s();", JIT_get_type(type)); + + push(type, "({%s})", expr); + + STR_free(expr); +} + + +static void push_subr_add(ushort code, const char *op, const char *opb, bool allow_pointer) +{ + char *expr; + char *expr1, *expr2; + TYPE type1, type2, type; + + check_stack(2); + + type1 = get_type(-2); + type2 = get_type(-1); + + if (TYPEID(type1) > TYPEID(type2)) + type = type1; + else + type = type2; + + switch(type) + { + case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: case T_SINGLE: case T_FLOAT: + break; + + case T_DATE: case T_STRING: case T_CSTRING: + type = T_FLOAT; + break; + + case T_POINTER: + if (allow_pointer) + break; + + default: + push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNWON, code); + return; + } + + expr1 = peek(-2, type); + expr2 = peek(-1, type); + + if (type == T_BOOLEAN) + op = opb; + + expr = STR_print("({%s _a = %s; %s _b = %s; _a %s _b;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, op); + + pop_stack(2); + + push(type, "(%s)", expr); + STR_free(expr); +} + + +static void push_subr_div(ushort code) +{ + char *expr; + char *expr1, *expr2; + TYPE type1, type2, type; + + check_stack(2); + + type1 = get_type(-2); + type2 = get_type(-1); + + if (TYPEID(type1) > TYPEID(type2)) + type = type1; + else + type = type2; + + switch(type) + { + case T_SINGLE: case T_FLOAT: + break; + + case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: + type = T_FLOAT; + break; + + default: + push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNWON, code); + return; + } + + expr1 = peek(-2, type); + expr2 = peek(-1, type); + + if (_unsafe) + expr = STR_print("({%s _a = %s; %s _b = %s; _a /= _b; _a;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2); + else + expr = STR_print("({%s _a = %s; %s _b = %s; _a /= _b; if (!isfinite(_a)) THROW(E_ZERO); _a;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2); + + pop_stack(2); + + push(type, "(%s)", expr); + STR_free(expr); +} + + +static void push_subr_arithmetic(char op, ushort code) +{ + TYPE type; + char *expr; + const char *func; + + check_stack(1); + + type = get_type(-1); + + switch (op) + { + case MATH_ABS: func = "MATH_ABS"; break; + case MATH_NEG: func = "- "; break; + case MATH_SGN: func = "MATH_SGN"; break; + } + + switch(type) + { + case T_BOOLEAN: + if (op == MATH_NEG) + return; + break; + + case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: case T_SINGLE: case T_FLOAT: + break; + + default: + push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNWON, code); + return; + } + + expr = STR_copy(peek(-1, type)); + + pop_stack(1); + push(type, "(%s(%s))", func, expr); + STR_free(expr); +} + + +static void push_subr_float_arithmetic(char op, ushort code) +{ + TYPE type; + char *expr; + const char *func; + + check_stack(1); + + type = get_type(-1); + + switch(type) + { + case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: + return; + + case T_SINGLE: + func = op == MATH_FIX ? "MATH_FIX_g" : "floorf"; + break; + + case T_FLOAT: + func = op == MATH_FIX ? "MATH_FIX_f" : "floor"; + break; + + default: + push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNWON, code); + return; + } + + expr = STR_copy(peek(-1, type)); + + pop_stack(1); + push(type, "(%s(%s))", func, expr); + STR_free(expr); +} + + +static void push_subr_quo(ushort code, const char *op) +{ + char *expr; + char *expr1, *expr2; + TYPE type1, type2, type; + + check_stack(2); + + type1 = get_type(-2); + type2 = get_type(-1); + + if (TYPEID(type1) > TYPEID(type2)) + type = type1; + else + type = type2; + + switch(type) + { + case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: + break; + + default: + push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNWON, code); + return; + } + + expr1 = peek(-2, type); + expr2 = peek(-1, type); + + if (_unsafe) + expr = STR_print("({%s _a = %s; %s _b = %s; _a %s _b;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, op); + else + expr = STR_print("({%s _a = %s; %s _b = %s; if (_b == 0) THROW(E_ZERO); _a %s _b;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, op); + + pop_stack(2); + push(type, "(%s)", expr); + STR_free(expr); +} + + +static void push_subr_and(ushort code, const char *op) +{ + char *expr; + char *expr1, *expr2; + TYPE type1, type2, type; + + check_stack(2); + + type1 = get_type(-2); + type2 = get_type(-1); + + if (TYPEID(type1) > TYPEID(type2)) + type = type1; + else + type = type2; + + switch(type) + { + case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: + break; + + case T_DATE: case T_STRING: case T_CSTRING: + type = T_BOOLEAN; + break; + + default: + push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNWON, code); + return; + } + + expr1 = peek(-2, type); + expr2 = peek(-1, type); + + expr = STR_print("({%s _a = %s; %s _b = %s; _a %s _b;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, op); + + pop_stack(2); + push(type, "(%s)", expr); + STR_free(expr); +} + + +static void push_subr_not(ushort code) +{ + TYPE type; + char *expr; + char *op; + + check_stack(1); + + type = get_type(-1); + + switch(type) + { + case T_BOOLEAN: + op = "!"; + break; + + case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: + op = "~"; + break; + + default: + push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNWON, code); + return; + } + + expr = STR_print("%s%s", op, peek(-1, type)); + + pop_stack(1); + push(type, "(%s)", expr); + STR_free(expr); +} + + +static void push_subr_comp(ushort code) +{ + char *op = NULL; + char *expr; + char *expr1, *expr2; + TYPE type1, type2, type; + + check_stack(2); + + type1 = get_type(-2); + type2 = get_type(-1); + + if (TYPEID(type1) > TYPEID(type2)) + type = type1; + else + type = type2; + + switch(type) + { + case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: case T_SINGLE: case T_FLOAT: case T_POINTER: + + switch(code & 0xFF00) + { + case C_EQ: op = "=="; break; + case C_NE: op = "!="; break; + case C_GT: op = ">"; break; + case C_LT: op = "<"; break; + case C_GE: op = ">="; break; + case C_LE: op = "<="; break; + } + break; + } + + if (!op) + { + switch(code & 0xFF00) + { + case C_EQ: case C_NE: + push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNWON, code); + break; + + case C_GT: case C_LT: case C_GE: case C_LE: + push_subr(CALL_SUBR_UNKNOWN + CALL_RETURN_UNKNWON, code); + break; + } + + return; + } + + expr1 = peek(-2, type); + expr2 = peek(-1, type); + + expr = STR_print("({%s _a = %s; %s _b = %s; _a %s _b;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, op); + + pop_stack(2); + + push(T_BOOLEAN, "(%s)", expr); + STR_free(expr); +} + + +static void push_subr_bit(ushort code) +{ + static const char *_actions[] = { + NULL, + "_v &= ~((%s)1 << _b)", // BClr + "_v |= ((%s)1 << _b)", // BSet + "((_v & ((%s)1 << _b)) != 0)", // BTst + "_v ^= ((%s)1 << _b)", // BChg + "_v = (((%s)_v << _b) & 0x%s) | (((%s)_v) & 0x%s)", // Asl + "_v = (((%s)_v >> _b) & 0x%s) | (((%s)_v) & 0x%s)", // Asr + "_v = ((%s)_v << _b) | ((%s)_v >> (%d - _b))", // Rol + "_v = ((%s)_v >> _b) | ((%s)_v << (%d - _b))", // Ror + "_v = ((%s)_v << _b)", // Lsl + "_v = ((%s)_v >> _b)", // Lsr + }; + + const char *action; + char *expr; + char *expr1, *expr2; + TYPE type; + const char *ctype, *uctype, *mask, *mask2; + int nbits; + + check_stack(2); + + type = get_type(-2); + + switch(type) + { + case T_BYTE: + ctype = "uchar"; uctype = "uchar"; + mask = "7F"; mask2 = "80"; + nbits = 8; + break; + + case T_SHORT: + ctype = "short"; uctype= "ushort"; + mask = "7FFF"; mask2 = "8000"; + nbits = 16; + break; + + case T_INTEGER: + ctype = "int"; uctype = "uint"; + mask = "7FFFFFFF"; mask2 = "80000000"; + nbits = 32; + break; + + case T_LONG: + ctype = "int64_t"; uctype= "uint64_t"; + mask = "7FFFFFFFFFFFFFFFLL"; mask2 = "8000000000000000LL"; + nbits = 64; + break; + + default: + push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNWON, code); + return; + } + + expr1 = peek(-2, type); + expr2 = peek(-1, T_INTEGER); + + code &= 0x3F; + + action = _actions[code]; + + if (_unsafe) + expr = STR_print("({ %s _v = %s; int _b = %s; ", ctype, expr1, expr2, nbits); + else + expr = STR_print("({ %s _v = %s; int _b = %s; if ((_b < 0) || (_b >= %d)) THROW(E_ARG); ", ctype, expr1, expr2, nbits); + + + switch(code) + { + case 1: case 2: case 3: case 4: + STR_add(&expr, action, uctype); + break; + + case 5: case 6: + STR_add(&expr, action, uctype, mask, uctype, mask2); + break; + + case 7: case 8: + STR_add(&expr, action, uctype, uctype, nbits); + break; + + case 9: case 10: + STR_add(&expr, action, uctype); + break; + } + + if (code == 3) + STR_add(&expr, "; })"); + else + STR_add(&expr, "; _v; })"); + + pop_stack(2); + + push(code == 3 ? T_BOOLEAN : type, "(%s)", expr); + STR_free(expr); +} + + +static void push_subr_conv(ushort code) +{ + char *expr; + TYPE type; + TYPE conv = code & 0x3F; + + check_stack(1); + type = get_type(-1); + + if (type == conv) + return; + + expr = STR_copy(peek(-1, conv)); + pop_stack(1); + push(conv, "(%s)", expr); + STR_free(expr); +} + + +static void push_subr_len(ushort code) +{ + char *expr; + + check_stack(1); + + expr = STR_copy(peek(-1, T_STRING)); + pop_stack(1); + push(T_INTEGER, "((%s).value.len)", expr); + STR_free(expr); +} + + +/*static void push_subr_left(ushort code) +{ + char *expr = NULL; + TYPE type; + char *expr_str, *expr_len = NULL; + + declare_sp(); + + JIT_print(" static ushort s%d = 0x%04X;\n", _subr_count, code); + + check_stack(1); + + if (_stack_current >= 2) + { + expr_len = peek(-1, T_INTEGER); + pop_stack(1); + } + + type = get_type(-1); + expr_str = peek(-1, T_CSTRING); + pop_stack(1); + + STR_add(&expr, "(PUSH_t(%s)", expr_str); + STR_free(expr_str); + if (expr_len) + { + STR_add(&expr, ",PUSH_i(%s)", expr_len); + STR_free(expr_len); + } + + push(type, "%s,CALL_SUBR_CODE(s%d),POP_t())", expr, _subr_count, code); + _subr_count++; +}*/ + + +static void push_call(ushort code) +{ + char *call = NULL; + const char *def; + int i, j; + int narg; + FUNCTION *func; + CLASS_EXTERN *ext; + int func_kind, func_index; + TYPE func_type; + int nopt, opt; + int nv; + + narg = code & 0x3F; + + if (_stack_current > narg && get_type(- narg - 1) == T_FUNCTION) + { + STACK_SLOT *s = &_stack[_stack_current - narg - 1]; + func_kind = s->func; + func_index = s->index; + func_type = T_UNKNOWN; + } + else + { + STACK_SLOT *s = &_stack[_stack_current - narg - 1]; + func_kind = CALL_UNKNOWN; + func_type = s->call; + } + + switch (func_kind) + { + case CALL_PRIVATE: + + func = &JIT_class->load->func[func_index]; + if (func->fast) + { + if (narg < func->npmin) + { + pop_stack(narg + 1); + push(T_UNKNOWN, "({ GB_VALUE temp; THROW(E_NEPARAM); temp; })"); + } + else if (narg > func->n_param && !func->vararg) + { + pop_stack(narg + 1); + push(T_UNKNOWN, "({ GB_VALUE temp; THROW(E_TMPARAM); temp; })"); + } + else + { + nv = 0; + if (func->vararg && (narg > func->n_param)) + { + if (func->type != T_VOID) + STR_add(&call, "%s _r;", JIT_get_ctype(func->type)); + + nv = narg - func->n_param; + for (i = 0; i < nv; i++) + STR_add(&call, "%s;", push_expr(i - nv, get_type(i - nv))); + } + + STR_add(&call, "SP=sp;"); + + if (nv && func->type != T_VOID) + STR_add(&call, "_r="); + + STR_add(&call, "jit_%s_%d_(", JIT_prefix, func_index); + + for (i = 0; i < func->npmin; i++) + { + if (i) STR_add(&call, ","); + STR_add(&call, "%s", peek(i - narg, func->param[i].type)); + } + + nopt = 0; + for (; i < func->n_param; i++) + { + if (i) STR_add(&call, ","); + + if (nopt == 0) + { + opt = 0; + for (j = 0; j < 8; j++) + { + if ((i + j) >= func->n_param) + break; + if (((i + j) >= narg) || get_type(i + j - narg) == T_VOID) + opt |= 1 << j; + } + STR_add(&call, "%d,", opt); + } + + if (i < narg) + STR_add(&call, "%s", peek(i - narg, func->param[i].type)); + else + { + def = JIT_get_default_value(func->param[i].type); + STR_add(&call, "({ %s temp = %s; temp; })", JIT_get_ctype(func->param[i].type), def); + } + + nopt++; + if (nopt >= 8) + nopt = 0; + } + + if (func->vararg) + { + if (func->n_param) + STR_add(&call, ","); + STR_add(&call, "%d,&sp[-%d]", nv, nv); + } + + STR_add(&call, ");"); + + if (nv) + { + STR_add(&call, "JIT.release_many(sp,%d);sp -= %d;", nv ,nv); + if (func->type != T_VOID) + STR_add(&call, "_r;"); + } + + pop_stack(narg + 1); + + push(func->type, "({%s})", call); + } + } + else + { + STR_add(&call, "PUSH_PRIVATE_FUNCTION(%d);", func_index); + + for (i = 0; i < narg; i++) + STR_add(&call, "%s;", push_expr(i - narg, get_type(i - narg))); + + pop_stack(narg + 1); + + STR_add(&call, "CALL_UNKNOWN(%d);POP_%s();", _pc, JIT_get_type(func->type)); + + push(func->type, "({%s})", call); + } + + break; + + case CALL_UNKNOWN: + + narg++; + for (i = 0; i < narg; i++) + STR_add(&call, "%s;", push_expr(i - narg, get_type(i - narg))); + + pop_stack(narg); + + STR_add(&call, "CALL_UNKNOWN(%d);POP_%s();", _pc, JIT_get_type(func_type)); + + push(func_type, "({%s})", call); + + break; + + case CALL_EVENT: + + for (i = 0; i < narg; i++) + STR_add(&call, "%s;", push_expr(i - narg, get_type(i - narg))); + + pop_stack(narg + 1); + + if (func_index != NO_SYMBOL) + STR_add(&call, "RAISE_EVENT(%d,%d);", func_index, narg); + else + STR_add(&call, "RAISE_UNKNOWN_EVENT(%d);", _pc); + + push(T_BOOLEAN, "({%s})", call); + + break; + + case CALL_EXTERN: + + ext = &JIT_class->load->ext[func_index]; + + if (narg < ext->n_param) + { + pop_stack(narg + 1); + push(T_UNKNOWN, "({ GB_VALUE temp; THROW(E_NEPARAM); temp })"); + } + else if (narg > ext->n_param && !ext->vararg) + { + pop_stack(narg + 1); + push(T_UNKNOWN, "({ GB_VALUE temp; THROW(E_TMPARAM); temp })"); + } + else + { + STR_add(&call,"SP = sp;(*(%s (*)())%p)(", JIT_get_ctype(ext->type), JIT.get_extern(ext)); + + for (i = 0; i < ext->n_param; i++) + { + if (i) STR_add(&call, ","); + STR_add(&call, "%s", peek(i - narg, ext->param[i].type)); + } + + STR_add(&call, ");"); + + pop_stack(narg + 1); + + push(ext->type, "({%s})", call); + } + + break; + + default: + + JIT_panic("Unsupported call"); + } + + STR_free(call); +} + + +static void push_subr_isnan(ushort code) +{ + char *func; + char *expr; + + check_stack(1); + + switch (code & 0xFF) + { + case 1: // IsNan + func = "isnan"; + break; + + case 2: // IsInf + func = "isinf"; + break; + + default: + push_subr(CALL_SUBR_CODE, code); + return; + } + + expr = STR_print("%s(%s) != 0", func, peek(-1, T_FLOAT)); + + pop_stack(1); + push(T_BOOLEAN, "(%s)", expr); + STR_free(expr); +} + +static void push_subr_math(ushort code) +{ + static const char *func[] = { + NULL, "frac(%s)", "__builtin_log(%s)", "__builtin_exp(%s)", "__builtin_sqrt(%s)", "__builtin_sin(%s)", "__builtin_cos(%s)", "__builtin_tan(%s)", + "__builtin_atan(%s)", "__builtin_asin(%s)", "__builtin_acos(%s)", + "((%s) * 180 / M_PI)", // deg() + "((%s) * M_PI / 180)", // rad() + "log10(%s)", + "__builtin_sinh(%s)", "__builtin_cosh(%s)", "__builtin_tanh(%s)", "__builtin_asinh(%s)", "__builtin_acosh(%s)", "__builtin_atanh(%s)", +#ifndef HAVE_EXP2 + "pow(2, (%s))", +#else + "__builtin_exp2(%s)", +#endif +#ifndef HAVE_EXP10 + "pow(10, (%s))", +#else + "__builtin_exp10(%s)", +#endif +#ifndef HAVE_LOG2 + "(log(%s) / M_LN2)", +#else + "__builtin_log2(%s)", +#endif + "__builtin_cbrt(%s)", "__builtin_expm1(%s)", "__builtin_log1p(%s)", "__builtin_floor(%s)", "__builtin_ceil(%s)" + }; + + char *expr; + + check_stack(1); + + expr = STR_print(func[code & 0x1F], peek(-1, T_FLOAT)); + pop_stack(1); + + push(T_FLOAT, "%s(%s)", _unsafe ? "CALL_MATH_UNSAFE" : "CALL_MATH", expr); + + STR_free(expr); +} + + +static void push_subr_pi(ushort code) +{ + char *expr; + + if ((code & 0xFF) == 0) + { + push(T_FLOAT, "M_PI"); + return; + } + + check_stack(1); + + expr = STR_copy(peek(-1, T_FLOAT)); + pop_stack(1); + + push(T_FLOAT, "(M_PI*(%s))", expr); + + STR_free(expr); +} + + +static void push_complex(void) +{ + char *expr; + + expr = STR_copy(peek(-1, T_FLOAT)); + pop_stack(1); + + push(T_OBJECT, "PUSH_COMPLEX(%s)", expr); + + STR_free(expr); +} + + +static void push_event(bool unknown, int index) +{ + CLASS_DESC *desc; + const char *name; + + if (unknown) + { + name = JIT_class->load->unknown[index]; + // The ':' is already in the name, thanks to the compiler. + index = JIT_find_symbol(JIT_class, name); + if (index != NO_SYMBOL) + { + desc = JIT_class->table[index].desc; + if (CLASS_DESC_get_type(desc) == CD_EVENT) + index = desc->event.index; + else + index = NO_SYMBOL; + } + } + else if (JIT_class->parent) + index += JIT_class->parent->n_event; + + push_function(CALL_EVENT, index); +} + + +static void push_subr_varptr(ushort code) +{ + ushort op; + char var[16]; + int index; + TYPE type; + char *expr; + + check_stack(1); + + op = (ushort)atoi(get_expr(-1)); + pop_stack(1); + + if ((code & 0xFF) == 1) // IsMissing + { + push(T_BOOLEAN, "(o%d & %d)", op / 8, 1 << (op % 8)); + return; + } + + if ((op & 0xFF00) == C_PUSH_LOCAL || (op & 0xFF00) == C_PUSH_PARAM) + { + if ((op & 0xFF00) == C_PUSH_PARAM) + { + index = _func->n_param + (op & 0xFF); + type = _func->param[index].type; + sprintf(var, "p%d", index); + } + else + { + index = op & 0xFF; + type = get_local_type(_func, index); + sprintf(var, "l%d", index); + } + + switch(TYPEID(type)) + { + case T_BOOLEAN: + case T_BYTE: + case T_SHORT: + case T_INTEGER: + case T_LONG: + case T_SINGLE: + case T_FLOAT: + case T_POINTER: + expr = STR_print("&%s", var); + break; + + case T_DATE: + case T_OBJECT: + expr = STR_print("&%s.value", var); + break; + + case T_STRING: + case T_CSTRING: + expr = STR_print("(%s.value.addr + %s.value.start)", var, var); + break; + + default: + goto _ILLEGAL; + } + } + else if ((op & 0xF800) == C_PUSH_DYNAMIC) + { + expr = STR_print("(&OP[%d])", JIT_class->load->dyn[op & 0x7FF].pos); + } + else if ((op & 0xF800) == C_PUSH_STATIC) + { + expr = STR_print("%p", JIT_class->stat + JIT_class->load->stat[op & 0x7FF].pos); + } + else + goto _ILLEGAL; + + push(T_POINTER, "((intptr_t)%s)", expr); + + STR_free(expr); + return; + +_ILLEGAL: + + JIT_panic("unsupported VarPtr()"); +} + + +#define GET_XXX() (((signed short)(code << 4)) >> 4) +#define GET_UXX() (code & 0xFFF) +#define GET_7XX() (code & 0x7FF) +#define GET_XX() ((signed char)code) +#define GET_UX() ((unsigned char)code) +#define GET_3X() (code & 0x3F) +#define TEST_XX() (code & 1) +#define PC (&func->code[p]) + +bool JIT_translate_body(FUNCTION *func, int ind) +{ + static const void *jump_table[256] = + { + /* 00 NOP */ &&_MAIN, + /* 01 PUSH LOCAL */ &&_PUSH_LOCAL, + /* 02 PUSH PARAM */ &&_PUSH_PARAM, + /* 03 PUSH ARRAY */ &&_PUSH_ARRAY, + /* 04 PUSH UNKNOWN */ &&_PUSH_UNKNOWN, + /* 05 PUSH EXTERN */ &&_PUSH_EXTERN, + /* 06 BYREF */ &&_BYREF, + /* 07 PUSH EVENT */ &&_PUSH_EVENT, + /* 08 QUIT */ &&_QUIT, + /* 09 POP LOCAL */ &&_POP_LOCAL, + /* 0A POP PARAM */ &&_POP_PARAM, + /* 0B POP ARRAY */ &&_POP_ARRAY, + /* 0C POP UNKNOWN */ &&_POP_UNKNOWN, + /* 0D POP OPTIONAL */ &&_POP_OPTIONAL, + /* 0E POP CTRL */ &&_POP_CTRL, + /* 0F BREAK */ &&_BREAK, + /* 10 RETURN */ &&_RETURN, + /* 11 PUSH SHORT */ &&_PUSH_SHORT, + /* 12 PUSH INTEGER */ &&_PUSH_INTEGER, + /* 13 PUSH CHAR */ &&_PUSH_CHAR, + /* 14 PUSH MISC */ &&_PUSH_MISC, + /* 15 PUSH ME */ &&_PUSH_ME, + /* 16 TRY */ &&_TRY, + /* 17 END TRY */ &&_END_TRY, + /* 18 CATCH */ &&_CATCH, + /* 19 DUP */ &&_DUP, + /* 1A DROP */ &&_DROP, + /* 1B NEW */ &&_NEW, + /* 1C CALL */ &&_CALL, + /* 1D CALL QUICK */ &&_CALL_QUICK, + /* 1E CALL EASY */ &&_CALL_SLOW, + /* 1F ON */ &&_ON_GOTO_GOSUB, + /* 20 JUMP */ &&_JUMP, + /* 21 JUMP IF TRUE */ &&_JUMP_IF_TRUE, + /* 22 JUMP IF FALSE */ &&_JUMP_IF_FALSE, + /* 23 GOSUB */ &&_GOSUB, + /* 24 JUMP FIRST */ &&_JUMP_FIRST, + /* 25 JUMP NEXT */ &&_JUMP_NEXT, + /* 26 FIRST */ &&_ENUM_FIRST, + /* 27 NEXT */ &&_ENUM_NEXT, + /* 28 = */ &&_SUBR_COMPE, + /* 29 <> */ &&_SUBR_COMPN, + /* 2A > */ &&_SUBR_COMPGT, + /* 2B <= */ &&_SUBR_COMPLE, + /* 2C < */ &&_SUBR_COMPLT, + /* 2D >= */ &&_SUBR_COMPGE, + /* 2E == */ &&_SUBR, + /* 2F CASE */ &&_SUBR_CODE, + /* 30 + */ &&_SUBR_ADD, + /* 31 - */ &&_SUBR_SUB, + /* 32 * */ &&_SUBR_MUL, + /* 33 / */ &&_SUBR_DIV, + /* 34 NEG */ &&_SUBR_NEG, + /* 35 \ */ &&_SUBR_QUO, + /* 36 MOD */ &&_SUBR_REM, + /* 37 ^ */ &&_SUBR_CODE, + /* 38 AND */ &&_SUBR_AND, + /* 39 OR */ &&_SUBR_OR, + /* 3A XOR */ &&_SUBR_XOR, + /* 3B NOT */ &&_SUBR_NOT, + /* 3C & */ &&_SUBR_CODE, + /* 3D LIKE */ &&_SUBR_CODE, + /* 3E &/ */ &&_SUBR_CODE, + /* 3F Is */ &&_SUBR_CODE, + /* 40 Left$ */ &&_SUBR_CODE, + /* 41 Mid$ */ &&_SUBR_CODE, + /* 42 Right$ */ &&_SUBR_CODE, + /* 43 Len */ &&_SUBR_LEN, + /* 44 Space$ */ &&_SUBR, + /* 45 String$ */ &&_SUBR, + /* 46 Trim$ */ &&_SUBR_CODE, + /* 47 UCase$ */ &&_SUBR_CODE, + /* 48 Oct$ */ &&_SUBR_CODE, + /* 49 Chr$ */ &&_SUBR, + /* 4A Asc */ &&_SUBR_CODE, + /* 4B InStr */ &&_SUBR_CODE, + /* 4C RInStr */ &&_SUBR_CODE, + /* 4D Subst$ */ &&_SUBR_CODE, + /* 4E Replace$ */ &&_SUBR_CODE, + /* 4F Split */ &&_SUBR_CODE, + /* 50 Scan */ &&_SUBR, + /* 51 Comp */ &&_SUBR_CODE, + /* 52 Conv */ &&_SUBR, + /* 53 DConv */ &&_SUBR_CODE, + /* 54 Abs */ &&_SUBR_ABS, + /* 55 Int */ &&_SUBR_INT, + /* 56 Fix */ &&_SUBR_FIX, + /* 57 Sgn */ &&_SUBR_SGN, + /* 58 Frac... */ &&_SUBR_MATH, + /* 59 Pi */ &&_SUBR_PI, + /* 5A Round */ &&_SUBR_CODE, + /* 5B Randomize */ &&_SUBR_CODE, + /* 5C Rnd */ &&_SUBR_CODE, + /* 5D Min */ &&_SUBR_CODE, + /* 5E Max */ &&_SUBR_CODE, + /* 5F IIf */ &&_SUBR_CODE, + /* 60 Choose */ &&_SUBR_CODE, + /* 61 Array */ &&_SUBR_CODE, + /* 62 ATan2... */ &&_SUBR_CODE, + /* 63 IsAscii... */ &&_SUBR_CODE, + /* 64 BClr... */ &&_SUBR_BIT, + /* 65 IsBoolean... */ &&_SUBR_CODE, + /* 66 TypeOf */ &&_SUBR_CODE, + /* 67 CBool... */ &&_SUBR_CONV, + /* 68 Bin$ */ &&_SUBR_CODE, + /* 69 Hex$ */ &&_SUBR_CODE, + /* 6A Val */ &&_SUBR, + /* 6B Str */ &&_SUBR, + /* 6C Format */ &&_SUBR_CODE, + /* 6D Timer */ &&_SUBR, + /* 6E Now */ &&_SUBR, + /* 6F Year... */ &&_SUBR_CODE, + /* 70 Week */ &&_SUBR_CODE, + /* 71 Date */ &&_SUBR_CODE, + /* 72 Time... */ &&_SUBR_CODE, + /* 73 DateAdd... */ &&_SUBR_CODE, + /* 74 Eval */ &&_SUBR_CODE, + /* 75 Error */ &&_SUBR, + /* 76 Debug */ &&_SUBR_CODE, + /* 77 Wait */ &&_SUBR_CODE, + /* 78 Open */ &&_SUBR_CODE, + /* 79 Close */ &&_SUBR, + /* 7A Input */ &&_SUBR_CODE, + /* 7B LineInput */ &&_SUBR, + /* 7C Print */ &&_SUBR_CODE, + /* 7D Read */ &&_SUBR_CODE, + /* 7E Write */ &&_SUBR_CODE, + /* 7F Flush */ &&_SUBR, + /* 80 Lock... */ &&_SUBR_CODE, + /* 81 InputFrom... */ &&_SUBR_CODE, + /* 82 Eof */ &&_SUBR_CODE, + /* 83 Lof */ &&_SUBR_CODE, + /* 84 Seek */ &&_SUBR_CODE, + /* 85 Kill */ &&_SUBR_CODE, + /* 86 Mkdir */ &&_SUBR_CODE, + /* 87 Rmdir */ &&_SUBR_CODE, + /* 88 Move */ &&_SUBR_CODE, + /* 89 Copy */ &&_SUBR_CODE, + /* 8A Link */ &&_SUBR_ISNAN, + /* 8B Exist */ &&_SUBR_CODE, + /* 8C Access */ &&_SUBR_CODE, + /* 8D Stat */ &&_SUBR_CODE, + /* 8E Dfree */ &&_SUBR, + /* 8F Temp$ */ &&_SUBR_CODE, + /* 90 IsDir */ &&_SUBR, + /* 91 Dir */ &&_SUBR_CODE, + /* 92 RDir */ &&_SUBR_CODE, + /* 93 Exec... */ &&_SUBR_CODE, + /* 94 Alloc */ &&_SUBR_CODE, + /* 95 Free */ &&_SUBR, + /* 96 Realloc */ &&_SUBR_CODE, + /* 97 StrPtr */ &&_SUBR_CODE, + /* 98 Sleep... */ &&_SUBR_CODE, + /* 99 VarPtr */ &&_SUBR_VARPTR, + /* 9A Collection */ &&_SUBR_CODE, + /* 9B Tr$ */ &&_SUBR, + /* 9C Quote$... */ &&_SUBR_CODE, + /* 9D Unquote$... */ &&_SUBR_CODE, + /* 9E MkInt$... */ &&_SUBR_CODE, + /* 9F Byte@... */ &&_SUBR_CODE, + /* A0 ADD QUICK */ &&_ADD_QUICK, + /* A1 ADD QUICK */ &&_ADD_QUICK, + /* A2 ADD QUICK */ &&_ADD_QUICK, + /* A3 ADD QUICK */ &&_ADD_QUICK, + /* A4 ADD QUICK */ &&_ADD_QUICK, + /* A5 ADD QUICK */ &&_ADD_QUICK, + /* A6 ADD QUICK */ &&_ADD_QUICK, + /* A7 ADD QUICK */ &&_ADD_QUICK, + /* A8 ADD QUICK */ &&_ADD_QUICK, + /* A9 ADD QUICK */ &&_ADD_QUICK, + /* AA ADD QUICK */ &&_ADD_QUICK, + /* AB ADD QUICK */ &&_ADD_QUICK, + /* AC ADD QUICK */ &&_ADD_QUICK, + /* AD ADD QUICK */ &&_ADD_QUICK, + /* AE ADD QUICK */ &&_ADD_QUICK, + /* AF ADD QUICK */ &&_ADD_QUICK, + /* B0 PUSH CLASS */ &&_PUSH_CLASS, + /* B1 PUSH CLASS */ &&_PUSH_CLASS, + /* B2 PUSH CLASS */ &&_PUSH_CLASS, + /* B3 PUSH CLASS */ &&_PUSH_CLASS, + /* B4 PUSH CLASS */ &&_PUSH_CLASS, + /* B5 PUSH CLASS */ &&_PUSH_CLASS, + /* B6 PUSH CLASS */ &&_PUSH_CLASS, + /* B7 PUSH CLASS */ &&_PUSH_CLASS, + /* B8 PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* B9 PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BA PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BB PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BC PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BD PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BE PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* BF PUSH FUNCTION */ &&_PUSH_FUNCTION, + /* C0 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C1 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C2 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C3 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C4 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C5 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C6 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C7 PUSH DYNAMIC */ &&_PUSH_DYNAMIC, + /* C8 PUSH STATIC */ &&_PUSH_STATIC, + /* C9 PUSH STATIC */ &&_PUSH_STATIC, + /* CA PUSH STATIC */ &&_PUSH_STATIC, + /* CB PUSH STATIC */ &&_PUSH_STATIC, + /* CC PUSH STATIC */ &&_PUSH_STATIC, + /* CD PUSH STATIC */ &&_PUSH_STATIC, + /* CE PUSH STATIC */ &&_PUSH_STATIC, + /* CF PUSH STATIC */ &&_PUSH_STATIC, + /* D0 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D1 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D2 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D3 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D4 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D5 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D6 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D7 POP DYNAMIC */ &&_POP_DYNAMIC, + /* D8 POP STATIC */ &&_POP_STATIC, + /* D9 POP STATIC */ &&_POP_STATIC, + /* DA POP STATIC */ &&_POP_STATIC, + /* DB POP STATIC */ &&_POP_STATIC, + /* DC POP STATIC */ &&_POP_STATIC, + /* DD POP STATIC */ &&_POP_STATIC, + /* DE POP STATIC */ &&_POP_STATIC, + /* DF POP STATIC */ &&_POP_STATIC, + /* E0 PUSH CONST */ &&_PUSH_CONST, + /* E1 PUSH CONST */ &&_PUSH_CONST, + /* E2 PUSH CONST */ &&_PUSH_CONST, + /* E3 PUSH CONST */ &&_PUSH_CONST, + /* E4 PUSH CONST */ &&_PUSH_CONST, + /* E5 PUSH CONST */ &&_PUSH_CONST, + /* E6 PUSH CONST */ &&_PUSH_CONST, + /* E7 PUSH CONST */ &&_PUSH_CONST, + /* E8 PUSH CONST */ &&_PUSH_CONST, + /* E9 PUSH CONST */ &&_PUSH_CONST, + /* EA PUSH CONST */ &&_PUSH_CONST, + /* EB PUSH CONST */ &&_PUSH_CONST, + /* EC PUSH CONST */ &&_PUSH_CONST, + /* ED PUSH CONST */ &&_PUSH_CONST, + /* EE PUSH CONST */ &&_PUSH_CONST, + /* EF PUSH CONST */ &&_PUSH_CONST_EX, + /* F0 PUSH QUICK */ &&_PUSH_QUICK, + /* F1 PUSH QUICK */ &&_PUSH_QUICK, + /* F2 PUSH QUICK */ &&_PUSH_QUICK, + /* F3 PUSH QUICK */ &&_PUSH_QUICK, + /* F4 PUSH QUICK */ &&_PUSH_QUICK, + /* F5 PUSH QUICK */ &&_PUSH_QUICK, + /* F6 PUSH QUICK */ &&_PUSH_QUICK, + /* F7 PUSH QUICK */ &&_PUSH_QUICK, + /* F8 PUSH QUICK */ &&_PUSH_QUICK, + /* F9 PUSH QUICK */ &&_PUSH_QUICK, + /* FA PUSH QUICK */ &&_PUSH_QUICK, + /* FB PUSH QUICK */ &&_PUSH_QUICK, + /* FC PUSH QUICK */ &&_PUSH_QUICK, + /* FD PUSH QUICK */ &&_PUSH_QUICK, + /* FE PUSH QUICK */ &&_PUSH_QUICK, + /* FF PUSH QUICK */ &&_PUSH_QUICK + }; + + CLASS *class = JIT_class; + TYPE type; + CTYPE ctype; + uint p = 0; + ushort code; + int index; + int size = JIT_get_code_size(func); + void *addr; + int pos; + int i; + + enter_function(func, ind); + + //JIT_print(" JIT.debug(\"SP = %%p\\n\", SP);\n"); + + goto _MAIN; + +_MAIN: + + //fprintf(stderr, "[%d] %d\n", p, _stack_current); + + if (_has_finally && p == func->error) + print_catch(); + + if (!JIT_last_print_is_label) + print_label(func, p); + + if (p >= (size - 1)) // ignore the last opcode which is RETURN + return leave_function(func, ind); + + _pc = p; + code = func->code[p++]; + //fprintf(stderr, "--------—-------------------- %d / %04X\n", _stack_current, code); + goto *jump_table[code >> 8]; + +_DUP: + + check_stack(1); + + _has_just_dup = TRUE; + + index = GB.Count(_dup_type); + *(TYPE *)GB.Add(&_dup_type) = type = get_type(-1); + + //JIT_print_decl(" %s d%d;\n", JIT_get_ctype(type), index); + JIT_declare(type, "d%d", index); + //_no_release = TRUE; + pop(type, "d%d", index); + //_no_release = FALSE; + push(type, "d%d", index); + push(type, "d%d", index); + + goto _MAIN; + +_PUSH_LOCAL: + + index = GET_XX(); + type = get_local_type(func, index); + + if (index >= func->n_local) + { + index = _ctrl_index[index - func->n_local]; + CTRL_INFO *info = &_ctrl_info[index]; + + if (info->expr) + push(type, info->expr); + else + push(type, "c%d", index); + } + else + push(type, "l%d", index); + + goto _MAIN; + +_POP_LOCAL: + + index = GET_XX(); + type = get_local_type(func, index); + + if (index >= func->n_local) + { + pop(type, "c%d", _ctrl_index[index - func->n_local]); + } + else + { + if (check_swap(type, "l%d = %%s", index)) + pop(type, "l%d", index); + } + + goto _MAIN; + +_POP_CTRL: + + pop_ctrl(GET_XX() - func->n_local, T_VOID); + goto _MAIN; + +_PUSH_PARAM: + + index = func->n_param + GET_XX(); + push(func->param[index].type, "p%d", index); + goto _MAIN; + +_POP_PARAM: + + index = func->n_param + GET_XX(); + type = func->param[index].type; + + if (check_swap(type, "p%d = %%s", index)) + pop(type, "p%d", index); + + goto _MAIN; + +_PUSH_QUICK: + + push(T_INTEGER, "%d", GET_XXX()); + goto _MAIN; + +_PUSH_SHORT: + + push(T_INTEGER, "%d", (short)PC[0]); + p++; + goto _MAIN; + +_PUSH_INTEGER: + + push(T_INTEGER, "%d", PC[0] | ((uint)PC[1] << 16)); + p += 2; + goto _MAIN; + +_PUSH_STATIC: + + index = GET_7XX(); + ctype = class->load->stat[index].type; + type = JIT_ctype_to_type(class, ctype); + addr = &class->stat[class->load->stat[index].pos]; + + switch(ctype.id) + { + case TC_STRUCT: + push(type, "GET_S(CP, %p, CLASS(%p))", addr, (CLASS *)type); + break; + + case TC_ARRAY: + declare(&_decl_ra, "void *ra = NULL"); + push(type, "GET_A(CP, CP, %p, CLASS(%p), %p)", addr, (CLASS *)type, JIT_class->load->array[ctype.value]); + break; + + case T_OBJECT: + if (TYPE_is_pure_object(type)) + push(type, "GET_o(%p, CLASS(%p))", addr, (CLASS *)type); + else + push(type, "GET_o(%p, GB_T_OBJECT)", addr); + break; + + default: + push(type, "GET_%s(%p)", JIT_get_type(type), addr); + } + + goto _MAIN; + +_POP_STATIC: + + index = GET_7XX(); + type = JIT_ctype_to_type(class, class->load->stat[index].type); + addr = &class->stat[class->load->stat[index].pos]; + + _no_release = TRUE; + if (check_swap(type, "SET_%s(%p, %%s)", JIT_get_type(type), addr)) + pop(type, "SET_%s(%p, %%s)", JIT_get_type(type), addr); + _no_release = FALSE; + + goto _MAIN; + +_PUSH_DYNAMIC: + + index = GET_7XX(); + pos = class->load->dyn[index].pos; + ctype = class->load->dyn[index].type; + type = JIT_ctype_to_type(class, ctype); + + switch(ctype.id) + { + case TC_STRUCT: + push(type, "GET_S(OP, &OP[%d], CLASS(%p))", pos, (CLASS *)type); + break; + + case TC_ARRAY: + push(type, "GET_A(CP, OP, &OP[%d], CLASS(%p), %p)", pos, (CLASS *)type, JIT_class->load->array[ctype.value]); + break; + + case T_OBJECT: + if (TYPE_is_pure_object(type)) + push(type, "GET_o(&OP[%d], CLASS(%p))", pos, (CLASS *)type); + else + push(type, "GET_o(&OP[%d], GB_T_OBJECT)", pos); + break; + + default: + push(type, "GET_%s(&OP[%d])", JIT_get_type(type), pos); + } + + goto _MAIN; + +_POP_DYNAMIC: + + index = GET_7XX(); + type = JIT_ctype_to_type(class, class->load->dyn[index].type); + pos = class->load->dyn[index].pos; + + _no_release = TRUE; + if (check_swap(type, "SET_%s(&OP[%d], %%s)", JIT_get_type(type), pos)) + pop(type, "SET_%s(&OP[%d], %%s)", JIT_get_type(type), pos); + _no_release = FALSE; + + goto _MAIN; + +_PUSH_MISC: + + switch (GET_UX()) + { + case 0: + push(T_NULL, "NULL"); + break; + + case 1: + push(T_VOID, NULL); + break; + + case 2: + push(T_BOOLEAN, "0"); + break; + + case 3: + push(T_BOOLEAN, "(-1)"); + break; + + case 4: + push(T_OBJECT, "GET_LAST()"); + break; + + case 5: + push(T_CSTRING, "GET_CSTRING(\"\", 0, 0)"); + break; + + case 6: + push(T_FLOAT, "INFINITY"); + break; + + case 7: + push(T_FLOAT, "-INFINITY"); + break; + + case 8: + push_complex(); + break; + + /*case 9: + EXEC_push_vargs(); + break; + + case 10: + EXEC_drop_vargs(); + break;*/ + + default: + goto _ILLEGAL; + } + goto _MAIN; + +_PUSH_CHAR: + + push(T_CSTRING, "GET_CHAR(%d)", GET_UX()); + goto _MAIN; + +_POP_OPTIONAL: + + check_stack(1); + if (get_type(-1) == T_VOID) + pop_stack(1); + else + { + index = func->n_param + GET_XX() - func->npmin; + JIT_print(" if (o%d & %d)\n ", index / 8, (1 << (index % 8))); + index = func->n_param + GET_XX(); + type = func->param[index].type; + _no_release = TRUE; + _no_release_but_borrow = TRUE; + pop(type, "p%d = %%s", index); + _no_release_but_borrow = FALSE; + _no_release = FALSE; + } + goto _MAIN; + +_PUSH_CLASS: + + index = GET_7XX(); + type = (TYPE)class->load->class_ref[index]; + push(T_CLASS, "CLASS(%p)", (CLASS *)type); + goto _MAIN; + +_PUSH_FUNCTION: + + push_function(CALL_PRIVATE, GET_7XX()); + goto _MAIN; + +_PUSH_ME: + + index = GET_UX(); + if (index & 2) + push((TYPE)(JIT_class->parent), "GET_SUPER(%p)", JIT_class->parent); + else + push((TYPE)JIT_class, "GET_ME(%p)", JIT_class); + + goto _MAIN; + + /*if (GET_UX() & 2) + { + // The used class must be in the stack, because it is tested by exec_push && exec_pop + if (LIKELY(OP != NULL)) + { + SP->_object.class = SP->_object.class->parent; + SP->_object.super = EXEC_super; + } + else + { + SP->_class.class = SP->_class.class->parent; + SP->_class.super = EXEC_super; + } + + EXEC_super = SP; + + //fprintf(stderr, "%s\n", DEBUG_get_current_position()); + //BREAKPOINT(); + } + + PUSH(); + goto _NEXT;*/ + +_PUSH_UNKNOWN: + + push_unknown(PC[0]); + p++; + goto _MAIN; + +_POP_UNKNOWN: + + pop_unknown(PC[0]); + p++; + goto _MAIN; + +_CALL: + + push_call(code); + goto _MAIN; + +_SUBR: + + push_subr(CALL_SUBR, code); + goto _MAIN; + +_SUBR_CODE: + + push_subr(CALL_SUBR_CODE, code); + goto _MAIN; + +_DROP: + + pop(T_VOID, NULL); + goto _MAIN; + +_NEW: + + push_subr(CALL_NEW, code); + goto _MAIN; + +_RETURN: + + switch(code & 0xFF) + { + case 0: + if (_try_finished) + JIT_print(" RETURN();\n"); + else + JIT_print(" RETURN_LEAVE_TRY();\n"); + break; + + case 1: + pop(func->type, "r"); + if (!_try_finished) JIT_print(" LEAVE_TRY();\n"); + JIT_print(" goto __RETURN;\n"); + break; + + case 2: + if (!_try_finished) JIT_print(" LEAVE_TRY();\n"); + JIT_print(" goto __RETURN;\n"); + break; + + default: + goto _ILLEGAL; + } + goto _MAIN; + +_GOSUB: + + JIT_print(" PUSH_GOSUB(__L%d); goto __L%d;\n", p + 1, p + (signed short)PC[0] + 1); + JIT_print("__L%d:;\n", p + 1); + p++; + goto _MAIN; + +_JUMP: + + JIT_print(" goto __L%d;\n", p + (signed short)PC[0] + 1); + p++; + goto _MAIN; + +_JUMP_IF_TRUE: + + JIT_print(" if (%s) goto __L%d;\n", peek(-1, T_BOOLEAN), p + (signed short)PC[0] + 1); + pop_stack(1); + p++; + goto _MAIN; + +_JUMP_IF_FALSE: + + JIT_print(" if (!(%s)) goto __L%d;\n", peek(-1, T_BOOLEAN), p + (signed short)PC[0] + 1); + pop_stack(1); + p++; + goto _MAIN; + +_JUMP_FIRST: + + index = PC[2] & 0xFF; + type = get_local_type(func, index); + if (strcmp(get_expr(-1), "1") == 0) + _loop_type = LOOP_UP; + else if (strcmp(get_expr(-1), "-1") == 0) + _loop_type = LOOP_DOWN; + else + _loop_type = LOOP_UNKNOWN; + + pop(type, "const %s i%d", JIT_get_ctype(type), _loop_count); + pop(type, "const %s e%d", JIT_get_ctype(type), _loop_count); + + JIT_print(" goto __L%ds;\n", p + 1); + + goto _MAIN; + +_JUMP_NEXT: + + index = PC[1] & 0xFF; + JIT_print(" l%d += i%d;\n", index, _loop_count); + JIT_print("__L%ds:\n", p); + + switch(_loop_type) + { + case LOOP_UP: + JIT_print(" if (l%d > e%d) goto __L%d;\n", index, _loop_count, p + (signed short)PC[0] + 1); + break; + + case LOOP_DOWN: + JIT_print(" if (l%d < e%d) goto __L%d;\n", index, _loop_count, p + (signed short)PC[0] + 1); + break; + + case LOOP_UNKNOWN: + JIT_print(" if (((i%d > 0) && (l%d > e%d)) || ((i%d < 0) && (l%d < e%d))) goto __L%d;\n", _loop_count, index, _loop_count, _loop_count, index, _loop_count, p + (signed short)PC[0] + 1); + } + + _loop_count++; + + p +=2; + goto _MAIN; + +_ENUM_FIRST: + + index = GET_XX() - func->n_local; + pop_ctrl(index, T_OBJECT); + add_ctrl(index + 1, T_OBJECT, NULL); + JIT_print(" ENUM_FIRST(0x%04X, c%d, c%d);\n", code, _ctrl_index[index], _ctrl_index[index + 1]); + goto _MAIN; + +_ENUM_NEXT: + + index = (PC[-2] & 0xFF) - func->n_local; + + JIT_print(" ENUM_NEXT(0x%04X, c%d, c%d, __L%d);\n", code, _ctrl_index[index], _ctrl_index[index + 1], p + (signed short)PC[0] + 1); + if ((code & 1) == 0) + push(T_UNKNOWN, "POP_u()"); + + p++; + goto _MAIN; + +_PUSH_CONST: + + index = GET_UXX(); + push_constant(class, index); + goto _MAIN; + +_PUSH_CONST_EX: + + push_constant(class, PC[0]); + p++; + goto _MAIN; + +_PUSH_ARRAY: + + push_array(code); + goto _MAIN; + +_POP_ARRAY: + + pop_array(code); + goto _MAIN; + +_ADD_QUICK: + + index = GET_XXX(); + push(T_INTEGER, "%d", abs(index)); + if (index < 0) + { + PC[-1] = code = 0x3100; + goto _SUBR_SUB; + } + else + PC[-1] = code = 0x3000; + +_SUBR_ADD: + + push_subr_add(code, "+", "|", TRUE); + goto _MAIN; + +_SUBR_SUB: + + push_subr_add(code, "-", "^", TRUE); + goto _MAIN; + +_SUBR_MUL: + + push_subr_add(code, "*", "&", FALSE); + goto _MAIN; + +_SUBR_DIV: + + push_subr_div(code); + goto _MAIN; + +_SUBR_NEG: + + push_subr_arithmetic(MATH_NEG, code); + goto _MAIN; + +_SUBR_QUO: + + push_subr_quo(code, "/"); + goto _MAIN; + +_SUBR_REM: + + push_subr_quo(code, "%"); + goto _MAIN; + +_SUBR_AND: + + push_subr_and(code, "&"); + goto _MAIN; + +_SUBR_OR: + + push_subr_and(code, "|"); + goto _MAIN; + +_SUBR_XOR: + + push_subr_and(code, "^"); + goto _MAIN; + +_SUBR_NOT: + + push_subr_not(code); + goto _MAIN; + +_SUBR_ABS: + + push_subr_arithmetic(MATH_ABS, code); + goto _MAIN; + +_SUBR_SGN: + + push_subr_arithmetic(MATH_SGN, code); + goto _MAIN; + +_SUBR_INT: + + push_subr_float_arithmetic(MATH_INT, code); + goto _MAIN; + +_SUBR_FIX: + + push_subr_float_arithmetic(MATH_FIX, code); + goto _MAIN; + +_SUBR_COMPE: +_SUBR_COMPN: +_SUBR_COMPGT: +_SUBR_COMPLE: +_SUBR_COMPLT: +_SUBR_COMPGE: + + push_subr_comp(code); + goto _MAIN; + +_SUBR_ISNAN: + + push_subr_isnan(code); + goto _MAIN; + +_SUBR_CONV: + + push_subr_conv(code); + goto _MAIN; + +_SUBR_LEN: + + push_subr_len(code); + goto _MAIN; + +_SUBR_MATH: + + push_subr_math(code); + goto _MAIN; + +_SUBR_PI: + + push_subr_pi(code); + goto _MAIN; + +_SUBR_BIT: + + push_subr_bit(code); + goto _MAIN; + +_SUBR_VARPTR: + + push_subr_varptr(code); + goto _MAIN; + +_BREAK: + + goto _MAIN; + +_QUIT: + + JIT_print(" "); + + if ((code & 3) == 3) + { + check_stack(1); + JIT_print("%s,", push_expr(-1, T_BYTE)); + pop_stack(1); + } + + JIT_print(" QUIT(0x%04X);\n", code); + goto _MAIN; + +_TRY: + + declare(&_decl_tp, "VALUE *tp"); + JIT_print(" tp = EP; EP = sp;\n"); + JIT_print(" { VALUE *volatile sp = EP; TRY {\n"); + p++; + goto _MAIN; + +_END_TRY: + + JIT_print(" *JIT.got_error = 0;\n"); + JIT_print(" } CATCH {\n"); + JIT_print(" if (SP > sp) sp = SP; else SP = sp;\n"); + JIT_print(" LEAVE_SUPER();\n"); + //JIT_print(" if (sp > EP) { fprintf(stderr, \"release try %%d\\n\", sp - EP); JIT.release_many(sp, sp - EP); SP = sp = EP; }\n"); + JIT_print(" if (sp > EP) { JIT.release_many(sp, sp - EP); SP = sp = EP; }\n"); + JIT_print(" *JIT.got_error = 1;\n"); + JIT_print(" JIT.error_set_last(FALSE);\n"); + JIT_print(" } END_TRY }\n"); + JIT_print(" EP = tp;\n"); + goto _MAIN; + +_CATCH: + + _has_catch = TRUE; + if (!_has_finally) + print_catch(); + + JIT_print(" if (!error) goto __RELEASE;\n"); + + goto _MAIN; + +_ON_GOTO_GOSUB: + + index = GET_XX(); + + JIT_print(" {\n"); + JIT_print(" static void *jump[] = { "); + + for (i = 0; i < index; i++) + { + if (i) JIT_print(", "); + JIT_print("&&__L%d", PC[i] + p + i); + } + + JIT_print(" };\n"); + + pop(T_INTEGER, " int n"); + + p += index + 2; + + JIT_print(" if (n >= 0 && n < %d)\n", index); + + if ((PC[index + 1] & 0xFF00) == C_GOSUB) + { + JIT_print(" {\n"); + JIT_print(" PUSH_GOSUB(__L%d);\n", p); + } + + JIT_print(" goto *jump[n];\n"); + + if ((PC[index + 1] & 0xFF00) == C_GOSUB) + JIT_print(" }\n"); + + JIT_print(" }\n"); + + JIT_print("__L%d:;\n", p); + goto _MAIN; + +_PUSH_EVENT: + + index = GET_UX(); + if (index == 0xFF) + { + push_event(TRUE, PC[0]); + p++; + } + else + push_event(FALSE, index); + + goto _MAIN; + +_PUSH_EXTERN: + + index = GET_UX(); + push_function(CALL_EXTERN, index); + goto _MAIN; + +_BYREF: +_CALL_QUICK: +_CALL_SLOW: +_ILLEGAL: + + JIT_panic("unsupported opcode %04X", code); +} + diff --git a/main/lib/jit/main.c b/main/lib/jit/main.c new file mode 100644 index 00000000..98e80336 --- /dev/null +++ b/main/lib/jit/main.c @@ -0,0 +1,66 @@ +/*************************************************************************** + + main.c + + (c) 2000-2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "jit.h" +#include "main.h" + +GB_INTERFACE *GB_PTR EXPORT; +JIT_INTERFACE *JIT_PTR EXPORT; + + +BEGIN_METHOD(Jit_Translate, GB_STRING klass; GB_STRING from) + + GB.ReturnString(JIT_translate(GB.ToZeroString(ARG(klass)), GB.ToZeroString(ARG(from)))); + +END_METHOD + + +GB_DESC JitDesc[] = +{ + GB_DECLARE_STATIC("__Jit"), + + GB_STATIC_METHOD("Translate", "s", Jit_Translate, "(Class)s(From)s"), + + GB_END_DECLARE +}; + + +GB_DESC *GB_CLASSES [] EXPORT = +{ + JitDesc, + NULL +}; + + +int EXPORT GB_INIT(void) +{ + return 0; +} + + +void EXPORT GB_EXIT() +{ +} + diff --git a/main/lib/jit/main.h b/main/lib/jit/main.h new file mode 100644 index 00000000..d0fe3c7d --- /dev/null +++ b/main/lib/jit/main.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + main.h + + (c) 2000-2018 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gb_common.h" +#include "gambas.h" +#include "gb.jit.h" + +#ifndef __MAIN_C +extern GB_INTERFACE *GB_PTR; +extern JIT_INTERFACE *JIT_PTR; +#endif + +#define GB (*GB_PTR) +#define JIT (*JIT_PTR) + +#endif /* __MAIN_H */ diff --git a/main/lib/option/Makefile.am b/main/lib/option/Makefile.am new file mode 100755 index 00000000..ffd21b28 --- /dev/null +++ b/main/lib/option/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.option +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.option.la + +gb_option_la_LIBADD = +gb_option_la_LDFLAGS = -module @LD_FLAGS@ +gb_option_la_CFLAGS = -I$(top_srcdir)/share @INCLTDL@ $(AM_CFLAGS) + +gb_option_la_SOURCES = \ + main.h main.c \ + getoptions.h getoptions.c + + diff --git a/main/lib/option/gb.option.component b/main/lib/option/gb.option.component new file mode 100755 index 00000000..bbb8616f --- /dev/null +++ b/main/lib/option/gb.option.component @@ -0,0 +1,4 @@ +[Component] +Key=gb.option +Author=Chintan Rao H +State=Deprecated diff --git a/main/lib/option/getoptions.c b/main/lib/option/getoptions.c new file mode 100755 index 00000000..cf189b8f --- /dev/null +++ b/main/lib/option/getoptions.c @@ -0,0 +1,396 @@ +/*************************************************************************** + + getoptions.c + + (c) 2000-2009 Chintan Rao + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GETOPTIONS_C + +#include "gb_common.h" +#include +#include +#include "getoptions.h" +#include "main.h" +#include + + +BEGIN_METHOD_VOID (getopt_calc) +{ + char **tmp,c,charec[2]={0}; + int i; + + THIS->index=0; + if(THIS->opt_arg!=NULL) + { + + for (i = 0; i < GB.Count(THIS->opt_arg); i++) + { + GB.FreeString(&(THIS->opt_arg[i])); + } + GB.FreeArray((void *) &(THIS->opt_arg)); + } + + if(THIS->opt_found!=NULL) + { + for (i = 0; i < GB.Count(THIS->opt_found); i++) + { + GB.FreeString(&(THIS->opt_found[i])); + } + GB.FreeArray((void *) &(THIS->opt_found)); + } + opterr=0; + optind=0; + GB.NewArray((void *) &(THIS->opt_found), sizeof(*(THIS->opt_found)), 0); + GB.NewArray((void *) &(THIS->opt_arg), sizeof(*(THIS->opt_arg)), 0); + GB.NewArray((void *) &(THIS->invalid), sizeof(*(THIS->invalid)), 0); + + + + while(1) + { + c=getopt(THIS->arg_count,THIS->argv,THIS->options); + if(c==-1) + { + break; + } + + + charec[0]=c; + tmp=(char **) GB.Add((void *) &(THIS->opt_found)); + *tmp = GB.NewZeroString(charec); + + charec[0]=optopt; + tmp=(char **) GB.Add((void *) &(THIS->invalid)); + if(optopt!=0) + { + *tmp = GB.NewZeroString(charec); + } + else + { + *tmp = GB.NewString("", 0); + } + + tmp=(char **) GB.Add((void *) &(THIS->opt_arg)); + if(optarg!=NULL) + { + *tmp = GB.NewZeroString(optarg); + } + else + { + *tmp = GB.NewString("", 0); + } + } + if(THIS->rest!=NULL) + { + GB.Unref((void *)&THIS->rest); + } + + GB.Array.New((void *) &(THIS->rest) , GB_T_STRING, THIS->arg_count-optind); + GB.Ref(THIS->rest); + for(i=optind;iarg_count;i++) + { + tmp=(char **)GB.Array.Get(THIS->rest,i-optind); + *tmp = GB.NewZeroString(THIS->argv[i]); + } +} +END_METHOD + +BEGIN_METHOD_VOID ( COPTIONS_next ) +{ + char *str; + + if(THIS->index >= GB.Count(THIS->opt_found) ) + { + GB.ReturnNewZeroString (""); + return; + } + str=THIS->opt_found [ THIS->index ]; + GB.ReturnNewZeroString (str); + THIS->index++; +} +END_METHOD + +BEGIN_METHOD_VOID ( COPTIONS_invalid ) +{ + char *str; + + if(THIS->index > GB.Count(THIS->invalid) || THIS->index<=0) + { + GB.ReturnNewZeroString (""); + return; + } + printf("%d\n",THIS->index); + str=THIS->invalid [ THIS->index-1 ]; + GB.ReturnNewZeroString (str); +} +END_METHOD + +BEGIN_METHOD_VOID (COPTIONS_opt_arg) +{ + char *str; + + if(THIS->index > GB.Count(THIS->opt_arg) || THIS->index<=0) + { + GB.ReturnNewZeroString (""); + return; + } + + str=THIS->opt_arg [ THIS->index - 1 ]; + GB.ReturnNewZeroString (str); +} +END_METHOD + +BEGIN_METHOD_VOID(COPTIONS_rest) +{ + GB.ReturnObject(THIS->rest); +} +END_METHOD + +BEGIN_METHOD ( COPTIONS_get, GB_STRING option ) + + int i; + char *str=NULL; + for ( i=0; iopt_found); i++ ) + { + if( THIS->opt_found[i][0]==STRING(option)[0] ) + { + if(THIS->opt_arg[i]!=NULL && STRING(option)[0]!='?') + { + str=THIS->opt_arg[i]; + } + else + { + GB.ReturnInteger(TRUE); + goto _RETURN; + } + } + } + if(str!=NULL) + { + GB.ReturnNewZeroString(str); + goto _RETURN; + } + + + /* should i chUck this out? + for ( i=0; iinvalid); i++ ) + { + if( THIS->invalid[i]!=NULL ) + { + if( THIS->invalid[i][0]==STRING(option)[0] ) + { + GB.ReturnInteger(TRUE); + return; + } + } + } + */ + + GB.ReturnInteger(FALSE); + +_RETURN: + + GB.ReturnConvVariant(); + +END_METHOD + +BEGIN_METHOD ( COPTIONS_getarg ,GB_STRING option) +{ + int i; + char **tmp; + if(THIS->return_temp!=NULL) + { + GB.Unref(&THIS->return_temp); + } + + GB.Array.New(&THIS->return_temp,GB_T_STRING,0); + for(i=0; iopt_found ) ; i++ ) + { + if( THIS->opt_found[i][0] == STRING(option)[0] ) + { + if(THIS->opt_arg[i]!=NULL ) + { + tmp=(char **)GB.Array.Add(THIS->return_temp); + *tmp = GB.NewZeroString(THIS->opt_arg[i]); + } + } + } + GB.Ref(THIS->return_temp); + GB.ReturnObject(THIS->return_temp); +} +END_METHOD + +BEGIN_METHOD_VOID ( COPTION_getallopt) +{ + int i; + char **tmp; + char charec[2]={0}; + if ( THIS->return_temp!=NULL ) + { + GB.Unref(&THIS->return_temp); + } + + GB.Array.New(&THIS->return_temp,GB_T_STRING,0); + for(i=0; iopt_found ) ; i++ ) + { + if( THIS->opt_found[i][0] != '?' && THIS->opt_found[i][0] != ':') + { + if(THIS->opt_found[i]!=NULL ) + { + tmp=(char **)GB.Array.Add(THIS->return_temp); + charec[0]=THIS->opt_found[i][0]; + *tmp = GB.NewZeroString(charec); + } + } + } + GB.Ref(THIS->return_temp); + GB.ReturnObject ( THIS->return_temp ); +} +END_METHOD + +BEGIN_PROPERTY(COPTIONS_cmdline) + + GB.ReturnObject(THIS->cmdline); + +END_PROPERTY + +BEGIN_PROPERTY(COPTIONS_options) + + THIS->options = GB.NewString(PSTRING(), PLENGTH()); + +END_PROPERTY + +BEGIN_METHOD ( COPTIONS_new, GB_STRING options; GB_OBJECT array ) +{ + char **tmp,*src; + int i,narg; + + THIS->options = GB.NewString(STRING(options), LENGTH(options)); + + GB.NewArray((void *) &(THIS->argv), sizeof(*(THIS->argv)), 0); // argv is where i keep track of what to free later + GB.Array.New((void *) &(THIS->cmdline) ,GB_T_STRING,arg_count); + GB.Ref(THIS->cmdline); + for(i=0;icmdline),i); + *tmp = GB.NewZeroString(cmd_arg[i]); + } + + if ( MISSING ( array ) ) + { + for ( i=0; iargv)); + *tmp = GB.NewZeroString(cmd_arg[i]); + } + THIS->arg_count=arg_count; + } + else + { + if(VARG(array)!=NULL) + { + narg = GB.Array.Count ( VARG ( array ) ); + for(i=0;iargv)); + *tmp = GB.NewZeroString(src); + } + THIS->arg_count=narg; + } + } + + /* start getting the options and store it*/ + + THIS->opt_arg=NULL; + THIS->opt_found=NULL; + THIS->return_temp=NULL; + THIS->index=0; + CALL_METHOD_VOID(getopt_calc); + + RETURN_SELF(); + return; +} +END_METHOD + +BEGIN_METHOD_VOID ( COPTIONS_free ) +{ + int i = 0; + + GB.FreeString(&(THIS->options)); + + for (i = 0; i < GB.Count(THIS->argv); i++) + { + GB.FreeString(&(THIS->argv[i])); + } + GB.FreeArray((void *) &(THIS->argv)); + + if(THIS->rest!=NULL) + { + GB.Unref((void **)&THIS->rest); + } + + for (i = 0; i < GB.Count(THIS->opt_arg); i++) + { + GB.FreeString(&(THIS->opt_arg[i])); + } + GB.FreeArray((void *) &(THIS->opt_arg)); + + for (i = 0; i < GB.Count(THIS->opt_found); i++) + { + GB.FreeString(&(THIS->opt_found[i])); + } + GB.FreeArray((void *) &(THIS->opt_found)); + + for (i = 0; i < GB.Count(THIS->invalid); i++) + { + GB.FreeString(&(THIS->invalid[i])); + } + + if(THIS->return_temp!=NULL) + { + GB.Unref(&THIS->return_temp); + } + + GB.FreeArray((void *) &(THIS->invalid)); + GB.Unref((void *)&THIS->cmdline); +} +END_METHOD + +GB_DESC GetOptionsDesc[] = +{ + GB_DECLARE("GetOptions", sizeof(COPTIONS)), + + GB_METHOD ( "_new", NULL, COPTIONS_new, "(Options)s[(Argument)String[];]" ), + GB_METHOD ( "_free", NULL, COPTIONS_free, NULL ), + GB_METHOD ( "_get" ,"v",COPTIONS_get,"(Option)s" ), + GB_METHOD ( "NextValidOption","s",COPTIONS_next,NULL ), + GB_METHOD ( "OptionArgument","s",COPTIONS_opt_arg,NULL ), + GB_METHOD ( "RestOfArgs","String[]",COPTIONS_rest,NULL ), + GB_METHOD ( "GetErrorOption","s",COPTIONS_invalid,NULL ), + GB_METHOD ( "GetArgument","String[]",COPTIONS_getarg,"(Option)s"), + GB_METHOD ( "GetAllOptions","String[]",COPTION_getallopt,NULL), + + //GB_PROPERTY ( "Argument", "String[]", COPTIONS_arg), + //GB_STATIC_PROPERTY ( "Options", "s",COPTIONS_options), + GB_PROPERTY_READ ("CommandLineArgs","String[]",COPTIONS_cmdline), + + GB_END_DECLARE +}; diff --git a/main/lib/option/getoptions.h b/main/lib/option/getoptions.h new file mode 100755 index 00000000..358764ed --- /dev/null +++ b/main/lib/option/getoptions.h @@ -0,0 +1,56 @@ +/*************************************************************************** + + getoptions.h + + (c) 2000-2009 Chintan Rao + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GETOPTIONS_H +#define __GETOPTIONS_H + +#include "gambas.h" + + +#ifndef __GETOPTIONS_C + +extern GB_DESC GetOptionsDesc[]; + +#else + +typedef +struct { + GB_BASE ob; + char *options; + char **argv; + int arg_count; + char **opt_found; + char **opt_arg; + char **invalid; + GB_ARRAY rest; + GB_ARRAY cmdline; + GB_ARRAY return_temp; + int index; +} +COPTIONS; + +#define THIS OBJECT(COPTIONS) + +#endif + +#endif diff --git a/main/lib/option/main.c b/main/lib/option/main.c new file mode 100755 index 00000000..bff19b44 --- /dev/null +++ b/main/lib/option/main.c @@ -0,0 +1,90 @@ +/*************************************************************************** + + main.c + + (c) 2000-2009 Chintan Rao + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include +#include +#include +#include + +#include "getoptions.h" + +#include "main.h" + +static void *_old_hook_main; + +char** cmd_arg; +int arg_count; + + +GB_INTERFACE GB EXPORT; + + + +GB_DESC *GB_CLASSES[] EXPORT = +{ + GetOptionsDesc, + NULL +}; + +static void hook_main(int *argc, char ***argv) +{ + int i; + char **tmp; + + CALL_HOOK_MAIN(_old_hook_main, argc, argv); + + arg_count = *argc; + + GB.NewArray(POINTER(&cmd_arg), sizeof(*cmd_arg), 0); + + for(i=0; i<*argc; i++) + { + tmp = (char **)GB.Add((void*)(&cmd_arg)); + *tmp = GB.NewZeroString((*argv)[i]); + } + + *argc = 1; +} + +int EXPORT GB_INIT(void) +{ + _old_hook_main = GB.Hook(GB_HOOK_MAIN, (void *)hook_main); + return 0; +} + + +void EXPORT GB_EXIT() +{ + int i; + for(i=0;i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +extern char ** cmd_arg; +extern int arg_count; +#endif + +#endif diff --git a/main/lib/signal/Makefile.am b/main/lib/signal/Makefile.am new file mode 100644 index 00000000..42d913ad --- /dev/null +++ b/main/lib/signal/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.signal +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.signal.la + +gb_signal_la_LIBADD = @GBX_THREAD_LIB@ +gb_signal_la_LDFLAGS = -module @LD_FLAGS@ +gb_signal_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx @GBX_THREAD_INC@ @INCLTDL@ $(AM_CFLAGS) + +gb_signal_la_SOURCES = \ + csignal.h csignal.c \ + main.h main.c + + diff --git a/main/lib/signal/csignal.c b/main/lib/signal/csignal.c new file mode 100644 index 00000000..5ce5c6c9 --- /dev/null +++ b/main/lib/signal/csignal.c @@ -0,0 +1,261 @@ +/*************************************************************************** + + csignal.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CSIGNAL_C + +#include +#include +#include +#include +#include + +#include "main.h" +#include "csignal.h" + +#ifndef SIGPOLL +#define SIGPOLL SIGIO +#endif + +// The "-1" signal is used for ignoring signal numbers + +#ifndef SIGPWR +#define SIGPWR -1 +#endif + +#ifdef OS_CYGWIN +#define SIGIOT -1 +#endif + + +#define NUM_SIGNALS 32 + +enum { + SH_DEFAULT = 0, + SH_IGNORE = 1, + SH_CATCH = 2 +}; + +typedef + struct SIGNAL_HANDLER { + GB_SIGNAL_CALLBACK *handler; + struct sigaction action; + char state; + } + SIGNAL_HANDLER; + +static SIGNAL_HANDLER _signals[NUM_SIGNALS] = { { 0 } }; +static int _signal = -1; +static GB_FUNCTION _application_signal_func; +static bool _init_signal = FALSE; + +static void init_signal(void) +{ + if (GB.GetFunction(&_application_signal_func, (void *)GB.Application.StartupClass(), "Application_Signal", "i", "")) + { + GB.Error("No Application_Signal event handler defined in startup class"); + return; + } + + _init_signal = TRUE; +} + +static void catch_signal(int num, intptr_t data) +{ + GB.Push(1, GB_T_INTEGER, num); + GB.Call(&_application_signal_func, 1, TRUE); +} + +static void handle_signal(int num, char state) +{ + struct sigaction action; + SIGNAL_HANDLER *sh; + + if (num < 0) + return; + + sh = &_signals[num]; + + if (sh->state == state) + return; + + if (sh->state == SH_IGNORE) + { + if (sigaction(num, &sh->action, NULL)) + { + GB.Error("Unable to reset signal handler"); + return; + } + } + else if (sh->state == SH_CATCH) + { + if (sh->handler) + { + GB.Signal.Unregister(num, sh->handler); + sh->handler = NULL; + } + } + + if (state == SH_IGNORE) + { + action.sa_handler = SIG_IGN; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + + if (sigaction(num, &action, &sh->action)) + { + GB.Error("Unable to modify signal handler"); + return; + } + } + else if (state == SH_CATCH) + { + if (num == SIGKILL || num == SIGSTOP) + { + GB.Error("SIGKILL and SIGSTOP cannot be caught"); + return; + } + + if (!_init_signal) + init_signal(); + + sh->handler = GB.Signal.Register(num, catch_signal, 0); + } + + sh->state = state; +} + +static void exit_signal(void) +{ + int i; + + for (i = 0; i < NUM_SIGNALS; i++) + handle_signal(i, SH_DEFAULT); +} + + +BEGIN_METHOD_VOID(Signal_Reset) + + handle_signal(_signal, SH_DEFAULT); + +END_METHOD + +BEGIN_METHOD_VOID(Signal_Ignore) + + handle_signal(_signal, SH_IGNORE); + +END_METHOD + +BEGIN_METHOD_VOID(Signal_Catch) + + handle_signal(_signal, SH_CATCH); + +END_METHOD + +BEGIN_METHOD(Signal_get, GB_INTEGER num) + + int num = VARG(num); + + if (num < -1 || num >= NUM_SIGNALS) + { + GB.Error("Bad signal number"); + return; + } + + _signal = num; + RETURN_SELF(); + +END_METHOD + +BEGIN_METHOD_VOID(Signal_exit) + + exit_signal(); + +END_METHOD + +BEGIN_METHOD(Signal_Send, GB_INTEGER pid; GB_INTEGER sig) + + if (kill(VARG(pid), VARG(sig))) + GB.Error("Unable to send signal: &1", strerror(errno)); + +END_METHOD + + +GB_DESC CSignalHandlerDesc[] = +{ + GB_DECLARE_VIRTUAL(".SignalHandler"), + + GB_STATIC_METHOD("Reset", NULL, Signal_Reset, NULL), + GB_STATIC_METHOD("Ignore", NULL, Signal_Ignore, NULL), + GB_STATIC_METHOD("Catch", NULL, Signal_Catch, NULL), + + GB_END_DECLARE +}; + +GB_DESC CSignalDesc[] = +{ + GB_DECLARE_STATIC("Signal"), + + GB_CONSTANT("SIGHUP", "i", SIGHUP), + GB_CONSTANT("SIGINT", "i", SIGINT), + GB_CONSTANT("SIGQUIT", "i", SIGQUIT), + GB_CONSTANT("SIGILL", "i", SIGILL), + GB_CONSTANT("SIGTRAP", "i", SIGTRAP), + GB_CONSTANT("SIGABRT", "i", SIGABRT), + GB_CONSTANT("SIGIOT", "i", SIGIOT), + GB_CONSTANT("SIGBUS", "i", SIGBUS), + GB_CONSTANT("SIGFPE", "i", SIGFPE), + GB_CONSTANT("SIGKILL", "i", SIGKILL), + GB_CONSTANT("SIGUSR1", "i", SIGUSR1), + GB_CONSTANT("SIGSEGV", "i", SIGSEGV), + GB_CONSTANT("SIGUSR2", "i", SIGUSR2), + GB_CONSTANT("SIGPIPE", "i", SIGPIPE), + GB_CONSTANT("SIGALRM", "i", SIGALRM), + GB_CONSTANT("SIGTERM", "i", SIGTERM), + //GB_CONSTANT("SIGSTKFLT", "i", SIGSTKFLT), + //GB_CONSTANT("SIGCLD", "i", SIGCLD), + GB_CONSTANT("SIGCHLD", "i", SIGCHLD), + GB_CONSTANT("SIGCONT", "i", SIGCONT), + GB_CONSTANT("SIGSTOP", "i", SIGSTOP), + GB_CONSTANT("SIGTSTP", "i", SIGTSTP), + GB_CONSTANT("SIGTTIN", "i", SIGTTIN), + GB_CONSTANT("SIGTTOU", "i", SIGTTOU), + GB_CONSTANT("SIGURG", "i", SIGURG), + GB_CONSTANT("SIGXCPU", "i", SIGXCPU), + GB_CONSTANT("SIGXFSZ", "i", SIGXFSZ), + GB_CONSTANT("SIGVTALRM", "i", SIGVTALRM), + GB_CONSTANT("SIGPROF", "i", SIGPROF), + GB_CONSTANT("SIGWINCH", "i", SIGWINCH), + GB_CONSTANT("SIGPOLL", "i", SIGPOLL), + GB_CONSTANT("SIGIO", "i", SIGIO), + GB_CONSTANT("SIGPWR", "i", SIGPWR), + GB_CONSTANT("SIGSYS", "i", SIGSYS), + + GB_STATIC_METHOD("_exit", NULL, Signal_exit, NULL), + + GB_STATIC_METHOD("_get", ".SignalHandler", Signal_get, "(Signal)i"), + + GB_STATIC_METHOD("Send", NULL, Signal_Send, "(Process)i(Signal)i"), + + GB_END_DECLARE +}; + diff --git a/main/lib/signal/csignal.h b/main/lib/signal/csignal.h new file mode 100644 index 00000000..7a620767 --- /dev/null +++ b/main/lib/signal/csignal.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + csignal.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CSIGNAL_H +#define __CSIGNAL_H + +#include "gambas.h" + +#ifndef __CDEBUG_C +extern GB_DESC CSignalDesc[]; +extern GB_DESC CSignalHandlerDesc[]; +#endif + +#endif diff --git a/main/lib/signal/gb.signal.component b/main/lib/signal/gb.signal.component new file mode 100644 index 00000000..89ae9ac9 --- /dev/null +++ b/main/lib/signal/gb.signal.component @@ -0,0 +1,3 @@ +[Component] +Key=gb.signal +Author=Benoît Minisini diff --git a/main/lib/signal/main.c b/main/lib/signal/main.c new file mode 100644 index 00000000..742f8bce --- /dev/null +++ b/main/lib/signal/main.c @@ -0,0 +1,48 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gambas.h" +#include "csignal.h" +#include "main.h" + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CSignalHandlerDesc, + CSignalDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} + + diff --git a/main/lib/signal/main.h b/main/lib/signal/main.h new file mode 100644 index 00000000..d14ee22f --- /dev/null +++ b/main/lib/signal/main.h @@ -0,0 +1,34 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/main/lib/term/Makefile.am b/main/lib/term/Makefile.am new file mode 100644 index 00000000..2ec5ec8a --- /dev/null +++ b/main/lib/term/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.term +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.term.la + +gb_term_la_LIBADD = @GBX_THREAD_LIB@ +gb_term_la_LDFLAGS = -module @LD_FLAGS@ +gb_term_la_CFLAGS = -I$(top_srcdir)/share -I$(top_srcdir)/gbx @GBX_THREAD_INC@ @INCLTDL@ $(AM_CFLAGS) + +gb_term_la_SOURCES = \ + cterm.h cterm.c \ + main.h main.c + + diff --git a/main/lib/term/cterm.c b/main/lib/term/cterm.c new file mode 100644 index 00000000..5567648c --- /dev/null +++ b/main/lib/term/cterm.c @@ -0,0 +1,478 @@ +/*************************************************************************** + + cterm.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __CTERM_C + +#include "main.h" +#include +#include "cterm.h" + +#define STREAM_FD (GB.Stream.Handle(GB.Stream.Get(_object))) + +#define THIS_SETTINGS ((CTERMINALSETTINGS *)_object) +#define SETTINGS THIS_SETTINGS->settings + +static bool check_error(int ret) +{ + if (ret) + { + GB.Error(strerror(errno)); + return TRUE; + } + else + return FALSE; +} + +/*static bool _convert(CTERMINAL *_object, GB_TYPE type, GB_VALUE *conv) +{ + if (!THIS && type >= GB_T_OBJECT && GB.Is(conv->_object.value, GB.FindClass("Stream"))) + { + + } + + return TRUE; + + switch(type) + { + case GB_T_FLOAT: + conv->_object.value = COMPLEX_create(conv->_float.value, 0); + return FALSE; + + case GB_T_SINGLE: + conv->_object.value = COMPLEX_create(conv->_single.value, 0); + return FALSE; + + case GB_T_LONG: + conv->_object.value = COMPLEX_create((double)conv->_long.value, 0); + return FALSE; + + case GB_T_INTEGER: + case GB_T_SHORT: + case GB_T_BYTE: + conv->_object.value = COMPLEX_create(conv->_integer.value, 0); + return FALSE; + + default: + return TRUE; + } +}*/ + +//--------------------------------------------------------------------------- + +static CTERMINALSETTINGS *TERMINALSETTINGS_copy(CTERMINALSETTINGS *_object) +{ + CTERMINALSETTINGS *settings; + settings = (CTERMINALSETTINGS *)GB.New(GB.FindClass("TerminalSettings"), NULL, NULL); + settings->settings = SETTINGS; + return settings; +} + +BEGIN_METHOD_VOID(TerminalSettings_MakeRaw) + + CTERMINALSETTINGS *settings = TERMINALSETTINGS_copy(THIS_SETTINGS); + cfmakeraw(&SETTINGS); + GB.ReturnObject(settings); + +END_METHOD + +BEGIN_METHOD_VOID(TerminalSettings_Copy) + + GB.ReturnObject(TERMINALSETTINGS_copy(THIS_SETTINGS)); + +END_METHOD + +static void handle_settings(tcflag_t *pflag, void *_param, int cst) +{ + if (READ_PROPERTY) + GB.ReturnBoolean(*pflag & cst); + else + { + if (VPROP(GB_BOOLEAN)) + *pflag |= cst; + else + *pflag &= ~cst; + } +} + +static void handle_settings_int(tcflag_t *pflag, void *_param, int mask) +{ + if (READ_PROPERTY) + GB.ReturnInteger(*pflag & mask); + else + { + *pflag &= ~mask; + *pflag |= VPROP(GB_INTEGER) & mask; + } +} + +static void handle_settings_char(cc_t pcc[], void *_param, int pos) +{ + if (READ_PROPERTY) + GB.ReturnInteger(pcc[pos]); + else + pcc[pos] = (cc_t)VPROP(GB_INTEGER); +} + +#define IMPLEMENT_TSP(_name, _field) \ +BEGIN_PROPERTY(TerminalSettings_##_name) \ + handle_settings(&SETTINGS._field, _param, _name); \ +END_PROPERTY + +#define IMPLEMENT_TSP_I(_name, _field) \ +BEGIN_PROPERTY(TerminalSettings_##_name) \ + handle_settings_int(&SETTINGS._field, _param, _name); \ +END_PROPERTY + +#define IMPLEMENT_TSP_C(_name) \ +BEGIN_PROPERTY(TerminalSettings_##_name) \ + handle_settings_char(SETTINGS.c_cc, _param, _name); \ +END_PROPERTY + +IMPLEMENT_TSP(IGNBRK, c_iflag) +IMPLEMENT_TSP(BRKINT, c_iflag) +IMPLEMENT_TSP(IGNPAR, c_iflag) +IMPLEMENT_TSP(PARMRK, c_iflag) +IMPLEMENT_TSP(INPCK, c_iflag) +IMPLEMENT_TSP(ISTRIP, c_iflag) +IMPLEMENT_TSP(INLCR, c_iflag) +IMPLEMENT_TSP(IGNCR, c_iflag) +IMPLEMENT_TSP(ICRNL, c_iflag) +IMPLEMENT_TSP(IXON, c_iflag) +IMPLEMENT_TSP(IXANY, c_iflag) +IMPLEMENT_TSP(IXOFF, c_iflag) +#ifdef OS_LINUX +IMPLEMENT_TSP(IUCLC, c_iflag) +IMPLEMENT_TSP(IUTF8, c_iflag) + +IMPLEMENT_TSP(OLCUC, c_oflag) +IMPLEMENT_TSP(OFILL, c_oflag) +#endif +IMPLEMENT_TSP(OPOST, c_oflag) +IMPLEMENT_TSP(ONLCR, c_oflag) +IMPLEMENT_TSP(OCRNL, c_oflag) +IMPLEMENT_TSP(ONOCR, c_oflag) +IMPLEMENT_TSP(ONLRET, c_oflag) + +#ifndef OS_BSD +IMPLEMENT_TSP_I(NLDLY, c_oflag) +IMPLEMENT_TSP_I(CRDLY, c_oflag) +IMPLEMENT_TSP_I(TABDLY, c_oflag) +IMPLEMENT_TSP_I(BSDLY, c_oflag) +IMPLEMENT_TSP_I(VTDLY, c_oflag) +IMPLEMENT_TSP_I(FFDLY, c_oflag) +#endif + +IMPLEMENT_TSP_I(CSIZE, c_cflag) +IMPLEMENT_TSP(CSTOPB, c_cflag) +IMPLEMENT_TSP(CREAD, c_cflag) +IMPLEMENT_TSP(PARENB, c_cflag) +IMPLEMENT_TSP(PARODD, c_cflag) +IMPLEMENT_TSP(HUPCL, c_cflag) +IMPLEMENT_TSP(CLOCAL, c_cflag) +IMPLEMENT_TSP(CRTSCTS, c_cflag) + +IMPLEMENT_TSP(ISIG, c_lflag) +IMPLEMENT_TSP(ICANON, c_lflag) +IMPLEMENT_TSP(ECHO, c_lflag) +IMPLEMENT_TSP(ECHOE, c_lflag) +IMPLEMENT_TSP(ECHOK, c_lflag) +IMPLEMENT_TSP(ECHONL, c_lflag) +IMPLEMENT_TSP(ECHOCTL, c_lflag) +IMPLEMENT_TSP(ECHOKE, c_lflag) +IMPLEMENT_TSP(FLUSHO, c_lflag) +IMPLEMENT_TSP(NOFLSH, c_lflag) +IMPLEMENT_TSP(TOSTOP, c_lflag) +IMPLEMENT_TSP(IEXTEN, c_lflag) + +#ifdef OS_LINUX +#ifndef CMSPAR +// This is a MIPS fix +#define CMSPAR 010000000000 +#endif + +IMPLEMENT_TSP(CMSPAR, c_cflag) +IMPLEMENT_TSP(XCASE, c_lflag) +#endif + +#ifndef OS_CYGWIN +IMPLEMENT_TSP(ECHOPRT, c_lflag) +IMPLEMENT_TSP(PENDIN, c_lflag) +#endif + +IMPLEMENT_TSP_C(VDISCARD) +IMPLEMENT_TSP_C(VEOF) +IMPLEMENT_TSP_C(VEOL) +IMPLEMENT_TSP_C(VEOL2) +IMPLEMENT_TSP_C(VERASE) +IMPLEMENT_TSP_C(VINTR) +IMPLEMENT_TSP_C(VKILL) +IMPLEMENT_TSP_C(VLNEXT) +IMPLEMENT_TSP_C(VMIN) +IMPLEMENT_TSP_C(VQUIT) +IMPLEMENT_TSP_C(VREPRINT) +IMPLEMENT_TSP_C(VSTART) +IMPLEMENT_TSP_C(VSTOP) +IMPLEMENT_TSP_C(VSUSP) +IMPLEMENT_TSP_C(VTIME) +IMPLEMENT_TSP_C(VWERASE) + +BEGIN_PROPERTY(TerminalSettings_InputSpeed) + + if (READ_PROPERTY) + GB.ReturnInteger(cfgetispeed(&SETTINGS)); + else + cfsetispeed(&SETTINGS, VPROP(GB_INTEGER)); + +END_PROPERTY + +BEGIN_PROPERTY(TerminalSettings_OutputSpeed) + + if (READ_PROPERTY) + GB.ReturnInteger(cfgetospeed(&SETTINGS)); + else + cfsetospeed(&SETTINGS, VPROP(GB_INTEGER)); + +END_PROPERTY + +//--------------------------------------------------------------------------- + +BEGIN_PROPERTY(StreamTerm_Name) + + GB.ReturnNewZeroString(ttyname(STREAM_FD)); + +END_PROPERTY + +BEGIN_METHOD_VOID(StreamTerm_GetAttr) + + CTERMINALSETTINGS *settings; + + //fprintf(stderr, "fd = %d\n", STREAM_FD); + + settings = (CTERMINALSETTINGS *)GB.New(GB.FindClass("TerminalSettings"), NULL, NULL); + if (check_error(tcgetattr(STREAM_FD, &settings->settings))) + { + GB.Unref(POINTER(&settings)); + return; + } + + GB.ReturnObject(settings); + +END_PROPERTY + +BEGIN_METHOD(StreamTerm_SetAttr, GB_INTEGER mode; GB_OBJECT settings) + + CTERMINALSETTINGS *settings; + struct termios check; + + settings = (CTERMINALSETTINGS *)VARG(settings); + if (GB.CheckObject(settings)) + return; + + check_error(tcsetattr(STREAM_FD, VARG(mode), &settings->settings)); + + tcgetattr(STREAM_FD, &check); + if (check.c_iflag != settings->settings.c_iflag || check.c_oflag != settings->settings.c_oflag || check.c_lflag != settings->settings.c_lflag) + GB.Error("Unable to set terminal attributes"); + +END_PROPERTY + +BEGIN_METHOD(StreamTerm_Flush, GB_INTEGER mode) + + check_error(tcflush(STREAM_FD, VARG(mode))); + +END_METHOD + +BEGIN_METHOD_VOID(StreamTerm_Drain) + + check_error(tcdrain(STREAM_FD)); + +END_METHOD + +BEGIN_METHOD(StreamTerm_Flow, GB_INTEGER mode) + + check_error(tcflow(STREAM_FD, VARG(mode))); + +END_METHOD + +BEGIN_METHOD_VOID(StreamTerm_SendBreak) + + check_error(tcsendbreak(STREAM_FD, 0)); + +END_METHOD + +/*BEGIN_PROPERTY(Terminal_Control) + + GB.ReturnNewZeroString(ctermid(NULL)); + +END_PROPERTY*/ + +//--------------------------------------------------------------------------- + +#define __TC(_name) GB_CONSTANT(#_name, "i", _name) + +GB_DESC TermDesc[] = +{ + GB_DECLARE_STATIC("Term"), + + __TC(TCSANOW), __TC(TCSADRAIN), __TC(TCSAFLUSH), + + __TC(TCIFLUSH), __TC(TCOFLUSH), __TC(TCIOFLUSH), + + __TC(TCIOFF), __TC(TCION), __TC(TCOOFF), __TC(TCOON), +#ifdef OS_LINUX + __TC(NL0), __TC(NL1), __TC(CR0), __TC(CR1), __TC(CR2), __TC(CR3), __TC(TAB0), __TC(TAB1), __TC(TAB2), __TC(TAB3), __TC(XTABS), __TC(BS0), __TC(BS1), __TC(VT0), __TC(VT1), __TC(FF0), __TC(FF1), +#endif + __TC(CS5), __TC(CS6), __TC(CS7), __TC(CS8), + + GB_CONSTANT("VDISABLE", "i", _POSIX_VDISABLE), + + __TC(B0), __TC(B50), __TC(B75), __TC(B110), __TC(B134), __TC(B150), __TC(B200), __TC(B300), __TC(B600), __TC(B1200), __TC(B1800), __TC(B2400), __TC(B4800), __TC(B9600), __TC(B19200), __TC(B38400), __TC(B57600), __TC(B115200), __TC(B230400), + +#ifndef OS_BSD + __TC(B460800), __TC(B500000), __TC(B576000), __TC(B921600), __TC(B1000000), __TC(B1152000), __TC(B1500000), __TC(B2000000), __TC(B2500000), __TC(B3000000), +#endif +#ifdef OS_LINUX +__TC(B3500000), __TC(B4000000), +#endif + + GB_END_DECLARE +}; + +#define __TSP(_name) GB_PROPERTY(#_name, "b", TerminalSettings_##_name) +#define __TSP_I(_name) GB_PROPERTY(#_name, "i", TerminalSettings_##_name) + +GB_DESC TerminalSettingsDesc[] = +{ + GB_DECLARE("TerminalSettings", sizeof(CTERMINALSETTINGS)), + GB_NOT_CREATABLE(), + + __TSP(IGNBRK), + __TSP(BRKINT), + __TSP(IGNPAR), + __TSP(PARMRK), + __TSP(INPCK), + __TSP(ISTRIP), + __TSP(INLCR), + __TSP(IGNCR), + __TSP(ICRNL), + __TSP(IXON), + __TSP(IXANY), + __TSP(IXOFF), +#ifdef OS_LINUX + __TSP(IUCLC), + __TSP(IUTF8), + + __TSP(OLCUC), + __TSP(OFILL), +#endif + __TSP(OPOST), + __TSP(ONLCR), + __TSP(OCRNL), + __TSP(ONOCR), + __TSP(ONLRET), + +#ifndef OS_BSD + __TSP_I(NLDLY), + __TSP_I(CRDLY), + __TSP_I(TABDLY), + __TSP_I(BSDLY), + __TSP_I(VTDLY), + __TSP_I(FFDLY), +#endif + + __TSP_I(CSIZE), + __TSP(CSTOPB), + __TSP(CREAD), + __TSP(PARENB), + __TSP(PARODD), + __TSP(HUPCL), + __TSP(CLOCAL), + __TSP(CRTSCTS), + + __TSP(ISIG), + __TSP(ICANON), + __TSP(ECHO), + __TSP(ECHOE), + __TSP(ECHOK), + __TSP(ECHONL), + __TSP(ECHOCTL), + __TSP(ECHOKE), + __TSP(FLUSHO), + __TSP(NOFLSH), + __TSP(TOSTOP), + __TSP(IEXTEN), + +#ifdef OS_LINUX + __TSP(CMSPAR), + __TSP(XCASE), +#endif +#ifndef OS_CYGWIN + __TSP(ECHOPRT), + __TSP(PENDIN), +#endif + + __TSP_I(VDISCARD), + __TSP_I(VEOF), + __TSP_I(VEOL), + __TSP_I(VEOL2), + __TSP_I(VERASE), + __TSP_I(VINTR), + __TSP_I(VKILL), + __TSP_I(VLNEXT), + __TSP_I(VMIN), + __TSP_I(VQUIT), + __TSP_I(VREPRINT), + __TSP_I(VSTART), + __TSP_I(VSTOP), + __TSP_I(VSUSP), + __TSP_I(VTIME), + __TSP_I(VWERASE), + + GB_PROPERTY("InputSpeed", "i", TerminalSettings_InputSpeed), + GB_PROPERTY("OutputSpeed", "i", TerminalSettings_OutputSpeed), + + GB_METHOD("MakeRaw", "TerminalSettings", TerminalSettings_MakeRaw, NULL), + GB_METHOD("Copy", "TerminalSettings", TerminalSettings_Copy, NULL), + + GB_END_DECLARE +}; + +GB_DESC StreamTermDesc[] = +{ + GB_DECLARE_VIRTUAL(".Stream.Term"), + + //GB_STATIC_PROPERTY_READ("Control", "s", Terminal_Control), + + GB_METHOD("Flush", NULL, StreamTerm_Flush, "(Mode)i"), + GB_METHOD("Drain", NULL, StreamTerm_Drain, NULL), + GB_METHOD("Flow", NULL, StreamTerm_Flow, "(Mode)i"), + GB_METHOD("SendBreak", NULL, StreamTerm_SendBreak, NULL), + + GB_METHOD("GetAttr", "TerminalSettings", StreamTerm_GetAttr, NULL), + GB_METHOD("SetAttr", NULL, StreamTerm_SetAttr, "(Mode)i(Settings)TerminalSettings;"), + + //GB_INTERFACE("_convert", &_convert), + + GB_END_DECLARE +}; + diff --git a/main/lib/term/cterm.h b/main/lib/term/cterm.h new file mode 100644 index 00000000..796abe4e --- /dev/null +++ b/main/lib/term/cterm.h @@ -0,0 +1,44 @@ +/*************************************************************************** + + cterm.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __CTERM_H +#define __CTERM_H + +#include "gambas.h" +#include +#include + +#ifndef __CTERM_C +extern GB_DESC TermDesc[]; +extern GB_DESC StreamTermDesc[]; +extern GB_DESC TerminalSettingsDesc[]; +#endif + +typedef + struct { + GB_BASE ob; + struct termios settings; + } + CTERMINALSETTINGS; + +#endif diff --git a/main/lib/term/gb.term.component b/main/lib/term/gb.term.component new file mode 100644 index 00000000..6e790664 --- /dev/null +++ b/main/lib/term/gb.term.component @@ -0,0 +1,3 @@ +[Component] +Key=gb.term +Author=Benoît Minisini diff --git a/main/lib/term/main.c b/main/lib/term/main.c new file mode 100644 index 00000000..1d8db370 --- /dev/null +++ b/main/lib/term/main.c @@ -0,0 +1,49 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "gambas.h" +#include "cterm.h" +#include "main.h" + +const GB_INTERFACE *GB_PTR EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + TermDesc, + TerminalSettingsDesc, + StreamTermDesc, + NULL +}; + +int EXPORT GB_INIT(void) +{ + return 0; +} + +void EXPORT GB_EXIT() +{ +} + + diff --git a/main/lib/term/main.h b/main/lib/term/main.h new file mode 100644 index 00000000..23f4e73b --- /dev/null +++ b/main/lib/term/main.h @@ -0,0 +1,36 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include "gambas.h" +#include "gb_common.h" + +#ifndef __MAIN_C +extern const GB_INTERFACE *GB_PTR; +#endif + +#define GB (*GB_PTR) + +#endif diff --git a/main/lib/vb/Makefile.am b/main/lib/vb/Makefile.am new file mode 100644 index 00000000..f157f2b5 --- /dev/null +++ b/main/lib/vb/Makefile.am @@ -0,0 +1,14 @@ +COMPONENT = gb.vb +include $(top_srcdir)/component.am + +gblib_LTLIBRARIES = gb.vb.la + +gb_vb_la_LIBADD = @C_LIB@ @MATH_LIB@ +gb_vb_la_LDFLAGS = -module @LD_FLAGS@ +gb_vb_la_CFLAGS = -I$(top_srcdir)/share $(AM_CFLAGS) + +gb_vb_la_SOURCES = main.h main.c vb.h vb.c vbdate.h vbdate.c + + + + diff --git a/main/lib/vb/gb.vb.component b/main/lib/vb/gb.vb.component new file mode 100644 index 00000000..35845d62 --- /dev/null +++ b/main/lib/vb/gb.vb.component @@ -0,0 +1,6 @@ +[Component] +Key=gb.vb +Name=Visual Basic compatibility +Author=Benoît Minisini,Daniel Campos Fernández,Nigel Gerrard + + diff --git a/main/lib/vb/main.c b/main/lib/vb/main.c new file mode 100644 index 00000000..e285447f --- /dev/null +++ b/main/lib/vb/main.c @@ -0,0 +1,50 @@ +/*************************************************************************** + + main.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __MAIN_C + +#include "vb.h" +#include "vbdate.h" +#include "main.h" + + +GB_INTERFACE GB EXPORT; + +GB_DESC *GB_CLASSES[] EXPORT = +{ + CVbDesc, + NULL +}; + + + +int EXPORT GB_INIT(void) +{ + return 0; +} + + + +void EXPORT GB_EXIT() +{ +} diff --git a/main/lib/vb/main.h b/main/lib/vb/main.h new file mode 100644 index 00000000..a903d840 --- /dev/null +++ b/main/lib/vb/main.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + main.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include + +#include "gambas.h" + +#ifndef __MAIN_C +extern GB_INTERFACE GB; +#endif + +#endif diff --git a/main/lib/vb/vb.c b/main/lib/vb/vb.c new file mode 100644 index 00000000..cf6bf43a --- /dev/null +++ b/main/lib/vb/vb.c @@ -0,0 +1,312 @@ +/*************************************************************************** + + vb.c + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __VB_C + +#include "vb.h" +#include "vbdate.h" +#include + +#ifdef OS_CYGWIN + #include +#else + #include +#endif + +#include + +BEGIN_METHOD(CVB_val, GB_STRING str) + + GB_VALUE result; + + GB.NumberFromString(GB_NB_READ_ALL | GB_NB_READ_HEX_BIN, STRING(str), LENGTH(str), &result); + + if (result.type == GB_T_INTEGER) + GB.ReturnInteger(((GB_INTEGER *)(void *)&result)->value); + if (result.type == GB_T_LONG) + GB.ReturnLong(((GB_LONG *)(void *)&result)->value); + else if (result.type == GB_T_FLOAT) + GB.ReturnFloat(((GB_FLOAT *)(void *)&result)->value); + else + GB.ReturnInteger(0); + + GB.ReturnConvVariant(); + +END_METHOD + + +/* val should be GB_VARIANT. This is just a trick, so that + I can use STRING() and LENGTH() macros just after having + converted val to a string. Yes, I know... Horrible. +*/ + +BEGIN_METHOD(CVB_str, GB_STRING val) + + GB.Conv((GB_VALUE *)(void *)ARG(val), GB_T_STRING); + + GB.ReturnNewString(STRING(val), LENGTH(val)); + +END_METHOD + +BEGIN_METHOD(CVB_Left, GB_STRING val;GB_INTEGER Count;) + + if (VARG(Count)<=0) + { + GB.Error("Invalid parameter"); + return; + } + if ( LENGTH(val)<=VARG(Count) ) + { + GB.ReturnNewString(STRING(val), LENGTH(val)); + return; + } + + GB.ReturnNewString(STRING(val), VARG(Count)); + +END_METHOD + +BEGIN_METHOD(CVB_Right, GB_STRING val;GB_INTEGER Count;) + + if (VARG(Count)<=0) + { + GB.Error("Invalid parameter"); + return; + } + if ( LENGTH(val)<=VARG(Count) ) + { + GB.ReturnNewString(STRING(val), LENGTH(val)); + return; + } + + GB.ReturnNewString(STRING(val)+(LENGTH(val)-VARG(Count)), VARG(Count)); + +END_METHOD + +BEGIN_METHOD(CVB_Mid, GB_STRING val;GB_INTEGER Start;GB_INTEGER Count;) + + int count; + + if (VARG(Count)<=0) + { + GB.Error("Invalid parameter"); + return; + } + + if (!MISSING (Count) ) + { + if (VARG(Count)<=0) + { + GB.Error("Invalid parameter"); + return; + } + count=VARG(Count); + } + else + count=LENGTH(val); + + if ( VARG(Start)>LENGTH(val) ) + GB.ReturnNewString(NULL,0); + + if ( ( LENGTH(val)-VARG(Start) ) <= count ) count=LENGTH(val)-VARG(Start)+1; + + GB.ReturnNewString(STRING(val)+VARG(Start)-1, count ); + + +END_METHOD + +BEGIN_METHOD(CVB_DateAdd, GB_STRING period;GB_INTEGER interval; GB_DATE Date) + + char *Period=NULL; + int Interval = 0; + GB_DATE RETURN; + Period=GB.ToZeroString(ARG(period)); + Interval = VARG(interval); + + RETURN.type = ARG(Date)->type; + RETURN.value.date = ARG(Date)->value.date; + RETURN.value.time = ARG(Date)->value.time; + + if (strncasecmp(Period,"yyyy",4) == 0){ //Add Years + DATE_adjust(&RETURN, 4, Interval); + } + else { + if (strncasecmp(Period,"ww",2)== 0){ //Add weeks + DATE_adjust(&RETURN, 1, (7 * Interval)); + } + else { + switch(Period[0]) + { + case 's': /* Seconds */ + case 'S': + DATE_adjust(&RETURN, 2, (1000 * Interval)); + break; + case 'n': /* Minutes */ + case 'N': + DATE_adjust(&RETURN, 2, (60000 * Interval)); + break; + case 'h': /* Hours */ + case 'H': + DATE_adjust(&RETURN, 2, (3600000 * Interval)); + break; + case 'd': /* Days */ + case 'D': + case 'y': /* Day of Year */ + case 'Y': + DATE_adjust(&RETURN, 1, Interval); + break; + case 'w': /* Weekdays : Monday - Friday */ + case 'W': + DATE_adjust(&RETURN, 3, Interval); + break; + case 'm': /* Calendar Months */ + case 'M': + DATE_adjust(&RETURN, 0, Interval); + break; + case 'q': /* Quarters */ + case 'Q': + DATE_adjust(&RETURN, 0, (3 * Interval)); + break; + default: + GB.Error("Invalid date parameter"); + } + } + } + + + GB.ReturnDate(&RETURN); + +END_METHOD + +BEGIN_METHOD(CVB_DateDiff, GB_STRING period;GB_DATE Date1; GB_DATE Date2) + + GB_DATE Date1, Date2; + char *Period=NULL; + int Interval = 0; + + Period=GB.ToZeroString(ARG(period)); + Date1.type = ARG(Date1)->type; + Date1.value.date = ARG(Date1)->value.date; + Date1.value.time = ARG(Date1)->value.time; + + Date2.type = ARG(Date2)->type; + Date2.value.date = ARG(Date2)->value.date; + Date2.value.time = ARG(Date2)->value.time; + + if (strncasecmp(Period,"yyyy",4) == 0){ //Add Years + Interval = DATE_diff(&Date1, &Date2, 4); + } + else { + if (strncasecmp(Period,"ww",2)== 0){ //Weeks + Interval = DATE_diff(&Date1, &Date2, 5); + } + else { + switch(Period[0]) + { + case 's': /* Seconds */ + case 'S': + Interval = DATE_diff(&Date1, &Date2, 2)/1000; + break; + case 'n': /* Minutes */ + case 'N': + Interval = DATE_diff(&Date1, &Date2, 2)/60000; + break; + case 'h': /* Hours */ + case 'H': + Interval = DATE_diff(&Date1, &Date2, 2)/3600000; + break; + case 'd': /* Days */ + case 'D': + case 'y': /* Day of Year */ + case 'Y': + Interval = DATE_diff(&Date1, &Date2, 1); + break; + case 'w': /* Weekdays : Monday - Friday */ + case 'W': + Interval = DATE_diff(&Date1, &Date2, 3); + break; + case 'm': /* Calendar Months */ + case 'M': + Interval = DATE_diff(&Date1, &Date2, 0); + break; + case 'q': /* Quarters */ + case 'Q': + Interval = (DATE_diff(&Date1, &Date2, 0)/3); + break; + default: + GB.Error("Invalid date parameter"); + } + } + } + + GB.ReturnInteger(Interval); + +END_METHOD + +BEGIN_METHOD(CVB_Round,GB_FLOAT Number;GB_INTEGER Decimals;) + + double decimals=VARGOPT(Decimals, 0); + double number=VARG(Number); + + if (decimals < 0) { GB.Error("Invalid argument"); return; } + + #if 0 /* test */ + decimals=exp10(decimals); + number*=decimals; + number=round(number); + number/=decimals; + #endif + + decimals = pow(10, decimals); + number *= decimals; + number = rint(number); + number /= decimals; + + GB.ReturnFloat(number); + +END_METHOD + +GB_DESC CVbDesc[] = +{ + GB_DECLARE("Vb", 0), GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("Val", "v", CVB_val, "(String)s"), + GB_STATIC_METHOD("Str", "s", CVB_str, "(Value)v"), + GB_STATIC_METHOD("Str$", "s", CVB_str, "(Value)v"), + GB_STATIC_METHOD("Left", "s", CVB_Left, "(String)s(Count)i"), + GB_STATIC_METHOD("Left$", "s", CVB_Left, "(String)s(Count)i"), + GB_STATIC_METHOD("Right", "s", CVB_Right, "(String)s(Count)i"), + GB_STATIC_METHOD("Right$", "s", CVB_Right, "(String)s(Count)i"), + GB_STATIC_METHOD("Mid", "s", CVB_Mid, "(String)s(Start)i[(Count)i]"), + GB_STATIC_METHOD("Mid$", "s", CVB_Mid, "(String)s(Start)i[(Count)i]"), + GB_STATIC_METHOD("DateAdd", "d", CVB_DateAdd, "(period)s(interval)i(Date)d"), + GB_STATIC_METHOD("DateDiff", "i", CVB_DateDiff, "(period)s(Date1)d(Date2)d"), + GB_STATIC_METHOD("Round","f",CVB_Round,"(Number)f[(Decimals)i"), + //GB_METHOD("Dir", "s", CVB_dir, "(Mask)s[(Attributes)i]"), + + GB_END_DECLARE +}; + + + + + diff --git a/main/lib/vb/vb.h b/main/lib/vb/vb.h new file mode 100644 index 00000000..f7c8d9b7 --- /dev/null +++ b/main/lib/vb/vb.h @@ -0,0 +1,33 @@ +/*************************************************************************** + + vb.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __VB_H +#define __VB_H + +#include "main.h" + +#ifndef __VB_C +extern GB_DESC CVbDesc[]; +#endif + +#endif diff --git a/main/lib/vb/vbdate.c b/main/lib/vb/vbdate.c new file mode 100644 index 00000000..5c16f1d7 --- /dev/null +++ b/main/lib/vb/vbdate.c @@ -0,0 +1,215 @@ +/*************************************************************************** + + vbdate.c + + (c) 2000-2003 Nigel Gerrard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __VBDATE_C + +#include "vbdate.h" +#include +#include + + +#define DATE_YEAR_MIN -4801 +#define DATE_YEAR_MAX 9999 + +/* Helper functions */ + +const char days_in_months[2][13] = +{ /* error, jan feb mar apr may jun jul aug sep oct nov dec */ + { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } /* leap year */ +}; + +const short days_in_year[2][14] = +{ /* 0, jan feb mar apr may jun jul aug sep oct nov dec */ + { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } /* leap year */ +}; + +/* Returns 1 for a leap year, 0 else */ +int date_is_leap_year(short year) +{ + if (year < 0) + year += 8001; + + if ((((year % 4) == 0) && ((year % 100) != 0)) || (year % 400) == 0) + return 1; + else + return 0; +} + +int date_is_valid(GB_DATE_SERIAL *date) +{ + return ((date->month >= 1) && (date->month <= 12) && + (date->year >= DATE_YEAR_MIN) && (date->year <= DATE_YEAR_MAX) && (date->year != 0) && + (date->day >= 1) && (date->day <= days_in_months[date_is_leap_year(date->year)][(short)date->month]) && + (date->hour >= 0) && (date->hour <= 23) && (date->min >= 0) && (date->min <= 59) && + (date->sec >= 0) && (date->sec <= 59)); +} + +/** Modulo function that works correctly for negative divisors */ +//#define modulo(x,y) ((x)%(y)<0?(x)%(y)+(y):(x)%(y)) + +static int modulo(int x, int y) +{ + int mod = x % y; + if (mod < 0) + mod += y; + return mod; +} + +void DATE_adjust( GB_DATE *vdate, int period, int interval) /* Adjust the date by the interval period */ +{ + + GB_DATE_SERIAL *date; + int year, month, day; + date = GB.SplitDate(vdate); + + switch(period){ + case 0: /* Calendar Month */ + year = ((date->year * 12) + (date->month - 1) + interval)/12; + month = modulo((date->month - 1)+interval, 12) + 1; + day = date->day > days_in_months[date_is_leap_year(year)][month] ? days_in_months[date_is_leap_year(year)][month] : date->day; + date->day = day; + date->month = month; + date->year = year; + GB.MakeDate(date, vdate); + break; + case 1: /* days */ + vdate->value.date += interval; + break; + case 2: /* Time */ + vdate->value.time += interval; + break; + case 3: /* weekdays - in this case weekdays are Mon - Fri */ + vdate->value.date += ( 7 * ( interval / 5)); + date->weekday += ( interval % 5 ); + if (date->weekday > 5){ + date->weekday -= 5; + vdate->value.date += 2; + } + if (date->weekday < 1){ + date->weekday += 5; + vdate->value.date -= 2; + } + vdate->value.date += ( interval % 5); + break; + case 4: /* Add year */ + while ( interval != 0){ + if ( interval < 0 ){ + vdate->value.date -= days_in_year[date_is_leap_year(date->year)][13]; + date->year--; + interval++; + } + else { + vdate->value.date += days_in_year[date_is_leap_year(date->year)][13]; + date->year++; + interval--; + } + } + break; + } + + /* Now if time takes it into another day */ + while (vdate->value.time >= 86400000){ + vdate->value.date++; + vdate->value.time -= 86400000; + } + /* Or time is negative so we need to take off a day */ + while (vdate->value.time < 0){ + vdate->value.date--; + vdate->value.time += 86400000; + } + + CLEAR(&date); + date = GB.SplitDate(vdate); + + if (!date_is_valid(date)){ + /*printf("Invalid date : year [%i] month [%i] day [%i] hour [%i] min [%i] sec [%i] weekday [%i] msec [%i]\n", + date->year, date->month, date->day, date->hour, date->min, date->sec, date->weekday, date->msec);*/ + GB.Error("Invalid Date Returned"); + } + CLEAR(&date); + +} + +int DATE_diff( GB_DATE *vdate1, GB_DATE *vdate2, int period) +{ + GB_DATE_SERIAL *date1, *date2; + int diff = 0; + int year1, year2, month1, month2, weekday1, weekday2; + + date1 = GB.SplitDate(vdate1); + weekday1 = date1->weekday; + year1 = date1->year; + month1 = date1->month; + + date2 = GB.SplitDate(vdate2); + weekday2 = date2->weekday; + year2 = date2->year; + month2 = date2->month; + + switch(period){ + case 0: /* Calendar Month */ + diff = ((year1 * 12) + month1) - ((year2 * 12) + month2); + break; + case 1: /* days */ + diff = vdate1->value.date - vdate2->value.date; + break; + case 2: /* Time */ + diff = ((vdate1->value.date - vdate2->value.date) * 86400000) + (vdate1->value.time - vdate2->value.time); + break; + case 3: /* weekdays */ + /* If invalid weekday then find closest valid day: + * eg. 7 -1, 6 -5*/ + weekday1 == 7 ? weekday1 = 1 : weekday1 == 6 ? weekday1 = 5 : weekday1; + weekday2 == 7 ? weekday2 = 1 : weekday2 == 6 ? weekday2 = 5 : weekday2; + + diff = (((vdate1->value.date - vdate2->value.date) / 7) * 5) + + (weekday1 - weekday2); + /* Needs validating */ + break; + case 4: /* year */ + diff = year1 - year2; + break; + case 5: /* Weeks */ + diff = (vdate1->value.date - vdate2->value.date)/7; + break; + } + CLEAR(&date1); + CLEAR(&date2); + return diff; +} + +/* End Helper functions */ + +/* +GB_DESC CVbDateDesc[] = +{ + GB_DECLARE("Vb", 0), GB_NOT_CREATABLE(), + + GB_STATIC_METHOD("DateAdd", "d", CVB_DateAdd, "(period)s(interval)i(Date)v"), + GB_STATIC_METHOD("DateDiff", "i", CVB_DateDiff, "(period)s(Date1)v(Date2)v"), + + GB_END_DECLARE +};*/ + diff --git a/main/lib/vb/vbdate.h b/main/lib/vb/vbdate.h new file mode 100644 index 00000000..c656b64f --- /dev/null +++ b/main/lib/vb/vbdate.h @@ -0,0 +1,32 @@ +/*************************************************************************** + + vbdate.h + + (c) 2000-2003 Nigel Gerrard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __VBDATE_H +#define __VBDATE_H + +#include "main.h" + +void DATE_adjust( GB_DATE *vdate, int period, int interval); /* Adjust the date by the interval period */ +int DATE_diff( GB_DATE *vdate1, GB_DATE *vdate2, int period); + +#endif diff --git a/main/m4 b/main/m4 new file mode 120000 index 00000000..7d49a2a4 --- /dev/null +++ b/main/m4 @@ -0,0 +1 @@ +../m4 \ No newline at end of file diff --git a/main/mime/application-x-gambas3.png b/main/mime/application-x-gambas3.png new file mode 100644 index 0000000000000000000000000000000000000000..b06a649087f16ccfb4a525a890f9bc7b64addf43 GIT binary patch literal 7788 zcmaKRcTiJ((C!HVLK8xf8oCG~RX}=gqErzmLBrkG$QL#p6d-D%40g z*K{9C$mnXjA3m+uiLbfH(jK|d8GL7bN48Vd8=9*f?<~L>%kiPNb4vW0xU=e10NQKp zaw1^rfgo~w$?Wv};wP20(XtG(HAF8rV6Wu|f!cpx-+6m5#NMTwGgeH^r?*z={0_I&#T46HD67?fb42tE`epavTF7bglt z6U2j*=&UJ#BESxS5;21Rg_NQ2I5xbl{}1M0H6AxP3=nmY|J3s@=6|VKe`JlfNA<6* z|HS-{J5sdCe5iTw|D*Q5^!yLzFRRV47^Uuapk3;)T2=Z&wG&lRG6Q~cgD{bDsImkX z32B6!T`_~x#PGCDxO4(_39NYZmiCmU$oQky4wk(7$yUM#)4XuWUPDHJ%~SXLDyuV> z0uA>;3@nk18gqC~#yK4W<%bdx*3zoGva#Ff+C>WW!x|xCqsk^Xn;ZD8kU~8TgC*djs0@6+$wRpGh87!AHC$&*IdZm>OCw)9ne{4tgD0cdZ{4>M zM0+HPT#k%i6o|4_KfB3yPptqAPy8VQnAaL4qG$_S_}>0$z%AS-X}8lswRy^I{$y?b zVwu`tNA%kvL=I*d-8x|Pi^K(){au;@At1x^a^**%^TAt*PnZ%E$t%gqxLS?GUeDiP z0RtAV?X+>~3CJ?p^1@RXPZG#I&-@}YU_nEgV3-RTP;_QiL2q-(6)3ou6i7X&E{x1x zk6tfg*oza8aSP#!1dX$SIeOEeXFkvF?j;Ef+?wdcY~fg5p#M)g>sD5aZ+e;iL4 z!6j$B=E3e6p?$GfolZgAY+haU!1lN}GJE2^7Z=s3COywl@F^HvAWw|w4W{n|;h$(L znnIJ26C3W+;RqrA5={`u-Ojl9JvlsYnSBN>`PB=bCj*jJSI>0@LQD+51^2?2)n}-OxU;!kss-tizUoT4yzZnvk14SqxhqY0yNU@e3665G3KXm-ggA7) z>q3Sk>b}s0fKi|tlc~A~HI1$pVpBY57&obYvFg4oR@NP z5WU%&u`QgheS!r2GP&@x&RX6l@;ab*DubbGq_4qhR z9!`kFyf;pMIU<0%?@b0VzmX{ z6`IqnNDQC<`mSC=K-GU0?tE++{h0fcbCu{>)VNiVIbCz>a#!8c_4JF4_^w1^toAcX zONvqL05fjw7xYU^S2n=A)#b{}TYg%yUd0QE`+duBT%X&a%GRAr8Sez%>Ra>H2|Yqa zX%j^1{M7way^^*G60<3oguXJD4*AX=;GO}DrbgO-%!)DW_cZgjr*2aFb#G=VF8RH6 zdKQ^o_(hB{8ii#VnY$6l4Yi!>=ZSk)&Yt{i6K=Atqc_{^sUL^Kdu+bW)ww24?}EI; zLCHl_8;m&~wYPD#7qs+We+ZkjSSI!;MsD6{&&{5G8n_q?Nh!I#Oo#QkWV+2oUaOqw zS=r$hF4P0&-#kMtIL|(kyPmh>|HUa>8nboc89}8BZdKf=EUuZWii0ioF7944v3HZ% zm)MTz`_hviMH6!^SZ5}xjti%;-oqfc_%nw2vgX2AbSTHGa?~i=Isvvm|N9AqT#;-! zIzRGch5J~Knka@rHGNImxv6-#5GnZ@eWfc3Jgn79bhnq4 zT+9|7%h|%#b$D7&hFSkm`#5ar=qJgn^|)nd&{C9See~d6mtVR-k6qylv}Nx;A={N- zVia)eOjud~<9ZK3mL2CRbSgxE+DlJI3pvYqXhplvYt~T*LJb@zn2& z0W!V7z3$)?+)JF8k!WOAL#{$F#iyF_-?2GM&!swSbOxe4wX?5yVT{EKJP@8EIii@E zS#g0_fx-LEPaoF^RDC36HQg$WFdd=`%m~a zvJAJLZ<_}M)z|?y%D%l~@-d(t6bULB{&D1o%?Wb6$mCCpS$gie{?R3%CX8l+stZ}N zaym;lSMvlwe7cBU)WM1qE4?nK)(T+h)p?N}7btT_iX0@;m!l&Klta5Ir5V{jO+

    c-FORp_J~q1K-NzK{^gnMl;~Fx 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 + + + + Gambas 3 executable + Exécutable Gambas 3 + + + + + + \ No newline at end of file diff --git a/main/reconf b/main/reconf new file mode 120000 index 00000000..48a376da --- /dev/null +++ b/main/reconf @@ -0,0 +1 @@ +../reconf \ No newline at end of file diff --git a/main/share/Makefile.am b/main/share/Makefile.am new file mode 100644 index 00000000..379c0ba7 --- /dev/null +++ b/main/share/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = *.h diff --git a/main/share/gambas.h b/main/share/gambas.h new file mode 100644 index 00000000..0df2610e --- /dev/null +++ b/main/share/gambas.h @@ -0,0 +1,1256 @@ +/*************************************************************************** + + gambas.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GAMBAS_H +#define __GAMBAS_H + +#ifdef __CYGWIN__ +#include +#endif + +#ifndef NO_CONFIG_H +#include "config.h" +#endif +#include +#include +#include + +/* Gambas API Version */ + +#define GB_VERSION 1 + + +/* Useful macros */ + +#ifndef CLEAR +#define CLEAR(s) (memset(s, 0, sizeof(*s))) +#endif + +#ifndef offsetof +#define offsetof(_type, _arg) ((size_t)&(((_type *)0)->_arg)) +#endif + +/* The following symbols must be declared with EXPORT in a component: + - GB + - GB_INIT() + - GB_EXIT() + - GB_CLASSES + - The component interface if present +*/ + +#ifdef EXPORT +#undef EXPORT +#endif +#ifdef HAVE_GCC_VISIBILITY +#define EXPORT __attribute__((visibility("default"))) +#else +#define EXPORT +#endif + +#if !defined(__cplusplus) + #ifdef bool + #undef bool + #endif + #define bool char +#endif + +#ifndef PACKED +#define PACKED __attribute__((packed)) +#endif + +/* Gambas datatypes identifiers */ + +#define GB_T_VOID 0 +#define GB_T_BOOLEAN 1 +#define GB_T_BYTE 2 +#define GB_T_SHORT 3 +#define GB_T_INTEGER 4 +#define GB_T_LONG 5 +#define GB_T_SINGLE 6 +#define GB_T_FLOAT 7 +#define GB_T_DATE 8 +#define GB_T_STRING 9 +#define GB_T_CSTRING 10 +#define GB_T_POINTER 11 +#define GB_T_VARIANT 12 +#define GB_T_FUNCTION 13 +#define GB_T_CLASS 14 +#define GB_T_NULL 15 +#define GB_T_OBJECT 16 + + +/* This type represents a Gambas datatype identifier */ + +typedef + uintptr_t GB_TYPE; + + +/* This opaque type represents a Gambas class identifier */ + +typedef + GB_TYPE GB_CLASS; + + +/* This structure represents the base of every Gambas object. + It must be placed in the beginning of all object structure defined + in a component. +*/ + +typedef + struct { + GB_CLASS klass; + intptr_t ref; + } + GB_BASE; + + +/* Gambas STRING datatype definition */ + +typedef + struct { + GB_TYPE type; + struct { + char *addr; + int start; + int len; + } value; + #if __WORDSIZE == 64 + intptr_t _reserved; + #endif + } + GB_STRING; + + +/* Gambas INTEGER datatype definition */ + +typedef + struct { + GB_TYPE type; + int value; + #if __WORDSIZE == 64 + int _pad; + #endif + intptr_t _reserved[2]; + } + GB_INTEGER; + + +/* Gambas LONG datatype definition */ + +typedef + struct { + GB_TYPE type; + #if __WORDSIZE == 32 + int _pad; + #endif + int64_t value; + #if __WORDSIZE == 64 + intptr_t _reserved[2]; + #endif + } + GB_LONG; + + +/* Gambas POINTER datatype definition */ + +typedef + struct { + GB_TYPE type; + intptr_t value; + intptr_t _reserved[2]; + } + GB_POINTER; + + +/* Gambas BOOLEAN datatype definition */ + +typedef + struct { + GB_TYPE type; + int value; + #if __WORDSIZE == 64 + int _pad; + #endif + intptr_t _reserved[2]; + } + GB_BOOLEAN; + + +/* Gambas SINGLE datatype definition */ + +typedef + struct { + GB_TYPE type; + float value; + #if __WORDSIZE == 64 + float _pad; + #endif + intptr_t _reserved[2]; + } + GB_SINGLE; + + +/* Gambas FLOAT datatype definition */ + +typedef + struct { + GB_TYPE type; + #if __WORDSIZE == 32 + int _pad; + #endif + double value; + #if __WORDSIZE == 64 + intptr_t _reserved[2]; + #endif + } + GB_FLOAT; + + +/* Gambas DATE datatype definition */ + +typedef + struct { + int date; + int time; + } + GB_DATE_VALUE; + +typedef + struct { + GB_TYPE type; + GB_DATE_VALUE value; + #if __WORDSIZE == 64 + intptr_t _reserved[2]; + #else + int _reserved; + #endif + } + GB_DATE; + + +/* Gambas OBJECT datatype definition */ + +typedef + struct { + GB_TYPE type; + void *value; + intptr_t _reserved[2]; + } + GB_OBJECT; + + +/* Gambas VARIANT datatype definition */ + +typedef + struct { + GB_TYPE type; + union { + char _boolean; + unsigned char _byte; + short _short; + int _integer; + int64_t _long; + float _single; + double _float; + GB_DATE_VALUE _date; + char *_string; + intptr_t _pointer; + void *_object; + int64_t data; + } + value; + } + PACKED + GB_VARIANT_VALUE; + +typedef + struct { + GB_TYPE type; + GB_VARIANT_VALUE value; + #if __WORDSIZE == 64 + int64_t _pad; + #endif + } + GB_VARIANT; + + +/* Gambas common value definition */ + +typedef + union { + GB_TYPE type; + GB_BOOLEAN _boolean; + GB_INTEGER _integer; + GB_LONG _long; + GB_SINGLE _single; + GB_FLOAT _float; + GB_DATE _date; + GB_STRING _string; + GB_POINTER _pointer; + GB_OBJECT _object; + GB_VARIANT _variant; + } + GB_VALUE; + + +/* Predefined errors constants */ + +#define GB_ERR_TYPE ((char *)6) +#define GB_ERR_OVERFLOW ((char *)7) +#define GB_ERR_NSYMBOL ((char *)11) +#define GB_ERR_NOBJECT ((char *)12) +#define GB_ERR_NWRITE ((char *)16) +#define GB_ERR_NPROPERTY ((char *)17) +#define GB_ERR_ARG ((char *)20) +#define GB_ERR_BOUND ((char *)21) +#define GB_ERR_ZERO ((char *)26) + + +/* Gambas description start macro */ + +#define GB_DECLARE(name, size) \ + { name, (intptr_t)GB_VERSION, (intptr_t)size } + + +/* Gambas description end macro */ + +#define GB_END_DECLARE { (char *)0 } + + +/* Special description identifiers */ + +#define GB_VIRTUAL_CLASS_ID ((char *)1) +#define GB_HOOK_CHECK_ID ((char *)2) +#define GB_NOT_CREATABLE_ID ((char *)3) +#define GB_AUTO_CREATABLE_ID ((char *)4) +#define GB_INHERITS_ID ((char *)5) + + +/* Description hook macros */ + +//#define GB_HOOK_NEW(hook) { GB_HOOK_NEW_ID, (int)hook } +//#define GB_HOOK_FREE(hook) { GB_HOOK_FREE_ID, (int)hook } +#define GB_HOOK_CHECK(hook) { GB_HOOK_CHECK_ID, (intptr_t)hook } + + +/* Virtual class description macro */ + +#define GB_VIRTUAL_CLASS() { GB_VIRTUAL_CLASS_ID }, { GB_NOT_CREATABLE_ID } + +#define GB_DECLARE_VIRTUAL(name) \ + { name, (intptr_t)GB_VERSION, (intptr_t)0 }, GB_VIRTUAL_CLASS() + + +/* Not creatable class macro */ + +#define GB_NOT_CREATABLE() { GB_NOT_CREATABLE_ID } + +#define GB_DECLARE_STATIC(name) \ + { name, (intptr_t)GB_VERSION, (intptr_t)0 }, GB_NOT_CREATABLE() + + +/* Auto creatable class macro */ + +#define GB_AUTO_CREATABLE() { GB_AUTO_CREATABLE_ID } + + +/* Symbol description prefixes */ + +#define GB_PROPERTY_ID 'p' +#define GB_METHOD_ID 'm' +#define GB_CONSTANT_ID 'C' +#define GB_EVENT_ID ':' +#define GB_ENUM_ID '#' +#define GB_STATIC_PROPERTY_ID 'P' +#define GB_STATIC_METHOD_ID 'M' + + +/* Symbol description macros */ + +#define GB_CONSTANT(symbol, type, value) \ + { "C" symbol, (intptr_t)type, (intptr_t)value } + +#define GB_FLOAT_CONSTANT(symbol, value) \ + { "C" symbol, (intptr_t)"f", (intptr_t)0, (intptr_t)0, (double)value } + +#define GB_PROPERTY(symbol, type, proc) \ + { "p" symbol, (intptr_t)type, (intptr_t)proc } + +#define GB_PROPERTY_READ(symbol, type, proc) \ + { "r" symbol, (intptr_t)type, (intptr_t)proc } + +#define GB_PROPERTY_SELF(symbol, type) \ + { "r" symbol, (intptr_t)type, (intptr_t)(-1) } + +#define GB_METHOD(symbol, type, exec, signature) \ + { "m" symbol, (intptr_t)type, (intptr_t)exec, (intptr_t)signature } + +#define GB_EVENT(symbol, type, signature, id) \ + { "::" symbol, (intptr_t)type, (intptr_t)id, (intptr_t)signature } + +#define GB_STATIC_PROPERTY(symbol, type, proc) \ + { "P" symbol, (intptr_t)type, (intptr_t)proc } + +#define GB_STATIC_PROPERTY_READ(symbol, type, proc) \ + { "R" symbol, (intptr_t)type, (intptr_t)proc } + +#define GB_STATIC_PROPERTY_SELF(symbol, type) \ + { "R" symbol, (intptr_t)type, (-1) } + +#define GB_STATIC_METHOD(symbol, type, exec, signature) \ + { "M" symbol, (intptr_t)type, (intptr_t)exec, (intptr_t)signature } + +#define GB_STATIC_FAST_METHOD(symbol, type, exec, signature) \ + { "M!" symbol, (intptr_t)type, (intptr_t)exec, (intptr_t)signature } + +#define GB_INHERITS(symbol) \ + { GB_INHERITS_ID, (intptr_t)symbol } + +#define GB_INTERFACE(symbol, pointer) \ + { "C_@" symbol, (intptr_t)"p", (intptr_t)pointer } + + +/* Method implementation begin macro */ + +#define BEGIN_METHOD(_name, par) \ +typedef \ + struct { \ + par; \ + } \ + _##_name; \ +\ +void _name(void *_object, void *_param) \ +{ \ +_##_name *_p = (_##_name *)_param; + + +/* Parameter-less Method implementation begin macro */ + +#define BEGIN_METHOD_VOID(_name) \ +void _name(void *_object, void *_param) { \ + + +/* Parameter access macro */ + +#define ARG(_name) (&(_p)->_name) + + +/* Testing if a argument is missing */ + +#define MISSING(_name) ((_p)->_name.type == GB_T_VOID) + + +/* Method implementation end macro */ + +#define END_METHOD } + + +/* Macro used for calling a parameter-less implementation method */ + +#define CALL_METHOD_VOID(_name) _name(_object, NULL) + + +/* Property implementation begin macro */ + +#define BEGIN_PROPERTY(_name) \ +void _name(void *_object, void *_param) { + + +/* Macro indicating if the property implementation is called for reading or writing */ + +#define READ_PROPERTY (_param == NULL) + + +/* Macro to get the value written to a property */ + +#define PROP(_type) ((_type *)_param) + + +/* Property implementation end macro */ + +#define END_PROPERTY } + + +/* Macros to get the value of an argument or a property */ + +#define VALUE(_arg) ((_arg)->value) +#define VARG(_p) VALUE(ARG(_p)) +#define VPROP(_p) VALUE(PROP(_p)) + + +/* Macros to get a string argument */ + +#define STRING(_arg) (VARG(_arg).addr + VARG(_arg).start) +#define LENGTH(_arg) (VARG(_arg).len) + + +/* Macros to get a string property */ + +#define PSTRING() (VPROP(GB_STRING).addr + VPROP(GB_STRING).start) +#define PLENGTH() (VPROP(GB_STRING).len) + + +/* Macro to get an optional argument */ + +#define VARGOPT(_arg, _default) (MISSING(_arg) ? (_default) : VARG(_arg)) + + +/* Casting macro. Usable only in an implementation function */ + +#define OBJECT(type) ((type *)_object) + + +/* Macro for returning itself. Usable only in an implementation function */ + +#define RETURN_SELF() GB.ReturnSelf(_object) + + +/* Macro for declaring a variable used for storing an event identifier */ + +#define DECLARE_EVENT(_event) static int _event + + +/* Macro to help accessing enumeration index. Use only in an enumeration method implementation */ + +#define ENUM(_type) (*((_type *)GB.GetEnum())) + + +/* Structure used for describing a class */ + +typedef + struct { + const char *name; + intptr_t val1; + intptr_t val2; + intptr_t val3; + #if __WORDSIZE == 64 + double val4; + intptr_t val5; + #else + double val4; + #endif + } + GB_DESC; + + +/* Type of a method implementation function */ + +typedef + void GB_METHOD_FUNC(void *, void *); + + +/* Type of a property implementation function */ + +typedef + void GB_PROPERTY_FUNC(void *, void *); + + +/* Macro for declaring a method implementation function */ + +#define DECLARE_METHOD(_method) GB_METHOD_FUNC _method + + +/* Macro for declaring a property implementation function */ + +#define DECLARE_PROPERTY(_property) GB_PROPERTY_FUNC _property + + +/* Constants used with the GB.Hook() API function */ + +#define GB_HOOK_MAX 10 + +#define GB_HOOK_MAIN 1 +#define GB_HOOK_LOOP 2 +#define GB_HOOK_WAIT 3 +#define GB_HOOK_TIMER 4 +#define GB_HOOK_LANG 5 +#define GB_HOOK_WATCH 6 +#define GB_HOOK_POST 7 +#define GB_HOOK_QUIT 8 +#define GB_HOOK_ERROR 9 +#define GB_HOOK_TIMEOUT 10 + +/* Macro for calling the previous hook */ + +#define CALL_HOOK_MAIN(_hook, _pargc, _pargv) do { if (_hook) { ((void (*)(int *, char ***))(_hook))((_pargc), (_pargv)); } } while (0); + +/* Constants that represent interpreter signals caught by GB_SIGNAL function */ + +#define GB_SIGNAL_DEBUG_BREAK 1 +#define GB_SIGNAL_DEBUG_CONTINUE 2 +#define GB_SIGNAL_DEBUG_FORWARD 3 + + +/* Constants used with the GB.Watch() API function */ + +#define GB_WATCH_NONE 0 +#define GB_WATCH_READ 1 +#define GB_WATCH_WRITE 2 + + +/* Type of a generic callback */ + +typedef + void (*GB_CALLBACK)(); + + +/* Type of a watch callback function */ + +typedef + void (*GB_WATCH_CALLBACK)(int, int, intptr_t); + + +/* Type of the GB.SubstString() callback */ + +typedef + void (*GB_SUBST_CALLBACK)(int, char **, int *); + + +/* Type of the GB.SubstStringAdd() callback */ + +typedef + void (*GB_SUBST_ADD_CALLBACK)(int); + + +/* Type of the GB.BrowseProject() callback */ + +typedef + void (*GB_BROWSE_PROJECT_CALLBACK)(const char *, int64_t); + + +/* Type of the GB.BrowseDirectory() callback */ + +typedef + void (*GB_BROWSE_CALLBACK)(const char *); + + +/* Type of a timer callback */ + +typedef + int (*GB_TIMER_CALLBACK)(intptr_t); + + +/* Type of a signal callback */ + +typedef + void (*GB_SIGNAL_CALLBACK)(int, intptr_t); + + +/* A structure for the components of a date */ + +typedef + struct { + int year; + int month; + int day; + int hour; + int min; + int sec; + int weekday; + int msec; + } + GB_DATE_SERIAL; + + +/* Opaque type of a Gambas interpreted or native function */ + +typedef + struct { + void *object; + void *desc; + } + GB_FUNCTION; + +#define GB_FUNCTION_IS_VALID(_func) ((_func)->desc) + + +/* Opaque type of a Gambas Array */ + +typedef + void *GB_ARRAY; + +typedef + struct { + int size; + int count; + GB_TYPE type; + void *data; + int *dim; + void *ref; + } + GB_ARRAY_BASE; + + +/* Opaque type of a Gambas Collection */ + +typedef + void *GB_COLLECTION; + + +/* Opaque type of a Gambas Collection iterator */ + +typedef + struct { + void *iter1; + void *iter2; + } + GB_COLLECTION_ITER; + + +/* opaque type of an hash table */ + +typedef + void *GB_HASHTABLE; + + +/* hash table enumeration function */ + +typedef + void (*GB_HASHTABLE_ENUM_FUNC)(void *); + + +/* Constants for end-of-line format */ + +#define GB_EOL_UNIX 0 +#define GB_EOL_WINDOWS 1 +#define GB_EOL_MAC 2 + + +/* opaque type for a Stream object */ + +struct GB_STREAM; + +typedef + struct { + int (*open)(struct GB_STREAM *stream, const char *path, int mode, void *data); + int (*close)(struct GB_STREAM *stream); + int (*read)(struct GB_STREAM *stream, char *buffer, int len); + int (*write)(struct GB_STREAM *stream, char *buffer, int len); + int (*seek)(struct GB_STREAM *stream, int64_t pos, int whence); + int (*tell)(struct GB_STREAM *stream, int64_t *pos); + int (*flush)(struct GB_STREAM *stream); + int (*eof)(struct GB_STREAM *stream); + int (*lof)(struct GB_STREAM *stream, int64_t *len); + int (*handle)(struct GB_STREAM *stream); + } + GB_STREAM_DESC; + +typedef + struct { + GB_STREAM_DESC *desc; + int64_t _reserved; + intptr_t _reserved2; + intptr_t _reserved3; + void *tag; + } + GB_STREAM_BASE; + +typedef + struct GB_STREAM { + GB_STREAM_DESC *desc; + int64_t _reserved; + intptr_t _reserved2; + intptr_t _reserved3; + void *tag; + #if __WORDSIZE == 64 + int _free[4]; + #else + int _free[5]; + #endif + GB_VARIANT_VALUE _reserved4; + } + GB_STREAM; + + +/* Constants used by the GB.NumberFromString() API function */ + +#define GB_NB_READ_INTEGER 1 +#define GB_NB_READ_LONG 2 +#define GB_NB_READ_INT_LONG 3 +#define GB_NB_READ_FLOAT 4 +#define GB_NB_READ_ALL 7 +#define GB_NB_READ_HEX_BIN 8 +#define GB_NB_LOCAL 16 + + +/* Constants used by the GB.Collection.New() and GB.HashTable.New() API function */ + +#define GB_COMP_BINARY 0 +#define GB_COMP_NOCASE 1 + + +/* Constant used by GB.ConvString to convert to 32 bits Unicode (that needs some special processing) */ + +#define GB_SC_UNICODE ((char *)-1) + + +/* Timer object */ + +typedef + struct { + GB_BASE object; + intptr_t id; + intptr_t tag; + unsigned delay : 31; + unsigned triggered : 1; + #if __WORDSIZE == 64 + int _pad; + #endif + GB_TIMER_CALLBACK callback; + } + GB_TIMER; + +/* Structure for GB.OnErrorBegin() handler */ + +typedef + struct { + void *prev; + void *context; + GB_CALLBACK handler; + intptr_t arg1; + intptr_t arg2; + } + GB_ERROR_HANDLER; + +/* Structure for GB.RaiseBegin handler */ + +typedef + struct { + void (*callback)(intptr_t); + intptr_t data; + void *old; + int level; + } + GB_RAISE_HANDLER; + +/* A macro for preventing gcc from warning about breaks in the + strict aliasing rules */ + +#define POINTER(_pointer) (void **)(void *)_pointer + +/* For classes that implements arithmetic operators (e.g. complex numbers...) */ + +typedef + struct { + int (*equal)(void *, void *, bool); + int (*equalf)(void *, double); + int (*equalo)(void *, void *, bool); + int (*comp)(void *, void *, bool); + int (*compf)(void *, double); + int (*compo)(void *, void *, bool); + void *(*add)(void *, void *, bool); + void *(*addf)(void *, double, bool); + void *(*addo)(void *, void *, bool); + void *(*sub)(void *, void *, bool); + void *(*subf)(void *, double, bool); + void *(*subo)(void *, void *, bool); + void *(*mul)(void *, void *, bool); + void *(*mulf)(void *, double, bool); + void *(*mulo)(void *, void *, bool); + void *(*div)(void *, void *, bool); + void *(*divf)(void *, double, bool); + void *(*divo)(void *, void *, bool); + void *(*pow)(void *, void *, bool); + void *(*powf)(void *, double, bool); + void *(*powo)(void *, void *, bool); + void *(*neg)(void *); + void *(*abs)(void *); + double (*fabs)(void *); + int (*sgn)(void *); + intptr_t _reserved; + } + GB_OPERATOR_DESC; + +/* Double-linked list API */ + +typedef + struct { + void *next; + void *prev; + } + GB_LIST; + +/* Information about a file */ + +typedef + struct { + short type; + short mode; + int atime; + int mtime; + int ctime; + int64_t size; + uid_t uid; + gid_t gid; + char hidden; + } + GB_FILE_STAT; + +/* Constants for the GB_FILE_STAT structure */ + +#define GB_STAT_FILE 1 +#define GB_STAT_DIRECTORY 2 +#define GB_STAT_DEVICE 3 +#define GB_STAT_PIPE 4 +#define GB_STAT_SOCKET 5 +#define GB_STAT_LINK 6 + +/* Gambas Application Programming Interface */ + +typedef + struct { + intptr_t version; + + bool (*GetInterface)(const char *, int, void *); + + void *(*Hook)(int, void *); + + struct { + bool (*Load)(const char *); + bool (*CanLoadLibrary)(const char *); + bool (*Exist)(const char *); + bool (*IsLoaded)(const char *); + char *(*Current)(void); + bool (*GetInfo)(const char *, void **); + void (*Signal)(int, void *); + void (*Declare)(GB_DESC *); + } + Component; + + void (*Push)(int, ...); + bool (*GetFunction)(GB_FUNCTION *, void *, const char *, const char *, const char *); + GB_VALUE *(*Call)(GB_FUNCTION *, int, int); + void *(*GetClassInterface)(GB_CLASS, const char *); + void *(*GetProperty)(void *, const char *); + bool (*SetProperty)(void *, const char *, GB_VALUE *value); + bool (*Serialize)(const char *path, GB_VALUE *value); + bool (*UnSerialize)(const char *path, GB_VALUE *value); + + bool (*Loop)(int); + void (*Wait)(int); + void (*Post)(GB_CALLBACK, intptr_t); + void (*Post2)(GB_CALLBACK, intptr_t, intptr_t); + GB_TIMER *(*Every)(int, GB_TIMER_CALLBACK, intptr_t); + bool (*Raise)(void *, int, int, ...); + void (*RaiseBegin)(GB_RAISE_HANDLER *handler); + void (*RaiseEnd)(GB_RAISE_HANDLER *handler); + void (*RaiseLater)(void *, int); + void (*CheckPost)(void); + bool (*CanRaise)(void *, int); + int (*GetEvent)(GB_CLASS, const char *); + char *(*GetLastEventName)(void); + void (*RaiseTimer)(void *); + bool (*Stopped)(void); + + int (*NParam)(void); + bool (*Conv)(GB_VALUE *, GB_TYPE); + char *(*GetUnknown)(void); + + void (*Error)(const char *, ...); + void (*Propagate)(void); + void (*Deprecated)(const char *, const char *, const char *); + void (*OnErrorBegin)(GB_ERROR_HANDLER *); + void (*OnErrorEnd)(GB_ERROR_HANDLER *); + + GB_CLASS (*GetClass)(void *); + char *(*GetClassName)(void *); + bool (*ExistClass)(const char *); + GB_CLASS (*FindClass)(const char *); + bool (*ExistClassLocal)(const char *); + GB_CLASS (*FindClassLocal)(const char *); + GB_TYPE (*GetArrayType)(GB_CLASS); + GB_CLASS (*GetArrayClass)(GB_CLASS); + GB_CLASS (*LoadClassFrom)(const char *, const char*); + bool (*Is)(void *, GB_CLASS); + void (*Ref)(void *); + void (*Unref)(void **); + //void (*UnrefKeep)(void **, int); + void (*Detach)(void *); + void (*Attach)(void *, void *, const char *); + void *(*Parent)(void *); + void *(*Create)(GB_CLASS, char *, void *); + void *(*New)(GB_CLASS, char *, void *); + void *(*AutoCreate)(GB_CLASS, int); + bool (*CheckObject)(void *); + + void *(*GetEnum)(void); + void (*StopEnum)(void); + void *(*BeginEnum)(void *); + void (*EndEnum)(void *); + bool (*NextEnum)(void); + void (*StopAllEnum)(void *); + + GB_VALUE *(*GetReturnValue)(void); + void (*Return)(GB_TYPE, ...); + void (*ReturnInteger)(int); + void (*ReturnLong)(int64_t); + void (*ReturnPointer)(void *); + void (*ReturnBoolean)(int); + void (*ReturnDate)(GB_DATE *); + void (*ReturnObject)(void *); + void (*ReturnNull)(void); + void (*ReturnSingle)(float); + void (*ReturnFloat)(double); + void (*ReturnVariant)(GB_VARIANT_VALUE *); + void (*ReturnConvVariant)(void); + void (*ReturnBorrow)(void); + void (*ReturnRelease)(void); + void (*ReturnPtr)(GB_TYPE, void *); + void (*ReturnSelf)(void *); + + void (*ReturnString)(char *); + void (*ReturnVoidString)(void); + void (*ReturnConstString)(const char *, int); + void (*ReturnConstZeroString)(const char *); + void (*ReturnNewString)(const char *, int); + void (*ReturnNewZeroString)(const char *); + + char *(*NewString)(const char *, int); + char *(*NewZeroString)(const char *); + char *(*TempString)(const char *, int); + char *(*RefString)(char *); + void (*FreeString)(char **); + char *(*FreeStringLater)(char *); + char *(*ExtendString)(char *, int); + char *(*AddString)(char *, const char *, int); + char *(*AddChar)(char *, char); + int (*StringLength)(char *); + char *(*ToZeroString)(GB_STRING *); + bool (*MatchString)(const char *, int, const char *, int); + bool (*NumberFromString)(int, const char *, int, GB_VALUE *); + bool (*NumberToString)(int, double, const char *, char **, int *); + char *(*Translate)(const char *); + + char *(*SubstString)(const char *, int, GB_SUBST_CALLBACK); + char *(*SubstStringAdd)(const char *, int, GB_SUBST_ADD_CALLBACK); + void (*SubstAddCallback)(const char *, int); + bool (*ConvString)(char **, const char *, int, const char *, const char *); + char *(*FileName)(char *, int); + char *(*RealFileName)(char *, int); + + bool (*LoadFile)(const char *, int, char **, int *); + void (*ReleaseFile)(char *, int); + char *(*TempDir)(void); + char *(*TempFile)(const char *); + bool (*CopyFile)(const char *, const char *); + void (*BrowseProject)(GB_BROWSE_PROJECT_CALLBACK); + void (*BrowseDirectory)(const char *, GB_BROWSE_CALLBACK, GB_BROWSE_CALLBACK); + bool (*StatFile)(const char *, GB_FILE_STAT *, bool); + + void (*Store)(GB_TYPE, GB_VALUE *, void *); + void (*StoreString)(GB_STRING *, char **); + void (*StoreObject)(GB_OBJECT *, void **); + void (*StoreVariant)(GB_VARIANT *, void *); + void (*ReadValue)(GB_VALUE *, void *, GB_TYPE); + void (*BorrowValue)(GB_VALUE *); + void (*ReleaseValue)(GB_VALUE *); + int (*CompVariant)(GB_VARIANT_VALUE *, GB_VARIANT_VALUE *); + + GB_DATE_SERIAL *(*SplitDate)(GB_DATE *); + bool (*MakeDate)(GB_DATE_SERIAL *, GB_DATE *); + void (*MakeDateFromTime)(int, int, GB_DATE *); + bool (*GetTime)(double *, int); + + void (*Watch)(int, int, void *, intptr_t); + + GB_VALUE *(*Eval)(void *, void *); + + void (*Alloc)(void **, int); + void (*AllocZero)(void **, int); + void (*Free)(void **); + void (*Realloc)(void **, int); + + void (*NewArray)(void *, int, int); + void (*FreeArray)(void *); + int (*Count)(void *); + void *(*Add)(void *); + void *(*Insert)(void *, int, int); + void (*Remove)(void *, int, int); + + int (*ToLower)(int); + int (*ToUpper)(int); + int (*StrCaseCmp)(const char *, const char *); + int (*StrNCaseCmp)(const char *, const char *, int); + + struct { + char *(*Name)(void); + char *(*Title)(void); + char *(*Version)(void); + char *(*Path)(void); + GB_CLASS (*StartupClass)(void); + } + Application; + + struct { + char *(*Charset)(void); + char *(*Language)(void); + void (*SetLanguage)(const char *); + char *(*DomainName)(void); + bool (*IsRightToLeft)(void); + char *(*Path)(void); + void (*HasForked)(void); + bool (*Debug)(void); + char *(*Home)(void); + int (*TimeZone)(void); + } + System; + + struct { + void (*New)(GB_ARRAY *, GB_TYPE, int); + int (*Count)(GB_ARRAY); + void *(*Add)(GB_ARRAY); + void *(*Get)(GB_ARRAY, int); + GB_TYPE (*Type)(GB_ARRAY); + } + Array; + + struct { + void (*New)(GB_COLLECTION *, int); + int (*Count)(GB_COLLECTION); + bool (*Set)(GB_COLLECTION, const char *, int, GB_VARIANT *); + bool (*Get)(GB_COLLECTION, const char *, int, GB_VARIANT *); + bool (*Enum)(GB_COLLECTION, GB_COLLECTION_ITER *, GB_VARIANT *, char **key, int *len); + } + Collection; + + struct { + void (*New)(GB_HASHTABLE *, int); + void (*Free)(GB_HASHTABLE *); + int (*Count)(GB_HASHTABLE); + void (*Add)(GB_HASHTABLE, const char *, int, void *); + void (*Remove)(GB_HASHTABLE, const char *, int); + bool (*Get)(GB_HASHTABLE, const char *, int, void **); + void (*Enum)(GB_HASHTABLE, GB_HASHTABLE_ENUM_FUNC); + bool (*First)(GB_HASHTABLE, void **); + } + HashTable; + + struct { + GB_STREAM *(*Get)(void *object); + void (*SetSwapping)(GB_STREAM *stream, int swap); + void (*SetAvailableNow)(GB_STREAM *stream, int available_now); + bool (*Block)(GB_STREAM *stream, int block); + int (*Read)(GB_STREAM *stream, void *addr, int len); + int (*Write)(GB_STREAM *stream, void *addr, int len); + int (*GetReadable)(GB_STREAM *stream, int *len); + bool (*Eof)(GB_STREAM *stream); + int (*Handle)(GB_STREAM *stream); + } + Stream; + + struct { + void (*Start)(int length); + char *(*End)(void); + void (*Add)(const char *src, int len); + } + String; + + struct { + char *(*GetCurrentPosition)(void); + void (*EnterEventLoop)(void); + void (*LeaveEventLoop)(void); + } + Debug; + + struct { + GB_SIGNAL_CALLBACK *(*Register)(int signum, void (*func)(int, intptr_t), intptr_t data); + void (*Unregister)(int signum, GB_SIGNAL_CALLBACK *cb); + } + Signal; + + struct { + void (*Add)(void *p_first, void *node, GB_LIST *list); + void (*Remove)(void *p_first, void *node, GB_LIST *list); + } + List; + + } + GB_INTERFACE; + + +/* + + Special methods that can be declared in a class + ----------------------------------------------- + + _get array reading operator + _put array writing operator + _new constructor + _free destructor + _next next iteration of an enumeration + _call called when the object or the class is used as a function + _unknown called when the name of the property or method is unknown + + + Syntax of a method or event signature + ------------------------------------- + + Gambas datatype String representation + + Boolean b + Byte c + Short h + Integer i + Long l + Single g + Float f + Date d + String s + Variant v + Object o + Pointer p + Any class ClassName; + +*/ + +#ifndef NO_GAMBAS_CASE_REPLACEMENT + +/* Replacements for case unsensitive comparisons. + They ensure that case comparison does not use current locale, + otherwise Turkish speakers will have problems! +*/ + +#include +#include + +#ifdef tolower +#undef tolower +#endif +#ifdef toupper +#undef toupper +#endif +#ifdef strcasecmp +#undef strcasecmp +#endif +#ifdef strncasecmp +#undef strncasecmp +#endif + +#define strcasecmp GB.StrCaseCmp +#define strncasecmp GB.StrNCaseCmp +#define toupper GB.ToUpper +#define tolower GB.ToLower + +#endif + +#endif diff --git a/main/share/gb_alloc.h b/main/share/gb_alloc.h new file mode 100644 index 00000000..a63c10e8 --- /dev/null +++ b/main/share/gb_alloc.h @@ -0,0 +1,117 @@ +/*************************************************************************** + + gb_alloc.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_ALLOC_H +#define __GB_ALLOC_H + +#include "gb_common.h" + +#define DEBUG_MEMORY 0 +#define OPTIMIZE_MEMORY 1 +//#define DO_NOT_PRINT_MEMORY + +#ifndef __GB_ALLOC_C +EXTERN int MEMORY_count; +#endif + +#define WHERE_AM_I MEMORY_where_am_i(__FILE__, __LINE__, __func__) + +#if DEBUG_MEMORY + +#undef OPTIMIZE_MEMORY +#define OPTIMIZE_MEMORY 0 + +typedef + struct ALLOC { + int _void; + struct ALLOC *next; + struct ALLOC *prev; + int id; + size_t size; + } + PACKED + ALLOC; + +EXTERN size_t MEMORY_size; +EXTERN FILE *MEMORY_log; + +#define ALLOC(_ptr, _size) MEMORY_alloc((void *)_ptr, _size, WHERE_AM_I) +#define ALLOC_ZERO(_ptr, _size) MEMORY_alloc_zero((void *)_ptr, _size, WHERE_AM_I) +#define REALLOC(_ptr, _size) MEMORY_realloc((void *)_ptr, _size, WHERE_AM_I) +#define FREE(_ptr) MEMORY_free((void *)_ptr, WHERE_AM_I) +#define IFREE(_ptr) FREE(&(_ptr)) + +#define GET_ALLOC_ID(_ptr) (((ALLOC *)((char *)(_ptr) - sizeof(ALLOC)))->id) + +char *MEMORY_where_am_i(const char *file, int line, const char *func); + +void MEMORY_alloc(void *p_ptr, size_t size, const char *src); +void MEMORY_alloc_zero(void *p_ptr, size_t size, const char *src); +void MEMORY_realloc(void *p_ptr, size_t size, const char *src); +void MEMORY_free(void *p_ptr, const char *src); +void MEMORY_check(void); + +void MEMORY_verify(void); +void MEMORY_check_ptr(void *ptr); + +#elif OPTIMIZE_MEMORY + +/*#define ALLOC(_ptr, _size, _src) (LIKELY((*(_ptr) = malloc(_size)) != 0) ? MEMORY_count++ : THROW_MEMORY()) +#define ALLOC_ZERO(_ptr, _size, _src) (LIKELY((*(_ptr) = calloc(_size, 1)) != 0) ? MEMORY_count++ : THROW_MEMORY()) +#define REALLOC(_ptr, _size, _src) (LIKELY((*(_ptr) = realloc(*(_ptr), _size)) != 0) ? 0 : THROW_MEMORY()) +#define FREE(_ptr, _src) (LIKELY(*(_ptr) != 0) ? free(*(_ptr)), *(_ptr) = NULL, MEMORY_count-- : 0) +#define IFREE(_ptr, _src) (LIKELY(_ptr != 0) ? free(_ptr), MEMORY_count-- : 0)*/ + +#define ALLOC(_ptr, _size) (*(_ptr) = my_malloc(_size)) +#define ALLOC_ZERO(_ptr, _size) (*(_ptr) = my_malloc(_size), memset(*(_ptr), 0, (_size))) +#define REALLOC(_ptr, _size) (*(_ptr) = my_realloc(*(_ptr), (_size))) +#define FREE(_ptr) (my_free(*(_ptr)), *(_ptr) = NULL) +#define IFREE(_ptr) (my_free(_ptr)) + +void *my_malloc(size_t len); +void my_free(void *alloc); +void *my_realloc(void *alloc, size_t len); + +#else + +#define ALLOC(_ptr, _size) MEMORY_alloc((void *)_ptr, _size) +#define ALLOC_ZERO(_ptr, _size) MEMORY_alloc_zero((void *)_ptr, _size) +#define REALLOC(_ptr, _size) MEMORY_realloc((void *)_ptr, _size) +#define FREE(_ptr) MEMORY_free((void *)_ptr) +#define IFREE(_ptr) FREE(&(_ptr)) + +void MEMORY_alloc(void *p_ptr, size_t size); +void MEMORY_alloc_zero(void *p_ptr, size_t size); +void MEMORY_realloc(void *p_ptr, size_t size); +void MEMORY_free(void *p_ptr); +void MEMORY_check(void); + +#endif + +void MEMORY_init(void); +void MEMORY_exit(void); +void MEMORY_clear_cache(void); +int THROW_MEMORY() NORETURN; + +#endif + diff --git a/main/share/gb_alloc_temp.h b/main/share/gb_alloc_temp.h new file mode 100644 index 00000000..0a2b4fdf --- /dev/null +++ b/main/share/gb_alloc_temp.h @@ -0,0 +1,493 @@ +/*************************************************************************** + + gb_alloc_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_ALLOC_C + +#include + +#ifdef HAVE_MALLOC_H +#include +#endif + +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_alloc.h" + +int MEMORY_count = 0; + +//#define DEBUG_ME +//#define DO_NOT_PRINT_MEMORY + +#if defined(DEBUG_ME) || DEBUG_MEMORY +size_t MEMORY_size = 0; +size_t MEMORY_pool_size = 0; +#endif + +#if DEBUG_MEMORY + +static int _id = 0; +ALLOC *_alloc = NULL; +#ifdef PROJECT_EXEC +extern char *DEBUG_get_current_position(void); +#endif +FILE *MEMORY_log; + +#elif OPTIMIZE_MEMORY + +#define SIZE_INC 16 +#define SIZE_SHIFT 4 +#define REAL_SIZE(_size) (((_size) + (SIZE_INC - 1)) & ~(SIZE_INC - 1)) + +#define POOL_SIZE 16 +#define POOL_MAX 128 + +#define POOL_MAX_LEN (POOL_SIZE * SIZE_INC) + +static size_t *_pool[POOL_SIZE] = { 0 }; +static int _pool_count[POOL_SIZE] = { 0 }; +/*static size_t *_first_alloc = 0; +static size_t *_max_alloc = 0;*/ + +#endif + +int THROW_MEMORY() +{ + THROW(E_MEMORY); +} + +void MEMORY_init(void) +{ + #if DEBUG_MEMORY + /*char path[256]; + sprintf(path, "/tmp/gambas-memory-%d-%d.log", getuid(), getpid()); + MEMORY_log = fopen(path, "w+");*/ + MEMORY_log = stderr; + #endif +} + +void MEMORY_clear_cache() +{ +#if OPTIMIZE_MEMORY + int i; + void *ptr, *next; + size_t size = 0; + + for (i = 0; i < POOL_SIZE; i++) + { + ptr = _pool[i]; + while (ptr) + { + next = *((void **)ptr); + size += (i + 1) * SIZE_INC; + #ifdef DEBUG_ME + MEMORY_size -= (i + 1) * SIZE_INC; + #endif + free(ptr); + ptr = next; + } + _pool[i] = NULL; + _pool_count[i] = 0; + } + + #ifdef DEBUG_ME + fprintf(stderr, "free %ld bytes [%ld / %p]\n", size, MEMORY_size, sbrk(0)); + #endif + +#endif +} + +void MEMORY_exit(void) +{ + MEMORY_clear_cache(); + +#if DEBUG_MEMORY + if (MEMORY_count) + { + fprintf(MEMORY_log, "\n*************************************************\n"); + fprintf(MEMORY_log, "warning: %d allocation(s) non freed.\n", MEMORY_count); + while (_alloc) + { + fprintf(MEMORY_log, "<%d>\n", _alloc->id); + _alloc = _alloc->next; + } + } + fclose(MEMORY_log); +#else + if (MEMORY_count) + ERROR_warning("%d allocation(s) non freed.\n", MEMORY_count); +#endif +} + +#if OPTIMIZE_MEMORY +#else + +#if DEBUG_MEMORY +char *MEMORY_where_am_i(const char *file, int line, const char *func) +{ + static char buffer[256]; + + snprintf(buffer, sizeof(buffer), "[%s] %s:%d", file, func, line); + return buffer; +} + +#endif + +#if DEBUG_MEMORY +void MEMORY_alloc(void *p_ptr, size_t size, const char *src) +{ + ALLOC *alloc; + + alloc = (ALLOC *)malloc(sizeof(ALLOC) + size); + if (!alloc) + THROW(E_MEMORY); + + _id++; + alloc->id = _id; + alloc->prev = NULL; + alloc->next = _alloc; + alloc->size = size; + + if (_alloc) + _alloc->prev = alloc; + + _alloc = alloc; + + *((void **)p_ptr) = (char *)alloc + sizeof(ALLOC); + MEMORY_count++; + MEMORY_size += size; + + #ifndef DO_NOT_PRINT_MEMORY + #ifdef PROJECT_EXEC + fprintf(MEMORY_log, "%s: ", DEBUG_get_current_position()); + #endif + fprintf(MEMORY_log, "<%d> %s: MEMORY_alloc(%d) -> %p\n", _id, src, (int)size, (char *)alloc + sizeof(ALLOC)); + fflush(MEMORY_log); + /*if (_id == 2700) + BREAKPOINT();*/ + #endif +} +#else +void MEMORY_alloc(void *p_ptr, size_t size) +{ + void *alloc; + + alloc = malloc(size); + + if (!alloc) + THROW(E_MEMORY); + + *((void **)p_ptr) = alloc; + MEMORY_count++; +} +#endif + + +#if DEBUG_MEMORY +void MEMORY_alloc_zero(void *p_ptr, size_t size, const char *src) +{ + MEMORY_alloc(p_ptr, size, src); + memset(*((void **)p_ptr), 0, size); +} +#else +void MEMORY_alloc_zero(void *p_ptr, size_t size) +{ + void *alloc; + + alloc = calloc(size, 1); + + if (!alloc) + THROW(E_MEMORY); + + *((void **)p_ptr) = alloc; + MEMORY_count++; +} +#endif + + +#if DEBUG_MEMORY +void MEMORY_realloc(void *p_ptr, size_t size, const char *src) +{ + ALLOC *alloc = (ALLOC *)(*((char **)p_ptr) - sizeof(ALLOC)); + ALLOC *old = alloc; + + if (size == 0) + { + MEMORY_free(p_ptr, src); + return; + } + + alloc = realloc(alloc, sizeof(ALLOC) + size); + + if (!alloc) + THROW(E_MEMORY); + + MEMORY_size += size - alloc->size; + + if (_alloc == old) + _alloc = alloc; + + if (alloc->prev) + alloc->prev->next = alloc; + if (alloc->next) + alloc->next->prev = alloc; + + #ifndef DO_NOT_PRINT_MEMORY + #ifdef PROJECT_EXEC + fprintf(MEMORY_log, "%s: ", DEBUG_get_current_position()); + #endif + fprintf(MEMORY_log, "<%d> %s: MEMORY_realloc(%p, %d) -> %p\n", alloc->id, src, *((void **)p_ptr), (int)size, (char *)alloc + sizeof(ALLOC)); + fflush(MEMORY_log); + #endif + + *((void **)p_ptr) = (char *)alloc + sizeof(ALLOC); +} +#else +void MEMORY_realloc(void *p_ptr, size_t size) +{ + void *alloc = *((void **)p_ptr); + + if (size == 0) + { + MEMORY_free(p_ptr); + return; + } + + alloc = realloc(alloc, size); + + if (!alloc) + THROW(E_MEMORY); + + *((void **)p_ptr) = alloc; +} +#endif + + +#if DEBUG_MEMORY +void MEMORY_free(void *p_ptr, const char *src) +{ + ALLOC *alloc = (ALLOC *)(*((char **)p_ptr) - sizeof(ALLOC)); + + if (!(*((void **)p_ptr))) + return; + + if (alloc->prev) + alloc->prev->next = alloc->next; + if (alloc->next) + alloc->next->prev = alloc->prev; + + if (alloc == _alloc) + _alloc = alloc->next; + + #ifndef DO_NOT_PRINT_MEMORY + #ifdef PROJECT_EXEC + fprintf(MEMORY_log, "%s: ", DEBUG_get_current_position()); + #endif + fprintf(MEMORY_log, "<%d> %s: MEMORY_free(%p) / %p\n", alloc->id, src, *((void **)p_ptr), (char *)alloc + sizeof(ALLOC)); + fflush(MEMORY_log); + #endif + + MEMORY_size -= alloc->size; + + //*((long *)alloc) = 0x31415926; + free(alloc); + + *((void **)p_ptr) = NULL; + MEMORY_count--; +} +#else +void MEMORY_free(void *p_ptr) +{ + void *alloc = *((void **)p_ptr); + + if (!alloc) + return; + + free(alloc); + + *((void **)p_ptr) = NULL; + MEMORY_count--; +} +#endif + +#endif // OPTIMIZE_MEMORY + +#if OPTIMIZE_MEMORY + +//void DEBUG_print_current_backtrace(void); +//static bool _print_backtrace = FALSE; + +void *my_malloc(size_t len) +{ + size_t *ptr; + int size = REAL_SIZE((int)len + sizeof(size_t)); + int pool = (size / SIZE_INC) - 1; + + MEMORY_count++; + + if (pool < POOL_SIZE) + { + if (_pool_count[pool]) + { + ptr = _pool[pool]; + #if defined(DEBUG_ME) && !defined(DO_NOT_PRINT_MEMORY) + MEMORY_pool_size -= size; + fprintf(stderr, "my_malloc: %d bytes from pool #%d -> %p (%p) [%ld / %ld]\n", size, pool, ptr + 1, sbrk(0), MEMORY_size - MEMORY_pool_size, MEMORY_size); + #endif + _pool[pool] = *((void **)ptr); + _pool_count[pool]--; + *ptr++ = size; + return ptr; + } + } + + #ifdef DEBUG_ME + MEMORY_size += size; + #endif + ptr = malloc(size); + if (!ptr) + THROW_MEMORY(); + #if defined(DEBUG_ME) && !defined(DO_NOT_PRINT_MEMORY) + fprintf(stderr, "my_malloc: %d bytes from malloc -> %p (%p) [%ld / %ld]\n", size, ptr + 1, sbrk(0), MEMORY_size - MEMORY_pool_size, MEMORY_size); + #endif + + /*if (!_first_alloc) + _first_alloc = ptr; + if (ptr > _max_alloc && !_print_backtrace) + { + _max_alloc = ptr; + fprintf(stderr, "%ld\n", (char *)_max_alloc - (char *)_first_alloc + size); + _print_backtrace = TRUE; + DEBUG_print_current_backtrace(); + _print_backtrace = FALSE; + }*/ + + *ptr++ = size; + return ptr; +} + +void my_free(void *alloc) +{ + size_t *ptr; + int size; + int pool; + + if (!alloc) + return; + + MEMORY_count--; + + ptr = alloc; + ptr--; + + size = (int)*ptr; + pool = (size / SIZE_INC) - 1; + + //if (ptr < (_first_alloc + (_max_alloc - _first_alloc) / 2)) + if (1) + { + if (pool < POOL_SIZE) + { + if (_pool_count[pool] < POOL_MAX) + { + #if defined(DEBUG_ME) && !defined(DO_NOT_PRINT_MEMORY) + MEMORY_pool_size += size; + fprintf(stderr, "my_free: %p (%p) -> %d bytes to pool #%d [%ld / %ld]\n", alloc, sbrk(0), size, pool, MEMORY_size - MEMORY_pool_size, MEMORY_size); + #endif + *((void **)ptr) = _pool[pool]; + _pool[pool] = ptr; + _pool_count[pool]++; + return; + } + } + } + + #ifdef DEBUG_ME + MEMORY_size -= size; + #ifndef DO_NOT_PRINT_MEMORY + fprintf(stderr, "my_free: %p (%p) -> %d bytes freed [%ld / %ld]\n", alloc, sbrk(0), size, MEMORY_size - MEMORY_pool_size, MEMORY_size); + #endif + #endif + free(ptr); +} + +void *my_realloc(void *alloc, size_t new_len) +{ + size_t *ptr; + int size; + int new_size; + + if (!alloc) + return my_malloc(new_len); + + ptr = alloc; + ptr--; + size = (int)*ptr; + new_size = REAL_SIZE(new_len + sizeof(size_t)); + + if (size == new_size) + return alloc; + + #if defined(DEBUG_ME) && !defined(DO_NOT_PRINT_MEMORY) + fprintf(stderr, "my_realloc: %p (%p) -> %d ==> %d\n", alloc, sbrk(0), size, new_size); + #endif + + if (new_len == 0) + { + my_free(alloc); + return NULL; + } + else if (size > POOL_MAX_LEN && new_size > POOL_MAX_LEN) + { + #ifdef DEBUG_ME + MEMORY_size += new_size - size; + #ifndef DO_NOT_PRINT_MEMORY + fprintf(stderr, "my_realloc: %p (%p) -> %d bytes reallocated to %d [%ld / %ld]\n", alloc, sbrk(0), size, new_size, MEMORY_size - MEMORY_pool_size, MEMORY_size); + #endif + #endif + ptr = realloc(ptr, new_size); + if (!ptr) + THROW_MEMORY(); + *ptr++ = new_size; + return ptr; + } + else + { + size_t *nptr = my_malloc(new_len); + + nptr--; + + if (new_size < size) + memcpy(nptr, ptr, new_size); + else + memcpy(nptr, ptr, size); + + my_free(alloc); + + *nptr++ = new_size; + return nptr; + } +} + +#endif diff --git a/main/share/gb_arch.h b/main/share/gb_arch.h new file mode 100644 index 00000000..4186e5a2 --- /dev/null +++ b/main/share/gb_arch.h @@ -0,0 +1,90 @@ +/*************************************************************************** + + gb_arch.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBX_ARCH_H +#define __GBX_ARCH_H + +#include "gb_common.h" +#include "gb_table.h" +#include "gb_magic.h" + +typedef + struct { + int magic; + int version; + int pos_data; + int pos_string; + int pos_table; + int n_symbol; + } + ARCH_HEADER; + +typedef + struct { + SYMBOL sym; + int pos; + int len; + } + ARCH_SYMBOL; + +typedef + struct { + struct { + uint name; + int len; + } + sym; + int pos; + int len; + } + ARCH_SYMBOL_32; + +typedef + struct { + int fd; + ARCH_HEADER header; + ARCH_SYMBOL *symbol; + ushort *sort; + char *string; + char *addr; + size_t length; + } + ARCH; + +typedef + struct { + ARCH_SYMBOL *sym; + int index; + int pos; + int len; + } + ARCH_FIND; + + +ARCH *ARCH_open(const char *path); +void ARCH_close(ARCH *arch); +bool ARCH_find(ARCH *arch, const char *path, int len_path, ARCH_FIND *find); +bool ARCH_read(ARCH *arch, int pos, void *buffer, int len); +//void ARCH_get_absolute_path(const char *path, int len_path, char *abs_path, int *len_abs_path); + +#endif diff --git a/main/share/gb_arch_temp.h b/main/share/gb_arch_temp.h new file mode 100644 index 00000000..495070eb --- /dev/null +++ b/main/share/gb_arch_temp.h @@ -0,0 +1,362 @@ +/*************************************************************************** + + gb_arch_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GBX_ARCH_C + +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_common_swap.h" +#include "gb_error.h" +#include "gb_alloc.h" +#include "gb_arch.h" + + +#ifndef PROJECT_EXEC +#define E_ARCH "Bad archive: &1" +#endif + +static bool _swap = FALSE; +static const char *_path; + +static void arch_error(const char *msg) +{ + const char *name = FILE_get_name(_path); + if (msg == NULL) + THROW(E_ARCH, name, strerror(errno)); + else + THROW(E_ARCH, name, msg); +} + +static void read_at(ARCH *arch, int pos, void *buf, int len) +{ + memcpy(buf, &arch->addr[pos], len); +} + + +static void load_arch(ARCH *arch, const char *path) +{ + int len, lens; + int i; + int pos; + int pos_sort; + struct stat info; + ARCH_SYMBOL_32 *sym; + + _path = path; + + arch->fd = open(path, O_RDONLY); + if (arch->fd < 0) + THROW(E_OPEN, path, strerror(errno)); + + if (fstat(arch->fd, &info) < 0) + THROW(E_OPEN, path, strerror(errno)); + + arch->length = info.st_size; + arch->addr = mmap(NULL, arch->length, PROT_READ, MAP_PRIVATE, arch->fd, 0); + if (arch->addr == MAP_FAILED) + THROW(E_OPEN, path, strerror(errno)); + + //fprintf(stderr, "mmap: %s\n", path); + + /* Header */ + + read_at(arch, 32, &arch->header, sizeof(ARCH_HEADER)); + _swap = arch->header.magic != ARCH_MAGIC; + + if (_swap) + SWAP_ints((int *)&arch->header, 6); + + if (arch->header.magic != ARCH_MAGIC) + arch_error("not an archive"); + + //if (arch->header.version != ARCH_VERSION) + // arch_error("bad version"); + + /* Strings */ + + len = arch->header.pos_table - arch->header.pos_string; + if (len <= 0) + arch_error("corrupted header"); + + ALLOC(&arch->string, len); + read_at(arch, arch->header.pos_string, arch->string, len); + + /* File names table */ + + len = arch->header.n_symbol * sizeof(ARCH_SYMBOL); + lens = arch->header.n_symbol * sizeof(ushort); + if (len <= 0 || lens <= 0) + arch_error("corrupted header"); + + ALLOC(&arch->symbol, len); + ALLOC(&arch->sort, lens); + + sym = (ARCH_SYMBOL_32 *)&arch->addr[arch->header.pos_table]; + for (i = 0; i < arch->header.n_symbol; i++, sym++) + { + //arch->symbol[i].sym.sort = sym->sym.sort; + arch->symbol[i].sym.len = sym->sym.len; + arch->symbol[i].sym.name = (char *)(intptr_t)sym->sym.name; + arch->symbol[i].pos = sym->pos; + arch->symbol[i].len = sym->len; + } + pos_sort = arch->header.pos_table + arch->header.n_symbol * sizeof(ARCH_SYMBOL_32); + + read_at(arch, pos_sort, arch->sort, lens); + + /* String relocation */ + + pos = 0; + if (_swap) + { + for (i = 0; i < arch->header.n_symbol; i++) + { + SWAP_short((short *)&arch->sort[i]); + SWAP_int(&arch->symbol[i].sym.len); + SWAP_int(&arch->symbol[i].pos); + SWAP_int(&arch->symbol[i].len); + arch->symbol[i].sym.name = &arch->string[pos]; + pos += arch->symbol[i].sym.len; + } + } + else + { + for (i = 0; i < arch->header.n_symbol; i++) + { + arch->symbol[i].sym.name = &arch->string[pos]; + pos += arch->symbol[i].sym.len; + } + } + + SYMBOL_compute_keys(arch->symbol, arch->header.n_symbol, sizeof(ARCH_SYMBOL), TF_NORMAL); + + _path = NULL; + + #if 0 + for (i = 0; i < arch->header.n_symbol; i++) + { + fprintf(stderr, "%i (%i) %.*s\n", i, arch->sort[i], arch->symbol[i].sym.len, arch->symbol[i].sym.name); + } + #endif +} + + +ARCH *ARCH_open(const char *path) +{ + ARCH *arch; + + ALLOC_ZERO(&arch, sizeof(ARCH)); + + load_arch(arch, path); + + return arch; +} + +void ARCH_close(ARCH *arch) +{ + if (arch->fd) + { + FREE(&arch->string); + FREE(&arch->symbol); + FREE(&arch->sort); + munmap(arch->addr, arch->length); + close(arch->fd); + } + + FREE(&arch); +} + + +static bool get_absolute_path(const char *path, int len_path, char *abs_path, int *len_abs_path) +{ + const char *p, *lp; + char *ap, *apm; + char c; + int rest; + bool err = FALSE; + + p = path; + lp = p; + ap = abs_path; + apm = &abs_path[PATH_MAX]; + *len_abs_path = 0; + + for(;;) + { + rest = &path[len_path] - p; + if (rest <= 0) + break; + + if (ap >= apm) + { + err = TRUE; + break; + } + + c = *p; + + if (p == lp && c == '.') + { + if (rest == 1 || p[1] == '/') + { + if (rest == 1) + p++; + else + p += 2; + + lp = p; + continue; + } + else if (rest >= 2 && p[1] == '.' && (rest == 2 || p[2] == '/')) + { + if (ap > abs_path) + { + ap--; // Jumps the last '/' + while (ap > abs_path) + { + ap--; + if (*ap == '/') + { + ap++; + break; + } + } + } + + p += 3; + lp = p; + continue; + } + } + + *ap++ = c; + p++; + + if (c == '/') + lp = p; + } + + *len_abs_path = ap - abs_path; + *ap = 0; + return err; +} + +static int strint(char *str, int value) +{ + char buffer[16]; + char *p; + int len; + + p = &buffer[15]; + while (value >= 10) + { + *p-- = '0' + value % 10; + value /= 10; + } + *p = '0' + value; + + len = buffer + sizeof(buffer) - p; + memcpy(str, p, len); + return len; +} + +bool ARCH_find(ARCH *arch, const char *path, int len_path, ARCH_FIND *find) +{ + int ind; + ARCH_SYMBOL *sym; + char tpath[PATH_MAX]; + int len_tpath; + int n; + + if (len_path <= 0) + len_path = strlen(path); + + while (len_path >= 2 && path[len_path - 1] == '/') + len_path--; + + if (get_absolute_path(path, len_path, tpath, &len_tpath)) + return TRUE; + + if (len_tpath == 0) + { + find->sym = NULL; + find->index = NO_SYMBOL; + find->pos = -1; + find->len = 0; + return FALSE; + } + + //if (arch->header.version == 2) + //{ + char *p; + char tpath2[len_tpath + 8]; + + for(;;) + { + p = index(tpath + 1, '/'); + if (!p) + break; + + ind = SYMBOL_find(arch->symbol, arch->sort, arch->header.n_symbol, sizeof(ARCH_SYMBOL), TF_NORMAL, tpath, p - tpath, 0); + if (ind == NO_SYMBOL) + break; + + sym = &arch->symbol[ind]; + tpath2[0] = '/'; + n = strint(&tpath2[1], ind); + tpath2[n + 1] = ':'; + strcpy(&tpath2[n + 2], p + 1); + len_tpath = n + 2 + strlen(p + 1); + memcpy(tpath, tpath2, len_tpath); + tpath[len_tpath] = 0; + } + + ind = SYMBOL_find(arch->symbol, arch->sort, arch->header.n_symbol, sizeof(ARCH_SYMBOL), TF_NORMAL, tpath, len_tpath, 0); + //} + //else + // SYMBOL_find_old(arch->symbol, arch->header.n_symbol, sizeof(ARCH_SYMBOL), TF_NORMAL, tpath, len_tpath, 0, &ind); + + if (ind == NO_SYMBOL) + return TRUE; + + sym = &arch->symbol[ind]; + + find->sym = sym; + find->pos = sym->pos; + find->len = sym->len; + find->index = ind; + + return FALSE; +} + + +bool ARCH_read(ARCH *arch, int pos, void *buffer, int len) +{ + memcpy(buffer, &arch->addr[pos], len); + return FALSE; +} diff --git a/main/share/gb_array.h b/main/share/gb_array.h new file mode 100644 index 00000000..0ffb6e49 --- /dev/null +++ b/main/share/gb_array.h @@ -0,0 +1,117 @@ +/*************************************************************************** + + gb_array.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_ARRAY_H +#define __GB_ARRAY_H + +typedef + struct { + int count; + int max; + int size; + int inc; + } + ARRAY; + +typedef + int (*ARRAY_COMP_FUNC)(const void *, const void *); + +#define DATA_TO_ARRAY(data) ((ARRAY *)(data) - 1) +#define ARRAY_TO_DATA(array) ((char *)((ARRAY *)(array) + 1)) + +#define ARRAY_create(data) ARRAY_create_with_size((data), sizeof(**(data)), 32) +#define ARRAY_create_inc(data, inc) ARRAY_create_with_size((data), sizeof(**(data)), (inc)) + +void ARRAY_create_with_size(void *p_data, size_t size, int inc); +void ARRAY_delete(void *p_data); + +#define ARRAY_size(_data) (DATA_TO_ARRAY(_data)->size) +#define ARRAY_count(_data) ((_data) ? DATA_TO_ARRAY(_data)->count : 0) + +void ARRAY_realloc(void *p_data); + +void *ARRAY_add_data(void *p_data, int num, bool zero); +void *ARRAY_add_data_one(void *p_data, bool zero); + +#define ARRAY_add_one(_pdata, _zero) \ +({ \ + ARRAY *__array = DATA_TO_ARRAY(*(_pdata)); \ + __typeof__(*(_pdata)) ptr; \ + int old_count = __array->count; \ + \ + __array->count++; \ + \ + if (__array->count <= __array->max) \ + { \ + ptr = *(_pdata) + old_count; \ + } \ + else \ + { \ + ARRAY_realloc(_pdata); \ + ptr = *(_pdata) + old_count; \ + } \ + if (_zero) memset(ptr, 0, sizeof(*ptr)); \ + ptr; \ +}) + +/*#define ARRAY_add_one_size(_pdata, _zero) \ +({ \ + ARRAY *array = DATA_TO_ARRAY(*(_pdata)); \ + __typeof__(*(_pdata)) ptr; \ + \ + if (array->count < array->max) \ + { \ + ptr = (void *)((char *)*(_pdata) + array->count * array->size); \ + memset(ptr, 0, array->size); \ + array->count++; \ + } \ + else \ + { \ + array->count++; \ + array = ARRAY_realloc(_pdata, (_zero)); \ + ptr = (void *)((char *)*(_pdata) + (array->count - 1) * array->size); \ + } \ + ptr; \ +})*/ + +#define ARRAY_add(_pdata) ARRAY_add_one(_pdata, FALSE) +#define ARRAY_add_void(_pdata) ARRAY_add_one(_pdata, TRUE) +#define ARRAY_add_size(_pdata) ARRAY_add_data_one(_pdata, FALSE) +#define ARRAY_add_void_size(_pdata) ARRAY_add_data_one(_pdata, TRUE) + +#define ARRAY_add_many(_pdata, _num) ARRAY_add_data(_pdata, _num, FALSE) +#define ARRAY_add_many_void(_pdata, _num) ARRAY_add_data(_pdata, _num, TRUE) + +//PUBLIC void *ARRAY_get(void *data, int pos); +#define ARRAY_get(_data, _pos) ((char *)(_data) + DATA_TO_ARRAY(_data)->size * (_pos)) + +void *ARRAY_insert_many(void *p_data, int pos, int count); +#define ARRAY_insert(_pdata, _pos) ARRAY_insert_many(_pdata, _pos, 1); +void ARRAY_remove_many(void *p_data, int pos, int count); +#define ARRAY_remove(_pdata, _pos) ARRAY_remove_many(_pdata, _pos, 1); + +void ARRAY_remove_last(void *p_data); + +void ARRAY_qsort(void *data, ARRAY_COMP_FUNC cmp); + +#endif diff --git a/main/share/gb_array_temp.h b/main/share/gb_array_temp.h new file mode 100644 index 00000000..98acc152 --- /dev/null +++ b/main/share/gb_array_temp.h @@ -0,0 +1,223 @@ +/*************************************************************************** + + gb_array_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __ARRAY_C + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_alloc.h" +#include "gb_array.h" + +void ARRAY_create_with_size(void *p_data, size_t size, int inc) +{ + ARRAY *array; + + ALLOC(&array, sizeof(ARRAY)); + + array->count = 0; + array->max = 0; + array->size = (int)size; + if (size > 2 && (size & 3)) + fprintf(stderr, "WARNING: ARRAY_create_with_size: size = %zi\n", size); + array->inc = inc; + + *((void **)p_data) = ARRAY_TO_DATA(array); +} + + +void ARRAY_delete(void *p_data) +{ + void **data = (void **)p_data; + ARRAY *alloc = DATA_TO_ARRAY(*data); + + if (!*data) + return; + + FREE(&alloc); + + *data = NULL; +} + +void *ARRAY_add_data(void *p_data, int num, bool zero) +{ + void **data = (void **)p_data; + register ARRAY *array = DATA_TO_ARRAY(*data); + ARRAY *new_array; + char *ptr; + + array->count += num; + + if (array->count > array->max) + { + array->max = array->inc + ((array->count + array->inc) / array->inc) * array->inc; + new_array = array; + REALLOC(&new_array, sizeof(ARRAY) + array->max * array->size); + array = new_array; + *data = ARRAY_TO_DATA(array); + } + + ptr = (char *)array + sizeof(ARRAY) + array->size * (array->count - num); + + if (zero) memset(ptr, 0, array->size * num); + + return ptr; +} + +void ARRAY_realloc(void *p_data) //, bool zero) +{ + void **data = (void **)p_data; + ARRAY *array = DATA_TO_ARRAY(*data); + ARRAY *new_array; + //int old_max = array->max; + int size = array->size; + + array->max = array->inc + ((array->count + array->inc) / array->inc) * array->inc; + new_array = array; + REALLOC(&new_array, sizeof(ARRAY) + array->max * size); + *data = ARRAY_TO_DATA(new_array); + //fprintf(stderr, "ARRAY_realloc: %p (%d) -> %p (%d) [%d]\n", array, old_max, new_array, new_array->max, size); + /*if (zero) + { + //fprintf(stderr, "ARRAY_realloc: memset(%p, 0, %d)\n", ARRAY_TO_DATA(new_array) + old_max * size, (new_array->max - old_max) * size); + memset(ARRAY_TO_DATA(new_array) + old_max * size, 0, (new_array->max - old_max) * size); + }*/ +} + + +void *ARRAY_add_data_one(void *p_data, bool zero) +{ + void **data = (void **)p_data; + register ARRAY *array = DATA_TO_ARRAY(*data); + ARRAY *new_array; + int size = array->size; + char *ptr; + + array->count++; + + if (array->count > array->max) + { + array->max = array->inc + ((array->count + array->inc) / array->inc) * array->inc; + new_array = array; + REALLOC(&new_array, sizeof(ARRAY) + array->max * size); + array = new_array; + *data = ARRAY_TO_DATA(array); + } + + ptr = (char *)array + sizeof(ARRAY) + size * (array->count - 1); + + if (zero) memset(ptr, 0, size); + + return ptr; +} + + + +void ARRAY_remove_last(void *p_data) +{ + void **data = (void **)p_data; + ARRAY *array = DATA_TO_ARRAY(*data); + + if (!array->count) + return; + + array->count--; +} + +#if 0 +void *ARRAY_get(void *data, int pos) +{ + ARRAY *array = DATA_TO_ARRAY(data); + + return (char *)data + array->size * pos; +} +#endif + +void *ARRAY_insert_many(void *p_data, int pos, int count) +{ + void **data; + ARRAY *array; + char *addr; + int len; + + data = (void **)p_data; + array = DATA_TO_ARRAY(*data); + + if ((pos < 0) || (pos > array->count)) + pos = array->count; + + ARRAY_add_many(p_data, count); + array = DATA_TO_ARRAY(*data); + + addr = ((char *)(*data)) + array->size * pos; + len = (array->count - pos - count) * array->size; + + if (len > 0) + memmove(addr + array->size * count, addr, len); + + memset(addr, 0, array->size * count); + + return addr; +} + + +void ARRAY_remove_many(void *p_data, int pos, int count) +{ + void **data = (void **)p_data; + ARRAY *array = DATA_TO_ARRAY(*data); + char *addr; + int len; + + if ((pos < 0) || (pos >= array->count)) + return; + + if (count < 0 || count > (array->count - pos)) + count = array->count - pos; + + addr = ((char *)(*data)) + array->size * pos; + len = (array->count - pos - count) * array->size; + + if (len > 0) + memmove(addr, addr + array->size * count, len); + + array->count -= count; + + if (array->max <= array->inc) + return; + + if (array->count > (array->max / 2)) + return; + + array->max = ((array->count + array->inc) / array->inc) * array->inc; + REALLOC(&array, sizeof(ARRAY) + array->max * array->size); + *data = ARRAY_TO_DATA(array); +} + + +void ARRAY_qsort(void *data, ARRAY_COMP_FUNC cmp) +{ + ARRAY *array = DATA_TO_ARRAY(data); + + if (array->count) + qsort(data, array->count, array->size, cmp); +} + diff --git a/main/share/gb_buffer.h b/main/share/gb_buffer.h new file mode 100644 index 00000000..8f457d74 --- /dev/null +++ b/main/share/gb_buffer.h @@ -0,0 +1,49 @@ +/*************************************************************************** + + gb_buffer.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_BUFFER_H +#define __GB_BUFFER_H + +typedef + struct { + size_t length; + size_t max; + } + BUFFER; + + +#define BUFFER_INC 256 + +void BUFFER_create(void *p_data); +void BUFFER_delete(void *p_data); +bool BUFFER_load_file(void *p_data, const char *name); +offset_t BUFFER_add(void *p_data, const void *string, int len); +bool BUFFER_need(void *p_data, size_t size); +void BUFFER_add_char(void *p_data, char c); + +#define DATA_TO_BUFFER(_data) ((BUFFER *)(_data) - 1) +#define BUFFER_TO_DATA(_buffer) ((char *)((BUFFER *)(_buffer) + 1)) + +#define BUFFER_length(_data) (DATA_TO_BUFFER(_data)->length) + +#endif diff --git a/main/share/gb_buffer_temp.h b/main/share/gb_buffer_temp.h new file mode 100644 index 00000000..fe82e0cf --- /dev/null +++ b/main/share/gb_buffer_temp.h @@ -0,0 +1,163 @@ +/*************************************************************************** + + gb_buffer_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __BUFFER_C + +#include +#include +#include +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_alloc.h" +#include "gb_buffer.h" + + +void BUFFER_create(void *p_data) +{ + BUFFER *buf; + + ALLOC(&buf, sizeof(BUFFER)); + buf->max = 0; + buf->length = 0; + + *((void **)p_data) = BUFFER_TO_DATA(buf); +} + + +void BUFFER_delete(void *p_data) +{ + void **data = (void **)p_data; + BUFFER *buf = DATA_TO_BUFFER(*data); + + FREE(&buf); + *data = NULL; +} + + +bool BUFFER_need(void *p_data, size_t size) +{ + void **data = (void **)p_data; + BUFFER *buffer = DATA_TO_BUFFER(*data); + + buffer->length += size; + //fprintf(stderr, "BUFFER_need: %ld (%ld / %ld)\n", size, buffer->length, buffer->max); + + if (buffer->length > buffer->max) + { + while (buffer->length >= buffer->max) + buffer->max += BUFFER_INC; + + REALLOC(&buffer, sizeof(char) * buffer->max + sizeof(BUFFER)); + *data = BUFFER_TO_DATA(buffer); + } + + return FALSE; +} + + +offset_t BUFFER_add(void *p_data, const void *string, int len) +{ + void **data = (void **)p_data; + BUFFER *buffer = DATA_TO_BUFFER(*data); + size_t pos; + + if (len < 0) + len = strlen((const char *)string); + + pos = buffer->length; + BUFFER_need(p_data, len); + + memcpy(*data + pos, string, len); + + //fprintf(stderr, ">> BUFFER_add\n"); + + return pos; +} + +void BUFFER_add_char(void *p_data, char c) +{ + void **data = (void **)p_data; + BUFFER *buffer = DATA_TO_BUFFER(*data); + size_t pos; + + pos = buffer->length; + BUFFER_need(p_data, 1); + *(char *)(*data + pos) = c; +} + +bool BUFFER_load_file(void *p_data, const char *name) +{ + void **data; + + int fd; + struct stat info; + int old_len; + int len, lenr; + void *p; + + fd = open(name, O_RDONLY); + if (fd < 0) + return TRUE; + + if (fstat(fd, &info)) + { + close(fd); + return TRUE; + } + + len = info.st_size; + + data = (void **)p_data; + old_len = DATA_TO_BUFFER(*data)->length; + + BUFFER_need(p_data, len); + + data = (void **)p_data; + + p = *data + old_len; + + for(;;) + { + lenr = read(fd, p, len); + if (lenr < 0) + { + close(fd); + return TRUE; + } + + if (lenr == len) + break; + + p += lenr; + len -= lenr; + } + + close(fd); + + return FALSE; +} diff --git a/main/share/gb_class_desc_common.h b/main/share/gb_class_desc_common.h new file mode 100644 index 00000000..5cda8690 --- /dev/null +++ b/main/share/gb_class_desc_common.h @@ -0,0 +1,40 @@ +/*************************************************************************** + + gb_class_desc_common.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_CLASS_DESC_COMMON_H +#define __GB_CLASS_DESC_COMMON_H + +enum +{ + CD_VARIABLE_ID = 1, + CD_STATIC_VARIABLE_ID = 2, + CD_PROPERTY_ID = 3, + CD_STATIC_PROPERTY_ID = 4, + CD_METHOD_ID = 5, + CD_STATIC_METHOD_ID = 6, + CD_CONSTANT_ID = 7, + CD_EVENT_ID = 8, + CD_EXTERN_ID = 9, +}; + +#endif diff --git a/main/share/gb_code.h b/main/share/gb_code.h new file mode 100644 index 00000000..a5eecb8a --- /dev/null +++ b/main/share/gb_code.h @@ -0,0 +1,156 @@ +/*************************************************************************** + + gb_code.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_CODE_H +#define __GB_CODE_H + +#include "gb_pcode.h" + +#ifndef __CODE_C +EXTERN short CODE_stack_usage; +EXTERN unsigned char CODE_disabled; +#endif + + +/* Number of instruction added to a function code buffer at once. Must be a power of 2 */ +#define CODE_INSTR_INC 1024 +#define CODE_NO_POS (ushort)0xFFFF + +#define CODE_disable() (CODE_disabled++) +#define CODE_enable() (CODE_disabled--) + +#ifdef PROJECT_EXEC + +void CODE_begin_function(void); +void CODE_end_function(void); +bool CODE_popify_last(void); + +#else + +#include "gb_common_swap.h" + +void CODE_begin_function(FUNCTION *func); +void CODE_end_function(FUNCTION *func); + +bool CODE_popify_last(void); +bool CODE_check_statement_last(void); +bool CODE_check_pop_local_last(short *local); +bool CODE_check_jump_not(void); + +bool CODE_check_varptr(void); +bool CODE_check_ismissing(void); + +void CODE_allow_break(void); +//void CODE_break(void); + +void CODE_pop_local(short num); +//void CODE_pop_param(short num); +void CODE_pop_global(short global, bool is_static); +void CODE_pop_symbol(short symbol); +void CODE_pop_unknown(short symbol); +void CODE_pop_optional(short num); + +//void CODE_push_special(short spec); +void CODE_push_event(short event); +void CODE_push_extern(short index); + +void CODE_jump(void); +void CODE_gosub(int ctrl_local); +void CODE_on(uchar num); +void CODE_jump_first(short local); +void CODE_jump_next(void); +void CODE_jump_if_true(void); +void CODE_jump_if_false(void); +void CODE_jump_length(ushort src, ushort dst); +void CODE_first(short local); +void CODE_next(bool drop); + +void CODE_new(ushort nparam, bool array, bool event); + +void CODE_quit(bool ret); +void CODE_stop(void); + +void CODE_event(bool on); +void CODE_stop_event(void); + +void CODE_try(void); +void CODE_end_try(void); +void CODE_catch(void); + +void CODE_pop_ctrl(short num); + +#endif /* PROJECT_COMP */ + +ushort CODE_get_current_pos(void); +ushort CODE_set_current_pos(ushort pos); +void CODE_ignore_next_stack_usage(void); + +void CODE_dump(PCODE *code, int count); + +void CODE_push_number(int value); +void CODE_push_const(ushort value); + +void CODE_push_local(short num); +//void CODE_push_param(short num); +void CODE_push_array(short nparam); +void CODE_push_global(short global, bool is_static, bool is_function); +void CODE_push_symbol(short symbol); +void CODE_push_unknown(short symbol); +void CODE_push_unknown_event(short symbol); +void CODE_push_class(short class); + +void CODE_op(short op, short subcode, short nparam, bool fixed); + +void CODE_push_me(bool); +void CODE_push_super(bool); +void CODE_push_last(void); +void CODE_push_null(void); +void CODE_push_void_string(); +void CODE_push_boolean(bool value); +void CODE_push_inf(bool neg); +void CODE_push_complex(); + +void CODE_push_vargs(); +void CODE_drop_vargs(); + +void CODE_dup(void); + +void CODE_return(int return_value); + +void CODE_push_char(uchar car); +void CODE_push_void(void); + +void CODE_subr(short subr, short nparam, short optype, bool fixed); +//void CODE_subr_output(short subr, short nparam, int output); + +void CODE_call(short nparam); +void CODE_call_byref(short nparam, uint64_t byref); +void CODE_byref(uint64_t byref); +void CODE_drop(void); +void CODE_push_return(void); + +void CODE_nop(void); + +void CODE_string_add(void); + +#endif diff --git a/main/share/gb_code_temp.h b/main/share/gb_code_temp.h new file mode 100644 index 00000000..3f90d284 --- /dev/null +++ b/main/share/gb_code_temp.h @@ -0,0 +1,1442 @@ +/*************************************************************************** + + gb_code_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +//#define DEBUG + +#define write_Zxxx(code, val) write_short(code | ((short)val & 0x0FFF)) +#define write_Z8xx(code, val) write_short(code | ((short)val & 0x07FF)) +#define write_ZZxx(code, val) write_short(code | ((short)val & 0x00FF)) + +#ifndef PROJECT_EXEC +#define LAST_CODE \ +{ \ + if (JOB->debug && !JOB->nobreak) \ + CODE_break(); \ + cur_func->last_code2 = cur_func->last_code; \ + cur_func->last_code = cur_func->ncode; \ +} +#define CURRENT_CLASS JOB->class +#else +#define LAST_CODE \ +{ \ + cur_func->last_code2 = cur_func->last_code; \ + cur_func->last_code = cur_func->ncode; \ +} +#define CURRENT_CLASS EVAL +#endif + +short CODE_stack_usage; +short CODE_stack; +unsigned char CODE_disabled = 0; + +static bool _ignore_next_stack_usage = FALSE; + +#ifdef PROJECT_EXEC +#define cur_func EVAL +#else +static FUNCTION *cur_func = NULL; +static int last_line = 0; +#endif + +static void alloc_code(void) +{ + cur_func->ncode_max += CODE_INSTR_INC; + if (!cur_func->code) + ALLOC(&cur_func->code, sizeof(short) * CODE_INSTR_INC); + else + REALLOC(&cur_func->code, sizeof(short) * cur_func->ncode_max); +} + +#define write_short(_value) \ +({ \ + if (CODE_disabled == 0) \ + { \ + if (cur_func->ncode >= cur_func->ncode_max) \ + alloc_code(); \ + \ + cur_func->code[cur_func->ncode] = _value; \ + /*fprintf(stderr, "[%d] %04hX\n", cur_func->ncode, (ushort)value);*/ \ + cur_func->ncode++; \ + } \ +}) + +#ifdef PROJECT_COMP + +static bool _allow_break = FALSE; + +void CODE_allow_break(void) +{ + _allow_break = TRUE; +} + +static void CODE_break(void) +{ + if (!_allow_break) + return; + + /*if (last_line < 0) + { + if (CODE_get_current_pos()) + return; + } + else + { + if (JOB->line == last_line) + return; + + last_line = JOB->line; + }*/ + + #ifdef DEBUG + printf("BREAK\n"); + #endif + + write_short(C_BREAK); + _allow_break = FALSE; +} + +#endif + + +static void write_int(int value) +{ + write_short(value & 0xFFFF); + write_short((unsigned int)value >> 16); +} + + +/*static void remove_last(void) +{ + ARRAY_remove_last(&cur_func->code); + cur_func->last_code = ARRAY_count(cur_func->code); +}*/ + + +static void use_stack(int use) +{ + if (_ignore_next_stack_usage) + { + _ignore_next_stack_usage = FALSE; + return; + } + + CODE_stack += use; + CODE_stack_usage = Max(CODE_stack_usage, CODE_stack); + #ifdef DEBUG + printf("%04d: %d\n", cur_func->ncode, CODE_stack); + #endif +} + +static void CODE_undo() +{ + cur_func->ncode = cur_func->last_code; + cur_func->last_code = cur_func->last_code2; + cur_func->last_code2 = (-1); +} + +ushort CODE_get_current_pos(void) +{ + return cur_func->ncode; +} + +ushort CODE_set_current_pos(ushort pos) +{ + ushort old = cur_func->ncode; + cur_func->ncode = pos; + return old; +} + + +void CODE_ignore_next_stack_usage(void) +{ + _ignore_next_stack_usage = TRUE; +} + +#ifdef PROJECT_EXEC + +void CODE_begin_function() +{ + CODE_stack = 0; + CODE_stack_usage = 0; +} + +void CODE_end_function() +{ + if (CODE_stack) + { + fprintf(stderr, "gb.eval: bad stack usage computed: %d\n", CODE_stack); + THROW("Internal compiler error"); + } +} + +#else + +void CODE_begin_function(FUNCTION *func) +{ + cur_func = func; + CODE_stack = 0; + CODE_stack_usage = 0; + if (func->start == NULL) + last_line = (-1); + else + last_line = 0; +} + +void CODE_end_function(FUNCTION *func) +{ + if (CODE_stack) + { + ERROR_warning("bad stack usage computed: %d\n", CODE_stack); + THROW("Internal compiler error"); + } +} + +#endif + + +static ushort *get_last_code() +{ + if ((ushort)cur_func->last_code == CODE_NO_POS) + return NULL; + + return &cur_func->code[cur_func->last_code]; +} + +static ushort *get_last_code2() +{ + if ((ushort)cur_func->last_code2 == CODE_NO_POS) + return NULL; + + return &cur_func->code[cur_func->last_code2]; +} + +bool CODE_popify_last(void) +{ + /* + #ifdef DEBUG + printf("CODE_is_last_popable ? "); + if (!last_code) printf("FALSE, last_code = NULL"); + else printf("0x%04hX", *last_code); + printf("\n"); + #endif + */ + unsigned short *last_code, op; + + last_code = get_last_code(); + if (!last_code) + return FALSE; + + op = *last_code & 0xFF00; + + if ((op >= C_PUSH_LOCAL) && (op <= C_PUSH_UNKNOWN)) + { + *last_code += 0x0800; + use_stack(-2); + #ifdef DEBUG + printf("Popify Last\n"); + #endif + return TRUE; + } + + if ((op & 0xF000) == C_PUSH_DYNAMIC) + { + *last_code += 0x1000; + use_stack(-2); + #ifdef DEBUG + printf("Popify Last\n"); + #endif + return TRUE; + } + + /* + if (*last_code == (C_PUSH_MISC | CPM_LAST)) + { + *last_code = C_PUSH_MISC | CPM_POP_LAST; + use_stack(-2); + return TRUE; + } + */ + /* + if (op == C_CALL) + { + *last_code = C_CALL_POP | (*last_code & 0xFF); + return TRUE; + } + */ + + return FALSE; +} + + +#ifdef PROJECT_COMP + +bool CODE_check_statement_last(void) +{ + unsigned short op; + PCODE *last_code; + + last_code = get_last_code(); + if (!last_code) + return FALSE; + + op = *last_code & 0xFF00; + + if (op == C_CALL) + return TRUE; + + op >>= 8; + + if (op >= CODE_FIRST_SUBR && op <= CODE_LAST_SUBR) + return TRUE; + + return FALSE; +} + + +bool CODE_check_pop_local_last(short *local) +{ + PCODE *last_code; + + last_code = get_last_code(); + if (!last_code) + return FALSE; + + if ((*last_code & 0xFF00) == C_POP_LOCAL) + { + *local = *last_code & 0xFF; + return TRUE; + } + + return FALSE; +} + +bool CODE_check_jump_not(void) +{ + ushort op; + PCODE *last_code = get_last_code(); + + if (!last_code) + return FALSE; + + op = *last_code & 0xFF00; + if (op != C_NOT) + return FALSE; + + CODE_undo(); + return TRUE; +} + +#endif + +bool CODE_check_varptr(void) +{ + unsigned short op; + PCODE *last_code; + + last_code = get_last_code(); + if (!last_code) + return TRUE; + + op = *last_code; + if (!((op & 0xFF00) == C_PUSH_LOCAL || (op & 0xFF00) == C_PUSH_PARAM || (op & 0xF800) == C_PUSH_STATIC || (op & 0xF800) == C_PUSH_DYNAMIC)) + return TRUE; + + *last_code = C_PUSH_INTEGER; + write_short((short)op); + return FALSE; +} + +bool CODE_check_ismissing(void) +{ + unsigned short op; + PCODE *last_code; + + last_code = get_last_code(); + if (!last_code) + return TRUE; + + op = *last_code; + if ((op & 0xFF00) != C_PUSH_PARAM) + return TRUE; + + *last_code = C_PUSH_QUICK | (op & 0xFF); + return FALSE; +} + +void CODE_nop(void) +{ + LAST_CODE; + write_short(C_NOP); +} + +void CODE_push_number(int value) +{ + LAST_CODE; + + use_stack(1); + + if (value >= -2048L && value < 2048L) + { + #ifdef DEBUG + printf("PUSH QUICK %d\n", value); + #endif + write_Zxxx(C_PUSH_QUICK, value); + } + else if (value >= -32768L && value < 32768L) + { + #ifdef DEBUG + printf("PUSH INTEGER %d\n", value); + #endif + write_short(C_PUSH_INTEGER); + write_short((short)value); + } + else + { + #ifdef DEBUG + printf("PUSH LONG %d\n", value); + #endif + write_short(C_PUSH_LONG); + write_int(value); + } +} + + +void CODE_push_const(ushort value) +{ + LAST_CODE; + + use_stack(1); + +#ifdef DEBUG + #ifdef PROJECT_EXEC + printf("PUSH CONST %d\n", value); + #else + printf("PUSH CONST %d %s\n", value, TABLE_get_symbol_name(CURRENT_CLASS->table, CURRENT_CLASS->constant[value].index)); + #endif +#endif + + if (value < 0xF00) + write_Zxxx(C_PUSH_CONST, value); + else + { + write_Zxxx(C_PUSH_CONST, 0xF00); + write_short((short)value); + } +} + + +void CODE_push_local(short num) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + if (num >= 0) + printf("PUSH LOCAL %d\n", num); + else + printf("PUSH PARAM %d\n", (-1) - num); + #endif + if (num >= 0) + write_ZZxx(C_PUSH_LOCAL, num); + else + write_ZZxx(C_PUSH_PARAM, num); +} + + +#ifdef PROJECT_COMP + +void CODE_pop_local(short num) +{ + LAST_CODE; + + use_stack(-1); + + #ifdef DEBUG + if (num >= 0) + printf("POP LOCAL #%d\n", num); + else + printf("POP PARAM #%d\n", (-1) - num); + #endif + if (num >= 0) + write_ZZxx(C_POP_LOCAL, num); + else + write_ZZxx(C_POP_PARAM, num); +} + + +void CODE_pop_ctrl(short num) +{ + LAST_CODE; + + use_stack(-1); + + #ifdef DEBUG + printf("POP CTRL #%d\n", num); + #endif + + write_ZZxx(C_POP_CTRL, num); +} + + +void CODE_pop_optional(short num) +{ + LAST_CODE; + + use_stack(-1); + + #ifdef DEBUG + printf("POP OPTIONAL #%d\n", (-1) - num); + #endif + write_ZZxx(C_POP_OPTIONAL, num); +} + +#endif /* PROJECT_COMP */ + + +void CODE_push_array(short nparam) +{ + LAST_CODE; + + use_stack(1 - nparam); + + write_ZZxx(C_PUSH_ARRAY, nparam); +} + + +void CODE_push_global(short global, bool is_static, bool is_function) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH %s %d\n", is_static ? "STATIC" : "DYNAMIC", global); + #endif + + if (is_function) + write_Z8xx(C_PUSH_FUNCTION, global); + else if (is_static) + write_Z8xx(C_PUSH_STATIC, global); + else + write_Z8xx(C_PUSH_DYNAMIC, global); +} + + +#ifdef PROJECT_COMP + +void CODE_pop_global(short global, bool is_static) +{ + LAST_CODE; + + use_stack(-1); + + #ifdef DEBUG + printf("POP %s %d\n", is_static ? "STATIC" : "DYNAMIC", global); + #endif + + if (is_static) + write_Z8xx(C_POP_STATIC, global); + else + write_Z8xx(C_POP_DYNAMIC, global); +} + +#endif + +/* +void CODE_push_symbol(short symbol) +{ + LAST_CODE; + + use_stack(0); + + #ifdef DEBUG + printf("PUSH SYMBOL %s\n", TABLE_get_symbol_name(CURRENT_CLASS->table, symbol)); + #endif + + write_short(C_PUSH_SYMBOL); + write_short(symbol); +} + + +void CODE_pop_symbol(short symbol) +{ + LAST_CODE; + + use_stack(-2); + + #ifdef DEBUG + printf("POP SYMBOL %s\n", TABLE_get_symbol_name(CURRENT_CLASS->table, symbol)); + #endif + + write_short(C_POP_SYMBOL); + write_short(symbol); +} +*/ + + +void CODE_push_unknown(short symbol) +{ + LAST_CODE; + + use_stack(0); + +#ifdef DEBUG + #ifdef PROJECT_EXEC + printf("PUSH UNKNOWN %s\n", CURRENT_CLASS->unknown[symbol]); + #else + printf("PUSH UNKNOWN %s\n", TABLE_get_symbol_name(CURRENT_CLASS->table, symbol)); + #endif +#endif + + write_short(C_PUSH_UNKNOWN); + write_short(symbol); +} + +void CODE_push_unknown_event(short symbol) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH UNKNOWN EVENT %s\n", TABLE_get_symbol_name(CURRENT_CLASS->table, symbol)); + #endif + + write_ZZxx(C_PUSH_EVENT, 0xFF); + write_short(symbol); +} + + +#ifdef PROJECT_COMP + +void CODE_pop_unknown(short symbol) +{ + LAST_CODE; + + use_stack(-2); + + #ifdef DEBUG + printf("POP UNKNOWN %s\n", TABLE_get_symbol_name(CURRENT_CLASS->table, symbol)); + #endif + + write_short(C_POP_UNKNOWN); + write_short(symbol); +} + +#endif + + +void CODE_push_class(short class) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH CLASS %d\n", class); + #endif + + write_Z8xx(C_PUSH_CLASS, class); + + #ifdef PROJECT_COMP + CURRENT_CLASS->class[class].used = TRUE; + #endif +} + +#ifdef PROJECT_COMP + +void CODE_jump() +{ + LAST_CODE; + + #ifdef DEBUG + printf("JUMP\n"); + #endif + write_short(C_JUMP); + write_short(0); +} + +void CODE_gosub(int ctrl_local) +{ + LAST_CODE; + + #ifdef DEBUG + printf("GOSUB\n"); + #endif + write_ZZxx(C_GOSUB, ctrl_local); + write_short(0); +} + +void CODE_on(uchar num) +{ + LAST_CODE; + + use_stack(-1); + + #ifdef DEBUG + printf("ON\n"); + #endif + write_ZZxx(C_ON, num); +} + + +void CODE_jump_if_true() +{ + /* + ushort *last_code = get_last_code(); + ushort op; + + if (last_code && PCODE_is(*last_code, C_NOT)) + { + remove_last(); + op = C_JUMP_IF_FALSE; + } + else + op = C_JUMP_IF_TRUE; + */ + + use_stack(-1); + + #ifdef DEBUG + printf("JUMP IF TRUE\n"); + #endif + + LAST_CODE; + + write_short(C_JUMP_IF_TRUE); + /**pos = CODE_get_current_pos();*/ + write_short(0); +} + + +void CODE_jump_if_false() +{ + /* + ushort *last_code = get_last_code(); + ushort op; + + if (last_code && PCODE_is(*last_code, C_NOT)) + { + remove_last(); + op = C_JUMP_IF_TRUE; + } + else + op = C_JUMP_IF_FALSE; + */ + + use_stack(-1); + + #ifdef DEBUG + printf("JUMP IF FALSE\n"); + #endif + + LAST_CODE; + + write_short(C_JUMP_IF_FALSE); + /**pos = CODE_get_current_pos();*/ + write_short(0); +} + + +void CODE_jump_first(short local) +{ + LAST_CODE; + + use_stack(-2); + + #ifdef DEBUG + printf("JUMP FIRST LOCAL %d\n", local); + #endif + + write_ZZxx(C_JUMP_FIRST, local); +} + + +void CODE_jump_next(void) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("JUMP NEXT\n"); + #endif + write_short(C_JUMP_NEXT); + /**pos = CODE_get_current_pos();*/ + write_short(0); +} + + +void CODE_jump_length(ushort src, ushort dst) +{ + if (src >= (cur_func->ncode - 1)) + return; + + int diff = (int)dst - (int)src; + + if (diff < -32768 || diff > 32767) + THROW("Jump is too far"); + + if (cur_func->code[src] == C_BREAK) + cur_func->code[src + 2] = (short)(diff - 3); //dst - (src + 2) - 1; + else if (cur_func->code[src] == C_NOP) + cur_func->code[src] = (short)diff; //dst - src; + else + cur_func->code[src + 1] = (short)(diff - 2); //dst - (src + 1) - 1; +} + + +void CODE_first(short local) +{ + LAST_CODE; + + use_stack(-1); + + #ifdef DEBUG + printf("ENUM FIRST LOCAL %d\n", local); + #endif + + write_ZZxx(C_FIRST, local); +} + + +void CODE_next(bool drop) +{ + LAST_CODE; + + use_stack(drop ? 0 : 1); + + #ifdef DEBUG + printf("ENUM NEXT%s\n", drop ? " DROP" : ""); + #endif + + write_ZZxx(C_NEXT, drop ? 1 : 0); + write_short(0); +} + +#endif /* PROJECT_COMP */ + +void CODE_op(short op, short subcode, short nparam, bool fixed) +{ + if (op == C_ADD || op == C_SUB) + { + PCODE *last_code; + short value, value2; + + last_code = get_last_code(); + + if (last_code && ((*last_code & 0xF000) == C_PUSH_QUICK)) + { + value = *last_code & 0xFFF; + if (value >= 0x800) value |= 0xF000; + if (op == C_SUB) value = (-value); + + #ifdef DEBUG + printf("ADD QUICK %d\n", value); + #endif + + *last_code = C_ADD_QUICK | (value & 0x0FFF); + + use_stack(1 - nparam); + + // Now, look if we are PUSH QUICK then ADD QUICK + + last_code = get_last_code2(); + if (last_code && ((*last_code & 0xF000) == C_PUSH_QUICK)) + { + value2 = *last_code & 0xFFF; + if (value2 >= 0x800) value2 |= 0xF000; + value += value2; + + if (value >= -2048L && value < 2048L) + { + *last_code = C_PUSH_QUICK | (value & 0x0FFF); + CODE_undo(); + } + } + + return; + } + } + + LAST_CODE; + + use_stack(1 - nparam); + + #ifdef DEBUG + printf("OP %d (%d)\n", op, nparam); + #endif + + if (fixed) + write_ZZxx(op, subcode); + else + write_ZZxx(op, nparam); +} + + +void CODE_push_me(bool debug) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH ME\n"); + #endif + + write_ZZxx(C_PUSH_ME, debug ? 1 : 0); +} + + +void CODE_push_super(bool debug) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH SUPER\n"); + #endif + + write_ZZxx(C_PUSH_ME, debug ? 3 : 2); +} + + +void CODE_push_last() +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH LAST\n"); + #endif + + write_ZZxx(C_PUSH_MISC, CPM_LAST); +} + + +void CODE_push_null() +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH NULL\n"); + #endif + + write_ZZxx(C_PUSH_MISC, CPM_NULL); +} + + +void CODE_push_void_string() +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH VOID STRING\n"); + #endif + + write_ZZxx(C_PUSH_MISC, CPM_STRING); +} + + + +/* +static bool change_last_call(ushort flag) +{ + ushort *last_code = get_last_code(); + + if (!last_code) + return FALSE; + + if ((*last_code & 0xFF00) == C_CALL) + { + *last_code = *last_code | flag; + return TRUE; + } + else if ((ushort)((*last_code) & 0xFF00) >= (ushort)CODE_FIRST_SUBR) + { + *last_code = *last_code | flag; + return TRUE; + } + else if (((*last_code & 0xFF00) == C_DROP) && flag == CODE_CALL_DROP) + + return FALSE; +} +*/ + +void CODE_dup(void) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("DUP\n"); + #endif + + write_short(C_DUP); +} + + +void CODE_return(int return_value) +{ + LAST_CODE; + + if (return_value == 1) + use_stack(-1); + + write_ZZxx(C_RETURN, return_value); + + #ifdef DEBUG + printf("RETURN (%d)\n", return_value); + #endif +} + + +#ifdef PROJECT_COMP + +void CODE_quit(bool ret) +{ + LAST_CODE; + + #ifdef DEBUG + printf("QUIT (%d)\n", ret); + #endif + + if (ret) + use_stack(-1); + + write_ZZxx(C_QUIT, ret ? 3 : 0); +} + + +void CODE_stop(void) +{ + LAST_CODE; + + #ifdef DEBUG + printf("STOP\n"); + #endif + + write_ZZxx(C_QUIT, 1); +} + +#endif /* PROJECT_COMP */ + + +void CODE_push_char(uchar car) +{ + LAST_CODE; + + use_stack(1); + write_ZZxx(C_PUSH_CHAR, car); + + #ifdef DEBUG + printf("PUSH CHAR %d\n", car); + #endif +} + + +void CODE_push_void(void) +{ + LAST_CODE; + + use_stack(1); + write_ZZxx(C_PUSH_MISC, CPM_VOID); + + #ifdef DEBUG + printf("PUSH VOID\n"); + #endif +} + + +#ifdef PROJECT_COMP + +void CODE_stop_event(void) +{ + LAST_CODE; + + write_ZZxx(C_QUIT, 2); + + #ifdef DEBUG + printf("STOP EVENT\n"); + #endif +} + +#endif + + +void CODE_subr(short subr, short nparam, short optype, bool fixed) +{ + LAST_CODE; + + use_stack(1 - nparam); + + #ifdef DEBUG + printf("SUBR %d (%d)\n", subr, nparam); + #endif + + if (optype == 0) + { + if (fixed) + nparam = 0; + } + else + { + nparam = optype; + } + + subr += CODE_FIRST_SUBR; + write_short(((subr & 0xFF) << 8) | (nparam & 0xFF)); +} + + +/*void CODE_subr_output(short subr, short nparam, int output) +{ + LAST_CODE; + + use_stack(output - nparam); + + #ifdef DEBUG + printf("SUBR OUTPUT %d %d (%d)\n", output, subr, nparam); + #endif + + subr += CODE_FIRST_SUBR; + write_short(((subr & 0xFF) << 8) | (nparam & 0xFF)); +}*/ + + +void CODE_call(short nparam) +{ + LAST_CODE; + + use_stack(-nparam); + + #ifdef DEBUG + printf("CALL ( %d )\n", nparam); + #endif + + write_ZZxx(C_CALL, nparam); +} + +void CODE_byref(uint64_t byref) +{ + LAST_CODE; + int n; + + #ifdef DEBUG + printf("BYREF\n"); + #endif + + if (byref >> 48) + n = 3; + else if (byref >> 32) + n = 2; + else if (byref >> 16) + n = 1; + else + n = 0; + + write_ZZxx(C_BYREF, n); + while (n >= 0) + { + write_short(byref & 0xFFFF); + byref >>= 16; + n--; + } +} + +void CODE_call_byref(short nparam, uint64_t byref) +{ + LAST_CODE; + int i, n; + + use_stack(-nparam); + + n = 0; + for (i = 0; i < nparam; i++) + { + if (byref & (1ULL << i)) + n++; + } + use_stack(n); + + #ifdef DEBUG + printf("CALL ( %d )\n", nparam); + #endif + + write_ZZxx(C_CALL, nparam); + CODE_byref(byref); +} + + +/*void CODE_push_return(void) +{ + LAST_CODE; + + use_stack(1); + write_short(C_PUSH_RETURN); + + #ifdef DEBUG + printf("PUSH RETURN\n"); + #endif +}*/ + + +#ifdef PROJECT_COMP + +void CODE_try(void) +{ + LAST_CODE; + + write_short(C_TRY); + write_short(0); + + #ifdef DEBUG + printf("TRY\n"); + #endif +} + + +void CODE_end_try(void) +{ + LAST_CODE; + + write_short(C_END_TRY); + + #ifdef DEBUG + printf("END TRY\n"); + #endif +} + + +void CODE_catch(void) +{ + LAST_CODE; + + write_short(C_CATCH); + + #ifdef DEBUG + printf("CATCH\n"); + #endif +} + +#endif + + +void CODE_drop(void) +{ + //ushort *last_code = get_last_code(); + //ushort subr; + + use_stack(-1); + + #ifdef DEBUG + printf("DROP\n"); + #endif + + /*if (last_code) + { + switch(*last_code & 0xFF00) + { + case C_DROP: + *last_code = (*last_code & 0xFF00) + (*last_code & 0xFF) + 1; + return; + + case C_CALL: + *last_code |= CODE_CALL_VOID; + return; + + default: + subr = (*last_code) >> 8; + if (subr >= CODE_FIRST_SUBR && subr <= CODE_LAST_SUBR && (!(*last_code & CODE_CALL_VOID))) + { + *last_code |= CODE_CALL_VOID; + return; + } + } + }*/ + + //THROW("Internal compiler error: Bad stack drop!"); + + LAST_CODE; + + write_ZZxx(C_DROP, 1); +} + + +/*void CODE_push_special(short spec) +{ + LAST_CODE; + + use_stack(0); + + #ifdef DEBUG + printf("PUSH SPECIAL %d\n", spec); + #endif + + write_ZZxx(C_PUSH_SPECIAL, spec); +} +*/ + +#ifdef PROJECT_COMP + +void CODE_push_event(short event) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH EVENT %d\n", event); + #endif + + write_ZZxx(C_PUSH_EVENT, event); +} + +void CODE_push_extern(short index) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH EXTERN %d\n", index); + #endif + + write_ZZxx(C_PUSH_EXTERN, index); +} + +void CODE_new(ushort nparam, bool array, bool event) +{ + LAST_CODE; + + use_stack(1 - nparam); + + #ifdef DEBUG + printf("NEW %s (%d)\n", (array ? "ARRAY" : (event ? "EVENT" : "")), nparam); + #endif + + if (array) + nparam |= CODE_NEW_ARRAY; + + if (event) + nparam |= CODE_NEW_EVENT; + + write_ZZxx(C_NEW, nparam); +} + +#endif + +void CODE_push_boolean(bool value) +{ + LAST_CODE; + + use_stack(1); + write_ZZxx(C_PUSH_MISC, value ? CPM_TRUE : CPM_FALSE); + + #ifdef DEBUG + printf("PUSH %s\n", value ? "TRUE" : "FALSE"); + #endif +} + +void CODE_push_inf(bool neg) +{ + LAST_CODE; + + use_stack(1); + + #ifdef DEBUG + printf("PUSH %cINF\n", neg ? '-' : '+'); + #endif + + write_ZZxx(C_PUSH_MISC, neg ? CPM_MINF : CPM_PINF); +} + +void CODE_push_complex(void) +{ + LAST_CODE; + + #ifdef DEBUG + printf("PUSH COMPLEX\n"); + #endif + + write_ZZxx(C_PUSH_MISC, CPM_COMPLEX); +} + +void CODE_push_vargs(void) +{ + LAST_CODE; + + #ifdef DEBUG + printf("PUSH VARGS\n"); + #endif + + write_ZZxx(C_PUSH_MISC, CPM_VARGS); +} + +void CODE_drop_vargs(void) +{ + LAST_CODE; + + #ifdef DEBUG + printf("DROP VARGS\n"); + #endif + + write_ZZxx(C_PUSH_MISC, CPM_DROP_VARGS); +} + +#ifdef CODE_DUMP + +void CODE_dump(PCODE *code, int count) +{ + int i; + + printf("\n"); + + for (i = 0; i < count;) + i += PCODE_dump(stdout, i, &code[i]); + + printf("\n"); + +} + +#endif + +void CODE_string_add(void) +{ + LAST_CODE; + + #ifdef DEBUG + printf("STOP\n"); + #endif + + write_ZZxx(C_QUIT, 3); +} + diff --git a/main/share/gb_common.h b/main/share/gb_common.h new file mode 100644 index 00000000..c6cf5331 --- /dev/null +++ b/main/share/gb_common.h @@ -0,0 +1,212 @@ +/*************************************************************************** + + gb_common.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_COMMON_H +#define __GB_COMMON_H + +#include "config.h" + +#ifdef _GNU_SOURCE +#undef _GNU_SOURCE +#endif +#define _GNU_SOURCE 500 + +#define _FILE_OFFSET_BITS 64 + +#define __STDC_FORMAT_MACROS 1 +#define _ISOC9X_SOURCE 1 +#define _ISOC99_SOURCE 1 +#define __USE_ISOC99 1 +#define __USE_ISOC9X 1 + +#if defined(__cplusplus) +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__GNU_LIBRARY__) || defined(OS_BSD) + +#include +#define HAVE_GETOPT_LONG 1 + +#endif + +#if defined(OS_CYGWIN) + + typedef void (*sighandler_t) (int); + typedef unsigned long ulong; + +#endif + +#if defined(OS_BSD) + + /* sighandler_t is replaced by sig_t */ + #define sighandler_t sig_t + + typedef unsigned long ulong; + + #if defined(UINTPTR_MAX) && defined(UINT64_MAX) && (UINTPTR_MAX == UINT64_MAX) + #define __WORDSIZE 64 + #else + #define __WORDSIZE 32 + #endif + +#endif + +#ifdef OS_SOLARIS + +/* PGS: The following #define prevents /usr/include/sys/mman.h on solaris + from #define'ing PRIVATE to 0x20, thus breaking Gambas. + Perhaps Gambas should use a different name? + BM: I don't use PRIVATE anymore! +*/ + #ifdef _POSIX_C_SOURCE + /* PGS: Stop compiler warnings when gcc on solaris does remember to define + _POSIX_C_SOURCE, e.g. when compiling qt related files. */ + #undef _POSIX_C_SOURCE + #endif + + #define _POSIX_C_SOURCE 3 + /* Get prototype for alloca() */ + #include + /* Get definition for index() */ + #include + +#endif + +#ifdef OS_MACOSX + +#include +#undef bool + +#endif + +#ifndef FALSE + enum + { + FALSE = 0, + TRUE = 1 + }; +#endif + +#if !defined(__cplusplus) + + #define bool char + +#endif + +typedef + unsigned char uchar; + +typedef + size_t offset_t; + +#define PUBLIC +#define INLINE __inline__ +#define EXTERN extern +#define PACKED __attribute__((packed)) +#define NORETURN __attribute__((noreturn)) +#define CONST __attribute__((const)) + +#if __WORDSIZE == 64 +#define OS_64BITS 1 +#endif + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define OS_LITTLE_ENDIAN 1 +#else +#define OS_BIG_ENDIAN 1 +#endif + +#ifndef LLONG_MAX +#define LLONG_MAX 9223372036854775807LL +#endif + +#ifndef PATH_MAX +#define PATH_MAX 4095 +#endif + +#ifndef MAXPATHLEN +#define MAXPATHLEN 4095 +#endif + +#define CLEAR(s) (memset(s, 0, sizeof(*s))) + +/* Workaround spurious gcc warnings */ +#define NO_WARNING(var) var = var + +#ifndef offsetof + #define offsetof(_type, _arg) ((size_t)&(((_type *)0)->_arg)) +#endif + +#define Max(a, b) ({ __typeof__(a) _a = (a), _b = (b); _a > _b ? _a : _b; }) +#define Min(a, b) ({ __typeof__(a) _a = (a), _b = (b); _a < _b ? _a : _b; }) +#define MinMax(v, a, b) ({ __typeof__(v) _v = (v), _a = (a), _b = (b); _v < _a ? _a : (_v > _b ? _b : _v); }) + +#if (defined (__i386__) || defined (__x86_64__)) && defined (__GNUC__) && __GNUC__ >= 2 + #define BREAKPOINT() { __asm__ __volatile__ ("int $03"); } +#elif (defined (_MSC_VER) || defined (__DMC__)) && defined (_M_IX86) + #define BREAKPOINT() { __asm int 3h }G_STMT_END +#elif defined (__alpha__) && !defined(__osf__) && defined (__GNUC__) && __GNUC__ >= 2 + #define BREAKPOINT() { __asm__ __volatile__ ("bpt"); } +#else /* !__i386__ && !__alpha__ */ + #define BREAKPOINT() { raise(SIGTRAP); } +#endif /* __i386__ */ + +#define COPYRIGHT "(c) Benoît Minisini\n\n" \ + "This program is free software; you can redistribute it and/or \n" \ + "modify it under the terms of the GNU General Public License as \n" \ + "published by the Free Software Foundation; either version 2, or \n" \ + "(at your option) any later version.\n\n" + +//#define LIKELY(_x) __builtin_expect((_x), 1) +//#define UNLIKELY(_x) __builtin_expect((_x), 0) +#define LIKELY(_x) (_x) +#define UNLIKELY(_x) (_x) + +#define $(_x) _x + +#define RESTART_SYSCALL(_code) \ + for(;;) \ + { \ + errno = 0; \ + if ((_code) >= 0 || (errno != EINTR)) \ + break; \ + } if (errno) + +#endif /* __COMMON_H */ diff --git a/main/share/gb_common_buffer.h b/main/share/gb_common_buffer.h new file mode 100644 index 00000000..6d6fc6e9 --- /dev/null +++ b/main/share/gb_common_buffer.h @@ -0,0 +1,46 @@ +/*************************************************************************** + + gb_common_buffer.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_COMMON_BUFFER_H +#define __GB_COMMON_BUFFER_H + +#define COMMON_BUF_MAX 512 + +#ifndef __COMMON_BUFFER_C +EXTERN int COMMON_pos; +EXTERN int COMMON_len; +EXTERN char COMMON_buffer[]; +#endif + +void COMMON_init(void); + +void COMMON_buffer_init(const char *str, int len); +int COMMON_get_char(void); +int COMMON_last_char(void); +int COMMON_look_char(void); +int COMMON_put_char(char c); +void COMMON_jump_space(void); +char *COMMON_get_current(void); +int COMMON_get_size_left(void); +bool COMMON_has_string(const char *str, int len); +#endif diff --git a/main/share/gb_common_buffer_temp.h b/main/share/gb_common_buffer_temp.h new file mode 100644 index 00000000..72113671 --- /dev/null +++ b/main/share/gb_common_buffer_temp.h @@ -0,0 +1,113 @@ +/*************************************************************************** + + gb_common_buffer_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __COMMON_BUFFER_C + +#include "gb_common.h" +#include "gb_common_buffer.h" + +char COMMON_buffer[COMMON_BUF_MAX]; +int COMMON_pos; +int COMMON_len; + +static char *common_buffer; +static int common_last; + +void COMMON_buffer_init(const char *str, int len) +{ + common_buffer = (char *)str; + COMMON_len = len; + COMMON_pos = 0; + common_last = (-1); +} + + +int COMMON_look_char(void) +{ + if (COMMON_pos >= COMMON_len) + return (-1); + + return (unsigned char)(common_buffer[COMMON_pos]); +} + + +int COMMON_get_char(void) +{ + if (COMMON_pos >= COMMON_len) + common_last = (-1); + else + common_last = (unsigned char)(common_buffer[COMMON_pos++]); + + return common_last; +} + + +int COMMON_last_char(void) +{ + return common_last; +} + + +int COMMON_put_char(char c) +{ + if (COMMON_pos >= COMMON_len) + return (-1); + + common_buffer[COMMON_pos++] = c; + return 0; +} + + +void COMMON_jump_space(void) +{ + int c; + + for(;;) + { + c = COMMON_look_char(); + if (c <= 0 || !isspace(c)) + break; + COMMON_pos++; + } +} + + +char *COMMON_get_current(void) +{ + return &common_buffer[COMMON_pos]; +} + + +int COMMON_get_size_left(void) +{ + return COMMON_len - COMMON_pos; +} + + +bool COMMON_has_string(const char *str, int len) +{ + if (COMMON_get_size_left() < len) + return FALSE; + + return memcmp(&common_buffer[COMMON_pos], str, len) == 0; +} diff --git a/main/share/gb_common_case.h b/main/share/gb_common_case.h new file mode 100644 index 00000000..4fc98bae --- /dev/null +++ b/main/share/gb_common_case.h @@ -0,0 +1,60 @@ +/*************************************************************************** + + gb_common_case.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_COMMON_CASE_H +#define __GB_COMMON_CASE_H + +#ifdef __CYGWIN__ +#include +#endif + +#include +#include + +extern const unsigned char COMMON_tolower[]; +extern const unsigned char COMMON_toupper[]; + +int COMMON_strcasecmp(const char *s1, const char *s2); +int COMMON_strncasecmp(const char *s1, const char *s2, size_t n); + +#ifdef tolower +#undef tolower +#endif +#ifdef toupper +#undef toupper +#endif +#ifdef strcasecmp +#undef strcasecmp +#endif +#ifdef strncasecmp +#undef strncasecmp +#endif + +#define tolower(_c) (COMMON_tolower[(unsigned char)(_c)]) +#define toupper(_c) (COMMON_toupper[(unsigned char)(_c)]) +#define strcasecmp(_s1, _s2) (COMMON_strcasecmp((_s1), (_s2))) +#define strncasecmp(_s1, _s2, _n) (COMMON_strncasecmp((_s1), (_s2), (_n))) + +#define NO_GAMBAS_CASE_REPLACEMENT + +#endif diff --git a/main/share/gb_common_case_temp.h b/main/share/gb_common_case_temp.h new file mode 100644 index 00000000..cc6bb50b --- /dev/null +++ b/main/share/gb_common_case_temp.h @@ -0,0 +1,104 @@ +/*************************************************************************** + + gb_common_case_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __COMMON_CASE_C + +#include +#include "gb_common.h" + +const unsigned char COMMON_tolower[256] = + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" + " !\"#$%&'()*+,-./" + "0123456789:;<=>?" + "@abcdefghijklmno" + "pqrstuvwxyz[\\]^_" + "`abcdefghijklmno" + "pqrstuvwxyz{|}~\x7F" + "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F" + "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F" + "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF" + "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF" + "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF" + "\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF" + "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF" + "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"; + +const unsigned char COMMON_toupper[256] = + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" + " !\"#$%&'()*+,-./" + "0123456789:;<=>?" + "@ABCDEFGHIJKLMNO" + "PQRSTUVWXYZ[\\]^_" + "`ABCDEFGHIJKLMNO" + "PQRSTUVWXYZ{|}~\x7F" + "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F" + "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F" + "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF" + "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF" + "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF" + "\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF" + "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF" + "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"; + + +int COMMON_strcasecmp(const char *s1, const char *s2) +{ + register offset_t i; + register int d; + register char c; + + for (i = 0;; i++) + { + c = COMMON_tolower[(unsigned char)s1[i]]; + d = c - COMMON_tolower[(unsigned char)s2[i]]; + if (d < 0) + return -1; + else if (d > 0) + return 1; + else if (c == 0) + return 0; + } +} + +int COMMON_strncasecmp(const char *s1, const char *s2, size_t n) +{ + register offset_t i; + register int d; + register char c; + + for (i = 0; i < n; i++) + { + c = COMMON_tolower[(unsigned char)s1[i]]; + d = c - COMMON_tolower[(unsigned char)s2[i]]; + if (d < 0) + return -1; + else if (d > 0) + return 1; + } + + return 0; +} + + diff --git a/main/share/gb_common_string.h b/main/share/gb_common_string.h new file mode 100644 index 00000000..21a61989 --- /dev/null +++ b/main/share/gb_common_string.h @@ -0,0 +1,37 @@ +/*************************************************************************** + + gb_common_string.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_COMMON_STRING_H +#define __GB_COMMON_STRING_H + +bool STRING_equal_same(const char *str1, const char *str2, int len); +bool STRING_equal_ignore_case_same(const char *str1, const char *str2, int len); +int STRING_compare(const char *str1, int len1, const char *str2, int len2); +int STRING_compare_ignore_case(const char *str1, int len1, const char *str2, int len2); + +// valgrind says that STRING_equal_same() is globally faster than memcmp_sse4, so... +//#define STRING_equal_same(_str1, _str2, _len) (memcmp(_str1, _str2, _len) == 0) +#define STRING_equal(_str1, _len1, _str2, _len2) ((_len1) == (_len2) && STRING_equal_same(_str1, _str2, _len1)) +#define STRING_equal_ignore_case(_str1, _len1, _str2, _len2) ((_len1) == (_len2) && STRING_equal_ignore_case_same(_str1, _str2, _len1)) + +#endif diff --git a/main/share/gb_common_string_temp.h b/main/share/gb_common_string_temp.h new file mode 100644 index 00000000..222db233 --- /dev/null +++ b/main/share/gb_common_string_temp.h @@ -0,0 +1,197 @@ +/*************************************************************************** + + gb_common_string_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common_case.h" + +bool STRING_equal_same(const char *str1, const char *str2, int len) +{ + static const void *jump[8] = { &&__LEN_0, &&__LEN_1, &&__LEN_2, &&__LEN_3, &&__LEN_4, &&__LEN_5, &&__LEN_6, &&__LEN_7 }; + + //return len == 0 || __builtin_memcmp(str1, str2, len) == 0; + + #if defined(ARCH_X86_64) || defined(ARCH_X86) + + for(;;) + { + if (len < 8) break; + + if (*((int64_t *)str1) ^ *((int64_t *)str2)) + return FALSE; + str1 += 8; + str2 += 8; + len -= 8; + + if (len < 8) break; + + if (*((int64_t *)str1) ^ *((int64_t *)str2)) + return FALSE; + str1 += 8; + str2 += 8; + len -= 8; + + if (len < 8) break; + + if (*((int64_t *)str1) ^ *((int64_t *)str2)) + return FALSE; + str1 += 8; + str2 += 8; + len -= 8; + + if (len < 8) break; + + if (*((int64_t *)str1) ^ *((int64_t *)str2)) + return FALSE; + str1 += 8; + str2 += 8; + len -= 8; + } + + #else + + while (len >= 8) + { + if (str1[0] ^ str2[0]) + return FALSE; + if (str1[1] ^ str2[1]) + return FALSE; + if (str1[2] ^ str2[2]) + return FALSE; + if (str1[3] ^ str2[3]) + return FALSE; + if (str1[4] ^ str2[4]) + return FALSE; + if (str1[5] ^ str2[5]) + return FALSE; + if (str1[6] ^ str2[6]) + return FALSE; + if (str1[7] ^ str2[7]) + return FALSE; + str1 += 8; + str2 += 8; + len -= 8; + } + + #endif + + goto *jump[len]; + +__LEN_7: if (str1[6] ^ str2[6]) return FALSE; +__LEN_6: if (str1[5] ^ str2[5]) return FALSE; +__LEN_5: if (str1[4] ^ str2[4]) return FALSE; +__LEN_4: if (str1[3] ^ str2[3]) return FALSE; +__LEN_3: if (str1[2] ^ str2[2]) return FALSE; +__LEN_2: if (str1[1] ^ str2[1]) return FALSE; +__LEN_1: if (str1[0] ^ str2[0]) return FALSE; +__LEN_0: return TRUE; +} + +bool STRING_equal_ignore_case_same(const char *str1, const char *str2, int len) +{ + static const void *jump[8] = { &&__LEN_0, &&__LEN_1, &&__LEN_2, &&__LEN_3, &&__LEN_4, &&__LEN_5, &&__LEN_6, &&__LEN_7 }; + + #if defined(ARCH_X86_64) || defined(ARCH_X86) + while (len >= 8) + { + if (*((int64_t *)str1) ^ *((int64_t *)str2)) + { + if (toupper(str1[0]) ^ toupper(str2[0])) return FALSE; + if (toupper(str1[1]) ^ toupper(str2[1])) return FALSE; + if (toupper(str1[2]) ^ toupper(str2[2])) return FALSE; + if (toupper(str1[3]) ^ toupper(str2[3])) return FALSE; + if (toupper(str1[4]) ^ toupper(str2[4])) return FALSE; + if (toupper(str1[5]) ^ toupper(str2[5])) return FALSE; + if (toupper(str1[6]) ^ toupper(str2[6])) return FALSE; + if (toupper(str1[7]) ^ toupper(str2[7])) return FALSE; + } + + str1 += 8; + str2 += 8; + len -= 8; + } + #else + while (len >= 8) + { + if (str1[0] ^ str2[0] && toupper(str1[0]) ^ toupper(str2[0])) return FALSE; + if (str1[1] ^ str2[1] && toupper(str1[1]) ^ toupper(str2[1])) return FALSE; + if (str1[2] ^ str2[2] && toupper(str1[2]) ^ toupper(str2[2])) return FALSE; + if (str1[3] ^ str2[3] && toupper(str1[3]) ^ toupper(str2[3])) return FALSE; + if (str1[4] ^ str2[4] && toupper(str1[4]) ^ toupper(str2[4])) return FALSE; + if (str1[5] ^ str2[5] && toupper(str1[5]) ^ toupper(str2[5])) return FALSE; + if (str1[6] ^ str2[6] && toupper(str1[6]) ^ toupper(str2[6])) return FALSE; + if (str1[7] ^ str2[7] && toupper(str1[7]) ^ toupper(str2[7])) return FALSE; + str1 += 8; + str2 += 8; + len -= 8; + } + #endif + + goto *jump[len]; + +__LEN_7: if (str1[6] ^ str2[6] && toupper(str1[6]) ^ toupper(str2[6])) return FALSE; +__LEN_6: if (str1[5] ^ str2[5] && toupper(str1[5]) ^ toupper(str2[5])) return FALSE; +__LEN_5: if (str1[4] ^ str2[4] && toupper(str1[4]) ^ toupper(str2[4])) return FALSE; +__LEN_4: if (str1[3] ^ str2[3] && toupper(str1[3]) ^ toupper(str2[3])) return FALSE; +__LEN_3: if (str1[2] ^ str2[2] && toupper(str1[2]) ^ toupper(str2[2])) return FALSE; +__LEN_2: if (str1[1] ^ str2[1] && toupper(str1[1]) ^ toupper(str2[1])) return FALSE; +__LEN_1: if (str1[0] ^ str2[0] && toupper(str1[0]) ^ toupper(str2[0])) return FALSE; +__LEN_0: return TRUE; +} + +int STRING_compare(const char *str1, int len1, const char *str2, int len2) +{ + uint i; + int len = len1 < len2 ? len1 : len2; + int diff; + register unsigned char c1, c2; + + for (i = 0; i < len; i++) + { + c1 = str1[i]; + c2 = str2[i]; + if (c1 > c2) return 1; + if (c1 < c2) return -1; + } + + diff = len1 - len2; + return (diff < 0) ? (-1) : (diff > 0) ? 1 : 0; +} + + +int STRING_compare_ignore_case(const char *str1, int len1, const char *str2, int len2) +{ + uint i; + int len = len1 < len2 ? len1 : len2; + int diff; + register unsigned char c1, c2; + + for (i = 0; i < len; i++) + { + c1 = tolower(str1[i]); + c2 = tolower(str2[i]); + if (c1 > c2) return 1; + if (c1 < c2) return -1; + } + + diff = len1 - len2; + return (diff < 0) ? (-1) : (diff > 0) ? 1 : 0; +} diff --git a/main/share/gb_common_swap.h b/main/share/gb_common_swap.h new file mode 100644 index 00000000..86ba057c --- /dev/null +++ b/main/share/gb_common_swap.h @@ -0,0 +1,42 @@ +/*************************************************************************** + + gb_common_swap.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_COMMON_SWAP_H +#define __GB_COMMON_SWAP_H + +void SWAP_int(int *val); +void SWAP_ints(int *val, int n); + +void SWAP_short(short *val); +void SWAP_double(double *val); + +#define SWAP_float(_val) SWAP_int((int *)_val) +#define SWAP_int64(_val) SWAP_double((double *)(void *)_val) + +#if OS_64BITS +#define SWAP_pointer(_val) SWAP_int64(_val) +#else +#define SWAP_pointer(_val) SWAP_int(((int *)(void *)_val)) +#endif + +#endif diff --git a/main/share/gb_common_swap_temp.h b/main/share/gb_common_swap_temp.h new file mode 100644 index 00000000..35401e04 --- /dev/null +++ b/main/share/gb_common_swap_temp.h @@ -0,0 +1,71 @@ +/*************************************************************************** + + gb_common_swap_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +void SWAP_int(int *val) +{ + char *p = (char *)val; + char t1, t2; + + t1 = p[0]; + t2 = p[1]; + p[0] = p[3]; + p[1] = p[2]; + p[3] = t1; + p[2] = t2; +} + +void SWAP_ints(int *val, int n) +{ + while (n > 0) + { + SWAP_int(val); + val++; + n--; + } +} + +void SWAP_short(short *val) +{ + char *p = (char *)val; + char t; + + t = p[0]; + p[0] = p[1]; + p[1] = t; +} + +void SWAP_double(double *val) +{ + char *p = (char *)val; + char t; + int i, j; + + for (i = 0; i < 4; i++) + { + j = i ^ 7; + t = p[i]; + p[i] = p[j]; + p[j] = t; + } +} + diff --git a/main/share/gb_component.h b/main/share/gb_component.h new file mode 100644 index 00000000..0aa42da2 --- /dev/null +++ b/main/share/gb_component.h @@ -0,0 +1,71 @@ +/*************************************************************************** + + gb_component.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_COMPONENT_H +#define __GB_COMPONENT_H + +#include "config.h" + +typedef + struct { + int version; + void *func[0]; + } + INTERFACE; + +#define LIB_INIT "GB_INIT" +#define LIB_AFTER_INIT "GB_AFTER_INIT" +#define LIB_EXIT "GB_EXIT" +#define LIB_INTERFACE "GB_INTERFACE" +#define LIB_CLASS "GB_CLASSES" +#define LIB_OPTIONAL "GB_OPTIONAL_CLASSES" +#define LIB_INCLUDE "GB_INCLUDE" +#define LIB_SIGNAL "GB_SIGNAL" +#define LIB_INFO "GB_INFO" +#define LIB_NEED "GB_NEED" +#define LIB_GAMBAS "GB" +#define LIB_JIT "JIT" +#define LIB_MAIN "GB_MAIN" + +#ifdef DONT_USE_LTDL + #if defined(OS_MACOSX) + #define LIB_PATTERN "%s/%s.so" + #elif defined(OS_OPENBSD) + #define LIB_PATTERN "%s/%s." SHARED_LIBRARY_EXT ".0.0" + #elif defined(OS_CYGWIN) + #define LIB_PATTERN "%s/%s-0." SHARED_LIBRARY_EXT + #else + #define LIB_PATTERN "%s/%s." SHARED_LIBRARY_EXT + #endif +#else +#define LIB_PATTERN "%s/%s.la" +#endif + +#define ARCH_PATTERN "%s/%s.gambas" + +#define GAMBAS_LINK_PATH "/usr/bin/gbx" GAMBAS_VERSION_STRING + +#define GAMBAS_LIB_PATH "lib/gambas" GAMBAS_VERSION_STRING +#define GAMBAS_LIB64_PATH "lib64/gambas" GAMBAS_VERSION_STRING + +#endif diff --git a/main/share/gb_error_common.h b/main/share/gb_error_common.h new file mode 100644 index 00000000..750c77be --- /dev/null +++ b/main/share/gb_error_common.h @@ -0,0 +1,213 @@ +/*************************************************************************** + + gb_error_common.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_ERROR_COMMON_H +#define __GB_ERROR_COMMON_H + +typedef + struct { + char code; // Error code + char native; // A native method has raised an error + char free; // If 'msg' sould be freed + char _reserved; + void *cp; + void *fp; + void *pc; + char *msg; + } + ERROR_INFO; + +typedef + struct _ERROR_CONTEXT { + struct _ERROR_CONTEXT *prev; + struct _ERROR_HANDLER *handler; + ERROR_INFO info; + jmp_buf env; + char ret; + } + ERROR_CONTEXT; + +typedef + struct _ERROR_HANDLER { + struct _ERROR_HANDLER *prev; + ERROR_CONTEXT *context; + void (*handler)(); + intptr_t arg1; + intptr_t arg2; + } + ERROR_HANDLER; + +#ifdef NO_ERROR_HANDLER +#define ERROR_handler NULL +#endif + +#define ERROR_LEAVE_DONE ((ERROR_CONTEXT *)-1) + +#if DEBUG_ERROR + +#define TRY \ + { \ + ERROR_CONTEXT __err_context; \ + { \ + ERROR_debug("TRY %s %p\n", __FUNCTION__, &__err_context); \ + ERROR_depth++; \ + ERROR_enter(&__err_context); \ + __err_context.ret = setjmp(__err_context.env); \ + if (__err_context.ret == 0) + +/*#define CATCH \ + fprintf(stderr, "%p == %p ? %d\n", ERROR_current, __err, __err->ret); \ + if (__err->ret != 0 && (__err->ret = 2))*/ + +#define CATCH \ + if (__err_context.ret) { ERROR_depth--; ERROR_debug("CATCH %s %p\n", __FUNCTION__, &__err_context); ERROR_depth++; } \ + if (__err_context.ret) + +#define END_TRY \ + ERROR_depth--; \ + ERROR_debug("END TRY %s %p\n", __FUNCTION__, &__err_context); \ + ERROR_depth++; \ + ERROR_leave(&__err_context); \ + } \ + } + +#define PROPAGATE() fprintf(stderr, "PROPAGATE %s\n", __FUNCTION__), ERROR_propagate() + +#define ERROR_enter(_err) \ +do { \ + (_err)->prev = ERROR_current; \ + (_err)->info.code = 0; \ + (_err)->info.native = 0; \ + (_err)->handler = ERROR_handler; \ + ERROR_current = (_err); \ +} while(0) + +#define ERROR_leave(_err) \ +do { \ + ERROR_CONTEXT *_prev = (_err); \ + ERROR_depth--; \ + if (_prev->prev != ERROR_LEAVE_DONE) \ + { \ + ERROR_current = _prev->prev; \ + if (ERROR_current) \ + { \ + if (_prev->info.code) \ + { \ + ERROR_reset(&ERROR_current->info); \ + ERROR_current->info = _prev->info; \ + } \ + } \ + else \ + ERROR_reset(&_prev->info); \ + _prev->prev = ERROR_LEAVE_DONE; \ + } \ +} while(0) + +#else /* DEBUG_ERROR */ + +#define TRY \ + { \ + ERROR_CONTEXT __err_context; \ + { \ + ERROR_CONTEXT *__err = &__err_context; \ + ERROR_enter(__err); \ + __err->ret = setjmp(__err->env); \ + if (__err->ret == 0) + +#define CATCH \ + else + +#define END_TRY \ + ERROR_leave(__err); \ + } \ + } + +#define PROPAGATE() ERROR_propagate() + +#define ERROR_enter(_err) \ +do { \ + _err->prev = ERROR_current; \ + _err->info.code = 0; \ + _err->info.native = 0; \ + _err->handler = ERROR_handler; \ + ERROR_current = _err; \ +} while(0) + +#ifdef GB_JIT + +static void _error_leave(ERROR_CONTEXT *_err) +{ + ERROR_CONTEXT *_prev = _err; + if (_prev->prev != ERROR_LEAVE_DONE) + { + ERROR_current = _prev->prev; + if (ERROR_current) + { + if (_prev->info.code) + { + ERROR_reset(&ERROR_current->info); + ERROR_current->info = _prev->info; + ERROR_current->info.native = FALSE; + } + } + else + ERROR_reset(&_prev->info); + _prev->prev = ERROR_LEAVE_DONE; + } +} + +#define ERROR_leave(_err) _error_leave(_err) + +#else + +#define ERROR_leave(_err) \ +do { \ + ERROR_CONTEXT *_prev = (_err); \ + if (_prev->prev != ERROR_LEAVE_DONE) \ + { \ + ERROR_current = _prev->prev; \ + if (ERROR_current) \ + { \ + if (_prev->info.code) \ + { \ + ERROR_reset(&ERROR_current->info); \ + ERROR_current->info = _prev->info; \ + ERROR_current->info.native = FALSE; \ + } \ + } \ + else \ + ERROR_reset(&_prev->info); \ + _prev->prev = ERROR_LEAVE_DONE; \ + } \ +} while(0) + +#endif + +#endif + +#define ERROR (&__err_context) + +#define ERROR_in_catch(_err) ((_err)->ret) + +#endif + diff --git a/main/share/gb_file_share.h b/main/share/gb_file_share.h new file mode 100644 index 00000000..89b0a9a1 --- /dev/null +++ b/main/share/gb_file_share.h @@ -0,0 +1,141 @@ +/*************************************************************************** + + gb_file_share.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_FILE_H +#define __GB_FILE_H + +#include + +#include "gb_common.h" + +#ifdef PROJECT_EXEC + +#include "gambas.h" + +typedef + GB_FILE_STAT FILE_STAT; + +/* Constants for Stat() function */ + +#define GB_STAT_FILE 1 +#define GB_STAT_DIRECTORY 2 +#define GB_STAT_DEVICE 3 +#define GB_STAT_PIPE 4 +#define GB_STAT_SOCKET 5 +#define GB_STAT_LINK 6 + +#define GB_STAT_READ R_OK +#define GB_STAT_WRITE W_OK +#define GB_STAT_EXEC X_OK + +#define GB_STAT_USER 0 +#define GB_STAT_GROUP 1 +#define GB_STAT_OTHER 2 + +#define FILE_TEMP_PREFIX "/tmp/gambas.%d" +#define FILE_TEMP_DIR FILE_TEMP_PREFIX "/%d" +#define FILE_TEMP_FILE FILE_TEMP_DIR "/%d.tmp" +#define FILE_TEMP_PATTERN FILE_TEMP_DIR "/%s.tmp" + +#endif + +#ifndef GBX_INFO + +const char *FILE_cat(const char *path, ...); +char *FILE_buffer(void); +int FILE_buffer_length(void); +const char *FILE_get_dir(const char *path); +const char *FILE_get_name(const char *path); +const char *FILE_get_ext(const char *path); +const char *FILE_get_basename(const char *path); +/*PUBLIC const char *FILE_get(const char *path);*/ +const char *FILE_set_ext(const char *path, const char *ext); + +const char *FILE_getcwd(const char *subdir); +#define FILE_get_current_dir() FILE_getcwd(NULL) +void FILE_chdir(const char *path); + +const char *FILE_readlink(const char *link); +bool FILE_is_dir(const char *path); + +const char *FILE_find_gambas(void); + +void FILE_rename_ext(const char *src, const char *dst, bool unlink); +#define FILE_rename(_src, _dst) FILE_rename_ext(_src, _dst, FALSE) +#define FILE_rename_unlink(_src, _dst) FILE_rename_ext(_src, _dst, TRUE) + +void FILE_unlink(const char *path); + +char *FILE_get_home(void); +void FILE_exit(void); + +#ifdef PROJECT_EXEC + +void FILE_init(void); +void FILE_remove_temp_file(void); +void FILE_remove_temp_file_pid(pid_t pid); + +bool FILE_exist_follow(const char *path, bool follow); +#define FILE_exist(_path) FILE_exist_follow(_path, FALSE) +bool FILE_exist_real(const char *path); + +void FILE_stat(const char *path, FILE_STAT *info, bool follow); +void FILE_dir_first(const char *path, const char *pattern, int attr); +bool FILE_dir_next(char **path, int *len); + +void FILE_rmdir(const char *path); +void FILE_mkdir(const char *path); +void FILE_copy(const char *src, const char *dst); + +bool FILE_access(const char *path, int mode); +void FILE_link(const char *src, const char *dst); + +char *FILE_make_temp(int *len, const char *pattern); + +void FILE_recursive_dir(const char *dir, void (*found)(const char *), void (*afterfound)(const char *), int attr, bool follow); + +void FILE_make_path_dir(const char *path); + +int64_t FILE_free(const char *path); + +char *FILE_mode_to_string(mode_t mode); +mode_t FILE_mode_from_string(mode_t mode, const char *str); + +void FILE_chmod(const char *path, mode_t mode); +void FILE_chown(const char *path, const char *user); +void FILE_chgrp(const char *path, const char *group); + +#else + +bool FILE_exist(const char *path); +time_t FILE_get_time(const char *path); +bool FILE_copy(const char *src, const char *dst); + +#endif + +#define FILE_is_absolute(_path) (*(_path) == '/' || * (_path) == '~') +#define FILE_is_relative(_path) (!FILE_is_absolute(_path)) + +#endif + +#endif diff --git a/main/share/gb_file_temp.h b/main/share/gb_file_temp.h new file mode 100644 index 00000000..c2357d9a --- /dev/null +++ b/main/share/gb_file_temp.h @@ -0,0 +1,1287 @@ +/*************************************************************************** + + gb_file_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_FILE_C + +#include "config.h" + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_limit.h" +#include "gb_component.h" + +#include +#include +#include +#include +#include +#include + +#ifdef PROJECT_EXEC + +#include + +#include +#include + +#endif + +#include "gb_file.h" + +#define OPT_NLINK 1 +#define OPT_FSTATAT 1 + +#ifdef PROJECT_EXEC + +//FILE_STAT FILE_stat_info = { 0 }; + +static DIR *file_dir = NULL; +static char *file_pattern = NULL; +static char *file_path = NULL; +static bool file_dir_arch = FALSE; +static int file_attr; +static char *file_rdir_path = NULL; +static bool _temp_used = FALSE; + +#endif + +static char file_buffer[PATH_MAX + 16]; +static int file_buffer_length; + +#ifdef _DIRENT_HAVE_D_TYPE +#undef _DIRENT_HAVE_D_TYPE +#endif + +#ifdef _DIRENT_HAVE_D_TYPE +static bool _last_is_dir; +#endif + +static char *_home = NULL; +static uid_t _uid = 0; + +#ifdef PROJECT_EXEC + +typedef + struct _path { + struct _path *next; + char *path; + } + FILE_PATH; + +typedef + struct { + unsigned int mask; + unsigned char test[4]; + unsigned int mode[4]; + } + FILE_MODE_DECODE; + +static void push_path(void **list, const char *path) +{ + FILE_PATH *slot; + + ALLOC(&slot, sizeof(FILE_PATH)); + slot->path = STRING_new_zero(path); + + slot->next = *list; + *list = slot; + + //printf("push_path: %s\n", path); +} + + +static char *pop_path(void **list) +{ + char *path; + FILE_PATH *slot; + + if (!*list) + return NULL; + + path = ((FILE_PATH *)*list)->path; + slot = *list; + *list = ((FILE_PATH *)*list)->next; + FREE(&slot); + + //printf("pop_path: %s\n", path); + return path; +} + + +static void dir_exit(void) +{ + if (file_dir != NULL) + { + closedir(file_dir); + file_dir = NULL; + } + + STRING_free(&file_pattern); + STRING_free(&file_path); +} + + +char *FILE_make_temp(int *len, const char *pattern) +{ + static int count = 0; + + _temp_used = TRUE; + + if (len) + { + if (pattern) + *len = snprintf(file_buffer, sizeof(file_buffer), FILE_TEMP_PATTERN, (int)getuid(), (int)getpid(), pattern); + else + { + count++; + *len = snprintf(file_buffer, sizeof(file_buffer), FILE_TEMP_FILE, (int)getuid(), (int)getpid(), count); + } + } + else + snprintf(file_buffer, sizeof(file_buffer), FILE_TEMP_DIR, (int)getuid(), (int)getpid()); + + return file_buffer; +} + +static void remove_temp_file(const char *path) +{ + if (FILE_is_dir(path)) + { + //fprintf(stderr, "rmdir: %s\n", path); + rmdir(path); + } + else + { + //fprintf(stderr, "unlink: %s\n", path); + unlink(path); + } +} + +void FILE_remove_temp_file(void) +{ + if (_temp_used) + FILE_recursive_dir(FILE_make_temp(NULL, NULL), NULL, remove_temp_file, 0, FALSE); + rmdir(FILE_make_temp(NULL, NULL)); + _temp_used = FALSE; +} + +void FILE_remove_temp_file_pid(pid_t pid) +{ + snprintf(file_buffer, sizeof(file_buffer), FILE_TEMP_DIR, (int)getuid(), (int)pid); + FILE_recursive_dir(file_buffer, NULL, remove_temp_file, 0, FALSE); + rmdir(FILE_make_temp(NULL, NULL)); +} + +void FILE_init(void) +{ + struct stat info; + + _temp_used = TRUE; + FILE_remove_temp_file(); + + snprintf(file_buffer, sizeof(file_buffer), FILE_TEMP_PREFIX, (int)getuid()); + (void)mkdir(file_buffer, S_IRWXU); + + if (lstat(file_buffer, &info) == 0 && S_ISDIR(info.st_mode) && chown(file_buffer, getuid(), getgid()) == 0 && chmod(file_buffer, S_IRWXU) == 0) + { + snprintf(file_buffer, sizeof(file_buffer), FILE_TEMP_DIR, (int)getuid(), (int)getpid()); + (void)mkdir(file_buffer, S_IRWXU); + if (lstat(file_buffer, &info) == 0 && S_ISDIR(info.st_mode) && chown(file_buffer, getuid(), getgid()) == 0 && chmod(file_buffer, S_IRWXU) == 0) + return; + } + + ERROR_fatal("cannot initialize interpreter temporary directory. Do you try to hijack Gambas?"); +} + +void FILE_exit(void) +{ + STRING_free(&_home); + FILE_remove_temp_file(); + STRING_free(&file_rdir_path); + dir_exit(); +} + +#else + +void FILE_exit(void) +{ + STR_free(_home); +} + +#endif + +static char *stradd(char *d, const char *s) +{ + for(;;) + { + if ((*d = *s) == 0) + break; + d++; + s++; + } + + return d; +} + +/*#define stradd(d, s) \ +({ \ + char *_d = (d); \ + const char *_s = (s); \ + \ + for(;;) \ + { \ + if ((*_d = *_s) == 0) \ + break; \ + \ + _d++; \ + _s++; \ + } \ + \ + _d; \ +})*/ + +const char *FILE_cat(const char *path, ...) +{ + char *p; + va_list args; + int len; + bool end_slash = FALSE; + bool add_slash = FALSE; + + va_start(args, path); + + p = file_buffer; + + if (path != file_buffer) + *p = 0; + + for(;;) + { + if (*path == '/' && p != file_buffer) + path++; + + len = strlen(path); + if (add_slash) + len++; + + if (len > 0) + { + if ((p + len) > &file_buffer[PATH_MAX]) + THROW(E_TOOLONG); + + if (p != path) + { + if (add_slash) + p = stradd(p, "/"); + + p = stradd(p, path); + } + else + p += len; + + end_slash = (p[-1] == '/'); + } + + path = va_arg(args, char *); + if (path == NULL) + break; + + add_slash = ((!end_slash) && (*path != 0) && (*path != '/')); + } + + va_end(args); + + file_buffer_length = p - file_buffer; + return file_buffer; +} + + +char *FILE_buffer(void) +{ + return file_buffer; +} + + +int FILE_buffer_length(void) +{ + if (file_buffer_length < 0) + file_buffer_length = strlen(file_buffer); + + return file_buffer_length; +} + +static void init_file_buffer(const char *path) +{ + int len; + + if (path == file_buffer) + return; + + len = strlen(path); + + if (len > PATH_MAX) + THROW(E_TOOLONG); + + strcpy(file_buffer, path); + file_buffer_length = len; +} + +//#define INIT_FILE_BUFFER(_path) (init_file_buffer(), _path = file_buffer) + +const char *FILE_get_dir(const char *path) +{ + char *p; + + if (path == NULL || path[0] == 0) + return NULL; + + if (path[0] == '/' && path[1] == 0) + return "/"; + + init_file_buffer(path); + + p = rindex(file_buffer, '/'); + + if (p == NULL) + *file_buffer = 0; + else + { + *p = 0; + + if (file_buffer[0] == 0 && path[0] == '/') + strcpy(file_buffer, "/"); + } + + file_buffer_length = -1; + return file_buffer; +} + + +const char *FILE_get_name(const char *path) +{ + const char *p; + + p = rindex(path, '/'); + if (p) + return &p[1]; + else + return path; +} + + +const char *FILE_get_ext(const char *path) +{ + const char *p; + + p = rindex(path, '/'); + if (p) + path = &p[1]; + + p = rindex(path, '.'); + if (p == NULL) + return &path[strlen(path)]; + else + return p + 1; +} + + +const char *FILE_set_ext(const char *path, const char *ext) +{ + char *p; + + init_file_buffer(path); + + p = (char *)FILE_get_ext(file_buffer); + + if (!ext) + { + if (p > file_buffer && p[-1] == '.') + p[-1] = 0; + else + *p = 0; + return file_buffer; + } + + if (&p[strlen(ext)] >= &file_buffer[PATH_MAX]) + THROW(E_TOOLONG); + + if (p == file_buffer || p[-1] != '.') + *p++ = '.'; + + if (*ext == '.') + ext++; + + strcpy(p, ext); + + file_buffer_length = -1; + return file_buffer; +} + + +const char *FILE_get_basename(const char *path) +{ + char *p; + + path = FILE_get_name(path); + + init_file_buffer(path); + + p = rindex(file_buffer, '.'); + if (p) + *p = 0; + + file_buffer_length = -1; + return file_buffer; +} + +bool FILE_is_dir(const char *path) +{ + struct stat buf; + +#ifdef PROJECT_EXEC + + if (FILE_is_relative(path)) + { + /*if (!EXEC_arch) + { + chdir(PROJECT_path); + if (lstat(path, &buf) == 0) + goto __OK; + }*/ + + return ARCHIVE_is_dir(NULL, path); + } + +#endif + + if (stat(path, &buf)) + return FALSE; + +/*#ifdef PROJECT_EXEC +__OK: +#endif*/ + + return (S_ISDIR(buf.st_mode)); +} + + +#ifdef PROJECT_EXEC + +bool FILE_exist_real(const char *path) +{ + struct stat buf; + + if (chdir(PROJECT_path)) return FALSE; + return (stat(path, &buf) == 0); +} + +void FILE_stat(const char *path, FILE_STAT *info, bool follow) +{ + struct stat buf; + int ret; + + //fprintf(stderr, "FILE_stat: %s\n", path); + + if (FILE_is_relative(path)) + { + /*if (!EXEC_arch) + { + chdir(PROJECT_path); + if (lstat(path, &buf) == 0) + goto _OK; + }*/ + + ARCHIVE_stat(NULL, path, info); + return; + } + + if (follow) + ret = stat(path, &buf); + else + ret = lstat(path, &buf); + + if (ret) + THROW_SYSTEM(errno, path); + +//_OK: + + if (S_ISREG(buf.st_mode)) + info->type = GB_STAT_FILE; + else if (S_ISDIR(buf.st_mode)) + info->type = GB_STAT_DIRECTORY; + else if (S_ISCHR(buf.st_mode) || S_ISBLK(buf.st_mode)) + info->type = GB_STAT_DEVICE; + else if (S_ISFIFO(buf.st_mode)) + info->type = GB_STAT_PIPE; + else if (S_ISSOCK(buf.st_mode)) + info->type = GB_STAT_SOCKET; + else if (S_ISLNK(buf.st_mode)) + info->type = GB_STAT_LINK; + + info->mode = buf.st_mode & 07777; + info->size = buf.st_size; + info->atime = (int)buf.st_atime; + info->mtime = (int)buf.st_mtime; + info->ctime = (int)buf.st_ctime; + info->hidden = (*FILE_get_name(path) == '.'); + info->uid = buf.st_uid; + info->gid = buf.st_gid; +} + +char *FILE_mode_to_string(mode_t mode) +{ + char *str = file_buffer; + + str[0] = mode & S_IRUSR ? 'r' : '-'; + str[1] = mode & S_IWUSR ? 'w' : '-'; + str[2] = (mode & S_ISUID + ? (mode & S_IXUSR ? 's' : 'S') + : (mode & S_IXUSR ? 'x' : '-')); + str[3] = mode & S_IRGRP ? 'r' : '-'; + str[4] = mode & S_IWGRP ? 'w' : '-'; + str[5] = (mode & S_ISGID + ? (mode & S_IXGRP ? 's' : 'S') + : (mode & S_IXGRP ? 'x' : '-')); + str[6] = mode & S_IROTH ? 'r' : '-'; + str[7] = mode & S_IWOTH ? 'w' : '-'; + str[8] = (mode & S_ISVTX + ? (mode & S_IXOTH ? 't' : 'T') + : (mode & S_IXOTH ? 'x' : '-')); + str[9] = 0; + + file_buffer_length = 9; + + return str; +} + +mode_t FILE_mode_from_string(mode_t mode, const char *str) +{ + static FILE_MODE_DECODE decode[] = { + { S_IRUSR, { '-', 'r' }, { 0, S_IRUSR } }, + { S_IWUSR, { '-', 'w' }, { 0, S_IWUSR } }, + { S_IXUSR | S_ISUID, { '-', 'x', 'S', 's' }, { 0, S_IXUSR, S_ISUID, S_IXUSR | S_ISUID } }, + { S_IRGRP, { '-', 'r' }, { 0, S_IRGRP } }, + { S_IWGRP, { '-', 'w' }, { 0, S_IWGRP } }, + { S_IXGRP | S_ISGID, { '-', 'x', 'S', 's' }, { 0, S_IXGRP, S_ISGID, S_IXGRP | S_ISGID } }, + { S_IROTH, { '-', 'r' }, { 0, S_IROTH } }, + { S_IWOTH, { '-', 'w' }, { 0, S_IWOTH } }, + { S_IXOTH | S_ISVTX, { '-', 'x', 'T', 't' }, { 0, S_IXOTH, S_ISVTX, S_IXOTH | S_ISVTX } }, + { 0 } + }; + + unsigned char c, test; + FILE_MODE_DECODE *d = decode; + int i; + + while (d->mask) + { + c = *str++; + if (!c) + break; + + for (i = 0; i <= 3; i++) + { + test = d->test[i]; + if (!test) + break; + if (c == test) + { + mode = (mode & ~(d->mask)) | d->mode[i]; + break; + } + } + + d++; + } + + return mode; +} + + +void FILE_chmod(const char *path, mode_t mode) +{ + if (FILE_is_relative(path)) + THROW(E_ACCESS); + + if (chmod(path, mode)) + THROW_SYSTEM(errno, path); +} + + +void FILE_chown(const char *path, const char *user) +{ + struct passwd *pwd; + uid_t uid; + + if (FILE_is_relative(path)) + THROW(E_ACCESS); + + if (!strcmp(user, "root")) + uid = (uid_t)0; + else + { + errno = 0; + pwd = getpwnam(user); + if (errno) + THROW_SYSTEM(errno, path); + if (!pwd) + THROW(E_USER); + uid = pwd->pw_uid; + } + + if (chown(path, uid, (gid_t)-1)) + THROW_SYSTEM(errno, path); +} + + +void FILE_chgrp(const char *path, const char *group) +{ + struct group *grp; + gid_t gid; + + if (FILE_is_relative(path)) + THROW(E_ACCESS); + + if (!strcmp(group, "root")) + gid = (gid_t)0; + else + { + errno = 0; + grp = getgrnam(group); + if (errno) + THROW_SYSTEM(errno, path); + if (!grp) + THROW(E_USER); + gid = grp->gr_gid; + } + + if (chown(path, (uid_t)-1, gid)) + THROW_SYSTEM(errno, path); +} + + +void FILE_dir_first(const char *path, const char *pattern, int attr) +{ + dir_exit(); + + if (!path || *path == 0) + path = "."; + + if (attr == (GB_STAT_FILE | GB_STAT_DIRECTORY)) + attr = 0; + + file_attr = attr; + + if (FILE_is_relative(path)) + { + /*if (!EXEC_arch) + { + chdir(PROJECT_path); + if (lstat(path, &buf) == 0) + goto _OK; + }*/ + + file_dir_arch = TRUE; + ARCHIVE_dir_first(NULL, path, pattern, attr); + return; + } + + file_dir_arch = FALSE; + file_dir = opendir(path); + if (file_dir == NULL) + THROW_SYSTEM(errno, path); + + file_pattern = STRING_new_zero(pattern); + file_path = STRING_new_zero(path); +} + +#if OPT_FSTATAT +#else + #ifdef FSTATAT + #undef FSTATAT + #endif +#endif + +bool FILE_dir_next(char **path, int *len) +{ + struct dirent *entry; + int len_entry; + bool ret; + char *name; + #ifndef _DIRENT_HAVE_D_TYPE + struct stat info; + #ifndef HAVE_FSTATAT + char *p = file_buffer; + #endif + #endif + + if (file_dir_arch) + { + ret = ARCHIVE_dir_next(path, len, file_attr); + if (ret) + file_dir_arch = FALSE; + return ret; + } + + if (file_dir == NULL) + return TRUE; + + #ifndef _DIRENT_HAVE_D_TYPE + #ifndef HAVE_FSTATAT + if (file_attr) + { + init_file_buffer(file_path); + p += file_buffer_length; + + if (p[-1] != '/' && (file_buffer[1] || file_buffer[0] != '/')) + *p++ = '/'; + } + #endif + #endif + + for(;;) + { + entry = readdir(file_dir); + if (entry == NULL) + { + dir_exit(); + return TRUE; + } + + name = entry->d_name; + + if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) + continue; + + len_entry = strlen(name); + if ((len_entry + file_buffer_length) > PATH_MAX) + continue; + + if (file_attr) + { + #ifdef _DIRENT_HAVE_D_TYPE + if ((file_attr == GB_STAT_DIRECTORY) ^ (entry->d_type == DT_DIR)) + continue; + #else + #if HAVE_FSTATAT + if (fstatat(dirfd(file_dir), name, &info, 0)) + #else + strcpy(p, name); + if (stat(file_buffer, &info)) + #endif + { + if (file_attr == GB_STAT_DIRECTORY) + continue; + } + else if ((file_attr == GB_STAT_DIRECTORY) ^ (S_ISDIR(info.st_mode) != 0)) + continue; + #endif + } + + if (file_pattern == NULL) + break; + + if (REGEXP_match(file_pattern, STRING_length(file_pattern), name, len_entry)) + break; + } + + *path = name; + *len = len_entry; + #ifdef _DIRENT_HAVE_D_TYPE + _last_is_dir = entry->d_type == DT_DIR; + #endif + + return FALSE; +} + +//#undef _DIRENT_HAVE_D_TYPE + +void FILE_recursive_dir(const char *dir, void (*found)(const char *), void (*afterfound)(const char *), int attr, bool follow) +{ + void *list = NULL; + void *dir_list = NULL; + char *file; + int len; + char *path; + //struct stat buf; + #ifdef _DIRENT_HAVE_D_TYPE + #else + FILE_STAT info; + #endif + char *temp; + bool is_dir; + #if OPT_NLINK + int nsubdir = -1; + #endif + + if (!dir || *dir == 0) + dir = "."; + else if (!FILE_is_dir(dir)) + return; + + STRING_free(&file_rdir_path); + file_rdir_path = STRING_new_zero(dir); + + FILE_dir_first(dir, NULL, attr != GB_STAT_DIRECTORY ? 0 : GB_STAT_DIRECTORY); + + #if OPT_NLINK + if (file_dir && !FILE_is_relative(dir)) + { + struct stat dinfo; + fstat(dirfd(file_dir), &dinfo); + // If the number of links N to the directory: + // - Is > 2, then the directory has N - 2 sub-directories + // - Is = 2, then the directory has no sub-directory + // - Is < 2, then the file system is not POSIX, so we must scan everything + nsubdir = dinfo.st_nlink - 2; + } + #endif + + while (!FILE_dir_next(&file, &len)) + { + temp = STRING_new_temp(file, len); + path = (char *)FILE_cat(file_rdir_path, temp, NULL); + + #if OPT_NLINK + if (nsubdir || follow) + #endif + { + #ifdef _DIRENT_HAVE_D_TYPE + is_dir = _last_is_dir; + #else + if (follow) + is_dir = FILE_is_dir(path); + else + { + FILE_stat(path, &info, FALSE); + is_dir = info.type == GB_STAT_DIRECTORY; + } + #endif + + if (is_dir) + { + nsubdir--; + push_path(&dir_list, path); + continue; + } + } + + push_path(&list, path); + } + + while (dir_list) + { + path = pop_path(&dir_list); + //fprintf(stderr, "%s\n", path); + + TRY + { + if (found && (!attr || (attr & GB_STAT_DIRECTORY))) (*found)(path); + FILE_recursive_dir(path, found, afterfound, attr, follow); + if (afterfound && (!attr || (attr & GB_STAT_DIRECTORY))) (*afterfound)(path); + } + CATCH + { + //ERROR_print_at(stdout); + } + END_TRY + + STRING_free((char **)&path); + } + + while (list) + { + path = pop_path(&list); + //fprintf(stderr, "%s\n", path); + + TRY + { + if (found) (*found)(path); + if (afterfound) (*afterfound)(path); + } + CATCH + { + //ERROR_print_at(stdout); + } + END_TRY + + STRING_free((char **)&path); + } +} + + +void FILE_rmdir(const char *path) +{ + if (FILE_is_relative(path)) + THROW(E_ACCESS); + + if (rmdir(path) != 0) + { + if (errno == ENOTEMPTY || errno == EEXIST) + THROW(E_NEMPTY); + else + THROW_SYSTEM(errno, path); + } +} + + +void FILE_mkdir(const char *path) +{ + if (FILE_is_relative(path)) + THROW(E_ACCESS); + + if (mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) + THROW_SYSTEM(errno, path); +} + + +void FILE_make_path_dir(const char *path) +{ + int i; + char c; + + if (FILE_is_relative(path)) + return; + + init_file_buffer(path); + + for (i = 1;; i++) + { + c = file_buffer[i]; + if (c == 0) + break; + if (c == '/') + { + file_buffer[i] = 0; + (void)mkdir(file_buffer, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + file_buffer[i] = c; + } + c++; + } +} + + +void FILE_copy(const char *src, const char *dst) +{ + STREAM stream_src; + STREAM stream_dst; + int64_t len; + int64_t n; + char *buf = NULL; + + CLEAR(&stream_src); + CLEAR(&stream_dst); + + if (FILE_exist(dst)) + THROW(E_EXIST, dst); + + ALLOC(&buf, MAX_IO); + + TRY + { + STREAM_open(&stream_src, src, STO_READ); + STREAM_open(&stream_dst, dst, STO_CREATE); + + STREAM_lof(&stream_src, &len); + + while (len) + { + n = len > MAX_IO ? MAX_IO : len; + STREAM_read(&stream_src, buf, n); + STREAM_write(&stream_dst, buf, n); + len -= n; + } + + STREAM_close(&stream_src); + STREAM_close(&stream_dst); + + FREE(&buf); + } + CATCH + { + if (stream_src.type) + STREAM_close(&stream_src); + if (stream_dst.type) + STREAM_close(&stream_dst); + FREE(&buf); + + PROPAGATE(); + } + END_TRY +} + + +bool FILE_access(const char *path, int mode) +{ + if (FILE_is_relative(path)) + { + if (mode & (W_OK | X_OK)) + return FALSE; + + /*if (!EXEC_arch) + { + chdir(PROJECT_path); + if (access(path, mode) == 0) + return TRUE; + }*/ + + return ARCHIVE_exist(NULL, path); + } + + return (access(path, mode) == 0); +} + + +bool FILE_exist_follow(const char *path, bool follow) +{ + struct stat buf; + + if (FILE_is_relative(path)) + return ARCHIVE_exist(NULL, path); + + return follow ? (stat(path, &buf) == 0) : (lstat(path, &buf) == 0); +} + + +void FILE_link(const char *src, const char *dst) +{ + /* src can be relative */ + if (FILE_is_relative(dst)) + THROW(E_ACCESS); + + if (FILE_exist(dst)) + THROW(E_EXIST, dst); + + if (symlink(src, dst) != 0) + THROW_SYSTEM(errno, dst); +} + +int64_t FILE_free(const char *path) +{ + struct statvfs info; + + if (FILE_is_relative(path)) + return 0; + + statvfs(path, &info); + return (int64_t)(getuid() == 0 ? info.f_bfree : info.f_bavail) * info.f_bsize; +} + +#else + +bool FILE_exist(const char *path) +{ + return (access(path, F_OK) == 0); +} + +time_t FILE_get_time(const char *path) +{ + struct stat info; + + if (stat(path, &info) == 0) + return info.st_mtime; + else + return (time_t)-1L; +} + +bool FILE_copy(const char *src, const char *dst) +{ + int src_fd; + int dst_fd; + ssize_t len; + char *buf = NULL; + int save_errno; + struct stat info; + + fprintf(stderr, "FILE_copy: %s -> %s\n", src, dst); + + if (stat(src, &info)) + return TRUE; + + src_fd = open(src, O_RDONLY); + if (src_fd < 0) + { + fprintf(stderr, "open src failed\n"); + return TRUE; + } + + dst_fd = creat(dst, info.st_mode); + if (dst_fd < 0) + { + fprintf(stderr, "open dst failed\n"); + save_errno = errno; + close(src_fd); + errno = save_errno; + return TRUE; + } + + ALLOC(&buf, MAX_IO); + + for(;;) + { + len = read(src_fd, buf, MAX_IO); + if (len == 0) + break; + if (len < 0 && errno == EINTR) + continue; + if (write(dst_fd, buf, len) < 0) + { + save_errno = errno; + close(src_fd); + close(dst_fd); + unlink(dst); + errno = save_errno; + IFREE(buf); + return TRUE; + } + } + + close(src_fd); + close(dst_fd); + IFREE(buf); + + return FALSE; +} + +#endif + +const char *FILE_getcwd(const char *subdir) +{ + if (getcwd(file_buffer, PATH_MAX) == NULL) + return NULL; + + file_buffer_length = strlen(file_buffer); + + if (subdir != NULL) + return FILE_cat(file_buffer, subdir, NULL); + else + return file_buffer; +} + + +const char *FILE_readlink(const char *link) +{ + int len = readlink(link, file_buffer, PATH_MAX); + + if (len < 0) + return NULL; + + file_buffer[len] = 0; + file_buffer_length = len; + return file_buffer; + +} + +const char *FILE_find_gambas(void) +{ + const char *path; + + path = getenv("GB_PATH"); + + if (!path || !*path) + { + if (FILE_exist(GAMBAS_LINK_PATH)) + { + path = FILE_readlink(GAMBAS_LINK_PATH); + if (!path) + path = GAMBAS_LINK_PATH; + } + else + { + path = GAMBAS_PATH "/gbx" GAMBAS_VERSION_STRING; + } + } + + return path; +} + +void FILE_chdir(const char *path) +{ + #ifdef PROJECT_EXEC + if (chdir(path)) + THROW_SYSTEM(errno, path); + #else + if (chdir(path)) + THROW("Cannot change current directory to '&1': &2", path, strerror(errno)); + #endif +} + +void FILE_unlink(const char *path) +{ + #ifdef PROJECT_EXEC + if (FILE_is_relative(path)) + THROW(E_ACCESS); + + if (unlink(path) != 0) + THROW_SYSTEM(errno, path); + #else + if (unlink(path) != 0 && errno != ENOENT) + THROW("Cannot remove file '&1': &2", path, strerror(errno)); + #endif +} + + +void FILE_rename_ext(const char *src, const char *dst, bool kill) +{ + #ifdef PROJECT_EXEC + if (FILE_is_relative(src) || FILE_is_relative(dst)) + THROW(E_ACCESS); + + if (!kill) + { + if (FILE_exist(dst)) + THROW(E_EXIST, dst); + } + + if (rename(src, dst) != 0) + THROW_SYSTEM(errno, dst); + #else + if (rename(src, dst) != 0) + THROW("Cannot rename file '&1' to '&2': &3", src, dst, strerror(errno)); + #endif +} + +char *FILE_get_home(void) +{ + struct passwd *info; + uid_t uid = getuid(); + + if (!_home || _uid != uid) + { +#ifdef PROJECT_EXEC + STRING_free(&_home); +#else + STR_free(_home); +#endif + info = getpwuid(uid); + if (info) +#ifdef PROJECT_EXEC + _home = STRING_new_zero(info->pw_dir); +#else + _home = STR_copy(info->pw_dir); +#endif + _uid = uid; + } + + return _home; +} diff --git a/main/share/gb_hash.h b/main/share/gb_hash.h new file mode 100644 index 00000000..659688c5 --- /dev/null +++ b/main/share/gb_hash.h @@ -0,0 +1,106 @@ +/*************************************************************************** + + gb_hash.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_HASH_H +#define __GB_HASH_H + +#define KEEP_ORDER + +typedef + enum { + HF_NORMAL = 0, + HF_IGNORE_CASE = 1 + } + HASH_FLAG; + +typedef + struct _HASH_NODE + { + struct _HASH_NODE *next; + #ifdef KEEP_ORDER + struct _HASH_NODE *snext; + struct _HASH_NODE *sprev; + #endif + } + HASH_NODE; + +typedef + struct { + ushort len; + char key[0]; + } + PACKED + HASH_KEY; + +typedef + struct + { + int size; + int nnodes; + HASH_NODE **nodes; + size_t s_value; + HASH_NODE *last; + HASH_FLAG mode; + #ifdef KEEP_ORDER + HASH_NODE *sfirst; + HASH_NODE *slast; + #endif + } + HASH_TABLE; + +// NOTE: If HASH_ENUM changes, GB_COLLECTION_ITER must be updated accordingly in gambas.h + +typedef + struct + { + HASH_NODE *node; + HASH_NODE *next; + } + HASH_ENUM; + +typedef + uint (*HASH_FUNC)(const char *, int); + +typedef + bool (*HASH_COMP)(const char *, const char *, int); + +#define HASH_TABLE_MIN_SIZE 11 +#define HASH_TABLE_MAX_SIZE 13845163 + +#ifndef __GB_HASH_C +extern uint HASH_seed; +#endif + +void HASH_TABLE_create(HASH_TABLE **hash, size_t s_value, HASH_FLAG mode); +void HASH_TABLE_delete(HASH_TABLE **hash); +int HASH_TABLE_size(HASH_TABLE *hash_table); +void *HASH_TABLE_lookup(HASH_TABLE *hash_table, const char *key, int len, bool set_last); +void *HASH_TABLE_insert(HASH_TABLE *hash_table, const char *key, int len); +void HASH_TABLE_remove(HASH_TABLE *hash_table, const char *key, int len); +void *HASH_TABLE_next(HASH_TABLE *hash_table, HASH_ENUM *iter, bool set_last); +void HASH_TABLE_get_key(HASH_TABLE *hash_table, HASH_NODE *node, char **key, int *len); +bool HASH_TABLE_get_last_key(HASH_TABLE *hash_table, char **key, int *len); +void HASH_TABLE_set_last_key(HASH_TABLE *hash_table, char *key, int len); + +#endif + diff --git a/main/share/gb_hash_temp.h b/main/share/gb_hash_temp.h new file mode 100644 index 00000000..80c217b9 --- /dev/null +++ b/main/share/gb_hash_temp.h @@ -0,0 +1,534 @@ +/*************************************************************************** + + gb_hash_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_HASH_C + +#include + +#include "gb_common.h" +#include "gb_common_case.h" +#include "gb_common_string.h" +#include "gb_alloc.h" +#include "gb_hash.h" + + +#define NODE_value(_node) ((void *)((char *)_node) + sizeof(HASH_NODE)) +//#define NODE_length(_table, _node) (*((ushort *)(((char *)_node) + sizeof(HASH_NODE) + (_table)->s_value))) +#define NODE_key(_table, _node) ((HASH_KEY *)(((char *)_node) + sizeof(HASH_NODE) + (_table)->s_value)) + +#define MUST_RESIZE(hash) ((hash_table->size >= 3 * hash_table->nnodes && hash_table->size > HASH_TABLE_MIN_SIZE) || (3 * hash_table->size <= hash_table->nnodes && hash_table->size < HASH_TABLE_MAX_SIZE)) +static void hash_table_resize(HASH_TABLE *hash_table); + +static HASH_NODE **hash_table_lookup_node(HASH_TABLE *hash_table, const char *key, int len); +static HASH_NODE *hash_node_new(HASH_TABLE *hash_table, const char *key, int len); +static void hash_node_destroy(HASH_NODE *hash_node); + +#ifdef KEEP_ORDER +#else +static void hash_nodes_destroy(HASH_NODE *hash_node); +#endif + +// Put a random number in that if you want to be safe on the Internet +uint HASH_seed = 0x9A177BA5; + +static const int primes[] = +{ + 11, 19, 37, 73, 109, 163, 251, 367, 557, 823, 1237, 1861, 2777, 4177, 6247, 9371, + 14057, 21089, 31627, 47431, 71143, 106721, 160073, 240101, 360163, 540217, 810343, + 1215497, 1823231, 2734867, 4102283, 6153409, 9230113, 13845163 +}; + +//static const uint seed[] = { 0x9A177BA5, 0x9A177BA4, 0x9A177BA7, 0x9A177BA6, 0x9A177BA1, 0x9A177BA0, 0x9A177BA3, 0x9A177BA2, 0x9A177BAD }; + +static const int nprimes = sizeof (primes) / sizeof (primes[0]); + +static int spaced_primes_closest(int num) +{ + int i; + + for (i = 0; i < nprimes; i++) + if (primes[i] > num) + return primes[i]; + + return primes[nprimes - 1]; +} + + +// Fast hashing functions. Sometimes Microsoft Research produces useful things. + +static uint key_hash_binary(const char *key, int len) +{ + static const void *jump[] = { &&__LEN_0, &&__LEN_1, &&__LEN_2, &&__LEN_3, &&__LEN_4, &&__LEN_5, &&__LEN_6, &&__LEN_7, &&__LEN_8 }; + uint seed = HASH_seed; + uint hash = seed ^ len; + + if (len > 8) + { + key += len - 8; + len = 8; + } + + goto *jump[len]; + +__LEN_8: + hash = hash * seed + key[7]; +__LEN_7: + hash = hash * seed + key[6]; +__LEN_6: + hash = hash * seed + key[5]; +__LEN_5: + hash = hash * seed + key[4]; +__LEN_4: + hash = hash * seed + key[3]; +__LEN_3: + hash = hash * seed + key[2]; +__LEN_2: + hash = hash * seed + key[1]; +__LEN_1: + hash = hash * seed + key[0]; +__LEN_0: + + return hash; +} + +static uint key_hash_text(const char *key, int len) +{ + static const void *jump[] = { &&__LEN_0, &&__LEN_1, &&__LEN_2, &&__LEN_3, &&__LEN_4, &&__LEN_5, &&__LEN_6, &&__LEN_7, &&__LEN_8 }; + uint seed = HASH_seed; + uint hash = seed ^ len; + + if (len > 8) + { + key += len - 8; + len = 8; + } + + goto *jump[len]; + +__LEN_8: + hash = hash * seed + ((uint)key[7] & ~0x20U); +__LEN_7: + hash = hash * seed + ((uint)key[6] & ~0x20U); +__LEN_6: + hash = hash * seed + ((uint)key[5] & ~0x20U); +__LEN_5: + hash = hash * seed + ((uint)key[4] & ~0x20U); +__LEN_4: + hash = hash * seed + ((uint)key[3] & ~0x20U); +__LEN_3: + hash = hash * seed + ((uint)key[2] & ~0x20U); +__LEN_2: + hash = hash * seed + ((uint)key[1] & ~0x20U); +__LEN_1: + hash = hash * seed + ((uint)key[0] & ~0x20U); +__LEN_0: + + return hash; +} + + +#define get_hash_func(_hash) ((_hash)->mode ? key_hash_text : key_hash_binary) + + +void HASH_TABLE_create(HASH_TABLE **hash, size_t s_value, HASH_FLAG mode) +{ + HASH_TABLE *hash_table; + /*int i;*/ + + ALLOC_ZERO(&hash_table, sizeof(HASH_TABLE)); + + hash_table->size = HASH_TABLE_MIN_SIZE; + hash_table->s_value = s_value; + + ALLOC_ZERO(&hash_table->nodes, sizeof(HASH_NODE *) * hash_table->size); + + if (mode == HF_IGNORE_CASE) + hash_table->mode = mode; + else + hash_table->mode = HF_NORMAL; + + *hash = hash_table; +} + + +void HASH_TABLE_delete(HASH_TABLE **hash) +{ + HASH_TABLE *hash_table = *hash; + + if (hash_table == NULL) + return; + + #ifdef KEEP_ORDER + HASH_NODE *node, *next; + + node = hash_table->sfirst; + hash_table->sfirst = NULL; + hash_table->slast = NULL; + + while (node) + { + next = node->snext; + FREE(&node); + node = next; + } + #else + int i; + + for (i = 0; i < hash_table->size; i++) + hash_nodes_destroy(hash_table->nodes[i]); + #endif + + FREE(&hash_table->nodes); + FREE(hash); +} + + +int HASH_TABLE_size(HASH_TABLE *hash_table) +{ + return hash_table->nnodes; +} + + +static HASH_NODE **hash_table_lookup_node(HASH_TABLE *hash_table, const char *key, int len) +{ + HASH_NODE **node; + HASH_KEY *node_key; + uint hash; + //int n; + + if (hash_table->mode) + { + hash = key_hash_text(key, len); + node = &hash_table->nodes[hash % hash_table->size]; + + //n = 0; + while (*node) + { + node_key = NODE_key(hash_table, *node); + //n++; + if (node_key->len == len && STRING_equal_ignore_case_same(key, node_key->key, len)) + break; + node = &(*node)->next; + } + } + else + { + hash = key_hash_binary(key, len); + node = &hash_table->nodes[hash % hash_table->size]; + + //n = 0; + while (*node) + { + node_key = NODE_key(hash_table, *node); + //n++; + if (node_key->len == len && STRING_equal_same(key, node_key->key, len)) + break; + node = &(*node)->next; + } + } + + //fprintf(stderr, "hash_table_lookup_node %p: %d %d -> %d\n", hash_table, hash_table->size, hash_table->nnodes, n); + return node; +} + + +void *HASH_TABLE_lookup(HASH_TABLE *hash_table, const char *key, int len, bool set_last) +{ + HASH_NODE *node; + + if (len == 0) + return NULL; + + node = *hash_table_lookup_node(hash_table, key, len); + if (set_last) + hash_table->last = node; + + return node ? NODE_value(node) : NULL; +} + + +void *HASH_TABLE_insert(HASH_TABLE *hash_table, const char *key, int len) +{ + HASH_NODE **node; + void *value; + + node = hash_table_lookup_node(hash_table, key, len); + + if (UNLIKELY(*node != NULL)) + return NODE_value(*node); + + *node = hash_node_new(hash_table, key, len); + hash_table->nnodes++; + /*if (!hash_table->frozen)*/ + + value = NODE_value(*node); + + if (MUST_RESIZE(hash_table)) + hash_table_resize(hash_table); + + return value; +} + + +void HASH_TABLE_remove(HASH_TABLE *hash_table, const char *key, int len) +{ + HASH_NODE **node, *dest; + + node = hash_table_lookup_node(hash_table, key, len); + + if (LIKELY(*node != NULL)) + { + dest = *node; + (*node) = dest->next; + + #ifdef KEEP_ORDER + + if (dest->sprev) + dest->sprev->snext = dest->snext; + else + hash_table->sfirst = dest->snext; + + if (dest->snext) + dest->snext->sprev = dest->sprev; + else + hash_table->slast = dest->sprev; + + #endif + + hash_node_destroy(dest); + hash_table->nnodes--; + + hash_table->last = NULL; + + /*if (!hash_table->frozen)*/ + if (MUST_RESIZE(hash_table)) + hash_table_resize(hash_table); + } +} + + +void *HASH_TABLE_next(HASH_TABLE *hash_table, HASH_ENUM *iter, bool set_last) +{ + #ifdef KEEP_ORDER + + if (iter->node == NULL) + iter->node = hash_table->sfirst; + else + iter->node = iter->next; + + if (set_last) + hash_table->last = iter->node; + + if (iter->node) + { + iter->next = iter->node->snext; + return NODE_value(iter->node); + } + else + return NULL; + + #else + + HASH_NODE *enum_node = iter->node; + int enum_i = iter->index; + + if (enum_node) + enum_node = enum_node->next; + + while (enum_node == NULL) + { + enum_i++; + + if (enum_i >= hash_table->size) + break; + + enum_node = hash_table->nodes[enum_i]; + }; + + iter->node = enum_node; + iter->index = enum_i; + + if (set_last) + hash_table->last = enum_node; + + if (enum_node) + return NODE_value(enum_node); + else + return NULL; + + #endif +} + +#if 0 +static void dump_hash_table(HASH_TABLE *hash_table) +{ + int i; + HASH_NODE *node; + HASH_KEY *node_key; + + for (i = 0; i < hash_table->size; i++) + { + fprintf(stderr, "%5d: ", i); + for (node = hash_table->nodes[i]; node; node = node->next) + { + node_key = NODE_key(hash_table, node); + fprintf(stderr, "%.*s ", node_key->len, node_key->key); + } + fprintf(stderr, "\n"); + } + fprintf(stderr, "\n"); +} +#endif + +static void hash_table_resize(HASH_TABLE *hash_table) +{ + HASH_NODE **old_nodes; + HASH_NODE **new_nodes; + HASH_NODE *node; + HASH_NODE *next; + HASH_KEY *node_key; + int hash_val; + int new_size; + int i; + HASH_FUNC hash_func = get_hash_func(hash_table); + + new_size = MinMax(spaced_primes_closest(hash_table->nnodes), HASH_TABLE_MIN_SIZE, HASH_TABLE_MAX_SIZE); + + //fprintf(stderr, "**** hash_table_resize %p %d: %d -> %d\n", hash_table, hash_table->nnodes, hash_table->size, new_size); + + //fprintf(stderr, "BEFORE:\n"); + //dump_hash_table(hash_table); + + old_nodes = hash_table->nodes; + ALLOC_ZERO(&new_nodes, new_size * sizeof(HASH_NODE *)); + + for (i = 0; i < hash_table->size; i++) + for (node = hash_table->nodes[i]; node; node = next) + { + next = node->next; + + node_key = NODE_key(hash_table, node); + hash_val = (*hash_func)(node_key->key, node_key->len) % new_size; + + node->next = new_nodes[hash_val]; + new_nodes[hash_val] = node; + } + + FREE(&old_nodes); + hash_table->nodes = new_nodes; + hash_table->size = new_size; + + //fprintf(stderr, "AFTER:\n"); + //dump_hash_table(hash_table); +} + + +static HASH_NODE *hash_node_new(HASH_TABLE *hash_table, const char *key, int len) +{ + HASH_NODE *hash_node; + int size; + HASH_KEY *node_key; + + if (len > 65535) + len = 65535; + + size = sizeof(HASH_NODE) + hash_table->s_value + sizeof(HASH_KEY) + len; + ALLOC_ZERO(&hash_node, size); + + node_key = NODE_key(hash_table, hash_node); + memcpy(node_key->key, key, len); + node_key->len = len; + //node_key->hash = hash_table->mode ? key_hash_text(key, len) : key_hash_binary(key, len); + + #ifdef KEEP_ORDER + + if (UNLIKELY(!hash_table->sfirst)) + { + hash_table->sfirst = hash_node; + hash_table->slast = hash_node; + } + else + { + hash_node->sprev = hash_table->slast; + hash_node->sprev->snext = hash_node; + hash_table->slast = hash_node; + } + + #endif + + return hash_node; +} + + +static void hash_node_destroy(HASH_NODE *hash_node) +{ + FREE(&hash_node); +} + +#ifdef KEEP_ORDER +#else +static void hash_nodes_destroy(HASH_NODE *hash_node) +{ + HASH_NODE *node = hash_node; + HASH_NODE *next; + + for(;;) + { + if (UNLIKELY(node == NULL)) + return; + + next = node->next; + FREE(&node); + node = next; + } +} +#endif + +void HASH_TABLE_get_key(HASH_TABLE *hash_table, HASH_NODE *node, char **key, int *len) +{ + HASH_KEY *node_key; + + if (LIKELY(node != NULL)) + { + node_key = NODE_key(hash_table, node); + *key = node_key->key; + *len = node_key->len; + } + else + *len = 0; +} + + +bool HASH_TABLE_get_last_key(HASH_TABLE *hash_table, char **key, int *len) +{ + if (hash_table->last == NULL) + return TRUE; + + HASH_TABLE_get_key(hash_table, hash_table->last, key, len); + return FALSE; +} + +void HASH_TABLE_set_last_key(HASH_TABLE *hash_table, char *key, int len) +{ + hash_table->last = (len == 0) ? NULL : *hash_table_lookup_node(hash_table, key, len); +} diff --git a/main/share/gb_limit.h b/main/share/gb_limit.h new file mode 100644 index 00000000..da4b4003 --- /dev/null +++ b/main/share/gb_limit.h @@ -0,0 +1,103 @@ +/*************************************************************************** + + gb_limit.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_LIMIT_H +#define __GB_LIMIT_H + +/* Maximum number of parameters for a function - CANNOT CHANGE */ +#define MAX_PARAM_FUNC 63 + +/* Maximum number of parameters for a subroutine - CANNOT CHANGE */ +#define MAX_PARAM_SUBR 63 + +/* Maximum number of parameters for an operator - CANNOT CHANGE */ +#define MAX_PARAM_OP 63 + +/* Maximum level of expression imbrication */ +#define MAX_EXPR_LEVEL 255 + +/* Maximum number of patterns in an expression - CANNOT CHANGE */ +#define MAX_EXPR_PATTERN 1023 + +/* Maximum length of a symbol */ +#define MAX_SYMBOL_LEN 255 + +/* Maximum number of constants in the same class - CANNOT CHANGE */ +#define MAX_CLASS_CONST 65536 + +/* Maximum number of static or dynamic symbols in the same class - CANNOT CHANGE */ +#define MAX_CLASS_SYMBOL 2048 + +/* Maximum number of functions in the same class - CANNOT CHANGE */ +#define MAX_CLASS_FUNCTION 2048 + +/* Maximum number of class uses in the same class - CANNOT CHANGE */ +#define MAX_CLASS_CLASS 2048 + +/* Maximum number of extern declaration in the same class - CANNOT CHANGE */ +#define MAX_CLASS_EXTERN 256 + +/* Maximum number of events in the same class - CANNOT CHANGE */ +#define MAX_CLASS_EVENT 254 + +/* Maximum number of unknown symbols in the same class - CANNOT CHANGE */ +#define MAX_CLASS_UNKNOWN 65536 + +/* Maximum number of array declarations in the same class - CANNOT CHANGE */ +#define MAX_CLASS_ARRAY 32768 + +/* Maximum number of local variables in a function - CANNOT CHANGE */ +#define MAX_LOCAL_SYMBOL 127 + +/* Maximum level of control structures imbrication */ +#define MAX_CTRL_LEVEL 32 + +/* Maximum number of dimensions in an array */ +#define MAX_ARRAY_DIM 8 + +/* Maximum level of controls imbrication in a form */ +#define MAX_FORM_PARENT 32 + +/* Maximum number of possible comparisons in one CASE instruction */ +#define MAX_CASE_EXPR 32 + +/* Maximum number of breakpoints */ +#define MAX_BREAKPOINT 255 + +/* Number of bytes written at once in a single input/output request */ +#define MAX_IO 4096 + +/* Maximum level of class inheritance */ +#define MAX_INHERITANCE 16 + +/* Maximum length of an error message */ +#define MAX_ERROR_MSG 511 + +/* Maximum number of fields in a structure */ +#define MAX_STRUCT_FIELD 255 + +/* Maximum number of digits in a Float */ +#define MAX_FLOAT_DIGIT 15 + +#endif + diff --git a/main/share/gb_list.h b/main/share/gb_list.h new file mode 100644 index 00000000..b02ee8ab --- /dev/null +++ b/main/share/gb_list.h @@ -0,0 +1,43 @@ +/*************************************************************************** + + gb_list.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_LIST_H +#define __GB_LIST_H + +typedef + struct + { + void *next; + void *prev; + } + LIST; + +void LIST_insert(void *p_first, void *node, LIST *list); +void LIST_remove(void *p_first, void *node, LIST *list); + +#define LIST_for_each_name(_var, _first, _name) \ + for(_var = (_first); _var; _var = (_var)->_name.next) + +#define LIST_for_each(_var, _first) LIST_for_each_name(_var, _first, list) + +#endif diff --git a/main/share/gb_list_temp.h b/main/share/gb_list_temp.h new file mode 100644 index 00000000..67590f97 --- /dev/null +++ b/main/share/gb_list_temp.h @@ -0,0 +1,90 @@ +/*************************************************************************** + + gb_list_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __GB_LIST_C + +#include "gb_common.h" +#include "gb_list.h" + +/*#define DEBUG*/ + +#define TO_LIST(_node) ((LIST *)(((char *)_node) + ((char *)list - (char *)node))) + + +void LIST_insert(void *p_first, void *node, LIST *list) +{ + void **first = (void **)p_first; + void *last; + + if (*first == NULL) + { + *first = node; + list->prev = node; + list->next = NULL; + return; + } + + last = TO_LIST(*first)->prev; + + TO_LIST(last)->next = node; + + list->prev = last; + list->next = NULL; + + TO_LIST(*first)->prev = node; +} + + +void LIST_remove(void *p_first, void *node, LIST *list) +{ + void **first = (void **)p_first; + void *next, *prev, *last; + + next = list->next; + prev = list->prev; + + if (*first == node) + { + if (next) + TO_LIST(next)->prev = prev; + + *first = next; + } + else + { + last = TO_LIST(*first)->prev; + + if (node == last) + TO_LIST(*first)->prev = prev; + + if (prev) + TO_LIST(prev)->next = next; + + if (next) + TO_LIST(next)->prev = prev; + } + + list->prev = NULL; + list->next = NULL; +} + diff --git a/main/share/gb_magic.h b/main/share/gb_magic.h new file mode 100644 index 00000000..85a2fcb5 --- /dev/null +++ b/main/share/gb_magic.h @@ -0,0 +1,35 @@ +/*************************************************************************** + + gb_magic.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_MAGIC_H +#define __GB_MAGIC_H + +#define OUTPUT_MAGIC 0x9A177BA5 +#define OUTPUT_ENDIAN 0x12345678 + +#define ARCH_MAGIC 0xA7C4117E +#define ARCH_VERSION 2 + +#define PROJECT_MAGIC "# Gambas Project File" + +#endif diff --git a/main/share/gb_pcode.h b/main/share/gb_pcode.h new file mode 100644 index 00000000..45463c84 --- /dev/null +++ b/main/share/gb_pcode.h @@ -0,0 +1,172 @@ +/*************************************************************************** + + gb_pcode.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_PCODE_H +#define __GB_PCODE_H + +/* If this file is modified, don't forget to update GAMBAS_PCODE_VERSION in acinclude.m4 */ + +#define C_NOP 0x0000 + +#define C_PUSH_QUICK 0xF000 +#define C_PUSH_CONST 0xE000 + +#define C_POP_STATIC 0xD800 +#define C_POP_DYNAMIC 0xD000 +#define C_PUSH_STATIC 0xC800 +#define C_PUSH_DYNAMIC 0xC000 +#define C_PUSH_FUNCTION 0xB800 +#define C_PUSH_CLASS 0xB000 + +#define C_ADD_QUICK 0xA000 + +#define C_PUSH_LOCAL 0x0100 +#define C_PUSH_PARAM 0x0200 +#define C_PUSH_ARRAY 0x0300 +#define C_PUSH_UNKNOWN 0x0400 + +#define C_PUSH_EXTERN 0x0500 +#define C_BYREF 0x0600 +#define C_PUSH_EVENT 0x0700 +#define C_QUIT 0x0800 + +#define C_POP_LOCAL 0x0900 +#define C_POP_PARAM 0x0A00 +#define C_POP_ARRAY 0x0B00 +#define C_POP_UNKNOWN 0x0C00 + +#define C_POP_OPTIONAL 0x0D00 +#define C_POP_CTRL 0x0E00 + +#define C_BREAK 0x0F00 + +#define C_RETURN 0x1000 + +#define C_PUSH_INTEGER 0x1100 +#define C_PUSH_LONG 0x1200 +#define C_PUSH_CHAR 0x1300 +#define C_PUSH_MISC 0x1400 +#define C_PUSH_ME 0x1500 + +#define CPM_NULL 0 +#define CPM_VOID 1 +#define CPM_FALSE 2 +#define CPM_TRUE 3 +#define CPM_LAST 4 +#define CPM_STRING 5 +#define CPM_PINF 6 +#define CPM_MINF 7 +#define CPM_COMPLEX 8 +#define CPM_VARGS 9 +#define CPM_DROP_VARGS 10 +#define CPM_RETURN 11 + +#define C_TRY 0x1600 +#define C_END_TRY 0x1700 +#define C_CATCH 0x1800 + +#define C_DUP 0x1900 +#define C_DROP 0x1A00 +#define C_NEW 0x1B00 + +#define C_CALL 0x1C00 +#define C_CALL_QUICK 0x1D00 +#define C_CALL_SLOW 0x1E00 +#define C_ON 0x1F00 + +#define C_JUMP 0x2000 +#define C_JUMP_IF_TRUE 0x2100 +#define C_JUMP_IF_FALSE 0x2200 +#define C_GOSUB 0x2300 + +#define C_JUMP_FIRST 0x2400 +#define C_JUMP_NEXT 0x2500 +#define C_FIRST 0x2600 +#define C_NEXT 0x2700 + +#define C_EQ 0x2800 +#define C_NE 0x2900 +#define C_GT 0x2A00 +#define C_LE 0x2B00 +#define C_LT 0x2C00 +#define C_GE 0x2D00 +#define C_NEAR 0x2E00 +#define C_CASE 0x2F00 + +#define C_ADD 0x3000 +#define C_SUB 0x3100 +#define C_MUL 0x3200 +#define C_DIV 0x3300 +#define C_NEG 0x3400 +#define C_QUO 0x3500 +#define C_REM 0x3600 +#define C_POW 0x3700 +#define C_AND 0x3800 +#define C_OR 0x3900 +#define C_XOR 0x3A00 +#define C_NOT 0x3B00 +#define C_CAT 0x3C00 +#define C_LIKE 0x3D00 +#define C_FILE 0x3E00 +#define C_IS 0x3F00 + + +#define CODE_FIRST_SUBR 0x40 +#define CODE_LAST_SUBR 0x9F + +#define CODE_CALL_VARIANT 0x80 +//#define CODE_CALL_VOID 0x40 + +#define CODE_STATIC 0x0800 +#define CODE_FUNCTION 0x0400 + +#define CODE_NEW_ARRAY 0x40 +#define CODE_NEW_EVENT 0x80 + +#define CODE_OCT (CODE_FIRST_SUBR + 8) +#define CODE_RINSTR (CODE_FIRST_SUBR + 12) +#define CODE_ABS (CODE_FIRST_SUBR + 20) +#define CODE_MAX (CODE_FIRST_SUBR + 30) +#define CODE_CONV (CODE_FIRST_SUBR + 39) +#define CODE_BIN (CODE_FIRST_SUBR + 40) +#define CODE_HEX (CODE_FIRST_SUBR + 41) +#define CODE_DEBUG (CODE_FIRST_SUBR + 54) + +typedef + ushort PCODE; + +#define PCODE_is(pcode, value) (((pcode) & 0xFF00) == (value)) +#define PCODE_get_nparam(pcode) ((pcode) & 0x3F) + +#define PCODE_is_variant(pcode) ((pcode) & CODE_CALL_VARIANT) +#define PCODE_is_void(pcode) ((pcode) & CODE_CALL_VOID) + +#define PCODE_is_breakpoint(pcode) PCODE_is(pcode, C_BREAK) + +#define PCODE_BREAKPOINT(num) ((PCODE)(C_BREAK | (num))) + +#ifndef NO_CODE_DUMP +short PCODE_dump(FILE *out, ushort addr, PCODE *code); +#endif + +#endif /* */ diff --git a/main/share/gb_pcode_temp.h b/main/share/gb_pcode_temp.h new file mode 100644 index 00000000..7fda1a4c --- /dev/null +++ b/main/share/gb_pcode_temp.h @@ -0,0 +1,478 @@ +/*************************************************************************** + + gb_pcode_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" + +#ifdef PROJECT_COMP +#include "gb_limit.h" +#include "gbc_compile.h" +#endif + +#include "gb_pcode.h" + +/*#define DEBUG*/ + +static void print_quoted_string(FILE *out, const char *s, bool trans) +{ + const char *p = s; + unsigned char c; + + if (trans) + fputc('(', out); + fputc('"', out); + while ((c = (unsigned char)*p++)) + { + if (c < 32) + { + fputc('\\', out); + if (c == '\n') + fputc('n', out); + else if (c == '\t') + fputc('n', out); + else + fprintf(out, "\\x%02X", c); + } + else + fputc(c, out); + } + fputc('"', out); + if (trans) + fputc(')', out); +} + +short PCODE_dump(FILE *out, ushort addr, PCODE *code) +{ + static const char *op_comp[] = { "=", "<>", ">", "<=", "<", ">=", "==", "CASE" }; + static const char *op_arith[] = { "+", "-" , "*", "/", "NEG", "\\", "MOD", "POW", "AND", "OR", "XOR", "NOT", "&", "LIKE", "&/" }; + + int j; + unsigned short op; + unsigned short digit; + int value; + #ifdef PROJECT_COMP + int index; + TABLE *table; + bool trans; + #endif + int ncode; + + op = *code; + + switch (op & 0xFF00) + { + case C_PUSH_UNKNOWN: case C_POP_UNKNOWN: + case C_PUSH_INTEGER: + case C_JUMP: case C_JUMP_IF_TRUE: case C_JUMP_IF_FALSE: case C_GOSUB: + case C_NEXT: case C_JUMP_NEXT: + case C_TRY: + + ncode = 2; + break; + + case C_PUSH_LONG: + + ncode = 3; + break; + + case C_BYREF: + ncode = 2 + (op & 0xFF); + break; + + case C_ON: + ncode = 1 + (op & 0xFF); + break; + + case C_PUSH_EVENT: + ncode = 1 + ((op & 0xFF) == 0xFF); + break; + + default: + + if ((op & 0xFF00) == (C_PUSH_CONST | 0xF00)) + ncode = 2; + else + ncode = 1; + } + + fprintf(out, "%04d : ", addr); + + for (j = 0; j < ncode; j++) + { + if (j > 2 && (j % 3) == 0) + fprintf(out, "\n : "); + fprintf(out, " %04hX", code[j]); + } + + for (; j < ((ncode + 2) / 3 * 3); j++) + fprintf(out, " "); + + fprintf(out, " "); + + digit = (op >> 12); + value = op & 0xFFF; + if (value >= 0x800) value |= 0xFFFFF000; + + switch (digit) + { + #ifdef PROJECT_COMP + + case 0xF: + fprintf(out, "PUSH QUICK %d", (short)value); + break; + + case 0xE: + if ((op & 0xF00) == 0xF00) + { + value = code[1]; + } + + fprintf(out, "PUSH CONST %d", value); + + switch(JOB->class->constant[value].type.t.id) + { + case T_STRING: + table = JOB->class->string; + trans = FALSE; + break; + + case T_CSTRING: + table = JOB->class->string; + trans = TRUE; + break; + + default: + table = JOB->class->table; + trans = FALSE; + break; + } + + fputc(' ', out); + + print_quoted_string(out, TABLE_get_symbol_name(table, JOB->class->constant[value].value), trans); + + break; + + case 0xD: case 0xC: + fprintf(out, "%s %s ", (digit == 0xD ? "POP" : "PUSH"), (value & 0x800) ? "STATIC" : "DYNAMIC"); + index = ((value & 0x800) ? JOB->class->stat[value & 0x7FF].index : JOB->class->dyn[value & 0x7FF].index); + fprintf(out, "%s", TABLE_get_symbol_name(JOB->class->table, index)); + break; + + case 0xB: + if (value & 0x800) + { + fprintf(out, "PUSH FUNCTION "); + index = JOB->class->function[value & 0x7FF].name; + } + else + { + fprintf(out, "PUSH CLASS "); + index = JOB->class->class[value].index; + } + fprintf(out, "%s", TABLE_get_symbol_name(JOB->class->table, index)); + break; + + case 0xA: + fprintf(out, "ADD QUICK %d", (short)value); + break; + + #else + + case 0xF: + fprintf(out, "PUSH QUICK %d", (short)value); + break; + + case 0xE: + fprintf(out, "PUSH CONST %d", (short)value); + break; + + case 0xD: + fprintf(out, "POP %s %d", (value & 0x800) ? "STATIC" : "DYNAMIC", value & 0x7FF); + break; + + case 0xC: + fprintf(out, "PUSH %s %d", (value & 0x800) ? "STATIC" : "DYNAMIC", value & 0x7FF); + break; + + case 0xB: + fprintf(out, "PUSH %s %d", (value & 0x800) ? "FUNCTION" : "CLASS", value & 0x7FF); + break; + + case 0xA: + fprintf(out, "ADD QUICK %d", (short)value); + break; + + #endif + + default: + + digit = op & 0xFF00; + value = op & 0xFF; + if (value >= 0x80) value |= 0xFFFFFF00; + + if (digit >= C_PUSH_LOCAL && digit < C_QUIT && digit != C_BYREF) + fprintf(out, "PUSH "); + else if (digit >= C_POP_LOCAL && digit < C_BREAK) + fprintf(out, "POP "); + + switch(digit) + { + case C_PUSH_LOCAL: case C_POP_LOCAL: case C_PUSH_PARAM: case C_POP_PARAM: + if (value >= 0) + fprintf(out, "LOCAL %d", (short)value); + else + fprintf(out, "PARAM %d", (short)value); + break; + + case C_POP_CTRL: + fprintf(out, "CTRL %d", (short)value); + break; + + case C_POP_OPTIONAL: + fprintf(out, "OPTIONAL %d", (short)value); + break; + + case C_PUSH_UNKNOWN: case C_POP_UNKNOWN: + value = code[1]; + #ifdef PROJECT_COMP + fprintf(out, "UNKNOWN %s", TABLE_get_symbol_name(JOB->class->table, JOB->class->unknown[value])); + #else + fprintf(out, "UNKNOWN %d", (short)value); + #endif + + break; + + /*case C_PUSH_SPECIAL: + fprintf(out, "SPECIAL %d", (short)value); + break;*/ + + case C_PUSH_EXTERN: + fprintf(out, "EXTERN %d", (short)value); + break; + + case C_PUSH_EVENT: + if ((unsigned char)value == 0xFF) + { + value = code[1]; + #ifdef PROJECT_COMP + fprintf(out, "UNKNOWN EVENT %s", TABLE_get_symbol_name(JOB->class->table, JOB->class->unknown[value])); + #else + fprintf(out, "UNKNOWN EVENT %d", (short)value); + #endif + } + else + fprintf(out, "EVENT %d", (short)value); + break; + + case C_PUSH_ARRAY: case C_POP_ARRAY: + fprintf(out, "ARRAY (%d)", (short)value); + break; + + case C_CALL: case C_CALL_QUICK: case C_CALL_SLOW: + if (digit == C_CALL) + fprintf(out, "CALL "); + else if (digit == C_CALL_QUICK) + fprintf(out, "CALL QUICK "); + else + fprintf(out, "CALL SLOW "); + if (value & CODE_CALL_VARIANT) + fprintf(out, "VARIANT "); + //if (value & CODE_CALL_VOID) + // fprintf(out, "VOID "); + + fprintf(out, "(%d)", (short)value & 0x3F); + break; + + case C_BYREF: + fprintf(out, "BYREF (%d) ", (short)value & 0x3F); + for (j = 1; j < ncode; j++) + fprintf(out, "%04X", code[j]); + break; + + case C_PUSH_INTEGER: + value = code[1]; + fprintf(out, "PUSH SHORT %d", (short)value); + break; + + case C_PUSH_LONG: + // FIXME: endianness + value = code[1] | (code[2] << 16); //*((int *)&code[1]); + fprintf(out, "PUSH INTEGER %d", value); + break; + + case C_PUSH_ME: + fprintf(out, "PUSH %s", (value & 2) ? "SUPER": "ME"); + break; + + case C_PUSH_MISC: + switch (value) + { + case CPM_NULL: fprintf(out, "PUSH NULL"); break; + case CPM_VOID: fprintf(out, "PUSH VOID"); break; + case CPM_FALSE: fprintf(out, "PUSH FALSE"); break; + case CPM_TRUE: fprintf(out, "PUSH TRUE"); break; + case CPM_LAST: fprintf(out, "PUSH LAST"); break; + case CPM_STRING: fprintf(out, "PUSH NULL STRING"); break; + case CPM_PINF: fprintf(out, "PUSH +INF"); break; + case CPM_MINF: fprintf(out, "PUSH -INF"); break; + case CPM_COMPLEX: fprintf(out, "PUSH COMPLEX"); break; + case CPM_VARGS: fprintf(out, "PUSH VARGS"); break; + } + break; + + case C_JUMP: case C_JUMP_IF_TRUE: case C_JUMP_IF_FALSE: + value = code[1]; + fprintf(out, "JUMP%s %04d", + (digit == C_JUMP ? "" : + digit == C_JUMP_IF_TRUE ? " IF TRUE" : + " IF FALSE"), + (short)(addr + value + 2)); + break; + + case C_JUMP_FIRST: + + fprintf(out, "JUMP FIRST LOCAL %d", (short)value); + break; + + case C_JUMP_NEXT: + + fprintf(out, "JUMP NEXT "); + + value = code[1]; + fprintf(out, "%04d ", (short)(addr + value + 2)); + + break; + + case C_GOSUB: + value = code[1]; + fprintf(out, "GOSUB %04d", (short)(addr + value + 2)); + break; + + case C_ON: + fprintf(out, "ON %d", (short)value); + break; + + case C_FIRST: + + fprintf(out, "ENUM FIRST LOCAL %d", (short)value); + break; + + case C_NEXT: + + fprintf(out, "ENUM NEXT "); + if (value & 0xFF) + fprintf(out, "DROP "); + value = code[1]; + fprintf(out, "%04d ", (short)(addr + value + 2)); + break; + + case C_DROP: + fprintf(out, "DROP (%d)", (short)value); + break; + + case C_DUP: + fprintf(out, "DUP"); + break; + + case C_NEW: + fprintf(out, "NEW "); + if (value & CODE_NEW_EVENT) + fprintf(out, "EVENT "); + if (value & CODE_NEW_ARRAY) + fprintf(out, "ARRAY "); + fprintf(out, "(%d)", (short)value & 0x3F); + break; + + case C_BREAK: + fprintf(out, "BREAK"); + break; + + case C_RETURN: + fprintf(out, "RETURN (%d)", (short)value); + break; + + case C_QUIT: + switch(value) + { + case 0: fprintf(out, "QUIT"); break; + case 1: fprintf(out, "STOP"); break; + case 2: default: fprintf(out, "STOP EVENT"); break; + case 3: fprintf(out, "QUIT (1)"); break; + } + break; + + case C_PUSH_CHAR: + fprintf(out, "PUSH CHAR (%d)", (short)value); + break; + + case C_TRY: + value = code[1]; + fprintf(out, "TRY %04d", (short)(addr + value + 2)); + break; + + case C_END_TRY: + fprintf(out, "END TRY"); + break; + + case C_CATCH: + fprintf(out, "CATCH"); + break; + + default: + digit = (digit >> 8); + if (digit >= CODE_FIRST_SUBR) + { + #ifdef PROJECT_COMP + fprintf(out, "SUBR %s ", SUBR_get_from_opcode(digit - CODE_FIRST_SUBR, (short)value & 0x3F)->name); + #else + fprintf(out, "SUBR #%d ", digit - CODE_FIRST_SUBR); + #endif + + if (value & CODE_CALL_VARIANT) + fprintf(out, "VARIANT "); + //if (value & CODE_CALL_VOID) + // fprintf(out, "VOID "); + + fprintf(out, "(%d)", (short)value & 0x3F); + } + else if (digit >= 0x28 && digit <= 0x2F) + fprintf(out, "%s (%d)", op_comp[digit - 0x28], (short)value); + else if (digit >= 0x30 && digit <= 0x3E) + fprintf(out, "%s (%d)", op_arith[digit - 0x30], (short)value); + else if (digit == 0x3F) + fprintf(out, "IS (%d)", (short)value); + else + fprintf(out, "ILLEGAL"); + } + } + + fprintf(out, "\n"); + return ncode; +} + + diff --git a/main/share/gb_replace.h b/main/share/gb_replace.h new file mode 100644 index 00000000..3e1d8726 --- /dev/null +++ b/main/share/gb_replace.h @@ -0,0 +1,51 @@ +/*************************************************************************** + + gb_replace.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_REPLACE_H +#define __GB_REPLACE_H + +#include +#include +#include + +#ifndef HAVE_SETENV +int setenv(const char *name, const char *value, int overwrite); +#endif + +#ifndef HAVE_UNSETENV +void unsetenv(const char *name); +#endif + +#ifndef HAVE_GETDOMAINNAME +int getdomainname(char *name, size_t len); +#endif + +#ifndef HAVE_GETPT +int getpt(void); +#endif + +#ifndef HAVE_CFMAKERAW +void cfmakeraw(struct termios *termios_p); +#endif + +#endif diff --git a/main/share/gb_replace_temp.h b/main/share/gb_replace_temp.h new file mode 100644 index 00000000..32c7fbb3 --- /dev/null +++ b/main/share/gb_replace_temp.h @@ -0,0 +1,133 @@ +/*************************************************************************** + + gb_replace_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_replace.h" + +#include +#include +#include +#include +#include + +#ifndef HAVE_SETENV + +int setenv(const char *name, const char *value, int overwrite) +{ + char *env; + int env_size; + + if (!name || *name == 0) + return (-1); + + if (overwrite == 0) + { + if (getenv(name)) + return 0; + } + + env_size = strlen(name) + strlen(value) + 2; + env = malloc(env_size); + if (!env) + return (-1); + + strcpy(env, name); + strcat(env, "="); + strcat(env, value); + putenv(env); + + return 0; +} + +#endif + +#ifndef HAVE_UNSETENV + +extern char **environ; + +void unsetenv(const char *name) +{ + size_t len; + char **ep; + + if (name == NULL || *name == '\0' || strchr (name, '=') != NULL) + return; + + len = strlen(name); + + ep = environ; + while (*ep != NULL) + { + if (!strncmp (*ep, name, len) && (*ep)[len] == '=') + { + char **dp = ep; + + do + dp[0] = dp[1]; + while (*dp++); + } + else + ++ep; + } +} + +#endif + +#ifndef HAVE_GETDOMAINNAME + +int getdomainname(char *name, size_t len) +{ +#if defined(HAVE_SYSINFO) && defined(SI_SRPC_DOMAIN) + sysinfo(SI_SRPC_DOMAIN, name, len); +#else + *name = 0; + return 0; +#endif +} + +#endif + +#ifndef HAVE_GETPT + +int getpt(void) +{ +#ifdef OS_BSD + return posix_openpt(O_RDWR | O_NOCTTY); +#else + return -1; +#endif +} + +#endif + +#ifndef HAVE_CFMAKERAW + +void cfmakeraw(struct termios *termios_p) +{ + termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + termios_p->c_oflag &= ~OPOST; + termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + termios_p->c_cflag &= ~(CSIZE|PARENB); + termios_p->c_cflag |= CS8; +} + +#endif diff --git a/main/share/gb_reserved.h b/main/share/gb_reserved.h new file mode 100644 index 00000000..16b2c706 --- /dev/null +++ b/main/share/gb_reserved.h @@ -0,0 +1,371 @@ +/*************************************************************************** + + gb_reserved.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_RESERVED_H +#define __GB_RESERVED_H + +#include "gb_common.h" +#include "gb_table.h" + +enum +{ + RSF_NONE = 0x0000, + RSF_OP = 0x0001, + RSF_TYPE = 0x0002, + RSF_ASGN = 0x0004, + RSF_NOT = 0x0008, // operator can have NOT before it + + RSF_N_ARY = 0x0000, + RSF_UNARY = 0x0010, + RSF_BINARY = 0x0020, + RSF_POST = 0x0030, + RSF_ONLY = 0x0040, + + RSF_OPN = 0x0001, + RSF_OP1 = 0x0011, + RSF_OP2 = 0x0021, + RSF_OPP = 0x0031, + RSF_OP2S = 0x0061, + RSF_OP2SM = 0x00A1, + + RSF_POINT = 0x0100, // last pattern is a point or an exclamation mark + RSF_IDENT = 0x0200, // last pattern waits for an identifier + RSF_CLASS = 0x0400, // last pattern waits for a class + RSF_AS = 0x0800, // last pattern waits for a datatype + RSF_PREV = 0x1000, // last pattern use the flags of the last last pattern + RSF_EVENT = 0x2000, // last pattern waits for an event name + RSF_PUB = 0x4000, // last pattern is PUBLIC, PRIVATE or STATIC + + RSF_IMASK = 0xFF00 +}; + +enum { + RST_SAME = 17, // T_OBJECT + 1 + RST_ADD, + RST_AND, + RST_NOT, + RST_BCLR, + RST_MIN, + RST_MOD, + RST_GET, + RST_COLLECTION, + RST_EXEC, + RST_READ +}; + +#define RES_is_operator(value) (COMP_res_info[value].flag & RSF_OP) +#define RES_is_type(value) (COMP_res_info[value].flag & RSF_TYPE) +#define RES_is_assignment(value) (COMP_res_info[value].flag & RSF_ASGN) +#define RES_is_only(value) (COMP_res_info[value].flag & RSF_ONLY) +#define RES_get_ident_flag(value) (COMP_res_info[value].flag & RSF_IMASK) +#define RES_get_read_switch(value) (COMP_res_info[value].read_switch) + +#define RES_priority(_res) (COMP_res_info[_res].priority) + +#define RES_is_unary(value) ((COMP_res_info[value].flag & 0x30) == RSF_UNARY) +#define RES_is_binary(value) ((COMP_res_info[value].flag & 0x30) == RSF_BINARY) +#define RES_is_n_ary(value) ((COMP_res_info[value].flag & 0x30) == RSF_N_ARY) +#define RES_is_post(value) ((COMP_res_info[value].flag & 0x30) == RSF_POST) + +#define RES_get_type(_res) (COMP_res_info[_res].value) +#define RES_get_assignment_operator(_res) (COMP_res_info[_res].value) + +#define RES_can_have_not_before(value) (COMP_res_info[value].flag & RSF_NOT) + +typedef + enum { + RS_NONE, + RS_BOOLEAN, + RS_BYTE, + RS_DATE, + RS_SINGLE, + RS_FLOAT, + RS_INTEGER, + RS_LONG, + RS_SHORT, + RS_STRING, + RS_VARIANT, + RS_OBJECT, + RS_POINTER, + RS_CLASS, + RS_FUNCTION, + RS_STRUCT, + RS_CONST, + RS_PRIVATE, + RS_PUBLIC, + RS_STATIC, + RS_FAST, + RS_UNSAFE, + RS_PROPERTY, + RS_EVENT, + RS_INHERITS, + RS_IMPLEMENTS, + RS_EXPORT, + RS_AS, + RS_OF, + RS_DIM, + RS_NEW, + RS_PROCEDURE, + RS_SUB, + RS_RETURN, + RS_OPTIONAL, + RS_OUTPUT, + RS_DO, + RS_LOOP, + RS_WHILE, + RS_UNTIL, + RS_REPEAT, + RS_WEND, + RS_IF, + RS_THEN, + RS_ELSE, + RS_ENDIF, + RS_END, + RS_FOR, + RS_TO, + RS_DOWNTO, + RS_FROM, + RS_STEP, + RS_NEXT, + RS_SELECT, + RS_CASE, + RS_EXIT, + RS_BREAK, + RS_CONTINUE, + RS_GOTO, + RS_GOSUB, + RS_ON, + RS_ME, + RS_LAST, + RS_TRY, + RS_FINALLY, + RS_CATCH, + RS_WITH, + RS_TRUE, + RS_FALSE, + RS_SWAP, + RS_NULL, + RS_EXTERN, + RS_EACH, + RS_IN, + RS_DEFAULT, + RS_STOP, + RS_QUIT, + RS_RAISE, + RS_ERROR, + RS_SUPER, + RS_ENUM, + RS_LET, + RS_PINF, + RS_MINF, + RS_USE, + + RS_PRINT, + RS_INPUT, + RS_READ, + RS_WRITE, + RS_OPEN, + RS_CLOSE, + RS_SEEK, + RS_APPEND, + RS_CREATE, + RS_BINARY, + RS_LINE, + RS_FLUSH, + RS_EXEC, + RS_SHELL, + RS_WAIT, + RS_SLEEP, + RS_KILL, + RS_MOVE, + RS_COPY, + RS_INC, + RS_DEC, + RS_MKDIR, + RS_RMDIR, + RS_WATCH, + RS_LINK, + RS_LOCK, + RS_UNLOCK, + RS_LIBRARY, + RS_DEBUG, + RS_ASSERT, + RS_PIPE, + RS_RANDOMIZE, + RS_BYREF, + RS_MEMORY, + RS_CHMOD, + RS_CHOWN, + RS_CHGRP, + + RS_P_IF, + RS_P_ELSE, + RS_P_ENDIF, + RS_P_CONST, + RS_P_LINE, + + RS_COLON, + RS_SCOLON, + RS_COMMA, + RS_3PTS, + RS_SHARP, + RS_AT, + RS_QUES, + RS_LBRC, + RS_RBRC, + RS_EQUAL, + RS_NEAR, + RS_LBRA, + RS_RBRA, + RS_PT, + RS_EXCL, + RS_PLUS, + RS_MINUS, + RS_STAR, + RS_SLASH, + RS_FLEX, + RS_AMP, + RS_FILE, + RS_GT, + RS_LT, + RS_GE, + RS_LE, + RS_NE, + RS_LSQR, + RS_RSQR, + RS_AND, + RS_OR, + RS_NOT, + RS_XOR, + RS_BSLASH, + RS_DIV, + RS_PERCENT, + RS_MOD, + RS_IS, + RS_NOT_IS, + RS_LIKE, + RS_NOT_LIKE, + RS_BEGINS, + RS_NOT_BEGINS, + RS_ENDS, + RS_NOT_ENDS, + + RS_PLUS_EQ, + RS_MINUS_EQ, + RS_STAR_EQ, + RS_SLASH_EQ, + RS_DIV_EQ, + RS_MOD_EQ, + RS_AMP_EQ, + RS_FILE_EQ, + } + RESERVED_ID; + +enum +{ + OP_NONE , + OP_COLON , + OP_EQUAL , + OP_NEAR , + OP_LBRA , + OP_RBRA , + OP_PT , + OP_EXCL , + OP_COMMA , + OP_3PTS , + OP_PLUS , + OP_MINUS , + OP_STAR , + OP_SLASH , + OP_FLEX , + OP_AMP , + OP_FILE , + OP_GT , + OP_LT , + OP_GE , + OP_LE , + OP_NE , + OP_LSQR , + OP_RSQR , + OP_AND , + OP_OR , + OP_NOT , + OP_XOR , + OP_DIV , + OP_MOD , + OP_IS , + OP_LIKE +}; + +typedef + struct { + const char *name; + short flag; + uchar value; + uchar read_switch; + uchar priority; + uchar type; + ushort code; + ushort subcode; + void (*func)(); + } + COMP_INFO; + +typedef + struct { + const char *name; + ushort opcode; + ushort optype; + uchar type; + short min_param; + short max_param; + } + SUBR_INFO; + +#ifndef __RESERVED_C + +EXTERN COMP_INFO COMP_res_info[]; +EXTERN SUBR_INFO COMP_subr_info[]; + +//EXTERN TABLE *COMP_res_table; +//EXTERN TABLE *COMP_subr_table; + +EXTERN int SUBR_VarPtr; +EXTERN int SUBR_IsMissing; +EXTERN int SUBR_Mid; +EXTERN int SUBR_MidS; + +#endif + +void RESERVED_init(void); +void RESERVED_exit(void); + +int RESERVED_find_word(const char *word, int len); +int RESERVED_find_subr(const char *word, int len); + +SUBR_INFO *SUBR_get(const char *subr_name); +SUBR_INFO *SUBR_get_from_opcode(ushort opcode, ushort optype); + +int RESERVED_get_from_opcode(ushort code); + +#endif diff --git a/main/share/gb_reserved_keyword.h b/main/share/gb_reserved_keyword.h new file mode 100644 index 00000000..f819396d --- /dev/null +++ b/main/share/gb_reserved_keyword.h @@ -0,0 +1,586 @@ +/*************************************************************************** + + gb_reserved_keyword.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_RESERVED_KEYWORD_H +#define __GB_RESERVED_KEYWORD_H + +COMP_INFO COMP_res_info[] = +{ + { "" }, + + { "Boolean", RSF_TYPE, T_BOOLEAN, 8 }, + { "Byte", RSF_TYPE, T_BYTE, 8 }, + { "Date", RSF_TYPE, T_DATE, 8 }, + { "Single", RSF_TYPE, T_SINGLE, 8 }, + { "Float", RSF_TYPE, T_FLOAT, 8 }, + { "Integer", RSF_TYPE, T_INTEGER, 8 }, + { "Long", RSF_TYPE, T_LONG, 8 }, + { "Short", RSF_TYPE, T_SHORT, 8 }, + { "String", RSF_TYPE, T_STRING, 8 }, + { "Variant", RSF_TYPE, T_VARIANT, 8 }, + { "Object", RSF_TYPE, T_OBJECT, 8 }, + { "Pointer", RSF_TYPE, T_POINTER, 8 }, + { "Class", RSF_IDENT, 0, 2 }, + { "Function", RSF_IDENT, 0, 4 }, + { "Struct", RSF_PREV, 0, 3 }, + { "Const", RSF_IDENT, 0, 5 }, + { "Private", RSF_IDENT|RSF_PUB, }, + { "Public", RSF_IDENT|RSF_PUB, }, + { "Static", RSF_PUB }, + { "Fast", RSF_PUB }, + { "Unsafe", RSF_PUB }, + { "Property", RSF_IDENT }, + { "Event", RSF_IDENT|RSF_EVENT }, + { "Inherits", RSF_CLASS|RSF_AS }, + { "Implements" }, + { "Export" }, + { "As", RSF_AS }, + { "Of" }, + { "Dim", RSF_IDENT, }, + { "New", RSF_AS , 0, 1 }, + { "Procedure", RSF_IDENT, 0, 4 }, + { "Sub", RSF_IDENT, 0, 4 }, + { "Return" }, + { "Optional" }, + { "Output" }, + { "Do" }, + { "Loop" }, + { "While" }, + { "Until" }, + { "Repeat" }, + { "Wend" }, + { "If" }, + { "Then" }, + { "Else" }, + { "Endif" }, + { "End" }, + { "For" }, + { "To" }, + { "DownTo" }, + { "From" }, + { "Step" }, + { "Next" }, + { "Select" }, + { "Case" }, + { "Exit" }, + { "Break" }, + { "Continue" }, + { "Goto" }, + { "GoSub" }, + { "On" }, + { "Me", 0, 0, 1 }, + { "Last", 0, 0, 1 }, + { "Try" }, + { "Finally" }, + { "Catch" }, + { "With" }, + { "True" }, + { "False" }, + { "Swap" }, + { "Null" }, + { "Extern", RSF_IDENT, 0, 5 }, + { "Each" }, + { "In" }, + { "Default" }, + { "Stop" }, + { "Quit" }, + { "Raise", RSF_IDENT|RSF_EVENT }, + { "Error" }, + { "Super", 0, 0, 1 }, + { "Enum", 0, 0, 6 }, + { "Let" }, + { "+Inf" }, + { "-Inf" }, + { "Use" }, + + { "Print" }, + { "Input" }, + { "Read", RSF_PREV, 0, 7 }, + { "Write" }, + { "Open" }, + { "Close" }, + { "Seek" }, + { "Append" }, + { "Create" }, + { "Binary" }, + { "Line" }, + { "Flush" }, + { "Exec" }, + { "Shell" }, + { "Wait" }, + { "Sleep" }, + { "Kill" }, + { "Move" }, + { "Copy" }, + { "Inc" }, + { "Dec" }, + { "Mkdir" }, + { "Rmdir" }, + { "Watch" }, + { "Link" }, + { "Lock" }, + { "Unlock" }, + { "Library" }, + { "Debug" }, + { "Assert" }, + { "Pipe" }, + { "Randomize" }, + { "ByRef" }, + { "Memory" }, + { "Chmod" }, + { "Chown" }, + { "Chgrp" }, + + { "#If" }, + { "#Else" }, + { "#Endif" }, + { "#Const" }, + { "#Line" }, + + { ":", RSF_NONE, OP_COLON, 0, 0, T_OBJECT }, // Use for the immediate collection syntax + { ";" }, + { "," }, + { "..." }, + { "#" }, + { "@" }, + { "?" }, + { "{" }, + { "}" }, + { "=", RSF_OP2S, OP_EQUAL, 0, 4, T_BOOLEAN, C_EQ }, + { "==", RSF_OP2S, OP_NEAR, 0, 4, T_BOOLEAN, C_NEAR }, + { "(", RSF_OPP, OP_LBRA, 0, 12 }, + { ")", }, + { ".", RSF_OP2|RSF_POINT, OP_PT, 0, 20, T_VARIANT }, + { "!", RSF_OP2|RSF_POINT, OP_EXCL, 0, 20, T_VARIANT }, + { "+", RSF_OP2, OP_PLUS, 0, 5, RST_ADD, C_ADD }, + { "-", RSF_OP2, OP_MINUS, 0, 5, RST_ADD, C_SUB }, + { "*", RSF_OP2, OP_STAR, 0, 6, RST_ADD, C_MUL }, + { "/", RSF_OP2, OP_SLASH, 0, 6, T_FLOAT, C_DIV }, + { "^", RSF_OP2S, OP_FLEX, 0, 7, T_FLOAT, C_POW }, + { "&", RSF_OPN, OP_AMP, 0, 9, T_STRING, C_CAT }, + { "&/", RSF_OPN, OP_FILE, 0, 8, T_STRING, C_FILE }, + { ">", RSF_OP2S, OP_GT, 0, 4, T_BOOLEAN, C_GT }, + { "<", RSF_OP2S, OP_LT, 0, 4, T_BOOLEAN, C_LT }, + { ">=", RSF_OP2S, OP_GE, 0, 4, T_BOOLEAN, C_GE }, + { "<=", RSF_OP2S, OP_LE, 0, 4, T_BOOLEAN, C_LE }, + { "<>", RSF_OP2S, OP_NE, 0, 4, T_BOOLEAN, C_NE }, + { "[", RSF_OPP, OP_LSQR, 0, 12, RST_GET }, + { "]", RSF_NONE, OP_RSQR, 0, 0, T_OBJECT }, // Use for the immediate array syntax + { "And", RSF_OP2SM, OP_AND, 0, 2, RST_AND, C_AND }, + { "Or", RSF_OP2SM, OP_OR, 0, 2, RST_AND, C_OR }, + { "Not", RSF_OP1, OP_NOT, 0, 10, RST_NOT, C_NOT }, + { "Xor", RSF_OP2SM, OP_XOR, 0, 2, RST_AND, C_XOR }, + { "\\", RSF_OP2S, OP_DIV, 0, 6, T_INTEGER, C_QUO }, + { "Div", RSF_OP2S, OP_DIV, 0, 6, T_INTEGER, C_QUO }, + { "%", RSF_OP2S, OP_MOD, 0, 6, RST_MOD, C_REM }, + { "Mod", RSF_OP2S, OP_MOD, 0, 6, RST_MOD, C_REM }, + { "Is", RSF_OP2|RSF_AS|RSF_NOT, OP_IS, 0, 11, T_BOOLEAN, C_IS, 0 }, + { "", RSF_OP2|RSF_AS, OP_IS, 0, 11, T_BOOLEAN, C_IS, 1 }, + { "Like", RSF_OP2S|RSF_NOT, OP_LIKE, 0, 4, T_BOOLEAN, C_LIKE, 0 }, + { "", RSF_OP2S, OP_LIKE, 0, 4, T_BOOLEAN, C_LIKE, 4 }, // NOT LIKE + { "Begins", RSF_OP2S|RSF_NOT, OP_LIKE, 0, 4, T_BOOLEAN, C_LIKE, 1 }, + { "", RSF_OP2S, OP_LIKE, 0, 4, T_BOOLEAN, C_LIKE, 5 }, // NOT BEGINS + { "Ends", RSF_OP2S|RSF_NOT, OP_LIKE, 0, 4, T_BOOLEAN, C_LIKE, 2 }, + { "", RSF_OP2S, OP_LIKE, 0, 4, T_BOOLEAN, C_LIKE, 6 }, // NOT ENDS + { "Match", RSF_OP2S|RSF_NOT, OP_LIKE, 0, 4, T_BOOLEAN, C_LIKE, 3 }, + { "", RSF_OP2S, OP_LIKE, 0, 4, T_BOOLEAN, C_LIKE, 7 }, // NOT MATCH + + { "+=", RSF_ASGN, RS_PLUS }, + { "-=", RSF_ASGN, RS_MINUS }, + { "*=", RSF_ASGN, RS_STAR }, + { "/=", RSF_ASGN, RS_SLASH }, + { "\\=", RSF_ASGN, RS_BSLASH }, + { "%=", RSF_ASGN, RS_PERCENT }, + { "&=", RSF_ASGN, RS_AMP }, + { "&/=", RSF_ASGN, RS_FILE }, + { "^=", RSF_ASGN, RS_FLEX }, + + { NULL } +}; + +SUBR_INFO COMP_subr_info[] = +{ + { "Left$", 0, 0, T_STRING, 1, 2 }, + { "Left", 0, 0, T_STRING, 1, 2 }, + + { "Mid$", 1, 0, T_STRING, 2, 3 }, + { "Mid", 1, 0, T_STRING, 2, 3 }, + + { "Right$", 2, 0, T_STRING, 1, 2 }, + { "Right", 2, 0, T_STRING, 1, 2 }, + + { "Len", 3, 0, T_INTEGER, 1 }, + + { "Space$", 4, 0, T_STRING, 1 }, + { "Space", 4, 0, T_STRING, 1 }, + + { "String$", 5, 0, T_STRING, 2 }, + { "String", 5, 0, T_STRING, 2 }, + + { "Trim$", 6, 0, T_STRING, 1 }, + { "Trim", 6, 0, T_STRING, 1 }, + + { "LTrim$", 6, 1, T_STRING, 1 }, + { "LTrim", 6, 1, T_STRING, 1 }, + + { "RTrim$", 6, 2, T_STRING, 1 }, + { "RTrim", 6, 2, T_STRING, 1 }, + + { "Upper$", 7, 0, T_STRING, 1 }, + { "Upper", 7, 0, T_STRING, 1 }, + { "UCase$", 7, 0, T_STRING, 1 }, + { "UCase", 7, 0, T_STRING, 1 }, + + { "Lower$", 7, 1, T_STRING, 1 }, + { "Lower", 7, 1, T_STRING, 1 }, + { "LCase$", 7, 1, T_STRING, 1 }, + { "LCase", 7, 1, T_STRING, 1 }, + + { "Oct$", 8, 0, T_STRING, 1, 2 }, // CODE_OCT + { "Oct", 8, 0, T_STRING, 1, 2 }, + + { "Chr$", 9, 0, T_STRING, 1 }, + { "Chr", 9, 0, T_STRING, 1 }, + + { "Asc", 10, 0, T_INTEGER, 1, 2 }, + + { "InStr", 11, 0, T_INTEGER, 2, 4 }, + + { "RInStr", 12, 0, T_INTEGER, 2, 4 }, // CODE_RINSTR + + { "Subst$", 13, 0, T_STRING, 1, 63 }, + { "Subst", 13, 0, T_STRING, 1, 63 }, + + { "Replace$", 14, 0, T_STRING, 3, 4 }, + { "Replace", 14, 0, T_STRING, 3, 4 }, + + { "Split", 15, 0, T_OBJECT, 1, 5 }, + { "Scan", 16, 0, T_OBJECT, 2 }, + + { "Comp", 17, 0, T_INTEGER, 2, 3 }, + + { "Conv", 18, 0, T_STRING, 3 }, + { "Conv$", 18, 0, T_STRING, 3 }, + { "SConv", 19, 0, T_STRING, 1 }, + { "SConv$", 19, 0, T_STRING, 1 }, + { "DConv", 19, 1, T_STRING, 1 }, + { "DConv$", 19, 1, T_STRING, 1 }, + + { "Abs", 20, 0, RST_SAME, 1 }, // CODE_ABS + { "Int", 21, 0, RST_SAME, 1 }, + { "Fix", 22, 0, RST_SAME, 1 }, + { "Sgn", 23, 0, T_INTEGER, 1 }, + + { "Frac", 24, 1, T_FLOAT, 1 }, + { "Log", 24, 2, T_FLOAT, 1 }, + { "Exp", 24, 3, T_FLOAT, 1 }, + { "Sqr", 24, 4, T_FLOAT, 1 }, + { "Sin", 24, 5, T_FLOAT, 1 }, + { "Cos", 24, 6, T_FLOAT, 1 }, + { "Tan", 24, 7, T_FLOAT, 1 }, + { "Atn", 24, 8, T_FLOAT, 1 }, + { "ATan", 24, 8, T_FLOAT, 1 }, + { "Asn", 24, 9, T_FLOAT, 1 }, + { "ASin", 24, 9, T_FLOAT, 1 }, + { "Acs", 24, 10, T_FLOAT, 1 }, + { "ACos", 24, 10, T_FLOAT, 1 }, + { "Deg", 24, 11, T_FLOAT, 1 }, + { "Rad", 24, 12, T_FLOAT, 1 }, + { "Log10", 24, 13, T_FLOAT, 1 }, + { "Sinh", 24, 14, T_FLOAT, 1 }, + { "Cosh", 24, 15, T_FLOAT, 1 }, + { "Tanh", 24, 16, T_FLOAT, 1 }, + { "Asnh", 24, 17, T_FLOAT, 1 }, + { "ASinh", 24, 17, T_FLOAT, 1 }, + { "Acsh", 24, 18, T_FLOAT, 1 }, + { "ACosh", 24, 18, T_FLOAT, 1 }, + { "Atnh", 24, 19, T_FLOAT, 1 }, + { "ATanh", 24, 19, T_FLOAT, 1 }, + { "Exp2", 24, 20, T_FLOAT, 1 }, + { "Exp10", 24, 21, T_FLOAT, 1 }, + { "Log2", 24, 22, T_FLOAT, 1 }, + { "Cbr", 24, 23, T_FLOAT, 1 }, + { "Expm", 24, 24, T_FLOAT, 1 }, + { "Logp", 24, 25, T_FLOAT, 1 }, + { "Floor", 24, 26, T_FLOAT, 1 }, + { "Ceil", 24, 27, T_FLOAT, 1 }, + + { "Pi", 25, 0, T_FLOAT, 0, 1 }, + { "Round", 26, 0, T_FLOAT, 1, 2 }, +#ifndef __EVAL_RESERVED_C + { ".Randomize", 27, 0, T_VOID, 0, 1 }, +#endif + { "Rnd", 28, 0, T_FLOAT, 0, 2 }, + { "Min", 29, 0, RST_MIN, 2, }, + { "Max", 30, 0, RST_MIN, 2, }, // CODE_MAX + + { "If", 31, 0, T_VARIANT, 3, }, + { "IIf", 31, 0, T_VARIANT, 3, }, + { "Choose", 32, 0, T_VARIANT, 1, 63 }, + + { ".Array", 33, 0, T_OBJECT, 0, 63 }, // Needed for Eval("[...]") + + { "ATan2", 34, 1, T_FLOAT, 2 }, + { "Atn2", 34, 1, T_FLOAT, 2 }, + { "Ang", 34, 2, T_FLOAT, 2 }, + { "Hyp", 34, 3, T_FLOAT, 2 }, + { "Mag", 34, 3, T_FLOAT, 2 }, + + { "IsAscii", 35, 1, T_BOOLEAN, 1 }, + { "IsLetter", 35, 2, T_BOOLEAN, 1 }, + { "IsLCase", 35, 3, T_BOOLEAN, 1 }, + { "IsLower", 35, 3, T_BOOLEAN, 1 }, + { "IsUCase", 35, 4, T_BOOLEAN, 1 }, + { "IsUpper", 35, 4, T_BOOLEAN, 1 }, + { "IsDigit", 35, 5, T_BOOLEAN, 1 }, + { "IsHexa", 35, 6, T_BOOLEAN, 1 }, + { "IsSpace", 35, 7, T_BOOLEAN, 1 }, + { "IsBlank", 35, 8, T_BOOLEAN, 1 }, + { "IsPunct", 35, 9, T_BOOLEAN, 1 }, + { "IsAlnum", 35, 10, T_BOOLEAN, 1 }, + + { "BClr", 36, 1, RST_BCLR, 2 }, + { "BSet", 36, 2, RST_BCLR, 2 }, + { "BTst", 36, 3, RST_BCLR, 2 }, + { "BChg", 36, 4, RST_BCLR, 2 }, + { "Shl", 36, 5, RST_BCLR, 2 }, + { "Asl", 36, 5, RST_BCLR, 2 }, + { "Shr", 36, 6, RST_BCLR, 2 }, + { "Asr", 36, 6, RST_BCLR, 2 }, + { "Rol", 36, 7, RST_BCLR, 2 }, + { "Ror", 36, 8, RST_BCLR, 2 }, + { "Lsl", 36, 9, RST_BCLR, 2 }, + { "Lsr", 36, 10, RST_BCLR, 2 }, + + { "IsBoolean", 37, 1, T_BOOLEAN, 1 }, + { "IsInteger", 37, 4, T_BOOLEAN, 1 }, + { "IsLong", 37, 5, T_BOOLEAN, 1 }, + { "IsFloat", 37, 7, T_BOOLEAN, 1 }, + { "IsDate", 37, 8, T_BOOLEAN, 1 }, + { "IsNumber", 37, 14, T_BOOLEAN, 1 }, + { "IsNull", 37, 15, T_BOOLEAN, 1 }, + + { "TypeOf", 38, 0, T_INTEGER, 1 }, + { "SizeOf", 38, 1, T_INTEGER, 1 }, + + { "CBool", 39, 1, T_BOOLEAN, 1 }, // CODE_CONV + { "CBoolean", 39, 1, T_BOOLEAN, 1 }, + { "CByte", 39, 2, T_BYTE, 1 }, + { "CShort", 39, 3, T_SHORT, 1 }, + { "CInt", 39, 4, T_INTEGER, 1 }, + { "CInteger", 39, 4, T_INTEGER, 1 }, + { "CLong", 39, 5, T_LONG, 1 }, + { "CSingle", 39, 6, T_SINGLE, 1 }, + { "CFloat", 39, 7, T_FLOAT, 1 }, + { "CDate", 39, 8, T_DATE, 1 }, + { "CStr", 39, 9, T_STRING, 1 }, + { "CString", 39, 9, T_STRING, 1 }, + { "CPointer", 39, 11, T_POINTER, 1 }, + { "CVariant", 39, 12, T_VARIANT, 1 }, + + { "Bin$", 40, 0, T_STRING, 1, 2 }, // CODE_BIN + { "Bin", 40, 0, T_STRING, 1, 2 }, + + { "Hex$", 41, 0, T_STRING, 1, 2 }, // CODE_HEX + { "Hex", 41, 0, T_STRING, 1, 2 }, + + { "Val", 42, 0, T_VARIANT, 1 }, + + { "Str$", 43, 0, T_STRING, 1 }, + { "Str", 43, 0, T_STRING, 1 }, + + { "Format$", 44, 0, T_STRING, 1, 2 }, + { "Format", 44, 0, T_STRING, 1, 2 }, + + { "Timer", 45, 0, T_FLOAT, 0 }, + + { "Now", 46, 0, T_DATE, 0 }, + + { "Year", 47, 1, T_INTEGER, 1 }, + { "Month", 47, 2, T_INTEGER, 1 }, + { "Day", 47, 3, T_INTEGER, 1 }, + { "Hour", 47, 4, T_INTEGER, 1 }, + { "Minute", 47, 5, T_INTEGER, 1 }, + { "Second", 47, 6, T_INTEGER, 1 }, + { "WeekDay", 47, 7, T_INTEGER, 1 }, + { "Week", 48, 0, T_INTEGER, 0, 3 }, + + { "Date", 49, 0, T_DATE, 0, 7 }, + { "Time", 50, 0, T_DATE, 0, 4 }, + + { "DateAdd", 51, 0, T_DATE, 3 }, + { "DateDiff", 51, 1, T_INTEGER, 3 }, + + { "Eval", 52, 0, T_VARIANT, 1, 2 }, + +#ifndef __EVAL_RESERVED_C + { ".Error", 53, 0, T_BOOLEAN, 0 }, + { ".Debug", 54, 0, T_VOID, 0, 1 }, // CODE_DEBUG + + { ".Wait", 55, 0, T_VOID, 0, 1 }, + + { ".Open", 56, 0, T_OBJECT, 2 }, + { ".OpenMemory", 56, 1, T_OBJECT, 2 }, + { ".Close", 57, 0, T_VOID, 1 }, + { ".Input", 58, 0, T_STRING, 0, 1 }, + { ".LineInput", 59, 0, T_STRING, 1 }, + { ".Print", 60, 0, T_VOID, 1, 63 }, + { ".Read", 61, 0, RST_READ, 2, }, + { ".ReadBytes", 61, 1, T_STRING, 2, }, + { ".Write", 62, 0, T_VOID, 3, }, + { ".WriteBytes", 62, 1, T_VOID, 3, }, + { ".Flush", 63, 0, T_VOID, 1 }, + + { ".Lock", 64, 0, T_VOID, 1 }, + { ".Unlock", 64, 1, T_VOID, 1 }, + { ".LockWait", 64, 2, T_VOID, 2 }, + + { ".InputFrom", 65, 0, T_VOID, 1 }, + { ".OutputTo", 65, 1, T_VOID, 1 }, + { ".ErrorTo", 65, 2, T_VOID, 1 }, +#endif + { "Eof", 66, 0, T_BOOLEAN, 0, 1 }, + { "Lof", 67, 0, T_LONG, 0, 1 }, + { "Seek", 68, 0, T_LONG, 1, 3 }, +#ifndef __EVAL_RESERVED_C + { ".Kill", 69, 0, T_VOID, 1 }, + { ".Mkdir", 69, 1, T_VOID, 1 }, + { ".Rmdir", 69, 2, T_VOID, 1 }, +#endif +//{ ".Mkdir", 70, 0, 1 }, // The old Mkdir from 3.0 + { "Even", 70, 1, T_BOOLEAN, 1 }, + { "Odd", 70, 2, T_BOOLEAN, 1 }, +//{ ".Rmdir", 71, 0, 1 }, // The old Rmdir from 3.0 + { "Rand", 71, 0, T_INTEGER, 1, 2 }, +#ifndef __EVAL_RESERVED_C + { ".Move", 72, 0, T_VOID, 2 }, + { ".Copy", 72, 1, T_VOID, 2 }, + { ".Link", 72, 2, T_VOID, 2 }, + { ".Chmod", 72, 3, T_VOID, 2 }, + { ".Chown", 72, 4, T_VOID, 2 }, + { ".Chgrp", 72, 5, T_VOID, 2 }, + { ".MoveKill", 72, 6, T_VOID, 2 }, +#endif + { "Swap", 73, 0, T_STRING, 1, 2 }, // at least one argument, because 73 is a deprecated Copy() too. + { "Swap$", 73, 0, T_STRING, 1, 2 }, + + { "IsNan", 74, 1, T_BOOLEAN, 1 }, + { "IsInf", 74, 2, T_BOOLEAN, 1 }, + + { "Exist", 75, 0, T_BOOLEAN, 1, 2 }, + { "Access", 76, 0, T_BOOLEAN, 1, 2 }, + { "Stat", 77, 0, T_OBJECT, 1, 2 }, + { "Dfree", 78, 0, T_LONG, 1 }, + + { "Temp", 79, 0, T_STRING, 0, 1 }, + { "Temp$", 79, 0, T_STRING, 0, 1 }, + + { "IsDir", 80, 0, T_BOOLEAN, 1 }, + + { "Dir", 81, 0, T_OBJECT, 1, 3 }, + { "RDir", 82, 0, T_OBJECT, 1, 4 }, + +#ifndef __EVAL_RESERVED_C + { ".Exec", 83, 0, RST_EXEC, 4 }, + { ".Shell", 83, 1, RST_EXEC, 4 }, +#endif + + { "Alloc", 84, 0, T_POINTER, 1, 2 }, + { "Free", 85, 0, T_VOID, 1 }, + { "Realloc", 86, 0, T_POINTER, 2, 3 }, + { "Str@", 87, 0, T_STRING, 1, 2 }, + { "String@", 87, 0, T_STRING, 1, 2 }, + +#ifndef __EVAL_RESERVED_C + { ".Sleep", 88, 0, T_VOID, 1 }, + { ".Use", 88, 1, T_VOID, 1 }, + { ".CheckExec", 88, 2, T_VOID, 1 }, +#endif + + { "VarPtr", 89, 0, T_POINTER, 1 }, + { "IsMissing", 89, 1, T_BOOLEAN, 1 }, + + { ".Collection", 90, 0, RST_COLLECTION, 1, 63 }, + + { "Tr", 91, 0, T_STRING, 1 }, + { "Tr$", 91, 0, T_STRING, 1 }, + + { "Quote", 92, 0, T_STRING, 1 }, + { "Quote$", 92, 0, T_STRING, 1 }, + { "Shell", 92, 1, T_STRING, 1 }, + { "Shell$", 92, 1, T_STRING, 1 }, + { "Html", 92, 2, T_STRING, 1 }, + { "Html$", 92, 2, T_STRING, 1 }, + { "Base64", 92, 3, T_STRING, 1 }, + { "Base64$", 92, 3, T_STRING, 1 }, + { "Url", 92, 4, T_STRING, 1 }, + { "Url$", 92, 4, T_STRING, 1 }, + + { "UnQuote", 93, 0, T_STRING, 1 }, + { "UnQuote$", 93, 0, T_STRING, 1 }, + { "UnBase64", 93, 1, T_STRING, 1 }, + { "UnBase64$", 93, 1, T_STRING, 1 }, + { "FromBase64", 93, 1, T_STRING, 1 }, + { "FromBase64$", 93, 1, T_STRING, 1 }, + { "FromUrl", 93, 2, T_STRING, 1 }, + { "FromUrl$", 93, 2, T_STRING, 1 }, + + { "MkBool", 94, 1, T_STRING, 1 }, + { "MkBool$", 94, 1, T_STRING, 1 }, + { "MkBoolean", 94, 1, T_STRING, 1 }, + { "MkBoolean$", 94, 1, T_STRING, 1 }, + { "MkByte", 94, 2, T_STRING, 1 }, + { "MkByte$", 94, 2, T_STRING, 1 }, + { "MkShort", 94, 3, T_STRING, 1 }, + { "MkShort$", 94, 3, T_STRING, 1 }, + { "MkInt", 94, 4, T_STRING, 1 }, + { "MkInt$", 94, 4, T_STRING, 1 }, + { "MkInteger", 94, 4, T_STRING, 1 }, + { "MkInteger$", 94, 4, T_STRING, 1 }, + { "MkLong", 94, 5, T_STRING, 1 }, + { "MkLong$", 94, 5, T_STRING, 1 }, + { "MkSingle", 94, 6, T_STRING, 1 }, + { "MkSingle$", 94, 6, T_STRING, 1 }, + { "MkFloat", 94, 7, T_STRING, 1 }, + { "MkFloat$", 94, 7, T_STRING, 1 }, + { "MkDate", 94, 8, T_STRING, 1 }, + { "MkDate$", 94, 8, T_STRING, 1 }, + { "MkPointer", 94, 11, T_STRING, 1 }, + { "MkPointer$", 94, 11, T_STRING, 1 }, + + { "Bool@", 95, 1, T_BOOLEAN, 1 }, + { "Boolean@", 95, 1, T_BOOLEAN, 1 }, + { "Byte@", 95, 2, T_BYTE, 1 }, + { "Short@", 95, 3, T_SHORT, 1 }, + { "Int@", 95, 4, T_INTEGER, 1 }, + { "Integer@", 95, 4, T_INTEGER, 1 }, + { "Long@", 95, 5, T_LONG, 1 }, + { "Single@", 95, 6, T_SINGLE, 1 }, + { "Float@", 95, 7, T_FLOAT, 1 }, + { "Date@", 95, 8, T_DATE, 1 }, + { "Pointer@", 95, 11, T_POINTER, 1 }, + + { NULL } +}; + +#endif diff --git a/main/share/gb_reserved_temp.h b/main/share/gb_reserved_temp.h new file mode 100644 index 00000000..1cd5d0a9 --- /dev/null +++ b/main/share/gb_reserved_temp.h @@ -0,0 +1,966 @@ +/*************************************************************************** + + gb_reserved_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#include "gb_common.h" +#include "gb_common_case.h" +#include "gb_pcode.h" +#include "gb_type_common.h" +#include "gb_reserved.h" + +/* If this file is modified, don't forget to update GAMBAS_PCODE_VERSION in acinclude.m4 if needed */ + +#include "gb_reserved_keyword.h" + +int SUBR_VarPtr; +int SUBR_IsMissing; +int SUBR_Mid; +int SUBR_MidS; + +static uchar _operator_table[256] = { 0 }; + +static int get_index(const char *subr_name) +{ + return RESERVED_find_subr(subr_name, strlen(subr_name)); +} + +void RESERVED_init(void) +{ + COMP_INFO *info; + SUBR_INFO *subr; + int len; + int i; + + /* Reserved words symbol table */ + + //TABLE_create(&COMP_res_table, 0, TF_IGNORE_CASE); + for (info = &COMP_res_info[0], i = 0; info->name; info++, i++) + { + len = strlen(info->name); + if (len == 1) + _operator_table[(uint)*info->name] = i; + + //TABLE_add_symbol(COMP_res_table, info->name, len, &index); + } + + /* Subroutines table */ + + //TABLE_create(&COMP_subr_table, 0, TF_IGNORE_CASE); + for (subr = &COMP_subr_info[0]; subr->name; subr++) + { + if (subr->max_param == 0) + subr->max_param = subr->min_param; + + //TABLE_add_symbol(COMP_subr_table, subr->name, strlen(subr->name), &index); + } + + SUBR_VarPtr = get_index("VarPtr"); + SUBR_IsMissing = get_index("IsMissing"); + SUBR_Mid = get_index("Mid"); + SUBR_MidS = get_index("Mid$"); +} + + +void RESERVED_exit(void) +{ + //TABLE_delete(&COMP_res_table); + //TABLE_delete(&COMP_subr_table); +} + + +SUBR_INFO *SUBR_get(const char *subr_name) +{ + int index = get_index(subr_name); + + if (index == NO_SYMBOL) + return NULL; + else + return &COMP_subr_info[index]; +} + + +SUBR_INFO *SUBR_get_from_opcode(ushort opcode, ushort optype) +{ + SUBR_INFO *si; + + for (si = COMP_subr_info; si->name; si++) + { + if (si->opcode == opcode) + { + if (si->min_param < si->max_param) + return si; + else if (si->optype == optype) + return si; + } + } + + /*ERROR_panic("SUBR_get_from_opcode: SUBR not found !");*/ + return NULL; +} + + +int RESERVED_get_from_opcode(ushort code) +{ + COMP_INFO *ci; + int n; + + code = code & 0xFF00; + + for (ci = COMP_res_info, n = 0; ci->name; ci++, n++) + { + if (ci->code == code) + return n; + } + + return -1; +} + + +int RESERVED_find_word(const char *word, int len) +{ + int ind; + + if (len == 1) + { + ind = _operator_table[(uint)*word]; + if (ind) + return ind; + else + return -1; + } + + // No symbol longer than 10 characters in the table + + if (len > 10) + return -1; + + // Now find it + + static void *jump[] = { + &&__20, &&__21, &&__22, &&__23, &&__24, &&__25, &&__26, &&__27, + &&__28, &&__29, &&__2A, &&__2B, &&__2C, &&__2D, &&__2E, &&__2F, + &&__30, &&__31, &&__32, &&__33, &&__34, &&__35, &&__36, &&__37, + &&__38, &&__39, &&__3A, &&__3B, &&__3C, &&__3D, &&__3E, &&__3F, + &&__40, &&__41, &&__42, &&__43, &&__44, &&__45, &&__46, &&__47, + &&__48, &&__49, &&__4A, &&__4B, &&__4C, &&__4D, &&__4E, &&__4F, + &&__50, &&__51, &&__52, &&__53, &&__54, &&__55, &&__56, &&__57, + &&__58, &&__59, &&__5A, &&__5B, &&__5C, &&__5D, &&__5E, &&__5F, + &&__60, &&__61, &&__62, &&__63, &&__64, &&__65, &&__66, &&__67, + &&__68, &&__69, &&__6A, &&__6B, &&__6C, &&__6D, &&__6E, &&__6F, + &&__70, &&__71, &&__72, &&__73, &&__74, &&__75, &&__76, &&__77, + &&__78, &&__79, &&__7A, &&__7B, &&__7C, &&__7D, &&__7E, + }; + + goto *jump[*word - 32]; + +__20: + return -1; +__21: + return -1; +__22: + return -1; +__23: + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'f') return 122; + if (len == 5 && tolower(word[1]) == 'e' && tolower(word[2]) == 'l' && tolower(word[3]) == 's' && tolower(word[4]) == 'e') return 123; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 'e') return 126; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'o' && tolower(word[3]) == 'n' && tolower(word[4]) == 's' && tolower(word[5]) == 't') return 125; + if (len == 6 && tolower(word[1]) == 'e' && tolower(word[2]) == 'n' && tolower(word[3]) == 'd' && tolower(word[4]) == 'i' && tolower(word[5]) == 'f') return 124; + return -1; +__24: + return -1; +__25: + if (len == 2 && word[1] == '=') return 179; + return -1; +__26: + if (len == 2 && word[1] == '/') return 148; + if (len == 2 && word[1] == '=') return 180; + if (len == 3 && word[1] == '/' && word[2] == '=') return 181; + return -1; +__27: + return -1; +__28: + return -1; +__29: + return -1; +__2A: + if (len == 2 && word[1] == '=') return 176; + return -1; +__2B: + if (len == 2 && word[1] == '=') return 174; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'f') return 82; + return -1; +__2C: + return -1; +__2D: + if (len == 2 && word[1] == '=') return 175; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'f') return 83; + return -1; +__2E: + if (len == 3 && word[1] == '.' && word[2] == '.') return 130; + return -1; +__2F: + if (len == 2 && word[1] == '=') return 177; + return -1; +__30: + return -1; +__31: + return -1; +__32: + return -1; +__33: + return -1; +__34: + return -1; +__35: + return -1; +__36: + return -1; +__37: + return -1; +__38: + return -1; +__39: + return -1; +__3A: + return -1; +__3B: + return -1; +__3C: + if (len == 2 && word[1] == '=') return 152; + if (len == 2 && word[1] == '>') return 153; + return -1; +__3D: + if (len == 2 && word[1] == '=') return 137; + return -1; +__3E: + if (len == 2 && word[1] == '=') return 151; + return -1; +__3F: + return -1; +__40: + return -1; +__41: +__61: + if (len == 2 && tolower(word[1]) == 's') return 27; + if (len == 3 && tolower(word[1]) == 'n' && tolower(word[2]) == 'd') return 156; + if (len == 6 && tolower(word[1]) == 'p' && tolower(word[2]) == 'p' && tolower(word[3]) == 'e' && tolower(word[4]) == 'n' && tolower(word[5]) == 'd') return 92; + if (len == 6 && tolower(word[1]) == 's' && tolower(word[2]) == 's' && tolower(word[3]) == 'e' && tolower(word[4]) == 'r' && tolower(word[5]) == 't') return 114; + return -1; +__42: +__62: + if (len == 4 && tolower(word[1]) == 'y' && tolower(word[2]) == 't' && tolower(word[3]) == 'e') return 2; + if (len == 5 && tolower(word[1]) == 'r' && tolower(word[2]) == 'e' && tolower(word[3]) == 'a' && tolower(word[4]) == 'k') return 56; + if (len == 5 && tolower(word[1]) == 'y' && tolower(word[2]) == 'r' && tolower(word[3]) == 'e' && tolower(word[4]) == 'f') return 117; + if (len == 6 && tolower(word[1]) == 'e' && tolower(word[2]) == 'g' && tolower(word[3]) == 'i' && tolower(word[4]) == 'n' && tolower(word[5]) == 's') return 168; + if (len == 6 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'a' && tolower(word[4]) == 'r' && tolower(word[5]) == 'y') return 94; + if (len == 7 && tolower(word[1]) == 'o' && tolower(word[2]) == 'o' && tolower(word[3]) == 'l' && tolower(word[4]) == 'e' && tolower(word[5]) == 'a' && tolower(word[6]) == 'n') return 1; + return -1; +__43: +__63: + if (len == 4 && tolower(word[1]) == 'a' && tolower(word[2]) == 's' && tolower(word[3]) == 'e') return 54; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'p' && tolower(word[3]) == 'y') return 103; + if (len == 5 && tolower(word[1]) == 'a' && tolower(word[2]) == 't' && tolower(word[3]) == 'c' && tolower(word[4]) == 'h') return 65; + if (len == 5 && tolower(word[1]) == 'h' && tolower(word[2]) == 'g' && tolower(word[3]) == 'r' && tolower(word[4]) == 'p') return 121; + if (len == 5 && tolower(word[1]) == 'h' && tolower(word[2]) == 'm' && tolower(word[3]) == 'o' && tolower(word[4]) == 'd') return 119; + if (len == 5 && tolower(word[1]) == 'h' && tolower(word[2]) == 'o' && tolower(word[3]) == 'w' && tolower(word[4]) == 'n') return 120; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'a' && tolower(word[3]) == 's' && tolower(word[4]) == 's') return 13; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'o' && tolower(word[3]) == 's' && tolower(word[4]) == 'e') return 90; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 'n' && tolower(word[3]) == 's' && tolower(word[4]) == 't') return 16; + if (len == 6 && tolower(word[1]) == 'r' && tolower(word[2]) == 'e' && tolower(word[3]) == 'a' && tolower(word[4]) == 't' && tolower(word[5]) == 'e') return 93; + if (len == 8 && tolower(word[1]) == 'o' && tolower(word[2]) == 'n' && tolower(word[3]) == 't' && tolower(word[4]) == 'i' && tolower(word[5]) == 'n' && tolower(word[6]) == 'u' && tolower(word[7]) == 'e') return 57; + return -1; +__44: +__64: + if (len == 2 && tolower(word[1]) == 'o') return 36; + if (len == 3 && tolower(word[1]) == 'e' && tolower(word[2]) == 'c') return 105; + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'm') return 29; + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'v') return 161; + if (len == 4 && tolower(word[1]) == 'a' && tolower(word[2]) == 't' && tolower(word[3]) == 'e') return 3; + if (len == 5 && tolower(word[1]) == 'e' && tolower(word[2]) == 'b' && tolower(word[3]) == 'u' && tolower(word[4]) == 'g') return 113; + if (len == 6 && tolower(word[1]) == 'o' && tolower(word[2]) == 'w' && tolower(word[3]) == 'n' && tolower(word[4]) == 't' && tolower(word[5]) == 'o') return 49; + if (len == 7 && tolower(word[1]) == 'e' && tolower(word[2]) == 'f' && tolower(word[3]) == 'a' && tolower(word[4]) == 'u' && tolower(word[5]) == 'l' && tolower(word[6]) == 't') return 74; + return -1; +__45: +__65: + if (len == 3 && tolower(word[1]) == 'n' && tolower(word[2]) == 'd') return 46; + if (len == 4 && tolower(word[1]) == 'a' && tolower(word[2]) == 'c' && tolower(word[3]) == 'h') return 72; + if (len == 4 && tolower(word[1]) == 'l' && tolower(word[2]) == 's' && tolower(word[3]) == 'e') return 44; + if (len == 4 && tolower(word[1]) == 'n' && tolower(word[2]) == 'd' && tolower(word[3]) == 's') return 170; + if (len == 4 && tolower(word[1]) == 'n' && tolower(word[2]) == 'u' && tolower(word[3]) == 'm') return 80; + if (len == 4 && tolower(word[1]) == 'x' && tolower(word[2]) == 'e' && tolower(word[3]) == 'c') return 97; + if (len == 4 && tolower(word[1]) == 'x' && tolower(word[2]) == 'i' && tolower(word[3]) == 't') return 55; + if (len == 5 && tolower(word[1]) == 'n' && tolower(word[2]) == 'd' && tolower(word[3]) == 'i' && tolower(word[4]) == 'f') return 45; + if (len == 5 && tolower(word[1]) == 'r' && tolower(word[2]) == 'r' && tolower(word[3]) == 'o' && tolower(word[4]) == 'r') return 78; + if (len == 5 && tolower(word[1]) == 'v' && tolower(word[2]) == 'e' && tolower(word[3]) == 'n' && tolower(word[4]) == 't') return 23; + if (len == 6 && tolower(word[1]) == 'x' && tolower(word[2]) == 'p' && tolower(word[3]) == 'o' && tolower(word[4]) == 'r' && tolower(word[5]) == 't') return 26; + if (len == 6 && tolower(word[1]) == 'x' && tolower(word[2]) == 't' && tolower(word[3]) == 'e' && tolower(word[4]) == 'r' && tolower(word[5]) == 'n') return 71; + return -1; +__46: +__66: + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'r') return 47; + if (len == 4 && tolower(word[1]) == 'a' && tolower(word[2]) == 's' && tolower(word[3]) == 't') return 20; + if (len == 4 && tolower(word[1]) == 'r' && tolower(word[2]) == 'o' && tolower(word[3]) == 'm') return 50; + if (len == 5 && tolower(word[1]) == 'a' && tolower(word[2]) == 'l' && tolower(word[3]) == 's' && tolower(word[4]) == 'e') return 68; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'o' && tolower(word[3]) == 'a' && tolower(word[4]) == 't') return 5; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'u' && tolower(word[3]) == 's' && tolower(word[4]) == 'h') return 96; + if (len == 7 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'a' && tolower(word[4]) == 'l' && tolower(word[5]) == 'l' && tolower(word[6]) == 'y') return 64; + if (len == 8 && tolower(word[1]) == 'u' && tolower(word[2]) == 'n' && tolower(word[3]) == 'c' && tolower(word[4]) == 't' && tolower(word[5]) == 'i' && tolower(word[6]) == 'o' && tolower(word[7]) == 'n') return 14; + return -1; +__47: +__67: + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 't' && tolower(word[3]) == 'o') return 58; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 's' && tolower(word[3]) == 'u' && tolower(word[4]) == 'b') return 59; + return -1; +__48: +__68: + return -1; +__49: +__69: + if (len == 2 && tolower(word[1]) == 'f') return 42; + if (len == 2 && tolower(word[1]) == 'n') return 73; + if (len == 2 && tolower(word[1]) == 's') return 164; + if (len == 3 && tolower(word[1]) == 'n' && tolower(word[2]) == 'c') return 104; + if (len == 5 && tolower(word[1]) == 'n' && tolower(word[2]) == 'p' && tolower(word[3]) == 'u' && tolower(word[4]) == 't') return 86; + if (len == 7 && tolower(word[1]) == 'n' && tolower(word[2]) == 't' && tolower(word[3]) == 'e' && tolower(word[4]) == 'g' && tolower(word[5]) == 'e' && tolower(word[6]) == 'r') return 6; + if (len == 8 && tolower(word[1]) == 'n' && tolower(word[2]) == 'h' && tolower(word[3]) == 'e' && tolower(word[4]) == 'r' && tolower(word[5]) == 'i' && tolower(word[6]) == 't' && tolower(word[7]) == 's') return 24; + if (len == 10 && tolower(word[1]) == 'm' && tolower(word[2]) == 'p' && tolower(word[3]) == 'l' && tolower(word[4]) == 'e' && tolower(word[5]) == 'm' && tolower(word[6]) == 'e' && tolower(word[7]) == 'n' && tolower(word[8]) == 't' && tolower(word[9]) == 's') return 25; + return -1; +__4A: +__6A: + return -1; +__4B: +__6B: + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'l' && tolower(word[3]) == 'l') return 101; + return -1; +__4C: +__6C: + if (len == 3 && tolower(word[1]) == 'e' && tolower(word[2]) == 't') return 81; + if (len == 4 && tolower(word[1]) == 'a' && tolower(word[2]) == 's' && tolower(word[3]) == 't') return 62; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'k' && tolower(word[3]) == 'e') return 166; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'e') return 95; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'k') return 109; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'c' && tolower(word[3]) == 'k') return 110; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'n' && tolower(word[3]) == 'g') return 7; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'o' && tolower(word[3]) == 'p') return 37; + if (len == 7 && tolower(word[1]) == 'i' && tolower(word[2]) == 'b' && tolower(word[3]) == 'r' && tolower(word[4]) == 'a' && tolower(word[5]) == 'r' && tolower(word[6]) == 'y') return 112; + return -1; +__4D: +__6D: + if (len == 2 && tolower(word[1]) == 'e') return 61; + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'd') return 163; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'v' && tolower(word[3]) == 'e') return 102; + if (len == 5 && tolower(word[1]) == 'a' && tolower(word[2]) == 't' && tolower(word[3]) == 'c' && tolower(word[4]) == 'h') return 172; + if (len == 5 && tolower(word[1]) == 'k' && tolower(word[2]) == 'd' && tolower(word[3]) == 'i' && tolower(word[4]) == 'r') return 106; + if (len == 6 && tolower(word[1]) == 'e' && tolower(word[2]) == 'm' && tolower(word[3]) == 'o' && tolower(word[4]) == 'r' && tolower(word[5]) == 'y') return 118; + return -1; +__4E: +__6E: + if (len == 3 && tolower(word[1]) == 'e' && tolower(word[2]) == 'w') return 30; + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 't') return 158; + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'x' && tolower(word[3]) == 't') return 52; + if (len == 4 && tolower(word[1]) == 'u' && tolower(word[2]) == 'l' && tolower(word[3]) == 'l') return 70; + return -1; +__4F: +__6F: + if (len == 2 && tolower(word[1]) == 'f') return 28; + if (len == 2 && tolower(word[1]) == 'n') return 60; + if (len == 2 && tolower(word[1]) == 'r') return 157; + if (len == 4 && tolower(word[1]) == 'p' && tolower(word[2]) == 'e' && tolower(word[3]) == 'n') return 89; + if (len == 6 && tolower(word[1]) == 'b' && tolower(word[2]) == 'j' && tolower(word[3]) == 'e' && tolower(word[4]) == 'c' && tolower(word[5]) == 't') return 11; + if (len == 6 && tolower(word[1]) == 'u' && tolower(word[2]) == 't' && tolower(word[3]) == 'p' && tolower(word[4]) == 'u' && tolower(word[5]) == 't') return 35; + if (len == 8 && tolower(word[1]) == 'p' && tolower(word[2]) == 't' && tolower(word[3]) == 'i' && tolower(word[4]) == 'o' && tolower(word[5]) == 'n' && tolower(word[6]) == 'a' && tolower(word[7]) == 'l') return 34; + return -1; +__50: +__70: + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'p' && tolower(word[3]) == 'e') return 115; + if (len == 5 && tolower(word[1]) == 'r' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 't') return 85; + if (len == 6 && tolower(word[1]) == 'u' && tolower(word[2]) == 'b' && tolower(word[3]) == 'l' && tolower(word[4]) == 'i' && tolower(word[5]) == 'c') return 18; + if (len == 7 && tolower(word[1]) == 'o' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 't' && tolower(word[5]) == 'e' && tolower(word[6]) == 'r') return 12; + if (len == 7 && tolower(word[1]) == 'r' && tolower(word[2]) == 'i' && tolower(word[3]) == 'v' && tolower(word[4]) == 'a' && tolower(word[5]) == 't' && tolower(word[6]) == 'e') return 17; + if (len == 8 && tolower(word[1]) == 'r' && tolower(word[2]) == 'o' && tolower(word[3]) == 'p' && tolower(word[4]) == 'e' && tolower(word[5]) == 'r' && tolower(word[6]) == 't' && tolower(word[7]) == 'y') return 22; + if (len == 9 && tolower(word[1]) == 'r' && tolower(word[2]) == 'o' && tolower(word[3]) == 'c' && tolower(word[4]) == 'e' && tolower(word[5]) == 'd' && tolower(word[6]) == 'u' && tolower(word[7]) == 'r' && tolower(word[8]) == 'e') return 31; + return -1; +__51: +__71: + if (len == 4 && tolower(word[1]) == 'u' && tolower(word[2]) == 'i' && tolower(word[3]) == 't') return 76; + return -1; +__52: +__72: + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'a' && tolower(word[3]) == 'd') return 87; + if (len == 5 && tolower(word[1]) == 'a' && tolower(word[2]) == 'i' && tolower(word[3]) == 's' && tolower(word[4]) == 'e') return 77; + if (len == 5 && tolower(word[1]) == 'm' && tolower(word[2]) == 'd' && tolower(word[3]) == 'i' && tolower(word[4]) == 'r') return 107; + if (len == 6 && tolower(word[1]) == 'e' && tolower(word[2]) == 'p' && tolower(word[3]) == 'e' && tolower(word[4]) == 'a' && tolower(word[5]) == 't') return 40; + if (len == 6 && tolower(word[1]) == 'e' && tolower(word[2]) == 't' && tolower(word[3]) == 'u' && tolower(word[4]) == 'r' && tolower(word[5]) == 'n') return 33; + if (len == 9 && tolower(word[1]) == 'a' && tolower(word[2]) == 'n' && tolower(word[3]) == 'd' && tolower(word[4]) == 'o' && tolower(word[5]) == 'm' && tolower(word[6]) == 'i' && tolower(word[7]) == 'z' && tolower(word[8]) == 'e') return 116; + return -1; +__53: +__73: + if (len == 3 && tolower(word[1]) == 'u' && tolower(word[2]) == 'b') return 32; + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'e' && tolower(word[3]) == 'k') return 91; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 'e' && tolower(word[3]) == 'p') return 51; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 'o' && tolower(word[3]) == 'p') return 75; + if (len == 4 && tolower(word[1]) == 'w' && tolower(word[2]) == 'a' && tolower(word[3]) == 'p') return 69; + if (len == 5 && tolower(word[1]) == 'h' && tolower(word[2]) == 'e' && tolower(word[3]) == 'l' && tolower(word[4]) == 'l') return 98; + if (len == 5 && tolower(word[1]) == 'h' && tolower(word[2]) == 'o' && tolower(word[3]) == 'r' && tolower(word[4]) == 't') return 8; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'e' && tolower(word[3]) == 'e' && tolower(word[4]) == 'p') return 100; + if (len == 5 && tolower(word[1]) == 'u' && tolower(word[2]) == 'p' && tolower(word[3]) == 'e' && tolower(word[4]) == 'r') return 79; + if (len == 6 && tolower(word[1]) == 'e' && tolower(word[2]) == 'l' && tolower(word[3]) == 'e' && tolower(word[4]) == 'c' && tolower(word[5]) == 't') return 53; + if (len == 6 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'g' && tolower(word[4]) == 'l' && tolower(word[5]) == 'e') return 4; + if (len == 6 && tolower(word[1]) == 't' && tolower(word[2]) == 'a' && tolower(word[3]) == 't' && tolower(word[4]) == 'i' && tolower(word[5]) == 'c') return 19; + if (len == 6 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'n' && tolower(word[5]) == 'g') return 9; + if (len == 6 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && tolower(word[3]) == 'u' && tolower(word[4]) == 'c' && tolower(word[5]) == 't') return 15; + return -1; +__54: +__74: + if (len == 2 && tolower(word[1]) == 'o') return 48; + if (len == 3 && tolower(word[1]) == 'r' && tolower(word[2]) == 'y') return 63; + if (len == 4 && tolower(word[1]) == 'h' && tolower(word[2]) == 'e' && tolower(word[3]) == 'n') return 43; + if (len == 4 && tolower(word[1]) == 'r' && tolower(word[2]) == 'u' && tolower(word[3]) == 'e') return 67; + return -1; +__55: +__75: + if (len == 3 && tolower(word[1]) == 's' && tolower(word[2]) == 'e') return 84; + if (len == 5 && tolower(word[1]) == 'n' && tolower(word[2]) == 't' && tolower(word[3]) == 'i' && tolower(word[4]) == 'l') return 39; + if (len == 6 && tolower(word[1]) == 'n' && tolower(word[2]) == 'l' && tolower(word[3]) == 'o' && tolower(word[4]) == 'c' && tolower(word[5]) == 'k') return 111; + if (len == 6 && tolower(word[1]) == 'n' && tolower(word[2]) == 's' && tolower(word[3]) == 'a' && tolower(word[4]) == 'f' && tolower(word[5]) == 'e') return 21; + return -1; +__56: +__76: + if (len == 7 && tolower(word[1]) == 'a' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'a' && tolower(word[5]) == 'n' && tolower(word[6]) == 't') return 10; + return -1; +__57: +__77: + if (len == 4 && tolower(word[1]) == 'a' && tolower(word[2]) == 'i' && tolower(word[3]) == 't') return 99; + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'n' && tolower(word[3]) == 'd') return 41; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 't' && tolower(word[3]) == 'h') return 66; + if (len == 5 && tolower(word[1]) == 'a' && tolower(word[2]) == 't' && tolower(word[3]) == 'c' && tolower(word[4]) == 'h') return 108; + if (len == 5 && tolower(word[1]) == 'h' && tolower(word[2]) == 'i' && tolower(word[3]) == 'l' && tolower(word[4]) == 'e') return 38; + if (len == 5 && tolower(word[1]) == 'r' && tolower(word[2]) == 'i' && tolower(word[3]) == 't' && tolower(word[4]) == 'e') return 88; + return -1; +__58: +__78: + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'r') return 159; + return -1; +__59: +__79: + return -1; +__5A: +__7A: + return -1; +__5B: + return -1; +__5C: + if (len == 2 && word[1] == '=') return 178; + return -1; +__5D: + return -1; +__5E: + if (len == 2 && word[1] == '=') return 182; + return -1; +__5F: + return -1; +__60: + return -1; +__7B: + return -1; +__7C: + return -1; +__7D: + return -1; +__7E: + return -1; +} + +int RESERVED_find_subr(const char *word, int len) +{ + // No symbol longer than 11 characters in the table + + if (len > 11) + return -1; + + // Now find it + + static void *jump[] = { + &&__20, &&__21, &&__22, &&__23, &&__24, &&__25, &&__26, &&__27, + &&__28, &&__29, &&__2A, &&__2B, &&__2C, &&__2D, &&__2E, &&__2F, + &&__30, &&__31, &&__32, &&__33, &&__34, &&__35, &&__36, &&__37, + &&__38, &&__39, &&__3A, &&__3B, &&__3C, &&__3D, &&__3E, &&__3F, + &&__40, &&__41, &&__42, &&__43, &&__44, &&__45, &&__46, &&__47, + &&__48, &&__49, &&__4A, &&__4B, &&__4C, &&__4D, &&__4E, &&__4F, + &&__50, &&__51, &&__52, &&__53, &&__54, &&__55, &&__56, &&__57, + &&__58, &&__59, &&__5A, &&__5B, &&__5C, &&__5D, &&__5E, &&__5F, + &&__60, &&__61, &&__62, &&__63, &&__64, &&__65, &&__66, &&__67, + &&__68, &&__69, &&__6A, &&__6B, &&__6C, &&__6D, &&__6E, &&__6F, + &&__70, &&__71, &&__72, &&__73, &&__74, &&__75, &&__76, &&__77, + &&__78, &&__79, &&__7A, &&__7B, &&__7C, &&__7D, &&__7E, + }; + + goto *jump[*word - 32]; + +__20: + return -1; +__21: + return -1; +__22: + return -1; +__23: + return -1; +__24: + return -1; +__25: + return -1; +__26: + return -1; +__27: + return -1; +__28: + return -1; +__29: + return -1; +__2A: + return -1; +__2B: + return -1; +__2C: + return -1; +__2D: + return -1; +__2E: + if (len == 4 && tolower(word[1]) == 'u' && tolower(word[2]) == 's' && tolower(word[3]) == 'e') return 225; + if (len == 5 && tolower(word[1]) == 'c' && tolower(word[2]) == 'o' && tolower(word[3]) == 'p' && tolower(word[4]) == 'y') return 198; + if (len == 5 && tolower(word[1]) == 'e' && tolower(word[2]) == 'x' && tolower(word[3]) == 'e' && tolower(word[4]) == 'c') return 217; + if (len == 5 && tolower(word[1]) == 'k' && tolower(word[2]) == 'i' && tolower(word[3]) == 'l' && tolower(word[4]) == 'l') return 191; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 'k') return 199; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'o' && tolower(word[3]) == 'c' && tolower(word[4]) == 'k') return 182; + if (len == 5 && tolower(word[1]) == 'm' && tolower(word[2]) == 'o' && tolower(word[3]) == 'v' && tolower(word[4]) == 'e') return 197; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 'p' && tolower(word[3]) == 'e' && tolower(word[4]) == 'n') return 171; + if (len == 5 && tolower(word[1]) == 'r' && tolower(word[2]) == 'e' && tolower(word[3]) == 'a' && tolower(word[4]) == 'd') return 177; + if (len == 5 && tolower(word[1]) == 'w' && tolower(word[2]) == 'a' && tolower(word[3]) == 'i' && tolower(word[4]) == 't') return 170; + if (len == 6 && tolower(word[1]) == 'a' && tolower(word[2]) == 'r' && tolower(word[3]) == 'r' && tolower(word[4]) == 'a' && tolower(word[5]) == 'y') return 91; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'h' && tolower(word[3]) == 'g' && tolower(word[4]) == 'r' && tolower(word[5]) == 'p') return 202; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'h' && tolower(word[3]) == 'm' && tolower(word[4]) == 'o' && tolower(word[5]) == 'd') return 200; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'h' && tolower(word[3]) == 'o' && tolower(word[4]) == 'w' && tolower(word[5]) == 'n') return 201; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'l' && tolower(word[3]) == 'o' && tolower(word[4]) == 's' && tolower(word[5]) == 'e') return 173; + if (len == 6 && tolower(word[1]) == 'd' && tolower(word[2]) == 'e' && tolower(word[3]) == 'b' && tolower(word[4]) == 'u' && tolower(word[5]) == 'g') return 169; + if (len == 6 && tolower(word[1]) == 'e' && tolower(word[2]) == 'r' && tolower(word[3]) == 'r' && tolower(word[4]) == 'o' && tolower(word[5]) == 'r') return 168; + if (len == 6 && tolower(word[1]) == 'f' && tolower(word[2]) == 'l' && tolower(word[3]) == 'u' && tolower(word[4]) == 's' && tolower(word[5]) == 'h') return 181; + if (len == 6 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'p' && tolower(word[4]) == 'u' && tolower(word[5]) == 't') return 174; + if (len == 6 && tolower(word[1]) == 'm' && tolower(word[2]) == 'k' && tolower(word[3]) == 'd' && tolower(word[4]) == 'i' && tolower(word[5]) == 'r') return 192; + if (len == 6 && tolower(word[1]) == 'p' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'n' && tolower(word[5]) == 't') return 176; + if (len == 6 && tolower(word[1]) == 'r' && tolower(word[2]) == 'm' && tolower(word[3]) == 'd' && tolower(word[4]) == 'i' && tolower(word[5]) == 'r') return 193; + if (len == 6 && tolower(word[1]) == 's' && tolower(word[2]) == 'h' && tolower(word[3]) == 'e' && tolower(word[4]) == 'l' && tolower(word[5]) == 'l') return 218; + if (len == 6 && tolower(word[1]) == 's' && tolower(word[2]) == 'l' && tolower(word[3]) == 'e' && tolower(word[4]) == 'e' && tolower(word[5]) == 'p') return 224; + if (len == 6 && tolower(word[1]) == 'w' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 't' && tolower(word[5]) == 'e') return 179; + if (len == 7 && tolower(word[1]) == 'u' && tolower(word[2]) == 'n' && tolower(word[3]) == 'l' && tolower(word[4]) == 'o' && tolower(word[5]) == 'c' && tolower(word[6]) == 'k') return 183; + if (len == 8 && tolower(word[1]) == 'e' && tolower(word[2]) == 'r' && tolower(word[3]) == 'r' && tolower(word[4]) == 'o' && tolower(word[5]) == 'r' && tolower(word[6]) == 't' && tolower(word[7]) == 'o') return 187; + if (len == 9 && tolower(word[1]) == 'l' && tolower(word[2]) == 'o' && tolower(word[3]) == 'c' && tolower(word[4]) == 'k' && tolower(word[5]) == 'w' && tolower(word[6]) == 'a' && tolower(word[7]) == 'i' && tolower(word[8]) == 't') return 184; + if (len == 9 && tolower(word[1]) == 'm' && tolower(word[2]) == 'o' && tolower(word[3]) == 'v' && tolower(word[4]) == 'e' && tolower(word[5]) == 'k' && tolower(word[6]) == 'i' && tolower(word[7]) == 'l' && tolower(word[8]) == 'l') return 203; + if (len == 9 && tolower(word[1]) == 'o' && tolower(word[2]) == 'u' && tolower(word[3]) == 't' && tolower(word[4]) == 'p' && tolower(word[5]) == 'u' && tolower(word[6]) == 't' && tolower(word[7]) == 't' && tolower(word[8]) == 'o') return 186; + if (len == 10 && tolower(word[1]) == 'c' && tolower(word[2]) == 'h' && tolower(word[3]) == 'e' && tolower(word[4]) == 'c' && tolower(word[5]) == 'k' && tolower(word[6]) == 'e' && tolower(word[7]) == 'x' && tolower(word[8]) == 'e' && tolower(word[9]) == 'c') return 226; + if (len == 10 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'p' && tolower(word[4]) == 'u' && tolower(word[5]) == 't' && tolower(word[6]) == 'f' && tolower(word[7]) == 'r' && tolower(word[8]) == 'o' && tolower(word[9]) == 'm') return 185; + if (len == 10 && tolower(word[1]) == 'l' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 'e' && tolower(word[5]) == 'i' && tolower(word[6]) == 'n' && tolower(word[7]) == 'p' && tolower(word[8]) == 'u' && tolower(word[9]) == 't') return 175; + if (len == 10 && tolower(word[1]) == 'r' && tolower(word[2]) == 'a' && tolower(word[3]) == 'n' && tolower(word[4]) == 'd' && tolower(word[5]) == 'o' && tolower(word[6]) == 'm' && tolower(word[7]) == 'i' && tolower(word[8]) == 'z' && tolower(word[9]) == 'e') return 84; + if (len == 10 && tolower(word[1]) == 'r' && tolower(word[2]) == 'e' && tolower(word[3]) == 'a' && tolower(word[4]) == 'd' && tolower(word[5]) == 'b' && tolower(word[6]) == 'y' && tolower(word[7]) == 't' && tolower(word[8]) == 'e' && tolower(word[9]) == 's') return 178; + if (len == 11 && tolower(word[1]) == 'c' && tolower(word[2]) == 'o' && tolower(word[3]) == 'l' && tolower(word[4]) == 'l' && tolower(word[5]) == 'e' && tolower(word[6]) == 'c' && tolower(word[7]) == 't' && tolower(word[8]) == 'i' && tolower(word[9]) == 'o' && tolower(word[10]) == 'n') return 229; + if (len == 11 && tolower(word[1]) == 'o' && tolower(word[2]) == 'p' && tolower(word[3]) == 'e' && tolower(word[4]) == 'n' && tolower(word[5]) == 'm' && tolower(word[6]) == 'e' && tolower(word[7]) == 'm' && tolower(word[8]) == 'o' && tolower(word[9]) == 'r' && tolower(word[10]) == 'y') return 172; + if (len == 11 && tolower(word[1]) == 'w' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 't' && tolower(word[5]) == 'e' && tolower(word[6]) == 'b' && tolower(word[7]) == 'y' && tolower(word[8]) == 't' && tolower(word[9]) == 'e' && tolower(word[10]) == 's') return 180; + return -1; +__2F: + return -1; +__30: + return -1; +__31: + return -1; +__32: + return -1; +__33: + return -1; +__34: + return -1; +__35: + return -1; +__36: + return -1; +__37: + return -1; +__38: + return -1; +__39: + return -1; +__3A: + return -1; +__3B: + return -1; +__3C: + return -1; +__3D: + return -1; +__3E: + return -1; +__3F: + return -1; +__40: + return -1; +__41: +__61: + if (len == 3 && tolower(word[1]) == 'b' && tolower(word[2]) == 's') return 45; + if (len == 3 && tolower(word[1]) == 'c' && tolower(word[2]) == 's') return 60; + if (len == 3 && tolower(word[1]) == 'n' && tolower(word[2]) == 'g') return 94; + if (len == 3 && tolower(word[1]) == 's' && tolower(word[2]) == 'c') return 29; + if (len == 3 && tolower(word[1]) == 's' && tolower(word[2]) == 'l') return 114; + if (len == 3 && tolower(word[1]) == 's' && tolower(word[2]) == 'n') return 58; + if (len == 3 && tolower(word[1]) == 's' && tolower(word[2]) == 'r') return 116; + if (len == 3 && tolower(word[1]) == 't' && tolower(word[2]) == 'n') return 56; + if (len == 4 && tolower(word[1]) == 'c' && tolower(word[2]) == 'o' && tolower(word[3]) == 's') return 61; + if (len == 4 && tolower(word[1]) == 'c' && tolower(word[2]) == 's' && tolower(word[3]) == 'h') return 70; + if (len == 4 && tolower(word[1]) == 's' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n') return 59; + if (len == 4 && tolower(word[1]) == 's' && tolower(word[2]) == 'n' && tolower(word[3]) == 'h') return 68; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 'a' && tolower(word[3]) == 'n') return 57; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 'n' && word[3] == '2') return 93; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 'n' && tolower(word[3]) == 'h') return 72; + if (len == 5 && tolower(word[1]) == 'c' && tolower(word[2]) == 'o' && tolower(word[3]) == 's' && tolower(word[4]) == 'h') return 71; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'l' && tolower(word[3]) == 'o' && tolower(word[4]) == 'c') return 219; + if (len == 5 && tolower(word[1]) == 's' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 'h') return 69; + if (len == 5 && tolower(word[1]) == 't' && tolower(word[2]) == 'a' && tolower(word[3]) == 'n' && word[4] == '2') return 92; + if (len == 5 && tolower(word[1]) == 't' && tolower(word[2]) == 'a' && tolower(word[3]) == 'n' && tolower(word[4]) == 'h') return 73; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'c' && tolower(word[3]) == 'e' && tolower(word[4]) == 's' && tolower(word[5]) == 's') return 209; + return -1; +__42: +__62: + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n') return 145; + if (len == 4 && tolower(word[1]) == 'c' && tolower(word[2]) == 'h' && tolower(word[3]) == 'g') return 112; + if (len == 4 && tolower(word[1]) == 'c' && tolower(word[2]) == 'l' && tolower(word[3]) == 'r') return 109; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && word[3] == '$') return 144; + if (len == 4 && tolower(word[1]) == 's' && tolower(word[2]) == 'e' && tolower(word[3]) == 't') return 110; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 's' && tolower(word[3]) == 't') return 111; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 'o' && tolower(word[3]) == 'l' && word[4] == '@') return 272; + if (len == 5 && tolower(word[1]) == 'y' && tolower(word[2]) == 't' && tolower(word[3]) == 'e' && word[4] == '@') return 274; + if (len == 6 && tolower(word[1]) == 'a' && tolower(word[2]) == 's' && tolower(word[3]) == 'e' && word[4] == '6' && word[5] == '4') return 238; + if (len == 7 && tolower(word[1]) == 'a' && tolower(word[2]) == 's' && tolower(word[3]) == 'e' && word[4] == '6' && word[5] == '4' && word[6] == '$') return 239; + if (len == 8 && tolower(word[1]) == 'o' && tolower(word[2]) == 'o' && tolower(word[3]) == 'l' && tolower(word[4]) == 'e' && tolower(word[5]) == 'a' && tolower(word[6]) == 'n' && word[7] == '@') return 273; + return -1; +__43: +__63: + if (len == 3 && tolower(word[1]) == 'b' && tolower(word[2]) == 'r') return 77; + if (len == 3 && tolower(word[1]) == 'h' && tolower(word[2]) == 'r') return 28; + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 's') return 54; + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'i' && tolower(word[3]) == 'l') return 81; + if (len == 4 && tolower(word[1]) == 'h' && tolower(word[2]) == 'r' && word[3] == '$') return 27; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 't') return 134; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'm' && tolower(word[3]) == 'p') return 38; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'n' && tolower(word[3]) == 'v') return 39; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 's' && tolower(word[3]) == 'h') return 66; + if (len == 4 && tolower(word[1]) == 's' && tolower(word[2]) == 't' && tolower(word[3]) == 'r') return 140; + if (len == 5 && tolower(word[1]) == 'b' && tolower(word[2]) == 'o' && tolower(word[3]) == 'o' && tolower(word[4]) == 'l') return 130; + if (len == 5 && tolower(word[1]) == 'b' && tolower(word[2]) == 'y' && tolower(word[3]) == 't' && tolower(word[4]) == 'e') return 132; + if (len == 5 && tolower(word[1]) == 'd' && tolower(word[2]) == 'a' && tolower(word[3]) == 't' && tolower(word[4]) == 'e') return 139; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'o' && tolower(word[3]) == 'n' && tolower(word[4]) == 'g') return 136; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 'n' && tolower(word[3]) == 'v' && word[4] == '$') return 40; + if (len == 6 && tolower(word[1]) == 'f' && tolower(word[2]) == 'l' && tolower(word[3]) == 'o' && tolower(word[4]) == 'a' && tolower(word[5]) == 't') return 138; + if (len == 6 && tolower(word[1]) == 'h' && tolower(word[2]) == 'o' && tolower(word[3]) == 'o' && tolower(word[4]) == 's' && tolower(word[5]) == 'e') return 90; + if (len == 6 && tolower(word[1]) == 's' && tolower(word[2]) == 'h' && tolower(word[3]) == 'o' && tolower(word[4]) == 'r' && tolower(word[5]) == 't') return 133; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 'g' && tolower(word[5]) == 'l' && tolower(word[6]) == 'e') return 137; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 't' && tolower(word[3]) == 'r' && tolower(word[4]) == 'i' && tolower(word[5]) == 'n' && tolower(word[6]) == 'g') return 141; + if (len == 8 && tolower(word[1]) == 'b' && tolower(word[2]) == 'o' && tolower(word[3]) == 'o' && tolower(word[4]) == 'l' && tolower(word[5]) == 'e' && tolower(word[6]) == 'a' && tolower(word[7]) == 'n') return 131; + if (len == 8 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 't' && tolower(word[4]) == 'e' && tolower(word[5]) == 'g' && tolower(word[6]) == 'e' && tolower(word[7]) == 'r') return 135; + if (len == 8 && tolower(word[1]) == 'p' && tolower(word[2]) == 'o' && tolower(word[3]) == 'i' && tolower(word[4]) == 'n' && tolower(word[5]) == 't' && tolower(word[6]) == 'e' && tolower(word[7]) == 'r') return 142; + if (len == 8 && tolower(word[1]) == 'v' && tolower(word[2]) == 'a' && tolower(word[3]) == 'r' && tolower(word[4]) == 'i' && tolower(word[5]) == 'a' && tolower(word[6]) == 'n' && tolower(word[7]) == 't') return 143; + return -1; +__44: +__64: + if (len == 3 && tolower(word[1]) == 'a' && tolower(word[2]) == 'y') return 157; + if (len == 3 && tolower(word[1]) == 'e' && tolower(word[2]) == 'g') return 62; + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'r') return 215; + if (len == 4 && tolower(word[1]) == 'a' && tolower(word[2]) == 't' && tolower(word[3]) == 'e') return 163; + if (len == 5 && tolower(word[1]) == 'a' && tolower(word[2]) == 't' && tolower(word[3]) == 'e' && word[4] == '@') return 281; + if (len == 5 && tolower(word[1]) == 'c' && tolower(word[2]) == 'o' && tolower(word[3]) == 'n' && tolower(word[4]) == 'v') return 43; + if (len == 5 && tolower(word[1]) == 'f' && tolower(word[2]) == 'r' && tolower(word[3]) == 'e' && tolower(word[4]) == 'e') return 211; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'o' && tolower(word[3]) == 'n' && tolower(word[4]) == 'v' && word[5] == '$') return 44; + if (len == 7 && tolower(word[1]) == 'a' && tolower(word[2]) == 't' && tolower(word[3]) == 'e' && tolower(word[4]) == 'a' && tolower(word[5]) == 'd' && tolower(word[6]) == 'd') return 165; + if (len == 8 && tolower(word[1]) == 'a' && tolower(word[2]) == 't' && tolower(word[3]) == 'e' && tolower(word[4]) == 'd' && tolower(word[5]) == 'i' && tolower(word[6]) == 'f' && tolower(word[7]) == 'f') return 166; + return -1; +__45: +__65: + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'f') return 188; + if (len == 3 && tolower(word[1]) == 'x' && tolower(word[2]) == 'p') return 51; + if (len == 4 && tolower(word[1]) == 'v' && tolower(word[2]) == 'a' && tolower(word[3]) == 'l') return 167; + if (len == 4 && tolower(word[1]) == 'v' && tolower(word[2]) == 'e' && tolower(word[3]) == 'n') return 194; + if (len == 4 && tolower(word[1]) == 'x' && tolower(word[2]) == 'p' && word[3] == '2') return 74; + if (len == 4 && tolower(word[1]) == 'x' && tolower(word[2]) == 'p' && tolower(word[3]) == 'm') return 78; + if (len == 5 && tolower(word[1]) == 'x' && tolower(word[2]) == 'i' && tolower(word[3]) == 's' && tolower(word[4]) == 't') return 208; + if (len == 5 && tolower(word[1]) == 'x' && tolower(word[2]) == 'p' && word[3] == '1' && word[4] == '0') return 75; + return -1; +__46: +__66: + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'x') return 47; + if (len == 4 && tolower(word[1]) == 'r' && tolower(word[2]) == 'a' && tolower(word[3]) == 'c') return 49; + if (len == 4 && tolower(word[1]) == 'r' && tolower(word[2]) == 'e' && tolower(word[3]) == 'e') return 220; + if (len == 5 && tolower(word[1]) == 'l' && tolower(word[2]) == 'o' && tolower(word[3]) == 'o' && tolower(word[4]) == 'r') return 80; + if (len == 6 && tolower(word[1]) == 'l' && tolower(word[2]) == 'o' && tolower(word[3]) == 'a' && tolower(word[4]) == 't' && word[5] == '@') return 280; + if (len == 6 && tolower(word[1]) == 'o' && tolower(word[2]) == 'r' && tolower(word[3]) == 'm' && tolower(word[4]) == 'a' && tolower(word[5]) == 't') return 152; + if (len == 7 && tolower(word[1]) == 'o' && tolower(word[2]) == 'r' && tolower(word[3]) == 'm' && tolower(word[4]) == 'a' && tolower(word[5]) == 't' && word[6] == '$') return 151; + if (len == 7 && tolower(word[1]) == 'r' && tolower(word[2]) == 'o' && tolower(word[3]) == 'm' && tolower(word[4]) == 'u' && tolower(word[5]) == 'r' && tolower(word[6]) == 'l') return 248; + if (len == 8 && tolower(word[1]) == 'r' && tolower(word[2]) == 'o' && tolower(word[3]) == 'm' && tolower(word[4]) == 'u' && tolower(word[5]) == 'r' && tolower(word[6]) == 'l' && word[7] == '$') return 249; + if (len == 10 && tolower(word[1]) == 'r' && tolower(word[2]) == 'o' && tolower(word[3]) == 'm' && tolower(word[4]) == 'b' && tolower(word[5]) == 'a' && tolower(word[6]) == 's' && tolower(word[7]) == 'e' && word[8] == '6' && word[9] == '4') return 246; + if (len == 11 && tolower(word[1]) == 'r' && tolower(word[2]) == 'o' && tolower(word[3]) == 'm' && tolower(word[4]) == 'b' && tolower(word[5]) == 'a' && tolower(word[6]) == 's' && tolower(word[7]) == 'e' && word[8] == '6' && word[9] == '4' && word[10] == '$') return 247; + return -1; +__47: +__67: + return -1; +__48: +__68: + if (len == 3 && tolower(word[1]) == 'e' && tolower(word[2]) == 'x') return 147; + if (len == 3 && tolower(word[1]) == 'y' && tolower(word[2]) == 'p') return 95; + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'x' && word[3] == '$') return 146; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'u' && tolower(word[3]) == 'r') return 158; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 'm' && tolower(word[3]) == 'l') return 236; + if (len == 5 && tolower(word[1]) == 't' && tolower(word[2]) == 'm' && tolower(word[3]) == 'l' && word[4] == '$') return 237; + return -1; +__49: +__69: + if (len == 2 && tolower(word[1]) == 'f') return 88; + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'f') return 89; + if (len == 3 && tolower(word[1]) == 'n' && tolower(word[2]) == 't') return 46; + if (len == 4 && tolower(word[1]) == 'n' && tolower(word[2]) == 't' && word[3] == '@') return 276; + if (len == 5 && tolower(word[1]) == 'n' && tolower(word[2]) == 's' && tolower(word[3]) == 't' && tolower(word[4]) == 'r') return 30; + if (len == 5 && tolower(word[1]) == 's' && tolower(word[2]) == 'd' && tolower(word[3]) == 'i' && tolower(word[4]) == 'r') return 214; + if (len == 5 && tolower(word[1]) == 's' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 'f') return 207; + if (len == 5 && tolower(word[1]) == 's' && tolower(word[2]) == 'n' && tolower(word[3]) == 'a' && tolower(word[4]) == 'n') return 206; + if (len == 6 && tolower(word[1]) == 's' && tolower(word[2]) == 'd' && tolower(word[3]) == 'a' && tolower(word[4]) == 't' && tolower(word[5]) == 'e') return 125; + if (len == 6 && tolower(word[1]) == 's' && tolower(word[2]) == 'h' && tolower(word[3]) == 'e' && tolower(word[4]) == 'x' && tolower(word[5]) == 'a') return 104; + if (len == 6 && tolower(word[1]) == 's' && tolower(word[2]) == 'l' && tolower(word[3]) == 'o' && tolower(word[4]) == 'n' && tolower(word[5]) == 'g') return 123; + if (len == 6 && tolower(word[1]) == 's' && tolower(word[2]) == 'n' && tolower(word[3]) == 'u' && tolower(word[4]) == 'l' && tolower(word[5]) == 'l') return 127; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'a' && tolower(word[3]) == 'l' && tolower(word[4]) == 'n' && tolower(word[5]) == 'u' && tolower(word[6]) == 'm') return 108; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'a' && tolower(word[3]) == 's' && tolower(word[4]) == 'c' && tolower(word[5]) == 'i' && tolower(word[6]) == 'i') return 97; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'b' && tolower(word[3]) == 'l' && tolower(word[4]) == 'a' && tolower(word[5]) == 'n' && tolower(word[6]) == 'k') return 106; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'd' && tolower(word[3]) == 'i' && tolower(word[4]) == 'g' && tolower(word[5]) == 'i' && tolower(word[6]) == 't') return 103; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'f' && tolower(word[3]) == 'l' && tolower(word[4]) == 'o' && tolower(word[5]) == 'a' && tolower(word[6]) == 't') return 124; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'l' && tolower(word[3]) == 'c' && tolower(word[4]) == 'a' && tolower(word[5]) == 's' && tolower(word[6]) == 'e') return 99; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'l' && tolower(word[3]) == 'o' && tolower(word[4]) == 'w' && tolower(word[5]) == 'e' && tolower(word[6]) == 'r') return 100; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'p' && tolower(word[3]) == 'u' && tolower(word[4]) == 'n' && tolower(word[5]) == 'c' && tolower(word[6]) == 't') return 107; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 's' && tolower(word[3]) == 'p' && tolower(word[4]) == 'a' && tolower(word[5]) == 'c' && tolower(word[6]) == 'e') return 105; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'u' && tolower(word[3]) == 'c' && tolower(word[4]) == 'a' && tolower(word[5]) == 's' && tolower(word[6]) == 'e') return 101; + if (len == 7 && tolower(word[1]) == 's' && tolower(word[2]) == 'u' && tolower(word[3]) == 'p' && tolower(word[4]) == 'p' && tolower(word[5]) == 'e' && tolower(word[6]) == 'r') return 102; + if (len == 8 && tolower(word[1]) == 'n' && tolower(word[2]) == 't' && tolower(word[3]) == 'e' && tolower(word[4]) == 'g' && tolower(word[5]) == 'e' && tolower(word[6]) == 'r' && word[7] == '@') return 277; + if (len == 8 && tolower(word[1]) == 's' && tolower(word[2]) == 'l' && tolower(word[3]) == 'e' && tolower(word[4]) == 't' && tolower(word[5]) == 't' && tolower(word[6]) == 'e' && tolower(word[7]) == 'r') return 98; + if (len == 8 && tolower(word[1]) == 's' && tolower(word[2]) == 'n' && tolower(word[3]) == 'u' && tolower(word[4]) == 'm' && tolower(word[5]) == 'b' && tolower(word[6]) == 'e' && tolower(word[7]) == 'r') return 126; + if (len == 9 && tolower(word[1]) == 's' && tolower(word[2]) == 'b' && tolower(word[3]) == 'o' && tolower(word[4]) == 'o' && tolower(word[5]) == 'l' && tolower(word[6]) == 'e' && tolower(word[7]) == 'a' && tolower(word[8]) == 'n') return 121; + if (len == 9 && tolower(word[1]) == 's' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 't' && tolower(word[5]) == 'e' && tolower(word[6]) == 'g' && tolower(word[7]) == 'e' && tolower(word[8]) == 'r') return 122; + if (len == 9 && tolower(word[1]) == 's' && tolower(word[2]) == 'm' && tolower(word[3]) == 'i' && tolower(word[4]) == 's' && tolower(word[5]) == 's' && tolower(word[6]) == 'i' && tolower(word[7]) == 'n' && tolower(word[8]) == 'g') return 228; + return -1; +__4A: +__6A: + return -1; +__4B: +__6B: + return -1; +__4C: +__6C: + if (len == 3 && tolower(word[1]) == 'e' && tolower(word[2]) == 'n') return 6; + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'f') return 189; + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'g') return 50; + if (len == 3 && tolower(word[1]) == 's' && tolower(word[2]) == 'l') return 119; + if (len == 3 && tolower(word[1]) == 's' && tolower(word[2]) == 'r') return 120; + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'f' && tolower(word[3]) == 't') return 1; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'g' && word[3] == '2') return 76; + if (len == 4 && tolower(word[1]) == 'o' && tolower(word[2]) == 'g' && tolower(word[3]) == 'p') return 79; + if (len == 5 && tolower(word[1]) == 'c' && tolower(word[2]) == 'a' && tolower(word[3]) == 's' && tolower(word[4]) == 'e') return 24; + if (len == 5 && tolower(word[1]) == 'e' && tolower(word[2]) == 'f' && tolower(word[3]) == 't' && word[4] == '$') return 0; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 'g' && word[3] == '1' && word[4] == '0') return 64; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 'n' && tolower(word[3]) == 'g' && word[4] == '@') return 278; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 'w' && tolower(word[3]) == 'e' && tolower(word[4]) == 'r') return 22; + if (len == 5 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'm') return 14; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'a' && tolower(word[3]) == 's' && tolower(word[4]) == 'e' && word[5] == '$') return 23; + if (len == 6 && tolower(word[1]) == 'o' && tolower(word[2]) == 'w' && tolower(word[3]) == 'e' && tolower(word[4]) == 'r' && word[5] == '$') return 21; + if (len == 6 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'm' && word[5] == '$') return 13; + return -1; +__4D: +__6D: + if (len == 3 && tolower(word[1]) == 'a' && tolower(word[2]) == 'g') return 96; + if (len == 3 && tolower(word[1]) == 'a' && tolower(word[2]) == 'x') return 87; + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'd') return 3; + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n') return 86; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'd' && word[3] == '$') return 2; + if (len == 5 && tolower(word[1]) == 'k' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 't') return 258; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 'n' && tolower(word[3]) == 't' && tolower(word[4]) == 'h') return 156; + if (len == 6 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'u' && tolower(word[4]) == 't' && tolower(word[5]) == 'e') return 159; + if (len == 6 && tolower(word[1]) == 'k' && tolower(word[2]) == 'b' && tolower(word[3]) == 'o' && tolower(word[4]) == 'o' && tolower(word[5]) == 'l') return 250; + if (len == 6 && tolower(word[1]) == 'k' && tolower(word[2]) == 'b' && tolower(word[3]) == 'y' && tolower(word[4]) == 't' && tolower(word[5]) == 'e') return 254; + if (len == 6 && tolower(word[1]) == 'k' && tolower(word[2]) == 'd' && tolower(word[3]) == 'a' && tolower(word[4]) == 't' && tolower(word[5]) == 'e') return 268; + if (len == 6 && tolower(word[1]) == 'k' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 't' && word[5] == '$') return 259; + if (len == 6 && tolower(word[1]) == 'k' && tolower(word[2]) == 'l' && tolower(word[3]) == 'o' && tolower(word[4]) == 'n' && tolower(word[5]) == 'g') return 262; + if (len == 7 && tolower(word[1]) == 'k' && tolower(word[2]) == 'b' && tolower(word[3]) == 'o' && tolower(word[4]) == 'o' && tolower(word[5]) == 'l' && word[6] == '$') return 251; + if (len == 7 && tolower(word[1]) == 'k' && tolower(word[2]) == 'b' && tolower(word[3]) == 'y' && tolower(word[4]) == 't' && tolower(word[5]) == 'e' && word[6] == '$') return 255; + if (len == 7 && tolower(word[1]) == 'k' && tolower(word[2]) == 'd' && tolower(word[3]) == 'a' && tolower(word[4]) == 't' && tolower(word[5]) == 'e' && word[6] == '$') return 269; + if (len == 7 && tolower(word[1]) == 'k' && tolower(word[2]) == 'f' && tolower(word[3]) == 'l' && tolower(word[4]) == 'o' && tolower(word[5]) == 'a' && tolower(word[6]) == 't') return 266; + if (len == 7 && tolower(word[1]) == 'k' && tolower(word[2]) == 'l' && tolower(word[3]) == 'o' && tolower(word[4]) == 'n' && tolower(word[5]) == 'g' && word[6] == '$') return 263; + if (len == 7 && tolower(word[1]) == 'k' && tolower(word[2]) == 's' && tolower(word[3]) == 'h' && tolower(word[4]) == 'o' && tolower(word[5]) == 'r' && tolower(word[6]) == 't') return 256; + if (len == 8 && tolower(word[1]) == 'k' && tolower(word[2]) == 'f' && tolower(word[3]) == 'l' && tolower(word[4]) == 'o' && tolower(word[5]) == 'a' && tolower(word[6]) == 't' && word[7] == '$') return 267; + if (len == 8 && tolower(word[1]) == 'k' && tolower(word[2]) == 's' && tolower(word[3]) == 'h' && tolower(word[4]) == 'o' && tolower(word[5]) == 'r' && tolower(word[6]) == 't' && word[7] == '$') return 257; + if (len == 8 && tolower(word[1]) == 'k' && tolower(word[2]) == 's' && tolower(word[3]) == 'i' && tolower(word[4]) == 'n' && tolower(word[5]) == 'g' && tolower(word[6]) == 'l' && tolower(word[7]) == 'e') return 264; + if (len == 9 && tolower(word[1]) == 'k' && tolower(word[2]) == 'b' && tolower(word[3]) == 'o' && tolower(word[4]) == 'o' && tolower(word[5]) == 'l' && tolower(word[6]) == 'e' && tolower(word[7]) == 'a' && tolower(word[8]) == 'n') return 252; + if (len == 9 && tolower(word[1]) == 'k' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 't' && tolower(word[5]) == 'e' && tolower(word[6]) == 'g' && tolower(word[7]) == 'e' && tolower(word[8]) == 'r') return 260; + if (len == 9 && tolower(word[1]) == 'k' && tolower(word[2]) == 'p' && tolower(word[3]) == 'o' && tolower(word[4]) == 'i' && tolower(word[5]) == 'n' && tolower(word[6]) == 't' && tolower(word[7]) == 'e' && tolower(word[8]) == 'r') return 270; + if (len == 9 && tolower(word[1]) == 'k' && tolower(word[2]) == 's' && tolower(word[3]) == 'i' && tolower(word[4]) == 'n' && tolower(word[5]) == 'g' && tolower(word[6]) == 'l' && tolower(word[7]) == 'e' && word[8] == '$') return 265; + if (len == 10 && tolower(word[1]) == 'k' && tolower(word[2]) == 'b' && tolower(word[3]) == 'o' && tolower(word[4]) == 'o' && tolower(word[5]) == 'l' && tolower(word[6]) == 'e' && tolower(word[7]) == 'a' && tolower(word[8]) == 'n' && word[9] == '$') return 253; + if (len == 10 && tolower(word[1]) == 'k' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 't' && tolower(word[5]) == 'e' && tolower(word[6]) == 'g' && tolower(word[7]) == 'e' && tolower(word[8]) == 'r' && word[9] == '$') return 261; + if (len == 10 && tolower(word[1]) == 'k' && tolower(word[2]) == 'p' && tolower(word[3]) == 'o' && tolower(word[4]) == 'i' && tolower(word[5]) == 'n' && tolower(word[6]) == 't' && tolower(word[7]) == 'e' && tolower(word[8]) == 'r' && word[9] == '$') return 271; + return -1; +__4E: +__6E: + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'w') return 154; + return -1; +__4F: +__6F: + if (len == 3 && tolower(word[1]) == 'c' && tolower(word[2]) == 't') return 26; + if (len == 3 && tolower(word[1]) == 'd' && tolower(word[2]) == 'd') return 195; + if (len == 4 && tolower(word[1]) == 'c' && tolower(word[2]) == 't' && word[3] == '$') return 25; + return -1; +__50: +__70: + if (len == 2 && tolower(word[1]) == 'i') return 82; + if (len == 8 && tolower(word[1]) == 'o' && tolower(word[2]) == 'i' && tolower(word[3]) == 'n' && tolower(word[4]) == 't' && tolower(word[5]) == 'e' && tolower(word[6]) == 'r' && word[7] == '@') return 282; + return -1; +__51: +__71: + if (len == 5 && tolower(word[1]) == 'u' && tolower(word[2]) == 'o' && tolower(word[3]) == 't' && tolower(word[4]) == 'e') return 232; + if (len == 6 && tolower(word[1]) == 'u' && tolower(word[2]) == 'o' && tolower(word[3]) == 't' && tolower(word[4]) == 'e' && word[5] == '$') return 233; + return -1; +__52: +__72: + if (len == 3 && tolower(word[1]) == 'a' && tolower(word[2]) == 'd') return 63; + if (len == 3 && tolower(word[1]) == 'n' && tolower(word[2]) == 'd') return 85; + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'l') return 117; + if (len == 3 && tolower(word[1]) == 'o' && tolower(word[2]) == 'r') return 118; + if (len == 4 && tolower(word[1]) == 'a' && tolower(word[2]) == 'n' && tolower(word[3]) == 'd') return 196; + if (len == 4 && tolower(word[1]) == 'd' && tolower(word[2]) == 'i' && tolower(word[3]) == 'r') return 216; + if (len == 5 && tolower(word[1]) == 'i' && tolower(word[2]) == 'g' && tolower(word[3]) == 'h' && tolower(word[4]) == 't') return 5; + if (len == 5 && tolower(word[1]) == 'o' && tolower(word[2]) == 'u' && tolower(word[3]) == 'n' && tolower(word[4]) == 'd') return 83; + if (len == 5 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'm') return 16; + if (len == 6 && tolower(word[1]) == 'i' && tolower(word[2]) == 'g' && tolower(word[3]) == 'h' && tolower(word[4]) == 't' && word[5] == '$') return 4; + if (len == 6 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 's' && tolower(word[4]) == 't' && tolower(word[5]) == 'r') return 31; + if (len == 6 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'm' && word[5] == '$') return 15; + if (len == 7 && tolower(word[1]) == 'e' && tolower(word[2]) == 'a' && tolower(word[3]) == 'l' && tolower(word[4]) == 'l' && tolower(word[5]) == 'o' && tolower(word[6]) == 'c') return 221; + if (len == 7 && tolower(word[1]) == 'e' && tolower(word[2]) == 'p' && tolower(word[3]) == 'l' && tolower(word[4]) == 'a' && tolower(word[5]) == 'c' && tolower(word[6]) == 'e') return 35; + if (len == 8 && tolower(word[1]) == 'e' && tolower(word[2]) == 'p' && tolower(word[3]) == 'l' && tolower(word[4]) == 'a' && tolower(word[5]) == 'c' && tolower(word[6]) == 'e' && word[7] == '$') return 34; + return -1; +__53: +__73: + if (len == 3 && tolower(word[1]) == 'g' && tolower(word[2]) == 'n') return 48; + if (len == 3 && tolower(word[1]) == 'h' && tolower(word[2]) == 'l') return 113; + if (len == 3 && tolower(word[1]) == 'h' && tolower(word[2]) == 'r') return 115; + if (len == 3 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n') return 53; + if (len == 3 && tolower(word[1]) == 'q' && tolower(word[2]) == 'r') return 52; + if (len == 3 && tolower(word[1]) == 't' && tolower(word[2]) == 'r') return 150; + if (len == 4 && tolower(word[1]) == 'c' && tolower(word[2]) == 'a' && tolower(word[3]) == 'n') return 37; + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'e' && tolower(word[3]) == 'k') return 190; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'h') return 65; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 'a' && tolower(word[3]) == 't') return 210; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && word[3] == '$') return 149; + if (len == 4 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && word[3] == '@') return 222; + if (len == 4 && tolower(word[1]) == 'w' && tolower(word[2]) == 'a' && tolower(word[3]) == 'p') return 204; + if (len == 5 && tolower(word[1]) == 'c' && tolower(word[2]) == 'o' && tolower(word[3]) == 'n' && tolower(word[4]) == 'v') return 41; + if (len == 5 && tolower(word[1]) == 'h' && tolower(word[2]) == 'e' && tolower(word[3]) == 'l' && tolower(word[4]) == 'l') return 234; + if (len == 5 && tolower(word[1]) == 'p' && tolower(word[2]) == 'a' && tolower(word[3]) == 'c' && tolower(word[4]) == 'e') return 8; + if (len == 5 && tolower(word[1]) == 'p' && tolower(word[2]) == 'l' && tolower(word[3]) == 'i' && tolower(word[4]) == 't') return 36; + if (len == 5 && tolower(word[1]) == 'u' && tolower(word[2]) == 'b' && tolower(word[3]) == 's' && tolower(word[4]) == 't') return 33; + if (len == 5 && tolower(word[1]) == 'w' && tolower(word[2]) == 'a' && tolower(word[3]) == 'p' && word[4] == '$') return 205; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'o' && tolower(word[3]) == 'n' && tolower(word[4]) == 'v' && word[5] == '$') return 42; + if (len == 6 && tolower(word[1]) == 'e' && tolower(word[2]) == 'c' && tolower(word[3]) == 'o' && tolower(word[4]) == 'n' && tolower(word[5]) == 'd') return 160; + if (len == 6 && tolower(word[1]) == 'h' && tolower(word[2]) == 'e' && tolower(word[3]) == 'l' && tolower(word[4]) == 'l' && word[5] == '$') return 235; + if (len == 6 && tolower(word[1]) == 'h' && tolower(word[2]) == 'o' && tolower(word[3]) == 'r' && tolower(word[4]) == 't' && word[5] == '@') return 275; + if (len == 6 && tolower(word[1]) == 'i' && tolower(word[2]) == 'z' && tolower(word[3]) == 'e' && tolower(word[4]) == 'o' && tolower(word[5]) == 'f') return 129; + if (len == 6 && tolower(word[1]) == 'p' && tolower(word[2]) == 'a' && tolower(word[3]) == 'c' && tolower(word[4]) == 'e' && word[5] == '$') return 7; + if (len == 6 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'n' && tolower(word[5]) == 'g') return 10; + if (len == 6 && tolower(word[1]) == 'u' && tolower(word[2]) == 'b' && tolower(word[3]) == 's' && tolower(word[4]) == 't' && word[5] == '$') return 32; + if (len == 7 && tolower(word[1]) == 'i' && tolower(word[2]) == 'n' && tolower(word[3]) == 'g' && tolower(word[4]) == 'l' && tolower(word[5]) == 'e' && word[6] == '@') return 279; + if (len == 7 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'n' && tolower(word[5]) == 'g' && word[6] == '$') return 9; + if (len == 7 && tolower(word[1]) == 't' && tolower(word[2]) == 'r' && tolower(word[3]) == 'i' && tolower(word[4]) == 'n' && tolower(word[5]) == 'g' && word[6] == '@') return 223; + return -1; +__54: +__74: + if (len == 2 && tolower(word[1]) == 'r') return 230; + if (len == 3 && tolower(word[1]) == 'a' && tolower(word[2]) == 'n') return 55; + if (len == 3 && tolower(word[1]) == 'r' && word[2] == '$') return 231; + if (len == 4 && tolower(word[1]) == 'a' && tolower(word[2]) == 'n' && tolower(word[3]) == 'h') return 67; + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'm' && tolower(word[3]) == 'p') return 212; + if (len == 4 && tolower(word[1]) == 'i' && tolower(word[2]) == 'm' && tolower(word[3]) == 'e') return 164; + if (len == 4 && tolower(word[1]) == 'r' && tolower(word[2]) == 'i' && tolower(word[3]) == 'm') return 12; + if (len == 5 && tolower(word[1]) == 'e' && tolower(word[2]) == 'm' && tolower(word[3]) == 'p' && word[4] == '$') return 213; + if (len == 5 && tolower(word[1]) == 'i' && tolower(word[2]) == 'm' && tolower(word[3]) == 'e' && tolower(word[4]) == 'r') return 153; + if (len == 5 && tolower(word[1]) == 'r' && tolower(word[2]) == 'i' && tolower(word[3]) == 'm' && word[4] == '$') return 11; + if (len == 6 && tolower(word[1]) == 'y' && tolower(word[2]) == 'p' && tolower(word[3]) == 'e' && tolower(word[4]) == 'o' && tolower(word[5]) == 'f') return 128; + return -1; +__55: +__75: + if (len == 3 && tolower(word[1]) == 'r' && tolower(word[2]) == 'l') return 240; + if (len == 4 && tolower(word[1]) == 'r' && tolower(word[2]) == 'l' && word[3] == '$') return 241; + if (len == 5 && tolower(word[1]) == 'c' && tolower(word[2]) == 'a' && tolower(word[3]) == 's' && tolower(word[4]) == 'e') return 20; + if (len == 5 && tolower(word[1]) == 'p' && tolower(word[2]) == 'p' && tolower(word[3]) == 'e' && tolower(word[4]) == 'r') return 18; + if (len == 6 && tolower(word[1]) == 'c' && tolower(word[2]) == 'a' && tolower(word[3]) == 's' && tolower(word[4]) == 'e' && word[5] == '$') return 19; + if (len == 6 && tolower(word[1]) == 'p' && tolower(word[2]) == 'p' && tolower(word[3]) == 'e' && tolower(word[4]) == 'r' && word[5] == '$') return 17; + if (len == 7 && tolower(word[1]) == 'n' && tolower(word[2]) == 'q' && tolower(word[3]) == 'u' && tolower(word[4]) == 'o' && tolower(word[5]) == 't' && tolower(word[6]) == 'e') return 242; + if (len == 8 && tolower(word[1]) == 'n' && tolower(word[2]) == 'b' && tolower(word[3]) == 'a' && tolower(word[4]) == 's' && tolower(word[5]) == 'e' && word[6] == '6' && word[7] == '4') return 244; + if (len == 8 && tolower(word[1]) == 'n' && tolower(word[2]) == 'q' && tolower(word[3]) == 'u' && tolower(word[4]) == 'o' && tolower(word[5]) == 't' && tolower(word[6]) == 'e' && word[7] == '$') return 243; + if (len == 9 && tolower(word[1]) == 'n' && tolower(word[2]) == 'b' && tolower(word[3]) == 'a' && tolower(word[4]) == 's' && tolower(word[5]) == 'e' && word[6] == '6' && word[7] == '4' && word[8] == '$') return 245; + return -1; +__56: +__76: + if (len == 3 && tolower(word[1]) == 'a' && tolower(word[2]) == 'l') return 148; + if (len == 6 && tolower(word[1]) == 'a' && tolower(word[2]) == 'r' && tolower(word[3]) == 'p' && tolower(word[4]) == 't' && tolower(word[5]) == 'r') return 227; + return -1; +__57: +__77: + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'e' && tolower(word[3]) == 'k') return 162; + if (len == 7 && tolower(word[1]) == 'e' && tolower(word[2]) == 'e' && tolower(word[3]) == 'k' && tolower(word[4]) == 'd' && tolower(word[5]) == 'a' && tolower(word[6]) == 'y') return 161; + return -1; +__58: +__78: + return -1; +__59: +__79: + if (len == 4 && tolower(word[1]) == 'e' && tolower(word[2]) == 'a' && tolower(word[3]) == 'r') return 155; + return -1; +__5A: +__7A: + return -1; +__5B: + return -1; +__5C: + return -1; +__5D: + return -1; +__5E: + return -1; +__5F: + return -1; +__60: + return -1; +__7B: + return -1; +__7C: + return -1; +__7D: + return -1; +__7E: + return -1; +} diff --git a/main/share/gb_table.h b/main/share/gb_table.h new file mode 100644 index 00000000..15871be1 --- /dev/null +++ b/main/share/gb_table.h @@ -0,0 +1,93 @@ +/*************************************************************************** + + gb_table.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_TABLE_H +#define __GB_TABLE_H + +#include "gb_array.h" + +#define NO_SYMBOL (-1) +//#define TABLE_USE_KEY 1 + +typedef + struct { + char *name; + int len; +#if TABLE_USE_KEY + uint key; +#endif + } + PACKED + SYMBOL; + +typedef + enum { + TF_NORMAL = 0, + TF_IGNORE_CASE = 1 + } + TABLE_FLAG; + + +typedef + struct _table { + SYMBOL *symbol; + ushort *sort; + TABLE_FLAG flag; + } + TABLE; + +void TABLE_create_static(TABLE *table, size_t size, TABLE_FLAG flag); +void TABLE_delete_static(TABLE *table); + +void TABLE_create(TABLE **result, size_t size, TABLE_FLAG flag); +void TABLE_create_from(TABLE **result, size_t size, const char *sym_list[], TABLE_FLAG flag); +void TABLE_delete(TABLE **table); + +char TABLE_compare_ignore_case(const char *s1, int len1, const char *s2, int len2); +char TABLE_compare(const char *s1, int len1, const char *s2, int len2); +char TABLE_compare_ignore_case_len(const char *s1, int len1, const char *s2, int len2); + +#define TABLE_count(_table) (ARRAY_count((_table)->symbol)) +const char *TABLE_get_symbol_name(TABLE *table, int index); +const char *TABLE_get_symbol_name_suffix(TABLE *table, int index, const char* suffix); +const char *SYMBOL_get_name(SYMBOL *sym); + +bool TABLE_find_symbol(TABLE *table, const char *name, int len, int *index); +bool TABLE_add_symbol(TABLE *table, const char *name, int len, int *index); +void TABLE_print(TABLE *table, bool sort); + +#define TABLE_get_symbol(table, ind) ((SYMBOL *)ARRAY_get((table)->symbol, ind)) + +SYMBOL *TABLE_get_symbol_sort(TABLE *table, int index); +void TABLE_copy_symbol_with_prefix(TABLE *table, int ind_src, char prefix, int *index); + +int SYMBOL_find(void *symbol, ushort *sort, int n_symbol, size_t s_symbol, int flag, const char *name, int len, const char *prefix); +#if TABLE_USE_KEY +void SYMBOL_compute_keys(void *symbol, int n_symbol, size_t s_symbol, int flag); +#else +#define SYMBOL_compute_keys(_symbol, _nsymbol, _ssymbol, _flag) +#endif +#endif + +#define SYMBOL_compare(_sym, _name) TABLE_compare((_sym)->name, (_sym)->len, (_name), strlen(_name)) +#define SYMBOL_compare_ignore_case(_sym, _name) TABLE_compare_ignore_case((_sym)->name, (_sym)->len, (_name), strlen(_name)) diff --git a/main/share/gb_table_temp.h b/main/share/gb_table_temp.h new file mode 100644 index 00000000..5ac3710c --- /dev/null +++ b/main/share/gb_table_temp.h @@ -0,0 +1,603 @@ +/*************************************************************************** + + gb_table_temp.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#define __TABLE_C + +#include +#include +#include +#include + +#include "gb_common.h" +#include "gb_error.h" +#include "gb_alloc.h" +#include "gb_limit.h" + +#include "gb_table.h" + +#define SYM(table, ind) (TABLE_get_symbol(table, ind)) +#define SSYM(_symbol, _pos, _size) ((SYMBOL *)((char *)(_symbol) + (_pos) * (_size))) + +static char _buffer[MAX_SYMBOL_LEN + 1]; + +#if TABLE_USE_KEY +static inline uint make_key(const char *sym, int len) +{ + static void *jump[] = { &&__LEN_0, &&__LEN_1, &&__LEN_2, &&__LEN_3 }; + const uchar *p = (uchar *)sym; + + if (len >= 4) + return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]; + + goto *jump[len]; + + __LEN_0: return 0; + __LEN_1: return (p[0] << 24); + __LEN_2: return (p[0] << 24) + (p[1] << 16); + __LEN_3: return (p[0] << 24) + (p[1] << 16) + (p[2] << 8); +} + +static inline uint make_key_ignore_case(const char *sym, int len) +{ + static void *jump[] = { &&__LEN_0, &&__LEN_1, &&__LEN_2, &&__LEN_3 }; + const uchar *p = (uchar *)sym; + + if (len >= 4) + return (tolower(p[0]) << 24) + (tolower(p[1]) << 16) + (tolower(p[2]) << 8) + tolower(p[3]); + + goto *jump[len]; + + __LEN_0: return 0; + __LEN_1: return (tolower(p[0]) << 24); + __LEN_2: return (tolower(p[0]) << 24) + (tolower(p[1]) << 16); + __LEN_3: return (tolower(p[0]) << 24) + (tolower(p[1]) << 16) + (tolower(p[2]) << 8); +} +#endif + +char TABLE_compare(const char *s1, int len1, const char *s2, int len2) +{ + int i; + int len = (len1 < len2) ? len1 : len2; + register unsigned char c1; + register unsigned char c2; + + for (i = 0; i < len; i++) + { + c1 = s1[i]; + c2 = s2[i]; + + if (LIKELY(c1 > c2)) return 1; + if (LIKELY(c1 < c2)) return -1; + } + + if (LIKELY(len1 < len2)) + return -1; + else if (LIKELY(len1 > len2)) + return 1; + else + return 0; +} + +char TABLE_compare_ignore_case(const char *s1, int len1, const char *s2, int len2) +{ + unsigned int len = (len1 < len2) ? len1 : len2; + unsigned int i; + int result; + + for (i = 0; len > 0; i++) + { + result = toupper(s1[i]) - toupper(s2[i]); + if (LIKELY(result)) + return result; // < 0 ? -1 : 1; + len--; + } + + if (LIKELY(len1 < len2)) + return -1; + else if (LIKELY(len1 > len2)) + return 1; + else + return 0; +} + +char TABLE_compare_ignore_case_len(const char *s1, int len1, const char *s2, int len2) +{ + int result; + + if (LIKELY(len1 < len2)) + return -1; + else if (LIKELY(len1 > len2)) + return 1; + + while (len1) + { + result = tolower(*s1++) - tolower(*s2++); + if (LIKELY(result)) + return result; // < 0 ? -1 : 1; + --len1; + } + + return 0; +} + +static inline bool search(void *symbol, ushort *sort, int n_symbol, size_t size, const char *name, int len, int *index) +{ + int pos, deb, fin; + SYMBOL *sym; + int l; + int result; // must be an integer (or a short) because uchar - uchar may not fit in a char! + const uchar *s1; + const uchar *s2; +#if TABLE_USE_KEY + uint key = make_key(name, len); +#endif + + pos = 0; + deb = 0; + fin = n_symbol; //ARRAY_count(table->symbol); + + for(;;) + { + if (UNLIKELY(deb >= fin)) + { + *index = deb; + return FALSE; + } + + pos = (deb + fin) >> 1; + + sym = SSYM(symbol, sort[pos], size); + + if (LIKELY(len < sym->len)) + goto __B_LOWER; + else if (LIKELY(len > sym->len)) + goto __B_GREATER; + +#if TABLE_USE_KEY + if (key < sym->key) goto __B_LOWER; + if (key > sym->key) goto __B_GREATER; + + l = len - 4; + + if (l > 0) + { + s1 = (uchar *)name + 4; + s2 = (uchar *)sym->name + 4; + +#else + { + l = len; + s1 = (uchar *)name; + s2 = (uchar *)sym->name; +#endif + for(;;) + { + result = *s1 - *s2; + + if (LIKELY(result < 0)) + goto __B_LOWER; + else if (LIKELY(result > 0)) + goto __B_GREATER; + + if (UNLIKELY(--l == 0)) + break; + + s1++; + s2++; + } + } + + *index = pos; + return TRUE; + + __B_LOWER: fin = pos; continue; + __B_GREATER: deb = pos + 1; continue; + } +} + + +static inline bool search_ignore_case(void *symbol, ushort *sort, int n_symbol, size_t size, const char *name, int len, int *index) +{ + int pos, deb, fin; + SYMBOL *sym; + int l; + int result; // must be an integer (or a short) because uchar - uchar may not fit in a char! + const uchar *s1; + const uchar *s2; +#if TABLE_USE_KEY + uint key = make_key_ignore_case(name, len); +#endif + + pos = 0; + deb = 0; + fin = n_symbol; + + for(;;) + { + if (UNLIKELY(deb >= fin)) + { + *index = deb; + return FALSE; + } + + pos = (deb + fin) >> 1; + + sym = SSYM(symbol, sort[pos], size); + + if (LIKELY(len < sym->len)) + goto __T_LOWER; + else if (LIKELY(len > sym->len)) + goto __T_GREATER; + +#if TABLE_USE_KEY + if (key < sym->key) goto __T_LOWER; + if (key > sym->key) goto __T_GREATER; + + l = len - 4; + + if (l > 0) + { + s1 = (uchar *)name + 4; + s2 = (uchar *)sym->name + 4; +#else + { + l = len; + s1 = (uchar *)name; + s2 = (uchar *)sym->name; +#endif + + for(;;) + { + result = tolower(*s1) - tolower(*s2); + + if (LIKELY(result < 0)) + goto __T_LOWER; + else if (LIKELY(result > 0)) + goto __T_GREATER; + + if (UNLIKELY(--l == 0)) + break; + + s1++; + s2++; + } + } + + *index = pos; + return TRUE; + + __T_LOWER: fin = pos; continue; + __T_GREATER: deb = pos + 1; continue; + } +} + + +const char *TABLE_get_symbol_name(TABLE *table, int index) +{ + if (UNLIKELY((index < 0) || (index >= ARRAY_count(table->symbol)))) + strcpy(_buffer, "?"); + else + SYMBOL_get_name(SYM(table, index)); + + return _buffer; +} + + +const char *TABLE_get_symbol_name_suffix(TABLE *table, int index, const char* suffix) +{ + SYMBOL *sym; + + if (UNLIKELY((index < 0) || (index >= ARRAY_count(table->symbol)))) + return "?"; + + sym = SYM(table, index); + if ((sym->len + strlen(suffix)) > MAX_SYMBOL_LEN) + return "?"; + + SYMBOL_get_name(sym); + strcat(_buffer, suffix); + return _buffer; +} + + +void TABLE_create_static(TABLE *table, size_t size, TABLE_FLAG flag) +{ + ARRAY_create_with_size(&table->symbol, Max(size, sizeof(SYMBOL)), 64); + ARRAY_create_with_size(&table->sort, sizeof(ushort), 64); + table->flag = flag; +} + + +void TABLE_create(TABLE **result, size_t size, TABLE_FLAG flag) +{ + TABLE *table; + + ALLOC(&table, sizeof(TABLE)); + TABLE_create_static(table, size, flag); + + *result = table; +} + + +void TABLE_create_from(TABLE **result, size_t size, const char *sym_list[], TABLE_FLAG flag) +{ + TABLE *table; + int index; + + TABLE_create(&table, size, flag); + + while (*sym_list) + { + TABLE_add_symbol(table, *sym_list, strlen(*sym_list), &index); + sym_list++; + } + + *result = table; +} + + +void TABLE_delete_static(TABLE *table) +{ + ARRAY_delete(&table->symbol); + ARRAY_delete(&table->sort); +} + + +void TABLE_delete(TABLE **p_table) +{ + if (*p_table) + { + TABLE_delete_static(*p_table); + FREE(p_table); + } +} + + +bool TABLE_find_symbol(TABLE *table, const char *name, int len, int *index) +{ + int ind; + bool result; + SYMBOL *tsym; + int count; + size_t size; + + tsym = table->symbol; + count = ARRAY_count(tsym); + size = ARRAY_size(tsym); + + if (table->flag) + result = search_ignore_case(tsym, table->sort, count, size, name, len, &ind); + else + result = search(tsym, table->sort, count, size, name, len, &ind); + + if (result) + *index = table->sort[ind]; + + return result; +} + +#if 0 +void TABLE_add_new_symbol_without_sort(TABLE *table, const char *name, int len, int sort, SYMBOL **symbol, int *index) +{ + SYMBOL *sym; + int count; + + len = Min(len, MAX_SYMBOL_LEN); + + count = ARRAY_count(table->symbol); + + sym = (SYMBOL *)ARRAY_add_void_size(&table->symbol); + + sym->name = (char *)name; + sym->len = len; + + *((ushort *)ARRAY_add(&table->sort)) = sort; + + if (symbol) *symbol = sym; /*&table->symbol[ind];*/ + if (index) *index = count; +} +#endif + +bool TABLE_add_symbol(TABLE *table, const char *name, int len, int *index) +{ + int ind; + bool result; + SYMBOL *sym; + int count; + size_t size; + + /*len = Min(len, MAX_SYMBOL_LEN);*/ + //len = Min(len, 65535); + + count = ARRAY_count(table->symbol); + size = ARRAY_size(table->symbol); + + if (table->flag) + result = search_ignore_case(table->symbol, table->sort, count, size, name, len, &ind); + else + result = search(table->symbol, table->sort, count, size, name, len, &ind); + + if (!result) + { + sym = (SYMBOL *)ARRAY_add_void_size(&table->symbol); + + sym->name = (char *)name; + sym->len = len; +#if TABLE_USE_KEY + sym->key = table->flag ? make_key_ignore_case(name, len) : make_key(name, len); +#endif + + /* + printf("TABLE_add_symbol: %.*s %d %d\n", len, name, ((CLASS_SYMBOL *)sym)->global.type, + ((CLASS_SYMBOL *)sym)->local.type); + */ + + //s1 = (SYMBOL *)((char *)table->symbol + count * size); + ARRAY_add(&table->sort); + if (count > ind) + memmove(&table->sort[ind + 1], &table->sort[ind], sizeof(ushort) * (count - ind)); + + table->sort[ind] = (ushort)count; + ind = count; + } + else + ind = table->sort[ind]; //SYM(table, ind)->sort; /*table->symbol[ind].sort;*/ + + *index = ind; + + return result; +} + + + +void TABLE_print(TABLE *table, bool sort) +{ + int i; + SYMBOL *sym; + + fprintf(stderr, "capacity %i\n", ARRAY_count(table->symbol)); + + /* + for (i = 0; i < ARRAY_count(table->symbol); i++) + { + sym = SYM(table, i); + printf("%*s (%li) ", (int)sym->len, sym->name, sym->sort); + } + + printf("\n"); + */ + + for (i = 0; i < ARRAY_count(table->symbol); i++) + { + if (sort) + { + sym = SYM(table, table->sort[i]); + fprintf(stderr, "%.*s ", (int)sym->len, sym->name); + } + else + { + sym = SYM(table, i); + fprintf(stderr, "%d %.*s ", (int)table->sort[i], (int)sym->len, sym->name); + } + + //if ((i > 0) && (!(i & 0xF))) + // fprintf(stderr, "\n"); + } + + fprintf(stderr, "\n\n"); +} + + +void TABLE_copy_symbol_with_prefix(TABLE *table, int ind_src, char prefix, int *index) +{ + SYMBOL *sym; + char *ptr; + + sym = TABLE_get_symbol(table, ind_src); + + ptr = (char *)sym->name - 1; + + if (UNLIKELY(!isspace((unsigned char)*ptr))) + ERROR_panic("Cannot add prefix to symbol"); + + *ptr = prefix; + + TABLE_add_symbol(table, ptr, sym->len + 1, index); +} + + +SYMBOL *TABLE_get_symbol_sort(TABLE *table, int index) +{ + return TABLE_get_symbol(table, table->sort[index]); +} + +// Symbol tables with no TABLE structure + +int SYMBOL_find(void *symbol, ushort *sort, int n_symbol, size_t s_symbol, int flag, const char *name, int len, const char *prefix) +{ + int index; + int len_prefix; + + if (UNLIKELY(prefix != NULL)) + { + len_prefix = strlen(prefix); + + if (UNLIKELY((len + len_prefix) > MAX_SYMBOL_LEN)) + ERROR_panic("SYMBOL_find: prefixed symbol too long"); + + strcpy(_buffer, prefix); + strcpy(&_buffer[len_prefix], name); + len += len_prefix; + name = _buffer; + } + + if (flag) + { + if (search_ignore_case(symbol, sort, n_symbol, s_symbol, name, len, &index)) + return sort[index]; + } + else + { + if (search(symbol, sort, n_symbol, s_symbol, name, len, &index)) + return sort[index]; + } + + return NO_SYMBOL; +} + + +const char *SYMBOL_get_name(SYMBOL *sym) +{ + int len; + + len = Min(MAX_SYMBOL_LEN, sym->len); + memcpy(_buffer, sym->name, len); + _buffer[len] = 0; + + return _buffer; +} + +#if TABLE_USE_KEY +void SYMBOL_compute_keys(void *symbol, int n_symbol, size_t s_symbol, int flag) +{ + int i; + SYMBOL *sym; + + if (flag) + { + for (i = 0; i < n_symbol; i++) + { + sym = SSYM(symbol, i, s_symbol); + sym->key = make_key_ignore_case(sym->name, sym->len); + } + } + else + { + for (i = 0; i < n_symbol; i++) + { + sym = SSYM(symbol, i, s_symbol); + sym->key = make_key(sym->name, sym->len); + } + } +} +#endif diff --git a/main/share/gb_type_common.h b/main/share/gb_type_common.h new file mode 100644 index 00000000..7a1b5aed --- /dev/null +++ b/main/share/gb_type_common.h @@ -0,0 +1,92 @@ +/*************************************************************************** + + gb_type_common.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GB_TYPE_COMMON_H +#define __GB_TYPE_COMMON_H + +enum { + TK_UNKNOWN = 0, /* External symbol */ + TK_VARIABLE = 1, /* Variable */ + TK_FUNCTION = 2, /* Function */ + TK_PROPERTY = 3, /* Property */ + TK_LABEL = 4, /* GOTO label */ + TK_EVENT = 5, /* Event */ + TK_EXTERN = 6, /* Shared library function */ + TK_CONST = 7, /* Constant */ + + TF_OPTIONAL = 16, + + TF_STATIC = 32, + TF_PUBLIC = 64, + }; + +#ifdef PROJECT_COMP + +enum { + T_VOID = 0, + T_BOOLEAN = 1, + T_BYTE = 2, + T_SHORT = 3, + T_INTEGER = 4, + T_LONG = 5, + T_SINGLE = 6, + T_FLOAT = 7, + T_DATE = 8, + T_STRING = 9, + T_CSTRING = 10, + T_POINTER = 11, + T_VARIANT = 12, + T_ARRAY = 13, + T_STRUCT = 14, + //T_NULL = 15, + T_OBJECT = 16 + }; + +#else + +enum { + T_VOID = 0, + T_BOOLEAN = 1, + T_BYTE = 2, + T_SHORT = 3, + T_INTEGER = 4, + T_LONG = 5, + T_SINGLE = 6, + T_FLOAT = 7, + T_DATE = 8, + T_STRING = 9, + T_CSTRING = 10, + T_POINTER = 11, + T_VARIANT = 12, + T_FUNCTION = 13, + T_CLASS = 14, + T_NULL = 15, + T_OBJECT = 16, + + TC_ARRAY = 13, + TC_STRUCT = 14 + }; + +#endif + +#endif diff --git a/main/share/gbc_read_common.h b/main/share/gbc_read_common.h new file mode 100644 index 00000000..1abaa32d --- /dev/null +++ b/main/share/gbc_read_common.h @@ -0,0 +1,93 @@ +/*************************************************************************** + + gbc_read_common.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_READ_COMMON_H +#define __GBC_READ_COMMON_H + +typedef + uint PATTERN; + +enum { + RT_END = 0, + RT_NEWLINE = 1, + RT_RESERVED = 2, + RT_IDENTIFIER = 3, + RT_NUMBER = 4, + RT_STRING = 5, + RT_TSTRING = 6, + RT_PARAM = 7, + RT_SUBR = 8, + RT_CLASS = 9, + RT_COMMENT = 10, // Used by Eval() + RT_OPERATOR = 11, // Used by Eval() + RT_COMMAND = 12, // unused + RT_OUTPUT = 0x20, + RT_POINT = 0x40, + RT_FIRST = 0x80 + }; + +enum { + RC_SECTION = 1 + }; + +#define NULL_PATTERN ((PATTERN)0L) + +#define PATTERN_make(type, index) ((PATTERN)((type) | ((index) << 8))) + +#define VOID_STRING_INDEX 0xFFFFFF + +#define PATTERN_flag(pattern) ((pattern) & 0xF0) +#define PATTERN_type(pattern) ((pattern) & 0xF) +#define PATTERN_index(pattern) ((pattern) >> 8) + +#define PATTERN_is(pattern, res) (pattern == PATTERN_make(RT_RESERVED, res)) + +#define PATTERN_is_null(pattern) (pattern == NULL_PATTERN) + +#define PATTERN_is_end(pattern) (PATTERN_type(pattern) == RT_END) +#define PATTERN_is_reserved(pattern) (PATTERN_type(pattern) == RT_RESERVED) + +#define PATTERN_is_identifier(pattern) (PATTERN_type(pattern) == RT_IDENTIFIER) +#define PATTERN_is_class(pattern) (PATTERN_type(pattern) == RT_CLASS) +#define PATTERN_is_newline(pattern) (PATTERN_type(pattern) == RT_NEWLINE) +#define PATTERN_is_param(pattern) (PATTERN_type(pattern) == RT_PARAM) +#define PATTERN_is_subr(pattern) (PATTERN_type(pattern) == RT_SUBR) +#define PATTERN_is_number(pattern) (PATTERN_type(pattern) == RT_NUMBER) +#define PATTERN_is_string(pattern) (PATTERN_type(pattern) == RT_STRING) +#define PATTERN_is_tstring(pattern) (PATTERN_type(pattern) == RT_TSTRING) +#define PATTERN_is_command(pattern) (PATTERN_type(pattern) == RT_COMMAND) +#define PATTERN_is_comment(pattern) (PATTERN_type(pattern) == RT_COMMENT) + +#define PATTERN_is_newline_end(pattern) (PATTERN_is_newline(pattern) || PATTERN_is_end(pattern)) + +#define PATTERN_is_first(pattern) (((pattern) & RT_FIRST) != 0) +#define PATTERN_is_point(pattern) (((pattern) & RT_POINT) != 0) +#define PATTERN_is_output(pattern) (((pattern) & RT_OUTPUT) != 0) + +#define PATTERN_set_flag(pattern, flag) ((pattern) | flag) +#define PATTERN_unset_flag(pattern, flag) ((pattern) & ~flag) + +#define PATTERN_is_operand(pattern) (PATTERN_is_reserved(pattern) && RES_is_operand(PATTERN_index(pattern))) +#define PATTERN_is_type(pattern) (PATTERN_is_reserved(pattern) && RES_is_type(PATTERN_index(pattern))) + +#endif diff --git a/main/share/gbc_trans_common.h b/main/share/gbc_trans_common.h new file mode 100644 index 00000000..4ea690ad --- /dev/null +++ b/main/share/gbc_trans_common.h @@ -0,0 +1,177 @@ +/*************************************************************************** + + gbc_trans_common.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifndef __GBC_TRANS_COMMMON_H +#define __GBC_TRANS_COMMMON_H + +typedef + struct { + TYPE type; + int index; + PATTERN *optional; + short value; + unsigned ignore : 1; + } + TRANS_PARAM; + +typedef + PATTERN TRANS_TREE; + +typedef + struct { + int type; + int ival; + int64_t lval; + double dval; + bool complex; + } + TRANS_NUMBER; + +typedef + struct { + TYPE type; + int ndim; + int dim[MAX_ARRAY_DIM]; + } + TRANS_ARRAY; + +typedef + struct { + int index; // index in symbol table + TYPE type; // data type + int value; // value + TRANS_ARRAY array; // array dimensions + PATTERN *init; // initialization code + int64_t lvalue; // The value of a LONG constant + unsigned is_new : 1; // if something must be instanciated + unsigned is_integer : 1; // if the constant is an integer + unsigned is_embedded : 1; // if it is an embedded array + unsigned no_warning : 1; // The symbol name is between braces + } + PACKED + TRANS_DECL; + +typedef + struct { + int index; + TYPE type; + short nparam; + unsigned vararg : 1; + unsigned _reserved : 15; + TRANS_PARAM param[MAX_PARAM_FUNC]; + PATTERN *start; + int line; + uint64_t byref; + unsigned fast : 1; + unsigned unsafe : 1; + } + PACKED + TRANS_FUNC; + +typedef + struct { + int index; + TYPE type; + short nparam; + short _reserved; + TRANS_PARAM param[MAX_PARAM_FUNC]; + } + PACKED + TRANS_EVENT; + +typedef + struct { + int index; + TYPE type; + short nparam; + unsigned vararg : 1; + unsigned _reserved : 15; + TRANS_PARAM param[MAX_PARAM_FUNC]; + int library; + int alias; + } + PACKED + TRANS_EXTERN; + +typedef + struct { + int index; + TYPE type; + int line; + int comment; + int synonymous[3]; + bool read; + unsigned char nsynonymous; + bool _reserved[2]; + } + PACKED + TRANS_PROPERTY; + +typedef + struct { + int type; + int value; + int state; + short local; + short id; + short loop_var; + short _reserved[3]; + ushort *pos; + ushort *pos_break; + ushort *pos_continue; + } + PACKED + TRANS_CTRL; + +typedef + struct { + int index; + int line; + ushort pos; + short ctrl_id; + unsigned gosub : 1; + unsigned on_goto : 1; + unsigned _reserved : 30; + } + PACKED + TRANS_GOTO; + +typedef + struct { + int index; + ushort pos; + short ctrl_id; + } + PACKED + TRANS_LABEL; + +typedef + struct { + RESERVED_ID id; + void (*func)(); + bool no_try; + } + TRANS_STATEMENT; + +#endif + diff --git a/main/share/gbx_subr_common.h b/main/share/gbx_subr_common.h new file mode 100644 index 00000000..eedc2628 --- /dev/null +++ b/main/share/gbx_subr_common.h @@ -0,0 +1,680 @@ +/*************************************************************************** + + gbx_subr_common.h + + (c) 2000-2017 Benoît Minisini + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. + +***************************************************************************/ + +#ifdef STATIC_SUBR +#undef STATIC_SUBR +#define STATIC_SUBR static +#endif + +#define OP_OBJECT_FLOAT (T_POINTER + 1) +#define OP_FLOAT_OBJECT (T_POINTER + 2) +#define OP_OBJECT_CONV (T_POINTER + 3) +#define OP_CONV_OBJECT (T_POINTER + 4) +#define OP_OBJECT (T_POINTER + 5) + +static int check_operators(VALUE *P1, VALUE *P2) +{ + if (TYPE_is_number(P1->type)) + { + if (OBJECT_class(P2->_object.object)->operators) + return OP_FLOAT_OBJECT; + } + else if (TYPE_is_number(P2->type)) + { + if (OBJECT_class(P1->_object.object)->operators) + return OP_OBJECT_FLOAT; + } + else + { + CLASS *class1 = OBJECT_class(P1->_object.object); + CLASS *class2 = OBJECT_class(P2->_object.object); + + if (class1->operators) + { + if (class1 == class2) + return OP_OBJECT; + + if (class2->operators) + { + if (class1->operators->strength > class2->operators->strength) + return OP_OBJECT_CONV; + else + return OP_CONV_OBJECT; + } + } + } + + return 0; +} + +static void operator_object_float(VALUE *P1, VALUE *P2, uchar op) +{ + void *(*func)(void *, double) = (void *(*)(void *, double))((void **)(OBJECT_class(P1->_object.object)->operators))[op]; + VALUE_conv_float(P2); + void *result = (*func)(P1->_object.object, P2->_float.value); + OBJECT_unref(P1->_object.object); + P1->_object.object = result; +} + +static void operator_float_object(VALUE *P1, VALUE *P2, uchar op) +{ + void *(*func)(void *, double) = (void *(*)(void *, double))((void **)(OBJECT_class(P2->_object.object)->operators))[op]; + VALUE_conv_float(P1); + void *result = (*func)(P2->_object.object, P1->_float.value); + P1->_object.class = P2->_object.class; + OBJECT_unref(P2->_object.object); + P1->_object.object = result; +} + +static void operator_object(VALUE *P1, VALUE *P2, uchar op) +{ + void *(*func)(void *, void *) = (void *(*)(void *, void *))((void **)(OBJECT_class(P2->_object.object)->operators))[op]; + void *result = (*func)(P1->_object.object, P2->_object.object); + OBJECT_unref(P1->_object.object); + OBJECT_unref(P2->_object.object); + P1->_object.object = result; +} + +static void operator_object_conv(VALUE *P1, VALUE *P2, char op) +{ + VALUE_conv(P2, (TYPE)P1->_object.class); + operator_object(P1, P2, op); +} + +static void operator_conv_object(VALUE *P1, VALUE *P2, char op) +{ + VALUE_conv(P1, (TYPE)P2->_object.class); + operator_object(P1, P2, op); +} + +#define MANAGE_VARIANT(_func) \ +({ \ + type = Max(P1->type, P2->type); \ + if (TYPE_is_void(P1->type) || TYPE_is_void(P2->type)) \ + THROW(E_NRETURN); \ + \ + if (TYPE_is_number_date(type)) \ + { \ + *PC |= type; \ + goto *jump[type]; \ + } \ + \ + VARIANT_undo(P1); \ + VARIANT_undo(P2); \ + \ + if (TYPE_is_string(P1->type)) \ + VALUE_conv_float(P1); \ + \ + if (TYPE_is_string(P2->type)) \ + VALUE_conv_float(P2); \ + \ + if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) \ + type = T_NULL; \ + else \ + type = Max(P1->type, P2->type); \ + \ + if (TYPE_is_number_date(type)) \ + { \ + (_func)(code | type); \ + VALUE_conv_variant(P1); \ + return; \ + } \ +}) + +#define MANAGE_VARIANT_POINTER(_func) \ +({ \ + type = Max(P1->type, P2->type); \ + if (TYPE_is_void(P1->type) || TYPE_is_void(P2->type)) \ + THROW(E_NRETURN); \ + \ + if (TYPE_is_number_date(type) || TYPE_is_pointer(type)) \ + { \ + *PC |= type; \ + goto *jump[type]; \ + } \ + \ + VARIANT_undo(P1); \ + VARIANT_undo(P2); \ + \ + if (TYPE_is_string(P1->type)) \ + VALUE_conv_float(P1); \ + \ + if (TYPE_is_string(P2->type)) \ + VALUE_conv_float(P2); \ + \ + if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) \ + type = T_NULL; \ + else \ + type = Max(P1->type, P2->type); \ + \ + if (TYPE_is_number_date(type) || TYPE_is_pointer(type)) \ + { \ + (_func)(code | type); \ + VALUE_conv_variant(P1); \ + return; \ + } \ +}) + + +#define MANAGE_VARIANT_POINTER_OBJECT(_func) \ +({ \ + type = Max(P1->type, P2->type); \ + if (TYPE_is_void(P1->type) || TYPE_is_void(P2->type)) \ + THROW(E_NRETURN); \ + \ + if (TYPE_is_number_date(type) || TYPE_is_pointer(type)) \ + { \ + *PC |= type; \ + goto *jump[type]; \ + } \ + \ + VARIANT_undo(P1); \ + VARIANT_undo(P2); \ + \ + if (TYPE_is_string(P1->type)) \ + VALUE_conv_float(P1); \ + \ + if (TYPE_is_string(P2->type)) \ + VALUE_conv_float(P2); \ + \ + if (TYPE_is_null(P1->type) || TYPE_is_null(P2->type)) \ + type = T_NULL; \ + else \ + type = Max(P1->type, P2->type); \ + \ + if (TYPE_is_number_date(type) || TYPE_is_pointer(type)) \ + { \ + (_func)(code | type); \ + VALUE_conv_variant(P1); \ + return; \ + } \ + \ + if (TYPE_is_object(type)) \ + { \ + type = check_operators(P1, P2); \ + if (type) \ + { \ + *PC |= type; \ + goto *jump[type]; \ + } \ + } \ +}) + + +STATIC_SUBR void _SUBR_add(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, + &&__DATE, NULL, NULL, &&__POINTER, + &&__OBJECT_FLOAT, &&__FLOAT_OBJECT, &&__OBJECT_CONV, &&__CONV_OBJECT, &&__OBJECT + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x0F; + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_BOOLEAN; + P1->_integer.value = P1->_integer.value | P2->_integer.value; goto __END; + +__BYTE: + + P1->type = T_BYTE; + P1->_integer.value = (unsigned char)(P1->_integer.value + P2->_integer.value); goto __END; + +__SHORT: + + P1->type = T_SHORT; + P1->_integer.value = (short)(P1->_integer.value + P2->_integer.value); goto __END; + +__INTEGER: + + P1->type = T_INTEGER; + P1->_integer.value += P2->_integer.value; goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + P1->_long.value += P2->_long.value; goto __END; + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + P1->_single.value += P2->_single.value; goto __END; + +__DATE: +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + P1->_float.value += P2->_float.value; + //fprintf(stderr, "+: %.24g\n", P1->_float.value); + goto __END; + +__POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + + P1->_pointer.value += (intptr_t)P2->_pointer.value; goto __END; + +__OBJECT_FLOAT: + + operator_object_float(P1, P2, CO_ADDF); + goto __END; + +__FLOAT_OBJECT: + + operator_float_object(P1, P2, CO_ADDF); + goto __END; + +__OBJECT_CONV: + + operator_object_conv(P1, P2, CO_ADDF); + goto __END; + +__CONV_OBJECT: + + operator_conv_object(P1, P2, CO_ADDF); + goto __END; + +__OBJECT: + + operator_object(P1, P2, CO_ADDF); + goto __END; + +__VARIANT: + + MANAGE_VARIANT_POINTER_OBJECT(_SUBR_add); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); + +__END: + + SP--; +} + +STATIC_SUBR void _SUBR_sub(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, NULL, NULL, &&__POINTER + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x0F; + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_BOOLEAN; + P1->_integer.value = P1->_integer.value ^ P2->_integer.value; goto __END; + +__BYTE: + + P1->type = T_BYTE; + P1->_integer.value = (unsigned char)(P1->_integer.value - P2->_integer.value); goto __END; + +__SHORT: + + P1->type = T_SHORT; + P1->_integer.value = (short)(P1->_integer.value - P2->_integer.value); goto __END; + +__INTEGER: + + P1->type = T_INTEGER; + P1->_integer.value -= P2->_integer.value; goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + P1->_long.value -= P2->_long.value; goto __END; + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + P1->_single.value -= P2->_single.value; goto __END; + +__DATE: +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + P1->_float.value -= P2->_float.value; goto __END; + +__POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + + P1->_pointer.value -= (intptr_t)P2->_pointer.value; goto __END; + +__VARIANT: + + MANAGE_VARIANT_POINTER(_SUBR_sub); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); + +__END: + + SP--; +} + +STATIC_SUBR void _SUBR_mul(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x0F; + goto *jump[type]; + +__BOOLEAN: + + P1->type = T_BOOLEAN; + P1->_integer.value = P1->_integer.value & P2->_integer.value; goto __END; + +__BYTE: + + P1->type = T_BYTE; + P1->_integer.value = (unsigned char)(P1->_integer.value * P2->_integer.value); goto __END; + +__SHORT: + + P1->type = T_SHORT; + P1->_integer.value = (short)(P1->_integer.value * P2->_integer.value); goto __END; + +__INTEGER: + + P1->type = T_INTEGER; + P1->_integer.value *= P2->_integer.value; goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + P1->_long.value *= P2->_long.value; goto __END; + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + P1->_single.value *= P2->_single.value; goto __END; + +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + P1->_float.value *= P2->_float.value; + //fprintf(stderr, "*: %.24g\n", P1->_float.value); + goto __END; + +__VARIANT: + + MANAGE_VARIANT(_SUBR_mul); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); + +__END: + + SP--; +} + +STATIC_SUBR void _SUBR_div(ushort code) +{ + static void *jump[] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__ERROR + }; + + TYPE type; + VALUE *P1, *P2; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x0F; + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: +__LONG: +__SINGLE: +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + P1->_float.value /= P2->_float.value; + if (isfinite(P1->_float.value)) + { + SP--; + return; + } + + THROW(E_ZERO); + +__VARIANT: + + MANAGE_VARIANT(_SUBR_div); + goto __ERROR; + +__ERROR: + + THROW(E_TYPE, "Number", TYPE_get_name(type)); +} + +STATIC_SUBR void _SUBR_compi(ushort code) +{ + static void *jump[17] = { + &&__VARIANT, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, + &&__STRING, &&__STRING, &&__POINTER, &&__ERROR, &&__ERROR, &&__ERROR, &&__NULL, &&__OBJECT + }; + + static void *test[] = { &&__GT, &&__LE, &&__LT, &&__GE }; + + char NO_WARNING(result); + VALUE *P1; + VALUE *P2; + TYPE type; + + P1 = SP - 2; + P2 = P1 + 1; + + type = code & 0x1F; + goto *jump[type]; + +__BOOLEAN: +__BYTE: +__SHORT: +__INTEGER: + + result = P1->_integer.value > P2->_integer.value ? 1 : P1->_integer.value < P2->_integer.value ? -1 : 0; + goto __END; + +__LONG: + + VALUE_conv(P1, T_LONG); + VALUE_conv(P2, T_LONG); + + result = P1->_long.value > P2->_long.value ? 1 : P1->_long.value < P2->_long.value ? -1 : 0; + goto __END; + +__DATE: + + VALUE_conv(P1, T_DATE); + VALUE_conv(P2, T_DATE); + + result = DATE_comp_value(P1, P2); + goto __END; + +__NULL: +__STRING: + + VALUE_conv_string(P1); + VALUE_conv_string(P2); + + result = STRING_compare(P1->_string.addr + P1->_string.start, P1->_string.len, P2->_string.addr + P2->_string.start, P2->_string.len); + + RELEASE_STRING(P1); + RELEASE_STRING(P2); + goto __END; + +__SINGLE: + + VALUE_conv(P1, T_SINGLE); + VALUE_conv(P2, T_SINGLE); + + result = P1->_single.value > P2->_single.value ? 1 : P1->_single.value < P2->_single.value ? -1 : 0; + goto __END; + +__FLOAT: + + VALUE_conv_float(P1); + VALUE_conv_float(P2); + + result = P1->_float.value > P2->_float.value ? 1 : P1->_float.value < P2->_float.value ? -1 : 0; + goto __END; + +__POINTER: + + VALUE_conv(P1, T_POINTER); + VALUE_conv(P2, T_POINTER); + + result = P1->_pointer.value > P2->_pointer.value ? 1 : P1->_pointer.value < P2->_pointer.value ? -1 : 0; + goto __END; + +__OBJECT: + + result = OBJECT_comp_value(P1, P2); + //RELEASE_OBJECT(P1); + //RELEASE_OBJECT(P2); + goto __END_RELEASE; + +__VARIANT: + + { + bool variant = FALSE; + + if (TYPE_is_variant(P1->type)) + { + VARIANT_undo(P1); + variant = TRUE; + } + + if (TYPE_is_variant(P2->type)) + { + VARIANT_undo(P2); + variant = TRUE; + } + + type = Max(P1->type, P2->type); + + if (type == T_NULL || TYPE_is_string(type)) + { + TYPE typem = Min(P1->type, P2->type); + if (!TYPE_is_string(typem)) + THROW(E_TYPE, TYPE_get_name(typem), TYPE_get_name(type)); + } + else if (TYPE_is_object(type)) + goto __ERROR; + else if (TYPE_is_void(type)) + THROW(E_NRETURN); + + if (!variant) + *PC |= type; + + goto *jump[type]; + } + +__ERROR: + + THROW(E_TYPE, "Number, Date or String", TYPE_get_name(type)); + +__END_RELEASE: + + RELEASE(P1); + RELEASE(P2); + +__END: + + P1->type = T_BOOLEAN; + SP--; + + goto *test[(code >> 8) - (C_GT >> 8)]; + +__GT: + P1->_boolean.value = result > 0 ? -1 : 0; + return; + +__GE: + P1->_boolean.value = result >= 0 ? -1 : 0; + return; + +__LT: + P1->_boolean.value = result < 0 ? -1 : 0; + return; + +__LE: + P1->_boolean.value = result <= 0 ? -1 : 0; + return; +} diff --git a/main/tools/gbh3/.directory b/main/tools/gbh3/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/main/tools/gbh3/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/main/tools/gbh3/.icon.png b/main/tools/gbh3/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..271c30e14c9248854a9134a55652942d13a81153 GIT binary patch literal 3398 zcmV-M4Y~4(P)gs`BNFX5)AP^553os70i?K1W38~#qV>?Y;*O|^Fp0=5^jyuzd zlQE2^?KG3PooSM0rW3#7)@hA1wiDdeuPUyKZHyGaydMG~B+#3CCEdrl=bY^ylF*d^ zj{pT~s_)Fs+;h&|J^TIcZ-4vS-&vT#6s9nRDNN!2E#!PRzw=VhBY~)I8*M8m(ujpr z!lfxHnLpWlfBDXp;rMt%f=g%6vFMI3+qlfPH?(bXn4QK<9X)GDa- z1Ixt}Yb`71*usRX-~7(YT{m8BVwWXAV8Qe4MVBD3)K>?EgK2s5FFz$BoOcR*YfHCX zRADc<(ZIryF2N73HoYPEfps$;uRisqvirwBGZ!M@1(z-Wz}(Wyt3SyI9XhSSIpKHD#h;vpIt%`(eYYG5leA_qj+hQ~Q32%#h z_qCI;>t^TaAj>c;M6Y&de-jWG9QmyjeB3hNn~#pA1{z!3G4VjisB2OH*9+p=*Fg|i z;{}q)F=h;5k&A2i(*mW&2EMMBNf;$6Y1G&eSL&kL{nWsWb8AXe(rB#`+Kgqa)ir7Y zGFAcYsf5u=C&!z{WbzMukCvnF6KmPe4eP&3;Cm=-F2CVRfE`CnE1fh(>6D|!c1YCZ zEP+Abvwhoh*xEYg*R7_bWhdS32e|&$FJBb_R4`V6jIt@CjY?{z#|j{qFF+9Z?B4zo zQn!T6;XTAu5d&ofjCx#uz%<*)>XsuG`Sb)7e3-H_&r_Z$DR9AzwIBPyfto;Z# z6+0ZC^BJ^gjx@f`+-2*D7EZr%0*qVVwGL87v?PtFaeutDjnMA5pW*b8Mtlq2@>LY( zPb0{<9S=1F=ab#Qq%+-=KMkF~I>;5a>{UU$* zm5qGq$ItNQzy1q<_40q=&RK}giBjgkfOvfH;j5Uo-8i!-2VlnbWal?{@-0->kXzcaS z)+caN5CkAXy1R{38n^ca{Nu`7_MJ`;d@6p%OMtkQmn)klL;^H>u z-Ejv6Ky>WJm;Q4xBp4JqyYR%Bpz2sf|bWp5muZ|AeNkuOroqcy`AL zj>S?a;VgM;E$-=7mQ*dFysGxf4gJW7XU%}2ZrQkTwCNOOMP&(azFf!$I~%CZj}r@( zFql(Hi72j{SHbR{6oG{wKD&i4e*SK9ZhMfXL#?#$ZY3Rn$<4zb>_@aA-Eo4MDUXHA zSEDEB{I`BBk5x0z|I8_wP2uW?FIcysp@G}Kx%KRI_7t!njy9bpto<$#EhBT$f>=AY zcW*TOj4#|(&o2&Y`jS3@xJ1OF2uNl;0?$L&uHYXw{RPg9Imo{KF{5$yz z>JcpY9qrt@;gNH;rJsaVGvVti&;Kk^JBR9TZn|LIO`A52S!T5sk>TMD0z_%Q%UF>P ztx!r0`985o_7vFu&K4e6Gml-VFg?~`uyrp5OII@(?_{Rq_@45zb~54kM?e3xxki! zQaY`nyyYTl+k2o&)m(8zuH2zo8%yhdsl6wyt;1E$R8?O$E!F0Bb_9 zprQKfv?X#8V|jFW2@kv#rz{LVUQ^8He-Y=Tr+9hK86I1kB|w%1h|UTIeVZkKQK=DS z0v`#Kpq&u;(<^Zb3K-b&0ckgZh!CDpLq)O|Wegpe0RQMNg8B6n&RK|!hKYCs%+MYO z_HJXzb+=5a08OFaB1l=pz(cRcNx7EgW$z zD4Q6W8>k@*2q}%tEvIV!Lgw~DER{x-qT`JhnYnTe`MxE$w36mzoSgDX(yq(Nw2M|k ze?gS8+-^=aH&Hon`J@TZ??wo)h!%R?fT$BNw;;;jHpB@$u)ZbcjSb|pgC8TK7GT9D zMg`C$M{9t#kY8B9zI|~HKm7xAK?Qv$Pl4xR6s%o!7yo_y0M{8fm0O5sheAy)LnlRA z&m@^PvzncUcHxO;&fKLaeff!x>HP<2$z6r$p?;Q@7=F1oLuSxIFeE@#(bziKuT^Hu zekf}SXi1G^ORR6tI*RMBTg7{L+;__owzjz(-mwiQXE7%}-bQacMcVRpSiQE4fG z2(P`h1;3yKwPHOh*4+hXILLnVd*sdtiui2Z3$LJc$#brFh&x= z@aP3vBs+ULyLK@fjAC0`3-e8a_YNPUWc3DuP%h2QEl4Df*oh;A2M37OE(2?ag%BLA z(D>{;-od^X|C2ct6(qWjlL-PA)!lH>7GUuCYR}`mmVA(p^6%x<_hSG=#ADr}C=aix z#u>Ng%?^c)5*cxVN-^pKj3VU?M}JB;6oeplx*aiL4(!{(yjgRJ`dx`}!3Mq)@5DJAb#*8lZjO8L~)CU-0bQMrqC3SGt)Wkne zo^D#nbZ%R_fvWvY{Jgc5P+1jmv2=Ew;Kr1oYOz?ti&xQ~ z8l*@mZdtIHnKg@OIlKpL^hM_bMZnQRC4x(J(HW|j&HZHIE@%N+fshfBGW>1Cx9CsW ztj$@ll)}dMNR?G#^NTqwiv7<%#X}n(pl<03DreQuxbuD9e(yDMYnBo&sUV(T#C~fz zdi*#G5AEjm4G&D1^!9f6!H)AkGslio*uUw5bsHKQn19c=vJ|w+4D-}B%Lgc}5+g@~ zEWVOY_&gj6g}Ha#Jv{yLGdPQH!i%4wEEFb_aS;&;rj@YvwvDW;zlG-J{WR|SfD>IE zxIw_ok}|5-Y(OsJ^Yb^hx)c8Ic+}qe_}CjFN`^DwBR)U?wjQvL_GfquAeGJ(dw~sK z)SEsfFiO%nVW$@$-{E^3>*Of|V8tIydKzm3Nv3@rSj#ncLPkJ|=s+nE5t|pXgD^Y= zVm)V$dZB_)BJHbGGCdqSU4sgM_8^=a)4G;OczB{msD#G7KG*Itcv7hyXBjjI "/" Then + If sArg = "." Then + sArg = Application.Dir + Else + sArg = Application.Dir &/ sArg + Endif + Endif + If IsDir(sArg) Then + aRec = RDir(sArg, "*.{c,cc,cpp}") + For Each sRec In aRec + OneFile(sArg &/ sRec, hOut) + Next + Else + OneFile(sArg, hOut) + Endif + Next + + If $sComponent Then + Close #hOut + If Stat(sPath).Size = 0 Then + PrintMessage("Removing void data file") + Try Kill sPath + Endif + Endif + +Catch + + sMsg = Error.Text & ": " & Error.Backtrace.Join(" ") + Output To Default + PrintError(sMsg) + +End + +Private Sub AddHelp(cHelp As Collection, sClass As String, sSymbol As String, sHelp As String) + + Dim cCol As Collection + + PrintMessage("AddHelp: " & sClass & "." & sSymbol) + + cCol = cHelp[sClass] + If Not cCol Then + cCol = New Collection + cHelp[sClass] = cCol + Endif + + cCol[sSymbol] = sHelp + +End + + +Private Sub OneFile(sPath As String, hOut As File) + + Dim hFile As File + Dim sLine As String + Dim aHelp As New String[] + Dim bInsideComments As Boolean + Dim sHelp As String + Dim iPos As Integer + Dim bInsideDesc As Boolean + Dim sMacro As String + Dim iPos2 As Integer + Dim aArg As String[] + Dim sClass As String + Dim sSymbol As String + Dim cHelp As Collection + Dim sDeclareClass As String + Dim cCol As Variant + Dim aClass As String[] + Dim sImpl As String + + PrintMessage("Processing " & sPath & "...") + hFile = Open sPath For Input + + cHelp = New Collection + + For Each sLine In hFile.Lines + + sLine = Trim(sLine) + If Not sLine Then Continue + + 'PrintMessage(sLine) + + If sLine Begins "/// " Then + + aHelp.Add(Mid$(sLine, 5)) + Continue + + Else If sLine = "/**G" Or If sLine = "/**" Then + + bInsideComments = True + Continue + + Else If sLine Begins "/** " Then + + aHelp.Add(Mid$(sLine, 5)) + bInsideComments = True + Continue + + Else If bInsideComments Then + + If RTrim(sLine) Ends "*/" Then + + bInsideComments = False + Continue + + Else + + While Left(sLine) = "*" + sLine = Mid$(sLine, 2) + Wend + If Left(sLine) = " " Then sLine = Mid$(sLine, 2) + aHelp.Add(sLine) + Continue + + Endif + + Endif + + If aHelp.Count Then + + sHelp = Trim(aHelp[0]) + If Len(sHelp) >= 3 And If sHelp Begins "[" And If sHelp Ends "]" Then + sHelp = Mid$(sHelp, 2, -1) + iPos = InStr(sHelp, ".") + If iPos = 0 Then + sClass = sHelp + sSymbol = "" + Else + sClass = Left$(sHelp, iPos - 1) + sSymbol = Mid$(sHelp, iPos + 1) + Endif + + aHelp.Remove(0) + AddHelp(cHelp, sClass, sSymbol, aHelp.Join("\n")) + sHelp = "" + Else + sHelp = Trim(aHelp.Join("\n")) + Endif + aHelp.Clear + + Endif + + If sHelp Then + If sLine Begins "BEGIN_METHOD" Or If sLine Begins "BEGIN_PROPERTY" Then + + iPos = InStr(sLine, "(") + If iPos = 0 Then Continue + sLine = Mid$(sLine, iPos + 1) + iPos = InStr(sLine, ",") + If iPos = 0 Then iPos = InStr(sLine, ")") + If iPos = 0 Then Continue + sLine = Left(sLine, iPos - 1) + + AddHelp(cHelp, "@", sLine, sHelp) + sHelp = "" + Continue + + Endif + Endif + + If sLine Begins "GB_DECLARE" Then + bInsideDesc = True + Try sDeclareClass = Scan(sLine, "GB_DECLARE*(\"*\"*")[1] + If Error Then PrintError("Missing class name in GB_DECLARE macro: " & sLine) + Continue + Endif + + If bInsideDesc Then + + If sLine Begins "GB_END_DECLARE" Then + bInsideDesc = False + Continue + Endif + + + iPos = InStr(sLine, "(") + If iPos = 0 Then Continue + sMacro = Left(sLine, iPos - 1) + + If sMacro Not Begins "GB_" Then Continue + If InStr(sMacro, "_CONSTANT") = 0 And If InStr(sMacro, "_PROPERTY") = 0 And If InStr(sMacro, "_METHOD") = 0 Then Continue + + iPos2 = InStr(sLine, ")", iPos + 1) + If iPos2 = 0 Then Continue + + aArg = Split(Mid$(sLine, iPos + 1, iPos2 - iPos - 1), ",", Chr$(34)) + sSymbol = Trim(aArg[0]) + Try sImpl = Trim(aArg[2]) + + If sHelp Then + + AddHelp(cHelp, sDeclareClass, sSymbol, sHelp) + sHelp = "" + + Else + + If InStr(sMacro, "_CONSTANT") Then Continue + If InStr(sMacro, "_SELF") Then Continue + Try sHelp = cHelp["@"][sImpl] + If sHelp Then + AddHelp(cHelp, sDeclareClass, sSymbol, sHelp) + sHelp = "" + Endif + + Endif + + Endif + + Next + + Close #hFile + + ' Make class list + + aClass = New String[] + For Each cCol In cHelp + If cHelp.Key = "@" Then Continue + aClass.Add(cHelp.Key) + Next + aClass.Sort(gb.IgnoreCase) + + ' Generate help + + For Each sClass In aClass + + cCol = cHelp[sClass] + If cCol.Count = 0 Then Continue + + Print #hOut, "#"; sClass + + For Each sHelp In cCol + Print #hOut, cCol.Key + Print #hOut, "'"; Split(sHelp, "\n").Join("\n'") + Next + + Next + +End + +' Private Function Extract(hFile As File) As String[] +' +' Dim hNameFn As New RegExp, hNameInline As New RegExp +' Dim sLine As String, sInline As String +' Dim aRes As New String[] +' Dim bRecord As Boolean +' +' hNameFn.Compile("^BEGIN_.*\\(([^,)]+).*") +' hNameInline.Compile("/\\*\\*G (.+)$") +' +' For Each sLine In hFile.Lines +' If sLine Match "^[\\t ]*\\*?\\*/$" Then +' If sInline Then +' aRes.Add("G " & sInline) +' sInline = "" +' bRecord = False +' Endif +' Continue +' Endif +' hNameFn.Exec(sLine) +' If hNameFn.Offset <> -1 And If bRecord And If Not sInline Then +' aRes.Add(hNameFn[1].Text) +' bRecord = False +' Endif +' +' If bRecord Then aRes.Add(RegExp.Replace(sLine, "^[\\t ]*\\*", "'")) +' +' If sLine Match "^/\\*\\*G$" Then +' If bRecord Then aRes.Add("ERROR") +' sInline = "" +' bRecord = True +' Endif +' hNameInline.Exec(sLine) +' If hNameInline.Offset <> -1 Then +' If bRecord Then aRes.Add("ERROR") +' sInline = LTrim$(hNameInline[1].Text) +' bRecord = True +' Endif +' Next +' If bRecord Then aRes.Add("ERROR") +' Return aRes +' End +' +' Private Function Translate(aSource As String[], sPath As String) As String[] +' +' Dim sLine As String, aRes As New String[] +' +' For Each sLine In aSource +' If Not sLine Then Continue +' If sLine = "ERROR" Or If sLine Begins "'" Then +' aRes.Add(sLine) +' Continue +' Endif +' If sLine Begins "G " Then ' Syntax-2? +' aRes.Add(Right$(sLine, -2)) +' Continue +' Endif +' ' Syntax-1 +' aRes.Add(GetSyntax1(sLine, sPath)) +' Next +' Return aRes +' End +' +' Private Function GetSyntax1(sFunc As String, sPath As String) As String +' +' Dim hClassName As New RegExp, hFunction As New RegExp +' Dim hFile As File, sLine, sClass As String +' Dim aRes As New String[] +' +' hClassName.Compile("GB_DECLARE\\(\\\"([^\\\"]+).*") +' hFunction.Compile("GB_[^(]+\\(\\\"([^\"]+)\\\".*" & sFunc & "\\W") +' +' hFile = Open sPath For Input +' sClass = "ERROR" +' For Each sLine In hFile.Lines +' If Not sLine Then Continue +' hClassName.Exec(sLine) +' If hClassName.Offset <> -1 Then sClass = hClassName[1].Text +' hFunction.Exec(sLine) +' If hFunction.Offset <> -1 Then +' If Not aRes.Count Then aRes.Add(sClass) +' aRes.Add(hFunction[1].Text) +' Endif +' Next +' Close #hFile +' Return aRes.Join(" ") +' End +' +' Public Sub MakeHelp(aSource As String[], hOut As File) +' +' Dim sLine, sCls, sSym As String +' Dim cHelp As New Collection, aCurrent As New String[] +' Dim cClass As Collection, aHelp, aSyn As String[] +' Dim iInd As Integer +' +' Output To hOut +' For Each sLine In aSource +' If Not sLine Then Continue +' If sLine Begins "'" Then +' aCurrent.Add(sLine) +' Else +' With Scan(sLine, "* *") +' If .Count = 0 Then ' +' sCls = sLine +' sSym = "#" +' Else If .Count = 2 Then ' +' sCls = Trim$(.[0]) +' sSym = Trim$(.[1]) +' Endif +' If Not cHelp[sCls] Then cHelp[sCls] = New Collection +' cHelp[sCls][sSym] = aCurrent +' aCurrent = New String[] +' End With +' Endif +' Next +' +' For Each cClass In cHelp +' Print "#"; cHelp.Key +' aHelp = cClass["#"] +' If aHelp Then Print aHelp.Join("\n") +' For Each aHelp In cClass +' If cClass.Key = "#" Then Continue +' aSyn = Split(cClass.Key, " ") +' Print aSyn[0] +' If aHelp.Count Then Print aHelp.Join("\n") +' For iInd = 1 To aSyn.Max +' Print aSyn[iInd] +' Print "' A synonym for";; aSyn[0]; "." +' Next +' Next +' Next +' Output To Default +' End diff --git a/main/tools/gbh3/.src/MOldMain.module b/main/tools/gbh3/.src/MOldMain.module new file mode 100644 index 00000000..d08330f4 --- /dev/null +++ b/main/tools/gbh3/.src/MOldMain.module @@ -0,0 +1,256 @@ +' Gambas module file + +' ' Gambas module file +' +' Private $sRoot As String +' Private $sComponent As String +' Private $bVerbose As Boolean +' +' Private Sub PrintError(sErr As String) +' +' Error File.Name(Args[0]); ": error: "; sErr +' Quit 1 +' +' End +' +' Private Sub PrintMessage(sMsg As String) +' +' If Not $bVerbose Then Return +' Error sMsg +' +' End +' +' Public Sub Main() +' +' Dim iInd As Integer, aSources As New String[] +' Dim sArg, sRec As String, aRec As String[] +' Dim sPath As String +' Dim hOut As File +' Dim bOnlySources As Boolean +' Dim sMsg As String +' +' $sRoot = System.Path +' +' For iInd = 1 To Args.Max +' Select Case Args[iInd] +' Case "-h", "--help" +' Print File.Load("usage") +' Quit +' Case "-V", "--version" +' Print Application.Version +' Quit +' Case "-L", "--license" +' Print File.Load("license") +' Quit +' Case "-v", "--verbose" +' $bVerbose = True +' Case "-r", "--root" +' $sRoot = Args[iInd + 1] +' If Not $sRoot Then PrintError(Args[iInd] & " requires an argument") +' Inc iInd +' Case "-c", "--component" +' $sComponent = Args[iInd + 1] +' If Not $sComponent Then PrintError(Args[iInd] & " requires an argument") +' Inc iInd +' Case "--" +' bOnlySources = True +' Default +' If Not bOnlySources Then +' If Args[iInd] Begins "-" Then +' PrintError("unknown option: " & Args[iInd]) +' Endif +' Endif +' aSources.Add(Args[iInd]) +' End Select +' Next +' +' If aSources.Count = 0 Then aSources.Add(".") +' +' If $sComponent Then +' sPath = $sRoot &/ "share/gambas" & System.Version &/ "info" &/ $sComponent & ".help" +' PrintMessage("Output to " & sPath) +' hOut = Open sPath For Create +' Else +' hOut = File.Out +' Endif +' +' For Each sArg In aSources +' If Left(sArg) <> "/" Then +' If sArg = "." Then +' sArg = Application.Dir +' Else +' sArg = Application.Dir &/ sArg +' Endif +' Endif +' If IsDir(sArg) Then +' aRec = RDir(sArg, "*.{c,cc,cpp}") +' For Each sRec In aRec +' OneFile(sArg &/ sRec, hOut) +' Next +' Else +' OneFile(sArg, hOut) +' Endif +' Next +' +' If $sComponent Then +' Close #hOut +' If Stat(sPath).Size = 0 Then +' PrintMessage("Removing void data file") +' Try Kill sPath +' Endif +' Endif +' +' Catch +' +' sMsg = Error.Text & ": " & Error.Backtrace.Join(" ") +' Output To Default +' PrintError(sMsg) +' +' End +' +' Private Sub OneFile(sPath As String, hOut As File) +' +' Dim hFile As File +' +' PrintMessage("Processing " & sPath & "...") +' hFile = Open sPath For Input +' +' MakeHelp(Translate(Extract(hFile), sPath), hOut) +' +' Close #hFile +' +' End +' +' Private Function Extract(hFile As File) As String[] +' +' Dim hNameFn As New RegExp, hNameInline As New RegExp +' Dim sLine As String, sInline As String +' Dim aRes As New String[] +' Dim bRecord As Boolean +' +' hNameFn.Compile("^BEGIN_.*\\(([^,)]+).*") +' hNameInline.Compile("/\\*\\*G (.+)$") +' +' For Each sLine In hFile.Lines +' If sLine Match "^[\\t ]*\\*?\\*/$" Then +' If sInline Then +' aRes.Add("G " & sInline) +' sInline = "" +' bRecord = False +' Endif +' Continue +' Endif +' hNameFn.Exec(sLine) +' If hNameFn.Offset <> -1 And If bRecord And If Not sInline Then +' aRes.Add(hNameFn[1].Text) +' bRecord = False +' Endif +' +' If bRecord Then aRes.Add(RegExp.Replace(sLine, "^[\\t ]*\\*", "'")) +' +' If sLine Match "^/\\*\\*G$" Then +' If bRecord Then aRes.Add("ERROR") +' sInline = "" +' bRecord = True +' Endif +' hNameInline.Exec(sLine) +' If hNameInline.Offset <> -1 Then +' If bRecord Then aRes.Add("ERROR") +' sInline = LTrim$(hNameInline[1].Text) +' bRecord = True +' Endif +' Next +' If bRecord Then aRes.Add("ERROR") +' Return aRes +' End +' +' Private Function Translate(aSource As String[], sPath As String) As String[] +' +' Dim sLine As String, aRes As New String[] +' +' For Each sLine In aSource +' If Not sLine Then Continue +' If sLine = "ERROR" Or If sLine Begins "'" Then +' aRes.Add(sLine) +' Continue +' Endif +' If sLine Begins "G " Then ' Syntax-2? +' aRes.Add(Right$(sLine, -2)) +' Continue +' Endif +' ' Syntax-1 +' aRes.Add(GetSyntax1(sLine, sPath)) +' Next +' Return aRes +' End +' +' Private Function GetSyntax1(sFunc As String, sPath As String) As String +' +' Dim hClassName As New RegExp, hFunction As New RegExp +' Dim hFile As File, sLine, sClass As String +' Dim aRes As New String[] +' +' hClassName.Compile("GB_DECLARE\\(\\\"([^\\\"]+).*") +' hFunction.Compile("GB_[^(]+\\(\\\"([^\"]+)\\\".*" & sFunc & "\\W") +' +' hFile = Open sPath For Input +' sClass = "ERROR" +' For Each sLine In hFile.Lines +' If Not sLine Then Continue +' hClassName.Exec(sLine) +' If hClassName.Offset <> -1 Then sClass = hClassName[1].Text +' hFunction.Exec(sLine) +' If hFunction.Offset <> -1 Then +' If Not aRes.Count Then aRes.Add(sClass) +' aRes.Add(hFunction[1].Text) +' Endif +' Next +' Close #hFile +' Return aRes.Join(" ") +' End +' +' Public Sub MakeHelp(aSource As String[], hOut As File) +' +' Dim sLine, sCls, sSym As String +' Dim cHelp As New Collection, aCurrent As New String[] +' Dim cClass As Collection, aHelp, aSyn As String[] +' Dim iInd As Integer +' +' Output To hOut +' For Each sLine In aSource +' If Not sLine Then Continue +' If sLine Begins "'" Then +' aCurrent.Add(sLine) +' Else +' With Scan(sLine, "* *") +' If .Count = 0 Then ' +' sCls = sLine +' sSym = "#" +' Else If .Count = 2 Then ' +' sCls = Trim$(.[0]) +' sSym = Trim$(.[1]) +' Endif +' If Not cHelp[sCls] Then cHelp[sCls] = New Collection +' cHelp[sCls][sSym] = aCurrent +' aCurrent = New String[] +' End With +' Endif +' Next +' +' For Each cClass In cHelp +' Print "#"; cHelp.Key +' aHelp = cClass["#"] +' If aHelp Then Print aHelp.Join("\n") +' For Each aHelp In cClass +' If cClass.Key = "#" Then Continue +' aSyn = Split(cClass.Key, " ") +' Print aSyn[0] +' If aHelp.Count Then Print aHelp.Join("\n") +' For iInd = 1 To aSyn.Max +' Print aSyn[iInd] +' Print "' A synonym for";; aSyn[0]; "." +' Next +' Next +' Next +' Output To Default +' End diff --git a/main/tools/gbh3/README b/main/tools/gbh3/README new file mode 100644 index 00000000..7f741ac1 --- /dev/null +++ b/main/tools/gbh3/README @@ -0,0 +1,112 @@ +About gbh3 +---------- + +gbh3 is used to extract Gambas documentation from C/C++ source files and +create .help files for a component from them. It is a single Gambas project +depending only on gb.pcre. + +... and why? +------------ + +Components written in Gambas can already be documented in-code. This docu- +mentation is then stored inside their .info files. With a similar result +provided by gbh3 for C/C++ components, we can document all[*] Gambas in +its source files (at least my components will be) which makes it, IMHO, +easier to keep the documentation up-to-date. Also the help could be +displayed locally by the IDE or remotely once these .help files are imported +to the gambaswiki.org site. + +Also, documentation can be bound to a specific source code version, so that +you get docs for the version of Gambas you are running, not only for the +development branch. + +[*] I'm not sure about intrinsic functions, though. Should be feasible, + looking at gbx_class_info.c... + +Great! How do I prepare my component? +------------------------------------- + +The No. 0 rule is to BE CAREFUL! There are quite some rules to remember: + +1. Documentation syntax. + +You may write a Gambas documentation comment like + + /**G + * Here goes the documentation. + **/ + BEGIN_METHOD_VOID(ClassName_MethodName) + /* ... */ + END_METHOD + +or alternatively + + /**G Class Symbol + * Documenting Class.Symbol + **/ + +Instead of the **/ at the end, you may also write */. Cool, huh? + +The second syntax can also be used to document the class per se: + + /**G Class + * Class documentation here + **/ + +The difference between both notations is that the first must immediately +precede a BEGIN_{PROPERTY,METHOD,METHOD_VOID) line that defines the symbol +to which the documentation refers. The second syntax documents the indicated +symbol -- no matter where the comment is. + +This is intended to document GB_CONSTANTs or GB_PROPERTY_SELFs which don't +have a BEGIN_* line. It can neverthelss be used for any other symbol but +NEVER intermix both syntaxes on a single symbol. + +The ClassName_MethodName part I call "(implementation) function name". The +particular function naming convention shown is not mandatory. The scripts +take the function name and look up the corresponding symbol in your +GB_DESC[]. + +2. Regular expressions. + +The regular expressions used to filter these comments are somewhat strict. +You have to pass the spaces exactly as indicated in the line where your +comment text starts, i.e. *text. Also note that Class and +Symbol in the second syntax are case sensitive! + +OTOH, I can't guarantee that the expressions will not consume junk that you +left behind where I didn't expect junk to be. + +3. Strict structure. + +DON'T try to confuse the parsers by not giving information it needs where +it expects it. + +4. Synonyms. + +The first match for a function name lookup in a GB_DESC[] is taken as the +"primary" implementation of that symbol. That means the help comment will +be attched to that symbol. All other symbols which call the same function +are considered synonyms and are *automatically* documented with the default +sentence: "This is a synonym for ". DO NOT document them +separately using a syntax-2 help block! + +5. Don't reuse method and property implementations (except for synonyms). + +As the implementation function name is searched in the GB_DESC[]s in your +source file, you may not put the same function into different classes -- or +the parser may get confused. + +Also, keep the GB_DESC[] in the same source file as the help comment. + +Umm... I need an example +------------------------ + +gb.openssl and gb.data are partially documented that way. Digest.Hash() and +Digest._call() in gb.openssl show how to deal with synonyms. There the entry +for Hash precedes the entry for _call, thus Hash is the primary symbol and +_call a synonym. + +To see a sample .help file (on stdout), you can do at the source tree root: + + $ gbx3 app/src/gbh3 -- gb.openssl diff --git a/main/tools/gbh3/icon.png b/main/tools/gbh3/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c2abe70a5ceb8f0631c2ef5dbb873b6839a1aafa GIT binary patch literal 3211 zcmV;640Q8}P)bw11R8 z=pPkS0`V8G%KSk^OOT3!pdbZ>LX$uUanhLDty3qq*N^p%_c1#=Gk50Ra}Iyp$K076 z%bs1^3yOcz(Yg1|+Aj-jzp#s&-Q z%h=>nCNV;-sWv5*y2o2Q#dV<|kBiJd6s}03!Q&RIau4zkztJr$k~-OIan}x@61I8OvzV1b~kC;+%^H* z9tIUA$7a5cJN+P2bF;j1#baT^4;mg8rNC3rRM3=(a*bjcY{>3fz@naC>4-s znn*K6(@9g$tg8PP3EWEeCJ!1~fK03E81#ByFb`|qXUi-=Eziuhv!CazXEDzp0DovHnaB}<$`N*sV3XN69zg% zNOo11c=}*JhbOY69S6s8uq+GHG|^h4ltN00>$wB+kjv!&sJK4=b*UBrlI`Wxvd5nsU#42E0+7vS zxpy+x4m|OEt%7&2)S*}$=7l#GZVv$plb4H6km(=i@`h{!+qI#h^@@DjVkl*|{Wp34 z+2tBi%AjF}5zH5^L2PuqSdnB3gZ#q}E~0er!(z__oJ}9Vw9@#bs5QdgkA|x*M7&lK z)a!Ksno{HX0%YsI@jVh|(-PARk{cle%~XC9@c8*UIIcD{QaG5V&FRJUo(bqV2%d62 zh@Bm#T$gC25kdsRNJsFC&j0*}k}GbL>3+e#o^McgW!L9A%H>15v)Fbkn*YnWW}FGq zIT9ptgteN)NcD02y=CSnino@4)6OAC_fu_z5;rtLiA}(x^QO}L?T?!X(QG}WOCJ2t zV4VY_L!?r{)OWcq`SuTMErscBLXn2}}<8ZdL(JP$Z3D_V@ z3LD?o@xFSi$2*8f5D;VT=zU8!_|!p%>A^lM%cABfzWh>|Qzfqjk}kj#Ns*^CI5y>K z??YkF1T?jUQUVp_{B)DPjuA%*QdboK2m2~Kd2ohQD#cP&^2Ha|IJ4r%z+=$R-4#?g zdsGIl=ihn)wAT2(3WvB5TW9c#fC|v)HWdUv1%EP?%cqYHlXe^~S0tbR?kZO{WE~S~ zfE}RY>j?#bMoEQj^u9jOGXYsyL3&LL?cj$kOwI%7AY+sC6H55VY!1^jdHzI&OXYU( zopt935L^oav1JHkvq2%#`{HTO1WfoBS!mQTjCA09dUNojC60ENpmhykb~uA!7`*t7 z7biEdB_$u#-9`Zoh5=2l#_UkGX99Z81P^#8(Dfx!qI>2Sr>!!)9%X&W>@Up!hw~|v zhSN*_=HJ76ThGv4Q(~>a2837P$jsoaC*Yv}Hom)rDSWi@L8BFu1x0yi>4TENGH){HNM9c8G;q#EK z_h{u~A~9%Cua|h{(F0q7vb80#qAv5W{|axc50UNL&AO{YHxQu`0v`|}SQ6HeFN9sB z{4)2CukhIX2$#w(FP?gbMQ;W~I1`MvZVZ&LmyK91p2-B{o&zj&C8CGb2C-}ie? zhi_N`nwA?&9T0J^{7;V85AlPQ6nz8xsWc>73A9M2gCa2@3KmZ;mV@Q+sIi7&aDIgV zD8xCij$NTu3!%wc2GxxVe0YACNA?fnc^*nBEGvLxn&u6w;f4uF#IUygyHC5v_x_kXmOpgQUQE%25Bg|wiuvB=Dz z#TR~Vf4qh$gouxUn2hV$4Q-4h0m}ptemE7aX@@_`e1TH-ZU%<-QEO`IO_lKfC2=rO*7tP={z=!YkT-j*&2(?p}}+2uk)Aw zGraA2jF0XmYul{V{5CZt17Ww_;TG@~0hS5bWCs_ZP9BDmXQ2<;~Tu@z0EhhU!#~GVRUemhA*hpeVTr}e$)ZD(}G_d+n_IP z;#jbOS;l06bK`^sXVX7i+N8^^XV!(Ng~ z(eQj+&!g_T)I67l=kdg$aX$ILG-LglxYv_)XNj)g$Oy2lFGP}RP~A%C7OmhZIKv)J z8T&bD%yU`XMOox&=pge;rz{3?HZvoA9Np8;k(nXp?] + +Options: + -r --root gives the Gambas installation directory + -c --component generate help directly in a component '*.help' file + -V --version display version + -L --license display license + -h --help display this help + +If contains directories, they are searched recursively for '*.c' +and '*.cpp' files. If is not specified, the current directory +is searched. + +The extracted help is printed on the standard output, unless the '-c' +option is specified. diff --git a/reconf b/reconf new file mode 100755 index 00000000..3f4d7a53 --- /dev/null +++ b/reconf @@ -0,0 +1,7 @@ +#!/bin/sh + +rm -f config.cache +rm -f acconfig.cache + +autoreconf -v --install $@ + diff --git a/reconf-all b/reconf-all new file mode 100755 index 00000000..4eb454c7 --- /dev/null +++ b/reconf-all @@ -0,0 +1,9 @@ +#!/bin/sh + +rm -f config.cache +rm -f acconfig.cache +rm -f libtool */libtool + +libtoolize --force --copy --nonrecursive +autoreconf -v --install $@ + diff --git a/version.m4 b/version.m4 new file mode 100644 index 00000000..5c19074e --- /dev/null +++ b/version.m4 @@ -0,0 +1,13 @@ +## Package version and e-mail for bugs are defined here + +m4_define([GB_VERSION], [3.12.2]) +m4_define([GB_MAIL], [gambas@users.sourceforge.net]) +m4_define([GB_URL], [http://gambas.sourceforge.net]) + +m4_define([GB_VERSION_MAJOR], [3]) +m4_define([GB_VERSION_MINOR], [12]) +m4_define([GB_VERSION_RELEASE], [2]) + +m4_define([GB_VERSION_FULL], [0x03120002]) +m4_define([GB_PCODE_VERSION], [0x03080000]) +m4_define([GB_PCODE_VERSION_MIN],[0x03000000]) -- 2.30.2

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%0Db{Pw*B*R@gJ zvK(6)q1c-wu04(%+`+0fH(#;<(O8U%%1S2ufYLo5;Z-gqUSCUMEJ<@q16_fl#C3_+ z*Wox*lpfg4WMvgq)zwU-QghmST4d(470H|yK-shu_R1>n0b!-FC3P&kZaJ-EFrLXG zN-^}>b2Mz)Le#gER5#M$q$sLuBy3Bv-(5#oV_>kP9M9%UO)dG8BE6>^7Bn=o=h$96(X_O#M(K-de2gcP zc!20F=PGE~m`S!L$z zhh~LduD{pMak5!>I-oeJLcfzS5tQD-aGyTM{(d_LTCA9LhOpW&Q zhu{8V_H=wmcgKDT^PkV?>Fi|h&Yf6mu~@1q7tq_+OI2lzj5EdLP_ZBe zHoxcCw|h5T?d@~s608(>n%N#;jHH8U>jhe5cK3AA(v@s8iXHvkTxZg}d%TmX&D#jV zC3JLjBgJKm_naUyHA(5xbztqZ5JE^RG(LN}hB)-xf6`K4PkN+_To7>miuLE^0gGB& zi8nUVJ(!~U`6u9kZ&F%Xj3AWk`v?Nd)cP9`@&>5h{tQ!}`4!qWZ6Yx`I;Z1gRK5_( zdjqF)fKg<;>E%NSXKYlOT34{GqKduychOkBfK*X2LC&RexSMq=){?vZ%Ou(luuHA&qxrEBANYynnJTk})%T{sMwg>pq#?8d~j^Okj2c@xCA}eoX!i3RE zar3g3e0l2^xM}@nw9%aHcjk|}c%18&EhCpYNmEl5$U0-9$}>SSe(OJ6O2D zFxI)BriOC7^cYK)Eu+4%an4)?56GANYE}pcj!eHB@h$q}Hft@*R#U#~T{1NbvC&G7 zi{kLpkMoVYA7I7mjWjkbX4jtgc=O#?C|SIkNL4+lXa$F@rM;_*<;V8%tJ@wp? znj)D@;<`B!BO`d8M>3Hh<2X#DQu#tClVNOh6fc(}F*-`lbvczxGBq(Vx8o#MnQ5Nd z&dLEwt90Q=ki}Q>F|UWi;Rs*cdM{7D_!Oa)H{qpDQ4@}k%ejaMu?1D!dduBxYP*?^ zj>GKQ`#wD*L%2aeLsbpUTecw=@cQ}shkpb>;n-@SiC#b3DD$qJv(YZ1WV!=h$N>WI z>Jj^`i5%Ys$YgVsUSK0!g)mC8MUl`LKs3bP+`U2`Hvm@rDaX@T8;Fzjbzm)5-3b{1 zC87hRL_};^*iON82#hCAwR_=MIGy!X#>r0CPFJAL~CZ1py#` zjIfkEH|&t8m{4g01=r7*_|k(;{X5v07*qoM6N<$f-zMzSO5S3 literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/.lang/ca.mo b/app/examples/Games/GNUBoxWorld/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..f41b28b08f9b91acd67574073d8fbe0ef14324e7 GIT binary patch literal 3974 zcmb_fO>ZMf8E#tL&v)b#Qj&?U%~yO58(&b>W72i`?%L|e+=W_T?vAJ0{;m7DDYkU zd;<6<;HQCq0rI+k0Dld9ABex;XPA5r_zU0~@HfC0fbRhzD)<+033%n>)%bbfr!am2 z$orRp_zSM#hu77CP$_5tdH*Kxr@*_wj{yG;T!Rho0pG{M7e86)^&Et*VE!r)uYx;J zn&s^RKLhLm`8)yqJum@6wV;ldtp6?GD)26l^|wGi@7q9@^Bo}X|KWW6V<7K;I3NEK z_&mmM1KGvz0$Kk*0DlGiGmzzd3yb(Y%j65{`*ol7qw^ouVR3yP53J)CakFo(;$|PR zoqVxeKAYpkI3&#o@(gkn20WGJL3MjPL7yhTg z=XS-Ua<9c$7cgTCX)x?Wr6~<1l~#_*bcNsny4N1;9Ucv+*||^mn!R4LGq}Hwm2ffM z%89JxsH{j8jDstU$la86K4|r}_prWsx4qvU+-J3S+JjE3->03!9yO`k>ccJU+h@3QE`7>++r6ll~QQLfzzS!lK~cCKjD>nA5CVUm}jHc1_k<|y0xRv3JBJJnVO8^f)p z$~Nl5t&O4CLgpdS#s*G;c9xt*I@qx{Gt(2KsidJkmHrLeo(b~+x)<6ij;;+^ikAez z2YMpUC2J%m>=lL+$V*H_#^QvDS2&V>4pGr3^gvE+gUVusVts-{OXbL?H~9*>aL9Hl zg?aK6Bdrt0d1)!aj`S!n2bI~jLPT;X44)7N%^^~w`VB)25h$)LTN-&MsETtA7i-ij zKx`&QlS|Q-!wh-p6a9@68n81Eh%!me2E#JJlyuPw`<1mB=`__R3ejxU4v-%t;+R!! zENuuH8#&s1DZ{kWje<)>B|sY8)u;EgN#iG^?e?#U9Wb2a+WZs_&T|g~EK0ly7dHq9H8C8^9TDw&V zggXTq0h%IbpjDin&;l;h=|qN9X&g(d671q-PCP#W=r3vRpWkg86H@Gli(ol6@A z_oWTGMjy+_)!K3G>s60yaJ4Tyy3{?a_2fix^Q*mzme%0DzE)d(xps4v)^0aeZ+#7Y zesyVISXUdM-B@^=8~$tB#ESZ2s#DrXLdy@YOvPC1P}(i5&y!LlvNn(+YtY#kj|c68 z*10lk;p!55Mdpw{Euesdx9bRl%GW6x3uC3*JR0oOZlAAXJx0>hT6qMf$dd-$9xAug zO-m3$ZAXJC8k84)Xg6=JBdYvnGpFTET3cWGMy;#CJg&A-vq9U!iB#lE&!oB!V!Ea; zmIc=dn!R~Oe8MP7Q-xYwC*)nBV_DLI7?dGZ=Z*rZQxS5-YA3J^RSu>@e~KkYhWAIa zhk2v~$L(pPhpb3Rq5WorVx9e@qQFSRf(pTSa4(2})ZURod{e$y1m2h?0fz8fL?74TD^mIr#T3zuLx!# zWEsa)kzf)%9CD{n^ezJgcJ22YL^Hd{qbr;H(o1Wa8~yL*02TlhSzR z)rN`pa>qF5-LCHjGN16NY|aE_gTBwlCa^{>$QPe7a2fM1J}eBQi$eIDsuU2QTq9>c z_Zbvv!$FrRB8jSjP5%!fu3Yvcmp=InCfsc6{1kwA#}e-!i=xHzO%%FWBl re9Fm(q@V90i=xg+Bw~af#-}Y#;glB0*L3!9, 2007-2010. +# +# +msgid "" +msgstr "" +"Project-Id-Version: GNUBoxWorld\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: 2011-03-20 18:06+0100\n" +"Last-Translator: Jordi Sayol \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: FMain.form:44 +msgid "About" +msgstr "Quant a" + +#: FrmAbout.form:13 +msgid "About..." +msgstr "Quant a..." + +#: FMain.class:254 +msgid "Are you sure?" +msgstr "N'esteu segur?" + +#: FrmAbout.form:20 +msgid "Author" +msgstr "Autor" + +#: FrmAbout.form:24 +msgid "Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n
pablomileti@gmail.com" +msgstr "Autor: Pablo Mileti.

Podeu enviar les vostres preguntes, suggeriments, errors, etc. a aquesta adreça de correu electrònic:\n pablomileti@gmail.com" + +#: FrmAbout.form:42 +msgid "Aim
\nThe shrimp has to push the movable boxes to the marked positions.

\nMove
\nTo move around the game board, use the arrow keys: up, down, left and right.

\nClear
\nTo restart the level, press the spacebar key." +msgstr "Objectiu
\nLa gambeta ha d'empènyer les caixes mòbils a les posicions marcades.

\nMoviment
\nPer moure-us per tot el tauler de joc, feu servir les tecles de fletxa: amunt, avall, esquerra i dreta.

\nNeteja
\nPer a reiniciar el nivell, premeu la tecla d'espai." + +#: FMain.form:21 +msgid "Clear" +msgstr "Neteja" + +#: FrmAbout.form:50 +msgid "&Close" +msgstr "&Tanca" + +#: FMain.form:18 +msgid "Game" +msgstr "Joc" + +#: .project:1 +msgid "GNUBoxWorld" +msgstr "GNUBoxWorld" + +#: FMain.class:81 +msgid "GNUBoxWorld - Congratulation! You're very clever!" +msgstr "GNUBoxWorld - Felicitats! Ets molt inteŀligent!" + +#: FMain.class:222 +msgid "GNUBoxWorld - Level " +msgstr "GNUBoxWorld - Nivell" + +#: FMain.class:83 +msgid "Good Luck!" +msgstr "Bona sort!" + +#: FMain.form:36 +msgid "Help" +msgstr "Ajuda" + +#: FMain.form:39 FrmAbout.form:38 +msgid "How to play?" +msgstr "Com jugar?" + +#: FMain.form:32 +msgid "Level" +msgstr "Nivell" + +#: FrmAbout.form:28 +msgid "License" +msgstr "Llicència" + +#: FrmAbout.form:32 +msgid "\n Copyright (C) 2010. Author: Pablo Mileti \n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see ." +msgstr "\n Copyright (C) 2010. Autor: Pablo Mileti \n\nAquest programa és programari lliure; el podeu distribuir i/o modificar sota els termes de la llicència GNU General Public License tal i com està publicada per la Free Software Foundation; ja sigui la versió 3 de la llicència, o bé (si ho preferiu) qualsevol versió posterior.\n\nAquest programa es distribueix amb la voluntat que pugui ser útil però SENSE CAP GARANTIA; ni tant sols les garanties implícites MERCANTILS o ESPECÍFIQUES PER UN PROPÒSIT DETERMINAT. Si voleu més informació, vegeu la llicència GNU General Public Licence.\n\nHauríeu d'haver rebut una còpia de la GNU General Public Licence juntament amb aquest programa. Si no fos així, mireu ." + +#: FMain.class:83 +msgid "Next level" +msgstr "Nivell següent" + +#: FMain.class:254 +msgid "No" +msgstr "-" + +#: FMain.form:26 +msgid "Quit" +msgstr "Surt" + +#: .project:2 +msgid "This is another version of the popular game Box World. Contains 16 levels, all are possible to resolve. Level designs are taken from Box It game for mobile phones." +msgstr "Aquesta és una altra versió del popular joc Box Wolrd. Conté 16 nivells, i tots es poden resoldre. El dissenys dels nivells s'han agafat del joc Box per a telèfons mòbils." + +#: FMain.class:254 +msgid "Yes" +msgstr "Sí" + diff --git a/app/examples/Games/GNUBoxWorld/.lang/cs.mo b/app/examples/Games/GNUBoxWorld/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..e8a898833707686b35d8b7d65cd7b27b2a5b064e GIT binary patch literal 3119 zcmb_eO>87b6)s?bF#IGef$V|9b0jv2GSjvJp=jdu?yNnwXPF;oJod^_E;ZdX)9vZ* zYO1QoJ4WJ4F5KX-EEfw22?;CW1VTzKv%>)iDc49SM?i~`D;FgAUe9>#T~JPx-2F{g zSH1VWpZeM#UcK@&fpHDjH*x(1*SB!};#0U`tbAICpW<4@^*KEM@rn@t0{$8JS>T^= zdj|ME@XNptfV}Q+z~2Es2I5cr3X@+1{sy=T{5|k_;3FVRiGKr2z$>4hKR*ln5}uz2 z^8O_t{=`eT@w#OoD2XbN_ul~i5_lW<8Q@=m0x>)Sawz`*$u9su0$#(!KY={|9E|b) ztH7@SUjVYd>%iXv%Rt`uA&}Sq9asTA0=@vehM?Hr3h=AI?*j2kY%TB&Am`^TAX8F6 z&Qk(p{y$v2{}GV+{}{;jUPbWik8SZ_4$Sj8ez-I^hv(yK_<`->8@Sl^Ra~4y_RE9q zK8uU_aEwTK!5JXF&NmE>>3ncca8Aw#^A;t-*wER;M8nY0%NwuIw^vqIJgVg`)TTyX;hUaIwxPIWKh5 z0Yz!SBGehe2w@F@9Y=9YeMPxdgFG%1GC+45y`9c}k815hx?AgZYwh0Q>sW~tU60_yR~kwv9aH*b!m6M zyW82TdqjIk3+MI|(m5!~VH5?*$tbo6^$^9gpcDrb%CSNbeHD#CNRp2hIZxpy7?rV3 zhecp;f6*vl)EH2zU70MHt%c5I)#c^m<701_=AJggWmKB0Y?s$PaeX7!R*AL#dM!%U zmiy~#eY1|vL!*TTNdh}dPNN-cScIAAG1^p7U*A*38;(7ZW(4k;wh>p?dTgbz1j8*o zRu`5v8WZtK!wpne9Lj{vNfVU0lEoaVqK|2$Cbmj>R;EB7qtRkDDB7Ef6ZC|WjaW%@ z*^5!uN#hD@G0Kki$S?=W99t%R)t81nc%s%vYs~MShes5O&z8-Nx+&&|a{*7D(SiVR zm|RUhiZ<^j=*yzfe~^O#Cj*7Zhr`DY{d|Zi<@_?|mDvn*9P8r<)vQ-q=ns-Xz*MWH zHA$gSgBvd5| z5CVuIcc6{9J;4Gg)bUt(G-n(r8x66GmpM6t4h)P=hz&Q~+{yf$_K{!8LY*q>i9=<@ zuF*%zca=s^Ihbo4`k=P){n~b&4(i>#MyFlc?NqvI9I*gaHX&sYD()WJxGp4##!{Y_M1&=*IIR2tG8;6=B?W1W*3sPj-}}^mqS(QDVbEM zS!-|av&*$+^Hv=3NxoQ9rBbWWs$cM1^;SxpD3!w9Bm*CZ0xY94QTjUhp)^*x8~eSj z%4-+vnASjvqsF*FF0F*}X2kxZg0mg7^JmjfNo-eK85$4O{vpfe-K@(IZ}m?ZKjatR8m|8`+4C z@J)^i`Ot>d5)kS_4b z2sWLb+E@ko(e#W{qADy3{}0_bn4pk|z@hk;)B}pKUC2d#q!P76cMvI#bP_Q!XQuBy ztrq9$Be7#7^dUe3Q7-rJfpycfCDERqW_tYaK_JB)IYOzYr|zbh{(PSFxqA3YY|40c ziU1b^qQw44r`&3yr|AgETD*r|ov;c(Ii{yv7+9PXXM7\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "GNUBoxWorld" +msgstr "-" + +#: .project:2 +msgid "This is another version of the popular game Box World. Contains 16 levels, all are possible to resolve. Level designs are taken from Box It game for mobile phones." +msgstr "To je další verze populární hry Box World. Obsahuje 16 úrovní, všechny je možné vyřešit. Vzory úrovňí jsou převzaty z Box hry pro mobilní telefony." + +#: FMain.class:81 +msgid "GNUBoxWorld - Congratulation! You're very clever!" +msgstr "GNUBoxWorld - Gratulujeme! Jsi velmi chytrý!" + +#: FMain.class:83 +msgid "Good Luck!" +msgstr "Hodně štěstí!" + +#: FMain.class:83 +msgid "Next level" +msgstr "Další level" + +#: FMain.class:222 +msgid "GNUBoxWorld - Level " +msgstr "GNUBoxWorld - úroveň " + +#: FMain.class:254 +msgid "Are you sure?" +msgstr "Jste si jisti?" + +#: FMain.class:254 +msgid "No" +msgstr "Ne" + +#: FMain.class:254 +msgid "Yes" +msgstr "Ano" + +#: FMain.form:18 +msgid "Game" +msgstr "Hra" + +#: FMain.form:21 +msgid "Clear" +msgstr "Vyčistit" + +#: FMain.form:26 +msgid "Quit" +msgstr "Ukončit" + +#: FMain.form:32 +msgid "Level" +msgstr "Úroveň" + +#: FMain.form:36 +msgid "Help" +msgstr "Nápověda" + +#: FMain.form:39 FrmAbout.form:38 +msgid "How to play?" +msgstr "Jak hrát?" + +#: FMain.form:44 +msgid "About" +msgstr "O" + +#: FrmAbout.form:13 +msgid "About..." +msgstr "O..." + +#: FrmAbout.form:20 +msgid "Author" +msgstr "Autor" + +#: FrmAbout.form:24 +msgid "" +"Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n" +" pablomileti@gmail.com" +msgstr "" +"Autor: Pablo Mileti.

Můžete odeslat Vaše dotazy, připomínky, chyby, atd., nanásledující e-mail:\n" +" pablomileti@gmail.com" + +#: FrmAbout.form:28 +msgid "License" +msgstr "Licence" + +#: FrmAbout.form:32 +msgid "" +"\n" +" Copyright (C) 2010. Author: Pablo Mileti \n" +"\n" +"This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License along with this program. If not, see ." +msgstr "-" + +#: FrmAbout.form:42 +msgid "" +"Aim
\n" +"The shrimp has to push the movable boxes to the marked positions.

\n" +"Move
\n" +"To move around the game board, use the arrow keys: up, down, left and right.

\n" +"Clear
\n" +"To restart the level, press the spacebar key." +msgstr "" +"Aim
\n" +"krevety musí tlačit pohyblivé boxy označených místech.

\n" +"Pohyb
\n" +"Pro pohyb na hrací ploše, pomocí kláves se šipkami: nahoru,dolů, doleva a doprava.

\n" +"Vymazat
\n" +"Chcete-li restartovat úroveň, stiskněte klávesu mezerník." + +#: FrmAbout.form:50 +msgid "&Close" +msgstr "&Zavřít" diff --git a/app/examples/Games/GNUBoxWorld/.lang/de.mo b/app/examples/Games/GNUBoxWorld/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..02b8b649ece2470b8366f7c07794592f2fd2a507 GIT binary patch literal 3748 zcmeHKO^hQ)6)s@IkNF9N@N>xF%}S6&_-BVmC~{^e+cV>tG1#8L_UvkMtL}bwH`Co^ zs;lkwULwvMffJDsVuf-*++aC`lO-ZWij*r70&xKpB+eW_@V#z3GrOQYA#q7-sy|nC z)vNFQ)O$6*f9l!~6rL}lJ%jc;v@fB(_5r-`-1wkU-$&a<`!M>yxu(=VfNukz0KSFS zM}hAEKMDLTQ0Dy^_yG7fApX=(G5B%d&w$&&Ujd&6z6*pX^)Fxzc{z z>$iaTQ_tch^IAYqQf;8DzX|*aa1Zz);GcjBI=l<~9wz=3BxV0s@p=a1-vx@?*RhH4 z|2gnU-~*tHzX^OD`1bny9LkAFUIo4Y{LZ@n3!uz@0K`=F>vjJv;HS`k2Po_Q0ECL_ zk3iYyFF@h_cc4)HC-8^BC$LEL`Y}+}iLUZ|3{B>LS~^!AvEk#cs(PIg`e|GLlCelD{mc zAIk2d_3m_NAXU5AWOd>gvMtTo=IGN4n+?{|>I62=X(xm&nVBql9xRuXXb{@0YyPLg zmwJU1XC;fs%%DaDX;5qtCkgpXMb48VsS_+fce>-_ld~~-gLAs$4Ts)feEt$K> zbFSpX=`4w%9876-=9a>_-x=;7W4^c7?RCfJLi?~g9&|<{Iy@PYN2lI!+}%Ixc|$rq z8=jtwI!&Sx%p$gZj5#NzJ1kSkPRB_ORnOtP9F&rfB0Xm~G2nO(LYe}E$i{?^5vmiD zO-seF{bitlQFlU_adpbUY$tMV)^4>Hi$!yq6-{HOEqGeIoVRwH>dX6y$yx3AyI!2` zwEW#2-|iyv5NMHwk-#n|XAll~RH~VpIl`35H+Q-GBD$yAUV!_|AP&C(AK-#VqK8mwB6tS)D?&K%gZ&DZ`t}6ZFs~`w44% zbr;LI)7F*J61W}VF~b-ri*7R=u&=G`&{Up}(5T+cCc1ExoUOdD{IaSJ=NcY9qa^_n zHN~596!XGQ5tn74f4cw!F$Nq_OsDU4{9=kBc0pb2Dr_buNz5XKH#?0!;)8S;3e|RP zM^j{Za`SqMVQ1Pi$q;FoAZ|@%tQnZ}`Y!#~@tv05Ri#}N!f4Ojy<=<=Uj0oC}IzfSYGLERGHrpxf;=WHG!=2)6C33%TgI1pid?LrclNy%5vKNS|t$X z)JOzKiV}f&Ea3?jFri82+@y+e$ay@)Dw*c=1@geenv_Iv*HxQT=hTONX%?A`^QJoI zT%B6;Ee>3x8#ZoNISz5)?SI2N>d@`ZaMV2+)J{(tL!QTy02>ELYi&C3c*92jq;cTU z@kzVi{mLsRgHEldbJrLn<>auqB>ls)UXKP|ze78nzSr&D@(vD$NIAQhnoSEm<;IwG z+NPd2I69LpcY3{BNh~M%a!#9S{cgW=$#1)Pqb7#p4BIVd;N#$c7CaMYFHsO_o3p!l zHa={;csWmKP1rU%S%Bh^P22RMAG_LPE7$vl>;1y@e<@s}AVLLS{fINEUa!QQY|{S$ zE)}DydZh(2ei);^@Pel(d=bt)jy&GFn3P|ncExOQQs|HqxhdsA6tmCSLj`6}1aP+5 zqt-{B53%NS!br_H)%bmEf~Zcz0>{qQahb|4a=Cobkb}tO6$gi6nUyQo6iXhQ(r5-1 zCSdT;wgWE8I`-baXDNj!$yH9vxS)HeR8r{}1+vFbM7U6BS8oJJa!s{*&1YM^rMm6m zu1F!)h%Ibc-;9;=_UJ*rtZoMdUFa;sxqtJ|tGUC4D~NCr&Y0FE0xrfS zT1wdJzC~=5`{rf2S=X^n=Aza8#7P}1-C5N!reyWY9G8\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "GNUBoxWorld" +msgstr "-" + +#: .project:2 +msgid "This is another version of the popular game Box World. Contains 16 levels, all are possible to resolve. Level designs are taken from Box It game for mobile phones." +msgstr "Dies ist noch eine Version des populären Spiels Box World. Enthält 16 Levels, alle lösbar. Die Level-Designs stammen von dem Spiel \"Box It\" für Handys." + +#: FMain.class:81 +msgid "GNUBoxWorld - Congratulation! You're very clever!" +msgstr "GNUBoxWorld - Gratulation! Du bist verdammt schlau!" + +#: FMain.class:83 +msgid "Good Luck!" +msgstr "Viel Glück!" + +#: FMain.class:83 +msgid "Next level" +msgstr "Nächstes Level" + +#: FMain.class:222 +msgid "GNUBoxWorld - Level " +msgstr "-" + +#: FMain.class:254 +msgid "Are you sure?" +msgstr "Bist du sicher?" + +#: FMain.class:254 +msgid "No" +msgstr "Nein" + +#: FMain.class:254 +msgid "Yes" +msgstr "Ja" + +#: FMain.form:18 +msgid "Game" +msgstr "Spiel" + +#: FMain.form:21 +msgid "Clear" +msgstr "Neustart" + +#: FMain.form:26 +msgid "Quit" +msgstr "Beenden" + +#: FMain.form:32 +msgid "Level" +msgstr "-" + +#: FMain.form:36 +msgid "Help" +msgstr "Hilfe" + +#: FMain.form:39 FrmAbout.form:38 +msgid "How to play?" +msgstr "Spielanleitung" + +#: FMain.form:44 +msgid "About" +msgstr "Über" + +#: FrmAbout.form:13 +msgid "About..." +msgstr "Über..." + +#: FrmAbout.form:20 +msgid "Author" +msgstr "Autor" + +#: FrmAbout.form:24 +msgid "Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n pablomileti@gmail.com" +msgstr "Autor: Pablo Mileti.

Du kannst Fragen, Vorschläge, Bugs, etc, an diese E-Mail-Adresse senden:\n pablomileti@gmail.com" + +#: FrmAbout.form:28 +msgid "License" +msgstr "Lizenz" + +#: FrmAbout.form:32 +msgid "\n Copyright (C) 2010. Author: Pablo Mileti \n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see ." +msgstr "\n Copyright (C) 2010. Autor: Pablo Mileti \n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see ." + +#: FrmAbout.form:42 +msgid "Aim
\nThe shrimp has to push the movable boxes to the marked positions.

\nMove
\nTo move around the game board, use the arrow keys: up, down, left and right.

\nClear
\nTo restart the level, press the spacebar key." +msgstr "Ziel
Der Shrimp muss die beweglichen Boxen auf die markierten Felder schieben.

\nBewegen
\nMit den Pfeiltasten Hoch, Runter, Links und Rechts bewegt man den Shrimp auf dem Spielfeld.

\nNeustart
\nUm das Level neu zu starten, Leertaste drücken." + +#: FrmAbout.form:50 +msgid "&Close" +msgstr "&Schließen" + diff --git a/app/examples/Games/GNUBoxWorld/.lang/es_AR.mo b/app/examples/Games/GNUBoxWorld/.lang/es_AR.mo new file mode 100644 index 0000000000000000000000000000000000000000..9a9164712c4c881a2ee5cf2ce94d2f745d9fbf60 GIT binary patch literal 2898 zcma)8zmp?J6>h+QEihmokO0M72j*O?w6{=zOJ2)6r`7Jt=$9q!U9HPW&&-q5ccbZX zcaL-e0wRipDhEJ9LKP7qbpeD_ZA!!+;J*O^a=tgxde;ZIn7Y=t-P8Tv>+ikq&3*Xt zbH7n|zK-!tjK5)g3*%>>!VjK*0Dt{yrTz*09Ol1xPN_cte+B$3@DM-G1Ahd35qJ!g zeFNa1ffqphQ!iri3&1`dEhU1??2eR{~b`` z_&xA5z!Plx7w{Y?p|=o}oI3{oUDknO?;9^{@$L?P0F?Od1OF=ffO75y1S57{0@i_d zfD&&6l>HBYvhTg!JOzsVM?m3Y4g50j=ezeG0RMvdZ-Jmw4MB$3mos8VYzhxw!jNClMP*o6*QAk-GfRDwaWF)++H_$&70%9`UK0MCIcD;97S`HvlQy=bMCar3^OPNh1yf+R^kiq%vN6&yztGvQZjiJ#ljZ2#sb!Gv9-xEikV8!vohN!FhCEx)AQlQl%l~UJ&eZVXfVBe z13N)6-tvmKcFeNK3@!&MozBBXV(xdw?Q`sp?st3L>7|5y+MNzMlL?&;#}v^h8c)0J zi(WLQ(ZzT)oOBvQ6VM{uegd7dDjkj~<)BUGan(yC&m*KPrG;KGl9;erAs|f&O5~ct zPcW)8o6oC@!Tn94AfoP!avS!^BeJ7K2u1VYV7*>9=6Tt$ZhnAFOP2jXtD#)c$dF)}aTT@uWK()JDVlm{&&HQ~d-@r%9Kc zXk70P>n9PN51ak&ci$ZjI<=nmp*}@J@E}@R^65pdM}w%}p`%Ve>h_MKlanzTLJM2- zd8y}IpR!&ysTU2-F2vk)r1|+;bT)EhycnDkOh;!LCHcp z&*9CB>1qA-n|%_?j9tBxC+O$-yh*ReCe-j{e6`JJU2Z2`vox@4{YTmwX{Rda!Ug5yL0?^1(ql$fihInsgeibe)w{mAZ<`N{jGVV7#! zU?X-d^vC4gLt8bs1YQN<*yJX`GCo9B>(Kk^G5Nbl2gdk>3lh`KxpW^@D+TW3el-|-mG=Iv-jR~MdihYkdkPIzLd^Yj9x6kT* zP!X%%mb|J-$u97oiZ-~dYF|BFT4a2#L0TnuTpPN%diuoR#Wp2sBBZJg+A)$rHXte0 z7hTFAm7MJazpb2S6c4so6kpk5P#SD%J^r(ji|9r3Rb}l&?;6y}z^&)2KULL;Z_xEo lnw8QYmMDeF?TtJ1fkKKpaCY^_nayz~;Y^y4w1SCI{{|-ERhs|+ literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/.lang/es_AR.po b/app/examples/Games/GNUBoxWorld/.lang/es_AR.po new file mode 100644 index 00000000..d960567c --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.lang/es_AR.po @@ -0,0 +1,113 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "GNUBoxWorld" +msgstr "-" + +#: .project:2 +msgid "This is another version of the popular game Box World. Contains 16 levels, all are possible to resolve. Level designs are taken from Box It game for mobile phones." +msgstr "Esta es otra versión del popular juego Box World. Contiene 16 niveles, todos son posibles de resolver. El diseño de los niveles fue tomado del juego Box It para teléfonos celulares." + +#: FMain.class:59 +msgid "GNUBoxWorld - Congratulation! You're very clever!" +msgstr "GNUBoxWorld - Felicitaciones! Eres muy listo!" + +#: FMain.class:61 +msgid "Good Luck!" +msgstr "Suerte!" + +#: FMain.class:61 +msgid "Next level" +msgstr "Próximo nivel " + +#: FMain.class:197 +msgid "GNUBoxWorld - Level " +msgstr "GNUBoxWorld - Nivel " + +#: FMain.class:224 +msgid "Are you sure?" +msgstr "Desea salir?" + +#: FMain.class:224 +msgid "Yes" +msgstr "Si" + +#: FMain.class:258 +msgid "Game" +msgstr "Juego" + +#: FMain.class:261 +msgid "Clear" +msgstr "Limpiar" + +#: FMain.class:266 +msgid "Quit" +msgstr "Salir" + +#: FMain.class:272 +msgid "Help" +msgstr "Ayuda" + +#: FMain.class:275 FrmAbout.class:61 +msgid "How to play?" +msgstr "Cómo jugar?" + +#: FMain.class:280 +msgid "About" +msgstr "Acerca de..." + +#: FrmAbout.class:36 +msgid "About..." +msgstr "Acerca de..." + +#: FrmAbout.class:43 +msgid "Author" +msgstr "Autor" + +#: FrmAbout.class:47 +msgid "" +"Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n" +" pablomileti@gmail.com" +msgstr "Autor: Pablo Mileti.

Pueden enviar sus dudas, sugerencias, bugs, etc, a la siguiente dirección de correo electrónico: pablomileti@gmail.com" + +#: FrmAbout.class:51 +msgid "License" +msgstr "Licencia" + +#: FrmAbout.class:55 +msgid "" +"\n" +" Copyright (C) 2010. Author: Pablo Mileti \n" +"\n" +"This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License along with this program. If not, see ." +msgstr "-" + +#: FrmAbout.class:65 +msgid "" +"Move
\n" +"To move around the game board, use the arrow keys: up, down, left and right.

\n" +"Clear
\n" +"To restart the level, press the spacebar key." +msgstr "" +"Movimientos
\n" +"Para moverse por el tablero de juego utilice las teclas de dirección arriba, abajo, izquierda y derecha.

\n" +"Limpiar
\n" +"Para reiniciar un nivel presione la barra espaciadora." + +#: FrmAbout.class:73 +msgid "&Close" +msgstr "&Cerrar" diff --git a/app/examples/Games/GNUBoxWorld/.project b/app/examples/Games/GNUBoxWorld/.project new file mode 100644 index 00000000..8fdb90f7 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.project @@ -0,0 +1,19 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=GNUBoxWorld +Startup=FMain +Icon=icon.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.form +Description="This is another version of the popular game Box World.\n\nIt contains 16 levels, all are possible to resolve. Level designs are taken from the Box It game for mobile phones." +Authors="Pablo Mileti from Buenos Aires, Argentina." +TabSize=2 +Translate=1 +Language=en +Vendor=Example +Packager=1 +Tags=Example,Game,LogicGame +Screenshot=.hidden/screenshots/2014-12-14.png +CreateMenu=1 diff --git a/app/examples/Games/GNUBoxWorld/.src/Cell.class b/app/examples/Games/GNUBoxWorld/.src/Cell.class new file mode 100644 index 00000000..f3f2f333 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.src/Cell.class @@ -0,0 +1,78 @@ +' Gambas class file + +' Copyright(C)2010. Autor: Pablo Mileti + +'This program Is free software: you can redistribute it And / Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option)any later version. + +'This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.See the GNU General Public License For more details. + +'You should have received a Copy Of the GNU General Public License along With this program.IfNot, see < http: / / www.gnu.org / licenses / > . + +Public Const gbFloor As Integer = 0 +Public Const gbMovable As Integer = 1 +Public Const gbMovableOnTarget As Integer = 2 +Public Const gbTarget As Integer = 3 +Public Const gbObstacle As Integer = 4 + +Public Busy As Boolean +Public Movable As Boolean +Public Target As Boolean +Public Pic As Picture +Public Type As Integer + +Public Sub _new(type As Integer) + Select Case type + + Case 0 + Me.Type = Me.gbFloor + Me.Busy = False + Me.Movable = False + Me.Pic = Picture["piso.png"] + Me.Target = False + + Case 1 + Me.Type = Me.gbMovable + Me.Busy = True + Me.Movable = True + Me.Pic = Picture["movible.png"] + Me.Target = False + + Case 2 + Me.Type = Me.gbMovableOnTarget + Me.Busy = True + Me.Movable = True + Me.Pic = Picture["movibleendestino.png"] + Me.Target = True + + Case 3 + Me.Type = Me.gbTarget + Me.Busy = False + Me.Movable = False + Me.Pic = Picture["destino.png"] + Me.Target = True + + Case 4 + Me.Type = Me.gbObstacle + Me.Busy = True + Me.Movable = False + Me.Pic = Picture["obstaculo.png"] + Me.Target = False + +End Select + +End + +Public Sub FixObstaculo(hLeft As Cell, hRight As Cell) + + Dim sSuffix As String + + If Me.Type <> gbObstacle Then Return + + If Not hLeft Or If hLeft.Type <> gbObstacle Then sSuffix &= "l" + If Not hRight Or If hRight.Type <> gbObstacle Then sSuffix &= "r" + + If Not sSuffix Then Return + + Pic = Picture["obstaculo-" & sSuffix & ".png"] + +End diff --git a/app/examples/Games/GNUBoxWorld/.src/FMain.class b/app/examples/Games/GNUBoxWorld/.src/FMain.class new file mode 100644 index 00000000..62896ebd --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.src/FMain.class @@ -0,0 +1,274 @@ +' Gambas class file + +' +' GNUBoxWorld +' This is another version of the popular game Box World. Contains 16 levels, all are possible to resolve. +' Level designs are taken From Box It game For mobile phones. +' +' Copyright (C) Pablo Mileti from Buenos Aires, Argentina. +' +' This program is free software; you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation; either version 3 of the License, or +' (at your option) any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY; without even the implied warranty of +' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +' GNU General Public License for more details. +' +' You should have received a copy of the GNU General Public License +' along with this program; if not, see http://www.gnu.org/licenses +' + +Public Game As GameBoard +Public level As Integer +Public PicturesBoxes As New PictureBox[10, 11] +Public PictureBoxPlayer As PictureBox +Public PictureBoxMover As PictureBox +Public DesX As Integer +Public DesY As Integer +Public XPosMove As Integer +Public YPosMove As Integer +Public IgnoreKeys As Boolean + +Public Sub Form_Open() + + Dim iInd As Integer + Dim hMenu As Menu + + For iInd = 1 To GameBoard.NUM_LEVELS + hMenu = New Menu(mnuLevel) As "mnuLevel" + hMenu.Text = Subst("Level &1", iInd) + hMenu.Tag = iInd + Next + + level = 1 + ignorekeys = False + newGame + Me.h = 64 * 10 + Me.w = 64 * 11 + btnClear.X = (Me.W / 2) - (btnClear.W / 2) + Me.Window.Center +End + +Public Sub btnClear_KeyPress() + If Key.code <> Key.up And Key.code <> Key.down And Key.code <> Key.left And Key.code <> Key.Right Then Return + Select Case Key.code + Case Key.Right + PictureBoxPlayer.Picture = picture["derecha.png"] + Case Key.Left + PictureBoxPlayer.Picture = picture["izquierda.png"] + Case Key.Up + PictureBoxPlayer.Picture = picture["arriba.png"] + Case Key.Down + PictureBoxPlayer.Picture = picture["abajo.png"] + End Select + If Game.ValidateMover(Key.code) And Not IgnoreKeys Then + Move(Key.code, Game.GetCell(Key.code, 1).Movable) + End If + If Game.isDone() Then + Inc level + If level > GameBoard.NUM_LEVELS Then + btnClear.Visible = False + MnuClear.Enabled = False + PictureBoxPlayer.X = 64 + PictureBoxPlayer.Y = 64 + PictureBoxPlayer.W = 560 + PictureBoxPlayer.H = 482 + PictureBoxPlayer.Picture = Picture["ganador.png"] + PictureBoxPlayer.SetFocus + Me.Title = ("GNUBoxWorld - Congratulation! You're very clever!") + Else + Message.Info(("Next level") & gb.NewLine & ("Good Luck!")) + newGame + End If + End If +End + +Public Sub btnClear_Click() + newGame() +End + +Private Sub Move(direction As Integer, desplace As Boolean) + Dim i As Integer + Select Case direction + Case Key.Right + DesX = 1 + DesY = 0 + PictureBoxPlayer.Picture = picture["derecha.png"] + Case Key.Left + DesX = -1 + DesY = 0 + PictureBoxPlayer.Picture = picture["izquierda.png"] + Case Key.Up + DesX = 0 + DesY = -1 + PictureBoxPlayer.Picture = picture["arriba.png"] + Case Key.Down + DesX = 0 + DesY = 1 + PictureBoxPlayer.Picture = picture["abajo.png"] + End Select + If desplace Then + PictureBoxMover.Picture = WhoMove(direction) + PictureBoxMover.X = XPosMove + PictureBoxMover.Y = YPosMove + PictureBoxMover.Raise + 'here will walk on + If Game.cells[Game.RowPlayer + DesY, Game.ColPlayer + DesX].Type = Cell.gbMovable Then + PicturesBoxes[Game.RowPlayer + DesY, Game.ColPlayer + DesX].picture = Picture["piso.png"] + End If + If Game.cells[Game.RowPlayer + DesY, Game.ColPlayer + DesX].Type = Cell.gbMovableOnTarget Then + PicturesBoxes[Game.RowPlayer + DesY, Game.ColPlayer + DesX].picture = Picture["destino.png"] + End If + End If + IgnoreKeys = True + For i = 1 To 64 + PictureBoxPlayer.Move(PictureBoxPlayer.x + Desx, PictureBoxPlayer.y + DesY) + If desplace Then PictureBoxmover.Move(PictureBoxmover.x + Desx, PictureBoxmover.y + DesY) + Wait 0.003 + Next + IgnoreKeys = False + 'here i was + 'cambio el contenido de la celda donde deje de estar + If Game.cells[Game.RowPlayer, Game.ColPlayer].Target Then + Game.cells[Game.RowPlayer, Game.ColPlayer] = New Cell(Cell.gbTarget) + Else + Game.cells[Game.RowPlayer, Game.ColPlayer] = New Cell(Cell.gbFloor) + End If + Game.RowPlayer = Game.RowPlayer + DesY + Game.ColPlayer = Game.ColPlayer + DesX + 'here i go + 'donde termino parado + If Not Game.cells[Game.RowPlayer, Game.ColPlayer].Target Then + Game.cells[Game.RowPlayer, Game.ColPlayer] = New Cell(Cell.gbFloor) + Else + Game.cells[Game.RowPlayer, Game.ColPlayer] = New Cell(Cell.gbTarget) + End If + If desplace Then 'here move obstacle + If Game.cells[Game.RowPlayer + desy, Game.ColPlayer + desX].Target Then 'go on target + Game.cells[Game.RowPlayer + desy, Game.ColPlayer + desX] = New Cell(Cell.gbMovableOnTarget) + Else 'go on floor + Game.cells[Game.RowPlayer + desy, Game.ColPlayer + desX] = New Cell(Cell.gbMovable) + End If + End If + ShowGameBoard + PictureBoxmover.Lower +End + +Private Function WhoMove(direction As Integer) As Picture + Dim row As Integer + Dim col As Integer + Select Case direction + Case Key.Up + row = Game.RowPlayer - 1 + col = Game.ColPlayer + Case Key.Down + row = Game.RowPlayer + 1 + col = Game.ColPlayer + Case Key.left + row = Game.RowPlayer + col = Game.ColPlayer - 1 + Case Key.right + row = Game.RowPlayer + col = Game.ColPlayer + 1 + End Select + XPosMove = PicturesBoxes[row, col].X + YPosMove = PicturesBoxes[row, col].Y + Return PicturesBoxes[row, col].Picture.Copy() +End + +Public Sub ShowGameBoard() + + Dim row As Integer + Dim col As Integer + For row = 0 To 9 + For col = 0 To 10 + PicturesBoxes[row, col].Picture = Game.Cells[row, col].Pic + Next + Next + +End + +Public Sub MakePictureBoxes() + Dim row As Integer + Dim col As Integer + For row = 0 To 9 + For col = 0 To 10 + PicturesBoxes[row, col] = New PictureBox(Me) + PicturesBoxes[row, col].h = 64 + PicturesBoxes[row, col].w = 64 + PicturesBoxes[row, col].Stretch = True + PicturesBoxes[row, col].top = (row * 64) + PicturesBoxes[row, col].left = (col * 64) + PicturesBoxes[row, col].Lower + Next + Next + PictureBoxPlayer = New PictureBox(Me) + PictureBoxMover = New PictureBox(Me) + PictureBoxmover.w = 64 + PictureBoxmover.h = 64 + PictureBoxPlayer.Picture = picture["derecha.png"] + PictureBoxPlayer.h = 64 + PictureBoxPlayer.w = 64 + PictureBoxPlayer.left = PicturesBoxes[Game.RowPlayer, Game.ColPlayer].left + PictureBoxPlayer.top = PicturesBoxes[Game.RowPlayer, Game.ColPlayer].top +End + +Private Sub newGame() + + Dim hMenu As Menu + + Me.Title = ("GNUBoxWorld - Level ") & level + Game = New GameBoard(level) + destroyPicturesBoxes + MakePictureBoxes + PictureBoxPlayer.left = PicturesBoxes[Game.RowPlayer, Game.ColPlayer].left + PictureBoxPlayer.top = PicturesBoxes[Game.RowPlayer, Game.ColPlayer].top + ShowGameBoard + + For Each hMenu In mnuLevel.Children + hMenu.Checked = hMenu.Tag = level + Next + +End + +Public Sub destroyPicturesBoxes() + Dim control As Object + For Each control In Me.Children + If Object.Type(control) = "PictureBox" Then + control.delete + End If + Next +End + +Public Sub MnuAbout_Click() + FrmAbout.Show +End + +Public Sub MnuQuit_Click() + Me.close +End + +Public Sub Form_Close() + If Message.Question(("Are you sure?"), ("Yes"), ("No")) = 2 Then + Stop Event + End If +End + +Public Sub MnuClear_Click() + newGame() +End + +Public Sub MnuHelpPlay_Click() + FrmAbout.TabStrip1.index = 2 + FrmAbout.Show +End + +Public Sub mnuLevel_Click() + + level = Last.Tag + newGame + +End diff --git a/app/examples/Games/GNUBoxWorld/.src/FMain.form b/app/examples/Games/GNUBoxWorld/.src/FMain.form new file mode 100644 index 00000000..4b64be77 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.src/FMain.form @@ -0,0 +1,37 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,42,16) + Icon = Picture["icon.png"] + Resizable = False + { MnuGame Menu + Text = ("Game") + { MnuClear Menu + Text = ("Clear") + Picture = Picture["icon:/32/clear"] + } + { MnuQuit Menu + Text = ("Quit") + Picture = Picture["icon:/32/quit"] + } + } + { mnuLevel Menu + Text = ("Level") + } + { MnuHelp Menu + Text = ("Help") + { MnuHelpPlay Menu + Text = ("How to play?") + Picture = Picture["icon:/32/help"] + } + { MnuAbout Menu + Text = ("About") + Picture = Picture["icon:/32/info"] + } + } + { btnClear Button + MoveScaled(15,2,16,6) + Font = Font["Bold,+4"] + Text = ("Clear") + } +} diff --git a/app/examples/Games/GNUBoxWorld/.src/FrmAbout.class b/app/examples/Games/GNUBoxWorld/.src/FrmAbout.class new file mode 100644 index 00000000..33a4a0fb --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.src/FrmAbout.class @@ -0,0 +1,19 @@ +' Gambas class file + +' Copyright(C)2010. Autor: Pablo Mileti + +'This program Is free software: you can redistribute it And / Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option)any later version. + +'This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.See the GNU General Public License For more details. + +'You should have received a Copy Of the GNU General Public License along With this program.IfNot, see < http: / / www.gnu.org / licenses / > . + + +Public Sub Form_Open() + Me.Window.Center + TextArea1.Pos = 0 +End + +Public Sub Button1_Click() + Me.Close +End diff --git a/app/examples/Games/GNUBoxWorld/.src/FrmAbout.form b/app/examples/Games/GNUBoxWorld/.src/FrmAbout.form new file mode 100644 index 00000000..165e8850 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.src/FrmAbout.form @@ -0,0 +1,45 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,79,39) + Text = ("About...") + Resizable = False + { !TabStrip1 TabStrip + MoveScaled(2,2,50,35) + #Public = True + Count = 3 + Index = 0 + Text = ("Author") + { TextLabel1 TextLabel + MoveScaled(2,2,45,21) + Text = ("Author: Pablo Mileti.

You can submit your questions, suggestions, bugs, etc, to the following E-Mail address:\n pablomileti@gmail.com") + Alignment = Align.Justify + } + Index = 1 + Text = ("License") + { TextArea1 TextArea + MoveScaled(1,1,46,25) + Text = ("\n Copyright (C) 2010. Author: Pablo Mileti \n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see .") + ReadOnly = True + Wrap = True + ScrollBar = Scroll.Vertical + } + Index = 2 + Text = ("How to play?") + { TextLabel2 TextLabel + MoveScaled(2,1,45,27) + Text = ("Aim
\nThe shrimp has to push the movable boxes to the marked positions.

\nMove
\nTo move around the game board, use the arrow keys: up, down, left and right.

\nClear
\nTo restart the level, press the spacebar key.") + Alignment = Align.Justify + } + Index = 0 + } + { Button1 Button + MoveScaled(59,30,14,6) + Text = ("&Close") + } + { PictureBox1 PictureBox + MoveScaled(54,5,22.7143,22.7143) + Picture = Picture["logo.png"] + Stretch = True + } +} diff --git a/app/examples/Games/GNUBoxWorld/.src/GameBoard.class b/app/examples/Games/GNUBoxWorld/.src/GameBoard.class new file mode 100644 index 00000000..09212783 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/.src/GameBoard.class @@ -0,0 +1,595 @@ +' Gambas class file + +' Copyright(C)2010. Autor: Pablo Mileti + +'This program Is free software: you can redistribute it And / Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option)any later version. + +'This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.See the GNU General Public License For more details. + +'You should have received a Copy Of the GNU General Public License along With this program.IfNot, see < http: / / www.gnu.org / licenses / > . + +Public Const NUM_LEVELS As Integer = 16 + +Public RowPlayer As Integer +Public ColPlayer As Integer +Public Cells As New Cell[10, 11] + +'See below constructor + +'This Function return true if player can move or false if exist obstacle +Public Function ValidateMover(Direction As Integer) As Boolean + Select Case Me.GetCell(Direction, 1).Type + Case Cell.gbMovableOnTarget + If Me.GetCell(Direction, 2).Busy Then + Return False + Else + Return True + End If + Case Cell.gbMovable + If Me.GetCell(Direction, 2).Busy Then + Return False + Else + Return True + End If + Case Cell.gbFloor + Return True + Case Cell.gbObstacle + Return False + Case Cell.gbTarget + Return True + End Select +End + +'This function return an object Cell if distance = 1 then Cell by side, distance = 2 Cell away 2 cells +Public Function GetCell(Where As Integer, distance As Integer) As Cell +Select Case Where + Case Key.up + Return Cells[Me.rowplayer - distance, Me.colplayer] + Case Key.down + Return Cells[Me.rowplayer + distance, Me.colplayer] + Case Key.left + Return Cells[Me.rowplayer, Me.colplayer - distance] + Case Key.right + Return Cells[Me.rowplayer, Me.colplayer + distance] +End Select +End + +'This function return true if all target are busy (Winner!) +Public Function isDone() As Boolean + Dim obj As Object + For Each obj In Me.cells + If obj.type = Cell.gbTarget Then Return False + Next + Return True +End + + +Public Sub _new(level As Integer) + Dim row As Integer + Dim col As Integer + 'All cells type Floor + For row = 0 To 9 + For col = 0 To 10 + Me.Cells[row, col] = New Cell(Cell.gbFloor) + Next + Next + 'Construct levels + Select Case level + 'LEVEL 1 + Case 1 + For col = 2 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 5 + Me.Cells[row, 2] = New Cell(Cell.gbObstacle) + Next + For col = 2 To 8 + Me.Cells[5, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 5 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 3] = New Cell(Cell.gbTarget) + Me.Cells[2, 7] = New Cell(Cell.gbTarget) + Me.Cells[4, 3] = New Cell(Cell.gbTarget) + Me.Cells[4, 7] = New Cell(Cell.gbTarget) + Me.Cells[2, 5] = New Cell(Cell.gbMovable) + Me.Cells[3, 4] = New Cell(Cell.gbMovable) + Me.Cells[3, 6] = New Cell(Cell.gbMovable) + Me.Cells[4, 5] = New Cell(Cell.gbMovable) + RowPlayer = 3 + ColPlayer = 5 + 'LEVEL 2 + Case 2 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 5 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[5, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 5 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 4] = New Cell(Cell.gbTarget) + Me.Cells[2, 5] = New Cell(Cell.gbTarget) + Me.Cells[4, 4] = New Cell(Cell.gbTarget) + Me.Cells[4, 5] = New Cell(Cell.gbTarget) + Me.Cells[2, 6] = New Cell(Cell.gbMovable) + Me.Cells[3, 6] = New Cell(Cell.gbMovable) + Me.Cells[3, 3] = New Cell(Cell.gbMovable) + Me.Cells[4, 3] = New Cell(Cell.gbMovable) + RowPlayer = 3 + ColPlayer = 5 + 'LEVEL 3 + Case 3 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 2] = New Cell(Cell.gbObstacle) + Me.Cells[3, 2] = New Cell(Cell.gbObstacle) + Me.Cells[6, 2] = New Cell(Cell.gbObstacle) + Me.Cells[3, 4] = New Cell(Cell.gbTarget) + Me.Cells[3, 6] = New Cell(Cell.gbTarget) + Me.Cells[5, 4] = New Cell(Cell.gbTarget) + Me.Cells[5, 6] = New Cell(Cell.gbTarget) + Me.Cells[3, 5] = New Cell(Cell.gbMovable) + Me.Cells[4, 4] = New Cell(Cell.gbMovable) + Me.Cells[4, 6] = New Cell(Cell.gbMovable) + Me.Cells[5, 5] = New Cell(Cell.gbMovable) + RowPlayer = 5 + ColPlayer = 2 + 'LEVEL 4 + Case 4 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 8 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[8, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 8 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 2] = New Cell(Cell.gbObstacle) + Me.Cells[3, 4] = New Cell(Cell.gbObstacle) + Me.Cells[4, 3] = New Cell(Cell.gbObstacle) + Me.Cells[4, 6] = New Cell(Cell.gbObstacle) + Me.Cells[5, 6] = New Cell(Cell.gbObstacle) + Me.Cells[6, 3] = New Cell(Cell.gbObstacle) + Me.Cells[7, 5] = New Cell(Cell.gbObstacle) + Me.Cells[7, 6] = New Cell(Cell.gbObstacle) + Me.Cells[7, 7] = New Cell(Cell.gbObstacle) + Me.Cells[5, 3] = New Cell(Cell.gbMovable) + Me.Cells[5, 5] = New Cell(Cell.gbMovable) + Me.Cells[6, 2] = New Cell(Cell.gbTarget) + Me.Cells[7, 2] = New Cell(Cell.gbTarget) + RowPlayer = 7 + ColPlayer = 4 + 'LEVEL 5 + Case 5 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 6 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[6, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 6 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 3] = New Cell(Cell.gbMovable) + Me.Cells[3, 4] = New Cell(Cell.gbMovable) + Me.Cells[4, 3] = New Cell(Cell.gbMovable) + Me.Cells[5, 4] = New Cell(Cell.gbMovable) + Me.Cells[2, 4] = New Cell(Cell.gbTarget) + Me.Cells[3, 3] = New Cell(Cell.gbTarget) + Me.Cells[4, 4] = New Cell(Cell.gbTarget) + Me.Cells[5, 3] = New Cell(Cell.gbTarget) + For row = 2 To 5 + Me.Cells[row, 6] = New Cell(Cell.gbMovable) + Next + For row = 2 To 5 + Me.Cells[row, 7] = New Cell(Cell.gbTarget) + Next + RowPlayer = 3 + ColPlayer = 2 + 'LEVEL 6 + Case 6 + For col = 1 To 9 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 9 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 9] = New Cell(Cell.gbObstacle) + Next + Me.Cells[3, 5] = New Cell(Cell.gbObstacle) + Me.Cells[2, 5] = New Cell(Cell.gbMovableOnTarget) + Me.Cells[3, 3] = New Cell(Cell.gbMovable) + Me.Cells[3, 7] = New Cell(Cell.gbMovable) + Me.Cells[4, 4] = New Cell(Cell.gbMovable) + Me.Cells[4, 6] = New Cell(Cell.gbMovable) + Me.Cells[5, 5] = New Cell(Cell.gbMovable) + Me.Cells[3, 4] = New Cell(Cell.gbTarget) + Me.Cells[3, 6] = New Cell(Cell.gbTarget) + Me.Cells[4, 5] = New Cell(Cell.gbTarget) + Me.Cells[5, 4] = New Cell(Cell.gbTarget) + Me.Cells[5, 6] = New Cell(Cell.gbTarget) + Me.Cells[5, 2] = New Cell(Cell.gbObstacle) + Me.Cells[5, 3] = New Cell(Cell.gbObstacle) + Me.Cells[5, 7] = New Cell(Cell.gbObstacle) + Me.Cells[5, 8] = New Cell(Cell.gbObstacle) + Me.Cells[6, 2] = New Cell(Cell.gbObstacle) + Me.Cells[6, 3] = New Cell(Cell.gbObstacle) + Me.Cells[6, 4] = New Cell(Cell.gbObstacle) + Me.Cells[6, 6] = New Cell(Cell.gbObstacle) + Me.Cells[6, 7] = New Cell(Cell.gbObstacle) + Me.Cells[6, 8] = New Cell(Cell.gbObstacle) + RowPlayer = 6 + ColPlayer = 5 + + 'LEVEL 7 + Case 7 + For col = 2 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 2] = New Cell(Cell.gbObstacle) + Next + For col = 2 To 8 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 3] = New Cell(Cell.gbObstacle) + Me.Cells[2, 4] = New Cell(Cell.gbObstacle) + Me.Cells[5, 3] = New Cell(Cell.gbObstacle) + Me.Cells[5, 4] = New Cell(Cell.gbObstacle) + Me.Cells[6, 3] = New Cell(Cell.gbObstacle) + Me.Cells[6, 4] = New Cell(Cell.gbObstacle) + Me.Cells[3, 6] = New Cell(Cell.gbMovable) + Me.Cells[4, 5] = New Cell(Cell.gbMovable) + Me.Cells[4, 7] = New Cell(Cell.gbMovable) + Me.Cells[5, 6] = New Cell(Cell.gbMovable) + Me.Cells[3, 5] = New Cell(Cell.gbTarget) + Me.Cells[3, 7] = New Cell(Cell.gbTarget) + Me.Cells[5, 5] = New Cell(Cell.gbTarget) + Me.Cells[5, 7] = New Cell(Cell.gbTarget) + RowPlayer = 3 + ColPlayer = 3 + + 'LEVEL 8 + Case 8 + For col = 1 To 9 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 9 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 9] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 8] = New Cell(Cell.gbObstacle) + Me.Cells[3, 8] = New Cell(Cell.gbObstacle) + Me.Cells[4, 2] = New Cell(Cell.gbObstacle) + Me.Cells[4, 3] = New Cell(Cell.gbObstacle) + Me.Cells[4, 6] = New Cell(Cell.gbObstacle) + Me.Cells[4, 3] = New Cell(Cell.gbObstacle) + Me.Cells[4, 6] = New Cell(Cell.gbObstacle) + Me.Cells[5, 2] = New Cell(Cell.gbObstacle) + Me.Cells[5, 3] = New Cell(Cell.gbObstacle) + Me.Cells[5, 4] = New Cell(Cell.gbObstacle) + Me.Cells[6, 2] = New Cell(Cell.gbObstacle) + Me.Cells[6, 3] = New Cell(Cell.gbObstacle) + Me.Cells[6, 4] = New Cell(Cell.gbObstacle) + Me.Cells[3, 4] = New Cell(Cell.gbTarget) + Me.Cells[3, 5] = New Cell(Cell.gbTarget) + Me.Cells[4, 4] = New Cell(Cell.gbTarget) + Me.Cells[4, 5] = New Cell(Cell.gbTarget) + Me.Cells[3, 3] = New Cell(Cell.gbMovable) + Me.Cells[5, 5] = New Cell(Cell.gbMovable) + Me.Cells[5, 6] = New Cell(Cell.gbMovable) + Me.Cells[5, 7] = New Cell(Cell.gbMovable) + RowPlayer = 2 + ColPlayer = 6 + + 'LEVEL 9 + Case 9 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 8 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[8, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 8 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + For row = 4 To 8 + Me.Cells[row, 2] = New Cell(Cell.gbObstacle) + Me.Cells[row, 7] = New Cell(Cell.gbObstacle) + Next + For row = 6 To 8 + Me.Cells[row, 3] = New Cell(Cell.gbObstacle) + Me.Cells[row, 6] = New Cell(Cell.gbObstacle) + Me.Cells[row, 7] = New Cell(Cell.gbObstacle) + Next + Me.Cells[3, 5] = New Cell(Cell.gbObstacle) + Me.Cells[5, 3] = New Cell(Cell.gbTarget) + Me.Cells[6, 4] = New Cell(Cell.gbTarget) + Me.Cells[7, 4] = New Cell(Cell.gbTarget) + Me.Cells[7, 5] = New Cell(Cell.gbTarget) + Me.Cells[3, 3] = New Cell(Cell.gbMovable) + Me.Cells[3, 4] = New Cell(Cell.gbMovable) + Me.Cells[4, 5] = New Cell(Cell.gbMovable) + Me.Cells[5, 4] = New Cell(Cell.gbMovable) + RowPlayer = 2 + ColPlayer = 5 + + 'LEVEL 10 + Case 10 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 8 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[8, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 8 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 4 + Me.Cells[row, 2] = New Cell(Cell.gbObstacle) + Me.Cells[row, 7] = New Cell(Cell.gbObstacle) + Next + Me.Cells[4, 3] = New Cell(Cell.gbObstacle) + Me.Cells[6, 3] = New Cell(Cell.gbObstacle) + Me.Cells[6, 5] = New Cell(Cell.gbObstacle) + Me.Cells[6, 6] = New Cell(Cell.gbObstacle) + Me.Cells[2, 3] = New Cell(Cell.gbTarget) + Me.Cells[2, 5] = New Cell(Cell.gbTarget) + Me.Cells[2, 6] = New Cell(Cell.gbTarget) + Me.Cells[3, 3] = New Cell(Cell.gbTarget) + Me.Cells[3, 6] = New Cell(Cell.gbTarget) + Me.Cells[3, 5] = New Cell(Cell.gbMovable) + Me.Cells[4, 6] = New Cell(Cell.gbMovable) + Me.Cells[5, 3] = New Cell(Cell.gbMovable) + Me.Cells[5, 6] = New Cell(Cell.gbMovable) + Me.Cells[6, 4] = New Cell(Cell.gbMovable) + RowPlayer = 7 + ColPlayer = 5 + + 'LEVEL 11 + Case 11 + For col = 2 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 2] = New Cell(Cell.gbObstacle) + Next + For col = 2 To 8 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 3] = New Cell(Cell.gbObstacle) + Me.Cells[6, 3] = New Cell(Cell.gbObstacle) + Me.Cells[6, 7] = New Cell(Cell.gbObstacle) + Me.Cells[2, 5] = New Cell(Cell.gbTarget) + Me.Cells[4, 3] = New Cell(Cell.gbTarget) + Me.Cells[4, 7] = New Cell(Cell.gbTarget) + Me.Cells[6, 5] = New Cell(Cell.gbTarget) + Me.Cells[3, 4] = New Cell(Cell.gbMovable) + Me.Cells[3, 6] = New Cell(Cell.gbMovable) + Me.Cells[5, 4] = New Cell(Cell.gbMovable) + Me.Cells[5, 6] = New Cell(Cell.gbMovable) + Me.Cells[4, 5] = New Cell(Cell.gbMovableOnTarget) + RowPlayer = 2 + ColPlayer = 7 + + 'LEVEL 12 + Case 12 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 2] = New Cell(Cell.gbObstacle) + Me.Cells[2, 3] = New Cell(Cell.gbObstacle) + Me.Cells[3, 2] = New Cell(Cell.gbObstacle) + Me.Cells[3, 3] = New Cell(Cell.gbObstacle) + Me.Cells[6, 2] = New Cell(Cell.gbObstacle) + Me.Cells[6, 3] = New Cell(Cell.gbObstacle) + Me.Cells[6, 4] = New Cell(Cell.gbObstacle) + Me.Cells[5, 7] = New Cell(Cell.gbObstacle) + Me.Cells[6, 7] = New Cell(Cell.gbObstacle) + Me.Cells[4, 5] = New Cell(Cell.gbTarget) + Me.Cells[4, 6] = New Cell(Cell.gbTarget) + Me.Cells[5, 4] = New Cell(Cell.gbTarget) + Me.Cells[5, 5] = New Cell(Cell.gbTarget) + Me.Cells[5, 6] = New Cell(Cell.gbTarget) + Me.Cells[5, 3] = New Cell(Cell.gbMovable) + Me.Cells[4, 4] = New Cell(Cell.gbMovable) + Me.Cells[3, 4] = New Cell(Cell.gbMovable) + Me.Cells[3, 5] = New Cell(Cell.gbMovable) + Me.Cells[3, 6] = New Cell(Cell.gbMovable) + RowPlayer = 5 + ColPlayer = 2 + +'LEVEL 13 + Case 13 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 4] = New Cell(Cell.gbObstacle) + Me.Cells[4, 7] = New Cell(Cell.gbObstacle) + Me.Cells[6, 4] = New Cell(Cell.gbObstacle) + Me.Cells[3, 4] = New Cell(Cell.gbTarget) + Me.Cells[3, 5] = New Cell(Cell.gbTarget) + Me.Cells[4, 4] = New Cell(Cell.gbTarget) + Me.Cells[5, 4] = New Cell(Cell.gbTarget) + Me.Cells[5, 5] = New Cell(Cell.gbTarget) + Me.Cells[3, 3] = New Cell(Cell.gbMovable) + Me.Cells[3, 6] = New Cell(Cell.gbMovable) + Me.Cells[4, 3] = New Cell(Cell.gbMovable) + Me.Cells[5, 3] = New Cell(Cell.gbMovable) + Me.Cells[5, 6] = New Cell(Cell.gbMovable) + Me.Cells[4, 5] = New Cell(Cell.gbMovableOnTarget) + RowPlayer = 4 + ColPlayer = 2 + +'LEVEL 14 + Case 14 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 2] = New Cell(Cell.gbObstacle) + Me.Cells[3, 2] = New Cell(Cell.gbObstacle) + Me.Cells[2, 6] = New Cell(Cell.gbObstacle) + Me.Cells[2, 7] = New Cell(Cell.gbObstacle) + Me.Cells[3, 4] = New Cell(Cell.gbObstacle) + Me.Cells[4, 6] = New Cell(Cell.gbObstacle) + Me.Cells[5, 3] = New Cell(Cell.gbObstacle) + Me.Cells[3, 7] = New Cell(Cell.gbTarget) + Me.Cells[4, 7] = New Cell(Cell.gbTarget) + Me.Cells[5, 7] = New Cell(Cell.gbTarget) + Me.Cells[6, 7] = New Cell(Cell.gbTarget) + Me.Cells[3, 3] = New Cell(Cell.gbMovable) + Me.Cells[4, 4] = New Cell(Cell.gbMovable) + Me.Cells[4, 5] = New Cell(Cell.gbMovable) + Me.Cells[5, 4] = New Cell(Cell.gbMovable) + RowPlayer = 3 + ColPlayer = 5 + +'LEVEL 15 + Case 15 + For col = 2 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 2] = New Cell(Cell.gbObstacle) + Next + For col = 2 To 8 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[3, 4] = New Cell(Cell.gbMovable) + Me.Cells[3, 5] = New Cell(Cell.gbMovable) + Me.Cells[3, 6] = New Cell(Cell.gbMovable) + Me.Cells[4, 4] = New Cell(Cell.gbMovable) + Me.Cells[4, 6] = New Cell(Cell.gbMovable) + Me.Cells[5, 4] = New Cell(Cell.gbMovable) + Me.Cells[5, 5] = New Cell(Cell.gbMovable) + Me.Cells[5, 6] = New Cell(Cell.gbMovable) + Me.Cells[2, 3] = New Cell(Cell.gbTarget) + Me.Cells[2, 5] = New Cell(Cell.gbTarget) + Me.Cells[2, 7] = New Cell(Cell.gbTarget) + Me.Cells[4, 3] = New Cell(Cell.gbTarget) + Me.Cells[4, 7] = New Cell(Cell.gbTarget) + Me.Cells[6, 3] = New Cell(Cell.gbTarget) + Me.Cells[6, 5] = New Cell(Cell.gbTarget) + Me.Cells[6, 7] = New Cell(Cell.gbTarget) + RowPlayer = 4 + ColPlayer = 5 + +'LEVEL 16 + Case 16 + For col = 1 To 8 + Me.Cells[1, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 1] = New Cell(Cell.gbObstacle) + Next + For col = 1 To 8 + Me.Cells[7, col] = New Cell(Cell.gbObstacle) + Next + For row = 2 To 7 + Me.Cells[row, 8] = New Cell(Cell.gbObstacle) + Next + Me.Cells[2, 2] = New Cell(Cell.gbObstacle) + Me.Cells[2, 7] = New Cell(Cell.gbObstacle) + Me.Cells[6, 2] = New Cell(Cell.gbObstacle) + Me.Cells[6, 3] = New Cell(Cell.gbObstacle) + Me.Cells[2, 7] = New Cell(Cell.gbObstacle) + Me.Cells[6, 6] = New Cell(Cell.gbObstacle) + Me.Cells[6, 7] = New Cell(Cell.gbObstacle) + For col = 2 To 7 + Me.Cells[4, col] = New Cell(Cell.gbTarget) + Next + Me.Cells[3, 3] = New Cell(Cell.gbMovable) + Me.Cells[3, 5] = New Cell(Cell.gbMovable) + Me.Cells[3, 6] = New Cell(Cell.gbMovable) + Me.Cells[5, 3] = New Cell(Cell.gbMovable) + Me.Cells[5, 4] = New Cell(Cell.gbMovable) + Me.Cells[5, 6] = New Cell(Cell.gbMovable) + RowPlayer = 6 + ColPlayer = 5 + + + End Select + + For row = 0 To 9 + Cells[row, 0].FixObstaculo(Null, Cells[row, 1]) + For col = 1 To 9 + Cells[row, col].FixObstaculo(Cells[row, col - 1], Cells[row, col + 1]) + Next + Cells[row, 10].FixObstaculo(Cells[row, 9], Null) + Next + +End diff --git a/app/examples/Games/GNUBoxWorld/License b/app/examples/Games/GNUBoxWorld/License new file mode 100644 index 00000000..b17f91b9 --- /dev/null +++ b/app/examples/Games/GNUBoxWorld/License @@ -0,0 +1,189 @@ +GNU GENERAL PUBLIC LICENSE + +Version 3, 29 June 2007 + +Copyright © 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. +Preamble + +The GNU General Public License is a free, copyleft license for software and other kinds of works. + +The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. +TERMS AND CONDITIONS +0. Definitions. + +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on the Program. + +To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. +1. Source Code. + +The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. + +The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. +2. Basic Permissions. + +All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. +3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. +4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. +5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: + + * a) The work must carry prominent notices stating that you modified it, and giving a relevant date. + * b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. + * c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. + * d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. +6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: + + * a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. + * b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. + * c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. + * d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. + * e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. + +A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. +7. Additional Terms. + +“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: + + * a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or + * b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or + * c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or + * d) Limiting the use for publicity purposes of names of licensors or authors of the material; or + * e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or + * f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. +8. Termination. + +You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. +9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. +10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. + +An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. +11. Patents. + +A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. + +In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. +12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. +13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. +14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. +15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. +16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/app/examples/Games/GNUBoxWorld/abajo.png b/app/examples/Games/GNUBoxWorld/abajo.png new file mode 100644 index 0000000000000000000000000000000000000000..7e716fbc7cee2fed0a7d82c7be5173276d44e352 GIT binary patch literal 5205 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 P00000NkvXXu0mjf8lo=L literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/arriba.png b/app/examples/Games/GNUBoxWorld/arriba.png new file mode 100644 index 0000000000000000000000000000000000000000..86e88f3f8767a36487fedd117e7556f1ffbeae16 GIT binary patch literal 5291 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/derecha.png b/app/examples/Games/GNUBoxWorld/derecha.png new file mode 100644 index 0000000000000000000000000000000000000000..4f37b4423076d694ffe46f4ca69bde88565c9e5d GIT binary patch literal 4347 zcmVS+9@M*aTb);lQtf&j4pFAtkl{PXg`(5&-QAxi-K{ zz+&JJbxg!1ooqMtW7*|{oq9lrUK=m9+gi4lKty93`%_>C?gIV{EC){V!?KZp2RHys z2U^w_v_!Ysl{9(2?DVg%^yF8UOJG>-qqLN_H%Q>|s|~1ebDF0<%Rz^j~upa`hD%;o=*TZtIFDNEPKQQJ+V_+znpIzo} z{uCg?-nN@NAh|n{gMVvlsa60KTuu0w_JClZJrG|z;AcNUCIuJ{{0~t1{~gfYHg&Zv zAw?QPqJ&U2aem<&j|O+U=hgV%OjmW66PpHFUIxIy%LGj(1*cR0B|YFUun8CsOaWE{ zUf?xqBXm79t?lzZU;`#xkMY1m;kOS?Fa|{kDJ8nwCF4f_WvMYRv_uM~3X?)r1yWwtInb;TtE(K|zJ8~xF#8oC5k+0B7)@*9o}N6g^7O%h zC<;LS&9n<#6+(z{X0v(k$4iz3ODQQWEk#O6dU`r#3#N+dbH|YN2#`_|W-I@$(bwg&+>- z3G|Z^grHS&3c)YT!|SX@s2c8?Dq5V}#NNyz!tQ&Ts&mJrUtE$v_o92;K9am1E-ac+ zT(o_C$j!b4a_7&V-(-`YACq%(IMcp8&Y~hpD=U$Hejp_qR(?v`hks3QOguJX_f`@sxOpMBHd0(_J575IB(`nOMqgsOq@A)@$|Te5DC5n zqk8*x0168W0dTtAXa)lskkCAa?N#+>`RWjgf~&MZmhRcI1-Jk=S1Ozg{Muv>TAsf1 z;Pm_N8X&jkI0QflpoN40ux{Nt3Non?$nkV3t@{Y~;D@T!u7>WWj5%kvn=c0A5{Z#7i$> z0E(&{>@9F$(5`KYloIK2<1Q~Icm0C*fHRk#HP$ZBJt+0A_gdaNyj^s=+vLTf5`m(k ze6@t7v)<-xK?%QIcNnv#(%L3iG^{6Q@O@xRTe5`K4-7)))^Iwrgk9NXWR^LuvFfSY zA{{yBWcKGDmXz+<8dCqcdV>u&SYr}a1m8Yn#<-VX3ln|;;$(iA5JDh~X8Nf4q;%}W zdwb4PQ0?2Y3fu~NGO~HBWhgF9Lr+X%<>u2E-Cn-AP{M^W$2HfzloBBnoO$PE*@16F z!ItI8_zEs{xRFaBCA|M*-}M_m-D21|ruy2e z&m3Z#+eN3m8u~_AI9sF8EWpgX{aKhbrB1${)gTl=A`}Hj-UV58{)EU{H7AXFu73$P zDhNIp{q&T5BPYIM9+4En#xsS&DKA&tRZ)V~>PMNwja%32+Wiw!a2Dl|w`w+9j-Dgm zqp~Nj3bR&E_1dIJF^H&#(whHn=JLr; zJtBiGs(9&(gJ=R2gAv_XQ`bo|nyIL9U^i(f^;}wd+=TTWM8Se-L~or+?{ijG+9E5U1gs=hJeofsmVD<)tN)$?1!%`LP{Gj8fcBL zUNH|}$!!y`ry)H+3A%IWjzI%%mrDLQ@u!0o7*wCz26w)b%w_M=G9T-YWq!;yPl7HnH7^1uA# z7_b+Zjjz^tRWz6fm?E1m{rx{aYX4H&o#OsyULYW;D|JN+uLoCYA*#`gYPBJRKSPtoK*h3|d@y?!&#pU4&B;Azwm>Y= zE$ie;_ie+{A{npEj^p?qvX{)nRg@!v{j~;ftDXJQAJer#2#6XyR(>_-b-DFik@)A% z^C$uo)xg<#(}+%c2F>Q1T`47sX5hk-nFM#~k1e4iuHsz0g;`u&`;qK2@DXwJ&_UTW z?HM6Gt~zzI*SwD*Fr2*fMMs_anal7MVE0~+t_=|cJGY4p!k}s5+lwW2iHh!Z(_+-i z*E@2~J_NXauCfh5K%&bpnAIOIxN)k0QZJYCgv@K{^d7Eu&@`?Sa@n~4UpoH$ z;lCmh`&q_2?82=-RmcD(s~jKk%!R#esWj81zd zwpB$&fwOgwNXA5T@DnZ8NawB{M&TK-nJ`_oD7k^U`~eCcW7#pyB|NFvv%&A=RbNi zyejjw$nzRHSX(5^t9|AwLZC}{W!ouCW|Mq#<>tGUi013BSJ?*11RYstiZlIV9v#@F z9buut94f3qF_>_b7Ert+o#GuEKtlG4S>d_I_ummadTMfFtCs!FeYf##zu3gv%FD}I z+Y>tr>2+Uo3{4Rn%q?Kx4N9*E&~YeI)JQIb8r>szUR!lFfX9XU4Q5tVfF4_xBM+*NQ7Op+6Vf#?kJ_l z{a<60stCT%EEVIr$Fs}Ux^vOi6@@?+P|?@~;404F6xM6dv(fzq$>~oI7GHWJu|~(E zT5M$e`(O(1XMlJ9_hft_xj5^XaW4`!F#SyNiv(^FLzWpAD+RI~L*$I%qQfxHSj z$2XJtSns!|^m#t&Z%@NjS2~t&6k?)e6snOV)k5QkUTg02lNG6Z32fJmbMvMVn9}!0y#PQlnkd`<6#?zKgVd$LU=VrbHS*icGSMcrHw72Z z4s&FknR&wrsKqf?aqb)5%90p8EVhH9s+1qwfnu^?Y1)#a?d!zJzm1)mv-ZRJxf>Q9 zsXTR{ye9ihh%qSKTCi;u_O_`~F&M?AZ;SsRSxU-}?4)4pip|pFj#Et*ap3v;w?_{d z*X;C+XHV7$V`!6Kjj(60`OppW>q~Ntc08`x0++R${z;l@@e}AeLe*r>7YoQ*KC2a% zm#+;#Z+qL+#j(R*3^xRZiK}{p5M-{HU6!|D;Zyi(0gnLhnId9#IP)%~0xd&({%VWv zajC^S((4XK8uNa@gY9RmUa8rFez@ME8DwtyVv+gDJ6(ZYSM*oS5iwhvJ@#BLYjnKS z{DUsL86u@bC@SuXVlw9ZX?NwR19t-DR~fel@Lk?6oUy{i*`-Skdc`me78$4;bMIy8vBOG?-Kv`@4x%SCUV8vmhSRmkuiJXj>?Q9_wb*ktw2*~ zlMh4tr9Bjo)I|za6ROo8)$E62Gy{^_3k1?rQ6esW@=k8?&h#F@*(()jYyz;xBpi$z zIi;O3C`>3Oi|>@jr#+f2P#%7Hn5a5)FabDq74hcA(5UHwDSaLfO6iSiu}SImd4*!K zpqR{cq#S%<&}LUj{*_nu8lM2b;1{2Ky4j<@k5Nq)G^@YVy@-s{JZ22d?3!67DREyu=0z3BIZ)??IH86^w pKRGwU*W4T7JGo6%%dMpeWv)ps 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 literal 0 HcmV?d00001 diff --git a/app/examples/Games/GNUBoxWorld/ganador.png b/app/examples/Games/GNUBoxWorld/ganador.png new file mode 100644 index 0000000000000000000000000000000000000000..f6672bf77dc04adbc3cac19d7bfceca31f837ca7 GIT binary patch literal 255686 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 E0T7;^U;qFB literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Painting/.icon.png b/app/examples/Drawing/Painting/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f7040fd5c56fd06b126c8d685cfb8f3f3c6d0f54 GIT binary patch literal 3331 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 N002ovPDHLkV1oZ@ZEyep literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Painting/.lang/ca.mo b/app/examples/Drawing/Painting/.lang/ca.mo new file mode 100644 index 0000000000000000000000000000000000000000..883678956fed62ff04f75835131fa14ec56d43e7 GIT binary patch literal 1687 zcmZXS&ue5w5XXBp>N@^dU4O*Y6-p3-$Qx&7JuDNm>@btWNWzYp7z9r>?{#K+ebYU5 z_nRbw9t82UpohJ9@+5l^59>)hIe&nHUc3l`c-o8qf!})bk}TNJ@AJC4y1J_R`{%`_ z+X~}l%*&Wd=ahO4ya&qo@&Tou1-}Bt{w??*_$&B0_&c}+{sq>+f4~dizuHD(FM-m6YoO$D9h5lRp!mHDJ_8Ox$!iA6 zeVf~Xsq-!aA&n2)9%6q*H zN*x-Md>tt9J_64 zkq%89QygpRv$2uVoI_9@pUNbwM;y&et8Q+l<%i6KM_3nrrpfCxvK{3o>*!RE!&t2! z@nj0?hRJhcn^B0~eORqSJjT|h=gepu)m9m={L++T)ZVIXV>J@X;y9?CB9DfG9!=m) zDNlpydp$GyQ0=l^ieE7DA!D<(i<@m zCN2r(qT@9;ah1D?B9pBw+1EZ8XWNTL;Y_W6BiThJVoiD+b(>m^W;1DANm>nRHQQI- ze7(_ZG-}%%VzTGihMc4GZFOp?4AM@C`x(?UiZ$|~Xoa||ywCQw> z-<_?U^|MTx^+v7htR$W69Z!)!L&e_&f&kPH5 zM4{lBi63+PpMiKXq?{Krt2C#Q`?OZ5ez8zR9BIllO&r9-YWGBH#NNBiSrb*)`5_al z%>pkFpgLWuZhQWnHB5#M*r!HhqjZQSmuP3Eo*qGhND\n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Catalan\n" + +#: .project:1 +msgid "Anti-Aliased Drawing" +msgstr "Dibuix suavitzat" + +#: .project:2 +msgid "Anti-aliased drawing with the Paint class" +msgstr "Dibuix suavitzat amb la classe Paint" + +#: FMain.form:62 +msgid "Arc" +msgstr "-" + +#: FMain.form:70 +msgid "Arc negative" +msgstr "Arc negatiu" + +#: FMain.form:77 +msgid "Clip" +msgstr "Punt" + +#: FMain.form:84 +msgid "Clip image" +msgstr "Imatge de mostra" + +#: FMain.form:91 +msgid "Curve rectangle" +msgstr "Rectangle arrodonit" + +#: FMain.form:98 +msgid "Curve to" +msgstr "Corba a" + +#: FMain.form:105 +msgid "Dash" +msgstr "Guions" + +#: FMain.form:51 +msgid "Example" +msgstr "Exemple" + +#: FMain.form:112 +msgid "Fill and stroke" +msgstr "Línia i farciment" + +#: FMain.form:119 +msgid "Fill style" +msgstr "estil de farciment" + +#: FMain.form:126 +msgid "Gradient" +msgstr "Degradat" + +#: FMain.form:133 +msgid "Image" +msgstr "Imatge" + +#: FMain.form:140 +msgid "Image pattern" +msgstr "Patró d'imatge" + +#: FMain.form:147 +msgid "Line widths" +msgstr "Amples de línia" + +#: FMain.form:154 +msgid "Multi segment caps" +msgstr "Caps en múltiples segments" + +#: FMain.form:230 +msgid "Preview" +msgstr "Visualització prèvia" + +#: FMain.form:203 +msgid "Rotate" +msgstr "Gira" + +#: FMain.form:214 +msgid "Scale" +msgstr "Escala" + +#: FMain.form:161 +msgid "Set line cap" +msgstr "Defineix el cap de línia" + +#: FMain.form:168 +msgid "Set line join" +msgstr "Defineix la unió de línies" + +#: FMain.form:239 +msgid "Source" +msgstr "Codi font" + +#: FMain.form:175 +msgid "SVG image" +msgstr "Imatge SVG" + +#: FMain.form:182 +msgid "Text" +msgstr "-" + +#: FMain.form:244 +msgid "TextArea1" +msgstr "-" + +#: FMain.form:189 +msgid "Text clipping" +msgstr "Retallat de text" + +#: FMain.form:196 +msgid "Text extents" +msgstr "Extensions de text" + diff --git a/app/examples/Drawing/Painting/.lang/cs.mo b/app/examples/Drawing/Painting/.lang/cs.mo new file mode 100644 index 0000000000000000000000000000000000000000..9fbc15d844933679d177fb692ff7b807c2906c90 GIT binary patch literal 1695 zcmZvaJ&YSg6vro!@L>|*Lih?_(;z_622!9zCPcTK_YUj)h3&K8nrN~+*puDcS_34IgMWd?!N0+y;2p38{tG?@9)Z(i;1Wpt zr@=?T*Ff5T10?_RAo+a&9s}3G6W~YS6X0d=Bp8Bp{&VmV@JnzB+yx&8e*o$H0Z6)j z1S!rhAo>3el8!$L`zO|kwCBs~Wp)%$b7-$06U6MPc94ZZ~a15%!2NKW}r;6r&%ffWBuknZnY z@L6yfd;#o%ILb6g^=2U5-w338-v#Muzb}a2`8$D>m$cvs^FeXxT25m=pIhc{LU}01 zD}{}8(4CQ9guOqg{^u}BH))_Aqd_`I8}%F2Oyk8l^4jQ^?kXQ{Y?b3iN~AL26(hD) z+-JE_6vkj~*Z4GfEc?Q$p=2wOO7ag^TcVG3W`>d*>01%^ zqkC(sSw*A+=3Nt839PFsinxdao?4^BVx8I@Y^@m)C>dK;&Ew@?o(OBDiCIm>62byI zNLf9Ltm3KcZ^4`UB1u`x$f1(ktgWrE63F5JWiHfodTWKwjxUg`BQ1|8CXO!_SG9^+ zM`y;DtShhC{DTWnNkX?pCXR`;X})lc6z7m!Uq#tYHE?>;q)NwS-f~yg-8GN*ymqJB zY?N9}rwxg;=~RT3W&Vlhww-#@skr>ZX1QKHv)OETrJ6{s(={SaBVn~E^VN-7jW^u7 z$1i$yw_00vE0wm_>0E+q+|O|HPFISpGOxLfwGB#nu~u7-)LCu%3!gGC)vI-Hq3_(; zcSs_66@^@ zA~BNjiEdve4I!VHg5 zn6XU(kHRyM5mJS0eYU5Dp?>}?AY&ag8y}J-p6-*A z#ED?8U?dAEqfoN7%H1b%8+X^reyc633?2*-rLv=&~$kZQgtbARn2~t TVg8C4@{dxIf)eOinZy1EV^gVb literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Painting/.lang/cs.po b/app/examples/Drawing/Painting/.lang/cs.po new file mode 100644 index 00000000..e74a4157 --- /dev/null +++ b/app/examples/Drawing/Painting/.lang/cs.po @@ -0,0 +1,128 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Anti-Aliased Drawing" +msgstr "Vyhlazené kreslení" + +#: .project:2 +msgid "Anti-aliased drawing with the Paint class" +msgstr "Vyhlazené kreslení s třídou Paint" + +#: FMain.form:53 +msgid "Example" +msgstr "Příklad" + +#: FMain.form:64 +msgid "Arc" +msgstr "Oblouk" + +#: FMain.form:72 +msgid "Arc negative" +msgstr "Obrácený oblouk" + +#: FMain.form:79 +msgid "Clip" +msgstr "Výřez" + +#: FMain.form:86 +msgid "Clip image" +msgstr "Výřez obrázku" + +#: FMain.form:93 +msgid "Curve rectangle" +msgstr "Zaoblený obdelník" + +#: FMain.form:100 +msgid "Curve to" +msgstr "Křivka" + +#: FMain.form:107 +msgid "Dash" +msgstr "Čerchované" + +#: FMain.form:114 +msgid "Fill and stroke" +msgstr "Výplň tahu" + +#: FMain.form:121 +msgid "Fill style" +msgstr "Styl výplně" + +#: FMain.form:128 +msgid "Gradient" +msgstr "Přechod" + +#: FMain.form:135 +msgid "Image" +msgstr "Obrázek" + +#: FMain.form:142 +msgid "Image pattern" +msgstr "Obrázkový vzor" + +#: FMain.form:149 +msgid "Line widths" +msgstr "Šířka čary" + +#: FMain.form:156 +msgid "Multi segment caps" +msgstr "Několika segmentová stopa" + +#: FMain.form:163 +msgid "Rounded rectangle" +msgstr "Zaoblený obdelník" + +#: FMain.form:170 +msgid "Set line cap" +msgstr "Nastavit stopu čáry" + +#: FMain.form:177 +msgid "Set line join" +msgstr "Nastavit spojení čáry" + +#: FMain.form:184 +msgid "SVG image" +msgstr "SVG obrázek" + +#: FMain.form:191 +msgid "Text" +msgstr "Text" + +#: FMain.form:198 +msgid "Text clipping" +msgstr "Výstřižek textu" + +#: FMain.form:205 +msgid "Text extents" +msgstr "Rozsáhlý text" + +#: FMain.form:212 +msgid "Rotate" +msgstr "Rotace" + +#: FMain.form:223 +msgid "Scale" +msgstr "Měřítko" + +#: FMain.form:243 +msgid "Preview" +msgstr "Náhled" + +#: FMain.form:250 +msgid "Source" +msgstr "Zdroj" + +#: FMain.form:255 +msgid "TextArea1" +msgstr "-" diff --git a/app/examples/Drawing/Painting/.lang/de.mo b/app/examples/Drawing/Painting/.lang/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..9cb54e6b7cdb17f540677447426cdb3d381a9fa7 GIT binary patch literal 1695 zcmZvazmFS56vrnJewjc>0tAj<(;);Kl8Rsg%KE%#th3J-+jkPzpz-cI@8rg_*39f( zQUDzdLZX2V1rQCQprcBIu7e68Au5Cfe*kG{NRjVbFK3V#Y4)@8X6C)`d-Hxhef(pA z@hs-km?w@2@d9`orbf`5X1zX9>A{|r7HX9Z;Y`I@hS zXR&U9Pk=ih$MGQRdYsiCh=D`|y@Y_`*wn0a+(aKXM+fE&5 zI~A2t)y8aLlm|NGGUOyTm9{~qMtLt{m-3XBrYh)t601hftB=$(HO0Dg`y?HuLD{MK zXB`UBRz4TWH8q>Vx~7eh%4X6BSM1emAFgAo<5Z@}2GOnjRbM$*A&|48r!7HP7IH88 zr3qSkn$B=qrfTlRrlWmL2Vz(R6$mJ8hBTLIM%$fQ=O|SOHliR6`^5F-;%cF-7!{>U zNsQ@Qs6Hrwp3k{jY?7D=>#IfVNL@hi;wsXPx>>YM&g;T90)+)Rb=}@i6KSqIN9b95nmg7k365Nu#HHh{jG?Z&WB;Q?70GdU7M~C-PF# zkGs9gal1WCMx!@y)J{uOJ{psnHDxc}=xlMwOTFG@qt6v?x;)dAjefVEEcIPDcfPS& zSWYDxU!OxyKw>Y?4W9fpndZuQ3a@XC*P@Hdb1Z9@T$I?f$WV}`yg1RJA$CZoxg{%S zI>=?8amf_A4*lgdxjna8KQ;RvQkn-!_e6_5NUPFZbO?P0ab$vM!TDU1fpPcnrZGX{rYvX33J$~?+kDVQ z;1P`~cdKC4XI6SNA@?Bqa{`fQu|DOF9La@o;ia@JeYBGHf$XCwOR z@P-d+QoBF$Bo7Y1%nj}@BJ7i!XqzF#(Z-dMjKNEgh!(9mAubH*47a#fClJWVWB(4p CM2#Z= literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Painting/.lang/de.po b/app/examples/Drawing/Painting/.lang/de.po new file mode 100644 index 00000000..abe77c45 --- /dev/null +++ b/app/examples/Drawing/Painting/.lang/de.po @@ -0,0 +1,129 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2002-11-01 04:27+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .project:1 +msgid "Anti-Aliased Drawing" +msgstr "Zeichnen mit Anti-Aliasing" + +#: .project:2 +msgid "Anti-aliased drawing with the Paint class" +msgstr "antialisiertes Zeichnen mit der Paint-Klasse" + +#: FMain.form:54 +msgid "Example" +msgstr "Beispiel" + +#: FMain.form:65 +msgid "Arc" +msgstr "Kreisbogen" + +#: FMain.form:73 +msgid "Arc negative" +msgstr "Negativer Kreisbogen" + +#: FMain.form:80 +msgid "Clip" +msgstr "-" + +#: FMain.form:87 +msgid "Clip image" +msgstr "Bildausschnitt" + +#: FMain.form:94 +msgid "Curve rectangle" +msgstr "Abgerundetes Rechteck" + +#: FMain.form:101 +msgid "Curve to" +msgstr "Bogen nach" + +#: FMain.form:108 +msgid "Dash" +msgstr "Gestrichelt" + +#: FMain.form:115 +msgid "Fill and stroke" +msgstr "Gefüllt und umrandet" + +#: FMain.form:122 +msgid "Fill style" +msgstr "Füllstil" + +#: FMain.form:129 +msgid "Gradient" +msgstr "Verlauf" + +#: FMain.form:136 +msgid "Image" +msgstr "Bild" + +#: FMain.form:143 +msgid "Image pattern" +msgstr "Bildmuster" + +#: FMain.form:150 +msgid "Line widths" +msgstr "Linienbreite" + +#: FMain.form:157 +msgid "Multi segment caps" +msgstr "Mehrere Linienumrandungen" + +#: FMain.form:164 +msgid "Rounded rectangle" +msgstr "Abgerundetes Rechteck" + +#: FMain.form:171 +msgid "Set line cap" +msgstr "Linienumrandung wählen" + +#: FMain.form:178 +msgid "Set line join" +msgstr "Linienverbindung wählen" + +#: FMain.form:185 +msgid "SVG image" +msgstr "SVG-Bild" + +#: FMain.form:192 +msgid "Text" +msgstr "-" + +#: FMain.form:199 +msgid "Text clipping" +msgstr "Text beschneiden" + +#: FMain.form:206 +msgid "Text extents" +msgstr "Textbereiche" + +#: FMain.form:213 +msgid "Rotate" +msgstr "Drehen" + +#: FMain.form:224 +msgid "Scale" +msgstr "Maßstab" + +#: FMain.form:248 +msgid "Preview" +msgstr "Vorschau" + +#: FMain.form:255 +msgid "Source" +msgstr "Quellcode" + +#: FMain.form:260 +msgid "TextArea1" +msgstr "-" + diff --git a/app/examples/Drawing/Painting/.project b/app/examples/Drawing/Painting/.project new file mode 100644 index 00000000..b2c5f0b1 --- /dev/null +++ b/app/examples/Drawing/Painting/.project @@ -0,0 +1,17 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.7.90 +Title=Anti-Aliased Drawing +Startup=FMain +Icon=icon.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Component=gb.form +Description="Anti-aliased drawing with the Paint class.\n\nThis example shows the different methods of the Paint class." +Authors="Fabien Bodard\nBenoît Minisini" +Environment="GB_GUI=gb.qt4" +TabSize=2 +Translate=1 +Language=en +Vendor=Example +Packager=1 diff --git a/app/examples/Drawing/Painting/.src/FMain.class b/app/examples/Drawing/Painting/.src/FMain.class new file mode 100644 index 00000000..1bb948ad --- /dev/null +++ b/app/examples/Drawing/Painting/.src/FMain.class @@ -0,0 +1,548 @@ +' Gambas class file + +Private Const IMAGE_NAME As String = "clovis.jpg" +Private Const SVG_EXPORT As String = "~/Gambas with red ballon.svg" + +Private $sFunctionName As String = "Example1" + +Public Sub _new() + + Editor1.Text = File.Load($sFunctionName) + +End + +Public Sub Form_Open() + + HSplit1.Layout = [1, 2] + +End + +Public Sub Example1() + + Dim XC As Float = 128 + Dim YC As Float = 128 + Dim Radius As Float = 100 + Dim Angle1 As Float = Rad(-45) + Dim Angle2 As Float = Rad(-180) + Dim X, Y As Float + + Paint.LineWidth = 10 + Paint.Arc(XC, YC, Radius, Angle1, Angle2 - Angle1) + Paint.Stroke + + 'Draw helping Lines + Paint.LineWidth = 6.0 + Paint.Brush = Paint.Color(Color.RGB(255, 0.2 * 255, 0.2 * 255, 0.6 * 255)) + + Paint.Arc(XC, YC, 10.0) + Paint.Fill + + Paint.Arc(XC, YC, Radius, Angle1, 0) + X = Paint.X + Y = Paint.Y + Paint.Arc(XC, YC, Radius, Angle2, 0) + Paint.LineTo(XC, YC) + Paint.LineTo(X, Y) + Paint.Stroke + +End + + +Public Sub Example2() + + Dim XC As Float = 128 + Dim YC As Float = 128 + Dim Radius As Float = 100 + Dim Angle1 As Float = Rad(-45) + Dim Angle2 As Float = Rad(180) + Dim X, Y As Float + + Paint.LineWidth = 10 + Paint.Arc(XC, YC, Radius, Angle1, Angle2 - Angle1) + Paint.Stroke + + 'Draw helping Lines + Paint.LineWidth = 6.0 + Paint.Brush = Paint.Color(Color.RGB(255, 0.2 * 255, 0.2 * 255, 0.6 * 255)) + + Paint.Arc(XC, YC, 10.0) + Paint.Fill + + Paint.Arc(XC, YC, Radius, Angle1, 0) + X = Paint.X + Y = Paint.Y + Paint.Arc(XC, YC, Radius, Angle2, 0) + Paint.LineTo(XC, YC) + Paint.LineTo(X, Y) + Paint.Stroke + +End + +Public Sub Example3() + + Paint.Arc(128.0, 128.0, 76.8) + Paint.Clip + Paint.Rectangle(0, 0, 256, 256) + Paint.Fill + Paint.Brush = Paint.Color(Color.RGB(0, 255, 0)) + Paint.MoveTo(0, 0) + Paint.LineTo(256, 256) + Paint.MoveTo(256, 0) + Paint.LineTo(0, 256) + Paint.LineWidth = 10.0 + Paint.Stroke + +End + +Public Sub Example4() + + Dim hImg As Image + + Paint.Arc(128.0, 128.0, 76.8) + Paint.Clip + hImg = Image.Load(IMAGE_NAME) + Paint.Scale(256 / hImg.Width, 256 / hImg.Height) + Paint.Brush = Paint.Image(hImg, 0, 0) + Paint.Rectangle(0, 0, 512, 512) + Paint.Fill() + +End + +Public Sub Example5() + + ' A custom shape that could be wrapped in a function + Dim X0 As Float = 25.6 + Dim Y0 As Float = 25.6 + Dim RectWidth As Float = 204.8 + Dim RectHeight As Float = 204.8 + + Dim Radius As Float = 102.4 + + Dim X1, Y1 As Float + + X1 = X0 + RectWidth + Y1 = Y0 + RectHeight + + If Not (RectWidth > 0.0) Or Not (RectHeight > 0.0) Then Return + + If RectWidth / 2 < Radius Then + If RectHeight / 2 < Radius Then + Paint.MoveTo(X0, (Y0 + Y1) / 2) + Paint.CurveTo(X0, Y0, X0, Y0, (X0 + X1) / 2, Y0) + Paint.CurveTo(X1, Y0, X1, Y0, X1, (Y0 + Y1) / 2) + Paint.CurveTo(X1, Y1, X1, Y1, (X1 + X0) / 2, Y1) + Paint.CurveTo(X0, Y1, X0, Y1, X0, (Y0 + Y1) / 2) + Else + Paint.MoveTo(X0, Y0 + Radius) + Paint.CurveTo(X0, Y0, X0, Y0, (X0 + X1) / 2, Y0) + Paint.CurveTo(X1, Y0, X1, Y0, X1, Y0 + Radius) + Paint.LineTo(X1, Y1 - Radius) + Paint.CurveTo(X1, Y1, X1, Y1, (X1 + X0) / 2, Y1) + Paint.CurveTo(X0, Y1, X0, Y1, X0, Y1 - Radius) + Endif + Else + If (RectHeight / 2 < Radius) Then + Paint.MoveTo(X0, (Y0 + Y1) / 2) + Paint.CurveTo(X0, Y0, X0, Y0, X0 + Radius, Y0) + Paint.LineTo(X1 - Radius, Y0) + Paint.CurveTo(X1, Y0, X1, Y0, X1, (Y0 + Y1) / 2) + Paint.CurveTo(X1, Y1, X1, Y1, X1 - Radius, Y1) + Paint.LineTo(X0 + Radius, Y1) + Paint.CurveTo(X0, Y1, X0, Y1, X0, (Y0 + Y1) / 2) + Else + Paint.MoveTo(X0, Y0 + Radius) + Paint.CurveTo(X0, Y0, X0, Y0, X0 + Radius, Y0) + Paint.LineTo(X1 - Radius, Y0) + Paint.CurveTo(X1, Y0, X1, Y0, X1, Y0 + Radius) + Paint.LineTo(X1, Y1 - Radius) + Paint.CurveTo(X1, Y1, X1, Y1, X1 - Radius, Y1) + Paint.LineTo(X0 + Radius, Y1) + Paint.CurveTo(X0, Y1, X0, Y1, X0, Y1 - Radius) + Endif + Endif + + Paint.ClosePath + Paint.Brush = Paint.Color(Color.RGB(128, 128, 255)) + Paint.Fill(True) + Paint.Brush = Paint.Color(Color.RGB(128, 0, 0, 128)) + Paint.LineWidth = 10.0 + Paint.Stroke + +End + +Public Sub Example6() + + Dim X As Float = 25.6 + Dim Y As Float = 128.0 + Dim X1 As Float = 102.4 + Dim Y1 As Float = 230.4 + Dim X2 As Float = 153.6 + Dim Y2 As Float = 25.6 + Dim X3 As Float = 230.4 + Dim Y3 As Float = 128.0 + + Paint.MoveTo(X, Y) + Paint.CurveTo(X1, Y1, X2, Y2, X3, Y3) + + Paint.LineWidth = 10 + Paint.Stroke + + Paint.Brush = Paint.Color(Color.RGB(255, 255 * 0.2, 255 * 0.2, 255 * 0.6)) + Paint.LineWidth = 6 + + Paint.MoveTo(X, Y) + Paint.LineTo(X1, Y1) + Paint.MoveTo(X2, Y2) + Paint.LineTo(X3, Y3) + Paint.Stroke + +End + +Public Sub Example7() + + Dim Dashes As Float[] = [5.0, 'ink + 1.0, 'skip + 1.0, 'ink + 1.0] 'skip + + Paint.Dash = Dashes + Paint.DashOffset = -5 + Paint.LineWidth = 10.0 + Paint.MoveTo(128.0, 25.6) + Paint.LineTo(230.4, 230.4) + Paint.RelLineTo(-102.4, 0) + Paint.CurveTo(51.2, 230.4, 51.2, 128.0, 128.0, 128.0) + Paint.Stroke + +End + +Public Sub Example8() + + Paint.MoveTo(128.0, 25.6) + Paint.LineTo(230.4, 230.4) + Paint.LineTo(Paint.X - 102.4, Paint.Y) + Paint.CurveTo(51.2, 230.4, 51.2, 128.0, 128.0, 128.0) + Paint.ClosePath + + Paint.MoveTo(64.0, 25.6) + Paint.RelLineTo(51.2, 51.2) + Paint.RelLineTo(-51.2, 51.2) + Paint.RelLineTo(-51.2, -51.2) + Paint.ClosePath + + Paint.LineWidth = 10 + Paint.Brush = Paint.Color(Color.Blue) + Paint.Fill(True) + + Paint.Brush = Paint.Color(Color.Black) + Paint.Stroke + +End + +Public Sub Example9() + + Paint.LineWidth = 6 + Paint.Rectangle(12, 12, 232, 70) + Paint.Arc(64, 64, 40, 0, Pi(2)) + Paint.Arc(192, 64, 40, 0, Pi(-2)) + + Paint.FillRule = Paint.FillRuleEvenOdd + Paint.Brush = Paint.Color(Color.RGB(0, 179, 0)) + Paint.Fill(True) + Paint.Brush = Paint.Color(0) + Paint.Stroke + Paint.Translate(0, 128) + Paint.Rectangle(12, 12, 232, 70) + Paint.Arc(64, 64, 40, 0, Pi(-2)) + Paint.Arc(192, 64, 40, 0, Pi(2)) + Paint.FillRule = Paint.FillRuleWinding + Paint.Brush = Paint.Color(Color.RGB(0, 0, 230)) + Paint.Fill(True) + Paint.Brush = Paint.Color(0) + Paint.Stroke + +End + +Public Sub Example10() + + Paint.Brush = Paint.LinearGradient(0, 0, 0, 256, [Color.Black, Color.White], [1.0, 0.0]) + Paint.Rectangle(0, 0, 256, 256) + Paint.Fill + + Paint.Brush = Paint.RadialGradient(102.4, 102.4, 128.0, 115.2, 102.4, [Color.Black, Color.White], [1.0, 0.1]) + Paint.Arc(128, 128, 76.8) + Paint.Fill + +End + +Public Sub Example11() + + Dim X, Y, W, H As Float + Dim hBrush As PaintBrush + Dim hImage As Image + + hImage = Image.Load(IMAGE_NAME) + + X = 16 + Y = 40 + W = 200 + H = 200 + + 'Paint.DrawImage(hImage, X, Y, W, H) + + hBrush = Paint.Image(hImage) + hBrush.Translate(X, Y) + hBrush.Scale(W / hImage.W, H / hImage.H) + Paint.Brush = hBrush + Paint.Rectangle(X, Y, W, H) + Paint.Fill + + Paint.Brush = Paint.Color(Color.RGB(255, 127, 127, 153)) + Paint.Arc(X, Y, 10) + Paint.Fill + +End + +Public Sub Example12() + + Dim hImg As Image + Dim hBrush As PaintBrush + + hImg = Image.Load(IMAGE_NAME) + + Paint.Translate(128, 128) + 'Paint.Rotate(Pi / 4) + Paint.Scale(1 / Sqr(2), 1 / Sqr(2)) + Paint.Translate(-128, -128) + hBrush = Paint.Image(himg, 0, 0) + hBrush.Matrix = hBrush.Matrix.Scale(1 / (himg.w / 256.0 * 5.0), 1 / (hImg.w / 256.0 * 5.0)) + Paint.Brush = hBrush + Paint.Rectangle(0, 0, 256, 256) + Paint.Fill + +End + +Public Sub Example13() + + Paint.MoveTo(50, 75) + Paint.LineTo(200, 75) + + Paint.MoveTo(50, 125) + Paint.LineTo(200, 125) + + Paint.MoveTo(50, 175) + Paint.LineTo(200, 175) + + Paint.LineWidth = 30 + Paint.LineCap = Paint.LineCapRound + Paint.Stroke + +End + +Public Sub Example14() + + Paint.LineWidth = 30 + Paint.LineCap = Paint.LineCapButt + Paint.MoveTo(64, 50) + Paint.LineTo(64, 200) + Paint.Stroke + Paint.LineCap = Paint.LineCapRound + Paint.MoveTo(128, 50) + Paint.LineTo(128, 200) + Paint.Stroke + Paint.LineCap = Paint.LineCapSquare + Paint.MoveTo(192, 50) + Paint.LineTo(192, 200) + Paint.Stroke + + 'Draw helping lines + Paint.Brush = Paint.Color(Color.RGB(255, 31, 31)) + Paint.LineWidth = 6 + Paint.MoveTo(64, 50) + Paint.LineTo(64, 200) + Paint.MoveTo(128, 50) + Paint.LineTo(128, 200) + Paint.MoveTo(192, 50) + Paint.LineTo(192, 200) + Paint.Stroke + +End + +Public Sub Example15() + + Paint.LineWidth = 40.96 + Paint.MoveTo(76.8, 84.48) + Paint.RelLineTo(51.2, -51.2) + Paint.RelLineTo(51.2, 51.2) + Paint.LineJoin = Paint.LineJoinMiter 'Default + Paint.Stroke + + Paint.MoveTo(76.8, 161.28) + Paint.RelLineTo(51.2, -51.2) + Paint.RelLineTo(51.2, 51.2) + Paint.LineJoin = Paint.LineJoinBevel + Paint.Stroke + + Paint.MoveTo(76.8, 238.08) + Paint.RelLineTo(51.2, -51.2) + Paint.RelLineTo(51.2, 51.2) + Paint.LineJoin = Paint.LineJoinRound + Paint.Stroke + +End + +Public Sub Example16() + + Paint.Font.Name = "Sans" + Paint.Font.Size = 90 + Paint.Font.Bold = True + Paint.MoveTo(10, 135) + Paint.Text("Hello") + Paint.Fill + + Paint.MoveTo(70, 165) + Paint.text("void") + Paint.Brush = Paint.Color(Color.RGB(128, 128, 255)) + Paint.Fill(True) + Paint.Brush = Paint.Color(0) + Paint.LineWidth = 0 '2.56 + Paint.Stroke + + 'Draw helping lines + Paint.Brush = Paint.Color(Color.RGB(255, 31, 31, 93)) + Paint.Arc(10, 135, 5.12) + Paint.ClosePath + Paint.Arc(70, 165, 5.12) + Paint.Fill + +End + +Public Sub Example17() + + Dim sText As String = "Gambas
already means
Basic!" + + Dim X, Y As Float + Dim hExt As PaintExtents + + Paint.Font = Font["Sans,40"] + X = 50 + Y = 100 + + Paint.MoveTo(X, Y) + + hExt = Paint.RichTextExtents(sText) + + Paint.Brush = Paint.RadialGradient(50, 100, 300, 50, 100, [Color.Yellow, Color.Cyan], [1.0, 0.0]) + Paint.RichText(sText) + Paint.Fill + + Paint.Brush = Paint.Color(Color.RGB(255, 31, 31, 93)) + Paint.LineWidth = 1 + Paint.Arc(X, Y, 10) + Paint.Fill + + Paint.Brush = Paint.Color(Color.RGB(255, 31, 31, 224)) + Paint.Rectangle(hExt.X, hExt.Y, hExt.Width, hExt.Height) + Paint.Fill + +End + +Public Sub Example18() + + Paint.Save + Paint.Rectangle(10, 10, 200, 100) + Paint.Stroke(True) + Paint.Clip + Paint.Font.Size = 18 + 'Paint.Brush = Paint.Color(Color.Blue) + Paint.Text("TO BE, OR NOT TO BE: THAT IS THE QUESTION:", 10, 10, 30, 30) + Paint.Fill + Paint.Restore + 'Paint.Brush = Paint.Color(Color.Blue) + Paint.Text("TO BE, OR NOT TO BE: THAT IS THE QUESTION:", 10, 40, 30, 30) + Paint.Fill + +End + + +Public Sub Example20() + + Dim eWidth As Float + Dim Y As Float + + Y = 20.5 + For eWidth = 0 To 5 Step 0.25 + Paint.LineWidth = eWidth + Paint.MoveTo(50, Y) + Paint.RelLineTo(200, 0) + Paint.Stroke + Paint.Text(eWidth, 0, Y - 10, 40, 20, Align.Right) + Paint.Fill + Y += 20 + Next + +End + +Private $hSvgImage As SvgImage + +Public Sub Example21() + + If Not $hSvgImage Then + $hSvgImage = SvgImage.Load("gambas.svg") + Paint.Begin($hSvgImage) + Paint.Brush = Paint.RadialGradient(200, 140, 40, 215, 115, [Color.RGB(255, 0, 0, 64), Color.White], [1.0, 0.1]) + Paint.Arc(200, 140, 40) + Paint.Fill + Paint.End + Endif + + $hSvgImage.Paint + +End + +Public Sub Example22() + + Paint.Background = Color.RGB(128, 128, 255) + Paint.Rectangle(32, 32, 192, 192, 32) + Paint.Fill(True) + Paint.Background = Color.RGB(128, 0, 0, 128) + Paint.LineWidth = 10 + Paint.Stroke + 'Paint.Background = Color.Black + 'Paint.Arc(128, 128, 96) + 'Paint.Stroke + +End + +Public Sub optExample_Click() + + $sFunctionName = "Example" & Last.tag + Try Editor1.text = File.Load($sFunctionName) + If Error Then Editor1.Text = Error.Text + DrawingArea1.Refresh + +End + +Public Sub DrawingArea1_Draw() + + Paint.Reset + Paint.Scale(sldScale.Value / 100, sldScale.Value / 100) + Paint.Translate(128, 128) + Paint.Rotate(Rad(sldRotate.Value)) + Paint.Translate(-128, -128) + Object.Call(Me, $sFunctionName) + +End + +Public Sub sldRotate_Change() + + DrawingArea1.Refresh + +End + +Public Sub sldScale_Change() + + DrawingArea1.Refresh + +End + diff --git a/app/examples/Drawing/Painting/.src/FMain.form b/app/examples/Drawing/Painting/.src/FMain.form new file mode 100644 index 00000000..f9ad642f --- /dev/null +++ b/app/examples/Drawing/Painting/.src/FMain.form @@ -0,0 +1,195 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,118,89) + Arrangement = Arrange.Fill + { HSplit1 HSplit + MoveScaled(1,1,96,87) + { VBox1 VBox + MoveScaled(1,1,35,85) + Spacing = True + Margin = True + { Label3 Label + MoveScaled(1,1,19,3) + Font = Font["Bold"] + Text = ("Example") + } + { VBox2 VBox + MoveScaled(2,4,32,64) + AutoResize = True + { Exemple1 RadioButton optExample + Name = "Exemple1" + MoveScaled(0,0,31,3) + Tag = "1" + Text = ("Arc") + Value = True + } + { Exemple2 RadioButton optExample + Name = "Exemple2" + MoveScaled(0,3,31,3) + Tag = "2" + Text = ("Arc negative") + } + { Exemple3 RadioButton optExample + Name = "Exemple3" + MoveScaled(0,6,31,3) + Tag = "3" + Text = ("Clip") + } + { Exemple4 RadioButton optExample + Name = "Exemple4" + MoveScaled(0,9,31,3) + Tag = "4" + Text = ("Clip image") + } + { Exemple5 RadioButton optExample + Name = "Exemple5" + MoveScaled(0,12,31,3) + Tag = "5" + Text = ("Curve rectangle") + } + { Exemple6 RadioButton optExample + Name = "Exemple6" + MoveScaled(0,15,31,3) + Tag = "6" + Text = ("Curve to") + } + { Exemple7 RadioButton optExample + Name = "Exemple7" + MoveScaled(0,18,31,3) + Tag = "7" + Text = ("Dash") + } + { Exemple8 RadioButton optExample + Name = "Exemple8" + MoveScaled(0,21,31,3) + Tag = "8" + Text = ("Fill and stroke") + } + { Exemple9 RadioButton optExample + Name = "Exemple9" + MoveScaled(0,24,31,3) + Tag = "9" + Text = ("Fill style") + } + { Exemple10 RadioButton optExample + Name = "Exemple10" + MoveScaled(0,27,31,3) + Tag = "10" + Text = ("Gradient") + } + { Exemple11 RadioButton optExample + Name = "Exemple11" + MoveScaled(0,30,31,3) + Tag = "11" + Text = ("Image") + } + { Exemple12 RadioButton optExample + Name = "Exemple12" + MoveScaled(0,33,31,3) + Tag = "12" + Text = ("Image pattern") + } + { Exemple20 RadioButton optExample + Name = "Exemple20" + MoveScaled(0,36,31,3) + Tag = "20" + Text = ("Line widths") + } + { Exemple13 RadioButton optExample + Name = "Exemple13" + MoveScaled(0,39,31,3) + Tag = "13" + Text = ("Multi segment caps") + } + { Exemple19 RadioButton optExample + Name = "Exemple19" + MoveScaled(0,42,31,3) + Tag = "22" + Text = ("Rounded rectangle") + } + { Exemple14 RadioButton optExample + Name = "Exemple14" + MoveScaled(0,45,31,3) + Tag = "14" + Text = ("Set line cap") + } + { Exemple15 RadioButton optExample + Name = "Exemple15" + MoveScaled(0,48,31,3) + Tag = "15" + Text = ("Set line join") + } + { Exemple21 RadioButton optExample + Name = "Exemple21" + MoveScaled(0,51,31,3) + Tag = "21" + Text = ("SVG image") + } + { Exemple16 RadioButton optExample + Name = "Exemple16" + MoveScaled(0,54,31,3) + Tag = "16" + Text = ("Text") + } + { Exemple18 RadioButton optExample + Name = "Exemple18" + MoveScaled(0,57,31,3) + Tag = "18" + Text = ("Text clipping") + } + { Exemple17 RadioButton optExample + Name = "Exemple17" + MoveScaled(0,60,31,3) + Tag = "17" + Text = ("Text extents") + } + } + { Label1 Label + MoveScaled(2,69,19,3) + Font = Font["Bold"] + Text = ("Rotate") + } + { sldRotate Slider + MoveScaled(2,73,31,4) + MaxValue = 360 + } + { Label2 Label + MoveScaled(2,77,19,3) + Font = Font["Bold"] + Text = ("Scale") + } + { sldScale Slider + MoveScaled(2,81,31,4) + MinValue = 100 + MaxValue = 400 + } + } + { Panel1 HBox + MoveScaled(40,2,56,73) + { TabStrip1 TabPanel + MoveScaled(6,0,49,72) + Expand = True + Arrangement = Arrange.Fill + Border = False + Count = 2 + Index = 0 + Text = ("Preview") + { DrawingArea1 DrawingArea + MoveScaled(3,1,38,45) + Background = &HFFFFFF& + } + Index = 1 + Text = ("Source") + { Editor1 TextArea + MoveScaled(8,2,32,37) + Font = Font["Monospace,-1"] + Text = ("TextArea1") + ReadOnly = True + Border = False + } + Index = 0 + } + } + } +} diff --git a/app/examples/Drawing/Painting/.src/MMakeSourceFile.module b/app/examples/Drawing/Painting/.src/MMakeSourceFile.module new file mode 100644 index 00000000..346b4717 --- /dev/null +++ b/app/examples/Drawing/Painting/.src/MMakeSourceFile.module @@ -0,0 +1,32 @@ +' Gambas module file + +Private Const ROOT As String = "~/gambas/3.0/trunk/examples/examples/Drawing/Painting/" + +Public Sub Main() + + Dim hFile As File + Dim sLine As String + Dim sName As String + Dim sSource As String + + hFile = Open ROOT &/ ".src/FMain.class" + While Not Eof(hFile) + Line Input #hFile, sLine + If Trim(sLine) Like "Public Sub Example*" Then + sName = Scan(sLine, "Public Sub *()")[0] + sSource = sLine & "\n" + Continue + Else If sName Then + If Trim(sLine) = "End" Then + sSource &= sLine & "\n" + File.Save(ROOT &/ sName, sSource) + Print sName + sName = "" + Else + sSource &= sLine & "\n" + Endif + Endif + Wend + Close hFile + +End diff --git a/app/examples/Drawing/Painting/Example1 b/app/examples/Drawing/Painting/Example1 new file mode 100644 index 00000000..46a36054 --- /dev/null +++ b/app/examples/Drawing/Painting/Example1 @@ -0,0 +1,29 @@ +Public Sub Example1() + + Dim XC As Float = 128 + Dim YC As Float = 128 + Dim Radius As Float = 100 + Dim Angle1 As Float = Rad(-45) + Dim Angle2 As Float = Rad(-180) + Dim X, Y As Float + + Paint.LineWidth = 10 + Paint.Arc(XC, YC, Radius, Angle1, Angle2 - Angle1) + Paint.Stroke + + 'Draw helping Lines + Paint.LineWidth = 6.0 + Paint.Brush = Paint.Color(Color.RGB(255, 0.2 * 255, 0.2 * 255, 0.6 * 255)) + + Paint.Arc(XC, YC, 10.0) + Paint.Fill + + Paint.Arc(XC, YC, Radius, Angle1, 0) + X = Paint.X + Y = Paint.Y + Paint.Arc(XC, YC, Radius, Angle2, 0) + Paint.LineTo(XC, YC) + Paint.LineTo(X, Y) + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example10 b/app/examples/Drawing/Painting/Example10 new file mode 100644 index 00000000..f259831c --- /dev/null +++ b/app/examples/Drawing/Painting/Example10 @@ -0,0 +1,11 @@ +Public Sub Example10() + + Paint.Brush = Paint.LinearGradient(0, 0, 0, 256, [Color.Black, Color.White], [1.0, 0.0]) + Paint.Rectangle(0, 0, 256, 256) + Paint.Fill + + Paint.Brush = Paint.RadialGradient(102.4, 102.4, 128.0, 115.2, 102.4, [Color.Black, Color.White], [1.0, 0.1]) + Paint.Arc(128, 128, 76.8) + Paint.Fill + +End diff --git a/app/examples/Drawing/Painting/Example11 b/app/examples/Drawing/Painting/Example11 new file mode 100644 index 00000000..5f026c8e --- /dev/null +++ b/app/examples/Drawing/Painting/Example11 @@ -0,0 +1,26 @@ +Public Sub Example11() + + Dim X, Y, W, H As Float + Dim hBrush As PaintBrush + Dim hImage As Image + + hImage = Image.Load(IMAGE_NAME) + + X = 16 + Y = 40 + W = 200 + H = 200 + + hBrush = Paint.Image(hImage) + hBrush.Translate(X, Y) + hBrush.Scale(W / hImage.W, H / hImage.H) + Paint.Brush = hBrush + Paint.Rectangle(X, Y, W, H) + Paint.Fill + 'Paint.DrawImage(hImage, X, Y, W, H) + + Paint.Brush = Paint.Color(Color.RGB(255, 127, 127, 153)) + Paint.Arc(X, Y, 10) + Paint.Fill + +End diff --git a/app/examples/Drawing/Painting/Example12 b/app/examples/Drawing/Painting/Example12 new file mode 100644 index 00000000..5dde3f3e --- /dev/null +++ b/app/examples/Drawing/Painting/Example12 @@ -0,0 +1,18 @@ +Public Sub Example12() + + Dim hImg As Image + Dim hBrush As PaintBrush + + hImg = Image.Load(IMAGE_NAME) + + Paint.Translate(128, 128) + 'Paint.Rotate(Pi / 4) + Paint.Scale(1 / Sqr(2), 1 / Sqr(2)) + Paint.Translate(-128, -128) + hBrush = Paint.Image(himg, 0, 0) + hBrush.Matrix = hBrush.Matrix.Scale(1 / (himg.w / 256.0 * 5.0), 1 / (hImg.w / 256.0 * 5.0)) + Paint.Brush = hBrush + Paint.Rectangle(0, 0, 256, 256) + Paint.Fill + +End diff --git a/app/examples/Drawing/Painting/Example13 b/app/examples/Drawing/Painting/Example13 new file mode 100644 index 00000000..20801a6a --- /dev/null +++ b/app/examples/Drawing/Painting/Example13 @@ -0,0 +1,16 @@ +Public Sub Example13() + + Paint.MoveTo(50, 75) + Paint.LineTo(200, 75) + + Paint.MoveTo(50, 125) + Paint.LineTo(200, 125) + + Paint.MoveTo(50, 175) + Paint.LineTo(200, 175) + + Paint.LineWidth = 30 + Paint.LineCap = Paint.LineCapRound + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example14 b/app/examples/Drawing/Painting/Example14 new file mode 100644 index 00000000..4dca4dd0 --- /dev/null +++ b/app/examples/Drawing/Painting/Example14 @@ -0,0 +1,28 @@ +Public Sub Example14() + + Paint.LineWidth = 30 + Paint.LineCap = Paint.LineCapButt + Paint.MoveTo(64, 50) + Paint.LineTo(64, 200) + Paint.Stroke + Paint.LineCap = Paint.LineCapRound + Paint.MoveTo(128, 50) + Paint.LineTo(128, 200) + Paint.Stroke + Paint.LineCap = Paint.LineCapSquare + Paint.MoveTo(192, 50) + Paint.LineTo(192, 200) + Paint.Stroke + + 'Draw helping lines + Paint.Brush = Paint.Color(Color.RGB(255, 31, 31)) + Paint.LineWidth = 6 + Paint.MoveTo(64, 50) + Paint.LineTo(64, 200) + Paint.MoveTo(128, 50) + Paint.LineTo(128, 200) + Paint.MoveTo(192, 50) + Paint.LineTo(192, 200) + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example15 b/app/examples/Drawing/Painting/Example15 new file mode 100644 index 00000000..910f33f7 --- /dev/null +++ b/app/examples/Drawing/Painting/Example15 @@ -0,0 +1,22 @@ +Public Sub Example15() + + Paint.LineWidth = 40.96 + Paint.MoveTo(76.8, 84.48) + Paint.RelLineTo(51.2, -51.2) + Paint.RelLineTo(51.2, 51.2) + Paint.LineJoin = Paint.LineJoinMiter 'Default + Paint.Stroke + + Paint.MoveTo(76.8, 161.28) + Paint.RelLineTo(51.2, -51.2) + Paint.RelLineTo(51.2, 51.2) + Paint.LineJoin = Paint.LineJoinBevel + Paint.Stroke + + Paint.MoveTo(76.8, 238.08) + Paint.RelLineTo(51.2, -51.2) + Paint.RelLineTo(51.2, 51.2) + Paint.LineJoin = Paint.LineJoinRound + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example16 b/app/examples/Drawing/Painting/Example16 new file mode 100644 index 00000000..f70640b7 --- /dev/null +++ b/app/examples/Drawing/Painting/Example16 @@ -0,0 +1,25 @@ +Public Sub Example16() + + Paint.Font.Name = "Sans" + Paint.Font.Size = 90 + Paint.Font.Bold = True + Paint.MoveTo(10, 135) + Paint.Text("Hello") + Paint.Fill + + Paint.MoveTo(70, 165) + Paint.text("void") + Paint.Brush = Paint.Color(Color.RGB(128, 128, 255)) + Paint.Fill(True) + Paint.Brush = Paint.Color(0) + Paint.LineWidth = 2.56 + Paint.Stroke + + 'Draw helping lines + Paint.Brush = Paint.Color(Color.RGB(255, 31, 31, 93)) + Paint.Arc(10, 135, 5.12) + Paint.ClosePath + Paint.Arc(70, 165, 5.12) + Paint.Fill + +End diff --git a/app/examples/Drawing/Painting/Example17 b/app/examples/Drawing/Painting/Example17 new file mode 100644 index 00000000..b4f0e315 --- /dev/null +++ b/app/examples/Drawing/Painting/Example17 @@ -0,0 +1,29 @@ +Public Sub Example17() + + Dim sText As String = "Gambas
already means
Basic!" + + Dim X, Y As Float + Dim hExt As PaintExtents + + Paint.Font = Font["Sans,40"] + X = 50 + Y = 100 + + Paint.MoveTo(X, Y) + + hExt = Paint.RichTextExtents(sText) + + Paint.Brush = Paint.RadialGradient(50, 100, 300, 50, 100, [Color.Yellow, Color.Cyan], [1.0, 0.0]) + Paint.RichText(sText) + Paint.Fill + + Paint.Brush = Paint.Color(Color.RGB(255, 31, 31, 93)) + Paint.LineWidth = 1 + Paint.Arc(X, Y, 10) + Paint.Fill + + Paint.Brush = Paint.Color(Color.RGB(255, 31, 31, 224)) + Paint.Rectangle(hExt.X, hExt.Y, hExt.Width, hExt.Height) + Paint.Fill + +End diff --git a/app/examples/Drawing/Painting/Example18 b/app/examples/Drawing/Painting/Example18 new file mode 100644 index 00000000..083f9032 --- /dev/null +++ b/app/examples/Drawing/Painting/Example18 @@ -0,0 +1,16 @@ +Public Sub Example18() + + Paint.Save + Paint.Rectangle(10, 10, 200, 100) + Paint.Stroke(True) + Paint.Clip + Paint.Font.Size = 18 + 'Paint.Brush = Paint.Color(Color.Blue) + Paint.Text("TO BE, OR NOT TO BE: THAT IS THE QUESTION:", 10, 10, 30, 30) + Paint.Fill + Paint.Restore + 'Paint.Brush = Paint.Color(Color.Blue) + Paint.Text("TO BE, OR NOT TO BE: THAT IS THE QUESTION:", 10, 40, 30, 30) + Paint.Fill + +End diff --git a/app/examples/Drawing/Painting/Example2 b/app/examples/Drawing/Painting/Example2 new file mode 100644 index 00000000..08f7a64d --- /dev/null +++ b/app/examples/Drawing/Painting/Example2 @@ -0,0 +1,29 @@ +Public Sub Example2() + + Dim XC As Float = 128 + Dim YC As Float = 128 + Dim Radius As Float = 100 + Dim Angle1 As Float = Rad(-45) + Dim Angle2 As Float = Rad(180) + Dim X, Y As Float + + Paint.LineWidth = 10 + Paint.Arc(XC, YC, Radius, Angle1, Angle2 - Angle1) + Paint.Stroke + + 'Draw helping Lines + Paint.LineWidth = 6.0 + Paint.Brush = Paint.Color(Color.RGB(255, 0.2 * 255, 0.2 * 255, 0.6 * 255)) + + Paint.Arc(XC, YC, 10.0) + Paint.Fill + + Paint.Arc(XC, YC, Radius, Angle1, 0) + X = Paint.X + Y = Paint.Y + Paint.Arc(XC, YC, Radius, Angle2, 0) + Paint.LineTo(XC, YC) + Paint.LineTo(X, Y) + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example20 b/app/examples/Drawing/Painting/Example20 new file mode 100644 index 00000000..d7d61caa --- /dev/null +++ b/app/examples/Drawing/Painting/Example20 @@ -0,0 +1,17 @@ +Public Sub Example20() + + Dim eWidth As Float + Dim Y As Float + + Y = 20.5 + For eWidth = 0 To 5 Step 0.25 + Paint.LineWidth = eWidth + Paint.MoveTo(50, Y) + Paint.RelLineTo(200, 0) + Paint.Stroke + Paint.Text(eWidth, 0, Y - 10, 40, 20, Align.Right) + Paint.Fill + Y += 20 + Next + +End diff --git a/app/examples/Drawing/Painting/Example21 b/app/examples/Drawing/Painting/Example21 new file mode 100644 index 00000000..429f40b0 --- /dev/null +++ b/app/examples/Drawing/Painting/Example21 @@ -0,0 +1,14 @@ +Public Sub Example21() + + If Not $hSvgImage Then + $hSvgImage = SvgImage.Load("gambas.svg") + Paint.Begin($hSvgImage) + Paint.Brush = Paint.RadialGradient(200, 140, 40, 215, 115, [Color.RGB(255, 0, 0, 64), Color.White], [1.0, 0.1]) + Paint.Arc(200, 140, 40) + Paint.Fill + Paint.End + Endif + + $hSvgImage.Paint + +End diff --git a/app/examples/Drawing/Painting/Example22 b/app/examples/Drawing/Painting/Example22 new file mode 100644 index 00000000..80132b6b --- /dev/null +++ b/app/examples/Drawing/Painting/Example22 @@ -0,0 +1,10 @@ +Public Sub Example22() + + Paint.Background = Color.RGB(128, 128, 255) + Paint.Rectangle(32, 32, 192, 192, 32) + Paint.Fill(True) + Paint.Background = Color.RGB(128, 0, 0, 128) + Paint.LineWidth = 10 + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example3 b/app/examples/Drawing/Painting/Example3 new file mode 100644 index 00000000..3e5829b2 --- /dev/null +++ b/app/examples/Drawing/Painting/Example3 @@ -0,0 +1,15 @@ +Public Sub Example3() + + Paint.Arc(128.0, 128.0, 76.8) + Paint.Clip + Paint.Rectangle(0, 0, 256, 256) + Paint.Fill + Paint.Brush = Paint.Color(Color.RGB(0, 255, 0)) + Paint.MoveTo(0, 0) + Paint.LineTo(256, 256) + Paint.MoveTo(256, 0) + Paint.LineTo(0, 256) + Paint.LineWidth = 10.0 + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example4 b/app/examples/Drawing/Painting/Example4 new file mode 100644 index 00000000..26902e15 --- /dev/null +++ b/app/examples/Drawing/Painting/Example4 @@ -0,0 +1,13 @@ +Public Sub Example4() + + Dim hImg As Image + + Paint.Arc(128.0, 128.0, 76.8) + Paint.Clip + hImg = Image.Load(IMAGE_NAME) + Paint.Scale(256 / hImg.Width, 256 / hImg.Height) + Paint.Brush = Paint.Image(hImg, 0, 0) + Paint.Rectangle(0, 0, 512, 512) + Paint.Fill() + +End diff --git a/app/examples/Drawing/Painting/Example5 b/app/examples/Drawing/Painting/Example5 new file mode 100644 index 00000000..e2056d94 --- /dev/null +++ b/app/examples/Drawing/Painting/Example5 @@ -0,0 +1,61 @@ +Public Sub Example5() + + ' A custom shape that could be wrapped in a function + Dim X0 As Float = 25.6 + Dim Y0 As Float = 25.6 + Dim RectWidth As Float = 204.8 + Dim RectHeight As Float = 204.8 + + Dim Radius As Float = 102.4 + + Dim X1, Y1 As Float + + X1 = X0 + RectWidth + Y1 = Y0 + RectHeight + + If Not (RectWidth > 0.0) Or Not (RectHeight > 0.0) Then Return + + If RectWidth / 2 < Radius Then + If RectHeight / 2 < Radius Then + Paint.MoveTo(X0, (Y0 + Y1) / 2) + Paint.CurveTo(X0, Y0, X0, Y0, (X0 + X1) / 2, Y0) + Paint.CurveTo(X1, Y0, X1, Y0, X1, (Y0 + Y1) / 2) + Paint.CurveTo(X1, Y1, X1, Y1, (X1 + X0) / 2, Y1) + Paint.CurveTo(X0, Y1, X0, Y1, X0, (Y0 + Y1) / 2) + Else + Paint.MoveTo(X0, Y0 + Radius) + Paint.CurveTo(X0, Y0, X0, Y0, (X0 + X1) / 2, Y0) + Paint.CurveTo(X1, Y0, X1, Y0, X1, Y0 + Radius) + Paint.LineTo(X1, Y1 - Radius) + Paint.CurveTo(X1, Y1, X1, Y1, (X1 + X0) / 2, Y1) + Paint.CurveTo(X0, Y1, X0, Y1, X0, Y1 - Radius) + Endif + Else + If (RectHeight / 2 < Radius) Then + Paint.MoveTo(X0, (Y0 + Y1) / 2) + Paint.CurveTo(X0, Y0, X0, Y0, X0 + Radius, Y0) + Paint.LineTo(X1 - Radius, Y0) + Paint.CurveTo(X1, Y0, X1, Y0, X1, (Y0 + Y1) / 2) + Paint.CurveTo(X1, Y1, X1, Y1, X1 - Radius, Y1) + Paint.LineTo(X0 + Radius, Y1) + Paint.CurveTo(X0, Y1, X0, Y1, X0, (Y0 + Y1) / 2) + Else + Paint.MoveTo(X0, Y0 + Radius) + Paint.CurveTo(X0, Y0, X0, Y0, X0 + Radius, Y0) + Paint.LineTo(X1 - Radius, Y0) + Paint.CurveTo(X1, Y0, X1, Y0, X1, Y0 + Radius) + Paint.LineTo(X1, Y1 - Radius) + Paint.CurveTo(X1, Y1, X1, Y1, X1 - Radius, Y1) + Paint.LineTo(X0 + Radius, Y1) + Paint.CurveTo(X0, Y1, X0, Y1, X0, Y1 - Radius) + Endif + Endif + + Paint.ClosePath + Paint.Brush = Paint.Color(Color.RGB(128, 128, 255)) + Paint.Fill(True) + Paint.Brush = Paint.Color(Color.RGB(128, 0, 0, 128)) + Paint.LineWidth = 10.0 + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example6 b/app/examples/Drawing/Painting/Example6 new file mode 100644 index 00000000..f9b00cfb --- /dev/null +++ b/app/examples/Drawing/Painting/Example6 @@ -0,0 +1,27 @@ +Public Sub Example6() + + Dim X As Float = 25.6 + Dim Y As Float = 128.0 + Dim X1 As Float = 102.4 + Dim Y1 As Float = 230.4 + Dim X2 As Float = 153.6 + Dim Y2 As Float = 25.6 + Dim X3 As Float = 230.4 + Dim Y3 As Float = 128.0 + + Paint.MoveTo(X, Y) + Paint.CurveTo(X1, Y1, X2, Y2, X3, Y3) + + Paint.LineWidth = 10 + Paint.Stroke + + Paint.Brush = Paint.Color(Color.RGB(255, 255 * 0.2, 255 * 0.2, 255 * 0.6)) + Paint.LineWidth = 6 + + Paint.MoveTo(X, Y) + Paint.LineTo(X1, Y1) + Paint.MoveTo(X2, Y2) + Paint.LineTo(X3, Y3) + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example7 b/app/examples/Drawing/Painting/Example7 new file mode 100644 index 00000000..08ca4a26 --- /dev/null +++ b/app/examples/Drawing/Painting/Example7 @@ -0,0 +1,17 @@ +Public Sub Example7() + + Dim Dashes As Float[] = [5.0, 'ink + 1.0, 'skip + 1.0, 'ink + 1.0] 'skip + + Paint.Dash = Dashes + Paint.DashOffset = -5 + Paint.LineWidth = 10.0 + Paint.MoveTo(128.0, 25.6) + Paint.LineTo(230.4, 230.4) + Paint.RelLineTo(-102.4, 0) + Paint.CurveTo(51.2, 230.4, 51.2, 128.0, 128.0, 128.0) + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example8 b/app/examples/Drawing/Painting/Example8 new file mode 100644 index 00000000..453c3041 --- /dev/null +++ b/app/examples/Drawing/Painting/Example8 @@ -0,0 +1,22 @@ +Public Sub Example8() + + Paint.MoveTo(128.0, 25.6) + Paint.LineTo(230.4, 230.4) + Paint.LineTo(Paint.X - 102.4, Paint.Y) + Paint.CurveTo(51.2, 230.4, 51.2, 128.0, 128.0, 128.0) + Paint.ClosePath + + Paint.MoveTo(64.0, 25.6) + Paint.RelLineTo(51.2, 51.2) + Paint.RelLineTo(-51.2, 51.2) + Paint.RelLineTo(-51.2, -51.2) + Paint.ClosePath + + Paint.LineWidth = 10 + Paint.Brush = Paint.Color(Color.Blue) + Paint.Fill(True) + + Paint.Brush = Paint.Color(Color.Black) + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/Example9 b/app/examples/Drawing/Painting/Example9 new file mode 100644 index 00000000..43ecfc4c --- /dev/null +++ b/app/examples/Drawing/Painting/Example9 @@ -0,0 +1,23 @@ +Public Sub Example9() + + Paint.LineWidth = 6 + Paint.Rectangle(12, 12, 232, 70) + Paint.Arc(64, 64, 40, 0, Pi(2)) + Paint.Arc(192, 64, 40, 0, Pi(-2)) + + Paint.FillRule = Paint.FillRuleEvenOdd + Paint.Brush = Paint.Color(Color.RGB(0, 179, 0)) + Paint.Fill(True) + Paint.Brush = Paint.Color(0) + Paint.Stroke + Paint.Translate(0, 128) + Paint.Rectangle(12, 12, 232, 70) + Paint.Arc(64, 64, 40, 0, Pi(-2)) + Paint.Arc(192, 64, 40, 0, Pi(2)) + Paint.FillRule = Paint.FillRuleWinding + Paint.Brush = Paint.Color(Color.RGB(0, 0, 230)) + Paint.Fill(True) + Paint.Brush = Paint.Color(0) + Paint.Stroke + +End diff --git a/app/examples/Drawing/Painting/clovis.jpg b/app/examples/Drawing/Painting/clovis.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2c4c89ffc7ae280aa7ae45cdf21e81fdbd10daaa GIT binary patch literal 15838 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 K&+;TcSN;#PjmS3u literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Painting/gambas.svg b/app/examples/Drawing/Painting/gambas.svg new file mode 100644 index 00000000..5bfafe4d --- /dev/null +++ b/app/examples/Drawing/Painting/gambas.svg @@ -0,0 +1,540 @@ + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Gambas 3 + 15/09/2008 + + + Fabien Bodard + + + + + Common Creative + + + + + Fabien Bodard + + + gbFBodard150908 + http://gambas.sf.net + + + gambas3 logo basic shrimp + + + A pretty shrimp, a malicious new gambas logo... we got the power ! + + + Fabien Bodard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/examples/Drawing/Painting/icon.png b/app/examples/Drawing/Painting/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..18f9f05d5d8bfbd0b241f564b753218c3c2c3e11 GIT binary patch literal 2078 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/QuasiRegular/.directory b/app/examples/Drawing/QuasiRegular/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Drawing/QuasiRegular/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Drawing/QuasiRegular/.hidden/screenshots/2014-12-14.png b/app/examples/Drawing/QuasiRegular/.hidden/screenshots/2014-12-14.png new file mode 100644 index 0000000000000000000000000000000000000000..a5876b55d2b5a7db3a51fc54a06080e4365aa7b5 GIT binary patch literal 44667 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/QuasiRegular/.icon.png b/app/examples/Drawing/QuasiRegular/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5b793fca03652dc51e469e9f2b6ca2fe14e2578e GIT binary patch literal 3730 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/RandomColorSort/.directory b/app/examples/Drawing/RandomColorSort/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Drawing/RandomColorSort/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Drawing/RandomColorSort/.hidden/screenshots/2014-12-14.png b/app/examples/Drawing/RandomColorSort/.hidden/screenshots/2014-12-14.png new file mode 100644 index 0000000000000000000000000000000000000000..c5fc40610b9c2f4173acce0c7144b42507b920f1 GIT binary patch literal 247987 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 z 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 L%o>n&L literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/RandomColorSort/.icon.png b/app/examples/Drawing/RandomColorSort/.icon.png new file mode 100644 index 0000000000000000000000000000000000000000..de4974fd0932fbb97a3bdb2538ad6f29e2270ca6 GIT binary patch literal 3663 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/RandomColorSort/.project b/app/examples/Drawing/RandomColorSort/.project new file mode 100644 index 00000000..642e0528 --- /dev/null +++ b/app/examples/Drawing/RandomColorSort/.project @@ -0,0 +1,15 @@ +# Gambas Project File 3.0 +# Compiled with Gambas 3.6.90 +Title=Random Colors, Sorted by Hue +Startup=FMain +Icon=RandomColorSort.png +Version=1.0.0 +Component=gb.image +Component=gb.gui +Description="Random colors, sorted by hue.\n\nRandom colors are sorted by hue on a two-dimentional circular chart. Colors are either displayed all at once, or slowed down to see each separate color plotted on the chart. The image can be saved as a PNG file." +Authors="Louis W. Adams, Jr." +Environment="GB_JIT_DEBUG=1" +TabSize=2 +KeepDebugInfo=0 +Vendor=Example +Packager=1 diff --git a/app/examples/Drawing/RandomColorSort/.src/FMain.class b/app/examples/Drawing/RandomColorSort/.src/FMain.class new file mode 100644 index 00000000..cca9d7df --- /dev/null +++ b/app/examples/Drawing/RandomColorSort/.src/FMain.class @@ -0,0 +1,265 @@ +' Gambas class file + +' RandomColorSort - Louis W. Adams, Jr. - February 2014 +' Drawing routine fixed by Benoît Minisini + +Const cFormWidth As Integer = 927 'Form size. +Const cFormHeight As Integer = 800 + +Const cChartWidthHeight As Integer = 783 'Color chart size, width and height. +Const cInset As Integer = 50 'Width of border around chart. +Const cCircleRadius As Integer = 15 'Radius, in pixels, of color circles. + +'Create picture and initialize size. +Private picChart As New Picture(cChartWidthHeight, cChartWidthHeight) + +Private a As Single 'Color attributes. +Private b As Single +Private cc As Single +Private hh As Single + +Private RR As Integer 'Color RGB values. +Private GG As Integer +Private BB As Integer + +Private iColorCount As Integer 'The number of colors to be plotted. + +Public Sub _new() + + With Me 'Set up form size and display characteristics. + .Width = cFormWidth + .Height = cFormHeight + .Resizable = False + .Center + End With + + With daColorChart 'Set up display area control size and other characteristics. + .Width = cChartWidthHeight + .Height = cChartWidthHeight + End With + + pnlControls.X = cChartWidthHeight + 20 'Position panel 20 pixels to the right of the display area control. + pnlControls.Y = (cChartWidthHeight - pnlControls.Height) \ 2 'Vertically center the panel on the form. + + scrColorCount.Value = 9 'The initial number of colors to display is 2^9 = 512 colors. + +End + +Private Sub RedrawPicture() + + Dim I As Integer + + iColorCount = 2 ^ scrColorCount.Value 'Number of colors. + lblColorCount.Text = Trim(Format(iColorCount, "##,###")) 'Convert the number of colors into a text label. + + picChart.Fill(Color.White) 'Initialize picture by filling it with white. + 'This also serves to "erase" a pre-existing picture. + + If chkDelay.Value Then 'Check to see if user wants a slow color display. + + For I = 1 To iColorCount + ComputeColor() + Paint.Begin(picChart) + DrawColorCircle() 'Paint/draw the color circle on the picture. + Paint.End + daColorChart.Refresh + Wait 0.25 'Delay next color displayed. + If Not chkDelay.Value Then Break 'Abort slow display if user unchecks checkbox. + Next + + Else 'Display all colors at once. + + Paint.Begin(picChart) + For I = 1 To iColorCount + ComputeColor() + DrawColorCircle() 'Paint/draw the color circle on the picture. + Next + Paint.End + + daColorChart.Refresh + + End If + +End + +Public Sub Form_Open() + + RedrawPicture + +End + +Private Sub ComputeColor() + + RR = Rnd(0, 255) 'Pick random RGB values for a color. + GG = Rnd(0, 255) + BB = Rnd(0, 255) + + cc = (Max(Max(RR, GG), BB) - Min(Min(RR, GG), BB)) / 255.0 'Chromaticness, the distance of the color from center. + hh = ATan2(((RR + GG) - 2 * BB), Sqr(3) * (RR - GG)) 'Hue angle. + + a = cc * Cos(hh) 'Color space coordinates, where a is the horizontal coordinate + b = cc * Sin(hh) 'and b is the vertical coordinate. Both coordinates range from -1 to 1. + +End + +Private Function XX(at As Single) As Integer 'Convert a color coordinate into horizontal screen coordinate. + + Return ((cChartWidthHeight - 2 * cInset) * at + cChartWidthHeight) * 0.5 + +End + +Private Function YY(bt As Single) As Integer 'Convert b color coordinate into vertical screen coordinate. + + Return (- (cChartWidthHeight - 2 * cInset) * bt + cChartWidthHeight) * 0.5 + +End + +Private Function DrawColorCircle() + + With Paint + .Brush = Paint.Color(Color.Black) 'Draw solid black disk. + .Ellipse(XX(a) - cCircleRadius, YY(b) - cCircleRadius, 2 * cCircleRadius, 2 * cCircleRadius) + .Fill() + + .Brush = Paint.Color(Color.RGB(RR, GG, BB)) 'Draw slightly smaller color disk so a black outline remains around the disk. + .Ellipse(XX(a) - cCircleRadius + 1, YY(b) - cCircleRadius + 1, 2 * (cCircleRadius - 1), 2 * (cCircleRadius - 1)) + .Fill() + End With + +End + +Private Function DrawChartAxes() + + Dim I As Integer + Dim T As Single + + If chkAxes.Value Then + + With Paint 'Paint mode is good for drawing lines. + + .AntiAlias = False 'Do not antialias vertical and horizontal lines. Using antialias blurs them a little. + + .Brush = Paint.Color(Color.Black) 'Draw 3 pixel wide a and b axes. + .LineWidth = 3 + + .MoveTo(XX(-1), YY(0)) + .LineTo(XX(1), YY(0)) + .Stroke + + .MoveTo(XX(0), YY(1)) + .LineTo(XX(0), YY(-1)) + .Stroke + + .AntiAlias = True 'Turn on antialiasing to smoothen angled dashed lines. + .Dash = [5, 5] 'The dashes are 5 pixels long separated by 5 pixel gaps. + + For I = 30 To 330 Step 30 'Major color hues are located at 30 degree increments. + If (I = 90) Or (I = 180) Or (I = 270) Then 'Omit dashed lines at 90, 180, and 270 degrees. + Continue + Else + .MoveTo(XX(0), YY(0)) 'Draw 1-unit long lines from the coordinate center at + .LineTo(XX(Cos(Rad(I))), YY(Sin(Rad(I)))) 'specified angles I. Convert degrees to radians for + .Stroke 'trig functions. + Endif + Next + + End With + + With Draw 'Draw mode is good for displaying text. + + .Font.Name = "DejaVu Sans Mono" + .Foreground = Color.Black + .Font.Size = 20 + .Font.Bold = False + + T = 1.09 'Radial distance for color letters. + + .Text("R", XX(T * Cos(Rad(30.0))), YY(T * Sin(Rad(30.0))), 1, 1, 3) 'Red. Center the letter on a 1x1 pixel area; i.e., text base is along radial line. + .Text("Y", XX(0), YY(T), 1, 1, 3) 'Yellow + .Text("O", XX(T * Cos(Rad(60.0))), YY(T * Sin(Rad(60.0))), 1, 1, 3) 'Orange + '.Text("L", XX(T * Cos(Rad(120.0))), YY(T * Sin(Rad(120.0))), 1, 1, 3) 'Lime. Yellowish green. Not a standard hue. + .Text("G", XX(T * Cos(Rad(150.0))), YY(T * Sin(Rad(150.0))), 1, 1, 3) 'Green + .Text("C", XX(T * Cos(Rad(210.0))), YY(T * Sin(Rad(210.0))), 1, 1, 3) 'Cyan + .Text("B", XX(0), YY(- T), 1, 1, 3) 'Blue. This is computer display blue, which is slightly violet. + '.Text("BB", XX(T * Cos(Rad(240.0))), YY(T * Sin(Rad(240.0))), 1, 1, 3) 'Baby Blue, sky blue. This is what many people think of as blue. + .Text("V", XX(T * Cos(Rad(300.0))), YY(T * Sin(Rad(300.0))), 1, 1, 3) 'Violet + .Text("M", XX(T * Cos(Rad(330.0))), YY(T * Sin(Rad(330.0))), 1, 1, 3) 'Magenta + + End With + + End If + +End + +Public Sub scrColorCount_Change() + + If chkDelay.Value Then 'In order to gracefully exit the slow color display loop when the number of colors is changed, + chkDelay.Value = False 'it is necessary to reset the control to stop slow display mode. + Endif + + 'Wait 'Execute all pending events before continuing. Otherwise duplicate scroll events are triggered + RedrawPicture 'for high color counts. Don't know why! + +End + +Public Sub btnSave_Click() + + Dim strFilter As New String[] + + strFilter.Add("*.png") 'GAMBAS can convert the picture into other file formats. + strFilter.Add("PNG") 'PNG uses lossless compression, and is used on the Internet. + + Dialog.Filter = strFilter 'Prompt user for file name. + + If (Dialog.SaveFile()) Then Return 'Cancel request (exit subroutine) if user hit cancel button. + + picChart.Save(Dialog.Path) 'Save picture to file. + +Catch + Message.Warning("Could not save image\n\n\t" & Dialog.Path & "\n \n" & Error.Text & ". Be sure to end the filename with the .png extension!") + +End + +Public Sub chkAxes_Click() + + daColorChart.Refresh + +End + +Public Sub btnReDo_Click() + + RedrawPicture + +End + +Public Sub chkDelay_Click() + + RedrawPicture + +End + +Public Sub btnQuit_Click() + + If chkDelay.Value Then 'Properly terminate delayed graphics calls prior to exiting program. + chkDelay.Value = False 'Force quit on delayed colors. + Wait 0.5 'Make sure delay loop times out. + Endif + + Me.Close + +End + +Public Sub Form_Close() 'If user manually closes window, capture event and redirect to Quit routine. + + btnQuit_Click() + +End + +Public Sub daColorChart_Draw() + + Draw.Picture(picChart, 0, 0) + If chkAxes.Value Then + DrawChartAxes() 'Show how the color space is divided up into hues. + Endif + +End diff --git a/app/examples/Drawing/RandomColorSort/.src/FMain.form b/app/examples/Drawing/RandomColorSort/.src/FMain.form new file mode 100644 index 00000000..d244bc9e --- /dev/null +++ b/app/examples/Drawing/RandomColorSort/.src/FMain.form @@ -0,0 +1,59 @@ +# Gambas Form File 3.0 + +{ Form Form + MoveScaled(0,0,74,85) + Text = ("Random Colors, Sorted By Hue") + Icon = Picture["icon:/32/fill"] + Resizable = False + { daColorChart DrawingArea + MoveScaled(1,1,26,26) + Background = &HFFFFFF& + Border = Border.Plain + } + { pnlControls Panel + MoveScaled(28,1,14,66) + Background = &HFFFFFF& + Border = Border.Plain + { btnSave Button + MoveScaled(2,55,10,4) + ToolTip = ("Create PNG File of Current Image") + Text = ("Save") + } + { chkAxes CheckBox + MoveScaled(3,2,8,3) + Text = ("Hues") + } + { btnQuit Button + MoveScaled(2,60,10,4) + ToolTip = ("Esc Key Exits Too") + Text = ("Exit") + Cancel = True + } + { btnReDo Button + MoveScaled(2,50,10,4) + ToolTip = ("Redraw the Colors") + Text = ("Repeat") + } + { Label1 Label + MoveScaled(4,10,6,3) + Text = ("Count") + Alignment = Align.Center + } + { lblColorCount Label + MoveScaled(4,46,6,3) + Text = ("2") + Alignment = Align.Center + } + { chkDelay CheckBox + MoveScaled(3,5,8,3) + Text = ("Slow") + } + { scrColorCount Slider + MoveScaled(5,13,4,33) + ToolTip = ("Change the Number of Colors") + MinValue = 1 + MaxValue = 15 + PageStep = 1 + } + } +} diff --git a/app/examples/Drawing/RandomColorSort/RandomColorSort.png b/app/examples/Drawing/RandomColorSort/RandomColorSort.png new file mode 100644 index 0000000000000000000000000000000000000000..7adc8473d35e3a197319533f2e667510e144f8ae GIT binary patch literal 10533 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 literal 0 HcmV?d00001 diff --git a/app/examples/Drawing/Tablet/.directory b/app/examples/Drawing/Tablet/.directory new file mode 100644 index 00000000..06dab1c8 --- /dev/null +++ b/app/examples/Drawing/Tablet/.directory @@ -0,0 +1,2 @@ +[Desktop Entry] +Icon=./.icon.png diff --git a/app/examples/Drawing/Tablet/.hidden/screenshots/2014-12-14.png b/app/examples/Drawing/Tablet/.hidden/screenshots/2014-12-14.png new file mode 100644 index 0000000000000000000000000000000000000000..93493ce0715b9c3969fff22e7c2686fd2e157e6c GIT binary patch literal 59466 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